@kyro-cms/core 0.9.8 → 0.10.0
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/dist/api-handler-graphql.cjs +22 -17
- package/dist/api-handler-graphql.cjs.map +1 -1
- package/dist/api-handler-graphql.js +18 -13
- package/dist/api-handler-graphql.js.map +1 -1
- package/dist/api-handler-trpc.cjs +13 -13
- package/dist/api-handler-trpc.js +11 -11
- package/dist/api-handler.cjs +14 -14
- package/dist/api-handler.js +11 -11
- package/dist/{bootstrap-4CELFLJO.cjs → bootstrap-FUAPY2OF.cjs} +9 -9
- package/dist/{bootstrap-4CELFLJO.cjs.map → bootstrap-FUAPY2OF.cjs.map} +1 -1
- package/dist/bootstrap-OXNVM7FP.js +7 -0
- package/dist/{bootstrap-3PV3GJ3S.js.map → bootstrap-OXNVM7FP.js.map} +1 -1
- package/dist/{chunk-HA4MGAZJ.js → chunk-354ISGA4.js} +112 -49
- package/dist/chunk-354ISGA4.js.map +1 -0
- package/dist/{chunk-2GXALYKV.js → chunk-3AKCVYDB.js} +4 -109
- package/dist/chunk-3AKCVYDB.js.map +1 -0
- package/dist/{chunk-OVZO4KB6.cjs → chunk-3P5AHK2U.cjs} +34 -38
- package/dist/chunk-3P5AHK2U.cjs.map +1 -0
- package/dist/{chunk-KGJRJ7CU.cjs → chunk-4OCMVGEY.cjs} +4 -110
- package/dist/chunk-4OCMVGEY.cjs.map +1 -0
- package/dist/{chunk-EQTMBMDR.js → chunk-526M5MOA.js} +19 -8
- package/dist/chunk-526M5MOA.js.map +1 -0
- package/dist/{chunk-5PMQQFRE.cjs → chunk-5GFQPHGT.cjs} +6 -9
- package/dist/chunk-5GFQPHGT.cjs.map +1 -0
- package/dist/chunk-5RPRLTTN.js +861 -0
- package/dist/chunk-5RPRLTTN.js.map +1 -0
- package/dist/{chunk-ZFHTJ2YX.js → chunk-AN2L2CJZ.js} +16 -20
- package/dist/chunk-AN2L2CJZ.js.map +1 -0
- package/dist/{chunk-RFFSZSCL.cjs → chunk-GYIJ3JJM.cjs} +3 -11
- package/dist/chunk-GYIJ3JJM.cjs.map +1 -0
- package/dist/{chunk-PQ72Z6WC.js → chunk-GZS4LMR7.js} +2 -5
- package/dist/chunk-GZS4LMR7.js.map +1 -0
- package/dist/{chunk-GAAHG2Z4.js → chunk-HY6B7B5A.js} +4 -7
- package/dist/chunk-HY6B7B5A.js.map +1 -0
- package/dist/{chunk-2CBCU5ML.cjs → chunk-ISKYUUP4.cjs} +142 -79
- package/dist/chunk-ISKYUUP4.cjs.map +1 -0
- package/dist/{chunk-Z7OH3MWT.cjs → chunk-LNTFRAHF.cjs} +110 -2
- package/dist/chunk-LNTFRAHF.cjs.map +1 -0
- package/dist/chunk-N4GN6Q3V.cjs +869 -0
- package/dist/chunk-N4GN6Q3V.cjs.map +1 -0
- package/dist/{chunk-6UNONDW7.js → chunk-N5QSHUA6.js} +2 -11
- package/dist/chunk-N5QSHUA6.js.map +1 -0
- package/dist/{chunk-PJ7F4TCC.cjs → chunk-NQJJYRLP.cjs} +24 -13
- package/dist/chunk-NQJJYRLP.cjs.map +1 -0
- package/dist/{chunk-65DSXNQL.js → chunk-OX6GOIPH.js} +4 -4
- package/dist/{chunk-65DSXNQL.js.map → chunk-OX6GOIPH.js.map} +1 -1
- package/dist/{chunk-Q23GAMLE.cjs → chunk-THBQG64D.cjs} +2 -5
- package/dist/chunk-THBQG64D.cjs.map +1 -0
- package/dist/{chunk-W3GT55TY.cjs → chunk-TQG7G7YI.cjs} +13 -13
- package/dist/{chunk-W3GT55TY.cjs.map → chunk-TQG7G7YI.cjs.map} +1 -1
- package/dist/{chunk-Y7AQK4R4.cjs → chunk-VKREAIBA.cjs} +2 -11
- package/dist/chunk-VKREAIBA.cjs.map +1 -0
- package/dist/{chunk-2Z7YAJBS.js → chunk-WDTTHKDS.js} +109 -3
- package/dist/chunk-WDTTHKDS.js.map +1 -0
- package/dist/{chunk-V7KZQIZ6.js → chunk-XHAAAEXX.js} +2 -10
- package/dist/chunk-XHAAAEXX.js.map +1 -0
- package/dist/cli/index.cjs +145 -51
- package/dist/cli/index.cjs.map +1 -1
- package/dist/cli/index.js +145 -51
- package/dist/cli/index.js.map +1 -1
- package/dist/drizzle/index.cjs +9 -9
- package/dist/drizzle/index.js +1 -1
- package/dist/graphql/index.cjs +7 -7
- package/dist/graphql/index.d.cts +21 -13
- package/dist/graphql/index.d.ts +21 -13
- package/dist/graphql/index.js +2 -2
- package/dist/index.cjs +92 -94
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -5
- package/dist/index.d.ts +6 -5
- package/dist/index.js +12 -14
- package/dist/index.js.map +1 -1
- package/dist/mongodb/index.cjs +3 -3
- package/dist/mongodb/index.js +1 -1
- package/dist/rest/index.cjs +8 -8
- package/dist/rest/index.js +6 -6
- package/dist/trpc/index.cjs +12 -12
- package/dist/trpc/index.js +3 -3
- package/package.json +1 -1
- package/dist/bootstrap-3PV3GJ3S.js +0 -7
- package/dist/chunk-2CBCU5ML.cjs.map +0 -1
- package/dist/chunk-2GXALYKV.js.map +0 -1
- package/dist/chunk-2Z7YAJBS.js.map +0 -1
- package/dist/chunk-5PMQQFRE.cjs.map +0 -1
- package/dist/chunk-6DURKQ6W.js +0 -1072
- package/dist/chunk-6DURKQ6W.js.map +0 -1
- package/dist/chunk-6UNONDW7.js.map +0 -1
- package/dist/chunk-EQTMBMDR.js.map +0 -1
- package/dist/chunk-GAAHG2Z4.js.map +0 -1
- package/dist/chunk-HA4MGAZJ.js.map +0 -1
- package/dist/chunk-KGJRJ7CU.cjs.map +0 -1
- package/dist/chunk-OVZO4KB6.cjs.map +0 -1
- package/dist/chunk-PF642V5O.cjs +0 -1080
- package/dist/chunk-PF642V5O.cjs.map +0 -1
- package/dist/chunk-PJ7F4TCC.cjs.map +0 -1
- package/dist/chunk-PQ72Z6WC.js.map +0 -1
- package/dist/chunk-Q23GAMLE.cjs.map +0 -1
- package/dist/chunk-RFFSZSCL.cjs.map +0 -1
- package/dist/chunk-V7KZQIZ6.js.map +0 -1
- package/dist/chunk-Y7AQK4R4.cjs.map +0 -1
- package/dist/chunk-Z7OH3MWT.cjs.map +0 -1
- package/dist/chunk-ZFHTJ2YX.js.map +0 -1
package/dist/chunk-6DURKQ6W.js
DELETED
|
@@ -1,1072 +0,0 @@
|
|
|
1
|
-
import { checkCollectionAccess, checkGlobalAccess } from './chunk-2GXALYKV.js';
|
|
2
|
-
import { GraphQLError, GraphQLObjectType, GraphQLString, GraphQLNonNull, GraphQLInputObjectType, GraphQLBoolean, GraphQLInt, GraphQLList, GraphQLDirective, DirectiveLocation, printSchema, GraphQLSchema, GraphQLUnionType, GraphQLFloat, GraphQLEnumType } from 'graphql';
|
|
3
|
-
export { printSchema } from 'graphql';
|
|
4
|
-
|
|
5
|
-
function toPascalCase(slug) {
|
|
6
|
-
return slug.split(/[-_]/).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
7
|
-
}
|
|
8
|
-
function toCamelCase(slug) {
|
|
9
|
-
const p = toPascalCase(slug);
|
|
10
|
-
return p.charAt(0).toLowerCase() + p.slice(1);
|
|
11
|
-
}
|
|
12
|
-
function toPascalSingular(slug) {
|
|
13
|
-
const p = toPascalCase(slug);
|
|
14
|
-
if (p.endsWith("s") && p.length > 1) return p.slice(0, -1);
|
|
15
|
-
return p;
|
|
16
|
-
}
|
|
17
|
-
function toCamelSingular(slug) {
|
|
18
|
-
return toCamelCase(slug).endsWith("s") && slug.length > 1 ? toCamelCase(slug).slice(0, -1) : toCamelCase(slug);
|
|
19
|
-
}
|
|
20
|
-
var RelationLoader = class {
|
|
21
|
-
db;
|
|
22
|
-
tenantID;
|
|
23
|
-
user;
|
|
24
|
-
caches = /* @__PURE__ */ new Map();
|
|
25
|
-
pending = /* @__PURE__ */ new Map();
|
|
26
|
-
constructor(opts) {
|
|
27
|
-
this.db = opts.db;
|
|
28
|
-
this.tenantID = opts.tenantID;
|
|
29
|
-
this.user = opts.user;
|
|
30
|
-
}
|
|
31
|
-
getCacheKey(collection) {
|
|
32
|
-
return collection;
|
|
33
|
-
}
|
|
34
|
-
async load(collection, id) {
|
|
35
|
-
const key = this.getCacheKey(collection);
|
|
36
|
-
let cache = this.caches.get(key);
|
|
37
|
-
if (!cache) {
|
|
38
|
-
cache = /* @__PURE__ */ new Map();
|
|
39
|
-
this.caches.set(key, cache);
|
|
40
|
-
}
|
|
41
|
-
if (cache.has(id)) {
|
|
42
|
-
return cache.get(id);
|
|
43
|
-
}
|
|
44
|
-
let pendingSet = this.pending.get(key);
|
|
45
|
-
if (!pendingSet) {
|
|
46
|
-
pendingSet = /* @__PURE__ */ new Set();
|
|
47
|
-
this.pending.set(key, pendingSet);
|
|
48
|
-
}
|
|
49
|
-
pendingSet.add(id);
|
|
50
|
-
cache.set(id, void 0);
|
|
51
|
-
return void 0;
|
|
52
|
-
}
|
|
53
|
-
async flushAll() {
|
|
54
|
-
for (const [key, ids] of this.pending) {
|
|
55
|
-
if (ids.size === 0) continue;
|
|
56
|
-
const cache = this.caches.get(key);
|
|
57
|
-
const docs = await this.db.find({
|
|
58
|
-
collection: key,
|
|
59
|
-
where: { id: { in: Array.from(ids) } },
|
|
60
|
-
limit: ids.size,
|
|
61
|
-
tenantID: this.tenantID,
|
|
62
|
-
draft: !!this.user
|
|
63
|
-
});
|
|
64
|
-
for (const doc of docs.docs || []) {
|
|
65
|
-
if (doc && doc.id) {
|
|
66
|
-
cache.set(doc.id, doc);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
this.pending.clear();
|
|
71
|
-
}
|
|
72
|
-
resolveOne(collection, id) {
|
|
73
|
-
return this.caches.get(this.getCacheKey(collection))?.get(id);
|
|
74
|
-
}
|
|
75
|
-
static async withBatch(loader, fn) {
|
|
76
|
-
const result = await fn();
|
|
77
|
-
await loader.flushAll();
|
|
78
|
-
return result;
|
|
79
|
-
}
|
|
80
|
-
};
|
|
81
|
-
function gqlError(message, code) {
|
|
82
|
-
const err = new Error(message);
|
|
83
|
-
err.extensions = { code };
|
|
84
|
-
return err;
|
|
85
|
-
}
|
|
86
|
-
function depthLimit(maxDepth) {
|
|
87
|
-
return (validationContext) => {
|
|
88
|
-
let depth = 0;
|
|
89
|
-
return {
|
|
90
|
-
Field() {
|
|
91
|
-
depth++;
|
|
92
|
-
if (depth > maxDepth) {
|
|
93
|
-
validationContext.reportError(
|
|
94
|
-
new GraphQLError(`Query exceeds max depth of ${maxDepth}`)
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
leaveField() {
|
|
99
|
-
depth--;
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
async function checkAccess(config, operation, context) {
|
|
105
|
-
const result = await checkCollectionAccess(config, operation, context);
|
|
106
|
-
if (!result.allowed) {
|
|
107
|
-
throw gqlError(result.error || "Access denied", "FORBIDDEN");
|
|
108
|
-
}
|
|
109
|
-
return result.extraWhere;
|
|
110
|
-
}
|
|
111
|
-
async function checkGqlGlobalAccess(config, operation, context) {
|
|
112
|
-
const result = await checkGlobalAccess(config, operation, context);
|
|
113
|
-
if (!result.allowed) {
|
|
114
|
-
throw gqlError(result.error || "Access denied", "FORBIDDEN");
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
function fieldToGraphQLType(field, registry, collectionTypes, isInputType = false) {
|
|
118
|
-
switch (field.type) {
|
|
119
|
-
case "text":
|
|
120
|
-
case "email":
|
|
121
|
-
case "password":
|
|
122
|
-
case "textarea":
|
|
123
|
-
case "color":
|
|
124
|
-
case "code":
|
|
125
|
-
case "markdown":
|
|
126
|
-
case "date":
|
|
127
|
-
case "select":
|
|
128
|
-
case "radio":
|
|
129
|
-
case "upload":
|
|
130
|
-
return GraphQLString;
|
|
131
|
-
case "number":
|
|
132
|
-
return field.integer ? GraphQLInt : GraphQLFloat;
|
|
133
|
-
case "checkbox":
|
|
134
|
-
return GraphQLBoolean;
|
|
135
|
-
case "json":
|
|
136
|
-
case "richtext":
|
|
137
|
-
return GraphQLString;
|
|
138
|
-
case "relationship":
|
|
139
|
-
if (typeof field.relationTo === "string") {
|
|
140
|
-
if (isInputType) {
|
|
141
|
-
if (field.hasMany) {
|
|
142
|
-
return new GraphQLList(GraphQLString);
|
|
143
|
-
}
|
|
144
|
-
return GraphQLString;
|
|
145
|
-
}
|
|
146
|
-
if (collectionTypes?.[field.relationTo]) {
|
|
147
|
-
const refType = collectionTypes[field.relationTo];
|
|
148
|
-
if (field.hasMany) {
|
|
149
|
-
return new GraphQLList(refType);
|
|
150
|
-
}
|
|
151
|
-
return refType;
|
|
152
|
-
}
|
|
153
|
-
const relatedCollection = registry.getCollection(field.relationTo);
|
|
154
|
-
if (relatedCollection) {
|
|
155
|
-
const refType = new GraphQLObjectType({
|
|
156
|
-
name: toPascalSingular(field.relationTo),
|
|
157
|
-
fields: () => ({
|
|
158
|
-
id: { type: GraphQLString },
|
|
159
|
-
...buildFieldsFromCollection(relatedCollection, registry, collectionTypes)
|
|
160
|
-
})
|
|
161
|
-
});
|
|
162
|
-
if (collectionTypes) {
|
|
163
|
-
collectionTypes[field.relationTo] = refType;
|
|
164
|
-
}
|
|
165
|
-
if (field.hasMany) {
|
|
166
|
-
return new GraphQLList(refType);
|
|
167
|
-
}
|
|
168
|
-
return refType;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
return field.hasMany ? new GraphQLList(GraphQLString) : GraphQLString;
|
|
172
|
-
case "array": {
|
|
173
|
-
if (isInputType) {
|
|
174
|
-
const arrFields2 = field.fields;
|
|
175
|
-
if (arrFields2 && arrFields2.length > 0) {
|
|
176
|
-
const arrTypeName = `${field.name?.replace(/-/g, "_") || "array"}_input_item`;
|
|
177
|
-
return new GraphQLList(fieldsToGraphQLInputObjectType(arrTypeName, arrFields2, registry));
|
|
178
|
-
}
|
|
179
|
-
return new GraphQLList(GraphQLString);
|
|
180
|
-
}
|
|
181
|
-
const arrFields = field.fields;
|
|
182
|
-
if (arrFields && arrFields.length > 0) {
|
|
183
|
-
const arrTypeName = `${field.name?.replace(/-/g, "_") || "array"}_item`;
|
|
184
|
-
const itemType = fieldsToGraphQLObjectType(arrTypeName, arrFields, registry, collectionTypes);
|
|
185
|
-
return new GraphQLList(itemType);
|
|
186
|
-
}
|
|
187
|
-
return new GraphQLList(GraphQLString);
|
|
188
|
-
}
|
|
189
|
-
case "group": {
|
|
190
|
-
if (isInputType) {
|
|
191
|
-
const groupFields2 = field.fields;
|
|
192
|
-
if (groupFields2 && groupFields2.length > 0) {
|
|
193
|
-
const groupTypeName = `${field.name?.replace(/-/g, "_") || "group"}_input_group`;
|
|
194
|
-
return fieldsToGraphQLInputObjectType(groupTypeName, groupFields2, registry);
|
|
195
|
-
}
|
|
196
|
-
return GraphQLString;
|
|
197
|
-
}
|
|
198
|
-
const groupFields = field.fields;
|
|
199
|
-
if (groupFields && groupFields.length > 0) {
|
|
200
|
-
const groupTypeName = `${field.name?.replace(/-/g, "_") || "group"}_group`;
|
|
201
|
-
return fieldsToGraphQLObjectType(groupTypeName, groupFields, registry, collectionTypes);
|
|
202
|
-
}
|
|
203
|
-
return GraphQLString;
|
|
204
|
-
}
|
|
205
|
-
case "blocks": {
|
|
206
|
-
if (isInputType) return GraphQLString;
|
|
207
|
-
const blocks = field.blocks;
|
|
208
|
-
if (blocks && blocks.length > 0) {
|
|
209
|
-
const blockTypes = [];
|
|
210
|
-
for (const block of blocks) {
|
|
211
|
-
const blockTypeName = `Block${toPascalCase(block.slug)}`;
|
|
212
|
-
const bType = new GraphQLObjectType({
|
|
213
|
-
name: blockTypeName,
|
|
214
|
-
fields: () => {
|
|
215
|
-
const out = {
|
|
216
|
-
blockType: { type: new GraphQLNonNull(GraphQLString) },
|
|
217
|
-
id: { type: GraphQLString }
|
|
218
|
-
};
|
|
219
|
-
if (block.fields) {
|
|
220
|
-
for (const bf of block.fields) {
|
|
221
|
-
if (!bf.name) continue;
|
|
222
|
-
const bfConfig = buildFieldConfig(bf, registry, collectionTypes);
|
|
223
|
-
if (bfConfig) out[bf.name] = bfConfig;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
return out;
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
blockTypes.push(bType);
|
|
230
|
-
}
|
|
231
|
-
if (blockTypes.length > 0) {
|
|
232
|
-
const unionName = `${field.name?.replace(/-/g, "_") || "blocks"}_union`;
|
|
233
|
-
return new GraphQLUnionType({
|
|
234
|
-
name: unionName,
|
|
235
|
-
types: blockTypes,
|
|
236
|
-
resolveType(value) {
|
|
237
|
-
const blockName = `Block${value?.blockType ? toPascalCase(value.blockType) : ""}`;
|
|
238
|
-
const blockType = blockTypes.find(
|
|
239
|
-
(bt) => bt.name === blockName
|
|
240
|
-
);
|
|
241
|
-
return blockType?.name || void 0;
|
|
242
|
-
}
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
return new GraphQLList(GraphQLString);
|
|
247
|
-
}
|
|
248
|
-
case "row":
|
|
249
|
-
case "collapsible":
|
|
250
|
-
case "tabs":
|
|
251
|
-
return GraphQLString;
|
|
252
|
-
default:
|
|
253
|
-
return GraphQLString;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
function buildFieldsFromCollection(config, registry, collectionTypes) {
|
|
257
|
-
const fields = {};
|
|
258
|
-
for (const field of config.fields) {
|
|
259
|
-
if (field.name && field.admin?.hidden !== true) {
|
|
260
|
-
const fc = buildFieldConfig(field, registry, collectionTypes);
|
|
261
|
-
if (fc) fields[field.name] = fc;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
return fields;
|
|
265
|
-
}
|
|
266
|
-
function buildSortEnum(config) {
|
|
267
|
-
const sortFields = {
|
|
268
|
-
id: { value: "id" },
|
|
269
|
-
createdAt: { value: "createdAt" },
|
|
270
|
-
updatedAt: { value: "updatedAt" }
|
|
271
|
-
};
|
|
272
|
-
for (const field of config.fields) {
|
|
273
|
-
if (field.name) {
|
|
274
|
-
sortFields[field.name] = { value: field.name };
|
|
275
|
-
sortFields[`${field.name}_asc`] = { value: `${field.name}` };
|
|
276
|
-
sortFields[`${field.name}_desc`] = { value: `-${field.name}` };
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return new GraphQLEnumType({
|
|
280
|
-
name: `${toPascalSingular(config.slug)}Sort`,
|
|
281
|
-
values: sortFields
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
function buildFieldFilterType(field, namePrefix) {
|
|
285
|
-
const safeName = `${namePrefix}_${field.name?.replace(/-/g, "_") || "field"}_filter`;
|
|
286
|
-
const stringOps = {
|
|
287
|
-
equals: { type: GraphQLString },
|
|
288
|
-
not_equals: { type: GraphQLString },
|
|
289
|
-
contains: { type: GraphQLString },
|
|
290
|
-
not_contains: { type: GraphQLString },
|
|
291
|
-
in: { type: new GraphQLList(GraphQLString) },
|
|
292
|
-
not_in: { type: new GraphQLList(GraphQLString) },
|
|
293
|
-
exists: { type: GraphQLBoolean }
|
|
294
|
-
};
|
|
295
|
-
const numberOps = {
|
|
296
|
-
equals: { type: GraphQLFloat },
|
|
297
|
-
not_equals: { type: GraphQLFloat },
|
|
298
|
-
greater_than: { type: GraphQLFloat },
|
|
299
|
-
less_than: { type: GraphQLFloat },
|
|
300
|
-
greater_than_equal: { type: GraphQLFloat },
|
|
301
|
-
less_than_equal: { type: GraphQLFloat },
|
|
302
|
-
in: { type: new GraphQLList(GraphQLFloat) },
|
|
303
|
-
not_in: { type: new GraphQLList(GraphQLFloat) },
|
|
304
|
-
exists: { type: GraphQLBoolean }
|
|
305
|
-
};
|
|
306
|
-
const boolOps = {
|
|
307
|
-
equals: { type: GraphQLBoolean },
|
|
308
|
-
exists: { type: GraphQLBoolean }
|
|
309
|
-
};
|
|
310
|
-
const dateOps = {
|
|
311
|
-
equals: { type: GraphQLString },
|
|
312
|
-
not_equals: { type: GraphQLString },
|
|
313
|
-
greater_than: { type: GraphQLString },
|
|
314
|
-
less_than: { type: GraphQLString },
|
|
315
|
-
exists: { type: GraphQLBoolean }
|
|
316
|
-
};
|
|
317
|
-
const relationOps = {
|
|
318
|
-
equals: { type: GraphQLString },
|
|
319
|
-
not_equals: { type: GraphQLString },
|
|
320
|
-
in: { type: new GraphQLList(GraphQLString) },
|
|
321
|
-
not_in: { type: new GraphQLList(GraphQLString) },
|
|
322
|
-
exists: { type: GraphQLBoolean }
|
|
323
|
-
};
|
|
324
|
-
const jsonOps = {
|
|
325
|
-
exists: { type: GraphQLBoolean }
|
|
326
|
-
};
|
|
327
|
-
let ops;
|
|
328
|
-
switch (field.type) {
|
|
329
|
-
case "text":
|
|
330
|
-
case "email":
|
|
331
|
-
case "password":
|
|
332
|
-
case "textarea":
|
|
333
|
-
case "color":
|
|
334
|
-
case "code":
|
|
335
|
-
case "markdown":
|
|
336
|
-
case "select":
|
|
337
|
-
case "radio":
|
|
338
|
-
case "upload":
|
|
339
|
-
ops = stringOps;
|
|
340
|
-
break;
|
|
341
|
-
case "number":
|
|
342
|
-
ops = numberOps;
|
|
343
|
-
break;
|
|
344
|
-
case "checkbox":
|
|
345
|
-
ops = boolOps;
|
|
346
|
-
break;
|
|
347
|
-
case "date":
|
|
348
|
-
ops = dateOps;
|
|
349
|
-
break;
|
|
350
|
-
case "relationship":
|
|
351
|
-
ops = relationOps;
|
|
352
|
-
break;
|
|
353
|
-
case "json":
|
|
354
|
-
case "richtext":
|
|
355
|
-
ops = jsonOps;
|
|
356
|
-
break;
|
|
357
|
-
default:
|
|
358
|
-
ops = { exists: { type: GraphQLBoolean } };
|
|
359
|
-
}
|
|
360
|
-
return new GraphQLInputObjectType({
|
|
361
|
-
name: safeName,
|
|
362
|
-
fields: ops
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
function buildCollectionFilterType(config, namePrefix) {
|
|
366
|
-
const fields = {
|
|
367
|
-
AND: { type: new GraphQLList(GraphQLString) },
|
|
368
|
-
OR: { type: new GraphQLList(GraphQLString) }
|
|
369
|
-
};
|
|
370
|
-
for (const field of config.fields) {
|
|
371
|
-
if (!field.name) continue;
|
|
372
|
-
const filterType = buildFieldFilterType(field, namePrefix);
|
|
373
|
-
if (filterType) {
|
|
374
|
-
fields[field.name] = { type: filterType };
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
return new GraphQLInputObjectType({
|
|
378
|
-
name: `${namePrefix}Filter`,
|
|
379
|
-
fields
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
function translateFilter(filter) {
|
|
383
|
-
if (!filter || typeof filter !== "object") return filter || {};
|
|
384
|
-
const result = {};
|
|
385
|
-
for (const [key, value] of Object.entries(filter)) {
|
|
386
|
-
if (key === "AND" || key === "OR") continue;
|
|
387
|
-
result[key] = value;
|
|
388
|
-
}
|
|
389
|
-
return result;
|
|
390
|
-
}
|
|
391
|
-
function fieldsToGraphQLInputObjectType(name, fields, registry) {
|
|
392
|
-
return new GraphQLInputObjectType({
|
|
393
|
-
name,
|
|
394
|
-
fields: () => {
|
|
395
|
-
const out = {};
|
|
396
|
-
for (const field of fields) {
|
|
397
|
-
if (!field.name) continue;
|
|
398
|
-
if (field.type === "row" || field.type === "collapsible") {
|
|
399
|
-
const subFields = field.fields;
|
|
400
|
-
if (subFields) {
|
|
401
|
-
for (const sf of subFields) {
|
|
402
|
-
if (!sf.name) continue;
|
|
403
|
-
out[sf.name] = { type: fieldToGraphQLType(sf, registry, void 0, true) };
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
} else if (field.type === "tabs") {
|
|
407
|
-
const tabs = field.tabs;
|
|
408
|
-
if (tabs) {
|
|
409
|
-
for (const tab of tabs) {
|
|
410
|
-
if (tab.fields) {
|
|
411
|
-
for (const sf of tab.fields) {
|
|
412
|
-
if (!sf.name) continue;
|
|
413
|
-
out[sf.name] = { type: fieldToGraphQLType(sf, registry, void 0, true) };
|
|
414
|
-
}
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
} else {
|
|
419
|
-
out[field.name] = { type: fieldToGraphQLType(field, registry, void 0, true) };
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
return out;
|
|
423
|
-
}
|
|
424
|
-
});
|
|
425
|
-
}
|
|
426
|
-
function fieldsToGraphQLObjectType(name, fields, registry, collectionTypes) {
|
|
427
|
-
return new GraphQLObjectType({
|
|
428
|
-
name,
|
|
429
|
-
fields: () => {
|
|
430
|
-
const out = {};
|
|
431
|
-
for (const field of fields) {
|
|
432
|
-
if (!field.name) continue;
|
|
433
|
-
if (field.type === "row" || field.type === "collapsible") {
|
|
434
|
-
const subFields = field.fields;
|
|
435
|
-
if (subFields) {
|
|
436
|
-
for (const sf of subFields) {
|
|
437
|
-
if (!sf.name) continue;
|
|
438
|
-
const fc = buildFieldConfig(sf, registry, collectionTypes);
|
|
439
|
-
if (fc) out[sf.name] = fc;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
} else if (field.type === "tabs") {
|
|
443
|
-
const tabs = field.tabs;
|
|
444
|
-
if (tabs) {
|
|
445
|
-
for (const tab of tabs) {
|
|
446
|
-
if (tab.fields) {
|
|
447
|
-
for (const sf of tab.fields) {
|
|
448
|
-
if (!sf.name) continue;
|
|
449
|
-
const fc = buildFieldConfig(sf, registry, collectionTypes);
|
|
450
|
-
if (fc) out[sf.name] = fc;
|
|
451
|
-
}
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
} else {
|
|
456
|
-
const fc = buildFieldConfig(field, registry, collectionTypes);
|
|
457
|
-
if (fc) out[field.name] = fc;
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
return out;
|
|
461
|
-
}
|
|
462
|
-
});
|
|
463
|
-
}
|
|
464
|
-
function buildFieldConfig(field, registry, collectionTypes) {
|
|
465
|
-
if (!field.name) return null;
|
|
466
|
-
const deprecationReason = field.deprecated || void 0;
|
|
467
|
-
if (field.type === "relationship" && !field.hasMany) {
|
|
468
|
-
const gqlType = fieldToGraphQLType(field, registry, collectionTypes, false);
|
|
469
|
-
return {
|
|
470
|
-
type: field.required ? new GraphQLNonNull(gqlType) : gqlType,
|
|
471
|
-
description: field.admin?.description || field.label,
|
|
472
|
-
deprecationReason,
|
|
473
|
-
resolve: (source, args, ctx) => {
|
|
474
|
-
const loader = ctx?.relationLoader;
|
|
475
|
-
const relField = field;
|
|
476
|
-
const id = source?.[relField.name];
|
|
477
|
-
if (!id) return null;
|
|
478
|
-
if (loader && typeof id === "string") {
|
|
479
|
-
const cached = loader.resolveOne(relField.relationTo, id);
|
|
480
|
-
if (cached) return cached;
|
|
481
|
-
loader.load(relField.relationTo, id);
|
|
482
|
-
}
|
|
483
|
-
return { id };
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
if (field.type === "relationship" && field.hasMany) {
|
|
488
|
-
const gqlType = fieldToGraphQLType(field, registry, collectionTypes, false);
|
|
489
|
-
return {
|
|
490
|
-
type: gqlType,
|
|
491
|
-
description: field.admin?.description || field.label,
|
|
492
|
-
deprecationReason,
|
|
493
|
-
resolve: (source, args, ctx) => {
|
|
494
|
-
const loader = ctx?.relationLoader;
|
|
495
|
-
const relField = field;
|
|
496
|
-
const ids = source?.[relField.name] || [];
|
|
497
|
-
if (!ids.length) return [];
|
|
498
|
-
if (loader) {
|
|
499
|
-
const results = [];
|
|
500
|
-
for (const id of ids) {
|
|
501
|
-
const cached = loader.resolveOne(relField.relationTo, id);
|
|
502
|
-
if (cached) results.push(cached);
|
|
503
|
-
else {
|
|
504
|
-
loader.load(relField.relationTo, id);
|
|
505
|
-
results.push({ id });
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return results;
|
|
509
|
-
}
|
|
510
|
-
return ids.map((id) => ({ id }));
|
|
511
|
-
}
|
|
512
|
-
};
|
|
513
|
-
}
|
|
514
|
-
return {
|
|
515
|
-
type: field.required ? new GraphQLNonNull(fieldToGraphQLType(field, registry, collectionTypes, false)) : fieldToGraphQLType(field, registry, collectionTypes, false),
|
|
516
|
-
description: field.admin?.description || field.label,
|
|
517
|
-
deprecationReason
|
|
518
|
-
};
|
|
519
|
-
}
|
|
520
|
-
function buildGraphQLSchema(options) {
|
|
521
|
-
const { registry, db, settings } = options;
|
|
522
|
-
const apiAccess = settings?.access?.apiAccess;
|
|
523
|
-
if (apiAccess?.graphqlEnabled === false) {
|
|
524
|
-
throw new Error("GraphQL API is disabled");
|
|
525
|
-
}
|
|
526
|
-
const collections = registry.getCollections();
|
|
527
|
-
const globals = registry.getGlobals();
|
|
528
|
-
const collectionTypes = {};
|
|
529
|
-
const collectionInputTypes = {};
|
|
530
|
-
for (const collection of collections) {
|
|
531
|
-
collectionTypes[collection.slug] = new GraphQLObjectType({
|
|
532
|
-
name: toPascalSingular(collection.slug),
|
|
533
|
-
fields: () => ({
|
|
534
|
-
id: { type: new GraphQLNonNull(GraphQLString) },
|
|
535
|
-
...buildFieldsFromCollection(collection, registry, collectionTypes),
|
|
536
|
-
...collection.timestamps ? {
|
|
537
|
-
createdAt: { type: GraphQLString },
|
|
538
|
-
updatedAt: { type: GraphQLString }
|
|
539
|
-
} : {},
|
|
540
|
-
...collection.tenantScoped ? {
|
|
541
|
-
tenantID: { type: GraphQLString }
|
|
542
|
-
} : {}
|
|
543
|
-
})
|
|
544
|
-
});
|
|
545
|
-
const inputFields = {};
|
|
546
|
-
for (const field of collection.fields) {
|
|
547
|
-
if (field.name && field.name !== "id") {
|
|
548
|
-
inputFields[field.name] = {
|
|
549
|
-
type: fieldToGraphQLType(field, registry, collectionTypes, true)
|
|
550
|
-
};
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
collectionInputTypes[collection.slug] = new GraphQLInputObjectType({
|
|
554
|
-
name: `${toPascalSingular(collection.slug)}Input`,
|
|
555
|
-
fields: () => inputFields
|
|
556
|
-
});
|
|
557
|
-
}
|
|
558
|
-
const globalTypes = {};
|
|
559
|
-
const globalInputTypes = {};
|
|
560
|
-
for (const global of globals) {
|
|
561
|
-
globalTypes[global.slug] = new GraphQLObjectType({
|
|
562
|
-
name: toPascalCase(global.slug),
|
|
563
|
-
fields: () => ({
|
|
564
|
-
id: { type: GraphQLString },
|
|
565
|
-
...buildFieldsFromCollection(
|
|
566
|
-
{ slug: global.slug, fields: global.fields },
|
|
567
|
-
registry,
|
|
568
|
-
collectionTypes
|
|
569
|
-
)
|
|
570
|
-
})
|
|
571
|
-
});
|
|
572
|
-
globalInputTypes[global.slug] = new GraphQLInputObjectType({
|
|
573
|
-
name: `${toPascalCase(global.slug)}Input`,
|
|
574
|
-
fields: () => {
|
|
575
|
-
const inputFields = {};
|
|
576
|
-
for (const field of global.fields) {
|
|
577
|
-
if (field.name && field.name !== "id") {
|
|
578
|
-
inputFields[field.name] = {
|
|
579
|
-
type: fieldToGraphQLType(field, registry, collectionTypes, true)
|
|
580
|
-
};
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
return inputFields;
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
}
|
|
587
|
-
const pageInfoType = new GraphQLObjectType({
|
|
588
|
-
name: "PageInfo",
|
|
589
|
-
fields: {
|
|
590
|
-
hasNextPage: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
591
|
-
hasPreviousPage: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
592
|
-
startCursor: { type: GraphQLString },
|
|
593
|
-
endCursor: { type: GraphQLString }
|
|
594
|
-
}
|
|
595
|
-
});
|
|
596
|
-
const queryFields = {};
|
|
597
|
-
for (const collection of collections) {
|
|
598
|
-
const type = collectionTypes[collection.slug];
|
|
599
|
-
if (!type) continue;
|
|
600
|
-
const filterType = buildCollectionFilterType(collection, toPascalSingular(collection.slug));
|
|
601
|
-
const sortEnum = buildSortEnum(collection);
|
|
602
|
-
queryFields[toCamelCase(collection.slug)] = {
|
|
603
|
-
type: new GraphQLObjectType({
|
|
604
|
-
name: `${toPascalSingular(collection.slug)}FindResult`,
|
|
605
|
-
fields: {
|
|
606
|
-
docs: { type: new GraphQLList(type) },
|
|
607
|
-
totalDocs: { type: GraphQLInt },
|
|
608
|
-
page: { type: GraphQLInt },
|
|
609
|
-
totalPages: { type: GraphQLInt },
|
|
610
|
-
hasNextPage: { type: GraphQLBoolean },
|
|
611
|
-
hasPrevPage: { type: GraphQLBoolean }
|
|
612
|
-
}
|
|
613
|
-
}),
|
|
614
|
-
args: {
|
|
615
|
-
where: { type: filterType },
|
|
616
|
-
sort: { type: sortEnum || GraphQLString },
|
|
617
|
-
limit: { type: GraphQLInt },
|
|
618
|
-
page: { type: GraphQLInt },
|
|
619
|
-
draft: { type: GraphQLBoolean }
|
|
620
|
-
},
|
|
621
|
-
resolve: async (_, args, ctx) => {
|
|
622
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, apiKey: ctxApiKey, db: ctxDb, relationLoader: ctxLoader } = ctx;
|
|
623
|
-
const extraWhere = await checkAccess(collection, "read", {
|
|
624
|
-
user: ctxUser,
|
|
625
|
-
req: ctxReq,
|
|
626
|
-
tenantID: ctxTenant,
|
|
627
|
-
apiKey: ctxApiKey
|
|
628
|
-
});
|
|
629
|
-
if (ctxTenant) {
|
|
630
|
-
ctxDb.setTenantContext({ tenantId: ctxTenant, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
|
|
631
|
-
}
|
|
632
|
-
let where = {};
|
|
633
|
-
if (args.where) {
|
|
634
|
-
if (typeof args.where === "string") {
|
|
635
|
-
try {
|
|
636
|
-
where = JSON.parse(args.where);
|
|
637
|
-
} catch {
|
|
638
|
-
}
|
|
639
|
-
} else {
|
|
640
|
-
where = translateFilter(args.where);
|
|
641
|
-
}
|
|
642
|
-
}
|
|
643
|
-
if (extraWhere) {
|
|
644
|
-
where = { ...where, ...extraWhere };
|
|
645
|
-
}
|
|
646
|
-
const isDraft = args.draft ?? !!ctxUser;
|
|
647
|
-
return RelationLoader.withBatch(ctxLoader, async () => {
|
|
648
|
-
return ctxDb.find({
|
|
649
|
-
collection: collection.slug,
|
|
650
|
-
where,
|
|
651
|
-
sort: args.sort,
|
|
652
|
-
limit: args.limit || 10,
|
|
653
|
-
page: args.page || 1,
|
|
654
|
-
tenantID: ctxTenant,
|
|
655
|
-
draft: isDraft
|
|
656
|
-
});
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
};
|
|
660
|
-
queryFields[toCamelSingular(collection.slug)] = {
|
|
661
|
-
type,
|
|
662
|
-
args: {
|
|
663
|
-
id: { type: new GraphQLNonNull(GraphQLString) },
|
|
664
|
-
draft: { type: GraphQLBoolean }
|
|
665
|
-
},
|
|
666
|
-
resolve: async (_, args, ctx) => {
|
|
667
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, apiKey: ctxApiKey, db: ctxDb, relationLoader: ctxLoader } = ctx;
|
|
668
|
-
await checkAccess(collection, "read", {
|
|
669
|
-
user: ctxUser,
|
|
670
|
-
req: ctxReq,
|
|
671
|
-
tenantID: ctxTenant,
|
|
672
|
-
apiKey: ctxApiKey
|
|
673
|
-
});
|
|
674
|
-
if (ctxTenant) {
|
|
675
|
-
ctxDb.setTenantContext({ tenantId: ctxTenant, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
|
|
676
|
-
}
|
|
677
|
-
const isDraft = args.draft ?? !!ctxUser;
|
|
678
|
-
return RelationLoader.withBatch(ctxLoader, async () => {
|
|
679
|
-
const doc = await ctxDb.findByID({
|
|
680
|
-
collection: collection.slug,
|
|
681
|
-
id: args.id,
|
|
682
|
-
tenantID: ctxTenant,
|
|
683
|
-
draft: isDraft
|
|
684
|
-
});
|
|
685
|
-
return doc;
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
};
|
|
689
|
-
queryFields[`count${toPascalSingular(collection.slug)}s`] = {
|
|
690
|
-
type: new GraphQLObjectType({
|
|
691
|
-
name: `${toPascalSingular(collection.slug)}Count`,
|
|
692
|
-
fields: {
|
|
693
|
-
totalDocs: { type: GraphQLInt }
|
|
694
|
-
}
|
|
695
|
-
}),
|
|
696
|
-
args: {
|
|
697
|
-
where: { type: GraphQLString }
|
|
698
|
-
},
|
|
699
|
-
resolve: async (_, args, ctx) => {
|
|
700
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, apiKey: ctxApiKey, db: ctxDb } = ctx;
|
|
701
|
-
await checkAccess(collection, "read", {
|
|
702
|
-
user: ctxUser,
|
|
703
|
-
req: ctxReq,
|
|
704
|
-
tenantID: ctxTenant,
|
|
705
|
-
apiKey: ctxApiKey
|
|
706
|
-
});
|
|
707
|
-
let where = {};
|
|
708
|
-
if (args.where) {
|
|
709
|
-
if (typeof args.where === "string") {
|
|
710
|
-
try {
|
|
711
|
-
where = JSON.parse(args.where);
|
|
712
|
-
} catch {
|
|
713
|
-
}
|
|
714
|
-
} else {
|
|
715
|
-
where = translateFilter(args.where);
|
|
716
|
-
}
|
|
717
|
-
}
|
|
718
|
-
const count = await ctxDb.count({
|
|
719
|
-
collection: collection.slug,
|
|
720
|
-
where,
|
|
721
|
-
tenantID: ctxTenant
|
|
722
|
-
});
|
|
723
|
-
return { totalDocs: count };
|
|
724
|
-
}
|
|
725
|
-
};
|
|
726
|
-
const edgeType = new GraphQLObjectType({
|
|
727
|
-
name: `${toPascalSingular(collection.slug)}Edge`,
|
|
728
|
-
fields: {
|
|
729
|
-
node: { type },
|
|
730
|
-
cursor: { type: new GraphQLNonNull(GraphQLString) }
|
|
731
|
-
}
|
|
732
|
-
});
|
|
733
|
-
const connectionType = new GraphQLObjectType({
|
|
734
|
-
name: `${toPascalSingular(collection.slug)}Connection`,
|
|
735
|
-
fields: {
|
|
736
|
-
edges: { type: new GraphQLList(edgeType) },
|
|
737
|
-
pageInfo: { type: new GraphQLNonNull(pageInfoType) },
|
|
738
|
-
totalCount: { type: GraphQLInt }
|
|
739
|
-
}
|
|
740
|
-
});
|
|
741
|
-
queryFields[`${toCamelCase(collection.slug)}Connection`] = {
|
|
742
|
-
type: connectionType,
|
|
743
|
-
args: {
|
|
744
|
-
first: { type: GraphQLInt },
|
|
745
|
-
after: { type: GraphQLString },
|
|
746
|
-
last: { type: GraphQLInt },
|
|
747
|
-
before: { type: GraphQLString },
|
|
748
|
-
where: { type: filterType },
|
|
749
|
-
sort: { type: sortEnum || GraphQLString },
|
|
750
|
-
draft: { type: GraphQLBoolean }
|
|
751
|
-
},
|
|
752
|
-
resolve: async (_, args, ctx) => {
|
|
753
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, apiKey: ctxApiKey, db: ctxDb, relationLoader: ctxLoader } = ctx;
|
|
754
|
-
const extraWhere = await checkAccess(collection, "read", {
|
|
755
|
-
user: ctxUser,
|
|
756
|
-
req: ctxReq,
|
|
757
|
-
tenantID: ctxTenant,
|
|
758
|
-
apiKey: ctxApiKey
|
|
759
|
-
});
|
|
760
|
-
if (ctxTenant) {
|
|
761
|
-
ctxDb.setTenantContext({ tenantId: ctxTenant, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
|
|
762
|
-
}
|
|
763
|
-
let where = {};
|
|
764
|
-
if (args.where) {
|
|
765
|
-
if (typeof args.where === "string") {
|
|
766
|
-
try {
|
|
767
|
-
where = JSON.parse(args.where);
|
|
768
|
-
} catch {
|
|
769
|
-
}
|
|
770
|
-
} else {
|
|
771
|
-
where = translateFilter(args.where);
|
|
772
|
-
}
|
|
773
|
-
}
|
|
774
|
-
if (extraWhere) where = { ...where, ...extraWhere };
|
|
775
|
-
const isDraft = args.draft ?? !!ctxUser;
|
|
776
|
-
return RelationLoader.withBatch(ctxLoader, async () => {
|
|
777
|
-
let page = 1;
|
|
778
|
-
let limit = args.first || args.last || 10;
|
|
779
|
-
if (args.after) {
|
|
780
|
-
try {
|
|
781
|
-
const afterIdx = parseInt(Buffer.from(args.after, "base64").toString("utf-8"), 10);
|
|
782
|
-
if (!isNaN(afterIdx)) page = Math.floor(afterIdx / limit) + 1;
|
|
783
|
-
} catch {
|
|
784
|
-
}
|
|
785
|
-
} else if (args.before) {
|
|
786
|
-
try {
|
|
787
|
-
const beforeIdx = parseInt(Buffer.from(args.before, "base64").toString("utf-8"), 10);
|
|
788
|
-
if (!isNaN(beforeIdx)) page = Math.max(1, Math.floor(beforeIdx / limit));
|
|
789
|
-
} catch {
|
|
790
|
-
}
|
|
791
|
-
}
|
|
792
|
-
const result = await ctxDb.find({
|
|
793
|
-
collection: collection.slug,
|
|
794
|
-
where,
|
|
795
|
-
sort: args.sort,
|
|
796
|
-
limit,
|
|
797
|
-
page,
|
|
798
|
-
tenantID: ctxTenant,
|
|
799
|
-
draft: isDraft
|
|
800
|
-
});
|
|
801
|
-
const edges = (result.docs || []).map((doc, idx) => {
|
|
802
|
-
const cursorIdx = (page - 1) * limit + idx;
|
|
803
|
-
return {
|
|
804
|
-
node: doc,
|
|
805
|
-
cursor: Buffer.from(String(cursorIdx)).toString("base64")
|
|
806
|
-
};
|
|
807
|
-
});
|
|
808
|
-
return {
|
|
809
|
-
edges,
|
|
810
|
-
pageInfo: {
|
|
811
|
-
hasNextPage: result.hasNextPage || false,
|
|
812
|
-
hasPreviousPage: result.hasPrevPage || false,
|
|
813
|
-
startCursor: edges.length > 0 ? edges[0].cursor : null,
|
|
814
|
-
endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : null
|
|
815
|
-
},
|
|
816
|
-
totalCount: result.totalDocs
|
|
817
|
-
};
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
};
|
|
821
|
-
}
|
|
822
|
-
for (const global of globals) {
|
|
823
|
-
const type = globalTypes[global.slug];
|
|
824
|
-
if (!type) continue;
|
|
825
|
-
queryFields[toCamelCase(global.slug)] = {
|
|
826
|
-
type,
|
|
827
|
-
resolve: async (_, args, ctx) => {
|
|
828
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, db: ctxDb } = ctx;
|
|
829
|
-
await checkGqlGlobalAccess(global, "read", {
|
|
830
|
-
user: ctxUser,
|
|
831
|
-
req: ctxReq,
|
|
832
|
-
tenantID: ctxTenant
|
|
833
|
-
});
|
|
834
|
-
if (ctxTenant) {
|
|
835
|
-
ctxDb.setTenantContext({ tenantId: ctxTenant, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
|
|
836
|
-
}
|
|
837
|
-
return ctxDb.findOne({
|
|
838
|
-
collection: `_globals_${global.slug}`,
|
|
839
|
-
where: {},
|
|
840
|
-
tenantID: ctxTenant
|
|
841
|
-
});
|
|
842
|
-
}
|
|
843
|
-
};
|
|
844
|
-
}
|
|
845
|
-
const Query = new GraphQLObjectType({
|
|
846
|
-
name: "Query",
|
|
847
|
-
fields: queryFields
|
|
848
|
-
});
|
|
849
|
-
const mutationFields = {};
|
|
850
|
-
for (const collection of collections) {
|
|
851
|
-
const type = collectionTypes[collection.slug];
|
|
852
|
-
const inputType = collectionInputTypes[collection.slug];
|
|
853
|
-
if (!type || !inputType) continue;
|
|
854
|
-
mutationFields[`create${toPascalSingular(collection.slug)}`] = {
|
|
855
|
-
type: new GraphQLObjectType({
|
|
856
|
-
name: `${toPascalSingular(collection.slug)}CreateResult`,
|
|
857
|
-
fields: {
|
|
858
|
-
doc: { type },
|
|
859
|
-
message: { type: GraphQLString }
|
|
860
|
-
}
|
|
861
|
-
}),
|
|
862
|
-
args: {
|
|
863
|
-
data: { type: new GraphQLNonNull(inputType) },
|
|
864
|
-
draft: { type: GraphQLBoolean }
|
|
865
|
-
},
|
|
866
|
-
resolve: async (_, args, ctx) => {
|
|
867
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, apiKey: ctxApiKey, db: ctxDb, registry: ctxRegistry } = ctx;
|
|
868
|
-
await checkAccess(collection, "create", {
|
|
869
|
-
user: ctxUser,
|
|
870
|
-
req: ctxReq,
|
|
871
|
-
tenantID: ctxTenant,
|
|
872
|
-
apiKey: ctxApiKey
|
|
873
|
-
});
|
|
874
|
-
if (ctxTenant) {
|
|
875
|
-
ctxDb.setTenantContext({ tenantId: ctxTenant, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
|
|
876
|
-
}
|
|
877
|
-
const createSchema = ctxRegistry.getCreateZodSchema(collection.slug);
|
|
878
|
-
let validated;
|
|
879
|
-
try {
|
|
880
|
-
validated = createSchema.parse(args.data);
|
|
881
|
-
} catch (err) {
|
|
882
|
-
if (err?.issues) {
|
|
883
|
-
const msg = err.issues.map((i) => `${i.path?.join(".")}: ${i.message}`).join("; ");
|
|
884
|
-
throw gqlError(msg, "BAD_USER_INPUT");
|
|
885
|
-
}
|
|
886
|
-
throw err;
|
|
887
|
-
}
|
|
888
|
-
const doc = await ctxDb.create({
|
|
889
|
-
collection: collection.slug,
|
|
890
|
-
data: validated,
|
|
891
|
-
tenantID: ctxTenant
|
|
892
|
-
});
|
|
893
|
-
return { doc, message: "Created successfully" };
|
|
894
|
-
}
|
|
895
|
-
};
|
|
896
|
-
mutationFields[`update${toPascalSingular(collection.slug)}`] = {
|
|
897
|
-
type: new GraphQLObjectType({
|
|
898
|
-
name: `${toPascalSingular(collection.slug)}UpdateResult`,
|
|
899
|
-
fields: {
|
|
900
|
-
doc: { type },
|
|
901
|
-
message: { type: GraphQLString }
|
|
902
|
-
}
|
|
903
|
-
}),
|
|
904
|
-
args: {
|
|
905
|
-
id: { type: new GraphQLNonNull(GraphQLString) },
|
|
906
|
-
data: { type: new GraphQLNonNull(inputType) },
|
|
907
|
-
baseUpdatedAt: { type: GraphQLString },
|
|
908
|
-
draft: { type: GraphQLBoolean }
|
|
909
|
-
},
|
|
910
|
-
resolve: async (_, args, ctx) => {
|
|
911
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, apiKey: ctxApiKey, db: ctxDb, registry: ctxRegistry } = ctx;
|
|
912
|
-
await checkAccess(collection, "update", {
|
|
913
|
-
user: ctxUser,
|
|
914
|
-
req: ctxReq,
|
|
915
|
-
tenantID: ctxTenant,
|
|
916
|
-
apiKey: ctxApiKey
|
|
917
|
-
});
|
|
918
|
-
if (ctxTenant) {
|
|
919
|
-
ctxDb.setTenantContext({ tenantId: ctxTenant, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
|
|
920
|
-
}
|
|
921
|
-
if (args.baseUpdatedAt) {
|
|
922
|
-
const originalDoc = await ctxDb.findByID({
|
|
923
|
-
collection: collection.slug,
|
|
924
|
-
id: args.id,
|
|
925
|
-
tenantID: ctxTenant,
|
|
926
|
-
draft: true
|
|
927
|
-
});
|
|
928
|
-
if (originalDoc && originalDoc.updatedAt && args.baseUpdatedAt !== originalDoc.updatedAt) {
|
|
929
|
-
throw gqlError(
|
|
930
|
-
`Revision conflict: document has changed since ${args.baseUpdatedAt}. Current updatedAt: ${originalDoc.updatedAt}`,
|
|
931
|
-
"CONFLICT"
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
}
|
|
935
|
-
const updateSchema = ctxRegistry.getUpdateZodSchema(collection.slug);
|
|
936
|
-
let validated;
|
|
937
|
-
try {
|
|
938
|
-
validated = updateSchema.parse(args.data);
|
|
939
|
-
} catch (err) {
|
|
940
|
-
if (err?.issues) {
|
|
941
|
-
const msg = err.issues.map((i) => `${i.path?.join(".")}: ${i.message}`).join("; ");
|
|
942
|
-
throw gqlError(msg, "BAD_USER_INPUT");
|
|
943
|
-
}
|
|
944
|
-
throw err;
|
|
945
|
-
}
|
|
946
|
-
const doc = await ctxDb.update({
|
|
947
|
-
collection: collection.slug,
|
|
948
|
-
id: args.id,
|
|
949
|
-
data: validated,
|
|
950
|
-
tenantID: ctxTenant
|
|
951
|
-
});
|
|
952
|
-
return { doc, message: "Updated successfully" };
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
mutationFields[`delete${toPascalSingular(collection.slug)}`] = {
|
|
956
|
-
type: new GraphQLObjectType({
|
|
957
|
-
name: `${toPascalSingular(collection.slug)}DeleteResult`,
|
|
958
|
-
fields: {
|
|
959
|
-
doc: { type },
|
|
960
|
-
message: { type: GraphQLString }
|
|
961
|
-
}
|
|
962
|
-
}),
|
|
963
|
-
args: {
|
|
964
|
-
id: { type: new GraphQLNonNull(GraphQLString) }
|
|
965
|
-
},
|
|
966
|
-
resolve: async (_, args, ctx) => {
|
|
967
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, apiKey: ctxApiKey, db: ctxDb } = ctx;
|
|
968
|
-
await checkAccess(collection, "delete", {
|
|
969
|
-
user: ctxUser,
|
|
970
|
-
req: ctxReq,
|
|
971
|
-
tenantID: ctxTenant,
|
|
972
|
-
apiKey: ctxApiKey
|
|
973
|
-
});
|
|
974
|
-
if (ctxTenant) {
|
|
975
|
-
ctxDb.setTenantContext({ tenantId: ctxTenant, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
|
|
976
|
-
}
|
|
977
|
-
const doc = await ctxDb.delete({
|
|
978
|
-
collection: collection.slug,
|
|
979
|
-
id: args.id,
|
|
980
|
-
tenantID: ctxTenant
|
|
981
|
-
});
|
|
982
|
-
return { doc, message: "Deleted successfully" };
|
|
983
|
-
}
|
|
984
|
-
};
|
|
985
|
-
}
|
|
986
|
-
for (const global of globals) {
|
|
987
|
-
const inputType = globalInputTypes[global.slug];
|
|
988
|
-
if (!inputType) continue;
|
|
989
|
-
mutationFields[`update${toPascalCase(global.slug)}`] = {
|
|
990
|
-
type: new GraphQLObjectType({
|
|
991
|
-
name: `${toPascalCase(global.slug)}UpdateResult`,
|
|
992
|
-
fields: {
|
|
993
|
-
doc: { type: globalTypes[global.slug] || GraphQLString },
|
|
994
|
-
message: { type: GraphQLString }
|
|
995
|
-
}
|
|
996
|
-
}),
|
|
997
|
-
args: {
|
|
998
|
-
data: { type: new GraphQLNonNull(inputType) }
|
|
999
|
-
},
|
|
1000
|
-
resolve: async (_, args, ctx) => {
|
|
1001
|
-
const { user: ctxUser, req: ctxReq, tenantID: ctxTenant, db: ctxDb } = ctx;
|
|
1002
|
-
await checkGqlGlobalAccess(global, "update", {
|
|
1003
|
-
user: ctxUser,
|
|
1004
|
-
req: ctxReq,
|
|
1005
|
-
tenantID: ctxTenant
|
|
1006
|
-
});
|
|
1007
|
-
if (ctxTenant) {
|
|
1008
|
-
ctxDb.setTenantContext({ tenantId: ctxTenant, userId: ctxUser?.id ?? "", role: ctxUser?.role, isSuperAdmin: ctxUser?.role === "super_admin" });
|
|
1009
|
-
}
|
|
1010
|
-
const doc = await ctxDb.findOne({
|
|
1011
|
-
collection: `_globals_${global.slug}`,
|
|
1012
|
-
where: {},
|
|
1013
|
-
tenantID: ctxTenant
|
|
1014
|
-
});
|
|
1015
|
-
let result;
|
|
1016
|
-
if (doc) {
|
|
1017
|
-
result = await ctxDb.update({
|
|
1018
|
-
collection: `_globals_${global.slug}`,
|
|
1019
|
-
id: doc.id,
|
|
1020
|
-
data: args.data,
|
|
1021
|
-
tenantID: ctxTenant
|
|
1022
|
-
});
|
|
1023
|
-
} else {
|
|
1024
|
-
result = await ctxDb.create({
|
|
1025
|
-
collection: `_globals_${global.slug}`,
|
|
1026
|
-
data: args.data,
|
|
1027
|
-
tenantID: ctxTenant
|
|
1028
|
-
});
|
|
1029
|
-
}
|
|
1030
|
-
return { doc: result, message: "Updated successfully" };
|
|
1031
|
-
}
|
|
1032
|
-
};
|
|
1033
|
-
}
|
|
1034
|
-
const Mutation = new GraphQLObjectType({
|
|
1035
|
-
name: "Mutation",
|
|
1036
|
-
fields: mutationFields
|
|
1037
|
-
});
|
|
1038
|
-
const keyDirective = new GraphQLDirective({
|
|
1039
|
-
name: "key",
|
|
1040
|
-
locations: [DirectiveLocation.OBJECT, DirectiveLocation.INTERFACE],
|
|
1041
|
-
args: {
|
|
1042
|
-
fields: { type: new GraphQLNonNull(GraphQLString) }
|
|
1043
|
-
}
|
|
1044
|
-
});
|
|
1045
|
-
const serviceSdl = printSchema(new GraphQLSchema({
|
|
1046
|
-
query: new GraphQLObjectType({ name: "Query", fields: { ...queryFields } }),
|
|
1047
|
-
mutation: Mutation,
|
|
1048
|
-
directives: [keyDirective]
|
|
1049
|
-
}));
|
|
1050
|
-
queryFields._service = {
|
|
1051
|
-
type: new GraphQLObjectType({
|
|
1052
|
-
name: "_Service",
|
|
1053
|
-
fields: {
|
|
1054
|
-
sdl: { type: GraphQLString }
|
|
1055
|
-
}
|
|
1056
|
-
}),
|
|
1057
|
-
resolve: () => ({ sdl: serviceSdl })
|
|
1058
|
-
};
|
|
1059
|
-
return new GraphQLSchema({
|
|
1060
|
-
query: Query,
|
|
1061
|
-
mutation: Mutation,
|
|
1062
|
-
directives: [keyDirective]
|
|
1063
|
-
});
|
|
1064
|
-
}
|
|
1065
|
-
function createGraphQLSchema(registry, db) {
|
|
1066
|
-
const schema = buildGraphQLSchema({ registry, db });
|
|
1067
|
-
return Object.assign(schema, { sdl: printSchema(schema) });
|
|
1068
|
-
}
|
|
1069
|
-
|
|
1070
|
-
export { RelationLoader, buildGraphQLSchema, createGraphQLSchema, depthLimit };
|
|
1071
|
-
//# sourceMappingURL=chunk-6DURKQ6W.js.map
|
|
1072
|
-
//# sourceMappingURL=chunk-6DURKQ6W.js.map
|