@smartive/graphql-magic 9.1.2 → 10.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 (120) 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 +2 -2
  5. package/README.md +1 -1
  6. package/dist/bin/gqm.cjs +684 -330
  7. package/dist/cjs/index.cjs +998 -554
  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/migrations/generate.d.ts +3 -4
  21. package/dist/esm/migrations/generate.js +114 -107
  22. package/dist/esm/migrations/generate.js.map +1 -1
  23. package/dist/esm/models/index.d.ts +1 -0
  24. package/dist/esm/models/index.js +1 -0
  25. package/dist/esm/models/index.js.map +1 -1
  26. package/dist/esm/models/model-definitions.d.ts +189 -0
  27. package/dist/esm/models/model-definitions.js +2 -0
  28. package/dist/esm/models/model-definitions.js.map +1 -0
  29. package/dist/esm/models/models.d.ts +128 -174
  30. package/dist/esm/models/models.js +411 -1
  31. package/dist/esm/models/models.js.map +1 -1
  32. package/dist/esm/models/mutation-hook.d.ts +2 -2
  33. package/dist/esm/models/utils.d.ts +35 -497
  34. package/dist/esm/models/utils.js +21 -144
  35. package/dist/esm/models/utils.js.map +1 -1
  36. package/dist/esm/permissions/check.d.ts +3 -3
  37. package/dist/esm/permissions/check.js +14 -7
  38. package/dist/esm/permissions/check.js.map +1 -1
  39. package/dist/esm/permissions/generate.js +6 -6
  40. package/dist/esm/permissions/generate.js.map +1 -1
  41. package/dist/esm/resolvers/filters.d.ts +8 -0
  42. package/dist/esm/resolvers/filters.js +28 -25
  43. package/dist/esm/resolvers/filters.js.map +1 -1
  44. package/dist/esm/resolvers/index.d.ts +1 -0
  45. package/dist/esm/resolvers/index.js +1 -0
  46. package/dist/esm/resolvers/index.js.map +1 -1
  47. package/dist/esm/resolvers/mutations.js +85 -21
  48. package/dist/esm/resolvers/mutations.js.map +1 -1
  49. package/dist/esm/resolvers/node.d.ts +13 -15
  50. package/dist/esm/resolvers/node.js +41 -36
  51. package/dist/esm/resolvers/node.js.map +1 -1
  52. package/dist/esm/resolvers/resolver.js +19 -49
  53. package/dist/esm/resolvers/resolver.js.map +1 -1
  54. package/dist/esm/resolvers/resolvers.d.ts +1 -8
  55. package/dist/esm/resolvers/resolvers.js +15 -7
  56. package/dist/esm/resolvers/resolvers.js.map +1 -1
  57. package/dist/esm/resolvers/selects.d.ts +3 -0
  58. package/dist/esm/resolvers/selects.js +50 -0
  59. package/dist/esm/resolvers/selects.js.map +1 -0
  60. package/dist/esm/resolvers/utils.d.ts +12 -4
  61. package/dist/esm/resolvers/utils.js +30 -22
  62. package/dist/esm/resolvers/utils.js.map +1 -1
  63. package/dist/esm/schema/generate.d.ts +4 -4
  64. package/dist/esm/schema/generate.js +122 -131
  65. package/dist/esm/schema/generate.js.map +1 -1
  66. package/dist/esm/schema/utils.d.ts +1 -1
  67. package/dist/esm/schema/utils.js +2 -1
  68. package/dist/esm/schema/utils.js.map +1 -1
  69. package/knexfile.ts +31 -0
  70. package/migrations/20230912185644_setup.ts +127 -0
  71. package/package.json +16 -14
  72. package/src/api/execute.ts +1 -1
  73. package/src/bin/gqm/gqm.ts +25 -23
  74. package/src/bin/gqm/parse-models.ts +5 -5
  75. package/src/bin/gqm/settings.ts +13 -4
  76. package/src/bin/gqm/static-eval.ts +5 -0
  77. package/src/bin/gqm/templates.ts +23 -3
  78. package/src/client/mutations.ts +11 -5
  79. package/src/client/queries.ts +43 -80
  80. package/src/context.ts +1 -2
  81. package/src/db/generate.ts +41 -41
  82. package/src/migrations/generate.ts +165 -146
  83. package/src/models/index.ts +1 -0
  84. package/src/models/model-definitions.ts +168 -0
  85. package/src/models/models.ts +510 -166
  86. package/src/models/mutation-hook.ts +2 -2
  87. package/src/models/utils.ts +53 -187
  88. package/src/permissions/check.ts +19 -11
  89. package/src/permissions/generate.ts +6 -6
  90. package/src/resolvers/filters.ts +44 -28
  91. package/src/resolvers/index.ts +1 -0
  92. package/src/resolvers/mutations.ts +98 -36
  93. package/src/resolvers/node.ts +79 -51
  94. package/src/resolvers/resolver.ts +20 -74
  95. package/src/resolvers/resolvers.ts +18 -7
  96. package/src/resolvers/selects.ts +77 -0
  97. package/src/resolvers/utils.ts +41 -25
  98. package/src/schema/generate.ts +106 -127
  99. package/src/schema/utils.ts +2 -1
  100. package/tests/api/__snapshots__/inheritance.spec.ts.snap +83 -0
  101. package/tests/api/inheritance.spec.ts +130 -0
  102. package/tests/generated/api/index.ts +1174 -0
  103. package/tests/generated/client/index.ts +1163 -0
  104. package/tests/generated/client/mutations.ts +109 -0
  105. package/tests/generated/db/index.ts +291 -0
  106. package/tests/generated/db/knex.ts +14 -0
  107. package/tests/generated/models.json +675 -0
  108. package/tests/generated/schema.graphql +325 -0
  109. package/tests/unit/__snapshots__/resolve.spec.ts.snap +23 -0
  110. package/tests/unit/queries.spec.ts +5 -5
  111. package/tests/unit/resolve.spec.ts +8 -8
  112. package/tests/utils/database/knex.ts +5 -13
  113. package/tests/utils/database/seed.ts +57 -18
  114. package/tests/utils/models.ts +62 -7
  115. package/tests/utils/server.ts +5 -5
  116. package/tsconfig.eslint.json +1 -0
  117. package/tests/unit/__snapshots__/generate.spec.ts.snap +0 -128
  118. package/tests/unit/generate.spec.ts +0 -8
  119. package/tests/utils/database/schema.ts +0 -64
  120. package/tests/utils/generate-migration.ts +0 -24
package/dist/bin/gqm.cjs CHANGED
@@ -36,9 +36,9 @@ var import_lodash = require("lodash");
36
36
  // src/client/mutations.ts
37
37
  var import_upperCase = __toESM(require("lodash/upperCase"), 1);
38
38
  var constantCase = (str) => (0, import_upperCase.default)(str).replace(/ /g, "_");
