wgc 0.84.2 → 0.85.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/package.json +1 -1
  2. package/dist/src/commands/grpc-service/commands/generate.js +85 -0
  3. package/dist/src/commands/grpc-service/commands/generate.js.map +1 -0
  4. package/dist/src/commands/{router/plugin/commands/test.d.ts → grpc-service/index.d.ts} +1 -1
  5. package/dist/src/commands/grpc-service/index.js +9 -0
  6. package/dist/src/commands/grpc-service/index.js.map +1 -0
  7. package/dist/src/commands/index.js +4 -0
  8. package/dist/src/commands/index.js.map +1 -1
  9. package/dist/src/commands/router/commands/compose.js +315 -241
  10. package/dist/src/commands/router/commands/compose.js.map +1 -1
  11. package/dist/src/commands/router/commands/plugin/commands/build.d.ts +4 -0
  12. package/dist/src/commands/router/commands/plugin/commands/build.js.map +1 -0
  13. package/dist/src/commands/router/{plugin → commands/plugin}/commands/init.d.ts +1 -1
  14. package/dist/src/commands/router/commands/plugin/commands/init.js.map +1 -0
  15. package/dist/src/commands/router/commands/plugin/commands/test.d.ts +4 -0
  16. package/dist/src/commands/router/commands/plugin/commands/test.js.map +1 -0
  17. package/dist/src/commands/router/{plugin → commands/plugin}/helper.js +4 -1
  18. package/dist/src/commands/router/commands/plugin/helper.js.map +1 -0
  19. package/dist/src/commands/router/commands/plugin/index.js.map +1 -0
  20. package/dist/src/commands/router/{plugin → commands/plugin}/templates/plugin.js +4 -4
  21. package/dist/src/commands/router/commands/plugin/templates/plugin.js.map +1 -0
  22. package/dist/src/commands/router/{plugin → commands/plugin}/templates/project.js +6 -6
  23. package/dist/src/commands/router/commands/plugin/templates/project.js.map +1 -0
  24. package/dist/src/commands/router/{plugin → commands/plugin}/toolchain.js +1 -1
  25. package/dist/src/commands/router/commands/plugin/toolchain.js.map +1 -0
  26. package/dist/src/commands/router/index.js +1 -1
  27. package/dist/src/commands/router/index.js.map +1 -1
  28. package/dist/src/core/client/client.d.ts +1 -1
  29. package/dist/tsconfig.tsbuildinfo +1 -1
  30. package/package.json +4 -4
  31. package/dist/src/commands/router/plugin/commands/build.js.map +0 -1
  32. package/dist/src/commands/router/plugin/commands/init.js.map +0 -1
  33. package/dist/src/commands/router/plugin/commands/test.js.map +0 -1
  34. package/dist/src/commands/router/plugin/helper.js.map +0 -1
  35. package/dist/src/commands/router/plugin/index.js.map +0 -1
  36. package/dist/src/commands/router/plugin/templates/plugin.js.map +0 -1
  37. package/dist/src/commands/router/plugin/templates/project.js.map +0 -1
  38. package/dist/src/commands/router/plugin/toolchain.js.map +0 -1
  39. /package/dist/src/commands/{router/plugin/index.d.ts → grpc-service/commands/generate.d.ts} +0 -0
  40. /package/dist/src/commands/router/{plugin → commands/plugin}/commands/build.js +0 -0
  41. /package/dist/src/commands/router/{plugin → commands/plugin}/commands/init.js +0 -0
  42. /package/dist/src/commands/router/{plugin → commands/plugin}/commands/test.js +0 -0
  43. /package/dist/src/commands/router/{plugin → commands/plugin}/helper.d.ts +0 -0
  44. /package/dist/src/commands/router/{plugin/commands/build.d.ts → commands/plugin/index.d.ts} +0 -0
  45. /package/dist/src/commands/router/{plugin → commands/plugin}/index.js +0 -0
  46. /package/dist/src/commands/router/{plugin → commands/plugin}/templates/plugin.d.ts +0 -0
  47. /package/dist/src/commands/router/{plugin → commands/plugin}/templates/project.d.ts +0 -0
  48. /package/dist/src/commands/router/{plugin → commands/plugin}/toolchain.d.ts +0 -0
