@vibeorm/schema 2.0.0 → 2.1.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/index.js CHANGED
@@ -16,14 +16,75 @@ function providerToDialect(params) {
16
16
  function isEnumType(fieldType) {
17
17
  return typeof fieldType === "object" && "enum" in fieldType;
18
18
  }
19
+ function fieldTypeIdentity(params) {
20
+ const { type } = params;
21
+ return isEnumType(type) ? `enum:${type.enum}` : type;
22
+ }
19
23
  var ENUM_STORAGES = ["database", "string"];
20
24
  function enumStorageOf(params) {
21
25
  return params.enumDef.storage ?? "database";
22
26
  }
27
+ // packages/schema/src/predicate.ts
28
+ var PREDICATE_COMPARISON_OPS = [
29
+ "eq",
30
+ "ne",
31
+ "gt",
32
+ "gte",
33
+ "lt",
34
+ "lte"
35
+ ];
36
+ function isPredicateFieldRef(operand) {
37
+ return operand.kind === "field";
38
+ }
39
+ function predicateFieldNames(params) {
40
+ const seen = new Set;
41
+ const walk = (node) => {
42
+ switch (node.kind) {
43
+ case "compare":
44
+ seen.add(node.left.name);
45
+ if (isPredicateFieldRef(node.right))
46
+ seen.add(node.right.name);
47
+ return;
48
+ case "inList":
49
+ case "isNull":
50
+ seen.add(node.field.name);
51
+ return;
52
+ case "and":
53
+ case "or":
54
+ for (const operand of node.operands)
55
+ walk(operand);
56
+ return;
57
+ case "not":
58
+ walk(node.operand);
59
+ return;
60
+ }
61
+ };
62
+ walk(params.predicate);
63
+ return [...seen];
64
+ }
65
+ function predicateKey(params) {
66
+ const encode = (node) => {
67
+ switch (node.kind) {
68
+ case "compare":
69
+ return `cmp(${node.op},${node.left.name},${isPredicateFieldRef(node.right) ? `f:${node.right.name}` : `v:${typeof node.right.value}:${String(node.right.value)}`})`;
70
+ case "inList":
71
+ return `in${node.negate === true ? "!" : ""}(${node.field.name},[${node.values.map((entry) => `${typeof entry.value}:${String(entry.value)}`).join("|")}])`;
72
+ case "isNull":
73
+ return `null${node.negate === true ? "!" : ""}(${node.field.name})`;
74
+ case "and":
75
+ case "or":
76
+ return `${node.kind}(${node.operands.map(encode).join(",")})`;
77
+ case "not":
78
+ return `not(${encode(node.operand)})`;
79
+ }
80
+ };
81
+ return encode(params.predicate);
82
+ }
23
83
  // packages/schema/src/errors.ts
24
84
  var VIBE_ERROR_CODES = [
25
85
  "VIBE_NOT_FOUND",
26
86
  "VIBE_UNIQUE_VIOLATION",
87
+ "VIBE_EXCLUSION_VIOLATION",
27
88
  "VIBE_FK_VIOLATION",
28
89
  "VIBE_VALIDATION",
29
90
  "VIBE_DATA_INVALID",
@@ -33,7 +94,9 @@ var VIBE_ERROR_CODES = [
33
94
  "VIBE_CONFIG",
34
95
  "VIBE_MIGRATION",
35
96
  "VIBE_TRANSACTION",
97
+ "VIBE_LOCK_NOT_AVAILABLE",
36
98
  "VIBE_SCOPED",
99
+ "VIBE_READ_ONLY",
37
100
  "VIBE_RLS_CONTEXT",
38
101
  "VIBE_PERMISSION_DENIED",
39
102
  "VIBE_ADAPTER",
@@ -137,6 +200,100 @@ class ScopedQueryError extends VibeError {
137
200
  this.reason = params.reason;
138
201
  }
139
202
  }
203
+ var READ_ONLY_REFUSAL_REASONS = [
204
+ "write",
205
+ "raw",
206
+ "control",
207
+ "unclassified",
208
+ "foreign_operation"
209
+ ];
210
+
211
+ class ReadOnlyClientError extends VibeError {
212
+ path;
213
+ reason;
214
+ constructor(params) {
215
+ super({
216
+ code: "VIBE_READ_ONLY",
217
+ message: params.message,
218
+ meta: { ...params.meta, path: params.path, reason: params.reason }
219
+ });
220
+ this.name = "ReadOnlyClientError";
221
+ this.path = params.path;
222
+ this.reason = params.reason;
223
+ }
224
+ }
225
+
226
+ // packages/schema/src/migration-objects.ts
227
+ function sqlObjectKey(params) {
228
+ const object = params.identity;
229
+ switch (object.kind) {
230
+ case "table":
231
+ case "view":
232
+ case "sequence":
233
+ case "postgresExtension":
234
+ return JSON.stringify([object.kind, object.name.namespace, object.name.name]);
235
+ case "function":
236
+ return JSON.stringify([object.kind, object.name.namespace, object.name.name, object.argumentTypes.map((type) => [type.namespace, type.name])]);
237
+ case "index":
238
+ case "trigger":
239
+ return JSON.stringify([object.kind, object.table.namespace, object.table.name, object.name]);
240
+ case "constraint":
241
+ return JSON.stringify([object.kind, object.table.namespace, object.table.name, object.name, object.constraintType]);
242
+ case "column":
243
+ case "columnDefault":
244
+ case "generatedColumn":
245
+ return JSON.stringify([object.kind, object.table.namespace, object.table.name, object.column]);
246
+ }
247
+ }
248
+ function record(value) {
249
+ return value !== null && typeof value === "object" && !Array.isArray(value);
250
+ }
251
+ function name(value) {
252
+ return record(value) && typeof value.namespace === "string" && value.namespace.length > 0 && typeof value.name === "string" && value.name.length > 0;
253
+ }
254
+ function isSqlObjectIdentity(value) {
255
+ if (!record(value))
256
+ return false;
257
+ switch (value.kind) {
258
+ case "table":
259
+ case "view":
260
+ case "sequence":
261
+ case "postgresExtension":
262
+ return name(value.name);
263
+ case "function":
264
+ return name(value.name) && Array.isArray(value.argumentTypes) && value.argumentTypes.every(name);
265
+ case "index":
266
+ case "trigger":
267
+ return name(value.table) && typeof value.name === "string" && value.name.length > 0;
268
+ case "constraint":
269
+ return name(value.table) && typeof value.name === "string" && value.name.length > 0 && ["check", "foreignKey", "exclusion", "unique", "primaryKey"].includes(String(value.constraintType));
270
+ case "column":
271
+ case "columnDefault":
272
+ case "generatedColumn":
273
+ return name(value.table) && typeof value.column === "string" && value.column.length > 0;
274
+ default:
275
+ return false;
276
+ }
277
+ }
278
+ function isSqlObjectDefinition(value) {
279
+ return record(value) && value.format === "catalog-v1" && isSqlObjectIdentity(value.identity) && record(value.facts) && Object.keys(value.facts).length > 0 && Object.values(value.facts).every((fact) => fact === null || typeof fact === "string" || typeof fact === "boolean" || typeof fact === "number" && Number.isFinite(fact) || Array.isArray(fact) && fact.every((entry) => typeof entry === "string")) && Array.isArray(value.dependencies) && value.dependencies.every(isSqlObjectIdentity);
280
+ }
281
+ function assertMigrationObjects(params) {
282
+ if (!Array.isArray(params.objects))
283
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "migrationObjects must be an array" });
284
+ const ids = new Set;
285
+ const owners = new Set;
286
+ for (const object of params.objects) {
287
+ if (!record(object) || typeof object.id !== "string" || object.id.length === 0 || !["postgres", "sqlite", "mysql"].includes(String(object.dialect)) || !isSqlObjectIdentity(object.identity) || !Array.isArray(object.create) || !object.create.every((sql) => typeof sql === "string") || !Array.isArray(object.drop) || !object.drop.every((sql) => typeof sql === "string") || !Array.isArray(object.dependsOn) || !object.dependsOn.every(isSqlObjectIdentity) || object.expected !== undefined && (!isSqlObjectDefinition(object.expected) || sqlObjectKey({ identity: object.expected.identity }) !== sqlObjectKey({ identity: object.identity }))) {
288
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Invalid migration-only object declaration" });
289
+ }
290
+ const key = sqlObjectKey({ identity: object.identity });
291
+ if (ids.has(object.id) || owners.has(key))
292
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate migration object identity or id", meta: { id: object.id, key } });
293
+ ids.add(object.id);
294
+ owners.add(key);
295
+ }
296
+ }
140
297
  // packages/schema/src/prisma-literal.ts
