@vibeorm/schema 2.3.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,85 +1,4 @@
1
1
  // @bun
2
- // packages/schema/src/ir.ts
3
- var DIALECTS = ["postgres", "sqlite", "mysql"];
4
- function providerToDialect(params) {
5
- const { provider } = params;
6
- switch (provider) {
7
- case "postgresql":
8
- case "pglite":
9
- return "postgres";
10
- case "sqlite":
11
- return "sqlite";
12
- case "mysql":
13
- return "mysql";
14
- }
15
- }
16
- function isEnumType(fieldType) {
17
- return typeof fieldType === "object" && "enum" in fieldType;
18
- }
19
- function fieldTypeIdentity(params) {
20
- const { type } = params;
21
- return isEnumType(type) ? `enum:${type.enum}` : type;
22
- }
23
- var ENUM_STORAGES = ["database", "string"];
24
- function enumStorageOf(params) {
25
- return params.enumDef.storage ?? "database";
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
- }
83
2
  // packages/schema/src/errors.ts
84
3
  var VIBE_ERROR_CODES = [
85
4
  "VIBE_NOT_FOUND",
@@ -223,10 +142,203 @@ class ReadOnlyClientError extends VibeError {
223
142
  }
224
143
  }
225
144
 
145
+ // packages/schema/src/physical-names.ts
146
+ var IDENTIFIER_LIMITS = Object.freeze({
147
+ postgres: { max: 63, unit: "bytes" },
148
+ mysql: { max: 64, unit: "characters" },
149
+ sqlite: undefined
150
+ });
151
+ var ENCODER = new TextEncoder;
152
+ function identifierLength(params) {
153
+ const limit = IDENTIFIER_LIMITS[params.dialect];
154
+ return limit?.unit === "bytes" ? ENCODER.encode(params.name).length : [...params.name].length;
155
+ }
156
+ function cutIdentifier(params) {
157
+ let out = "";
158
+ let used = 0;
159
+ for (const char of params.name) {
160
+ const size = params.unit === "bytes" ? ENCODER.encode(char).length : 1;
161
+ if (used + size > params.length)
162
+ break;
163
+ out += char;
164
+ used += size;
165
+ }
166
+ return out;
167
+ }
168
+ function fitDefaultName(params) {
169
+ const limit = IDENTIFIER_LIMITS[params.dialect];
170
+ if (limit === undefined)
171
+ return `${params.prefix}${params.suffix}`;
172
+ const room = limit.max - identifierLength({ name: params.suffix, dialect: params.dialect });
173
+ if (identifierLength({ name: params.prefix, dialect: params.dialect }) <= room)
174
+ return `${params.prefix}${params.suffix}`;
175
+ return `${cutIdentifier({ name: params.prefix, length: room, unit: limit.unit })}${params.suffix}`;
176
+ }
177
+ function assertIdentifierFits(params) {
178
+ const limit = IDENTIFIER_LIMITS[params.dialect];
179
+ if (limit === undefined)
180
+ return;
181
+ const length = identifierLength({ name: params.name, dialect: params.dialect });
182
+ if (length <= limit.max)
183
+ return;
184
+ throw new VibeError({
185
+ code: "VIBE_SCHEMA_INVALID",
186
+ message: `${params.what} "${params.name}" is ${length} ${limit.unit} \u2014 ${params.dialect} keeps at most ${limit.max}; the created object would never carry this name and migrations could not converge. ${params.remedy}`,
187
+ meta: { name: params.name, dialect: params.dialect, length, max: limit.max }
188
+ });
189
+ }
190
+ function defaultIndexName(params) {
191
+ return fitDefaultName({
192
+ prefix: `${params.table}_${params.columns.join("_")}`,
193
+ suffix: params.unique ? "_key" : "_idx",
194
+ dialect: params.dialect
195
+ });
196
+ }
197
+ function defaultForeignKeyName(params) {
198
+ return fitDefaultName({ prefix: `${params.table}_${params.columns.join("_")}`, suffix: "_fkey", dialect: params.dialect });
199
+ }
200
+ function defaultPrimaryKeyName(params) {
201
+ return fitDefaultName({ prefix: params.table, suffix: "_pkey", dialect: params.dialect });
202
+ }
203
+ function implicitJoinTableObjectNames(params) {
204
+ return {
205
+ uniqueIndex: `${params.table}_AB_unique`,
206
+ indexB: `${params.table}_B_index`,
207
+ foreignKeyA: `${params.table}_A_fkey`,
208
+ foreignKeyB: `${params.table}_B_fkey`
209
+ };
210
+ }
211
+ function postgresGeneratedObjectName(params) {
212
+ const limit = IDENTIFIER_LIMITS.postgres;
213
+ const overhead = params.label.length + 2;
214
+ let chars1 = [...params.name1];
215
+ let chars2 = [...params.name2];
216
+ const bytes = (chars) => ENCODER.encode(chars.join("")).length;
217
+ while (bytes(chars1) + bytes(chars2) + overhead > limit.max) {
218
+ if (bytes(chars1) > bytes(chars2))
219
+ chars1 = chars1.slice(0, -1);
220
+ else
221
+ chars2 = chars2.slice(0, -1);
222
+ }
223
+ return `${chars1.join("")}_${chars2.join("")}_${params.label}`;
224
+ }
225
+
226
+ // packages/schema/src/namespaces.ts
227
+ var SQL_NAMESPACES = Object.freeze({
228
+ postgres: "public",
229
+ sqlite: "main",
230
+ private: Object.freeze(["_vibeorm_shadow", "_vibe_shadow"])
231
+ });
232
+ function sqlNamespaceIssue(params) {
233
+ const { namespace } = params;
234
+ if (typeof namespace !== "string" || namespace.trim().length === 0 || namespace.includes("\x00"))
235
+ return "Namespace must be a non-empty identifier without NUL bytes";
236
+ if (identifierLength({ name: namespace, dialect: "postgres" }) > IDENTIFIER_LIMITS.postgres.max)
237
+ return "PostgreSQL namespace exceeds its identifier byte limit";
238
+ if (namespace === "information_schema" || namespace.startsWith("pg_") || SQL_NAMESPACES.private.includes(namespace))
239
+ return "System and private VibeORM namespaces cannot be declared";
240
+ if (params.allowPublic === false && namespace === SQL_NAMESPACES.postgres)
241
+ return "A fixed package namespace must be non-public; omit namespace for a legacy public artifact";
242
+ return;
243
+ }
244
+ function assertSqlNamespace(params) {
245
+ if (params.dialect !== "postgres")
246
+ throw unsupportedCapability({ dialect: params.dialect, feature: "fixedNamespaces" });
247
+ const issue = sqlNamespaceIssue(params);
248
+ if (issue !== undefined)
249
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: issue, meta: { rule: "namespace", namespace: params.namespace } });
250
+ }
251
+ function qualifiedSqlName(params) {
252
+ return { namespace: params.namespace ?? params.defaultNamespace ?? SQL_NAMESPACES.postgres, name: params.name };
253
+ }
254
+ // packages/schema/src/capabilities.ts
255
+ var POSTGRES_CAPABILITIES = Object.freeze({
256
+ fixedNamespaces: true,
257
+ externalObjects: true,
258
+ indexOperatorClasses: true,
259
+ exclusionConstraints: true,
260
+ exclusionExtensions: Object.freeze(["btree_gist"]),
261
+ checkNormalization: "rollback",
262
+ nativeRowLevelSecurity: true,
263
+ returning: true,
264
+ scalarArrays: true,
265
+ nativeEnums: true,
266
+ ilike: true,
267
+ lateralJoin: true,
268
+ distinctOn: true,
269
+ partialIndexes: true,
270
+ conflictTargetConstraint: true,
271
+ transactionalDdl: true,
272
+ createManyAndReturn: true,
273
+ jsonColumnType: true,
274
+ orderByNulls: true,
275
+ fullTextEngines: Object.freeze(["bm25", "tsvector"]),
276
+ rowLockStrengths: Object.freeze(["update", "noKeyUpdate", "share", "keyShare"]),
277
+ rowLockWaitPolicies: Object.freeze(["wait", "nowait", "skipLocked"]),
278
+ advisoryLocks: true
279
+ });
280
+ var SQLITE_CAPABILITIES = Object.freeze({
281
+ fixedNamespaces: false,
282
+ externalObjects: false,
283
+ indexOperatorClasses: false,
284
+ exclusionConstraints: false,
285
+ exclusionExtensions: Object.freeze([]),
286
+ checkNormalization: "sourceText",
287
+ nativeRowLevelSecurity: false,
288
+ returning: true,
289
+ scalarArrays: false,
290
+ nativeEnums: false,
291
+ ilike: false,
292
+ lateralJoin: false,
293
+ distinctOn: false,
294
+ partialIndexes: true,
295
+ conflictTargetConstraint: false,
296
+ transactionalDdl: true,
297
+ createManyAndReturn: true,
298
+ jsonColumnType: false,
299
+ orderByNulls: true,
300
+ fullTextEngines: Object.freeze(["fts5"]),
301
+ rowLockStrengths: Object.freeze([]),
302
+ rowLockWaitPolicies: Object.freeze([]),
303
+ advisoryLocks: false
304
+ });
305
+ var MYSQL_CAPABILITIES = Object.freeze({
306
+ fixedNamespaces: false,
307
+ externalObjects: false,
308
+ indexOperatorClasses: false,
309
+ exclusionConstraints: false,
310
+ exclusionExtensions: Object.freeze([]),
311
+ checkNormalization: "temporary",
312
+ nativeRowLevelSecurity: false,
313
+ returning: false,
314
+ scalarArrays: false,
315
+ nativeEnums: true,
316
+ ilike: false,
317
+ lateralJoin: false,
318
+ distinctOn: false,
319
+ partialIndexes: false,
320
+ conflictTargetConstraint: false,
321
+ transactionalDdl: false,
322
+ createManyAndReturn: false,
323
+ jsonColumnType: true,
324
+ orderByNulls: false,
325
+ fullTextEngines: Object.freeze(["fulltext"]),
326
+ rowLockStrengths: Object.freeze(["update", "share"]),
327
+ rowLockWaitPolicies: Object.freeze(["wait", "nowait", "skipLocked"]),
328
+ advisoryLocks: false
329
+ });
330
+ var DIALECT_CAPABILITIES = Object.freeze({
331
+ postgres: POSTGRES_CAPABILITIES,
332
+ sqlite: SQLITE_CAPABILITIES,
333
+ mysql: MYSQL_CAPABILITIES
334
+ });
335
+
226
336
  // packages/schema/src/migration-objects.ts
