@m1212e/rumble 0.16.16 → 0.16.17

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
@@ -3,6 +3,7 @@ import { t as generateFromSchema } from "./generate-PDRNfY6V.mjs";
3
3
  import { GraphQLError } from "graphql";
4
4
  import { EnvelopArmorPlugin } from "@escape.tech/graphql-armor";
5
5
  import { useDisableIntrospection } from "@graphql-yoga/plugin-disable-introspection";
6
+ import { AttributeNames, SpanNames, createOpenTelemetryWrapper } from "@pothos/tracing-opentelemetry";
6
7
  import { capitalize, cloneDeep, debounce, merge, toMerged } from "es-toolkit";
7
8
  import { createPubSub, createYoga } from "graphql-yoga";
8
9
  import { useSofa } from "sofa-api";
@@ -15,6 +16,7 @@ import { SQLiteTable } from "drizzle-orm/sqlite-core";
15
16
  import SchemaBuilder, { BasePlugin } from "@pothos/core";
16
17
  import DrizzlePlugin from "@pothos/plugin-drizzle";
17
18
  import SmartSubscriptionsPlugin, { subscribeOptionsFromIterator } from "@pothos/plugin-smart-subscriptions";
19
+ import TracingPlugin, { isRootField } from "@pothos/plugin-tracing";
18
20
  import { DateResolver, DateTimeISOResolver, JSONResolver } from "graphql-scalars";
19
21
 
20
22
  //#region lib/types/rumbleError.ts
@@ -342,7 +344,7 @@ Warning! No abilities have been registered for
342
344
  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.
