@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/README.md +94 -70
- package/out/client/generate.cjs +1 -1
- package/out/client/generate.d.cts +0 -2
- package/out/client/generate.d.cts.map +1 -1
- package/out/client/generate.d.mts +0 -2
- package/out/client/generate.d.mts.map +1 -1
- package/out/client/generate.mjs +1 -2
- package/out/client.cjs +7 -14
- package/out/client.cjs.map +1 -1
- package/out/client.d.cts +19 -28
- package/out/client.d.cts.map +1 -1
- package/out/client.d.mts +19 -28
- package/out/client.d.mts.map +1 -1
- package/out/client.mjs +7 -14
- package/out/client.mjs.map +1 -1
- package/out/{generate-D0CKBths.cjs → generate-Btr_u0Lr.cjs} +4 -7
- package/out/{generate-D0CKBths.cjs.map → generate-Btr_u0Lr.cjs.map} +1 -1
- package/out/{generate-BySnFVve.mjs → generate-Dzf2klR2.mjs} +4 -7
- package/out/{generate-BySnFVve.mjs.map → generate-Dzf2klR2.mjs.map} +1 -1
- package/out/index.cjs +250 -171
- package/out/index.cjs.map +1 -1
- package/out/index.d.cts +56 -300
- package/out/index.d.cts.map +1 -1
- package/out/index.d.mts +59 -303
- package/out/index.d.mts.map +1 -1
- package/out/index.mjs +252 -173
- package/out/index.mjs.map +1 -1
- package/package.json +15 -12
package/out/index.mjs
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { t as generateFromSchema } from "./generate-
|
|
2
|
-
import { One,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
782
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
1050
|
+
return respondWithFilters(action, allQueryFilters);
|
|
958
1051
|
};
|
|
959
1052
|
return { withContext: (userContext) => {
|
|
960
|
-
const
|
|
961
|
-
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();
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3047
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|