227
337
  function sqlObjectKey(params) {
228
338
  const object = params.identity;
229
339
  switch (object.kind) {
340
+ case "namespace":
341
+ return JSON.stringify([object.kind, object.name]);
230
342
  case "table":
231
343
  case "view":
232
344
  case "sequence":
@@ -246,6 +358,12 @@ function sqlObjectKey(params) {
246
358
  return JSON.stringify([object.kind, object.table.namespace, object.table.name, object.column]);
247
359
  }
248
360
  }
361
+ function sqlObjectNamespace(params) {
362
+ const identity = params.identity;
363
+ if (identity.kind === "namespace")
364
+ return identity.name;
365
+ return "table" in identity ? identity.table.namespace : identity.name.namespace;
366
+ }
249
367
  function record(value) {
250
368
  return value !== null && typeof value === "object" && !Array.isArray(value);
251
369
  }
@@ -256,6 +374,8 @@ function isSqlObjectIdentity(value) {
256
374
  if (!record(value))
257
375
  return false;
258
376
  switch (value.kind) {
377
+ case "namespace":
378
+ return typeof value.name === "string" && value.name.length > 0;
259
379
  case "table":
260
380
  case "view":
261
381
  case "sequence":
@@ -280,22 +400,255 @@ function isSqlObjectIdentity(value) {
280
400
  function isSqlObjectDefinition(value) {
281
401
  return record(value) && (value.format === "catalog-v1" || value.format === "catalog-v2") && 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);
282
402
  }
403
+ function qualifiedNameIssue(params) {
404
+ if (!record(params.value))
405
+ return { path: params.path, expected: "qualified SQL name { namespace, name }" };
406
+ const member = typeof params.value.namespace !== "string" || params.value.namespace.length === 0 ? "namespace" : "name";
407
+ return { path: `${params.path}.${member}`, expected: "non-empty string" };
408
+ }
409
+ function identityIssue(params) {
410
+ const { value, path } = params;
411
+ if (!record(value))
412
+ return { path, expected: "SQL object identity" };
413
+ switch (value.kind) {
414
+ case "namespace":
415
+ return { path: `${path}.name`, expected: "non-empty namespace string" };
416
+ case "table":
417
+ case "view":
418
+ case "sequence":
419
+ case "postgresExtension":
420
+ case "enum":
421
+ return qualifiedNameIssue({ value: value.name, path: `${path}.name` });
422
+ case "function": {
423
+ if (!name(value.name))
424
+ return qualifiedNameIssue({ value: value.name, path: `${path}.name` });
425
+ if (!Array.isArray(value.argumentTypes))
426
+ return { path: `${path}.argumentTypes`, expected: "array of qualified SQL type names" };
427
+ const index = value.argumentTypes.findIndex((entry) => !name(entry));
428
+ return qualifiedNameIssue({ value: value.argumentTypes[index], path: `${path}.argumentTypes[${index}]` });
429
+ }
430
+ case "index":
431
+ case "trigger":
432
+ case "constraint":
433
+ case "column":
434
+ case "columnDefault":
435
+ case "generatedColumn": {
436
+ if (!name(value.table))
437
+ return qualifiedNameIssue({ value: value.table, path: `${path}.table` });
438
+ if (value.kind === "column" || value.kind === "columnDefault" || value.kind === "generatedColumn")
439
+ return { path: `${path}.column`, expected: "non-empty column name" };
440
+ if (typeof value.name !== "string" || value.name.length === 0)
441
+ return { path: `${path}.name`, expected: "non-empty object name" };
442
+ return { path: `${path}.constraintType`, expected: ["check", "foreignKey", "exclusion", "unique", "primaryKey"] };
443
+ }
444
+ default:
445
+ return { path: `${path}.kind`, expected: ["namespace", "table", "view", "sequence", "postgresExtension", "enum", "function", "index", "trigger", "constraint", "column", "columnDefault", "generatedColumn"] };
446
+ }
447
+ }
448
+ function definitionIssue(params) {
449
+ const { value, path } = params;
450
+ if (!record(value))
451
+ return { path, expected: "independent SQL object definition" };
452
+ if (value.format !== "catalog-v1" && value.format !== "catalog-v2")
453
+ return { path: `${path}.format`, expected: ["catalog-v1", "catalog-v2"] };
454
+ if (!isSqlObjectIdentity(value.identity))
455
+ return identityIssue({ value: value.identity, path: `${path}.identity` });
456
+ if (!record(value.facts) || Object.keys(value.facts).length === 0)
457
+ return { path: `${path}.facts`, expected: "non-empty serializable catalog facts" };
458
+ for (const [key, fact] of Object.entries(value.facts)) {
459
+ if (!(fact === null || typeof fact === "string" || typeof fact === "boolean" || typeof fact === "number" && Number.isFinite(fact) || Array.isArray(fact) && fact.every((entry) => typeof entry === "string")))
460
+ return { path: `${path}.facts.${key}`, expected: "string, finite number, boolean, null or string array" };
461
+ }
462
+ if (!Array.isArray(value.dependencies))
463
+ return { path: `${path}.dependencies`, expected: "array of SQL object identities" };
464
+ const index = value.dependencies.findIndex((entry) => !isSqlObjectIdentity(entry));
465
+ return identityIssue({ value: value.dependencies[index], path: `${path}.dependencies[${index}]` });
466
+ }
467
+ function invalidDeclaration(params) {
468
+ const expected = typeof params.expected === "string" ? params.expected : params.expected.join(", ");
469
+ throw new VibeError({
470
+ code: "VIBE_SCHEMA_INVALID",
471
+ message: `Invalid migration-only object declaration at ${params.path}; expected ${expected}`,
472
+ meta: { path: params.path, expected: params.expected, ...params.id === undefined ? {} : { id: params.id } }
473
+ });
474
+ }
283
475
  function assertMigrationObjects(params) {
284
476
  if (!Array.isArray(params.objects))
285
- throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "migrationObjects must be an array" });
477
+ invalidDeclaration({ path: "migrationObjects", expected: "array" });
286
478
  const ids = new Set;
287
479
  const owners = new Set;
288
- for (const object of params.objects) {
289
- 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 }))) {
290
- throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Invalid migration-only object declaration" });
291
- }
292
- const key = sqlObjectKey({ identity: object.identity });
293
- if (ids.has(object.id) || owners.has(key))
294
- throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate migration object identity or id", meta: { id: object.id, key } });
295
- ids.add(object.id);
480
+ for (const [index, value] of params.objects.entries()) {
481
+ const object = value;
482
+ const path = `migrationObjects[${index}]`;
483
+ if (!record(object))
484
+ invalidDeclaration({ path, expected: "object" });
485
+ if (typeof object.id !== "string" || object.id.length === 0)
486
+ invalidDeclaration({ path: `${path}.id`, expected: "non-empty string" });
487
+ const id = object.id;
488
+ if (!["postgres", "sqlite", "mysql"].includes(String(object.dialect)))
489
+ invalidDeclaration({ path: `${path}.dialect`, expected: ["postgres", "sqlite", "mysql"], id });
490
+ const identity = object.identity;
491
+ if (!isSqlObjectIdentity(identity))
492
+ invalidDeclaration({ ...identityIssue({ value: identity, path: `${path}.identity` }), id });
493
+ for (const member of ["create", "drop"]) {
494
+ const statements = object[member];
495
+ if (!Array.isArray(statements))
496
+ invalidDeclaration({ path: `${path}.${member}`, expected: "array of SQL strings", id });
497
+ const invalid = statements.findIndex((entry) => typeof entry !== "string");
498
+ if (invalid !== -1)
499
+ invalidDeclaration({ path: `${path}.${member}[${invalid}]`, expected: "SQL string", id });
500
+ }
501
+ if (!Array.isArray(object.dependsOn))
502
+ invalidDeclaration({ path: `${path}.dependsOn`, expected: "array of SQL object identities", id });
503
+ for (const [dependencyIndex, dependency] of object.dependsOn.entries())
504
+ if (!isSqlObjectIdentity(dependency))
505
+ invalidDeclaration({ ...identityIssue({ value: dependency, path: `${path}.dependsOn[${dependencyIndex}]` }), id });
506
+ if (object.expected !== undefined) {
507
+ if (!isSqlObjectDefinition(object.expected))
508
+ invalidDeclaration({ ...definitionIssue({ value: object.expected, path: `${path}.expected` }), id });
509
+ if (sqlObjectKey({ identity: object.expected.identity }) !== sqlObjectKey({ identity }))
510
+ invalidDeclaration({ path: `${path}.expected.identity`, expected: "the declaration's SQL object identity", id });
511
+ }
512
+ const key = sqlObjectKey({ identity });
513
+ if (ids.has(id) || owners.has(key))
514
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Duplicate migration object identity or id", meta: { id, key, path: `${path}.${ids.has(id) ? "id" : "identity"}` } });
515
+ ids.add(id);
296
516
  owners.add(key);
297
517
  }
