@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 +1901 -0
- package/out/index.cjs.map +1 -0
- package/out/index.d.cts +922 -0
- package/out/index.d.cts.map +1 -0
- package/out/index.d.ts +922 -0
- package/out/index.d.ts.map +1 -0
- package/out/index.js +1849 -0
- package/out/index.js.map +1 -0
- package/package.json +61 -1
- package/LICENSE +0 -201
- package/README.md +0 -285
- package/index.cjs +0 -65
- package/index.cjs.map +0 -1
- package/index.d.cts +0 -833
- package/index.d.ts +0 -833
- package/index.js +0 -65
- package/index.js.map +0 -1
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
|