@smartive/graphql-magic 16.3.7 → 16.3.8

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 (76) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/dist/bin/gqm.cjs +37 -35
  3. package/dist/cjs/index.cjs +51 -48
  4. package/dist/esm/api/execute.d.ts +1 -5
  5. package/dist/esm/api/execute.js.map +1 -1
  6. package/dist/esm/client/queries.d.ts +4 -4
  7. package/dist/esm/client/queries.js.map +1 -1
  8. package/dist/esm/db/generate.js +2 -0
  9. package/dist/esm/db/generate.js.map +1 -1
  10. package/dist/esm/migrations/generate.js +1 -1
  11. package/dist/esm/migrations/generate.js.map +1 -1
  12. package/dist/esm/models/models.d.ts +1 -1
  13. package/dist/esm/models/models.js.map +1 -1
  14. package/dist/esm/models/utils.d.ts +8 -12
  15. package/dist/esm/models/utils.js +3 -5
  16. package/dist/esm/models/utils.js.map +1 -1
  17. package/dist/esm/permissions/check.js +0 -15
  18. package/dist/esm/permissions/check.js.map +1 -1
  19. package/dist/esm/permissions/generate.d.ts +5 -19
  20. package/dist/esm/permissions/generate.js.map +1 -1
  21. package/dist/esm/resolvers/arguments.js.map +1 -1
  22. package/dist/esm/resolvers/filters.js +0 -2
  23. package/dist/esm/resolvers/filters.js.map +1 -1
  24. package/dist/esm/resolvers/mutations.js +5 -4
  25. package/dist/esm/resolvers/mutations.js.map +1 -1
  26. package/dist/esm/resolvers/node.js.map +1 -1
  27. package/dist/esm/resolvers/resolver.d.ts +2 -2
  28. package/dist/esm/resolvers/resolver.js +1 -1
  29. package/dist/esm/resolvers/resolver.js.map +1 -1
  30. package/dist/esm/resolvers/selects.js.map +1 -1
  31. package/dist/esm/resolvers/utils.d.ts +2 -6
  32. package/dist/esm/resolvers/utils.js +4 -2
  33. package/dist/esm/resolvers/utils.js.map +1 -1
  34. package/dist/esm/schema/utils.d.ts +4 -4
  35. package/dist/esm/schema/utils.js +31 -30
  36. package/dist/esm/schema/utils.js.map +1 -1
  37. package/dist/esm/utils/dates.d.ts +2 -4
  38. package/dist/esm/utils/dates.js +1 -3
  39. package/dist/esm/utils/dates.js.map +1 -1
  40. package/docs/docs/1-tutorial.md +1 -1
  41. package/eslint.config.mjs +42 -0
  42. package/migrations/20230912185644_setup.ts +1 -1
  43. package/package.json +6 -11
  44. package/src/api/execute.ts +1 -0
  45. package/src/bin/gqm/codegen.ts +1 -1
  46. package/src/client/gql.ts +1 -1
  47. package/src/client/mutations.ts +8 -8
  48. package/src/client/queries.ts +15 -14
  49. package/src/db/generate.ts +8 -5
  50. package/src/migrations/generate.ts +26 -22
  51. package/src/models/models.ts +24 -9
  52. package/src/models/mutation-hook.ts +1 -1
  53. package/src/models/utils.ts +8 -7
  54. package/src/permissions/check.ts +22 -30
  55. package/src/permissions/generate.ts +8 -25
  56. package/src/resolvers/arguments.ts +7 -2
  57. package/src/resolvers/filters.ts +8 -10
  58. package/src/resolvers/mutations.ts +19 -16
  59. package/src/resolvers/node.ts +3 -2
  60. package/src/resolvers/resolver.ts +11 -10
  61. package/src/resolvers/selects.ts +4 -3
  62. package/src/resolvers/utils.ts +15 -10
  63. package/src/schema/generate.ts +11 -11
  64. package/src/schema/utils.ts +84 -82
  65. package/src/utils/dates.ts +3 -3
  66. package/tests/generated/api/index.ts +2 -2
  67. package/tests/generated/client/index.ts +1 -193
  68. package/tests/generated/db/index.ts +4 -4
  69. package/tests/generated/models.json +2 -1
  70. package/tests/generated/schema.graphql +1 -1
  71. package/tests/utils/graphql-client.ts +49 -0
  72. package/tests/utils/models.ts +1 -0
  73. package/tests/utils/server.ts +4 -5
  74. package/tsconfig.eslint.json +18 -3
  75. package/tsconfig.json +3 -1
  76. package/.eslintrc +0 -13
