@antelopejs/dms-frontend 0.5.5 → 0.6.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 (75) hide show
  1. package/README.md +377 -19
  2. package/dist/app-configs.d.ts +9 -0
  3. package/dist/app-configs.js +52 -0
  4. package/dist/asset-roots.d.ts +7 -0
  5. package/dist/asset-roots.js +34 -0
  6. package/dist/commands/build-action.js +9 -0
  7. package/dist/commands/dev-action.js +88 -19
  8. package/dist/commands/start-action.js +2 -0
  9. package/dist/common.d.ts +1 -0
  10. package/dist/common.js +1 -0
  11. package/dist/config.d.ts +2 -0
  12. package/dist/config.js +9 -3
  13. package/dist/derived-outputs.d.ts +2 -0
  14. package/dist/derived-outputs.js +38 -31
  15. package/dist/help.js +10 -3
  16. package/dist/layer-watch.js +123 -131
  17. package/dist/layers.js +2 -0
  18. package/dist/manifest-secrets.js +5 -5
  19. package/dist/manifest.js +7 -1
  20. package/dist/materialize.js +21 -1
  21. package/dist/module-set.d.ts +16 -0
  22. package/dist/module-set.js +80 -0
  23. package/dist/server-process.d.ts +1 -0
  24. package/dist/server-process.js +28 -2
  25. package/dist/verify-source-build.d.ts +11 -0
  26. package/dist/verify-source-build.js +113 -0
  27. package/dist/verify-source-runner.js +14 -57
  28. package/dist/workspace.d.ts +9 -0
  29. package/package.json +2 -1
  30. package/templates/vue/app-runtime.ts +58 -16
  31. package/templates/vue/client-server-guard.mjs +73 -0
  32. package/templates/vue/email-renderer.ts +17 -4
  33. package/templates/vue/email-runtime.ts +1 -1
  34. package/templates/vue/frontend-module.ts +1817 -259
  35. package/templates/vue/globals.d.ts +1 -1
  36. package/templates/vue/icon-hydration.ts +106 -1
  37. package/templates/vue/index.html +0 -1
  38. package/templates/vue/layer-app-config.ts +92 -0
  39. package/templates/vue/main.ts +10 -2
  40. package/templates/vue/package.json +5 -1
  41. package/templates/vue/prepare-types.mjs +34 -0
  42. package/templates/vue/server/auth/backend.mjs +13 -19
  43. package/templates/vue/server/auth/client-ip.mjs +27 -0
  44. package/templates/vue/server/auth/establish-endpoints.mjs +70 -0
  45. package/templates/vue/server/auth/session.mjs +6 -97
  46. package/templates/vue/server/auth-hooks.mjs +84 -0
  47. package/templates/vue/server/backend-proxies.mjs +110 -0
  48. package/templates/vue/server/dev-private-files.mjs +99 -0
  49. package/templates/vue/server/email.mjs +11 -33
  50. package/templates/vue/server/favicon.mjs +20 -0
  51. package/templates/vue/server/inertia.mjs +48 -3
  52. package/templates/vue/server/listen-host.mjs +19 -0
  53. package/templates/vue/server/module-event.mjs +265 -0
  54. package/templates/vue/server/module-routes.mjs +513 -0
  55. package/templates/vue/server/module-server.mjs +225 -0
  56. package/templates/vue/server/module-testing.mjs +65 -0
  57. package/templates/vue/server/page-props.mjs +151 -0
  58. package/templates/vue/server/path-patterns.mjs +38 -0
  59. package/templates/vue/server/proxy-headers.mjs +25 -0
  60. package/templates/vue/server/public-endpoints.mjs +69 -11
  61. package/templates/vue/server/request-routing.mjs +47 -0
  62. package/templates/vue/server/route-rules.mjs +129 -0
  63. package/templates/vue/server-module.ts +294 -0
  64. package/templates/vue/server.mjs +178 -169
  65. package/templates/vue/ssr-renderer.ts +60 -19
  66. package/templates/vue/tsconfig.json +2 -1
  67. package/templates/vue/ui-app-config.ts +18 -6
  68. package/templates/vue/ui-inertia-page.mjs +37 -0
  69. package/templates/vue/vite.config.ts +31 -10
  70. package/templates/vue/vite.email.config.ts +1 -1
  71. package/templates/vue/vite.server.config.ts +57 -0
  72. package/templates/vue/server/auth/oauth.mjs +0 -233
  73. package/templates/vue/server/auth/routes.mjs +0 -444
  74. package/templates/vue/server/render-token.mjs +0 -81
  75. package/templates/vue/server/tester.mjs +0 -227
