@antelopejs/dms-frontend 0.7.1 → 0.8.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 +4 -4
  2. package/dist/commands/build-action.d.ts +8 -5
  3. package/dist/commands/build-action.js +13 -41
  4. package/dist/commands/build.js +9 -15
  5. package/dist/commands/dev-action.js +5 -1
  6. package/dist/commands/start-action.d.ts +2 -0
  7. package/dist/commands/start-action.js +36 -49
  8. package/dist/commands/start.js +8 -6
  9. package/dist/config.d.ts +2 -0
  10. package/dist/config.js +6 -0
  11. package/dist/derived-outputs.js +2 -2
  12. package/dist/fs-sync.d.ts +1 -0
  13. package/dist/fs-sync.js +6 -0
  14. package/dist/help.js +8 -4
  15. package/dist/index.js +4 -6
  16. package/dist/layer-watch.js +4 -0
  17. package/dist/layers.d.ts +1 -3
  18. package/dist/layers.js +1 -95
  19. package/dist/manifest.d.ts +8 -8
  20. package/dist/manifest.js +15 -39
  21. package/dist/materialize.js +6 -0
  22. package/dist/module-set.d.ts +14 -12
  23. package/dist/module-set.js +39 -31
  24. package/dist/private-values.d.ts +2 -2
  25. package/dist/private-values.js +1 -6
  26. package/dist/project-modules.d.ts +46 -0
  27. package/dist/project-modules.js +233 -0
  28. package/dist/workspace-setup.d.ts +3 -7
  29. package/dist/workspace-setup.js +41 -62
  30. package/dist/workspace.d.ts +2 -4
  31. package/dist/workspace.js +1 -1
  32. package/package.json +5 -7
  33. package/skills/dms-frontend/SKILL.md +2 -2
  34. package/templates/vue/client-chunking.mjs +61 -0
  35. package/templates/vue/email-renderer.ts +6 -1
  36. package/templates/vue/frontend-build-loader.ts +1 -1
  37. package/templates/vue/frontend-module.ts +24 -0
  38. package/templates/vue/main.ts +11 -2
  39. package/templates/vue/package.json +2 -2
  40. package/templates/vue/prepare-types.mjs +2 -7
  41. package/templates/vue/public-options.ts +19 -0
  42. package/templates/vue/server/auth/backend.mjs +20 -3
  43. package/templates/vue/server/auth/client-ip.mjs +64 -22
  44. package/templates/vue/server/auth/session.mjs +4 -11
  45. package/templates/vue/server/backend-proxies.mjs +13 -2
  46. package/templates/vue/server/backend-response.mjs +2 -13
  47. package/templates/vue/server/cli-report.mjs +42 -18
  48. package/templates/vue/server/client-manifest.mjs +22 -58
  49. package/templates/vue/server/dev-private-files.mjs +1 -1
  50. package/templates/vue/server/dev-styles.mjs +16 -5
  51. package/templates/vue/server/homepage.mjs +4 -1
  52. package/templates/vue/server/module-event.mjs +23 -8
  53. package/templates/vue/server/module-server.mjs +4 -4
  54. package/templates/vue/server/module-testing.mjs +5 -3
  55. package/templates/vue/server/proxy-compression.mjs +15 -8
  56. package/templates/vue/server/proxy-headers.mjs +12 -1
  57. package/templates/vue/server/proxy-redirects.mjs +66 -0
  58. package/templates/vue/server/proxy-transfers.mjs +84 -0
  59. package/templates/vue/server/public-endpoints.mjs +2 -2
  60. package/templates/vue/server/public-options.mjs +49 -0
  61. package/templates/vue/server/request-routing.mjs +20 -0
  62. package/templates/vue/server/web-body.mjs +24 -0
  63. package/templates/vue/server-module.ts +19 -7
  64. package/templates/vue/server.mjs +59 -49
  65. package/templates/vue/ssr-renderer.ts +19 -1
  66. package/templates/vue/vite.config.ts +72 -46
  67. package/templates/vue/vite.email.config.ts +6 -3
  68. package/templates/vue/vite.server.config.ts +5 -2
  69. package/dist/commands/snapshot-action.d.ts +0 -6
  70. package/dist/commands/snapshot-action.js +0 -53
  71. package/dist/commands/snapshot.d.ts +0 -2
  72. package/dist/commands/snapshot.js +0 -55
  73. package/dist/snapshot.d.ts +0 -25
  74. package/dist/snapshot.js +0 -90
  75. package/templates/vue/server/backend-wake.mjs +0 -129
