@soda-gql/typegen 0.10.1 → 0.11.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.mjs CHANGED
@@ -7,6 +7,78 @@ import { err, ok } from "neverthrow";
7
7
  import { existsSync, readFileSync } from "node:fs";
8
8
  import { build } from "esbuild";
9
9
 
10
+ //#region packages/typegen/src/errors.ts
11
+ /**
12
+ * Error constructor helpers for concise error creation.
13
+ */
14
+ const typegenErrors = {
15
+ codegenRequired: (outdir) => ({
16
+ code: "TYPEGEN_CODEGEN_REQUIRED",
17
+ message: `Generated graphql-system module not found at '${outdir}'. Run 'soda-gql codegen' first.`,
18
+ outdir
19
+ }),
20
+ schemaLoadFailed: (schemaNames, cause) => ({
21
+ code: "TYPEGEN_SCHEMA_LOAD_FAILED",
22
+ message: `Failed to load schemas: ${schemaNames.join(", ")}`,
23
+ schemaNames,
24
+ cause
25
+ }),
26
+ buildFailed: (message, cause) => ({
27
+ code: "TYPEGEN_BUILD_FAILED",
28
+ message,
29
+ cause
30
+ }),
31
+ emitFailed: (path, message, cause) => ({
32
+ code: "TYPEGEN_EMIT_FAILED",
33
+ message,
34
+ path,
35
+ cause
36
+ }),
37
+ bundleFailed: (path, message, cause) => ({
38
+ code: "TYPEGEN_BUNDLE_FAILED",
39
+ message,
40
+ path,
41
+ cause
42
+ }),
43
+ fragmentMissingKey: (fragments) => ({
44
+ code: "TYPEGEN_FRAGMENT_MISSING_KEY",
45
+ message: `${fragments.length} fragment(s) missing required 'key' property for prebuilt types`,
46
+ fragments
47
+ })
48
+ };
49
+ /**
50
+ * Format TypegenError for console output (human-readable).
51
+ */
52
+ const formatTypegenError = (error) => {
53
+ const lines = [];
54
+ lines.push(`Error [${error.code}]: ${error.message}`);
55
+ switch (error.code) {
56
+ case "TYPEGEN_CODEGEN_REQUIRED":
57
+ lines.push(` Output directory: ${error.outdir}`);
58
+ lines.push(" Hint: Run 'soda-gql codegen' to generate the graphql-system module first.");
59
+ break;
60
+ case "TYPEGEN_SCHEMA_LOAD_FAILED":
61
+ lines.push(` Schemas: ${error.schemaNames.join(", ")}`);
62
+ break;
63
+ case "TYPEGEN_EMIT_FAILED":
64
+ case "TYPEGEN_BUNDLE_FAILED":
65
+ lines.push(` Path: ${error.path}`);
66
+ break;
67
+ case "TYPEGEN_FRAGMENT_MISSING_KEY":
68
+ lines.push(" Fragments missing 'key' property:");
69
+ for (const fragment of error.fragments) {
70
+ lines.push(` - ${fragment.canonicalId} (${fragment.typename} on ${fragment.schemaLabel})`);
71
+ }
72
+ lines.push(" Hint: Add a 'key' property to each fragment for prebuilt type resolution.");
73
+ break;
74
+ }
75
+ if ("cause" in error && error.cause) {
76
+ lines.push(` Caused by: ${error.cause}`);
77
+ }
78
+ return lines.join("\n");
79
+ };
80
+
81
+ //#endregion
10
82
  //#region packages/typegen/src/emitter.ts
11
83
  /**
12
84
  * Prebuilt types emitter.
@@ -36,11 +108,16 @@ import { build } from "esbuild";
36
108
  * Group field selections by schema.
37
109
  * Uses the schemaLabel from each selection to group them correctly.
38
110
  *
111
+ * In strict mode, all fragments must have a 'key' property. Fragments
112
+ * without keys will cause an error.
113
+ *
39
114
  * @returns Result containing grouped selections and warnings, or error if schema not found
115
+ * or fragments are missing keys
40
116
  */
