@drzl/cli 4.23.0 → 4.24.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.
@@ -1,646 +0,0 @@
1
- // ../generator-graphql/dist/index.js
2
- import { fileWriter } from "@drzl/validation-core";
3
- import {
4
- formatCode,
5
- importSpecifier,
6
- insertColumns,
7
- isIntegerColumn,
8
- selectColumns,
9
- updateColumns
10
- } from "@drzl/validation-core";
11
- var APP_MODULE = "index";
12
- var SCALARS_MODULE = "scalars";
13
- var GRAPHQL_NAME = /^[_A-Za-z][_0-9A-Za-z]*$/;
14
- var RESERVED_ENUM_VALUES = /* @__PURE__ */ new Set(["true", "false", "null"]);
15
- var INT32_MIN = -2147483648n;
16
- var INT32_MAX = 2147483647n;
17
- var cap = (s) => s.charAt(0).toUpperCase() + s.slice(1);
18
- var isIdent = (s) => /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(s);
19
- var q = (v) => `'${v.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
20
- function gqlIdent(s) {
21
- const cleaned = s.replace(/[^_0-9A-Za-z]+/g, "_");
22
- return /^[0-9]/.test(cleaned) ? `_${cleaned}` : cleaned;
23
- }
24
- function tpl(s) {
25
- return s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${");
26
- }
27
- function keyColumns(table) {
28
- const names = table.primaryKey?.columns ?? [];
29
- if (!names.length) return null;
30
- const cols = names.map((n) => table.columns.find((c) => c.name === n));
31
- if (cols.some((c) => !c)) return null;
32
- return cols;
33
- }
34
- function fitsInt32(c) {
35
- if (c.min === void 0 || c.max === void 0) return false;
36
- try {
37
- return BigInt(c.min) >= INT32_MIN && BigInt(c.max) <= INT32_MAX;
38
- } catch {
39
- return false;
40
- }
41
- }
42
- function planEnum(typeName, values) {
43
- const members = values.map((value) => {
44
- const verbatimOk = GRAPHQL_NAME.test(value) && !RESERVED_ENUM_VALUES.has(value) && !value.startsWith("__");
45
- if (verbatimOk) return { name: value, value, renamed: false };
46
- let name = gqlIdent(value.toUpperCase());
47
- name = name.replace(/^_+/, "_");
48
- if (!GRAPHQL_NAME.test(name) || RESERVED_ENUM_VALUES.has(name) || name.startsWith("__")) {
49
- return { name: "", value, renamed: true };
50
- }
51
- return { name, value, renamed: true };
52
- });
53
- if (members.some((m) => !m.name)) return null;
54
- const seen = /* @__PURE__ */ new Set();
55
- for (const m of members) {
56
- if (seen.has(m.name)) return null;
57
- seen.add(m.name);
58
- }
59
- return { typeName, members };
60
- }
61
- function mapped(sdl, rowTs, inputTs = rowTs, scalars = []) {
62
- return { sdl, rowTs, inputTs, scalars };
63
- }
64
- function planColumn(table, c) {
65
- const field = GRAPHQL_NAME.test(c.name) ? c.name : gqlIdent(c.name);
66
- const base = (() => {
67
- if (c.enumValues && c.enumValues.length) {
68
- const typeName = `${cap(gqlIdent(table.tsName))}${cap(gqlIdent(c.name))}Enum`;
69
- const plan = planEnum(typeName, c.enumValues);
70
- if (!plan) {
71
- return {
72
- ...mapped("String", "string"),
73
- note: `Column "${c.name}": the enum values ${c.enumValues.join(", ")} cannot all be spelled as distinct GraphQL enum members, so the column is exposed as String carrying the database values verbatim.`
74
- };
75
- }
76
- const union = c.enumValues.map(q).join(" | ");
77
- return { ...mapped(plan.typeName, union), enumPlan: plan };
78
- }
79
- switch (c.shape?.kind) {
80
- case "tuple":
81
- return mapped("[Float!]", "number[]");
82
- case "numberObject":
83
- return mapped("JSON", "unknown", "unknown", ["JSON"]);
84
- case "buffer":
85
- return {
86
- ...mapped("JSON", "unknown", "unknown", ["JSON"]),
87
- note: `Column "${c.name}" is binary; GraphQL has no binary type, so it rides the JSON scalar and your resolver picks an encoding.`
88
- };
89
- case "json":
90
- return mapped("JSON", "unknown", "unknown", ["JSON"]);
91
- case "bitstring":
92
- case "byteString":
93
- return mapped("String", "string");
94
- default:
95
- break;
96
- }
97
- if (c.dbType === "VECTOR") return mapped("[Float!]", "number[]");
98
- switch (c.tsType) {
99
- case "number":
100
- return mapped(isIntegerColumn(c) && fitsInt32(c) ? "Int" : "Float", "number");
101
- case "string":
102
- return mapped(c.format === "uuid" ? "ID" : "String", "string");
103
- case "boolean":
104
- return mapped("Boolean", "boolean");
105
- case "Date":
106
- return mapped("DateTime", "Date", "Date", ["DateTime"]);
107
- case "bigint":
108
- return mapped("BigInt", "string | bigint", "string", ["BigInt"]);
109
- default:
110
- return {
111
- ...mapped("JSON", "unknown", "unknown", ["JSON"]),
112
- note: `No GraphQL type for column "${c.name}": DRZL could not derive one from the schema, so it is exposed through the JSON scalar and accepts any value there.`
113
- };
114
- }
115
- })();
116
- let { sdl, rowTs, inputTs } = base;
117
- const dims = c.arrayDimensions ?? 0;
118
- for (let i = 0; i < dims; i++) {
119
- sdl = `[${sdl}]`;
120
- rowTs = `(${rowTs} | null)[]`;
121
- inputTs = `(${inputTs} | null)[]`;
122
- }
123
- return {
124
- column: c,
125
- field,
126
- renamed: field !== c.name,
127
- sdl,
128
- rowTs,
129
- inputTs,
130
- scalars: base.scalars,
131
- enumPlan: base.enumPlan,
132
- note: base.note
133
- };
134
- }
135
- function planTable(table) {
136
- const typeName = cap(gqlIdent(table.tsName));
137
- const fieldBase = gqlIdent(table.tsName);
138
- const select = selectColumns(table).map((c) => planColumn(table, c));
139
- const writable = !table.readOnly;
140
- const insert = writable ? insertColumns(table).map((c) => planColumn(table, c)) : [];
141
- const update = writable ? updateColumns(table).map((c) => planColumn(table, c)) : [];
142
- const keyCols = keyColumns(table);
143
- const key = keyCols ? keyCols.map((c) => planColumn(table, c)) : null;
144
- const scalars = /* @__PURE__ */ new Set();
145
- const enums = [];
146
- const notes = [];
147
- for (const p of select) {
148
- for (const s of p.scalars) scalars.add(s);
149
- if (p.enumPlan) enums.push(p.enumPlan);
150
- if (p.note) notes.push(p.note);
151
- }
152
- const createInput = `Create${typeName}Input`;
153
- const updateInput = `Update${typeName}Input`;
154
- const hasCreate = writable && insert.length > 0;
155
- const hasUpdate = writable && key !== null && update.length > 0;
156
- const keyArgsSdl = key ? key.map((p) => `${p.field}: ${p.sdl}!`).join(", ") : "";
157
- const keyArgsTs = key ? key.map((p) => `${p.field}: ${p.inputTs}`).join("; ") : "";
158
- const ops = [];
159
- ops.push({
160
- parent: "Query",
161
- name: fieldBase,
162
- argsSdl: "",
163
- resultSdl: `[${typeName}!]!`,
164
- argsTs: "Record<string, never>",
165
- resultTs: `${typeName}[]`
166
- });
167
- if (key) {
168
- ops.push({
169
- parent: "Query",
170
- name: `${fieldBase}ById`,
171
- argsSdl: keyArgsSdl,
172
- resultSdl: typeName,
173
- argsTs: `{ ${keyArgsTs} }`,
174
- resultTs: `${typeName} | null`
175
- });
176
- }
177
- if (hasCreate) {
178
- ops.push({
179
- parent: "Mutation",
180
- name: `create${typeName}`,
181
- argsSdl: `input: ${createInput}!`,
182
- resultSdl: `${typeName}!`,
183
- argsTs: `{ input: ${createInput} }`,
184
- resultTs: typeName
185
- });
186
- }
187
- if (hasUpdate) {
188
- ops.push({
189
- parent: "Mutation",
190
- name: `update${typeName}`,
191
- argsSdl: `${keyArgsSdl}, input: ${updateInput}!`,
192
- resultSdl: `${typeName}!`,
193
- argsTs: `{ ${keyArgsTs}; input: ${updateInput} }`,
194
- resultTs: typeName
195
- });
196
- }
197
- if (writable && key) {
198
- ops.push({
199
- parent: "Mutation",
200
- name: `delete${typeName}`,
201
- argsSdl: keyArgsSdl,
202
- resultSdl: "Boolean!",
203
- argsTs: `{ ${keyArgsTs} }`,
204
- resultTs: "boolean"
205
- });
206
- }
207
- const renamedSelect = select.filter((p) => p.renamed);
208
- for (const p of renamedSelect) {
209
- notes.push(
210
- `Column "${p.column.name}" is not a valid GraphQL field name, so it is exposed as "${p.field}": output is mapped back by the emitted field resolver, and on inputs the value arrives under "${p.field}" for your resolver to write back.`
211
- );
212
- }
213
- return {
214
- table,
215
- typeName,
216
- createInput,
217
- updateInput,
218
- select,
219
- insert,
220
- update,
221
- key,
222
- writable,
223
- hasCreate,
224
- hasUpdate,
225
- ops,
226
- scalars,
227
- enums,
228
- notes,
229
- renamedSelect
230
- };
231
- }
232
- function renderEnumSdl(plan) {
233
- const lines = [`enum ${plan.typeName} {`];
234
- for (const m of plan.members) {
235
- if (m.renamed) lines.push(` ${JSON.stringify(`Database value: ${m.value}`)}`);
236
- lines.push(` ${m.name}`);
237
- }
238
- lines.push("}");
239
- return lines.join("\n");
240
- }
241
- function renderTypeSdl(plan) {
242
- const parts = [];
243
- for (const e of plan.enums) parts.push(renderEnumSdl(e));
244
- parts.push(
245
- [
246
- `type ${plan.typeName} {`,
247
- ...plan.select.map((p) => ` ${p.field}: ${p.sdl}${p.column.nullable ? "" : "!"}`),
248
- "}"
249
- ].join("\n")
250
- );
251
- if (plan.hasCreate) {
252
- parts.push(
253
- [
254
- `input ${plan.createInput} {`,
255
- ...plan.insert.map((p) => {
256
- const required = !p.column.nullable && !p.column.hasDefault;
257
- return ` ${p.field}: ${p.sdl}${required ? "!" : ""}`;
258
- }),
259
- "}"
260
- ].join("\n")
261
- );
262
- }
263
- if (plan.hasUpdate) {
264
- parts.push(
265
- [
266
- `input ${plan.updateInput} {`,
267
- ...plan.update.map((p) => ` ${p.field}: ${p.sdl}`),
268
- "}"
269
- ].join("\n")
270
- );
271
- }
272
- return parts.join("\n\n");
273
- }
274
- function rowField(p) {
275
- const key = isIdent(p.column.name) ? p.column.name : q(p.column.name);
276
- return ` ${key}: ${p.rowTs}${p.column.nullable ? " | null" : ""};`;
277
- }
278
- function inputField(p, mode) {
279
- const required = mode === "insert" && !p.column.nullable && !p.column.hasDefault;
280
- return required ? ` ${p.field}: ${p.inputTs};` : ` ${p.field}?: ${p.inputTs} | null;`;
281
- }
282
- var stubBody = (parent, name) => `throw new Error('Not implemented: ${parent}.${name}. Replace this stub with your data layer.');`;
283
- function renderResolvers(plan) {
284
- const lines = ["{"];
285
- const byParent = (parent) => plan.ops.filter((o) => o.parent === parent);
286
- const renderOps = (parent) => {
287
- lines.push(` ${parent}: {`);
288
- for (const op of byParent(parent)) {
289
- lines.push(
290
- ` ${op.name}: (_parent: unknown, _args: ${op.argsTs}): ${op.resultTs} => {`,
291
- ` ${stubBody(parent, op.name)}`,
292
- ` },`
293
- );
294
- }
295
- lines.push(" },");
296
- };
297
- renderOps("Query");
298
- if (byParent("Mutation").length) renderOps("Mutation");
299
- for (const e of plan.enums) {
300
- const mapped2 = e.members.filter((m) => m.renamed);
301
- if (!mapped2.length) continue;
302
- lines.push(
303
- ` ${e.typeName}: { ${mapped2.map((m) => `${m.name}: ${q(m.value)}`).join(", ")} },`
304
- );
305
- }
306
- if (plan.renamedSelect.length) {
307
- lines.push(` ${plan.typeName}: {`);
308
- for (const p of plan.renamedSelect) {
309
- const type = `${p.rowTs}${p.column.nullable ? " | null" : ""}`;
310
- lines.push(` ${p.field}: (parent: ${plan.typeName}): ${type} => parent[${q(p.column.name)}],`);
311
- }
312
- lines.push(" },");
313
- }
314
- lines.push("}");
315
- return lines.join("\n");
316
- }
317
- function renderTable(plan) {
318
- const t = plan.table;
319
- const declared = [];
320
- declared.push(
321
- `/** One ${t.name} row, as your resolvers return it: database values, database spellings. */`,
322
- `export interface ${plan.typeName} {`,
323
- ...plan.select.map(rowField),
324
- "}"
325
- );
326
- if (plan.hasCreate) {
327
- declared.push(
328
- "",
329
- `/** The create${plan.typeName} input, as GraphQL hands it to your resolver. */`,
330
- `export interface ${plan.createInput} {`,
331
- ...plan.insert.map((p) => inputField(p, "insert")),
332
- "}"
333
- );
334
- }
335
- if (plan.hasUpdate) {
336
- declared.push(
337
- "",
338
- `/** The update${plan.typeName} patch: every field optional, primary key excluded. */`,
339
- `export interface ${plan.updateInput} {`,
340
- ...plan.update.map((p) => inputField(p, "update")),
341
- "}"
342
- );
343
- }
344
- const tsName = gqlIdent(t.tsName);
345
- const notes = plan.notes.length ? plan.notes.map((n) => `// ${n.replace(/\n/g, " ")}`).join("\n") + "\n" : "";
346
- return `// Generated by @drzl/generator-graphql
347
- // GraphQL SDL and resolver stubs for table: ${t.name}
348
- ${notes}${declared.join("\n")}
349
-
350
- /** The SDL for this table's types. The Query and Mutation fields live in the barrel. */
351
- export const ${tsName}TypeDefs = \`${tpl(renderTypeSdl(plan))}\`;
352
-
353
- /** Stubs that throw until replaced, plus the enum value maps and field resolvers the schema needs. */
354
- export const ${tsName}Resolvers = ${renderResolvers(plan)};
355
- `;
356
- }
357
- function renderScalarsModule() {
358
- return `// Generated by @drzl/generator-graphql
359
- // Dependency-free scalar configs for the resolvers map. Each hook is named twice because
360
- // graphql 17 renamed serialize/parseValue/parseLiteral to coerceOutputValue/coerceInputValue/
361
- // coerceInputLiteral, and the schema builder assigns whichever names the running graphql reads.
362
-
363
- /** The literal AST shape the literal hooks read, structurally, so nothing is imported. */
364
- export interface LiteralNode {
365
- kind: string;
366
- value?: unknown;
367
- values?: LiteralNode[];
368
- fields?: { name: { value: string }; value: LiteralNode }[];
369
- name?: { value: string };
370
- }
371
-
372
- // Strict ISO 8601 datetime with seconds and an offset: new Date('1') is the year 2001.
373
- const ISO_DATETIME = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$/;
374
-
375
- const toIso = (value: unknown): string => {
376
- if (value instanceof Date && !Number.isNaN(value.getTime())) return value.toISOString();
377
- throw new Error('DateTime.serialize: expected a Date');
378
- };
379
- const fromIso = (value: unknown): Date => {
380
- if (typeof value !== 'string' || !ISO_DATETIME.test(value)) {
381
- throw new Error('DateTime: expected a strict ISO 8601 datetime string');
382
- }
383
- const parsed = new Date(value);
384
- if (Number.isNaN(parsed.getTime())) throw new Error('DateTime: unreadable datetime string');
385
- return parsed;
386
- };
387
- const fromIsoLiteral = (ast: LiteralNode): Date => {
388
- if (ast.kind !== 'StringValue') throw new Error('DateTime: expected a string literal');
389
- return fromIso(ast.value);
390
- };
391
-
392
- /** ISO 8601 datetime string in, real Date to the resolver, toISOString() out. */
393
- export const DateTimeScalar = {
394
- name: 'DateTime',
395
- description: 'A strict ISO 8601 datetime string, e.g. 2026-01-02T03:04:05.000Z.',
396
- serialize: toIso,
397
- parseValue: fromIso,
398
- parseLiteral: fromIsoLiteral,
399
- coerceOutputValue: toIso,
400
- coerceInputValue: fromIso,
401
- coerceInputLiteral: fromIsoLiteral,
402
- };
403
-
404
- const DIGITS = /^-?\\d+$/;
405
-
406
- const toDigits = (value: unknown): string => {
407
- if (typeof value === 'bigint') return value.toString();
408
- if (typeof value === 'string' && DIGITS.test(value)) return value;
409
- throw new Error('BigInt.serialize: expected a bigint or a decimal digit string');
410
- };
411
- // Variables take the digit string only: a JSON number was already rounded by JSON.parse.
412
- const fromDigits = (value: unknown): string => {
413
- if (typeof value !== 'string' || !DIGITS.test(value)) {
414
- throw new Error('BigInt: expected a string of decimal digits');
415
- }
416
- return value;
417
- };
418
- // An inline integer literal is lossless: the AST carries its raw digits as a string.
419
- const fromDigitsLiteral = (ast: LiteralNode): string => {
420
- if (ast.kind === 'IntValue') return String(ast.value);
421
- if (ast.kind === 'StringValue' && typeof ast.value === 'string' && DIGITS.test(ast.value)) {
422
- return ast.value;
423
- }
424
- throw new Error('BigInt: expected an integer literal or a string of decimal digits');
425
- };
426
-
427
- /** A 64-bit-safe integer crossing the wire as its decimal digits. */
428
- export const BigIntScalar = {
429
- name: 'BigInt',
430
- description: 'An arbitrary-precision integer as a string of decimal digits.',
431
- serialize: toDigits,
432
- parseValue: fromDigits,
433
- parseLiteral: fromDigitsLiteral,
434
- coerceOutputValue: toDigits,
435
- coerceInputValue: fromDigits,
436
- coerceInputLiteral: fromDigitsLiteral,
437
- };
438
-
439
- const identity = (value: unknown): unknown => value;
440
- const fromJSONLiteral = (ast: LiteralNode, variables?: Record<string, unknown> | null): unknown => {
441
- switch (ast.kind) {
442
- case 'StringValue':
443
- case 'BooleanValue':
444
- case 'EnumValue':
445
- return ast.value;
446
- case 'IntValue':
447
- case 'FloatValue':
448
- return Number(ast.value);
449
- case 'NullValue':
450
- return null;
451
- case 'ListValue':
452
- return (ast.values ?? []).map((v) => fromJSONLiteral(v, variables));
453
- case 'ObjectValue': {
454
- const out: Record<string, unknown> = {};
455
- for (const f of ast.fields ?? []) out[f.name.value] = fromJSONLiteral(f.value, variables);
456
- return out;
457
- }
458
- case 'Variable':
459
- return variables ? variables[ast.name?.value ?? ''] : undefined;
460
- default:
461
- throw new Error('JSON: unsupported literal kind ' + ast.kind);
462
- }
463
- };
464
-
465
- /** Any JSON value, untouched on both value paths, rebuilt from the AST for inline literals. */
466
- export const JSONScalar = {
467
- name: 'JSON',
468
- description: 'Any JSON value, passed through as-is.',
469
- serialize: identity,
470
- parseValue: identity,
471
- parseLiteral: fromJSONLiteral,
472
- coerceOutputValue: identity,
473
- coerceInputValue: identity,
474
- coerceInputLiteral: fromJSONLiteral,
475
- };
476
- `;
477
- }
478
- var SCALAR_EXPORTS = {
479
- DateTime: "DateTimeScalar",
480
- BigInt: "BigIntScalar",
481
- JSON: "JSONScalar"
482
- };
483
- function renderBarrel(plans, modules, usedScalars, scalarsSpec) {
484
- if (!plans.length) {
485
- return `// Generated by @drzl/generator-graphql
486
- // No tables detected in analysis. Add tables to your schema and regenerate.
487
- // A GraphQL schema needs a Query type with at least one field, so no typeDefs are composed.
488
- export * from '${scalarsSpec}';
489
-
490
- export const typeDefs = '';
491
-
492
- export const resolvers = {};
493
- `;
494
- }
495
- const imports = [];
496
- if (usedScalars.length) {
497
- const names = usedScalars.map((s) => SCALAR_EXPORTS[s]).sort();
498
- imports.push(`import { ${names.join(", ")} } from '${scalarsSpec}';`);
499
- }
500
- for (const plan of plans) {
501
- const tsName = gqlIdent(plan.table.tsName);
502
- imports.push(
503
- `import { ${tsName}Resolvers, ${tsName}TypeDefs } from '${modules.get(plan)}';`
504
- );
505
- }
506
- const reExports = [
507
- `export * from '${scalarsSpec}';`,
508
- ...plans.map((p) => `export * from '${modules.get(p)}';`)
509
- ];
510
- const queryLines = plans.flatMap(
511
- (p) => p.ops.filter((o) => o.parent === "Query").map((o) => ` ${o.name}${o.argsSdl ? `(${o.argsSdl})` : ""}: ${o.resultSdl}`)
512
- );
513
- const mutationLines = plans.flatMap(
514
- (p) => p.ops.filter((o) => o.parent === "Mutation").map((o) => ` ${o.name}(${o.argsSdl}): ${o.resultSdl}`)
515
- );
516
- const typeDefParts = [
517
- ...usedScalars.map((s) => `'scalar ${s}'`),
518
- ...plans.map((p) => `${gqlIdent(p.table.tsName)}TypeDefs`),
519
- `\`${tpl(["type Query {", ...queryLines, "}"].join("\n"))}\``
520
- ];
521
- if (mutationLines.length) {
522
- typeDefParts.push(`\`${tpl(["type Mutation {", ...mutationLines, "}"].join("\n"))}\``);
523
- }
524
- const resolverLines = ["export const resolvers = {"];
525
- for (const s of usedScalars) resolverLines.push(` ${s}: ${SCALAR_EXPORTS[s]},`);
526
- for (const p of plans) {
527
- const tsName = gqlIdent(p.table.tsName);
528
- for (const e of p.enums) {
529
- if (e.members.some((m) => m.renamed)) {
530
- resolverLines.push(` ${e.typeName}: ${tsName}Resolvers.${e.typeName},`);
531
- }
532
- }
533
- if (p.renamedSelect.length) {
534
- resolverLines.push(` ${p.typeName}: ${tsName}Resolvers.${p.typeName},`);
535
- }
536
- }
537
- resolverLines.push(" Query: {");
538
- for (const p of plans) resolverLines.push(` ...${gqlIdent(p.table.tsName)}Resolvers.Query,`);
539
- resolverLines.push(" },");
540
- const mutating = plans.filter((p) => p.ops.some((o) => o.parent === "Mutation"));
541
- if (mutating.length) {
542
- resolverLines.push(" Mutation: {");
543
- for (const p of mutating) {
544
- resolverLines.push(` ...${gqlIdent(p.table.tsName)}Resolvers.Mutation,`);
545
- }
546
- resolverLines.push(" },");
547
- }
548
- resolverLines.push("};");
549
- return `// Generated by @drzl/generator-graphql
550
- // The whole schema in one pair: hand typeDefs and resolvers to makeExecutableSchema,
551
- // createSchema (graphql-yoga) or new ApolloServer(...). Plain buildSchema(typeDefs) accepts
552
- // the SDL too, but takes no resolvers, so scalar and enum behaviour will not attach there.
553
- ${imports.join("\n")}
554
-
555
- ${reExports.join("\n")}
556
-
557
- /** The whole schema's SDL. */
558
- export const typeDefs = [
559
- ${typeDefParts.map((p) => ` ${p},`).join("\n")}
560
- ].join('\\n\\n');
561
-
562
- /** Everything merged. Override per field: { ...resolvers, Query: { ...resolvers.Query, users: yours } } */
563
- ${resolverLines.join("\n")}
564
- `;
565
- }
566
- function buildHeader(h) {
567
- if (h && h.enabled === false) return "";
568
- const text = h?.text?.trim();
569
- const lines = text ? text.split(/\r?\n/).map((l) => `// ${l}`) : [
570
- "// Generated by DRZL (@drzl/*)",
571
- "// Generated output is granted to you under your project's license.",
572
- "// You may use, copy, modify, and distribute without attribution."
573
- ];
574
- return lines.join("\n") + "\n\n";
575
- }
576
- function toCase(s, c) {
577
- if (!c) return s;
578
- const parts = s.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]/g, " ").split(/\s+/);
579
- if (c === "camel") {
580
- return parts.map(
581
- (p, i) => i === 0 ? p.toLowerCase() : p.charAt(0).toUpperCase() + p.slice(1).toLowerCase()
582
- ).join("");
583
- }
584
- if (c === "kebab") return parts.map((p) => p.toLowerCase()).join("-");
585
- if (c === "snake") return parts.map((p) => p.toLowerCase()).join("_");
586
- return s;
587
- }
588
- var GraphQLGenerator = class {
589
- constructor(analysis) {
590
- this.analysis = analysis;
591
- }
592
- async generate(opts) {
593
- const fs = fileWriter(opts.fileSink);
594
- const path = await import("path");
595
- const out = path.resolve(process.cwd(), opts.outputDir);
596
- await fs.mkdir(out, { recursive: true });
597
- const files = [];
598
- const write = async (filePath, content) => {
599
- const formatted = await formatCode(
600
- buildHeader(opts.outputHeader) + content,
601
- filePath,
602
- opts.format
603
- );
604
- await fs.writeFile(filePath, formatted, "utf8");
605
- files.push(filePath);
606
- };
607
- const barrelPath = path.join(out, `${APP_MODULE}.ts`);
608
- const scalarsPath = path.join(out, `${SCALARS_MODULE}.ts`);
609
- const plans = this.analysis.tables.map(planTable);
610
- const modules = /* @__PURE__ */ new Map();
611
- const total = plans.length;
612
- let index = 0;
613
- for (const plan of plans) {
614
- const base = `${plan.table.tsName}${opts.naming?.routerSuffix ?? ""}`;
615
- const filePath = path.join(out, `${toCase(base, opts.naming?.procedureCase)}.ts`);
616
- if (filePath === barrelPath || filePath === scalarsPath) {
617
- const which = filePath === barrelPath ? "the barrel" : "the scalars module";
618
- throw new Error(
619
- `@drzl/generator-graphql: the module for table "${plan.table.name}" would be written to ${filePath}, which is ${which} this generator also writes. Set naming.routerSuffix to move it out of the way.`
620
- );
621
- }
622
- await write(filePath, renderTable(plan));
623
- modules.set(
624
- plan,
625
- importSpecifier("./" + path.relative(out, filePath).replace(/\\/g, "/"), opts.importExtension)
626
- );
627
- index++;
628
- opts.onProgress?.({ index, total, table: plan.table.name, filePath });
629
- }
630
- const usedScalars = ["DateTime", "BigInt", "JSON"].filter(
631
- (s) => plans.some((p) => p.scalars.has(s))
632
- );
633
- const scalarsSpec = importSpecifier(`./${SCALARS_MODULE}.ts`, opts.importExtension);
634
- await write(scalarsPath, renderScalarsModule());
635
- await write(barrelPath, renderBarrel(plans, modules, usedScalars, scalarsSpec));
636
- return { files };
637
- }
638
- };
639
- var index_default = GraphQLGenerator;
640
- export {
641
- APP_MODULE,
642
- GraphQLGenerator,
643
- SCALARS_MODULE,
644
- index_default as default
645
- };
646
- //# sourceMappingURL=dist-UVP6B4XJ.js.map