@vibeorm/generator 2.1.0 → 2.2.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/dist/emit-dts.d.ts.map +1 -1
- package/dist/emit-module.d.ts +11 -0
- package/dist/emit-module.d.ts.map +1 -0
- package/dist/generate.d.ts +3 -1
- package/dist/generate.d.ts.map +1 -1
- package/dist/index.js +208 -135
- package/dist/index.js.map +10 -9
- package/dist/schema-view.d.ts.map +1 -1
- package/dist/type-names.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,22 +1,151 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// packages/generator/src/emit-module.ts
|
|
3
|
+
import { createHash } from "crypto";
|
|
4
|
+
import { VibeError as VibeError2, providerToDialect } from "@vibeorm/schema";
|
|
5
|
+
|
|
6
|
+
// packages/generator/src/util.ts
|
|
7
|
+
import { VibeError } from "@vibeorm/schema";
|
|
8
|
+
function pascal(params) {
|
|
9
|
+
const { name } = params;
|
|
10
|
+
if (name.length === 0)
|
|
11
|
+
return name;
|
|
12
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
13
|
+
}
|
|
14
|
+
function clientNameOf(params) {
|
|
15
|
+
const { name } = params;
|
|
16
|
+
if (name.length === 0)
|
|
17
|
+
return name;
|
|
18
|
+
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
19
|
+
}
|
|
20
|
+
var IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
21
|
+
function propKey(params) {
|
|
22
|
+
const { name } = params;
|
|
23
|
+
return IDENTIFIER_RE.test(name) ? name : JSON.stringify(name);
|
|
24
|
+
}
|
|
25
|
+
function tsString(params) {
|
|
26
|
+
return JSON.stringify(params.value);
|
|
27
|
+
}
|
|
28
|
+
function maybeParens(params) {
|
|
29
|
+
const { type } = params;
|
|
30
|
+
return type.includes("|") ? `(${type})` : type;
|
|
31
|
+
}
|
|
32
|
+
function stableStringify(params) {
|
|
33
|
+
return renderValue({ value: params.value, indent: "" });
|
|
34
|
+
}
|
|
35
|
+
function renderValue(params) {
|
|
36
|
+
const { value, indent } = params;
|
|
37
|
+
if (value === null)
|
|
38
|
+
return "null";
|
|
39
|
+
const kind = typeof value;
|
|
40
|
+
if (kind === "string" || kind === "number" || kind === "boolean")
|
|
41
|
+
return JSON.stringify(value);
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
if (value.length === 0)
|
|
44
|
+
return "[]";
|
|
45
|
+
const inner = `${indent} `;
|
|
46
|
+
const items = value.map((item) => inner + renderValue({ value: item, indent: inner }));
|
|
47
|
+
return `[
|
|
48
|
+
${items.join(`,
|
|
49
|
+
`)}
|
|
50
|
+
${indent}]`;
|
|
51
|
+
}
|
|
52
|
+
if (kind === "object") {
|
|
53
|
+
const record = value;
|
|
54
|
+
const keys = Object.keys(record).filter((key) => record[key] !== undefined).sort();
|
|
55
|
+
if (keys.length === 0)
|
|
56
|
+
return "{}";
|
|
57
|
+
const inner = `${indent} `;
|
|
58
|
+
const items = keys.map((key) => `${inner}${JSON.stringify(key)}: ${renderValue({ value: record[key], indent: inner })}`);
|
|
59
|
+
return `{
|
|
60
|
+
${items.join(`,
|
|
61
|
+
`)}
|
|
62
|
+
${indent}}`;
|
|
63
|
+
}
|
|
64
|
+
throw new VibeError({
|
|
65
|
+
code: "VIBE_INTERNAL",
|
|
66
|
+
message: `internal: cannot serialize a ${kind} value into the generated client`,
|
|
67
|
+
meta: { kind }
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// packages/generator/src/emit-module.ts
|
|
72
|
+
var MODULE_DECLARATIONS = `import type { ClientContext, DatabaseAdapter, QueryOperation, TransactionOptions } from "@vibeorm/runtime";
|
|
73
|
+
import type { DatabaseModuleManifest, SchemaIR } from "@vibeorm/schema";
|
|
74
|
+
import type { VibeClientInstance, VibeTransactionClientInstance, ReadOnlyClientInstance } from "./index.js";
|
|
75
|
+
export { sql, decode, sqlModels, sqlProjection } from "./index.js";
|
|
76
|
+
export type ModuleClient = Omit<VibeClientInstance, "$connect" | "$disconnect" | "$transaction" | "$scoped" | "$withPolicy" | "$bypassPolicy" | "$readOnly"> & {
|
|
77
|
+
readonly $transaction: ModuleTransactionClient["$transaction"];
|
|
78
|
+
readonly $readOnly: () => ReadOnlyClientInstance;
|
|
79
|
+
};
|
|
80
|
+
export type ModuleTransactionClient = Omit<VibeTransactionClientInstance, "$transaction" | "$scoped" | "$withPolicy" | "$bypassPolicy"> & {
|
|
81
|
+
readonly $transaction: {
|
|
82
|
+
<T>(run: (tx: ModuleTransactionClient) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
83
|
+
<T extends readonly QueryOperation<unknown, "read" | "write">[]>(operations: readonly [...T], options?: TransactionOptions): Promise<{ -readonly [K in keyof T]: Awaited<T[K]> }>;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
export declare const databaseModule: {
|
|
87
|
+
readonly manifest: DatabaseModuleManifest;
|
|
88
|
+
readonly schema: SchemaIR;
|
|
89
|
+
readonly create: (params: { adapter: DatabaseAdapter }) => ModuleClient;
|
|
90
|
+
readonly bind: {
|
|
91
|
+
(params: { db: ClientContext<"root"> }): ModuleClient;
|
|
92
|
+
(params: { db: ClientContext<"transaction"> }): ModuleTransactionClient;
|
|
93
|
+
(params: { db: ClientContext<"readOnly"> }): ReadOnlyClientInstance;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
`;
|
|
97
|
+
function emitDatabaseModule(params) {
|
|
98
|
+
const { view, module } = params;
|
|
99
|
+
for (const key of ["id", "contract", "revision"]) {
|
|
100
|
+
if (typeof module[key] !== "string" || module[key].trim().length === 0 || module[key].includes("\x00"))
|
|
101
|
+
throw new VibeError2({ code: "VIBE_CONFIG", message: `Database module ${key} must be a non-empty identifier`, meta: { reason: "module_manifest", key } });
|
|
102
|
+
}
|
|
103
|
+
if (view.schema.rls !== undefined || view.schema.extensions.length > 0 || view.schema.models.some((model) => model.policy !== undefined))
|
|
104
|
+
throw new VibeError2({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Module ${module.id} cannot declare extensions, policy or native RLS in this module protocol`, meta: { reason: "module_schema_feature", moduleId: module.id } });
|
|
105
|
+
const serialized = stableStringify({ value: view.schema });
|
|
106
|
+
const manifest = {
|
|
107
|
+
...module,
|
|
108
|
+
protocol: 1,
|
|
109
|
+
dialect: providerToDialect({ provider: view.schema.datasource.provider }),
|
|
110
|
+
schemaChecksum: createHash("sha256").update(JSON.stringify(JSON.parse(serialized))).digest("hex")
|
|
111
|
+
};
|
|
112
|
+
return [
|
|
113
|
+
{ name: "module.js", content: `// Generated package module. No connection or DDL on import.
|
|
114
|
+
import { defineDatabaseModule } from "@vibeorm/runtime";
|
|
115
|
+
import { schema } from "./schema.js";
|
|
116
|
+
export { sql, decode, sqlModels, sqlProjection } from "./index.js";
|
|
117
|
+
export const databaseModule = defineDatabaseModule({ ...${JSON.stringify(module)}, schema, manifest: ${JSON.stringify(manifest)} });
|
|
118
|
+
` },
|
|
119
|
+
{ name: "schema.js", content: `// Generated data-only schema and runtime manifest.
|
|
120
|
+
export const schema = ${serialized};
|
|
121
|
+
export const moduleManifest = ${JSON.stringify(manifest)};
|
|
122
|
+
` },
|
|
123
|
+
{ name: "schema.d.ts", content: `import type { SchemaIR, DatabaseModuleManifest } from "@vibeorm/schema";
|
|
124
|
+
export declare const schema: SchemaIR;
|
|
125
|
+
export declare const moduleManifest: DatabaseModuleManifest;
|
|
126
|
+
` },
|
|
127
|
+
{ name: "module.d.ts", content: MODULE_DECLARATIONS }
|
|
128
|
+
];
|
|
129
|
+
}
|
|
130
|
+
|
|
2
131
|
// packages/generator/src/generate.ts
|
|
3
132
|
import { mkdir, writeFile } from "fs/promises";
|
|
4
133
|
import { join } from "path";
|
|
5
|
-
import { buildComputedManifest, validateComputedConfig } from "@vibeorm/schema";
|
|
134
|
+
import { VibeError as VibeError6, buildComputedManifest, validateComputedConfig } from "@vibeorm/schema";
|
|
6
135
|
|
|
7
136
|
// packages/generator/src/emit-dts.ts
|
|
8
|
-
import { DIALECT_CAPABILITIES, VibeError as
|
|
137
|
+
import { DIALECT_CAPABILITIES, VibeError as VibeError4, providerToDialect as providerToDialect3 } from "@vibeorm/schema";
|
|
9
138
|
|
|
10
139
|
// packages/generator/src/emit-js.ts
|
|
11
140
|
import { internalError } from "@vibeorm/schema";
|
|
12
141
|
|
|
13
142
|
// packages/generator/src/extension-bake.ts
|
|
14
143
|
import {
|
|
15
|
-
VibeError,
|
|
144
|
+
VibeError as VibeError3,
|
|
16
145
|
buildExtensionRegistry,
|
|
17
146
|
extensionDeclarations,
|
|
18
147
|
extensionManifestEntry,
|
|
19
|
-
providerToDialect
|
|
148
|
+
providerToDialect as providerToDialect2
|
|
20
149
|
} from "@vibeorm/schema";
|
|
21
150
|
var EMPTY_BAKED_EXTENSIONS = {
|
|
22
151
|
jsImports: [],
|
|
@@ -54,7 +183,7 @@ function readOnlyClassification(params) {
|
|
|
54
183
|
if (readOnly === undefined)
|
|
55
184
|
return;
|
|
56
185
|
const refuse = (problem) => {
|
|
57
|
-
throw new
|
|
186
|
+
throw new VibeError3({
|
|
58
187
|
code: "VIBE_CONFIG",
|
|
59
188
|
message: `extension "${extension.name}": readOnly ${problem}`,
|
|
60
189
|
meta: { extension: extension.name, kind: contribution.kind }
|
|
@@ -84,7 +213,7 @@ function bakeExtensions(params) {
|
|
|
84
213
|
if (params.extensions.length === 0)
|
|
85
214
|
return EMPTY_BAKED_EXTENSIONS;
|
|
86
215
|
buildExtensionRegistry({ extensions: params.extensions });
|
|
87
|
-
const dialect =
|
|
216
|
+
const dialect = providerToDialect2({ provider: schema.datasource.provider });
|
|
88
217
|
const provider = schema.datasource.provider;
|
|
89
218
|
const sorted = [...params.extensions].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
90
219
|
const jsImports = new ImportCollector;
|
|
@@ -115,7 +244,7 @@ function bakeExtensions(params) {
|
|
|
115
244
|
}
|
|
116
245
|
} else {
|
|
117
246
|
clientDts.push(...indent(contribution.dts));
|
|
118
|
-
const classified = readOnlyLine({ owner: "VibeClientInstance", property, readOnly });
|
|
247
|
+
const classified = readOnlyLine({ owner: schema.rls === undefined ? "VibeClientInstance" : "BoundVibeClientInstance", property, readOnly });
|
|
119
248
|
if (classified !== undefined)
|
|
120
249
|
readOnlyClientDts.push(classified);
|
|
121
250
|
}
|
|
@@ -141,7 +270,7 @@ function bakeExtensions(params) {
|
|
|
141
270
|
continue;
|
|
142
271
|
const clash = model.fields.some((field) => field.name === extension.name) || model.relations.some((relation) => relation.name === extension.name);
|
|
143
272
|
if (clash) {
|
|
144
|
-
throw new
|
|
273
|
+
throw new VibeError3({
|
|
145
274
|
code: "VIBE_CONFIG",
|
|
146
275
|
message: `extension "${extension.name}" collides with a field/relation named "${extension.name}" on model "${model.name}" \u2014 rename one of them`,
|
|
147
276
|
meta: { extension: extension.name, model: model.name }
|
|
@@ -178,71 +307,6 @@ function bakeExtensions(params) {
|
|
|
178
307
|
};
|
|
179
308
|
}
|
|
180
309
|
|
|
181
|
-
// packages/generator/src/util.ts
|
|
182
|
-
import { VibeError as VibeError2 } from "@vibeorm/schema";
|
|
183
|
-
function pascal(params) {
|
|
184
|
-
const { name } = params;
|
|
185
|
-
if (name.length === 0)
|
|
186
|
-
return name;
|
|
187
|
-
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
188
|
-
}
|
|
189
|
-
function clientNameOf(params) {
|
|
190
|
-
const { name } = params;
|
|
191
|
-
if (name.length === 0)
|
|
192
|
-
return name;
|
|
193
|
-
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
194
|
-
}
|
|
195
|
-
var IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
196
|
-
function propKey(params) {
|
|
197
|
-
const { name } = params;
|
|
198
|
-
return IDENTIFIER_RE.test(name) ? name : JSON.stringify(name);
|
|
199
|
-
}
|
|
200
|
-
function tsString(params) {
|
|
201
|
-
return JSON.stringify(params.value);
|
|
202
|
-
}
|
|
203
|
-
function maybeParens(params) {
|
|
204
|
-
const { type } = params;
|
|
205
|
-
return type.includes("|") ? `(${type})` : type;
|
|
206
|
-
}
|
|
207
|
-
function stableStringify(params) {
|
|
208
|
-
return renderValue({ value: params.value, indent: "" });
|
|
209
|
-
}
|
|
210
|
-
function renderValue(params) {
|
|
211
|
-
const { value, indent: indent2 } = params;
|
|
212
|
-
if (value === null)
|
|
213
|
-
return "null";
|
|
214
|
-
const kind = typeof value;
|
|
215
|
-
if (kind === "string" || kind === "number" || kind === "boolean")
|
|
216
|
-
return JSON.stringify(value);
|
|
217
|
-
if (Array.isArray(value)) {
|
|
218
|
-
if (value.length === 0)
|
|
219
|
-
return "[]";
|
|
220
|
-
const inner = `${indent2} `;
|
|
221
|
-
const items = value.map((item) => inner + renderValue({ value: item, indent: inner }));
|
|
222
|
-
return `[
|
|
223
|
-
${items.join(`,
|
|
224
|
-
`)}
|
|
225
|
-
${indent2}]`;
|
|
226
|
-
}
|
|
227
|
-
if (kind === "object") {
|
|
228
|
-
const record = value;
|
|
229
|
-
const keys = Object.keys(record).filter((key) => record[key] !== undefined).sort();
|
|
230
|
-
if (keys.length === 0)
|
|
231
|
-
return "{}";
|
|
232
|
-
const inner = `${indent2} `;
|
|
233
|
-
const items = keys.map((key) => `${inner}${JSON.stringify(key)}: ${renderValue({ value: record[key], indent: inner })}`);
|
|
234
|
-
return `{
|
|
235
|
-
${items.join(`,
|
|
236
|
-
`)}
|
|
237
|
-
${indent2}}`;
|
|
238
|
-
}
|
|
239
|
-
throw new VibeError2({
|
|
240
|
-
code: "VIBE_INTERNAL",
|
|
241
|
-
message: `internal: cannot serialize a ${kind} value into the generated client`,
|
|
242
|
-
meta: { kind }
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
|
|
246
310
|
// packages/generator/src/emit-js.ts
|
|
247
311
|
var HEADER = `// Generated by VibeORM (@vibeorm/generator) \u2014 DO NOT EDIT.
|
|
248
312
|
// Regenerate with \`vibeorm generate\` after every schema change.`;
|
|
@@ -459,13 +523,15 @@ var MINMAX_SCALARS = new Set([
|
|
|
459
523
|
function payloadBaseType(params) {
|
|
460
524
|
const { field } = params;
|
|
461
525
|
if (field.jsonType !== undefined)
|
|
462
|
-
return field.jsonType.name
|
|
526
|
+
return `import(${tsString({ value: field.jsonType.from })}).${field.jsonType.name}`;
|
|
463
527
|
return field.enumName ?? PAYLOAD_TYPES[field.scalar];
|
|
464
528
|
}
|
|
465
529
|
function inputBaseType(params) {
|
|
466
530
|
const { field } = params;
|
|
467
531
|
if (field.jsonType !== undefined)
|
|
468
|
-
return field.jsonType.name
|
|
532
|
+
return `import(${tsString({ value: field.jsonType.from })}).${field.jsonType.name}`;
|
|
533
|
+
if (field.isList && field.scalar === "DateTime")
|
|
534
|
+
return "Date | string";
|
|
469
535
|
return field.enumName ?? INPUT_TYPES[field.scalar];
|
|
470
536
|
}
|
|
471
537
|
function filterName(params) {
|
|
@@ -488,7 +554,7 @@ function banner(params) {
|
|
|
488
554
|
return `// \u2500\u2500\u2500 ${params.title} \u2500\u2500\u2500`;
|
|
489
555
|
}
|
|
490
556
|
function capabilitiesOf(params) {
|
|
491
|
-
return DIALECT_CAPABILITIES[
|
|
557
|
+
return DIALECT_CAPABILITIES[providerToDialect3({ provider: params.view.schema.datasource.provider })];
|
|
492
558
|
}
|
|
493
559
|
function literalUnion(params) {
|
|
494
560
|
return params.members.map((member) => tsString({ value: member })).join(" | ");
|
|
@@ -587,6 +653,10 @@ export type KeysetOptions = {
|
|
|
587
653
|
/** Result of createMany/updateMany/deleteMany. */
|
|
588
654
|
export type BatchPayload = { readonly count: number };
|
|
589
655
|
|
|
656
|
+
/** Static operation classification only; runtime verifies instance ownership and active lifetime. */
|
|
657
|
+
declare const vibeTransaction: unique symbol;
|
|
658
|
+
export type VibeQueryPromise<T, Access extends "read" | "write" = "read"> = QueryOperation<T, Access>;
|
|
659
|
+
|
|
590
660
|
/**
|
|
591
661
|
* What upsertMany reports. \`count\` is the rows submitted after duplicate-key
|
|
592
662
|
* validation \u2014 every one of them was inserted or updated, since the statement
|
|
@@ -609,7 +679,7 @@ type Provided<T> = Exclude<T, Skip | undefined>;
|
|
|
609
679
|
type OmittedKeys<O> = keyof { [K in keyof O as O[K] extends true ? K : never]: 0 };
|
|
610
680
|
|
|
611
681
|
/** Keys a selection has not RULED OUT \u2014 everything except a literal false/undefined. */
|
|
612
|
-
type PossibleKeys<S> = keyof { [K in keyof S as S[K] extends false | undefined ? never : K]: 0 };
|
|
682
|
+
type PossibleKeys<S> = keyof { [K in keyof S as S[K] extends false | undefined | Skip ? never : K]: 0 };
|
|
613
683
|
|
|
614
684
|
/**
|
|
615
685
|
* Keys whose flag can STILL be false or missing when the query runs \u2014 a widened
|
|
@@ -617,7 +687,7 @@ type PossibleKeys<S> = keyof { [K in keyof S as S[K] extends false | undefined ?
|
|
|
617
687
|
* result type must not promise a field a run-time flag can switch off.
|
|
618
688
|
*/
|
|
619
689
|
type UncertainKeys<S> = PossibleKeys<S> &
|
|
620
|
-
keyof { [K in keyof S as [false] extends [S[K]] ? K : [undefined] extends [S[K]] ? K : never]: 0 };
|
|
690
|
+
keyof { [K in keyof S as [false] extends [S[K]] ? K : [undefined] extends [S[K]] ? K : [Skip] extends [S[K]] ? K : never]: 0 };
|
|
621
691
|
|
|
622
692
|
/** Keys the selection definitely turns on: a literal \`true\`, or a nested selection object. */
|
|
623
693
|
type ExactKeys<S> = Exclude<PossibleKeys<S>, UncertainKeys<S>>;
|
|
@@ -892,7 +962,7 @@ function register(params) {
|
|
|
892
962
|
const existing = registry.get(name);
|
|
893
963
|
if (existing !== undefined) {
|
|
894
964
|
if (existing !== text) {
|
|
895
|
-
throw new
|
|
965
|
+
throw new VibeError4({
|
|
896
966
|
code: "VIBE_INTERNAL",
|
|
897
967
|
message: `internal: nested input type "${name}" generated with two different bodies`,
|
|
898
968
|
meta: { name }
|
|
@@ -1400,8 +1470,8 @@ export type ${n}UpdateWhereInput = ${n}WhereUniqueInput & ${n}WhereInput;`);
|
|
|
1400
1470
|
...aggregateOpProps
|
|
1401
1471
|
]
|
|
1402
1472
|
}));
|
|
1403
|
-
const exactPart = (source) => `${source}[K] extends false | undefined ? never : false extends ${source}[K] ? never : undefined extends ${source}[K] ? never : K`;
|
|
1404
|
-
const uncertainPart = (source) => `${source}[K] extends false | undefined ? never : false extends ${source}[K] ? K : undefined extends ${source}[K] ? K : never`;
|
|
1473
|
+
const exactPart = (source) => `${source}[K] extends false | undefined | Skip ? never : false extends ${source}[K] ? never : undefined extends ${source}[K] ? never : Skip extends ${source}[K] ? never : K`;
|
|
1474
|
+
const uncertainPart = (source) => `${source}[K] extends false | undefined | Skip ? never : false extends ${source}[K] ? K : undefined extends ${source}[K] ? K : Skip extends ${source}[K] ? K : never`;
|
|
1405
1475
|
parts.push(`type ${n}OmitBase<A> = A extends { readonly omit: infer O extends object }
|
|
1406
1476
|
` + ` ? Omit<${n}, PossibleKeys<O> & keyof ${n}> & { [K in UncertainKeys<O> & keyof ${n}]?: ${n}[K] }
|
|
1407
1477
|
` + ` : "omit" extends keyof A ? Omit<${n}, PossibleKeys<Provided<A["omit"]>> & keyof ${n}> & Partial<Pick<${n}, PossibleKeys<Provided<A["omit"]>> & keyof ${n}>> : ${n};`);
|
|
@@ -1411,10 +1481,11 @@ export type ${n}UpdateWhereInput = ${n}WhereUniqueInput & ${n}WhereInput;`);
|
|
|
1411
1481
|
parts.push(`type ${n}CountResult<C> = C extends { readonly select: infer CS extends object }
|
|
1412
1482
|
` + ` ? { [K in keyof CS & (${listUnion}) as ${exactPart("CS")}]: number } &
|
|
1413
1483
|
` + ` { [K in keyof CS & (${listUnion}) as ${uncertainPart("CS")}]?: number }
|
|
1414
|
-
` + ` : { ${allCounts} }
|
|
1484
|
+
` + ` : C extends { readonly select: Skip } ? { ${allCounts} }
|
|
1485
|
+
` + ` : "select" extends keyof C ? Partial<{ ${allCounts} }> : { ${allCounts} };`);
|
|
1415
1486
|
parts.push(`type ${n}CountPayload<S> = S extends { readonly _count: infer C extends true | object } ? { _count: ${n}CountResult<C> }
|
|
1416
|
-
` + ` : S extends { readonly _count: false | undefined } ? unknown
|
|
1417
|
-
` + ` : S extends { readonly _count?: infer C extends boolean | object } ? { _count?: ${n}CountResult<C
|
|
1487
|
+
` + ` : S extends { readonly _count: false | undefined | Skip } ? unknown
|
|
1488
|
+
` + ` : S extends { readonly _count?: infer C extends boolean | object | Skip } ? { _count?: ${n}CountResult<Provided<C>> } : unknown;`);
|
|
1418
1489
|
}
|
|
1419
1490
|
if (hasRelations) {
|
|
1420
1491
|
const relationCases = model.relations.map((relation) => {
|
|
@@ -1445,12 +1516,14 @@ ${relationCases}
|
|
|
1445
1516
|
` + ` { [K in keyof S & keyof ${n}Relations as ${exactPart("S")}]: ${n}RelationPayload<K, S[K]> } &
|
|
1446
1517
|
` + ` { [K in keyof S & keyof ${n}Relations as ${uncertainPart("S")}]?: ${n}RelationPayload<K, S[K]> }${hasCounts ? ` &
|
|
1447
1518
|
${n}CountPayload<S>` : ""}
|
|
1519
|
+
` + ` : A extends { readonly select: Skip } ? ${n}OmitBase<A> & ${n}IncludePayload<A>
|
|
1448
1520
|
` + ` : "select" extends keyof A ? Partial<${n}>
|
|
1449
1521
|
` + ` : ${n}OmitBase<A> & ${n}IncludePayload<A>;`);
|
|
1450
1522
|
} else {
|
|
1451
1523
|
parts.push(`export type ${n}GetPayload<A> = A extends { readonly select: infer S extends object }
|
|
1452
1524
|
` + ` ? { [K in keyof S & ${selectableEnum} as ${exactPart("S")}]: ${n}[K & keyof ${n}] } &
|
|
1453
1525
|
` + ` { [K in keyof S & ${selectableEnum} as ${uncertainPart("S")}]?: ${n}[K & keyof ${n}] }
|
|
1526
|
+
` + ` : A extends { readonly select: Skip } ? ${n}OmitBase<A>
|
|
1454
1527
|
` + ` : "select" extends keyof A ? Partial<${n}> : ${n}OmitBase<A>;`);
|
|
1455
1528
|
}
|
|
1456
1529
|
const aggregateFields = (fields, value) => `{ ${fields.map((field) => `${propKey({ name: field.name })}: ${value(field)}`).join("; ")} }`;
|
|
@@ -1470,33 +1543,33 @@ ${relationCases}
|
|
|
1470
1543
|
` + ` : "select" extends keyof A ? number | ${n}AggregateSelection<${n}AggregateValues["_count"], Provided<A["select"]>> : number;`);
|
|
1471
1544
|
parts.push(`export type ${n}AggregateResult<A> =
|
|
1472
1545
|
` + ` { [K in keyof A & keyof ${n}AggregateValues as HasSelectedField<A[K]> extends true ? K : never]: ${n}AggregateSelection<${n}AggregateValues[K], A[K]> } &
|
|
1473
|
-
` + ` { [K in keyof A & keyof ${n}AggregateValues as HasSelectedField<A[K]> extends true ? never : K]?: ${n}AggregateSelection<${n}AggregateValues[K], Provided<A[K]>> };`);
|
|
1546
|
+
` + ` { [K in keyof A & keyof ${n}AggregateValues as A[K] extends false | undefined | Skip ? never : HasSelectedField<A[K]> extends true ? never : K]?: ${n}AggregateSelection<${n}AggregateValues[K], Provided<A[K]>> };`);
|
|
1474
1547
|
parts.push(`export type ${n}GroupByResult<A extends ${n}GroupByArgs> =
|
|
1475
1548
|
` + ` (number extends A["by"]["length"] ? Partial<Pick<${n}, A["by"][number]>> : Pick<${n}, A["by"][number]>) & ${n}AggregateResult<A>;`);
|
|
1476
1549
|
const readMethods = [
|
|
1477
|
-
` findMany<A extends ${n}FindManyArgs = {}>(args?: A):
|
|
1478
|
-
` findFirst<A extends ${n}FindFirstArgs = {}>(args?: A):
|
|
1479
|
-
` findFirstOrThrow<A extends ${n}FindFirstArgs = {}>(args?: A):
|
|
1480
|
-
` findUnique<A extends ${n}FindUniqueArgs>(args: A):
|
|
1481
|
-
` findUniqueOrThrow<A extends ${n}FindUniqueArgs>(args: A):
|
|
1550
|
+
` findMany<A extends ${n}FindManyArgs = {}>(args?: A): VibeQueryPromise<${n}GetPayload<A>[]>;`,
|
|
1551
|
+
` findFirst<A extends ${n}FindFirstArgs = {}>(args?: A): VibeQueryPromise<${n}GetPayload<A> | null>;`,
|
|
1552
|
+
` findFirstOrThrow<A extends ${n}FindFirstArgs = {}>(args?: A): VibeQueryPromise<${n}GetPayload<A>>;`,
|
|
1553
|
+
` findUnique<A extends ${n}FindUniqueArgs>(args: A): VibeQueryPromise<${n}GetPayload<A> | null>;`,
|
|
1554
|
+
` findUniqueOrThrow<A extends ${n}FindUniqueArgs>(args: A): VibeQueryPromise<${n}GetPayload<A>>;`
|
|
1482
1555
|
];
|
|
1483
1556
|
const writeMethods = [
|
|
1484
|
-
` create<A extends ${n}CreateOrSkipArgs>(args: A):
|
|
1485
|
-
` create<A extends ${n}CreateArgs>(args: A):
|
|
1486
|
-
` createMany(args: ${n}CreateManyArgs):
|
|
1487
|
-
` createManyAndReturn<A extends ${n}CreateManyAndReturnArgs>(args: A):
|
|
1488
|
-
` update<A extends ${n}UpdateArgs>(args: A):
|
|
1489
|
-
` updateMany(args: ${n}UpdateManyArgs):
|
|
1490
|
-
` updateManyAndReturn<A extends ${n}UpdateManyAndReturnArgs>(args: A):
|
|
1491
|
-
` upsert<A extends ${n}UpsertArgs>(args: A):
|
|
1492
|
-
` upsertMany(args: ${n}UpsertManyArgs):
|
|
1493
|
-
` delete<A extends ${n}DeleteArgs>(args: A):
|
|
1494
|
-
` deleteMany(args?: ${n}DeleteManyArgs):
|
|
1557
|
+
` create<A extends ${n}CreateOrSkipArgs>(args: A): VibeQueryPromise<${n}GetPayload<A> | null, "write">;`,
|
|
1558
|
+
` create<A extends ${n}CreateArgs>(args: A): VibeQueryPromise<${n}GetPayload<A>, "write">;`,
|
|
1559
|
+
` createMany(args: ${n}CreateManyArgs): VibeQueryPromise<BatchPayload, "write">;`,
|
|
1560
|
+
` createManyAndReturn<A extends ${n}CreateManyAndReturnArgs>(args: A): VibeQueryPromise<${n}GetPayload<A>[], "write">;`,
|
|
1561
|
+
` update<A extends ${n}UpdateArgs>(args: A): VibeQueryPromise<${n}GetPayload<A>, "write">;`,
|
|
1562
|
+
` updateMany(args: ${n}UpdateManyArgs): VibeQueryPromise<BatchPayload, "write">;`,
|
|
1563
|
+
` updateManyAndReturn<A extends ${n}UpdateManyAndReturnArgs>(args: A): VibeQueryPromise<${n}GetPayload<A>[], "write">;`,
|
|
1564
|
+
` upsert<A extends ${n}UpsertArgs>(args: A): VibeQueryPromise<${n}GetPayload<A>, "write">;`,
|
|
1565
|
+
` upsertMany(args: ${n}UpsertManyArgs): VibeQueryPromise<UpsertManyPayload, "write">;`,
|
|
1566
|
+
` delete<A extends ${n}DeleteArgs>(args: A): VibeQueryPromise<${n}GetPayload<A>, "write">;`,
|
|
1567
|
+
` deleteMany(args?: ${n}DeleteManyArgs): VibeQueryPromise<BatchPayload, "write">;`
|
|
1495
1568
|
];
|
|
1496
1569
|
const aggregateMethods = [
|
|
1497
|
-
` count<A extends ${n}CountArgs = {}>(args?: A):
|
|
1498
|
-
` aggregate<A extends ${n}AggregateArgs>(args: A):
|
|
1499
|
-
` groupBy<const A extends ${n}GroupByArgs>(args: A):
|
|
1570
|
+
` count<A extends ${n}CountArgs = {}>(args?: A): VibeQueryPromise<${n}CountOutput<A>>;`,
|
|
1571
|
+
` aggregate<A extends ${n}AggregateArgs>(args: A): VibeQueryPromise<${n}AggregateResult<A>>;`,
|
|
1572
|
+
` groupBy<const A extends ${n}GroupByArgs>(args: A): VibeQueryPromise<${n}GroupByResult<A>[]>;`
|
|
1500
1573
|
];
|
|
1501
1574
|
const delegateMethods = model.isView ? [...readMethods, ...aggregateMethods] : [...readMethods, ...writeMethods, ...aggregateMethods];
|
|
1502
1575
|
delegateMethods.push(...params.extensionLines ?? []);
|
|
@@ -1570,8 +1643,8 @@ function emitViewTypesForModel(params) {
|
|
|
1570
1643
|
` readonly relationStrategy?: RelationStrategy;`
|
|
1571
1644
|
]
|
|
1572
1645
|
}));
|
|
1573
|
-
const exactPart = (source) => `${source}[K] extends false | undefined ? never : false extends ${source}[K] ? never : undefined extends ${source}[K] ? never : K`;
|
|
1574
|
-
const uncertainPart = (source) => `${source}[K] extends false | undefined ? never : false extends ${source}[K] ? K : undefined extends ${source}[K] ? K : never`;
|
|
1646
|
+
const exactPart = (source) => `${source}[K] extends false | undefined | Skip ? never : false extends ${source}[K] ? never : undefined extends ${source}[K] ? never : Skip extends ${source}[K] ? never : K`;
|
|
1647
|
+
const uncertainPart = (source) => `${source}[K] extends false | undefined | Skip ? never : false extends ${source}[K] ? K : undefined extends ${source}[K] ? K : Skip extends ${source}[K] ? K : never`;
|
|
1575
1648
|
parts.push(`type ${n}ViewSelectScalars<F, A> = A extends { readonly select: infer S extends object }
|
|
1576
1649
|
` + ` ? { [K in keyof S & keyof F & ${n}ScalarFieldEnum as ${exactPart("S")}]: ${n}[K & keyof ${n}] } &
|
|
1577
1650
|
` + ` { [K in keyof S & keyof F & ${n}ScalarFieldEnum as ${uncertainPart("S")}]?: ${n}[K & keyof ${n}] }
|
|
@@ -1651,19 +1724,6 @@ ${view.models.map((model) => ` ${propKey({ name: model.clientName })}: ${model.
|
|
|
1651
1724
|
}
|
|
1652
1725
|
return parts;
|
|
1653
1726
|
}
|
|
1654
|
-
function jsonTypeImportLines(params) {
|
|
1655
|
-
const bySpecifier = new Map;
|
|
1656
|
-
for (const model of params.view.models) {
|
|
1657
|
-
for (const field of model.fields) {
|
|
1658
|
-
if (field.jsonType === undefined)
|
|
1659
|
-
continue;
|
|
1660
|
-
const names = bySpecifier.get(field.jsonType.from) ?? new Set;
|
|
1661
|
-
names.add(field.jsonType.name);
|
|
1662
|
-
bySpecifier.set(field.jsonType.from, names);
|
|
1663
|
-
}
|
|
1664
|
-
}
|
|
1665
|
-
return [...bySpecifier.keys()].sort().map((from) => `import type { ${[...bySpecifier.get(from)].sort().join(", ")} } from ${tsString({ value: from })};`);
|
|
1666
|
-
}
|
|
1667
1727
|
function hasNativeRls(params) {
|
|
1668
1728
|
return params.view.schema.rls !== undefined;
|
|
1669
1729
|
}
|
|
@@ -1806,7 +1866,7 @@ function transactionLines(params) {
|
|
|
1806
1866
|
return `${doc}
|
|
1807
1867
|
` + ` readonly $transaction: {
|
|
1808
1868
|
` + ` <T>(fn: (tx: ${transactionType}) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
1809
|
-
` + ` <T extends readonly unknown[]>(operations: readonly [...T], options?: TransactionOptions): Promise<{ -readonly [K in keyof T]: Awaited<T[K]> }>;
|
|
1869
|
+
` + ` <T extends readonly VibeQueryPromise<unknown, ${reads ? '"read"' : '"read" | "write"'}>[]>(operations: readonly [...T], options?: TransactionOptions): Promise<{ -readonly [K in keyof T]: Awaited<T[K]> }>;
|
|
1810
1870
|
` + ` };
|
|
1811
1871
|
`;
|
|
1812
1872
|
}
|
|
@@ -1828,7 +1888,8 @@ var READ_ONLY_LINE = ` /** A read-only handle over this one: reads pass through
|
|
|
1828
1888
|
`;
|
|
1829
1889
|
function instanceSurfaceLines(params) {
|
|
1830
1890
|
const { view, transactionType, inTransaction, selfLabel } = params;
|
|
1831
|
-
return
|
|
1891
|
+
return (inTransaction ? ` readonly [vibeTransaction]: true;
|
|
1892
|
+
` : "") + delegateSurfaceLines({ view, delegateSuffix: "Delegate" }) + transactionLines({ transactionType, reads: false }) + RAW_SURFACE_LINES + (inTransaction ? advisoryLockLines({ view, selfLabel }) : "") + (inTransaction ? "" : CONNECTION_SURFACE_LINES) + DEFINE_VIEW_LINE;
|
|
1832
1893
|
}
|
|
1833
1894
|
function telemetryDeclarations() {
|
|
1834
1895
|
return [
|
|
@@ -1945,8 +2006,12 @@ function emitClient(params) {
|
|
|
1945
2006
|
name: "VibeClientOptions",
|
|
1946
2007
|
lines: [
|
|
1947
2008
|
` readonly adapter: DatabaseAdapter;`,
|
|
2009
|
+
` /** Strict argument validation defaults to true; false restores legacy omission of undefined values. */`,
|
|
2010
|
+
` readonly strictArguments?: boolean;`,
|
|
1948
2011
|
` readonly onQuery?: (event: QueryEvent) => void;`,
|
|
1949
2012
|
` readonly relationStrategy?: RelationStrategy;`,
|
|
2013
|
+
` /** Opt-in concurrent relation loading: how many of ONE query's relation statements (siblings, \`_count\` entries, key chunks) may be in flight at once. Default 1; ignored inside \`$transaction\`. */`,
|
|
2014
|
+
` readonly relationLoading?: { readonly concurrency?: number };`,
|
|
1950
2015
|
` /** Append the primary key to ORDER BY when a find has no explicit orderBy (default false) \u2014 deterministic row order. */`,
|
|
1951
2016
|
` readonly defaultOrderByPk?: boolean;`,
|
|
1952
2017
|
` /** Append the primary key as a tie-breaker to \`distinct\` ordering (default true) \u2014 pins WHICH row represents each distinct key. */`,
|
|
@@ -1955,6 +2020,8 @@ function emitClient(params) {
|
|
|
1955
2020
|
` readonly validate?: WriteValidators;`,
|
|
1956
2021
|
` /** Opt-in operation telemetry: one event per public ORM call, with the statements it ran. Parameter values are redacted by default and the exporter can never change a database outcome. */`,
|
|
1957
2022
|
` readonly telemetry?: VibeTelemetryOptions;`,
|
|
2023
|
+
` /** Statement diagnostics: every statement over \`slowerThanMs\` is reported with its duration and, with \`explain\`, an automatic EXPLAIN summary on PostgreSQL/PGlite (\`"analyze"\` re-executes read statements). \`VIBEORM_DIAGNOSTICS=analyze:5\` switches it on for a run; local runs only. */`,
|
|
2024
|
+
` readonly diagnostics?: StatementDiagnosticsOptions;`,
|
|
1958
2025
|
...baked.empty ? [] : [
|
|
1959
2026
|
` /** The live extension instances \u2014 verified against the baked manifest. */`,
|
|
1960
2027
|
` readonly extensions?: readonly VibeExtension[];`
|
|
@@ -1973,7 +2040,7 @@ function emitClient(params) {
|
|
|
1973
2040
|
`)}
|
|
1974
2041
|
`;
|
|
1975
2042
|
if (nativeRls) {
|
|
1976
|
-
parts.push(`export type BoundVibeClientInstance = {
|
|
2043
|
+
parts.push(`export type BoundVibeClientInstance = ClientContext<"restricted"> & {
|
|
1977
2044
|
` + instanceSurfaceLines({
|
|
1978
2045
|
view,
|
|
1979
2046
|
transactionType: "BoundVibeTransactionClientInstance",
|
|
@@ -1981,7 +2048,7 @@ function emitClient(params) {
|
|
|
1981
2048
|
selfLabel: "db"
|
|
1982
2049
|
}) + READ_ONLY_LINE + clientExtensionLines + `};`);
|
|
1983
2050
|
parts.push(`/** The bound handle inside \`$transaction\`: same identity, plus the locks that require a transaction, minus the connection lifecycle the transaction owns. */
|
|
1984
|
-
` + `export type BoundVibeTransactionClientInstance = {
|
|
2051
|
+
` + `export type BoundVibeTransactionClientInstance = ClientContext<"restricted"> & {
|
|
1985
2052
|
` + instanceSurfaceLines({
|
|
1986
2053
|
view,
|
|
1987
2054
|
transactionType: "BoundVibeTransactionClientInstance",
|
|
@@ -1989,7 +2056,7 @@ function emitClient(params) {
|
|
|
1989
2056
|
selfLabel: "tx"
|
|
1990
2057
|
}) + READ_ONLY_LINE + clientExtensionLines + ` // Deliberately ABSENT: $connect / $disconnect (the open transaction owns the connection) and $withContext (identity is bound once, from the root).
|
|
1991
2058
|
` + `};`);
|
|
1992
|
-
parts.push(`export type VibeClientInstance = {
|
|
2059
|
+
parts.push(`export type VibeClientInstance = ClientContext<"restricted"> & {
|
|
1993
2060
|
` + ` /** Bind the request's identity context \u2014 ALL of it, every declared slot \u2014 and get the client that can actually query. */
|
|
1994
2061
|
` + ` readonly $withContext: (context: RlsContext) => BoundVibeClientInstance;
|
|
1995
2062
|
` + ` /** Subscribe to client events ("query" fires after every statement, with the same payload as onQuery \u2014 both fire). Returns an unsubscribe function. */
|
|
@@ -1999,7 +2066,7 @@ function emitClient(params) {
|
|
|
1999
2066
|
` + ` // Deliberately ABSENT: model delegates, $models, $transaction, $queryRaw / $executeRaw / $queryRawUnsafe / $executeRawUnsafe, $advisoryLock / $tryAdvisoryLock, $defineView, $readOnly and every extension mount \u2014 an unbound client has no identity, so it cannot read or write a row. Bind one with $withContext.
|
|
2000
2067
|
` + `};`);
|
|
2001
2068
|
} else {
|
|
2002
|
-
parts.push(`export type VibeClientInstance = {
|
|
2069
|
+
parts.push(`export type VibeClientInstance = ClientContext<"root"> & {
|
|
2003
2070
|
` + instanceSurfaceLines({
|
|
2004
2071
|
view,
|
|
2005
2072
|
transactionType: "VibeTransactionClientInstance",
|
|
@@ -2007,7 +2074,7 @@ function emitClient(params) {
|
|
|
2007
2074
|
selfLabel: "db"
|
|
2008
2075
|
}) + policyClientLines({ view, selfType: "VibeClientInstance" }) + scopedClientLines({ view, scopedType: "ScopedClientInstance" }) + READ_ONLY_LINE + clientExtensionLines + `};`);
|
|
2009
2076
|
parts.push(`/** The handle \`$transaction\` gives its callback. A helper that REQUIRES a transaction takes this type, and an ordinary client no longer satisfies it. */
|
|
2010
|
-
` + `export type VibeTransactionClientInstance = {
|
|
2077
|
+
` + `export type VibeTransactionClientInstance = ClientContext<"transaction"> & {
|
|
2011
2078
|
` + instanceSurfaceLines({
|
|
2012
2079
|
view,
|
|
2013
2080
|
transactionType: "VibeTransactionClientInstance",
|
|
@@ -2053,7 +2120,8 @@ function emitScopedSection(params) {
|
|
|
2053
2120
|
const byClientName = ` readonly ${propKey({ name: model.clientName })}: ${scopedDelegateType(model)};`;
|
|
2054
2121
|
return model.name === model.clientName ? [byClientName] : [byClientName, ` readonly ${propKey({ name: model.name })}: ${scopedDelegateType(model)};`];
|
|
2055
2122
|
});
|
|
2056
|
-
const scopedSurface = (params2) =>
|
|
2123
|
+
const scopedSurface = (params2) => (params2.inTransaction ? ` readonly [vibeTransaction]: true;
|
|
2124
|
+
` : "") + `${delegateProps.join(`
|
|
2057
2125
|
`)}
|
|
2058
2126
|
` + ` /** Delegates keyed by BOTH camelCase client name and model name \u2014 the same objects as the top-level properties (dynamic access). */
|
|
2059
2127
|
` + ` readonly $models: {
|
|
@@ -2065,7 +2133,7 @@ ${modelsProps.join(`
|
|
|
2065
2133
|
` + ` /** ONE transaction; the callback receives a handle that INHERITS the scope. The array form takes un-awaited scoped delegate calls. */
|
|
2066
2134
|
` + ` readonly $transaction: {
|
|
2067
2135
|
` + ` <T>(fn: (tx: ${params2.transactionType}) => Promise<T>, options?: TransactionOptions): Promise<T>;
|
|
2068
|
-
` + ` <T extends readonly unknown[]>(operations: readonly [...T], options?: TransactionOptions): Promise<{ -readonly [K in keyof T]: Awaited<T[K]> }>;
|
|
2136
|
+
` + ` <T extends readonly VibeQueryPromise<unknown, "read" | "write">[]>(operations: readonly [...T], options?: TransactionOptions): Promise<{ -readonly [K in keyof T]: Awaited<T[K]> }>;
|
|
2069
2137
|
` + ` };
|
|
2070
2138
|
` + (params2.inTransaction ? advisoryLockLines({ view, selfLabel: "tx" }) : CONNECTION_SURFACE_LINES) + ` /** Field-masking views compose over the scoped handle (columns \xD7 rows). */
|
|
2071
2139
|
` + ` readonly $defineView: <D extends ViewDefinitionInput>(options: { readonly name: string; readonly definition: D }) => ViewClient<D>;
|
|
@@ -2133,7 +2201,7 @@ function emitReadOnlySection(params) {
|
|
|
2133
2201
|
` + ` * await reader.user.findMany(); // fine
|
|
2134
2202
|
` + ` * await reader.user.upsertMany({ \u2026 }); // VIBE_READ_ONLY, and a compile error
|
|
2135
2203
|
` + ` */
|
|
2136
|
-
` + `export type ReadOnlyClientInstance = {
|
|
2204
|
+
` + `export type ReadOnlyClientInstance = ClientContext<"readOnly"> & {
|
|
2137
2205
|
` + delegateSurfaceLines({ view, delegateSuffix: "ReadOnlyDelegate" }) + transactionLines({ transactionType: "ReadOnlyClientInstance", reads: true }) + DEFINE_VIEW_LINE + policyClientLines({ view, selfType: "ReadOnlyClientInstance" }) + scopedClientLines({ view, scopedType: "ReadOnlyClientInstance" }) + ` /** Idempotent \u2014 restricting an already-restricted handle returns the same handle. */
|
|
2138
2206
|
` + ` readonly $readOnly: () => ReadOnlyClientInstance;
|
|
2139
2207
|
` + extensionLines + ` // Deliberately ABSENT: every write method, $queryRaw / $executeRaw / $queryRawUnsafe / $executeRawUnsafe, $connect / $disconnect, $advisoryLock / $tryAdvisoryLock, $withContext, and every extension surface its author did not classify as a read.
|
|
@@ -2159,6 +2227,8 @@ function emitDts(params) {
|
|
|
2159
2227
|
...hasComputed ? ["ComputedConfig"] : []
|
|
2160
2228
|
].sort();
|
|
2161
2229
|
const runtimeTypeImports = [
|
|
2230
|
+
"ClientContext",
|
|
2231
|
+
"QueryOperation",
|
|
2162
2232
|
"SqlFragment",
|
|
2163
2233
|
"ResultDecoder",
|
|
2164
2234
|
"SqlColumn",
|
|
@@ -2166,6 +2236,7 @@ function emitDts(params) {
|
|
|
2166
2236
|
"DbNow",
|
|
2167
2237
|
"QueryEvent",
|
|
2168
2238
|
"Skip",
|
|
2239
|
+
"StatementDiagnosticsOptions",
|
|
2169
2240
|
"TransactionOptions",
|
|
2170
2241
|
"WriteValidators",
|
|
2171
2242
|
...capabilitiesOf({ view }).advisoryLocks ? ["AdvisoryLockOptions"] : []
|
|
@@ -2173,7 +2244,6 @@ function emitDts(params) {
|
|
|
2173
2244
|
const importLines = [
|
|
2174
2245
|
`import type { ${runtimeTypeImports.join(", ")} } from "@vibeorm/runtime";`,
|
|
2175
2246
|
`import type { ${schemaTypeImports.join(", ")} } from "@vibeorm/schema";`,
|
|
2176
|
-
...jsonTypeImportLines({ view }),
|
|
2177
2247
|
...baked.dtsImports,
|
|
2178
2248
|
`import { ${enumEntryExports({ view }).join(", ")} } from "./enums.js";`
|
|
2179
2249
|
];
|
|
@@ -2235,7 +2305,7 @@ ${view.models.map((model) => ` readonly ${propKey({ name: model.clientName })}:
|
|
|
2235
2305
|
}
|
|
2236
2306
|
|
|
2237
2307
|
// packages/generator/src/schema-view.ts
|
|
2238
|
-
import { VibeError as
|
|
2308
|
+
import { VibeError as VibeError5, assertValidSchemaIR, enumStorageOf, isEnumType } from "@vibeorm/schema";
|
|
2239
2309
|
import { postgresDialect, renderRlsPolicy } from "@vibeorm/sql";
|
|
2240
2310
|
var JSON_TYPE_RE = /@type\(\s*([A-Za-z_$][A-Za-z0-9_$]*)\s+from\s+"([^"]+)"\s*\)/;
|
|
2241
2311
|
function parseJsonTypeAnnotation(params) {
|
|
@@ -2254,7 +2324,7 @@ function buildFieldView(params) {
|
|
|
2254
2324
|
const enumRef = isEnumType(field.type);
|
|
2255
2325
|
const annotation = parseJsonTypeAnnotation({ comment: field.documentation });
|
|
2256
2326
|
if (annotation !== null && (enumRef || field.type !== "Json")) {
|
|
2257
|
-
throw new
|
|
2327
|
+
throw new VibeError5({
|
|
2258
2328
|
code: "VIBE_SCHEMA_INVALID",
|
|
2259
2329
|
message: `@type(...) annotation on ${model}.${field.name} \u2014 typed JSON is only valid on Json fields`,
|
|
2260
2330
|
meta: { model, field: field.name }
|
|
@@ -2328,7 +2398,7 @@ function buildModelView(params) {
|
|
|
2328
2398
|
target: relation.targetModel,
|
|
2329
2399
|
type: relation.type,
|
|
2330
2400
|
isList: relation.isList,
|
|
2331
|
-
isOptional: relation.isOptional,
|
|
2401
|
+
isOptional: relation.isOptional || !relation.isList && (relation.fields?.length ?? 0) === 0,
|
|
2332
2402
|
fkFields: relation.fields ?? [],
|
|
2333
2403
|
inverseName: target === undefined ? null : resolveInverse({ relation, target })
|
|
2334
2404
|
};
|
|
@@ -2391,12 +2461,15 @@ async function generateClient(params) {
|
|
|
2391
2461
|
validateComputedConfig({ schema, computed: params.computed });
|
|
2392
2462
|
computed = buildComputedManifest({ computed: params.computed });
|
|
2393
2463
|
}
|
|
2464
|
+
if (params.module !== undefined && (!baked.empty || computed.length > 0))
|
|
2465
|
+
throw new VibeError6({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: "Package modules do not support computed fields or extension mounts in this protocol", meta: { reason: "module_schema_feature", moduleId: params.module.id } });
|
|
2394
2466
|
const outputs = [
|
|
2395
2467
|
{ name: "index.js", content: emitJs({ view, baked, computed }) },
|
|
2396
2468
|
{ name: "index.d.ts", content: emitDts({ view, baked, computed }) },
|
|
2397
2469
|
{ name: "enums.js", content: emitEnumsJs({ view }) },
|
|
2398
2470
|
{ name: "enums.d.ts", content: emitEnumsDts({ view }) },
|
|
2399
|
-
{ name: "README.md", content: README }
|
|
2471
|
+
{ name: "README.md", content: README },
|
|
2472
|
+
...params.module === undefined ? [] : emitDatabaseModule({ view, module: params.module })
|
|
2400
2473
|
];
|
|
2401
2474
|
await mkdir(outDir, { recursive: true });
|
|
2402
2475
|
const files = [];
|
|
@@ -2412,4 +2485,4 @@ export {
|
|
|
2412
2485
|
bakeExtensions
|
|
2413
2486
|
};
|
|
2414
2487
|
|
|
2415
|
-
//# debugId=
|
|
2488
|
+
//# debugId=732A6C5C8F7D397B64756E2164756E21
|