@vibeorm/schema 5.8.0 → 5.9.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
@@ -442,6 +442,8 @@ var POSTGRES_CAPABILITIES = Object.freeze({
442
442
  fixedNamespaces: true,
443
443
  externalObjects: true,
444
444
  indexOperatorClasses: true,
445
+ indexIncludeColumns: true,
446
+ tableStorageParameters: true,
445
447
  exclusionConstraints: true,
446
448
  exclusionExtensions: Object.freeze(["btree_gist"]),
447
449
  checkNormalization: "rollback",
@@ -474,7 +476,10 @@ var POSTGRES_CAPABILITIES = Object.freeze({
474
476
  uuidColumns: "native",
475
477
  localDateColumns: true,
476
478
  dateTruncation: true,
477
- rowCapture: true
479
+ filteredAggregates: "filterClause",
480
+ groupingSets: "groupingSets",
481
+ rowCapture: true,
482
+ returningReadsJoinedSource: true
478
483
  });
479
484
  var SQLITE_CAPABILITIES = Object.freeze({
480
485
  rowRuleAppendOnly: true,
@@ -484,6 +489,8 @@ var SQLITE_CAPABILITIES = Object.freeze({
484
489
  fixedNamespaces: false,
485
490
  externalObjects: false,
486
491
  indexOperatorClasses: false,
492
+ indexIncludeColumns: false,
493
+ tableStorageParameters: false,
487
494
  exclusionConstraints: false,
488
495
  exclusionExtensions: Object.freeze([]),
489
496
  checkNormalization: "sourceText",
@@ -516,7 +523,10 @@ var SQLITE_CAPABILITIES = Object.freeze({
516
523
  uuidColumns: "text",
517
524
  localDateColumns: true,
518
525
  dateTruncation: false,
519
- rowCapture: false
526
+ filteredAggregates: "filterClause",
527
+ groupingSets: "none",
528
+ rowCapture: false,
529
+ returningReadsJoinedSource: false
520
530
  });
521
531
  var MYSQL_CAPABILITIES = Object.freeze({
522
532
  rowRuleAppendOnly: true,
@@ -526,6 +536,8 @@ var MYSQL_CAPABILITIES = Object.freeze({
526
536
  fixedNamespaces: false,
527
537
  externalObjects: false,
528
538
  indexOperatorClasses: false,
539
+ indexIncludeColumns: false,
540
+ tableStorageParameters: false,
529
541
  exclusionConstraints: false,
530
542
  exclusionExtensions: Object.freeze([]),
531
543
  checkNormalization: "temporary",
@@ -558,7 +570,10 @@ var MYSQL_CAPABILITIES = Object.freeze({
558
570
  uuidColumns: "unsupported",
559
571
  localDateColumns: true,
560
572
  dateTruncation: false,
561
- rowCapture: false
573
+ filteredAggregates: "caseWhen",
574
+ groupingSets: "rollup",
575
+ rowCapture: false,
576
+ returningReadsJoinedSource: false
562
577
  });
563
578
  var DIALECT_CAPABILITIES = Object.freeze({
564
579
  postgres: POSTGRES_CAPABILITIES,
@@ -1070,10 +1085,12 @@ function shapeIssue(params) {
1070
1085
  const { index } = params;
1071
1086
  if (!dataOnly({ value: index }))
1072
1087
  return "Expression indexes accept JSON-safe plain data only, not accessors, callbacks or cycles";
1073
- if (!record({ value: index, required: ["kind", "fields", "expressions"], optional: ["name", "clientName", "constraintName", "where", "wherePredicate", "nullsNotDistinct"] }))
1088
+ if (!record({ value: index, required: ["kind", "fields", "expressions"], optional: ["name", "clientName", "constraintName", "where", "wherePredicate", "nullsNotDistinct", "include"] }))
1074
1089
  return "Expression indexes require canonical index data properties";
1075
1090
  if (typeof index.kind !== "string")
1076
1091
  return "Index kind must be a string";
1092
+ if (index.include !== undefined && (!Array.isArray(index.include) || index.include.some((name) => !text({ value: name }))))
1093
+ return "Expression index INCLUDE columns must be a list of field names";
1077
1094
  if (index.nullsNotDistinct !== undefined && typeof index.nullsNotDistinct !== "boolean" || index.name !== undefined && !text({ value: index.name }) || index.where !== undefined && !text({ value: index.where }))
1078
1095
  return "Expression index names, predicates and flags must have their canonical data types";
1079
1096
  const descriptor = Object.getOwnPropertyDescriptor(index, "expressions");
@@ -1139,14 +1156,15 @@ function structuralData(params) {
1139
1156
  }
1140
1157
  function indexKeyDefinitionKey(params) {
1141
1158
  const { index, model } = params;
1159
+ const include = index.include === undefined ? [] : [{ include: index.include.map((name) => model?.fields.find((entry) => entry.name === name)?.dbName ?? name) }];
1142
1160
  if (!isExpressionIndex({ index })) {
1143
1161
  const columns = index.fields.map((field) => [model?.fields.find((entry) => entry.name === field.name)?.dbName ?? field.name, field.sort === "desc", field.operatorClass === undefined ? null : [field.operatorClass.namespace, field.operatorClass.name]]);
1144
- return JSON.stringify([index.kind === "unique", index.nullsNotDistinct === true, index.kind === "unique" ? "btree" : index.kind, columns, index.where?.trim() ?? ""]);
1162
+ return JSON.stringify([index.kind === "unique", index.nullsNotDistinct === true, index.kind === "unique" ? "btree" : index.kind, columns, index.where?.trim() ?? "", ...include]);
1145
1163
  }
1146
1164
  const issue = shapeIssue({ index });
1147
1165
  if (issue !== undefined)
1148
1166
  throw invalid({ message: issue });
1149
- return JSON.stringify(["expression", index.kind, structuralData({ value: index.expressions, model }), index.nullsNotDistinct === true, index.where?.trim() ?? "", structuralData({ value: index.wherePredicate, model }) ?? null]);
1167
+ return JSON.stringify(["expression", index.kind, structuralData({ value: index.expressions, model }), index.nullsNotDistinct === true, index.where?.trim() ?? "", structuralData({ value: index.wherePredicate, model }) ?? null, ...include]);
1150
1168
  }
1151
1169
  function scalarType(params) {
1152
1170
  const { expression, model, dialect, add } = params;
@@ -2335,6 +2353,172 @@ function assertIndexOperatorClasses(params) {
2335
2353
  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 } });
2336
2354
  }
2337
2355
  }
2356
+ // packages/schema/src/index-include.ts
2357
+ var INCLUDE_INDEX_KINDS = Object.freeze(["btree", "unique", "gist", "spgist"]);
2358
+ function hasIndexIncludeColumns(params) {
2359
+ return params.schema.models.some((model) => model.indexes.some((index) => index.include !== undefined));
2360
+ }
2361
+ function assertIndexIncludeColumns(params) {
2362
+ const { schema, dialect } = params;
2363
+ for (const model of schema.models)
2364
+ for (const index of model.indexes) {
2365
+ const include = index.include;
2366
+ if (include === undefined)
2367
+ continue;
2368
+ const site = `${index.kind === "unique" ? "@@unique" : "@@index"}${index.name === undefined ? "" : ` "${index.name}"`} on "${model.name}"`;
2369
+ const invalid = (params) => new VibeError({ code: "VIBE_SCHEMA_INVALID", message: `${site}: ${params.message}`, meta: { rule: params.rule, model: model.name, ...index.name === undefined ? {} : { index: index.name }, ...params.field === undefined ? {} : { field: params.field } } });
2370
+ if (!Array.isArray(include) || include.length === 0 || include.some((name) => typeof name !== "string" || name === ""))
2371
+ throw invalid({ rule: "index-include-shape", message: "include must be a non-empty list of field names (omit it for an index without INCLUDE columns)" });
2372
+ if (!DIALECT_CAPABILITIES[dialect].indexIncludeColumns)
2373
+ throw unsupportedCapability({ dialect, feature: "indexIncludeColumns", hint: `${site} declares INCLUDE columns; covering indexes are PostgreSQL only` });
2374
+ if (!INCLUDE_INDEX_KINDS.includes(index.kind))
2375
+ throw unsupportedCapability({ dialect, feature: `INCLUDE columns on a ${index.kind} index`, hint: `${site}: PostgreSQL stores INCLUDE columns in btree, unique, GiST and SP-GiST indexes only` });
2376
+ const keys = keyColumns({ index });
2377
+ const seen = new Set;
2378
+ for (const name of include) {
2379
+ if (!model.fields.some((field) => field.name === name))
2380
+ throw invalid({ rule: "index-include-field", message: `include names "${name}", which is not a scalar field of the model`, field: name });
2381
+ if (seen.has(name))
2382
+ throw invalid({ rule: "index-include-duplicate", message: `include lists "${name}" twice`, field: name });
2383
+ if (keys.has(name))
2384
+ throw invalid({ rule: "index-include-key-column", message: `"${name}" is already a key column of the index; PostgreSQL refuses a column that is both key and included`, field: name });
2385
+ seen.add(name);
2386
+ }
2387
+ }
2388
+ }
2389
+ function keyColumns(params) {
2390
+ return new Set(indexKeyTerms({ index: params.index }).flatMap((term) => term.kind === "field" ? [term.field.name] : []));
2391
+ }
2392
+ // packages/schema/src/table-storage.ts
2393
+ var INT_MAX = 2147483647;
2394
+ var HEAP_ONLY = {
2395
+ fillfactor: { type: "integer", min: 10, max: 100 },
2396
+ toast_tuple_target: { type: "integer", min: 128, max: 8160 },
2397
+ parallel_workers: { type: "integer", min: 0, max: 1024 },
2398
+ autovacuum_analyze_threshold: { type: "integer", min: 0, max: INT_MAX },
2399
+ autovacuum_analyze_scale_factor: { type: "real", min: 0, max: 100 }
2400
+ };
2401
+ var VACUUM = {
2402
+ autovacuum_enabled: { type: "boolean" },
2403
+ vacuum_index_cleanup: { type: "enum", values: ["auto", "on", "off"] },
2404
+ vacuum_truncate: { type: "boolean" },
2405
+ autovacuum_vacuum_threshold: { type: "integer", min: 0, max: INT_MAX },
2406
+ autovacuum_vacuum_scale_factor: { type: "real", min: 0, max: 100 },
2407
+ autovacuum_vacuum_insert_threshold: { type: "integer", min: -1, max: INT_MAX },
2408
+ autovacuum_vacuum_insert_scale_factor: { type: "real", min: 0, max: 100 },
2409
+ autovacuum_vacuum_cost_delay: { type: "real", min: 0, max: 100 },
2410
+ autovacuum_vacuum_cost_limit: { type: "integer", min: 1, max: 1e4 },
2411
+ autovacuum_freeze_min_age: { type: "integer", min: 0, max: 1e9 },
2412
+ autovacuum_freeze_max_age: { type: "integer", min: 1e5, max: 2000000000 },
2413
+ autovacuum_freeze_table_age: { type: "integer", min: 0, max: 2000000000 },
2414
+ autovacuum_multixact_freeze_min_age: { type: "integer", min: 0, max: 1e9 },
2415
+ autovacuum_multixact_freeze_max_age: { type: "integer", min: 1e4, max: 2000000000 },
2416
+ autovacuum_multixact_freeze_table_age: { type: "integer", min: 0, max: 2000000000 },
2417
+ log_autovacuum_min_duration: { type: "integer", min: -1, max: INT_MAX }
2418
+ };
2419
+ var TABLE_STORAGE_PARAMETERS = Object.freeze({
2420
+ ...HEAP_ONLY,
2421
+ ...VACUUM,
2422
+ ...Object.fromEntries(Object.entries(VACUUM).map(([key, spec]) => [`toast.${key}`, spec]))
2423
+ });
2424
+ var TABLE_STORAGE_KEYS = Object.freeze(Object.keys(TABLE_STORAGE_PARAMETERS));
2425
+ var TOAST_PREFIX = "toast.";
2426
+ function specOf(params) {
2427
+ return Object.hasOwn(TABLE_STORAGE_PARAMETERS, params.key) ? TABLE_STORAGE_PARAMETERS[params.key] : undefined;
2428
+ }
2429
+ function isToastStorageKey(params) {
2430
+ return params.key.startsWith(TOAST_PREFIX);
2431
+ }
2432
+ function tableStoragePrismaKey(params) {
2433
+ return isToastStorageKey(params) ? `toast_${params.key.slice(TOAST_PREFIX.length)}` : params.key;
2434
+ }
2435
+ function tableStorageKeyFromPrisma(params) {
2436
+ if (specOf({ key: params.name }) !== undefined)
2437
+ return params.name;
2438
+ if (!params.name.startsWith("toast_"))
2439
+ return;
2440
+ const key = `${TOAST_PREFIX}${params.name.slice("toast_".length)}`;
2441
+ return specOf({ key }) === undefined ? undefined : key;
2442
+ }
2443
+ var TRUE_WORDS = new Set(["true", "t", "on", "yes", "y", "1"]);
2444
+ var FALSE_WORDS = new Set(["false", "f", "off", "no", "n", "0"]);
2445
+ function parseTableStorageValue(params) {
2446
+ const spec = specOf({ key: params.key });
2447
+ if (spec === undefined)
2448
+ return;
2449
+ const text = params.text.trim().toLowerCase();
2450
+ switch (spec.type) {
2451
+ case "boolean":
2452
+ return TRUE_WORDS.has(text) ? true : FALSE_WORDS.has(text) ? false : undefined;
2453
+ case "enum":
2454
+ return spec.values?.includes(text) === true ? text : TRUE_WORDS.has(text) ? "on" : FALSE_WORDS.has(text) ? "off" : undefined;
2455
+ case "integer":
2456
+ case "real": {
2457
+ if (text === "" || !/^[-+]?(\d+(\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(text))
2458
+ return;
2459
+ const value = Number(text);
2460
+ return Number.isFinite(value) ? value : undefined;
2461
+ }
2462
+ }
2463
+ }
2464
+ function renderTableStorageValue(params) {
2465
+ return typeof params.value === "boolean" ? params.value ? "true" : "false" : String(params.value);
2466
+ }
2467
+ function valueIssue(params) {
2468
+ const { spec, value } = params;
2469
+ switch (spec.type) {
2470
+ case "boolean":
2471
+ return typeof value === "boolean" ? undefined : "takes true or false";
2472
+ case "enum":
2473
+ return typeof value === "string" && spec.values?.includes(value) === true ? undefined : `takes one of ${spec.values?.map((entry) => `"${entry}"`).join(", ")}`;
2474
+ case "integer":
2475
+ case "real": {
2476
+ const bounds = `between ${spec.min} and ${spec.max}`;
2477
+ if (typeof value !== "number" || !Number.isFinite(value))
2478
+ return `takes a number ${bounds}`;
2479
+ if (spec.type === "integer" && !Number.isInteger(value))
2480
+ return `takes a whole number ${bounds}`;
2481
+ return value < (spec.min ?? -Infinity) || value > (spec.max ?? Infinity) ? `takes a value ${bounds}` : undefined;
2482
+ }
2483
+ }
2484
+ }
2485
+ function hasTableStorage(params) {
2486
+ return params.schema.models.some((model) => model.storage !== undefined);
2487
+ }
2488
+ function assertTableStorage(params) {
2489
+ const { schema, dialect } = params;
2490
+ for (const model of schema.models) {
2491
+ const storage = model.storage;
2492
+ if (storage === undefined)
2493
+ continue;
2494
+ const invalid = (issue) => new VibeError({ code: "VIBE_SCHEMA_INVALID", message: `@@storage on "${model.name}": ${issue.message}`, meta: { rule: issue.rule, model: model.name, ...issue.parameter === undefined ? {} : { parameter: issue.parameter } } });
2495
+ if (storage === null || typeof storage !== "object" || Array.isArray(storage) || Object.keys(storage).length === 0)
2496
+ throw invalid({ rule: "table-storage-shape", message: "storage takes a non-empty record of parameters (omit it for a table without any)" });
2497
+ if (model.isView)
2498
+ throw invalid({ rule: "table-storage-view", message: "a view has no table storage parameters" });
2499
+ if (!DIALECT_CAPABILITIES[dialect].tableStorageParameters)
2500
+ throw unsupportedCapability({ dialect, feature: "tableStorageParameters", hint: `@@storage on "${model.name}" declares table storage parameters; they are PostgreSQL only` });
2501
+ for (const [key, value] of Object.entries(storage)) {
2502
+ const spec = specOf({ key });
2503
+ if (spec === undefined)
2504
+ throw invalid({ rule: "table-storage-parameter", message: `"${key}" is not a table storage parameter VibeORM declares (${TABLE_STORAGE_KEYS.filter((entry) => !isToastStorageKey({ key: entry })).join(", ")}, and toast. variants of the vacuum ones)`, parameter: key });
2505
+ const issue = valueIssue({ spec, value });
2506
+ if (issue !== undefined)
2507
+ throw invalid({ rule: "table-storage-value", message: `"${key}" ${issue}`, parameter: key });
2508
+ }
2509
+ }
2510
+ }
2511
+ function tableStorageEntries(params) {
2512
+ const storage = params.model.storage;
2513
+ if (storage === undefined)
2514
+ return [];
2515
+ const known = TABLE_STORAGE_KEYS.flatMap((key) => {
2516
+ const value = Object.hasOwn(storage, key) ? storage[key] : undefined;
2517
+ return value === undefined ? [] : [[key, value]];
2518
+ });
2519
+ const unknown = Object.entries(storage).flatMap(([key, value]) => specOf({ key }) !== undefined || value === undefined ? [] : [[key, value]]);
2520
+ return [...known, ...unknown];
2521
+ }
2338
2522
  // packages/schema/src/module-schema.ts
2339
2523
  function prepareDatabaseModuleSchema(params) {
2340
2524
  const { schema, module } = params;
@@ -2857,6 +3041,8 @@ var RESERVED_ENUM_NAMES = [
2857
3041
  "SelectionMember",
2858
3042
  "SelectionValue",
2859
3043
  "BagKeys",
3044
+ "__VibeBagKeysIn",
3045
+ "__VibeCertain",
2860
3046
  "IncludedRelation",
2861
3047
  "IncludedCount",
2862
3048
  "ViewDelegateMap",
@@ -2865,6 +3051,8 @@ var RESERVED_ENUM_NAMES = [
2865
3051
  "__VibeAt",
2866
3052
  "__VibeItem",
2867
3053
  "__VibeExact",
3054
+ "__VibePreviousInput",
3055
+ "__VibePrevious",
2868
3056
  "extensionManifest",
2869
3057
  "extensionMounts",
2870
3058
  "computedManifest",
@@ -2905,6 +3093,12 @@ var RESERVED_ENUM_NAMES = [
2905
3093
  "RowLockRequest",
2906
3094
  "GroupByTruncateUnit",
2907
3095
  "GroupByKeyName",
3096
+ "GroupByPath",
3097
+ "GroupByExpressionValues",
3098
+ "GroupByKeyOrderInput",
3099
+ "GroupByOrderKeys",
3100
+ "GroupByKeyOrderCheck",
3101
+ "GroupingSetRow",
2908
3102
  "JsonValue",
2909
3103
  "InputJsonValue",
2910
3104
  "InputJsonArray",
@@ -2930,9 +3124,20 @@ var RESERVED_ENUM_NAMES = [
2930
3124
  "createClient",
2931
3125
  "DatabaseAdapter",
2932
3126
  "DbNow",
3127
+ "BigIntFieldRef",
3128
+ "BooleanFieldRef",
3129
+ "DateTimeFieldRef",
3130
+ "DecimalFieldRef",
3131
+ "EnumFieldRef",
3132
+ "FloatFieldRef",
3133
+ "IntFieldRef",
3134
+ "NumericFieldRef",
3135
+ "StringFieldRef",
2933
3136
  "JoinedField",
2934
3137
  "TransactionLocalSettings",
2935
3138
  "VibeUpdateManySources",
3139
+ "VibeModelRows",
3140
+ "VibeHandleTypes",
2936
3141
  "ClientContext",
2937
3142
  "QueryOperation",
2938
3143
  "QueryEvent",
@@ -3016,6 +3221,12 @@ var GENERATED_TYPE_NAMES = {
3016
3221
  "HavingInput",
3017
3222
  "GroupByOrderByInput",
3018
3223
  "GroupByTruncatedKey",
3224
+ "GroupByExpressionKey",
3225
+ "FilteredAggregateInput",
3226
+ "FilteredAggregateHavingInput",
3227
+ "FilteredAggregateOrderByInput",
3228
+ "FilteredResult",
3229
+ "CoalesceOrderInput",
3019
3230
  "FindManyArgs",
3020
3231
  "FindPageArgs",
3021
3232
  "FindFirstArgs",
@@ -3065,6 +3276,7 @@ var GENERATED_TYPE_NAMES = {
3065
3276
  "ViewDelegate",
3066
3277
  "ReadOnlyDelegate"
3067
3278
  ],
3279
+ generatedColumnSuffixes: ["WritableScalarFieldEnum", "GeneratedWriteGuard", "GeneratedBulkWriteRow"],
3068
3280
  nestedPrefixes: [
3069
3281
  "Create",
3070
3282
  "UncheckedCreate",
@@ -3140,33 +3352,118 @@ var INVALID_BINDING_NAMES = new Set([
3140
3352
  "object",
3141
3353
  "undefined"
3142
3354
  ]);
3143
- function validateGeneratedNames(params) {
3144
- const { schema, issues } = params;
3145
- const owners = new Map(RESERVED_ENUM_NAMES.map((name) => [name, "generated client"]));
3355
+ var DISAMBIGUATED_TYPE_SUFFIXES = {
3356
+ ListRelationFilter: true,
3357
+ RelationFilter: true,
3358
+ RelationOrderByInput: true,
3359
+ RelationFindManyArgs: true,
3360
+ CreateArgs: true,
3361
+ UpdateArgs: true,
3362
+ UpdateWhereInput: true,
3363
+ UpsertArgs: true,
3364
+ DeleteArgs: true,
3365
+ CountArgs: true,
3366
+ CountSelect: true,
3367
+ AggregateArgs: true,
3368
+ ViewSelect: true,
3369
+ ViewInclude: true,
3370
+ ViewFindManyArgs: true,
3371
+ ViewFindUniqueArgs: true,
3372
+ ViewDelegate: true
3373
+ };
3374
+ function isDisambiguatedTypeSuffix(suffix) {
3375
+ return Object.hasOwn(DISAMBIGUATED_TYPE_SUFFIXES, suffix);
3376
+ }
3377
+ function generatedNameClaims(params) {
3378
+ const { schema } = params;
3379
+ const claims = [];
3380
+ const fixed = (options) => {
3381
+ for (const name of options.names)
3382
+ claims.push({ name, owner: options.owner, kind: "client", stem: name, suffix: "" });
3383
+ };
3384
+ fixed({ names: RESERVED_ENUM_NAMES, owner: "generated client" });
3146
3385
  if (schema.rls !== undefined) {
3147
- owners.set("RlsContext", "native RLS client");
3148
- owners.set("BoundVibeClientInstance", "native RLS client");
3149
- owners.set("BoundVibeTransactionClientInstance", "native RLS client");
3150
- owners.set("OperatorVibeClientInstance", "native RLS client");
3151
- owners.set("OperatorVibeTransactionClientInstance", "native RLS client");
3386
+ fixed({ names: ["RlsContext", "BoundVibeClientInstance", "BoundVibeTransactionClientInstance", "OperatorVibeClientInstance", "OperatorVibeTransactionClientInstance"], owner: "native RLS client" });
3152
3387
  }
3153
3388
  if (schema.models.some((model) => model.policy !== undefined))
3154
- owners.set("PolicyClientInstance", "policy client");
3389
+ fixed({ names: ["PolicyClientInstance"], owner: "policy client" });
3155
3390
  if (schema.models.some((model) => model.fields.some((field) => isLocalDateField({ field })))) {
3156
- for (const name of ["LocalDate", "LocalDateFilter", "LocalDateNullableFilter"])
3157
- owners.set(name, "local-date filters");
3391
+ fixed({ names: ["LocalDate", "LocalDateFilter", "LocalDateNullableFilter"], owner: "local-date filters" });
3158
3392
  }
3159
- const register = (params) => {
3160
- if (!/^[$_\p{ID_Start}][$_\u200c\u200d\p{ID_Continue}]*$/u.test(params.name) || INVALID_BINDING_NAMES.has(params.name)) {
3161
- issues.push({ rule: "invalid-generated-identifier", message: `Generated name "${params.name}" from ${params.owner} is not a legal TypeScript identifier` });
3393
+ for (const model of schema.models) {
3394
+ const owner = `model "${model.name}"`;
3395
+ const claim = (suffix) => {
3396
+ claims.push({ name: `${model.name}${suffix}`, owner, kind: "model", stem: model.name, suffix });
3397
+ };
3398
+ for (const suffix of GENERATED_TYPE_NAMES.modelSuffixes)
3399
+ claim(suffix);
3400
+ if (model.fields.some((field) => field.generated !== undefined && field.isUnsupported !== true)) {
3401
+ for (const suffix of GENERATED_TYPE_NAMES.generatedColumnSuffixes)
3402
+ claim(suffix);
3162
3403
  }
3163
- const previous = owners.get(params.name);
3164
- if (previous !== undefined && previous !== params.owner) {
3165
- issues.push({ rule: "generated-name-collision", message: `Generated name "${params.name}" from ${params.owner} conflicts with ${previous}` });
3166
- } else {
3167
- owners.set(params.name, params.owner);
3404
+ const withouts = ["", ...model.relations.map((relation) => `Without${relation.name.charAt(0).toUpperCase()}${relation.name.slice(1)}`)];
3405
+ for (const without of withouts) {
3406
+ for (const prefix of GENERATED_TYPE_NAMES.nestedPrefixes)
3407
+ claim(`${prefix}${without}Input`);
3408
+ }
3409
+ }
3410
+ for (const declared of schema.enums) {
3411
+ for (const suffix of GENERATED_TYPE_NAMES.enumSuffixes) {
3412
+ claims.push({ name: `${declared.name}${suffix}`, owner: `enum "${declared.name}"`, kind: "enum", stem: declared.name, suffix });
3413
+ }
3414
+ }
3415
+ return claims;
3416
+ }
3417
+ function renameHint(params) {
3418
+ const named = [params.claim, params.keeper].filter((claim) => claim.kind !== "client");
3419
+ const table = named.some((claim) => claim.kind === "model") ? " (@@map, or map in the DSL, keeps a model's table name)" : "";
3420
+ return `rename ${[...new Set(named.map((claim) => claim.owner))].join(" or ")}${table}`;
3421
+ }
3422
+ function resolveGeneratedNames(params) {
3423
+ const claims = generatedNameClaims({ schema: params.schema });
3424
+ const issues = [];
3425
+ const keepers = new Map;
3426
+ for (const claim of claims) {
3427
+ if (claim.kind !== "client" && (!/^[$_\p{ID_Start}][$_\u200c\u200d\p{ID_Continue}]*$/u.test(claim.name) || INVALID_BINDING_NAMES.has(claim.name))) {
3428
+ issues.push({ rule: "invalid-generated-identifier", message: `Generated name "${claim.name}" from ${claim.owner} is not a legal TypeScript identifier` });
3429
+ }
3430
+ const keeper = keepers.get(claim.name);
3431
+ if (keeper === undefined || claim.stem.length > keeper.stem.length)
3432
+ keepers.set(claim.name, claim);
3433
+ }
3434
+ const renamed = new Map;
3435
+ const moved = [];
3436
+ for (const claim of claims) {
3437
+ const keeper = keepers.get(claim.name) ?? claim;
3438
+ if (keeper.owner === claim.owner)
3439
+ continue;
3440
+ if (claim.kind === "model" && isDisambiguatedTypeSuffix(claim.suffix) && keeper.suffix !== "" && claim.stem.length < keeper.stem.length) {
3441
+ const name = `${claim.stem}_${claim.suffix}`;
3442
+ renamed.set(`${claim.stem}\x00${claim.suffix}`, name);
3443
+ moved.push({ claim, keeper, name });
3444
+ continue;
3168
3445
  }
3446
+ issues.push({ rule: "generated-name-collision", message: `Generated name "${claim.name}" from ${claim.owner} conflicts with ${keeper.owner}; ${renameHint({ claim, keeper })}` });
3447
+ }
3448
+ for (const { claim, keeper, name } of moved) {
3449
+ const taken = keepers.get(name);
3450
+ if (taken === undefined || taken.owner === claim.owner)
3451
+ continue;
3452
+ issues.push({
3453
+ rule: "generated-name-collision",
3454
+ message: `Generated name "${name}" from ${claim.owner} (renamed because ${keeper.owner} keeps "${claim.name}") conflicts with ${taken.owner}; ${renameHint({ claim, keeper: taken })}`
3455
+ });
3456
+ }
3457
+ return { renamed, issues };
3458
+ }
3459
+ function generatedTypeNames(params) {
3460
+ const { renamed } = resolveGeneratedNames({ schema: params.schema });
3461
+ return {
3462
+ model: ({ model, suffix }) => renamed.get(`${model}\x00${suffix}`) ?? `${model}${suffix}`
3169
3463
  };
3464
+ }
3465
+ function validateGeneratedNames(params) {
3466
+ const { schema, issues } = params;
3170
3467
  const aliases = new Map;
3171
3468
  for (const model of schema.models) {
3172
3469
  const clientName = model.name.charAt(0).toLowerCase() + model.name.slice(1);
@@ -3180,19 +3477,8 @@ function validateGeneratedNames(params) {
3180
3477
  if (RESERVED_CLIENT_NAMES.includes(clientName)) {
3181
3478
  issues.push({ rule: "reserved-client-name", message: `Model "${model.name}" conflicts with client member "${clientName}"`, model: model.name });
3182
3479
  }
3183
- const owner = `model "${model.name}"`;
3184
- for (const suffix of GENERATED_TYPE_NAMES.modelSuffixes)
3185
- register({ name: `${model.name}${suffix}`, owner });
3186
- const withouts = ["", ...model.relations.map((relation) => `Without${relation.name.charAt(0).toUpperCase()}${relation.name.slice(1)}`)];
3187
- for (const without of withouts) {
3188
- for (const prefix of GENERATED_TYPE_NAMES.nestedPrefixes)
3189
- register({ name: `${model.name}${prefix}${without}Input`, owner });
3190
- }
3191
- }
3192
- for (const declared of schema.enums) {
3193
- for (const suffix of GENERATED_TYPE_NAMES.enumSuffixes)
3194
- register({ name: `${declared.name}${suffix}`, owner: `enum "${declared.name}"` });
3195
3480
  }
3481
+ issues.push(...resolveGeneratedNames({ schema }).issues);
3196
3482
  }
3197
3483
  function foreignKeyKeys(params) {
3198
3484
  const distinct = new Set;
@@ -3495,7 +3781,7 @@ function validatePhysicalNameUniqueness(params) {
3495
3781
  for (const { index, site } of declarations) {
3496
3782
  const unique = index.kind === "unique";
3497
3783
  const columns = index.fields.map((entry) => [columnOf(entry.name), entry.sort === "desc", entry.operatorClass === undefined ? null : [entry.operatorClass.namespace, entry.operatorClass.name]]);
3498
- const key = isExpressionIndex({ index }) ? indexKeyDefinitionKey({ index, model }) : JSON.stringify([unique, index.nullsNotDistinct === true, unique ? "btree" : index.kind, columns, index.where?.trim() ?? ""]);
3784
+ const key = isExpressionIndex({ index }) ? indexKeyDefinitionKey({ index, model }) : JSON.stringify([unique, index.nullsNotDistinct === true, unique ? "btree" : index.kind, columns, index.where?.trim() ?? "", ...index.include === undefined ? [] : [{ include: index.include.map(columnOf) }]]);
3499
3785
  if (seenIndexKeys.has(key))
3500
3786
  continue;
3501
3787
  seenIndexKeys.add(key);
@@ -3933,6 +4219,27 @@ function validateModel(params) {
3933
4219
  }
3934
4220
  }
3935
4221
  }
4222
+ if (relation.inverseKey !== undefined) {
4223
+ const { fields, references } = relation.inverseKey;
4224
+ if (relation.isList || hasFields || hasReferences || !Array.isArray(fields) || !Array.isArray(references) || fields.length === 0 || fields.length !== references.length) {
4225
+ issues.push({
4226
+ rule: "inverse-key-shape",
4227
+ message: `Relation "${model.name}.${relation.name}" declares an inverse key, which needs a to-one without foreign-key fields and as many references as fields`,
4228
+ model: model.name,
4229
+ field: relation.name
4230
+ });
4231
+ } else {
4232
+ for (const name of fields) {
4233
+ if (!fieldNames.has(name))
4234
+ issues.push({ rule: "unknown-relation-field", message: `Relation "${model.name}.${relation.name}" uses unknown inverse-key field "${name}"`, model: model.name, field: relation.name });
4235
+ }
4236
+ const targetFields = target === undefined ? undefined : lookups.fieldsByModel.get(target);
4237
+ for (const name of references) {
4238
+ if (targetFields !== undefined && !targetFields.has(name))
4239
+ issues.push({ rule: "unknown-relation-reference", message: `Relation "${model.name}.${relation.name}" references unknown inverse-key field "${relation.targetModel}.${name}"`, model: model.name, field: relation.name });
4240
+ }
4241
+ }
4242
+ }
3936
4243
  }
3937
4244
  for (const field of model.fields) {
3938
4245
  if (field.collation !== undefined && (field.type !== "String" || isLocalDateField({ field }))) {
@@ -4240,6 +4547,8 @@ function assertValidSchemaIR(params) {
4240
4547
  }
4241
4548
  assertExtensionPlacement({ schema: params.schema, dialect });
4242
4549
  assertIndexOperatorClasses({ schema: params.schema, dialect });
4550
+ assertIndexIncludeColumns({ schema: params.schema, dialect });
4551
+ assertTableStorage({ schema: params.schema, dialect });
4243
4552
  for (const entry of [...params.schema.models, ...params.schema.enums])
4244
4553
  if (entry.namespace !== undefined)
4245
4554
  assertSqlNamespace({ namespace: entry.namespace, dialect });
@@ -4548,6 +4857,7 @@ export {
4548
4857
  EMPTY_EXTENSION_REGISTRY,
4549
4858
  ENUM_STORAGES,
4550
4859
  IDENTIFIER_LIMITS,
4860
+ INCLUDE_INDEX_KINDS,
4551
4861
  MYSQL_CAPABILITIES,
4552
4862
  MYSQL_FUNCTIONAL_KEY_LIMITS,
4553
4863
  MYSQL_ROW_RULE_MESSAGE_MAX_BYTES,
@@ -4565,6 +4875,8 @@ export {
4565
4875
  SQLITE_ROW_RULE_ERROR_PREFIX,
4566
4876
  SQL_NAMESPACES,
4567
4877
  ScopedQueryError,
4878
+ TABLE_STORAGE_KEYS,
4879
+ TABLE_STORAGE_PARAMETERS,
4568
4880
  VIBE_ERROR_CODES,
4569
4881
  VibeError,
4570
4882
  assertExtensionModelPlacement,
@@ -4573,6 +4885,7 @@ export {
4573
4885
  assertGeneratedColumns,
4574
4886
  assertIdentifierFits,
4575
4887
  assertIndexExpressions,
4888
+ assertIndexIncludeColumns,
4576
4889
  assertIndexOperatorClasses,
4577
4890
  assertJsonSchemaRef,
4578
4891
  assertMigrationObjects,
@@ -4582,6 +4895,7 @@ export {
4582
4895
  assertRowRuleEquality,
4583
4896
  assertRowRuleReferentialActions,
4584
4897
  assertSqlNamespace,
4898
+ assertTableStorage,
4585
4899
  assertValidSchemaIR,
4586
4900
  assertValidatorRef,
4587
4901
  buildComputedManifest,
@@ -4607,7 +4921,10 @@ export {
4607
4921
  fitDefaultName,
4608
4922
  generatedColumnFromSql,
4609
4923
  generatedColumnShapeIssue,
4924
+ generatedTypeNames,
4925
+ hasIndexIncludeColumns,
4610
4926
  hasJsonSchemaAnnotation,
4927
+ hasTableStorage,
4611
4928
  hasValidatorAnnotation,
4612
4929
  identifierLength,
4613
4930
  implicitJoinTable,
@@ -4632,6 +4949,7 @@ export {
4632
4949
  isReservedDelegateName,
4633
4950
  isSqlObjectDefinition,
4634
4951
  isSqlObjectIdentity,
4952
+ isToastStorageKey,
4635
4953
  jsonSchemaRefIssue,
4636
4954
  jsonTypeAnnotationIssue,
4637
4955
  jsonTypeAnnotationRef,
@@ -4643,6 +4961,7 @@ export {
4643
4961
  normalizeJsonSchemaRef,
4644
4962
  normalizeRowRules,
4645
4963
  normalizeValidatorRef,
4964
+ parseTableStorageValue,
4646
4965
  postgresGeneratedObjectName,
4647
4966
  predicateCapabilityIssue,
4648
4967
  predicateFieldNames,
@@ -4650,6 +4969,7 @@ export {
4650
4969
  prepareDatabaseModuleSchema,
4651
4970
  providerToDialect,
4652
4971
  qualifiedSqlName,
4972
+ renderTableStorageValue,
4653
4973
  resolveOnDelete,
4654
4974
  resolveOnUpdate,
4655
4975
  resolveReferentialActions,
@@ -4670,6 +4990,9 @@ export {
4670
4990
  sqlObjectKey,
4671
4991
  sqlObjectNamespace,
4672
4992
  sqliteRowRuleConstraint,
4993
+ tableStorageEntries,
4994
+ tableStorageKeyFromPrisma,
4995
+ tableStoragePrismaKey,
4673
4996
  timeOfDayType,
4674
4997
  unsupportedCapability,
4675
4998
  validateComputedConfig,
@@ -4681,4 +5004,4 @@ export {
4681
5004
  validatorRefIssue
4682
5005
  };
4683
5006
 
4684
- //# debugId=16597011ACA85C7264756E2164756E21
5007
+ //# debugId=61CF54A7622E428F64756E2164756E21