@@ -19,7 +19,7 @@ import {
19
19
 
20
20
  const isNotFalsy = <T>(v: T | null | undefined | false): v is T => typeof v !== 'undefined' && v !== null && v !== false;
21
21
 
22
- export const merge = <T>(objects: ({ [name: string]: T } | undefined | false)[] | undefined): { [name: string]: T } =>
22
+ export const merge = <T>(objects: (Record<string, T> | undefined | false)[] | undefined): Record<string, T> =>
23
23
  (objects || []).filter(isNotFalsy).reduce((i, acc) => ({ ...acc, ...i }), {});
24
24
 
25
25
  // Target -> target
@@ -42,7 +42,7 @@ export const not =
42
42
  (field: T) =>
43
43
  !predicate(field);
44
44
 
45
- export const isRootModel = (model: EntityModel) => model.root;
45
+ export const isRootModel = (model: EntityModel) => !!model.root;
46
46
 
47
47
  export const isEntityModel = (model: Model): model is EntityModel => model instanceof EntityModel;
48
48
 
@@ -77,7 +77,7 @@ export const isEnum = (field: EntityField): field is EnumField => field.kind ===
77
77
 
78
78
  export const isRelation = (field: EntityField): field is RelationField => field.kind === 'relation';
79
79
 
80
- export const isInherited = (field: EntityField) => field.inherited;
80
+ export const isInherited = (field: EntityField) => !!field.inherited;
81
81
 
82
82
  export const isInTable = (field: EntityField) => field.name === 'id' || !field.inherited;
83
83
 
@@ -114,7 +114,7 @@ export const getActionableRelations = (model: EntityModel, action: 'create' | 'u
114
114
  (relation) =>
115
115
  relation.field[
116
116
  `${action === 'filter' ? action : action.slice(0, -1)}able` as 'filterable' | 'creatable' | 'updatable'
117
- ]
117
+ ],
118
118
  )
119
119
  .map(({ name }) => name);
120
120
 
@@ -133,6 +133,7 @@ export const summon = <T>(array: readonly T[] | undefined, cb: Parameters<T[]['f
133
133
  console.trace();
134
134
  throw new Error(errorMessage || 'Element not found.');
135
135
  }
136
+
136
137
  return result;
137
138
  };
138
139
 
@@ -154,11 +155,13 @@ export const get = <T, U extends keyof ForSure<T>>(object: T | null | undefined,
154
155
  console.warn(error);
155
156
  throw error;
156
157
  }
158
+
157
159
  return value as ForSure<ForSure<T>[U]>;
158
160
  };
159
161
 
160
162
  export const getString = (v: unknown) => {
161
163
  assert(typeof v === 'string');
164
+
162
165
  return v;
163
166
  };
164
167
 
@@ -168,9 +171,8 @@ export const retry = async <T>(cb: () => Promise<T>, condition: (e: any) => bool
168
171
  } catch (e) {
169
172
  if (condition(e)) {
170
173
  return await cb();
171
- } else {
172
- throw e;
173
174
  }
175
+ throw e;
174
176
  }
175
177
  };
176
178
 
@@ -182,7 +184,6 @@ type Typeof = {
182
184
  symbol: symbol;
183
185
  undefined: undefined;
184
186
  object: object;
185
- // eslint-disable-next-line @typescript-eslint/ban-types
186
187
  function: Function;
187
188
  };
188
189
 
