better-supabase 0.0.0 → 0.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.
Files changed (154) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +88 -1
  3. package/bin/better-supabase.js +3 -0
  4. package/dist/casing-da0uRTqt.js +14 -0
  5. package/dist/cli/bin.d.ts +1 -0
  6. package/dist/cli/bin.js +15 -0
  7. package/dist/cli/index.d.ts +226 -0
  8. package/dist/cli/index.js +3 -0
  9. package/dist/cli-CoByWYja.js +6472 -0
  10. package/dist/client/index.d.ts +2 -0
  11. package/dist/client/index.js +112 -0
  12. package/dist/compiler-DL18ukPe.d.ts +14 -0
  13. package/dist/config/index.d.ts +3 -0
  14. package/dist/config/index.js +3 -0
  15. package/dist/config-D-aR2ZoN.js +276 -0
  16. package/dist/define-CTfWw-SS.d.ts +662 -0
  17. package/dist/define-QGkq04oe.js +1068 -0
  18. package/dist/edge/index.d.ts +43 -0
  19. package/dist/edge/index.js +103 -0
  20. package/dist/entitlements-BBg61DQZ.d.ts +20 -0
  21. package/dist/entitlements-BC_khwRy.js +17 -0
  22. package/dist/env/index.d.ts +2 -0
  23. package/dist/env/index.js +2 -0
  24. package/dist/env-BEToab0I.js +204 -0
  25. package/dist/errors-Bl3rkOzo.js +196 -0
  26. package/dist/events/index.d.ts +2 -0
  27. package/dist/events/index.js +2 -0
  28. package/dist/events-2OJsVG_d.d.ts +84 -0
  29. package/dist/events-BuzTgn2D.js +135 -0
  30. package/dist/executor-Bazhgsgq.d.ts +195 -0
  31. package/dist/executor-Bje4suQF.d.ts +25 -0
  32. package/dist/executor-Ceq60hPM.js +89 -0
  33. package/dist/hono/index.d.ts +45 -0
  34. package/dist/hono/index.js +70 -0
  35. package/dist/index-BD6wKthp.d.ts +597 -0
  36. package/dist/index-DQVvHzjW.d.ts +74 -0
  37. package/dist/index-DRid0qIv.d.ts +76 -0
  38. package/dist/index-Dup-nSe9.d.ts +117 -0
  39. package/dist/index-Dy6kuF0P.d.ts +71 -0
  40. package/dist/index-Hf8PP9sP.d.ts +116 -0
  41. package/dist/index-JXHBuS53.d.ts +144 -0
  42. package/dist/index-Nxo3TJcm.d.ts +208 -0
  43. package/dist/index-Sw43J8HG.d.ts +62 -0
  44. package/dist/index-cEeweTeM.d.ts +38 -0
  45. package/dist/index.d.ts +110 -0
  46. package/dist/index.js +42 -0
  47. package/dist/invalidate-DbxuL2jL.js +18 -0
  48. package/dist/jobs/index.d.ts +173 -0
  49. package/dist/jobs/index.js +477 -0
  50. package/dist/json-schema-BbwO2mZg.js +181 -0
  51. package/dist/lint/index.d.ts +75 -0
  52. package/dist/lint/index.js +167 -0
  53. package/dist/list/index.d.ts +2 -0
  54. package/dist/list/index.js +410 -0
  55. package/dist/live-6b9i72Ux.d.ts +180 -0
  56. package/dist/live-D8rV1Ldw.js +168 -0
  57. package/dist/mcp/index.d.ts +105 -0
  58. package/dist/mcp/index.js +330 -0
  59. package/dist/mfa-CpogP66z.js +521 -0
  60. package/dist/mfa-TVia554c.d.ts +44 -0
  61. package/dist/next/image/index.d.ts +34 -0
  62. package/dist/next/image/index.js +40 -0
  63. package/dist/next/index.d.ts +210 -0
  64. package/dist/next/index.js +426 -0
  65. package/dist/openapi/index.d.ts +2 -0
  66. package/dist/openapi/index.js +332 -0
  67. package/dist/orpc/index.d.ts +46 -0
  68. package/dist/orpc/index.js +50 -0
  69. package/dist/otel/index.d.ts +40 -0
  70. package/dist/otel/index.js +168 -0
  71. package/dist/path-Cv__-OTj.d.ts +17 -0
  72. package/dist/plugin-BqR6wKMB.js +23 -0
  73. package/dist/plugin-HvGqCurB.d.ts +118 -0
  74. package/dist/plugins/actor/index.d.ts +13 -0
  75. package/dist/plugins/actor/index.js +43 -0
  76. package/dist/plugins/rules/index.d.ts +88 -0
  77. package/dist/plugins/rules/index.js +174 -0
  78. package/dist/plugins/soft-delete/index.d.ts +38 -0
  79. package/dist/plugins/soft-delete/index.js +69 -0
  80. package/dist/plugins/tenant/index.d.ts +43 -0
  81. package/dist/plugins/tenant/index.js +73 -0
  82. package/dist/plugins/timestamps/index.d.ts +9 -0
  83. package/dist/plugins/timestamps/index.js +37 -0
  84. package/dist/plugins/validation/index.d.ts +24 -0
  85. package/dist/plugins/validation/index.js +59 -0
  86. package/dist/pool-BFeqzwQS.d.ts +37 -0
  87. package/dist/postgres/index.d.ts +28 -0
  88. package/dist/postgres/index.js +79 -0
  89. package/dist/postgrest-Dm-vhY6R.d.ts +43 -0
  90. package/dist/problem-DPu6Fh-t.js +97 -0
  91. package/dist/problem-DlgB0lYl.d.ts +47 -0
  92. package/dist/query/index.d.ts +2 -0
  93. package/dist/query/index.js +3 -0
  94. package/dist/query-ChvDbsDE.js +186 -0
  95. package/dist/react/index.d.ts +7 -0
  96. package/dist/react/index.js +5 -0
  97. package/dist/react/server.d.ts +13 -0
  98. package/dist/react/server.js +25 -0
  99. package/dist/react/session.d.ts +2 -0
  100. package/dist/react/session.js +3 -0
  101. package/dist/react-CrOvrnIP.js +241 -0
  102. package/dist/read-set-DhX2d04c.js +216 -0
  103. package/dist/realtime/index.d.ts +3 -0
  104. package/dist/realtime/index.js +253 -0
  105. package/dist/resolve-DW8h9NPm.d.ts +155 -0
  106. package/dist/resource-CYiu6Ygi.js +175 -0
  107. package/dist/resource-SkPTRJDD.d.ts +65 -0
  108. package/dist/respond-4-dhoV7x.js +380 -0
  109. package/dist/respond-PoTpy5DT.d.ts +169 -0
  110. package/dist/result-CTkCZ6JA.d.ts +198 -0
  111. package/dist/result-DWXatkd6.js +103 -0
  112. package/dist/scope-CDHcS7dE.js +90 -0
  113. package/dist/seed-CzecODcJ.js +101 -0
  114. package/dist/server/index.d.ts +42 -0
  115. package/dist/server/index.js +68 -0
  116. package/dist/session-C3osL21k.d.ts +21 -0
  117. package/dist/session-CD-3orO_.js +19 -0
  118. package/dist/shared-_6-uPL7E.js +57 -0
  119. package/dist/simplify-oHiDKRVQ.js +665 -0
  120. package/dist/spec-pins-CJPVghHy.d.ts +20 -0
  121. package/dist/spec-pins-t6sPaHvl.js +20 -0
  122. package/dist/sql-M8l3fnS0.js +260 -0
  123. package/dist/ssr/index.d.ts +5 -0
  124. package/dist/ssr/index.js +4 -0
  125. package/dist/standard-CyhRzctb.d.ts +7 -0
  126. package/dist/standard-FUi3Z3-7.js +21 -0
  127. package/dist/stats-3iy8u3_P.js +566 -0
  128. package/dist/storage/index.d.ts +4 -0
  129. package/dist/storage/index.js +2 -0
  130. package/dist/storage-BWXVUXGk.js +461 -0
  131. package/dist/tables-rZxRxNPW.js +59 -0
  132. package/dist/template-BeOGnynM.js +93 -0
  133. package/dist/template-mbvlysOo.d.ts +6 -0
  134. package/dist/testing/index.d.ts +294 -0
  135. package/dist/testing/index.js +751 -0
  136. package/dist/types-6cYK9pbJ.js +29 -0
  137. package/dist/types-C2fslP1z.d.ts +195 -0
  138. package/dist/version-Cqq1m3UY.js +4 -0
  139. package/dist/view-B0ltUmTi.d.ts +43 -0
  140. package/dist/view-B6F9QbG-.js +35 -0
  141. package/dist/webhooks/index.d.ts +152 -0
  142. package/dist/webhooks/index.js +2 -0
  143. package/dist/webhooks-DbMTJJX2.js +139 -0
  144. package/package.json +246 -17
  145. package/schemas/config-v1.json +328 -0
  146. package/schemas/doctor-report-v1.json +105 -0
  147. package/schemas/snapshot-v2.json +1002 -0
  148. package/skills/better-supabase/SKILL.md +84 -0
  149. package/skills/better-supabase/references/plugins.md +56 -0
  150. package/skills/better-supabase/references/troubleshooting.md +39 -0
  151. package/skills/better-supabase-api/SKILL.md +67 -0
  152. package/skills/better-supabase-api/references/adapters.md +108 -0
  153. package/skills/better-supabase-testing/SKILL.md +65 -0
  154. package/index.js +0 -1
