@zenstackhq/orm 3.4.5 → 3.5.0-beta.1

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.cjs CHANGED
@@ -67,6 +67,14 @@ module.exports = __toCommonJS(src_exports);
67
67
  // src/client/client-impl.ts
68
68
  var import_common_helpers14 = require("@zenstackhq/common-helpers");
69
69
  var import_kysely12 = require("kysely");
70
+ var import_zod3 = __toESM(require("zod"), 1);
71
+
72
+ // src/utils/zod-utils.ts
73
+ var import_v4 = require("zod-validation-error/v4");
74
+ function formatError(error) {
75
+ return (0, import_v4.fromError)(error).toString();
76
+ }
77
+ __name(formatError, "formatError");
70
78
 
71
79
  // src/client/crud/operations/aggregate.ts
72
80
  var import_ts_pattern8 = require("ts-pattern");
@@ -1217,16 +1225,22 @@ var BaseCrudDialect = class {
1217
1225
  }
1218
1226
  }
1219
1227
  buildJsonEqualityFilter(lhs, rhs) {
1220
- return this.buildLiteralFilter(lhs, "Json", rhs);
1228
+ return this.buildValueFilter(lhs, "Json", rhs);
1221
1229
  }
1222
- buildLiteralFilter(lhs, type, rhs) {
1223
- return this.eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformInput(rhs, type, false) : rhs);
1230
+ buildValueFilter(lhs, type, rhs) {
1231
+ if (rhs === void 0) {
1232
+ return this.true();
1233
+ }
1234
+ if (rhs === null) {
1235
+ return this.eb(lhs, "is", null);
1236
+ }
1237
+ return this.eb(lhs, "=", this.transformInput(rhs, type, false));
1224
1238
  }
