@sequenceholdings/orm 0.1.3 → 2.1.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/README.md +108 -38
- package/dist/authoring.cjs +128 -16
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +21 -4
- package/dist/authoring.d.ts +21 -4
- package/dist/authoring.js +128 -16
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.cjs +83 -9
- package/dist/canonical.cjs.map +1 -1
- package/dist/canonical.d.cts +47 -1
- package/dist/canonical.d.ts +47 -1
- package/dist/canonical.js +83 -10
- package/dist/canonical.js.map +1 -1
- package/dist/cli.cjs +816 -134
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +818 -136
- package/dist/cli.js.map +1 -1
- package/dist/client.cjs.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/compiler.cjs +488 -96
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +34 -2
- package/dist/compiler.d.ts +34 -2
- package/dist/compiler.js +489 -97
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +420 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +266 -9
- package/dist/index.d.ts +266 -9
- package/dist/index.js +410 -73
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +146 -12
- package/dist/ir.cjs.map +1 -1
- package/dist/ir.d.cts +161 -5
- package/dist/ir.d.ts +161 -5
- package/dist/ir.js +144 -13
- package/dist/ir.js.map +1 -1
- package/dist/{naming-CTvWSRTf.d.ts → naming-DCn94LJ4.d.ts} +15 -1
- package/dist/{naming-DRyinU4u.d.cts → naming-DPglwzkB.d.cts} +15 -1
- package/dist/predicate-v2.cjs +136 -11
- package/dist/predicate-v2.cjs.map +1 -1
- package/dist/predicate-v2.d.cts +3 -0
- package/dist/predicate-v2.d.ts +3 -0
- package/dist/predicate-v2.js +136 -11
- package/dist/predicate-v2.js.map +1 -1
- package/dist/sdl.cjs +460 -13
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.d.cts +1 -1
- package/dist/sdl.d.ts +1 -1
- package/dist/sdl.js +459 -14
- package/dist/sdl.js.map +1 -1
- package/package.json +6 -4
- package/src/codegen-gql/preset.cjs +64 -0
- package/src/codegen-gql/server-fns.cjs +11 -6
- package/templates/orm-namespace/codegen.preset.cjs +17 -0
- package/templates/orm-namespace/codegen.ts +1 -1
- package/templates/orm-namespace/package.json +3 -0
- package/templates/orm-namespace/schema/tables.ts +1 -1
- package/templates/orm-namespace/sequence.config.ts +1 -1
package/dist/predicate-v2.d.cts
CHANGED
package/dist/predicate-v2.d.ts
CHANGED
package/dist/predicate-v2.js
CHANGED
|
@@ -29,10 +29,12 @@ var PolicyCompileError = class extends Error {
|
|
|
29
29
|
};
|
|
30
30
|
|
|
31
31
|
// src/constants.ts
|
|
32
|
+
var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
|
|
32
33
|
var CAPABILITY_KEY_PATTERN = /^(?:org\/)?[a-z0-9-]{1,64}$/;
|
|
33
34
|
var CANONICAL_FORMAT = 2;
|
|
34
35
|
var IDENTIFIER_RE = /^[a-z][a-z0-9_]*$/;
|
|
35
36
|
var identifier = z.string().min(1).max(63).regex(IDENTIFIER_RE);
|
|
37
|
+
var columnIdentifier = z.string().min(1).max(63).regex(COLUMN_IDENTIFIER_PATTERN);
|
|
36
38
|
var COLUMN_TYPES = [
|
|
37
39
|
"uuid",
|
|
38
40
|
"text",
|
|
@@ -53,18 +55,33 @@ var canonicalEnumSchema = z.object({
|
|
|
53
55
|
var canonicalColumnSchema = z.object({
|
|
54
56
|
/** Property name in TS (camelCase); columnName is the Postgres name. */
|
|
55
57
|
name: z.string().min(1).max(63),
|
|
56
|
-
columnName:
|
|
58
|
+
columnName: columnIdentifier,
|
|
57
59
|
type: z.union([columnTypeSchema, z.object({ enum: identifier })]),
|
|
58
60
|
nullable: z.boolean().default(true),
|
|
59
61
|
unique: z.boolean().default(false),
|
|
60
62
|
/** Literal default, or one of the allowlisted SQL functions. */
|
|
61
63
|
default: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
|
|
62
64
|
defaultFn: z.enum(["gen_random_uuid()", "now()"]).optional(),
|
|
65
|
+
/**
|
|
66
|
+
* Stamp this column from the database clock on UPDATE (`now()`). The
|
|
67
|
+
* column stays insertable (defaults fill gaps) but is omitted from
|
|
68
|
+
* SetInput / settable so callers cannot backdate it.
|
|
69
|
+
*/
|
|
70
|
+
onUpdateFn: z.enum(["now()"]).optional(),
|
|
63
71
|
description: z.string().max(500).optional(),
|
|
64
72
|
/** Migration hints — same semantics as v1. */
|
|
65
|
-
renamedFrom:
|
|
73
|
+
renamedFrom: columnIdentifier.optional(),
|
|
66
74
|
cast: z.string().max(200).optional(),
|
|
67
75
|
deprecated: z.boolean().default(false)
|
|
76
|
+
}).superRefine((column, ctx) => {
|
|
77
|
+
if (column.onUpdateFn === void 0) return;
|
|
78
|
+
if (typeof column.type !== "string" || column.type !== "timestamptz" && column.type !== "date") {
|
|
79
|
+
ctx.addIssue({
|
|
80
|
+
code: "custom",
|
|
81
|
+
path: ["onUpdateFn"],
|
|
82
|
+
message: `onUpdateFn '${column.onUpdateFn}' is only valid on timestamptz/date columns`
|
|
83
|
+
});
|
|
84
|
+
}
|
|
68
85
|
});
|
|
69
86
|
var canonicalLinkSchema = z.object({
|
|
70
87
|
name: z.string().min(1).max(63),
|
|
@@ -72,15 +89,15 @@ var canonicalLinkSchema = z.object({
|
|
|
72
89
|
/** Target table name (namespace-local). Absent for toPlatformUser. */
|
|
73
90
|
target: identifier.optional(),
|
|
74
91
|
/** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
|
|
75
|
-
on:
|
|
92
|
+
on: columnIdentifier,
|
|
76
93
|
/** Referenced column on the target (default: target's PK / platform user `sub`). */
|
|
77
|
-
references:
|
|
94
|
+
references: columnIdentifier.optional(),
|
|
78
95
|
/** enforce: true emits a real FK constraint (same-namespace toOne only). */
|
|
79
96
|
enforce: z.boolean().default(false),
|
|
80
97
|
description: z.string().max(500).optional()
|
|
81
98
|
});
|
|
82
99
|
var canonicalIndexSchema = z.object({
|
|
83
|
-
columns: z.array(
|
|
100
|
+
columns: z.array(columnIdentifier).min(1),
|
|
84
101
|
unique: z.boolean().default(false),
|
|
85
102
|
name: identifier.optional()
|
|
86
103
|
});
|
|
@@ -171,6 +188,13 @@ var canonicalCowMutationSchema = z.object({
|
|
|
171
188
|
}
|
|
172
189
|
}
|
|
173
190
|
});
|
|
191
|
+
var canonicalForeignKeySchema = z.object({
|
|
192
|
+
name: identifier,
|
|
193
|
+
columns: z.array(columnIdentifier).min(1).max(8),
|
|
194
|
+
target: identifier,
|
|
195
|
+
references: z.array(columnIdentifier).min(1).max(8),
|
|
196
|
+
deferrable: z.enum(["initially-immediate", "initially-deferred"]).optional()
|
|
197
|
+
}).strict();
|
|
174
198
|
var canonicalTableSchema = z.object({
|
|
175
199
|
table: identifier,
|
|
176
200
|
description: z.string().max(2e3).optional(),
|
|
@@ -179,7 +203,7 @@ var canonicalTableSchema = z.object({
|
|
|
179
203
|
/** Synced/reflected read-only tables (`source: gold.*`) — v1 semantics. */
|
|
180
204
|
source: z.string().regex(/^gold\.[a-z][a-z0-9_]*$/).optional(),
|
|
181
205
|
columns: z.array(canonicalColumnSchema).default([]),
|
|
182
|
-
primaryKey: z.array(
|
|
206
|
+
primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
|
|
183
207
|
links: z.array(canonicalLinkSchema).default([]),
|
|
184
208
|
indexes: z.array(canonicalIndexSchema).default([]),
|
|
185
209
|
policies: canonicalPoliciesSchema.default({}),
|
|
@@ -190,14 +214,16 @@ var canonicalTableSchema = z.object({
|
|
|
190
214
|
*/
|
|
191
215
|
actions: z.array(canonicalActionSchema).default([]),
|
|
192
216
|
/** Optional (not defaulted) so pre-CoW canonical documents keep byte-identical hashes. */
|
|
193
|
-
cowMutations: z.array(canonicalCowMutationSchema).optional()
|
|
217
|
+
cowMutations: z.array(canonicalCowMutationSchema).optional(),
|
|
218
|
+
/** Omitted by old definitions, preserving their existing content hashes. */
|
|
219
|
+
foreignKeys: z.array(canonicalForeignKeySchema).max(100).optional()
|
|
194
220
|
});
|
|
195
221
|
var canonicalViewSchema = z.object({
|
|
196
222
|
view: identifier,
|
|
197
223
|
description: z.string().max(2e3).optional(),
|
|
198
224
|
columns: z.array(canonicalColumnSchema).min(1),
|
|
199
225
|
/** Logical identity for GraphQL only; PostgreSQL views have no PK constraint. */
|
|
200
|
-
primaryKey: z.array(
|
|
226
|
+
primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
|
|
201
227
|
links: z.array(canonicalLinkSchema).default([]),
|
|
202
228
|
policies: z.object({ read: policyExpressionSchema }).strict(),
|
|
203
229
|
query: z.string().min(1).max(1e5).refine(isSingleViewQuerySql, {
|
|
@@ -210,6 +236,9 @@ var canonicalViewSchema = z.object({
|
|
|
210
236
|
if (column.default !== void 0 || column.defaultFn !== void 0) {
|
|
211
237
|
ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare a default` });
|
|
212
238
|
}
|
|
239
|
+
if (column.onUpdateFn !== void 0) {
|
|
240
|
+
ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare onUpdateFn` });
|
|
241
|
+
}
|
|
213
242
|
if (column.unique) {
|
|
214
243
|
ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare unique` });
|
|
215
244
|
}
|
|
@@ -243,6 +272,45 @@ z.object({
|
|
|
243
272
|
ctx.addIssue({ code: "custom", path: ["tables", index, "table"], message: `duplicate relation '${table.table}'` });
|
|
244
273
|
}
|
|
245
274
|
seen.add(table.table);
|
|
275
|
+
if (table.source !== void 0) {
|
|
276
|
+
for (const [columnIndex, column] of table.columns.entries()) {
|
|
277
|
+
if (column.onUpdateFn !== void 0) {
|
|
278
|
+
ctx.addIssue({
|
|
279
|
+
code: "custom",
|
|
280
|
+
path: ["tables", index, "columns", columnIndex],
|
|
281
|
+
message: `synced table '${table.table}' column '${column.name}' cannot declare onUpdateFn`
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
|
|
288
|
+
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
289
|
+
const names = /* @__PURE__ */ new Set([
|
|
290
|
+
...table.primaryKey?.length ? [`${table.table}_pkey`] : [],
|
|
291
|
+
...table.columns.filter((column) => column.unique && !(table.primaryKey?.length === 1 && table.primaryKey[0] === column.columnName)).map((column) => `${table.table}_${column.columnName}_unique`),
|
|
292
|
+
...table.links.filter((link) => link.enforce).map((link) => `fk_${table.table}_${link.name}`)
|
|
293
|
+
]);
|
|
294
|
+
for (const [index, fk] of (table.foreignKeys ?? []).entries()) {
|
|
295
|
+
const fail = (message) => ctx.addIssue({ code: "custom", path: ["tables", tableIndex, "foreignKeys", index], message });
|
|
296
|
+
if (names.has(fk.name)) fail(`duplicate foreign key '${fk.name}'`);
|
|
297
|
+
names.add(fk.name);
|
|
298
|
+
const target = fkTables.get(fk.target);
|
|
299
|
+
if (table.source || !target || target.source) {
|
|
300
|
+
fail("foreign keys must connect owned tables in the same namespace");
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (fk.columns.length !== fk.references.length) fail("foreign key columns and references must have the same number of columns");
|
|
304
|
+
if (new Set(fk.columns).size !== fk.columns.length || new Set(fk.references).size !== fk.references.length) fail("foreign key columns must not repeat");
|
|
305
|
+
for (const [position, name] of fk.columns.entries()) {
|
|
306
|
+
const sourceColumn = table.columns.find((column) => column.columnName === name);
|
|
307
|
+
const targetColumn = target.columns.find((column) => column.columnName === fk.references[position]);
|
|
308
|
+
if (!sourceColumn || !targetColumn) fail("foreign key references an unknown column");
|
|
309
|
+
else if (JSON.stringify(sourceColumn.type) !== JSON.stringify(targetColumn.type)) fail("foreign key column types must match");
|
|
310
|
+
}
|
|
311
|
+
const uniqueKeys = [target.primaryKey ?? [], ...target.indexes.filter((index2) => index2.unique).map((index2) => index2.columns), ...target.columns.filter((column) => column.unique).map((column) => [column.columnName])];
|
|
312
|
+
if (!uniqueKeys.some((key) => key.length === fk.references.length && key.every((column) => fk.references.includes(column)))) fail("foreign key target columns must form a unique key");
|
|
313
|
+
}
|
|
246
314
|
}
|
|
247
315
|
const tableByName = new Map(definition.tables.map((table) => [table.table, table]));
|
|
248
316
|
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
@@ -361,7 +429,10 @@ z.object({
|
|
|
361
429
|
});
|
|
362
430
|
|
|
363
431
|
// src/ir/index.ts
|
|
364
|
-
var IR_VERSION =
|
|
432
|
+
var IR_VERSION = 2;
|
|
433
|
+
var LEGACY_IR_VERSION = 1;
|
|
434
|
+
var irVersionSchema = z.union([z.literal(LEGACY_IR_VERSION), z.literal(IR_VERSION)]);
|
|
435
|
+
z.object({ irVersion: irVersionSchema });
|
|
365
436
|
var CLAIMS_CONTRACT = {
|
|
366
437
|
sub: "string",
|
|
367
438
|
org: "string?",
|
|
@@ -488,6 +559,33 @@ var aggregateRootSchema = z.object({
|
|
|
488
559
|
nodes: selectionIrSchema.optional(),
|
|
489
560
|
includeIf: includeIfSchema.optional()
|
|
490
561
|
});
|
|
562
|
+
var DATE_BUCKETS = ["year", "quarter", "month", "week", "day", "hour"];
|
|
563
|
+
var groupByKeyIrSchema = z.object({
|
|
564
|
+
column: z.string(),
|
|
565
|
+
bucket: z.enum(DATE_BUCKETS).optional()
|
|
566
|
+
});
|
|
567
|
+
var aggregateGroupsRootSchema = z.object({
|
|
568
|
+
kind: z.literal("aggregateGroups"),
|
|
569
|
+
alias: z.string(),
|
|
570
|
+
table: z.string(),
|
|
571
|
+
/** Composed at compile time: AND(policy, documentWhere?, dynamicFilter?). */
|
|
572
|
+
where: predIrSchema,
|
|
573
|
+
groupBy: z.array(groupByKeyIrSchema).min(1),
|
|
574
|
+
/**
|
|
575
|
+
* The `key { … }` projection: which group columns appear in each entry's key
|
|
576
|
+
* object, in selection order. A subset of `groupBy` (the compiler rejects a
|
|
577
|
+
* key field that is not a group key — it would be indeterminate within the
|
|
578
|
+
* group). Empty when the document selects only `aggregate`.
|
|
579
|
+
*/
|
|
580
|
+
key: z.array(z.object({ alias: z.string(), column: z.string() })).default([]),
|
|
581
|
+
count: z.object({ columns: z.array(z.string()).optional(), distinct: z.boolean().optional() }).optional(),
|
|
582
|
+
sum: z.array(z.string()).optional(),
|
|
583
|
+
avg: z.array(z.string()).optional(),
|
|
584
|
+
/** Bounds the GROUP count, not the scanned rows — see MAX_GROUPS. */
|
|
585
|
+
limit: limitIrSchema,
|
|
586
|
+
offset: z.object({ variable: z.string().optional(), value: z.number().int().optional() }).optional(),
|
|
587
|
+
includeIf: includeIfSchema.optional()
|
|
588
|
+
});
|
|
491
589
|
var writeValuesSchema = z.union([
|
|
492
590
|
z.object({ slot: z.string() }),
|
|
493
591
|
z.object({ entries: z.array(z.object({ column: z.string(), value: valueIrSchema })) })
|
|
@@ -533,12 +631,15 @@ var mutationRootSchema = z.object({
|
|
|
533
631
|
insertCheck: predIrSchema.optional(),
|
|
534
632
|
returning: selectionIrSchema.nullable(),
|
|
535
633
|
affectedRows: z.boolean().default(false),
|
|
634
|
+
/** v2: abort the transaction when the actual write count differs, regardless of selection. */
|
|
635
|
+
assertAffectedRows: z.number().int().min(0).max(2147483647).optional(),
|
|
536
636
|
includeIf: includeIfSchema.optional()
|
|
537
637
|
});
|
|
538
638
|
var rootIrSchema = z.discriminatedUnion("kind", [
|
|
539
639
|
selectRootSchema,
|
|
540
640
|
byPkRootSchema,
|
|
541
641
|
aggregateRootSchema,
|
|
642
|
+
aggregateGroupsRootSchema,
|
|
542
643
|
mutationRootSchema
|
|
543
644
|
]);
|
|
544
645
|
var INPUT_REF_KINDS = [
|
|
@@ -568,7 +669,7 @@ var variableIrSchema = z.object({
|
|
|
568
669
|
defaultValue: variableDefaultIrSchema.optional()
|
|
569
670
|
});
|
|
570
671
|
var compiledOperationSchema = z.object({
|
|
571
|
-
irVersion:
|
|
672
|
+
irVersion: irVersionSchema,
|
|
572
673
|
name: z.string(),
|
|
573
674
|
opHash: z.string().regex(/^sha256:[0-9a-f]{64}$/),
|
|
574
675
|
kind: z.enum(["query", "mutation"]),
|
|
@@ -580,6 +681,13 @@ var compiledOperationSchema = z.object({
|
|
|
580
681
|
/** Every table the op touches — data-token scoping + audit. */
|
|
581
682
|
tableSet: z.array(z.string()).min(1),
|
|
582
683
|
roots: z.array(rootIrSchema).min(1)
|
|
684
|
+
}).superRefine((operation, ctx) => {
|
|
685
|
+
for (const [index, root] of operation.roots.entries()) {
|
|
686
|
+
if (root.kind !== "mutation" || root.assertAffectedRows === void 0) continue;
|
|
687
|
+
if (operation.irVersion < 2 || operation.kind !== "mutation" || root.includeIf !== void 0) {
|
|
688
|
+
ctx.addIssue({ code: "custom", path: ["roots", index], message: "affected-row assertions require unconditional mutation roots and IR v2" });
|
|
689
|
+
}
|
|
690
|
+
}
|
|
583
691
|
});
|
|
584
692
|
var tableMetaIrSchema = z.object({
|
|
585
693
|
table: z.string(),
|
|
@@ -616,18 +724,29 @@ var tableMetaIrSchema = z.object({
|
|
|
616
724
|
insertable: z.array(z.string()),
|
|
617
725
|
settable: z.array(z.string()),
|
|
618
726
|
appendable: z.array(z.string()),
|
|
727
|
+
/**
|
|
728
|
+
* Logical columns stamped with Postgres `now()` on UPDATE (canonical
|
|
729
|
+
* `onUpdateFn`). Omitted when empty so pre-onUpdate artifacts still parse.
|
|
730
|
+
*/
|
|
731
|
+
touchOnUpdate: z.array(z.string()).optional(),
|
|
619
732
|
constraints: z.array(z.object({ name: z.string(), columns: z.array(z.string()) })),
|
|
620
733
|
/** Depth/node caps for dynamic filter trees on this table. */
|
|
621
734
|
dynamicFilterMaxDepth: z.number().int().positive().default(6),
|
|
622
735
|
dynamicFilterMaxNodes: z.number().int().positive().default(64)
|
|
623
736
|
});
|
|
624
737
|
z.object({
|
|
625
|
-
irVersion:
|
|
738
|
+
irVersion: irVersionSchema,
|
|
626
739
|
namespace: z.string(),
|
|
627
740
|
schemaHash: z.string(),
|
|
628
741
|
opsHash: z.string(),
|
|
629
742
|
tables: z.record(z.string(), tableMetaIrSchema),
|
|
630
743
|
operations: z.record(z.string(), compiledOperationSchema)
|
|
744
|
+
}).superRefine((artifact, ctx) => {
|
|
745
|
+
for (const [name, operation] of Object.entries(artifact.operations)) {
|
|
746
|
+
if (operation.irVersion > artifact.irVersion) {
|
|
747
|
+
ctx.addIssue({ code: "custom", path: ["operations", name, "irVersion"], message: "operation requires a newer IR version than its artifact" });
|
|
748
|
+
}
|
|
749
|
+
}
|
|
631
750
|
});
|
|
632
751
|
|
|
633
752
|
// src/predicate-v2/parse.ts
|
|
@@ -748,6 +867,10 @@ var Parser = class {
|
|
|
748
867
|
this.expect("rparen", "')'");
|
|
749
868
|
return inner;
|
|
750
869
|
}
|
|
870
|
+
if (this.peekKeyword("true") || this.peekKeyword("false")) {
|
|
871
|
+
const token = this.advance();
|
|
872
|
+
return { kind: "literal", value: token.value === "true" };
|
|
873
|
+
}
|
|
751
874
|
if (this.peekKeyword("capability") || this.peekKeyword("role")) {
|
|
752
875
|
const fn = this.peek().value;
|
|
753
876
|
const node = this.parseBooleanCall(fn);
|
|
@@ -931,6 +1054,8 @@ var PolicyCompiler = class {
|
|
|
931
1054
|
}
|
|
932
1055
|
compile(node, table, depth) {
|
|
933
1056
|
switch (node.kind) {
|
|
1057
|
+
case "literal":
|
|
1058
|
+
return { kind: "const", value: node.value };
|
|
934
1059
|
case "and":
|
|
935
1060
|
case "or":
|
|
936
1061
|
return { kind: node.kind, operands: node.operands.map((operand) => this.compile(operand, table, depth)) };
|