@coderich/autograph 0.13.10 → 0.13.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@coderich/autograph",
3
3
  "main": "index.js",
4
- "version": "0.13.10",
4
+ "version": "0.13.12",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -15,7 +15,7 @@
15
15
  "dev": "coderich-dev"
16
16
  },
17
17
  "dependencies": {
18
- "@coderich/util": "0.1.4",
18
+ "@coderich/util": "0.1.11",
19
19
  "@graphql-tools/merge": "9.0.0",
20
20
  "@graphql-tools/resolvers-composition": "7.0.0",
21
21
  "@hapi/boom": "10.0.1",
@@ -36,7 +36,7 @@
36
36
  "@coderich/dev": "0.1.0",
37
37
  "@graphql-tools/schema": "10.0.0",
38
38
  "graphql": "16.6.0",
39
- "mongodb": "5.7.0",
39
+ "mongodb": "5.9.2",
40
40
  "mongodb-memory-server": "8.13.0",
41
41
  "validator": "13.9.0"
42
42
  },
@@ -78,8 +78,9 @@ class Emitter extends EventEmitter {
78
78
  } : (event, next) => {
79
79
  if (arr.includes(`${event.query[prop]}`)) {
80
80
  if (once) this.removeListener(eventName, wrapper);
81
- next(listener(event, next));
81
+ return next(listener(event, next));
82
82
  }
83
+ return next();
83
84
  };
84
85
 
85
86
  return this.on(eventName, wrapper);
@@ -76,12 +76,15 @@ module.exports = class Resolver {
76
76
  });
77
77
  }
78
78
 
