@m1212e/rumble 0.27.0 → 0.27.4

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
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region \0rolldown/runtime.js
2
+ // \0rolldown/runtime.js
3
3
  var __create = Object.create;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -20,23 +20,22 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
20
20
  value: mod,
21
21
  enumerable: true
22
22
  }) : target, mod));
23
- //#endregion
24
- const require_generate = require("./generate-D0CKBths.cjs");
23
+ const require_generate = require("./generate-Btr_u0Lr.cjs");
25
24
  let drizzle_orm = require("drizzle-orm");
26
25
  let graphql = require("graphql");
27
26
  let es_toolkit = require("es-toolkit");
28
27
  let _opentelemetry_api = require("@opentelemetry/api");
29
28
  let _opentelemetry_semantic_conventions_incubating = require("@opentelemetry/semantic-conventions/incubating");
30
29
  let _pothos_tracing_opentelemetry = require("@pothos/tracing-opentelemetry");
30
+ let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
31
+ let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
32
+ let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
31
33
  let _escape_tech_graphql_armor = require("@escape.tech/graphql-armor");
32
34
  let _graphql_yoga_plugin_disable_introspection = require("@graphql-yoga/plugin-disable-introspection");
33
35
  let graphql_yoga = require("graphql-yoga");
34
36
  let drizzle_orm_casing = require("drizzle-orm/casing");
35
- let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
36
- let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
37
37
  let pluralize = require("pluralize");
38
38
  pluralize = __toESM(pluralize, 1);
39
- let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
40
39
  let _pothos_core = require("@pothos/core");
41
40
  _pothos_core = __toESM(_pothos_core, 1);
42
41
  let _pothos_plugin_drizzle = require("@pothos/plugin-drizzle");
@@ -52,7 +51,7 @@ let dataloader = require("dataloader");
52
51
  dataloader = __toESM(dataloader, 1);
53
52
  let zod = require("zod");
54
53
  let libphonenumber_js = require("libphonenumber-js");
55
- //#region lib/types/rumbleError.ts
54
+ // lib/types/rumbleError.ts
56
55
  /**
57
56
  * An error that gets raised by rumble whenever something does not go according to plan.
58
57
  * Mostly internals, configuration errors or other unexpected things.
@@ -69,8 +68,7 @@ var RumbleError = class extends Error {
69
68
  * E.g. the assert helpers issue these.
70
69
  */
71
70
  var RumbleErrorSafe = class extends graphql.GraphQLError {};
72
- //#endregion
73
- //#region lib/helpers/tableHelpers.ts
71
+ // lib/helpers/tableHelpers.ts
74
72
  const indexCache = /* @__PURE__ */ new WeakMap();
75
73
  function buildIndex(db) {
76
74
  const byTsName = /* @__PURE__ */ new Map();
@@ -139,8 +137,7 @@ function tableHelper({ db, table }) {
139
137
  foundRelation: entry.foundRelation
140
138
  };
141
139
  }
142
- //#endregion
143
- //#region lib/helpers/mergeFilters.ts
140
+ // lib/helpers/mergeFilters.ts
144
141
  const EmptyFilter$1 = Symbol.for("drizzle:EmptyFilter");
145
142
  /**
146
143
  * Normalizes drizzle-orm's `EmptyFilter` sentinel to `undefined` so it's
@@ -184,8 +181,7 @@ function mergeFilters(filterA, filterB, mode = "AND") {
184
181
  with: mergeRecords(filterA?.with, filterB?.with)
185
182
  };
186
183
  }
187
- //#endregion
188
- //#region lib/helpers/sanitizeFilterValue.ts
184
+ // lib/helpers/sanitizeFilterValue.ts
189
185
  const EmptyFilter = Symbol.for("drizzle:EmptyFilter");
190
186
  function isPlainObject(value) {
191
187
  if (typeof value !== "object" || value === null) return false;
@@ -198,17 +194,16 @@ function sanitizeFilterValue(value) {
198
194
  if (isPlainObject(value)) return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, sanitizeFilterValue(entry)]));
199
195
  return value;
200
196
  }
201
- //#endregion
202
- //#region lib/abilityBuilder/keys.ts
197
+ // lib/abilityBuilder/keys.ts
203
198
  const ACTION_FLAG_KEY = "__rumble_action";
204
199
  const columnFlagKey = (group) => `__rumble_columns_${group}`;
205
200
  const columnMaskKey = Symbol.for("rumble:columnMask");
206
201
  const columnVisibilityKey = Symbol.for("rumble:columnVisibility");
207
202
  const guardInputKey = Symbol.for("rumble:guardInput");
208
203
  const resolvedFilterKey = Symbol.for("rumble:resolvedFilter");
204
+ const refreshAbilitiesKey = Symbol.for("rumble:refreshAbilities");
209
205
  const requestStateKey = Symbol.for("rumble:requestState");
210
- //#endregion
211
- //#region lib/abilityBuilder/requestState.ts
206
+ // lib/abilityBuilder/requestState.ts
212
207
  function createRequestState() {
213
208
  return {
214
209
  masked: /* @__PURE__ */ new WeakMap(),