@@ -13,6 +13,53 @@ import { FeatureFlagRouterExecutionConfig, FeatureFlagRouterExecutionConfigs, GR
13
13
  import Table from 'cli-table3';
14
14
  import { ROUTER_COMPATIBILITY_VERSION_ONE } from '@wundergraph/composition';
15
15
  import { composeSubgraphs, introspectSubgraph } from '../../../utils.js';
16
+ function constructRouterSubgraph(result, s, index) {
17
+ const subgraphConfig = result.subgraphConfigBySubgraphName.get(s.name);
18
+ const schema = subgraphConfig === null || subgraphConfig === void 0 ? void 0 : subgraphConfig.schema;
19
+ const configurationDataByTypeName = subgraphConfig === null || subgraphConfig === void 0 ? void 0 : subgraphConfig.configurationDataByTypeName;
20
+ if (s.kind === SubgraphKind.Standard) {
21
+ const composedSubgraph = {
22
+ kind: SubgraphKind.Standard,
23
+ id: `${index}`,
24
+ name: s.name,
25
+ url: s.routingUrl,
26
+ sdl: s.sdl,
27
+ subscriptionUrl: s.subscriptionUrl,
28
+ subscriptionProtocol: s.subscriptionProtocol,
29
+ websocketSubprotocol: s.websocketSubprotocol,
30
+ schema,
31
+ configurationDataByTypeName,
32
+ };
33
+ return composedSubgraph;
34
+ }
35
+ if (s.kind === SubgraphKind.Plugin) {
36
+ const composedSubgraphPlugin = {
37
+ kind: SubgraphKind.Plugin,
38
+ id: `${index}`,
39
+ name: s.name,
40
+ url: `http://localhost:3000/plugin/${index}`,
41
+ sdl: s.sdl,
42
+ mapping: s.mapping,
43
+ protoSchema: s.protoSchema,
44
+ version: s.version,
45
+ schema,
46
+ configurationDataByTypeName,
47
+ };
48
+ return composedSubgraphPlugin;
49
+ }
50
+ const composedSubgraphGRPC = {
51
+ kind: SubgraphKind.GRPC,
52
+ id: `${index}`,
53
+ name: s.name,
54
+ sdl: s.sdl,
55
+ url: s.routingUrl,
56
+ protoSchema: s.protoSchema,
57
+ mapping: s.mapping,
58
+ schema,
59
+ configurationDataByTypeName,
60
+ };
61
+ return composedSubgraphGRPC;
62
+ }
16
63
  export default (opts) => {
17
64
  const command = new Command('compose');
18
65
  command.description('Generates a router config from a local composition file. This makes it easy to test your router without a control-plane connection. For production, please use the "router fetch" command');
@@ -20,7 +67,6 @@ export default (opts) => {
20
67
  command.option('-o, --out [string]', 'Destination file for the router config.');
21
68
  command.option('--suppress-warnings', 'This flag suppresses any warnings produced by composition.');
22
69
  command.action(async (options) => {
23
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
24
70
  const inputFile = resolve(options.input);
25
71
  const inputFileLocation = dirname(inputFile);
26
72
  if (!existsSync(inputFile)) {
@@ -29,89 +75,9 @@ export default (opts) => {
29
75
  const fileContent = (await readFile(inputFile)).toString();
30
76
  const config = yaml.load(fileContent);
31
77
  const subgraphs = [];
32
- for (const [index, s] of config.subgraphs.entries()) {
33
- // This is a subgraph plugin
34
- if ('plugin' in s) {
35
- if (!s.plugin.path) {
36
- program.error(pc.red(pc.bold(`The plugin path is missing in the input file. Please check the path and try again.`)));
37
- }
38
- if (!existsSync(s.plugin.path)) {
39
- program.error(pc.red(pc.bold(`The plugin path '${pc.bold(s.plugin.path)}' does not exist. Please check the path and try again.`)));
40
- }
41
- if (!s.plugin.version) {
42
- program.error(pc.red(pc.bold(`The plugin version is missing in the input file. Please check the version and try again.`)));
43
- }
44
- // Check if valid semver
45
- if (!semver.valid(s.plugin.version)) {
46
- program.error(pc.red(pc.bold(`The plugin version '${pc.bold(s.plugin.version)}' is not a valid semver. Please check the version and try again.`)));
47
- }
48
- const pluginName = basename(s.plugin.path);
49
- // Check if a plugin with the same name already exists
50
- if (subgraphs.some((sg) => sg.kind === SubgraphKind.Plugin && sg.name === pluginName)) {
51
- program.error(pc.red(pc.bold(`A plugin with the name '${pc.bold(pluginName)}' is already registered. Plugin names must be unique.`)));
52
- }
53
- const mappingFilePath = resolve(s.plugin.path, 'generated', 'mapping.json');
54
- const mappingFile = await readFile(mappingFilePath, 'utf8');
55
- const schemaFilePath = resolve(s.plugin.path, 'src', 'schema.graphql');
56
- const sdl = await readFile(schemaFilePath, 'utf8');
57
- const protoSchemaFilePath = resolve(s.plugin.path, 'generated', 'service.proto');
58
- const protoSchema = await readFile(protoSchemaFilePath, 'utf8');
59
- subgraphs.push({
60
- kind: SubgraphKind.Plugin,
61
- name: pluginName,
62
- protoSchema,
63
- version: s.plugin.version,
64
- sdl,
65
- mapping: GRPCMapping.fromJsonString(mappingFile),
66
- });
67
- }
68
- else {
69
- // The subgraph name is required
70
- if (!s.name) {
71
- program.error(pc.red(pc.bold(`The subgraph name is required for subgraph at index ${index}. Please provide a name and try again.`)));
72
- }
73
- const url = (_b = (_a = s.introspection) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : s.routing_url;
74
- // Check if a subgraph with the same name already exists
75
- if (subgraphs.some((sg) => sg.name === s.name)) {
76
- program.error(pc.red(pc.bold(`A subgraph with the name '${pc.bold(s.name)}' is already registered. Subgraph names must be unique.`)));
77
- }
78
- let schemaSDL = '';
79
- // The GraphQL schema is provided in the input file
80
- if ((_c = s.schema) === null || _c === void 0 ? void 0 : _c.file) {
81
- const schemaFile = resolve(inputFileLocation, s.schema.file);
82
- const sdl = (await readFile(schemaFile)).toString();
83
- schemaSDL = sdl;
84
- }
85
- else {
86
- // The GraphQL schema is not provided in the input file, so we need to introspect it
87
- try {
88
- const result = await introspectSubgraph({
89
- subgraphURL: url,
90
- additionalHeaders: Object.entries((_e = (_d = s.introspection) === null || _d === void 0 ? void 0 : _d.headers) !== null && _e !== void 0 ? _e : {}).map(([key, value]) => ({
91
- key,
92
- value,
93
- })),
94
- rawIntrospection: (_f = s.introspection) === null || _f === void 0 ? void 0 : _f.raw,
95
- });
96
- if (!result.success) {
97
- program.error(`Could not introspect subgraph ${s.name}, URL: ${url}: ${(_g = result.errorMessage) !== null && _g !== void 0 ? _g : 'failed'}`);
98
- }
99
- schemaSDL = result.sdl;
100
- }
101
- catch (e) {
102
- program.error(`Could not introspect subgraph ${s.name}, URL: ${url}: ${e.message}`);
103
- }
104
- }
105
- subgraphs.push({
106
- kind: SubgraphKind.Standard,
107
- name: s.name,
108
- sdl: schemaSDL,
109
- subscriptionUrl: ((_h = s.subscription) === null || _h === void 0 ? void 0 : _h.url) || s.routing_url,
110
- subscriptionProtocol: ((_j = s.subscription) === null || _j === void 0 ? void 0 : _j.protocol) || 'ws',
111
- websocketSubprotocol: ((_k = s.subscription) === null || _k === void 0 ? void 0 : _k.protocol) === 'ws' ? (_l = s.subscription) === null || _l === void 0 ? void 0 : _l.websocketSubprotocol : 'auto',
112
- routingUrl: normalizeURL(s.routing_url),
113
- });
114
- }
78
+ for (const [index, subgraphConfig] of config.subgraphs.entries()) {
79
+ const metadata = await toSubgraphMetadata(inputFileLocation, index, subgraphConfig, subgraphs);
80
+ subgraphs.push(metadata);
115
81
  }
116
82
  const result = composeSubgraphs(subgraphs.map((s, index) => {
117
83
  var _a;
@@ -162,166 +128,11 @@ export default (opts) => {
162
128
  // @TODO get router compatibility version programmatically
163
129
  routerCompatibilityVersion: ROUTER_COMPATIBILITY_VERSION_ONE,
164
130
  schemaVersionId: 'static',
165
- subgraphs: subgraphs.map((s, index) => {
166
- const subgraphConfig = result.subgraphConfigBySubgraphName.get(s.name);
167
- const schema = subgraphConfig === null || subgraphConfig === void 0 ? void 0 : subgraphConfig.schema;
168
- const configurationDataByTypeName = subgraphConfig === null || subgraphConfig === void 0 ? void 0 : subgraphConfig.configurationDataByTypeName;
169
- if (s.kind === SubgraphKind.Standard) {
170
- const composedSubgraph = {
171
- kind: SubgraphKind.Standard,
172
- id: `${index}`,
173
- name: s.name,
174
- url: s.routingUrl,
175
- sdl: s.sdl,
176
- subscriptionUrl: s.subscriptionUrl,
177
- subscriptionProtocol: s.subscriptionProtocol,
178
- websocketSubprotocol: s.websocketSubprotocol,
179
- schema,
180
- configurationDataByTypeName,
181
- };
182
- return composedSubgraph;
183
- }
184
- const composedSubgraphPlugin = {
185
- kind: SubgraphKind.Plugin,
186
- id: `${index}`,
187
- name: s.name,
188
- url: `http://localhost:3000/plugin/${index}`,
189
- sdl: s.sdl,
190
- mapping: s.mapping,
191
- protoSchema: s.protoSchema,
192
- version: s.version,
193
- schema,
194
- configurationDataByTypeName,
195
- };
196
- return composedSubgraphPlugin;
197
- }),
131
+ subgraphs: subgraphs.map((s, index) => constructRouterSubgraph(result, s, index)),
198
132
  });
199
133
  routerConfig.version = randomUUID();
200
134
  if (config.feature_flags && config.feature_flags.length > 0) {
201
- const ffConfigs = new FeatureFlagRouterExecutionConfigs();
202
- // @TODO This logic should exist only once in the shared package and reused across
203
- // control-plane and cli
204
- for (const ff of config.feature_flags) {
205
- const featureSubgraphs = [];
206
- const standardSubgraphs = config.subgraphs.filter((ss) => !('plugin' in ss));
207
- // Process each subgraph for this feature flag
208
- for (const s of standardSubgraphs) {
209
- // Check if this subgraph is overridden by a feature graph
210
- const featureSubgraph = ff.feature_graphs.find((ffs) => ffs.subgraph_name === s.name);
211
- if (featureSubgraph) {
212
- // This subgraph is overridden by a feature graph
213
- const url = (_o = (_m = featureSubgraph.introspection) === null || _m === void 0 ? void 0 : _m.url) !== null && _o !== void 0 ? _o : featureSubgraph.routing_url;
214
- let schemaSDL = '';
215
- if ((_p = featureSubgraph.schema) === null || _p === void 0 ? void 0 : _p.file) {
216
- const schemaFile = resolve(inputFileLocation, featureSubgraph.schema.file);
217
- schemaSDL = await readFile(schemaFile, 'utf8');
218
- }
219
- else {
220
- try {
221
- const result = await introspectSubgraph({
222
- subgraphURL: url,
223
- additionalHeaders: Object.entries((_r = (_q = featureSubgraph.introspection) === null || _q === void 0 ? void 0 : _q.headers) !== null && _r !== void 0 ? _r : {}).map(([key, value]) => ({
224
- key,
225
- value,
226
- })),
227
- rawIntrospection: (_s = featureSubgraph.introspection) === null || _s === void 0 ? void 0 : _s.raw,
228
- });
229
- if (!result.success) {
230
- program.error(`Could not introspect feature-graph subgraph ${featureSubgraph.name}, URL: ${url}: ${(_t = result.errorMessage) !== null && _t !== void 0 ? _t : 'failed'}`);
231
- }
232
- schemaSDL = result.sdl;
233
- }
234
- catch (e) {
235
- program.error(`Could not introspect feature-graph subgraph ${featureSubgraph.name}, URL: ${url}: ${e.message}`);
236
- }
237
- }
238
- featureSubgraphs.push({
239
- kind: SubgraphKind.Standard,
240
- name: featureSubgraph.name,
241
- sdl: schemaSDL,
242
- routingUrl: featureSubgraph.routing_url,
243
- subscriptionUrl: ((_u = featureSubgraph.subscription) === null || _u === void 0 ? void 0 : _u.url) || featureSubgraph.routing_url,
244
- subscriptionProtocol: ((_v = featureSubgraph.subscription) === null || _v === void 0 ? void 0 : _v.protocol) || 'ws',
245
- websocketSubprotocol: ((_w = featureSubgraph.subscription) === null || _w === void 0 ? void 0 : _w.protocol) === 'ws'
246
- ? ((_x = featureSubgraph.subscription) === null || _x === void 0 ? void 0 : _x.websocketSubprotocol) || 'auto'
247
- : 'auto',
248
- });
249
- }
250
- else {
251
- // Use the base subgraph as is
252
- // Find the corresponding metadata in the original subgraphs array
253
- const originalSubgraph = subgraphs.find((sub) => sub.kind === SubgraphKind.Standard && sub.name === s.name);
254
- if (originalSubgraph) {
255
- featureSubgraphs.push(originalSubgraph);
256
- }
257
- }
258
- }
259
- const featureResult = composeSubgraphs(featureSubgraphs.map((s) => ({
260
- name: s.name,
261
- url: normalizeURL(s.routingUrl),
262
- definitions: parse(s.sdl),
263
- })));
264
- if (!featureResult.success) {
265
- const compositionErrorsTable = new Table({
266
- head: [pc.bold(pc.white('ERROR_MESSAGE'))],
267
- colWidths: [120],
268
- wordWrap: true,
269
- });
270
- console.log(pc.red(`We found composition errors, while composing the feature flag ${pc.italic(ff.name)}.\n${pc.bold('Please check the errors below:')}`));
271
- for (const compositionError of featureResult.errors) {
272
- compositionErrorsTable.push([compositionError.message]);
273
- }
274
- console.log(compositionErrorsTable.toString());
275
- continue;
276
- }
277
- if (!options.suppressWarnings && featureResult.warnings.length > 0) {
278
- const compositionWarningsTable = new Table({
279
- head: [pc.bold(pc.white('WARNING_MESSAGE'))],
280
- colWidths: [120],
281
- wordWrap: true,
282
- });
283
- console.log(pc.yellow(`The following warnings were produced while composing the feature flag ${pc.italic(ff.name)}:`));
284
- for (const warning of featureResult.warnings) {
285
- compositionWarningsTable.push([warning.message]);
286
- }
287
- console.log(compositionWarningsTable.toString());
288
- }
289
- const featureFederatedClientSDL = featureResult.shouldIncludeClientSchema
290
- ? printSchema(featureResult.federatedGraphClientSchema)
291
- : '';
292
- const featureRouterConfig = buildRouterConfig({
293
- federatedClientSDL: featureFederatedClientSDL,
294
- federatedSDL: printSchemaWithDirectives(featureResult.federatedGraphSchema),
295
- fieldConfigurations: featureResult.fieldConfigurations,
296
- // @TODO get router compatibility version programmatically
297
- routerCompatibilityVersion: ROUTER_COMPATIBILITY_VERSION_ONE,
298
- schemaVersionId: `static`,
299
- subgraphs: featureSubgraphs.map((s, index) => {
300
- const subgraphConfig = featureResult.subgraphConfigBySubgraphName.get(s.name);
301
- const schema = subgraphConfig === null || subgraphConfig === void 0 ? void 0 : subgraphConfig.schema;
302
- const configurationDataByTypeName = subgraphConfig === null || subgraphConfig === void 0 ? void 0 : subgraphConfig.configurationDataByTypeName;
303
- const composedSubgraph = {
304
- kind: SubgraphKind.Standard,
305
- id: `${index}`,
306
- name: s.name,
307
- url: s.routingUrl,
308
- sdl: s.sdl,
309
- subscriptionUrl: s.subscriptionUrl,
310
- subscriptionProtocol: s.subscriptionProtocol,
311
- websocketSubprotocol: s.websocketSubprotocol,
312
- schema,
313
- configurationDataByTypeName,
314
- };
315
- return composedSubgraph;
316
- }),
317
- });
318
- ffConfigs.configByFeatureFlagName[ff.name] = new FeatureFlagRouterExecutionConfig({
319
- version: featureRouterConfig.version,
320
- subgraphs: featureRouterConfig.subgraphs,
321
- engineConfig: featureRouterConfig.engineConfig,
322
- });
323
- ffConfigs.configByFeatureFlagName[ff.name].version = randomUUID();
324
- }
135
+ const ffConfigs = await buildFeatureFlagsConfig(config, inputFileLocation, subgraphs, options);
325
136
  routerConfig.featureFlagConfigs = ffConfigs;
326
137
  }
327
138
  if (options.out) {
@@ -334,4 +145,267 @@ export default (opts) => {
334
145
  });
335
146
  return command;
336
147
  };
148
+ function toSubgraphMetadata(inputFileLocation, index, subgraphConfig, subgraphs) {
149
+ if ('plugin' in subgraphConfig) {
150
+ return toSubgraphMetadataPlugin(subgraphConfig, subgraphs);
151
+ }
152
+ if ('grpc' in subgraphConfig) {
153
+ return toSubgraphMetadataGRPC(subgraphConfig);
154
+ }
155
+ return toSubgraphMetadataStandard(inputFileLocation, index, subgraphConfig, subgraphs);
156
+ }
157
+ async function toSubgraphMetadataGRPC(s) {
158
+ validateGRPCSubgraph(s);
159
+ const mappingFileContent = await readFile(s.grpc.mapping_file, 'utf8');
160
+ const mapping = GRPCMapping.fromJsonString(mappingFileContent);
161
+ const protoSchemaFileContent = await readFile(s.grpc.proto_file, 'utf8');
162
+ const sdl = await readFile(s.grpc.schema_file, 'utf8');
163
+ return {
164
+ kind: SubgraphKind.GRPC,
165
+ name: s.name,
166
+ sdl,
167
+ routingUrl: s.routing_url,
168
+ protoSchema: protoSchemaFileContent,
169
+ mapping,
170
+ };
171
+ }
172
+ async function toSubgraphMetadataPlugin(s, subgraphs) {
173
+ const pluginName = basename(s.plugin.path);
174
+ if (subgraphs.some((sg) => sg.kind === SubgraphKind.Plugin && sg.name === pluginName)) {
175
+ program.error(pc.red(pc.bold(`A plugin with the name '${pc.bold(pluginName)}' is already registered. Plugin names must be unique.`)));
176
+ }
177
+ validateSubgraphPlugin(s);
178
+ // Check if a plugin with the same name already exists
179
+ const mappingFilePath = resolve(s.plugin.path, 'generated', 'mapping.json');
180
+ const mappingFile = await readFile(mappingFilePath, 'utf8');
181
+ const schemaFilePath = resolve(s.plugin.path, 'src', 'schema.graphql');
182
+ const sdl = await readFile(schemaFilePath, 'utf8');
183
+ const protoSchemaFilePath = resolve(s.plugin.path, 'generated', 'service.proto');
184
+ const protoSchema = await readFile(protoSchemaFilePath, 'utf8');
185
+ return {
186
+ kind: SubgraphKind.Plugin,
187
+ name: pluginName,
188
+ protoSchema,
189
+ version: s.plugin.version,
190
+ sdl,
191
+ mapping: GRPCMapping.fromJsonString(mappingFile),
192
+ };
193
+ }
194
+ async function toSubgraphMetadataStandard(inputFileLocation, index, s, subgraphs) {
195
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
196
+ // The subgraph name is required
197
+ if (!s.name) {
198
+ program.error(pc.red(pc.bold(`The subgraph name is required for subgraph at index ${index}. Please provide a name and try again.`)));
199
+ }
200
+ const url = (_b = (_a = s.introspection) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : s.routing_url;
201
+ // Check if a subgraph with the same name already exists
202
+ if (subgraphs.some((sg) => sg.name === s.name)) {
203
+ program.error(pc.red(pc.bold(`A subgraph with the name '${pc.bold(s.name)}' is already registered. Subgraph names must be unique.`)));
204
+ }
205
+ let schemaSDL = '';
206
+ // The GraphQL schema is provided in the input file
207
+ if ((_c = s.schema) === null || _c === void 0 ? void 0 : _c.file) {
208
+ const schemaFile = resolve(inputFileLocation, s.schema.file);
209
+ const sdl = (await readFile(schemaFile)).toString();
210
+ schemaSDL = sdl;
211
+ }
212
+ else {
213
+ // The GraphQL schema is not provided in the input file, so we need to introspect it
214
+ try {
215
+ const result = await introspectSubgraph({
216
+ subgraphURL: url,
217
+ additionalHeaders: Object.entries((_e = (_d = s.introspection) === null || _d === void 0 ? void 0 : _d.headers) !== null && _e !== void 0 ? _e : {}).map(([key, value]) => ({
218
+ key,
219
+ value,
220
+ })),
221
+ rawIntrospection: (_f = s.introspection) === null || _f === void 0 ? void 0 : _f.raw,
222
+ });
223
+ if (!result.success) {
224
+ program.error(`Could not introspect subgraph ${s.name}, URL: ${url}: ${(_g = result.errorMessage) !== null && _g !== void 0 ? _g : 'failed'}`);
225
+ }
226
+ schemaSDL = result.sdl;
227
+ }
228
+ catch (e) {
229
+ program.error(`Could not introspect subgraph ${s.name}, URL: ${url}: ${e.message}`);
230
+ }
231
+ }
232
+ return {
233
+ kind: SubgraphKind.Standard,
234
+ name: s.name,
235
+ sdl: schemaSDL,
236
+ subscriptionUrl: ((_h = s.subscription) === null || _h === void 0 ? void 0 : _h.url) || s.routing_url,
237
+ subscriptionProtocol: ((_j = s.subscription) === null || _j === void 0 ? void 0 : _j.protocol) || 'ws',
238
+ websocketSubprotocol: ((_k = s.subscription) === null || _k === void 0 ? void 0 : _k.protocol) === 'ws' ? ((_m = (_l = s.subscription) === null || _l === void 0 ? void 0 : _l.websocketSubprotocol) !== null && _m !== void 0 ? _m : 'auto') : 'auto',
239
+ routingUrl: normalizeURL(s.routing_url),
240
+ };
241
+ }
242
+ function validateGRPCSubgraph(s) {
243
+ if (!s.name) {
244
+ program.error(pc.red(pc.bold(`The subgraph name is missing in the input file. Please check the name and try again.`)));
245
+ }
246
+ if (!s.routing_url) {
247
+ program.error(pc.red(pc.bold(`The routing URL is missing in the input file. Please check the routing URL and try again.`)));
248
+ }
249
+ if (!s.grpc.schema_file) {
250
+ program.error(pc.red(pc.bold(`The schema file is missing in the input file. Please check the schema file and try again.`)));
251
+ }
252
+ if (!s.grpc.proto_file) {
253
+ program.error(pc.red(pc.bold(`The proto file is missing in the input file. Please check the proto file and try again.`)));
254
+ }
255
+ if (!s.grpc.mapping_file) {
256
+ program.error(pc.red(pc.bold(`The mapping file is missing in the input file. Please check the mapping file and try again.`)));
257
+ }
258
+ if (!existsSync(s.grpc.schema_file)) {
259
+ program.error(pc.red(pc.bold(`The schema file '${pc.bold(s.grpc.schema_file)}' does not exist. Please check the path and try again.`)));
260
+ }
261
+ if (!existsSync(s.grpc.proto_file)) {
262
+ program.error(pc.red(pc.bold(`The proto file '${pc.bold(s.grpc.proto_file)}' does not exist. Please check the path and try again.`)));
263
+ }
264
+ if (!existsSync(s.grpc.mapping_file)) {
265
+ program.error(pc.red(pc.bold(`The mapping file '${pc.bold(s.grpc.mapping_file)}' does not exist. Please check the path and try again.`)));
266
+ }
267
+ }
268
+ function validateSubgraphPlugin(s) {
269
+ if (!s.plugin.path) {
270
+ program.error(pc.red(pc.bold(`The plugin path is missing in the input file. Please check the path and try again.`)));
271
+ }
272
+ if (!existsSync(s.plugin.path)) {
273
+ program.error(pc.red(pc.bold(`The plugin path '${pc.bold(s.plugin.path)}' does not exist. Please check the path and try again.`)));
274
+ }
275
+ if (!s.plugin.version) {
276
+ program.error(pc.red(pc.bold(`The plugin version is missing in the input file. Please check the version and try again.`)));
277
+ }
278
+ // Check if valid semver
279
+ if (!semver.valid(s.plugin.version)) {
280
+ program.error(pc.red(pc.bold(`The plugin version '${pc.bold(s.plugin.version)}' is not a valid semver. Please check the version and try again.`)));
281
+ }
282
+ }
283
+ async function buildFeatureFlagsConfig(config, inputFileLocation, subgraphs, options) {
284
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
285
+ const ffConfigs = new FeatureFlagRouterExecutionConfigs();
286
+ // @TODO This logic should exist only once in the shared package and reused across
287
+ // control-plane and cli
288
+ for (const ff of config.feature_flags) {
289
+ const featureSubgraphs = [];
290
+ const standardSubgraphs = config.subgraphs.filter((ss) => !('plugin' in ss) && !('grpc' in ss));
291
+ // Process each subgraph for this feature flag
292
+ for (const s of standardSubgraphs) {
293
+ // Check if this subgraph is overridden by a feature graph
294
+ const featureSubgraph = ff.feature_graphs.find((ffs) => ffs.subgraph_name === s.name);
295
+ if (featureSubgraph) {
296
+ // This subgraph is overridden by a feature graph
297
+ const url = (_b = (_a = featureSubgraph.introspection) === null || _a === void 0 ? void 0 : _a.url) !== null && _b !== void 0 ? _b : featureSubgraph.routing_url;
298
+ let schemaSDL = '';
299
+ if ((_c = featureSubgraph.schema) === null || _c === void 0 ? void 0 : _c.file) {
300
+ const schemaFile = resolve(inputFileLocation, featureSubgraph.schema.file);
301
+ schemaSDL = await readFile(schemaFile, 'utf8');
302
+ }
303
+ else {
304
+ try {
305
+ const result = await introspectSubgraph({
306
+ subgraphURL: url,
307
+ additionalHeaders: Object.entries((_e = (_d = featureSubgraph.introspection) === null || _d === void 0 ? void 0 : _d.headers) !== null && _e !== void 0 ? _e : {}).map(([key, value]) => ({
308
+ key,
309
+ value,
310
+ })),
311
+ rawIntrospection: (_f = featureSubgraph.introspection) === null || _f === void 0 ? void 0 : _f.raw,
312
+ });
313
+ if (!result.success) {
314
+ program.error(`Could not introspect feature-graph subgraph ${featureSubgraph.name}, URL: ${url}: ${(_g = result.errorMessage) !== null && _g !== void 0 ? _g : 'failed'}`);
315
+ }
316
+ schemaSDL = result.sdl;
317
+ }
318
+ catch (e) {
319
+ program.error(`Could not introspect feature-graph subgraph ${featureSubgraph.name}, URL: ${url}: ${e.message}`);
320
+ }
321
+ }
322
+ featureSubgraphs.push({
323
+ kind: SubgraphKind.Standard,
324
+ name: featureSubgraph.name,
325
+ sdl: schemaSDL,
326
+ routingUrl: featureSubgraph.routing_url,
327
+ subscriptionUrl: ((_h = featureSubgraph.subscription) === null || _h === void 0 ? void 0 : _h.url) || featureSubgraph.routing_url,
328
+ subscriptionProtocol: ((_j = featureSubgraph.subscription) === null || _j === void 0 ? void 0 : _j.protocol) || 'ws',
329
+ websocketSubprotocol: ((_k = featureSubgraph.subscription) === null || _k === void 0 ? void 0 : _k.protocol) === 'ws'
330
+ ? ((_l = featureSubgraph.subscription) === null || _l === void 0 ? void 0 : _l.websocketSubprotocol) || 'auto'
331
+ : 'auto',
332
+ });
333
+ }
334
+ else {
335
+ // Use the base subgraph as is
336
+ // Find the corresponding metadata in the original subgraphs array
337
+ const originalSubgraph = subgraphs.find((sub) => sub.kind === SubgraphKind.Standard && sub.name === s.name);
338
+ if (originalSubgraph) {
339
+ featureSubgraphs.push(originalSubgraph);
340
+ }
341
+ }
342
+ }
343
+ const featureResult = composeSubgraphs(featureSubgraphs.map((s) => ({
344
+ name: s.name,
345
+ url: normalizeURL(s.routingUrl),
346
+ definitions: parse(s.sdl),
347
+ })));
348
+ if (!featureResult.success) {
349
+ const compositionErrorsTable = new Table({
350
+ head: [pc.bold(pc.white('ERROR_MESSAGE'))],
351
+ colWidths: [120],
352
+ wordWrap: true,
353
+ });
354
+ console.log(pc.red(`We found composition errors, while composing the feature flag ${pc.italic(ff.name)}.\n${pc.bold('Please check the errors below:')}`));
355
+ for (const compositionError of featureResult.errors) {
356
+ compositionErrorsTable.push([compositionError.message]);
357
+ }
358
+ console.log(compositionErrorsTable.toString());
359
+ continue;
360
+ }
361
+ if (!options.suppressWarnings && featureResult.warnings.length > 0) {
362
+ const compositionWarningsTable = new Table({
363
+ head: [pc.bold(pc.white('WARNING_MESSAGE'))],
364
+ colWidths: [120],
365
+ wordWrap: true,
366
+ });
367
+ console.log(pc.yellow(`The following warnings were produced while composing the feature flag ${pc.italic(ff.name)}:`));
368
+ for (const warning of featureResult.warnings) {
369
+ compositionWarningsTable.push([warning.message]);
370
+ }
371
+ console.log(compositionWarningsTable.toString());
372
+ }
373
+ const featureFederatedClientSDL = featureResult.shouldIncludeClientSchema
374
+ ? printSchema(featureResult.federatedGraphClientSchema)
375
+ : '';
376
+ const featureRouterConfig = buildRouterConfig({
377
+ federatedClientSDL: featureFederatedClientSDL,
378
+ federatedSDL: printSchemaWithDirectives(featureResult.federatedGraphSchema),
379
+ fieldConfigurations: featureResult.fieldConfigurations,
380
+ // @TODO get router compatibility version programmatically
381
+ routerCompatibilityVersion: ROUTER_COMPATIBILITY_VERSION_ONE,
382
+ schemaVersionId: `static`,
383
+ subgraphs: featureSubgraphs.map((s, index) => {
384
+ const subgraphConfig = featureResult.subgraphConfigBySubgraphName.get(s.name);
385
+ const schema = subgraphConfig === null || subgraphConfig === void 0 ? void 0 : subgraphConfig.schema;
386
+ const configurationDataByTypeName = subgraphConfig === null || subgraphConfig === void 0 ? void 0 : subgraphConfig.configurationDataByTypeName;
387
+ const composedSubgraph = {
388
+ kind: SubgraphKind.Standard,
389
+ id: `${index}`,
390
+ name: s.name,
391
+ url: s.routingUrl,
392
+ sdl: s.sdl,
393
+ subscriptionUrl: s.subscriptionUrl,
394
+ subscriptionProtocol: s.subscriptionProtocol,
395
+ websocketSubprotocol: s.websocketSubprotocol,
396
+ schema,
397
+ configurationDataByTypeName,
398
+ };
399
+ return composedSubgraph;
400
+ }),
401
+ });
402
+ ffConfigs.configByFeatureFlagName[ff.name] = new FeatureFlagRouterExecutionConfig({
403
+ version: featureRouterConfig.version,
404
+ subgraphs: featureRouterConfig.subgraphs,
405
+ engineConfig: featureRouterConfig.engineConfig,
406
+ });
407
+ ffConfigs.configByFeatureFlagName[ff.name].version = randomUUID();
408
+ }
409
+ return ffConfigs;
410
+ }
337
411
  //# sourceMappingURL=compose.js.map