@m1212e/rumble 0.22.2 → 0.23.2

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/out/index.cjs CHANGED
@@ -33,9 +33,9 @@ let graphql_yoga = require("graphql-yoga");
33
33
  let drizzle_orm = require("drizzle-orm");
34
34
  let drizzle_orm_casing = require("drizzle-orm/casing");
35
35
  let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
36
+ let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
36
37
  let pluralize = require("pluralize");
37
38
  pluralize = __toESM(pluralize, 1);
38
- let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
39
39
  let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
40
40
  let _pothos_core = require("@pothos/core");
41
41
  _pothos_core = __toESM(_pothos_core, 1);
@@ -208,7 +208,7 @@ function mapNullFieldsToUndefined(obj) {
208
208
  }
209
209
  //#endregion
210
210
  //#region package.json
211
- var version = "0.22.2";
211
+ var version = "0.23.2";
212
212
  //#endregion
213
213
  //#region lib/helpers/mergeFilters.ts
214
214
  function mergeFilters(filterA, filterB, mode = "AND") {
@@ -227,23 +227,47 @@ function mergeFilters(filterA, filterB, mode = "AND") {
227
227
  }
228
228
  //#endregion
229
229
  //#region lib/helpers/sqlTypes/types.ts
230
+ /**
231
+ * Strips parameter suffixes like `(256)`, `(10,2)` and timezone modifiers
232
+ * (`with time zone`, `without time zone`) from raw getSQLType() output so
233
+ * every classifier works on the base type name only.
234
+ */
235
+ function normalizeSQLType(raw) {
236
+ return raw.replace(/\s*with(?:out)?\s+time\s+zone/i, "").replace(/\s*\([^)]*\)/, "").trim().toLowerCase();
237
+ }
230
238
  const intLikeSQLTypeStrings = [
231
239
  "serial",
232
240
  "int",
233
241
  "integer",
234
242
  "tinyint",
235
243
  "smallint",
236
- "mediumint"
244
+ "mediumint",
245
+ "int2",
246
+ "int4",
247
+ "smallserial"
237
248
  ];
238
249
  function isIntLikeSQLTypeString(sqlType) {
239
250
  return intLikeSQLTypeStrings.includes(sqlType);
240
251
  }
252
+ const bigIntLikeSQLTypeStrings = [
253
+ "bigint",
254
+ "bigserial",
255
+ "int8",
256
+ "bigint unsigned"
257
+ ];
258
+ function isBigIntLikeSQLTypeString(sqlType) {
259
+ return bigIntLikeSQLTypeStrings.includes(sqlType);
260
+ }
241
261
  const floatLikeSQLTypeStrings = [
242
262
  "real",
243
263
  "decimal",
244
264
  "double",
245
265
  "float",
246
- "numeric"
266
+ "numeric",
267
+ "double precision",
268
+ "float4",
269
+ "float8",
270
+ "money"
247
271
  ];