343
345
  `);
344
346
  }, 1e3);
345
- const createAbilityBuilder = ({ db, actions, defaultLimit }) => {
347
+ const createAbilityBuilder = ({ db, actions, defaultLimit, otel }) => {
346
348
  let hasBeenBuilt = false;
347
349
  const createBuilderForTable = () => {
348
350
  const queryFilters = /* @__PURE__ */ new Map();
@@ -474,27 +476,49 @@ const createAbilityBuilder = ({ db, actions, defaultLimit }) => {
474
476
  }
475
477
  return { withContext: (userContext) => {
476
478
  return { filter: (action) => {
477
- const filters = queryFilters.get(action);
478
- if (filters === "unrestricted") return transformToResponse();
479
- if (!filters) {
480
- nothingRegisteredWarningLogger(tableName.toString(), action);
481
- return transformToResponse(blockEverythingFilter);
482
- }
483
- const dynamicResults = new Array(dynamicQueryFilters[action].length);
484
- let filtersReturned = 0;
485
- for (let i = 0; i < dynamicQueryFilters[action].length; i++) {
486
- const func = dynamicQueryFilters[action][i];
487
- const result = func(userContext);
488
- if (result === "allow") return transformToResponse();
489
- if (result === void 0) continue;
490
- dynamicResults[filtersReturned++] = result;
491
- }
492
- dynamicResults.length = filtersReturned;
493
- const allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
494
- if (allQueryFilters.length === 0) return transformToResponse(blockEverythingFilter);
495
- return transformToResponse(allQueryFilters.length === 1 ? allQueryFilters[0] : allQueryFilters.reduce((a, b) => {
496
- return mergeFilters(a, b);
497
- }, {}));
479
+ const assembleAbilities = (span) => {
480
+ const filters = queryFilters.get(action);
481
+ if (filters === "unrestricted") {
482
+ span?.setAttribute("abilities.status", "unrestricted");
483
+ return transformToResponse();
484
+ }
485
+ if (!filters) {
486
+ span?.setAttribute("abilities.status", "blocked_everything");
487
+ nothingRegisteredWarningLogger(tableName.toString(), action);
488
+ return transformToResponse(blockEverythingFilter);
489
+ }
490
+ const dynamicResults = new Array(dynamicQueryFilters[action].length);
491
+ let filtersReturned = 0;
492
+ for (let i = 0; i < dynamicQueryFilters[action].length; i++) {
493
+ const func = dynamicQueryFilters[action][i];
494
+ const result = func(userContext);
495
+ if (result === "allow") return transformToResponse();
496
+ if (result === void 0) continue;
497
+ dynamicResults[filtersReturned++] = result;
498
+ }
499
+ dynamicResults.length = filtersReturned;
500
+ span?.setAttribute("abilities.dynamic", dynamicResults.length);
501
+ span?.setAttribute("abilities.static", simpleQueryFilters[action].length);
502
+ const allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
503
+ span?.setAttribute("abilities.total", allQueryFilters.length);
504
+ if (allQueryFilters.length === 0) {
505
+ span?.setAttribute("abilities.status", "blocked_everything");
506
+ return transformToResponse(blockEverythingFilter);
507
+ }
508
+ const mergedFilters = allQueryFilters.length === 1 ? allQueryFilters[0] : allQueryFilters.reduce((a, b) => {
509
+ return mergeFilters(a, b);
510
+ }, {});
511
+ span?.setAttribute("abilities.status", "applied");
512
+ return transformToResponse(mergedFilters);
513
+ };
514
+ if (otel?.tracer) return otel.tracer.startActiveSpan(`prepare_query_abilities_${action}`, (span) => {
515
+ try {
516
+ return assembleAbilities(span);
517
+ } finally {
518
+ span.end();
519
+ }
520
+ });
521
+ else return assembleAbilities();
498
522
  } };
499
523
  } };
500
524
  };
@@ -1399,22 +1423,43 @@ const applyFilters = async ({ filters, entities, context }) => {
1399
1423
  //#region lib/runtimeFiltersPlugin/runtimeFiltersPlugin.ts
1400
1424
  const applyFiltersKey = "applyFilters";
1401
1425
  var RuntimeFiltersPlugin = class extends BasePlugin {
1426
+ tracer;
1427
+ onTypeConfig(typeConfig) {
1428
+ this.tracer = this.builder.options.otelTracer;
1429
+ return typeConfig;
1430
+ }
1402
1431
  wrapResolve(resolver, fieldConfig) {
1403
1432
  return async (parent, args, context, info) => {
1404
- let filters;
1405
- const fieldType = fieldConfig?.type;
1406
- if (fieldType.kind === "List") filters = fieldType.type?.ref.currentConfig.pothosOptions[applyFiltersKey];
1407
- else if (fieldType.kind === "Object") filters = fieldType.ref.currentConfig.pothosOptions[applyFiltersKey];
1408
- if (!filters || !Array.isArray(filters) || filters.length === 0) return resolver(parent, args, context, info);
1409
- const resolved = await resolver(parent, args, context, info);
1410
- const allResolvedValues = Array.isArray(resolved) ? resolved : [resolved];
1411
- const allowed = await applyFilters({
1412
- filters: Array.isArray(filters) ? filters : [filters],
1413
- entities: allResolvedValues,
1414
- context
1433
+ const runFilters = async (span) => {
1434
+ let filters;
1435
+ const fieldType = fieldConfig?.type;
1436
+ if (fieldType.kind === "List") filters = fieldType.type?.ref.currentConfig.pothosOptions[applyFiltersKey];
1437
+ else if (fieldType.kind === "Object") filters = fieldType.ref.currentConfig.pothosOptions[applyFiltersKey];
1438
+ if (!filters || !Array.isArray(filters) || filters.length === 0) {
1439
+ span?.setAttribute("filters.status", "unrestricted");
1440
+ return resolver(parent, args, context, info);
1441
+ }
1442
+ const resolved = await resolver(parent, args, context, info);
1443
+ const allResolvedValues = Array.isArray(resolved) ? resolved : [resolved];
1444
+ const allFilters = Array.isArray(filters) ? filters : [filters];
1445
+ span?.setAttribute("filters.total", allFilters.length);
1446
+ const allowed = await applyFilters({
1447
+ filters: allFilters,
1448
+ entities: allResolvedValues,
1449
+ context
1450
+ });
1451
+ span?.setAttribute("filters.allowed", allowed.length);
1452
+ if (Array.isArray(resolved)) return allowed;
1453
+ return allowed[0] ?? null;
1454
+ };
1455
+ if (this.tracer) return this.tracer.startActiveSpan(`apply_filters_${fieldConfig.name}`, async (span) => {
1456
+ try {
1457
+ return await runFilters();
1458
+ } finally {
1459
+ span.end();
1460
+ }
1415
1461
  });
1416
- if (Array.isArray(resolved)) return allowed;
1417
- return allowed[0] ?? null;
1462
+ else return runFilters();
1418
1463
  };
1419
1464
  }
1420
1465
  };
@@ -1428,7 +1473,8 @@ function registerRuntimeFiltersPlugin() {
1428
1473
 
1429
1474
  //#endregion
1430
1475
  //#region lib/schemaBuilder.ts
1431
- const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig }) => {
1476
+ const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig, otel }) => {
1477
+ const createSpan = otel?.tracer ? createOpenTelemetryWrapper(otel.tracer, otel.options) : void 0;
1432
1478
  registerRuntimeFiltersPlugin();
1433
1479
  const schemaBuilder = new SchemaBuilder({
1434
1480
  ...pothosConfig,
@@ -1436,6 +1482,7 @@ const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig }
1436
1482
  pluginName,
1437
1483
  DrizzlePlugin,
1438
1484
  SmartSubscriptionsPlugin,
1485
+ TracingPlugin,
1439
1486
  ...pothosConfig?.plugins ?? []
1440
1487
  ],
1441
1488
  drizzle: {
@@ -1451,7 +1498,12 @@ const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig }
1451
1498
  smartSubscriptions: { ...subscribeOptionsFromIterator((name, _context) => {
1452
1499
  return pubsub.subscribe(name);
1453
1500
  }) },
1454
- defaultFieldNullability: false
1501
+ defaultFieldNullability: false,
1502
+ tracing: {
1503
+ default: otel?.tracer ? (config) => isRootField(config) : () => false,
1504
+ wrap: createSpan ? (resolver, options) => createSpan(resolver, options) : (resolver) => resolver
1505
+ },
1506
+ otelTracer: otel?.tracer
1455
1507
  });
1456
1508
  schemaBuilder.addScalarType("JSON", JSONResolver);
1457
1509
  schemaBuilder.addScalarType("Date", DateResolver);
@@ -1487,7 +1539,7 @@ export const db = drizzle(
1487
1539
  "postgres://postgres:postgres@localhost:5432/postgres",
1488
1540
  {
1489
1541
  relations, // <--- add this line
1490
- schema,
1542
+ schema,
1491
1543
  },
1492
1544
  );
1493
1545
 
@@ -1550,7 +1602,22 @@ export const db = drizzle(
1550
1602
  return createYoga({
1551
1603
  ...args,
1552
1604
  graphiql: enableApiDocs,
1553
- plugins: [...args?.plugins ?? [], ...enableApiDocs ? [] : [useDisableIntrospection(), EnvelopArmorPlugin()]].filter(Boolean),
1605
+ plugins: [
1606
+ ...args?.plugins ?? [],
1607
+ ...enableApiDocs ? [] : [useDisableIntrospection(), EnvelopArmorPlugin()],
1608
+ rumbleInput.otel?.tracer ? { onExecute: ({ setExecuteFn, executeFn }) => {
1609
+ setExecuteFn((options) => rumbleInput.otel.tracer.startActiveSpan(SpanNames.EXECUTE, { attributes: { [AttributeNames.OPERATION_NAME]: options.operationName ?? "anonymous" } }, async (span) => {
1610
+ try {
1611
+ return await executeFn(options);
1612
+ } catch (error) {
1613
+ span.recordException(error);
1614
+ throw error;
1615
+ } finally {
1616
+ span.end();
1617
+ }
1618
+ }));
1619
+ } } : false
1620
+ ].filter(Boolean),
1554
1621
  schema: builtSchema(),
1555
1622
  context
1556
1623
  });