1225
1239
  buildStandardFilter(type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
1226
1240
  if (payload === null || !(0, import_common_helpers2.isPlainObject)(payload)) {
1227
1241
  return {
1228
1242
  conditions: [
1229
- this.buildLiteralFilter(lhs, type, payload)
1243
+ this.buildValueFilter(lhs, type, payload)
1230
1244
  ],
1231
1245
  consumedKeys: []
1232
1246
  };
@@ -1980,7 +1994,10 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
1980
1994
  overrideTypeParsers() {
1981
1995
  if (this.options.fixPostgresTimezone !== false && !_PostgresCrudDialect.typeParserOverrideApplied) {
1982
1996
  _PostgresCrudDialect.typeParserOverrideApplied = true;
1983
- import("pg").then((pg) => {
1997
+ import(
1998
+ /* webpackIgnore: true */
1999
+ "pg"
2000
+ ).then((pg) => {
1984
2001
  pg.types.setTypeParser(pg.types.builtins.TIMESTAMP, (value) => {
1985
2002
  if (typeof value !== "string") {
1986
2003
  return value;
@@ -3183,7 +3200,7 @@ var BaseOperationHandler = class {
3183
3200
  const length = firstArgVal;
3184
3201
  const generated = typeof length === "number" ? (0, import_nanoid.nanoid)(length) : (0, import_nanoid.nanoid)();
3185
3202
  return this.formatGeneratedValue(generated, defaultValue.args?.[1]);
3186
- }).with("ulid", () => this.formatGeneratedValue((0, import_ulid.ulid)(), defaultValue.args?.[0])).otherwise(() => void 0);
3203
+ }).with("ulid", () => this.formatGeneratedValue((0, import_ulid.ulid)(), defaultValue.args?.[0])).with("now", () => /* @__PURE__ */ new Date()).otherwise(() => void 0);
3187
3204
  } else if (schema_exports.ExpressionUtils.isMember(defaultValue) && schema_exports.ExpressionUtils.isCall(defaultValue.receiver) && defaultValue.receiver.function === "auth") {
3188
3205
  let val = this.client.$auth;
3189
3206
  for (const member of defaultValue.members) {
@@ -4664,13 +4681,6 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
4664
4681
  var import_common_helpers9 = require("@zenstackhq/common-helpers");
4665
4682
  var import_ts_pattern14 = require("ts-pattern");
4666
4683
 
4667
- // src/utils/zod-utils.ts
4668
- var import_v4 = require("zod-validation-error/v4");
4669
- function formatError(error) {
4670
- return (0, import_v4.fromError)(error).toString();
4671
- }
4672
- __name(formatError, "formatError");
4673
-
4674
4684
  // src/client/zod/factory.ts
4675
4685
  var import_common_helpers8 = require("@zenstackhq/common-helpers");
4676
4686
  var import_zod = require("@zenstackhq/zod");
@@ -4722,6 +4732,15 @@ function _ts_metadata(k, v) {
4722
4732
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4723
4733
  }
4724
4734
  __name(_ts_metadata, "_ts_metadata");
4735
+ function toFieldInfo(def) {
4736
+ return {
4737
+ name: def.name,
4738
+ type: def.type,
4739
+ optional: def.optional,
4740
+ array: def.array
4741
+ };
4742
+ }
4743
+ __name(toFieldInfo, "toFieldInfo");
4725
4744
  function createQuerySchemaFactory(clientOrSchema, options) {
4726
4745
  return new ZodSchemaFactory(clientOrSchema, options);
4727
4746
  }
@@ -4736,6 +4755,7 @@ var ZodSchemaFactory = class {
4736
4755
  ];
4737
4756
  schema;
4738
4757
  options;
4758
+ extraValidationsEnabled = true;
4739
4759
  constructor(clientOrSchema, options) {
4740
4760
  if ("$schema" in clientOrSchema) {
4741
4761
  this.schema = clientOrSchema.$schema;
@@ -4748,9 +4768,6 @@ var ZodSchemaFactory = class {
4748
4768
  get plugins() {
4749
4769
  return this.options.plugins ?? [];
4750
4770
  }
4751
- get extraValidationsEnabled() {
4752
- return this.options.validateInput !== false;
4753
- }
4754
4771
  shouldIncludeRelations(options) {
4755
4772
  return options?.relationDepth === void 0 || options.relationDepth > 0;
4756
4773
  }
@@ -4771,14 +4788,13 @@ var ZodSchemaFactory = class {
4771
4788
  setCache(cacheKey, schema) {
4772
4789
  return this.schemaCache.set(cacheKey, schema);
4773
4790
  }
4774
- // @ts-ignore
4775
- printCacheStats(detailed = false) {
4776
- console.log("Schema cache size:", this.schemaCache.size);
4777
- if (detailed) {
4778
- for (const key of this.schemaCache.keys()) {
4779
- console.log(` ${key}`);
4780
- }
4781
- }
4791
+ get cacheStats() {
4792
+ return {
4793
+ size: this.schemaCache.size,
4794
+ keys: [
4795
+ ...this.schemaCache.keys()
4796
+ ]
4797
+ };
4782
4798
  }
4783
4799
  // #endregion
4784
4800
  // #region Find
@@ -4926,14 +4942,14 @@ var ZodSchemaFactory = class {
4926
4942
  const enumDef = getEnum(this.schema, fieldDef.type);
4927
4943
  if (enumDef) {
4928
4944
  if (Object.keys(enumDef.values).length > 0) {
4929
- fieldSchema = this.makeEnumFilterSchema(model, fieldDef, withAggregations, ignoreSlicing);
4945
+ fieldSchema = this.makeEnumFilterSchema(model, toFieldInfo(fieldDef), withAggregations, ignoreSlicing);
4930
4946
  }
4931
4947
  } else if (fieldDef.array) {
4932
- fieldSchema = this.makeArrayFilterSchema(model, fieldDef);
4948
+ fieldSchema = this.makeArrayFilterSchema(model, toFieldInfo(fieldDef));
4933
4949
  } else if (this.isTypeDefType(fieldDef.type)) {
4934
- fieldSchema = this.makeTypedJsonFilterSchema(model, fieldDef);
4950
+ fieldSchema = this.makeTypedJsonFilterSchema(model, toFieldInfo(fieldDef));
4935
4951
  } else {
4936
- fieldSchema = this.makePrimitiveFilterSchema(model, fieldDef, withAggregations, ignoreSlicing);
4952
+ fieldSchema = this.makePrimitiveFilterSchema(model, toFieldInfo(fieldDef), withAggregations, ignoreSlicing);
4937
4953
  }
4938
4954
  }
4939
4955
  if (fieldSchema) {
@@ -4950,12 +4966,12 @@ var ZodSchemaFactory = class {
4950
4966
  const enumDef = getEnum(this.schema, def.type);
4951
4967
  if (enumDef) {
4952
4968
  if (Object.keys(enumDef.values).length > 0) {
4953
- fieldSchema = this.makeEnumFilterSchema(model, def, false, true);
4969
+ fieldSchema = this.makeEnumFilterSchema(model, toFieldInfo(def), false, true);
4954
4970
  } else {
4955
4971
  fieldSchema = import_zod2.z.never();
4956
4972
  }
4957
4973
  } else {
4958
- fieldSchema = this.makePrimitiveFilterSchema(model, def, false, true);
4974
+ fieldSchema = this.makePrimitiveFilterSchema(model, toFieldInfo(def), false, true);
4959
4975
  }
4960
4976
  return [
4961
4977
  key,
@@ -5002,15 +5018,15 @@ var ZodSchemaFactory = class {
5002
5018
  const fieldSchemas = {};
5003
5019
  for (const [fieldName, fieldDef] of Object.entries(typeDef.fields)) {
5004
5020
  if (this.isTypeDefType(fieldDef.type)) {
5005
- fieldSchemas[fieldName] = this.makeTypedJsonFilterSchema(contextModel, fieldDef).optional();
5021
+ fieldSchemas[fieldName] = this.makeTypedJsonFilterSchema(contextModel, toFieldInfo(fieldDef)).optional();
5006
5022
  } else {
5007
5023
  const enumDef = getEnum(this.schema, fieldDef.type);
5008
5024
  if (enumDef) {
5009
- fieldSchemas[fieldName] = this.makeEnumFilterSchema(contextModel, fieldDef, false).optional();
5025
+ fieldSchemas[fieldName] = this.makeEnumFilterSchema(contextModel, toFieldInfo(fieldDef), false).optional();
5010
5026
  } else if (fieldDef.array) {
5011
- fieldSchemas[fieldName] = this.makeArrayFilterSchema(contextModel, fieldDef).optional();
5027
+ fieldSchemas[fieldName] = this.makeArrayFilterSchema(contextModel, toFieldInfo(fieldDef)).optional();
5012
5028
  } else {
5013
- fieldSchemas[fieldName] = this.makePrimitiveFilterSchema(contextModel, fieldDef, false).optional();
5029
+ fieldSchemas[fieldName] = this.makePrimitiveFilterSchema(contextModel, toFieldInfo(fieldDef), false).optional();
5014
5030
  }
5015
5031
  }
5016
5032
  }
@@ -6594,9 +6610,11 @@ var InputValidator = class {
6594
6610
  }
6595
6611
  client;
6596
6612
  zodFactory;
6597
- constructor(client) {
6613
+ enabled;
6614
+ constructor(client, options) {
6598
6615
  this.client = client;
6599
6616
  this.zodFactory = new ZodSchemaFactory(client);
6617
+ this.enabled = options?.enabled !== false;
6600
6618
  }
6601
6619
  // #region Entry points
6602
6620
  validateFindArgs(model, args, operation) {
@@ -6643,6 +6661,9 @@ var InputValidator = class {
6643
6661
  }
6644
6662
  // TODO: turn it into a Zod schema and cache
6645
6663
  validateProcedureInput(proc, input) {
6664
+ if (!this.enabled) {
6665
+ return input;
6666
+ }
6646
6667
  const procDef = (this.client.$schema.procedures ?? {})[proc];
6647
6668
  (0, import_common_helpers9.invariant)(procDef, `Procedure "${proc}" not found in schema`);
6648
6669
  const params = Object.values(procDef.params ?? {});
@@ -6707,6 +6728,9 @@ var InputValidator = class {
6707
6728
  // #endregion
6708
6729
  // #region Validation helpers
6709
6730
  validate(model, operation, getSchema, args) {
6731
+ if (!this.enabled) {
6732
+ return args;
6733
+ }
6710
6734
  const schema = getSchema(model);
6711
6735
  const { error, data } = schema.safeParse(args);
6712
6736
  if (error) {
@@ -7541,6 +7565,7 @@ var TempAliasTransformer = class extends import_kysely8.OperationNodeTransformer
7541
7565
  };
7542
7566
 
7543
7567
  // src/client/executor/zenstack-query-executor.ts
7568
+ var DEFAULT_MAX_SLOW_RECORDS = 100;
7544
7569
  var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends import_kysely9.DefaultQueryExecutor {
7545
7570
  static {
7546
7571
  __name(this, "ZenStackQueryExecutor");
@@ -7957,13 +7982,49 @@ In such cases, ZenStack cannot reliably determine the IDs of the mutated entitie
7957
7982
  parameters
7958
7983
  };
7959
7984
  }
7985
+ const trackSlowQuery = this.options.diagnostics !== void 0;
7986
+ const startTimestamp = trackSlowQuery ? performance.now() : void 0;
7987
+ const startedAt = trackSlowQuery ? /* @__PURE__ */ new Date() : void 0;
7960
7988
  try {
7961
7989
  const result = await connection.executeQuery(compiledQuery);
7990
+ if (startTimestamp !== void 0) {
7991
+ this.trackSlowQuery(compiledQuery, startTimestamp, startedAt);
7992
+ }
7962
7993
  return this.ensureProperQueryResult(compiledQuery.query, result);
7963
7994
  } catch (err) {
7964
7995
  throw createDBQueryError(`Failed to execute query: ${err}`, err, compiledQuery.sql, compiledQuery.parameters);
7965
7996
  }
7966
7997
  }
7998
+ trackSlowQuery(compiledQuery, startTimestamp, startedAt) {
7999
+ const durationMs = performance.now() - startTimestamp;
8000
+ const thresholdMs = this.options.diagnostics?.slowQueryThresholdMs;
8001
+ if (thresholdMs === void 0 || durationMs < thresholdMs) {
8002
+ return;
8003
+ }
8004
+ const slowQueries = this.client.slowQueries;
8005
+ const maxRecords = this.options.diagnostics?.slowQueryMaxRecords ?? DEFAULT_MAX_SLOW_RECORDS;
8006
+ if (maxRecords <= 0) {
8007
+ return;
8008
+ }
8009
+ const queryInfo = {
8010
+ startedAt,
8011
+ durationMs,
8012
+ sql: compiledQuery.sql
8013
+ };
8014
+ if (slowQueries.length >= maxRecords) {
8015
+ let minIndex = 0;
8016
+ for (let i = 1; i < slowQueries.length; i++) {
8017
+ if (slowQueries[i].durationMs < slowQueries[minIndex].durationMs) {
8018
+ minIndex = i;
8019
+ }
8020
+ }
8021
+ if (durationMs > slowQueries[minIndex].durationMs) {
8022
+ slowQueries[minIndex] = queryInfo;
8023
+ }
8024
+ } else {
8025
+ slowQueries.push(queryInfo);
8026
+ }
8027
+ }
7967
8028
  ensureProperQueryResult(query, result) {
7968
8029
  let finalResult = result;
7969
8030
  if (this.isMutationNode(query)) {
@@ -8110,7 +8171,7 @@ var isEmpty = /* @__PURE__ */ __name((eb, args, { dialect }) => {
8110
8171
  }
8111
8172
  return eb(dialect.buildArrayLength(field), "=", import_kysely10.sql.lit(0));
8112
8173
  }, "isEmpty");
8113
- var now = /* @__PURE__ */ __name(() => import_kysely10.sql.raw("CURRENT_TIMESTAMP"), "now");
8174
+ var now = /* @__PURE__ */ __name((_eb, _args, context) => (0, import_ts_pattern16.match)(context.dialect.provider).with("sqlite", () => import_kysely10.sql.raw("strftime('%Y-%m-%dT%H:%M:%fZ')")).with("mysql", () => import_kysely10.sql.raw("CONCAT(SUBSTRING(DATE_FORMAT(UTC_TIMESTAMP(3), '%Y-%m-%dT%H:%i:%s.%f'), 1, 23), '+00:00')")).with("postgresql", () => import_kysely10.sql.raw("CURRENT_TIMESTAMP")).exhaustive(), "now");
8114
8175
  var currentModel = /* @__PURE__ */ __name((_eb, args, { model }) => {
8115
8176
  let result = model;
8116
8177
  const [casing] = args;
@@ -8635,6 +8696,7 @@ var ClientImpl = class _ClientImpl {
8635
8696
  kyselyProps;
8636
8697
  auth;
8637
8698
  inputValidator;
8699
+ slowQueries = [];
8638
8700
  constructor(schema, options, baseClient, executor) {
8639
8701
  this.schema = schema;
8640
8702
  this.options = options;
@@ -8644,9 +8706,7 @@ var ClientImpl = class _ClientImpl {
8644
8706
  ...functions_exports,
8645
8707
  ...this.$options.functions
8646
8708
  };
8647
- if (!baseClient && !options.skipValidationForComputedFields) {
8648
- this.validateComputedFieldsConfig();
8649
- }
8709
+ this.validateOptions(baseClient, options);
8650
8710
  if (baseClient) {
8651
8711
  this.kyselyProps = {
8652
8712
  ...baseClient.kyselyProps,
@@ -8654,6 +8714,7 @@ var ClientImpl = class _ClientImpl {
8654
8714
  };
8655
8715
  this.kyselyRaw = baseClient.kyselyRaw;
8656
8716
  this.auth = baseClient.auth;
8717
+ this.slowQueries = baseClient.slowQueries;
8657
8718
  } else {
8658
8719
  const driver = new ZenStackDriver(options.dialect.createDriver(), new import_kysely12.Log(this.$options.log ?? []));
8659
8720
  const compiler = options.dialect.createQueryCompiler();
@@ -8674,32 +8735,31 @@ var ClientImpl = class _ClientImpl {
8674
8735
  });
8675
8736
  }
8676
8737
  this.kysely = new import_kysely12.Kysely(this.kyselyProps);
8677
- this.inputValidator = baseClient?.inputValidator ?? new InputValidator(this);
8738
+ this.inputValidator = baseClient?.inputValidator ?? new InputValidator(this, {
8739
+ enabled: this.$options.validateInput !== false
8740
+ });
8678
8741
  return createClientProxy(this);
8679
8742
  }
8680
- get $qb() {
8681
- return this.kysely;
8682
- }
8683
- get $qbRaw() {
8684
- return this.kyselyRaw;
8685
- }
8686
- get $zod() {
8687
- return this.inputValidator.zodFactory;
8688
- }
8689
- get isTransaction() {
8690
- return this.kysely.isTransaction;
8691
- }
8692
- /**
8693
- * Create a new client with a new query executor.
8694
- */
8695
- withExecutor(executor) {
8696
- return new _ClientImpl(this.schema, this.$options, this, executor);
8743
+ validateOptions(baseClient, options) {
8744
+ if (!baseClient && !options.skipValidationForComputedFields) {
8745
+ this.validateComputedFieldsConfig(options);
8746
+ }
8747
+ if (options.diagnostics) {
8748
+ const diagnosticsSchema = import_zod3.default.object({
8749
+ slowQueryThresholdMs: import_zod3.default.number().nonnegative().optional(),
8750
+ slowQueryMaxRecords: import_zod3.default.int().nonnegative().or(import_zod3.default.literal(Infinity)).optional()
8751
+ });
8752
+ const parseResult = diagnosticsSchema.safeParse(options.diagnostics);
8753
+ if (!parseResult.success) {
8754
+ throw createConfigError(`Invalid diagnostics configuration: ${formatError(parseResult.error)}`);
8755
+ }
8756
+ }
8697
8757
  }
8698
8758
  /**
8699
8759
  * Validates that all computed fields in the schema have corresponding configurations.
8700
8760
  */
8701
- validateComputedFieldsConfig() {
8702
- const computedFieldsConfig = "computedFields" in this.$options ? this.$options.computedFields : void 0;
8761
+ validateComputedFieldsConfig(options) {
8762
+ const computedFieldsConfig = "computedFields" in options ? options.computedFields : void 0;
8703
8763
  for (const [modelName, modelDef] of Object.entries(this.$schema.models)) {
8704
8764
  if (modelDef.computedFields) {
8705
8765
  for (const fieldName of Object.keys(modelDef.computedFields)) {
@@ -8715,6 +8775,24 @@ var ClientImpl = class _ClientImpl {
8715
8775
  }
8716
8776
  }
8717
8777
  }
8778
+ get $qb() {
8779
+ return this.kysely;
8780
+ }
8781
+ get $qbRaw() {
8782
+ return this.kyselyRaw;
8783
+ }
8784
+ get $zod() {
8785
+ return this.inputValidator.zodFactory;
8786
+ }
8787
+ get isTransaction() {
8788
+ return this.kysely.isTransaction;
8789
+ }
8790
+ /**
8791
+ * Create a new client with a new query executor.
8792
+ */
8793
+ withExecutor(executor) {
8794
+ return new _ClientImpl(this.schema, this.$options, this, executor);
8795
+ }
8718
8796
  // implementation
8719
8797
  async $transaction(input, options) {
8720
8798
  (0, import_common_helpers14.invariant)(typeof input === "function" || Array.isArray(input) && input.every((p) => p.then && p.cb), "Invalid transaction input, expected a function or an array of ZenStackPromise");
@@ -8836,7 +8914,9 @@ var ClientImpl = class _ClientImpl {
8836
8914
  plugins: newPlugins
8837
8915
  };
8838
8916
  const newClient = new _ClientImpl(this.schema, newOptions, this);
8839
- newClient.inputValidator = new InputValidator(newClient);
8917
+ newClient.inputValidator = new InputValidator(newClient, {
8918
+ enabled: newOptions.validateInput !== false
8919
+ });
8840
8920
  return newClient;
8841
8921
  }
8842
8922
  $unuse(pluginId) {
@@ -8851,7 +8931,9 @@ var ClientImpl = class _ClientImpl {
8851
8931
  plugins: newPlugins
8852
8932
  };
8853
8933
  const newClient = new _ClientImpl(this.schema, newOptions, this);
8854
- newClient.inputValidator = new InputValidator(newClient);
8934
+ newClient.inputValidator = new InputValidator(newClient, {
8935
+ enabled: newClient.$options.validateInput !== false
8936
+ });
8855
8937
  return newClient;
8856
8938
  }
8857
8939
  $unuseAll() {
@@ -8860,7 +8942,9 @@ var ClientImpl = class _ClientImpl {
8860
8942
  plugins: []
8861
8943
  };
8862
8944
  const newClient = new _ClientImpl(this.schema, newOptions, this);
8863
- newClient.inputValidator = new InputValidator(newClient);
8945
+ newClient.inputValidator = new InputValidator(newClient, {
8946
+ enabled: newOptions.validateInput !== false
8947
+ });
8864
8948
  return newClient;
8865
8949
  }
8866
8950
  $setAuth(auth) {
@@ -8876,7 +8960,9 @@ var ClientImpl = class _ClientImpl {
8876
8960
  }
8877
8961
  $setOptions(options) {
8878
8962
  const newClient = new _ClientImpl(this.schema, options, this);
8879
- newClient.inputValidator = new InputValidator(newClient);
8963
+ newClient.inputValidator = new InputValidator(newClient, {
8964
+ enabled: newClient.$options.validateInput !== false
8965
+ });
8880
8966
  return newClient;
8881
8967
  }
8882
8968
  $setInputValidation(enable) {
@@ -8886,6 +8972,14 @@ var ClientImpl = class _ClientImpl {
8886
8972
  };
8887
8973
  return this.$setOptions(newOptions);
8888
8974
  }
8975
+ async $diagnostics() {
8976
+ return {
8977
+ zodCache: this.inputValidator.zodFactory.cacheStats,
8978
+ slowQueries: this.slowQueries.map((q) => ({
8979
+ ...q
8980
+ }))
8981
+ };
8982
+ }
8889
8983
  $executeRaw(query, ...values) {
8890
8984
  return createZenStackPromise(async () => {
8891
8985
  const result = await (0, import_kysely12.sql)(query, ...values).execute(this.kysely);