248
272
  function isFloatLikeSQLTypeString(sqlType) {
249
273
  return floatLikeSQLTypeStrings.includes(sqlType);
@@ -253,7 +277,20 @@ const stringLikeSQLTypeStrings = [
253
277
  "text",
254
278
  "varchar",
255
279
  "char",
256
- "text(256)"
280
+ "character varying",
281
+ "character",
282
+ "inet",
283
+ "cidr",
284
+ "macaddr",
285
+ "macaddr8",
286
+ "interval",
287
+ "time",
288
+ "year",
289
+ "binary",
290
+ "varbinary",
291
+ "tinytext",
292
+ "mediumtext",
293
+ "longtext"
257
294
  ];
258
295
  function isStringLikeSQLTypeString(sqlType) {
259
296
  return stringLikeSQLTypeStrings.includes(sqlType);
@@ -262,7 +299,7 @@ const IDLikeSQLTypeStrings = ["uuid"];
262
299
  function isIDLikeSQLTypeString(sqlType) {
263
300
  return IDLikeSQLTypeStrings.includes(sqlType);
264
301
  }
265
- const booleanSQLTypeStrings = ["boolean"];
302
+ const booleanSQLTypeStrings = ["boolean", "bool"];
266
303
  function isBooleanSQLTypeString(sqlType) {
267
304
  return booleanSQLTypeStrings.includes(sqlType);
268
305
  }
@@ -278,16 +315,23 @@ const jsonLikeSQLTypeStrings = ["json", "jsonb"];
278
315
  function isJSONLikeSQLTypeString(sqlType) {
279
316
  return jsonLikeSQLTypeStrings.includes(sqlType);
280
317
  }
281
- [
282
- ...intLikeSQLTypeStrings,
283
- ...floatLikeSQLTypeStrings,
284
- ...stringLikeSQLTypeStrings,
285
- ...IDLikeSQLTypeStrings,
286
- ...booleanSQLTypeStrings,
287
- ...dateTimeLikeSQLTypeStrings,
288
- ...dateLikeSQLTypeStrings,
289
- ...jsonLikeSQLTypeStrings
318
+ const jsonFallbackSQLTypeStrings = [
319
+ "point",
320
+ "line",
321
+ "lseg",
322
+ "box",
323
+ "path",
324
+ "polygon",
325
+ "circle",
326
+ "geometry"
290
327
  ];
328
+ function isJSONFallbackSQLTypeString(sqlType) {
329
+ return jsonFallbackSQLTypeStrings.includes(sqlType) || sqlType.startsWith("geometry");
330
+ }
331
+ const bytesSQLTypeStrings = ["bytea", "blob"];
332
+ function isBytesSQLTypeString(sqlType) {
333
+ return bytesSQLTypeStrings.includes(sqlType);
334
+ }
291
335
  const UnknownTypeRumbleError = (sqlType, additionalInfo) => new RumbleError(`RumbleError: Unknown SQL type '${sqlType}'. Please open an issue (https://github.com/m1212e/rumble/issues) so it can be added. (${additionalInfo})`);
292
336
  //#endregion
293
337
  //#region lib/helpers/sqlTypes/distinctValuesFromSQLType.ts
@@ -402,16 +446,17 @@ function isDynamicQueryFilter(filter) {
402
446
  function isStaticQueryFilter(filter) {
403
447
  return typeof filter !== "function";
404
448
  }
405
- const nothingRegisteredWarningLogger = (0, es_toolkit.debounce)((model, action) => {
406
- console.warn(`
407
- Warning! No abilities have been registered for
408
-
409
- ${model}/${action}
410
-
411
- but has been accessed. This will block everything. If this is intended, you can ignore this warning. If not, please ensure that you register the ability in your ability builder.
412
- `);
449
+ const makeNothingRegisteredWarner = (logger) => (0, es_toolkit.debounce)((model, action) => {
450
+ const msg = `No abilities registered for ${model}/${action} — blocking everything. Register the ability or ignore this warning if intentional.`;
451
+ if (logger) logger.warn({
452
+ "rumble.table": model,
453
+ "rumble.action": action
454
+ }, msg);
455
+ else console.warn(msg);
413
456
  }, 1e3);
414
- const createAbilityBuilder = ({ db, actions, defaultLimit, otel }) => {
457
+ const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerConfig }) => {
458
+ const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
459
+ const nothingRegisteredWarningLogger = makeNothingRegisteredWarner(log);
415
460
  let hasBeenBuilt = false;
416
461
  const createBuilderForTable = () => {
417
462
  const queryFilters = /* @__PURE__ */ new Map();
@@ -659,7 +704,8 @@ by: (explicitFilter) => {
659
704
  * Merges the current query filters with the provided filters for this call only
660
705
  */
661
706
  function merge(p) {
662
- return internalTransformer(mergeFilters(ret.query.many, p), p.limit);
707
+ const merged = mergeFilters(ret.query.many, p);
708
+ return internalTransformer(merged, p.limit);
663
709
  }
664
710
  ret.merge = merge;
665
711
  return ret;
@@ -701,14 +747,39 @@ by: (explicitFilter) => {
701
747
  span?.setAttribute("abilities.status", "applied");
702
748
  return transformToResponse(mergedFilters);
703
749
  };
704
- if (otel?.enabled && otel.tracer) return otel.tracer.startActiveSpan(`prepare_query_abilities_${action}`, (span) => {
750
+ if (otel?.enabled && otel.tracer) return otel.tracer.startActiveSpan(`rumble.abilities.prepare`, (span) => {
751
+ span.setAttribute("rumble.action", action);
752
+ span.setAttribute("rumble.table", tableName.toString());
705
753
  try {
706
- return assembleAbilities(span);
754
+ const attrs = {
755
+ "rumble.table": tableName.toString(),
756
+ "rumble.action": action
757
+ };
758
+ const proxy = new Proxy(span, { get(target, prop) {
759
+ if (prop === "setAttribute") return (k, v) => {
760
+ attrs[k] = v;
761
+ return target.setAttribute(k, v);
762
+ };
763
+ return target[prop];
764
+ } });
765
+ const result = assembleAbilities(proxy);
766
+ log?.debug(attrs, "abilities prepared");
767
+ return result;
707
768
  } finally {
708
769
  span.end();
709
770
  }
710
771
  });
711
- else return assembleAbilities();
772
+ else {
773
+ const attrs = {
774
+ "rumble.table": tableName.toString(),
775
+ "rumble.action": action
776
+ };
777
+ const result = assembleAbilities(log ? { setAttribute: (k, v) => {
778
+ attrs[k] = v;
779
+ } } : void 0);
780
+ log?.debug(attrs, "abilities prepared");
781
+ return result;
782
+ }
712
783
  } };
713
784
  } };
714
785
  };
@@ -737,29 +808,14 @@ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
737
808
  if (ret) return ret;
738
809
  const implement = () => {
739
810
  return schemaBuilder.inputType(inputTypeName, { fields: (t) => {
740
- const fields = Object.entries(tableSchema.columns).reduce((acc, [key]) => {
811
+ return Object.entries(tableSchema.columns).reduce((acc, [key]) => {
812
+ if (key.startsWith("__")) return acc;
741
813
  acc[key] = t.field({
742
814
  type: sortingParameterEnumRef(),
743
815
  required: false
744
816
  });
745
817
  return acc;
746
818
  }, {});
747
- const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
748
- const targetTsName = value.targetTableName;
749
- const referenceModel = orderArgImplementer({ dbName: tableHelper({
750
- db,
751
- table: targetTsName
752
- }).dbName });
753
- acc[key] = t.field({
754
- type: referenceModel,
755
- required: false
756
- });
757
- return acc;
758
- }, {});
759
- return {
760
- ...fields,
761
- ...relations
762
- };
763
819
  } });
764
820
  };
765
821
  ret = implement();
@@ -771,10 +827,10 @@ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
771
827
  //#endregion
772
828
  //#region lib/enum.ts
773
829
  /**
774
- * Checks if a column is a Postgres enum column
830
+ * Checks if a column is a database enum column (Postgres or MySQL).
775
831
  */
776
832
  function isEnumSchema(schemaType) {
777
- return (0, drizzle_orm.is)(schemaType, drizzle_orm_pg_core.PgEnumColumn) || (0, drizzle_orm.is)(schemaType, drizzle_orm_pg_core.PgEnumObjectColumn);
833
+ return (0, drizzle_orm.is)(schemaType, drizzle_orm_pg_core.PgEnumColumn) || (0, drizzle_orm.is)(schemaType, drizzle_orm_pg_core.PgEnumObjectColumn) || (0, drizzle_orm.is)(schemaType, drizzle_orm_mysql_core.MySqlEnumColumn) || (0, drizzle_orm.is)(schemaType, drizzle_orm_mysql_core.MySqlEnumObjectColumn);
778
834
  }
779
835
  const createEnumImplementer = ({ schema, schemaBuilder }) => {
780
836
  const referenceStorage = /* @__PURE__ */ new Map();
@@ -806,7 +862,12 @@ const createEnumImplementer = ({ schema, schemaBuilder }) => {
806
862
  if (enumObject) {
807
863
  enumSchemaName = enumObject.enumName;
808
864
  enumValues = enumObject.enumValues;
809
- } else if (enumColumn) {
865
+ } else if (enumColumn) if ((0, drizzle_orm.is)(enumColumn, drizzle_orm_mysql_core.MySqlEnumColumn) || (0, drizzle_orm.is)(enumColumn, drizzle_orm_mysql_core.MySqlEnumObjectColumn)) {
866
+ enumValues = enumColumn.enumValues;
867
+ const colTable = enumColumn.table;
868
+ const tableName = colTable && (0, drizzle_orm.isTable)(colTable) ? (0, drizzle_orm.getTableName)(colTable) : void 0;
869
+ enumSchemaName = tableName ? `${tableName}_${enumColumn.name}` : enumColumn.name;
870
+ } else {
810
871
  const pgEnum = enumColumn.enum;
811
872
  if (!pgEnum) throw new RumbleError(`Could not find enum definition on column. Make sure the column is a pgEnum column.`);
812
873
  enumSchemaName = pgEnum.enumName;
@@ -825,16 +886,21 @@ const createEnumImplementer = ({ schema, schemaBuilder }) => {
825
886
  //#endregion
826
887
  //#region lib/helpers/sqlTypes/mapSQLTypeToTSType.ts
827
888
  function mapSQLTypeToGraphQLType({ sqlType, fieldName }) {
889
+ if (/^enum\(/.test(sqlType)) return "String";
890
+ const normalized = normalizeSQLType(sqlType);
828
891
  let ret;
829
- if (isIntLikeSQLTypeString(sqlType)) ret = "Int";
830
- if (isFloatLikeSQLTypeString(sqlType)) ret = "Float";
831
- if (isStringLikeSQLTypeString(sqlType)) if (fieldName && (fieldName.toLowerCase().endsWith("_id") || fieldName.toLowerCase().endsWith("id"))) ret = "ID";
892
+ if (isIntLikeSQLTypeString(normalized)) ret = "Int";
893
+ if (isBigIntLikeSQLTypeString(normalized)) ret = "BigInt";
894
+ if (isFloatLikeSQLTypeString(normalized)) ret = "Float";
895
+ if (isStringLikeSQLTypeString(normalized)) if (fieldName && (fieldName.toLowerCase().endsWith("_id") || fieldName.toLowerCase().endsWith("id"))) ret = "ID";
832
896
  else ret = "String";
833
- if (isIDLikeSQLTypeString(sqlType)) ret = "ID";
834
- if (isBooleanSQLTypeString(sqlType)) ret = "Boolean";
835
- if (isDateTimeLikeSQLTypeString(sqlType)) ret = "DateTime";
836
- if (isDateLikeSQLTypeString(sqlType)) ret = "Date";
837
- if (isJSONLikeSQLTypeString(sqlType)) ret = "JSON";
897
+ if (isIDLikeSQLTypeString(normalized)) ret = "ID";
898
+ if (isBooleanSQLTypeString(normalized)) ret = "Boolean";
899
+ if (isDateTimeLikeSQLTypeString(normalized)) ret = "DateTime";
900
+ if (isDateLikeSQLTypeString(normalized)) ret = "Date";
901
+ if (isJSONLikeSQLTypeString(normalized)) ret = "JSON";
902
+ if (isJSONFallbackSQLTypeString(normalized)) ret = "JSON";
903
+ if (isBytesSQLTypeString(normalized)) ret = "Bytes";
838
904
  if (ret !== void 0) return ret;
839
905
  throw UnknownTypeRumbleError(sqlType, "SQL to GQL");
840
906
  }
@@ -844,6 +910,60 @@ const makeDefaultName = (dbName) => `${(0, es_toolkit.capitalize)((0, drizzle_or
844
910
  const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
845
911
  const referenceStorage = /* @__PURE__ */ new Map();
846
912
  const enumArrayWhereInputStorage = /* @__PURE__ */ new Map();
913
+ const scalarArrayWhereInputStorage = /* @__PURE__ */ new Map();
914
+ const scalarArrayWhereInputName = (gqlType) => `${gqlType}ArrayWhereInputArgument`;
915
+ const getScalarArrayWhereInput = (gqlType) => {
916
+ const typeName = scalarArrayWhereInputName(gqlType);
917
+ const existing = scalarArrayWhereInputStorage.get(typeName);
918
+ if (existing) return existing;
919
+ const ret = schemaBuilder.inputRef(typeName);
920
+ scalarArrayWhereInputStorage.set(typeName, ret);
921
+ ret.implement({ fields: (t) => ({
922
+ eq: t.field({
923
+ type: [gqlType],
924
+ required: false
925
+ }),
926
+ ne: t.field({
927
+ type: [gqlType],
928
+ required: false
929
+ }),
930
+ in: t.field({
931
+ type: [[gqlType]],
932
+ required: false
933
+ }),
934
+ notIn: t.field({
935
+ type: [[gqlType]],
936
+ required: false
937
+ }),
938
+ isNull: t.boolean({ required: false }),
939
+ isNotNull: t.boolean({ required: false }),
940
+ arrayOverlaps: t.field({
941
+ type: [gqlType],
942
+ required: false
943
+ }),
944
+ arrayContained: t.field({
945
+ type: [gqlType],
946
+ required: false
947
+ }),
948
+ arrayContains: t.field({
949
+ type: [gqlType],
950
+ required: false
951
+ }),
952
+ AND: t.field({
953
+ type: [ret],
954
+ required: false
955
+ }),
956
+ OR: t.field({
957
+ type: [ret],
958
+ required: false
959
+ }),
960
+ NOT: t.field({
961
+ type: ret,
962
+ required: false
963
+ })
964
+ }) });
965
+ return ret;
966
+ };
847
967
  const getEnumArrayWhereInput = (enumImpl) => {
848
968
  const typeName = `${enumImpl.name}WhereInputArgument`;
849
969
  const existing = enumArrayWhereInputStorage.get(typeName);
@@ -907,39 +1027,82 @@ const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
907
1027
  const ret = schemaBuilder.inputRef(inputTypeName);
908
1028
  referenceStorage.set(inputTypeName, ret);
909
1029
  ret.implement({ fields: (t) => {
910
- const mapSQLTypeStringToInputPothosType = (sqlType, fieldName) => {
1030
+ const mapSQLTypeStringToInputPothosType = (sqlType, fieldName, isArray) => {
911
1031
  const gqlType = mapSQLTypeToGraphQLType({
912
1032
  sqlType,
913
1033
  fieldName
914
1034
  });
1035
+ if (isArray) return t.field({
1036
+ type: getScalarArrayWhereInput(gqlType),
1037
+ required: false
1038
+ });
915
1039
  switch (gqlType) {
916
- case "Int": return t.field({ type: "IntWhereInputArgument" });
917
- case "String": return t.field({ type: "StringWhereInputArgument" });
918
- case "Boolean": return t.field({ type: "BooleanWhereInputArgument" });
919
- case "Date": return t.field({ type: "DateWhereInputArgument" });
920
- case "DateTime": return t.field({ type: "DateWhereInputArgument" });
921
- case "Float": return t.field({ type: "FloatWhereInputArgument" });
922
- case "ID": return t.field({ type: "IDWhereInputArgument" });
923
- case "JSON": return t.field({ type: "JSONWhereInputArgument" });
1040
+ case "Int": return t.field({
1041
+ type: "IntWhereInputArgument",
1042
+ required: false
1043
+ });
1044
+ case "BigInt": return t.field({
1045
+ type: "BigIntWhereInputArgument",
1046
+ required: false
1047
+ });
1048
+ case "String": return t.field({
1049
+ type: "StringWhereInputArgument",
1050
+ required: false
1051
+ });
1052
+ case "Boolean": return t.field({
1053
+ type: "BooleanWhereInputArgument",
1054
+ required: false
1055
+ });
1056
+ case "Date": return t.field({
1057
+ type: "DateWhereInputArgument",
1058
+ required: false
1059
+ });
1060
+ case "DateTime": return t.field({
1061
+ type: "DateTimeWhereInputArgument",
1062
+ required: false
1063
+ });
1064
+ case "Float": return t.field({
1065
+ type: "FloatWhereInputArgument",
1066
+ required: false
1067
+ });
1068
+ case "ID": return t.field({
1069
+ type: "IDWhereInputArgument",
1070
+ required: false
1071
+ });
1072
+ case "JSON": return t.field({
1073
+ type: "JSONWhereInputArgument",
1074
+ required: false
1075
+ });
1076
+ case "Bytes": return null;
924
1077
  default: throw new RumbleError(`Unsupported argument type ${gqlType} for column ${sqlType}`);
925
1078
  }
926
1079
  };
927
1080
  const fields = Object.entries(tableSchema.columns).reduce((acc, [key, value]) => {
1081
+ if (key.startsWith("__")) return acc;
1082
+ let isArray = false;
1083
+ if ((0, drizzle_orm.is)(value, drizzle_orm_pg_core.PgColumn)) {
1084
+ const dims = value.dimensions;
1085
+ if (dims && dims > 0) isArray = true;
1086
+ }
928
1087
  if (isEnumSchema(value)) {
929
1088
  const enumImpl = enumImplementer({ enumColumn: value });
930
1089
  acc[key] = t.field({
931
- type: value.dimensions > 0 ? getEnumArrayWhereInput(enumImpl) : enumImpl,
1090
+ type: isArray ? getEnumArrayWhereInput(enumImpl) : enumImpl,
932
1091
  required: false
933
1092
  });
934
- } else acc[key] = mapSQLTypeStringToInputPothosType(value.getSQLType(), key);
1093
+ } else {
1094
+ const field = mapSQLTypeStringToInputPothosType(value.getSQLType(), key, isArray);
1095
+ if (field !== null) acc[key] = field;
1096
+ }
935
1097
  return acc;
936
1098
  }, {});
937
1099
  const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
938
1100
  const targetTsName = value.targetTableName;
939
- const referenceModel = whereArgImplementer({ dbName: tableHelper({
1101
+ const relationSchema = tableHelper({
940
1102
  db,
941
1103
  table: targetTsName
942
- }).dbName });
1104
+ });
1105
+ const referenceModel = whereArgImplementer({ dbName: relationSchema.dbName });
943
1106
  acc[key] = t.field({
944
1107
  type: referenceModel,
945
1108
  required: false
@@ -1145,6 +1308,11 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
1145
1308
  });
1146
1309
  switch (gqlType) {
1147
1310
  case "Int": return isArray ? builder.exposeIntList(fieldName, { nullable }) : builder.exposeInt(fieldName, { nullable });
1311
+ case "BigInt": return builder.field({
1312
+ type: isArray ? ["BigInt"] : "BigInt",
1313
+ resolve: (element) => element[fieldName],
1314
+ nullable
1315
+ });
1148
1316
  case "String": return isArray ? builder.exposeStringList(fieldName, { nullable }) : builder.exposeString(fieldName, { nullable });
1149
1317
  case "Boolean": return isArray ? builder.exposeBooleanList(fieldName, { nullable }) : builder.exposeBoolean(fieldName, { nullable });
1150
1318
  case "Date": return builder.field({
@@ -1154,7 +1322,11 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
1154
1322
  });
1155
1323
  case "DateTime": return builder.field({
1156
1324
  type: isArray ? ["DateTime"] : "DateTime",
1157
- resolve: (element) => element[fieldName],
1325
+ resolve: (element) => {
1326
+ const v = element[fieldName];
1327
+ if (v == null) return v;
1328
+ return v instanceof Date ? v : new Date(v);
1329
+ },
1158
1330
  nullable
1159
1331
  });
1160
1332
  case "Float": return isArray ? builder.exposeFloatList(fieldName, { nullable }) : builder.exposeFloat(fieldName, { nullable });
@@ -1164,6 +1336,15 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
1164
1336
  resolve: (element) => element[fieldName],
1165
1337
  nullable
1166
1338
  });
1339
+ case "Bytes": return builder.field({
1340
+ type: "Bytes",
1341
+ resolve: (element) => {
1342
+ const v = element[fieldName];
1343
+ if (v == null) return v;
1344
+ return Buffer.isBuffer(v) ? v.toString("base64") : String(v);
1345
+ },
1346
+ nullable
1347
+ });
1167
1348
  default: throw new RumbleError(`Unsupported object type ${gqlType} for column ${fieldName}`);
1168
1349
  }
1169
1350
  }
@@ -1195,14 +1376,17 @@ function isPostgresDB(db) {
1195
1376
  //#endregion
1196
1377
  //#region lib/search.ts
1197
1378
  async function initSearchIfApplicable(input) {
1379
+ const log = input.logger?.enabled ? input.logger.logger : void 0;
1198
1380
  if (!isPostgresDB(input.db)) {
1199
- console.info("Database dialect is not compatible with search, skipping search initialization. Only PostgreSQL is supported.");
1381
+ const msg = "Database dialect is not compatible with search, skipping search initialization. Only PostgreSQL is supported.";
1382
+ log ? log.info({}, msg) : console.info(msg);
1200
1383
  return;
1201
1384
  }
1202
1385
  try {
1203
1386
  await input.db.execute(drizzle_orm.sql`CREATE EXTENSION IF NOT EXISTS pg_trgm;`);
1204
1387
  } catch (error) {
1205
- console.error("Failed to create pg_trgm extension. Search functionality may not work. Ensure the database user has CREATE privilege or the extension is pre-installed.", error);
1388
+ const msg = "Failed to create pg_trgm extension. Search functionality may not work. Ensure the database user has CREATE privilege or the extension is pre-installed.";
1389
+ log ? log.error({ err: error }, msg) : console.error(msg, error);
1206
1390
  return;
1207
1391
  }
1208
1392
  if (input.search?.threshold) {
@@ -1261,13 +1445,17 @@ const isProbablyAConfigObject = (t) => {
1261
1445
  ].find((e) => e === k))) return true;
1262
1446
  return false;
1263
1447
  };
1264
- const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance, whereArgImplementer, orderArgImplementer, enumImplementer, abilityBuilder }) => {
1448
+ const createObjectImplementer = ({ db, search, logger: loggerConfig, schemaBuilder, makePubSubInstance, whereArgImplementer, orderArgImplementer, enumImplementer, abilityBuilder }) => {
1265
1449
  return ({ table, refName, readAction = "read", adjust }) => {
1266
1450
  const tableSchema = tableHelper({
1267
1451
  db,
1268
1452
  table
1269
1453
  });
1270
- if (Object.keys(tableSchema.primaryKey).length === 0) console.warn(`Could not find primary key for ${table.toString()}. Cannot register subscriptions!`);
1454
+ if (Object.keys(tableSchema.primaryKey).length === 0) {
1455
+ const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
1456
+ const msg = `Could not find primary key for ${table.toString()}. Cannot register subscriptions!`;
1457
+ log ? log.warn({ "rumble.table": table.toString() }, msg) : console.warn(msg);
1458
+ }
1271
1459
  const primaryKey = Object.values(tableSchema.primaryKey)[0];
1272
1460
  const { registerOnInstance } = makePubSubInstance({ table });
1273
1461
  return schemaBuilder.drizzleObject(table, {
@@ -1276,7 +1464,9 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
1276
1464
  if (!primaryKey) return;
1277
1465
  const primaryKeyValue = element[primaryKey.name];
1278
1466
  if (!primaryKeyValue) {
1279
- console.warn(`Could not find primary key value for ${JSON.stringify(element)}. Cannot register subscription!`);
1467
+ const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
1468
+ const msg = `Could not find primary key value for element on ${table.toString()}. Cannot register subscription!`;
1469
+ log ? log.warn({ "rumble.table": table.toString() }, msg) : console.warn(msg);
1280
1470
  return;
1281
1471
  }
1282
1472
  registerOnInstance({
@@ -1307,6 +1497,10 @@ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance
1307
1497
  };
1308
1498
  } })) ?? {};
1309
1499
  const fields = Object.entries(columns).reduce((acc, [key, column]) => {
1500
+ if (key.startsWith("__")) {
1501
+ console.warn(`Skipping column "${key}" on table "${tableSchema.tsName}": names starting with "__" are reserved by GraphQL introspection. Use a custom adjust() call if you really need to expose this column.`);
1502
+ return acc;
1503
+ }
1310
1504
  if (userAdjustments[key]) {
1311
1505
  const { params, creatorFunction, configObject } = configMap.get(userAdjustments[key]);
1312
1506
  if (typeof configObject.nullable !== "boolean") configObject.nullable = !column.notNull;
@@ -1507,7 +1701,8 @@ const createPubSubInstance = ({ subscriptions }) => {
1507
1701
  };
1508
1702
  //#endregion
1509
1703
  //#region lib/query.ts
1510
- const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer, orderArgImplementer, makePubSubInstance }) => {
1704
+ const createQueryImplementer = ({ db, schemaBuilder, search, logger: loggerConfig, whereArgImplementer, orderArgImplementer, makePubSubInstance }) => {
1705
+ const registeredQueryFieldNames = /* @__PURE__ */ new Set();
1511
1706
  return ({ table, readAction = "read", listAction = "read" }) => {
1512
1707
  const WhereArg = whereArgImplementer({ table });
1513
1708
  const OrderArg = orderArgImplementer({ table });
@@ -1517,6 +1712,12 @@ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer
1517
1712
  });
1518
1713
  const primaryKeyField = Object.values(tableSchema.primaryKey)[0];
1519
1714
  const { registerOnInstance } = makePubSubInstance({ table });
1715
+ const pluralName = pluralize.default.plural(table.toString());
1716
+ const singularName = pluralize.default.singular(table.toString());
1717
+ for (const name of [pluralName, singularName]) {
1718
+ if (registeredQueryFieldNames.has(name)) throw new RumbleError(`Duplicate query field "${name}": the table "${table.toString()}" generates a query field that collides with one already registered. Use unique table names (or refNames) to avoid plural/singular collisions (e.g. "post" and "posts").`);
1719
+ registeredQueryFieldNames.add(name);
1720
+ }
1520
1721
  return schemaBuilder.queryFields((t) => {
1521
1722
  const manyArgs = {
1522
1723
  where: t.arg({
@@ -1533,11 +1734,11 @@ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer
1533
1734
  };
1534
1735
  if (!search?.enabled) delete manyArgs.search;
1535
1736
  return {
1536
- [pluralize.default.plural(table.toString())]: t.drizzleField({
1737
+ [pluralName]: t.drizzleField({
1537
1738
  type: [table],
1538
1739
  nullable: false,
1539
1740
  smartSubscription: true,
1540
- description: `List all ${pluralize.default.plural(table.toString())}`,
1741
+ description: `List all ${pluralName}`,
1541
1742
  subscribe: (subscriptions, _root, _args, _ctx, _info) => {
1542
1743
  registerOnInstance({
1543
1744
  instance: subscriptions,
@@ -1565,17 +1766,21 @@ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer
1565
1766
  if (filter.columns) queryInstance.columns = filter.columns;
1566
1767
  if (search?.enabled && args.search && args.search.length > 0 && search?.cpu_operator_cost) return db.transaction(async (tx) => {
1567
1768
  if (isPostgresDB(tx)) await tx.execute(drizzle_orm.sql.raw(`SET cpu_operator_cost = ${search.cpu_operator_cost};`));
1568
- else console.info("Database dialect is not postgresql, cannot set cpu_operator_cost.");
1769
+ else {
1770
+ const msg = "Database dialect is not postgresql, cannot set cpu_operator_cost.";
1771
+ const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
1772
+ log ? log.info({}, msg) : console.info(msg);
1773
+ }
1569
1774
  return tx.query[table].findMany(queryInstance);
1570
1775
  });
1571
1776
  return db.query[table].findMany(queryInstance);
1572
1777
  }
1573
1778
  }),
1574
- [pluralize.default.singular(table.toString())]: t.drizzleField({
1779
+ [singularName]: t.drizzleField({
1575
1780
  type: table,
1576
1781
  nullable: false,
1577
1782
  smartSubscription: true,
1578
- description: `Get a single ${pluralize.default.singular(table.toString())} by ID`,
1783
+ description: `Get a single ${singularName} by ID`,
1579
1784
  args: { id: t.arg.id({ required: true }) },
1580
1785
  resolve: (query, _root, args, ctx, _info) => {
1581
1786
  deepSetProto(args);
@@ -1751,6 +1956,96 @@ function implementDefaultWhereInputArgs(schemaBuilder) {
1751
1956
  OR: t.field({ type: [JSONWhereInputArgument] }),
1752
1957
  NOT: t.field({ type: JSONWhereInputArgument })
1753
1958
  }) });
1959
+ const BigIntWhereInputArgument = schemaBuilder.inputRef("BigIntWhereInputArgument").implement({ fields: (t) => ({
1960
+ eq: t.field({
1961
+ type: "BigInt",
1962
+ required: false
1963
+ }),
1964
+ ne: t.field({
1965
+ type: "BigInt",
1966
+ required: false
1967
+ }),
1968
+ gt: t.field({
1969
+ type: "BigInt",
1970
+ required: false
1971
+ }),
1972
+ gte: t.field({
1973
+ type: "BigInt",
1974
+ required: false
1975
+ }),
1976
+ lt: t.field({
1977
+ type: "BigInt",
1978
+ required: false
1979
+ }),
1980
+ lte: t.field({
1981
+ type: "BigInt",
1982
+ required: false
1983
+ }),
1984
+ in: t.field({
1985
+ type: ["BigInt"],
1986
+ required: false
1987
+ }),
1988
+ notIn: t.field({
1989
+ type: ["BigInt"],
1990
+ required: false
1991
+ }),
1992
+ isNull: t.boolean(),
1993
+ isNotNull: t.boolean(),
1994
+ AND: t.field({ type: [BigIntWhereInputArgument] }),
1995
+ OR: t.field({ type: [BigIntWhereInputArgument] }),
1996
+ NOT: t.field({ type: BigIntWhereInputArgument })
1997
+ }) });
1998
+ const DateTimeWhereInputArgument = schemaBuilder.inputRef("DateTimeWhereInputArgument").implement({ fields: (t) => ({
1999
+ eq: t.field({
2000
+ type: "DateTime",
2001
+ required: false
2002
+ }),
2003
+ ne: t.field({
2004
+ type: "DateTime",
2005
+ required: false
2006
+ }),
2007
+ gt: t.field({
2008
+ type: "DateTime",
2009
+ required: false
2010
+ }),
2011
+ gte: t.field({
2012
+ type: "DateTime",
2013
+ required: false
2014
+ }),
2015
+ lt: t.field({
2016
+ type: "DateTime",
2017
+ required: false
2018
+ }),
2019
+ lte: t.field({
2020
+ type: "DateTime",
2021
+ required: false
2022
+ }),
2023
+ in: t.field({
2024
+ type: ["DateTime"],
2025
+ required: false
2026
+ }),
2027
+ notIn: t.field({
2028
+ type: ["DateTime"],
2029
+ required: false
2030
+ }),
2031
+ isNull: t.boolean(),
2032
+ isNotNull: t.boolean(),
2033
+ arrayOverlaps: t.field({
2034
+ type: ["DateTime"],
2035
+ required: false
2036
+ }),
2037
+ arrayContained: t.field({
2038
+ type: ["DateTime"],
2039
+ required: false
2040
+ }),
2041
+ arrayContains: t.field({
2042
+ type: ["DateTime"],
2043
+ required: false
2044
+ }),
2045
+ AND: t.field({ type: [DateTimeWhereInputArgument] }),
2046
+ OR: t.field({ type: [DateTimeWhereInputArgument] }),
2047
+ NOT: t.field({ type: DateTimeWhereInputArgument })
2048
+ }) });
1754
2049
  }
1755
2050
  //#endregion
1756
2051
  //#region lib/runtimeFiltersPlugin/filterTypes.ts
@@ -1761,9 +2056,11 @@ const applyFiltersKey = "applyFilters";
1761
2056
  var RuntimeFiltersPlugin = class extends _pothos_core.BasePlugin {
1762
2057
  tracer;
1763
2058
  tracerEnabled;
2059
+ logger;
1764
2060
  onTypeConfig(typeConfig) {
1765
2061
  this.tracer = this.builder.options.otel?.tracer;
1766
2062
  this.tracerEnabled = this.builder.options.otel?.enabled;
2063
+ this.logger = this.builder.options.logger?.enabled ? this.builder.options.logger.logger : void 0;
1767
2064
  return typeConfig;
1768
2065
  }
1769
2066
  wrapResolve(resolver, fieldConfig) {
@@ -1792,7 +2089,8 @@ var RuntimeFiltersPlugin = class extends _pothos_core.BasePlugin {
1792
2089
  }));
1793
2090
  let resolved, prefetchedFilters;
1794
2091
  if (this.tracer && this.tracerEnabled) {
1795
- const o = await this.tracer.startActiveSpan(`apply_filters`, async (span) => {
2092
+ const o = await this.tracer.startActiveSpan(`rumble.filter.resolve`, async (span) => {
2093
+ span.setAttribute("graphql.field.name", fieldConfig.name);
1796
2094
  try {
1797
2095
  return await Promise.all([resolver(parent, args, context, info), prefetchedFiltersPromises]);
1798
2096
  } finally {
@@ -1814,17 +2112,37 @@ var RuntimeFiltersPlugin = class extends _pothos_core.BasePlugin {
1814
2112
  return acc;
1815
2113
  }, /* @__PURE__ */ new Set()));
1816
2114
  span?.setAttribute("filters.allowed", allowed.length);
2115
+ this.logger?.debug({
2116
+ "graphql.field.name": fieldConfig.name,
2117
+ "filters.total": allFilters.length,
2118
+ "filters.allowed": allowed.length
2119
+ }, "runtime filters applied");
1817
2120
  if (Array.isArray(resolved)) return allowed;
1818
2121
  return allowed[0] ?? null;
1819
2122
  };
1820
- if (this.tracer && this.tracerEnabled) return this.tracer.startActiveSpan(`filter_${fieldConfig.name}`, async (span) => {
2123
+ if (this.tracer && this.tracerEnabled) return this.tracer.startActiveSpan(`rumble.filter.apply`, async (span) => {
2124
+ span.setAttribute("graphql.field.name", fieldConfig.name);
1821
2125
  try {
1822
2126
  return await runFilters(span);
2127
+ } catch (error) {
2128
+ this.logger?.error({
2129
+ "graphql.field.name": fieldConfig.name,
2130
+ err: error
2131
+ }, "runtime filter threw");
2132
+ throw error;
1823
2133
  } finally {
1824
2134
  span.end();
1825
2135
  }
1826
2136
  });
1827
- else return runFilters();
2137
+ else try {
2138
+ return await runFilters();
2139
+ } catch (error) {
2140
+ this.logger?.error({
2141
+ "graphql.field.name": fieldConfig.name,
2142
+ err: error
2143
+ }, "runtime filter threw");
2144
+ throw error;
2145
+ }
1828
2146
  };
1829
2147
  }
1830
2148
  };
@@ -1837,7 +2155,7 @@ function registerRuntimeFiltersPlugin() {
1837
2155
  }
1838
2156
  //#endregion
1839
2157
  //#region lib/schemaBuilder.ts
1840
- const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig, otel }) => {
2158
+ const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger }) => {
1841
2159
  const createSpan = otel?.enabled && otel.tracer ? (0, _pothos_tracing_opentelemetry.createOpenTelemetryWrapper)(otel.tracer, otel.options) : void 0;
1842
2160
  registerRuntimeFiltersPlugin();
1843
2161
  const schemaBuilder = new _pothos_core.default({
@@ -1865,18 +2183,40 @@ const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig,
1865
2183
  }) },
1866
2184
  defaultFieldNullability: false,
1867
2185
  tracing: {
1868
- default: otel?.enabled ? (config) => (0, _pothos_plugin_tracing.isRootField)(config) : () => false,
1869
- wrap: createSpan ? (resolver, options) => createSpan(resolver, options) : (resolver) => resolver
2186
+ default: otel?.enabled || logger?.enabled ? (config) => (0, _pothos_plugin_tracing.isRootField)(config) : () => false,
2187
+ wrap: (resolver, options, config) => {
2188
+ let r = createSpan ? createSpan(resolver, options) : resolver;
2189
+ if (logger?.enabled) {
2190
+ const log = logger.logger;
2191
+ r = (0, _pothos_plugin_tracing.wrapResolver)(r, (error, duration) => {
2192
+ if (error) log.error({
2193
+ "graphql.field.name": config.name,
2194
+ "graphql.parent.type": config.parentType,
2195
+ durationMs: duration,
2196
+ err: error
2197
+ }, "resolver failed");
2198
+ else log.debug({
2199
+ "graphql.field.name": config.name,
2200
+ "graphql.parent.type": config.parentType,
2201
+ durationMs: duration
2202
+ }, "resolver completed");
2203
+ });
2204
+ }
2205
+ return r;
2206
+ }
1870
2207
  },
1871
- otel
2208
+ otel,
2209
+ logger
1872
2210
  });
1873
2211
  schemaBuilder.addScalarType("JSON", graphql_scalars.JSONResolver);
1874
2212
  schemaBuilder.addScalarType("Date", graphql_scalars.DateResolver);
1875
2213
  schemaBuilder.addScalarType("DateTime", graphql_scalars.DateTimeISOResolver);
2214
+ schemaBuilder.addScalarType("BigInt", graphql_scalars.BigIntResolver);
2215
+ schemaBuilder.addScalarType("Bytes", graphql_scalars.ByteResolver);
1876
2216
  implementDefaultWhereInputArgs(schemaBuilder);
1877
- if (!disableDefaultObjects?.query) schemaBuilder.queryType({});
1878
- if (!disableDefaultObjects?.subscription) schemaBuilder.subscriptionType({});
1879
- if (!disableDefaultObjects?.mutation) schemaBuilder.mutationType({});
2217
+ schemaBuilder.queryType({});
2218
+ schemaBuilder.subscriptionType({});
2219
+ schemaBuilder.mutationType({});
1880
2220
  return { schemaBuilder };
1881
2221
  };
1882
2222
  //#endregion
@@ -1959,7 +2299,33 @@ export const r = rumble({
1959
2299
  whereArgImplementer: whereArg,
1960
2300
  makePubSubInstance
1961
2301
  });
1962
- const builtSchema = require_lazy.lazy(() => schemaBuilder.toSchema());
2302
+ const builtSchema = require_lazy.lazy(() => {
2303
+ let raw;
2304
+ try {
2305
+ raw = schemaBuilder.toSchema();
2306
+ } catch (e) {
2307
+ const unimplMatch = (e?.message ?? String(e)).match(/ObjectRef<(.+?)> has not been implemented/);
2308
+ if (unimplMatch) throw new RumbleError(`Could not build GraphQL schema: the table "${unimplMatch[1]}" is referenced in a relation but has not been registered. Call rumble.object({ table: "${unimplMatch[1]}" }) before building the schema.`);
2309
+ throw e;
2310
+ }
2311
+ const mutation = raw.getMutationType();
2312
+ const subscription = raw.getSubscriptionType();
2313
+ const emptyMutation = mutation && Object.keys(mutation.getFields()).length === 0;
2314
+ const emptySubscription = subscription && Object.keys(subscription.getFields()).length === 0;
2315
+ if (!emptyMutation && !emptySubscription) return raw;
2316
+ const config = raw.toConfig();
2317
+ const filteredTypes = config.types.filter((t) => {
2318
+ if (emptyMutation && t === mutation) return false;
2319
+ if (emptySubscription && t === subscription) return false;
2320
+ return true;
2321
+ });
2322
+ return new graphql.GraphQLSchema({
2323
+ ...config,
2324
+ mutation: emptyMutation ? void 0 : config.mutation,
2325
+ subscription: emptySubscription ? void 0 : config.subscription,
2326
+ types: filteredTypes
2327
+ });
2328
+ });
1963
2329
  const createYoga = (args) => {
1964
2330
  const enableApiDocs = args?.enableApiDocs ?? process?.env?.NODE_ENV === "development";
1965
2331
  return (0, graphql_yoga.createYoga)({
@@ -1970,26 +2336,60 @@ export const r = rumble({
1970
2336
  plugins: [
1971
2337
  ...args?.plugins ?? [],
1972
2338
  ...enableApiDocs ? [] : [(0, _graphql_yoga_plugin_disable_introspection.useDisableIntrospection)(), (0, _escape_tech_graphql_armor.EnvelopArmorPlugin)(args?.armorConfig)],
1973
- rumbleInput.otel?.enabled ? { onExecute: ({ setExecuteFn, executeFn }) => {
1974
- setExecuteFn((options) => rumbleInput.otel.tracer.startActiveSpan(_pothos_tracing_opentelemetry.SpanNames.EXECUTE, { attributes: {
1975
- [_pothos_tracing_opentelemetry.AttributeNames.OPERATION_NAME]: options.operationName ?? "anonymous",
1976
- [_pothos_tracing_opentelemetry.AttributeNames.SOURCE]: options.document
1977
- } }, async (span) => {
1978
- try {
2339
+ rumbleInput.otel?.enabled || rumbleInput.logger?.enabled ? { onExecute: ({ setExecuteFn, executeFn }) => {
2340
+ setExecuteFn(async (options) => {
2341
+ const log = rumbleInput.logger?.enabled ? rumbleInput.logger.logger : void 0;
2342
+ const operationName = options.operationName ?? "anonymous";
2343
+ const start = Date.now();
2344
+ log?.info({ "graphql.operation.name": operationName }, "graphql execute start");
2345
+ const run = async () => {
1979
2346
  const result = await executeFn(options);
1980
- if (result && "errors" in result && result.errors?.length) {
1981
- for (const error of result.errors) span.recordException(error);
2347
+ if (result && "errors" in result && result.errors?.length) log?.error({
2348
+ "graphql.operation.name": operationName,
2349
+ durationMs: Date.now() - start,
2350
+ errors: result.errors.map((e) => e.message)
2351
+ }, "graphql execute completed with errors");
2352
+ else log?.info({
2353
+ "graphql.operation.name": operationName,
2354
+ durationMs: Date.now() - start
2355
+ }, "graphql execute completed");
2356
+ return result;
2357
+ };
2358
+ if (rumbleInput.otel?.enabled) return rumbleInput.otel.tracer.startActiveSpan(_pothos_tracing_opentelemetry.SpanNames.EXECUTE, { attributes: {
2359
+ [_pothos_tracing_opentelemetry.AttributeNames.OPERATION_NAME]: operationName,
2360
+ [_pothos_tracing_opentelemetry.AttributeNames.SOURCE]: options.document
2361
+ } }, async (span) => {
2362
+ try {
2363
+ const result = await run();
2364
+ if (result && "errors" in result && result.errors?.length) {
2365
+ for (const error of result.errors) span.recordException(error);
2366
+ span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
2367
+ }
2368
+ return result;
2369
+ } catch (error) {
2370
+ if (error instanceof Error) span.recordException(error);
2371
+ log?.error({
2372
+ "graphql.operation.name": operationName,
2373
+ durationMs: Date.now() - start,
2374
+ err: error
2375
+ }, "graphql execute threw");
1982
2376
  span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
2377
+ throw error;
2378
+ } finally {
2379
+ span.end();
1983
2380
  }
1984
- return result;
2381
+ });
2382
+ try {
2383
+ return await run();
1985
2384
  } catch (error) {
1986
- if (error instanceof Error) span.recordException(error);
1987
- span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
2385
+ log?.error({
2386
+ "graphql.operation.name": operationName,
2387
+ durationMs: Date.now() - start,
2388
+ err: error
2389
+ }, "graphql execute threw");
1988
2390
  throw error;
1989
- } finally {
1990
- span.end();
1991
2391
  }
1992
- }));
2392
+ });
1993
2393
  } } : false
1994
2394
  ].filter(Boolean)
1995
2395
  });