@zenstackhq/runtime 3.0.0-alpha.7 → 3.0.0-alpha.9
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/{contract-DW8XGrtV.d.cts → contract-BiU0iYAh.d.cts} +112 -36
- package/dist/{contract-DW8XGrtV.d.ts → contract-BiU0iYAh.d.ts} +112 -36
- package/dist/index.cjs +419 -235
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +28 -3
- package/dist/index.d.ts +28 -3
- package/dist/index.js +414 -235
- package/dist/index.js.map +1 -1
- package/dist/plugins/policy.cjs +81 -56
- package/dist/plugins/policy.cjs.map +1 -1
- package/dist/plugins/policy.d.cts +2 -2
- package/dist/plugins/policy.d.ts +2 -2
- package/dist/plugins/policy.js +81 -56
- package/dist/plugins/policy.js.map +1 -1
- package/package.json +7 -17
- package/dist/client.cjs +0 -6110
- package/dist/client.cjs.map +0 -1
- package/dist/client.d.cts +0 -17
- package/dist/client.d.ts +0 -17
- package/dist/client.js +0 -6075
- package/dist/client.js.map +0 -1
package/dist/plugins/policy.cjs
CHANGED
|
@@ -56,8 +56,10 @@ var QueryError = class extends Error {
|
|
|
56
56
|
static {
|
|
57
57
|
__name(this, "QueryError");
|
|
58
58
|
}
|
|
59
|
-
constructor(message) {
|
|
60
|
-
super(message
|
|
59
|
+
constructor(message, cause) {
|
|
60
|
+
super(message, {
|
|
61
|
+
cause
|
|
62
|
+
});
|
|
61
63
|
}
|
|
62
64
|
};
|
|
63
65
|
var InternalError = class extends Error {
|
|
@@ -77,7 +79,7 @@ __name(getModel, "getModel");
|
|
|
77
79
|
function requireModel(schema, model) {
|
|
78
80
|
const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
|
|
79
81
|
if (!matchedName) {
|
|
80
|
-
throw new QueryError(`Model "${model}" not found`);
|
|
82
|
+
throw new QueryError(`Model "${model}" not found in schema`);
|
|
81
83
|
}
|
|
82
84
|
return schema.models[matchedName];
|
|
83
85
|
}
|
|
@@ -140,6 +142,11 @@ function getRelationForeignKeyFieldPairs(schema, model, relationField) {
|
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
__name(getRelationForeignKeyFieldPairs, "getRelationForeignKeyFieldPairs");
|
|
145
|
+
function isRelationField(schema, model, field) {
|
|
146
|
+
const fieldDef = requireField(schema, model, field);
|
|
147
|
+
return !!fieldDef.relation;
|
|
148
|
+
}
|
|
149
|
+
__name(isRelationField, "isRelationField");
|
|
143
150
|
function getUniqueFields(schema, model) {
|
|
144
151
|
const modelDef = requireModel(schema, model);
|
|
145
152
|
const result = [];
|
|
@@ -176,7 +183,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias) {
|
|
|
176
183
|
computer = computedFields?.[model]?.[field];
|
|
177
184
|
}
|
|
178
185
|
if (!computer) {
|
|
179
|
-
throw new QueryError(`Computed field "${field}" implementation not provided`);
|
|
186
|
+
throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
|
|
180
187
|
}
|
|
181
188
|
return computer(eb);
|
|
182
189
|
}
|
|
@@ -284,7 +291,7 @@ var BaseCrudDialect = class {
|
|
|
284
291
|
this.schema = schema;
|
|
285
292
|
this.options = options;
|
|
286
293
|
}
|
|
287
|
-
transformPrimitive(value, _type) {
|
|
294
|
+
transformPrimitive(value, _type, _forArrayField) {
|
|
288
295
|
return value;
|
|
289
296
|
}
|
|
290
297
|
buildFilter(eb, model, modelAlias, where) {
|
|
@@ -427,7 +434,7 @@ var BaseCrudDialect = class {
|
|
|
427
434
|
if (_value === void 0) {
|
|
428
435
|
continue;
|
|
429
436
|
}
|
|
430
|
-
const value = this.transformPrimitive(_value, fieldType);
|
|
437
|
+
const value = this.transformPrimitive(_value, fieldType, !!fieldDef.array);
|
|
431
438
|
switch (key) {
|
|
432
439
|
case "equals": {
|
|
433
440
|
clauses.push(this.buildLiteralFilter(eb, fieldRef, fieldType, eb.val(value)));
|
|
@@ -465,10 +472,14 @@ var BaseCrudDialect = class {
|
|
|
465
472
|
if (isEnum(this.schema, fieldDef.type)) {
|
|
466
473
|
return this.buildEnumFilter(eb, modelAlias, field, fieldDef, payload);
|
|
467
474
|
}
|
|
468
|
-
return (0, import_ts_pattern.match)(fieldDef.type).with("String", () => this.buildStringFilter(eb, modelAlias, field, payload)).with(import_ts_pattern.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(eb, model, modelAlias, field, type, payload)).with("Boolean", () => this.buildBooleanFilter(eb, modelAlias, field, payload)).with("DateTime", () => this.buildDateTimeFilter(eb, modelAlias, field, payload)).with("Bytes", () => this.buildBytesFilter(eb, modelAlias, field, payload)).
|
|
475
|
+
return (0, import_ts_pattern.match)(fieldDef.type).with("String", () => this.buildStringFilter(eb, modelAlias, field, payload)).with(import_ts_pattern.P.union("Int", "Float", "Decimal", "BigInt"), (type) => this.buildNumberFilter(eb, model, modelAlias, field, type, payload)).with("Boolean", () => this.buildBooleanFilter(eb, modelAlias, field, payload)).with("DateTime", () => this.buildDateTimeFilter(eb, modelAlias, field, payload)).with("Bytes", () => this.buildBytesFilter(eb, modelAlias, field, payload)).with("Json", () => {
|
|
476
|
+
throw new InternalError("JSON filters are not supported yet");
|
|
477
|
+
}).with("Unsupported", () => {
|
|
478
|
+
throw new QueryError(`Unsupported field cannot be used in filters`);
|
|
479
|
+
}).exhaustive();
|
|
469
480
|
}
|
|
470
481
|
buildLiteralFilter(eb, lhs, type, rhs) {
|
|
471
|
-
return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type) : rhs);
|
|
482
|
+
return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
|
|
472
483
|
}
|
|
473
484
|
buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0) {
|
|
474
485
|
if (payload === null || !(0, import_common_helpers.isPlainObject)(payload)) {
|
|
@@ -555,22 +566,22 @@ var BaseCrudDialect = class {
|
|
|
555
566
|
}
|
|
556
567
|
}
|
|
557
568
|
buildNumberFilter(eb, model, table, field, type, payload) {
|
|
558
|
-
const { conditions } = this.buildStandardFilter(eb, type, payload, buildFieldRef(this.schema, model, field, this.options, eb), (value) => this.transformPrimitive(value, type), (value) => this.buildNumberFilter(eb, model, table, field, type, value));
|
|
569
|
+
const { conditions } = this.buildStandardFilter(eb, type, payload, buildFieldRef(this.schema, model, field, this.options, eb), (value) => this.transformPrimitive(value, type, false), (value) => this.buildNumberFilter(eb, model, table, field, type, value));
|
|
559
570
|
return this.and(eb, ...conditions);
|
|
560
571
|
}
|
|
561
572
|
buildBooleanFilter(eb, table, field, payload) {
|
|
562
|
-
const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Boolean"), (value) => this.buildBooleanFilter(eb, table, field, value), true, [
|
|
573
|
+
const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Boolean", false), (value) => this.buildBooleanFilter(eb, table, field, value), true, [
|
|
563
574
|
"equals",
|
|
564
575
|
"not"
|
|
565
576
|
]);
|
|
566
577
|
return this.and(eb, ...conditions);
|
|
567
578
|
}
|
|
568
579
|
buildDateTimeFilter(eb, table, field, payload) {
|
|
569
|
-
const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "DateTime"), (value) => this.buildDateTimeFilter(eb, table, field, value), true);
|
|
580
|
+
const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "DateTime", false), (value) => this.buildDateTimeFilter(eb, table, field, value), true);
|
|
570
581
|
return this.and(eb, ...conditions);
|
|
571
582
|
}
|
|
572
583
|
buildBytesFilter(eb, table, field, payload) {
|
|
573
|
-
const conditions = this.buildStandardFilter(eb, "Bytes", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Bytes"), (value) => this.buildBytesFilter(eb, table, field, value), true, [
|
|
584
|
+
const conditions = this.buildStandardFilter(eb, "Bytes", payload, import_kysely.sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Bytes", false), (value) => this.buildBytesFilter(eb, table, field, value), true, [
|
|
574
585
|
"equals",
|
|
575
586
|
"in",
|
|
576
587
|
"notIn",
|
|
@@ -669,10 +680,10 @@ var BaseCrudDialect = class {
|
|
|
669
680
|
return negated ? sort === "asc" ? "desc" : "asc" : sort;
|
|
670
681
|
}
|
|
671
682
|
true(eb) {
|
|
672
|
-
return eb.lit(this.transformPrimitive(true, "Boolean"));
|
|
683
|
+
return eb.lit(this.transformPrimitive(true, "Boolean", false));
|
|
673
684
|
}
|
|
674
685
|
false(eb) {
|
|
675
|
-
return eb.lit(this.transformPrimitive(false, "Boolean"));
|
|
686
|
+
return eb.lit(this.transformPrimitive(false, "Boolean", false));
|
|
676
687
|
}
|
|
677
688
|
isTrue(expression) {
|
|
678
689
|
const node = expression.toOperationNode();
|
|
@@ -721,14 +732,18 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
721
732
|
get provider() {
|
|
722
733
|
return "postgresql";
|
|
723
734
|
}
|
|
724
|
-
transformPrimitive(value, type) {
|
|
735
|
+
transformPrimitive(value, type, forArrayField) {
|
|
725
736
|
if (value === void 0) {
|
|
726
737
|
return value;
|
|
727
738
|
}
|
|
728
739
|
if (Array.isArray(value)) {
|
|
729
|
-
|
|
740
|
+
if (type === "Json" && !forArrayField) {
|
|
741
|
+
return JSON.stringify(value);
|
|
742
|
+
} else {
|
|
743
|
+
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
744
|
+
}
|
|
730
745
|
} else {
|
|
731
|
-
return (0, import_ts_pattern2.match)(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).otherwise(() => value);
|
|
746
|
+
return (0, import_ts_pattern2.match)(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
|
|
732
747
|
}
|
|
733
748
|
}
|
|
734
749
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
@@ -795,25 +810,33 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
|
|
|
795
810
|
buildFieldRef(this.schema, relationModel, field, this.options, eb)
|
|
796
811
|
]).flatMap((v) => v));
|
|
797
812
|
} else if (payload.select) {
|
|
798
|
-
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) =>
|
|
799
|
-
|
|
800
|
-
buildFieldRef(this.schema, relationModel, field, this.options, eb)
|
|
801
|
-
|
|
813
|
+
objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => {
|
|
814
|
+
const fieldDef = requireField(this.schema, relationModel, field);
|
|
815
|
+
const fieldValue = fieldDef.relation ? eb.ref(`${parentName}$${relationField}$${field}.$j`) : buildFieldRef(this.schema, relationModel, field, this.options, eb);
|
|
816
|
+
return [
|
|
817
|
+
import_kysely2.sql.lit(field),
|
|
818
|
+
fieldValue
|
|
819
|
+
];
|
|
820
|
+
}).flatMap((v) => v));
|
|
802
821
|
}
|
|
803
822
|
if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
|
|
804
823
|
objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
|
|
805
824
|
import_kysely2.sql.lit(field),
|
|
825
|
+
// reference the synthesized JSON field
|
|
806
826
|
eb.ref(`${parentName}$${relationField}$${field}.$j`)
|
|
807
827
|
]).flatMap((v) => v));
|
|
808
828
|
}
|
|
809
829
|
return objArgs;
|
|
810
830
|
}
|
|
811
|
-
buildRelationJoins(
|
|
831
|
+
buildRelationJoins(relationModel, relationField, qb, payload, parentName) {
|
|
812
832
|
let result = qb;
|
|
813
|
-
if (typeof payload === "object"
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
833
|
+
if (typeof payload === "object") {
|
|
834
|
+
const selectInclude = payload.include ?? payload.select;
|
|
835
|
+
if (selectInclude && typeof selectInclude === "object") {
|
|
836
|
+
Object.entries(selectInclude).filter(([, value]) => value).filter(([field]) => isRelationField(this.schema, relationModel, field)).forEach(([field, value]) => {
|
|
837
|
+
result = this.buildRelationJSON(relationModel, result, field, `${parentName}$${relationField}`, value);
|
|
838
|
+
});
|
|
839
|
+
}
|
|
817
840
|
}
|
|
818
841
|
return result;
|
|
819
842
|
}
|
|
@@ -866,14 +889,14 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
|
|
|
866
889
|
get provider() {
|
|
867
890
|
return "sqlite";
|
|
868
891
|
}
|
|
869
|
-
transformPrimitive(value, type) {
|
|
892
|
+
transformPrimitive(value, type, _forArrayField) {
|
|
870
893
|
if (value === void 0) {
|
|
871
894
|
return value;
|
|
872
895
|
}
|
|
873
896
|
if (Array.isArray(value)) {
|
|
874
|
-
return value.map((v) => this.transformPrimitive(v, type));
|
|
897
|
+
return value.map((v) => this.transformPrimitive(v, type, false));
|
|
875
898
|
} else {
|
|
876
|
-
return (0, import_ts_pattern3.match)(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).otherwise(() => value);
|
|
899
|
+
return (0, import_ts_pattern3.match)(type).with("Boolean", () => value ? 1 : 0).with("DateTime", () => value instanceof Date ? value.toISOString() : value).with("Decimal", () => value.toString()).with("Bytes", () => Buffer.from(value)).with("Json", () => JSON.stringify(value)).otherwise(() => value);
|
|
877
900
|
}
|
|
878
901
|
}
|
|
879
902
|
buildRelationSelection(query, model, relationField, parentAlias, payload) {
|
|
@@ -1478,11 +1501,11 @@ var ExpressionEvaluator = class {
|
|
|
1478
1501
|
// src/plugins/policy/utils.ts
|
|
1479
1502
|
var import_kysely5 = require("kysely");
|
|
1480
1503
|
function trueNode(dialect) {
|
|
1481
|
-
return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean"));
|
|
1504
|
+
return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
|
|
1482
1505
|
}
|
|
1483
1506
|
__name(trueNode, "trueNode");
|
|
1484
1507
|
function falseNode(dialect) {
|
|
1485
|
-
return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean"));
|
|
1508
|
+
return import_kysely5.ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
|
|
1486
1509
|
}
|
|
1487
1510
|
__name(falseNode, "falseNode");
|
|
1488
1511
|
function isTrueNode(node) {
|
|
@@ -1740,7 +1763,7 @@ var ExpressionTransformer = class {
|
|
|
1740
1763
|
}
|
|
1741
1764
|
}
|
|
1742
1765
|
transformValue(value, type) {
|
|
1743
|
-
return import_kysely6.ValueNode.create(this.dialect.transformPrimitive(value, type) ?? null);
|
|
1766
|
+
return import_kysely6.ValueNode.create(this.dialect.transformPrimitive(value, type, false) ?? null);
|
|
1744
1767
|
}
|
|
1745
1768
|
_unary(expr2, context) {
|
|
1746
1769
|
(0, import_common_helpers5.invariant)(expr2.op === "!", 'only "!" operator is supported');
|
|
@@ -1983,7 +2006,7 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
1983
2006
|
get kysely() {
|
|
1984
2007
|
return this.client.$qb;
|
|
1985
2008
|
}
|
|
1986
|
-
async handle(node, proceed
|
|
2009
|
+
async handle(node, proceed) {
|
|
1987
2010
|
if (!this.isCrudQueryNode(node)) {
|
|
1988
2011
|
throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
|
|
1989
2012
|
}
|
|
@@ -2003,27 +2026,20 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
2003
2026
|
if (!mutationRequiresTransaction && !node.returning) {
|
|
2004
2027
|
return proceed(this.transformNode(node));
|
|
2005
2028
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
const
|
|
2013
|
-
if (
|
|
2014
|
-
|
|
2015
|
-
if (readBackResult.rows.length !== result2.rows.length) {
|
|
2016
|
-
readBackError = true;
|
|
2017
|
-
}
|
|
2018
|
-
return readBackResult;
|
|
2019
|
-
} else {
|
|
2020
|
-
return result2;
|
|
2029
|
+
if (import_kysely7.InsertQueryNode.is(node)) {
|
|
2030
|
+
await this.enforcePreCreatePolicy(node, proceed);
|
|
2031
|
+
}
|
|
2032
|
+
const transformedNode = this.transformNode(node);
|
|
2033
|
+
const result = await proceed(transformedNode);
|
|
2034
|
+
if (!this.onlyReturningId(node)) {
|
|
2035
|
+
const readBackResult = await this.processReadBack(node, result, proceed);
|
|
2036
|
+
if (readBackResult.rows.length !== result.rows.length) {
|
|
2037
|
+
throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
|
|
2021
2038
|
}
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2039
|
+
return readBackResult;
|
|
2040
|
+
} else {
|
|
2041
|
+
return result;
|
|
2025
2042
|
}
|
|
2026
|
-
return result;
|
|
2027
2043
|
}
|
|
2028
2044
|
onlyReturningId(node) {
|
|
2029
2045
|
if (!node.returning) {
|
|
@@ -2084,11 +2100,11 @@ var PolicyHandler = class extends import_kysely7.OperationNodeTransformer {
|
|
|
2084
2100
|
if (typeof item === "object" && item && "kind" in item) {
|
|
2085
2101
|
(0, import_common_helpers6.invariant)(item.kind === "ValueNode", "expecting a ValueNode");
|
|
2086
2102
|
result.push({
|
|
2087
|
-
node: import_kysely7.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type)),
|
|
2103
|
+
node: import_kysely7.ValueNode.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
|
|
2088
2104
|
raw: item.value
|
|
2089
2105
|
});
|
|
2090
2106
|
} else {
|
|
2091
|
-
const value = this.dialect.transformPrimitive(item, fieldDef.type);
|
|
2107
|
+
const value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
|
|
2092
2108
|
if (Array.isArray(value)) {
|
|
2093
2109
|
result.push({
|
|
2094
2110
|
node: import_kysely7.RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
|
|
@@ -2319,9 +2335,18 @@ var PolicyPlugin = class {
|
|
|
2319
2335
|
get description() {
|
|
2320
2336
|
return "Enforces access policies defined in the schema.";
|
|
2321
2337
|
}
|
|
2322
|
-
onKyselyQuery({
|
|
2338
|
+
onKyselyQuery({
|
|
2339
|
+
query,
|
|
2340
|
+
client,
|
|
2341
|
+
proceed
|
|
2342
|
+
/*, transaction*/
|
|
2343
|
+
}) {
|
|
2323
2344
|
const handler = new PolicyHandler(client);
|
|
2324
|
-
return handler.handle(
|
|
2345
|
+
return handler.handle(
|
|
2346
|
+
query,
|
|
2347
|
+
proceed
|
|
2348
|
+
/*, transaction*/
|
|
2349
|
+
);
|
|
2325
2350
|
}
|
|
2326
2351
|
};
|
|
2327
2352
|
// Annotate the CommonJS export names for ESM import in node:
|