@cerbos/orm-prisma 4.1.0 → 5.0.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/README.md +598 -603
- package/lib/collections.d.ts +30 -0
- package/lib/collections.d.ts.map +1 -0
- package/lib/collections.js +421 -0
- package/lib/collections.js.map +1 -0
- package/lib/comparison.d.ts +24 -0
- package/lib/comparison.d.ts.map +1 -0
- package/lib/comparison.js +731 -0
- package/lib/comparison.js.map +1 -0
- package/lib/errors.d.ts +13 -0
- package/lib/errors.d.ts.map +1 -0
- package/lib/errors.js +17 -0
- package/lib/errors.js.map +1 -0
- package/lib/evaluate.d.ts +5 -0
- package/lib/evaluate.d.ts.map +1 -0
- package/lib/evaluate.js +118 -0
- package/lib/evaluate.js.map +1 -0
- package/lib/fields.d.ts +34 -0
- package/lib/fields.d.ts.map +1 -0
- package/lib/fields.js +253 -0
- package/lib/fields.js.map +1 -0
- package/lib/functions.d.ts +5 -0
- package/lib/functions.d.ts.map +1 -0
- package/lib/functions.js +408 -0
- package/lib/functions.js.map +1 -0
- package/lib/hierarchy.d.ts +6 -0
- package/lib/hierarchy.d.ts.map +1 -0
- package/lib/hierarchy.js +266 -0
- package/lib/hierarchy.js.map +1 -0
- package/lib/index.d.ts +23 -13
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +34 -2555
- package/lib/index.js.map +1 -1
- package/lib/literals.d.ts +20 -0
- package/lib/literals.d.ts.map +1 -0
- package/lib/literals.js +290 -0
- package/lib/literals.js.map +1 -0
- package/lib/mapping.d.ts +95 -0
- package/lib/mapping.d.ts.map +1 -0
- package/lib/mapping.js +316 -0
- package/lib/mapping.js.map +1 -0
- package/lib/narrowing.d.ts +12 -0
- package/lib/narrowing.d.ts.map +1 -0
- package/lib/narrowing.js +32 -0
- package/lib/narrowing.js.map +1 -0
- package/lib/plan.d.ts +49 -0
- package/lib/plan.d.ts.map +1 -0
- package/lib/plan.js +86 -0
- package/lib/plan.js.map +1 -0
- package/lib/presence.d.ts +25 -0
- package/lib/presence.d.ts.map +1 -0
- package/lib/presence.js +82 -0
- package/lib/presence.js.map +1 -0
- package/lib/regex.d.ts +33 -0
- package/lib/regex.d.ts.map +1 -0
- package/lib/regex.js +480 -0
- package/lib/regex.js.map +1 -0
- package/lib/relations.d.ts +58 -0
- package/lib/relations.d.ts.map +1 -0
- package/lib/relations.js +175 -0
- package/lib/relations.js.map +1 -0
- package/lib/rewrite.d.ts +36 -0
- package/lib/rewrite.d.ts.map +1 -0
- package/lib/rewrite.js +366 -0
- package/lib/rewrite.js.map +1 -0
- package/lib/solve.d.ts +20 -0
- package/lib/solve.d.ts.map +1 -0
- package/lib/solve.js +77 -0
- package/lib/solve.js.map +1 -0
- package/lib/strings.d.ts +12 -0
- package/lib/strings.d.ts.map +1 -0
- package/lib/strings.js +110 -0
- package/lib/strings.js.map +1 -0
- package/lib/ternary.d.ts +12 -0
- package/lib/ternary.d.ts.map +1 -0
- package/lib/ternary.js +192 -0
- package/lib/ternary.js.map +1 -0
- package/lib/timestamp.d.ts +45 -0
- package/lib/timestamp.d.ts.map +1 -0
- package/lib/timestamp.js +225 -0
- package/lib/timestamp.js.map +1 -0
- package/lib/translate.d.ts +20 -0
- package/lib/translate.d.ts.map +1 -0
- package/lib/translate.js +287 -0
- package/lib/translate.js.map +1 -0
- package/lib/types.d.ts +29 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +980 -0
- package/lib/types.js.map +1 -0
- package/lib/value.d.ts +8 -0
- package/lib/value.d.ts.map +1 -0
- package/lib/value.js +7 -0
- package/lib/value.js.map +1 -0
- package/package.json +27 -17
- package/src/adversarial.test.ts +376 -1071
- package/src/collections.ts +738 -0
- package/src/comparison.ts +1157 -0
- package/src/corpus.ts +433 -250
- package/src/errors.ts +12 -0
- package/src/evaluate.ts +170 -0
- package/src/fields.ts +373 -0
- package/src/functions.ts +548 -0
- package/src/generated/prisma-adversarial/browser.ts +10 -0
- package/src/generated/prisma-adversarial/client.ts +10 -0
- package/src/generated/prisma-adversarial/commonInputTypes.ts +116 -127
- package/src/generated/prisma-adversarial/internal/class.ts +24 -4
- package/src/generated/prisma-adversarial/internal/prismaNamespace.ts +174 -3
- package/src/generated/prisma-adversarial/internal/prismaNamespaceBrowser.ts +23 -2
- package/src/generated/prisma-adversarial/models/AdversarialBoolListElement.ts +1272 -0
- package/src/generated/prisma-adversarial/models/AdversarialInner.ts +54 -54
- package/src/generated/prisma-adversarial/models/AdversarialNumberListElement.ts +1316 -0
- package/src/generated/prisma-adversarial/models/AdversarialParent.ts +66 -66
- package/src/generated/prisma-adversarial/models/AdversarialResource.ts +435 -87
- package/src/generated/prisma-adversarial/models.ts +2 -0
- package/src/generated/prisma-adversarial-mysql/browser.ts +64 -0
- package/src/generated/prisma-adversarial-mysql/client.ts +86 -0
- package/src/generated/prisma-adversarial-mysql/commonInputTypes.ts +349 -0
- package/src/generated/prisma-adversarial-mysql/enums.ts +15 -0
- package/src/generated/prisma-adversarial-mysql/internal/class.ts +284 -0
- package/src/generated/prisma-adversarial-mysql/internal/prismaNamespace.ts +1518 -0
- package/src/generated/prisma-adversarial-mysql/internal/prismaNamespaceBrowser.ts +274 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialBoolListElement.ts +1157 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialCategory.ts +1286 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialInner.ts +1247 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialLabel.ts +1157 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialNumberListElement.ts +1201 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialParent.ts +1352 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialResource.ts +2021 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialSubCategory.ts +1286 -0
- package/src/generated/prisma-adversarial-mysql/models/AdversarialTag.ts +1190 -0
- package/src/generated/prisma-adversarial-mysql/models.ts +20 -0
- package/src/generated/prisma-adversarial-mysql-v6/client.d.ts +1 -0
- package/src/generated/prisma-adversarial-mysql-v6/default.d.ts +1 -0
- package/src/generated/prisma-adversarial-mysql-v6/edge.d.ts +1 -0
- package/src/generated/prisma-adversarial-mysql-v6/index.d.ts +13822 -0
- package/src/generated/prisma-adversarial-mysql-v6/runtime/index-browser.d.ts +370 -0
- package/src/generated/prisma-adversarial-mysql-v6/runtime/library.d.ts +3982 -0
- package/src/generated/prisma-adversarial-mysql-v6/wasm.d.ts +1 -0
- package/src/generated/prisma-adversarial-pg/browser.ts +10 -0
- package/src/generated/prisma-adversarial-pg/client.ts +10 -0
- package/src/generated/prisma-adversarial-pg/commonInputTypes.ts +118 -129
- package/src/generated/prisma-adversarial-pg/internal/class.ts +24 -4
- package/src/generated/prisma-adversarial-pg/internal/prismaNamespace.ts +174 -3
- package/src/generated/prisma-adversarial-pg/internal/prismaNamespaceBrowser.ts +23 -2
- package/src/generated/prisma-adversarial-pg/models/AdversarialBoolListElement.ts +1275 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialInner.ts +54 -54
- package/src/generated/prisma-adversarial-pg/models/AdversarialNumberListElement.ts +1319 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialParent.ts +66 -66
- package/src/generated/prisma-adversarial-pg/models/AdversarialResource.ts +435 -87
- package/src/generated/prisma-adversarial-pg/models.ts +2 -0
- package/src/generated/prisma-adversarial-pg-v6/index.d.ts +3476 -388
- package/src/generated/prisma-adversarial-v6/index.d.ts +3465 -383
- package/src/hierarchy.ts +405 -0
- package/src/index.ts +66 -3646
- package/src/literals.ts +363 -0
- package/src/mapping.ts +474 -0
- package/src/narrowing.ts +33 -0
- package/src/plan.ts +125 -0
- package/src/presence.ts +114 -0
- package/src/regex.ts +596 -0
- package/src/relations.ts +226 -0
- package/src/rewrite.ts +524 -0
- package/src/solve.ts +96 -0
- package/src/strings.ts +152 -0
- package/src/ternary.ts +359 -0
- package/src/test-setup.adversarial-url.ts +6 -3
- package/src/test-setup.adversarial.mysql.v6.ts +13 -0
- package/src/test-setup.adversarial.mysql.v7.ts +12 -0
- package/src/test-setup.adversarial.v7.ts +1 -0
- package/src/timestamp.ts +308 -0
- package/src/translate.ts +419 -0
- package/src/translator.test.ts +497 -1317
- package/src/types.ts +1310 -0
- package/src/value.ts +16 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.d.ts","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":"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"}
|
package/lib/mapping.js
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Resolving plan references through the caller's mapper, and the per-translation state that
|
|
3
|
+
// resolution reads: the mapper in scope, the root model, the null convention and the lambda scopes.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.currentScope = currentScope;
|
|
6
|
+
exports.isResolvedFieldReference = isResolvedFieldReference;
|
|
7
|
+
exports.isResolvedValue = isResolvedValue;
|
|
8
|
+
exports.isExplicitNullReference = isExplicitNullReference;
|
|
9
|
+
exports.getLeafField = getLeafField;
|
|
10
|
+
exports.lookupMapping = lookupMapping;
|
|
11
|
+
exports.lookupElementFields = lookupElementFields;
|
|
12
|
+
exports.namesToOneRelation = namesToOneRelation;
|
|
13
|
+
exports.resolveFieldReference = resolveFieldReference;
|
|
14
|
+
exports.recordErroringNullElement = recordErroringNullElement;
|
|
15
|
+
exports.enterLambdaScope = enterLambdaScope;
|
|
16
|
+
exports.assertNullOperandTranslatable = assertNullOperandTranslatable;
|
|
17
|
+
exports.assertStructuralNulls = assertStructuralNulls;
|
|
18
|
+
const plan_1 = require("./plan");
|
|
19
|
+
const errors_1 = require("./errors");
|
|
20
|
+
const narrowing_1 = require("./narrowing");
|
|
21
|
+
/** The innermost lambda scope, if the translation is inside one. */
|
|
22
|
+
function currentScope(context) {
|
|
23
|
+
return context.scopes[context.scopes.length - 1];
|
|
24
|
+
}
|
|
25
|
+
function isResolvedFieldReference(operand) {
|
|
26
|
+
return "path" in operand;
|
|
27
|
+
}
|
|
28
|
+
function isResolvedValue(operand) {
|
|
29
|
+
return "value" in operand;
|
|
30
|
+
}
|
|
31
|
+
/** Whether this reference is one the caller sends as an explicit `null` when the column is NULL. */
|
|
32
|
+
function isExplicitNullReference(fieldRef) {
|
|
33
|
+
return fieldRef.nullAttributeRepresentation === "explicit";
|
|
34
|
+
}
|
|
35
|
+
function getLeafField(path) {
|
|
36
|
+
const fieldName = path[path.length - 1];
|
|
37
|
+
if (!fieldName) {
|
|
38
|
+
throw new Error("Field path cannot be empty");
|
|
39
|
+
}
|
|
40
|
+
return fieldName;
|
|
41
|
+
}
|
|
42
|
+
/** The mapping the caller declared for `key`, through either mapper form. */
|
|
43
|
+
function lookupMapping(mapper, key) {
|
|
44
|
+
return (0, narrowing_1.isFunction)(mapper) ? mapper(key) : mapper[key];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The element field mappings of the collection at `collectionPath`. A chained collection
|
|
48
|
+
* (`R.attr.a.b`) has no mapping of its own, so it is found by descending from the longest mapped
|
|
49
|
+
* prefix through the nested `relation.fields`, the same walk resolveFieldReference performs.
|
|
50
|
+
*/
|
|
51
|
+
function lookupElementFields(mapper, collectionPath) {
|
|
52
|
+
const direct = lookupMapping(mapper, collectionPath);
|
|
53
|
+
if (direct) {
|
|
54
|
+
return direct.relation?.fields;
|
|
55
|
+
}
|
|
56
|
+
const parts = collectionPath.split(".");
|
|
57
|
+
for (let i = parts.length - 1; i > 0; i--) {
|
|
58
|
+
let relation = lookupMapping(mapper, parts.slice(0, i).join("."))?.relation;
|
|
59
|
+
if (!relation) {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
for (const part of parts.slice(i)) {
|
|
63
|
+
relation = relation?.fields?.[part]?.relation;
|
|
64
|
+
}
|
|
65
|
+
return relation?.fields;
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Whether `reference` names a to-one relation itself, rather than a column reached through it.
|
|
71
|
+
* CEL reads such a relation as a map whose keys are the attribute names present on the related
|
|
72
|
+
* row, and no Prisma filter enumerates those.
|
|
73
|
+
*/
|
|
74
|
+
function namesToOneRelation(mapper, reference) {
|
|
75
|
+
const direct = lookupMapping(mapper, reference)?.relation;
|
|
76
|
+
if (direct)
|
|
77
|
+
return direct.type === "one" && direct.field === undefined;
|
|
78
|
+
const parts = reference.split(".");
|
|
79
|
+
for (let i = parts.length - 1; i > 0; i--) {
|
|
80
|
+
let relation = lookupMapping(mapper, parts.slice(0, i).join("."))?.relation;
|
|
81
|
+
if (!relation) {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
for (const part of parts.slice(i)) {
|
|
85
|
+
relation = relation?.fields?.[part]?.relation;
|
|
86
|
+
}
|
|
87
|
+
return relation?.type === "one" && relation.field === undefined;
|
|
88
|
+
}
|
|
89
|
+
return false;
|
|
90
|
+
}
|
|
91
|
+
function toRelationConfig(relation) {
|
|
92
|
+
return {
|
|
93
|
+
name: relation.name,
|
|
94
|
+
type: relation.type,
|
|
95
|
+
model: relation.model,
|
|
96
|
+
field: relation.field,
|
|
97
|
+
subqueryFilter: relation.subqueryFilter,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Resolves a field reference considering relations and nested fields.
|
|
102
|
+
*/
|
|
103
|
+
function resolveFieldReference(reference, context) {
|
|
104
|
+
const parts = reference.split(".");
|
|
105
|
+
const config = lookupMapping(context.mapper, reference);
|
|
106
|
+
// Without a direct match, the longest dotted prefix that has a mapping applies.
|
|
107
|
+
let matchedPrefix = "";
|
|
108
|
+
let matchedConfig;
|
|
109
|
+
if (!config) {
|
|
110
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
111
|
+
const prefix = parts.slice(0, i + 1).join(".");
|
|
112
|
+
const prefixConfig = lookupMapping(context.mapper, prefix);
|
|
113
|
+
if (prefixConfig) {
|
|
114
|
+
matchedPrefix = prefix;
|
|
115
|
+
matchedConfig = prefixConfig;
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
const activeConfig = config ?? matchedConfig;
|
|
121
|
+
// A scalar column has no keys: reading one beneath it would compare the column itself.
|
|
122
|
+
if (!config && matchedConfig && !matchedConfig.relation) {
|
|
123
|
+
throw new errors_1.UnsupportedQueryPlanError(`"${reference}" reads "${reference.slice(matchedPrefix.length + 1)}" beneath "${matchedPrefix}", which is mapped to a scalar field; map the full reference`);
|
|
124
|
+
}
|
|
125
|
+
if (!activeConfig?.relation) {
|
|
126
|
+
return {
|
|
127
|
+
path: [activeConfig?.field || reference],
|
|
128
|
+
valueType: activeConfig?.valueType,
|
|
129
|
+
nullable: activeConfig?.nullable,
|
|
130
|
+
nullAttributeRepresentation: activeConfig?.nullAttributeRepresentation,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const { fields } = activeConfig.relation;
|
|
134
|
+
const remainingParts = matchedPrefix
|
|
135
|
+
? parts.slice(matchedPrefix.split(".").length)
|
|
136
|
+
: [];
|
|
137
|
+
const lastPart = parts[parts.length - 1];
|
|
138
|
+
// A function mapper may return a relation config for the full leaf reference.
|
|
139
|
+
// Resolve that leaf here too, before any filter is constructed.
|
|
140
|
+
let field = activeConfig.relation.field ??
|
|
141
|
+
(config && fields && lastPart
|
|
142
|
+
? (fields[lastPart]?.field ?? lastPart)
|
|
143
|
+
: undefined);
|
|
144
|
+
const relations = [toRelationConfig(activeConfig.relation)];
|
|
145
|
+
// Walk the remaining segments through nested relation mappings; the first segment that is
|
|
146
|
+
// not a relation names the leaf column.
|
|
147
|
+
let leafConfig;
|
|
148
|
+
if (fields) {
|
|
149
|
+
let currentMapper = fields;
|
|
150
|
+
let currentParts = remainingParts;
|
|
151
|
+
while (currentParts.length > 0) {
|
|
152
|
+
const currentPart = currentParts[0];
|
|
153
|
+
if (!currentPart) {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
const nextConfig = currentMapper[currentPart];
|
|
157
|
+
if (!nextConfig?.relation) {
|
|
158
|
+
const leafPart = currentParts[currentParts.length - 1];
|
|
159
|
+
if (leafPart) {
|
|
160
|
+
field = nextConfig?.field || leafPart;
|
|
161
|
+
}
|
|
162
|
+
if (currentParts.length === 1)
|
|
163
|
+
leafConfig = nextConfig;
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
relations.push(toRelationConfig(nextConfig.relation));
|
|
167
|
+
field = nextConfig.relation.field;
|
|
168
|
+
currentMapper = nextConfig.relation.fields || {};
|
|
169
|
+
currentParts = currentParts.slice(1);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
path: field ? [field] : remainingParts,
|
|
174
|
+
relations,
|
|
175
|
+
// A column reached through a relation is typed by its own entry in `relation.fields`.
|
|
176
|
+
valueType: leafConfig?.valueType ?? activeConfig.valueType,
|
|
177
|
+
nullable: leafConfig ? leafConfig.nullable : activeConfig.nullable,
|
|
178
|
+
nullAttributeRepresentation: activeConfig.nullAttributeRepresentation,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Records that the lambda body being built touches a nullable element column, so the
|
|
183
|
+
* enclosing collection operator can add its three-valued-logic guard.
|
|
184
|
+
*
|
|
185
|
+
* An element column is nullable unless its mapping says `nullable: false`. Omitting the guard is
|
|
186
|
+
* what over-grants (a negated `exists`, an `all` or a `hasIntersection` over `map` would admit
|
|
187
|
+
* rows holding a NULL element the PDP denies), so silence has to mean the safe reading. A
|
|
188
|
+
* nested relation is not a column and never gets a NULL guard.
|
|
189
|
+
*/
|
|
190
|
+
function recordNullableElementField(context, config, defaultField) {
|
|
191
|
+
if (config.nullable === false || config.relation) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
currentScope(context)?.nullableFields.add(config.field || defaultField);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Records that `operand`, the bare variable of a projected scalar list (`tagNames.all(t, ...)`),
|
|
198
|
+
* is read by a function that raises an error on a null element (`t.startsWith(...)`,
|
|
199
|
+
* `t.upperAscii()`). A null element of such a list is a VALUE, so resolving the variable records
|
|
200
|
+
* nothing; it is the function that errors, and the enclosing macro needs the same guard as for a
|
|
201
|
+
* missing element column.
|
|
202
|
+
*/
|
|
203
|
+
function recordErroringNullElement(context, operand, resolved) {
|
|
204
|
+
if (!(0, plan_1.isNamedOperand)(operand) || !isResolvedFieldReference(resolved))
|
|
205
|
+
return;
|
|
206
|
+
if (resolved.nullable === false || (resolved.relations?.length ?? 0) > 0)
|
|
207
|
+
return;
|
|
208
|
+
const scope = [...context.scopes]
|
|
209
|
+
.reverse()
|
|
210
|
+
.find((candidate) => candidate.variableName === operand.name);
|
|
211
|
+
scope?.nullableFields.add(getLeafField(resolved.path));
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* The context for a lambda body iterating `collectionPath`: `scope` is pushed, and references
|
|
215
|
+
* to the lambda variable resolve against the collection's element mapping.
|
|
216
|
+
*/
|
|
217
|
+
function enterLambdaScope(context, collectionPath, scope) {
|
|
218
|
+
const scopedContext = {
|
|
219
|
+
...context,
|
|
220
|
+
scopes: [...context.scopes, scope],
|
|
221
|
+
};
|
|
222
|
+
const { variableName } = scope;
|
|
223
|
+
const fullMapper = context.mapper;
|
|
224
|
+
const scopedMapper = (key) => {
|
|
225
|
+
if (key === variableName) {
|
|
226
|
+
const { relations } = resolveFieldReference(collectionPath, scopedContext);
|
|
227
|
+
const projection = relations?.[relations.length - 1]?.field;
|
|
228
|
+
if (projection) {
|
|
229
|
+
return {
|
|
230
|
+
field: projection,
|
|
231
|
+
valueType: lookupMapping(fullMapper, collectionPath)?.valueType ??
|
|
232
|
+
lookupElementFields(fullMapper, collectionPath)?.[projection]?.valueType,
|
|
233
|
+
// A projected list carries null values, unlike missing fields on object elements.
|
|
234
|
+
nullAttributeRepresentation: "explicit",
|
|
235
|
+
nullable: lookupElementFields(fullMapper, collectionPath)?.[projection]?.nullable,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
// A key that starts with the variable name accesses the collection element.
|
|
240
|
+
if (key.startsWith(variableName + ".")) {
|
|
241
|
+
const strippedKey = key.replace(variableName + ".", "");
|
|
242
|
+
const baseConfig = lookupElementFields(fullMapper, collectionPath);
|
|
243
|
+
if (!baseConfig) {
|
|
244
|
+
const fieldConfig = { field: strippedKey };
|
|
245
|
+
if (!strippedKey.includes(".")) {
|
|
246
|
+
recordNullableElementField(scopedContext, fieldConfig, strippedKey);
|
|
247
|
+
}
|
|
248
|
+
return fieldConfig;
|
|
249
|
+
}
|
|
250
|
+
// For nested paths, traverse the fields configuration.
|
|
251
|
+
const parts = strippedKey.split(".");
|
|
252
|
+
let currentConfig = baseConfig;
|
|
253
|
+
let field = parts[0] || strippedKey;
|
|
254
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
255
|
+
const part = parts[i];
|
|
256
|
+
const nextPart = parts[i + 1];
|
|
257
|
+
if (!part || !nextPart) {
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
const nextFields = currentConfig[part]?.relation?.fields;
|
|
261
|
+
if (!nextFields) {
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
currentConfig = nextFields;
|
|
265
|
+
field = nextPart;
|
|
266
|
+
}
|
|
267
|
+
// The field config if it exists, otherwise a default one.
|
|
268
|
+
const fieldConfig = currentConfig[field] || { field };
|
|
269
|
+
recordNullableElementField(scopedContext, fieldConfig, field);
|
|
270
|
+
return fieldConfig;
|
|
271
|
+
}
|
|
272
|
+
// Keys not referencing the collection element resolve through the enclosing mapper.
|
|
273
|
+
if ((0, narrowing_1.isFunction)(fullMapper)) {
|
|
274
|
+
return fullMapper(key);
|
|
275
|
+
}
|
|
276
|
+
return fullMapper[key] || { field: key };
|
|
277
|
+
};
|
|
278
|
+
return { ...scopedContext, mapper: scopedMapper };
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Guards every site that would emit a NULL-selecting predicate out of a `null` comparison
|
|
282
|
+
* operand.
|
|
283
|
+
*
|
|
284
|
+
* Under the `"omitted"` representation a NULL column carries no attribute, so CEL raises a
|
|
285
|
+
* missing-attribute error and `check()` denies the row — `IS NULL` would return exactly the rows
|
|
286
|
+
* the PDP refuses. The rejection is deliberately wider than the over-granting shapes: `ne(x,
|
|
287
|
+
* null)` on its own is aligned, but Prisma applies negation by wrapping (`{ NOT: ... }`) rather
|
|
288
|
+
* than by pushing it into the leaf, so a leaf cannot tell whether an enclosing `not` will flip
|
|
289
|
+
* `IS NOT NULL` back into a NULL-selecting predicate. Rejecting every null operand is correct
|
|
290
|
+
* under any nesting; narrowing it requires negation-parity tracking.
|
|
291
|
+
*/
|
|
292
|
+
function assertNullOperandTranslatable(translation, context, declared) {
|
|
293
|
+
if ((declared ?? translation.nullRepresentation) === "omitted") {
|
|
294
|
+
throw new errors_1.UnsupportedQueryPlanError(`Cannot translate ${context} under nullAttributeRepresentation "omitted": a NULL column ` +
|
|
295
|
+
"sends no attribute, so Cerbos evaluates the comparison as a missing-attribute error " +
|
|
296
|
+
"(deny) while a NULL-selecting filter would return those rows. Send NULL columns as " +
|
|
297
|
+
'explicit nulls and use "explicit", or keep this shape out of the policy.');
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Applies the null-operand guard to struct literals (`set-field` members) anywhere in the plan,
|
|
302
|
+
* under the convention of the attribute each literal is compared with.
|
|
303
|
+
*/
|
|
304
|
+
function assertStructuralNulls(operand, context, declared) {
|
|
305
|
+
if (!(0, plan_1.isOperatorOperand)(operand))
|
|
306
|
+
return;
|
|
307
|
+
const sibling = operand.operands.find(plan_1.isNamedOperand);
|
|
308
|
+
const mapping = sibling && lookupMapping(context.mapper, sibling.name);
|
|
309
|
+
const convention = mapping?.nullAttributeRepresentation ?? declared;
|
|
310
|
+
if (operand.operator === "set-field" &&
|
|
311
|
+
operand.operands.some((part) => (0, plan_1.isValueOperand)(part) && part.value === null)) {
|
|
312
|
+
assertNullOperandTranslatable(context, "a null member in a struct literal", convention);
|
|
313
|
+
}
|
|
314
|
+
operand.operands.forEach((part) => assertStructuralNulls(part, context, convention));
|
|
315
|
+
}
|
|
316
|
+
//# sourceMappingURL=mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../src/mapping.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Value } from "@cerbos/core";
|
|
2
|
+
export declare const isString: <T>(value: T) => value is T & string;
|
|
3
|
+
export declare const isNumber: <T>(value: T) => value is T & number;
|
|
4
|
+
export declare const isBoolean: <T>(value: T) => value is T & boolean;
|
|
5
|
+
export declare const isFunction: <T>(value: T) => value is Extract<T, Function>;
|
|
6
|
+
export declare const isObject: <T>(value: T) => value is T & object;
|
|
7
|
+
export declare const typeofValue: (value: Value) => "string" | "number" | "boolean" | "object";
|
|
8
|
+
export declare const lookup: <T extends object>(table: T, key: PropertyKey) => T[keyof T] | undefined;
|
|
9
|
+
export declare const single: <T>([a]: readonly T[]) => [T];
|
|
10
|
+
export declare const pair: <T>([a, b]: readonly T[]) => [T, T];
|
|
11
|
+
export declare const triple: <T>([a, b, c]: readonly T[]) => [T, T, T];
|
|
12
|
+
//# sourceMappingURL=narrowing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrowing.d.ts","sourceRoot":"","sources":["../src/narrowing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAE1C,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,KAAK,IAAI,CAAC,GAAG,MAAmC,CAAC;AAExF,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,KAAK,IAAI,CAAC,GAAG,MAAmC,CAAC;AAExF,eAAO,MAAM,SAAS,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,KAAK,IAAI,CAAC,GAAG,OAAqC,CAAC;AAE3F,eAAO,MAAM,UAAU,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,KAAK,IAAI,OAAO,CAAC,CAAC,EAAE,QAAQ,CAAgC,CAAC;AAEtG,eAAO,MAAM,QAAQ,GAAI,CAAC,EAAE,OAAO,CAAC,KAAG,KAAK,IAAI,CAAC,GAAG,MAAqD,CAAC;AAE1G,eAAO,MAAM,WAAW,GAAI,OAAO,KAAK,KAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QACsB,CAAC;AAIpG,eAAO,MAAM,MAAM,GAAI,CAAC,SAAS,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,WAAW,KAAG,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,SACtC,CAAC;AAM9C,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,KAAG,CAAC,CAAC,CAA4B,CAAC;AAE7E,eAAO,MAAM,IAAI,GAAI,CAAC,EAAE,QAAQ,SAAS,CAAC,EAAE,KAAG,CAAC,CAAC,EAAE,CAAC,CAAmD,CAAC;AAExG,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,WAAW,SAAS,CAAC,EAAE,KAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAI3D,CAAC"}
|
package/lib/narrowing.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.triple = exports.pair = exports.single = exports.lookup = exports.typeofValue = exports.isObject = exports.isFunction = exports.isBoolean = exports.isNumber = exports.isString = void 0;
|
|
4
|
+
const isString = (value) => typeof value === "string";
|
|
5
|
+
exports.isString = isString;
|
|
6
|
+
const isNumber = (value) => typeof value === "number";
|
|
7
|
+
exports.isNumber = isNumber;
|
|
8
|
+
const isBoolean = (value) => typeof value === "boolean";
|
|
9
|
+
exports.isBoolean = isBoolean;
|
|
10
|
+
const isFunction = (value) => typeof value === "function";
|
|
11
|
+
exports.isFunction = isFunction;
|
|
12
|
+
const isObject = (value) => typeof value === "object" && value !== null;
|
|
13
|
+
exports.isObject = isObject;
|
|
14
|
+
const typeofValue = (value) => (0, exports.isString)(value) ? "string" : (0, exports.isNumber)(value) ? "number" : (0, exports.isBoolean)(value) ? "boolean" : "object";
|
|
15
|
+
exports.typeofValue = typeofValue;
|
|
16
|
+
const hasKey = (table, key) => Object.hasOwn(table, key);
|
|
17
|
+
const lookup = (table, key) => hasKey(table, key) ? table[key] : undefined;
|
|
18
|
+
exports.lookup = lookup;
|
|
19
|
+
const missingOperand = () => {
|
|
20
|
+
throw new Error("An expression is missing an operand");
|
|
21
|
+
};
|
|
22
|
+
const single = ([a]) => [a ?? missingOperand()];
|
|
23
|
+
exports.single = single;
|
|
24
|
+
const pair = ([a, b]) => [a ?? missingOperand(), b ?? missingOperand()];
|
|
25
|
+
exports.pair = pair;
|
|
26
|
+
const triple = ([a, b, c]) => [
|
|
27
|
+
a ?? missingOperand(),
|
|
28
|
+
b ?? missingOperand(),
|
|
29
|
+
c ?? missingOperand(),
|
|
30
|
+
];
|
|
31
|
+
exports.triple = triple;
|
|
32
|
+
//# sourceMappingURL=narrowing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"narrowing.js","sourceRoot":"","sources":["../src/narrowing.ts"],"names":[],"mappings":";;;AAEO,MAAM,QAAQ,GAAG,CAAI,KAAQ,EAAuB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;AAA3E,QAAA,QAAQ,YAAmE;AAEjF,MAAM,QAAQ,GAAG,CAAI,KAAQ,EAAuB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC;AAA3E,QAAA,QAAQ,YAAmE;AAEjF,MAAM,SAAS,GAAG,CAAI,KAAQ,EAAwB,EAAE,CAAC,OAAO,KAAK,KAAK,SAAS,CAAC;AAA9E,QAAA,SAAS,aAAqE;AAEpF,MAAM,UAAU,GAAG,CAAI,KAAQ,EAAiC,EAAE,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC;AAAzF,QAAA,UAAU,cAA+E;AAE/F,MAAM,QAAQ,GAAG,CAAI,KAAQ,EAAuB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AAA7F,QAAA,QAAQ,YAAqF;AAEnG,MAAM,WAAW,GAAG,CAAC,KAAY,EAA8C,EAAE,CACtF,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAA,gBAAQ,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAA,iBAAS,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;AADvF,QAAA,WAAW,eAC4E;AAEpG,MAAM,MAAM,GAAG,CAAmB,KAAQ,EAAE,GAAgB,EAAkB,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAEpG,MAAM,MAAM,GAAG,CAAmB,KAAQ,EAAE,GAAgB,EAA0B,EAAE,CAC7F,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;AADjC,QAAA,MAAM,UAC2B;AAE9C,MAAM,cAAc,GAAG,GAAU,EAAE;IACjC,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;AACzD,CAAC,CAAC;AAEK,MAAM,MAAM,GAAG,CAAI,CAAC,CAAC,CAAe,EAAO,EAAE,CAAC,CAAC,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC;AAAhE,QAAA,MAAM,UAA0D;AAEtE,MAAM,IAAI,GAAG,CAAI,CAAC,CAAC,EAAE,CAAC,CAAe,EAAU,EAAE,CAAC,CAAC,CAAC,IAAI,cAAc,EAAE,EAAE,CAAC,IAAI,cAAc,EAAE,CAAC,CAAC;AAA3F,QAAA,IAAI,QAAuF;AAEjG,MAAM,MAAM,GAAG,CAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAe,EAAa,EAAE,CAAC;IAC/D,CAAC,IAAI,cAAc,EAAE;IACrB,CAAC,IAAI,cAAc,EAAE;IACrB,CAAC,IAAI,cAAc,EAAE;CACtB,CAAC;AAJW,QAAA,MAAM,UAIjB"}
|
package/lib/plan.d.ts
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { PlanExpressionOperand, Value } from "@cerbos/core";
|
|
2
|
+
export interface NamedOperand {
|
|
3
|
+
name: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ValueOperand {
|
|
6
|
+
value: Value;
|
|
7
|
+
}
|
|
8
|
+
export interface OperatorOperand {
|
|
9
|
+
operator: string;
|
|
10
|
+
operands: PlanExpressionOperand[];
|
|
11
|
+
}
|
|
12
|
+
export declare function isNamedOperand(operand: PlanExpressionOperand): operand is NamedOperand;
|
|
13
|
+
export declare function isValueOperand(operand: PlanExpressionOperand): operand is ValueOperand;
|
|
14
|
+
export declare function isOperatorOperand(operand: PlanExpressionOperand): operand is OperatorOperand;
|
|
15
|
+
export declare function assertDefined<T>(value: T | undefined, message: string): T;
|
|
16
|
+
/**
|
|
17
|
+
* Refuses an `and`/`or` with no operands. The planner never emits one, and neither empty form has
|
|
18
|
+
* a safe rendering: `{ AND: [] }` is true in Prisma and matches every row, and the constant folder
|
|
19
|
+
* would reduce an empty `and` to `true` (an unconditional filter) before the translator saw it. A
|
|
20
|
+
* malformed plan is a bug report, never a filter.
|
|
21
|
+
*/
|
|
22
|
+
export declare function assertLogicalOperands(operator: string, operands: PlanExpressionOperand[]): void;
|
|
23
|
+
/** The Prisma scalar filter each Cerbos comparison operator lowers to. */
|
|
24
|
+
export declare const CERBOS_TO_PRISMA_OPERATOR: {
|
|
25
|
+
eq: string;
|
|
26
|
+
ne: string;
|
|
27
|
+
lt: string;
|
|
28
|
+
le: string;
|
|
29
|
+
gt: string;
|
|
30
|
+
ge: string;
|
|
31
|
+
};
|
|
32
|
+
/** The Cerbos comparison operators: exactly the keys of CERBOS_TO_PRISMA_OPERATOR. */
|
|
33
|
+
export declare const COMPARISON_OPERATORS: ReadonlySet<string>;
|
|
34
|
+
/** The operator that holds when the two operands of `operator` swap sides. */
|
|
35
|
+
export declare function mirrorOperator(operator: string): string;
|
|
36
|
+
type BinaryComparison = {
|
|
37
|
+
operator: string;
|
|
38
|
+
operands: PlanExpressionOperand[];
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Normalize a binary comparison to field-side-first. The planner preserves policy source
|
|
42
|
+
* order, so a constant may precede the field it constrains (`1 < R.attr.x` arrives as
|
|
43
|
+
* `lt(value, variable)`) — swap the operands and mirror directional operators so downstream
|
|
44
|
+
* handlers can assume the field/expression side is first. Without this, value-first
|
|
45
|
+
* comparisons are silently inverted (#256).
|
|
46
|
+
*/
|
|
47
|
+
export declare function normalizeBinaryOperands(operator: string, operands: PlanExpressionOperand[]): BinaryComparison;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAIjE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,KAAK,CAAC;CACd;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,YAAY,CAEzB;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,YAAY,CAEzB;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,qBAAqB,GAC7B,OAAO,IAAI,eAAe,CAO5B;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,CAMzE;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAAE,GAChC,IAAI,CAQN;AAED,0EAA0E;AAC1E,eAAO,MAAM,yBAAyB;;;;;;;CAOJ,CAAC;AAEnC,sFAAsF;AACtF,eAAO,MAAM,oBAAoB,EAAE,WAAW,CAAC,MAAM,CAEpD,CAAC;AAUF,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,KAAK,gBAAgB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,qBAAqB,EAAE,CAAA;CAAE,CAAC;AAEhF;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,qBAAqB,EAAE,GAChC,gBAAgB,CAclB"}
|
package/lib/plan.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Reading the planner's expression tree: operand shapes, and the comparison-operator tables every
|
|
3
|
+
// handler shares.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.COMPARISON_OPERATORS = exports.CERBOS_TO_PRISMA_OPERATOR = void 0;
|
|
6
|
+
exports.isNamedOperand = isNamedOperand;
|
|
7
|
+
exports.isValueOperand = isValueOperand;
|
|
8
|
+
exports.isOperatorOperand = isOperatorOperand;
|
|
9
|
+
exports.assertDefined = assertDefined;
|
|
10
|
+
exports.assertLogicalOperands = assertLogicalOperands;
|
|
11
|
+
exports.mirrorOperator = mirrorOperator;
|
|
12
|
+
exports.normalizeBinaryOperands = normalizeBinaryOperands;
|
|
13
|
+
const errors_1 = require("./errors");
|
|
14
|
+
const narrowing_1 = require("./narrowing");
|
|
15
|
+
function isNamedOperand(operand) {
|
|
16
|
+
return "name" in operand && typeof operand.name === "string";
|
|
17
|
+
}
|
|
18
|
+
function isValueOperand(operand) {
|
|
19
|
+
return "value" in operand && operand.value !== undefined;
|
|
20
|
+
}
|
|
21
|
+
function isOperatorOperand(operand) {
|
|
22
|
+
return ("operator" in operand &&
|
|
23
|
+
typeof operand.operator === "string" &&
|
|
24
|
+
"operands" in operand &&
|
|
25
|
+
Array.isArray(operand.operands));
|
|
26
|
+
}
|
|
27
|
+
function assertDefined(value, message) {
|
|
28
|
+
if (value === undefined) {
|
|
29
|
+
throw new errors_1.UnsupportedQueryPlanError(message);
|
|
30
|
+
}
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Refuses an `and`/`or` with no operands. The planner never emits one, and neither empty form has
|
|
35
|
+
* a safe rendering: `{ AND: [] }` is true in Prisma and matches every row, and the constant folder
|
|
36
|
+
* would reduce an empty `and` to `true` (an unconditional filter) before the translator saw it. A
|
|
37
|
+
* malformed plan is a bug report, never a filter.
|
|
38
|
+
*/
|
|
39
|
+
function assertLogicalOperands(operator, operands) {
|
|
40
|
+
if ((operator === "and" || operator === "or") && operands.length === 0) {
|
|
41
|
+
throw new errors_1.UnsupportedQueryPlanError(`${operator} requires at least one operand: an empty ${operator} is not something the ` +
|
|
42
|
+
"planner emits, and translating it would produce an unconditional filter " +
|
|
43
|
+
"({ AND: [] } matches every row)");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** The Prisma scalar filter each Cerbos comparison operator lowers to. */
|
|
47
|
+
exports.CERBOS_TO_PRISMA_OPERATOR = {
|
|
48
|
+
eq: "equals",
|
|
49
|
+
ne: "not",
|
|
50
|
+
lt: "lt",
|
|
51
|
+
le: "lte",
|
|
52
|
+
gt: "gt",
|
|
53
|
+
ge: "gte",
|
|
54
|
+
};
|
|
55
|
+
/** The Cerbos comparison operators: exactly the keys of CERBOS_TO_PRISMA_OPERATOR. */
|
|
56
|
+
exports.COMPARISON_OPERATORS = new Set(Object.keys(exports.CERBOS_TO_PRISMA_OPERATOR));
|
|
57
|
+
// Directional operators mirror when their operands swap sides; symmetric operators are unchanged.
|
|
58
|
+
const MIRRORED_OPERATOR = {
|
|
59
|
+
lt: "gt",
|
|
60
|
+
gt: "lt",
|
|
61
|
+
le: "ge",
|
|
62
|
+
ge: "le",
|
|
63
|
+
};
|
|
64
|
+
/** The operator that holds when the two operands of `operator` swap sides. */
|
|
65
|
+
function mirrorOperator(operator) {
|
|
66
|
+
return (0, narrowing_1.lookup)(MIRRORED_OPERATOR, operator) ?? operator;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Normalize a binary comparison to field-side-first. The planner preserves policy source
|
|
70
|
+
* order, so a constant may precede the field it constrains (`1 < R.attr.x` arrives as
|
|
71
|
+
* `lt(value, variable)`) — swap the operands and mirror directional operators so downstream
|
|
72
|
+
* handlers can assume the field/expression side is first. Without this, value-first
|
|
73
|
+
* comparisons are silently inverted (#256).
|
|
74
|
+
*/
|
|
75
|
+
function normalizeBinaryOperands(operator, operands) {
|
|
76
|
+
const [first, second] = operands;
|
|
77
|
+
if (operands.length === 2 &&
|
|
78
|
+
first !== undefined &&
|
|
79
|
+
second !== undefined &&
|
|
80
|
+
isValueOperand(first) &&
|
|
81
|
+
!isValueOperand(second)) {
|
|
82
|
+
return { operator: mirrorOperator(operator), operands: [second, first] };
|
|
83
|
+
}
|
|
84
|
+
return { operator, operands };
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=plan.js.map
|
package/lib/plan.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../src/plan.ts"],"names":[],"mappings":";AAAA,kGAAkG;AAClG,kBAAkB;;;AAmBlB,wCAIC;AAED,wCAIC;AAED,8CASC;AAED,sCAMC;AAQD,sDAWC;AA0BD,wCAEC;AAWD,0DAiBC;AAxHD,qCAAqD;AACrD,2CAAqC;AAerC,SAAgB,cAAc,CAC5B,OAA8B;IAE9B,OAAO,MAAM,IAAI,OAAO,IAAI,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC/D,CAAC;AAED,SAAgB,cAAc,CAC5B,OAA8B;IAE9B,OAAO,OAAO,IAAI,OAAO,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,CAAC;AAC3D,CAAC;AAED,SAAgB,iBAAiB,CAC/B,OAA8B;IAE9B,OAAO,CACL,UAAU,IAAI,OAAO;QACrB,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ;QACpC,UAAU,IAAI,OAAO;QACrB,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAChC,CAAC;AACJ,CAAC;AAED,SAAgB,aAAa,CAAI,KAAoB,EAAE,OAAe;IACpE,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,kCAAyB,CAAC,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,SAAgB,qBAAqB,CACnC,QAAgB,EAChB,QAAiC;IAEjC,IAAI,CAAC,QAAQ,KAAK,KAAK,IAAI,QAAQ,KAAK,IAAI,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvE,MAAM,IAAI,kCAAyB,CACjC,GAAG,QAAQ,4CAA4C,QAAQ,wBAAwB;YACrF,0EAA0E;YAC1E,iCAAiC,CACpC,CAAC;IACJ,CAAC;AACH,CAAC;AAED,0EAA0E;AAC7D,QAAA,yBAAyB,GAAG;IACvC,EAAE,EAAE,QAAQ;IACZ,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,KAAK;IACT,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,KAAK;CACuB,CAAC;AAEnC,sFAAsF;AACzE,QAAA,oBAAoB,GAAwB,IAAI,GAAG,CAC9D,MAAM,CAAC,IAAI,CAAC,iCAAyB,CAAC,CACvC,CAAC;AAEF,kGAAkG;AAClG,MAAM,iBAAiB,GAAG;IACxB,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;IACR,EAAE,EAAE,IAAI;CACwB,CAAC;AAEnC,8EAA8E;AAC9E,SAAgB,cAAc,CAAC,QAAgB;IAC7C,OAAO,IAAA,kBAAM,EAAC,iBAAiB,EAAE,QAAQ,CAAC,IAAI,QAAQ,CAAC;AACzD,CAAC;AAID;;;;;;GAMG;AACH,SAAgB,uBAAuB,CACrC,QAAgB,EAChB,QAAiC;IAEjC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC;IAEjC,IACE,QAAQ,CAAC,MAAM,KAAK,CAAC;QACrB,KAAK,KAAK,SAAS;QACnB,MAAM,KAAK,SAAS;QACpB,cAAc,CAAC,KAAK,CAAC;QACrB,CAAC,cAAc,CAAC,MAAM,CAAC,EACvB,CAAC;QACD,OAAO,EAAE,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAAC;IAC3E,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PlanExpressionOperand } from "@cerbos/core";
|
|
2
|
+
import type { PrismaFilter } from "./index";
|
|
3
|
+
import type { TranslationContext } from "./mapping";
|
|
4
|
+
import type { OperatorOperand } from "./plan";
|
|
5
|
+
/**
|
|
6
|
+
* `has(ref)`, or `!has(ref)` when `negated`.
|
|
7
|
+
*
|
|
8
|
+
* Presence is a property of the caller's null convention, not of the column: an attribute sent as
|
|
9
|
+
* an explicit null is present, so `has()` is true for every row, and one omitted when its column
|
|
10
|
+
* is NULL is present exactly when the column is not NULL. Element columns inside a lambda follow
|
|
11
|
+
* the omitted convention unless they are declared `nullable: false`, as the collection macros
|
|
12
|
+
* already assume. A to-one relation is present when the related row exists.
|
|
13
|
+
*
|
|
14
|
+
* `has()` itself never errors on a missing value, so `!has()` is a plain NOT. The one error it can
|
|
15
|
+
* raise is selecting through a missing to-one hop, `has(parent.x)` on a parentless row, which
|
|
16
|
+
* denies under both polarities, so every hop BEFORE the tested key is required outside the NOT.
|
|
17
|
+
*/
|
|
18
|
+
export declare function handleHasOperator(operands: PlanExpressionOperand[], context: TranslationContext, negated: boolean): PrismaFilter;
|
|
19
|
+
/**
|
|
20
|
+
* `has(R.attr.x)` on an attribute sent as an explicit null is true on every row, so it becomes the
|
|
21
|
+
* constant `true` before translation. Left to the translator it lowers to `{}`, which Prisma drops
|
|
22
|
+
* from an OR or a NOT, losing the rows it admits and, negated, admitting rows it denies.
|
|
23
|
+
*/
|
|
24
|
+
export declare function constantPresence(node: OperatorOperand, context: TranslationContext): PlanExpressionOperand | undefined;
|
|
25
|
+
//# sourceMappingURL=presence.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../src/presence.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAE1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAEpD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAI9C;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,qBAAqB,EAAE,EACjC,OAAO,EAAE,kBAAkB,EAC3B,OAAO,EAAE,OAAO,GACf,YAAY,CAmCd;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,OAAO,EAAE,kBAAkB,GAC1B,qBAAqB,GAAG,SAAS,CAmBnC"}
|