@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.
@@ -1,5 +1,5 @@
1
1
  import * as kysely from 'kysely';
2
- import { V as RuntimePlugin, X as OnKyselyQueryArgs } from '../contract-DW8XGrtV.cjs';
2
+ import { V as RuntimePlugin, X as OnKyselyQueryArgs } from '../contract-BiU0iYAh.cjs';
3
3
  import { SchemaDef } from '@zenstackhq/sdk/schema';
4
4
  import 'decimal.js';
5
5
 
@@ -16,7 +16,7 @@ declare class PolicyPlugin<Schema extends SchemaDef> implements RuntimePlugin<Sc
16
16
  get id(): string;
17
17
  get name(): string;
18
18
  get description(): string;
19
- onKyselyQuery({ query, client, proceed, transaction }: OnKyselyQueryArgs<Schema>): Promise<kysely.QueryResult<any>>;
19
+ onKyselyQuery({ query, client, proceed }: OnKyselyQueryArgs<Schema>): Promise<kysely.QueryResult<any>>;
20
20
  }
21
21
 
22
22
  export { PolicyPlugin, RejectedByPolicyError };
@@ -1,5 +1,5 @@
1
1
  import * as kysely from 'kysely';
2
- import { V as RuntimePlugin, X as OnKyselyQueryArgs } from '../contract-DW8XGrtV.js';
2
+ import { V as RuntimePlugin, X as OnKyselyQueryArgs } from '../contract-BiU0iYAh.js';
3
3
  import { SchemaDef } from '@zenstackhq/sdk/schema';
4
4
  import 'decimal.js';
5
5
 
@@ -16,7 +16,7 @@ declare class PolicyPlugin<Schema extends SchemaDef> implements RuntimePlugin<Sc
16
16
  get id(): string;
17
17
  get name(): string;
18
18
  get description(): string;
19
- onKyselyQuery({ query, client, proceed, transaction }: OnKyselyQueryArgs<Schema>): Promise<kysely.QueryResult<any>>;
19
+ onKyselyQuery({ query, client, proceed }: OnKyselyQueryArgs<Schema>): Promise<kysely.QueryResult<any>>;
20
20
  }
21
21
 
22
22
  export { PolicyPlugin, RejectedByPolicyError };
@@ -31,8 +31,10 @@ var QueryError = class extends Error {
31
31
  static {
32
32
  __name(this, "QueryError");
33
33
  }
34
- constructor(message) {
35
- super(message);
34
+ constructor(message, cause) {
35
+ super(message, {
36
+ cause
37
+ });
36
38
  }
37
39
  };
38
40
  var InternalError = class extends Error {
@@ -52,7 +54,7 @@ __name(getModel, "getModel");
52
54
  function requireModel(schema, model) {
53
55
  const matchedName = Object.keys(schema.models).find((k) => k.toLowerCase() === model.toLowerCase());
54
56
  if (!matchedName) {
55
- throw new QueryError(`Model "${model}" not found`);
57
+ throw new QueryError(`Model "${model}" not found in schema`);
56
58
  }
57
59
  return schema.models[matchedName];
58
60
  }
@@ -115,6 +117,11 @@ function getRelationForeignKeyFieldPairs(schema, model, relationField) {
115
117
  }
116
118
  }
117
119
  __name(getRelationForeignKeyFieldPairs, "getRelationForeignKeyFieldPairs");
120
+ function isRelationField(schema, model, field) {
121
+ const fieldDef = requireField(schema, model, field);
122
+ return !!fieldDef.relation;
123
+ }
124
+ __name(isRelationField, "isRelationField");
118
125
  function getUniqueFields(schema, model) {
119
126
  const modelDef = requireModel(schema, model);
120
127
  const result = [];
@@ -151,7 +158,7 @@ function buildFieldRef(schema, model, field, options, eb, modelAlias) {
151
158
  computer = computedFields?.[model]?.[field];
152
159
  }
153
160
  if (!computer) {
154
- throw new QueryError(`Computed field "${field}" implementation not provided`);
161
+ throw new QueryError(`Computed field "${field}" implementation not provided for model "${model}"`);
155
162
  }
156
163
  return computer(eb);
157
164
  }
@@ -259,7 +266,7 @@ var BaseCrudDialect = class {
259
266
  this.schema = schema;
260
267
  this.options = options;
261
268
  }
262
- transformPrimitive(value, _type) {
269
+ transformPrimitive(value, _type, _forArrayField) {
263
270
  return value;
264
271
  }
265
272
  buildFilter(eb, model, modelAlias, where) {
@@ -402,7 +409,7 @@ var BaseCrudDialect = class {
402
409
  if (_value === void 0) {
403
410
  continue;
404
411
  }
405
- const value = this.transformPrimitive(_value, fieldType);
412
+ const value = this.transformPrimitive(_value, fieldType, !!fieldDef.array);
406
413
  switch (key) {
407
414
  case "equals": {
408
415
  clauses.push(this.buildLiteralFilter(eb, fieldRef, fieldType, eb.val(value)));
@@ -440,10 +447,14 @@ var BaseCrudDialect = class {
440
447
  if (isEnum(this.schema, fieldDef.type)) {
441
448
  return this.buildEnumFilter(eb, modelAlias, field, fieldDef, payload);
442
449
  }
443
- return match(fieldDef.type).with("String", () => this.buildStringFilter(eb, modelAlias, field, payload)).with(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)).exhaustive();
450
+ return match(fieldDef.type).with("String", () => this.buildStringFilter(eb, modelAlias, field, payload)).with(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", () => {
451
+ throw new InternalError("JSON filters are not supported yet");
452
+ }).with("Unsupported", () => {
453
+ throw new QueryError(`Unsupported field cannot be used in filters`);
454
+ }).exhaustive();
444
455
  }
445
456
  buildLiteralFilter(eb, lhs, type, rhs) {
446
- return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type) : rhs);
457
+ return eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformPrimitive(rhs, type, false) : rhs);
447
458
  }
448
459
  buildStandardFilter(eb, type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0) {
449
460
  if (payload === null || !isPlainObject(payload)) {
@@ -530,22 +541,22 @@ var BaseCrudDialect = class {
530
541
  }
531
542
  }
532
543
  buildNumberFilter(eb, model, table, field, type, payload) {
533
- 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));
544
+ 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));
534
545
  return this.and(eb, ...conditions);