298
518
  }
519
+
520
+ // packages/schema/src/index-operator-classes.ts
521
+ function assertIndexOperatorClasses(params) {
522
+ for (const model of params.schema.models)
523
+ for (const index of model.indexes)
524
+ for (const field of index.fields) {
525
+ if (field.operatorClass === undefined)
526
+ continue;
527
+ if (!DIALECT_CAPABILITIES[params.dialect].indexOperatorClasses)
528
+ throw unsupportedCapability({ dialect: params.dialect, feature: "indexOperatorClasses" });
529
+ if (index.kind === "unique" && params.physical !== true)
530
+ throw unsupportedCapability({ dialect: params.dialect, feature: "uniqueIndexOperatorClasses", hint: "custom unique operator classes can change client selector equality" });
531
+ if (!isSqlObjectIdentity({ kind: "table", name: field.operatorClass }))
532
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Index operator class must be a qualified SQL name { namespace, name }", meta: { rule: "index-operator-class-identity", model: model.name, field: field.name } });
533
+ }
534
+ }
535
+ // packages/schema/src/ir.ts
536
+ var DIALECTS = ["postgres", "sqlite", "mysql"];
537
+ function providerToDialect(params) {
538
+ const { provider } = params;
539
+ switch (provider) {
540
+ case "postgresql":
541
+ case "pglite":
542
+ return "postgres";
543
+ case "sqlite":
544
+ return "sqlite";
545
+ case "mysql":
546
+ return "mysql";
547
+ }
548
+ }
549
+ function isEnumType(fieldType) {
550
+ return typeof fieldType === "object" && "enum" in fieldType;
551
+ }
552
+ function fieldTypeIdentity(params) {
553
+ const { type } = params;
554
+ return isEnumType(type) ? type.namespace === undefined || type.namespace === "public" ? `enum:${type.enum}` : JSON.stringify(["enum", type.namespace, type.enum]) : type;
555
+ }
556
+ var ENUM_STORAGES = ["database", "string"];
557
+ function enumStorageOf(params) {
558
+ return params.enumDef.storage ?? "database";
559
+ }
560
+
561
+ // packages/schema/src/module-schema.ts
562
+ function prepareDatabaseModuleSchema(params) {
563
+ const { schema, module } = params;
564
+ const namespace = module.namespace;
565
+ const dialect = providerToDialect({ provider: schema.datasource.provider });
566
+ if (namespace !== undefined)
567
+ assertSqlNamespace({ namespace, dialect, allowPublic: false });
568
+ const expected = namespace ?? SQL_NAMESPACES.postgres;
569
+ for (const entry of [...schema.models, ...schema.enums]) {
570
+ if (entry.namespace === undefined)
571
+ continue;
572
+ assertSqlNamespace({ namespace: entry.namespace, dialect });
573
+ if (qualifiedSqlName({ name: entry.dbName ?? entry.name, namespace: entry.namespace }).namespace !== expected)
574
+ throw new VibeError({ code: "VIBE_CONFIG", message: "Module schema placement differs from its immutable namespace; host configuration cannot remap it", meta: { reason: "module-placement-mismatch", moduleId: module.id, name: entry.name } });
575
+ }
576
+ for (const model of schema.models)
577
+ for (const field of model.fields) {
578
+ if (!isEnumType(field.type) || field.type.namespace === undefined)
579
+ continue;
580
+ assertSqlNamespace({ namespace: field.type.namespace, dialect });
581
+ if (field.type.namespace !== expected)
582
+ throw new VibeError({ code: "VIBE_CONFIG", message: "Module enum reference placement differs from its immutable namespace", meta: { reason: "module-placement-mismatch", moduleId: module.id, model: model.name, field: field.name } });
583
+ }
584
+ if (namespace === undefined)
585
+ return schema;
586
+ const nativeEnums = new Set(schema.enums.filter((entry) => enumStorageOf({ enumDef: entry }) === "database").map((entry) => entry.name));
587
+ return {
588
+ ...schema,
589
+ models: schema.models.map((model) => ({ ...model, namespace, fields: model.fields.map((field) => {
590
+ const type = field.type;
591
+ return isEnumType(type) && nativeEnums.has(type.enum) ? { ...field, type: { ...type, namespace } } : field;
592
+ }) })),
593
+ enums: schema.enums.map((entry) => enumStorageOf({ enumDef: entry }) === "database" ? { ...entry, namespace } : entry)
594
+ };
595
+ }
596
+ // packages/schema/src/predicate.ts
597
+ var PREDICATE_COMPARISON_OPS = [
598
+ "eq",
599
+ "ne",
600
+ "gt",
601
+ "gte",
602
+ "lt",
603
+ "lte"
604
+ ];
605
+ function isPredicateFieldRef(operand) {
606
+ return operand.kind === "field";
607
+ }
608
+ function predicateFieldNames(params) {
609
+ const seen = new Set;
610
+ const walk = (node) => {
611
+ switch (node.kind) {
612
+ case "compare":
613
+ seen.add(node.left.name);
614
+ if (isPredicateFieldRef(node.right))
615
+ seen.add(node.right.name);
616
+ return;
617
+ case "inList":
618
+ case "isNull":
619
+ seen.add(node.field.name);
620
+ return;
621
+ case "and":
622
+ case "or":
623
+ for (const operand of node.operands)
624
+ walk(operand);
625
+ return;
626
+ case "not":
627
+ walk(node.operand);
628
+ return;
629
+ }
630
+ };
631
+ walk(params.predicate);
632
+ return [...seen];
633
+ }
634
+ function predicateKey(params) {
635
+ const encode = (node) => {
636
+ switch (node.kind) {
637
+ case "compare":
638
+ return `cmp(${node.op},${node.left.name},${isPredicateFieldRef(node.right) ? `f:${node.right.name}` : `v:${typeof node.right.value}:${String(node.right.value)}`})`;
639
+ case "inList":
640
+ return `in${node.negate === true ? "!" : ""}(${node.field.name},[${node.values.map((entry) => `${typeof entry.value}:${String(entry.value)}`).join("|")}])`;
641
+ case "isNull":
642
+ return `null${node.negate === true ? "!" : ""}(${node.field.name})`;
643
+ case "and":
644
+ case "or":
645
+ return `${node.kind}(${node.operands.map(encode).join(",")})`;
646
+ case "not":
647
+ return `not(${encode(node.operand)})`;
648
+ }
649
+ };
650
+ return encode(params.predicate);
651
+ }
299
652
  // packages/schema/src/prisma-literal.ts
