@cerbos/orm-prisma 4.0.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 +611 -460
- 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 +91 -8
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +34 -2301
- 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 +31 -18
- package/src/adversarial.test.ts +488 -488
- package/src/collections.ts +738 -0
- package/src/comparison.ts +1157 -0
- package/src/corpus.ts +597 -0
- 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 +20 -0
- package/src/generated/prisma-adversarial/client.ts +20 -0
- package/src/generated/prisma-adversarial/commonInputTypes.ts +116 -127
- package/src/generated/prisma-adversarial/internal/class.ts +44 -4
- package/src/generated/prisma-adversarial/internal/prismaNamespace.ts +350 -3
- package/src/generated/prisma-adversarial/internal/prismaNamespaceBrowser.ts +49 -2
- package/src/generated/prisma-adversarial/models/AdversarialBoolListElement.ts +1272 -0
- package/src/generated/prisma-adversarial/models/AdversarialInner.ts +1369 -0
- package/src/generated/prisma-adversarial/models/AdversarialNumberListElement.ts +1316 -0
- package/src/generated/prisma-adversarial/models/AdversarialParent.ts +1474 -0
- package/src/generated/prisma-adversarial/models/AdversarialResource.ts +547 -75
- package/src/generated/prisma-adversarial/models.ts +4 -0
- package/src/generated/prisma-adversarial-mysql/browser.ts +64 -0
- package/src/generated/{prisma → prisma-adversarial-mysql}/client.ts +23 -18
- package/src/generated/{prisma → prisma-adversarial-mysql}/commonInputTypes.ts +167 -92
- 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/index.d.ts +13822 -0
- package/src/generated/prisma-adversarial-pg/browser.ts +64 -0
- package/src/generated/prisma-adversarial-pg/client.ts +86 -0
- package/src/generated/prisma-adversarial-pg/commonInputTypes.ts +345 -0
- package/src/generated/prisma-adversarial-pg/enums.ts +15 -0
- package/src/generated/prisma-adversarial-pg/internal/class.ts +284 -0
- package/src/generated/prisma-adversarial-pg/internal/prismaNamespace.ts +1542 -0
- package/src/generated/{prisma → prisma-adversarial-pg}/internal/prismaNamespaceBrowser.ts +72 -43
- package/src/generated/prisma-adversarial-pg/models/AdversarialBoolListElement.ts +1275 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialCategory.ts +1404 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialInner.ts +1371 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialLabel.ts +1275 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialNumberListElement.ts +1319 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialParent.ts +1476 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialResource.ts +2139 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialSubCategory.ts +1404 -0
- package/src/generated/prisma-adversarial-pg/models/AdversarialTag.ts +1310 -0
- package/src/generated/prisma-adversarial-pg/models.ts +20 -0
- package/src/generated/prisma-adversarial-pg-v6/client.d.ts +1 -0
- package/src/generated/prisma-adversarial-pg-v6/default.d.ts +1 -0
- package/src/generated/prisma-adversarial-pg-v6/edge.d.ts +1 -0
- package/src/generated/prisma-adversarial-pg-v6/index.d.ts +14918 -0
- package/src/generated/prisma-adversarial-pg-v6/runtime/index-browser.d.ts +370 -0
- package/src/generated/prisma-adversarial-pg-v6/runtime/library.d.ts +3982 -0
- package/src/generated/prisma-adversarial-pg-v6/wasm.d.ts +1 -0
- package/src/generated/prisma-adversarial-v6/index.d.ts +6515 -304
- package/src/hierarchy.ts +405 -0
- package/src/index.ts +134 -3325
- 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 +21 -0
- 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.pg.v6.ts +13 -0
- package/src/test-setup.adversarial.pg.v7.ts +9 -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 +766 -0
- package/src/types.ts +1310 -0
- package/src/value.ts +16 -0
- package/src/generated/prisma/browser.ts +0 -59
- package/src/generated/prisma/internal/class.ts +0 -274
- package/src/generated/prisma/internal/prismaNamespace.ts +0 -1403
- package/src/generated/prisma/models/Category.ts +0 -1412
- package/src/generated/prisma/models/Label.ts +0 -1276
- package/src/generated/prisma/models/NestedResource.ts +0 -1562
- package/src/generated/prisma/models/NextLevelNestedResource.ts +0 -1337
- package/src/generated/prisma/models/Resource.ts +0 -2196
- package/src/generated/prisma/models/SubCategory.ts +0 -1412
- package/src/generated/prisma/models/Tag.ts +0 -1276
- package/src/generated/prisma/models/User.ts +0 -1503
- package/src/generated/prisma/models.ts +0 -19
- package/src/generated/prisma-v6/index.d.ts +0 -13726
- package/src/index.test.ts +0 -8885
- package/src/test-setup.v6.ts +0 -5
- package/src/test-setup.v7.ts +0 -7
- /package/src/generated/{prisma → prisma-adversarial-mysql}/enums.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/client.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/default.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/edge.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/runtime/index-browser.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/runtime/library.d.ts +0 -0
- /package/src/generated/{prisma-v6 → prisma-adversarial-mysql-v6}/wasm.d.ts +0 -0
package/lib/relations.js
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Nesting a filter through the relations a reference crosses, and the hop-existence guards that
|
|
3
|
+
// keep an absent to-one parent denied under negation.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.relationOperator = relationOperator;
|
|
6
|
+
exports.relationFilter = relationFilter;
|
|
7
|
+
exports.wrapInRelations = wrapInRelations;
|
|
8
|
+
exports.buildLeadingHopsExistFilter = buildLeadingHopsExistFilter;
|
|
9
|
+
exports.negateFilter = negateFilter;
|
|
10
|
+
exports.negateRequiringHops = negateRequiringHops;
|
|
11
|
+
exports.referencesRequiredHop = referencesRequiredHop;
|
|
12
|
+
const mapping_1 = require("./mapping");
|
|
13
|
+
const plan_1 = require("./plan");
|
|
14
|
+
const errors_1 = require("./errors");
|
|
15
|
+
/** The Prisma operator that reaches into a relation for an existence test. */
|
|
16
|
+
function relationOperator(relation) {
|
|
17
|
+
return relation.type === "one" ? "is" : "some";
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* One relation-scoped filter (`{ tags: { some: … } }`), narrowed by the store-side predicate the
|
|
21
|
+
* caller declared on the mapping.
|
|
22
|
+
*
|
|
23
|
+
* Every nested relation filter the adapter emits goes through here, so a declared
|
|
24
|
+
* `subqueryFilter` reaches the collection macros, the membership shapes, the emptiness checks and
|
|
25
|
+
* the hop-existence guard alike. Adding a new relation shape means routing it through here, or
|
|
26
|
+
* the declaration silently stops applying to it.
|
|
27
|
+
*
|
|
28
|
+
* `every` is the one operator that cannot simply absorb the predicate. `{ every: AND(W, P) }`
|
|
29
|
+
* would REQUIRE every record to satisfy W, which is the opposite of "ignore the records W hides",
|
|
30
|
+
* so it is rewritten to `{ none: AND(W, NOT P) }` — no visible record violates P. Both agree with
|
|
31
|
+
* `every` when nothing is declared, and both stay vacuously true over an empty relation.
|
|
32
|
+
*/
|
|
33
|
+
function relationFilter(relation, operator, inner) {
|
|
34
|
+
const declared = relation.subqueryFilter;
|
|
35
|
+
if (declared === undefined) {
|
|
36
|
+
return { [relation.name]: { [operator]: inner } };
|
|
37
|
+
}
|
|
38
|
+
const isEmpty = Object.keys(inner).length === 0;
|
|
39
|
+
if (operator === "every") {
|
|
40
|
+
return isEmpty
|
|
41
|
+
? { [relation.name]: { every: inner } }
|
|
42
|
+
: { [relation.name]: { none: { AND: [declared, { NOT: inner }] } } };
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
[relation.name]: {
|
|
46
|
+
[operator]: isEmpty ? declared : { AND: [declared, inner] },
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `filter` nested through every relation in `relations`, outermost first, each reached with its
|
|
52
|
+
* existence operator (`is` for to-one, `some` for to-many). No relations leaves it unchanged.
|
|
53
|
+
*/
|
|
54
|
+
function wrapInRelations(relations, filter) {
|
|
55
|
+
let current = filter;
|
|
56
|
+
for (const relation of [...(relations ?? [])].reverse()) {
|
|
57
|
+
current = relationFilter(relation, relationOperator(relation), current);
|
|
58
|
+
}
|
|
59
|
+
return current;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* "Every one of these relations exists", as a nested Prisma filter with an empty innermost
|
|
63
|
+
* predicate — `{ parent: { is: { inner: { is: {} } } } }`.
|
|
64
|
+
*/
|
|
65
|
+
function buildHopsExistFilter(hops) {
|
|
66
|
+
return hops.length === 0 ? undefined : wrapInRelations(hops, {});
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* "Every intermediate hop of a dotted path exists", as a Prisma filter.
|
|
70
|
+
*
|
|
71
|
+
* CEL cannot dot through a list, so each intermediate segment of `a.b.c` is a to-ONE
|
|
72
|
+
* parent: absent, the caller sends no attribute and CEL raises a missing-path error, which
|
|
73
|
+
* denies. `NOT { categories: { some: { subCategories: { some: P } } } }` is TRUE for a row
|
|
74
|
+
* with no category at all, so the negation returns rows the PDP denies — the parent must be
|
|
75
|
+
* required separately (cerbos/query-plan-adapters#309).
|
|
76
|
+
*
|
|
77
|
+
* Returns undefined when the reference has no intermediate hop, so a direct relation keeps
|
|
78
|
+
* its empty-collection semantics (`!tags.exists(...)` over zero tags is still TRUE).
|
|
79
|
+
*/
|
|
80
|
+
function buildLeadingHopsExistFilter(head, restRelations) {
|
|
81
|
+
if (restRelations.length === 0) {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
// The last entry is the collection being iterated; everything before it is a hop.
|
|
85
|
+
return buildHopsExistFilter([head, ...restRelations.slice(0, -1)]);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* "Every to-one hop this expression dots through exists", as a Prisma filter, or undefined
|
|
89
|
+
* when it dots through none.
|
|
90
|
+
*
|
|
91
|
+
* Prisma has no UNKNOWN: a chained relation filter is exact under the POSITIVE polarity —
|
|
92
|
+
* `categories: { some: { subCategories: { some: P } } }` cannot hold without the category —
|
|
93
|
+
* but `NOT` inverts the hop requirement along with the predicate, so every operator built on
|
|
94
|
+
* a chain readmits the parentless rows once negated. #309 fixed that inside the collection
|
|
95
|
+
* macros, which left the sibling operators reached through the same chain unguarded:
|
|
96
|
+
* membership and `hasIntersection` (cerbos/query-plan-adapters#315), and the negated count
|
|
97
|
+
* spelling `!(size(chain) > 0)` (#316), all of which the macros' guard never saw.
|
|
98
|
+
*
|
|
99
|
+
* Requiring the hops OUTSIDE the negation is what fixes all of them at once, and it is
|
|
100
|
+
* unconditionally faithful to CEL rather than a per-operator patch: a missing hop is a
|
|
101
|
+
* missing-path error, which denies under BOTH polarities, so the requirement never depends
|
|
102
|
+
* on which operator sits above the chain.
|
|
103
|
+
*
|
|
104
|
+
* Lambda bodies are deliberately not walked — their references resolve against a scoped
|
|
105
|
+
* mapper, and a negated macro carries its own guard through the negated collection operators.
|
|
106
|
+
*/
|
|
107
|
+
function buildExpressionHopsExistFilter(operand, context) {
|
|
108
|
+
const hops = [];
|
|
109
|
+
const seen = new Set();
|
|
110
|
+
const visit = (expr) => {
|
|
111
|
+
if ((0, plan_1.isNamedOperand)(expr)) {
|
|
112
|
+
if (seen.has(expr.name)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
seen.add(expr.name);
|
|
116
|
+
const { relations } = (0, mapping_1.resolveFieldReference)(expr.name, context);
|
|
117
|
+
if (!relations || relations.length === 0) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const last = relations[relations.length - 1];
|
|
121
|
+
// Every to-ONE relation on the path is a hop that must EXIST — including a TRAILING one,
|
|
122
|
+
// which is the case a chain of to-many relations never produces. `parent.aBool` resolves
|
|
123
|
+
// to a single to-one relation, and `NOT { parent: { is: { aBool: true } } }` is TRUE for
|
|
124
|
+
// a row with no parent at all, so the negation returns rows the PDP denies. A trailing
|
|
125
|
+
// to-MANY relation is the collection being iterated rather than a hop, and keeps its
|
|
126
|
+
// empty-collection semantics (`!tags.exists(...)` over zero tags is still TRUE).
|
|
127
|
+
const hopFilter = buildHopsExistFilter(last.type === "one" ? relations : relations.slice(0, -1));
|
|
128
|
+
if (hopFilter !== undefined) {
|
|
129
|
+
hops.push(hopFilter);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (!(0, plan_1.isOperatorOperand)(expr) || expr.operator === "lambda") {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
expr.operands.forEach(visit);
|
|
137
|
+
};
|
|
138
|
+
visit(operand);
|
|
139
|
+
if (hops.length === 0) {
|
|
140
|
+
return undefined;
|
|
141
|
+
}
|
|
142
|
+
return hops.length === 1 ? hops[0] : { AND: hops };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* `{ NOT: filter }` for a filter translated from a plan sub-expression.
|
|
146
|
+
*
|
|
147
|
+
* An unconditional filter (`{}`, which the hierarchy operators return for a relationship that
|
|
148
|
+
* holds between two constants) cannot be negated this way: Prisma drops an empty condition, so
|
|
149
|
+
* `{ NOT: {} }` is true and matches every row, where the negation of an unconditional condition
|
|
150
|
+
* matches none. Prisma has no field-free false condition to emit instead, so the shape is refused.
|
|
151
|
+
*/
|
|
152
|
+
function negateFilter(filter) {
|
|
153
|
+
if (Object.keys(filter).length === 0) {
|
|
154
|
+
throw new errors_1.UnsupportedQueryPlanError("Cannot negate an unconditional filter: Prisma evaluates { NOT: {} } as true and matches " +
|
|
155
|
+
"every row, where the negated condition matches none");
|
|
156
|
+
}
|
|
157
|
+
return { NOT: filter };
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* `{ NOT: filter }`, with every to-one hop `operand` dots through required OUTSIDE the
|
|
161
|
+
* negation so an absent parent stays denied (see buildExpressionHopsExistFilter).
|
|
162
|
+
*/
|
|
163
|
+
function negateRequiringHops(operand, filter, context) {
|
|
164
|
+
const hops = buildExpressionHopsExistFilter(operand, context);
|
|
165
|
+
const negated = negateFilter(filter);
|
|
166
|
+
return hops === undefined ? negated : { AND: [hops, negated] };
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Whether negating `expr` needs a hop requirement (see buildExpressionHopsExistFilter): it dots
|
|
170
|
+
* through at least one to-one hop, which is absent on a parentless row.
|
|
171
|
+
*/
|
|
172
|
+
function referencesRequiredHop(expr, context) {
|
|
173
|
+
return buildExpressionHopsExistFilter(expr, context) !== undefined;
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=relations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"relations.js","sourceRoot":"","sources":["../src/relations.ts"],"names":[],"mappings":"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"}
|
package/lib/rewrite.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PlanExpressionOperand, Value } from "@cerbos/core";
|
|
2
|
+
/** The boolean collection macros: each takes a collection and a lambda. */
|
|
3
|
+
export declare const COLLECTION_OPERATORS: Set<string>;
|
|
4
|
+
export declare function containsCollectionOperator(expr: PlanExpressionOperand): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Substitute a lambda iteration variable with a concrete collection element
|
|
7
|
+
* inside a lambda body. A bare reference to the variable becomes the element
|
|
8
|
+
* itself; a `variable.path.to.field` reference drills into the element. A
|
|
9
|
+
* nested collection macro whose lambda rebinds the same variable name shadows
|
|
10
|
+
* the outer variable, so substitution only descends into its collection
|
|
11
|
+
* operand.
|
|
12
|
+
*/
|
|
13
|
+
export declare function substituteLambdaVariable(operand: PlanExpressionOperand, variableName: string, element: Value): PlanExpressionOperand;
|
|
14
|
+
/**
|
|
15
|
+
* Rewrites lambda bodies that reference columns of an ENCLOSING scope (the root row, or an
|
|
16
|
+
* outer lambda's element) so that every filter lands on the model it belongs to. Without this,
|
|
17
|
+
* an outer column referenced inside `tags.exists(t, ...)` would be emitted as a field of the
|
|
18
|
+
* tag model. Two sound transforms, applied bottom-up:
|
|
19
|
+
*
|
|
20
|
+
* - Conjunct hoisting (exists only): `exists(c, P(c) && Q)` with row-constant Q becomes
|
|
21
|
+
* `exists(c, P(c)) && Q`. Valid in three-valued logic (AND distributes over the per-element
|
|
22
|
+
* OR), including the empty-collection case (both sides are FALSE).
|
|
23
|
+
* - Case split (any collection op): a bare outer boolean reference Q in a boolean position
|
|
24
|
+
* becomes `(Q && op[Q:=true]) || (!Q && op[Q:=false]) || (Q && !Q && exists(c, true))`. The
|
|
25
|
+
* contradiction arm keeps the whole expression UNKNOWN (not FALSE) when Q is NULL-derived and
|
|
26
|
+
* the collection has elements, mirroring the guarded-ternary encoding.
|
|
27
|
+
*/
|
|
28
|
+
export declare function hoistOuterScopeReferences(expr: PlanExpressionOperand, enclosingVariables: string[]): PlanExpressionOperand;
|
|
29
|
+
/**
|
|
30
|
+
* Bottom-up constant folding over the plan AST. Only shapes the case-split transform can
|
|
31
|
+
* produce need folding (constant ternary conditions, comparisons between two constants,
|
|
32
|
+
* logical operators with constant operands, collection macros with constant bodies); real
|
|
33
|
+
* planner output arrives pre-folded.
|
|
34
|
+
*/
|
|
35
|
+
export declare function constantFoldExpression(expr: PlanExpressionOperand): PlanExpressionOperand;
|
|
36
|
+
//# sourceMappingURL=rewrite.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewrite.d.ts","sourceRoot":"","sources":["../src/rewrite.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AAkBjE,2EAA2E;AAC3E,eAAO,MAAM,oBAAoB,aAM/B,CAAC;AAKH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAU/E;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,qBAAqB,EAC9B,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,KAAK,GACb,qBAAqB,CAsEvB;AA4HD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,qBAAqB,EAC3B,kBAAkB,EAAE,MAAM,EAAE,GAC3B,qBAAqB,CAoGvB;AAyBD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,qBAAqB,GAC1B,qBAAqB,CA0HvB"}
|
package/lib/rewrite.js
ADDED
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Plan-to-plan rewrites that run before translation. None of them knows about Prisma: each takes
|
|
3
|
+
// an expression tree and returns an equivalent one that the translator can lower more directly.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.COLLECTION_OPERATORS = void 0;
|
|
6
|
+
exports.containsCollectionOperator = containsCollectionOperator;
|
|
7
|
+
exports.substituteLambdaVariable = substituteLambdaVariable;
|
|
8
|
+
exports.hoistOuterScopeReferences = hoistOuterScopeReferences;
|
|
9
|
+
exports.constantFoldExpression = constantFoldExpression;
|
|
10
|
+
const evaluate_1 = require("./evaluate");
|
|
11
|
+
const plan_1 = require("./plan");
|
|
12
|
+
const errors_1 = require("./errors");
|
|
13
|
+
const narrowing_1 = require("./narrowing");
|
|
14
|
+
/** The boolean collection macros: each takes a collection and a lambda. */
|
|
15
|
+
exports.COLLECTION_OPERATORS = new Set([
|
|
16
|
+
"exists",
|
|
17
|
+
"exists_one",
|
|
18
|
+
"all",
|
|
19
|
+
"except",
|
|
20
|
+
"filter",
|
|
21
|
+
]);
|
|
22
|
+
// Operators whose second operand is a lambda that binds an iteration variable.
|
|
23
|
+
const LAMBDA_BINDING_OPERATORS = new Set([...exports.COLLECTION_OPERATORS, "map"]);
|
|
24
|
+
function containsCollectionOperator(expr) {
|
|
25
|
+
if (!(0, plan_1.isOperatorOperand)(expr)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
if (exports.COLLECTION_OPERATORS.has(expr.operator)) {
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
return expr.operands.some(containsCollectionOperator);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Substitute a lambda iteration variable with a concrete collection element
|
|
35
|
+
* inside a lambda body. A bare reference to the variable becomes the element
|
|
36
|
+
* itself; a `variable.path.to.field` reference drills into the element. A
|
|
37
|
+
* nested collection macro whose lambda rebinds the same variable name shadows
|
|
38
|
+
* the outer variable, so substitution only descends into its collection
|
|
39
|
+
* operand.
|
|
40
|
+
*/
|
|
41
|
+
function substituteLambdaVariable(operand, variableName, element) {
|
|
42
|
+
if ((0, plan_1.isNamedOperand)(operand)) {
|
|
43
|
+
if (operand.name === variableName) {
|
|
44
|
+
return { value: element };
|
|
45
|
+
}
|
|
46
|
+
if (operand.name.startsWith(`${variableName}.`)) {
|
|
47
|
+
let current = element;
|
|
48
|
+
for (const segment of operand.name
|
|
49
|
+
.slice(variableName.length + 1)
|
|
50
|
+
.split(".")) {
|
|
51
|
+
if (!(0, narrowing_1.isObject)(current) ||
|
|
52
|
+
Array.isArray(current) ||
|
|
53
|
+
!(segment in current) ||
|
|
54
|
+
current[segment] === undefined) {
|
|
55
|
+
throw new errors_1.UnsupportedQueryPlanError(`Cannot resolve "${operand.name}": collection element has no field "${segment}"`);
|
|
56
|
+
}
|
|
57
|
+
current = current[segment];
|
|
58
|
+
}
|
|
59
|
+
return { value: current };
|
|
60
|
+
}
|
|
61
|
+
return operand;
|
|
62
|
+
}
|
|
63
|
+
if (!(0, plan_1.isOperatorOperand)(operand)) {
|
|
64
|
+
return operand;
|
|
65
|
+
}
|
|
66
|
+
const [nestedCollection, nestedLambda] = operand.operands;
|
|
67
|
+
if (LAMBDA_BINDING_OPERATORS.has(operand.operator) &&
|
|
68
|
+
operand.operands.length === 2 &&
|
|
69
|
+
nestedCollection !== undefined &&
|
|
70
|
+
nestedLambda !== undefined &&
|
|
71
|
+
(0, plan_1.isOperatorOperand)(nestedLambda) &&
|
|
72
|
+
nestedLambda.operator === "lambda") {
|
|
73
|
+
const nestedVariable = nestedLambda.operands[1];
|
|
74
|
+
if (nestedVariable !== undefined &&
|
|
75
|
+
(0, plan_1.isNamedOperand)(nestedVariable) &&
|
|
76
|
+
nestedVariable.name === variableName) {
|
|
77
|
+
// The nested lambda shadows our variable: substitute only in the collection operand.
|
|
78
|
+
return {
|
|
79
|
+
operator: operand.operator,
|
|
80
|
+
operands: [
|
|
81
|
+
substituteLambdaVariable(nestedCollection, variableName, element),
|
|
82
|
+
nestedLambda,
|
|
83
|
+
],
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
operator: operand.operator,
|
|
89
|
+
operands: operand.operands.map((o) => substituteLambdaVariable(o, variableName, element)),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function mentionsVariable(expr, variableName) {
|
|
93
|
+
if ((0, plan_1.isNamedOperand)(expr)) {
|
|
94
|
+
return (expr.name === variableName || expr.name.startsWith(variableName + "."));
|
|
95
|
+
}
|
|
96
|
+
if ((0, plan_1.isOperatorOperand)(expr)) {
|
|
97
|
+
return expr.operands.some((operand) => mentionsVariable(operand, variableName));
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
function isOuterScopeName(name, enclosingVariables) {
|
|
102
|
+
return !enclosingVariables.some((variable) => name === variable || name.startsWith(variable + "."));
|
|
103
|
+
}
|
|
104
|
+
/** Replaces every occurrence of the bare named operand `name` with a boolean constant. */
|
|
105
|
+
function substituteNamedOperand(expr, name, value) {
|
|
106
|
+
if ((0, plan_1.isNamedOperand)(expr)) {
|
|
107
|
+
return expr.name === name ? { value } : expr;
|
|
108
|
+
}
|
|
109
|
+
if ((0, plan_1.isOperatorOperand)(expr)) {
|
|
110
|
+
return {
|
|
111
|
+
operator: expr.operator,
|
|
112
|
+
operands: expr.operands.map((operand) => substituteNamedOperand(operand, name, value)),
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
return expr;
|
|
116
|
+
}
|
|
117
|
+
const BOOLEAN_POSITION_OPERATORS = new Set(["and", "or", "not", "if"]);
|
|
118
|
+
/**
|
|
119
|
+
* Finds a bare boolean reference to an OUTER column (root or enclosing lambda scope) in a
|
|
120
|
+
* boolean position of a lambda body: a direct operand of and/or/not, a ternary condition, or
|
|
121
|
+
* the body itself.
|
|
122
|
+
*/
|
|
123
|
+
function findOuterBooleanReference(expr, enclosingVariables) {
|
|
124
|
+
if ((0, plan_1.isNamedOperand)(expr)) {
|
|
125
|
+
return isOuterScopeName(expr.name, enclosingVariables)
|
|
126
|
+
? expr.name
|
|
127
|
+
: undefined;
|
|
128
|
+
}
|
|
129
|
+
if (!(0, plan_1.isOperatorOperand)(expr)) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
const booleanPositions = BOOLEAN_POSITION_OPERATORS.has(expr.operator)
|
|
133
|
+
? expr.operands
|
|
134
|
+
: // Comparisons wrapping a ternary keep the outer reference in the ternary condition.
|
|
135
|
+
expr.operands.filter((operand) => (0, plan_1.isOperatorOperand)(operand) &&
|
|
136
|
+
BOOLEAN_POSITION_OPERATORS.has(operand.operator));
|
|
137
|
+
for (const operand of booleanPositions) {
|
|
138
|
+
const found = findOuterBooleanReference(operand, enclosingVariables);
|
|
139
|
+
if (found !== undefined) {
|
|
140
|
+
return found;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* The parts of `macro(collection, lambda(body, variable))` when `expr` is a boolean collection
|
|
147
|
+
* macro over a single-variable lambda, or undefined for any other shape.
|
|
148
|
+
*/
|
|
149
|
+
function parseLambdaMacro(expr) {
|
|
150
|
+
const [collection, lambda] = expr.operands;
|
|
151
|
+
if (!exports.COLLECTION_OPERATORS.has(expr.operator) ||
|
|
152
|
+
expr.operands.length !== 2 ||
|
|
153
|
+
collection === undefined ||
|
|
154
|
+
lambda === undefined ||
|
|
155
|
+
!(0, plan_1.isOperatorOperand)(lambda) ||
|
|
156
|
+
lambda.operator !== "lambda" ||
|
|
157
|
+
lambda.operands.length !== 2) {
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
const variable = lambda.operands[1];
|
|
161
|
+
if (variable === undefined || !(0, plan_1.isNamedOperand)(variable)) {
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
return { collection, lambda, variable };
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Rewrites lambda bodies that reference columns of an ENCLOSING scope (the root row, or an
|
|
168
|
+
* outer lambda's element) so that every filter lands on the model it belongs to. Without this,
|
|
169
|
+
* an outer column referenced inside `tags.exists(t, ...)` would be emitted as a field of the
|
|
170
|
+
* tag model. Two sound transforms, applied bottom-up:
|
|
171
|
+
*
|
|
172
|
+
* - Conjunct hoisting (exists only): `exists(c, P(c) && Q)` with row-constant Q becomes
|
|
173
|
+
* `exists(c, P(c)) && Q`. Valid in three-valued logic (AND distributes over the per-element
|
|
174
|
+
* OR), including the empty-collection case (both sides are FALSE).
|
|
175
|
+
* - Case split (any collection op): a bare outer boolean reference Q in a boolean position
|
|
176
|
+
* becomes `(Q && op[Q:=true]) || (!Q && op[Q:=false]) || (Q && !Q && exists(c, true))`. The
|
|
177
|
+
* contradiction arm keeps the whole expression UNKNOWN (not FALSE) when Q is NULL-derived and
|
|
178
|
+
* the collection has elements, mirroring the guarded-ternary encoding.
|
|
179
|
+
*/
|
|
180
|
+
function hoistOuterScopeReferences(expr, enclosingVariables) {
|
|
181
|
+
if (!(0, plan_1.isOperatorOperand)(expr)) {
|
|
182
|
+
return expr;
|
|
183
|
+
}
|
|
184
|
+
const macro = parseLambdaMacro(expr);
|
|
185
|
+
if (macro === undefined) {
|
|
186
|
+
return {
|
|
187
|
+
operator: expr.operator,
|
|
188
|
+
operands: expr.operands.map((operand) => hoistOuterScopeReferences(operand, enclosingVariables)),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const { collection, lambda, variable } = macro;
|
|
192
|
+
const scopeVariables = [...enclosingVariables, variable.name];
|
|
193
|
+
const body = hoistOuterScopeReferences((0, plan_1.assertDefined)(lambda.operands[0], "Lambda requires a condition"), scopeVariables);
|
|
194
|
+
const rebuild = (newBody) => ({
|
|
195
|
+
operator: expr.operator,
|
|
196
|
+
operands: [collection, { operator: "lambda", operands: [newBody, variable] }],
|
|
197
|
+
});
|
|
198
|
+
// Conjunct hoisting out of exists().
|
|
199
|
+
if (expr.operator === "exists" &&
|
|
200
|
+
(0, plan_1.isOperatorOperand)(body) &&
|
|
201
|
+
body.operator === "and") {
|
|
202
|
+
const elementConjuncts = body.operands.filter((operand) => mentionsVariable(operand, variable.name));
|
|
203
|
+
const outerConjuncts = body.operands.filter((operand) => !mentionsVariable(operand, variable.name));
|
|
204
|
+
if (outerConjuncts.length > 0 && elementConjuncts.length > 0) {
|
|
205
|
+
const innerBody = elementConjuncts.length === 1
|
|
206
|
+
? elementConjuncts[0]
|
|
207
|
+
: { operator: "and", operands: elementConjuncts };
|
|
208
|
+
return {
|
|
209
|
+
operator: "and",
|
|
210
|
+
operands: [rebuild(innerBody), ...outerConjuncts],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
// Case split on a bare outer boolean reference.
|
|
215
|
+
const outerRef = findOuterBooleanReference(body, scopeVariables);
|
|
216
|
+
if (outerRef !== undefined) {
|
|
217
|
+
const q = { name: outerRef };
|
|
218
|
+
const notQ = { operator: "not", operands: [q] };
|
|
219
|
+
const arms = [
|
|
220
|
+
{
|
|
221
|
+
operator: "and",
|
|
222
|
+
operands: [q, rebuild(substituteNamedOperand(body, outerRef, true))],
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
operator: "and",
|
|
226
|
+
operands: [notQ, rebuild(substituteNamedOperand(body, outerRef, false))],
|
|
227
|
+
},
|
|
228
|
+
// Contradiction arm: UNKNOWN when the reference is NULL-derived, never TRUE. CEL never
|
|
229
|
+
// evaluates the body over an empty collection, so there the arm is FALSE, not UNKNOWN.
|
|
230
|
+
{
|
|
231
|
+
operator: "and",
|
|
232
|
+
operands: [
|
|
233
|
+
q,
|
|
234
|
+
notQ,
|
|
235
|
+
{
|
|
236
|
+
operator: "exists",
|
|
237
|
+
operands: [collection, { operator: "lambda", operands: [{ value: true }, variable] }],
|
|
238
|
+
},
|
|
239
|
+
],
|
|
240
|
+
},
|
|
241
|
+
];
|
|
242
|
+
// all() over an empty collection never evaluates its body, so CEL holds it TRUE even when
|
|
243
|
+
// the reference is missing; the arms above are all UNKNOWN on that row. `all(c, false)` is
|
|
244
|
+
// TRUE exactly when the collection is empty, and definite, so it restores that row without
|
|
245
|
+
// touching any other (#488).
|
|
246
|
+
if (expr.operator === "all") {
|
|
247
|
+
arms.push(rebuild({ value: false }));
|
|
248
|
+
}
|
|
249
|
+
return { operator: "or", operands: arms };
|
|
250
|
+
}
|
|
251
|
+
return rebuild(body);
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* `and`/`or` over already-folded operands: a dominating constant decides the whole expression,
|
|
255
|
+
* and the other constants drop out.
|
|
256
|
+
*/
|
|
257
|
+
function foldLogical(operator, operands) {
|
|
258
|
+
const dominant = operator === "or";
|
|
259
|
+
if (operands.some((o) => (0, plan_1.isValueOperand)(o) && o.value === dominant)) {
|
|
260
|
+
return { value: dominant };
|
|
261
|
+
}
|
|
262
|
+
const remaining = operands.filter((o) => !(0, plan_1.isValueOperand)(o));
|
|
263
|
+
if (remaining.length === 0)
|
|
264
|
+
return { value: !dominant };
|
|
265
|
+
if (remaining.length === 1)
|
|
266
|
+
return remaining[0];
|
|
267
|
+
return { operator, operands: remaining };
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Bottom-up constant folding over the plan AST. Only shapes the case-split transform can
|
|
271
|
+
* produce need folding (constant ternary conditions, comparisons between two constants,
|
|
272
|
+
* logical operators with constant operands, collection macros with constant bodies); real
|
|
273
|
+
* planner output arrives pre-folded.
|
|
274
|
+
*/
|
|
275
|
+
function constantFoldExpression(expr) {
|
|
276
|
+
if (!(0, plan_1.isOperatorOperand)(expr)) {
|
|
277
|
+
return expr;
|
|
278
|
+
}
|
|
279
|
+
(0, plan_1.assertLogicalOperands)(expr.operator, expr.operands);
|
|
280
|
+
const operands = expr.operands.map(constantFoldExpression);
|
|
281
|
+
const folded = { operator: expr.operator, operands };
|
|
282
|
+
const [first, second] = operands;
|
|
283
|
+
const bothConstant = first !== undefined &&
|
|
284
|
+
second !== undefined &&
|
|
285
|
+
(0, plan_1.isValueOperand)(first) &&
|
|
286
|
+
(0, plan_1.isValueOperand)(second);
|
|
287
|
+
switch (expr.operator) {
|
|
288
|
+
case "if": {
|
|
289
|
+
const [condition, thenBranch, elseBranch] = operands;
|
|
290
|
+
if (condition !== undefined && (0, plan_1.isValueOperand)(condition)) {
|
|
291
|
+
if (!(0, narrowing_1.isBoolean)(condition.value)) {
|
|
292
|
+
throw new errors_1.UnsupportedQueryPlanError("if (ternary) condition must be a boolean expression");
|
|
293
|
+
}
|
|
294
|
+
return (0, plan_1.assertDefined)(condition.value ? thenBranch : elseBranch, "if (ternary) requires branch operands");
|
|
295
|
+
}
|
|
296
|
+
return folded;
|
|
297
|
+
}
|
|
298
|
+
case "and":
|
|
299
|
+
case "or":
|
|
300
|
+
return foldLogical(expr.operator, operands);
|
|
301
|
+
case "not":
|
|
302
|
+
if (first !== undefined && (0, plan_1.isValueOperand)(first)) {
|
|
303
|
+
return { value: first.value !== true };
|
|
304
|
+
}
|
|
305
|
+
return folded;
|
|
306
|
+
case "add":
|
|
307
|
+
case "sub":
|
|
308
|
+
case "mult":
|
|
309
|
+
case "div":
|
|
310
|
+
return bothConstant
|
|
311
|
+
? { value: (0, evaluate_1.foldArithmetic)(expr.operator, first.value, second.value) }
|
|
312
|
+
: folded;
|
|
313
|
+
case "contains":
|
|
314
|
+
case "startsWith":
|
|
315
|
+
case "endsWith": {
|
|
316
|
+
if (bothConstant &&
|
|
317
|
+
(0, narrowing_1.isString)(first.value) &&
|
|
318
|
+
(0, narrowing_1.isString)(second.value)) {
|
|
319
|
+
const receiver = first.value;
|
|
320
|
+
const needle = second.value;
|
|
321
|
+
return {
|
|
322
|
+
value: expr.operator === "contains"
|
|
323
|
+
? receiver.includes(needle)
|
|
324
|
+
: expr.operator === "startsWith"
|
|
325
|
+
? receiver.startsWith(needle)
|
|
326
|
+
: receiver.endsWith(needle),
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
return folded;
|
|
330
|
+
}
|
|
331
|
+
case "exists":
|
|
332
|
+
case "all": {
|
|
333
|
+
const lambda = second;
|
|
334
|
+
if (first !== undefined &&
|
|
335
|
+
lambda !== undefined &&
|
|
336
|
+
(0, plan_1.isOperatorOperand)(lambda) &&
|
|
337
|
+
lambda.operator === "lambda" &&
|
|
338
|
+
lambda.operands[0] !== undefined &&
|
|
339
|
+
(0, plan_1.isValueOperand)(lambda.operands[0])) {
|
|
340
|
+
const bodyValue = lambda.operands[0].value === true;
|
|
341
|
+
if (expr.operator === "exists" && !bodyValue)
|
|
342
|
+
return { value: false };
|
|
343
|
+
if (expr.operator === "all" && bodyValue)
|
|
344
|
+
return { value: true };
|
|
345
|
+
const sizeExpr = {
|
|
346
|
+
operator: "size",
|
|
347
|
+
operands: [first],
|
|
348
|
+
};
|
|
349
|
+
// exists(c, true) is "collection is non-empty"; all(c, false) is "collection is empty".
|
|
350
|
+
return expr.operator === "exists"
|
|
351
|
+
? { operator: "gt", operands: [sizeExpr, { value: 0 }] }
|
|
352
|
+
: { operator: "eq", operands: [sizeExpr, { value: 0 }] };
|
|
353
|
+
}
|
|
354
|
+
return folded;
|
|
355
|
+
}
|
|
356
|
+
default:
|
|
357
|
+
if (plan_1.COMPARISON_OPERATORS.has(expr.operator) &&
|
|
358
|
+
bothConstant) {
|
|
359
|
+
return {
|
|
360
|
+
value: (0, evaluate_1.evaluateConstantComparison)(expr.operator, first.value, second.value),
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
return folded;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
//# sourceMappingURL=rewrite.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewrite.js","sourceRoot":"","sources":["../src/rewrite.ts"],"names":[],"mappings":"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"}
|
package/lib/solve.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The doubles `x` for which a comparison holds, as an interval of finite doubles: `lo`/`hi` are
|
|
3
|
+
* inclusive bounds, undefined when that side is unbounded. `empty` when no double satisfies it.
|
|
4
|
+
*/
|
|
5
|
+
export type DoubleInterval = {
|
|
6
|
+
empty: true;
|
|
7
|
+
} | {
|
|
8
|
+
empty: false;
|
|
9
|
+
lo: number | undefined;
|
|
10
|
+
hi: number | undefined;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* The doubles `x` with `f(x) CMP v`, for `f` non-decreasing over the finite doubles — IEEE-754
|
|
14
|
+
* addition and multiplication by a positive constant are, whatever rounding does to them, which
|
|
15
|
+
* is what makes this exact where subtracting the constant from `v` is not.
|
|
16
|
+
*/
|
|
17
|
+
export declare function solveMonotone(f: (x: number) => number, operator: "eq" | "lt" | "le" | "gt" | "ge", v: number): DoubleInterval;
|
|
18
|
+
/** The interval of `-x` for `x` in `interval`. */
|
|
19
|
+
export declare function negateInterval(interval: DoubleInterval): DoubleInterval;
|
|
20
|
+
//# sourceMappingURL=solve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solve.d.ts","sourceRoot":"","sources":["../src/solve.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,MAAM,cAAc,GACtB;IAAE,KAAK,EAAE,IAAI,CAAA;CAAE,GACf;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAsCrE;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,EACxB,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,EAC1C,CAAC,EAAE,MAAM,GACR,cAAc,CA6BhB;AAED,kDAAkD;AAClD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,cAAc,CAQvE"}
|