535
546
  }
536
547
  buildBooleanFilter(eb, table, field, payload) {
537
- const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Boolean"), (value) => this.buildBooleanFilter(eb, table, field, value), true, [
548
+ const { conditions } = this.buildStandardFilter(eb, "Boolean", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Boolean", false), (value) => this.buildBooleanFilter(eb, table, field, value), true, [
538
549
  "equals",
539
550
  "not"
540
551
  ]);
541
552
  return this.and(eb, ...conditions);
542
553
  }
543
554
  buildDateTimeFilter(eb, table, field, payload) {
544
- const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "DateTime"), (value) => this.buildDateTimeFilter(eb, table, field, value), true);
555
+ const { conditions } = this.buildStandardFilter(eb, "DateTime", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "DateTime", false), (value) => this.buildDateTimeFilter(eb, table, field, value), true);
545
556
  return this.and(eb, ...conditions);
546
557
  }
547
558
  buildBytesFilter(eb, table, field, payload) {
548
- const conditions = this.buildStandardFilter(eb, "Bytes", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Bytes"), (value) => this.buildBytesFilter(eb, table, field, value), true, [
559
+ const conditions = this.buildStandardFilter(eb, "Bytes", payload, sql.ref(`${table}.${field}`), (value) => this.transformPrimitive(value, "Bytes", false), (value) => this.buildBytesFilter(eb, table, field, value), true, [
549
560
  "equals",
550
561
  "in",
551
562
  "notIn",
@@ -644,10 +655,10 @@ var BaseCrudDialect = class {
644
655
  return negated ? sort === "asc" ? "desc" : "asc" : sort;
645
656
  }
646
657
  true(eb) {
647
- return eb.lit(this.transformPrimitive(true, "Boolean"));
658
+ return eb.lit(this.transformPrimitive(true, "Boolean", false));
648
659
  }
649
660
  false(eb) {
650
- return eb.lit(this.transformPrimitive(false, "Boolean"));
661
+ return eb.lit(this.transformPrimitive(false, "Boolean", false));
651
662
  }
652
663
  isTrue(expression) {
653
664
  const node = expression.toOperationNode();
@@ -696,14 +707,18 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
696
707
  get provider() {
697
708
  return "postgresql";
698
709
  }
699
- transformPrimitive(value, type) {
710
+ transformPrimitive(value, type, forArrayField) {
700
711
  if (value === void 0) {
701
712
  return value;
702
713
  }
703
714
  if (Array.isArray(value)) {
704
- return value.map((v) => this.transformPrimitive(v, type));
715
+ if (type === "Json" && !forArrayField) {
716
+ return JSON.stringify(value);
717
+ } else {
718
+ return value.map((v) => this.transformPrimitive(v, type, false));
719
+ }
705
720
  } else {
706
- return match2(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).otherwise(() => value);
721
+ return match2(type).with("DateTime", () => value instanceof Date ? value : typeof value === "string" ? new Date(value) : value).with("Decimal", () => value !== null ? value.toString() : value).otherwise(() => value);
707
722
  }
708
723
  }
709
724
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
@@ -770,25 +785,33 @@ var PostgresCrudDialect = class extends BaseCrudDialect {
770
785
  buildFieldRef(this.schema, relationModel, field, this.options, eb)
771
786
  ]).flatMap((v) => v));
772
787
  } else if (payload.select) {
773
- objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => [
774
- sql2.lit(field),
775
- buildFieldRef(this.schema, relationModel, field, this.options, eb)
776
- ]).flatMap((v) => v));
788
+ objArgs.push(...Object.entries(payload.select).filter(([, value]) => value).map(([field]) => {
789
+ const fieldDef = requireField(this.schema, relationModel, field);
790
+ const fieldValue = fieldDef.relation ? eb.ref(`${parentName}$${relationField}$${field}.$j`) : buildFieldRef(this.schema, relationModel, field, this.options, eb);
791
+ return [
792
+ sql2.lit(field),
793
+ fieldValue
794
+ ];
795
+ }).flatMap((v) => v));
777
796
  }
778
797
  if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
779
798
  objArgs.push(...Object.entries(payload.include).filter(([, value]) => value).map(([field]) => [
780
799
  sql2.lit(field),
800
+ // reference the synthesized JSON field
781
801
  eb.ref(`${parentName}$${relationField}$${field}.$j`)
782
802
  ]).flatMap((v) => v));
783
803
  }