package/dist/layers.js CHANGED
@@ -1,57 +1,17 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.assertLayerPathsServed = assertLayerPathsServed;
37
- exports.assertCachedArchivesExist = assertCachedArchivesExist;
38
4
  exports.readLayerPackage = readLayerPackage;
39
5
  exports.buildLayersFromPaths = buildLayersFromPaths;
40
- exports.downloadAndExtractLayers = downloadAndExtractLayers;
41
- exports.buildLayersFromCache = buildLayersFromCache;
42
6
  exports.getLayerSafeName = getLayerSafeName;
43
7
  exports.getLayerWorkspacePath = getLayerWorkspacePath;
44
8
  const node_fs_1 = require("node:fs");
45
- const promises_1 = require("node:fs/promises");
46
9
  const node_path_1 = require("node:path");
47
- const node_stream_1 = require("node:stream");
48
- const consumers_1 = require("node:stream/consumers");
49
10
  const cli_1 = require("@antelopejs/core/cli");
50
11
  const manifest_1 = require("./manifest");
51
- const fs_sync_1 = require("./fs-sync");
52
12
  const config_1 = require("./config");
53
13
  const output_1 = require("./output");
54
- const BUILD_FIX = "A remote or production backend never sends them: use `ajs dms build` against it";
14
+ const BUILD_FIX = "A remote or production backend never sends them: build the frontend from the project instead, with `ajs dms build`";
55
15
  function assertLayerPathsServed(modules, backendUrl, { bootstrapSecret, cache } = {}) {
56
16
  const pathless = modules.filter((mod) => !mod.path);
57
17
  if (pathless.length === 0)
@@ -87,17 +47,6 @@ function assertLayerPathsServed(modules, backendUrl, { bootstrapSecret, cache }
87
47
  fixes: [fix, BUILD_FIX],
88
48
  });
89
49
  }
90
- function assertCachedArchivesExist(modules, cacheDir, fetchedAt) {
91
- const present = new Set((0, node_fs_1.readdirSync)(cacheDir));
92
- const missing = modules.filter((mod) => !present.has(mod.archiveName));
93
- if (missing.length === 0)
94
- return;
95
- throw new cli_1.CliError({
96
- title: `The cached layers archive is missing entries listed in the manifest${fetchedAt ? ` (fetched ${(0, output_1.formatAge)(fetchedAt)})` : ""}`,
97
- fixes: ["Run once with the backend reachable to download the layers again"],
98
- details: missing.map((mod) => `${mod.name}: ${mod.archiveName}`),
99
- });
100
- }
101
50
  function sortModulesByPriority(modules) {
102
51
  return [...modules].sort((a, b) => b.priority - a.priority);
103
52
  }
