@soda-gql/common 0.11.10 → 0.11.12

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.
Files changed (63) hide show
  1. package/dist/{canonical-id-BJahCcrS.mjs → canonical-id-9alor9gv.mjs} +66 -7
  2. package/dist/canonical-id-9alor9gv.mjs.map +1 -0
  3. package/dist/{canonical-id-CgMNOZyn.cjs → canonical-id-DHdeYIsT.cjs} +76 -5
  4. package/dist/canonical-id-DHdeYIsT.cjs.map +1 -0
  5. package/dist/canonical-id.cjs +5 -3
  6. package/dist/canonical-id.d.cts +2 -2
  7. package/dist/canonical-id.d.mts +2 -2
  8. package/dist/canonical-id.mjs +3 -3
  9. package/dist/{index-Cm2Zwk9m.d.cts → index-AqkJhrm3.d.mts} +53 -3
  10. package/dist/index-AqkJhrm3.d.mts.map +1 -0
  11. package/dist/index-BCu9PNbZ.d.mts +139 -0
  12. package/dist/index-BCu9PNbZ.d.mts.map +1 -0
  13. package/dist/{index-D1tzB3W5.d.cts → index-BMa2_rDb.d.mts} +6 -35
  14. package/dist/index-BMa2_rDb.d.mts.map +1 -0
  15. package/dist/index-BMl8pzFY.d.cts +139 -0
  16. package/dist/index-BMl8pzFY.d.cts.map +1 -0
  17. package/dist/{index-B424kKYS.d.mts → index-CbQyueYV.d.cts} +6 -35
  18. package/dist/index-CbQyueYV.d.cts.map +1 -0
  19. package/dist/{index-CPpVc8Id.d.mts → index-DZSebwar.d.cts} +53 -3
  20. package/dist/index-DZSebwar.d.cts.map +1 -0
  21. package/dist/index-Dit86qkX.d.mts.map +1 -1
  22. package/dist/index.cjs +9 -7
  23. package/dist/index.d.cts +4 -4
  24. package/dist/index.d.mts +4 -4
  25. package/dist/index.mjs +4 -4
  26. package/dist/portable-B3K3IE7E.cjs +239 -0
  27. package/dist/portable-B3K3IE7E.cjs.map +1 -0
  28. package/dist/portable-BFrcBOaX.mjs +196 -0
  29. package/dist/portable-BFrcBOaX.mjs.map +1 -0
  30. package/dist/portable.cjs +2 -5
  31. package/dist/portable.d.cts +2 -2
  32. package/dist/portable.d.mts +2 -2
  33. package/dist/portable.mjs +2 -2
  34. package/dist/utils-Rs7YbafF.cjs +431 -0
  35. package/dist/utils-Rs7YbafF.cjs.map +1 -0
  36. package/dist/utils-ZCE_eqCf.mjs +371 -0
  37. package/dist/utils-ZCE_eqCf.mjs.map +1 -0
  38. package/dist/utils.cjs +4 -1
  39. package/dist/utils.d.cts +2 -2
  40. package/dist/utils.d.mts +2 -2
  41. package/dist/utils.mjs +2 -2
  42. package/dist/{zod-C_6JfuYV.cjs → zod-DjI3IUH3.cjs} +2 -2
  43. package/dist/{zod-C_6JfuYV.cjs.map → zod-DjI3IUH3.cjs.map} +1 -1
  44. package/dist/zod.cjs +1 -1
  45. package/package.json +1 -1
  46. package/dist/canonical-id-BJahCcrS.mjs.map +0 -1
  47. package/dist/canonical-id-CgMNOZyn.cjs.map +0 -1
  48. package/dist/index-B424kKYS.d.mts.map +0 -1
  49. package/dist/index-CPpVc8Id.d.mts.map +0 -1
  50. package/dist/index-Cm2Zwk9m.d.cts.map +0 -1
  51. package/dist/index-D1tzB3W5.d.cts.map +0 -1
  52. package/dist/index-Dv8spPt0.d.mts +0 -61
  53. package/dist/index-Dv8spPt0.d.mts.map +0 -1
  54. package/dist/index-LaXfl_e_.d.cts +0 -61
  55. package/dist/index-LaXfl_e_.d.cts.map +0 -1
  56. package/dist/portable-BT3ahkQN.mjs +0 -391
  57. package/dist/portable-BT3ahkQN.mjs.map +0 -1
  58. package/dist/portable-cJqkfeHw.cjs +0 -451
  59. package/dist/portable-cJqkfeHw.cjs.map +0 -1
  60. package/dist/utils-CsTwS1dw.cjs +0 -148
  61. package/dist/utils-CsTwS1dw.cjs.map +0 -1
  62. package/dist/utils-DLEgAn7q.mjs +0 -106
  63. package/dist/utils-DLEgAn7q.mjs.map +0 -1
@@ -1,11 +1,28 @@
1
- import { i as normalizePath } from "./utils-DLEgAn7q.mjs";
2
- import { isAbsolute, resolve } from "node:path";
1
+ import { s as normalizePath } from "./utils-ZCE_eqCf.mjs";
2
+ import { isAbsolute, relative, resolve } from "node:path";
3
3
  import z$1 from "zod";
4
4
 
5
5
  //#region packages/common/src/canonical-id/canonical-id.ts
6
6
  const canonicalIdSeparator = "::";
7
7
  const CanonicalIdSchema = z$1.string();