784
804
  return objArgs;
785
805
  }
786
- buildRelationJoins(model, relationField, qb, payload, parentName) {
806
+ buildRelationJoins(relationModel, relationField, qb, payload, parentName) {
787
807
  let result = qb;
788
- if (typeof payload === "object" && payload.include && typeof payload.include === "object") {
789
- Object.entries(payload.include).filter(([, value]) => value).forEach(([field, value]) => {
790
- result = this.buildRelationJSON(model, result, field, `${parentName}$${relationField}`, value);
791
- });
808
+ if (typeof payload === "object") {
809
+ const selectInclude = payload.include ?? payload.select;
810
+ if (selectInclude && typeof selectInclude === "object") {
811
+ Object.entries(selectInclude).filter(([, value]) => value).filter(([field]) => isRelationField(this.schema, relationModel, field)).forEach(([field, value]) => {
812
+ result = this.buildRelationJSON(relationModel, result, field, `${parentName}$${relationField}`, value);
813
+ });
814
+ }
792
815
  }
793
816
  return result;
794
817
  }
@@ -841,14 +864,14 @@ var SqliteCrudDialect = class extends BaseCrudDialect {
841
864
  get provider() {
842
865
  return "sqlite";
843
866
  }
844
- transformPrimitive(value, type) {
867
+ transformPrimitive(value, type, _forArrayField) {
845
868
  if (value === void 0) {
846
869
  return value;
847
870
  }
848
871
  if (Array.isArray(value)) {
849
- return value.map((v) => this.transformPrimitive(v, type));
872
+ return value.map((v) => this.transformPrimitive(v, type, false));
850
873
  } else {
851
- return match3(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);
874
+ return match3(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);
852
875
  }
853
876
  }
854
877
  buildRelationSelection(query, model, relationField, parentAlias, payload) {
@@ -1453,11 +1476,11 @@ var ExpressionEvaluator = class {
1453
1476
  // src/plugins/policy/utils.ts
1454
1477
  import { AliasNode, AndNode, BinaryOperationNode, FunctionNode, OperatorNode, OrNode, ParensNode, ReferenceNode, TableNode, UnaryOperationNode, ValueNode } from "kysely";
1455
1478
  function trueNode(dialect) {
1456
- return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean"));
1479
+ return ValueNode.createImmediate(dialect.transformPrimitive(true, "Boolean", false));
1457
1480
  }
1458
1481
  __name(trueNode, "trueNode");
1459
1482
  function falseNode(dialect) {
1460
- return ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean"));
1483
+ return ValueNode.createImmediate(dialect.transformPrimitive(false, "Boolean", false));
1461
1484
  }
1462
1485
  __name(falseNode, "falseNode");
1463
1486
  function isTrueNode(node) {
@@ -1715,7 +1738,7 @@ var ExpressionTransformer = class {
1715
1738
  }
1716
1739
  }
1717
1740
  transformValue(value, type) {
1718
- return ValueNode2.create(this.dialect.transformPrimitive(value, type) ?? null);
1741
+ return ValueNode2.create(this.dialect.transformPrimitive(value, type, false) ?? null);
1719
1742
  }
1720
1743
  _unary(expr2, context) {
1721
1744
  invariant5(expr2.op === "!", 'only "!" operator is supported');
@@ -1958,7 +1981,7 @@ var PolicyHandler = class extends OperationNodeTransformer {
1958
1981
  get kysely() {
1959
1982
  return this.client.$qb;
1960
1983
  }
1961
- async handle(node, proceed, transaction) {
1984
+ async handle(node, proceed) {
1962
1985
  if (!this.isCrudQueryNode(node)) {
1963
1986
  throw new RejectedByPolicyError(void 0, "non-CRUD queries are not allowed");
1964
1987
  }
@@ -1978,27 +2001,20 @@ var PolicyHandler = class extends OperationNodeTransformer {
1978
2001
  if (!mutationRequiresTransaction && !node.returning) {
1979
2002
  return proceed(this.transformNode(node));
1980
2003
  }
1981
- let readBackError = false;
1982
- const result = await transaction(async (txProceed) => {
1983
- if (InsertQueryNode.is(node)) {
1984
- await this.enforcePreCreatePolicy(node, txProceed);
1985
- }
1986
- const transformedNode = this.transformNode(node);
1987
- const result2 = await txProceed(transformedNode);
1988
- if (!this.onlyReturningId(node)) {
1989
- const readBackResult = await this.processReadBack(node, result2, txProceed);
1990
- if (readBackResult.rows.length !== result2.rows.length) {
1991
- readBackError = true;
1992
- }
1993
- return readBackResult;
1994
- } else {
1995
- return result2;
2004
+ if (InsertQueryNode.is(node)) {
2005
+ await this.enforcePreCreatePolicy(node, proceed);
2006
+ }
2007
+ const transformedNode = this.transformNode(node);
2008
+ const result = await proceed(transformedNode);
2009
+ if (!this.onlyReturningId(node)) {
2010
+ const readBackResult = await this.processReadBack(node, result, proceed);
2011
+ if (readBackResult.rows.length !== result.rows.length) {
2012
+ throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
1996
2013
  }
1997
- });
1998
- if (readBackError) {
1999
- throw new RejectedByPolicyError(mutationModel, "result is not allowed to be read back");
2014
+ return readBackResult;
2015
+ } else {
2016
+ return result;
2000
2017
  }
2001
- return result;
2002
2018
  }
2003
2019
  onlyReturningId(node) {
2004
2020
  if (!node.returning) {
@@ -2059,11 +2075,11 @@ var PolicyHandler = class extends OperationNodeTransformer {
2059
2075
  if (typeof item === "object" && item && "kind" in item) {
2060
2076
  invariant6(item.kind === "ValueNode", "expecting a ValueNode");
2061
2077
  result.push({
2062
- node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type)),
2078
+ node: ValueNode3.create(this.dialect.transformPrimitive(item.value, fieldDef.type, !!fieldDef.array)),
2063
2079
  raw: item.value
2064
2080
  });
2065
2081
  } else {
2066
- const value = this.dialect.transformPrimitive(item, fieldDef.type);
2082
+ const value = this.dialect.transformPrimitive(item, fieldDef.type, !!fieldDef.array);
2067
2083
  if (Array.isArray(value)) {
2068
2084
  result.push({
2069
2085
  node: RawNode.createWithSql(this.dialect.buildArrayLiteralSQL(value)),
@@ -2294,9 +2310,18 @@ var PolicyPlugin = class {
2294
2310
  get description() {
2295
2311
  return "Enforces access policies defined in the schema.";
2296
2312
  }
2297
- onKyselyQuery({ query, client, proceed, transaction }) {
2313
+ onKyselyQuery({
2314
+ query,
2315
+ client,
2316
+ proceed
2317
+ /*, transaction*/
2318
+ }) {
2298
2319
  const handler = new PolicyHandler(client);
2299
- return handler.handle(query, proceed, transaction);
2320
+ return handler.handle(
2321
+ query,
2322
+ proceed
2323
+ /*, transaction*/
2324
+ );
2300
2325
  }
2301
2326
  };
2302
2327
  export {