@@ -14,7 +14,7 @@ const derived_outputs_1 = require("./derived-outputs");
14
14
  const output_1 = require("./output");
15
15
  const fs_sync_1 = require("./fs-sync");
16
16
  const WATCH_DEBOUNCE_MS = 80;
17
- const DERIVED_REFRESH_DEBOUNCE_MS = 100;
17
+ const DERIVED_REFRESH_GAP_MS = 100;
18
18
  function capitalize(text) {
19
19
  return `${text.charAt(0).toUpperCase()}${text.slice(1)}`;
20
20
  }
@@ -37,126 +37,88 @@ function createSyncFailures(src, reporter) {
37
37
  },
38
38
  };
39
39
  }
40
- function createLayerSync(src, dest, onFlushed, reporter) {
40
+ function createLayerSync(src, dest, reporter, onStaged) {
41
41
  const fileUpserts = new Set();
42
42
  const fileDeletes = new Set();
43
43
  const dirCreates = new Set();
44
44
  const dirDeletes = new Set();
45
45
  const failures = createSyncFailures(src, reporter);
46
- let timer = null;
47
46
  const toRel = (p) => {
48
47
  const r = (0, node_path_1.relative)(src, p);
49
48
  return r ? r : null;
50
49
  };
51
- const flush = () => {
52
- timer = null;
53
- const touched = [
54
- ...dirCreates,
55
- ...fileUpserts,
56
- ...fileDeletes,
57
- ...dirDeletes,
58
- ];
59
- for (const rel of [...dirCreates].sort((a, b) => a.length - b.length)) {
60
- try {
61
- (0, node_fs_1.mkdirSync)((0, node_path_1.join)(dest, rel), { recursive: true });
62
- failures.applied(rel);
50
+ const take = (staged, select) => {
51
+ const taken = [...staged].filter(select);
52
+ for (const rel of taken)
53
+ staged.delete(rel);
54
+ return taken;
55
+ };
56
+ const upsert = (rel) => {
57
+ try {
58
+ const srcPath = (0, node_path_1.join)(src, rel);
59
+ if (!(0, node_fs_1.existsSync)(srcPath))
60
+ return;
61
+ const destPath = (0, node_path_1.join)(dest, rel);
62
+ if (rel === "package.json") {
63
+ (0, fs_sync_1.applyContent)((0, fs_sync_1.sanitizedPackageContent)((0, node_fs_1.readFileSync)(srcPath, "utf-8")), destPath);
63
64
  }
64
- catch (err) {
65
- failures.fail(rel, err);
65
+ else {
66
+ (0, fs_sync_1.applyFile)(srcPath, destPath);
66
67
  }
68
+ failures.applied(rel);
67
69
  }
68
- dirCreates.clear();
69
- for (const rel of fileUpserts) {
70
- try {
71
- const srcPath = (0, node_path_1.join)(src, rel);
72
- if (!(0, node_fs_1.existsSync)(srcPath))
73
- continue;
74
- const destPath = (0, node_path_1.join)(dest, rel);
75
- if (rel === "package.json") {
76
- (0, fs_sync_1.applyContent)((0, fs_sync_1.sanitizedPackageContent)((0, node_fs_1.readFileSync)(srcPath, "utf-8")), destPath);
77
- }
78
- else {
79
- (0, fs_sync_1.applyFile)(srcPath, destPath);
80
- }
81
- failures.applied(rel);
82
- }
83
- catch (err) {
84
- failures.fail(rel, err);
85
- }
70
+ catch (err) {
71
+ failures.fail(rel, err);
86
72
  }
87
- fileUpserts.clear();
88
- for (const rel of fileDeletes) {
89
- try {
90
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(src, rel)))
91
- continue;
92
- const destPath = (0, node_path_1.join)(dest, rel);
93
- if ((0, node_fs_1.existsSync)(destPath))
94
- (0, node_fs_1.rmSync)(destPath, { force: true });
95
- failures.applied(rel);
96
- }
97
- catch (err) {
98
- failures.fail(rel, err);
99
- }
73
+ };
74
+ const remove = (rel, recursive) => {
75
+ try {
76
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(src, rel)))
77
+ return;
78
+ const destPath = (0, node_path_1.join)(dest, rel);
79
+ if ((0, node_fs_1.existsSync)(destPath))
80
+ (0, node_fs_1.rmSync)(destPath, { recursive, force: true });
81
+ failures.applied(rel);
100
82
  }
101
- fileDeletes.clear();
102
- for (const rel of [...dirDeletes].sort((a, b) => b.length - a.length)) {
103
- try {
104
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(src, rel)))
105
- continue;
106
- const destPath = (0, node_path_1.join)(dest, rel);
107
- if ((0, node_fs_1.existsSync)(destPath))
108
- (0, node_fs_1.rmSync)(destPath, { recursive: true, force: true });
109
- failures.applied(rel);
110
- }
111
- catch (err) {
112
- failures.fail(rel, err);
113
- }
83
+ catch (err) {
84
+ failures.fail(rel, err);
114
85
  }
115
- dirDeletes.clear();
116
- onFlushed(touched);
117
86
  };
118
- const arm = () => {
119
- if (timer)
120
- clearTimeout(timer);
121
- timer = setTimeout(flush, WATCH_DEBOUNCE_MS);
87
+ const stage = (p, add, cancel) => {
88
+ const r = toRel(p);
89
+ if (!r)
90
+ return;
91
+ cancel.delete(r);
92
+ add.add(r);
93
+ onStaged();
122
94
  };
123
95
  return {
124
- upsertFile(p) {
125
- const r = toRel(p);
126
- if (!r)
127
- return;
128
- fileDeletes.delete(r);
129
- fileUpserts.add(r);
130
- arm();
96
+ src,
97
+ upsertFile: (p) => stage(p, fileUpserts, fileDeletes),
98
+ deleteFile: (p) => stage(p, fileDeletes, fileUpserts),
99
+ createDir: (p) => stage(p, dirCreates, dirDeletes),
100
+ deleteDir: (p) => stage(p, dirDeletes, dirCreates),
101
+ pending() {
102
+ return [...dirCreates, ...fileUpserts, ...fileDeletes, ...dirDeletes];
131
103
  },
132
- deleteFile(p) {
133
- const r = toRel(p);
134
- if (!r)
135
- return;
136
- fileUpserts.delete(r);
137
- fileDeletes.add(r);
138
- arm();
139
- },
140
- createDir(p) {
141
- const r = toRel(p);
142
- if (!r)
143
- return;
144
- dirDeletes.delete(r);
145
- dirCreates.add(r);
146
- arm();
147
- },
148
- deleteDir(p) {
149
- const r = toRel(p);
150
- if (!r)
151
- return;
152
- dirCreates.delete(r);
153
- dirDeletes.add(r);
154
- arm();
104
+ createDirs() {
105
+ for (const rel of take(dirCreates, () => true).sort((a, b) => a.length - b.length)) {
106
+ try {
107
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(dest, rel), { recursive: true });
108
+ failures.applied(rel);
109
+ }
110
+ catch (err) {
111
+ failures.fail(rel, err);
112
+ }
113
+ }
155
114
  },
156
- flushNow() {
157
- if (timer)
158
- clearTimeout(timer);
159
- flush();
115
+ apply(select) {
116
+ for (const rel of take(fileUpserts, select))
117
+ upsert(rel);
118
+ for (const rel of take(fileDeletes, select))
119
+ remove(rel, false);
120
+ for (const rel of take(dirDeletes, select).sort((a, b) => b.length - a.length))
121
+ remove(rel, true);
160
122
  },
161
123
  };
162
124
  }
@@ -186,10 +148,8 @@ function describeLayerDirectories(paths, reporter) {
186
148
  : `Layer directory ${shown[0]} removed`;
187
149
  }
188
150
  function createDerivedOutputRefresher(workspaceDir, layers, reporter) {
189
- const pending = new Map();
190
151
  const failures = new Map();
191
152
  let hasRestartNotice = false;
192
- let timer = null;
193
153
  const fail = (key, failure) => {
194
154
  failures.set(key, failure);
195
155
  reporter.warn(failure.text, failure.options);
@@ -215,19 +175,18 @@ function createDerivedOutputRefresher(workspaceDir, layers, reporter) {
215
175
  recover(output);
216
176
  if (isChanged)
217
177
  noticeRestart(output, paths);
178
+ return isChanged;
218
179
  }
219
180
  catch (err) {
220
181
  fail(output, describeOutputFailure(output, err, reporter));
182
+ return false;
221
183
  }
222
184
  };
223
- const refresh = () => {
224
- timer = null;
225
- const outputs = derived_outputs_1.DERIVED_OUTPUTS.filter((output) => pending.has(output)).map((output) => [output, pending.get(output) ?? []]);
226
- pending.clear();
227
- let registry;
185
+ const buildRegistry = () => {
228
186
  try {
229
- registry = (0, derived_outputs_1.createFrontendModuleRegistry)(workspaceDir, layers);
187
+ const registry = (0, derived_outputs_1.createFrontendModuleRegistry)(workspaceDir, layers);
230
188
  recover("registry");
189
+ return registry;
231
190
  }
232
191
  catch (err) {
233
192
  fail("registry", {
@@ -240,35 +199,69 @@ function createDerivedOutputRefresher(workspaceDir, layers, reporter) {
240
199
  },
241
200
  fixed: "Derived files regenerated",
242
201
  });
243
- return;
202
+ return undefined;
244
203
  }
245
- for (const [output, paths] of outputs)
246
- regenerate(output, paths, registry);
247
204
  };
248
205
  return {
249
- schedule(src, paths) {
250
- let isAffected = false;
206
+ plan(touched) {
207
+ const pending = new Map();
251
208
  for (const output of derived_outputs_1.DERIVED_OUTPUTS) {
252
- const affected = paths.filter((path) => output.affects(path));
253
- if (affected.length === 0)
254
- continue;
255
- isAffected = true;
256
- pending.set(output, [
257
- ...(pending.get(output) ?? []),
258
- ...affected.map((path) => (0, node_path_1.join)(src, path)),
259
- ]);
209
+ const affected = touched.flatMap(({ src, paths }) => paths
210
+ .filter((path) => output.affects(path))
211
+ .map((path) => (0, node_path_1.join)(src, path)));
212
+ if (affected.length > 0)
213
+ pending.set(output, affected);
260
214
  }
261
- if (!isAffected)
262
- return;
215
+ const registry = pending.size > 0 ? buildRegistry() : undefined;
216
+ return (select) => {
217
+ let isChanged = false;
218
+ if (!registry)
219
+ return isChanged;
220
+ for (const [output, paths] of pending) {
221
+ if (select(output))
222
+ isChanged = regenerate(output, paths, registry) || isChanged;
223
+ }
224
+ return isChanged;
225
+ };
226
+ },
227
+ };
228
+ }
229
+ function isReadAsData(relativePath) {
230
+ return derived_outputs_1.DERIVED_OUTPUTS.some((output) => output.readsSourcesAsData && output.affects(relativePath));
231
+ }
232
+ function runPass(syncs, refresher) {
233
+ const writeOutputs = refresher.plan(syncs.map((sync) => ({ src: sync.src, paths: sync.pending() })));
234
+ const isDataOutput = (output) => output.readsSourcesAsData === true;
235
+ for (const sync of syncs)
236
+ sync.createDirs();
237
+ for (const sync of syncs)
238
+ sync.apply(isReadAsData);
239
+ const isDataChanged = writeOutputs(isDataOutput);
240
+ for (const sync of syncs)
241
+ sync.apply((path) => !isReadAsData(path));
242
+ const isChanged = writeOutputs((output) => !isDataOutput(output));
243
+ return isDataChanged || isChanged;
244
+ }
245
+ function createPassScheduler(pass) {
246
+ let timer = null;
247
+ let lastRegeneration = Number.NEGATIVE_INFINITY;
248
+ const flush = () => {
249
+ timer = null;
250
+ if (pass())
251
+ lastRegeneration = Date.now();
252
+ };
253
+ return {
254
+ arm: () => {
263
255
  if (timer)
264
256
  clearTimeout(timer);
265
- timer = setTimeout(refresh, DERIVED_REFRESH_DEBOUNCE_MS);
257
+ const gap = lastRegeneration + DERIVED_REFRESH_GAP_MS - Date.now();
258
+ timer = setTimeout(flush, Math.max(WATCH_DEBOUNCE_MS, gap));
266
259
  },
267
- flushNow() {
260
+ flushNow: () => {
268
261
  if (!timer)
269
262
  return;
270
263
  clearTimeout(timer);
271
- refresh();
264
+ flush();
272
265
  },
273
266
  };
274
267
  }
@@ -289,12 +282,13 @@ function startLayerWatchers(workspaceDir, layers, ui = (0, cli_1.getProcessUi)()
289
282
  const watchers = [];
290
283
  const syncs = [];
291
284
  const refresher = createDerivedOutputRefresher(workspaceDir, layers, reporter);
285
+ const passes = createPassScheduler(() => runPass(syncs, refresher));
292
286
  for (const layer of layers) {
293
287
  if (!layer.packageName)
294
288
  continue;
295
289
  const dest = (0, layers_1.getLayerWorkspacePath)(workspaceDir, layer);
296
290
  const src = layer.path;
297
- const sync = createLayerSync(src, dest, (paths) => refresher.schedule(src, paths), reporter);
291
+ const sync = createLayerSync(src, dest, reporter, passes.arm);
298
292
  syncs.push(sync);
299
293
  const watcher = chokidar_1.default.watch(src, {
300
294
  ignoreInitial: true,
@@ -310,8 +304,6 @@ function startLayerWatchers(workspaceDir, layers, ui = (0, cli_1.getProcessUi)()
310
304
  }
311
305
  return async () => {
312
306
  await Promise.all(watchers.map((w) => w.close().catch(() => { })));
313
- for (const sync of syncs)
314
- sync.flushNow();
315
- refresher.flushNow();
307
+ passes.flushNow();
316
308
  };
317
309
  }
package/dist/layers.js CHANGED
@@ -125,8 +125,10 @@ function resolveLayer(layerPath, mod) {
125
125
  priority: mod.priority,
126
126
  configKey: mod.configKey,
127
127
  options: mod.options,
128
+ privateOptions: mod.privateOptions,
128
129
  authEstablishEndpoints: mod.authEstablishEndpoints,
129
130
  publicEndpoints: mod.publicEndpoints,
131
+ backendProxies: mod.backendProxies,
130
132
  };
131
133
  }
132
134
  function buildLayersFromPaths(modules) {
@@ -59,14 +59,14 @@ function resolveManifestSecrets(secrets, env = process.env) {
59
59
  for (const name of SECRET_NAMES) {
60
60
  const explicit = env[name];
61
61
  const published = secrets.published[name];
62
- if (explicit) {
63
- resolved.env[name] = explicit;
64
- resolved.sources[name] = "env";
65
- }
66
- else if (published) {
62
+ if (published) {
67
63
  resolved.env[name] = published.value;
68
64
  resolved.sources[name] = "manifest";
69
65
  }
66
+ else if (explicit) {
67
+ resolved.env[name] = explicit;
68
+ resolved.sources[name] = "env";
69
+ }
70
70
  else {
71
71
  resolved.sources[name] = "not set";
72
72
  }
package/dist/manifest.js CHANGED
@@ -192,7 +192,13 @@ async function fetchManifest(backendUrl, clientUrl, bootstrapSecret) {
192
192
  if (incompatible.length) {
193
193
  throw new ManifestRefusedError(`Frontend manifest contains incompatible renderer modules: ${incompatible.map((module) => module.name).join(", ")}`);
194
194
  }
195
- return { pack: manifest.archive, modules: manifest.modules };
195
+ const resolved = {
196
+ pack: manifest.archive,
197
+ modules: manifest.modules,
198
+ };
199
+ if (manifest.settling)
200
+ resolved.settling = true;
201
+ return resolved;
196
202
  }
197
203
  const MANIFEST_CACHE_FILE = ".manifest-cache.json";
198
204
  function readCachedManifest(workspaceDir) {
@@ -13,6 +13,7 @@ const cli_1 = require("@antelopejs/core/cli");
13
13
  const config_1 = require("./config");
14
14
  const derived_outputs_1 = require("./derived-outputs");
15
15
  const layers_1 = require("./layers");
16
+ const manifest_secrets_1 = require("./manifest-secrets");
16
17
  const fs_sync_1 = require("./fs-sync");
17
18
  function getPackageRoot() {
18
19
  return (0, node_path_1.resolve)(__dirname, "..");
@@ -98,13 +99,32 @@ function writeAuthEstablishEndpoints(workspaceDir, layers) {
98
99
  function collectPublicEndpoints(layers) {
99
100
  return layers.flatMap((layer) => (Array.isArray(layer.publicEndpoints) ? layer.publicEndpoints : []).filter((endpoint) => typeof endpoint === "object" && endpoint !== null));
100
101
  }
102
+ function collectBackendProxies(layers) {
103
+ return layers.flatMap((layer) => (Array.isArray(layer.backendProxies) ? layer.backendProxies : [])
104
+ .filter((proxy) => typeof proxy === "object" && proxy !== null)
105
+ .map((proxy) => ({
106
+ ...proxy,
107
+ module: layer.name ?? layer.packageName ?? "unknown",
108
+ })));
109
+ }
101
110
  function writePublicEndpoints(workspaceDir, layers) {
102
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspaceDir, config_1.PUBLIC_ENDPOINTS_FILE), `${JSON.stringify({ endpoints: collectPublicEndpoints(layers) }, null, 2)}\n`);
111
+ const declared = {
112
+ endpoints: collectPublicEndpoints(layers),
113
+ proxies: collectBackendProxies(layers),
114
+ };
115
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspaceDir, config_1.PUBLIC_ENDPOINTS_FILE), `${JSON.stringify(declared, null, 2)}\n`);
116
+ }
117
+ function writeServerOptions(workspaceDir, layers) {
118
+ const modules = Object.fromEntries(layers.flatMap((layer) => layer.privateOptions
119
+ ? [[(0, layers_1.getLayerSafeName)(layer), layer.privateOptions]]
120
+ : []));
121
+ (0, config_1.writeSecretBearingFile)((0, node_path_1.join)(workspaceDir, config_1.SERVER_OPTIONS_FILE), `${JSON.stringify({ modules, environment: manifest_secrets_1.MANIFEST_SECRET_ENV }, null, 2)}\n`);
103
122
  }
104
123
  function writeFrontendModuleRegistry(workspaceDir, layers) {
105
124
  const registry = (0, derived_outputs_1.createFrontendModuleRegistry)(workspaceDir, layers);
106
125
  writeAuthEstablishEndpoints(workspaceDir, layers);
107
126
  writePublicEndpoints(workspaceDir, layers);
127
+ writeServerOptions(workspaceDir, layers);
108
128
  for (const output of derived_outputs_1.DERIVED_OUTPUTS)
109
129
  output.write(workspaceDir, registry);
110
130
  }
@@ -0,0 +1,16 @@
1
+ import { type ManifestModule } from "./workspace";
2
+ export declare const MODULE_SET_POLL_INTERVAL_MS = 3000;
3
+ export type ModuleSetMember = Pick<ManifestModule, "name" | "path" | "priority" | "publicEndpoints" | "backendProxies">;
4
+ export type BootstrapSecretSource = () => string | undefined;
5
+ export interface ModuleSetFollowOptions {
6
+ backendUrl: string;
7
+ clientUrl?: string;
8
+ bootstrapSecret?: BootstrapSecretSource;
9
+ fingerprint: string;
10
+ onChange: () => void;
11
+ onUnchanged?: () => void;
12
+ intervalMs?: number;
13
+ }
14
+ export declare function moduleSetFingerprint(modules: readonly ModuleSetMember[]): string;
15
+ export declare function followModuleSet(options: ModuleSetFollowOptions): () => void;
16
+ export declare function reportFrontendReady(backendUrl: string, moduleNames: readonly string[], bootstrapSecret?: string): Promise<boolean>;
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MODULE_SET_POLL_INTERVAL_MS = void 0;
4
+ exports.moduleSetFingerprint = moduleSetFingerprint;
5
+ exports.followModuleSet = followModuleSet;
6
+ exports.reportFrontendReady = reportFrontendReady;
7
+ const manifest_1 = require("./manifest");
8
+ const workspace_1 = require("./workspace");
9
+ exports.MODULE_SET_POLL_INTERVAL_MS = 3000;
10
+ const READY_ENDPOINT = "/dms/frontend/ready";
11
+ const READY_TIMEOUT_MS = 5000;
12
+ const JSON_CONTENT_TYPE = "application/json";
13
+ function moduleSetFingerprint(modules) {
14
+ return JSON.stringify([...modules]
15
+ .sort((left, right) => left.name.localeCompare(right.name))
16
+ .map((module) => [
17
+ module.name,
18
+ module.path ?? "",
19
+ module.priority,
20
+ module.publicEndpoints ?? [],
21
+ module.backendProxies ?? [],
22
+ ]));
23
+ }
24
+ function followModuleSet(options) {
25
+ let isStopped = false;
26
+ let timer;
27
+ let candidate;
28
+ const inspect = (fingerprint) => {
29
+ if (fingerprint === options.fingerprint) {
30
+ candidate = undefined;
31
+ options.onUnchanged?.();
32
+ return;
33
+ }
34
+ if (fingerprint !== candidate) {
35
+ candidate = fingerprint;
36
+ return;
37
+ }
38
+ isStopped = true;
39
+ options.onChange();
40
+ };
41
+ const poll = async () => {
42
+ try {
43
+ const manifest = await (0, manifest_1.fetchManifest)(options.backendUrl, options.clientUrl, options.bootstrapSecret?.());
44
+ if (!isStopped && !manifest.settling) {
45
+ inspect(moduleSetFingerprint(manifest.modules));
46
+ }
47
+ }
48
+ catch {
49
+ }
50
+ if (!isStopped)
51
+ schedule();
52
+ };
53
+ const schedule = () => {
54
+ timer = setTimeout(() => void poll(), options.intervalMs ?? exports.MODULE_SET_POLL_INTERVAL_MS);
55
+ timer.unref();
56
+ };
57
+ schedule();
58
+ return () => {
59
+ isStopped = true;
60
+ if (timer)
61
+ clearTimeout(timer);
62
+ };
63
+ }
64
+ async function reportFrontendReady(backendUrl, moduleNames, bootstrapSecret) {
65
+ try {
66
+ const response = await fetch((0, manifest_1.backendEndpoint)(backendUrl, READY_ENDPOINT), {
67
+ method: "POST",
68
+ headers: {
69
+ ...(0, workspace_1.bootstrapHeaders)(bootstrapSecret),
70
+ "content-type": JSON_CONTENT_TYPE,
71
+ },
72
+ body: JSON.stringify({ modules: moduleNames }),
73
+ signal: AbortSignal.timeout(READY_TIMEOUT_MS),
74
+ });
75
+ return response.ok;
76
+ }
77
+ catch {
78
+ return false;
79
+ }
80
+ }
@@ -30,6 +30,7 @@ export interface RunServerOptions {
30
30
  env: NodeJS.ProcessEnv;
31
31
  mapPath?: PathMapper;
32
32
  onReady: (address: ServerAddress) => void;
33
+ stopSignal?: AbortSignal;
33
34
  }
34
35
  export declare function describeStartError(error: StartError, mapPath?: PathMapper): CliProblem;
35
36
  export declare function describeListenError(error: ListenError): CliProblem;
@@ -22,6 +22,30 @@ const VIRTUAL_INTERFACE_NAME = /^(docker\d|br-|veth|virbr|cni|flannel|cali|ciliu
22
22
  const LINK_LOCAL_IPV4 = /^169\.254\./;
23
23
  const MAX_NETWORK_URLS = 3;
24
24
  const LINUX_NET_DIR = "/sys/class/net";
25
+ function stopOnAbort(child, signal) {
26
+ if (!signal)
27
+ return;
28
+ const stop = () => {
29
+ if (child.pid === undefined || child.exitCode !== null)
30
+ return;
31
+ try {
32
+ if (process.platform === "win32") {
33
+ child.kill();
34
+ }
35
+ else {
36
+ process.kill(-child.pid, "SIGTERM");
37
+ }
38
+ }
39
+ catch {
40
+ }
41
+ };
42
+ if (signal.aborted) {
43
+ stop();
44
+ return;
45
+ }
46
+ signal.addEventListener("abort", stop, { once: true });
47
+ child.once("exit", () => signal.removeEventListener("abort", stop));
48
+ }
25
49
  function isRecord(value) {
26
50
  return typeof value === "object" && value !== null;
27
51
  }
@@ -128,8 +152,9 @@ function describeListenError(error) {
128
152
  reason: error.message,
129
153
  };
130
154
  }
155
+ const LOOPBACK_ADDRESSES = new Set(["127.0.0.1", "::1"]);
131
156
  function urlHost(address) {
132
- if (WILDCARD_ADDRESSES.has(address))
157
+ if (WILDCARD_ADDRESSES.has(address) || LOOPBACK_ADDRESSES.has(address))
133
158
  return LOCAL_HOST;
134
159
  return address.includes(":") ? `[${address}]` : address;
135
160
  }
@@ -188,7 +213,7 @@ function readyLines(address, settings, interfaces, isGuestInterface) {
188
213
  ];
189
214
  }
190
215
  async function runServer(options) {
191
- const { name, script, cwd, env, mapPath, onReady } = options;
216
+ const { name, script, cwd, env, mapPath, onReady, stopSignal } = options;
192
217
  let readyAt;
193
218
  let listenError;
194
219
  let startError;
@@ -199,6 +224,7 @@ async function runServer(options) {
199
224
  shell: false,
200
225
  stdio: ["inherit", "inherit", "inherit", "ipc"],
201
226
  }, (child) => {
227
+ stopOnAbort(child, stopSignal);
202
228
  child.on("message", (message) => {
203
229
  const ready = readReady(message);
204
230
  if (ready && readyAt === undefined) {
@@ -0,0 +1,11 @@
1
+ import { type PathMapper } from "./child-output";
2
+ import { ViteWarnings } from "./vite-output";
3
+ export interface VerificationContext {
4
+ workspace: string;
5
+ env: NodeJS.ProcessEnv;
6
+ mapLine: PathMapper;
7
+ warnings: ViteWarnings;
8
+ isVerbose: boolean;
9
+ }
10
+ export declare function prepareTypes(context: VerificationContext): Promise<void>;
11
+ export declare function buildAndTypeCheck(context: VerificationContext): Promise<void>;