79
+ /**
80
+ * Execute a user-defined loader (curry in context)
81
+ */
79
82
  loader(name) {
80
83
  const context = this.#context;
81
84
 
82
85
  return new Proxy(loaders[name], {
83
86
  get(loader, fn, proxy) {
84
- if (fn === 'load') return args => loader.load(args, context);
87
+ if (fn.startsWith('load')) return args => loader[fn](args, context);
85
88
  return Reflect.get(loader, fn, proxy);
86
89
  },
87
90
  });
@@ -276,6 +279,10 @@ module.exports = class Resolver {
276
279
  const type = query.isMutation ? 'Mutation' : 'Query';
277
280
  const event = { schema: this.#schema, context: this.#context, resolver: this, query };
278
281
 
282
+ // Backwards compat
283
+ query.match = { ...query.where };
284
+ query.toObject = () => query;
285
+
279
286
  return Emitter.emit(`pre${type}`, event).then(async (resultEarly) => {
280
287
  if (resultEarly !== undefined) return resultEarly;
281
288
  if (Util.isEqual(query.changeset, { added: {}, updated: {}, deleted: {} })) return query.doc;
@@ -9,11 +9,13 @@ const Emitter = require('../data/Emitter');
9
9
 
10
10
  const operations = ['Query', 'Mutation', 'Subscription'];
11
11
  // const interfaceKinds = [Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION];
12
+ const scalarKinds = [Kind.SCALAR_TYPE_DEFINITION, Kind.SCALAR_TYPE_EXTENSION];
12
13
  const modelKinds = [Kind.OBJECT_TYPE_DEFINITION, Kind.OBJECT_TYPE_EXTENSION, Kind.INTERFACE_TYPE_DEFINITION, Kind.INTERFACE_TYPE_EXTENSION];
13
- const allowedKinds = modelKinds.concat(Kind.DOCUMENT, Kind.FIELD_DEFINITION, Kind.NON_NULL_TYPE, Kind.NAMED_TYPE, Kind.LIST_TYPE, Kind.DIRECTIVE);
14
+ const fieldKinds = [Kind.FIELD_DEFINITION].concat(scalarKinds);
15
+ const allowedKinds = modelKinds.concat(fieldKinds).concat(Kind.DOCUMENT, Kind.NON_NULL_TYPE, Kind.NAMED_TYPE, Kind.LIST_TYPE, Kind.DIRECTIVE);
14
16
  const pipelines = ['finalize', 'construct', 'restruct', 'instruct', 'normalize', 'serialize'];
15
17
  const inputPipelines = ['finalize', 'construct', 'instruct', 'normalize', 'serialize'];
16
- const scalars = ['ID', 'String', 'Float', 'Int', 'Boolean'];
18
+ // const scalars = ['ID', 'String', 'Float', 'Int', 'Boolean'];
17
19
 
18
20
  module.exports = class Schema {
19
21
  #config;
@@ -29,9 +31,19 @@ module.exports = class Schema {
29
31
  this.#config.directives.field ??= 'field';
30
32
  this.#config.directives.link ??= 'link';
31
33
  this.#config.directives.index ??= 'index';
32
- // this.#typeDefs = Schema.#framework(this.#config.directives);
34
+ this.#typeDefs = Schema.#framework(this.#config.directives);
35
+ }
36
+
37
+ /* ****** DEPRECATE! ****** */
38
+ getModels() {
39
+ return this.#schema.models;
33
40
  }
34
41
 
42
+ getModel(name) {
43
+ return this.#schema.models[`${name}`];
44
+ }
45
+ /* ***************** */
46
+
35
47
  /**
36
48
  * Decorate each marked @model with config-driven field decorators
37
49
  */
@@ -106,38 +118,41 @@ module.exports = class Schema {
106
118
  let model, field, isField, isList;
107
119
  const thunks = [];
108
120
 
121
+ // Deprecate
122
+ this.#schema.getModel = name => this.#schema.models[`${name}`];
123
+
109
124
  // Parse AST
110
125
  visit(this.#typeDefs, {
111
126
  enter: (node) => {
112
127
  const name = node.name?.value;
113
- if (!allowedKinds.includes(node.kind)) return false;
128
+ if (!allowedKinds.includes(node.kind) || operations.includes(name)) return false;
114
129
 
115
- if (modelKinds.includes(node.kind) && !operations.includes(name)) {
130
+ if (modelKinds.includes(node.kind)) {
116
131
  // this.#schema.types[name] = schema.getType(name);
117
132
 
118
133
  model = this.#schema.models[name] = {
119
134
  name,
120
135
  key: name,
121
136
  fields: {},
137
+ crud: 'crud', // For use when creating API Queries and Mutations
138
+ scope: 'crud', // For use when defining types (how it's field.model reference can be used)
122
139
  idField: 'id',
123
- crud: 'crud',
124
- scope: 'crud',
125
140
  isPersistable: true,
126
141
  source: this.#config.dataSources?.default,
127
142
  loader: this.#config.dataLoaders?.default,
128
143
  directives: {},
129
144
  toString: () => name,
130
145
  };
131
- } else if (node.kind === Kind.FIELD_DEFINITION) {
146
+ } else if (fieldKinds.includes(node.kind)) {
132
147
  isField = true;
133
- field = model.fields[name] = {
148
+ field = {
134
149
  name,
135
150
  key: name,
136
- crud: 'crud',
137
151
  pipelines: pipelines.reduce((prev, key) => Object.assign(prev, { [key]: [] }), {}),
138
152
  directives: {},
139
153
  toString: () => name,
140
154
  };
155
+ if (model) model.fields[name] = field;
141
156
  } else if (node.kind === Kind.NON_NULL_TYPE) {
142
157
  field[isList ? 'isArrayRequired' : 'isRequired'] = true;
143
158
  } else if (node.kind === Kind.NAMED_TYPE) {
@@ -154,8 +169,8 @@ module.exports = class Schema {
154
169
 
155
170
  node.arguments.forEach((arg) => {
156
171
  const key = arg.name.value;
157
- const { value: val, values } = arg.value;
158
- const value = values ? values.map(n => n.value) : val;
172
+ const { value: val, values = { value: val }, kind } = arg.value;
173
+ const value = kind === 'NullValue' ? null : Util.map(values, n => n.value);
159
174
  target.directives[name][key] = value;
160
175
 
161
176
  if (name === directives.index) this.#schema.indexes[this.#schema.indexes.length - 1][key] = value;
@@ -214,7 +229,6 @@ module.exports = class Schema {
214
229
  case 'model-gqlScope': { model.crud = value; break; }
215
230
  case 'model-fieldScope': { model.scope = value; break; }
216
231
  case 'field-gqlScope': { field.crud = value; break; }
217
- case 'field-fieldScope': { field.scope = value; break; }
218
232
 
219
233
  // Pipelines
220
234
  default: {
@@ -230,7 +244,7 @@ module.exports = class Schema {
230
244
  return undefined; // Continue
231
245
  },
232
246
  leave: (node) => {
233
- if (modelKinds.includes(node.kind) && !operations.includes(node.name.value)) {
247
+ if (modelKinds.includes(node.kind)) {
234
248
  const $model = model;
235
249
  // const idField = $model.fields[$model.idField];
236
250
  // $model.primaryKey = Util.nvl(idField?.key, idField?.name, 'id');
@@ -295,11 +309,12 @@ module.exports = class Schema {
295
309
  // Field resolution comes first (unshift)
296
310
  thunks.unshift(($schema) => {
297
311
  $field.model = $schema.models[$field.type];
298
- $field.linkBy = $field.linkBy || $field.model?.idField;
312
+ $field.crud = Util.uvl($field.crud, $field.model?.scope, 'crud');
313
+ $field.linkBy ??= $field.model?.idField;
299
314
  $field.linkField = $field.isVirtual ? $model.fields[$model.idField] : $field;
300
315
  $field.isFKReference = !$field.isPrimaryKey && $field.model?.isMarkedModel && !$field.model?.isEmbedded;
316
+ // $field.isScalar = Boolean(!$field.model || scalars.includes($field.type));
301
317
  $field.isEmbedded = Boolean($field.model && !$field.isFKReference && !$field.isPrimaryKey);
302
- $field.isScalar = Boolean(!$field.model || scalars.includes($field.type));
303
318
 
304
319
  if ($field.isArray) $field.pipelines.normalize.unshift('toArray');
305
320
  if ($field.isPrimaryKey) $field.pipelines.serialize.unshift('$pk'); // Will create/convert to FK type always
@@ -431,7 +446,6 @@ module.exports = class Schema {
431
446
  ref: AutoGraphMixed # Specify the modelRef field's name (overrides isEmbedded)
432
447
  gqlScope: AutoGraphMixed # Dictate how GraphQL API behaves
433
448
  dalScope: AutoGraphMixed # Dictate how the DAL behaves
434
- fieldScope: AutoGraphMixed # Dictate how a FIELD may use me
435
449
  destruct: [AutoGraphPipelineEnum!]
436
450
  transform: [AutoGraphPipelineEnum!]
437
451
  deserialize: [AutoGraphPipelineEnum!]
@@ -453,9 +467,9 @@ module.exports = class Schema {
453
467
 
454
468
  static #api(schema) {
455
469
  // These models are for creating types
456
- const readModels = Object.values(schema.models).filter(model => model.crud?.includes('r'));
457
- const createModels = Object.values(schema.models).filter(model => model.crud?.includes('c'));
458
- const updateModels = Object.values(schema.models).filter(model => model.crud?.includes('u'));
470
+ const readModels = Object.values(schema.models).filter(model => [model.crud, model.scope].join()?.includes('r'));
471
+ const createModels = Object.values(schema.models).filter(model => [model.crud, model.scope].join()?.includes('c'));
472
+ const updateModels = Object.values(schema.models).filter(model => [model.crud, model.scope].join()?.includes('u'));
459
473
 
460
474
  // These are for defining schema queries/mutations
461
475
  const entityModels = Object.values(schema.models).filter(model => model.isEntity);
@@ -573,6 +587,47 @@ module.exports = class Schema {
573
587
  ): ${model}SubscriptionPayload!
574
588
  `)}
575
589
  }
590
+
591
+ ${subscriptionModels.map((model) => {
592
+ const fields = Object.values(model.fields).filter(field => field.crud?.includes('r'));
593
+
594
+ return `
595
+ input ${model}SubscriptionInputFilter {
596
+ when: [SubscriptionWhenEnum!]! = [preEvent, postEvent]
597
+ where: ${model}SubscriptionInputWhere! = {}
598
+ }
599
+
600
+ input ${model}SubscriptionInputWhere {
601
+ ${fields.map(field => `${field}: ${field.model?.isEntity ? `${field.model}InputWhere` : 'AutoGraphMixed'}`)}
602
+ }
603
+
604
+ type ${model}SubscriptionPayload {
605
+ event: ${model}SubscriptionPayloadEvent
606
+ query: ${model}SubscriptionQuery
607
+ }
608
+
609
+ type ${model}SubscriptionPayloadEvent {
610
+ crud: SubscriptionCrudEnum!
611
+ data: ${model}SubscriptionPayloadEventData!
612
+ }
613
+
614
+ type ${model}SubscriptionPayloadEventData {
615
+ ${fields.map(field => `${field}: ${Schema.#getGQLType(field)}`)}
616
+ }
617
+
618
+ interface ${model}SubscriptionQuery {
619
+ ${fields.map(field => `${field}: ${Schema.#getGQLType(field)}`)}
620
+ }
621
+
622
+ type ${model}Create implements ${model}SubscriptionQuery {
623
+ ${fields.map(field => `${field}: ${Schema.#getGQLType(field)}`)}
624
+ }
625
+
626
+ type ${model}Update implements ${model}SubscriptionQuery {
627
+ ${fields.map(field => `${field}: ${Schema.#getGQLType(field)}`)}
628
+ }
629
+ `;
630
+ })}
576
631
  ` : ''}
577
632
  `,
578
633
  resolvers: {