39
- var generateMutations = (models) => {
39
+ var generateMutations = (models, gqmModule = "@smartive/graphql-magic") => {
40
40
  const parts = [];
41
- for (const { name: name2, creatable, updatable, deletable } of models.filter(isEntityModel)) {
41
+ for (const { name: name2, creatable, updatable, deletable } of models.entities.filter(not(isRootModel))) {
42
42
  if (creatable) {
43
43
  parts.push(
44
44
  `export const CREATE_${constantCase(
@@ -69,11 +69,20 @@ var generateMutations = (models) => {
69
69
  mutation Delete${name2}Mutation($id: ID!) {
70
70
  delete${name2}(where: { id: $id })
71
71
  }
72
+ \`;`
73
+ );
74
+ parts.push(
75
+ `export const RESTORE_${constantCase(
76
+ name2
77
+ )} = gql\`
78
+ mutation Restore${name2}Mutation($id: ID!) {
79
+ restore${name2}(where: { id: $id })
80
+ }
72
81
  \`;`
73
82
  );
74
83
  }
75
84
  }
76
- return `import { gql } from "@smartive/graphql-magic";
85
+ return `import { gql } from "${gqmModule}";
77
86
 
78
87
  ${parts.join("\n\n")}`;
79
88
  };
@@ -82,77 +91,104 @@ ${parts.join("\n\n")}`;
82
91
  var import_upperFirst = __toESM(require("lodash/upperFirst"), 1);
83
92
 
84
93
  // src/models/utils.ts
85
- var import_inflection = require("inflection");
86
- var import_camelCase = __toESM(require("lodash/camelCase"), 1);
94
+ var import_lodash3 = require("lodash");
87
95
  var import_get = __toESM(require("lodash/get"), 1);
88
- var import_kebabCase = __toESM(require("lodash/kebabCase"), 1);
89
- var import_startCase = __toESM(require("lodash/startCase"), 1);
90
- var typeToField = (type) => type.substr(0, 1).toLowerCase() + type.substr(1);
91
- var getModelPlural = (model) => model.plural || (0, import_inflection.pluralize)(model.name);
92
- var getModelPluralField = (model) => typeToField(getModelPlural(model));
93
- var isEntityModel = (model) => model.kind === "entity";
94
- var isEnumModel = (model) => model.kind === "enum";
95
- var isRawEnumModel = (model) => model.kind === "raw-enum";
96
- var isScalarModel = (model) => model.kind === "scalar";
97
- var isObjectModel = (model) => model.kind === "object";
98
- var isInputModel = (model) => model.kind === "input";
99
- var and = (...predicates) => (field) => predicates.every((predicate) => predicate(field));
100
- var not = (predicate) => (field) => !predicate(field);
101
- var isRelation = (field) => field.kind === "relation";
102
- var isQueriableField = ({ queriable }) => queriable !== false;
103
- var isCustomField = (field) => field.kind === "custom";
104
- var isSimpleField = and(not(isRelation), not(isCustomField));
105
- var getModels = (rawModels) => {
106
- const models = rawModels.filter(isEntityModel).map((model) => {
107
- const objectModel = {
108
- ...model,
109
- fieldsByName: {},
110
- relations: [],
111
- relationsByName: {},
112
- reverseRelations: [],
113
- reverseRelationsByName: {},
114
- fields: [
115
- { name: "id", type: "ID", nonNull: true, unique: true, primary: true, generated: true },
116
- ...model.fields,
117
- ...model.creatable ? [
96
+
97
+ // src/models/models.ts
98
+ var import_inflection = require("inflection");
99
+ var import_lodash2 = require("lodash");
100
+ var Models = class {
101
+ models;
102
+ modelsByName = {};
103
+ scalars;
104
+ rawEnums;
105
+ enums;
106
+ inputs;
107
+ interfaces;
108
+ objects;
109
+ entities;
110
+ definitions;
111
+ constructor(definitions) {
112
+ this.definitions = (0, import_lodash2.cloneDeep)(definitions);
113
+ this.definitions.push(
114
+ {
115
+ kind: "scalar",
116
+ name: "DateTime"
117
+ },
118
+ { kind: "scalar", name: "Upload" },
119
+ {
120
+ kind: "raw-enum",
121
+ name: "Order",
122
+ values: ["ASC", "DESC"]
123
+ }
124
+ );
125
+ const entities = this.definitions.filter(isEntityModelDefinition);
126
+ for (const entity of entities) {
127
+ if (entity.root) {
128
+ this.definitions.push({
129
+ kind: "enum",
130
+ name: `${entity.name}Type`,
131
+ values: entities.filter((subModel) => subModel.parent === entity.name).map((subModel) => subModel.name)
132
+ });
133
+ }
134
+ entity.fields = [
135
+ {
136
+ name: "id",
137
+ type: "ID",
138
+ nonNull: true,
139
+ unique: true,
140
+ primary: true,
141
+ generated: true
142
+ },
143
+ ...entity.root ? [
144
+ {
145
+ name: "type",
146
+ kind: "enum",
147
+ type: `${entity.name}Type`,
148
+ nonNull: true,
149
+ generated: true
150
+ }
151
+ ] : [],
152
+ ...entity.fields,
153
+ ...entity.creatable ? [
118
154
  {
119
155
  name: "createdAt",
120
156
  type: "DateTime",
121
157
  nonNull: true,
122
158
  orderable: true,
123
159
  generated: true,
124
- ...typeof model.creatable === "object" && model.creatable.createdAt
160
+ ...typeof entity.creatable === "object" && entity.creatable.createdAt
125
161
  },
126
162
  {
127
163
  name: "createdBy",
128
164
  kind: "relation",
129
165
  type: "User",
130
166
  nonNull: true,
131
- reverse: `created${getModelPlural(model)}`,
167
+ reverse: `created${getModelPlural(entity)}`,
132
168
  generated: true,
133
- ...typeof model.creatable === "object" && model.creatable.createdBy
169
+ ...typeof entity.creatable === "object" && entity.creatable.createdBy
134
170
  }
135
171
  ] : [],
136
- ...model.updatable ? [
172
+ ...entity.updatable ? [
137
173
  {
138
174
  name: "updatedAt",
139
175
  type: "DateTime",
140
176
  nonNull: true,
141
177
  orderable: true,
142
178
  generated: true,
143
- ...typeof model.updatable === "object" && model.updatable.updatedAt
179
+ ...typeof entity.updatable === "object" && entity.updatable.updatedAt
144
180
  },
145
181
  {
146
182
  name: "updatedBy",
147
183
  kind: "relation",
148
184
  type: "User",
149
185
  nonNull: true,
150
- reverse: `updated${getModelPlural(model)}`,
186
+ reverse: `updated${getModelPlural(entity)}`,
151
187
  generated: true,
152
- ...typeof model.updatable === "object" && model.updatable.updatedBy
188
+ ...typeof entity.updatable === "object" && entity.updatable.updatedBy
153
189
  }
154
190
  ] : [],
155
- ...model.deletable ? [
191
+ ...entity.deletable ? [
156
192
  {
157
193
  name: "deleted",
158
194
  type: "Boolean",
@@ -160,65 +196,326 @@ var getModels = (rawModels) => {
160
196
  defaultValue: false,
161
197
  filterable: { default: false },
162
198
  generated: true,
163
- ...typeof model.deletable === "object" && model.deletable.deleted
199
+ ...typeof entity.deletable === "object" && entity.deletable.deleted
164
200
  },
165
201
  {
166
202
  name: "deletedAt",
167
203
  type: "DateTime",
168
204
  orderable: true,
169
205
  generated: true,
170
- ...typeof model.deletable === "object" && model.deletable.deletedAt
206
+ ...typeof entity.deletable === "object" && entity.deletable.deletedAt
171
207
  },
172
208
  {
173
209
  name: "deletedBy",
174
210
  kind: "relation",
175
211
  type: "User",
176
- reverse: `deleted${getModelPlural(model)}`,
212
+ reverse: `deleted${getModelPlural(entity)}`,
177
213
  generated: true,
178
- ...typeof model.deletable === "object" && model.deletable.deletedBy
214
+ ...typeof entity.deletable === "object" && entity.deletable.deletedBy
179
215
  }
180
216
  ] : []
181
- ].map((field) => ({
182
- ...field,
183
- ...field.kind === "relation" && {
184
- foreignKey: field.foreignKey || `${field.name}Id`
217
+ ];
218
+ for (const field of entity.fields) {
219
+ if (field.kind === "relation") {
220
+ field.foreignKey = field.foreignKey || `${field.name}Id`;
185
221
  }
186
- }))
187
- };
188
- for (const field of objectModel.fields) {
189
- objectModel.fieldsByName[field.name] = field;
222
+ }
190
223
  }
191
- return objectModel;
192
- });
193
- for (const model of models) {
194
- for (const field of model.fields) {
195
- if (field.kind !== "relation") {
196
- continue;
224
+ for (const model of entities) {
225
+ if (model.parent) {
226
+ const parent = summonByName(entities, model.parent);
227
+ const INHERITED_FIELDS = ["queriable", "listQueriable", "creatable", "updatable", "deletable"];
228
+ Object.assign(model, (0, import_lodash2.pick)(parent, INHERITED_FIELDS), (0, import_lodash2.pick)(model, INHERITED_FIELDS));
229
+ model.fields = [
230
+ ...parent.fields.map((field) => ({
231
+ ...field,
232
+ ...field.kind === "relation" && field.reverse && { reverse: field.reverse.replace(getModelPlural(parent), getModelPlural(model)) },
233
+ ...model.fields.find((childField) => childField.name === field.name),
234
+ inherited: true
235
+ })),
236
+ ...model.fields.filter((field) => !parent.fields.some((parentField) => parentField.name === field.name))
237
+ ];
197
238
  }
198
- const fieldModel = summonByName(models, field.type);
199
- const reverseRelation = {
200
- kind: "relation",
201
- name: field.reverse || (field.toOne ? typeToField(model.name) : getModelPluralField(model)),
202
- foreignKey: get(field, "foreignKey"),
203
- type: model.name,
204
- toOne: !!field.toOne,
205
- fieldModel,
206
- field,
207
- model
208
- };
209
- const relation = {
210
- field,
211
- model: fieldModel,
212
- reverseRelation
213
- };
214
- model.relations.push(relation);
215
- model.relationsByName[relation.field.name] = relation;
216
- fieldModel.reverseRelations.push(reverseRelation);
217
- fieldModel.reverseRelationsByName[reverseRelation.name] = reverseRelation;
218
239
  }
240
+ this.models = this.definitions.map(
241
+ (definition) => new MODEL_KIND_TO_CLASS_MAPPING[definition.kind](this, definition)
242
+ );
243
+ for (const model of this.models) {
244
+ this.modelsByName[model.name] = model;
245
+ }
246
+ this.entities = this.models.filter((model) => model instanceof EntityModel);
247
+ this.enums = this.models.filter((model) => model instanceof EnumModel);
248
+ this.inputs = this.models.filter((model) => model instanceof InputModel);
249
+ this.interfaces = this.models.filter((model) => model instanceof InterfaceModel);
250
+ this.objects = this.models.filter((model) => model instanceof ObjectModel);
251
+ this.rawEnums = this.models.filter((model) => model instanceof RawEnumModel);
252
+ this.scalars = this.models.filter((model) => model instanceof ScalarModel);
253
+ }
254
+ getModel(name2, kind) {
255
+ const model = get(this.modelsByName, name2);
256
+ if (!kind) {
257
+ return model;
258
+ }
259
+ const expectedType = MODEL_KIND_TO_CLASS_MAPPING[kind];
260
+ if (!(model instanceof expectedType)) {
261
+ throw new Error(`Model ${name2} is not of kind ${kind}.`);
262
+ }
263
+ return model;
264
+ }
265
+ };
266
+ var Model = class {
267
+ constructor(models, definition) {
268
+ this.models = models;
269
+ Object.assign(this, definition);
270
+ this.plural = definition.plural || (0, import_inflection.pluralize)(definition.name);
271
+ }
272
+ name;
273
+ plural;
274
+ description;
275
+ };
276
+ var ScalarModel = class extends Model {
277
+ kind;
278
+ };
279
+ var EnumModel = class extends Model {
280
+ kind;
281
+ values;
282
+ deleted;
283
+ constructor(models, definition) {
284
+ super(models, definition);
285
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
286
+ }
287
+ };
288
+ var RawEnumModel = class extends Model {
289
+ kind;
290
+ values;
291
+ constructor(models, definition) {
292
+ super(models, definition);
293
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
294
+ }
295
+ };
296
+ var InterfaceModel = class extends Model {
297
+ kind;
298
+ fields;
299
+ constructor(models, definition) {
300
+ super(models, definition);
301
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
219
302
  }
220
- return models;
221
303
  };
304
+ var InputModel = class extends Model {
305
+ kind;
306
+ fields;
307
+ constructor(models, definition) {
308
+ super(models, definition);
309
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
310
+ }
311
+ };
312
+ var ObjectModel = class extends Model {
313
+ kind;
314
+ fields;
315
+ constructor(models, definition) {
316
+ super(models, definition);
317
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
318
+ }
319
+ };
320
+ var EntityModel = class extends Model {
321
+ kind;
322
+ root;
323
+ parent;
324
+ interfaces;
325
+ queriable;
326
+ listQueriable;
327
+ creatable;
328
+ updatable;
329
+ deletable;
330
+ displayField;
331
+ defaultOrderBy;
332
+ fields;
333
+ // temporary fields for the generation of migrations
334
+ deleted;
335
+ oldName;
336
+ fieldsByName = {};
337
+ fieldsByColumnName = {};
338
+ _relations;
339
+ _relationsByName;
340
+ _reverseRelations;
341
+ _reverseRelationsByName;
342
+ _manyToManyRelations;
343
+ _manyToManyRelationsByName;
344
+ pluralField;
345
+ slug;
346
+ labelPlural;
347
+ label;
348
+ _parentModel;
349
+ constructor(models, definition) {
350
+ super(models, definition);
351
+ Object.assign(this, (0, import_lodash2.omit)(definition, "name", "plural", "description"));
352
+ this.pluralField = typeToField(this.plural);
353
+ this.slug = (0, import_lodash2.kebabCase)(this.plural);
354
+ this.labelPlural = getLabel(this.plural);
355
+ this.label = getLabel(definition.name);
356
+ for (const field of definition.fields) {
357
+ this.fieldsByName[field.name] = field;
358
+ }
359
+ }
360
+ getField(name2) {
361
+ return get(this.fieldsByName, name2);
362
+ }
363
+ get relations() {
364
+ if (!this._relations) {
365
+ this._relations = this.fields.filter(isRelation).map((relationField) => new NormalRelation(this, relationField, this.models.getModel(relationField.type, "entity")));
366
+ }
367
+ return this._relations;
368
+ }
369
+ get relationsByName() {
370
+ if (!this._relationsByName) {
371
+ this._relationsByName = {};
372
+ for (const relation of this.relations) {
373
+ this._relationsByName[relation.name] = relation;
374
+ }
375
+ }
376
+ return this._relationsByName;
377
+ }
378
+ getRelation(name2) {
379
+ return get(this.relationsByName, name2);
380
+ }
381
+ get reverseRelations() {
382
+ if (!this._reverseRelations) {
383
+ this._reverseRelations = this.models.entities.flatMap(
384
+ (model) => model.relations.filter((relation) => relation.targetModel.name === this.name || relation.targetModel.name === this.rootModel.name).map((relation) => relation.reverse)
385
+ );
386
+ }
387
+ return this._reverseRelations;
388
+ }
389
+ get reverseRelationsByName() {
390
+ if (!this._reverseRelationsByName) {
391
+ this._reverseRelationsByName = {};
392
+ for (const reverseRelation of this.reverseRelations) {
393
+ this._reverseRelationsByName[reverseRelation.name] = reverseRelation;
394
+ }
395
+ }
396
+ return this._reverseRelationsByName;
397
+ }
398
+ getReverseRelation(name2) {
399
+ return get(this.reverseRelationsByName, name2);
400
+ }
401
+ get manyToManyRelations() {
402
+ if (!this._manyToManyRelations) {
403
+ this._manyToManyRelations = [];
404
+ for (const relationFromSource of this.reverseRelations) {
405
+ const relationToTarget = relationFromSource.targetModel.relations.find(
406
+ (relation) => !relation.field.generated && relation.field.name !== relationFromSource.field.name
407
+ );
408
+ if (!relationToTarget) {
409
+ continue;
410
+ }
411
+ const inapplicableFields = relationFromSource.targetModel.fields.filter(
412
+ (otherField) => !otherField.generated && ![relationFromSource.field.name, relationToTarget.field.name].includes(otherField.name)
413
+ );
414
+ if (inapplicableFields.length) {
415
+ continue;
416
+ }
417
+ this._manyToManyRelations.push(new ManyToManyRelation(relationFromSource, relationToTarget));
418
+ }
419
+ }
420
+ return this._manyToManyRelations;
421
+ }
422
+ get manyToManyRelationsByName() {
423
+ if (!this._manyToManyRelationsByName) {
424
+ this._manyToManyRelationsByName = {};
425
+ for (const manyToManyRelation of this.manyToManyRelations) {
426
+ this._manyToManyRelationsByName[manyToManyRelation.name] = manyToManyRelation;
427
+ }
428
+ }
429
+ return this._manyToManyRelationsByName;
430
+ }
431
+ getManyToManyRelation(name2) {
432
+ return get(this.manyToManyRelationsByName, name2);
433
+ }
434
+ get parentModel() {
435
+ if (this.parent) {
436
+ if (!this._parentModel) {
437
+ this._parentModel = this.models.getModel(this.parent, "entity");
438
+ }
439
+ return this._parentModel;
440
+ }
441
+ }
442
+ get rootModel() {
443
+ return this.parentModel || this;
444
+ }
445
+ };
446
+ var MODEL_KIND_TO_CLASS_MAPPING = {
447
+ entity: EntityModel,
448
+ enum: EnumModel,
449
+ input: InputModel,
450
+ interface: InterfaceModel,
451
+ object: ObjectModel,
452
+ "raw-enum": RawEnumModel,
453
+ scalar: ScalarModel
454
+ };
455
+ var Relation = class {
456
+ constructor(name2, sourceModel, field, targetModel) {
457
+ this.name = name2;
458
+ this.sourceModel = sourceModel;
459
+ this.field = field;
460
+ this.targetModel = targetModel;
461
+ }
462
+ };
463
+ var NormalRelation = class extends Relation {
464
+ constructor(sourceModel, field, targetModel) {
465
+ super(field.name, sourceModel, field, targetModel);
466
+ this.field = field;
467
+ this.reverse = new ReverseRelation(this);
468
+ }
469
+ reverse;
470
+ };
471
+ var ReverseRelation = class extends Relation {
472
+ constructor(reverse) {
473
+ super(
474
+ reverse.field.reverse || (reverse.field.toOne ? typeToField(reverse.sourceModel.name) : reverse.sourceModel.pluralField),
475
+ reverse.targetModel,
476
+ reverse.field,
477
+ reverse.sourceModel
478
+ );
479
+ this.reverse = reverse;
480
+ }
481
+ };
482
+ var ManyToManyRelation = class {
483
+ name;
484
+ sourceModel;
485
+ relationFromSource;
486
+ relationModel;
487
+ relationToTarget;
488
+ targetModel;
489
+ constructor(relationFromSource, relationToTarget) {
490
+ this.name = relationFromSource.name;
491
+ this.sourceModel = relationFromSource.sourceModel;
492
+ this.relationFromSource = relationFromSource;
493
+ this.relationModel = relationFromSource.targetModel;
494
+ if (this.relationModel !== relationToTarget.sourceModel) {
495
+ throw new Error(`Relation model is ambiguous.`);
496
+ }
497
+ this.relationToTarget = relationToTarget;
498
+ this.targetModel = relationToTarget.targetModel;
499
+ }
500
+ };
501
+ var isEntityModelDefinition = (definition) => definition.kind === "entity";
502
+ var getModelPlural = (model) => model.plural || (0, import_inflection.pluralize)(model.name);
503
+
504
+ // src/models/utils.ts
505
+ var typeToField = (type) => type.substr(0, 1).toLowerCase() + type.substr(1);
506
+ var getLabel = (s) => (0, import_lodash3.startCase)((0, import_lodash3.camelCase)(s));
507
+ var and = (...predicates) => (field) => predicates.every((predicate) => predicate(field));
508
+ var not = (predicate) => (field) => !predicate(field);
509
+ var isRootModel = (model) => model.root;
510
+ var isUpdatableModel = (model) => model.updatable && model.fields.some(isUpdatableField);
511
+ var isUpdatableField = (field) => !field.inherited && !!field.updatable;
512
+ var modelNeedsTable = (model) => model.fields.some((field) => !field.inherited);
513
+ var isRelation = (field) => field.kind === "relation";
514
+ var isInherited = (field) => field.inherited;
515
+ var isInTable = (field) => field.name === "id" || !field.inherited;
516
+ var isQueriableField = ({ queriable }) => queriable !== false;
517
+ var isCustomField = (field) => field.kind === "custom";
518
+ var isSimpleField = and(not(isRelation), not(isCustomField));
222
519
  var summonByName = (array, value2) => summonByKey(array, "name", value2);
223
520
  var summonByKey = (array, key, value2) => summon(array, (element) => (0, import_get.default)(element, key) === value2, `No element found with ${key} ${value2}`);
224
521
  var summon = (array, cb, errorMessage) => {
@@ -243,8 +540,9 @@ var it = (object2) => {
243
540
  var get = (object2, key) => {
244
541
  const value2 = it(object2)[key];
245
542
  if (value2 === void 0 || value2 === null) {
246
- console.trace();
247
- throw new Error(`Object doesn't have ${String(key)}`);
543
+ const error = new Error(`Object doesn't have ${String(key)}`);
544
+ console.warn(error);
545
+ throw error;
248
546
  }
249
547
  return value2;
250
548
  };
@@ -261,56 +559,59 @@ var PRIMITIVE_TYPES = {
261
559
  DateTime: "DateTime | string"
262
560
  };
263
561
  var OPTIONAL_SEED_FIELDS = ["createdAt", "createdById", "updatedAt", "updatedById", "deletedAt", "deletedById"];
264
- var generateDBModels = (rawModels) => {
562
+ var generateDBModels = (models) => {
265
563
  const writer = new import_code_block_writer.default["default"]({
266
564
  useSingleQuote: true,
267
565
  indentNumberOfSpaces: 2
268
566
  });
269
567
  writer.write(`import { DateTime } from 'luxon';`).blankLine();
270
- for (const enm2 of rawModels.filter(isEnumModel)) {
568
+ for (const enm2 of models.enums) {
271
569
  writer.write(`export type ${enm2.name} = ${enm2.values.map((v) => `'${v}'`).join(" | ")};`).blankLine();
272
570
  }
273
- const models = getModels(rawModels);
274
- for (const model of models) {
275
- const fields2 = model.fields.some((field) => field.kind === "relation" && field.foreignKey === "id") ? model.fields.filter((field) => field.name !== "id") : model.fields;
571
+ for (const model of models.entities) {
572
+ const fields2 = model.relations.some((relation) => relation.field.foreignKey === "id") ? model.fields.filter((field) => field.name !== "id") : model.fields;
276
573
  writer.write(`export type ${model.name} = `).inlineBlock(() => {
277
574
  for (const field of fields2.filter(not(isCustomField))) {
278
- writer.write(`'${getFieldName(field)}': ${getFieldType(field)}${field.nonNull ? "" : " | null"},`).newLine();
575
+ writer.write(`'${getColumnName(field)}': ${getFieldType(field)}${field.nonNull ? "" : " | null"};`).newLine();
279
576
  }
280
577
  }).blankLine();
281
578
  writer.write(`export type ${model.name}Initializer = `).inlineBlock(() => {
282
- for (const field of fields2.filter(not(isCustomField))) {
579
+ for (const field of fields2.filter(not(isCustomField)).filter(isInTable)) {
283
580
  writer.write(
284
- `'${getFieldName(field)}'${field.nonNull && field.defaultValue === void 0 ? "" : "?"}: ${getFieldType(
581
+ `'${getColumnName(field)}'${field.nonNull && field.defaultValue === void 0 ? "" : "?"}: ${getFieldType(
285
582
  field
286
- )}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"},`
583
+ )}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"};`
287
584
  ).newLine();
288
585
  }
289
586
  }).blankLine();
290
587
  writer.write(`export type ${model.name}Mutator = `).inlineBlock(() => {
291
- for (const field of fields2.filter(not(isCustomField))) {
588
+ for (const field of fields2.filter(not(isCustomField)).filter(isInTable)) {
292
589
  writer.write(
293
- `'${getFieldName(field)}'?: ${getFieldType(field)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"},`
294
- ).newLine();
295
- }
296
- }).blankLine();
297
- writer.write(`export type ${model.name}Seed = `).inlineBlock(() => {
298
- for (const field of fields2.filter(not(isCustomField))) {
299
- const fieldName = getFieldName(field);
300
- writer.write(
301
- `'${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"},`
590
+ `'${getColumnName(field)}'?: ${getFieldType(field)}${field.list ? " | string" : ""}${field.nonNull ? "" : " | null"};`
302
591
  ).newLine();
303
592
  }
304
593
  }).blankLine();
594
+ if (!isRootModel(model)) {
595
+ writer.write(`export type ${model.name}Seed = `).inlineBlock(() => {
596
+ for (const field of fields2.filter(not(isCustomField))) {
597
+ if (model.parent && field.name === "type") {
598
+ continue;
599
+ }
600
+ const fieldName = getColumnName(field);
601
+ writer.write(
602
+ `'${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"};`
603
+ ).newLine();
604
+ }
605
+ }).blankLine();
606
+ }
305
607
  }
306
608
  writer.write(`export type SeedData = `).inlineBlock(() => {
307
- for (const model of models) {
308
- writer.write(`${model.name}: ${model.name}Seed[],`).newLine();
609
+ for (const model of models.entities.filter(not(isRootModel))) {
610
+ writer.write(`${model.name}: ${model.name}Seed[];`).newLine();
309
611
  }
310
612
  });
311
613
  return writer.toString();
312
614
  };
313
- var getFieldName = (field) => field.kind === "relation" ? field.foreignKey || `${field.name}Id` : field.name;
314
615
  var getFieldType = (field) => {
315
616
  const kind = field.kind;
316
617
  switch (kind) {
@@ -331,19 +632,18 @@ var getFieldType = (field) => {
331
632
  }
332
633
  }
333
634
  };
334
- var generateKnexTables = (rawModels) => {
635
+ var generateKnexTables = (models) => {
335
636
  const writer = new import_code_block_writer.default["default"]({
336
637
  useSingleQuote: true,
337
638
  indentNumberOfSpaces: 2
338
639
  });
339
- const models = getModels(rawModels);
340
640
  writer.write(`import { Knex } from 'knex';`).newLine();
341
641
  writer.write(
342
- `import { ${models.map((model) => `${model.name}, ${model.name}Initializer, ${model.name}Mutator`).join(", ")} } from '.';`
642
+ `import { ${models.entities.map((model) => `${model.name}, ${model.name}Initializer, ${model.name}Mutator`).join(", ")} } from '.';`
343
643
  ).blankLine();
344
644
  writer.write(`declare module 'knex/types/tables' `).inlineBlock(() => {
345
645
  writer.write(`interface Tables `).inlineBlock(() => {
346
- for (const model of models) {
646
+ for (const model of models.entities) {
347
647
  writer.write(`'${model.name}': Knex.CompositeTableType<${model.name}, ${model.name}Initializer, ${model.name}Mutator>,`).newLine();
348
648
  }
349
649
  });
@@ -356,10 +656,9 @@ var import_code_block_writer2 = __toESM(require("code-block-writer"), 1);
356
656
  var import_knex_schema_inspector = require("knex-schema-inspector");
357
657
  var import_lowerFirst = __toESM(require("lodash/lowerFirst"), 1);
358
658
  var MigrationGenerator = class {
359
- constructor(knex2, rawModels) {
360
- this.rawModels = rawModels;
659
+ constructor(knex2, models) {
660
+ this.models = models;
361
661
  this.schema = (0, import_knex_schema_inspector.SchemaInspector)(knex2);
362
- this.models = getModels(rawModels);
363
662
  }
364
663
  writer = new import_code_block_writer2.default["default"]({
365
664
  useSingleQuote: true,
@@ -369,9 +668,8 @@ var MigrationGenerator = class {
369
668
  columns = {};
370
669
  uuidUsed;
371
670
  nowUsed;
372
- models;
373
671
  async generate() {
374
- const { writer, schema, rawModels, models } = this;
672
+ const { writer, schema, models } = this;
375
673
  const enums = (await schema.knex("pg_type").where({ typtype: "e" }).select("typname")).map(({ typname }) => typname);
376
674
  const tables = await schema.tables();
377
675
  for (const table of tables) {
@@ -380,11 +678,11 @@ var MigrationGenerator = class {
380
678
  const up = [];
381
679
  const down = [];
382
680
  this.createEnums(
383
- rawModels.filter(isEnumModel).filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
681
+ this.models.enums.filter((enm2) => !enums.includes((0, import_lowerFirst.default)(enm2.name))),
384
682
  up,
385
683
  down
386
684
  );
387
- for (const model of models) {
685
+ for (const model of models.entities) {
388
686
  if (model.deleted) {
389
687
  up.push(() => {
390
688
  this.dropTable(model.name);
@@ -396,7 +694,7 @@ var MigrationGenerator = class {
396
694
  }
397
695
  });
398
696
  });
399
- if (model.updatable) {
697
+ if (isUpdatableModel(model)) {
400
698
  up.push(() => {
401
699
  this.dropTable(`${model.name}Revision`);
402
700
  });
@@ -415,7 +713,7 @@ var MigrationGenerator = class {
415
713
  tables[tables.indexOf(model.oldName)] = model.name;
416
714
  this.columns[model.name] = this.columns[model.oldName];
417
715
  delete this.columns[model.oldName];
418
- if (model.updatable) {
716
+ if (isUpdatableModel(model)) {
419
717
  up.push(() => {
420
718
  this.renameTable(`${model.oldName}Revision`, `${model.name}Revision`);
421
719
  this.alterTable(`${model.name}Revision`, () => {
@@ -433,92 +731,102 @@ var MigrationGenerator = class {
433
731
  delete this.columns[`${model.oldName}Revision`];
434
732
  }
435
733
  }
436
- if (!tables.includes(model.name)) {
437
- up.push(() => {
438
- this.createTable(model.name, () => {
439
- for (const field of model.fields) {
440
- this.column(field);
441
- }
442
- });
443
- });
444
- down.push(() => {
445
- this.dropTable(model.name);
446
- });
447
- } else {
448
- this.renameFields(
449
- model,
450
- model.fields.filter(({ oldName }) => oldName),
451
- up,
452
- down
453
- );
454
- this.createFields(
455
- model,
456
- model.fields.filter(
457
- ({ name: name2, ...field }) => field.kind !== "custom" && !this.columns[model.name].some(
458
- (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
459
- )
460
- ),
461
- up,
462
- down
463
- );
464
- const existingFields = model.fields.filter(({ name: name2, kind, nonNull: nonNull2 }) => {
465
- const col = this.columns[model.name].find((col2) => col2.name === (kind === "relation" ? `${name2}Id` : name2));
466
- if (!col) {
467
- return false;
468
- }
469
- return !nonNull2 && !col.is_nullable;
470
- });
471
- this.updateFields(model, existingFields, up, down);
472
- }
473
- if (model.updatable) {
474
- if (!tables.includes(`${model.name}Revision`)) {
734
+ if (modelNeedsTable(model)) {
735
+ if (!tables.includes(model.name)) {
475
736
  up.push(() => {
476
- this.createRevisionTable(model);
477
- });
478
- if (tables.includes(model.name)) {
479
- up.push(() => {
480
- writer.block(() => {
481
- writer.writeLine(`const data = await knex('${model.name}');`);
482
- writer.write(`if (data.length)`).block(() => {
483
- writer.write(`await knex.batchInsert('${model.name}Revision', data.map((row) => (`).inlineBlock(() => {
484
- writer.writeLine(`id: uuid(),`);
485
- writer.writeLine(`${typeToField(model.name)}Id: row.id,`);
486
- this.nowUsed = true;
487
- writer.writeLine(`createdAt: row.updatedAt || row.createdAt || now,`);
488
- writer.writeLine(`createdById: row.updatedById || row.createdById,`);
489
- if (model.deletable) {
490
- writer.writeLine(`deleted: row.deleted,`);
491
- }
492
- for (const { name: name2, kind } of model.fields.filter(({ updatable }) => updatable)) {
493
- const col = kind === "relation" ? `${name2}Id` : name2;
494
- writer.writeLine(`${col}: row.${col},`);
495
- }
496
- }).write(")));").newLine();
737
+ this.createTable(model.name, () => {
738
+ if (model.parent) {
739
+ this.column({
740
+ ...model.fieldsByName.id,
741
+ kind: "relation",
742
+ type: model.parent,
743
+ foreignKey: "id"
497
744
  });
498
- }).blankLine();
745
+ }
746
+ for (const field of model.fields.filter(not(isInherited))) {
747
+ this.column(field);
748
+ }
499
749
  });
500
- }
750
+ });
501
751
  down.push(() => {
502
- this.dropTable(`${model.name}Revision`);
752
+ this.dropTable(model.name);
503
753
  });
504
754
  } else {
505
- const revisionTable = `${model.name}Revision`;
506
- const missingRevisionFields = model.fields.filter(
507
- ({ name: name2, updatable, ...field }) => field.kind !== "custom" && updatable && !this.columns[revisionTable].some(
508
- (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
509
- )
755
+ this.renameFields(
756
+ model,
757
+ model.fields.filter(not(isInherited)).filter(({ oldName }) => oldName),
758
+ up,
759
+ down
510
760
  );
511
- this.createRevisionFields(model, missingRevisionFields, up, down);
512
- const revisionFieldsToRemove = model.fields.filter(
513
- ({ name: name2, updatable, generated, ...field }) => !generated && field.kind !== "custom" && !updatable && !(field.kind === "relation" && field.foreignKey === "id") && this.columns[revisionTable].some(
514
- (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
515
- )
761
+ this.createFields(
762
+ model,
763
+ model.fields.filter(not(isInherited)).filter(
764
+ ({ name: name2, ...field }) => field.kind !== "custom" && !this.columns[model.name].some(
765
+ (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
766
+ )
767
+ ),
768
+ up,
769
+ down
516
770
  );
517
- this.createRevisionFields(model, revisionFieldsToRemove, down, up);
771
+ const existingFields = model.fields.filter(({ name: name2, kind, nonNull: nonNull2 }) => {
772
+ const col = this.columns[model.name].find((col2) => col2.name === (kind === "relation" ? `${name2}Id` : name2));
773
+ if (!col) {
774
+ return false;
775
+ }
776
+ return !nonNull2 && !col.is_nullable;
777
+ });
778
+ this.updateFields(model, existingFields, up, down);
779
+ }
780
+ if (isUpdatableModel(model)) {
781
+ if (!tables.includes(`${model.name}Revision`)) {
782
+ up.push(() => {
783
+ this.createRevisionTable(model);
784
+ });
785
+ if (tables.includes(model.name)) {
786
+ up.push(() => {
787
+ writer.block(() => {
788
+ writer.writeLine(`const data = await knex('${model.name}');`);
789
+ writer.write(`if (data.length)`).block(() => {
790
+ writer.write(`await knex.batchInsert('${model.name}Revision', data.map((row) => (`).inlineBlock(() => {
791
+ writer.writeLine(`id: uuid(),`);
792
+ writer.writeLine(`${typeToField(model.name)}Id: row.id,`);
793
+ this.nowUsed = true;
794
+ writer.writeLine(`createdAt: row.updatedAt || row.createdAt || now,`);
795
+ writer.writeLine(`createdById: row.updatedById || row.createdById,`);
796
+ if (model.deletable) {
797
+ writer.writeLine(`deleted: row.deleted,`);
798
+ }
799
+ for (const { name: name2, kind } of model.fields.filter(isUpdatableField)) {
800
+ const col = kind === "relation" ? `${name2}Id` : name2;
801
+ writer.writeLine(`${col}: row.${col},`);
802
+ }
803
+ }).write(")));").newLine();
804
+ });
805
+ }).blankLine();
806
+ });
807
+ }
808
+ down.push(() => {
809
+ this.dropTable(`${model.name}Revision`);
810
+ });
811
+ } else {
812
+ const revisionTable = `${model.name}Revision`;
813
+ const missingRevisionFields = model.fields.filter(isUpdatableField).filter(
814
+ ({ name: name2, ...field }) => field.kind !== "custom" && !this.columns[revisionTable].some(
815
+ (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
816
+ )
817
+ );
818
+ this.createRevisionFields(model, missingRevisionFields, up, down);
819
+ const revisionFieldsToRemove = model.fields.filter(
820
+ ({ name: name2, updatable, generated, ...field }) => !generated && field.kind !== "custom" && !updatable && !(field.kind === "relation" && field.foreignKey === "id") && this.columns[revisionTable].some(
821
+ (col) => col.name === (field.kind === "relation" ? field.foreignKey || `${name2}Id` : name2)
822
+ )
823
+ );
824
+ this.createRevisionFields(model, revisionFieldsToRemove, down, up);
825
+ }
518
826
  }
519
827
  }
520
828
  }
521
- for (const model of getModels(rawModels)) {
829
+ for (const model of models.entities) {
522
830
  if (tables.includes(model.name)) {
523
831
  this.createFields(
524
832
  model,
@@ -526,10 +834,10 @@ var MigrationGenerator = class {
526
834
  down,
527
835
  up
528
836
  );
529
- if (model.updatable) {
837
+ if (isUpdatableModel(model)) {
530
838
  this.createRevisionFields(
531
839
  model,
532
- model.fields.filter(({ deleted, updatable }) => updatable && deleted),
840
+ model.fields.filter(isUpdatableField).filter(({ deleted }) => deleted),
533
841
  down,
534
842
  up
535
843
  );
@@ -537,7 +845,7 @@ var MigrationGenerator = class {
537
845
  }
538
846
  }
539
847
  this.createEnums(
540
- rawModels.filter(isEnumModel).filter((enm2) => enm2.deleted),
848
+ this.models.enums.filter((enm2) => enm2.deleted),
541
849
  down,
542
850
  up
543
851
  );
@@ -645,8 +953,8 @@ var MigrationGenerator = class {
645
953
  }
646
954
  });
647
955
  });
648
- if (model.updatable) {
649
- const updatableFields = fields2.filter(({ updatable }) => updatable);
956
+ if (isUpdatableModel(model)) {
957
+ const updatableFields = fields2.filter(isUpdatableField);
650
958
  if (!updatableFields.length) {
651
959
  return;
652
960
  }
@@ -674,15 +982,15 @@ var MigrationGenerator = class {
674
982
  const writer = this.writer;
675
983
  this.createTable(`${model.name}Revision`, () => {
676
984
  writer.writeLine(`table.uuid('id').notNullable().primary();`);
677
- writer.writeLine(`table.uuid('${typeToField(model.name)}Id').notNullable();`);
678
- writer.write(`table.uuid('createdById')`);
679
- writer.write(".notNullable()");
680
- writer.write(";").newLine();
681
- writer.writeLine(`table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now(0));`);
682
- if (model.deletable) {
683
- writer.writeLine(`table.boolean('deleted').notNullable();`);
985
+ if (!model.parent) {
986
+ writer.writeLine(`table.uuid('${typeToField(model.name)}Id').notNullable();`);
987
+ writer.writeLine(`table.uuid('createdById').notNullable();`);
988
+ writer.writeLine(`table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now(0));`);
989
+ if (model.deletable) {
990
+ writer.writeLine(`table.boolean('deleted').notNullable();`);
991
+ }
684
992
  }
685
- for (const field of model.fields.filter((field2) => field2.updatable)) {
993
+ for (const field of model.fields.filter(and(isUpdatableField, not(isInherited)))) {
686
994
  this.column(field, { setUnique: false, setDefault: false });
687
995
  }
688
996
  });
@@ -730,7 +1038,7 @@ var MigrationGenerator = class {
730
1038
  `await knex.raw(\`CREATE TYPE "${name2}" AS ENUM (${enm2.values.map((value2) => `'${value2}'`).join(",")})\`);`
731
1039
  ).newLine()
732
1040
  );
733
- down.push(() => this.writer.writeLine(`await knex.raw('DROP TYPE "${name2}"')`));
1041
+ down.push(() => this.writer.writeLine(`await knex.raw('DROP TYPE "${name2}"');`));
734
1042
  }
735
1043
  }
736
1044
  migration(name2, cbs) {
@@ -810,7 +1118,7 @@ var MigrationGenerator = class {
810
1118
  if (field.double) {
811
1119
  col(`table.double('${name2}')`);
812
1120
  } else {
813
- col(`table.decimal('${name2}', ${get(field, "precision")}, ${get(field, "scale")})`);
1121
+ col(`table.decimal('${name2}', ${field.precision ?? "undefined"}, ${field.scale ?? "undefined"})`);
814
1122
  }
815
1123
  break;
816
1124
  case "String":
@@ -831,14 +1139,16 @@ var MigrationGenerator = class {
831
1139
  }
832
1140
  break;
833
1141
  case "relation":
834
- col(`table.uuid('${name2}Id')`);
1142
+ col(`table.uuid('${field.foreignKey}')`);
835
1143
  if (foreign && !alter) {
836
- this.writer.writeLine(`table.foreign('${name2}Id').references('id').inTable('${field.type}');`);
1144
+ this.writer.writeLine(
1145
+ `table.foreign('${field.foreignKey}').references('id').inTable('${field.type}').onDelete('CASCADE');`
1146
+ );
837
1147
  }
838
1148
  break;
839
1149
  case "enum":
840
1150
  if (list2) {
841
- this.writer.write(`table.specificType('${name2}', '"${typeToField(field.type)}"[]');`);
1151
+ this.writer.write(`table.specificType('${name2}', '"${typeToField(field.type)}"[]')`);
842
1152
  } else {
843
1153
  this.writer.write(`table.enum('${name2}', null as any, `).inlineBlock(() => {
844
1154
  this.writer.writeLine(`useNative: true,`);
@@ -876,6 +1186,8 @@ var import_graphql2 = require("graphql");
876
1186
 
877
1187
  // src/resolvers/utils.ts
878
1188
  var import_graphql3 = require("graphql");
1189
+ var import_lodash4 = require("lodash");
1190
+ var getColumnName = (field) => field.kind === "relation" ? field.foreignKey || `${field.name}Id` : field.name;
879
1191
 
880
1192
  // src/resolvers/arguments.ts
881
1193
  var import_graphql4 = require("graphql");
@@ -889,7 +1201,6 @@ var import_flatMap = __toESM(require("lodash/flatMap"), 1);
889
1201
 
890
1202
  // src/schema/generate.ts
891
1203
  var import_graphql5 = require("graphql");
892
- var import_flatMap2 = __toESM(require("lodash/flatMap"), 1);
893
1204
 
894
1205
  // src/schema/utils.ts
895
1206
  var import_luxon = require("luxon");
@@ -914,6 +1225,13 @@ var object = (nme, fds, interfaces, dvs) => ({
914
1225
  interfaces: interfaces && interfaces.map((i) => namedType(i)),
915
1226
  directives: directives(dvs)
916
1227
  });
1228
+ var iface = (nme, fds, interfaces, dvs) => ({
1229
+ name: name(nme),
1230
+ fields: fields(fds),
1231
+ kind: "InterfaceTypeDefinition",
1232
+ interfaces: interfaces && interfaces.map((i) => namedType(i)),
1233
+ directives: directives(dvs)
1234
+ });
917
1235
  var inputValues = (fields2) => fields2.map(
918
1236
  (field) => ({
919
1237
  kind: "InputValueDefinition",
@@ -1019,84 +1337,86 @@ var value = (val = null) => val === null ? {
1019
1337
  };
1020
1338
 
1021
1339
  // src/schema/generate.ts
1022
- var generateDefinitions = (rawModels) => {
1023
- const models = getModels(rawModels);
1340
+ var generateDefinitions = ({
1341
+ scalars,
1342
+ rawEnums,
1343
+ enums,
1344
+ inputs,
1345
+ interfaces,
1346
+ entities,
1347
+ objects
1348
+ }) => {
1024
1349
  return [
1025
1350
  // Predefined types
1026
- enm("Order", ["ASC", "DESC"]),
1027
- scalar("DateTime"),
1028
- scalar("Upload"),
1029
- ...rawModels.filter(isEnumModel).map((model) => enm(model.name, model.values)),
1030
- ...rawModels.filter(isRawEnumModel).map((model) => enm(model.name, model.values)),
1031
- ...rawModels.filter(isScalarModel).map((model) => scalar(model.name)),
1032
- ...rawModels.filter(isObjectModel).filter(({ name: name2 }) => !["Query", "Mutation"].includes(name2)).map((model) => object(model.name, model.fields)),
1033
- ...rawModels.filter(isInputModel).map((model) => input(model.name, model.fields)),
1034
- ...rawModels.filter(isObjectModel).filter(
1035
- (model) => models.some((m) => m.creatable && m.fields.some((f) => f.creatable && f.kind === "json" && f.type === model.name))
1351
+ ...rawEnums.map((model) => enm(model.name, model.values)),
1352
+ ...enums.map((model) => enm(model.name, model.values)),
1353
+ ...scalars.map((model) => scalar(model.name)),
1354
+ ...objects.filter(({ name: name2 }) => !["Query", "Mutation"].includes(name2)).map((model) => object(model.name, model.fields)),
1355
+ ...interfaces.map(({ name: name2, fields: fields2 }) => iface(name2, fields2)),
1356
+ ...inputs.map((model) => input(model.name, model.fields)),
1357
+ ...objects.filter(
1358
+ (model) => entities.some((m) => m.creatable && m.fields.some((f) => f.creatable && f.kind === "json" && f.type === model.name))
1036
1359
  ).map((model) => input(`Create${model.name}`, model.fields)),
1037
- ...rawModels.filter(isObjectModel).filter(
1038
- (model) => models.some((m) => m.creatable && m.fields.some((f) => f.creatable && f.kind === "json" && f.type === model.name))
1360
+ ...objects.filter(
1361
+ (model) => entities.some((m) => m.updatable && m.fields.some((f) => f.updatable && f.kind === "json" && f.type === model.name))
1039
1362
  ).map((model) => input(`Update${model.name}`, model.fields)),
1040
- ...(0, import_flatMap2.default)(
1041
- models.map((model) => {
1042
- const types = [
1043
- object(
1044
- model.name,
1045
- [
1046
- ...model.fields.filter(isQueriableField).map((field) => ({
1047
- ...field,
1048
- type: field.type,
1049
- args: [...field.args || []],
1050
- directives: field.directives
1051
- })),
1052
- ...model.reverseRelations.map(({ name: name2, field, model: model2 }) => ({
1053
- name: name2,
1054
- type: model2.name,
1055
- list: !field.toOne,
1056
- nonNull: !field.toOne,
1057
- args: [
1058
- { name: "where", type: `${model2.name}Where` },
1059
- ...model2.fields.some(({ searchable }) => searchable) ? [{ name: "search", type: "String" }] : [],
1060
- ...model2.fields.some(({ orderable }) => orderable) ? [{ name: "orderBy", type: `${model2.name}OrderBy`, list: true }] : [],
1061
- { name: "limit", type: "Int" },
1062
- { name: "offset", type: "Int" }
1063
- ]
1064
- }))
1065
- ],
1066
- model.interfaces
1067
- ),
1068
- input(`${model.name}Where`, [
1069
- ...model.fields.filter(({ kind, unique, filterable }) => (unique || filterable) && kind !== "relation").map((field) => ({
1070
- name: field.name,
1363
+ ...entities.flatMap((model) => {
1364
+ const types = [
1365
+ (isRootModel(model) ? iface : object)(
1366
+ model.name,
1367
+ [
1368
+ ...model.fields.filter(isQueriableField).map((field) => ({
1369
+ ...field,
1071
1370
  type: field.type,
1072
- list: true,
1073
- default: typeof field.filterable === "object" ? field.filterable.default : void 0
1371
+ args: [...field.args || []],
1372
+ directives: field.directives
1074
1373
  })),
1075
- ...(0, import_flatMap2.default)(
1076
- model.fields.filter(({ comparable }) => comparable),
1077
- (field) => [
1078
- { name: `${field.name}_GT`, type: field.type },
1079
- { name: `${field.name}_GTE`, type: field.type },
1080
- { name: `${field.name}_LT`, type: field.type },
1081
- { name: `${field.name}_LTE`, type: field.type }
1082
- ]
1083
- ),
1084
- ...model.fields.filter(isRelation).filter(({ filterable }) => filterable).map(({ name: name2, type }) => ({
1374
+ ...model.reverseRelations.map(({ name: name2, field, targetModel }) => ({
1085
1375
  name: name2,
1086
- type: `${type}Where`
1376
+ type: targetModel.name,
1377
+ list: !field.toOne,
1378
+ nonNull: !field.toOne,
1379
+ args: [
1380
+ { name: "where", type: `${targetModel.name}Where` },
1381
+ ...targetModel.fields.some(({ searchable }) => searchable) ? [{ name: "search", type: "String" }] : [],
1382
+ ...targetModel.fields.some(({ orderable }) => orderable) ? [{ name: "orderBy", type: `${targetModel.name}OrderBy`, list: true }] : [],
1383
+ { name: "limit", type: "Int" },
1384
+ { name: "offset", type: "Int" }
1385
+ ]
1087
1386
  }))
1387
+ ],
1388
+ [...model.parent ? [model.parent] : [], ...model.interfaces || []]
1389
+ ),
1390
+ input(`${model.name}Where`, [
1391
+ ...model.fields.filter(({ kind, unique, filterable }) => (unique || filterable) && kind !== "relation").map((field) => ({
1392
+ name: field.name,
1393
+ type: field.type,
1394
+ list: true,
1395
+ default: typeof field.filterable === "object" ? field.filterable.default : void 0
1396
+ })),
1397
+ ...model.fields.filter(({ comparable }) => comparable).flatMap((field) => [
1398
+ { name: `${field.name}_GT`, type: field.type },
1399
+ { name: `${field.name}_GTE`, type: field.type },
1400
+ { name: `${field.name}_LT`, type: field.type },
1401
+ { name: `${field.name}_LTE`, type: field.type }
1088
1402
  ]),
1403
+ ...model.fields.filter(isRelation).filter(({ filterable }) => filterable).map(({ name: name2, type }) => ({
1404
+ name: name2,
1405
+ type: `${type}Where`
1406
+ }))
1407
+ ]),
1408
+ input(
1409
+ `${model.name}WhereUnique`,
1410
+ model.fields.filter(({ unique }) => unique).map((field) => ({ name: field.name, type: field.type }))
1411
+ ),
1412
+ ...model.fields.some(({ orderable }) => orderable) ? [
1089
1413
  input(
1090
- `${model.name}WhereUnique`,
1091
- model.fields.filter(({ unique }) => unique).map((field) => ({ name: field.name, type: field.type }))
1092
- ),
1093
- ...model.fields.some(({ orderable }) => orderable) ? [
1094
- input(
1095
- `${model.name}OrderBy`,
1096
- model.fields.filter(({ orderable }) => orderable).map(({ name: name2 }) => ({ name: name2, type: "Order" }))
1097
- )
1098
- ] : []
1099
- ];
1414
+ `${model.name}OrderBy`,
1415
+ model.fields.filter(({ orderable }) => orderable).map(({ name: name2 }) => ({ name: name2, type: "Order" }))
1416
+ )
1417
+ ] : []
1418
+ ];
1419
+ if (!isRootModel(model)) {
1100
1420
  if (model.creatable) {
1101
1421
  types.push(
1102
1422
  input(
@@ -1126,15 +1446,15 @@ var generateDefinitions = (rawModels) => {
1126
1446
  )
1127
1447
  );
1128
1448
  }
1129
- return types;
1130
- })
1131
- ),
1449
+ }
1450
+ return types;
1451
+ }),
1132
1452
  object("Query", [
1133
1453
  {
1134
1454
  name: "me",
1135
1455
  type: "User"
1136
1456
  },
1137
- ...models.filter(({ queriable }) => queriable).map(({ name: name2 }) => ({
1457
+ ...entities.filter(({ queriable }) => queriable).map(({ name: name2 }) => ({
1138
1458
  name: typeToField(name2),
1139
1459
  type: name2,
1140
1460
  nonNull: true,
@@ -1146,8 +1466,8 @@ var generateDefinitions = (rawModels) => {
1146
1466
  }
1147
1467
  ]
1148
1468
  })),
1149
- ...models.filter(({ listQueriable }) => listQueriable).map((model) => ({
1150
- name: getModelPluralField(model),
1469
+ ...entities.filter(({ listQueriable }) => listQueriable).map((model) => ({
1470
+ name: model.pluralField,
1151
1471
  type: model.name,
1152
1472
  list: true,
1153
1473
  nonNull: true,
@@ -1159,12 +1479,12 @@ var generateDefinitions = (rawModels) => {
1159
1479
  { name: "offset", type: "Int" }
1160
1480
  ]
1161
1481
  })),
1162
- ...rawModels.filter(isObjectModel).filter((model) => model.name === "Query").flatMap((model) => model.fields)
1482
+ ...objects.filter((model) => model.name === "Query").flatMap((model) => model.fields)
1163
1483
  ]),
1164
1484
  object("Mutation", [
1165
- ...(0, import_flatMap2.default)(
1166
- models.map((model) => {
1167
- const mutations = [];
1485
+ ...entities.flatMap((model) => {
1486
+ const mutations = [];
1487
+ if (!isRootModel(model)) {
1168
1488
  if (model.creatable) {
1169
1489
  mutations.push({
1170
1490
  name: `create${model.name}`,
@@ -1228,14 +1548,14 @@ var generateDefinitions = (rawModels) => {
1228
1548
  ]
1229
1549
  });
1230
1550
  }
1231
- return mutations;
1232
- })
1233
- ),
1234
- ...rawModels.filter(isObjectModel).filter((model) => model.name === "Mutation").flatMap((model) => model.fields)
1551
+ }
1552
+ return mutations;
1553
+ }),
1554
+ ...objects.filter((model) => model.name === "Mutation").flatMap((model) => model.fields)
1235
1555
  ])
1236
1556
  ];
1237
1557
  };
1238
- var generate = (rawModels) => document(generateDefinitions(rawModels));
1558
+ var generate = (models) => document(generateDefinitions(models));
1239
1559
  var printSchema = (schema) => [
1240
1560
  ...schema.getDirectives().map((d) => d.astNode && (0, import_graphql5.print)(d.astNode)),
1241
1561
  ...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))
@@ -1267,9 +1587,9 @@ var readLine = (prompt) => {
1267
1587
 
1268
1588
  // src/bin/gqm/templates.ts
1269
1589
  var EMPTY_MODELS = `
1270
- import { RawModels, getModels } from '@smartive/graphql-magic';
1590
+ import { RawModels, Models } from '@smartive/graphql-magic';
1271
1591
 
1272
- export const rawModels: RawModels = [
1592
+ const rawModels: RawModels = [
1273
1593
  {
1274
1594
  kind: 'entity',
1275
1595
  name: 'User',
@@ -1277,9 +1597,22 @@ export const rawModels: RawModels = [
1277
1597
  },
1278
1598
  ]
1279
1599
 
1280
- export const models = getModels(rawModels);
1600
+ export const models = new Models(rawModels);
1281
1601
  `;
1282
1602
  var KNEXFILE = `
1603
+ import { DateTime } from 'luxon';
1604
+ import { types } from 'pg';
1605
+
1606
+ const dateOids = { date: 1082, timestamptz: 1184, timestamp: 1114 };
1607
+ for (const oid of Object.values(dateOids)) {
1608
+ types.setTypeParser(oid, (val) => DateTime.fromSQL(val));
1609
+ }
1610
+
1611
+ const numberOids = { int8: 20, float8: 701, numeric: 1700 };
1612
+ for (const oid of Object.values(numberOids)) {
1613
+ types.setTypeParser(oid, Number);
1614
+ }
1615
+
1283
1616
  const config = {
1284
1617
  client: 'postgresql',
1285
1618
  connection: {
@@ -1288,6 +1621,13 @@ const config = {
1288
1621
  user: process.env.DATABASE_USER,
1289
1622
  password: process.env.DATABASE_PASSWORD,
1290
1623
  },
1624
+ migrations: {
1625
+ tableName: 'knex_migrations',
1626
+ },
1627
+ pool: {
1628
+ min: 0,
1629
+ max: 30,
1630
+ },
1291
1631
  } as const;
1292
1632
 
1293
1633
  export default config;
@@ -1319,6 +1659,9 @@ var DEFAULTS = {
1319
1659
  init: (path) => {
1320
1660
  ensureDirectoryExists(path);
1321
1661
  }
1662
+ },
1663
+ gqlModule: {
1664
+ defaultValue: "@smartive/graphql-magic"
1322
1665
  }
1323
1666
  };
1324
1667
  var initSetting = async (name2) => {
@@ -1329,8 +1672,10 @@ var initSetting = async (name2) => {
1329
1672
  };
1330
1673
  var initSettings = async () => {
1331
1674
  const settings = {};
1332
- for (const name2 of Object.keys(DEFAULTS)) {
1333
- settings[name2] = await initSetting(name2);
1675
+ for (const [name2, config2] of Object.entries(DEFAULTS)) {
1676
+ if ("queston" in config2) {
1677
+ settings[name2] = await initSetting(name2);
1678
+ }
1334
1679
  }
1335
1680
  saveSettings(settings);
1336
1681
  };
@@ -1346,8 +1691,12 @@ var getSettings = async () => {
1346
1691
  var getSetting = async (name2) => {
1347
1692
  const settings = await getSettings();
1348
1693
  if (!(name2 in settings)) {
1349
- settings[name2] = await initSetting(name2);
1350
- saveSettings(settings);
1694
+ if ("question" in DEFAULTS[name2]) {
1695
+ settings[name2] = await initSetting(name2);
1696
+ saveSettings(settings);
1697
+ } else {
1698
+ return DEFAULTS[name2].defaultValue;
1699
+ }
1351
1700
  }
1352
1701
  return settings[name2];
1353
1702
  };
@@ -1512,6 +1861,8 @@ var VISITOR = {
1512
1861
  return process;
1513
1862
  case "Symbol":
1514
1863
  return Symbol;
1864
+ case "Models":
1865
+ return Models;
1515
1866
  }
1516
1867
  const definitionNodes = node.getDefinitionNodes();
1517
1868
  if (!definitionNodes.length) {
@@ -1652,7 +2003,8 @@ var VISITOR = {
1652
2003
  return target[argument];
1653
2004
  },
1654
2005
  [import_ts_morph2.SyntaxKind.NoSubstitutionTemplateLiteral]: (node) => node.getLiteralValue(),
1655
- [import_ts_morph2.SyntaxKind.NullKeyword]: () => null
2006
+ [import_ts_morph2.SyntaxKind.NullKeyword]: () => null,
2007
+ [import_ts_morph2.SyntaxKind.NewExpression]: (node, context) => new (staticEval(node.getExpression(), context))(...node.getArguments().map((arg) => staticEval(arg, context)))
1656
2008
  };
1657
2009
 
1658
2010
  // src/bin/gqm/utils.ts
@@ -1703,11 +2055,11 @@ var parseModels = async () => {
1703
2055
  });
1704
2056
  const modelsPath = await getSetting("modelsPath");
1705
2057
  const sourceFile = project.addSourceFileAtPath(modelsPath);
1706
- const modelsDeclaration = findDeclarationInFile(sourceFile, "rawModels");
1707
- const rawModels = staticEval(modelsDeclaration, {});
2058
+ const modelsDeclaration = findDeclarationInFile(sourceFile, "models");
2059
+ const models = staticEval(modelsDeclaration, {});
1708
2060
  const generatedFolderPath = await getSetting("generatedFolderPath");
1709
- writeToFile(`${generatedFolderPath}/models.json`, JSON.stringify(rawModels, null, 2));
1710
- return rawModels;
2061
+ writeToFile(`${generatedFolderPath}/models.json`, JSON.stringify(models.definitions, null, 2));
2062
+ return models;
1711
2063
  };
1712
2064
 
1713
2065
  // src/bin/gqm/gqm.ts
@@ -1723,39 +2075,39 @@ import_commander.program.command("setup").description("Set up the project").acti
1723
2075
  ensureFileExists(KNEXFILE_PATH, KNEXFILE);
1724
2076
  });
1725
2077
  import_commander.program.command("generate").description("Generate all the things").action(async () => {
1726
- const rawModels = await parseModels();
2078
+ const models = await parseModels();
1727
2079
  const generatedFolderPath = await getSetting("generatedFolderPath");
1728
- writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(rawModels));
1729
- writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(rawModels));
1730
- writeToFile(`${generatedFolderPath}/db/index.ts`, generateDBModels(rawModels));
1731
- writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(rawModels));
2080
+ const gqlModule = await getSetting("gqlModule");
2081
+ writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(models));
2082
+ writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(models, gqlModule));
2083
+ writeToFile(`${generatedFolderPath}/db/index.ts`, generateDBModels(models));
2084
+ writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(models));
1732
2085
  await generateGraphqlApiTypes();
1733
2086
  await generateGraphqlClientTypes();
1734
2087
  });
1735
2088
  import_commander.program.command("generate-models").description("Generate models.json").action(async () => {
1736
- const rawModels = await parseModels();
1737
- const generatedFolderPath = await getSetting("generatedFolderPath");
1738
- writeToFile(`${generatedFolderPath}/models.json`, JSON.stringify(rawModels, null, 2));
2089
+ await parseModels();
1739
2090
  });
1740
2091
  import_commander.program.command("generate-schema").description("Generate schema").action(async () => {
1741
- const rawModels = await parseModels();
2092
+ const models = await parseModels();
1742
2093
  const generatedFolderPath = await getSetting("generatedFolderPath");
1743
- writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(rawModels));
2094
+ writeToFile(`${generatedFolderPath}/schema.graphql`, printSchemaFromModels(models));
1744
2095
  });
1745
2096
  import_commander.program.command("generate-mutation-queries").description("Generate mutation-queries").action(async () => {
1746
- const rawModels = await parseModels();
2097
+ const models = await parseModels();
1747
2098
  const generatedFolderPath = await getSetting("generatedFolderPath");
1748
- writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(rawModels));
2099
+ const gqlModule = await getSetting("gqlModule");
2100
+ writeToFile(`${generatedFolderPath}/client/mutations.ts`, generateMutations(models, gqlModule));
1749
2101
  });
1750
2102
  import_commander.program.command("generate-db-types").description("Generate DB types").action(async () => {
1751
- const rawModels = await parseModels();
2103
+ const models = await parseModels();
1752
2104
  const generatedFolderPath = await getSetting("generatedFolderPath");
1753
- writeToFile(`${generatedFolderPath}/db/index.ts`, generateMutations(rawModels));
2105
+ writeToFile(`${generatedFolderPath}/db/index.ts`, generateDBModels(models));
1754
2106
  });
1755
2107
  import_commander.program.command("generate-knex-types").description("Generate Knex types").action(async () => {
1756
- const rawModels = await parseModels();
2108
+ const models = await parseModels();
1757
2109
  const generatedFolderPath = await getSetting("generatedFolderPath");
1758
- writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(rawModels));
2110
+ writeToFile(`${generatedFolderPath}/db/knex.ts`, generateKnexTables(models));
1759
2111
  });
1760
2112
  import_commander.program.command("generate-graphql-api-types").description("Generate Graphql API types").action(async () => {
1761
2113
  await generateGraphqlApiTypes();
@@ -1763,18 +2115,20 @@ import_commander.program.command("generate-graphql-api-types").description("Gene
1763
2115
  import_commander.program.command("generate-graphql-client-types").description("Generate Graphql client types").action(async () => {
1764
2116
  await generateGraphqlClientTypes();
1765
2117
  });
1766
- import_commander.program.command("generate-migration").description("Generate Migration").action(async () => {
2118
+ import_commander.program.command("generate-migration [<name>] [<date>]").description("Generate Migration").action(async (name2, date) => {
1767
2119
  const git = (0, import_simple_git.simpleGit)();
1768
- let name2 = process.argv[process.argv.indexOf("gqm") + 1] || (await git.branch()).current.split("/").pop();
1769
- if (name2 && ["staging", "production"].includes(name2)) {
2120
+ if (!name2) {
2121
+ name2 = (await git.branch()).current.split("/").pop();
2122
+ }
2123
+ if (!name2 || ["main", "staging", "production"].includes(name2)) {
1770
2124
  name2 = await readLine("Migration name:");
1771
2125
  }
1772
2126
  const knexfile = await parseKnexfile();
1773
2127
  const db = (0, import_knex.default)(knexfile);
1774
2128
  try {
1775
- const rawModels = await parseModels();
1776
- const migrations = await new MigrationGenerator(db, rawModels).generate();
1777
- writeToFile(`migrations/${getMigrationDate()}_${name2}.ts`, migrations);
2129
+ const models = await parseModels();
2130
+ const migrations = await new MigrationGenerator(db, models).generate();
2131
+ writeToFile(`migrations/${date || getMigrationDate()}_${name2}.ts`, migrations);
1778
2132
  } finally {
1779
2133
  await db.destroy();
1780
2134
  }