@antelopejs/dms-frontend 0.7.1 → 0.9.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 (98) 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 +16 -41
  4. package/dist/commands/build.js +9 -15
  5. package/dist/commands/dev-action.d.ts +2 -0
  6. package/dist/commands/dev-action.js +14 -3
  7. package/dist/commands/dev.js +3 -1
  8. package/dist/commands/start-action.d.ts +2 -0
  9. package/dist/commands/start-action.js +65 -47
  10. package/dist/commands/start.js +8 -6
  11. package/dist/common.d.ts +1 -0
  12. package/dist/common.js +1 -0
  13. package/dist/config.d.ts +2 -0
  14. package/dist/config.js +9 -0
  15. package/dist/derived-outputs.d.ts +1 -5
  16. package/dist/derived-outputs.js +48 -35
  17. package/dist/dev-modules.d.ts +5 -0
  18. package/dist/dev-modules.js +88 -0
  19. package/dist/entry-weight.d.ts +24 -0
  20. package/dist/entry-weight.js +86 -0
  21. package/dist/fs-sync.d.ts +1 -0
  22. package/dist/fs-sync.js +6 -0
  23. package/dist/help.js +12 -4
  24. package/dist/index.js +4 -6
  25. package/dist/layer-watch.js +7 -2
  26. package/dist/layers.d.ts +2 -3
  27. package/dist/layers.js +1 -95
  28. package/dist/locale-files.d.ts +6 -0
  29. package/dist/locale-files.js +75 -0
  30. package/dist/manifest.d.ts +8 -8
  31. package/dist/manifest.js +17 -39
  32. package/dist/materialize.js +6 -0
  33. package/dist/module-set.d.ts +14 -12
  34. package/dist/module-set.js +39 -31
  35. package/dist/private-values.d.ts +3 -2
  36. package/dist/private-values.js +16 -5
  37. package/dist/project-modules-child.d.ts +13 -0
  38. package/dist/project-modules-child.js +26 -0
  39. package/dist/project-modules.d.ts +47 -0
  40. package/dist/project-modules.js +268 -0
  41. package/dist/workspace-setup.d.ts +3 -7
  42. package/dist/workspace-setup.js +79 -62
  43. package/dist/workspace.d.ts +8 -4
  44. package/dist/workspace.js +1 -1
  45. package/package.json +13 -8
  46. package/skills/dms-frontend/SKILL.md +2 -2
  47. package/templates/vue/app-runtime.ts +8 -1
  48. package/templates/vue/client-chunking.mjs +61 -0
  49. package/templates/vue/email-renderer.ts +6 -1
  50. package/templates/vue/entry-weight.mjs +132 -0
  51. package/templates/vue/frontend-build-loader.ts +1 -1
  52. package/templates/vue/frontend-module.ts +168 -1
  53. package/templates/vue/gitignore +1 -0
  54. package/templates/vue/main.ts +32 -2
  55. package/templates/vue/network-error.ts +3 -1
  56. package/templates/vue/package.json +3 -3
  57. package/templates/vue/patches/@vueuse__core@14.4.0.patch +24 -0
  58. package/templates/vue/pnpm-workspace.yaml +5 -0
  59. package/templates/vue/prepare-types.mjs +2 -7
  60. package/templates/vue/public-options.ts +19 -0
  61. package/templates/vue/server/auth/backend.mjs +20 -3
  62. package/templates/vue/server/auth/client-ip.mjs +64 -22
  63. package/templates/vue/server/auth/session.mjs +4 -11
  64. package/templates/vue/server/backend-proxies.mjs +13 -2
  65. package/templates/vue/server/backend-response.mjs +2 -13
  66. package/templates/vue/server/cli-report.mjs +42 -18
  67. package/templates/vue/server/client-manifest.mjs +34 -58
  68. package/templates/vue/server/dev-private-files.mjs +1 -1
  69. package/templates/vue/server/dev-styles.mjs +16 -5
  70. package/templates/vue/server/homepage.mjs +4 -1
  71. package/templates/vue/server/in-place-render.mjs +110 -0
  72. package/templates/vue/server/module-event.mjs +23 -8
  73. package/templates/vue/server/module-server.mjs +4 -4
  74. package/templates/vue/server/module-testing.mjs +5 -3
  75. package/templates/vue/server/page-props.mjs +10 -0
  76. package/templates/vue/server/proxy-compression.mjs +15 -8
  77. package/templates/vue/server/proxy-headers.mjs +12 -1
  78. package/templates/vue/server/proxy-redirects.mjs +66 -0
  79. package/templates/vue/server/proxy-transfers.mjs +110 -0
  80. package/templates/vue/server/public-endpoints.mjs +2 -2
  81. package/templates/vue/server/public-options.mjs +49 -0
  82. package/templates/vue/server/request-routing.mjs +20 -0
  83. package/templates/vue/server/web-body.mjs +24 -0
  84. package/templates/vue/server-module.ts +19 -7
  85. package/templates/vue/server.mjs +98 -78
  86. package/templates/vue/sfc-global-types.mjs +119 -0
  87. package/templates/vue/ssr-renderer.ts +27 -1
  88. package/templates/vue/ui-kbd.mjs +45 -0
  89. package/templates/vue/vite.config.ts +104 -47
  90. package/templates/vue/vite.email.config.ts +6 -3
  91. package/templates/vue/vite.server.config.ts +5 -2
  92. package/dist/commands/snapshot-action.d.ts +0 -6
  93. package/dist/commands/snapshot-action.js +0 -53
  94. package/dist/commands/snapshot.d.ts +0 -2
  95. package/dist/commands/snapshot.js +0 -55
  96. package/dist/snapshot.d.ts +0 -25
  97. package/dist/snapshot.js +0 -90
  98. package/templates/vue/server/backend-wake.mjs +0 -129
