@m1212e/rumble 0.27.3 → 0.27.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/index.cjs +199 -66
- package/out/index.cjs.map +1 -1
- package/out/index.d.cts +46 -0
- package/out/index.d.cts.map +1 -1
- package/out/index.d.mts +49 -3
- package/out/index.d.mts.map +1 -1
- package/out/index.mjs +202 -70
- package/out/index.mjs.map +1 -1
- package/package.json +1 -1
package/out/index.cjs
CHANGED
|
@@ -27,15 +27,15 @@ let es_toolkit = require("es-toolkit");
|
|
|
27
27
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
28
28
|
let _opentelemetry_semantic_conventions_incubating = require("@opentelemetry/semantic-conventions/incubating");
|
|
29
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");
|
|
30
33
|
let _escape_tech_graphql_armor = require("@escape.tech/graphql-armor");
|
|
31
34
|
let _graphql_yoga_plugin_disable_introspection = require("@graphql-yoga/plugin-disable-introspection");
|
|
32
35
|
let graphql_yoga = require("graphql-yoga");
|
|
33
36
|
let drizzle_orm_casing = require("drizzle-orm/casing");
|
|
34
|
-
let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
|
|
35
|
-
let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
|
|
36
37
|
let pluralize = require("pluralize");
|
|
37
38
|
pluralize = __toESM(pluralize, 1);
|
|
38
|
-
let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
|
|
39
39
|
let _pothos_core = require("@pothos/core");
|
|
40
40
|
_pothos_core = __toESM(_pothos_core, 1);
|
|
41
41
|
let _pothos_plugin_drizzle = require("@pothos/plugin-drizzle");
|
|
@@ -201,6 +201,7 @@ const columnMaskKey = Symbol.for("rumble:columnMask");
|
|
|
201
201
|
const columnVisibilityKey = Symbol.for("rumble:columnVisibility");
|
|
202
202
|
const guardInputKey = Symbol.for("rumble:guardInput");
|
|
203
203
|
const resolvedFilterKey = Symbol.for("rumble:resolvedFilter");
|
|
204
|
+
const refreshAbilitiesKey = Symbol.for("rumble:refreshAbilities");
|
|
204
205
|
const requestStateKey = Symbol.for("rumble:requestState");
|
|
205
206
|
// lib/abilityBuilder/requestState.ts
|
|
206
207
|
function createRequestState() {
|
|
@@ -218,14 +219,6 @@ function requestStateOf(abilities) {
|
|
|
218
219
|
return abilities?.[requestStateKey];
|
|
219
220
|
}
|
|
220
221
|
// lib/abilityBuilder/columnMask.ts
|
|
221
|
-
function referencesRelation(filter, relations) {
|
|
222
|
-
if (!filter || typeof filter !== "object") return false;
|
|
223
|
-
return Object.entries(filter).some(([key, value]) => {
|
|
224
|
-
if (key === "AND" || key === "OR") return value.some((v) => referencesRelation(v, relations));
|
|
225
|
-
if (key === "NOT") return referencesRelation(value, relations);
|
|
226
|
-
return key in relations;
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
222
|
function selectedColumns(columns, allColumns) {
|
|
230
223
|
if (!columns) return new Set(allColumns);
|
|
231
224
|
const entries = Object.entries(columns).filter(([, v]) => v !== void 0);
|
|
@@ -284,12 +277,7 @@ function conditionalGroups(rules, guaranteed) {
|
|
|
284
277
|
function createColumnAccessResolver({ db, actions }, tableSchema) {
|
|
285
278
|
const allColumnNames = Object.keys(tableSchema.columns);
|
|
286
279
|
const primaryKeyNames = Object.keys(tableSchema.primaryKey);
|
|
287
|
-
const primaryKeyName = primaryKeyNames[0];
|
|
288
280
|
const toSQL = (table, wheres) => (0, drizzle_orm.or)(...wheres.map((where) => (0, drizzle_orm.relationsFilterToSQL)(table, sanitizeFilterValue(where), tableSchema.relations, db._.relations)));
|
|
289
|
-
const uncorrelated = (table, wheres, group) => {
|
|
290
|
-
const inner = (0, drizzle_orm.aliasedTable)(tableSchema.table, `rumble_columns_${group}`);
|
|
291
|
-
return drizzle_orm.sql`exists ${db.select({ pk: inner[primaryKeyName] }).from(inner).where(drizzle_orm.sql`${inner[primaryKeyName]} = ${table[primaryKeyName]} and (${toSQL(inner, wheres)})`)}`;
|
|
292
|
-
};
|
|
293
281
|
return function resolveColumnAccess(action, filters) {
|
|
294
282
|
const rules = filters.map((f) => ({
|
|
295
283
|
where: realWhere(f?.where),
|
|
@@ -320,11 +308,7 @@ function createColumnAccessResolver({ db, actions }, tableSchema) {
|
|
|
320
308
|
const actionIndex = drizzle_orm.sql.raw(String(actions.indexOf(action)));
|
|
321
309
|
const extras = { [ACTION_FLAG_KEY]: () => drizzle_orm.sql`${actionIndex}` };
|
|
322
310
|
groups.forEach(({ wheres }, group) => {
|
|
323
|
-
|
|
324
|
-
extras[mask.flags[group]] = (table) => {
|
|
325
|
-
const condition = relational ? uncorrelated(table, wheres, group) : toSQL(table, wheres);
|
|
326
|
-
return drizzle_orm.sql`case when ${condition} then 1 else 0 end`;
|
|
327
|
-
};
|
|
311
|
+
extras[mask.flags[group]] = (table) => drizzle_orm.sql`case when ${toSQL(table, wheres)} then 1 else 0 end`;
|
|
328
312
|
});
|
|
329
313
|
return {
|
|
330
314
|
columns,
|
|
@@ -574,6 +558,7 @@ const ATTR_ABILITIES_STATUS = "rumble.abilities.status";
|
|
|
574
558
|
const ATTR_ABILITIES_STATIC = "rumble.abilities.static";
|
|
575
559
|
const ATTR_ABILITIES_DYNAMIC = "rumble.abilities.dynamic";
|
|
576
560
|
const ATTR_ABILITIES_TOTAL = "rumble.abilities.total";
|
|
561
|
+
const ATTR_ABILITIES_RESOLVED = "rumble.abilities.resolved";
|
|
577
562
|
const ATTR_FILTERS_TOTAL = "rumble.filters.total";
|
|
578
563
|
const ATTR_FILTERS_ALLOWED = "rumble.filters.allowed";
|
|
579
564
|
const ATTR_SUBSCRIPTION_EVENT_INDEX = "rumble.subscription.event_index";
|
|
@@ -796,6 +781,117 @@ function isDynamicQueryFilter(filter) {
|
|
|
796
781
|
function isStaticQueryFilter(filter) {
|
|
797
782
|
return typeof filter !== "function";
|
|
798
783
|
}
|
|
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
|
+
}
|
|
799
895
|
// lib/helpers/errorLogging.ts
|
|
800
896
|
function hasMessage(error) {
|
|
801
897
|
return typeof error === "object" && error !== null && "message" in error;
|
|
@@ -933,7 +1029,23 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
|
|
|
933
1029
|
const blockEverythingFilter = createBlockEverythingFilter(String(tableName), tableSchema);
|
|
934
1030
|
const resolveColumnAccess = createColumnAccessResolver(settings, tableSchema);
|
|
935
1031
|
const transformToResponse = createFilterResponder(settings, tableSchema);
|
|
936
|
-
const
|
|
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) => {
|
|
937
1049
|
const filters = queryFilters.get(action);
|
|
938
1050
|
if (filters === "unrestricted") {
|
|
939
1051
|
attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
|
|
@@ -944,7 +1056,7 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
|
|
|
944
1056
|
settings.warnNothingRegistered(String(tableName), action);
|
|
945
1057
|
return transformToResponse(blockEverythingFilter);
|
|
946
1058
|
}
|
|
947
|
-
const rawResults = await
|
|
1059
|
+
const rawResults = await evaluate();
|
|
948
1060
|
if (rawResults.includes("allow")) {
|
|
949
1061
|
attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
|
|
950
1062
|
return transformToResponse();
|
|
@@ -952,28 +1064,32 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
|
|
|
952
1064
|
const dynamicResults = rawResults.filter((r) => r !== void 0).map(withRestrictingWhere);
|
|
953
1065
|
attributes[ATTR_ABILITIES_DYNAMIC] = dynamicResults.length;
|
|
954
1066
|
attributes[ATTR_ABILITIES_STATIC] = simpleQueryFilters[action].length;
|
|
955
|
-
|
|
1067
|
+
let allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
|
|
956
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
|
+
}
|
|
957
1074
|
if (allQueryFilters.length === 0) {
|
|
958
1075
|
attributes[ATTR_ABILITIES_STATUS] = "blocked_everything";
|
|
959
1076
|
return transformToResponse(blockEverythingFilter);
|
|
960
1077
|
}
|
|
961
|
-
const mergedFilters = allQueryFilters.length === 1 ? { ...allQueryFilters[0] } : allQueryFilters.reduce((a, b) => {
|
|
962
|
-
return mergeFilters(a, b, "OR");
|
|
963
|
-
});
|
|
964
|
-
const { columns, extras, mask, visibility } = resolveColumnAccess(action, allQueryFilters);
|
|
965
|
-
if (columns) mergedFilters.columns = columns;
|
|
966
|
-
else delete mergedFilters.columns;
|
|
967
|
-
if (extras) mergedFilters.extras = {
|
|
968
|
-
...mergedFilters.extras,
|
|
969
|
-
...extras
|
|
970
|
-
};
|
|
971
1078
|
attributes[ATTR_ABILITIES_STATUS] = "applied";
|
|
972
|
-
return
|
|
1079
|
+
return respondWithFilters(action, allQueryFilters);
|
|
973
1080
|
};
|
|
974
1081
|
return { withContext: (userContext) => {
|
|
975
|
-
const
|
|
976
|
-
const
|
|
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();
|
|
977
1093
|
const resolved = /* @__PURE__ */ new Map();
|
|
978
1094
|
const abilities = { filter: (action) => {
|
|
979
1095
|
let prepared = cache.get(action);
|
|
@@ -985,6 +1101,9 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
|
|
|
985
1101
|
return prepared;
|
|
986
1102
|
} };
|
|
987
1103
|
abilities[resolvedFilterKey] = (action) => resolved.get(action);
|
|
1104
|
+
abilities[refreshAbilitiesKey] = () => {
|
|
1105
|
+
if (resolveRules) cache = /* @__PURE__ */ new Map();
|
|
1106
|
+
};
|
|
988
1107
|
return abilities;
|
|
989
1108
|
} };
|
|
990
1109
|
};
|
|
@@ -1089,11 +1208,11 @@ function createInputGuard({ db, abilities, readActionOf }) {
|
|
|
1089
1208
|
db,
|
|
1090
1209
|
table
|
|
1091
1210
|
});
|
|
1092
|
-
return (aliased, { asc, desc }) => entries.map(([key, direction]) => {
|
|
1211
|
+
return (aliased, { asc, desc }) => entries.filter(([key]) => visibility(key) !== false).map(([key, direction]) => {
|
|
1093
1212
|
const condition = visibility(key);
|
|
1094
1213
|
const column = aliased[key];
|
|
1095
|
-
const value = condition === true ? column :
|
|
1096
|
-
return (direction === "asc" ? asc : desc)(value
|
|
1214
|
+
const value = condition === true ? column : drizzle_orm.sql`case when ${(0, drizzle_orm.relationsFilterToSQL)(aliased, sanitizeFilterValue(condition), schema.relations, db._.relations)} then ${column} end`;
|
|
1215
|
+
return (direction === "asc" ? asc : desc)(value);
|
|
1097
1216
|
});
|
|
1098
1217
|
};
|
|
1099
1218
|
return {
|
|
@@ -1205,8 +1324,20 @@ when: (queryFilter) => {
|
|
|
1205
1324
|
}
|
|
1206
1325
|
};
|
|
1207
1326
|
};
|
|
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
|
+
}
|
|
1208
1339
|
// lib/abilityBuilder/index.ts
|
|
1209
|
-
const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerConfig }) => {
|
|
1340
|
+
const createAbilityBuilder = ({ db, actions, defaultLimit, resolveRelationalRules, otel, logger: loggerConfig }) => {
|
|
1210
1341
|
const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
|
|
1211
1342
|
const telemetryConfig = {
|
|
1212
1343
|
otel,
|
|
@@ -1216,6 +1347,7 @@ const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerC
|
|
|
1216
1347
|
db,
|
|
1217
1348
|
actions,
|
|
1218
1349
|
defaultLimit,
|
|
1350
|
+
resolveRelationalRules,
|
|
1219
1351
|
otel,
|
|
1220
1352
|
telemetryConfig,
|
|
1221
1353
|
log,
|
|
@@ -1424,7 +1556,7 @@ function mapNullFieldsToUndefined(obj) {
|
|
|
1424
1556
|
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, value === null ? void 0 : value]));
|
|
1425
1557
|
}
|
|
1426
1558
|
// package.json
|
|
1427
|
-
var version = "0.27.
|
|
1559
|
+
var version = "0.27.5";
|
|
1428
1560
|
// lib/args/orderArg.ts
|
|
1429
1561
|
const makeDefaultName$1 = (dbName) => `${(0, es_toolkit.capitalize)((0, drizzle_orm_casing.toCamelCase)(dbName.toString()))}OrderInputArgument`;
|
|
1430
1562
|
const createOrderArgImplementer = ({ db, schemaBuilder }) => {
|
|
@@ -2172,30 +2304,6 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
|
|
|
2172
2304
|
default: throw new RumbleError(`Unsupported object type ${gqlType} for column ${fieldName}`);
|
|
2173
2305
|
}
|
|
2174
2306
|
}
|
|
2175
|
-
// lib/helpers/determineDialectFromSchema.ts
|
|
2176
|
-
const dialectCache = /* @__PURE__ */ new WeakMap();
|
|
2177
|
-
function determineDBDialectFromSchema(schema) {
|
|
2178
|
-
const cached = dialectCache.get(schema);
|
|
2179
|
-
if (cached) return cached;
|
|
2180
|
-
const found = /* @__PURE__ */ new Set();
|
|
2181
|
-
for (const table of Object.values(schema).map((t) => t.table)) {
|
|
2182
|
-
if (!table || typeof table !== "object") continue;
|
|
2183
|
-
if ((0, drizzle_orm.is)(table, drizzle_orm_pg_core.PgTable)) found.add("postgres");
|
|
2184
|
-
else if ((0, drizzle_orm.is)(table, drizzle_orm_mysql_core.MySqlTable)) found.add("mysql");
|
|
2185
|
-
else if ((0, drizzle_orm.is)(table, drizzle_orm_sqlite_core.SQLiteTable)) found.add("sqlite");
|
|
2186
|
-
}
|
|
2187
|
-
const dialects = Array.from(found);
|
|
2188
|
-
if (dialects.length === 1) {
|
|
2189
|
-
const only = dialects[0];
|
|
2190
|
-
dialectCache.set(schema, only);
|
|
2191
|
-
return only;
|
|
2192
|
-
}
|
|
2193
|
-
if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
|
|
2194
|
-
throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
|
|
2195
|
-
}
|
|
2196
|
-
function isPostgresDB(db) {
|
|
2197
|
-
return determineDBDialectFromSchema(db._.relations) === "postgres";
|
|
2198
|
-
}
|
|
2199
2307
|
// lib/search.ts
|
|
2200
2308
|
async function initSearchIfApplicable(input) {
|
|
2201
2309
|
const log = input.logger?.enabled ? input.logger.logger : void 0;
|
|
@@ -3030,6 +3138,28 @@ function createAbilityAwareClient({ db, abilityBuilder }) {
|
|
|
3030
3138
|
} });
|
|
3031
3139
|
};
|
|
3032
3140
|
}
|
|
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
|
+
}
|
|
3033
3163
|
// lib/runtimeFiltersPlugin/filterTypes.ts
|
|
3034
3164
|
const pluginName = "RuntimeFiltersPlugin";
|
|
3035
3165
|
// lib/runtimeFiltersPlugin/runtimeFiltersPlugin.ts
|
|
@@ -9199,10 +9329,12 @@ const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger, validatio
|
|
|
9199
9329
|
const createSpan = otel?.enabled && otel.tracer ? (0, _pothos_tracing_opentelemetry.createOpenTelemetryWrapper)(otel.tracer, otel.options) : void 0;
|
|
9200
9330
|
registerRuntimeFiltersPlugin();
|
|
9201
9331
|
registerColumnMaskPlugin();
|
|
9332
|
+
registerRefreshAbilitiesPlugin();
|
|
9202
9333
|
const schemaBuilder = new _pothos_core.default({
|
|
9203
9334
|
...pothosConfig,
|
|
9204
9335
|
plugins: [
|
|
9205
9336
|
columnMaskPluginName,
|
|
9337
|
+
refreshAbilitiesPluginName,
|
|
9206
9338
|
pluginName,
|
|
9207
9339
|
_pothos_plugin_drizzle.default,
|
|
9208
9340
|
_pothos_plugin_smart_subscriptions.default,
|
|
@@ -9623,6 +9755,7 @@ exports.normalizeAddress = normalizeAddress;
|
|
|
9623
9755
|
exports.normalizeEmailAddress = normalizeEmailAddress;
|
|
9624
9756
|
exports.normalizePersonName = normalizePersonName;
|
|
9625
9757
|
exports.parsePhoneNumber = parsePhoneNumber;
|
|
9758
|
+
exports.refreshAbilities = refreshAbilities;
|
|
9626
9759
|
exports.rumble = rumble;
|
|
9627
9760
|
exports.validateAddress = validateAddress;
|
|
9628
9761
|
|