Skip to content

Commit a7a0138

Browse files
authored
Fix: #356 - Triplex should run offline (#378)
* fixed: #356 - added ignore of Statsig error prompted to the stderr (fork.ts). fetch errors are always reported to the stderr. - added catch statement in Statsig initialisation and gate fallback (fg.ts) * fixed blocking projectCache * better Statsig error catching * fixed linting problems * docs(changeset): fixed #356 - triplex should run offline
1 parent 696c0e0 commit a7a0138

4 files changed

Lines changed: 62 additions & 38 deletions

File tree

.changeset/hungry-spies-cheat.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@triplex/lib": patch
3+
"triplex-vsce": patch
4+
---
5+
6+
fixed #356 - triplex should run offline

apps/vscode/src/extension/editor/project.ts

Lines changed: 33 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import { type Args } from "../../project";
1313
import { fork } from "../util/fork";
1414
import { getPort } from "../util/port";
1515

16+
const PROJECT_RETRY_TIME = 1000;
17+
1618
export interface TriplexProject {
1719
/** Number of active sessions of this project */
1820
active: number;
@@ -65,32 +67,40 @@ export async function resolveProject(
6567
userId: vscode.env.machineId,
6668
};
6769

68-
const p = await fork<Args>(
69-
process.env.NODE_ENV === "production"
70-
? join(context.extensionPath, "dist/project.js")
71-
: join(context.extensionPath, "src/project/index.ts"),
72-
{
73-
cwd: context.extensionPath,
74-
data: args,
75-
},
76-
);
70+
try {
71+
const projectProcess = await fork<Args>(
72+
process.env.NODE_ENV === "production"
73+
? join(context.extensionPath, "dist/project.js")
74+
: join(context.extensionPath, "src/project/index.ts"),
75+
{
76+
cwd: context.extensionPath,
77+
data: args,
78+
},
79+
);
7780

78-
const project: TriplexProject = {
79-
active: 1,
80-
args,
81-
dispose: () => {
82-
project.active -= 1;
81+
const project: TriplexProject = {
82+
active: 1,
83+
args,
84+
dispose: () => {
85+
project.active -= 1;
8386

84-
if (project.active === 0) {
85-
projectCache.delete(cwd);
86-
p.kill();
87-
}
88-
},
89-
on: p.on,
90-
ports,
91-
};
87+
if (project.active === 0) {
88+
projectCache.delete(cwd);
89+
projectProcess.kill();
90+
}
91+
},
92+
on: projectProcess.on,
93+
ports,
94+
};
9295

93-
resolve(project);
96+
resolve(project);
97+
// eslint-disable-next-line unicorn/prefer-optional-catch-binding, @typescript-eslint/no-unused-vars
98+
} catch (error) {
99+
// free up the cache on failure to initialize after standdown time.
100+
setTimeout(() => {
101+
projectCache.delete(cwd);
102+
}, PROJECT_RETRY_TIME);
103+
}
94104
});
95105

96106
projectCache.set(cwd, projectResolver);

apps/vscode/src/extension/util/fork.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,11 @@ export function fork<TData extends Record<string, unknown>>(
9292

9393
fork.stderr?.on("data", (data) => {
9494
const err = data.toString();
95-
if (err.includes("inspector")) {
95+
if (
96+
err.includes("inspector") ||
97+
(err.includes("Statsig") && err.includes("fetch failed")) || // excludes Statsig fetch errors
98+
(err.includes("Statsig") && err.includes("Timeout")) // excludes Statsig timeout error
99+
) {
96100
// Ignore inspector errors.
97101
return;
98102
}

packages/lib/src/fg.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { type FGEnvironment } from "./types";
1212

1313
const overrideAdapter = new LocalOverrideAdapter();
1414

15-
let instance: StatsigClient;
15+
let instance: StatsigClient | undefined;
1616

1717
export async function initFeatureGates({
1818
environment,
@@ -40,19 +40,23 @@ export async function initFeatureGates({
4040
return;
4141
}
4242

43-
const client = new StatsigClient(
44-
"client-RoO8UZOrk5aM4zXe3AP7vQjy66PWeumvN2PfQ2P6xt7",
45-
{ userID: userId },
46-
{
47-
environment: { tier: environment },
48-
networkConfig: { preventAllNetworkTraffic: environment === "local" },
49-
overrideAdapter,
50-
},
51-
);
43+
try {
44+
const client = new StatsigClient(
45+
"client-RoO8UZOrk5aM4zXe3AP7vQjy66PWeumvN2PfQ2P6xt7",
46+
{ userID: userId },
47+
{
48+
environment: { tier: environment },
49+
networkConfig: { networkTimeoutMs: 2500, preventAllNetworkTraffic: environment === "local" },
50+
overrideAdapter,
51+
},
52+
);
5253

53-
await client.initializeAsync();
54-
55-
instance = client;
54+
await client.initializeAsync();
55+
} catch (error) {
56+
// Ignore initialization errors to prevent blocking app startup but report to console for now
57+
// eslint-disable-next-line no-console
58+
console.log("[Statsig] Failed to initialize Statsig:", error);
59+
}
5660

5761
if (typeof document !== "undefined") {
5862
document.documentElement.setAttribute("data-fg-user", userId);
@@ -69,7 +73,7 @@ export async function initFeatureGates({
6973
}
7074

7175
export function fg(key: string): boolean {
72-
return instance.checkGate(key);
76+
return instance?.checkGate(key) ?? false; // Default to false if not initialized
7377
}
7478

7579
export function overrideFg(key: string, value: boolean) {

0 commit comments

Comments
 (0)