@@ -0,0 +1,268 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FRONTEND_DECLARATION_FIELD = void 0;
37
+ exports.loadProjectConfig = loadProjectConfig;
38
+ exports.readShippingVersion = readShippingVersion;
39
+ exports.readProjectFrontendModules = readProjectFrontendModules;
40
+ const node_fs_1 = require("node:fs");
41
+ const fsPromises = __importStar(require("node:fs/promises"));
42
+ const node_path_1 = require("node:path");
43
+ const cli_1 = require("@antelopejs/core/cli");
44
+ const layers_1 = require("./layers");
45
+ const private_values_1 = require("./private-values");
46
+ exports.FRONTEND_DECLARATION_FIELD = "dmsFrontend";
47
+ const FRONTEND_SOURCE_DIR = "frontend-vue";
48
+ const RENDERER = { name: "vue", version: "3" };
49
+ const CONFIG_VAR_PATTERN = /\$\{@([^.}]+)\.([^}]+)\}/g;
50
+ const PURE_CONFIG_VAR = /^\$\{@([^.}]+)\.([^}]+)\}$/;
51
+ const MODULE_MANIFEST_FILE = "antelope.module.json";
52
+ const NODE_FILE_SYSTEM = {
53
+ readFile: (path) => fsPromises.readFile(path),
54
+ readFileString: (path, encoding = "utf-8") => fsPromises.readFile(path, encoding),
55
+ writeFile: (path, data) => fsPromises.writeFile(path, data),
56
+ readdir: (path) => fsPromises.readdir(path),
57
+ stat: (path) => fsPromises.stat(path),
58
+ mkdir: async (path, options) => {
59
+ await fsPromises.mkdir(path, options);
60
+ },
61
+ rm: (path, options) => fsPromises.rm(path, options),
62
+ exists: async (path) => (0, node_fs_1.existsSync)(path),
63
+ access: (path) => fsPromises.access(path),
64
+ copyFile: (src, dest) => fsPromises.copyFile(src, dest),
65
+ rename: (oldPath, newPath) => fsPromises.rename(oldPath, newPath),
66
+ };
67
+ function readAntelopeManifest(folder) {
68
+ const dedicated = (0, node_path_1.join)(folder, MODULE_MANIFEST_FILE);
69
+ if ((0, node_fs_1.existsSync)(dedicated))
70
+ return JSON.parse((0, node_fs_1.readFileSync)(dedicated, "utf-8"));
71
+ return (0, layers_1.readLayerPackage)(folder)?.antelopeJs;
72
+ }
73
+ function warnUndeclaredConfigVars(id) {
74
+ (0, cli_1.getProcessUi)().message("warn", `${id} declares no antelopeJs.configVarsFile in its package.json: the \${@${id}.<VAR>} references are left unresolved`, { detail: `Upgrade ${id} to a version that declares it` });
75
+ }
76
+ function configVarsProvider(id, folder) {
77
+ const manifest = readAntelopeManifest(folder);
78
+ const file = manifest?.configVarsFile;
79
+ if (typeof file !== "string") {
80
+ warnUndeclaredConfigVars(id);
81
+ return undefined;
82
+ }
83
+ const { configVars } = require((0, node_path_1.join)(folder, file));
84
+ if (typeof configVars !== "function") {
85
+ throw new cli_1.CliError({
86
+ title: `${id}: ${file} exports no configVars function`,
87
+ fixes: [`Export configVars(config) from ${file}`],
88
+ });
89
+ }
90
+ const declared = Array.isArray(manifest?.configVars)
91
+ ? manifest.configVars.filter((name) => typeof name === "string")
92
+ : [];
93
+ return { declared, configVars };
94
+ }
95
+ function computeConfigVars(id, provider, config) {
96
+ let computed;
97
+ try {
98
+ computed = provider.configVars(config) ?? {};
99
+ }
100
+ catch (error) {
101
+ throw new cli_1.CliError({
102
+ title: `${id}: could not compute its config variables`,
103
+ reason: error instanceof Error ? error.message : String(error),
104
+ }, { cause: error });
105
+ }
106
+ return Object.fromEntries(provider.declared
107
+ .filter((name) => name in computed)
108
+ .map((name) => [name, computed[name]]));
109
+ }
110
+ function configVarLookup(modules) {
111
+ const published = new Map();
112
+ const lookup = (id, name) => {
113
+ if (!published.has(id)) {
114
+ published.set(id, undefined);
115
+ const module = modules[id];
116
+ const provider = module?.folder && configVarsProvider(id, module.folder);
117
+ if (provider)
118
+ published.set(id, computeConfigVars(id, provider, substituteConfigVars(module.config, lookup)));
119
+ }
120
+ return published.get(id)?.[name];
121
+ };
122
+ return lookup;
123
+ }
124
+ function substituteConfigVars(value, lookup) {
125
+ if (typeof value === "string") {
126
+ const pure = value.match(PURE_CONFIG_VAR);
127
+ if (pure)
128
+ return lookup(pure[1], pure[2]);
129
+ return value.replace(CONFIG_VAR_PATTERN, (token, module, name) => {
130
+ const resolved = lookup(module, name);
131
+ return resolved === undefined ? token : String(resolved);
132
+ });
133
+ }
134
+ if (Array.isArray(value))
135
+ return value.map((item) => substituteConfigVars(item, lookup));
136
+ if (value && typeof value === "object") {
137
+ return Object.fromEntries(Object.entries(value).map(([key, item]) => [
138
+ key,
139
+ substituteConfigVars(item, lookup),
140
+ ]));
141
+ }
142
+ return value;
143
+ }
144
+ async function loadProjectConfig(projectDir, environment) {
145
+ const { ConfigLoader } = await Promise.resolve().then(() => __importStar(require("@antelopejs/core")));
146
+ const loader = new ConfigLoader(NODE_FILE_SYSTEM);
147
+ let loaded;
148
+ try {
149
+ loaded = (await loader.load(projectDir, environment));
150
+ }
151
+ catch (error) {
152
+ throw new cli_1.CliError({
153
+ title: `Could not evaluate the project's configuration in ${projectDir}`,
154
+ reason: error instanceof Error ? error.message : String(error),
155
+ fixes: ["Run from the project root, or pass --project <dir>"],
156
+ }, { cause: error });
157
+ }
158
+ const located = Object.fromEntries(Object.entries(loaded.modules).map(([id, module]) => [
159
+ id,
160
+ {
161
+ ...module,
162
+ folder: moduleFolder(projectDir, loaded.cacheFolder, module.source),
163
+ },
164
+ ]));
165
+ const lookup = configVarLookup(located);
166
+ return {
167
+ cacheFolder: loaded.cacheFolder,
168
+ modules: Object.fromEntries(Object.entries(located).map(([id, module]) => [
169
+ id,
170
+ { ...module, config: substituteConfigVars(module.config, lookup) },
171
+ ])),
172
+ };
173
+ }
174
+ function moduleFolder(projectDir, cacheFolder, source) {
175
+ const candidates = source.type === "local" && source.path
176
+ ? [(0, node_path_1.resolve)(projectDir, source.path)]
177
+ : source.package
178
+ ? [
179
+ (0, node_path_1.join)(projectDir, "node_modules", source.package),
180
+ (0, node_path_1.resolve)(projectDir, cacheFolder, source.package),
181
+ ]
182
+ : [];
183
+ return candidates.find((folder) => (0, node_fs_1.existsSync)((0, node_path_1.join)(folder, "package.json")));
184
+ }
185
+ function readShippingVersion(sourcePath) {
186
+ try {
187
+ const pkg = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)((0, node_path_1.dirname)(sourcePath), "package.json"), "utf-8"));
188
+ return typeof pkg.version === "string" ? pkg.version : undefined;
189
+ }
190
+ catch {
191
+ return undefined;
192
+ }
193
+ }
194
+ function toLayer(registration) {
195
+ const name = (0, layers_1.readLayerPackage)(registration.sourcePath)?.name;
196
+ return {
197
+ path: registration.sourcePath,
198
+ name: registration.name,
199
+ sourcePath: registration.sourcePath,
200
+ packageName: typeof name === "string" ? name : undefined,
201
+ priority: registration.priority ?? 0,
202
+ configKey: registration.configKey,
203
+ options: registration.options ?? {},
204
+ privateOptions: registration.privateOptions &&
205
+ (0, private_values_1.emptiedOptions)(registration.privateOptions),
206
+ authEstablishEndpoints: registration.authEstablishEndpoints ?? [],
207
+ publicEndpoints: registration.publicEndpoints ?? [],
208
+ backendProxies: registration.backendProxies ?? [],
209
+ };
210
+ }
211
+ function warnMissingModule(id) {
212
+ (0, cli_1.getProcessUi)().message("warn", `${id} is not installed in the project: its pages, if it has any, are left out`, { detail: "Install the project's modules first: ajs project build" });
213
+ }
214
+ function warnUndeclaredFrontend(id, folder) {
215
+ if (!(0, node_fs_1.existsSync)((0, node_path_1.join)(folder, FRONTEND_SOURCE_DIR)))
216
+ return;
217
+ (0, cli_1.getProcessUi)().message("warn", `${id} ships ${FRONTEND_SOURCE_DIR}/ but declares no ${exports.FRONTEND_DECLARATION_FIELD} in its package.json: its pages are left out`);
218
+ }
219
+ function registrationOf(id, folder, config, context) {
220
+ const declaration = (0, layers_1.readLayerPackage)(folder)?.[exports.FRONTEND_DECLARATION_FIELD];
221
+ if (typeof declaration !== "string") {
222
+ warnUndeclaredFrontend(id, folder);
223
+ return undefined;
224
+ }
225
+ const { frontendModule } = require((0, node_path_1.join)(folder, declaration));
226
+ if (typeof frontendModule !== "function") {
227
+ throw new cli_1.CliError({
228
+ title: `${id}: ${declaration} exports no frontendModule function`,
229
+ fixes: [`Export frontendModule(config, context) from ${declaration}`],
230
+ });
231
+ }
232
+ return frontendModule(config, context);
233
+ }
234
+ function isServedRenderer(registration) {
235
+ return (registration.renderer.name === RENDERER.name &&
236
+ registration.renderer.version === RENDERER.version);
237
+ }
238
+ function keepHighestPriority(registrations) {
239
+ const byName = new Map();
240
+ for (const registration of registrations) {
241
+ const kept = byName.get(registration.name);
242
+ if (!kept || (kept.priority ?? 0) <= (registration.priority ?? 0))
243
+ byName.set(registration.name, registration);
244
+ }
245
+ return [...byName.values()];
246
+ }
247
+ async function readProjectFrontendModules(options) {
248
+ const projectDir = (0, node_path_1.resolve)(options.projectDir);
249
+ const config = await loadProjectConfig(projectDir, options.environment);
250
+ const context = { dev: !!options.dev };
251
+ const registrations = Object.entries(config.modules).flatMap(([id, module]) => {
252
+ const folder = module.folder;
253
+ if (!folder) {
254
+ warnMissingModule(id);
255
+ return [];
256
+ }
257
+ const registration = registrationOf(id, folder, module.config, context);
258
+ return registration && isServedRenderer(registration)
259
+ ? [registration]
260
+ : [];
261
+ });
262
+ return keepHighestPriority(registrations)
263
+ .sort((left, right) => (right.priority ?? 0) - (left.priority ?? 0))
264
+ .map((registration) => ({
265
+ layer: toLayer(registration),
266
+ version: readShippingVersion(registration.sourcePath),
267
+ }));
268
+ }
@@ -1,8 +1,8 @@
1
1
  import { type ChildProcess, type SpawnOptions } from "node:child_process";
