@vibeorm/runtime 2.0.0-alpha.1 → 2.0.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +3 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/computed.d.ts +43 -0
- package/dist/computed.d.ts.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +223 -35
- package/dist/index.js.map +9 -8
- package/dist/model-meta.d.ts +13 -1
- package/dist/model-meta.d.ts.map +1 -1
- package/dist/query-builder.d.ts +8 -0
- package/dist/query-builder.d.ts.map +1 -1
- package/dist/relation-loader.d.ts.map +1 -1
- package/dist/relation-plan.d.ts +2 -0
- package/dist/relation-plan.d.ts.map +1 -1
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ function toSqlExecutor(params) {
|
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
// packages/runtime/src/model-meta.ts
|
|
14
|
-
import { VibeError, isEnumType } from "@vibeorm/schema";
|
|
14
|
+
import { VibeError, isEnumType, validateComputedConfig } from "@vibeorm/schema";
|
|
15
15
|
function toClientName(params) {
|
|
16
16
|
const { name } = params;
|
|
17
17
|
if (name.length === 0)
|
|
@@ -165,6 +165,11 @@ function buildModelMeta(params) {
|
|
|
165
165
|
if (index.kind === "unique")
|
|
166
166
|
addCompound(index.fields.map((entry) => entry.name));
|
|
167
167
|
}
|
|
168
|
+
const computedSpecs = params.computed;
|
|
169
|
+
const computed = computedSpecs === undefined || Object.keys(computedSpecs).length === 0 ? null : new Map(Object.entries(computedSpecs).map(([name, spec]) => [
|
|
170
|
+
name,
|
|
171
|
+
{ name, type: spec.type, needs: spec.needs, compute: spec.compute }
|
|
172
|
+
]));
|
|
168
173
|
return {
|
|
169
174
|
name: model.name,
|
|
170
175
|
clientName: toClientName({ name: model.name }),
|
|
@@ -177,12 +182,19 @@ function buildModelMeta(params) {
|
|
|
177
182
|
compoundUniques,
|
|
178
183
|
relations: model.relations,
|
|
179
184
|
relationByName: new Map(model.relations.map((relation) => [relation.name, relation])),
|
|
180
|
-
isView: model.isView
|
|
185
|
+
isView: model.isView,
|
|
186
|
+
computed
|
|
181
187
|
};
|
|
182
188
|
}
|
|
183
189
|
function buildRuntimeMeta(params) {
|
|
184
|
-
const { schema } = params;
|
|
185
|
-
|
|
190
|
+
const { schema, computed } = params;
|
|
191
|
+
if (computed !== undefined && Object.keys(computed).length > 0) {
|
|
192
|
+
validateComputedConfig({ schema, computed });
|
|
193
|
+
}
|
|
194
|
+
const models = schema.models.map((model) => {
|
|
195
|
+
const specs = computed?.[model.name];
|
|
196
|
+
return buildModelMeta({ model, ...specs === undefined ? {} : { computed: specs } });
|
|
197
|
+
});
|
|
186
198
|
const modelByName = new Map;
|
|
187
199
|
for (const model of models) {
|
|
188
200
|
modelByName.set(model.name, model);
|
|
@@ -210,6 +222,13 @@ function getFieldMeta(params) {
|
|
|
210
222
|
const { model, field, context } = params;
|
|
211
223
|
const found = model.fieldByName.get(field);
|
|
212
224
|
if (found === undefined) {
|
|
225
|
+
if (model.computed !== null && model.computed.has(field)) {
|
|
226
|
+
throw new VibeError({
|
|
227
|
+
code: "VIBE_VALIDATION",
|
|
228
|
+
message: `"${field}" on model "${model.name}" is a computed field \u2014 computed fields appear in results (select/omit) only, not in ${context}`,
|
|
229
|
+
meta: { model: model.name, field, context, computed: true }
|
|
230
|
+
});
|
|
231
|
+
}
|
|
213
232
|
if (model.relationByName.has(field)) {
|
|
214
233
|
throw new VibeError({
|
|
215
234
|
code: "VIBE_VALIDATION",
|
|
@@ -678,6 +697,15 @@ function compileChildSelect(params) {
|
|
|
678
697
|
continue;
|
|
679
698
|
if (name === "_count" || model.relationByName.has(name))
|
|
680
699
|
continue;
|
|
700
|
+
if (model.computed !== null && model.computed.has(name)) {
|
|
701
|
+
if (value !== true) {
|
|
702
|
+
throw validationError({
|
|
703
|
+
message: `select.${name} must be true or false on model "${model.name}"`,
|
|
704
|
+
meta: { model: model.name, field: name }
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
continue;
|
|
708
|
+
}
|
|
681
709
|
const field = model.fieldByName.get(name);
|
|
682
710
|
if (field === undefined) {
|
|
683
711
|
throw validationError({
|
|
@@ -719,6 +747,8 @@ function compileChildOmit(params) {
|
|
|
719
747
|
meta: { model: model.name, relation, field: name }
|
|
720
748
|
});
|
|
721
749
|
}
|
|
750
|
+
if (model.computed !== null && model.computed.has(name))
|
|
751
|
+
continue;
|
|
722
752
|
if (!model.fieldByName.has(name)) {
|
|
723
753
|
throw validationError({
|
|
724
754
|
message: `unknown field "${name}" on model "${model.name}" in omit`,
|
|
@@ -827,6 +857,19 @@ function resolveNode(params) {
|
|
|
827
857
|
include: raw.include
|
|
828
858
|
});
|
|
829
859
|
const select = raw.select !== undefined ? compileChildSelect({ model: link.target, select: raw.select }) : raw.omit !== undefined ? compileChildOmit({ model: link.target, omit: raw.omit, relation: relation.name }) : null;
|
|
860
|
+
const targetComputed = link.target.computed;
|
|
861
|
+
let computed = null;
|
|
862
|
+
if (targetComputed !== null) {
|
|
863
|
+
if (raw.select !== undefined) {
|
|
864
|
+
const names = Object.entries(raw.select).filter(([name, value]) => targetComputed.has(name) && value === true).map(([name]) => name);
|
|
865
|
+
computed = names.length === 0 ? null : names;
|
|
866
|
+
} else if (raw.omit !== undefined) {
|
|
867
|
+
const names = [...targetComputed.keys()].filter((name) => raw.omit[name] !== true);
|
|
868
|
+
computed = names.length === 0 ? null : names;
|
|
869
|
+
} else {
|
|
870
|
+
computed = [...targetComputed.keys()];
|
|
871
|
+
}
|
|
872
|
+
}
|
|
830
873
|
let projected = null;
|
|
831
874
|
const strip = [];
|
|
832
875
|
if (select !== null) {
|
|
@@ -837,6 +880,13 @@ function resolveNode(params) {
|
|
|
837
880
|
additions.add(name);
|
|
838
881
|
}
|
|
839
882
|
}
|
|
883
|
+
if (computed !== null && targetComputed !== null) {
|
|
884
|
+
for (const name of computed) {
|
|
885
|
+
for (const need of targetComputed.get(name)?.needs ?? []) {
|
|
886
|
+
additions.add(need);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
}
|
|
840
890
|
for (const name of additions) {
|
|
841
891
|
if (!fetch.includes(name)) {
|
|
842
892
|
fetch.push(name);
|
|
@@ -848,7 +898,7 @@ function resolveNode(params) {
|
|
|
848
898
|
return {
|
|
849
899
|
name: relation.name,
|
|
850
900
|
link,
|
|
851
|
-
args: { where, orderBy: raw.orderBy, take, skip, select, projected, strip, nested }
|
|
901
|
+
args: { where, orderBy: raw.orderBy, take, skip, select, projected, strip, computed, nested }
|
|
852
902
|
};
|
|
853
903
|
}
|
|
854
904
|
function resolveRelationSelection(params) {
|
|
@@ -1807,6 +1857,8 @@ function compileSelect(params) {
|
|
|
1807
1857
|
continue;
|
|
1808
1858
|
if (name === "_count" || model.relationByName.has(name))
|
|
1809
1859
|
continue;
|
|
1860
|
+
if (model.computed !== null && model.computed.has(name))
|
|
1861
|
+
continue;
|
|
1810
1862
|
if (value !== true) {
|
|
1811
1863
|
throw validationError2({
|
|
1812
1864
|
message: `select.${name} must be true or false on model "${model.name}"`,
|
|
@@ -1823,6 +1875,38 @@ function compileSelect(params) {
|
|
|
1823
1875
|
}
|
|
1824
1876
|
return names;
|
|
1825
1877
|
}
|
|
1878
|
+
function resolveComputedSelection(params) {
|
|
1879
|
+
const { model, args, aggregating } = params;
|
|
1880
|
+
const specs = model.computed;
|
|
1881
|
+
if (specs === null || aggregating)
|
|
1882
|
+
return null;
|
|
1883
|
+
if (isPlainObject2(args.select)) {
|
|
1884
|
+
const names = [];
|
|
1885
|
+
for (const [name, value] of Object.entries(args.select)) {
|
|
1886
|
+
if (!specs.has(name) || value === undefined || value === false)
|
|
1887
|
+
continue;
|
|
1888
|
+
if (value !== true) {
|
|
1889
|
+
throw validationError2({
|
|
1890
|
+
message: `select.${name} must be true or false on model "${model.name}"`,
|
|
1891
|
+
meta: { model: model.name, field: name }
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
names.push(name);
|
|
1895
|
+
}
|
|
1896
|
+
return names.length === 0 ? null : names;
|
|
1897
|
+
}
|
|
1898
|
+
if (isPlainObject2(args.omit)) {
|
|
1899
|
+
const names = [];
|
|
1900
|
+
for (const name of specs.keys()) {
|
|
1901
|
+
const value = args.omit[name];
|
|
1902
|
+
if (value === true)
|
|
1903
|
+
continue;
|
|
1904
|
+
names.push(name);
|
|
1905
|
+
}
|
|
1906
|
+
return names.length === 0 ? null : names;
|
|
1907
|
+
}
|
|
1908
|
+
return [...specs.keys()];
|
|
1909
|
+
}
|
|
1826
1910
|
var fullProjectionCache = new WeakMap;
|
|
1827
1911
|
function projectionColumns(params) {
|
|
1828
1912
|
const { model, selectedFields, table } = params;
|
|
@@ -2567,6 +2651,8 @@ function compileOmit(params) {
|
|
|
2567
2651
|
meta: { model: model.name, field: name }
|
|
2568
2652
|
});
|
|
2569
2653
|
}
|
|
2654
|
+
if (model.computed !== null && model.computed.has(name))
|
|
2655
|
+
continue;
|
|
2570
2656
|
omitted.add(getFieldMeta({ model, field: name, context: "omit" }).name);
|
|
2571
2657
|
}
|
|
2572
2658
|
const kept = model.fields.filter((field) => !omitted.has(field.name)).map((field) => field.name);
|
|
@@ -2907,7 +2993,8 @@ function resolveResultShape(params) {
|
|
|
2907
2993
|
const { meta, model, method, args } = params;
|
|
2908
2994
|
assertArgs({ method, args, model });
|
|
2909
2995
|
const relations = resolveRelationSelection({ meta, model, select: args.select, include: args.include });
|
|
2910
|
-
const
|
|
2996
|
+
const computed = resolveComputedSelection({ model, args, aggregating: false });
|
|
2997
|
+
const scalarSelect = args.select === undefined ? null : compileSelect({ model, select: args.select, allowEmpty: relations !== null || computed !== null });
|
|
2911
2998
|
const resultFields = scalarSelect === null ? null : [...scalarSelect];
|
|
2912
2999
|
if (relations !== null && resultFields !== null) {
|
|
2913
3000
|
for (const node of relations.relations)
|
|
@@ -2915,7 +3002,11 @@ function resolveResultShape(params) {
|
|
|
2915
3002
|
if (relations.counts.length > 0)
|
|
2916
3003
|
resultFields.push("_count");
|
|
2917
3004
|
}
|
|
2918
|
-
|
|
3005
|
+
if (computed !== null && resultFields !== null) {
|
|
3006
|
+
for (const name of computed)
|
|
3007
|
+
resultFields.push(name);
|
|
3008
|
+
}
|
|
3009
|
+
return { relations, resultFields, computed };
|
|
2919
3010
|
}
|
|
2920
3011
|
function buildQuery(params) {
|
|
2921
3012
|
const { meta, dialect, method } = params;
|
|
@@ -2951,7 +3042,8 @@ function buildQuery(params) {
|
|
|
2951
3042
|
meta: { model: model.name, method }
|
|
2952
3043
|
});
|
|
2953
3044
|
}
|
|
2954
|
-
const
|
|
3045
|
+
const computedSelection = resolveComputedSelection({ model, args, aggregating });
|
|
3046
|
+
const scalarSelect = aggregating ? null : args.select !== undefined ? compileSelect({ model, select: args.select, allowEmpty: relations !== null || computedSelection !== null }) : args.omit !== undefined ? compileOmit({ model, omit: args.omit }) : null;
|
|
2955
3047
|
let selectedFields = scalarSelect === null ? null : [...scalarSelect];
|
|
2956
3048
|
let resultFields = scalarSelect === null ? null : [...scalarSelect];
|
|
2957
3049
|
if (relations !== null && resultFields !== null) {
|
|
@@ -2960,6 +3052,10 @@ function buildQuery(params) {
|
|
|
2960
3052
|
if (relations.counts.length > 0)
|
|
2961
3053
|
resultFields.push("_count");
|
|
2962
3054
|
}
|
|
3055
|
+
if (computedSelection !== null && resultFields !== null) {
|
|
3056
|
+
for (const name of computedSelection)
|
|
3057
|
+
resultFields.push(name);
|
|
3058
|
+
}
|
|
2963
3059
|
if (relations !== null && selectedFields !== null) {
|
|
2964
3060
|
for (const name of requiredParentFields({ meta, model, selection: relations })) {
|
|
2965
3061
|
if (!selectedFields.includes(name))
|
|
@@ -2972,12 +3068,21 @@ function buildQuery(params) {
|
|
|
2972
3068
|
selectedFields.push(field.name);
|
|
2973
3069
|
}
|
|
2974
3070
|
}
|
|
3071
|
+
if (computedSelection !== null && selectedFields !== null && model.computed !== null) {
|
|
3072
|
+
for (const name of computedSelection) {
|
|
3073
|
+
for (const need of model.computed.get(name)?.needs ?? []) {
|
|
3074
|
+
if (!selectedFields.includes(need))
|
|
3075
|
+
selectedFields.push(need);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
}
|
|
2975
3079
|
const selectArg = args.select === undefined || aggregating ? null : args.select;
|
|
2976
3080
|
const base = {
|
|
2977
3081
|
method,
|
|
2978
3082
|
model,
|
|
2979
3083
|
selectedFields,
|
|
2980
3084
|
resultFields,
|
|
3085
|
+
computed: computedSelection,
|
|
2981
3086
|
relations,
|
|
2982
3087
|
relationStrategy,
|
|
2983
3088
|
takeApplied: args.take !== undefined,
|
|
@@ -3352,7 +3457,68 @@ function buildQuery(params) {
|
|
|
3352
3457
|
}
|
|
3353
3458
|
}
|
|
3354
3459
|
// packages/runtime/src/relation-loader.ts
|
|
3460
|
+
import { VibeError as VibeError6 } from "@vibeorm/schema";
|
|
3461
|
+
|
|
3462
|
+
// packages/runtime/src/computed.ts
|
|
3355
3463
|
import { VibeError as VibeError5 } from "@vibeorm/schema";
|
|
3464
|
+
function sameNeeds(a, b) {
|
|
3465
|
+
if (a.length !== b.length)
|
|
3466
|
+
return false;
|
|
3467
|
+
const left = [...a].sort();
|
|
3468
|
+
const right = [...b].sort();
|
|
3469
|
+
return left.every((value, index) => value === right[index]);
|
|
3470
|
+
}
|
|
3471
|
+
function assertComputedManifest(params) {
|
|
3472
|
+
const { manifest } = params;
|
|
3473
|
+
const specs = params.specs ?? {};
|
|
3474
|
+
const live = new Map;
|
|
3475
|
+
for (const [model, fields] of Object.entries(specs)) {
|
|
3476
|
+
for (const [field, spec] of Object.entries(fields)) {
|
|
3477
|
+
live.set(`${model}.${field}`, { type: spec.type, needs: spec.needs });
|
|
3478
|
+
}
|
|
3479
|
+
}
|
|
3480
|
+
const problems = [];
|
|
3481
|
+
for (const baked of manifest) {
|
|
3482
|
+
const key = `${baked.model}.${baked.field}`;
|
|
3483
|
+
const current = live.get(key);
|
|
3484
|
+
if (current === undefined) {
|
|
3485
|
+
problems.push(`"${key}" was baked into the client but is missing from the computed config`);
|
|
3486
|
+
continue;
|
|
3487
|
+
}
|
|
3488
|
+
if (current.type !== baked.type) {
|
|
3489
|
+
problems.push(`"${key}" type changed (generated ${JSON.stringify(baked.type)}, live ${JSON.stringify(current.type)})`);
|
|
3490
|
+
} else if (!sameNeeds(current.needs, baked.needs)) {
|
|
3491
|
+
problems.push(`"${key}" needs changed since generate`);
|
|
3492
|
+
}
|
|
3493
|
+
live.delete(key);
|
|
3494
|
+
}
|
|
3495
|
+
for (const key of live.keys()) {
|
|
3496
|
+
problems.push(`"${key}" is configured but missing from the generated client`);
|
|
3497
|
+
}
|
|
3498
|
+
if (problems.length > 0) {
|
|
3499
|
+
throw new VibeError5({
|
|
3500
|
+
code: "VIBE_STALE_CLIENT",
|
|
3501
|
+
message: `computed-field config changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
|
|
3502
|
+
meta: { problems }
|
|
3503
|
+
});
|
|
3504
|
+
}
|
|
3505
|
+
}
|
|
3506
|
+
function applyComputedFields(params) {
|
|
3507
|
+
const { model, rows, names } = params;
|
|
3508
|
+
const specs = model.computed;
|
|
3509
|
+
if (names === null || names.length === 0 || specs === null || rows.length === 0)
|
|
3510
|
+
return;
|
|
3511
|
+
for (const row of rows) {
|
|
3512
|
+
for (const name of names) {
|
|
3513
|
+
const spec = specs.get(name);
|
|
3514
|
+
if (spec === undefined)
|
|
3515
|
+
continue;
|
|
3516
|
+
row[name] = spec.compute(row);
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
}
|
|
3520
|
+
|
|
3521
|
+
// packages/runtime/src/relation-loader.ts
|
|
3356
3522
|
var COUNT_VALUE_ALIAS = "__vibe_count";
|
|
3357
3523
|
function distinctKeys(params) {
|
|
3358
3524
|
const { rows, field } = params;
|
|
@@ -3494,7 +3660,7 @@ async function loadManyToMany(params) {
|
|
|
3494
3660
|
const link = node.link;
|
|
3495
3661
|
const join = link.join;
|
|
3496
3662
|
if (join === null) {
|
|
3497
|
-
throw new
|
|
3663
|
+
throw new VibeError6({
|
|
3498
3664
|
code: "VIBE_INTERNAL",
|
|
3499
3665
|
message: `manyToMany relation "${node.name}" resolved without join-table wiring`
|
|
3500
3666
|
});
|
|
@@ -3643,6 +3809,9 @@ async function loadNode(params) {
|
|
|
3643
3809
|
}
|
|
3644
3810
|
async function finishNode(params) {
|
|
3645
3811
|
const { ctx, node, kept } = params;
|
|
3812
|
+
if (node.args.computed !== null && kept.length > 0) {
|
|
3813
|
+
applyComputedFields({ model: node.link.target, rows: kept, names: node.args.computed });
|
|
3814
|
+
}
|
|
3646
3815
|
if (node.args.nested !== null && kept.length > 0) {
|
|
3647
3816
|
await attachRelations({
|
|
3648
3817
|
dialect: ctx.dialect,
|
|
@@ -3689,7 +3858,7 @@ async function attachLateralRelations(params) {
|
|
|
3689
3858
|
try {
|
|
3690
3859
|
parsed = JSON.parse(raw);
|
|
3691
3860
|
} catch (cause) {
|
|
3692
|
-
throw new
|
|
3861
|
+
throw new VibeError6({
|
|
3693
3862
|
code: "VIBE_INTERNAL",
|
|
3694
3863
|
message: `relation column "${column}" returned text that is not valid JSON`,
|
|
3695
3864
|
meta: { relation: node.name },
|
|
@@ -3726,12 +3895,12 @@ async function attachLateralRelations(params) {
|
|
|
3726
3895
|
await loadCounts({ ctx, model, rows, counts: selection.counts });
|
|
3727
3896
|
}
|
|
3728
3897
|
// packages/runtime/src/nested-writes.ts
|
|
3729
|
-
import { VibeError as
|
|
3898
|
+
import { VibeError as VibeError7 } from "@vibeorm/schema";
|
|
3730
3899
|
function validationError3(params) {
|
|
3731
|
-
return new
|
|
3900
|
+
return new VibeError7({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
3732
3901
|
}
|
|
3733
3902
|
function notFound(params) {
|
|
3734
|
-
return new
|
|
3903
|
+
return new VibeError7({ code: "VIBE_NOT_FOUND", message: params.message, meta: params.meta });
|
|
3735
3904
|
}
|
|
3736
3905
|
function isPlainObject3(value) {
|
|
3737
3906
|
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
|
|
@@ -4561,7 +4730,7 @@ async function updateWithNested(params) {
|
|
|
4561
4730
|
});
|
|
4562
4731
|
}
|
|
4563
4732
|
// packages/runtime/src/extensions.ts
|
|
4564
|
-
import { VibeError as
|
|
4733
|
+
import { VibeError as VibeError8, extensionManifestEntry } from "@vibeorm/schema";
|
|
4565
4734
|
function assertExtensionManifest(params) {
|
|
4566
4735
|
const { manifest } = params;
|
|
4567
4736
|
const instances = params.instances ?? [];
|
|
@@ -4584,7 +4753,7 @@ function assertExtensionManifest(params) {
|
|
|
4584
4753
|
problems.push(`"${name}" is configured but missing from the generated client`);
|
|
4585
4754
|
}
|
|
4586
4755
|
if (problems.length > 0) {
|
|
4587
|
-
throw new
|
|
4756
|
+
throw new VibeError8({
|
|
4588
4757
|
code: "VIBE_STALE_CLIENT",
|
|
4589
4758
|
message: `extension list changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
|
|
4590
4759
|
meta: { manifest, problems }
|
|
@@ -4599,7 +4768,7 @@ function buildExtensionOperatorMap(params) {
|
|
|
4599
4768
|
for (const operator of extension.sql?.whereOperators ?? []) {
|
|
4600
4769
|
const existing = owner.get(operator.name);
|
|
4601
4770
|
if (existing !== undefined) {
|
|
4602
|
-
throw new
|
|
4771
|
+
throw new VibeError8({
|
|
4603
4772
|
code: "VIBE_CONFIG",
|
|
4604
4773
|
message: `where operator "${operator.name}" is claimed by both "${existing}" and "${extension.name}"`,
|
|
4605
4774
|
meta: { operator: operator.name }
|
|
@@ -4615,7 +4784,7 @@ function buildExtensionOperatorMap(params) {
|
|
|
4615
4784
|
options: extension.options
|
|
4616
4785
|
});
|
|
4617
4786
|
if ("error" in compiled) {
|
|
4618
|
-
throw new
|
|
4787
|
+
throw new VibeError8({
|
|
4619
4788
|
code: "VIBE_VALIDATION",
|
|
4620
4789
|
message: `"${operator.name}" on ${model}.${field}: ${compiled.error}`,
|
|
4621
4790
|
meta: { model, field, operator: operator.name, extension: extension.name }
|
|
@@ -4647,9 +4816,9 @@ function buildConnectHooks(params) {
|
|
|
4647
4816
|
return hooks;
|
|
4648
4817
|
}
|
|
4649
4818
|
// packages/runtime/src/views.ts
|
|
4650
|
-
import { VibeError as
|
|
4819
|
+
import { VibeError as VibeError9 } from "@vibeorm/schema";
|
|
4651
4820
|
function validationError4(params) {
|
|
4652
|
-
return new
|
|
4821
|
+
return new VibeError9({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
4653
4822
|
}
|
|
4654
4823
|
|
|
4655
4824
|
class ViewResult {
|
|
@@ -5001,7 +5170,7 @@ function createView(params) {
|
|
|
5001
5170
|
for (const [clientName, mask] of resolved) {
|
|
5002
5171
|
const delegate = models[clientName];
|
|
5003
5172
|
if (delegate === undefined) {
|
|
5004
|
-
throw new
|
|
5173
|
+
throw new VibeError9({
|
|
5005
5174
|
code: "VIBE_INTERNAL",
|
|
5006
5175
|
message: `view "${name}": no delegate for model "${mask.model.name}"`,
|
|
5007
5176
|
meta: { view: name, model: mask.model.name }
|
|
@@ -5017,7 +5186,7 @@ function attachDefineView(params) {
|
|
|
5017
5186
|
Object.defineProperty(client, "$defineView", { value, enumerable: true });
|
|
5018
5187
|
}
|
|
5019
5188
|
// packages/runtime/src/client.ts
|
|
5020
|
-
import { VibeError as
|
|
5189
|
+
import { VibeError as VibeError10, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
|
|
5021
5190
|
import { ParamCollector, getSqlDialect } from "@vibeorm/sql";
|
|
5022
5191
|
|
|
5023
5192
|
// packages/runtime/src/render-cache.ts
|
|
@@ -5437,7 +5606,7 @@ function renderStatementCached(params) {
|
|
|
5437
5606
|
// packages/runtime/src/client.ts
|
|
5438
5607
|
function notFound2(params) {
|
|
5439
5608
|
const { model, method } = params;
|
|
5440
|
-
return new
|
|
5609
|
+
return new VibeError10({
|
|
5441
5610
|
code: "VIBE_NOT_FOUND",
|
|
5442
5611
|
message: `no ${model} record matched the where clause of ${method}()`,
|
|
5443
5612
|
meta: { model, method }
|
|
@@ -5445,7 +5614,7 @@ function notFound2(params) {
|
|
|
5445
5614
|
}
|
|
5446
5615
|
function viewIsReadOnly(params) {
|
|
5447
5616
|
const { model, method } = params;
|
|
5448
|
-
return new
|
|
5617
|
+
return new VibeError10({
|
|
5449
5618
|
code: "VIBE_VALIDATION",
|
|
5450
5619
|
message: `${model} is a view \u2014 ${method}() is not available on views (they are read-only); write to the underlying table instead`,
|
|
5451
5620
|
meta: { model, method }
|
|
@@ -5574,7 +5743,7 @@ class QueryPromise extends Promise {
|
|
|
5574
5743
|
}
|
|
5575
5744
|
claim() {
|
|
5576
5745
|
if (this.started) {
|
|
5577
|
-
throw new
|
|
5746
|
+
throw new VibeError10({
|
|
5578
5747
|
code: "VIBE_VALIDATION",
|
|
5579
5748
|
message: "$transaction([...]) needs operations that have not dispatched \u2014 pass delegate calls in the same tick they are created (this one was awaited or created earlier and already executed outside the transaction)"
|
|
5580
5749
|
});
|
|
@@ -5718,7 +5887,7 @@ async function insertedKeyWhere(params) {
|
|
|
5718
5887
|
});
|
|
5719
5888
|
const id = result.rows[0]?.id;
|
|
5720
5889
|
if (id === undefined || id === null) {
|
|
5721
|
-
throw new
|
|
5890
|
+
throw new VibeError10({
|
|
5722
5891
|
code: "VIBE_INTERNAL",
|
|
5723
5892
|
message: `could not read back the generated key for ${source.model.name}`,
|
|
5724
5893
|
meta: { model: source.model.name }
|
|
@@ -5895,6 +6064,9 @@ async function finalizeRows(params) {
|
|
|
5895
6064
|
});
|
|
5896
6065
|
}
|
|
5897
6066
|
}
|
|
6067
|
+
if (shape.computed !== null && rows.length > 0) {
|
|
6068
|
+
applyComputedFields({ model, rows, names: shape.computed });
|
|
6069
|
+
}
|
|
5898
6070
|
if (shape.resultFields === null)
|
|
5899
6071
|
return rows;
|
|
5900
6072
|
return rows.map((row) => project({ row, fields: shape.resultFields }));
|
|
@@ -5968,7 +6140,8 @@ function createDelegate(params) {
|
|
|
5968
6140
|
const shapeOf = (built) => ({
|
|
5969
6141
|
relations: built.relations,
|
|
5970
6142
|
resultFields: built.resultFields,
|
|
5971
|
-
relationStrategy: built.relationStrategy
|
|
6143
|
+
relationStrategy: built.relationStrategy,
|
|
6144
|
+
computed: built.computed
|
|
5972
6145
|
});
|
|
5973
6146
|
const findMany = async (target, args) => {
|
|
5974
6147
|
const built = plan({ engine: target, model, method: "findMany", args });
|
|
@@ -6061,7 +6234,7 @@ function createDelegate(params) {
|
|
|
6061
6234
|
const upsert = async (target, args) => {
|
|
6062
6235
|
const modelMeta = getModelMeta({ meta: target.meta, model });
|
|
6063
6236
|
if (hasNestedWrites({ model: modelMeta, data: args.create }) || hasNestedWrites({ model: modelMeta, data: args.update })) {
|
|
6064
|
-
throw new
|
|
6237
|
+
throw new VibeError10({
|
|
6065
6238
|
code: "VIBE_VALIDATION",
|
|
6066
6239
|
message: `nested writes are not supported inside upsert arms on ${modelMeta.name} \u2014 use create()/update() with nested data`,
|
|
6067
6240
|
meta: { model: modelMeta.name }
|
|
@@ -6216,7 +6389,7 @@ function buildClient(params) {
|
|
|
6216
6389
|
let claimError = null;
|
|
6217
6390
|
for (const operation of operations) {
|
|
6218
6391
|
if (!(operation instanceof QueryPromise)) {
|
|
6219
|
-
claimError = new
|
|
6392
|
+
claimError = new VibeError10({
|
|
6220
6393
|
code: "VIBE_VALIDATION",
|
|
6221
6394
|
message: "$transaction([...]) takes query promises from model delegate calls \u2014 a foreign promise is already running and cannot join the transaction"
|
|
6222
6395
|
});
|
|
@@ -6255,7 +6428,7 @@ function buildClient(params) {
|
|
|
6255
6428
|
$models: models,
|
|
6256
6429
|
$on: (event, listener) => {
|
|
6257
6430
|
if (event !== "query") {
|
|
6258
|
-
throw new
|
|
6431
|
+
throw new VibeError10({
|
|
6259
6432
|
code: "VIBE_VALIDATION",
|
|
6260
6433
|
message: `unknown client event "${String(event)}" \u2014 supported: "query"`,
|
|
6261
6434
|
meta: { event }
|
|
@@ -6318,7 +6491,7 @@ function makeMountContext(params) {
|
|
|
6318
6491
|
compileWhere: ({ where, model: modelName, qualify }) => {
|
|
6319
6492
|
const target = modelName === undefined ? model : getModelMeta({ meta: engine.meta, model: modelName });
|
|
6320
6493
|
if (target === null) {
|
|
6321
|
-
throw new
|
|
6494
|
+
throw new VibeError10({
|
|
6322
6495
|
code: "VIBE_INTERNAL",
|
|
6323
6496
|
message: `extension "${extension}" compiled a where with no model in scope`,
|
|
6324
6497
|
meta: { extension }
|
|
@@ -6344,7 +6517,7 @@ function attachExtensionMounts(params) {
|
|
|
6344
6517
|
const model = getModelMeta({ meta: engine.meta, model: mount.model ?? "" });
|
|
6345
6518
|
const delegate = models[model.name];
|
|
6346
6519
|
if (delegate === undefined || mount.property in delegate) {
|
|
6347
|
-
throw new
|
|
6520
|
+
throw new VibeError10({
|
|
6348
6521
|
code: "VIBE_CONFIG",
|
|
6349
6522
|
message: `extension "${mount.extension}" cannot mount "${mount.property}" on ${model.name} \u2014 the name is taken`,
|
|
6350
6523
|
meta: { extension: mount.extension, model: model.name, property: mount.property }
|
|
@@ -6355,7 +6528,7 @@ function attachExtensionMounts(params) {
|
|
|
6355
6528
|
continue;
|
|
6356
6529
|
}
|
|
6357
6530
|
if (!mount.property.startsWith("$") || mount.property in client) {
|
|
6358
|
-
throw new
|
|
6531
|
+
throw new VibeError10({
|
|
6359
6532
|
code: "VIBE_CONFIG",
|
|
6360
6533
|
message: `extension "${mount.extension}" cannot mount client property "${mount.property}" \u2014 root mounts are $-prefixed and must not collide`,
|
|
6361
6534
|
meta: { extension: mount.extension, property: mount.property }
|
|
@@ -6366,15 +6539,28 @@ function attachExtensionMounts(params) {
|
|
|
6366
6539
|
}
|
|
6367
6540
|
}
|
|
6368
6541
|
function createClient(params) {
|
|
6369
|
-
const { schema, adapter, options, extensions } = params;
|
|
6542
|
+
const { schema, adapter, options, extensions, computed } = params;
|
|
6370
6543
|
const dialectName = adapter.dialect;
|
|
6371
6544
|
if (extensions?.manifest !== undefined) {
|
|
6372
6545
|
assertExtensionManifest({ manifest: extensions.manifest, instances: extensions.instances });
|
|
6373
6546
|
}
|
|
6547
|
+
if (computed?.manifest !== undefined) {
|
|
6548
|
+
assertComputedManifest({ manifest: computed.manifest, specs: computed.specs });
|
|
6549
|
+
}
|
|
6550
|
+
if (computed === undefined && options !== undefined && options["computed"] !== undefined) {
|
|
6551
|
+
throw new VibeError10({
|
|
6552
|
+
code: "VIBE_CONFIG",
|
|
6553
|
+
message: "this client was generated without computed fields \u2014 declare them in vibeorm.config.ts and re-run vibeorm generate",
|
|
6554
|
+
meta: {}
|
|
6555
|
+
});
|
|
6556
|
+
}
|
|
6374
6557
|
const instances = extensions?.instances ?? [];
|
|
6375
6558
|
const operatorMap = instances.length === 0 ? null : buildExtensionOperatorMap({ instances, dialect: dialectName, provider: adapter.provider });
|
|
6376
6559
|
const connectHooks = instances.length === 0 ? [] : buildConnectHooks({ instances, dialect: dialectName, provider: adapter.provider });
|
|
6377
|
-
const baseMeta = buildRuntimeMeta({
|
|
6560
|
+
const baseMeta = buildRuntimeMeta({
|
|
6561
|
+
schema,
|
|
6562
|
+
...computed?.specs === undefined ? {} : { computed: computed.specs }
|
|
6563
|
+
});
|
|
6378
6564
|
const meta = operatorMap === null ? baseMeta : { ...baseMeta, extensionOperators: operatorMap };
|
|
6379
6565
|
return buildClient({
|
|
6380
6566
|
engine: {
|
|
@@ -6426,6 +6612,8 @@ export {
|
|
|
6426
6612
|
attachLateralRelations,
|
|
6427
6613
|
attachDefineView,
|
|
6428
6614
|
assertExtensionManifest,
|
|
6615
|
+
assertComputedManifest,
|
|
6616
|
+
applyComputedFields,
|
|
6429
6617
|
ViewResult,
|
|
6430
6618
|
SQLITE_CODECS,
|
|
6431
6619
|
POSTGRES_CODECS,
|
|
@@ -6434,4 +6622,4 @@ export {
|
|
|
6434
6622
|
COUNT_ALIAS
|
|
6435
6623
|
};
|
|
6436
6624
|
|
|
6437
|
-
//# debugId=
|
|
6625
|
+
//# debugId=D09B25CE72AA9DC064756E2164756E21
|