@@ -11,7 +11,7 @@ export const getRole = (ctx: Pick<FullContext, 'user'>) => ctx.user?.role ?? 'UN
11
11
  export const getPermissionStack = (
12
12
  ctx: Pick<FullContext, 'permissions' | 'user'>,
13
13
  type: string,
14
- action: PermissionAction
14
+ action: PermissionAction,
15
15
  ): boolean | PermissionStack => {
16
16
  const rolePermissions = ctx.permissions[getRole(ctx)];
17
17
  if (typeof rolePermissions === 'boolean' || rolePermissions === undefined) {
@@ -37,7 +37,7 @@ export const applyPermissions = (
37
37
  tableAlias: string,
38
38
  query: Knex.QueryBuilder,
39
39
  action: PermissionAction,
40
- verifiedPermissionStack?: PermissionStack
40
+ verifiedPermissionStack?: PermissionStack,
41
41
  ): boolean | PermissionStack => {
42
42
  const permissionStack = getPermissionStack(ctx, type, action);
43
43
 
@@ -47,8 +47,9 @@ export const applyPermissions = (
47
47
 
48
48
  if (permissionStack === false) {
49
49
  console.error(`No applicable permissions exist for ${getRole(ctx)} ${type} ${action}.`);
50
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
50
+
51
51
  query.where(false);
52
+
52
53
  return permissionStack;
53
54
  }
54
55
 
@@ -59,8 +60,8 @@ export const applyPermissions = (
59
60
  hash(prefixChain) === hash(chain.slice(0, -1)) &&
60
61
  // TODO: this is stricter than it could be if we add these checks to the query
61
62
  !('where' in get(chain, chain.length - 1)) &&
62
- !('me' in get(chain, chain.length - 1))
63
- )
63
+ !('me' in get(chain, chain.length - 1)),
64
+ ),
64
65
  )
65
66
  ) {
66
67
  // The user has access to a parent entity with one or more from a set of rules, all of which are inherited by this entity
@@ -68,15 +69,14 @@ export const applyPermissions = (
68
69
  return permissionStack;
69
70
  }
70
71
 
71
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
72
72
  ors(
73
73
  query,
74
74
  permissionStack.map(
75
75
  (links) => (query) =>
76
76
  query
77
77
  .whereNull(`${tableAlias}.id`)
78
- .orWhereExists((subQuery) => permissionLinkQuery(ctx, subQuery, links, ctx.knex.raw(`"${tableAlias}".id`)))
79
- )
78
+ .orWhereExists((subQuery) => permissionLinkQuery(ctx, subQuery, links, ctx.knex.raw(`"${tableAlias}".id`))),
79
+ ),
80
80
  );
81
81
 
82
82
  return permissionStack;
@@ -89,7 +89,7 @@ export const getEntityToMutate = async (
89
89
  ctx: Pick<FullContext, 'models' | 'permissions' | 'user' | 'knex'>,
90
90
  model: EntityModel,
91
91
  where: Record<string, unknown>,
92
- action: 'UPDATE' | 'DELETE' | 'RESTORE'
92
+ action: 'UPDATE' | 'DELETE' | 'RESTORE',
93
93
  ) => {
94
94
  const query = ctx
95
95
  .knex(model.parent || model.name)
@@ -101,7 +101,7 @@ export const getEntityToMutate = async (
101
101
  console.error(
102
102
  `Not found: ${Object.entries(where)
103
103
  .map(([key, value]) => `${key}: ${value}`)
104
- .join(', ')}`
104
+ .join(', ')}`,
105
105
  );
106
106
  throw new NotFoundError(`Entity to ${action.toLowerCase()}`);
107
107
  }
@@ -112,7 +112,7 @@ export const getEntityToMutate = async (
112
112
  console.error(
113
113
  `Permission error: ${Object.entries(where)
114
114
  .map(([key, value]) => `${key}: ${value}`)
115
- .join(', ')}`
115
+ .join(', ')}`,
116
116
  );
117
117
  throw new PermissionError(getRole(ctx), action, `this ${model.name}`, 'no available permissions applied');
118
118
  }
@@ -132,7 +132,7 @@ export const checkCanWrite = async (
132
132
  ctx: Pick<FullContext, 'models' | 'permissions' | 'user' | 'knex'>,
133
133
  model: EntityModel,
134
134
  data: Record<string, unknown>,
135
- action: 'CREATE' | 'UPDATE'
135
+ action: 'CREATE' | 'UPDATE',
136
136
  ) => {
137
137
  const permissionStack = getPermissionStack(ctx, model.name, action);
138
138
 
@@ -143,7 +143,6 @@ export const checkCanWrite = async (
143
143
  throw new PermissionError(getRole(ctx), action, model.plural, 'no applicable permissions');
144
144
  }
145
145
 
146
- // eslint-disable-next-line @typescript-eslint/no-explicit-any -- using `select(1 as any)` to instantiate an "empty" query builder
147
146
  const query = ctx.knex.select(1 as any).first();
148
147
  let linked = false;
149
148
 
@@ -168,7 +167,7 @@ export const checkCanWrite = async (
168
167
 
169
168
  if (fieldPermissionStack === true) {
170
169
  // User can link any entity from this type, just check whether it exists
171
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
170
+
172
171
  query.whereExists((subQuery) => subQuery.from(`${field.type} as a`).whereRaw(`a.id = ?`, foreignId));
173
172
  continue;
174
173
  }
@@ -178,16 +177,15 @@ export const checkCanWrite = async (
178
177
  role,
179
178
  action,
180
179
  `this ${model.name}'s ${field.name}`,
181
- 'no applicable permissions on data to link'
180
+ 'no applicable permissions on data to link',
182
181
  );
183
182
  }
184
183
 
185
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
186
184
  ors(
187
185
  query,
188
186
  fieldPermissionStack.map(
189
- (links) => (query) => query.whereExists((subQuery) => permissionLinkQuery(ctx, subQuery, links, foreignId))
190
- )
187
+ (links) => (query) => query.whereExists((subQuery) => permissionLinkQuery(ctx, subQuery, links, foreignId)),
188
+ ),
191
189
  );
192
190
  }
