@antelopejs/dms-frontend 0.3.8 → 0.3.9

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 (80) hide show
  1. package/README.md +82 -23
  2. package/dist/cancellation.d.ts +8 -0
  3. package/dist/cancellation.js +23 -0
  4. package/dist/child-output.d.ts +45 -0
  5. package/dist/child-output.js +183 -0
  6. package/dist/commands/build-action.d.ts +21 -0
  7. package/dist/commands/build-action.js +144 -0
  8. package/dist/commands/build.js +54 -62
  9. package/dist/commands/clean-action.d.ts +8 -0
  10. package/dist/commands/clean-action.js +97 -0
  11. package/dist/commands/clean.js +52 -39
  12. package/dist/commands/dev-action.d.ts +8 -0
  13. package/dist/commands/dev-action.js +120 -0
  14. package/dist/commands/dev.js +55 -115
  15. package/dist/commands/prepare-action.d.ts +8 -0
  16. package/dist/commands/prepare-action.js +73 -0
  17. package/dist/commands/prepare.js +55 -54
  18. package/dist/commands/start-action.d.ts +5 -0
  19. package/dist/commands/start-action.js +77 -0
  20. package/dist/commands/start.js +48 -48
  21. package/dist/commands/verify-source-action.d.ts +10 -0
  22. package/dist/commands/verify-source-action.js +104 -0
  23. package/dist/commands/verify-source.d.ts +0 -1
  24. package/dist/commands/verify-source.js +53 -31
  25. package/dist/commands/workspace-task.d.ts +2 -0
  26. package/dist/commands/workspace-task.js +26 -0
  27. package/dist/commands/workspaces-action.d.ts +25 -0
  28. package/dist/commands/workspaces-action.js +71 -0
  29. package/dist/commands/workspaces.d.ts +2 -0
  30. package/dist/commands/workspaces.js +60 -0
  31. package/dist/common.d.ts +1 -0
  32. package/dist/common.js +1 -0
  33. package/dist/config.d.ts +13 -4
  34. package/dist/config.js +115 -18
  35. package/dist/derived-outputs.d.ts +6 -1
  36. package/dist/derived-outputs.js +12 -3
  37. package/dist/discovery.d.ts +2 -1
  38. package/dist/discovery.js +28 -11
  39. package/dist/env-file.js +5 -2
  40. package/dist/help.d.ts +5 -0
  41. package/dist/help.js +70 -0
  42. package/dist/index.js +98 -38
  43. package/dist/layer-watch.d.ts +2 -1
  44. package/dist/layer-watch.js +129 -26
  45. package/dist/layers.d.ts +2 -2
  46. package/dist/layers.js +71 -23
  47. package/dist/manifest-secrets.d.ts +3 -2
  48. package/dist/manifest-secrets.js +25 -21
  49. package/dist/manifest.d.ts +22 -5
  50. package/dist/manifest.js +184 -28
  51. package/dist/materialize.js +2 -1
  52. package/dist/output.d.ts +41 -0
  53. package/dist/output.js +197 -0
  54. package/dist/ports.d.ts +2 -0
  55. package/dist/ports.js +23 -13
  56. package/dist/renderer-range.d.ts +3 -1
  57. package/dist/renderer-range.js +49 -17
  58. package/dist/server-process.d.ts +39 -0
  59. package/dist/server-process.js +221 -0
  60. package/dist/typecheck-output.d.ts +13 -0
  61. package/dist/typecheck-output.js +87 -0
  62. package/dist/update-check.d.ts +14 -2
  63. package/dist/update-check.js +95 -29
  64. package/dist/verify-source-result.d.ts +11 -0
  65. package/dist/verify-source-result.js +17 -0
  66. package/dist/verify-source-runner.js +266 -131
  67. package/dist/vite-output.d.ts +14 -0
  68. package/dist/vite-output.js +99 -0
  69. package/dist/workspace-setup.d.ts +17 -3
  70. package/dist/workspace-setup.js +119 -35
  71. package/dist/workspace.d.ts +8 -2
  72. package/dist/workspace.js +47 -12
  73. package/package.json +4 -12
  74. package/templates/vue/package.json +4 -0
  75. package/templates/vue/server/cli-report.mjs +91 -0
  76. package/templates/vue/server.mjs +18 -15
  77. package/templates/vue/vite.config.ts +8 -1
  78. package/templates/vue/vite.email.config.ts +5 -1
  79. package/dist/utils/cli-ui.d.ts +0 -27
  80. package/dist/utils/cli-ui.js +0 -178
package/README.md CHANGED
@@ -71,6 +71,8 @@ pnpm add @antelopejs/core @antelopejs/dms-frontend
71
71
  pnpm add -g @antelopejs/core @antelopejs/dms-frontend
