@vibeorm/schema 5.7.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/capabilities.d.ts +29 -0
- package/dist/index-include.d.ts +18 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.js +376 -39
- package/dist/index.js.map +9 -7
- package/dist/ir.d.ts +73 -0
- package/dist/table-storage.d.ts +66 -0
- package/dist/validate.d.ts +23 -0
- package/package.json +1 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,9 +3041,18 @@ 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",
|
|
3049
|
+
"__VibeObjects",
|
|
3050
|
+
"__VibeKeys",
|
|
3051
|
+
"__VibeAt",
|
|
3052
|
+
"__VibeItem",
|
|
3053
|
+
"__VibeExact",
|
|
3054
|
+
"__VibePreviousInput",
|
|
3055
|
+
"__VibePrevious",
|
|
2863
3056
|
"extensionManifest",
|
|
2864
3057
|
"extensionMounts",
|
|
2865
3058
|
"computedManifest",
|
|
@@ -2879,6 +3072,7 @@ var RESERVED_ENUM_NAMES = [
|
|
|
2879
3072
|
"Date",
|
|
2880
3073
|
"Uint8Array",
|
|
2881
3074
|
"schema",
|
|
3075
|
+
"jsonSchemas",
|
|
2882
3076
|
"VibeClient",
|
|
2883
3077
|
"VibeClientInstance",
|
|
2884
3078
|
"VibeQueryPromise",
|
|
@@ -2899,6 +3093,12 @@ var RESERVED_ENUM_NAMES = [
|
|
|
2899
3093
|
"RowLockRequest",
|
|
2900
3094
|
"GroupByTruncateUnit",
|
|
2901
3095
|
"GroupByKeyName",
|
|
3096
|
+
"GroupByPath",
|
|
3097
|
+
"GroupByExpressionValues",
|
|
3098
|
+
"GroupByKeyOrderInput",
|
|
3099
|
+
"GroupByOrderKeys",
|
|
3100
|
+
"GroupByKeyOrderCheck",
|
|
3101
|
+
"GroupingSetRow",
|
|
2902
3102
|
"JsonValue",
|
|
2903
3103
|
"InputJsonValue",
|
|
2904
3104
|
"InputJsonArray",
|
|
@@ -2924,9 +3124,20 @@ var RESERVED_ENUM_NAMES = [
|
|
|
2924
3124
|
"createClient",
|
|
2925
3125
|
"DatabaseAdapter",
|
|
2926
3126
|
"DbNow",
|
|
3127
|
+
"BigIntFieldRef",
|
|
3128
|
+
"BooleanFieldRef",
|
|
3129
|
+
"DateTimeFieldRef",
|
|
3130
|
+
"DecimalFieldRef",
|
|
3131
|
+
"EnumFieldRef",
|
|
3132
|
+
"FloatFieldRef",
|
|
3133
|
+
"IntFieldRef",
|
|
3134
|
+
"NumericFieldRef",
|
|
3135
|
+
"StringFieldRef",
|
|
2927
3136
|
"JoinedField",
|
|
2928
3137
|
"TransactionLocalSettings",
|
|
2929
3138
|
"VibeUpdateManySources",
|
|
3139
|
+
"VibeModelRows",
|
|
3140
|
+
"VibeHandleTypes",
|
|
2930
3141
|
"ClientContext",
|
|
2931
3142
|
"QueryOperation",
|
|
2932
3143
|
"QueryEvent",
|
|
@@ -3010,6 +3221,12 @@ var GENERATED_TYPE_NAMES = {
|
|
|
3010
3221
|
"HavingInput",
|
|
3011
3222
|
"GroupByOrderByInput",
|
|
3012
3223
|
"GroupByTruncatedKey",
|
|
3224
|
+
"GroupByExpressionKey",
|
|
3225
|
+
"FilteredAggregateInput",
|
|
3226
|
+
"FilteredAggregateHavingInput",
|
|
3227
|
+
"FilteredAggregateOrderByInput",
|
|
3228
|
+
"FilteredResult",
|
|
3229
|
+
"CoalesceOrderInput",
|
|
3013
3230
|
"FindManyArgs",
|
|
3014
3231
|
"FindPageArgs",
|
|
3015
3232
|
"FindFirstArgs",
|
|
@@ -3059,6 +3276,7 @@ var GENERATED_TYPE_NAMES = {
|
|
|
3059
3276
|
"ViewDelegate",
|
|
3060
3277
|
"ReadOnlyDelegate"
|
|
3061
3278
|
],
|
|
3279
|
+
generatedColumnSuffixes: ["WritableScalarFieldEnum", "GeneratedWriteGuard", "GeneratedBulkWriteRow"],
|
|
3062
3280
|
nestedPrefixes: [
|
|
3063
3281
|
"Create",
|
|
3064
3282
|
"UncheckedCreate",
|
|
@@ -3134,33 +3352,118 @@ var INVALID_BINDING_NAMES = new Set([
|
|
|
3134
3352
|
"object",
|
|
3135
3353
|
"undefined"
|
|
3136
3354
|
]);
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
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" });
|
|
3140
3385
|
if (schema.rls !== undefined) {
|
|
3141
|
-
|
|
3142
|
-
owners.set("BoundVibeClientInstance", "native RLS client");
|
|
3143
|
-
owners.set("BoundVibeTransactionClientInstance", "native RLS client");
|
|
3144
|
-
owners.set("OperatorVibeClientInstance", "native RLS client");
|
|
3145
|
-
owners.set("OperatorVibeTransactionClientInstance", "native RLS client");
|
|
3386
|
+
fixed({ names: ["RlsContext", "BoundVibeClientInstance", "BoundVibeTransactionClientInstance", "OperatorVibeClientInstance", "OperatorVibeTransactionClientInstance"], owner: "native RLS client" });
|
|
3146
3387
|
}
|
|
3147
3388
|
if (schema.models.some((model) => model.policy !== undefined))
|
|
3148
|
-
|
|
3389
|
+
fixed({ names: ["PolicyClientInstance"], owner: "policy client" });
|
|
3149
3390
|
if (schema.models.some((model) => model.fields.some((field) => isLocalDateField({ field })))) {
|
|
3150
|
-
|
|
3151
|
-
|
|
3391
|
+
fixed({ names: ["LocalDate", "LocalDateFilter", "LocalDateNullableFilter"], owner: "local-date filters" });
|
|
3392
|
+
}
|
|
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);
|
|
3403
|
+
}
|
|
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
|
+
}
|
|
3152
3409
|
}
|
|
3153
|
-
const
|
|
3154
|
-
|
|
3155
|
-
|
|
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 });
|
|
3156
3413
|
}
|
|
3157
|
-
|
|
3158
|
-
|
|
3159
|
-
|
|
3160
|
-
|
|
3161
|
-
|
|
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;
|
|
3162
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}`
|
|
3163
3463
|
};
|
|
3464
|
+
}
|
|
3465
|
+
function validateGeneratedNames(params) {
|
|
3466
|
+
const { schema, issues } = params;
|
|
3164
3467
|
const aliases = new Map;
|
|
3165
3468
|
for (const model of schema.models) {
|
|
3166
3469
|
const clientName = model.name.charAt(0).toLowerCase() + model.name.slice(1);
|
|
@@ -3174,19 +3477,8 @@ function validateGeneratedNames(params) {
|
|
|
3174
3477
|
if (RESERVED_CLIENT_NAMES.includes(clientName)) {
|
|
3175
3478
|
issues.push({ rule: "reserved-client-name", message: `Model "${model.name}" conflicts with client member "${clientName}"`, model: model.name });
|
|
3176
3479
|
}
|
|
3177
|
-
const owner = `model "${model.name}"`;
|
|
3178
|
-
for (const suffix of GENERATED_TYPE_NAMES.modelSuffixes)
|
|
3179
|
-
register({ name: `${model.name}${suffix}`, owner });
|
|
3180
|
-
const withouts = ["", ...model.relations.map((relation) => `Without${relation.name.charAt(0).toUpperCase()}${relation.name.slice(1)}`)];
|
|
3181
|
-
for (const without of withouts) {
|
|
3182
|
-
for (const prefix of GENERATED_TYPE_NAMES.nestedPrefixes)
|
|
3183
|
-
register({ name: `${model.name}${prefix}${without}Input`, owner });
|
|
3184
|
-
}
|
|
3185
|
-
}
|
|
3186
|
-
for (const declared of schema.enums) {
|
|
3187
|
-
for (const suffix of GENERATED_TYPE_NAMES.enumSuffixes)
|
|
3188
|
-
register({ name: `${declared.name}${suffix}`, owner: `enum "${declared.name}"` });
|
|
3189
3480
|
}
|
|
3481
|
+
issues.push(...resolveGeneratedNames({ schema }).issues);
|
|
3190
3482
|
}
|
|
3191
3483
|
function foreignKeyKeys(params) {
|
|
3192
3484
|
const distinct = new Set;
|
|
@@ -3489,7 +3781,7 @@ function validatePhysicalNameUniqueness(params) {
|
|
|
3489
3781
|
for (const { index, site } of declarations) {
|
|
3490
3782
|
const unique = index.kind === "unique";
|
|
3491
3783
|
const columns = index.fields.map((entry) => [columnOf(entry.name), entry.sort === "desc", entry.operatorClass === undefined ? null : [entry.operatorClass.namespace, entry.operatorClass.name]]);
|
|
3492
|
-
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) }]]);
|
|
3493
3785
|
if (seenIndexKeys.has(key))
|
|
3494
3786
|
continue;
|
|
3495
3787
|
seenIndexKeys.add(key);
|
|
@@ -3927,6 +4219,27 @@ function validateModel(params) {
|
|
|
3927
4219
|
}
|
|
3928
4220
|
}
|
|
3929
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
|
+
}
|
|
3930
4243
|
}
|
|
3931
4244
|
for (const field of model.fields) {
|
|
3932
4245
|
if (field.collation !== undefined && (field.type !== "String" || isLocalDateField({ field }))) {
|
|
@@ -3937,6 +4250,14 @@ function validateModel(params) {
|
|
|
3937
4250
|
field: field.name
|
|
3938
4251
|
});
|
|
3939
4252
|
}
|
|
4253
|
+
if (field.bigIntMode !== undefined && (field.type !== "BigInt" || field.bigIntMode !== "number")) {
|
|
4254
|
+
issues.push({
|
|
4255
|
+
rule: "bigint-mode",
|
|
4256
|
+
message: `@number on "${model.name}.${field.name}" \u2014 a number read mode is only valid on BigInt fields, and its one value is "number"`,
|
|
4257
|
+
model: model.name,
|
|
4258
|
+
field: field.name
|
|
4259
|
+
});
|
|
4260
|
+
}
|
|
3940
4261
|
}
|
|
3941
4262
|
if (model.policy !== undefined) {
|
|
3942
4263
|
const policyField = model.fields.find((field) => field.name === model.policy?.field);
|
|
@@ -4226,6 +4547,8 @@ function assertValidSchemaIR(params) {
|
|
|
4226
4547
|
}
|
|
4227
4548
|
assertExtensionPlacement({ schema: params.schema, dialect });
|
|
4228
4549
|
assertIndexOperatorClasses({ schema: params.schema, dialect });
|
|
4550
|
+
assertIndexIncludeColumns({ schema: params.schema, dialect });
|
|
4551
|
+
assertTableStorage({ schema: params.schema, dialect });
|
|
4229
4552
|
for (const entry of [...params.schema.models, ...params.schema.enums])
|
|
4230
4553
|
if (entry.namespace !== undefined)
|
|
4231
4554
|
assertSqlNamespace({ namespace: entry.namespace, dialect });
|
|
@@ -4534,6 +4857,7 @@ export {
|
|
|
4534
4857
|
EMPTY_EXTENSION_REGISTRY,
|
|
4535
4858
|
ENUM_STORAGES,
|
|
4536
4859
|
IDENTIFIER_LIMITS,
|
|
4860
|
+
INCLUDE_INDEX_KINDS,
|
|
4537
4861
|
MYSQL_CAPABILITIES,
|
|
4538
4862
|
MYSQL_FUNCTIONAL_KEY_LIMITS,
|
|
4539
4863
|
MYSQL_ROW_RULE_MESSAGE_MAX_BYTES,
|
|
@@ -4551,6 +4875,8 @@ export {
|
|
|
4551
4875
|
SQLITE_ROW_RULE_ERROR_PREFIX,
|
|
4552
4876
|
SQL_NAMESPACES,
|
|
4553
4877
|
ScopedQueryError,
|
|
4878
|
+
TABLE_STORAGE_KEYS,
|
|
4879
|
+
TABLE_STORAGE_PARAMETERS,
|
|
4554
4880
|
VIBE_ERROR_CODES,
|
|
4555
4881
|
VibeError,
|
|
4556
4882
|
assertExtensionModelPlacement,
|
|
@@ -4559,6 +4885,7 @@ export {
|
|
|
4559
4885
|
assertGeneratedColumns,
|
|
4560
4886
|
assertIdentifierFits,
|
|
4561
4887
|
assertIndexExpressions,
|
|
4888
|
+
assertIndexIncludeColumns,
|
|
4562
4889
|
assertIndexOperatorClasses,
|
|
4563
4890
|
assertJsonSchemaRef,
|
|
4564
4891
|
assertMigrationObjects,
|
|
@@ -4568,6 +4895,7 @@ export {
|
|
|
4568
4895
|
assertRowRuleEquality,
|
|
4569
4896
|
assertRowRuleReferentialActions,
|
|
4570
4897
|
assertSqlNamespace,
|
|
4898
|
+
assertTableStorage,
|
|
4571
4899
|
assertValidSchemaIR,
|
|
4572
4900
|
assertValidatorRef,
|
|
4573
4901
|
buildComputedManifest,
|
|
@@ -4593,7 +4921,10 @@ export {
|
|
|
4593
4921
|
fitDefaultName,
|
|
4594
4922
|
generatedColumnFromSql,
|
|
4595
4923
|
generatedColumnShapeIssue,
|
|
4924
|
+
generatedTypeNames,
|
|
4925
|
+
hasIndexIncludeColumns,
|
|
4596
4926
|
hasJsonSchemaAnnotation,
|
|
4927
|
+
hasTableStorage,
|
|
4597
4928
|
hasValidatorAnnotation,
|
|
4598
4929
|
identifierLength,
|
|
4599
4930
|
implicitJoinTable,
|
|
@@ -4618,6 +4949,7 @@ export {
|
|
|
4618
4949
|
isReservedDelegateName,
|
|
4619
4950
|
isSqlObjectDefinition,
|
|
4620
4951
|
isSqlObjectIdentity,
|
|
4952
|
+
isToastStorageKey,
|
|
4621
4953
|
jsonSchemaRefIssue,
|
|
4622
4954
|
jsonTypeAnnotationIssue,
|
|
4623
4955
|
jsonTypeAnnotationRef,
|
|
@@ -4629,6 +4961,7 @@ export {
|
|
|
4629
4961
|
normalizeJsonSchemaRef,
|
|
4630
4962
|
normalizeRowRules,
|
|
4631
4963
|
normalizeValidatorRef,
|
|
4964
|
+
parseTableStorageValue,
|
|
4632
4965
|
postgresGeneratedObjectName,
|
|
4633
4966
|
predicateCapabilityIssue,
|
|
4634
4967
|
predicateFieldNames,
|
|
@@ -4636,6 +4969,7 @@ export {
|
|
|
4636
4969
|
prepareDatabaseModuleSchema,
|
|
4637
4970
|
providerToDialect,
|
|
4638
4971
|
qualifiedSqlName,
|
|
4972
|
+
renderTableStorageValue,
|
|
4639
4973
|
resolveOnDelete,
|
|
4640
4974
|
resolveOnUpdate,
|
|
4641
4975
|
resolveReferentialActions,
|
|
@@ -4656,6 +4990,9 @@ export {
|
|
|
4656
4990
|
sqlObjectKey,
|
|
4657
4991
|
sqlObjectNamespace,
|
|
4658
4992
|
sqliteRowRuleConstraint,
|
|
4993
|
+
tableStorageEntries,
|
|
4994
|
+
tableStorageKeyFromPrisma,
|
|
4995
|
+
tableStoragePrismaKey,
|
|
4659
4996
|
timeOfDayType,
|
|
4660
4997
|
unsupportedCapability,
|
|
4661
4998
|
validateComputedConfig,
|
|
@@ -4667,4 +5004,4 @@ export {
|
|
|
4667
5004
|
validatorRefIssue
|
|
4668
5005
|
};
|
|
4669
5006
|
|
|
4670
|
-
//# debugId=
|
|
5007
|
+
//# debugId=61CF54A7622E428F64756E2164756E21
|