300
653
  var STRING_ESCAPES = {
301
654
  '"': '"',
@@ -389,9 +742,25 @@ function exactIntegerLiteral(params) {
389
742
  return asNumber;
390
743
  return BigInt(text).toString();
391
744
  }
745
+ var DECIMAL_LITERAL = /^-?\d+(\.\d+)?([eE][+-]?\d+)?$/;
746
+ function exactDecimalLiteral(params) {
747
+ const { text } = params;
748
+ if (!DECIMAL_LITERAL.test(text)) {
749
+ throw new VibeError({
750
+ code: "VIBE_SCHEMA_INVALID",
751
+ message: `Not a numeric literal: ${JSON.stringify(text)}`,
752
+ meta: { issues: [{ rule: "malformed-numeric-literal", message: text }], literal: text }
753
+ });
754
+ }
755
+ if (INTEGER_LITERAL.test(text))
756
+ return exactIntegerLiteral({ text });
757
+ const mantissa = text.replace(/[eE].*$/, "").replace("-", "").replace(".", "").replace(/^0+/, "");
758
+ return mantissa.length <= 15 ? Number(text) : text;
759
+ }
392
760
  // packages/schema/src/reserved.ts
393
761
  var RESERVED_DELEGATE_NAMES = [
394
762
  "findMany",
763
+ "findPage",
395
764
  "findFirst",
396
765
  "findUnique",
397
766
  "findFirstOrThrow",
@@ -710,78 +1079,20 @@ function scalarListDefault(params) {
710
1079
  });
711
1080
  }
