@m1212e/rumble 0.27.3 → 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 +196 -63
- 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 +199 -67
- package/out/index.mjs.map +1 -1
- package/package.json +1 -1
package/out/index.mjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
import { t as generateFromSchema } from "./generate-Dzf2klR2.mjs";
|
|
2
|
-
import { One,
|
|
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";
|
|
@@ -172,6 +172,7 @@ const columnMaskKey = Symbol.for("rumble:columnMask");
|
|
|
172
172
|
const columnVisibilityKey = Symbol.for("rumble:columnVisibility");
|
|
173
173
|
const guardInputKey = Symbol.for("rumble:guardInput");
|
|
174
174
|
const resolvedFilterKey = Symbol.for("rumble:resolvedFilter");
|
|
175
|
+
const refreshAbilitiesKey = Symbol.for("rumble:refreshAbilities");
|
|
175
176
|
const requestStateKey = Symbol.for("rumble:requestState");
|
|
176
177
|
// lib/abilityBuilder/requestState.ts
|
|
177
178
|
function createRequestState() {
|
|
@@ -189,14 +190,6 @@ function requestStateOf(abilities) {
|
|
|
189
190
|
return abilities?.[requestStateKey];
|
|
190
191
|
}
|
|
191
192
|
// lib/abilityBuilder/columnMask.ts
|
|
192
|
-
function referencesRelation(filter, relations) {
|
|
193
|
-
if (!filter || typeof filter !== "object") return false;
|
|
194
|
-
return Object.entries(filter).some(([key, value]) => {
|
|
195
|
-
if (key === "AND" || key === "OR") return value.some((v) => referencesRelation(v, relations));
|
|
196
|
-
if (key === "NOT") return referencesRelation(value, relations);
|
|
197
|
-
return key in relations;
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
193
|
function selectedColumns(columns, allColumns) {
|
|
201
194
|
if (!columns) return new Set(allColumns);
|
|
202
195
|
const entries = Object.entries(columns).filter(([, v]) => v !== void 0);
|
|
@@ -255,12 +248,7 @@ function conditionalGroups(rules, guaranteed) {
|
|
|
255
248
|
function createColumnAccessResolver({ db, actions }, tableSchema) {
|
|
256
249
|
const allColumnNames = Object.keys(tableSchema.columns);
|
|
257
250
|
const primaryKeyNames = Object.keys(tableSchema.primaryKey);
|
|
258
|
-
const primaryKeyName = primaryKeyNames[0];
|
|
259
251
|
const toSQL = (table, wheres) => or(...wheres.map((where) => relationsFilterToSQL(table, sanitizeFilterValue(where), tableSchema.relations, db._.relations)));
|
|
260
|
-
const uncorrelated = (table, wheres, group) => {
|
|
261
|
-
const inner = aliasedTable(tableSchema.table, `rumble_columns_${group}`);
|
|
262
|
-
return sql`exists ${db.select({ pk: inner[primaryKeyName] }).from(inner).where(sql`${inner[primaryKeyName]} = ${table[primaryKeyName]} and (${toSQL(inner, wheres)})`)}`;
|
|
263
|
-
};
|
|
264
252
|
return function resolveColumnAccess(action, filters) {
|
|
265
253
|
const rules = filters.map((f) => ({
|
|
266
254
|
where: realWhere(f?.where),
|
|
@@ -291,11 +279,7 @@ function createColumnAccessResolver({ db, actions }, tableSchema) {
|
|
|
291
279
|
const actionIndex = sql.raw(String(actions.indexOf(action)));
|
|
292
280
|
const extras = { [ACTION_FLAG_KEY]: () => sql`${actionIndex}` };
|
|
293
281
|
groups.forEach(({ wheres }, group) => {
|
|
294
|
-
|
|
295
|
-
extras[mask.flags[group]] = (table) => {
|
|
296
|
-
const condition = relational ? uncorrelated(table, wheres, group) : toSQL(table, wheres);
|
|
297
|
-
return sql`case when ${condition} then 1 else 0 end`;
|
|
298
|
-
};
|
|
282
|
+
extras[mask.flags[group]] = (table) => sql`case when ${toSQL(table, wheres)} then 1 else 0 end`;
|
|
299
283
|
});
|
|
300
284
|
return {
|
|
301
285
|
columns,
|
|
@@ -545,6 +529,7 @@ const ATTR_ABILITIES_STATUS = "rumble.abilities.status";
|
|
|
545
529
|
const ATTR_ABILITIES_STATIC = "rumble.abilities.static";
|
|
546
530
|
const ATTR_ABILITIES_DYNAMIC = "rumble.abilities.dynamic";
|
|
547
531
|
const ATTR_ABILITIES_TOTAL = "rumble.abilities.total";
|
|
532
|
+
const ATTR_ABILITIES_RESOLVED = "rumble.abilities.resolved";
|
|
548
533
|
const ATTR_FILTERS_TOTAL = "rumble.filters.total";
|
|
549
534
|
const ATTR_FILTERS_ALLOWED = "rumble.filters.allowed";
|
|
550
535
|
const ATTR_SUBSCRIPTION_EVENT_INDEX = "rumble.subscription.event_index";
|
|
@@ -767,6 +752,117 @@ function isDynamicQueryFilter(filter) {
|
|
|
767
752
|
function isStaticQueryFilter(filter) {
|
|
768
753
|
return typeof filter !== "function";
|
|
769
754
|
}
|
|
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
|
+
}
|
|
770
866
|
// lib/helpers/errorLogging.ts
|
|
771
867
|
function hasMessage(error) {
|
|
772
868
|
return typeof error === "object" && error !== null && "message" in error;
|
|
@@ -904,7 +1000,23 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
|
|
|
904
1000
|
const blockEverythingFilter = createBlockEverythingFilter(String(tableName), tableSchema);
|
|
905
1001
|
const resolveColumnAccess = createColumnAccessResolver(settings, tableSchema);
|
|
906
1002
|
const transformToResponse = createFilterResponder(settings, tableSchema);
|
|
907
|
-
const
|
|
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) => {
|
|
908
1020
|
const filters = queryFilters.get(action);
|
|
909
1021
|
if (filters === "unrestricted") {
|
|
910
1022
|
attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
|
|
@@ -915,7 +1027,7 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
|
|
|
915
1027
|
settings.warnNothingRegistered(String(tableName), action);
|
|
916
1028
|
return transformToResponse(blockEverythingFilter);
|
|
917
1029
|
}
|
|
918
|
-
const rawResults = await
|
|
1030
|
+
const rawResults = await evaluate();
|
|
919
1031
|
if (rawResults.includes("allow")) {
|
|
920
1032
|
attributes[ATTR_ABILITIES_STATUS] = "unrestricted";
|
|
921
1033
|
return transformToResponse();
|
|
@@ -923,28 +1035,32 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
|
|
|
923
1035
|
const dynamicResults = rawResults.filter((r) => r !== void 0).map(withRestrictingWhere);
|
|
924
1036
|
attributes[ATTR_ABILITIES_DYNAMIC] = dynamicResults.length;
|
|
925
1037
|
attributes[ATTR_ABILITIES_STATIC] = simpleQueryFilters[action].length;
|
|
926
|
-
|
|
1038
|
+
let allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
|
|
927
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
|
+
}
|
|
928
1045
|
if (allQueryFilters.length === 0) {
|
|
929
1046
|
attributes[ATTR_ABILITIES_STATUS] = "blocked_everything";
|
|
930
1047
|
return transformToResponse(blockEverythingFilter);
|
|
931
1048
|
}
|
|
932
|
-
const mergedFilters = allQueryFilters.length === 1 ? { ...allQueryFilters[0] } : allQueryFilters.reduce((a, b) => {
|
|
933
|
-
return mergeFilters(a, b, "OR");
|
|
934
|
-
});
|
|
935
|
-
const { columns, extras, mask, visibility } = resolveColumnAccess(action, allQueryFilters);
|
|
936
|
-
if (columns) mergedFilters.columns = columns;
|
|
937
|
-
else delete mergedFilters.columns;
|
|
938
|
-
if (extras) mergedFilters.extras = {
|
|
939
|
-
...mergedFilters.extras,
|
|
940
|
-
...extras
|
|
941
|
-
};
|
|
942
1049
|
attributes[ATTR_ABILITIES_STATUS] = "applied";
|
|
943
|
-
return
|
|
1050
|
+
return respondWithFilters(action, allQueryFilters);
|
|
944
1051
|
};
|
|
945
1052
|
return { withContext: (userContext) => {
|
|
946
|
-
const
|
|
947
|
-
const
|
|
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();
|
|
948
1064
|
const resolved = /* @__PURE__ */ new Map();
|
|
949
1065
|
const abilities = { filter: (action) => {
|
|
950
1066
|
let prepared = cache.get(action);
|
|
@@ -956,6 +1072,9 @@ const createTableAbilities = (settings, tableName, queryFilters) => {
|
|
|
956
1072
|
return prepared;
|
|
957
1073
|
} };
|
|
958
1074
|
abilities[resolvedFilterKey] = (action) => resolved.get(action);
|
|
1075
|
+
abilities[refreshAbilitiesKey] = () => {
|
|
1076
|
+
if (resolveRules) cache = /* @__PURE__ */ new Map();
|
|
1077
|
+
};
|
|
959
1078
|
return abilities;
|
|
960
1079
|
} };
|
|
961
1080
|
};
|
|
@@ -1176,8 +1295,20 @@ when: (queryFilter) => {
|
|
|
1176
1295
|
}
|
|
1177
1296
|
};
|
|
1178
1297
|
};
|
|
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
|
+
}
|
|
1179
1310
|
// lib/abilityBuilder/index.ts
|
|
1180
|
-
const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerConfig }) => {
|
|
1311
|
+
const createAbilityBuilder = ({ db, actions, defaultLimit, resolveRelationalRules, otel, logger: loggerConfig }) => {
|
|
1181
1312
|
const log = loggerConfig?.enabled ? loggerConfig.logger : void 0;
|
|
1182
1313
|
const telemetryConfig = {
|
|
1183
1314
|
otel,
|
|
@@ -1187,6 +1318,7 @@ const createAbilityBuilder = ({ db, actions, defaultLimit, otel, logger: loggerC
|
|
|
1187
1318
|
db,
|
|
1188
1319
|
actions,
|
|
1189
1320
|
defaultLimit,
|
|
1321
|
+
resolveRelationalRules,
|
|
1190
1322
|
otel,
|
|
1191
1323
|
telemetryConfig,
|
|
1192
1324
|
log,
|
|
@@ -1395,7 +1527,7 @@ function mapNullFieldsToUndefined(obj) {
|
|
|
1395
1527
|
return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, value === null ? void 0 : value]));
|
|
1396
1528
|
}
|
|
1397
1529
|
// package.json
|
|
1398
|
-
var version = "0.27.
|
|
1530
|
+
var version = "0.27.4";
|
|
1399
1531
|
// lib/args/orderArg.ts
|
|
1400
1532
|
const makeDefaultName$1 = (dbName) => `${capitalize(toCamelCase(dbName.toString()))}OrderInputArgument`;
|
|
1401
1533
|
const createOrderArgImplementer = ({ db, schemaBuilder }) => {
|
|
@@ -2143,30 +2275,6 @@ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, n
|
|
|
2143
2275
|
default: throw new RumbleError(`Unsupported object type ${gqlType} for column ${fieldName}`);
|
|
2144
2276
|
}
|
|
2145
2277
|
}
|
|
2146
|
-
// lib/helpers/determineDialectFromSchema.ts
|
|
2147
|
-
const dialectCache = /* @__PURE__ */ new WeakMap();
|
|
2148
|
-
function determineDBDialectFromSchema(schema) {
|
|
2149
|
-
const cached = dialectCache.get(schema);
|
|
2150
|
-
if (cached) return cached;
|
|
2151
|
-
const found = /* @__PURE__ */ new Set();
|
|
2152
|
-
for (const table of Object.values(schema).map((t) => t.table)) {
|
|
2153
|
-
if (!table || typeof table !== "object") continue;
|
|
2154
|
-
if (is(table, PgTable)) found.add("postgres");
|
|
2155
|
-
else if (is(table, MySqlTable)) found.add("mysql");
|
|
2156
|
-
else if (is(table, SQLiteTable)) found.add("sqlite");
|
|
2157
|
-
}
|
|
2158
|
-
const dialects = Array.from(found);
|
|
2159
|
-
if (dialects.length === 1) {
|
|
2160
|
-
const only = dialects[0];
|
|
2161
|
-
dialectCache.set(schema, only);
|
|
2162
|
-
return only;
|
|
2163
|
-
}
|
|
2164
|
-
if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
|
|
2165
|
-
throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
|
|
2166
|
-
}
|
|
2167
|
-
function isPostgresDB(db) {
|
|
2168
|
-
return determineDBDialectFromSchema(db._.relations) === "postgres";
|
|
2169
|
-
}
|
|
2170
2278
|
// lib/search.ts
|
|
2171
2279
|
async function initSearchIfApplicable(input) {
|
|
2172
2280
|
const log = input.logger?.enabled ? input.logger.logger : void 0;
|
|
@@ -3001,6 +3109,28 @@ function createAbilityAwareClient({ db, abilityBuilder }) {
|
|
|
3001
3109
|
} });
|
|
3002
3110
|
};
|
|
3003
3111
|
}
|
|
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
|
+
}
|
|
3004
3134
|
// lib/runtimeFiltersPlugin/filterTypes.ts
|
|
3005
3135
|
const pluginName = "RuntimeFiltersPlugin";
|
|
3006
3136
|
// lib/runtimeFiltersPlugin/runtimeFiltersPlugin.ts
|
|
@@ -9170,10 +9300,12 @@ const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger, validatio
|
|
|
9170
9300
|
const createSpan = otel?.enabled && otel.tracer ? createOpenTelemetryWrapper(otel.tracer, otel.options) : void 0;
|
|
9171
9301
|
registerRuntimeFiltersPlugin();
|
|
9172
9302
|
registerColumnMaskPlugin();
|
|
9303
|
+
registerRefreshAbilitiesPlugin();
|
|
9173
9304
|
const schemaBuilder = new SchemaBuilder({
|
|
9174
9305
|
...pothosConfig,
|
|
9175
9306
|
plugins: [
|
|
9176
9307
|
columnMaskPluginName,
|
|
9308
|
+
refreshAbilitiesPluginName,
|
|
9177
9309
|
pluginName,
|
|
9178
9310
|
DrizzlePlugin,
|
|
9179
9311
|
SmartSubscriptionsPlugin,
|
|
@@ -9188,8 +9320,8 @@ const createSchemaBuilder = ({ db, pubsub, pothosConfig, otel, logger, validatio
|
|
|
9188
9320
|
}),
|
|
9189
9321
|
relations: db._.relations,
|
|
9190
9322
|
getTableConfig(table) {
|
|
9191
|
-
if (is(table, PgTable)) return getTableConfig(table);
|
|
9192
|
-
if (is(table, MySqlTable)) return getTableConfig
|
|
9323
|
+
if (is(table, PgTable)) return getTableConfig$1(table);
|
|
9324
|
+
if (is(table, MySqlTable)) return getTableConfig(table);
|
|
9193
9325
|
if (is(table, SQLiteTable)) return getTableConfig$2(table);
|
|
9194
9326
|
return {
|
|
9195
9327
|
columns: isTable(table) ? Object.values(getTableColumns(table)) : [],
|
|
@@ -9577,6 +9709,6 @@ export const r = rumble({
|
|
|
9577
9709
|
buildSchema: builtSchema
|
|
9578
9710
|
};
|
|
9579
9711
|
};
|
|
9580
|
-
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 };
|
|
9581
9713
|
|
|
9582
9714
|
//# sourceMappingURL=index.mjs.map
|