@m1212e/rumble 0.11.7 → 0.12.1

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 ADDED
@@ -0,0 +1,1901 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+
23
+ //#endregion
24
+ let node_fs_promises = require("node:fs/promises");
25
+ node_fs_promises = __toESM(node_fs_promises);
26
+ let node_path = require("node:path");
27
+ node_path = __toESM(node_path);
28
+ let graphql = require("graphql");
29
+ graphql = __toESM(graphql);
30
+ let es_toolkit = require("es-toolkit");
31
+ es_toolkit = __toESM(es_toolkit);
32
+ let wonka = require("wonka");
33
+ wonka = __toESM(wonka);
34
+ let __escape_tech_graphql_armor = require("@escape.tech/graphql-armor");
35
+ __escape_tech_graphql_armor = __toESM(__escape_tech_graphql_armor);
36
+ let __graphql_yoga_plugin_disable_introspection = require("@graphql-yoga/plugin-disable-introspection");
37
+ __graphql_yoga_plugin_disable_introspection = __toESM(__graphql_yoga_plugin_disable_introspection);
38
+ let graphql_yoga = require("graphql-yoga");
39
+ graphql_yoga = __toESM(graphql_yoga);
40
+ let sofa_api = require("sofa-api");
41
+ sofa_api = __toESM(sofa_api);
42
+ let drizzle_orm = require("drizzle-orm");
43
+ drizzle_orm = __toESM(drizzle_orm);
44
+ let drizzle_orm_casing = require("drizzle-orm/casing");
45
+ drizzle_orm_casing = __toESM(drizzle_orm_casing);
46
+ let drizzle_orm_pg_core = require("drizzle-orm/pg-core");
47
+ drizzle_orm_pg_core = __toESM(drizzle_orm_pg_core);
48
+ let pluralize = require("pluralize");
49
+ pluralize = __toESM(pluralize);
50
+ let drizzle_orm_mysql_core = require("drizzle-orm/mysql-core");
51
+ drizzle_orm_mysql_core = __toESM(drizzle_orm_mysql_core);
52
+ let drizzle_orm_sqlite_core = require("drizzle-orm/sqlite-core");
53
+ drizzle_orm_sqlite_core = __toESM(drizzle_orm_sqlite_core);
54
+ let __pothos_core = require("@pothos/core");
55
+ __pothos_core = __toESM(__pothos_core);
56
+ let __pothos_plugin_drizzle = require("@pothos/plugin-drizzle");
57
+ __pothos_plugin_drizzle = __toESM(__pothos_plugin_drizzle);
58
+ let __pothos_plugin_smart_subscriptions = require("@pothos/plugin-smart-subscriptions");
59
+ __pothos_plugin_smart_subscriptions = __toESM(__pothos_plugin_smart_subscriptions);
60
+ let graphql_scalars = require("graphql-scalars");
61
+ graphql_scalars = __toESM(graphql_scalars);
62
+
63
+ //#region lib/client/generate/client.ts
64
+ function generateClient({ apiUrl, rumbleImportPath, useExternalUrqlClient, availableSubscriptions }) {
65
+ const imports = [];
66
+ let code = "";
67
+ if (typeof useExternalUrqlClient === "string") imports.push(`import { urqlClient } from "${useExternalUrqlClient}";`);
68
+ else {
69
+ imports.push(`import { Client, fetchExchange } from '@urql/core';`);
70
+ imports.push(`import { cacheExchange } from '@urql/exchange-graphcache';`);
71
+ }
72
+ imports.push(`import { makeLiveQuery, makeMutation, makeSubscription, makeQuery } from '${rumbleImportPath}';`);
73
+ if (!useExternalUrqlClient) code += `
74
+ const urqlClient = new Client({
75
+ url: "${apiUrl ?? "PLEASE PROVIDE A URL WHEN GENERATING OR IMPORT AN EXTERNAL URQL CLIENT"}",
76
+ fetchSubscriptions: true,
77
+ exchanges: [cacheExchange({}), fetchExchange],
78
+ fetchOptions: {
79
+ credentials: "include",
80
+ },
81
+ requestPolicy: "cache-and-network",
82
+ });
83
+ `;
84
+ code += `
85
+ export const client = {
86
+ /**
87
+ * A query and subscription combination. First queries and if exists, also subscribes to a subscription of the same name.
88
+ * Combines the results of both, so the result is first the query result and then live updates from the subscription.
89
+ * Assumes that the query and subscription return the same fields as per default when using the rumble query helpers.
90
+ * If no subscription with the same name exists, this will just be a query.
91
+ */
92
+ liveQuery: makeLiveQuery<Query>({
93
+ urqlClient,
94
+ availableSubscriptions: new Set([${availableSubscriptions.values().toArray().map((value) => `"${value}"`).join(", ")}]),
95
+ }),
96
+ /**
97
+ * A mutation that can be used to e.g. create, update or delete data.
98
+ */
99
+ mutate: makeMutation<Mutation>({
100
+ urqlClient,
101
+ }),
102
+ /**
103
+ * A continuous stream of results that updates when the server sends new data.
104
+ */
105
+ subscribe: makeSubscription<Subscription>({
106
+ urqlClient,
107
+ }),
108
+ /**
109
+ * A one-time fetch of data.
110
+ */
111
+ query: makeQuery<Query>({
112
+ urqlClient,
113
+ }),
114
+ }`;
115
+ return {
116
+ imports,
117
+ code
118
+ };
119
+ }
120
+
121
+ //#endregion
122
+ //#region lib/client/generate/tsRepresentation.ts
123
+ function makeTSRepresentation(model) {
124
+ if (model instanceof graphql.GraphQLObjectType) return makeTSTypeFromObject(model);
125
+ else if (model instanceof graphql.GraphQLScalarType) return mapGraphqlScalarToTSTypeString(model);
126
+ else if (model instanceof graphql.GraphQLEnumType) return makeStringLiteralUnionFromEnum(model);
127
+ else if (model instanceof graphql.GraphQLInputObjectType) return makeTSTypeFromInputObject(model);
128
+ throw new Error(`Unknown model type: ${model}`);
129
+ }
130
+ function makeTSTypeFromObject(model) {
131
+ const stringifiedFields = /* @__PURE__ */ new Map();
132
+ for (const [key, value] of Object.entries(model.getFields())) stringifiedFields.set(key, makeTSObjectTypeField(value.type, value.args));
133
+ return `{
134
+ ${stringifiedFields.entries().map(([key, value]) => `${key}: ${value}`).toArray().join(",\n ")}
135
+ }`;
136
+ }
137
+ function makeTSTypeFromInputObject(model) {
138
+ const stringifiedFields = /* @__PURE__ */ new Map();
139
+ for (const [key, value] of Object.entries(model.getFields())) stringifiedFields.set(key, makeTSInputObjectTypeField(value.type));
140
+ return `{
141
+ ${stringifiedFields.entries().map(([key, value]) => `${key}${value.includes("| undefined") ? "?" : ""}: ${value}`).toArray().join(",\n ")}
142
+ }`;
143
+ }
144
+ function makeTSObjectTypeField(returnType, args) {
145
+ let isNonNullReturnType = false;
146
+ let isList = false;
147
+ for (let index = 0; index < 3; index++) {
148
+ if (returnType instanceof graphql.GraphQLList) {
149
+ isList = true;
150
+ returnType = returnType.ofType;
151
+ }
152
+ if (returnType instanceof graphql.GraphQLNonNull) {
153
+ isNonNullReturnType = true;
154
+ returnType = returnType.ofType;
155
+ }
156
+ }
157
+ let returnTypeString = returnType.name;
158
+ if (isList) returnTypeString += "[]";
159
+ if (!isNonNullReturnType) returnTypeString += " | null";
160
+ const isRelationType = returnType instanceof graphql.GraphQLObjectType;
161
+ const argsStringMap = /* @__PURE__ */ new Map();
162
+ for (const arg of args ?? []) argsStringMap.set(arg.name, stringifyTSObjectArg(arg.type));
163
+ if (isRelationType) {
164
+ const makePOptional = argsStringMap.entries().every(([, value]) => value.includes("| undefined"));
165
+ return `(${(args ?? []).length > 0 ? `p${makePOptional ? "?" : ""}: {
166
+ ${argsStringMap.entries().map(([key, value]) => ` ${key}${value.includes("| undefined") ? "?" : ""}: ${value}`).toArray().join(",\n ")}
167
+ }` : ""}) => ${returnTypeString}`;
168
+ } else return returnTypeString;
169
+ }
170
+ function makeTSInputObjectTypeField(returnType) {
171
+ let isNonNullReturnType = false;
172
+ let isList = false;
173
+ for (let index = 0; index < 3; index++) {
174
+ if (returnType instanceof graphql.GraphQLList) {
175
+ isList = true;
176
+ returnType = returnType.ofType;
177
+ }
178
+ if (returnType instanceof graphql.GraphQLNonNull) {
179
+ isNonNullReturnType = true;
180
+ returnType = returnType.ofType;
181
+ }
182
+ }
183
+ let returnTypeString = returnType.name;
184
+ if (isList) returnTypeString += "[]";
185
+ if (!isNonNullReturnType) returnTypeString += " | null | undefined";
186
+ else if (isList) returnTypeString += " | undefined";
187
+ return returnTypeString;
188
+ }
189
+ function stringifyTSObjectArg(arg) {
190
+ let ret = "unknown";
191
+ let isNullable = true;
192
+ if (arg instanceof graphql.GraphQLNonNull) {
193
+ isNullable = false;
194
+ arg = arg.ofType;
195
+ }
196
+ if (arg instanceof graphql.GraphQLInputObjectType || arg instanceof graphql.GraphQLScalarType) ret = arg.name;
197
+ if (isNullable) ret += " | null | undefined";
198
+ return ret;
199
+ }
200
+ function mapGraphqlScalarToTSTypeString(arg) {
201
+ switch (arg.name) {
202
+ case "ID": return "string";
203
+ case "String": return "string";
204
+ case "Boolean": return "boolean";
205
+ case "Int": return "number";
206
+ case "Float": return "number";
207
+ case "Date": return "Date";
208
+ case "DateTime": return "Date";
209
+ case "JSON": return "any";
210
+ default: return "unknown";
211
+ }
212
+ }
213
+ function makeStringLiteralUnionFromEnum(enumType) {
214
+ return enumType.getValues().map((value) => `"${value.name}"`).join(" | ");
215
+ }
216
+
217
+ //#endregion
218
+ //#region lib/client/generate/generate.ts
219
+ async function generateFromSchema({ outputPath, schema, rumbleImportPath = "@m1212e/rumble", apiUrl, useExternalUrqlClient = false }) {
220
+ try {
221
+ await (0, node_fs_promises.access)(outputPath);
222
+ await (0, node_fs_promises.rm)(outputPath, {
223
+ recursive: true,
224
+ force: true
225
+ });
226
+ } catch (_error) {}
227
+ await (0, node_fs_promises.mkdir)(outputPath, { recursive: true });
228
+ if (!outputPath.endsWith("/")) outputPath += "/";
229
+ const imports = [];
230
+ let code = "";
231
+ const typeMap = /* @__PURE__ */ new Map();
232
+ for (const [key, object] of Object.entries(schema.getTypeMap())) {
233
+ if (key.startsWith("__")) continue;
234
+ typeMap.set(key, object);
235
+ }
236
+ for (const [key, object] of typeMap.entries()) {
237
+ const rep = makeTSRepresentation(object);
238
+ if (rep === key) continue;
239
+ code += `
240
+ export type ${key} = ${rep};
241
+ `;
242
+ }
243
+ const c = generateClient({
244
+ apiUrl,
245
+ useExternalUrqlClient,
246
+ rumbleImportPath,
247
+ availableSubscriptions: new Set(Object.keys(schema.getSubscriptionType()?.getFields() || {}))
248
+ });
249
+ imports.push(...c.imports);
250
+ code += c.code;
251
+ await (0, node_fs_promises.writeFile)((0, node_path.join)(outputPath, "client.ts"), `${imports.join("\n")}\n${code}`);
252
+ }
253
+
254
+ //#endregion
255
+ //#region lib/client/request.ts
256
+ const argsKey = "__args";
257
+ function makeGraphQLQuery({ queryName, input, client, enableSubscription = false }) {
258
+ const otwQueryName = `${(0, es_toolkit.capitalize)(queryName)}Query`;
259
+ const argsString = stringifyArgumentObjectToGraphqlList(input[argsKey] ?? {});
260
+ const operationString = (operationVerb) => `${operationVerb} ${otwQueryName} { ${queryName}${argsString} { ${stringifySelection(input)} }}`;
261
+ let awaitedReturnValueReference;
262
+ const response = (0, wonka.pipe)((0, wonka.merge)([client.query(operationString("query"), {}), enableSubscription ? client.subscription(operationString("subscription"), {}) : wonka.empty]), (0, wonka.map)((v) => {
263
+ const data = v.data?.[queryName];
264
+ if (!data && v.error) throw v.error;
265
+ return data;
266
+ }), (0, wonka.onPush)((data) => {
267
+ if (awaitedReturnValueReference) Object.assign(awaitedReturnValueReference, data);
268
+ }));
269
+ const promise = new Promise((resolve) => {
270
+ const unsub = (0, wonka.toObservable)(response).subscribe((v) => {
271
+ unsub();
272
+ const newObservableWithAwaitedValue = (0, wonka.pipe)((0, wonka.merge)([response, (0, wonka.fromValue)(v)]), wonka.toObservable);
273
+ awaitedReturnValueReference = {
274
+ ...v,
275
+ ...newObservableWithAwaitedValue
276
+ };
277
+ resolve(awaitedReturnValueReference);
278
+ }).unsubscribe;
279
+ });
280
+ Object.assign(promise, (0, wonka.toObservable)(response));
281
+ return promise;
282
+ }
283
+ function makeGraphQLMutation({ mutationName, input, client }) {
284
+ const otwMutationName = `${(0, es_toolkit.capitalize)(mutationName)}Mutation`;
285
+ const argsString = stringifyArgumentObjectToGraphqlList(input[argsKey] ?? {});
286
+ const response = (0, wonka.pipe)(client.mutation(`mutation ${otwMutationName} { ${mutationName}${argsString} { ${stringifySelection(input)} }}`, {}), (0, wonka.map)((v) => {
287
+ const data = v.data?.[mutationName];
288
+ if (!data && v.error) throw v.error;
289
+ return data;
290
+ }));
291
+ const observable = (0, wonka.toObservable)(response);
292
+ const promise = (0, wonka.toPromise)(response).then((res) => {
293
+ Object.assign(res, observable);
294
+ return res;
295
+ });
296
+ Object.assign(promise, observable);
297
+ return promise;
298
+ }
299
+ function makeGraphQLSubscription({ subscriptionName, input, client }) {
300
+ const otwSubscriptionName = `${(0, es_toolkit.capitalize)(subscriptionName)}Subscription`;
301
+ const argsString = stringifyArgumentObjectToGraphqlList(input[argsKey] ?? {});
302
+ return (0, wonka.pipe)(client.subscription(`subscription ${otwSubscriptionName} { ${subscriptionName}${argsString} { ${stringifySelection(input)} }}`, {}), (0, wonka.map)((v) => {
303
+ const data = v.data?.[subscriptionName];
304
+ if (!data && v.error) throw v.error;
305
+ return data;
306
+ }), wonka.toObservable);
307
+ }
308
+ function stringifySelection(selection) {
309
+ return Object.entries(selection).filter(([key]) => key !== argsKey).reduce((acc, [key, value]) => {
310
+ if (typeof value === "object") {
311
+ if (value[argsKey]) {
312
+ const argsString = stringifyArgumentObjectToGraphqlList(value[argsKey]);
313
+ acc += `${key}${argsString} { ${stringifySelection(value)} }
314
+ `;
315
+ return acc;
316
+ }
317
+ acc += `${key} { ${stringifySelection(value)} }
318
+ `;
319
+ } else acc += `${key}
320
+ `;
321
+ return acc;
322
+ }, "");
323
+ }
324
+ function stringifyArgumentObjectToGraphqlList(args) {
325
+ const entries = Object.entries(args);
326
+ if (entries.length === 0) return "";
327
+ return `(${entries.map(([key, value]) => `${key}: ${stringifyArgumentValue(value)}`).join(", ")})`;
328
+ }
329
+ function stringifyArgumentValue(arg) {
330
+ switch (typeof arg) {
331
+ case "string": return `"${arg}"`;
332
+ case "number": return `${arg}`;
333
+ case "bigint": return `${arg}`;
334
+ case "boolean": return `${arg}`;
335
+ case "symbol": throw new Error("Cannot stringify a symbol to send as gql arg");
336
+ case "undefined": return "null";
337
+ case "object": return `{ ${Object.entries(arg).map(([key, value]) => `${key}: ${stringifyArgumentValue(value)}`).join(", ")} }`;
338
+ case "function": throw new Error("Cannot stringify a function to send as gql arg");
339
+ }
340
+ }
341
+
342
+ //#endregion
343
+ //#region lib/client/liveQuery.ts
344
+ function makeLiveQuery({ urqlClient, availableSubscriptions }) {
345
+ return new Proxy({}, { get: (_target, prop) => {
346
+ return (input) => {
347
+ return makeGraphQLQuery({
348
+ queryName: prop,
349
+ input,
350
+ client: urqlClient,
351
+ enableSubscription: availableSubscriptions.has(prop)
352
+ });
353
+ };
354
+ } });
355
+ }
356
+
357
+ //#endregion
358
+ //#region lib/client/mutation.ts
359
+ function makeMutation({ urqlClient }) {
360
+ return new Proxy({}, { get: (_target, prop) => {
361
+ return (input) => {
362
+ return makeGraphQLMutation({
363
+ mutationName: prop,
364
+ input,
365
+ client: urqlClient
366
+ });
367
+ };
368
+ } });
369
+ }
370
+
371
+ //#endregion
372
+ //#region lib/client/query.ts
373
+ function makeQuery({ urqlClient }) {
374
+ return new Proxy({}, { get: (_target, prop) => {
375
+ return (input) => {
376
+ return makeGraphQLQuery({
377
+ queryName: prop,
378
+ input,
379
+ client: urqlClient,
380
+ enableSubscription: false
381
+ });
382
+ };
383
+ } });
384
+ }
385
+
386
+ //#endregion
387
+ //#region lib/client/subscription.ts
388
+ function makeSubscription({ urqlClient }) {
389
+ return new Proxy({}, { get: (_target, prop) => {
390
+ return (input) => {
391
+ return makeGraphQLSubscription({
392
+ subscriptionName: prop,
393
+ input,
394
+ client: urqlClient
395
+ });
396
+ };
397
+ } });
398
+ }
399
+
400
+ //#endregion
401
+ //#region lib/types/rumbleError.ts
402
+ /**
403
+ * An error that gets raised by rumble whenever something does not go according to plan.
404
+ * Mostly internals, configuration errors or other unexpected things.
405
+ */
406
+ var RumbleError = class extends Error {
407
+ constructor(message) {
408
+ super(message);
409
+ this.name = "RumbleError";
410
+ }
411
+ };
412
+ /**
413
+ * An error that gets raised by rumble whenever an error occurs in a resolver, containing
414
+ * information safely exposeable to the user.
415
+ * E.g. the assert helpers issue these.
416
+ */
417
+ var RumbleErrorSafe = class extends graphql.GraphQLError {};
418
+
419
+ //#endregion
420
+ //#region lib/helpers/asserts.ts
421
+ /**
422
+ *
423
+ * Helper function to map a drizzle findFirst query result,
424
+ * which may be optional, to a correct drizzle type.
425
+ *
426
+ * @throws RumbleError
427
+ *
428
+ * @example
429
+ *
430
+ * ```ts
431
+ * schemaBuilder.queryFields((t) => {
432
+ return {
433
+ findFirstUser: t.drizzleField({
434
+ type: UserRef,
435
+ resolve: (query, root, args, ctx, info) => {
436
+ return (
437
+ db.query.users
438
+ .findFirst({
439
+ ...query,
440
+ where: ctx.abilities.users.filter("read").single.where,
441
+ })
442
+ // note that we need to manually raise an error if the value is not found
443
+ .then(assertFindFirstExists)
444
+ );
445
+ },
446
+ }),
447
+ };
448
+ });
449
+ * ```
450
+ */
451
+ const assertFindFirstExists = (value) => {
452
+ if (!value) throw new RumbleErrorSafe("Value not found but required (findFirst)");
453
+ return value;
454
+ };
455
+ /**
456
+ *
457
+ * Helper function to map a drizzle findFirst query result,
458
+ * which may be optional, to a correct drizzle type.
459
+ *
460
+ * @throws RumbleError
461
+ *
462
+ * @example
463
+ *
464
+ * ```ts
465
+ schemaBuilder.mutationFields((t) => {
466
+ return {
467
+ updateUsername: t.drizzleField({
468
+ type: UserRef,
469
+ args: {
470
+ userId: t.arg.int({ required: true }),
471
+ newName: t.arg.string({ required: true }),
472
+ },
473
+ resolve: (query, root, args, ctx, info) => {
474
+ return db
475
+ .update(schema.users)
476
+ .set({
477
+ name: args.newName,
478
+ })
479
+ .where(
480
+ and(
481
+ eq(schema.users.id, args.userId),
482
+ ctx.abilities.users.filter("update").single.where
483
+ )
484
+ )
485
+ .returning({ id: schema.users.id, name: schema.users.name })
486
+ // note that we need to manually raise an error if the value is not found
487
+ .then(assertFirstEntryExists);
488
+ },
489
+ }),
490
+ };
491
+ });
492
+ * ```
493
+ */
494
+ const assertFirstEntryExists = (value) => {
495
+ const v = value.at(0);
496
+ if (!v) throw new RumbleErrorSafe("Value not found but required (firstEntry)");
497
+ return v;
498
+ };
499
+
500
+ //#endregion
501
+ //#region lib/helpers/mapNullFieldsToUndefined.ts
502
+ /**
503
+ * Helper to map null fields to undefined
504
+ * @param obj The object to map
505
+ * @returns The mapped object with all fields of 'null' transformed to 'undefined'
506
+ *
507
+ * This becomes useful for update mutations where you do not want to pass null (unset value in db)
508
+ * but undefined (do not touch value in db) in case of a value not beeing set in the args of said mutation
509
+ * @example
510
+ * ```ts
511
+ * updateUser: t.drizzleField({
512
+ type: User,
513
+ args: {
514
+ id: t.arg.string({ required: true }),
515
+ email: t.arg.string(),
516
+ lastName: t.arg.string(),
517
+ firstName: t.arg.string(),
518
+ },
519
+ resolve: async (query, root, args, ctx, info) => {
520
+ const mappedArgs = mapNullFieldsToUndefined(args);
521
+ const user = await db.transaction(async (tx) => {
522
+ const user = await tx
523
+ .update(schema.user)
524
+ .set({
525
+
526
+ // email: args.email ?? undefined,
527
+ // lastName: args.lastName ?? undefined,
528
+ // firstName: args.firstName ?? undefined,
529
+
530
+ // becomes this
531
+
532
+ email: mappedArgs.email,
533
+ lastName: mappedArgs.lastName,
534
+ firstName: mappedArgs.firstName,
535
+ })
536
+ .returning()
537
+ .then(assertFirstEntryExists);
538
+ return user;
539
+ });
540
+
541
+ pubsub.updated(user.id);
542
+
543
+ return db.query.user
544
+ .findFirst(
545
+ query(
546
+ ctx.abilities.user.filter('read').merge(
547
+ {
548
+ where: { id: user.id },
549
+ }
550
+ 1).query.single,
551
+ ),
552
+ )
553
+ .then(assertFindFirstExists);
554
+ },
555
+ }),
556
+ *
557
+ *
558
+ * ```
559
+ */
560
+ function mapNullFieldsToUndefined(obj) {
561
+ return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, value === null ? void 0 : value]));
562
+ }
563
+
564
+ //#endregion
565
+ //#region lib/helpers/lazy.ts
566
+ /**
567
+ * Creates a lazily initialized function.
568
+ *
569
+ * The returned function will call the initializer function on its first call and
570
+ * store the result. On subsequent calls, it will return the stored result.
571
+ *
572
+ * @param initializer The function to be called for initialization.
573
+ * @returns A function that calls the initializer function on its first call and
574
+ * returns the stored result on subsequent calls.
575
+ */
576
+ function lazy(initializer) {
577
+ let value;
578
+ let initialized = false;
579
+ return () => {
580
+ if (!initialized) {
581
+ value = initializer();
582
+ initialized = true;
583
+ }
584
+ return value;
585
+ };
586
+ }
587
+
588
+ //#endregion
589
+ //#region lib/helpers/mergeFilters.ts
590
+ function mergeFilters(filterA, filterB) {
591
+ return {
592
+ where: filterA?.where && filterB?.where ? { AND: [filterA?.where, filterB?.where] } : filterA?.where ?? filterB?.where,
593
+ columns: filterA?.columns || filterB?.columns ? new Set([Object.entries(filterA?.columns ?? {}), Object.entries(filterB?.columns ?? {})].flat().filter(([, v]) => v === true).map(([k]) => k)).entries().reduce((acc, [key]) => {
594
+ acc[key] = true;
595
+ return acc;
596
+ }, {}) : void 0,
597
+ extras: filterA?.extras || filterB?.extras ? (0, es_toolkit.toMerged)(filterA?.extras ?? {}, filterB?.extras ?? {}) : void 0,
598
+ orderBy: filterA?.orderBy || filterB?.orderBy ? (0, es_toolkit.toMerged)(filterA?.orderBy ?? {}, filterB?.orderBy ?? {}) : void 0,
599
+ limit: filterA?.limit || filterB?.limit ? Math.min(filterA?.limit ?? Infinity, filterB?.limit ?? Infinity) : void 0,
600
+ offset: filterA?.offset || filterB?.offset ? Math.min(filterA?.offset ?? Infinity, filterB?.offset ?? Infinity) : void 0,
601
+ with: filterA?.with || filterB?.with ? (0, es_toolkit.toMerged)(filterA?.with ?? {}, filterB?.with ?? {}) : void 0
602
+ };
603
+ }
604
+
605
+ //#endregion
606
+ //#region lib/helpers/sqlTypes/types.ts
607
+ const intLikeSQLTypeStrings = [
608
+ "serial",
609
+ "int",
610
+ "integer",
611
+ "tinyint",
612
+ "smallint",
613
+ "mediumint"
614
+ ];
615
+ function isIntLikeSQLTypeString(sqlType) {
616
+ return intLikeSQLTypeStrings.includes(sqlType);
617
+ }
618
+ const floatLikeSQLTypeStrings = [
619
+ "real",
620
+ "decimal",
621
+ "double",
622
+ "float",
623
+ "numeric"
624
+ ];
625
+ function isFloatLikeSQLTypeString(sqlType) {
626
+ return floatLikeSQLTypeStrings.includes(sqlType);
627
+ }
628
+ const stringLikeSQLTypeStrings = [
629
+ "string",
630
+ "text",
631
+ "varchar",
632
+ "char",
633
+ "text(256)"
634
+ ];
635
+ function isStringLikeSQLTypeString(sqlType) {
636
+ return stringLikeSQLTypeStrings.includes(sqlType);
637
+ }
638
+ const IDLikeSQLTypeStrings = ["uuid"];
639
+ function isIDLikeSQLTypeString(sqlType) {
640
+ return IDLikeSQLTypeStrings.includes(sqlType);
641
+ }
642
+ const booleanSQLTypeStrings = ["boolean"];
643
+ function isBooleanSQLTypeString(sqlType) {
644
+ return booleanSQLTypeStrings.includes(sqlType);
645
+ }
646
+ const dateTimeLikeSQLTypeStrings = ["timestamp", "datetime"];
647
+ function isDateTimeLikeSQLTypeString(sqlType) {
648
+ return dateTimeLikeSQLTypeStrings.includes(sqlType);
649
+ }
650
+ const dateLikeSQLTypeStrings = ["date"];
651
+ function isDateLikeSQLTypeString(sqlType) {
652
+ return dateLikeSQLTypeStrings.includes(sqlType);
653
+ }
654
+ const jsonLikeSQLTypeStrings = ["json"];
655
+ function isJSONLikeSQLTypeString(sqlType) {
656
+ return jsonLikeSQLTypeStrings.includes(sqlType);
657
+ }
658
+ [
659
+ ...intLikeSQLTypeStrings,
660
+ ...floatLikeSQLTypeStrings,
661
+ ...stringLikeSQLTypeStrings,
662
+ ...IDLikeSQLTypeStrings,
663
+ ...booleanSQLTypeStrings,
664
+ ...dateTimeLikeSQLTypeStrings,
665
+ ...dateLikeSQLTypeStrings,
666
+ ...jsonLikeSQLTypeStrings
667
+ ];
668
+ 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})`);
669
+
670
+ //#endregion
671
+ //#region lib/helpers/sqlTypes/distinctValuesFromSQLType.ts
672
+ function createDistinctValuesFromSQLType(sqlType) {
673
+ if (isIntLikeSQLTypeString(sqlType)) return {
674
+ value1: 1,
675
+ value2: 2
676
+ };
677
+ if (isFloatLikeSQLTypeString(sqlType)) return {
678
+ value1: 1.1,
679
+ value2: 2.2
680
+ };
681
+ if (isStringLikeSQLTypeString(sqlType)) return {
682
+ value1: "a",
683
+ value2: "b"
684
+ };
685
+ if (isIDLikeSQLTypeString(sqlType)) return {
686
+ value1: "fba31870-5528-42d7-b27e-2e5ee657aea5",
687
+ value2: "fc65db81-c2d1-483d-8a25-a30e2cf6e02d"
688
+ };
689
+ if (isBooleanSQLTypeString(sqlType)) return {
690
+ value1: true,
691
+ value2: false
692
+ };
693
+ if (isDateTimeLikeSQLTypeString(sqlType)) return {
694
+ value1: new Date(2022, 1, 1),
695
+ value2: new Date(2022, 1, 2)
696
+ };
697
+ if (isDateLikeSQLTypeString(sqlType)) return {
698
+ value1: new Date(2022, 1, 1),
699
+ value2: new Date(2022, 1, 2)
700
+ };
701
+ if (isJSONLikeSQLTypeString(sqlType)) return {
702
+ value1: { a: 1 },
703
+ value2: { b: 2 }
704
+ };
705
+ throw UnknownTypeRumbleError(sqlType, "Distinct");
706
+ }
707
+
708
+ //#endregion
709
+ //#region lib/helpers/tableHelpers.ts
710
+ const drizzleNameSymbol = Symbol.for("drizzle:Name");
711
+ const drizzleOriginalNameSymbol = Symbol.for("drizzle:OriginalName");
712
+ const drizzleBaseNameSymbol = Symbol.for("drizzle:BaseName");
713
+ function tableHelper({ db, table }) {
714
+ if (typeof table !== "string") table = table.tsName || table.dbName || table[drizzleNameSymbol] || table[drizzleOriginalNameSymbol] || table[drizzleBaseNameSymbol];
715
+ const foundRelation = Object.values(db._.relations).find((schema) => schema.name === table || schema.table[drizzleNameSymbol] === table || schema.table[drizzleOriginalNameSymbol] === table || schema.table[drizzleBaseNameSymbol] === table);
716
+ if (!foundRelation) throw new RumbleError(`Could not find schema for ${JSON.stringify(table)}`);
717
+ const foundSchema = Object.values(db._.schema).find((schema) => schema.dbName === foundRelation.table[drizzleOriginalNameSymbol]);
718
+ if (!foundSchema) throw new RumbleError(`Could not find schema for ${JSON.stringify(table)}`);
719
+ return {
720
+ columns: foundSchema.columns,
721
+ primaryKey: foundSchema.primaryKey,
722
+ relations: foundRelation.relations,
723
+ dbName: foundSchema.dbName,
724
+ tsName: foundSchema.tsName,
725
+ foundSchema,
726
+ foundRelation
727
+ };
728
+ }
729
+
730
+ //#endregion
731
+ //#region lib/abilityBuilder.ts
732
+ function isDynamicQueryFilter(filter) {
733
+ return typeof filter === "function" && filter.constructor.name !== "AsyncFunction";
734
+ }
735
+ function isStaticQueryFilter(filter) {
736
+ return typeof filter !== "function";
737
+ }
738
+ const nothingRegisteredWarningLogger = (0, es_toolkit.debounce)((model, action) => {
739
+ console.warn(`
740
+ Warning! No abilities have been registered for
741
+
742
+ ${model}/${action}
743
+
744
+ but has been accessed. This will block everything. If this is intended, you can ignore this warning. If not, please ensure that you register the ability in your ability builder.
745
+ `);
746
+ }, 1e3);
747
+ const createAbilityBuilder = ({ db, actions, defaultLimit }) => {
748
+ let hasBeenBuilt = false;
749
+ const createBuilderForTable = () => {
750
+ const queryFilters = /* @__PURE__ */ new Map();
751
+ const runtimeFilters = /* @__PURE__ */ new Map();
752
+ for (const action of actions) if (!runtimeFilters.has(action)) runtimeFilters.set(action, []);
753
+ return {
754
+ allow: (action) => {
755
+ if (hasBeenBuilt) throw new RumbleError("You can't call allow() after the ability builder has been built. Please ensure that you register all abilities before accessing them.");
756
+ const actions$1 = Array.isArray(action) ? action : [action];
757
+ for (const action$1 of actions$1) {
758
+ let filters = queryFilters.get(action$1);
759
+ if (!filters) {
760
+ filters = "unrestricted";
761
+ queryFilters.set(action$1, filters);
762
+ }
763
+ }
764
+ return { when: (queryFilter) => {
765
+ for (const action$1 of actions$1) {
766
+ if (queryFilters.get(action$1) === "unrestricted") queryFilters.set(action$1, []);
767
+ queryFilters.get(action$1).push(queryFilter);
768
+ }
769
+ } };
770
+ },
771
+ filter: (action) => {
772
+ const actions$1 = Array.isArray(action) ? action : [action];
773
+ return { by: (explicitFilter) => {
774
+ for (const action$1 of actions$1) runtimeFilters.get(action$1).push(explicitFilter);
775
+ } };
776
+ },
777
+ _: {
778
+ runtimeFilters,
779
+ queryFilters
780
+ }
781
+ };
782
+ };
783
+ const buildersPerTable = Object.fromEntries(Object.keys(db.query).map((tableName) => [tableName, createBuilderForTable()]));
784
+ return {
785
+ ...buildersPerTable,
786
+ _: {
787
+ registeredFilters({ action, table }) {
788
+ return buildersPerTable[table]._.runtimeFilters.get(action);
789
+ },
790
+ build() {
791
+ const createFilterForTable = (tableName) => {
792
+ const queryFilters = buildersPerTable[tableName]._.queryFilters;
793
+ const simpleQueryFilters = Object.fromEntries(actions.map((action) => {
794
+ const filters = queryFilters.get(action);
795
+ if (!filters || filters === "unrestricted") return [action, []];
796
+ return [action, filters.filter(isStaticQueryFilter)];
797
+ }));
798
+ const dynamicQueryFilters = Object.fromEntries(actions.map((action) => {
799
+ const filters = queryFilters.get(action);
800
+ if (!filters || filters === "unrestricted") return [action, []];
801
+ return [action, filters.filter(isDynamicQueryFilter)];
802
+ }));
803
+ const tableSchema = tableHelper({
804
+ db,
805
+ table: tableName
806
+ });
807
+ if (Object.keys(tableSchema.primaryKey).length === 0) throw new RumbleError(`No primary key found for entity ${tableName.toString()}`);
808
+ const primaryKeyField = Object.values(tableSchema.primaryKey)[0];
809
+ const distinctValues = createDistinctValuesFromSQLType(primaryKeyField.getSQLType());
810
+ const blockEverythingFilter = { where: { AND: [{ [primaryKeyField.name]: distinctValues.value1 }, { [primaryKeyField.name]: distinctValues.value2 }] } };
811
+ /**
812
+ * Packs the filters into a response object that can be applied for queries by the user
813
+ */
814
+ function transformToResponse(queryFilters$1) {
815
+ const internalTransformer = (filters, mergedLimit) => {
816
+ const limit = lazy(() => {
817
+ if (mergedLimit !== void 0) {
818
+ if (!filters?.limit) return mergedLimit;
819
+ if (filters.limit > mergedLimit) return mergedLimit;
820
+ }
821
+ let limit$1 = filters?.limit;
822
+ if (defaultLimit && (limit$1 === void 0 || limit$1 > defaultLimit)) limit$1 = defaultLimit;
823
+ return limit$1 ?? void 0;
824
+ });
825
+ const sqlTransformedWhere = lazy(() => {
826
+ return filters?.where ? (0, drizzle_orm.relationsFilterToSQL)(tableSchema.foundRelation.table, filters.where) : void 0;
827
+ });
828
+ if (filters?.columns) return {
829
+ query: {
830
+ single: {
831
+ where: filters?.where,
832
+ columns: filters?.columns
833
+ },
834
+ many: {
835
+ where: filters?.where,
836
+ columns: filters?.columns,
837
+ get limit() {
838
+ return limit();
839
+ }
840
+ }
841
+ },
842
+ sql: { get where() {
843
+ return sqlTransformedWhere();
844
+ } }
845
+ };
846
+ else return {
847
+ query: {
848
+ single: { where: filters?.where },
849
+ many: {
850
+ where: filters?.where,
851
+ get limit() {
852
+ return limit();
853
+ }
854
+ }
855
+ },
856
+ sql: { get where() {
857
+ return sqlTransformedWhere();
858
+ } }
859
+ };
860
+ };
861
+ const ret = internalTransformer(queryFilters$1);
862
+ /**
863
+ * Merges the current query filters with the provided filters for this call only
864
+ */
865
+ function merge$2(p) {
866
+ return internalTransformer(mergeFilters(ret.query.many, p), p.limit);
867
+ }
868
+ ret.merge = merge$2;
869
+ return ret;
870
+ }
871
+ return { withContext: (userContext) => {
872
+ return { filter: (action) => {
873
+ const filters = queryFilters.get(action);
874
+ if (filters === "unrestricted") return transformToResponse();
875
+ if (!filters) {
876
+ nothingRegisteredWarningLogger(tableName.toString(), action);
877
+ return transformToResponse(blockEverythingFilter);
878
+ }
879
+ const dynamicResults = new Array(dynamicQueryFilters[action].length);
880
+ let filtersReturned = 0;
881
+ for (let i = 0; i < dynamicQueryFilters[action].length; i++) {
882
+ const func = dynamicQueryFilters[action][i];
883
+ const result = func(userContext);
884
+ if (result === "allow") return transformToResponse();
885
+ if (result === void 0) continue;
886
+ dynamicResults[filtersReturned++] = result;
887
+ }
888
+ dynamicResults.length = filtersReturned;
889
+ const allQueryFilters = [...simpleQueryFilters[action], ...dynamicResults];
890
+ if (allQueryFilters.length === 0) return transformToResponse(blockEverythingFilter);
891
+ return transformToResponse(allQueryFilters.length === 1 ? allQueryFilters[0] : allQueryFilters.reduce((a, b) => {
892
+ return mergeFilters(a, b);
893
+ }, {}));
894
+ } };
895
+ } };
896
+ };
897
+ const abilitiesPerTable = Object.fromEntries(Object.keys(db.query).map((tableName) => [tableName, createFilterForTable(tableName)]));
898
+ hasBeenBuilt = true;
899
+ return (ctx) => {
900
+ return Object.fromEntries(Object.keys(abilitiesPerTable).map((tableName) => [tableName, abilitiesPerTable[tableName].withContext(ctx)]));
901
+ };
902
+ }
903
+ }
904
+ };
905
+ };
906
+
907
+ //#endregion
908
+ //#region lib/args/orderArg.ts
909
+ const makeDefaultName$1 = (dbName) => `${(0, es_toolkit.capitalize)((0, drizzle_orm_casing.toCamelCase)(dbName.toString()))}OrderInputArgument`;
910
+ const createOrderArgImplementer = ({ db, schemaBuilder }) => {
911
+ const referenceStorage = /* @__PURE__ */ new Map();
912
+ const sortingParameterEnumRef = lazy(() => schemaBuilder.enumType("SortingParameter", { values: ["asc", "desc"] }));
913
+ const orderArgImplementer = ({ table, refName, dbName }) => {
914
+ const tableSchema = tableHelper({
915
+ db,
916
+ table: dbName ?? table
917
+ });
918
+ const inputTypeName = refName ?? makeDefaultName$1(tableSchema.tsName);
919
+ let ret = referenceStorage.get(inputTypeName);
920
+ if (ret) return ret;
921
+ const implement = () => {
922
+ return schemaBuilder.inputType(inputTypeName, { fields: (t) => {
923
+ const fields = Object.entries(tableSchema.columns).reduce((acc, [key]) => {
924
+ acc[key] = t.field({
925
+ type: sortingParameterEnumRef(),
926
+ required: false
927
+ });
928
+ return acc;
929
+ }, {});
930
+ const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
931
+ const referenceModel = orderArgImplementer({ dbName: tableHelper({
932
+ db,
933
+ table: value.targetTable
934
+ }).dbName });
935
+ acc[key] = t.field({
936
+ type: referenceModel,
937
+ required: false
938
+ });
939
+ return acc;
940
+ }, {});
941
+ return {
942
+ ...fields,
943
+ ...relations
944
+ };
945
+ } });
946
+ };
947
+ ret = implement();
948
+ referenceStorage.set(inputTypeName, ret);
949
+ return ret;
950
+ };
951
+ return orderArgImplementer;
952
+ };
953
+
954
+ //#endregion
955
+ //#region lib/enum.ts
956
+ /**
957
+ * Checks if a schema type is an enum
958
+ */
959
+ function isEnumSchema(schemaType) {
960
+ return schemaType instanceof drizzle_orm_pg_core.PgEnumColumn;
961
+ }
962
+ const createEnumImplementer = ({ db, schemaBuilder }) => {
963
+ const referenceStorage = /* @__PURE__ */ new Map();
964
+ const enumImplementer = ({ tsName, enumColumn, refName }) => {
965
+ let enumSchemaName;
966
+ let enumValues;
967
+ if (tsName) {
968
+ const schemaEnum = db._.schema[tsName];
969
+ enumSchemaName = tsName.toString();
970
+ const enumCol = Object.values(db._.schema).filter((s) => typeof s === "object").map((s) => Object.values(s.columns)).flat(2).find((s) => s.config?.enum === schemaEnum);
971
+ if (!enumCol) throw new RumbleError(`Could not find applied enum column for ${tsName.toString()}.
972
+ Please ensure that you use the enum at least once as a column of a table!`);
973
+ enumValues = enumCol.enumValues;
974
+ } else if (enumColumn) {
975
+ enumSchemaName = enumColumn.config.name;
976
+ enumValues = enumColumn.enumValues;
977
+ }
978
+ if (!enumSchemaName || !enumValues) throw new RumbleError("Could not determine enum structure!");
979
+ const graphqlImplementationName = refName ?? `${(0, es_toolkit.capitalize)((0, drizzle_orm_casing.toCamelCase)(enumSchemaName))}Enum`;
980
+ let ret = referenceStorage.get(graphqlImplementationName);
981
+ if (ret) return ret;
982
+ const implement = () => schemaBuilder.enumType(graphqlImplementationName, { values: enumValues });
983
+ ret = implement();
984
+ referenceStorage.set(graphqlImplementationName, ret);
985
+ return ret;
986
+ };
987
+ return enumImplementer;
988
+ };
989
+
990
+ //#endregion
991
+ //#region lib/helpers/sqlTypes/mapSQLTypeToTSType.ts
992
+ function mapSQLTypeToGraphQLType({ sqlType, fieldName }) {
993
+ let ret;
994
+ if (isIntLikeSQLTypeString(sqlType)) ret = "Int";
995
+ if (isFloatLikeSQLTypeString(sqlType)) ret = "Float";
996
+ if (isStringLikeSQLTypeString(sqlType)) if (fieldName && (fieldName.toLowerCase().endsWith("_id") || fieldName.toLowerCase().endsWith("id"))) ret = "ID";
997
+ else ret = "String";
998
+ if (isIDLikeSQLTypeString(sqlType)) ret = "ID";
999
+ if (isBooleanSQLTypeString(sqlType)) ret = "Boolean";
1000
+ if (isDateTimeLikeSQLTypeString(sqlType)) ret = "DateTime";
1001
+ if (isDateLikeSQLTypeString(sqlType)) ret = "Date";
1002
+ if (isJSONLikeSQLTypeString(sqlType)) ret = "JSON";
1003
+ if (ret !== void 0) return ret;
1004
+ throw UnknownTypeRumbleError(sqlType, "SQL to GQL");
1005
+ }
1006
+
1007
+ //#endregion
1008
+ //#region lib/args/whereArg.ts
1009
+ const makeDefaultName = (dbName) => `${(0, es_toolkit.capitalize)((0, drizzle_orm_casing.toCamelCase)(dbName.toString()))}WhereInputArgument`;
1010
+ const createWhereArgImplementer = ({ db, schemaBuilder, enumImplementer }) => {
1011
+ const referenceStorage = /* @__PURE__ */ new Map();
1012
+ const whereArgImplementer = ({ table, refName, dbName }) => {
1013
+ const tableSchema = tableHelper({
1014
+ db,
1015
+ table: dbName ?? table
1016
+ });
1017
+ const inputTypeName = refName ?? makeDefaultName(tableSchema.tsName);
1018
+ let ret = referenceStorage.get(inputTypeName);
1019
+ if (ret) return ret;
1020
+ const implement = () => {
1021
+ return schemaBuilder.inputType(inputTypeName, { fields: (t) => {
1022
+ const mapSQLTypeStringToInputPothosType = (sqlType, fieldName) => {
1023
+ const gqlType = mapSQLTypeToGraphQLType({
1024
+ sqlType,
1025
+ fieldName
1026
+ });
1027
+ switch (gqlType) {
1028
+ case "Int": return t.field({ type: "IntWhereInputArgument" });
1029
+ case "String": return t.field({ type: "StringWhereInputArgument" });
1030
+ case "Boolean": return t.boolean({ required: false });
1031
+ case "Date": return t.field({ type: "DateWhereInputArgument" });
1032
+ case "DateTime": return t.field({ type: "DateWhereInputArgument" });
1033
+ case "Float": return t.field({ type: "FloatWhereInputArgument" });
1034
+ case "ID": return t.id({ required: false });
1035
+ case "JSON": return t.field({
1036
+ type: "JSON",
1037
+ required: false
1038
+ });
1039
+ default: throw new RumbleError(`Unsupported argument type ${gqlType} for column ${sqlType}`);
1040
+ }
1041
+ };
1042
+ const fields = Object.entries(tableSchema.columns).reduce((acc, [key, value]) => {
1043
+ if (isEnumSchema(value)) {
1044
+ const enumImpl = enumImplementer({ enumColumn: value });
1045
+ acc[key] = t.field({
1046
+ type: enumImpl,
1047
+ required: false
1048
+ });
1049
+ } else acc[key] = mapSQLTypeStringToInputPothosType(value.getSQLType(), key);
1050
+ return acc;
1051
+ }, {});
1052
+ const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
1053
+ const referenceModel = whereArgImplementer({ dbName: tableHelper({
1054
+ db,
1055
+ table: value.targetTable
1056
+ }).dbName });
1057
+ acc[key] = t.field({
1058
+ type: referenceModel,
1059
+ required: false
1060
+ });
1061
+ return acc;
1062
+ }, {});
1063
+ return {
1064
+ ...fields,
1065
+ ...relations
1066
+ };
1067
+ } });
1068
+ };
1069
+ ret = implement();
1070
+ referenceStorage.set(inputTypeName, ret);
1071
+ return ret;
1072
+ };
1073
+ return whereArgImplementer;
1074
+ };
1075
+
1076
+ //#endregion
1077
+ //#region lib/client/client.ts
1078
+ const clientCreatorImplementer = ({ builtSchema }) => {
1079
+ if (process.env.NODE_ENV !== "development") console.warn("Running rumble client generation in non development mode. Are you sure this is correct?");
1080
+ const clientCreator = async ({ apiUrl, outputPath, rumbleImportPath, useExternalUrqlClient }) => {
1081
+ await generateFromSchema({
1082
+ schema: builtSchema(),
1083
+ outputPath,
1084
+ rumbleImportPath,
1085
+ apiUrl,
1086
+ useExternalUrqlClient
1087
+ });
1088
+ };
1089
+ return clientCreator;
1090
+ };
1091
+
1092
+ //#endregion
1093
+ //#region lib/context.ts
1094
+ const createContextFunction = ({ context: makeUserContext, abilityBuilder }) => {
1095
+ const builtAbilityBuilder = lazy(() => abilityBuilder._.build());
1096
+ return async (req) => {
1097
+ const userContext = makeUserContext ? await makeUserContext(req) : {};
1098
+ return {
1099
+ ...userContext,
1100
+ abilities: builtAbilityBuilder()(userContext)
1101
+ };
1102
+ };
1103
+ };
1104
+
1105
+ //#endregion
1106
+ //#region lib/helpers/sofaOpenAPIWebhookDocs.ts
1107
+ const sofaOpenAPIWebhookDocs = {
1108
+ paths: {
1109
+ "/webhook": { post: {
1110
+ operationId: "webhook_create",
1111
+ description: "Creates a webhook subscription.",
1112
+ tags: [],
1113
+ parameters: [],
1114
+ requestBody: { content: { "application/json": { schema: { $ref: "#/components/schemas/WebhookCreateBody" } } } },
1115
+ responses: { "200": {
1116
+ description: "",
1117
+ content: { "application/json": { schema: { $ref: "#/components/schemas/WebhookDetailResponse" } } }
1118
+ } }
1119
+ } },
1120
+ "/webhook/{id}": {
1121
+ post: {
1122
+ operationId: "webhook_update",
1123
+ description: "Updates a webhook subscription.",
1124
+ parameters: [{
1125
+ name: "id",
1126
+ in: "path",
1127
+ description: "The ID of the webhook to update",
1128
+ required: true,
1129
+ schema: { type: "string" }
1130
+ }],
1131
+ requestBody: { content: { "application/json": { schema: { $ref: "#/components/schemas/WebhookCreateBody" } } } },
1132
+ responses: { "200": {
1133
+ description: "",
1134
+ content: { "application/json": { schema: { $ref: "#/components/schemas/WebhookDetailResponse" } } }
1135
+ } }
1136
+ },
1137
+ delete: {
1138
+ operationId: "webhook_delete",
1139
+ description: "Removes a webhook subscription.",
1140
+ tags: [],
1141
+ parameters: [{
1142
+ name: "id",
1143
+ in: "path",
1144
+ description: "The ID of the webhook to delete",
1145
+ required: true,
1146
+ schema: { type: "string" }
1147
+ }],
1148
+ responses: { "200": {
1149
+ description: "",
1150
+ content: { "application/json": { schema: { $ref: "#/components/schemas/WebhookDetailResponse" } } }
1151
+ } }
1152
+ }
1153
+ }
1154
+ },
1155
+ components: { schemas: {
1156
+ WebhookCreateBody: {
1157
+ type: "object",
1158
+ properties: {
1159
+ subscription: {
1160
+ description: "The subscription to subscribe to. In many cases, these match the available query IDs without the '_query' suffix. E.g., 'users_query' -> 'users'. See the graphql schema for more details on what subscriptions are available.",
1161
+ type: "string"
1162
+ },
1163
+ variables: {
1164
+ description: "The variables to pass to the subscription.",
1165
+ type: "object"
1166
+ },
1167
+ url: {
1168
+ description: "The URL to send the webhook to.",
1169
+ type: "string"
1170
+ }
1171
+ }
1172
+ },
1173
+ WebhookDetailResponse: {
1174
+ type: "object",
1175
+ properties: { id: {
1176
+ description: "The ID of the webhook. Can be used as reference in update or delete calls.",
1177
+ type: "string"
1178
+ } }
1179
+ },
1180
+ DateTime: {
1181
+ type: "string",
1182
+ format: "date-time"
1183
+ },
1184
+ Date: {
1185
+ type: "string",
1186
+ format: "date"
1187
+ }
1188
+ } }
1189
+ };
1190
+
1191
+ //#endregion
1192
+ //#region lib/helpers/sqlTypes/mapDrizzleTypeToGraphQlType.ts
1193
+ function buildPothosResponseTypeFromGraphQLType({ builder, sqlType, fieldName, nullable }) {
1194
+ const gqlType = mapSQLTypeToGraphQLType({
1195
+ sqlType,
1196
+ fieldName
1197
+ });
1198
+ switch (gqlType) {
1199
+ case "Int": return builder.exposeInt(fieldName, { nullable });
1200
+ case "String": return builder.exposeString(fieldName, { nullable });
1201
+ case "Boolean": return builder.exposeBoolean(fieldName, { nullable });
1202
+ case "Date": return builder.field({
1203
+ type: "Date",
1204
+ resolve: (element) => element[fieldName],
1205
+ nullable
1206
+ });
1207
+ case "DateTime": return builder.field({
1208
+ type: "DateTime",
1209
+ resolve: (element) => element[fieldName],
1210
+ nullable
1211
+ });
1212
+ case "Float": return builder.exposeFloat(fieldName, { nullable });
1213
+ case "ID": return builder.exposeID(fieldName, { nullable });
1214
+ case "JSON": return builder.field({
1215
+ type: "JSON",
1216
+ resolve: (element) => element[fieldName],
1217
+ nullable
1218
+ });
1219
+ default: throw new RumbleError(`Unsupported object type ${gqlType} for column ${fieldName}`);
1220
+ }
1221
+ }
1222
+
1223
+ //#endregion
1224
+ //#region lib/helpers/determineDialectFromSchema.ts
1225
+ function determineDBDialectFromSchema(schema) {
1226
+ const found = /* @__PURE__ */ new Set();
1227
+ for (const table of Object.values(schema)) {
1228
+ if (typeof table !== "object") continue;
1229
+ if (table instanceof drizzle_orm_pg_core.PgTable) found.add("postgres");
1230
+ else if (table instanceof drizzle_orm_mysql_core.MySqlTable) found.add("mysql");
1231
+ else if (table instanceof drizzle_orm_sqlite_core.SQLiteTable) found.add("sqlite");
1232
+ }
1233
+ const dialects = Array.from(found);
1234
+ if (dialects.length === 1) return dialects[0];
1235
+ if (dialects.length === 0) throw new Error("No tables found in schema, could not determine dialect");
1236
+ throw new Error(`Multiple dialects found in schema: ${dialects.join(", ")}`);
1237
+ }
1238
+ function isPostgresDB(db) {
1239
+ return determineDBDialectFromSchema(db._.schema) === "postgres";
1240
+ }
1241
+
1242
+ //#endregion
1243
+ //#region lib/search.ts
1244
+ async function initSearchIfApplicable(db) {
1245
+ if (!isPostgresDB(db)) {
1246
+ console.info("Database dialect is not compatible with search, skipping search initialization.");
1247
+ return;
1248
+ }
1249
+ await db.execute(drizzle_orm.sql`CREATE EXTENSION IF NOT EXISTS pg_trgm;`);
1250
+ }
1251
+ /**
1252
+ * Performs adjustments to the query args to issue a full text search in case the
1253
+ * respective feature is enabled and a search term was provided.
1254
+ */
1255
+ function adjustQueryArgsForSearch({ search, args, tableSchema, abilities }) {
1256
+ if (search?.enabled && args.search && args.search.length > 0) {
1257
+ const originalOrderBy = (0, es_toolkit.cloneDeep)(args.orderBy);
1258
+ args.orderBy = (table) => {
1259
+ const argsOrderBySQL = drizzle_orm.sql.join(Object.entries(originalOrderBy ?? {}).map(([key, value]) => {
1260
+ if (value === "asc") return drizzle_orm.sql`${table[key]} ASC`;
1261
+ else if (value === "desc") return drizzle_orm.sql`${table[key]} DESC`;
1262
+ else throw new Error(`Invalid value ${value} for orderBy`);
1263
+ }), drizzle_orm.sql.raw(", "));
1264
+ const columnsToSearch = abilities.query.many.columns ? Object.entries(tableSchema.columns).filter(([key]) => abilities.query.many.columns[key]) : Object.entries(tableSchema.columns);
1265
+ const searchSQL = drizzle_orm.sql`GREATEST(${drizzle_orm.sql.join(columnsToSearch.map(([key]) => {
1266
+ return drizzle_orm.sql`similarity(${table[key]}::TEXT, ${args.search})`;
1267
+ }), drizzle_orm.sql.raw(", "))}) DESC`;
1268
+ return originalOrderBy ? drizzle_orm.sql.join([argsOrderBySQL, searchSQL], drizzle_orm.sql.raw(", ")) : searchSQL;
1269
+ };
1270
+ args.where = { AND: [(0, es_toolkit.cloneDeep)(args.where) ?? {}, { RAW: (table) => {
1271
+ return drizzle_orm.sql`GREATEST(${drizzle_orm.sql.join(Object.entries(tableSchema.columns).map(([key]) => {
1272
+ return drizzle_orm.sql`similarity(${table[key]}::TEXT, ${args.search})`;
1273
+ }), drizzle_orm.sql.raw(", "))}) > ${search.threshold ?? .1}`;
1274
+ } }] };
1275
+ }
1276
+ }
1277
+
1278
+ //#endregion
1279
+ //#region lib/object.ts
1280
+ const isProbablyAConfigObject = (t) => {
1281
+ if (typeof t !== "object") return false;
1282
+ if (Object.keys(t).some((k) => [
1283
+ "args",
1284
+ "nullable",
1285
+ "query",
1286
+ "subscribe",
1287
+ "description",
1288
+ "type",
1289
+ "resolve"
1290
+ ].find((e) => e === k))) return true;
1291
+ return false;
1292
+ };
1293
+ const createObjectImplementer = ({ db, search, schemaBuilder, makePubSubInstance, whereArgImplementer, orderArgImplementer, enumImplementer, abilityBuilder }) => {
1294
+ return ({ table, refName, readAction = "read", adjust }) => {
1295
+ const tableSchema = tableHelper({
1296
+ db,
1297
+ table
1298
+ });
1299
+ if (Object.keys(tableSchema.primaryKey).length === 0) console.warn(`Could not find primary key for ${table.toString()}. Cannot register subscriptions!`);
1300
+ const primaryKey = Object.values(tableSchema.primaryKey)[0];
1301
+ const { registerOnInstance } = makePubSubInstance({ table });
1302
+ return schemaBuilder.drizzleObject(table, {
1303
+ name: refName ?? (0, es_toolkit.capitalize)(table.toString()),
1304
+ subscribe: (subscriptions, element, _context) => {
1305
+ if (!primaryKey) return;
1306
+ const primaryKeyValue = element[primaryKey.name];
1307
+ if (!primaryKeyValue) {
1308
+ console.warn(`Could not find primary key value for ${JSON.stringify(element)}. Cannot register subscription!`);
1309
+ return;
1310
+ }
1311
+ registerOnInstance({
1312
+ instance: subscriptions,
1313
+ action: "updated",
1314
+ primaryKeyValue
1315
+ });
1316
+ },
1317
+ applyFilters: abilityBuilder?._.registeredFilters({
1318
+ table,
1319
+ action: readAction
1320
+ }),
1321
+ fields: (t) => {
1322
+ const columns = tableSchema.columns;
1323
+ const configMap = /* @__PURE__ */ new Map();
1324
+ const userAdjustments = adjust?.(new Proxy(t, { get: (target, prop) => {
1325
+ if (typeof target[prop] !== "function" || prop === "arg" || prop === "builder" || prop === "graphqlKind" || prop === "kind" || prop === "listRef" || prop === "table" || prop === "typename" || prop === "variant" || prop.toString().startsWith("boolean") || prop.toString().startsWith("float") || prop.toString().startsWith("id") || prop.toString().startsWith("int") || prop.toString().startsWith("string") || prop.toString().startsWith("expose")) return target[prop];
1326
+ return (...params) => {
1327
+ const ref = target[prop](...params);
1328
+ const configObject = params.find(isProbablyAConfigObject);
1329
+ if (!configObject) throw new RumbleError("Expected config object to be passed to adjust field");
1330
+ configMap.set(ref, {
1331
+ params,
1332
+ creatorFunction: target[prop],
1333
+ configObject
1334
+ });
1335
+ return ref;
1336
+ };
1337
+ } })) ?? {};
1338
+ const fields = Object.entries(columns).reduce((acc, [key, value]) => {
1339
+ if (userAdjustments[key]) {
1340
+ const { params, creatorFunction, configObject } = configMap.get(userAdjustments[key]);
1341
+ if (typeof configObject.nullable !== "boolean") configObject.nullable = !value.notNull;
1342
+ userAdjustments[key] = creatorFunction.bind(t)(...params);
1343
+ return acc;
1344
+ }
1345
+ if (isEnumSchema(value)) {
1346
+ const enumImpl = enumImplementer({ enumColumn: value });
1347
+ acc[key] = t.field({
1348
+ type: enumImpl,
1349
+ resolve: (element) => element[key],
1350
+ nullable: !value.notNull
1351
+ });
1352
+ } else acc[key] = buildPothosResponseTypeFromGraphQLType({
1353
+ builder: t,
1354
+ sqlType: value.getSQLType(),
1355
+ fieldName: key,
1356
+ nullable: !value.notNull
1357
+ });
1358
+ return acc;
1359
+ }, {});
1360
+ const relations = Object.entries(tableSchema.relations ?? {}).reduce((acc, [key, value]) => {
1361
+ const relationSchema = tableHelper({
1362
+ db,
1363
+ table: value.targetTable
1364
+ });
1365
+ const WhereArg = whereArgImplementer({ dbName: relationSchema.dbName });
1366
+ const OrderArg = orderArgImplementer({ dbName: relationSchema.dbName });
1367
+ const relationTablePubSub = makePubSubInstance({ table: relationSchema.tsName });
1368
+ let nullable = false;
1369
+ let isMany = true;
1370
+ let filterSpecifier = "many";
1371
+ if (value instanceof drizzle_orm.One) {
1372
+ isMany = false;
1373
+ nullable = value.optional;
1374
+ filterSpecifier = "single";
1375
+ }
1376
+ const subscribe = (subscriptions, _element) => {
1377
+ relationTablePubSub.registerOnInstance({
1378
+ instance: subscriptions,
1379
+ action: "created"
1380
+ });
1381
+ relationTablePubSub.registerOnInstance({
1382
+ instance: subscriptions,
1383
+ action: "removed"
1384
+ });
1385
+ };
1386
+ if (userAdjustments[key]) {
1387
+ const { params, creatorFunction, configObject } = configMap.get(userAdjustments[key]);
1388
+ if (typeof configObject.nullable !== "boolean") configObject.nullable = nullable;
1389
+ if (typeof configObject.subscribe !== "function") configObject.subscribe = subscribe;
1390
+ userAdjustments[key] = creatorFunction.bind(t)(...params);
1391
+ return acc;
1392
+ }
1393
+ const args = {
1394
+ where: t.arg({
1395
+ type: WhereArg,
1396
+ required: false
1397
+ }),
1398
+ orderBy: t.arg({
1399
+ type: OrderArg,
1400
+ required: false
1401
+ }),
1402
+ ...isMany ? {
1403
+ offset: t.arg.int({ required: false }),
1404
+ limit: t.arg.int({ required: false })
1405
+ } : {},
1406
+ search: t.arg.string({ required: false })
1407
+ };
1408
+ if (!search?.enabled || !isMany) delete args.search;
1409
+ acc[key] = t.relation(key, {
1410
+ args,
1411
+ subscribe,
1412
+ nullable,
1413
+ description: `Get the ${pluralize.default.plural(relationSchema.tsName)} related to this ${pluralize.default.singular(tableSchema.tsName)}`,
1414
+ query: (args$1, ctx) => {
1415
+ args$1 = JSON.parse(JSON.stringify(args$1));
1416
+ if (isMany) adjustQueryArgsForSearch({
1417
+ search,
1418
+ args: args$1,
1419
+ tableSchema: relationSchema,
1420
+ abilities: ctx.abilities[relationSchema.tsName].filter(readAction)
1421
+ });
1422
+ const filter = ctx.abilities[relationSchema.tsName].filter(readAction).merge({
1423
+ where: args$1.where,
1424
+ limit: args$1.limit
1425
+ }).query[filterSpecifier];
1426
+ if (args$1.offset) filter.offset = args$1.offset;
1427
+ if (args$1.orderBy) filter.orderBy = args$1.orderBy;
1428
+ return filter;
1429
+ }
1430
+ });
1431
+ return acc;
1432
+ }, {});
1433
+ return {
1434
+ ...fields,
1435
+ ...relations,
1436
+ ...userAdjustments
1437
+ };
1438
+ }
1439
+ });
1440
+ };
1441
+ };
1442
+
1443
+ //#endregion
1444
+ //#region lib/pubsub.ts
1445
+ const SUBSCRIPTION_NOTIFIER_RUMBLE_PREFIX = "RUMBLE_SUBSCRIPTION_NOTIFICATION";
1446
+ const SUBSCRIPTION_NOTIFIER_REMOVED = "REMOVED";
1447
+ const SUBSCRIPTION_NOTIFIER_UPDATED = "UPDATED";
1448
+ const SUBSCRIPTION_NOTIFIER_CREATED = "CREATED";
1449
+ const createPubSubInstance = ({ subscriptions }) => {
1450
+ const pubsub = subscriptions ? (0, graphql_yoga.createPubSub)(...subscriptions) : (0, graphql_yoga.createPubSub)();
1451
+ const makePubSubInstance = ({ table }) => {
1452
+ function makePubSubKey({ action, tableName, primaryKeyValue }) {
1453
+ let actionKey;
1454
+ switch (action) {
1455
+ case "created":
1456
+ actionKey = SUBSCRIPTION_NOTIFIER_CREATED;
1457
+ break;
1458
+ case "removed":
1459
+ actionKey = SUBSCRIPTION_NOTIFIER_REMOVED;
1460
+ break;
1461
+ case "updated":
1462
+ actionKey = SUBSCRIPTION_NOTIFIER_UPDATED;
1463
+ break;
1464
+ default: throw new Error(`Unknown action: ${action}`);
1465
+ }
1466
+ return `${SUBSCRIPTION_NOTIFIER_RUMBLE_PREFIX}/${tableName}${primaryKeyValue ? `/${primaryKeyValue}` : ""}/${actionKey}`;
1467
+ }
1468
+ return {
1469
+ registerOnInstance({ instance, action, primaryKeyValue }) {
1470
+ const key = makePubSubKey({
1471
+ tableName: table.toString(),
1472
+ action,
1473
+ primaryKeyValue
1474
+ });
1475
+ instance.register(key);
1476
+ },
1477
+ created() {
1478
+ const key = makePubSubKey({
1479
+ tableName: table.toString(),
1480
+ action: "created"
1481
+ });
1482
+ return pubsub.publish(key);
1483
+ },
1484
+ removed() {
1485
+ const key = makePubSubKey({
1486
+ tableName: table.toString(),
1487
+ action: "removed"
1488
+ });
1489
+ return pubsub.publish(key);
1490
+ },
1491
+ updated(primaryKeyValue) {
1492
+ const keys = (Array.isArray(primaryKeyValue) ? primaryKeyValue : [primaryKeyValue]).map((primaryKeyValue$1) => makePubSubKey({
1493
+ tableName: table.toString(),
1494
+ action: "updated",
1495
+ primaryKeyValue: primaryKeyValue$1
1496
+ }));
1497
+ const uniqueKeys = Array.from(new Set(keys));
1498
+ for (const key of uniqueKeys) pubsub.publish(key);
1499
+ }
1500
+ };
1501
+ };
1502
+ return {
1503
+ pubsub,
1504
+ makePubSubInstance
1505
+ };
1506
+ };
1507
+
1508
+ //#endregion
1509
+ //#region lib/query.ts
1510
+ const createQueryImplementer = ({ db, schemaBuilder, search, whereArgImplementer, orderArgImplementer, makePubSubInstance }) => {
1511
+ return ({ table, readAction = "read", listAction = "read" }) => {
1512
+ const WhereArg = whereArgImplementer({ table });
1513
+ const OrderArg = orderArgImplementer({ table });
1514
+ const tableSchema = tableHelper({
1515
+ db,
1516
+ table
1517
+ });
1518
+ const primaryKeyField = Object.values(tableSchema.primaryKey)[0];
1519
+ const { registerOnInstance } = makePubSubInstance({ table });
1520
+ return schemaBuilder.queryFields((t) => {
1521
+ const manyArgs = {
1522
+ where: t.arg({
1523
+ type: WhereArg,
1524
+ required: false
1525
+ }),
1526
+ orderBy: t.arg({
1527
+ type: OrderArg,
1528
+ required: false
1529
+ }),
1530
+ limit: t.arg.int({ required: false }),
1531
+ offset: t.arg.int({ required: false }),
1532
+ search: t.arg.string({ required: false })
1533
+ };
1534
+ if (!search?.enabled) delete manyArgs.search;
1535
+ return {
1536
+ [pluralize.default.plural(table.toString())]: t.drizzleField({
1537
+ type: [table],
1538
+ nullable: false,
1539
+ smartSubscription: true,
1540
+ description: `List all ${pluralize.default.plural(table.toString())}`,
1541
+ subscribe: (subscriptions, _root, _args, _ctx, _info) => {
1542
+ registerOnInstance({
1543
+ instance: subscriptions,
1544
+ action: "created"
1545
+ });
1546
+ registerOnInstance({
1547
+ instance: subscriptions,
1548
+ action: "removed"
1549
+ });
1550
+ },
1551
+ args: manyArgs,
1552
+ resolve: (query, _root, args, ctx, _info) => {
1553
+ Object.setPrototypeOf(args, Object.prototype);
1554
+ adjustQueryArgsForSearch({
1555
+ search,
1556
+ args,
1557
+ tableSchema,
1558
+ abilities: ctx.abilities[table].filter(listAction)
1559
+ });
1560
+ const mappedArgs = mapNullFieldsToUndefined(args);
1561
+ const filter = ctx.abilities[table].filter(listAction).merge(mappedArgs).query.many;
1562
+ if (mappedArgs.offset) filter.offset = mappedArgs.offset;
1563
+ if (mappedArgs.orderBy) filter.orderBy = mappedArgs.orderBy;
1564
+ const queryInstance = query(filter);
1565
+ if (filter.columns) queryInstance.columns = filter.columns;
1566
+ return db.query[table].findMany(queryInstance);
1567
+ }
1568
+ }),
1569
+ [pluralize.default.singular(table.toString())]: t.drizzleField({
1570
+ type: table,
1571
+ nullable: false,
1572
+ smartSubscription: true,
1573
+ description: `Get a single ${pluralize.default.singular(table.toString())} by ID`,
1574
+ args: { id: t.arg.id({ required: true }) },
1575
+ resolve: (query, _root, args, ctx, _info) => {
1576
+ Object.setPrototypeOf(args, Object.prototype);
1577
+ const filter = ctx.abilities[table].filter(readAction).merge({ where: { [primaryKeyField.name]: args.id } }).query.single;
1578
+ const q = query(filter);
1579
+ if (filter.columns) q.columns = filter.columns;
1580
+ return db.query[table].findFirst(q).then(assertFindFirstExists);
1581
+ }
1582
+ })
1583
+ };
1584
+ });
1585
+ };
1586
+ };
1587
+
1588
+ //#endregion
1589
+ //#region lib/args/whereArgsImplementer.ts
1590
+ function implementDefaultWhereInputArgs(schemaBuilder) {
1591
+ const IntWhereInputArgument = schemaBuilder.inputRef("IntWhereInputArgument").implement({ fields: (t) => ({
1592
+ eq: t.int(),
1593
+ ne: t.int(),
1594
+ gt: t.int(),
1595
+ gte: t.int(),
1596
+ lt: t.int(),
1597
+ lte: t.int(),
1598
+ in: t.intList(),
1599
+ notIn: t.intList(),
1600
+ like: t.string(),
1601
+ ilike: t.string(),
1602
+ notLike: t.string(),
1603
+ notIlike: t.string(),
1604
+ isNull: t.boolean(),
1605
+ isNotNull: t.boolean(),
1606
+ arrayOverlaps: t.intList(),
1607
+ arrayContained: t.intList(),
1608
+ arrayContains: t.intList(),
1609
+ AND: t.field({ type: [IntWhereInputArgument] }),
1610
+ OR: t.field({ type: [IntWhereInputArgument] }),
1611
+ NOT: t.field({ type: IntWhereInputArgument })
1612
+ }) });
1613
+ const FloatWhereInputArgument = schemaBuilder.inputRef("FloatWhereInputArgument").implement({ fields: (t) => ({
1614
+ eq: t.float(),
1615
+ ne: t.float(),
1616
+ gt: t.float(),
1617
+ gte: t.float(),
1618
+ lt: t.float(),
1619
+ lte: t.float(),
1620
+ in: t.floatList(),
1621
+ notIn: t.floatList(),
1622
+ like: t.string(),
1623
+ ilike: t.string(),
1624
+ notLike: t.string(),
1625
+ notIlike: t.string(),
1626
+ isNull: t.boolean(),
1627
+ isNotNull: t.boolean(),
1628
+ arrayOverlaps: t.floatList(),
1629
+ arrayContained: t.floatList(),
1630
+ arrayContains: t.floatList(),
1631
+ AND: t.field({ type: [FloatWhereInputArgument] }),
1632
+ OR: t.field({ type: [FloatWhereInputArgument] }),
1633
+ NOT: t.field({ type: FloatWhereInputArgument })
1634
+ }) });
1635
+ const StringWhereInputArgument = schemaBuilder.inputRef("StringWhereInputArgument").implement({ fields: (t) => ({
1636
+ eq: t.string(),
1637
+ ne: t.string(),
1638
+ gt: t.string(),
1639
+ gte: t.string(),
1640
+ lt: t.string(),
1641
+ lte: t.string(),
1642
+ in: t.stringList(),
1643
+ notIn: t.stringList(),
1644
+ like: t.string(),
1645
+ ilike: t.string(),
1646
+ notLike: t.string(),
1647
+ notIlike: t.string(),
1648
+ isNull: t.boolean(),
1649
+ isNotNull: t.boolean(),
1650
+ arrayOverlaps: t.stringList(),
1651
+ arrayContained: t.stringList(),
1652
+ arrayContains: t.stringList(),
1653
+ AND: t.field({ type: [StringWhereInputArgument] }),
1654
+ OR: t.field({ type: [StringWhereInputArgument] }),
1655
+ NOT: t.field({ type: StringWhereInputArgument })
1656
+ }) });
1657
+ const DateWhereInputArgument = schemaBuilder.inputRef("DateWhereInputArgument").implement({ fields: (t) => ({
1658
+ eq: t.field({ type: "Date" }),
1659
+ ne: t.field({ type: "Date" }),
1660
+ gt: t.field({ type: "Date" }),
1661
+ gte: t.field({ type: "Date" }),
1662
+ lt: t.field({ type: "Date" }),
1663
+ lte: t.field({ type: "Date" }),
1664
+ in: t.field({ type: ["Date"] }),
1665
+ notIn: t.field({ type: ["Date"] }),
1666
+ like: t.string(),
1667
+ ilike: t.string(),
1668
+ notLike: t.string(),
1669
+ notIlike: t.string(),
1670
+ isNull: t.boolean(),
1671
+ isNotNull: t.boolean(),
1672
+ arrayOverlaps: t.field({ type: ["Date"] }),
1673
+ arrayContained: t.field({ type: ["Date"] }),
1674
+ arrayContains: t.field({ type: ["Date"] }),
1675
+ AND: t.field({ type: [DateWhereInputArgument] }),
1676
+ OR: t.field({ type: [DateWhereInputArgument] }),
1677
+ NOT: t.field({ type: DateWhereInputArgument })
1678
+ }) });
1679
+ }
1680
+
1681
+ //#endregion
1682
+ //#region lib/runtimeFiltersPlugin/filterTypes.ts
1683
+ const pluginName = "RuntimeFiltersPlugin";
1684
+
1685
+ //#endregion
1686
+ //#region lib/helpers/applyFilters.ts
1687
+ /**
1688
+ * A helper to apply a list of filters to a given list of entities.
1689
+ *
1690
+ * @example
1691
+ *
1692
+ * ```ts
1693
+ * const filtered = await applyFilters({
1694
+ filters: abilityBuilder.registeredFilters.posts.update,
1695
+ entities: entitiesToFilter,
1696
+ context: ctx,
1697
+ });
1698
+ * ```
1699
+ */
1700
+ const applyFilters = async ({ filters, entities, context }) => {
1701
+ return Array.from((await Promise.all(filters.map((f) => f({
1702
+ context,
1703
+ entities
1704
+ })))).reduce((acc, val) => {
1705
+ for (const element of val) acc.add(element);
1706
+ return acc;
1707
+ }, /* @__PURE__ */ new Set()));
1708
+ };
1709
+
1710
+ //#endregion
1711
+ //#region lib/runtimeFiltersPlugin/runtimeFiltersPlugin.ts
1712
+ const applyFiltersKey = "applyFilters";
1713
+ var RuntimeFiltersPlugin = class extends __pothos_core.BasePlugin {
1714
+ wrapResolve(resolver, fieldConfig) {
1715
+ return async (parent, args, context, info) => {
1716
+ let filters;
1717
+ const fieldType = fieldConfig?.type;
1718
+ if (fieldType.kind === "List") filters = fieldType.type?.ref.currentConfig.pothosOptions[applyFiltersKey];
1719
+ else if (fieldType.kind === "Object") filters = fieldType.ref.currentConfig.pothosOptions[applyFiltersKey];
1720
+ if (!filters || !Array.isArray(filters) || filters.length === 0) return resolver(parent, args, context, info);
1721
+ const resolved = await resolver(parent, args, context, info);
1722
+ const allResolvedValues = Array.isArray(resolved) ? resolved : [resolved];
1723
+ const allowed = await applyFilters({
1724
+ filters: Array.isArray(filters) ? filters : [filters],
1725
+ entities: allResolvedValues,
1726
+ context
1727
+ });
1728
+ if (Array.isArray(resolved)) return allowed;
1729
+ return allowed[0] ?? null;
1730
+ };
1731
+ }
1732
+ };
1733
+ let registered = false;
1734
+ function registerRuntimeFiltersPlugin() {
1735
+ if (!registered) {
1736
+ __pothos_core.default.registerPlugin(pluginName, RuntimeFiltersPlugin);
1737
+ registered = true;
1738
+ }
1739
+ }
1740
+
1741
+ //#endregion
1742
+ //#region lib/schemaBuilder.ts
1743
+ const createSchemaBuilder = ({ db, disableDefaultObjects, pubsub, pothosConfig }) => {
1744
+ registerRuntimeFiltersPlugin();
1745
+ const schemaBuilder = new __pothos_core.default({
1746
+ ...pothosConfig,
1747
+ plugins: [
1748
+ pluginName,
1749
+ __pothos_plugin_drizzle.default,
1750
+ __pothos_plugin_smart_subscriptions.default,
1751
+ ...pothosConfig?.plugins ?? []
1752
+ ],
1753
+ drizzle: {
1754
+ client: db,
1755
+ relations: db._.relations,
1756
+ getTableConfig(table) {
1757
+ return {
1758
+ columns: Object.values(table[Symbol.for("drizzle:Columns")]),
1759
+ primaryKeys: Object.values(table[Symbol.for("drizzle:Columns")]).filter((v) => v.primary)
1760
+ };
1761
+ }
1762
+ },
1763
+ smartSubscriptions: { ...(0, __pothos_plugin_smart_subscriptions.subscribeOptionsFromIterator)((name, _context) => {
1764
+ return pubsub.subscribe(name);
1765
+ }) }
1766
+ });
1767
+ schemaBuilder.addScalarType("JSON", graphql_scalars.JSONResolver);
1768
+ schemaBuilder.addScalarType("Date", graphql_scalars.DateResolver);
1769
+ schemaBuilder.addScalarType("DateTime", graphql_scalars.DateTimeISOResolver);
1770
+ implementDefaultWhereInputArgs(schemaBuilder);
1771
+ if (!disableDefaultObjects?.query) schemaBuilder.queryType({});
1772
+ if (!disableDefaultObjects?.subscription) schemaBuilder.subscriptionType({});
1773
+ if (!disableDefaultObjects?.mutation) schemaBuilder.mutationType({});
1774
+ return { schemaBuilder };
1775
+ };
1776
+
1777
+ //#endregion
1778
+ //#region lib/rumble.ts
1779
+ const rumble = (rumbleInput) => {
1780
+ if (!rumbleInput.db._.schema) throw new RumbleError(`
1781
+ rumble could not find any schema in the provided drizzle instance.
1782
+ Make sure you import the schema and pass it to the drizzle instance:
1783
+
1784
+ export const db = drizzle(
1785
+ "postgres://postgres:postgres@localhost:5432/postgres",
1786
+ {
1787
+ relations,
1788
+ schema, // <--- add this line
1789
+ },
1790
+ );
1791
+
1792
+ `);
1793
+ if (!rumbleInput.db._.relations) throw new RumbleError(`
1794
+ rumble could not find any relations in the provided drizzle instance.
1795
+ Make sure you import the relations and pass them to the drizzle instance:
1796
+
1797
+ export const db = drizzle(
1798
+ "postgres://postgres:postgres@localhost:5432/postgres",
1799
+ {
1800
+ relations, // <--- add this line
1801
+ schema,
1802
+ },
1803
+ );
1804
+
1805
+ `);
1806
+ if (!rumbleInput.actions) rumbleInput.actions = [
1807
+ "read",
1808
+ "update",
1809
+ "delete"
1810
+ ];
1811
+ if (rumbleInput.defaultLimit === void 0) rumbleInput.defaultLimit = 100;
1812
+ if (rumbleInput.search?.enabled) initSearchIfApplicable(rumbleInput.db);
1813
+ const abilityBuilder = createAbilityBuilder(rumbleInput);
1814
+ const context = createContextFunction({
1815
+ ...rumbleInput,
1816
+ abilityBuilder
1817
+ });
1818
+ const { makePubSubInstance, pubsub } = createPubSubInstance({ ...rumbleInput });
1819
+ const { schemaBuilder } = createSchemaBuilder({
1820
+ ...rumbleInput,
1821
+ pubsub
1822
+ });
1823
+ const enum_ = createEnumImplementer({
1824
+ ...rumbleInput,
1825
+ schemaBuilder
1826
+ });
1827
+ const whereArg = createWhereArgImplementer({
1828
+ ...rumbleInput,
1829
+ schemaBuilder,
1830
+ enumImplementer: enum_
1831
+ });
1832
+ const orderArg = createOrderArgImplementer({
1833
+ ...rumbleInput,
1834
+ schemaBuilder
1835
+ });
1836
+ const object = createObjectImplementer({
1837
+ ...rumbleInput,
1838
+ schemaBuilder,
1839
+ makePubSubInstance,
1840
+ whereArgImplementer: whereArg,
1841
+ orderArgImplementer: orderArg,
1842
+ enumImplementer: enum_,
1843
+ abilityBuilder
1844
+ });
1845
+ const query = createQueryImplementer({
1846
+ ...rumbleInput,
1847
+ schemaBuilder,
1848
+ whereArgImplementer: whereArg,
1849
+ orderArgImplementer: orderArg,
1850
+ makePubSubInstance
1851
+ });
1852
+ const builtSchema = lazy(() => schemaBuilder.toSchema());
1853
+ const createYoga = (args) => {
1854
+ const enableApiDocs = args?.enableApiDocs ?? process?.env?.NODE_ENV === "development";
1855
+ return (0, graphql_yoga.createYoga)({
1856
+ ...args,
1857
+ graphiql: enableApiDocs,
1858
+ plugins: [...args?.plugins ?? [], ...enableApiDocs ? [] : [(0, __graphql_yoga_plugin_disable_introspection.useDisableIntrospection)(), (0, __escape_tech_graphql_armor.EnvelopArmorPlugin)()]].filter(Boolean),
1859
+ schema: builtSchema(),
1860
+ context
1861
+ });
1862
+ };
1863
+ const createSofa = (args) => {
1864
+ if (args.openAPI) (0, es_toolkit.merge)(args.openAPI, sofaOpenAPIWebhookDocs);
1865
+ return (0, sofa_api.useSofa)({
1866
+ ...args,
1867
+ schema: builtSchema(),
1868
+ context
1869
+ });
1870
+ };
1871
+ return {
1872
+ abilityBuilder,
1873
+ schemaBuilder,
1874
+ createYoga,
1875
+ createSofa,
1876
+ object,
1877
+ whereArg,
1878
+ orderArg,
1879
+ query,
1880
+ pubsub: makePubSubInstance,
1881
+ enum_,
1882
+ clientCreator: clientCreatorImplementer({
1883
+ ...rumbleInput,
1884
+ builtSchema
1885
+ })
1886
+ };
1887
+ };
1888
+
1889
+ //#endregion
1890
+ exports.RumbleError = RumbleError;
1891
+ exports.RumbleErrorSafe = RumbleErrorSafe;
1892
+ exports.assertFindFirstExists = assertFindFirstExists;
1893
+ exports.assertFirstEntryExists = assertFirstEntryExists;
1894
+ exports.generateFromSchema = generateFromSchema;
1895
+ exports.makeLiveQuery = makeLiveQuery;
1896
+ exports.makeMutation = makeMutation;
1897
+ exports.makeQuery = makeQuery;
1898
+ exports.makeSubscription = makeSubscription;
1899
+ exports.mapNullFieldsToUndefined = mapNullFieldsToUndefined;
1900
+ exports.rumble = rumble;
1901
+ //# sourceMappingURL=index.cjs.map