@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.mjs CHANGED
@@ -1,18 +1,18 @@
1
- import { t as generateFromSchema } from "./generate-BySnFVve.mjs";
2
- import { One, aliasedTable, asc, count, desc, getTableColumns, getTableName, is, isTable, or, relationsFilterToSQL, sql } from "drizzle-orm";
1
+ import { t as generateFromSchema } from "./generate-Dzf2klR2.mjs";
2
+ import { One, asc, count, desc, getTableColumns, getTableName, is, isTable, or, relationsFilterToSQL, sql } from "drizzle-orm";
3
3
  import { GraphQLError, GraphQLScalarType, GraphQLSchema, Kind, execute as defaultExecute, getOperationAST, print, subscribe as defaultSubscribe } from "graphql";
4
4
  import { capitalize, cloneDeep, debounce, merge, once, toMerged } from "es-toolkit";
5
5
  import { SpanStatusCode, isSpanContextValid, trace } from "@opentelemetry/api";
6
6
  import { ATTR_GRAPHQL_DOCUMENT, ATTR_GRAPHQL_OPERATION_NAME, ATTR_GRAPHQL_OPERATION_TYPE } from "@opentelemetry/semantic-conventions/incubating";
7
7
  import { AttributeNames, SpanNames, createOpenTelemetryWrapper } from "@pothos/tracing-opentelemetry";
8
+ import { MySqlEnumColumn, MySqlEnumObjectColumn, MySqlTable, getTableConfig, unionAll } from "drizzle-orm/mysql-core";
9
+ import { PgColumn, PgEnumColumn, PgEnumObjectColumn, PgTable, getTableConfig as getTableConfig$1, isPgEnum, unionAll as unionAll$1 } from "drizzle-orm/pg-core";
10
+ import { SQLiteTable, getTableConfig as getTableConfig$2, unionAll as unionAll$2 } from "drizzle-orm/sqlite-core";
8
11
  import { EnvelopArmorPlugin } from "@escape.tech/graphql-armor";
9
12
  import { useDisableIntrospection } from "@graphql-yoga/plugin-disable-introspection";
10
13
  import { createPubSub, createYoga } from "graphql-yoga";
11
14
  import { toCamelCase } from "drizzle-orm/casing";
12
- import { PgColumn, PgEnumColumn, PgEnumObjectColumn, PgTable, getTableConfig, isPgEnum } from "drizzle-orm/pg-core";
13
- import { MySqlEnumColumn, MySqlEnumObjectColumn, MySqlTable, getTableConfig as getTableConfig$1 } from "drizzle-orm/mysql-core";
14
15
  import pluralize from "pluralize";
15
- import { SQLiteTable, getTableConfig as getTableConfig$2 } from "drizzle-orm/sqlite-core";
16
16
  import SchemaBuilder, { BasePlugin, isThenable } from "@pothos/core";
17
17
  import DrizzlePlugin from "@pothos/plugin-drizzle";
18
18
  import SmartSubscriptionsPlugin, { subscribeOptionsFromIterator } from "@pothos/plugin-smart-subscriptions";