@@ -223,16 +218,7 @@ function attachRequestState(abilities, state) {
223
218
  function requestStateOf(abilities) {
224
219
  return abilities?.[requestStateKey];
225
220
  }
226
- //#endregion
227
- //#region lib/abilityBuilder/columnMask.ts
228
- function referencesRelation(filter, relations) {
229
- if (!filter || typeof filter !== "object") return false;
230
- return Object.entries(filter).some(([key, value]) => {
231
- if (key === "AND" || key === "OR") return value.some((v) => referencesRelation(v, relations));
232
- if (key === "NOT") return referencesRelation(value, relations);
233
- return key in relations;
234
- });
235
- }
221
+ // lib/abilityBuilder/columnMask.ts
236
222
  function selectedColumns(columns, allColumns) {
237
223
  if (!columns) return new Set(allColumns);
238
224
  const entries = Object.entries(columns).filter(([, v]) => v !== void 0);
@@ -291,12 +277,7 @@ function conditionalGroups(rules, guaranteed) {
291
277
  function createColumnAccessResolver({ db, actions }, tableSchema) {
292
278
  const allColumnNames = Object.keys(tableSchema.columns);
293
279
  const primaryKeyNames = Object.keys(tableSchema.primaryKey);
294
- const primaryKeyName = primaryKeyNames[0];
295
280
  const toSQL = (table, wheres) => (0, drizzle_orm.or)(...wheres.map((where) => (0, drizzle_orm.relationsFilterToSQL)(table, sanitizeFilterValue(where), tableSchema.relations, db._.relations)));
296
- const uncorrelated = (table, wheres, group) => {
297
- const inner = (0, drizzle_orm.aliasedTable)(tableSchema.table, `rumble_columns_${group}`);
298
- return drizzle_orm.sql`exists ${db.select({ pk: inner[primaryKeyName] }).from(inner).where(drizzle_orm.sql`${inner[primaryKeyName]} = ${table[primaryKeyName]} and (${toSQL(inner, wheres)})`)}`;
299
- };
300
281
  return function resolveColumnAccess(action, filters) {
301
282
  const rules = filters.map((f) => ({
302
283
  where: realWhere(f?.where),
@@ -327,11 +308,7 @@ function createColumnAccessResolver({ db, actions }, tableSchema) {
327
308
  const actionIndex = drizzle_orm.sql.raw(String(actions.indexOf(action)));
328
309
  const extras = { [ACTION_FLAG_KEY]: () => drizzle_orm.sql`${actionIndex}` };
329
310
  groups.forEach(({ wheres }, group) => {
330
- const relational = wheres.some((w) => referencesRelation(w, tableSchema.relations));
331
- extras[mask.flags[group]] = (table) => {
332
- const condition = relational ? uncorrelated(table, wheres, group) : toSQL(table, wheres);
333
- return drizzle_orm.sql`case when ${condition} then 1 else 0 end`;
334
- };
311
+ extras[mask.flags[group]] = (table) => drizzle_orm.sql`case when ${toSQL(table, wheres)} then 1 else 0 end`;
335
312
  });
336
313
  return {
337
314
  columns,
@@ -414,8 +391,7 @@ function createColumnMasker({ actions }, tables) {
414
391
  columnsHidden
415
392
  };
416
393
  }
417
- //#endregion
418
- //#region lib/helpers/sqlTypes/types.ts
394
+ // lib/helpers/sqlTypes/types.ts
419
395
  /**
420
396
  * Strips parameter suffixes like `(256)`, `(10,2)` and timezone modifiers
421
397
  * (`with time zone`, `without time zone`) from raw getSQLType() output so
@@ -522,8 +498,7 @@ function isBytesSQLTypeString(sqlType) {
522
498
  return bytesSQLTypeStrings.includes(sqlType);
523
499
  }
524
500
  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})`);
525
- //#endregion
526
- //#region lib/helpers/sqlTypes/distinctValuesFromSQLType.ts
501
+ // lib/helpers/sqlTypes/distinctValuesFromSQLType.ts
527
502
  function createDistinctValuesFromSQLType(sqlType) {
528
503
  if (isIntLikeSQLTypeString(sqlType)) return {
529
504
  value1: 1,
@@ -559,8 +534,7 @@ function createDistinctValuesFromSQLType(sqlType) {
559
534
  };
560
535
  throw UnknownTypeRumbleError(sqlType, "Distinct");
561
536
  }
562
- //#endregion
563
- //#region lib/helpers/telemetry.ts
537
+ // lib/helpers/telemetry.ts
564
538
  const SPAN_EXECUTE = _pothos_tracing_opentelemetry.SpanNames.EXECUTE;
565
539
  /** @pothos/tracing-opentelemetry's SpanNames enum has no subscribe member. */
566
540
  const SPAN_SUBSCRIBE = "graphql.subscribe";
@@ -584,6 +558,7 @@ const ATTR_ABILITIES_STATUS = "rumble.abilities.status";
584
558
  const ATTR_ABILITIES_STATIC = "rumble.abilities.static";
585
559
  const ATTR_ABILITIES_DYNAMIC = "rumble.abilities.dynamic";
586
560
  const ATTR_ABILITIES_TOTAL = "rumble.abilities.total";
561
+ const ATTR_ABILITIES_RESOLVED = "rumble.abilities.resolved";
587
562
  const ATTR_FILTERS_TOTAL = "rumble.filters.total";
588
563
  const ATTR_FILTERS_ALLOWED = "rumble.filters.allowed";
589
564
  const ATTR_SUBSCRIPTION_EVENT_INDEX = "rumble.subscription.event_index";
@@ -734,8 +709,7 @@ function startTimer() {
734
709
  function durationMs(start) {
735
710
  return Math.round((performance.now() - start) * 100) / 100;
736
711
  }
737
- //#endregion
738
- //#region lib/abilityBuilder/filterResponse.ts
712
+ // lib/abilityBuilder/filterResponse.ts
739
713
  /**
740
714
  * The limit of a find many call. An injected merge limit wins unless the
741
715
  * ability limit is lower, otherwise the default limit caps the ability limit.
@@ -800,16 +774,125 @@ function createFilterResponder({ db, defaultLimit }, tableSchema) {
800
774
  return ret;
801
775
  };
802
776
  }
803
- //#endregion
804
- //#region lib/abilityBuilder/queryFilter.ts
777
+ // lib/abilityBuilder/queryFilter.ts
805
778
  function isDynamicQueryFilter(filter) {
806
779
  return typeof filter === "function";
807
780
  }
808
781
  function isStaticQueryFilter(filter) {
809
782
  return typeof filter !== "function";
810
783
  }
811
- //#endregion
812
- //#region lib/helpers/errorLogging.ts
784
+ // lib/helpers/determineDialectFromSchema.ts
785
+ const dialectCache = /* @__PURE__ */ new WeakMap();
786
+ function determineDBDialectFromSchema(schema) {
787
+ const cached = dialectCache.get(schema);
788
+ if (cached) return cached;
789
+ const found = /* @__PURE__ */ new Set();
790
+ for (const table of Object.values(schema).map((t) => t.table)) {
791
+ if (!table || typeof table !== "object") continue;
792
+ if ((0, drizzle_orm.is)(table, drizzle_orm_pg_core.PgTable)) found.add("postgres");
793
+ else if ((0, drizzle_orm.is)(table, drizzle_orm_mysql_core.MySqlTable)) found.add("mysql");
794
+ else if ((0, drizzle_orm.is)(table, drizzle_orm_sqlite_core.SQLiteTable)) found.add("sqlite");
795
+ }
796
+ const dialects = Array.from(found);
797
+ if (dialects.length === 1) {
798
+ const only = dialects[0];
799
+ dialectCache.set(schema, only);
800
+ return only;
801
+ }
802
+ if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
803
+ throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
804
+ }
805
+ function isPostgresDB(db) {
806
+ return determineDBDialectFromSchema(db._.relations) === "postgres";
807
+ }
808
+ // lib/abilityBuilder/resolveRules.ts
809
+ const DEFAULT_MAX_RESOLVED_IDS = 500;
810
+ /**
811
+ * Whether the where names a relation of the table, so it renders as (nested)
812
+ * `exists` subqueries.
813
+ */
814
+ function referencesRelation(filter, relations) {
815
+ if (!filter || typeof filter !== "object") return false;
816
+ return Object.entries(filter).some(([key, value]) => {
817
+ if (key === "AND" || key === "OR") return value.some((v) => referencesRelation(v, relations));
818
+ if (key === "NOT") return referencesRelation(value, relations);
819
+ return key in relations;
820
+ });
821
+ }
822
+ /**
823
+ * Creates the function replacing the relational rules of a table by the
824
+ * primary keys they match, resolved with one query per call.
825
+ *
826
+ * A relational rule ("the users whose delegation the caller supervises")
827
+ * renders as a chain of correlated `exists` subqueries, once in the row filter
828
+ * and once more in every column flag, at every place the table is nested in a
829
+ * query. Planning such a statement costs the database tens of megabytes. For a
830
+ * given caller the rule matches a fixed set of rows though, so this looks them
831
+ * up once (`select pk from table where <rule>`) and the rule becomes
832
+ * `pk in (...)`, which costs the planner nothing. Rules matching more than
833
+ * `maxIds` rows stay relational, so broad rules do not turn into huge lists.
834
+ *
835
+ * Returns undefined when disabled or the table has no single primary key.
836
+ */
837
+ function createRuleResolver(settings, tableSchema) {
838
+ const config = settings.resolveRelationalRules;
839
+ const primaryKeys = Object.keys(tableSchema.primaryKey);
840
+ if (!config?.enabled || primaryKeys.length !== 1) return void 0;
841
+ const maxIds = config.maxIds ?? DEFAULT_MAX_RESOLVED_IDS;
842
+ const primaryKeyName = primaryKeys[0];
843
+ const db = settings.db;
844
+ const table = tableSchema.table;
845
+ const dialect = determineDBDialectFromSchema(db._.relations);
846
+ const unionAll = {
847
+ postgres: drizzle_orm_pg_core.unionAll,
848
+ sqlite: drizzle_orm_sqlite_core.unionAll,
849
+ mysql: drizzle_orm_mysql_core.unionAll
850
+ }[dialect];
851
+ const listFilter = (ids) => dialect === "postgres" ? { RAW: (aliased) => drizzle_orm.sql`${aliased[primaryKeyName]} = any(${drizzle_orm.sql.param(ids)})` } : { [primaryKeyName]: { in: ids } };
852
+ /** The primary keys matching the rule, tagged with the rule's index. */
853
+ const matches = (where, rule) => {
854
+ const limited = db.select({
855
+ pk: table[primaryKeyName],
856
+ rule: drizzle_orm.sql.raw(String(rule)).as("rule")
857
+ }).from(table).where((0, drizzle_orm.relationsFilterToSQL)(table, sanitizeFilterValue(where), tableSchema.relations, db._.relations)).limit(maxIds + 1).as(`rumble_rule_${rule}`);
858
+ return db.select({
859
+ pk: limited.pk,
860
+ rule: limited.rule
861
+ }).from(limited);
862
+ };
863
+ const whereOf = (filter) => realWhere(filter?.where);
864
+ return async function resolveRules(filters) {
865
+ const relational = filters.flatMap((filter, index) => referencesRelation(whereOf(filter), tableSchema.relations) ? [index] : []);
866
+ if (relational.length === 0) return {
867
+ filters,
868
+ resolved: 0
869
+ };
870
+ const queries = relational.map((index) => matches(whereOf(filters[index]), index));
871
+ const rows = await (queries.length === 1 ? queries[0] : unionAll(...queries));
872
+ const idsPerRule = new Map(relational.map((index) => [index, []]));
873
+ for (const row of rows) idsPerRule.get(Number(row.rule)).push(row.pk);
874
+ let resolved = 0;
875
+ const result = [];
876
+ filters.forEach((filter, index) => {
877
+ const ids = idsPerRule.get(index);
878
+ if (!ids || ids.length > maxIds) {
879
+ result.push(filter);
880
+ return;
881
+ }
882
+ resolved++;
883
+ if (ids.length === 0) return;
884
+ result.push({
885
+ ...filter,
886
+ where: listFilter(ids)
887
+ });
888
+ });
889
+ return {
890
+ filters: result,
891
+ resolved
892
+ };
893
+ };
894
+ }
895
+ // lib/helpers/errorLogging.ts
813
896
  function hasMessage(error) {
814
897
  return typeof error === "object" && error !== null && "message" in error;
815
898
  }
@@ -840,8 +923,7 @@ function errorsLogField(errors) {
840
923
  function errorLogField(error) {
841
924
  return errorsLogField([error]);
842
925
  }
843
- //#endregion
844
- //#region lib/abilityBuilder/telemetry.ts
926
+ // lib/abilityBuilder/telemetry.ts
845
927
  const makeNothingRegisteredWarner = (config, logger) => (0, es_toolkit.debounce)((model, action) => {
846
928
  const msg = `No abilities registered for ${model}/${action} — blocking everything. Register the ability or ignore this warning if intentional.`;
847
929
  if (logger) logger.warn({
@@ -889,8 +971,7 @@ function traceAbilityPreparation({ otel, log, telemetryConfig }, tableName, acti
889
971
  });
890
972
  return run();
891
973
  }
892
- //#endregion
893
- //#region lib/abilityBuilder/tableAbilities.ts
974
+ // lib/abilityBuilder/tableAbilities.ts
894
975
  /**
895
976
  * filter() is async. Without type checking (plain JS, `any`) a forgotten await would
896
977
  * hand the promise itself to drizzle
@@ -948,7 +1029,23 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
948
1029
  const blockEverythingFilter = createBlockEverythingFilter(String(tableName), tableSchema);
949
1030
  const resolveColumnAccess = createColumnAccessResolver(settings, tableSchema);
950
1031
  const transformToResponse = createFilterResponder(settings, tableSchema);
951
- const assembleAbilities = async (userContext, action, attributes) => {
1032
+ const resolveRules = createRuleResolver(settings, tableSchema);
1033
+ const evaluateDynamicFilters = (userContext, action) => Promise.all(dynamicQueryFilters[action].map((func) => func(userContext)));
1034
+ /** The response for the rules applying to the caller: their OR, columns and flags. */
1035
+ const respondWithFilters = (action, allQueryFilters) => {
1036
+ const mergedFilters = allQueryFilters.length === 1 ? { ...allQueryFilters[0] } : allQueryFilters.reduce((a, b) => {
1037
+ return mergeFilters(a, b, "OR");
1038
+ });
1039
+ const { columns, extras, mask, visibility } = resolveColumnAccess(action, allQueryFilters);
1040
+ if (columns) mergedFilters.columns = columns;
1041
+ else delete mergedFilters.columns;
1042
+ if (extras) mergedFilters.extras = {
1043
+ ...mergedFilters.extras,
1044
+ ...extras
1045
+ };
1046
+ return transformToResponse(mergedFilters, mask, visibility);
1047
+ };
1048
+ const assembleAbilities = async (action, attributes, evaluate) => {
952
1049
  const filters = queryFilters.get(action);
953
1050
  if (filters === "unrestricted") {
954
1051
  attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
@@ -959,7 +1056,7 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
959
1056
  settings.warnNothingRegistered(String(tableName), action);
960
1057
  return transformToResponse(blockEverythingFilter);
961
1058
  }
962
- const rawResults = await Promise.all(dynamicQueryFilters[action].map((func) => func(userContext)));
1059
+ const rawResults = await evaluate();
963
1060
  if (rawResults.includes("allow")) {
964
1061
  attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
965
1062
  return transformToResponse();
@@ -967,28 +1064,32 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
967
1064
  const dynamicResults = rawResults.filter((r) => r !== void 0).map(withRestrictingWhere);
968
1065
  attributes[ATTR_ABILITIES_DYNAMIC] = dynamicResults.length;
969
1066
  attributes[ATTR_ABILITIES_STATIC] = simpleQueryFilters[action].length;
970
- const allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
1067
+ let allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
971
1068
  attributes[ATTR_ABILITIES_TOTAL] = allQueryFilters.length;
1069
+ if (resolveRules && allQueryFilters.length > 0) {
1070
+ const resolution = await resolveRules(allQueryFilters);
1071
+ allQueryFilters = resolution.filters;
1072
+ attributes[ATTR_ABILITIES_RESOLVED] = resolution.resolved;
1073
+ }
972
1074
  if (allQueryFilters.length === 0) {
973
1075
  attributes[ATTR_ABILITIES_STATUS] = "blocked_everything";
974
1076
  return transformToResponse(blockEverythingFilter);
975
1077
  }
976
- const mergedFilters = allQueryFilters.length === 1 ? { ...allQueryFilters[0] } : allQueryFilters.reduce((a, b) => {
977
- return mergeFilters(a, b, "OR");
978
- });
979
- const { columns, extras, mask, visibility } = resolveColumnAccess(action, allQueryFilters);
980
- if (columns) mergedFilters.columns = columns;
981
- else delete mergedFilters.columns;
982
- if (extras) mergedFilters.extras = {
983
- ...mergedFilters.extras,
984
- ...extras
985
- };
986
1078
  attributes[ATTR_ABILITIES_STATUS] = "applied";
987
- return transformToResponse(mergedFilters, mask, visibility);
1079
+ return respondWithFilters(action, allQueryFilters);
988
1080
  };
989
1081
  return { withContext: (userContext) => {
990
- const prepare = (action) => traceAbilityPreparation(settings, String(tableName), action, (attributes) => assembleAbilities(userContext, action, attributes));
991
- const cache = /* @__PURE__ */ new Map();
1082
+ const evaluated = /* @__PURE__ */ new Map();
1083
+ const evaluate = (action) => {
1084
+ let results = evaluated.get(action);
1085
+ if (!results) {
1086
+ results = evaluateDynamicFilters(userContext, action);
1087
+ evaluated.set(action, results);
1088
+ }
1089
+ return results;
1090
+ };
1091
+ const prepare = (action) => traceAbilityPreparation(settings, String(tableName), action, (attributes) => assembleAbilities(action, attributes, () => evaluate(action)));
1092
+ let cache = /* @__PURE__ */ new Map();
992
1093
  const resolved = /* @__PURE__ */ new Map();
993
1094
  const abilities = { filter: (action) => {
994
1095
  let prepared = cache.get(action);
@@ -1000,11 +1101,13 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
1000
1101
  return prepared;
1001
1102
  } };
1002
1103
  abilities[resolvedFilterKey] = (action) => resolved.get(action);
1104
+ abilities[refreshAbilitiesKey] = () => {
1105
+ if (resolveRules) cache = /* @__PURE__ */ new Map();
1106
+ };
1003
1107
  return abilities;
1004
1108
  } };
1005
1109
  };
1006
- //#endregion
1007
- //#region lib/abilityBuilder/inputGuard.ts
1110
+ // lib/abilityBuilder/inputGuard.ts
1008
1111
  /** The ts names of the columns a relation joins on, on either side. */
1009
1112
  function relationColumnKeys(db, table, columns) {
1010
1113
  const known = tableHelper({
@@ -1137,8 +1240,7 @@ async function relationExposure(abilities, table, action, columns) {
1137
1240
  const guard = abilities[guardInputKey];
1138
1241
  return guard ? guard.exposed(table, action, columns) : [];
1139
1242
  }
1140
- //#endregion
1141
- //#region lib/abilityBuilder/tableBuilder.ts
1243
+ // lib/abilityBuilder/tableBuilder.ts
1142
1244
  /**
1143
1245
  * Creates the registration API (allow/filter) of a single table.
1144
1246
  */
@@ -1222,9 +1324,20 @@ when: (queryFilter) => {
1222
1324
  }
1223
1325
  };
1224
1326
  };
1225
- //#endregion
1226
- //#region lib/abilityBuilder/index.ts
1227
- const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerConfig }) => {
1327
+ // lib/abilityBuilder/refresh.ts
1328
+ /**
1329
+ * Starts a new snapshot of the resolved relational rules of every table, see
1330
+ * `resolveRelationalRules`. Rumble calls this whenever a root field starts to
1331
+ * resolve. Call it yourself in a resolver that writes rows and then reads them
1332
+ * through a table whose rules it already resolved before the write. A no-op
1333
+ * unless rules are resolved.
1334
+ */
1335
+ function refreshAbilities(abilities) {
1336
+ if (!abilities) return;
1337
+ for (const table of Object.values(abilities)) table?.[refreshAbilitiesKey]?.();
1338
+ }
1339
+ // lib/abilityBuilder/index.ts
1340
+ const createAbilityBuilder = ({ db, actions, defaultLimit, resolveRelationalRules, otel, logger: loggerConfig }) => {
1228
1341
  const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
1229
1342
  const telemetryConfig = {
1230
1343
  otel,
@@ -1234,6 +1347,7 @@ const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerC
1234
1347
  db,
1235
1348
  actions,
1236
1349
  defaultLimit,
1350
+ resolveRelationalRules,
1237
1351
  otel,
1238
1352
  telemetryConfig,
1239
1353
  log,
@@ -1300,8 +1414,7 @@ const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerC
1300
1414
  }
1301
1415
  };
1302
1416
  };
1303
- //#endregion
1304
- //#region lib/helpers/asserts.ts
1417
+ // lib/helpers/asserts.ts
1305
1418
  /**
1306
1419
  *
1307
1420
  * Helper function to map a drizzle findFirst query result,
@@ -1380,8 +1493,7 @@ const assertFirstEntryExists = (value) => {
1380
1493
  if (!v) throw new RumbleErrorSafe("Value not found but required (firstEntry)");
1381
1494
  return v;
1382
1495
  };
1383
- //#endregion
1384
- //#region lib/helpers/mapNullFieldsToUndefined.ts
1496
+ // lib/helpers/mapNullFieldsToUndefined.ts
1385
1497
  /**
1386
1498
  * Helper to map null fields to undefined
1387
1499
  * @param obj The object to map
@@ -1443,11 +1555,9 @@ where: { id: user.id },
1443
1555
  function mapNullFieldsToUndefined(obj) {
1444
1556
  return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, value === null ? void 0 : value]));
1445
1557
  }
1446
- //#endregion
1447
- //#region package.json
1448
- var version = "0.27.0";
1449
- //#endregion
1450
- //#region lib/args/orderArg.ts
1558
+ // package.json
1559
+ var version = "0.27.4";
1560
+ // lib/args/orderArg.ts
1451
1561
  const makeDefaultName$1 = (dbName) => `${(0, es_toolkit.capitalize)((0, drizzle_orm_casing.toCamelCase)(dbName.toString()))}OrderInputArgument`;
1452
1562
  const createOrderArgImplementer = ({ db, schemaBuilder }) => {
1453
1563
  const referenceStorage = /* @__PURE__ */ new Map();
@@ -1478,8 +1588,7 @@ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
1478
1588
  };
1479
1589
  return orderArgImplementer;
1480
1590
  };
1481
- //#endregion
1482
- //#region lib/enum.ts
1591
+ // lib/enum.ts
1483
1592
  /**
1484
1593
  * Checks if a column is a database enum column (Postgres or MySQL).
1485
1594
  */
@@ -1539,8 +1648,7 @@ const createEnumImplementer = ({ schema, schemaBuilder }) => {
1539
1648
  };
1540
1649
  return enumImplementer;
1541
1650
  };
1542
- //#endregion
1543
- //#region lib/helpers/sqlTypes/mapSQLTypeToTSType.ts
1651
+ // lib/helpers/sqlTypes/mapSQLTypeToTSType.ts
1544
1652
  function mapSQLTypeToGraphQLType({ sqlType, fieldName }) {
1545
1653
  if (/^enum\(/.test(sqlType)) return "String";
1546
1654
  const normalized = normalizeSQLType(sqlType);
@@ -1562,8 +1670,7 @@ function mapSQLTypeToGraphQLType({ sqlType, fieldName }) {
1562
1670
  if (ret !== void 0) return ret;
1563
1671
  throw UnknownTypeRumbleError(sqlType, "SQL to GQL");
1564
1672
  }
1565
- //#endregion
1566
- //#region lib/args/whereArg.ts
1673
+ // lib/args/whereArg.ts
1567
1674
  const makeDefaultName = (dbName) => `${(0, es_toolkit.capitalize)((0, drizzle_orm_casing.toCamelCase)(dbName.toString()))}WhereInputArgument`;
1568
1675
  const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
1569
1676
  const referenceStorage = /* @__PURE__ */ new Map();
@@ -1788,8 +1895,7 @@ const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
1788
1895
  };
1789
1896
  return whereArgImplementer;
1790
1897
  };
1791
- //#endregion
1792
- //#region lib/client/client.ts
1898
+ // lib/client/client.ts
1793
1899
  const clientCreatorImplementer = ({ builtSchema }) => {
1794
1900
  const clientCreator = async ({ apiUrl, outputPath, rumbleImportPath, useExternalUrqlClient, removeExisting, forceReactivity, autoIncludeIdField = false }) => {
1795
1901
  if (process.env.NODE_ENV !== "development") console.warn(`Running rumble client generation in non development mode. Are you sure this is correct? Called with arguments: ${JSON.stringify({
@@ -1813,8 +1919,7 @@ const clientCreatorImplementer = ({ builtSchema }) => {
1813
1919
  };
1814
1920
  return clientCreator;
1815
1921
  };
1816
- //#endregion
1817
- //#region lib/context.ts
1922
+ // lib/context.ts
1818
1923
  const createContextFunction = ({ context: makeUserContext, abilityBuilder }) => {
1819
1924
  const builtAbilityBuilder = (0, es_toolkit.once)(() => abilityBuilder._.build());
1820
1925
  return async (req) => {
@@ -1825,8 +1930,7 @@ const createContextFunction = ({ context: makeUserContext, abilityBuilder }) =>
1825
1930
  };
1826
1931
  };
1827
1932
  };
1828
- //#endregion
1829
- //#region lib/helpers/protoMapper.ts
1933
+ // lib/helpers/protoMapper.ts
1830
1934
  function deepSetProto(obj, proto = Object.prototype, seen = /* @__PURE__ */ new WeakSet()) {
1831
1935
  if (obj === null || typeof obj !== "object") return;
1832
1936
  if (seen.has(obj)) return;
@@ -1840,8 +1944,7 @@ function deepSetProto(obj, proto = Object.prototype, seen = /* @__PURE__ */ new
1840
1944
  Object.setPrototypeOf(obj, proto);
1841
1945
  for (const key of Object.keys(obj)) deepSetProto(obj[key], proto, seen);
1842
1946
  }
1843
- //#endregion
1844
- //#region lib/countQuery.ts
1947
+ // lib/countQuery.ts
1845
1948
  const createCountQueryImplementer = ({ db, schemaBuilder, whereArgImplementer, makePubSubInstance }) => {
1846
1949
  return ({ table, listAction = "read", isAllowed }) => {
1847
1950
  const WhereArg = whereArgImplementer({ table });
@@ -1881,8 +1984,7 @@ const createCountQueryImplementer = ({ db, schemaBuilder, whereArgImplementer, m
1881
1984
  });
1882
1985
  };
1883
1986
  };
1884
- //#endregion
1885
- //#region lib/helpers/sofaOpenAPIWebhookDocs.ts
1987
+ // lib/helpers/sofaOpenAPIWebhookDocs.ts
1886
1988
  const sofaOpenAPIWebhookDocs = {
1887
1989
  paths: {
1888
1990
  "/webhook": { post: {
@@ -1966,8 +2068,7 @@ const sofaOpenAPIWebhookDocs = {
1966
2068
  }
1967
2069
  } }
1968
2070
  };
1969
- //#endregion
1970
- //#region lib/helpers/tracing.ts
2071
+ // lib/helpers/tracing.ts
1971
2072
  function isAsyncIterable(value) {
1972
2073
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value;
1973
2074
  }
@@ -2155,8 +2256,7 @@ function buildTracedSubscribe(subscribeFn, rumbleInput, transport) {
2155
2256
  });
2156
2257
  };
2157
2258
  }
2158
- //#endregion
2159
- //#region lib/helpers/sqlTypes/mapDrizzleTypeToGraphQlType.ts
2259
+ // lib/helpers/sqlTypes/mapDrizzleTypeToGraphQlType.ts
2160
2260
  function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, nullable, isArray }) {
2161
2261
  const gqlType = mapSQLTypeToGraphQLType({
2162
2262
  sqlType,
@@ -2204,33 +2304,7 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
2204
2304
  default: throw new RumbleError(`Unsupported object type ${gqlType} for column ${fieldName}`);
2205
2305
  }
2206
2306
  }
2207
- //#endregion
2208
- //#region lib/helpers/determineDialectFromSchema.ts
2209
- const dialectCache = /* @__PURE__ */ new WeakMap();
2210
- function determineDBDialectFromSchema(schema) {
2211
- const cached = dialectCache.get(schema);
2212
- if (cached) return cached;
2213
- const found = /* @__PURE__ */ new Set();
2214
- for (const table of Object.values(schema).map((t) => t.table)) {
2215
- if (!table || typeof table !== "object") continue;
2216
- if ((0, drizzle_orm.is)(table, drizzle_orm_pg_core.PgTable)) found.add("postgres");
2217
- else if ((0, drizzle_orm.is)(table, drizzle_orm_mysql_core.MySqlTable)) found.add("mysql");
2218
- else if ((0, drizzle_orm.is)(table, drizzle_orm_sqlite_core.SQLiteTable)) found.add("sqlite");
2219
- }
2220
- const dialects = Array.from(found);
2221
- if (dialects.length === 1) {
2222
- const only = dialects[0];
2223
- dialectCache.set(schema, only);
2224
- return only;
2225
- }
2226
- if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
2227
- throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
2228
- }
2229
- function isPostgresDB(db) {
2230
- return determineDBDialectFromSchema(db._.relations) === "postgres";
2231
- }
2232
- //#endregion
2233
- //#region lib/search.ts
2307
+ // lib/search.ts
2234
2308
  async function initSearchIfApplicable(input) {
2235
2309
  const log = input.logger?.enabled ? input.logger.logger : void 0;
2236
2310
  if (!isPostgresDB(input.db)) {
@@ -2290,8 +2364,7 @@ function adjustQueryArgsForSearch({ search, args, tableSchema, abilities }) {
2290
2364
  }), drizzle_orm.sql.raw(" OR "))})` }] };
2291
2365
  }
2292
2366
  }
2293
- //#endregion
2294
- //#region lib/object.ts
2367
+ // lib/object.ts
2295
2368
  const isProbablyAConfigObject = (t) => {
2296
2369
  if (typeof t !== "object") return false;
2297
2370
  if (Object.keys(t).some((k) => [
@@ -2519,8 +2592,7 @@ const createObjectImplementer = ({ db, search, otel, logger: loggerConfig, schem
2519
2592
  });
2520
2593
  };
2521
2594
  };
2522
- //#endregion
2523
- //#region lib/pubsub.ts
2595
+ // lib/pubsub.ts
2524
2596
  const SUBSCRIPTION_NOTIFIER_RUMBLE_PREFIX = "RUMBLE_SUBSCRIPTION_NOTIFICATION";
2525
2597
  const SUBSCRIPTION_NOTIFIER_REMOVED = "REMOVED";
2526
2598
  const SUBSCRIPTION_NOTIFIER_UPDATED = "UPDATED";
@@ -2595,8 +2667,7 @@ const createPubSubInstance = ({ subscriptions }) => {
2595
2667
  makePubSubInstance
2596
2668
  };
2597
2669
  };
2598
- //#endregion
2599
- //#region lib/query.ts
2670
+ // lib/query.ts
2600
2671
  const createQueryImplementer = ({ db, schemaBuilder, search, otel, logger: loggerConfig, whereArgImplementer, orderArgImplementer, makePubSubInstance }) => {
2601
2672
  const registeredQueryFieldNames = /* @__PURE__ */ new Set();
2602
2673
  const telemetryConfig = {
@@ -2711,8 +2782,7 @@ const createQueryImplementer = ({ db, schemaBuilder, search, otel, logger: logge
2711
2782
  });
2712
2783
  };
2713
2784
  };
2714
- //#endregion
2715
- //#region lib/args/whereArgsImplementer.ts
2785
+ // lib/args/whereArgsImplementer.ts
2716
2786
  function implementDefaultWhereInputArgs(schemaBuilder) {
2717
2787
  const IntWhereInputArgument = schemaBuilder.inputRef("IntWhereInputArgument").implement({ fields: (t) => ({
2718
2788
  eq: t.int(),
@@ -2964,16 +3034,14 @@ function implementDefaultWhereInputArgs(schemaBuilder) {
2964
3034
  NOT: t.field({ type: DateTimeWhereInputArgument })
2965
3035
  }) });
2966
3036
  }
2967
- //#endregion
2968
- //#region lib/helpers/objectTypeOptions.ts
3037
+ // lib/helpers/objectTypeOptions.ts
2969
3038
  function objectTypeOptionsOfField(buildCache, fieldConfig) {
2970
3039
  const type = fieldConfig.type;
2971
3040
  const ref = type.kind === "List" ? type.type.kind === "Object" ? type.type.ref : void 0 : type.kind === "Object" ? type.ref : void 0;
2972
3041
  if (!ref) return void 0;
2973
3042
  return buildCache.getTypeConfig(ref, "Object").pothosOptions;
2974
3043
  }
2975
- //#endregion
2976
- //#region lib/helpers/registerPlugin.ts
3044
+ // lib/helpers/registerPlugin.ts
2977
3045
  const registered = /* @__PURE__ */ new Set();
2978
3046
  function registerPluginOnce(name, plugin) {
2979
3047
  if (registered.has(name)) return;
@@ -2981,15 +3049,13 @@ function registerPluginOnce(name, plugin) {
2981
3049
  _pothos_core.default.registerPlugin(name, plugin);
2982
3050
  registered.add(name);
2983
3051
  }
2984
- //#endregion
2985
- //#region lib/columnMaskPlugin/maskTypes.ts
3052
+ // lib/columnMaskPlugin/maskTypes.ts
2986
3053
  const maskColumnsKey = "maskColumns";
2987
3054
  const maskColumnsActionKey = "maskColumnsAction";
2988
3055
  const maskPrimaryKeysKey = "maskPrimaryKeys";
2989
3056
  const maskRelationsKey = "maskRelations";
2990
3057
  const columnMaskPluginName = "ColumnMaskPlugin";
2991
- //#endregion
2992
- //#region lib/columnMaskPlugin/columnMaskPlugin.ts
3058
+ // lib/columnMaskPlugin/columnMaskPlugin.ts
2993
3059
  var ColumnMaskPlugin = class extends _pothos_core.BasePlugin {
2994
3060
  wrapResolve(resolver, fieldConfig) {
2995
3061
  return this.maskResult(this.maskRelation(this.maskPrimaryKey(resolver, fieldConfig), fieldConfig), fieldConfig);
@@ -3028,8 +3094,7 @@ var ColumnMaskPlugin = class extends _pothos_core.BasePlugin {
3028
3094
  function registerColumnMaskPlugin() {
3029
3095
  registerPluginOnce(columnMaskPluginName, ColumnMaskPlugin);
3030
3096
  }
3031
- //#endregion
3032
- //#region lib/helpers/abilityAwareClient.ts
3097
+ // lib/helpers/abilityAwareClient.ts
3033
3098
  function createAbilityAwareClient({ db, abilityBuilder }) {
3034
3099
  return (context) => {
3035
3100
  const apis = /* @__PURE__ */ new Map();
@@ -3073,11 +3138,31 @@ function createAbilityAwareClient({ db, abilityBuilder }) {
3073
3138
  } });
3074
3139
  };
3075
3140
  }
3076
- //#endregion
3077
- //#region lib/runtimeFiltersPlugin/filterTypes.ts
3141
+ // lib/refreshAbilitiesPlugin/refreshAbilitiesPlugin.ts
3142
+ const refreshAbilitiesPluginName = "RefreshAbilitiesPlugin";
3143
+ /**
3144
+ * Starts a new snapshot of the resolved relational rules whenever a root
3145
+ * field (query, mutation or subscription event) starts to resolve, see
3146
+ * `resolveRelationalRules`. A mutation's second root field sees what the first
3147
+ * wrote, and a live query refetching sees the rows related to the caller by
3148
+ * now. A no-op unless rules are resolved.
3149
+ */
3150
+ var RefreshAbilitiesPlugin = class extends _pothos_core.BasePlugin {
3151
+ wrapResolve(resolver, fieldConfig) {
3152
+ const { kind } = this.buildCache.getTypeConfig(fieldConfig.parentType);
3153
+ if (kind !== "Query" && kind !== "Mutation" && kind !== "Subscription") return resolver;
3154
+ return (parent, args, context, info) => {
3155
+ refreshAbilities(context?.abilities);
3156
+ return resolver(parent, args, context, info);
3157
+ };
3158
+ }
3159
+ };
3160
+ function registerRefreshAbilitiesPlugin() {
3161
+ registerPluginOnce(refreshAbilitiesPluginName, RefreshAbilitiesPlugin);
3162
+ }
3163
+ // lib/runtimeFiltersPlugin/filterTypes.ts
3078
3164
  const pluginName = "RuntimeFiltersPlugin";
3079
- //#endregion
3080
- //#region lib/runtimeFiltersPlugin/runtimeFiltersPlugin.ts
3165
+ // lib/runtimeFiltersPlugin/runtimeFiltersPlugin.ts
3081
3166
  const applyFiltersKey = "applyFilters";
3082
3167
  var RuntimeFiltersPlugin = class extends _pothos_core.BasePlugin {
3083
3168
  tracer;
@@ -3179,8 +3264,7 @@ var RuntimeFiltersPlugin = class extends _pothos_core.BasePlugin {
3179
3264
  function registerRuntimeFiltersPlugin() {
3180
3265
  registerPluginOnce(pluginName, RuntimeFiltersPlugin);
3181
3266
  }
3182
- //#endregion
3183
- //#region lib/scalars/addressData.ts
3267
+ // lib/scalars/addressData.ts
3184
3268
  /*! Address data derived from Google's Address Data Service (https://github.com/google/libaddressinput),
3185
3269
  * licensed under CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/).
3186
3270
  * Changes: reduced to required fields, postal code patterns and examples, and regions with
@@ -8992,8 +9076,7 @@ const addressData = {
8992
9076
  },
8993
9077
  ZW: { require: "AC" }
8994
9078
  };
8995
- //#endregion
8996
- //#region lib/scalars/addressValidation.ts
9079
+ // lib/scalars/addressValidation.ts
8997
9080
  const REQUIRED_FIELD_PATHS = {
8998
9081
  A: "streetAddress",
8999
9082
  C: "locality",
@@ -9123,8 +9206,7 @@ const addressValidationSchema = zod.z.object({
9123
9206
  message: issue.message
9124
9207
  });
9125
9208
  });
9126
- //#endregion
9127
- //#region lib/scalars/address.ts
9209
+ // lib/scalars/address.ts
9128
9210
  function implementDefaultAddressTypes(schemaBuilder) {
9129
9211
  return {
9130
9212
  Address: schemaBuilder.objectRef("Address").implement({
@@ -9150,8 +9232,7 @@ function implementDefaultAddressTypes(schemaBuilder) {
9150
9232
  })
9151
9233
  };
9152
9234
  }
9153
- //#endregion
9154
- //#region lib/scalars/emailAddress.ts
9235
+ // lib/scalars/emailAddress.ts
9155
9236
  /**
9156
9237
  * Validates an email address using graphql-scalars' own format check, then lowercases it, exactly
9157
9238
  * as the `EmailAddress` scalar does. Exported for values that do not arrive as GraphQL input,
@@ -9177,8 +9258,7 @@ const EmailAddressResolver = new graphql.GraphQLScalarType({
9177
9258
  return normalizeEmailAddress(ast.value);
9178
9259
  }
9179
9260
  });
9180
- //#endregion
9181
- //#region lib/scalars/personName.ts
9261
+ // lib/scalars/personName.ts
9182
9262
  const MAX_LENGTH = 200;
9183
9263
  const NAME_REGEX = /^[\p{L}\p{M}]+(?:(?:[ '-]|\.\x20?)[\p{L}\p{M}]+)*\.?$/u;
9184
9264
  const APOSTROPHE_VARIANTS = /[\u2018\u2019\u02BC\u00B4\u0060]/gu;
@@ -9207,8 +9287,7 @@ const PersonNameResolver = new graphql.GraphQLScalarType({
9207
9287
  return normalizePersonName(ast.value);
9208
9288
  }
9209
9289
  });
9210
- //#endregion
9211
- //#region lib/scalars/phoneNumber.ts
9290
+ // lib/scalars/phoneNumber.ts
9212
9291
  /**
9213
9292
  * Parses and validates a phone number using libphonenumber-js (Google's phone number metadata),
9214
9293
  * which checks real per-country validity (length, area code plausibility, ...) rather than just
@@ -9245,16 +9324,17 @@ const PhoneNumberResolver = new graphql.GraphQLScalarType({
9245
9324
  return parsePhoneNumber(ast.value);
9246
9325
  }
9247
9326
  });
9248
- //#endregion
9249
- //#region lib/schemaBuilder.ts
9327
+ // lib/schemaBuilder.ts
9250
9328
  const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger, validation, abilityBuilder }) => {
9251
9329
  const createSpan = otel?.enabled && otel.tracer ? (0, _pothos_tracing_opentelemetry.createOpenTelemetryWrapper)(otel.tracer, otel.options) : void 0;
9252
9330
  registerRuntimeFiltersPlugin();
9253
9331
  registerColumnMaskPlugin();
9332
+ registerRefreshAbilitiesPlugin();
9254
9333
  const schemaBuilder = new _pothos_core.default({
9255
9334
  ...pothosConfig,
9256
9335
  plugins: [
9257
9336
  columnMaskPluginName,
9337
+ refreshAbilitiesPluginName,
9258
9338
  pluginName,
9259
9339
  _pothos_plugin_drizzle.default,
9260
9340
  _pothos_plugin_smart_subscriptions.default,
@@ -9330,8 +9410,7 @@ const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger, validatio
9330
9410
  schemaBuilder.mutationType({});
9331
9411
  return { schemaBuilder };
9332
9412
  };
9333
- //#endregion
9334
- //#region lib/rumble.ts
9413
+ // lib/rumble.ts
9335
9414
  /**
9336
9415
  * Mirrors sofa's own default error handler: it derives the HTTP status from
9337
9416
  * `extensions.http` and returns the errors as JSON. rumble only adds telemetry
@@ -9659,7 +9738,6 @@ export const r = rumble({
9659
9738
  buildSchema: builtSchema
9660
9739
  };
9661
9740
  };
9662
- //#endregion
9663
9741
  exports.RumbleError = RumbleError;
9664
9742
  exports.RumbleErrorSafe = RumbleErrorSafe;
9665
9743
  exports.addressChecks = addressChecks;
@@ -9677,6 +9755,7 @@ exports.normalizeAddress = normalizeAddress;
9677
9755
  exports.normalizeEmailAddress = normalizeEmailAddress;
9678
9756
  exports.normalizePersonName = normalizePersonName;
9679
9757
  exports.parsePhoneNumber = parsePhoneNumber;
9758
+ exports.refreshAbilities = refreshAbilities;
9680
9759
  exports.rumble = rumble;
9681
9760
  exports.validateAddress = validateAddress;
9682
9761