712
1081
 
713
- // packages/schema/src/capabilities.ts
714
- var POSTGRES_CAPABILITIES = Object.freeze({
715
- exclusionConstraints: true,
716
- exclusionExtensions: Object.freeze(["btree_gist"]),
717
- checkNormalization: "rollback",
718
- nativeRowLevelSecurity: true,
719
- returning: true,
720
- scalarArrays: true,
721
- nativeEnums: true,
722
- ilike: true,
723
- lateralJoin: true,
724
- distinctOn: true,
725
- partialIndexes: true,
726
- conflictTargetConstraint: true,
727
- transactionalDdl: true,
728
- createManyAndReturn: true,
729
- jsonColumnType: true,
730
- orderByNulls: true,
731
- fullTextEngines: Object.freeze(["bm25", "tsvector"]),
732
- rowLockStrengths: Object.freeze(["update", "noKeyUpdate", "share", "keyShare"]),
733
- rowLockWaitPolicies: Object.freeze(["wait", "nowait", "skipLocked"]),
734
- advisoryLocks: true
735
- });
736
- var SQLITE_CAPABILITIES = Object.freeze({
737
- exclusionConstraints: false,
738
- exclusionExtensions: Object.freeze([]),
739
- checkNormalization: "sourceText",
740
- nativeRowLevelSecurity: false,
741
- returning: true,
742
- scalarArrays: false,
743
- nativeEnums: false,
744
- ilike: false,
745
- lateralJoin: false,
746
- distinctOn: false,
747
- partialIndexes: true,
748
- conflictTargetConstraint: false,
749
- transactionalDdl: true,
750
- createManyAndReturn: true,
751
- jsonColumnType: false,
752
- orderByNulls: true,
753
- fullTextEngines: Object.freeze(["fts5"]),
754
- rowLockStrengths: Object.freeze([]),
755
- rowLockWaitPolicies: Object.freeze([]),
756
- advisoryLocks: false
757
- });
758
- var MYSQL_CAPABILITIES = Object.freeze({
759
- exclusionConstraints: false,
760
- exclusionExtensions: Object.freeze([]),
761
- checkNormalization: "temporary",
762
- nativeRowLevelSecurity: false,
763
- returning: false,
764
- scalarArrays: false,
765
- nativeEnums: true,
766
- ilike: false,
767
- lateralJoin: false,
768
- distinctOn: false,
769
- partialIndexes: false,
770
- conflictTargetConstraint: false,
771
- transactionalDdl: false,
772
- createManyAndReturn: false,
773
- jsonColumnType: true,
774
- orderByNulls: false,
775
- fullTextEngines: Object.freeze(["fulltext"]),
776
- rowLockStrengths: Object.freeze(["update", "share"]),
777
- rowLockWaitPolicies: Object.freeze(["wait", "nowait", "skipLocked"]),
778
- advisoryLocks: false
779
- });
780
- var DIALECT_CAPABILITIES = Object.freeze({
781
- postgres: POSTGRES_CAPABILITIES,
782
- sqlite: SQLITE_CAPABILITIES,
783
- mysql: MYSQL_CAPABILITIES
784
- });
1082
+ // packages/schema/src/m2m.ts
1083
+ function implicitJoinTable(params) {
1084
+ const sorted = [params.modelA, params.modelB].sort();
1085
+ const first = sorted[0];
1086
+ const second = sorted[1];
1087
+ const key = params.relationName ?? `${first}To${second}`;
1088
+ return {
1089
+ tableName: `_${key}`,
1090
+ modelA: first,
1091
+ modelB: second,
1092
+ columnA: "A",
1093
+ columnB: "B"
1094
+ };
1095
+ }
785
1096
 
786
1097
  // packages/schema/src/validate.ts