72
72
  ```
73
73
 
74
+ The plugin prints through the output module of `@antelopejs/core`
75
+ (`@antelopejs/core/cli`), so it needs `@antelopejs/core` 1.13.0 or later.
74
76
  `npm install -g` works too; this repository and every generated workspace use
75
77
  pnpm. Inside a package script, `ajs` resolves from `node_modules/.bin`, and the
76
78
  `dms` command it delegates to resolves the project-local plugin, so a script
@@ -83,25 +85,68 @@ ajs dms prepare -b http://localhost:5010
83
85
  ajs dms dev -b http://localhost:5010 -p 3001
84
86
  ajs dms build -b https://dms.example.com
85
87
  ajs dms start -b https://dms.example.com -p 3001
88
+ ajs dms workspaces
89
+ ajs dms workspaces --json
86
90
  ajs dms clean -b https://dms.example.com
87
91
  ajs dms clean --all
92
+ ajs dms help environment
88
93
  ```
89
94
 
90
- `--help`, `--version` and `clean` run from any directory. `prepare` also runs
91
- anywhere: with no backend in reach it warns and exits 0, so a frontend module's
92
- `postinstall` hook never fails an install, and the generated types are refreshed
93
- later from a development environment. `dev`, `build` and `start` need a backend
94
- URL, either through `-b` or through the enclosing antelope project's
95
- `.antelope/dev.json`, and say so on exit 1 when they have neither;
95
+ `ajs dms <command> --help` shows what a command does, its options and a few
96
+ examples. `ajs dms help environment` lists every variable the CLI and the
97
+ generated server read, from the environment or the project's `.env`.
98
+
99
+ `--help`, `--version`, `workspaces` and `clean` run from any directory. `build` and `start`
100
+ need a backend URL through `-b` or `DMS_API_BASE_URL`, and exit 2 without one.
101
+ `dev` also discovers it from the enclosing antelope project's
102
+ `.antelope/dev.json`, and exits 1 when there is none to discover.
96
103
  `verify-source` needs `--layer` instead.
97
104
 
105
+ `prepare` runs anywhere: when it cannot prepare the workspace it warns on
106
+ stderr and exits 0, so a frontend module's `postinstall` hook never fails an
107
+ install. The warning says whether prepare was skipped or failed. Skipped means
108
+ no backend is in reach (no URL, an unreachable backend, `--offline` without a
109
+ cached manifest), as in CI: the generated types are refreshed later from a
110
+ development environment. Failed means something a later run will not fix: a
111
+ refused credential, an incompatible manifest or renderer range, an invalid URL.
112
+ A step that needs the types, such as a CI type-check, passes `--strict` (or
113
+ sets `DMS_PREPARE_STRICT=1`): prepare then exits 1 in every one of these cases,
114
+ and 2 when the backend URL is missing or invalid. On success, it says how many
115
+ modules it found and how long it took.
116
+
117
+ `build` ends with where it wrote the production frontend (the `dist`
118
+ directory of the workspace), how long it took, and the `ajs dms start` command
119
+ to run next, with the same backend URL and `DMS_SESSION_SECRET`. Its output is
120
+ deployed, so it never builds silently from the cache: when the backend cannot
121
+ be reached or fails, `build` exits 1, says whether a cached manifest exists and
122
+ how old it is, and builds from it only when asked with `--offline`. `dev` and
123
+ `prepare` still fall back to the cache, with a warning.
124
+
125
+ The commands follow the output contract of `ajs`: what the CLI itself prints
126
+ on stdout is only the help and the version, and everything else (progress,
127
+ warnings, errors) goes to stderr. The dependency install and each step of the
128
+ production build run as one progress line each: their output is kept and its
129
+ last lines are shown when they fail, with paths into the generated workspace
130
+ pointing at the frontend-module sources instead. The server `dev` and `start`
131
+ run keeps its own output. A command exits 0 on success, 1 on a failure, 2 on a
132
+ usage error (an unknown command or option, a missing or invalid argument, a
133
+ missing configuration value) and 130 when stopped with Ctrl+C. `--no-color` or
134
+ `NO_COLOR=1` turns colors off, and terminals that cannot draw Unicode, such as
135
+ `TERM=dumb`, get ASCII symbols. `--verbose` or `ANTELOPEJS_VERBOSE` streams
136
+ the full `pnpm` and Vite output, each line behind the name of the step, and adds
137
+ the stack trace of the underlying error to a failure. `ajs --no-color dms …`,
138
+ `ajs --verbose dms …` and `ajs --verbose=<channels> dms …` set those variables
139
+ for the plugin.
140
+
98
141
  The CLI checks npm for a newer release at most once a day and prints a one-line
99
- notice on stderr. A lookup that comes back empty — offline, throttled, or simply
100
- raced by a command that blocked the event loop past the deadline — is retried
101
- after an hour instead of counting as the day's attempt. The throttle stamp lives
102
- at `~/.antelopejs/dms-frontend/update-check.json`. Set
103
- `NO_UPDATE_NOTIFIER=1`, pass `--no-update-check`, or run under `CI` to turn the
104
- check off.
142
+ notice on stderr, only to a terminal: as the last line of a command that
143
+ succeeded, or under the ready block of `dev` and `start`. Help, `--version`,
144
+ `--json`, failed commands, pipes and `TERM=dumb` never show it. The lookup starts
145
+ with the command and never delays its exit; a lookup that comes back empty —
146
+ offline or throttled — is retried after an hour instead of counting as the day's
147
+ attempt. The throttle stamp lives at
148
+ `~/.antelopejs/dms-frontend/update-check.json`. Set `NO_UPDATE_NOTIFIER=1`, pass
149
+ `--no-update-check`, or run under `CI` to turn the check off.
105
150
 
