@ebay/muse-runner 1.0.21

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 (55) hide show
  1. package/.muserc +1 -0
  2. package/README.md +8 -0
  3. package/bin/muse-runner.js +19 -0
  4. package/lib/AppRunner.js +54 -0
  5. package/lib/Command.js +55 -0
  6. package/lib/MuseRunner.js +135 -0
  7. package/lib/PluginRunner.js +67 -0
  8. package/lib/apis/checkUpdate.js +25 -0
  9. package/lib/apis/gitStatus.js +89 -0
  10. package/lib/apis/settings.js +9 -0
  11. package/lib/apis/startPlugin.js +22 -0
  12. package/lib/apis/stopPlugin.js +8 -0
  13. package/lib/apis/terminals.js +118 -0
  14. package/lib/appWorker.js +325 -0
  15. package/lib/reactFastRefresh.js +452 -0
  16. package/lib/server.js +463 -0
  17. package/lib/test.js +20 -0
  18. package/lib/upgrades/up_240102.js +25 -0
  19. package/lib/utils.js +79 -0
  20. package/package.json +49 -0
  21. package/public/index.html +14 -0
  22. package/public/muse-assets/p/@ebay.muse-boot-default/v1.0.25/dist/asset-manifest.json +10 -0
  23. package/public/muse-assets/p/@ebay.muse-boot-default/v1.0.25/dist/boot.js +2 -0
  24. package/public/muse-assets/p/@ebay.muse-boot-default/v1.0.25/dist/static/media/logo.0629cb217459ef0a31a2.png +0 -0
  25. package/public/muse-assets/p/@ebay.muse-layout-antd/v1.1.26/dist/assets/index-BeGfgbtf.css +1 -0
  26. package/public/muse-assets/p/@ebay.muse-layout-antd/v1.1.26/dist/assets/muse-V3RbDVED.png +0 -0
  27. package/public/muse-assets/p/@ebay.muse-layout-antd/v1.1.26/dist/deps-manifest.json +19 -0
  28. package/public/muse-assets/p/@ebay.muse-layout-antd/v1.1.26/dist/main.js +16 -0
  29. package/public/muse-assets/p/@ebay.muse-layout-antd/v1.1.26/dist/readme.txt +1 -0
  30. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/asset-manifest.json +12 -0
  31. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/deps-manifest.json +26 -0
  32. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/info.json +3 -0
  33. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/lib-manifest.json +32768 -0
  34. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/main.js +3 -0
  35. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/main.js.LICENSE.txt +30 -0
  36. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/readme.txt +1 -0
  37. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/report.html +39 -0
  38. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/static/js/205.bfaa7a71.chunk.js +2 -0
  39. package/public/muse-assets/p/@ebay.muse-lib-antd/v1.2.22/dist/static/media/lightOn.f95be1d2e4156b9a7459.svg +1 -0
  40. package/public/muse-assets/p/@ebay.muse-lib-react/v1.2.20/dist/asset-manifest.json +11 -0
  41. package/public/muse-assets/p/@ebay.muse-lib-react/v1.2.20/dist/favicon.png +0 -0
  42. package/public/muse-assets/p/@ebay.muse-lib-react/v1.2.20/dist/info.json +3 -0
  43. package/public/muse-assets/p/@ebay.muse-lib-react/v1.2.20/dist/lib-manifest.json +3821 -0
  44. package/public/muse-assets/p/@ebay.muse-lib-react/v1.2.20/dist/main.js +3 -0
  45. package/public/muse-assets/p/@ebay.muse-lib-react/v1.2.20/dist/main.js.LICENSE.txt +136 -0
  46. package/public/muse-assets/p/@ebay.muse-lib-react/v1.2.20/dist/static/media/logo.b23b880b0dac2229042c.png +0 -0
  47. package/public/muse-assets/p/@ebay.muse-lib-react/v1.2.20/dist/static/media/subAppLoading2.bf08007b83457287ade1cedb71bc70f7.svg +7 -0
  48. package/public/muse-assets/p/app-icon.muserunner/v0.0.1/dist/icon.png +0 -0
  49. package/public/muse-assets/p/muse-runner-ui/v1.0.26/dist/asset-manifest.json +10 -0
  50. package/public/muse-assets/p/muse-runner-ui/v1.0.26/dist/deps-manifest.json +43 -0
  51. package/public/muse-assets/p/muse-runner-ui/v1.0.26/dist/main.js +3 -0
  52. package/public/muse-assets/p/muse-runner-ui/v1.0.26/dist/main.js.LICENSE.txt +43 -0
  53. package/public/muse-assets/p/muse-runner-ui/v1.0.26/dist/readme.txt +2 -0
  54. package/public/muse-assets/p/muse-runner-ui/v1.0.26/dist/static/media/vscode.e44746dcd601802dfb4fe3374cfab5b0.svg +18 -0
  55. package/public/muse-sw.js +111 -0