41
117
  const groupBySchema = (fieldSelections, schemas) => {
42
118
  const grouped = new Map();
43
119
  const warnings = [];
120
+ const missingKeyFragments = [];
44
121
  for (const schemaName of Object.keys(schemas)) {
45
122
  grouped.set(schemaName, {
46
123
  fragments: [],
@@ -62,6 +139,11 @@ const groupBySchema = (fieldSelections, schemas) => {
62
139
  };
63
140
  if (selection.type === "fragment") {
64
141
  if (!selection.key) {
142
+ missingKeyFragments.push({
143
+ canonicalId,
144
+ typename: selection.typename,
145
+ schemaLabel: selection.schemaLabel
146
+ });
65
147
  continue;
66
148
  }
67
149
  try {
@@ -74,6 +156,7 @@ const groupBySchema = (fieldSelections, schemas) => {
74
156
  const inputType = hasVariables ? generateInputTypeFromSpecifiers(schema, selection.variableDefinitions, { formatters: inputFormatters }) : "void";
75
157
  group.fragments.push({
76
158
  key: selection.key,
159
+ typename: selection.typename,
77
160
  inputType,
78
161
  outputType
79
162
  });
@@ -98,23 +181,15 @@ const groupBySchema = (fieldSelections, schemas) => {
98
181
  }
99
182
  }
100
183
  }
184
+ if (missingKeyFragments.length > 0) {
185
+ return err(typegenErrors.fragmentMissingKey(missingKeyFragments));
186
+ }
101
187
  return ok({
102
188
  grouped,
103
189
  warnings
104
190
  });
105
191
  };
106
192
  /**
107
- * Calculate relative import path from one file to another.
108
- */
109
- const toImportSpecifier$1 = (from, to) => {
110
- const fromDir = dirname(from);
111
- let relativePath = relative(fromDir, to);
112
- if (!relativePath.startsWith(".")) {
113
- relativePath = `./${relativePath}`;
114
- }
115
- return relativePath.replace(/\.ts$/, "");
116
- };
117
- /**
118
193
  * Extract input object names from a GraphQL TypeNode.
119
194
  */
120
195
  const extractInputObjectsFromType = (schema, typeNode, inputObjects) => {
@@ -210,8 +285,8 @@ const generateInputObjectTypeDefinitions = (schema, schemaName, inputNames) => {
210
285
  /**
211
286
  * Generate the TypeScript code for prebuilt types.
212
287
  */
213
- const generateTypesCode = (grouped, schemas, injects, outdir) => {
214
- const typesFilePath = join(outdir, "prebuilt", "types.ts");
288
+ const generateTypesCode = (grouped, schemas, injectsModulePath) => {
289
+ const schemaNames = Object.keys(schemas);
215
290
  const lines = [
216
291
  "/**",
217
292
  " * Prebuilt type registry.",
@@ -225,12 +300,10 @@ const generateTypesCode = (grouped, schemas, injects, outdir) => {
225
300
  "",
226
301
  "import type { PrebuiltTypeRegistry } from \"@soda-gql/core\";"
227
302
  ];
228
- for (const [schemaName, inject] of Object.entries(injects)) {
229
- const relativePath = toImportSpecifier$1(typesFilePath, inject.scalars);
230
- lines.push(`import type { scalar as scalar_${schemaName} } from "${relativePath}";`);
231
- }
303
+ const scalarImports = schemaNames.map((name) => `scalar_${name}`).join(", ");
304
+ lines.push(`import type { ${scalarImports} } from "${injectsModulePath}";`);
232
305
  lines.push("");
233
- for (const schemaName of Object.keys(injects)) {
306
+ for (const schemaName of schemaNames) {
234
307
  lines.push(`type ScalarInput_${schemaName}<T extends keyof typeof scalar_${schemaName}> = ` + `typeof scalar_${schemaName}[T]["$type"]["input"];`);
235
308
  lines.push(`type ScalarOutput_${schemaName}<T extends keyof typeof scalar_${schemaName}> = ` + `typeof scalar_${schemaName}[T]["$type"]["output"];`);
236
309
  }
@@ -243,7 +316,7 @@ const generateTypesCode = (grouped, schemas, injects, outdir) => {
243
316
  lines.push(...inputTypeLines);
244
317
  lines.push("");
245
318
  }
246
- const fragmentEntries = fragments.sort((a, b) => a.key.localeCompare(b.key)).map((f) => ` readonly "${f.key}": { readonly input: ${f.inputType}; readonly output: ${f.outputType} };`);
319
+ const fragmentEntries = fragments.sort((a, b) => a.key.localeCompare(b.key)).map((f) => ` readonly "${f.key}": { readonly typename: "${f.typename}"; readonly input: ${f.inputType}; readonly output: ${f.outputType} };`);
247
320
  const operationEntries = operations.sort((a, b) => a.key.localeCompare(b.key)).map((o) => ` readonly "${o.key}": { readonly input: ${o.inputType}; readonly output: ${o.outputType} };`);
248
321
  lines.push(`export type PrebuiltTypes_${schemaName} = {`);
249
322
  lines.push(" readonly fragments: {");
@@ -256,7 +329,7 @@ const generateTypesCode = (grouped, schemas, injects, outdir) => {
256
329
  lines.push(...operationEntries);
257
330
  }
258
331
  lines.push(" };");
259
- lines.push("} satisfies PrebuiltTypeRegistry;");
332
+ lines.push("};");
260
333
  lines.push("");
261
334
  }
262
335
  return lines.join("\n");
@@ -291,14 +364,14 @@ const generateTypesCode = (grouped, schemas, injects, outdir) => {
291
364
  * ```
292
365
  */
293
366
  const emitPrebuiltTypes = async (options) => {
294
- const { schemas, fieldSelections, outdir, injects } = options;
367
+ const { schemas, fieldSelections, outdir, injectsModulePath } = options;
295
368
  const groupResult = groupBySchema(fieldSelections, schemas);
296
369
  if (groupResult.isErr()) {
297
370
  return err(groupResult.error);
298
371
  }
299
372
  const { grouped, warnings } = groupResult.value;
300
- const code = generateTypesCode(grouped, schemas, injects, outdir);
301
- const typesPath = join(outdir, "prebuilt", "types.ts");
373
+ const code = generateTypesCode(grouped, schemas, injectsModulePath);
374
+ const typesPath = join(outdir, "types.prebuilt.ts");
302
375
  try {
303
376
  await writeFile(typesPath, code, "utf-8");
304
377
  return ok({
@@ -310,143 +383,193 @@ const emitPrebuiltTypes = async (options) => {
310
383
  }
311
384
  };
312
385
 
313
- //#endregion
314
- //#region packages/typegen/src/errors.ts
315
- /**
316
- * Error constructor helpers for concise error creation.
317
- */
318
- const typegenErrors = {
319
- codegenRequired: (outdir) => ({
320
- code: "TYPEGEN_CODEGEN_REQUIRED",
321
- message: `Generated graphql-system module not found at '${outdir}'. Run 'soda-gql codegen' first.`,
322
- outdir
323
- }),
324
- schemaLoadFailed: (schemaNames, cause) => ({
325
- code: "TYPEGEN_SCHEMA_LOAD_FAILED",
326
- message: `Failed to load schemas: ${schemaNames.join(", ")}`,
327
- schemaNames,
328
- cause
329
- }),
330
- buildFailed: (message, cause) => ({
331
- code: "TYPEGEN_BUILD_FAILED",
332
- message,
333
- cause
334
- }),
335
- emitFailed: (path, message, cause) => ({
336
- code: "TYPEGEN_EMIT_FAILED",
337
- message,
338
- path,
339
- cause
340
- }),
341
- bundleFailed: (path, message, cause) => ({
342
- code: "TYPEGEN_BUNDLE_FAILED",
343
- message,
344
- path,
345
- cause
346
- })
347
- };
348
- /**
349
- * Format TypegenError for console output (human-readable).
350
- */
351
- const formatTypegenError = (error) => {
352
- const lines = [];
353
- lines.push(`Error [${error.code}]: ${error.message}`);
354
- switch (error.code) {
355
- case "TYPEGEN_CODEGEN_REQUIRED":
356
- lines.push(` Output directory: ${error.outdir}`);
357
- lines.push(" Hint: Run 'soda-gql codegen' to generate the graphql-system module first.");
358
- break;
359
- case "TYPEGEN_SCHEMA_LOAD_FAILED":
360
- lines.push(` Schemas: ${error.schemaNames.join(", ")}`);
361
- break;
362
- case "TYPEGEN_EMIT_FAILED":
363
- case "TYPEGEN_BUNDLE_FAILED":
364
- lines.push(` Path: ${error.path}`);
365
- break;
366
- }
367
- if ("cause" in error && error.cause) {
368
- lines.push(` Caused by: ${error.cause}`);
369
- }
370
- return lines.join("\n");
371
- };
372
-
373
386
  //#endregion
374
387
  //#region packages/typegen/src/prebuilt-generator.ts
375
388
  /**
376
- * Generate the prebuilt module code.
389
+ * Generate the prebuilt index module code.
377
390
  *
378
- * This generates:
379
- * - prebuilt/index.ts: Uses createPrebuiltGqlElementComposer with types from PrebuiltTypes
380
- * - prebuilt/types.ts: Placeholder types that typegen will populate
391
+ * Generates index.prebuilt.ts with builder-level type resolution.
392
+ * Types are resolved at the fragment/operation builder level using TKey/TName,
393
+ * eliminating the need for ResolvePrebuiltElement at the composer level.
381
394
  */
382
395
  const generatePrebuiltModule = (schemas, options) => {
383
396
  const schemaNames = Array.from(schemas.keys());
384
- const typeImports = [];
385
- const runtimeImports = [];
386
- for (const name of schemaNames) {
387
- typeImports.push(`Schema_${name}`, `FragmentBuilders_${name}`, `Context_${name}`);
388
- runtimeImports.push(`__schema_${name}`, `__inputTypeMethods_${name}`, `__directiveMethods_${name}`);
389
- const hasAdapter = options.injection?.get(name)?.adapterImportPath !== undefined;
390
- if (hasAdapter) {
391
- typeImports.push(`Adapter_${name}`);
392
- runtimeImports.push(`__adapter_${name}`);
393
- }
394
- }
395
- const gqlEntries = [];
397
+ const injection = options.injection ?? new Map();
398
+ const adapterImports = [];
396
399
  for (const name of schemaNames) {
397
- const document = schemas.get(name);
398
- if (!document) continue;
399
- const hasAdapter = options.injection?.get(name)?.adapterImportPath !== undefined;
400
- if (hasAdapter) {
401
- gqlEntries.push(` ${name}: createPrebuiltGqlElementComposer<Schema_${name}, PrebuiltTypes_${name}, FragmentBuilders_${name}, typeof __directiveMethods_${name}, Context_${name}, Adapter_${name}>(__schema_${name}, { adapter: __adapter_${name}, inputTypeMethods: __inputTypeMethods_${name}, directiveMethods: __directiveMethods_${name} })`);
402
- } else {
403
- gqlEntries.push(` ${name}: createPrebuiltGqlElementComposer<Schema_${name}, PrebuiltTypes_${name}, FragmentBuilders_${name}, typeof __directiveMethods_${name}, Context_${name}>(__schema_${name}, { inputTypeMethods: __inputTypeMethods_${name}, directiveMethods: __directiveMethods_${name} })`);
400
+ const config = injection.get(name);
401
+ if (config?.hasAdapter) {
402
+ adapterImports.push(`adapter_${name}`);
404
403
  }
405
404
  }
405
+ const internalImports = schemaNames.flatMap((name) => [
406
+ `__schema_${name}`,
407
+ `__inputTypeMethods_${name}`,
408
+ `__directiveMethods_${name}`
409
+ ]);
410
+ const genericTypes = `
411
+ /**
412
+ * Generic field factory for type-erased field access.
413
+ * Returns a callable for nested field builders. Primitive fields can be spread directly.
414
+ * Runtime behavior differs but spread works for both: ...f.id() and ...f.user()(...)
415
+ */
416
+ type GenericFieldFactory = (
417
+ ...args: unknown[]
418
+ ) => (nest: (tools: GenericFieldsBuilderTools) => AnyFields) => AnyFields;
419
+
420
+ /**
421
+ * Generic tools for fields builder callbacks.
422
+ * Uses type-erased factory to allow any field access while maintaining strict mode compatibility.
423
+ */
424
+ type GenericFieldsBuilderTools = {
425
+ readonly f: Record<string, GenericFieldFactory>;
426
+ readonly $: Record<string, unknown>;
427
+ };
428
+ `;
429
+ const contextTypes = schemaNames.map((name) => `
430
+ /**
431
+ * Resolve fragment types at builder level using TKey.
432
+ * If TKey is a known key in PrebuiltTypes, return resolved types.
433
+ * Otherwise, return PrebuiltEntryNotFound.
434
+ */
435
+ type ResolveFragmentAtBuilder_${name}<
436
+ TKey extends string | undefined
437
+ > = TKey extends keyof PrebuiltTypes_${name}["fragments"]
438
+ ? Fragment<
439
+ PrebuiltTypes_${name}["fragments"][TKey]["typename"],
440
+ PrebuiltTypes_${name}["fragments"][TKey]["input"] extends infer TInput
441
+ ? TInput extends void ? void : Partial<TInput & object>
442
+ : void,
443
+ Partial<AnyFields>,
444
+ PrebuiltTypes_${name}["fragments"][TKey]["output"] & object
445
+ >
446
+ : TKey extends undefined
447
+ ? Fragment<"(unknown)", PrebuiltEntryNotFound<"(undefined)", "fragment">, Partial<AnyFields>, PrebuiltEntryNotFound<"(undefined)", "fragment">>
448
+ : Fragment<"(unknown)", PrebuiltEntryNotFound<TKey & string, "fragment">, Partial<AnyFields>, PrebuiltEntryNotFound<TKey & string, "fragment">>;
449
+
450
+ /**
451
+ * Resolve operation types at builder level using TName.
452
+ */
453
+ type ResolveOperationAtBuilder_${name}<
454
+ TOperationType extends OperationType,
455
+ TName extends string
456
+ > = TName extends keyof PrebuiltTypes_${name}["operations"]
457
+ ? Operation<
458
+ TOperationType,
459
+ TName,
460
+ string[],
461
+ PrebuiltTypes_${name}["operations"][TName]["input"] & AnyConstAssignableInput,
462
+ Partial<AnyFields>,
463
+ PrebuiltTypes_${name}["operations"][TName]["output"] & object
464
+ >
465
+ : Operation<
466
+ TOperationType,
467
+ TName,
468
+ string[],
469
+ PrebuiltEntryNotFound<TName, "operation">,
470
+ Partial<AnyFields>,
471
+ PrebuiltEntryNotFound<TName, "operation">
472
+ >;
473
+
474
+ /**
475
+ * Fragment builder that resolves types at builder level using TKey.
476
+ */
477
+ type PrebuiltFragmentBuilder_${name} = <TKey extends string | undefined = undefined>(
478
+ options: {
479
+ key?: TKey;
480
+ fields: (tools: GenericFieldsBuilderTools) => AnyFields;
481
+ variables?: Record<string, unknown>;
482
+ metadata?: unknown;
483
+ }
484
+ ) => ResolveFragmentAtBuilder_${name}<TKey>;
485
+
486
+ /**
487
+ * Operation builder that resolves types at builder level using TName.
488
+ */
489
+ type PrebuiltOperationBuilder_${name}<TOperationType extends OperationType> = <TName extends string>(
490
+ options: {
491
+ name: TName;
492
+ fields: (tools: GenericFieldsBuilderTools) => AnyFields;
493
+ variables?: Record<string, unknown>;
494
+ metadata?: unknown;
495
+ }
496
+ ) => ResolveOperationAtBuilder_${name}<TOperationType, TName>;
497
+
498
+ /**
499
+ * Prebuilt context with builder-level type resolution for schema "${name}".
500
+ */
501
+ type PrebuiltContext_${name} = {
502
+ readonly fragment: { [K: string]: PrebuiltFragmentBuilder_${name} };
503
+ readonly query: { readonly operation: PrebuiltOperationBuilder_${name}<"query"> };
504
+ readonly mutation: { readonly operation: PrebuiltOperationBuilder_${name}<"mutation"> };
505
+ readonly subscription: { readonly operation: PrebuiltOperationBuilder_${name}<"subscription"> };
506
+ readonly $var: unknown;
507
+ readonly $dir: StandardDirectives;
508
+ readonly $colocate: unknown;
509
+ };`).join("\n");
510
+ const gqlEntries = schemaNames.map((name) => {
511
+ const config = injection.get(name);
512
+ const adapterLine = config?.hasAdapter ? `,\n adapter: adapter_${name}` : "";
513
+ return ` ${name}: createGqlElementComposer(
514
+ __schema_${name} as AnyGraphqlSchema,
515
+ {
516
+ inputTypeMethods: __inputTypeMethods_${name},
517
+ directiveMethods: __directiveMethods_${name}${adapterLine}
518
+ }
519
+ ) as unknown as GqlComposer_${name}`;
520
+ });
521
+ const injectsImportSpecifiers = adapterImports.length > 0 ? adapterImports.join(", ") : "";
522
+ const injectsImportLine = injectsImportSpecifiers ? `import { ${injectsImportSpecifiers} } from "${options.injectsModulePath}";` : "";
406
523
  const indexCode = `\
407
524
  /**
408
- * Prebuilt GQL module for bundler-compatible type resolution.
525
+ * Prebuilt GQL module with builder-level type resolution.
409
526
  *
410
- * This module uses createPrebuiltGqlElementComposer which looks up types
411
- * from PrebuiltTypes instead of using complex type inference.
527
+ * Types are resolved at the fragment/operation builder level using TKey/TName,
528
+ * not at the composer level. This enables proper typing for builder arguments
529
+ * and eliminates the need for ResolvePrebuiltElement.
412
530
  *
413
531
  * @module
414
532
  * @generated by @soda-gql/typegen
415
533
  */
416
534
 
417
- import { createPrebuiltGqlElementComposer } from "@soda-gql/core";
418
535
  import {
419
- ${runtimeImports.join(",\n ")},
420
- type ${typeImports.join(",\n type ")},
421
- } from "${options.mainModulePath}";
422
- import type { ${schemaNames.map((name) => `PrebuiltTypes_${name}`).join(", ")} } from "./types";
536
+ createGqlElementComposer,
537
+ type AnyConstAssignableInput,
538
+ type AnyFields,
539
+ type AnyGraphqlSchema,
540
+ type Fragment,
541
+ type Operation,
542
+ type OperationType,
543
+ type PrebuiltEntryNotFound,
544
+ type StandardDirectives,
545
+ } from "@soda-gql/core";
546
+ ${injectsImportLine}
547
+ import { ${internalImports.join(", ")} } from "${options.internalModulePath}";
548
+ import type { ${schemaNames.map((name) => `PrebuiltTypes_${name}`).join(", ")} } from "./types.prebuilt";
549
+ ${genericTypes}
550
+ ${contextTypes}
423
551
 
424
- export const gql = {
425
- ${gqlEntries.join(",\n")}
426
- };
552
+ // Export context types for explicit annotation
553
+ ${schemaNames.map((name) => `export type { PrebuiltContext_${name} };`).join("\n")}
554
+
555
+ // Composer type - TResult already has resolved types from builders, no ResolvePrebuiltElement needed
556
+ ${schemaNames.map((name) => `type GqlComposer_${name} = {
557
+ <TResult>(composeElement: (context: PrebuiltContext_${name}) => TResult): TResult;
558
+ readonly $schema: AnyGraphqlSchema;
559
+ };`).join("\n")}
427
560
 
428
- // Re-export types from main module
429
- export type { ${typeImports.join(", ")} };
430
- `;
431
- const typesCode = `\
432
561
  /**
433
- * Prebuilt type registry.
434
- *
435
- * This file contains placeholder types that will be populated by typegen
436
- * when running \`soda-gql typegen\` command.
562
+ * Prebuilt GQL composers with builder-level type resolution.
437
563
  *
438
- * @module
439
- * @generated by @soda-gql/typegen
564
+ * These composers have the same runtime behavior as the base composers,
565
+ * but their return types are resolved from the prebuilt type registry
566
+ * at the builder level instead of using ResolvePrebuiltElement.
440
567
  */
441
-
442
- import type { EmptyPrebuiltTypeRegistry } from "@soda-gql/core";
443
-
444
- ${schemaNames.map((name) => `// Placeholder for ${name} schema - populated by typegen\nexport type PrebuiltTypes_${name} = EmptyPrebuiltTypeRegistry;`).join("\n\n")}
568
+ export const gql: { ${schemaNames.map((name) => `${name}: GqlComposer_${name}`).join("; ")} } = {
569
+ ${gqlEntries.join(",\n")}
570
+ };
445
571
  `;
446
- return {
447
- indexCode,
448
- typesCode
449
- };
572
+ return { indexCode };
450
573
  };
451
574
 
452
575
  //#endregion
@@ -456,10 +579,10 @@ ${schemaNames.map((name) => `// Placeholder for ${name} schema - populated by ty
456
579
  *
457
580
  * Orchestrates the prebuilt type generation process:
458
581
  * 1. Load schemas from generated CJS bundle
459
- * 2. Generate prebuilt/index.ts
582
+ * 2. Generate index.prebuilt.ts
460
583
  * 3. Build artifact to evaluate elements
461
584
  * 4. Extract field selections
462
- * 5. Emit prebuilt/types.ts
585
+ * 5. Emit types.prebuilt.ts
463
586
  * 6. Bundle prebuilt module
464
587
  *
465
588
  * @module
@@ -549,10 +672,10 @@ const loadSchemaDocuments = (schemasConfig) => {
549
672
  *
550
673
  * This function:
551
674
  * 1. Loads schemas from the generated CJS bundle
552
- * 2. Generates prebuilt/index.ts using generatePrebuiltModule
675
+ * 2. Generates index.prebuilt.ts using generatePrebuiltModule
553
676
  * 3. Creates a BuilderService and builds the artifact
554
677
  * 4. Extracts field selections from the artifact
555
- * 5. Emits prebuilt/types.ts using emitPrebuiltTypes
678
+ * 5. Emits types.prebuilt.ts using emitPrebuiltTypes
556
679
  * 6. Bundles the prebuilt module
557
680
  *
558
681
  * @param options - Typegen options including config
@@ -572,23 +695,19 @@ const runTypegen = async (options) => {
572
695
  return err(typegenErrors.schemaLoadFailed(schemaNames, schemasResult.error));
573
696
  }
574
697
  const schemas = schemasResult.value;
575
- const prebuiltDir = join(outdir, "prebuilt");
576
- await mkdir(prebuiltDir, { recursive: true });
577
698
  const schemaDocuments = loadSchemaDocuments(config.schemas);
578
- const mainModulePath = toImportSpecifier(join(prebuiltDir, "index.ts"), join(outdir, "index.ts"), importSpecifierOptions);
699
+ const prebuiltIndexPath = join(outdir, "index.prebuilt.ts");
700
+ const internalModulePath = toImportSpecifier(prebuiltIndexPath, join(outdir, "_internal.ts"), importSpecifierOptions);
701
+ const injectsModulePath = toImportSpecifier(prebuiltIndexPath, join(outdir, "_internal-injects.ts"), importSpecifierOptions);
579
702
  const injection = new Map();
580
703
  for (const [schemaName, schemaConfig] of Object.entries(config.schemas)) {
581
- if (schemaConfig.inject.adapter) {
582
- injection.set(schemaName, { adapterImportPath: toImportSpecifier(join(outdir, "index.ts"), schemaConfig.inject.adapter, importSpecifierOptions) });
583
- } else {
584
- injection.set(schemaName, {});
585
- }
704
+ injection.set(schemaName, { hasAdapter: !!schemaConfig.inject.adapter });
586
705
  }
587
706
  const prebuilt = generatePrebuiltModule(schemaDocuments, {
588
- mainModulePath,
707
+ internalModulePath,
708
+ injectsModulePath,
589
709
  injection
590
710
  });
591
- const prebuiltIndexPath = join(prebuiltDir, "index.ts");
592
711
  try {
593
712
  await writeModule(prebuiltIndexPath, prebuilt.indexCode);
594
713
  } catch (error) {
@@ -605,15 +724,11 @@ const runTypegen = async (options) => {
605
724
  }
606
725
  const fieldSelectionsResult = extractFieldSelections(intermediateElements);
607
726
  const { selections: fieldSelections, warnings: extractWarnings } = fieldSelectionsResult;
608
- const injects = {};
609
- for (const [schemaName, schemaConfig] of Object.entries(config.schemas)) {
610
- injects[schemaName] = { scalars: schemaConfig.inject.scalars };
611
- }
612
727
  const emitResult = await emitPrebuiltTypes({
613
728
  schemas,
614
729
  fieldSelections,
615
730
  outdir,
616
- injects
731
+ injectsModulePath
617
732
  });
618
733
  if (emitResult.isErr()) {
619
734
  return err(emitResult.error);