@soda-gql/builder 0.0.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/index.cjs ADDED
@@ -0,0 +1,2935 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ let __soda_gql_common = require("@soda-gql/common");
25
+ __soda_gql_common = __toESM(__soda_gql_common);
26
+ let zod = require("zod");
27
+ zod = __toESM(zod);
28
+ let node_path = require("node:path");
29
+ node_path = __toESM(node_path);
30
+ let neverthrow = require("neverthrow");
31
+ neverthrow = __toESM(neverthrow);
32
+ let node_crypto = require("node:crypto");
33
+ node_crypto = __toESM(node_crypto);
34
+ let __swc_core = require("@swc/core");
35
+ __swc_core = __toESM(__swc_core);
36
+ let typescript = require("typescript");
37
+ typescript = __toESM(typescript);
38
+ let node_fs = require("node:fs");
39
+ node_fs = __toESM(node_fs);
40
+ let fast_glob = require("fast-glob");
41
+ fast_glob = __toESM(fast_glob);
42
+ let node_vm = require("node:vm");
43
+ node_vm = __toESM(node_vm);
44
+ let __soda_gql_core = require("@soda-gql/core");
45
+ __soda_gql_core = __toESM(__soda_gql_core);
46
+ let __soda_gql_runtime = require("@soda-gql/runtime");
47
+ __soda_gql_runtime = __toESM(__soda_gql_runtime);
48
+
49
+ //#region packages/builder/src/schemas/artifact.ts
50
+ const BuilderArtifactElementMetadataSchema = zod.z.object({
51
+ sourcePath: zod.z.string(),
52
+ sourceHash: zod.z.string(),
53
+ contentHash: zod.z.string()
54
+ });
55
+ const BuilderArtifactOperationSchema = zod.z.object({
56
+ id: zod.z.string(),
57
+ type: zod.z.literal("operation"),
58
+ metadata: BuilderArtifactElementMetadataSchema,
59
+ prebuild: zod.z.object({
60
+ operationType: zod.z.enum([
61
+ "query",
62
+ "mutation",
63
+ "subscription"
64
+ ]),
65
+ operationName: zod.z.string(),
66
+ document: zod.z.unknown(),
67
+ variableNames: zod.z.array(zod.z.string()),
68
+ projectionPathGraph: zod.z.unknown()
69
+ })
70
+ });
71
+ const BuilderArtifactSliceSchema = zod.z.object({
72
+ id: zod.z.string(),
73
+ type: zod.z.literal("slice"),
74
+ metadata: BuilderArtifactElementMetadataSchema,
75
+ prebuild: zod.z.object({ operationType: zod.z.enum([
76
+ "query",
77
+ "mutation",
78
+ "subscription"
79
+ ]) })
80
+ });
81
+ const BuilderArtifactModelSchema = zod.z.object({
82
+ id: zod.z.string(),
83
+ type: zod.z.literal("model"),
84
+ metadata: BuilderArtifactElementMetadataSchema,
85
+ prebuild: zod.z.object({ typename: zod.z.string() })
86
+ });
87
+ const BuilderArtifactElementSchema = zod.z.discriminatedUnion("type", [
88
+ BuilderArtifactOperationSchema,
89
+ BuilderArtifactSliceSchema,
90
+ BuilderArtifactModelSchema
91
+ ]);
92
+ const BuilderArtifactSchema = zod.z.object({
93
+ elements: zod.z.record(zod.z.string(), BuilderArtifactElementSchema),
94
+ report: zod.z.object({
95
+ durationMs: zod.z.number(),
96
+ warnings: zod.z.array(zod.z.string()),
97
+ stats: zod.z.object({
98
+ hits: zod.z.number(),
99
+ misses: zod.z.number(),
100
+ skips: zod.z.number()
101
+ })
102
+ })
103
+ });
104
+
105
+ //#endregion
106
+ //#region packages/builder/src/artifact/aggregate.ts
107
+ const canonicalToFilePath$1 = (canonicalId) => canonicalId.split("::")[0] ?? canonicalId;
108
+ const computeContentHash = (prebuild) => {
109
+ const hash = (0, node_crypto.createHash)("sha1");
110
+ hash.update(JSON.stringify(prebuild));
111
+ return hash.digest("hex");
112
+ };
113
+ const emitRegistrationError = (definition, message) => ({
114
+ code: "RUNTIME_MODULE_LOAD_FAILED",
115
+ filePath: canonicalToFilePath$1(definition.canonicalId),
116
+ astPath: definition.astPath,
117
+ message
118
+ });
119
+ const aggregate = ({ analyses, elements }) => {
120
+ const registry = new Map();
121
+ for (const analysis of analyses.values()) {
122
+ for (const definition of analysis.definitions) {
123
+ const element = elements[definition.canonicalId];
124
+ if (!element) {
125
+ const availableIds = Object.keys(elements).join(", ");
126
+ const message = `ARTIFACT_NOT_FOUND_IN_RUNTIME_MODULE: ${definition.canonicalId}\nAvailable: ${availableIds}`;
127
+ return (0, neverthrow.err)(emitRegistrationError(definition, message));
128
+ }
129
+ if (registry.has(definition.canonicalId)) {
130
+ return (0, neverthrow.err)(emitRegistrationError(definition, `ARTIFACT_ALREADY_REGISTERED`));
131
+ }
132
+ const metadata = {
133
+ sourcePath: analysis.filePath ?? canonicalToFilePath$1(definition.canonicalId),
134
+ sourceHash: analysis.signature,
135
+ contentHash: ""
136
+ };
137
+ if (element.type === "model") {
138
+ const prebuild = { typename: element.element.typename };
139
+ registry.set(definition.canonicalId, {
140
+ id: definition.canonicalId,
141
+ type: "model",
142
+ prebuild,
143
+ metadata: {
144
+ ...metadata,
145
+ contentHash: computeContentHash(prebuild)
146
+ }
147
+ });
148
+ continue;
149
+ }
150
+ if (element.type === "slice") {
151
+ const prebuild = { operationType: element.element.operationType };
152
+ registry.set(definition.canonicalId, {
153
+ id: definition.canonicalId,
154
+ type: "slice",
155
+ prebuild,
156
+ metadata: {
157
+ ...metadata,
158
+ contentHash: computeContentHash(prebuild)
159
+ }
160
+ });
161
+ continue;
162
+ }
163
+ if (element.type === "operation") {
164
+ const prebuild = {
165
+ operationType: element.element.operationType,
166
+ operationName: element.element.operationName,
167
+ document: element.element.document,
168
+ variableNames: element.element.variableNames,
169
+ projectionPathGraph: element.element.projectionPathGraph
170
+ };
171
+ registry.set(definition.canonicalId, {
172
+ id: definition.canonicalId,
173
+ type: "operation",
174
+ prebuild,
175
+ metadata: {
176
+ ...metadata,
177
+ contentHash: computeContentHash(prebuild)
178
+ }
179
+ });
180
+ continue;
181
+ }
182
+ if (element.type === "inlineOperation") {
183
+ const prebuild = {
184
+ operationType: element.element.operationType,
185
+ operationName: element.element.operationName,
186
+ document: element.element.document,
187
+ variableNames: element.element.variableNames
188
+ };
189
+ registry.set(definition.canonicalId, {
190
+ id: definition.canonicalId,
191
+ type: "inlineOperation",
192
+ prebuild,
193
+ metadata: {
194
+ ...metadata,
195
+ contentHash: computeContentHash(prebuild)
196
+ }
197
+ });
198
+ continue;
199
+ }
200
+ return (0, neverthrow.err)(emitRegistrationError(definition, "UNKNOWN_ARTIFACT_KIND"));
201
+ }
202
+ }
203
+ return (0, neverthrow.ok)(registry);
204
+ };
205
+
206
+ //#endregion
207
+ //#region packages/builder/src/artifact/issue-handler.ts
208
+ const canonicalToFilePath = (canonicalId) => canonicalId.split("::")[0] ?? canonicalId;
209
+ const checkIssues = ({ elements }) => {
210
+ const operationNames = new Set();
211
+ for (const [canonicalId, { type, element }] of Object.entries(elements)) {
212
+ if (type !== "operation") {
213
+ continue;
214
+ }
215
+ if (operationNames.has(element.operationName)) {
216
+ const sources = [canonicalToFilePath(canonicalId)];
217
+ return (0, neverthrow.err)({
218
+ code: "DOC_DUPLICATE",
219
+ message: `Duplicate document name: ${element.operationName}`,
220
+ name: element.operationName,
221
+ sources
222
+ });
223
+ }
224
+ operationNames.add(element.operationName);
225
+ }
226
+ return (0, neverthrow.ok)([]);
227
+ };
228
+
229
+ //#endregion
230
+ //#region packages/builder/src/artifact/builder.ts
231
+ const buildArtifact = ({ elements, analyses, stats: cache }) => {
232
+ const issuesResult = checkIssues({ elements });
233
+ if (issuesResult.isErr()) {
234
+ return (0, neverthrow.err)(issuesResult.error);
235
+ }
236
+ const warnings = issuesResult.value;
237
+ const aggregationResult = aggregate({
238
+ analyses,
239
+ elements
240
+ });
241
+ if (aggregationResult.isErr()) {
242
+ return (0, neverthrow.err)(aggregationResult.error);
243
+ }
244
+ return (0, neverthrow.ok)({
245
+ elements: Object.fromEntries(aggregationResult.value.entries()),
246
+ report: {
247
+ durationMs: 0,
248
+ warnings,
249
+ stats: cache
250
+ }
251
+ });
252
+ };
253
+
254
+ //#endregion
255
+ //#region packages/builder/src/errors.ts
256
+ /**
257
+ * Error constructor helpers for concise error creation.
258
+ */
259
+ const builderErrors = {
260
+ entryNotFound: (entry, message) => ({
261
+ code: "ENTRY_NOT_FOUND",
262
+ message: message ?? `Entry not found: ${entry}`,
263
+ entry
264
+ }),
265
+ configNotFound: (path, message) => ({
266
+ code: "CONFIG_NOT_FOUND",
267
+ message: message ?? `Config file not found: ${path}`,
268
+ path
269
+ }),
270
+ configInvalid: (path, message, cause) => ({
271
+ code: "CONFIG_INVALID",
272
+ message,
273
+ path,
274
+ cause
275
+ }),
276
+ discoveryIOError: (path, message, errno, cause) => ({
277
+ code: "DISCOVERY_IO_ERROR",
278
+ message,
279
+ path,
280
+ errno,
281
+ cause
282
+ }),
283
+ fingerprintFailed: (filePath, message, cause) => ({
284
+ code: "FINGERPRINT_FAILED",
285
+ message,
286
+ filePath,
287
+ cause
288
+ }),
289
+ unsupportedAnalyzer: (analyzer, message) => ({
290
+ code: "UNSUPPORTED_ANALYZER",
291
+ message: message ?? `Unsupported analyzer: ${analyzer}`,
292
+ analyzer
293
+ }),
294
+ canonicalPathInvalid: (path, reason) => ({
295
+ code: "CANONICAL_PATH_INVALID",
296
+ message: `Invalid canonical path: ${path}${reason ? ` (${reason})` : ""}`,
297
+ path,
298
+ reason
299
+ }),
300
+ canonicalScopeMismatch: (expected, actual) => ({
301
+ code: "CANONICAL_SCOPE_MISMATCH",
302
+ message: `Scope mismatch: expected ${expected}, got ${actual}`,
303
+ expected,
304
+ actual
305
+ }),
306
+ graphCircularDependency: (chain) => ({
307
+ code: "GRAPH_CIRCULAR_DEPENDENCY",
308
+ message: `Circular dependency detected: ${chain.join(" → ")}`,
309
+ chain
310
+ }),
311
+ graphMissingImport: (importer, importee) => ({
312
+ code: "GRAPH_MISSING_IMPORT",
313
+ message: `Missing import: "${importer}" imports "${importee}" but it's not in the graph`,
314
+ importer,
315
+ importee
316
+ }),
317
+ docDuplicate: (name, sources) => ({
318
+ code: "DOC_DUPLICATE",
319
+ message: `Duplicate document name: ${name} found in ${sources.length} files`,
320
+ name,
321
+ sources
322
+ }),
323
+ writeFailed: (outPath, message, cause) => ({
324
+ code: "WRITE_FAILED",
325
+ message,
326
+ outPath,
327
+ cause
328
+ }),
329
+ cacheCorrupted: (message, cachePath, cause) => ({
330
+ code: "CACHE_CORRUPTED",
331
+ message,
332
+ cachePath,
333
+ cause
334
+ }),
335
+ runtimeModuleLoadFailed: (filePath, astPath, message, cause) => ({
336
+ code: "RUNTIME_MODULE_LOAD_FAILED",
337
+ message,
338
+ filePath,
339
+ astPath,
340
+ cause
341
+ }),
342
+ artifactRegistrationFailed: (elementId, reason) => ({
343
+ code: "ARTIFACT_REGISTRATION_FAILED",
344
+ message: `Failed to register artifact element ${elementId}: ${reason}`,
345
+ elementId,
346
+ reason
347
+ }),
348
+ internalInvariant: (message, context, cause) => ({
349
+ code: "INTERNAL_INVARIANT",
350
+ message: `Internal invariant violated: ${message}`,
351
+ context,
352
+ cause
353
+ })
354
+ };
355
+ /**
356
+ * Convenience helper to create an err Result from BuilderError.
357
+ */
358
+ const builderErr = (error) => (0, neverthrow.err)(error);
359
+ /**
360
+ * Type guard for BuilderError.
361
+ */
362
+ const isBuilderError = (error) => {
363
+ return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && "message" in error && typeof error.message === "string";
364
+ };
365
+ /**
366
+ * Format BuilderError for console output (human-readable).
367
+ */
368
+ const formatBuilderError = (error) => {
369
+ const lines = [];
370
+ lines.push(`Error [${error.code}]: ${error.message}`);
371
+ switch (error.code) {
372
+ case "ENTRY_NOT_FOUND":
373
+ lines.push(` Entry: ${error.entry}`);
374
+ break;
375
+ case "CONFIG_NOT_FOUND":
376
+ case "CONFIG_INVALID":
377
+ lines.push(` Path: ${error.path}`);
378
+ if (error.code === "CONFIG_INVALID" && error.cause) {
379
+ lines.push(` Cause: ${error.cause}`);
380
+ }
381
+ break;
382
+ case "DISCOVERY_IO_ERROR":
383
+ lines.push(` Path: ${error.path}`);
384
+ if (error.errno !== undefined) {
385
+ lines.push(` Errno: ${error.errno}`);
386
+ }
387
+ break;
388
+ case "FINGERPRINT_FAILED":
389
+ lines.push(` File: ${error.filePath}`);
390
+ break;
391
+ case "CANONICAL_PATH_INVALID":
392
+ lines.push(` Path: ${error.path}`);
393
+ if (error.reason) {
394
+ lines.push(` Reason: ${error.reason}`);
395
+ }
396
+ break;
397
+ case "CANONICAL_SCOPE_MISMATCH":
398
+ lines.push(` Expected: ${error.expected}`);
399
+ lines.push(` Actual: ${error.actual}`);
400
+ break;
401
+ case "GRAPH_CIRCULAR_DEPENDENCY":
402
+ lines.push(` Chain: ${error.chain.join(" → ")}`);
403
+ break;
404
+ case "GRAPH_MISSING_IMPORT":
405
+ lines.push(` Importer: ${error.importer}`);
406
+ lines.push(` Importee: ${error.importee}`);
407
+ break;
408
+ case "DOC_DUPLICATE":
409
+ lines.push(` Name: ${error.name}`);
410
+ lines.push(` Sources:\n ${error.sources.join("\n ")}`);
411
+ break;
412
+ case "WRITE_FAILED":
413
+ lines.push(` Output path: ${error.outPath}`);
414
+ break;
415
+ case "CACHE_CORRUPTED":
416
+ if (error.cachePath) {
417
+ lines.push(` Cache path: ${error.cachePath}`);
418
+ }
419
+ break;
420
+ case "RUNTIME_MODULE_LOAD_FAILED":
421
+ lines.push(` File: ${error.filePath}`);
422
+ lines.push(` AST path: ${error.astPath}`);
423
+ break;
424
+ case "ARTIFACT_REGISTRATION_FAILED":
425
+ lines.push(` Element ID: ${error.elementId}`);
426
+ lines.push(` Reason: ${error.reason}`);
427
+ break;
428
+ case "INTERNAL_INVARIANT":
429
+ if (error.context) {
430
+ lines.push(` Context: ${error.context}`);
431
+ }
432
+ break;
433
+ }
434
+ if ("cause" in error && error.cause && !["CONFIG_INVALID"].includes(error.code)) {
435
+ lines.push(` Caused by: ${error.cause}`);
436
+ }
437
+ return lines.join("\n");
438
+ };
439
+ /**
440
+ * Assert unreachable code path (for exhaustiveness checks).
441
+ * This is the ONLY acceptable throw in builder code.
442
+ */
443
+ const assertUnreachable = (value, context) => {
444
+ throw new Error(`Unreachable code path${context ? ` in ${context}` : ""}: received ${JSON.stringify(value)}`);
445
+ };
446
+
447
+ //#endregion
448
+ //#region packages/builder/src/ast/common/scope.ts
449
+ /**
450
+ * Build AST path from scope stack
451
+ */
452
+ const buildAstPath$1 = (stack) => {
453
+ return stack.map((frame) => frame.nameSegment).join(".");
454
+ };
455
+ /**
456
+ * Create an occurrence tracker for disambiguating anonymous/duplicate scopes.
457
+ */
458
+ const createOccurrenceTracker$1 = () => {
459
+ const occurrenceCounters = new Map();
460
+ return { getNextOccurrence(key) {
461
+ const current = occurrenceCounters.get(key) ?? 0;
462
+ occurrenceCounters.set(key, current + 1);
463
+ return current;
464
+ } };
465
+ };
466
+ /**
467
+ * Create a path uniqueness tracker to ensure AST paths are unique.
468
+ */
469
+ const createPathTracker$1 = () => {
470
+ const usedPaths = new Set();
471
+ return { ensureUniquePath(basePath) {
472
+ let path = basePath;
473
+ let suffix = 0;
474
+ while (usedPaths.has(path)) {
475
+ suffix++;
476
+ path = `${basePath}$${suffix}`;
477
+ }
478
+ usedPaths.add(path);
479
+ return path;
480
+ } };
481
+ };
482
+ /**
483
+ * Create an export bindings map from module exports.
484
+ * Maps local variable names to their exported names.
485
+ */
486
+ const createExportBindingsMap = (exports$1) => {
487
+ const exportBindings = new Map();
488
+ exports$1.forEach((exp) => {
489
+ if (exp.kind === "named" && exp.local && !exp.isTypeOnly) {
490
+ exportBindings.set(exp.local, exp.exported);
491
+ }
492
+ });
493
+ return exportBindings;
494
+ };
495
+
496
+ //#endregion
497
+ //#region packages/builder/src/ast/adapters/swc.ts
498
+ const getLineStarts = (source) => {
499
+ const starts = [0];
500
+ for (let index = 0; index < source.length; index += 1) {
501
+ if (source[index] === "\n") {
502
+ starts.push(index + 1);
503
+ }
504
+ }
505
+ return starts;
506
+ };
507
+ const toPositionResolver = (source) => {
508
+ const lineStarts = getLineStarts(source);
509
+ return (offset) => {
510
+ let low = 0;
511
+ let high = lineStarts.length - 1;
512
+ while (low <= high) {
513
+ const mid = Math.floor((low + high) / 2);
514
+ const start = lineStarts[mid];
515
+ const next = mid + 1 < lineStarts.length ? lineStarts[mid + 1] : source.length + 1;
516
+ if (start == null || next == null) {
517
+ break;
518
+ }
519
+ if (offset < start) {
520
+ high = mid - 1;
521
+ } else if (offset >= next) {
522
+ low = mid + 1;
523
+ } else {
524
+ return {
525
+ line: mid + 1,
526
+ column: offset - start + 1
527
+ };
528
+ }
529
+ }
530
+ return {
531
+ line: lineStarts.length,
532
+ column: offset - lineStarts[lineStarts.length - 1] + 1
533
+ };
534
+ };
535
+ };
536
+ const toLocation$1 = (resolvePosition, span) => ({
537
+ start: resolvePosition(span.start),
538
+ end: resolvePosition(span.end)
539
+ });
540
+ const collectImports$1 = (module$1) => {
541
+ const imports = [];
542
+ const handle = (declaration) => {
543
+ const source = declaration.source.value;
544
+ declaration.specifiers?.forEach((specifier) => {
545
+ if (specifier.type === "ImportSpecifier") {
546
+ const imported = specifier.imported ? specifier.imported.value : specifier.local.value;
547
+ imports.push({
548
+ source,
549
+ imported,
550
+ local: specifier.local.value,
551
+ kind: "named",
552
+ isTypeOnly: Boolean(specifier.isTypeOnly)
553
+ });
554
+ return;
555
+ }
556
+ if (specifier.type === "ImportNamespaceSpecifier") {
557
+ imports.push({
558
+ source,
559
+ imported: "*",
560
+ local: specifier.local.value,
561
+ kind: "namespace",
562
+ isTypeOnly: false
563
+ });
564
+ return;
565
+ }
566
+ if (specifier.type === "ImportDefaultSpecifier") {
567
+ imports.push({
568
+ source,
569
+ imported: "default",
570
+ local: specifier.local.value,
571
+ kind: "default",
572
+ isTypeOnly: false
573
+ });
574
+ }
575
+ });
576
+ };
577
+ module$1.body.forEach((item) => {
578
+ if (item.type === "ImportDeclaration") {
579
+ handle(item);
580
+ return;
581
+ }
582
+ if ("declaration" in item && item.declaration && "type" in item.declaration && item.declaration.type === "ImportDeclaration") {
583
+ handle(item.declaration);
584
+ }
585
+ });
586
+ return imports;
587
+ };
588
+ const collectExports$1 = (module$1) => {
589
+ const exports$1 = [];
590
+ const handle = (declaration) => {
591
+ if (declaration.type === "ExportDeclaration") {
592
+ if (declaration.declaration.type === "VariableDeclaration") {
593
+ declaration.declaration.declarations.forEach((decl) => {
594
+ if (decl.id.type === "Identifier") {
595
+ exports$1.push({
596
+ kind: "named",
597
+ exported: decl.id.value,
598
+ local: decl.id.value,
599
+ isTypeOnly: false
600
+ });
601
+ }
602
+ });
603
+ }
604
+ if (declaration.declaration.type === "FunctionDeclaration") {
605
+ const ident = declaration.declaration.identifier;
606
+ if (ident) {
607
+ exports$1.push({
608
+ kind: "named",
609
+ exported: ident.value,
610
+ local: ident.value,
611
+ isTypeOnly: false
612
+ });
613
+ }
614
+ }
615
+ return;
616
+ }
617
+ if (declaration.type === "ExportNamedDeclaration") {
618
+ const source = declaration.source?.value;
619
+ declaration.specifiers?.forEach((specifier) => {
620
+ if (specifier.type !== "ExportSpecifier") {
621
+ return;
622
+ }
623
+ const exported = specifier.exported ? specifier.exported.value : specifier.orig.value;
624
+ const local = specifier.orig.value;
625
+ if (source) {
626
+ exports$1.push({
627
+ kind: "reexport",
628
+ exported,
629
+ local,
630
+ source,
631
+ isTypeOnly: Boolean(specifier.isTypeOnly)
632
+ });
633
+ return;
634
+ }
635
+ exports$1.push({
636
+ kind: "named",
637
+ exported,
638
+ local,
639
+ isTypeOnly: Boolean(specifier.isTypeOnly)
640
+ });
641
+ });
642
+ return;
643
+ }
644
+ if (declaration.type === "ExportAllDeclaration") {
645
+ exports$1.push({
646
+ kind: "reexport",
647
+ exported: "*",
648
+ source: declaration.source.value,
649
+ isTypeOnly: false
650
+ });
651
+ return;
652
+ }
653
+ if (declaration.type === "ExportDefaultDeclaration" || declaration.type === "ExportDefaultExpression") {
654
+ exports$1.push({
655
+ kind: "named",
656
+ exported: "default",
657
+ local: "default",
658
+ isTypeOnly: false
659
+ });
660
+ }
661
+ };
662
+ module$1.body.forEach((item) => {
663
+ if (item.type === "ExportDeclaration" || item.type === "ExportNamedDeclaration" || item.type === "ExportAllDeclaration" || item.type === "ExportDefaultDeclaration" || item.type === "ExportDefaultExpression") {
664
+ handle(item);
665
+ return;
666
+ }
667
+ if ("declaration" in item && item.declaration) {
668
+ const declaration = item.declaration;
669
+ if (declaration.type === "ExportDeclaration" || declaration.type === "ExportNamedDeclaration" || declaration.type === "ExportAllDeclaration" || declaration.type === "ExportDefaultDeclaration" || declaration.type === "ExportDefaultExpression") {
670
+ handle(declaration);
671
+ }
672
+ }
673
+ });
674
+ return exports$1;
675
+ };
676
+ const collectGqlIdentifiers = (module$1, helper) => {
677
+ const identifiers = new Set();
678
+ module$1.body.forEach((item) => {
679
+ const declaration = item.type === "ImportDeclaration" ? item : "declaration" in item && item.declaration && item.declaration.type === "ImportDeclaration" ? item.declaration : null;
680
+ if (!declaration) {
681
+ return;
682
+ }
683
+ if (!helper.isGraphqlSystemImportSpecifier({
684
+ filePath: module$1.__filePath,
685
+ specifier: declaration.source.value
686
+ })) {
687
+ return;
688
+ }
689
+ declaration.specifiers?.forEach((specifier) => {
690
+ if (specifier.type === "ImportSpecifier") {
691
+ const imported = specifier.imported ? specifier.imported.value : specifier.local.value;
692
+ if (imported === "gql") {
693
+ identifiers.add(specifier.local.value);
694
+ }
695
+ }
696
+ });
697
+ });
698
+ return identifiers;
699
+ };
700
+ const isGqlCall = (identifiers, call) => {
701
+ const callee = call.callee;
702
+ if (callee.type !== "MemberExpression") {
703
+ return false;
704
+ }
705
+ if (callee.object.type !== "Identifier") {
706
+ return false;
707
+ }
708
+ if (!identifiers.has(callee.object.value)) {
709
+ return false;
710
+ }
711
+ if (callee.property.type !== "Identifier") {
712
+ return false;
713
+ }
714
+ const firstArg = call.arguments[0];
715
+ if (!firstArg?.expression || firstArg.expression.type !== "ArrowFunctionExpression") {
716
+ return false;
717
+ }
718
+ return true;
719
+ };
720
+ const collectAllDefinitions$1 = ({ module: module$1, gqlIdentifiers, imports: _imports, exports: exports$1, resolvePosition, source }) => {
721
+ const getPropertyName$1 = (property) => {
722
+ if (!property) {
723
+ return null;
724
+ }
725
+ if (property.type === "Identifier") {
726
+ return property.value;
727
+ }
728
+ if (property.type === "StringLiteral" || property.type === "NumericLiteral") {
729
+ return property.value;
730
+ }
731
+ return null;
732
+ };
733
+ const pending = [];
734
+ const handledCalls = [];
735
+ const exportBindings = createExportBindingsMap(exports$1);
736
+ const tracker = (0, __soda_gql_common.createCanonicalTracker)({
737
+ filePath: module$1.__filePath,
738
+ getExportName: (localName) => exportBindings.get(localName)
739
+ });
740
+ const anonymousCounters = new Map();
741
+ const getAnonymousName = (kind) => {
742
+ const count = anonymousCounters.get(kind) ?? 0;
743
+ anonymousCounters.set(kind, count + 1);
744
+ return `${kind}#${count}`;
745
+ };
746
+ const withScope = (stack, segment, kind, stableKey, callback) => {
747
+ const handle = tracker.enterScope({
748
+ segment,
749
+ kind,
750
+ stableKey
751
+ });
752
+ try {
753
+ const frame = {
754
+ nameSegment: segment,
755
+ kind
756
+ };
757
+ return callback([...stack, frame]);
758
+ } finally {
759
+ tracker.exitScope(handle);
760
+ }
761
+ };
762
+ const expressionFromCall = (call) => {
763
+ let start = call.span.start;
764
+ if (start > 0 && source[start] === "q" && source[start - 1] === "g" && source.slice(start, start + 3) === "ql.") {
765
+ start -= 1;
766
+ }
767
+ const raw = source.slice(start, call.span.end);
768
+ const marker = raw.indexOf("gql");
769
+ if (marker >= 0) {
770
+ return raw.slice(marker);
771
+ }
772
+ return raw;
773
+ };
774
+ const visit = (node, stack) => {
775
+ if (!node || typeof node !== "object") {
776
+ return;
777
+ }
778
+ if (node.type === "CallExpression" && isGqlCall(gqlIdentifiers, node)) {
779
+ const { astPath } = tracker.registerDefinition();
780
+ const isTopLevel = stack.length === 1;
781
+ let isExported = false;
782
+ let exportBinding;
783
+ if (isTopLevel && stack[0]) {
784
+ const topLevelName = stack[0].nameSegment;
785
+ if (exportBindings.has(topLevelName)) {
786
+ isExported = true;
787
+ exportBinding = exportBindings.get(topLevelName);
788
+ }
789
+ }
790
+ handledCalls.push(node);
791
+ pending.push({
792
+ astPath,
793
+ isTopLevel,
794
+ isExported,
795
+ exportBinding,
796
+ loc: toLocation$1(resolvePosition, node.span),
797
+ expression: expressionFromCall(node)
798
+ });
799
+ return;
800
+ }
801
+ if (node.type === "VariableDeclaration") {
802
+ node.declarations?.forEach((decl) => {
803
+ if (decl.id?.type === "Identifier") {
804
+ const varName = decl.id.value;
805
+ if (decl.init) {
806
+ withScope(stack, varName, "variable", `var:${varName}`, (newStack) => {
807
+ visit(decl.init, newStack);
808
+ });
809
+ }
810
+ }
811
+ });
812
+ return;
813
+ }
814
+ if (node.type === "FunctionDeclaration") {
815
+ const funcName = node.identifier?.value ?? getAnonymousName("function");
816
+ if (node.body) {
817
+ withScope(stack, funcName, "function", `func:${funcName}`, (newStack) => {
818
+ visit(node.body, newStack);
819
+ });
820
+ }
821
+ return;
822
+ }
823
+ if (node.type === "ArrowFunctionExpression") {
824
+ const arrowName = getAnonymousName("arrow");
825
+ if (node.body) {
826
+ withScope(stack, arrowName, "function", "arrow", (newStack) => {
827
+ visit(node.body, newStack);
828
+ });
829
+ }
830
+ return;
831
+ }
832
+ if (node.type === "FunctionExpression") {
833
+ const funcName = node.identifier?.value ?? getAnonymousName("function");
834
+ if (node.body) {
835
+ withScope(stack, funcName, "function", `func:${funcName}`, (newStack) => {
836
+ visit(node.body, newStack);
837
+ });
838
+ }
839
+ return;
840
+ }
841
+ if (node.type === "ClassDeclaration") {
842
+ const className = node.identifier?.value ?? getAnonymousName("class");
843
+ withScope(stack, className, "class", `class:${className}`, (classStack) => {
844
+ node.body?.forEach((member) => {
845
+ if (member.type === "MethodProperty" || member.type === "ClassProperty") {
846
+ const memberName = member.key?.value ?? null;
847
+ if (memberName) {
848
+ const memberKind = member.type === "MethodProperty" ? "method" : "property";
849
+ withScope(classStack, memberName, memberKind, `member:${className}.${memberName}`, (memberStack) => {
850
+ if (member.type === "MethodProperty" && member.body) {
851
+ visit(member.body, memberStack);
852
+ } else if (member.type === "ClassProperty" && member.value) {
853
+ visit(member.value, memberStack);
854
+ }
855
+ });
856
+ }
857
+ }
858
+ });
859
+ });
860
+ return;
861
+ }
862
+ if (node.type === "KeyValueProperty") {
863
+ const propName = getPropertyName$1(node.key);
864
+ if (propName) {
865
+ withScope(stack, propName, "property", `prop:${propName}`, (newStack) => {
866
+ visit(node.value, newStack);
867
+ });
868
+ }
869
+ return;
870
+ }
871
+ if (Array.isArray(node)) {
872
+ for (const child of node) {
873
+ visit(child, stack);
874
+ }
875
+ } else {
876
+ for (const value of Object.values(node)) {
877
+ if (Array.isArray(value)) {
878
+ for (const child of value) {
879
+ visit(child, stack);
880
+ }
881
+ } else if (value && typeof value === "object") {
882
+ visit(value, stack);
883
+ }
884
+ }
885
+ }
886
+ };
887
+ module$1.body.forEach((statement) => {
888
+ visit(statement, []);
889
+ });
890
+ const definitions = pending.map((item) => ({
891
+ canonicalId: (0, __soda_gql_common.createCanonicalId)(module$1.__filePath, item.astPath),
892
+ astPath: item.astPath,
893
+ isTopLevel: item.isTopLevel,
894
+ isExported: item.isExported,
895
+ exportBinding: item.exportBinding,
896
+ loc: item.loc,
897
+ expression: item.expression
898
+ }));
899
+ return {
900
+ definitions,
901
+ handledCalls
902
+ };
903
+ };
904
+ /**
905
+ * Collect diagnostics (now empty since we support all definition types)
906
+ */
907
+ const collectDiagnostics$1 = () => {
908
+ return [];
909
+ };
910
+ /**
911
+ * SWC adapter implementation
912
+ */
913
+ const swcAdapter = {
914
+ parse(input) {
915
+ const program = (0, __swc_core.parseSync)(input.source, {
916
+ syntax: "typescript",
917
+ tsx: input.filePath.endsWith(".tsx"),
918
+ target: "es2022",
919
+ decorators: false,
920
+ dynamicImport: true
921
+ });
922
+ if (program.type !== "Module") {
923
+ return null;
924
+ }
925
+ const swcModule = program;
926
+ swcModule.__filePath = input.filePath;
927
+ return swcModule;
928
+ },
929
+ collectGqlIdentifiers(file, helper) {
930
+ return collectGqlIdentifiers(file, helper);
931
+ },
932
+ collectImports(file) {
933
+ return collectImports$1(file);
934
+ },
935
+ collectExports(file) {
936
+ return collectExports$1(file);
937
+ },
938
+ collectDefinitions(file, context) {
939
+ const resolvePosition = toPositionResolver(context.source);
940
+ const { definitions, handledCalls } = collectAllDefinitions$1({
941
+ module: file,
942
+ gqlIdentifiers: context.gqlIdentifiers,
943
+ imports: context.imports,
944
+ exports: context.exports,
945
+ resolvePosition,
946
+ source: context.source
947
+ });
948
+ return {
949
+ definitions,
950
+ handles: handledCalls
951
+ };
952
+ },
953
+ collectDiagnostics(_file, _context) {
954
+ return collectDiagnostics$1();
955
+ }
956
+ };
957
+
958
+ //#endregion
959
+ //#region packages/builder/src/ast/adapters/typescript.ts
960
+ const createSourceFile = (filePath, source) => {
961
+ const scriptKind = (0, node_path.extname)(filePath) === ".tsx" ? typescript.default.ScriptKind.TSX : typescript.default.ScriptKind.TS;
962
+ return typescript.default.createSourceFile(filePath, source, typescript.default.ScriptTarget.ES2022, true, scriptKind);
963
+ };
964
+ const toLocation = (sourceFile, node) => {
965
+ const start = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
966
+ const end = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
967
+ return {
968
+ start: {
969
+ line: start.line + 1,
970
+ column: start.character + 1
971
+ },
972
+ end: {
973
+ line: end.line + 1,
974
+ column: end.character + 1
975
+ }
976
+ };
977
+ };
978
+ const collectGqlImports = (sourceFile, helper) => {
979
+ const identifiers = new Set();
980
+ sourceFile.statements.forEach((statement) => {
981
+ if (!typescript.default.isImportDeclaration(statement) || !statement.importClause) {
982
+ return;
983
+ }
984
+ const moduleText = statement.moduleSpecifier.text;
985
+ if (!helper.isGraphqlSystemImportSpecifier({
986
+ filePath: sourceFile.fileName,
987
+ specifier: moduleText
988
+ })) {
989
+ return;
990
+ }
991
+ if (statement.importClause.namedBindings && typescript.default.isNamedImports(statement.importClause.namedBindings)) {
992
+ statement.importClause.namedBindings.elements.forEach((element) => {
993
+ const imported = element.propertyName ? element.propertyName.text : element.name.text;
994
+ if (imported === "gql") {
995
+ identifiers.add(element.name.text);
996
+ }
997
+ });
998
+ }
999
+ });
1000
+ return identifiers;
1001
+ };
1002
+ const collectImports = (sourceFile) => {
1003
+ const imports = [];
1004
+ sourceFile.statements.forEach((statement) => {
1005
+ if (!typescript.default.isImportDeclaration(statement) || !statement.importClause) {
1006
+ return;
1007
+ }
1008
+ const moduleText = statement.moduleSpecifier.text;
1009
+ const { importClause } = statement;
1010
+ if (importClause.name) {
1011
+ imports.push({
1012
+ source: moduleText,
1013
+ imported: "default",
1014
+ local: importClause.name.text,
1015
+ kind: "default",
1016
+ isTypeOnly: Boolean(importClause.isTypeOnly)
1017
+ });
1018
+ }
1019
+ const { namedBindings } = importClause;
1020
+ if (!namedBindings) {
1021
+ return;
1022
+ }
1023
+ if (typescript.default.isNamespaceImport(namedBindings)) {
1024
+ imports.push({
1025
+ source: moduleText,
1026
+ imported: "*",
1027
+ local: namedBindings.name.text,
1028
+ kind: "namespace",
1029
+ isTypeOnly: Boolean(importClause.isTypeOnly)
1030
+ });
1031
+ return;
1032
+ }
1033
+ namedBindings.elements.forEach((element) => {
1034
+ imports.push({
1035
+ source: moduleText,
1036
+ imported: element.propertyName ? element.propertyName.text : element.name.text,
1037
+ local: element.name.text,
1038
+ kind: "named",
1039
+ isTypeOnly: Boolean(importClause.isTypeOnly || element.isTypeOnly)
1040
+ });
1041
+ });
1042
+ });
1043
+ return imports;
1044
+ };
1045
+ const collectExports = (sourceFile) => {
1046
+ const exports$1 = [];
1047
+ sourceFile.statements.forEach((statement) => {
1048
+ if (typescript.default.isExportDeclaration(statement)) {
1049
+ const moduleSpecifier = statement.moduleSpecifier ? statement.moduleSpecifier.text : undefined;
1050
+ if (statement.exportClause && typescript.default.isNamedExports(statement.exportClause)) {
1051
+ statement.exportClause.elements.forEach((element) => {
1052
+ if (moduleSpecifier) {
1053
+ exports$1.push({
1054
+ kind: "reexport",
1055
+ exported: element.name.text,
1056
+ local: element.propertyName ? element.propertyName.text : undefined,
1057
+ source: moduleSpecifier,
1058
+ isTypeOnly: Boolean(statement.isTypeOnly || element.isTypeOnly)
1059
+ });
1060
+ } else {
1061
+ exports$1.push({
1062
+ kind: "named",
1063
+ exported: element.name.text,
1064
+ local: element.propertyName ? element.propertyName.text : element.name.text,
1065
+ isTypeOnly: Boolean(statement.isTypeOnly || element.isTypeOnly)
1066
+ });
1067
+ }
1068
+ });
1069
+ return;
1070
+ }
1071
+ if (moduleSpecifier) {
1072
+ exports$1.push({
1073
+ kind: "reexport",
1074
+ exported: "*",
1075
+ source: moduleSpecifier,
1076
+ isTypeOnly: Boolean(statement.isTypeOnly)
1077
+ });
1078
+ }
1079
+ return;
1080
+ }
1081
+ if (typescript.default.isExportAssignment(statement)) {
1082
+ exports$1.push({
1083
+ kind: "named",
1084
+ exported: "default",
1085
+ local: "default",
1086
+ isTypeOnly: false
1087
+ });
1088
+ }
1089
+ if (typescript.default.isVariableStatement(statement) && statement.modifiers?.some((modifier) => modifier.kind === typescript.default.SyntaxKind.ExportKeyword)) {
1090
+ statement.declarationList.declarations.forEach((declaration) => {
1091
+ if (typescript.default.isIdentifier(declaration.name)) {
1092
+ exports$1.push({
1093
+ kind: "named",
1094
+ exported: declaration.name.text,
1095
+ local: declaration.name.text,
1096
+ isTypeOnly: false
1097
+ });
1098
+ }
1099
+ });
1100
+ }
1101
+ if (typescript.default.isFunctionDeclaration(statement) && statement.modifiers?.some((modifier) => modifier.kind === typescript.default.SyntaxKind.ExportKeyword) && statement.name) {
1102
+ exports$1.push({
1103
+ kind: "named",
1104
+ exported: statement.name.text,
1105
+ local: statement.name.text,
1106
+ isTypeOnly: false
1107
+ });
1108
+ }
1109
+ });
1110
+ return exports$1;
1111
+ };
1112
+ const isGqlDefinitionCall = (identifiers, callExpression) => {
1113
+ const expression = callExpression.expression;
1114
+ if (!typescript.default.isPropertyAccessExpression(expression)) {
1115
+ return false;
1116
+ }
1117
+ if (!typescript.default.isIdentifier(expression.expression) || !identifiers.has(expression.expression.text)) {
1118
+ return false;
1119
+ }
1120
+ const [factory] = callExpression.arguments;
1121
+ if (!factory || !typescript.default.isArrowFunction(factory)) {
1122
+ return false;
1123
+ }
1124
+ return true;
1125
+ };
1126
+ /**
1127
+ * Get property name from AST node
1128
+ */
1129
+ const getPropertyName = (name) => {
1130
+ if (typescript.default.isIdentifier(name)) {
1131
+ return name.text;
1132
+ }
1133
+ if (typescript.default.isStringLiteral(name) || typescript.default.isNumericLiteral(name)) {
1134
+ return name.text;
1135
+ }
1136
+ return null;
1137
+ };
1138
+ /**
1139
+ * Collect all gql definitions (exported, non-exported, top-level, nested)
1140
+ */
1141
+ const collectAllDefinitions = ({ sourceFile, identifiers, exports: exports$1 }) => {
1142
+ const pending = [];
1143
+ const handledCalls = [];
1144
+ const exportBindings = createExportBindingsMap(exports$1);
1145
+ const tracker = (0, __soda_gql_common.createCanonicalTracker)({
1146
+ filePath: sourceFile.fileName,
1147
+ getExportName: (localName) => exportBindings.get(localName)
1148
+ });
1149
+ const anonymousCounters = new Map();
1150
+ const getAnonymousName = (kind) => {
1151
+ const count = anonymousCounters.get(kind) ?? 0;
1152
+ anonymousCounters.set(kind, count + 1);
1153
+ return `${kind}#${count}`;
1154
+ };
1155
+ const withScope = (stack, segment, kind, stableKey, callback) => {
1156
+ const handle = tracker.enterScope({
1157
+ segment,
1158
+ kind,
1159
+ stableKey
1160
+ });
1161
+ try {
1162
+ const frame = {
1163
+ nameSegment: segment,
1164
+ kind
1165
+ };
1166
+ return callback([...stack, frame]);
1167
+ } finally {
1168
+ tracker.exitScope(handle);
1169
+ }
1170
+ };
1171
+ const visit = (node, stack) => {
1172
+ if (typescript.default.isCallExpression(node) && isGqlDefinitionCall(identifiers, node)) {
1173
+ const { astPath } = tracker.registerDefinition();
1174
+ const isTopLevel = stack.length === 1;
1175
+ let isExported = false;
1176
+ let exportBinding;
1177
+ if (isTopLevel && stack[0]) {
1178
+ const topLevelName = stack[0].nameSegment;
1179
+ if (exportBindings.has(topLevelName)) {
1180
+ isExported = true;
1181
+ exportBinding = exportBindings.get(topLevelName);
1182
+ }
1183
+ }
1184
+ handledCalls.push(node);
1185
+ pending.push({
1186
+ astPath,
1187
+ isTopLevel,
1188
+ isExported,
1189
+ exportBinding,
1190
+ loc: toLocation(sourceFile, node),
1191
+ expression: node.getText(sourceFile)
1192
+ });
1193
+ return;
1194
+ }
1195
+ if (typescript.default.isVariableDeclaration(node) && node.name && typescript.default.isIdentifier(node.name)) {
1196
+ const varName = node.name.text;
1197
+ if (node.initializer) {
1198
+ const next = node.initializer;
1199
+ withScope(stack, varName, "variable", `var:${varName}`, (newStack) => {
1200
+ visit(next, newStack);
1201
+ });
1202
+ }
1203
+ return;
1204
+ }
1205
+ if (typescript.default.isFunctionDeclaration(node)) {
1206
+ const funcName = node.name?.text ?? getAnonymousName("function");
1207
+ if (node.body) {
1208
+ const next = node.body;
1209
+ withScope(stack, funcName, "function", `func:${funcName}`, (newStack) => {
1210
+ typescript.default.forEachChild(next, (child) => visit(child, newStack));
1211
+ });
1212
+ }
1213
+ return;
1214
+ }
1215
+ if (typescript.default.isArrowFunction(node)) {
1216
+ const arrowName = getAnonymousName("arrow");
1217
+ withScope(stack, arrowName, "function", "arrow", (newStack) => {
1218
+ if (typescript.default.isBlock(node.body)) {
1219
+ typescript.default.forEachChild(node.body, (child) => visit(child, newStack));
1220
+ } else {
1221
+ visit(node.body, newStack);
1222
+ }
1223
+ });
1224
+ return;
1225
+ }
1226
+ if (typescript.default.isFunctionExpression(node)) {
1227
+ const funcName = node.name?.text ?? getAnonymousName("function");
1228
+ if (node.body) {
1229
+ withScope(stack, funcName, "function", `func:${funcName}`, (newStack) => {
1230
+ typescript.default.forEachChild(node.body, (child) => visit(child, newStack));
1231
+ });
1232
+ }
1233
+ return;
1234
+ }
1235
+ if (typescript.default.isClassDeclaration(node)) {
1236
+ const className = node.name?.text ?? getAnonymousName("class");
1237
+ withScope(stack, className, "class", `class:${className}`, (classStack) => {
1238
+ node.members.forEach((member) => {
1239
+ if (typescript.default.isMethodDeclaration(member) || typescript.default.isPropertyDeclaration(member)) {
1240
+ const memberName = member.name && typescript.default.isIdentifier(member.name) ? member.name.text : null;
1241
+ if (memberName) {
1242
+ const memberKind = typescript.default.isMethodDeclaration(member) ? "method" : "property";
1243
+ withScope(classStack, memberName, memberKind, `member:${className}.${memberName}`, (memberStack) => {
1244
+ if (typescript.default.isMethodDeclaration(member) && member.body) {
1245
+ typescript.default.forEachChild(member.body, (child) => visit(child, memberStack));
1246
+ } else if (typescript.default.isPropertyDeclaration(member) && member.initializer) {
1247
+ visit(member.initializer, memberStack);
1248
+ }
1249
+ });
1250
+ }
1251
+ }
1252
+ });
1253
+ });
1254
+ return;
1255
+ }
1256
+ if (typescript.default.isPropertyAssignment(node)) {
1257
+ const propName = getPropertyName(node.name);
1258
+ if (propName) {
1259
+ withScope(stack, propName, "property", `prop:${propName}`, (newStack) => {
1260
+ visit(node.initializer, newStack);
1261
+ });
1262
+ }
1263
+ return;
1264
+ }
1265
+ typescript.default.forEachChild(node, (child) => visit(child, stack));
1266
+ };
1267
+ sourceFile.statements.forEach((statement) => {
1268
+ visit(statement, []);
1269
+ });
1270
+ const definitions = pending.map((item) => ({
1271
+ canonicalId: (0, __soda_gql_common.createCanonicalId)(sourceFile.fileName, item.astPath),
1272
+ astPath: item.astPath,
1273
+ isTopLevel: item.isTopLevel,
1274
+ isExported: item.isExported,
1275
+ exportBinding: item.exportBinding,
1276
+ loc: item.loc,
1277
+ expression: item.expression
1278
+ }));
1279
+ return {
1280
+ definitions,
1281
+ handledCalls
1282
+ };
1283
+ };
1284
+ /**
1285
+ * Collect diagnostics (now empty since we support all definition types)
1286
+ */
1287
+ const collectDiagnostics = (_sourceFile, _identifiers, _handledCalls) => {
1288
+ return [];
1289
+ };
1290
+ /**
1291
+ * TypeScript adapter implementation
1292
+ */
1293
+ const typescriptAdapter = {
1294
+ parse(input) {
1295
+ return createSourceFile(input.filePath, input.source);
1296
+ },
1297
+ collectGqlIdentifiers(file, helper) {
1298
+ return collectGqlImports(file, helper);
1299
+ },
1300
+ collectImports(file) {
1301
+ return collectImports(file);
1302
+ },
1303
+ collectExports(file) {
1304
+ return collectExports(file);
1305
+ },
1306
+ collectDefinitions(file, context) {
1307
+ const { definitions, handledCalls } = collectAllDefinitions({
1308
+ sourceFile: file,
1309
+ identifiers: context.gqlIdentifiers,
1310
+ exports: context.exports
1311
+ });
1312
+ return {
1313
+ definitions,
1314
+ handles: handledCalls
1315
+ };
1316
+ },
1317
+ collectDiagnostics(file, context) {
1318
+ return collectDiagnostics(file, context.gqlIdentifiers, context.handledCalls);
1319
+ }
1320
+ };
1321
+
1322
+ //#endregion
1323
+ //#region packages/builder/src/ast/core.ts
1324
+ /**
1325
+ * Core analyzer function that orchestrates the analysis pipeline.
1326
+ * Adapters implement the AnalyzerAdapter interface to provide parser-specific logic.
1327
+ */
1328
+ const analyzeModuleCore = (input, adapter, graphqlHelper) => {
1329
+ const hasher = (0, __soda_gql_common.getPortableHasher)();
1330
+ const signature = hasher.hash(input.source, "xxhash");
1331
+ const file = adapter.parse(input);
1332
+ if (!file) {
1333
+ return {
1334
+ filePath: input.filePath,
1335
+ signature,
1336
+ definitions: [],
1337
+ diagnostics: [],
1338
+ imports: [],
1339
+ exports: []
1340
+ };
1341
+ }
1342
+ const gqlIdentifiers = adapter.collectGqlIdentifiers(file, graphqlHelper);
1343
+ const imports = adapter.collectImports(file);
1344
+ const exports$1 = adapter.collectExports(file);
1345
+ const { definitions, handles } = adapter.collectDefinitions(file, {
1346
+ gqlIdentifiers,
1347
+ imports,
1348
+ exports: exports$1,
1349
+ source: input.source
1350
+ });
1351
+ const diagnostics = adapter.collectDiagnostics(file, {
1352
+ gqlIdentifiers,
1353
+ handledCalls: handles,
1354
+ source: input.source
1355
+ });
1356
+ return {
1357
+ filePath: input.filePath,
1358
+ signature,
1359
+ definitions,
1360
+ diagnostics,
1361
+ imports,
1362
+ exports: exports$1
1363
+ };
1364
+ };
1365
+
1366
+ //#endregion
1367
+ //#region packages/builder/src/ast/index.ts
1368
+ const createAstAnalyzer = ({ analyzer, graphqlHelper }) => {
1369
+ const analyze = (input) => {
1370
+ if (analyzer === "ts") {
1371
+ return analyzeModuleCore(input, typescriptAdapter, graphqlHelper);
1372
+ }
1373
+ if (analyzer === "swc") {
1374
+ return analyzeModuleCore(input, swcAdapter, graphqlHelper);
1375
+ }
1376
+ return assertUnreachable(analyzer, "createAstAnalyzer");
1377
+ };
1378
+ return {
1379
+ type: analyzer,
1380
+ analyze
1381
+ };
1382
+ };
1383
+ const getAstAnalyzer = (analyzer) => {
1384
+ throw new Error("getAstAnalyzer is deprecated. Use createAstAnalyzer with graphqlHelper parameter instead.");
1385
+ };
1386
+
1387
+ //#endregion
1388
+ //#region packages/builder/src/cache/json-cache.ts
1389
+ const JSON_EXT = ".json";
1390
+ const sanitizeSegment = (segment) => segment.replace(/[\\/]/g, "_");
1391
+ const ensureDirectory = (directory) => {
1392
+ if (!(0, node_fs.existsSync)(directory)) {
1393
+ (0, node_fs.mkdirSync)(directory, { recursive: true });
1394
+ }
1395
+ };
1396
+ const toNamespacePath = (root, segments) => (0, node_path.join)(root, ...segments.map(sanitizeSegment));
1397
+ const toEntryFilename = (key) => {
1398
+ const hasher = (0, __soda_gql_common.getPortableHasher)();
1399
+ return `${hasher.hash(key, "xxhash")}${JSON_EXT}`;
1400
+ };
1401
+ const createJsonCache = ({ rootDir, prefix = [] }) => {
1402
+ const basePath = toNamespacePath(rootDir, prefix);
1403
+ ensureDirectory(basePath);
1404
+ const enumerateFiles = (directory) => {
1405
+ if (!(0, node_fs.existsSync)(directory)) {
1406
+ return [];
1407
+ }
1408
+ return (0, node_fs.readdirSync)(directory, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(JSON_EXT)).map((entry) => (0, node_path.join)(directory, entry.name));
1409
+ };
1410
+ return {
1411
+ createStore: ({ namespace, schema, version = "v1" }) => {
1412
+ const storeRoot = toNamespacePath(basePath, namespace);
1413
+ ensureDirectory(storeRoot);
1414
+ const envelopeSchema = zod.z.object({
1415
+ key: zod.z.string(),
1416
+ version: zod.z.string(),
1417
+ value: schema
1418
+ });
1419
+ const resolveEntryPath = (key) => (0, node_path.join)(storeRoot, toEntryFilename(key));
1420
+ const readEnvelope = (filePath) => {
1421
+ try {
1422
+ const raw = (0, node_fs.readFileSync)(filePath, "utf8");
1423
+ const parsed = envelopeSchema.safeParse(JSON.parse(raw));
1424
+ if (!parsed.success) {
1425
+ (0, node_fs.unlinkSync)(filePath);
1426
+ return null;
1427
+ }
1428
+ if (parsed.data.version !== version) {
1429
+ (0, node_fs.unlinkSync)(filePath);
1430
+ return null;
1431
+ }
1432
+ return parsed.data;
1433
+ } catch {
1434
+ (0, node_fs.unlinkSync)(filePath);
1435
+ return null;
1436
+ }
1437
+ };
1438
+ const load = (key) => {
1439
+ const filePath = resolveEntryPath(key);
1440
+ if (!(0, node_fs.existsSync)(filePath)) {
1441
+ return null;
1442
+ }
1443
+ const envelope = readEnvelope(filePath);
1444
+ if (!envelope || envelope.key !== key) {
1445
+ return null;
1446
+ }
1447
+ return envelope.value;
1448
+ };
1449
+ const store = (key, value) => {
1450
+ const filePath = resolveEntryPath(key);
1451
+ ensureDirectory(storeRoot);
1452
+ const envelope = {
1453
+ key,
1454
+ version,
1455
+ value
1456
+ };
1457
+ (0, node_fs.writeFileSync)(filePath, JSON.stringify(envelope));
1458
+ };
1459
+ const deleteEntry = (key) => {
1460
+ const filePath = resolveEntryPath(key);
1461
+ if ((0, node_fs.existsSync)(filePath)) {
1462
+ (0, node_fs.unlinkSync)(filePath);
1463
+ }
1464
+ };
1465
+ function* iterateEntries() {
1466
+ for (const filePath of enumerateFiles(storeRoot)) {
1467
+ const envelope = readEnvelope(filePath);
1468
+ if (!envelope) {
1469
+ continue;
1470
+ }
1471
+ yield [envelope.key, envelope.value];
1472
+ }
1473
+ }
1474
+ const clear = () => {
1475
+ for (const filePath of enumerateFiles(storeRoot)) {
1476
+ (0, node_fs.unlinkSync)(filePath);
1477
+ }
1478
+ };
1479
+ const size = () => {
1480
+ let count = 0;
1481
+ for (const _ of iterateEntries()) {
1482
+ count += 1;
1483
+ }
1484
+ return count;
1485
+ };
1486
+ return {
1487
+ load,
1488
+ store,
1489
+ delete: deleteEntry,
1490
+ entries: iterateEntries,
1491
+ clear,
1492
+ size
1493
+ };
1494
+ },
1495
+ clearAll: () => {
1496
+ if ((0, node_fs.existsSync)(basePath)) {
1497
+ (0, node_fs.rmSync)(basePath, {
1498
+ recursive: true,
1499
+ force: true
1500
+ });
1501
+ }
1502
+ ensureDirectory(basePath);
1503
+ }
1504
+ };
1505
+ };
1506
+
1507
+ //#endregion
1508
+ //#region packages/builder/src/cache/json-entity-cache.ts
1509
+ /**
1510
+ * Abstract base class for JSON-based entity caches.
1511
+ * Provides common caching functionality with signature-based eviction.
1512
+ */
1513
+ var JsonEntityCache = class {
1514
+ cacheStore;
1515
+ keyNormalizer;
1516
+ constructor(options) {
1517
+ this.cacheStore = options.factory.createStore({
1518
+ namespace: [...options.namespace],
1519
+ schema: options.schema,
1520
+ version: options.version
1521
+ });
1522
+ this.keyNormalizer = options.keyNormalizer ?? __soda_gql_common.normalizePath;
1523
+ }
1524
+ /**
1525
+ * Normalize a key for consistent cache lookups.
1526
+ */
1527
+ normalizeKey(key) {
1528
+ return this.keyNormalizer(key);
1529
+ }
1530
+ /**
1531
+ * Load raw value from cache without signature validation.
1532
+ */
1533
+ loadRaw(key) {
1534
+ return this.cacheStore.load(key);
1535
+ }
1536
+ /**
1537
+ * Store raw value to cache.
1538
+ */
1539
+ storeRaw(key, value) {
1540
+ this.cacheStore.store(key, value);
1541
+ }
1542
+ /**
1543
+ * Delete an entry from the cache.
1544
+ */
1545
+ delete(key) {
1546
+ const normalizedKey = this.normalizeKey(key);
1547
+ this.cacheStore.delete(normalizedKey);
1548
+ }
1549
+ /**
1550
+ * Get all cached entries.
1551
+ * Subclasses should override this to provide custom iteration.
1552
+ */
1553
+ *baseEntries() {
1554
+ for (const [, value] of this.cacheStore.entries()) {
1555
+ yield value;
1556
+ }
1557
+ }
1558
+ /**
1559
+ * Clear all entries from the cache.
1560
+ */
1561
+ clear() {
1562
+ this.cacheStore.clear();
1563
+ }
1564
+ /**
1565
+ * Get the number of entries in the cache.
1566
+ */
1567
+ size() {
1568
+ return this.cacheStore.size();
1569
+ }
1570
+ };
1571
+
1572
+ //#endregion
1573
+ //#region packages/builder/src/schemas/cache.ts
1574
+ const SourcePositionSchema = zod.z.object({
1575
+ line: zod.z.number(),
1576
+ column: zod.z.number()
1577
+ });
1578
+ const SourceLocationSchema = zod.z.object({
1579
+ start: SourcePositionSchema,
1580
+ end: SourcePositionSchema
1581
+ });
1582
+ const ModuleDefinitionSchema = zod.z.object({
1583
+ canonicalId: __soda_gql_common.CanonicalIdSchema,
1584
+ astPath: zod.z.string(),
1585
+ isTopLevel: zod.z.boolean(),
1586
+ isExported: zod.z.boolean(),
1587
+ exportBinding: zod.z.string().optional(),
1588
+ loc: SourceLocationSchema,
1589
+ expression: zod.z.string()
1590
+ });
1591
+ const ModuleDiagnosticSchema = zod.z.object({
1592
+ code: zod.z.literal("NON_TOP_LEVEL_DEFINITION"),
1593
+ message: zod.z.string(),
1594
+ loc: SourceLocationSchema
1595
+ });
1596
+ const ModuleImportSchema = zod.z.object({
1597
+ source: zod.z.string(),
1598
+ imported: zod.z.string(),
1599
+ local: zod.z.string(),
1600
+ kind: zod.z.enum([
1601
+ "named",
1602
+ "namespace",
1603
+ "default"
1604
+ ]),
1605
+ isTypeOnly: zod.z.boolean()
1606
+ });
1607
+ const ModuleExportSchema = zod.z.discriminatedUnion("kind", [zod.z.object({
1608
+ kind: zod.z.literal("named"),
1609
+ exported: zod.z.string(),
1610
+ local: zod.z.string(),
1611
+ source: zod.z.undefined().optional(),
1612
+ isTypeOnly: zod.z.boolean()
1613
+ }), zod.z.object({
1614
+ kind: zod.z.literal("reexport"),
1615
+ exported: zod.z.string(),
1616
+ source: zod.z.string(),
1617
+ local: zod.z.string().optional(),
1618
+ isTypeOnly: zod.z.boolean()
1619
+ })]);
1620
+ const ModuleAnalysisSchema = zod.z.object({
1621
+ filePath: zod.z.string(),
1622
+ signature: zod.z.string(),
1623
+ definitions: zod.z.array(ModuleDefinitionSchema).readonly(),
1624
+ diagnostics: zod.z.array(ModuleDiagnosticSchema).readonly(),
1625
+ imports: zod.z.array(ModuleImportSchema).readonly(),
1626
+ exports: zod.z.array(ModuleExportSchema).readonly()
1627
+ });
1628
+
1629
+ //#endregion
1630
+ //#region packages/builder/src/schemas/discovery.ts
1631
+ const FileFingerprintSchema = zod.z.object({
1632
+ hash: zod.z.string(),
1633
+ sizeBytes: zod.z.number(),
1634
+ mtimeMs: zod.z.number()
1635
+ });
1636
+ const DiscoveredDependencySchema = zod.z.object({
1637
+ specifier: zod.z.string(),
1638
+ resolvedPath: zod.z.string().nullable(),
1639
+ isExternal: zod.z.boolean()
1640
+ });
1641
+ const DiscoverySnapshotSchema = zod.z.object({
1642
+ filePath: zod.z.string(),
1643
+ normalizedFilePath: zod.z.string(),
1644
+ signature: zod.z.string(),
1645
+ fingerprint: FileFingerprintSchema,
1646
+ analyzer: zod.z.string(),
1647
+ createdAtMs: zod.z.number(),
1648
+ analysis: ModuleAnalysisSchema,
1649
+ dependencies: zod.z.array(DiscoveredDependencySchema).readonly()
1650
+ });
1651
+
1652
+ //#endregion
1653
+ //#region packages/builder/src/discovery/cache.ts
1654
+ const DISCOVERY_CACHE_VERSION = "discovery-cache/v3";
1655
+ var JsonDiscoveryCache = class extends JsonEntityCache {
1656
+ constructor(options) {
1657
+ const namespace = [
1658
+ ...options.namespacePrefix ?? ["discovery"],
1659
+ options.analyzer,
1660
+ options.evaluatorId
1661
+ ];
1662
+ super({
1663
+ factory: options.factory,
1664
+ namespace,
1665
+ schema: DiscoverySnapshotSchema,
1666
+ version: options.version ?? DISCOVERY_CACHE_VERSION
1667
+ });
1668
+ }
1669
+ load(filePath, expectedSignature) {
1670
+ const key = this.normalizeKey(filePath);
1671
+ const snapshot = this.loadRaw(key);
1672
+ if (!snapshot) {
1673
+ return null;
1674
+ }
1675
+ if (snapshot.signature !== expectedSignature) {
1676
+ this.delete(filePath);
1677
+ return null;
1678
+ }
1679
+ return snapshot;
1680
+ }
1681
+ peek(filePath) {
1682
+ const key = this.normalizeKey(filePath);
1683
+ return this.loadRaw(key);
1684
+ }
1685
+ store(snapshot) {
1686
+ const key = this.normalizeKey(snapshot.filePath);
1687
+ this.storeRaw(key, snapshot);
1688
+ }
1689
+ entries() {
1690
+ return this.baseEntries();
1691
+ }
1692
+ };
1693
+ const createDiscoveryCache = (options) => new JsonDiscoveryCache(options);
1694
+
1695
+ //#endregion
1696
+ //#region packages/builder/src/discovery/common.ts
1697
+ /**
1698
+ * Extract all unique dependencies (relative + external) from the analysis.
1699
+ * Resolves local specifiers immediately so discovery only traverses once.
1700
+ */
1701
+ const extractModuleDependencies = (analysis) => {
1702
+ const dependencies = new Map();
1703
+ const addDependency = (specifier) => {
1704
+ if (dependencies.has(specifier)) {
1705
+ return;
1706
+ }
1707
+ const isExternal = (0, __soda_gql_common.isExternalSpecifier)(specifier);
1708
+ const resolvedPath = isExternal ? null : (0, __soda_gql_common.resolveRelativeImportWithExistenceCheck)({
1709
+ filePath: analysis.filePath,
1710
+ specifier
1711
+ });
1712
+ dependencies.set(specifier, {
1713
+ specifier,
1714
+ resolvedPath,
1715
+ isExternal
1716
+ });
1717
+ };
1718
+ for (const imp of analysis.imports) {
1719
+ addDependency(imp.source);
1720
+ }
1721
+ for (const exp of analysis.exports) {
1722
+ if (exp.kind === "reexport") {
1723
+ addDependency(exp.source);
1724
+ }
1725
+ }
1726
+ return Array.from(dependencies.values());
1727
+ };
1728
+ const createSourceHash = (source) => {
1729
+ const hasher = (0, __soda_gql_common.getPortableHasher)();
1730
+ return hasher.hash(source, "xxhash");
1731
+ };
1732
+
1733
+ //#endregion
1734
+ //#region packages/builder/src/discovery/fingerprint.ts
1735
+ /**
1736
+ * In-memory fingerprint cache keyed by absolute path
1737
+ */
1738
+ const fingerprintCache = new Map();
1739
+ /**
1740
+ * Lazy-loaded xxhash instance
1741
+ */
1742
+ let xxhashInstance = null;
1743
+ /**
1744
+ * Lazily load xxhash-wasm instance
1745
+ */
1746
+ async function getXXHash() {
1747
+ if (xxhashInstance === null) {
1748
+ const { default: xxhash } = await import("xxhash-wasm");
1749
+ xxhashInstance = await xxhash();
1750
+ }
1751
+ return xxhashInstance;
1752
+ }
1753
+ /**
1754
+ * Compute file fingerprint with memoization.
1755
+ * Uses mtime to short-circuit re-hashing unchanged files.
1756
+ *
1757
+ * @param path - Absolute path to file
1758
+ * @returns Result containing FileFingerprint or FingerprintError
1759
+ */
1760
+ function computeFingerprint(path) {
1761
+ try {
1762
+ const stats = (0, node_fs.statSync)(path);
1763
+ if (!stats.isFile()) {
1764
+ return (0, neverthrow.err)({
1765
+ code: "NOT_A_FILE",
1766
+ path,
1767
+ message: `Path is not a file: ${path}`
1768
+ });
1769
+ }
1770
+ const mtimeMs = stats.mtimeMs;
1771
+ const cached = fingerprintCache.get(path);
1772
+ if (cached && cached.mtimeMs === mtimeMs) {
1773
+ return (0, neverthrow.ok)(cached);
1774
+ }
1775
+ const contents = (0, node_fs.readFileSync)(path);
1776
+ const sizeBytes = stats.size;
1777
+ const hash = computeHashSync(contents);
1778
+ const fingerprint = {
1779
+ hash,
1780
+ sizeBytes,
1781
+ mtimeMs
1782
+ };
1783
+ fingerprintCache.set(path, fingerprint);
1784
+ return (0, neverthrow.ok)(fingerprint);
1785
+ } catch (error) {
1786
+ if (error.code === "ENOENT") {
1787
+ return (0, neverthrow.err)({
1788
+ code: "FILE_NOT_FOUND",
1789
+ path,
1790
+ message: `File not found: ${path}`
1791
+ });
1792
+ }
1793
+ return (0, neverthrow.err)({
1794
+ code: "READ_ERROR",
1795
+ path,
1796
+ message: `Failed to read file: ${error}`
1797
+ });
1798
+ }
1799
+ }
1800
+ /**
1801
+ * Compute hash synchronously.
1802
+ * For first call, uses simple string hash as fallback.
1803
+ * Subsequent calls will use xxhash after async initialization.
1804
+ */
1805
+ function computeHashSync(contents) {
1806
+ if (xxhashInstance !== null) {
1807
+ const hash = xxhashInstance.h64Raw(new Uint8Array(contents));
1808
+ return hash.toString(16);
1809
+ }
1810
+ void getXXHash();
1811
+ return simpleHash(contents);
1812
+ }
1813
+ /**
1814
+ * Simple hash function for initial calls before xxhash loads
1815
+ */
1816
+ function simpleHash(buffer) {
1817
+ let hash = 0;
1818
+ for (let i = 0; i < buffer.length; i++) {
1819
+ const byte = buffer[i];
1820
+ if (byte !== undefined) {
1821
+ hash = (hash << 5) - hash + byte;
1822
+ hash = hash & hash;
1823
+ }
1824
+ }
1825
+ return hash.toString(16);
1826
+ }
1827
+ /**
1828
+ * Invalidate cached fingerprint for a specific path
1829
+ *
1830
+ * @param path - Absolute path to invalidate
1831
+ */
1832
+ function invalidateFingerprint(path) {
1833
+ fingerprintCache.delete(path);
1834
+ }
1835
+ /**
1836
+ * Clear all cached fingerprints
1837
+ */
1838
+ function clearFingerprintCache() {
1839
+ fingerprintCache.clear();
1840
+ }
1841
+
1842
+ //#endregion
1843
+ //#region packages/builder/src/discovery/discoverer.ts
1844
+ /**
1845
+ * Discover and analyze all modules starting from entry points.
1846
+ * Uses AST parsing instead of RegExp for reliable dependency extraction.
1847
+ * Supports caching with fingerprint-based invalidation to skip re-parsing unchanged files.
1848
+ */
1849
+ const discoverModules = ({ entryPaths, astAnalyzer, incremental }) => {
1850
+ const snapshots = new Map();
1851
+ const stack = [...entryPaths];
1852
+ const changedFiles = incremental?.changedFiles ?? new Set();
1853
+ const removedFiles = incremental?.removedFiles ?? new Set();
1854
+ const affectedFiles = incremental?.affectedFiles ?? new Set();
1855
+ const invalidatedSet = new Set([
1856
+ ...changedFiles,
1857
+ ...removedFiles,
1858
+ ...affectedFiles
1859
+ ]);
1860
+ let cacheHits = 0;
1861
+ let cacheMisses = 0;
1862
+ let cacheSkips = 0;
1863
+ if (incremental) {
1864
+ for (const filePath of removedFiles) {
1865
+ incremental.cache.delete(filePath);
1866
+ invalidateFingerprint(filePath);
1867
+ }
1868
+ }
1869
+ while (stack.length > 0) {
1870
+ const rawFilePath = stack.pop();
1871
+ if (!rawFilePath) {
1872
+ continue;
1873
+ }
1874
+ const filePath = (0, __soda_gql_common.normalizePath)(rawFilePath);
1875
+ if (snapshots.has(filePath)) {
1876
+ continue;
1877
+ }
1878
+ if (invalidatedSet.has(filePath)) {
1879
+ invalidateFingerprint(filePath);
1880
+ cacheSkips++;
1881
+ } else if (incremental) {
1882
+ const cached = incremental.cache.peek(filePath);
1883
+ if (cached) {
1884
+ try {
1885
+ const stats = (0, node_fs.statSync)(filePath);
1886
+ const mtimeMs = stats.mtimeMs;
1887
+ const sizeBytes = stats.size;
1888
+ if (cached.fingerprint.mtimeMs === mtimeMs && cached.fingerprint.sizeBytes === sizeBytes) {
1889
+ snapshots.set(filePath, cached);
1890
+ cacheHits++;
1891
+ for (const dep of cached.dependencies) {
1892
+ if (dep.resolvedPath && !snapshots.has(dep.resolvedPath)) {
1893
+ stack.push(dep.resolvedPath);
1894
+ }
1895
+ }
1896
+ continue;
1897
+ }
1898
+ } catch {}
1899
+ }
1900
+ }
1901
+ let source;
1902
+ try {
1903
+ source = (0, node_fs.readFileSync)(filePath, "utf8");
1904
+ } catch (error) {
1905
+ if (error.code === "ENOENT") {
1906
+ incremental?.cache.delete(filePath);
1907
+ invalidateFingerprint(filePath);
1908
+ continue;
1909
+ }
1910
+ return (0, neverthrow.err)(builderErrors.discoveryIOError(filePath, error instanceof Error ? error.message : String(error)));
1911
+ }
1912
+ const signature = createSourceHash(source);
1913
+ const analysis = astAnalyzer.analyze({
1914
+ filePath,
1915
+ source
1916
+ });
1917
+ cacheMisses++;
1918
+ const dependencies = extractModuleDependencies(analysis);
1919
+ for (const dep of dependencies) {
1920
+ if (!dep.isExternal && dep.resolvedPath && !snapshots.has(dep.resolvedPath)) {
1921
+ stack.push(dep.resolvedPath);
1922
+ }
1923
+ }
1924
+ const fingerprintResult = computeFingerprint(filePath);
1925
+ if (fingerprintResult.isErr()) {
1926
+ return (0, neverthrow.err)(builderErrors.discoveryIOError(filePath, `Failed to compute fingerprint: ${fingerprintResult.error.message}`));
1927
+ }
1928
+ const fingerprint = fingerprintResult.value;
1929
+ const snapshot = {
1930
+ filePath,
1931
+ normalizedFilePath: (0, __soda_gql_common.normalizePath)(filePath),
1932
+ signature,
1933
+ fingerprint,
1934
+ analyzer: astAnalyzer.type,
1935
+ createdAtMs: Date.now(),
1936
+ analysis,
1937
+ dependencies
1938
+ };
1939
+ snapshots.set(filePath, snapshot);
1940
+ if (incremental) {
1941
+ incremental.cache.store(snapshot);
1942
+ }
1943
+ }
1944
+ return (0, neverthrow.ok)({
1945
+ snapshots: Array.from(snapshots.values()),
1946
+ cacheHits,
1947
+ cacheMisses,
1948
+ cacheSkips
1949
+ });
1950
+ };
1951
+
1952
+ //#endregion
1953
+ //#region packages/builder/src/utils/glob.ts
1954
+ /**
1955
+ * Scan files matching a glob pattern from the given directory
1956
+ * @param pattern - Glob pattern (e.g., "src/**\/*.ts")
1957
+ * @param cwd - Working directory (defaults to process.cwd())
1958
+ * @returns Array of matched file paths (relative to cwd)
1959
+ */
1960
+ const scanGlob = (pattern, cwd = process.cwd()) => {
1961
+ if (typeof Bun !== "undefined" && Bun.Glob) {
1962
+ const { Glob } = Bun;
1963
+ const glob = new Glob(pattern);
1964
+ return Array.from(glob.scanSync(cwd));
1965
+ }
1966
+ return fast_glob.default.sync(pattern, {
1967
+ cwd,
1968
+ dot: true,
1969
+ onlyFiles: true
1970
+ });
1971
+ };
1972
+
1973
+ //#endregion
1974
+ //#region packages/builder/src/discovery/entry-paths.ts
1975
+ const scanEntries = (pattern) => {
1976
+ return scanGlob(pattern, process.cwd());
1977
+ };
1978
+ /**
1979
+ * Resolve entry file paths from glob patterns or direct paths.
1980
+ * Used by the discovery system to find entry points for module traversal.
1981
+ * All paths are normalized to POSIX format for consistent cache key matching.
1982
+ * Uses Node.js normalize() + backslash replacement to match normalizePath from @soda-gql/common.
1983
+ */
1984
+ const resolveEntryPaths = (entries) => {
1985
+ const resolvedPaths = entries.flatMap((entry) => {
1986
+ const absolute = (0, node_path.resolve)(entry);
1987
+ if ((0, node_fs.existsSync)(absolute)) {
1988
+ return [(0, node_path.normalize)(absolute).replace(/\\/g, "/")];
1989
+ }
1990
+ const matches = scanEntries(entry).map((match) => {
1991
+ return (0, node_path.normalize)((0, node_path.resolve)(match)).replace(/\\/g, "/");
1992
+ });
1993
+ return matches;
1994
+ });
1995
+ if (resolvedPaths.length === 0) {
1996
+ return (0, neverthrow.err)({
1997
+ code: "ENTRY_NOT_FOUND",
1998
+ message: `No entry files matched ${entries.join(", ")}`,
1999
+ entry: entries.join(", ")
2000
+ });
2001
+ }
2002
+ return (0, neverthrow.ok)(resolvedPaths);
2003
+ };
2004
+
2005
+ //#endregion
2006
+ //#region packages/builder/src/intermediate-module/codegen.ts
2007
+ const formatFactory = (expression) => {
2008
+ const trimmed = expression.trim();
2009
+ if (!trimmed.includes("\n")) {
2010
+ return trimmed;
2011
+ }
2012
+ const lines = trimmed.split("\n").map((line) => line.trimEnd());
2013
+ const indented = lines.map((line, index) => index === 0 ? line : ` ${line}`).join("\n");
2014
+ return `(\n ${indented}\n )`;
2015
+ };
2016
+ const buildTree = (definitions) => {
2017
+ const roots = new Map();
2018
+ definitions.forEach((definition) => {
2019
+ const parts = definition.astPath.split(".");
2020
+ const expressionText = definition.expression.trim();
2021
+ if (parts.length === 1) {
2022
+ const rootName = parts[0];
2023
+ if (rootName) {
2024
+ roots.set(rootName, {
2025
+ expression: expressionText,
2026
+ canonicalId: definition.canonicalId,
2027
+ children: new Map()
2028
+ });
2029
+ }
2030
+ } else {
2031
+ const rootName = parts[0];
2032
+ if (!rootName) return;
2033
+ let root = roots.get(rootName);
2034
+ if (!root) {
2035
+ root = { children: new Map() };
2036
+ roots.set(rootName, root);
2037
+ }
2038
+ let current = root;
2039
+ for (let i = 1; i < parts.length - 1; i++) {
2040
+ const part = parts[i];
2041
+ if (!part) continue;
2042
+ let child = current.children.get(part);
2043
+ if (!child) {
2044
+ child = { children: new Map() };
2045
+ current.children.set(part, child);
2046
+ }
2047
+ current = child;
2048
+ }
2049
+ const leafName = parts[parts.length - 1];
2050
+ if (leafName) {
2051
+ current.children.set(leafName, {
2052
+ expression: expressionText,
2053
+ canonicalId: definition.canonicalId,
2054
+ children: new Map()
2055
+ });
2056
+ }
2057
+ }
2058
+ });
2059
+ return roots;
2060
+ };
2061
+ /**
2062
+ * Check if a string is a valid JavaScript identifier
2063
+ */
2064
+ const isValidIdentifier = (name) => {
2065
+ return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(name) && !isReservedWord(name);
2066
+ };
2067
+ /**
2068
+ * Check if a string is a JavaScript reserved word
2069
+ */
2070
+ const isReservedWord = (name) => {
2071
+ const reserved = new Set([
2072
+ "break",
2073
+ "case",
2074
+ "catch",
2075
+ "class",
2076
+ "const",
2077
+ "continue",
2078
+ "debugger",
2079
+ "default",
2080
+ "delete",
2081
+ "do",
2082
+ "else",
2083
+ "export",
2084
+ "extends",
2085
+ "finally",
2086
+ "for",
2087
+ "function",
2088
+ "if",
2089
+ "import",
2090
+ "in",
2091
+ "instanceof",
2092
+ "new",
2093
+ "return",
2094
+ "super",
2095
+ "switch",
2096
+ "this",
2097
+ "throw",
2098
+ "try",
2099
+ "typeof",
2100
+ "var",
2101
+ "void",
2102
+ "while",
2103
+ "with",
2104
+ "yield",
2105
+ "let",
2106
+ "static",
2107
+ "enum",
2108
+ "await",
2109
+ "implements",
2110
+ "interface",
2111
+ "package",
2112
+ "private",
2113
+ "protected",
2114
+ "public"
2115
+ ]);
2116
+ return reserved.has(name);
2117
+ };
2118
+ /**
2119
+ * Format a key for use in an object literal
2120
+ * Invalid identifiers are quoted, valid ones are not
2121
+ */
2122
+ const formatObjectKey = (key) => {
2123
+ return isValidIdentifier(key) ? key : `"${key}"`;
2124
+ };
2125
+ const renderTreeNode = (node, indent) => {
2126
+ if (node.expression && node.children.size === 0 && node.canonicalId) {
2127
+ const expr = formatFactory(node.expression);
2128
+ return `registry.addElement("${node.canonicalId}", () => ${expr})`;
2129
+ }
2130
+ const indentStr = " ".repeat(indent);
2131
+ const entries = Array.from(node.children.entries()).map(([key, child]) => {
2132
+ const value = renderTreeNode(child, indent + 1);
2133
+ const formattedKey = formatObjectKey(key);
2134
+ return `${indentStr} ${formattedKey}: ${value},`;
2135
+ });
2136
+ if (entries.length === 0) {
2137
+ return "{}";
2138
+ }
2139
+ return `{\n${entries.join("\n")}\n${indentStr}}`;
2140
+ };
2141
+ const buildNestedObject = (definition) => {
2142
+ const tree = buildTree(definition);
2143
+ const declarations = [];
2144
+ const returnEntries = [];
2145
+ tree.forEach((node, rootName) => {
2146
+ if (node.children.size > 0) {
2147
+ const objectLiteral = renderTreeNode(node, 2);
2148
+ declarations.push(` const ${rootName} = ${objectLiteral};`);
2149
+ returnEntries.push(rootName);
2150
+ } else if (node.expression && node.canonicalId) {
2151
+ const expr = formatFactory(node.expression);
2152
+ declarations.push(` const ${rootName} = registry.addElement("${node.canonicalId}", () => ${expr});`);
2153
+ returnEntries.push(rootName);
2154
+ }
2155
+ });
2156
+ const returnStatement = returnEntries.length > 0 ? ` return {\n${returnEntries.map((name) => ` ${name},`).join("\n")}\n };` : " return {};";
2157
+ if (declarations.length === 0) {
2158
+ return returnStatement;
2159
+ }
2160
+ return `${declarations.join("\n")}\n${returnStatement}`;
2161
+ };
2162
+ /**
2163
+ * Render import statements for the intermediate module using ModuleSummary.
2164
+ * Only includes imports from modules that have gql exports.
2165
+ */
2166
+ const renderImportStatements = ({ filePath, analysis, analyses }) => {
2167
+ const importLines = [];
2168
+ const importedRootNames = new Set();
2169
+ const namespaceImports = new Set();
2170
+ const importsByFile = new Map();
2171
+ analysis.imports.forEach((imp) => {
2172
+ if (imp.isTypeOnly) {
2173
+ return;
2174
+ }
2175
+ if (!imp.source.startsWith(".")) {
2176
+ return;
2177
+ }
2178
+ const resolvedPath = (0, __soda_gql_common.resolveRelativeImportWithReferences)({
2179
+ filePath,
2180
+ specifier: imp.source,
2181
+ references: analyses
2182
+ });
2183
+ if (!resolvedPath) {
2184
+ return;
2185
+ }
2186
+ const imports = importsByFile.get(resolvedPath) ?? [];
2187
+ imports.push(imp);
2188
+ importsByFile.set(resolvedPath, imports);
2189
+ });
2190
+ importsByFile.forEach((imports, filePath$1) => {
2191
+ const namespaceImport = imports.find((imp) => imp.kind === "namespace");
2192
+ if (namespaceImport) {
2193
+ importLines.push(` const ${namespaceImport.local} = registry.importModule("${filePath$1}");`);
2194
+ namespaceImports.add(namespaceImport.local);
2195
+ importedRootNames.add(namespaceImport.local);
2196
+ } else {
2197
+ const rootNames = new Set();
2198
+ imports.forEach((imp) => {
2199
+ if (imp.kind === "named" || imp.kind === "default") {
2200
+ rootNames.add(imp.local);
2201
+ importedRootNames.add(imp.local);
2202
+ }
2203
+ });
2204
+ if (rootNames.size > 0) {
2205
+ const destructured = Array.from(rootNames).sort().join(", ");
2206
+ importLines.push(` const { ${destructured} } = registry.importModule("${filePath$1}");`);
2207
+ }
2208
+ }
2209
+ });
2210
+ return {
2211
+ imports: importLines.length > 0 ? `${importLines.join("\n")}` : "",
2212
+ importedRootNames,
2213
+ namespaceImports
2214
+ };
2215
+ };
2216
+ const renderRegistryBlock = ({ filePath, analysis, analyses }) => {
2217
+ const { imports } = renderImportStatements({
2218
+ filePath,
2219
+ analysis,
2220
+ analyses
2221
+ });
2222
+ return [
2223
+ `registry.setModule("${filePath}", () => {`,
2224
+ imports,
2225
+ "",
2226
+ buildNestedObject(analysis.definitions),
2227
+ "});"
2228
+ ].join("\n");
2229
+ };
2230
+
2231
+ //#endregion
2232
+ //#region packages/builder/src/intermediate-module/registry.ts
2233
+ const createIntermediateRegistry = () => {
2234
+ const modules = new Map();
2235
+ const elements = new Map();
2236
+ const setModule = (filePath, factory) => {
2237
+ let cached;
2238
+ modules.set(filePath, () => cached ??= factory());
2239
+ };
2240
+ const importModule = (filePath) => {
2241
+ const factory = modules.get(filePath);
2242
+ if (!factory) {
2243
+ throw new Error(`Module not found or yet to be registered: ${filePath}`);
2244
+ }
2245
+ return factory();
2246
+ };
2247
+ const addElement = (canonicalId, factory) => {
2248
+ const builder = factory();
2249
+ __soda_gql_core.GqlElement.setContext(builder, { canonicalId });
2250
+ elements.set(canonicalId, builder);
2251
+ return builder;
2252
+ };
2253
+ const evaluate = () => {
2254
+ for (const mod of modules.values()) {
2255
+ mod();
2256
+ }
2257
+ for (const element of elements.values()) {
2258
+ __soda_gql_core.GqlElement.evaluate(element);
2259
+ }
2260
+ const artifacts = {};
2261
+ for (const [canonicalId, element] of elements.entries()) {
2262
+ if (element instanceof __soda_gql_core.Model) {
2263
+ artifacts[canonicalId] = {
2264
+ type: "model",
2265
+ element
2266
+ };
2267
+ } else if (element instanceof __soda_gql_core.Slice) {
2268
+ artifacts[canonicalId] = {
2269
+ type: "slice",
2270
+ element
2271
+ };
2272
+ } else if (element instanceof __soda_gql_core.ComposedOperation) {
2273
+ artifacts[canonicalId] = {
2274
+ type: "operation",
2275
+ element
2276
+ };
2277
+ } else if (element instanceof __soda_gql_core.InlineOperation) {
2278
+ artifacts[canonicalId] = {
2279
+ type: "inlineOperation",
2280
+ element
2281
+ };
2282
+ }
2283
+ }
2284
+ return artifacts;
2285
+ };
2286
+ const clear = () => {
2287
+ modules.clear();
2288
+ elements.clear();
2289
+ };
2290
+ return {
2291
+ setModule,
2292
+ importModule,
2293
+ addElement,
2294
+ evaluate,
2295
+ clear
2296
+ };
2297
+ };
2298
+
2299
+ //#endregion
2300
+ //#region packages/builder/src/intermediate-module/evaluation.ts
2301
+ const transpile = ({ filePath, sourceCode }) => {
2302
+ try {
2303
+ const result = (0, __swc_core.transformSync)(sourceCode, {
2304
+ filename: `${filePath}.ts`,
2305
+ jsc: {
2306
+ parser: {
2307
+ syntax: "typescript",
2308
+ tsx: false
2309
+ },
2310
+ target: "es2022"
2311
+ },
2312
+ module: { type: "es6" },
2313
+ sourceMaps: false,
2314
+ minify: false
2315
+ });
2316
+ return (0, neverthrow.ok)(result.code);
2317
+ } catch (error) {
2318
+ const message = error instanceof Error ? error.message : String(error);
2319
+ return (0, neverthrow.err)({
2320
+ code: "RUNTIME_MODULE_LOAD_FAILED",
2321
+ filePath,
2322
+ astPath: "",
2323
+ message: `SWC transpilation failed: ${message}`
2324
+ });
2325
+ }
2326
+ };
2327
+ /**
2328
+ * Resolve graphql system path to the bundled CJS file.
2329
+ * Accepts both .ts (for backward compatibility) and .cjs paths.
2330
+ * Maps .ts to sibling .cjs file if it exists.
2331
+ */
2332
+ function resolveGraphqlSystemPath(configPath) {
2333
+ const ext = (0, node_path.extname)(configPath);
2334
+ if (ext === ".cjs") {
2335
+ return (0, node_path.resolve)(process.cwd(), configPath);
2336
+ }
2337
+ if (ext === ".ts") {
2338
+ const basePath = configPath.slice(0, -3);
2339
+ const cjsPath = `${basePath}.cjs`;
2340
+ const resolvedCjsPath = (0, node_path.resolve)(process.cwd(), cjsPath);
2341
+ if ((0, node_fs.existsSync)(resolvedCjsPath)) {
2342
+ return resolvedCjsPath;
2343
+ }
2344
+ return (0, node_path.resolve)(process.cwd(), configPath);
2345
+ }
2346
+ return (0, node_path.resolve)(process.cwd(), configPath);
2347
+ }
2348
+ /**
2349
+ * Bundle and execute GraphQL system module using rspack + memfs.
2350
+ * Creates a self-contained bundle that can run in VM context.
2351
+ * This is cached per session to avoid re-bundling.
2352
+ */
2353
+ let cachedGql = null;
2354
+ let cachedModulePath = null;
2355
+ function executeGraphqlSystemModule(modulePath) {
2356
+ if (cachedModulePath === modulePath && cachedGql !== null) {
2357
+ return { gql: cachedGql };
2358
+ }
2359
+ const bundledCode = (0, node_fs.readFileSync)(modulePath, "utf-8");
2360
+ const moduleExports = {};
2361
+ const sandbox = {
2362
+ require: (path) => {
2363
+ if (path === "@soda-gql/core") {
2364
+ return __soda_gql_core;
2365
+ }
2366
+ if (path === "@soda-gql/runtime") {
2367
+ return __soda_gql_runtime;
2368
+ }
2369
+ throw new Error(`Unknown module: ${path}`);
2370
+ },
2371
+ module: { exports: moduleExports },
2372
+ exports: moduleExports,
2373
+ __dirname: (0, node_path.resolve)(modulePath, ".."),
2374
+ __filename: modulePath,
2375
+ global: undefined,
2376
+ globalThis: undefined
2377
+ };
2378
+ sandbox.global = sandbox;
2379
+ sandbox.globalThis = sandbox;
2380
+ new node_vm.Script(bundledCode, { filename: modulePath }).runInNewContext(sandbox);
2381
+ const exportedGql = moduleExports.gql ?? moduleExports.default;
2382
+ if (exportedGql === undefined) {
2383
+ throw new Error(`No 'gql' export found in GraphQL system module: ${modulePath}`);
2384
+ }
2385
+ cachedGql = exportedGql;
2386
+ cachedModulePath = modulePath;
2387
+ return { gql: cachedGql };
2388
+ }
2389
+ /**
2390
+ * Build intermediate modules from dependency graph.
2391
+ * Each intermediate module corresponds to one source file.
2392
+ */
2393
+ const generateIntermediateModules = function* ({ analyses, targetFiles }) {
2394
+ for (const filePath of targetFiles) {
2395
+ const analysis = analyses.get(filePath);
2396
+ if (!analysis) {
2397
+ continue;
2398
+ }
2399
+ const sourceCode = renderRegistryBlock({
2400
+ filePath,
2401
+ analysis,
2402
+ analyses
2403
+ });
2404
+ const transpiledCodeResult = transpile({
2405
+ filePath,
2406
+ sourceCode
2407
+ });
2408
+ if (transpiledCodeResult.isErr()) {
2409
+ continue;
2410
+ }
2411
+ const transpiledCode = transpiledCodeResult.value;
2412
+ const script = new node_vm.Script(transpiledCode);
2413
+ const hash = (0, node_crypto.createHash)("sha1");
2414
+ hash.update(transpiledCode);
2415
+ const contentHash = hash.digest("hex");
2416
+ const canonicalIds = analysis.definitions.map((definition) => definition.canonicalId);
2417
+ yield {
2418
+ filePath,
2419
+ canonicalIds,
2420
+ sourceCode,
2421
+ transpiledCode,
2422
+ contentHash,
2423
+ script
2424
+ };
2425
+ }
2426
+ };
2427
+ const evaluateIntermediateModules = ({ intermediateModules, graphqlSystemPath }) => {
2428
+ const registry = createIntermediateRegistry();
2429
+ const gqlImportPath = resolveGraphqlSystemPath(graphqlSystemPath);
2430
+ const { gql } = executeGraphqlSystemModule(gqlImportPath);
2431
+ const vmContext = (0, node_vm.createContext)({
2432
+ gql,
2433
+ registry
2434
+ });
2435
+ for (const { script, filePath } of intermediateModules.values()) {
2436
+ try {
2437
+ script.runInContext(vmContext);
2438
+ } catch (error) {
2439
+ console.error(`Error evaluating intermediate module ${filePath}:`, error);
2440
+ throw error;
2441
+ }
2442
+ }
2443
+ const elements = registry.evaluate();
2444
+ registry.clear();
2445
+ return elements;
2446
+ };
2447
+
2448
+ //#endregion
2449
+ //#region packages/builder/src/internal/graphql-system.ts
2450
+ /**
2451
+ * Create a canonical file name getter based on platform.
2452
+ * On case-sensitive filesystems, paths are returned as-is.
2453
+ * On case-insensitive filesystems, paths are lowercased for comparison.
2454
+ */
2455
+ const createGetCanonicalFileName = (useCaseSensitiveFileNames) => {
2456
+ return useCaseSensitiveFileNames ? (path) => path : (path) => path.toLowerCase();
2457
+ };
2458
+ /**
2459
+ * Detect if the filesystem is case-sensitive.
2460
+ * We assume Unix-like systems are case-sensitive, and Windows is not.
2461
+ */
2462
+ const getUseCaseSensitiveFileNames = () => {
2463
+ return process.platform !== "win32";
2464
+ };
2465
+ /**
2466
+ * Create a GraphqlSystemIdentifyHelper from the resolved config.
2467
+ * Uses canonical path comparison to handle casing, symlinks, and aliases.
2468
+ */
2469
+ const createGraphqlSystemIdentifyHelper = (config) => {
2470
+ const getCanonicalFileName = createGetCanonicalFileName(getUseCaseSensitiveFileNames());
2471
+ const toCanonical = (file) => {
2472
+ const resolved = (0, node_path.resolve)(file);
2473
+ return getCanonicalFileName(resolved);
2474
+ };
2475
+ const graphqlSystemPath = (0, node_path.resolve)(config.outdir, "index.ts");
2476
+ const canonicalGraphqlSystemPath = toCanonical(graphqlSystemPath);
2477
+ const canonicalAliases = new Set(config.graphqlSystemAliases.map((alias) => alias));
2478
+ return {
2479
+ isGraphqlSystemFile: ({ filePath }) => {
2480
+ return toCanonical(filePath) === canonicalGraphqlSystemPath;
2481
+ },
2482
+ isGraphqlSystemImportSpecifier: ({ filePath, specifier }) => {
2483
+ if (canonicalAliases.has(specifier)) {
2484
+ return true;
2485
+ }
2486
+ if (!specifier.startsWith(".")) {
2487
+ return false;
2488
+ }
2489
+ const resolved = (0, __soda_gql_common.resolveRelativeImportWithExistenceCheck)({
2490
+ filePath,
2491
+ specifier
2492
+ });
2493
+ if (!resolved) {
2494
+ return false;
2495
+ }
2496
+ return toCanonical(resolved) === canonicalGraphqlSystemPath;
2497
+ }
2498
+ };
2499
+ };
2500
+
2501
+ //#endregion
2502
+ //#region packages/builder/src/tracker/file-tracker.ts
2503
+ /**
2504
+ * Create a file tracker that maintains in-memory state for change detection.
2505
+ *
2506
+ * The tracker keeps file metadata (mtime, size) in memory during the process lifetime
2507
+ * and detects which files have been added, updated, or removed. State is scoped to
2508
+ * the process and does not persist across restarts.
2509
+ */
2510
+ const createFileTracker = () => {
2511
+ let currentScan = { files: new Map() };
2512
+ let nextScan = null;
2513
+ const scan = (extraPaths) => {
2514
+ const allPathsToScan = new Set([...extraPaths, ...currentScan.files.keys()]);
2515
+ const files = new Map();
2516
+ for (const path of allPathsToScan) {
2517
+ try {
2518
+ const normalized = (0, __soda_gql_common.normalizePath)(path);
2519
+ const stats = (0, node_fs.statSync)(normalized);
2520
+ files.set(normalized, {
2521
+ mtimeMs: stats.mtimeMs,
2522
+ size: stats.size
2523
+ });
2524
+ } catch {}
2525
+ }
2526
+ nextScan = { files };
2527
+ return (0, neverthrow.ok)(nextScan);
2528
+ };
2529
+ const detectChanges = () => {
2530
+ const previous = currentScan;
2531
+ const current = nextScan ?? currentScan;
2532
+ const added = new Set();
2533
+ const updated = new Set();
2534
+ const removed = new Set();
2535
+ for (const [path, currentMetadata] of current.files) {
2536
+ const previousMetadata = previous.files.get(path);
2537
+ if (!previousMetadata) {
2538
+ added.add(path);
2539
+ } else if (previousMetadata.mtimeMs !== currentMetadata.mtimeMs || previousMetadata.size !== currentMetadata.size) {
2540
+ updated.add(path);
2541
+ }
2542
+ }
2543
+ for (const path of previous.files.keys()) {
2544
+ if (!current.files.has(path)) {
2545
+ removed.add(path);
2546
+ }
2547
+ }
2548
+ return {
2549
+ added,
2550
+ updated,
2551
+ removed
2552
+ };
2553
+ };
2554
+ const update = (scan$1) => {
2555
+ currentScan = scan$1;
2556
+ nextScan = null;
2557
+ };
2558
+ return {
2559
+ scan,
2560
+ detectChanges,
2561
+ update
2562
+ };
2563
+ };
2564
+ /**
2565
+ * Check if a file diff is empty (no changes detected).
2566
+ */
2567
+ const isEmptyDiff = (diff) => {
2568
+ return diff.added.size === 0 && diff.updated.size === 0 && diff.removed.size === 0;
2569
+ };
2570
+
2571
+ //#endregion
2572
+ //#region packages/builder/src/session/dependency-validation.ts
2573
+ const validateModuleDependencies = ({ analyses }) => {
2574
+ for (const analysis of analyses.values()) {
2575
+ for (const { source, isTypeOnly } of analysis.imports) {
2576
+ if (isTypeOnly) {
2577
+ continue;
2578
+ }
2579
+ if ((0, __soda_gql_common.isRelativeSpecifier)(source)) {
2580
+ const resolvedModule = (0, __soda_gql_common.resolveRelativeImportWithReferences)({
2581
+ filePath: analysis.filePath,
2582
+ specifier: source,
2583
+ references: analyses
2584
+ });
2585
+ if (!resolvedModule) {
2586
+ return (0, neverthrow.err)({
2587
+ code: "MISSING_IMPORT",
2588
+ chain: [analysis.filePath, source]
2589
+ });
2590
+ }
2591
+ }
2592
+ }
2593
+ }
2594
+ return (0, neverthrow.ok)(null);
2595
+ };
2596
+
2597
+ //#endregion
2598
+ //#region packages/builder/src/session/module-adjacency.ts
2599
+ /**
2600
+ * Extract module-level adjacency from dependency graph.
2601
+ * Returns Map of file path -> set of files that import it.
2602
+ * All paths are normalized to POSIX format for consistent cache key matching.
2603
+ */
2604
+ const extractModuleAdjacency = ({ snapshots }) => {
2605
+ const importsByModule = new Map();
2606
+ for (const snapshot of snapshots.values()) {
2607
+ const { normalizedFilePath, dependencies, analysis } = snapshot;
2608
+ const imports = new Set();
2609
+ for (const { resolvedPath } of dependencies) {
2610
+ if (resolvedPath && resolvedPath !== normalizedFilePath && snapshots.has(resolvedPath)) {
2611
+ imports.add(resolvedPath);
2612
+ }
2613
+ }
2614
+ if (dependencies.length === 0 && analysis.imports.length > 0) {
2615
+ for (const imp of analysis.imports) {
2616
+ if (imp.isTypeOnly) {
2617
+ continue;
2618
+ }
2619
+ const resolved = (0, __soda_gql_common.resolveRelativeImportWithReferences)({
2620
+ filePath: normalizedFilePath,
2621
+ specifier: imp.source,
2622
+ references: snapshots
2623
+ });
2624
+ if (resolved) {
2625
+ imports.add(resolved);
2626
+ }
2627
+ }
2628
+ }
2629
+ if (imports.size > 0) {
2630
+ importsByModule.set(normalizedFilePath, imports);
2631
+ }
2632
+ }
2633
+ const adjacency = new Map();
2634
+ for (const [importer, imports] of importsByModule) {
2635
+ for (const imported of imports) {
2636
+ if (!adjacency.has(imported)) {
2637
+ adjacency.set(imported, new Set());
2638
+ }
2639
+ adjacency.get(imported)?.add(importer);
2640
+ }
2641
+ }
2642
+ for (const modulePath of snapshots.keys()) {
2643
+ if (!adjacency.has(modulePath)) {
2644
+ adjacency.set(modulePath, new Set());
2645
+ }
2646
+ }
2647
+ return adjacency;
2648
+ };
2649
+ /**
2650
+ * Collect all modules affected by changes, including transitive dependents.
2651
+ * Uses BFS to traverse module adjacency graph.
2652
+ * All paths are already normalized from extractModuleAdjacency.
2653
+ */
2654
+ const collectAffectedFiles = (input) => {
2655
+ const { changedFiles, removedFiles, previousModuleAdjacency } = input;
2656
+ const affected = new Set([...changedFiles, ...removedFiles]);
2657
+ const queue = [...changedFiles];
2658
+ const visited = new Set(changedFiles);
2659
+ while (queue.length > 0) {
2660
+ const current = queue.shift();
2661
+ if (!current) break;
2662
+ const dependents = previousModuleAdjacency.get(current);
2663
+ if (dependents) {
2664
+ for (const dependent of dependents) {
2665
+ if (!visited.has(dependent)) {
2666
+ visited.add(dependent);
2667
+ affected.add(dependent);
2668
+ queue.push(dependent);
2669
+ }
2670
+ }
2671
+ }
2672
+ }
2673
+ return affected;
2674
+ };
2675
+
2676
+ //#endregion
2677
+ //#region packages/builder/src/session/builder-session.ts
2678
+ /**
2679
+ * Create a new builder session.
2680
+ *
2681
+ * The session maintains in-memory state across builds to enable incremental processing.
2682
+ */
2683
+ const createBuilderSession = (options) => {
2684
+ const config = options.config;
2685
+ const evaluatorId = options.evaluatorId ?? "default";
2686
+ const entrypoints = new Set(options.entrypointsOverride ?? config.include);
2687
+ const state = {
2688
+ gen: 0,
2689
+ snapshots: new Map(),
2690
+ moduleAdjacency: new Map(),
2691
+ intermediateModules: new Map(),
2692
+ lastArtifact: null
2693
+ };
2694
+ const cacheFactory = createJsonCache({
2695
+ rootDir: (0, node_path.join)(process.cwd(), ".cache", "soda-gql", "builder"),
2696
+ prefix: ["builder"]
2697
+ });
2698
+ const graphqlHelper = createGraphqlSystemIdentifyHelper(config);
2699
+ const ensureAstAnalyzer = (0, __soda_gql_common.cachedFn)(() => createAstAnalyzer({
2700
+ analyzer: config.analyzer,
2701
+ graphqlHelper
2702
+ }));
2703
+ const ensureDiscoveryCache = (0, __soda_gql_common.cachedFn)(() => createDiscoveryCache({
2704
+ factory: cacheFactory,
2705
+ analyzer: config.analyzer,
2706
+ evaluatorId
2707
+ }));
2708
+ const ensureFileTracker = (0, __soda_gql_common.cachedFn)(() => createFileTracker());
2709
+ const build = (options$1) => {
2710
+ const force = options$1?.force ?? false;
2711
+ const entryPathsResult = resolveEntryPaths(Array.from(entrypoints));
2712
+ if (entryPathsResult.isErr()) {
2713
+ return (0, neverthrow.err)(entryPathsResult.error);
2714
+ }
2715
+ const entryPaths = entryPathsResult.value;
2716
+ const tracker = ensureFileTracker();
2717
+ const scanResult = tracker.scan(entryPaths);
2718
+ if (scanResult.isErr()) {
2719
+ const trackerError = scanResult.error;
2720
+ return (0, neverthrow.err)(builderErrors.discoveryIOError(trackerError.type === "scan-failed" ? trackerError.path : "unknown", `Failed to scan files: ${trackerError.message}`));
2721
+ }
2722
+ const currentScan = scanResult.value;
2723
+ const diff = tracker.detectChanges();
2724
+ const prepareResult = prepare({
2725
+ diff,
2726
+ entryPaths,
2727
+ lastArtifact: state.lastArtifact,
2728
+ force
2729
+ });
2730
+ if (prepareResult.isErr()) {
2731
+ return (0, neverthrow.err)(prepareResult.error);
2732
+ }
2733
+ if (prepareResult.value.type === "should-skip") {
2734
+ return (0, neverthrow.ok)(prepareResult.value.data.artifact);
2735
+ }
2736
+ const { changedFiles, removedFiles } = prepareResult.value.data;
2737
+ const discoveryCache = ensureDiscoveryCache();
2738
+ const astAnalyzer = ensureAstAnalyzer();
2739
+ const discoveryResult = discover({
2740
+ discoveryCache,
2741
+ astAnalyzer,
2742
+ removedFiles,
2743
+ changedFiles,
2744
+ entryPaths,
2745
+ previousModuleAdjacency: state.moduleAdjacency
2746
+ });
2747
+ if (discoveryResult.isErr()) {
2748
+ return (0, neverthrow.err)(discoveryResult.error);
2749
+ }
2750
+ const { snapshots, analyses, currentModuleAdjacency, affectedFiles, stats } = discoveryResult.value;
2751
+ const buildResult = buildDiscovered({
2752
+ analyses,
2753
+ affectedFiles,
2754
+ stats,
2755
+ previousIntermediateModules: state.intermediateModules,
2756
+ graphqlSystemPath: (0, node_path.resolve)(config.outdir, "index.ts")
2757
+ });
2758
+ if (buildResult.isErr()) {
2759
+ return (0, neverthrow.err)(buildResult.error);
2760
+ }
2761
+ const { intermediateModules, artifact } = buildResult.value;
2762
+ state.gen++;
2763
+ state.snapshots = snapshots;
2764
+ state.moduleAdjacency = currentModuleAdjacency;
2765
+ state.lastArtifact = artifact;
2766
+ state.intermediateModules = intermediateModules;
2767
+ tracker.update(currentScan);
2768
+ return (0, neverthrow.ok)(artifact);
2769
+ };
2770
+ return {
2771
+ build,
2772
+ getGeneration: () => state.gen,
2773
+ getCurrentArtifact: () => state.lastArtifact
2774
+ };
2775
+ };
2776
+ const prepare = (input) => {
2777
+ const { diff, lastArtifact, force } = input;
2778
+ const changedFiles = new Set([...diff.added, ...diff.updated]);
2779
+ const removedFiles = diff.removed;
2780
+ if (!force && isEmptyDiff(diff) && lastArtifact) {
2781
+ return (0, neverthrow.ok)({
2782
+ type: "should-skip",
2783
+ data: { artifact: lastArtifact }
2784
+ });
2785
+ }
2786
+ return (0, neverthrow.ok)({
2787
+ type: "should-build",
2788
+ data: {
2789
+ changedFiles,
2790
+ removedFiles
2791
+ }
2792
+ });
2793
+ };
2794
+ const discover = ({ discoveryCache, astAnalyzer, removedFiles, changedFiles, entryPaths, previousModuleAdjacency }) => {
2795
+ const affectedFiles = collectAffectedFiles({
2796
+ changedFiles,
2797
+ removedFiles,
2798
+ previousModuleAdjacency
2799
+ });
2800
+ const discoveryResult = discoverModules({
2801
+ entryPaths,
2802
+ astAnalyzer,
2803
+ incremental: {
2804
+ cache: discoveryCache,
2805
+ changedFiles,
2806
+ removedFiles,
2807
+ affectedFiles
2808
+ }
2809
+ });
2810
+ if (discoveryResult.isErr()) {
2811
+ return (0, neverthrow.err)(discoveryResult.error);
2812
+ }
2813
+ const { cacheHits, cacheMisses, cacheSkips } = discoveryResult.value;
2814
+ const snapshots = new Map(discoveryResult.value.snapshots.map((snapshot) => [snapshot.normalizedFilePath, snapshot]));
2815
+ const analyses = new Map(discoveryResult.value.snapshots.map((snapshot) => [snapshot.normalizedFilePath, snapshot.analysis]));
2816
+ const dependenciesValidationResult = validateModuleDependencies({ analyses });
2817
+ if (dependenciesValidationResult.isErr()) {
2818
+ const error = dependenciesValidationResult.error;
2819
+ return (0, neverthrow.err)(builderErrors.graphMissingImport(error.chain[0], error.chain[1]));
2820
+ }
2821
+ const currentModuleAdjacency = extractModuleAdjacency({ snapshots });
2822
+ const stats = {
2823
+ hits: cacheHits,
2824
+ misses: cacheMisses,
2825
+ skips: cacheSkips
2826
+ };
2827
+ return (0, neverthrow.ok)({
2828
+ snapshots,
2829
+ analyses,
2830
+ currentModuleAdjacency,
2831
+ affectedFiles,
2832
+ stats
2833
+ });
2834
+ };
2835
+ const buildDiscovered = ({ analyses, affectedFiles, stats, previousIntermediateModules, graphqlSystemPath }) => {
2836
+ const intermediateModules = new Map(previousIntermediateModules);
2837
+ const targetFiles = new Set(affectedFiles);
2838
+ for (const filePath of analyses.keys()) {
2839
+ if (!previousIntermediateModules.has(filePath)) {
2840
+ targetFiles.add(filePath);
2841
+ }
2842
+ }
2843
+ if (targetFiles.size === 0) {
2844
+ for (const filePath of analyses.keys()) {
2845
+ targetFiles.add(filePath);
2846
+ }
2847
+ }
2848
+ for (const targetFilePath of targetFiles) {
2849
+ intermediateModules.delete(targetFilePath);
2850
+ }
2851
+ for (const intermediateModule of generateIntermediateModules({
2852
+ analyses,
2853
+ targetFiles
2854
+ })) {
2855
+ intermediateModules.set(intermediateModule.filePath, intermediateModule);
2856
+ }
2857
+ const elements = evaluateIntermediateModules({
2858
+ intermediateModules,
2859
+ graphqlSystemPath
2860
+ });
2861
+ const artifactResult = buildArtifact({
2862
+ analyses,
2863
+ elements,
2864
+ stats
2865
+ });
2866
+ if (artifactResult.isErr()) {
2867
+ return (0, neverthrow.err)(artifactResult.error);
2868
+ }
2869
+ return (0, neverthrow.ok)({
2870
+ intermediateModules,
2871
+ artifact: artifactResult.value
2872
+ });
2873
+ };
2874
+
2875
+ //#endregion
2876
+ //#region packages/builder/src/service.ts
2877
+ /**
2878
+ * Create a builder service instance with session support.
2879
+ *
2880
+ * The service maintains a long-lived session for incremental builds.
2881
+ * File changes are automatically detected using an internal file tracker.
2882
+ * First build() call initializes the session, subsequent calls perform
2883
+ * incremental builds based on detected file changes.
2884
+ *
2885
+ * Note: Empty entry arrays will produce ENTRY_NOT_FOUND errors at build time.
2886
+ *
2887
+ * @param config - Builder configuration including entry patterns, analyzer, mode, and optional debugDir
2888
+ * @returns BuilderService instance
2889
+ */
2890
+ const createBuilderService = ({ config, entrypointsOverride }) => {
2891
+ const session = createBuilderSession({
2892
+ config,
2893
+ entrypointsOverride
2894
+ });
2895
+ return {
2896
+ build: (options) => session.build(options),
2897
+ getGeneration: () => session.getGeneration(),
2898
+ getCurrentArtifact: () => session.getCurrentArtifact()
2899
+ };
2900
+ };
2901
+
2902
+ //#endregion
2903
+ exports.BuilderArtifactSchema = BuilderArtifactSchema;
2904
+ Object.defineProperty(exports, 'buildAstPath', {
2905
+ enumerable: true,
2906
+ get: function () {
2907
+ return __soda_gql_common.buildAstPath;
2908
+ }
2909
+ });
2910
+ exports.createBuilderService = createBuilderService;
2911
+ exports.createBuilderSession = createBuilderSession;
2912
+ Object.defineProperty(exports, 'createCanonicalId', {
2913
+ enumerable: true,
2914
+ get: function () {
2915
+ return __soda_gql_common.createCanonicalId;
2916
+ }
2917
+ });
2918
+ Object.defineProperty(exports, 'createCanonicalTracker', {
2919
+ enumerable: true,
2920
+ get: function () {
2921
+ return __soda_gql_common.createCanonicalTracker;
2922
+ }
2923
+ });
2924
+ Object.defineProperty(exports, 'createOccurrenceTracker', {
2925
+ enumerable: true,
2926
+ get: function () {
2927
+ return __soda_gql_common.createOccurrenceTracker;
2928
+ }
2929
+ });
2930
+ Object.defineProperty(exports, 'createPathTracker', {
2931
+ enumerable: true,
2932
+ get: function () {
2933
+ return __soda_gql_common.createPathTracker;
2934
+ }
2935
+ });