@m1212e/rumble 0.23.2 → 0.23.5

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.mjs CHANGED
@@ -1,10 +1,9 @@
1
1
  import { t as lazy } from "./lazy-C7IyM4M4.mjs";
2
2
  import { t as generateFromSchema } from "./generate-0QFVc3Sg.mjs";
3
- import { GraphQLError, GraphQLSchema } from "graphql";
3
+ import { GraphQLError, GraphQLSchema, execute as defaultExecute, subscribe as defaultSubscribe } from "graphql";
4
4
  import { EnvelopArmorPlugin } from "@escape.tech/graphql-armor";
5
5
  import { useDisableIntrospection } from "@graphql-yoga/plugin-disable-introspection";
6
6
  import { SpanStatusCode, trace } from "@opentelemetry/api";
7
- import { AttributeNames, SpanNames, createOpenTelemetryWrapper } from "@pothos/tracing-opentelemetry";
8
7
  import { capitalize, cloneDeep, debounce, merge, toMerged } from "es-toolkit";
9
8
  import { createPubSub, createYoga } from "graphql-yoga";
10
9
  import { One, count, getTableColumns, getTableName, is, isTable, relationsFilterToSQL, sql } from "drizzle-orm";
@@ -12,6 +11,7 @@ import { toCamelCase } from "drizzle-orm/casing";
12
11
  import { PgColumn, PgEnumColumn, PgEnumObjectColumn, PgTable, isPgEnum } from "drizzle-orm/pg-core";
13
12
  import { MySqlEnumColumn, MySqlEnumObjectColumn, MySqlTable } from "drizzle-orm/mysql-core";
14
13
  import pluralize from "pluralize";
14
+ import { AttributeNames, SpanNames, createOpenTelemetryWrapper } from "@pothos/tracing-opentelemetry";
15
15
  import { SQLiteTable } from "drizzle-orm/sqlite-core";
16
16
  import SchemaBuilder, { BasePlugin } from "@pothos/core";
17
17
  import DrizzlePlugin from "@pothos/plugin-drizzle";
@@ -180,12 +180,25 @@ function mapNullFieldsToUndefined(obj) {
180
180
  }
181
181
  //#endregion
182
182
  //#region package.json
183
- var version = "0.23.2";
183
+ var version = "0.23.5";
184
184
  //#endregion
185
185
  //#region lib/helpers/mergeFilters.ts
