@smartive/graphql-magic 1.0.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.
Files changed (124) hide show
  1. package/.eslintrc +21 -0
  2. package/.github/workflows/release.yml +24 -0
  3. package/.github/workflows/testing.yml +37 -0
  4. package/.nvmrc +1 -0
  5. package/.prettierignore +34 -0
  6. package/.prettierrc.json +1 -0
  7. package/.releaserc +27 -0
  8. package/CHANGELOG.md +6 -0
  9. package/README.md +15 -0
  10. package/dist/cjs/index.cjs +2646 -0
  11. package/dist/esm/client/gql.d.ts +1 -0
  12. package/dist/esm/client/gql.js +5 -0
  13. package/dist/esm/client/gql.js.map +1 -0
  14. package/dist/esm/client/index.d.ts +2 -0
  15. package/dist/esm/client/index.js +4 -0
  16. package/dist/esm/client/index.js.map +1 -0
  17. package/dist/esm/client/queries.d.ts +24 -0
  18. package/dist/esm/client/queries.js +152 -0
  19. package/dist/esm/client/queries.js.map +1 -0
  20. package/dist/esm/context.d.ts +30 -0
  21. package/dist/esm/context.js +2 -0
  22. package/dist/esm/context.js.map +1 -0
  23. package/dist/esm/errors.d.ts +17 -0
  24. package/dist/esm/errors.js +27 -0
  25. package/dist/esm/errors.js.map +1 -0
  26. package/dist/esm/generate/generate.d.ts +7 -0
  27. package/dist/esm/generate/generate.js +211 -0
  28. package/dist/esm/generate/generate.js.map +1 -0
  29. package/dist/esm/generate/index.d.ts +3 -0
  30. package/dist/esm/generate/index.js +5 -0
  31. package/dist/esm/generate/index.js.map +1 -0
  32. package/dist/esm/generate/mutations.d.ts +2 -0
  33. package/dist/esm/generate/mutations.js +18 -0
  34. package/dist/esm/generate/mutations.js.map +1 -0
  35. package/dist/esm/generate/utils.d.ts +22 -0
  36. package/dist/esm/generate/utils.js +150 -0
  37. package/dist/esm/generate/utils.js.map +1 -0
  38. package/dist/esm/index.d.ts +10 -0
  39. package/dist/esm/index.js +12 -0
  40. package/dist/esm/index.js.map +1 -0
  41. package/dist/esm/migrations/generate.d.ts +28 -0
  42. package/dist/esm/migrations/generate.js +516 -0
  43. package/dist/esm/migrations/generate.js.map +1 -0
  44. package/dist/esm/migrations/index.d.ts +1 -0
  45. package/dist/esm/migrations/index.js +3 -0
  46. package/dist/esm/migrations/index.js.map +1 -0
  47. package/dist/esm/models.d.ts +170 -0
  48. package/dist/esm/models.js +27 -0
  49. package/dist/esm/models.js.map +1 -0
  50. package/dist/esm/permissions/check.d.ts +15 -0
  51. package/dist/esm/permissions/check.js +162 -0
  52. package/dist/esm/permissions/check.js.map +1 -0
  53. package/dist/esm/permissions/generate.d.ts +45 -0
  54. package/dist/esm/permissions/generate.js +77 -0
  55. package/dist/esm/permissions/generate.js.map +1 -0
  56. package/dist/esm/permissions/index.d.ts +2 -0
  57. package/dist/esm/permissions/index.js +4 -0
  58. package/dist/esm/permissions/index.js.map +1 -0
  59. package/dist/esm/resolvers/arguments.d.ts +26 -0
  60. package/dist/esm/resolvers/arguments.js +88 -0
  61. package/dist/esm/resolvers/arguments.js.map +1 -0
  62. package/dist/esm/resolvers/filters.d.ts +5 -0
  63. package/dist/esm/resolvers/filters.js +126 -0
  64. package/dist/esm/resolvers/filters.js.map +1 -0
  65. package/dist/esm/resolvers/index.d.ts +7 -0
  66. package/dist/esm/resolvers/index.js +9 -0
  67. package/dist/esm/resolvers/index.js.map +1 -0
  68. package/dist/esm/resolvers/mutations.d.ts +3 -0
  69. package/dist/esm/resolvers/mutations.js +255 -0
  70. package/dist/esm/resolvers/mutations.js.map +1 -0
  71. package/dist/esm/resolvers/node.d.ts +44 -0
  72. package/dist/esm/resolvers/node.js +102 -0
  73. package/dist/esm/resolvers/node.js.map +1 -0
  74. package/dist/esm/resolvers/resolver.d.ts +5 -0
  75. package/dist/esm/resolvers/resolver.js +143 -0
  76. package/dist/esm/resolvers/resolver.js.map +1 -0
  77. package/dist/esm/resolvers/resolvers.d.ts +9 -0
  78. package/dist/esm/resolvers/resolvers.js +39 -0
  79. package/dist/esm/resolvers/resolvers.js.map +1 -0
  80. package/dist/esm/resolvers/utils.d.ts +43 -0
  81. package/dist/esm/resolvers/utils.js +125 -0
  82. package/dist/esm/resolvers/utils.js.map +1 -0
  83. package/dist/esm/utils.d.ts +25 -0
  84. package/dist/esm/utils.js +159 -0
  85. package/dist/esm/utils.js.map +1 -0
  86. package/dist/esm/values.d.ts +15 -0
  87. package/dist/esm/values.js +7 -0
  88. package/dist/esm/values.js.map +1 -0
  89. package/jest.config.ts +12 -0
  90. package/package.json +66 -0
  91. package/renovate.json +32 -0
  92. package/src/client/gql.ts +7 -0
  93. package/src/client/index.ts +4 -0
  94. package/src/client/queries.ts +251 -0
  95. package/src/context.ts +27 -0
  96. package/src/errors.ts +32 -0
  97. package/src/generate/generate.ts +273 -0
  98. package/src/generate/index.ts +5 -0
  99. package/src/generate/mutations.ts +35 -0
  100. package/src/generate/utils.ts +223 -0
  101. package/src/index.ts +12 -0
  102. package/src/migrations/generate.ts +633 -0
  103. package/src/migrations/index.ts +3 -0
  104. package/src/models.ts +228 -0
  105. package/src/permissions/check.ts +239 -0
  106. package/src/permissions/generate.ts +143 -0
  107. package/src/permissions/index.ts +4 -0
  108. package/src/resolvers/arguments.ts +129 -0
  109. package/src/resolvers/filters.ts +163 -0
  110. package/src/resolvers/index.ts +9 -0
  111. package/src/resolvers/mutations.ts +313 -0
  112. package/src/resolvers/node.ts +193 -0
  113. package/src/resolvers/resolver.ts +223 -0
  114. package/src/resolvers/resolvers.ts +40 -0
  115. package/src/resolvers/utils.ts +188 -0
  116. package/src/utils.ts +186 -0
  117. package/src/values.ts +19 -0
  118. package/tests/unit/__snapshots__/generate.spec.ts.snap +105 -0
  119. package/tests/unit/__snapshots__/resolve.spec.ts.snap +60 -0
  120. package/tests/unit/generate.spec.ts +8 -0
  121. package/tests/unit/resolve.spec.ts +128 -0
  122. package/tests/unit/utils.ts +82 -0
  123. package/tsconfig.jest.json +13 -0
  124. package/tsconfig.json +13 -0
