@adobe/aio-cli-plugin-api-mesh 5.2.2-beta.2 → 5.2.3-alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/oclif.manifest.json +1 -1
- package/package.json +7 -3
- package/src/commands/api-mesh/__tests__/create.test.js +18 -6
- package/src/commands/api-mesh/__tests__/run.test.js +191 -65
- package/src/commands/api-mesh/__tests__/update.test.js +4 -2
- package/src/commands/api-mesh/create.js +2 -3
- package/src/commands/api-mesh/run.js +207 -161
- package/src/commands/api-mesh/update.js +2 -3
- package/src/commands/{PLUGINNAME/__tests__/index.test.js → api-mesh.js} +13 -15
- package/src/helpers.js +20 -10
- package/src/lib/devConsole.js +1 -1
- package/src/meshArtifact.js +231 -0
- package/src/project.js +56 -0
- package/src/server.js +74 -32
- package/src/utils.js +10 -2
- package/src/{index.js → worker.js} +9 -7
- package/src/wranglerCli.js +54 -0
- package/src/commands/PLUGINNAME/index.js +0 -32
- package/src/wranglerServer.js +0 -80
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const { fixPlugins } = require('./fixPlugins');
|
|
4
|
+
const logger = require('../src/classes/logger');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Whether file is TypeScript
|
|
8
|
+
* @param filePath Filepath
|
|
9
|
+
*/
|
|
10
|
+
function isTypeScriptFile(filePath) {
|
|
11
|
+
const ext = path.extname(filePath);
|
|
12
|
+
return ext === '.ts' || ext === '.tsx';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Gets
|
|
17
|
+
* @param builtMeshTenantDir
|
|
18
|
+
* @returns {string}
|
|
19
|
+
*/
|
|
20
|
+
function getBuiltMeshEntrypoint(builtMeshTenantDir) {
|
|
21
|
+
let builtMeshIndexPath = path.join(builtMeshTenantDir, 'index');
|
|
22
|
+
return fs.existsSync(`${builtMeshIndexPath}.ts`)
|
|
23
|
+
? `${builtMeshIndexPath}.ts`
|
|
24
|
+
: `${builtMeshIndexPath}.js`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Converts composer string to static imports compatible with bundling
|
|
29
|
+
* @param data {string} Data read from the built mesh
|
|
30
|
+
* @returns {string} Updated data
|
|
31
|
+
* @example Converts composer string
|
|
32
|
+
* ```
|
|
33
|
+
* "beforeAll": {
|
|
34
|
+
* "composer": "/Users/user/project/hooks.js#isAuth",
|
|
35
|
+
* "blocking": true
|
|
36
|
+
* }
|
|
37
|
+
* ```
|
|
38
|
+
* To:
|
|
39
|
+
* ```
|
|
40
|
+
* "beforeAll": {
|
|
41
|
+
* "module": await import("/Users/user/project/hooks.js"), "fn": "isAuth",
|
|
42
|
+
* "blocking": true
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
function resolveComposerAsTypeScriptModule(data) {
|
|
47
|
+
return data.replace(
|
|
48
|
+
/"composer":\s*"([^#]+)#([^"]+)"/,
|
|
49
|
+
`"module": await import("$1"), "fn": "$2"`,
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Converts composer string to static imports compatible with bundling
|
|
55
|
+
* @param data {string} Data read from the built mesh
|
|
56
|
+
* @returns {string} Updated data
|
|
57
|
+
* @example Converts composer string
|
|
58
|
+
* ```
|
|
59
|
+
* "beforeAll": {
|
|
60
|
+
* "composer": "/Users/user/project/hooks.js#isAuth",
|
|
61
|
+
* "blocking": true
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
* To:
|
|
65
|
+
* ```
|
|
66
|
+
* "beforeAll": {
|
|
67
|
+
* "module": __importStar(require("/Users/user/project/hooks.js")), "fn": "isAuth",
|
|
68
|
+
* "blocking": true
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
function resolveComposerAsJavaScriptModule(data) {
|
|
73
|
+
return data.replace(
|
|
74
|
+
/"composer":\s*"([^#]+)#([^"]+)"/,
|
|
75
|
+
`"module": __importStar(require("$1")), "fn": "$2"`,
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Takes a mesh artifact and converts composer configuration to static imports
|
|
81
|
+
* compatible with bundling
|
|
82
|
+
* @param meshArtifactPath Path to the mesh artifact used to determine extension
|
|
83
|
+
* @param data {string} Data read from the built mesh
|
|
84
|
+
* @returns {string} Updated data
|
|
85
|
+
* @see {@link resolveComposerAsJavaScriptModule}
|
|
86
|
+
* @see {@link resolveComposerAsTypeScriptModule}
|
|
87
|
+
*/
|
|
88
|
+
function resolveComposerAsStaticImport(meshArtifactPath, data) {
|
|
89
|
+
return isTypeScriptFile(meshArtifactPath)
|
|
90
|
+
? resolveComposerAsTypeScriptModule(data)
|
|
91
|
+
: resolveComposerAsJavaScriptModule(data);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// /**
|
|
95
|
+
// * Converts handler string to static imports compatible with bundling
|
|
96
|
+
// * @param data {string} Data read from the built mesh
|
|
97
|
+
// * @returns {string} Updated data
|
|
98
|
+
// * @example Converts composer string
|
|
99
|
+
// * ```
|
|
100
|
+
// * "onFetch": {
|
|
101
|
+
// * "handler": "/Users/user/project/fetch.js",
|
|
102
|
+
// * "blocking": true
|
|
103
|
+
// * }
|
|
104
|
+
// * ```
|
|
105
|
+
// * To:
|
|
106
|
+
// * ```
|
|
107
|
+
// * "onFetch": {
|
|
108
|
+
// * "handler": await import("/Users/user/project/fetch.js"),
|
|
109
|
+
// * "blocking": true
|
|
110
|
+
// * }
|
|
111
|
+
// * ```
|
|
112
|
+
// */
|
|
113
|
+
// function resolveHandlerAsTypeScriptModule(data) {
|
|
114
|
+
// return data.replace(/"handler":\s*"([^"]+)"/, `"handler": await import("$1")`);
|
|
115
|
+
// }
|
|
116
|
+
|
|
117
|
+
// /**
|
|
118
|
+
// * Converts handler string to static imports compatible with bundling
|
|
119
|
+
// * @param data {string} Data read from the built mesh
|
|
120
|
+
// * @returns {string} Updated data
|
|
121
|
+
// * @example Converts composer string
|
|
122
|
+
// * ```
|
|
123
|
+
// * "onFetch": {
|
|
124
|
+
// * "handler": "/Users/user/project/fetch.js",
|
|
125
|
+
// * "blocking": true
|
|
126
|
+
// * }
|
|
127
|
+
// * ```
|
|
128
|
+
// * To:
|
|
129
|
+
// * ```
|
|
130
|
+
// * "onFetch": {
|
|
131
|
+
// * "module": __importStar(require("/Users/user/project/fetch.js")),
|
|
132
|
+
// * "blocking": true
|
|
133
|
+
// * }
|
|
134
|
+
// * ```
|
|
135
|
+
// */
|
|
136
|
+
// function resolveHandlerAsJavaScriptModule(data) {
|
|
137
|
+
// return data.replace(/"handler":\s*"([^"]+)"/, `"handler": __importStar(require("$1"))`);
|
|
138
|
+
// }
|
|
139
|
+
|
|
140
|
+
// /**
|
|
141
|
+
// * Takes a mesh artifact and converts handler configuration to static imports
|
|
142
|
+
// * compatible with bundling
|
|
143
|
+
// * @param meshArtifactPath Path to the mesh artifact used to determine extension
|
|
144
|
+
// * @param data {string} Data read from the built mesh
|
|
145
|
+
// * @returns {string} Updated data
|
|
146
|
+
// * @see {@link resolveHandlerAsJavaScriptModule}
|
|
147
|
+
// * @see {@link resolveHandlerAsTypeScriptModule}
|
|
148
|
+
// */
|
|
149
|
+
// TODO: onFetch support - requires plugin changes
|
|
150
|
+
// function resolveHandlerAsStaticImport(meshArtifactPath, data) {
|
|
151
|
+
// return isTypeScriptFile(meshArtifactPath)
|
|
152
|
+
// ? resolveHandlerAsTypeScriptModule(data)
|
|
153
|
+
// : resolveHandlerAsJavaScriptModule(data);
|
|
154
|
+
// }
|
|
155
|
+
|
|
156
|
+
const resolveRelativeSources = async builtMeshTenantDir => {
|
|
157
|
+
let builtMeshPath = getBuiltMeshEntrypoint(builtMeshTenantDir);
|
|
158
|
+
|
|
159
|
+
// Fix http details extensions plugin for edge compatibility
|
|
160
|
+
await fixPlugins(builtMeshPath);
|
|
161
|
+
|
|
162
|
+
// Read tenant files inventory
|
|
163
|
+
const artifactFilesPath = path.join(builtMeshTenantDir, 'files.json');
|
|
164
|
+
if (fs.existsSync(artifactFilesPath)) {
|
|
165
|
+
// Read mesh artifact
|
|
166
|
+
let builtMeshData = fs.readFileSync(builtMeshPath).toString();
|
|
167
|
+
|
|
168
|
+
const parentDirRegex = new RegExp('../tenantFiles'.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
169
|
+
builtMeshData = builtMeshData.replace(parentDirRegex, './tenantFiles');
|
|
170
|
+
|
|
171
|
+
// Write the modified mesh artifact
|
|
172
|
+
fs.writeFileSync(builtMeshPath, builtMeshData, 'utf8');
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Resolve original sources to materialized files in mesh artifact for local development and debugging
|
|
178
|
+
* @param builtMeshTenantDir Built mesh tenant directory
|
|
179
|
+
* @param localFileOverrides Local file overrides
|
|
180
|
+
*/
|
|
181
|
+
const resolveOriginalSources = async (builtMeshTenantDir, localFileOverrides) => {
|
|
182
|
+
let builtMeshPath = getBuiltMeshEntrypoint(builtMeshTenantDir);
|
|
183
|
+
|
|
184
|
+
// Read tenant files inventory
|
|
185
|
+
const artifactFilesPath = path.join(builtMeshTenantDir, 'files.json');
|
|
186
|
+
if (fs.existsSync(artifactFilesPath)) {
|
|
187
|
+
let files = {};
|
|
188
|
+
try {
|
|
189
|
+
files = JSON.parse(fs.readFileSync(artifactFilesPath).toString());
|
|
190
|
+
} catch (err) {
|
|
191
|
+
logger.error('Malformed "files.json" file. Skipping original source resolution.');
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Read mesh artifact
|
|
195
|
+
let builtMeshData = fs.readFileSync(builtMeshPath).toString();
|
|
196
|
+
files.files.forEach(file => {
|
|
197
|
+
// Skip replacement of files for local development when the user was prompted
|
|
198
|
+
// to override and answered no
|
|
199
|
+
if (
|
|
200
|
+
Object.keys(localFileOverrides).includes(file.path) &&
|
|
201
|
+
localFileOverrides[file.path] === false
|
|
202
|
+
) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// When the source exists in project use it instead of the materialized file
|
|
207
|
+
const absoluteFilePath = path.resolve(file.path);
|
|
208
|
+
|
|
209
|
+
// Replace all occurrences of the materialized path with the fully qualified original path when it exists
|
|
210
|
+
if (fs.existsSync(absoluteFilePath)) {
|
|
211
|
+
const regex = new RegExp(file.materializedPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
212
|
+
builtMeshData = builtMeshData.replace(regex, absoluteFilePath);
|
|
213
|
+
builtMeshData = resolveComposerAsStaticImport(builtMeshPath, builtMeshData);
|
|
214
|
+
// TODO: onFetch support - requires plugin changes
|
|
215
|
+
// builtMeshData = resolveHandlerAsStaticImport(builtMeshPath, builtMeshData);
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Write the modified mesh artifact
|
|
220
|
+
fs.writeFileSync(builtMeshPath, builtMeshData, 'utf8');
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
isTypeScriptFile,
|
|
226
|
+
resolveComposerAsTypeScriptModule,
|
|
227
|
+
resolveComposerAsJavaScriptModule,
|
|
228
|
+
resolveComposerAsStaticImport,
|
|
229
|
+
resolveRelativeSources,
|
|
230
|
+
resolveOriginalSources,
|
|
231
|
+
};
|
package/src/project.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const { cpSync, existsSync, renameSync, rmSync } = require('node:fs');
|
|
2
|
+
const { join } = require('node:path');
|
|
3
|
+
const { resolveOriginalSources } = require('./meshArtifact');
|
|
4
|
+
|
|
5
|
+
const BUILT_MESH_ARTIFACT_DIRECTORY = 'mesh-artifact';
|
|
6
|
+
const BUILT_MESH_TENANT_FILES_DIRECTORY = join(BUILT_MESH_ARTIFACT_DIRECTORY, 'tenantFiles');
|
|
7
|
+
const PACKAGED_MESH_DIR = '.mesh';
|
|
8
|
+
const PACKAGED_MESH_DIR_TENANT_FILES_DIRECTORY = join(PACKAGED_MESH_DIR, 'tenantFiles');
|
|
9
|
+
const PACKAGED_MESH_CLI_MIRROR_DIR = `${__dirname}/../${PACKAGED_MESH_DIR}`;
|
|
10
|
+
const TEMP_FILES_DIRECTORY = 'tempfiles';
|
|
11
|
+
const getBuiltMeshTenantDirectory = meshId => join(BUILT_MESH_ARTIFACT_DIRECTORY, meshId);
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Copy built mesh artifact to packaged directory that runs local development.
|
|
15
|
+
* @param builtMeshTenantDir Built mesh directory path
|
|
16
|
+
* @returns {Promise<void>}
|
|
17
|
+
*/
|
|
18
|
+
const copyBuiltMeshToPackage = async builtMeshTenantDir => {
|
|
19
|
+
// Reset packaged directories
|
|
20
|
+
safeDelete(PACKAGED_MESH_DIR);
|
|
21
|
+
safeDelete(PACKAGED_MESH_CLI_MIRROR_DIR);
|
|
22
|
+
|
|
23
|
+
// Copy the built mesh to the packaged directory for in-project SDK
|
|
24
|
+
safeRename(builtMeshTenantDir, PACKAGED_MESH_DIR);
|
|
25
|
+
safeRename(BUILT_MESH_TENANT_FILES_DIRECTORY, PACKAGED_MESH_DIR_TENANT_FILES_DIRECTORY);
|
|
26
|
+
|
|
27
|
+
// Copy the packaged mesh to the CLI mirror directory for local server
|
|
28
|
+
safeCopy(PACKAGED_MESH_DIR, PACKAGED_MESH_CLI_MIRROR_DIR);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const safeDelete = path => {
|
|
32
|
+
if (existsSync(path)) {
|
|
33
|
+
rmSync(path, { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const safeRename = (oldPath, newPath) => {
|
|
38
|
+
if (existsSync(oldPath)) {
|
|
39
|
+
renameSync(oldPath, newPath);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const safeCopy = (source, destination) => {
|
|
44
|
+
if (existsSync(source)) {
|
|
45
|
+
cpSync(source, destination, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
module.exports = {
|
|
50
|
+
BUILT_MESH_ARTIFACT_DIRECTORY,
|
|
51
|
+
safeDelete,
|
|
52
|
+
copyBuiltMeshToPackage,
|
|
53
|
+
resolveOriginalSources,
|
|
54
|
+
TEMP_FILES_DIRECTORY,
|
|
55
|
+
getBuiltMeshTenantDirectory,
|
|
56
|
+
};
|
package/src/server.js
CHANGED
|
@@ -1,38 +1,80 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
});
|
|
1
|
+
import { getMesh } from '@graphql-mesh/runtime';
|
|
2
|
+
|
|
3
|
+
const { getCorsOptions } = require('./cors');
|
|
4
|
+
const { createYoga } = require('graphql-yoga');
|
|
5
|
+
const { GraphQLError } = require('graphql/error');
|
|
6
|
+
|
|
7
|
+
const { loadMeshSecrets, getSecretsHandler } = require('./secrets');
|
|
8
|
+
const useComplianceHeaders = require('./plugins/complianceHeaders');
|
|
9
|
+
const UseHttpDetailsExtensions = require('./plugins/httpDetailsExtensions');
|
|
10
|
+
const useSourceHeaders = require('@adobe/plugin-source-headers');
|
|
11
|
+
const { useDisableIntrospection } = require('@envelop/disable-introspection');
|
|
12
|
+
|
|
13
|
+
let meshInstance$;
|
|
14
|
+
|
|
15
|
+
async function buildMeshInstance(meshArtifacts, meshConfig) {
|
|
16
|
+
const { getMeshOptions } = meshArtifacts;
|
|
17
|
+
const options = await getMeshOptions();
|
|
18
|
+
|
|
19
|
+
options.additionalEnvelopPlugins = (options.additionalEnvelopPlugins || []).concat(
|
|
20
|
+
useComplianceHeaders(),
|
|
21
|
+
UseHttpDetailsExtensions({
|
|
22
|
+
// Get the details of responseConfig.includeHTTPDetails and store in Cache
|
|
23
|
+
if: meshConfig.responseConfig?.includeHTTPDetails || false,
|
|
24
|
+
}),
|
|
25
|
+
useSourceHeaders(meshConfig),
|
|
26
|
+
);
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
if (meshConfig.disableIntrospection) {
|
|
29
|
+
options.additionalEnvelopPlugins.push(useDisableIntrospection());
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return getMesh(options).then(mesh => {
|
|
33
|
+
const id = mesh.pubsub.subscribe('destroy', () => {
|
|
34
|
+
meshInstance$ = undefined;
|
|
35
|
+
mesh.pubsub.unsubscribe(id);
|
|
36
|
+
});
|
|
37
|
+
return mesh;
|
|
31
38
|
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function getBuiltMesh(meshArtifacts, meshConfig) {
|
|
42
|
+
if (meshInstance$ == null) {
|
|
43
|
+
meshInstance$ = buildMeshInstance(meshArtifacts, meshConfig);
|
|
44
|
+
}
|
|
45
|
+
return meshInstance$;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const buildServer = async (loggerInstance, env, meshArtifacts, meshConfig) => {
|
|
49
|
+
const { Secret: secret } = env;
|
|
50
|
+
const tenantMesh = await getBuiltMesh(meshArtifacts, meshConfig);
|
|
51
|
+
const meshSecrets = loadMeshSecrets(loggerInstance, secret);
|
|
52
|
+
return await buildYogaServer(env, tenantMesh, meshConfig, meshSecrets);
|
|
53
|
+
};
|
|
32
54
|
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
async function buildYogaServer(env, tenantMesh, meshConfig, meshSecrets) {
|
|
56
|
+
const secretsProxy = new Proxy(meshSecrets, getSecretsHandler);
|
|
57
|
+
return createYoga({
|
|
58
|
+
plugins: tenantMesh.plugins,
|
|
59
|
+
graphqlEndpoint: `/graphql`,
|
|
60
|
+
cors: getCorsOptions(env, meshConfig),
|
|
61
|
+
context: initialContext => ({
|
|
62
|
+
...initialContext,
|
|
63
|
+
secrets: secretsProxy,
|
|
64
|
+
}),
|
|
65
|
+
maskedErrors: {
|
|
66
|
+
maskError: maskError,
|
|
67
|
+
},
|
|
68
|
+
logging: 'debug',
|
|
35
69
|
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const maskError = error => {
|
|
73
|
+
if (error instanceof GraphQLError && error.extensions?.http?.headers) {
|
|
74
|
+
delete error.extensions.http.headers;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return error;
|
|
36
78
|
};
|
|
37
79
|
|
|
38
|
-
module.exports = {
|
|
80
|
+
module.exports = { buildServer };
|
package/src/utils.js
CHANGED
|
@@ -62,6 +62,13 @@ const secretsFlag = Flags.string({
|
|
|
62
62
|
const portNoFlag = Flags.integer({
|
|
63
63
|
char: 'p',
|
|
64
64
|
description: 'Port number for the local dev server',
|
|
65
|
+
default: 5000,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const inspectPortNoFlag = Flags.integer({
|
|
69
|
+
char: 'i',
|
|
70
|
+
description: 'Port number for the local dev server inspector',
|
|
71
|
+
default: 9229,
|
|
65
72
|
});
|
|
66
73
|
|
|
67
74
|
const debugFlag = Flags.boolean({
|
|
@@ -277,7 +284,7 @@ function checkPlaceholders(mesh) {
|
|
|
277
284
|
* @param {string} file
|
|
278
285
|
* @param {object} command
|
|
279
286
|
* @param {string} filetype
|
|
280
|
-
* @returns {string}
|
|
287
|
+
* @returns {Promise<string>}
|
|
281
288
|
*/
|
|
282
289
|
async function readFileContents(file, command, filetype) {
|
|
283
290
|
try {
|
|
@@ -345,7 +352,7 @@ function validateFileName(filesList) {
|
|
|
345
352
|
* @param {string} inputMeshData
|
|
346
353
|
* @param {string} envFilePath
|
|
347
354
|
* @param {object} command
|
|
348
|
-
* @returns {string}
|
|
355
|
+
* @returns {Promise<string>}
|
|
349
356
|
*/
|
|
350
357
|
async function validateAndInterpolateMesh(inputMeshData, envFilePath, command) {
|
|
351
358
|
//Read the environment file
|
|
@@ -760,6 +767,7 @@ module.exports = {
|
|
|
760
767
|
validateAndInterpolateMesh,
|
|
761
768
|
getAppRootDir,
|
|
762
769
|
portNoFlag,
|
|
770
|
+
inspectPortNoFlag,
|
|
763
771
|
debugFlag,
|
|
764
772
|
selectFlag,
|
|
765
773
|
secretsFlag,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ServedTier, addServedHeader } from './served';
|
|
2
|
+
import { buildServer } from './server';
|
|
2
3
|
import { bindedlogger as logger } from './utils/logger';
|
|
3
4
|
import { getRequestId } from './utils/requestId';
|
|
4
|
-
import { buildServer } from './wranglerServer';
|
|
5
5
|
|
|
6
6
|
let server;
|
|
7
7
|
|
|
@@ -21,10 +21,10 @@ export default {
|
|
|
21
21
|
const { MESH_ID: meshId, LOG_LEVEL: logLevel } = env;
|
|
22
22
|
const loggerInstance = logger({ logLevel, meshId, requestId });
|
|
23
23
|
const meshArtifacts = await import('../.mesh');
|
|
24
|
-
const
|
|
24
|
+
const meshConfig = await import('../.mesh/.meshrc.json');
|
|
25
25
|
|
|
26
26
|
if (!server) {
|
|
27
|
-
server = await this.buildAndCacheServer(env, loggerInstance, meshArtifacts,
|
|
27
|
+
server = await this.buildAndCacheServer(env, loggerInstance, meshArtifacts, meshConfig);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
loggerInstance.debug('WORKER HOT: Fetching via worker');
|
|
@@ -34,11 +34,13 @@ export default {
|
|
|
34
34
|
},
|
|
35
35
|
/**
|
|
36
36
|
* Build and cache mesh instance/server in global variable.
|
|
37
|
-
* @param env
|
|
38
|
-
* @param
|
|
37
|
+
* @param env Environment
|
|
38
|
+
* @param logger Logger
|
|
39
|
+
* @param meshArtifacts Mesh artifact
|
|
40
|
+
* @param meshConfig Mesh config
|
|
39
41
|
*/
|
|
40
|
-
async buildAndCacheServer(env,
|
|
41
|
-
server = await buildServer(
|
|
42
|
+
async buildAndCacheServer(env, logger, meshArtifacts, meshConfig) {
|
|
43
|
+
server = await buildServer(logger, env, meshArtifacts, meshConfig);
|
|
42
44
|
return server;
|
|
43
45
|
},
|
|
44
46
|
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const { spawn } = require('child_process');
|
|
2
|
+
const { readSecretsFile } = require('./serverUtils');
|
|
3
|
+
const packageData = require('../package.json');
|
|
4
|
+
const { join } = require('node:path');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Starts the wrangler dev server
|
|
8
|
+
* @param command {Command} CLI command
|
|
9
|
+
* @param port Port number
|
|
10
|
+
* @param debug Whether debug mode is enabled
|
|
11
|
+
* @param inspectPort Port number for local dev server inspector
|
|
12
|
+
*/
|
|
13
|
+
const start = (command, port, debug, inspectPort) => {
|
|
14
|
+
const wranglerPackageNumber = packageData.dependencies.wrangler;
|
|
15
|
+
const wranglerVersion = `wrangler@${wranglerPackageNumber.replace(/^[\^~]/, '')}`;
|
|
16
|
+
const wranglerToml = join(__dirname, '..', 'wrangler.toml');
|
|
17
|
+
const meshDir = '.mesh';
|
|
18
|
+
const secrets = readSecretsFile(meshDir);
|
|
19
|
+
const entrypoint = join(__dirname, 'worker.js');
|
|
20
|
+
|
|
21
|
+
const commandArgs = [
|
|
22
|
+
wranglerVersion,
|
|
23
|
+
'--config',
|
|
24
|
+
wranglerToml,
|
|
25
|
+
'--cwd',
|
|
26
|
+
process.cwd(),
|
|
27
|
+
'dev',
|
|
28
|
+
entrypoint,
|
|
29
|
+
'--show-interactive-dev-session',
|
|
30
|
+
'false',
|
|
31
|
+
'--var',
|
|
32
|
+
`Secret:${JSON.stringify(secrets)}`,
|
|
33
|
+
'--port',
|
|
34
|
+
port,
|
|
35
|
+
'--inspector-port',
|
|
36
|
+
debug ? inspectPort : 0,
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const wrangler = spawn('npx', commandArgs, {
|
|
40
|
+
stdio: 'inherit',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
wrangler.on('close', code => {
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.log(`wrangler dev process exited with code ${code}`);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
wrangler.on('error', error => {
|
|
49
|
+
// eslint-disable-next-line no-console
|
|
50
|
+
console.error(`Failed to start wrangler dev: ${error.message}`);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
module.exports = { start };
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
Copyright 2020 Adobe. All rights reserved.
|
|
3
|
-
This file is licensed to you under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
you may not use this file except in compliance with the License. You may obtain a copy
|
|
5
|
-
of the License at http://www.apache.org/licenses/LICENSE-2.0
|
|
6
|
-
|
|
7
|
-
Unless required by applicable law or agreed to in writing, software distributed under
|
|
8
|
-
the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
|
|
9
|
-
OF ANY KIND, either express or implied. See the License for the specific language
|
|
10
|
-
governing permissions and limitations under the License.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const { Command, flags } = require('@oclif/command');
|
|
14
|
-
const aioLogger = require('@adobe/aio-lib-core-logging')('PLUGINNAME', { provider: 'debug' });
|
|
15
|
-
|
|
16
|
-
class IndexCommand extends Command {
|
|
17
|
-
async run() {
|
|
18
|
-
// const { args, flags } = this.parse(IndexCommand)
|
|
19
|
-
aioLogger.debug('this is the index command.');
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
IndexCommand.flags = {
|
|
24
|
-
someflag: flags.string({ char: 'f', description: 'this is some flag' }),
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
// this is set in package.json, see https://github.com/oclif/oclif/issues/120
|
|
28
|
-
// if not set it will get the first (alphabetical) topic's help description
|
|
29
|
-
IndexCommand.description = 'Your description here';
|
|
30
|
-
IndexCommand.examples = ['$ aio PLUGINNAME:some_command'];
|
|
31
|
-
|
|
32
|
-
module.exports = IndexCommand;
|
package/src/wranglerServer.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { getMesh } from '@graphql-mesh/runtime';
|
|
2
|
-
|
|
3
|
-
const { getCorsOptions } = require('./cors');
|
|
4
|
-
const { createYoga } = require('graphql-yoga');
|
|
5
|
-
const { GraphQLError } = require('graphql/error');
|
|
6
|
-
|
|
7
|
-
const { loadMeshSecrets, getSecretsHandler } = require('./secrets');
|
|
8
|
-
const useComplianceHeaders = require('./plugins/complianceHeaders');
|
|
9
|
-
const UseHttpDetailsExtensions = require('./plugins/httpDetailsExtensions');
|
|
10
|
-
const useSourceHeaders = require('@adobe/plugin-source-headers');
|
|
11
|
-
const { useDisableIntrospection } = require('@envelop/disable-introspection');
|
|
12
|
-
|
|
13
|
-
let meshInstance$;
|
|
14
|
-
|
|
15
|
-
async function buildMeshInstance(meshArtifacts, meshConfig) {
|
|
16
|
-
const { getMeshOptions } = meshArtifacts;
|
|
17
|
-
const options = await getMeshOptions();
|
|
18
|
-
|
|
19
|
-
options.additionalEnvelopPlugins = (options.additionalEnvelopPlugins || []).concat(
|
|
20
|
-
useComplianceHeaders(),
|
|
21
|
-
UseHttpDetailsExtensions({
|
|
22
|
-
// Get the details of responseConfig.includeHTTPDetails and store in Cache
|
|
23
|
-
if: meshConfig.responseConfig?.includeHTTPDetails || false,
|
|
24
|
-
}),
|
|
25
|
-
useSourceHeaders(meshConfig),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
if (meshConfig.disableIntrospection) {
|
|
29
|
-
options.additionalEnvelopPlugins.push(useDisableIntrospection());
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return getMesh(options).then(mesh => {
|
|
33
|
-
const id = mesh.pubsub.subscribe('destroy', () => {
|
|
34
|
-
meshInstance$ = undefined;
|
|
35
|
-
mesh.pubsub.unsubscribe(id);
|
|
36
|
-
});
|
|
37
|
-
return mesh;
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function getBuiltMesh(meshArtifacts, meshConfig) {
|
|
42
|
-
if (meshInstance$ == null) {
|
|
43
|
-
meshInstance$ = buildMeshInstance(meshArtifacts, meshConfig);
|
|
44
|
-
}
|
|
45
|
-
return meshInstance$;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const buildServer = async (loggerInstance, env, meshArtifacts, meshConfig) => {
|
|
49
|
-
const { Secret: secret } = env;
|
|
50
|
-
const tenantMesh = await getBuiltMesh(meshArtifacts, meshConfig);
|
|
51
|
-
const meshSecrets = loadMeshSecrets(loggerInstance, secret);
|
|
52
|
-
return await buildYogaServer(env, tenantMesh, meshConfig, meshSecrets);
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
async function buildYogaServer(env, tenantMesh, meshConfig, meshSecrets) {
|
|
56
|
-
const secretsProxy = new Proxy(meshSecrets, getSecretsHandler);
|
|
57
|
-
return createYoga({
|
|
58
|
-
plugins: tenantMesh.plugins,
|
|
59
|
-
graphqlEndpoint: `/graphql`,
|
|
60
|
-
cors: getCorsOptions(env, meshConfig),
|
|
61
|
-
context: initialContext => ({
|
|
62
|
-
...initialContext,
|
|
63
|
-
secrets: secretsProxy,
|
|
64
|
-
}),
|
|
65
|
-
maskedErrors: {
|
|
66
|
-
maskError: maskError,
|
|
67
|
-
},
|
|
68
|
-
logging: 'debug',
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const maskError = error => {
|
|
73
|
-
if (error instanceof GraphQLError && error.extensions?.http?.headers) {
|
|
74
|
-
delete error.extensions.http.headers;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
return error;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
module.exports = { buildServer };
|