@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.js CHANGED
@@ -20,6 +20,14 @@ var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "defau
20
20
  // src/client/client-impl.ts
21
21
  import { invariant as invariant14, lowerCaseFirst as lowerCaseFirst3 } from "@zenstackhq/common-helpers";
22
22
  import { CompiledQuery, DefaultConnectionProvider, DefaultQueryExecutor as DefaultQueryExecutor2, Kysely, Log, sql as sql8, Transaction } from "kysely";
23
+ import z2 from "zod";
24
+
25
+ // src/utils/zod-utils.ts
26
+ import { fromError } from "zod-validation-error/v4";
27
+ function formatError(error) {
28
+ return fromError(error).toString();
29
+ }
30
+ __name(formatError, "formatError");
23
31
 
24
32
  // src/client/crud/operations/aggregate.ts
25
33
  import { match as match8 } from "ts-pattern";
@@ -1171,16 +1179,22 @@ var BaseCrudDialect = class {
1171
1179
  }
1172
1180
  }
1173
1181
  buildJsonEqualityFilter(lhs, rhs) {
1174
- return this.buildLiteralFilter(lhs, "Json", rhs);
1182
+ return this.buildValueFilter(lhs, "Json", rhs);
1175
1183
  }
1176
- buildLiteralFilter(lhs, type, rhs) {
1177
- return this.eb(lhs, "=", rhs !== null && rhs !== void 0 ? this.transformInput(rhs, type, false) : rhs);
1184
+ buildValueFilter(lhs, type, rhs) {
1185
+ if (rhs === void 0) {
1186
+ return this.true();
1187
+ }
1188
+ if (rhs === null) {
1189
+ return this.eb(lhs, "is", null);
1190
+ }
1191
+ return this.eb(lhs, "=", this.transformInput(rhs, type, false));
1178
1192
  }
1179
1193
  buildStandardFilter(type, payload, lhs, getRhs, recurse, throwIfInvalid = false, onlyForKeys = void 0, excludeKeys = []) {
1180
1194
  if (payload === null || !isPlainObject(payload)) {
1181
1195
  return {
1182
1196
  conditions: [
1183
- this.buildLiteralFilter(lhs, type, payload)
1197
+ this.buildValueFilter(lhs, type, payload)
1184
1198
  ],
1185
1199
  consumedKeys: []
1186
1200
  };
@@ -1934,7 +1948,10 @@ var PostgresCrudDialect = class _PostgresCrudDialect extends LateralJoinDialectB
1934
1948
  overrideTypeParsers() {
1935
1949
  if (this.options.fixPostgresTimezone !== false && !_PostgresCrudDialect.typeParserOverrideApplied) {
1936
1950
  _PostgresCrudDialect.typeParserOverrideApplied = true;
1937
- import("pg").then((pg) => {
1951
+ import(
1952
+ /* webpackIgnore: true */
1953
+ "pg"
1954
+ ).then((pg) => {
1938
1955
  pg.types.setTypeParser(pg.types.builtins.TIMESTAMP, (value) => {
1939
1956
  if (typeof value !== "string") {
1940
1957
  return value;
@@ -3137,7 +3154,7 @@ var BaseOperationHandler = class {
3137
3154
  const length = firstArgVal;
3138
3155
  const generated = typeof length === "number" ? nanoid(length) : nanoid();
3139
3156
  return this.formatGeneratedValue(generated, defaultValue.args?.[1]);
3140
- }).with("ulid", () => this.formatGeneratedValue(ulid(), defaultValue.args?.[0])).otherwise(() => void 0);
3157
+ }).with("ulid", () => this.formatGeneratedValue(ulid(), defaultValue.args?.[0])).with("now", () => /* @__PURE__ */ new Date()).otherwise(() => void 0);
3141
3158
  } else if (schema_exports.ExpressionUtils.isMember(defaultValue) && schema_exports.ExpressionUtils.isCall(defaultValue.receiver) && defaultValue.receiver.function === "auth") {
3142
3159
  let val = this.client.$auth;
3143
3160
  for (const member of defaultValue.members) {
@@ -4618,13 +4635,6 @@ var UpdateOperationHandler = class extends BaseOperationHandler {
4618
4635
  import { invariant as invariant9 } from "@zenstackhq/common-helpers";
4619
4636
  import { match as match14 } from "ts-pattern";
4620
4637
 
4621
- // src/utils/zod-utils.ts
4622
- import { fromError } from "zod-validation-error/v4";
4623
- function formatError(error) {
4624
- return fromError(error).toString();
4625
- }
4626
- __name(formatError, "formatError");
4627
-
4628
4638
  // src/client/zod/factory.ts
4629
4639
  import { enumerate as enumerate3, invariant as invariant8, lowerCaseFirst } from "@zenstackhq/common-helpers";
4630
4640
  import { ZodUtils } from "@zenstackhq/zod";
@@ -4676,6 +4686,15 @@ function _ts_metadata(k, v) {
4676
4686
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
4677
4687
  }
4678
4688
  __name(_ts_metadata, "_ts_metadata");
4689
+ function toFieldInfo(def) {
4690
+ return {
4691
+ name: def.name,
4692
+ type: def.type,
4693
+ optional: def.optional,
4694
+ array: def.array
4695
+ };
4696
+ }
4697
+ __name(toFieldInfo, "toFieldInfo");
4679
4698
  function createQuerySchemaFactory(clientOrSchema, options) {
4680
4699
  return new ZodSchemaFactory(clientOrSchema, options);
4681
4700
  }
@@ -4690,6 +4709,7 @@ var ZodSchemaFactory = class {
4690
4709
  ];
4691
4710
  schema;
4692
4711
  options;
4712
+ extraValidationsEnabled = true;
4693
4713
  constructor(clientOrSchema, options) {
4694
4714
  if ("$schema" in clientOrSchema) {
4695
4715
  this.schema = clientOrSchema.$schema;
@@ -4702,9 +4722,6 @@ var ZodSchemaFactory = class {
4702
4722
  get plugins() {
4703
4723
  return this.options.plugins ?? [];
4704
4724
  }
4705
- get extraValidationsEnabled() {
4706
- return this.options.validateInput !== false;
4707
- }
4708
4725
  shouldIncludeRelations(options) {
4709
4726
  return options?.relationDepth === void 0 || options.relationDepth > 0;
4710
4727
  }
@@ -4725,14 +4742,13 @@ var ZodSchemaFactory = class {
4725
4742
  setCache(cacheKey, schema) {
4726
4743
  return this.schemaCache.set(cacheKey, schema);
4727
4744
  }
4728
- // @ts-ignore
4729
- printCacheStats(detailed = false) {
4730
- console.log("Schema cache size:", this.schemaCache.size);
4731
- if (detailed) {
4732
- for (const key of this.schemaCache.keys()) {
4733
- console.log(` ${key}`);
4734
- }
4735
- }
4745
+ get cacheStats() {
4746
+ return {
4747
+ size: this.schemaCache.size,
4748
+ keys: [
4749
+ ...this.schemaCache.keys()
4750
+ ]
4751
+ };
4736
4752
  }
4737
4753
  // #endregion
4738
4754
  // #region Find
@@ -4880,14 +4896,14 @@ var ZodSchemaFactory = class {
4880
4896
  const enumDef = getEnum(this.schema, fieldDef.type);
4881
4897
  if (enumDef) {
4882
4898
  if (Object.keys(enumDef.values).length > 0) {
4883
- fieldSchema = this.makeEnumFilterSchema(model, fieldDef, withAggregations, ignoreSlicing);
4899
+ fieldSchema = this.makeEnumFilterSchema(model, toFieldInfo(fieldDef), withAggregations, ignoreSlicing);
4884
4900
  }
4885
4901
  } else if (fieldDef.array) {
4886
- fieldSchema = this.makeArrayFilterSchema(model, fieldDef);
4902
+ fieldSchema = this.makeArrayFilterSchema(model, toFieldInfo(fieldDef));
4887
4903
  } else if (this.isTypeDefType(fieldDef.type)) {
4888
- fieldSchema = this.makeTypedJsonFilterSchema(model, fieldDef);
4904
+ fieldSchema = this.makeTypedJsonFilterSchema(model, toFieldInfo(fieldDef));
4889
4905
  } else {
4890
- fieldSchema = this.makePrimitiveFilterSchema(model, fieldDef, withAggregations, ignoreSlicing);
4906
+ fieldSchema = this.makePrimitiveFilterSchema(model, toFieldInfo(fieldDef), withAggregations, ignoreSlicing);
4891
4907
  }
4892
4908
  }
4893
4909
  if (fieldSchema) {
@@ -4904,12 +4920,12 @@ var ZodSchemaFactory = class {
4904
4920
  const enumDef = getEnum(this.schema, def.type);
4905
4921
  if (enumDef) {
4906
4922
  if (Object.keys(enumDef.values).length > 0) {
4907
- fieldSchema = this.makeEnumFilterSchema(model, def, false, true);
4923
+ fieldSchema = this.makeEnumFilterSchema(model, toFieldInfo(def), false, true);
4908
4924
  } else {
4909
4925
  fieldSchema = z.never();
4910
4926
  }
4911
4927
  } else {
4912
- fieldSchema = this.makePrimitiveFilterSchema(model, def, false, true);
4928
+ fieldSchema = this.makePrimitiveFilterSchema(model, toFieldInfo(def), false, true);
4913
4929
  }
4914
4930
  return [
4915
4931
  key,
@@ -4956,15 +4972,15 @@ var ZodSchemaFactory = class {
4956
4972
  const fieldSchemas = {};
4957
4973
  for (const [fieldName, fieldDef] of Object.entries(typeDef.fields)) {
4958
4974
  if (this.isTypeDefType(fieldDef.type)) {
4959
- fieldSchemas[fieldName] = this.makeTypedJsonFilterSchema(contextModel, fieldDef).optional();
4975
+ fieldSchemas[fieldName] = this.makeTypedJsonFilterSchema(contextModel, toFieldInfo(fieldDef)).optional();
4960
4976
  } else {
4961
4977
  const enumDef = getEnum(this.schema, fieldDef.type);
4962
4978
  if (enumDef) {
4963
- fieldSchemas[fieldName] = this.makeEnumFilterSchema(contextModel, fieldDef, false).optional();
4979
+ fieldSchemas[fieldName] = this.makeEnumFilterSchema(contextModel, toFieldInfo(fieldDef), false).optional();
4964
4980
  } else if (fieldDef.array) {
4965
- fieldSchemas[fieldName] = this.makeArrayFilterSchema(contextModel, fieldDef).optional();
4981
+ fieldSchemas[fieldName] = this.makeArrayFilterSchema(contextModel, toFieldInfo(fieldDef)).optional();
4966
4982
  } else {
4967
- fieldSchemas[fieldName] = this.makePrimitiveFilterSchema(contextModel, fieldDef, false).optional();
4983
+ fieldSchemas[fieldName] = this.makePrimitiveFilterSchema(contextModel, toFieldInfo(fieldDef), false).optional();
4968
4984
  }
4969
4985
  }
4970
4986
  }
@@ -6548,9 +6564,11 @@ var InputValidator = class {
6548
6564
  }
6549
6565
  client;
6550
6566
  zodFactory;
6551
- constructor(client) {
6567
+ enabled;
6568
+ constructor(client, options) {
6552
6569
  this.client = client;
6553
6570
  this.zodFactory = new ZodSchemaFactory(client);
6571
+ this.enabled = options?.enabled !== false;
6554
6572
  }
6555
6573
  // #region Entry points
6556
6574
  validateFindArgs(model, args, operation) {
@@ -6597,6 +6615,9 @@ var InputValidator = class {
6597
6615
  }
6598
6616
  // TODO: turn it into a Zod schema and cache
6599
6617
  validateProcedureInput(proc, input) {
6618
+ if (!this.enabled) {
6619
+ return input;
6620
+ }
6600
6621
  const procDef = (this.client.$schema.procedures ?? {})[proc];
6601
6622
  invariant9(procDef, `Procedure "${proc}" not found in schema`);
6602
6623
  const params = Object.values(procDef.params ?? {});
@@ -6661,6 +6682,9 @@ var InputValidator = class {
6661
6682
  // #endregion
6662
6683
  // #region Validation helpers
6663
6684
  validate(model, operation, getSchema, args) {
6685
+ if (!this.enabled) {
6686
+ return args;
6687
+ }
6664
6688
  const schema = getSchema(model);
6665
6689
  const { error, data } = schema.safeParse(args);
6666
6690
  if (error) {
@@ -7495,6 +7519,7 @@ var TempAliasTransformer = class extends OperationNodeTransformer2 {
7495
7519
  };
7496
7520
 
7497
7521
  // src/client/executor/zenstack-query-executor.ts
7522
+ var DEFAULT_MAX_SLOW_RECORDS = 100;
7498
7523
  var ZenStackQueryExecutor = class _ZenStackQueryExecutor extends DefaultQueryExecutor {
7499
7524
  static {
7500
7525
  __name(this, "ZenStackQueryExecutor");
@@ -7911,13 +7936,49 @@ In such cases, ZenStack cannot reliably determine the IDs of the mutated entitie
7911
7936
  parameters
7912
7937
  };
7913
7938
  }
7939
+ const trackSlowQuery = this.options.diagnostics !== void 0;
7940
+ const startTimestamp = trackSlowQuery ? performance.now() : void 0;
7941
+ const startedAt = trackSlowQuery ? /* @__PURE__ */ new Date() : void 0;
7914
7942
  try {
7915
7943
  const result = await connection.executeQuery(compiledQuery);
7944
+ if (startTimestamp !== void 0) {
7945
+ this.trackSlowQuery(compiledQuery, startTimestamp, startedAt);
7946
+ }
7916
7947
  return this.ensureProperQueryResult(compiledQuery.query, result);
7917
7948
  } catch (err) {
7918
7949
  throw createDBQueryError(`Failed to execute query: ${err}`, err, compiledQuery.sql, compiledQuery.parameters);
7919
7950
  }
7920
7951
  }
7952
+ trackSlowQuery(compiledQuery, startTimestamp, startedAt) {
7953
+ const durationMs = performance.now() - startTimestamp;
7954
+ const thresholdMs = this.options.diagnostics?.slowQueryThresholdMs;
7955
+ if (thresholdMs === void 0 || durationMs < thresholdMs) {
7956
+ return;
7957
+ }
7958
+ const slowQueries = this.client.slowQueries;
7959
+ const maxRecords = this.options.diagnostics?.slowQueryMaxRecords ?? DEFAULT_MAX_SLOW_RECORDS;
7960
+ if (maxRecords <= 0) {
7961
+ return;
7962
+ }
7963
+ const queryInfo = {
7964
+ startedAt,
7965
+ durationMs,
7966
+ sql: compiledQuery.sql
7967
+ };
7968
+ if (slowQueries.length >= maxRecords) {
7969
+ let minIndex = 0;
7970
+ for (let i = 1; i < slowQueries.length; i++) {
7971
+ if (slowQueries[i].durationMs < slowQueries[minIndex].durationMs) {
7972
+ minIndex = i;
7973
+ }
7974
+ }
7975
+ if (durationMs > slowQueries[minIndex].durationMs) {
7976
+ slowQueries[minIndex] = queryInfo;
7977
+ }
7978
+ } else {
7979
+ slowQueries.push(queryInfo);
7980
+ }
7981
+ }
7921
7982
  ensureProperQueryResult(query, result) {
7922
7983
  let finalResult = result;
7923
7984
  if (this.isMutationNode(query)) {
@@ -8064,7 +8125,7 @@ var isEmpty = /* @__PURE__ */ __name((eb, args, { dialect }) => {
8064
8125
  }
8065
8126
  return eb(dialect.buildArrayLength(field), "=", sql6.lit(0));
8066
8127
  }, "isEmpty");
8067
- var now = /* @__PURE__ */ __name(() => sql6.raw("CURRENT_TIMESTAMP"), "now");
8128
+ var now = /* @__PURE__ */ __name((_eb, _args, context) => match16(context.dialect.provider).with("sqlite", () => sql6.raw("strftime('%Y-%m-%dT%H:%M:%fZ')")).with("mysql", () => sql6.raw("CONCAT(SUBSTRING(DATE_FORMAT(UTC_TIMESTAMP(3), '%Y-%m-%dT%H:%i:%s.%f'), 1, 23), '+00:00')")).with("postgresql", () => sql6.raw("CURRENT_TIMESTAMP")).exhaustive(), "now");
8068
8129
  var currentModel = /* @__PURE__ */ __name((_eb, args, { model }) => {
8069
8130
  let result = model;
8070
8131
  const [casing] = args;
@@ -8589,6 +8650,7 @@ var ClientImpl = class _ClientImpl {
8589
8650
  kyselyProps;
8590
8651
  auth;
8591
8652
  inputValidator;
8653
+ slowQueries = [];
8592
8654
  constructor(schema, options, baseClient, executor) {
8593
8655
  this.schema = schema;
8594
8656
  this.options = options;
@@ -8598,9 +8660,7 @@ var ClientImpl = class _ClientImpl {
8598
8660
  ...functions_exports,
8599
8661
  ...this.$options.functions
8600
8662
  };
8601
- if (!baseClient && !options.skipValidationForComputedFields) {
8602
- this.validateComputedFieldsConfig();
8603
- }
8663
+ this.validateOptions(baseClient, options);
8604
8664
  if (baseClient) {
8605
8665
  this.kyselyProps = {
8606
8666
  ...baseClient.kyselyProps,
@@ -8608,6 +8668,7 @@ var ClientImpl = class _ClientImpl {
8608
8668
  };
8609
8669
  this.kyselyRaw = baseClient.kyselyRaw;
8610
8670
  this.auth = baseClient.auth;
8671
+ this.slowQueries = baseClient.slowQueries;
8611
8672
  } else {
8612
8673
  const driver = new ZenStackDriver(options.dialect.createDriver(), new Log(this.$options.log ?? []));
8613
8674
  const compiler = options.dialect.createQueryCompiler();
@@ -8628,32 +8689,31 @@ var ClientImpl = class _ClientImpl {
8628
8689
  });
8629
8690
  }
8630
8691
  this.kysely = new Kysely(this.kyselyProps);
8631
- this.inputValidator = baseClient?.inputValidator ?? new InputValidator(this);
8692
+ this.inputValidator = baseClient?.inputValidator ?? new InputValidator(this, {
8693
+ enabled: this.$options.validateInput !== false
8694
+ });
8632
8695
  return createClientProxy(this);
8633
8696
  }
8634
- get $qb() {
8635
- return this.kysely;
8636
- }
8637
- get $qbRaw() {
8638
- return this.kyselyRaw;
8639
- }
8640
- get $zod() {
8641
- return this.inputValidator.zodFactory;
8642
- }
8643
- get isTransaction() {
8644
- return this.kysely.isTransaction;
8645
- }
8646
- /**
8647
- * Create a new client with a new query executor.
8648
- */
8649
- withExecutor(executor) {
8650
- return new _ClientImpl(this.schema, this.$options, this, executor);
8697
+ validateOptions(baseClient, options) {
8698
+ if (!baseClient && !options.skipValidationForComputedFields) {
8699
+ this.validateComputedFieldsConfig(options);
8700
+ }
8701
+ if (options.diagnostics) {
8702
+ const diagnosticsSchema = z2.object({
8703
+ slowQueryThresholdMs: z2.number().nonnegative().optional(),
8704
+ slowQueryMaxRecords: z2.int().nonnegative().or(z2.literal(Infinity)).optional()
8705
+ });
8706
+ const parseResult = diagnosticsSchema.safeParse(options.diagnostics);
8707
+ if (!parseResult.success) {
8708
+ throw createConfigError(`Invalid diagnostics configuration: ${formatError(parseResult.error)}`);
8709
+ }
8710
+ }
8651
8711
  }
8652
8712
  /**
8653
8713
  * Validates that all computed fields in the schema have corresponding configurations.
8654
8714
  */
8655
- validateComputedFieldsConfig() {
8656
- const computedFieldsConfig = "computedFields" in this.$options ? this.$options.computedFields : void 0;
8715
+ validateComputedFieldsConfig(options) {
8716
+ const computedFieldsConfig = "computedFields" in options ? options.computedFields : void 0;
8657
8717
  for (const [modelName, modelDef] of Object.entries(this.$schema.models)) {
8658
8718
  if (modelDef.computedFields) {
8659
8719
  for (const fieldName of Object.keys(modelDef.computedFields)) {
@@ -8669,6 +8729,24 @@ var ClientImpl = class _ClientImpl {
8669
8729
  }
8670
8730
  }
8671
8731
  }
8732
+ get $qb() {
8733
+ return this.kysely;
8734
+ }
8735
+ get $qbRaw() {
8736
+ return this.kyselyRaw;
8737
+ }
8738
+ get $zod() {
8739
+ return this.inputValidator.zodFactory;
8740
+ }
8741
+ get isTransaction() {
8742
+ return this.kysely.isTransaction;
8743
+ }
8744
+ /**
8745
+ * Create a new client with a new query executor.
8746
+ */
8747
+ withExecutor(executor) {
8748
+ return new _ClientImpl(this.schema, this.$options, this, executor);
8749
+ }
8672
8750
  // implementation
8673
8751
  async $transaction(input, options) {
8674
8752
  invariant14(typeof input === "function" || Array.isArray(input) && input.every((p) => p.then && p.cb), "Invalid transaction input, expected a function or an array of ZenStackPromise");
@@ -8790,7 +8868,9 @@ var ClientImpl = class _ClientImpl {
8790
8868
  plugins: newPlugins
8791
8869
  };
8792
8870
  const newClient = new _ClientImpl(this.schema, newOptions, this);
8793
- newClient.inputValidator = new InputValidator(newClient);
8871
+ newClient.inputValidator = new InputValidator(newClient, {
8872
+ enabled: newOptions.validateInput !== false
8873
+ });
8794
8874
  return newClient;
8795
8875
  }
8796
8876
  $unuse(pluginId) {
@@ -8805,7 +8885,9 @@ var ClientImpl = class _ClientImpl {
8805
8885
  plugins: newPlugins
8806
8886
  };
8807
8887
  const newClient = new _ClientImpl(this.schema, newOptions, this);
8808
- newClient.inputValidator = new InputValidator(newClient);
8888
+ newClient.inputValidator = new InputValidator(newClient, {
8889
+ enabled: newClient.$options.validateInput !== false
8890
+ });
8809
8891
  return newClient;
8810
8892
  }
8811
8893
  $unuseAll() {
@@ -8814,7 +8896,9 @@ var ClientImpl = class _ClientImpl {
8814
8896
  plugins: []
8815
8897
  };
8816
8898
  const newClient = new _ClientImpl(this.schema, newOptions, this);
8817
- newClient.inputValidator = new InputValidator(newClient);
8899
+ newClient.inputValidator = new InputValidator(newClient, {
8900
+ enabled: newOptions.validateInput !== false
8901
+ });
8818
8902
  return newClient;
8819
8903
  }
8820
8904
  $setAuth(auth) {
@@ -8830,7 +8914,9 @@ var ClientImpl = class _ClientImpl {
8830
8914
  }
8831
8915
  $setOptions(options) {
8832
8916
  const newClient = new _ClientImpl(this.schema, options, this);
8833
- newClient.inputValidator = new InputValidator(newClient);
8917
+ newClient.inputValidator = new InputValidator(newClient, {
8918
+ enabled: newClient.$options.validateInput !== false
8919
+ });
8834
8920
  return newClient;
8835
8921
  }
8836
8922
  $setInputValidation(enable) {
@@ -8840,6 +8926,14 @@ var ClientImpl = class _ClientImpl {
8840
8926
  };
8841
8927
  return this.$setOptions(newOptions);
8842
8928
  }
8929
+ async $diagnostics() {
8930
+ return {
8931
+ zodCache: this.inputValidator.zodFactory.cacheStats,
8932
+ slowQueries: this.slowQueries.map((q) => ({
8933
+ ...q
8934
+ }))
8935
+ };
8936
+ }
8843
8937
  $executeRaw(query, ...values) {
8844
8938
  return createZenStackPromise(async () => {
8845
8939
  const result = await sql8(query, ...values).execute(this.kysely);