@@ -0,0 +1,665 @@
1
+ import { n as dbError, t as DbException } from "./errors-Bl3rkOzo.js";
2
+ import { i as or, r as not, t as and } from "./types-6cYK9pbJ.js";
3
+ //#region src/schema/define.ts
4
+ /**
5
+ * Binds generated model types to runtime metadata. Called from the generated
6
+ * module; apps import the returned `schema`, never call this themselves.
7
+ */
8
+ function defineSchema(meta) {
9
+ return { meta: validateMeta(meta) };
10
+ }
11
+ function validateMeta(meta) {
12
+ if (meta.version !== 1) throw new TypeError(`better-supabase: unsupported schema metadata version ${String(meta.version)}. Run \`better-supabase gen\` again.`);
13
+ return meta;
14
+ }
15
+ function tableMeta(meta, table) {
16
+ const found = meta.tables[table];
17
+ if (!found) throw new TypeError(`better-supabase: unknown table "${table}"`);
18
+ return found;
19
+ }
20
+ function relationMeta(meta, table, relation) {
21
+ const found = table.relations[relation];
22
+ if (!found) throw new TypeError(`better-supabase: unknown relation "${relation}" on "${table.key}"`);
23
+ return {
24
+ relation: found,
25
+ target: tableMeta(meta, found.table)
26
+ };
27
+ }
28
+ //#endregion
29
+ //#region src/ir/codec.ts
30
+ /** Wire form of an app value: `Date` to ISO text, `bigint` to decimal text. */
31
+ function encodeValue(value) {
32
+ if (value instanceof Date) return value.toISOString();
33
+ if (typeof value === "bigint") return value.toString();
34
+ if (Array.isArray(value)) return value.map(encodeValue);
35
+ return value;
36
+ }
37
+ function decodeScalar(codec, value) {
38
+ if (value === null || value === void 0) return value;
39
+ switch (codec) {
40
+ case "date": return new Date(value);
41
+ case "bigint": return BigInt(value);
42
+ case "string": return String(value);
43
+ default: return codec;
44
+ }
45
+ }
46
+ function decode(codec, value) {
47
+ return Array.isArray(value) ? value.map((item) => decodeScalar(codec, item)) : decodeScalar(codec, value);
48
+ }
49
+ /** Whether decoding `selection` changes anything, so plain reads skip the walk. */
50
+ function needsDecoding(selection) {
51
+ return selection.aggregate !== void 0 || selection.columns.some((column) => column.codec !== void 0) || selection.includes.some((include) => include.count !== void 0 || include.aggregate !== void 0 || needsDecoding(include.selection));
52
+ }
53
+ /** PostgREST returns `[{ count }]` for a `(count)` embed; SQL returns the number. */
54
+ function countOf(value) {
55
+ if (Array.isArray(value)) {
56
+ const first = value[0];
57
+ return Number(first?.count ?? 0);
58
+ }
59
+ return Number(value ?? 0);
60
+ }
61
+ function measureOf(measure, value) {
62
+ if (value === void 0 || value === null) return null;
63
+ return measure.codec ? decodeScalar(measure.codec, value) : value;
64
+ }
65
+ /** Adds `value` under `row[key][name]`, next to what is already there. */
66
+ function fold(row, key, name, value) {
67
+ row[key] = {
68
+ ...row[key] ?? {},
69
+ [name]: value
70
+ };
71
+ }
72
+ function decodeRow(selection, row) {
73
+ const out = { ...row };
74
+ for (const column of selection.columns) if (column.codec && column.alias in out) out[column.alias] = decode(column.codec, out[column.alias]);
75
+ if (selection.aggregate) {
76
+ if (selection.aggregate.count) out["_count"] = countOf(out["_count"]);
77
+ for (const measure of selection.aggregate.measures) {
78
+ const value = out[measure.key];
79
+ delete out[measure.key];
80
+ fold(out, `_${measure.fn}`, measure.alias, measureOf(measure, value));
81
+ }
82
+ }
83
+ for (const include of selection.includes) {
84
+ const value = out[include.alias];
85
+ if (include.count !== void 0) {
86
+ if (!(include.alias in out)) continue;
87
+ delete out[include.alias];
88
+ fold(out, "_count", include.count, countOf(value));
89
+ continue;
90
+ }
91
+ if (include.aggregate !== void 0) {
92
+ if (!(include.alias in out)) continue;
93
+ delete out[include.alias];
94
+ const inner = Array.isArray(value) ? value[0] : value;
95
+ const measures = {};
96
+ for (const measure of include.selection.aggregate?.measures ?? []) measures[measure.alias] = measureOf(measure, inner?.[measure.key]);
97
+ fold(out, `_${include.aggregate.fn}`, include.aggregate.name, measures);
98
+ continue;
99
+ }
100
+ if (Array.isArray(value)) out[include.alias] = value.map((item) => decodeRow(include.selection, item));
101
+ else if (value && typeof value === "object") out[include.alias] = decodeRow(include.selection, value);
102
+ }
103
+ return out;
104
+ }
105
+ /** Applies column codecs to returned rows, including embedded relations. */
106
+ function decodeRows(selection, rows) {
107
+ if (!selection || !needsDecoding(selection)) return rows;
108
+ return rows.map((row) => decodeRow(selection, row));
109
+ }
110
+ //#endregion
111
+ //#region src/ir/build.ts
112
+ const AGGREGATE_KEYS = {
113
+ _sum: "sum",
114
+ _avg: "avg",
115
+ _min: "min",
116
+ _max: "max"
117
+ };
118
+ const NUMERIC_TYPES = /* @__PURE__ */ new Set([
119
+ "int2",
120
+ "int4",
121
+ "int8",
122
+ "float4",
123
+ "float8",
124
+ "numeric",
125
+ "smallint",
126
+ "integer",
127
+ "bigint",
128
+ "real",
129
+ "double precision"
130
+ ]);
131
+ /** Thrown while building IR; surfaces as an `invalid_request` Result. */
132
+ function invalidRequest(message, table) {
133
+ throw new DbException(dbError("invalid_request", message, table ? { table } : {}));
134
+ }
135
+ const FIELD_OPS = /* @__PURE__ */ new Set([
136
+ "eq",
137
+ "neq",
138
+ "in",
139
+ "notIn",
140
+ "isNull",
141
+ "not",
142
+ "gt",
143
+ "gte",
144
+ "lt",
145
+ "lte",
146
+ "like",
147
+ "ilike",
148
+ "contains",
149
+ "startsWith",
150
+ "endsWith",
151
+ "search",
152
+ "hasEvery",
153
+ "hasSome",
154
+ "has",
155
+ "containedBy"
156
+ ]);
157
+ function isPlainObject(value) {
158
+ if (typeof value !== "object" || value === null) return false;
159
+ const proto = Object.getPrototypeOf(value);
160
+ return proto === Object.prototype || proto === null;
161
+ }
162
+ function isOpsObject(value) {
163
+ if (!isPlainObject(value)) return false;
164
+ const keys = Object.keys(value);
165
+ return keys.length > 0 && keys.every((key) => FIELD_OPS.has(key));
166
+ }
167
+ function writable(meta, mode) {
168
+ if (meta.generated || meta.identity === "always") return false;
169
+ return mode === "insert" ? meta.insertable !== false : meta.updatable !== false;
170
+ }
171
+ /** Escapes LIKE wildcards so user input matches literally. */
172
+ function escapeLike(value) {
173
+ return value.replace(/[\\%_]/g, (char) => `\\${char}`);
174
+ }
175
+ var IrBuilder = class {
176
+ meta;
177
+ constructor(meta) {
178
+ this.meta = meta;
179
+ }
180
+ table(key) {
181
+ const table = this.meta.tables[key];
182
+ if (!table) invalidRequest(`Unknown table "${key}"`);
183
+ return table;
184
+ }
185
+ column(table, name) {
186
+ const column = table.columns[name];
187
+ if (!column) invalidRequest(`Unknown column "${name}" on "${table.key}"`, table.key);
188
+ return column.db;
189
+ }
190
+ where(table, input) {
191
+ if (input === void 0) return void 0;
192
+ if (!isPlainObject(input)) invalidRequest(`"where" on "${table.key}" must be an object`, table.key);
193
+ const items = [];
194
+ for (const [key, value] of Object.entries(input)) {
195
+ if (value === void 0) continue;
196
+ if (key === "AND") {
197
+ const list = Array.isArray(value) ? value : [value];
198
+ items.push(and(...list.map((item) => this.where(table, item))));
199
+ } else if (key === "OR") {
200
+ if (!Array.isArray(value)) invalidRequest(`"OR" on "${table.key}" must be an array`, table.key);
201
+ const branches = value.map((item) => this.where(table, item)).filter((item) => item !== void 0);
202
+ items.push(branches.length === 0 ? not({
203
+ kind: "and",
204
+ items: []
205
+ }) : or(...branches));
206
+ } else if (key === "NOT") {
207
+ const inner = this.where(table, value);
208
+ if (inner) items.push(not(inner));
209
+ } else if (key in table.relations) items.push(this.relationFilter(table, key, value));
210
+ else items.push(this.fieldFilter(table, key, value));
211
+ }
212
+ return and(...items);
213
+ }
214
+ relationFilter(table, name, value) {
215
+ const { relation, target } = relationMeta(this.meta, table, name);
216
+ const make = (quantifier, where) => ({
217
+ kind: "relation",
218
+ name,
219
+ relation,
220
+ target,
221
+ quantifier,
222
+ where: where === null ? void 0 : this.where(target, where)
223
+ });
224
+ if (relation.kind === "many") {
225
+ if (!isPlainObject(value)) invalidRequest(`Filter on to-many relation "${name}" needs some, none or every`, table.key);
226
+ const items = [];
227
+ for (const [quantifier, where] of Object.entries(value)) {
228
+ if (where === void 0) continue;
229
+ if (quantifier !== "some" && quantifier !== "none" && quantifier !== "every") invalidRequest(`Unknown quantifier "${quantifier}" on relation "${name}"`, table.key);
230
+ items.push(make(quantifier, where));
231
+ }
232
+ return and(...items) ?? make("some", {});
233
+ }
234
+ if (value === null) return make("none", null);
235
+ if (isPlainObject(value)) {
236
+ const keys = Object.keys(value);
237
+ if (keys.length > 0 && keys.every((key) => key === "is" || key === "isNot")) {
238
+ const items = [];
239
+ if (value.is !== void 0) items.push(value.is === null ? make("none", null) : make("some", value.is));
240
+ if (value.isNot !== void 0) items.push(value.isNot === null ? make("some", null) : not(make("some", value.isNot)));
241
+ return and(...items) ?? make("some", null);
242
+ }
243
+ }
244
+ return make("some", value);
245
+ }
246
+ fieldFilter(table, name, value) {
247
+ const column = this.column(table, name);
248
+ if (value === null) return {
249
+ kind: "column",
250
+ column,
251
+ op: "is",
252
+ value: null
253
+ };
254
+ if (!isOpsObject(value)) return {
255
+ kind: "column",
256
+ column,
257
+ op: "eq",
258
+ value: encodeValue(value)
259
+ };
260
+ const items = [];
261
+ for (const [op, operand] of Object.entries(value)) {
262
+ if (operand === void 0) continue;
263
+ items.push(this.fieldOp(table, name, column, op, operand));
264
+ }
265
+ return and(...items) ?? {
266
+ kind: "and",
267
+ items: []
268
+ };
269
+ }
270
+ fieldOp(table, name, column, op, operand) {
271
+ const col = (kind, value) => ({
272
+ kind: "column",
273
+ column,
274
+ op: kind,
275
+ value: encodeValue(value)
276
+ });
277
+ const meta = table.columns[name];
278
+ switch (op) {
279
+ case "eq": return operand === null ? col("is", null) : col("eq", operand);
280
+ case "neq": return operand === null ? not(col("is", null)) : col("neq", operand);
281
+ case "in":
282
+ case "notIn": {
283
+ if (!Array.isArray(operand)) invalidRequest(`"${op}" on "${name}" needs an array`, table.key);
284
+ const condition = col("in", operand);
285
+ return op === "in" ? condition : not(condition);
286
+ }
287
+ case "isNull": return operand ? col("is", null) : not(col("is", null));
288
+ case "not": {
289
+ const inner = this.fieldFilter(table, name, operand);
290
+ return not(inner);
291
+ }
292
+ case "gt":
293
+ case "gte":
294
+ case "lt":
295
+ case "lte":
296
+ case "like":
297
+ case "ilike": return col(op, operand);
298
+ case "contains":
299
+ if (meta?.json || meta?.array) return col("contains", operand);
300
+ return col("ilike", `%${escapeLike(String(operand))}%`);
301
+ case "startsWith": return col("like", `${escapeLike(String(operand))}%`);
302
+ case "endsWith": return col("like", `%${escapeLike(String(operand))}`);
303
+ case "search": {
304
+ if (typeof operand === "string") return col("fts", operand);
305
+ const { query, config } = operand;
306
+ return config ? {
307
+ kind: "column",
308
+ column,
309
+ op: "fts",
310
+ value: query,
311
+ config
312
+ } : col("fts", query);
313
+ }
314
+ case "hasEvery": return col("contains", operand);
315
+ case "has": return col("contains", [operand]);
316
+ case "hasSome": return col("overlaps", operand);
317
+ case "containedBy": return col("containedBy", operand);
318
+ default: return invalidRequest(`Unknown operator "${op}" on "${name}"`, table.key);
319
+ }
320
+ }
321
+ selection(table, select, include) {
322
+ const columns = (select ?? Object.keys(table.columns)).map((alias) => this.selectColumn(table, alias));
323
+ const includes = [];
324
+ if (include !== void 0) {
325
+ if (!isPlainObject(include)) invalidRequest(`"include" on "${table.key}" must be an object`, table.key);
326
+ for (const [name, value] of Object.entries(include)) {
327
+ if (value === void 0 || value === false) continue;
328
+ const fn = AGGREGATE_KEYS[name];
329
+ if (name === "_count") includes.push(...this.counts(table, value));
330
+ else if (fn) includes.push(...this.relationAggregates(table, fn, value));
331
+ else includes.push(this.include(table, name, value));
332
+ }
333
+ }
334
+ return {
335
+ columns,
336
+ includes
337
+ };
338
+ }
339
+ /**
340
+ * `db.x.aggregate(args)`: the grouping columns, `_count` and the measures
341
+ * of each group.
342
+ */
343
+ aggregation(table, args) {
344
+ const groupBy = args.groupBy ?? [];
345
+ if (!Array.isArray(groupBy)) invalidRequest(`"groupBy" on "${table.key}" must be an array`, table.key);
346
+ const columns = groupBy.map((name) => this.selectColumn(table, String(name)));
347
+ const measures = [];
348
+ for (const [key, fn] of Object.entries(AGGREGATE_KEYS)) {
349
+ const value = args[key];
350
+ if (value === void 0) continue;
351
+ for (const alias of this.measured(table, key, value)) measures.push(this.measure(table, fn, alias, `${key}_${alias}`));
352
+ }
353
+ const count = args._count === true;
354
+ if (!count && measures.length === 0) invalidRequest(`aggregate on "${table.key}" needs _count, _sum, _avg, _min or _max`, table.key);
355
+ return {
356
+ columns,
357
+ includes: [],
358
+ aggregate: {
359
+ count,
360
+ measures
361
+ }
362
+ };
363
+ }
364
+ relationAggregates(table, fn, value) {
365
+ const key = `_${fn}`;
366
+ if (!isPlainObject(value)) invalidRequest(`"${key}" on "${table.key}" must map relations to columns`, table.key);
367
+ const includes = [];
368
+ for (const [name, entry] of Object.entries(value)) {
369
+ if (entry === void 0 || entry === false) continue;
370
+ const { relation, target } = relationMeta(this.meta, table, name);
371
+ if (relation.kind !== "many") invalidRequest(`"${key}.${name}" on "${table.key}" needs a to-many relation`, table.key);
372
+ const measures = this.measured(target, `${key}.${name}`, entry).map((alias) => this.measure(target, fn, alias, alias));
373
+ if (measures.length === 0) continue;
374
+ includes.push({
375
+ alias: `${key}_${name}`,
376
+ relation,
377
+ target,
378
+ selection: {
379
+ columns: [],
380
+ includes: [],
381
+ aggregate: {
382
+ count: false,
383
+ measures
384
+ }
385
+ },
386
+ where: void 0,
387
+ orderBy: [],
388
+ limit: void 0,
389
+ required: false,
390
+ aggregate: {
391
+ fn,
392
+ name
393
+ }
394
+ });
395
+ }
396
+ return includes;
397
+ }
398
+ /** The columns switched on in `{ amount: true, tax: true }`. */
399
+ measured(table, path, value) {
400
+ if (!isPlainObject(value)) invalidRequest(`"${path}" on "${table.key}" must map columns to true`, table.key);
401
+ return Object.entries(value).filter(([, on]) => on === true).map(([alias]) => alias);
402
+ }
403
+ measure(table, fn, alias, key) {
404
+ const column = this.column(table, alias);
405
+ const meta = table.columns[alias];
406
+ if ((fn === "sum" || fn === "avg") && !NUMERIC_TYPES.has(meta?.type ?? "")) invalidRequest(`_${fn} needs a numeric column; "${alias}" on "${table.key}" is ${meta?.type}`, table.key);
407
+ const codec = fn === "avg" ? void 0 : meta?.codec;
408
+ if (!codec) return {
409
+ fn,
410
+ key,
411
+ alias,
412
+ column
413
+ };
414
+ return codec === "date" ? {
415
+ fn,
416
+ key,
417
+ alias,
418
+ column,
419
+ codec
420
+ } : {
421
+ fn,
422
+ key,
423
+ alias,
424
+ column,
425
+ cast: "text",
426
+ codec
427
+ };
428
+ }
429
+ /** A selected column, with the cast and codec its metadata asks for. */
430
+ selectColumn(table, alias) {
431
+ const column = this.column(table, alias);
432
+ const codec = table.columns[alias]?.codec;
433
+ if (!codec) return {
434
+ alias,
435
+ column
436
+ };
437
+ return codec === "date" ? {
438
+ alias,
439
+ column,
440
+ codec
441
+ } : {
442
+ alias,
443
+ column,
444
+ cast: "text",
445
+ codec
446
+ };
447
+ }
448
+ counts(table, value) {
449
+ if (!isPlainObject(value)) invalidRequest(`"_count" on "${table.key}" must map relations to true or { where }`, table.key);
450
+ const counts = [];
451
+ for (const [name, entry] of Object.entries(value)) {
452
+ if (entry === void 0 || entry === false) continue;
453
+ const { relation, target } = relationMeta(this.meta, table, name);
454
+ if (relation.kind !== "many") invalidRequest(`"_count.${name}" on "${table.key}" needs a to-many relation`, table.key);
455
+ const args = isPlainObject(entry) ? entry : {};
456
+ counts.push({
457
+ alias: `_count_${name}`,
458
+ relation,
459
+ target,
460
+ selection: {
461
+ columns: [],
462
+ includes: []
463
+ },
464
+ where: this.where(target, args.where),
465
+ orderBy: [],
466
+ limit: void 0,
467
+ required: false,
468
+ count: name
469
+ });
470
+ }
471
+ return counts;
472
+ }
473
+ include(table, name, value) {
474
+ const { relation, target } = relationMeta(this.meta, table, name);
475
+ const args = value === true ? {} : isPlainObject(value) ? value : {};
476
+ return {
477
+ alias: name,
478
+ relation,
479
+ target,
480
+ selection: this.selection(target, args.select, args.include),
481
+ where: this.where(target, args.where),
482
+ orderBy: this.orderBy(target, args.orderBy),
483
+ limit: typeof args.limit === "number" ? args.limit : void 0,
484
+ required: args.required === true
485
+ };
486
+ }
487
+ orderBy(table, input) {
488
+ if (input === void 0) return [];
489
+ const list = Array.isArray(input) ? input : [input];
490
+ const terms = [];
491
+ for (const item of list) {
492
+ if (!isPlainObject(item)) invalidRequest(`"orderBy" on "${table.key}" must be an object`, table.key);
493
+ for (const [name, spec] of Object.entries(item)) {
494
+ if (spec === void 0) continue;
495
+ const column = this.column(table, name);
496
+ if (spec === "asc" || spec === "desc") terms.push({
497
+ column,
498
+ direction: spec
499
+ });
500
+ else if (isPlainObject(spec)) {
501
+ const direction = spec.direction === "desc" ? "desc" : "asc";
502
+ const nulls = spec.nulls;
503
+ terms.push(nulls === "first" || nulls === "last" ? {
504
+ column,
505
+ direction,
506
+ nulls
507
+ } : {
508
+ column,
509
+ direction
510
+ });
511
+ } else invalidRequest(`Invalid sort for "${name}" on "${table.key}"`, table.key);
512
+ }
513
+ }
514
+ return terms;
515
+ }
516
+ /**
517
+ * Maps an app-cased row to database column names. Rejects columns the
518
+ * database won't accept for `mode`: generated columns, `identity always`
519
+ * columns and read-only view columns.
520
+ */
521
+ row(table, input, mode = "insert") {
522
+ if (!isPlainObject(input)) invalidRequest(`Row for "${table.key}" must be an object`, table.key);
523
+ const out = {};
524
+ for (const [name, value] of Object.entries(input)) {
525
+ if (value === void 0) continue;
526
+ const column = this.column(table, name);
527
+ const meta = table.columns[name];
528
+ if (meta && !writable(meta, mode)) invalidRequest(`"${name}" on "${table.key}" is read-only${meta.generated ? " (generated)" : meta.identity === "always" ? " (identity always)" : ""} and can't be set in an ${mode}`, table.key);
529
+ out[column] = encodeValue(value);
530
+ }
531
+ return out;
532
+ }
533
+ /**
534
+ * Condition matching exactly one row by the primary key or by the columns
535
+ * of a named unique key. The given columns must be one complete key.
536
+ */
537
+ uniqueKey(table, input) {
538
+ if (!isPlainObject(input)) invalidRequest(`"where" on "${table.key}" must be an object`, table.key);
539
+ const given = Object.keys(input).filter((key) => input[key] !== void 0);
540
+ const keys = [table.primaryKey, ...Object.values(table.uniqueKeys)];
541
+ const match = keys.find((key) => key.length > 0 && key.length === given.length && key.every((column) => given.includes(column)));
542
+ if (!match) {
543
+ const names = keys.filter((key) => key.length > 0).map((key) => `{ ${key.join(", ")} }`).join(", ");
544
+ invalidRequest(`findUnique on "${table.key}" needs one complete unique key: ${names}`, table.key);
545
+ }
546
+ return {
547
+ kind: "and",
548
+ items: match.map((name) => ({
549
+ kind: "column",
550
+ column: this.column(table, name),
551
+ op: input[name] === null ? "is" : "eq",
552
+ value: encodeValue(input[name])
553
+ }))
554
+ };
555
+ }
556
+ /** Condition matching one primary key value. */
557
+ primaryKey(table, id) {
558
+ const pk = table.primaryKey;
559
+ if (pk.length === 0) invalidRequest(`Table "${table.key}" has no primary key`, table.key);
560
+ if (pk.length === 1) {
561
+ const [name] = pk;
562
+ const value = isPlainObject(id) && name in id ? id[name] : id;
563
+ return {
564
+ kind: "column",
565
+ column: this.column(table, name),
566
+ op: "eq",
567
+ value: encodeValue(value)
568
+ };
569
+ }
570
+ if (!isPlainObject(id)) invalidRequest(`Table "${table.key}" has a composite key; pass { ${pk.join(", ")} }`, table.key);
571
+ return {
572
+ kind: "and",
573
+ items: pk.map((name) => ({
574
+ kind: "column",
575
+ column: this.column(table, name),
576
+ op: "eq",
577
+ value: encodeValue(id[name])
578
+ }))
579
+ };
580
+ }
581
+ };
582
+ //#endregion
583
+ //#region src/ir/simplify.ts
584
+ /**
585
+ * Folds constant conditions. `true` means "no filter", `false` means "matches
586
+ * nothing", so executors can skip the request entirely.
587
+ */
588
+ function simplify(condition) {
589
+ if (condition === void 0) return true;
590
+ switch (condition.kind) {
591
+ case "and": {
592
+ const items = [];
593
+ for (const item of condition.items) {
594
+ const simple = simplify(item);
595
+ if (simple === false) return false;
596
+ if (simple !== true) items.push(simple);
597
+ }
598
+ if (items.length === 0) return true;
599
+ return items.length === 1 && items[0] ? items[0] : {
600
+ kind: "and",
601
+ items
602
+ };
603
+ }
604
+ case "or": {
605
+ const items = [];
606
+ for (const item of condition.items) {
607
+ const simple = simplify(item);
608
+ if (simple === true) return true;
609
+ if (simple !== false) items.push(simple);
610
+ }
611
+ if (items.length === 0) return false;
612
+ return items.length === 1 && items[0] ? items[0] : {
613
+ kind: "or",
614
+ items
615
+ };
616
+ }
617
+ case "not": {
618
+ const inner = simplify(condition.item);
619
+ if (typeof inner === "boolean") return !inner;
620
+ if (inner.kind === "not") return inner.item;
621
+ return {
622
+ kind: "not",
623
+ item: inner
624
+ };
625
+ }
626
+ case "relation": {
627
+ const where = simplify(condition.where);
628
+ if (where === true) {
629
+ if (condition.quantifier === "every") return true;
630
+ return {
631
+ ...condition,
632
+ where: void 0
633
+ };
634
+ }
635
+ if (where === false) {
636
+ if (condition.quantifier === "some") return false;
637
+ if (condition.quantifier === "none") return true;
638
+ return {
639
+ ...condition,
640
+ quantifier: "none",
641
+ where: void 0
642
+ };
643
+ }
644
+ return {
645
+ ...condition,
646
+ where
647
+ };
648
+ }
649
+ case "column":
650
+ if (condition.op === "in" && Array.isArray(condition.value) && condition.value.length === 0) return false;
651
+ return condition;
652
+ default: return condition;
653
+ }
654
+ }
655
+ /** Returns the condition, or `undefined` when it folds to `true`. */
656
+ function simplifyOrFalse(condition) {
657
+ const simple = simplify(condition);
658
+ if (simple === false) return { never: true };
659
+ return {
660
+ never: false,
661
+ condition: simple === true ? void 0 : simple
662
+ };
663
+ }
664
+ //#endregion
665
+ export { invalidRequest as a, needsDecoding as c, escapeLike as i, defineSchema as l, simplifyOrFalse as n, decodeRows as o, IrBuilder as r, encodeValue as s, simplify as t, tableMeta as u };
@@ -0,0 +1,20 @@
1
+ //#region src/core/spec-pins.d.ts
2
+ /**
3
+ * Versions of evolving specs and conventions the code follows. Moving a pin
4
+ * is a minor release with a changeset (see the standards docs page).
5
+ */
6
+ declare const SPEC_PINS: {
7
+ readonly otelSemconv: "1.37.0";
8
+ readonly mcp: "2025-06-18";
9
+ readonly openapi: "3.1.1";
10
+ readonly cloudevents: "1.0.2";
11
+ readonly standardWebhooks: "1.0.0";
12
+ readonly sarif: "2.1.0";
13
+ readonly jsonSchema: "2020-12";
14
+ readonly postgrestAggregates: "12";
15
+ readonly serverTiming: "WD-20260407";
16
+ readonly stripeSyncEngine: "0.48.5";
17
+ readonly pgvector: "0.8";
18
+ };
19
+ //#endregion
20
+ export { SPEC_PINS as t };
@@ -0,0 +1,20 @@
1
+ //#region src/core/spec-pins.ts
2
+ /**
3
+ * Versions of evolving specs and conventions the code follows. Moving a pin
4
+ * is a minor release with a changeset (see the standards docs page).
5
+ */
6
+ const SPEC_PINS = {
7
+ otelSemconv: "1.37.0",
8
+ mcp: "2025-06-18",
9
+ openapi: "3.1.1",
10
+ cloudevents: "1.0.2",
11
+ standardWebhooks: "1.0.0",
12
+ sarif: "2.1.0",
13
+ jsonSchema: "2020-12",
14
+ postgrestAggregates: "12",
15
+ serverTiming: "WD-20260407",
16
+ stripeSyncEngine: "0.48.5",
17
+ pgvector: "0.8"
18
+ };
19
+ //#endregion
20
+ export { SPEC_PINS as t };