@@ -134,49 +83,6 @@ function resolveLayer(layerPath, mod) {
134
83
  function buildLayersFromPaths(modules) {
135
84
  return sortModulesByPriority(modules).map((mod) => resolveLayer(mod.path, mod));
136
85
  }
137
- async function downloadAndExtractLayers(backendUrl, packUrl, cacheDir, force, bootstrapSecret) {
138
- const url = (0, manifest_1.backendEndpoint)(backendUrl, packUrl);
139
- const response = await (0, manifest_1.fetchFromBackend)(backendUrl, url, bootstrapSecret);
140
- if (!response.ok) {
141
- throw new manifest_1.BackendResponseError("the layers archive", url, response);
142
- }
143
- if (!response.body) {
144
- throw new Error(`The layers archive from ${(0, manifest_1.displayUrl)(url)} came without a body`);
145
- }
146
- const modulesBlob = await (0, consumers_1.buffer)(node_stream_1.Readable.fromWeb(response.body));
147
- const { Open } = await Promise.resolve().then(() => __importStar(require("unzipper")));
148
- const cacheExists = (0, node_fs_1.existsSync)(cacheDir);
149
- if (force || !cacheExists) {
150
- if (cacheExists) {
151
- (0, node_fs_1.rmSync)(cacheDir, { recursive: true, force: true });
152
- }
153
- await (await Open.buffer(modulesBlob)).extract({ path: cacheDir });
154
- return;
155
- }
156
- const tempDir = `${cacheDir}_temp`;
157
- if ((0, node_fs_1.existsSync)(tempDir)) {
158
- (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
159
- }
160
- await (await Open.buffer(modulesBlob)).extract({ path: tempDir });
161
- const tempEntries = (0, node_fs_1.readdirSync)(tempDir);
162
- for (const entry of tempEntries) {
163
- (0, fs_sync_1.syncDirectories)((0, node_path_1.join)(tempDir, entry), (0, node_path_1.join)(cacheDir, entry));
164
- }
165
- (0, node_fs_1.rmSync)(tempDir, { recursive: true, force: true });
166
- }
167
- async function buildLayersFromCache(modules, cacheDir) {
168
- if (!(0, node_fs_1.existsSync)(cacheDir))
169
- return [];
170
- const layerFiles = await (0, promises_1.readdir)(cacheDir);
171
- const layers = [];
172
- for (const mod of sortModulesByPriority(modules)) {
173
- if (!layerFiles.includes(mod.archiveName))
174
- continue;
175
- const layerPath = (0, node_path_1.join)(cacheDir, mod.archiveName);
176
- layers.push(resolveLayer(layerPath, mod));
177
- }
178
- return layers;
179
- }
180
86
  function getLayerSafeName(layer) {
181
87
  if (layer.packageName) {
182
88
  return layer.packageName.replace(/^@/, "").replace(/\//g, "__");
@@ -18,6 +18,7 @@ export declare class ManifestRefusedError extends CliError {
18
18
  }
19
19
  export declare function describeManifestVersion(version: unknown): CliProblem;
20
20
  export declare class BackendResponseError extends CliError {
21
+ readonly status: number;
21
22
  constructor(subject: string, url: string | URL, response: Pick<Response, "status" | "statusText">);
22
23
  }
23
24
  export declare class NoCachedManifestError extends CliError {
@@ -29,19 +30,18 @@ export interface ManifestCacheEntry {
29
30
  fetchedAt: string;
30
31
  }
31
32
  export declare function readCachedManifest(workspaceDir: string): ManifestCacheEntry | undefined;
32
- export declare function writeCachedManifest(workspaceDir: string, manifest: Manifest, privateValues: PrivateValues, fetchedAt?: string): void;
33
- export type PrivateValues = "keep" | "drop";
34
- export type ManifestCacheUse = "fallback" | "offline" | "never";
33
+ export declare function writeCachedManifest(workspaceDir: string, manifest: Manifest, fetchedAt?: string): void;
34
+ export type ManifestCacheUse = "fallback" | "offline";
35
+ export interface ManifestRequest {
36
+ clientUrl?: string;
37
+ bootstrapSecret?: string;
38
+ }
35
39
  export interface ResolvedManifest {
36
40
  manifest: Manifest;
37
41
  fromCache: boolean;
38
42
  fetchedAt?: string;
39
43
  }
40
- export declare function resolveManifest(workspaceDir: string, backendUrl: string, cacheUse: ManifestCacheUse, { clientUrl, bootstrapSecret, privateValues, }?: {
41
- clientUrl?: string;
42
- bootstrapSecret?: string;
43
- privateValues?: PrivateValues;
44
- }): Promise<ResolvedManifest>;
44
+ export declare function resolveManifest(workspaceDir: string, backendUrl: string, cacheUse: ManifestCacheUse, { clientUrl, bootstrapSecret }?: ManifestRequest): Promise<ResolvedManifest>;
45
45
  export declare function assertCachedLayerPathsExist(modules: ServedWithPath[], fetchedAt?: string): void;
46
46
  export type ServedWithPath = ManifestModule & {
47
47
  path: string;
package/dist/manifest.js CHANGED
@@ -14,7 +14,6 @@ const node_fs_1 = require("node:fs");
14
14
  const node_path_1 = require("node:path");
15
15
  const cli_1 = require("@antelopejs/core/cli");
16
16
  const config_1 = require("./config");
17
- const private_values_1 = require("./private-values");
18
17
  const output_1 = require("./output");
19
18
  const workspace_1 = require("./workspace");
20
19
  class ManifestUnauthorizedError extends cli_1.CliError {
@@ -162,6 +161,7 @@ class BackendResponseError extends cli_1.CliError {
162
161
  };
163
162
  super(problem);
164
163
  this.name = "BackendResponseError";
164
+ this.status = status;
165
165
  }
166
166
  }
167
167
  exports.BackendResponseError = BackendResponseError;
@@ -194,10 +194,7 @@ async function fetchManifest(backendUrl, clientUrl, bootstrapSecret) {
194
194
  if (incompatible.length) {
195
195
  throw new ManifestRefusedError(`Frontend manifest contains incompatible renderer modules: ${incompatible.map((module) => module.name).join(", ")}`);
196
196
  }
197
- const resolved = {
198
- pack: manifest.archive,
199
- modules: manifest.modules,
200
- };
197
+ const resolved = { modules: manifest.modules };
201
198
  if (manifest.settling)
202
199
  resolved.settling = true;
203
200
  return resolved;
@@ -217,19 +214,15 @@ function readCachedManifest(workspaceDir) {
217
214
  return undefined;
218
215
  }
219
216
  }
220
- function writeCachedManifest(workspaceDir, manifest, privateValues, fetchedAt = new Date().toISOString()) {
217
+ function writeCachedManifest(workspaceDir, manifest, fetchedAt = new Date().toISOString()) {
221
218
  const entry = { manifest, fetchedAt };
222
- const write = privateValues === "keep" ? config_1.writeSecretBearingFile : config_1.writeShareableFile;
223
- write((0, node_path_1.join)(workspaceDir, MANIFEST_CACHE_FILE), `${JSON.stringify(entry, null, 2)}\n`);
219
+ (0, config_1.writeSecretBearingFile)((0, node_path_1.join)(workspaceDir, MANIFEST_CACHE_FILE), `${JSON.stringify(entry, null, 2)}\n`);
224
220
  }
225
- async function resolveManifest(workspaceDir, backendUrl, cacheUse, { clientUrl, bootstrapSecret, privateValues = "keep", } = {}) {
221
+ async function resolveManifest(workspaceDir, backendUrl, cacheUse, { clientUrl, bootstrapSecret } = {}) {
226
222
  if (cacheUse !== "offline") {
227
223
  try {
228
- const served = await fetchManifest(backendUrl, clientUrl, bootstrapSecret);
229
- const manifest = privateValues === "keep"
230
- ? served
231
- : { ...served, modules: (0, private_values_1.withoutPrivateValues)(served.modules) };
232
- writeCachedManifest(workspaceDir, manifest, privateValues);
224
+ const manifest = await fetchManifest(backendUrl, clientUrl, bootstrapSecret);
225
+ writeCachedManifest(workspaceDir, manifest);
233
226
  return { manifest, fromCache: false };
234
227
  }
235
228
  catch (err) {
@@ -240,37 +233,20 @@ async function resolveManifest(workspaceDir, backendUrl, cacheUse, { clientUrl,
240
233
  const cached = readCachedManifest(workspaceDir);
241
234
  if (!cached)
242
235
  throw err;
243
- if (cacheUse === "never")
244
- throw cacheNotUsed(err, backendUrl, cached);
245
- return fromCachedEntry(workspaceDir, cached, privateValues);
236
+ return fromCachedEntry(cached);
246
237
  }
247
238
  }
248
239
  const cached = readCachedManifest(workspaceDir);
249
240
  if (!cached)
250
241
  throw new NoCachedManifestError();
251
- return fromCachedEntry(workspaceDir, cached, privateValues);
252
- }
253
- function cacheNotUsed(failure, backendUrl, cached) {
254
- const problem = failure instanceof cli_1.CliError
255
- ? failure.problem
256
- : { title: errorMessage(failure) };
257
- const cache = `A manifest${(0, output_1.cachedAge)(cached.fetchedAt)} exists; a build uses it only with --offline.`;
258
- return new cli_1.CliError({
259
- ...problem,
260
- reason: problem.reason ? `${problem.reason} ${cache}` : cache,
261
- fixes: [
262
- ...(problem.fixes ?? []),
263
- `Or build from the cache on purpose: ajs dms build -b ${displayUrl(backendUrl)} --offline`,
264
- ],
265
- }, { cause: failure });
242
+ return fromCachedEntry(cached);
266
243
  }
267
- function fromCachedEntry(workspaceDir, cached, privateValues) {
268
- let { manifest } = cached;
269
- if (privateValues === "drop") {
270
- manifest = { ...manifest, modules: (0, private_values_1.withoutPrivateValues)(manifest.modules) };
271
- writeCachedManifest(workspaceDir, manifest, "drop", cached.fetchedAt);
272
- }
273
- return { manifest, fromCache: true, fetchedAt: cached.fetchedAt };
244
+ function fromCachedEntry(cached) {
245
+ return {
246
+ manifest: cached.manifest,
247
+ fromCache: true,
248
+ fetchedAt: cached.fetchedAt,
249
+ };
274
250
  }
275
251
  function assertCachedLayerPathsExist(modules, fetchedAt) {
276
252
  const missing = modules.filter((mod) => !(0, node_fs_1.existsSync)(mod.path));
@@ -70,6 +70,12 @@ function copyLayerSource(src, dest) {
70
70
  filter: (srcPath) => !(0, fs_sync_1.isBlocklistedCopyPath)(src, srcPath),
71
71
  });
72
72
  sanitizeLayerPackage((0, node_path_1.join)(dest, "package.json"));
73
+ sanitizeBuildDeclaration((0, node_path_1.join)(dest, config_1.FRONTEND_BUILD_ENTRY));
74
+ }
75
+ function sanitizeBuildDeclaration(file) {
76
+ if (!(0, node_fs_1.existsSync)(file))
77
+ return;
78
+ (0, fs_sync_1.applyContent)((0, fs_sync_1.sanitizedBuildDeclaration)((0, node_fs_1.readFileSync)(file, "utf-8")), file);
73
79
  }
74
80
  function sanitizeLayerPackage(pkgPath) {
75
81
  if (!(0, node_fs_1.existsSync)(pkgPath))
@@ -1,5 +1,8 @@
1
1
  import { type ManifestModule } from "./workspace";
2
2
  export declare const MODULE_SET_POLL_INTERVAL_MS = 3000;
3
+ export declare const OWN_RELEASE: {
4
+ version: string;
5
+ };
3
6
  export type ModuleSetMember = Pick<ManifestModule, "name" | "path" | "priority" | "publicEndpoints" | "backendProxies">;
4
7
  export type BootstrapSecretSource = () => string | undefined;
5
8
  export interface ModuleSetFollowOptions {
@@ -13,15 +16,14 @@ export interface ModuleSetFollowOptions {
13
16
  }
14
17
  export declare function moduleSetFingerprint(modules: readonly ModuleSetMember[]): string;
15
18
  export declare function followModuleSet(options: ModuleSetFollowOptions): () => void;
16
- export declare function reportFrontendReady(backendUrl: string, moduleNames: readonly string[], bootstrapSecret?: string): Promise<boolean>;
17
- export type BuildDrift = {
18
- status: "matches";
19
- served: string;
20
- } | {
21
- status: "drifted";
22
- served: string;
23
- } | {
24
- status: "unknown";
25
- reason: string;
26
- };
27
- export declare function compareWithBackend(backendUrl: string, builtDigest: string, digestOf: (modules: readonly ManifestModule[]) => string, bootstrapSecret?: string): Promise<BuildDrift>;
19
+ export interface ServedModule {
20
+ name: string;
21
+ version?: string;
22
+ }
23
+ export interface ReadyRetry {
24
+ firstDelayMs: number;
25
+ maxDelayMs: number;
26
+ timeoutMs: number;
27
+ }
28
+ export declare function reportFrontendReady(backendUrl: string, modules: readonly ServedModule[], bootstrapSecret?: string): Promise<boolean>;
29
+ export declare function reportFrontendReadyOnceAnswered(backendUrl: string, modules: readonly ServedModule[], bootstrapSecret?: string, retry?: ReadyRetry): Promise<boolean>;
@@ -1,17 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MODULE_SET_POLL_INTERVAL_MS = void 0;
3
+ exports.OWN_RELEASE = exports.MODULE_SET_POLL_INTERVAL_MS = void 0;
4
4
  exports.moduleSetFingerprint = moduleSetFingerprint;
5
5
  exports.followModuleSet = followModuleSet;
6
6
  exports.reportFrontendReady = reportFrontendReady;
7
- exports.compareWithBackend = compareWithBackend;
7
+ exports.reportFrontendReadyOnceAnswered = reportFrontendReadyOnceAnswered;
8
+ const promises_1 = require("node:timers/promises");
8
9
  const manifest_1 = require("./manifest");
9
10
  const workspace_1 = require("./workspace");
10
11
  exports.MODULE_SET_POLL_INTERVAL_MS = 3000;
11
12
  const READY_ENDPOINT = "/dms/frontend/ready";
12
13
  const READY_TIMEOUT_MS = 5000;
13
14
  const JSON_CONTENT_TYPE = "application/json";
14
- const OWN_RELEASE = require("../package.json");
15
+ exports.OWN_RELEASE = require("../package.json");
15
16
  function moduleSetFingerprint(modules) {
16
17
  return JSON.stringify([...modules]
17
18
  .sort((left, right) => left.name.localeCompare(right.name))
@@ -63,40 +64,47 @@ function followModuleSet(options) {
63
64
  clearTimeout(timer);
64
65
  };
65
66
  }
66
- async function reportFrontendReady(backendUrl, moduleNames, bootstrapSecret) {
67
+ const READY_WAKE_TIMEOUT_MS = 90_000;
68
+ const READY_FIRST_RETRY_MS = 2_000;
69
+ const READY_MAX_RETRY_MS = 60_000;
70
+ const SERVER_ERROR = 500;
71
+ const DEFAULT_READY_RETRY = {
72
+ firstDelayMs: READY_FIRST_RETRY_MS,
73
+ maxDelayMs: READY_MAX_RETRY_MS,
74
+ timeoutMs: READY_WAKE_TIMEOUT_MS,
75
+ };
76
+ async function postReadyReport(backendUrl, modules, bootstrapSecret, timeoutMs) {
77
+ const response = await fetch((0, manifest_1.backendEndpoint)(backendUrl, READY_ENDPOINT), {
78
+ method: "POST",
79
+ headers: {
80
+ ...(0, workspace_1.bootstrapHeaders)(bootstrapSecret),
81
+ "content-type": JSON_CONTENT_TYPE,
82
+ },
83
+ body: JSON.stringify({ modules, version: exports.OWN_RELEASE.version }),
84
+ signal: AbortSignal.timeout(timeoutMs),
85
+ });
86
+ await response.body?.cancel();
87
+ return response;
88
+ }
89
+ async function reportFrontendReady(backendUrl, modules, bootstrapSecret) {
67
90
  try {
68
- const response = await fetch((0, manifest_1.backendEndpoint)(backendUrl, READY_ENDPOINT), {
69
- method: "POST",
70
- headers: {
71
- ...(0, workspace_1.bootstrapHeaders)(bootstrapSecret),
72
- "content-type": JSON_CONTENT_TYPE,
73
- },
74
- body: JSON.stringify({
75
- modules: moduleNames,
76
- version: OWN_RELEASE.version,
77
- }),
78
- signal: AbortSignal.timeout(READY_TIMEOUT_MS),
79
- });
91
+ const response = await postReadyReport(backendUrl, modules, bootstrapSecret, READY_TIMEOUT_MS);
80
92
  return response.ok;
81
93
  }
82
94
  catch {
83
95
  return false;
84
96
  }
85
97
  }
86
- const DRIFT_CHECK_TIMEOUT_MS = 10_000;
87
- async function compareWithBackend(backendUrl, builtDigest, digestOf, bootstrapSecret) {
88
- try {
89
- const manifest = await Promise.race([
90
- (0, manifest_1.fetchManifest)(backendUrl, undefined, bootstrapSecret),
91
- new Promise((_resolve, reject) => setTimeout(() => reject(new Error("the backend did not answer in time")), DRIFT_CHECK_TIMEOUT_MS).unref()),
92
- ]);
93
- const served = digestOf(manifest.modules);
94
- return { status: served === builtDigest ? "matches" : "drifted", served };
95
- }
96
- catch (error) {
97
- return {
98
- status: "unknown",
99
- reason: error instanceof Error ? error.message : String(error),
100
- };
98
+ async function reportFrontendReadyOnceAnswered(backendUrl, modules, bootstrapSecret, retry = DEFAULT_READY_RETRY) {
99
+ for (let delayMs = retry.firstDelayMs;;) {
100
+ try {
101
+ const response = await postReadyReport(backendUrl, modules, bootstrapSecret, retry.timeoutMs);
102
+ if (response.status < SERVER_ERROR)
103
+ return response.ok;
104
+ }
105
+ catch {
106
+ }
107
+ await (0, promises_1.setTimeout)(delayMs, undefined, { ref: false });
108
+ delayMs = Math.min(retry.maxDelayMs, delayMs * 2);
101
109
  }
102
110
  }
@@ -1,5 +1,5 @@
1
- import type { FrontendModuleOptions, ManifestModule } from "./workspace";
2
- export declare function withoutPrivateValues(modules: readonly ManifestModule[]): ManifestModule[];
1
+ import type { FrontendModuleOptions } from "./workspace";
2
+ export declare function emptiedOptions(options: FrontendModuleOptions): FrontendModuleOptions;
3
3
  export declare const PRIVATE_OPTIONS_VARIABLE = "DMS_PRIVATE_OPTIONS";
4
4
  export interface UnfilledPrivateValue {
5
5
  module: string;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.PRIVATE_OPTIONS_VARIABLE = void 0;
4
- exports.withoutPrivateValues = withoutPrivateValues;
4
+ exports.emptiedOptions = emptiedOptions;
5
5
  exports.parsePrivateOptions = parsePrivateOptions;
6
6
  exports.unfilledPrivateValues = unfilledPrivateValues;
7
7
  const config_1 = require("./config");
@@ -17,11 +17,6 @@ function emptied(value) {
17
17
  function emptiedOptions(options) {
18
18
  return Object.fromEntries(Object.entries(options).map(([key, value]) => [key, emptied(value)]));
19
19
  }
20
- function withoutPrivateValues(modules) {
21
- return modules.map((module) => module.privateOptions
22
- ? { ...module, privateOptions: emptiedOptions(module.privateOptions) }
23
- : { ...module });
24
- }
25
20
  exports.PRIVATE_OPTIONS_VARIABLE = "DMS_PRIVATE_OPTIONS";
26
21
  function isOptions(value) {
27
22
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -0,0 +1,46 @@
1
+ import type { BackendProxyDeclaration, FrontendModuleOptions, PublicEndpointDeclaration, ResolvedLayer } from "./workspace";
2
+ export declare const FRONTEND_DECLARATION_FIELD = "dmsFrontend";
3
+ export interface FrontendRegistration {
4
+ name: string;
5
+ sourcePath: string;
6
+ renderer: {
7
+ name: string;
8
+ version: string;
9
+ };
10
+ priority?: number;
11
+ configKey?: string;
12
+ options?: FrontendModuleOptions;
13
+ privateOptions?: FrontendModuleOptions;
14
+ authEstablishEndpoints?: string[];
15
+ publicEndpoints?: PublicEndpointDeclaration[];
16
+ backendProxies?: BackendProxyDeclaration[];
17
+ }
18
+ export interface FrontendModuleContext {
19
+ dev: boolean;
20
+ }
21
+ interface ModuleSource {
22
+ type: string;
23
+ path?: string;
24
+ package?: string;
25
+ }
26
+ interface ProjectModule {
27
+ source: ModuleSource;
28
+ config: unknown;
29
+ }
30
+ interface ProjectConfig {
31
+ cacheFolder: string;
32
+ modules: Record<string, ProjectModule>;
33
+ }
34
+ export interface ProjectFrontendModule {
35
+ layer: ResolvedLayer;
36
+ version?: string;
37
+ }
38
+ export interface ProjectModulesOptions {
39
+ projectDir: string;
40
+ environment: string;
41
+ dev?: boolean;
42
+ }
43
+ export declare function loadProjectConfig(projectDir: string, environment: string): Promise<ProjectConfig>;
44
+ export declare function readShippingVersion(sourcePath: string): string | undefined;
45
+ export declare function readProjectFrontendModules(options: ProjectModulesOptions): Promise<ProjectFrontendModule[]>;
46
+ export {};