193
191
 
@@ -206,7 +204,7 @@ const permissionLinkQuery = (
206
204
  ctx: Pick<FullContext, 'models' | 'user'>,
207
205
  subQuery: Knex.QueryBuilder,
208
206
  links: PermissionLink[],
209
- id: Knex.RawBinding | Knex.ValueDict
207
+ id: Knex.RawBinding | Knex.ValueDict,
210
208
  ) => {
211
209
  const aliases = new AliasGenerator();
212
210
  let alias = aliases.getShort();
@@ -214,16 +212,14 @@ const permissionLinkQuery = (
214
212
 
215
213
  if (me) {
216
214
  if (!ctx.user) {
217
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
218
215
  subQuery.where(false);
216
+
219
217
  return;
220
218
  }
221
219
 
222
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
223
220
  subQuery.where({ [`${alias}.id`]: ctx.user.id });
224
221
  }
225
222
 
226
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
227
223
  subQuery.from(`${type} as ${alias}`);
228
224
 
229
225
  if (where) {
@@ -234,20 +230,18 @@ const permissionLinkQuery = (
234
230
  const model = ctx.models.getModel(type, 'entity');
235
231
  const subAlias = aliases.getShort();
236
232
  if (reverse) {
237
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
238
233
  subQuery.leftJoin(`${type} as ${subAlias}`, `${alias}.${foreignKey || 'id'}`, `${subAlias}.id`);
239
234
  } else {
240
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
241
235
  subQuery.rightJoin(`${type} as ${subAlias}`, `${alias}.id`, `${subAlias}.${foreignKey || 'id'}`);
242
236
  }
243
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
237
+
244
238
  subQuery.where({ [`${subAlias}.deleted`]: false });
245
239
  if (where) {
246
240
  applyWhere(model, subQuery, subAlias, where, aliases);
247
241
  }
248
242
  alias = subAlias;
249
243
  }
250
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
244
+
251
245
  subQuery.whereRaw(`"${alias}".id = ?`, id);
252
246
  };
253
247
 
@@ -257,18 +251,16 @@ const applyWhere = (model: EntityModel, query: Knex.QueryBuilder, alias: string,
257
251
 
258
252
  if (relation) {
259
253
  const subAlias = aliases.getShort();
260
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
254
+
261
255
  query.leftJoin(
262
256
  `${relation.targetModel.name} as ${subAlias}`,
263
257
  `${alias}.${relation.field.foreignKey || `${relation.field.name}Id`}`,
264
- `${subAlias}.id`
258
+ `${subAlias}.id`,
265
259
  );
266
260
  applyWhere(relation.targetModel, query, subAlias, value, aliases);
267
261
  } else if (Array.isArray(value)) {
268
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
269
262
  query.whereIn(`${alias}.${key}`, value);
270
263
  } else {
271
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
272
264
  query.where({ [`${alias}.${key}`]: value });
273
265
  }
274
266
  }
@@ -8,36 +8,19 @@ const ACTIONS: PermissionAction[] = ['READ', 'CREATE', 'UPDATE', 'DELETE', 'REST
8
8
  /**
9
9
  * Initial representation (tree structure, as defined by user).
10
10
  */
11
- export type PermissionsConfig = {
12
- [role: string]:
13
- | true
14
- | {
15
- [type: string]: PermissionsBlock;
16
- };
17
- };
11
+ export type PermissionsConfig = Record<string, true | Record<string, PermissionsBlock>>;
18
12
 
19
- export type PermissionsBlock = {
20
- [action in PermissionAction]?: true;
21
- } & {
22
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
13
+ export type PermissionsBlock = Partial<Record<PermissionAction, true>> & {
23
14
  WHERE?: Record<string, any>;
24
- RELATIONS?: {
25
- [relation: string]: PermissionsBlock;
26
- };
15
+ RELATIONS?: Record<string, PermissionsBlock>;
27
16
  };
28
17
 
29
18
  /**
30
19
  * Final representation (lookup table (role, model, action) -> permission stack).
31
20
  */
32
- export type Permissions = {
33
- [role: string]: true | RolePermissions;
34
- };
21
+ export type Permissions = Record<string, true | RolePermissions>;
35
22
 
36
- type RolePermissions = {
37
- [type: string]: {
38
- [action in PermissionAction]?: true | PermissionStack;
39
- };
40
- };
23
+ type RolePermissions = Record<string, Partial<Record<PermissionAction, true | PermissionStack>>>;
41
24
 
42
25
  /**
43
26
  * For a given role, model and action,
@@ -53,7 +36,7 @@ export type PermissionLink = {
53
36
  foreignKey?: string;
54
37
  reverse?: boolean;
55
38
  me?: boolean;
56
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
+
57
40
  where?: any;
58
41
  };
59
42
 
@@ -85,7 +68,7 @@ export const generatePermissions = (models: Models, config: PermissionsConfig) =
85
68
  ...('WHERE' in block && { where: block.WHERE }),
86
69
  },
87
70
  ],
88
- block
71
+ block,
89
72
  );
90
73
  }
91
74
  permissions[role] = rolePermissions;
@@ -107,7 +90,7 @@ const addPermissions = (models: Models, permissions: RolePermissions, links: Per
107
90
  permissions[type][action] = [];
108
91
  }
109
92
  if (permissions[type][action] !== true) {
110
- (permissions[type][action] as PermissionStack).push(links);
93
+ permissions[type][action].push(links);
111
94
  }
112
95
  }
113
96
  }
@@ -35,6 +35,7 @@ function getRawValue(value: ValueNode, values?: VariableValues): Value {
35
35
  if (!values) {
36
36
  return;
37
37
  }
38
+
38
39
  return value.values.map((value) => getRawValue(value, values));
39
40
  case Kind.VARIABLE:
40
41
  return values?.[value.name.value];
@@ -56,6 +57,7 @@ function getRawValue(value: ValueNode, values?: VariableValues): Value {
56
57
  for (const field of value.fields) {
57
58
  res[field.name.value] = getRawValue(field.value, values);
58
59
  }
60
+
59
61
  return res;
60
62
  }
61
63
  }
@@ -69,7 +71,7 @@ export const normalizeArguments = (node: FieldResolverNode) => {
69
71
  const normalizedValue = normalizeValue(
70
72
  rawValue,
71
73
  summonByKey(node.fieldDefinition.arguments || [], 'name.value', argument.name.value).type,
72
- node.ctx.info.schema
74
+ node.ctx.info.schema,
73
75
  );
74
76
  if (normalizedValue === undefined) {
75
77
  continue;
@@ -77,6 +79,7 @@ export const normalizeArguments = (node: FieldResolverNode) => {
77
79
  normalizedArguments[argument.name.value] = normalizedValue as any;
78
80
  }
79
81
  }
82
+
80
83
  return normalizedArguments;
81
84
  };
82
85
 
@@ -88,6 +91,7 @@ export function normalizeValue(value: Value, type: TypeNode, schema: GraphQLSche
88
91
  for (const v of value) {
89
92
  res.push(normalizeValue(v, type.type, schema));
90
93
  }
94
+
91
95
  return res;
92
96
  }
93
97
 
@@ -104,7 +108,7 @@ export function normalizeValue(value: Value, type: TypeNode, schema: GraphQLSche
104
108
  return normalizeValueByTypeDefinition(
105
109
  value,
106
110
  (schema.getType(type.name.value) as Maybe<GraphQLObjectType>)?.astNode,
107
- schema
111
+ schema,
108
112
  );
109
113
  }
110
114
  }
@@ -125,5 +129,6 @@ export const normalizeValueByTypeDefinition = (value: Value, type: Maybe<TypeDef
125
129
  }
126
130
  res[key] = normalizedValue;
127
131
  }
132
+
128
133
  return res;
129
134
  };
@@ -33,12 +33,10 @@ export const applyFilters = (node: FieldResolverNode, query: Knex.QueryBuilder,
33
33
  const { limit, offset, orderBy, where, search } = normalizedArguments;
34
34
 
35
35
  if (limit) {
36
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
37
36
  query.limit(limit);
38
37
  }
39
38
 
40
39
  if (offset) {
41
- // eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
42
40
  query.offset(offset);
43
41
  }
44
42
 
@@ -108,8 +106,8 @@ const applyWhere = (node: WhereNode, where: Where | undefined, ops: QueryBuilder
108
106
  ops.push((query) =>
109
107
  ors(
110
108
  query,
111
- allSubOps.map((subOps) => (subQuery) => apply(subQuery, subOps))
112
- )
109
+ allSubOps.map((subOps) => (subQuery) => apply(subQuery, subOps)),
110
+ ),
113
111
  );
114
112
  continue;
115
113
  }
@@ -145,7 +143,7 @@ const applyWhere = (node: WhereNode, where: Where | undefined, ops: QueryBuilder
145
143
  ]);
146
144
  void apply(subQuery, subOps);
147
145
  applyJoins(aliases, subQuery, subJoins);
148
- })
146
+ }),
149
147
  );
150
148
  continue;
151
149
  }
@@ -183,8 +181,8 @@ const applyWhere = (node: WhereNode, where: Where | undefined, ops: QueryBuilder
183
181
  ops.push((query) =>
184
182
  ors(
185
183
  query,
186
- value.map((v) => (subQuery) => subQuery.whereRaw('? = ANY(??)', [v, column] as string[]))
187
- )
184
+ value.map((v) => (subQuery) => subQuery.whereRaw('? = ANY(??)', [v, column] as string[])),
185
+ ),
188
186
  );
189
187
  continue;
190
188
  }
@@ -195,7 +193,7 @@ const applyWhere = (node: WhereNode, where: Where | undefined, ops: QueryBuilder
195
193
  ors(query, [
196
194
  (subQuery) => subQuery.whereIn(column, value.filter((v) => v !== null) as string[]),
197
195
  (subQuery) => subQuery.whereNull(column),
198
- ])
196
+ ]),
199
197
  );
200
198
  continue;
201
199
  }
@@ -220,8 +218,8 @@ const applySearch = (node: FieldResolverNode, search: string, query: Knex.QueryB
220
218
  .map(
221
219
  ({ name }) =>
222
220
  (query) =>
223
- query.whereILike(getColumn(node, name), `%${search}%`)
224
- )
221
+ query.whereILike(getColumn(node, name), `%${search}%`),
222
+ ),
225
223
  );
226
224
 
227
225
  const applyOrderBy = (node: FieldResolverNode, orderBy: OrderBy, query: Knex.QueryBuilder) => {
@@ -137,15 +137,17 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
137
137
  throw new ForbiddenError('Entity is already deleted.');
138
138
  }
139
139
 
140
- const toDelete: { [type: string]: { [id: string]: string } } = {};
141
- const toUnlink: {
142
- [type: string]: {
143
- [id: string]: {
140
+ const toDelete: Record<string, Record<string, string>> = {};
141
+ const toUnlink: Record<
142
+ string,
143
+ Record<
144
+ string,
145
+ {
144
146
  display: string;
145
147
  fields: string[];
146
- };
147
- };
148
- } = {};
148
+ }
149
+ >
150
+ > = {};
149
151
 
150
152
  const beforeHooks: Callbacks = [];
151
153
  const mutations: Callbacks = [];
@@ -167,18 +169,19 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
167
169
  if (!dryRun) {
168
170
  const normalizedInput = { deleted: true, deletedAt: ctx.now, deletedById: ctx.user?.id };
169
171
  const data = { prev: entity, input: {}, normalizedInput, next: { ...entity, ...normalizedInput } };
170
- if (ctx.mutationHook) {
172
+ const mutationHook = ctx.mutationHook;
173
+ if (mutationHook) {
171
174
  beforeHooks.push(async () => {
172
- await ctx.mutationHook(currentModel, 'delete', 'before', data, ctx);
175
+ await mutationHook(currentModel, 'delete', 'before', data, ctx);
173
176
  });
174
177
  }
175
178
  mutations.push(async () => {
176
179
  await ctx.knex(currentModel.name).where({ id: entity.id }).update(normalizedInput);
177
180
  await createRevision(currentModel, { ...entity, deleted: true }, ctx);
178
181
  });
179
- if (ctx.mutationHook) {
182
+ if (mutationHook) {
180
183
  afterHooks.push(async () => {
181
- await ctx.mutationHook(currentModel, 'delete', 'after', data, ctx);
184
+ await mutationHook(currentModel, 'delete', 'after', data, ctx);
182
185
  });
183
186
  }
184
187
  }
@@ -270,7 +273,7 @@ const restore = async (model: EntityModel, { where }: { where: any }, ctx: FullC
270
273
  if (
271
274
  !relatedEntity.deleted ||
272
275
  !relatedEntity.deletedAt ||
273
- !anyDateToLuxon(relatedEntity.deletedAt, ctx.timeZone).equals(anyDateToLuxon(entity.deletedAt, ctx.timeZone))
276
+ !anyDateToLuxon(relatedEntity.deletedAt, ctx.timeZone)!.equals(anyDateToLuxon(entity.deletedAt, ctx.timeZone)!)
274
277
  ) {
275
278
  return;
276
279
  }
@@ -279,7 +282,7 @@ const restore = async (model: EntityModel, { where }: { where: any }, ctx: FullC
279
282
  const data = { prev: relatedEntity, input: {}, normalizedInput, next: { ...relatedEntity, ...normalizedInput } };
280
283
  if (ctx.mutationHook) {
281
284
  beforeHooks.push(async () => {
282
- await ctx.mutationHook(currentModel, 'restore', 'before', data, ctx);
285
+ await ctx.mutationHook!(currentModel, 'restore', 'before', data, ctx);
283
286
  });
284
287
  }
285
288
  mutations.push(async () => {
@@ -288,7 +291,7 @@ const restore = async (model: EntityModel, { where }: { where: any }, ctx: FullC
288
291
  });
289
292
  if (ctx.mutationHook) {
290
293
  afterHooks.push(async () => {
291
- await ctx.mutationHook(currentModel, 'restore', 'after', data, ctx);
294
+ await ctx.mutationHook!(currentModel, 'restore', 'after', data, ctx);
292
295
  });
293
296
  }
294
297
 
@@ -369,7 +372,7 @@ const sanitize = (ctx: FullContext, model: EntityModel, data: Entity) => {
369
372
  }
370
373
 
371
374
  if (isEndOfDay(field) && data[key]) {
372
- data[key] = anyDateToLuxon(data[key], ctx.timeZone).endOf('day');
375
+ data[key] = anyDateToLuxon(data[key], ctx.timeZone)!.endOf('day');
373
376
  continue;
374
377
  }
375
378
 
@@ -380,5 +383,5 @@ const sanitize = (ctx: FullContext, model: EntityModel, data: Entity) => {
380
383
  }
381
384
  };
382
385
 
383
- const isEndOfDay = (field?: EntityField) =>
386
+ const isEndOfDay = (field: EntityField) =>
384
387
  isPrimitive(field) && field.type === 'DateTime' && field?.endOfDay === true && field?.dateTimeType === 'date';
@@ -149,7 +149,7 @@ export const getInlineFragments = (node: ResolverNode) =>
149
149
 
150
150
  baseTypeDefinition: node.baseTypeDefinition,
151
151
  typeName: getFragmentTypeName(subNode),
152
- })
152
+ }),
153
153
  );
154
154
 
155
155
  export const getFragmentSpreads = (node: ResolverNode) =>
@@ -166,7 +166,7 @@ export const getFragmentSpreads = (node: ResolverNode) =>
166
166
 
167
167
  baseTypeDefinition: node.baseTypeDefinition,
168
168
  typeName: node.model.name,
169
- })
169
+ }),
170
170
  );
171
171
 
172
172
  export const getJoins = (node: ResolverNode, toMany: boolean) => {
@@ -217,5 +217,6 @@ export const getJoins = (node: ResolverNode, toMany: boolean) => {
217
217
  isList: isListType(fieldDefinition.type),
218
218
  });
219
219
  }
220
+
220
221
  return nodes;
221
222
  };
@@ -19,7 +19,7 @@ export const resolve = async (ctx: FullContext, id?: string) => {
19
19
  const fieldNode = summonByKey(ctx.info.fieldNodes, 'name.value', ctx.info.fieldName);
20
20
  const baseTypeDefinition = get(
21
21
  ctx.info.operation.operation === 'query' ? ctx.info.schema.getQueryType() : ctx.info.schema.getMutationType(),
22
- 'astNode'
22
+ 'astNode',
23
23
  );
24
24
  const node = getRootFieldNode({
25
25
  ctx,
@@ -29,7 +29,7 @@ export const resolve = async (ctx: FullContext, id?: string) => {
29
29
  const { query, verifiedPermissionStacks } = await buildQuery(node);
30
30
 
31
31
  if (ctx.info.fieldName === 'me') {
32
- if (!node.ctx.user.id) {
32
+ if (!node.ctx.user?.id) {
33
33
  return undefined;
34
34
  }
35
35
 
@@ -66,7 +66,7 @@ type VerifiedPermissionStacks = Record<string, PermissionStack>;
66
66
 
67
67
  const buildQuery = async (
68
68
  node: FieldResolverNode,
69
- parentVerifiedPermissionStacks?: VerifiedPermissionStacks
69
+ parentVerifiedPermissionStacks?: VerifiedPermissionStacks,
70
70
  ): Promise<{ query: Knex.QueryBuilder; verifiedPermissionStacks: VerifiedPermissionStacks }> => {
71
71
  const query = node.ctx.knex.fromRaw(`"${node.rootModel.name}" as "${node.ctx.aliases.getShort(node.resultAlias)}"`);
72
72
 
@@ -88,7 +88,7 @@ const buildQuery = async (
88
88
  node.ctx.aliases.getShort(alias),
89
89
  query,
90
90
  'READ',
91
- parentVerifiedPermissionStacks?.[alias.split('__').slice(0, -1).join('__')]
91
+ parentVerifiedPermissionStacks?.[alias.split('__').slice(0, -1).join('__')],
92
92
  );
93
93
 
94
94
  if (typeof verifiedPermissionStack !== 'boolean') {
@@ -102,13 +102,13 @@ const buildQuery = async (
102
102
  const applySubQueries = async (
103
103
  node: ResolverNode,
104
104
  entries: Entry[],
105
- parentVerifiedPermissionStacks: VerifiedPermissionStacks
105
+ parentVerifiedPermissionStacks: VerifiedPermissionStacks,
106
106
  ): Promise<void> => {
107
107
  if (!entries.length) {
108
108
  return;
109
109
  }
110
110
 
111
- const entriesById: { [id: string]: Entry[] } = {};
111
+ const entriesById: Record<string, Entry[]> = {};
112
112
  for (const entry of entries) {
113
113
  if (!entriesById[entry[ID_ALIAS]]) {
114
114
  entriesById[entry[ID_ALIAS]] = [];
@@ -130,7 +130,7 @@ const applySubQueries = async (
130
130
  query
131
131
  .clone()
132
132
  .select(`${shortTableAlias}.${foreignKey} as ${shortResultAlias}__${foreignKey}`)
133
- .where({ [`${shortTableAlias}.${foreignKey}`]: id })
133
+ .where({ [`${shortTableAlias}.${foreignKey}`]: id }),
134
134
  );
135
135
 
136
136
  // TODO: make unionAll faster then promise.all...
@@ -153,10 +153,11 @@ const applySubQueries = async (
153
153
  flatMap(
154
154
  entries.map((entry) => {
155
155
  const children = entry[fieldName];
156
+
156
157
  return (isList ? children : children ? [children] : []) as Entry[];
157
- })
158
+ }),
158
159
  ),
159
- verifiedPermissionStacks
160
+ verifiedPermissionStacks,
160
161
  );
161
162
  }
162
163
 
@@ -172,7 +173,7 @@ const applySubQueries = async (
172
173
  await applySubQueries(
173
174
  subNode,
174
175
  entries.map((item) => item[getNameOrAlias(subNode.field)] as Entry).filter(Boolean),
175
- parentVerifiedPermissionStacks
176
+ parentVerifiedPermissionStacks,
176
177
  );
177
178
  }
178
179
  };
@@ -34,7 +34,7 @@ export const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins
34
34
  role,
35
35
  'READ',
36
36
  `${node.model.name}'s field "${field.name}"`,
37
- 'field permission not available'
37
+ 'field permission not available',
38
38
  );
39
39
  }
40
40
 
@@ -49,6 +49,7 @@ export const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins
49
49
  if ([ID_ALIAS, TYPE_ALIAS].includes(fieldAlias)) {
50
50
  throw new UserInputError(`Keyword ${fieldAlias} is reserved by graphql-magic.`);
51
51
  }
52
+
52
53
  return {
53
54
  fieldNode,
54
55
  field: fieldNode.name.value,
@@ -59,8 +60,8 @@ export const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins
59
60
  }),
60
61
  ].map(
61
62
  ({ tableAlias, resultAlias, field, fieldAlias }) =>
62
- `${node.ctx.aliases.getShort(tableAlias)}.${field} as ${node.ctx.aliases.getShort(resultAlias)}__${fieldAlias}`
63
- )
63
+ `${node.ctx.aliases.getShort(tableAlias)}.${field} as ${node.ctx.aliases.getShort(resultAlias)}__${fieldAlias}`,
64
+ ),
64
65
  );
65
66
 
66
67
  for (const subNode of getInlineFragments(node)) {