@vibeorm/schema 5.6.0 → 5.8.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 +5 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +41 -2
- package/dist/index.js.map +6 -6
- package/dist/ir.d.ts +10 -0
- package/dist/referential-actions.d.ts +11 -0
- package/package.json +1 -1
package/dist/errors.d.ts
CHANGED
|
@@ -194,7 +194,11 @@ export declare const SCOPED_REFUSAL_REASONS: readonly [
|
|
|
194
194
|
/** A create leaves out a column a list scope restricts — the handle never picks a value from the list. */
|
|
195
195
|
"scope_value_required",
|
|
196
196
|
/** `$scoped({ scopes: {} })` — the named form binds at least one scope. */
|
|
197
|
-
"no_scope_bound"
|
|
197
|
+
"no_scope_bound",
|
|
198
|
+
/** A { through } relation's effective onDelete/onUpdate is SetDefault, or SetNull on an all-nullable key: the database could detach the row from its parent. */
|
|
199
|
+
"through_detachable",
|
|
200
|
+
/** A { column } entry's column is part of a foreign key declaring SetDefault: the database could write another tenant's value into it. */
|
|
201
|
+
"column_set_default"];
|
|
198
202
|
export type ScopedRefusalReason = (typeof SCOPED_REFUSAL_REASONS)[number];
|
|
199
203
|
/**
|
|
200
204
|
* A refusal from a tenant-scoped handle (`$scoped`). Carries the model, the
|
package/dist/index.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export { PREDICATE_COMPARISON_OPS, assertPredicateCapabilities, inspectPredicate
|
|
|
21
21
|
export type { PredicateBooleanUsage, PredicateCapabilityInput, PredicateCapabilityIssue, PredicateCapabilityTable, PredicateComparisonOp, PredicateExpression, PredicateFieldRef, PredicateInspection, PredicateIR, PredicateNodeKind, PredicateNumericType, PredicateOperand, PredicateResultType, PredicateUsage, PredicateValue } from "./predicate.ts";
|
|
22
22
|
export { CHECK_RULE_GROUP_MAX, checkRulePredicate } from "./check-rules.ts";
|
|
23
23
|
export type { CheckRule, CheckRuleGroupKind, CheckRuleLiteral } from "./check-rules.ts";
|
|
24
|
-
export { resolveOnDelete, resolveOnUpdate, resolveReferentialActions } from "./referential-actions.ts";
|
|
24
|
+
export { resolveOnDelete, resolveOnUpdate, resolveReferentialActions, setDefaultRelationOf } from "./referential-actions.ts";
|
|
25
25
|
export { assertMigrationObjects, isSqlObjectDefinition, isSqlObjectIdentity, sqlObjectKey, sqlObjectNamespace } from "./migration-objects.ts";
|
|
26
26
|
export type { MigrationObjectIR, QualifiedSqlName, SqlObjectDefinition, SqlObjectDefinitionFormat, SqlObjectFacts, SqlObjectIdentity } from "./migration-objects.ts";
|
|
27
27
|
export type { AdvisoryLockMode, DialectCapabilities, FullTextEngine, RowLockStrength, RowLockWaitPolicy, TimeOfDayType, } from "./capabilities.ts";
|
package/dist/index.js
CHANGED
|
@@ -109,7 +109,9 @@ var SCOPED_REFUSAL_REASONS = [
|
|
|
109
109
|
"conflict_target_unscoped",
|
|
110
110
|
"model_denied",
|
|
111
111
|
"scope_value_required",
|
|
112
|
-
"no_scope_bound"
|
|
112
|
+
"no_scope_bound",
|
|
113
|
+
"through_detachable",
|
|
114
|
+
"column_set_default"
|
|
113
115
|
];
|
|
114
116
|
|
|
115
117
|
class ScopedQueryError extends VibeError {
|
|
@@ -1596,6 +1598,9 @@ function resolveReferentialActions(params) {
|
|
|
1596
1598
|
const required = (relation.fields ?? []).every((name) => params.field(name)?.isOptional !== true);
|
|
1597
1599
|
return { onDelete: resolveOnDelete({ explicit: relation.onDelete, required }), onUpdate: resolveOnUpdate({ explicit: relation.onUpdate }) };
|
|
1598
1600
|
}
|
|
1601
|
+
function setDefaultRelationOf(params) {
|
|
1602
|
+
return params.relations.find((relation) => !relation.isList && (relation.fields ?? []).includes(params.field) && (relation.onDelete === "SetDefault" || relation.onUpdate === "SetDefault"));
|
|
1603
|
+
}
|
|
1599
1604
|
|
|
1600
1605
|
// packages/schema/src/generated-columns.ts
|
|
1601
1606
|
function declarationIssue(params) {
|
|
@@ -2855,6 +2860,11 @@ var RESERVED_ENUM_NAMES = [
|
|
|
2855
2860
|
"IncludedRelation",
|
|
2856
2861
|
"IncludedCount",
|
|
2857
2862
|
"ViewDelegateMap",
|
|
2863
|
+
"__VibeObjects",
|
|
2864
|
+
"__VibeKeys",
|
|
2865
|
+
"__VibeAt",
|
|
2866
|
+
"__VibeItem",
|
|
2867
|
+
"__VibeExact",
|
|
2858
2868
|
"extensionManifest",
|
|
2859
2869
|
"extensionMounts",
|
|
2860
2870
|
"computedManifest",
|
|
@@ -2874,6 +2884,7 @@ var RESERVED_ENUM_NAMES = [
|
|
|
2874
2884
|
"Date",
|
|
2875
2885
|
"Uint8Array",
|
|
2876
2886
|
"schema",
|
|
2887
|
+
"jsonSchemas",
|
|
2877
2888
|
"VibeClient",
|
|
2878
2889
|
"VibeClientInstance",
|
|
2879
2890
|
"VibeQueryPromise",
|
|
@@ -3932,6 +3943,14 @@ function validateModel(params) {
|
|
|
3932
3943
|
field: field.name
|
|
3933
3944
|
});
|
|
3934
3945
|
}
|
|
3946
|
+
if (field.bigIntMode !== undefined && (field.type !== "BigInt" || field.bigIntMode !== "number")) {
|
|
3947
|
+
issues.push({
|
|
3948
|
+
rule: "bigint-mode",
|
|
3949
|
+
message: `@number on "${model.name}.${field.name}" \u2014 a number read mode is only valid on BigInt fields, and its one value is "number"`,
|
|
3950
|
+
model: model.name,
|
|
3951
|
+
field: field.name
|
|
3952
|
+
});
|
|
3953
|
+
}
|
|
3935
3954
|
}
|
|
3936
3955
|
if (model.policy !== undefined) {
|
|
3937
3956
|
const policyField = model.fields.find((field) => field.name === model.policy?.field);
|
|
@@ -3955,6 +3974,16 @@ function validateModel(params) {
|
|
|
3955
3974
|
model: model.name,
|
|
3956
3975
|
field: policyField.name
|
|
3957
3976
|
});
|
|
3977
|
+
} else {
|
|
3978
|
+
const repointing = setDefaultRelationOf({ relations: model.relations, field: policyField.name });
|
|
3979
|
+
if (repointing !== undefined) {
|
|
3980
|
+
issues.push({
|
|
3981
|
+
rule: "policy-set-default",
|
|
3982
|
+
message: `@@policy field "${model.name}.${policyField.name}" is a foreign key the database can re-point: relation "${repointing.name}" declares SetDefault, so deleting or re-keying its ${repointing.targetModel} writes the column's default, which can be another tenant's value. Declare onDelete and onUpdate as Cascade, Restrict or NoAction`,
|
|
3983
|
+
model: model.name,
|
|
3984
|
+
field: policyField.name
|
|
3985
|
+
});
|
|
3986
|
+
}
|
|
3958
3987
|
}
|
|
3959
3988
|
}
|
|
3960
3989
|
const checkNames = new Set;
|
|
@@ -4448,6 +4477,10 @@ function validateNativeRls(params) {
|
|
|
4448
4477
|
else if (slot.type !== field.type)
|
|
4449
4478
|
add({ rule: "rls-context-type-mismatch", message: `Context ${ref.context} does not match ${model.name}.${field.name}`, field: field.name });
|
|
4450
4479
|
}
|
|
4480
|
+
const repointing = field === undefined ? undefined : setDefaultRelationOf({ relations: model.relations, field: field.name });
|
|
4481
|
+
if (repointing !== undefined) {
|
|
4482
|
+
add({ rule: "rls-where-set-default", message: `Native RLS ownership column ${model.name}.${String(fieldName)} is a foreign key the database can re-point: relation "${repointing.name}" declares SetDefault, and referential actions bypass row security, so deleting or re-keying its ${repointing.targetModel} writes the column's default, which can be another tenant's value. Declare onDelete and onUpdate as Cascade, Restrict, NoAction or SetNull`, field: String(fieldName) });
|
|
4483
|
+
}
|
|
4451
4484
|
}
|
|
4452
4485
|
continue;
|
|
4453
4486
|
}
|
|
@@ -4462,6 +4495,11 @@ function validateNativeRls(params) {
|
|
|
4462
4495
|
add({ rule: "rls-nullable-through", message: "Native RLS through requires a non-nullable parent relation and foreign key; NULL parent links cannot satisfy WITH CHECK. Make the link required or use a direct ownership rule", field: relation.name });
|
|
4463
4496
|
continue;
|
|
4464
4497
|
}
|
|
4498
|
+
const actions = resolveReferentialActions({ relation, field: (name) => model.fields.find((field) => field.name === name) });
|
|
4499
|
+
if (actions.onDelete === "SetDefault" || actions.onUpdate === "SetDefault") {
|
|
4500
|
+
add({ rule: "rls-through-set-default", message: `Native RLS through requires a foreign key the database cannot re-point: ON ${actions.onDelete === "SetDefault" ? "DELETE" : "UPDATE"} SET DEFAULT moves the row to its key's default parent, which can belong to another tenant, and referential actions bypass row security. Declare onDelete and onUpdate as Cascade, Restrict or NoAction`, field: relation.name });
|
|
4501
|
+
continue;
|
|
4502
|
+
}
|
|
4465
4503
|
if (table === "parent")
|
|
4466
4504
|
add({ rule: "rls-through-alias-conflict", message: "A through table mapped to parent would shadow the fixed policy parent alias; use another table name" });
|
|
4467
4505
|
throughTargets.set(model.name, target.name);
|
|
@@ -4627,6 +4665,7 @@ export {
|
|
|
4627
4665
|
scalarListDefault,
|
|
4628
4666
|
schemaRowRuleIdentities,
|
|
4629
4667
|
serializeJsonDefault,
|
|
4668
|
+
setDefaultRelationOf,
|
|
4630
4669
|
sqlNamespaceIssue,
|
|
4631
4670
|
sqlObjectKey,
|
|
4632
4671
|
sqlObjectNamespace,
|
|
@@ -4642,4 +4681,4 @@ export {
|
|
|
4642
4681
|
validatorRefIssue
|
|
4643
4682
|
};
|
|
4644
4683
|
|
|
4645
|
-
//# debugId=
|
|
4684
|
+
//# debugId=16597011ACA85C7264756E2164756E21
|