@vibeorm/generator 2.2.3 → 3.0.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/LICENSE +21 -0
- package/dist/emit-dts.d.ts +11 -0
- package/dist/generate.d.ts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +232 -59
- package/dist/index.js.map +10 -9
- package/dist/module-schema.d.ts +3 -0
- package/dist/type-names.d.ts +5 -0
- package/dist/util.d.ts +4 -0
- package/package.json +6 -4
- package/dist/emit-dts.d.ts.map +0 -1
- package/dist/emit-js.d.ts.map +0 -1
- package/dist/emit-module.d.ts.map +0 -1
- package/dist/extension-bake.d.ts.map +0 -1
- package/dist/generate.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/schema-view.d.ts.map +0 -1
- package/dist/type-names.d.ts.map +0 -1
- package/dist/util.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
// packages/generator/src/module-schema.ts
|
|
3
|
+
import { prepareDatabaseModuleSchema } from "@vibeorm/schema";
|
|
2
4
|
// packages/generator/src/emit-module.ts
|
|
3
5
|
import { createHash } from "crypto";
|
|
4
6
|
import { VibeError as VibeError2, providerToDialect } from "@vibeorm/schema";
|
|
@@ -30,10 +32,13 @@ function maybeParens(params) {
|
|
|
30
32
|
return type.includes("|") ? `(${type})` : type;
|
|
31
33
|
}
|
|
32
34
|
function stableStringify(params) {
|
|
33
|
-
return renderValue({ value: params.value, indent: "" });
|
|
35
|
+
return renderValue({ value: params.value, indent: "", javascript: false });
|
|
36
|
+
}
|
|
37
|
+
function stableJs(params) {
|
|
38
|
+
return renderValue({ value: params.value, indent: "", javascript: true });
|
|
34
39
|
}
|
|
35
40
|
function renderValue(params) {
|
|
36
|
-
const { value, indent } = params;
|
|
41
|
+
const { value, indent, javascript } = params;
|
|
37
42
|
if (value === null)
|
|
38
43
|
return "null";
|
|
39
44
|
const kind = typeof value;
|
|
@@ -43,7 +48,7 @@ function renderValue(params) {
|
|
|
43
48
|
if (value.length === 0)
|
|
44
49
|
return "[]";
|
|
45
50
|
const inner = `${indent} `;
|
|
46
|
-
const items = value.map((item) => inner + renderValue({ value: item, indent: inner }));
|
|
51
|
+
const items = value.map((item) => inner + renderValue({ value: item, indent: inner, javascript }));
|
|
47
52
|
return `[
|
|
48
53
|
${items.join(`,
|
|
49
54
|
`)}
|
|
@@ -55,7 +60,7 @@ ${indent}]`;
|
|
|
55
60
|
if (keys.length === 0)
|
|
56
61
|
return "{}";
|
|
57
62
|
const inner = `${indent} `;
|
|
58
|
-
const items = keys.map((key) => `${inner}${JSON.stringify(key)}: ${renderValue({ value: record[key], indent: inner })}`);
|
|
63
|
+
const items = keys.map((key) => `${inner}${javascript && key === "__proto__" ? `[${JSON.stringify(key)}]` : JSON.stringify(key)}: ${renderValue({ value: record[key], indent: inner, javascript })}`);
|
|
59
64
|
return `{
|
|
60
65
|
${items.join(`,
|
|
61
66
|
`)}
|
|
@@ -117,7 +122,7 @@ export { sql, decode, sqlModels, sqlProjection } from "./index.js";
|
|
|
117
122
|
export const databaseModule = defineDatabaseModule({ ...${JSON.stringify(module)}, schema, manifest: ${JSON.stringify(manifest)} });
|
|
118
123
|
` },
|
|
119
124
|
{ name: "schema.js", content: `// Generated data-only schema and runtime manifest.
|
|
120
|
-
export const schema = ${
|
|
125
|
+
export const schema = ${stableJs({ value: view.schema })};
|
|
121
126
|
export const moduleManifest = ${JSON.stringify(manifest)};
|
|
122
127
|
` },
|
|
123
128
|
{ name: "schema.d.ts", content: `import type { SchemaIR, DatabaseModuleManifest } from "@vibeorm/schema";
|
|
@@ -369,7 +374,7 @@ ${validatorLines}
|
|
|
369
374
|
function serializeSchema(params) {
|
|
370
375
|
const { schema } = params;
|
|
371
376
|
if (schema.rls === undefined)
|
|
372
|
-
return
|
|
377
|
+
return stableJs({ value: schema });
|
|
373
378
|
return renderIr({ value: schema, indent: "", ordered: false });
|
|
374
379
|
}
|
|
375
380
|
function renderIr(params) {
|
|
@@ -398,7 +403,7 @@ ${indent}]`;
|
|
|
398
403
|
if (keys.length === 0)
|
|
399
404
|
return "{}";
|
|
400
405
|
const inner = `${indent} `;
|
|
401
|
-
const items = keys.map((key) => `${inner}${JSON.stringify(key)}: ${renderIr({ value: record[key], indent: inner, ordered: ordered || key === "rls" })}`);
|
|
406
|
+
const items = keys.map((key) => `${inner}${key === "__proto__" ? `[${JSON.stringify(key)}]` : JSON.stringify(key)}: ${renderIr({ value: record[key], indent: inner, ordered: ordered || key === "rls" })}`);
|
|
402
407
|
return `{
|
|
403
408
|
${items.join(`,
|
|
404
409
|
`)}
|
|
@@ -439,7 +444,7 @@ ${view.models.map((model) => ` [${tsString({ value: model.clientName })}]: (opt
|
|
|
439
444
|
}
|
|
440
445
|
if (!baked.empty) {
|
|
441
446
|
parts.push(`/** Extension manifest \u2014 verified against the live extension list (VIBE_STALE_CLIENT). */
|
|
442
|
-
` + `const extensionManifest = ${
|
|
447
|
+
` + `const extensionManifest = ${stableJs({ value: baked.manifest })};`);
|
|
443
448
|
parts.push(`/** Baked extension surfaces (extensions.md D9). */
|
|
444
449
|
` + `const extensionMounts = [
|
|
445
450
|
${baked.mounts.map((mount) => ` ${mount},`).join(`
|
|
@@ -448,7 +453,7 @@ ${baked.mounts.map((mount) => ` ${mount},`).join(`
|
|
|
448
453
|
}
|
|
449
454
|
if (hasComputed) {
|
|
450
455
|
parts.push(`/** Computed-field manifest \u2014 verified against the live computed config (VIBE_STALE_CLIENT). */
|
|
451
|
-
` + `const computedManifest = ${
|
|
456
|
+
` + `const computedManifest = ${stableJs({ value: computed })};`);
|
|
452
457
|
}
|
|
453
458
|
const destructured = [
|
|
454
459
|
"adapter",
|
|
@@ -534,6 +539,15 @@ function inputBaseType(params) {
|
|
|
534
539
|
return "Date | string";
|
|
535
540
|
return field.enumName ?? INPUT_TYPES[field.scalar];
|
|
536
541
|
}
|
|
542
|
+
function aggregateOperandType(params) {
|
|
543
|
+
const { field, key } = params;
|
|
544
|
+
if (key === "_count")
|
|
545
|
+
return "number";
|
|
546
|
+
if (key === "_avg")
|
|
547
|
+
return "number";
|
|
548
|
+
const value = field.scalar === "DateTime" ? "Date | string" : inputBaseType({ field });
|
|
549
|
+
return value;
|
|
550
|
+
}
|
|
537
551
|
function filterName(params) {
|
|
538
552
|
const { field } = params;
|
|
539
553
|
const prefix = field.enumName ?? FILTER_PREFIX[field.scalar];
|
|
@@ -589,6 +603,12 @@ function orMany(params) {
|
|
|
589
603
|
const { type } = params;
|
|
590
604
|
return `${type} | readonly ${maybeParens({ type })}[]`;
|
|
591
605
|
}
|
|
606
|
+
function aggregatePredicateType(params) {
|
|
607
|
+
const { value } = params;
|
|
608
|
+
const skip = params.skippable === false ? "" : " | Skip";
|
|
609
|
+
const operators = ["equals", "not", "gt", "gte", "lt", "lte"];
|
|
610
|
+
return `${value} | { ${operators.map((operator) => `readonly ${operator}?: ${value}${skip}`).join("; ")} }`;
|
|
611
|
+
}
|
|
592
612
|
function payloadFieldType(params) {
|
|
593
613
|
const { field } = params;
|
|
594
614
|
const base = payloadBaseType({ field });
|
|
@@ -638,16 +658,25 @@ export type RelationStrategy = "query" | "join";
|
|
|
638
658
|
* query still applies its own where/scoped/policy/RLS filters.
|
|
639
659
|
*/
|
|
640
660
|
export type KeysetToken = string;
|
|
661
|
+
/** A bounded page; the continuation is null when no lookahead row exists. */
|
|
662
|
+
export type KeysetPage<T> = { readonly items: T[]; readonly nextCursor: KeysetToken | null };
|
|
641
663
|
|
|
642
664
|
/** Per-query keyset options. */
|
|
643
665
|
export type KeysetOptions = {
|
|
644
666
|
/**
|
|
645
667
|
* Asserts that every DateTime ordering column in this query holds
|
|
646
|
-
* millisecond-exact values.
|
|
647
|
-
* may be finer than a JavaScript Date
|
|
648
|
-
*
|
|
668
|
+
* millisecond-exact values. Needed on postgres and mysql only for a column
|
|
669
|
+
* that may be finer than a JavaScript Date \u2014 one without a declared
|
|
670
|
+
* precision of 0-3; a column declared \`@db.Timestamptz(3)\` needs none. NOT
|
|
671
|
+
* safe for a microsecond \`@default(now())\` column.
|
|
649
672
|
*/
|
|
650
673
|
readonly timestampPrecision?: "milliseconds";
|
|
674
|
+
/**
|
|
675
|
+
* The owner a page token belongs to (a user or tenant id). Mixed into the
|
|
676
|
+
* token's query binding beside the filters, so a token minted for one scope
|
|
677
|
+
* is refused for another. Encoded, not secret.
|
|
678
|
+
*/
|
|
679
|
+
readonly scope?: string;
|
|
651
680
|
};
|
|
652
681
|
|
|
653
682
|
/** Result of createMany/updateMany/deleteMany. */
|
|
@@ -675,6 +704,21 @@ export type UpsertManyPayload = { readonly count: number; readonly affectedRows:
|
|
|
675
704
|
*/
|
|
676
705
|
type Provided<T> = Exclude<T, Skip | undefined>;
|
|
677
706
|
|
|
707
|
+
/** An absent projection member supplies the inactive fallback value. */
|
|
708
|
+
type SelectionMember<S, K> = K extends keyof S ? S[K] : undefined;
|
|
709
|
+
|
|
710
|
+
/** Object-bag keys only; primitive SKIP contributes no Symbol members. */
|
|
711
|
+
type BagKeys<S> = S extends object ? keyof S : never;
|
|
712
|
+
|
|
713
|
+
/** Read only object bags, preserving unions and optional bag omission. */
|
|
714
|
+
type SelectionValue<S, K> = S extends object ? SelectionMember<S, K> : undefined;
|
|
715
|
+
|
|
716
|
+
/** Only an active included relation replaces the selected relation. */
|
|
717
|
+
type IncludedRelation<S, I> = I extends false | undefined | Skip ? S : I;
|
|
718
|
+
|
|
719
|
+
/** Count include overrides even with false; omitted/nullish values fall back. */
|
|
720
|
+
type IncludedCount<S, I> = I extends null | undefined | Skip ? S : I;
|
|
721
|
+
|
|
678
722
|
/** Keys of an omit object whose value is exactly true \u2014 the scalars the result drops. */
|
|
679
723
|
type OmittedKeys<O> = keyof { [K in keyof O as O[K] extends true ? K : never]: 0 };
|
|
680
724
|
|
|
@@ -935,7 +979,13 @@ function checkedCreateLines(params) {
|
|
|
935
979
|
function uncheckedCreateLines(params) {
|
|
936
980
|
const { model, omitRelation } = params;
|
|
937
981
|
const omitted = new Set(omitRelation === undefined ? [] : model.relations.find((relation) => relation.name === omitRelation)?.fkFields ?? []);
|
|
938
|
-
return
|
|
982
|
+
return [
|
|
983
|
+
...model.fields.filter((field) => !omitted.has(field.name)).map((field) => createFieldProp({ field })),
|
|
984
|
+
...model.relations.map((relation) => {
|
|
985
|
+
const value = params.scalarOnly !== true && relation.fkFields.length === 0 && relation.name !== omitRelation ? nestedRefName({ relation, verb: "Create" }) : "never";
|
|
986
|
+
return ` readonly ${propKey({ name: relation.name })}?: ${value};`;
|
|
987
|
+
})
|
|
988
|
+
];
|
|
939
989
|
}
|
|
940
990
|
function checkedUpdateLines(params) {
|
|
941
991
|
const { model, omitRelation } = params;
|
|
@@ -955,7 +1005,13 @@ function checkedUpdateLines(params) {
|
|
|
955
1005
|
function uncheckedUpdateLines(params) {
|
|
956
1006
|
const { model, omitRelation } = params;
|
|
957
1007
|
const omitted = new Set(omitRelation === undefined ? [] : model.relations.find((relation) => relation.name === omitRelation)?.fkFields ?? []);
|
|
958
|
-
return
|
|
1008
|
+
return [
|
|
1009
|
+
...model.fields.filter((field) => !omitted.has(field.name)).map((field) => updateFieldProp({ field })),
|
|
1010
|
+
...model.relations.map((relation) => {
|
|
1011
|
+
const value = params.scalarOnly !== true && relation.fkFields.length === 0 && relation.name !== omitRelation ? nestedRefName({ relation, verb: "Update" }) : "never";
|
|
1012
|
+
return ` readonly ${propKey({ name: relation.name })}?: ${value};`;
|
|
1013
|
+
})
|
|
1014
|
+
];
|
|
959
1015
|
}
|
|
960
1016
|
function register(params) {
|
|
961
1017
|
const { registry, name, text } = params;
|
|
@@ -1032,7 +1088,7 @@ function registerNestedContext(params) {
|
|
|
1032
1088
|
lines: [` readonly where: ${t}WhereUniqueInput;`, ` readonly create: ${createUnion};`]
|
|
1033
1089
|
})
|
|
1034
1090
|
});
|
|
1035
|
-
const createManyData = inverse === null ? `readonly ${t}CreateManyInput[]` : `readonly ${
|
|
1091
|
+
const createManyData = inverse === null ? `readonly ${t}CreateManyInput[]` : `readonly { ${uncheckedCreateLines({ model: target, omitRelation: inverse, scalarOnly: true }).map((line) => skippableMembers({ block: line }).trim()).join(" ")} }[]`;
|
|
1036
1092
|
if (many) {
|
|
1037
1093
|
register({
|
|
1038
1094
|
registry,
|
|
@@ -1123,7 +1179,7 @@ function registerNestedContext(params) {
|
|
|
1123
1179
|
` readonly connectOrCreate?: ${connectOrCreateName};`,
|
|
1124
1180
|
` readonly update?: ${updateUnion};`,
|
|
1125
1181
|
` readonly upsert?: { readonly create: ${createUnion}; readonly update: ${updateUnion} };`,
|
|
1126
|
-
...allowDetach ? [` readonly disconnect?: boolean;`, ` readonly delete?: boolean;`] : []
|
|
1182
|
+
...allowDetach ? [` readonly disconnect?: boolean | ${t}WhereInput;`, ` readonly delete?: boolean | ${t}WhereInput;`] : []
|
|
1127
1183
|
]
|
|
1128
1184
|
})
|
|
1129
1185
|
});
|
|
@@ -1145,7 +1201,7 @@ ${[
|
|
|
1145
1201
|
].join(`
|
|
1146
1202
|
`)}
|
|
1147
1203
|
};`);
|
|
1148
|
-
parts.push(`export type ${n}ScalarFieldEnum = ${model.fields.map((field) =>
|
|
1204
|
+
parts.push(`export type ${n}ScalarFieldEnum = ${literalUnion({ members: model.fields.map((field) => field.name) }) || "never"};`);
|
|
1149
1205
|
if (computedEntries.length > 0) {
|
|
1150
1206
|
parts.push(`export type ${n}SelectableFieldEnum = ${n}ScalarFieldEnum | ${computedEntries.map((entry) => tsString({ value: entry.field })).join(" | ")};`);
|
|
1151
1207
|
}
|
|
@@ -1265,12 +1321,12 @@ ${model.relations.map((relation) => ` ${propKey({ name: relation.name })}: ${re
|
|
|
1265
1321
|
}));
|
|
1266
1322
|
parts.push(inputAlias({ name: `${n}CreateInput`, lines: checkedCreateLines({ model }) }));
|
|
1267
1323
|
parts.push(inputAlias({ name: `${n}UncheckedCreateInput`, lines: uncheckedCreateLines({ model }) }));
|
|
1268
|
-
parts.push(
|
|
1324
|
+
parts.push(inputAlias({ name: `${n}CreateManyInput`, lines: uncheckedCreateLines({ model, scalarOnly: true }) }));
|
|
1269
1325
|
parts.push(inputAlias({ name: `${n}UpdateInput`, lines: checkedUpdateLines({ model }) }));
|
|
1270
1326
|
parts.push(inputAlias({ name: `${n}UncheckedUpdateInput`, lines: uncheckedUpdateLines({ model }) }));
|
|
1271
|
-
parts.push(
|
|
1327
|
+
parts.push(inputAlias({ name: `${n}UpdateManyInput`, lines: uncheckedUpdateLines({ model, scalarOnly: true }) }));
|
|
1272
1328
|
const numericFields = model.fields.filter((field) => !field.isList && field.scalar !== null && NUMERIC_SCALARS.has(field.scalar));
|
|
1273
|
-
const minmaxFields = model.fields.filter((field) => !field.isList && field.scalar !== null && MINMAX_SCALARS.has(field.scalar));
|
|
1329
|
+
const minmaxFields = model.fields.filter((field) => !field.isList && (field.enumName !== null || field.scalar !== null && MINMAX_SCALARS.has(field.scalar)));
|
|
1274
1330
|
parts.push(inputAlias({
|
|
1275
1331
|
name: `${n}CountAggregateInput`,
|
|
1276
1332
|
lines: [
|
|
@@ -1297,6 +1353,44 @@ ${model.relations.map((relation) => ` ${propKey({ name: relation.name })}: ${re
|
|
|
1297
1353
|
aggregateOpProps.push(` readonly _min?: ${n}MinAggregateInput;`);
|
|
1298
1354
|
aggregateOpProps.push(` readonly _max?: ${n}MaxAggregateInput;`);
|
|
1299
1355
|
}
|
|
1356
|
+
const groupAggregates = [
|
|
1357
|
+
{ key: "_count", prefix: "Count", fields: model.fields },
|
|
1358
|
+
...numericFields.length > 0 ? [
|
|
1359
|
+
{ key: "_avg", prefix: "Avg", fields: numericFields },
|
|
1360
|
+
{ key: "_sum", prefix: "Sum", fields: numericFields }
|
|
1361
|
+
] : [],
|
|
1362
|
+
...minmaxFields.length > 0 ? [
|
|
1363
|
+
{ key: "_min", prefix: "Min", fields: minmaxFields },
|
|
1364
|
+
{ key: "_max", prefix: "Max", fields: minmaxFields }
|
|
1365
|
+
] : []
|
|
1366
|
+
];
|
|
1367
|
+
const groupAggregateFields = new Map(groupAggregates.map((aggregate) => [aggregate, new Set(aggregate.fields)]));
|
|
1368
|
+
const havingLines = [
|
|
1369
|
+
` readonly AND?: ${orMany({ type: `${n}HavingInput` })};`,
|
|
1370
|
+
` readonly OR?: ${orMany({ type: `${n}HavingInput` })};`,
|
|
1371
|
+
` readonly NOT?: ${orMany({ type: `${n}HavingInput` })};`
|
|
1372
|
+
];
|
|
1373
|
+
const groupOrderLines = [...scalarOrderLines];
|
|
1374
|
+
for (const aggregate of groupAggregates) {
|
|
1375
|
+
const havingFields = aggregate.fields.map((field) => ` readonly ${propKey({ name: field.name })}?: ${aggregatePredicateType({ value: aggregateOperandType({ field, key: aggregate.key }) })};`);
|
|
1376
|
+
const orderFields = aggregate.fields.map((field) => ` readonly ${propKey({ name: field.name })}?: SortOrder;`);
|
|
1377
|
+
if (aggregate.key === "_count") {
|
|
1378
|
+
havingFields.push(` readonly _all?: ${aggregatePredicateType({ value: "number" })};`);
|
|
1379
|
+
orderFields.push(` readonly _all?: SortOrder;`);
|
|
1380
|
+
}
|
|
1381
|
+
parts.push(inputAlias({ name: `${n}${aggregate.prefix}AggregateHavingInput`, lines: havingFields }));
|
|
1382
|
+
parts.push(inputAlias({ name: `${n}${aggregate.prefix}AggregateOrderByInput`, lines: orderFields }));
|
|
1383
|
+
havingLines.push(` readonly ${aggregate.key}?: ${n}${aggregate.prefix}AggregateHavingInput;`);
|
|
1384
|
+
groupOrderLines.push(` readonly ${aggregate.key}?: ${n}${aggregate.prefix}AggregateOrderByInput;`);
|
|
1385
|
+
}
|
|
1386
|
+
for (const field of model.fields) {
|
|
1387
|
+
const aggregates = groupAggregates.filter((aggregate) => groupAggregateFields.get(aggregate).has(field)).map((aggregate) => `readonly ${aggregate.key}?: ${aggregatePredicateType({ value: aggregateOperandType({ field, key: aggregate.key }), skippable: field.scalar !== "Json" })} | Skip`);
|
|
1388
|
+
const filter = `(${filterName({ field })} & { ${aggregates.join("; ")} })`;
|
|
1389
|
+
const value = field.isList || field.scalar === "Json" ? filter : `${inputBaseType({ field })} | ${filter}${field.isOptional ? " | null" : ""}`;
|
|
1390
|
+
havingLines.push(` readonly ${propKey({ name: field.name })}?: ${value};`);
|
|
1391
|
+
}
|
|
1392
|
+
parts.push(inputAlias({ name: `${n}HavingInput`, lines: havingLines }));
|
|
1393
|
+
parts.push(inputAlias({ name: `${n}GroupByOrderByInput`, lines: groupOrderLines }));
|
|
1300
1394
|
for (const nested of [false, true]) {
|
|
1301
1395
|
parts.push(inputAlias({
|
|
1302
1396
|
name: `${n}${nested ? "RelationFindManyArgs" : "FindManyArgs"}`,
|
|
@@ -1319,6 +1413,21 @@ ${model.relations.map((relation) => ` ${propKey({ name: relation.name })}: ${re
|
|
|
1319
1413
|
]
|
|
1320
1414
|
}));
|
|
1321
1415
|
}
|
|
1416
|
+
parts.push(inputAlias({
|
|
1417
|
+
name: `${n}FindPageArgs`,
|
|
1418
|
+
lines: [
|
|
1419
|
+
` readonly where?: ${n}WhereInput;`,
|
|
1420
|
+
` readonly orderBy: ${orMany({ type: `${n}ScalarOrderByInput` })};`,
|
|
1421
|
+
` readonly take: number;`,
|
|
1422
|
+
` readonly after?: KeysetToken;`,
|
|
1423
|
+
` readonly keyset?: KeysetOptions;`,
|
|
1424
|
+
` readonly select?: ${n}Select;`,
|
|
1425
|
+
` readonly include?: ${n}Include;`,
|
|
1426
|
+
` readonly omit?: ${n}OmitInput;`,
|
|
1427
|
+
` readonly relationStrategy?: RelationStrategy;`,
|
|
1428
|
+
...["skip", "cursor", "distinct", "lock"].map((key) => ` readonly ${key}?: never;`)
|
|
1429
|
+
]
|
|
1430
|
+
}));
|
|
1322
1431
|
parts.push(`export type ${n}FindFirstArgs = ${n}FindManyArgs;`);
|
|
1323
1432
|
parts.push(inputAlias({
|
|
1324
1433
|
name: `${n}FindUniqueArgs`,
|
|
@@ -1334,6 +1443,7 @@ ${model.relations.map((relation) => ` ${propKey({ name: relation.name })}: ${re
|
|
|
1334
1443
|
parts.push(inputAlias({
|
|
1335
1444
|
name: `${n}CreateArgs`,
|
|
1336
1445
|
lines: [
|
|
1446
|
+
` readonly relationStrategy?: never;`,
|
|
1337
1447
|
` readonly onConflict?: never;`,
|
|
1338
1448
|
` readonly data: ${n}CreateInput | ${n}UncheckedCreateInput;`,
|
|
1339
1449
|
` readonly select?: ${n}Select;`,
|
|
@@ -1348,6 +1458,7 @@ ${model.relations.map((relation) => ` ${propKey({ name: relation.name })}: ${re
|
|
|
1348
1458
|
parts.push(inputAlias({
|
|
1349
1459
|
name: `${n}CreateOrSkipArgs`,
|
|
1350
1460
|
lines: [
|
|
1461
|
+
` readonly relationStrategy?: never;`,
|
|
1351
1462
|
` readonly data: ${n}CreateInput | ${n}UncheckedCreateInput;`,
|
|
1352
1463
|
` readonly onConflict: ${n}CreateOnConflictInput;`,
|
|
1353
1464
|
` readonly select?: ${n}Select;`,
|
|
@@ -1358,6 +1469,7 @@ ${model.relations.map((relation) => ` ${propKey({ name: relation.name })}: ${re
|
|
|
1358
1469
|
parts.push(inputAlias({
|
|
1359
1470
|
name: `${n}CreateManyArgs`,
|
|
1360
1471
|
lines: [
|
|
1472
|
+
` readonly relationStrategy?: never;`,
|
|
1361
1473
|
` readonly data: ${orMany({ type: `${n}CreateManyInput` })};`,
|
|
1362
1474
|
` readonly skipDuplicates?: boolean | { readonly target?: ${n}ConflictTarget; readonly where?: string };`
|
|
1363
1475
|
]
|
|
@@ -1365,6 +1477,7 @@ ${model.relations.map((relation) => ` ${propKey({ name: relation.name })}: ${re
|
|
|
1365
1477
|
parts.push(inputAlias({
|
|
1366
1478
|
name: `${n}CreateManyAndReturnArgs`,
|
|
1367
1479
|
lines: [
|
|
1480
|
+
` readonly relationStrategy?: never;`,
|
|
1368
1481
|
` readonly data: ${orMany({ type: `${n}CreateManyInput` })};`,
|
|
1369
1482
|
` readonly skipDuplicates?: boolean | { readonly target?: ${n}ConflictTarget; readonly where?: string };`,
|
|
1370
1483
|
` readonly select?: ${n}Select;`,
|
|
@@ -1403,13 +1516,19 @@ export type ${n}UpdateWhereInput = ${n}WhereUniqueInput & ${n}WhereInput;`);
|
|
|
1403
1516
|
` readonly skipUpdatedAt?: boolean;`
|
|
1404
1517
|
]
|
|
1405
1518
|
}));
|
|
1519
|
+
const returningSelect = [
|
|
1520
|
+
...model.fields.map((field) => `readonly ${propKey({ name: field.name })}?: boolean | Skip`),
|
|
1521
|
+
...computedEntries.map((entry) => `readonly ${propKey({ name: entry.field })}?: boolean | Skip`),
|
|
1522
|
+
...model.relations.map((relation) => `readonly ${propKey({ name: relation.name })}?: false | Skip`),
|
|
1523
|
+
...hasCounts ? ["readonly _count?: false | Skip"] : []
|
|
1524
|
+
].join("; ");
|
|
1406
1525
|
parts.push(inputAlias({
|
|
1407
1526
|
name: `${n}UpdateManyAndReturnArgs`,
|
|
1408
1527
|
lines: [
|
|
1409
1528
|
` readonly include?: never;`,
|
|
1410
1529
|
` readonly where?: ${n}WhereInput;`,
|
|
1411
1530
|
` readonly data: ${n}UpdateManyInput;`,
|
|
1412
|
-
` readonly select?: ${
|
|
1531
|
+
` readonly select?: { ${returningSelect} };`,
|
|
1413
1532
|
` readonly omit?: ${n}OmitInput;`,
|
|
1414
1533
|
` readonly skipUpdatedAt?: boolean;`
|
|
1415
1534
|
]
|
|
@@ -1461,8 +1580,8 @@ export type ${n}UpdateWhereInput = ${n}WhereUniqueInput & ${n}WhereInput;`);
|
|
|
1461
1580
|
lines: [
|
|
1462
1581
|
` readonly by: readonly ${n}ScalarFieldEnum[];`,
|
|
1463
1582
|
` readonly where?: ${n}WhereInput;`,
|
|
1464
|
-
` readonly orderBy?: ${orMany({ type: `${n}
|
|
1465
|
-
` readonly having?: ${n}
|
|
1583
|
+
` readonly orderBy?: ${orMany({ type: `${n}GroupByOrderByInput` })};`,
|
|
1584
|
+
` readonly having?: ${n}HavingInput;`,
|
|
1466
1585
|
` readonly take?: number;`,
|
|
1467
1586
|
` readonly skip?: number;`,
|
|
1468
1587
|
...aggregateOpProps
|
|
@@ -1481,9 +1600,9 @@ export type ${n}UpdateWhereInput = ${n}WhereUniqueInput & ${n}WhereInput;`);
|
|
|
1481
1600
|
` + ` { [K in keyof CS & (${listUnion}) as ${uncertainPart("CS")}]?: number }
|
|
1482
1601
|
` + ` : C extends { readonly select: Skip } ? { ${allCounts} }
|
|
1483
1602
|
` + ` : "select" extends keyof C ? Partial<{ ${allCounts} }> : { ${allCounts} };`);
|
|
1484
|
-
parts.push(`type ${n}CountPayload<
|
|
1485
|
-
` + ` :
|
|
1486
|
-
` + ` :
|
|
1603
|
+
parts.push(`type ${n}CountPayload<C> = [C] extends [true | object] ? { _count: ${n}CountResult<C> }
|
|
1604
|
+
` + ` : [C] extends [false | undefined | Skip] ? unknown
|
|
1605
|
+
` + ` : { _count?: ${n}CountResult<Exclude<C, false | undefined | Skip>> };`);
|
|
1487
1606
|
}
|
|
1488
1607
|
if (hasRelations) {
|
|
1489
1608
|
const relationCases = model.relations.map((relation) => {
|
|
@@ -1503,20 +1622,23 @@ export type ${n}UpdateWhereInput = ${n}WhereUniqueInput & ${n}WhereInput;`);
|
|
|
1503
1622
|
parts.push(`type ${n}RelationPayload<K extends keyof ${n}Relations, A> = {
|
|
1504
1623
|
${relationCases}
|
|
1505
1624
|
}[K];`);
|
|
1506
|
-
parts.push(`type ${n}
|
|
1507
|
-
` + `
|
|
1508
|
-
` + `
|
|
1509
|
-
${n}
|
|
1510
|
-
` + `
|
|
1625
|
+
parts.push(`type ${n}ResolvedSelection<A, S = SelectionValue<A, "select">, I = SelectionValue<A, "include">> = {
|
|
1626
|
+
` + ` [K in (BagKeys<S> | BagKeys<I>) & (${hasCounts ? `keyof ${n}Relations | "_count"` : `keyof ${n}Relations`})]: ${hasCounts ? `K extends "_count" ? IncludedCount<SelectionValue<S, K>, SelectionValue<I, K>> : ` : ""}IncludedRelation<SelectionValue<S, K>, SelectionValue<I, K>>;
|
|
1627
|
+
` + `};`);
|
|
1628
|
+
parts.push(`type ${n}IncludePayload<A, I = "include" extends keyof A ? "select" extends keyof A ? ${n}ResolvedSelection<A> : SelectionValue<A, "include"> : SelectionValue<A, "select">> =
|
|
1629
|
+
` + ` [Exclude<SelectionValue<I, BagKeys<I> & (${hasCounts ? `keyof ${n}Relations | "_count"` : `keyof ${n}Relations`})>, false | undefined | Skip>] extends [never] ? unknown :
|
|
1630
|
+
` + ` { [K in BagKeys<I> & keyof ${n}Relations as ${exactPart("I").replaceAll("I[K]", "SelectionValue<I, K>")}]: ${n}RelationPayload<K, Exclude<SelectionValue<I, K>, false | undefined | Skip>> } &
|
|
1631
|
+
` + ` { [K in BagKeys<I> & keyof ${n}Relations as ${uncertainPart("I").replaceAll("I[K]", "SelectionValue<I, K>")}]?: ${n}RelationPayload<K, Exclude<SelectionValue<I, K>, false | undefined | Skip>> }${hasCounts ? ` &
|
|
1632
|
+
${n}CountPayload<SelectionValue<I, "_count">>` : ""};`);
|
|
1511
1633
|
parts.push(`export type ${n}GetPayload<A> = A extends { readonly select: infer S extends object }
|
|
1512
1634
|
` + ` ? { [K in keyof S & ${selectableEnum} as ${exactPart("S")}]: ${n}[K & keyof ${n}] } &
|
|
1513
1635
|
` + ` { [K in keyof S & ${selectableEnum} as ${uncertainPart("S")}]?: ${n}[K & keyof ${n}] } &
|
|
1514
|
-
` + `
|
|
1515
|
-
` + `
|
|
1516
|
-
${n}CountPayload<S>` : ""}
|
|
1636
|
+
` + ` ("include" extends keyof A ? ${n}IncludePayload<A> :
|
|
1637
|
+
` + ` [Exclude<SelectionValue<S, BagKeys<S> & (${hasCounts ? `keyof ${n}Relations | "_count"` : `keyof ${n}Relations`})>, false | undefined | Skip>] extends [never] ? unknown : ${n}IncludePayload<A>)
|
|
1517
1638
|
` + ` : A extends { readonly select: Skip } ? ${n}OmitBase<A> & ${n}IncludePayload<A>
|
|
1518
|
-
` + ` : "select" extends keyof A ? Partial<${n}>
|
|
1519
|
-
` + ` : ${n}OmitBase<A> & ${n}IncludePayload<A
|
|
1639
|
+
` + ` : "select" extends keyof A ? Partial<${n}> & ${n}IncludePayload<A>
|
|
1640
|
+
` + ` : "include" extends keyof A ? ${n}OmitBase<A> & ${n}IncludePayload<A>
|
|
1641
|
+
` + ` : ${n}OmitBase<A>;`);
|
|
1520
1642
|
} else {
|
|
1521
1643
|
parts.push(`export type ${n}GetPayload<A> = A extends { readonly select: infer S extends object }
|
|
1522
1644
|
` + ` ? { [K in keyof S & ${selectableEnum} as ${exactPart("S")}]: ${n}[K & keyof ${n}] } &
|
|
@@ -1525,7 +1647,7 @@ ${relationCases}
|
|
|
1525
1647
|
` + ` : "select" extends keyof A ? Partial<${n}> : ${n}OmitBase<A>;`);
|
|
1526
1648
|
}
|
|
1527
1649
|
const aggregateFields = (fields, value) => `{ ${fields.map((field) => `${propKey({ name: field.name })}: ${value(field)}`).join("; ")} }`;
|
|
1528
|
-
const countValues =
|
|
1650
|
+
const countValues = `{ ${[...model.fields.map((field) => `${propKey({ name: field.name })}: number`), "_all: number"].join("; ")} }`;
|
|
1529
1651
|
parts.push(`type ${n}AggregateValues = {
|
|
1530
1652
|
` + ` _count: ${countValues};
|
|
1531
1653
|
` + ` _avg: ${aggregateFields(numericFields, () => "number | null")};
|
|
@@ -1546,6 +1668,7 @@ ${relationCases}
|
|
|
1546
1668
|
` + ` (number extends A["by"]["length"] ? Partial<Pick<${n}, A["by"][number]>> : Pick<${n}, A["by"][number]>) & ${n}AggregateResult<A>;`);
|
|
1547
1669
|
const readMethods = [
|
|
1548
1670
|
` findMany<A extends ${n}FindManyArgs = {}>(args?: A): VibeQueryPromise<${n}GetPayload<A>[]>;`,
|
|
1671
|
+
` findPage<A extends ${n}FindPageArgs>(args: A): VibeQueryPromise<KeysetPage<${n}GetPayload<A>>>;`,
|
|
1549
1672
|
` findFirst<A extends ${n}FindFirstArgs = {}>(args?: A): VibeQueryPromise<${n}GetPayload<A> | null>;`,
|
|
1550
1673
|
` findFirstOrThrow<A extends ${n}FindFirstArgs = {}>(args?: A): VibeQueryPromise<${n}GetPayload<A>>;`,
|
|
1551
1674
|
` findUnique<A extends ${n}FindUniqueArgs>(args: A): VibeQueryPromise<${n}GetPayload<A> | null>;`,
|
|
@@ -1656,7 +1779,7 @@ function emitViewTypesForModel(params) {
|
|
|
1656
1779
|
const target = relation.target;
|
|
1657
1780
|
const targetClientKey = view.modelByName.get(target)?.clientName ?? target;
|
|
1658
1781
|
const guard = `D extends { readonly ${propKey({ name: targetClientKey })}: infer RF extends object }`;
|
|
1659
|
-
const masked =
|
|
1782
|
+
const masked = `${target}ViewResult<RF, Exclude<A, false | undefined | Skip>, D>`;
|
|
1660
1783
|
const unmasked = `(A extends object ? ${target}GetPayload<A> : ${target})`;
|
|
1661
1784
|
let expression;
|
|
1662
1785
|
if (relation.isList) {
|
|
@@ -1881,8 +2004,9 @@ var CONNECTION_SURFACE_LINES = ` readonly $connect: () => Promise<void>;
|
|
|
1881
2004
|
var DEFINE_VIEW_LINE = ` /** Field-masking views (v1 defineView parity): a read-only masked projection of models. */
|
|
1882
2005
|
` + ` readonly $defineView: <D extends ViewDefinitionInput>(options: { readonly name: string; readonly definition: D }) => ViewClient<D>;
|
|
1883
2006
|
`;
|
|
1884
|
-
var
|
|
1885
|
-
|
|
2007
|
+
var READ_ONLY_DOC = ` /** A read-only handle over this one: reads pass through, and every write, raw-SQL and control method is refused with \`VIBE_READ_ONLY\`. Closed under composition \u2014 transactions, views, scoped and policy handles built from it stay read-only. */
|
|
2008
|
+
`;
|
|
2009
|
+
var READ_ONLY_LINE = READ_ONLY_DOC + ` readonly $readOnly: () => ReadOnlyClientInstance;
|
|
1886
2010
|
`;
|
|
1887
2011
|
function instanceSurfaceLines(params) {
|
|
1888
2012
|
const { view, transactionType, inTransaction, selfLabel } = params;
|
|
@@ -2064,7 +2188,7 @@ function emitClient(params) {
|
|
|
2064
2188
|
` + ` // 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.
|
|
2065
2189
|
` + `};`);
|
|
2066
2190
|
} else {
|
|
2067
|
-
parts.push(`export type VibeClientInstance = ClientContext
|
|
2191
|
+
parts.push(`export type VibeClientInstance = ClientContext<${tsString({ value: view.models.some((model) => model.policy !== undefined) ? "restricted" : "root" })}> & {
|
|
2068
2192
|
` + instanceSurfaceLines({
|
|
2069
2193
|
view,
|
|
2070
2194
|
transactionType: "VibeTransactionClientInstance",
|
|
@@ -2072,7 +2196,7 @@ function emitClient(params) {
|
|
|
2072
2196
|
selfLabel: "db"
|
|
2073
2197
|
}) + policyClientLines({ view, selfType: "VibeClientInstance" }) + scopedClientLines({ view, scopedType: "ScopedClientInstance" }) + READ_ONLY_LINE + clientExtensionLines + `};`);
|
|
2074
2198
|
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. */
|
|
2075
|
-
` + `export type VibeTransactionClientInstance = ClientContext
|
|
2199
|
+
` + `export type VibeTransactionClientInstance = ClientContext<${tsString({ value: view.models.some((model) => model.policy !== undefined) ? "restricted" : "transaction" })}> & {
|
|
2076
2200
|
` + instanceSurfaceLines({
|
|
2077
2201
|
view,
|
|
2078
2202
|
transactionType: "VibeTransactionClientInstance",
|
|
@@ -2135,7 +2259,8 @@ ${modelsProps.join(`
|
|
|
2135
2259
|
` + ` };
|
|
2136
2260
|
` + (params2.inTransaction ? advisoryLockLines({ view, selfLabel: "tx" }) : CONNECTION_SURFACE_LINES) + ` /** Field-masking views compose over the scoped handle (columns \xD7 rows). */
|
|
2137
2261
|
` + ` readonly $defineView: <D extends ViewDefinitionInput>(options: { readonly name: string; readonly definition: D }) => ViewClient<D>;
|
|
2138
|
-
` +
|
|
2262
|
+
` + READ_ONLY_DOC + ` readonly $readOnly: () => ReadOnlyClientInstance<"restricted">;
|
|
2263
|
+
`;
|
|
2139
2264
|
return [
|
|
2140
2265
|
banner({ title: "Tenant scoping ($scoped)" }),
|
|
2141
2266
|
`/** What a scoped handle does when this model appears INSIDE another model's nested write: "verify" (default) rewrites reference verbs into tenant-verified lookups; "refuse" rejects them. */
|
|
@@ -2145,11 +2270,11 @@ export type ScopedModelsConfig = {
|
|
|
2145
2270
|
${entries.join(`
|
|
2146
2271
|
`)}
|
|
2147
2272
|
};`,
|
|
2148
|
-
`export type ScopedClientInstance = {
|
|
2273
|
+
`export type ScopedClientInstance = ClientContext<"restricted"> & {
|
|
2149
2274
|
` + scopedSurface({ transactionType: "ScopedTransactionClientInstance", inTransaction: false }) + ` // Deliberately ABSENT: $queryRaw / $executeRaw / $queryRawUnsafe / $executeRawUnsafe (raw SQL cannot be tenant-verified \u2014 use the base client) and $scoped (scope once, from the base client).
|
|
2150
2275
|
` + `};`,
|
|
2151
2276
|
`/** The scoped handle inside \`$transaction\`: same scope, plus the locks that require a transaction. */
|
|
2152
|
-
` + `export type ScopedTransactionClientInstance = {
|
|
2277
|
+
` + `export type ScopedTransactionClientInstance = ClientContext<"restricted"> & {
|
|
2153
2278
|
` + scopedSurface({ transactionType: "ScopedTransactionClientInstance", inTransaction: true }) + ` // Deliberately ABSENT: the raw-SQL doors, $scoped, and $connect / $disconnect (the open transaction owns the connection).
|
|
2154
2279
|
` + `};`
|
|
2155
2280
|
];
|
|
@@ -2186,6 +2311,9 @@ function emitReadOnlySection(params) {
|
|
|
2186
2311
|
const extensionLines = baked.readOnlyClientDts.length === 0 ? "" : `${baked.readOnlyClientDts.join(`
|
|
2187
2312
|
`)}
|
|
2188
2313
|
`;
|
|
2314
|
+
const scopeLines = scopedClientLines({ view, scopedType: 'ReadOnlyClientInstance<"restricted">' });
|
|
2315
|
+
const scopeSurface = scopeLines.length === 0 ? "" : `(ContextKind extends "readOnly" ? {
|
|
2316
|
+
${scopeLines}} : {}) & `;
|
|
2189
2317
|
return [
|
|
2190
2318
|
banner({ title: "Read-only clients ($readOnly)" }),
|
|
2191
2319
|
`/**
|
|
@@ -2199,24 +2327,66 @@ function emitReadOnlySection(params) {
|
|
|
2199
2327
|
` + ` * await reader.user.findMany(); // fine
|
|
2200
2328
|
` + ` * await reader.user.upsertMany({ \u2026 }); // VIBE_READ_ONLY, and a compile error
|
|
2201
2329
|
` + ` */
|
|
2202
|
-
` + `export type ReadOnlyClientInstance =
|
|
2203
|
-
` + delegateSurfaceLines({ view, delegateSuffix: "ReadOnlyDelegate" }) + transactionLines({ transactionType: "ReadOnlyClientInstance", reads: true }) + DEFINE_VIEW_LINE + policyClientLines({ view, selfType: "ReadOnlyClientInstance" }) +
|
|
2204
|
-
` + ` readonly $readOnly: () => ReadOnlyClientInstance
|
|
2330
|
+
` + `export type ReadOnlyClientInstance<ContextKind extends "readOnly" | "restricted" = ${tsString({ value: hasNativeRls({ view }) || view.models.some((model) => model.policy !== undefined) ? "restricted" : "readOnly" })}> = ClientContext<ContextKind> & ${scopeSurface}{
|
|
2331
|
+
` + delegateSurfaceLines({ view, delegateSuffix: "ReadOnlyDelegate" }) + transactionLines({ transactionType: "ReadOnlyClientInstance<ContextKind>", reads: true }) + DEFINE_VIEW_LINE + policyClientLines({ view, selfType: "ReadOnlyClientInstance<ContextKind>" }) + ` /** Idempotent: the same read-only restriction and underlying context are retained. */
|
|
2332
|
+
` + ` readonly $readOnly: () => ReadOnlyClientInstance<ContextKind>;
|
|
2205
2333
|
` + 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.
|
|
2206
2334
|
` + `};`
|
|
2207
2335
|
];
|
|
2208
2336
|
}
|
|
2209
|
-
|
|
2337
|
+
var UNSUPPORTED_INPUT_MEMBER_NAMES = new Set([
|
|
2338
|
+
"toString",
|
|
2339
|
+
"valueOf",
|
|
2340
|
+
"constructor",
|
|
2341
|
+
"toLocaleString",
|
|
2342
|
+
"hasOwnProperty",
|
|
2343
|
+
"isPrototypeOf",
|
|
2344
|
+
"propertyIsEnumerable"
|
|
2345
|
+
]);
|
|
2346
|
+
function groupComputed(params) {
|
|
2347
|
+
const byModel = new Map;
|
|
2348
|
+
for (const entry of params.computed) {
|
|
2349
|
+
const model = entry.model;
|
|
2350
|
+
const list = byModel.get(model);
|
|
2351
|
+
if (list === undefined)
|
|
2352
|
+
byModel.set(model, [entry]);
|
|
2353
|
+
else
|
|
2354
|
+
list.push(entry);
|
|
2355
|
+
}
|
|
2356
|
+
return byModel;
|
|
2357
|
+
}
|
|
2358
|
+
function assertGeneratedInputNames(params) {
|
|
2359
|
+
const check = (member) => {
|
|
2360
|
+
if (!UNSUPPORTED_INPUT_MEMBER_NAMES.has(member.name))
|
|
2361
|
+
return;
|
|
2362
|
+
const hint = member.kind === "field" ? "Rename the logical field and preserve its database column with @map or DSL .map()." : member.kind === "relation" ? "Rename the logical relation while keeping its FK fields, references, relation pairing name and constraint mapping. Relation fields do not have a column @map." : "Rename the computed alias in the computed configuration; it has no database column to map.";
|
|
2363
|
+
throw new VibeError4({
|
|
2364
|
+
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
2365
|
+
message: `Generated TypeScript inputs cannot represent logical ${member.kind} "${member.model}.${member.name}" safely because it conflicts with an inherited Object member. ${hint} SchemaIR and the dynamic client still accept this name.`,
|
|
2366
|
+
meta: { reason: "generated_input_member", model: member.model, member: member.name, kind: member.kind }
|
|
2367
|
+
});
|
|
2368
|
+
};
|
|
2369
|
+
const computedByModel = groupComputed({ computed: params.computed ?? [] });
|
|
2370
|
+
for (const model of params.view.models) {
|
|
2371
|
+
for (const field of model.fields)
|
|
2372
|
+
check({ model: model.name, name: field.name, kind: "field" });
|
|
2373
|
+
for (const relation of model.relations)
|
|
2374
|
+
check({ model: model.name, name: relation.name, kind: "relation" });
|
|
2375
|
+
for (const entry of computedByModel.get(model.name) ?? []) {
|
|
2376
|
+
check({ model: model.name, name: entry.field, kind: "computed" });
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
}
|
|
2380
|
+
function prepareDts(params) {
|
|
2381
|
+
assertGeneratedInputNames({ view: params.view, computed: params.computed ?? [] });
|
|
2382
|
+
return () => emitPreparedDts(params);
|
|
2383
|
+
}
|
|
2384
|
+
function emitPreparedDts(params) {
|
|
2210
2385
|
const { view } = params;
|
|
2211
2386
|
const baked = params.baked ?? EMPTY_BAKED_EXTENSIONS;
|
|
2212
2387
|
const computed = params.computed ?? [];
|
|
2213
2388
|
const hasComputed = computed.length > 0;
|
|
2214
|
-
const computedByModel =
|
|
2215
|
-
for (const entry of computed) {
|
|
2216
|
-
const list = computedByModel.get(entry.model) ?? [];
|
|
2217
|
-
list.push(entry);
|
|
2218
|
-
computedByModel.set(entry.model, list);
|
|
2219
|
-
}
|
|
2389
|
+
const computedByModel = groupComputed({ computed });
|
|
2220
2390
|
const parts = [];
|
|
2221
2391
|
parts.push(GENERATED_HEADER);
|
|
2222
2392
|
const schemaTypeImports = [
|
|
@@ -2451,7 +2621,8 @@ var README = `# Generated VibeORM client
|
|
|
2451
2621
|
This directory was generated by \`@vibeorm/generator\` from your VibeORM schema \u2014 do not edit it by hand, any change is overwritten on the next generate. After every schema change, regenerate with \`vibeorm generate\` (or programmatically via \`generateClient({ schema, outDir })\` from \`@vibeorm/generator\`).
|
|
2452
2622
|
`;
|
|
2453
2623
|
async function generateClient(params) {
|
|
2454
|
-
const {
|
|
2624
|
+
const { outDir } = params;
|
|
2625
|
+
const schema = params.module === undefined ? params.schema : prepareDatabaseModuleSchema({ schema: params.schema, module: params.module });
|
|
2455
2626
|
const view = buildSchemaView({ schema });
|
|
2456
2627
|
const baked = bakeExtensions({ schema: view.schema, extensions: params.extensions ?? [] });
|
|
2457
2628
|
let computed = [];
|
|
@@ -2459,11 +2630,12 @@ async function generateClient(params) {
|
|
|
2459
2630
|
validateComputedConfig({ schema, computed: params.computed });
|
|
2460
2631
|
computed = buildComputedManifest({ computed: params.computed });
|
|
2461
2632
|
}
|
|
2633
|
+
const declarations = prepareDts({ view, baked, computed });
|
|
2462
2634
|
if (params.module !== undefined && (!baked.empty || computed.length > 0))
|
|
2463
2635
|
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 } });
|
|
2464
2636
|
const outputs = [
|
|
2465
2637
|
{ name: "index.js", content: emitJs({ view, baked, computed }) },
|
|
2466
|
-
{ name: "index.d.ts", content:
|
|
2638
|
+
{ name: "index.d.ts", content: declarations() },
|
|
2467
2639
|
{ name: "enums.js", content: emitEnumsJs({ view }) },
|
|
2468
2640
|
{ name: "enums.d.ts", content: emitEnumsDts({ view }) },
|
|
2469
2641
|
{ name: "README.md", content: README },
|
|
@@ -2480,7 +2652,8 @@ async function generateClient(params) {
|
|
|
2480
2652
|
}
|
|
2481
2653
|
export {
|
|
2482
2654
|
bakeExtensions,
|
|
2483
|
-
generateClient
|
|
2655
|
+
generateClient,
|
|
2656
|
+
prepareDatabaseModuleSchema
|
|
2484
2657
|
};
|
|
2485
2658
|
|
|
2486
|
-
//# debugId=
|
|
2659
|
+
//# debugId=0EF8A4E0C9CC959A64756E2164756E21
|