@@ -22,7 +22,7 @@ import { BigIntResolver, ByteResolver, DateResolver, DateTimeISOResolver, EmailA
22
22
  import DataLoader from "dataloader";
23
23
  import { z } from "zod";
24
24
  import { parsePhoneNumberFromString } from "libphonenumber-js";
25
- //#region lib/types/rumbleError.ts
25
+ // lib/types/rumbleError.ts
26
26
  /**
27
27
  * An error that gets raised by rumble whenever something does not go according to plan.
28
28
  * Mostly internals, configuration errors or other unexpected things.
@@ -39,8 +39,7 @@ var RumbleError = class extends Error {
39
39
  * E.g. the assert helpers issue these.
40
40
  */
41
41
  var RumbleErrorSafe = class extends GraphQLError {};
42
- //#endregion
43
- //#region lib/helpers/tableHelpers.ts
42
+ // lib/helpers/tableHelpers.ts
44
43
  const indexCache = /* @__PURE__ */ new WeakMap();
45
44
  function buildIndex(db) {
46
45
  const byTsName = /* @__PURE__ */ new Map();
@@ -109,8 +108,7 @@ function tableHelper({ db, table }) {
109
108
  foundRelation: entry.foundRelation
110
109
  };
111
110
  }
112
- //#endregion
113
- //#region lib/helpers/mergeFilters.ts
111
+ // lib/helpers/mergeFilters.ts
114
112
  const EmptyFilter$1 = Symbol.for("drizzle:EmptyFilter");
115
113
  /**
116
114
  * Normalizes drizzle-orm's `EmptyFilter` sentinel to `undefined` so it's
@@ -154,8 +152,7 @@ function mergeFilters(filterA, filterB, mode = "AND") {
154
152
  with: mergeRecords(filterA?.with, filterB?.with)
155
153
  };
156
154
  }
157
- //#endregion
158
- //#region lib/helpers/sanitizeFilterValue.ts
155
+ // lib/helpers/sanitizeFilterValue.ts
159
156
  const EmptyFilter = Symbol.for("drizzle:EmptyFilter");
160
157
  function isPlainObject(value) {
161
158
  if (typeof value !== "object" || value === null) return false;
@@ -168,17 +165,16 @@ function sanitizeFilterValue(value) {
168
165
  if (isPlainObject(value)) return Object.fromEntries(Object.entries(value).map(([key, entry]) => [key, sanitizeFilterValue(entry)]));
169
166
  return value;
170
167
  }
171
- //#endregion
172
- //#region lib/abilityBuilder/keys.ts
168
+ // lib/abilityBuilder/keys.ts
173
169
  const ACTION_FLAG_KEY = "__rumble_action";
174
170
  const columnFlagKey = (group) => `__rumble_columns_${group}`;
175
171
  const columnMaskKey = Symbol.for("rumble:columnMask");
176
172
  const columnVisibilityKey = Symbol.for("rumble:columnVisibility");
177
173
  const guardInputKey = Symbol.for("rumble:guardInput");
178
174
  const resolvedFilterKey = Symbol.for("rumble:resolvedFilter");
175
+ const refreshAbilitiesKey = Symbol.for("rumble:refreshAbilities");
179
176
  const requestStateKey = Symbol.for("rumble:requestState");
180
- //#endregion
181
- //#region lib/abilityBuilder/requestState.ts
177
+ // lib/abilityBuilder/requestState.ts
182
178
  function createRequestState() {
183
179
  return {
184
180
  masked: /* @__PURE__ */ new WeakMap(),
@@ -193,16 +189,7 @@ function attachRequestState(abilities, state) {
193
189
  function requestStateOf(abilities) {
194
190
  return abilities?.[requestStateKey];
195
191
  }
196
- //#endregion
197
- //#region lib/abilityBuilder/columnMask.ts
198
- function referencesRelation(filter, relations) {
199
- if (!filter || typeof filter !== "object") return false;
200
- return Object.entries(filter).some(([key, value]) => {
201
- if (key === "AND" || key === "OR") return value.some((v) => referencesRelation(v, relations));
202
- if (key === "NOT") return referencesRelation(value, relations);
203
- return key in relations;
204
- });
205
- }
192
+ // lib/abilityBuilder/columnMask.ts
206
193
  function selectedColumns(columns, allColumns) {
207
194
  if (!columns) return new Set(allColumns);
208
195
  const entries = Object.entries(columns).filter(([, v]) => v !== void 0);
@@ -261,12 +248,7 @@ function conditionalGroups(rules, guaranteed) {
261
248
  function createColumnAccessResolver({ db, actions }, tableSchema) {
262
249
  const allColumnNames = Object.keys(tableSchema.columns);
263
250
  const primaryKeyNames = Object.keys(tableSchema.primaryKey);
264
- const primaryKeyName = primaryKeyNames[0];
265
251
  const toSQL = (table, wheres) => or(...wheres.map((where) => relationsFilterToSQL(table, sanitizeFilterValue(where), tableSchema.relations, db._.relations)));
266
- const uncorrelated = (table, wheres, group) => {
267
- const inner = aliasedTable(tableSchema.table, `rumble_columns_${group}`);
268
- return sql`exists ${db.select({ pk: inner[primaryKeyName] }).from(inner).where(sql`${inner[primaryKeyName]} = ${table[primaryKeyName]} and (${toSQL(inner, wheres)})`)}`;
269
- };
270
252
  return function resolveColumnAccess(action, filters) {
271
253
  const rules = filters.map((f) => ({
272
254
  where: realWhere(f?.where),
@@ -297,11 +279,7 @@ function createColumnAccessResolver({ db, actions }, tableSchema) {
297
279
  const actionIndex = sql.raw(String(actions.indexOf(action)));
298
280
  const extras = { [ACTION_FLAG_KEY]: () => sql`${actionIndex}` };
299
281
  groups.forEach(({ wheres }, group) => {
300
- const relational = wheres.some((w) => referencesRelation(w, tableSchema.relations));
301
- extras[mask.flags[group]] = (table) => {
302
- const condition = relational ? uncorrelated(table, wheres, group) : toSQL(table, wheres);
303
- return sql`case when ${condition} then 1 else 0 end`;
304
- };
282
+ extras[mask.flags[group]] = (table) => sql`case when ${toSQL(table, wheres)} then 1 else 0 end`;
305
283
  });
306
284
  return {
307
285
  columns,
@@ -384,8 +362,7 @@ function createColumnMasker({ actions }, tables) {
384
362
  columnsHidden
385
363
  };
386
364
  }
387
- //#endregion
388
- //#region lib/helpers/sqlTypes/types.ts
365
+ // lib/helpers/sqlTypes/types.ts
389
366
  /**
390
367
  * Strips parameter suffixes like `(256)`, `(10,2)` and timezone modifiers
391
368
  * (`with time zone`, `without time zone`) from raw getSQLType() output so
@@ -492,8 +469,7 @@ function isBytesSQLTypeString(sqlType) {
492
469
  return bytesSQLTypeStrings.includes(sqlType);
493
470
  }
494
471
  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})`);
495
- //#endregion
496
- //#region lib/helpers/sqlTypes/distinctValuesFromSQLType.ts
472
+ // lib/helpers/sqlTypes/distinctValuesFromSQLType.ts
497
473
  function createDistinctValuesFromSQLType(sqlType) {
498
474
  if (isIntLikeSQLTypeString(sqlType)) return {
499
475
  value1: 1,
@@ -529,8 +505,7 @@ function createDistinctValuesFromSQLType(sqlType) {
529
505
  };
530
506
  throw UnknownTypeRumbleError(sqlType, "Distinct");
531
507
  }
532
- //#endregion
533
- //#region lib/helpers/telemetry.ts
508
+ // lib/helpers/telemetry.ts
534
509
  const SPAN_EXECUTE = SpanNames.EXECUTE;
535
510
  /** @pothos/tracing-opentelemetry's SpanNames enum has no subscribe member. */
536
511
  const SPAN_SUBSCRIBE = "graphql.subscribe";
@@ -554,6 +529,7 @@ const ATTR_ABILITIES_STATUS = "rumble.abilities.status";
554
529
  const ATTR_ABILITIES_STATIC = "rumble.abilities.static";
555
530
  const ATTR_ABILITIES_DYNAMIC = "rumble.abilities.dynamic";
556
531
  const ATTR_ABILITIES_TOTAL = "rumble.abilities.total";
532
+ const ATTR_ABILITIES_RESOLVED = "rumble.abilities.resolved";
557
533
  const ATTR_FILTERS_TOTAL = "rumble.filters.total";
558
534
  const ATTR_FILTERS_ALLOWED = "rumble.filters.allowed";
559
535
  const ATTR_SUBSCRIPTION_EVENT_INDEX = "rumble.subscription.event_index";
@@ -704,8 +680,7 @@ function startTimer() {
704
680
  function durationMs(start) {
705
681
  return Math.round((performance.now() - start) * 100) / 100;
706
682
  }
707
- //#endregion
708
- //#region lib/abilityBuilder/filterResponse.ts
683
+ // lib/abilityBuilder/filterResponse.ts
709
684
  /**
710
685
  * The limit of a find many call. An injected merge limit wins unless the
711
686
  * ability limit is lower, otherwise the default limit caps the ability limit.
@@ -770,16 +745,125 @@ function createFilterResponder({ db, defaultLimit }, tableSchema) {
770
745
  return ret;
771
746
  };
772
747
  }
773
- //#endregion
774
- //#region lib/abilityBuilder/queryFilter.ts
748
+ // lib/abilityBuilder/queryFilter.ts
775
749
  function isDynamicQueryFilter(filter) {
776
750
  return typeof filter === "function";
777
751
  }
778
752
  function isStaticQueryFilter(filter) {
779
753
  return typeof filter !== "function";
780
754
  }
781
- //#endregion
782
- //#region lib/helpers/errorLogging.ts
755
+ // lib/helpers/determineDialectFromSchema.ts
756
+ const dialectCache = /* @__PURE__ */ new WeakMap();
757
+ function determineDBDialectFromSchema(schema) {
758
+ const cached = dialectCache.get(schema);
759
+ if (cached) return cached;
760
+ const found = /* @__PURE__ */ new Set();
761
+ for (const table of Object.values(schema).map((t) => t.table)) {
762
+ if (!table || typeof table !== "object") continue;
763
+ if (is(table, PgTable)) found.add("postgres");
764
+ else if (is(table, MySqlTable)) found.add("mysql");
765
+ else if (is(table, SQLiteTable)) found.add("sqlite");
766
+ }
767
+ const dialects = Array.from(found);
768
+ if (dialects.length === 1) {
769
+ const only = dialects[0];
770
+ dialectCache.set(schema, only);
771
+ return only;
772
+ }
773
+ if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
774
+ throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
775
+ }
776
+ function isPostgresDB(db) {
777
+ return determineDBDialectFromSchema(db._.relations) === "postgres";
778
+ }
779
+ // lib/abilityBuilder/resolveRules.ts
780
+ const DEFAULT_MAX_RESOLVED_IDS = 500;
781
+ /**
782
+ * Whether the where names a relation of the table, so it renders as (nested)
783
+ * `exists` subqueries.
784
+ */
785
+ function referencesRelation(filter, relations) {
786
+ if (!filter || typeof filter !== "object") return false;
787
+ return Object.entries(filter).some(([key, value]) => {
788
+ if (key === "AND" || key === "OR") return value.some((v) => referencesRelation(v, relations));
789
+ if (key === "NOT") return referencesRelation(value, relations);
790
+ return key in relations;
791
+ });
792
+ }
793
+ /**
794
+ * Creates the function replacing the relational rules of a table by the
795
+ * primary keys they match, resolved with one query per call.
796
+ *
797
+ * A relational rule ("the users whose delegation the caller supervises")
798
+ * renders as a chain of correlated `exists` subqueries, once in the row filter
799
+ * and once more in every column flag, at every place the table is nested in a
800
+ * query. Planning such a statement costs the database tens of megabytes. For a
801
+ * given caller the rule matches a fixed set of rows though, so this looks them
802
+ * up once (`select pk from table where <rule>`) and the rule becomes
803
+ * `pk in (...)`, which costs the planner nothing. Rules matching more than
804
+ * `maxIds` rows stay relational, so broad rules do not turn into huge lists.
805
+ *
806
+ * Returns undefined when disabled or the table has no single primary key.
807
+ */
808
+ function createRuleResolver(settings, tableSchema) {
809
+ const config = settings.resolveRelationalRules;
810
+ const primaryKeys = Object.keys(tableSchema.primaryKey);
811
+ if (!config?.enabled || primaryKeys.length !== 1) return void 0;
812
+ const maxIds = config.maxIds ?? DEFAULT_MAX_RESOLVED_IDS;
813
+ const primaryKeyName = primaryKeys[0];
814
+ const db = settings.db;
815
+ const table = tableSchema.table;
816
+ const dialect = determineDBDialectFromSchema(db._.relations);
817
+ const unionAll$3 = {
818
+ postgres: unionAll$1,
819
+ sqlite: unionAll$2,
820
+ mysql: unionAll
821
+ }[dialect];
822
+ const listFilter = (ids) => dialect === "postgres" ? { RAW: (aliased) => sql`${aliased[primaryKeyName]} = any(${sql.param(ids)})` } : { [primaryKeyName]: { in: ids } };
823
+ /** The primary keys matching the rule, tagged with the rule's index. */
824
+ const matches = (where, rule) => {
825
+ const limited = db.select({
826
+ pk: table[primaryKeyName],
827
+ rule: sql.raw(String(rule)).as("rule")
828
+ }).from(table).where(relationsFilterToSQL(table, sanitizeFilterValue(where), tableSchema.relations, db._.relations)).limit(maxIds + 1).as(`rumble_rule_${rule}`);
829
+ return db.select({
830
+ pk: limited.pk,
831
+ rule: limited.rule
832
+ }).from(limited);
833
+ };
834
+ const whereOf = (filter) => realWhere(filter?.where);
835
+ return async function resolveRules(filters) {
836
+ const relational = filters.flatMap((filter, index) => referencesRelation(whereOf(filter), tableSchema.relations) ? [index] : []);
837
+ if (relational.length === 0) return {
838
+ filters,
839
+ resolved: 0
840
+ };
841
+ const queries = relational.map((index) => matches(whereOf(filters[index]), index));
842
+ const rows = await (queries.length === 1 ? queries[0] : unionAll$3(...queries));
843
+ const idsPerRule = new Map(relational.map((index) => [index, []]));
844
+ for (const row of rows) idsPerRule.get(Number(row.rule)).push(row.pk);
845
+ let resolved = 0;
846
+ const result = [];
847
+ filters.forEach((filter, index) => {
848
+ const ids = idsPerRule.get(index);
849
+ if (!ids || ids.length > maxIds) {
850
+ result.push(filter);
851
+ return;
852
+ }
853
+ resolved++;
854
+ if (ids.length === 0) return;
855
+ result.push({
856
+ ...filter,
857
+ where: listFilter(ids)
858
+ });
859
+ });
860
+ return {
861
+ filters: result,
862
+ resolved
863
+ };
864
+ };
865
+ }
866
+ // lib/helpers/errorLogging.ts
783
867
  function hasMessage(error) {
784
868
  return typeof error === "object" && error !== null && "message" in error;
785
869
  }
@@ -810,8 +894,7 @@ function errorsLogField(errors) {
810
894
  function errorLogField(error) {
811
895
  return errorsLogField([error]);
812
896
  }
813
- //#endregion
814
- //#region lib/abilityBuilder/telemetry.ts
897
+ // lib/abilityBuilder/telemetry.ts
815
898
  const makeNothingRegisteredWarner = (config, logger) => debounce((model, action) => {
816
899
  const msg = `No abilities registered for ${model}/${action} — blocking everything. Register the ability or ignore this warning if intentional.`;
817
900
  if (logger) logger.warn({
@@ -859,8 +942,7 @@ function traceAbilityPreparation({ otel, log, telemetryConfig }, tableName, acti
859
942
  });
860
943
  return run();
861
944
  }
862
- //#endregion
863
- //#region lib/abilityBuilder/tableAbilities.ts
945
+ // lib/abilityBuilder/tableAbilities.ts
864
946
  /**
865
947
  * filter() is async. Without type checking (plain JS, `any`) a forgotten await would
866
948
  * hand the promise itself to drizzle
@@ -918,7 +1000,23 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
918
1000
  const blockEverythingFilter = createBlockEverythingFilter(String(tableName), tableSchema);
919
1001
  const resolveColumnAccess = createColumnAccessResolver(settings, tableSchema);
920
1002
  const transformToResponse = createFilterResponder(settings, tableSchema);
921
- const assembleAbilities = async (userContext, action, attributes) => {
1003
+ const resolveRules = createRuleResolver(settings, tableSchema);
1004
+ const evaluateDynamicFilters = (userContext, action) => Promise.all(dynamicQueryFilters[action].map((func) => func(userContext)));
1005
+ /** The response for the rules applying to the caller: their OR, columns and flags. */
1006
+ const respondWithFilters = (action, allQueryFilters) => {
1007
+ const mergedFilters = allQueryFilters.length === 1 ? { ...allQueryFilters[0] } : allQueryFilters.reduce((a, b) => {
1008
+ return mergeFilters(a, b, "OR");
1009
+ });
1010
+ const { columns, extras, mask, visibility } = resolveColumnAccess(action, allQueryFilters);
1011
+ if (columns) mergedFilters.columns = columns;
1012
+ else delete mergedFilters.columns;
1013
+ if (extras) mergedFilters.extras = {
1014
+ ...mergedFilters.extras,
1015
+ ...extras
1016
+ };
1017
+ return transformToResponse(mergedFilters, mask, visibility);
1018
+ };
1019
+ const assembleAbilities = async (action, attributes, evaluate) => {
922
1020
  const filters = queryFilters.get(action);
923
1021
  if (filters === "unrestricted") {
924
1022
  attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
@@ -929,7 +1027,7 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
929
1027
  settings.warnNothingRegistered(String(tableName), action);
930
1028
  return transformToResponse(blockEverythingFilter);
931
1029
  }
932
- const rawResults = await Promise.all(dynamicQueryFilters[action].map((func) => func(userContext)));
1030
+ const rawResults = await evaluate();
933
1031
  if (rawResults.includes("allow")) {
934
1032
  attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
935
1033
  return transformToResponse();
@@ -937,28 +1035,32 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
937
1035
  const dynamicResults = rawResults.filter((r) => r !== void 0).map(withRestrictingWhere);
938
1036
  attributes[ATTR_ABILITIES_DYNAMIC] = dynamicResults.length;
939
1037
  attributes[ATTR_ABILITIES_STATIC] = simpleQueryFilters[action].length;
940
- const allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
1038
+ let allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
941
1039
  attributes[ATTR_ABILITIES_TOTAL] = allQueryFilters.length;
1040
+ if (resolveRules && allQueryFilters.length > 0) {
1041
+ const resolution = await resolveRules(allQueryFilters);
1042
+ allQueryFilters = resolution.filters;
1043
+ attributes[ATTR_ABILITIES_RESOLVED] = resolution.resolved;
1044
+ }
942
1045
  if (allQueryFilters.length === 0) {
943
1046
  attributes[ATTR_ABILITIES_STATUS] = "blocked_everything";
944
1047
  return transformToResponse(blockEverythingFilter);
945
1048
  }
946
- const mergedFilters = allQueryFilters.length === 1 ? { ...allQueryFilters[0] } : allQueryFilters.reduce((a, b) => {
947
- return mergeFilters(a, b, "OR");
948
- });
949
- const { columns, extras, mask, visibility } = resolveColumnAccess(action, allQueryFilters);
950
- if (columns) mergedFilters.columns = columns;
951
- else delete mergedFilters.columns;
952
- if (extras) mergedFilters.extras = {
953
- ...mergedFilters.extras,
954
- ...extras
955
- };
956
1049
  attributes[ATTR_ABILITIES_STATUS] = "applied";
957
- return transformToResponse(mergedFilters, mask, visibility);
1050
+ return respondWithFilters(action, allQueryFilters);
958
1051
  };
959
1052
  return { withContext: (userContext) => {
960
- const prepare = (action) => traceAbilityPreparation(settings, String(tableName), action, (attributes) => assembleAbilities(userContext, action, attributes));
961
- const cache = /* @__PURE__ */ new Map();
1053
+ const evaluated = /* @__PURE__ */ new Map();
1054
+ const evaluate = (action) => {
1055
+ let results = evaluated.get(action);
1056
+ if (!results) {
1057
+ results = evaluateDynamicFilters(userContext, action);
1058
+ evaluated.set(action, results);
1059
+ }
1060
+ return results;
1061
+ };
1062
+ const prepare = (action) => traceAbilityPreparation(settings, String(tableName), action, (attributes) => assembleAbilities(action, attributes, () => evaluate(action)));
1063
+ let cache = /* @__PURE__ */ new Map();
962
1064
  const resolved = /* @__PURE__ */ new Map();
963
1065
  const abilities = { filter: (action) => {
964
1066
  let prepared = cache.get(action);
@@ -970,11 +1072,13 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
970
1072
  return prepared;
971
1073
  } };
972
1074
  abilities[resolvedFilterKey] = (action) => resolved.get(action);
1075
+ abilities[refreshAbilitiesKey] = () => {
1076
+ if (resolveRules) cache = /* @__PURE__ */ new Map();
1077
+ };
973
1078
  return abilities;
974
1079
  } };
975
1080
  };
976
- //#endregion
977
- //#region lib/abilityBuilder/inputGuard.ts
1081
+ // lib/abilityBuilder/inputGuard.ts
978
1082
  /** The ts names of the columns a relation joins on, on either side. */
979
1083
  function relationColumnKeys(db, table, columns) {
980
1084
  const known = tableHelper({
@@ -1107,8 +1211,7 @@ async function relationExposure(abilities, table, action, columns) {
1107
1211
  const guard = abilities[guardInputKey];
1108
1212
  return guard ? guard.exposed(table, action, columns) : [];
1109
1213
  }
1110
- //#endregion
1111
- //#region lib/abilityBuilder/tableBuilder.ts
1214
+ // lib/abilityBuilder/tableBuilder.ts
1112
1215
  /**
1113
1216
  * Creates the registration API (allow/filter) of a single table.
1114
1217
  */
@@ -1192,9 +1295,20 @@ when: (queryFilter) => {
1192
1295
  }
1193
1296
  };
1194
1297
  };
1195
- //#endregion
1196
- //#region lib/abilityBuilder/index.ts
1197
- const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerConfig }) => {
1298
+ // lib/abilityBuilder/refresh.ts
1299
+ /**
1300
+ * Starts a new snapshot of the resolved relational rules of every table, see
1301
+ * `resolveRelationalRules`. Rumble calls this whenever a root field starts to
1302
+ * resolve. Call it yourself in a resolver that writes rows and then reads them
1303
+ * through a table whose rules it already resolved before the write. A no-op
1304
+ * unless rules are resolved.
1305
+ */
1306
+ function refreshAbilities(abilities) {
1307
+ if (!abilities) return;
1308
+ for (const table of Object.values(abilities)) table?.[refreshAbilitiesKey]?.();
1309
+ }
1310
+ // lib/abilityBuilder/index.ts
1311
+ const createAbilityBuilder = ({ db, actions, defaultLimit, resolveRelationalRules, otel, logger: loggerConfig }) => {
1198
1312
  const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
1199
1313
  const telemetryConfig = {
1200
1314
  otel,
@@ -1204,6 +1318,7 @@ const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerC
1204
1318
  db,
1205
1319
  actions,
1206
1320
  defaultLimit,
1321
+ resolveRelationalRules,
1207
1322
  otel,
1208
1323
  telemetryConfig,
1209
1324
  log,
@@ -1270,8 +1385,7 @@ const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerC
1270
1385
  }
1271
1386
  };
1272
1387
  };
1273
- //#endregion
1274
- //#region lib/helpers/asserts.ts
1388
+ // lib/helpers/asserts.ts
1275
1389
  /**
1276
1390
  *
1277
1391
  * Helper function to map a drizzle findFirst query result,
@@ -1350,8 +1464,7 @@ const assertFirstEntryExists = (value) => {
1350
1464
  if (!v) throw new RumbleErrorSafe("Value not found but required (firstEntry)");
1351
1465
  return v;
1352
1466
  };
1353
- //#endregion
1354
- //#region lib/helpers/mapNullFieldsToUndefined.ts
1467
+ // lib/helpers/mapNullFieldsToUndefined.ts
1355
1468
  /**
1356
1469
  * Helper to map null fields to undefined
1357
1470
  * @param obj The object to map
@@ -1413,11 +1526,9 @@ where: { id: user.id },
1413
1526
  function mapNullFieldsToUndefined(obj) {
1414
1527
  return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, value === null ? void 0 : value]));
1415
1528
  }
1416
- //#endregion
1417
- //#region package.json
1418
- var version = "0.27.0";
1419
- //#endregion
1420
- //#region lib/args/orderArg.ts
1529
+ // package.json
1530
+ var version = "0.27.4";
1531
+ // lib/args/orderArg.ts
1421
1532
  const makeDefaultName$1 = (dbName) => `${capitalize(toCamelCase(dbName.toString()))}OrderInputArgument`;
1422
1533
  const createOrderArgImplementer = ({ db, schemaBuilder }) => {
1423
1534
  const referenceStorage = /* @__PURE__ */ new Map();
@@ -1448,8 +1559,7 @@ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
1448
1559
  };
1449
1560
  return orderArgImplementer;
1450
1561
  };
1451
- //#endregion
1452
- //#region lib/enum.ts
1562
+ // lib/enum.ts
1453
1563
  /**
1454
1564
  * Checks if a column is a database enum column (Postgres or MySQL).
1455
1565
  */
@@ -1509,8 +1619,7 @@ const createEnumImplementer = ({ schema, schemaBuilder }) => {
1509
1619
  };
1510
1620
  return enumImplementer;
1511
1621
  };
1512
- //#endregion
1513
- //#region lib/helpers/sqlTypes/mapSQLTypeToTSType.ts
1622
+ // lib/helpers/sqlTypes/mapSQLTypeToTSType.ts
1514
1623
  function mapSQLTypeToGraphQLType({ sqlType, fieldName }) {
1515
1624
  if (/^enum\(/.test(sqlType)) return "String";
1516
1625
  const normalized = normalizeSQLType(sqlType);
@@ -1532,8 +1641,7 @@ function mapSQLTypeToGraphQLType({ sqlType, fieldName }) {
1532
1641
  if (ret !== void 0) return ret;
1533
1642
  throw UnknownTypeRumbleError(sqlType, "SQL to GQL");
1534
1643
  }
1535
- //#endregion
1536
- //#region lib/args/whereArg.ts
1644
+ // lib/args/whereArg.ts
1537
1645
  const makeDefaultName = (dbName) => `${capitalize(toCamelCase(dbName.toString()))}WhereInputArgument`;
1538
1646
  const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
1539
1647
  const referenceStorage = /* @__PURE__ */ new Map();
@@ -1758,8 +1866,7 @@ const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
1758
1866
  };
1759
1867
  return whereArgImplementer;
1760
1868
  };
1761
- //#endregion
1762
- //#region lib/client/client.ts
1869
+ // lib/client/client.ts
1763
1870
  const clientCreatorImplementer = ({ builtSchema }) => {
1764
1871
  const clientCreator = async ({ apiUrl, outputPath, rumbleImportPath, useExternalUrqlClient, removeExisting, forceReactivity, autoIncludeIdField = false }) => {
1765
1872
  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({
@@ -1783,8 +1890,7 @@ const clientCreatorImplementer = ({ builtSchema }) => {
1783
1890
  };
1784
1891
  return clientCreator;
1785
1892
  };
1786
- //#endregion
1787
- //#region lib/context.ts
1893
+ // lib/context.ts
1788
1894
  const createContextFunction = ({ context: makeUserContext, abilityBuilder }) => {
1789
1895
  const builtAbilityBuilder = once(() => abilityBuilder._.build());
1790
1896
  return async (req) => {
@@ -1795,8 +1901,7 @@ const createContextFunction = ({ context: makeUserContext, abilityBuilder }) =>
1795
1901
  };
1796
1902
  };
1797
1903
  };
1798
- //#endregion
1799
- //#region lib/helpers/protoMapper.ts
1904
+ // lib/helpers/protoMapper.ts
1800
1905
  function deepSetProto(obj, proto = Object.prototype, seen = /* @__PURE__ */ new WeakSet()) {
1801
1906
  if (obj === null || typeof obj !== "object") return;
1802
1907
  if (seen.has(obj)) return;
@@ -1810,8 +1915,7 @@ function deepSetProto(obj, proto = Object.prototype, seen = /* @__PURE__ */ new
1810
1915
  Object.setPrototypeOf(obj, proto);
1811
1916
  for (const key of Object.keys(obj)) deepSetProto(obj[key], proto, seen);
1812
1917
  }
1813
- //#endregion
1814
- //#region lib/countQuery.ts
1918
+ // lib/countQuery.ts
1815
1919
  const createCountQueryImplementer = ({ db, schemaBuilder, whereArgImplementer, makePubSubInstance }) => {
1816
1920
  return ({ table, listAction = "read", isAllowed }) => {
1817
1921
  const WhereArg = whereArgImplementer({ table });
@@ -1851,8 +1955,7 @@ const createCountQueryImplementer = ({ db, schemaBuilder, whereArgImplementer, m
1851
1955
  });
1852
1956
  };
1853
1957
  };
1854
- //#endregion
1855
- //#region lib/helpers/sofaOpenAPIWebhookDocs.ts
1958
+ // lib/helpers/sofaOpenAPIWebhookDocs.ts
1856
1959
  const sofaOpenAPIWebhookDocs = {
1857
1960
  paths: {
1858
1961
  "/webhook": { post: {
@@ -1936,8 +2039,7 @@ const sofaOpenAPIWebhookDocs = {
1936
2039
  }
1937
2040
  } }
1938
2041
  };
1939
- //#endregion
1940
- //#region lib/helpers/tracing.ts
2042
+ // lib/helpers/tracing.ts
1941
2043
  function isAsyncIterable(value) {
1942
2044
  return typeof value === "object" && value !== null && Symbol.asyncIterator in value;
1943
2045
  }
@@ -2125,8 +2227,7 @@ function buildTracedSubscribe(subscribeFn, rumbleInput, transport) {
2125
2227
  });
2126
2228
  };
2127
2229
  }
2128
- //#endregion
2129
- //#region lib/helpers/sqlTypes/mapDrizzleTypeToGraphQlType.ts
2230
+ // lib/helpers/sqlTypes/mapDrizzleTypeToGraphQlType.ts
2130
2231
  function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, nullable, isArray }) {
2131
2232
  const gqlType = mapSQLTypeToGraphQLType({
2132
2233
  sqlType,
@@ -2174,33 +2275,7 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
2174
2275
  default: throw new RumbleError(`Unsupported object type ${gqlType} for column ${fieldName}`);
2175
2276
  }
2176
2277
  }
2177
- //#endregion
2178
- //#region lib/helpers/determineDialectFromSchema.ts
2179
- const dialectCache = /* @__PURE__ */ new WeakMap();
2180
- function determineDBDialectFromSchema(schema) {
2181
- const cached = dialectCache.get(schema);
2182
- if (cached) return cached;
2183
- const found = /* @__PURE__ */ new Set();
2184
- for (const table of Object.values(schema).map((t) => t.table)) {
2185
- if (!table || typeof table !== "object") continue;
2186
- if (is(table, PgTable)) found.add("postgres");
2187
- else if (is(table, MySqlTable)) found.add("mysql");
2188
- else if (is(table, SQLiteTable)) found.add("sqlite");
2189
- }
2190
- const dialects = Array.from(found);
2191
- if (dialects.length === 1) {
2192
- const only = dialects[0];
2193
- dialectCache.set(schema, only);
2194
- return only;
2195
- }
2196
- if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
2197
- throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
2198
- }
2199
- function isPostgresDB(db) {
2200
- return determineDBDialectFromSchema(db._.relations) === "postgres";
2201
- }
2202
- //#endregion
2203
- //#region lib/search.ts
2278
+ // lib/search.ts
2204
2279
  async function initSearchIfApplicable(input) {
2205
2280
  const log = input.logger?.enabled ? input.logger.logger : void 0;
2206
2281
  if (!isPostgresDB(input.db)) {
@@ -2260,8 +2335,7 @@ function adjustQueryArgsForSearch({ search, args, tableSchema, abilities }) {
2260
2335
  }), sql.raw(" OR "))})` }] };
2261
2336
  }
2262
2337
  }
2263
- //#endregion
2264
- //#region lib/object.ts
2338
+ // lib/object.ts
2265
2339
  const isProbablyAConfigObject = (t) => {
2266
2340
  if (typeof t !== "object") return false;
2267
2341
  if (Object.keys(t).some((k) => [
@@ -2489,8 +2563,7 @@ const createObjectImplementer = ({ db, search, otel, logger: loggerConfig, schem
2489
2563
  });
2490
2564
  };
2491
2565
  };
2492
- //#endregion
2493
- //#region lib/pubsub.ts
2566
+ // lib/pubsub.ts
2494
2567
  const SUBSCRIPTION_NOTIFIER_RUMBLE_PREFIX = "RUMBLE_SUBSCRIPTION_NOTIFICATION";
2495
2568
  const SUBSCRIPTION_NOTIFIER_REMOVED = "REMOVED";
2496
2569
  const SUBSCRIPTION_NOTIFIER_UPDATED = "UPDATED";
@@ -2565,8 +2638,7 @@ const createPubSubInstance = ({ subscriptions }) => {
2565
2638
  makePubSubInstance
2566
2639
  };
2567
2640
  };
2568
- //#endregion
2569
- //#region lib/query.ts
2641
+ // lib/query.ts
2570
2642
  const createQueryImplementer = ({ db, schemaBuilder, search, otel, logger: loggerConfig, whereArgImplementer, orderArgImplementer, makePubSubInstance }) => {
2571
2643
  const registeredQueryFieldNames = /* @__PURE__ */ new Set();
2572
2644
  const telemetryConfig = {
@@ -2681,8 +2753,7 @@ const createQueryImplementer = ({ db, schemaBuilder, search, otel, logger: logge
2681
2753
  });
2682
2754
  };
2683
2755
  };
2684
- //#endregion
2685
- //#region lib/args/whereArgsImplementer.ts
2756
+ // lib/args/whereArgsImplementer.ts
2686
2757
  function implementDefaultWhereInputArgs(schemaBuilder) {
2687
2758
  const IntWhereInputArgument = schemaBuilder.inputRef("IntWhereInputArgument").implement({ fields: (t) => ({
2688
2759
  eq: t.int(),
@@ -2934,16 +3005,14 @@ function implementDefaultWhereInputArgs(schemaBuilder) {
2934
3005
  NOT: t.field({ type: DateTimeWhereInputArgument })
2935
3006
  }) });
2936
3007
  }
2937
- //#endregion
2938
- //#region lib/helpers/objectTypeOptions.ts
3008
+ // lib/helpers/objectTypeOptions.ts
2939
3009
  function objectTypeOptionsOfField(buildCache, fieldConfig) {
2940
3010
  const type = fieldConfig.type;
2941
3011
  const ref = type.kind === "List" ? type.type.kind === "Object" ? type.type.ref : void 0 : type.kind === "Object" ? type.ref : void 0;
2942
3012
  if (!ref) return void 0;
2943
3013
  return buildCache.getTypeConfig(ref, "Object").pothosOptions;
2944
3014
  }
2945
- //#endregion
2946
- //#region lib/helpers/registerPlugin.ts
3015
+ // lib/helpers/registerPlugin.ts
2947
3016
  const registered = /* @__PURE__ */ new Set();
2948
3017
  function registerPluginOnce(name, plugin) {
2949
3018
  if (registered.has(name)) return;
@@ -2951,15 +3020,13 @@ function registerPluginOnce(name, plugin) {
2951
3020
  SchemaBuilder.registerPlugin(name, plugin);
2952
3021
  registered.add(name);
2953
3022
  }
2954
- //#endregion
2955
- //#region lib/columnMaskPlugin/maskTypes.ts
3023
+ // lib/columnMaskPlugin/maskTypes.ts
2956
3024
  const maskColumnsKey = "maskColumns";
2957
3025
  const maskColumnsActionKey = "maskColumnsAction";
2958
3026
  const maskPrimaryKeysKey = "maskPrimaryKeys";
2959
3027
  const maskRelationsKey = "maskRelations";
2960
3028
  const columnMaskPluginName = "ColumnMaskPlugin";
2961
- //#endregion
2962
- //#region lib/columnMaskPlugin/columnMaskPlugin.ts
3029
+ // lib/columnMaskPlugin/columnMaskPlugin.ts
2963
3030
  var ColumnMaskPlugin = class extends BasePlugin {
2964
3031
  wrapResolve(resolver, fieldConfig) {
2965
3032
  return this.maskResult(this.maskRelation(this.maskPrimaryKey(resolver, fieldConfig), fieldConfig), fieldConfig);
@@ -2998,8 +3065,7 @@ var ColumnMaskPlugin = class extends BasePlugin {
2998
3065
  function registerColumnMaskPlugin() {
2999
3066
  registerPluginOnce(columnMaskPluginName, ColumnMaskPlugin);
3000
3067
  }
3001
- //#endregion
3002
- //#region lib/helpers/abilityAwareClient.ts
3068
+ // lib/helpers/abilityAwareClient.ts
3003
3069
  function createAbilityAwareClient({ db, abilityBuilder }) {
3004
3070
  return (context) => {
3005
3071
  const apis = /* @__PURE__ */ new Map();
@@ -3043,11 +3109,31 @@ function createAbilityAwareClient({ db, abilityBuilder }) {
3043
3109
  } });
3044
3110
  };
3045
3111
  }
3046
- //#endregion
3047
- //#region lib/runtimeFiltersPlugin/filterTypes.ts
3112
+ // lib/refreshAbilitiesPlugin/refreshAbilitiesPlugin.ts
3113
+ const refreshAbilitiesPluginName = "RefreshAbilitiesPlugin";
3114
+ /**
3115
+ * Starts a new snapshot of the resolved relational rules whenever a root
3116
+ * field (query, mutation or subscription event) starts to resolve, see
3117
+ * `resolveRelationalRules`. A mutation's second root field sees what the first
3118
+ * wrote, and a live query refetching sees the rows related to the caller by
3119
+ * now. A no-op unless rules are resolved.
3120
+ */
3121
+ var RefreshAbilitiesPlugin = class extends BasePlugin {
3122
+ wrapResolve(resolver, fieldConfig) {
3123
+ const { kind } = this.buildCache.getTypeConfig(fieldConfig.parentType);
3124
+ if (kind !== "Query" && kind !== "Mutation" && kind !== "Subscription") return resolver;
3125
+ return (parent, args, context, info) => {
3126
+ refreshAbilities(context?.abilities);
3127
+ return resolver(parent, args, context, info);
3128
+ };
3129
+ }
3130
+ };
3131
+ function registerRefreshAbilitiesPlugin() {
3132
+ registerPluginOnce(refreshAbilitiesPluginName, RefreshAbilitiesPlugin);
3133
+ }
3134
+ // lib/runtimeFiltersPlugin/filterTypes.ts
3048
3135
  const pluginName = "RuntimeFiltersPlugin";
3049
- //#endregion
3050
- //#region lib/runtimeFiltersPlugin/runtimeFiltersPlugin.ts
3136
+ // lib/runtimeFiltersPlugin/runtimeFiltersPlugin.ts
3051
3137
  const applyFiltersKey = "applyFilters";
3052
3138
  var RuntimeFiltersPlugin = class extends BasePlugin {
3053
3139
  tracer;
@@ -3149,8 +3235,7 @@ var RuntimeFiltersPlugin = class extends BasePlugin {
3149
3235
  function registerRuntimeFiltersPlugin() {
3150
3236
  registerPluginOnce(pluginName, RuntimeFiltersPlugin);
3151
3237
  }
3152
- //#endregion
3153
- //#region lib/scalars/addressData.ts
3238
+ // lib/scalars/addressData.ts
3154
3239
  /*! Address data derived from Google's Address Data Service (https://github.com/google/libaddressinput),
3155
3240
  * licensed under CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/).
3156
3241
  * Changes: reduced to required fields, postal code patterns and examples, and regions with
@@ -8962,8 +9047,7 @@ const addressData = {
8962
9047
  },
8963
9048
  ZW: { require: "AC" }
8964
9049
  };
8965
- //#endregion
8966
- //#region lib/scalars/addressValidation.ts
9050
+ // lib/scalars/addressValidation.ts
8967
9051
  const REQUIRED_FIELD_PATHS = {
8968
9052
  A: "streetAddress",
8969
9053
  C: "locality",
@@ -9093,8 +9177,7 @@ const addressValidationSchema = z.object({
9093
9177
  message: issue.message
9094
9178
  });
9095
9179
  });
9096
- //#endregion
9097
- //#region lib/scalars/address.ts
9180
+ // lib/scalars/address.ts
9098
9181
  function implementDefaultAddressTypes(schemaBuilder) {
9099
9182
  return {
9100
9183
  Address: schemaBuilder.objectRef("Address").implement({
@@ -9120,8 +9203,7 @@ function implementDefaultAddressTypes(schemaBuilder) {
9120
9203
  })
9121
9204
  };
9122
9205
  }
9123
- //#endregion
9124
- //#region lib/scalars/emailAddress.ts
9206
+ // lib/scalars/emailAddress.ts
9125
9207
  /**
9126
9208
  * Validates an email address using graphql-scalars' own format check, then lowercases it, exactly
9127
9209
  * as the `EmailAddress` scalar does. Exported for values that do not arrive as GraphQL input,
@@ -9147,8 +9229,7 @@ const EmailAddressResolver$1 = new GraphQLScalarType({
9147
9229
  return normalizeEmailAddress(ast.value);
9148
9230
  }
9149
9231
  });
9150
- //#endregion
9151
- //#region lib/scalars/personName.ts
9232
+ // lib/scalars/personName.ts
9152
9233
  const MAX_LENGTH = 200;
9153
9234
  const NAME_REGEX = /^[\p{L}\p{M}]+(?:(?:[ '-]|\.\x20?)[\p{L}\p{M}]+)*\.?$/u;
9154
9235
  const APOSTROPHE_VARIANTS = /[\u2018\u2019\u02BC\u00B4\u0060]/gu;
@@ -9177,8 +9258,7 @@ const PersonNameResolver = new GraphQLScalarType({
9177
9258
  return normalizePersonName(ast.value);
9178
9259
  }
9179
9260
  });
9180
- //#endregion
9181
- //#region lib/scalars/phoneNumber.ts
9261
+ // lib/scalars/phoneNumber.ts
9182
9262
  /**
9183
9263
  * Parses and validates a phone number using libphonenumber-js (Google's phone number metadata),
9184
9264
  * which checks real per-country validity (length, area code plausibility, ...) rather than just
@@ -9215,16 +9295,17 @@ const PhoneNumberResolver = new GraphQLScalarType({
9215
9295
  return parsePhoneNumber(ast.value);
9216
9296
  }
9217
9297
  });
9218
- //#endregion
9219
- //#region lib/schemaBuilder.ts
9298
+ // lib/schemaBuilder.ts
9220
9299
  const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger, validation, abilityBuilder }) => {
9221
9300
  const createSpan = otel?.enabled && otel.tracer ? createOpenTelemetryWrapper(otel.tracer, otel.options) : void 0;
9222
9301
  registerRuntimeFiltersPlugin();
9223
9302
  registerColumnMaskPlugin();
9303
+ registerRefreshAbilitiesPlugin();
9224
9304
  const schemaBuilder = new SchemaBuilder({
9225
9305
  ...pothosConfig,
9226
9306
  plugins: [
9227
9307
  columnMaskPluginName,
9308
+ refreshAbilitiesPluginName,
9228
9309
  pluginName,
9229
9310
  DrizzlePlugin,
9230
9311
  SmartSubscriptionsPlugin,
@@ -9239,8 +9320,8 @@ const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger, validatio
9239
9320
  }),
9240
9321
  relations: db._.relations,
9241
9322
  getTableConfig(table) {
9242
- if (is(table, PgTable)) return getTableConfig(table);
9243
- if (is(table, MySqlTable)) return getTableConfig$1(table);
9323
+ if (is(table, PgTable)) return getTableConfig$1(table);
9324
+ if (is(table, MySqlTable)) return getTableConfig(table);
9244
9325
  if (is(table, SQLiteTable)) return getTableConfig$2(table);
9245
9326
  return {
9246
9327
  columns: isTable(table) ? Object.values(getTableColumns(table)) : [],
@@ -9300,8 +9381,7 @@ const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger, validatio
9300
9381
  schemaBuilder.mutationType({});
9301
9382
  return { schemaBuilder };
9302
9383
  };
9303
- //#endregion
9304
- //#region lib/rumble.ts
9384
+ // lib/rumble.ts
9305
9385
  /**
9306
9386
  * Mirrors sofa's own default error handler: it derives the HTTP status from
9307
9387
  * `extensions.http` and returns the errors as JSON. rumble only adds telemetry
@@ -9629,7 +9709,6 @@ export const r = rumble({
9629
9709
  buildSchema: builtSchema
9630
9710
  };
9631
9711
  };
9632
- //#endregion
9633
- export { RumbleError, RumbleErrorSafe, addressChecks, addressData, addressValidationSchema, assertFindFirstExists, assertFirstEntryExists, checkPostalCode, checkRegion, checkRequiredFields, findRegionCode, guardFilterInput, mapNullFieldsToUndefined, normalizeAddress, normalizeEmailAddress, normalizePersonName, parsePhoneNumber, rumble, validateAddress };
9712
+ export { RumbleError, RumbleErrorSafe, addressChecks, addressData, addressValidationSchema, assertFindFirstExists, assertFirstEntryExists, checkPostalCode, checkRegion, checkRequiredFields, findRegionCode, guardFilterInput, mapNullFieldsToUndefined, normalizeAddress, normalizeEmailAddress, normalizePersonName, parsePhoneNumber, refreshAbilities, rumble, validateAddress };
9634
9713
 
9635
9714
  //# sourceMappingURL=index.mjs.map