@vibeorm/schema 4.0.0 → 4.1.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/errors.d.ts +8 -0
- package/dist/extension.d.ts +19 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +263 -197
- package/dist/index.js.map +10 -8
- package/dist/model-sql-namespace.d.ts +8 -0
- package/dist/native-rls-admission.d.ts +7 -0
- package/package.json +1 -1
package/dist/errors.d.ts
CHANGED
|
@@ -58,6 +58,14 @@ export declare const VIBE_ERROR_CODES: readonly [
|
|
|
58
58
|
"VIBE_RLS_CONTEXT",
|
|
59
59
|
/** SQLSTATE 42501: insufficient privilege, including policy WITH CHECK failure. */
|
|
60
60
|
"VIBE_PERMISSION_DENIED",
|
|
61
|
+
/**
|
|
62
|
+
* A known read was cancelled by its caller. Fixed metadata: reason:
|
|
63
|
+
* "caller-aborted", stage: "before-dispatch" | "waiting-for-connection" |
|
|
64
|
+
* "running", databaseCancelled: boolean. True requires the original
|
|
65
|
+
* statement's server cancellation failure, not just a signal being sent.
|
|
66
|
+
* Unrelated driver failures retain their original mapped codes.
|
|
67
|
+
*/
|
|
68
|
+
"VIBE_CANCELLED",
|
|
61
69
|
/** Driver/connection failure not mapped to a more specific code. */
|
|
62
70
|
"VIBE_ADAPTER",
|
|
63
71
|
/**
|
package/dist/extension.d.ts
CHANGED
|
@@ -324,15 +324,32 @@ export declare function buildExtensionRegistry(params: {
|
|
|
324
324
|
}): ExtensionRegistry;
|
|
325
325
|
/** An empty registry — the default when no extensions are configured. */
|
|
326
326
|
export declare const EMPTY_EXTENSION_REGISTRY: ExtensionRegistry;
|
|
327
|
+
/** Whether built-in declarations establish public-only physical ownership for this model. */
|
|
328
|
+
export declare function hasPublicExtensionDeclaration(params: {
|
|
329
|
+
schema: SchemaIR;
|
|
330
|
+
model: string;
|
|
331
|
+
}): boolean;
|
|
332
|
+
/** Refuse placement that built-in extension SQL and ownership cannot preserve. */
|
|
333
|
+
export declare function assertExtensionModelPlacement(params: {
|
|
334
|
+
namespace?: string;
|
|
335
|
+
dialect?: Dialect;
|
|
336
|
+
}): void;
|
|
337
|
+
/** Admit built-in extension declarations before generation, migration or runtime work. */
|
|
338
|
+
export declare function assertExtensionPlacement(params: {
|
|
339
|
+
schema: SchemaIR;
|
|
340
|
+
dialect?: Dialect;
|
|
341
|
+
}): void;
|
|
327
342
|
/**
|
|
328
343
|
* Merge two declaration fragments for the same (extension, model) — e.g. a
|
|
329
344
|
* field-type lowering plus a model attribute. One level deep: object values
|
|
330
|
-
* merge shallowly
|
|
331
|
-
*
|
|
345
|
+
* merge shallowly. Built-in index-bearing declarations cannot repeat their
|
|
346
|
+
* field list; custom extension merging retains its existing last-wins behavior.
|
|
347
|
+
* Used identically by the parser and TS DSL.
|
|
332
348
|
*/
|
|
333
349
|
export declare function mergeExtensionDeclarations(params: {
|
|
334
350
|
base: Readonly<Record<string, unknown>>;
|
|
335
351
|
next: Readonly<Record<string, unknown>>;
|
|
352
|
+
extension?: string;
|
|
336
353
|
}): Record<string, unknown>;
|
|
337
354
|
/**
|
|
338
355
|
* Per-model declarations one extension gathered from schema attributes,
|
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* and the VibeError contract for VibeORM v2.
|
|
4
4
|
*/
|
|
5
5
|
export { SQL_NAMESPACES, assertSqlNamespace, qualifiedSqlName, sqlNamespaceIssue } from "./namespaces.ts";
|
|
6
|
+
export { assertNativeRlsAdmission } from "./native-rls-admission.ts";
|
|
7
|
+
export { modelSqlNamespace } from "./model-sql-namespace.ts";
|
|
6
8
|
export { assertIndexOperatorClasses } from "./index-operator-classes.ts";
|
|
7
9
|
export type { DatabaseModuleOptions, DatabaseModuleManifest, DatabaseModuleArtifact } from "./database-module.ts";
|
|
8
10
|
export { prepareDatabaseModuleSchema } from "./module-schema.ts";
|
|
@@ -19,7 +21,7 @@ export { READ_ONLY_REFUSAL_REASONS, ReadOnlyClientError, SCOPED_REFUSAL_REASONS,
|
|
|
19
21
|
export type { ReadOnlyRefusalReason, ScopedRefusalReason, VibeErrorCode } from "./errors.ts";
|
|
20
22
|
export { decodePrismaString, encodePrismaString, exactDecimalLiteral, exactIntegerLiteral } from "./prisma-literal.ts";
|
|
21
23
|
export { RESERVED_CLIENT_NAMES, RESERVED_DELEGATE_NAMES, isReservedClientName, isReservedDelegateName, } from "./reserved.ts";
|
|
22
|
-
export { EMPTY_EXTENSION_REGISTRY, buildExtensionRegistry, defineExtension, extensionDeclarations, extensionManifestEntry, extensionOptionsHash, mergeExtensionDeclarations, } from "./extension.ts";
|
|
24
|
+
export { assertExtensionModelPlacement, assertExtensionPlacement, EMPTY_EXTENSION_REGISTRY, buildExtensionRegistry, defineExtension, extensionDeclarations, extensionManifestEntry, extensionOptionsHash, mergeExtensionDeclarations, } from "./extension.ts";
|
|
23
25
|
export type { ExtensionArtifact, ExtensionArtifactOwner, ExtensionAttributeArgs, ExtensionAttributeValue, ExtensionClientContribution, ExtensionClientPlane, ExtensionDoctorCheck, ExtensionDoctorHook, ExtensionDoctorReport, ExtensionFieldType, ExtensionImportSpec, ExtensionManifestEntry, ExtensionMigratePlane, ExtensionModelAttribute, ExtensionModelTypeNames, ExtensionRegistry, ExtensionRuntimePlane, ExtensionSchemaContext, ExtensionSqlExecutor, ExtensionSqlPlane, ExtensionWhereOperator, SqlFragmentPart, SqlFragmentSpec, VibeExtension, } from "./extension.ts";
|
|
24
26
|
export { buildComputedManifest, validateComputedConfig } from "./computed.ts";
|
|
25
27
|
export type { ComputedConfig, ComputedFieldSpec, ComputedManifestEntry } from "./computed.ts";
|
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ var VIBE_ERROR_CODES = [
|
|
|
18
18
|
"VIBE_READ_ONLY",
|
|
19
19
|
"VIBE_RLS_CONTEXT",
|
|
20
20
|
"VIBE_PERMISSION_DENIED",
|
|
21
|
+
"VIBE_CANCELLED",
|
|
21
22
|
"VIBE_ADAPTER",
|
|
22
23
|
"VIBE_ADAPTER_CLOSED",
|
|
23
24
|
"VIBE_INTERNAL"
|
|
@@ -340,6 +341,261 @@ var DIALECT_CAPABILITIES = Object.freeze({
|
|
|
340
341
|
mysql: MYSQL_CAPABILITIES
|
|
341
342
|
});
|
|
342
343
|
|
|
344
|
+
// packages/schema/src/ir.ts
|
|
345
|
+
var DIALECTS = ["postgres", "sqlite", "mysql"];
|
|
346
|
+
function providerToDialect(params) {
|
|
347
|
+
const { provider } = params;
|
|
348
|
+
switch (provider) {
|
|
349
|
+
case "postgresql":
|
|
350
|
+
case "pglite":
|
|
351
|
+
return "postgres";
|
|
352
|
+
case "sqlite":
|
|
353
|
+
return "sqlite";
|
|
354
|
+
case "mysql":
|
|
355
|
+
return "mysql";
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
function isEnumType(fieldType) {
|
|
359
|
+
return typeof fieldType === "object" && "enum" in fieldType;
|
|
360
|
+
}
|
|
361
|
+
function fieldTypeIdentity(params) {
|
|
362
|
+
const { type } = params;
|
|
363
|
+
return isEnumType(type) ? type.namespace === undefined || type.namespace === "public" ? `enum:${type.enum}` : JSON.stringify(["enum", type.namespace, type.enum]) : type;
|
|
364
|
+
}
|
|
365
|
+
var ENUM_STORAGES = ["database", "string"];
|
|
366
|
+
function enumStorageOf(params) {
|
|
367
|
+
return params.enumDef.storage ?? "database";
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// packages/schema/src/native-rls-admission.ts
|
|
371
|
+
function assertNativeRlsAdmission(params) {
|
|
372
|
+
const { schema } = params;
|
|
373
|
+
if (schema.rls === undefined && !schema.models.some((model) => model.rls !== undefined))
|
|
374
|
+
return;
|
|
375
|
+
const dialect = params.dialect ?? providerToDialect({ provider: schema.datasource.provider });
|
|
376
|
+
if (!DIALECT_CAPABILITIES[dialect].nativeRowLevelSecurity) {
|
|
377
|
+
throw unsupportedCapability({ dialect, feature: "nativeRowLevelSecurity" });
|
|
378
|
+
}
|
|
379
|
+
for (const model of schema.models) {
|
|
380
|
+
const rule = model.rls;
|
|
381
|
+
if (rule === undefined || rule === "unprotected")
|
|
382
|
+
continue;
|
|
383
|
+
const parentName = rule !== null && typeof rule === "object" && "through" in rule ? model.relations.find((relation) => relation.name === rule.through)?.targetModel : undefined;
|
|
384
|
+
const parent = parentName === undefined ? undefined : schema.models.find((entry) => entry.name === parentName);
|
|
385
|
+
if (model.namespace !== undefined && model.namespace !== "public" || parent?.namespace !== undefined && parent.namespace !== "public") {
|
|
386
|
+
throw unsupportedCapability({
|
|
387
|
+
dialect,
|
|
388
|
+
feature: "fixedNamespaceNativeRls",
|
|
389
|
+
hint: "Native RLS protects public tables only, including deny-protected models and through-policy parents"
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// packages/schema/src/reserved.ts
|
|
395
|
+
var RESERVED_DELEGATE_NAMES = [
|
|
396
|
+
"findMany",
|
|
397
|
+
"findPage",
|
|
398
|
+
"findFirst",
|
|
399
|
+
"findUnique",
|
|
400
|
+
"findFirstOrThrow",
|
|
401
|
+
"findUniqueOrThrow",
|
|
402
|
+
"create",
|
|
403
|
+
"createMany",
|
|
404
|
+
"createManyAndReturn",
|
|
405
|
+
"update",
|
|
406
|
+
"updateMany",
|
|
407
|
+
"updateManyAndReturn",
|
|
408
|
+
"upsert",
|
|
409
|
+
"upsertMany",
|
|
410
|
+
"delete",
|
|
411
|
+
"deleteMany",
|
|
412
|
+
"count",
|
|
413
|
+
"aggregate",
|
|
414
|
+
"groupBy",
|
|
415
|
+
"fields"
|
|
416
|
+
];
|
|
417
|
+
var RESERVED_CLIENT_NAMES = [
|
|
418
|
+
"$connect",
|
|
419
|
+
"$disconnect",
|
|
420
|
+
"$transaction",
|
|
421
|
+
"$querySql",
|
|
422
|
+
"$querySqlResult",
|
|
423
|
+
"$executeSql",
|
|
424
|
+
"$queryRaw",
|
|
425
|
+
"$queryRawUnsafe",
|
|
426
|
+
"$executeRaw",
|
|
427
|
+
"$executeRawUnsafe",
|
|
428
|
+
"$on",
|
|
429
|
+
"$extends",
|
|
430
|
+
"$defineView",
|
|
431
|
+
"$withPolicy",
|
|
432
|
+
"$bypassPolicy",
|
|
433
|
+
"$scoped",
|
|
434
|
+
"$readOnly",
|
|
435
|
+
"$models",
|
|
436
|
+
"$withContext",
|
|
437
|
+
"$advisoryLock",
|
|
438
|
+
"$tryAdvisoryLock"
|
|
439
|
+
];
|
|
440
|
+
var RESERVED_COLUMN_PREFIX = "__vibe_";
|
|
441
|
+
function isReservedDelegateName(params) {
|
|
442
|
+
return RESERVED_DELEGATE_NAMES.includes(params.name);
|
|
443
|
+
}
|
|
444
|
+
function isReservedClientName(params) {
|
|
445
|
+
const withPrefix = params.name.startsWith("$") ? params.name : `$${params.name}`;
|
|
446
|
+
return RESERVED_CLIENT_NAMES.includes(withPrefix);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// packages/schema/src/extension.ts
|
|
450
|
+
function stableSerialize(value) {
|
|
451
|
+
if (value === null || typeof value !== "object")
|
|
452
|
+
return JSON.stringify(value) ?? "undefined";
|
|
453
|
+
if (Array.isArray(value))
|
|
454
|
+
return `[${value.map(stableSerialize).join(",")}]`;
|
|
455
|
+
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableSerialize(entryValue)}`);
|
|
456
|
+
return `{${entries.join(",")}}`;
|
|
457
|
+
}
|
|
458
|
+
function extensionOptionsHash(params) {
|
|
459
|
+
const text = stableSerialize(params.options);
|
|
460
|
+
let hash = 2166136261;
|
|
461
|
+
for (let index = 0;index < text.length; index += 1) {
|
|
462
|
+
hash ^= text.charCodeAt(index);
|
|
463
|
+
hash = Math.imul(hash, 16777619);
|
|
464
|
+
}
|
|
465
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
466
|
+
}
|
|
467
|
+
function extensionManifestEntry(params) {
|
|
468
|
+
const { extension } = params;
|
|
469
|
+
return {
|
|
470
|
+
name: extension.name,
|
|
471
|
+
version: extension.version,
|
|
472
|
+
optionsHash: extensionOptionsHash({ options: extension.options })
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
var EXTENSION_NAME_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
|
|
476
|
+
function defineExtension(extension) {
|
|
477
|
+
const { name } = extension;
|
|
478
|
+
if (!EXTENSION_NAME_PATTERN.test(name)) {
|
|
479
|
+
throw new VibeError({
|
|
480
|
+
code: "VIBE_CONFIG",
|
|
481
|
+
message: `extension name "${name}" is invalid \u2014 must match ${EXTENSION_NAME_PATTERN} (it becomes a client identifier)`,
|
|
482
|
+
meta: { name }
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
if (RESERVED_DELEGATE_NAMES.includes(name)) {
|
|
486
|
+
throw new VibeError({
|
|
487
|
+
code: "VIBE_CONFIG",
|
|
488
|
+
message: `extension name "${name}" collides with a core delegate method`,
|
|
489
|
+
meta: { name }
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
if (RESERVED_CLIENT_NAMES.includes(`$${name}`)) {
|
|
493
|
+
throw new VibeError({
|
|
494
|
+
code: "VIBE_CONFIG",
|
|
495
|
+
message: `extension name "${name}" collides with the core client method "$${name}"`,
|
|
496
|
+
meta: { name }
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
return Object.freeze(extension);
|
|
500
|
+
}
|
|
501
|
+
function buildExtensionRegistry(params) {
|
|
502
|
+
const byName = new Map;
|
|
503
|
+
const modelAttributes = new Map;
|
|
504
|
+
const fieldTypes = new Map;
|
|
505
|
+
for (const extension of params.extensions) {
|
|
506
|
+
if (byName.has(extension.name)) {
|
|
507
|
+
throw new VibeError({
|
|
508
|
+
code: "VIBE_CONFIG",
|
|
509
|
+
message: `duplicate extension name "${extension.name}" in the configured extension list`,
|
|
510
|
+
meta: { name: extension.name }
|
|
511
|
+
});
|
|
512
|
+
}
|
|
513
|
+
byName.set(extension.name, extension);
|
|
514
|
+
for (const attribute of extension.schema?.modelAttributes ?? []) {
|
|
515
|
+
const existing = modelAttributes.get(attribute.name);
|
|
516
|
+
if (existing !== undefined) {
|
|
517
|
+
throw new VibeError({
|
|
518
|
+
code: "VIBE_CONFIG",
|
|
519
|
+
message: `model attribute "@@${attribute.name}" is claimed by both "${existing.extension.name}" and "${extension.name}"`,
|
|
520
|
+
meta: { attribute: attribute.name }
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
modelAttributes.set(attribute.name, { extension, attribute });
|
|
524
|
+
}
|
|
525
|
+
for (const fieldType of extension.schema?.fieldTypes ?? []) {
|
|
526
|
+
const existing = fieldTypes.get(fieldType.name);
|
|
527
|
+
if (existing !== undefined) {
|
|
528
|
+
throw new VibeError({
|
|
529
|
+
code: "VIBE_CONFIG",
|
|
530
|
+
message: `field type "${fieldType.name}" is claimed by both "${existing.extension.name}" and "${extension.name}"`,
|
|
531
|
+
meta: { fieldType: fieldType.name }
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
fieldTypes.set(fieldType.name, { extension, fieldType });
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return { extensions: params.extensions, byName, modelAttributes, fieldTypes };
|
|
538
|
+
}
|
|
539
|
+
var EMPTY_EXTENSION_REGISTRY = buildExtensionRegistry({
|
|
540
|
+
extensions: []
|
|
541
|
+
});
|
|
542
|
+
var PUBLIC_ONLY_EXTENSIONS = ["search", "vector", "trgm"];
|
|
543
|
+
function hasPublicExtensionDeclaration(params) {
|
|
544
|
+
return PUBLIC_ONLY_EXTENSIONS.some((name) => Object.hasOwn(extensionDeclarations({ schema: params.schema, name }), params.model));
|
|
545
|
+
}
|
|
546
|
+
function assertExtensionModelPlacement(params) {
|
|
547
|
+
if (params.namespace !== undefined && params.namespace !== "public") {
|
|
548
|
+
throw unsupportedCapability({ dialect: params.dialect ?? "postgres", feature: "fixedNamespaceExtensions", hint: "Search, vector and trigram extensions currently require public table placement" });
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function assertExtensionPlacement(params) {
|
|
552
|
+
const dialect = params.dialect ?? providerToDialect({ provider: params.schema.datasource.provider });
|
|
553
|
+
for (const extension of params.schema.extensions) {
|
|
554
|
+
if (!PUBLIC_ONLY_EXTENSIONS.includes(extension.name))
|
|
555
|
+
continue;
|
|
556
|
+
const declarations = extensionDeclarations({ schema: params.schema, name: extension.name });
|
|
557
|
+
for (const model of params.schema.models) {
|
|
558
|
+
if (Object.hasOwn(declarations, model.name))
|
|
559
|
+
assertExtensionModelPlacement({ namespace: model.namespace, dialect });
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
function isPlainObject(value) {
|
|
564
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
565
|
+
}
|
|
566
|
+
function mergeExtensionDeclarations(params) {
|
|
567
|
+
if (params.extension !== undefined && PUBLIC_ONLY_EXTENSIONS.includes(params.extension) && Array.isArray(params.base.fields) && Array.isArray(params.next.fields)) {
|
|
568
|
+
throw new VibeError({ code: "VIBE_SCHEMA_INVALID", message: `Repeated index-bearing ${params.extension} declarations are unsupported; use one common-configuration multi-field declaration`, meta: { rule: "repeated-extension-declaration", extension: params.extension } });
|
|
569
|
+
}
|
|
570
|
+
const out = { ...params.base };
|
|
571
|
+
for (const [key, value] of Object.entries(params.next)) {
|
|
572
|
+
const existing = out[key];
|
|
573
|
+
out[key] = isPlainObject(existing) && isPlainObject(value) ? { ...existing, ...value } : value;
|
|
574
|
+
}
|
|
575
|
+
return out;
|
|
576
|
+
}
|
|
577
|
+
function extensionDeclarations(params) {
|
|
578
|
+
const decl = params.schema.extensions.find((entry) => entry.name === params.name);
|
|
579
|
+
if (decl === undefined)
|
|
580
|
+
return {};
|
|
581
|
+
const models = decl.options["models"];
|
|
582
|
+
if (models === null || typeof models !== "object" || Array.isArray(models))
|
|
583
|
+
return {};
|
|
584
|
+
return models;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// packages/schema/src/model-sql-namespace.ts
|
|
588
|
+
function modelSqlNamespace(params) {
|
|
589
|
+
const { schema, model } = params;
|
|
590
|
+
if (model.namespace !== undefined || (params.dialect ?? providerToDialect({ provider: schema.datasource.provider })) !== "postgres")
|
|
591
|
+
return model.namespace;
|
|
592
|
+
const protectedModel = model.rls !== undefined && model.rls !== "unprotected";
|
|
593
|
+
const throughParent = schema.models.some((child) => {
|
|
594
|
+
const rule = child.rls;
|
|
595
|
+
return rule !== null && typeof rule === "object" && "through" in rule && child.relations.some((relation) => relation.name === rule.through && relation.targetModel === model.name);
|
|
596
|
+
});
|
|
597
|
+
return protectedModel || throughParent || hasPublicExtensionDeclaration({ schema, model: model.name }) ? SQL_NAMESPACES.postgres : undefined;
|
|
598
|
+
}
|
|
343
599
|
// packages/schema/src/migration-objects.ts
|
|
344
600
|
function sqlObjectKey(params) {
|
|
345
601
|
const object = params.identity;
|
|
@@ -539,32 +795,6 @@ function assertIndexOperatorClasses(params) {
|
|
|
539
795
|
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 } });
|
|
540
796
|
}
|
|
541
797
|
}
|
|
542
|
-
// packages/schema/src/ir.ts
|
|
543
|
-
var DIALECTS = ["postgres", "sqlite", "mysql"];
|
|
544
|
-
function providerToDialect(params) {
|
|
545
|
-
const { provider } = params;
|
|
546
|
-
switch (provider) {
|
|
547
|
-
case "postgresql":
|
|
548
|
-
case "pglite":
|
|
549
|
-
return "postgres";
|
|
550
|
-
case "sqlite":
|
|
551
|
-
return "sqlite";
|
|
552
|
-
case "mysql":
|
|
553
|
-
return "mysql";
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
function isEnumType(fieldType) {
|
|
557
|
-
return typeof fieldType === "object" && "enum" in fieldType;
|
|
558
|
-
}
|
|
559
|
-
function fieldTypeIdentity(params) {
|
|
560
|
-
const { type } = params;
|
|
561
|
-
return isEnumType(type) ? type.namespace === undefined || type.namespace === "public" ? `enum:${type.enum}` : JSON.stringify(["enum", type.namespace, type.enum]) : type;
|
|
562
|
-
}
|
|
563
|
-
var ENUM_STORAGES = ["database", "string"];
|
|
564
|
-
function enumStorageOf(params) {
|
|
565
|
-
return params.enumDef.storage ?? "database";
|
|
566
|
-
}
|
|
567
|
-
|
|
568
798
|
// packages/schema/src/module-schema.ts
|
|
569
799
|
function prepareDatabaseModuleSchema(params) {
|
|
570
800
|
const { schema, module } = params;
|
|
@@ -865,173 +1095,6 @@ function exactDecimalLiteral(params) {
|
|
|
865
1095
|
const mantissa = text.replace(/[eE].*$/, "").replace("-", "").replace(".", "").replace(/^0+/, "");
|
|
866
1096
|
return mantissa.length <= 15 ? Number(text) : text;
|
|
867
1097
|
}
|
|
868
|
-
// packages/schema/src/reserved.ts
|
|
869
|
-
var RESERVED_DELEGATE_NAMES = [
|
|
870
|
-
"findMany",
|
|
871
|
-
"findPage",
|
|
872
|
-
"findFirst",
|
|
873
|
-
"findUnique",
|
|
874
|
-
"findFirstOrThrow",
|
|
875
|
-
"findUniqueOrThrow",
|
|
876
|
-
"create",
|
|
877
|
-
"createMany",
|
|
878
|
-
"createManyAndReturn",
|
|
879
|
-
"update",
|
|
880
|
-
"updateMany",
|
|
881
|
-
"updateManyAndReturn",
|
|
882
|
-
"upsert",
|
|
883
|
-
"upsertMany",
|
|
884
|
-
"delete",
|
|
885
|
-
"deleteMany",
|
|
886
|
-
"count",
|
|
887
|
-
"aggregate",
|
|
888
|
-
"groupBy",
|
|
889
|
-
"fields"
|
|
890
|
-
];
|
|
891
|
-
var RESERVED_CLIENT_NAMES = [
|
|
892
|
-
"$connect",
|
|
893
|
-
"$disconnect",
|
|
894
|
-
"$transaction",
|
|
895
|
-
"$querySql",
|
|
896
|
-
"$querySqlResult",
|
|
897
|
-
"$executeSql",
|
|
898
|
-
"$queryRaw",
|
|
899
|
-
"$queryRawUnsafe",
|
|
900
|
-
"$executeRaw",
|
|
901
|
-
"$executeRawUnsafe",
|
|
902
|
-
"$on",
|
|
903
|
-
"$extends",
|
|
904
|
-
"$defineView",
|
|
905
|
-
"$withPolicy",
|
|
906
|
-
"$bypassPolicy",
|
|
907
|
-
"$scoped",
|
|
908
|
-
"$readOnly",
|
|
909
|
-
"$models",
|
|
910
|
-
"$withContext",
|
|
911
|
-
"$advisoryLock",
|
|
912
|
-
"$tryAdvisoryLock"
|
|
913
|
-
];
|
|
914
|
-
var RESERVED_COLUMN_PREFIX = "__vibe_";
|
|
915
|
-
function isReservedDelegateName(params) {
|
|
916
|
-
return RESERVED_DELEGATE_NAMES.includes(params.name);
|
|
917
|
-
}
|
|
918
|
-
function isReservedClientName(params) {
|
|
919
|
-
const withPrefix = params.name.startsWith("$") ? params.name : `$${params.name}`;
|
|
920
|
-
return RESERVED_CLIENT_NAMES.includes(withPrefix);
|
|
921
|
-
}
|
|
922
|
-
// packages/schema/src/extension.ts
|
|
923
|
-
function stableSerialize(value) {
|
|
924
|
-
if (value === null || typeof value !== "object")
|
|
925
|
-
return JSON.stringify(value) ?? "undefined";
|
|
926
|
-
if (Array.isArray(value))
|
|
927
|
-
return `[${value.map(stableSerialize).join(",")}]`;
|
|
928
|
-
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([key, entryValue]) => `${JSON.stringify(key)}:${stableSerialize(entryValue)}`);
|
|
929
|
-
return `{${entries.join(",")}}`;
|
|
930
|
-
}
|
|
931
|
-
function extensionOptionsHash(params) {
|
|
932
|
-
const text = stableSerialize(params.options);
|
|
933
|
-
let hash = 2166136261;
|
|
934
|
-
for (let index = 0;index < text.length; index += 1) {
|
|
935
|
-
hash ^= text.charCodeAt(index);
|
|
936
|
-
hash = Math.imul(hash, 16777619);
|
|
937
|
-
}
|
|
938
|
-
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
939
|
-
}
|
|
940
|
-
function extensionManifestEntry(params) {
|
|
941
|
-
const { extension } = params;
|
|
942
|
-
return {
|
|
943
|
-
name: extension.name,
|
|
944
|
-
version: extension.version,
|
|
945
|
-
optionsHash: extensionOptionsHash({ options: extension.options })
|
|
946
|
-
};
|
|
947
|
-
}
|
|
948
|
-
var EXTENSION_NAME_PATTERN = /^[a-z][a-zA-Z0-9]*$/;
|
|
949
|
-
function defineExtension(extension) {
|
|
950
|
-
const { name } = extension;
|
|
951
|
-
if (!EXTENSION_NAME_PATTERN.test(name)) {
|
|
952
|
-
throw new VibeError({
|
|
953
|
-
code: "VIBE_CONFIG",
|
|
954
|
-
message: `extension name "${name}" is invalid \u2014 must match ${EXTENSION_NAME_PATTERN} (it becomes a client identifier)`,
|
|
955
|
-
meta: { name }
|
|
956
|
-
});
|
|
957
|
-
}
|
|
958
|
-
if (RESERVED_DELEGATE_NAMES.includes(name)) {
|
|
959
|
-
throw new VibeError({
|
|
960
|
-
code: "VIBE_CONFIG",
|
|
961
|
-
message: `extension name "${name}" collides with a core delegate method`,
|
|
962
|
-
meta: { name }
|
|
963
|
-
});
|
|
964
|
-
}
|
|
965
|
-
if (RESERVED_CLIENT_NAMES.includes(`$${name}`)) {
|
|
966
|
-
throw new VibeError({
|
|
967
|
-
code: "VIBE_CONFIG",
|
|
968
|
-
message: `extension name "${name}" collides with the core client method "$${name}"`,
|
|
969
|
-
meta: { name }
|
|
970
|
-
});
|
|
971
|
-
}
|
|
972
|
-
return Object.freeze(extension);
|
|
973
|
-
}
|
|
974
|
-
function buildExtensionRegistry(params) {
|
|
975
|
-
const byName = new Map;
|
|
976
|
-
const modelAttributes = new Map;
|
|
977
|
-
const fieldTypes = new Map;
|
|
978
|
-
for (const extension of params.extensions) {
|
|
979
|
-
if (byName.has(extension.name)) {
|
|
980
|
-
throw new VibeError({
|
|
981
|
-
code: "VIBE_CONFIG",
|
|
982
|
-
message: `duplicate extension name "${extension.name}" in the configured extension list`,
|
|
983
|
-
meta: { name: extension.name }
|
|
984
|
-
});
|
|
985
|
-
}
|
|
986
|
-
byName.set(extension.name, extension);
|
|
987
|
-
for (const attribute of extension.schema?.modelAttributes ?? []) {
|
|
988
|
-
const existing = modelAttributes.get(attribute.name);
|
|
989
|
-
if (existing !== undefined) {
|
|
990
|
-
throw new VibeError({
|
|
991
|
-
code: "VIBE_CONFIG",
|
|
992
|
-
message: `model attribute "@@${attribute.name}" is claimed by both "${existing.extension.name}" and "${extension.name}"`,
|
|
993
|
-
meta: { attribute: attribute.name }
|
|
994
|
-
});
|
|
995
|
-
}
|
|
996
|
-
modelAttributes.set(attribute.name, { extension, attribute });
|
|
997
|
-
}
|
|
998
|
-
for (const fieldType of extension.schema?.fieldTypes ?? []) {
|
|
999
|
-
const existing = fieldTypes.get(fieldType.name);
|
|
1000
|
-
if (existing !== undefined) {
|
|
1001
|
-
throw new VibeError({
|
|
1002
|
-
code: "VIBE_CONFIG",
|
|
1003
|
-
message: `field type "${fieldType.name}" is claimed by both "${existing.extension.name}" and "${extension.name}"`,
|
|
1004
|
-
meta: { fieldType: fieldType.name }
|
|
1005
|
-
});
|
|
1006
|
-
}
|
|
1007
|
-
fieldTypes.set(fieldType.name, { extension, fieldType });
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
return { extensions: params.extensions, byName, modelAttributes, fieldTypes };
|
|
1011
|
-
}
|
|
1012
|
-
var EMPTY_EXTENSION_REGISTRY = buildExtensionRegistry({
|
|
1013
|
-
extensions: []
|
|
1014
|
-
});
|
|
1015
|
-
function isPlainObject(value) {
|
|
1016
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1017
|
-
}
|
|
1018
|
-
function mergeExtensionDeclarations(params) {
|
|
1019
|
-
const out = { ...params.base };
|
|
1020
|
-
for (const [key, value] of Object.entries(params.next)) {
|
|
1021
|
-
const existing = out[key];
|
|
1022
|
-
out[key] = isPlainObject(existing) && isPlainObject(value) ? { ...existing, ...value } : value;
|
|
1023
|
-
}
|
|
1024
|
-
return out;
|
|
1025
|
-
}
|
|
1026
|
-
function extensionDeclarations(params) {
|
|
1027
|
-
const decl = params.schema.extensions.find((entry) => entry.name === params.name);
|
|
1028
|
-
if (decl === undefined)
|
|
1029
|
-
return {};
|
|
1030
|
-
const models = decl.options["models"];
|
|
1031
|
-
if (models === null || typeof models !== "object" || Array.isArray(models))
|
|
1032
|
-
return {};
|
|
1033
|
-
return models;
|
|
1034
|
-
}
|
|
1035
1098
|
// packages/schema/src/computed.ts
|
|
1036
1099
|
function buildComputedManifest(params) {
|
|
1037
1100
|
const { computed } = params;
|
|
@@ -2401,6 +2464,8 @@ function validatePredicateFields(params) {
|
|
|
2401
2464
|
}
|
|
2402
2465
|
function assertValidSchemaIR(params) {
|
|
2403
2466
|
const dialect = providerToDialect({ provider: params.schema.datasource.provider });
|
|
2467
|
+
assertNativeRlsAdmission({ schema: params.schema, dialect });
|
|
2468
|
+
assertExtensionPlacement({ schema: params.schema, dialect });
|
|
2404
2469
|
assertIndexOperatorClasses({ schema: params.schema, dialect });
|
|
2405
2470
|
for (const entry of [...params.schema.models, ...params.schema.enums])
|
|
2406
2471
|
if (entry.namespace !== undefined)
|
|
@@ -2448,9 +2513,6 @@ function assertValidSchemaIR(params) {
|
|
|
2448
2513
|
}
|
|
2449
2514
|
if (params.schema.migrationObjects !== undefined)
|
|
2450
2515
|
assertMigrationObjects({ objects: params.schema.migrationObjects });
|
|
2451
|
-
if (params.schema.rls !== undefined && !DIALECT_CAPABILITIES[dialect].nativeRowLevelSecurity) {
|
|
2452
|
-
throw unsupportedCapability({ dialect, feature: "nativeRowLevelSecurity" });
|
|
2453
|
-
}
|
|
2454
2516
|
for (const model of params.schema.models) {
|
|
2455
2517
|
const counters = model.fields.filter((field) => field.default?.kind === "autoincrement");
|
|
2456
2518
|
if (dialect === "mysql" && counters.length > 1)
|
|
@@ -2696,10 +2758,13 @@ export {
|
|
|
2696
2758
|
ScopedQueryError,
|
|
2697
2759
|
VIBE_ERROR_CODES,
|
|
2698
2760
|
VibeError,
|
|
2761
|
+
assertExtensionModelPlacement,
|
|
2762
|
+
assertExtensionPlacement,
|
|
2699
2763
|
assertIdentifierFits,
|
|
2700
2764
|
assertIndexOperatorClasses,
|
|
2701
2765
|
assertJsonSchemaRef,
|
|
2702
2766
|
assertMigrationObjects,
|
|
2767
|
+
assertNativeRlsAdmission,
|
|
2703
2768
|
assertPredicateCapabilities,
|
|
2704
2769
|
assertSqlNamespace,
|
|
2705
2770
|
assertValidSchemaIR,
|
|
@@ -2737,6 +2802,7 @@ export {
|
|
|
2737
2802
|
jsonTypeAnnotationIssue,
|
|
2738
2803
|
jsonTypeAnnotationRef,
|
|
2739
2804
|
mergeExtensionDeclarations,
|
|
2805
|
+
modelSqlNamespace,
|
|
2740
2806
|
normalizeJsonSchemaRef,
|
|
2741
2807
|
postgresGeneratedObjectName,
|
|
2742
2808
|
predicateFieldNames,
|
|
@@ -2756,4 +2822,4 @@ export {
|
|
|
2756
2822
|
validateSchemaIR
|
|
2757
2823
|
};
|
|
2758
2824
|
|
|
2759
|
-
//# debugId=
|
|
2825
|
+
//# debugId=721E026706902A1F64756E2164756E21
|