141
298
  var STRING_ESCAPES = {
142
299
  '"': '"',
@@ -255,6 +412,8 @@ var RESERVED_CLIENT_NAMES = [
255
412
  "$connect",
256
413
  "$disconnect",
257
414
  "$transaction",
415
+ "$querySql",
416
+ "$executeSql",
258
417
  "$queryRaw",
259
418
  "$queryRawUnsafe",
260
419
  "$executeRaw",
@@ -264,7 +423,8 @@ var RESERVED_CLIENT_NAMES = [
264
423
  "$defineView",
265
424
  "$withPolicy",
266
425
  "$bypassPolicy",
267
- "$scoped"
426
+ "$scoped",
427
+ "$readOnly"
268
428
  ];
269
429
  var RESERVED_COLUMN_PREFIX = "__vibe_";
270
430
  function isReservedDelegateName(params) {
@@ -302,26 +462,26 @@ function extensionManifestEntry(params) {
302
462
  }
303
463
  var EXTENSION_NAME_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
304
464
  function defineExtension(extension) {
305
- const { name } = extension;
306
- if (!EXTENSION_NAME_PATTERN.test(name)) {
465
+ const { name: name2 } = extension;
466
+ if (!EXTENSION_NAME_PATTERN.test(name2)) {
307
467
  throw new VibeError({
308
468
  code: "VIBE_CONFIG",
309
- message: `extension name "${name}" is invalid \u2014 must match ${EXTENSION_NAME_PATTERN} (it becomes a client identifier)`,
310
- meta: { name }
469
+ message: `extension name "${name2}" is invalid \u2014 must match ${EXTENSION_NAME_PATTERN} (it becomes a client identifier)`,
470
+ meta: { name: name2 }
311
471
  });
312
472
  }
313
- if (RESERVED_DELEGATE_NAMES.includes(name)) {
473
+ if (RESERVED_DELEGATE_NAMES.includes(name2)) {
314
474
  throw new VibeError({
315
475
  code: "VIBE_CONFIG",
316
- message: `extension name "${name}" collides with a core delegate method`,
317
- meta: { name }
476
+ message: `extension name "${name2}" collides with a core delegate method`,
477
+ meta: { name: name2 }
318
478
  });
319
479
  }
320
- if (RESERVED_CLIENT_NAMES.includes(`$${name}`)) {
480
+ if (RESERVED_CLIENT_NAMES.includes(`$${name2}`)) {
321
481
  throw new VibeError({
322
482
  code: "VIBE_CONFIG",
323
- message: `extension name "${name}" collides with the core client method "$${name}"`,
324
- meta: { name }
483
+ message: `extension name "${name2}" collides with the core client method "$${name2}"`,
484
+ meta: { name: name2 }
325
485
  });
326
486
  }
327
487
  return Object.freeze(extension);
@@ -545,6 +705,9 @@ function scalarListDefault(params) {
545
705
 
546
706
  // packages/schema/src/capabilities.ts
547
707
  var POSTGRES_CAPABILITIES = Object.freeze({
708
+ exclusionConstraints: true,
709
+ exclusionExtensions: Object.freeze(["btree_gist"]),
710
+ checkNormalization: "rollback",
548
711
  nativeRowLevelSecurity: true,
549
712
  returning: true,
550
713
  scalarArrays: true,
@@ -558,9 +721,15 @@ var POSTGRES_CAPABILITIES = Object.freeze({
558
721
  createManyAndReturn: true,
559
722
  jsonColumnType: true,
560
723
  orderByNulls: true,
561
- fullTextEngines: Object.freeze(["bm25", "tsvector"])
724
+ fullTextEngines: Object.freeze(["bm25", "tsvector"]),
725
+ rowLockStrengths: Object.freeze(["update", "noKeyUpdate", "share", "keyShare"]),
726
+ rowLockWaitPolicies: Object.freeze(["wait", "nowait", "skipLocked"]),
727
+ advisoryLocks: true
562
728
  });
563
729
  var SQLITE_CAPABILITIES = Object.freeze({
730
+ exclusionConstraints: false,
731
+ exclusionExtensions: Object.freeze([]),
732
+ checkNormalization: "sourceText",
564
733
  nativeRowLevelSecurity: false,
565
734
  returning: true,
566
735
  scalarArrays: false,
@@ -574,9 +743,15 @@ var SQLITE_CAPABILITIES = Object.freeze({
574
743
  createManyAndReturn: true,
575
744
  jsonColumnType: false,
576
745
  orderByNulls: true,
577
- fullTextEngines: Object.freeze(["fts5"])
746
+ fullTextEngines: Object.freeze(["fts5"]),
747
+ rowLockStrengths: Object.freeze([]),
748
+ rowLockWaitPolicies: Object.freeze([]),
749
+ advisoryLocks: false
578
750
  });
579
751
  var MYSQL_CAPABILITIES = Object.freeze({
752
+ exclusionConstraints: false,
753
+ exclusionExtensions: Object.freeze([]),
754
+ checkNormalization: "temporary",
580
755
  nativeRowLevelSecurity: false,
581
756
  returning: false,
582
757
  scalarArrays: false,
@@ -590,7 +765,10 @@ var MYSQL_CAPABILITIES = Object.freeze({
590
765
  createManyAndReturn: false,
591
766
  jsonColumnType: true,
592
767
  orderByNulls: false,
593
- fullTextEngines: Object.freeze(["fulltext"])
768
+ fullTextEngines: Object.freeze(["fulltext"]),
769
+ rowLockStrengths: Object.freeze(["update", "share"]),
770
+ rowLockWaitPolicies: Object.freeze(["wait", "nowait", "skipLocked"]),
771
+ advisoryLocks: false
594
772
  });
595
773
  var DIALECT_CAPABILITIES = Object.freeze({
596
774
  postgres: POSTGRES_CAPABILITIES,
@@ -600,6 +778,15 @@ var DIALECT_CAPABILITIES = Object.freeze({
600
778
 
601
779
  // packages/schema/src/validate.ts
602
780
  var RESERVED_ENUM_NAMES = [
781
+ "sql",
782
+ "decode",
783
+ "sqlProjection",
784
+ "sqlModels",
785
+ "createSqlModel",
786
+ "SqlColumn",
787
+ "SqlFragment",
788
+ "ResultDecoder",
789
+ "ExtendedDateTime",
603
790
  "enumValidators",
604
791
  "enumValidator",
605
792
  "OmittedKeys",
@@ -641,6 +828,7 @@ var RESERVED_ENUM_NAMES = [
641
828
  "SortOrderWithNulls",
642
829
  "KeysetToken",
643
830
  "KeysetOptions",
831
+ "RowLockRequest",
644
832
  "JsonValue",
645
833
  "InputJsonValue",
646
834
  "InputJsonArray",
@@ -653,6 +841,9 @@ var RESERVED_ENUM_NAMES = [
653
841
  "ScopedClientInstance",
654
842
  "ScopedModelsConfig",
655
843
  "ScopedNestedMode",
844
+ "VibeTransactionClientInstance",
845
+ "ScopedTransactionClientInstance",
846
+ "ReadOnlyClientInstance",
656
847
  "ViewClient",
657
848
  "ViewDefinitionInput",
658
849
  "ViewRow",
@@ -662,6 +853,8 @@ var RESERVED_ENUM_NAMES = [
662
853
  "QueryEvent",
663
854
  "Skip",
664
855
  "TransactionOptions",
856
+ "AdvisoryLockOptions",
857
+ "StatementDiagnosticsOptions",
665
858
  "SchemaIR",
666
859
  "VibeExtension",
667
860
  "ComputedConfig",
@@ -695,6 +888,9 @@ var GENERATED_TYPE_NAMES = {
695
888
  "WhereInput",
696
889
  "WhereUniqueInput",
697
890
  "OrderByInput",
891
+ "ScalarOrderByInput",
892
+ "RelationOrderByInput",
893
+ "RelationFindManyArgs",
698
894
  "CreateInput",
699
895
  "UncheckedCreateInput",
700
896
  "CreateManyInput",
@@ -750,7 +946,8 @@ var GENERATED_TYPE_NAMES = {
750
946
  "ViewRelationResult",
751
947
  "ViewIncludeResult",
752
948
  "ViewResult",
753
- "ViewDelegate"
949
+ "ViewDelegate",
950
+ "ReadOnlyDelegate"
754
951
  ],
755
952
  nestedPrefixes: [
756
953
  "Create",
@@ -769,10 +966,11 @@ var GENERATED_TYPE_NAMES = {
769
966
  };
770
967
  function validateGeneratedNames(params) {
771
968
  const { schema, issues } = params;
772
- const owners = new Map(RESERVED_ENUM_NAMES.map((name) => [name, "generated client"]));
969
+ const owners = new Map(RESERVED_ENUM_NAMES.map((name2) => [name2, "generated client"]));
773
970
  if (schema.rls !== undefined) {
774
971
  owners.set("RlsContext", "native RLS client");
775
972
  owners.set("BoundVibeClientInstance", "native RLS client");
973
+ owners.set("BoundVibeTransactionClientInstance", "native RLS client");
776
974
  }
777
975
  const register = (params2) => {
778
976
  const previous = owners.get(params2.name);
@@ -1157,6 +1355,15 @@ function validateModel(params) {
1157
1355
  });
1158
1356
  }
1159
1357
  checkNames.add(check.name);
1358
+ if (check.predicate !== undefined) {
1359
+ validatePredicateFields({
1360
+ predicate: check.predicate,
1361
+ model,
1362
+ fieldNames,
1363
+ site: `CHECK "${check.name}"`,
1364
+ issues
1365
+ });
1366
+ }
1160
1367
  }
1161
1368
  for (const index of model.indexes) {
1162
1369
  for (const indexField of index.fields) {
@@ -1169,10 +1376,66 @@ function validateModel(params) {
1169
1376
  });
1170
1377
  }
1171
1378
  }
1379
+ if (index.wherePredicate !== undefined) {
1380
+ validatePredicateFields({
1381
+ predicate: index.wherePredicate,
1382
+ model,
1383
+ fieldNames,
1384
+ site: `partial index on [${index.fields.map((entry) => entry.name).join(", ")}]`,
1385
+ issues
1386
+ });
1387
+ }
1388
+ }
1389
+ }
1390
+ function validatePredicateFields(params) {
1391
+ const { predicate, model, fieldNames, site, issues } = params;
1392
+ for (const name2 of predicateFieldNames({ predicate })) {
1393
+ if (!fieldNames.has(name2)) {
1394
+ issues.push({
1395
+ rule: "predicate-unknown-field",
1396
+ message: `${site} on "${model.name}" references unknown field "${name2}"`,
1397
+ model: model.name,
1398
+ field: name2
1399
+ });
1400
+ continue;
1401
+ }
1402
+ if (model.fields.find((field) => field.name === name2)?.isList === true) {
1403
+ issues.push({
1404
+ rule: "predicate-list-field",
1405
+ message: `${site} on "${model.name}" compares scalar list "${name2}" \u2014 use a raw expression for array operators`,
1406
+ model: model.name,
1407
+ field: name2
1408
+ });
1409
+ }
1172
1410
  }
1173
1411
  }
1174
1412
  function assertValidSchemaIR(params) {
1175
1413
  const dialect = providerToDialect({ provider: params.schema.datasource.provider });
1414
+ for (const model of params.schema.models) {
1415
+ if (model.exclusions !== undefined && !Array.isArray(model.exclusions))
1416
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Exclusions must be an array", meta: { rule: "exclusion-definition" } });
1417
+ if ((model.exclusions?.length ?? 0) === 0)
1418
+ continue;
1419
+ if (!DIALECT_CAPABILITIES[dialect].exclusionConstraints)
1420
+ throw unsupportedCapability({ dialect, feature: "exclusionConstraints" });
1421
+ const names = new Set;
1422
+ const exclusions = model.exclusions ?? [];
1423
+ for (const exclusion of exclusions) {
1424
+ if (exclusion === null || typeof exclusion !== "object")
1425
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Invalid exclusion declaration", meta: { rule: "exclusion-definition" } });
1426
+ const source = exclusion.source;
1427
+ if (source !== undefined) {
1428
+ const references = [...source.keys.flatMap((key) => ("field" in key) ? [key.field] : key.fields), ...[source.range.lower, source.range.upper].flatMap((endpoint) => ("field" in endpoint) ? [endpoint.field] : []), ...source.where === undefined ? [] : predicateFieldNames({ predicate: source.where })];
1429
+ if (references.some((name2) => !model.fields.some((field) => field.name === name2 && !field.isList)))
1430
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Exclusion references an unknown or list field", meta: { rule: "exclusion-field", model: model.name } });
1431
+ }
1432
+ if (!exclusion.name || names.has(exclusion.name) || typeof exclusion.definition !== "string" || !exclusion.definition.startsWith("EXCLUDE USING ") || !Array.isArray(exclusion.requiredExtensions) || exclusion.requiredExtensions.some((name2) => name2 !== "btree_gist"))
1433
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Invalid exclusion declaration", meta: { rule: "exclusion-definition", model: model.name } });
1434
+ names.add(exclusion.name);
1435
+ }
1436
+ }
1437
+ if (params.schema.migrationObjects !== undefined)
1438
+ assertMigrationObjects({ objects: params.schema.migrationObjects });
1176
1439
  if (params.schema.rls !== undefined && !DIALECT_CAPABILITIES[dialect].nativeRowLevelSecurity) {
1177
1440
  throw unsupportedCapability({ dialect, feature: "nativeRowLevelSecurity" });
1178
1441
  }
@@ -1188,8 +1451,8 @@ function assertValidSchemaIR(params) {
1188
1451
  throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: `List default on non-list field ${model.name}.${field.name}` });
1189
1452
  scalarListDefault({ type: field.type, values: field.default.values });
1190
1453
  if (isEnumType(field.type)) {
1191
- const name = field.type.enum;
1192
- const members = params.schema.enums.find((entry) => entry.name === name)?.values ?? [];
1454
+ const name2 = field.type.enum;
1455
+ const members = params.schema.enums.find((entry) => entry.name === name2)?.values ?? [];
1193
1456
  if (field.default.values.some((value) => typeof value !== "string" || !members.includes(value)))
1194
1457
  throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: `Invalid enum-list default on ${model.name}.${field.name}` });
1195
1458
  }
@@ -1289,10 +1552,10 @@ function validateNativeRls(params) {
1289
1552
  tables.add(table);
1290
1553
  const columns = new Set;
1291
1554
  for (const field of model.fields) {
1292
- const name = field.dbName ?? field.name;
1293
- if (columns.has(name))
1294
- add({ rule: "rls-duplicate-column", message: `RLS model ${model.name} repeats physical column ${name}`, field: field.name });
1295
- columns.add(name);
1555
+ const name2 = field.dbName ?? field.name;
1556
+ if (columns.has(name2))
1557
+ add({ rule: "rls-duplicate-column", message: `RLS model ${model.name} repeats physical column ${name2}`, field: field.name });
1558
+ columns.add(name2);
1296
1559
  }
1297
1560
  if (model.policy !== undefined)
1298
1561
  add({ rule: "rls-policy-conflict", message: "Native RLS cannot combine with application-level ModelIR.policy" });
@@ -1345,7 +1608,7 @@ function validateNativeRls(params) {
1345
1608
  add({ rule: "rls-invalid-through", message: "Native RLS through requires a to-one relation with an actual, complete foreign key on this model" });
1346
1609
  continue;
1347
1610
  }
1348
- if (relation.isOptional || relation.fields?.some((name) => model.fields.find((field) => field.name === name)?.isOptional)) {
1611
+ if (relation.isOptional || relation.fields?.some((name2) => model.fields.find((field) => field.name === name2)?.isOptional)) {
1349
1612
  add({ rule: "rls-nullable-through", message: "Native RLS through requires a non-nullable parent relation and foreign key; NULL parent links cannot satisfy WITH CHECK. Make the link required or use a direct ownership rule", field: relation.name });
1350
1613
  continue;
1351
1614
  }
@@ -1377,8 +1640,8 @@ function validRlsForeignKey(params) {
1377
1640
  const refs = relation.references;
1378
1641
  if (fields === undefined || refs === undefined || fields.length === 0 || fields.length !== refs.length || new Set(fields).size !== fields.length || new Set(refs).size !== refs.length)
1379
1642
  return false;
1380
- const validFields = fields.every((name, index) => {
1381
- const field = model.fields.find((f) => f.name === name);
1643
+ const validFields = fields.every((name2, index) => {
1644
+ const field = model.fields.find((f) => f.name === name2);
1382
1645
  const ref = target.fields.find((f) => f.name === refs[index]);
1383
1646
  return field !== undefined && ref !== undefined && !field.isList && !ref.isList && !field.isUnsupported && !ref.isUnsupported && (field.type === ref.type || isEnumType(field.type) && isEnumType(ref.type) && field.type.enum === ref.type.enum);
1384
1647
  });
@@ -1387,7 +1650,7 @@ function validRlsForeignKey(params) {
1387
1650
  ...target.primaryKey === undefined ? [] : [target.primaryKey.fields],
1388
1651
  ...target.indexes.filter((index) => index.kind === "unique" && index.where === undefined).map((index) => index.fields.map((f) => f.name))
1389
1652
  ];
1390
- return validFields && uniqueSets.some((set) => set.length === refs.length && set.every((name) => refs.includes(name)));
1653
+ return validFields && uniqueSets.some((set) => set.length === refs.length && set.every((name2) => refs.includes(name2)));
1391
1654
  }
1392
1655
  // packages/schema/src/m2m.ts
1393
1656
  function implicitJoinTable(params) {
@@ -1408,17 +1671,24 @@ export {
1408
1671
  validateRlsContextIR,
1409
1672
  validateComputedConfig,
1410
1673
  unsupportedCapability,
1674
+ sqlObjectKey,
1411
1675
  serializeJsonDefault,
1412
1676
  scalarListDefault,
1413
1677
  providerToDialect,
1678
+ predicateKey,
1679
+ predicateFieldNames,
1414
1680
  mergeExtensionDeclarations,
1681
+ isSqlObjectIdentity,
1682
+ isSqlObjectDefinition,
1415
1683
  isReservedDelegateName,
1416
1684
  isReservedClientName,
1685
+ isPredicateFieldRef,
1417
1686
  isImplicitManyToManyRelation,
1418
1687
  isEnumType,
1419
1688
  invalidStoredData,
1420
1689
  internalError,
1421
1690
  implicitJoinTable,
1691
+ fieldTypeIdentity,
1422
1692
  extensionOptionsHash,
1423
1693
  extensionManifestEntry,
1424
1694
  extensionDeclarations,
@@ -1430,16 +1700,20 @@ export {
1430
1700
  buildExtensionRegistry,
1431
1701
  buildComputedManifest,
1432
1702
  assertValidSchemaIR,
1703
+ assertMigrationObjects,
1433
1704
  VibeError,
1434
1705
  VIBE_ERROR_CODES,
1435
1706
  ScopedQueryError,
1436
1707
  SQLITE_CAPABILITIES,
1437
1708
  SCOPED_REFUSAL_REASONS,
1438
1709
  RlsContextError,
1710
+ ReadOnlyClientError,
1439
1711
  RLS_CONTEXT_REASONS,
1440
1712
  RESERVED_ENUM_NAMES,
1441
1713
  RESERVED_DELEGATE_NAMES,
1442
1714
  RESERVED_CLIENT_NAMES,
1715
+ READ_ONLY_REFUSAL_REASONS,
1716
+ PREDICATE_COMPARISON_OPS,
1443
1717
  POSTGRES_CAPABILITIES,
1444
1718
  MYSQL_CAPABILITIES,
1445
1719
  ENUM_STORAGES,
@@ -1448,4 +1722,4 @@ export {
1448
1722
  DIALECTS
1449
1723
  };
1450
1724
 
1451
- //# debugId=E40659BCEBFDD98E64756E2164756E21
1725
+ //# debugId=A977A79FC940B2A264756E2164756E21