@smartive/graphql-magic 9.1.3 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/.eslintrc +2 -10
  2. package/.github/workflows/release.yml +1 -1
  3. package/.gqmrc.json +6 -0
  4. package/CHANGELOG.md +1 -6
  5. package/README.md +1 -1
  6. package/dist/bin/gqm.cjs +676 -324
  7. package/dist/cjs/index.cjs +1016 -561
  8. package/dist/esm/api/execute.js +1 -1
  9. package/dist/esm/api/execute.js.map +1 -1
  10. package/dist/esm/client/mutations.d.ts +2 -2
  11. package/dist/esm/client/mutations.js +5 -4
  12. package/dist/esm/client/mutations.js.map +1 -1
  13. package/dist/esm/client/queries.d.ts +12 -17
  14. package/dist/esm/client/queries.js +30 -50
  15. package/dist/esm/client/queries.js.map +1 -1
  16. package/dist/esm/context.d.ts +1 -2
  17. package/dist/esm/db/generate.d.ts +3 -3
  18. package/dist/esm/db/generate.js +31 -29
  19. package/dist/esm/db/generate.js.map +1 -1
  20. package/dist/esm/errors.d.ts +1 -1
  21. package/dist/esm/errors.js +2 -2
  22. package/dist/esm/errors.js.map +1 -1
  23. package/dist/esm/migrations/generate.d.ts +3 -4
  24. package/dist/esm/migrations/generate.js +114 -107
  25. package/dist/esm/migrations/generate.js.map +1 -1
  26. package/dist/esm/models/index.d.ts +1 -0
  27. package/dist/esm/models/index.js +1 -0
  28. package/dist/esm/models/index.js.map +1 -1
  29. package/dist/esm/models/model-definitions.d.ts +189 -0
  30. package/dist/esm/models/model-definitions.js +2 -0
  31. package/dist/esm/models/model-definitions.js.map +1 -0
  32. package/dist/esm/models/models.d.ts +128 -174
  33. package/dist/esm/models/models.js +411 -1
  34. package/dist/esm/models/models.js.map +1 -1
  35. package/dist/esm/models/mutation-hook.d.ts +2 -2
  36. package/dist/esm/models/utils.d.ts +35 -497
  37. package/dist/esm/models/utils.js +21 -144
  38. package/dist/esm/models/utils.js.map +1 -1
  39. package/dist/esm/permissions/check.d.ts +3 -3
  40. package/dist/esm/permissions/check.js +19 -12
  41. package/dist/esm/permissions/check.js.map +1 -1
  42. package/dist/esm/permissions/generate.js +6 -6
  43. package/dist/esm/permissions/generate.js.map +1 -1
  44. package/dist/esm/resolvers/filters.d.ts +8 -0
  45. package/dist/esm/resolvers/filters.js +28 -25
  46. package/dist/esm/resolvers/filters.js.map +1 -1
  47. package/dist/esm/resolvers/index.d.ts +1 -0
  48. package/dist/esm/resolvers/index.js +1 -0
  49. package/dist/esm/resolvers/index.js.map +1 -1
  50. package/dist/esm/resolvers/mutations.js +85 -21
  51. package/dist/esm/resolvers/mutations.js.map +1 -1
  52. package/dist/esm/resolvers/node.d.ts +13 -15
  53. package/dist/esm/resolvers/node.js +41 -36
  54. package/dist/esm/resolvers/node.js.map +1 -1
  55. package/dist/esm/resolvers/resolver.js +19 -49
  56. package/dist/esm/resolvers/resolver.js.map +1 -1
  57. package/dist/esm/resolvers/resolvers.d.ts +1 -8
  58. package/dist/esm/resolvers/resolvers.js +15 -7
  59. package/dist/esm/resolvers/resolvers.js.map +1 -1
  60. package/dist/esm/resolvers/selects.d.ts +3 -0
  61. package/dist/esm/resolvers/selects.js +50 -0
  62. package/dist/esm/resolvers/selects.js.map +1 -0
  63. package/dist/esm/resolvers/utils.d.ts +12 -4
  64. package/dist/esm/resolvers/utils.js +30 -22
  65. package/dist/esm/resolvers/utils.js.map +1 -1
  66. package/dist/esm/schema/generate.d.ts +4 -4
  67. package/dist/esm/schema/generate.js +122 -131
  68. package/dist/esm/schema/generate.js.map +1 -1
  69. package/dist/esm/schema/utils.d.ts +1 -1
  70. package/dist/esm/schema/utils.js +2 -1
  71. package/dist/esm/schema/utils.js.map +1 -1
  72. package/knexfile.ts +31 -0
  73. package/migrations/20230912185644_setup.ts +127 -0
  74. package/package.json +16 -14
  75. package/src/api/execute.ts +1 -1
  76. package/src/bin/gqm/gqm.ts +16 -16
  77. package/src/bin/gqm/parse-models.ts +5 -5
  78. package/src/bin/gqm/settings.ts +13 -4
  79. package/src/bin/gqm/static-eval.ts +5 -0
  80. package/src/bin/gqm/templates.ts +23 -3
  81. package/src/client/mutations.ts +11 -5
  82. package/src/client/queries.ts +44 -81
  83. package/src/context.ts +1 -2
  84. package/src/db/generate.ts +41 -41
  85. package/src/errors.ts +2 -2
  86. package/src/migrations/generate.ts +165 -146
  87. package/src/models/index.ts +1 -0
  88. package/src/models/model-definitions.ts +168 -0
  89. package/src/models/models.ts +510 -166
  90. package/src/models/mutation-hook.ts +2 -2
  91. package/src/models/utils.ts +53 -187
  92. package/src/permissions/check.ts +34 -16
  93. package/src/permissions/generate.ts +6 -6
  94. package/src/resolvers/filters.ts +44 -28
  95. package/src/resolvers/index.ts +1 -0
  96. package/src/resolvers/mutations.ts +98 -36
  97. package/src/resolvers/node.ts +79 -51
  98. package/src/resolvers/resolver.ts +20 -74
  99. package/src/resolvers/resolvers.ts +18 -7
  100. package/src/resolvers/selects.ts +78 -0
  101. package/src/resolvers/utils.ts +41 -25
  102. package/src/schema/generate.ts +106 -127
  103. package/src/schema/utils.ts +2 -1
  104. package/tests/api/__snapshots__/inheritance.spec.ts.snap +83 -0
  105. package/tests/api/inheritance.spec.ts +130 -0
  106. package/tests/generated/api/index.ts +1174 -0
  107. package/tests/generated/client/index.ts +1163 -0
  108. package/tests/generated/client/mutations.ts +109 -0
  109. package/tests/generated/db/index.ts +291 -0
  110. package/tests/generated/db/knex.ts +14 -0
  111. package/tests/generated/models.json +675 -0
  112. package/tests/generated/schema.graphql +325 -0
  113. package/tests/unit/__snapshots__/resolve.spec.ts.snap +23 -0
  114. package/tests/unit/queries.spec.ts +5 -5
  115. package/tests/unit/resolve.spec.ts +8 -8
  116. package/tests/utils/database/knex.ts +5 -13
  117. package/tests/utils/database/seed.ts +57 -18
  118. package/tests/utils/models.ts +62 -7
  119. package/tests/utils/server.ts +5 -5
  120. package/tsconfig.eslint.json +1 -0
  121. package/tests/unit/__snapshots__/generate.spec.ts.snap +0 -128
  122. package/tests/unit/generate.spec.ts +0 -8
  123. package/tests/utils/database/schema.ts +0 -64
  124. package/tests/utils/generate-migration.ts +0 -24
