@graphprotocol/graph-cli 0.37.4 → 0.37.5

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 (100) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/dist/bin.js +6 -0
  3. package/dist/cli.js +26 -0
  4. package/dist/codegen/schema.js +244 -0
  5. package/dist/codegen/schema.test.js +455 -0
  6. package/dist/codegen/template.js +66 -0
  7. package/dist/codegen/types/conversions.js +335 -0
  8. package/dist/codegen/types/index.js +71 -0
  9. package/dist/codegen/types/index.test.js +563 -0
  10. package/dist/codegen/typescript.js +200 -0
  11. package/dist/codegen/util.js +40 -0
  12. package/dist/codegen/util.test.js +93 -0
  13. package/dist/command-helpers/abi.js +76 -0
  14. package/dist/command-helpers/auth.js +76 -0
  15. package/dist/command-helpers/compiler.js +66 -0
  16. package/dist/command-helpers/data-sources.js +29 -0
  17. package/dist/command-helpers/fs.js +9 -0
  18. package/dist/command-helpers/gluegun.js +48 -0
  19. package/dist/command-helpers/ipfs.js +5 -0
  20. package/dist/command-helpers/jsonrpc.js +27 -0
  21. package/dist/command-helpers/network.js +90 -0
  22. package/dist/command-helpers/network.test.js +86 -0
  23. package/dist/command-helpers/node.js +40 -0
  24. package/dist/command-helpers/scaffold.js +110 -0
  25. package/dist/command-helpers/spinner.js +80 -0
  26. package/dist/command-helpers/studio.js +14 -0
  27. package/dist/command-helpers/studio.test.js +41 -0
  28. package/dist/command-helpers/subgraph.js +22 -0
  29. package/dist/command-helpers/version.js +66 -0
  30. package/dist/command-helpers/version.test.js +186 -0
  31. package/dist/commands/add.js +187 -0
  32. package/dist/commands/auth.js +122 -0
  33. package/dist/commands/build.js +136 -0
  34. package/dist/commands/codegen.js +128 -0
  35. package/dist/commands/create.js +94 -0
  36. package/dist/commands/deploy.js +335 -0
  37. package/dist/commands/init.js +800 -0
  38. package/dist/commands/local.js +380 -0
  39. package/dist/commands/remove.js +95 -0
  40. package/dist/commands/test.js +293 -0
  41. package/dist/compiler/asc.js +83 -0
  42. package/dist/compiler/index.js +466 -0
  43. package/dist/debug.js +16 -0
  44. package/dist/migrations/mapping_api_version_0_0_1.js +83 -0
  45. package/dist/migrations/mapping_api_version_0_0_2.js +83 -0
  46. package/dist/migrations/mapping_api_version_0_0_3.js +83 -0
  47. package/dist/migrations/mapping_api_version_0_0_4.js +85 -0
  48. package/dist/migrations/mapping_api_version_0_0_5.js +85 -0
  49. package/dist/migrations/spec_version_0_0_2.js +45 -0
  50. package/dist/migrations/spec_version_0_0_3.js +50 -0
  51. package/dist/migrations/util/load-manifest.js +16 -0
  52. package/dist/migrations/util/versions.js +28 -0
  53. package/dist/migrations.js +54 -0
  54. package/dist/protocols/arweave/manifest.graphql +57 -0
  55. package/dist/protocols/arweave/scaffold/manifest.js +18 -0
  56. package/dist/protocols/arweave/scaffold/mapping.js +52 -0
  57. package/dist/protocols/arweave/subgraph.js +23 -0
  58. package/dist/protocols/contract.js +2 -0
  59. package/dist/protocols/cosmos/manifest.graphql +76 -0
  60. package/dist/protocols/cosmos/scaffold/manifest.js +15 -0
  61. package/dist/protocols/cosmos/scaffold/mapping.js +38 -0
  62. package/dist/protocols/cosmos/subgraph.js +28 -0
  63. package/dist/protocols/ethereum/abi.js +138 -0
  64. package/dist/protocols/ethereum/codegen/abi.js +474 -0
  65. package/dist/protocols/ethereum/codegen/abi.test.js +325 -0
  66. package/dist/protocols/ethereum/codegen/template.js +52 -0
  67. package/dist/protocols/ethereum/contract.js +22 -0
  68. package/dist/protocols/ethereum/manifest.graphql +94 -0
  69. package/dist/protocols/ethereum/scaffold/manifest.js +32 -0
  70. package/dist/protocols/ethereum/scaffold/mapping.js +65 -0
  71. package/dist/protocols/ethereum/subgraph.js +172 -0
  72. package/dist/protocols/ethereum/type-generator.js +123 -0
  73. package/dist/protocols/index.js +266 -0
  74. package/dist/protocols/ipfs/codegen/file_template.js +53 -0
  75. package/dist/protocols/near/contract.js +41 -0
  76. package/dist/protocols/near/manifest.graphql +64 -0
  77. package/dist/protocols/near/scaffold/manifest.js +16 -0
  78. package/dist/protocols/near/scaffold/mapping.js +38 -0
  79. package/dist/protocols/near/subgraph.js +23 -0
  80. package/dist/protocols/subgraph.js +2 -0
  81. package/dist/protocols/substreams/manifest.graphql +45 -0
  82. package/dist/protocols/substreams/scaffold/manifest.js +12 -0
  83. package/dist/protocols/substreams/subgraph.js +23 -0
  84. package/dist/scaffold/cosmos.test.js +82 -0
  85. package/dist/scaffold/ethereum.test.js +496 -0
  86. package/dist/scaffold/index.js +133 -0
  87. package/dist/scaffold/mapping.js +57 -0
  88. package/dist/scaffold/near.test.js +85 -0
  89. package/dist/scaffold/schema.js +86 -0
  90. package/dist/scaffold/tests.js +179 -0
  91. package/dist/schema.js +54 -0
  92. package/dist/subgraph.js +243 -0
  93. package/dist/type-generator.js +225 -0
  94. package/dist/validation/contract.js +44 -0
  95. package/dist/validation/index.js +10 -0
  96. package/dist/validation/manifest.js +266 -0
  97. package/dist/validation/schema.js +768 -0
  98. package/dist/validation/schema.test.js +35 -0
  99. package/dist/watcher.js +79 -0
  100. package/package.json +1 -1