@@ -0,0 +1,2646 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name2 in all)
9
+ __defProp(target, name2, { get: all[name2], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+
29
+ // src/index.ts
30
+ var src_exports = {};
31
+ __export(src_exports, {
32
+ AliasGenerator: () => AliasGenerator,
33
+ Enum: () => Enum,
34
+ ForbiddenError: () => ForbiddenError,
35
+ GraphQLError: () => GraphQLError,
36
+ ID_ALIAS: () => ID_ALIAS,
37
+ MigrationGenerator: () => MigrationGenerator,
38
+ NotFoundError: () => NotFoundError,
39
+ PermissionError: () => PermissionError,
40
+ SPECIAL_FILTERS: () => SPECIAL_FILTERS,
41
+ UserInputError: () => UserInputError,
42
+ actionableRelations: () => actionableRelations,
43
+ addJoin: () => addJoin,
44
+ and: () => and,
45
+ apply: () => apply,
46
+ applyFilters: () => applyFilters,
47
+ applyJoins: () => applyJoins,
48
+ applyPermissions: () => applyPermissions,
49
+ args: () => args,
50
+ checkCanWrite: () => checkCanWrite,
51
+ directive: () => directive,
52
+ directives: () => directives,
53
+ displayField: () => displayField,
54
+ document: () => document,
55
+ enm: () => enm,
56
+ fieldType: () => fieldType,
57
+ fields: () => fields,
58
+ generate: () => generate,
59
+ generateDefinitions: () => generateDefinitions,
60
+ generateMutations: () => generateMutations,
61
+ generatePermissions: () => generatePermissions,
62
+ get: () => get,
63
+ getEditEntityRelationsQuery: () => getEditEntityRelationsQuery,
64
+ getEntityListQuery: () => getEntityListQuery,
65
+ getEntityQuery: () => getEntityQuery,
66
+ getEntityToMutate: () => getEntityToMutate,
67
+ getFindEntityQuery: () => getFindEntityQuery,
68
+ getFragmentSpreads: () => getFragmentSpreads,
69
+ getFragmentTypeName: () => getFragmentTypeName,
70
+ getInlineFragments: () => getInlineFragments,
71
+ getJoins: () => getJoins,
72
+ getLabel: () => getLabel,
73
+ getManyToManyRelation: () => getManyToManyRelation,
74
+ getManyToManyRelations: () => getManyToManyRelations,
75
+ getManyToManyRelationsQuery: () => getManyToManyRelationsQuery,
76
+ getModelLabel: () => getModelLabel,
77
+ getModelLabelPlural: () => getModelLabelPlural,
78
+ getModelPlural: () => getModelPlural,
79
+ getModelPluralField: () => getModelPluralField,
80
+ getModelSlug: () => getModelSlug,
81
+ getModels: () => getModels,
82
+ getMutationQuery: () => getMutationQuery,
83
+ getNameOrAlias: () => getNameOrAlias,
84
+ getPermissionStack: () => getPermissionStack,
85
+ getResolverNode: () => getResolverNode,
86
+ getResolvers: () => getResolvers,
87
+ getRootFieldNode: () => getRootFieldNode,
88
+ getSimpleFields: () => getSimpleFields,
89
+ getString: () => getString,
90
+ getType: () => getType,
91
+ getTypeName: () => getTypeName,
92
+ getUpdateEntityQuery: () => getUpdateEntityQuery,
93
+ gql: () => gql,
94
+ hash: () => hash,
95
+ hydrate: () => hydrate,
96
+ iface: () => iface,
97
+ input: () => input,
98
+ inputValue: () => inputValue,
99
+ inputValues: () => inputValues,
100
+ isCreatable: () => isCreatable,
101
+ isCreatableBy: () => isCreatableBy,
102
+ isEnumList: () => isEnumList,
103
+ isEnumModel: () => isEnumModel,
104
+ isFieldNode: () => isFieldNode,
105
+ isFragmentSpreadNode: () => isFragmentSpreadNode,
106
+ isInlineFragmentNode: () => isInlineFragmentNode,
107
+ isJsonObjectModel: () => isJsonObjectModel,
108
+ isListType: () => isListType,
109
+ isObjectModel: () => isObjectModel,
110
+ isQueriableBy: () => isQueriableBy,
111
+ isQueriableField: () => isQueriableField,
112
+ isRaw: () => isRaw,
113
+ isRawEnumModel: () => isRawEnumModel,
114
+ isRawObjectModel: () => isRawObjectModel,
115
+ isRelation: () => isRelation,
116
+ isScalarModel: () => isScalarModel,
117
+ isSimpleField: () => isSimpleField,
118
+ isToOneRelation: () => isToOneRelation,
119
+ isUpdatable: () => isUpdatable,
120
+ isUpdatableBy: () => isUpdatableBy,
121
+ isVisible: () => isVisible,
122
+ isVisibleRelation: () => isVisibleRelation,
123
+ it: () => it,
124
+ list: () => list,
125
+ merge: () => merge,
126
+ mutationResolver: () => mutationResolver,
127
+ name: () => name,
128
+ namedType: () => namedType,
129
+ nonNull: () => nonNull,
130
+ normalizeArguments: () => normalizeArguments,
131
+ normalizeValue: () => normalizeValue,
132
+ normalizeValueByTypeDefinition: () => normalizeValueByTypeDefinition,
133
+ not: () => not,
134
+ object: () => object,
135
+ ors: () => ors,
136
+ printSchema: () => printSchema,
137
+ printSchemaFromDocument: () => printSchemaFromDocument,
138
+ printSchemaFromModels: () => printSchemaFromModels,
139
+ queryRelations: () => queryRelations,
140
+ queryResolver: () => queryResolver,
141
+ resolve: () => resolve,
142
+ retry: () => retry,
143
+ scalar: () => scalar,
144
+ summon: () => summon,
145
+ summonByKey: () => summonByKey,
146
+ summonByName: () => summonByName,
147
+ typeToField: () => typeToField,
148
+ value: () => value
149
+ });
150
+ module.exports = __toCommonJS(src_exports);
151
+
152
+ // src/client/gql.ts
153
+ var gql = (chunks, ...variables) => {
154
+ return chunks.reduce(
155
+ (accumulator, chunk, index) => `${accumulator}${chunk}${index in variables ? variables[index] : ""}`,
156
+ ""
157
+ );
158
+ };
159
+
160
+ // src/client/queries.ts
161
+ var import_upperFirst = __toESM(require("lodash/upperFirst"), 1);
162
+
163
+ // src/models.ts
164
+ var isObjectModel = (model) => model.type === "object";
165
+ var isEnumModel = (model) => model.type === "enum";
166
+ var isRawEnumModel = (model) => model.type === "raw-enum";
167
+ var isScalarModel = (model) => model.type === "scalar";
168
+ var isRawObjectModel = (model) => model.type === "raw-object";
169
+ var isJsonObjectModel = (model) => model.type === "json-object";
170
+ var isEnumList = (models, field) => field?.list === true && models.find(({ name: name2 }) => name2 === field.type)?.type === "enum";
171
+ var and = (...predicates) => (field) => predicates.every((predicate) => predicate(field));
172
+ var not = (predicate) => (field) => !predicate(field);
173
+ var isRelation = ({ relation }) => !!relation;
174
+ var isVisibleRelation = (visibleRelationsByRole, modelName, role) => {
175
+ const whitelist = visibleRelationsByRole[role]?.[modelName];
176
+ return ({ name: name2 }) => whitelist ? whitelist.includes(name2) : true;
177
+ };
178
+ var isToOneRelation = ({ toOne }) => !!toOne;
179
+ var isQueriableField = ({ queriable }) => queriable !== false;
180
+ var isRaw = ({ raw }) => !!raw;
181
+ var isVisible = ({ hidden }) => hidden !== true;
182
+ var isSimpleField = and(not(isRelation), not(isRaw));
183
+ var isUpdatable = ({ updatable }) => !!updatable;
184
+ var isCreatable = ({ creatable }) => !!creatable;
185
+ var isQueriableBy = (role) => (field) => isQueriableField(field) && (!field.queriableBy || field.queriableBy.includes(role));
186
+ var isUpdatableBy = (role) => (field) => isUpdatable(field) && (!field.updatableBy || field.updatableBy.includes(role));
187
+ var isCreatableBy = (role) => (field) => isCreatable(field) && (!field.creatableBy || field.creatableBy.includes(role));
188
+ var actionableRelations = (model, action) => model.fields.filter(
189
+ ({ relation, ...field }) => relation && field[`${action === "filter" ? action : action.slice(0, -1)}able`]
190
+ );
191
+
192
+ // src/utils.ts
193
+ var import_assert = __toESM(require("assert"), 1);
194
+ var import_inflection = require("inflection");
195
+ var import_camelCase = __toESM(require("lodash/camelCase"), 1);
196
+ var import_get = __toESM(require("lodash/get"), 1);
197
+ var import_kebabCase = __toESM(require("lodash/kebabCase"), 1);
198
+ var import_startCase = __toESM(require("lodash/startCase"), 1);
199
+ var isNotFalsy = (v) => typeof v !== "undefined" && v !== null && v !== false;
200
+ var merge = (objects) => (objects || []).filter(isNotFalsy).reduce((i, acc) => ({ ...acc, ...i }), {});
201
+ var typeToField = (type) => type.substr(0, 1).toLowerCase() + type.substr(1);
202
+ var getModelPlural = (model) => model.plural || (0, import_inflection.pluralize)(model.name);
203
+ var getModelPluralField = (model) => typeToField(getModelPlural(model));
204
+ var getModelSlug = (model) => (0, import_kebabCase.default)(getModelPlural(model));
205
+ var getModelLabelPlural = (model) => getLabel(getModelPlural(model));
206
+ var getModelLabel = (model) => getLabel(model.name);
207
+ var getLabel = (s) => (0, import_startCase.default)((0, import_camelCase.default)(s));
208
+ var getModels = (rawModels) => {
209
+ const models = rawModels.filter(isObjectModel).map((model) => {
210
+ const objectModel = {
211
+ ...model,
212
+ fieldsByName: {},
213
+ relations: [],
214
+ relationsByName: {},
215
+ reverseRelations: [],
216
+ reverseRelationsByName: {},
217
+ fields: [
218
+ { name: "id", type: "ID", nonNull: true, unique: true, primary: true, generated: true },
219
+ ...model.fields,
220
+ ...model.creatable ? [
221
+ { name: "createdAt", type: "DateTime", nonNull: !model.nonStrict, orderable: true, generated: true },
222
+ {
223
+ name: "createdBy",
224
+ type: "User",
225
+ relation: true,
226
+ nonNull: !model.nonStrict,
227
+ reverse: `created${getModelPlural(model)}`,
228
+ generated: true
229
+ }
230
+ ] : [],
231
+ ...model.updatable ? [
232
+ { name: "updatedAt", type: "DateTime", nonNull: !model.nonStrict, orderable: true, generated: true },
233
+ {
234
+ name: "updatedBy",
235
+ type: "User",
236
+ relation: true,
237
+ nonNull: !model.nonStrict,
238
+ reverse: `updated${getModelPlural(model)}`,
239
+ generated: true
240
+ }
241
+ ] : [],
242
+ ...model.deletable ? [
243
+ {
244
+ name: "deleted",
245
+ type: "Boolean",
246
+ nonNull: true,
247
+ default: false,
248
+ filterable: true,
249
+ defaultFilter: false,
250
+ generated: true
251
+ },
252
+ { name: "deletedAt", type: "DateTime", orderable: true, generated: true },
253
+ {
254
+ name: "deletedBy",
255
+ type: "User",
256
+ relation: true,
257
+ reverse: `deleted${getModelPlural(model)}`,
258
+ generated: true
259
+ }
260
+ ] : []
261
+ ].map(({ foreignKey, ...field }) => ({
262
+ ...field,
263
+ ...field.relation && {
264
+ foreignKey: foreignKey || `${field.name}Id`
265
+ }
266
+ }))
267
+ };
268
+ for (const field of objectModel.fields) {
269
+ objectModel.fieldsByName[field.name] = field;
270
+ }
271
+ return objectModel;
272
+ });
273
+ for (const model of models) {
274
+ for (const field of model.fields.filter(({ relation }) => relation)) {
275
+ const fieldModel = summonByName(models, field.type);
276
+ const reverseRelation = {
277
+ name: field.reverse || (field.toOne ? typeToField(model.name) : getModelPluralField(model)),
278
+ foreignKey: get(field, "foreignKey"),
279
+ type: model.name,
280
+ toOne: !!field.toOne,
281
+ fieldModel,
282
+ field,
283
+ model
284
+ };
285
+ const relation = {
286
+ field,
287
+ model: fieldModel,
288
+ reverseRelation
289
+ };
290
+ model.relations.push(relation);
291
+ model.relationsByName[relation.field.name] = relation;
292
+ fieldModel.reverseRelations.push(reverseRelation);
293
+ fieldModel.reverseRelationsByName[reverseRelation.name] = reverseRelation;
294
+ }
295
+ }
296
+ return models;
297
+ };
298
+ var summonByName = (array, value2) => summonByKey(array, "name", value2);
299
+ var summonByKey = (array, key, value2) => summon(array, (element) => (0, import_get.default)(element, key) === value2, `No element found with ${key} ${value2}`);
300
+ var summon = (array, cb, errorMessage) => {
301
+ if (array === void 0) {
302
+ throw new Error("Base array is not defined.");
303
+ }
304
+ const result = array.find(cb);
305
+ if (result === void 0) {
306
+ console.trace();
307
+ throw new Error(errorMessage || "Element not found.");
308
+ }
309
+ return result;
310
+ };
311
+ var it = (object2) => {
312
+ if (object2 === void 0 || object2 === null) {
313
+ console.trace();
314
+ throw new Error("Base object is not defined.");
315
+ }
316
+ return object2;
317
+ };
318
+ var get = (object2, key) => {
319
+ const value2 = it(object2)[key];
320
+ if (value2 === void 0 || value2 === null) {
321
+ console.trace();
322
+ throw new Error(`Object doesn't have ${String(key)}`);
323
+ }
324
+ return value2;
325
+ };
326
+ var getString = (v) => {
327
+ (0, import_assert.default)(typeof v === "string");
328
+ return v;
329
+ };
330
+ var retry = async (cb, condition) => {
331
+ try {
332
+ return await cb();
333
+ } catch (e) {
334
+ if (condition(e)) {
335
+ return await cb();
336
+ } else {
337
+ throw e;
338
+ }
339
+ }
340
+ };
341
+
342
+ // src/client/queries.ts
343
+ var getUpdateEntityQuery = (model, role, fields2, additionalFields = "") => `query Update${model.name}Fields ($id: ID!) {
344
+ data: ${typeToField(model.name)}(where: { id: $id }) {
345
+ id
346
+ ${model.fields.filter(({ name: name2 }) => !fields2 || fields2.includes(name2)).filter(not(isRelation)).filter(isUpdatableBy(role)).map(({ name: name2 }) => name2).join(" ")}
347
+ ${actionableRelations(model, "update").filter(({ name: name2 }) => !fields2 || fields2.includes(name2)).map(({ name: name2 }) => `${name2} { id }`)}
348
+ ${additionalFields}
349
+ }
350
+ }`;
351
+ var getEditEntityRelationsQuery = (models, model, action, fields2, ignoreFields, additionalFields = {}) => {
352
+ const relations = actionableRelations(model, action).filter(
353
+ ({ name: name2 }) => (!fields2 || fields2.includes(name2)) && (!ignoreFields || !ignoreFields.includes(name2))
354
+ );
355
+ return !!relations.length && `query ${(0, import_upperFirst.default)(action)}${model.name}Relations {
356
+ ${relations.map(({ name: name2, type }) => {
357
+ const model2 = summonByName(models, type);
358
+ return `${name2}: ${getModelPluralField(model2)} {
359
+ id
360
+ display: ${model2.displayField || ""}
361
+ ${additionalFields[name2] || ""}
362
+ }`;
363
+ }).join(" ")}
364
+ }`;
365
+ };
366
+ var getManyToManyRelations = (model, fields2, ignoreFields) => {
367
+ const manyToManyRelations = [];
368
+ for (const field of model.reverseRelations) {
369
+ if (fields2 && !fields2.includes(field.name) || ignoreFields && ignoreFields.includes(field.name)) {
370
+ continue;
371
+ }
372
+ const relation = field.model.relations.find(
373
+ (relation2) => !relation2.field.generated && relation2.field.name !== field.field.name
374
+ );
375
+ if (!relation) {
376
+ continue;
377
+ }
378
+ const inapplicableFields = field.model.fields.filter(
379
+ (otherField) => !otherField.generated && ![field.field.name, relation.field.name].includes(otherField.name)
380
+ );
381
+ if (inapplicableFields.length) {
382
+ continue;
383
+ }
384
+ manyToManyRelations.push([field, relation]);
385
+ }
386
+ return manyToManyRelations;
387
+ };
388
+ var getManyToManyRelation = (model, name2) => getManyToManyRelations(model, [name2])[0];
389
+ var getManyToManyRelationsQuery = (model, action, manyToManyRelations) => !!manyToManyRelations.length && (action === "update" ? `query Update${model.name}ManyToManyRelations($id: ID!) {
390
+ ${typeToField(model.name)}(where: { id: $id }) {
391
+ ${manyToManyRelations.map(([reverseRelation, { field }]) => {
392
+ return `${reverseRelation.name} {
393
+ id
394
+ ${field.name} {
395
+ id
396
+ }
397
+ }`;
398
+ }).join(" ")}
399
+ }
400
+ ${manyToManyRelations.map(([reverseRelation, { model: model2 }]) => {
401
+ return `${reverseRelation.name}: ${getModelPluralField(model2)} {
402
+ id
403
+ ${model2.displayField || ""}
404
+ }`;
405
+ }).join(" ")}
406
+ }` : `query Create${model.name}ManyToManyRelations {
407
+ ${manyToManyRelations.map(([reverseRelation, { model: model2 }]) => {
408
+ return `${reverseRelation.name}: ${getModelPluralField(model2)} {
409
+ id
410
+ ${model2.displayField || ""}
411
+ }`;
412
+ }).join(" ")}
413
+ }`);
414
+ var getMutationQuery = (model, action) => action === "create" ? `
415
+ mutation Create${model.name} ($data: Create${model.name}!) {
416
+ mutated: create${model.name}(data: $data) {
417
+ id
418
+ }
419
+ }
420
+ ` : action === "update" ? `
421
+ mutation Update${model.name} ($id: ID!, $data: Update${model.name}!) {
422
+ mutated: update${model.name}(where: { id: $id } data: $data) {
423
+ id
424
+ }
425
+ }
426
+ ` : `
427
+ mutation Delete${model.name} ($id: ID!) {
428
+ mutated: delete${model.name}(where: { id: $id }) {
429
+ id
430
+ }
431
+ }
432
+ `;
433
+ var displayField = (model) => `
434
+ ${model.displayField ? `display: ${model.displayField}` : ""}
435
+ `;
436
+ var getEntityListQuery = (model, role, additionalFields = "", root) => `query ${getModelPlural(model)}List(
437
+ ${root ? "$id: ID!," : ""}
438
+ $limit: Int!,
439
+ $where: ${model.name}Where!,
440
+ ${model.fields.some(({ searchable }) => searchable) ? "$search: String," : ""}
441
+ ) {
442
+ ${root ? `root: ${typeToField(root.model.name)}(where: { id: $id }) {` : ""}
443
+ data: ${root ? root.reverseRelationName : getModelPluralField(model)}(limit: $limit, where: $where, ${model.fields.some(({ searchable }) => searchable) ? ", search: $search" : ""}) {
444
+ ${displayField(model)}
445
+ ${model.fields.filter(and(isSimpleField, isQueriableBy(role))).map(({ name: name2 }) => name2)}
446
+ ${additionalFields}
447
+ }
448
+ ${root ? "}" : ""}
449
+ }`;
450
+ var getEntityQuery = (models, model, role, visibleRelationsByRole, typesWithSubRelations) => `query Admin${model.name} ($id: ID!) {
451
+ data: ${typeToField(model.name)}(where: { id: $id }) {
452
+ ${displayField(model)}
453
+ ${model.fields.filter(and(isSimpleField, isQueriableBy(role))).map(({ name: name2 }) => name2)}
454
+ ${queryRelations(
455
+ models,
456
+ model.fields.filter(and(isRelation, isVisibleRelation(visibleRelationsByRole, model.name, role))),
457
+ role,
458
+ typesWithSubRelations
459
+ )}
460
+ ${queryRelations(
461
+ models,
462
+ model.reverseRelations.filter(and(isToOneRelation, isVisibleRelation(visibleRelationsByRole, model.name, role))),
463
+ role,
464
+ typesWithSubRelations
465
+ )}
466
+ }
467
+ }`;
468
+ var getFindEntityQuery = (model, role) => `query Find${model.name}($where: ${model.name}Where!, $orderBy: [${model.name}OrderBy!]) {
469
+ data: ${getModelPluralField(model)}(limit: 1, where: $where, orderBy: $orderBy) {
470
+ ${model.fields.filter(and(isSimpleField, isQueriableBy(role))).map(({ name: name2 }) => name2)}
471
+ }
472
+ }`;
473
+ var queryRelations = (models, relations, role, typesWithSubRelations) => relations.map(({ name: name2, type }) => {
474
+ const relatedModel = summonByName(models, type);
475
+ const subRelations = typesWithSubRelations.includes(type) ? relatedModel.fields.filter(isRelation) : [];
476
+ return `${name2} {
477
+ id
478
+ ${displayField(relatedModel)}
479
+ ${subRelations.length > 0 ? queryRelations(models, subRelations, role, typesWithSubRelations) : ""}
480
+ }`;
481
+ }).join("\n");
482
+
483
+ // src/generate/generate.ts
484
+ var import_graphql = require("graphql");
485
+ var import_flatMap = __toESM(require("lodash/flatMap"), 1);
486
+
487
+ // src/generate/utils.ts
488
+ var import_luxon = require("luxon");
489
+
490
+ // src/values.ts
491
+ var Enum = class {
492
+ constructor(value2) {
493
+ this.value = value2;
494
+ }
495
+ };
496
+
497
+ // src/generate/utils.ts
498
+ var document = (definitions) => ({
499
+ kind: "Document",
500
+ definitions
501
+ });
502
+ var directive = (nme, locations, fields2) => ({
503
+ name: name(nme),
504
+ repeatable: false,
505
+ kind: "DirectiveDefinition",
506
+ arguments: fields2 && inputValues(fields2),
507
+ locations: locations.map(name)
508
+ });
509
+ var scalar = (nme) => ({
510
+ name: name(nme),
511
+ kind: "ScalarTypeDefinition"
512
+ });
513
+ var input = (nme, fields2, dvs) => ({
514
+ name: name(nme),
515
+ fields: inputValues(fields2),
516
+ kind: "InputObjectTypeDefinition",
517
+ directives: directives(dvs)
518
+ });
519
+ var object = (nme, fds, interfaces, dvs) => ({
520
+ name: name(nme),
521
+ fields: fields(fds),
522
+ kind: "ObjectTypeDefinition",
523
+ interfaces: interfaces && interfaces.map((i) => namedType(i)),
524
+ directives: directives(dvs)
525
+ });
526
+ var iface = (nme, fds, dvs) => ({
527
+ name: name(nme),
528
+ fields: fields(fds),
529
+ kind: "InterfaceTypeDefinition",
530
+ directives: directives(dvs)
531
+ });
532
+ var inputValues = (fields2) => fields2.map(
533
+ (field) => ({
534
+ kind: "InputValueDefinition",
535
+ name: name(field.name),
536
+ type: fieldType(field),
537
+ defaultValue: field.default === void 0 ? void 0 : value(field.default),
538
+ directives: directives(field.directives)
539
+ })
540
+ );
541
+ var fields = (fields2) => fields2.map(
542
+ (field) => ({
543
+ kind: "FieldDefinition",
544
+ name: name(field.name),
545
+ type: fieldType(field),
546
+ arguments: field.args?.map((arg) => ({
547
+ kind: "InputValueDefinition",
548
+ name: name(arg.name),
549
+ type: fieldType(arg),
550
+ defaultValue: arg.default === void 0 ? void 0 : value(arg.default)
551
+ })),
552
+ directives: directives(field.directives)
553
+ })
554
+ );
555
+ var inputValue = (nme, type, dvs, description, defaultValue) => ({
556
+ name: name(nme),
557
+ type,
558
+ kind: "InputValueDefinition",
559
+ directives: directives(dvs),
560
+ defaultValue: defaultValue ? value(defaultValue) : void 0,
561
+ description: description ? value(description) : void 0
562
+ });
563
+ var directives = (directives2) => directives2?.map(
564
+ (directive2) => ({
565
+ kind: "Directive",
566
+ name: name(directive2.name),
567
+ arguments: args(directive2.values)
568
+ })
569
+ );
570
+ var args = (ags) => ags?.map(
571
+ (argument) => ({
572
+ kind: "Argument",
573
+ name: name(argument.name),
574
+ value: value(argument.values)
575
+ })
576
+ );
577
+ var enm = (nme, values) => ({
578
+ name: name(nme),
579
+ kind: "EnumTypeDefinition",
580
+ values: values.map(
581
+ (v) => ({
582
+ kind: "EnumValueDefinition",
583
+ name: name(v)
584
+ })
585
+ )
586
+ });
587
+ var nonNull = (type) => ({
588
+ type,
589
+ kind: "NonNullType"
590
+ });
591
+ var namedType = (nme) => ({
592
+ kind: "NamedType",
593
+ name: name(nme)
594
+ });
595
+ var list = (type) => ({
596
+ type,
597
+ kind: "ListType"
598
+ });
599
+ var name = (name2) => ({
600
+ kind: "Name",
601
+ value: name2
602
+ });
603
+ var fieldType = (field) => {
604
+ let type = namedType(field.type);
605
+ if (field.list) {
606
+ type = list(nonNull(type));
607
+ }
608
+ if (field.nonNull) {
609
+ type = nonNull(type);
610
+ }
611
+ return type;
612
+ };
613
+ var value = (val = null) => val === null ? {
614
+ kind: "NullValue"
615
+ } : typeof val === "boolean" ? {
616
+ kind: "BooleanValue",
617
+ value: val
618
+ } : typeof val === "number" ? {
619
+ kind: "IntValue",
620
+ value: `${val}`
621
+ } : typeof val === "string" ? {
622
+ kind: "StringValue",
623
+ value: val
624
+ } : val instanceof Enum ? {
625
+ kind: "EnumValue",
626
+ value: val.value
627
+ } : Array.isArray(val) ? {
628
+ kind: "ListValue",
629
+ values: val.map(value)
630
+ } : val instanceof import_luxon.DateTime ? {
631
+ kind: "StringValue",
632
+ value: val.toString()
633
+ } : {
634
+ kind: "ObjectValue",
635
+ fields: Object.keys(val).map(
636
+ (nme) => ({
637
+ kind: "ObjectField",
638
+ name: name(nme),
639
+ value: value(val[nme])
640
+ })
641
+ )
642
+ };
643
+
644
+ // src/generate/generate.ts
645
+ var generateDefinitions = (rawModels) => {
646
+ const models = getModels(rawModels);
647
+ return [
648
+ // Predefined types
649
+ enm("Order", ["ASC", "DESC"]),
650
+ scalar("DateTime"),
651
+ scalar("Upload"),
652
+ ...rawModels.filter(isEnumModel).map((model) => enm(model.name, model.values)),
653
+ ...rawModels.filter(isRawEnumModel).map((model) => enm(model.name, model.values)),
654
+ ...rawModels.filter(isScalarModel).map((model) => scalar(model.name)),
655
+ ...rawModels.filter(isRawObjectModel).map((model) => object(model.name, model.fields)),
656
+ ...rawModels.filter(isJsonObjectModel).map((model) => object(model.name, model.fields)),
657
+ ...rawModels.filter(isRawObjectModel).filter(({ rawFilters }) => rawFilters).map(
658
+ (model) => input(
659
+ `${model.name}Where`,
660
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- array gets filtered above to only include models with rawFilters
661
+ model.rawFilters.map(({ name: name2, type, list: list2 = false, nonNull: nonNull2 = false }) => ({ name: name2, type, list: list2, nonNull: nonNull2 }))
662
+ )
663
+ ),
664
+ ...(0, import_flatMap.default)(
665
+ models.map((model) => {
666
+ const types = [
667
+ object(
668
+ model.name,
669
+ [
670
+ ...model.fields.filter(isQueriableField).map((field) => ({
671
+ ...field,
672
+ args: [
673
+ ...field.args || [],
674
+ ...hasRawFilters(rawModels, field.type) ? [{ name: "where", type: `${field.type}Where` }] : []
675
+ ],
676
+ directives: field.directives
677
+ })),
678
+ ...model.reverseRelations.map(({ name: name2, field, model: model2 }) => ({
679
+ name: name2,
680
+ type: model2.name,
681
+ list: !field.toOne,
682
+ nonNull: !field.toOne,
683
+ args: [
684
+ { name: "where", type: `${model2.name}Where` },
685
+ ...model2.fields.some(({ searchable }) => searchable) ? [{ name: "search", type: "String" }] : [],
686
+ ...model2.fields.some(({ orderable }) => orderable) ? [{ name: "orderBy", type: `${model2.name}OrderBy`, list: true }] : [],
687
+ { name: "limit", type: "Int" },
688
+ { name: "offset", type: "Int" }
689
+ ]
690
+ }))
691
+ ],
692
+ model.interfaces
693
+ ),
694
+ input(`${model.name}Where`, [
695
+ ...model.fields.filter(({ unique, filterable, relation }) => (unique || filterable) && !relation).map(({ name: name2, type, defaultFilter }) => ({ name: name2, type, list: true, default: defaultFilter })),
696
+ ...(0, import_flatMap.default)(
697
+ model.fields.filter(({ comparable }) => comparable),
698
+ ({ name: name2, type }) => [
699
+ { name: `${name2}_GT`, type },
700
+ { name: `${name2}_GTE`, type },
701
+ { name: `${name2}_LT`, type },
702
+ { name: `${name2}_LTE`, type }
703
+ ]
704
+ ),
705
+ ...model.fields.filter(({ filterable, relation }) => filterable && relation).map(({ name: name2, type }) => ({
706
+ name: name2,
707
+ type: `${type}Where`
708
+ }))
709
+ ]),
710
+ input(
711
+ `${model.name}WhereUnique`,
712
+ model.fields.filter(({ unique }) => unique).map(({ name: name2, type }) => ({ name: name2, type }))
713
+ ),
714
+ ...model.fields.some(({ orderable }) => orderable) ? [
715
+ input(
716
+ `${model.name}OrderBy`,
717
+ model.fields.filter(({ orderable }) => orderable).map(({ name: name2 }) => ({ name: name2, type: "Order" }))
718
+ )
719
+ ] : []
720
+ ];
721
+ if (model.creatable) {
722
+ types.push(
723
+ input(
724
+ `Create${model.name}`,
725
+ model.fields.filter(({ creatable }) => creatable).map(
726
+ ({ name: name2, relation, type, nonNull: nonNull2, list: list2, default: defaultValue }) => relation ? { name: `${name2}Id`, type: "ID", nonNull: nonNull2 } : { name: name2, type, list: list2, nonNull: nonNull2 && defaultValue === void 0 }
727
+ )
728
+ )
729
+ );
730
+ }
731
+ if (model.updatable) {
732
+ types.push(
733
+ input(
734
+ `Update${model.name}`,
735
+ model.fields.filter(({ updatable }) => updatable).map(
736
+ ({ name: name2, relation, type, list: list2 }) => relation ? { name: `${name2}Id`, type: "ID" } : { name: name2, type, list: list2 }
737
+ )
738
+ )
739
+ );
740
+ }
741
+ return types;
742
+ })
743
+ ),
744
+ object("Query", [
745
+ {
746
+ name: "me",
747
+ type: "User"
748
+ },
749
+ ...models.filter(({ queriable }) => queriable).map(({ name: name2 }) => ({
750
+ name: typeToField(name2),
751
+ type: name2,
752
+ nonNull: true,
753
+ args: [
754
+ {
755
+ name: "where",
756
+ type: `${name2}WhereUnique`,
757
+ nonNull: true
758
+ }
759
+ ]
760
+ })),
761
+ ...models.filter(({ listQueriable }) => listQueriable).map((model) => ({
762
+ name: getModelPluralField(model),
763
+ type: model.name,
764
+ list: true,
765
+ nonNull: true,
766
+ args: [
767
+ { name: "where", type: `${model.name}Where` },
768
+ ...model.fields.some(({ searchable }) => searchable) ? [{ name: "search", type: "String" }] : [],
769
+ ...model.fields.some(({ orderable }) => orderable) ? [{ name: "orderBy", type: `${model.name}OrderBy`, list: true }] : [],
770
+ { name: "limit", type: "Int" },
771
+ { name: "offset", type: "Int" }
772
+ ]
773
+ }))
774
+ ]),
775
+ object("Mutation", [
776
+ ...(0, import_flatMap.default)(
777
+ models.map((model) => {
778
+ const mutations = [];
779
+ if (model.creatable) {
780
+ mutations.push({
781
+ name: `create${model.name}`,
782
+ type: model.name,
783
+ nonNull: true,
784
+ args: [
785
+ {
786
+ name: "data",
787
+ type: `Create${model.name}`,
788
+ nonNull: true
789
+ }
790
+ ]
791
+ });
792
+ }
793
+ if (model.updatable) {
794
+ mutations.push({
795
+ name: `update${model.name}`,
796
+ type: model.name,
797
+ nonNull: true,
798
+ args: [
799
+ {
800
+ name: "where",
801
+ type: `${model.name}WhereUnique`,
802
+ nonNull: true
803
+ },
804
+ {
805
+ name: "data",
806
+ type: `Update${model.name}`,
807
+ nonNull: true
808
+ }
809
+ ]
810
+ });
811
+ }
812
+ if (model.deletable) {
813
+ mutations.push({
814
+ name: `delete${model.name}`,
815
+ type: "ID",
816
+ nonNull: true,
817
+ args: [
818
+ {
819
+ name: "where",
820
+ type: `${model.name}WhereUnique`,
821
+ nonNull: true
822
+ },
823
+ {
824
+ name: "dryRun",
825
+ type: "Boolean"
826
+ }
827
+ ]
828
+ });
829
+ mutations.push({
830
+ name: `restore${model.name}`,
831
+ type: "ID",
832
+ nonNull: true,
833
+ args: [
834
+ {
835
+ name: "where",
836
+ type: `${model.name}WhereUnique`,
837
+ nonNull: true
838
+ }
839
+ ]
840
+ });
841
+ }
842
+ return mutations;
843
+ })
844
+ )
845
+ ])
846
+ ];
847
+ };
848
+ var generate = (rawModels) => document(generateDefinitions(rawModels));
849
+ var printSchema = (schema) => [
850
+ ...schema.getDirectives().map((d) => d.astNode && (0, import_graphql.print)(d.astNode)),
851
+ ...Object.values(schema.getTypeMap()).filter((t) => !t.name.match(/^__/)).sort((a, b) => a.name > b.name ? 1 : -1).map((t) => t.astNode && (0, import_graphql.print)(t.astNode))
852
+ ].filter(Boolean).map((s) => `${s}
853
+ `).join("\n");
854
+ var hasRawFilters = (models, type) => models.filter(isRawObjectModel).some(({ name: name2, rawFilters }) => name2 === type && !!rawFilters);
855
+ var printSchemaFromDocument = (document2) => printSchema((0, import_graphql.buildASTSchema)(document2));
856
+ var printSchemaFromModels = (models) => printSchema((0, import_graphql.buildASTSchema)(generate(models)));
857
+
858
+ // src/generate/mutations.ts
859
+ var import_upperCase = __toESM(require("lodash/upperCase"), 1);
860
+ var constantCase = (str) => (0, import_upperCase.default)(str).replace(/ /g, "_");
861
+ var generateMutations = (models) => {
862
+ const parts = [];
863
+ for (const { name: name2, creatable, updatable, deletable } of models) {
864
+ if (creatable) {
865
+ parts.push(
866
+ `export const CREATE_${constantCase(
867
+ name2
868
+ )} = gql\`
869
+ mutation Create${name2}Mutation($data: Create${name2}!) {
870
+ create${name2}(data: $data) { id }
871
+ }
872
+ \`;`
873
+ );
874
+ }
875
+ if (updatable) {
876
+ parts.push(
877
+ `export const UPDATE_${constantCase(
878
+ name2
879
+ )} = gql\`
880
+ mutation Update${name2}Mutation($id: ID!, $data: Update${name2}!) {
881
+ update${name2}(where: { id: $id }, data: $data) { id }
882
+ }
883
+ \`;`
884
+ );
885
+ }
886
+ if (deletable) {
887
+ parts.push(
888
+ `export const DELETE_${constantCase(
889
+ name2
890
+ )} = gql\`
891
+ mutation Delete${name2}Mutation($id: ID!) {
892
+ delete${name2}(where: { id: $id })
893
+ }
894
+ \`;`
895
+ );
896
+ }
897
+ }
898
+ return `import { gql } from "@smartive-private/graphql-magic";
899
+
900
+ ${parts.join("\n\n")}`;
901
+ };
902
+
903
+ // src/migrations/generate.ts
904
+ var import_code_block_writer = __toESM(require("code-block-writer"), 1);
905
+ var import_knex_schema_inspector = require("knex-schema-inspector");
906
+ var import_lowerFirst = __toESM(require("lodash/lowerFirst"), 1);
907
+ var MigrationGenerator = class {
908
+ constructor(knex, rawModels) {
909
+ this.rawModels = rawModels;
910
+ this.schema = (0, import_knex_schema_inspector.SchemaInspector)(knex);
911
+ this.models = getModels(rawModels);
912
+ }
913
+ writer = new (import_code_block_writer.default["default"] || import_code_block_writer.default)({
914
+ useSingleQuote: true,
915
+ indentNumberOfSpaces: 2
916
+ });
917
+ schema;
918
+ columns = {};
919
+ uuidUsed;
920
+ nowUsed;
921
+ models;
922
+ async generate() {
923
+ const { writer, schema, rawModels, models } = this;
924
+ const enums = (await schema.knex("pg_type").where({ typtype: "e" }).select("typname")).map(({ typname }) => typname);
925
+ const tables = await schema.tables();
926
+ for (const table of tables) {
927
+ this.columns[table] = await schema.columnInfo(table);
928
+ }
929
+ const up = [];
930
+ const down = [];
931
+ this.createEnums(
932
+ rawModels.filter(isEnumModel).filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
933
+ up,
934
+ down
935
+ );
936
+ for (const model of models) {
937
+ if (model.deleted) {
938
+ up.push(() => {
939
+ this.dropTable(model.name);
940
+ });
941
+ down.push(() => {
942
+ this.createTable(model.name, () => {
943
+ for (const field of model.fields) {
944
+ this.column(field);
945
+ }
946
+ });
947
+ });
948
+ if (model.updatable) {
949
+ up.push(() => {
950
+ this.dropTable(`${model.name}Revision`);
951
+ });
952
+ down.push(() => {
953
+ this.createRevisionTable(model);
954
+ });
955
+ }
956
+ }
957
+ if (model.oldName) {
958
+ up.push(() => {
959
+ this.renameTable(model.oldName, model.name);
960
+ });
961
+ down.push(() => {
962
+ this.renameTable(model.name, model.oldName);
963
+ });
964
+ tables[tables.indexOf(model.oldName)] = model.name;
965
+ this.columns[model.name] = this.columns[model.oldName];
966
+ delete this.columns[model.oldName];
967
+ if (model.updatable) {
968
+ up.push(() => {
969
+ this.renameTable(`${model.oldName}Revision`, `${model.name}Revision`);
970
+ this.alterTable(`${model.name}Revision`, () => {
971
+ this.renameColumn(`${typeToField(get(model, "oldName"))}Id`, `${typeToField(model.name)}Id`);
972
+ });
973
+ });
974
+ down.push(() => {
975
+ this.renameTable(`${model.name}Revision`, `${model.oldName}Revision`);
976
+ this.alterTable(`${model.oldName}Revision`, () => {
977
+ this.renameColumn(`${typeToField(model.name)}Id`, `${typeToField(get(model, "oldName"))}Id`);
978
+ });
979
+ });
980
+ tables[tables.indexOf(`${model.oldName}Revision`)] = `${model.name}Revision`;
981
+ this.columns[`${model.name}Revision`] = this.columns[`${model.oldName}Revision`];
982
+ delete this.columns[`${model.oldName}Revision`];
983
+ }
984
+ }
985
+ if (!tables.includes(model.name)) {
986
+ up.push(() => {
987
+ this.createTable(model.name, () => {
988
+ for (const field of model.fields) {
989
+ this.column(field);
990
+ }
991
+ });
992
+ });
993
+ down.push(() => {
994
+ this.dropTable(model.name);
995
+ });
996
+ } else {
997
+ this.renameFields(
998
+ model,
999
+ model.fields.filter(({ oldName }) => oldName),
1000
+ up,
1001
+ down
1002
+ );
1003
+ this.createFields(
1004
+ model,
1005
+ model.fields.filter(
1006
+ ({ name: name2, relation, raw, foreignKey }) => !raw && !this.columns[model.name].some((col) => col.name === (foreignKey || (relation ? `${name2}Id` : name2)))
1007
+ ),
1008
+ up,
1009
+ down
1010
+ );
1011
+ const existingFields = model.fields.filter(({ name: name2, relation, nonNull: nonNull2 }) => {
1012
+ const col = this.columns[model.name].find((col2) => col2.name === (relation ? `${name2}Id` : name2));
1013
+ if (!col) {
1014
+ return false;
1015
+ }
1016
+ return !model.nonStrict && !nonNull2 && !col.is_nullable;
1017
+ });
1018
+ this.updateFields(model, existingFields, up, down);
1019
+ }
1020
+ if (model.updatable) {
1021
+ if (!tables.includes(`${model.name}Revision`)) {
1022
+ up.push(() => {
1023
+ this.createRevisionTable(model);
1024
+ });
1025
+ if (tables.includes(model.name)) {
1026
+ up.push(() => {
1027
+ writer.block(() => {
1028
+ writer.writeLine(`const data = await knex('${model.name}');`);
1029
+ writer.write(`if (data.length)`).block(() => {
1030
+ writer.write(`await knex.batchInsert('${model.name}Revision', data.map((row) => (`).inlineBlock(() => {
1031
+ writer.writeLine(`id: uuid(),`);
1032
+ writer.writeLine(`${typeToField(model.name)}Id: row.id,`);
1033
+ this.nowUsed = true;
1034
+ writer.writeLine(`createdAt: row.updatedAt || row.createdAt || now,`);
1035
+ writer.writeLine(`createdById: row.updatedById || row.createdById,`);
1036
+ if (model.deletable) {
1037
+ writer.writeLine(`deleted: row.deleted,`);
1038
+ }
1039
+ for (const { name: name2, relation } of model.fields.filter(({ updatable }) => updatable)) {
1040
+ const col = relation ? `${name2}Id` : name2;
1041
+ writer.writeLine(`${col}: row.${col},`);
1042
+ }
1043
+ }).write(")));").newLine();
1044
+ });
1045
+ }).blankLine();
1046
+ });
1047
+ }
1048
+ down.push(() => {
1049
+ this.dropTable(`${model.name}Revision`);
1050
+ });
1051
+ } else {
1052
+ const revisionTable = `${model.name}Revision`;
1053
+ const missingRevisionFields = model.fields.filter(
1054
+ ({ name: name2, relation, raw, foreignKey, updatable }) => !raw && updatable && !this.columns[revisionTable].some((col) => col.name === (foreignKey || (relation ? `${name2}Id` : name2)))
1055
+ );
1056
+ this.createRevisionFields(model, missingRevisionFields, up, down);
1057
+ const revisionFieldsToRemove = model.fields.filter(
1058
+ ({ name: name2, updatable, foreignKey, relation, raw, generated }) => !generated && !raw && !updatable && foreignKey !== "id" && this.columns[revisionTable].some((col) => col.name === (foreignKey || (relation ? `${name2}Id` : name2)))
1059
+ );
1060
+ this.createRevisionFields(model, revisionFieldsToRemove, down, up);
1061
+ }
1062
+ }
1063
+ }
1064
+ for (const model of getModels(rawModels)) {
1065
+ if (tables.includes(model.name)) {
1066
+ this.createFields(
1067
+ model,
1068
+ model.fields.filter(({ name: name2, deleted }) => deleted && this.columns[model.name].some((col) => col.name === name2)),
1069
+ down,
1070
+ up
1071
+ );
1072
+ if (model.updatable) {
1073
+ this.createRevisionFields(
1074
+ model,
1075
+ model.fields.filter(({ deleted, updatable }) => updatable && deleted),
1076
+ down,
1077
+ up
1078
+ );
1079
+ }
1080
+ }
1081
+ }
1082
+ this.createEnums(
1083
+ rawModels.filter(isEnumModel).filter((enm2) => enm2.deleted),
1084
+ down,
1085
+ up
1086
+ );
1087
+ writer.writeLine(`import { Knex } from 'knex';`);
1088
+ if (this.uuidUsed) {
1089
+ writer.writeLine(`import { v4 as uuid } from 'uuid';`);
1090
+ }
1091
+ if (this.nowUsed) {
1092
+ writer.writeLine(`import { date } from '../src/utils/dates';`);
1093
+ }
1094
+ writer.blankLine();
1095
+ if (this.nowUsed) {
1096
+ writer.writeLine(`const now = date();`).blankLine();
1097
+ }
1098
+ this.migration("up", up);
1099
+ this.migration("down", down.reverse());
1100
+ return writer.toString();
1101
+ }
1102
+ renameFields(model, fields2, up, down) {
1103
+ if (!fields2.length) {
1104
+ return;
1105
+ }
1106
+ up.push(() => {
1107
+ for (const field of fields2) {
1108
+ this.alterTable(model.name, () => {
1109
+ this.renameColumn(
1110
+ field.relation ? `${field.oldName}Id` : get(field, "oldName"),
1111
+ field.relation ? `${field.name}Id` : field.name
1112
+ );
1113
+ });
1114
+ }
1115
+ });
1116
+ down.push(() => {
1117
+ for (const field of fields2) {
1118
+ this.alterTable(model.name, () => {
1119
+ this.renameColumn(
1120
+ field.relation ? `${field.name}Id` : field.name,
1121
+ field.relation ? `${field.oldName}Id` : get(field, "oldName")
1122
+ );
1123
+ });
1124
+ }
1125
+ });
1126
+ for (const field of fields2) {
1127
+ summonByName(this.columns[model.name], field.relation ? `${field.oldName}Id` : field.oldName).name = field.relation ? `${field.name}Id` : field.name;
1128
+ }
1129
+ }
1130
+ createFields(model, fields2, up, down) {
1131
+ if (!fields2.length) {
1132
+ return;
1133
+ }
1134
+ up.push(() => {
1135
+ const alter = [];
1136
+ const updates = [];
1137
+ const postAlter = [];
1138
+ for (const field of fields2) {
1139
+ alter.push(() => this.column(field, { setNonNull: field.default !== void 0 }));
1140
+ if (field.nonNull && field.default === void 0) {
1141
+ updates.push(() => this.writer.write(`${field.name}: 'TODO',`).newLine());
1142
+ postAlter.push(() => this.column(field, { alter: true, foreign: false }));
1143
+ }
1144
+ }
1145
+ if (alter.length) {
1146
+ this.alterTable(model.name, () => {
1147
+ alter.map((cb) => cb());
1148
+ });
1149
+ }
1150
+ if (updates.length) {
1151
+ this.writer.write(`await knex('${model.name}').update(`).inlineBlock(() => {
1152
+ updates.map((cb) => cb());
1153
+ }).write(");").newLine().blankLine();
1154
+ }
1155
+ if (postAlter.length) {
1156
+ this.alterTable(model.name, () => {
1157
+ postAlter.map((cb) => cb());
1158
+ });
1159
+ }
1160
+ });
1161
+ down.push(() => {
1162
+ this.alterTable(model.name, () => {
1163
+ for (const { relation, name: name2 } of fields2) {
1164
+ this.dropColumn(relation ? `${name2}Id` : name2);
1165
+ }
1166
+ });
1167
+ });
1168
+ }
1169
+ updateFields(model, fields2, up, down) {
1170
+ if (!fields2.length) {
1171
+ return;
1172
+ }
1173
+ up.push(() => {
1174
+ this.alterTable(model.name, () => {
1175
+ for (const field of fields2) {
1176
+ this.column(field, { alter: true });
1177
+ }
1178
+ });
1179
+ });
1180
+ down.push(() => {
1181
+ this.alterTable(model.name, () => {
1182
+ for (const field of fields2) {
1183
+ this.column(
1184
+ field,
1185
+ { alter: true },
1186
+ summonByName(this.columns[model.name], field.relation ? `${field.name}Id` : field.name)
1187
+ );
1188
+ }
1189
+ });
1190
+ });
1191
+ if (model.updatable) {
1192
+ const updatableFields = fields2.filter(({ updatable }) => updatable);
1193
+ if (!updatableFields.length) {
1194
+ return;
1195
+ }
1196
+ up.push(() => {
1197
+ this.alterTable(`${model.name}Revision`, () => {
1198
+ for (const field of updatableFields) {
1199
+ this.column(field, { alter: true });
1200
+ }
1201
+ });
1202
+ });
1203
+ down.push(() => {
1204
+ this.alterTable(`${model.name}Revision`, () => {
1205
+ for (const field of updatableFields) {
1206
+ this.column(
1207
+ field,
1208
+ { alter: true },
1209
+ summonByName(this.columns[model.name], field.relation ? `${field.name}Id` : field.name)
1210
+ );
1211
+ }
1212
+ });
1213
+ });
1214
+ }
1215
+ }
1216
+ createRevisionTable(model) {
1217
+ const writer = this.writer;
1218
+ this.createTable(`${model.name}Revision`, () => {
1219
+ writer.writeLine(`table.uuid('id').notNullable().primary();`);
1220
+ writer.writeLine(`table.uuid('${typeToField(model.name)}Id').notNullable();`);
1221
+ writer.write(`table.uuid('createdById')`);
1222
+ if (!model.nonStrict) {
1223
+ writer.write(".notNullable()");
1224
+ }
1225
+ writer.write(";").newLine();
1226
+ writer.writeLine(`table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now(0));`);
1227
+ if (model.deletable) {
1228
+ writer.writeLine(`table.boolean('deleted').notNullable();`);
1229
+ }
1230
+ for (const field of model.fields.filter((field2) => field2.updatable)) {
1231
+ this.column(field, { setUnique: false, setDefault: false });
1232
+ }
1233
+ });
1234
+ }
1235
+ createRevisionFields(model, missingRevisionFields, up, down) {
1236
+ const revisionTable = `${model.name}Revision`;
1237
+ if (missingRevisionFields.length) {
1238
+ up.push(() => {
1239
+ this.alterTable(revisionTable, () => {
1240
+ for (const field of missingRevisionFields) {
1241
+ this.column(field, { setUnique: false, setNonNull: false, setDefault: false });
1242
+ }
1243
+ });
1244
+ this.writer.write(`await knex('${model.name}Revision').update(`).inlineBlock(() => {
1245
+ for (const { name: name2, relation } of missingRevisionFields) {
1246
+ const col = relation ? `${name2}Id` : name2;
1247
+ this.writer.write(
1248
+ `${col}: knex.raw('(select "${col}" from "${model.name}" where "${model.name}".id = "${model.name}Revision"."${typeToField(model.name)}Id")'),`
1249
+ ).newLine();
1250
+ }
1251
+ }).write(");").newLine().blankLine();
1252
+ const nonNullableMissingRevisionFields = missingRevisionFields.filter(({ nonNull: nonNull2 }) => nonNull2);
1253
+ if (nonNullableMissingRevisionFields.length) {
1254
+ this.alterTable(revisionTable, () => {
1255
+ for (const field of nonNullableMissingRevisionFields) {
1256
+ this.column(field, { setUnique: false, setDefault: false, alter: true });
1257
+ }
1258
+ });
1259
+ }
1260
+ });
1261
+ down.push(() => {
1262
+ this.alterTable(revisionTable, () => {
1263
+ for (const field of missingRevisionFields) {
1264
+ this.dropColumn(field.relation ? `${field.name}Id` : field.name);
1265
+ }
1266
+ });
1267
+ });
1268
+ }
1269
+ }
1270
+ createEnums(enums, up, down) {
1271
+ for (const enm2 of enums) {
1272
+ const name2 = (0, import_lowerFirst.default)(enm2.name);
1273
+ up.push(
1274
+ () => this.writer.writeLine(
1275
+ `await knex.raw(\`CREATE TYPE "${name2}" AS ENUM (${enm2.values.map((value2) => `'${value2}'`).join(",")})\`);`
1276
+ ).newLine()
1277
+ );
1278
+ down.push(() => this.writer.writeLine(`await knex.raw('DROP TYPE "${name2}"')`));
1279
+ }
1280
+ }
1281
+ migration(name2, cbs) {
1282
+ return this.writer.write(`export const ${name2} = async (knex: Knex) => `).inlineBlock(() => {
1283
+ cbs.map((cb) => cb());
1284
+ }).write(";").newLine().blankLine();
1285
+ }
1286
+ createTable(table, block) {
1287
+ return this.writer.write(`await knex.schema.createTable('${table}', (table) => `).inlineBlock(block).write(");").newLine().blankLine();
1288
+ }
1289
+ alterTable(table, block) {
1290
+ return this.writer.write(`await knex.schema.alterTable('${table}', (table) => `).inlineBlock(block).write(");").newLine().blankLine();
1291
+ }
1292
+ dropColumn(col) {
1293
+ return this.writer.writeLine(`table.dropColumn('${col}');`);
1294
+ }
1295
+ dropTable(table) {
1296
+ return this.writer.writeLine(`await knex.schema.dropTable('${table}');`).blankLine();
1297
+ }
1298
+ renameTable(from, to) {
1299
+ return this.writer.writeLine(`await knex.schema.renameTable('${from}', '${to}');`).blankLine();
1300
+ }
1301
+ renameColumn(from, to) {
1302
+ this.writer.writeLine(`table.renameColumn('${from}', '${to}')`);
1303
+ }
1304
+ value(value2) {
1305
+ if (typeof value2 === "string") {
1306
+ return `'${value2}'`;
1307
+ }
1308
+ return value2;
1309
+ }
1310
+ column({ name: name2, relation, type, primary, list: list2, ...field }, { setUnique = true, setNonNull = true, alter = false, foreign = true, setDefault = true } = {}, toColumn) {
1311
+ const col = (what) => {
1312
+ if (what) {
1313
+ this.writer.write(what);
1314
+ }
1315
+ if (setNonNull) {
1316
+ if (toColumn) {
1317
+ if (toColumn.is_nullable) {
1318
+ this.writer.write(`.nullable()`);
1319
+ } else {
1320
+ this.writer.write(".notNullable()");
1321
+ }
1322
+ } else {
1323
+ if (field.nonNull) {
1324
+ this.writer.write(`.notNullable()`);
1325
+ } else {
1326
+ this.writer.write(".nullable()");
1327
+ }
1328
+ }
1329
+ }
1330
+ if (setDefault && field.default !== void 0) {
1331
+ this.writer.write(`.defaultTo(${this.value(field.default)})`);
1332
+ }
1333
+ if (primary) {
1334
+ this.writer.write(".primary()");
1335
+ } else if (setUnique && field.unique) {
1336
+ this.writer.write(".unique()");
1337
+ }
1338
+ if (alter) {
1339
+ this.writer.write(".alter()");
1340
+ }
1341
+ this.writer.write(";").newLine();
1342
+ };
1343
+ if (relation) {
1344
+ col(`table.uuid('${name2}Id')`);
1345
+ if (foreign && !alter) {
1346
+ this.writer.writeLine(`table.foreign('${name2}Id').references('id').inTable('${type}');`);
1347
+ }
1348
+ } else if (this.rawModels.some((m) => m.name === type && m.type === "enum")) {
1349
+ list2 ? this.writer.write(`table.specificType('${name2}', '"${typeToField(type)}"[]');`) : this.writer.write(`table.enum('${name2}', null as any, `).inlineBlock(() => {
1350
+ this.writer.writeLine(`useNative: true,`);
1351
+ this.writer.writeLine(`existingType: true,`);
1352
+ this.writer.writeLine(`enumName: '${typeToField(type)}',`);
1353
+ }).write(")");
1354
+ col();
1355
+ } else {
1356
+ switch (type) {
1357
+ case "Boolean":
1358
+ col(`table.boolean('${name2}')`);
1359
+ break;
1360
+ case "Int":
1361
+ col(`table.integer('${name2}')`);
1362
+ break;
1363
+ case "Float":
1364
+ if (field.double) {
1365
+ col(`table.double('${name2}')`);
1366
+ } else {
1367
+ col(`table.decimal('${name2}', ${get(field, "precision")}, ${get(field, "scale")})`);
1368
+ }
1369
+ break;
1370
+ case "String":
1371
+ if (field.large) {
1372
+ col(`table.text('${name2}')`);
1373
+ } else {
1374
+ col(`table.string('${name2}', ${field.maxLength})`);
1375
+ }
1376
+ break;
1377
+ case "DateTime":
1378
+ col(`table.timestamp('${name2}')`);
1379
+ break;
1380
+ case "ID":
1381
+ if (field.maxLength) {
1382
+ col(`table.string('${name2}', ${get(field, "maxLength")})`);
1383
+ } else {
1384
+ col(`table.uuid('${name2}')`);
1385
+ }
1386
+ break;
1387
+ case "Upload":
1388
+ break;
1389
+ default:
1390
+ throw new Error(`Unknown field type ${type}`);
1391
+ }
1392
+ }
1393
+ }
1394
+ };
1395
+
1396
+ // src/errors.ts
1397
+ var import_graphql2 = require("graphql");
1398
+ var GraphQLError = class extends import_graphql2.GraphQLError {
1399
+ constructor(message, extensions) {
1400
+ super(message, void 0, void 0, void 0, void 0, void 0, extensions);
1401
+ }
1402
+ };
1403
+ var ForbiddenError = class extends GraphQLError {
1404
+ constructor(what) {
1405
+ super(what, { code: "FORBIDDEN" });
1406
+ }
1407
+ };
1408
+ var NotFoundError = class extends GraphQLError {
1409
+ constructor(what) {
1410
+ super(what, { code: "NOT_FOUND" });
1411
+ }
1412
+ };
1413
+ var UserInputError = class extends GraphQLError {
1414
+ constructor(what) {
1415
+ super(what, { code: "BAD_USER_INPUT" });
1416
+ }
1417
+ };
1418
+ var PermissionError = class extends ForbiddenError {
1419
+ constructor(action, what) {
1420
+ super(`You do not have sufficient permissions to ${action.toLowerCase()} ${what}.`);
1421
+ }
1422
+ };
1423
+
1424
+ // src/resolvers/utils.ts
1425
+ var import_crypto = require("crypto");
1426
+ var import_graphql3 = require("graphql");
1427
+ var ID_ALIAS = "ID";
1428
+ var getTypeName = (t) => {
1429
+ switch (t.kind) {
1430
+ case "ListType":
1431
+ case "NonNullType":
1432
+ return getTypeName(t.type);
1433
+ default:
1434
+ return t.name.value;
1435
+ }
1436
+ };
1437
+ var isListType = (type) => {
1438
+ switch (type.kind) {
1439
+ case "ListType":
1440
+ return true;
1441
+ case "NonNullType":
1442
+ return isListType(type.type);
1443
+ default:
1444
+ return false;
1445
+ }
1446
+ };
1447
+ var isFieldNode = (n) => n.kind === import_graphql3.Kind.FIELD;
1448
+ var isInlineFragmentNode = (n) => n.kind === import_graphql3.Kind.INLINE_FRAGMENT;
1449
+ var isFragmentSpreadNode = (n) => n.kind === import_graphql3.Kind.FRAGMENT_SPREAD;
1450
+ var getType = (schema, name2) => get(schema.getType(name2), "astNode");
1451
+ var getFragmentTypeName = (node) => get(get(node.typeCondition, "name"), "value");
1452
+ function hydrate(node, raw) {
1453
+ const tableAlias = node.tableAlias;
1454
+ const res = raw.map((entry) => {
1455
+ const res2 = {};
1456
+ outer:
1457
+ for (const [column, value2] of Object.entries(entry)) {
1458
+ let current = res2;
1459
+ const shortParts = column.split("__");
1460
+ const fieldName = shortParts.pop();
1461
+ const columnWithoutField = shortParts.join("__");
1462
+ const longColumn = node.ctx.aliases.getLong(columnWithoutField);
1463
+ const longColumnWithoutRoot = longColumn.replace(new RegExp(`^${tableAlias}(__)?`), "");
1464
+ const allParts = [tableAlias, ...longColumnWithoutRoot ? longColumnWithoutRoot.split("__") : [], fieldName];
1465
+ for (let i = 0; i < allParts.length - 1; i++) {
1466
+ const part = allParts[i];
1467
+ if (!current[part]) {
1468
+ const idField = [node.ctx.aliases.getShort(allParts.slice(0, i + 1).join("__")), ID_ALIAS].join("__");
1469
+ if (!entry[idField]) {
1470
+ continue outer;
1471
+ }
1472
+ current[part] = {};
1473
+ }
1474
+ current = current[part];
1475
+ }
1476
+ current[it(fieldName)] = value2;
1477
+ }
1478
+ return res2[tableAlias];
1479
+ });
1480
+ return res;
1481
+ }
1482
+ var ors = (query, [first, ...rest]) => {
1483
+ if (!first) {
1484
+ return query;
1485
+ }
1486
+ return query.where((subQuery) => {
1487
+ subQuery.where((subSubQuery) => {
1488
+ first(subSubQuery);
1489
+ });
1490
+ for (const cb of rest) {
1491
+ subQuery.orWhere((subSubQuery) => {
1492
+ cb(subSubQuery);
1493
+ });
1494
+ }
1495
+ });
1496
+ };
1497
+ var getNameOrAlias = (node) => {
1498
+ const name2 = node.alias ? node.alias.value : node.name.value;
1499
+ if ([ID_ALIAS].indexOf(name2) >= 0) {
1500
+ throw new UserInputError(`"${name2}" can not be used as alias since it's a reserved word`);
1501
+ }
1502
+ return name2;
1503
+ };
1504
+ var apply = (target, ops) => ops.reduce((target2, op) => op(target2), target);
1505
+ var applyJoins = (aliases, query, joins) => {
1506
+ for (const [tableName, { table1Alias, column1, column2 }] of Object.entries(joins)) {
1507
+ const [table, alias] = tableName.split(":");
1508
+ const table1ShortAlias = aliases.getShort(table1Alias);
1509
+ const table2ShortAlias = aliases.getShort(alias);
1510
+ query.leftJoin(`${table} as ${table2ShortAlias}`, `${table1ShortAlias}.${column1}`, `${table2ShortAlias}.${column2}`);
1511
+ }
1512
+ };
1513
+ var addJoin = (joins, table1Alias, table2, alias2, column1, column2) => {
1514
+ joins[`${table2}:${alias2}`] ||= { table1Alias, column1, column2 };
1515
+ };
1516
+ var AliasGenerator = class {
1517
+ reverse = {};
1518
+ getShort(long) {
1519
+ if (!long) {
1520
+ const short2 = `a${Object.keys(this.reverse).length}`;
1521
+ return this.reverse[short2] = short2;
1522
+ }
1523
+ const shortPrefix = long.split("__").map((part) => part[0] + part.slice(1).replaceAll(/[a-z]+/g, "")).join("__");
1524
+ let postfix = 0;
1525
+ let short = shortPrefix + (postfix || "");
1526
+ while (short in this.reverse && this.reverse[short] !== long) {
1527
+ short = shortPrefix + ++postfix;
1528
+ }
1529
+ this.reverse[short] = long;
1530
+ return short;
1531
+ }
1532
+ getLong(short) {
1533
+ return get(this.reverse, short);
1534
+ }
1535
+ };
1536
+ var hash = (s) => (0, import_crypto.createHash)("md5").update(JSON.stringify(s)).digest("hex");
1537
+
1538
+ // src/permissions/check.ts
1539
+ var getPermissionStack = (ctx, type, action) => {
1540
+ const rolePermissions = ctx.permissions[ctx.user.role];
1541
+ if (typeof rolePermissions === "boolean" || rolePermissions === void 0) {
1542
+ return !!rolePermissions;
1543
+ }
1544
+ const typePermissions = rolePermissions[type];
1545
+ if (typeof typePermissions === "boolean" || typePermissions === void 0) {
1546
+ return !!typePermissions;
1547
+ }
1548
+ const actionPermission = typePermissions[action];
1549
+ if (typeof actionPermission === "boolean" || actionPermission === void 0) {
1550
+ return !!actionPermission;
1551
+ }
1552
+ return actionPermission;
1553
+ };
1554
+ var applyPermissions = (ctx, type, tableAlias, query, action, verifiedPermissionStack) => {
1555
+ const permissionStack = getPermissionStack(ctx, type, action);
1556
+ if (permissionStack === true) {
1557
+ return permissionStack;
1558
+ }
1559
+ if (permissionStack === false) {
1560
+ query.where(false);
1561
+ return permissionStack;
1562
+ }
1563
+ if (verifiedPermissionStack?.every(
1564
+ (prefixChain) => permissionStack.some(
1565
+ (chain) => hash(prefixChain) === hash(chain.slice(0, -1)) && // TODO: this is stricter than it could be if we add these checks to the query
1566
+ !("where" in get(chain, chain.length - 1)) && !("me" in get(chain, chain.length - 1))
1567
+ )
1568
+ )) {
1569
+ return permissionStack;
1570
+ }
1571
+ ors(
1572
+ query,
1573
+ permissionStack.map(
1574
+ (links) => (query2) => query2.whereNull(`${tableAlias}.id`).orWhereExists((subQuery) => permissionLinkQuery(ctx, subQuery, links, ctx.knex.raw(`"${tableAlias}".id`)))
1575
+ )
1576
+ );
1577
+ return permissionStack;
1578
+ };
1579
+ var getEntityToMutate = async (ctx, model, where, action) => {
1580
+ const query = ctx.knex(model.name).where(where).first();
1581
+ let entity = await query.clone();
1582
+ if (!entity) {
1583
+ throw new NotFoundError("Entity to mutate");
1584
+ }
1585
+ applyPermissions(ctx, model.name, model.name, query, action);
1586
+ entity = await query;
1587
+ if (!entity) {
1588
+ throw new PermissionError(action, `this ${model.name}`);
1589
+ }
1590
+ return entity;
1591
+ };
1592
+ var checkCanWrite = async (ctx, model, data, action) => {
1593
+ const permissionStack = getPermissionStack(ctx, model.name, action);
1594
+ if (permissionStack === true) {
1595
+ return;
1596
+ }
1597
+ if (permissionStack === false) {
1598
+ throw new PermissionError(action, getModelPlural(model));
1599
+ }
1600
+ const query = ctx.knex.select(1).first();
1601
+ let linked = false;
1602
+ for (const field of model.fields.filter(({ relation }) => relation).filter((field2) => field2.generated || (action === "CREATE" ? field2.creatable : field2.updatable))) {
1603
+ const foreignKey = field.foreignKey || `${field.name}Id`;
1604
+ const foreignId = data[foreignKey];
1605
+ if (!foreignId) {
1606
+ continue;
1607
+ }
1608
+ const fieldPermissions = field[action === "CREATE" ? "creatableBy" : "updatableBy"];
1609
+ if (fieldPermissions && !fieldPermissions.includes(ctx.user.role)) {
1610
+ throw new PermissionError(action, `this ${model.name}'s ${field.name}`);
1611
+ }
1612
+ linked = true;
1613
+ const fieldPermissionStack = getPermissionStack(ctx, field.type, "LINK");
1614
+ if (fieldPermissionStack === true) {
1615
+ query.whereExists((subQuery) => subQuery.from(`${field.type} as a`).whereRaw(`a.id = ?`, foreignId));
1616
+ continue;
1617
+ }
1618
+ if (fieldPermissionStack === false || !fieldPermissionStack.length) {
1619
+ throw new PermissionError(action, `this ${model.name}'s ${field.name}`);
1620
+ }
1621
+ ors(
1622
+ query,
1623
+ fieldPermissionStack.map(
1624
+ (links) => (query2) => query2.whereExists((subQuery) => permissionLinkQuery(ctx, subQuery, links, foreignId))
1625
+ )
1626
+ );
1627
+ }
1628
+ if (linked) {
1629
+ const canMutate = await query;
1630
+ if (!canMutate) {
1631
+ throw new PermissionError(action, `this ${model.name} because there are no entities you can link it to`);
1632
+ }
1633
+ } else if (action === "CREATE") {
1634
+ throw new PermissionError(action, `this ${model.name} because there are no entity types you can link it to`);
1635
+ }
1636
+ };
1637
+ var permissionLinkQuery = (ctx, subQuery, links, id) => {
1638
+ const aliases = new AliasGenerator();
1639
+ let alias = aliases.getShort();
1640
+ const { type, me, where } = links[0];
1641
+ subQuery.from(`${type} as ${alias}`);
1642
+ if (me) {
1643
+ subQuery.where({ [`${alias}.id`]: ctx.user.id });
1644
+ }
1645
+ if (where) {
1646
+ applyWhere(summonByName(ctx.models, type), subQuery, alias, where, aliases);
1647
+ }
1648
+ for (const { type: type2, foreignKey, reverse, where: where2 } of links) {
1649
+ const model = summonByName(ctx.models, type2);
1650
+ const subAlias = aliases.getShort();
1651
+ if (reverse) {
1652
+ subQuery.leftJoin(`${type2} as ${subAlias}`, `${alias}.${foreignKey || "id"}`, `${subAlias}.id`);
1653
+ } else {
1654
+ subQuery.rightJoin(`${type2} as ${subAlias}`, `${alias}.id`, `${subAlias}.${foreignKey || "id"}`);
1655
+ }
1656
+ subQuery.where({ [`${subAlias}.deleted`]: false });
1657
+ if (where2) {
1658
+ applyWhere(model, subQuery, subAlias, where2, aliases);
1659
+ }
1660
+ alias = subAlias;
1661
+ }
1662
+ subQuery.whereRaw(`"${alias}".id = ?`, id);
1663
+ };
1664
+ var applyWhere = (model, query, alias, where, aliases) => {
1665
+ for (const [key, value2] of Object.entries(where)) {
1666
+ const relation = model.relationsByName[key];
1667
+ if (relation) {
1668
+ const subAlias = aliases.getShort();
1669
+ query.leftJoin(
1670
+ `${relation.model.name} as ${subAlias}`,
1671
+ `${alias}.${relation.field.foreignKey || `${relation.field.name}Id`}`,
1672
+ `${subAlias}.id`
1673
+ );
1674
+ applyWhere(relation.model, query, subAlias, value2, aliases);
1675
+ } else if (Array.isArray(value2)) {
1676
+ query.whereIn(`${alias}.${key}`, value2);
1677
+ } else {
1678
+ query.where({ [`${alias}.${key}`]: value2 });
1679
+ }
1680
+ }
1681
+ };
1682
+
1683
+ // src/permissions/generate.ts
1684
+ var ACTIONS = ["READ", "CREATE", "UPDATE", "DELETE", "RESTORE", "LINK"];
1685
+ var generatePermissions = (models, config) => {
1686
+ const permissions = {};
1687
+ for (const [role, roleConfig] of Object.entries(config)) {
1688
+ if (roleConfig === true) {
1689
+ permissions[role] = true;
1690
+ continue;
1691
+ }
1692
+ const rolePermissions = {};
1693
+ for (const [key, block] of Object.entries(roleConfig)) {
1694
+ const type = key === "me" ? "User" : key;
1695
+ if (key !== "me" && !("WHERE" in block)) {
1696
+ rolePermissions[type] = {};
1697
+ for (const action of ACTIONS) {
1698
+ if (action === "READ" || action in block) {
1699
+ rolePermissions[type][action] = true;
1700
+ }
1701
+ }
1702
+ }
1703
+ addPermissions(
1704
+ models,
1705
+ rolePermissions,
1706
+ [
1707
+ {
1708
+ type,
1709
+ ...key === "me" && { me: true },
1710
+ ..."WHERE" in block && { where: block.WHERE }
1711
+ }
1712
+ ],
1713
+ block
1714
+ );
1715
+ }
1716
+ permissions[role] = rolePermissions;
1717
+ }
1718
+ return permissions;
1719
+ };
1720
+ var addPermissions = (models, permissions, links, block) => {
1721
+ const { type } = links[links.length - 1];
1722
+ const model = summonByName(models, type);
1723
+ for (const action of ACTIONS) {
1724
+ if (action === "READ" || action in block) {
1725
+ if (!permissions[type]) {
1726
+ permissions[type] = {};
1727
+ }
1728
+ if (!permissions[type][action]) {
1729
+ permissions[type][action] = [];
1730
+ }
1731
+ if (permissions[type][action] !== true) {
1732
+ permissions[type][action].push(links);
1733
+ }
1734
+ }
1735
+ }
1736
+ if (block.RELATIONS) {
1737
+ for (const [relation, subBlock] of Object.entries(block.RELATIONS)) {
1738
+ const field = model.fields.find((field2) => field2.relation && field2.name === relation);
1739
+ let link;
1740
+ if (field) {
1741
+ link = {
1742
+ type: field.type,
1743
+ foreignKey: field.foreignKey || `${field.name}Id`,
1744
+ reverse: true
1745
+ };
1746
+ } else {
1747
+ const field2 = model.reverseRelationsByName[relation];
1748
+ if (!field2) {
1749
+ throw new Error(`Relation ${relation} in model ${model.name} does not exist.`);
1750
+ }
1751
+ link = {
1752
+ type: field2.model.name,
1753
+ foreignKey: field2.foreignKey
1754
+ };
1755
+ }
1756
+ if (subBlock.WHERE) {
1757
+ link.where = subBlock.WHERE;
1758
+ }
1759
+ addPermissions(models, permissions, [...links, link], subBlock);
1760
+ }
1761
+ }
1762
+ };
1763
+
1764
+ // src/resolvers/arguments.ts
1765
+ var import_graphql4 = require("graphql");
1766
+ function getRawValue(value2, values) {
1767
+ switch (value2.kind) {
1768
+ case import_graphql4.Kind.LIST:
1769
+ if (!values) {
1770
+ return;
1771
+ }
1772
+ return value2.values.map((value3) => getRawValue(value3, values));
1773
+ case import_graphql4.Kind.VARIABLE:
1774
+ return values?.[value2.name.value];
1775
+ case import_graphql4.Kind.INT:
1776
+ return parseInt(value2.value, 10);
1777
+ case import_graphql4.Kind.NULL:
1778
+ return null;
1779
+ case import_graphql4.Kind.FLOAT:
1780
+ return parseFloat(value2.value);
1781
+ case import_graphql4.Kind.STRING:
1782
+ case import_graphql4.Kind.BOOLEAN:
1783
+ case import_graphql4.Kind.ENUM:
1784
+ return value2.value;
1785
+ case import_graphql4.Kind.OBJECT: {
1786
+ if (!value2.fields.length) {
1787
+ return;
1788
+ }
1789
+ const res = {};
1790
+ for (const field of value2.fields) {
1791
+ res[field.name.value] = getRawValue(field.value, values);
1792
+ }
1793
+ return res;
1794
+ }
1795
+ }
1796
+ }
1797
+ var normalizeArguments = (node) => {
1798
+ const normalizedArguments = {};
1799
+ if (node.field.arguments) {
1800
+ for (const argument of node.field.arguments) {
1801
+ const rawValue = getRawValue(argument.value, node.ctx.info.variableValues);
1802
+ const normalizedValue = normalizeValue(
1803
+ rawValue,
1804
+ summonByKey(node.fieldDefinition.arguments || [], "name.value", argument.name.value).type,
1805
+ node.ctx.info.schema
1806
+ );
1807
+ if (normalizedValue === void 0) {
1808
+ continue;
1809
+ }
1810
+ normalizedArguments[argument.name.value] = normalizedValue;
1811
+ }
1812
+ }
1813
+ return normalizedArguments;
1814
+ };
1815
+ function normalizeValue(value2, type, schema) {
1816
+ switch (type.kind) {
1817
+ case import_graphql4.Kind.LIST_TYPE: {
1818
+ if (Array.isArray(value2)) {
1819
+ const res = [];
1820
+ for (const v of value2) {
1821
+ res.push(normalizeValue(v, type.type, schema));
1822
+ }
1823
+ return res;
1824
+ }
1825
+ const normalizedValue = normalizeValue(value2, type.type, schema);
1826
+ if (normalizedValue === void 0) {
1827
+ return;
1828
+ }
1829
+ return [normalizedValue];
1830
+ }
1831
+ case import_graphql4.Kind.NON_NULL_TYPE:
1832
+ return normalizeValue(value2, type.type, schema);
1833
+ case import_graphql4.Kind.NAMED_TYPE:
1834
+ return normalizeValueByTypeDefinition(
1835
+ value2,
1836
+ schema.getType(type.name.value)?.astNode,
1837
+ schema
1838
+ );
1839
+ }
1840
+ }
1841
+ var normalizeValueByTypeDefinition = (value2, type, schema) => {
1842
+ if (!type || type.kind !== import_graphql4.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
1843
+ return value2;
1844
+ }
1845
+ if (!value2) {
1846
+ return;
1847
+ }
1848
+ const res = {};
1849
+ for (const key of Object.keys(value2)) {
1850
+ const field = summonByKey(type.fields, "name.value", key);
1851
+ const normalizedValue = normalizeValue(value2[key], field.type, schema);
1852
+ if (normalizedValue === void 0) {
1853
+ continue;
1854
+ }
1855
+ res[key] = normalizedValue;
1856
+ }
1857
+ return res;
1858
+ };
1859
+
1860
+ // src/resolvers/filters.ts
1861
+ var SPECIAL_FILTERS = {
1862
+ GT: "?? > ?",
1863
+ GTE: "?? >= ?",
1864
+ LT: "?? < ?",
1865
+ LTE: "?? <= ?"
1866
+ };
1867
+ var applyFilters = (node, query, joins) => {
1868
+ const normalizedArguments = normalizeArguments(node);
1869
+ if (!normalizedArguments.orderBy) {
1870
+ if (node.model.defaultOrderBy) {
1871
+ normalizedArguments.orderBy = node.model.defaultOrderBy;
1872
+ } else if (node.model.creatable) {
1873
+ normalizedArguments.orderBy = [{ createdAt: "DESC" }];
1874
+ }
1875
+ }
1876
+ if (node.model.deletable) {
1877
+ if (!normalizedArguments.where) {
1878
+ normalizedArguments.where = {};
1879
+ }
1880
+ if (normalizedArguments.where.deleted && (!Array.isArray(normalizedArguments.where.deleted) || normalizedArguments.where.deleted.some((v) => v))) {
1881
+ if (node.ctx.user.role !== "ADMIN") {
1882
+ throw new ForbiddenError("You cannot access deleted entries.");
1883
+ }
1884
+ } else {
1885
+ normalizedArguments.where.deleted = false;
1886
+ }
1887
+ }
1888
+ const { limit, offset, orderBy, where, search } = normalizedArguments;
1889
+ if (limit) {
1890
+ query.limit(limit);
1891
+ }
1892
+ if (offset) {
1893
+ query.offset(offset);
1894
+ }
1895
+ if (orderBy) {
1896
+ applyOrderBy(node, orderBy, query);
1897
+ }
1898
+ if (where) {
1899
+ const ops = [];
1900
+ applyWhere2(node, where, ops, joins);
1901
+ apply(query, ops);
1902
+ }
1903
+ if (search) {
1904
+ applySearch(node, search, query);
1905
+ }
1906
+ };
1907
+ var applyWhere2 = (node, where, ops, joins) => {
1908
+ for (const key of Object.keys(where)) {
1909
+ const value2 = where[key];
1910
+ const specialFilter = key.match(/^(\w+)_(\w+)$/);
1911
+ if (specialFilter) {
1912
+ const [, actualKey, filter] = specialFilter;
1913
+ if (!SPECIAL_FILTERS[filter]) {
1914
+ throw new Error(`Invalid filter ${key}.`);
1915
+ }
1916
+ ops.push(
1917
+ (query) => query.whereRaw(SPECIAL_FILTERS[filter], [`${node.shortTableAlias}.${actualKey}`, value2])
1918
+ );
1919
+ continue;
1920
+ }
1921
+ const field = summonByName(node.model.fields, key);
1922
+ const fullKey = `${node.shortTableAlias}.${key}`;
1923
+ if (field.relation) {
1924
+ const relation = get(node.model.relationsByName, field.name);
1925
+ const tableAlias = `${node.model.name}__W__${key}`;
1926
+ const subNode = {
1927
+ ctx: node.ctx,
1928
+ model: relation.model,
1929
+ tableName: relation.model.name,
1930
+ tableAlias,
1931
+ shortTableAlias: node.ctx.aliases.getShort(tableAlias),
1932
+ foreignKey: relation.field.foreignKey
1933
+ };
1934
+ addJoin(joins, node.tableAlias, subNode.tableName, subNode.tableAlias, get(subNode, "foreignKey"), "id");
1935
+ applyWhere2(subNode, value2, ops, joins);
1936
+ continue;
1937
+ }
1938
+ if (Array.isArray(value2)) {
1939
+ if (field && field.list) {
1940
+ ops.push(
1941
+ (query) => ors(
1942
+ query,
1943
+ value2.map((v) => (subQuery) => subQuery.whereRaw("? = ANY(??)", [v, fullKey]))
1944
+ )
1945
+ );
1946
+ continue;
1947
+ }
1948
+ if (value2.some((v) => v === null)) {
1949
+ if (value2.some((v) => v !== null)) {
1950
+ ops.push(
1951
+ (query) => ors(query, [
1952
+ (subQuery) => subQuery.whereIn(fullKey, value2.filter((v) => v !== null)),
1953
+ (subQuery) => subQuery.whereNull(fullKey)
1954
+ ])
1955
+ );
1956
+ continue;
1957
+ }
1958
+ ops.push((query) => query.whereNull(fullKey));
1959
+ continue;
1960
+ }
1961
+ ops.push((query) => query.whereIn(fullKey, value2));
1962
+ continue;
1963
+ }
1964
+ ops.push((query) => query.where({ [fullKey]: value2 }));
1965
+ }
1966
+ };
1967
+ var applySearch = (node, search, query) => ors(
1968
+ query,
1969
+ node.model.fields.filter(({ searchable }) => searchable).map(
1970
+ ({ name: name2 }) => (query2) => query2.whereILike(`${node.shortTableAlias}.${name2}`, `%${search}%`)
1971
+ )
1972
+ );
1973
+ var applyOrderBy = (node, orderBy, query) => {
1974
+ for (const vals of orderBy) {
1975
+ const keys = Object.keys(vals);
1976
+ if (keys.length !== 1) {
1977
+ throw new UserInputError(`You need to specify exactly 1 value to order by for each orderBy entry.`);
1978
+ }
1979
+ const key = keys[0];
1980
+ const value2 = vals[key];
1981
+ query.orderBy(`${node.shortTableAlias}.${key}`, value2);
1982
+ }
1983
+ };
1984
+
1985
+ // src/resolvers/mutations.ts
1986
+ var import_uuid = require("uuid");
1987
+
1988
+ // src/resolvers/resolver.ts
1989
+ var import_cloneDeep = __toESM(require("lodash/cloneDeep"), 1);
1990
+ var import_flatMap2 = __toESM(require("lodash/flatMap"), 1);
1991
+
1992
+ // src/resolvers/node.ts
1993
+ var getResolverNode = ({
1994
+ ctx,
1995
+ node,
1996
+ tableAlias,
1997
+ baseTypeDefinition,
1998
+ typeName
1999
+ }) => ({
2000
+ ctx,
2001
+ tableName: typeName,
2002
+ tableAlias,
2003
+ shortTableAlias: ctx.aliases.getShort(tableAlias),
2004
+ baseTypeDefinition,
2005
+ baseModel: ctx.models.find((model) => model.name === baseTypeDefinition.name.value),
2006
+ typeDefinition: getType(ctx.info.schema, typeName),
2007
+ model: summonByName(ctx.models, typeName),
2008
+ selectionSet: get(node.selectionSet, "selections")
2009
+ });
2010
+ var getRootFieldNode = ({
2011
+ ctx,
2012
+ node,
2013
+ baseTypeDefinition
2014
+ }) => {
2015
+ const fieldName = node.name.value;
2016
+ const fieldDefinition = summonByKey(baseTypeDefinition.fields || [], "name.value", fieldName);
2017
+ const typeName = getTypeName(fieldDefinition.type);
2018
+ return {
2019
+ ctx,
2020
+ tableName: typeName,
2021
+ tableAlias: typeName,
2022
+ shortTableAlias: ctx.aliases.getShort(typeName),
2023
+ baseTypeDefinition,
2024
+ typeDefinition: getType(ctx.info.schema, typeName),
2025
+ model: summonByName(ctx.models, typeName),
2026
+ selectionSet: get(node.selectionSet, "selections"),
2027
+ field: node,
2028
+ fieldDefinition,
2029
+ isList: isListType(fieldDefinition.type)
2030
+ };
2031
+ };
2032
+ var getSimpleFields = (node) => {
2033
+ return node.selectionSet.filter(isFieldNode).filter((selection) => {
2034
+ if (!selection.selectionSet) {
2035
+ return true;
2036
+ }
2037
+ return node.model.fields.some(({ json, name: name2 }) => json && name2 === selection.name.value);
2038
+ });
2039
+ };
2040
+ var getInlineFragments = (node) => node.selectionSet.filter(isInlineFragmentNode).map(
2041
+ (subNode) => getResolverNode({
2042
+ ctx: node.ctx,
2043
+ node: subNode,
2044
+ tableAlias: node.tableAlias + "__" + getFragmentTypeName(subNode),
2045
+ baseTypeDefinition: node.baseTypeDefinition,
2046
+ typeName: getFragmentTypeName(subNode)
2047
+ })
2048
+ );
2049
+ var getFragmentSpreads = (node) => node.selectionSet.filter(isFragmentSpreadNode).map(
2050
+ (subNode) => getResolverNode({
2051
+ ctx: node.ctx,
2052
+ node: node.ctx.info.fragments[subNode.name.value],
2053
+ tableAlias: node.tableAlias,
2054
+ baseTypeDefinition: node.baseTypeDefinition,
2055
+ typeName: node.model.name
2056
+ })
2057
+ );
2058
+ var getJoins = (node, toMany) => {
2059
+ const nodes = [];
2060
+ for (const subNode of node.selectionSet.filter(isFieldNode).filter(({ selectionSet }) => selectionSet)) {
2061
+ const ctx = node.ctx;
2062
+ const baseTypeDefinition = node.typeDefinition;
2063
+ const fieldName = subNode.name.value;
2064
+ const fieldNameOrAlias = getNameOrAlias(subNode);
2065
+ const fieldDefinition = summonByKey(baseTypeDefinition.fields || [], "name.value", fieldName);
2066
+ const typeName = getTypeName(fieldDefinition.type);
2067
+ if (isJsonObjectModel(summonByName(ctx.rawModels, typeName))) {
2068
+ continue;
2069
+ }
2070
+ const baseModel = summonByName(ctx.models, baseTypeDefinition.name.value);
2071
+ let foreignKey;
2072
+ if (toMany) {
2073
+ const reverseRelation = baseModel.reverseRelationsByName[fieldName];
2074
+ if (!reverseRelation) {
2075
+ continue;
2076
+ }
2077
+ foreignKey = reverseRelation.foreignKey;
2078
+ } else {
2079
+ const modelField = baseModel.fieldsByName[fieldName];
2080
+ if (!modelField || modelField.raw) {
2081
+ continue;
2082
+ }
2083
+ foreignKey = modelField.foreignKey;
2084
+ }
2085
+ const tableAlias = node.tableAlias + "__" + fieldNameOrAlias;
2086
+ nodes.push({
2087
+ ctx,
2088
+ tableName: typeName,
2089
+ tableAlias,
2090
+ shortTableAlias: ctx.aliases.getShort(tableAlias),
2091
+ baseTypeDefinition,
2092
+ baseModel,
2093
+ typeDefinition: getType(ctx.info.schema, typeName),
2094
+ model: summonByName(ctx.models, typeName),
2095
+ selectionSet: get(subNode.selectionSet, "selections"),
2096
+ field: subNode,
2097
+ fieldDefinition,
2098
+ foreignKey,
2099
+ isList: isListType(fieldDefinition.type)
2100
+ });
2101
+ }
2102
+ return nodes;
2103
+ };
2104
+
2105
+ // src/resolvers/resolver.ts
2106
+ var queryResolver = (_parent, _args, ctx, info) => resolve({ ...ctx, info, aliases: new AliasGenerator() });
2107
+ var resolve = async (ctx, id) => {
2108
+ const fieldNode = summonByKey(ctx.info.fieldNodes, "name.value", ctx.info.fieldName);
2109
+ const baseTypeDefinition = get(
2110
+ ctx.info.operation.operation === "query" ? ctx.info.schema.getQueryType() : ctx.info.schema.getMutationType(),
2111
+ "astNode"
2112
+ );
2113
+ const node = getRootFieldNode({
2114
+ ctx,
2115
+ node: fieldNode,
2116
+ baseTypeDefinition
2117
+ });
2118
+ const { query, verifiedPermissionStacks } = await buildQuery(node);
2119
+ if (ctx.info.fieldName === "me") {
2120
+ query.where({ [`${node.shortTableAlias}.id`]: node.ctx.user.id });
2121
+ }
2122
+ if (!node.isList) {
2123
+ query.limit(1);
2124
+ }
2125
+ if (id) {
2126
+ query.where({ id });
2127
+ }
2128
+ const raw = await query;
2129
+ const res = hydrate(node, raw);
2130
+ await applySubQueries(node, res, verifiedPermissionStacks);
2131
+ if (node.isList) {
2132
+ return res;
2133
+ }
2134
+ if (!res[0]) {
2135
+ throw new NotFoundError("Entity not found");
2136
+ }
2137
+ return res[0];
2138
+ };
2139
+ var buildQuery = async (node, parentVerifiedPermissionStacks) => {
2140
+ const { tableAlias, shortTableAlias, tableName, model, ctx } = node;
2141
+ const query = ctx.knex.fromRaw(`"${tableName}" as "${shortTableAlias}"`);
2142
+ const joins = {};
2143
+ applyFilters(node, query, joins);
2144
+ applySelects(node, query, joins);
2145
+ applyJoins(node.ctx.aliases, query, joins);
2146
+ const tables = [
2147
+ [model.name, tableAlias],
2148
+ ...Object.keys(joins).map((tableName2) => tableName2.split(":"))
2149
+ ];
2150
+ const verifiedPermissionStacks = {};
2151
+ for (const [table, alias] of tables) {
2152
+ const verifiedPermissionStack = applyPermissions(
2153
+ ctx,
2154
+ table,
2155
+ node.ctx.aliases.getShort(alias),
2156
+ query,
2157
+ "READ",
2158
+ parentVerifiedPermissionStacks?.[alias.split("__").slice(0, -1).join("__")]
2159
+ );
2160
+ if (typeof verifiedPermissionStack !== "boolean") {
2161
+ verifiedPermissionStacks[alias] = verifiedPermissionStack;
2162
+ }
2163
+ }
2164
+ return { query, verifiedPermissionStacks };
2165
+ };
2166
+ var applySelects = (node, query, joins) => {
2167
+ query.select(
2168
+ ...[
2169
+ { field: "id", alias: ID_ALIAS },
2170
+ ...getSimpleFields(node).filter((n) => {
2171
+ const field = node.model.fields.find(({ name: name2 }) => name2 === n.name.value);
2172
+ if (!field || field.relation || field.raw) {
2173
+ return false;
2174
+ }
2175
+ if (field.queriableBy && !field.queriableBy.includes(node.ctx.user.role)) {
2176
+ throw new PermissionError("READ", `${node.model.name}'s field "${field.name}"`);
2177
+ }
2178
+ return true;
2179
+ }).map((n) => ({ field: n.name.value, alias: getNameOrAlias(n) }))
2180
+ ].map(
2181
+ ({ field, alias }) => `${node.shortTableAlias}.${field} as ${node.shortTableAlias}__${alias}`
2182
+ )
2183
+ );
2184
+ for (const subNode of getInlineFragments(node)) {
2185
+ applySelects(subNode, query, joins);
2186
+ }
2187
+ for (const subNode of getFragmentSpreads(node)) {
2188
+ applySelects(subNode, query, joins);
2189
+ }
2190
+ for (const subNode of getJoins(node, false)) {
2191
+ addJoin(joins, node.tableAlias, subNode.tableName, subNode.tableAlias, get(subNode, "foreignKey"), "id");
2192
+ applySelects(subNode, query, joins);
2193
+ }
2194
+ };
2195
+ var applySubQueries = async (node, entries, parentVerifiedPermissionStacks) => {
2196
+ if (!entries.length) {
2197
+ return;
2198
+ }
2199
+ const entriesById = {};
2200
+ for (const entry of entries) {
2201
+ if (!entriesById[entry[ID_ALIAS]]) {
2202
+ entriesById[entry[ID_ALIAS]] = [];
2203
+ }
2204
+ entriesById[entry[ID_ALIAS]].push(entry);
2205
+ }
2206
+ const ids = Object.keys(entriesById);
2207
+ for (const subNode of getJoins(node, true)) {
2208
+ const fieldName = getNameOrAlias(subNode.field);
2209
+ const isList = isListType(subNode.fieldDefinition.type);
2210
+ entries.forEach((entry) => entry[fieldName] = isList ? [] : null);
2211
+ const foreignKey = get(subNode, "foreignKey");
2212
+ const { query, verifiedPermissionStacks } = await buildQuery(subNode, parentVerifiedPermissionStacks);
2213
+ const queries = ids.map(
2214
+ (id) => query.clone().select(`${subNode.shortTableAlias}.${foreignKey} as ${subNode.shortTableAlias}__${foreignKey}`).where({ [`${subNode.shortTableAlias}.${foreignKey}`]: id })
2215
+ );
2216
+ const rawChildren = (await Promise.all(queries)).flat();
2217
+ const children = hydrate(subNode, rawChildren);
2218
+ for (const child of children) {
2219
+ for (const entry of entriesById[child[foreignKey]]) {
2220
+ if (isList) {
2221
+ entry[fieldName].push((0, import_cloneDeep.default)(child));
2222
+ } else {
2223
+ entry[fieldName] = (0, import_cloneDeep.default)(child);
2224
+ }
2225
+ }
2226
+ }
2227
+ await applySubQueries(
2228
+ subNode,
2229
+ (0, import_flatMap2.default)(
2230
+ entries.map((entry) => {
2231
+ const children2 = entry[fieldName];
2232
+ return isList ? children2 : children2 ? [children2] : [];
2233
+ })
2234
+ ),
2235
+ verifiedPermissionStacks
2236
+ );
2237
+ }
2238
+ for (const subNode of getInlineFragments(node)) {
2239
+ await applySubQueries(subNode, entries, parentVerifiedPermissionStacks);
2240
+ }
2241
+ for (const subNode of getFragmentSpreads(node)) {
2242
+ await applySubQueries(subNode, entries, parentVerifiedPermissionStacks);
2243
+ }
2244
+ for (const subNode of getJoins(node, false)) {
2245
+ await applySubQueries(
2246
+ subNode,
2247
+ entries.map((item) => item[getNameOrAlias(subNode.field)]).filter(Boolean),
2248
+ parentVerifiedPermissionStacks
2249
+ );
2250
+ }
2251
+ };
2252
+
2253
+ // src/resolvers/mutations.ts
2254
+ var mutationResolver = async (_parent, args2, partialCtx, info) => {
2255
+ return await partialCtx.knex.transaction(async (knex) => {
2256
+ const [, mutation, modelName] = it(info.fieldName.match(/^(create|update|delete|restore)(.+)$/));
2257
+ const ctx = { ...partialCtx, knex, info, aliases: new AliasGenerator() };
2258
+ const model = summonByName(ctx.models, modelName);
2259
+ switch (mutation) {
2260
+ case "create":
2261
+ return await create(model, args2, ctx);
2262
+ case "update":
2263
+ return await update(model, args2, ctx);
2264
+ case "delete":
2265
+ return await del(model, args2, ctx);
2266
+ case "restore":
2267
+ return await restore(model, args2, ctx);
2268
+ }
2269
+ });
2270
+ };
2271
+ var create = async (model, { data: input2 }, ctx) => {
2272
+ const normalizedInput = { ...input2 };
2273
+ normalizedInput.id = (0, import_uuid.v4)();
2274
+ normalizedInput.createdAt = ctx.now;
2275
+ normalizedInput.createdById = ctx.user.id;
2276
+ sanitize(ctx, model, normalizedInput);
2277
+ await checkCanWrite(ctx, model, normalizedInput, "CREATE");
2278
+ await ctx.handleUploads?.(normalizedInput);
2279
+ const data = { prev: {}, input: input2, normalizedInput, next: normalizedInput };
2280
+ await ctx.mutationHook?.(model, "create", "before", data, ctx);
2281
+ await ctx.knex(model.name).insert(normalizedInput);
2282
+ await createRevision(model, normalizedInput, ctx);
2283
+ await ctx.mutationHook?.(model, "create", "after", data, ctx);
2284
+ return await resolve(ctx, normalizedInput.id);
2285
+ };
2286
+ var update = async (model, { where, data: input2 }, ctx) => {
2287
+ if (Object.keys(where).length === 0) {
2288
+ throw new Error(`No ${model.name} specified.`);
2289
+ }
2290
+ const normalizedInput = { ...input2 };
2291
+ sanitize(ctx, model, normalizedInput);
2292
+ const prev = await getEntityToMutate(ctx, model, where, "UPDATE");
2293
+ for (const key of Object.keys(normalizedInput)) {
2294
+ if (normalizedInput[key] === prev[key]) {
2295
+ delete normalizedInput[key];
2296
+ }
2297
+ }
2298
+ if (Object.keys(normalizedInput).length > 0) {
2299
+ await checkCanWrite(ctx, model, normalizedInput, "UPDATE");
2300
+ await ctx.handleUploads?.(normalizedInput);
2301
+ const next = { ...prev, ...normalizedInput };
2302
+ const data = { prev, input: input2, normalizedInput, next };
2303
+ await ctx.mutationHook?.(model, "update", "before", data, ctx);
2304
+ await ctx.knex(model.name).where(where).update(normalizedInput);
2305
+ await createRevision(model, next, ctx);
2306
+ await ctx.mutationHook?.(model, "update", "after", data, ctx);
2307
+ }
2308
+ return await resolve(ctx);
2309
+ };
2310
+ var del = async (model, { where, dryRun }, ctx) => {
2311
+ if (Object.keys(where).length === 0) {
2312
+ throw new Error(`No ${model.name} specified.`);
2313
+ }
2314
+ const entity = await getEntityToMutate(ctx, model, where, "DELETE");
2315
+ if (entity.deleted) {
2316
+ throw new ForbiddenError("Entity is already deleted.");
2317
+ }
2318
+ const toDelete = {};
2319
+ const toUnlink = {};
2320
+ const beforeHooks = [];
2321
+ const mutations = [];
2322
+ const afterHooks = [];
2323
+ const deleteCascade = async (currentModel, entity2) => {
2324
+ if (entity2.deleted) {
2325
+ return;
2326
+ }
2327
+ if (dryRun) {
2328
+ if (!(currentModel.name in toDelete)) {
2329
+ toDelete[currentModel.name] = {};
2330
+ }
2331
+ toDelete[currentModel.name][entity2.id] = entity2[currentModel.displayField || "id"] || entity2.id;
2332
+ } else {
2333
+ const normalizedInput = { deleted: true, deletedAt: ctx.now, deletedById: ctx.user.id };
2334
+ const data = { prev: entity2, input: {}, normalizedInput, next: { ...entity2, ...normalizedInput } };
2335
+ if (ctx.mutationHook) {
2336
+ beforeHooks.push(async () => {
2337
+ await ctx.mutationHook(currentModel, "delete", "before", data, ctx);
2338
+ });
2339
+ }
2340
+ mutations.push(async () => {
2341
+ await ctx.knex(currentModel.name).where({ id: entity2.id }).update(normalizedInput);
2342
+ await createRevision(currentModel, { ...entity2, deleted: true }, ctx);
2343
+ });
2344
+ if (ctx.mutationHook) {
2345
+ afterHooks.push(async () => {
2346
+ await ctx.mutationHook(currentModel, "delete", "after", data, ctx);
2347
+ });
2348
+ }
2349
+ }
2350
+ for (const {
2351
+ model: descendantModel,
2352
+ foreignKey,
2353
+ field: { name: name2, onDelete }
2354
+ } of currentModel.reverseRelations) {
2355
+ const query = ctx.knex(descendantModel.name).where({ [foreignKey]: entity2.id });
2356
+ switch (onDelete) {
2357
+ case "set-null": {
2358
+ const descendants = await query;
2359
+ for (const descendant of descendants) {
2360
+ if (dryRun) {
2361
+ if (!toUnlink[descendantModel.name]) {
2362
+ toUnlink[descendantModel.name] = {};
2363
+ }
2364
+ if (!toUnlink[descendantModel.name][descendant.id]) {
2365
+ toUnlink[descendantModel.name][descendant.id] = {
2366
+ display: descendant[descendantModel.displayField || "id"] || entity2.id,
2367
+ fields: []
2368
+ };
2369
+ }
2370
+ toUnlink[descendantModel.name][descendant.id].fields.push(name2);
2371
+ } else {
2372
+ mutations.push(async () => {
2373
+ await ctx.knex(descendantModel.name).where({ id: descendant.id }).update({
2374
+ [`${name2}Id`]: null
2375
+ });
2376
+ });
2377
+ }
2378
+ }
2379
+ break;
2380
+ }
2381
+ case "cascade":
2382
+ default: {
2383
+ applyPermissions(ctx, descendantModel.name, descendantModel.name, query, "DELETE");
2384
+ const descendants = await query;
2385
+ if (descendants.length && !descendantModel.deletable) {
2386
+ throw new ForbiddenError(`This ${model.name} depends on a ${descendantModel.name} which cannot be deleted.`);
2387
+ }
2388
+ for (const descendant of descendants) {
2389
+ await deleteCascade(descendantModel, descendant);
2390
+ }
2391
+ break;
2392
+ }
2393
+ }
2394
+ }
2395
+ };
2396
+ await deleteCascade(model, entity);
2397
+ for (const callback of [...beforeHooks, ...mutations, ...afterHooks]) {
2398
+ await callback();
2399
+ }
2400
+ if (dryRun) {
2401
+ throw new GraphQLError(`Delete dry run:`, {
2402
+ code: "DELETE_DRY_RUN",
2403
+ toDelete,
2404
+ toUnlink
2405
+ });
2406
+ }
2407
+ return entity.id;
2408
+ };
2409
+ var restore = async (model, { where }, ctx) => {
2410
+ if (Object.keys(where).length === 0) {
2411
+ throw new Error(`No ${model.name} specified.`);
2412
+ }
2413
+ const entity = await getEntityToMutate(ctx, model, where, "RESTORE");
2414
+ if (!entity.deleted) {
2415
+ throw new ForbiddenError("Entity is not deleted.");
2416
+ }
2417
+ const beforeHooks = [];
2418
+ const mutations = [];
2419
+ const afterHooks = [];
2420
+ const restoreCascade = async (currentModel, relatedEntity) => {
2421
+ if (!relatedEntity.deleted || !relatedEntity.deletedAt || !relatedEntity.deletedAt.equals(entity.deletedAt)) {
2422
+ return;
2423
+ }
2424
+ const normalizedInput = { deleted: false, deletedAt: null, deletedById: null };
2425
+ const data = { prev: relatedEntity, input: {}, normalizedInput, next: { ...relatedEntity, ...normalizedInput } };
2426
+ if (ctx.mutationHook) {
2427
+ beforeHooks.push(async () => {
2428
+ await ctx.mutationHook(model, "restore", "before", data, ctx);
2429
+ });
2430
+ }
2431
+ mutations.push(async () => {
2432
+ await ctx.knex(currentModel.name).where({ id: relatedEntity.id }).update(normalizedInput);
2433
+ await createRevision(currentModel, { ...relatedEntity, deleted: false }, ctx);
2434
+ });
2435
+ if (ctx.mutationHook) {
2436
+ afterHooks.push(async () => {
2437
+ await ctx.mutationHook(model, "restore", "after", data, ctx);
2438
+ });
2439
+ }
2440
+ for (const { model: descendantModel, foreignKey } of currentModel.reverseRelations.filter(
2441
+ ({ model: { deletable } }) => deletable
2442
+ )) {
2443
+ const query = ctx.knex(descendantModel.name).where({ [foreignKey]: relatedEntity.id });
2444
+ applyPermissions(ctx, descendantModel.name, descendantModel.name, query, "RESTORE");
2445
+ const descendants = await query;
2446
+ for (const descendant of descendants) {
2447
+ await restoreCascade(descendantModel, descendant);
2448
+ }
2449
+ }
2450
+ };
2451
+ await restoreCascade(model, entity);
2452
+ for (const callback of [...beforeHooks, ...mutations, ...afterHooks]) {
2453
+ await callback();
2454
+ }
2455
+ return entity.id;
2456
+ };
2457
+ var createRevision = async (model, data, ctx) => {
2458
+ if (model.updatable) {
2459
+ const revisionData = {
2460
+ id: (0, import_uuid.v4)(),
2461
+ [`${typeToField(model.name)}Id`]: data.id,
2462
+ createdAt: ctx.now,
2463
+ createdById: ctx.user.id
2464
+ };
2465
+ if (model.deletable) {
2466
+ revisionData.deleted = data.deleted || false;
2467
+ }
2468
+ for (const { name: name2, relation, nonNull: nonNull2, ...field } of model.fields.filter(({ updatable }) => updatable)) {
2469
+ const col = relation ? `${name2}Id` : name2;
2470
+ if (nonNull2 && (!(col in data) || col === void 0 || col === null)) {
2471
+ revisionData[col] = get(field, "default");
2472
+ } else {
2473
+ revisionData[col] = data[col];
2474
+ }
2475
+ }
2476
+ await ctx.knex(`${model.name}Revision`).insert(revisionData);
2477
+ }
2478
+ };
2479
+ var sanitize = (ctx, model, data) => {
2480
+ if (model.updatable) {
2481
+ data.updatedAt = ctx.now;
2482
+ data.updatedById = ctx.user.id;
2483
+ }
2484
+ for (const key of Object.keys(data)) {
2485
+ const field = model.fields.find(({ name: name2 }) => name2 === key);
2486
+ if (!field) {
2487
+ continue;
2488
+ }
2489
+ if (isEndOfDay(field) && data[key]) {
2490
+ data[key] = data[key].endOf("day");
2491
+ continue;
2492
+ }
2493
+ if (isEnumList(ctx.rawModels, field) && Array.isArray(data[key])) {
2494
+ data[key] = `{${data[key].join(",")}}`;
2495
+ continue;
2496
+ }
2497
+ }
2498
+ };
2499
+ var isEndOfDay = (field) => field?.endOfDay === true && field?.dateTimeType === "date" && field?.type === "DateTime";
2500
+
2501
+ // src/resolvers/resolvers.ts
2502
+ var getResolvers = (models) => ({
2503
+ Query: merge([
2504
+ {
2505
+ me: queryResolver
2506
+ },
2507
+ ...models.filter(({ queriable }) => queriable).map((model) => ({
2508
+ [typeToField(model.name)]: queryResolver
2509
+ })),
2510
+ ...models.filter(({ listQueriable }) => listQueriable).map((model) => ({
2511
+ [getModelPluralField(model)]: queryResolver
2512
+ }))
2513
+ ]),
2514
+ Mutation: merge([
2515
+ ...models.filter(({ creatable }) => creatable).map((model) => ({
2516
+ [`create${model.name}`]: mutationResolver
2517
+ })),
2518
+ ...models.filter(({ updatable }) => updatable).map((model) => ({
2519
+ [`update${model.name}`]: mutationResolver
2520
+ })),
2521
+ ...models.filter(({ deletable }) => deletable).map((model) => ({
2522
+ [`delete${model.name}`]: mutationResolver,
2523
+ [`restore${model.name}`]: mutationResolver
2524
+ }))
2525
+ ])
2526
+ });
2527
+ // Annotate the CommonJS export names for ESM import in node:
2528
+ 0 && (module.exports = {
2529
+ AliasGenerator,
2530
+ Enum,
2531
+ ForbiddenError,
2532
+ GraphQLError,
2533
+ ID_ALIAS,
2534
+ MigrationGenerator,
2535
+ NotFoundError,
2536
+ PermissionError,
2537
+ SPECIAL_FILTERS,
2538
+ UserInputError,
2539
+ actionableRelations,
2540
+ addJoin,
2541
+ and,
2542
+ apply,
2543
+ applyFilters,
2544
+ applyJoins,
2545
+ applyPermissions,
2546
+ args,
2547
+ checkCanWrite,
2548
+ directive,
2549
+ directives,
2550
+ displayField,
2551
+ document,
2552
+ enm,
2553
+ fieldType,
2554
+ fields,
2555
+ generate,
2556
+ generateDefinitions,
2557
+ generateMutations,
2558
+ generatePermissions,
2559
+ get,
2560
+ getEditEntityRelationsQuery,
2561
+ getEntityListQuery,
2562
+ getEntityQuery,
2563
+ getEntityToMutate,
2564
+ getFindEntityQuery,
2565
+ getFragmentSpreads,
2566
+ getFragmentTypeName,
2567
+ getInlineFragments,
2568
+ getJoins,
2569
+ getLabel,
2570
+ getManyToManyRelation,
2571
+ getManyToManyRelations,
2572
+ getManyToManyRelationsQuery,
2573
+ getModelLabel,
2574
+ getModelLabelPlural,
2575
+ getModelPlural,
2576
+ getModelPluralField,
2577
+ getModelSlug,
2578
+ getModels,
2579
+ getMutationQuery,
2580
+ getNameOrAlias,
2581
+ getPermissionStack,
2582
+ getResolverNode,
2583
+ getResolvers,
2584
+ getRootFieldNode,
2585
+ getSimpleFields,
2586
+ getString,
2587
+ getType,
2588
+ getTypeName,
2589
+ getUpdateEntityQuery,
2590
+ gql,
2591
+ hash,
2592
+ hydrate,
2593
+ iface,
2594
+ input,
2595
+ inputValue,
2596
+ inputValues,
2597
+ isCreatable,
2598
+ isCreatableBy,
2599
+ isEnumList,
2600
+ isEnumModel,
2601
+ isFieldNode,
2602
+ isFragmentSpreadNode,
2603
+ isInlineFragmentNode,
2604
+ isJsonObjectModel,
2605
+ isListType,
2606
+ isObjectModel,
2607
+ isQueriableBy,
2608
+ isQueriableField,
2609
+ isRaw,
2610
+ isRawEnumModel,
2611
+ isRawObjectModel,
2612
+ isRelation,
2613
+ isScalarModel,
2614
+ isSimpleField,
2615
+ isToOneRelation,
2616
+ isUpdatable,
2617
+ isUpdatableBy,
2618
+ isVisible,
2619
+ isVisibleRelation,
2620
+ it,
2621
+ list,
2622
+ merge,
2623
+ mutationResolver,
2624
+ name,
2625
+ namedType,
2626
+ nonNull,
2627
+ normalizeArguments,
2628
+ normalizeValue,
2629
+ normalizeValueByTypeDefinition,
2630
+ not,
2631
+ object,
2632
+ ors,
2633
+ printSchema,
2634
+ printSchemaFromDocument,
2635
+ printSchemaFromModels,
2636
+ queryRelations,
2637
+ queryResolver,
2638
+ resolve,
2639
+ retry,
2640
+ scalar,
2641
+ summon,
2642
+ summonByKey,
2643
+ summonByName,
2644
+ typeToField,
2645
+ value
2646
+ });