186
+ const EmptyFilter$1 = Symbol.for("drizzle:EmptyFilter");
187
+ /**
188
+ * Normalizes drizzle-orm's `EmptyFilter` sentinel to `undefined` so it's
189
+ * treated as "no filter" rather than a real filter value to combine with
190
+ * AND/OR — otherwise merging an unrestricted (EmptyFilter) base filter with
191
+ * a real `where` would needlessly wrap it as `{ AND: [EmptyFilter, where] }`
192
+ * instead of just `where`.
193
+ */
194
+ function realWhere(where) {
195
+ return where === EmptyFilter$1 ? void 0 : where;
196
+ }
186
197
  function mergeFilters(filterA, filterB, mode = "AND") {
198
+ const filterAWhere = realWhere(filterA?.where);
199
+ const filterBWhere = realWhere(filterB?.where);
187
200
  return {
188
- where: filterA?.where && filterB?.where ? mode === "OR" ? { OR: [filterA.where, filterB.where] } : { AND: [filterA.where, filterB.where] } : filterA?.where ?? filterB?.where,
201
+ where: filterAWhere && filterBWhere ? mode === "OR" ? { OR: [filterAWhere, filterBWhere] } : { AND: [filterAWhere, filterBWhere] } : filterAWhere ?? filterBWhere,
189
202
  columns: filterA?.columns || filterB?.columns ? new Set([Object.entries(filterA?.columns ?? {}), Object.entries(filterB?.columns ?? {})].flat().filter(([, v]) => v === true).map(([k]) => k)).entries().reduce((acc, [key]) => {
190
203
  acc[key] = true;
191
204
  return acc;
@@ -412,6 +425,21 @@ function tableHelper({ db, table }) {
412
425
  }
413
426
  //#endregion
414
427
  //#region lib/abilityBuilder.ts
428
+ /**
429
+ * drizzle-orm's sentinel value for "this filter field is deliberately
430
+ * skipped", as opposed to `undefined`, which some drizzle-orm versions
431
+ * reject when it ends up as an explicit (present but `undefined`) `where`
432
+ * property nested inside a relational `with` query.
433
+ *
434
+ * drizzle-orm defines this via `Symbol.for("drizzle:EmptyFilter")` — a
435
+ * globally registered symbol — so recreating it the same way here yields
436
+ * the exact same value drizzle-orm's relational query engine checks for,
437
+ * without importing it directly: not every drizzle-orm version rumble
438
+ * supports exports it as a named export, and older versions that don't know
439
+ * about it at all still handle it safely (`Object.entries` on a symbol
440
+ * yields no entries, i.e. "no filter").
441
+ */
442
+ const EmptyFilter = Symbol.for("drizzle:EmptyFilter");
415
443
  function isDynamicQueryFilter(filter) {
416
444
  return typeof filter === "function" && filter.constructor.name !== "AsyncFunction";
417
445
  }
@@ -580,7 +608,7 @@ by: (explicitFilter) => {
580
608
  */
581
609
  single: {
582
610
  extras: filters?.extras,
583
- where: filters?.where,
611
+ where: filters?.where ?? EmptyFilter,
584
612
  columns: filters?.columns
585
613
  },
586
614
  /**
@@ -588,7 +616,7 @@ by: (explicitFilter) => {
588
616
  */
589
617
  many: {
590
618
  extras: filters?.extras,
591
- where: filters?.where,
619
+ where: filters?.where ?? EmptyFilter,
592
620
  columns: filters?.columns,
593
621
  get limit() {
594
622
  return limit();
@@ -634,14 +662,14 @@ by: (explicitFilter) => {
634
662
  */
635
663
  single: {
636
664
  extras: filters?.extras,
637
- where: filters?.where
665
+ where: filters?.where ?? EmptyFilter
638
666
  },
639
667
  /**
640
668
  * For find many calls
641
669
  */
642
670
  many: {
643
671
  extras: filters?.extras,
644
- where: filters?.where,
672
+ where: filters?.where ?? EmptyFilter,
645
673
  get limit() {
646
674
  return limit();
647
675
  }
@@ -1272,6 +1300,165 @@ const sofaOpenAPIWebhookDocs = {
1272
1300
  } }
1273
1301
  };
1274
1302
  //#endregion
1303
+ //#region lib/helpers/tracing.ts
1304
+ function isAsyncIterable(value) {
1305
+ return typeof value === "object" && value !== null && Symbol.asyncIterator in value;
1306
+ }
1307
+ async function* wrapSubscriptionIterator(iterator, log, operationName) {
1308
+ let eventCount = 0;
1309
+ try {
1310
+ for await (const event of iterator) {
1311
+ eventCount++;
1312
+ if (event.errors?.length) log.error({
1313
+ "graphql.operation.name": operationName,
1314
+ eventCount,
1315
+ errors: event.errors.map((e) => e.message)
1316
+ }, "graphql subscription event error");
1317
+ yield event;
1318
+ }
1319
+ log.info({
1320
+ "graphql.operation.name": operationName,
1321
+ eventCount
1322
+ }, "graphql subscription completed");
1323
+ } catch (error) {
1324
+ log.error({
1325
+ "graphql.operation.name": operationName,
1326
+ eventCount,
1327
+ err: error
1328
+ }, "graphql subscription threw");
1329
+ throw error;
1330
+ }
1331
+ }
1332
+ function buildTracedExecute(executeFn, rumbleInput) {
1333
+ return async (options) => {
1334
+ let log = rumbleInput.logger?.enabled ? rumbleInput.logger.logger : void 0;
1335
+ const operationName = options.operationName ?? "anonymous";
1336
+ const start = Date.now();
1337
+ log?.info({ "graphql.operation.name": operationName }, "graphql execute start");
1338
+ const run = async () => {
1339
+ const result = await executeFn(options);
1340
+ if (result && "errors" in result && result.errors?.length) log?.error({
1341
+ "graphql.operation.name": operationName,
1342
+ durationMs: Date.now() - start,
1343
+ errors: result.errors.map((e) => e.message)
1344
+ }, "graphql execute completed with errors");
1345
+ else log?.info({
1346
+ "graphql.operation.name": operationName,
1347
+ durationMs: Date.now() - start
1348
+ }, "graphql execute completed");
1349
+ return result;
1350
+ };
1351
+ if (rumbleInput.otel?.enabled) return rumbleInput.otel.tracer.startActiveSpan(SpanNames.EXECUTE, { attributes: {
1352
+ [AttributeNames.OPERATION_NAME]: operationName,
1353
+ [AttributeNames.SOURCE]: options.document
1354
+ } }, async (span) => {
1355
+ if (log && rumbleInput.logger?.injectTraceId !== false) {
1356
+ const { traceId, spanId } = span.spanContext();
1357
+ log = log.child({
1358
+ traceId,
1359
+ spanId
1360
+ });
1361
+ }
1362
+ try {
1363
+ const result = await run();
1364
+ if (result && "errors" in result && result.errors?.length) {
1365
+ for (const error of result.errors) span.recordException(error);
1366
+ span.setStatus({ code: SpanStatusCode.ERROR });
1367
+ }
1368
+ return result;
1369
+ } catch (error) {
1370
+ if (error instanceof Error) span.recordException(error);
1371
+ log?.error({
1372
+ "graphql.operation.name": operationName,
1373
+ durationMs: Date.now() - start,
1374
+ err: error
1375
+ }, "graphql execute threw");
1376
+ span.setStatus({ code: SpanStatusCode.ERROR });
1377
+ throw error;
1378
+ } finally {
1379
+ span.end();
1380
+ }
1381
+ });
1382
+ try {
1383
+ return await run();
1384
+ } catch (error) {
1385
+ log?.error({
1386
+ "graphql.operation.name": operationName,
1387
+ durationMs: Date.now() - start,
1388
+ err: error
1389
+ }, "graphql execute threw");
1390
+ throw error;
1391
+ }
1392
+ };
1393
+ }
1394
+ function buildTracedSubscribe(subscribeFn, rumbleInput) {
1395
+ return async (options) => {
1396
+ let log = rumbleInput.logger?.enabled ? rumbleInput.logger.logger : void 0;
1397
+ const operationName = options.operationName ?? "anonymous";
1398
+ const start = Date.now();
1399
+ const doSubscribe = async () => {
1400
+ log?.info({ "graphql.operation.name": operationName }, "graphql subscribe start");
1401
+ const result = await subscribeFn(options);
1402
+ if (!isAsyncIterable(result)) {
1403
+ const execResult = result;
1404
+ if (execResult.errors?.length) log?.error({
1405
+ "graphql.operation.name": operationName,
1406
+ durationMs: Date.now() - start,
1407
+ errors: execResult.errors.map((e) => e.message)
1408
+ }, "graphql subscribe completed with errors");
1409
+ return execResult;
1410
+ }
1411
+ log?.info({
1412
+ "graphql.operation.name": operationName,
1413
+ durationMs: Date.now() - start
1414
+ }, "graphql subscription established");
1415
+ if (log) return wrapSubscriptionIterator(result, log, operationName);
1416
+ return result;
1417
+ };
1418
+ if (rumbleInput.otel?.enabled) return rumbleInput.otel.tracer.startActiveSpan("graphql.subscribe", async (span) => {
1419
+ if (log && rumbleInput.logger?.injectTraceId !== false) {
1420
+ const { traceId, spanId } = span.spanContext();
1421
+ log = log.child({
1422
+ traceId,
1423
+ spanId
1424
+ });
1425
+ }
1426
+ try {
1427
+ const result = await doSubscribe();
1428
+ if (!isAsyncIterable(result)) {
1429
+ const execResult = result;
1430
+ if (execResult.errors?.length) {
1431
+ for (const error of execResult.errors) span.recordException(error);
1432
+ span.setStatus({ code: SpanStatusCode.ERROR });
1433
+ }
1434
+ }
1435
+ return result;
1436
+ } catch (error) {
1437
+ if (error instanceof Error) span.recordException(error);
1438
+ log?.error({
1439
+ "graphql.operation.name": operationName,
1440
+ durationMs: Date.now() - start,
1441
+ err: error
1442
+ }, "graphql subscribe threw");
1443
+ span.setStatus({ code: SpanStatusCode.ERROR });
1444
+ throw error;
1445
+ } finally {
1446
+ span.end();
1447
+ }
1448
+ });
1449
+ try {
1450
+ return await doSubscribe();
1451
+ } catch (error) {
1452
+ log?.error({
1453
+ "graphql.operation.name": operationName,
1454
+ durationMs: Date.now() - start,
1455
+ err: error
1456
+ }, "graphql subscribe threw");
1457
+ throw error;
1458
+ }
1459
+ };
1460
+ }
1461
+ //#endregion
1275
1462
  //#region lib/helpers/sqlTypes/mapDrizzleTypeToGraphQlType.ts
1276
1463
  function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, nullable, isArray }) {
1277
1464
  const gqlType = mapSQLTypeToGraphQLType({
@@ -2309,59 +2496,7 @@ export const r = rumble({
2309
2496
  ...args?.plugins ?? [],
2310
2497
  ...enableApiDocs ? [] : [useDisableIntrospection(), EnvelopArmorPlugin(args?.armorConfig)],
2311
2498
  rumbleInput.otel?.enabled || rumbleInput.logger?.enabled ? { onExecute: ({ setExecuteFn, executeFn }) => {
2312
- setExecuteFn(async (options) => {
2313
- const log = rumbleInput.logger?.enabled ? rumbleInput.logger.logger : void 0;
2314
- const operationName = options.operationName ?? "anonymous";
2315
- const start = Date.now();
2316
- log?.info({ "graphql.operation.name": operationName }, "graphql execute start");
2317
- const run = async () => {
2318
- const result = await executeFn(options);
2319
- if (result && "errors" in result && result.errors?.length) log?.error({
2320
- "graphql.operation.name": operationName,
2321
- durationMs: Date.now() - start,
2322
- errors: result.errors.map((e) => e.message)
2323
- }, "graphql execute completed with errors");
2324
- else log?.info({
2325
- "graphql.operation.name": operationName,
2326
- durationMs: Date.now() - start
2327
- }, "graphql execute completed");
2328
- return result;
2329
- };
2330
- if (rumbleInput.otel?.enabled) return rumbleInput.otel.tracer.startActiveSpan(SpanNames.EXECUTE, { attributes: {
2331
- [AttributeNames.OPERATION_NAME]: operationName,
2332
- [AttributeNames.SOURCE]: options.document
2333
- } }, async (span) => {
2334
- try {
2335
- const result = await run();
2336
- if (result && "errors" in result && result.errors?.length) {
2337
- for (const error of result.errors) span.recordException(error);
2338
- span.setStatus({ code: SpanStatusCode.ERROR });
2339
- }
2340
- return result;
2341
- } catch (error) {
2342
- if (error instanceof Error) span.recordException(error);
2343
- log?.error({
2344
- "graphql.operation.name": operationName,
2345
- durationMs: Date.now() - start,
2346
- err: error
2347
- }, "graphql execute threw");
2348
- span.setStatus({ code: SpanStatusCode.ERROR });
2349
- throw error;
2350
- } finally {
2351
- span.end();
2352
- }
2353
- });
2354
- try {
2355
- return await run();
2356
- } catch (error) {
2357
- log?.error({
2358
- "graphql.operation.name": operationName,
2359
- durationMs: Date.now() - start,
2360
- err: error
2361
- }, "graphql execute threw");
2362
- throw error;
2363
- }
2364
- });
2499
+ setExecuteFn(buildTracedExecute(executeFn, rumbleInput));
2365
2500
  } } : false
2366
2501
  ].filter(Boolean)
2367
2502
  });
@@ -2382,6 +2517,8 @@ export const r = rumble({
2382
2517
  ...args,
2383
2518
  schema: builtSchema(),
2384
2519
  context,
2520
+ execute: buildTracedExecute(args.execute ?? defaultExecute, rumbleInput),
2521
+ subscribe: buildTracedSubscribe(args.subscribe ?? defaultSubscribe, rumbleInput),
2385
2522
  errorHandler(errors) {
2386
2523
  const span = trace.getActiveSpan();
2387
2524
  for (const error of errors) span?.recordException(error);
@@ -2394,7 +2531,9 @@ export const r = rumble({
2394
2531
  return implementation({
2395
2532
  ...args,
2396
2533
  schema: builtSchema(),
2397
- context
2534
+ context,
2535
+ execute: buildTracedExecute(args.execute ?? defaultExecute, rumbleInput),
2536
+ subscribe: buildTracedSubscribe(args.subscribe ?? defaultSubscribe, rumbleInput)
2398
2537
  }, ...rest);
2399
2538
  };
2400
2539
  return {