106
151
  Manifest negotiation and module materialization are the renderer contract described in [Renderers](#renderers).
107
152
 
@@ -151,7 +196,7 @@ A frontend module names the `@antelopejs/dms-frontend` releases it runs on in it
151
196
  }
152
197
  ```
153
198
 
154
- `prepare`, `dev`, `build` and `verify-source` read it before they generate anything, whether the manifest was just fetched or replayed from the cache, and stop when this release falls outside a module's range: `dev`, `build` and `verify-source` exit 1, and `prepare` warns and exits 0, as it does for any setup it cannot complete. The message names each such module, its range and this release, rather than letting the build, the type-check or a render fail later on an API the release no longer has, or does not have yet. A prerelease is checked as the release it leads to: `0.4.0-next.1` is outside `<0.4.0`. A module that declares no range still loads, and a warning names it once. `start` checks nothing: it runs what `build` produced.
199
+ `prepare`, `dev`, `build` and `verify-source` read it before they generate anything, whether the manifest was just fetched or replayed from the cache, and stop when this release falls outside a module's range: `dev`, `build` and `verify-source` exit 1, and `prepare` warns and exits 0, as it does for any setup it cannot complete, unless `--strict` is given. The message names each such module, its range and this release, rather than letting the build, the type-check or a render fail later on an API the release no longer has, or does not have yet. A prerelease is checked as the release it leads to: `0.4.0-next.1` is outside `<0.4.0`. A module that declares no range still loads, and a warning names it once. `start` checks nothing: it runs what `build` produced.
155
200
 
156
201
  Cap the range below the next breaking release, which is the next minor release while this package is 0.x, and widen it once the module has been checked against that release. A range left open to `<1.0.0` admits the very release that removes something the module calls.
157
202
 
@@ -213,7 +258,7 @@ which is the order the loader itself materializes them in.
213
258
 
214
259
  In development, `ajs dms` discovers the backend from the nearest live `.antelope/dev.json`. It reads the local bootstrap credential from `.antelope/dms-dev.json` only when that discovered backend matches the destination URL. For production and CI, set `DMS_API_BASE_URL` and `DMS_BOOTSTRAP_SECRET` in the environment, or in the project's `.env`, rather than passing credentials on the command line.
215
260
 
216
- Each canonical backend URL gets an owner-only workspace under `~/.antelopejs/dms-frontend` (see [Workspaces](#workspaces) for how the key is derived). Manifest caches, private module configuration, and extracted archives retain restrictive permissions. `--offline` reuses the last successful manifest and archive; an authorization failure never falls back to privileged cached data.
261
+ Each canonical backend URL gets an owner-only workspace under `~/.antelopejs/dms-frontend` (see [Workspaces](#workspaces) for how the key is derived). Manifest caches, private module configuration, and extracted archives retain restrictive permissions. `--offline` reuses the last successful manifest and archive; `dev` and `prepare` also fall back to them when the backend cannot be reached or fails, and `build` only with `--offline`. An authorization failure never falls back to privileged cached data.
217
262
 
218
263
  ## Configuration
219
264
 
@@ -324,9 +369,21 @@ different port between runs and re-keying on the URL would discard
324
369
  `node_modules`, the manifest cache and the client-side appId scope every time it
325
370
  does. The consequence is that `ajs dms dev` followed by `ajs dms build -b <url>`
326
371
  against the same backend creates two workspaces of their own — several hundred
327
- megabytes each. Pass `-b` to `dev` to share a single one. `clean --all` lists
328
- both and names the project a workspace is keyed on; `clean -b <url>` only
329
- reaches the URL-keyed one.
372
+ megabytes each. Pass `-b` to `dev` to share a single one. `workspaces` lists
373
+ both, with the project a workspace is keyed on, its size and its last use;
374
+ `clean -b <url>` only reaches the URL-keyed one. `clean` takes its target from
375
+ `-b` or `--all` only: it never reads `DMS_API_BASE_URL` from the environment or
376
+ `.env`, so running it without either is an error rather than a deletion.
377
+
378
+ `workspaces` prints an aligned table on a terminal. Piped, it prints one
379
+ tab-separated line per workspace, without a header: id, backend URL, key type
380
+ (`url` or `project`), project directory, size in bytes, last use (ISO 8601) and
381
+ workspace directory. `--json` prints one JSON document with the same fields;
382
+ everything else the command says goes to stderr.
383
+
384
+ `clean --all` lists what it removes and asks before removing it. Without a
385
+ terminal to ask on (a pipe, CI), it exits 2 unless `--yes` is given. Both forms
386
+ of `clean` say how much space they freed.
330
387
 
331
388
  A `DMS_API_BASE_URL` line in `.env` counts as an explicit backend, so a project
332
389
  that configures one gets the single shared workspace and gives up autodiscovery
@@ -378,11 +435,13 @@ needs no exception for the Iconify API. Icons reach the page two ways:
378
435
 
379
436
  | Option | Environment | Purpose |
380
437
  | --- | --- | --- |
381
- | `-b, --backend-url` | `DMS_API_BASE_URL` | DMS backend URL |
382
- | `-p, --port` | `PORT` | Frontend port, default `3001` |
383
- | `-f, --force` | | Reinstall workspace dependencies |
384
- | `--offline` | `DMS_OFFLINE` | Reuse cached manifest and archives |
438
+ | `-b, --backend-url` | `DMS_API_BASE_URL` (except `clean`) | DMS backend URL, `http://` or `https://` |
439
+ | `-p, --port` | `PORT` | Frontend port from 1 to 65535, default `3001`; `dev` moves to the next free port, `start` stops when it is in use |
440
+ | `-f, --force` | | Reinstall workspace dependencies; `build` also extracts the layers archive from scratch |
441
+ | `--offline` | `DMS_OFFLINE` | Reuse cached manifest and archives; the only way `build` uses them |
442
+ | `--strict` | `DMS_PREPARE_STRICT` | Make `prepare` fail instead of warning; see [Commands](#commands) |
385
443
  | `--bootstrap-secret` | `DMS_BOOTSTRAP_SECRET` | Backend bootstrap credential |
444
+ | | `HOST` | Address the frontend server binds to (default `0.0.0.0`) |
386
445
  | | `DMS_COOKIE_SECURE` | Secure cookies (`true` by default; `ajs dms dev` defaults to `false`) |
387
446
  | | `DMS_TRUSTED_PROXY_HOPS` | Number of trusted, rightmost reverse-proxy hops (default `0`) |
388
447
  | | `DMS_SESSION_SECRET` | Session cookie encryption key, 32 characters or more (required for login) |
@@ -393,7 +452,7 @@ needs no exception for the Iconify API. Icons reach the page two ways:
393
452
 
394
453
  All of these can be set in the project's `.env` instead of the environment; see [Configuration](#configuration).
395
454
 
396
- `ajs dms dev` takes the two manifest secrets from the manifest it fetches, and `ajs dms start` from the manifest cached by `ajs dms build`. An explicitly set variable always wins over the manifest value. Before the server starts, both commands log where each secret comes from (`env`, `manifest`, `build-time manifest` or `not set`), never its value. When several modules publish the same secret, the first one in manifest-priority order wins, and a module publishing a different value is named in a warning.
455
+ `ajs dms dev` takes the two manifest secrets from the manifest it fetches, and `ajs dms start` from the manifest cached by `ajs dms build`. An explicitly set variable always wins over the manifest value. Before the server starts, both commands warn about each secret that is not set and what stops working without it; once the server is ready, they list where the others come from (the environment, the manifest or the build-time manifest), never their values. When several modules publish the same secret, the first one in manifest-priority order wins, and a module publishing a different value is named in a warning.
397
456
 
398
457
  Use pnpm for all repository and workspace operations.
399
458
 
@@ -408,6 +467,6 @@ ajs dms verify-source \
408
467
  --local-package @antelopejs/dms=/path/to/dms
409
468
  ```
410
469
 
411
- A supplied package whose declared range excludes this adapter version is refused before anything is installed (see [Declaring the loader releases a module supports](#declaring-the-loader-releases-a-module-supports)). The verifier prints its generated workspace path, builds client, SSR, and email bundles, runs `vue-tsc`, renders eight DMS email templates, and checks that the email bundle excludes browser-only modules. It leaves the generated workspace in the temporary directory for inspection. It does not start a backend or publish packages. Repository development can invoke the same runner with `DMS_LAYER_SOURCE` through `pnpm test:real-source`.
470
+ A supplied package whose declared range excludes this adapter version is refused before anything is installed (see [Declaring the loader releases a module supports](#declaring-the-loader-releases-a-module-supports)). The verifier generates a temporary workspace, installs it, builds client, SSR, and email bundles, checks what the client bundle loads, runs `vue-tsc`, renders eight DMS email templates, and checks that the email bundle excludes browser-only modules. Each check is one line that ends in ✔ or ✖, and the run ends with `Verified <n> modules` and its duration, or with the first failure: a type or Vite error points at the file in the package you passed (`./frontend-vue/app/components/Callout.vue:17:7`), not at its copy in the workspace, and the command exits 1. `--verbose` streams the pnpm, Vite and `vue-tsc` output. The workspace is removed when the run ends. It does not start a backend or publish packages. Repository development can invoke the same runner with `DMS_LAYER_SOURCE` through `pnpm test:real-source`.
412
471
 
413
472
  Refresh rotation is single-flight within one frontend process. Horizontally scaled deployments must use sticky routing so a browser reaches the same process, or replace this process-local behavior with an external session adapter. It does not provide a distributed rotation guarantee.
@@ -0,0 +1,8 @@
1
+ export declare function exitCodeForSignal(signal: NodeJS.Signals): number;
2
+ export declare class CancelledError extends Error {
3
+ readonly signal: NodeJS.Signals;
4
+ readonly exitCode: number;
5
+ readonly stopped: string;
6
+ readonly context: string[];
7
+ constructor(signal: NodeJS.Signals, stopped?: string, context?: string);
8
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CancelledError = void 0;
4
+ exports.exitCodeForSignal = exitCodeForSignal;
5
+ const node_os_1 = require("node:os");
6
+ const cli_1 = require("@antelopejs/core/cli");
7
+ function exitCodeForSignal(signal) {
8
+ const number = node_os_1.constants.signals[signal];
9
+ return 128 + (typeof number === "number" ? number : 15);
10
+ }
11
+ class CancelledError extends Error {
12
+ constructor(signal, stopped = "Stopped", context) {
13
+ const cause = signal === "SIGINT" ? "" : ` (${signal})`;
14
+ const parts = [`${stopped}${cause}`, ...(context ? [context] : [])];
15
+ super(parts.join((0, cli_1.getProcessUi)().symbols.separator));
16
+ this.signal = signal;
17
+ this.name = "CancelledError";
18
+ this.exitCode = exitCodeForSignal(signal);
19
+ this.stopped = parts[0];
20
+ this.context = parts.slice(1);
21
+ }
22
+ }
23
+ exports.CancelledError = CancelledError;
@@ -0,0 +1,45 @@
1
+ import { type CliProblem, type Ui } from "@antelopejs/core/cli";
2
+ import type { ResolvedLayer } from "./workspace";
3
+ export declare const VERBOSE_OUTPUT_HINT = "Run with --verbose for the full output.";
4
+ export declare function stripAnsi(text: string): string;
5
+ export declare class LineSplitter {
6
+ private readonly onLine;
7
+ private pending;
8
+ constructor(onLine: (line: string) => void);
9
+ push(chunk: string): void;
10
+ flush(): void;
11
+ }
12
+ export declare class OutputTail {
13
+ private readonly capacity;
14
+ private readonly kept;
15
+ constructor(capacity?: number);
16
+ push(line: string): void;
17
+ lines(): string[];
18
+ }
19
+ export declare function failureTail(lines: string[], count?: number, keepStack?: boolean): string[];
20
+ export interface ChildFailure {
21
+ title: string;
22
+ command: string;
23
+ code: number;
24
+ lines: string[];
25
+ isVerbose: boolean;
26
+ }
27
+ export declare function describeChildFailure(failure: ChildFailure): CliProblem;
28
+ export type PathMapper = (line: string) => string;
29
+ export interface PathMapperOptions {
30
+ showSource?: (path: string) => string;
31
+ shownWorkspace?: string;
32
+ platform?: NodeJS.Platform;
33
+ }
34
+ export declare function createFileNamer(layers: ResolvedLayer[]): (file: string) => string;
35
+ export declare function createPathMapper(workspaceDir: string, layers: ResolvedLayer[], options?: PathMapperOptions): PathMapper;
36
+ export declare function writeChildLine(name: string, line: string, ui?: Ui): void;
37
+ export declare class InstallProgress {
38
+ private resolved?;
39
+ private added?;
40
+ private installed?;
41
+ private isUpToDate;
42
+ read(line: string): boolean;
43
+ get label(): string;
44
+ get doneLabel(): string;
45
+ }
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InstallProgress = exports.OutputTail = exports.LineSplitter = exports.VERBOSE_OUTPUT_HINT = void 0;
4
+ exports.stripAnsi = stripAnsi;
5
+ exports.failureTail = failureTail;
6
+ exports.describeChildFailure = describeChildFailure;
7
+ exports.createFileNamer = createFileNamer;
8
+ exports.createPathMapper = createPathMapper;
9
+ exports.writeChildLine = writeChildLine;
10
+ const node_fs_1 = require("node:fs");
11
+ const cli_1 = require("@antelopejs/core/cli");
12
+ const layers_1 = require("./layers");
13
+ const output_1 = require("./output");
14
+ const ESC = "\u001b";
15
+ const ANSI_SEQUENCE = new RegExp(`${ESC}\\[[0-9;?]*[ -/]*[@-~]`, "g");
16
+ const LINE_BREAK = /\r?\n/;
17
+ const STACK_FRAME = /^\s+at\s/;
18
+ const NODE_DEPRECATION_LINES = [
19
+ /^\(node:\d+\) \[DEP\d+\]/,
20
+ /^\(Use `node --trace-/,
21
+ ];
22
+ const GUTTER_BARS = { unicode: "│", ascii: "|" };
23
+ const KEPT_OUTPUT_LINES = 200;
24
+ const FAILURE_TAIL_LINES = 15;
25
+ exports.VERBOSE_OUTPUT_HINT = "Run with --verbose for the full output.";
26
+ function stripAnsi(text) {
27
+ return text.replace(ANSI_SEQUENCE, "");
28
+ }
29
+ class LineSplitter {
30
+ constructor(onLine) {
31
+ this.onLine = onLine;
32
+ this.pending = "";
33
+ }
34
+ push(chunk) {
35
+ const lines = `${this.pending}${chunk}`.split(LINE_BREAK);
36
+ this.pending = lines.pop() ?? "";
37
+ lines.forEach((line) => this.onLine(line));
38
+ }
39
+ flush() {
40
+ if (this.pending !== "")
41
+ this.onLine(this.pending);
42
+ this.pending = "";
43
+ }
44
+ }
45
+ exports.LineSplitter = LineSplitter;
46
+ class OutputTail {
47
+ constructor(capacity = KEPT_OUTPUT_LINES) {
48
+ this.capacity = capacity;
49
+ this.kept = [];
50
+ }
51
+ push(line) {
52
+ this.kept.push(line);
53
+ if (this.kept.length > this.capacity)
54
+ this.kept.shift();
55
+ }
56
+ lines() {
57
+ return [...this.kept];
58
+ }
59
+ }
60
+ exports.OutputTail = OutputTail;
61
+ function failureTail(lines, count = FAILURE_TAIL_LINES, keepStack = false) {
62
+ const shown = lines
63
+ .map((line) => stripAnsi(line).trimEnd())
64
+ .filter((line) => line.trim() !== "")
65
+ .filter((line) => !NODE_DEPRECATION_LINES.some((notice) => notice.test(line)))
66
+ .filter((line) => keepStack || !STACK_FRAME.test(line));
67
+ return shown.slice(Math.max(0, shown.length - count));
68
+ }
69
+ function describeChildFailure(failure) {
70
+ const { title, command, code, lines, isVerbose } = failure;
71
+ if (isVerbose) {
72
+ return {
73
+ title,
74
+ reason: `${command} exited with code ${code}; its output is above.`,
75
+ };
76
+ }
77
+ return {
78
+ title,
79
+ reason: `${command} exited with code ${code}.`,
80
+ details: [...failureTail(lines), exports.VERBOSE_OUTPUT_HINT],
81
+ };
82
+ }
83
+ const PATH_END = "(?![\\w.@-])";
84
+ const RELATIVE_START = `(?:(?<=${ESC}\\[[0-9;]*m)|(?<![\\w.@/\\\\-]))`;
85
+ const LAYERS_SUBDIR = "frontend-modules";
86
+ function escapeRegExp(text) {
87
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
88
+ }
89
+ function pathPattern(path) {
90
+ return path
91
+ .split(/[\\/]/)
92
+ .map((segment) => escapeRegExp(segment))
93
+ .join("[\\\\/]");
94
+ }
95
+ function hasLocalSource(layer) {
96
+ return !!layer.sourcePath && (0, node_fs_1.existsSync)(layer.sourcePath);
97
+ }
98
+ function shownSource(layer, showSource) {
99
+ if (hasLocalSource(layer))
100
+ return showSource(layer.sourcePath ?? "");
101
+ return layer.packageName ?? (0, layers_1.getLayerSafeName)(layer);
102
+ }
103
+ function createFileNamer(layers) {
104
+ const packages = layers
105
+ .filter((layer) => layer.packageName && !hasLocalSource(layer))
106
+ .map((layer) => shownSource(layer, output_1.showPath));
107
+ return (file) => {
108
+ const owner = packages.find((name) => ["/", "\\"].some((separator) => file.startsWith(`${name}${separator}`)));
109
+ if (!owner)
110
+ return file;
111
+ return `${file.slice(owner.length + 1)} in ${owner}`;
112
+ };
113
+ }
114
+ function createPathMapper(workspaceDir, layers, options = {}) {
115
+ const { showSource = output_1.showPath, shownWorkspace = (0, output_1.showWorkspace)(workspaceDir), platform = process.platform, } = options;
116
+ const flags = platform === "win32" ? "gi" : "g";
117
+ const layerRules = layers
118
+ .filter((layer) => layer.packageName)
119
+ .flatMap((layer) => {
120
+ const shown = shownSource(layer, showSource);
121
+ const copy = pathPattern((0, layers_1.getLayerWorkspacePath)(workspaceDir, layer));
122
+ const relative = `${RELATIVE_START}${pathPattern(`${LAYERS_SUBDIR}/${(0, layers_1.getLayerSafeName)(layer)}`)}`;
123
+ return [copy, relative].map((pattern) => ({
124
+ pattern: new RegExp(`${pattern}${PATH_END}`, flags),
125
+ shown,
126
+ }));
127
+ });
128
+ const rules = [
129
+ ...layerRules,
130
+ {
131
+ pattern: new RegExp(`${pathPattern(workspaceDir)}${PATH_END}`, flags),
132
+ shown: shownWorkspace,
133
+ },
134
+ ];
135
+ return (line) => rules.reduce((mapped, { pattern, shown }) => mapped.replace(pattern, () => shown), line);
136
+ }
137
+ function hasFeedbackColor() {
138
+ return (0, cli_1.detectCapabilities)((0, cli_1.processCapabilityContext)()).colors.feedback;
139
+ }
140
+ function writeChildLine(name, line, ui = (0, cli_1.getProcessUi)()) {
141
+ const isUnicode = ui.symbols === cli_1.SYMBOL_SETS.unicode;
142
+ const bar = isUnicode ? GUTTER_BARS.unicode : GUTTER_BARS.ascii;
143
+ const text = hasFeedbackColor() ? line : stripAnsi(line);
144
+ (0, cli_1.getProcessTasks)().write(process.stderr, `${ui.palette().dim(`${name} ${bar}`)} ${text}\n`);
145
+ }
146
+ const PROGRESS_LINE = /^Progress: resolved (\d+), reused \d+, downloaded \d+, added (\d+)/;
147
+ const PACKAGES_LINE = /^Packages: \+(\d+)/;
148
+ const UP_TO_DATE_LINE = /^Already up to date/;
149
+ class InstallProgress {
150
+ constructor() {
151
+ this.isUpToDate = false;
152
+ }
153
+ read(line) {
154
+ const progress = PROGRESS_LINE.exec(line);
155
+ if (progress) {
156
+ this.resolved = Number(progress[1]);
157
+ this.added = Number(progress[2]);
158
+ return true;
159
+ }
160
+ const packages = PACKAGES_LINE.exec(line);
161
+ if (packages)
162
+ this.installed = Number(packages[1]);
163
+ if (UP_TO_DATE_LINE.test(line))
164
+ this.isUpToDate = true;
165
+ return false;
166
+ }
167
+ get label() {
168
+ if (this.resolved === undefined)
169
+ return "Installing dependencies";
170
+ return [
171
+ "Installing dependencies",
172
+ `${this.resolved} resolved, ${this.added ?? 0} added`,
173
+ ].join((0, cli_1.getProcessUi)().symbols.separator);
174
+ }
175
+ get doneLabel() {
176
+ if (this.installed !== undefined)
177
+ return `Installed ${(0, cli_1.pluralize)(this.installed, "package")}`;
178
+ if (this.isUpToDate)
179
+ return "Dependencies up to date";
180
+ return "Installed the dependencies";
181
+ }
182
+ }
183
+ exports.InstallProgress = InstallProgress;
@@ -0,0 +1,21 @@
1
+ import { CliError } from "@antelopejs/core/cli";
2
+ import { type FramedCommandResult } from "../common";
3
+ export interface BuildOptions {
4
+ backendUrl?: string;
5
+ force?: boolean;
6
+ offline?: boolean;
7
+ bootstrapSecret?: string;
8
+ }
9
+ interface BuildStep {
10
+ script: string;
11
+ name: string;
12
+ subject: string;
13
+ running: string;
14
+ done: string;
15
+ failed: string;
16
+ stopped: string;
17
+ }
18
+ export declare const BUILD_STEPS: BuildStep[];
19
+ export declare function buildStepFailure(step: BuildStep, result: FramedCommandResult, isVerbose: boolean, rerun?: string, nameFile?: (file: string) => string): CliError;
20
+ export declare function runBuild(options: BuildOptions): Promise<void>;
21
+ export {};
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BUILD_STEPS = void 0;
4
+ exports.buildStepFailure = buildStepFailure;
5
+ exports.runBuild = runBuild;
6
+ const node_path_1 = require("node:path");
7
+ const cli_1 = require("@antelopejs/core/cli");
8
+ const child_output_1 = require("../child-output");
9
+ const common_1 = require("../common");
10
+ const output_1 = require("../output");
11
+ const vite_output_1 = require("../vite-output");
12
+ const workspace_task_1 = require("./workspace-task");
13
+ exports.BUILD_STEPS = [
14
+ {
15
+ script: "build:client",
16
+ name: "client",
17
+ subject: "client bundle",
18
+ running: "Building the client bundle",
19
+ done: "Built the client bundle",
20
+ failed: "Client bundle failed",
21
+ stopped: "Client bundle stopped",
22
+ },
23
+ {
24
+ script: "build:compress",
25
+ name: "compress",
26
+ subject: "compressed assets",
27
+ running: "Compressing the client assets",
28
+ done: "Compressed the client assets",
29
+ failed: "Asset compression failed",
30
+ stopped: "Asset compression stopped",
31
+ },
32
+ {
33
+ script: "build:ssr",
34
+ name: "ssr",
35
+ subject: "SSR bundle",
36
+ running: "Building the SSR bundle",
37
+ done: "Built the SSR bundle",
38
+ failed: "SSR bundle failed",
39
+ stopped: "SSR bundle stopped",
40
+ },
41
+ {
42
+ script: "build:email",
43
+ name: "email",
44
+ subject: "e-mail bundle",
45
+ running: "Building the e-mail bundle",
46
+ done: "Built the e-mail bundle",
47
+ failed: "E-mail bundle failed",
48
+ stopped: "E-mail bundle stopped",
49
+ },
50
+ ];
51
+ function buildStepFailure(step, result, isVerbose, rerun, nameFile) {
52
+ const problem = (0, vite_output_1.describeViteFailure)(result.lines, step.subject, isVerbose, rerun, nameFile) ??
53
+ (0, child_output_1.describeChildFailure)({
54
+ title: "The production build failed",
55
+ command: `pnpm run ${step.script}`,
56
+ code: result.code,
57
+ lines: result.lines,
58
+ isVerbose,
59
+ });
60
+ return new cli_1.CliError(problem);
61
+ }
62
+ async function runBuildStep(step, context) {
63
+ const task = (0, cli_1.getProcessTasks)().start(step.running);
64
+ let result;
65
+ try {
66
+ result = await (0, common_1.runFramedCommand)("pnpm", ["--silent", "run", step.script], {
67
+ name: step.name,
68
+ cwd: context.workspaceDir,
69
+ env: context.env,
70
+ mapLine: context.mapLine,
71
+ onLine: (line) => context.warnings.read(line),
72
+ });
73
+ }
74
+ catch (error) {
75
+ (0, output_1.endInterruptedTask)(task, error, step.stopped);
76
+ throw error;
77
+ }
78
+ if (result.code !== 0) {
79
+ task.fail(step.failed);
80
+ throw buildStepFailure(step, result, context.isVerbose, undefined, context.nameFile);
81
+ }
82
+ task.succeed(step.done);
83
+ }
84
+ async function runBuild(options) {
85
+ const startedAt = Date.now();
86
+ try {
87
+ await buildProduction(options, startedAt);
88
+ }
89
+ catch (error) {
90
+ if (!(error instanceof common_1.CancelledError))
91
+ throw error;
92
+ throw new common_1.CancelledError(error.signal, "Stopped the production build", `ran ${(0, cli_1.formatDuration)(Date.now() - startedAt)}`);
93
+ }
94
+ }
95
+ async function buildProduction(options, startedAt) {
96
+ const backendUrl = (0, common_1.requireBackendUrl)(options.backendUrl);
97
+ const sessionSecret = (0, common_1.resolveSessionSecret)("build");
98
+ const bootstrapSecret = (0, common_1.normalizeBootstrapSecret)(options.bootstrapSecret);
99
+ const ui = (0, cli_1.getProcessUi)();
100
+ (0, output_1.writeHeader)("build", [backendUrl, "production"]);
101
+ const { workspaceDir, layers, manifestFromCache, manifestFetchedAt } = await (0, workspace_task_1.setUpWorkspace)({
102
+ backendUrl,
103
+ force: !!options.force,
104
+ mode: "build",
105
+ offline: (0, common_1.flagOrEnv)(options.offline, "DMS_OFFLINE"),
106
+ bootstrapSecret,
107
+ });
108
+ if (manifestFromCache) {
109
+ ui.message("warn", `Building from the manifest and layers archive${(0, output_1.cachedAge)(manifestFetchedAt)}`, { detail: "The output may not match the current backend." });
110
+ }
111
+ ui.message("info", "Building for production", {
112
+ detail: `Workspace ${(0, output_1.showWorkspace)(workspaceDir)}`,
113
+ });
114
+ const isVerbose = (0, cli_1.isVerboseRun)();
115
+ const context = {
116
+ workspaceDir,
117
+ isVerbose,
118
+ warnings: new vite_output_1.ViteWarnings(),
119
+ mapLine: (0, child_output_1.createPathMapper)(workspaceDir, layers),
120
+ nameFile: (0, child_output_1.createFileNamer)(layers),
121
+ env: {
122
+ ...process.env,
123
+ DMS_SESSION_SECRET: sessionSecret,
124
+ NODE_OPTIONS: "--max-old-space-size=4096",
125
+ NODE_PATH: (0, node_path_1.join)(workspaceDir, "node_modules"),
126
+ [vite_output_1.VITE_LOG_LEVEL_VARIABLE]: (0, vite_output_1.viteLogLevel)(isVerbose),
127
+ },
128
+ };
129
+ for (const step of exports.BUILD_STEPS)
130
+ await runBuildStep(step, context);
131
+ (0, vite_output_1.reportViteWarnings)(context.warnings, isVerbose, ui);
132
+ (0, output_1.writeBlankLine)();
133
+ ui.summary({
134
+ headline: "Built the production frontend",
135
+ artifact: `${(0, output_1.showWorkspace)(workspaceDir)}${node_path_1.sep}dist`,
136
+ durationMs: Date.now() - startedAt,
137
+ nextSteps: [
138
+ {
139
+ command: `ajs dms start -b ${backendUrl}`,
140
+ description: "with the DMS_SESSION_SECRET this build used",
141
+ },
142
+ ],
143
+ });
144
+ }