@@ -0,0 +1,325 @@
1
+ /**
2
+ * This worker simulates the Muse app server, it manages the plugins and serves the app.
3
+ *
4
+ * - It listens to the parent process for the app config and plugin config change
5
+ * - Running plugins are served plugin runners
6
+ * - If a running plugin is a lib plugin, use URL to load the plugin
7
+ * - Override app and plugin variables from config
8
+ * - If a lib plugin is running at local mode, it has highest priority to load
9
+ */
10
+ import { workerData, parentPort } from 'node:worker_threads';
11
+ import fs from 'node:fs';
12
+ import express from 'express';
13
+ import muse from '@ebay/muse-core';
14
+ import _ from 'lodash';
15
+ import cors from 'cors';
16
+ import https from 'node:https';
17
+ import path from 'path';
18
+ import crypto from 'crypto';
19
+ import museDevUtils from '@ebay/muse-dev-utils/lib/utils.js';
20
+ import * as url from 'url';
21
+ import museAssetsMiddleware from '@ebay/muse-express-middleware/lib/assets.js';
22
+ import museAppMiddleware from '@ebay/muse-express-middleware/lib/app.js';
23
+
24
+ const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
25
+ const host = process.env.MUSE_LOCAL_HOST_NAME || 'localhost';
26
+
27
+ const { port } = workerData;
28
+ const promiseResolvers = {};
29
+ parentPort.on('message', (msg) => {
30
+ if (msg.type === 'resolve-promise') {
31
+ const resolve = promiseResolvers[msg.payload.promiseId];
32
+ if (resolve) {
33
+ resolve(msg.payload.result);
34
+ delete promiseResolvers[msg.payload.promiseId];
35
+ }
36
+ }
37
+ });
38
+
39
+ const callParentApi = (key) => {
40
+ const promiseId = crypto.randomBytes(12).toString('hex');
41
+ const promise = new Promise((resolve) => {
42
+ promiseResolvers[promiseId] = resolve;
43
+ parentPort.postMessage({
44
+ type: 'call-parent-api',
45
+ payload: {
46
+ promiseId,
47
+ key,
48
+ },
49
+ });
50
+ });
51
+
52
+ return promise;
53
+ };
54
+
55
+ muse.plugin.register({
56
+ name: 'muse-runner',
57
+ museMiddleware: {
58
+ app: {
59
+ processIndexHtml: async (ctx) => {
60
+ // This is to support vite-react
61
+ // This may need to be implemented as a plugin
62
+ const appConfig = await callParentApi('get-app-config');
63
+ const viteProtocolAndPorts = appConfig.plugins
64
+ ?.filter((p) => p.esModule && p.running)
65
+ .map((p) => [p.port, p.protocol])
66
+ .filter(Boolean);
67
+ if (!viteProtocolAndPorts || !viteProtocolAndPorts.length) return;
68
+ const importMap = { imports: {} };
69
+ viteProtocolAndPorts.forEach(([vitePort, protocol]) => {
70
+ importMap.imports[`${protocol}://${host}:${vitePort}/@react-refresh`] = `/@react-refresh`;
71
+ });
72
+ ctx.indexHtml = ctx.indexHtml.replace(
73
+ '<head>',
74
+ `<head>
75
+
76
+ <script type="importmap">
77
+ ${JSON.stringify(importMap, null, 2)}
78
+ </script>
79
+ <script type="module">
80
+ import RefreshRuntime from "/@react-refresh"
81
+ RefreshRuntime.injectIntoGlobalHook(window)
82
+ window.$RefreshReg$ = () => {}
83
+ window.$RefreshSig$ = () => (type) => type
84
+ window.__vite_plugin_react_preamble_installed__ = true
85
+ </script>`,
86
+ );
87
+ },
88
+ getAppInfo: async () => {
89
+ // NOTE: appConfig always changes, so need to get it every time
90
+ const appConfig = await callParentApi('get-app-config');
91
+ return { appName: appConfig.app, envName: appConfig.env };
92
+ },
93
+
94
+ // Use this ext point to process the app info, and set the env variables
95
+ processAppInfo: async ({ app, env }) => {
96
+ // Get the app config in muse runner from parent process
97
+ // NOTE: appConfig always changes, so need to get it every time
98
+ // This gets all data about the app, including the plugins, variables, envs, etc.
99
+ const appConfig = await callParentApi('get-app-config');
100
+
101
+ // All deployed plugins on the app
102
+ const deployedPluginByName = _.keyBy(env.plugins, 'name');
103
+
104
+ if (!env.variables) env.variables = {};
105
+ if (!env.pluginVariables) env.pluginVariables = {};
106
+
107
+ // Override dev time app variables
108
+ Object.assign(env.variables, appConfig.variables || {});
109
+
110
+ // Override dev time plugin variables
111
+ Object.entries(appConfig.pluginVariables || {}).forEach(([pluginName, variables]) => {
112
+ if (!env.pluginVariables[pluginName]) env.pluginVariables[pluginName] = {};
113
+ Object.assign(env.pluginVariables[pluginName], variables);
114
+ });
115
+
116
+ // const linkedPlugins = [];
117
+
118
+ // For local installed lib plugins, need to load them from local dev server of the parent
119
+ // plugin because they may have not been deployed.
120
+ // Here use localLibs to store the local lib plugins' urls
121
+ const localLibPluings = {};
122
+
123
+ // Determine how to load the plugins
124
+ appConfig?.plugins?.forEach((p) => {
125
+ // NOTE: if a plugin uses deployed mode but not found, just return
126
+ if (p.mode === 'deployed') return;
127
+ let deployedPlugin = deployedPluginByName[p.name];
128
+
129
+ // If the configured plugin is not deployed, then add it
130
+ // If a plugin is local but not deployed and not running, it's not added
131
+ // The url will be set later
132
+ if (!deployedPlugin && (p.mode !== 'local' || p.running)) {
133
+ deployedPlugin = {
134
+ name: p.name,
135
+ type: p.type,
136
+ };
137
+ deployedPluginByName[p.name] = deployedPlugin;
138
+ env.plugins.push(deployedPlugin);
139
+ }
140
+ // set a flag to avoid being removed
141
+
142
+ switch (p.mode) {
143
+ case 'excluded':
144
+ _.remove(env.plugins, (p2) => p2.name === p.name);
145
+ break;
146
+ case 'version':
147
+ deployedPlugin.version = p.version;
148
+ break;
149
+ case 'url':
150
+ deployedPlugin.url = p.url;
151
+ deployedPlugin.esModule = !!p.urlEsModule;
152
+ break;
153
+ case 'local': {
154
+ if (p.running) {
155
+ const pluginProtocol = p.protocol || 'https';
156
+ if (p.esModule) {
157
+ const entryFile = museDevUtils.getEntryFile(p.dir);
158
+ deployedPlugin.url = `${pluginProtocol}://${host}:${p.port}/${entryFile}`;
159
+ deployedPlugin.esModule = true;
160
+ } else {
161
+ deployedPlugin.url = `${pluginProtocol}://${host}:${p.port}/${
162
+ p.type === 'boot' ? 'boot' : 'main'
163
+ }.js`;
164
+ }
165
+ // isLocal is used to show log in muse-boot
166
+ deployedPlugin.isLocal = true;
167
+ // linkedPlugins.push(
168
+ // ...(p.linkedPlugins || []).map((lp) => ({
169
+ // name: lp.name,
170
+ // parent: p.name,
171
+ // })),
172
+ // );
173
+
174
+ // Get all installed libs, and add them to localLibPluings
175
+ const museLibs = museDevUtils.getMuseLibsByFolder(p.dir);
176
+ // p.linkedPlugins?.forEach((lp) => {
177
+ // museDevUtils.getMuseLibsByFolder(lp.dir).forEach((lib) => {
178
+ // museLibs.push(lib);
179
+ // });
180
+ // });
181
+
182
+ // Serve lib plugins from the first running plugin
183
+ // We don't handle lib plugin version conflicts here because
184
+ // all local running plugins on the app should have the same version of a lib plugin
185
+ // There should be auto update logic to ensure all plugins have the same version of a lib plugin
186
+ // We don't serve lib plugins from remote because maybe a lib plugin has not been deployed yet.
187
+ _.uniqBy(museLibs, 'name').forEach((lib) => {
188
+ if (localLibPluings[lib.name]) return;
189
+ const pid = muse.utils.getPluginId(lib.name);
190
+ localLibPluings[lib.name] = {
191
+ url: `${pluginProtocol}://${host}:${p.port}/muse-assets/local/p/${pid}/dev/main.js`,
192
+ version: lib.version,
193
+ };
194
+ });
195
+ }
196
+ break;
197
+ }
198
+ case 'deployed':
199
+ // already return
200
+ break;
201
+ default:
202
+ // some error happened?
203
+ break;
204
+ }
205
+ });
206
+
207
+ // linkedPlugins.forEach((lp) => {
208
+ // const name = `${lp.name} (linked to ${lp.parent})})`;
209
+ // if (deployedPluginByName[lp.name]) {
210
+ // deployedPluginByName[lp.name].linkedTo = lp.parent;
211
+ // } else {
212
+ // env.plugins.push({
213
+ // name: name,
214
+ // linkedTo: lp.parent,
215
+ // type: lp.type,
216
+ // });
217
+ // }
218
+ // });
219
+
220
+ const configPluginByName = _.keyBy(appConfig.plugins, 'name');
221
+ if (!appConfig.loadAllPlugins) {
222
+ // NOTE: exluded plugins already removed
223
+ env.plugins = env.plugins.filter(
224
+ // boot/init/lib plugins are always loaded for an app
225
+ (p) =>
226
+ app?.pluginConfig?.[p.name]?.core || // if configured as core plugins, always load
227
+ p.type === 'boot' ||
228
+ p.type === 'init' ||
229
+ p.type === 'lib' ||
230
+ configPluginByName[p.name], //|| // NOTE: excluded plugins already removed
231
+ // p.linkedTo, // if a plugin is linked, need to get config, so we need to load it
232
+ );
233
+ }
234
+
235
+ // For local installed lib plugins, need to serve them from local dev server if the mode is local or undefined
236
+ const pluginMode = _.mapValues(configPluginByName, 'mode');
237
+ env.plugins.forEach((p) => {
238
+ // For a lib plugin, if installed locally, then serve it from local
239
+ // dev server unless the mode is 'version' or 'deployed' or 'url'
240
+
241
+ // If it's local and running, the url is already set
242
+ if (p.url || p.type !== 'lib' || !localLibPluings[p.name]) {
243
+ return;
244
+ }
245
+
246
+ const mode = pluginMode[p.name];
247
+ if (!mode || mode === 'local') {
248
+ p.url = localLibPluings[p.name].url;
249
+ p.deployedVersion = p.version;
250
+ p.version = localLibPluings[p.name].version;
251
+ p.isLocalLib = true;
252
+ }
253
+ });
254
+
255
+ // For new local installed lib plugins
256
+ _.keys(localLibPluings).forEach((name) => {
257
+ // If lib plugin not deployed and not excluded, add it
258
+ if (_.find(env.plugins, { name }) || pluginMode[name] === 'excluded') return;
259
+ env.plugins.push({
260
+ name: name,
261
+ type: 'lib',
262
+ url: localLibPluings[name].url,
263
+ version: localLibPluings[name].version,
264
+ isLocalLib: true,
265
+ notDeployed: true,
266
+ });
267
+ });
268
+
269
+ // If a lib plugin is linked to some other running (local mode) plugin,
270
+ // Then don't load it from anywhere else
271
+ // env.plugins.forEach((p) => {
272
+ // // if (p.linkedTo && !p.running) delete p.url;
273
+ // // TODO: may not need this logic since link mechanism will be changed
274
+ // });
275
+ },
276
+ },
277
+ },
278
+ });
279
+
280
+ const app = express();
281
+ app.use(cors());
282
+
283
+ // TODO: vite/client seems useless but just for no error
284
+ app.use('/@react-refresh', express.static(path.join(__dirname, 'reactFastRefresh.js')));
285
+
286
+ app.use(museAssetsMiddleware({}));
287
+
288
+ app.use(
289
+ museAppMiddleware({
290
+ isDev: true,
291
+ isLocal: true,
292
+ cdn: '/muse-assets',
293
+ }),
294
+ );
295
+
296
+ const appConfig = await callParentApi('get-app-config');
297
+ const isHttps = appConfig.protocol !== 'http';
298
+
299
+ if (
300
+ isHttps &&
301
+ (!process.env.SSL_CRT_FILE ||
302
+ !process.env.SSL_KEY_FILE ||
303
+ !fs.existsSync(process.env.SSL_KEY_FILE) ||
304
+ !fs.existsSync(process.env.SSL_CRT_FILE))
305
+ ) {
306
+ console.log('Failed to start app: SSL_KEY_FILE and SSL_CRT_FILE are required for https');
307
+ process.exit(1);
308
+ } else if (isHttps) {
309
+ https
310
+ .createServer(
311
+ {
312
+ key: fs.readFileSync(process.env.SSL_KEY_FILE),
313
+ cert: fs.readFileSync(process.env.SSL_CRT_FILE),
314
+ },
315
+ app,
316
+ )
317
+ .listen(port);
318
+
319
+ console.log(`Muse app started: https://${host}:${port}`);
320
+ } else {
321
+ app.listen(port, () => {
322
+ const host = process.env.MUSE_LOCAL_HOST_NAME || 'localhost';
323
+ console.log(`Muse app started: http://${host}:${port}`);
324
+ });
325
+ }