@@ -30,13 +30,27 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  var src_exports = {};
31
31
  __export(src_exports, {
32
32
  AliasGenerator: () => AliasGenerator,
33
+ EntityModel: () => EntityModel,
34
+ EnumModel: () => EnumModel,
33
35
  ForbiddenError: () => ForbiddenError,
34
36
  GraphQLError: () => GraphQLError,
35
37
  ID_ALIAS: () => ID_ALIAS,
38
+ InputModel: () => InputModel,
39
+ InterfaceModel: () => InterfaceModel,
40
+ ManyToManyRelation: () => ManyToManyRelation,
36
41
  MigrationGenerator: () => MigrationGenerator,
42
+ Model: () => Model,
43
+ Models: () => Models,
44
+ NormalRelation: () => NormalRelation,
37
45
  NotFoundError: () => NotFoundError,
46
+ ObjectModel: () => ObjectModel,
38
47
  PermissionError: () => PermissionError,
48
+ RawEnumModel: () => RawEnumModel,
49
+ Relation: () => Relation,
50
+ ReverseRelation: () => ReverseRelation,
39
51
  SPECIAL_FILTERS: () => SPECIAL_FILTERS,
52
+ ScalarModel: () => ScalarModel,
53
+ TYPE_ALIAS: () => TYPE_ALIAS,
40
54
  UserInputError: () => UserInputError,
41
55
  actionableRelations: () => actionableRelations,
42
56
  addJoin: () => addJoin,
@@ -45,6 +59,7 @@ __export(src_exports, {
45
59
  applyFilters: () => applyFilters,
46
60
  applyJoins: () => applyJoins,
47
61
  applyPermissions: () => applyPermissions,
62
+ applySelects: () => applySelects,
48
63
  args: () => args,
49
64
  checkCanWrite: () => checkCanWrite,
50
65
  directive: () => directive,
@@ -62,6 +77,8 @@ __export(src_exports, {
62
77
  generateMutations: () => generateMutations,
63
78
  generatePermissions: () => generatePermissions,
64
79
  get: () => get,
80
+ getColumn: () => getColumn,
81
+ getColumnName: () => getColumnName,
65
82
  getEditEntityRelationsQuery: () => getEditEntityRelationsQuery,
66
83
  getEntityListQuery: () => getEntityListQuery,
67
84
  getEntityQuery: () => getEntityQuery,
@@ -72,16 +89,8 @@ __export(src_exports, {
72
89
  getInlineFragments: () => getInlineFragments,
73
90
  getJoins: () => getJoins,
74
91
  getLabel: () => getLabel,
75
- getManyToManyRelation: () => getManyToManyRelation,
76
- getManyToManyRelations: () => getManyToManyRelations,
77
92
  getManyToManyRelationsQuery: () => getManyToManyRelationsQuery,
78
93
  getMigrationDate: () => getMigrationDate,
79
- getModelLabel: () => getModelLabel,
80
- getModelLabelPlural: () => getModelLabelPlural,
81
- getModelPlural: () => getModelPlural,
82
- getModelPluralField: () => getModelPluralField,
83
- getModelSlug: () => getModelSlug,
84
- getModels: () => getModels,
85
94
  getMutationQuery: () => getMutationQuery,
86
95
  getNameOrAlias: () => getNameOrAlias,
87
96
  getPermissionStack: () => getPermissionStack,
@@ -94,6 +103,7 @@ __export(src_exports, {
94
103
  getTypeName: () => getTypeName,
95
104
  getUpdateEntityQuery: () => getUpdateEntityQuery,
96
105
  gql: () => gql,
106
+ hasName: () => hasName,
97
107
  hash: () => hash,
98
108
  hydrate: () => hydrate,
99
109
  iface: () => iface,
@@ -105,12 +115,14 @@ __export(src_exports, {
105
115
  isCustomField: () => isCustomField,
106
116
  isEntityModel: () => isEntityModel,
107
117
  isEnum: () => isEnum,
108
- isEnumList: () => isEnumList,
109
118
  isEnumModel: () => isEnumModel,
110
119
  isFieldNode: () => isFieldNode,
111
120
  isFragmentSpreadNode: () => isFragmentSpreadNode,
121
+ isInTable: () => isInTable,
122
+ isInherited: () => isInherited,
112
123
  isInlineFragmentNode: () => isInlineFragmentNode,
113
124
  isInputModel: () => isInputModel,
125
+ isInterfaceModel: () => isInterfaceModel,
114
126
  isListType: () => isListType,
115
127
  isObjectModel: () => isObjectModel,
116
128
  isPrimitive: () => isPrimitive,
@@ -118,16 +130,20 @@ __export(src_exports, {
118
130
  isQueriableField: () => isQueriableField,
119
131
  isRawEnumModel: () => isRawEnumModel,
120
132
  isRelation: () => isRelation,
133
+ isRootModel: () => isRootModel,
121
134
  isScalarModel: () => isScalarModel,
122
135
  isSimpleField: () => isSimpleField,
123
136
  isToOneRelation: () => isToOneRelation,
124
137
  isUpdatable: () => isUpdatable,
125
138
  isUpdatableBy: () => isUpdatableBy,
139
+ isUpdatableField: () => isUpdatableField,
140
+ isUpdatableModel: () => isUpdatableModel,
126
141
  isVisible: () => isVisible,
127
142
  isVisibleRelation: () => isVisibleRelation,
128
143
  it: () => it,
129
144
  list: () => list,
130
145
  merge: () => merge2,
146
+ modelNeedsTable: () => modelNeedsTable,
131
147
  mutationResolver: () => mutationResolver,
132
148
  name: () => name,
133
149
  namedType: () => namedType,
@@ -137,6 +153,7 @@ __export(src_exports, {
137
153
  normalizeValueByTypeDefinition: () => normalizeValueByTypeDefinition,
138
154
  not: () => not,
139
155
  object: () => object,
156
+ or: () => or,
140
157
  ors: () => ors,
141
158
  printSchema: () => printSchema,
142
159
  printSchemaFromDocument: () => printSchemaFromDocument,
@@ -163,7 +180,7 @@ var execute = async ({
163
180
  body,
164
181
  ...ctx
165
182
  }) => {
166
- const document2 = generate(ctx.rawModels);
183
+ const document2 = generate(ctx.models);
167
184
  const generatedResolvers = getResolvers(ctx.models);
168
185
  const schema = (0, import_schema.makeExecutableSchema)({
169
186
  typeDefs: document2,
@@ -199,9 +216,9 @@ var gql = (chunks, ...variables) => {
199
216
  // src/client/mutations.ts
200
217
  var import_upperCase = __toESM(require("lodash/upperCase"), 1);
201
218
  var constantCase = (str) => (0, import_upperCase.default)(str).replace(/ /g, "_");
202
- var generateMutations = (models) => {
219
+ var generateMutations = (models, gqmModule = "@smartive/graphql-magic") => {
203
220
  const parts = [];
204
- for (const { name: name2, creatable, updatable, deletable } of models.filter(isEntityModel)) {
221
+ for (const { name: name2, creatable, updatable, deletable } of models.entities.filter(not(isRootModel))) {
205
222
  if (creatable) {
206
223
  parts.push(
207
224
  `export const CREATE_${constantCase(
@@ -232,11 +249,20 @@ var generateMutations = (models) => {
232
249
  mutation Delete${name2}Mutation($id: ID!) {
233
250
  delete${name2}(where: { id: $id })
234
251
  }
252
+ \`;`
253
+ );
254
+ parts.push(
255
+ `export const RESTORE_${constantCase(
256
+ name2
257
+ )} = gql\`
258
+ mutation Restore${name2}Mutation($id: ID!) {
259
+ restore${name2}(where: { id: $id })
260
+ }
235
261
  \`;`
236
262
  );
237
263
  }
238
264
  }
239
- return `import { gql } from "@smartive/graphql-magic";
265
+ return `import { gql } from "${gqmModule}";
240
266
 
241
267
  ${parts.join("\n\n")}`;
242
268
  };
@@ -246,96 +272,104 @@ var import_upperFirst = __toESM(require("lodash/upperFirst"), 1);
246
272
 
247
273
  // src/models/utils.ts
248
274
  var import_assert = __toESM(require("assert"), 1);
249
- var import_inflection = require("inflection");
250
- var import_camelCase = __toESM(require("lodash/camelCase"), 1);
275
+ var import_lodash3 = require("lodash");
251
276
  var import_get = __toESM(require("lodash/get"), 1);
252
- var import_kebabCase = __toESM(require("lodash/kebabCase"), 1);
253
- var import_startCase = __toESM(require("lodash/startCase"), 1);
254
- var isNotFalsy = (v) => typeof v !== "undefined" && v !== null && v !== false;
255
- var merge2 = (objects) => (objects || []).filter(isNotFalsy).reduce((i, acc) => ({ ...acc, ...i }), {});
256
- var typeToField = (type) => type.substr(0, 1).toLowerCase() + type.substr(1);
257
- var getModelPlural = (model) => model.plural || (0, import_inflection.pluralize)(model.name);
258
- var getModelPluralField = (model) => typeToField(getModelPlural(model));
259
- var getModelSlug = (model) => (0, import_kebabCase.default)(getModelPlural(model));
260
- var getModelLabelPlural = (model) => getLabel(getModelPlural(model));
261
- var getModelLabel = (model) => getLabel(model.name);
262
- var getLabel = (s) => (0, import_startCase.default)((0, import_camelCase.default)(s));
263
- var isEntityModel = (model) => model.kind === "entity";
264
- var isEnumModel = (model) => model.kind === "enum";
265
- var isRawEnumModel = (model) => model.kind === "raw-enum";
266
- var isScalarModel = (model) => model.kind === "scalar";
267
- var isObjectModel = (model) => model.kind === "object";
268
- var isInputModel = (model) => model.kind === "input";
269
- var isEnumList = (models, field) => field?.list === true && models.find(({ name: name2 }) => name2 === field.kind)?.kind === "enum";
270
- var and = (...predicates) => (field) => predicates.every((predicate) => predicate(field));
271
- var not = (predicate) => (field) => !predicate(field);
272
- var isPrimitive = (field) => field.kind === void 0 || field.kind === "primitive";
273
- var isEnum = (field) => field.kind === "enum";
274
- var isRelation = (field) => field.kind === "relation";
275
- var isToOneRelation = (field) => isRelation(field) && !!field.toOne;
276
- var isQueriableField = ({ queriable }) => queriable !== false;
277
- var isCustomField = (field) => field.kind === "custom";
278
- var isVisible = ({ hidden }) => hidden !== true;
279
- var isSimpleField = and(not(isRelation), not(isCustomField));
280
- var isUpdatable = ({ updatable }) => !!updatable;
281
- var isCreatable = ({ creatable }) => !!creatable;
282
- var isQueriableBy = (role) => (field) => field.queriable !== false && (field.queriable === void 0 || field.queriable === true || !field.queriable.roles || field.queriable.roles.includes(role));
283
- var isUpdatableBy = (role) => (field) => field.updatable && (field.updatable === true || !field.updatable.roles || field.updatable.roles.includes(role));
284
- var isCreatableBy = (role) => (field) => field.creatable && (field.creatable === true || !field.creatable.roles || field.creatable.roles.includes(role));
285
- var actionableRelations = (model, action) => model.fields.filter(isRelation).filter(
286
- (field) => field[`${action === "filter" ? action : action.slice(0, -1)}able`]
287
- );
288
- var getModels = (rawModels) => {
289
- const models = rawModels.filter(isEntityModel).map((model) => {
290
- const objectModel = {
291
- ...model,
292
- fieldsByName: {},
293
- relations: [],
294
- relationsByName: {},
295
- reverseRelations: [],
296
- reverseRelationsByName: {},
297
- fields: [
298
- { name: "id", type: "ID", nonNull: true, unique: true, primary: true, generated: true },
299
- ...model.fields,
300
- ...model.creatable ? [
277
+
278
+ // src/models/models.ts
279
+ var import_inflection = require("inflection");
280
+ var import_lodash2 = require("lodash");
281
+ var Models = class {
282
+ models;
283
+ modelsByName = {};
284
+ scalars;
285
+ rawEnums;
286
+ enums;
287
+ inputs;
288
+ interfaces;
289
+ objects;
290
+ entities;
291
+ definitions;
292
+ constructor(definitions) {
293
+ this.definitions = (0, import_lodash2.cloneDeep)(definitions);
294
+ this.definitions.push(
295
+ {
296
+ kind: "scalar",
297
+ name: "DateTime"
298
+ },
299
+ { kind: "scalar", name: "Upload" },
300
+ {
301
+ kind: "raw-enum",
302
+ name: "Order",
303
+ values: ["ASC", "DESC"]
304
+ }
305
+ );
306
+ const entities = this.definitions.filter(isEntityModelDefinition);
307
+ for (const entity of entities) {
308
+ if (entity.root) {
309
+ this.definitions.push({
310
+ kind: "enum",
311
+ name: `${entity.name}Type`,
312
+ values: entities.filter((subModel) => subModel.parent === entity.name).map((subModel) => subModel.name)
313
+ });
314
+ }
315
+ entity.fields = [
316
+ {
317
+ name: "id",
318
+ type: "ID",
319
+ nonNull: true,
320
+ unique: true,
321
+ primary: true,
322
+ generated: true
323
+ },
324
+ ...entity.root ? [
325
+ {
326
+ name: "type",
327
+ kind: "enum",
328
+ type: `${entity.name}Type`,
329
+ nonNull: true,
330
+ generated: true
331
+ }
332
+ ] : [],
333
+ ...entity.fields,
334
+ ...entity.creatable ? [
301
335
  {
302
336
  name: "createdAt",
303
337
  type: "DateTime",
304
338
  nonNull: true,
305
339
  orderable: true,
306
340
  generated: true,
307
- ...typeof model.creatable === "object" && model.creatable.createdAt
341
+ ...typeof entity.creatable === "object" && entity.creatable.createdAt
308
342
  },
309
343
  {
310
344
  name: "createdBy",
311
345
  kind: "relation",
312
346
  type: "User",
313
347
  nonNull: true,
314
- reverse: `created${getModelPlural(model)}`,
348
+ reverse: `created${getModelPlural(entity)}`,
315
349
  generated: true,
316
- ...typeof model.creatable === "object" && model.creatable.createdBy
350
+ ...typeof entity.creatable === "object" && entity.creatable.createdBy
317
351
  }
318
352
  ] : [],
319
- ...model.updatable ? [
353
+ ...entity.updatable ? [
320
354
  {
321
355
  name: "updatedAt",
322
356
  type: "DateTime",
323
357
  nonNull: true,
324
358
  orderable: true,
325
359
  generated: true,
326
- ...typeof model.updatable === "object" && model.updatable.updatedAt
360
+ ...typeof entity.updatable === "object" && entity.updatable.updatedAt
327
361
  },
328
362
  {
329
363
  name: "updatedBy",
330
364
  kind: "relation",
331
365
  type: "User",
332
366
  nonNull: true,
333
- reverse: `updated${getModelPlural(model)}`,
367
+ reverse: `updated${getModelPlural(entity)}`,
334
368
  generated: true,
335
- ...typeof model.updatable === "object" && model.updatable.updatedBy
369
+ ...typeof entity.updatable === "object" && entity.updatable.updatedBy
336
370
  }
337
371
  ] : [],
338
- ...model.deletable ? [
372
+ ...entity.deletable ? [
339
373
  {
340
374
  name: "deleted",
341
375
  type: "Boolean",
@@ -343,65 +377,349 @@ var getModels = (rawModels) => {
343
377
  defaultValue: false,
344
378
  filterable: { default: false },
345
379
  generated: true,
346
- ...typeof model.deletable === "object" && model.deletable.deleted
380
+ ...typeof entity.deletable === "object" && entity.deletable.deleted
347
381
  },
348
382
  {
349
383
  name: "deletedAt",
350
384
  type: "DateTime",
351
385
  orderable: true,
352
386
  generated: true,
353
- ...typeof model.deletable === "object" && model.deletable.deletedAt
387
+ ...typeof entity.deletable === "object" && entity.deletable.deletedAt
354
388
  },
355
389
  {
356
390
  name: "deletedBy",
357
391
  kind: "relation",
358
392
  type: "User",
359
- reverse: `deleted${getModelPlural(model)}`,
393
+ reverse: `deleted${getModelPlural(entity)}`,
360
394
  generated: true,
361
- ...typeof model.deletable === "object" && model.deletable.deletedBy
395
+ ...typeof entity.deletable === "object" && entity.deletable.deletedBy
362
396
  }
363
397
  ] : []
364
- ].map((field) => ({
365
- ...field,
366
- ...field.kind === "relation" && {
367
- foreignKey: field.foreignKey || `${field.name}Id`
398
+ ];
399
+ for (const field of entity.fields) {
400
+ if (field.kind === "relation") {
401
+ field.foreignKey = field.foreignKey || `${field.name}Id`;
368
402
  }
369
- }))
370
- };
371
- for (const field of objectModel.fields) {
372
- objectModel.fieldsByName[field.name] = field;
403
+ }
373
404
  }
374
- return objectModel;
375
- });
376
- for (const model of models) {
377
- for (const field of model.fields) {
378
- if (field.kind !== "relation") {
379
- continue;
405
+ for (const model of entities) {
406
+ if (model.parent) {
407
+ const parent = summonByName(entities, model.parent);
408
+ const INHERITED_FIELDS = ["queriable", "listQueriable", "creatable", "updatable", "deletable"];
409
+ Object.assign(model, (0, import_lodash2.pick)(parent, INHERITED_FIELDS), (0, import_lodash2.pick)(model, INHERITED_FIELDS));
410
+ model.fields = [
411
+ ...parent.fields.map((field) => ({
412
+ ...field,
413
+ ...field.kind === "relation" && field.reverse && { reverse: field.reverse.replace(getModelPlural(parent), getModelPlural(model)) },
414
+ ...model.fields.find((childField) => childField.name === field.name),
415
+ inherited: true
416
+ })),
417
+ ...model.fields.filter((field) => !parent.fields.some((parentField) => parentField.name === field.name))
418
+ ];
380
419
  }
381
- const fieldModel = summonByName(models, field.type);
382
- const reverseRelation = {
383
- kind: "relation",
384
- name: field.reverse || (field.toOne ? typeToField(model.name) : getModelPluralField(model)),
385
- foreignKey: get(field, "foreignKey"),
386
- type: model.name,
387
- toOne: !!field.toOne,
388
- fieldModel,
389
- field,
390
- model
391
- };
392
- const relation = {
393
- field,
394
- model: fieldModel,
395
- reverseRelation
396
- };
397
- model.relations.push(relation);
398
- model.relationsByName[relation.field.name] = relation;
399
- fieldModel.reverseRelations.push(reverseRelation);
400
- fieldModel.reverseRelationsByName[reverseRelation.name] = reverseRelation;
401
420
  }
421
+ this.models = this.definitions.map(
422
+ (definition) => new MODEL_KIND_TO_CLASS_MAPPING[definition.kind](this, definition)
423
+ );
424
+ for (const model of this.models) {
425
+ this.modelsByName[model.name] = model;
426
+ }
427
+ this.entities = this.models.filter((model) => model instanceof EntityModel);
428
+ this.enums = this.models.filter((model) => model instanceof EnumModel);
429
+ this.inputs = this.models.filter((model) => model instanceof InputModel);
430
+ this.interfaces = this.models.filter((model) => model instanceof InterfaceModel);
431
+ this.objects = this.models.filter((model) => model instanceof ObjectModel);
432
+ this.rawEnums = this.models.filter((model) => model instanceof RawEnumModel);
433
+ this.scalars = this.models.filter((model) => model instanceof ScalarModel);
434
+ }
435
+ getModel(name2, kind) {
436
+ const model = get(this.modelsByName, name2);
437
+ if (!kind) {
438
+ return model;
439
+ }
440
+ const expectedType = MODEL_KIND_TO_CLASS_MAPPING[kind];
441
+ if (!(model instanceof expectedType)) {
442
+ throw new Error(`Model ${name2} is not of kind ${kind}.`);
443
+ }
444
+ return model;
445
+ }
446
+ };
447
+ var Model = class {
448
+ constructor(models, definition) {
449
+ this.models = models;
450
+ Object.assign(this, definition);
451
+ this.plural = definition.plural || (0, import_inflection.pluralize)(definition.name);
452
+ }
453
+ name;
454
+ plural;
455
+ description;
456
+ };
457
+ var ScalarModel = class extends Model {
458
+ kind;
459
+ };
460
+ var EnumModel = class extends Model {
461
+ kind;
462
+ values;
463
+ deleted;
464
+ constructor(models, definition) {
465
+ super(models, definition);
466
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
467
+ }
468
+ };
469
+ var RawEnumModel = class extends Model {
470
+ kind;
471
+ values;
472
+ constructor(models, definition) {
473
+ super(models, definition);
474
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
475
+ }
476
+ };
477
+ var InterfaceModel = class extends Model {
478
+ kind;
479
+ fields;
480
+ constructor(models, definition) {
481
+ super(models, definition);
482
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
483
+ }
484
+ };
485
+ var InputModel = class extends Model {
486
+ kind;
487
+ fields;
488
+ constructor(models, definition) {
489
+ super(models, definition);
490
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
491
+ }
492
+ };
493
+ var ObjectModel = class extends Model {
494
+ kind;
495
+ fields;
496
+ constructor(models, definition) {
497
+ super(models, definition);
498
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
402
499
  }
403
- return models;
404
500
  };
501
+ var EntityModel = class extends Model {
502
+ kind;
503
+ root;
504
+ parent;
505
+ interfaces;
506
+ queriable;
507
+ listQueriable;
508
+ creatable;
509
+ updatable;
510
+ deletable;
511
+ displayField;
512
+ defaultOrderBy;
513
+ fields;
514
+ // temporary fields for the generation of migrations
515
+ deleted;
516
+ oldName;
517
+ fieldsByName = {};
518
+ fieldsByColumnName = {};
519
+ _relations;
520
+ _relationsByName;
521
+ _reverseRelations;
522
+ _reverseRelationsByName;
523
+ _manyToManyRelations;
524
+ _manyToManyRelationsByName;
525
+ pluralField;
526
+ slug;
527
+ labelPlural;
528
+ label;
529
+ _parentModel;
530
+ constructor(models, definition) {
531
+ super(models, definition);
532
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
533
+ this.pluralField = typeToField(this.plural);
534
+ this.slug = (0, import_lodash2.kebabCase)(this.plural);
535
+ this.labelPlural = getLabel(this.plural);
536
+ this.label = getLabel(definition.name);
537
+ for (const field of definition.fields) {
538
+ this.fieldsByName[field.name] = field;
539
+ }
540
+ }
541
+ getField(name2) {
542
+ return get(this.fieldsByName, name2);
543
+ }
544
+ get relations() {
545
+ if (!this._relations) {
546
+ this._relations = this.fields.filter(isRelation).map((relationField) => new NormalRelation(this, relationField, this.models.getModel(relationField.type, "entity")));
547
+ }
548
+ return this._relations;
549
+ }
550
+ get relationsByName() {
551
+ if (!this._relationsByName) {
552
+ this._relationsByName = {};
553
+ for (const relation of this.relations) {
554
+ this._relationsByName[relation.name] = relation;
555
+ }
556
+ }
557
+ return this._relationsByName;
558
+ }
559
+ getRelation(name2) {
560
+ return get(this.relationsByName, name2);
561
+ }
562
+ get reverseRelations() {
563
+ if (!this._reverseRelations) {
564
+ this._reverseRelations = this.models.entities.flatMap(
565
+ (model) => model.relations.filter((relation) => relation.targetModel.name === this.name || relation.targetModel.name === this.rootModel.name).map((relation) => relation.reverse)
566
+ );
567
+ }
568
+ return this._reverseRelations;
569
+ }
570
+ get reverseRelationsByName() {
571
+ if (!this._reverseRelationsByName) {
572
+ this._reverseRelationsByName = {};
573
+ for (const reverseRelation of this.reverseRelations) {
574
+ this._reverseRelationsByName[reverseRelation.name] = reverseRelation;
575
+ }
576
+ }
577
+ return this._reverseRelationsByName;
578
+ }
579
+ getReverseRelation(name2) {
580
+ return get(this.reverseRelationsByName, name2);
581
+ }
582
+ get manyToManyRelations() {
583
+ if (!this._manyToManyRelations) {
584
+ this._manyToManyRelations = [];
585
+ for (const relationFromSource of this.reverseRelations) {
586
+ const relationToTarget = relationFromSource.targetModel.relations.find(
587
+ (relation) => !relation.field.generated && relation.field.name !== relationFromSource.field.name
588
+ );
589
+ if (!relationToTarget) {
590
+ continue;
591
+ }
592
+ const inapplicableFields = relationFromSource.targetModel.fields.filter(
593
+ (otherField) => !otherField.generated && ![relationFromSource.field.name, relationToTarget.field.name].includes(otherField.name)
594
+ );
595
+ if (inapplicableFields.length) {
596
+ continue;
597
+ }
598
+ this._manyToManyRelations.push(new ManyToManyRelation(relationFromSource, relationToTarget));
599
+ }
600
+ }
601
+ return this._manyToManyRelations;
602
+ }
603
+ get manyToManyRelationsByName() {
604
+ if (!this._manyToManyRelationsByName) {
605
+ this._manyToManyRelationsByName = {};
606
+ for (const manyToManyRelation of this.manyToManyRelations) {
607
+ this._manyToManyRelationsByName[manyToManyRelation.name] = manyToManyRelation;
608
+ }
609
+ }
610
+ return this._manyToManyRelationsByName;
611
+ }
612
+ getManyToManyRelation(name2) {
613
+ return get(this.manyToManyRelationsByName, name2);
614
+ }
615
+ get parentModel() {
616
+ if (this.parent) {
617
+ if (!this._parentModel) {
618
+ this._parentModel = this.models.getModel(this.parent, "entity");
619
+ }
620
+ return this._parentModel;
621
+ }
622
+ }
623
+ get rootModel() {
624
+ return this.parentModel || this;
625
+ }
626
+ };
627
+ var MODEL_KIND_TO_CLASS_MAPPING = {
628
+ entity: EntityModel,
629
+ enum: EnumModel,
630
+ input: InputModel,
631
+ interface: InterfaceModel,
632
+ object: ObjectModel,
633
+ "raw-enum": RawEnumModel,
634
+ scalar: ScalarModel
635
+ };
636
+ var Relation = class {
637
+ constructor(name2, sourceModel, field, targetModel) {
638
+ this.name = name2;
639
+ this.sourceModel = sourceModel;
640
+ this.field = field;
641
+ this.targetModel = targetModel;
642
+ }
643
+ };
644
+ var NormalRelation = class extends Relation {
645
+ constructor(sourceModel, field, targetModel) {
646
+ super(field.name, sourceModel, field, targetModel);
647
+ this.field = field;
648
+ this.reverse = new ReverseRelation(this);
649
+ }
650
+ reverse;
651
+ };
652
+ var ReverseRelation = class extends Relation {
653
+ constructor(reverse) {
654
+ super(
655
+ reverse.field.reverse || (reverse.field.toOne ? typeToField(reverse.sourceModel.name) : reverse.sourceModel.pluralField),
656
+ reverse.targetModel,
657
+ reverse.field,
658
+ reverse.sourceModel
659
+ );
660
+ this.reverse = reverse;
661
+ }
662
+ };
663
+ var ManyToManyRelation = class {
664
+ name;
665
+ sourceModel;
666
+ relationFromSource;
667
+ relationModel;
668
+ relationToTarget;
669
+ targetModel;
670
+ constructor(relationFromSource, relationToTarget) {
671
+ this.name = relationFromSource.name;
672
+ this.sourceModel = relationFromSource.sourceModel;
673
+ this.relationFromSource = relationFromSource;
674
+ this.relationModel = relationFromSource.targetModel;
675
+ if (this.relationModel !== relationToTarget.sourceModel) {
676
+ throw new Error(`Relation model is ambiguous.`);
677
+ }
678
+ this.relationToTarget = relationToTarget;
679
+ this.targetModel = relationToTarget.targetModel;
680
+ }
681
+ };
682
+ var isEntityModelDefinition = (definition) => definition.kind === "entity";
683
+ var getModelPlural = (model) => model.plural || (0, import_inflection.pluralize)(model.name);
684
+
685
+ // src/models/utils.ts
686
+ var isNotFalsy = (v) => typeof v !== "undefined" && v !== null && v !== false;
687
+ var merge2 = (objects) => (objects || []).filter(isNotFalsy).reduce((i, acc) => ({ ...acc, ...i }), {});
688
+ var typeToField = (type) => type.substr(0, 1).toLowerCase() + type.substr(1);
689
+ var getLabel = (s) => (0, import_lodash3.startCase)((0, import_lodash3.camelCase)(s));
690
+ var or = (...predicates) => (field) => predicates.some((predicate) => predicate(field));
691
+ var and = (...predicates) => (field) => predicates.every((predicate) => predicate(field));
692
+ var not = (predicate) => (field) => !predicate(field);
693
+ var isRootModel = (model) => model.root;
694
+ var isEntityModel = (model) => model instanceof EntityModel;
695
+ var isEnumModel = (model) => model instanceof EnumModel;
696
+ var isRawEnumModel = (model) => model instanceof RawEnumModel;
697
+ var isScalarModel = (model) => model instanceof ScalarModel;
698
+ var isObjectModel = (model) => model instanceof ObjectModel;
699
+ var isInputModel = (model) => model instanceof InputModel;
700
+ var isInterfaceModel = (model) => model instanceof InterfaceModel;
701
+ var isUpdatableModel = (model) => model.updatable && model.fields.some(isUpdatableField);
702
+ var isUpdatableField = (field) => !field.inherited && !!field.updatable;
703
+ var modelNeedsTable = (model) => model.fields.some((field) => !field.inherited);
704
+ var hasName = (name2) => (field) => field.name == name2;
705
+ var isPrimitive = (field) => field.kind === void 0 || field.kind === "primitive";
706
+ var isEnum = (field) => field.kind === "enum";
707
+ var isRelation = (field) => field.kind === "relation";
708
+ var isInherited = (field) => field.inherited;
709
+ var isInTable = (field) => field.name === "id" || !field.inherited;
710
+ var isToOneRelation = (field) => isRelation(field) && !!field.toOne;
711
+ var isQueriableField = ({ queriable }) => queriable !== false;
712
+ var isCustomField = (field) => field.kind === "custom";
713
+ var isVisible = ({ hidden }) => hidden !== true;
714
+ var isSimpleField = and(not(isRelation), not(isCustomField));
715
+ var isUpdatable = ({ updatable }) => !!updatable;
716
+ var isCreatable = ({ creatable }) => !!creatable;
717
+ var isQueriableBy = (role) => (field) => field.queriable !== false && (field.queriable === void 0 || field.queriable === true || !field.queriable.roles || field.queriable.roles.includes(role));
718
+ var isUpdatableBy = (role) => (field) => field.updatable && (field.updatable === true || !field.updatable.roles || field.updatable.roles.includes(role));
719
+ var isCreatableBy = (role) => (field) => field.creatable && (field.creatable === true || !field.creatable.roles || field.creatable.roles.includes(role));
720
+ var actionableRelations = (model, action) => model.relations.filter(
721
+ (relation) => relation.field[`${action === "filter" ? action : action.slice(0, -1)}able`]
722
+ );
405
723
  var summonByName = (array, value2) => summonByKey(array, "name", value2);
406
724
  var summonByKey = (array, key, value2) => summon(array, (element) => (0, import_get.default)(element, key) === value2, `No element found with ${key} ${value2}`);
407
725
  var summon = (array, cb, errorMessage) => {
@@ -426,8 +744,9 @@ var it = (object2) => {
426
744
  var get = (object2, key) => {
427
745
  const value2 = it(object2)[key];
428
746
  if (value2 === void 0 || value2 === null) {
429
- console.trace();
430
- throw new Error(`Object doesn't have ${String(key)}`);
747
+ const error = new Error(`Object doesn't have ${String(key)}`);
748
+ console.warn(error);
749
+ throw error;
431
750
  }
432
751
  return value2;
433
752
  };
@@ -456,73 +775,49 @@ var getUpdateEntityQuery = (model, role, fields2, additionalFields = "") => `que
456
775
  ${additionalFields}
457
776
  }
458
777
  }`;
459
- var getEditEntityRelationsQuery = (models, model, action, fields2, ignoreFields, additionalFields = {}) => {
778
+ var getEditEntityRelationsQuery = (model, action, fields2, ignoreFields, additionalFields = {}) => {
460
779
  const relations = actionableRelations(model, action).filter(
461
780
  ({ name: name2 }) => (!fields2 || fields2.includes(name2)) && (!ignoreFields || !ignoreFields.includes(name2))
462
781
  );
463
782
  return !!relations.length && `query ${(0, import_upperFirst.default)(action)}${model.name}Relations {
464
- ${relations.map(({ name: name2, type }) => {
465
- const model2 = summonByName(models, type);
783
+ ${relations.map((relation) => {
466
784
  let filters = "";
467
- if (model2.displayField) {
468
- const displayField2 = model2.fieldsByName[model2.displayField];
785
+ if (relation.targetModel.displayField) {
786
+ const displayField2 = relation.targetModel.fieldsByName[relation.targetModel.displayField];
469
787
  if (displayField2.orderable) {
470
- filters = `(orderBy: [{ ${model2.displayField}: ASC }])`;
788
+ filters = `(orderBy: [{ ${relation.targetModel.displayField}: ASC }])`;
471
789
  }
472
790
  }
473
- return `${name2}: ${getModelPluralField(model2)}${filters} {
791
+ return `${relation.name}: ${relation.targetModel.pluralField}${filters} {
474
792
  id
475
- display: ${model2.displayField || "id"}
476
- ${additionalFields[name2] || ""}
793
+ display: ${relation.targetModel.displayField || "id"}
794
+ ${additionalFields[relation.name] || ""}
477
795
  }`;
478
796
  }).join(" ")}
479
797
  }`;
480
798
  };
481
- var getManyToManyRelations = (model, fields2, ignoreFields) => {
482
- const manyToManyRelations = [];
483
- for (const field of model.reverseRelations) {
484
- if (fields2 && !fields2.includes(field.name) || ignoreFields && ignoreFields.includes(field.name)) {
485
- continue;
486
- }
487
- const relation = field.model.relations.find(
488
- (relation2) => !relation2.field.generated && relation2.field.name !== field.field.name
489
- );
490
- if (!relation) {
491
- continue;
492
- }
493
- const inapplicableFields = field.model.fields.filter(
494
- (otherField) => !otherField.generated && ![field.field.name, relation.field.name].includes(otherField.name)
495
- );
496
- if (inapplicableFields.length) {
497
- continue;
498
- }
499
- manyToManyRelations.push([field, relation]);
500
- }
501
- return manyToManyRelations;
502
- };
503
- var getManyToManyRelation = (model, name2) => getManyToManyRelations(model, [name2])[0];
504
799
  var getManyToManyRelationsQuery = (model, action, manyToManyRelations) => !!manyToManyRelations.length && (action === "update" ? `query Update${model.name}ManyToManyRelations($id: ID!) {
505
800
  ${typeToField(model.name)}(where: { id: $id }) {
506
- ${manyToManyRelations.map(([reverseRelation, { field }]) => {
507
- return `${reverseRelation.name} {
801
+ ${manyToManyRelations.map((relation) => {
802
+ return `${relation.name} {
508
803
  id
509
- ${field.name} {
804
+ ${relation.relationToTarget.name} {
510
805
  id
511
806
  }
512
807
  }`;
513
808
  }).join(" ")}
514
809
  }
515
- ${manyToManyRelations.map(([reverseRelation, { model: model2 }]) => {
516
- return `${reverseRelation.name}: ${getModelPluralField(model2)} {
810
+ ${manyToManyRelations.map((relation) => {
811
+ return `${relation.name}: ${relation.targetModel.pluralField} {
517
812
  id
518
- ${model2.displayField || ""}
813
+ ${relation.targetModel.displayField || ""}
519
814
  }`;
520
815
  }).join(" ")}
521
816
  }` : `query Create${model.name}ManyToManyRelations {
522
- ${manyToManyRelations.map(([reverseRelation, { model: model2 }]) => {
523
- return `${reverseRelation.name}: ${getModelPluralField(model2)} {
817
+ ${manyToManyRelations.map((relation) => {
818
+ return `${relation.name}: ${relation.targetModel.pluralField} {
524
819
  id
525
- ${model2.displayField || ""}
820
+ ${relation.targetModel.displayField || ""}
526
821
  }`;
527
822
  }).join(" ")}
528
823
  }`);
@@ -548,14 +843,14 @@ var getMutationQuery = (model, action) => action === "create" ? `
548
843
  var displayField = (model) => `
549
844
  ${model.displayField ? `display: ${model.displayField}` : ""}
550
845
  `;
551
- var getEntityListQuery = (model, role, additionalFields = "", root) => `query ${getModelPlural(model)}List(
846
+ var getEntityListQuery = (model, role, additionalFields = "", root) => `query ${model.plural}List(
552
847
  ${root ? "$id: ID!," : ""}
553
848
  $limit: Int!,
554
849
  $where: ${model.name}Where!,
555
850
  ${model.fields.some(({ searchable }) => searchable) ? "$search: String," : ""}
556
851
  ) {
557
852
  ${root ? `root: ${typeToField(root.model.name)}(where: { id: $id }) {` : ""}
558
- data: ${root ? root.reverseRelationName : getModelPluralField(model)}(limit: $limit, where: $where, ${model.fields.some(({ searchable }) => searchable) ? ", search: $search" : ""}) {
853
+ data: ${root ? root.reverseRelationName : model.pluralField}(limit: $limit, where: $where, ${model.fields.some(({ searchable }) => searchable) ? ", search: $search" : ""}) {
559
854
  ${displayField(model)}
560
855
  ${model.fields.filter(and(isSimpleField, isQueriableBy(role))).map(({ name: name2 }) => name2)}
561
856
  ${additionalFields}
@@ -564,37 +859,38 @@ var getEntityListQuery = (model, role, additionalFields = "", root) => `query ${
564
859
  }`;
565
860
  var isVisibleRelation = (visibleRelationsByRole, modelName, role) => {
566
861
  const whitelist = visibleRelationsByRole[role]?.[modelName];
567
- return ({ name: name2 }) => whitelist ? whitelist.includes(name2) : true;
862
+ return (relation) => whitelist ? whitelist.includes(relation.name) : true;
568
863
  };
569
- var getEntityQuery = (models, model, role, visibleRelationsByRole, typesWithSubRelations) => `query Admin${model.name} ($id: ID!) {
864
+ var getEntityQuery = (model, role, visibleRelationsByRole, typesWithSubRelations) => `query Get${model.name}Entity ($id: ID!) {
570
865
  data: ${typeToField(model.name)}(where: { id: $id }) {
571
866
  ${displayField(model)}
572
867
  ${model.fields.filter(and(isSimpleField, isQueriableBy(role))).map(({ name: name2 }) => name2)}
573
868
  ${queryRelations(
574
- models,
575
- model.fields.filter(isRelation).filter(isVisibleRelation(visibleRelationsByRole, model.name, role)),
869
+ model.models,
870
+ model.relations.filter(isVisibleRelation(visibleRelationsByRole, model.name, role)),
576
871
  role,
577
872
  typesWithSubRelations
578
873
  )}
579
874
  ${queryRelations(
580
- models,
581
- model.reverseRelations.filter(and(isToOneRelation, isVisibleRelation(visibleRelationsByRole, model.name, role))),
875
+ model.models,
876
+ model.reverseRelations.filter(
877
+ (reverseRelation) => isToOneRelation(reverseRelation.field) && isVisibleRelation(visibleRelationsByRole, model.name, role)(reverseRelation)
878
+ ),
582
879
  role,
583
880
  typesWithSubRelations
584
881
  )}
585
882
  }
586
883
  }`;
587
884
  var getFindEntityQuery = (model, role) => `query Find${model.name}($where: ${model.name}Where!, $orderBy: [${model.name}OrderBy!]) {
588
- data: ${getModelPluralField(model)}(limit: 1, where: $where, orderBy: $orderBy) {
885
+ data: ${model.pluralField}(limit: 1, where: $where, orderBy: $orderBy) {
589
886
  ${model.fields.filter(and(isSimpleField, isQueriableBy(role))).map(({ name: name2 }) => name2)}
590
887
  }
591
888
  }`;
592
- var queryRelations = (models, relations, role, typesWithSubRelations) => relations.map(({ name: name2, type }) => {
593
- const relatedModel = summonByName(models, type);
594
- const subRelations = typesWithSubRelations.includes(type) ? relatedModel.fields.filter(isRelation) : [];
595
- return `${name2} {
889
+ var queryRelations = (models, relations, role, typesWithSubRelations) => relations.map((relation) => {
890
+ const subRelations = typesWithSubRelations.includes(relation.targetModel.name) ? relation.targetModel.relations : [];
891
+ return `${relation.name} {
596
892
  id
597
- ${displayField(relatedModel)}
893
+ ${displayField(relation.targetModel)}
598
894
  ${subRelations.length > 0 ? queryRelations(models, subRelations, role, typesWithSubRelations) : ""}
599
895
  }`;
600
896
  }).join("\n");
@@ -611,56 +907,59 @@ var PRIMITIVE_TYPES = {
611
907
  DateTime: "DateTime | string"
612
908
  };
613
909
  var OPTIONAL_SEED_FIELDS = ["createdAt", "createdById", "updatedAt", "updatedById", "deletedAt", "deletedById"];
614
- var generateDBModels = (rawModels) => {
910
+ var generateDBModels = (models) => {
615
911
  const writer = new import_code_block_writer.default["default"]({
616
912
  useSingleQuote: true,
617
913
  indentNumberOfSpaces: 2
618
914
  });
619
915
  writer.write(`import { DateTime } from 'luxon';`).blankLine();
620
- for (const enm2 of rawModels.filter(isEnumModel)) {
916
+ for (const enm2 of models.enums) {
621
917
  writer.write(`export type ${enm2.name} = ${enm2.values.map((v) => `'${v}'`).join(" | ")};`).blankLine();
622
918
  }
623
- const models = getModels(rawModels);
624
- for (const model of models) {
625
- const fields2 = model.fields.some((field) => field.kind === "relation" && field.foreignKey === "id") ? model.fields.filter((field) => field.name !== "id") : model.fields;
919
+ for (const model of models.entities) {
920
+ const fields2 = model.relations.some((relation) => relation.field.foreignKey === "id") ? model.fields.filter((field) => field.name !== "id") : model.fields;
626
921
  writer.write(`export type ${model.name} = `).inlineBlock(() => {
627
922
  for (const field of fields2.filter(not(isCustomField))) {
628
- writer.write(`'${getFieldName(field)}': ${getFieldType(field)}${field.nonNull ? "" : " | null"},`).newLine();
923
+ writer.write(`'${getColumnName(field)}': ${getFieldType(field)}${field.nonNull ? "" : " | null"};`).newLine();
629
924
  }
630
925
  }).blankLine();
631
926
  writer.write(`export type ${model.name}Initializer = `).inlineBlock(() => {
632
- for (const field of fields2.filter(not(isCustomField))) {
927
+ for (const field of fields2.filter(not(isCustomField)).filter(isInTable)) {
633
928
  writer.write(
634
- `'${getFieldName(field)}'${field.nonNull && field.defaultValue === void 0 ? "" : "?"}: ${getFieldType(
929
+ `'${getColumnName(field)}'${field.nonNull && field.defaultValue === void 0 ? "" : "?"}: ${getFieldType(
635
930
  field
636
- )}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"},`
931
+ )}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"};`
637
932
  ).newLine();
638
933
  }
639
934
  }).blankLine();
640
935
  writer.write(`export type ${model.name}Mutator = `).inlineBlock(() => {
641
- for (const field of fields2.filter(not(isCustomField))) {
642
- writer.write(
643
- `'${getFieldName(field)}'?: ${getFieldType(field)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"},`
644
- ).newLine();
645
- }
646
- }).blankLine();
647
- writer.write(`export type ${model.name}Seed = `).inlineBlock(() => {
648
- for (const field of fields2.filter(not(isCustomField))) {
649
- const fieldName = getFieldName(field);
936
+ for (const field of fields2.filter(not(isCustomField)).filter(isInTable)) {
650
937
  writer.write(
651
- `'${getFieldName(field)}'${field.nonNull && field.defaultValue === void 0 && !OPTIONAL_SEED_FIELDS.includes(fieldName) ? "" : "?"}: ${field.kind === "enum" ? field.list ? "string[]" : "string" : getFieldType(field)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"},`
938
+ `'${getColumnName(field)}'?: ${getFieldType(field)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"};`
652
939
  ).newLine();
653
940
  }
654
941
  }).blankLine();
942
+ if (!isRootModel(model)) {
943
+ writer.write(`export type ${model.name}Seed = `).inlineBlock(() => {
944
+ for (const field of fields2.filter(not(isCustomField))) {
945
+ if (model.parent && field.name === "type") {
946
+ continue;
947
+ }
948
+ const fieldName = getColumnName(field);
949
+ writer.write(
950
+ `'${getColumnName(field)}'${field.nonNull && field.defaultValue === void 0 && !OPTIONAL_SEED_FIELDS.includes(fieldName) ? "" : "?"}: ${field.kind === "enum" ? field.list ? "string[]" : "string" : getFieldType(field)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"};`
951
+ ).newLine();
952
+ }
953
+ }).blankLine();
954
+ }
655
955
  }
656
956
  writer.write(`export type SeedData = `).inlineBlock(() => {
657
- for (const model of models) {
658
- writer.write(`${model.name}: ${model.name}Seed[],`).newLine();
957
+ for (const model of models.entities.filter(not(isRootModel))) {
958
+ writer.write(`${model.name}: ${model.name}Seed[];`).newLine();
659
959
  }
660
960
  });
661
961
  return writer.toString();
662
962
  };
663
- var getFieldName = (field) => field.kind === "relation" ? field.foreignKey || `${field.name}Id` : field.name;
664
963
  var getFieldType = (field) => {
665
964
  const kind = field.kind;
666
965
  switch (kind) {
@@ -681,19 +980,18 @@ var getFieldType = (field) => {
681
980
  }
682
981
  }
683
982
  };
684
- var generateKnexTables = (rawModels) => {
983
+ var generateKnexTables = (models) => {
685
984
  const writer = new import_code_block_writer.default["default"]({
686
985
  useSingleQuote: true,
687
986
  indentNumberOfSpaces: 2
688
987
  });
689
- const models = getModels(rawModels);
690
988
  writer.write(`import { Knex } from 'knex';`).newLine();
691
989
  writer.write(
692
- `import { ${models.map((model) => `${model.name}, ${model.name}Initializer, ${model.name}Mutator`).join(", ")} } from '.';`
990
+ `import { ${models.entities.map((model) => `${model.name}, ${model.name}Initializer, ${model.name}Mutator`).join(", ")} } from '.';`
693
991
  ).blankLine();
694
992
  writer.write(`declare module 'knex/types/tables' `).inlineBlock(() => {
695
993
  writer.write(`interface Tables `).inlineBlock(() => {
696
- for (const model of models) {
994
+ for (const model of models.entities) {
697
995
  writer.write(`'${model.name}': Knex.CompositeTableType<${model.name}, ${model.name}Initializer, ${model.name}Mutator>,`).newLine();
698
996
  }
699
997
  });
@@ -706,10 +1004,9 @@ var import_code_block_writer2 = __toESM(require("code-block-writer"), 1);
706
1004
  var import_knex_schema_inspector = require("knex-schema-inspector");
707
1005
  var import_lowerFirst = __toESM(require("lodash/lowerFirst"), 1);
708
1006
  var MigrationGenerator = class {
709
- constructor(knex, rawModels) {
710
- this.rawModels = rawModels;
1007
+ constructor(knex, models) {
1008
+ this.models = models;
711
1009
  this.schema = (0, import_knex_schema_inspector.SchemaInspector)(knex);
712
- this.models = getModels(rawModels);
713
1010
  }
714
1011
  writer = new import_code_block_writer2.default["default"]({
715
1012
  useSingleQuote: true,
@@ -719,9 +1016,8 @@ var MigrationGenerator = class {
719
1016
  columns = {};
720
1017
  uuidUsed;
721
1018
  nowUsed;
722
- models;
723
1019
  async generate() {
724
- const { writer, schema, rawModels, models } = this;
1020
+ const { writer, schema, models } = this;
725
1021
  const enums = (await schema.knex("pg_type").where({ typtype: "e" }).select("typname")).map(({ typname }) => typname);
726
1022
  const tables = await schema.tables();
727
1023
  for (const table of tables) {
@@ -730,11 +1026,11 @@ var MigrationGenerator = class {
730
1026
  const up = [];
731
1027
  const down = [];
732
1028
  this.createEnums(
733
- rawModels.filter(isEnumModel).filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
1029
+ this.models.enums.filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
734
1030
  up,
735
1031
  down
736
1032
  );
737
- for (const model of models) {
1033
+ for (const model of models.entities) {
738
1034
  if (model.deleted) {
739
1035
  up.push(() => {
740
1036
  this.dropTable(model.name);
@@ -746,7 +1042,7 @@ var MigrationGenerator = class {
746
1042
  }
747
1043
  });
748
1044
  });
749
- if (model.updatable) {
1045
+ if (isUpdatableModel(model)) {
750
1046
  up.push(() => {
751
1047
  this.dropTable(`${model.name}Revision`);
752
1048
  });
@@ -765,7 +1061,7 @@ var MigrationGenerator = class {
765
1061
  tables[tables.indexOf(model.oldName)] = model.name;
766
1062
  this.columns[model.name] = this.columns[model.oldName];
767
1063
  delete this.columns[model.oldName];
768
- if (model.updatable) {
1064
+ if (isUpdatableModel(model)) {
769
1065
  up.push(() => {
770
1066
  this.renameTable(`${model.oldName}Revision`, `${model.name}Revision`);
771
1067
  this.alterTable(`${model.name}Revision`, () => {
@@ -783,92 +1079,102 @@ var MigrationGenerator = class {
783
1079
  delete this.columns[`${model.oldName}Revision`];
784
1080
  }
785
1081
  }
786
- if (!tables.includes(model.name)) {
787
- up.push(() => {
788
- this.createTable(model.name, () => {
789
- for (const field of model.fields) {
790
- this.column(field);
791
- }
792
- });
793
- });
794
- down.push(() => {
795
- this.dropTable(model.name);
796
- });
797
- } else {
798
- this.renameFields(
799
- model,
800
- model.fields.filter(({ oldName }) => oldName),
801
- up,
802
- down
803
- );
804
- this.createFields(
805
- model,
806
- model.fields.filter(
807
- ({ name: name2, ...field }) => field.kind !== "custom" && !this.columns[model.name].some(
808
- (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
809
- )
810
- ),
811
- up,
812
- down
813
- );
814
- const existingFields = model.fields.filter(({ name: name2, kind, nonNull: nonNull2 }) => {
815
- const col = this.columns[model.name].find((col2) => col2.name === (kind === "relation" ? `${name2}Id` : name2));
816
- if (!col) {
817
- return false;
818
- }
819
- return !nonNull2 && !col.is_nullable;
820
- });
821
- this.updateFields(model, existingFields, up, down);
822
- }
823
- if (model.updatable) {
824
- if (!tables.includes(`${model.name}Revision`)) {
1082
+ if (modelNeedsTable(model)) {
1083
+ if (!tables.includes(model.name)) {
825
1084
  up.push(() => {
826
- this.createRevisionTable(model);
827
- });
828
- if (tables.includes(model.name)) {
829
- up.push(() => {
830
- writer.block(() => {
831
- writer.writeLine(`const data = await knex('${model.name}');`);
832
- writer.write(`if (data.length)`).block(() => {
833
- writer.write(`await knex.batchInsert('${model.name}Revision', data.map((row) => (`).inlineBlock(() => {
834
- writer.writeLine(`id: uuid(),`);
835
- writer.writeLine(`${typeToField(model.name)}Id: row.id,`);
836
- this.nowUsed = true;
837
- writer.writeLine(`createdAt: row.updatedAt || row.createdAt || now,`);
838
- writer.writeLine(`createdById: row.updatedById || row.createdById,`);
839
- if (model.deletable) {
840
- writer.writeLine(`deleted: row.deleted,`);
841
- }
842
- for (const { name: name2, kind } of model.fields.filter(({ updatable }) => updatable)) {
843
- const col = kind === "relation" ? `${name2}Id` : name2;
844
- writer.writeLine(`${col}: row.${col},`);
845
- }
846
- }).write(")));").newLine();
1085
+ this.createTable(model.name, () => {
1086
+ if (model.parent) {
1087
+ this.column({
1088
+ ...model.fieldsByName.id,
1089
+ kind: "relation",
1090
+ type: model.parent,
1091
+ foreignKey: "id"
847
1092
  });
848
- }).blankLine();
1093
+ }
1094
+ for (const field of model.fields.filter(not(isInherited))) {
1095
+ this.column(field);
1096
+ }
849
1097
  });
850
- }
1098
+ });
851
1099
  down.push(() => {
852
- this.dropTable(`${model.name}Revision`);
1100
+ this.dropTable(model.name);
853
1101
  });
854
1102
  } else {
855
- const revisionTable = `${model.name}Revision`;
856
- const missingRevisionFields = model.fields.filter(
857
- ({ name: name2, updatable, ...field }) => field.kind !== "custom" && updatable && !this.columns[revisionTable].some(
858
- (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
859
- )
1103
+ this.renameFields(
1104
+ model,
1105
+ model.fields.filter(not(isInherited)).filter(({ oldName }) => oldName),
1106
+ up,
1107
+ down
860
1108
  );
861
- this.createRevisionFields(model, missingRevisionFields, up, down);
862
- const revisionFieldsToRemove = model.fields.filter(
863
- ({ name: name2, updatable, generated, ...field }) => !generated && field.kind !== "custom" && !updatable && !(field.kind === "relation" && field.foreignKey === "id") && this.columns[revisionTable].some(
864
- (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
865
- )
1109
+ this.createFields(
1110
+ model,
1111
+ model.fields.filter(not(isInherited)).filter(
1112
+ ({ name: name2, ...field }) => field.kind !== "custom" && !this.columns[model.name].some(
1113
+ (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
1114
+ )
1115
+ ),
1116
+ up,
1117
+ down
866
1118
  );
867
- this.createRevisionFields(model, revisionFieldsToRemove, down, up);
1119
+ const existingFields = model.fields.filter(({ name: name2, kind, nonNull: nonNull2 }) => {
1120
+ const col = this.columns[model.name].find((col2) => col2.name === (kind === "relation" ? `${name2}Id` : name2));
1121
+ if (!col) {
1122
+ return false;
1123
+ }
1124
+ return !nonNull2 && !col.is_nullable;
1125
+ });
1126
+ this.updateFields(model, existingFields, up, down);
1127
+ }
1128
+ if (isUpdatableModel(model)) {
1129
+ if (!tables.includes(`${model.name}Revision`)) {
1130
+ up.push(() => {
1131
+ this.createRevisionTable(model);
1132
+ });
1133
+ if (tables.includes(model.name)) {
1134
+ up.push(() => {
1135
+ writer.block(() => {
1136
+ writer.writeLine(`const data = await knex('${model.name}');`);
1137
+ writer.write(`if (data.length)`).block(() => {
1138
+ writer.write(`await knex.batchInsert('${model.name}Revision', data.map((row) => (`).inlineBlock(() => {
1139
+ writer.writeLine(`id: uuid(),`);
1140
+ writer.writeLine(`${typeToField(model.name)}Id: row.id,`);
1141
+ this.nowUsed = true;
1142
+ writer.writeLine(`createdAt: row.updatedAt || row.createdAt || now,`);
1143
+ writer.writeLine(`createdById: row.updatedById || row.createdById,`);
1144
+ if (model.deletable) {
1145
+ writer.writeLine(`deleted: row.deleted,`);
1146
+ }
1147
+ for (const { name: name2, kind } of model.fields.filter(isUpdatableField)) {
1148
+ const col = kind === "relation" ? `${name2}Id` : name2;
1149
+ writer.writeLine(`${col}: row.${col},`);
1150
+ }
1151
+ }).write(")));").newLine();
1152
+ });
1153
+ }).blankLine();
1154
+ });
1155
+ }
1156
+ down.push(() => {
1157
+ this.dropTable(`${model.name}Revision`);
1158
+ });
1159
+ } else {
1160
+ const revisionTable = `${model.name}Revision`;
1161
+ const missingRevisionFields = model.fields.filter(isUpdatableField).filter(
1162
+ ({ name: name2, ...field }) => field.kind !== "custom" && !this.columns[revisionTable].some(
1163
+ (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
1164
+ )
1165
+ );
1166
+ this.createRevisionFields(model, missingRevisionFields, up, down);
1167
+ const revisionFieldsToRemove = model.fields.filter(
1168
+ ({ name: name2, updatable, generated, ...field }) => !generated && field.kind !== "custom" && !updatable && !(field.kind === "relation" && field.foreignKey === "id") && this.columns[revisionTable].some(
1169
+ (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
1170
+ )
1171
+ );
1172
+ this.createRevisionFields(model, revisionFieldsToRemove, down, up);
1173
+ }
868
1174
  }
869
1175
  }
870
1176
  }
871
- for (const model of getModels(rawModels)) {
1177
+ for (const model of models.entities) {
872
1178
  if (tables.includes(model.name)) {
873
1179
  this.createFields(
874
1180
  model,
@@ -876,10 +1182,10 @@ var MigrationGenerator = class {
876
1182
  down,
877
1183
  up
878
1184
  );
879
- if (model.updatable) {
1185
+ if (isUpdatableModel(model)) {
880
1186
  this.createRevisionFields(
881
1187
  model,
882
- model.fields.filter(({ deleted, updatable }) => updatable && deleted),
1188
+ model.fields.filter(isUpdatableField).filter(({ deleted }) => deleted),
883
1189
  down,
884
1190
  up
885
1191
  );
@@ -887,7 +1193,7 @@ var MigrationGenerator = class {
887
1193
  }
888
1194
  }
889
1195
  this.createEnums(
890
- rawModels.filter(isEnumModel).filter((enm2) => enm2.deleted),
1196
+ this.models.enums.filter((enm2) => enm2.deleted),
891
1197
  down,
892
1198
  up
893
1199
  );
@@ -995,8 +1301,8 @@ var MigrationGenerator = class {
995
1301
  }
996
1302
  });
997
1303
  });
998
- if (model.updatable) {
999
- const updatableFields = fields2.filter(({ updatable }) => updatable);
1304
+ if (isUpdatableModel(model)) {
1305
+ const updatableFields = fields2.filter(isUpdatableField);
1000
1306
  if (!updatableFields.length) {
1001
1307
  return;
1002
1308
  }
@@ -1024,15 +1330,15 @@ var MigrationGenerator = class {
1024
1330
  const writer = this.writer;
1025
1331
  this.createTable(`${model.name}Revision`, () => {
1026
1332
  writer.writeLine(`table.uuid('id').notNullable().primary();`);
1027
- writer.writeLine(`table.uuid('${typeToField(model.name)}Id').notNullable();`);
1028
- writer.write(`table.uuid('createdById')`);
1029
- writer.write(".notNullable()");
1030
- writer.write(";").newLine();
1031
- writer.writeLine(`table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now(0));`);
1032
- if (model.deletable) {
1033
- writer.writeLine(`table.boolean('deleted').notNullable();`);
1333
+ if (!model.parent) {
1334
+ writer.writeLine(`table.uuid('${typeToField(model.name)}Id').notNullable();`);
1335
+ writer.writeLine(`table.uuid('createdById').notNullable();`);
1336
+ writer.writeLine(`table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now(0));`);
1337
+ if (model.deletable) {
1338
+ writer.writeLine(`table.boolean('deleted').notNullable();`);
1339
+ }
1034
1340
  }
1035
- for (const field of model.fields.filter((field2) => field2.updatable)) {
1341
+ for (const field of model.fields.filter(and(isUpdatableField, not(isInherited)))) {
1036
1342
  this.column(field, { setUnique: false, setDefault: false });
1037
1343
  }
1038
1344
  });
@@ -1080,7 +1386,7 @@ var MigrationGenerator = class {
1080
1386
  `await knex.raw(\`CREATE TYPE "${name2}" AS ENUM (${enm2.values.map((value2) => `'${value2}'`).join(",")})\`);`
1081
1387
  ).newLine()
1082
1388
  );
1083
- down.push(() => this.writer.writeLine(`await knex.raw('DROP TYPE "${name2}"')`));
1389
+ down.push(() => this.writer.writeLine(`await knex.raw('DROP TYPE "${name2}"');`));
1084
1390
  }
1085
1391
  }
1086
1392
  migration(name2, cbs) {
@@ -1160,7 +1466,7 @@ var MigrationGenerator = class {
1160
1466
  if (field.double) {
1161
1467
  col(`table.double('${name2}')`);
1162
1468
  } else {
1163
- col(`table.decimal('${name2}', ${get(field, "precision")}, ${get(field, "scale")})`);
1469
+ col(`table.decimal('${name2}', ${field.precision ?? "undefined"}, ${field.scale ?? "undefined"})`);
1164
1470
  }
1165
1471
  break;
1166
1472
  case "String":
@@ -1181,14 +1487,16 @@ var MigrationGenerator = class {
1181
1487
  }
1182
1488
  break;
1183
1489
  case "relation":
1184
- col(`table.uuid('${name2}Id')`);
1490
+ col(`table.uuid('${field.foreignKey}')`);
1185
1491
  if (foreign && !alter) {
1186
- this.writer.writeLine(`table.foreign('${name2}Id').references('id').inTable('${field.type}');`);
1492
+ this.writer.writeLine(
1493
+ `table.foreign('${field.foreignKey}').references('id').inTable('${field.type}').onDelete('CASCADE');`
1494
+ );
1187
1495
  }
1188
1496
  break;
1189
1497
  case "enum":
1190
1498
  if (list2) {
1191
- this.writer.write(`table.specificType('${name2}', '"${typeToField(field.type)}"[]');`);
1499
+ this.writer.write(`table.specificType('${name2}', '"${typeToField(field.type)}"[]')`);
1192
1500
  } else {
1193
1501
  this.writer.write(`table.enum('${name2}', null as any, `).inlineBlock(() => {
1194
1502
  this.writer.writeLine(`useNative: true,`);
@@ -1244,15 +1552,17 @@ var UserInputError = class extends GraphQLError {
1244
1552
  }
1245
1553
  };
1246
1554
  var PermissionError = class extends ForbiddenError {
1247
- constructor(action, what, why) {
1248
- super(`You do not have sufficient permissions to ${action.toLowerCase()} ${what} (${why}).`);
1555
+ constructor(role, action, what, why) {
1556
+ super(`Role ${role} does not have sufficient permissions to ${action.toLowerCase()} ${what} (${why}).`);
1249
1557
  }
1250
1558
  };
1251
1559
 
1252
1560
  // src/resolvers/utils.ts
1253
1561
  var import_crypto = require("crypto");
1254
1562
  var import_graphql3 = require("graphql");
1563
+ var import_lodash4 = require("lodash");
1255
1564
  var ID_ALIAS = "ID";
1565
+ var TYPE_ALIAS = "TYPE";
1256
1566
  var getTypeName = (t) => {
1257
1567
  switch (t.kind) {
1258
1568
  case "ListType":
@@ -1278,18 +1588,16 @@ var isFragmentSpreadNode = (n) => n.kind === import_graphql3.Kind.FRAGMENT_SPREA
1278
1588
  var getType = (schema, name2) => get(schema.getType(name2), "astNode");
1279
1589
  var getFragmentTypeName = (node) => get(get(node.typeCondition, "name"), "value");
1280
1590
  function hydrate(node, raw) {
1281
- const tableAlias = node.tableAlias;
1591
+ const resultAlias = node.resultAlias;
1282
1592
  const res = raw.map((entry) => {
1283
1593
  const res2 = {};
1284
1594
  outer:
1285
1595
  for (const [column, value2] of Object.entries(entry)) {
1286
1596
  let current = res2;
1287
- const shortParts = column.split("__");
1288
- const fieldName = shortParts.pop();
1289
- const columnWithoutField = shortParts.join("__");
1597
+ const [, columnWithoutField, fieldName] = column.match(/^(.*\w)__(\w+)$/);
1290
1598
  const longColumn = node.ctx.aliases.getLong(columnWithoutField);
1291
- const longColumnWithoutRoot = longColumn.replace(new RegExp(`^${tableAlias}(__)?`), "");
1292
- const allParts = [tableAlias, ...longColumnWithoutRoot ? longColumnWithoutRoot.split("__") : [], fieldName];
1599
+ const longColumnWithoutRoot = longColumn.replace(new RegExp(`^${resultAlias}(__)?`), "");
1600
+ const allParts = [resultAlias, ...longColumnWithoutRoot ? longColumnWithoutRoot.split("__") : [], fieldName];
1293
1601
  for (let i = 0; i < allParts.length - 1; i++) {
1294
1602
  const part = allParts[i];
1295
1603
  if (!current[part]) {
@@ -1303,7 +1611,7 @@ function hydrate(node, raw) {
1303
1611
  }
1304
1612
  current[it(fieldName)] = value2;
1305
1613
  }
1306
- return res2[tableAlias];
1614
+ return res2[resultAlias];
1307
1615
  });
1308
1616
  return res;
1309
1617
  }
@@ -1312,12 +1620,12 @@ var ors = (query, [first, ...rest]) => {
1312
1620
  return query;
1313
1621
  }
1314
1622
  return query.where((subQuery) => {
1315
- subQuery.where((subSubQuery) => {
1316
- first(subSubQuery);
1623
+ void subQuery.where((subSubQuery) => {
1624
+ void first(subSubQuery);
1317
1625
  });
1318
1626
  for (const cb of rest) {
1319
- subQuery.orWhere((subSubQuery) => {
1320
- cb(subSubQuery);
1627
+ void subQuery.orWhere((subSubQuery) => {
1628
+ void cb(subSubQuery);
1321
1629
  });
1322
1630
  }
1323
1631
  });
@@ -1331,15 +1639,26 @@ var getNameOrAlias = (node) => {
1331
1639
  };
1332
1640
  var apply = (target, ops) => ops.reduce((target2, op) => op(target2), target);
1333
1641
  var applyJoins = (aliases, query, joins) => {
1334
- for (const [tableName, { table1Alias, column1, column2 }] of Object.entries(joins)) {
1335
- const [table, alias] = tableName.split(":");
1642
+ for (const { table1Alias, table2Name, table2Alias, column1, column2 } of joins) {
1336
1643
  const table1ShortAlias = aliases.getShort(table1Alias);
1337
- const table2ShortAlias = aliases.getShort(alias);
1338
- query.leftJoin(`${table} as ${table2ShortAlias}`, `${table1ShortAlias}.${column1}`, `${table2ShortAlias}.${column2}`);
1644
+ const table2ShortAlias = aliases.getShort(table2Alias);
1645
+ void query.leftJoin(
1646
+ `${table2Name} as ${table2ShortAlias}`,
1647
+ `${table1ShortAlias}.${column1}`,
1648
+ `${table2ShortAlias}.${column2}`
1649
+ );
1339
1650
  }
1340
1651
  };
1341
- var addJoin = (joins, table1Alias, table2, alias2, column1, column2) => {
1342
- joins[`${table2}:${alias2}`] ||= { table1Alias, column1, column2 };
1652
+ var addJoin = (joins, table1Alias, table2Name, table2Alias, column1, column2) => {
1653
+ const join = { table1Alias, table2Name, table2Alias, column1, column2 };
1654
+ const existingJoin = joins.find((j) => j.table2Alias === join.table2Alias);
1655
+ if (existingJoin) {
1656
+ if (!(0, import_lodash4.isEqual)(existingJoin, join)) {
1657
+ throw new Error(`Join collision: ${existingJoin}, ${join}`);
1658
+ }
1659
+ } else {
1660
+ joins.push(join);
1661
+ }
1343
1662
  };
1344
1663
  var AliasGenerator = class {
1345
1664
  reverse = {};
@@ -1362,6 +1681,11 @@ var AliasGenerator = class {
1362
1681
  }
1363
1682
  };
1364
1683
  var hash = (s) => (0, import_crypto.createHash)("md5").update(JSON.stringify(s)).digest("hex");
1684
+ var getColumnName = (field) => field.kind === "relation" ? field.foreignKey || `${field.name}Id` : field.name;
1685
+ var getColumn = (node, key) => {
1686
+ const field = node.model.fields.find((field2) => getColumnName(field2) === key);
1687
+ return `${node.ctx.aliases.getShort(field.inherited ? node.rootTableAlias : node.tableAlias)}.${key}`;
1688
+ };
1365
1689
 
1366
1690
  // src/permissions/check.ts
1367
1691
  var getPermissionStack = (ctx, type, action) => {
@@ -1406,7 +1730,7 @@ var applyPermissions = (ctx, type, tableAlias, query, action, verifiedPermission
1406
1730
  return permissionStack;
1407
1731
  };
1408
1732
  var getEntityToMutate = async (ctx, model, where, action) => {
1409
- const query = ctx.knex(model.name).where(where).first();
1733
+ const query = ctx.knex(model.parent || model.name).where(where).first();
1410
1734
  let entity = await query.clone();
1411
1735
  if (!entity) {
1412
1736
  console.error(
@@ -1420,7 +1744,11 @@ var getEntityToMutate = async (ctx, model, where, action) => {
1420
1744
  console.error(
1421
1745
  `Permission error: ${Object.entries(where).map(([key, value2]) => `${key}: ${value2}`).join(", ")}`
1422
1746
  );
1423
- throw new PermissionError(action, `this ${model.name}`, "no available permissions applied");
1747
+ throw new PermissionError(ctx.user.role, action, `this ${model.name}`, "no available permissions applied");
1748
+ }
1749
+ if (model.parent) {
1750
+ const subEntity = await ctx.knex(model.name).where({ id: entity.id }).first();
1751
+ Object.assign(entity, subEntity);
1424
1752
  }
1425
1753
  return entity;
1426
1754
  };
@@ -1430,7 +1758,7 @@ var checkCanWrite = async (ctx, model, data, action) => {
1430
1758
  return;
1431
1759
  }
1432
1760
  if (permissionStack === false) {
1433
- throw new PermissionError(action, getModelPlural(model), "no applicable permissions");
1761
+ throw new PermissionError(ctx.user.role, action, model.plural, "no applicable permissions");
1434
1762
  }
1435
1763
  const query = ctx.knex.select(1).first();
1436
1764
  let linked = false;
@@ -1442,7 +1770,12 @@ var checkCanWrite = async (ctx, model, data, action) => {
1442
1770
  }
1443
1771
  const fieldPermissions = field[action === "CREATE" ? "creatable" : "updatable"];
1444
1772
  if (fieldPermissions && typeof fieldPermissions === "object" && !fieldPermissions.roles?.includes(ctx.user.role)) {
1445
- throw new PermissionError(action, `this ${model.name}'s ${field.name}`, "field permission not available");
1773
+ throw new PermissionError(
1774
+ ctx.user.role,
1775
+ action,
1776
+ `this ${model.name}'s ${field.name}`,
1777
+ "field permission not available"
1778
+ );
1446
1779
  }
1447
1780
  linked = true;
1448
1781
  const fieldPermissionStack = getPermissionStack(ctx, field.type, "LINK");
@@ -1451,7 +1784,12 @@ var checkCanWrite = async (ctx, model, data, action) => {
1451
1784
  continue;
1452
1785
  }
1453
1786
  if (fieldPermissionStack === false || !fieldPermissionStack.length) {
1454
- throw new PermissionError(action, `this ${model.name}'s ${field.name}`, "no applicable permissions on data to link");
1787
+ throw new PermissionError(
1788
+ ctx.user.role,
1789
+ action,
1790
+ `this ${model.name}'s ${field.name}`,
1791
+ "no applicable permissions on data to link"
1792
+ );
1455
1793
  }
1456
1794
  ors(
1457
1795
  query,
@@ -1463,10 +1801,10 @@ var checkCanWrite = async (ctx, model, data, action) => {
1463
1801
  if (linked) {
1464
1802
  const canMutate = await query;
1465
1803
  if (!canMutate) {
1466
- throw new PermissionError(action, `this ${model.name}`, "no linkable entities");
1804
+ throw new PermissionError(ctx.user.role, action, `this ${model.name}`, "no linkable entities");
1467
1805
  }
1468
1806
  } else if (action === "CREATE") {
1469
- throw new PermissionError(action, `this ${model.name}`, "no linkable entities");
1807
+ throw new PermissionError(ctx.user.role, action, `this ${model.name}`, "no linkable entities");
1470
1808
  }
1471
1809
  };
1472
1810
  var permissionLinkQuery = (ctx, subQuery, links, id) => {
@@ -1478,10 +1816,10 @@ var permissionLinkQuery = (ctx, subQuery, links, id) => {
1478
1816
  subQuery.where({ [`${alias}.id`]: ctx.user.id });
1479
1817
  }
1480
1818
  if (where) {
1481
- applyWhere(summonByName(ctx.models, type), subQuery, alias, where, aliases);
1819
+ applyWhere(ctx.models.getModel(type, "entity"), subQuery, alias, where, aliases);
1482
1820
  }
1483
1821
  for (const { type: type2, foreignKey, reverse, where: where2 } of links) {
1484
- const model = summonByName(ctx.models, type2);
1822
+ const model = ctx.models.getModel(type2, "entity");
1485
1823
  const subAlias = aliases.getShort();
1486
1824
  if (reverse) {
1487
1825
  subQuery.leftJoin(`${type2} as ${subAlias}`, `${alias}.${foreignKey || "id"}`, `${subAlias}.id`);
@@ -1502,11 +1840,11 @@ var applyWhere = (model, query, alias, where, aliases) => {
1502
1840
  if (relation) {
1503
1841
  const subAlias = aliases.getShort();
1504
1842
  query.leftJoin(
1505
- `${relation.model.name} as ${subAlias}`,
1843
+ `${relation.targetModel.name} as ${subAlias}`,
1506
1844
  `${alias}.${relation.field.foreignKey || `${relation.field.name}Id`}`,
1507
1845
  `${subAlias}.id`
1508
1846
  );
1509
- applyWhere(relation.model, query, subAlias, value2, aliases);
1847
+ applyWhere(relation.targetModel, query, subAlias, value2, aliases);
1510
1848
  } else if (Array.isArray(value2)) {
1511
1849
  query.whereIn(`${alias}.${key}`, value2);
1512
1850
  } else {
@@ -1554,7 +1892,7 @@ var generatePermissions = (models, config) => {
1554
1892
  };
1555
1893
  var addPermissions = (models, permissions, links, block) => {
1556
1894
  const { type } = links[links.length - 1];
1557
- const model = summonByName(models, type);
1895
+ const model = models.getModel(type, "entity");
1558
1896
  for (const action of ACTIONS) {
1559
1897
  if (action === "READ" || action in block) {
1560
1898
  if (!permissions[type]) {
@@ -1579,13 +1917,13 @@ var addPermissions = (models, permissions, links, block) => {
1579
1917
  reverse: true
1580
1918
  };
1581
1919
  } else {
1582
- const field2 = model.reverseRelationsByName[relation];
1583
- if (!field2) {
1920
+ const reverseRelation = model.reverseRelationsByName[relation];
1921
+ if (!reverseRelation) {
1584
1922
  throw new Error(`Relation ${relation} in model ${model.name} does not exist.`);
1585
1923
  }
1586
1924
  link = {
1587
- type: field2.model.name,
1588
- foreignKey: field2.foreignKey
1925
+ type: reverseRelation.targetModel.name,
1926
+ foreignKey: reverseRelation.field.foreignKey
1589
1927
  };
1590
1928
  }
1591
1929
  if (subBlock.WHERE) {
@@ -1730,13 +2068,18 @@ var applyFilters = (node, query, joins) => {
1730
2068
  if (orderBy) {
1731
2069
  applyOrderBy(node, orderBy, query);
1732
2070
  }
2071
+ if (node.model.parent) {
2072
+ void query.where({
2073
+ [getColumn(node, "type")]: node.model.name
2074
+ });
2075
+ }
1733
2076
  if (where) {
1734
2077
  const ops = [];
1735
2078
  applyWhere2(node, where, ops, joins);
1736
- apply(query, ops);
2079
+ void apply(query, ops);
1737
2080
  }
1738
2081
  if (search) {
1739
- applySearch(node, search, query);
2082
+ void applySearch(node, search, query);
1740
2083
  }
1741
2084
  };
1742
2085
  var applyWhere2 = (node, where, ops, joins) => {
@@ -1748,34 +2091,34 @@ var applyWhere2 = (node, where, ops, joins) => {
1748
2091
  if (!SPECIAL_FILTERS[filter]) {
1749
2092
  throw new Error(`Invalid filter ${key}.`);
1750
2093
  }
1751
- ops.push(
1752
- (query) => query.whereRaw(SPECIAL_FILTERS[filter], [`${node.shortTableAlias}.${actualKey}`, value2])
1753
- );
2094
+ ops.push((query) => query.whereRaw(SPECIAL_FILTERS[filter], [getColumn(node, actualKey), value2]));
1754
2095
  continue;
1755
2096
  }
1756
- const field = summonByName(node.model.fields, key);
1757
- const fullKey = `${node.shortTableAlias}.${key}`;
2097
+ const field = node.model.getField(key);
1758
2098
  if (field.kind === "relation") {
1759
- const relation = get(node.model.relationsByName, field.name);
1760
- const tableAlias = `${node.model.name}__W__${key}`;
2099
+ const relation = node.model.getRelation(field.name);
2100
+ const targetModel = relation.targetModel;
2101
+ const rootModel = targetModel.parentModel || targetModel;
2102
+ const rootTableAlias = `${node.model.name}__W__${key}`;
2103
+ const tableAlias = targetModel === rootModel ? rootTableAlias : `${node.model.name}__WS__${key}`;
1761
2104
  const subNode = {
1762
2105
  ctx: node.ctx,
1763
- model: relation.model,
1764
- tableName: relation.model.name,
1765
- tableAlias,
1766
- shortTableAlias: node.ctx.aliases.getShort(tableAlias),
1767
- foreignKey: relation.field.foreignKey
2106
+ rootModel,
2107
+ rootTableAlias,
2108
+ model: targetModel,
2109
+ tableAlias
1768
2110
  };
1769
- addJoin(joins, node.tableAlias, subNode.tableName, subNode.tableAlias, get(subNode, "foreignKey"), "id");
2111
+ addJoin(joins, node.tableAlias, subNode.model.name, subNode.tableAlias, relation.field.foreignKey, "id");
1770
2112
  applyWhere2(subNode, value2, ops, joins);
1771
2113
  continue;
1772
2114
  }
2115
+ const column = getColumn(node, key);
1773
2116
  if (Array.isArray(value2)) {
1774
2117
  if (field && field.list) {
1775
2118
  ops.push(
1776
2119
  (query) => ors(
1777
2120
  query,
1778
- value2.map((v) => (subQuery) => subQuery.whereRaw("? = ANY(??)", [v, fullKey]))
2121
+ value2.map((v) => (subQuery) => subQuery.whereRaw("? = ANY(??)", [v, column]))
1779
2122
  )
1780
2123
  );
1781
2124
  continue;
@@ -1784,25 +2127,25 @@ var applyWhere2 = (node, where, ops, joins) => {
1784
2127
  if (value2.some((v) => v !== null)) {
1785
2128
  ops.push(
1786
2129
  (query) => ors(query, [
1787
- (subQuery) => subQuery.whereIn(fullKey, value2.filter((v) => v !== null)),
1788
- (subQuery) => subQuery.whereNull(fullKey)
2130
+ (subQuery) => subQuery.whereIn(column, value2.filter((v) => v !== null)),
2131
+ (subQuery) => subQuery.whereNull(column)
1789
2132
  ])
1790
2133
  );
1791
2134
  continue;
1792
2135
  }
1793
- ops.push((query) => query.whereNull(fullKey));
2136
+ ops.push((query) => query.whereNull(column));
1794
2137
  continue;
1795
2138
  }
1796
- ops.push((query) => query.whereIn(fullKey, value2));
2139
+ ops.push((query) => query.whereIn(column, value2));
1797
2140
  continue;
1798
2141
  }
1799
- ops.push((query) => query.where({ [fullKey]: value2 }));
2142
+ ops.push((query) => query.where({ [column]: value2 }));
1800
2143
  }
1801
2144
  };
1802
2145
  var applySearch = (node, search, query) => ors(
1803
2146
  query,
1804
2147
  node.model.fields.filter(({ searchable }) => searchable).map(
1805
- ({ name: name2 }) => (query2) => query2.whereILike(`${node.shortTableAlias}.${name2}`, `%${search}%`)
2148
+ ({ name: name2 }) => (query2) => query2.whereILike(getColumn(node, name2), `%${search}%`)
1806
2149
  )
1807
2150
  );
1808
2151
  var applyOrderBy = (node, orderBy, query) => {
@@ -1813,7 +2156,7 @@ var applyOrderBy = (node, orderBy, query) => {
1813
2156
  }
1814
2157
  const key = keys[0];
1815
2158
  const value2 = vals[key];
1816
- query.orderBy(`${node.shortTableAlias}.${key}`, value2);
2159
+ void query.orderBy(getColumn(node, key), value2);
1817
2160
  }
1818
2161
  };
1819
2162
 
@@ -1829,19 +2172,26 @@ var getResolverNode = ({
1829
2172
  ctx,
1830
2173
  node,
1831
2174
  tableAlias,
2175
+ rootTableAlias,
2176
+ resultAlias,
1832
2177
  baseTypeDefinition,
1833
2178
  typeName
1834
- }) => ({
1835
- ctx,
1836
- tableName: typeName,
1837
- tableAlias,
1838
- shortTableAlias: ctx.aliases.getShort(tableAlias),
1839
- baseTypeDefinition,
1840
- baseModel: ctx.models.find((model) => model.name === baseTypeDefinition.name.value),
1841
- typeDefinition: getType(ctx.info.schema, typeName),
1842
- model: summonByName(ctx.models, typeName),
1843
- selectionSet: get(node.selectionSet, "selections")
1844
- });
2179
+ }) => {
2180
+ const model = ctx.models.getModel(typeName, "entity");
2181
+ const rootModel = model.parent ? ctx.models.getModel(model.parent, "entity") : model;
2182
+ return {
2183
+ ctx,
2184
+ rootModel,
2185
+ rootTableAlias,
2186
+ model,
2187
+ tableAlias,
2188
+ resultAlias,
2189
+ baseTypeDefinition,
2190
+ baseModel: ctx.models.entities.find((model2) => model2.name === baseTypeDefinition.name.value),
2191
+ typeDefinition: getType(ctx.info.schema, typeName),
2192
+ selectionSet: get(node.selectionSet, "selections")
2193
+ };
2194
+ };
1845
2195
  var getRootFieldNode = ({
1846
2196
  ctx,
1847
2197
  node,
@@ -1850,14 +2200,17 @@ var getRootFieldNode = ({
1850
2200
  const fieldName = node.name.value;
1851
2201
  const fieldDefinition = summonByKey(baseTypeDefinition.fields || [], "name.value", fieldName);
1852
2202
  const typeName = getTypeName(fieldDefinition.type);
2203
+ const model = ctx.models.getModel(typeName, "entity");
2204
+ const rootModel = model.parent ? ctx.models.getModel(model.parent, "entity") : model;
1853
2205
  return {
1854
2206
  ctx,
1855
- tableName: typeName,
1856
- tableAlias: typeName,
1857
- shortTableAlias: ctx.aliases.getShort(typeName),
2207
+ rootModel,
2208
+ rootTableAlias: rootModel.name,
2209
+ model,
2210
+ tableAlias: model.name,
2211
+ resultAlias: rootModel.name,
1858
2212
  baseTypeDefinition,
1859
2213
  typeDefinition: getType(ctx.info.schema, typeName),
1860
- model: summonByName(ctx.models, typeName),
1861
2214
  selectionSet: get(node.selectionSet, "selections"),
1862
2215
  field: node,
1863
2216
  fieldDefinition,
@@ -1876,7 +2229,9 @@ var getInlineFragments = (node) => node.selectionSet.filter(isInlineFragmentNode
1876
2229
  (subNode) => getResolverNode({
1877
2230
  ctx: node.ctx,
1878
2231
  node: subNode,
2232
+ rootTableAlias: node.rootTableAlias,
1879
2233
  tableAlias: node.tableAlias + "__" + getFragmentTypeName(subNode),
2234
+ resultAlias: node.resultAlias,
1880
2235
  baseTypeDefinition: node.baseTypeDefinition,
1881
2236
  typeName: getFragmentTypeName(subNode)
1882
2237
  })
@@ -1885,7 +2240,9 @@ var getFragmentSpreads = (node) => node.selectionSet.filter(isFragmentSpreadNode
1885
2240
  (subNode) => getResolverNode({
1886
2241
  ctx: node.ctx,
1887
2242
  node: node.ctx.info.fragments[subNode.name.value],
2243
+ rootTableAlias: node.rootTableAlias,
1888
2244
  tableAlias: node.tableAlias,
2245
+ resultAlias: node.resultAlias,
1889
2246
  baseTypeDefinition: node.baseTypeDefinition,
1890
2247
  typeName: node.model.name
1891
2248
  })
@@ -1899,44 +2256,91 @@ var getJoins = (node, toMany) => {
1899
2256
  const fieldNameOrAlias = getNameOrAlias(subNode);
1900
2257
  const fieldDefinition = summonByKey(baseTypeDefinition.fields || [], "name.value", fieldName);
1901
2258
  const typeName = getTypeName(fieldDefinition.type);
1902
- if (isObjectModel(summonByName(ctx.rawModels, typeName))) {
2259
+ if (isObjectModel(ctx.models.getModel(typeName))) {
1903
2260
  continue;
1904
2261
  }
1905
- const baseModel = summonByName(ctx.models, baseTypeDefinition.name.value);
1906
- let foreignKey;
1907
- if (toMany) {
1908
- const reverseRelation = baseModel.reverseRelationsByName[fieldName];
1909
- if (!reverseRelation) {
1910
- continue;
1911
- }
1912
- foreignKey = reverseRelation.foreignKey;
1913
- } else {
1914
- const modelField = baseModel.fieldsByName[fieldName];
1915
- if (modelField?.kind !== "relation") {
1916
- continue;
1917
- }
1918
- foreignKey = modelField.foreignKey;
2262
+ const baseModel = ctx.models.getModel(baseTypeDefinition.name.value, "entity");
2263
+ const relation = (toMany ? baseModel.reverseRelationsByName : baseModel.relationsByName)[fieldName];
2264
+ if (!relation) {
2265
+ continue;
1919
2266
  }
1920
2267
  const tableAlias = node.tableAlias + "__" + fieldNameOrAlias;
2268
+ const model = ctx.models.getModel(typeName, "entity");
2269
+ const rootModel = model;
1921
2270
  nodes.push({
1922
2271
  ctx,
1923
- tableName: typeName,
2272
+ rootModel,
2273
+ rootTableAlias: tableAlias,
2274
+ model,
1924
2275
  tableAlias,
1925
- shortTableAlias: ctx.aliases.getShort(tableAlias),
2276
+ resultAlias: tableAlias,
1926
2277
  baseTypeDefinition,
1927
2278
  baseModel,
1928
2279
  typeDefinition: getType(ctx.info.schema, typeName),
1929
- model: summonByName(ctx.models, typeName),
1930
2280
  selectionSet: get(subNode.selectionSet, "selections"),
1931
2281
  field: subNode,
1932
2282
  fieldDefinition,
1933
- foreignKey,
2283
+ relation,
2284
+ foreignKey: relation.field.foreignKey,
1934
2285
  isList: isListType(fieldDefinition.type)
1935
2286
  });
1936
2287
  }
1937
2288
  return nodes;
1938
2289
  };
1939
2290
 
2291
+ // src/resolvers/selects.ts
2292
+ var applySelects = (node, query, joins) => {
2293
+ void query.select(
2294
+ ...[
2295
+ { tableAlias: node.rootTableAlias, resultAlias: node.resultAlias, field: "id", fieldAlias: ID_ALIAS },
2296
+ ...node.model.root ? [{ tableAlias: node.rootTableAlias, resultAlias: node.resultAlias, field: "type", fieldAlias: TYPE_ALIAS }] : [],
2297
+ ...getSimpleFields(node).filter((fieldNode) => {
2298
+ const field = node.model.fieldsByName[fieldNode.name.value];
2299
+ if (!field || field.kind === "relation" || field.kind === "custom") {
2300
+ return false;
2301
+ }
2302
+ if (typeof field.queriable === "object" && !field.queriable.roles?.includes(node.ctx.user.role)) {
2303
+ throw new PermissionError(
2304
+ node.ctx.user.role,
2305
+ "READ",
2306
+ `${node.model.name}'s field "${field.name}"`,
2307
+ "field permission not available"
2308
+ );
2309
+ }
2310
+ return true;
2311
+ }).map((fieldNode) => {
2312
+ const field = node.model.getField(fieldNode.name.value);
2313
+ if (node.model.parent && !field.inherited) {
2314
+ addJoin(joins, node.rootTableAlias, node.model.name, node.tableAlias, "id", "id");
2315
+ }
2316
+ const fieldAlias = getNameOrAlias(fieldNode);
2317
+ if ([ID_ALIAS, TYPE_ALIAS].includes(fieldAlias)) {
2318
+ throw new UserInputError(`Keyword ${fieldAlias} is reserved by graphql-magic.`);
2319
+ }
2320
+ return {
2321
+ fieldNode,
2322
+ field: fieldNode.name.value,
2323
+ tableAlias: field.inherited ? node.rootTableAlias : node.tableAlias,
2324
+ resultAlias: node.resultAlias,
2325
+ fieldAlias
2326
+ };
2327
+ })
2328
+ ].map(
2329
+ ({ tableAlias, resultAlias, field, fieldAlias }) => `${node.ctx.aliases.getShort(tableAlias)}.${field} as ${node.ctx.aliases.getShort(resultAlias)}__${fieldAlias}`
2330
+ )
2331
+ );
2332
+ for (const subNode of getInlineFragments(node)) {
2333
+ applySelects(subNode, query, joins);
2334
+ }
2335
+ for (const subNode of getFragmentSpreads(node)) {
2336
+ applySelects(subNode, query, joins);
2337
+ }
2338
+ for (const subNode of getJoins(node, false)) {
2339
+ addJoin(joins, node.tableAlias, subNode.rootModel.name, subNode.rootTableAlias, subNode.foreignKey, "id");
2340
+ applySelects(subNode, query, joins);
2341
+ }
2342
+ };
2343
+
1940
2344
  // src/resolvers/resolver.ts
1941
2345
  var queryResolver = (_parent, _args, ctx, info) => resolve({ ...ctx, info, aliases: new AliasGenerator() });
1942
2346
  var resolve = async (ctx, id) => {
@@ -1952,13 +2356,13 @@ var resolve = async (ctx, id) => {
1952
2356
  });
1953
2357
  const { query, verifiedPermissionStacks } = await buildQuery(node);
1954
2358
  if (ctx.info.fieldName === "me") {
1955
- query.where({ [`${node.shortTableAlias}.id`]: node.ctx.user.id });
1956
- }
1957
- if (!node.isList) {
1958
- query.limit(1);
2359
+ void query.where({ [getColumn(node, "id")]: node.ctx.user.id });
1959
2360
  }
1960
2361
  if (id) {
1961
- query.where({ id });
2362
+ void query.where({ [getColumn(node, "id")]: id });
2363
+ }
2364
+ if (!node.isList) {
2365
+ void query.limit(1);
1962
2366
  }
1963
2367
  const raw = await query;
1964
2368
  const res = hydrate(node, raw);
@@ -1973,20 +2377,19 @@ var resolve = async (ctx, id) => {
1973
2377
  return res[0];
1974
2378
  };
1975
2379
  var buildQuery = async (node, parentVerifiedPermissionStacks) => {
1976
- const { tableAlias, shortTableAlias, tableName, model, ctx } = node;
1977
- const query = ctx.knex.fromRaw(`"${tableName}" as "${shortTableAlias}"`);
1978
- const joins = {};
2380
+ const query = node.ctx.knex.fromRaw(`"${node.rootModel.name}" as "${node.ctx.aliases.getShort(node.resultAlias)}"`);
2381
+ const joins = [];
1979
2382
  applyFilters(node, query, joins);
1980
2383
  applySelects(node, query, joins);
1981
2384
  applyJoins(node.ctx.aliases, query, joins);
1982
2385
  const tables = [
1983
- [model.name, tableAlias],
1984
- ...Object.keys(joins).map((tableName2) => tableName2.split(":"))
2386
+ [node.rootModel.name, node.rootTableAlias],
2387
+ ...joins.map(({ table2Name, table2Alias }) => [table2Name, table2Alias])
1985
2388
  ];
1986
2389
  const verifiedPermissionStacks = {};
1987
2390
  for (const [table, alias] of tables) {
1988
2391
  const verifiedPermissionStack = applyPermissions(
1989
- ctx,
2392
+ node.ctx,
1990
2393
  table,
1991
2394
  node.ctx.aliases.getShort(alias),
1992
2395
  query,
@@ -1999,39 +2402,6 @@ var buildQuery = async (node, parentVerifiedPermissionStacks) => {
1999
2402
  }
2000
2403
  return { query, verifiedPermissionStacks };
2001
2404
  };
2002
- var applySelects = (node, query, joins) => {
2003
- query.select(
2004
- ...[
2005
- { field: "id", alias: ID_ALIAS },
2006
- ...getSimpleFields(node).filter((n) => {
2007
- const field = node.model.fields.find(({ name: name2 }) => name2 === n.name.value);
2008
- if (!field || field.kind === "relation" || field.kind === "custom") {
2009
- return false;
2010
- }
2011
- if (typeof field.queriable === "object" && !field.queriable.roles?.includes(node.ctx.user.role)) {
2012
- throw new PermissionError(
2013
- "READ",
2014
- `${node.model.name}'s field "${field.name}"`,
2015
- "field permission not available"
2016
- );
2017
- }
2018
- return true;
2019
- }).map((n) => ({ field: n.name.value, alias: getNameOrAlias(n) }))
2020
- ].map(
2021
- ({ field, alias }) => `${node.shortTableAlias}.${field} as ${node.shortTableAlias}__${alias}`
2022
- )
2023
- );
2024
- for (const subNode of getInlineFragments(node)) {
2025
- applySelects(subNode, query, joins);
2026
- }
2027
- for (const subNode of getFragmentSpreads(node)) {
2028
- applySelects(subNode, query, joins);
2029
- }
2030
- for (const subNode of getJoins(node, false)) {
2031
- addJoin(joins, node.tableAlias, subNode.tableName, subNode.tableAlias, get(subNode, "foreignKey"), "id");
2032
- applySelects(subNode, query, joins);
2033
- }
2034
- };
2035
2405
  var applySubQueries = async (node, entries, parentVerifiedPermissionStacks) => {
2036
2406
  if (!entries.length) {
2037
2407
  return;
@@ -2048,10 +2418,12 @@ var applySubQueries = async (node, entries, parentVerifiedPermissionStacks) => {
2048
2418
  const fieldName = getNameOrAlias(subNode.field);
2049
2419
  const isList = isListType(subNode.fieldDefinition.type);
2050
2420
  entries.forEach((entry) => entry[fieldName] = isList ? [] : null);
2051
- const foreignKey = get(subNode, "foreignKey");
2421
+ const foreignKey = subNode.foreignKey;
2052
2422
  const { query, verifiedPermissionStacks } = await buildQuery(subNode, parentVerifiedPermissionStacks);
2423
+ const shortTableAlias = subNode.ctx.aliases.getShort(subNode.tableAlias);
2424
+ const shortResultAlias = subNode.ctx.aliases.getShort(subNode.resultAlias);
2053
2425
  const queries = ids.map(
2054
- (id) => query.clone().select(`${subNode.shortTableAlias}.${foreignKey} as ${subNode.shortTableAlias}__${foreignKey}`).where({ [`${subNode.shortTableAlias}.${foreignKey}`]: id })
2426
+ (id) => query.clone().select(`${shortTableAlias}.${foreignKey} as ${shortResultAlias}__${foreignKey}`).where({ [`${shortTableAlias}.${foreignKey}`]: id })
2055
2427
  );
2056
2428
  const rawChildren = (await Promise.all(queries)).flat();
2057
2429
  const children = hydrate(subNode, rawChildren);
@@ -2095,7 +2467,7 @@ var mutationResolver = async (_parent, args2, partialCtx, info) => {
2095
2467
  return await partialCtx.knex.transaction(async (knex) => {
2096
2468
  const [, mutation, modelName] = it(info.fieldName.match(/^(create|update|delete|restore)(.+)$/));
2097
2469
  const ctx = { ...partialCtx, knex, info, aliases: new AliasGenerator() };
2098
- const model = summonByName(ctx.models, modelName);
2470
+ const model = ctx.models.getModel(modelName, "entity");
2099
2471
  switch (mutation) {
2100
2472
  case "create":
2101
2473
  return await create(model, args2, ctx);
@@ -2113,12 +2485,32 @@ var create = async (model, { data: input2 }, ctx) => {
2113
2485
  normalizedInput.id = (0, import_uuid.v4)();
2114
2486
  normalizedInput.createdAt = ctx.now;
2115
2487
  normalizedInput.createdById = ctx.user.id;
2488
+ if (model.parent) {
2489
+ normalizedInput.type = model.name;
2490
+ }
2116
2491
  sanitize(ctx, model, normalizedInput);
2117
2492
  await checkCanWrite(ctx, model, normalizedInput, "CREATE");
2118
2493
  await ctx.handleUploads?.(normalizedInput);
2119
2494
  const data = { prev: {}, input: input2, normalizedInput, next: normalizedInput };
2120
2495
  await ctx.mutationHook?.(model, "create", "before", data, ctx);
2121
- await ctx.knex(model.name).insert(normalizedInput);
2496
+ if (model.parent) {
2497
+ const rootInput = {};
2498
+ const childInput = { id: normalizedInput.id };
2499
+ for (const field of model.fields) {
2500
+ const columnName = field.kind === "relation" ? `${field.name}Id` : field.name;
2501
+ if (columnName in normalizedInput) {
2502
+ if (field.inherited) {
2503
+ rootInput[columnName] = normalizedInput[columnName];
2504
+ } else {
2505
+ childInput[columnName] = normalizedInput[columnName];
2506
+ }
2507
+ }
2508
+ }
2509
+ await ctx.knex(model.parent).insert(rootInput);
2510
+ await ctx.knex(model.name).insert(childInput);
2511
+ } else {
2512
+ await ctx.knex(model.name).insert(normalizedInput);
2513
+ }
2122
2514
  await createRevision(model, normalizedInput, ctx);
2123
2515
  await ctx.mutationHook?.(model, "create", "after", data, ctx);
2124
2516
  return await resolve(ctx, normalizedInput.id);
@@ -2141,7 +2533,28 @@ var update = async (model, { where, data: input2 }, ctx) => {
2141
2533
  const next = { ...prev, ...normalizedInput };
2142
2534
  const data = { prev, input: input2, normalizedInput, next };
2143
2535
  await ctx.mutationHook?.(model, "update", "before", data, ctx);
2144
- await ctx.knex(model.name).where(where).update(normalizedInput);
2536
+ if (model.parent) {
2537
+ const rootInput = {};
2538
+ const childInput = {};
2539
+ for (const field of model.fields) {
2540
+ const columnName = field.kind === "relation" ? `${field.name}Id` : field.name;
2541
+ if (columnName in normalizedInput) {
2542
+ if (field.inherited) {
2543
+ rootInput[columnName] = normalizedInput[columnName];
2544
+ } else {
2545
+ childInput[columnName] = normalizedInput[columnName];
2546
+ }
2547
+ }
2548
+ }
2549
+ if (Object.keys(rootInput).length) {
2550
+ await ctx.knex(model.parent).where({ id: prev.id }).update(rootInput);
2551
+ }
2552
+ if (Object.keys(childInput).length) {
2553
+ await ctx.knex(model.name).where({ id: prev.id }).update(childInput);
2554
+ }
2555
+ } else {
2556
+ await ctx.knex(model.name).where({ id: prev.id }).update(normalizedInput);
2557
+ }
2145
2558
  await createRevision(model, next, ctx);
2146
2559
  await ctx.mutationHook?.(model, "update", "after", data, ctx);
2147
2560
  }
@@ -2151,7 +2564,8 @@ var del = async (model, { where, dryRun }, ctx) => {
2151
2564
  if (Object.keys(where).length === 0) {
2152
2565
  throw new Error(`No ${model.name} specified.`);
2153
2566
  }
2154
- const entity = await getEntityToMutate(ctx, model, where, "DELETE");
2567
+ const rootModel = model.rootModel;
2568
+ const entity = await getEntityToMutate(ctx, rootModel, where, "DELETE");
2155
2569
  if (entity.deleted) {
2156
2570
  throw new ForbiddenError("Entity is already deleted.");
2157
2571
  }
@@ -2190,10 +2604,9 @@ var del = async (model, { where, dryRun }, ctx) => {
2190
2604
  }
2191
2605
  }
2192
2606
  for (const {
2193
- model: descendantModel,
2194
- foreignKey,
2195
- field: { name: name2, onDelete }
2196
- } of currentModel.reverseRelations) {
2607
+ targetModel: descendantModel,
2608
+ field: { name: name2, foreignKey, onDelete }
2609
+ } of currentModel.reverseRelations.filter((reverseRelation) => !reverseRelation.field.inherited)) {
2197
2610
  const query = ctx.knex(descendantModel.name).where({ [foreignKey]: entity2.id });
2198
2611
  switch (onDelete) {
2199
2612
  case "set-null": {
@@ -2235,7 +2648,7 @@ var del = async (model, { where, dryRun }, ctx) => {
2235
2648
  }
2236
2649
  }
2237
2650
  };
2238
- await deleteCascade(model, entity);
2651
+ await deleteCascade(rootModel, entity);
2239
2652
  for (const callback of [...beforeHooks, ...mutations, ...afterHooks]) {
2240
2653
  await callback();
2241
2654
  }
@@ -2252,7 +2665,8 @@ var restore = async (model, { where }, ctx) => {
2252
2665
  if (Object.keys(where).length === 0) {
2253
2666
  throw new Error(`No ${model.name} specified.`);
2254
2667
  }
2255
- const entity = await getEntityToMutate(ctx, model, where, "RESTORE");
2668
+ const rootModel = model.rootModel;
2669
+ const entity = await getEntityToMutate(ctx, rootModel, where, "RESTORE");
2256
2670
  if (!entity.deleted) {
2257
2671
  throw new ForbiddenError("Entity is not deleted.");
2258
2672
  }
@@ -2279,9 +2693,10 @@ var restore = async (model, { where }, ctx) => {
2279
2693
  await ctx.mutationHook(model, "restore", "after", data, ctx);
2280
2694
  });
2281
2695
  }
2282
- for (const { model: descendantModel, foreignKey } of currentModel.reverseRelations.filter(
2283
- ({ model: { deletable } }) => deletable
2284
- )) {
2696
+ for (const {
2697
+ targetModel: descendantModel,
2698
+ field: { foreignKey }
2699
+ } of currentModel.reverseRelations.filter((reverseRelation) => !reverseRelation.field.inherited).filter(({ targetModel: { deletable } }) => deletable)) {
2285
2700
  const query = ctx.knex(descendantModel.name).where({ [foreignKey]: relatedEntity.id });
2286
2701
  applyPermissions(ctx, descendantModel.name, descendantModel.name, query, "RESTORE");
2287
2702
  const descendants = await query;
@@ -2290,7 +2705,7 @@ var restore = async (model, { where }, ctx) => {
2290
2705
  }
2291
2706
  }
2292
2707
  };
2293
- await restoreCascade(model, entity);
2708
+ await restoreCascade(rootModel, entity);
2294
2709
  for (const callback of [...beforeHooks, ...mutations, ...afterHooks]) {
2295
2710
  await callback();
2296
2711
  }
@@ -2298,24 +2713,37 @@ var restore = async (model, { where }, ctx) => {
2298
2713
  };
2299
2714
  var createRevision = async (model, data, ctx) => {
2300
2715
  if (model.updatable) {
2301
- const revisionData = {
2302
- id: (0, import_uuid.v4)(),
2303
- [`${typeToField(model.name)}Id`]: data.id,
2716
+ const revisionId = (0, import_uuid.v4)();
2717
+ const rootRevisionData = {
2718
+ id: revisionId,
2719
+ [`${typeToField(model.parent || model.name)}Id`]: data.id,
2304
2720
  createdAt: ctx.now,
2305
2721
  createdById: ctx.user.id
2306
2722
  };
2307
2723
  if (model.deletable) {
2308
- revisionData.deleted = data.deleted || false;
2309
- }
2310
- for (const { kind: type, name: name2, nonNull: nonNull2, ...field } of model.fields.filter(({ updatable }) => updatable)) {
2311
- const col = type === "relation" ? `${name2}Id` : name2;
2312
- if (nonNull2 && (!(col in data) || col === void 0 || col === null)) {
2313
- revisionData[col] = get(field, "defaultValue");
2724
+ rootRevisionData.deleted = data.deleted || false;
2725
+ }
2726
+ const childRevisionData = { id: revisionId };
2727
+ for (const field of model.fields.filter(({ updatable }) => updatable)) {
2728
+ const col = field.kind === "relation" ? `${field.name}Id` : field.name;
2729
+ let value2;
2730
+ if (field.nonNull && (!(col in data) || col === void 0 || col === null)) {
2731
+ value2 = get(field, "defaultValue");
2314
2732
  } else {
2315
- revisionData[col] = data[col];
2733
+ value2 = data[col];
2316
2734
  }
2735
+ if (!model.parent || field.inherited) {
2736
+ rootRevisionData[col] = value2;
2737
+ } else {
2738
+ childRevisionData[col] = value2;
2739
+ }
2740
+ }
2741
+ if (model.parent) {
2742
+ await ctx.knex(`${model.parent}Revision`).insert(rootRevisionData);
2743
+ await ctx.knex(`${model.name}Revision`).insert(childRevisionData);
2744
+ } else {
2745
+ await ctx.knex(`${model.name}Revision`).insert(rootRevisionData);
2317
2746
  }
2318
- await ctx.knex(`${model.name}Revision`).insert(revisionData);
2319
2747
  }
2320
2748
  };
2321
2749
  var sanitize = (ctx, model, data) => {
@@ -2332,7 +2760,7 @@ var sanitize = (ctx, model, data) => {
2332
2760
  data[key] = data[key].endOf("day");
2333
2761
  continue;
2334
2762
  }
2335
- if (isEnumList(ctx.rawModels, field) && Array.isArray(data[key])) {
2763
+ if (field.list && field.kind === "enum" && Array.isArray(data[key])) {
2336
2764
  data[key] = `{${data[key].join(",")}}`;
2337
2765
  continue;
2338
2766
  }
@@ -2346,30 +2774,37 @@ var getResolvers = (models) => ({
2346
2774
  {
2347
2775
  me: queryResolver
2348
2776
  },
2349
- ...models.filter(({ queriable }) => queriable).map((model) => ({
2777
+ ...models.entities.filter(({ queriable }) => queriable).map((model) => ({
2350
2778
  [typeToField(model.name)]: queryResolver
2351
2779
  })),
2352
- ...models.filter(({ listQueriable }) => listQueriable).map((model) => ({
2353
- [getModelPluralField(model)]: queryResolver
2780
+ ...models.entities.filter(({ listQueriable }) => listQueriable).map((model) => ({
2781
+ [model.pluralField]: queryResolver
2354
2782
  }))
2355
2783
  ]),
2356
2784
  Mutation: merge2([
2357
- ...models.filter(({ creatable }) => creatable).map((model) => ({
2785
+ ...models.entities.filter(not(isRootModel)).filter(({ creatable }) => creatable).map((model) => ({
2358
2786
  [`create${model.name}`]: mutationResolver
2359
2787
  })),
2360
- ...models.filter(({ updatable }) => updatable).map((model) => ({
2788
+ ...models.entities.filter(not(isRootModel)).filter(({ updatable }) => updatable).map((model) => ({
2361
2789
  [`update${model.name}`]: mutationResolver
2362
2790
  })),
2363
- ...models.filter(({ deletable }) => deletable).map((model) => ({
2791
+ ...models.entities.filter(not(isRootModel)).filter(({ deletable }) => deletable).map((model) => ({
2364
2792
  [`delete${model.name}`]: mutationResolver,
2365
2793
  [`restore${model.name}`]: mutationResolver
2366
2794
  }))
2367
- ])
2795
+ ]),
2796
+ ...Object.assign(
2797
+ {},
2798
+ ...models.entities.filter(isRootModel).map((model) => ({
2799
+ [model.name]: {
2800
+ __resolveType: ({ TYPE }) => TYPE
2801
+ }
2802
+ }))
2803
+ )
2368
2804
  });
2369
2805
 
2370
2806
  // src/schema/generate.ts
2371
2807
  var import_graphql5 = require("graphql");
2372
- var import_flatMap2 = __toESM(require("lodash/flatMap"), 1);
2373
2808
 
2374
2809
  // src/schema/utils.ts
2375
2810
  var import_luxon = require("luxon");
@@ -2401,10 +2836,11 @@ var object = (nme, fds, interfaces, dvs) => ({
2401
2836
  interfaces: interfaces && interfaces.map((i) => namedType(i)),
2402
2837
  directives: directives(dvs)
2403
2838
  });
2404
- var iface = (nme, fds, dvs) => ({
2839
+ var iface = (nme, fds, interfaces, dvs) => ({
2405
2840
  name: name(nme),
2406
2841
  fields: fields(fds),
2407
2842
  kind: "InterfaceTypeDefinition",
2843
+ interfaces: interfaces && interfaces.map((i) => namedType(i)),
2408
2844
  directives: directives(dvs)
2409
2845
  });
2410
2846
  var inputValues = (fields2) => fields2.map(
@@ -2520,84 +2956,86 @@ var value = (val = null) => val === null ? {
2520
2956
  };
2521
2957
 
2522
2958
  // src/schema/generate.ts
2523
- var generateDefinitions = (rawModels) => {
2524
- const models = getModels(rawModels);
2959
+ var generateDefinitions = ({
2960
+ scalars,
2961
+ rawEnums,
2962
+ enums,
2963
+ inputs,
2964
+ interfaces,
2965
+ entities,
2966
+ objects
2967
+ }) => {
2525
2968
  return [
2526
2969
  // Predefined types
2527
- enm("Order", ["ASC", "DESC"]),
2528
- scalar("DateTime"),
2529
- scalar("Upload"),
2530
- ...rawModels.filter(isEnumModel).map((model) => enm(model.name, model.values)),
2531
- ...rawModels.filter(isRawEnumModel).map((model) => enm(model.name, model.values)),
2532
- ...rawModels.filter(isScalarModel).map((model) => scalar(model.name)),
2533
- ...rawModels.filter(isObjectModel).filter(({ name: name2 }) => !["Query", "Mutation"].includes(name2)).map((model) => object(model.name, model.fields)),
2534
- ...rawModels.filter(isInputModel).map((model) => input(model.name, model.fields)),
2535
- ...rawModels.filter(isObjectModel).filter(
2536
- (model) => models.some((m) => m.creatable && m.fields.some((f) => f.creatable && f.kind === "json" && f.type === model.name))
2970
+ ...rawEnums.map((model) => enm(model.name, model.values)),
2971
+ ...enums.map((model) => enm(model.name, model.values)),
2972
+ ...scalars.map((model) => scalar(model.name)),
2973
+ ...objects.filter(({ name: name2 }) => !["Query", "Mutation"].includes(name2)).map((model) => object(model.name, model.fields)),
2974
+ ...interfaces.map(({ name: name2, fields: fields2 }) => iface(name2, fields2)),
2975
+ ...inputs.map((model) => input(model.name, model.fields)),
2976
+ ...objects.filter(
2977
+ (model) => entities.some((m) => m.creatable && m.fields.some((f) => f.creatable && f.kind === "json" && f.type === model.name))
2537
2978
  ).map((model) => input(`Create${model.name}`, model.fields)),
2538
- ...rawModels.filter(isObjectModel).filter(
2539
- (model) => models.some((m) => m.creatable && m.fields.some((f) => f.creatable && f.kind === "json" && f.type === model.name))
2979
+ ...objects.filter(
2980
+ (model) => entities.some((m) => m.updatable && m.fields.some((f) => f.updatable && f.kind === "json" && f.type === model.name))
2540
2981
  ).map((model) => input(`Update${model.name}`, model.fields)),
2541
- ...(0, import_flatMap2.default)(
2542
- models.map((model) => {
2543
- const types = [
2544
- object(
2545
- model.name,
2546
- [
2547
- ...model.fields.filter(isQueriableField).map((field) => ({
2548
- ...field,
2549
- type: field.type,
2550
- args: [...field.args || []],
2551
- directives: field.directives
2552
- })),
2553
- ...model.reverseRelations.map(({ name: name2, field, model: model2 }) => ({
2554
- name: name2,
2555
- type: model2.name,
2556
- list: !field.toOne,
2557
- nonNull: !field.toOne,
2558
- args: [
2559
- { name: "where", type: `${model2.name}Where` },
2560
- ...model2.fields.some(({ searchable }) => searchable) ? [{ name: "search", type: "String" }] : [],
2561
- ...model2.fields.some(({ orderable }) => orderable) ? [{ name: "orderBy", type: `${model2.name}OrderBy`, list: true }] : [],
2562
- { name: "limit", type: "Int" },
2563
- { name: "offset", type: "Int" }
2564
- ]
2565
- }))
2566
- ],
2567
- model.interfaces
2568
- ),
2569
- input(`${model.name}Where`, [
2570
- ...model.fields.filter(({ kind, unique, filterable }) => (unique || filterable) && kind !== "relation").map((field) => ({
2571
- name: field.name,
2982
+ ...entities.flatMap((model) => {
2983
+ const types = [
2984
+ (isRootModel(model) ? iface : object)(
2985
+ model.name,
2986
+ [
2987
+ ...model.fields.filter(isQueriableField).map((field) => ({
2988
+ ...field,
2572
2989
  type: field.type,
2573
- list: true,
2574
- default: typeof field.filterable === "object" ? field.filterable.default : void 0
2990
+ args: [...field.args || []],
2991
+ directives: field.directives
2575
2992
  })),
2576
- ...(0, import_flatMap2.default)(
2577
- model.fields.filter(({ comparable }) => comparable),
2578
- (field) => [
2579
- { name: `${field.name}_GT`, type: field.type },
2580
- { name: `${field.name}_GTE`, type: field.type },
2581
- { name: `${field.name}_LT`, type: field.type },
2582
- { name: `${field.name}_LTE`, type: field.type }
2583
- ]
2584
- ),
2585
- ...model.fields.filter(isRelation).filter(({ filterable }) => filterable).map(({ name: name2, type }) => ({
2993
+ ...model.reverseRelations.map(({ name: name2, field, targetModel }) => ({
2586
2994
  name: name2,
2587
- type: `${type}Where`
2995
+ type: targetModel.name,
2996
+ list: !field.toOne,
2997
+ nonNull: !field.toOne,
2998
+ args: [
2999
+ { name: "where", type: `${targetModel.name}Where` },
3000
+ ...targetModel.fields.some(({ searchable }) => searchable) ? [{ name: "search", type: "String" }] : [],
3001
+ ...targetModel.fields.some(({ orderable }) => orderable) ? [{ name: "orderBy", type: `${targetModel.name}OrderBy`, list: true }] : [],
3002
+ { name: "limit", type: "Int" },
3003
+ { name: "offset", type: "Int" }
3004
+ ]
2588
3005
  }))
3006
+ ],
3007
+ [...model.parent ? [model.parent] : [], ...model.interfaces || []]
3008
+ ),
3009
+ input(`${model.name}Where`, [
3010
+ ...model.fields.filter(({ kind, unique, filterable }) => (unique || filterable) && kind !== "relation").map((field) => ({
3011
+ name: field.name,
3012
+ type: field.type,
3013
+ list: true,
3014
+ default: typeof field.filterable === "object" ? field.filterable.default : void 0
3015
+ })),
3016
+ ...model.fields.filter(({ comparable }) => comparable).flatMap((field) => [
3017
+ { name: `${field.name}_GT`, type: field.type },
3018
+ { name: `${field.name}_GTE`, type: field.type },
3019
+ { name: `${field.name}_LT`, type: field.type },
3020
+ { name: `${field.name}_LTE`, type: field.type }
2589
3021
  ]),
3022
+ ...model.fields.filter(isRelation).filter(({ filterable }) => filterable).map(({ name: name2, type }) => ({
3023
+ name: name2,
3024
+ type: `${type}Where`
3025
+ }))
3026
+ ]),
3027
+ input(
3028
+ `${model.name}WhereUnique`,
3029
+ model.fields.filter(({ unique }) => unique).map((field) => ({ name: field.name, type: field.type }))
3030
+ ),
3031
+ ...model.fields.some(({ orderable }) => orderable) ? [
2590
3032
  input(
2591
- `${model.name}WhereUnique`,
2592
- model.fields.filter(({ unique }) => unique).map((field) => ({ name: field.name, type: field.type }))
2593
- ),
2594
- ...model.fields.some(({ orderable }) => orderable) ? [
2595
- input(
2596
- `${model.name}OrderBy`,
2597
- model.fields.filter(({ orderable }) => orderable).map(({ name: name2 }) => ({ name: name2, type: "Order" }))
2598
- )
2599
- ] : []
2600
- ];
3033
+ `${model.name}OrderBy`,
3034
+ model.fields.filter(({ orderable }) => orderable).map(({ name: name2 }) => ({ name: name2, type: "Order" }))
3035
+ )
3036
+ ] : []
3037
+ ];
3038
+ if (!isRootModel(model)) {
2601
3039
  if (model.creatable) {
2602
3040
  types.push(
2603
3041
  input(
@@ -2627,15 +3065,15 @@ var generateDefinitions = (rawModels) => {
2627
3065
  )
2628
3066
  );
2629
3067
  }
2630
- return types;
2631
- })
2632
- ),
3068
+ }
3069
+ return types;
3070
+ }),
2633
3071
  object("Query", [
2634
3072
  {
2635
3073
  name: "me",
2636
3074
  type: "User"
2637
3075
  },
2638
- ...models.filter(({ queriable }) => queriable).map(({ name: name2 }) => ({
3076
+ ...entities.filter(({ queriable }) => queriable).map(({ name: name2 }) => ({
2639
3077
  name: typeToField(name2),
2640
3078
  type: name2,
2641
3079
  nonNull: true,
@@ -2647,8 +3085,8 @@ var generateDefinitions = (rawModels) => {
2647
3085
  }
2648
3086
  ]
2649
3087
  })),
2650
- ...models.filter(({ listQueriable }) => listQueriable).map((model) => ({
2651
- name: getModelPluralField(model),
3088
+ ...entities.filter(({ listQueriable }) => listQueriable).map((model) => ({
3089
+ name: model.pluralField,
2652
3090
  type: model.name,
2653
3091
  list: true,
2654
3092
  nonNull: true,
@@ -2660,12 +3098,12 @@ var generateDefinitions = (rawModels) => {
2660
3098
  { name: "offset", type: "Int" }
2661
3099
  ]
2662
3100
  })),
2663
- ...rawModels.filter(isObjectModel).filter((model) => model.name === "Query").flatMap((model) => model.fields)
3101
+ ...objects.filter((model) => model.name === "Query").flatMap((model) => model.fields)
2664
3102
  ]),
2665
3103
  object("Mutation", [
2666
- ...(0, import_flatMap2.default)(
2667
- models.map((model) => {
2668
- const mutations = [];
3104
+ ...entities.flatMap((model) => {
3105
+ const mutations = [];
3106
+ if (!isRootModel(model)) {
2669
3107
  if (model.creatable) {
2670
3108
  mutations.push({
2671
3109
  name: `create${model.name}`,
@@ -2729,14 +3167,14 @@ var generateDefinitions = (rawModels) => {
2729
3167
  ]
2730
3168
  });
2731
3169
  }
2732
- return mutations;
2733
- })
2734
- ),
2735
- ...rawModels.filter(isObjectModel).filter((model) => model.name === "Mutation").flatMap((model) => model.fields)
3170
+ }
3171
+ return mutations;
3172
+ }),
3173
+ ...objects.filter((model) => model.name === "Mutation").flatMap((model) => model.fields)
2736
3174
  ])
2737
3175
  ];
2738
3176
  };
2739
- var generate = (rawModels) => document(generateDefinitions(rawModels));
3177
+ var generate = (models) => document(generateDefinitions(models));
2740
3178
  var printSchema = (schema) => [
2741
3179
  ...schema.getDirectives().map((d) => d.astNode && (0, import_graphql5.print)(d.astNode)),
2742
3180
  ...Object.values(schema.getTypeMap()).filter((t) => !t.name.match(/^__/)).sort((a, b) => a.name > b.name ? 1 : -1).map((t) => t.astNode && (0, import_graphql5.print)(t.astNode))
@@ -2747,13 +3185,27 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildAST
2747
3185
  // Annotate the CommonJS export names for ESM import in node:
2748
3186
  0 && (module.exports = {
2749
3187
  AliasGenerator,
3188
+ EntityModel,
3189
+ EnumModel,
2750
3190
  ForbiddenError,
2751
3191
  GraphQLError,
2752
3192
  ID_ALIAS,
3193
+ InputModel,
3194
+ InterfaceModel,
3195
+ ManyToManyRelation,
2753
3196
  MigrationGenerator,
3197
+ Model,
3198
+ Models,
3199
+ NormalRelation,
2754
3200
  NotFoundError,
3201
+ ObjectModel,
2755
3202
  PermissionError,
3203
+ RawEnumModel,
3204
+ Relation,
3205
+ ReverseRelation,
2756
3206
  SPECIAL_FILTERS,
3207
+ ScalarModel,
3208
+ TYPE_ALIAS,
2757
3209
  UserInputError,
2758
3210
  actionableRelations,
2759
3211
  addJoin,
@@ -2762,6 +3214,7 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildAST
2762
3214
  applyFilters,
2763
3215
  applyJoins,
2764
3216
  applyPermissions,
3217
+ applySelects,
2765
3218
  args,
2766
3219
  checkCanWrite,
2767
3220
  directive,
@@ -2779,6 +3232,8 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildAST
2779
3232
  generateMutations,
2780
3233
  generatePermissions,
2781
3234
  get,
3235
+ getColumn,
3236
+ getColumnName,
2782
3237
  getEditEntityRelationsQuery,
2783
3238
  getEntityListQuery,
2784
3239
  getEntityQuery,
@@ -2789,16 +3244,8 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildAST
2789
3244
  getInlineFragments,
2790
3245
  getJoins,
2791
3246
  getLabel,
2792
- getManyToManyRelation,
2793
- getManyToManyRelations,
2794
3247
  getManyToManyRelationsQuery,
2795
3248
  getMigrationDate,
2796
- getModelLabel,
2797
- getModelLabelPlural,
2798
- getModelPlural,
2799
- getModelPluralField,
2800
- getModelSlug,
2801
- getModels,
2802
3249
  getMutationQuery,
2803
3250
  getNameOrAlias,
2804
3251
  getPermissionStack,
@@ -2811,6 +3258,7 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildAST
2811
3258
  getTypeName,
2812
3259
  getUpdateEntityQuery,
2813
3260
  gql,
3261
+ hasName,
2814
3262
  hash,
2815
3263
  hydrate,
2816
3264
  iface,
@@ -2822,12 +3270,14 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildAST
2822
3270
  isCustomField,
2823
3271
  isEntityModel,
2824
3272
  isEnum,
2825
- isEnumList,
2826
3273
  isEnumModel,
2827
3274
  isFieldNode,
2828
3275
  isFragmentSpreadNode,
3276
+ isInTable,
3277
+ isInherited,
2829
3278
  isInlineFragmentNode,
2830
3279
  isInputModel,
3280
+ isInterfaceModel,
2831
3281
  isListType,
2832
3282
  isObjectModel,
2833
3283
  isPrimitive,
@@ -2835,16 +3285,20 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildAST
2835
3285
  isQueriableField,
2836
3286
  isRawEnumModel,
2837
3287
  isRelation,
3288
+ isRootModel,
2838
3289
  isScalarModel,
2839
3290
  isSimpleField,
2840
3291
  isToOneRelation,
2841
3292
  isUpdatable,
2842
3293
  isUpdatableBy,
3294
+ isUpdatableField,
3295
+ isUpdatableModel,
2843
3296
  isVisible,
2844
3297
  isVisibleRelation,
2845
3298
  it,
2846
3299
  list,
2847
3300
  merge,
3301
+ modelNeedsTable,
2848
3302
  mutationResolver,
2849
3303
  name,
2850
3304
  namedType,
@@ -2854,6 +3308,7 @@ var printSchemaFromModels = (models) => printSchema((0, import_graphql5.buildAST
2854
3308
  normalizeValueByTypeDefinition,
2855
3309
  not,
2856
3310
  object,
3311
+ or,
2857
3312
  ors,
2858
3313
  printSchema,
2859
3314
  printSchemaFromDocument,