@sequenceholdings/orm 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +1 -1
  2. package/dist/authoring.cjs +794 -0
  3. package/dist/authoring.cjs.map +1 -0
  4. package/dist/authoring.d.cts +274 -0
  5. package/dist/authoring.d.ts +274 -0
  6. package/dist/authoring.js +763 -0
  7. package/dist/authoring.js.map +1 -0
  8. package/dist/canonical.cjs +146 -0
  9. package/dist/canonical.cjs.map +1 -0
  10. package/dist/canonical.d.cts +369 -0
  11. package/dist/canonical.d.ts +369 -0
  12. package/dist/canonical.js +131 -0
  13. package/dist/canonical.js.map +1 -0
  14. package/dist/cli.cjs +6208 -1693
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.d.cts +8 -1
  17. package/dist/cli.d.ts +8 -1
  18. package/dist/cli.js +6262 -1749
  19. package/dist/cli.js.map +1 -1
  20. package/dist/client.cjs +120 -0
  21. package/dist/client.cjs.map +1 -1
  22. package/dist/client.d.cts +74 -2
  23. package/dist/client.d.ts +74 -2
  24. package/dist/client.js +116 -1
  25. package/dist/client.js.map +1 -1
  26. package/dist/compiler.cjs +3001 -0
  27. package/dist/compiler.cjs.map +1 -0
  28. package/dist/compiler.d.cts +581 -0
  29. package/dist/compiler.d.ts +581 -0
  30. package/dist/compiler.js +2947 -0
  31. package/dist/compiler.js.map +1 -0
  32. package/dist/index.cjs +1730 -327
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +126 -56
  35. package/dist/index.d.ts +126 -56
  36. package/dist/index.js +1643 -324
  37. package/dist/index.js.map +1 -1
  38. package/dist/ir.cjs +407 -0
  39. package/dist/ir.cjs.map +1 -0
  40. package/dist/ir.d.cts +1179 -0
  41. package/dist/ir.d.ts +1179 -0
  42. package/dist/ir.js +388 -0
  43. package/dist/ir.js.map +1 -0
  44. package/dist/naming-CTvWSRTf.d.ts +79 -0
  45. package/dist/naming-DRyinU4u.d.cts +79 -0
  46. package/dist/sdl.cjs +833 -0
  47. package/dist/sdl.cjs.map +1 -0
  48. package/dist/sdl.d.cts +96 -0
  49. package/dist/sdl.d.ts +96 -0
  50. package/dist/sdl.js +809 -0
  51. package/dist/sdl.js.map +1 -0
  52. package/package.json +36 -3
  53. package/src/codegen-gql/apollo-hooks.cjs +98 -0
  54. package/src/codegen-gql/server-fns.cjs +101 -0
  55. package/templates/README.md +13 -0
  56. package/templates/orm-namespace/CLAUDE.md +81 -0
  57. package/templates/orm-namespace/codegen.ts +22 -0
  58. package/templates/orm-namespace/gitignore +5 -0
  59. package/templates/orm-namespace/graphql/example/queries.ts +56 -0
  60. package/templates/orm-namespace/package.json +24 -0
  61. package/templates/orm-namespace/schema/tables.ts +71 -0
  62. package/templates/orm-namespace/sequence.config.ts +32 -0
  63. package/templates/orm-namespace/tsconfig.json +15 -0
