@zenstackhq/runtime 3.0.0-alpha.30 → 3.0.0-alpha.32

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/index.js CHANGED
@@ -6,8 +6,8 @@ var __export = (target, all) => {
6
6
  };
7
7
 
8
8
  // src/client/client-impl.ts
9
- import { invariant as invariant12 } from "@zenstackhq/common-helpers";
10
- import { CompiledQuery, DefaultConnectionProvider, DefaultQueryExecutor as DefaultQueryExecutor2, Kysely, Log, sql as sql9 } from "kysely";
9
+ import { invariant as invariant15 } from "@zenstackhq/common-helpers";
10
+ import { CompiledQuery, DefaultConnectionProvider, DefaultQueryExecutor as DefaultQueryExecutor2, Kysely, Log, sql as sql9, Transaction } from "kysely";
11
11
 
12
12
  // src/client/crud/operations/aggregate.ts
13
13
  import { sql as sql5 } from "kysely";
@@ -2704,6 +2704,16 @@ function clone(value) {
2704
2704
  }
2705
2705
  __name(clone, "clone");
2706
2706
 
2707
+ // src/client/contract.ts
2708
+ var TransactionIsolationLevel = /* @__PURE__ */ function(TransactionIsolationLevel2) {
2709
+ TransactionIsolationLevel2["ReadUncommitted"] = "read uncommitted";
2710
+ TransactionIsolationLevel2["ReadCommitted"] = "read committed";
2711
+ TransactionIsolationLevel2["RepeatableRead"] = "repeatable read";
2712
+ TransactionIsolationLevel2["Serializable"] = "serializable";
2713
+ TransactionIsolationLevel2["Snapshot"] = "snapshot";
2714
+ return TransactionIsolationLevel2;
2715
+ }({});
2716
+
2707
2717
  // src/client/crud/operations/base.ts