8
- const createCanonicalId = (filePath, astPath) => {
8
+ /**
9
+ * Create a canonical ID from a file path and AST path.
10
+ *
11
+ * @param filePath - The file path (absolute, or relative if baseDir is provided)
12
+ * @param astPath - The AST path identifying the definition within the file
13
+ * @param options - Optional configuration including baseDir for relative path support
14
+ * @returns A canonical ID in the format "{path}::{astPath}"
15
+ */
16
+ const createCanonicalId = (filePath, astPath, options) => {
17
+ const { baseDir } = options ?? {};
18
+ if (baseDir) {
19
+ const absolutePath = isAbsolute(filePath) ? filePath : resolve(baseDir, filePath);
20
+ const resolved$1 = resolve(absolutePath);
21
+ const relativePath = relative(baseDir, resolved$1);
22
+ const normalized$1 = normalizePath(relativePath);
23
+ const idParts$1 = [normalized$1, astPath];
24
+ return idParts$1.join(canonicalIdSeparator);
25
+ }
9
26
  if (!isAbsolute(filePath)) {
10
27
  throw new Error("[INTERNAL] CANONICAL_ID_REQUIRES_ABSOLUTE_PATH");
11
28
  }
@@ -15,6 +32,16 @@ const createCanonicalId = (filePath, astPath) => {
15
32
  return idParts.join(canonicalIdSeparator);
16
33
  };
17
34
  /**
35
+ * Check if a canonical ID uses a relative path.
36
+ * Relative canonical IDs do not start with '/'.
37
+ *
38
+ * @param canonicalId - The canonical ID to check
39
+ * @returns true if the canonical ID uses a relative path
40
+ */
41
+ const isRelativeCanonicalId = (canonicalId) => {
42
+ return !canonicalId.startsWith("/");
43
+ };
44
+ /**
18
45
  * Parse a canonical ID into its components.
19
46
  * @param canonicalId - The canonical ID to parse (e.g., "/app/src/user.ts::userFragment")
20
47
  * @returns An object with filePath and astPath
@@ -32,6 +59,38 @@ const parseCanonicalId = (canonicalId) => {
32
59
  astPath: canonicalId.slice(idx + canonicalIdSeparator.length)
33
60
  };
34
61
  };
62
+ /**
63
+ * Validate a canonical ID format.
64
+ * A valid canonical ID has format: "{filePath}::{astPath}"
65
+ * where both filePath and astPath are non-empty.
66
+ *
67
+ * @param canonicalId - The canonical ID to validate
68
+ * @returns Validation result with isValid boolean and optional error reason
69
+ */
70
+ const validateCanonicalId = (canonicalId) => {
71
+ const idx = canonicalId.indexOf(canonicalIdSeparator);
72
+ if (idx === -1) {
73
+ return {
74
+ isValid: false,
75
+ reason: "missing '::' separator"
76
+ };
77
+ }
78
+ const filePath = canonicalId.slice(0, idx);
79
+ const astPath = canonicalId.slice(idx + canonicalIdSeparator.length);
80
+ if (filePath === "") {
81
+ return {
82
+ isValid: false,
83
+ reason: "empty file path"
84
+ };
85
+ }
86
+ if (astPath === "") {
87
+ return {
88
+ isValid: false,
89
+ reason: "empty AST path"
90
+ };
91
+ }
92
+ return { isValid: true };
93
+ };
35
94
 
36
95
  //#endregion
37
96
  //#region packages/common/src/canonical-id/path-tracker.ts
@@ -68,7 +127,7 @@ const _buildAstPath = (stack) => {
68
127
  * ```
69
128
  */
70
129
  const createCanonicalTracker = (options) => {
71
- const { filePath, getExportName } = options;
130
+ const { filePath, baseDir, getExportName } = options;
72
131
  const scopeStack = [];
73
132
  const occurrenceCounters = new Map();
74
133
  const usedPaths = new Set();
@@ -124,7 +183,7 @@ const createCanonicalTracker = (options) => {
124
183
  };
125
184
  },
126
185
  resolveCanonicalId(astPath) {
127
- return createCanonicalId(filePath, astPath);
186
+ return createCanonicalId(filePath, astPath, { baseDir });
128
187
  },
129
188
  registerExportBinding(local, exported) {
130
189
  exportBindings.set(local, exported);
@@ -169,5 +228,5 @@ const buildAstPath = (stack) => {
169
228
  };
170
229
 
171
230
  //#endregion
172
- export { CanonicalIdSchema as a, createPathTracker as i, createCanonicalTracker as n, createCanonicalId as o, createOccurrenceTracker as r, parseCanonicalId as s, buildAstPath as t };
173
- //# sourceMappingURL=canonical-id-BJahCcrS.mjs.map
231
+ export { CanonicalIdSchema as a, parseCanonicalId as c, createPathTracker as i, validateCanonicalId as l, createCanonicalTracker as n, createCanonicalId as o, createOccurrenceTracker as r, isRelativeCanonicalId as s, buildAstPath as t };
232
+ //# sourceMappingURL=canonical-id-9alor9gv.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-id-9alor9gv.mjs","names":["CanonicalIdSchema: z.ZodType<CanonicalId>","z","resolved","normalized","idParts","scopeStack: ScopeFrame[]","frame: ScopeFrame","exportBinding: string | undefined"],"sources":["../src/canonical-id/canonical-id.ts","../src/canonical-id/path-tracker.ts"],"sourcesContent":["import { isAbsolute, relative, resolve } from \"node:path\";\nimport z from \"zod\";\nimport { normalizePath } from \"../utils\";\n\nexport type CanonicalId = string & { readonly __brand: \"CanonicalId\" };\n\nconst canonicalIdSeparator = \"::\" as const;\n\nexport const CanonicalIdSchema: z.ZodType<CanonicalId> = z.string() as unknown as z.ZodType<CanonicalId>;\n\n/**\n * Options for creating a canonical ID.\n */\nexport type CreateCanonicalIdOptions = {\n /**\n * Base directory for relative path computation.\n * When provided, the canonical ID will use a relative path from baseDir.\n * When undefined, an absolute path is required and used as-is.\n */\n readonly baseDir?: string;\n};\n\n/**\n * Create a canonical ID from a file path and AST path.\n *\n * @param filePath - The file path (absolute, or relative if baseDir is provided)\n * @param astPath - The AST path identifying the definition within the file\n * @param options - Optional configuration including baseDir for relative path support\n * @returns A canonical ID in the format \"{path}::{astPath}\"\n */\nexport const createCanonicalId = (filePath: string, astPath: string, options?: CreateCanonicalIdOptions): CanonicalId => {\n const { baseDir } = options ?? {};\n\n if (baseDir) {\n // With baseDir, compute relative path\n const absolutePath = isAbsolute(filePath) ? filePath : resolve(baseDir, filePath);\n const resolved = resolve(absolutePath);\n const relativePath = relative(baseDir, resolved);\n const normalized = normalizePath(relativePath);\n\n const idParts = [normalized, astPath];\n return idParts.join(canonicalIdSeparator) as CanonicalId;\n }\n\n // Without baseDir, require absolute path (legacy behavior)\n if (!isAbsolute(filePath)) {\n throw new Error(\"[INTERNAL] CANONICAL_ID_REQUIRES_ABSOLUTE_PATH\");\n }\n\n const resolved = resolve(filePath);\n const normalized = normalizePath(resolved);\n\n // Create a 2-part ID: {absPath}::{astPath}\n // astPath uniquely identifies the definition's location in the AST (e.g., \"MyComponent.useQuery.def\")\n const idParts = [normalized, astPath];\n\n return idParts.join(canonicalIdSeparator) as CanonicalId;\n};\n\n/**\n * Check if a canonical ID uses a relative path.\n * Relative canonical IDs do not start with '/'.\n *\n * @param canonicalId - The canonical ID to check\n * @returns true if the canonical ID uses a relative path\n */\nexport const isRelativeCanonicalId = (canonicalId: CanonicalId | string): boolean => {\n return !canonicalId.startsWith(\"/\");\n};\n\n/**\n * Parse a canonical ID into its components.\n * @param canonicalId - The canonical ID to parse (e.g., \"/app/src/user.ts::userFragment\")\n * @returns An object with filePath and astPath\n */\nexport const parseCanonicalId = (\n canonicalId: CanonicalId | string,\n): {\n filePath: string;\n astPath: string;\n} => {\n const idx = canonicalId.indexOf(canonicalIdSeparator);\n if (idx === -1) {\n return { filePath: canonicalId, astPath: \"\" };\n }\n return {\n filePath: canonicalId.slice(0, idx),\n astPath: canonicalId.slice(idx + canonicalIdSeparator.length),\n };\n};\n\n/**\n * Validation result for canonical ID format.\n */\nexport type CanonicalIdValidationResult = { readonly isValid: true } | { readonly isValid: false; readonly reason: string };\n\n/**\n * Validate a canonical ID format.\n * A valid canonical ID has format: \"{filePath}::{astPath}\"\n * where both filePath and astPath are non-empty.\n *\n * @param canonicalId - The canonical ID to validate\n * @returns Validation result with isValid boolean and optional error reason\n */\nexport const validateCanonicalId = (canonicalId: string): CanonicalIdValidationResult => {\n const idx = canonicalId.indexOf(canonicalIdSeparator);\n\n if (idx === -1) {\n return { isValid: false, reason: \"missing '::' separator\" };\n }\n\n const filePath = canonicalId.slice(0, idx);\n const astPath = canonicalId.slice(idx + canonicalIdSeparator.length);\n\n if (filePath === \"\") {\n return { isValid: false, reason: \"empty file path\" };\n }\n\n if (astPath === \"\") {\n return { isValid: false, reason: \"empty AST path\" };\n }\n\n return { isValid: true };\n};\n","/**\n * Canonical path tracker for AST traversal.\n *\n * This module provides a stateful helper that tracks scope information during\n * AST traversal to generate canonical IDs. It's designed to integrate with\n * existing plugin visitor patterns (Babel, SWC, TypeScript) without requiring\n * a separate AST traversal.\n *\n * Usage pattern:\n * 1. Plugin creates tracker at file/program entry\n * 2. Plugin calls enterScope/exitScope during its traversal\n * 3. Plugin calls registerDefinition when discovering GQL definitions\n * 4. Tracker provides canonical ID information\n */\n\nimport type { CanonicalId } from \"./canonical-id\";\nimport { createCanonicalId } from \"./canonical-id\";\n\n/**\n * Scope frame for tracking AST path segments\n */\nexport type ScopeFrame = {\n /** Name segment (e.g., \"MyComponent\", \"useQuery\", \"arrow#1\") */\n readonly nameSegment: string;\n /** Kind of scope */\n readonly kind: \"function\" | \"class\" | \"variable\" | \"property\" | \"method\" | \"expression\";\n /** Occurrence index for disambiguation */\n readonly occurrence: number;\n};\n\n/**\n * Opaque handle for scope tracking\n */\nexport type ScopeHandle = {\n readonly __brand: \"ScopeHandle\";\n readonly depth: number;\n};\n\n/**\n * Canonical path tracker interface\n */\nexport interface CanonicalPathTracker {\n /**\n * Enter a new scope during traversal\n * @param options Scope information\n * @returns Handle to use when exiting the scope\n */\n enterScope(options: { segment: string; kind: ScopeFrame[\"kind\"]; stableKey?: string }): ScopeHandle;\n\n /**\n * Exit a scope during traversal\n * @param handle Handle returned from enterScope\n */\n exitScope(handle: ScopeHandle): void;\n\n /**\n * Register a definition discovered during traversal\n * @returns Definition metadata including astPath and canonical ID information\n */\n registerDefinition(): {\n astPath: string;\n isTopLevel: boolean;\n exportBinding?: string;\n };\n\n /**\n * Resolve a canonical ID from an astPath\n * @param astPath AST path string\n * @returns Canonical ID\n */\n resolveCanonicalId(astPath: string): CanonicalId;\n\n /**\n * Register an export binding\n * @param local Local variable name\n * @param exported Exported name\n */\n registerExportBinding(local: string, exported: string): void;\n\n /**\n * Get current scope depth\n * @returns Current depth (0 = top level)\n */\n currentDepth(): number;\n}\n\n/**\n * Build AST path from scope stack (internal helper)\n */\nconst _buildAstPath = (stack: readonly ScopeFrame[]): string => {\n return stack.map((frame) => frame.nameSegment).join(\".\");\n};\n\n/**\n * Create a canonical path tracker\n *\n * @param options Configuration options\n * @returns Tracker instance\n *\n * @example\n * ```typescript\n * // In a Babel plugin\n * const tracker = createCanonicalTracker({ filePath: state.filename });\n *\n * const visitor = {\n * FunctionDeclaration: {\n * enter(path) {\n * const handle = tracker.enterScope({\n * segment: path.node.id.name,\n * kind: 'function'\n * });\n * },\n * exit(path) {\n * tracker.exitScope(handle);\n * }\n * }\n * };\n * ```\n */\nexport const createCanonicalTracker = (options: {\n filePath: string;\n /**\n * Base directory for relative path computation in canonical IDs.\n * When provided, canonical IDs will use relative paths from baseDir.\n */\n baseDir?: string;\n getExportName?: (localName: string) => string | undefined;\n}): CanonicalPathTracker => {\n const { filePath, baseDir, getExportName } = options;\n\n // Scope stack\n const scopeStack: ScopeFrame[] = [];\n\n // Occurrence counters for disambiguating duplicate names\n const occurrenceCounters = new Map<string, number>();\n\n // Used paths for ensuring uniqueness\n const usedPaths = new Set<string>();\n\n // Export bindings map\n const exportBindings = new Map<string, string>();\n\n const getNextOccurrence = (key: string): number => {\n const current = occurrenceCounters.get(key) ?? 0;\n occurrenceCounters.set(key, current + 1);\n return current;\n };\n\n const ensureUniquePath = (basePath: string): string => {\n let path = basePath;\n let suffix = 0;\n while (usedPaths.has(path)) {\n suffix++;\n path = `${basePath}$${suffix}`;\n }\n usedPaths.add(path);\n return path;\n };\n\n return {\n enterScope({ segment, kind, stableKey }): ScopeHandle {\n const key = stableKey ?? `${kind}:${segment}`;\n const occurrence = getNextOccurrence(key);\n\n const frame: ScopeFrame = {\n nameSegment: segment,\n kind,\n occurrence,\n };\n\n scopeStack.push(frame);\n\n return {\n __brand: \"ScopeHandle\",\n depth: scopeStack.length - 1,\n } as ScopeHandle;\n },\n\n exitScope(handle: ScopeHandle): void {\n // Validate handle depth matches current stack\n if (handle.depth !== scopeStack.length - 1) {\n throw new Error(`[INTERNAL] Invalid scope exit: expected depth ${scopeStack.length - 1}, got ${handle.depth}`);\n }\n scopeStack.pop();\n },\n\n registerDefinition(): {\n astPath: string;\n isTopLevel: boolean;\n exportBinding?: string;\n } {\n const basePath = _buildAstPath(scopeStack);\n const astPath = ensureUniquePath(basePath);\n const isTopLevel = scopeStack.length === 0;\n\n // Check export binding if provided\n let exportBinding: string | undefined;\n if (getExportName && isTopLevel) {\n // For top-level definitions, try to get export name\n // This is a simplified version - real logic depends on how the definition is bound\n exportBinding = undefined;\n }\n\n return {\n astPath,\n isTopLevel,\n exportBinding,\n };\n },\n\n resolveCanonicalId(astPath: string): CanonicalId {\n return createCanonicalId(filePath, astPath, { baseDir });\n },\n\n registerExportBinding(local: string, exported: string): void {\n exportBindings.set(local, exported);\n },\n\n currentDepth(): number {\n return scopeStack.length;\n },\n };\n};\n\n/**\n * Helper to create occurrence tracker (for backward compatibility)\n */\nexport const createOccurrenceTracker = (): {\n getNextOccurrence: (key: string) => number;\n} => {\n const occurrenceCounters = new Map<string, number>();\n\n return {\n getNextOccurrence(key: string): number {\n const current = occurrenceCounters.get(key) ?? 0;\n occurrenceCounters.set(key, current + 1);\n return current;\n },\n };\n};\n\n/**\n * Helper to create path tracker (for backward compatibility)\n */\nexport const createPathTracker = (): {\n ensureUniquePath: (basePath: string) => string;\n} => {\n const usedPaths = new Set<string>();\n\n return {\n ensureUniquePath(basePath: string): string {\n let path = basePath;\n let suffix = 0;\n while (usedPaths.has(path)) {\n suffix++;\n path = `${basePath}$${suffix}`;\n }\n usedPaths.add(path);\n return path;\n },\n };\n};\n\n/**\n * Build AST path from scope stack (for backward compatibility)\n */\nexport const buildAstPath = (stack: readonly ScopeFrame[]): string => {\n return stack.map((frame) => frame.nameSegment).join(\".\");\n};\n"],"mappings":"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"}
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
25
25
  }) : target, mod));