787
1098
  var RESERVED_ENUM_NAMES = [
@@ -837,6 +1148,7 @@ var RESERVED_ENUM_NAMES = [
837
1148
  "SortOrder",
838
1149
  "SortOrderWithNulls",
839
1150
  "KeysetToken",
1151
+ "KeysetPage",
840
1152
  "KeysetOptions",
841
1153
  "RowLockRequest",
842
1154
  "JsonValue",
@@ -915,6 +1227,7 @@ var GENERATED_TYPE_NAMES = {
915
1227
  "MinAggregateInput",
916
1228
  "MaxAggregateInput",
917
1229
  "FindManyArgs",
1230
+ "FindPageArgs",
918
1231
  "FindFirstArgs",
919
1232
  "FindUniqueArgs",
920
1233
  "CreateArgs",
@@ -1085,6 +1398,15 @@ function validateGeneratedNames(params) {
1085
1398
  function validateSchemaIR(params) {
1086
1399
  const { schema } = params;
1087
1400
  const issues = [];
1401
+ for (const entry of [...schema.models, ...schema.enums]) {
1402
+ if (entry.namespace === undefined)
1403
+ continue;
1404
+ const issue = sqlNamespaceIssue({ namespace: entry.namespace });
1405
+ if (issue !== undefined)
1406
+ issues.push({ rule: "namespace", message: issue });
1407
+ if (providerToDialect({ provider: schema.datasource.provider }) !== "postgres")
1408
+ issues.push({ rule: "namespace-dialect", message: "Explicit fixed namespaces require PostgreSQL or PGlite" });
1409
+ }
1088
1410
  const modelByName = new Map;
1089
1411
  for (const model of schema.models) {
1090
1412
  if (modelByName.has(model.name)) {
@@ -1256,10 +1578,197 @@ function validateSchemaIR(params) {
1256
1578
  });
1257
1579
  }
1258
1580
  }
1581
+ validateIdentifierLengths({ schema, issues });
1582
+ validatePhysicalNameUniqueness({ schema, issues });
1259
1583
  validateGeneratedNames({ schema, issues });
1260
1584
  validateNativeRls({ schema, issues });
1261
1585
  return issues;
1262
1586
  }
1587
+ function validatePhysicalNameUniqueness(params) {
1588
+ const { schema, issues } = params;
1589
+ const dialect = providerToDialect({ provider: schema.datasource.provider });
1590
+ const owners = new Map;
1591
+ const claim = (entry) => {
1592
+ const key = JSON.stringify([entry.physicalNamespace ?? "public", entry.namespace, entry.name]);
1593
+ const previous = owners.get(key);
1594
+ if (previous === undefined) {
1595
+ owners.set(key, `${entry.what} of ${entry.owner}`);
1596
+ return true;
1597
+ }
1598
+ issues.push({
1599
+ rule: "duplicate-physical-name",
1600
+ message: `${entry.what} "${entry.name}" of ${entry.owner} collides with the ${previous} \u2014 both resolve to one physical ${entry.namespace === "relation" ? "object" : "name"}, and only one can exist`,
1601
+ ...entry.model === undefined ? {} : { model: entry.model },
1602
+ ...entry.field === undefined ? {} : { field: entry.field }
1603
+ });
1604
+ return false;
1605
+ };
1606
+ const indexNamespace = (table) => dialect === "mysql" ? `index:${table}` : "relation";
1607
+ const constraintNamespace = (table) => dialect === "mysql" ? "constraint" : `constraint:${table}`;
1608
+ for (const model of schema.models) {
1609
+ const table = model.dbName ?? model.name;
1610
+ const physicalNamespace = qualifiedSqlName({ name: table, namespace: model.namespace }).namespace;
1611
+ if (!claim({ namespace: "relation", physicalNamespace, name: table, what: model.isView ? "view" : "table", owner: `model "${model.name}"`, model: model.name }))
1612
+ continue;
1613
+ for (const fieldIR of model.fields) {
1614
+ claim({ namespace: `column:${table}`, physicalNamespace, name: fieldIR.dbName ?? fieldIR.name, what: "column", owner: `field "${model.name}.${fieldIR.name}"`, model: model.name, field: fieldIR.name });
1615
+ }
1616
+ if (model.isView)
1617
+ continue;
1618
+ const columnOf = (name) => model.fields.find((candidate) => candidate.name === name)?.dbName ?? name;
1619
+ const seenIndexKeys = new Set;
1620
+ const declarations = [
1621
+ ...model.indexes.map((index) => ({ index, site: index.kind === "unique" ? "@@unique" : "@@index" })),
1622
+ ...model.fields.filter((fieldIR) => fieldIR.isUnique).map((fieldIR) => ({ index: { kind: "unique", fields: [{ name: fieldIR.name }] }, site: `@unique on "${fieldIR.name}"` }))
1623
+ ];
1624
+ for (const { index, site } of declarations) {
1625
+ const unique = index.kind === "unique";
1626
+ const columns = index.fields.map((entry) => `${columnOf(entry.name)}${entry.sort === "desc" ? " desc" : ""}`);
1627
+ const key = `${unique ? "u" : "i"}${index.nullsNotDistinct === true ? "n" : ""}:${unique ? "btree" : index.kind}:${columns.join(",")}:${index.where?.trim() ?? ""}`;
1628
+ if (seenIndexKeys.has(key))
1629
+ continue;
1630
+ seenIndexKeys.add(key);
1631
+ const name = index.name ?? defaultIndexName({ table, columns: index.fields.map((entry) => columnOf(entry.name)), unique, dialect });
1632
+ claim({ namespace: indexNamespace(table), physicalNamespace, name, what: unique ? "unique index" : "index", owner: `${site} on "${model.name}"`, model: model.name });
1633
+ if (index.constraintName !== undefined) {
1634
+ claim({ namespace: constraintNamespace(table), physicalNamespace, name: index.constraintName, what: "unique constraint", owner: `${site} on "${model.name}"`, model: model.name });
1635
+ }
1636
+ }
1637
+ if (dialect === "postgres" && (model.primaryKey !== undefined || model.fields.some((fieldIR) => fieldIR.isId))) {
1638
+ const name = model.primaryKeyConstraintName ?? defaultPrimaryKeyName({ table, dialect });
1639
+ claim({ namespace: "relation", physicalNamespace, name, what: "primary key", owner: `model "${model.name}"`, model: model.name });
1640
+ claim({ namespace: constraintNamespace(table), physicalNamespace, name, what: "primary key", owner: `model "${model.name}"`, model: model.name });
1641
+ }
1642
+ for (const relation of model.relations) {
1643
+ if (relation.fields === undefined || relation.fields.length === 0)
1644
+ continue;
1645
+ const target = schema.models.find((candidate) => candidate.name === relation.targetModel);
1646
+ if (target !== undefined && target.isView)
1647
+ continue;
1648
+ const name = relation.constraintName ?? defaultForeignKeyName({ table, columns: relation.fields.map((entry) => columnOf(entry)), dialect });
1649
+ claim({ namespace: constraintNamespace(table), physicalNamespace, name, what: "foreign key", owner: `relation "${model.name}.${relation.name}"`, model: model.name });
1650
+ }
1651
+ for (const checkIR of model.checks ?? []) {
1652
+ claim({ namespace: constraintNamespace(table), physicalNamespace, name: checkIR.name, what: "CHECK constraint", owner: `model "${model.name}"`, model: model.name });
1653
+ }
1654
+ for (const exclusion of model.exclusions ?? []) {
1655
+ claim({ namespace: constraintNamespace(table), physicalNamespace, name: exclusion.name, what: "exclusion constraint", owner: `model "${model.name}"`, model: model.name });
1656
+ if (dialect === "postgres")
1657
+ claim({ namespace: "relation", physicalNamespace, name: exclusion.name, what: "exclusion constraint", owner: `model "${model.name}"`, model: model.name });
1658
+ }
1659
+ }
1660
+ if (dialect === "postgres") {
1661
+ for (const enumDef of schema.enums) {
1662
+ if (enumStorageOf({ enumDef }) !== "database")
1663
+ continue;
1664
+ claim({ namespace: "relation", physicalNamespace: qualifiedSqlName({ name: enumDef.dbName ?? enumDef.name, namespace: enumDef.namespace }).namespace, name: enumDef.dbName ?? enumDef.name, what: "enum type", owner: `enum "${enumDef.name}"` });
1665
+ }
1666
+ }
1667
+ const seenJoinTables = new Set;
1668
+ for (const model of schema.models) {
1669
+ for (const relation of model.relations) {
1670
+ if (relation.targetModel === model.name)
1671
+ continue;
1672
+ if (!isImplicitManyToManyRelation({ model, relation, models: schema.models }))
1673
+ continue;
1674
+ const target = schema.models.find((candidate) => candidate.name === relation.targetModel);
1675
+ const physicalNamespace = model.namespace ?? "public";
1676
+ if ((target?.namespace ?? "public") !== physicalNamespace) {
1677
+ issues.push({ rule: "implicit-join-namespace", message: "Implicit many-to-many endpoints must share one fixed namespace", model: model.name, field: relation.name });
1678
+ continue;
1679
+ }
1680
+ const join = implicitJoinTable({ modelA: model.name, modelB: relation.targetModel, ...relation.relationName === undefined ? {} : { relationName: relation.relationName } });
1681
+ const joinKey = JSON.stringify([physicalNamespace, join.tableName]);
1682
+ if (seenJoinTables.has(joinKey))
1683
+ continue;
1684
+ seenJoinTables.add(joinKey);
1685
+ const owner = `implicit relation "${model.name}.${relation.name}"`;
1686
+ claim({ namespace: "relation", physicalNamespace, name: join.tableName, what: "join table", owner, model: model.name });
1687
+ const derived = implicitJoinTableObjectNames({ table: join.tableName });
1688
+ claim({ namespace: indexNamespace(join.tableName), physicalNamespace, name: derived.uniqueIndex, what: "unique index", owner, model: model.name });
1689
+ claim({ namespace: indexNamespace(join.tableName), physicalNamespace, name: derived.indexB, what: "index", owner, model: model.name });
1690
+ claim({ namespace: constraintNamespace(join.tableName), physicalNamespace, name: derived.foreignKeyA, what: "foreign key", owner, model: model.name });
1691
+ claim({ namespace: constraintNamespace(join.tableName), physicalNamespace, name: derived.foreignKeyB, what: "foreign key", owner, model: model.name });
1692
+ }
1693
+ }
1694
+ }
1695
+ function validateIdentifierLengths(params) {
1696
+ const { schema, issues } = params;
1697
+ const dialect = providerToDialect({ provider: schema.datasource.provider });
1698
+ const limit = IDENTIFIER_LIMITS[dialect];
1699
+ if (limit === undefined)
1700
+ return;
1701
+ const { max, unit } = limit;
1702
+ const consequence = dialect === "postgres" ? `PostgreSQL silently truncates identifiers to ${max} bytes (NAMEDATALEN - 1), so the created object never carries this name and migrations cannot converge` : `MySQL refuses identifiers longer than ${max} characters (ER_TOO_LONG_IDENT)`;
1703
+ const check = (entry) => {
1704
+ const length = identifierLength({ name: entry.name, dialect });
1705
+ if (length <= max)
1706
+ return;
1707
+ issues.push({
1708
+ rule: "identifier-too-long",
1709
+ message: `${entry.what} "${entry.name}" is ${length} ${unit} \u2014 ${consequence}. ${entry.remedy}`,
1710
+ ...entry.model === undefined ? {} : { model: entry.model }
1711
+ });
1712
+ };
1713
+ for (const model of schema.models) {
1714
+ const table = model.dbName ?? model.name;
1715
+ check({ name: table, what: model.isView ? "View name" : "Table name", model: model.name, remedy: `Map "${model.name}" to a shorter physical name (@@map).` });
1716
+ for (const fieldIR of model.fields) {
1717
+ check({ name: fieldIR.dbName ?? fieldIR.name, what: "Column name", model: model.name, remedy: `Map "${model.name}.${fieldIR.name}" to a shorter column name (@map).` });
1718
+ }
1719
+ if (model.isView)
1720
+ continue;
1721
+ for (const index of model.indexes) {
1722
+ if (index.name !== undefined) {
1723
+ check({ name: index.name, what: index.kind === "unique" ? "Unique index name" : "Index name", model: model.name, remedy: "Shorten the index name (map:)." });
1724
+ }
1725
+ if (index.constraintName !== undefined) {
1726
+ check({ name: index.constraintName, what: "Unique constraint name", model: model.name, remedy: "Shorten the constraint name." });
1727
+ }
1728
+ }
1729
+ if (model.primaryKeyConstraintName !== undefined) {
1730
+ check({ name: model.primaryKeyConstraintName, what: "Primary key constraint name", model: model.name, remedy: "Shorten the constraint name (map:)." });
1731
+ }
1732
+ for (const relation of model.relations) {
1733
+ if (relation.constraintName === undefined)
1734
+ continue;
1735
+ check({ name: relation.constraintName, what: "Foreign key constraint name", model: model.name, remedy: "Shorten the constraint name (map:)." });
1736
+ }
1737
+ for (const checkIR of model.checks ?? []) {
1738
+ check({ name: checkIR.name, what: "CHECK constraint name", model: model.name, remedy: "Shorten the CHECK name." });
1739
+ }
1740
+ for (const exclusion of model.exclusions ?? []) {
1741
+ check({ name: exclusion.name, what: "Exclusion constraint name", model: model.name, remedy: "Shorten the exclusion name." });
1742
+ }
1743
+ }
1744
+ if (dialect === "postgres") {
1745
+ for (const enumDef of schema.enums) {
1746
+ if (enumStorageOf({ enumDef }) !== "database")
1747
+ continue;
1748
+ check({ name: enumDef.dbName ?? enumDef.name, what: "Enum type name", remedy: `Map enum "${enumDef.name}" to a shorter type name (@@map).` });
1749
+ }
1750
+ }
1751
+ const seenJoinTables = new Set;
1752
+ for (const model of schema.models) {
1753
+ for (const relation of model.relations) {
1754
+ if (relation.targetModel === model.name)
1755
+ continue;
1756
+ if (!isImplicitManyToManyRelation({ model, relation, models: schema.models }))
1757
+ continue;
1758
+ const join = implicitJoinTable({ modelA: model.name, modelB: relation.targetModel, ...relation.relationName === undefined ? {} : { relationName: relation.relationName } });
1759
+ if (seenJoinTables.has(join.tableName))
1760
+ continue;
1761
+ seenJoinTables.add(join.tableName);
1762
+ const remedy = `Give the relation an explicit shorter name (@relation("...")).`;
1763
+ check({ name: join.tableName, what: "Implicit join table name", model: model.name, remedy });
1764
+ const derived = implicitJoinTableObjectNames({ table: join.tableName });
1765
+ check({ name: derived.uniqueIndex, what: "Implicit join table index name", model: model.name, remedy });
1766
+ check({ name: derived.indexB, what: "Implicit join table index name", model: model.name, remedy });
1767
+ check({ name: derived.foreignKeyA, what: "Implicit join table foreign key name", model: model.name, remedy });
1768
+ check({ name: derived.foreignKeyB, what: "Implicit join table foreign key name", model: model.name, remedy });
1769
+ }
1770
+ }
1771
+ }
1263
1772
  function validateModel(params) {
1264
1773
  const { model, modelByName, enumByName, issues } = params;
1265
1774
  const memberNames = new Set;
@@ -1545,6 +2054,20 @@ function validatePredicateFields(params) {
1545
2054
  }
1546
2055
  function assertValidSchemaIR(params) {
1547
2056
  const dialect = providerToDialect({ provider: params.schema.datasource.provider });
2057
+ assertIndexOperatorClasses({ schema: params.schema, dialect });
2058
+ for (const entry of [...params.schema.models, ...params.schema.enums])
2059
+ if (entry.namespace !== undefined)
2060
+ assertSqlNamespace({ namespace: entry.namespace, dialect });
2061
+ for (const model of params.schema.models)
2062
+ for (const field of model.fields) {
2063
+ if (!isEnumType(field.type) || field.type.namespace === undefined)
2064
+ continue;
2065
+ assertSqlNamespace({ namespace: field.type.namespace, dialect });
2066
+ const enumType = field.type;
2067
+ const declared = params.schema.enums.find((entry) => entry.name === enumType.enum || entry.dbName === enumType.enum);
2068
+ if (declared !== undefined && (declared.namespace ?? "public") !== enumType.namespace)
2069
+ throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Enum reference namespace differs from its declared native type", meta: { rule: "enum-namespace", model: model.name, field: field.name } });
2070
+ }
1548
2071
  for (const model of params.schema.models) {
1549
2072
  if (model.exclusions !== undefined && !Array.isArray(model.exclusions))
1550
2073
  throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: "Exclusions must be an array", meta: { rule: "exclusion-definition" } });
@@ -1786,25 +2309,12 @@ function validRlsForeignKey(params) {
1786
2309
  ];
1787
2310
  return validFields && uniqueSets.some((set) => set.length === refs.length && set.every((name) => refs.includes(name)));
1788
2311
  }
1789
- // packages/schema/src/m2m.ts
1790
- function implicitJoinTable(params) {
1791
- const sorted = [params.modelA, params.modelB].sort();
1792
- const first = sorted[0];
1793
- const second = sorted[1];
1794
- const key = params.relationName ?? `${first}To${second}`;
1795
- return {
1796
- tableName: `_${key}`,
1797
- modelA: first,
1798
- modelB: second,
1799
- columnA: "A",
1800
- columnB: "B"
1801
- };
1802
- }
1803
2312
  export {
1804
2313
  DIALECTS,
1805
2314
  DIALECT_CAPABILITIES,
1806
2315
  EMPTY_EXTENSION_REGISTRY,
1807
2316
  ENUM_STORAGES,
2317
+ IDENTIFIER_LIMITS,
1808
2318
  MYSQL_CAPABILITIES,
1809
2319
  POSTGRES_CAPABILITIES,
1810
2320
  PREDICATE_COMPARISON_OPS,
@@ -1817,23 +2327,34 @@ export {
1817
2327
  RlsContextError,
1818
2328
  SCOPED_REFUSAL_REASONS,
1819
2329
  SQLITE_CAPABILITIES,
2330
+ SQL_NAMESPACES,
1820
2331
  ScopedQueryError,
1821
2332
  VIBE_ERROR_CODES,
1822
2333
  VibeError,
2334
+ assertIdentifierFits,
2335
+ assertIndexOperatorClasses,
1823
2336
  assertMigrationObjects,
2337
+ assertSqlNamespace,
1824
2338
  assertValidSchemaIR,
1825
2339
  buildComputedManifest,
1826
2340
  buildExtensionRegistry,
1827
2341
  decodePrismaString,
2342
+ defaultForeignKeyName,
2343
+ defaultIndexName,
2344
+ defaultPrimaryKeyName,
1828
2345
  defineExtension,
1829
2346
  encodePrismaString,
1830
2347
  enumStorageOf,
2348
+ exactDecimalLiteral,
1831
2349
  exactIntegerLiteral,
1832
2350
  extensionDeclarations,
1833
2351
  extensionManifestEntry,
1834
2352
  extensionOptionsHash,
1835
2353
  fieldTypeIdentity,
2354
+ fitDefaultName,
2355
+ identifierLength,
1836
2356
  implicitJoinTable,
2357
+ implicitJoinTableObjectNames,
1837
2358
  internalError,
1838
2359
  invalidStoredData,
1839
2360
  isEnumType,
@@ -1844,16 +2365,21 @@ export {
1844
2365
  isSqlObjectDefinition,
1845
2366
  isSqlObjectIdentity,
1846
2367
  mergeExtensionDeclarations,
2368
+ postgresGeneratedObjectName,
1847
2369
  predicateFieldNames,
1848
2370
  predicateKey,
2371
+ prepareDatabaseModuleSchema,
1849
2372
  providerToDialect,
2373
+ qualifiedSqlName,
1850
2374
  scalarListDefault,
1851
2375
  serializeJsonDefault,
2376
+ sqlNamespaceIssue,
1852
2377
  sqlObjectKey,
2378
+ sqlObjectNamespace,
1853
2379
  unsupportedCapability,
1854
2380
  validateComputedConfig,
1855
2381
  validateRlsContextIR,
1856
2382
  validateSchemaIR
1857
2383
  };
1858
2384
 
1859
- //# debugId=5275C43BC09D1F0064756E2164756E21
2385
+ //# debugId=20984641ACA6D36E64756E2164756E21