@@ -0,0 +1,768 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ exports.validateSchema = exports.typeSuggestion = void 0;
30
+ const fs_1 = __importDefault(require("fs"));
31
+ const graphql = __importStar(require("graphql/language"));
32
+ const immutable_1 = __importDefault(require("immutable"));
33
+ const List = immutable_1.default.List;
34
+ const Set = immutable_1.default.Set;
35
+ // Builtin scalar types
36
+ const BUILTIN_SCALAR_TYPES = ['Boolean', 'Int', 'BigDecimal', 'String', 'BigInt', 'Bytes', 'ID'];
37
+ // Type suggestions for common mistakes
38
+ const TYPE_SUGGESTIONS = [
39
+ ['Address', 'Bytes'],
40
+ ['address', 'Bytes'],
41
+ ['Account', 'String'],
42
+ ['account', 'String'],
43
+ ['AccountId', 'String'],
44
+ ['AccountID', 'String'],
45
+ ['accountId', 'String'],
46
+ ['accountid', 'String'],
47
+ ['bytes', 'Bytes'],
48
+ ['string', 'String'],
49
+ ['bool', 'Boolean'],
50
+ ['boolean', 'Boolean'],
51
+ ['Bool', 'Boolean'],
52
+ ['float', 'BigDecimal'],
53
+ ['Float', 'BigDecimal'],
54
+ ['int', 'Int'],
55
+ ['uint', 'BigInt'],
56
+ ['owner', 'String'],
57
+ ['Owner', 'String'],
58
+ [/^(u|uint)(8|16|24)$/, 'Int'],
59
+ [/^(i|int)(8|16|24|32)$/, 'Int'],
60
+ [/^(u|uint)32$/, 'BigInt'],
61
+ [
62
+ /^(u|uint|i|int)(40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)$/,
63
+ 'BigInt',
64
+ ],
65
+ ];
66
+ // As a convention, the type _Schema_ is reserved to define imports on.
67
+ const RESERVED_TYPE = '_Schema_';
68
+ /**
69
+ * Returns a GraphQL type suggestion for a given input type.
70
+ * Returns `undefined` if no suggestion is available for the type.
71
+ */
72
+ const typeSuggestion = (typeName) => TYPE_SUGGESTIONS.filter(([pattern, _]) => {
73
+ return typeof pattern === 'string' ? pattern === typeName : typeName.match(pattern);
74
+ }).map(([_, suggestion]) => suggestion)[0];
75
+ exports.typeSuggestion = typeSuggestion;
76
+ const loadSchema = (filename) => {
77
+ try {
78
+ return fs_1.default.readFileSync(filename, 'utf-8');
79
+ }
80
+ catch (e) {
81
+ throw new Error(`Failed to load GraphQL schema: ${e}`);
82
+ }
83
+ };
84
+ const parseSchema = (doc) => {
85
+ try {
86
+ return graphql.parse(doc);
87
+ }
88
+ catch (e) {
89
+ throw new Error(`Invalid GraphQL schema: ${e}`);
90
+ }
91
+ };
92
+ const validateEntityDirective = (def) => def.directives.find((directive) => directive.name.value === 'entity')
93
+ ? List()
94
+ : immutable_1.default.fromJS([
95
+ {
96
+ loc: def.loc,
97
+ entity: def.name.value,
98
+ message: `Defined without @entity directive`,
99
+ },
100
+ ]);
101
+ const validateEntityID = (def) => {
102
+ const idField = def.fields.find((field) => field.name.value === 'id');
103
+ if (idField === undefined) {
104
+ return immutable_1.default.fromJS([
105
+ {
106
+ loc: def.loc,
107
+ entity: def.name.value,
108
+ message: `Missing field: id: ID!`,
109
+ },
110
+ ]);
111
+ }
112
+ if (idField.type.kind === 'NonNullType' &&
113
+ idField.type.type.kind === 'NamedType' &&
114
+ (idField.type.type.name.value === 'ID' ||
115
+ idField.type.type.name.value === 'Bytes' ||
116
+ idField.type.type.name.value === 'String')) {
117
+ return List();
118
+ }
119
+ return immutable_1.default.fromJS([
120
+ {
121
+ loc: idField.loc,
122
+ entity: def.name.value,
123
+ message: `Field 'id': Entity ids must be of type Bytes! or String!`,
124
+ },
125
+ ]);
126
+ };
127
+ const validateListFieldType = (def, field) => field.type.kind === 'NonNullType' &&
128
+ field.type.kind === 'ListType' &&
129
+ field.type.type.kind !== 'NonNullType'
130
+ ? immutable_1.default.fromJS([
131
+ {
132
+ loc: field.loc,
133
+ entity: def.name.value,
134
+ message: `\
135
+ Field '${field.name.value}':
136
+ Field has type [${field.type.type.name.value}]! but
137
+ must have type [${field.type.type.name.value}!]!
138
+
139
+ Reason: Lists with null elements are not supported.`,
140
+ },
141
+ ])
142
+ : field.type.kind === 'ListType' && field.type.type.kind !== 'NonNullType'
143
+ ? immutable_1.default.fromJS([
144
+ {
145
+ loc: field.loc,
146
+ entity: def.name.value,
147
+ message: `\
148
+ Field '${field.name.value}':
149
+ Field has type [${field.type.type.name.value}] but
150
+ must have type [${field.type.type.name.value}!]
151
+
152
+ Reason: Lists with null elements are not supported.`,
153
+ },
154
+ ])
155
+ : List();
156
+ const unwrapType = (type) => {
157
+ const innerTypeFromList = (listType) => listType.type.kind === 'NonNullType' ? innerTypeFromNonNull(listType.type) : listType.type;
158
+ const innerTypeFromNonNull = (nonNullType) => nonNullType.type.kind === 'ListType' ? innerTypeFromList(nonNullType.type) : nonNullType.type;
159
+ // Obtain the inner-most type from the field
160
+ return type.kind === 'NonNullType'
161
+ ? innerTypeFromNonNull(type)
162
+ : type.kind === 'ListType'
163
+ ? innerTypeFromList(type)
164
+ : type;
165
+ };
166
+ const gatherLocalTypes = (defs) => defs
167
+ .filter(def => def.kind === 'ObjectTypeDefinition' ||
168
+ def.kind === 'EnumTypeDefinition' ||
169
+ def.kind === 'InterfaceTypeDefinition')
170
+ .map(def =>
171
+ // @ts-expect-error TODO: name field does not exist on definition, really?
172
+ def.name.value);
173
+ const gatherImportedTypes = (defs) => defs
174
+ .filter(def => def.kind === 'ObjectTypeDefinition' &&
175
+ def.name.value == RESERVED_TYPE &&
176
+ def.directives?.find((directive) => directive.name.value == 'import' &&
177
+ directive.arguments.find((argument) => argument.name.value == 'types')))
178
+ .map(def =>
179
+ // @ts-expect-error TODO: directives field does not exist on definition, really?
180
+ def.directives
181
+ .filter((directive) => directive.name.value == 'import' &&
182
+ directive.arguments.find((argument) => argument.name.value == 'types'))
183
+ .map((imp) => imp.arguments.find((argument) => argument.name.value == 'types' && argument.value.kind == 'ListValue'))
184
+ .map((arg) => arg.value.values.map((type) => type.kind == 'StringValue'
185
+ ? type.value
186
+ : type.kind == 'ObjectValue' &&
187
+ type.fields.find((field) => field.name.value == 'as' && field.value.kind == 'StringValue')
188
+ ? type.fields.find((field) => field.name.value == 'as').value.value
189
+ : undefined)))
190
+ .reduce((flattened, types_args) => flattened.concat(types_args.reduce((flattened, types_arg) => {
191
+ types_arg.forEach(type => (type ? flattened.push(type) : undefined));
192
+ return flattened;
193
+ }, [])), []);
194
+ const entityTypeByName = (defs, name) => defs
195
+ .filter(def => def.kind === 'InterfaceTypeDefinition' ||
196
+ (def.kind === 'ObjectTypeDefinition' &&
197
+ def.directives.find((directive) => directive.name.value === 'entity')))
198
+ .find(def => def.name.value === name);
199
+ const fieldTargetEntityName = (field) => unwrapType(field.type).name.value;
200
+ const fieldTargetEntity = (defs, field) => entityTypeByName(defs, fieldTargetEntityName(field));
201
+ const validateInnerFieldType = (defs, def, field) => {
202
+ const innerType = unwrapType(field.type);
203
+ // Get the name of the type
204
+ const typeName = innerType.name.value;
205
+ // Look up a possible suggestion for the type to catch common mistakes
206
+ const suggestion = (0, exports.typeSuggestion)(typeName);
207
+ // Collect all types that we can use here: built-ins + entities + enums + interfaces
208
+ const availableTypes = List.of(...BUILTIN_SCALAR_TYPES, ...gatherLocalTypes(defs), ...gatherImportedTypes(defs));
209
+ // Check whether the type name is available, otherwise return an error
210
+ return availableTypes.includes(typeName)
211
+ ? List()
212
+ : immutable_1.default.fromJS([
213
+ {
214
+ loc: field.loc,
215
+ entity: def.name.value,
216
+ message: `\
217
+ Field '${field.name.value}': \
218
+ Unknown type '${typeName}'.${suggestion !== undefined ? ` Did you mean '${suggestion}'?` : ''}`,
219
+ },
220
+ ]);
221
+ };
222
+ const validateEntityFieldType = (defs, def, field) => List.of(...validateListFieldType(def, field), ...validateInnerFieldType(defs, def, field));
223
+ const validateEntityFieldArguments = (_defs, def, field) => field.arguments.length > 0
224
+ ? immutable_1.default.fromJS([
225
+ {
226
+ loc: field.loc,
227
+ entity: def.name.value,
228
+ message: `\
229
+ Field '${field.name.value}': \
230
+ Field arguments are not supported.`,
231
+ },
232
+ ])
233
+ : List();
234
+ const validateDerivedFromDirective = (defs, def, field, directive) => {
235
+ // Validate that there is a `field` argument and nothing else
236
+ if (directive.arguments.length !== 1 || directive.arguments[0].name.value !== 'field') {
237
+ return immutable_1.default.fromJS([
238
+ {
239
+ loc: directive.loc,
240
+ entity: def.name.value,
241
+ message: `\
242
+ Field '${field.name.value}': \
243
+ @derivedFrom directive must have a 'field' argument`,
244
+ },
245
+ ]);
246
+ }
247
+ // Validate that the "field" argument value is a string
248
+ if (directive.arguments[0].value.kind !== 'StringValue') {
249
+ return immutable_1.default.fromJS([
250
+ {
251
+ loc: directive.loc,
252
+ entity: def.name.value,
253
+ message: `\
254
+ Field '${field.name.value}': \
255
+ Value of the @derivedFrom 'field' argument must be a string`,
256
+ },
257
+ ]);
258
+ }
259
+ const targetEntity = fieldTargetEntity(defs, field);
260
+ if (targetEntity === undefined) {
261
+ // This is handled in `validateInnerFieldType` but if we don't catch
262
+ // the undefined case here, the code below will throw, as it assumes
263
+ // the target entity exists
264
+ return immutable_1.default.fromJS([]);
265
+ }
266
+ const derivedFromField = targetEntity.fields.find((field) => field.name.value === directive.arguments[0].value.value);
267
+ if (derivedFromField === undefined) {
268
+ return immutable_1.default.fromJS([
269
+ {
270
+ loc: directive.loc,
271
+ entity: def.name.value,
272
+ message: `\
273
+ Field '${field.name.value}': \
274
+ @derivedFrom field '${directive.arguments[0].value.value}' \
275
+ does not exist on type '${targetEntity.name.value}'`,
276
+ },
277
+ ]);
278
+ }
279
+ const backrefTypeName = unwrapType(derivedFromField.type);
280
+ const backRefEntity = entityTypeByName(defs, backrefTypeName.name.value);
281
+ // The field we are deriving from must either have type 'def' or one of the
282
+ // interface types that 'def' is implementing
283
+ if (!backRefEntity ||
284
+ (backRefEntity.name.value !== def.name.value &&
285
+ !def.interfaces.find((intf) => intf.name.value === backRefEntity.name.value))) {
286
+ return immutable_1.default.fromJS([
287
+ {
288
+ loc: directive.loc,
289
+ entity: def.name.value,
290
+ message: `\
291
+ Field '${field.name.value}': \
292
+ @derivedFrom field '${directive.arguments[0].value.value}' \
293
+ on type '${targetEntity.name.value}' must have the type \
294
+ '${def.name.value}', '${def.name.value}!', '[${def.name.value}!]!', \
295
+ or one of the interface types that '${def.name.value}' implements`,
296
+ },
297
+ ]);
298
+ }
299
+ return List();
300
+ };
301
+ const validateEntityFieldDirective = (defs, def, field, directive) => directive.name.value === 'derivedFrom'
302
+ ? validateDerivedFromDirective(defs, def, field, directive)
303
+ : List();
304
+ const validateEntityFieldDirectives = (defs, def, field) => field.directives.reduce((errors, directive) => errors.concat(validateEntityFieldDirective(defs, def, field, directive)), List());
305
+ const validateEntityFields = (defs, def) => def.fields.reduce((errors, field) => errors
306
+ .concat(validateEntityFieldType(defs, def, field))
307
+ .concat(validateEntityFieldArguments(defs, def, field))
308
+ .concat(validateEntityFieldDirectives(defs, def, field)), List());
309
+ const validateNoImportDirective = (def) => def.directives.find((directive) => directive.name.value == 'import')
310
+ ? List([
311
+ immutable_1.default.fromJS({
312
+ loc: def.name.loc,
313
+ entity: def.name.value,
314
+ message: `@import directive only allowed on '${RESERVED_TYPE}' type`,
315
+ }),
316
+ ])
317
+ : List();
318
+ const validateNoFulltext = (def) => def.directives.find((directive) => directive.name.value == 'fulltext')
319
+ ? List([
320
+ immutable_1.default.fromJS({
321
+ loc: def.name.loc,
322
+ entity: def.name.value,
323
+ message: `@fulltext directive only allowed on '${RESERVED_TYPE}' type`,
324
+ }),
325
+ ])
326
+ : List();
327
+ const validateFulltextFields = (def, directive) => {
328
+ return directive.arguments.reduce((errors, argument) => {
329
+ return errors.concat(['name', 'language', 'algorithm', 'include'].includes(argument.name.value)
330
+ ? List([])
331
+ : List([
332
+ immutable_1.default.fromJS({
333
+ loc: directive.name.loc,
334
+ entity: def.name.value,
335
+ directive: fulltextDirectiveName(directive),
336
+ message: `found invalid argument: '${argument.name.value}', @fulltext directives only allow 'name', 'language', 'algorithm', and 'includes' arguments`,
337
+ }),
338
+ ]));
339
+ }, List([]));
340
+ };
341
+ const validateFulltextName = (def, directive) => {
342
+ const name = directive.arguments.find((argument) => argument.name.value == 'name');
343
+ return name
344
+ ? validateFulltextArgumentName(def, directive, name)
345
+ : List([
346
+ immutable_1.default.fromJS({
347
+ loc: directive.name.loc,
348
+ entity: def.name.value,
349
+ directive: fulltextDirectiveName(directive),
350
+ message: `@fulltext argument 'name' must be specified`,
351
+ }),
352
+ ]);
353
+ };
354
+ const validateFulltextArgumentName = (def, directive, argument) => {
355
+ return argument.value.kind != 'StringValue'
356
+ ? List([
357
+ immutable_1.default.fromJS({
358
+ loc: directive.name.loc,
359
+ entity: def.name.value,
360
+ directive: fulltextDirectiveName(directive),
361
+ message: `@fulltext argument 'name' must be a string`,
362
+ }),
363
+ ])
364
+ : List([]);
365
+ };
366
+ const fulltextDirectiveName = (directive) => {
367
+ const arg = directive.arguments.find((argument) => argument.name.value == 'name');
368
+ return arg ? arg.value.value : 'Other';
369
+ };
370
+ const validateFulltextLanguage = (def, directive) => {
371
+ const language = directive.arguments.find((argument) => argument.name.value == 'language');
372
+ return language
373
+ ? validateFulltextArgumentLanguage(def, directive, language)
374
+ : List([
375
+ immutable_1.default.fromJS({
376
+ loc: directive.name.loc,
377
+ entity: def.name.value,
378
+ directive: fulltextDirectiveName(directive),
379
+ message: `@fulltext argument 'language' must be specified`,
380
+ }),
381
+ ]);
382
+ };
383
+ const validateFulltextArgumentLanguage = (def, directive, argument) => {
384
+ const languages = [
385
+ 'simple',
386
+ 'da',
387
+ 'nl',
388
+ 'en',
389
+ 'fi',
390
+ 'fr',
391
+ 'de',
392
+ 'hu',
393
+ 'it',
394
+ 'no',
395
+ 'pt',
396
+ 'ro',
397
+ 'ru',
398
+ 'es',
399
+ 'sv',
400
+ 'tr',
401
+ ];
402
+ if (argument.value.kind != 'EnumValue') {
403
+ return List([
404
+ immutable_1.default.fromJS({
405
+ loc: directive.name.loc,
406
+ entity: def.name.value,
407
+ directive: fulltextDirectiveName(directive),
408
+ message: `@fulltext 'language' value must be one of: ${languages.join(', ')}`,
409
+ }),
410
+ ]);
411
+ }
412
+ if (!languages.includes(argument.value.value)) {
413
+ return List([
414
+ immutable_1.default.fromJS({
415
+ loc: directive.name.loc,
416
+ entity: def.name.value,
417
+ directive: fulltextDirectiveName(directive),
418
+ message: `@fulltext directive 'language' value must be one of: ${languages.join(', ')}`,
419
+ }),
420
+ ]);
421
+ }
422
+ return List([]);
423
+ };
424
+ const validateFulltextAlgorithm = (def, directive) => {
425
+ const algorithm = directive.arguments.find((argument) => argument.name.value == 'algorithm');
426
+ return algorithm
427
+ ? validateFulltextArgumentAlgorithm(def, directive, algorithm)
428
+ : List([
429
+ immutable_1.default.fromJS({
430
+ loc: directive.name.loc,
431
+ entity: def.name.value,
432
+ directive: fulltextDirectiveName(directive),
433
+ message: `@fulltext argument 'algorithm' must be specified`,
434
+ }),
435
+ ]);
436
+ };
437
+ const validateFulltextArgumentAlgorithm = (def, directive, argument) => {
438
+ if (argument.value.kind != 'EnumValue') {
439
+ return List([
440
+ immutable_1.default.fromJS({
441
+ loc: directive.name.loc,
442
+ entity: def.name.value,
443
+ directive: fulltextDirectiveName(directive),
444
+ message: `@fulltext argument 'algorithm' must be one of: rank, proximityRank`,
445
+ }),
446
+ ]);
447
+ }
448
+ if (!['rank', 'proximityRank'].includes(argument.value.value)) {
449
+ return List([
450
+ immutable_1.default.fromJS({
451
+ loc: directive.name.loc,
452
+ entity: def.name.value,
453
+ directive: fulltextDirectiveName(directive),
454
+ message: `@fulltext 'algorithm' value, '${argument.value.value}', must be one of: rank, proximityRank`,
455
+ }),
456
+ ]);
457
+ }
458
+ return List([]);
459
+ };
460
+ const validateFulltextInclude = (def, directive) => {
461
+ const include = directive.arguments.find((argument) => argument.name.value == 'include');
462
+ if (include) {
463
+ if (include.value.kind != 'ListValue') {
464
+ return List([
465
+ immutable_1.default.fromJS({
466
+ loc: directive.name.loc,
467
+ entity: def.name.value,
468
+ directive: fulltextDirectiveName(directive),
469
+ message: `@fulltext argument 'include' must be a list`,
470
+ }),
471
+ ]);
472
+ }
473
+ return include.value.values.reduce((errors, type) => errors.concat(validateFulltextArgumentInclude(def, directive, type)), List());
474
+ }
475
+ return List([
476
+ immutable_1.default.fromJS({
477
+ loc: directive.name.loc,
478
+ entity: def.name.value,
479
+ directive: fulltextDirectiveName(directive),
480
+ message: `@fulltext argument 'include' must be specified`,
481
+ }),
482
+ ]);
483
+ };
484
+ const validateFulltextArgumentInclude = (def, directive, argument) => {
485
+ if (argument.kind != 'ObjectValue') {
486
+ return List([
487
+ immutable_1.default.fromJS({
488
+ loc: directive.name.loc,
489
+ entity: def.name.value,
490
+ directive: fulltextDirectiveName(directive),
491
+ message: `@fulltext argument 'include' must have the form '[{entity: "entityName", fields: [{name: "fieldName"}, ...]} ...]`,
492
+ }),
493
+ ]);
494
+ }
495
+ if (argument.fields.length != 2) {
496
+ return List([
497
+ immutable_1.default.fromJS({
498
+ loc: directive.name.loc,
499
+ entity: def.name.value,
500
+ directive: fulltextDirectiveName(directive),
501
+ message: `@fulltext argument include must have two fields, 'entity' and 'fields'`,
502
+ }),
503
+ ]);
504
+ }
505
+ return argument.fields.reduce((errors, field) => errors.concat(validateFulltextArgumentIncludeFields(def, directive, field)), List([]));
506
+ };
507
+ const validateFulltextArgumentIncludeFields = (def, directive, field) => {
508
+ if (!['entity', 'fields'].includes(field.name.value)) {
509
+ return List([
510
+ immutable_1.default.fromJS({
511
+ loc: directive.name.loc,
512
+ entity: def.name.value,
513
+ directive: fulltextDirectiveName(directive),
514
+ message: `@fulltext argument 'include > ${field.name.value}' must be be one of: entity, fields`,
515
+ }),
516
+ ]);
517
+ }
518
+ if (field.name.value == 'entity' && field.value.kind != 'StringValue') {
519
+ return List([
520
+ immutable_1.default.fromJS({
521
+ loc: directive.name.loc,
522
+ entity: def.name.value,
523
+ directive: fulltextDirectiveName(directive),
524
+ message: `@fulltext argument 'include > entity' must be the name of an entity in the schema enclosed in double quotes`,
525
+ }),
526
+ ]);
527
+ }
528
+ if (field.name.value == 'fields' && field.value.kind != 'ListValue') {
529
+ return List([
530
+ immutable_1.default.fromJS({
531
+ loc: directive.name.loc,
532
+ entity: def.name.value,
533
+ directive: fulltextDirectiveName(directive),
534
+ message: `@fulltext argument 'include > fields' must be a list`,
535
+ }),
536
+ ]);
537
+ }
538
+ if (field.name.value == 'fields' && field.value.kind == 'ListValue') {
539
+ return field.value.values.reduce((errors, field) => errors.concat(validateFulltextArgumentIncludeFieldsObjects(def, directive, field)), List([]));
540
+ }
541
+ return List([]);
542
+ };
543
+ const validateFulltextArgumentIncludeFieldsObjects = (def, directive, argument) => {
544
+ if (argument.kind != 'ObjectValue') {
545
+ return List([
546
+ immutable_1.default.fromJS({
547
+ loc: directive.name.loc,
548
+ entity: def.name.value,
549
+ directive: fulltextDirectiveName(directive),
550
+ message: `@fulltext argument 'include > fields' must have the form '[{ name: "fieldName" }, ...]`,
551
+ }),
552
+ ]);
553
+ }
554
+ return argument.fields.reduce((errors, field) => errors.concat(validateFulltextArgumentIncludeArgumentFieldsObject(def, directive, field)), List());
555
+ };
556
+ const validateFulltextArgumentIncludeArgumentFieldsObject = (def, directive, field) => {
557
+ if (!['name'].includes(field.name.value)) {
558
+ return List([
559
+ immutable_1.default.fromJS({
560
+ loc: directive.name.loc,
561
+ entity: def.name.value,
562
+ directive: fulltextDirectiveName(directive),
563
+ message: `@fulltext argument 'include > fields' has invalid member '${field.name.value}', must be one of: name`,
564
+ }),
565
+ ]);
566
+ }
567
+ if (field.name.value == 'name' && field.value.kind != 'StringValue') {
568
+ return List([
569
+ immutable_1.default.fromJS({
570
+ loc: directive.name.loc,
571
+ entity: def.name.value,
572
+ directive: fulltextDirectiveName(directive),
573
+ message: `@fulltext argument 'include > fields > name' must be the name of an entity field enclosed in double quotes`,
574
+ }),
575
+ ]);
576
+ }
577
+ return List([]);
578
+ };
579
+ const importDirectiveTypeValidators = {
580
+ StringValue: (_def, _directive, _type) => List(),
581
+ ObjectValue: (def, directive, type) => {
582
+ const errors = List();
583
+ if (type.fields.length != 2) {
584
+ return errors.push(immutable_1.default.fromJS({
585
+ loc: directive.name.loc,
586
+ entity: def.name.value,
587
+ message: `Import must be one of "Name" or { name: "Name", as: "Alias" }`,
588
+ }));
589
+ }
590
+ return type.fields.reduce((errors, field) => {
591
+ if (!['name', 'as'].includes(field.name.value)) {
592
+ return errors.push(immutable_1.default.fromJS({
593
+ loc: directive.name.loc,
594
+ entity: def.name.value,
595
+ message: `@import field '${field.name.value}' invalid, may only be one of: name, as`,
596
+ }));
597
+ }
598
+ if (field.value.kind != 'StringValue') {
599
+ return errors.push(immutable_1.default.fromJS({
600
+ loc: directive.name.loc,
601
+ entity: def.name.value,
602
+ message: `@import fields [name, as] must be strings`,
603
+ }));
604
+ }
605
+ return errors;
606
+ }, errors);
607
+ },
608
+ };
609
+ const validateImportDirectiveType = (def, directive, type) => {
610
+ return importDirectiveTypeValidators[type.kind]
611
+ ? importDirectiveTypeValidators[type.kind](def, directive, type)
612
+ : List([
613
+ immutable_1.default.fromJS({
614
+ loc: directive.name.loc,
615
+ entity: def.name.value,
616
+ message: `Import must be one of "Name" or { name: "Name", as: "Alias" }`,
617
+ }),
618
+ ]);
619
+ };
620
+ const validateImportDirectiveArgumentTypes = (def, directive, argument) => {
621
+ if (argument.value.kind != 'ListValue') {
622
+ return List([
623
+ immutable_1.default.fromJS({
624
+ loc: directive.name.loc,
625
+ entity: def.name.value,
626
+ message: `@import argument 'types' must be an list`,
627
+ }),
628
+ ]);
629
+ }
630
+ return argument.value.values.reduce((errors, type) => errors.concat(validateImportDirectiveType(def, directive, type)), List());
631
+ };
632
+ const validateImportDirectiveArgumentFrom = (def, directive, argument) => {
633
+ if (argument.value.kind != 'ObjectValue') {
634
+ return List([
635
+ immutable_1.default.fromJS({
636
+ loc: directive.name.loc,
637
+ entity: def.name.value,
638
+ message: `@import argument 'from' must be an object`,
639
+ }),
640
+ ]);
641
+ }
642
+ if (argument.value.fields.length != 1) {
643
+ return List([
644
+ immutable_1.default.fromJS({
645
+ loc: directive.name.loc,
646
+ entity: def.name.value,
647
+ message: `@import argument 'from' must have an 'id' or 'name' field`,
648
+ }),
649
+ ]);
650
+ }
651
+ return argument.value.fields.reduce((errors, field) => {
652
+ if (!['name', 'id'].includes(field.name.value)) {
653
+ return errors.push(immutable_1.default.fromJS({
654
+ loc: field.name.loc,
655
+ entity: def.name.value,
656
+ message: `@import argument 'from' must be one of { name: "Name" } or { id: "ID" }`,
657
+ }));
658
+ }
659
+ if (field.value.kind != 'StringValue') {
660
+ return errors.push(immutable_1.default.fromJS({
661
+ loc: field.name.loc,
662
+ entity: def.name.value,
663
+ message: `@import argument 'from' must be one of { name: "Name" } or { id: "ID" } with string values`,
664
+ }));
665
+ }
666
+ return errors;
667
+ }, List());
668
+ };
669
+ const validateImportDirectiveFields = (def, directive) => {
670
+ return directive.arguments.reduce((errors, argument) => {
671
+ return errors.concat(['types', 'from'].includes(argument.name.value)
672
+ ? List([])
673
+ : List([
674
+ immutable_1.default.fromJS({
675
+ loc: directive.name.loc,
676
+ entity: def.name.value,
677
+ message: `found invalid argument: '${argument.name.value}', @import directives only allow 'types' and 'from' arguments`,
678
+ }),
679
+ ]));
680
+ }, List([]));
681
+ };
682
+ const validateImportDirectiveTypes = (def, directive) => {
683
+ const types = directive.arguments.find((argument) => argument.name.value == 'types');
684
+ return types
685
+ ? validateImportDirectiveArgumentTypes(def, directive, types)
686
+ : List([
687
+ immutable_1.default.fromJS({
688
+ loc: directive.name.loc,
689
+ entity: def.name.value,
690
+ message: `@import argument 'types' must be specified`,
691
+ }),
692
+ ]);
693
+ };
694
+ const validateImportDirectiveFrom = (def, directive) => {
695
+ const from = directive.arguments.find((argument) => argument.name.value == 'from');
696
+ return from
697
+ ? validateImportDirectiveArgumentFrom(def, directive, from)
698
+ : List([
699
+ immutable_1.default.fromJS({
700
+ loc: directive.name.loc,
701
+ entity: def.name.value,
702
+ message: `@import argument 'from' must be specified`,
703
+ }),
704
+ ]);
705
+ };
706
+ const validateImportDirective = (def, directive) => List.of(...validateImportDirectiveFields(def, directive), ...validateImportDirectiveTypes(def, directive), ...validateImportDirectiveFrom(def, directive));
707
+ const validateFulltext = (def, directive) => List.of(...validateFulltextFields(def, directive), ...validateFulltextName(def, directive), ...validateFulltextLanguage(def, directive), ...validateFulltextAlgorithm(def, directive), ...validateFulltextInclude(def, directive));
708
+ const validateSubgraphSchemaDirective = (def, directive) => {
709
+ if (directive.name.value == 'import') {
710
+ return validateImportDirective(def, directive);
711
+ }
712
+ if (directive.name.value == 'fulltext') {
713
+ return validateFulltext(def, directive);
714
+ }
715
+ return List([
716
+ immutable_1.default.fromJS({
717
+ loc: directive.name.loc,
718
+ entity: def.name.value,
719
+ message: `${RESERVED_TYPE} type only allows @import and @fulltext directives`,
720
+ }),
721
+ ]);
722
+ };
723
+ const validateSubgraphSchemaDirectives = (def) => def.directives.reduce((errors, directive) => errors.concat(validateSubgraphSchemaDirective(def, directive)), List());
724
+ const validateTypeHasNoFields = (def) => def.fields.length
725
+ ? List([
726
+ immutable_1.default.fromJS({
727
+ loc: def.name.loc,
728
+ entity: def.name.value,
729
+ message: `${def.name.value} type is not allowed any fields by convention`,
730
+ }),
731
+ ])
732
+ : List();
733
+ const validateAtLeastOneExtensionField = (_def) => List();
734
+ const typeDefinitionValidators = {
735
+ ObjectTypeDefinition: (defs, def) => def.name && def.name.value == RESERVED_TYPE
736
+ ? List.of(...validateSubgraphSchemaDirectives(def), ...validateTypeHasNoFields(def))
737
+ : List.of(...validateEntityDirective(def), ...validateEntityID(def), ...validateEntityFields(defs, def), ...validateNoImportDirective(def), ...validateNoFulltext(def)),
738
+ ObjectTypeExtension: (_defs, def) => validateAtLeastOneExtensionField(def),
739
+ };
740
+ const validateTypeDefinition = (defs, def) => typeDefinitionValidators[def.kind] !== undefined
741
+ ? typeDefinitionValidators[def.kind](defs, def)
742
+ : List();
743
+ const validateTypeDefinitions = (defs) => defs.reduce((errors, def) => errors.concat(validateTypeDefinition(defs, def)), List());
744
+ const validateNamingCollisionsInTypes = (types) => {
745
+ let seen = Set();
746
+ let conflicting = Set();
747
+ return types.reduce((errors, type) => {
748
+ if (seen.has(type) && !conflicting.has(type)) {
749
+ errors = errors.push(immutable_1.default.fromJS({
750
+ loc: { start: 1, end: 1 },
751
+ entity: type,
752
+ message: `Type '${type}' is defined more than once`,
753
+ }));
754
+ conflicting = conflicting.add(type);
755
+ }
756
+ else {
757
+ seen = seen.add(type);
758
+ }
759
+ return errors;
760
+ }, List());
761
+ };
762
+ const validateNamingCollisions = (local, imported) => validateNamingCollisionsInTypes(local.concat(imported));
763
+ const validateSchema = (filename) => {
764
+ const doc = loadSchema(filename);
765
+ const schema = parseSchema(doc);
766
+ return List.of(...validateTypeDefinitions(schema.definitions), ...validateNamingCollisions(gatherLocalTypes(schema.definitions), gatherImportedTypes(schema.definitions)));
767
+ };
768
+ exports.validateSchema = validateSchema;