2
2
  import { type PathMapper } from "./child-output";
3
+ import { type ProjectModulesOptions } from "./project-modules";
3
4
  import { type ManifestSecrets } from "./manifest-secrets";
4
5
  import { ResolvedLayer } from "./workspace";
5
- import { type BuildOrigin, type FrontendSnapshot } from "./snapshot";
6
6
  export declare function computeDepsHash(layerPaths: string[], workspacePackage?: string, installInputs?: readonly string[]): string;
7
7
  export declare function needsInstall(workspaceDir: string, layerPaths: string[], force: boolean): boolean;
8
8
  export declare function saveDepsHash(workspaceDir: string, layerPaths: string[]): void;
@@ -21,18 +21,15 @@ export interface FramedCommandResult {
21
21
  }
22
22
  export declare function runFramedCommand(command: string, args: string[], options: FramedCommandOptions): Promise<FramedCommandResult>;
23
23
  export interface SetupWorkspaceOptions {
24
- backendUrl: string;
25
24
  force: boolean;
26
25
  mode: "dev" | "build";
26
+ backendUrl?: string;
27
27
  offline?: boolean;
28
28
  clientUrl?: string;
29
29
  workspaceKey?: string;
30
30
  bootstrapSecret?: string;
31
31
  workspaceDir?: string;
32
- snapshot?: {
33
- dir: string;
34
- snapshot: FrontendSnapshot;
35
- };
32
+ project?: ProjectModulesOptions;
36
33
  beforeInstall?: () => Promise<void>;
37
34
  }
