@glasstrace/sdk 0.14.1 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ERGEG4ZQ.js → chunk-2LDBR3F3.js} +16 -3
- package/dist/chunk-2LDBR3F3.js.map +1 -0
- package/dist/chunk-A2AZL6MZ.js +309 -0
- package/dist/chunk-A2AZL6MZ.js.map +1 -0
- package/dist/{chunk-ARAOZCZT.js → chunk-ROFOJQWN.js} +118 -16
- package/dist/chunk-ROFOJQWN.js.map +1 -0
- package/dist/{chunk-WV3NIPWJ.js → chunk-ZNOD6FC7.js} +18 -276
- package/dist/chunk-ZNOD6FC7.js.map +1 -0
- package/dist/cli/init.cjs +458 -115
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +33 -1
- package/dist/cli/init.d.ts +33 -1
- package/dist/cli/init.js +144 -42
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/mcp-add.cjs.map +1 -1
- package/dist/cli/mcp-add.js +4 -2
- package/dist/cli/mcp-add.js.map +1 -1
- package/dist/cli/uninit.cjs +181 -60
- package/dist/cli/uninit.cjs.map +1 -1
- package/dist/cli/uninit.d.cts +38 -8
- package/dist/cli/uninit.d.ts +38 -8
- package/dist/cli/uninit.js +6 -3
- package/dist/cli/validate.cjs +135 -0
- package/dist/cli/validate.cjs.map +1 -0
- package/dist/cli/validate.d.cts +60 -0
- package/dist/cli/validate.d.ts +60 -0
- package/dist/cli/validate.js +103 -0
- package/dist/cli/validate.js.map +1 -0
- package/dist/index.cjs +123 -47
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -5
- package/dist/index.d.ts +45 -5
- package/dist/index.js +109 -46
- package/dist/index.js.map +1 -1
- package/dist/{source-map-uploader-W6VPGY26.js → source-map-uploader-3GWUQDTS.js} +6 -2
- package/package.json +6 -4
- package/dist/chunk-ARAOZCZT.js.map +0 -1
- package/dist/chunk-ERGEG4ZQ.js.map +0 -1
- package/dist/chunk-WV3NIPWJ.js.map +0 -1
- /package/dist/{source-map-uploader-W6VPGY26.js.map → source-map-uploader-3GWUQDTS.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/validate.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/**\n * A single artifact-state inconsistency detected by `sdk init --validate`.\n */\nexport interface ValidationIssue {\n /** Stable machine-readable identifier for the issue class. */\n code:\n | \"glasstrace-dir-without-register-import\"\n | \"sdk-import-without-glasstrace-dir\"\n | \"mcp-marker-without-configs\"\n | \"mcp-configs-without-marker\";\n /** Human-readable message describing the inconsistency. */\n message: string;\n /** Suggested command or manual action to resolve the issue. */\n fix: string;\n}\n\n/** Options for `runValidate`. */\nexport interface ValidateOptions {\n projectRoot: string;\n}\n\n/** Structured result of running the validator. */\nexport interface ValidateResult {\n /** Zero when no issues; non-zero when any issue is detected. */\n exitCode: number;\n /** Ordered lines of human-friendly summary output. */\n summary: string[];\n /** Detailed per-issue findings. */\n issues: ValidationIssue[];\n}\n\n/** MCP config files init may create. Used to detect stale state. */\nconst MCP_CONFIG_CANDIDATES = [\n \".mcp.json\",\n \".cursor/mcp.json\",\n \".gemini/settings.json\",\n \".codex/config.toml\",\n] as const;\n\n/**\n * Returns true when the given instrumentation file imports\n * `@glasstrace/sdk` (which includes the `registerGlasstrace` import\n * emitted by `sdk init`).\n *\n * @internal Exported for unit testing only.\n */\nexport function hasGlasstraceImport(content: string): boolean {\n return /@glasstrace\\/sdk/.test(content);\n}\n\n/**\n * Returns true when the file imports `registerGlasstrace` specifically\n * (as opposed to other named exports such as `withGlasstraceConfig`).\n *\n * @internal Exported for unit testing only.\n */\nexport function hasRegisterGlasstraceImport(content: string): boolean {\n // Single- or multi-specifier imports from @glasstrace/sdk that include\n // `registerGlasstrace` as a named export.\n const match = /import\\s*\\{([^}]+)\\}\\s*from\\s*[\"']@glasstrace\\/sdk[\"']/;\n const importMatch = match.exec(content);\n if (!importMatch) return false;\n return importMatch[1]\n .split(\",\")\n .map((s) => s.trim())\n .includes(\"registerGlasstrace\");\n}\n\n/**\n * Validates consistency between the filesystem artifacts that `sdk init`\n * produces (DISC-1247 Scenario 4). Detects four classes of inconsistency:\n *\n * 1. `.glasstrace/` exists but `instrumentation.ts` does not import\n * `registerGlasstrace` from `@glasstrace/sdk`.\n * 2. `.glasstrace/` is missing but `instrumentation.ts` still imports\n * from `@glasstrace/sdk`.\n * 3. `.glasstrace/mcp-connected` marker exists but no MCP config files.\n * 4. MCP config files exist but no `.glasstrace/mcp-connected` marker.\n *\n * Each issue includes a stable `code`, a message, and a suggested fix.\n * Exit code is non-zero whenever any issue is detected so CI pipelines\n * can gate on `sdk init --validate`.\n *\n * @param options - Configuration for the validator.\n * @returns A structured result describing detected inconsistencies.\n */\nexport function runValidate(options: ValidateOptions): ValidateResult {\n const { projectRoot } = options;\n const issues: ValidationIssue[] = [];\n\n const glasstraceDir = path.join(projectRoot, \".glasstrace\");\n const instrumentationPath = path.join(projectRoot, \"instrumentation.ts\");\n const markerPath = path.join(glasstraceDir, \"mcp-connected\");\n\n const glasstraceDirExists = isDirectorySafe(glasstraceDir);\n const instrumentationExists = fs.existsSync(instrumentationPath);\n const instrumentationContent = instrumentationExists\n ? safeReadFile(instrumentationPath)\n : null;\n const markerExists = fs.existsSync(markerPath);\n\n const mcpConfigsPresent = MCP_CONFIG_CANDIDATES.filter((rel) =>\n fs.existsSync(path.join(projectRoot, rel)),\n );\n\n // 1. .glasstrace/ present but instrumentation missing the SDK import\n if (glasstraceDirExists) {\n if (\n instrumentationContent === null ||\n !hasRegisterGlasstraceImport(instrumentationContent)\n ) {\n issues.push({\n code: \"glasstrace-dir-without-register-import\",\n message:\n \".glasstrace/ exists but instrumentation.ts is missing the registerGlasstrace import.\",\n fix: \"Run `npx glasstrace init` to re-scaffold instrumentation.ts, or remove .glasstrace/ if the SDK is no longer in use.\",\n });\n }\n }\n\n // 2. .glasstrace/ missing but instrumentation still imports the SDK\n if (!glasstraceDirExists && instrumentationContent !== null) {\n if (hasGlasstraceImport(instrumentationContent)) {\n issues.push({\n code: \"sdk-import-without-glasstrace-dir\",\n message:\n \"instrumentation.ts imports from @glasstrace/sdk but .glasstrace/ is missing.\",\n fix: \"Run `npx glasstrace init` to recreate .glasstrace/, or `npx glasstrace uninit` to fully remove the SDK.\",\n });\n }\n }\n\n // 3. MCP marker present but no MCP config files exist\n if (markerExists && mcpConfigsPresent.length === 0) {\n issues.push({\n code: \"mcp-marker-without-configs\",\n message:\n \".glasstrace/mcp-connected marker is present but no MCP config files were found.\",\n fix: \"Run `npx glasstrace mcp add --force` to regenerate MCP configs, or delete .glasstrace/mcp-connected.\",\n });\n }\n\n // 4. MCP config files exist but no marker\n if (!markerExists && mcpConfigsPresent.length > 0) {\n issues.push({\n code: \"mcp-configs-without-marker\",\n message: `MCP config files exist (${mcpConfigsPresent.join(\", \")}) but .glasstrace/mcp-connected marker is missing.`,\n fix: \"Run `npx glasstrace init` to re-register the marker, or `npx glasstrace uninit` to fully remove MCP configuration.\",\n });\n }\n\n const summary: string[] = [];\n if (issues.length === 0) {\n summary.push(\"Glasstrace install state is consistent.\");\n } else {\n summary.push(\n `Detected ${issues.length} inconsistenc${issues.length === 1 ? \"y\" : \"ies\"} in Glasstrace install state:`,\n );\n }\n\n return {\n exitCode: issues.length > 0 ? 1 : 0,\n summary,\n issues,\n };\n}\n\n/**\n * Reads a file as UTF-8, returning `null` if the file cannot be read.\n */\nfunction safeReadFile(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Returns true when the path exists and is a directory. Returns false\n * when the path is missing, is not a directory, or when `statSync`\n * throws (permission denied, TOCTOU race between existsSync and\n * statSync, etc). Validation is best-effort and must not throw — a\n * crash here would turn a reporting tool into a hard failure.\n */\nfunction isDirectorySafe(dirPath: string): boolean {\n try {\n if (!fs.existsSync(dirPath)) return false;\n return fs.statSync(dirPath).isDirectory();\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,SAAoB;AACpB,WAAsB;AAkCtB,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,SAAS,oBAAoB,SAA0B;AAC5D,SAAO,mBAAmB,KAAK,OAAO;AACxC;AAQO,SAAS,4BAA4B,SAA0B;AAGpE,QAAM,QAAQ;AACd,QAAM,cAAc,MAAM,KAAK,OAAO;AACtC,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,YAAY,CAAC,EACjB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,SAAS,oBAAoB;AAClC;AAoBO,SAAS,YAAY,SAA0C;AACpE,QAAM,EAAE,YAAY,IAAI;AACxB,QAAM,SAA4B,CAAC;AAEnC,QAAM,gBAAqB,UAAK,aAAa,aAAa;AAC1D,QAAM,sBAA2B,UAAK,aAAa,oBAAoB;AACvE,QAAM,aAAkB,UAAK,eAAe,eAAe;AAE3D,QAAM,sBAAsB,gBAAgB,aAAa;AACzD,QAAM,wBAA2B,cAAW,mBAAmB;AAC/D,QAAM,yBAAyB,wBAC3B,aAAa,mBAAmB,IAChC;AACJ,QAAM,eAAkB,cAAW,UAAU;AAE7C,QAAM,oBAAoB,sBAAsB;AAAA,IAAO,CAAC,QACnD,cAAgB,UAAK,aAAa,GAAG,CAAC;AAAA,EAC3C;AAGA,MAAI,qBAAqB;AACvB,QACE,2BAA2B,QAC3B,CAAC,4BAA4B,sBAAsB,GACnD;AACA,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE;AAAA,QACF,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,CAAC,uBAAuB,2BAA2B,MAAM;AAC3D,QAAI,oBAAoB,sBAAsB,GAAG;AAC/C,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE;AAAA,QACF,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,gBAAgB,kBAAkB,WAAW,GAAG;AAClD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SACE;AAAA,MACF,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,gBAAgB,kBAAkB,SAAS,GAAG;AACjD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,2BAA2B,kBAAkB,KAAK,IAAI,CAAC;AAAA,MAChE,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AAEA,QAAM,UAAoB,CAAC;AAC3B,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,KAAK,yCAAyC;AAAA,EACxD,OAAO;AACL,YAAQ;AAAA,MACN,YAAY,OAAO,MAAM,gBAAgB,OAAO,WAAW,IAAI,MAAM,KAAK;AAAA,IAC5E;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,OAAO,SAAS,IAAI,IAAI;AAAA,IAClC;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,aAAa,UAAiC;AACrD,MAAI;AACF,WAAU,gBAAa,UAAU,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,gBAAgB,SAA0B;AACjD,MAAI;AACF,QAAI,CAAI,cAAW,OAAO,EAAG,QAAO;AACpC,WAAU,YAAS,OAAO,EAAE,YAAY;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single artifact-state inconsistency detected by `sdk init --validate`.
|
|
3
|
+
*/
|
|
4
|
+
interface ValidationIssue {
|
|
5
|
+
/** Stable machine-readable identifier for the issue class. */
|
|
6
|
+
code: "glasstrace-dir-without-register-import" | "sdk-import-without-glasstrace-dir" | "mcp-marker-without-configs" | "mcp-configs-without-marker";
|
|
7
|
+
/** Human-readable message describing the inconsistency. */
|
|
8
|
+
message: string;
|
|
9
|
+
/** Suggested command or manual action to resolve the issue. */
|
|
10
|
+
fix: string;
|
|
11
|
+
}
|
|
12
|
+
/** Options for `runValidate`. */
|
|
13
|
+
interface ValidateOptions {
|
|
14
|
+
projectRoot: string;
|
|
15
|
+
}
|
|
16
|
+
/** Structured result of running the validator. */
|
|
17
|
+
interface ValidateResult {
|
|
18
|
+
/** Zero when no issues; non-zero when any issue is detected. */
|
|
19
|
+
exitCode: number;
|
|
20
|
+
/** Ordered lines of human-friendly summary output. */
|
|
21
|
+
summary: string[];
|
|
22
|
+
/** Detailed per-issue findings. */
|
|
23
|
+
issues: ValidationIssue[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns true when the given instrumentation file imports
|
|
27
|
+
* `@glasstrace/sdk` (which includes the `registerGlasstrace` import
|
|
28
|
+
* emitted by `sdk init`).
|
|
29
|
+
*
|
|
30
|
+
* @internal Exported for unit testing only.
|
|
31
|
+
*/
|
|
32
|
+
declare function hasGlasstraceImport(content: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Returns true when the file imports `registerGlasstrace` specifically
|
|
35
|
+
* (as opposed to other named exports such as `withGlasstraceConfig`).
|
|
36
|
+
*
|
|
37
|
+
* @internal Exported for unit testing only.
|
|
38
|
+
*/
|
|
39
|
+
declare function hasRegisterGlasstraceImport(content: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Validates consistency between the filesystem artifacts that `sdk init`
|
|
42
|
+
* produces (DISC-1247 Scenario 4). Detects four classes of inconsistency:
|
|
43
|
+
*
|
|
44
|
+
* 1. `.glasstrace/` exists but `instrumentation.ts` does not import
|
|
45
|
+
* `registerGlasstrace` from `@glasstrace/sdk`.
|
|
46
|
+
* 2. `.glasstrace/` is missing but `instrumentation.ts` still imports
|
|
47
|
+
* from `@glasstrace/sdk`.
|
|
48
|
+
* 3. `.glasstrace/mcp-connected` marker exists but no MCP config files.
|
|
49
|
+
* 4. MCP config files exist but no `.glasstrace/mcp-connected` marker.
|
|
50
|
+
*
|
|
51
|
+
* Each issue includes a stable `code`, a message, and a suggested fix.
|
|
52
|
+
* Exit code is non-zero whenever any issue is detected so CI pipelines
|
|
53
|
+
* can gate on `sdk init --validate`.
|
|
54
|
+
*
|
|
55
|
+
* @param options - Configuration for the validator.
|
|
56
|
+
* @returns A structured result describing detected inconsistencies.
|
|
57
|
+
*/
|
|
58
|
+
declare function runValidate(options: ValidateOptions): ValidateResult;
|
|
59
|
+
|
|
60
|
+
export { type ValidateOptions, type ValidateResult, type ValidationIssue, hasGlasstraceImport, hasRegisterGlasstraceImport, runValidate };
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single artifact-state inconsistency detected by `sdk init --validate`.
|
|
3
|
+
*/
|
|
4
|
+
interface ValidationIssue {
|
|
5
|
+
/** Stable machine-readable identifier for the issue class. */
|
|
6
|
+
code: "glasstrace-dir-without-register-import" | "sdk-import-without-glasstrace-dir" | "mcp-marker-without-configs" | "mcp-configs-without-marker";
|
|
7
|
+
/** Human-readable message describing the inconsistency. */
|
|
8
|
+
message: string;
|
|
9
|
+
/** Suggested command or manual action to resolve the issue. */
|
|
10
|
+
fix: string;
|
|
11
|
+
}
|
|
12
|
+
/** Options for `runValidate`. */
|
|
13
|
+
interface ValidateOptions {
|
|
14
|
+
projectRoot: string;
|
|
15
|
+
}
|
|
16
|
+
/** Structured result of running the validator. */
|
|
17
|
+
interface ValidateResult {
|
|
18
|
+
/** Zero when no issues; non-zero when any issue is detected. */
|
|
19
|
+
exitCode: number;
|
|
20
|
+
/** Ordered lines of human-friendly summary output. */
|
|
21
|
+
summary: string[];
|
|
22
|
+
/** Detailed per-issue findings. */
|
|
23
|
+
issues: ValidationIssue[];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns true when the given instrumentation file imports
|
|
27
|
+
* `@glasstrace/sdk` (which includes the `registerGlasstrace` import
|
|
28
|
+
* emitted by `sdk init`).
|
|
29
|
+
*
|
|
30
|
+
* @internal Exported for unit testing only.
|
|
31
|
+
*/
|
|
32
|
+
declare function hasGlasstraceImport(content: string): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Returns true when the file imports `registerGlasstrace` specifically
|
|
35
|
+
* (as opposed to other named exports such as `withGlasstraceConfig`).
|
|
36
|
+
*
|
|
37
|
+
* @internal Exported for unit testing only.
|
|
38
|
+
*/
|
|
39
|
+
declare function hasRegisterGlasstraceImport(content: string): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Validates consistency between the filesystem artifacts that `sdk init`
|
|
42
|
+
* produces (DISC-1247 Scenario 4). Detects four classes of inconsistency:
|
|
43
|
+
*
|
|
44
|
+
* 1. `.glasstrace/` exists but `instrumentation.ts` does not import
|
|
45
|
+
* `registerGlasstrace` from `@glasstrace/sdk`.
|
|
46
|
+
* 2. `.glasstrace/` is missing but `instrumentation.ts` still imports
|
|
47
|
+
* from `@glasstrace/sdk`.
|
|
48
|
+
* 3. `.glasstrace/mcp-connected` marker exists but no MCP config files.
|
|
49
|
+
* 4. MCP config files exist but no `.glasstrace/mcp-connected` marker.
|
|
50
|
+
*
|
|
51
|
+
* Each issue includes a stable `code`, a message, and a suggested fix.
|
|
52
|
+
* Exit code is non-zero whenever any issue is detected so CI pipelines
|
|
53
|
+
* can gate on `sdk init --validate`.
|
|
54
|
+
*
|
|
55
|
+
* @param options - Configuration for the validator.
|
|
56
|
+
* @returns A structured result describing detected inconsistencies.
|
|
57
|
+
*/
|
|
58
|
+
declare function runValidate(options: ValidateOptions): ValidateResult;
|
|
59
|
+
|
|
60
|
+
export { type ValidateOptions, type ValidateResult, type ValidationIssue, hasGlasstraceImport, hasRegisterGlasstraceImport, runValidate };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import {
|
|
2
|
+
init_esm_shims
|
|
3
|
+
} from "../chunk-BGZ7J74D.js";
|
|
4
|
+
|
|
5
|
+
// src/cli/validate.ts
|
|
6
|
+
init_esm_shims();
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as path from "path";
|
|
9
|
+
var MCP_CONFIG_CANDIDATES = [
|
|
10
|
+
".mcp.json",
|
|
11
|
+
".cursor/mcp.json",
|
|
12
|
+
".gemini/settings.json",
|
|
13
|
+
".codex/config.toml"
|
|
14
|
+
];
|
|
15
|
+
function hasGlasstraceImport(content) {
|
|
16
|
+
return /@glasstrace\/sdk/.test(content);
|
|
17
|
+
}
|
|
18
|
+
function hasRegisterGlasstraceImport(content) {
|
|
19
|
+
const match = /import\s*\{([^}]+)\}\s*from\s*["']@glasstrace\/sdk["']/;
|
|
20
|
+
const importMatch = match.exec(content);
|
|
21
|
+
if (!importMatch) return false;
|
|
22
|
+
return importMatch[1].split(",").map((s) => s.trim()).includes("registerGlasstrace");
|
|
23
|
+
}
|
|
24
|
+
function runValidate(options) {
|
|
25
|
+
const { projectRoot } = options;
|
|
26
|
+
const issues = [];
|
|
27
|
+
const glasstraceDir = path.join(projectRoot, ".glasstrace");
|
|
28
|
+
const instrumentationPath = path.join(projectRoot, "instrumentation.ts");
|
|
29
|
+
const markerPath = path.join(glasstraceDir, "mcp-connected");
|
|
30
|
+
const glasstraceDirExists = isDirectorySafe(glasstraceDir);
|
|
31
|
+
const instrumentationExists = fs.existsSync(instrumentationPath);
|
|
32
|
+
const instrumentationContent = instrumentationExists ? safeReadFile(instrumentationPath) : null;
|
|
33
|
+
const markerExists = fs.existsSync(markerPath);
|
|
34
|
+
const mcpConfigsPresent = MCP_CONFIG_CANDIDATES.filter(
|
|
35
|
+
(rel) => fs.existsSync(path.join(projectRoot, rel))
|
|
36
|
+
);
|
|
37
|
+
if (glasstraceDirExists) {
|
|
38
|
+
if (instrumentationContent === null || !hasRegisterGlasstraceImport(instrumentationContent)) {
|
|
39
|
+
issues.push({
|
|
40
|
+
code: "glasstrace-dir-without-register-import",
|
|
41
|
+
message: ".glasstrace/ exists but instrumentation.ts is missing the registerGlasstrace import.",
|
|
42
|
+
fix: "Run `npx glasstrace init` to re-scaffold instrumentation.ts, or remove .glasstrace/ if the SDK is no longer in use."
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!glasstraceDirExists && instrumentationContent !== null) {
|
|
47
|
+
if (hasGlasstraceImport(instrumentationContent)) {
|
|
48
|
+
issues.push({
|
|
49
|
+
code: "sdk-import-without-glasstrace-dir",
|
|
50
|
+
message: "instrumentation.ts imports from @glasstrace/sdk but .glasstrace/ is missing.",
|
|
51
|
+
fix: "Run `npx glasstrace init` to recreate .glasstrace/, or `npx glasstrace uninit` to fully remove the SDK."
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (markerExists && mcpConfigsPresent.length === 0) {
|
|
56
|
+
issues.push({
|
|
57
|
+
code: "mcp-marker-without-configs",
|
|
58
|
+
message: ".glasstrace/mcp-connected marker is present but no MCP config files were found.",
|
|
59
|
+
fix: "Run `npx glasstrace mcp add --force` to regenerate MCP configs, or delete .glasstrace/mcp-connected."
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
if (!markerExists && mcpConfigsPresent.length > 0) {
|
|
63
|
+
issues.push({
|
|
64
|
+
code: "mcp-configs-without-marker",
|
|
65
|
+
message: `MCP config files exist (${mcpConfigsPresent.join(", ")}) but .glasstrace/mcp-connected marker is missing.`,
|
|
66
|
+
fix: "Run `npx glasstrace init` to re-register the marker, or `npx glasstrace uninit` to fully remove MCP configuration."
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
const summary = [];
|
|
70
|
+
if (issues.length === 0) {
|
|
71
|
+
summary.push("Glasstrace install state is consistent.");
|
|
72
|
+
} else {
|
|
73
|
+
summary.push(
|
|
74
|
+
`Detected ${issues.length} inconsistenc${issues.length === 1 ? "y" : "ies"} in Glasstrace install state:`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
exitCode: issues.length > 0 ? 1 : 0,
|
|
79
|
+
summary,
|
|
80
|
+
issues
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function safeReadFile(filePath) {
|
|
84
|
+
try {
|
|
85
|
+
return fs.readFileSync(filePath, "utf-8");
|
|
86
|
+
} catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function isDirectorySafe(dirPath) {
|
|
91
|
+
try {
|
|
92
|
+
if (!fs.existsSync(dirPath)) return false;
|
|
93
|
+
return fs.statSync(dirPath).isDirectory();
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
hasGlasstraceImport,
|
|
100
|
+
hasRegisterGlasstraceImport,
|
|
101
|
+
runValidate
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/validate.ts"],"sourcesContent":["import * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\n/**\n * A single artifact-state inconsistency detected by `sdk init --validate`.\n */\nexport interface ValidationIssue {\n /** Stable machine-readable identifier for the issue class. */\n code:\n | \"glasstrace-dir-without-register-import\"\n | \"sdk-import-without-glasstrace-dir\"\n | \"mcp-marker-without-configs\"\n | \"mcp-configs-without-marker\";\n /** Human-readable message describing the inconsistency. */\n message: string;\n /** Suggested command or manual action to resolve the issue. */\n fix: string;\n}\n\n/** Options for `runValidate`. */\nexport interface ValidateOptions {\n projectRoot: string;\n}\n\n/** Structured result of running the validator. */\nexport interface ValidateResult {\n /** Zero when no issues; non-zero when any issue is detected. */\n exitCode: number;\n /** Ordered lines of human-friendly summary output. */\n summary: string[];\n /** Detailed per-issue findings. */\n issues: ValidationIssue[];\n}\n\n/** MCP config files init may create. Used to detect stale state. */\nconst MCP_CONFIG_CANDIDATES = [\n \".mcp.json\",\n \".cursor/mcp.json\",\n \".gemini/settings.json\",\n \".codex/config.toml\",\n] as const;\n\n/**\n * Returns true when the given instrumentation file imports\n * `@glasstrace/sdk` (which includes the `registerGlasstrace` import\n * emitted by `sdk init`).\n *\n * @internal Exported for unit testing only.\n */\nexport function hasGlasstraceImport(content: string): boolean {\n return /@glasstrace\\/sdk/.test(content);\n}\n\n/**\n * Returns true when the file imports `registerGlasstrace` specifically\n * (as opposed to other named exports such as `withGlasstraceConfig`).\n *\n * @internal Exported for unit testing only.\n */\nexport function hasRegisterGlasstraceImport(content: string): boolean {\n // Single- or multi-specifier imports from @glasstrace/sdk that include\n // `registerGlasstrace` as a named export.\n const match = /import\\s*\\{([^}]+)\\}\\s*from\\s*[\"']@glasstrace\\/sdk[\"']/;\n const importMatch = match.exec(content);\n if (!importMatch) return false;\n return importMatch[1]\n .split(\",\")\n .map((s) => s.trim())\n .includes(\"registerGlasstrace\");\n}\n\n/**\n * Validates consistency between the filesystem artifacts that `sdk init`\n * produces (DISC-1247 Scenario 4). Detects four classes of inconsistency:\n *\n * 1. `.glasstrace/` exists but `instrumentation.ts` does not import\n * `registerGlasstrace` from `@glasstrace/sdk`.\n * 2. `.glasstrace/` is missing but `instrumentation.ts` still imports\n * from `@glasstrace/sdk`.\n * 3. `.glasstrace/mcp-connected` marker exists but no MCP config files.\n * 4. MCP config files exist but no `.glasstrace/mcp-connected` marker.\n *\n * Each issue includes a stable `code`, a message, and a suggested fix.\n * Exit code is non-zero whenever any issue is detected so CI pipelines\n * can gate on `sdk init --validate`.\n *\n * @param options - Configuration for the validator.\n * @returns A structured result describing detected inconsistencies.\n */\nexport function runValidate(options: ValidateOptions): ValidateResult {\n const { projectRoot } = options;\n const issues: ValidationIssue[] = [];\n\n const glasstraceDir = path.join(projectRoot, \".glasstrace\");\n const instrumentationPath = path.join(projectRoot, \"instrumentation.ts\");\n const markerPath = path.join(glasstraceDir, \"mcp-connected\");\n\n const glasstraceDirExists = isDirectorySafe(glasstraceDir);\n const instrumentationExists = fs.existsSync(instrumentationPath);\n const instrumentationContent = instrumentationExists\n ? safeReadFile(instrumentationPath)\n : null;\n const markerExists = fs.existsSync(markerPath);\n\n const mcpConfigsPresent = MCP_CONFIG_CANDIDATES.filter((rel) =>\n fs.existsSync(path.join(projectRoot, rel)),\n );\n\n // 1. .glasstrace/ present but instrumentation missing the SDK import\n if (glasstraceDirExists) {\n if (\n instrumentationContent === null ||\n !hasRegisterGlasstraceImport(instrumentationContent)\n ) {\n issues.push({\n code: \"glasstrace-dir-without-register-import\",\n message:\n \".glasstrace/ exists but instrumentation.ts is missing the registerGlasstrace import.\",\n fix: \"Run `npx glasstrace init` to re-scaffold instrumentation.ts, or remove .glasstrace/ if the SDK is no longer in use.\",\n });\n }\n }\n\n // 2. .glasstrace/ missing but instrumentation still imports the SDK\n if (!glasstraceDirExists && instrumentationContent !== null) {\n if (hasGlasstraceImport(instrumentationContent)) {\n issues.push({\n code: \"sdk-import-without-glasstrace-dir\",\n message:\n \"instrumentation.ts imports from @glasstrace/sdk but .glasstrace/ is missing.\",\n fix: \"Run `npx glasstrace init` to recreate .glasstrace/, or `npx glasstrace uninit` to fully remove the SDK.\",\n });\n }\n }\n\n // 3. MCP marker present but no MCP config files exist\n if (markerExists && mcpConfigsPresent.length === 0) {\n issues.push({\n code: \"mcp-marker-without-configs\",\n message:\n \".glasstrace/mcp-connected marker is present but no MCP config files were found.\",\n fix: \"Run `npx glasstrace mcp add --force` to regenerate MCP configs, or delete .glasstrace/mcp-connected.\",\n });\n }\n\n // 4. MCP config files exist but no marker\n if (!markerExists && mcpConfigsPresent.length > 0) {\n issues.push({\n code: \"mcp-configs-without-marker\",\n message: `MCP config files exist (${mcpConfigsPresent.join(\", \")}) but .glasstrace/mcp-connected marker is missing.`,\n fix: \"Run `npx glasstrace init` to re-register the marker, or `npx glasstrace uninit` to fully remove MCP configuration.\",\n });\n }\n\n const summary: string[] = [];\n if (issues.length === 0) {\n summary.push(\"Glasstrace install state is consistent.\");\n } else {\n summary.push(\n `Detected ${issues.length} inconsistenc${issues.length === 1 ? \"y\" : \"ies\"} in Glasstrace install state:`,\n );\n }\n\n return {\n exitCode: issues.length > 0 ? 1 : 0,\n summary,\n issues,\n };\n}\n\n/**\n * Reads a file as UTF-8, returning `null` if the file cannot be read.\n */\nfunction safeReadFile(filePath: string): string | null {\n try {\n return fs.readFileSync(filePath, \"utf-8\");\n } catch {\n return null;\n }\n}\n\n/**\n * Returns true when the path exists and is a directory. Returns false\n * when the path is missing, is not a directory, or when `statSync`\n * throws (permission denied, TOCTOU race between existsSync and\n * statSync, etc). Validation is best-effort and must not throw — a\n * crash here would turn a reporting tool into a hard failure.\n */\nfunction isDirectorySafe(dirPath: string): boolean {\n try {\n if (!fs.existsSync(dirPath)) return false;\n return fs.statSync(dirPath).isDirectory();\n } catch {\n return false;\n }\n}\n"],"mappings":";;;;;AAAA;AAAA,YAAY,QAAQ;AACpB,YAAY,UAAU;AAkCtB,IAAM,wBAAwB;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AASO,SAAS,oBAAoB,SAA0B;AAC5D,SAAO,mBAAmB,KAAK,OAAO;AACxC;AAQO,SAAS,4BAA4B,SAA0B;AAGpE,QAAM,QAAQ;AACd,QAAM,cAAc,MAAM,KAAK,OAAO;AACtC,MAAI,CAAC,YAAa,QAAO;AACzB,SAAO,YAAY,CAAC,EACjB,MAAM,GAAG,EACT,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EACnB,SAAS,oBAAoB;AAClC;AAoBO,SAAS,YAAY,SAA0C;AACpE,QAAM,EAAE,YAAY,IAAI;AACxB,QAAM,SAA4B,CAAC;AAEnC,QAAM,gBAAqB,UAAK,aAAa,aAAa;AAC1D,QAAM,sBAA2B,UAAK,aAAa,oBAAoB;AACvE,QAAM,aAAkB,UAAK,eAAe,eAAe;AAE3D,QAAM,sBAAsB,gBAAgB,aAAa;AACzD,QAAM,wBAA2B,cAAW,mBAAmB;AAC/D,QAAM,yBAAyB,wBAC3B,aAAa,mBAAmB,IAChC;AACJ,QAAM,eAAkB,cAAW,UAAU;AAE7C,QAAM,oBAAoB,sBAAsB;AAAA,IAAO,CAAC,QACnD,cAAgB,UAAK,aAAa,GAAG,CAAC;AAAA,EAC3C;AAGA,MAAI,qBAAqB;AACvB,QACE,2BAA2B,QAC3B,CAAC,4BAA4B,sBAAsB,GACnD;AACA,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE;AAAA,QACF,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,CAAC,uBAAuB,2BAA2B,MAAM;AAC3D,QAAI,oBAAoB,sBAAsB,GAAG;AAC/C,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,SACE;AAAA,QACF,KAAK;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,gBAAgB,kBAAkB,WAAW,GAAG;AAClD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SACE;AAAA,MACF,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AAGA,MAAI,CAAC,gBAAgB,kBAAkB,SAAS,GAAG;AACjD,WAAO,KAAK;AAAA,MACV,MAAM;AAAA,MACN,SAAS,2BAA2B,kBAAkB,KAAK,IAAI,CAAC;AAAA,MAChE,KAAK;AAAA,IACP,CAAC;AAAA,EACH;AAEA,QAAM,UAAoB,CAAC;AAC3B,MAAI,OAAO,WAAW,GAAG;AACvB,YAAQ,KAAK,yCAAyC;AAAA,EACxD,OAAO;AACL,YAAQ;AAAA,MACN,YAAY,OAAO,MAAM,gBAAgB,OAAO,WAAW,IAAI,MAAM,KAAK;AAAA,IAC5E;AAAA,EACF;AAEA,SAAO;AAAA,IACL,UAAU,OAAO,SAAS,IAAI,IAAI;AAAA,IAClC;AAAA,IACA;AAAA,EACF;AACF;AAKA,SAAS,aAAa,UAAiC;AACrD,MAAI;AACF,WAAU,gBAAa,UAAU,OAAO;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AASA,SAAS,gBAAgB,SAA0B;AACjD,MAAI;AACF,QAAI,CAAI,cAAW,OAAO,EAAG,QAAO;AACpC,WAAU,YAAS,OAAO,EAAE,YAAY;AAAA,EAC1C,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -16508,6 +16508,8 @@ var init_console_capture = __esm({
|
|
|
16508
16508
|
var source_map_uploader_exports = {};
|
|
16509
16509
|
__export(source_map_uploader_exports, {
|
|
16510
16510
|
PRESIGNED_THRESHOLD_BYTES: () => PRESIGNED_THRESHOLD_BYTES,
|
|
16511
|
+
_resetBlobClientLoaderForTesting: () => _resetBlobClientLoaderForTesting,
|
|
16512
|
+
_setBlobClientLoaderForTesting: () => _setBlobClientLoaderForTesting,
|
|
16511
16513
|
collectSourceMaps: () => collectSourceMaps,
|
|
16512
16514
|
computeBuildHash: () => computeBuildHash,
|
|
16513
16515
|
discoverSourceMapFiles: () => discoverSourceMapFiles,
|
|
@@ -16671,10 +16673,20 @@ async function requestPresignedTokens(apiKey, endpoint, buildHash, files) {
|
|
|
16671
16673
|
const json2 = await response.json();
|
|
16672
16674
|
return PresignedUploadResponseSchema.parse(json2);
|
|
16673
16675
|
}
|
|
16676
|
+
async function defaultBlobClientLoader() {
|
|
16677
|
+
const dynamicImport = Function("id", "return import(id)");
|
|
16678
|
+
return dynamicImport("@vercel/blob/client");
|
|
16679
|
+
}
|
|
16680
|
+
function _setBlobClientLoaderForTesting(loader) {
|
|
16681
|
+
_blobClientLoader = loader;
|
|
16682
|
+
}
|
|
16683
|
+
function _resetBlobClientLoaderForTesting() {
|
|
16684
|
+
_blobClientLoader = defaultBlobClientLoader;
|
|
16685
|
+
}
|
|
16674
16686
|
async function uploadToBlob(clientToken, pathname, content) {
|
|
16675
16687
|
let mod;
|
|
16676
16688
|
try {
|
|
16677
|
-
mod = await
|
|
16689
|
+
mod = await _blobClientLoader();
|
|
16678
16690
|
} catch (err) {
|
|
16679
16691
|
const code = err.code;
|
|
16680
16692
|
if (code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND") {
|
|
@@ -16787,7 +16799,7 @@ async function uploadSourceMapsAuto(apiKey, endpoint, buildHash, maps, options)
|
|
|
16787
16799
|
}
|
|
16788
16800
|
const checkAvailable = options?.checkBlobAvailable ?? (async () => {
|
|
16789
16801
|
try {
|
|
16790
|
-
await
|
|
16802
|
+
await _blobClientLoader();
|
|
16791
16803
|
return true;
|
|
16792
16804
|
} catch {
|
|
16793
16805
|
return false;
|
|
@@ -16809,7 +16821,7 @@ async function uploadSourceMapsAuto(apiKey, endpoint, buildHash, maps, options)
|
|
|
16809
16821
|
);
|
|
16810
16822
|
return uploadSourceMaps(apiKey, endpoint, buildHash, maps);
|
|
16811
16823
|
}
|
|
16812
|
-
var fs2, path2, crypto2, import_node_child_process, LARGE_FILE_WARNING_BYTES, PRESIGNED_THRESHOLD_BYTES;
|
|
16824
|
+
var fs2, path2, crypto2, import_node_child_process, LARGE_FILE_WARNING_BYTES, PRESIGNED_THRESHOLD_BYTES, _blobClientLoader;
|
|
16813
16825
|
var init_source_map_uploader = __esm({
|
|
16814
16826
|
"src/source-map-uploader.ts"() {
|
|
16815
16827
|
"use strict";
|
|
@@ -16822,6 +16834,7 @@ var init_source_map_uploader = __esm({
|
|
|
16822
16834
|
init_dist();
|
|
16823
16835
|
LARGE_FILE_WARNING_BYTES = 50 * 1024 * 1024;
|
|
16824
16836
|
PRESIGNED_THRESHOLD_BYTES = 45e5;
|
|
16837
|
+
_blobClientLoader = defaultBlobClientLoader;
|
|
16825
16838
|
}
|
|
16826
16839
|
});
|
|
16827
16840
|
|
|
@@ -17215,6 +17228,7 @@ async function saveCachedConfig(response, projectRoot) {
|
|
|
17215
17228
|
const root = projectRoot ?? process.cwd();
|
|
17216
17229
|
const dirPath = modules.path.join(root, GLASSTRACE_DIR2);
|
|
17217
17230
|
const configPath = modules.path.join(dirPath, CONFIG_FILE);
|
|
17231
|
+
const tmpPath = `${configPath}.tmp`;
|
|
17218
17232
|
try {
|
|
17219
17233
|
await modules.fs.mkdir(dirPath, { recursive: true, mode: 448 });
|
|
17220
17234
|
await modules.fs.chmod(dirPath, 448);
|
|
@@ -17222,7 +17236,20 @@ async function saveCachedConfig(response, projectRoot) {
|
|
|
17222
17236
|
response,
|
|
17223
17237
|
cachedAt: Date.now()
|
|
17224
17238
|
};
|
|
17225
|
-
await modules.fs.writeFile(
|
|
17239
|
+
await modules.fs.writeFile(tmpPath, JSON.stringify(cached2), {
|
|
17240
|
+
encoding: "utf-8",
|
|
17241
|
+
mode: 384
|
|
17242
|
+
});
|
|
17243
|
+
try {
|
|
17244
|
+
await modules.fs.chmod(tmpPath, 384);
|
|
17245
|
+
await modules.fs.rename(tmpPath, configPath);
|
|
17246
|
+
} catch (renameErr) {
|
|
17247
|
+
try {
|
|
17248
|
+
await modules.fs.unlink(tmpPath);
|
|
17249
|
+
} catch {
|
|
17250
|
+
}
|
|
17251
|
+
throw renameErr;
|
|
17252
|
+
}
|
|
17226
17253
|
await modules.fs.chmod(configPath, 384);
|
|
17227
17254
|
} catch (err) {
|
|
17228
17255
|
console.warn(
|
|
@@ -21370,7 +21397,6 @@ async function configureOtel(config2, sessionManager) {
|
|
|
21370
21397
|
await provider.shutdown();
|
|
21371
21398
|
}
|
|
21372
21399
|
});
|
|
21373
|
-
registerSignalHandlers();
|
|
21374
21400
|
registerBeforeExitTrigger();
|
|
21375
21401
|
const prismaModule = await tryImport("@prisma/instrumentation");
|
|
21376
21402
|
if (prismaModule) {
|
|
@@ -21431,12 +21457,15 @@ var HEARTBEAT_INTERVAL_MS = 5 * 60 * 1e3;
|
|
|
21431
21457
|
var BACKOFF_BASE_MS = HEARTBEAT_INTERVAL_MS;
|
|
21432
21458
|
var BACKOFF_MAX_MS = 30 * 60 * 1e3;
|
|
21433
21459
|
var BACKOFF_JITTER = 0.2;
|
|
21460
|
+
var HEARTBEAT_SHUTDOWN_PRIORITY = 10;
|
|
21461
|
+
var SHUTDOWN_MARKER_RELPATH = ".glasstrace/shutdown-requested";
|
|
21434
21462
|
var heartbeatTimer = null;
|
|
21435
21463
|
var heartbeatGeneration = 0;
|
|
21436
21464
|
var backoffAttempts = 0;
|
|
21437
21465
|
var backoffUntil = 0;
|
|
21438
21466
|
var tickInProgress = false;
|
|
21439
|
-
var
|
|
21467
|
+
var shutdownHookRegistered = false;
|
|
21468
|
+
var shutdownFired = false;
|
|
21440
21469
|
function startHeartbeat(config2, anonKey, sdkVersion, generation, onClaimTransition) {
|
|
21441
21470
|
if (heartbeatTimer !== null) return;
|
|
21442
21471
|
heartbeatGeneration = generation;
|
|
@@ -21444,7 +21473,7 @@ function startHeartbeat(config2, anonKey, sdkVersion, generation, onClaimTransit
|
|
|
21444
21473
|
void heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTransition);
|
|
21445
21474
|
}, HEARTBEAT_INTERVAL_MS);
|
|
21446
21475
|
heartbeatTimer.unref();
|
|
21447
|
-
|
|
21476
|
+
registerHeartbeatShutdownHook(config2, anonKey, sdkVersion);
|
|
21448
21477
|
if (config2.verbose) {
|
|
21449
21478
|
sdkLog("info", "[glasstrace] Heartbeat started (5-minute interval).");
|
|
21450
21479
|
}
|
|
@@ -21454,7 +21483,26 @@ function stopHeartbeat() {
|
|
|
21454
21483
|
clearInterval(heartbeatTimer);
|
|
21455
21484
|
heartbeatTimer = null;
|
|
21456
21485
|
}
|
|
21457
|
-
|
|
21486
|
+
}
|
|
21487
|
+
function checkShutdownMarker(projectRoot) {
|
|
21488
|
+
let fsSync2 = null;
|
|
21489
|
+
let pathSync = null;
|
|
21490
|
+
try {
|
|
21491
|
+
fsSync2 = require("fs");
|
|
21492
|
+
pathSync = require("path");
|
|
21493
|
+
} catch {
|
|
21494
|
+
return { triggered: false };
|
|
21495
|
+
}
|
|
21496
|
+
const root = projectRoot ?? (typeof process !== "undefined" ? process.cwd() : ".");
|
|
21497
|
+
const markerPath = pathSync.join(root, SHUTDOWN_MARKER_RELPATH);
|
|
21498
|
+
if (!fsSync2.existsSync(markerPath)) return { triggered: false };
|
|
21499
|
+
try {
|
|
21500
|
+
fsSync2.unlinkSync(markerPath);
|
|
21501
|
+
} catch {
|
|
21502
|
+
}
|
|
21503
|
+
const shutdown = executeShutdown().catch(() => {
|
|
21504
|
+
});
|
|
21505
|
+
return { triggered: true, shutdown };
|
|
21458
21506
|
}
|
|
21459
21507
|
async function heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTransition) {
|
|
21460
21508
|
if (tickInProgress) return;
|
|
@@ -21464,6 +21512,14 @@ async function heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTr
|
|
|
21464
21512
|
stopHeartbeat();
|
|
21465
21513
|
return;
|
|
21466
21514
|
}
|
|
21515
|
+
const markerResult = checkShutdownMarker();
|
|
21516
|
+
if (markerResult.triggered) {
|
|
21517
|
+
stopHeartbeat();
|
|
21518
|
+
if (markerResult.shutdown) {
|
|
21519
|
+
await markerResult.shutdown;
|
|
21520
|
+
}
|
|
21521
|
+
return;
|
|
21522
|
+
}
|
|
21467
21523
|
if (Date.now() < backoffUntil) {
|
|
21468
21524
|
if (config2.verbose) {
|
|
21469
21525
|
sdkLog("info", "[glasstrace] Heartbeat skipped (rate-limit backoff).");
|
|
@@ -21498,35 +21554,26 @@ async function heartbeatTick(config2, anonKey, sdkVersion, generation, onClaimTr
|
|
|
21498
21554
|
tickInProgress = false;
|
|
21499
21555
|
}
|
|
21500
21556
|
}
|
|
21501
|
-
function
|
|
21502
|
-
if (
|
|
21503
|
-
|
|
21504
|
-
|
|
21505
|
-
|
|
21506
|
-
|
|
21507
|
-
|
|
21508
|
-
|
|
21509
|
-
|
|
21510
|
-
|
|
21511
|
-
|
|
21557
|
+
function registerHeartbeatShutdownHook(config2, anonKey, sdkVersion) {
|
|
21558
|
+
if (shutdownHookRegistered) return;
|
|
21559
|
+
shutdownHookRegistered = true;
|
|
21560
|
+
registerShutdownHook({
|
|
21561
|
+
name: "heartbeat-final-report",
|
|
21562
|
+
priority: HEARTBEAT_SHUTDOWN_PRIORITY,
|
|
21563
|
+
fn: async () => {
|
|
21564
|
+
if (shutdownFired) return;
|
|
21565
|
+
shutdownFired = true;
|
|
21566
|
+
if (heartbeatTimer !== null) {
|
|
21567
|
+
clearInterval(heartbeatTimer);
|
|
21568
|
+
heartbeatTimer = null;
|
|
21569
|
+
}
|
|
21570
|
+
try {
|
|
21571
|
+
const healthReport = collectHealthReport(sdkVersion);
|
|
21572
|
+
await performInit(config2, anonKey, sdkVersion, healthReport);
|
|
21573
|
+
} catch {
|
|
21574
|
+
}
|
|
21512
21575
|
}
|
|
21513
|
-
|
|
21514
|
-
void performInit(config2, anonKey, sdkVersion, healthReport).catch(() => {
|
|
21515
|
-
}).finally(() => {
|
|
21516
|
-
removeShutdownHandlers();
|
|
21517
|
-
process.kill(process.pid, signal);
|
|
21518
|
-
});
|
|
21519
|
-
};
|
|
21520
|
-
_shutdownHandler = handler;
|
|
21521
|
-
process.once("SIGTERM", _shutdownHandler);
|
|
21522
|
-
process.once("SIGINT", _shutdownHandler);
|
|
21523
|
-
}
|
|
21524
|
-
function removeShutdownHandlers() {
|
|
21525
|
-
if (_shutdownHandler && typeof process !== "undefined") {
|
|
21526
|
-
process.removeListener("SIGTERM", _shutdownHandler);
|
|
21527
|
-
process.removeListener("SIGINT", _shutdownHandler);
|
|
21528
|
-
_shutdownHandler = null;
|
|
21529
|
-
}
|
|
21576
|
+
});
|
|
21530
21577
|
}
|
|
21531
21578
|
|
|
21532
21579
|
// src/runtime-state.ts
|
|
@@ -21628,7 +21675,7 @@ function registerGlasstrace(options) {
|
|
|
21628
21675
|
setCoreState(CoreState.REGISTERING);
|
|
21629
21676
|
startRuntimeStateWriter({
|
|
21630
21677
|
projectRoot: process.cwd(),
|
|
21631
|
-
sdkVersion: "0.
|
|
21678
|
+
sdkVersion: "0.15.1"
|
|
21632
21679
|
});
|
|
21633
21680
|
const config2 = resolveConfig(options);
|
|
21634
21681
|
if (config2.verbose) {
|
|
@@ -21644,6 +21691,11 @@ function registerGlasstrace(options) {
|
|
|
21644
21691
|
if (config2.verbose) {
|
|
21645
21692
|
console.info("[glasstrace] Not production-disabled.");
|
|
21646
21693
|
}
|
|
21694
|
+
const existingProbe = trace.getTracerProvider().getTracer("glasstrace-probe");
|
|
21695
|
+
const anotherProviderRegistered = existingProbe.constructor.name !== "ProxyTracer";
|
|
21696
|
+
if (!anotherProviderRegistered) {
|
|
21697
|
+
registerSignalHandlers();
|
|
21698
|
+
}
|
|
21647
21699
|
const anonymous = isAnonymousMode(config2);
|
|
21648
21700
|
let effectiveKey = config2.apiKey;
|
|
21649
21701
|
initAuthState(anonymous ? AuthState.ANONYMOUS : AuthState.AUTHENTICATED);
|
|
@@ -21674,8 +21726,6 @@ function registerGlasstrace(options) {
|
|
|
21674
21726
|
}
|
|
21675
21727
|
setCoreState(CoreState.KEY_PENDING);
|
|
21676
21728
|
const currentGeneration = registrationGeneration;
|
|
21677
|
-
const existingProbe = trace.getTracerProvider().getTracer("glasstrace-probe");
|
|
21678
|
-
const anotherProviderRegistered = existingProbe.constructor.name !== "ProxyTracer";
|
|
21679
21729
|
if (anotherProviderRegistered) {
|
|
21680
21730
|
if (config2.verbose) {
|
|
21681
21731
|
console.info("[glasstrace] Another OTel provider detected \u2014 using existing context manager.");
|
|
@@ -21790,8 +21840,8 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
|
|
|
21790
21840
|
if (config2.verbose) {
|
|
21791
21841
|
console.info("[glasstrace] Background init firing.");
|
|
21792
21842
|
}
|
|
21793
|
-
const healthReport = collectHealthReport("0.
|
|
21794
|
-
const initResult = await performInit(config2, anonKeyForInit, "0.
|
|
21843
|
+
const healthReport = collectHealthReport("0.15.1");
|
|
21844
|
+
const initResult = await performInit(config2, anonKeyForInit, "0.15.1", healthReport);
|
|
21795
21845
|
if (generation !== registrationGeneration) return;
|
|
21796
21846
|
const currentState = getCoreState();
|
|
21797
21847
|
if (currentState === CoreState.SHUTTING_DOWN || currentState === CoreState.SHUTDOWN) {
|
|
@@ -21814,7 +21864,7 @@ async function backgroundInit(config2, anonKeyForInit, generation) {
|
|
|
21814
21864
|
}
|
|
21815
21865
|
maybeInstallConsoleCapture();
|
|
21816
21866
|
if (didLastInitSucceed()) {
|
|
21817
|
-
startHeartbeat(config2, anonKeyForInit, "0.
|
|
21867
|
+
startHeartbeat(config2, anonKeyForInit, "0.15.1", generation, (newApiKey, accountId) => {
|
|
21818
21868
|
setAuthState(AuthState.CLAIMING);
|
|
21819
21869
|
emitLifecycleEvent("auth:claim_started", { accountId });
|
|
21820
21870
|
setResolvedApiKey(newApiKey);
|
|
@@ -21845,16 +21895,34 @@ function isDiscoveryEnabled(config2) {
|
|
|
21845
21895
|
|
|
21846
21896
|
// src/config-wrapper.ts
|
|
21847
21897
|
init_cjs_shims();
|
|
21898
|
+
function isTurbopackBuild() {
|
|
21899
|
+
if (typeof process === "undefined") return false;
|
|
21900
|
+
const argv = Array.isArray(process.argv) ? process.argv : [];
|
|
21901
|
+
if (argv.includes("--webpack")) return false;
|
|
21902
|
+
if (argv.includes("--turbopack")) return true;
|
|
21903
|
+
if (process.env?.TURBOPACK === "1") return true;
|
|
21904
|
+
return false;
|
|
21905
|
+
}
|
|
21848
21906
|
function withGlasstraceConfig(nextConfig) {
|
|
21849
21907
|
if (typeof process === "undefined" || typeof process.versions?.node !== "string") {
|
|
21850
21908
|
return nextConfig != null ? { ...nextConfig } : {};
|
|
21851
21909
|
}
|
|
21852
21910
|
const config2 = nextConfig != null ? { ...nextConfig } : {};
|
|
21853
|
-
const
|
|
21854
|
-
|
|
21855
|
-
|
|
21856
|
-
|
|
21857
|
-
|
|
21911
|
+
const bag = config2;
|
|
21912
|
+
const existingExperimental = bag.experimental ?? {};
|
|
21913
|
+
bag.experimental = {
|
|
21914
|
+
...existingExperimental,
|
|
21915
|
+
serverSourceMaps: true
|
|
21916
|
+
};
|
|
21917
|
+
if (bag.turbopack == null) {
|
|
21918
|
+
bag.turbopack = {};
|
|
21919
|
+
}
|
|
21920
|
+
if (isTurbopackBuild()) {
|
|
21921
|
+
warnTurbopackLimitationOnce();
|
|
21922
|
+
}
|
|
21923
|
+
const distDir = typeof bag.distDir === "string" ? bag.distDir : ".next";
|
|
21924
|
+
const existingWebpack = bag.webpack;
|
|
21925
|
+
bag.webpack = (webpackConfig, context2) => {
|
|
21858
21926
|
let result = webpackConfig;
|
|
21859
21927
|
if (typeof existingWebpack === "function") {
|
|
21860
21928
|
result = existingWebpack(webpackConfig, context2);
|
|
@@ -21881,6 +21949,14 @@ function withGlasstraceConfig(nextConfig) {
|
|
|
21881
21949
|
};
|
|
21882
21950
|
return config2;
|
|
21883
21951
|
}
|
|
21952
|
+
var _turbopackWarningEmitted = false;
|
|
21953
|
+
function warnTurbopackLimitationOnce() {
|
|
21954
|
+
if (_turbopackWarningEmitted) return;
|
|
21955
|
+
_turbopackWarningEmitted = true;
|
|
21956
|
+
console.warn(
|
|
21957
|
+
"[glasstrace] Turbopack detected. Source-map upload currently runs only under webpack \u2014 run `next build --webpack` to upload source maps, or wait for the Turbopack port in a future SDK release."
|
|
21958
|
+
);
|
|
21959
|
+
}
|
|
21884
21960
|
async function handleSourceMapUpload(distDir) {
|
|
21885
21961
|
try {
|
|
21886
21962
|
const apiKey = process.env.GLASSTRACE_API_KEY;
|