2708
2718
  var BaseOperationHandler = class {
2709
2719
  static {
@@ -3942,7 +3952,7 @@ var BaseOperationHandler = class {
3942
3952
  return callback(this.kysely);
3943
3953
  } else {
3944
3954
  let txBuilder = this.kysely.transaction();
3945
- txBuilder = txBuilder.setIsolationLevel(isolationLevel ?? "repeatable read");
3955
+ txBuilder = txBuilder.setIsolationLevel(isolationLevel ?? TransactionIsolationLevel.RepeatableRead);
3946
3956
  return txBuilder.execute(callback);
3947
3957
  }
3948
3958
  }
@@ -5350,10 +5360,11 @@ var InputValidator = class {
5350
5360
  makeGroupBySchema(model) {
5351
5361
  const modelDef = requireModel(this.schema, model);
5352
5362
  const nonRelationFields = Object.keys(modelDef.fields).filter((field) => !modelDef.fields[field]?.relation);
5363
+ const bySchema = nonRelationFields.length > 0 ? this.orArray(z.enum(nonRelationFields), true) : z.never();
5353
5364
  let schema = z.strictObject({
5354
5365
  where: this.makeWhereSchema(model, false).optional(),
5355
5366
  orderBy: this.orArray(this.makeOrderBySchema(model, false, true), true).optional(),
5356
- by: this.orArray(z.enum(nonRelationFields), true),
5367
+ by: bySchema,
5357
5368
  having: this.makeHavingSchema(model).optional(),
5358
5369
  skip: this.makeSkipSchema().optional(),
5359
5370
  take: this.makeTakeSchema().optional(),
@@ -5507,7 +5518,11 @@ var ZenStackDriver = class {
5507
5518
  this.#txConnections.delete(connection);
5508
5519
  if (callbacks) {
5509
5520
  for (const callback of callbacks) {
5510
- await callback();
5521
+ try {
5522
+ await callback();
5523
+ } catch (err) {
5524
+ console.error(`Error executing transaction commit callback: ${err}`);
5525
+ }
5511
5526
  }
5512
5527
  }
5513
5528
  return result;
@@ -5625,12 +5640,32 @@ function performanceNow() {
5625
5640
  __name(performanceNow, "performanceNow");
5626
5641
 
5627
5642
  // src/client/executor/zenstack-query-executor.ts
5628
- import { AndNode as AndNode2, DefaultQueryExecutor, DeleteQueryNode as DeleteQueryNode2, InsertQueryNode as InsertQueryNode2, ReturningNode as ReturningNode3, SelectionNode as SelectionNode4, UpdateQueryNode as UpdateQueryNode2, WhereNode as WhereNode3 } from "kysely";
5629
- import { nanoid as nanoid2 } from "nanoid";
5643
+ import { invariant as invariant11 } from "@zenstackhq/common-helpers";
5644
+ import { AndNode as AndNode2, DefaultQueryExecutor, DeleteQueryNode as DeleteQueryNode2, InsertQueryNode as InsertQueryNode2, ReturningNode as ReturningNode2, SelectionNode as SelectionNode4, SingleConnectionProvider, TableNode as TableNode5, UpdateQueryNode as UpdateQueryNode2, WhereNode as WhereNode3 } from "kysely";
5630
5645
  import { match as match16 } from "ts-pattern";
5631
5646
 
5647
+ // src/client/executor/kysely-utils.ts
5648
+ import { invariant as invariant9 } from "@zenstackhq/common-helpers";
5649
+ import { AliasNode as AliasNode4, IdentifierNode as IdentifierNode3 } from "kysely";
5650
+ function stripAlias(node) {
5651
+ if (AliasNode4.is(node)) {
5652
+ invariant9(IdentifierNode3.is(node.alias), "Expected identifier as alias");
5653
+ return {
5654
+ alias: node.alias.name,
5655
+ node: node.node
5656
+ };
5657
+ } else {
5658
+ return {
5659
+ alias: void 0,
5660
+ node
5661
+ };
5662
+ }
5663
+ }
5664
+ __name(stripAlias, "stripAlias");
5665
+
5632
5666
  // src/client/executor/name-mapper.ts
5633
- import { AliasNode as AliasNode4, ColumnNode as ColumnNode3, IdentifierNode as IdentifierNode3, OperationNodeTransformer as OperationNodeTransformer2, ReferenceNode as ReferenceNode3, ReturningNode as ReturningNode2, SelectAllNode, SelectionNode as SelectionNode3, TableNode as TableNode4 } from "kysely";
5667
+ import { invariant as invariant10 } from "@zenstackhq/common-helpers";
5668
+ import { AliasNode as AliasNode5, ColumnNode as ColumnNode3, FromNode as FromNode3, IdentifierNode as IdentifierNode4, OperationNodeTransformer as OperationNodeTransformer2, ReferenceNode as ReferenceNode3, SelectAllNode, SelectionNode as SelectionNode3, TableNode as TableNode4 } from "kysely";
5634
5669
  var QueryNameMapper = class extends OperationNodeTransformer2 {
5635
5670
  static {
5636
5671
  __name(this, "QueryNameMapper");
@@ -5638,7 +5673,7 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
5638
5673
  schema;
5639
5674
  modelToTableMap = /* @__PURE__ */ new Map();
5640
5675
  fieldToColumnMap = /* @__PURE__ */ new Map();
5641
- modelStack = [];
5676
+ modelScopes = [];
5642
5677
  constructor(schema) {
5643
5678
  super(), this.schema = schema;
5644
5679
  for (const [modelName, modelDef] of Object.entries(schema.models)) {
@@ -5654,150 +5689,185 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
5654
5689
  }
5655
5690
  }
5656
5691
  }
5657
- get currentModel() {
5658
- return this.modelStack[this.modelStack.length - 1];
5659
- }
5660
- transformCreateTable(node) {
5661
- try {
5662
- this.modelStack.push(node.table.table.identifier.name);
5663
- return super.transformCreateTable(node);
5664
- } finally {
5665
- this.modelStack.pop();
5692
+ // #region overrides
5693
+ transformSelectQuery(node) {
5694
+ if (!node.from?.froms) {
5695
+ return super.transformSelectQuery(node);
5666
5696
  }
5697
+ const scopes = this.createScopesFromFroms(node.from, true);
5698
+ return this.withScopes(scopes, () => {
5699
+ return {
5700
+ ...super.transformSelectQuery(node),
5701
+ // convert "from" to nested query as needed
5702
+ from: this.processFrom(node.from)
5703
+ };
5704
+ });
5667
5705
  }
5668
5706
  transformInsertQuery(node) {
5669
- try {
5670
- if (node.into?.table.identifier.name) {
5671
- this.modelStack.push(node.into.table.identifier.name);
5672
- }
5707
+ if (!node.into) {
5673
5708
  return super.transformInsertQuery(node);
5674
- } finally {
5675
- if (node.into?.table.identifier.name) {
5676
- this.modelStack.pop();
5677
- }
5678
5709
  }
5710
+ return this.withScope({
5711
+ model: node.into.table.identifier.name
5712
+ }, () => ({
5713
+ ...super.transformInsertQuery(node),
5714
+ // map table name
5715
+ into: this.processTableRef(node.into)
5716
+ }));
5679
5717
  }
5680
5718
  transformReturning(node) {
5681
- return ReturningNode2.create(this.transformSelections(node.selections, node));
5719
+ return {
5720
+ kind: node.kind,
5721
+ // map column names in returning selections (include returningAll)
5722
+ selections: this.processSelections(node.selections)
5723
+ };
5682
5724
  }
5683
- transformUpdateQuery(node) {
5684
- let pushed = false;
5685
- if (node.table && TableNode4.is(node.table)) {
5686
- this.modelStack.push(node.table.table.identifier.name);
5687
- pushed = true;
5688
- }
5689
- try {
5690
- return super.transformUpdateQuery(node);
5691
- } finally {
5692
- if (pushed) {
5693
- this.modelStack.pop();
5725
+ transformJoin(node) {
5726
+ const { alias, node: innerNode } = stripAlias(node.table);
5727
+ if (TableNode4.is(innerNode)) {
5728
+ const modelName = innerNode.table.identifier.name;
5729
+ if (this.hasMappedColumns(modelName)) {
5730
+ const select = this.createSelectAll(modelName);
5731
+ return {
5732
+ ...super.transformJoin(node),
5733
+ table: this.wrapAlias(select, alias ?? modelName)
5734
+ };
5694
5735
  }
5695
5736
  }
5737
+ return super.transformJoin(node);
5696
5738
  }
5697
- transformDeleteQuery(node) {
5698
- let pushed = false;
5699
- if (node.from?.froms && node.from.froms.length === 1 && node.from.froms[0]) {
5700
- const from = node.from.froms[0];
5701
- if (TableNode4.is(from)) {
5702
- this.modelStack.push(from.table.identifier.name);
5703
- pushed = true;
5704
- } else if (AliasNode4.is(from) && TableNode4.is(from.node)) {
5705
- this.modelStack.push(from.node.table.identifier.name);
5706
- pushed = true;
5707
- }
5739
+ transformReference(node) {
5740
+ if (!ColumnNode3.is(node.column)) {
5741
+ return super.transformReference(node);
5708
5742
  }
5709
- try {
5710
- return super.transformDeleteQuery(node);
5711
- } finally {
5712
- if (pushed) {
5713
- this.modelStack.pop();
5743
+ const { fieldDef, modelDef, scope } = this.resolveFieldFromScopes(node.column.column.name, node.table?.table.identifier.name);
5744
+ if (fieldDef && !scope.namesMapped) {
5745
+ const mappedFieldName = this.mapFieldName(modelDef.name, fieldDef.name);
5746
+ let mappedTableName = node.table?.table.identifier.name;
5747
+ if (mappedTableName) {
5748
+ if (scope.alias === mappedTableName) {
5749
+ } else if (scope.model === mappedTableName) {
5750
+ mappedTableName = this.mapTableName(scope.model);
5751
+ }
5714
5752
  }
5753
+ return ReferenceNode3.create(ColumnNode3.create(mappedFieldName), mappedTableName ? TableNode4.create(mappedTableName) : void 0);
5754
+ } else {
5755
+ return super.transformReference(node);
5715
5756
  }
5716
5757
  }
5717
- transformSelectQuery(node) {
5718
- if (!node.from?.froms || node.from.froms.length === 0) {
5719
- return super.transformSelectQuery(node);
5758
+ transformColumn(node) {
5759
+ const { modelDef, fieldDef, scope } = this.resolveFieldFromScopes(node.column.name);
5760
+ if (!fieldDef || scope.namesMapped) {
5761
+ return super.transformColumn(node);
5720
5762
  }
5721
- if (node.from.froms.length > 1) {
5722
- throw new InternalError(`SelectQueryNode must have a single table in from clause`);
5763
+ const mappedName = this.mapFieldName(modelDef.name, fieldDef.name);
5764
+ return ColumnNode3.create(mappedName);
5765
+ }
5766
+ transformUpdateQuery(node) {
5767
+ if (!node.table) {
5768
+ return super.transformUpdateQuery(node);
5723
5769
  }
5724
- let pushed = false;
5725
- const from = node.from.froms[0];
5726
- if (TableNode4.is(from)) {
5727
- this.modelStack.push(from.table.identifier.name);
5728
- pushed = true;
5729
- } else if (AliasNode4.is(from) && TableNode4.is(from.node)) {
5730
- this.modelStack.push(from.node.table.identifier.name);
5731
- pushed = true;
5770
+ const { alias, node: innerTable } = stripAlias(node.table);
5771
+ if (!innerTable || !TableNode4.is(innerTable)) {
5772
+ return super.transformUpdateQuery(node);
5732
5773
  }
5733
- const selections = node.selections ? this.transformSelections(node.selections, node) : node.selections;
5734
- try {
5774
+ return this.withScope({
5775
+ model: innerTable.table.identifier.name,
5776
+ alias
5777
+ }, () => {
5735
5778
  return {
5736
- ...super.transformSelectQuery(node),
5737
- selections
5779
+ ...super.transformUpdateQuery(node),
5780
+ // map table name
5781
+ table: this.wrapAlias(this.processTableRef(innerTable), alias)
5738
5782
  };
5739
- } finally {
5740
- if (pushed) {
5741
- this.modelStack.pop();
5783
+ });
5784
+ }
5785
+ transformDeleteQuery(node) {
5786
+ const scopes = this.createScopesFromFroms(node.from, false);
5787
+ const froms = node.from.froms.map((from) => {
5788
+ const { alias, node: innerNode } = stripAlias(from);
5789
+ if (TableNode4.is(innerNode)) {
5790
+ return this.wrapAlias(this.processTableRef(innerNode), alias);
5791
+ } else {
5792
+ return super.transformNode(from);
5742
5793
  }
5743
- }
5794
+ });
5795
+ return this.withScopes(scopes, () => {
5796
+ return {
5797
+ ...super.transformDeleteQuery(node),
5798
+ from: FromNode3.create(froms)
5799
+ };
5800
+ });
5744
5801
  }
5745
- transformSelections(selections, contextNode) {
5746
- const result = [];
5747
- for (const selection of selections) {
5748
- let selectAllFromModel = void 0;
5749
- let isSelectAll = false;
5750
- if (SelectAllNode.is(selection.selection)) {
5751
- selectAllFromModel = this.currentModel;
5752
- isSelectAll = true;
5753
- } else if (ReferenceNode3.is(selection.selection) && SelectAllNode.is(selection.selection.column)) {
5754
- selectAllFromModel = selection.selection.table?.table.identifier.name ?? this.currentModel;
5755
- isSelectAll = true;
5756
- }
5757
- if (isSelectAll) {
5758
- if (!selectAllFromModel) {
5759
- continue;
5802
+ // #endregion
5803
+ // #region utils
5804
+ resolveFieldFromScopes(name, qualifier) {
5805
+ for (const scope of this.modelScopes.toReversed()) {
5806
+ if (qualifier) {
5807
+ if (scope.alias) {
5808
+ if (qualifier !== scope.alias) {
5809
+ continue;
5810
+ }
5760
5811
  } else {
5761
- const scalarFields = this.getModelScalarFields(contextNode, selectAllFromModel);
5762
- const fromModelDef = requireModel(this.schema, selectAllFromModel);
5763
- const mappedTableName = this.getMappedName(fromModelDef) ?? selectAllFromModel;
5764
- result.push(...scalarFields.map((fieldName) => {
5765
- const fieldRef = ReferenceNode3.create(ColumnNode3.create(this.mapFieldName(fieldName)), TableNode4.create(mappedTableName));
5766
- return SelectionNode3.create(this.fieldHasMappedName(fieldName) ? AliasNode4.create(fieldRef, IdentifierNode3.create(fieldName)) : fieldRef);
5767
- }));
5812
+ if (qualifier !== scope.model) {
5813
+ continue;
5814
+ }
5768
5815
  }
5769
- } else {
5770
- result.push(this.transformSelectionWithAlias(selection));
5816
+ }
5817
+ const modelDef = getModel(this.schema, scope.model);
5818
+ if (!modelDef) {
5819
+ continue;
5820
+ }
5821
+ if (modelDef.fields[name]) {
5822
+ return {
5823
+ modelDef,
5824
+ fieldDef: modelDef.fields[name],
5825
+ scope
5826
+ };
5771
5827
  }
5772
5828
  }
5773
- return result;
5829
+ return {
5830
+ modelDef: void 0,
5831
+ fieldDef: void 0,
5832
+ scope: void 0
5833
+ };
5774
5834
  }
5775
- transformSelectionWithAlias(node) {
5776
- if (ColumnNode3.is(node.selection) && this.fieldHasMappedName(node.selection.column.name)) {
5777
- return SelectionNode3.create(AliasNode4.create(this.transformColumn(node.selection), IdentifierNode3.create(node.selection.column.name)));
5778
- } else if (ReferenceNode3.is(node.selection) && this.fieldHasMappedName(node.selection.column.column.name)) {
5779
- return SelectionNode3.create(AliasNode4.create(this.transformReference(node.selection), IdentifierNode3.create(node.selection.column.column.name)));
5780
- } else {
5781
- return this.transformSelection(node);
5835
+ pushScope(scope) {
5836
+ this.modelScopes.push(scope);
5837
+ }
5838
+ withScope(scope, fn) {
5839
+ this.pushScope(scope);
5840
+ try {
5841
+ return fn();
5842
+ } finally {
5843
+ this.modelScopes.pop();
5782
5844
  }
5783
5845
  }
5784
- fieldHasMappedName(name) {
5785
- if (!this.currentModel) {
5786
- return false;
5846
+ withScopes(scopes, fn) {
5847
+ scopes.forEach((s) => this.pushScope(s));
5848
+ try {
5849
+ return fn();
5850
+ } finally {
5851
+ scopes.forEach(() => this.modelScopes.pop());
5787
5852
  }
5788
- return this.fieldToColumnMap.has(`${this.currentModel}.${name}`);
5789
5853
  }
5790
- transformTable(node) {
5791
- const tableName = node.table.identifier.name;
5792
- const mappedName = this.modelToTableMap.get(tableName);
5793
- if (mappedName) {
5794
- return TableNode4.create(mappedName);
5795
- } else {
5854
+ wrapAlias(node, alias) {
5855
+ return alias ? AliasNode5.create(node, IdentifierNode4.create(alias)) : node;
5856
+ }
5857
+ ensureAlias(node, alias, fallbackName) {
5858
+ if (!node) {
5796
5859
  return node;
5797
5860
  }
5861
+ return alias ? AliasNode5.create(node, IdentifierNode4.create(alias)) : AliasNode5.create(node, IdentifierNode4.create(fallbackName));
5798
5862
  }
5799
- transformColumn(node) {
5800
- return ColumnNode3.create(this.mapFieldName(node.column.name));
5863
+ processTableRef(node) {
5864
+ if (!node) {
5865
+ return node;
5866
+ }
5867
+ if (!TableNode4.is(node)) {
5868
+ return super.transformNode(node);
5869
+ }
5870
+ return TableNode4.create(this.mapTableName(node.table.identifier.name));
5801
5871
  }
5802
5872
  getMappedName(def) {
5803
5873
  const mapAttr = def.attributes?.find((attr) => attr.name === "@@map" || attr.name === "@map");
@@ -5809,28 +5879,132 @@ var QueryNameMapper = class extends OperationNodeTransformer2 {
5809
5879
  }
5810
5880
  return void 0;
5811
5881
  }
5812
- mapFieldName(fieldName) {
5813
- if (!this.currentModel) {
5814
- return fieldName;
5882
+ mapFieldName(model, field) {
5883
+ const mappedName = this.fieldToColumnMap.get(`${model}.${field}`);
5884
+ if (mappedName) {
5885
+ return mappedName;
5886
+ } else {
5887
+ return field;
5815
5888
  }
5816
- const mappedName = this.fieldToColumnMap.get(`${this.currentModel}.${fieldName}`);
5889
+ }
5890
+ mapTableName(tableName) {
5891
+ const mappedName = this.modelToTableMap.get(tableName);
5817
5892
  if (mappedName) {
5818
5893
  return mappedName;
5819
5894
  } else {
5820
- return fieldName;
5895
+ return tableName;
5821
5896
  }
5822
5897
  }
5823
- requireCurrentModel(node) {
5824
- if (!this.currentModel) {
5825
- throw new InternalError(`Missing model context for "${node}"`);
5898
+ hasMappedColumns(modelName) {
5899
+ return [
5900
+ ...this.fieldToColumnMap.keys()
5901
+ ].some((key) => key.startsWith(modelName + "."));
5902
+ }
5903
+ createScopesFromFroms(node, namesMapped) {
5904
+ if (!node) {
5905
+ return [];
5826
5906
  }
5907
+ return node.froms.map((from) => {
5908
+ const { alias, node: innerNode } = stripAlias(from);
5909
+ if (innerNode && TableNode4.is(innerNode)) {
5910
+ return {
5911
+ model: innerNode.table.identifier.name,
5912
+ alias,
5913
+ namesMapped
5914
+ };
5915
+ } else {
5916
+ return void 0;
5917
+ }
5918
+ }).filter((s) => !!s);
5827
5919
  }
5828
- getModelScalarFields(contextNode, model) {
5829
- this.requireCurrentModel(contextNode);
5830
- model = model ?? this.currentModel;
5920
+ // convert a "from" node to a nested query if there are columns with name mapping
5921
+ processFrom(node) {
5922
+ return {
5923
+ ...super.transformFrom(node),
5924
+ froms: node.froms.map((from) => {
5925
+ const { alias, node: innerNode } = stripAlias(from);
5926
+ if (!innerNode) {
5927
+ return super.transformNode(from);
5928
+ }
5929
+ if (TableNode4.is(innerNode)) {
5930
+ if (this.hasMappedColumns(innerNode.table.identifier.name)) {
5931
+ const selectAll = this.createSelectAll(innerNode.table.identifier.name);
5932
+ return this.ensureAlias(selectAll, alias, innerNode.table.identifier.name);
5933
+ }
5934
+ }
5935
+ return this.transformNode(from);
5936
+ })
5937
+ };
5938
+ }
5939
+ // create a `SelectQueryNode` for the given model with all columns mapped
5940
+ createSelectAll(model) {
5831
5941
  const modelDef = requireModel(this.schema, model);
5832
- const scalarFields = Object.entries(modelDef.fields).filter(([, fieldDef]) => !fieldDef.relation && !fieldDef.computed && !fieldDef.originModel).map(([fieldName]) => fieldName);
5833
- return scalarFields;
5942
+ const tableName = this.mapTableName(model);
5943
+ return {
5944
+ kind: "SelectQueryNode",
5945
+ from: FromNode3.create([
5946
+ TableNode4.create(tableName)
5947
+ ]),
5948
+ selections: this.getModelFields(modelDef).map((fieldDef) => {
5949
+ const columnName = this.mapFieldName(model, fieldDef.name);
5950
+ const columnRef = ReferenceNode3.create(ColumnNode3.create(columnName), TableNode4.create(tableName));
5951
+ if (columnName !== fieldDef.name) {
5952
+ const aliased = AliasNode5.create(columnRef, IdentifierNode4.create(fieldDef.name));
5953
+ return SelectionNode3.create(aliased);
5954
+ } else {
5955
+ return SelectionNode3.create(columnRef);
5956
+ }
5957
+ })
5958
+ };
5959
+ }
5960
+ getModelFields(modelDef) {
5961
+ return Object.values(modelDef.fields).filter((f) => !f.relation && !f.computed && !f.originModel);
5962
+ }
5963
+ processSelections(selections) {
5964
+ const result = [];
5965
+ selections.forEach((selection) => {
5966
+ if (SelectAllNode.is(selection.selection)) {
5967
+ const processed = this.processSelectAll(selection.selection);
5968
+ if (Array.isArray(processed)) {
5969
+ result.push(...processed.map((s) => SelectionNode3.create(s)));
5970
+ } else {
5971
+ result.push(SelectionNode3.create(processed));
5972
+ }
5973
+ } else {
5974
+ result.push(SelectionNode3.create(this.processSelection(selection.selection)));
5975
+ }
5976
+ });
5977
+ return result;
5978
+ }
5979
+ processSelection(node) {
5980
+ let alias;
5981
+ if (!AliasNode5.is(node)) {
5982
+ alias = this.extractFieldName(node);
5983
+ }
5984
+ const result = super.transformNode(node);
5985
+ return this.wrapAlias(result, alias);
5986
+ }
5987
+ processSelectAll(node) {
5988
+ const scope = this.modelScopes[this.modelScopes.length - 1];
5989
+ invariant10(scope);
5990
+ if (!this.hasMappedColumns(scope.model)) {
5991
+ return super.transformSelectAll(node);
5992
+ }
5993
+ const modelDef = requireModel(this.schema, scope.model);
5994
+ return this.getModelFields(modelDef).map((fieldDef) => {
5995
+ const columnName = this.mapFieldName(scope.model, fieldDef.name);
5996
+ const columnRef = ReferenceNode3.create(ColumnNode3.create(columnName));
5997
+ return columnName !== fieldDef.name ? this.wrapAlias(columnRef, fieldDef.name) : columnRef;
5998
+ });
5999
+ }
6000
+ extractFieldName(node) {
6001
+ if (ReferenceNode3.is(node) && ColumnNode3.is(node.column)) {
6002
+ return node.column.column.name;
6003
+ } else if (ColumnNode3.is(node)) {
6004
+ return node.column.name;
6005
+ } else {
6006
+ return void 0;
6007
+ }
5834
6008
  }
5835
6009
  };
5836
6010
 
@@ -5843,9 +6017,10 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5843
6017
  driver;
5844
6018
  compiler;
5845
6019
  connectionProvider;
6020
+ suppressMutationHooks;
5846
6021
  nameMapper;
5847
- constructor(client, driver, compiler, adapter, connectionProvider, plugins = []) {
5848
- super(compiler, adapter, connectionProvider, plugins), this.client = client, this.driver = driver, this.compiler = compiler, this.connectionProvider = connectionProvider;
6022
+ constructor(client, driver, compiler, adapter, connectionProvider, plugins = [], suppressMutationHooks = false) {
6023
+ super(compiler, adapter, connectionProvider, plugins), this.client = client, this.driver = driver, this.compiler = compiler, this.connectionProvider = connectionProvider, this.suppressMutationHooks = suppressMutationHooks;
5849
6024
  this.nameMapper = new QueryNameMapper(client.$schema);
5850
6025
  }
5851
6026
  get kysely() {
@@ -5854,38 +6029,13 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5854
6029
  get options() {
5855
6030
  return this.client.$options;
5856
6031
  }
5857
- async executeQuery(compiledQuery, _queryId) {
5858
- let queryNode = compiledQuery.query;
5859
- let mutationInterceptionInfo;
5860
- if (this.isMutationNode(queryNode) && this.hasMutationHooks) {
5861
- mutationInterceptionInfo = await this.callMutationInterceptionFilters(queryNode);
5862
- }
5863
- const task = /* @__PURE__ */ __name(async () => {
5864
- if (this.isMutationNode(queryNode)) {
5865
- await this.callBeforeMutationHooks(queryNode, mutationInterceptionInfo);
5866
- }
5867
- const oldQueryNode = queryNode;
5868
- if ((InsertQueryNode2.is(queryNode) || UpdateQueryNode2.is(queryNode)) && mutationInterceptionInfo?.loadAfterMutationEntities) {
5869
- queryNode = {
5870
- ...queryNode,
5871
- returning: ReturningNode3.create([
5872
- SelectionNode4.createSelectAll()
5873
- ])
5874
- };
5875
- }
5876
- const queryParams = compiledQuery.$raw ? compiledQuery.parameters : void 0;
5877
- const result = await this.proceedQueryWithKyselyInterceptors(queryNode, queryParams);
5878
- if (this.isMutationNode(queryNode)) {
5879
- await this.callAfterMutationHooks(result.result, queryNode, mutationInterceptionInfo, result.connection);
5880
- }
5881
- if (oldQueryNode !== queryNode) {
5882
- }
5883
- return result.result;
5884
- }, "task");
5885
- return task();
6032
+ async executeQuery(compiledQuery, queryId) {
6033
+ const queryParams = compiledQuery.$raw ? compiledQuery.parameters : void 0;
6034
+ const result = await this.proceedQueryWithKyselyInterceptors(compiledQuery.query, queryParams, queryId.queryId);
6035
+ return result.result;
5886
6036
  }
5887
- proceedQueryWithKyselyInterceptors(queryNode, parameters) {
5888
- let proceed = /* @__PURE__ */ __name((q) => this.proceedQuery(q, parameters), "proceed");
6037
+ async proceedQueryWithKyselyInterceptors(queryNode, parameters, queryId) {
6038
+ let proceed = /* @__PURE__ */ __name((q) => this.proceedQuery(q, parameters, queryId), "proceed");
5889
6039
  const hooks = [];
5890
6040
  for (const plugin of this.client.$options.plugins ?? []) {
5891
6041
  if (plugin.onKyselyQuery) {
@@ -5895,10 +6045,8 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5895
6045
  for (const hook of hooks) {
5896
6046
  const _proceed = proceed;
5897
6047
  proceed = /* @__PURE__ */ __name(async (query) => {
5898
- let connection;
5899
6048
  const _p = /* @__PURE__ */ __name(async (q) => {
5900
6049
  const r = await _proceed(q);
5901
- connection = r.connection;
5902
6050
  return r.result;
5903
6051
  }, "_p");
5904
6052
  const hookResult = await hook({
@@ -5909,35 +6057,129 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5909
6057
  proceed: _p
5910
6058
  });
5911
6059
  return {
5912
- result: hookResult,
5913
- connection
6060
+ result: hookResult
5914
6061
  };
5915
6062
  }, "proceed");
5916
6063
  }
5917
- return proceed(queryNode);
6064
+ const result = await proceed(queryNode);
6065
+ return result;
5918
6066
  }
5919
- async proceedQuery(query, parameters) {
5920
- const finalQuery = this.nameMapper.transformNode(query);
5921
- let compiled = this.compileQuery(finalQuery);
5922
- if (parameters) {
5923
- compiled = {
5924
- ...compiled,
5925
- parameters
5926
- };
5927
- }
6067
+ getMutationInfo(queryNode) {
6068
+ const model = this.getMutationModel(queryNode);
6069
+ const { action, where } = match16(queryNode).when(InsertQueryNode2.is, () => ({
6070
+ action: "create",
6071
+ where: void 0
6072
+ })).when(UpdateQueryNode2.is, (node) => ({
6073
+ action: "update",
6074
+ where: node.where
6075
+ })).when(DeleteQueryNode2.is, (node) => ({
6076
+ action: "delete",
6077
+ where: node.where
6078
+ })).exhaustive();
6079
+ return {
6080
+ model,
6081
+ action,
6082
+ where
6083
+ };
6084
+ }
6085
+ async proceedQuery(query, parameters, queryId) {
6086
+ let compiled;
5928
6087
  try {
5929
6088
  return await this.provideConnection(async (connection) => {
5930
- const result = await connection.executeQuery(compiled);
5931
- return {
5932
- result,
5933
- connection
5934
- };
6089
+ if (this.suppressMutationHooks || !this.isMutationNode(query) || !this.hasEntityMutationPlugins) {
6090
+ const finalQuery2 = this.nameMapper.transformNode(query);
6091
+ compiled = this.compileQuery(finalQuery2);
6092
+ if (parameters) {
6093
+ compiled = {
6094
+ ...compiled,
6095
+ parameters
6096
+ };
6097
+ }
6098
+ const result = await connection.executeQuery(compiled);
6099
+ return {
6100
+ result
6101
+ };
6102
+ }
6103
+ if ((InsertQueryNode2.is(query) || UpdateQueryNode2.is(query)) && this.hasEntityMutationPluginsWithAfterMutationHooks) {
6104
+ query = {
6105
+ ...query,
6106
+ returning: ReturningNode2.create([
6107
+ SelectionNode4.createSelectAll()
6108
+ ])
6109
+ };
6110
+ }
6111
+ const finalQuery = this.nameMapper.transformNode(query);
6112
+ compiled = this.compileQuery(finalQuery);
6113
+ if (parameters) {
6114
+ compiled = {
6115
+ ...compiled,
6116
+ parameters
6117
+ };
6118
+ }
6119
+ const currentlyInTx = this.driver.isTransactionConnection(connection);
6120
+ const connectionClient = this.createClientForConnection(connection, currentlyInTx);
6121
+ const mutationInfo = this.getMutationInfo(finalQuery);
6122
+ let beforeMutationEntities;
6123
+ const loadBeforeMutationEntities = /* @__PURE__ */ __name(async () => {
6124
+ if (beforeMutationEntities === void 0 && (UpdateQueryNode2.is(query) || DeleteQueryNode2.is(query))) {
6125
+ beforeMutationEntities = await this.loadEntities(mutationInfo.model, mutationInfo.where, connection);
6126
+ }
6127
+ return beforeMutationEntities;
6128
+ }, "loadBeforeMutationEntities");
6129
+ await this.callBeforeMutationHooks(finalQuery, mutationInfo, loadBeforeMutationEntities, connectionClient, queryId);
6130
+ const shouldCreateTx = this.hasPluginRequestingAfterMutationWithinTransaction && !this.driver.isTransactionConnection(connection);
6131
+ if (!shouldCreateTx) {
6132
+ const result = await connection.executeQuery(compiled);
6133
+ if (!this.driver.isTransactionConnection(connection)) {
6134
+ await this.callAfterMutationHooks(result, finalQuery, mutationInfo, connectionClient, "all", queryId);
6135
+ } else {
6136
+ await this.callAfterMutationHooks(result, finalQuery, mutationInfo, connectionClient, "inTx", queryId);
6137
+ this.driver.registerTransactionCommitCallback(connection, () => this.callAfterMutationHooks(result, finalQuery, mutationInfo, connectionClient, "outTx", queryId));
6138
+ }
6139
+ return {
6140
+ result
6141
+ };
6142
+ } else {
6143
+ await this.driver.beginTransaction(connection, {
6144
+ isolationLevel: TransactionIsolationLevel.ReadCommitted
6145
+ });
6146
+ try {
6147
+ const result = await connection.executeQuery(compiled);
6148
+ await this.callAfterMutationHooks(result, finalQuery, mutationInfo, connectionClient, "inTx", queryId);
6149
+ await this.driver.commitTransaction(connection);
6150
+ await this.callAfterMutationHooks(result, finalQuery, mutationInfo, connectionClient, "outTx", queryId);
6151
+ return {
6152
+ result
6153
+ };
6154
+ } catch (err) {
6155
+ await this.driver.rollbackTransaction(connection);
6156
+ throw err;
6157
+ }
6158
+ }
5935
6159
  });
5936
6160
  } catch (err) {
5937
- const message = `Failed to execute query: ${err}, sql: ${compiled.sql}`;
6161
+ const message = `Failed to execute query: ${err}, sql: ${compiled?.sql}`;
5938
6162
  throw new QueryError(message, err);
5939
6163
  }
5940
6164
  }
6165
+ createClientForConnection(connection, inTx) {
6166
+ const innerExecutor = this.withConnectionProvider(new SingleConnectionProvider(connection));
6167
+ innerExecutor.suppressMutationHooks = true;
6168
+ const innerClient = this.client.withExecutor(innerExecutor);
6169
+ if (inTx) {
6170
+ innerClient.forceTransaction();
6171
+ }
6172
+ return innerClient;
6173
+ }
6174
+ get hasEntityMutationPlugins() {
6175
+ return (this.client.$options.plugins ?? []).some((plugin) => plugin.onEntityMutation);
6176
+ }
6177
+ get hasEntityMutationPluginsWithAfterMutationHooks() {
6178
+ return (this.client.$options.plugins ?? []).some((plugin) => plugin.onEntityMutation?.afterEntityMutation);
6179
+ }
6180
+ get hasPluginRequestingAfterMutationWithinTransaction() {
6181
+ return (this.client.$options.plugins ?? []).some((plugin) => plugin.onEntityMutation?.runAfterMutationWithinTransaction);
6182
+ }
5941
6183
  isMutationNode(queryNode) {
5942
6184
  return InsertQueryNode2.is(queryNode) || UpdateQueryNode2.is(queryNode) || DeleteQueryNode2.is(queryNode);
5943
6185
  }
@@ -5945,154 +6187,102 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
5945
6187
  return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, this.connectionProvider, [
5946
6188
  ...this.plugins,
5947
6189
  plugin
5948
- ]);
6190
+ ], this.suppressMutationHooks);
5949
6191
  }
5950
6192
  withPlugins(plugins) {
5951
6193
  return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, this.connectionProvider, [
5952
6194
  ...this.plugins,
5953
6195
  ...plugins
5954
- ]);
6196
+ ], this.suppressMutationHooks);
5955
6197
  }
5956
6198
  withPluginAtFront(plugin) {
5957
6199
  return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, this.connectionProvider, [
5958
6200
  plugin,
5959
6201
  ...this.plugins
5960
- ]);
6202
+ ], this.suppressMutationHooks);
5961
6203
  }
5962
6204
  withoutPlugins() {
5963
- return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, this.connectionProvider, []);
6205
+ return new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, this.connectionProvider, [], this.suppressMutationHooks);
5964
6206
  }
5965
6207
  withConnectionProvider(connectionProvider) {
5966
- const newExecutor = new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, connectionProvider);
6208
+ const newExecutor = new _ZenStackQueryExecutor(this.client, this.driver, this.compiler, this.adapter, connectionProvider, this.plugins, this.suppressMutationHooks);
5967
6209
  newExecutor.client = this.client.withExecutor(newExecutor);
5968
6210
  return newExecutor;
5969
6211
  }
5970
- get hasMutationHooks() {
5971
- return this.client.$options.plugins?.some((plugin) => !!plugin.onEntityMutation);
5972
- }
5973
6212
  getMutationModel(queryNode) {
5974
- return match16(queryNode).when(InsertQueryNode2.is, (node) => node.into.table.identifier.name).when(UpdateQueryNode2.is, (node) => node.table.table.identifier.name).when(DeleteQueryNode2.is, (node) => {
5975
- if (node.from.froms.length !== 1) {
5976
- throw new InternalError(`Delete query must have exactly one from table`);
5977
- }
5978
- return node.from.froms[0].table.identifier.name;
6213
+ return match16(queryNode).when(InsertQueryNode2.is, (node) => {
6214
+ invariant11(node.into, "InsertQueryNode must have an into clause");
6215
+ return node.into.table.identifier.name;
6216
+ }).when(UpdateQueryNode2.is, (node) => {
6217
+ invariant11(node.table, "UpdateQueryNode must have a table");
6218
+ const { node: tableNode } = stripAlias(node.table);
6219
+ invariant11(TableNode5.is(tableNode), "UpdateQueryNode must use a TableNode");
6220
+ return tableNode.table.identifier.name;
6221
+ }).when(DeleteQueryNode2.is, (node) => {
6222
+ invariant11(node.from.froms.length === 1, "Delete query must have exactly one from table");
6223
+ const { node: tableNode } = stripAlias(node.from.froms[0]);
6224
+ invariant11(TableNode5.is(tableNode), "DeleteQueryNode must use a TableNode");
6225
+ return tableNode.table.identifier.name;
5979
6226
  }).otherwise((node) => {
5980
6227
  throw new InternalError(`Invalid query node: ${node}`);
5981
6228
  });
5982
6229
  }
5983
- async callMutationInterceptionFilters(queryNode) {
5984
- const plugins = this.client.$options.plugins;
5985
- if (plugins) {
5986
- const mutationModel = this.getMutationModel(queryNode);
5987
- const result = {
5988
- intercept: false
5989
- };
5990
- const { action, where } = match16(queryNode).when(InsertQueryNode2.is, () => ({
5991
- action: "create",
5992
- where: void 0
5993
- })).when(UpdateQueryNode2.is, (node) => ({
5994
- action: "update",
5995
- where: node.where
5996
- })).when(DeleteQueryNode2.is, (node) => ({
5997
- action: "delete",
5998
- where: node.where
5999
- })).exhaustive();
6000
- for (const plugin of plugins) {
6001
- const onEntityMutation = plugin.onEntityMutation;
6002
- if (!onEntityMutation) {
6003
- continue;
6004
- }
6005
- if (!onEntityMutation.mutationInterceptionFilter) {
6006
- result.intercept = true;
6007
- } else {
6008
- const filterResult = await onEntityMutation.mutationInterceptionFilter({
6009
- model: mutationModel,
6010
- action,
6011
- queryNode
6012
- });
6013
- result.intercept ||= filterResult.intercept;
6014
- result.loadBeforeMutationEntities ||= filterResult.loadBeforeMutationEntities;
6015
- result.loadAfterMutationEntities ||= filterResult.loadAfterMutationEntities;
6016
- }
6017
- }
6018
- let beforeMutationEntities;
6019
- if (result.loadBeforeMutationEntities && (UpdateQueryNode2.is(queryNode) || DeleteQueryNode2.is(queryNode))) {
6020
- beforeMutationEntities = await this.loadEntities(mutationModel, where);
6021
- }
6022
- return {
6023
- ...result,
6024
- mutationModel,
6025
- action,
6026
- where,
6027
- beforeMutationEntities
6028
- };
6029
- } else {
6030
- return void 0;
6031
- }
6032
- }
6033
- async callBeforeMutationHooks(queryNode, mutationInterceptionInfo) {
6034
- if (!mutationInterceptionInfo?.intercept) {
6035
- return;
6036
- }
6230
+ async callBeforeMutationHooks(queryNode, mutationInfo, loadBeforeMutationEntities, client, queryId) {
6037
6231
  if (this.options.plugins) {
6038
- const mutationModel = this.getMutationModel(queryNode);
6039
6232
  for (const plugin of this.options.plugins) {
6040
6233
  const onEntityMutation = plugin.onEntityMutation;
6041
- if (onEntityMutation?.beforeEntityMutation) {
6042
- await onEntityMutation.beforeEntityMutation({
6043
- model: mutationModel,
6044
- action: mutationInterceptionInfo.action,
6045
- queryNode,
6046
- entities: mutationInterceptionInfo.beforeMutationEntities
6047
- });
6234
+ if (!onEntityMutation?.beforeEntityMutation) {
6235
+ continue;
6048
6236
  }
6237
+ await onEntityMutation.beforeEntityMutation({
6238
+ model: mutationInfo.model,
6239
+ action: mutationInfo.action,
6240
+ queryNode,
6241
+ loadBeforeMutationEntities,
6242
+ client,
6243
+ queryId
6244
+ });
6049
6245
  }
6050
6246
  }
6051
6247
  }
6052
- async callAfterMutationHooks(queryResult, queryNode, mutationInterceptionInfo, connection) {
6053
- if (!mutationInterceptionInfo?.intercept) {
6054
- return;
6055
- }
6248
+ async callAfterMutationHooks(queryResult, queryNode, mutationInfo, client, filterFor, queryId) {
6056
6249
  const hooks = [];
6057
6250
  for (const plugin of this.options.plugins ?? []) {
6058
6251
  const onEntityMutation = plugin.onEntityMutation;
6059
- if (onEntityMutation?.afterEntityMutation) {
6060
- hooks.push(onEntityMutation.afterEntityMutation.bind(plugin));
6252
+ if (!onEntityMutation?.afterEntityMutation) {
6253
+ continue;
6254
+ }
6255
+ if (filterFor === "inTx" && !onEntityMutation.runAfterMutationWithinTransaction) {
6256
+ continue;
6061
6257
  }
6258
+ if (filterFor === "outTx" && onEntityMutation.runAfterMutationWithinTransaction) {
6259
+ continue;
6260
+ }
6261
+ hooks.push(onEntityMutation.afterEntityMutation.bind(plugin));
6062
6262
  }
6063
6263
  if (hooks.length === 0) {
6064
6264
  return;
6065
6265
  }
6066
6266
  const mutationModel = this.getMutationModel(queryNode);
6067
- const inTransaction = this.driver.isTransactionConnection(connection);
6068
- for (const hook of hooks) {
6069
- let afterMutationEntities = void 0;
6070
- if (mutationInterceptionInfo.loadAfterMutationEntities) {
6071
- if (InsertQueryNode2.is(queryNode) || UpdateQueryNode2.is(queryNode)) {
6072
- afterMutationEntities = queryResult.rows;
6073
- }
6074
- }
6075
- const action = /* @__PURE__ */ __name(async () => {
6076
- try {
6077
- await hook({
6078
- model: mutationModel,
6079
- action: mutationInterceptionInfo.action,
6080
- queryNode,
6081
- beforeMutationEntities: mutationInterceptionInfo.beforeMutationEntities,
6082
- afterMutationEntities
6083
- });
6084
- } catch (err) {
6085
- console.error(`Error in afterEntityMutation hook for model "${mutationModel}": ${err}`);
6086
- }
6087
- }, "action");
6088
- if (inTransaction) {
6089
- this.driver.registerTransactionCommitCallback(connection, action);
6267
+ const loadAfterMutationEntities = /* @__PURE__ */ __name(async () => {
6268
+ if (mutationInfo.action === "delete") {
6269
+ return void 0;
6090
6270
  } else {
6091
- await action();
6271
+ return queryResult.rows;
6092
6272
  }
6273
+ }, "loadAfterMutationEntities");
6274
+ for (const hook of hooks) {
6275
+ await hook({
6276
+ model: mutationModel,
6277
+ action: mutationInfo.action,
6278
+ queryNode,
6279
+ loadAfterMutationEntities,
6280
+ client,
6281
+ queryId
6282
+ });
6093
6283
  }
6094
6284
  }
6095
- async loadEntities(model, where) {
6285
+ async loadEntities(model, where, connection) {
6096
6286
  const selectQuery = this.kysely.selectFrom(model).selectAll();
6097
6287
  let selectQueryNode = selectQuery.toOperationNode();
6098
6288
  selectQueryNode = {
@@ -6100,9 +6290,7 @@ var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExe
6100
6290
  where: this.andNodes(selectQueryNode.where, where)
6101
6291
  };
6102
6292
  const compiled = this.compileQuery(selectQueryNode);
6103
- const result = await this.executeQuery(compiled, {
6104
- queryId: `zenstack-${nanoid2()}`
6105
- });
6293
+ const result = await connection.executeQuery(compiled);
6106
6294
  return result.rows;
6107
6295
  }
6108
6296
  andNodes(condition1, condition2) {
@@ -6131,7 +6319,7 @@ __export(functions_exports, {
6131
6319
  search: () => search,
6132
6320
  startsWith: () => startsWith
6133
6321
  });
6134
- import { invariant as invariant9, lowerCaseFirst, upperCaseFirst } from "@zenstackhq/common-helpers";
6322
+ import { invariant as invariant12, lowerCaseFirst, upperCaseFirst } from "@zenstackhq/common-helpers";
6135
6323
  import { sql as sql7, ValueNode as ValueNode4 } from "kysely";
6136
6324
  import { match as match17 } from "ts-pattern";
6137
6325
  var contains = /* @__PURE__ */ __name((eb, args) => {
@@ -6238,7 +6426,7 @@ var currentOperation = /* @__PURE__ */ __name((_eb, args, { operation }) => {
6238
6426
  }, "currentOperation");
6239
6427
  function processCasing(casing, result, model) {
6240
6428
  const opNode = casing.toOperationNode();
6241
- invariant9(ValueNode4.is(opNode) && typeof opNode.value === "string", '"casting" parameter must be a string value');
6429
+ invariant12(ValueNode4.is(opNode) && typeof opNode.value === "string", '"casting" parameter must be a string value');
6242
6430
  result = match17(opNode.value).with("original", () => model).with("upper", () => result.toUpperCase()).with("lower", () => result.toLowerCase()).with("capitalize", () => upperCaseFirst(result)).with("uncapitalize", () => lowerCaseFirst(result)).otherwise(() => {
6243
6431
  throw new Error(`Invalid casing value: ${opNode.value}. Must be "original", "upper", "lower", "capitalize", or "uncapitalize".`);
6244
6432
  });
@@ -6247,7 +6435,7 @@ function processCasing(casing, result, model) {
6247
6435
  __name(processCasing, "processCasing");
6248
6436
 
6249
6437
  // src/client/helpers/schema-db-pusher.ts
6250
- import { invariant as invariant10 } from "@zenstackhq/common-helpers";
6438
+ import { invariant as invariant13 } from "@zenstackhq/common-helpers";
6251
6439
  import { sql as sql8 } from "kysely";
6252
6440
  import toposort from "toposort";
6253
6441
  import { match as match18 } from "ts-pattern";
@@ -6343,7 +6531,7 @@ var SchemaDbPusher = class {
6343
6531
  }
6344
6532
  addUniqueConstraint(table, modelDef) {
6345
6533
  for (const [key, value] of Object.entries(modelDef.uniqueFields)) {
6346
- invariant10(typeof value === "object", "expecting an object");
6534
+ invariant13(typeof value === "object", "expecting an object");
6347
6535
  if ("type" in value) {
6348
6536
  const fieldDef = modelDef.fields[key];
6349
6537
  if (fieldDef.unique) {
@@ -6411,7 +6599,7 @@ var SchemaDbPusher = class {
6411
6599
  return fieldDef.default && ExpressionUtils.isCall(fieldDef.default) && fieldDef.default.function === "autoincrement";
6412
6600
  }
6413
6601
  addForeignKeyConstraint(table, model, fieldName, fieldDef) {
6414
- invariant10(fieldDef.relation, "field must be a relation");
6602
+ invariant13(fieldDef.relation, "field must be a relation");
6415
6603
  if (!fieldDef.relation.fields || !fieldDef.relation.references) {
6416
6604
  return table;
6417
6605
  }
@@ -6466,7 +6654,7 @@ function valueToPromise(thing) {
6466
6654
  __name(valueToPromise, "valueToPromise");
6467
6655
 
6468
6656
  // src/client/result-processor.ts
6469
- import { invariant as invariant11 } from "@zenstackhq/common-helpers";
6657
+ import { invariant as invariant14 } from "@zenstackhq/common-helpers";
6470
6658
  import Decimal2 from "decimal.js";
6471
6659
  import { match as match19 } from "ts-pattern";
6472
6660
  var ResultProcessor = class {
@@ -6566,14 +6754,14 @@ var ResultProcessor = class {
6566
6754
  if (value instanceof Decimal2) {
6567
6755
  return value;
6568
6756
  }
6569
- invariant11(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
6757
+ invariant14(typeof value === "string" || typeof value === "number" || value instanceof Decimal2, `Expected string, number or Decimal, got ${typeof value}`);
6570
6758
  return new Decimal2(value);
6571
6759
  }
6572
6760
  transformBigInt(value) {
6573
6761
  if (typeof value === "bigint") {
6574
6762
  return value;
6575
6763
  }
6576
- invariant11(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
6764
+ invariant14(typeof value === "string" || typeof value === "number", `Expected string or number, got ${typeof value}`);
6577
6765
  return BigInt(value);
6578
6766
  }
6579
6767
  transformBoolean(value) {
@@ -6617,7 +6805,7 @@ var ResultProcessor = class {
6617
6805
  }
6618
6806
  transformJson(value) {
6619
6807
  return match19(this.schema.provider.type).with("sqlite", () => {
6620
- invariant11(typeof value === "string", "Expected string, got " + typeof value);
6808
+ invariant14(typeof value === "string", "Expected string, got " + typeof value);
6621
6809
  return JSON.parse(value);
6622
6810
  }).otherwise(() => value);
6623
6811
  }
@@ -6694,13 +6882,18 @@ var ClientImpl = class _ClientImpl {
6694
6882
  }
6695
6883
  // implementation
6696
6884
  async $transaction(input, options) {
6697
- invariant12(typeof input === "function" || Array.isArray(input) && input.every((p) => p.then && p.cb), "Invalid transaction input, expected a function or an array of ZenStackPromise");
6885
+ invariant15(typeof input === "function" || Array.isArray(input) && input.every((p) => p.then && p.cb), "Invalid transaction input, expected a function or an array of ZenStackPromise");
6698
6886
  if (typeof input === "function") {
6699
6887
  return this.interactiveTransaction(input, options);
6700
6888
  } else {
6701
6889
  return this.sequentialTransaction(input, options);
6702
6890
  }
6703
6891
  }
6892
+ forceTransaction() {
6893
+ if (!this.kysely.isTransaction) {
6894
+ this.kysely = new Transaction(this.kyselyProps);
6895
+ }
6896
+ }
6704
6897
  async interactiveTransaction(callback, options) {
6705
6898
  if (this.kysely.isTransaction) {
6706
6899
  return callback(this);