38
35
  export interface SetupWorkspaceResult {
@@ -41,6 +38,5 @@ export interface SetupWorkspaceResult {
41
38
  manifestFromCache: boolean;
42
39
  manifestFetchedAt?: string;
43
40
  manifestSecrets: ManifestSecrets;
44
- buildOrigin: BuildOrigin;
45
41
  }
46
42
  export declare function setupWorkspace(opts: SetupWorkspaceOptions): Promise<SetupWorkspaceResult>;
@@ -15,14 +15,16 @@ const cli_1 = require("@antelopejs/core/cli");
15
15
  const child_output_1 = require("./child-output");
16
16
  const layers_1 = require("./layers");
17
17
  const manifest_1 = require("./manifest");
18
+ const dev_modules_1 = require("./dev-modules");
19
+ const project_modules_1 = require("./project-modules");
18
20
  const manifest_secrets_1 = require("./manifest-secrets");
19
21
  const renderer_range_1 = require("./renderer-range");
20
22
  const materialize_1 = require("./materialize");
21
23
  const workspace_1 = require("./workspace");
22
- const snapshot_1 = require("./snapshot");
23
24
  const cancellation_1 = require("./cancellation");
24
25
  const output_1 = require("./output");
25
26
  const config_1 = require("./config");
27
+ const locale_files_1 = require("./locale-files");
26
28
  const WORKSPACE_PNPM_CONFIG = "pnpm-workspace.yaml";
27
29
  const WORKSPACE_PATCHES_DIR = "patches";
28
30
  function workspaceInstallInputs(workspaceDir) {
@@ -215,85 +217,90 @@ async function runFramedCommand(command, args, options) {
215
217
  await Promise.race([ended, drainTimeout()]);
216
218
  return { code, lines: tails.flatMap((tail) => tail.lines()) };
217
219
  }
218
- function manifestCacheUse(mode, offline) {
219
- if (offline)
220
- return "offline";
221
- return mode === "build" ? "never" : "fallback";
222
- }
223
- async function snapshotLayers({ dir, snapshot }, workspaceDir) {
224
- const { manifest, takenAt } = snapshot;
225
- const cacheDir = (0, node_path_1.join)(dir, snapshot_1.SNAPSHOT_LAYERS_DIR);
226
- (0, layers_1.assertCachedArchivesExist)(manifest.modules, cacheDir, takenAt);
227
- (0, manifest_1.writeCachedManifest)(workspaceDir, manifest, "drop", takenAt);
220
+ async function projectLayers(project) {
221
+ const modules = await (0, project_modules_1.readProjectFrontendModules)(project);
228
222
  return {
229
- manifest,
230
223
  fromCache: false,
231
- layers: await (0, layers_1.buildLayersFromCache)(manifest.modules, cacheDir),
224
+ layers: modules.map((module) => module.layer),
225
+ secrets: { published: {}, conflicts: [] },
232
226
  };
233
227
  }
234
- async function servedLayers(opts, workspaceDir) {
235
- const { backendUrl, force, mode, offline, clientUrl, bootstrapSecret } = opts;
236
- const resolved = await (0, manifest_1.resolveManifest)(workspaceDir, backendUrl, manifestCacheUse(mode, offline), {
228
+ function servedManifest(opts, backendUrl, workspaceDir) {
229
+ const { offline, clientUrl, bootstrapSecret } = opts;
230
+ const cacheUse = offline ? "offline" : "fallback";
231
+ return (0, manifest_1.resolveManifest)(workspaceDir, backendUrl, cacheUse, {
237
232
  clientUrl,
238
233
  bootstrapSecret,
239
- privateValues: mode === "build" ? "drop" : "keep",
240
234
  });
241
- const { manifest, fromCache, fetchedAt } = resolved;
242
- if (mode === "dev") {
243
- (0, layers_1.assertLayerPathsServed)(manifest.modules, backendUrl, {
244
- bootstrapSecret,
245
- cache: fromCache ? { fetchedAt } : undefined,
246
- });
247
- if (fromCache) {
248
- (0, manifest_1.assertCachedLayerPathsExist)(manifest.modules, fetchedAt);
249
- }
250
- return { ...resolved, layers: (0, layers_1.buildLayersFromPaths)(manifest.modules) };
251
- }
252
- const cacheDir = (0, node_path_1.join)(workspaceDir, ".layers-cache");
235
+ }
236
+ async function projectLayersInDev(opts, project, backendUrl, workspaceDir) {
237
+ const { manifest, fromCache, fetchedAt } = await servedManifest(opts, backendUrl, workspaceDir);
238
+ const modules = await (0, dev_modules_1.readProjectModulesFresh)({ ...project, dev: true });
239
+ const layers = (0, dev_modules_1.withBackendValues)(modules, manifest);
240
+ (0, dev_modules_1.warnModuleDrift)(layers, manifest);
241
+ return {
242
+ fromCache,
243
+ fetchedAt,
244
+ layers,
245
+ secrets: (0, manifest_secrets_1.collectManifestSecrets)(layers.map((layer) => ({
246
+ name: layer.name ?? "",
247
+ priority: layer.priority ?? 0,
248
+ privateOptions: layer.privateOptions,
249
+ }))),
250
+ };
251
+ }
252
+ async function servedLayers(opts, backendUrl, workspaceDir) {
253
+ const { bootstrapSecret } = opts;
254
+ const { manifest, fromCache, fetchedAt } = await servedManifest(opts, backendUrl, workspaceDir);
255
+ (0, layers_1.assertLayerPathsServed)(manifest.modules, backendUrl, {
256
+ bootstrapSecret,
257
+ cache: fromCache ? { fetchedAt } : undefined,
258
+ });
253
259
  if (fromCache) {
254
- if (!(0, node_fs_1.existsSync)(cacheDir)) {
255
- throw new cli_1.CliError({
256
- title: "No layers archive was downloaded for this workspace",
257
- fixes: [
258
- "Run once without --offline, with the backend reachable, first",
259
- ],
260
- });
261
- }
262
- (0, layers_1.assertCachedArchivesExist)(manifest.modules, cacheDir, fetchedAt);
263
- }
264
- else {
265
- await (0, layers_1.downloadAndExtractLayers)(backendUrl, manifest.pack, cacheDir, force, bootstrapSecret);
260
+ (0, manifest_1.assertCachedLayerPathsExist)(manifest.modules, fetchedAt);
266
261
  }
267
262
  return {
268
- ...resolved,
269
- layers: await (0, layers_1.buildLayersFromCache)(manifest.modules, cacheDir),
263
+ fromCache,
264
+ fetchedAt,
265
+ layers: (0, layers_1.buildLayersFromPaths)(manifest.modules),
266
+ secrets: (0, manifest_secrets_1.collectManifestSecrets)(manifest.modules),
270
267
  };
271
268
  }
272
- async function setupWorkspace(opts) {
273
- const { backendUrl, force } = opts;
269
+ function workspaceDirFor(opts) {
270
+ if (opts.workspaceDir)
271
+ return (0, workspace_1.ensureExplicitWorkspace)(opts.workspaceDir);
272
+ const backendUrl = requireDevBackend(opts);
274
273
  const workspaceKey = opts.workspaceKey ?? (0, config_1.canonicalizeBackendUrl)(backendUrl);
275
- const workspaceDir = opts.workspaceDir
276
- ? (0, workspace_1.ensureExplicitWorkspace)(opts.workspaceDir)
277
- : (0, workspace_1.ensureWorkspace)(workspaceKey);
274
+ const workspaceDir = (0, workspace_1.ensureWorkspace)(workspaceKey);
278
275
  (0, workspace_1.writeWorkspaceMeta)(workspaceDir, backendUrl, workspaceKey);
279
- const { manifest, fromCache, fetchedAt, layers } = opts.snapshot
280
- ? await snapshotLayers(opts.snapshot, workspaceDir)
281
- : await servedLayers(opts, workspaceDir);
282
- const buildOrigin = {
283
- source: opts.snapshot ? "snapshot" : "backend",
284
- backendUrl: (0, config_1.canonicalizeBackendUrl)(backendUrl),
285
- takenAt: opts.snapshot?.snapshot.takenAt ?? fetchedAt ?? new Date().toISOString(),
286
- digest: (0, snapshot_1.manifestDigest)(manifest.modules),
287
- authenticated: !!opts.snapshot || (!!opts.bootstrapSecret && !fromCache),
288
- };
276
+ return workspaceDir;
277
+ }
278
+ function requireDevBackend(opts) {
279
+ if (!opts.backendUrl)
280
+ throw new Error("A dev workspace is set up from a backend URL");
281
+ return opts.backendUrl;
282
+ }
283
+ function workspaceSource(opts, workspaceDir) {
284
+ if (opts.mode === "build") {
285
+ if (!opts.project)
286
+ throw new Error("A build reads a project");
287
+ return projectLayers(opts.project);
288
+ }
289
+ if (opts.project)
290
+ return projectLayersInDev(opts, opts.project, requireDevBackend(opts), workspaceDir);
291
+ return servedLayers(opts, requireDevBackend(opts), workspaceDir);
292
+ }
293
+ async function setupWorkspace(opts) {
294
+ const workspaceDir = workspaceDirFor(opts);
295
+ const { fromCache, fetchedAt, layers, secrets } = await workspaceSource(opts, workspaceDir);
289
296
  (0, renderer_range_1.assertLayersSupportRenderer)(layers, { workspaceDir });
290
297
  (0, materialize_1.copyStaticTemplates)(workspaceDir);
291
298
  (0, materialize_1.writeWorkspacePackageJson)(workspaceDir, layers);
292
299
  (0, materialize_1.materializeLayers)(workspaceDir, layers);
293
- (0, materialize_1.writeFrontendModuleRegistry)(workspaceDir, layers);
300
+ writeDerivedOutputs(workspaceDir, layers);
294
301
  (0, materialize_1.writeDmsMainCss)(workspaceDir, layers);
295
302
  const layerPaths = layers.map((layer) => layer.path);
296
- const shouldInstall = needsInstall(workspaceDir, layerPaths, force);
303
+ const shouldInstall = needsInstall(workspaceDir, layerPaths, opts.force);
297
304
  if (shouldInstall) {
298
305
  await opts.beforeInstall?.();
299
306
  await installDeps(workspaceDir, (0, child_output_1.createPathMapper)(workspaceDir, layers));
@@ -304,7 +311,17 @@ async function setupWorkspace(opts) {
304
311
  layers,
305
312
  manifestFromCache: fromCache,
306
313
  manifestFetchedAt: fetchedAt,
307
- manifestSecrets: (0, manifest_secrets_1.collectManifestSecrets)(manifest.modules),
308
- buildOrigin,
314
+ manifestSecrets: secrets,
309
315
  };
310
316
  }
317
+ function writeDerivedOutputs(workspaceDir, layers) {
318
+ try {
319
+ (0, materialize_1.writeFrontendModuleRegistry)(workspaceDir, layers);
320
+ }
321
+ catch (error) {
322
+ if (!(error instanceof locale_files_1.InvalidLocaleFileError))
323
+ throw error;
324
+ const showSource = (0, child_output_1.createPathMapper)(workspaceDir, layers);
325
+ throw new locale_files_1.InvalidLocaleFileError(showSource(error.path), error.reason);
326
+ }
327
+ }
@@ -4,7 +4,6 @@ export interface FrontendModuleOptions {
4
4
  }
5
5
  export interface ManifestModule {
6
6
  name: string;
7
- archiveName: string;
8
7
  renderer?: {
9
8
  name: string;
10
9
  version: string;
@@ -21,22 +20,27 @@ export interface ManifestModule {
21
20
  export interface BackendProxyDeclaration {
22
21
  path: string;
23
22
  auth?: "session" | "none";
23
+ maxBodyBytes?: number;
24
24
  }
25
25
  export interface PublicEndpointDeclaration {
26
26
  path: string;
27
27
  methods?: string[];
28
28
  headers?: string[];
29
29
  }
30
+ export interface OriginRewrite {
31
+ from: string;
32
+ to: string;
33
+ }
30
34
  export interface Manifest {
31
- pack: string;
32
35
  modules: ManifestModule[];
33
36
  settling?: boolean;
37
+ originRewrites?: OriginRewrite[];
34
38
  }
35
39
  export interface FrontendManifest {
36
40
  version: number;
37
- archive: string;
38
41
  modules: ManifestModule[];
39
42
  settling?: boolean;
43
+ originRewrites?: OriginRewrite[];
40
44
  }
41
45
  export interface ResolvedLayer {
42
46
  path: string;
@@ -70,6 +74,6 @@ export interface WorkspaceListing {
70
74
  export declare function listWorkspaces(): WorkspaceListing;
71
75
  export declare function directorySize(dir: string): number;
72
76
  export declare function writeWorkspaceMeta(workspaceDir: string, backendUrl: string, workspaceKey: string): void;
73
- export declare const FRONTEND_MANIFEST_VERSION = 1;
77
+ export declare const FRONTEND_MANIFEST_VERSION = 2;
74
78
  export declare function bootstrapHeaders(secret?: string): Record<string, string>;
75
79
  export declare function isUnauthorized(status: number): boolean;
package/dist/workspace.js CHANGED
@@ -131,7 +131,7 @@ function writeWorkspaceMeta(workspaceDir, backendUrl, workspaceKey) {
131
131
  }
132
132
  const BOOTSTRAP_HEADER = "x-dms-bootstrap";
133
133
  const UNAUTHORIZED_STATUSES = [401, 403];
134
- exports.FRONTEND_MANIFEST_VERSION = 1;
134
+ exports.FRONTEND_MANIFEST_VERSION = 2;
135
135
  function bootstrapHeaders(secret) {
136
136
  return secret ? { [BOOTSTRAP_HEADER]: secret } : {};
137
137
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/dms-frontend",
3
- "version": "0.7.1",
3
+ "version": "0.9.0",
4
4
  "description": "Frontend-agnostic loader for AntelopeJS DMS, shipping the Vue 3 renderer (Vite, Inertia, SSR)",
5
5
  "keywords": [
6
6
  "antelope",
@@ -73,11 +73,11 @@
73
73
  "docs:reference:check": "tsx scripts/docs-reference.ts --check"
74
74
  },
75
75
  "dependencies": {
76
+ "@intlify/message-compiler": "^11.1.12",
76
77
  "chokidar": "^4.0.3",
77
78
  "commander": "^13.1.0",
78
79
  "ignore": "^7.0.5",
79
- "semver": "^7.7.4",
80
- "unzipper": "^0.12.3"
80
+ "semver": "^7.7.4"
81
81
  },
82
82
  "devDependencies": {
83
83
  "@antelopejs/core": "^1.16.0",
@@ -87,10 +87,10 @@
87
87
  "@inertiajs/vue3": "^3.7.1",
88
88
  "@types/node": "^24.8.1",
89
89
  "@types/semver": "^7.7.1",
90
- "@types/unzipper": "^0.10.11",
91
90
  "@unhead/vue": "^2.1.4",
92
- "@vitejs/plugin-vue": "^6.0.1",
91
+ "@vitejs/plugin-vue": "^6.0.6",
93
92
  "@vue/server-renderer": "^3.5.25",
93
+ "@vueuse/core": "14.4.0",
94
94
  "defu": "^6.1.4",
95
95
  "devalue": "^5.9.4",
96
96
  "knip": "^6.34.0",
@@ -101,10 +101,10 @@
101
101
  "release-it": "^19.2.4",
102
102
  "release-it-changelogen": "^0.1.0",
103
103
  "rimraf": "^6.1.2",
104
- "tsx": "^4.20.6",
104
+ "tsx": "^4.23.15",
105
105
  "typescript": "^6.0.3",
106
106
  "unplugin-auto-import": "^20.2.0",
107
- "vite": "^7.2.4",
107
+ "vite": "^8.3.4",
108
108
  "vue": "^3.5.25",
109
109
  "vue-i18n": "^11.2.7"
110
110
  },
@@ -119,9 +119,14 @@
119
119
  "@oxc-parser/binding-win32-x64-msvc": "^0.95.0"
120
120
  },
121
121
  "engines": {
122
- "node": ">=20.12.0"
122
+ "node": ">=20.19.0"
123
123
  },
124
124
  "packageManager": "pnpm@10.6.5",
125
+ "pnpm": {
126
+ "patchedDependencies": {
127
+ "@vueuse/core@14.4.0": "templates/vue/patches/@vueuse__core@14.4.0.patch"
128
+ }
129
+ },
125
130
  "antelopeJs": {
126
131
  "skills": [
127
132
  "./skills"
@@ -18,8 +18,8 @@ tags: [frontend, cli, renderer, vue, inertia, ssr, deployment, dms-module]
18
18
 
19
19
  ## Rules that bite
20
20
 
21
- - **One workspace per backend URL**, under `~/.antelopejs/dms-frontend/<sha256>/`. `dev` without `-b` keys its own on the project directory instead, so `dev` then `build -b` builds a second workspace. `start` serves the workspace `build` made for the same URL, user and home directory.
22
- - **Secrets.** `build` and `start` need the same `DMS_SESSION_SECRET` (32+ characters); `dev` generates one only when it is unset. `build` takes `DMS_BOOTSTRAP_SECRET` explicitly, never from `.antelope/dms-dev.json`. A build without it still succeeds, but loses the modules' private options, the manifest's backend proxies and the e-mail and OAuth secrets: `start` then warns that e-mail renders and OAuth sign-in will be refused.
21
+ - **One dev workspace per backend URL**, under `~/.antelopejs/dms-frontend/<sha256>/`. `dev` without `-b` keys its own on the project directory instead. A production build is different: `build` reads the project itself (`antelope.config.ts` with `-e production`, each installed module's `dmsFrontend` function), needs no backend, and writes `<project>/.dms-frontend`, which `start` serves from the project root.
22
+ - **Secrets.** `start` needs `DMS_SESSION_SECRET` (32+ characters); `dev` generates one only when it is unset. `build` needs no secret. `start` reads `DMS_HTML_RENDER_SECRET` and `DMS_OAUTH_RELAY_SECRET` from its environment and warns when they are unset, and computes the modules' public options from the project's configuration with its own variables, which must match the backend's.
23
23
  - **`.env`** is read from the current directory only (shell, then `.env.local`, then `.env`). A `DMS_API_BASE_URL` in `.env` turns off backend discovery for `dev`. `clean` never reads it.
24
24
  - **`prepare` never fails** without `--strict`: it warns and exits 0. Use `--strict` where the types are required.
25
25
  - **Exit codes:** 0 success, 1 failure, 2 usage error, 130 Ctrl+C.
@@ -58,6 +58,7 @@ import {
58
58
  drawLoadedIconsAtOnce,
59
59
  } from "./icon-hydration";
60
60
  import {
61
+ catalogLocale,
61
62
  loadLocaleMessages,
62
63
  localeMessages,
63
64
  supportedLocales,
@@ -85,6 +86,8 @@ export interface DmsAppOptions {
85
86
  }
86
87
 
87
88
  export interface DmsConfiguredApp {
89
+ /** The locale of the catalog the app shows its messages from now. */
90
+ catalogLocale: () => string | undefined;
88
91
  mounted: () => Promise<void>;
89
92
  runtime: ReturnType<typeof createDmsFrontendRuntime>;
90
93
  }
@@ -348,7 +351,11 @@ export async function configureDmsApp(
348
351
  loadLocaleMessages as DmsLocaleLoader,
349
352
  supportedLocales,
350
353
  );
351
- return { mounted, runtime };
354
+ return {
355
+ catalogLocale: () => catalogLocale(i18n.global.locale.value),
356
+ mounted,
357
+ runtime,
358
+ };
352
359
  }
353
360
 
354
361
  export { trackDmsAsyncComponents };