26
26
 
27
27
  //#endregion
28
- const require_utils = require('./utils-CsTwS1dw.cjs');
28
+ const require_utils = require('./utils-Rs7YbafF.cjs');
29
29
  let node_path = require("node:path");
30
30
  let zod = require("zod");
31
31
  zod = __toESM(zod);
@@ -33,7 +33,24 @@ zod = __toESM(zod);
33
33
  //#region packages/common/src/canonical-id/canonical-id.ts
34
34
  const canonicalIdSeparator = "::";
35
35
  const CanonicalIdSchema = zod.default.string();
36
- const createCanonicalId = (filePath, astPath) => {
36
+ /**
37
+ * Create a canonical ID from a file path and AST path.
38
+ *
39
+ * @param filePath - The file path (absolute, or relative if baseDir is provided)
40
+ * @param astPath - The AST path identifying the definition within the file
41
+ * @param options - Optional configuration including baseDir for relative path support
42
+ * @returns A canonical ID in the format "{path}::{astPath}"
43
+ */
44
+ const createCanonicalId = (filePath, astPath, options) => {
45
+ const { baseDir } = options ?? {};
46
+ if (baseDir) {
47
+ const absolutePath = (0, node_path.isAbsolute)(filePath) ? filePath : (0, node_path.resolve)(baseDir, filePath);
48
+ const resolved$1 = (0, node_path.resolve)(absolutePath);
49
+ const relativePath = (0, node_path.relative)(baseDir, resolved$1);
50
+ const normalized$1 = require_utils.normalizePath(relativePath);
51
+ const idParts$1 = [normalized$1, astPath];
52
+ return idParts$1.join(canonicalIdSeparator);
53
+ }
37
54
  if (!(0, node_path.isAbsolute)(filePath)) {
38
55
  throw new Error("[INTERNAL] CANONICAL_ID_REQUIRES_ABSOLUTE_PATH");
39
56
  }
@@ -43,6 +60,16 @@ const createCanonicalId = (filePath, astPath) => {
43
60
  return idParts.join(canonicalIdSeparator);
44
61
  };
45
62
  /**
63
+ * Check if a canonical ID uses a relative path.
64
+ * Relative canonical IDs do not start with '/'.
65
+ *
66
+ * @param canonicalId - The canonical ID to check
67
+ * @returns true if the canonical ID uses a relative path
68
+ */
69
+ const isRelativeCanonicalId = (canonicalId) => {
70
+ return !canonicalId.startsWith("/");
71
+ };
72
+ /**
46
73
  * Parse a canonical ID into its components.
47
74
  * @param canonicalId - The canonical ID to parse (e.g., "/app/src/user.ts::userFragment")
48
75
  * @returns An object with filePath and astPath
@@ -60,6 +87,38 @@ const parseCanonicalId = (canonicalId) => {
60
87
  astPath: canonicalId.slice(idx + canonicalIdSeparator.length)
61
88
  };
62
89
  };
90
+ /**
91
+ * Validate a canonical ID format.
92
+ * A valid canonical ID has format: "{filePath}::{astPath}"
93
+ * where both filePath and astPath are non-empty.
94
+ *
95
+ * @param canonicalId - The canonical ID to validate
96
+ * @returns Validation result with isValid boolean and optional error reason
97
+ */
98
+ const validateCanonicalId = (canonicalId) => {
99
+ const idx = canonicalId.indexOf(canonicalIdSeparator);
100
+ if (idx === -1) {
101
+ return {
102
+ isValid: false,
103
+ reason: "missing '::' separator"
104
+ };
105
+ }
106
+ const filePath = canonicalId.slice(0, idx);
107
+ const astPath = canonicalId.slice(idx + canonicalIdSeparator.length);
108
+ if (filePath === "") {
109
+ return {
110
+ isValid: false,
111
+ reason: "empty file path"
112
+ };
113
+ }
114
+ if (astPath === "") {
115
+ return {
116
+ isValid: false,
117
+ reason: "empty AST path"
118
+ };
119
+ }
120
+ return { isValid: true };
121
+ };
63
122
 
64
123
  //#endregion
65
124
  //#region packages/common/src/canonical-id/path-tracker.ts
@@ -96,7 +155,7 @@ const _buildAstPath = (stack) => {
96
155
  * ```
97
156
  */
98
157
  const createCanonicalTracker = (options) => {
99
- const { filePath, getExportName } = options;
158
+ const { filePath, baseDir, getExportName } = options;
100
159
  const scopeStack = [];
101
160
  const occurrenceCounters = new Map();
102
161
  const usedPaths = new Set();
@@ -152,7 +211,7 @@ const createCanonicalTracker = (options) => {
152
211
  };
153
212
  },
154
213
  resolveCanonicalId(astPath) {
155
- return createCanonicalId(filePath, astPath);
214
+ return createCanonicalId(filePath, astPath, { baseDir });
156
215
  },
157
216
  registerExportBinding(local, exported) {
158
217
  exportBindings.set(local, exported);
@@ -239,10 +298,22 @@ Object.defineProperty(exports, 'createPathTracker', {
239
298
  return createPathTracker;
240
299
  }
241
300
  });
301
+ Object.defineProperty(exports, 'isRelativeCanonicalId', {
302
+ enumerable: true,
303
+ get: function () {
304
+ return isRelativeCanonicalId;
305
+ }
306
+ });
242
307
  Object.defineProperty(exports, 'parseCanonicalId', {
243
308
  enumerable: true,
244
309
  get: function () {
245
310
  return parseCanonicalId;
246
311
  }
247
312
  });
248
- //# sourceMappingURL=canonical-id-CgMNOZyn.cjs.map
313
+ Object.defineProperty(exports, 'validateCanonicalId', {
314
+ enumerable: true,
315
+ get: function () {
316
+ return validateCanonicalId;
317
+ }
318
+ });
319
+ //# sourceMappingURL=canonical-id-DHdeYIsT.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"canonical-id-DHdeYIsT.cjs","names":["CanonicalIdSchema: z.ZodType<CanonicalId>","z","resolved","normalized","normalizePath","idParts","scopeStack: ScopeFrame[]","frame: ScopeFrame","exportBinding: string | undefined"],"sources":["../src/canonical-id/canonical-id.ts","../src/canonical-id/path-tracker.ts"],"sourcesContent":["import { isAbsolute, relative, resolve } from \"node:path\";\nimport z from \"zod\";\nimport { normalizePath } from \"../utils\";\n\nexport type CanonicalId = string & { readonly __brand: \"CanonicalId\" };\n\nconst canonicalIdSeparator = \"::\" as const;\n\nexport const CanonicalIdSchema: z.ZodType<CanonicalId> = z.string() as unknown as z.ZodType<CanonicalId>;\n\n/**\n * Options for creating a canonical ID.\n */\nexport type CreateCanonicalIdOptions = {\n /**\n * Base directory for relative path computation.\n * When provided, the canonical ID will use a relative path from baseDir.\n * When undefined, an absolute path is required and used as-is.\n */\n readonly baseDir?: string;\n};\n\n/**\n * Create a canonical ID from a file path and AST path.\n *\n * @param filePath - The file path (absolute, or relative if baseDir is provided)\n * @param astPath - The AST path identifying the definition within the file\n * @param options - Optional configuration including baseDir for relative path support\n * @returns A canonical ID in the format \"{path}::{astPath}\"\n */\nexport const createCanonicalId = (filePath: string, astPath: string, options?: CreateCanonicalIdOptions): CanonicalId => {\n const { baseDir } = options ?? {};\n\n if (baseDir) {\n // With baseDir, compute relative path\n const absolutePath = isAbsolute(filePath) ? filePath : resolve(baseDir, filePath);\n const resolved = resolve(absolutePath);\n const relativePath = relative(baseDir, resolved);\n const normalized = normalizePath(relativePath);\n\n const idParts = [normalized, astPath];\n return idParts.join(canonicalIdSeparator) as CanonicalId;\n }\n\n // Without baseDir, require absolute path (legacy behavior)\n if (!isAbsolute(filePath)) {\n throw new Error(\"[INTERNAL] CANONICAL_ID_REQUIRES_ABSOLUTE_PATH\");\n }\n\n const resolved = resolve(filePath);\n const normalized = normalizePath(resolved);\n\n // Create a 2-part ID: {absPath}::{astPath}\n // astPath uniquely identifies the definition's location in the AST (e.g., \"MyComponent.useQuery.def\")\n const idParts = [normalized, astPath];\n\n return idParts.join(canonicalIdSeparator) as CanonicalId;\n};\n\n/**\n * Check if a canonical ID uses a relative path.\n * Relative canonical IDs do not start with '/'.\n *\n * @param canonicalId - The canonical ID to check\n * @returns true if the canonical ID uses a relative path\n */\nexport const isRelativeCanonicalId = (canonicalId: CanonicalId | string): boolean => {\n return !canonicalId.startsWith(\"/\");\n};\n\n/**\n * Parse a canonical ID into its components.\n * @param canonicalId - The canonical ID to parse (e.g., \"/app/src/user.ts::userFragment\")\n * @returns An object with filePath and astPath\n */\nexport const parseCanonicalId = (\n canonicalId: CanonicalId | string,\n): {\n filePath: string;\n astPath: string;\n} => {\n const idx = canonicalId.indexOf(canonicalIdSeparator);\n if (idx === -1) {\n return { filePath: canonicalId, astPath: \"\" };\n }\n return {\n filePath: canonicalId.slice(0, idx),\n astPath: canonicalId.slice(idx + canonicalIdSeparator.length),\n };\n};\n\n/**\n * Validation result for canonical ID format.\n */\nexport type CanonicalIdValidationResult = { readonly isValid: true } | { readonly isValid: false; readonly reason: string };\n\n/**\n * Validate a canonical ID format.\n * A valid canonical ID has format: \"{filePath}::{astPath}\"\n * where both filePath and astPath are non-empty.\n *\n * @param canonicalId - The canonical ID to validate\n * @returns Validation result with isValid boolean and optional error reason\n */\nexport const validateCanonicalId = (canonicalId: string): CanonicalIdValidationResult => {\n const idx = canonicalId.indexOf(canonicalIdSeparator);\n\n if (idx === -1) {\n return { isValid: false, reason: \"missing '::' separator\" };\n }\n\n const filePath = canonicalId.slice(0, idx);\n const astPath = canonicalId.slice(idx + canonicalIdSeparator.length);\n\n if (filePath === \"\") {\n return { isValid: false, reason: \"empty file path\" };\n }\n\n if (astPath === \"\") {\n return { isValid: false, reason: \"empty AST path\" };\n }\n\n return { isValid: true };\n};\n","/**\n * Canonical path tracker for AST traversal.\n *\n * This module provides a stateful helper that tracks scope information during\n * AST traversal to generate canonical IDs. It's designed to integrate with\n * existing plugin visitor patterns (Babel, SWC, TypeScript) without requiring\n * a separate AST traversal.\n *\n * Usage pattern:\n * 1. Plugin creates tracker at file/program entry\n * 2. Plugin calls enterScope/exitScope during its traversal\n * 3. Plugin calls registerDefinition when discovering GQL definitions\n * 4. Tracker provides canonical ID information\n */\n\nimport type { CanonicalId } from \"./canonical-id\";\nimport { createCanonicalId } from \"./canonical-id\";\n\n/**\n * Scope frame for tracking AST path segments\n */\nexport type ScopeFrame = {\n /** Name segment (e.g., \"MyComponent\", \"useQuery\", \"arrow#1\") */\n readonly nameSegment: string;\n /** Kind of scope */\n readonly kind: \"function\" | \"class\" | \"variable\" | \"property\" | \"method\" | \"expression\";\n /** Occurrence index for disambiguation */\n readonly occurrence: number;\n};\n\n/**\n * Opaque handle for scope tracking\n */\nexport type ScopeHandle = {\n readonly __brand: \"ScopeHandle\";\n readonly depth: number;\n};\n\n/**\n * Canonical path tracker interface\n */\nexport interface CanonicalPathTracker {\n /**\n * Enter a new scope during traversal\n * @param options Scope information\n * @returns Handle to use when exiting the scope\n */\n enterScope(options: { segment: string; kind: ScopeFrame[\"kind\"]; stableKey?: string }): ScopeHandle;\n\n /**\n * Exit a scope during traversal\n * @param handle Handle returned from enterScope\n */\n exitScope(handle: ScopeHandle): void;\n\n /**\n * Register a definition discovered during traversal\n * @returns Definition metadata including astPath and canonical ID information\n */\n registerDefinition(): {\n astPath: string;\n isTopLevel: boolean;\n exportBinding?: string;\n };\n\n /**\n * Resolve a canonical ID from an astPath\n * @param astPath AST path string\n * @returns Canonical ID\n */\n resolveCanonicalId(astPath: string): CanonicalId;\n\n /**\n * Register an export binding\n * @param local Local variable name\n * @param exported Exported name\n */\n registerExportBinding(local: string, exported: string): void;\n\n /**\n * Get current scope depth\n * @returns Current depth (0 = top level)\n */\n currentDepth(): number;\n}\n\n/**\n * Build AST path from scope stack (internal helper)\n */\nconst _buildAstPath = (stack: readonly ScopeFrame[]): string => {\n return stack.map((frame) => frame.nameSegment).join(\".\");\n};\n\n/**\n * Create a canonical path tracker\n *\n * @param options Configuration options\n * @returns Tracker instance\n *\n * @example\n * ```typescript\n * // In a Babel plugin\n * const tracker = createCanonicalTracker({ filePath: state.filename });\n *\n * const visitor = {\n * FunctionDeclaration: {\n * enter(path) {\n * const handle = tracker.enterScope({\n * segment: path.node.id.name,\n * kind: 'function'\n * });\n * },\n * exit(path) {\n * tracker.exitScope(handle);\n * }\n * }\n * };\n * ```\n */\nexport const createCanonicalTracker = (options: {\n filePath: string;\n /**\n * Base directory for relative path computation in canonical IDs.\n * When provided, canonical IDs will use relative paths from baseDir.\n */\n baseDir?: string;\n getExportName?: (localName: string) => string | undefined;\n}): CanonicalPathTracker => {\n const { filePath, baseDir, getExportName } = options;\n\n // Scope stack\n const scopeStack: ScopeFrame[] = [];\n\n // Occurrence counters for disambiguating duplicate names\n const occurrenceCounters = new Map<string, number>();\n\n // Used paths for ensuring uniqueness\n const usedPaths = new Set<string>();\n\n // Export bindings map\n const exportBindings = new Map<string, string>();\n\n const getNextOccurrence = (key: string): number => {\n const current = occurrenceCounters.get(key) ?? 0;\n occurrenceCounters.set(key, current + 1);\n return current;\n };\n\n const ensureUniquePath = (basePath: string): string => {\n let path = basePath;\n let suffix = 0;\n while (usedPaths.has(path)) {\n suffix++;\n path = `${basePath}$${suffix}`;\n }\n usedPaths.add(path);\n return path;\n };\n\n return {\n enterScope({ segment, kind, stableKey }): ScopeHandle {\n const key = stableKey ?? `${kind}:${segment}`;\n const occurrence = getNextOccurrence(key);\n\n const frame: ScopeFrame = {\n nameSegment: segment,\n kind,\n occurrence,\n };\n\n scopeStack.push(frame);\n\n return {\n __brand: \"ScopeHandle\",\n depth: scopeStack.length - 1,\n } as ScopeHandle;\n },\n\n exitScope(handle: ScopeHandle): void {\n // Validate handle depth matches current stack\n if (handle.depth !== scopeStack.length - 1) {\n throw new Error(`[INTERNAL] Invalid scope exit: expected depth ${scopeStack.length - 1}, got ${handle.depth}`);\n }\n scopeStack.pop();\n },\n\n registerDefinition(): {\n astPath: string;\n isTopLevel: boolean;\n exportBinding?: string;\n } {\n const basePath = _buildAstPath(scopeStack);\n const astPath = ensureUniquePath(basePath);\n const isTopLevel = scopeStack.length === 0;\n\n // Check export binding if provided\n let exportBinding: string | undefined;\n if (getExportName && isTopLevel) {\n // For top-level definitions, try to get export name\n // This is a simplified version - real logic depends on how the definition is bound\n exportBinding = undefined;\n }\n\n return {\n astPath,\n isTopLevel,\n exportBinding,\n };\n },\n\n resolveCanonicalId(astPath: string): CanonicalId {\n return createCanonicalId(filePath, astPath, { baseDir });\n },\n\n registerExportBinding(local: string, exported: string): void {\n exportBindings.set(local, exported);\n },\n\n currentDepth(): number {\n return scopeStack.length;\n },\n };\n};\n\n/**\n * Helper to create occurrence tracker (for backward compatibility)\n */\nexport const createOccurrenceTracker = (): {\n getNextOccurrence: (key: string) => number;\n} => {\n const occurrenceCounters = new Map<string, number>();\n\n return {\n getNextOccurrence(key: string): number {\n const current = occurrenceCounters.get(key) ?? 0;\n occurrenceCounters.set(key, current + 1);\n return current;\n },\n };\n};\n\n/**\n * Helper to create path tracker (for backward compatibility)\n */\nexport const createPathTracker = (): {\n ensureUniquePath: (basePath: string) => string;\n} => {\n const usedPaths = new Set<string>();\n\n return {\n ensureUniquePath(basePath: string): string {\n let path = basePath;\n let suffix = 0;\n while (usedPaths.has(path)) {\n suffix++;\n path = `${basePath}$${suffix}`;\n }\n usedPaths.add(path);\n return path;\n },\n };\n};\n\n/**\n * Build AST path from scope stack (for backward compatibility)\n */\nexport const buildAstPath = (stack: readonly ScopeFrame[]): string => {\n return stack.map((frame) => frame.nameSegment).join(\".\");\n};\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- const require_canonical_id = require('./canonical-id-CgMNOZyn.cjs');
2
- require('./utils-CsTwS1dw.cjs');
1
+ const require_canonical_id = require('./canonical-id-DHdeYIsT.cjs');
2
+ require('./utils-Rs7YbafF.cjs');
3
3
 
4
4
  exports.CanonicalIdSchema = require_canonical_id.CanonicalIdSchema;
5
5
  exports.buildAstPath = require_canonical_id.buildAstPath;
@@ -7,4 +7,6 @@ exports.createCanonicalId = require_canonical_id.createCanonicalId;
7
7
  exports.createCanonicalTracker = require_canonical_id.createCanonicalTracker;
8
8
  exports.createOccurrenceTracker = require_canonical_id.createOccurrenceTracker;
9
9
  exports.createPathTracker = require_canonical_id.createPathTracker;
10
- exports.parseCanonicalId = require_canonical_id.parseCanonicalId;
10
+ exports.isRelativeCanonicalId = require_canonical_id.isRelativeCanonicalId;
11
+ exports.parseCanonicalId = require_canonical_id.parseCanonicalId;
12
+ exports.validateCanonicalId = require_canonical_id.validateCanonicalId;
@@ -1,2 +1,2 @@
1
- import { a as createCanonicalTracker, c as CanonicalId, d as parseCanonicalId, i as buildAstPath, l as CanonicalIdSchema, n as ScopeFrame, o as createOccurrenceTracker, r as ScopeHandle, s as createPathTracker, t as CanonicalPathTracker, u as createCanonicalId } from "./index-Cm2Zwk9m.cjs";
2
- export { CanonicalId, CanonicalIdSchema, CanonicalPathTracker, ScopeFrame, ScopeHandle, buildAstPath, createCanonicalId, createCanonicalTracker, createOccurrenceTracker, createPathTracker, parseCanonicalId };
1
+ import { a as createCanonicalTracker, c as CanonicalId, d as CreateCanonicalIdOptions, f as createCanonicalId, h as validateCanonicalId, i as buildAstPath, l as CanonicalIdSchema, m as parseCanonicalId, n as ScopeFrame, o as createOccurrenceTracker, p as isRelativeCanonicalId, r as ScopeHandle, s as createPathTracker, t as CanonicalPathTracker, u as CanonicalIdValidationResult } from "./index-DZSebwar.cjs";
2
+ export { CanonicalId, CanonicalIdSchema, CanonicalIdValidationResult, CanonicalPathTracker, CreateCanonicalIdOptions, ScopeFrame, ScopeHandle, buildAstPath, createCanonicalId, createCanonicalTracker, createOccurrenceTracker, createPathTracker, isRelativeCanonicalId, parseCanonicalId, validateCanonicalId };
@@ -1,2 +1,2 @@
1
- import { a as createCanonicalTracker, c as CanonicalId, d as parseCanonicalId, i as buildAstPath, l as CanonicalIdSchema, n as ScopeFrame, o as createOccurrenceTracker, r as ScopeHandle, s as createPathTracker, t as CanonicalPathTracker, u as createCanonicalId } from "./index-CPpVc8Id.mjs";
2
- export { CanonicalId, CanonicalIdSchema, CanonicalPathTracker, ScopeFrame, ScopeHandle, buildAstPath, createCanonicalId, createCanonicalTracker, createOccurrenceTracker, createPathTracker, parseCanonicalId };
1
+ import { a as createCanonicalTracker, c as CanonicalId, d as CreateCanonicalIdOptions, f as createCanonicalId, h as validateCanonicalId, i as buildAstPath, l as CanonicalIdSchema, m as parseCanonicalId, n as ScopeFrame, o as createOccurrenceTracker, p as isRelativeCanonicalId, r as ScopeHandle, s as createPathTracker, t as CanonicalPathTracker, u as CanonicalIdValidationResult } from "./index-AqkJhrm3.mjs";
2
+ export { CanonicalId, CanonicalIdSchema, CanonicalIdValidationResult, CanonicalPathTracker, CreateCanonicalIdOptions, ScopeFrame, ScopeHandle, buildAstPath, createCanonicalId, createCanonicalTracker, createOccurrenceTracker, createPathTracker, isRelativeCanonicalId, parseCanonicalId, validateCanonicalId };
@@ -1,4 +1,4 @@
1
- import "./utils-DLEgAn7q.mjs";
2
- import { a as CanonicalIdSchema, i as createPathTracker, n as createCanonicalTracker, o as createCanonicalId, r as createOccurrenceTracker, s as parseCanonicalId, t as buildAstPath } from "./canonical-id-BJahCcrS.mjs";
1
+ import "./utils-ZCE_eqCf.mjs";
2
+ import { a as CanonicalIdSchema, c as parseCanonicalId, i as createPathTracker, l as validateCanonicalId, n as createCanonicalTracker, o as createCanonicalId, r as createOccurrenceTracker, s as isRelativeCanonicalId, t as buildAstPath } from "./canonical-id-9alor9gv.mjs";
3
3
 
4
- export { CanonicalIdSchema, buildAstPath, createCanonicalId, createCanonicalTracker, createOccurrenceTracker, createPathTracker, parseCanonicalId };
4
+ export { CanonicalIdSchema, buildAstPath, createCanonicalId, createCanonicalTracker, createOccurrenceTracker, createPathTracker, isRelativeCanonicalId, parseCanonicalId, validateCanonicalId };
@@ -5,7 +5,34 @@ type CanonicalId = string & {
5
5
  readonly __brand: "CanonicalId";
6
6
  };
7
7
  declare const CanonicalIdSchema: z$1.ZodType<CanonicalId>;
8
- declare const createCanonicalId: (filePath: string, astPath: string) => CanonicalId;
8
+ /**
9
+ * Options for creating a canonical ID.
10
+ */
11
+ type CreateCanonicalIdOptions = {
12
+ /**
13
+ * Base directory for relative path computation.
14
+ * When provided, the canonical ID will use a relative path from baseDir.
15
+ * When undefined, an absolute path is required and used as-is.
16
+ */
17
+ readonly baseDir?: string;
18
+ };
19
+ /**
20
+ * Create a canonical ID from a file path and AST path.
21
+ *
22
+ * @param filePath - The file path (absolute, or relative if baseDir is provided)
23
+ * @param astPath - The AST path identifying the definition within the file
24
+ * @param options - Optional configuration including baseDir for relative path support
25
+ * @returns A canonical ID in the format "{path}::{astPath}"
26
+ */
27
+ declare const createCanonicalId: (filePath: string, astPath: string, options?: CreateCanonicalIdOptions) => CanonicalId;
28
+ /**
29
+ * Check if a canonical ID uses a relative path.
30
+ * Relative canonical IDs do not start with '/'.
31
+ *
32
+ * @param canonicalId - The canonical ID to check
33
+ * @returns true if the canonical ID uses a relative path
34
+ */
35
+ declare const isRelativeCanonicalId: (canonicalId: CanonicalId | string) => boolean;
9
36
  /**
10
37
  * Parse a canonical ID into its components.
11
38
  * @param canonicalId - The canonical ID to parse (e.g., "/app/src/user.ts::userFragment")
@@ -15,6 +42,24 @@ declare const parseCanonicalId: (canonicalId: CanonicalId | string) => {
15
42
  filePath: string;
16
43
  astPath: string;
17
44
  };
45
+ /**
46
+ * Validation result for canonical ID format.
47
+ */
48
+ type CanonicalIdValidationResult = {
49
+ readonly isValid: true;
50
+ } | {
51
+ readonly isValid: false;
52
+ readonly reason: string;
53
+ };
54
+ /**
55
+ * Validate a canonical ID format.
56
+ * A valid canonical ID has format: "{filePath}::{astPath}"
57
+ * where both filePath and astPath are non-empty.
58
+ *
59
+ * @param canonicalId - The canonical ID to validate
60
+ * @returns Validation result with isValid boolean and optional error reason
61
+ */
62
+ declare const validateCanonicalId: (canonicalId: string) => CanonicalIdValidationResult;
18
63
  //#endregion
19
64
  //#region packages/common/src/canonical-id/path-tracker.d.ts
20
65
 
@@ -110,6 +155,11 @@ interface CanonicalPathTracker {
110
155
  */
111
156
  declare const createCanonicalTracker: (options: {
112
157
  filePath: string;
158
+ /**
159
+ * Base directory for relative path computation in canonical IDs.
160
+ * When provided, canonical IDs will use relative paths from baseDir.
161
+ */
162
+ baseDir?: string;
113
163
  getExportName?: (localName: string) => string | undefined;
114
164
  }) => CanonicalPathTracker;
115
165
  /**
@@ -129,5 +179,5 @@ declare const createPathTracker: () => {
129
179
  */
130
180
  declare const buildAstPath: (stack: readonly ScopeFrame[]) => string;
131
181
  //#endregion
132
- export { createCanonicalTracker as a, CanonicalId as c, parseCanonicalId as d, buildAstPath as i, CanonicalIdSchema as l, ScopeFrame as n, createOccurrenceTracker as o, ScopeHandle as r, createPathTracker as s, CanonicalPathTracker as t, createCanonicalId as u };
133
- //# sourceMappingURL=index-Cm2Zwk9m.d.cts.map
182
+ export { createCanonicalTracker as a, CanonicalId as c, CreateCanonicalIdOptions as d, createCanonicalId as f, validateCanonicalId as h, buildAstPath as i, CanonicalIdSchema as l, parseCanonicalId as m, ScopeFrame as n, createOccurrenceTracker as o, isRelativeCanonicalId as p, ScopeHandle as r, createPathTracker as s, CanonicalPathTracker as t, CanonicalIdValidationResult as u };
183
+ //# sourceMappingURL=index-AqkJhrm3.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-AqkJhrm3.d.mts","names":[],"sources":["../src/canonical-id/canonical-id.ts","../src/canonical-id/path-tracker.ts"],"sourcesContent":[],"mappings":";;;KAIY,WAAA;;AAAZ,CAAA;AAIa,cAAA,iBAA6B,EAAV,GAAA,CAAE,OAAF,CAAU,WAAD,CAAA;AAKzC;AAiBA;AAoCA;AASa,KA9DD,wBAAA,GA+DG;EAkBH;AAUZ;;;;ECnFY,SAAA,OAAU,CAAA,EAAA,MAAA;AAYtB,CAAA;AAQA;;;;;;AA8EA;AA4GA;AAiBa,cDtNA,iBCuOZ,EAAA,CAAA,QAAA,EAAA,MAAA,EAAA,OAAA,EAAA,MAAA,EAAA,OAAA,CAAA,EDvO8E,wBCuO9E,EAAA,GDvOyG,WCuOzG;AAKD;;;;;;;cDxMa,qCAAsC;;;;;;cAStC,gCACE;;;;;;;KAkBH,2BAAA;;;;;;;;;;;;;;cAUC,8CAA6C;;;;ACvE1D;AAQA;;AAM0F,KA1B9E,UAAA,GA0B8E;EAMtE;EAiBmB,SAAA,WAAA,EAAA,MAAA;EAAW;EAiDrC,SAAA,IAAA,EAAA,UAuGZ,GAAA,OAAA,GA/FG,UAAA,GAAA,UA+FH,GAAA,QAAA,GAAA,YAAA;EAKY;EAiBA,SAAA,UAAA,EAiBZ,MAAA;AAKD,CAAA;;;;KAzOY,WAAA;;;;;;;UAQK,oBAAA;;;;;;;;UAM8B;;MAA2C;;;;;oBAMtE;;;;;;;;;;;;;;;uCAiBmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAiD1B;;;;;;;;MAQT;;;;cAoGS;;;;;;cAiBA;;;;;;cAsBA,+BAAgC"}
@@ -0,0 +1,139 @@
1
+ import { Result } from "neverthrow";
2
+
3
+ //#region packages/common/src/utils/tsconfig.d.ts
4
+
5
+ /**
6
+ * Parsed tsconfig.json paths configuration.
7
+ * All paths are resolved to absolute paths.
8
+ */
9
+ type TsconfigPathsConfig = {
10
+ /** Absolute base URL for path resolution */
11
+ readonly baseUrl: string;
12
+ /** Path mappings with absolute paths */
13
+ readonly paths: Readonly<Record<string, readonly string[]>>;
14
+ };
15
+ /**
16
+ * Error types for tsconfig reading.
17
+ */
18
+ type TsconfigReadError = {
19
+ readonly code: "TSCONFIG_READ_FAILED" | "TSCONFIG_PARSE_FAILED" | "TSCONFIG_INVALID";
20
+ readonly message: string;
21
+ };
22
+ /**
23
+ * Read and parse tsconfig.json to extract paths configuration.
24
+ * Currently does not support `extends` - reads only the specified file.
25
+ *
26
+ * @param tsconfigPath - Absolute path to tsconfig.json
27
+ * @returns Parsed paths configuration or null if no paths defined
28
+ */
29
+ declare const readTsconfigPaths: (tsconfigPath: string) => Result<TsconfigPathsConfig | null, TsconfigReadError>;
30
+ //#endregion
31
+ //#region packages/common/src/utils/alias-resolver.d.ts
32
+ /**
33
+ * Alias resolver interface for resolving path aliases to file paths.
34
+ */
35
+ type AliasResolver = {
36
+ /**
37
+ * Resolve an alias specifier to an absolute file path.
38
+ * Returns null if the specifier doesn't match any alias pattern
39
+ * or if the resolved file doesn't exist.
40
+ */
41
+ readonly resolve: (specifier: string) => string | null;
42
+ };
43
+ /**
44
+ * Create an alias resolver from tsconfig paths configuration.
45
+ *
46
+ * Resolution behavior:
47
+ * 1. Try each pattern in order (first match wins per TS spec)
48
+ * 2. For each matched pattern, try all target paths in order
49
+ * 3. For each target, apply extension resolution
50
+ * 4. Return first found file, or null if none found
51
+ */
52
+ declare const createAliasResolver: (config: TsconfigPathsConfig) => AliasResolver;
53
+ //#endregion
54
+ //#region packages/common/src/utils/cached-fn.d.ts
55
+ declare const cachedFn: <T>(fn: () => T) => {
56
+ (): T;
57
+ clear(): void;
58
+ };
59
+ //#endregion
60
+ //#region packages/common/src/utils/path.d.ts
61
+ /**
62
+ * File extensions to try when resolving module specifiers.
63
+ * Ordered to match TypeScript's module resolution order.
64
+ * @see https://www.typescriptlang.org/docs/handbook/module-resolution.html
65
+ */
66
+ declare const MODULE_EXTENSION_CANDIDATES: readonly [".ts", ".tsx", ".mts", ".cts", ".js", ".mjs", ".cjs", ".jsx"];
67
+ /**
68
+ * Result of parsing a JS extension from a specifier.
69
+ */
70
+ type JsExtensionInfo = {
71
+ /** The specifier without the JS extension */
72
+ readonly base: string;
73
+ /** The JS extension found (e.g., ".js", ".mjs") */
74
+ readonly jsExtension: string;
75
+ /** The corresponding TS extensions to try (e.g., [".ts", ".tsx"] for ".js") */
76
+ readonly tsExtensions: readonly string[];
77
+ };
78
+ /**
79
+ * Parse a JS extension from a specifier for ESM-style import resolution.
80
+ * Returns the base path (without extension), the JS extension, and corresponding TS extensions.
81
+ *
82
+ * @param specifier - The import specifier to parse
83
+ * @returns Object with base, jsExtension, and tsExtensions, or null if no JS extension found
84
+ *
85
+ * @example
86
+ * parseJsExtension("./foo.js") // { base: "./foo", jsExtension: ".js", tsExtensions: [".ts", ".tsx"] }
87
+ * parseJsExtension("./foo.mjs") // { base: "./foo", jsExtension: ".mjs", tsExtensions: [".mts"] }
88
+ * parseJsExtension("./foo") // null
89
+ * parseJsExtension("./foo.ts") // null
90
+ */
91
+ declare const parseJsExtension: (specifier: string) => JsExtensionInfo | null;
92
+ /**
93
+ * Normalize path to use forward slashes (cross-platform).
94
+ * Ensures consistent path handling across platforms.
95
+ */
96
+ declare const normalizePath: (value: string) => string;
97
+ /**
98
+ * Resolve a relative import specifier to an absolute file path.
99
+ * Tries the specifier as-is, with extensions, and as a directory with index files.
100
+ *
101
+ * @param from - Absolute path to the importing file
102
+ * @param specifier - Relative module specifier (must start with '.')
103
+ * @returns Absolute POSIX path to the resolved file, or null if not found
104
+ */
105
+ declare const resolveRelativeImportWithExistenceCheck: ({
106
+ filePath,
107
+ specifier
108
+ }: {
109
+ filePath: string;
110
+ specifier: string;
111
+ }) => string | null;
112
+ /**
113
+ * Resolve a relative import specifier to an absolute file path.
114
+ * Tries the specifier as-is, with extensions, and as a directory with index files.
115
+ *
116
+ * @param from - Absolute path to the importing file
117
+ * @param specifier - Relative module specifier (must start with '.')
118
+ * @returns Absolute POSIX path to the resolved file, or null if not found
119
+ */
120
+ declare const resolveRelativeImportWithReferences: <_>({
121
+ filePath,
122
+ specifier,
123
+ references
124
+ }: {
125
+ filePath: string;
126
+ specifier: string;
127
+ references: Map<string, _> | Set<string>;
128
+ }) => string | null;
129
+ /**
130
+ * Check if a module specifier is relative (starts with '.' or '..')
131
+ */
132
+ declare const isRelativeSpecifier: (specifier: string) => boolean;
133
+ /**
134
+ * Check if a module specifier is external (package name, not relative)
135
+ */
136
+ declare const isExternalSpecifier: (specifier: string) => boolean;
137
+ //#endregion
138
+ export { normalizePath as a, resolveRelativeImportWithReferences as c, createAliasResolver as d, TsconfigPathsConfig as f, isRelativeSpecifier as i, cachedFn as l, readTsconfigPaths as m, MODULE_EXTENSION_CANDIDATES as n, parseJsExtension as o, TsconfigReadError as p, isExternalSpecifier as r, resolveRelativeImportWithExistenceCheck as s, JsExtensionInfo as t, AliasResolver as u };
139
+ //# sourceMappingURL=index-BCu9PNbZ.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-BCu9PNbZ.d.mts","names":[],"sources":["../src/utils/tsconfig.ts","../src/utils/alias-resolver.ts","../src/utils/cached-fn.ts","../src/utils/path.ts"],"sourcesContent":[],"mappings":";;;;;;AAQA;AAUA;AA4Ba,KAtCD,mBAAA,GAgGX;EA1D+D;EAA4B,SAAA,OAAA,EAAA,MAAA;EAAnC;EAAM,SAAA,KAAA,EAlC7C,QAkC6C,CAlCpC,MAkCoC,CAAA,MAAA,EAAA,SAAA,MAAA,EAAA,CAAA,CAAA;;;;ACtC/D;AAoJa,KD1ID,iBAAA,GCyKX;;;;AC3LD;;;;ACQA;AAgBA;AAsBA;AAiBa,cHjBA,iBGiB+E,EAAA,CAAA,YAAA,EAAA,MAAA,EAAA,GHjBnC,MGiBmC,CHjB5B,mBGiB4B,GAAA,IAAA,EHjBA,iBGiBA,CAAA;;;;;AHvD5F;AAUY,KCVA,aAAA,GDUiB;EA4BhB;;;;;;;;ACtCb;AAoJA;;;;AC5JA;;;cD4Ja,8BAA+B,wBAAsB;;;cC5JrD,wBAAyB;MAAA;;AFQtC,CAAA;;;;;;AAAA;AAUA;AA4Ba,cGtCA,2BHgGZ,EAAA,SAAA,CAAA,KAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,EAAA,KAAA,EAAA,MAAA,EAAA,MAAA,EAAA,MAAA,CAAA;;;;AA1D8D,KGtBnD,eAAA,GHsBmD;;;;ECtCnD,SAAA,WAAa,EAAA,MAAA;EAoJZ;;;;AC5Jb;;;;ACQA;AAgBA;AAsBA;AAiBA;AAUA;AA+EA;;;AAAuD,cA1G1C,gBA0G0C,EAAA,CAAA,SAAA,EAAA,MAAA,EAAA,GA1GF,eA0GE,GAAA,IAAA;;;;;AA4D1C,cArJA,aAqJ+G,EAAA,CAAA,KAAA,EAAA,MAAA,EAAA,GAAA,MAAA;AAK5H;;;;;;;;cAhJa;;;;;;;;;;;;;;;cA+EA;;;;;;;cAOC,YAAY,KAAK;;;;;cAqDlB;;;;cAKA"}