@graphprotocol/graph-cli 0.37.2 → 0.37.3-alpha-20230110163550-d64bef6

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