@soda-gql/builder 0.6.0 → 0.8.0
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 +481 -248
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +104 -4
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +105 -5
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +479 -250
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -1,20 +1,461 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { existsSync, mkdirSync, readFileSync, realpathSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { readFile, stat } from "node:fs/promises";
|
|
3
|
+
import { err, ok } from "neverthrow";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
3
6
|
import { dirname, extname, join, normalize, resolve } from "node:path";
|
|
4
7
|
import { Script, createContext } from "node:vm";
|
|
5
|
-
import { CanonicalIdSchema, Effect, Effects, ParallelEffect, cachedFn, createAsyncScheduler, createCanonicalId, createCanonicalTracker, createSyncScheduler, getPortableHasher, isExternalSpecifier, isRelativeSpecifier, normalizePath, resolveRelativeImportWithExistenceCheck, resolveRelativeImportWithReferences } from "@soda-gql/common";
|
|
8
|
+
import { CanonicalIdSchema, Effect, Effects, ParallelEffect, cachedFn, createAsyncScheduler, createCanonicalId, createCanonicalTracker, createSyncScheduler, getPortableHasher, isExternalSpecifier, isRelativeSpecifier, normalizePath, parseCanonicalId, resolveRelativeImportWithExistenceCheck, resolveRelativeImportWithReferences } from "@soda-gql/common";
|
|
6
9
|
import * as sandboxCore from "@soda-gql/core";
|
|
7
10
|
import { Fragment, GqlElement, Operation } from "@soda-gql/core";
|
|
8
11
|
import * as sandboxCoreAdapter from "@soda-gql/core/adapter";
|
|
9
12
|
import * as sandboxCoreRuntime from "@soda-gql/core/runtime";
|
|
10
13
|
import * as sandboxRuntime from "@soda-gql/runtime";
|
|
11
14
|
import { parseSync, transformSync } from "@swc/core";
|
|
12
|
-
import { err, ok } from "neverthrow";
|
|
13
|
-
import { readFile, stat } from "node:fs/promises";
|
|
14
|
-
import { z } from "zod";
|
|
15
15
|
import ts from "typescript";
|
|
16
16
|
import fg from "fast-glob";
|
|
17
17
|
|
|
18
|
+
//#region packages/builder/src/schemas/artifact.ts
|
|
19
|
+
const BuilderArtifactElementMetadataSchema = z.object({
|
|
20
|
+
sourcePath: z.string(),
|
|
21
|
+
contentHash: z.string()
|
|
22
|
+
});
|
|
23
|
+
const BuilderArtifactOperationSchema = z.object({
|
|
24
|
+
id: z.string(),
|
|
25
|
+
type: z.literal("operation"),
|
|
26
|
+
metadata: BuilderArtifactElementMetadataSchema,
|
|
27
|
+
prebuild: z.object({
|
|
28
|
+
operationType: z.enum([
|
|
29
|
+
"query",
|
|
30
|
+
"mutation",
|
|
31
|
+
"subscription"
|
|
32
|
+
]),
|
|
33
|
+
operationName: z.string(),
|
|
34
|
+
document: z.unknown(),
|
|
35
|
+
variableNames: z.array(z.string())
|
|
36
|
+
})
|
|
37
|
+
});
|
|
38
|
+
const BuilderArtifactFragmentSchema = z.object({
|
|
39
|
+
id: z.string(),
|
|
40
|
+
type: z.literal("fragment"),
|
|
41
|
+
metadata: BuilderArtifactElementMetadataSchema,
|
|
42
|
+
prebuild: z.object({ typename: z.string() })
|
|
43
|
+
});
|
|
44
|
+
const BuilderArtifactElementSchema = z.discriminatedUnion("type", [BuilderArtifactOperationSchema, BuilderArtifactFragmentSchema]);
|
|
45
|
+
const BuilderArtifactMetaSchema = z.object({
|
|
46
|
+
version: z.string(),
|
|
47
|
+
createdAt: z.string()
|
|
48
|
+
});
|
|
49
|
+
const BuilderArtifactSchema = z.object({
|
|
50
|
+
meta: BuilderArtifactMetaSchema.optional(),
|
|
51
|
+
elements: z.record(z.string(), BuilderArtifactElementSchema),
|
|
52
|
+
report: z.object({
|
|
53
|
+
durationMs: z.number(),
|
|
54
|
+
warnings: z.array(z.string()),
|
|
55
|
+
stats: z.object({
|
|
56
|
+
hits: z.number(),
|
|
57
|
+
misses: z.number(),
|
|
58
|
+
skips: z.number()
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region packages/builder/src/artifact/loader.ts
|
|
65
|
+
/**
|
|
66
|
+
* Load a pre-built artifact from a JSON file asynchronously.
|
|
67
|
+
*
|
|
68
|
+
* @param path - Absolute path to the artifact JSON file
|
|
69
|
+
* @returns Result with the parsed artifact or an error
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const result = await loadArtifact("/path/to/artifact.json");
|
|
74
|
+
* if (result.isOk()) {
|
|
75
|
+
* const artifact = result.value;
|
|
76
|
+
* // Use artifact...
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
const loadArtifact = async (path) => {
|
|
81
|
+
if (!existsSync(path)) {
|
|
82
|
+
return err({
|
|
83
|
+
code: "ARTIFACT_NOT_FOUND",
|
|
84
|
+
message: `Artifact file not found: ${path}`,
|
|
85
|
+
filePath: path
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
let content;
|
|
89
|
+
try {
|
|
90
|
+
content = await readFile(path, "utf-8");
|
|
91
|
+
} catch (error) {
|
|
92
|
+
return err({
|
|
93
|
+
code: "ARTIFACT_NOT_FOUND",
|
|
94
|
+
message: `Failed to read artifact file: ${error instanceof Error ? error.message : String(error)}`,
|
|
95
|
+
filePath: path
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
return parseAndValidateArtifact(content, path);
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Load a pre-built artifact from a JSON file synchronously.
|
|
102
|
+
*
|
|
103
|
+
* @param path - Absolute path to the artifact JSON file
|
|
104
|
+
* @returns Result with the parsed artifact or an error
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* const result = loadArtifactSync("/path/to/artifact.json");
|
|
109
|
+
* if (result.isOk()) {
|
|
110
|
+
* const artifact = result.value;
|
|
111
|
+
* // Use artifact...
|
|
112
|
+
* }
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
const loadArtifactSync = (path) => {
|
|
116
|
+
if (!existsSync(path)) {
|
|
117
|
+
return err({
|
|
118
|
+
code: "ARTIFACT_NOT_FOUND",
|
|
119
|
+
message: `Artifact file not found: ${path}`,
|
|
120
|
+
filePath: path
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
let content;
|
|
124
|
+
try {
|
|
125
|
+
content = readFileSync(path, "utf-8");
|
|
126
|
+
} catch (error) {
|
|
127
|
+
return err({
|
|
128
|
+
code: "ARTIFACT_NOT_FOUND",
|
|
129
|
+
message: `Failed to read artifact file: ${error instanceof Error ? error.message : String(error)}`,
|
|
130
|
+
filePath: path
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return parseAndValidateArtifact(content, path);
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Parse JSON content and validate against BuilderArtifactSchema.
|
|
137
|
+
*/
|
|
138
|
+
function parseAndValidateArtifact(content, filePath) {
|
|
139
|
+
let parsed;
|
|
140
|
+
try {
|
|
141
|
+
parsed = JSON.parse(content);
|
|
142
|
+
} catch (error) {
|
|
143
|
+
return err({
|
|
144
|
+
code: "ARTIFACT_PARSE_ERROR",
|
|
145
|
+
message: `Invalid JSON in artifact file: ${error instanceof Error ? error.message : String(error)}`,
|
|
146
|
+
filePath
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const validated = BuilderArtifactSchema.safeParse(parsed);
|
|
150
|
+
if (!validated.success) {
|
|
151
|
+
return err({
|
|
152
|
+
code: "ARTIFACT_VALIDATION_ERROR",
|
|
153
|
+
message: `Invalid artifact structure: ${validated.error.message}`,
|
|
154
|
+
filePath
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return ok(validated.data);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region packages/builder/src/errors/formatter.ts
|
|
162
|
+
/**
|
|
163
|
+
* Hints for each error code to help users understand and fix issues.
|
|
164
|
+
*/
|
|
165
|
+
const errorHints = {
|
|
166
|
+
ELEMENT_EVALUATION_FAILED: "Check if all imported fragments are properly exported and included in entry patterns.",
|
|
167
|
+
GRAPH_CIRCULAR_DEPENDENCY: "Break the circular import by extracting shared types to a common module.",
|
|
168
|
+
GRAPH_MISSING_IMPORT: "Verify the import path exists and the module is included in entry patterns.",
|
|
169
|
+
RUNTIME_MODULE_LOAD_FAILED: "Ensure the module can be imported and all dependencies are installed.",
|
|
170
|
+
CONFIG_NOT_FOUND: "Create a soda-gql.config.ts file in your project root.",
|
|
171
|
+
CONFIG_INVALID: "Check your configuration file for syntax errors or invalid options.",
|
|
172
|
+
ENTRY_NOT_FOUND: "Verify the entry pattern matches your file structure.",
|
|
173
|
+
INTERNAL_INVARIANT: "This is an internal error. Please report it at https://github.com/soda-gql/soda-gql/issues"
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* Format a BuilderError into a structured FormattedError object.
|
|
177
|
+
*/
|
|
178
|
+
const formatBuilderErrorStructured = (error) => {
|
|
179
|
+
const base = {
|
|
180
|
+
code: error.code,
|
|
181
|
+
message: error.message,
|
|
182
|
+
hint: errorHints[error.code],
|
|
183
|
+
cause: "cause" in error ? error.cause : undefined
|
|
184
|
+
};
|
|
185
|
+
switch (error.code) {
|
|
186
|
+
case "ELEMENT_EVALUATION_FAILED": return {
|
|
187
|
+
...base,
|
|
188
|
+
location: {
|
|
189
|
+
modulePath: error.modulePath,
|
|
190
|
+
astPath: error.astPath || undefined
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
case "RUNTIME_MODULE_LOAD_FAILED": return {
|
|
194
|
+
...base,
|
|
195
|
+
location: {
|
|
196
|
+
modulePath: error.filePath,
|
|
197
|
+
astPath: error.astPath
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
case "GRAPH_MISSING_IMPORT": return {
|
|
201
|
+
...base,
|
|
202
|
+
relatedFiles: [error.importer, error.importee]
|
|
203
|
+
};
|
|
204
|
+
case "GRAPH_CIRCULAR_DEPENDENCY": return {
|
|
205
|
+
...base,
|
|
206
|
+
relatedFiles: error.chain
|
|
207
|
+
};
|
|
208
|
+
case "CONFIG_NOT_FOUND":
|
|
209
|
+
case "CONFIG_INVALID": return {
|
|
210
|
+
...base,
|
|
211
|
+
location: { modulePath: error.path }
|
|
212
|
+
};
|
|
213
|
+
case "FINGERPRINT_FAILED": return {
|
|
214
|
+
...base,
|
|
215
|
+
location: { modulePath: error.filePath }
|
|
216
|
+
};
|
|
217
|
+
case "DISCOVERY_IO_ERROR": return {
|
|
218
|
+
...base,
|
|
219
|
+
location: { modulePath: error.path }
|
|
220
|
+
};
|
|
221
|
+
default: return base;
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Format a BuilderError for CLI/stderr output with human-readable formatting.
|
|
226
|
+
* Includes location, hint, and related files when available.
|
|
227
|
+
*/
|
|
228
|
+
const formatBuilderErrorForCLI = (error) => {
|
|
229
|
+
const formatted = formatBuilderErrorStructured(error);
|
|
230
|
+
const lines = [];
|
|
231
|
+
lines.push(`Error [${formatted.code}]: ${formatted.message}`);
|
|
232
|
+
if (formatted.location) {
|
|
233
|
+
lines.push(` at ${formatted.location.modulePath}`);
|
|
234
|
+
if (formatted.location.astPath) {
|
|
235
|
+
lines.push(` in ${formatted.location.astPath}`);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (formatted.hint) {
|
|
239
|
+
lines.push("");
|
|
240
|
+
lines.push(` Hint: ${formatted.hint}`);
|
|
241
|
+
}
|
|
242
|
+
if (formatted.relatedFiles && formatted.relatedFiles.length > 0) {
|
|
243
|
+
lines.push("");
|
|
244
|
+
lines.push(" Related files:");
|
|
245
|
+
for (const file of formatted.relatedFiles) {
|
|
246
|
+
lines.push(` - ${file}`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return lines.join("\n");
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region packages/builder/src/errors.ts
|
|
254
|
+
/**
|
|
255
|
+
* Error constructor helpers for concise error creation.
|
|
256
|
+
*/
|
|
257
|
+
const builderErrors = {
|
|
258
|
+
entryNotFound: (entry, message) => ({
|
|
259
|
+
code: "ENTRY_NOT_FOUND",
|
|
260
|
+
message: message ?? `Entry not found: ${entry}`,
|
|
261
|
+
entry
|
|
262
|
+
}),
|
|
263
|
+
configNotFound: (path, message) => ({
|
|
264
|
+
code: "CONFIG_NOT_FOUND",
|
|
265
|
+
message: message ?? `Config file not found: ${path}`,
|
|
266
|
+
path
|
|
267
|
+
}),
|
|
268
|
+
configInvalid: (path, message, cause) => ({
|
|
269
|
+
code: "CONFIG_INVALID",
|
|
270
|
+
message,
|
|
271
|
+
path,
|
|
272
|
+
cause
|
|
273
|
+
}),
|
|
274
|
+
discoveryIOError: (path, message, errno, cause) => ({
|
|
275
|
+
code: "DISCOVERY_IO_ERROR",
|
|
276
|
+
message,
|
|
277
|
+
path,
|
|
278
|
+
errno,
|
|
279
|
+
cause
|
|
280
|
+
}),
|
|
281
|
+
fingerprintFailed: (filePath, message, cause) => ({
|
|
282
|
+
code: "FINGERPRINT_FAILED",
|
|
283
|
+
message,
|
|
284
|
+
filePath,
|
|
285
|
+
cause
|
|
286
|
+
}),
|
|
287
|
+
unsupportedAnalyzer: (analyzer, message) => ({
|
|
288
|
+
code: "UNSUPPORTED_ANALYZER",
|
|
289
|
+
message: message ?? `Unsupported analyzer: ${analyzer}`,
|
|
290
|
+
analyzer
|
|
291
|
+
}),
|
|
292
|
+
canonicalPathInvalid: (path, reason) => ({
|
|
293
|
+
code: "CANONICAL_PATH_INVALID",
|
|
294
|
+
message: `Invalid canonical path: ${path}${reason ? ` (${reason})` : ""}`,
|
|
295
|
+
path,
|
|
296
|
+
reason
|
|
297
|
+
}),
|
|
298
|
+
canonicalScopeMismatch: (expected, actual) => ({
|
|
299
|
+
code: "CANONICAL_SCOPE_MISMATCH",
|
|
300
|
+
message: `Scope mismatch: expected ${expected}, got ${actual}`,
|
|
301
|
+
expected,
|
|
302
|
+
actual
|
|
303
|
+
}),
|
|
304
|
+
graphCircularDependency: (chain) => ({
|
|
305
|
+
code: "GRAPH_CIRCULAR_DEPENDENCY",
|
|
306
|
+
message: `Circular dependency detected: ${chain.join(" → ")}`,
|
|
307
|
+
chain
|
|
308
|
+
}),
|
|
309
|
+
graphMissingImport: (importer, importee) => ({
|
|
310
|
+
code: "GRAPH_MISSING_IMPORT",
|
|
311
|
+
message: `Missing import: "${importer}" imports "${importee}" but it's not in the graph`,
|
|
312
|
+
importer,
|
|
313
|
+
importee
|
|
314
|
+
}),
|
|
315
|
+
docDuplicate: (name, sources) => ({
|
|
316
|
+
code: "DOC_DUPLICATE",
|
|
317
|
+
message: `Duplicate document name: ${name} found in ${sources.length} files`,
|
|
318
|
+
name,
|
|
319
|
+
sources
|
|
320
|
+
}),
|
|
321
|
+
writeFailed: (outPath, message, cause) => ({
|
|
322
|
+
code: "WRITE_FAILED",
|
|
323
|
+
message,
|
|
324
|
+
outPath,
|
|
325
|
+
cause
|
|
326
|
+
}),
|
|
327
|
+
cacheCorrupted: (message, cachePath, cause) => ({
|
|
328
|
+
code: "CACHE_CORRUPTED",
|
|
329
|
+
message,
|
|
330
|
+
cachePath,
|
|
331
|
+
cause
|
|
332
|
+
}),
|
|
333
|
+
runtimeModuleLoadFailed: (filePath, astPath, message, cause) => ({
|
|
334
|
+
code: "RUNTIME_MODULE_LOAD_FAILED",
|
|
335
|
+
message,
|
|
336
|
+
filePath,
|
|
337
|
+
astPath,
|
|
338
|
+
cause
|
|
339
|
+
}),
|
|
340
|
+
artifactRegistrationFailed: (elementId, reason) => ({
|
|
341
|
+
code: "ARTIFACT_REGISTRATION_FAILED",
|
|
342
|
+
message: `Failed to register artifact element ${elementId}: ${reason}`,
|
|
343
|
+
elementId,
|
|
344
|
+
reason
|
|
345
|
+
}),
|
|
346
|
+
elementEvaluationFailed: (modulePath, astPath, message, cause) => ({
|
|
347
|
+
code: "ELEMENT_EVALUATION_FAILED",
|
|
348
|
+
message,
|
|
349
|
+
modulePath,
|
|
350
|
+
astPath,
|
|
351
|
+
cause
|
|
352
|
+
}),
|
|
353
|
+
internalInvariant: (message, context, cause) => ({
|
|
354
|
+
code: "INTERNAL_INVARIANT",
|
|
355
|
+
message: `Internal invariant violated: ${message}`,
|
|
356
|
+
context,
|
|
357
|
+
cause
|
|
358
|
+
})
|
|
359
|
+
};
|
|
360
|
+
/**
|
|
361
|
+
* Convenience helper to create an err Result from BuilderError.
|
|
362
|
+
*/
|
|
363
|
+
const builderErr = (error) => err(error);
|
|
364
|
+
/**
|
|
365
|
+
* Type guard for BuilderError.
|
|
366
|
+
*/
|
|
367
|
+
const isBuilderError = (error) => {
|
|
368
|
+
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && "message" in error && typeof error.message === "string";
|
|
369
|
+
};
|
|
370
|
+
/**
|
|
371
|
+
* Format BuilderError for console output (human-readable).
|
|
372
|
+
*/
|
|
373
|
+
const formatBuilderError = (error) => {
|
|
374
|
+
const lines = [];
|
|
375
|
+
lines.push(`Error [${error.code}]: ${error.message}`);
|
|
376
|
+
switch (error.code) {
|
|
377
|
+
case "ENTRY_NOT_FOUND":
|
|
378
|
+
lines.push(` Entry: ${error.entry}`);
|
|
379
|
+
break;
|
|
380
|
+
case "CONFIG_NOT_FOUND":
|
|
381
|
+
case "CONFIG_INVALID":
|
|
382
|
+
lines.push(` Path: ${error.path}`);
|
|
383
|
+
if (error.code === "CONFIG_INVALID" && error.cause) {
|
|
384
|
+
lines.push(` Cause: ${error.cause}`);
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
case "DISCOVERY_IO_ERROR":
|
|
388
|
+
lines.push(` Path: ${error.path}`);
|
|
389
|
+
if (error.errno !== undefined) {
|
|
390
|
+
lines.push(` Errno: ${error.errno}`);
|
|
391
|
+
}
|
|
392
|
+
break;
|
|
393
|
+
case "FINGERPRINT_FAILED":
|
|
394
|
+
lines.push(` File: ${error.filePath}`);
|
|
395
|
+
break;
|
|
396
|
+
case "CANONICAL_PATH_INVALID":
|
|
397
|
+
lines.push(` Path: ${error.path}`);
|
|
398
|
+
if (error.reason) {
|
|
399
|
+
lines.push(` Reason: ${error.reason}`);
|
|
400
|
+
}
|
|
401
|
+
break;
|
|
402
|
+
case "CANONICAL_SCOPE_MISMATCH":
|
|
403
|
+
lines.push(` Expected: ${error.expected}`);
|
|
404
|
+
lines.push(` Actual: ${error.actual}`);
|
|
405
|
+
break;
|
|
406
|
+
case "GRAPH_CIRCULAR_DEPENDENCY":
|
|
407
|
+
lines.push(` Chain: ${error.chain.join(" → ")}`);
|
|
408
|
+
break;
|
|
409
|
+
case "GRAPH_MISSING_IMPORT":
|
|
410
|
+
lines.push(` Importer: ${error.importer}`);
|
|
411
|
+
lines.push(` Importee: ${error.importee}`);
|
|
412
|
+
break;
|
|
413
|
+
case "DOC_DUPLICATE":
|
|
414
|
+
lines.push(` Name: ${error.name}`);
|
|
415
|
+
lines.push(` Sources:\n ${error.sources.join("\n ")}`);
|
|
416
|
+
break;
|
|
417
|
+
case "WRITE_FAILED":
|
|
418
|
+
lines.push(` Output path: ${error.outPath}`);
|
|
419
|
+
break;
|
|
420
|
+
case "CACHE_CORRUPTED":
|
|
421
|
+
if (error.cachePath) {
|
|
422
|
+
lines.push(` Cache path: ${error.cachePath}`);
|
|
423
|
+
}
|
|
424
|
+
break;
|
|
425
|
+
case "RUNTIME_MODULE_LOAD_FAILED":
|
|
426
|
+
lines.push(` File: ${error.filePath}`);
|
|
427
|
+
lines.push(` AST path: ${error.astPath}`);
|
|
428
|
+
break;
|
|
429
|
+
case "ARTIFACT_REGISTRATION_FAILED":
|
|
430
|
+
lines.push(` Element ID: ${error.elementId}`);
|
|
431
|
+
lines.push(` Reason: ${error.reason}`);
|
|
432
|
+
break;
|
|
433
|
+
case "ELEMENT_EVALUATION_FAILED":
|
|
434
|
+
lines.push(` at ${error.modulePath}`);
|
|
435
|
+
if (error.astPath) {
|
|
436
|
+
lines.push(` in ${error.astPath}`);
|
|
437
|
+
}
|
|
438
|
+
break;
|
|
439
|
+
case "INTERNAL_INVARIANT":
|
|
440
|
+
if (error.context) {
|
|
441
|
+
lines.push(` Context: ${error.context}`);
|
|
442
|
+
}
|
|
443
|
+
break;
|
|
444
|
+
}
|
|
445
|
+
if ("cause" in error && error.cause && !["CONFIG_INVALID"].includes(error.code)) {
|
|
446
|
+
lines.push(` Caused by: ${error.cause}`);
|
|
447
|
+
}
|
|
448
|
+
return lines.join("\n");
|
|
449
|
+
};
|
|
450
|
+
/**
|
|
451
|
+
* Assert unreachable code path (for exhaustiveness checks).
|
|
452
|
+
* This is the ONLY acceptable throw in builder code.
|
|
453
|
+
*/
|
|
454
|
+
const assertUnreachable = (value, context) => {
|
|
455
|
+
throw new Error(`Unreachable code path${context ? ` in ${context}` : ""}: received ${JSON.stringify(value)}`);
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
//#endregion
|
|
18
459
|
//#region packages/builder/src/scheduler/effects.ts
|
|
19
460
|
/**
|
|
20
461
|
* File read effect - reads a file from the filesystem.
|
|
@@ -139,6 +580,8 @@ var OptionalFileStatEffect = class extends Effect {
|
|
|
139
580
|
* Supports both sync and async schedulers, enabling parallel element evaluation
|
|
140
581
|
* when using async scheduler.
|
|
141
582
|
*
|
|
583
|
+
* Wraps errors with module context for better debugging.
|
|
584
|
+
*
|
|
142
585
|
* @example
|
|
143
586
|
* yield* new ElementEvaluationEffect(element).run();
|
|
144
587
|
*/
|
|
@@ -147,19 +590,39 @@ var ElementEvaluationEffect = class extends Effect {
|
|
|
147
590
|
super();
|
|
148
591
|
this.element = element;
|
|
149
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* Wrap an error with element context for better debugging.
|
|
595
|
+
*/
|
|
596
|
+
wrapError(error) {
|
|
597
|
+
const context = GqlElement.getContext(this.element);
|
|
598
|
+
if (context) {
|
|
599
|
+
const { filePath, astPath } = parseCanonicalId(context.canonicalId);
|
|
600
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
601
|
+
throw builderErrors.elementEvaluationFailed(filePath, astPath, message, error);
|
|
602
|
+
}
|
|
603
|
+
throw error;
|
|
604
|
+
}
|
|
150
605
|
_executeSync() {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
606
|
+
try {
|
|
607
|
+
const generator = GqlElement.createEvaluationGenerator(this.element);
|
|
608
|
+
const result = generator.next();
|
|
609
|
+
while (!result.done) {
|
|
610
|
+
throw new Error("Async operation required during sync element evaluation");
|
|
611
|
+
}
|
|
612
|
+
} catch (error) {
|
|
613
|
+
this.wrapError(error);
|
|
155
614
|
}
|
|
156
615
|
}
|
|
157
616
|
async _executeAsync() {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
617
|
+
try {
|
|
618
|
+
const generator = GqlElement.createEvaluationGenerator(this.element);
|
|
619
|
+
let result = generator.next();
|
|
620
|
+
while (!result.done) {
|
|
621
|
+
await result.value;
|
|
622
|
+
result = generator.next();
|
|
623
|
+
}
|
|
624
|
+
} catch (error) {
|
|
625
|
+
this.wrapError(error);
|
|
163
626
|
}
|
|
164
627
|
}
|
|
165
628
|
};
|
|
@@ -883,47 +1346,6 @@ const createGraphqlSystemIdentifyHelper = (config) => {
|
|
|
883
1346
|
};
|
|
884
1347
|
};
|
|
885
1348
|
|
|
886
|
-
//#endregion
|
|
887
|
-
//#region packages/builder/src/schemas/artifact.ts
|
|
888
|
-
const BuilderArtifactElementMetadataSchema = z.object({
|
|
889
|
-
sourcePath: z.string(),
|
|
890
|
-
contentHash: z.string()
|
|
891
|
-
});
|
|
892
|
-
const BuilderArtifactOperationSchema = z.object({
|
|
893
|
-
id: z.string(),
|
|
894
|
-
type: z.literal("operation"),
|
|
895
|
-
metadata: BuilderArtifactElementMetadataSchema,
|
|
896
|
-
prebuild: z.object({
|
|
897
|
-
operationType: z.enum([
|
|
898
|
-
"query",
|
|
899
|
-
"mutation",
|
|
900
|
-
"subscription"
|
|
901
|
-
]),
|
|
902
|
-
operationName: z.string(),
|
|
903
|
-
document: z.unknown(),
|
|
904
|
-
variableNames: z.array(z.string())
|
|
905
|
-
})
|
|
906
|
-
});
|
|
907
|
-
const BuilderArtifactFragmentSchema = z.object({
|
|
908
|
-
id: z.string(),
|
|
909
|
-
type: z.literal("fragment"),
|
|
910
|
-
metadata: BuilderArtifactElementMetadataSchema,
|
|
911
|
-
prebuild: z.object({ typename: z.string() })
|
|
912
|
-
});
|
|
913
|
-
const BuilderArtifactElementSchema = z.discriminatedUnion("type", [BuilderArtifactOperationSchema, BuilderArtifactFragmentSchema]);
|
|
914
|
-
const BuilderArtifactSchema = z.object({
|
|
915
|
-
elements: z.record(z.string(), BuilderArtifactElementSchema),
|
|
916
|
-
report: z.object({
|
|
917
|
-
durationMs: z.number(),
|
|
918
|
-
warnings: z.array(z.string()),
|
|
919
|
-
stats: z.object({
|
|
920
|
-
hits: z.number(),
|
|
921
|
-
misses: z.number(),
|
|
922
|
-
skips: z.number()
|
|
923
|
-
})
|
|
924
|
-
})
|
|
925
|
-
});
|
|
926
|
-
|
|
927
1349
|
//#endregion
|
|
928
1350
|
//#region packages/builder/src/artifact/aggregate.ts
|
|
929
1351
|
const canonicalToFilePath$1 = (canonicalId) => canonicalId.split("::")[0] ?? canonicalId;
|
|
@@ -1041,199 +1463,6 @@ const buildArtifact = ({ elements, analyses, stats: cache }) => {
|
|
|
1041
1463
|
});
|
|
1042
1464
|
};
|
|
1043
1465
|
|
|
1044
|
-
//#endregion
|
|
1045
|
-
//#region packages/builder/src/errors.ts
|
|
1046
|
-
/**
|
|
1047
|
-
* Error constructor helpers for concise error creation.
|
|
1048
|
-
*/
|
|
1049
|
-
const builderErrors = {
|
|
1050
|
-
entryNotFound: (entry, message) => ({
|
|
1051
|
-
code: "ENTRY_NOT_FOUND",
|
|
1052
|
-
message: message ?? `Entry not found: ${entry}`,
|
|
1053
|
-
entry
|
|
1054
|
-
}),
|
|
1055
|
-
configNotFound: (path, message) => ({
|
|
1056
|
-
code: "CONFIG_NOT_FOUND",
|
|
1057
|
-
message: message ?? `Config file not found: ${path}`,
|
|
1058
|
-
path
|
|
1059
|
-
}),
|
|
1060
|
-
configInvalid: (path, message, cause) => ({
|
|
1061
|
-
code: "CONFIG_INVALID",
|
|
1062
|
-
message,
|
|
1063
|
-
path,
|
|
1064
|
-
cause
|
|
1065
|
-
}),
|
|
1066
|
-
discoveryIOError: (path, message, errno, cause) => ({
|
|
1067
|
-
code: "DISCOVERY_IO_ERROR",
|
|
1068
|
-
message,
|
|
1069
|
-
path,
|
|
1070
|
-
errno,
|
|
1071
|
-
cause
|
|
1072
|
-
}),
|
|
1073
|
-
fingerprintFailed: (filePath, message, cause) => ({
|
|
1074
|
-
code: "FINGERPRINT_FAILED",
|
|
1075
|
-
message,
|
|
1076
|
-
filePath,
|
|
1077
|
-
cause
|
|
1078
|
-
}),
|
|
1079
|
-
unsupportedAnalyzer: (analyzer, message) => ({
|
|
1080
|
-
code: "UNSUPPORTED_ANALYZER",
|
|
1081
|
-
message: message ?? `Unsupported analyzer: ${analyzer}`,
|
|
1082
|
-
analyzer
|
|
1083
|
-
}),
|
|
1084
|
-
canonicalPathInvalid: (path, reason) => ({
|
|
1085
|
-
code: "CANONICAL_PATH_INVALID",
|
|
1086
|
-
message: `Invalid canonical path: ${path}${reason ? ` (${reason})` : ""}`,
|
|
1087
|
-
path,
|
|
1088
|
-
reason
|
|
1089
|
-
}),
|
|
1090
|
-
canonicalScopeMismatch: (expected, actual) => ({
|
|
1091
|
-
code: "CANONICAL_SCOPE_MISMATCH",
|
|
1092
|
-
message: `Scope mismatch: expected ${expected}, got ${actual}`,
|
|
1093
|
-
expected,
|
|
1094
|
-
actual
|
|
1095
|
-
}),
|
|
1096
|
-
graphCircularDependency: (chain) => ({
|
|
1097
|
-
code: "GRAPH_CIRCULAR_DEPENDENCY",
|
|
1098
|
-
message: `Circular dependency detected: ${chain.join(" → ")}`,
|
|
1099
|
-
chain
|
|
1100
|
-
}),
|
|
1101
|
-
graphMissingImport: (importer, importee) => ({
|
|
1102
|
-
code: "GRAPH_MISSING_IMPORT",
|
|
1103
|
-
message: `Missing import: "${importer}" imports "${importee}" but it's not in the graph`,
|
|
1104
|
-
importer,
|
|
1105
|
-
importee
|
|
1106
|
-
}),
|
|
1107
|
-
docDuplicate: (name, sources) => ({
|
|
1108
|
-
code: "DOC_DUPLICATE",
|
|
1109
|
-
message: `Duplicate document name: ${name} found in ${sources.length} files`,
|
|
1110
|
-
name,
|
|
1111
|
-
sources
|
|
1112
|
-
}),
|
|
1113
|
-
writeFailed: (outPath, message, cause) => ({
|
|
1114
|
-
code: "WRITE_FAILED",
|
|
1115
|
-
message,
|
|
1116
|
-
outPath,
|
|
1117
|
-
cause
|
|
1118
|
-
}),
|
|
1119
|
-
cacheCorrupted: (message, cachePath, cause) => ({
|
|
1120
|
-
code: "CACHE_CORRUPTED",
|
|
1121
|
-
message,
|
|
1122
|
-
cachePath,
|
|
1123
|
-
cause
|
|
1124
|
-
}),
|
|
1125
|
-
runtimeModuleLoadFailed: (filePath, astPath, message, cause) => ({
|
|
1126
|
-
code: "RUNTIME_MODULE_LOAD_FAILED",
|
|
1127
|
-
message,
|
|
1128
|
-
filePath,
|
|
1129
|
-
astPath,
|
|
1130
|
-
cause
|
|
1131
|
-
}),
|
|
1132
|
-
artifactRegistrationFailed: (elementId, reason) => ({
|
|
1133
|
-
code: "ARTIFACT_REGISTRATION_FAILED",
|
|
1134
|
-
message: `Failed to register artifact element ${elementId}: ${reason}`,
|
|
1135
|
-
elementId,
|
|
1136
|
-
reason
|
|
1137
|
-
}),
|
|
1138
|
-
internalInvariant: (message, context, cause) => ({
|
|
1139
|
-
code: "INTERNAL_INVARIANT",
|
|
1140
|
-
message: `Internal invariant violated: ${message}`,
|
|
1141
|
-
context,
|
|
1142
|
-
cause
|
|
1143
|
-
})
|
|
1144
|
-
};
|
|
1145
|
-
/**
|
|
1146
|
-
* Convenience helper to create an err Result from BuilderError.
|
|
1147
|
-
*/
|
|
1148
|
-
const builderErr = (error) => err(error);
|
|
1149
|
-
/**
|
|
1150
|
-
* Type guard for BuilderError.
|
|
1151
|
-
*/
|
|
1152
|
-
const isBuilderError = (error) => {
|
|
1153
|
-
return typeof error === "object" && error !== null && "code" in error && typeof error.code === "string" && "message" in error && typeof error.message === "string";
|
|
1154
|
-
};
|
|
1155
|
-
/**
|
|
1156
|
-
* Format BuilderError for console output (human-readable).
|
|
1157
|
-
*/
|
|
1158
|
-
const formatBuilderError = (error) => {
|
|
1159
|
-
const lines = [];
|
|
1160
|
-
lines.push(`Error [${error.code}]: ${error.message}`);
|
|
1161
|
-
switch (error.code) {
|
|
1162
|
-
case "ENTRY_NOT_FOUND":
|
|
1163
|
-
lines.push(` Entry: ${error.entry}`);
|
|
1164
|
-
break;
|
|
1165
|
-
case "CONFIG_NOT_FOUND":
|
|
1166
|
-
case "CONFIG_INVALID":
|
|
1167
|
-
lines.push(` Path: ${error.path}`);
|
|
1168
|
-
if (error.code === "CONFIG_INVALID" && error.cause) {
|
|
1169
|
-
lines.push(` Cause: ${error.cause}`);
|
|
1170
|
-
}
|
|
1171
|
-
break;
|
|
1172
|
-
case "DISCOVERY_IO_ERROR":
|
|
1173
|
-
lines.push(` Path: ${error.path}`);
|
|
1174
|
-
if (error.errno !== undefined) {
|
|
1175
|
-
lines.push(` Errno: ${error.errno}`);
|
|
1176
|
-
}
|
|
1177
|
-
break;
|
|
1178
|
-
case "FINGERPRINT_FAILED":
|
|
1179
|
-
lines.push(` File: ${error.filePath}`);
|
|
1180
|
-
break;
|
|
1181
|
-
case "CANONICAL_PATH_INVALID":
|
|
1182
|
-
lines.push(` Path: ${error.path}`);
|
|
1183
|
-
if (error.reason) {
|
|
1184
|
-
lines.push(` Reason: ${error.reason}`);
|
|
1185
|
-
}
|
|
1186
|
-
break;
|
|
1187
|
-
case "CANONICAL_SCOPE_MISMATCH":
|
|
1188
|
-
lines.push(` Expected: ${error.expected}`);
|
|
1189
|
-
lines.push(` Actual: ${error.actual}`);
|
|
1190
|
-
break;
|
|
1191
|
-
case "GRAPH_CIRCULAR_DEPENDENCY":
|
|
1192
|
-
lines.push(` Chain: ${error.chain.join(" → ")}`);
|
|
1193
|
-
break;
|
|
1194
|
-
case "GRAPH_MISSING_IMPORT":
|
|
1195
|
-
lines.push(` Importer: ${error.importer}`);
|
|
1196
|
-
lines.push(` Importee: ${error.importee}`);
|
|
1197
|
-
break;
|
|
1198
|
-
case "DOC_DUPLICATE":
|
|
1199
|
-
lines.push(` Name: ${error.name}`);
|
|
1200
|
-
lines.push(` Sources:\n ${error.sources.join("\n ")}`);
|
|
1201
|
-
break;
|
|
1202
|
-
case "WRITE_FAILED":
|
|
1203
|
-
lines.push(` Output path: ${error.outPath}`);
|
|
1204
|
-
break;
|
|
1205
|
-
case "CACHE_CORRUPTED":
|
|
1206
|
-
if (error.cachePath) {
|
|
1207
|
-
lines.push(` Cache path: ${error.cachePath}`);
|
|
1208
|
-
}
|
|
1209
|
-
break;
|
|
1210
|
-
case "RUNTIME_MODULE_LOAD_FAILED":
|
|
1211
|
-
lines.push(` File: ${error.filePath}`);
|
|
1212
|
-
lines.push(` AST path: ${error.astPath}`);
|
|
1213
|
-
break;
|
|
1214
|
-
case "ARTIFACT_REGISTRATION_FAILED":
|
|
1215
|
-
lines.push(` Element ID: ${error.elementId}`);
|
|
1216
|
-
lines.push(` Reason: ${error.reason}`);
|
|
1217
|
-
break;
|
|
1218
|
-
case "INTERNAL_INVARIANT":
|
|
1219
|
-
if (error.context) {
|
|
1220
|
-
lines.push(` Context: ${error.context}`);
|
|
1221
|
-
}
|
|
1222
|
-
break;
|
|
1223
|
-
}
|
|
1224
|
-
if ("cause" in error && error.cause && !["CONFIG_INVALID"].includes(error.code)) {
|
|
1225
|
-
lines.push(` Caused by: ${error.cause}`);
|
|
1226
|
-
}
|
|
1227
|
-
return lines.join("\n");
|
|
1228
|
-
};
|
|
1229
|
-
/**
|
|
1230
|
-
* Assert unreachable code path (for exhaustiveness checks).
|
|
1231
|
-
* This is the ONLY acceptable throw in builder code.
|
|
1232
|
-
*/
|
|
1233
|
-
const assertUnreachable = (value, context) => {
|
|
1234
|
-
throw new Error(`Unreachable code path${context ? ` in ${context}` : ""}: received ${JSON.stringify(value)}`);
|
|
1235
|
-
};
|
|
1236
|
-
|
|
1237
1466
|
//#endregion
|
|
1238
1467
|
//#region packages/builder/src/ast/common/scope.ts
|
|
1239
1468
|
/**
|
|
@@ -3002,7 +3231,7 @@ const createBuilderSession = (options) => {
|
|
|
3002
3231
|
prefix: ["builder"],
|
|
3003
3232
|
persistence: {
|
|
3004
3233
|
enabled: true,
|
|
3005
|
-
filePath: join(process.cwd(), ".cache", "soda-gql", "builder", "cache.json")
|
|
3234
|
+
filePath: join(process.cwd(), "node_modules", ".cache", "soda-gql", "builder", "cache.json")
|
|
3006
3235
|
}
|
|
3007
3236
|
});
|
|
3008
3237
|
process.on("beforeExit", () => {
|
|
@@ -3287,5 +3516,5 @@ const createBuilderService = ({ config, entrypointsOverride }) => {
|
|
|
3287
3516
|
};
|
|
3288
3517
|
|
|
3289
3518
|
//#endregion
|
|
3290
|
-
export { BuilderArtifactSchema, BuilderEffects, FileReadEffect, FileStatEffect, __clearGqlCache, collectAffectedFiles, createBuilderService, createBuilderSession, createGraphqlSystemIdentifyHelper, extractModuleAdjacency };
|
|
3519
|
+
export { BuilderArtifactSchema, BuilderEffects, FileReadEffect, FileStatEffect, __clearGqlCache, collectAffectedFiles, createBuilderService, createBuilderSession, createGraphqlSystemIdentifyHelper, extractModuleAdjacency, formatBuilderErrorForCLI, formatBuilderErrorStructured, loadArtifact, loadArtifactSync };
|
|
3291
3520
|
//# sourceMappingURL=index.mjs.map
|