@sequenceholdings/orm 0.1.1 → 0.1.3
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 +12 -2
- package/dist/authoring.cjs +1801 -0
- package/dist/authoring.cjs.map +1 -0
- package/dist/authoring.d.cts +539 -0
- package/dist/authoring.d.ts +539 -0
- package/dist/authoring.js +1759 -0
- package/dist/authoring.js.map +1 -0
- package/dist/canonical.cjs +552 -0
- package/dist/canonical.cjs.map +1 -0
- package/dist/canonical.d.cts +603 -0
- package/dist/canonical.d.ts +603 -0
- package/dist/canonical.js +532 -0
- package/dist/canonical.js.map +1 -0
- package/dist/cli.cjs +7843 -1815
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +8 -1
- package/dist/cli.d.ts +8 -1
- package/dist/cli.js +7854 -1828
- package/dist/cli.js.map +1 -1
- package/dist/client.cjs +120 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +74 -2
- package/dist/client.d.ts +74 -2
- package/dist/client.js +116 -1
- package/dist/client.js.map +1 -1
- package/dist/compiler.cjs +3453 -0
- package/dist/compiler.cjs.map +1 -0
- package/dist/compiler.d.cts +389 -0
- package/dist/compiler.d.ts +389 -0
- package/dist/compiler.js +3396 -0
- package/dist/compiler.js.map +1 -0
- package/dist/drizzle.cjs +14 -0
- package/dist/drizzle.cjs.map +1 -0
- package/dist/drizzle.d.cts +1 -0
- package/dist/drizzle.d.ts +1 -0
- package/dist/drizzle.js +3 -0
- package/dist/drizzle.js.map +1 -0
- package/dist/dynamic-input.cjs +391 -0
- package/dist/dynamic-input.cjs.map +1 -0
- package/dist/dynamic-input.d.cts +111 -0
- package/dist/dynamic-input.d.ts +111 -0
- package/dist/dynamic-input.js +380 -0
- package/dist/dynamic-input.js.map +1 -0
- package/dist/index.cjs +3727 -344
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +236 -59
- package/dist/index.d.ts +236 -59
- package/dist/index.js +3757 -482
- package/dist/index.js.map +1 -1
- package/dist/ir.cjs +642 -0
- package/dist/ir.cjs.map +1 -0
- package/dist/ir.d.cts +1119 -0
- package/dist/ir.d.ts +1119 -0
- package/dist/ir.js +623 -0
- package/dist/ir.js.map +1 -0
- package/dist/naming-CTvWSRTf.d.ts +79 -0
- package/dist/naming-DRyinU4u.d.cts +79 -0
- package/dist/predicate-v2.cjs +1093 -0
- package/dist/predicate-v2.cjs.map +1 -0
- package/dist/predicate-v2.d.cts +139 -0
- package/dist/predicate-v2.d.ts +139 -0
- package/dist/predicate-v2.js +1083 -0
- package/dist/predicate-v2.js.map +1 -0
- package/dist/sdl.cjs +1122 -0
- package/dist/sdl.cjs.map +1 -0
- package/dist/sdl.d.cts +94 -0
- package/dist/sdl.d.ts +94 -0
- package/dist/sdl.js +1098 -0
- package/dist/sdl.js.map +1 -0
- package/package.json +51 -3
- package/src/codegen-gql/apollo-hooks.cjs +98 -0
- package/src/codegen-gql/server-fns.cjs +101 -0
- package/templates/README.md +13 -0
- package/templates/orm-namespace/CLAUDE.md +82 -0
- package/templates/orm-namespace/codegen.ts +22 -0
- package/templates/orm-namespace/gitignore +5 -0
- package/templates/orm-namespace/graphql/example/queries.ts +56 -0
- package/templates/orm-namespace/package.json +24 -0
- package/templates/orm-namespace/schema/tables.ts +71 -0
- package/templates/orm-namespace/sequence.config.ts +32 -0
- package/templates/orm-namespace/tsconfig.json +15 -0
package/dist/ir.js
ADDED
|
@@ -0,0 +1,623 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// src/ir/index.ts
|
|
4
|
+
var CANONICAL_FORMAT = 2;
|
|
5
|
+
var IDENTIFIER_RE = /^[a-z][a-z0-9_]*$/;
|
|
6
|
+
var identifier = z.string().min(1).max(63).regex(IDENTIFIER_RE);
|
|
7
|
+
var COLUMN_TYPES = [
|
|
8
|
+
"uuid",
|
|
9
|
+
"text",
|
|
10
|
+
"integer",
|
|
11
|
+
"bigint",
|
|
12
|
+
"numeric",
|
|
13
|
+
"double",
|
|
14
|
+
"boolean",
|
|
15
|
+
"timestamptz",
|
|
16
|
+
"date",
|
|
17
|
+
"jsonb"
|
|
18
|
+
];
|
|
19
|
+
var columnTypeSchema = z.enum(COLUMN_TYPES);
|
|
20
|
+
var canonicalEnumSchema = z.object({
|
|
21
|
+
name: identifier,
|
|
22
|
+
values: z.array(z.string().min(1).max(63)).min(1).max(100)
|
|
23
|
+
});
|
|
24
|
+
var canonicalColumnSchema = z.object({
|
|
25
|
+
/** Property name in TS (camelCase); columnName is the Postgres name. */
|
|
26
|
+
name: z.string().min(1).max(63),
|
|
27
|
+
columnName: identifier,
|
|
28
|
+
type: z.union([columnTypeSchema, z.object({ enum: identifier })]),
|
|
29
|
+
nullable: z.boolean().default(true),
|
|
30
|
+
unique: z.boolean().default(false),
|
|
31
|
+
/** Literal default, or one of the allowlisted SQL functions. */
|
|
32
|
+
default: z.union([z.string(), z.number(), z.boolean(), z.null()]).optional(),
|
|
33
|
+
defaultFn: z.enum(["gen_random_uuid()", "now()"]).optional(),
|
|
34
|
+
description: z.string().max(500).optional(),
|
|
35
|
+
/** Migration hints — same semantics as v1. */
|
|
36
|
+
renamedFrom: identifier.optional(),
|
|
37
|
+
cast: z.string().max(200).optional(),
|
|
38
|
+
deprecated: z.boolean().default(false)
|
|
39
|
+
});
|
|
40
|
+
var canonicalLinkSchema = z.object({
|
|
41
|
+
name: z.string().min(1).max(63),
|
|
42
|
+
kind: z.enum(["toOne", "toMany", "toPlatformUser"]),
|
|
43
|
+
/** Target table name (namespace-local). Absent for toPlatformUser. */
|
|
44
|
+
target: identifier.optional(),
|
|
45
|
+
/** FK column on the owning side (toOne/toPlatformUser: on this table; toMany: on the target). */
|
|
46
|
+
on: identifier,
|
|
47
|
+
/** Referenced column on the target (default: target's PK / platform user `sub`). */
|
|
48
|
+
references: identifier.optional(),
|
|
49
|
+
/** enforce: true emits a real FK constraint (same-namespace toOne only). */
|
|
50
|
+
enforce: z.boolean().default(false),
|
|
51
|
+
description: z.string().max(500).optional()
|
|
52
|
+
});
|
|
53
|
+
var canonicalIndexSchema = z.object({
|
|
54
|
+
columns: z.array(identifier).min(1),
|
|
55
|
+
unique: z.boolean().default(false),
|
|
56
|
+
name: identifier.optional()
|
|
57
|
+
});
|
|
58
|
+
var policyVariantSchema = z.object({
|
|
59
|
+
/** Claims-only guard (capability()/role()/claim() comparisons — no columns). */
|
|
60
|
+
when: z.string().min(1).max(1e3),
|
|
61
|
+
/** Exactly one of: allow-all bypass, or a row predicate. */
|
|
62
|
+
allow: z.literal(true).optional(),
|
|
63
|
+
policy: z.string().min(1).max(2e3).optional()
|
|
64
|
+
}).refine((v) => v.allow === true !== (typeof v.policy === "string"), {
|
|
65
|
+
message: "A variant declares exactly one of allow: true or policy"
|
|
66
|
+
});
|
|
67
|
+
var policyExpressionSchema = z.union([
|
|
68
|
+
z.string().min(1).max(2e3),
|
|
69
|
+
z.object({
|
|
70
|
+
default: z.string().min(1).max(2e3),
|
|
71
|
+
variants: z.array(policyVariantSchema).max(10).default([])
|
|
72
|
+
})
|
|
73
|
+
]);
|
|
74
|
+
var canonicalPoliciesSchema = z.object({
|
|
75
|
+
read: policyExpressionSchema.optional(),
|
|
76
|
+
write: policyExpressionSchema.optional(),
|
|
77
|
+
insert: policyExpressionSchema.optional(),
|
|
78
|
+
update: policyExpressionSchema.optional()
|
|
79
|
+
});
|
|
80
|
+
function isSingleViewQuerySql(value) {
|
|
81
|
+
const query = value.trim();
|
|
82
|
+
if (!/^\(*(?:select|with)\b/i.test(query) || query.includes("\0")) return false;
|
|
83
|
+
let state = "normal";
|
|
84
|
+
for (let index = 0; index < query.length; index += 1) {
|
|
85
|
+
const char = query[index];
|
|
86
|
+
const next = query[index + 1];
|
|
87
|
+
if (state === "single") {
|
|
88
|
+
if (char === "'" && next === "'") index += 1;
|
|
89
|
+
else if (char === "'") state = "normal";
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (state === "double") {
|
|
93
|
+
if (char === '"' && next === '"') index += 1;
|
|
94
|
+
else if (char === '"') state = "normal";
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (char === "'") state = "single";
|
|
98
|
+
else if (char === '"') state = "double";
|
|
99
|
+
else if (char === ";" || char === "-" && next === "-" || char === "/" && next === "*") return false;
|
|
100
|
+
}
|
|
101
|
+
return state === "normal" && !/\$[a-z_0-9]*\$/i.test(query);
|
|
102
|
+
}
|
|
103
|
+
var canonicalActionSchema = z.object({
|
|
104
|
+
name: identifier,
|
|
105
|
+
requires: z.string().max(1e3).optional(),
|
|
106
|
+
params: z.record(z.string(), z.object({
|
|
107
|
+
type: columnTypeSchema,
|
|
108
|
+
nullable: z.boolean().default(false)
|
|
109
|
+
})).default({}),
|
|
110
|
+
executes: z.string().min(1).max(1e4),
|
|
111
|
+
returns: z.enum(["rows", "rowsAffected", "scalar"]).default("rowsAffected"),
|
|
112
|
+
audit: z.object({ action: z.string(), resource: z.string() }).optional()
|
|
113
|
+
});
|
|
114
|
+
var canonicalCowMutationSchema = z.object({
|
|
115
|
+
kind: z.enum(["override", "revert"]),
|
|
116
|
+
name: identifier,
|
|
117
|
+
/** Synced table (same namespace) the overlay copies from / falls back to. */
|
|
118
|
+
synced: identifier,
|
|
119
|
+
/** Single-column primary key PROPERTY (TS camelCase, like column `name`) shared by overlay and synced. */
|
|
120
|
+
pk: z.string().min(1).max(63),
|
|
121
|
+
/** Override: contract properties copied INSERT…SELECT from synced (pk included). Empty for revert. */
|
|
122
|
+
copy: z.array(z.string().min(1).max(63)).default([]),
|
|
123
|
+
/** Override: overlay properties written by the UPDATE (never the pk). Empty for revert. */
|
|
124
|
+
set: z.array(z.string().min(1).max(63)).default([])
|
|
125
|
+
}).superRefine((mutation, ctx) => {
|
|
126
|
+
if (mutation.kind === "override") {
|
|
127
|
+
if (mutation.set.length === 0) {
|
|
128
|
+
ctx.addIssue({ code: "custom", path: ["set"], message: "override mutations must set at least one column" });
|
|
129
|
+
}
|
|
130
|
+
if (!mutation.copy.includes(mutation.pk)) {
|
|
131
|
+
ctx.addIssue({ code: "custom", path: ["copy"], message: "override copy list must include the pk" });
|
|
132
|
+
}
|
|
133
|
+
} else if (mutation.set.length > 0 || mutation.copy.length > 0) {
|
|
134
|
+
ctx.addIssue({ code: "custom", path: ["set"], message: "revert mutations take no copy/set columns" });
|
|
135
|
+
}
|
|
136
|
+
if (mutation.set.includes(mutation.pk)) {
|
|
137
|
+
ctx.addIssue({ code: "custom", path: ["set"], message: "set must not include the pk \u2014 identity columns are pinned" });
|
|
138
|
+
}
|
|
139
|
+
for (const [field, values] of [["copy", mutation.copy], ["set", mutation.set]]) {
|
|
140
|
+
if (new Set(values).size !== values.length) {
|
|
141
|
+
ctx.addIssue({ code: "custom", path: [field], message: `${field} must not contain duplicate columns` });
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
var canonicalTableSchema = z.object({
|
|
146
|
+
table: identifier,
|
|
147
|
+
description: z.string().max(2e3).optional(),
|
|
148
|
+
/** Table-level rename hint (new in v2). */
|
|
149
|
+
renamedFrom: identifier.optional(),
|
|
150
|
+
/** Synced/reflected read-only tables (`source: gold.*`) — v1 semantics. */
|
|
151
|
+
source: z.string().regex(/^gold\.[a-z][a-z0-9_]*$/).optional(),
|
|
152
|
+
columns: z.array(canonicalColumnSchema).default([]),
|
|
153
|
+
primaryKey: z.array(identifier).min(1).max(8).optional(),
|
|
154
|
+
links: z.array(canonicalLinkSchema).default([]),
|
|
155
|
+
indexes: z.array(canonicalIndexSchema).default([]),
|
|
156
|
+
policies: canonicalPoliciesSchema.default({}),
|
|
157
|
+
/**
|
|
158
|
+
* v1-conversion carry-over only. v2 has no action surface — the runtime
|
|
159
|
+
* neither compiles nor executes these; writes are mutations. Kept parseable
|
|
160
|
+
* so stored definitions and their content hashes stay stable.
|
|
161
|
+
*/
|
|
162
|
+
actions: z.array(canonicalActionSchema).default([]),
|
|
163
|
+
/** Optional (not defaulted) so pre-CoW canonical documents keep byte-identical hashes. */
|
|
164
|
+
cowMutations: z.array(canonicalCowMutationSchema).optional()
|
|
165
|
+
});
|
|
166
|
+
var canonicalViewSchema = z.object({
|
|
167
|
+
view: identifier,
|
|
168
|
+
description: z.string().max(2e3).optional(),
|
|
169
|
+
columns: z.array(canonicalColumnSchema).min(1),
|
|
170
|
+
/** Logical identity for GraphQL only; PostgreSQL views have no PK constraint. */
|
|
171
|
+
primaryKey: z.array(identifier).min(1).max(8).optional(),
|
|
172
|
+
links: z.array(canonicalLinkSchema).default([]),
|
|
173
|
+
policies: z.object({ read: policyExpressionSchema }).strict(),
|
|
174
|
+
query: z.string().min(1).max(1e5).refine(isSingleViewQuerySql, {
|
|
175
|
+
message: "view query must be one SELECT/WITH statement without comments or dollar-quoted bodies"
|
|
176
|
+
}),
|
|
177
|
+
/** Namespace-local logical relation names, sorted for deterministic hashing. */
|
|
178
|
+
dependencies: z.array(identifier).max(500)
|
|
179
|
+
}).superRefine((view, ctx) => {
|
|
180
|
+
for (const [index, column] of view.columns.entries()) {
|
|
181
|
+
if (column.default !== void 0 || column.defaultFn !== void 0) {
|
|
182
|
+
ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare a default` });
|
|
183
|
+
}
|
|
184
|
+
if (column.unique) {
|
|
185
|
+
ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare unique` });
|
|
186
|
+
}
|
|
187
|
+
if (column.renamedFrom !== void 0 || column.cast !== void 0) {
|
|
188
|
+
ctx.addIssue({ code: "custom", path: ["columns", index], message: `view column '${column.name}' cannot declare migration hints` });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
for (const [index, link] of view.links.entries()) {
|
|
192
|
+
if (link.enforce) {
|
|
193
|
+
ctx.addIssue({ code: "custom", path: ["links", index, "enforce"], message: `view link '${link.name}' cannot enforce a foreign key` });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
z.object({
|
|
198
|
+
definition_format: z.literal(CANONICAL_FORMAT),
|
|
199
|
+
namespace: identifier.max(41),
|
|
200
|
+
description: z.string().max(2e3).optional(),
|
|
201
|
+
enums: z.array(canonicalEnumSchema).default([]),
|
|
202
|
+
tables: z.array(canonicalTableSchema).min(0).max(500),
|
|
203
|
+
/** Optional to preserve byte-identical hashes for pre-view canonical v2 documents. */
|
|
204
|
+
views: z.array(canonicalViewSchema).max(500).optional(),
|
|
205
|
+
/** Verbatim custom-roles grammar (@sequenceholdings/custom-roles-manifest). */
|
|
206
|
+
capabilities: z.object({
|
|
207
|
+
roles: z.unknown().optional(),
|
|
208
|
+
uses: z.array(z.string()).default([])
|
|
209
|
+
}).default({ uses: [] })
|
|
210
|
+
}).superRefine((definition, ctx) => {
|
|
211
|
+
const seen = /* @__PURE__ */ new Set();
|
|
212
|
+
for (const [index, table] of definition.tables.entries()) {
|
|
213
|
+
if (seen.has(table.table)) {
|
|
214
|
+
ctx.addIssue({ code: "custom", path: ["tables", index, "table"], message: `duplicate relation '${table.table}'` });
|
|
215
|
+
}
|
|
216
|
+
seen.add(table.table);
|
|
217
|
+
}
|
|
218
|
+
const tableByName = new Map(definition.tables.map((table) => [table.table, table]));
|
|
219
|
+
for (const [tableIndex, table] of definition.tables.entries()) {
|
|
220
|
+
const cowMutations = table.cowMutations ?? [];
|
|
221
|
+
if (cowMutations.length === 0) continue;
|
|
222
|
+
const path = (index, ...rest) => ["tables", tableIndex, "cowMutations", index, ...rest];
|
|
223
|
+
if (table.source !== void 0) {
|
|
224
|
+
ctx.addIssue({ code: "custom", path: path(0), message: `synced table '${table.table}' cannot declare cow mutations \u2014 it is read-only` });
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
for (const [index, mutation] of cowMutations.entries()) {
|
|
228
|
+
const synced = tableByName.get(mutation.synced);
|
|
229
|
+
if (!synced || synced.source === void 0) {
|
|
230
|
+
ctx.addIssue({
|
|
231
|
+
code: "custom",
|
|
232
|
+
path: path(index, "synced"),
|
|
233
|
+
message: `cow mutation '${mutation.name}' references '${mutation.synced}', which is not a synced table in this namespace`
|
|
234
|
+
});
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const overlayColumnsByName = new Map(table.columns.map((column) => [column.name, column]));
|
|
238
|
+
const syncedColumnsByName = new Map(synced.columns.map((column) => [column.name, column]));
|
|
239
|
+
let columnsExist = true;
|
|
240
|
+
for (const property of [mutation.pk, ...mutation.copy, ...mutation.set]) {
|
|
241
|
+
if (!overlayColumnsByName.has(property)) {
|
|
242
|
+
columnsExist = false;
|
|
243
|
+
ctx.addIssue({ code: "custom", path: path(index), message: `cow mutation '${mutation.name}': column '${property}' does not exist on overlay '${table.table}'` });
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
for (const property of [mutation.pk, ...mutation.copy]) {
|
|
247
|
+
if (!syncedColumnsByName.has(property)) {
|
|
248
|
+
columnsExist = false;
|
|
249
|
+
ctx.addIssue({ code: "custom", path: path(index), message: `cow mutation '${mutation.name}': column '${property}' does not exist on synced '${mutation.synced}'` });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (!columnsExist) continue;
|
|
253
|
+
const overlayPk = table.primaryKey ?? [];
|
|
254
|
+
const syncedPk = synced.primaryKey ?? [];
|
|
255
|
+
if (overlayPk.length !== 1 || overlayPk[0] !== mutation.pk || syncedPk.length !== 1 || syncedPk[0] !== mutation.pk) {
|
|
256
|
+
ctx.addIssue({
|
|
257
|
+
code: "custom",
|
|
258
|
+
path: path(index, "pk"),
|
|
259
|
+
message: `cow mutation '${mutation.name}': pk '${mutation.pk}' must be the single-column primary key on both '${table.table}' and '${mutation.synced}'`
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
for (const property of [mutation.pk, ...mutation.copy]) {
|
|
263
|
+
const overlayColumn = overlayColumnsByName.get(property);
|
|
264
|
+
const syncedColumn = syncedColumnsByName.get(property);
|
|
265
|
+
if (!overlayColumn || !syncedColumn) continue;
|
|
266
|
+
if (JSON.stringify(overlayColumn.type) !== JSON.stringify(syncedColumn.type)) {
|
|
267
|
+
ctx.addIssue({
|
|
268
|
+
code: "custom",
|
|
269
|
+
path: path(index),
|
|
270
|
+
message: `cow mutation '${mutation.name}': column '${property}' must have the same type on overlay and synced`
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
if (overlayColumn.columnName !== syncedColumn.columnName) {
|
|
274
|
+
ctx.addIssue({
|
|
275
|
+
code: "custom",
|
|
276
|
+
path: path(index),
|
|
277
|
+
message: `cow mutation '${mutation.name}': column '${property}' maps to different Postgres names on overlay ('${overlayColumn.columnName}') and synced ('${syncedColumn.columnName}')`
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
if (syncedColumn.nullable !== false && overlayColumn.nullable === false) {
|
|
281
|
+
ctx.addIssue({
|
|
282
|
+
code: "custom",
|
|
283
|
+
path: path(index),
|
|
284
|
+
message: `cow mutation '${mutation.name}': column '${property}' is nullable on synced but NOT NULL on overlay \u2014 a NULL synced value would fail the copy`
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
for (const [index, view] of (definition.views ?? []).entries()) {
|
|
291
|
+
if (seen.has(view.view)) {
|
|
292
|
+
ctx.addIssue({ code: "custom", path: ["views", index, "view"], message: `duplicate relation '${view.view}'` });
|
|
293
|
+
}
|
|
294
|
+
seen.add(view.view);
|
|
295
|
+
}
|
|
296
|
+
const views = definition.views ?? [];
|
|
297
|
+
const viewByName = new Map(views.map((view) => [view.view, view]));
|
|
298
|
+
for (const [viewIndex, view] of views.entries()) {
|
|
299
|
+
for (const [dependencyIndex, dependency] of view.dependencies.entries()) {
|
|
300
|
+
if (!seen.has(dependency)) {
|
|
301
|
+
ctx.addIssue({
|
|
302
|
+
code: "custom",
|
|
303
|
+
path: ["views", viewIndex, "dependencies", dependencyIndex],
|
|
304
|
+
message: `view '${view.view}' depends on unknown relation '${dependency}'`
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
const visiting = /* @__PURE__ */ new Set();
|
|
310
|
+
const visited = /* @__PURE__ */ new Set();
|
|
311
|
+
let cycleReported = false;
|
|
312
|
+
const visit = (name, path) => {
|
|
313
|
+
if (visited.has(name) || cycleReported) return;
|
|
314
|
+
if (visiting.has(name)) {
|
|
315
|
+
cycleReported = true;
|
|
316
|
+
ctx.addIssue({
|
|
317
|
+
code: "custom",
|
|
318
|
+
path: ["views"],
|
|
319
|
+
message: `view dependencies form a cycle: ${[...path, name].join(" -> ")}`
|
|
320
|
+
});
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
visiting.add(name);
|
|
324
|
+
const view = viewByName.get(name);
|
|
325
|
+
for (const dependency of view?.dependencies ?? []) {
|
|
326
|
+
if (viewByName.has(dependency)) visit(dependency, [...path, name]);
|
|
327
|
+
}
|
|
328
|
+
visiting.delete(name);
|
|
329
|
+
visited.add(name);
|
|
330
|
+
};
|
|
331
|
+
for (const view of views) visit(view.view, []);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
// src/ir/index.ts
|
|
335
|
+
var IR_VERSION = 1;
|
|
336
|
+
var CLAIMS_CONTRACT = {
|
|
337
|
+
sub: "string",
|
|
338
|
+
org: "string?",
|
|
339
|
+
roles: "string[]",
|
|
340
|
+
capabilities: "string[]"
|
|
341
|
+
};
|
|
342
|
+
var scalarLiteral = z.union([z.string(), z.number(), z.boolean(), z.null()]);
|
|
343
|
+
var valueIrSchema = z.discriminatedUnion("kind", [
|
|
344
|
+
z.object({ kind: z.literal("literal"), value: z.union([scalarLiteral, z.array(scalarLiteral)]) }),
|
|
345
|
+
z.object({ kind: z.literal("variable"), name: z.string() }),
|
|
346
|
+
z.object({ kind: z.literal("claim"), key: z.enum(["sub", "org"]) })
|
|
347
|
+
]);
|
|
348
|
+
var CMP_OPS = [
|
|
349
|
+
"eq",
|
|
350
|
+
"neq",
|
|
351
|
+
"gt",
|
|
352
|
+
"gte",
|
|
353
|
+
"lt",
|
|
354
|
+
"lte",
|
|
355
|
+
"in",
|
|
356
|
+
"nin",
|
|
357
|
+
"like",
|
|
358
|
+
"ilike",
|
|
359
|
+
"isNull",
|
|
360
|
+
"contains",
|
|
361
|
+
"hasKey"
|
|
362
|
+
];
|
|
363
|
+
var predIrSchema = z.lazy(
|
|
364
|
+
() => z.discriminatedUnion("kind", [
|
|
365
|
+
z.object({ kind: z.literal("and"), operands: z.array(predIrSchema).min(1) }),
|
|
366
|
+
z.object({ kind: z.literal("or"), operands: z.array(predIrSchema).min(1) }),
|
|
367
|
+
z.object({ kind: z.literal("not"), operand: predIrSchema }),
|
|
368
|
+
z.object({ kind: z.literal("cmp"), op: z.enum(CMP_OPS), column: z.string(), value: valueIrSchema }),
|
|
369
|
+
z.object({ kind: z.literal("exists"), link: z.string(), where: predIrSchema }),
|
|
370
|
+
z.object({
|
|
371
|
+
kind: z.literal("aggPredicate"),
|
|
372
|
+
link: z.string(),
|
|
373
|
+
fn: z.literal("count"),
|
|
374
|
+
op: z.enum(["eq", "neq", "gt", "gte", "lt", "lte"]),
|
|
375
|
+
value: z.number(),
|
|
376
|
+
where: predIrSchema.optional()
|
|
377
|
+
}),
|
|
378
|
+
z.object({ kind: z.literal("capability"), key: z.string() }),
|
|
379
|
+
z.object({ kind: z.literal("role"), key: z.string() }),
|
|
380
|
+
z.object({ kind: z.literal("dynamicFilter"), slot: z.string() }),
|
|
381
|
+
z.object({ kind: z.literal("const"), value: z.boolean() })
|
|
382
|
+
])
|
|
383
|
+
);
|
|
384
|
+
var orderIrSchema = z.discriminatedUnion("kind", [
|
|
385
|
+
z.object({
|
|
386
|
+
kind: z.literal("column"),
|
|
387
|
+
column: z.string(),
|
|
388
|
+
/** Order through a to-one link: `link` is set and `column` is on the target. */
|
|
389
|
+
link: z.string().optional(),
|
|
390
|
+
direction: z.enum(["asc", "desc"]),
|
|
391
|
+
nulls: z.enum(["first", "last"]).optional()
|
|
392
|
+
}),
|
|
393
|
+
/** §4f: `$orderBy: [FooOrderBy!]` variable — validated + lowered at runtime. */
|
|
394
|
+
z.object({ kind: z.literal("dynamic"), slot: z.string() })
|
|
395
|
+
]);
|
|
396
|
+
var limitIrSchema = z.object({
|
|
397
|
+
/** Fixed value or a variable, clamped to [1, max]; absent → default. */
|
|
398
|
+
variable: z.string().optional(),
|
|
399
|
+
value: z.number().int().positive().optional(),
|
|
400
|
+
default: z.number().int().positive(),
|
|
401
|
+
max: z.number().int().positive()
|
|
402
|
+
});
|
|
403
|
+
var selectionIrSchema = z.lazy(
|
|
404
|
+
() => z.object({
|
|
405
|
+
typename: z.string(),
|
|
406
|
+
columns: z.array(z.object({ alias: z.string(), column: z.string() })),
|
|
407
|
+
relations: z.array(z.object({
|
|
408
|
+
alias: z.string(),
|
|
409
|
+
link: z.string(),
|
|
410
|
+
selection: selectionIrSchema,
|
|
411
|
+
where: predIrSchema.optional(),
|
|
412
|
+
orderBy: z.array(orderIrSchema).optional(),
|
|
413
|
+
limit: limitIrSchema.optional(),
|
|
414
|
+
offset: z.object({ variable: z.string().optional(), value: z.number().int().optional() }).optional()
|
|
415
|
+
})),
|
|
416
|
+
aggregates: z.array(z.object({
|
|
417
|
+
alias: z.string(),
|
|
418
|
+
link: z.string().optional(),
|
|
419
|
+
where: predIrSchema.optional(),
|
|
420
|
+
count: z.object({ columns: z.array(z.string()).optional(), distinct: z.boolean().optional() }).optional(),
|
|
421
|
+
sum: z.array(z.string()).optional(),
|
|
422
|
+
avg: z.array(z.string()).optional(),
|
|
423
|
+
nodes: selectionIrSchema.optional()
|
|
424
|
+
}))
|
|
425
|
+
})
|
|
426
|
+
);
|
|
427
|
+
var includeIfSchema = z.object({ variable: z.string(), negated: z.boolean() });
|
|
428
|
+
var selectRootSchema = z.object({
|
|
429
|
+
kind: z.literal("select"),
|
|
430
|
+
alias: z.string(),
|
|
431
|
+
table: z.string(),
|
|
432
|
+
selection: selectionIrSchema,
|
|
433
|
+
/** Composed at compile time: AND(policy, documentWhere?, dynamicFilter?). */
|
|
434
|
+
where: predIrSchema,
|
|
435
|
+
orderBy: z.array(orderIrSchema).default([]),
|
|
436
|
+
distinctOn: z.union([z.array(z.string()), z.object({ slot: z.string() })]).optional(),
|
|
437
|
+
limit: limitIrSchema,
|
|
438
|
+
offset: z.object({ variable: z.string().optional(), value: z.number().int().optional() }).optional(),
|
|
439
|
+
includeIf: includeIfSchema.optional()
|
|
440
|
+
});
|
|
441
|
+
var byPkRootSchema = z.object({
|
|
442
|
+
kind: z.literal("byPk"),
|
|
443
|
+
alias: z.string(),
|
|
444
|
+
table: z.string(),
|
|
445
|
+
selection: selectionIrSchema,
|
|
446
|
+
pk: z.array(z.object({ column: z.string(), value: valueIrSchema })).min(1),
|
|
447
|
+
/** Policy predicate — ANDed with the PK match. */
|
|
448
|
+
where: predIrSchema,
|
|
449
|
+
includeIf: includeIfSchema.optional()
|
|
450
|
+
});
|
|
451
|
+
var aggregateRootSchema = z.object({
|
|
452
|
+
kind: z.literal("aggregate"),
|
|
453
|
+
alias: z.string(),
|
|
454
|
+
table: z.string(),
|
|
455
|
+
where: predIrSchema,
|
|
456
|
+
count: z.object({ columns: z.array(z.string()).optional(), distinct: z.boolean().optional() }).optional(),
|
|
457
|
+
sum: z.array(z.string()).optional(),
|
|
458
|
+
avg: z.array(z.string()).optional(),
|
|
459
|
+
nodes: selectionIrSchema.optional(),
|
|
460
|
+
includeIf: includeIfSchema.optional()
|
|
461
|
+
});
|
|
462
|
+
var writeValuesSchema = z.union([
|
|
463
|
+
z.object({ slot: z.string() }),
|
|
464
|
+
z.object({ entries: z.array(z.object({ column: z.string(), value: valueIrSchema })) })
|
|
465
|
+
]);
|
|
466
|
+
var onConflictIrSchema = z.union([
|
|
467
|
+
z.object({ slot: z.string() }),
|
|
468
|
+
z.object({ constraint: z.string(), updateColumns: z.array(z.string()) })
|
|
469
|
+
]);
|
|
470
|
+
var mutationRootSchema = z.object({
|
|
471
|
+
kind: z.literal("mutation"),
|
|
472
|
+
/**
|
|
473
|
+
* CRUD verbs plus the structured copy-on-write ops. `cowOverride` copies the
|
|
474
|
+
* synced row into the overlay (INSERT…SELECT, ON CONFLICT DO NOTHING) then
|
|
475
|
+
* applies `set` — both statements generated by the executor from this
|
|
476
|
+
* metadata, never authored SQL. `cowRevert` deletes the override so the
|
|
477
|
+
* coalesce view falls back to synced. Both are gated on a matching synced
|
|
478
|
+
* row and carry the write policy in `where` like update/delete.
|
|
479
|
+
*/
|
|
480
|
+
op: z.enum(["insert", "insertOne", "update", "updateByPk", "delete", "deleteByPk", "cowOverride", "cowRevert"]),
|
|
481
|
+
alias: z.string(),
|
|
482
|
+
table: z.string(),
|
|
483
|
+
/** CoW ops: logical name of the synced twin (read through its policy-filtered view). */
|
|
484
|
+
cowSynced: z.string().optional(),
|
|
485
|
+
/** cowOverride: logical properties copied INSERT…SELECT from synced (pk included). */
|
|
486
|
+
cowCopyColumns: z.array(z.string()).optional(),
|
|
487
|
+
/** cowOverride: insert-policy WITH CHECK evaluated over the copied rows. */
|
|
488
|
+
cowCopyCheck: predIrSchema.optional(),
|
|
489
|
+
/** insert/insertOne: rows come from a slot ($object/$objects) or literals. */
|
|
490
|
+
objects: z.union([
|
|
491
|
+
z.object({ slot: z.string() }),
|
|
492
|
+
z.object({ rows: z.array(z.array(z.object({ column: z.string(), value: valueIrSchema }))) })
|
|
493
|
+
]).optional(),
|
|
494
|
+
/** Server-pinned columns (e.g. author_sub ← claim('sub')) merged into every inserted row. */
|
|
495
|
+
pinnedColumns: z.array(z.object({ column: z.string(), value: valueIrSchema })).default([]),
|
|
496
|
+
onConflict: onConflictIrSchema.optional(),
|
|
497
|
+
set: writeValuesSchema.optional(),
|
|
498
|
+
append: writeValuesSchema.optional(),
|
|
499
|
+
inc: writeValuesSchema.optional(),
|
|
500
|
+
/** update/delete row filter: AND(writePolicy, documentWhere/pk, dynamicFilter?). */
|
|
501
|
+
where: predIrSchema.optional(),
|
|
502
|
+
pk: z.array(z.object({ column: z.string(), value: valueIrSchema })).optional(),
|
|
503
|
+
/** WITH CHECK analogue evaluated against NEW row values (insert/update). */
|
|
504
|
+
insertCheck: predIrSchema.optional(),
|
|
505
|
+
returning: selectionIrSchema.nullable(),
|
|
506
|
+
affectedRows: z.boolean().default(false),
|
|
507
|
+
includeIf: includeIfSchema.optional()
|
|
508
|
+
});
|
|
509
|
+
var rootIrSchema = z.discriminatedUnion("kind", [
|
|
510
|
+
selectRootSchema,
|
|
511
|
+
byPkRootSchema,
|
|
512
|
+
aggregateRootSchema,
|
|
513
|
+
mutationRootSchema
|
|
514
|
+
]);
|
|
515
|
+
var INPUT_REF_KINDS = [
|
|
516
|
+
"boolExp",
|
|
517
|
+
"orderBy",
|
|
518
|
+
"selectColumn",
|
|
519
|
+
"insertInput",
|
|
520
|
+
"setInput",
|
|
521
|
+
"appendInput",
|
|
522
|
+
"onConflict"
|
|
523
|
+
];
|
|
524
|
+
var variableDefaultIrSchema = z.object({
|
|
525
|
+
value: z.union([scalarLiteral, z.array(scalarLiteral)])
|
|
526
|
+
});
|
|
527
|
+
var variableIrSchema = z.object({
|
|
528
|
+
name: z.string(),
|
|
529
|
+
type: z.union([
|
|
530
|
+
z.object({ kind: z.literal("scalar"), scalar: z.enum(COLUMN_TYPES) }),
|
|
531
|
+
/** `values` is the declared membership whitelist — the executor validates against it. */
|
|
532
|
+
z.object({ kind: z.literal("enum"), name: z.string(), values: z.array(z.string()).min(1) }),
|
|
533
|
+
/** §4f dynamic slot: validated against `tables[table]` whitelists at runtime. */
|
|
534
|
+
z.object({ kind: z.literal("inputRef"), input: z.enum(INPUT_REF_KINDS), table: z.string() })
|
|
535
|
+
]),
|
|
536
|
+
nullable: z.boolean(),
|
|
537
|
+
list: z.boolean(),
|
|
538
|
+
/** Absent = no default declared; see {@link variableDefaultIrSchema}. */
|
|
539
|
+
defaultValue: variableDefaultIrSchema.optional()
|
|
540
|
+
});
|
|
541
|
+
var compiledOperationSchema = z.object({
|
|
542
|
+
irVersion: z.literal(IR_VERSION),
|
|
543
|
+
name: z.string(),
|
|
544
|
+
opHash: z.string().regex(/^sha256:[0-9a-f]{64}$/),
|
|
545
|
+
kind: z.enum(["query", "mutation"]),
|
|
546
|
+
requires: z.object({
|
|
547
|
+
capability: z.string().optional(),
|
|
548
|
+
role: z.string().optional()
|
|
549
|
+
}).nullable(),
|
|
550
|
+
variables: z.array(variableIrSchema),
|
|
551
|
+
/** Every table the op touches — data-token scoping + audit. */
|
|
552
|
+
tableSet: z.array(z.string()).min(1),
|
|
553
|
+
roots: z.array(rootIrSchema).min(1)
|
|
554
|
+
});
|
|
555
|
+
var tableMetaIrSchema = z.object({
|
|
556
|
+
table: z.string(),
|
|
557
|
+
schema: z.string(),
|
|
558
|
+
typename: z.string(),
|
|
559
|
+
/**
|
|
560
|
+
* True when statements over this relation evaluate a claims-gated synced
|
|
561
|
+
* view: synced twins (`source: gold.*`) materialize as views whose baked
|
|
562
|
+
* policy reads `current_setting('request.jwt.claims')`, and namespace views
|
|
563
|
+
* inherit the gate transitively through their declared dependencies. The
|
|
564
|
+
* executor must stamp that GUC (transaction-scoped) before any statement
|
|
565
|
+
* touching such a relation, or the synced arm yields zero rows regardless
|
|
566
|
+
* of the compiled predicates.
|
|
567
|
+
*/
|
|
568
|
+
claimsGated: z.boolean().default(false),
|
|
569
|
+
columns: z.array(z.object({
|
|
570
|
+
name: z.string(),
|
|
571
|
+
columnName: z.string(),
|
|
572
|
+
type: z.union([z.enum(COLUMN_TYPES), z.object({ enum: z.string(), values: z.array(z.string()) })]),
|
|
573
|
+
nullable: z.boolean()
|
|
574
|
+
})),
|
|
575
|
+
/** Empty for keyless read-only views; keyed tables and views list logical columns. */
|
|
576
|
+
primaryKey: z.array(z.string()),
|
|
577
|
+
links: z.array(z.object({
|
|
578
|
+
name: z.string(),
|
|
579
|
+
kind: z.enum(["toOne", "toMany", "toPlatformUser"]),
|
|
580
|
+
target: z.string(),
|
|
581
|
+
on: z.string(),
|
|
582
|
+
references: z.string()
|
|
583
|
+
})),
|
|
584
|
+
/** §4f whitelists: what dynamic inputs may reference. */
|
|
585
|
+
filterable: z.array(z.string()),
|
|
586
|
+
orderable: z.array(z.string()),
|
|
587
|
+
insertable: z.array(z.string()),
|
|
588
|
+
settable: z.array(z.string()),
|
|
589
|
+
appendable: z.array(z.string()),
|
|
590
|
+
constraints: z.array(z.object({ name: z.string(), columns: z.array(z.string()) })),
|
|
591
|
+
/** Depth/node caps for dynamic filter trees on this table. */
|
|
592
|
+
dynamicFilterMaxDepth: z.number().int().positive().default(6),
|
|
593
|
+
dynamicFilterMaxNodes: z.number().int().positive().default(64)
|
|
594
|
+
});
|
|
595
|
+
var compiledArtifactSchema = z.object({
|
|
596
|
+
irVersion: z.literal(IR_VERSION),
|
|
597
|
+
namespace: z.string(),
|
|
598
|
+
schemaHash: z.string(),
|
|
599
|
+
opsHash: z.string(),
|
|
600
|
+
tables: z.record(z.string(), tableMetaIrSchema),
|
|
601
|
+
operations: z.record(z.string(), compiledOperationSchema)
|
|
602
|
+
});
|
|
603
|
+
var IrVersionError = class extends Error {
|
|
604
|
+
constructor(found) {
|
|
605
|
+
super(`Unsupported IR version ${String(found)} \u2014 this executor supports up to ${IR_VERSION}. Upgrade @sequenceholdings/orm or re-push the namespace.`);
|
|
606
|
+
this.name = "IrVersionError";
|
|
607
|
+
}
|
|
608
|
+
};
|
|
609
|
+
function normalizeIr(raw) {
|
|
610
|
+
if (!raw || typeof raw !== "object") throw new IrVersionError(void 0);
|
|
611
|
+
const version = raw.irVersion;
|
|
612
|
+
if (version !== IR_VERSION) throw new IrVersionError(version);
|
|
613
|
+
return compiledArtifactSchema.parse(raw);
|
|
614
|
+
}
|
|
615
|
+
function indexByHash(artifact) {
|
|
616
|
+
const map = /* @__PURE__ */ new Map();
|
|
617
|
+
for (const op of Object.values(artifact.operations)) map.set(op.opHash, op);
|
|
618
|
+
return map;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export { CLAIMS_CONTRACT, CMP_OPS, INPUT_REF_KINDS, IR_VERSION, IrVersionError, compiledArtifactSchema, compiledOperationSchema, indexByHash, limitIrSchema, normalizeIr, orderIrSchema, predIrSchema, rootIrSchema, selectionIrSchema, tableMetaIrSchema, valueIrSchema, variableDefaultIrSchema, variableIrSchema };
|
|
622
|
+
//# sourceMappingURL=ir.js.map
|
|
623
|
+
//# sourceMappingURL=ir.js.map
|