@vibeorm/runtime 2.2.0 → 2.3.1
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/adapter-kit/index.d.ts +6 -3
- package/dist/adapter-kit/index.d.ts.map +1 -1
- package/dist/adapter-kit/index.js +191 -32
- package/dist/adapter-kit/index.js.map +8 -5
- package/dist/adapter-kit/nested-options.d.ts +22 -0
- package/dist/adapter-kit/nested-options.d.ts.map +1 -0
- package/dist/adapter-kit/savepoint-gate.d.ts +31 -0
- package/dist/adapter-kit/savepoint-gate.d.ts.map +1 -1
- package/dist/adapter-kit/session.d.ts +9 -0
- package/dist/adapter-kit/session.d.ts.map +1 -0
- package/dist/adapter-kit/sqlite-session.d.ts +8 -0
- package/dist/adapter-kit/sqlite-session.d.ts.map +1 -0
- package/dist/adapter.d.ts +34 -27
- package/dist/adapter.d.ts.map +1 -1
- package/dist/bulk-upsert.d.ts +5 -3
- package/dist/bulk-upsert.d.ts.map +1 -1
- package/dist/client-types.d.ts +14 -0
- package/dist/client-types.d.ts.map +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/codecs.d.ts +5 -0
- package/dist/codecs.d.ts.map +1 -1
- package/dist/database-module.d.ts +27 -0
- package/dist/database-module.d.ts.map +1 -0
- package/dist/diagnostics/plan.d.ts +3 -1
- package/dist/diagnostics/plan.d.ts.map +1 -1
- package/dist/diagnostics/preview.d.ts +3 -1
- package/dist/diagnostics/preview.d.ts.map +1 -1
- package/dist/diagnostics/statement-diagnostics.d.ts +10 -6
- package/dist/diagnostics/statement-diagnostics.d.ts.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +992 -759
- package/dist/index.js.map +20 -18
- package/dist/module-context.d.ts +20 -0
- package/dist/module-context.d.ts.map +1 -0
- package/dist/nested-writes.d.ts.map +1 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/query-builder.d.ts.map +1 -1
- package/dist/read-only.d.ts.map +1 -1
- package/dist/scoped.d.ts.map +1 -1
- package/dist/sql-access.d.ts +18 -2
- package/dist/sql-access.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -50,22 +50,30 @@ function createSqlModel(params) {
|
|
|
50
50
|
return Object.freeze({ table: params.alias === undefined ? table : sql`${table} AS ${sql.ident({ parts: [params.alias] })}`, fields: Object.freeze(fields) });
|
|
51
51
|
}
|
|
52
52
|
// packages/runtime/src/adapter.ts
|
|
53
|
+
import { VibeError as VibeError2 } from "@vibeorm/schema";
|
|
53
54
|
function toSqlExecutor(params) {
|
|
54
55
|
const { adapter } = params;
|
|
55
|
-
|
|
56
|
-
const result = await adapter.executeUnsafe(
|
|
57
|
-
text: queryParams.text,
|
|
58
|
-
values: queryParams.values
|
|
59
|
-
});
|
|
56
|
+
const execute = async (queryParams) => {
|
|
57
|
+
const result = await adapter.executeUnsafe(queryParams);
|
|
60
58
|
return result.rows;
|
|
61
59
|
};
|
|
60
|
+
const withSession = async ({ run }) => {
|
|
61
|
+
if (adapter.withSession === undefined)
|
|
62
|
+
throw new VibeError2({
|
|
63
|
+
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
64
|
+
message: "This adapter cannot reserve a migration session. Supply an adapter with session support or an explicitly owned SqlExecutor.",
|
|
65
|
+
meta: { capability: "session", provider: adapter.provider }
|
|
66
|
+
});
|
|
67
|
+
return adapter.withSession({ run });
|
|
68
|
+
};
|
|
69
|
+
return Object.assign(execute, { withSession });
|
|
62
70
|
}
|
|
63
71
|
// packages/runtime/src/db-now.ts
|
|
64
|
-
import { VibeError as
|
|
72
|
+
import { VibeError as VibeError3 } from "@vibeorm/schema";
|
|
65
73
|
function dbNow(options) {
|
|
66
74
|
const offset = options?.plusSeconds ?? 0;
|
|
67
75
|
if (typeof offset !== "number" || !Number.isFinite(offset)) {
|
|
68
|
-
throw new
|
|
76
|
+
throw new VibeError3({
|
|
69
77
|
code: "VIBE_VALIDATION",
|
|
70
78
|
message: `dbNow plusSeconds must be a finite number of seconds (fractional is fine), got ${String(offset)}`,
|
|
71
79
|
meta: { plusSeconds: offset }
|
|
@@ -84,10 +92,10 @@ function isDbNow(value) {
|
|
|
84
92
|
return candidate.__vibeDbNow === true && typeof candidate.offsetSeconds === "number" && Number.isFinite(candidate.offsetSeconds);
|
|
85
93
|
}
|
|
86
94
|
// packages/runtime/src/advisory-lock.ts
|
|
87
|
-
import { VibeError as
|
|
95
|
+
import { VibeError as VibeError4, unsupportedCapability } from "@vibeorm/schema";
|
|
88
96
|
import { ADVISORY_KEY_BOUNDS } from "@vibeorm/sql";
|
|
89
97
|
function validationError(params) {
|
|
90
|
-
return new
|
|
98
|
+
return new VibeError4({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
91
99
|
}
|
|
92
100
|
function readInt32(params) {
|
|
93
101
|
const { value, member, method } = params;
|
|
@@ -157,8 +165,8 @@ function compileAdvisoryLock(params) {
|
|
|
157
165
|
}
|
|
158
166
|
const mode = readMode({ value: request.mode, method });
|
|
159
167
|
if (hasSingle) {
|
|
160
|
-
const
|
|
161
|
-
return statementFor({ method, mode, key
|
|
168
|
+
const key = { key: readInt64({ value: request.key, method }) };
|
|
169
|
+
return statementFor({ method, mode, key });
|
|
162
170
|
}
|
|
163
171
|
if (!("classId" in request) || !("objectId" in request)) {
|
|
164
172
|
throw validationError({
|
|
@@ -192,7 +200,7 @@ function statementFor(params) {
|
|
|
192
200
|
};
|
|
193
201
|
}
|
|
194
202
|
// packages/runtime/src/model-meta.ts
|
|
195
|
-
import { VibeError as
|
|
203
|
+
import { VibeError as VibeError5, enumStorageOf, isEnumType as isEnumType2, validateComputedConfig } from "@vibeorm/schema";
|
|
196
204
|
import { enumMembershipCodec } from "@vibeorm/sql";
|
|
197
205
|
function toClientName(params) {
|
|
198
206
|
const { name } = params;
|
|
@@ -321,7 +329,7 @@ function buildModelMeta(params) {
|
|
|
321
329
|
for (const name of model.primaryKey.fields) {
|
|
322
330
|
const field = fieldByName.get(name);
|
|
323
331
|
if (field === undefined) {
|
|
324
|
-
throw new
|
|
332
|
+
throw new VibeError5({
|
|
325
333
|
code: "VIBE_SCHEMA_INVALID",
|
|
326
334
|
message: `model "${model.name}" declares composite primary-key field "${name}", which does not exist`,
|
|
327
335
|
meta: { model: model.name, field: name }
|
|
@@ -427,7 +435,7 @@ function getModelMeta(params) {
|
|
|
427
435
|
const { meta, model } = params;
|
|
428
436
|
const found = meta.modelByName.get(model);
|
|
429
437
|
if (found === undefined) {
|
|
430
|
-
throw new
|
|
438
|
+
throw new VibeError5({
|
|
431
439
|
code: "VIBE_VALIDATION",
|
|
432
440
|
message: `unknown model "${model}"`,
|
|
433
441
|
meta: { model, known: [...new Set(meta.models.map((entry) => entry.name))] }
|
|
@@ -440,20 +448,20 @@ function getFieldMeta(params) {
|
|
|
440
448
|
const found = model.fieldByName.get(field);
|
|
441
449
|
if (found === undefined) {
|
|
442
450
|
if (model.computed !== null && model.computed.has(field)) {
|
|
443
|
-
throw new
|
|
451
|
+
throw new VibeError5({
|
|
444
452
|
code: "VIBE_VALIDATION",
|
|
445
453
|
message: `"${field}" on model "${model.name}" is a computed field \u2014 computed fields appear in results (select/omit) only, not in ${context}`,
|
|
446
454
|
meta: { model: model.name, field, context, computed: true }
|
|
447
455
|
});
|
|
448
456
|
}
|
|
449
457
|
if (model.relationByName.has(field)) {
|
|
450
|
-
throw new
|
|
458
|
+
throw new VibeError5({
|
|
451
459
|
code: "VIBE_VALIDATION",
|
|
452
460
|
message: `"${field}" on model "${model.name}" is a relation \u2014 not valid in ${context} here (relation writes use nested-write verbs, relation reads use select/include)`,
|
|
453
461
|
meta: { model: model.name, field, context }
|
|
454
462
|
});
|
|
455
463
|
}
|
|
456
|
-
throw new
|
|
464
|
+
throw new VibeError5({
|
|
457
465
|
code: "VIBE_VALIDATION",
|
|
458
466
|
message: `unknown field "${field}" on model "${model.name}" in ${context}`,
|
|
459
467
|
meta: { model: model.name, field, context }
|
|
@@ -463,7 +471,7 @@ function getFieldMeta(params) {
|
|
|
463
471
|
}
|
|
464
472
|
// packages/runtime/src/codecs.ts
|
|
465
473
|
import { JSON_TRANSPORT, projectionOutputName, parsePgArrayLiteral, toPgArrayLiteral } from "@vibeorm/sql";
|
|
466
|
-
import { VibeError as
|
|
474
|
+
import { VibeError as VibeError6, isEnumType as isEnumType3 } from "@vibeorm/schema";
|
|
467
475
|
import { DIALECT_CODECS, decodeIsNoop } from "@vibeorm/sql";
|
|
468
476
|
import {
|
|
469
477
|
DIALECT_CODECS as DIALECT_CODECS2,
|
|
@@ -512,10 +520,29 @@ function decodeValue(params) {
|
|
|
512
520
|
return codec.decode(value);
|
|
513
521
|
}
|
|
514
522
|
var DECIMAL_LITERAL = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/;
|
|
523
|
+
function validateAtomicOperand(params) {
|
|
524
|
+
const { field, value } = params;
|
|
525
|
+
if (field.scalarType === "BigInt") {
|
|
526
|
+
if (typeof value === "bigint" || typeof value === "number" && Number.isSafeInteger(value))
|
|
527
|
+
return value;
|
|
528
|
+
} else if (field.scalarType === "Decimal") {
|
|
529
|
+
if (typeof value === "string" && DECIMAL_LITERAL.test(value.trim()))
|
|
530
|
+
return value.trim();
|
|
531
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
532
|
+
return value;
|
|
533
|
+
} else if (typeof value === "number" && Number.isFinite(value)) {
|
|
534
|
+
return value;
|
|
535
|
+
}
|
|
536
|
+
throw new VibeError6({
|
|
537
|
+
code: "VIBE_VALIDATION",
|
|
538
|
+
message: `Invalid arithmetic operand for ${field.scalarType} field "${field.name}"`,
|
|
539
|
+
meta: { field: field.name, value }
|
|
540
|
+
});
|
|
541
|
+
}
|
|
515
542
|
function toRealNumber(params) {
|
|
516
543
|
const { field, value } = params;
|
|
517
544
|
const refuse = () => {
|
|
518
|
-
throw new
|
|
545
|
+
throw new VibeError6({
|
|
519
546
|
code: "VIBE_VALIDATION",
|
|
520
547
|
message: `"${String(value)}" is not a numeric value for the Decimal field "${field.name}"`,
|
|
521
548
|
meta: { field: field.name, value }
|
|
@@ -584,20 +611,20 @@ function wirePlanKey(wire) {
|
|
|
584
611
|
}
|
|
585
612
|
function fieldDecoder(params) {
|
|
586
613
|
const { dialect, field, wire } = params;
|
|
587
|
-
const
|
|
614
|
+
const decode = fieldCodec({ dialect, field }).decode;
|
|
588
615
|
if (isPostgresEnumList({ dialect, field })) {
|
|
589
616
|
return (value) => decodeValue({ dialect, field, value });
|
|
590
617
|
}
|
|
591
|
-
if (
|
|
618
|
+
if (decode === IDENTITY_DECODE)
|
|
592
619
|
return null;
|
|
593
620
|
if (field.isList) {
|
|
594
621
|
if (field.scalarType === "Json" && decodeIsNoop({ scalarType: "Json", wire }))
|
|
595
622
|
return null;
|
|
596
|
-
return (value) => Array.isArray(value) ? value.map((element) => element === null || element === undefined ? element :
|
|
623
|
+
return (value) => Array.isArray(value) ? value.map((element) => element === null || element === undefined ? element : decode(element)) : decode(value);
|
|
597
624
|
}
|
|
598
625
|
if (field.scalarType !== null && decodeIsNoop({ scalarType: field.scalarType, wire }))
|
|
599
626
|
return null;
|
|
600
|
-
return
|
|
627
|
+
return decode;
|
|
601
628
|
}
|
|
602
629
|
function buildDecodePlan(params) {
|
|
603
630
|
const { dialect, model, wire } = params;
|
|
@@ -605,12 +632,12 @@ function buildDecodePlan(params) {
|
|
|
605
632
|
const inPlace = [];
|
|
606
633
|
const renamedColumns = [];
|
|
607
634
|
for (const field of model.fields) {
|
|
608
|
-
const
|
|
609
|
-
byColumn.set(field.columnName, { name: field.name, decode
|
|
635
|
+
const decode = fieldDecoder({ dialect, field, ...wire === undefined ? {} : { wire } });
|
|
636
|
+
byColumn.set(field.columnName, { name: field.name, decode });
|
|
610
637
|
if (field.columnName !== field.name)
|
|
611
638
|
renamedColumns.push(field.columnName);
|
|
612
|
-
if (
|
|
613
|
-
inPlace.push({ column: field.columnName, decode
|
|
639
|
+
if (decode !== null)
|
|
640
|
+
inPlace.push({ column: field.columnName, decode });
|
|
614
641
|
}
|
|
615
642
|
return { byColumn, renamedColumns, decoders: inPlace, inPlace: renamedColumns.length > 0 ? null : inPlace };
|
|
616
643
|
}
|
|
@@ -737,12 +764,12 @@ function getJsonDecodePlan(model) {
|
|
|
737
764
|
const inPlace = [];
|
|
738
765
|
const renamedColumns = [];
|
|
739
766
|
for (const field of model.fields) {
|
|
740
|
-
const
|
|
741
|
-
byColumn.set(field.columnName, { name: field.name, decode
|
|
767
|
+
const decode = field.scalarType === "Json" ? null : field.scalarType === "Bytes" && !field.isList ? decodeBase64Bytes : fieldDecoder({ dialect: "postgres", field });
|
|
768
|
+
byColumn.set(field.columnName, { name: field.name, decode });
|
|
742
769
|
if (field.columnName !== field.name)
|
|
743
770
|
renamedColumns.push(field.columnName);
|
|
744
|
-
if (
|
|
745
|
-
inPlace.push({ column: field.columnName, decode
|
|
771
|
+
if (decode !== null)
|
|
772
|
+
inPlace.push({ column: field.columnName, decode });
|
|
746
773
|
}
|
|
747
774
|
plan = { byColumn, renamedColumns, decoders: inPlace, inPlace: renamedColumns.length > 0 ? null : inPlace };
|
|
748
775
|
jsonDecodePlanCache.set(model, plan);
|
|
@@ -755,7 +782,7 @@ function positionalMaterializer(params) {
|
|
|
755
782
|
const output = projectionOutputName({ column });
|
|
756
783
|
const entry = plan.byColumn.get(output);
|
|
757
784
|
if (entry === undefined)
|
|
758
|
-
throw new
|
|
785
|
+
throw new VibeError6({ code: "VIBE_INTERNAL", message: "positional projection has no schema field", meta: { model: model.name, column: output } });
|
|
759
786
|
return entry;
|
|
760
787
|
});
|
|
761
788
|
const width = slots.length;
|
|
@@ -763,7 +790,7 @@ function positionalMaterializer(params) {
|
|
|
763
790
|
const chunked = width > chunkSize;
|
|
764
791
|
const count = Math.ceil(width / chunkSize);
|
|
765
792
|
const invalid = () => {
|
|
766
|
-
throw new
|
|
793
|
+
throw new VibeError6({ code: "VIBE_INTERNAL", message: "positional relation payload does not match its SQL projection", meta: { model: model.name, expectedColumns: width } });
|
|
767
794
|
};
|
|
768
795
|
if (width === 0 || new Set(slots.map((slot) => slot.name)).size !== width)
|
|
769
796
|
return invalid();
|
|
@@ -805,7 +832,7 @@ function decodeAggregateValue(params) {
|
|
|
805
832
|
return field === null ? value : decodeValue({ dialect, field, value });
|
|
806
833
|
}
|
|
807
834
|
// packages/runtime/src/lateral-projection.ts
|
|
808
|
-
import { VibeError as
|
|
835
|
+
import { VibeError as VibeError7, unsupportedCapability as unsupportedCapability2 } from "@vibeorm/schema";
|
|
809
836
|
function isPlainObject(value) {
|
|
810
837
|
return value !== null && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
811
838
|
}
|
|
@@ -822,7 +849,7 @@ function lateralProjection(params) {
|
|
|
822
849
|
const columns = names.map((name) => {
|
|
823
850
|
const field = model.fieldByName.get(name);
|
|
824
851
|
if (field === undefined)
|
|
825
|
-
throw new
|
|
852
|
+
throw new VibeError7({ code: "VIBE_INTERNAL", message: "lateral projection references an unknown field", meta: { model: model.name, field: name } });
|
|
826
853
|
return Object.freeze(applyLateralTransform({ model, column: { table: LATERAL_CHILD_ALIAS, name: field.columnName } }));
|
|
827
854
|
});
|
|
828
855
|
const descriptor = Object.freeze({ columns: Object.freeze(columns), hiddenOrder: Object.freeze(hiddenOrder) });
|
|
@@ -871,7 +898,7 @@ function applyLateralTransform(params) {
|
|
|
871
898
|
|
|
872
899
|
// packages/runtime/src/query-builder.ts
|
|
873
900
|
import { isSqlFragment, mapConflictColumns } from "@vibeorm/sql";
|
|
874
|
-
import { VibeError as
|
|
901
|
+
import { VibeError as VibeError11, unsupportedCapability as unsupportedCapability4 } from "@vibeorm/schema";
|
|
875
902
|
import {
|
|
876
903
|
SQL_DEFAULT_CELL,
|
|
877
904
|
containsPattern,
|
|
@@ -885,9 +912,9 @@ import {
|
|
|
885
912
|
import { describeRowLock } from "@vibeorm/sql";
|
|
886
913
|
|
|
887
914
|
// packages/runtime/src/bulk-upsert.ts
|
|
888
|
-
import { VibeError as
|
|
915
|
+
import { VibeError as VibeError8, unsupportedCapability as unsupportedCapability3 } from "@vibeorm/schema";
|
|
889
916
|
function validationError2(params) {
|
|
890
|
-
return new
|
|
917
|
+
return new VibeError8({
|
|
891
918
|
code: "VIBE_VALIDATION",
|
|
892
919
|
message: params.message,
|
|
893
920
|
...params.meta === undefined ? {} : { meta: params.meta }
|
|
@@ -1069,8 +1096,13 @@ function conflictKeyOf(params) {
|
|
|
1069
1096
|
function assertNoDuplicateConflictKeys(params) {
|
|
1070
1097
|
const { model, compiled, targetColumns } = params;
|
|
1071
1098
|
const firstIndexByKey = new Map;
|
|
1099
|
+
const signature = [...targetColumns].sort((left, right) => left < right ? -1 : 1).join("\x00");
|
|
1100
|
+
const nullsNotDistinct = [...model.compoundUniques.values(), ...model.singleIndexUniques].some((group) => group.nullsNotDistinct && signatureOf({ fields: group.fields }) === signature);
|
|
1072
1101
|
for (let index = 0;index < compiled.length; index += 1) {
|
|
1073
|
-
const
|
|
1102
|
+
const row = compiled[index];
|
|
1103
|
+
if (!nullsNotDistinct && targetColumns.some((column) => row[column] === null))
|
|
1104
|
+
continue;
|
|
1105
|
+
const key = conflictKeyOf({ row, columns: targetColumns });
|
|
1074
1106
|
const first = firstIndexByKey.get(key);
|
|
1075
1107
|
if (first !== undefined) {
|
|
1076
1108
|
throw validationError2({
|
|
@@ -1206,7 +1238,7 @@ function assertConflictKeysAreComparable(params) {
|
|
|
1206
1238
|
}
|
|
1207
1239
|
|
|
1208
1240
|
// packages/runtime/src/keyset.ts
|
|
1209
|
-
import { VibeError as
|
|
1241
|
+
import { VibeError as VibeError9 } from "@vibeorm/schema";
|
|
1210
1242
|
var KEYSET_TOKEN_VERSION = 1;
|
|
1211
1243
|
var KEYSET_TOKEN_PREFIX = "vk1.";
|
|
1212
1244
|
var KEYSET_MAX_PAGE_SIZE = 1e4;
|
|
@@ -1216,10 +1248,10 @@ var DATETIME_NEEDS_PRECISION_ASSERTION = Object.freeze({
|
|
|
1216
1248
|
sqlite: false
|
|
1217
1249
|
});
|
|
1218
1250
|
function keysetValidation(params) {
|
|
1219
|
-
return new
|
|
1251
|
+
return new VibeError9({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
1220
1252
|
}
|
|
1221
1253
|
function keysetUnsupported(params) {
|
|
1222
|
-
return new
|
|
1254
|
+
return new VibeError9({
|
|
1223
1255
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
1224
1256
|
message: params.message,
|
|
1225
1257
|
meta: params.meta
|
|
@@ -1602,9 +1634,9 @@ function decodeKeysetToken(params) {
|
|
|
1602
1634
|
}
|
|
1603
1635
|
|
|
1604
1636
|
// packages/runtime/src/relation-plan.ts
|
|
1605
|
-
import { VibeError as
|
|
1637
|
+
import { VibeError as VibeError10, implicitJoinTable } from "@vibeorm/schema";
|
|
1606
1638
|
function validationError3(params) {
|
|
1607
|
-
return new
|
|
1639
|
+
return new VibeError10({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
1608
1640
|
}
|
|
1609
1641
|
function isPlainObject2(value) {
|
|
1610
1642
|
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date);
|
|
@@ -1629,7 +1661,7 @@ function keyFields(params) {
|
|
|
1629
1661
|
return names.map((name) => {
|
|
1630
1662
|
const field = model.fieldByName.get(name);
|
|
1631
1663
|
if (field === undefined) {
|
|
1632
|
-
throw new
|
|
1664
|
+
throw new VibeError10({
|
|
1633
1665
|
code: "VIBE_SCHEMA_INVALID",
|
|
1634
1666
|
message: `relation "${relation.name}" on ${relation.model} names unknown ${what} field "${name}"`,
|
|
1635
1667
|
meta: { model: relation.model, relation: relation.name }
|
|
@@ -1641,7 +1673,7 @@ function keyFields(params) {
|
|
|
1641
1673
|
function singleKeyPair(params) {
|
|
1642
1674
|
const { link, what } = params;
|
|
1643
1675
|
if (link.parentKeys.length !== 1 || link.childKeys.length !== 1) {
|
|
1644
|
-
throw new
|
|
1676
|
+
throw new VibeError10({
|
|
1645
1677
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
1646
1678
|
message: `${what} on relation "${link.relation.name}" of ${link.relation.model} needs a single-column relation key \u2014 this relation links ${link.parentKeys.length} columns; use an explicit junction model`,
|
|
1647
1679
|
meta: { model: link.relation.model, relation: link.relation.name, arity: link.parentKeys.length }
|
|
@@ -1685,7 +1717,7 @@ function resolveRelationLinkUncached(params) {
|
|
|
1685
1717
|
const target = getModelMeta({ meta, model: relation.targetModel });
|
|
1686
1718
|
if (relation.type === "manyToMany") {
|
|
1687
1719
|
if (relation.model === relation.targetModel) {
|
|
1688
|
-
throw new
|
|
1720
|
+
throw new VibeError10({
|
|
1689
1721
|
code: "VIBE_SCHEMA_INVALID",
|
|
1690
1722
|
message: `relation "${relation.name}" on ${relation.model} is an implicit self many-to-many: both sides own the ` + `same join-table column, so this field and its inverse would read the same rows. Model each direction ` + `with an explicit junction model.`,
|
|
1691
1723
|
meta: { model: relation.model, relation: relation.name, targetModel: relation.targetModel }
|
|
@@ -1727,7 +1759,7 @@ function resolveRelationLinkUncached(params) {
|
|
|
1727
1759
|
}
|
|
1728
1760
|
const reverse = findReverseRelation({ relation, target });
|
|
1729
1761
|
if (reverse === null || reverse.fields === undefined || reverse.fields.length === 0) {
|
|
1730
|
-
throw new
|
|
1762
|
+
throw new VibeError10({
|
|
1731
1763
|
code: "VIBE_SCHEMA_INVALID",
|
|
1732
1764
|
message: `relation "${relation.name}" on ${relation.model} has no foreign-key counterpart on ${relation.targetModel}`,
|
|
1733
1765
|
meta: { model: relation.model, relation: relation.name, target: relation.targetModel }
|
|
@@ -1752,7 +1784,7 @@ function pairedKeys(params) {
|
|
|
1752
1784
|
const parentKeys = keyFields({ model, names: parentNames, relation, what: "fields" });
|
|
1753
1785
|
const childKeys = keyFields({ model: target, names: childNames, relation, what: "references" });
|
|
1754
1786
|
if (parentKeys.length !== childKeys.length) {
|
|
1755
|
-
throw new
|
|
1787
|
+
throw new VibeError10({
|
|
1756
1788
|
code: "VIBE_SCHEMA_INVALID",
|
|
1757
1789
|
message: `relation "${relation.name}" on ${relation.model} links ${parentKeys.length} field(s) to ${childKeys.length} reference(s) \u2014 a foreign key must pair every column`,
|
|
1758
1790
|
meta: { model: relation.model, relation: relation.name }
|
|
@@ -2102,7 +2134,7 @@ var AGG_FN_BY_KEY = {
|
|
|
2102
2134
|
_max: "max"
|
|
2103
2135
|
};
|
|
2104
2136
|
function validationError4(params) {
|
|
2105
|
-
return new
|
|
2137
|
+
return new VibeError11({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
2106
2138
|
}
|
|
2107
2139
|
function isPlainObject3(value) {
|
|
2108
2140
|
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
|
|
@@ -2584,7 +2616,7 @@ function compileOperatorFilter(params) {
|
|
|
2584
2616
|
}
|
|
2585
2617
|
const insensitive = mode === "insensitive";
|
|
2586
2618
|
const strict = mode === "strict";
|
|
2587
|
-
const ciCollated = (
|
|
2619
|
+
const ciCollated = (params) => params.dialect.name === "mysql" && params.field.collation?.toLowerCase().endsWith("_ci") === true;
|
|
2588
2620
|
const requireStringOperand = (operator, operand) => {
|
|
2589
2621
|
if (typeof operand !== "string") {
|
|
2590
2622
|
throw validationError4({
|
|
@@ -2667,6 +2699,12 @@ function compileOperatorFilter(params) {
|
|
|
2667
2699
|
});
|
|
2668
2700
|
}
|
|
2669
2701
|
for (const item of operand) {
|
|
2702
|
+
if (item === null) {
|
|
2703
|
+
throw validationError4({
|
|
2704
|
+
message: `"${operator}" on ${model.name}.${field.name} cannot contain null; use an explicit null comparison`,
|
|
2705
|
+
meta: { model: model.name, field: field.name, operator }
|
|
2706
|
+
});
|
|
2707
|
+
}
|
|
2670
2708
|
if (isDbNow(item)) {
|
|
2671
2709
|
throw validationError4({
|
|
2672
2710
|
message: `dbNow() cannot ride an "${operator}" list on ${model.name}.${field.name} \u2014 compare with lt/lte/gt/gte/equals instead`,
|
|
@@ -2865,13 +2903,13 @@ function compileRelationFilter(params) {
|
|
|
2865
2903
|
meta: { model: model.name, relation: relation.name }
|
|
2866
2904
|
});
|
|
2867
2905
|
}
|
|
2868
|
-
const
|
|
2906
|
+
const conditions = [];
|
|
2869
2907
|
for (const [operator, operand] of Object.entries(value)) {
|
|
2870
2908
|
if (operand === undefined)
|
|
2871
2909
|
continue;
|
|
2872
2910
|
switch (operator) {
|
|
2873
2911
|
case "some":
|
|
2874
|
-
|
|
2912
|
+
conditions.push(buildRelationExists({
|
|
2875
2913
|
ctx,
|
|
2876
2914
|
link,
|
|
2877
2915
|
outer,
|
|
@@ -2879,7 +2917,7 @@ function compileRelationFilter(params) {
|
|
|
2879
2917
|
}));
|
|
2880
2918
|
break;
|
|
2881
2919
|
case "none":
|
|
2882
|
-
|
|
2920
|
+
conditions.push(buildRelationExists({
|
|
2883
2921
|
ctx,
|
|
2884
2922
|
link,
|
|
2885
2923
|
outer,
|
|
@@ -2888,7 +2926,7 @@ function compileRelationFilter(params) {
|
|
|
2888
2926
|
}));
|
|
2889
2927
|
break;
|
|
2890
2928
|
case "every":
|
|
2891
|
-
|
|
2929
|
+
conditions.push(buildRelationExists({
|
|
2892
2930
|
ctx,
|
|
2893
2931
|
link,
|
|
2894
2932
|
outer,
|
|
@@ -2904,15 +2942,15 @@ function compileRelationFilter(params) {
|
|
|
2904
2942
|
});
|
|
2905
2943
|
}
|
|
2906
2944
|
}
|
|
2907
|
-
if (
|
|
2945
|
+
if (conditions.length === 0) {
|
|
2908
2946
|
throw validationError4({
|
|
2909
2947
|
message: `filter on relation ${model.name}.${relation.name} must contain some, every or none`,
|
|
2910
2948
|
meta: { model: model.name, relation: relation.name }
|
|
2911
2949
|
});
|
|
2912
2950
|
}
|
|
2913
|
-
if (
|
|
2914
|
-
return
|
|
2915
|
-
return { kind: "and", operands:
|
|
2951
|
+
if (conditions.length === 1)
|
|
2952
|
+
return conditions[0];
|
|
2953
|
+
return { kind: "and", operands: conditions };
|
|
2916
2954
|
}
|
|
2917
2955
|
let isArm;
|
|
2918
2956
|
let hasIs = false;
|
|
@@ -3107,17 +3145,17 @@ function compileRelationOrderTerms(params) {
|
|
|
3107
3145
|
meta: { model: model.name, relation: relation.name }
|
|
3108
3146
|
});
|
|
3109
3147
|
}
|
|
3110
|
-
const
|
|
3111
|
-
const
|
|
3148
|
+
const direction = requireDirection(value, `orderBy.${relation.name}._count`);
|
|
3149
|
+
const alias = `__ord${++aliasSeq.n}`;
|
|
3112
3150
|
const select = link.kind === "manyToMany" ? {
|
|
3113
3151
|
kind: "select",
|
|
3114
|
-
table: { name: link.join.tableName, alias
|
|
3152
|
+
table: { name: link.join.tableName, alias },
|
|
3115
3153
|
columns: [],
|
|
3116
3154
|
selectExpr: { kind: "countAll" },
|
|
3117
3155
|
where: {
|
|
3118
3156
|
kind: "cmpCol",
|
|
3119
3157
|
op: "eq",
|
|
3120
|
-
left: { table:
|
|
3158
|
+
left: { table: alias, name: link.join.parentColumn },
|
|
3121
3159
|
right: {
|
|
3122
3160
|
table: outer,
|
|
3123
3161
|
name: singleKeyPair({ link, what: "implicit many-to-many ordering" }).parent.columnName
|
|
@@ -3125,12 +3163,12 @@ function compileRelationOrderTerms(params) {
|
|
|
3125
3163
|
}
|
|
3126
3164
|
} : {
|
|
3127
3165
|
kind: "select",
|
|
3128
|
-
table: { name: link.target.tableName, alias
|
|
3166
|
+
table: { name: link.target.tableName, alias },
|
|
3129
3167
|
columns: [],
|
|
3130
3168
|
selectExpr: { kind: "countAll" },
|
|
3131
|
-
where: correlateLink({ link, childTable:
|
|
3169
|
+
where: correlateLink({ link, childTable: alias, outerTable: outer })
|
|
3132
3170
|
};
|
|
3133
|
-
terms.push({ select, direction
|
|
3171
|
+
terms.push({ select, direction });
|
|
3134
3172
|
continue;
|
|
3135
3173
|
}
|
|
3136
3174
|
if (isList) {
|
|
@@ -3496,21 +3534,17 @@ function orderedColumns(params) {
|
|
|
3496
3534
|
return model.fields.map((field) => field.columnName).filter((column) => (column in values));
|
|
3497
3535
|
}
|
|
3498
3536
|
function compileAtomicOperation(params) {
|
|
3499
|
-
const { model,
|
|
3537
|
+
const { model, entry, operator, operand } = params;
|
|
3538
|
+
const field = entry.field;
|
|
3500
3539
|
if (field.scalarType === null || !NUMERIC_TYPES.includes(field.scalarType)) {
|
|
3501
3540
|
throw validationError4({
|
|
3502
3541
|
message: `"${operator}" needs a numeric field, but ${model.name}.${field.name} is ${String(field.enumName ?? field.scalarType)}`,
|
|
3503
3542
|
meta: { model: model.name, field: field.name, operator }
|
|
3504
3543
|
});
|
|
3505
3544
|
}
|
|
3506
|
-
const numeric =
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
message: `"${operator}" on ${model.name}.${field.name} expects a number`,
|
|
3510
|
-
meta: { model: model.name, field: field.name, operator, operand }
|
|
3511
|
-
});
|
|
3512
|
-
}
|
|
3513
|
-
return { __op: operator, value: numeric };
|
|
3545
|
+
const numeric = validateAtomicOperand({ field, value: operand });
|
|
3546
|
+
const encoded = encodeWith(entry, numeric);
|
|
3547
|
+
return { __op: operator, value: encoded, ...field.scalarType === "Decimal" ? { numericType: "Decimal" } : {} };
|
|
3514
3548
|
}
|
|
3515
3549
|
function compileUpdateSet(params) {
|
|
3516
3550
|
const { dialect, model, data } = params;
|
|
@@ -3542,7 +3576,7 @@ function compileUpdateSet(params) {
|
|
|
3542
3576
|
});
|
|
3543
3577
|
}
|
|
3544
3578
|
if (isPlainObject3(value) && field.scalarType !== "Json") {
|
|
3545
|
-
const entries = Object.entries(value).filter(([,
|
|
3579
|
+
const entries = Object.entries(value).filter(([, operand]) => operand !== undefined);
|
|
3546
3580
|
if (entries.length !== 1) {
|
|
3547
3581
|
throw validationError4({
|
|
3548
3582
|
message: `update of ${model.name}.${field.name} must use exactly one operation`,
|
|
@@ -3565,7 +3599,7 @@ function compileUpdateSet(params) {
|
|
|
3565
3599
|
meta: { model: model.name, field: field.name, operator }
|
|
3566
3600
|
});
|
|
3567
3601
|
}
|
|
3568
|
-
set[field.columnName] = compileAtomicOperation({ model,
|
|
3602
|
+
set[field.columnName] = compileAtomicOperation({ model, entry, operator: atomic, operand });
|
|
3569
3603
|
continue;
|
|
3570
3604
|
}
|
|
3571
3605
|
set[field.columnName] = encodeWith(entry, value);
|
|
@@ -4100,7 +4134,7 @@ function buildFindShape(params) {
|
|
|
4100
4134
|
},
|
|
4101
4135
|
...where === undefined ? {} : { where }
|
|
4102
4136
|
};
|
|
4103
|
-
const
|
|
4137
|
+
const statement = {
|
|
4104
4138
|
kind: "select",
|
|
4105
4139
|
table: { name: DISTINCT_TABLE_ALIAS },
|
|
4106
4140
|
columns: projectionColumns({ model, selectedFields }),
|
|
@@ -4110,7 +4144,7 @@ function buildFindShape(params) {
|
|
|
4110
4144
|
...limit === undefined ? {} : { limit },
|
|
4111
4145
|
...offset === undefined ? {} : { offset }
|
|
4112
4146
|
};
|
|
4113
|
-
return { statement
|
|
4147
|
+
return { statement, reverseRows: backward, resortBy };
|
|
4114
4148
|
}
|
|
4115
4149
|
if (distinctOuterOrder !== null) {
|
|
4116
4150
|
const inner = {
|
|
@@ -4126,7 +4160,7 @@ function buildFindShape(params) {
|
|
|
4126
4160
|
...projectionColumns({ model, selectedFields }),
|
|
4127
4161
|
...lateralJoins.map((join) => ({ name: join.columnAlias }))
|
|
4128
4162
|
];
|
|
4129
|
-
const
|
|
4163
|
+
const statement = {
|
|
4130
4164
|
kind: "select",
|
|
4131
4165
|
table: { name: DISTINCT_TABLE_ALIAS },
|
|
4132
4166
|
columns: outerColumns,
|
|
@@ -4135,7 +4169,7 @@ function buildFindShape(params) {
|
|
|
4135
4169
|
...limit === undefined ? {} : { limit },
|
|
4136
4170
|
...offset === undefined ? {} : { offset }
|
|
4137
4171
|
};
|
|
4138
|
-
return { statement
|
|
4172
|
+
return { statement, reverseRows: backward, resortBy };
|
|
4139
4173
|
}
|
|
4140
4174
|
const pageFirst = lateralJoins.length > 0 && (limit !== undefined || offset !== undefined) && orderTerms.every((term) => ("column" in term));
|
|
4141
4175
|
if (pageFirst && selection !== undefined) {
|
|
@@ -4151,7 +4185,7 @@ function buildFindShape(params) {
|
|
|
4151
4185
|
...limit === undefined ? {} : { limit },
|
|
4152
4186
|
...offset === undefined ? {} : { offset }
|
|
4153
4187
|
};
|
|
4154
|
-
const
|
|
4188
|
+
const statement = {
|
|
4155
4189
|
kind: "select",
|
|
4156
4190
|
table: { name: model.tableName },
|
|
4157
4191
|
columns,
|
|
@@ -4159,7 +4193,7 @@ function buildFindShape(params) {
|
|
|
4159
4193
|
lateralJoins,
|
|
4160
4194
|
...orderTerms.length === 0 ? {} : { orderBy: orderTerms }
|
|
4161
4195
|
};
|
|
4162
|
-
return { statement
|
|
4196
|
+
return { statement, reverseRows: backward, resortBy };
|
|
4163
4197
|
}
|
|
4164
4198
|
const statement = {
|
|
4165
4199
|
kind: "select",
|
|
@@ -4584,9 +4618,9 @@ function compileGroupByOrder(params) {
|
|
|
4584
4618
|
}
|
|
4585
4619
|
agg = { fn: "countAll" };
|
|
4586
4620
|
} else {
|
|
4587
|
-
const
|
|
4588
|
-
validateAggregatedField({ model, field
|
|
4589
|
-
const aggColumn = key === "_count" ? { name:
|
|
4621
|
+
const field = getFieldMeta({ model, field: selector, context: `orderBy ${name}` });
|
|
4622
|
+
validateAggregatedField({ model, field, key });
|
|
4623
|
+
const aggColumn = key === "_count" ? { name: field.columnName } : compareColumnRef({ dialect, field });
|
|
4590
4624
|
agg = { fn: AGG_FN_BY_KEY[key], column: aggColumn };
|
|
4591
4625
|
}
|
|
4592
4626
|
terms.push({ agg, direction });
|
|
@@ -5036,7 +5070,7 @@ function buildQuery(params) {
|
|
|
5036
5070
|
throw validationError4({ message: "A conflict update requires an explicit target" });
|
|
5037
5071
|
const updates = requireObject({ value: spec.update, what: "onConflict.update", model });
|
|
5038
5072
|
const fields = resolveUpsertUpdateFields({ model, update: Object.keys(updates), targetColumns: new Set(compiled.columns ?? []), skipUpdatedAt: true });
|
|
5039
|
-
const
|
|
5073
|
+
const columns = Object.fromEntries(model.fields.map((field) => [field.name, field.columnName]));
|
|
5040
5074
|
conflictClause = {
|
|
5041
5075
|
columns: compiled.columns ?? [],
|
|
5042
5076
|
...compiled.constraint === undefined ? {} : { constraint: compiled.constraint },
|
|
@@ -5045,7 +5079,7 @@ function buildQuery(params) {
|
|
|
5045
5079
|
const fragment = updates[field.name];
|
|
5046
5080
|
if (!isSqlFragment(fragment))
|
|
5047
5081
|
throw validationError4({ message: "Conflict update expressions must be constructed SQL fragments" });
|
|
5048
|
-
return [field.columnName, mapConflictColumns({ fragment, columns
|
|
5082
|
+
return [field.columnName, mapConflictColumns({ fragment, columns })];
|
|
5049
5083
|
}))
|
|
5050
5084
|
};
|
|
5051
5085
|
} else
|
|
@@ -5283,7 +5317,7 @@ function buildQuery(params) {
|
|
|
5283
5317
|
const targetFields = conflictTargetGroup({ model, where }).fields;
|
|
5284
5318
|
for (const field of targetFields) {
|
|
5285
5319
|
if (!(field.columnName in row.values)) {
|
|
5286
|
-
throw new
|
|
5320
|
+
throw new VibeError11({
|
|
5287
5321
|
code: "VIBE_INTERNAL",
|
|
5288
5322
|
message: `upsert on ${model.name}: create data does not bind conflict-target column "${field.columnName}" \u2014 this shape must run the find-then-write fallback flow`,
|
|
5289
5323
|
meta: { model: model.name, column: field.columnName }
|
|
@@ -5294,7 +5328,7 @@ function buildQuery(params) {
|
|
|
5294
5328
|
for (const [key, value] of Object.entries(where)) {
|
|
5295
5329
|
if (value === undefined || nativeTargetNames.has(key))
|
|
5296
5330
|
continue;
|
|
5297
|
-
throw new
|
|
5331
|
+
throw new VibeError11({
|
|
5298
5332
|
code: "VIBE_INTERNAL",
|
|
5299
5333
|
message: `upsert on ${model.name}: where field "${key}" is not part of the conflict target \u2014 guarded upserts must run the find-then-write fallback flow`,
|
|
5300
5334
|
meta: { model: model.name, field: key }
|
|
@@ -5346,8 +5380,95 @@ function buildQuery(params) {
|
|
|
5346
5380
|
// packages/runtime/src/diagnostics/preview.ts
|
|
5347
5381
|
import { getSqlDialect, renderStatement } from "@vibeorm/sql";
|
|
5348
5382
|
|
|
5383
|
+
// packages/runtime/src/extensions.ts
|
|
5384
|
+
import { VibeError as VibeError12, extensionManifestEntry } from "@vibeorm/schema";
|
|
5385
|
+
function assertExtensionManifest(params) {
|
|
5386
|
+
const { manifest } = params;
|
|
5387
|
+
const instances = params.instances ?? [];
|
|
5388
|
+
const live = new Map(instances.map((extension) => [extension.name, extensionManifestEntry({ extension })]));
|
|
5389
|
+
const problems = [];
|
|
5390
|
+
for (const baked of manifest) {
|
|
5391
|
+
const current = live.get(baked.name);
|
|
5392
|
+
if (current === undefined) {
|
|
5393
|
+
problems.push(`"${baked.name}" was baked into the client but not passed to VibeClient`);
|
|
5394
|
+
continue;
|
|
5395
|
+
}
|
|
5396
|
+
if (current.version !== baked.version) {
|
|
5397
|
+
problems.push(`"${baked.name}" version changed (generated ${baked.version}, live ${current.version})`);
|
|
5398
|
+
} else if (current.optionsHash !== baked.optionsHash) {
|
|
5399
|
+
problems.push(`"${baked.name}" options changed since generate`);
|
|
5400
|
+
}
|
|
5401
|
+
live.delete(baked.name);
|
|
5402
|
+
}
|
|
5403
|
+
for (const name of live.keys()) {
|
|
5404
|
+
problems.push(`"${name}" is configured but missing from the generated client`);
|
|
5405
|
+
}
|
|
5406
|
+
if (problems.length > 0) {
|
|
5407
|
+
throw new VibeError12({
|
|
5408
|
+
code: "VIBE_STALE_CLIENT",
|
|
5409
|
+
message: `extension list changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
|
|
5410
|
+
meta: { manifest, problems }
|
|
5411
|
+
});
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
function buildExtensionOperatorMap(params) {
|
|
5415
|
+
const { instances, dialect, provider } = params;
|
|
5416
|
+
const map = new Map;
|
|
5417
|
+
const owner = new Map;
|
|
5418
|
+
for (const extension of instances) {
|
|
5419
|
+
for (const operator of extension.sql?.whereOperators ?? []) {
|
|
5420
|
+
const existing = owner.get(operator.name);
|
|
5421
|
+
if (existing !== undefined) {
|
|
5422
|
+
throw new VibeError12({
|
|
5423
|
+
code: "VIBE_CONFIG",
|
|
5424
|
+
message: `where operator "${operator.name}" is claimed by both "${existing}" and "${extension.name}"`,
|
|
5425
|
+
meta: { operator: operator.name }
|
|
5426
|
+
});
|
|
5427
|
+
}
|
|
5428
|
+
owner.set(operator.name, extension.name);
|
|
5429
|
+
map.set(operator.name, ({ column, operand, model, field }) => {
|
|
5430
|
+
const compiled = operator.compile({
|
|
5431
|
+
column,
|
|
5432
|
+
operand,
|
|
5433
|
+
dialect,
|
|
5434
|
+
provider,
|
|
5435
|
+
options: extension.options
|
|
5436
|
+
});
|
|
5437
|
+
if ("error" in compiled) {
|
|
5438
|
+
throw new VibeError12({
|
|
5439
|
+
code: "VIBE_VALIDATION",
|
|
5440
|
+
message: `"${operator.name}" on ${model}.${field}: ${compiled.error}`,
|
|
5441
|
+
meta: { model, field, operator: operator.name, extension: extension.name }
|
|
5442
|
+
});
|
|
5443
|
+
}
|
|
5444
|
+
return { kind: "fragment", fragment: compiled.fragment };
|
|
5445
|
+
});
|
|
5446
|
+
}
|
|
5447
|
+
}
|
|
5448
|
+
return map.size === 0 ? null : map;
|
|
5449
|
+
}
|
|
5450
|
+
function buildConnectHooks(params) {
|
|
5451
|
+
const { instances, dialect, provider } = params;
|
|
5452
|
+
const hooks = [];
|
|
5453
|
+
for (const extension of instances) {
|
|
5454
|
+
const onConnect = extension.runtime?.onConnect;
|
|
5455
|
+
if (onConnect === undefined)
|
|
5456
|
+
continue;
|
|
5457
|
+
hooks.push(({ execute }) => onConnect({
|
|
5458
|
+
execute: (execParams) => execute({
|
|
5459
|
+
text: execParams.text,
|
|
5460
|
+
...execParams.values === undefined ? {} : { values: execParams.values }
|
|
5461
|
+
}),
|
|
5462
|
+
dialect,
|
|
5463
|
+
provider,
|
|
5464
|
+
options: extension.options
|
|
5465
|
+
}));
|
|
5466
|
+
}
|
|
5467
|
+
return hooks;
|
|
5468
|
+
}
|
|
5469
|
+
|
|
5349
5470
|
// packages/runtime/src/relation-key.ts
|
|
5350
|
-
import { VibeError as
|
|
5471
|
+
import { VibeError as VibeError13 } from "@vibeorm/schema";
|
|
5351
5472
|
function relationValueKey(params) {
|
|
5352
5473
|
const { value } = params;
|
|
5353
5474
|
if (value === null || value === undefined)
|
|
@@ -5380,7 +5501,7 @@ function relationTupleKey(params) {
|
|
|
5380
5501
|
if (value === null || value === undefined)
|
|
5381
5502
|
return null;
|
|
5382
5503
|
if (typeof value === "object" && !(value instanceof Date) && !(value instanceof Uint8Array)) {
|
|
5383
|
-
throw new
|
|
5504
|
+
throw new VibeError13({
|
|
5384
5505
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
5385
5506
|
message: "a relation key of this type cannot be compared faithfully in memory \u2014 relation links support String, Int, BigInt, Boolean, DateTime and Bytes keys",
|
|
5386
5507
|
meta: { received: Object.prototype.toString.call(value) }
|
|
@@ -5406,7 +5527,7 @@ function assertWriteScope(params) {
|
|
|
5406
5527
|
}
|
|
5407
5528
|
|
|
5408
5529
|
// packages/runtime/src/nested-writes.ts
|
|
5409
|
-
import { VibeError as
|
|
5530
|
+
import { VibeError as VibeError14 } from "@vibeorm/schema";
|
|
5410
5531
|
function parentTupleOf(params) {
|
|
5411
5532
|
const { link, row } = params;
|
|
5412
5533
|
return link.parentKeys.map((field) => row[field.name]);
|
|
@@ -5453,10 +5574,10 @@ function detachAssignment(params) {
|
|
|
5453
5574
|
return out;
|
|
5454
5575
|
}
|
|
5455
5576
|
function validationError5(params) {
|
|
5456
|
-
return new
|
|
5577
|
+
return new VibeError14({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
5457
5578
|
}
|
|
5458
5579
|
function notFound(params) {
|
|
5459
|
-
return new
|
|
5580
|
+
return new VibeError14({ code: "VIBE_NOT_FOUND", message: params.message, meta: params.meta });
|
|
5460
5581
|
}
|
|
5461
5582
|
function isPlainObject4(value) {
|
|
5462
5583
|
return typeof value === "object" && value !== null && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof Uint8Array);
|
|
@@ -5601,7 +5722,7 @@ function chunkKeyList(params) {
|
|
|
5601
5722
|
const { dialect, keys, perKey, reserved } = params;
|
|
5602
5723
|
if (keys.length === 0)
|
|
5603
5724
|
return [];
|
|
5604
|
-
if (dialect.inArrayStrategy !== "expand")
|
|
5725
|
+
if (params.expanded !== true && dialect.inArrayStrategy !== "expand")
|
|
5605
5726
|
return [keys];
|
|
5606
5727
|
const size = Math.max(1, Math.floor((dialect.paramBudget - reserved) / perKey));
|
|
5607
5728
|
const chunks = [];
|
|
@@ -5638,27 +5759,30 @@ async function deleteJoinRows(params) {
|
|
|
5638
5759
|
const join = link.join;
|
|
5639
5760
|
const pair = singleKeyPair({ link, what: "implicit many-to-many writes" });
|
|
5640
5761
|
const parentValue = encodeValue({ dialect: dialect.name, field: pair.parent, value: parentKey });
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5646
|
-
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5650
|
-
{
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5762
|
+
const chunks = childKeys === undefined ? [undefined] : chunkKeyList({ dialect, keys: childKeys, perKey: 1, reserved: 1 });
|
|
5763
|
+
for (const chunk of chunks) {
|
|
5764
|
+
await run.statement({
|
|
5765
|
+
statement: {
|
|
5766
|
+
kind: "delete",
|
|
5767
|
+
table: { name: join.tableName },
|
|
5768
|
+
where: {
|
|
5769
|
+
kind: "and",
|
|
5770
|
+
operands: [
|
|
5771
|
+
{ kind: "cmp", op: "eq", column: { name: join.parentColumn }, value: parentValue },
|
|
5772
|
+
...chunk === undefined ? [] : [
|
|
5773
|
+
{
|
|
5774
|
+
kind: "inArray",
|
|
5775
|
+
column: { name: join.childColumn },
|
|
5776
|
+
values: chunk.map((key) => encodeValue({ dialect: dialect.name, field: pair.child, value: key }))
|
|
5777
|
+
}
|
|
5778
|
+
]
|
|
5655
5779
|
]
|
|
5656
|
-
|
|
5657
|
-
}
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
}
|
|
5780
|
+
}
|
|
5781
|
+
},
|
|
5782
|
+
model: link.relation.model,
|
|
5783
|
+
method: "nested.disconnect"
|
|
5784
|
+
});
|
|
5785
|
+
}
|
|
5662
5786
|
}
|
|
5663
5787
|
async function assertM2MMembers(params) {
|
|
5664
5788
|
const { run, ctx, link, parentKey, childKeys, target, verb, relation } = params;
|
|
@@ -5666,10 +5790,10 @@ async function assertM2MMembers(params) {
|
|
|
5666
5790
|
return;
|
|
5667
5791
|
const members = await loadMemberKeys({ run, ctx, link, parentKey, childKeys });
|
|
5668
5792
|
const pair = singleKeyPair({ link, what: "implicit many-to-many writes" });
|
|
5669
|
-
const connected = new Set(members.map((member) =>
|
|
5793
|
+
const connected = new Set(members.map((member) => relationValueKey({ value: decodeValue({ dialect: ctx.dialect.name, field: pair.child, value: member }) })));
|
|
5670
5794
|
for (const childKey of childKeys) {
|
|
5671
|
-
const childValue =
|
|
5672
|
-
if (connected.has(
|
|
5795
|
+
const childValue = decodeValue({ dialect: ctx.dialect.name, field: pair.child, value: childKey });
|
|
5796
|
+
if (connected.has(relationValueKey({ value: childValue })))
|
|
5673
5797
|
continue;
|
|
5674
5798
|
throw notFound({
|
|
5675
5799
|
message: `nested "${verb}" on ${link.relation.model}.${relation}: ${target} is not connected`,
|
|
@@ -5760,7 +5884,7 @@ function isFilterableUniqueWhere(params) {
|
|
|
5760
5884
|
return isUniqueWhere({ model: target, where });
|
|
5761
5885
|
}
|
|
5762
5886
|
async function connectChildrenByFk(params) {
|
|
5763
|
-
const { run, op, items, fk, verb } = params;
|
|
5887
|
+
const { dialect, run, op, items, fk, verb } = params;
|
|
5764
5888
|
const target = op.link.target;
|
|
5765
5889
|
if (items.length === 0)
|
|
5766
5890
|
return;
|
|
@@ -5771,11 +5895,18 @@ async function connectChildrenByFk(params) {
|
|
|
5771
5895
|
}
|
|
5772
5896
|
return;
|
|
5773
5897
|
}
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5898
|
+
let perKey = 1;
|
|
5899
|
+
for (const item of items)
|
|
5900
|
+
perKey = Math.max(perKey, Object.values(item).filter((value) => value !== undefined && value !== null).length);
|
|
5901
|
+
const reserved = Object.values(fk).filter((value) => value !== undefined).length + target.fields.filter((field) => field.isUpdatedAt && fk[field.name] === undefined).length;
|
|
5902
|
+
let affected = 0;
|
|
5903
|
+
for (const chunk of chunkKeyList({ dialect, keys: items, perKey, reserved, expanded: true })) {
|
|
5904
|
+
affected += await run.affected({
|
|
5905
|
+
model: target.name,
|
|
5906
|
+
method: "updateMany",
|
|
5907
|
+
args: { where: { OR: chunk.map((item) => ({ ...item })) }, data: fk }
|
|
5908
|
+
});
|
|
5909
|
+
}
|
|
5779
5910
|
if (affected >= items.length)
|
|
5780
5911
|
return;
|
|
5781
5912
|
for (const item of items) {
|
|
@@ -5845,7 +5976,7 @@ async function applyChildCreateOps(params) {
|
|
|
5845
5976
|
continue;
|
|
5846
5977
|
}
|
|
5847
5978
|
if (verb === "connect") {
|
|
5848
|
-
await connectChildrenByFk({ run, op, items, fk, verb });
|
|
5979
|
+
await connectChildrenByFk({ dialect: ctx.dialect, run, op, items, fk, verb });
|
|
5849
5980
|
continue;
|
|
5850
5981
|
}
|
|
5851
5982
|
for (const item of items) {
|
|
@@ -6082,7 +6213,8 @@ async function loadMemberKeys(params) {
|
|
|
6082
6213
|
const values = childKeys.map((childKey) => encodeValue({ dialect: ctx.dialect.name, field: pair.child, value: childKey }));
|
|
6083
6214
|
const found = [];
|
|
6084
6215
|
for (const chunk of chunkKeyList({ dialect: ctx.dialect, keys: values, perKey: 1, reserved: 1 })) {
|
|
6085
|
-
|
|
6216
|
+
for (const member of await scan(chunk))
|
|
6217
|
+
found.push(member);
|
|
6086
6218
|
}
|
|
6087
6219
|
return found;
|
|
6088
6220
|
}
|
|
@@ -6128,7 +6260,7 @@ async function applyChildUpdateOps(params) {
|
|
|
6128
6260
|
method: "updateMany",
|
|
6129
6261
|
args: { where: fk, data: detach }
|
|
6130
6262
|
});
|
|
6131
|
-
await connectChildrenByFk({ run, op, items, fk, verb });
|
|
6263
|
+
await connectChildrenByFk({ dialect: ctx.dialect, run, op, items, fk, verb });
|
|
6132
6264
|
}
|
|
6133
6265
|
break;
|
|
6134
6266
|
}
|
|
@@ -6578,7 +6710,9 @@ function previewOperation(params) {
|
|
|
6578
6710
|
const { schema, model, method, args, relationStrategy, clientOptions } = params;
|
|
6579
6711
|
const includeValues = params.includeParameterValues === true;
|
|
6580
6712
|
const dialect = getSqlDialect({ dialect: params.dialect });
|
|
6581
|
-
const
|
|
6713
|
+
const baseMeta = buildRuntimeMeta({ schema });
|
|
6714
|
+
const operatorMap = buildExtensionOperatorMap({ instances: params.extensions ?? [], dialect: params.dialect, provider: schema.datasource.provider });
|
|
6715
|
+
const meta = operatorMap === null ? baseMeta : { ...baseMeta, extensionOperators: operatorMap };
|
|
6582
6716
|
const modelMeta = getModelMeta({ meta, model });
|
|
6583
6717
|
const writesNested = (method === "create" || method === "update" || method === "upsert") && (hasNestedWrites({ model: modelMeta, data: args?.data }) || hasNestedWrites({ model: modelMeta, data: args?.create }) || hasNestedWrites({ model: modelMeta, data: args?.update }));
|
|
6584
6718
|
const effectiveArgs = args === undefined || !writesNested ? args : {
|
|
@@ -6660,7 +6794,7 @@ function previewOperation(params) {
|
|
|
6660
6794
|
};
|
|
6661
6795
|
}
|
|
6662
6796
|
// packages/runtime/src/diagnostics/plan.ts
|
|
6663
|
-
import { VibeError as
|
|
6797
|
+
import { VibeError as VibeError15, unsupportedCapability as unsupportedCapability5 } from "@vibeorm/schema";
|
|
6664
6798
|
import { NATIVE_RLS, getSqlDialect as getSqlDialect2, renderPostgresExplain, renderPostgresWorkload, renderStatement as renderStatement2 } from "@vibeorm/sql";
|
|
6665
6799
|
var PLAN_DEFAULTS = Object.freeze({ maxNodes: 200, maxConditionLength: 2000 });
|
|
6666
6800
|
var CONDITION_KEYS = [
|
|
@@ -6679,7 +6813,7 @@ var CONDITION_KEYS = [
|
|
|
6679
6813
|
"Output"
|
|
6680
6814
|
];
|
|
6681
6815
|
function planError(params) {
|
|
6682
|
-
return new
|
|
6816
|
+
return new VibeError15({ code: "VIBE_ADAPTER", message: params.message, meta: {} });
|
|
6683
6817
|
}
|
|
6684
6818
|
function asRecord(value) {
|
|
6685
6819
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
@@ -6712,14 +6846,14 @@ function parsePostgresPlan(params) {
|
|
|
6712
6846
|
const maxNodes = params.maxNodes ?? PLAN_DEFAULTS.maxNodes;
|
|
6713
6847
|
const includeLiterals = params.includeLiterals === true;
|
|
6714
6848
|
if (!Number.isInteger(maxNodes) || maxNodes < 1) {
|
|
6715
|
-
throw new
|
|
6849
|
+
throw new VibeError15({ code: "VIBE_CONFIG", message: `maxNodes must be a positive integer, got ${String(maxNodes)}`, meta: { maxNodes } });
|
|
6716
6850
|
}
|
|
6717
6851
|
let parsed = payload;
|
|
6718
6852
|
if (typeof payload === "string") {
|
|
6719
6853
|
try {
|
|
6720
6854
|
parsed = JSON.parse(payload);
|
|
6721
6855
|
} catch {
|
|
6722
|
-
throw new
|
|
6856
|
+
throw new VibeError15({
|
|
6723
6857
|
code: "VIBE_ADAPTER",
|
|
6724
6858
|
message: "EXPLAIN returned malformed JSON",
|
|
6725
6859
|
meta: { reason: "malformed-plan-json", mode }
|
|
@@ -6787,7 +6921,7 @@ function parsePostgresPlan(params) {
|
|
|
6787
6921
|
var WRITE_LEADERS = ["INSERT", "UPDATE", "DELETE", "MERGE", "TRUNCATE", "CREATE", "DROP", "ALTER", "GRANT", "REVOKE", "COPY", "CALL", "DO"];
|
|
6788
6922
|
var SIDE_EFFECT_CALLS = /\bpg_(?:try_)?advisory_(?:xact_)?lock(?:_shared)?\s*\(/i;
|
|
6789
6923
|
function refuse(params) {
|
|
6790
|
-
return new
|
|
6924
|
+
return new VibeError15({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta ?? {} });
|
|
6791
6925
|
}
|
|
6792
6926
|
function executableReadRefusal(params) {
|
|
6793
6927
|
const normalized = normalizeSql({ text: params.text });
|
|
@@ -6847,7 +6981,7 @@ function requirePostgres(params) {
|
|
|
6847
6981
|
return getSqlDialect2({ dialect: params.dialect });
|
|
6848
6982
|
}
|
|
6849
6983
|
async function explainSql(params) {
|
|
6850
|
-
const { executor, sql
|
|
6984
|
+
const { executor, sql, rlsContext } = params;
|
|
6851
6985
|
const mode = params.mode ?? "estimate";
|
|
6852
6986
|
const dialect = requirePostgres({ dialect: params.dialect });
|
|
6853
6987
|
const warnings = [];
|
|
@@ -6864,7 +6998,7 @@ async function explainSql(params) {
|
|
|
6864
6998
|
meta: { mode }
|
|
6865
6999
|
});
|
|
6866
7000
|
}
|
|
6867
|
-
assertExecutableRead({ text:
|
|
7001
|
+
assertExecutableRead({ text: sql.text });
|
|
6868
7002
|
warnings.push("this statement was EXECUTED to produce observed rows and milliseconds; a read-only transaction and a keyword check are not a proof of safety, because a SELECT can call a function with side effects");
|
|
6869
7003
|
}
|
|
6870
7004
|
const contextKeys = rlsContext === undefined ? [] : Object.keys(rlsContext);
|
|
@@ -6880,7 +7014,7 @@ async function explainSql(params) {
|
|
|
6880
7014
|
const report = await explainOnSession({
|
|
6881
7015
|
executor,
|
|
6882
7016
|
dialect: params.dialect,
|
|
6883
|
-
sql
|
|
7017
|
+
sql,
|
|
6884
7018
|
mode,
|
|
6885
7019
|
warnings,
|
|
6886
7020
|
...rlsContext === undefined ? {} : { rlsContext },
|
|
@@ -6899,7 +7033,7 @@ async function explainSql(params) {
|
|
|
6899
7033
|
}
|
|
6900
7034
|
}
|
|
6901
7035
|
async function explainOnSession(params) {
|
|
6902
|
-
const { executor, sql
|
|
7036
|
+
const { executor, sql, mode, rlsContext } = params;
|
|
6903
7037
|
const dialect = requirePostgres({ dialect: params.dialect });
|
|
6904
7038
|
const warnings = params.warnings ?? [];
|
|
6905
7039
|
const contextKeys = rlsContext === undefined ? [] : Object.keys(rlsContext);
|
|
@@ -6917,7 +7051,7 @@ async function explainOnSession(params) {
|
|
|
6917
7051
|
const context = await readPlanContext({ executor, dialect, rlsContextKeys: contextKeys });
|
|
6918
7052
|
if (!context.representative) {
|
|
6919
7053
|
if (params.requireRepresentativeContext === true) {
|
|
6920
|
-
throw new
|
|
7054
|
+
throw new VibeError15({
|
|
6921
7055
|
code: "VIBE_RLS_CONTEXT",
|
|
6922
7056
|
message: `refusing to report a plan taken under a non-representative identity: ${context.reason ?? "role bypasses row policies"}`,
|
|
6923
7057
|
meta: { currentUser: context.currentUser, bypassRls: context.bypassRls, superuser: context.superuser }
|
|
@@ -6927,7 +7061,7 @@ async function explainOnSession(params) {
|
|
|
6927
7061
|
}
|
|
6928
7062
|
const explain = renderPostgresExplain({
|
|
6929
7063
|
dialect,
|
|
6930
|
-
query: { text:
|
|
7064
|
+
query: { text: sql.text, values: [...sql.values], returning: "none" },
|
|
6931
7065
|
options: mode === "analyze" ? { analyze: true, buffers: true } : {}
|
|
6932
7066
|
});
|
|
6933
7067
|
const rows = await run(explain);
|
|
@@ -6960,7 +7094,9 @@ async function explainOperation(params) {
|
|
|
6960
7094
|
const { schema, model, method, args } = params;
|
|
6961
7095
|
const dialect = requirePostgres({ dialect: params.dialect });
|
|
6962
7096
|
const mode = params.mode ?? "estimate";
|
|
6963
|
-
const
|
|
7097
|
+
const baseMeta = buildRuntimeMeta({ schema });
|
|
7098
|
+
const operatorMap = buildExtensionOperatorMap({ instances: params.extensions ?? [], dialect: params.dialect, provider: schema.datasource.provider });
|
|
7099
|
+
const meta = operatorMap === null ? baseMeta : { ...baseMeta, extensionOperators: operatorMap };
|
|
6964
7100
|
const plan = buildQuery({
|
|
6965
7101
|
meta,
|
|
6966
7102
|
dialect,
|
|
@@ -6991,18 +7127,19 @@ async function explainOperation(params) {
|
|
|
6991
7127
|
});
|
|
6992
7128
|
}
|
|
6993
7129
|
// packages/runtime/src/diagnostics/statement-diagnostics.ts
|
|
6994
|
-
import { VibeError as
|
|
7130
|
+
import { VibeError as VibeError16 } from "@vibeorm/schema";
|
|
6995
7131
|
import { renderPostgresWorkload as renderPostgresWorkload2 } from "@vibeorm/sql";
|
|
6996
7132
|
var STATEMENT_DIAGNOSTICS_DEFAULTS = Object.freeze({
|
|
6997
7133
|
slowerThanMs: 0,
|
|
6998
7134
|
explain: "off",
|
|
6999
7135
|
explainTimeoutMs: 5000,
|
|
7136
|
+
maxOutstandingExplains: 4,
|
|
7000
7137
|
analyzeWarnBelowMs: 1,
|
|
7001
7138
|
envVariable: "VIBEORM_DIAGNOSTICS"
|
|
7002
7139
|
});
|
|
7003
7140
|
var EXPLAIN_MODES = ["off", "estimate", "analyze"];
|
|
7004
7141
|
function configError(params) {
|
|
7005
|
-
return new
|
|
7142
|
+
return new VibeError16({ code: "VIBE_CONFIG", message: params.message, meta: params.meta ?? {} });
|
|
7006
7143
|
}
|
|
7007
7144
|
function isExplainMode(value) {
|
|
7008
7145
|
return typeof value === "string" && EXPLAIN_MODES.includes(value);
|
|
@@ -7128,7 +7265,7 @@ function summarizePlan(params) {
|
|
|
7128
7265
|
};
|
|
7129
7266
|
}
|
|
7130
7267
|
function errorCode(error) {
|
|
7131
|
-
return error instanceof
|
|
7268
|
+
return error instanceof VibeError16 ? error.code : "VIBE_INTERNAL";
|
|
7132
7269
|
}
|
|
7133
7270
|
function createStatementDiagnostics(params) {
|
|
7134
7271
|
const { config, adapter, dialect } = params;
|
|
@@ -7151,7 +7288,7 @@ function createStatementDiagnostics(params) {
|
|
|
7151
7288
|
text: query.text,
|
|
7152
7289
|
values: (query.values ?? []).map((value) => Array.isArray(value) ? tx.formatArrayParam(value) : value)
|
|
7153
7290
|
});
|
|
7154
|
-
const
|
|
7291
|
+
const report = await explainOnSession({
|
|
7155
7292
|
executor,
|
|
7156
7293
|
dialect: dialect.name,
|
|
7157
7294
|
sql: { text: statement.text, values: statement.values },
|
|
@@ -7160,11 +7297,11 @@ function createStatementDiagnostics(params) {
|
|
|
7160
7297
|
});
|
|
7161
7298
|
return summarizePlan({
|
|
7162
7299
|
plan: {
|
|
7163
|
-
root:
|
|
7164
|
-
nodeCount:
|
|
7165
|
-
truncated:
|
|
7166
|
-
...
|
|
7167
|
-
...
|
|
7300
|
+
root: report.root,
|
|
7301
|
+
nodeCount: report.nodeCount,
|
|
7302
|
+
truncated: report.truncated,
|
|
7303
|
+
...report.planningTimeMs === undefined ? {} : { planningTimeMs: report.planningTimeMs },
|
|
7304
|
+
...report.executionTimeMs === undefined ? {} : { executionTimeMs: report.executionTimeMs }
|
|
7168
7305
|
},
|
|
7169
7306
|
mode
|
|
7170
7307
|
});
|
|
@@ -7215,6 +7352,10 @@ function createStatementDiagnostics(params) {
|
|
|
7215
7352
|
report({ ...base, explainSkipped: "not-a-read" });
|
|
7216
7353
|
return;
|
|
7217
7354
|
}
|
|
7355
|
+
if (outstanding.size >= STATEMENT_DIAGNOSTICS_DEFAULTS.maxOutstandingExplains) {
|
|
7356
|
+
report({ ...base, explainSkipped: "overload" });
|
|
7357
|
+
return;
|
|
7358
|
+
}
|
|
7218
7359
|
schedule(statement, config.explain, base);
|
|
7219
7360
|
},
|
|
7220
7361
|
flush: async () => {
|
|
@@ -7346,7 +7487,7 @@ async function readWorkloadStatistics(params) {
|
|
|
7346
7487
|
};
|
|
7347
7488
|
}
|
|
7348
7489
|
// packages/runtime/src/diagnostics/insight.ts
|
|
7349
|
-
import { VibeError as
|
|
7490
|
+
import { VibeError as VibeError17 } from "@vibeorm/schema";
|
|
7350
7491
|
var DIAGNOSTIC_THRESHOLDS = Object.freeze({
|
|
7351
7492
|
repeatThreshold: 5,
|
|
7352
7493
|
minRepeatThreshold: 2,
|
|
@@ -7364,7 +7505,7 @@ function bounded(params) {
|
|
|
7364
7505
|
if (value === undefined)
|
|
7365
7506
|
return fallback;
|
|
7366
7507
|
if (!Number.isInteger(value) || value < min || value > max) {
|
|
7367
|
-
throw new
|
|
7508
|
+
throw new VibeError17({
|
|
7368
7509
|
code: "VIBE_CONFIG",
|
|
7369
7510
|
message: `${name} must be an integer between ${min} and ${max}, got ${String(value)}`,
|
|
7370
7511
|
meta: { [name]: value }
|
|
@@ -7509,13 +7650,13 @@ function recommendFromPlan(params) {
|
|
|
7509
7650
|
candidate: false
|
|
7510
7651
|
});
|
|
7511
7652
|
} else {
|
|
7512
|
-
const
|
|
7653
|
+
const removed = node.rowsRemovedByFilter;
|
|
7513
7654
|
findings.push({
|
|
7514
7655
|
code: "sequential-scan-on-large-table",
|
|
7515
7656
|
severity: "warn",
|
|
7516
7657
|
node: node.nodeType,
|
|
7517
7658
|
relation,
|
|
7518
|
-
evidence: `sequential scan over "${relation}" (about ${facts.estimatedRows} rows, planner cost ${node.estimatedTotalCost} cost units)` + (
|
|
7659
|
+
evidence: `sequential scan over "${relation}" (about ${facts.estimatedRows} rows, planner cost ${node.estimatedTotalCost} cost units)` + (removed === undefined ? "" : `, discarding ${removed} rows by filter`) + (node.actualRows === undefined ? "" : `, returning ${node.actualRows}`),
|
|
7519
7660
|
candidate: true,
|
|
7520
7661
|
indexCandidate: {
|
|
7521
7662
|
table: relation,
|
|
@@ -7587,11 +7728,11 @@ function recommendFromPlan(params) {
|
|
|
7587
7728
|
return findings.slice(0, maxFindings);
|
|
7588
7729
|
}
|
|
7589
7730
|
// packages/runtime/src/relation-loader.ts
|
|
7590
|
-
import { VibeError as
|
|
7731
|
+
import { VibeError as VibeError19 } from "@vibeorm/schema";
|
|
7591
7732
|
import { renderStatement as renderStatement3 } from "@vibeorm/sql";
|
|
7592
7733
|
|
|
7593
7734
|
// packages/runtime/src/computed.ts
|
|
7594
|
-
import { VibeError as
|
|
7735
|
+
import { VibeError as VibeError18 } from "@vibeorm/schema";
|
|
7595
7736
|
function sameNeeds(a, b) {
|
|
7596
7737
|
if (a.length !== b.length)
|
|
7597
7738
|
return false;
|
|
@@ -7627,7 +7768,7 @@ function assertComputedManifest(params) {
|
|
|
7627
7768
|
problems.push(`"${key}" is configured but missing from the generated client`);
|
|
7628
7769
|
}
|
|
7629
7770
|
if (problems.length > 0) {
|
|
7630
|
-
throw new
|
|
7771
|
+
throw new VibeError18({
|
|
7631
7772
|
code: "VIBE_STALE_CLIENT",
|
|
7632
7773
|
message: `computed-field config changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
|
|
7633
7774
|
meta: { problems }
|
|
@@ -7655,7 +7796,7 @@ function resolveRelationConcurrency(params) {
|
|
|
7655
7796
|
if (concurrency === undefined)
|
|
7656
7797
|
return 1;
|
|
7657
7798
|
if (!Number.isInteger(concurrency) || concurrency < 1) {
|
|
7658
|
-
throw new
|
|
7799
|
+
throw new VibeError19({
|
|
7659
7800
|
code: "VIBE_CONFIG",
|
|
7660
7801
|
message: `relationLoading.concurrency must be a positive integer \u2014 got ${String(concurrency)}`,
|
|
7661
7802
|
meta: { concurrency }
|
|
@@ -7840,7 +7981,7 @@ function chunkKeys(params) {
|
|
|
7840
7981
|
const rendered = renderStatement3({ dialect: ctx.dialect, statement: build(keys.slice(0, 1)) });
|
|
7841
7982
|
fixed = rendered.values.length - (expands ? arity : 1);
|
|
7842
7983
|
if (fixed + arity > budget) {
|
|
7843
|
-
throw new
|
|
7984
|
+
throw new VibeError19({
|
|
7844
7985
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
7845
7986
|
message: `relation "${relation}" needs ${fixed + arity} bound parameters for its own predicate, more than ${ctx.dialect.name}'s ${budget}-parameter statement budget \u2014 narrow the nested filter`,
|
|
7846
7987
|
meta: { relation, dialect: ctx.dialect.name, required: fixed + arity, budget }
|
|
@@ -7970,7 +8111,7 @@ async function loadManyToMany(params) {
|
|
|
7970
8111
|
const link = node.link;
|
|
7971
8112
|
const join = link.join;
|
|
7972
8113
|
if (join === null) {
|
|
7973
|
-
throw new
|
|
8114
|
+
throw new VibeError19({
|
|
7974
8115
|
code: "VIBE_INTERNAL",
|
|
7975
8116
|
message: `manyToMany relation "${node.name}" resolved without join-table wiring`
|
|
7976
8117
|
});
|
|
@@ -8066,8 +8207,8 @@ async function loadManyToMany(params) {
|
|
|
8066
8207
|
for (const pair of pairs) {
|
|
8067
8208
|
const rawParent = pair[M2M_PARENT_ALIAS];
|
|
8068
8209
|
delete pair[M2M_PARENT_ALIAS];
|
|
8069
|
-
const
|
|
8070
|
-
const key = relationValueKey({ value:
|
|
8210
|
+
const parentKey = decodeParent === null || rawParent === null || rawParent === undefined ? rawParent : decodeParent(rawParent);
|
|
8211
|
+
const key = relationValueKey({ value: parentKey });
|
|
8071
8212
|
const group = groups.get(key);
|
|
8072
8213
|
if (group === undefined)
|
|
8073
8214
|
groups.set(key, [pair]);
|
|
@@ -8262,12 +8403,12 @@ async function attachLateralRelations(params) {
|
|
|
8262
8403
|
for (const row of rows) {
|
|
8263
8404
|
const raw = row[column];
|
|
8264
8405
|
delete row[column];
|
|
8265
|
-
let
|
|
8406
|
+
let parsed = raw;
|
|
8266
8407
|
if (typeof raw === "string") {
|
|
8267
8408
|
try {
|
|
8268
|
-
|
|
8409
|
+
parsed = JSON.parse(raw);
|
|
8269
8410
|
} catch (cause) {
|
|
8270
|
-
throw new
|
|
8411
|
+
throw new VibeError19({
|
|
8271
8412
|
code: "VIBE_INTERNAL",
|
|
8272
8413
|
message: `relation column "${column}" returned text that is not valid JSON`,
|
|
8273
8414
|
meta: { relation: node.name },
|
|
@@ -8276,10 +8417,10 @@ async function attachLateralRelations(params) {
|
|
|
8276
8417
|
}
|
|
8277
8418
|
}
|
|
8278
8419
|
if (isList) {
|
|
8279
|
-
if (!Array.isArray(
|
|
8280
|
-
throw new
|
|
8420
|
+
if (!Array.isArray(parsed)) {
|
|
8421
|
+
throw new VibeError19({ code: "VIBE_INTERNAL", message: "to-many relation payload must be an array", meta: { relation: node.name } });
|
|
8281
8422
|
}
|
|
8282
|
-
const children =
|
|
8423
|
+
const children = parsed.map(decodePosition);
|
|
8283
8424
|
for (const child of children) {
|
|
8284
8425
|
for (const name of hiddenOrder)
|
|
8285
8426
|
delete child[name];
|
|
@@ -8287,11 +8428,11 @@ async function attachLateralRelations(params) {
|
|
|
8287
8428
|
row[node.name] = children;
|
|
8288
8429
|
pushAll(kept, children);
|
|
8289
8430
|
} else {
|
|
8290
|
-
if (
|
|
8431
|
+
if (parsed === null) {
|
|
8291
8432
|
row[node.name] = null;
|
|
8292
8433
|
continue;
|
|
8293
8434
|
}
|
|
8294
|
-
const child = decodePosition(
|
|
8435
|
+
const child = decodePosition(parsed);
|
|
8295
8436
|
for (const name of hiddenOrder)
|
|
8296
8437
|
delete child[name];
|
|
8297
8438
|
row[node.name] = child;
|
|
@@ -8318,7 +8459,7 @@ async function attachLateralRelations(params) {
|
|
|
8318
8459
|
const raw = row[column];
|
|
8319
8460
|
const count = typeof raw === "number" || typeof raw === "bigint" || typeof raw === "string" && /^\d+$/.test(raw) ? Number(raw) : NaN;
|
|
8320
8461
|
if (!Number.isSafeInteger(count) || count < 0) {
|
|
8321
|
-
throw new
|
|
8462
|
+
throw new VibeError19({ code: "VIBE_INTERNAL", message: "relation count payload must be a non-negative safe integer", meta: { relation: selection.counts[index]?.name } });
|
|
8322
8463
|
}
|
|
8323
8464
|
counts[selection.counts[index].name] = count;
|
|
8324
8465
|
delete row[column];
|
|
@@ -8326,97 +8467,12 @@ async function attachLateralRelations(params) {
|
|
|
8326
8467
|
row._count = counts;
|
|
8327
8468
|
}
|
|
8328
8469
|
}
|
|
8329
|
-
// packages/runtime/src/extensions.ts
|
|
8330
|
-
import { VibeError as VibeError18, extensionManifestEntry } from "@vibeorm/schema";
|
|
8331
|
-
function assertExtensionManifest(params) {
|
|
8332
|
-
const { manifest } = params;
|
|
8333
|
-
const instances = params.instances ?? [];
|
|
8334
|
-
const live = new Map(instances.map((extension) => [extension.name, extensionManifestEntry({ extension })]));
|
|
8335
|
-
const problems = [];
|
|
8336
|
-
for (const baked of manifest) {
|
|
8337
|
-
const current = live.get(baked.name);
|
|
8338
|
-
if (current === undefined) {
|
|
8339
|
-
problems.push(`"${baked.name}" was baked into the client but not passed to VibeClient`);
|
|
8340
|
-
continue;
|
|
8341
|
-
}
|
|
8342
|
-
if (current.version !== baked.version) {
|
|
8343
|
-
problems.push(`"${baked.name}" version changed (generated ${baked.version}, live ${current.version})`);
|
|
8344
|
-
} else if (current.optionsHash !== baked.optionsHash) {
|
|
8345
|
-
problems.push(`"${baked.name}" options changed since generate`);
|
|
8346
|
-
}
|
|
8347
|
-
live.delete(baked.name);
|
|
8348
|
-
}
|
|
8349
|
-
for (const name of live.keys()) {
|
|
8350
|
-
problems.push(`"${name}" is configured but missing from the generated client`);
|
|
8351
|
-
}
|
|
8352
|
-
if (problems.length > 0) {
|
|
8353
|
-
throw new VibeError18({
|
|
8354
|
-
code: "VIBE_STALE_CLIENT",
|
|
8355
|
-
message: `extension list changed since generate; run vibeorm generate \u2014 ${problems.join("; ")}`,
|
|
8356
|
-
meta: { manifest, problems }
|
|
8357
|
-
});
|
|
8358
|
-
}
|
|
8359
|
-
}
|
|
8360
|
-
function buildExtensionOperatorMap(params) {
|
|
8361
|
-
const { instances, dialect, provider } = params;
|
|
8362
|
-
const map = new Map;
|
|
8363
|
-
const owner = new Map;
|
|
8364
|
-
for (const extension of instances) {
|
|
8365
|
-
for (const operator of extension.sql?.whereOperators ?? []) {
|
|
8366
|
-
const existing = owner.get(operator.name);
|
|
8367
|
-
if (existing !== undefined) {
|
|
8368
|
-
throw new VibeError18({
|
|
8369
|
-
code: "VIBE_CONFIG",
|
|
8370
|
-
message: `where operator "${operator.name}" is claimed by both "${existing}" and "${extension.name}"`,
|
|
8371
|
-
meta: { operator: operator.name }
|
|
8372
|
-
});
|
|
8373
|
-
}
|
|
8374
|
-
owner.set(operator.name, extension.name);
|
|
8375
|
-
map.set(operator.name, ({ column, operand, model, field }) => {
|
|
8376
|
-
const compiled = operator.compile({
|
|
8377
|
-
column,
|
|
8378
|
-
operand,
|
|
8379
|
-
dialect,
|
|
8380
|
-
provider,
|
|
8381
|
-
options: extension.options
|
|
8382
|
-
});
|
|
8383
|
-
if ("error" in compiled) {
|
|
8384
|
-
throw new VibeError18({
|
|
8385
|
-
code: "VIBE_VALIDATION",
|
|
8386
|
-
message: `"${operator.name}" on ${model}.${field}: ${compiled.error}`,
|
|
8387
|
-
meta: { model, field, operator: operator.name, extension: extension.name }
|
|
8388
|
-
});
|
|
8389
|
-
}
|
|
8390
|
-
return { kind: "fragment", fragment: compiled.fragment };
|
|
8391
|
-
});
|
|
8392
|
-
}
|
|
8393
|
-
}
|
|
8394
|
-
return map.size === 0 ? null : map;
|
|
8395
|
-
}
|
|
8396
|
-
function buildConnectHooks(params) {
|
|
8397
|
-
const { instances, dialect, provider } = params;
|
|
8398
|
-
const hooks = [];
|
|
8399
|
-
for (const extension of instances) {
|
|
8400
|
-
const onConnect = extension.runtime?.onConnect;
|
|
8401
|
-
if (onConnect === undefined)
|
|
8402
|
-
continue;
|
|
8403
|
-
hooks.push(({ execute }) => onConnect({
|
|
8404
|
-
execute: (execParams) => execute({
|
|
8405
|
-
text: execParams.text,
|
|
8406
|
-
...execParams.values === undefined ? {} : { values: execParams.values }
|
|
8407
|
-
}),
|
|
8408
|
-
dialect,
|
|
8409
|
-
provider,
|
|
8410
|
-
options: extension.options
|
|
8411
|
-
}));
|
|
8412
|
-
}
|
|
8413
|
-
return hooks;
|
|
8414
|
-
}
|
|
8415
8470
|
// packages/runtime/src/views.ts
|
|
8416
|
-
import { VibeError as
|
|
8471
|
+
import { VibeError as VibeError21 } from "@vibeorm/schema";
|
|
8417
8472
|
|
|
8418
8473
|
// packages/runtime/src/sql-access.ts
|
|
8419
|
-
import {
|
|
8474
|
+
import { Buffer as Buffer2 } from "buffer";
|
|
8475
|
+
import { VibeError as VibeError20 } from "@vibeorm/schema";
|
|
8420
8476
|
import { isSqlFragment as isSqlFragment2 } from "@vibeorm/sql";
|
|
8421
8477
|
function containsSqlFragment(value, seen = new Set) {
|
|
8422
8478
|
if (isSqlFragment2(value))
|
|
@@ -8424,42 +8480,92 @@ function containsSqlFragment(value, seen = new Set) {
|
|
|
8424
8480
|
if (typeof value !== "object" || value === null || seen.has(value))
|
|
8425
8481
|
return false;
|
|
8426
8482
|
seen.add(value);
|
|
8427
|
-
|
|
8428
|
-
|
|
8429
|
-
|
|
8483
|
+
const keys = new Set;
|
|
8484
|
+
for (let object = value;object !== null && object !== Object.prototype; object = Object.getPrototypeOf(object)) {
|
|
8485
|
+
for (const key of Object.getOwnPropertyNames(object)) {
|
|
8486
|
+
if (keys.has(key) || key === "constructor")
|
|
8487
|
+
continue;
|
|
8488
|
+
keys.add(key);
|
|
8489
|
+
const descriptor = Object.getOwnPropertyDescriptor(object, key);
|
|
8490
|
+
if (descriptor === undefined)
|
|
8491
|
+
continue;
|
|
8492
|
+
const child = "value" in descriptor ? descriptor.value : object === value && descriptor.enumerable ? value[key] : undefined;
|
|
8493
|
+
if (containsSqlFragment(child, seen))
|
|
8494
|
+
return true;
|
|
8495
|
+
}
|
|
8496
|
+
}
|
|
8497
|
+
return false;
|
|
8430
8498
|
}
|
|
8431
8499
|
function restrictedSqlArgs(params) {
|
|
8500
|
+
const refuse = () => params.refusal?.() ?? new VibeError20({ code: "VIBE_VALIDATION", message: `SQL fragments are unavailable through ${params.path}; use the unrestricted client`, meta: { path: params.path } });
|
|
8501
|
+
if (containsSqlFragment(params.value))
|
|
8502
|
+
throw refuse();
|
|
8432
8503
|
const seen = new Map;
|
|
8433
8504
|
const copy = (value) => {
|
|
8434
8505
|
if (isSqlFragment2(value))
|
|
8435
|
-
throw
|
|
8506
|
+
throw refuse();
|
|
8436
8507
|
if (typeof value !== "object" || value === null)
|
|
8437
8508
|
return value;
|
|
8438
8509
|
if (seen.has(value))
|
|
8439
8510
|
return seen.get(value);
|
|
8511
|
+
let out;
|
|
8440
8512
|
if (Array.isArray(value)) {
|
|
8441
|
-
const
|
|
8442
|
-
|
|
8513
|
+
const array = [];
|
|
8514
|
+
out = array;
|
|
8515
|
+
seen.set(value, out);
|
|
8443
8516
|
for (const item of value)
|
|
8444
|
-
|
|
8445
|
-
|
|
8446
|
-
|
|
8447
|
-
if (value instanceof
|
|
8448
|
-
|
|
8449
|
-
|
|
8450
|
-
|
|
8451
|
-
|
|
8517
|
+
array.push(copy(item));
|
|
8518
|
+
} else if (value instanceof Date) {
|
|
8519
|
+
out = new Date(value.getTime());
|
|
8520
|
+
} else if (value instanceof Uint8Array) {
|
|
8521
|
+
out = Buffer2.isBuffer(value) ? Buffer2.from(value) : new Uint8Array(value);
|
|
8522
|
+
} else {
|
|
8523
|
+
const record = Object.create(null);
|
|
8524
|
+
out = record;
|
|
8525
|
+
seen.set(value, out);
|
|
8526
|
+
for (const [key, child] of Object.entries(value)) {
|
|
8527
|
+
if (key !== "toJSON" || typeof child !== "function")
|
|
8528
|
+
record[key] = copy(child);
|
|
8529
|
+
}
|
|
8530
|
+
}
|
|
8452
8531
|
seen.set(value, out);
|
|
8453
|
-
|
|
8454
|
-
|
|
8532
|
+
const toJSON = value.toJSON;
|
|
8533
|
+
const nativeJson = value instanceof Date && toJSON === Date.prototype.toJSON || Buffer2.isBuffer(value) && toJSON === Buffer2.prototype.toJSON;
|
|
8534
|
+
if (typeof toJSON === "function" && !nativeJson) {
|
|
8535
|
+
Object.defineProperty(out, "toJSON", { enumerable: false, value: (key) => {
|
|
8536
|
+
const result = toJSON.call(value, key);
|
|
8537
|
+
return result === value ? out : restrictedSqlArgs({ ...params, value: result });
|
|
8538
|
+
} });
|
|
8539
|
+
}
|
|
8455
8540
|
return out;
|
|
8456
8541
|
};
|
|
8457
8542
|
return copy(params.value);
|
|
8458
8543
|
}
|
|
8544
|
+
var conflictSchemas = new WeakMap;
|
|
8545
|
+
function registerRestrictedSqlSchema(params) {
|
|
8546
|
+
conflictSchemas.set(params.meta, new Map(params.schema.models.map((model) => [
|
|
8547
|
+
model.name,
|
|
8548
|
+
model.indexes.flatMap((index) => index.kind === "unique" && index.where !== undefined ? [{ fields: index.fields.map((field) => field.name), where: index.where }] : [])
|
|
8549
|
+
])));
|
|
8550
|
+
}
|
|
8551
|
+
function restrictConflictPredicates(params) {
|
|
8552
|
+
for (const key of ["onConflict", "skipDuplicates"]) {
|
|
8553
|
+
const option = params.args[key];
|
|
8554
|
+
if (typeof option !== "object" || option === null)
|
|
8555
|
+
continue;
|
|
8556
|
+
const spec = option;
|
|
8557
|
+
if (spec.where === undefined)
|
|
8558
|
+
continue;
|
|
8559
|
+
const target = spec.target;
|
|
8560
|
+
const declared = conflictSchemas.get(params.meta)?.get(params.model.name)?.some((index) => typeof spec.where === "string" && index.where === spec.where && Array.isArray(target) && target.length === index.fields.length && index.fields.every((field, position) => field === target[position]));
|
|
8561
|
+
if (declared !== true)
|
|
8562
|
+
throw new VibeError20({ code: "VIBE_VALIDATION", message: `${params.path}: ${key}.where must match a declared unique index and its target fields; free-form SQL requires the unrestricted client`, meta: { model: params.model.name, path: params.path } });
|
|
8563
|
+
}
|
|
8564
|
+
}
|
|
8459
8565
|
|
|
8460
8566
|
// packages/runtime/src/views.ts
|
|
8461
8567
|
function validationError6(params) {
|
|
8462
|
-
return new
|
|
8568
|
+
return new VibeError21({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
8463
8569
|
}
|
|
8464
8570
|
|
|
8465
8571
|
class ViewResult {
|
|
@@ -8690,9 +8796,9 @@ function mergeLevel(params) {
|
|
|
8690
8796
|
continue;
|
|
8691
8797
|
}
|
|
8692
8798
|
if (spec === true) {
|
|
8693
|
-
const
|
|
8694
|
-
select[relationName] =
|
|
8695
|
-
relationPlans.set(relationName,
|
|
8799
|
+
const child = mergeLevel({ meta, name, definition, mask: targetMask, args: {} });
|
|
8800
|
+
select[relationName] = child.merged;
|
|
8801
|
+
relationPlans.set(relationName, child.plan);
|
|
8696
8802
|
continue;
|
|
8697
8803
|
}
|
|
8698
8804
|
if (!isPlainObject5(spec)) {
|
|
@@ -8813,7 +8919,7 @@ function createView(params) {
|
|
|
8813
8919
|
for (const [clientName, mask] of resolved) {
|
|
8814
8920
|
const delegate = models[clientName];
|
|
8815
8921
|
if (delegate === undefined) {
|
|
8816
|
-
throw new
|
|
8922
|
+
throw new VibeError21({
|
|
8817
8923
|
code: "VIBE_INTERNAL",
|
|
8818
8924
|
message: `view "${name}": no delegate for model "${mask.model.name}"`,
|
|
8819
8925
|
meta: { view: name, model: mask.model.name }
|
|
@@ -8829,9 +8935,9 @@ function attachDefineView(params) {
|
|
|
8829
8935
|
Object.defineProperty(client, "$defineView", { value, enumerable: true });
|
|
8830
8936
|
}
|
|
8831
8937
|
// packages/runtime/src/validators.ts
|
|
8832
|
-
import { VibeError as
|
|
8938
|
+
import { VibeError as VibeError22 } from "@vibeorm/schema";
|
|
8833
8939
|
function validationFailed(params) {
|
|
8834
|
-
return new
|
|
8940
|
+
return new VibeError22({
|
|
8835
8941
|
code: "VIBE_VALIDATION",
|
|
8836
8942
|
message: `validation failed for ${params.model}.${params.field}: ${params.issues[0]?.message ?? "invalid value"}`,
|
|
8837
8943
|
meta: { model: params.model, field: params.field, issues: params.issues }
|
|
@@ -8840,7 +8946,7 @@ function validationFailed(params) {
|
|
|
8840
8946
|
function validateValue(params) {
|
|
8841
8947
|
const result = params.schema["~standard"].validate(params.value);
|
|
8842
8948
|
if (result instanceof Promise) {
|
|
8843
|
-
throw new
|
|
8949
|
+
throw new VibeError22({
|
|
8844
8950
|
code: "VIBE_VALIDATION",
|
|
8845
8951
|
message: `async validator on ${params.model}.${params.field} \u2014 write validators must be synchronous (validate before calling the client for async rules)`,
|
|
8846
8952
|
meta: { model: params.model, field: params.field }
|
|
@@ -8908,12 +9014,12 @@ function validateNestedVerbs(params) {
|
|
|
8908
9014
|
return value;
|
|
8909
9015
|
if (!arms.some((arm) => value[arm] !== undefined))
|
|
8910
9016
|
return one(value);
|
|
8911
|
-
const
|
|
9017
|
+
const next = { ...value };
|
|
8912
9018
|
for (const arm of arms) {
|
|
8913
9019
|
if (value[arm] !== undefined)
|
|
8914
|
-
|
|
9020
|
+
next[arm] = many(value[arm]);
|
|
8915
9021
|
}
|
|
8916
|
-
return
|
|
9022
|
+
return next;
|
|
8917
9023
|
};
|
|
8918
9024
|
const eachBody = (value, arms) => Array.isArray(value) ? value.map((item) => body(item, arms)) : body(value, arms);
|
|
8919
9025
|
const next = { ...verbs };
|
|
@@ -8963,7 +9069,29 @@ function deferPolicyOperation(params) {
|
|
|
8963
9069
|
}
|
|
8964
9070
|
|
|
8965
9071
|
// packages/runtime/src/policy.ts
|
|
8966
|
-
import { VibeError as
|
|
9072
|
+
import { VibeError as VibeError24 } from "@vibeorm/schema";
|
|
9073
|
+
|
|
9074
|
+
// packages/runtime/src/module-context.ts
|
|
9075
|
+
import { VibeError as VibeError23 } from "@vibeorm/schema";
|
|
9076
|
+
var binders = new WeakMap;
|
|
9077
|
+
function registerModuleBinder(params) {
|
|
9078
|
+
binders.set(params.client, params.bind);
|
|
9079
|
+
}
|
|
9080
|
+
function inheritReadOnlyModuleBinder(params) {
|
|
9081
|
+
const bind = binders.get(params.from);
|
|
9082
|
+
if (bind !== undefined)
|
|
9083
|
+
binders.set(params.to, (artifact) => bind(artifact).$readOnly());
|
|
9084
|
+
}
|
|
9085
|
+
function bindModuleContext(params) {
|
|
9086
|
+
const bind = binders.get(params.db);
|
|
9087
|
+
if (bind === undefined)
|
|
9088
|
+
throw new VibeError23({
|
|
9089
|
+
code: "VIBE_CONFIG",
|
|
9090
|
+
message: `Module ${params.artifact.manifest.id} needs an unrestricted handle from the same VibeORM runtime installation; scoped and foreign handles cannot attach package models`,
|
|
9091
|
+
meta: { reason: "module_handle", moduleId: params.artifact.manifest.id, protocol: params.artifact.manifest.protocol }
|
|
9092
|
+
});
|
|
9093
|
+
return bind(params.artifact);
|
|
9094
|
+
}
|
|
8967
9095
|
|
|
8968
9096
|
// packages/runtime/src/read-only.ts
|
|
8969
9097
|
import { ReadOnlyClientError } from "@vibeorm/schema";
|
|
@@ -9050,7 +9178,7 @@ function classifiedMount(params) {
|
|
|
9050
9178
|
return typeof value === "function" ? (...args) => {
|
|
9051
9179
|
if (containsSqlFragment(args))
|
|
9052
9180
|
return Promise.reject(readOnlyRefusal({ path: "extension", reason: "raw" }));
|
|
9053
|
-
return value(...restrictedSqlArgs({ value: args, path: "$readOnly extension" }));
|
|
9181
|
+
return value(...restrictedSqlArgs({ value: args, path: "$readOnly extension", refusal: () => readOnlyRefusal({ path: "extension", reason: "raw" }) }));
|
|
9054
9182
|
} : undefined;
|
|
9055
9183
|
if (typeof value !== "object" || value === null)
|
|
9056
9184
|
return;
|
|
@@ -9062,7 +9190,7 @@ function classifiedMount(params) {
|
|
|
9062
9190
|
namespace[member] = (...args) => {
|
|
9063
9191
|
if (containsSqlFragment(args))
|
|
9064
9192
|
return Promise.reject(readOnlyRefusal({ path: member, reason: "raw" }));
|
|
9065
|
-
return fn(...restrictedSqlArgs({ value: args, path: "$readOnly extension" }));
|
|
9193
|
+
return fn(...restrictedSqlArgs({ value: args, path: "$readOnly extension", refusal: () => readOnlyRefusal({ path: member, reason: "raw" }) }));
|
|
9066
9194
|
};
|
|
9067
9195
|
}
|
|
9068
9196
|
return Object.freeze(namespace);
|
|
@@ -9077,7 +9205,7 @@ function readOnlyDelegate(params) {
|
|
|
9077
9205
|
delegate[method] = (...args) => {
|
|
9078
9206
|
if (containsSqlFragment(args))
|
|
9079
9207
|
return Promise.reject(readOnlyRefusal({ path: `${model.clientName}.${method}`, reason: "raw" }));
|
|
9080
|
-
const operation = fn(...args);
|
|
9208
|
+
const operation = fn(...restrictedSqlArgs({ value: args, path: "$readOnly", refusal: () => readOnlyRefusal({ path: `${model.clientName}.${method}`, reason: "raw" }) }));
|
|
9081
9209
|
params.operations.restrictSql?.({ value: operation });
|
|
9082
9210
|
return operation;
|
|
9083
9211
|
};
|
|
@@ -9196,6 +9324,7 @@ function readOnlyOver(params) {
|
|
|
9196
9324
|
if (existing !== undefined)
|
|
9197
9325
|
return existing;
|
|
9198
9326
|
const built = buildReadOnly(params);
|
|
9327
|
+
inheritReadOnlyModuleBinder({ from: params.client, to: built });
|
|
9199
9328
|
facades.set(params.client, built);
|
|
9200
9329
|
return built;
|
|
9201
9330
|
}
|
|
@@ -9226,7 +9355,7 @@ function inheritReadOnly(params) {
|
|
|
9226
9355
|
|
|
9227
9356
|
// packages/runtime/src/policy.ts
|
|
9228
9357
|
function policyError(message, meta) {
|
|
9229
|
-
return new
|
|
9358
|
+
return new VibeError24({ code: "VIBE_VALIDATION", message, meta: meta ?? {} });
|
|
9230
9359
|
}
|
|
9231
9360
|
function policiedModels(meta) {
|
|
9232
9361
|
return meta.models.filter((model) => model.policy !== undefined);
|
|
@@ -9392,20 +9521,20 @@ function scopeSelection(params) {
|
|
|
9392
9521
|
if (spec === undefined || spec === false)
|
|
9393
9522
|
continue;
|
|
9394
9523
|
if (name === "_count") {
|
|
9395
|
-
const names = spec === true ? model.relations.filter((
|
|
9396
|
-
const entry = spec.select[
|
|
9524
|
+
const names = spec === true ? model.relations.filter((relation) => relation.isList).map((relation) => relation.name) : isPlainObject7(spec) && isPlainObject7(spec.select) ? Object.keys(spec.select).filter((key) => {
|
|
9525
|
+
const entry = spec.select[key];
|
|
9397
9526
|
return entry === true || isPlainObject7(entry);
|
|
9398
9527
|
}) : [];
|
|
9399
9528
|
const countSelect = isPlainObject7(spec) && isPlainObject7(spec.select) ? { ...spec.select } : undefined;
|
|
9400
9529
|
for (const counted of names) {
|
|
9401
|
-
const
|
|
9402
|
-
const
|
|
9403
|
-
if (
|
|
9530
|
+
const relation = model.relationByName.get(counted);
|
|
9531
|
+
const target = relation === undefined ? undefined : meta.modelByName.get(relation.targetModel);
|
|
9532
|
+
if (target?.policy !== undefined) {
|
|
9404
9533
|
throw policyError(`_count of ${model.name}.${counted} cannot enforce @@policy because relation counts take no filter \u2014 use a scoped child count query instead`, { model: model.name, relation: counted });
|
|
9405
9534
|
}
|
|
9406
9535
|
const entry = countSelect?.[counted];
|
|
9407
|
-
if (
|
|
9408
|
-
countSelect[counted] = { ...entry, where: scopeWhere({ meta, model:
|
|
9536
|
+
if (target !== undefined && countSelect !== undefined && isPlainObject7(entry) && entry.where !== undefined) {
|
|
9537
|
+
countSelect[counted] = { ...entry, where: scopeWhere({ meta, model: target, where: entry.where, context }) };
|
|
9409
9538
|
}
|
|
9410
9539
|
}
|
|
9411
9540
|
if (countSelect !== undefined)
|
|
@@ -9498,8 +9627,8 @@ function forceWriteData(params) {
|
|
|
9498
9627
|
}
|
|
9499
9628
|
function forceNestedVerbs(params) {
|
|
9500
9629
|
const { meta, target, verbs, context } = params;
|
|
9501
|
-
const
|
|
9502
|
-
const each = (value, phase) => Array.isArray(value) ? value.map((row) =>
|
|
9630
|
+
const walk = (row, phase) => isPlainObject7(row) ? forceWriteData({ meta, model: target, data: row, context, phase }) : row;
|
|
9631
|
+
const each = (value, phase) => Array.isArray(value) ? value.map((row) => walk(row, phase)) : walk(value, phase);
|
|
9503
9632
|
const body = (value, keys) => {
|
|
9504
9633
|
if (!isPlainObject7(value))
|
|
9505
9634
|
return value;
|
|
@@ -9551,7 +9680,11 @@ function scopedDelegate(params) {
|
|
|
9551
9680
|
const flat = uniqueToFilter({ model, where: scoped.where ?? {} });
|
|
9552
9681
|
return { ...scoped, where: mergeWhere({ where: flat, field: policy.field, value }) };
|
|
9553
9682
|
};
|
|
9554
|
-
const scopeWrite = (
|
|
9683
|
+
const scopeWrite = (input) => {
|
|
9684
|
+
const args = restrictedSqlArgs({ value: input, path: "$withPolicy" });
|
|
9685
|
+
restrictConflictPredicates({ meta, model, args, path: "$withPolicy" });
|
|
9686
|
+
return scopeSelection({ meta, model, args, context });
|
|
9687
|
+
};
|
|
9555
9688
|
const writeData = (data, phase) => forceWriteData({ meta, model, data: data ?? {}, context, phase });
|
|
9556
9689
|
const pkClientKey = () => {
|
|
9557
9690
|
for (const [key, group] of model.compoundUniques) {
|
|
@@ -9572,7 +9705,7 @@ function scopedDelegate(params) {
|
|
|
9572
9705
|
const pkWhere = pk.length === 1 ? { [pk[0]]: target[pk[0]] } : { [pkClientKey()]: Object.fromEntries(pk.map((name) => [name, target[name]])) };
|
|
9573
9706
|
return write(raw, pkWhere);
|
|
9574
9707
|
};
|
|
9575
|
-
const
|
|
9708
|
+
const notFound = (method) => new VibeError24({ code: "VIBE_NOT_FOUND", message: `${method} on ${model.name}: no row matches the unique where inside the bound policy context`, meta: { model: model.name, method } });
|
|
9576
9709
|
return {
|
|
9577
9710
|
findMany: (args) => base.findMany(scopeFind(args)),
|
|
9578
9711
|
findFirst: (args) => base.findFirst(scopeFind(args)),
|
|
@@ -9581,7 +9714,7 @@ function scopedDelegate(params) {
|
|
|
9581
9714
|
findUniqueOrThrow: (args) => base.findFirstOrThrow(scopeUnique(args)),
|
|
9582
9715
|
create: (args) => deferred(() => base.create({ ...scopeWrite(args), data: writeData(args.data, "create") })),
|
|
9583
9716
|
createMany: (args) => deferred(() => base.createMany({
|
|
9584
|
-
...args,
|
|
9717
|
+
...scopeWrite(args),
|
|
9585
9718
|
data: (args.data ?? []).map((row) => writeData(row, "create"))
|
|
9586
9719
|
})),
|
|
9587
9720
|
createManyAndReturn: (args) => deferred(() => base.createManyAndReturn({
|
|
@@ -9591,9 +9724,9 @@ function scopedDelegate(params) {
|
|
|
9591
9724
|
update: (args) => defer(async (tx) => {
|
|
9592
9725
|
const raw = tx.$bypassPolicy === undefined ? tx : tx.$bypassPolicy();
|
|
9593
9726
|
const data = writeData(args.data, "update");
|
|
9594
|
-
const row = await lockThen(raw, args, (
|
|
9727
|
+
const row = await lockThen(raw, args, (raw, pkWhere) => raw.$models[model.name].update({ ...scopeWrite(args), data, where: pkWhere }));
|
|
9595
9728
|
if (row === null)
|
|
9596
|
-
throw
|
|
9729
|
+
throw notFound("update");
|
|
9597
9730
|
return row;
|
|
9598
9731
|
}),
|
|
9599
9732
|
updateMany: (args) => deferred(() => base.updateMany({
|
|
@@ -9606,7 +9739,7 @@ function scopedDelegate(params) {
|
|
|
9606
9739
|
})
|
|
9607
9740
|
})),
|
|
9608
9741
|
upsertMany: () => {
|
|
9609
|
-
throw new
|
|
9742
|
+
throw new VibeError24({
|
|
9610
9743
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
9611
9744
|
message: "upsertMany is not available on a policy-bound client: its conflict target is not policy-checked yet",
|
|
9612
9745
|
meta: { model: model.name }
|
|
@@ -9615,7 +9748,7 @@ function scopedDelegate(params) {
|
|
|
9615
9748
|
updateManyAndReturn: (args) => deferred(() => {
|
|
9616
9749
|
const run = base.updateManyAndReturn;
|
|
9617
9750
|
if (run === undefined) {
|
|
9618
|
-
throw new
|
|
9751
|
+
throw new VibeError24({
|
|
9619
9752
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
9620
9753
|
message: "updateManyAndReturn is not available on this delegate",
|
|
9621
9754
|
meta: { model: model.name }
|
|
@@ -9636,16 +9769,16 @@ function scopedDelegate(params) {
|
|
|
9636
9769
|
const update = writeData(args.update, "update");
|
|
9637
9770
|
const create = writeData(args.create, "create");
|
|
9638
9771
|
const projection = scopeWrite(Object.fromEntries(["select", "include"].flatMap((key) => args[key] === undefined ? [] : [[key, args[key]]])));
|
|
9639
|
-
const row = await lockThen(raw, args, (
|
|
9772
|
+
const row = await lockThen(raw, args, (raw, pkWhere) => raw.$models[model.name].update({ ...projection, where: pkWhere, data: update }));
|
|
9640
9773
|
if (row !== null)
|
|
9641
9774
|
return row;
|
|
9642
9775
|
return await raw.$models[model.name].create({ ...projection, data: create });
|
|
9643
9776
|
}),
|
|
9644
9777
|
delete: (args) => defer(async (tx) => {
|
|
9645
9778
|
const raw = tx.$bypassPolicy === undefined ? tx : tx.$bypassPolicy();
|
|
9646
|
-
const row = await lockThen(raw, args, (
|
|
9779
|
+
const row = await lockThen(raw, args, (raw, pkWhere) => raw.$models[model.name].delete({ ...scopeWrite(args), where: pkWhere }));
|
|
9647
9780
|
if (row === null)
|
|
9648
|
-
throw
|
|
9781
|
+
throw notFound("delete");
|
|
9649
9782
|
return row;
|
|
9650
9783
|
}),
|
|
9651
9784
|
deleteMany: (args) => deferred(() => base.deleteMany({
|
|
@@ -9664,7 +9797,10 @@ function scopedDelegate(params) {
|
|
|
9664
9797
|
function globalDelegate(params) {
|
|
9665
9798
|
const { meta, model, base, context } = params;
|
|
9666
9799
|
const readArgs = (args) => scopeArgs({ meta, model, args: args ?? {}, context });
|
|
9667
|
-
const sel = (
|
|
9800
|
+
const sel = (input) => {
|
|
9801
|
+
const args = params.policyBound === false ? input : restrictedSqlArgs({ value: input, path: "$withPolicy" });
|
|
9802
|
+
if (params.policyBound !== false)
|
|
9803
|
+
restrictConflictPredicates({ meta, model, args, path: "$withPolicy" });
|
|
9668
9804
|
const scoped = scopeSelection({ meta, model, args, context });
|
|
9669
9805
|
return scoped.where === undefined ? scoped : { ...scoped, where: scopeWhere({ meta, model, where: scoped.where, context }) };
|
|
9670
9806
|
};
|
|
@@ -9679,14 +9815,14 @@ function globalDelegate(params) {
|
|
|
9679
9815
|
aggregate: (args) => base.aggregate(readArgs(args)),
|
|
9680
9816
|
groupBy: (args) => base.groupBy(readArgs(args)),
|
|
9681
9817
|
create: (args) => deferred(() => base.create({ ...sel(args), data: payload(args.data, "create") })),
|
|
9682
|
-
createMany: (args) => deferred(() => base.createMany({ ...args, data: (args.data ?? []).map((r) => payload(r, "create")) })),
|
|
9818
|
+
createMany: (args) => deferred(() => base.createMany({ ...sel(args), data: (args.data ?? []).map((r) => payload(r, "create")) })),
|
|
9683
9819
|
createManyAndReturn: (args) => deferred(() => base.createManyAndReturn({ ...sel(args), data: (args.data ?? []).map((r) => payload(r, "create")) })),
|
|
9684
9820
|
update: (args) => deferred(() => base.update({ ...sel(args), data: payload(args.data, "update") })),
|
|
9685
9821
|
updateMany: (args) => deferred(() => base.updateMany({ ...sel(args), data: payload(args.data, "update") })),
|
|
9686
9822
|
updateManyAndReturn: (args) => deferred(() => {
|
|
9687
9823
|
const run = base.updateManyAndReturn;
|
|
9688
9824
|
if (run === undefined) {
|
|
9689
|
-
throw new
|
|
9825
|
+
throw new VibeError24({
|
|
9690
9826
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
9691
9827
|
message: "updateManyAndReturn is not available on this delegate"
|
|
9692
9828
|
});
|
|
@@ -9697,7 +9833,7 @@ function globalDelegate(params) {
|
|
|
9697
9833
|
upsertMany: (args) => deferred(() => {
|
|
9698
9834
|
if (params.policyBound === false)
|
|
9699
9835
|
return base.upsertMany(args);
|
|
9700
|
-
throw new
|
|
9836
|
+
throw new VibeError24({
|
|
9701
9837
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
9702
9838
|
message: "upsertMany is not available on a policy-bound client: its conflict target is not policy-checked yet"
|
|
9703
9839
|
});
|
|
@@ -9764,13 +9900,26 @@ function attachPolicy(params) {
|
|
|
9764
9900
|
});
|
|
9765
9901
|
}
|
|
9766
9902
|
}
|
|
9767
|
-
const buildScoped = (
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
if (context[key] === undefined || context[key] === null) {
|
|
9771
|
-
throw policyError(`$withPolicy: missing context value "${key}" (required by ${model.name})`, { context: key });
|
|
9772
|
-
}
|
|
9903
|
+
const buildScoped = (input) => {
|
|
9904
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
9905
|
+
throw policyError("$withPolicy: context must contain own scalar identity values");
|
|
9773
9906
|
}
|
|
9907
|
+
const snapshot = Object.create(null);
|
|
9908
|
+
for (const model of policied) {
|
|
9909
|
+
const policy = model.policy;
|
|
9910
|
+
const key = policy.context;
|
|
9911
|
+
const descriptor = Object.getOwnPropertyDescriptor(Object.hasOwn(snapshot, key) ? snapshot : input, key);
|
|
9912
|
+
if (descriptor === undefined || !("value" in descriptor) || descriptor.value === undefined || descriptor.value === null) {
|
|
9913
|
+
throw policyError(`$withPolicy: missing context value "${key}" (required as an own data property by ${model.name})`, { context: key });
|
|
9914
|
+
}
|
|
9915
|
+
const value = descriptor.value;
|
|
9916
|
+
const field = model.fieldByName.get(policy.field);
|
|
9917
|
+
const valid = field !== undefined && !field.isList && (field.scalarType === "String" ? typeof value === "string" : field.scalarType === "Int" ? typeof value === "number" && Number.isSafeInteger(value) : field.scalarType === "BigInt" ? typeof value === "bigint" : field.scalarType === "Float" || field.scalarType === "Decimal" ? typeof value === "number" && Number.isFinite(value) : field.enumName !== null && typeof value === "string" && meta.enums.get(field.enumName)?.includes(value) === true);
|
|
9918
|
+
if (!valid)
|
|
9919
|
+
throw policyError(`$withPolicy: context "${key}" must match the scalar type of ${model.name}.${policy.field}`, { context: key, model: model.name, field: policy.field });
|
|
9920
|
+
snapshot[key] = value;
|
|
9921
|
+
}
|
|
9922
|
+
const context = Object.freeze(snapshot);
|
|
9774
9923
|
const scoped = Object.create(client);
|
|
9775
9924
|
const scopedModels = { ...client.$models };
|
|
9776
9925
|
for (const model of meta.models) {
|
|
@@ -9855,7 +10004,7 @@ function attachPolicy(params) {
|
|
|
9855
10004
|
});
|
|
9856
10005
|
}
|
|
9857
10006
|
// packages/runtime/src/scoped.ts
|
|
9858
|
-
import { ScopedQueryError, VibeError as
|
|
10007
|
+
import { ScopedQueryError, VibeError as VibeError25 } from "@vibeorm/schema";
|
|
9859
10008
|
function refusal(params) {
|
|
9860
10009
|
return new ScopedQueryError(params);
|
|
9861
10010
|
}
|
|
@@ -10440,7 +10589,7 @@ function walkNestedVerbs(params) {
|
|
|
10440
10589
|
const mode = nestedModeFor(targetEntry);
|
|
10441
10590
|
const out = { ...verbs };
|
|
10442
10591
|
const pred = (verbPath) => requirePred({ ctx, model: target, path: verbPath });
|
|
10443
|
-
const
|
|
10592
|
+
const refuse = (verb, why) => {
|
|
10444
10593
|
throw refusal({
|
|
10445
10594
|
model: target.name,
|
|
10446
10595
|
path: `${path}.${verb}`,
|
|
@@ -10469,7 +10618,7 @@ function walkNestedVerbs(params) {
|
|
|
10469
10618
|
continue;
|
|
10470
10619
|
}
|
|
10471
10620
|
if (scoped && mode === "refuse" && REFERENCE_VERBS2.includes(verb)) {
|
|
10472
|
-
|
|
10621
|
+
refuse(verb, `is disabled \u2014 ${target.name} is configured nested: "refuse"; go through the base client or its own delegate`);
|
|
10473
10622
|
}
|
|
10474
10623
|
switch (verb) {
|
|
10475
10624
|
case "connect":
|
|
@@ -10484,9 +10633,9 @@ function walkNestedVerbs(params) {
|
|
|
10484
10633
|
if (scoped) {
|
|
10485
10634
|
const kind = link().kind;
|
|
10486
10635
|
if (kind === "manyToMany")
|
|
10487
|
-
|
|
10636
|
+
refuse(verb, "clears the whole join set \u2014 unlinking other tenants' rows cannot be verified; disconnect explicit rows instead");
|
|
10488
10637
|
if (!implicitSafe)
|
|
10489
|
-
|
|
10638
|
+
refuse(verb, "first detaches EVERY current child, which cannot be tenant-filtered on this edge; disconnect explicit rows instead");
|
|
10490
10639
|
}
|
|
10491
10640
|
out[verb] = mapItems(operand ?? [], (item) => isPlainObject8(item) ? uniqueWhere(item, verbPath) : item);
|
|
10492
10641
|
break;
|
|
@@ -10497,10 +10646,10 @@ function walkNestedVerbs(params) {
|
|
|
10497
10646
|
const kind = link().kind;
|
|
10498
10647
|
if (kind === "toOneOwner") {
|
|
10499
10648
|
if (tenancyEdge) {
|
|
10500
|
-
|
|
10649
|
+
refuse(verb, "would orphan the row out of its tenant (through-parent set to null) \u2014 delete it or connect a replacement");
|
|
10501
10650
|
}
|
|
10502
10651
|
} else if (!implicitSafe) {
|
|
10503
|
-
|
|
10652
|
+
refuse(verb, "targets whatever row currently points here \u2014 not tenant-verifiable on this edge");
|
|
10504
10653
|
}
|
|
10505
10654
|
}
|
|
10506
10655
|
break;
|
|
@@ -10511,7 +10660,7 @@ function walkNestedVerbs(params) {
|
|
|
10511
10660
|
case "delete": {
|
|
10512
10661
|
if (!relation.isList) {
|
|
10513
10662
|
if (scoped && !implicitSafe) {
|
|
10514
|
-
|
|
10663
|
+
refuse(verb, "targets whatever row is currently connected \u2014 not tenant-verifiable on this edge");
|
|
10515
10664
|
}
|
|
10516
10665
|
break;
|
|
10517
10666
|
}
|
|
@@ -10521,7 +10670,7 @@ function walkNestedVerbs(params) {
|
|
|
10521
10670
|
case "update": {
|
|
10522
10671
|
if (!relation.isList) {
|
|
10523
10672
|
if (scoped && !implicitSafe) {
|
|
10524
|
-
|
|
10673
|
+
refuse(verb, "targets whatever row is currently connected \u2014 not tenant-verifiable on this edge; update it through its own delegate");
|
|
10525
10674
|
}
|
|
10526
10675
|
out[verb] = isPlainObject8(operand) ? "data" in operand && isPlainObject8(operand.data) ? { ...operand, data: walkChildUpdate(operand.data, verbPath) } : walkChildUpdate(operand, verbPath) : operand;
|
|
10527
10676
|
break;
|
|
@@ -10538,7 +10687,7 @@ function walkNestedVerbs(params) {
|
|
|
10538
10687
|
case "upsert": {
|
|
10539
10688
|
if (!relation.isList) {
|
|
10540
10689
|
if (scoped && !implicitSafe) {
|
|
10541
|
-
|
|
10690
|
+
refuse(verb, "updates whatever row is currently connected \u2014 not tenant-verifiable on this edge");
|
|
10542
10691
|
}
|
|
10543
10692
|
out[verb] = isPlainObject8(operand) ? {
|
|
10544
10693
|
...operand,
|
|
@@ -10588,6 +10737,7 @@ function rewriteScopedCall(params) {
|
|
|
10588
10737
|
const scoped = entry.kind !== "none";
|
|
10589
10738
|
const root = `${model.clientName}.${method}`;
|
|
10590
10739
|
const args = restrictedSqlArgs({ value: params.args ?? {}, path: "$scoped" });
|
|
10740
|
+
restrictConflictPredicates({ meta, model, args, path: "$scoped" });
|
|
10591
10741
|
const ownPred = () => requirePred({ ctx, model, path: root });
|
|
10592
10742
|
const walkOwnWhere = (where, path) => isPlainObject8(where) ? walkWhere({ ctx, model, where, path }) : where;
|
|
10593
10743
|
if (FIND_LIKE.includes(method)) {
|
|
@@ -10606,9 +10756,9 @@ function rewriteScopedCall(params) {
|
|
|
10606
10756
|
case "findUniqueOrThrow": {
|
|
10607
10757
|
walkSelection({ ctx, model, args, path: root });
|
|
10608
10758
|
if (!scoped) {
|
|
10609
|
-
const
|
|
10610
|
-
if (
|
|
10611
|
-
args.where =
|
|
10759
|
+
const walked = walkOwnWhere(args.where, `${root}.where`);
|
|
10760
|
+
if (walked !== undefined)
|
|
10761
|
+
args.where = walked;
|
|
10612
10762
|
return { model: model.name, method, args };
|
|
10613
10763
|
}
|
|
10614
10764
|
const flat = isPlainObject8(args.where) ? flattenUniqueWhere({ model, where: args.where }) : args.where;
|
|
@@ -10810,7 +10960,7 @@ function attachScoped(params) {
|
|
|
10810
10960
|
Object.defineProperty(client, "$scoped", {
|
|
10811
10961
|
value: (config) => {
|
|
10812
10962
|
if (!isPlainObject8(config) || !isPlainObject8(config.models)) {
|
|
10813
|
-
throw new
|
|
10963
|
+
throw new VibeError25({
|
|
10814
10964
|
code: "VIBE_VALIDATION",
|
|
10815
10965
|
message: "$scoped takes { scope: string | number | bigint | null, models: { <Model>: \u2026 } }"
|
|
10816
10966
|
});
|
|
@@ -10826,7 +10976,7 @@ function attachScoped(params) {
|
|
|
10826
10976
|
}
|
|
10827
10977
|
const scope = config.scope;
|
|
10828
10978
|
if (scope !== null && typeof scope !== "string" && typeof scope !== "number" && typeof scope !== "bigint") {
|
|
10829
|
-
throw new
|
|
10979
|
+
throw new VibeError25({
|
|
10830
10980
|
code: "VIBE_VALIDATION",
|
|
10831
10981
|
message: `$scoped: scope must be a string, number, bigint or null \u2014 got ${typeof scope}`
|
|
10832
10982
|
});
|
|
@@ -10842,7 +10992,7 @@ function attachScoped(params) {
|
|
|
10842
10992
|
});
|
|
10843
10993
|
}
|
|
10844
10994
|
// packages/runtime/src/rls-readiness.ts
|
|
10845
|
-
import { VibeError as
|
|
10995
|
+
import { VibeError as VibeError26, assertValidSchemaIR, unsupportedCapability as unsupportedCapability7 } from "@vibeorm/schema";
|
|
10846
10996
|
import { NATIVE_RLS as NATIVE_RLS2, getSqlDialect as getSqlDialect4, renderRlsPolicy, renderRlsRole, NATIVE_RLS_PROBE_POLICY, renderRlsReadinessQuery, renderRlsProbeTable } from "@vibeorm/sql";
|
|
10847
10997
|
import { RLS_READINESS_SQL, NATIVE_RLS_PROBE_POLICY as NATIVE_RLS_PROBE_POLICY2 } from "@vibeorm/sql";
|
|
10848
10998
|
function textOf(params) {
|
|
@@ -10866,7 +11016,7 @@ function intOf(params) {
|
|
|
10866
11016
|
return Number.parseInt(value, 10);
|
|
10867
11017
|
}
|
|
10868
11018
|
function unreadableCatalog(params) {
|
|
10869
|
-
return new
|
|
11019
|
+
return new VibeError26({
|
|
10870
11020
|
code: "VIBE_ADAPTER",
|
|
10871
11021
|
message: `native RLS readiness could not read the "${params.key}" result of the folded catalog read: ${params.detail}. ` + "The inspection is refused rather than treated as an empty result set.",
|
|
10872
11022
|
meta: { result: params.key }
|
|
@@ -10925,7 +11075,7 @@ function requireNativeRls(params) {
|
|
|
10925
11075
|
throw unsupportedCapability7({ dialect: dialect.name, feature: "nativeRowLevelSecurity" });
|
|
10926
11076
|
}
|
|
10927
11077
|
if (params.schema.rls === undefined) {
|
|
10928
|
-
throw new
|
|
11078
|
+
throw new VibeError26({
|
|
10929
11079
|
code: "VIBE_CONFIG",
|
|
10930
11080
|
message: "Native row-level security readiness requires a schema that declares native RLS"
|
|
10931
11081
|
});
|
|
@@ -11338,7 +11488,7 @@ async function inspectNativeRls(params) {
|
|
|
11338
11488
|
function requireEmbeddedRole(params) {
|
|
11339
11489
|
const { role } = params;
|
|
11340
11490
|
if (typeof role !== "string" || role.length === 0 || role.includes("\x00")) {
|
|
11341
|
-
throw new
|
|
11491
|
+
throw new VibeError26({
|
|
11342
11492
|
code: "VIBE_CONFIG",
|
|
11343
11493
|
message: "PGlite native row-level security requires a pre-existing restricted runtime role (nativeRls.runtimeRole) \u2014 its default embedded session is privileged and bypasses every policy",
|
|
11344
11494
|
meta: { provider: "pglite" }
|
|
@@ -11350,7 +11500,7 @@ function assertNativeRlsReady(params) {
|
|
|
11350
11500
|
const { report } = params;
|
|
11351
11501
|
if (report.ready)
|
|
11352
11502
|
return;
|
|
11353
|
-
throw new
|
|
11503
|
+
throw new VibeError26({
|
|
11354
11504
|
code: "VIBE_CONFIG",
|
|
11355
11505
|
message: `Native row-level security is not ready on this database: ${report.problems.map((problem) => `${problem.name} (${problem.status})`).join(", ")}`,
|
|
11356
11506
|
meta: {
|
|
@@ -11374,9 +11524,13 @@ function formatNativeRlsReport(params) {
|
|
|
11374
11524
|
}
|
|
11375
11525
|
return lines;
|
|
11376
11526
|
}
|
|
11527
|
+
// packages/runtime/src/database-module.ts
|
|
11528
|
+
import { createHash } from "crypto";
|
|
11529
|
+
import { VibeError as VibeError31, assertValidSchemaIR as assertValidSchemaIR3, providerToDialect } from "@vibeorm/schema";
|
|
11530
|
+
|
|
11377
11531
|
// packages/runtime/src/client.ts
|
|
11378
11532
|
import { compileSql } from "@vibeorm/sql";
|
|
11379
|
-
import { RlsContextError as RlsContextError2, VibeError as
|
|
11533
|
+
import { RlsContextError as RlsContextError2, VibeError as VibeError30, assertValidSchemaIR as assertValidSchemaIR2, unsupportedCapability as unsupportedCapability8 } from "@vibeorm/schema";
|
|
11380
11534
|
import { ADVISORY_LOCK_RESULT_COLUMN, ParamCollector, getSqlDialect as getSqlDialect5, renderRlsContext, renderRlsPolicy as renderRlsPolicy2, renderRlsRole as renderRlsRole2, renderStatement as renderStatement5 } from "@vibeorm/sql";
|
|
11381
11535
|
|
|
11382
11536
|
// packages/runtime/src/render-cache.ts
|
|
@@ -11416,186 +11570,186 @@ class StatementRenderCache {
|
|
|
11416
11570
|
this.entries.set(key, slot);
|
|
11417
11571
|
}
|
|
11418
11572
|
}
|
|
11419
|
-
function addIdent(
|
|
11420
|
-
|
|
11573
|
+
function addIdent(walk, name) {
|
|
11574
|
+
walk.key += `\xA7${name.length}~${name}`;
|
|
11421
11575
|
}
|
|
11422
|
-
function addCol(
|
|
11576
|
+
function addCol(walk, column) {
|
|
11423
11577
|
if (column.table !== undefined)
|
|
11424
|
-
addIdent(
|
|
11425
|
-
|
|
11426
|
-
addIdent(
|
|
11578
|
+
addIdent(walk, column.table);
|
|
11579
|
+
walk.key += ".";
|
|
11580
|
+
addIdent(walk, column.name);
|
|
11427
11581
|
if (column.transform !== undefined)
|
|
11428
|
-
|
|
11582
|
+
walk.key += column.transform === "castText" ? "^t" : "^b";
|
|
11429
11583
|
if (column.castReal === true)
|
|
11430
|
-
|
|
11584
|
+
walk.key += "^r";
|
|
11431
11585
|
if (column.alias !== undefined) {
|
|
11432
|
-
|
|
11433
|
-
addIdent(
|
|
11586
|
+
walk.key += "^a";
|
|
11587
|
+
addIdent(walk, column.alias);
|
|
11434
11588
|
}
|
|
11435
11589
|
}
|
|
11436
|
-
function addTable(
|
|
11437
|
-
addIdent(
|
|
11590
|
+
function addTable(walk, table) {
|
|
11591
|
+
addIdent(walk, table.name);
|
|
11438
11592
|
if (table.alias !== undefined) {
|
|
11439
|
-
|
|
11440
|
-
addIdent(
|
|
11593
|
+
walk.key += "@";
|
|
11594
|
+
addIdent(walk, table.alias);
|
|
11441
11595
|
}
|
|
11442
11596
|
}
|
|
11443
|
-
function addAgg(
|
|
11444
|
-
|
|
11597
|
+
function addAgg(walk, agg) {
|
|
11598
|
+
walk.key += agg.fn;
|
|
11445
11599
|
if (agg.fn !== "countAll")
|
|
11446
|
-
addCol(
|
|
11600
|
+
addCol(walk, agg.column);
|
|
11447
11601
|
}
|
|
11448
|
-
function addSetEntry(
|
|
11602
|
+
function addSetEntry(walk, name, value) {
|
|
11449
11603
|
if (isJsonParamCell(value)) {
|
|
11450
|
-
|
|
11451
|
-
addIdent(
|
|
11452
|
-
|
|
11604
|
+
walk.key += "=j";
|
|
11605
|
+
addIdent(walk, name);
|
|
11606
|
+
walk.values.push(value.text);
|
|
11453
11607
|
return;
|
|
11454
11608
|
}
|
|
11455
11609
|
if (isSetOperation(value)) {
|
|
11456
|
-
|
|
11457
|
-
addIdent(
|
|
11458
|
-
|
|
11610
|
+
walk.key += `=o${value.__op}`;
|
|
11611
|
+
addIdent(walk, name);
|
|
11612
|
+
walk.values.push(value.value);
|
|
11459
11613
|
return;
|
|
11460
11614
|
}
|
|
11461
11615
|
if (isDbNowMarker(value)) {
|
|
11462
|
-
|
|
11463
|
-
addIdent(
|
|
11616
|
+
walk.key += value.offsetSeconds === 0 ? "=N0" : "=N1";
|
|
11617
|
+
addIdent(walk, name);
|
|
11464
11618
|
if (value.offsetSeconds !== 0)
|
|
11465
|
-
|
|
11619
|
+
walk.values.push(value.offsetSeconds);
|
|
11466
11620
|
return;
|
|
11467
11621
|
}
|
|
11468
11622
|
if (isListSetOperation(value)) {
|
|
11469
|
-
|
|
11470
|
-
addIdent(
|
|
11471
|
-
|
|
11623
|
+
walk.key += `=l${value.__listOp}`;
|
|
11624
|
+
addIdent(walk, name);
|
|
11625
|
+
walk.values.push(value.value);
|
|
11472
11626
|
return;
|
|
11473
11627
|
}
|
|
11474
|
-
|
|
11475
|
-
addIdent(
|
|
11476
|
-
|
|
11628
|
+
walk.key += "=v";
|
|
11629
|
+
addIdent(walk, name);
|
|
11630
|
+
walk.values.push(value);
|
|
11477
11631
|
}
|
|
11478
|
-
function addReturning(
|
|
11632
|
+
function addReturning(walk, returning) {
|
|
11479
11633
|
if (returning === undefined || returning.length === 0) {
|
|
11480
|
-
|
|
11634
|
+
walk.key += "r0";
|
|
11481
11635
|
return;
|
|
11482
11636
|
}
|
|
11483
|
-
|
|
11637
|
+
walk.key += "r(";
|
|
11484
11638
|
for (const name of returning)
|
|
11485
|
-
addIdent(
|
|
11486
|
-
|
|
11639
|
+
addIdent(walk, name);
|
|
11640
|
+
walk.key += ")";
|
|
11487
11641
|
}
|
|
11488
|
-
function walkExpr(
|
|
11642
|
+
function walkExpr(walk, dialect, expr) {
|
|
11489
11643
|
switch (expr.kind) {
|
|
11490
11644
|
case "cmp":
|
|
11491
11645
|
if (isDbNowMarker(expr.value)) {
|
|
11492
|
-
|
|
11493
|
-
addCol(
|
|
11646
|
+
walk.key += `cN${expr.op}${expr.value.offsetSeconds === 0 ? "0" : "1"}`;
|
|
11647
|
+
addCol(walk, expr.column);
|
|
11494
11648
|
if (expr.value.offsetSeconds !== 0)
|
|
11495
|
-
|
|
11649
|
+
walk.values.push(expr.value.offsetSeconds);
|
|
11496
11650
|
return true;
|
|
11497
11651
|
}
|
|
11498
|
-
|
|
11499
|
-
addCol(
|
|
11500
|
-
|
|
11652
|
+
walk.key += `c${expr.op}`;
|
|
11653
|
+
addCol(walk, expr.column);
|
|
11654
|
+
walk.values.push(expr.value);
|
|
11501
11655
|
return true;
|
|
11502
11656
|
case "cmpCol":
|
|
11503
|
-
|
|
11504
|
-
addCol(
|
|
11505
|
-
addCol(
|
|
11657
|
+
walk.key += `k${expr.op}`;
|
|
11658
|
+
addCol(walk, expr.left);
|
|
11659
|
+
addCol(walk, expr.right);
|
|
11506
11660
|
return true;
|
|
11507
11661
|
case "isNull":
|
|
11508
|
-
|
|
11509
|
-
addCol(
|
|
11662
|
+
walk.key += expr.negate === true ? "n1" : "n0";
|
|
11663
|
+
addCol(walk, expr.column);
|
|
11510
11664
|
return true;
|
|
11511
11665
|
case "inArray": {
|
|
11512
11666
|
if (expr.values.length === 0) {
|
|
11513
|
-
|
|
11667
|
+
walk.key += expr.negate === true ? "z1" : "z0";
|
|
11514
11668
|
return true;
|
|
11515
11669
|
}
|
|
11516
|
-
|
|
11517
|
-
addCol(
|
|
11670
|
+
walk.key += expr.negate === true ? "i1" : "i0";
|
|
11671
|
+
addCol(walk, expr.column);
|
|
11518
11672
|
if (dialect.inArrayStrategy === "any") {
|
|
11519
|
-
|
|
11673
|
+
walk.values.push([...expr.values]);
|
|
11520
11674
|
} else {
|
|
11521
|
-
|
|
11675
|
+
walk.key += `#${expr.values.length}`;
|
|
11522
11676
|
for (const value of expr.values)
|
|
11523
|
-
|
|
11677
|
+
walk.values.push(value);
|
|
11524
11678
|
}
|
|
11525
11679
|
return true;
|
|
11526
11680
|
}
|
|
11527
11681
|
case "tupleIn": {
|
|
11528
11682
|
if (expr.columns.length === 0)
|
|
11529
11683
|
return false;
|
|
11530
|
-
|
|
11684
|
+
walk.key += `Tu${expr.negate === true ? "1" : "0"}${expr.columns.length}#${expr.rows.length}(`;
|
|
11531
11685
|
for (const column of expr.columns)
|
|
11532
|
-
addCol(
|
|
11533
|
-
|
|
11686
|
+
addCol(walk, column);
|
|
11687
|
+
walk.key += ")";
|
|
11534
11688
|
if (expr.rows.length === 0)
|
|
11535
11689
|
return true;
|
|
11536
11690
|
for (const row of expr.rows) {
|
|
11537
11691
|
if (row.length !== expr.columns.length)
|
|
11538
11692
|
return false;
|
|
11539
11693
|
for (const value of row)
|
|
11540
|
-
|
|
11694
|
+
walk.values.push(value);
|
|
11541
11695
|
}
|
|
11542
11696
|
return true;
|
|
11543
11697
|
}
|
|
11544
11698
|
case "like":
|
|
11545
|
-
|
|
11546
|
-
addCol(
|
|
11547
|
-
|
|
11699
|
+
walk.key += `L${expr.insensitive === true ? "i" : "s"}${expr.negate === true ? "n" : "p"}`;
|
|
11700
|
+
addCol(walk, expr.column);
|
|
11701
|
+
walk.values.push(expr.pattern);
|
|
11548
11702
|
return true;
|
|
11549
11703
|
case "strictMatch":
|
|
11550
|
-
|
|
11551
|
-
addCol(
|
|
11552
|
-
|
|
11704
|
+
walk.key += `S${expr.op}${expr.negate === true ? "n" : "p"}`;
|
|
11705
|
+
addCol(walk, expr.column);
|
|
11706
|
+
walk.values.push(expr.op === "equals" ? expr.value : strictMatchPattern({ strategy: dialect.strictMatchStrategy, op: expr.op, value: expr.value }));
|
|
11553
11707
|
return true;
|
|
11554
11708
|
case "and":
|
|
11555
11709
|
case "or": {
|
|
11556
|
-
|
|
11710
|
+
walk.key += `${expr.kind === "and" ? "A" : "O"}${expr.operands.length}(`;
|
|
11557
11711
|
for (const operand of expr.operands) {
|
|
11558
|
-
if (!walkExpr(
|
|
11712
|
+
if (!walkExpr(walk, dialect, operand))
|
|
11559
11713
|
return false;
|
|
11560
11714
|
}
|
|
11561
|
-
|
|
11715
|
+
walk.key += ")";
|
|
11562
11716
|
return true;
|
|
11563
11717
|
}
|
|
11564
11718
|
case "not":
|
|
11565
|
-
|
|
11566
|
-
if (!walkExpr(
|
|
11719
|
+
walk.key += "!(";
|
|
11720
|
+
if (!walkExpr(walk, dialect, expr.operand))
|
|
11567
11721
|
return false;
|
|
11568
|
-
|
|
11722
|
+
walk.key += ")";
|
|
11569
11723
|
return true;
|
|
11570
11724
|
case "exists":
|
|
11571
|
-
|
|
11572
|
-
if (!walkSelect(
|
|
11725
|
+
walk.key += expr.negate === true ? "E1(" : "E0(";
|
|
11726
|
+
if (!walkSelect(walk, dialect, expr.select))
|
|
11573
11727
|
return false;
|
|
11574
|
-
|
|
11728
|
+
walk.key += ")";
|
|
11575
11729
|
return true;
|
|
11576
11730
|
case "aggCmp":
|
|
11577
|
-
|
|
11578
|
-
addAgg(
|
|
11579
|
-
|
|
11731
|
+
walk.key += `g${expr.op}`;
|
|
11732
|
+
addAgg(walk, expr.agg);
|
|
11733
|
+
walk.values.push(expr.value);
|
|
11580
11734
|
return true;
|
|
11581
11735
|
case "arrayHas":
|
|
11582
|
-
|
|
11583
|
-
addCol(
|
|
11584
|
-
|
|
11736
|
+
walk.key += expr.negate === true ? "h1" : "h0";
|
|
11737
|
+
addCol(walk, expr.column);
|
|
11738
|
+
walk.values.push(expr.value);
|
|
11585
11739
|
return true;
|
|
11586
11740
|
case "arrayContainsAll":
|
|
11587
|
-
|
|
11588
|
-
addCol(
|
|
11589
|
-
|
|
11741
|
+
walk.key += "H";
|
|
11742
|
+
addCol(walk, expr.column);
|
|
11743
|
+
walk.values.push([...expr.values]);
|
|
11590
11744
|
return true;
|
|
11591
11745
|
case "arrayOverlaps":
|
|
11592
|
-
|
|
11593
|
-
addCol(
|
|
11594
|
-
|
|
11746
|
+
walk.key += "V";
|
|
11747
|
+
addCol(walk, expr.column);
|
|
11748
|
+
walk.values.push([...expr.values]);
|
|
11595
11749
|
return true;
|
|
11596
11750
|
case "arrayIsEmpty":
|
|
11597
|
-
|
|
11598
|
-
addCol(
|
|
11751
|
+
walk.key += expr.empty ? "e1" : "e0";
|
|
11752
|
+
addCol(walk, expr.column);
|
|
11599
11753
|
return true;
|
|
11600
11754
|
case "jsonFilter":
|
|
11601
11755
|
return false;
|
|
@@ -11603,140 +11757,140 @@ function walkExpr(walk2, dialect, expr) {
|
|
|
11603
11757
|
case "fragment":
|
|
11604
11758
|
return false;
|
|
11605
11759
|
case "raw":
|
|
11606
|
-
|
|
11607
|
-
addIdent(
|
|
11608
|
-
|
|
11760
|
+
walk.key += "R(";
|
|
11761
|
+
addIdent(walk, expr.text);
|
|
11762
|
+
walk.key += ")";
|
|
11609
11763
|
return true;
|
|
11610
11764
|
}
|
|
11611
11765
|
}
|
|
11612
|
-
function walkOrderTerm2(
|
|
11766
|
+
function walkOrderTerm2(walk, dialect, term) {
|
|
11613
11767
|
if ("agg" in term) {
|
|
11614
|
-
|
|
11615
|
-
addAgg(
|
|
11768
|
+
walk.key += "a";
|
|
11769
|
+
addAgg(walk, term.agg);
|
|
11616
11770
|
} else if ("select" in term) {
|
|
11617
|
-
|
|
11618
|
-
if (!walkSelect(
|
|
11771
|
+
walk.key += "q(";
|
|
11772
|
+
if (!walkSelect(walk, dialect, term.select))
|
|
11619
11773
|
return false;
|
|
11620
|
-
|
|
11774
|
+
walk.key += ")";
|
|
11621
11775
|
} else {
|
|
11622
|
-
addCol(
|
|
11623
|
-
|
|
11776
|
+
addCol(walk, term.column);
|
|
11777
|
+
walk.key += term.nulls === undefined ? "-" : term.nulls === "first" ? "^" : "$";
|
|
11624
11778
|
}
|
|
11625
|
-
|
|
11779
|
+
walk.key += term.direction === "asc" ? "+" : "~";
|
|
11626
11780
|
return true;
|
|
11627
11781
|
}
|
|
11628
|
-
function walkSelect(
|
|
11629
|
-
|
|
11782
|
+
function walkSelect(walk, dialect, statement) {
|
|
11783
|
+
walk.key += "S{";
|
|
11630
11784
|
if (statement.distinctOn !== undefined && statement.distinctOn.length > 0) {
|
|
11631
|
-
|
|
11785
|
+
walk.key += "d(";
|
|
11632
11786
|
for (const column of statement.distinctOn)
|
|
11633
|
-
addCol(
|
|
11634
|
-
|
|
11787
|
+
addCol(walk, column);
|
|
11788
|
+
walk.key += ")";
|
|
11635
11789
|
}
|
|
11636
|
-
|
|
11790
|
+
walk.key += "p(";
|
|
11637
11791
|
for (const column of statement.columns)
|
|
11638
|
-
addCol(
|
|
11639
|
-
|
|
11792
|
+
addCol(walk, column);
|
|
11793
|
+
walk.key += ")";
|
|
11640
11794
|
if (statement.selectExpr !== undefined) {
|
|
11641
11795
|
if (statement.selectExpr.kind === "one") {
|
|
11642
|
-
|
|
11796
|
+
walk.key += "x1";
|
|
11643
11797
|
} else {
|
|
11644
|
-
|
|
11798
|
+
walk.key += "xc";
|
|
11645
11799
|
if (statement.selectExpr.alias !== undefined)
|
|
11646
|
-
addIdent(
|
|
11800
|
+
addIdent(walk, statement.selectExpr.alias);
|
|
11647
11801
|
}
|
|
11648
11802
|
}
|
|
11649
11803
|
if (statement.aggregates !== undefined) {
|
|
11650
11804
|
for (const selection of statement.aggregates) {
|
|
11651
|
-
|
|
11652
|
-
addAgg(
|
|
11653
|
-
addIdent(
|
|
11805
|
+
walk.key += "a";
|
|
11806
|
+
addAgg(walk, selection.agg);
|
|
11807
|
+
addIdent(walk, selection.alias);
|
|
11654
11808
|
}
|
|
11655
11809
|
}
|
|
11656
11810
|
if (statement.rowNumber !== undefined) {
|
|
11657
|
-
|
|
11811
|
+
walk.key += "W(";
|
|
11658
11812
|
for (const column of statement.rowNumber.partitionBy)
|
|
11659
|
-
addCol(
|
|
11660
|
-
|
|
11813
|
+
addCol(walk, column);
|
|
11814
|
+
walk.key += ":";
|
|
11661
11815
|
for (const term of statement.rowNumber.orderBy) {
|
|
11662
|
-
if (!walkOrderTerm2(
|
|
11816
|
+
if (!walkOrderTerm2(walk, dialect, term))
|
|
11663
11817
|
return false;
|
|
11664
11818
|
}
|
|
11665
|
-
addIdent(
|
|
11666
|
-
|
|
11819
|
+
addIdent(walk, statement.rowNumber.alias);
|
|
11820
|
+
walk.key += ")";
|
|
11667
11821
|
}
|
|
11668
|
-
|
|
11669
|
-
addTable(
|
|
11822
|
+
walk.key += "f";
|
|
11823
|
+
addTable(walk, statement.table);
|
|
11670
11824
|
if (statement.fromSelect !== undefined) {
|
|
11671
|
-
|
|
11672
|
-
if (!walkSelect(
|
|
11825
|
+
walk.key += "F(";
|
|
11826
|
+
if (!walkSelect(walk, dialect, statement.fromSelect))
|
|
11673
11827
|
return false;
|
|
11674
|
-
|
|
11828
|
+
walk.key += ")";
|
|
11675
11829
|
}
|
|
11676
11830
|
if (statement.innerJoins !== undefined && statement.innerJoins.length > 0) {
|
|
11677
11831
|
for (const join of statement.innerJoins) {
|
|
11678
|
-
|
|
11679
|
-
addTable(
|
|
11832
|
+
walk.key += "J(";
|
|
11833
|
+
addTable(walk, join.table);
|
|
11680
11834
|
for (const pair of join.on) {
|
|
11681
|
-
addCol(
|
|
11682
|
-
|
|
11683
|
-
addCol(
|
|
11835
|
+
addCol(walk, pair.left);
|
|
11836
|
+
walk.key += "=";
|
|
11837
|
+
addCol(walk, pair.right);
|
|
11684
11838
|
}
|
|
11685
|
-
|
|
11839
|
+
walk.key += ")";
|
|
11686
11840
|
}
|
|
11687
11841
|
}
|
|
11688
11842
|
if (statement.lateralJoins !== undefined && statement.lateralJoins.length > 0) {
|
|
11689
11843
|
for (const join of statement.lateralJoins) {
|
|
11690
|
-
|
|
11691
|
-
|
|
11692
|
-
if (!walkSelect(
|
|
11844
|
+
walk.key += join.agg === "count" ? "Jc(" : join.agg === "jsonArray" ? "Ja(" : "Jo(";
|
|
11845
|
+
walk.key += join.rowFormat === "positional" ? "pos;" : "obj;";
|
|
11846
|
+
if (!walkSelect(walk, dialect, join.select))
|
|
11693
11847
|
return false;
|
|
11694
|
-
addIdent(
|
|
11695
|
-
addIdent(
|
|
11696
|
-
|
|
11848
|
+
addIdent(walk, join.columnAlias);
|
|
11849
|
+
addIdent(walk, join.alias);
|
|
11850
|
+
walk.key += ")";
|
|
11697
11851
|
}
|
|
11698
11852
|
}
|
|
11699
11853
|
if (statement.where !== undefined) {
|
|
11700
|
-
|
|
11701
|
-
if (!walkExpr(
|
|
11854
|
+
walk.key += "w";
|
|
11855
|
+
if (!walkExpr(walk, dialect, statement.where))
|
|
11702
11856
|
return false;
|
|
11703
11857
|
}
|
|
11704
11858
|
if (statement.groupBy !== undefined && statement.groupBy.length > 0) {
|
|
11705
|
-
|
|
11859
|
+
walk.key += "b(";
|
|
11706
11860
|
for (const column of statement.groupBy)
|
|
11707
|
-
addCol(
|
|
11708
|
-
|
|
11861
|
+
addCol(walk, column);
|
|
11862
|
+
walk.key += ")";
|
|
11709
11863
|
}
|
|
11710
11864
|
if (statement.having !== undefined) {
|
|
11711
|
-
|
|
11712
|
-
if (!walkExpr(
|
|
11865
|
+
walk.key += "v";
|
|
11866
|
+
if (!walkExpr(walk, dialect, statement.having))
|
|
11713
11867
|
return false;
|
|
11714
11868
|
}
|
|
11715
11869
|
if (statement.orderBy !== undefined && statement.orderBy.length > 0) {
|
|
11716
|
-
|
|
11870
|
+
walk.key += "o(";
|
|
11717
11871
|
for (const term of statement.orderBy) {
|
|
11718
|
-
if (!walkOrderTerm2(
|
|
11872
|
+
if (!walkOrderTerm2(walk, dialect, term))
|
|
11719
11873
|
return false;
|
|
11720
11874
|
}
|
|
11721
|
-
|
|
11875
|
+
walk.key += ")";
|
|
11722
11876
|
}
|
|
11723
11877
|
if (statement.limit !== undefined)
|
|
11724
|
-
|
|
11878
|
+
walk.key += `t${statement.limit}`;
|
|
11725
11879
|
if (statement.offset !== undefined)
|
|
11726
|
-
|
|
11880
|
+
walk.key += `s${statement.offset}`;
|
|
11727
11881
|
if (statement.rowLock !== undefined) {
|
|
11728
|
-
|
|
11882
|
+
walk.key += `u:${statement.rowLock.strength}/${statement.rowLock.wait}`;
|
|
11729
11883
|
}
|
|
11730
|
-
|
|
11884
|
+
walk.key += "}";
|
|
11731
11885
|
return true;
|
|
11732
11886
|
}
|
|
11733
|
-
function walkInsert(
|
|
11734
|
-
|
|
11735
|
-
addTable(
|
|
11736
|
-
|
|
11887
|
+
function walkInsert(walk, dialect, statement) {
|
|
11888
|
+
walk.key += "I{";
|
|
11889
|
+
addTable(walk, statement.table);
|
|
11890
|
+
walk.key += "c(";
|
|
11737
11891
|
for (const name of statement.columns)
|
|
11738
|
-
addIdent(
|
|
11739
|
-
|
|
11892
|
+
addIdent(walk, name);
|
|
11893
|
+
walk.key += `)r${statement.rows.length}`;
|
|
11740
11894
|
const arity = statement.columns.length;
|
|
11741
11895
|
for (let rowIndex = 0;rowIndex < statement.rows.length; rowIndex++) {
|
|
11742
11896
|
const row = statement.rows[rowIndex];
|
|
@@ -11745,95 +11899,95 @@ function walkInsert(walk2, dialect, statement) {
|
|
|
11745
11899
|
for (let cell = 0;cell < row.length; cell++) {
|
|
11746
11900
|
const value = row[cell];
|
|
11747
11901
|
if (isSqlDefaultCell(value))
|
|
11748
|
-
|
|
11902
|
+
walk.key += `D${rowIndex}:${cell};`;
|
|
11749
11903
|
else if (isJsonParamCell(value)) {
|
|
11750
|
-
|
|
11751
|
-
|
|
11904
|
+
walk.key += `J${rowIndex}:${cell};`;
|
|
11905
|
+
walk.values.push(value.text);
|
|
11752
11906
|
} else if (isDbNowMarker(value)) {
|
|
11753
|
-
|
|
11907
|
+
walk.key += `T${rowIndex}:${cell}:${value.offsetSeconds === 0 ? "0" : "1"};`;
|
|
11754
11908
|
if (value.offsetSeconds !== 0)
|
|
11755
|
-
|
|
11909
|
+
walk.values.push(value.offsetSeconds);
|
|
11756
11910
|
} else
|
|
11757
|
-
|
|
11911
|
+
walk.values.push(value);
|
|
11758
11912
|
}
|
|
11759
11913
|
}
|
|
11760
11914
|
const onConflict = statement.onConflict;
|
|
11761
11915
|
if (onConflict !== undefined) {
|
|
11762
11916
|
if ("doNothing" in onConflict) {
|
|
11763
|
-
|
|
11917
|
+
walk.key += "N(";
|
|
11764
11918
|
if (onConflict.constraint !== undefined) {
|
|
11765
|
-
|
|
11766
|
-
addIdent(
|
|
11919
|
+
walk.key += "K";
|
|
11920
|
+
addIdent(walk, onConflict.constraint);
|
|
11767
11921
|
}
|
|
11768
11922
|
if (onConflict.columns !== undefined) {
|
|
11769
11923
|
for (const name of onConflict.columns)
|
|
11770
|
-
addIdent(
|
|
11924
|
+
addIdent(walk, name);
|
|
11771
11925
|
}
|
|
11772
11926
|
if (onConflict.conflictWhere !== undefined) {
|
|
11773
|
-
|
|
11774
|
-
addIdent(
|
|
11927
|
+
walk.key += "w";
|
|
11928
|
+
addIdent(walk, onConflict.conflictWhere);
|
|
11775
11929
|
}
|
|
11776
|
-
|
|
11930
|
+
walk.key += ")";
|
|
11777
11931
|
} else {
|
|
11778
11932
|
if (Object.values(onConflict.update).some(isSqlFragment3))
|
|
11779
11933
|
return false;
|
|
11780
|
-
|
|
11934
|
+
walk.key += "U(";
|
|
11781
11935
|
if (onConflict.constraint !== undefined) {
|
|
11782
|
-
|
|
11783
|
-
addIdent(
|
|
11936
|
+
walk.key += "K";
|
|
11937
|
+
addIdent(walk, onConflict.constraint);
|
|
11784
11938
|
}
|
|
11785
11939
|
for (const name of onConflict.columns)
|
|
11786
|
-
addIdent(
|
|
11787
|
-
|
|
11940
|
+
addIdent(walk, name);
|
|
11941
|
+
walk.key += ":";
|
|
11788
11942
|
for (const [name, value] of Object.entries(onConflict.update))
|
|
11789
|
-
addSetEntry(
|
|
11943
|
+
addSetEntry(walk, name, value);
|
|
11790
11944
|
if (onConflict.lastInsertIdColumn !== undefined) {
|
|
11791
|
-
|
|
11792
|
-
addIdent(
|
|
11945
|
+
walk.key += "@";
|
|
11946
|
+
addIdent(walk, onConflict.lastInsertIdColumn);
|
|
11793
11947
|
}
|
|
11794
|
-
|
|
11948
|
+
walk.key += ")";
|
|
11795
11949
|
}
|
|
11796
11950
|
}
|
|
11797
|
-
addReturning(
|
|
11798
|
-
|
|
11951
|
+
addReturning(walk, statement.returning);
|
|
11952
|
+
walk.key += "}";
|
|
11799
11953
|
return true;
|
|
11800
11954
|
}
|
|
11801
|
-
function walkUpdate(
|
|
11802
|
-
|
|
11803
|
-
addTable(
|
|
11955
|
+
function walkUpdate(walk, dialect, statement) {
|
|
11956
|
+
walk.key += "U{";
|
|
11957
|
+
addTable(walk, statement.table);
|
|
11804
11958
|
for (const [name, value] of Object.entries(statement.set))
|
|
11805
|
-
addSetEntry(
|
|
11959
|
+
addSetEntry(walk, name, value);
|
|
11806
11960
|
if (statement.where !== undefined) {
|
|
11807
|
-
|
|
11808
|
-
if (!walkExpr(
|
|
11961
|
+
walk.key += "w";
|
|
11962
|
+
if (!walkExpr(walk, dialect, statement.where))
|
|
11809
11963
|
return false;
|
|
11810
11964
|
}
|
|
11811
|
-
addReturning(
|
|
11812
|
-
|
|
11965
|
+
addReturning(walk, statement.returning);
|
|
11966
|
+
walk.key += "}";
|
|
11813
11967
|
return true;
|
|
11814
11968
|
}
|
|
11815
|
-
function walkDelete(
|
|
11816
|
-
|
|
11817
|
-
addTable(
|
|
11969
|
+
function walkDelete(walk, dialect, statement) {
|
|
11970
|
+
walk.key += "D{";
|
|
11971
|
+
addTable(walk, statement.table);
|
|
11818
11972
|
if (statement.where !== undefined) {
|
|
11819
|
-
|
|
11820
|
-
if (!walkExpr(
|
|
11973
|
+
walk.key += "w";
|
|
11974
|
+
if (!walkExpr(walk, dialect, statement.where))
|
|
11821
11975
|
return false;
|
|
11822
11976
|
}
|
|
11823
|
-
addReturning(
|
|
11824
|
-
|
|
11977
|
+
addReturning(walk, statement.returning);
|
|
11978
|
+
walk.key += "}";
|
|
11825
11979
|
return true;
|
|
11826
11980
|
}
|
|
11827
|
-
function walkStatement(
|
|
11981
|
+
function walkStatement(walk, dialect, statement) {
|
|
11828
11982
|
switch (statement.kind) {
|
|
11829
11983
|
case "select":
|
|
11830
|
-
return walkSelect(
|
|
11984
|
+
return walkSelect(walk, dialect, statement);
|
|
11831
11985
|
case "insert":
|
|
11832
|
-
return walkInsert(
|
|
11986
|
+
return walkInsert(walk, dialect, statement);
|
|
11833
11987
|
case "update":
|
|
11834
|
-
return walkUpdate(
|
|
11988
|
+
return walkUpdate(walk, dialect, statement);
|
|
11835
11989
|
case "delete":
|
|
11836
|
-
return walkDelete(
|
|
11990
|
+
return walkDelete(walk, dialect, statement);
|
|
11837
11991
|
case "rlsEnable":
|
|
11838
11992
|
case "rlsForce":
|
|
11839
11993
|
case "rlsDisable":
|
|
@@ -11871,27 +12025,27 @@ function sameValues(walked, rendered) {
|
|
|
11871
12025
|
}
|
|
11872
12026
|
function renderStatementCached(params) {
|
|
11873
12027
|
const { dialect, statement, cache } = params;
|
|
11874
|
-
const
|
|
11875
|
-
if (!walkStatement(
|
|
12028
|
+
const walk = { key: "", values: [] };
|
|
12029
|
+
if (!walkStatement(walk, dialect, statement)) {
|
|
11876
12030
|
return renderStatement4({ dialect, statement });
|
|
11877
12031
|
}
|
|
11878
|
-
const slot = cache.get(
|
|
12032
|
+
const slot = cache.get(walk.key);
|
|
11879
12033
|
if (slot !== undefined) {
|
|
11880
12034
|
if (slot === null)
|
|
11881
12035
|
return renderStatement4({ dialect, statement });
|
|
11882
|
-
return { text: slot.text, values:
|
|
12036
|
+
return { text: slot.text, values: walk.values, returning: slot.returning };
|
|
11883
12037
|
}
|
|
11884
12038
|
const rendered = renderStatement4({ dialect, statement });
|
|
11885
|
-
cache.set(
|
|
12039
|
+
cache.set(walk.key, sameValues(walk.values, rendered.values) ? { text: rendered.text, returning: rendered.returning } : null);
|
|
11886
12040
|
return rendered;
|
|
11887
12041
|
}
|
|
11888
12042
|
|
|
11889
12043
|
// packages/runtime/src/strict-args.ts
|
|
11890
|
-
import { VibeError as
|
|
12044
|
+
import { VibeError as VibeError27 } from "@vibeorm/schema";
|
|
11891
12045
|
var SKIP = Symbol.for("vibeorm.skip");
|
|
11892
12046
|
function explicitUndefined(params) {
|
|
11893
12047
|
const { model, method, path } = params;
|
|
11894
|
-
return new
|
|
12048
|
+
return new VibeError27({
|
|
11895
12049
|
code: "VIBE_VALIDATION",
|
|
11896
12050
|
message: `${model}.${method}(): explicit undefined at ${path} \u2014 an undefined value is refused ` + `because it would silently drop the property (widening a filter or losing a write). ` + `Omit the property, or pass SKIP to omit it deliberately; ` + `VibeClient({ strictArguments: false }) restores the legacy behaviour.`,
|
|
11897
12051
|
meta: { model, method, path }
|
|
@@ -12068,10 +12222,10 @@ function bindRlsContext(params) {
|
|
|
12068
12222
|
}
|
|
12069
12223
|
|
|
12070
12224
|
// packages/runtime/src/telemetry/recorder.ts
|
|
12071
|
-
import { VibeError as
|
|
12225
|
+
import { VibeError as VibeError29 } from "@vibeorm/schema";
|
|
12072
12226
|
|
|
12073
12227
|
// packages/runtime/src/telemetry/config.ts
|
|
12074
|
-
import { VibeError as
|
|
12228
|
+
import { VibeError as VibeError28 } from "@vibeorm/schema";
|
|
12075
12229
|
var TELEMETRY_DEFAULTS = Object.freeze({
|
|
12076
12230
|
captureSql: false,
|
|
12077
12231
|
captureParameterValues: false,
|
|
@@ -12086,7 +12240,7 @@ var TELEMETRY_DEFAULTS = Object.freeze({
|
|
|
12086
12240
|
latencyBucketsMs: Object.freeze([1, 5, 10, 25, 50, 100, 250, 500, 1000, 2500, 5000])
|
|
12087
12241
|
});
|
|
12088
12242
|
function configError2(params) {
|
|
12089
|
-
return new
|
|
12243
|
+
return new VibeError28({
|
|
12090
12244
|
code: "VIBE_CONFIG",
|
|
12091
12245
|
message: `telemetry: ${params.message}`,
|
|
12092
12246
|
...params.meta === undefined ? {} : { meta: params.meta }
|
|
@@ -12174,7 +12328,7 @@ function deriveStage(params) {
|
|
|
12174
12328
|
return "decode";
|
|
12175
12329
|
}
|
|
12176
12330
|
function errorCodeOf(error) {
|
|
12177
|
-
return error instanceof
|
|
12331
|
+
return error instanceof VibeError29 ? error.code : undefined;
|
|
12178
12332
|
}
|
|
12179
12333
|
function newHistogram(bounds) {
|
|
12180
12334
|
return { counts: new Array(bounds.length + 1).fill(0), sum: 0, count: 0 };
|
|
@@ -12557,7 +12711,7 @@ async function instrumentStatement(params) {
|
|
|
12557
12711
|
// packages/runtime/src/client.ts
|
|
12558
12712
|
function notFound2(params) {
|
|
12559
12713
|
const { model, method } = params;
|
|
12560
|
-
return new
|
|
12714
|
+
return new VibeError30({
|
|
12561
12715
|
code: "VIBE_NOT_FOUND",
|
|
12562
12716
|
message: `no ${model} record matched the where clause of ${method}()`,
|
|
12563
12717
|
meta: { model, method }
|
|
@@ -12565,7 +12719,7 @@ function notFound2(params) {
|
|
|
12565
12719
|
}
|
|
12566
12720
|
function transactionLifecycleRefusal(params) {
|
|
12567
12721
|
const { method } = params;
|
|
12568
|
-
return new
|
|
12722
|
+
return new VibeError30({
|
|
12569
12723
|
code: "VIBE_VALIDATION",
|
|
12570
12724
|
message: `${method}() is not available on a transaction client \u2014 the connection is owned by the open transaction; call it on the client that started the transaction`,
|
|
12571
12725
|
meta: { method, inTransaction: true }
|
|
@@ -12573,7 +12727,7 @@ function transactionLifecycleRefusal(params) {
|
|
|
12573
12727
|
}
|
|
12574
12728
|
function viewIsReadOnly(params) {
|
|
12575
12729
|
const { model, method } = params;
|
|
12576
|
-
return new
|
|
12730
|
+
return new VibeError30({
|
|
12577
12731
|
code: "VIBE_VALIDATION",
|
|
12578
12732
|
message: `${model} is a view \u2014 ${method}() is not available on views (they are read-only); write to the underlying table instead`,
|
|
12579
12733
|
meta: { model, method }
|
|
@@ -12656,6 +12810,7 @@ function assembleAggregates(params) {
|
|
|
12656
12810
|
}
|
|
12657
12811
|
return into;
|
|
12658
12812
|
}
|
|
12813
|
+
var moduleMetadata = new WeakMap;
|
|
12659
12814
|
function engineStateError(params) {
|
|
12660
12815
|
const { engine } = params;
|
|
12661
12816
|
for (let frame = engine.lifetime;frame !== undefined; frame = frame.parent) {
|
|
@@ -12679,7 +12834,7 @@ async function engineTransaction(params) {
|
|
|
12679
12834
|
const support = engine.adapter.budgets?.transactionDeadline;
|
|
12680
12835
|
const requested = options.deadline.enforcement ?? "between-statements";
|
|
12681
12836
|
if (support === undefined || support === "unsupported" || requested === "cancel-running-statements" && support !== "cancel-running-statements") {
|
|
12682
|
-
throw new
|
|
12837
|
+
throw new VibeError30({
|
|
12683
12838
|
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
12684
12839
|
message: "the adapter does not declare support for the requested transaction deadline",
|
|
12685
12840
|
meta: { provider: engine.adapter.provider, feature: "transactionDeadline", requested, supported: support ?? "undeclared" }
|
|
@@ -12854,8 +13009,6 @@ class QueryPromise extends Promise {
|
|
|
12854
13009
|
const state = engineStateError({ engine: this.#target });
|
|
12855
13010
|
if (state !== undefined)
|
|
12856
13011
|
return state;
|
|
12857
|
-
if (engine.rlsSchema === undefined && this.#target.rlsSchema === undefined)
|
|
12858
|
-
return;
|
|
12859
13012
|
if (this.#target.rootIdentity !== engine.rootIdentity || this.#target.lifetime !== engine.lifetime || !sameRecord(this.#target.context?.settings ?? {}, engine.context?.settings ?? {}))
|
|
12860
13013
|
return new RlsContextError2({ reason: "conflicting" });
|
|
12861
13014
|
return;
|
|
@@ -12870,7 +13023,7 @@ class QueryPromise extends Promise {
|
|
|
12870
13023
|
}
|
|
12871
13024
|
#claim() {
|
|
12872
13025
|
if (this.#started) {
|
|
12873
|
-
throw new
|
|
13026
|
+
throw new VibeError30({
|
|
12874
13027
|
code: "VIBE_VALIDATION",
|
|
12875
13028
|
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)"
|
|
12876
13029
|
});
|
|
@@ -12879,6 +13032,7 @@ class QueryPromise extends Promise {
|
|
|
12879
13032
|
const scope = this.#scope;
|
|
12880
13033
|
return {
|
|
12881
13034
|
start: (target) => {
|
|
13035
|
+
target = { ...target, meta: this.#target.meta, renderCache: this.#target.renderCache, options: this.#target.options };
|
|
12882
13036
|
if (this.#target.readOnlySql === true)
|
|
12883
13037
|
target = { ...target, readOnlySql: true };
|
|
12884
13038
|
if (scope !== undefined && target.operation !== undefined) {
|
|
@@ -12939,11 +13093,11 @@ async function runRows(params) {
|
|
|
12939
13093
|
return materializeRows({ dialect: engine.dialect.name, model: plan.model, rows, wire: engine.adapter.wire });
|
|
12940
13094
|
}
|
|
12941
13095
|
async function runBoundAffected(params) {
|
|
12942
|
-
const { adapter, text
|
|
12943
|
-
const
|
|
12944
|
-
if (
|
|
12945
|
-
return await
|
|
12946
|
-
return await adapter.executeUnsafe({ text
|
|
13096
|
+
const { adapter, text, values } = params;
|
|
13097
|
+
const bound = adapter.executeAffected;
|
|
13098
|
+
if (bound !== undefined)
|
|
13099
|
+
return await bound.call(adapter, { text, values });
|
|
13100
|
+
return await adapter.executeUnsafe({ text, values });
|
|
12947
13101
|
}
|
|
12948
13102
|
async function runAffected(params) {
|
|
12949
13103
|
const { engine, plan } = params;
|
|
@@ -12983,19 +13137,18 @@ async function runAffected(params) {
|
|
|
12983
13137
|
}
|
|
12984
13138
|
var LOCKABLE_METHODS = Object.freeze(["findMany", "findFirst", "findUnique"]);
|
|
12985
13139
|
function plan(params) {
|
|
12986
|
-
const { engine, model, method
|
|
12987
|
-
|
|
12988
|
-
throw readOnlyRefusal({ path: `${model}.${method}`, reason: "raw" });
|
|
13140
|
+
const { engine, model, method } = params;
|
|
13141
|
+
const args = engine.readOnlySql === true ? restrictedSqlArgs({ value: params.args, path: "$readOnly", refusal: () => readOnlyRefusal({ path: `${model}.${method}`, reason: "raw" }) }) : params.args;
|
|
12989
13142
|
if (args !== undefined && args["lock"] !== undefined) {
|
|
12990
13143
|
if (engine.inTransaction !== true) {
|
|
12991
|
-
throw new
|
|
13144
|
+
throw new VibeError30({
|
|
12992
13145
|
code: "VIBE_VALIDATION",
|
|
12993
13146
|
message: `lock on ${model}.${method}() requires a transaction \u2014 call it on the client $transaction hands its callback (outside one, the lock would release at statement end)`,
|
|
12994
13147
|
meta: { model, method }
|
|
12995
13148
|
});
|
|
12996
13149
|
}
|
|
12997
13150
|
if (!LOCKABLE_METHODS.includes(method)) {
|
|
12998
|
-
throw new
|
|
13151
|
+
throw new VibeError30({
|
|
12999
13152
|
code: "VIBE_VALIDATION",
|
|
13000
13153
|
message: `lock on ${model}.${method}() is not supported \u2014 only ${LOCKABLE_METHODS.join(", ")} take a lock. ` + `Lock the rows with a find inside the transaction, then write them`,
|
|
13001
13154
|
meta: { model, method }
|
|
@@ -13022,7 +13175,7 @@ function readAcquiredVerdict(params) {
|
|
|
13022
13175
|
return true;
|
|
13023
13176
|
if (value === false || value === 0 || value === "f" || value === "false" || value === "0")
|
|
13024
13177
|
return false;
|
|
13025
|
-
throw new
|
|
13178
|
+
throw new VibeError30({
|
|
13026
13179
|
code: "VIBE_INTERNAL",
|
|
13027
13180
|
message: `internal: ${params.method}() expected a boolean "${ADVISORY_LOCK_RESULT_COLUMN}" column, got ${String(value)}`,
|
|
13028
13181
|
meta: { method: params.method, value: String(value) }
|
|
@@ -13070,6 +13223,10 @@ async function insertedKeyWhere(params) {
|
|
|
13070
13223
|
hint: "this model's key is assigned by the database and cannot be read back on this dialect"
|
|
13071
13224
|
});
|
|
13072
13225
|
}
|
|
13226
|
+
const insertId = params.insertId;
|
|
13227
|
+
if (typeof insertId === "number" && Number.isSafeInteger(insertId) || typeof insertId === "string" && /^-?\d+$/.test(insertId)) {
|
|
13228
|
+
return { [keyField.name]: decodeValue({ dialect: engine.dialect.name, field: keyField, value: insertId }) };
|
|
13229
|
+
}
|
|
13073
13230
|
const result = await runStatement({
|
|
13074
13231
|
engine,
|
|
13075
13232
|
kind: "rows",
|
|
@@ -13082,7 +13239,7 @@ async function insertedKeyWhere(params) {
|
|
|
13082
13239
|
});
|
|
13083
13240
|
const id = result.rows[0]?.id;
|
|
13084
13241
|
if (id === undefined || id === null) {
|
|
13085
|
-
throw new
|
|
13242
|
+
throw new VibeError30({
|
|
13086
13243
|
code: "VIBE_INTERNAL",
|
|
13087
13244
|
message: `could not read back the generated key for ${source.model.name}`,
|
|
13088
13245
|
meta: { model: source.model.name }
|
|
@@ -13120,7 +13277,7 @@ async function runSingleMutation(params) {
|
|
|
13120
13277
|
values: bindValues({ adapter: target.adapter, values: rendered.values })
|
|
13121
13278
|
})
|
|
13122
13279
|
});
|
|
13123
|
-
return result
|
|
13280
|
+
return result;
|
|
13124
13281
|
};
|
|
13125
13282
|
if (readBeforeWrite) {
|
|
13126
13283
|
const where = source.refetchWhere;
|
|
@@ -13131,7 +13288,7 @@ async function runSingleMutation(params) {
|
|
|
13131
13288
|
const before = await refetch({ engine: txEngine, source, where, lock: true });
|
|
13132
13289
|
if (before === null)
|
|
13133
13290
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
13134
|
-
const affected = await executeWrite(txEngine);
|
|
13291
|
+
const { affectedRows: affected } = await executeWrite(txEngine);
|
|
13135
13292
|
if (affected === 0)
|
|
13136
13293
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
13137
13294
|
return before;
|
|
@@ -13157,25 +13314,25 @@ async function runSingleMutation(params) {
|
|
|
13157
13314
|
if (!hasIdTrick)
|
|
13158
13315
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
13159
13316
|
const where = await insertedKeyWhere({ engine: txEngine, source: { ...source, refetchWhere: null } });
|
|
13160
|
-
const
|
|
13161
|
-
if (
|
|
13317
|
+
const after = await refetch({ engine: txEngine, source, where });
|
|
13318
|
+
if (after === null)
|
|
13162
13319
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
13163
|
-
return
|
|
13320
|
+
return after;
|
|
13164
13321
|
});
|
|
13165
13322
|
}
|
|
13166
13323
|
if (source.method === "update") {
|
|
13167
13324
|
return await engine.adapter.transaction(async (txAdapter) => {
|
|
13168
13325
|
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
13169
|
-
const
|
|
13170
|
-
if (
|
|
13326
|
+
const { affectedRows: affected } = await executeWrite(txEngine);
|
|
13327
|
+
if (affected === 0)
|
|
13171
13328
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
13172
|
-
const
|
|
13173
|
-
if (
|
|
13329
|
+
const after = await refetch({ engine: txEngine, source, where: knownWhere });
|
|
13330
|
+
if (after === null)
|
|
13174
13331
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
13175
|
-
return
|
|
13332
|
+
return after;
|
|
13176
13333
|
});
|
|
13177
13334
|
}
|
|
13178
|
-
const affected = await executeWrite(engine);
|
|
13335
|
+
const { affectedRows: affected } = await executeWrite(engine);
|
|
13179
13336
|
if (affected === 0 && params.allowZeroRows === true)
|
|
13180
13337
|
return null;
|
|
13181
13338
|
const after = await refetch({ engine, source, where: knownWhere });
|
|
@@ -13185,10 +13342,10 @@ async function runSingleMutation(params) {
|
|
|
13185
13342
|
}
|
|
13186
13343
|
return await engine.adapter.transaction(async (txAdapter) => {
|
|
13187
13344
|
const txEngine = { ...engine, adapter: txAdapter, inTransaction: true };
|
|
13188
|
-
const affected = await executeWrite(txEngine);
|
|
13345
|
+
const { affectedRows: affected, insertId } = await executeWrite(txEngine);
|
|
13189
13346
|
if (affected === 0 && params.allowZeroRows === true)
|
|
13190
13347
|
return null;
|
|
13191
|
-
const where = await insertedKeyWhere({ engine: txEngine, source });
|
|
13348
|
+
const where = await insertedKeyWhere({ engine: txEngine, source, insertId });
|
|
13192
13349
|
const after = await refetch({ engine: txEngine, source, where });
|
|
13193
13350
|
if (after === null)
|
|
13194
13351
|
throw notFound2({ model: source.model.name, method: source.method });
|
|
@@ -13352,21 +13509,21 @@ function makeWriteContext(params) {
|
|
|
13352
13509
|
}
|
|
13353
13510
|
function readOnlyDelegate2(params) {
|
|
13354
13511
|
const { delegate, model } = params;
|
|
13355
|
-
const
|
|
13512
|
+
const refuse = (method) => async () => {
|
|
13356
13513
|
throw viewIsReadOnly({ model, method });
|
|
13357
13514
|
};
|
|
13358
13515
|
return {
|
|
13359
13516
|
...delegate,
|
|
13360
|
-
create:
|
|
13361
|
-
createMany:
|
|
13362
|
-
createManyAndReturn:
|
|
13363
|
-
update:
|
|
13364
|
-
updateMany:
|
|
13365
|
-
updateManyAndReturn:
|
|
13366
|
-
upsert:
|
|
13367
|
-
upsertMany:
|
|
13368
|
-
delete:
|
|
13369
|
-
deleteMany:
|
|
13517
|
+
create: refuse("create"),
|
|
13518
|
+
createMany: refuse("createMany"),
|
|
13519
|
+
createManyAndReturn: refuse("createManyAndReturn"),
|
|
13520
|
+
update: refuse("update"),
|
|
13521
|
+
updateMany: refuse("updateMany"),
|
|
13522
|
+
updateManyAndReturn: refuse("updateManyAndReturn"),
|
|
13523
|
+
upsert: refuse("upsert"),
|
|
13524
|
+
upsertMany: refuse("upsertMany"),
|
|
13525
|
+
delete: refuse("delete"),
|
|
13526
|
+
deleteMany: refuse("deleteMany")
|
|
13370
13527
|
};
|
|
13371
13528
|
}
|
|
13372
13529
|
function isEffectivelyEmptyData(value) {
|
|
@@ -13410,14 +13567,14 @@ function createDelegate(params) {
|
|
|
13410
13567
|
const skipping = args.onConflict !== undefined;
|
|
13411
13568
|
if (hasNestedWrites({ model: modelMeta, data: args.data })) {
|
|
13412
13569
|
if (skipping) {
|
|
13413
|
-
throw new
|
|
13570
|
+
throw new VibeError30({
|
|
13414
13571
|
code: "VIBE_VALIDATION",
|
|
13415
13572
|
message: `onConflict cannot combine with nested writes on ${modelMeta.name}.create() \u2014 insert the parent row alone, then connect relations`,
|
|
13416
13573
|
meta: { model: modelMeta.name }
|
|
13417
13574
|
});
|
|
13418
13575
|
}
|
|
13419
13576
|
const shape = resolveResultShape({ meta: target.meta, model: modelMeta, method: "create", args });
|
|
13420
|
-
const
|
|
13577
|
+
const row = await createWithNested({
|
|
13421
13578
|
ctx: makeWriteContext({ engine: target }),
|
|
13422
13579
|
model: modelMeta,
|
|
13423
13580
|
data: args.data
|
|
@@ -13426,7 +13583,7 @@ function createDelegate(params) {
|
|
|
13426
13583
|
engine: target,
|
|
13427
13584
|
model: modelMeta,
|
|
13428
13585
|
shape: { ...shape, relationStrategy: "query" },
|
|
13429
|
-
row
|
|
13586
|
+
row
|
|
13430
13587
|
});
|
|
13431
13588
|
}
|
|
13432
13589
|
const built = plan({ engine: target, model, method: "create", args });
|
|
@@ -13444,7 +13601,7 @@ function createDelegate(params) {
|
|
|
13444
13601
|
const modelMeta = getModelMeta({ meta: target.meta, model });
|
|
13445
13602
|
if (hasNestedWrites({ model: modelMeta, data: args.data })) {
|
|
13446
13603
|
const shape = resolveResultShape({ meta: target.meta, model: modelMeta, method: "update", args });
|
|
13447
|
-
const
|
|
13604
|
+
const row = await updateWithNested({
|
|
13448
13605
|
ctx: makeWriteContext({ engine: target }),
|
|
13449
13606
|
model: modelMeta,
|
|
13450
13607
|
where: args.where,
|
|
@@ -13454,7 +13611,7 @@ function createDelegate(params) {
|
|
|
13454
13611
|
engine: target,
|
|
13455
13612
|
model: modelMeta,
|
|
13456
13613
|
shape: { ...shape, relationStrategy: "query" },
|
|
13457
|
-
row
|
|
13614
|
+
row
|
|
13458
13615
|
});
|
|
13459
13616
|
}
|
|
13460
13617
|
const built = plan({ engine: target, model, method: "update", args });
|
|
@@ -13511,7 +13668,7 @@ function createDelegate(params) {
|
|
|
13511
13668
|
const upsert = async (target, args) => {
|
|
13512
13669
|
const modelMeta = getModelMeta({ meta: target.meta, model });
|
|
13513
13670
|
if (hasNestedWrites({ model: modelMeta, data: args.create }) || hasNestedWrites({ model: modelMeta, data: args.update })) {
|
|
13514
|
-
throw new
|
|
13671
|
+
throw new VibeError30({
|
|
13515
13672
|
code: "VIBE_VALIDATION",
|
|
13516
13673
|
message: `nested writes are not supported inside upsert arms on ${modelMeta.name} \u2014 use create()/update() with nested data`,
|
|
13517
13674
|
meta: { model: modelMeta.name }
|
|
@@ -13519,7 +13676,7 @@ function createDelegate(params) {
|
|
|
13519
13676
|
}
|
|
13520
13677
|
if (!upsertUsesNativeStatement({ model: modelMeta, args })) {
|
|
13521
13678
|
if (args.onConflict !== undefined) {
|
|
13522
|
-
throw new
|
|
13679
|
+
throw new VibeError30({
|
|
13523
13680
|
code: "VIBE_VALIDATION",
|
|
13524
13681
|
message: `onConflict on ${modelMeta.name}.upsert() needs the native single-statement path \u2014 this shape (a guard field beyond the conflict target, or create data not binding the where's key) runs the find-then-write fallback, which cannot honor an explicit conflict target`,
|
|
13525
13682
|
meta: { model: modelMeta.name }
|
|
@@ -13531,12 +13688,12 @@ function createDelegate(params) {
|
|
|
13531
13688
|
method: "upsert",
|
|
13532
13689
|
args
|
|
13533
13690
|
});
|
|
13534
|
-
const
|
|
13691
|
+
const row = await runUpsertFallback({ engine: target, model: modelMeta, args });
|
|
13535
13692
|
return await finalizeRow({
|
|
13536
13693
|
engine: target,
|
|
13537
13694
|
model: modelMeta,
|
|
13538
13695
|
shape: { ...shape, relationStrategy: "query" },
|
|
13539
|
-
row
|
|
13696
|
+
row
|
|
13540
13697
|
});
|
|
13541
13698
|
}
|
|
13542
13699
|
const built = plan({ engine: target, model, method: "upsert", args });
|
|
@@ -13691,11 +13848,11 @@ function withStrictArguments(params) {
|
|
|
13691
13848
|
function buildRawStatement(params) {
|
|
13692
13849
|
const { dialect, strings, values } = params;
|
|
13693
13850
|
const collector = new ParamCollector(dialect);
|
|
13694
|
-
let
|
|
13851
|
+
let text = strings[0] ?? "";
|
|
13695
13852
|
for (let index = 0;index < values.length; index++) {
|
|
13696
|
-
|
|
13853
|
+
text += collector.add(values[index]) + (strings[index + 1] ?? "");
|
|
13697
13854
|
}
|
|
13698
|
-
return { text
|
|
13855
|
+
return { text, values: collector.values };
|
|
13699
13856
|
}
|
|
13700
13857
|
function buildClient(params) {
|
|
13701
13858
|
const { engine } = params;
|
|
@@ -13712,7 +13869,7 @@ function buildClient(params) {
|
|
|
13712
13869
|
models[model.name] = delegate;
|
|
13713
13870
|
models[model.clientName] = delegate;
|
|
13714
13871
|
}
|
|
13715
|
-
const runRaw = async (method, statement,
|
|
13872
|
+
const runRaw = async (method, statement, decodeRows) => {
|
|
13716
13873
|
const scope = beginOperation({ engine, kind: "raw", model: null, method });
|
|
13717
13874
|
scope?.dispatch();
|
|
13718
13875
|
const running = executeOperation({
|
|
@@ -13726,9 +13883,9 @@ function buildClient(params) {
|
|
|
13726
13883
|
text: statement.text,
|
|
13727
13884
|
values: statement.values,
|
|
13728
13885
|
rowCount: (value) => method.startsWith("$query") ? value.rows.length : value.affectedRows,
|
|
13729
|
-
run: () => statement.values.length > 0 ? runBoundAffected({ adapter: target.adapter, text: statement.text, values: statement.values }) : target.adapter.executeUnsafe(statement)
|
|
13886
|
+
run: () => statement.values.length > 0 ? target.adapter.executeRawBound !== undefined ? target.adapter.executeRawBound(statement) : runBoundAffected({ adapter: target.adapter, text: statement.text, values: statement.values }) : target.adapter.executeUnsafe(statement)
|
|
13730
13887
|
});
|
|
13731
|
-
|
|
13888
|
+
decodeRows?.(result.rows);
|
|
13732
13889
|
return result;
|
|
13733
13890
|
}
|
|
13734
13891
|
});
|
|
@@ -13744,8 +13901,8 @@ function buildClient(params) {
|
|
|
13744
13901
|
});
|
|
13745
13902
|
return await running;
|
|
13746
13903
|
};
|
|
13747
|
-
const runAdvisoryLock = async (
|
|
13748
|
-
const { method, options } =
|
|
13904
|
+
const runAdvisoryLock = async (params) => {
|
|
13905
|
+
const { method, options } = params;
|
|
13749
13906
|
assertEngineActive({ engine });
|
|
13750
13907
|
const statement = compileAdvisoryLock({
|
|
13751
13908
|
dialect: engine.dialect,
|
|
@@ -13797,7 +13954,7 @@ function buildClient(params) {
|
|
|
13797
13954
|
}
|
|
13798
13955
|
for (const operation of operations) {
|
|
13799
13956
|
if (!isQueryPromise(operation)) {
|
|
13800
|
-
claimError = new
|
|
13957
|
+
claimError = new VibeError30({
|
|
13801
13958
|
code: "VIBE_VALIDATION",
|
|
13802
13959
|
message: "$transaction([...]) takes query promises from model delegate calls \u2014 a foreign promise is already running and cannot join the transaction"
|
|
13803
13960
|
});
|
|
@@ -13862,7 +14019,7 @@ function buildClient(params) {
|
|
|
13862
14019
|
$models: models,
|
|
13863
14020
|
$on: (event, listener) => {
|
|
13864
14021
|
if (event !== "query") {
|
|
13865
|
-
throw new
|
|
14022
|
+
throw new VibeError30({
|
|
13866
14023
|
code: "VIBE_VALIDATION",
|
|
13867
14024
|
message: `unknown client event "${String(event)}" \u2014 supported: "query"`,
|
|
13868
14025
|
meta: { event }
|
|
@@ -13877,7 +14034,7 @@ function buildClient(params) {
|
|
|
13877
14034
|
$querySql: async ({ query, decoder }) => {
|
|
13878
14035
|
assertEngineActive({ engine });
|
|
13879
14036
|
if (decoder === null || typeof decoder !== "object" || typeof decoder.decode !== "function")
|
|
13880
|
-
throw new
|
|
14037
|
+
throw new VibeError30({ code: "VIBE_VALIDATION", message: "$querySql requires an explicit result decoder" });
|
|
13881
14038
|
const statement = compileSql({ dialect: engine.dialect, query });
|
|
13882
14039
|
let output = [];
|
|
13883
14040
|
await runRaw("$querySql", statement, (rows) => {
|
|
@@ -13891,14 +14048,16 @@ function buildClient(params) {
|
|
|
13891
14048
|
},
|
|
13892
14049
|
$queryRaw: async (strings, ...values) => (await runRaw("$queryRaw", buildRawStatement({ dialect: engine.dialect, strings, values }))).rows,
|
|
13893
14050
|
$executeRaw: async (strings, ...values) => (await runRaw("$executeRaw", buildRawStatement({ dialect: engine.dialect, strings, values }))).affectedRows,
|
|
13894
|
-
$queryRawUnsafe: async (
|
|
13895
|
-
$executeRawUnsafe: async (
|
|
14051
|
+
$queryRawUnsafe: async (text, ...values) => (await runRaw("$queryRawUnsafe", { text, values })).rows,
|
|
14052
|
+
$executeRawUnsafe: async (text, ...values) => (await runRaw("$executeRawUnsafe", { text, values })).affectedRows,
|
|
13896
14053
|
$advisoryLock: async (options) => {
|
|
13897
14054
|
await runAdvisoryLock({ method: "$advisoryLock", options });
|
|
13898
14055
|
},
|
|
13899
14056
|
$tryAdvisoryLock: (options) => runAdvisoryLock({ method: "$tryAdvisoryLock", options }),
|
|
13900
14057
|
$connect: async () => {
|
|
13901
14058
|
assertEngineActive({ engine, requireContext: false });
|
|
14059
|
+
if (engine.borrowedModule === true)
|
|
14060
|
+
throw new VibeError30({ code: "VIBE_VALIDATION", message: "A package module borrows its adapter; the application owns connection lifecycle", meta: { reason: "module_borrowed_lifecycle", method: "$connect" } });
|
|
13902
14061
|
if (engine.inTransaction === true)
|
|
13903
14062
|
throw transactionLifecycleRefusal({ method: "$connect" });
|
|
13904
14063
|
if (engine.rlsSchema !== undefined) {
|
|
@@ -13919,6 +14078,8 @@ function buildClient(params) {
|
|
|
13919
14078
|
},
|
|
13920
14079
|
$disconnect: async () => {
|
|
13921
14080
|
assertEngineActive({ engine, requireContext: false });
|
|
14081
|
+
if (engine.borrowedModule === true)
|
|
14082
|
+
throw new VibeError30({ code: "VIBE_VALIDATION", message: "A package module borrows its adapter; the application owns connection lifecycle", meta: { reason: "module_borrowed_lifecycle", method: "$disconnect" } });
|
|
13922
14083
|
if (engine.inTransaction === true)
|
|
13923
14084
|
throw transactionLifecycleRefusal({ method: "$disconnect" });
|
|
13924
14085
|
await engine.diagnostics?.flush();
|
|
@@ -13934,7 +14095,7 @@ function buildClient(params) {
|
|
|
13934
14095
|
}
|
|
13935
14096
|
attachExtensionMounts({ engine, client, models });
|
|
13936
14097
|
attachDefineView({ client, meta: engine.meta, models });
|
|
13937
|
-
const wrapDelegate = engine.options?.strictArguments !== false ? (
|
|
14098
|
+
const wrapDelegate = engine.options?.strictArguments !== false ? (params) => withStrictArguments({ engine, ...params }) : undefined;
|
|
13938
14099
|
if (engine.rlsSchema === undefined)
|
|
13939
14100
|
attachScoped({ client, meta: engine.meta, ...wrapDelegate === undefined ? {} : { wrapDelegate } });
|
|
13940
14101
|
if (engine.rlsSchema === undefined) {
|
|
@@ -13954,7 +14115,7 @@ function buildClient(params) {
|
|
|
13954
14115
|
});
|
|
13955
14116
|
} else {
|
|
13956
14117
|
const refuseLegacy = () => {
|
|
13957
|
-
throw new
|
|
14118
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: "Native row-level security cannot be combined with application policy or tenant scoping" });
|
|
13958
14119
|
};
|
|
13959
14120
|
Object.defineProperties(client, {
|
|
13960
14121
|
$withContext: { enumerable: true, value: (context) => {
|
|
@@ -13980,6 +14141,25 @@ function buildClient(params) {
|
|
|
13980
14141
|
Object.freeze(models);
|
|
13981
14142
|
Object.freeze(client);
|
|
13982
14143
|
}
|
|
14144
|
+
registerModuleBinder({ client, bind: ({ schema, manifest }) => {
|
|
14145
|
+
assertEngineActive({ engine, requireContext: false });
|
|
14146
|
+
if (engine.rlsSchema !== undefined || engine.meta.models.some((model) => model.policy !== undefined))
|
|
14147
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: `Module ${manifest.id} cannot attach models to a policy or native-RLS context`, meta: { reason: "module_restricted_context", moduleId: manifest.id } });
|
|
14148
|
+
if (manifest.dialect !== engine.dialect.name)
|
|
14149
|
+
throw new VibeError30({ code: "VIBE_UNSUPPORTED_CAPABILITY", message: `Module ${manifest.id} requires ${manifest.dialect}, received ${engine.dialect.name}`, meta: { reason: "module_dialect", moduleId: manifest.id } });
|
|
14150
|
+
const previous = engine.moduleManifests.get(manifest.id);
|
|
14151
|
+
if (previous !== undefined && (previous.schemaChecksum !== manifest.schemaChecksum || previous.revision !== manifest.revision || previous.contract !== manifest.contract))
|
|
14152
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: `Module ${manifest.id} was already bound with a different manifest; use compatible package artifacts`, meta: { reason: "module_manifest_conflict", moduleId: manifest.id, revision: manifest.revision } });
|
|
14153
|
+
let meta = moduleMetadata.get(schema);
|
|
14154
|
+
if (meta === undefined) {
|
|
14155
|
+
meta = buildRuntimeMeta({ schema });
|
|
14156
|
+
registerRestrictedSqlSchema({ meta, schema });
|
|
14157
|
+
moduleMetadata.set(schema, meta);
|
|
14158
|
+
}
|
|
14159
|
+
engine.moduleManifests.set(manifest.id, manifest);
|
|
14160
|
+
const { validate: _validate, ...options } = engine.options;
|
|
14161
|
+
return buildClient({ engine: { ...engine, meta, options, borrowedModule: true, extensionMounts: [], connectHooks: [], renderCache: new StatementRenderCache } });
|
|
14162
|
+
} });
|
|
13983
14163
|
return client;
|
|
13984
14164
|
}
|
|
13985
14165
|
function makeMountContext(params) {
|
|
@@ -13990,7 +14170,7 @@ function makeMountContext(params) {
|
|
|
13990
14170
|
provider: engine.adapter.provider,
|
|
13991
14171
|
meta: engine.meta,
|
|
13992
14172
|
model,
|
|
13993
|
-
executeRows: async ({ text
|
|
14173
|
+
executeRows: async ({ text, values, method }) => {
|
|
13994
14174
|
const scope = beginOperation({
|
|
13995
14175
|
engine,
|
|
13996
14176
|
kind: "extension",
|
|
@@ -14006,11 +14186,11 @@ function makeMountContext(params) {
|
|
|
14006
14186
|
kind: "extension",
|
|
14007
14187
|
model: model?.name ?? null,
|
|
14008
14188
|
method: method ?? extension,
|
|
14009
|
-
text
|
|
14189
|
+
text,
|
|
14010
14190
|
values,
|
|
14011
14191
|
rowCount: (value) => value.length,
|
|
14012
14192
|
run: () => target.adapter.execute({
|
|
14013
|
-
text
|
|
14193
|
+
text,
|
|
14014
14194
|
values: bindValues({ adapter: target.adapter, values: [...values] })
|
|
14015
14195
|
})
|
|
14016
14196
|
});
|
|
@@ -14024,7 +14204,7 @@ function makeMountContext(params) {
|
|
|
14024
14204
|
compileWhere: ({ where, model: modelName, qualify }) => {
|
|
14025
14205
|
const target = modelName === undefined ? model : getModelMeta({ meta: engine.meta, model: modelName });
|
|
14026
14206
|
if (target === null) {
|
|
14027
|
-
throw new
|
|
14207
|
+
throw new VibeError30({
|
|
14028
14208
|
code: "VIBE_INTERNAL",
|
|
14029
14209
|
message: `extension "${extension}" compiled a where with no model in scope`,
|
|
14030
14210
|
meta: { extension }
|
|
@@ -14053,24 +14233,24 @@ function createMountSurface(params) {
|
|
|
14053
14233
|
for (const key of path) {
|
|
14054
14234
|
receiver = current;
|
|
14055
14235
|
if (current === null || typeof current !== "object" && typeof current !== "function")
|
|
14056
|
-
throw new
|
|
14236
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
|
|
14057
14237
|
current = Reflect.get(current, key);
|
|
14058
14238
|
}
|
|
14059
14239
|
if (typeof current !== "function")
|
|
14060
|
-
throw new
|
|
14240
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: "Extension mount changed its method shape" });
|
|
14061
14241
|
return Reflect.apply(current, receiver, args);
|
|
14062
14242
|
} });
|
|
14063
14243
|
}
|
|
14064
14244
|
if (value === null || typeof value !== "object")
|
|
14065
14245
|
return value;
|
|
14066
14246
|
if (Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
14067
|
-
throw new
|
|
14247
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: "Native RLS extension mounts must expose plain objects and methods" });
|
|
14068
14248
|
}
|
|
14069
14249
|
const result = {};
|
|
14070
14250
|
for (const key of Reflect.ownKeys(value)) {
|
|
14071
14251
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
14072
14252
|
if (!("value" in descriptor))
|
|
14073
|
-
throw new
|
|
14253
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: "Native RLS extension mount accessors are unsupported" });
|
|
14074
14254
|
Object.defineProperty(result, key, { value: wrap(descriptor.value, [...path, key]), enumerable: descriptor.enumerable });
|
|
14075
14255
|
}
|
|
14076
14256
|
return Object.freeze(result);
|
|
@@ -14087,18 +14267,18 @@ function attachExtensionMounts(params) {
|
|
|
14087
14267
|
const model = getModelMeta({ meta: engine.meta, model: mount.model ?? "" });
|
|
14088
14268
|
const delegate = models[model.name];
|
|
14089
14269
|
if (delegate === undefined || mount.property in delegate) {
|
|
14090
|
-
throw new
|
|
14270
|
+
throw new VibeError30({
|
|
14091
14271
|
code: "VIBE_CONFIG",
|
|
14092
14272
|
message: `extension "${mount.extension}" cannot mount "${mount.property}" on ${model.name} \u2014 the name is taken`,
|
|
14093
14273
|
meta: { extension: mount.extension, model: model.name, property: mount.property }
|
|
14094
14274
|
});
|
|
14095
14275
|
}
|
|
14096
|
-
const
|
|
14097
|
-
Object.defineProperty(delegate, mount.property, { value
|
|
14276
|
+
const value = createMountSurface({ engine, mount, model });
|
|
14277
|
+
Object.defineProperty(delegate, mount.property, { value, enumerable: true, configurable: true });
|
|
14098
14278
|
continue;
|
|
14099
14279
|
}
|
|
14100
14280
|
if (!mount.property.startsWith("$") || mount.property in client) {
|
|
14101
|
-
throw new
|
|
14281
|
+
throw new VibeError30({
|
|
14102
14282
|
code: "VIBE_CONFIG",
|
|
14103
14283
|
message: `extension "${mount.extension}" cannot mount client property "${mount.property}" \u2014 root mounts are $-prefixed and must not collide`,
|
|
14104
14284
|
meta: { extension: mount.extension, property: mount.property }
|
|
@@ -14133,9 +14313,9 @@ function createClient(params) {
|
|
|
14133
14313
|
const role = options?.nativeRls?.runtimeRole;
|
|
14134
14314
|
if (adapter.provider === "pglite") {
|
|
14135
14315
|
if (typeof role !== "string" || role.length === 0 || role.includes("\x00"))
|
|
14136
|
-
throw new
|
|
14316
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: "PGlite native row-level security requires a pre-existing restricted runtimeRole; embedded binding is not a boundary against the host process" });
|
|
14137
14317
|
} else if (role !== undefined) {
|
|
14138
|
-
throw new
|
|
14318
|
+
throw new VibeError30({ code: "VIBE_CONFIG", message: "Server native row-level security must use the runtime login role, not a runtimeRole switch" });
|
|
14139
14319
|
}
|
|
14140
14320
|
}
|
|
14141
14321
|
if (extensions?.manifest !== undefined) {
|
|
@@ -14145,7 +14325,7 @@ function createClient(params) {
|
|
|
14145
14325
|
assertComputedManifest({ manifest: computed.manifest, specs: computed.specs });
|
|
14146
14326
|
}
|
|
14147
14327
|
if (computed === undefined && options !== undefined && options["computed"] !== undefined) {
|
|
14148
|
-
throw new
|
|
14328
|
+
throw new VibeError30({
|
|
14149
14329
|
code: "VIBE_CONFIG",
|
|
14150
14330
|
message: "this client was generated without computed fields \u2014 declare them in vibeorm.config.ts and re-run vibeorm generate",
|
|
14151
14331
|
meta: {}
|
|
@@ -14159,9 +14339,11 @@ function createClient(params) {
|
|
|
14159
14339
|
...computed?.specs === undefined ? {} : { computed: computed.specs }
|
|
14160
14340
|
});
|
|
14161
14341
|
const meta = operatorMap === null ? baseMeta : { ...baseMeta, extensionOperators: operatorMap };
|
|
14342
|
+
registerRestrictedSqlSchema({ meta, schema });
|
|
14162
14343
|
return buildClient({
|
|
14163
14344
|
engine: {
|
|
14164
14345
|
rootIdentity: Object.freeze({}),
|
|
14346
|
+
moduleManifests: new Map,
|
|
14165
14347
|
...schema.rls === undefined ? {} : { rlsSchema: schema },
|
|
14166
14348
|
meta,
|
|
14167
14349
|
adapter,
|
|
@@ -14176,14 +14358,64 @@ function createClient(params) {
|
|
|
14176
14358
|
}
|
|
14177
14359
|
});
|
|
14178
14360
|
}
|
|
14361
|
+
|
|
14362
|
+
// packages/runtime/src/database-module.ts
|
|
14363
|
+
function canonicalValue(params) {
|
|
14364
|
+
const { value } = params;
|
|
14365
|
+
if (Array.isArray(value))
|
|
14366
|
+
return value.map((entry) => canonicalValue({ value: entry }));
|
|
14367
|
+
if (value === null || typeof value !== "object")
|
|
14368
|
+
return value;
|
|
14369
|
+
return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entry]) => [key, canonicalValue({ value: entry })]));
|
|
14370
|
+
}
|
|
14371
|
+
function freezeData(params) {
|
|
14372
|
+
if (params.value === null || typeof params.value !== "object" || Object.isFrozen(params.value))
|
|
14373
|
+
return;
|
|
14374
|
+
for (const value of Object.values(params.value))
|
|
14375
|
+
freezeData({ value });
|
|
14376
|
+
Object.freeze(params.value);
|
|
14377
|
+
}
|
|
14378
|
+
function defineDatabaseModule(params) {
|
|
14379
|
+
for (const key of ["id", "contract", "revision"]) {
|
|
14380
|
+
if (typeof params[key] !== "string" || params[key].trim().length === 0 || params[key].includes("\x00"))
|
|
14381
|
+
throw new VibeError31({ code: "VIBE_CONFIG", message: `Database module ${key} must be a non-empty identifier`, meta: { reason: "module_manifest", key } });
|
|
14382
|
+
}
|
|
14383
|
+
assertValidSchemaIR3({ schema: params.schema });
|
|
14384
|
+
if (params.schema.rls !== undefined || params.schema.extensions.length > 0 || params.schema.models.some((model) => model.policy !== undefined))
|
|
14385
|
+
throw new VibeError31({
|
|
14386
|
+
code: "VIBE_UNSUPPORTED_CAPABILITY",
|
|
14387
|
+
message: `Module ${params.id} cannot declare extensions, policy or native RLS in this module protocol`,
|
|
14388
|
+
meta: { reason: "module_schema_feature", moduleId: params.id }
|
|
14389
|
+
});
|
|
14390
|
+
const serialized = JSON.stringify(canonicalValue({ value: params.schema }));
|
|
14391
|
+
const schema = JSON.parse(serialized);
|
|
14392
|
+
freezeData({ value: schema });
|
|
14393
|
+
const manifest = Object.freeze({
|
|
14394
|
+
id: params.id,
|
|
14395
|
+
contract: params.contract,
|
|
14396
|
+
revision: params.revision,
|
|
14397
|
+
protocol: 1,
|
|
14398
|
+
dialect: providerToDialect({ provider: schema.datasource.provider }),
|
|
14399
|
+
schemaChecksum: createHash("sha256").update(serialized).digest("hex")
|
|
14400
|
+
});
|
|
14401
|
+
if (params.manifest !== undefined && Object.keys(manifest).some((key) => params.manifest[key] !== manifest[key]))
|
|
14402
|
+
throw new VibeError31({
|
|
14403
|
+
code: "VIBE_STALE_CLIENT",
|
|
14404
|
+
message: `Module ${params.id} has incompatible or modified generated metadata; regenerate with a compatible runtime`,
|
|
14405
|
+
meta: { reason: "module_artifact", moduleId: params.id, revision: params.revision, protocol: params.manifest.protocol }
|
|
14406
|
+
});
|
|
14407
|
+
const artifact = Object.freeze({ schema, manifest });
|
|
14408
|
+
const bind = ({ db }) => bindModuleContext({ db, artifact });
|
|
14409
|
+
return Object.freeze({ ...artifact, bind, create: ({ adapter }) => bind({ db: createClient({ schema, adapter }) }) });
|
|
14410
|
+
}
|
|
14179
14411
|
// packages/runtime/src/keyset-iterator.ts
|
|
14180
|
-
import { VibeError as
|
|
14412
|
+
import { VibeError as VibeError32 } from "@vibeorm/schema";
|
|
14181
14413
|
function validation(params) {
|
|
14182
|
-
return new
|
|
14414
|
+
return new VibeError32({ code: "VIBE_VALIDATION", message: params.message, meta: params.meta });
|
|
14183
14415
|
}
|
|
14184
14416
|
function projectionFor(params) {
|
|
14185
|
-
const { plan
|
|
14186
|
-
const names =
|
|
14417
|
+
const { plan, select, omit } = params;
|
|
14418
|
+
const names = plan.keys.map((key) => key.field.name);
|
|
14187
14419
|
if (select !== undefined) {
|
|
14188
14420
|
const hidden = names.filter((name) => select[name] !== true);
|
|
14189
14421
|
if (hidden.length === 0)
|
|
@@ -14213,13 +14445,13 @@ function iterateKeyset(options) {
|
|
|
14213
14445
|
});
|
|
14214
14446
|
}
|
|
14215
14447
|
const model = getModelMeta({ meta, model: options.model });
|
|
14216
|
-
const
|
|
14448
|
+
const plan = compileKeysetOrderPlan({
|
|
14217
14449
|
model,
|
|
14218
14450
|
dialect,
|
|
14219
14451
|
terms: compileKeysetOrderInputs({ dialect, meta, model, orderBy }),
|
|
14220
14452
|
...options.timestampPrecision === undefined ? {} : { timestampPrecision: options.timestampPrecision }
|
|
14221
14453
|
});
|
|
14222
|
-
const projection = projectionFor({ plan
|
|
14454
|
+
const projection = projectionFor({ plan, select: options.select, omit: options.omit });
|
|
14223
14455
|
const keysetArg = options.timestampPrecision === undefined ? {} : { keyset: { timestampPrecision: options.timestampPrecision } };
|
|
14224
14456
|
let buffer = [];
|
|
14225
14457
|
let position = options.after ?? null;
|
|
@@ -14250,16 +14482,16 @@ function iterateKeyset(options) {
|
|
|
14250
14482
|
const first = rows[0];
|
|
14251
14483
|
if (position !== null && first !== undefined) {
|
|
14252
14484
|
const advanced = keysetRowIsAfter({
|
|
14253
|
-
keys:
|
|
14254
|
-
row: keysetRowValues({ plan
|
|
14255
|
-
position: decodeKeysetToken({ plan
|
|
14485
|
+
keys: plan.keys,
|
|
14486
|
+
row: keysetRowValues({ plan, row: first }),
|
|
14487
|
+
position: decodeKeysetToken({ plan, token: position })
|
|
14256
14488
|
});
|
|
14257
14489
|
if (advanced === false) {
|
|
14258
14490
|
stop();
|
|
14259
|
-
throw new
|
|
14491
|
+
throw new VibeError32({
|
|
14260
14492
|
code: "VIBE_DATA_INVALID",
|
|
14261
14493
|
message: `keyset traversal of "${options.model}" is not advancing: the engine returned a row that does not sort after the previous position. ` + "The ordering is not total, or a cursor value lost precision \u2014 a DateTime column finer than milliseconds asserted as millisecond-exact does this.",
|
|
14262
|
-
meta: { model: options.model, order:
|
|
14494
|
+
meta: { model: options.model, order: plan.signature, reason: "keyset-position-not-advancing" }
|
|
14263
14495
|
});
|
|
14264
14496
|
}
|
|
14265
14497
|
}
|
|
@@ -14291,7 +14523,7 @@ function iterateKeyset(options) {
|
|
|
14291
14523
|
}
|
|
14292
14524
|
}
|
|
14293
14525
|
const raw = buffer.shift();
|
|
14294
|
-
pending = encodeKeysetToken({ plan
|
|
14526
|
+
pending = encodeKeysetToken({ plan, row: raw });
|
|
14295
14527
|
position = pending;
|
|
14296
14528
|
if (projection.hidden.length === 0)
|
|
14297
14529
|
return { done: false, value: raw };
|
|
@@ -14329,7 +14561,7 @@ function iterateKeyset(options) {
|
|
|
14329
14561
|
return iterator;
|
|
14330
14562
|
}
|
|
14331
14563
|
// packages/runtime/src/telemetry/collector.ts
|
|
14332
|
-
import { VibeError as
|
|
14564
|
+
import { VibeError as VibeError33 } from "@vibeorm/schema";
|
|
14333
14565
|
function createTelemetryCollector(params) {
|
|
14334
14566
|
const throwOn = new Set(params?.throwOn ?? []);
|
|
14335
14567
|
const operations = [];
|
|
@@ -14339,7 +14571,7 @@ function createTelemetryCollector(params) {
|
|
|
14339
14571
|
let shutdowns = 0;
|
|
14340
14572
|
const hostile = (phase) => {
|
|
14341
14573
|
if (throwOn.has(phase)) {
|
|
14342
|
-
throw new
|
|
14574
|
+
throw new VibeError33({
|
|
14343
14575
|
code: "VIBE_INTERNAL",
|
|
14344
14576
|
message: `telemetry collector: deliberate ${phase} failure`,
|
|
14345
14577
|
meta: { reason: "telemetry-collector-deliberate-failure", phase }
|
|
@@ -14395,7 +14627,7 @@ function createTelemetryCollector(params) {
|
|
|
14395
14627
|
operationFor: (lookup) => {
|
|
14396
14628
|
const matches = operations.filter((event) => event.method === lookup.method && (lookup.model === undefined || event.model === lookup.model));
|
|
14397
14629
|
if (matches.length > 1) {
|
|
14398
|
-
throw new
|
|
14630
|
+
throw new VibeError33({
|
|
14399
14631
|
code: "VIBE_VALIDATION",
|
|
14400
14632
|
message: `telemetry collector: ${matches.length} operations match ${lookup.model ?? "*"}.${lookup.method}`,
|
|
14401
14633
|
meta: {
|
|
@@ -14411,114 +14643,115 @@ function createTelemetryCollector(params) {
|
|
|
14411
14643
|
};
|
|
14412
14644
|
}
|
|
14413
14645
|
export {
|
|
14414
|
-
|
|
14415
|
-
updateWithNested,
|
|
14416
|
-
toSqlExecutor,
|
|
14417
|
-
toClientName,
|
|
14418
|
-
summarizePlan,
|
|
14419
|
-
sqlProjection,
|
|
14420
|
-
sql2 as sql,
|
|
14421
|
-
rewriteScopedCall,
|
|
14422
|
-
resolveTelemetryConfig,
|
|
14423
|
-
resolveStatementDiagnostics,
|
|
14424
|
-
resolveScopedModels,
|
|
14425
|
-
resolveResultShape,
|
|
14426
|
-
resolveRelationSelection,
|
|
14427
|
-
resolveRelationLink,
|
|
14428
|
-
recommendFromPlan,
|
|
14429
|
-
readWorkloadStatistics,
|
|
14430
|
-
readOnlyRefusal,
|
|
14431
|
-
queryFingerprint,
|
|
14432
|
-
previewOperation,
|
|
14433
|
-
parsePostgresPlan,
|
|
14434
|
-
parseDiagnosticsEnv,
|
|
14435
|
-
parameterTypeTag,
|
|
14436
|
-
normalizeSql,
|
|
14437
|
-
lateralColumnAlias,
|
|
14438
|
-
keysetRowValues,
|
|
14439
|
-
keysetRowIsAfter,
|
|
14440
|
-
iterateKeyset,
|
|
14441
|
-
isExecutableRead,
|
|
14442
|
-
isDbNow,
|
|
14443
|
-
inspectNativeRlsTransaction,
|
|
14444
|
-
inspectNativeRls,
|
|
14445
|
-
inheritReadOnly,
|
|
14446
|
-
hasNestedWrites,
|
|
14447
|
-
getModelMeta,
|
|
14448
|
-
getFieldMeta,
|
|
14449
|
-
getCodec,
|
|
14450
|
-
generateUuid,
|
|
14451
|
-
generateUlid,
|
|
14452
|
-
generateNanoid,
|
|
14453
|
-
generateDefaultValue,
|
|
14454
|
-
generateCuid,
|
|
14455
|
-
formatStatementDiagnostic,
|
|
14456
|
-
formatNativeRlsReport,
|
|
14457
|
-
explainSql,
|
|
14458
|
-
explainOperation,
|
|
14459
|
-
explainOnSession,
|
|
14460
|
-
encodeValue,
|
|
14461
|
-
encodeKeysetToken,
|
|
14462
|
-
delegateMethodAccess,
|
|
14463
|
-
decodeValue,
|
|
14464
|
-
decodeRows,
|
|
14465
|
-
decodeRow,
|
|
14466
|
-
decodeKeysetToken,
|
|
14467
|
-
decodeIsNoop2 as decodeIsNoop,
|
|
14468
|
-
decodeAggregateValue,
|
|
14469
|
-
decode2 as decode,
|
|
14470
|
-
dbNow,
|
|
14471
|
-
createWithNested,
|
|
14472
|
-
createView,
|
|
14473
|
-
createTelemetryRecorder,
|
|
14474
|
-
createTelemetryCollector,
|
|
14475
|
-
createStatementDiagnostics,
|
|
14476
|
-
createSqlModel,
|
|
14477
|
-
createScopeRewriter,
|
|
14478
|
-
createClient,
|
|
14479
|
-
compileWhere,
|
|
14480
|
-
compileSelect,
|
|
14481
|
-
compileOrderBy,
|
|
14482
|
-
compileKeysetOrderPlan,
|
|
14483
|
-
compileKeysetOrderInputs,
|
|
14484
|
-
compileAdvisoryLock,
|
|
14485
|
-
clientMemberAccess,
|
|
14486
|
-
checkStrictArguments,
|
|
14487
|
-
buildRuntimeMeta,
|
|
14488
|
-
buildQuery,
|
|
14489
|
-
buildExtensionOperatorMap,
|
|
14490
|
-
buildConnectHooks,
|
|
14491
|
-
attachScoped,
|
|
14492
|
-
attachRelations,
|
|
14493
|
-
attachReadOnly,
|
|
14494
|
-
attachPolicy,
|
|
14495
|
-
attachLateralRelations,
|
|
14496
|
-
attachDefineView,
|
|
14497
|
-
assertNativeRlsReady,
|
|
14498
|
-
assertExtensionManifest,
|
|
14499
|
-
assertComputedManifest,
|
|
14500
|
-
applyWriteValidators,
|
|
14501
|
-
applyComputedFields,
|
|
14502
|
-
analyzeOperationTelemetry,
|
|
14503
|
-
affectedRowsAreRowCounts,
|
|
14504
|
-
WRITE_DELEGATE_METHODS,
|
|
14505
|
-
ViewResult,
|
|
14506
|
-
TELEMETRY_DEFAULTS,
|
|
14507
|
-
STATEMENT_DIAGNOSTICS_DEFAULTS,
|
|
14508
|
-
SQLITE_CODECS,
|
|
14509
|
-
SKIP,
|
|
14510
|
-
READ_DELEGATE_METHODS,
|
|
14511
|
-
POSTGRES_CODECS,
|
|
14512
|
-
PLAN_DEFAULTS,
|
|
14513
|
-
MYSQL_CODECS,
|
|
14514
|
-
KEYSET_TOKEN_VERSION,
|
|
14515
|
-
KEYSET_TOKEN_PREFIX,
|
|
14516
|
-
KEYSET_MAX_PAGE_SIZE,
|
|
14517
|
-
DIALECT_CODECS2 as DIALECT_CODECS,
|
|
14518
|
-
DIAGNOSTIC_THRESHOLDS,
|
|
14519
|
-
DELEGATE_METHOD_ACCESS,
|
|
14646
|
+
CLIENT_MEMBER_ACCESS,
|
|
14520
14647
|
COUNT_ALIAS,
|
|
14521
|
-
|
|
14648
|
+
DELEGATE_METHOD_ACCESS,
|
|
14649
|
+
DIAGNOSTIC_THRESHOLDS,
|
|
14650
|
+
DIALECT_CODECS2 as DIALECT_CODECS,
|
|
14651
|
+
KEYSET_MAX_PAGE_SIZE,
|
|
14652
|
+
KEYSET_TOKEN_PREFIX,
|
|
14653
|
+
KEYSET_TOKEN_VERSION,
|
|
14654
|
+
MYSQL_CODECS,
|
|
14655
|
+
PLAN_DEFAULTS,
|
|
14656
|
+
POSTGRES_CODECS,
|
|
14657
|
+
READ_DELEGATE_METHODS,
|
|
14658
|
+
SKIP,
|
|
14659
|
+
SQLITE_CODECS,
|
|
14660
|
+
STATEMENT_DIAGNOSTICS_DEFAULTS,
|
|
14661
|
+
TELEMETRY_DEFAULTS,
|
|
14662
|
+
ViewResult,
|
|
14663
|
+
WRITE_DELEGATE_METHODS,
|
|
14664
|
+
affectedRowsAreRowCounts,
|
|
14665
|
+
analyzeOperationTelemetry,
|
|
14666
|
+
applyComputedFields,
|
|
14667
|
+
applyWriteValidators,
|
|
14668
|
+
assertComputedManifest,
|
|
14669
|
+
assertExtensionManifest,
|
|
14670
|
+
assertNativeRlsReady,
|
|
14671
|
+
attachDefineView,
|
|
14672
|
+
attachLateralRelations,
|
|
14673
|
+
attachPolicy,
|
|
14674
|
+
attachReadOnly,
|
|
14675
|
+
attachRelations,
|
|
14676
|
+
attachScoped,
|
|
14677
|
+
buildConnectHooks,
|
|
14678
|
+
buildExtensionOperatorMap,
|
|
14679
|
+
buildQuery,
|
|
14680
|
+
buildRuntimeMeta,
|
|
14681
|
+
checkStrictArguments,
|
|
14682
|
+
clientMemberAccess,
|
|
14683
|
+
compileAdvisoryLock,
|
|
14684
|
+
compileKeysetOrderInputs,
|
|
14685
|
+
compileKeysetOrderPlan,
|
|
14686
|
+
compileOrderBy,
|
|
14687
|
+
compileSelect,
|
|
14688
|
+
compileWhere,
|
|
14689
|
+
createClient,
|
|
14690
|
+
createScopeRewriter,
|
|
14691
|
+
createSqlModel,
|
|
14692
|
+
createStatementDiagnostics,
|
|
14693
|
+
createTelemetryCollector,
|
|
14694
|
+
createTelemetryRecorder,
|
|
14695
|
+
createView,
|
|
14696
|
+
createWithNested,
|
|
14697
|
+
dbNow,
|
|
14698
|
+
decode2 as decode,
|
|
14699
|
+
decodeAggregateValue,
|
|
14700
|
+
decodeIsNoop2 as decodeIsNoop,
|
|
14701
|
+
decodeKeysetToken,
|
|
14702
|
+
decodeRow,
|
|
14703
|
+
decodeRows,
|
|
14704
|
+
decodeValue,
|
|
14705
|
+
defineDatabaseModule,
|
|
14706
|
+
delegateMethodAccess,
|
|
14707
|
+
encodeKeysetToken,
|
|
14708
|
+
encodeValue,
|
|
14709
|
+
explainOnSession,
|
|
14710
|
+
explainOperation,
|
|
14711
|
+
explainSql,
|
|
14712
|
+
formatNativeRlsReport,
|
|
14713
|
+
formatStatementDiagnostic,
|
|
14714
|
+
generateCuid,
|
|
14715
|
+
generateDefaultValue,
|
|
14716
|
+
generateNanoid,
|
|
14717
|
+
generateUlid,
|
|
14718
|
+
generateUuid,
|
|
14719
|
+
getCodec,
|
|
14720
|
+
getFieldMeta,
|
|
14721
|
+
getModelMeta,
|
|
14722
|
+
hasNestedWrites,
|
|
14723
|
+
inheritReadOnly,
|
|
14724
|
+
inspectNativeRls,
|
|
14725
|
+
inspectNativeRlsTransaction,
|
|
14726
|
+
isDbNow,
|
|
14727
|
+
isExecutableRead,
|
|
14728
|
+
iterateKeyset,
|
|
14729
|
+
keysetRowIsAfter,
|
|
14730
|
+
keysetRowValues,
|
|
14731
|
+
lateralColumnAlias,
|
|
14732
|
+
normalizeSql,
|
|
14733
|
+
parameterTypeTag,
|
|
14734
|
+
parseDiagnosticsEnv,
|
|
14735
|
+
parsePostgresPlan,
|
|
14736
|
+
previewOperation,
|
|
14737
|
+
queryFingerprint,
|
|
14738
|
+
readOnlyRefusal,
|
|
14739
|
+
readWorkloadStatistics,
|
|
14740
|
+
recommendFromPlan,
|
|
14741
|
+
resolveRelationLink,
|
|
14742
|
+
resolveRelationSelection,
|
|
14743
|
+
resolveResultShape,
|
|
14744
|
+
resolveScopedModels,
|
|
14745
|
+
resolveStatementDiagnostics,
|
|
14746
|
+
resolveTelemetryConfig,
|
|
14747
|
+
rewriteScopedCall,
|
|
14748
|
+
sql2 as sql,
|
|
14749
|
+
sqlProjection,
|
|
14750
|
+
summarizePlan,
|
|
14751
|
+
toClientName,
|
|
14752
|
+
toSqlExecutor,
|
|
14753
|
+
updateWithNested,
|
|
14754
|
+
validateWriteData
|
|
14522
14755
|
};
|
|
14523
14756
|
|
|
14524
|
-
//# debugId=
|
|
14757
|
+
//# debugId=28EC8E14B301CBEB64756E2164756E21
|