package/dist/sdl.cjs ADDED
@@ -0,0 +1,833 @@
1
+ 'use strict';
2
+
3
+ var zod = require('zod');
4
+ var crypto = require('crypto');
5
+
6
+ // src/canonical/v2.ts
7
+ var CANONICAL_FORMAT = 2;
8
+ var IDENTIFIER_RE = /^[a-z][a-z0-9_]*$/;
9
+ var identifier = zod.z.string().min(1).max(63).regex(IDENTIFIER_RE);
10
+ var COLUMN_TYPES = [
11
+ "uuid",
12
+ "text",
13
+ "integer",
14
+ "bigint",
15
+ "numeric",
16
+ "double",
17
+ "boolean",
18
+ "timestamptz",
19
+ "date",
20
+ "jsonb"
21
+ ];
22
+ var columnTypeSchema = zod.z.enum(COLUMN_TYPES);
23
+ var canonicalEnumSchema = zod.z.object({
24
+ name: identifier,
25
+ values: zod.z.array(zod.z.string().min(1).max(63)).min(1).max(100)
26
+ });
27
+ var canonicalColumnSchema = zod.z.object({
28
+ /** Property name in TS (camelCase); columnName is the Postgres name. */
29
+ name: zod.z.string().min(1).max(63),
30
+ columnName: identifier,
31
+ type: zod.z.union([columnTypeSchema, zod.z.object({ enum: identifier })]),
32
+ nullable: zod.z.boolean().default(true),
33
+ unique: zod.z.boolean().default(false),
34
+ /** Literal default, or one of the allowlisted SQL functions. */
35
+ default: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()]).optional(),
36
+ defaultFn: zod.z.enum(["gen_random_uuid()", "now()"]).optional(),
37
+ description: zod.z.string().max(500).optional(),
38
+ /** Migration hints — same semantics as v1. */
39
+ renamedFrom: identifier.optional(),
40
+ cast: zod.z.string().max(200).optional(),
41
+ deprecated: zod.z.boolean().default(false)
42
+ });
43
+ var canonicalLinkSchema = zod.z.object({
44
+ name: zod.z.string().min(1).max(63),
45
+ kind: zod.z.enum(["toOne", "toMany", "toPlatformUser"]),
46
+ /** Target table name (namespace-local). Absent for toPlatformUser. */
47
+ target: identifier.optional(),
48
+ /** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
49
+ on: identifier,
50
+ /** Referenced column on the target (default: target's PK / platform user `sub`). */
51
+ references: identifier.optional(),
52
+ /** enforce: true emits a real FK constraint (same-namespace toOne only). */
53
+ enforce: zod.z.boolean().default(false),
54
+ description: zod.z.string().max(500).optional()
55
+ });
56
+ var canonicalIndexSchema = zod.z.object({
57
+ columns: zod.z.array(identifier).min(1),
58
+ unique: zod.z.boolean().default(false),
59
+ name: identifier.optional()
60
+ });
61
+ var policyVariantSchema = zod.z.object({
62
+ /** Claims-only guard (capability()/role()/claim() comparisons — no columns). */
63
+ when: zod.z.string().min(1).max(1e3),
64
+ /** Exactly one of: allow-all bypass, or a row predicate. */
65
+ allow: zod.z.literal(true).optional(),
66
+ policy: zod.z.string().min(1).max(2e3).optional()
67
+ }).refine((v) => v.allow === true !== (typeof v.policy === "string"), {
68
+ message: "A variant declares exactly one of allow: true or policy"
69
+ });
70
+ var policyExpressionSchema = zod.z.union([
71
+ zod.z.string().min(1).max(2e3),
72
+ zod.z.object({
73
+ default: zod.z.string().min(1).max(2e3),
74
+ variants: zod.z.array(policyVariantSchema).max(10).default([])
75
+ })
76
+ ]);
77
+ var canonicalPoliciesSchema = zod.z.object({
78
+ read: policyExpressionSchema.optional(),
79
+ write: policyExpressionSchema.optional()
80
+ });
81
+ var canonicalActionSchema = zod.z.object({
82
+ name: identifier,
83
+ requires: zod.z.string().max(1e3).optional(),
84
+ params: zod.z.record(zod.z.string(), zod.z.object({
85
+ type: columnTypeSchema,
86
+ nullable: zod.z.boolean().default(false)
87
+ })).default({}),
88
+ executes: zod.z.string().min(1).max(1e4),
89
+ returns: zod.z.enum(["rows", "rowsAffected", "scalar"]).default("rowsAffected"),
90
+ audit: zod.z.object({ action: zod.z.string(), resource: zod.z.string() }).optional()
91
+ });
92
+ var canonicalTableSchema = zod.z.object({
93
+ table: identifier,
94
+ description: zod.z.string().max(2e3).optional(),
95
+ /** Table-level rename hint (new in v2). */
96
+ renamedFrom: identifier.optional(),
97
+ /** Synced/reflected read-only tables (`source: gold.*`) — v1 semantics. */
98
+ source: zod.z.string().regex(/^gold\.[a-z][a-z0-9_]*$/).optional(),
99
+ columns: zod.z.array(canonicalColumnSchema).default([]),
100
+ primaryKey: zod.z.array(identifier).min(1).max(8).optional(),
101
+ links: zod.z.array(canonicalLinkSchema).default([]),
102
+ indexes: zod.z.array(canonicalIndexSchema).default([]),
103
+ policies: canonicalPoliciesSchema.default({}),
104
+ actions: zod.z.array(canonicalActionSchema).default([])
105
+ });
106
+ var canonicalDefinitionSchema = zod.z.object({
107
+ definition_format: zod.z.literal(CANONICAL_FORMAT),
108
+ namespace: identifier.max(41),
109
+ description: zod.z.string().max(2e3).optional(),
110
+ enums: zod.z.array(canonicalEnumSchema).default([]),
111
+ tables: zod.z.array(canonicalTableSchema).min(0).max(500),
112
+ /** Verbatim custom-roles grammar (@sequenceholdings/custom-roles-manifest). */
113
+ capabilities: zod.z.object({
114
+ roles: zod.z.unknown().optional(),
115
+ uses: zod.z.array(zod.z.string()).default([])
116
+ }).default({ uses: [] })
117
+ });
118
+ function normalizeCanonical(definition) {
119
+ const sortByName = (items) => [...items].sort((a, b) => a.name.localeCompare(b.name));
120
+ return {
121
+ ...definition,
122
+ enums: sortByName(definition.enums),
123
+ tables: [...definition.tables].sort((a, b) => a.table.localeCompare(b.table)).map((t) => ({
124
+ ...t,
125
+ links: sortByName(t.links),
126
+ indexes: [...t.indexes].sort((a, b) => (a.name ?? a.columns.join("_")).localeCompare(b.name ?? b.columns.join("_"))),
127
+ actions: sortByName(t.actions)
128
+ }))
129
+ };
130
+ }
131
+
132
+ // src/sdl/naming.ts
133
+ function camelCase(name) {
134
+ const segments = name.split("_").filter((segment) => segment.length > 0);
135
+ const first = segments[0];
136
+ if (first === void 0) return name;
137
+ const head = first.charAt(0).toLowerCase() + first.slice(1);
138
+ return head + segments.slice(1).map(capitalizeFirst).join("");
139
+ }
140
+ function pascalCase(name) {
141
+ return capitalizeFirst(camelCase(name));
142
+ }
143
+ function capitalizeFirst(segment) {
144
+ return segment.charAt(0).toUpperCase() + segment.slice(1);
145
+ }
146
+ function singularizeWord(word) {
147
+ if (word.endsWith("ies") && word.length > 3) return `${word.slice(0, -3)}y`;
148
+ for (const suffix of ["sses", "xes", "ches", "shes"]) {
149
+ if (word.endsWith(suffix)) return word.slice(0, -2);
150
+ }
151
+ if (word.endsWith("ss")) return word;
152
+ if (word.endsWith("s") && word.length > 1) return word.slice(0, -1);
153
+ return word;
154
+ }
155
+ function typeName(table) {
156
+ const segments = table.split("_").filter((segment) => segment.length > 0);
157
+ const last = segments[segments.length - 1];
158
+ if (last === void 0) return pascalCase(table);
159
+ return [...segments.slice(0, -1), singularizeWord(last)].map(capitalizeFirst).join("");
160
+ }
161
+ function fieldName(column) {
162
+ return camelCase(column);
163
+ }
164
+ function enumTypeName(name) {
165
+ return pascalCase(name);
166
+ }
167
+ function comparisonExpName(gqlTypeName) {
168
+ return `${capitalizeFirst(gqlTypeName)}ComparisonExp`;
169
+ }
170
+ var SCALAR_FOR_COLUMN_TYPE = {
171
+ uuid: "uuid",
172
+ text: "String",
173
+ integer: "Int",
174
+ bigint: "bigint",
175
+ numeric: "numeric",
176
+ double: "Float",
177
+ boolean: "Boolean",
178
+ timestamptz: "timestamptz",
179
+ date: "date",
180
+ jsonb: "jsonb"
181
+ };
182
+ function graphqlScalarFor(type) {
183
+ return SCALAR_FOR_COLUMN_TYPE[type];
184
+ }
185
+ var COLUMN_TYPE_BY_SCALAR = new Map([
186
+ ["ID", "text"],
187
+ ...COLUMN_TYPES.map((type) => [graphqlScalarFor(type), type])
188
+ ]);
189
+ function columnTypeForScalar(scalarName) {
190
+ return COLUMN_TYPE_BY_SCALAR.get(scalarName);
191
+ }
192
+ var VARIABLE_SCALAR_NAMES = [...COLUMN_TYPE_BY_SCALAR.keys()];
193
+ var CUSTOM_SCALARS = ["bigint", "date", "jsonb", "numeric", "timestamptz", "uuid"];
194
+ var NUMERICISH_COLUMN_TYPES = ["integer", "bigint", "numeric", "double"];
195
+ var ORDER_BY_ENUM = "OrderBy";
196
+ var ORDER_BY_ENUM_VALUES = [
197
+ "ASC",
198
+ "ASC_NULLS_FIRST",
199
+ "ASC_NULLS_LAST",
200
+ "DESC",
201
+ "DESC_NULLS_FIRST",
202
+ "DESC_NULLS_LAST"
203
+ ];
204
+ var PLATFORM_USER_TYPENAME = "PlatformUser";
205
+ function tableNames(table) {
206
+ const root = camelCase(table);
207
+ const rootPascal = pascalCase(table);
208
+ const type = typeName(table);
209
+ return {
210
+ typename: type,
211
+ selectRoot: root,
212
+ byPkRoot: `${root}ByPk`,
213
+ aggregateRoot: `${root}Aggregate`,
214
+ insertRoot: `insert${rootPascal}`,
215
+ insertOneRoot: `insert${rootPascal}One`,
216
+ updateRoot: `update${rootPascal}`,
217
+ updateByPkRoot: `update${rootPascal}ByPk`,
218
+ deleteRoot: `delete${rootPascal}`,
219
+ deleteByPkRoot: `delete${rootPascal}ByPk`,
220
+ boolExp: `${type}BoolExp`,
221
+ orderBy: `${type}OrderBy`,
222
+ selectColumn: `${type}SelectColumn`,
223
+ insertInput: `${type}InsertInput`,
224
+ setInput: `${type}SetInput`,
225
+ appendInput: `${type}AppendInput`,
226
+ incInput: `${type}IncInput`,
227
+ onConflict: `${type}OnConflict`,
228
+ pkColumnsInput: `${type}PkColumnsInput`,
229
+ constraintEnum: `${type}Constraint`,
230
+ updateColumnEnum: `${type}UpdateColumn`,
231
+ mutationResponse: `${type}MutationResponse`,
232
+ aggregate: `${type}Aggregate`,
233
+ aggregateFields: `${type}AggregateFields`,
234
+ sumFields: `${type}SumFields`,
235
+ avgFields: `${type}AvgFields`,
236
+ aggregateBoolExp: `${type}AggregateBoolExp`,
237
+ aggregateBoolExpCount: `${type}AggregateBoolExpCount`
238
+ };
239
+ }
240
+
241
+ // src/sdl/generate.ts
242
+ var SdlGenerationError = class extends Error {
243
+ constructor(message) {
244
+ super(message);
245
+ this.name = "SdlGenerationError";
246
+ }
247
+ };
248
+ var GRAPHQL_NAME_RE = /^[_A-Za-z][_0-9A-Za-z]*$/;
249
+ var RESERVED_ENUM_VALUES = /* @__PURE__ */ new Set(["true", "false", "null"]);
250
+ var NUMERICISH = new Set(NUMERICISH_COLUMN_TYPES);
251
+ var PLATFORM_USER_FIELDS = [
252
+ { name: "sub", gqlType: "String", nullable: false },
253
+ { name: "email", gqlType: "String", nullable: true },
254
+ { name: "name", gqlType: "String", nullable: true },
255
+ { name: "organization", gqlType: "String", nullable: true },
256
+ { name: "role", gqlType: "String", nullable: true },
257
+ { name: "managerId", gqlType: "String", nullable: true },
258
+ { name: "accountEnabled", gqlType: "Boolean", nullable: false }
259
+ ];
260
+ function generateSdl(canonical, options = {}) {
261
+ const def = normalizeCanonical(canonicalDefinitionSchema.parse(canonical));
262
+ const tables = def.tables.filter((table) => table.source === void 0);
263
+ if (tables.length === 0) {
264
+ throw new SdlGenerationError(
265
+ `Namespace "${def.namespace}" exposes no tables (synced tables are excluded from SDL in v2.0) \u2014 nothing to generate.`
266
+ );
267
+ }
268
+ for (const table of tables) {
269
+ if (table.columns.length === 0) {
270
+ throw new SdlGenerationError(`Table "${table.table}" has no columns \u2014 cannot generate an object type.`);
271
+ }
272
+ }
273
+ const namesByTable = new Map(tables.map((table) => [table.table, tableNames(table.table)]));
274
+ const enumByName = new Map(def.enums.map((entry) => [entry.name, entry]));
275
+ const hasPlatformUser = tables.some((table) => table.links.some((link) => link.kind === "toPlatformUser"));
276
+ const usedEnums = /* @__PURE__ */ new Set();
277
+ for (const table of tables) {
278
+ for (const column of table.columns) {
279
+ if (typeof column.type === "object") {
280
+ if (!enumByName.has(column.type.enum)) {
281
+ throw new SdlGenerationError(
282
+ `Column "${table.table}.${column.columnName}" references undeclared enum "${column.type.enum}".`
283
+ );
284
+ }
285
+ usedEnums.add(column.type.enum);
286
+ }
287
+ }
288
+ }
289
+ const aggregateBoolExpTargets = /* @__PURE__ */ new Set();
290
+ for (const table of tables) {
291
+ for (const link of table.links) {
292
+ if (link.kind === "toMany" && link.target !== void 0 && namesByTable.has(link.target)) {
293
+ aggregateBoolExpTargets.add(link.target);
294
+ }
295
+ }
296
+ }
297
+ const usedComparisonScalars = /* @__PURE__ */ new Set();
298
+ for (const table of tables) {
299
+ for (const column of table.columns) {
300
+ if (typeof column.type !== "object") usedComparisonScalars.add(graphqlScalarFor(column.type));
301
+ }
302
+ }
303
+ if (hasPlatformUser) {
304
+ usedComparisonScalars.add("String");
305
+ usedComparisonScalars.add("Boolean");
306
+ }
307
+ if (aggregateBoolExpTargets.size > 0) usedComparisonScalars.add("Int");
308
+ const blocks = [];
309
+ blocks.push(
310
+ `# Generated by @sequenceholdings/orm \u2014 GraphQL SDL for namespace "${def.namespace}".
311
+ # Do not edit: regenerate with \`seq-studio orm generate\`.`
312
+ );
313
+ blocks.push("directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT");
314
+ blocks.push(
315
+ '"""Gates an operation behind a platform capability or custom role \u2014 checked by the data plane before execution."""\ndirective @requires(capability: String, role: String) on QUERY | MUTATION'
316
+ );
317
+ blocks.push("directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT");
318
+ for (const scalar of CUSTOM_SCALARS) blocks.push(`scalar ${scalar}`);
319
+ blocks.push(renderEnum(ORDER_BY_ENUM, [...ORDER_BY_ENUM_VALUES]));
320
+ for (const enumName of [...usedEnums].sort()) {
321
+ const entry = enumByName.get(enumName);
322
+ if (!entry) continue;
323
+ for (const value of entry.values) assertEnumValue(value, `enum "${enumName}"`);
324
+ blocks.push(renderEnum(enumTypeName(enumName), entry.values));
325
+ }
326
+ for (const scalar of [...usedComparisonScalars].sort()) {
327
+ blocks.push(renderComparisonExp(scalar));
328
+ }
329
+ for (const enumName of [...usedEnums].sort()) {
330
+ blocks.push(renderComparisonExp(enumTypeName(enumName)));
331
+ }
332
+ if (hasPlatformUser) blocks.push(...renderPlatformUserBlocks());
333
+ for (const table of tables) {
334
+ const names = namesByTable.get(table.table);
335
+ if (!names) continue;
336
+ blocks.push(
337
+ ...renderTableBlocks({
338
+ table,
339
+ names,
340
+ namesByTable,
341
+ emitAggregateBoolExp: aggregateBoolExpTargets.has(table.table),
342
+ pinned: options.pinnedColumns?.[table.table] ?? []
343
+ })
344
+ );
345
+ }
346
+ blocks.push(renderQueryRoot(tables, namesByTable));
347
+ const mutationRoot = renderMutationRoot(tables, namesByTable);
348
+ if (mutationRoot !== null) blocks.push(mutationRoot);
349
+ return `${blocks.join("\n\n")}
350
+ `;
351
+ }
352
+ function assertEnumValue(value, context) {
353
+ if (!GRAPHQL_NAME_RE.test(value) || RESERVED_ENUM_VALUES.has(value)) {
354
+ throw new SdlGenerationError(
355
+ `Value "${value}" in ${context} is not a valid GraphQL enum value (must match ${GRAPHQL_NAME_RE} and not be true/false/null).`
356
+ );
357
+ }
358
+ }
359
+ function renderEnum(name, values) {
360
+ return `enum ${name} {
361
+ ${values.map((value) => ` ${value}`).join("\n")}
362
+ }`;
363
+ }
364
+ function descriptionLines(text, indent) {
365
+ const escaped = text.replace(/"""/g, '\\"""');
366
+ return [`${indent}"""`, ...escaped.split("\n").map((line) => `${indent}${line}`), `${indent}"""`];
367
+ }
368
+ function renderComparisonExp(gqlTypeName) {
369
+ const fields = [
370
+ ["_eq", gqlTypeName],
371
+ ["_neq", gqlTypeName],
372
+ ["_gt", gqlTypeName],
373
+ ["_gte", gqlTypeName],
374
+ ["_lt", gqlTypeName],
375
+ ["_lte", gqlTypeName],
376
+ ["_in", `[${gqlTypeName}!]`],
377
+ ["_nin", `[${gqlTypeName}!]`],
378
+ ["_isNull", "Boolean"]
379
+ ];
380
+ if (gqlTypeName === "String") {
381
+ fields.push(["_like", "String"], ["_ilike", "String"]);
382
+ }
383
+ if (gqlTypeName === "jsonb") {
384
+ fields.push(["_contains", "jsonb"], ["_hasKey", "String"]);
385
+ }
386
+ fields.sort(([a], [b]) => a.localeCompare(b));
387
+ const body = fields.map(([field, type]) => ` ${field}: ${type}`).join("\n");
388
+ return `input ${comparisonExpName(gqlTypeName)} {
389
+ ${body}
390
+ }`;
391
+ }
392
+ function renderPlatformUserBlocks() {
393
+ const typeFields = PLATFORM_USER_FIELDS.map(
394
+ (field) => ` ${field.name}: ${field.gqlType}${field.nullable ? "" : "!"}`
395
+ ).join("\n");
396
+ const boolExpName = `${PLATFORM_USER_TYPENAME}BoolExp`;
397
+ const boolFields = [
398
+ ` _and: [${boolExpName}!]`,
399
+ ` _not: ${boolExpName}`,
400
+ ` _or: [${boolExpName}!]`,
401
+ ...PLATFORM_USER_FIELDS.map((field) => ` ${field.name}: ${comparisonExpName(field.gqlType)}`)
402
+ ].join("\n");
403
+ const orderFields = PLATFORM_USER_FIELDS.map((field) => ` ${field.name}: ${ORDER_BY_ENUM}`).join("\n");
404
+ return [
405
+ `"""Curated read-only view over platform users (plan \xA74b) \u2014 reachable only through \`toPlatformUser\` links."""
406
+ type ${PLATFORM_USER_TYPENAME} {
407
+ ${typeFields}
408
+ }`,
409
+ `input ${boolExpName} {
410
+ ${boolFields}
411
+ }`,
412
+ `input ${PLATFORM_USER_TYPENAME}OrderBy {
413
+ ${orderFields}
414
+ }`
415
+ ];
416
+ }
417
+ function relationArgs(target) {
418
+ return `where: ${target.boolExp}, orderBy: [${target.orderBy}!], limit: Int, offset: Int`;
419
+ }
420
+ function rootArgs(names) {
421
+ return `where: ${names.boolExp}, orderBy: [${names.orderBy}!], distinctOn: [${names.selectColumn}!], limit: Int, offset: Int`;
422
+ }
423
+ function primaryKeyColumns(table) {
424
+ if (!table.primaryKey || table.primaryKey.length === 0) return [];
425
+ return table.primaryKey.map((identifier2) => {
426
+ const column = table.columns.find((c) => c.columnName === identifier2 || c.name === identifier2);
427
+ if (!column) {
428
+ throw new SdlGenerationError(`Primary key column "${identifier2}" not found on table "${table.table}".`);
429
+ }
430
+ return column;
431
+ });
432
+ }
433
+ function renderTableBlocks(ctx) {
434
+ const { table, names } = ctx;
435
+ const blocks = [
436
+ renderObjectType(ctx),
437
+ renderSelectColumnEnum(ctx),
438
+ renderOrderByInput(ctx),
439
+ renderBoolExp(ctx),
440
+ ...renderAggregateTypes(ctx)
441
+ ];
442
+ if (ctx.emitAggregateBoolExp) blocks.push(...renderAggregateBoolExp(ctx));
443
+ const pk = primaryKeyColumns(table);
444
+ if (pk.length > 0) {
445
+ const fields = pk.map((column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}!`).join("\n");
446
+ blocks.push(`input ${names.pkColumnsInput} {
447
+ ${fields}
448
+ }`);
449
+ }
450
+ if (isWritable(table)) blocks.push(...renderWriteBlocks(ctx));
451
+ return blocks;
452
+ }
453
+ function isWritable(table) {
454
+ return table.policies.write !== void 0;
455
+ }
456
+ function renderObjectType(ctx) {
457
+ const { table, names } = ctx;
458
+ const lines = [];
459
+ if (table.description !== void 0) lines.push(...descriptionLines(table.description, ""));
460
+ lines.push(`type ${names.typename} {`);
461
+ for (const column of table.columns) {
462
+ if (column.description !== void 0) lines.push(...descriptionLines(column.description, " "));
463
+ const bang = column.nullable ? "" : "!";
464
+ const deprecated = column.deprecated ? " @deprecated" : "";
465
+ lines.push(` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}${bang}${deprecated}`);
466
+ }
467
+ for (const link of table.links) {
468
+ if (link.description !== void 0) lines.push(...descriptionLines(link.description, " "));
469
+ if (link.kind === "toPlatformUser") {
470
+ lines.push(` ${fieldName(link.name)}: ${PLATFORM_USER_TYPENAME}`);
471
+ continue;
472
+ }
473
+ const target = link.target !== void 0 ? ctx.namesByTable.get(link.target) : void 0;
474
+ if (!target) continue;
475
+ if (link.kind === "toOne") {
476
+ lines.push(` ${fieldName(link.name)}: ${target.typename}`);
477
+ } else {
478
+ lines.push(` ${fieldName(link.name)}(${relationArgs(target)}): [${target.typename}!]!`);
479
+ lines.push(` ${fieldName(link.name)}Aggregate(${relationArgs(target)}): ${target.aggregate}!`);
480
+ }
481
+ }
482
+ lines.push("}");
483
+ return lines.join("\n");
484
+ }
485
+ function renderSelectColumnEnum(ctx) {
486
+ const values = ctx.table.columns.map((column) => fieldName(column.name));
487
+ for (const value of values) assertEnumValue(value, `select-column enum for table "${ctx.table.table}"`);
488
+ return renderEnum(ctx.names.selectColumn, values);
489
+ }
490
+ function renderOrderByInput(ctx) {
491
+ const { table, names } = ctx;
492
+ const lines = table.columns.map((column) => ` ${fieldName(column.name)}: ${ORDER_BY_ENUM}`);
493
+ for (const link of table.links) {
494
+ if (link.kind === "toPlatformUser") {
495
+ lines.push(` ${fieldName(link.name)}: ${PLATFORM_USER_TYPENAME}OrderBy`);
496
+ } else if (link.kind === "toOne" && link.target !== void 0) {
497
+ const target = ctx.namesByTable.get(link.target);
498
+ if (target) lines.push(` ${fieldName(link.name)}: ${target.orderBy}`);
499
+ }
500
+ }
501
+ return `input ${names.orderBy} {
502
+ ${lines.join("\n")}
503
+ }`;
504
+ }
505
+ function renderBoolExp(ctx) {
506
+ const { table, names } = ctx;
507
+ const lines = [
508
+ ` _and: [${names.boolExp}!]`,
509
+ ` _not: ${names.boolExp}`,
510
+ ` _or: [${names.boolExp}!]`
511
+ ];
512
+ for (const column of table.columns) {
513
+ lines.push(` ${fieldName(column.name)}: ${comparisonExpName(columnGqlTypeOf(table, column))}`);
514
+ }
515
+ for (const link of table.links) {
516
+ if (link.kind === "toPlatformUser") {
517
+ lines.push(` ${fieldName(link.name)}: ${PLATFORM_USER_TYPENAME}BoolExp`);
518
+ continue;
519
+ }
520
+ const target = link.target !== void 0 ? ctx.namesByTable.get(link.target) : void 0;
521
+ if (!target) continue;
522
+ lines.push(` ${fieldName(link.name)}: ${target.boolExp}`);
523
+ if (link.kind === "toMany") {
524
+ lines.push(` ${fieldName(link.name)}Aggregate: ${target.aggregateBoolExp}`);
525
+ }
526
+ }
527
+ return `input ${names.boolExp} {
528
+ ${lines.join("\n")}
529
+ }`;
530
+ }
531
+ function numericishColumns(table) {
532
+ return table.columns.filter((column) => typeof column.type !== "object" && NUMERICISH.has(column.type));
533
+ }
534
+ function jsonbColumns(table) {
535
+ return table.columns.filter((column) => column.type === "jsonb");
536
+ }
537
+ function renderAggregateTypes(ctx) {
538
+ const { table, names } = ctx;
539
+ const numericish = numericishColumns(table);
540
+ const blocks = [];
541
+ blocks.push(
542
+ `type ${names.aggregate} {
543
+ aggregate: ${names.aggregateFields}
544
+ nodes: [${names.typename}!]!
545
+ }`
546
+ );
547
+ const fieldLines = [` count(columns: [${names.selectColumn}!], distinct: Boolean): Int!`];
548
+ if (numericish.length > 0) {
549
+ fieldLines.push(` avg: ${names.avgFields}`);
550
+ fieldLines.push(` sum: ${names.sumFields}`);
551
+ }
552
+ blocks.push(`type ${names.aggregateFields} {
553
+ ${fieldLines.join("\n")}
554
+ }`);
555
+ if (numericish.length > 0) {
556
+ const avgLines = numericish.map((column) => ` ${fieldName(column.name)}: Float`);
557
+ blocks.push(`type ${names.avgFields} {
558
+ ${avgLines.join("\n")}
559
+ }`);
560
+ const sumLines = numericish.map((column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`);
561
+ blocks.push(`type ${names.sumFields} {
562
+ ${sumLines.join("\n")}
563
+ }`);
564
+ }
565
+ return blocks;
566
+ }
567
+ function renderAggregateBoolExp(ctx) {
568
+ const { names } = ctx;
569
+ return [
570
+ `input ${names.aggregateBoolExp} {
571
+ count: ${names.aggregateBoolExpCount}
572
+ }`,
573
+ `input ${names.aggregateBoolExpCount} {
574
+ arguments: [${names.selectColumn}!]
575
+ distinct: Boolean
576
+ filter: ${names.boolExp}
577
+ predicate: IntComparisonExp!
578
+ }`
579
+ ];
580
+ }
581
+ function constraintValues(table) {
582
+ const values = /* @__PURE__ */ new Set();
583
+ if (table.primaryKey && table.primaryKey.length > 0) values.add(`${table.table}_pkey`);
584
+ for (const column of table.columns) {
585
+ if (column.unique) values.add(`${table.table}_${column.columnName}_key`);
586
+ }
587
+ for (const index of table.indexes) {
588
+ if (index.unique) values.add(index.name ?? `${table.table}_${index.columns.join("_")}_key`);
589
+ }
590
+ return [...values].sort();
591
+ }
592
+ function renderWriteBlocks(ctx) {
593
+ const { table, names, pinned } = ctx;
594
+ const blocks = [];
595
+ const pinnedSet = new Set(pinned);
596
+ const insertable = table.columns.filter(
597
+ (column) => !pinnedSet.has(column.name) && !pinnedSet.has(column.columnName)
598
+ );
599
+ if (insertable.length === 0) {
600
+ throw new SdlGenerationError(
601
+ `Table "${table.table}" has no insertable columns after excluding pinned columns [${pinned.join(", ")}].`
602
+ );
603
+ }
604
+ const insertLines = insertable.map(
605
+ (column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`
606
+ );
607
+ blocks.push(`input ${names.insertInput} {
608
+ ${insertLines.join("\n")}
609
+ }`);
610
+ const setLines = table.columns.map(
611
+ (column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`
612
+ );
613
+ blocks.push(`input ${names.setInput} {
614
+ ${setLines.join("\n")}
615
+ }`);
616
+ const jsonb = jsonbColumns(table);
617
+ if (jsonb.length > 0) {
618
+ const appendLines = jsonb.map((column) => ` ${fieldName(column.name)}: jsonb`);
619
+ blocks.push(`input ${names.appendInput} {
620
+ ${appendLines.join("\n")}
621
+ }`);
622
+ }
623
+ const numericish = numericishColumns(table);
624
+ if (numericish.length > 0) {
625
+ const incLines = numericish.map(
626
+ (column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`
627
+ );
628
+ blocks.push(`input ${names.incInput} {
629
+ ${incLines.join("\n")}
630
+ }`);
631
+ }
632
+ const constraints = constraintValues(table);
633
+ if (constraints.length > 0) {
634
+ for (const value of constraints) assertEnumValue(value, `constraint enum for table "${table.table}"`);
635
+ blocks.push(renderEnum(names.constraintEnum, constraints));
636
+ const updateColumns = table.columns.map((column) => fieldName(column.name));
637
+ blocks.push(renderEnum(names.updateColumnEnum, updateColumns));
638
+ blocks.push(
639
+ `input ${names.onConflict} {
640
+ constraint: ${names.constraintEnum}!
641
+ updateColumns: [${names.updateColumnEnum}!]! = []
642
+ where: ${names.boolExp}
643
+ }`
644
+ );
645
+ }
646
+ blocks.push(
647
+ `type ${names.mutationResponse} {
648
+ affectedRows: Int!
649
+ returning: [${names.typename}!]!
650
+ }`
651
+ );
652
+ return blocks;
653
+ }
654
+ function renderQueryRoot(tables, namesByTable) {
655
+ const lines = ["type Query {"];
656
+ for (const table of tables) {
657
+ const names = namesByTable.get(table.table);
658
+ if (!names) continue;
659
+ lines.push(` ${names.selectRoot}(${rootArgs(names)}): [${names.typename}!]!`);
660
+ const pk = primaryKeyColumns(table);
661
+ if (pk.length > 0) {
662
+ const args = pk.map((column) => `${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}!`).join(", ");
663
+ lines.push(` ${names.byPkRoot}(${args}): ${names.typename}`);
664
+ }
665
+ lines.push(` ${names.aggregateRoot}(${rootArgs(names)}): ${names.aggregate}!`);
666
+ }
667
+ lines.push("}");
668
+ return lines.join("\n");
669
+ }
670
+ function columnGqlTypeOf(table, column) {
671
+ return typeof column.type === "object" ? enumTypeName(column.type.enum) : graphqlScalarFor(column.type);
672
+ }
673
+ function renderMutationRoot(tables, namesByTable) {
674
+ const lines = [];
675
+ for (const table of tables) {
676
+ if (!isWritable(table)) continue;
677
+ const names = namesByTable.get(table.table);
678
+ if (!names) continue;
679
+ const constraints = constraintValues(table);
680
+ const onConflictArg = constraints.length > 0 ? `, onConflict: ${names.onConflict}` : "";
681
+ const updateArgs = [`_set: ${names.setInput}`];
682
+ if (jsonbColumns(table).length > 0) updateArgs.push(`_append: ${names.appendInput}`);
683
+ if (numericishColumns(table).length > 0) updateArgs.push(`_inc: ${names.incInput}`);
684
+ const updateArgList = updateArgs.join(", ");
685
+ lines.push(` ${names.insertRoot}(objects: [${names.insertInput}!]!${onConflictArg}): ${names.mutationResponse}`);
686
+ lines.push(` ${names.insertOneRoot}(object: ${names.insertInput}!${onConflictArg}): ${names.typename}`);
687
+ lines.push(` ${names.updateRoot}(where: ${names.boolExp}!, ${updateArgList}): ${names.mutationResponse}`);
688
+ const pk = primaryKeyColumns(table);
689
+ if (pk.length > 0) {
690
+ lines.push(
691
+ ` ${names.updateByPkRoot}(pkColumns: ${names.pkColumnsInput}!, ${updateArgList}): ${names.typename}`
692
+ );
693
+ }
694
+ lines.push(` ${names.deleteRoot}(where: ${names.boolExp}!): ${names.mutationResponse}`);
695
+ if (pk.length > 0) {
696
+ const args = pk.map((column) => `${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}!`).join(", ");
697
+ lines.push(` ${names.deleteByPkRoot}(${args}): ${names.typename}`);
698
+ }
699
+ }
700
+ if (lines.length === 0) return null;
701
+ return `type Mutation {
702
+ ${lines.join("\n")}
703
+ }`;
704
+ }
705
+ function stableJson(value) {
706
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
707
+ if (Array.isArray(value)) return `[${value.map(stableJson).join(",")}]`;
708
+ const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== void 0).sort(([a], [b]) => a < b ? -1 : 1).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableJson(entryValue)}`);
709
+ return `{${entries.join(",")}}`;
710
+ }
711
+
712
+ // src/codegen-gql/errors.ts
713
+ var CodegenGqlError = class extends Error {
714
+ constructor(message) {
715
+ super(message);
716
+ this.name = "CodegenGqlError";
717
+ }
718
+ };
719
+
720
+ // src/codegen-gql/manifest.ts
721
+ var MANIFEST_VERSION = 1;
722
+ var OP_HASH_RE = /^sha256:[0-9a-f]{64}$/;
723
+ var GRAPHQL_NAME_RE2 = /^[_A-Za-z][_0-9A-Za-z]*$/;
724
+ var manifestRequiresSchema = zod.z.object({
725
+ capability: zod.z.string().min(1).optional(),
726
+ role: zod.z.string().min(1).optional()
727
+ }).nullable();
728
+ var manifestOperationSchema = zod.z.object({
729
+ hash: zod.z.string().regex(OP_HASH_RE),
730
+ kind: zod.z.enum(["query", "mutation", "action"]),
731
+ requires: manifestRequiresSchema
732
+ });
733
+ var operationsManifestSchema = zod.z.object({
734
+ manifestVersion: zod.z.literal(MANIFEST_VERSION),
735
+ schemaHash: zod.z.string().min(1),
736
+ opsHash: zod.z.string().regex(OP_HASH_RE),
737
+ operations: zod.z.record(zod.z.string().regex(GRAPHQL_NAME_RE2), manifestOperationSchema)
738
+ });
739
+ function buildOperationsManifest({
740
+ schemaHash,
741
+ operations
742
+ }) {
743
+ if (schemaHash.length === 0) throw new CodegenGqlError("schemaHash must be a non-empty string.");
744
+ const sorted = [...operations].sort((a, b) => a.name.localeCompare(b.name));
745
+ const seen = /* @__PURE__ */ new Set();
746
+ const entries = sorted.map((op) => {
747
+ if (!GRAPHQL_NAME_RE2.test(op.name)) {
748
+ throw new CodegenGqlError(`Operation name "${op.name}" is not a valid GraphQL name.`);
749
+ }
750
+ if (seen.has(op.name)) {
751
+ throw new CodegenGqlError(`Duplicate operation name "${op.name}" \u2014 operation names must be unique per namespace.`);
752
+ }
753
+ seen.add(op.name);
754
+ if (!OP_HASH_RE.test(op.hash)) {
755
+ throw new CodegenGqlError(`Operation "${op.name}" has malformed hash "${op.hash}" (expected sha256:<64 hex>).`);
756
+ }
757
+ return { name: op.name, hash: op.hash, kind: op.kind, requires: normalizeRequires(op.requires) };
758
+ });
759
+ const opsHash = `sha256:${crypto.createHash("sha256").update(stableJson(entries)).digest("hex")}`;
760
+ const operationsRecord = {};
761
+ for (const entry of entries) {
762
+ operationsRecord[entry.name] = { hash: entry.hash, kind: entry.kind, requires: entry.requires };
763
+ }
764
+ return {
765
+ manifestVersion: MANIFEST_VERSION,
766
+ schemaHash,
767
+ opsHash,
768
+ operations: operationsRecord
769
+ };
770
+ }
771
+ function normalizeRequires(requires) {
772
+ if (requires === void 0 || requires === null) return null;
773
+ const normalized = {};
774
+ if (requires.capability !== void 0) normalized.capability = requires.capability;
775
+ if (requires.role !== void 0) normalized.role = requires.role;
776
+ return normalized.capability === void 0 && normalized.role === void 0 ? null : normalized;
777
+ }
778
+
779
+ // src/codegen-gql/type-policies.ts
780
+ function generateTypePolicies(canonical) {
781
+ const def = normalizeCanonical(canonicalDefinitionSchema.parse(canonical));
782
+ const entries = [];
783
+ let hasPlatformUser = false;
784
+ for (const table of def.tables) {
785
+ if (table.source !== void 0) continue;
786
+ if (table.links.some((link) => link.kind === "toPlatformUser")) hasPlatformUser = true;
787
+ if (!table.primaryKey || table.primaryKey.length === 0) continue;
788
+ const keyFields = table.primaryKey.map((identifier2) => {
789
+ const column = table.columns.find((c) => c.columnName === identifier2 || c.name === identifier2);
790
+ if (!column) {
791
+ throw new CodegenGqlError(`Primary key column "${identifier2}" not found on table "${table.table}".`);
792
+ }
793
+ return fieldName(column.name);
794
+ });
795
+ entries.push({ typename: tableNames(table.table).typename, keyFields });
796
+ }
797
+ if (hasPlatformUser) entries.push({ typename: PLATFORM_USER_TYPENAME, keyFields: ["sub"] });
798
+ entries.sort((a, b) => a.typename.localeCompare(b.typename));
799
+ const body = entries.map((entry) => ` ${entry.typename}: { keyFields: [${entry.keyFields.map((key) => `'${key}'`).join(", ")}] },`).join("\n");
800
+ return `/* Generated by @sequenceholdings/orm \u2014 Apollo cache type policies. Do not edit. */
801
+ import type { TypePolicies } from '@apollo/client'
802
+
803
+ export const typePolicies: TypePolicies = {
804
+ ${body}
805
+ }
806
+ `;
807
+ }
808
+
809
+ exports.CUSTOM_SCALARS = CUSTOM_SCALARS;
810
+ exports.MANIFEST_VERSION = MANIFEST_VERSION;
811
+ exports.NUMERICISH_COLUMN_TYPES = NUMERICISH_COLUMN_TYPES;
812
+ exports.ORDER_BY_ENUM = ORDER_BY_ENUM;
813
+ exports.ORDER_BY_ENUM_VALUES = ORDER_BY_ENUM_VALUES;
814
+ exports.PLATFORM_USER_TYPENAME = PLATFORM_USER_TYPENAME;
815
+ exports.SdlGenerationError = SdlGenerationError;
816
+ exports.VARIABLE_SCALAR_NAMES = VARIABLE_SCALAR_NAMES;
817
+ exports.buildOperationsManifest = buildOperationsManifest;
818
+ exports.camelCase = camelCase;
819
+ exports.columnTypeForScalar = columnTypeForScalar;
820
+ exports.comparisonExpName = comparisonExpName;
821
+ exports.enumTypeName = enumTypeName;
822
+ exports.fieldName = fieldName;
823
+ exports.generateSdl = generateSdl;
824
+ exports.generateTypePolicies = generateTypePolicies;
825
+ exports.graphqlScalarFor = graphqlScalarFor;
826
+ exports.manifestOperationSchema = manifestOperationSchema;
827
+ exports.manifestRequiresSchema = manifestRequiresSchema;
828
+ exports.operationsManifestSchema = operationsManifestSchema;
829
+ exports.pascalCase = pascalCase;
830
+ exports.tableNames = tableNames;
831
+ exports.typeName = typeName;
832
+ //# sourceMappingURL=sdl.cjs.map
833
+ //# sourceMappingURL=sdl.cjs.map