@antelopejs/dms-frontend 0.3.8 → 0.3.10

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 +87 -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 +71 -0
  42. package/dist/index.js +129 -38
  43. package/dist/layer-watch.d.ts +2 -1
  44. package/dist/layer-watch.js +129 -26
  45. package/dist/layers.d.ts +9 -2
  46. package/dist/layers.js +82 -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 +43 -0
  53. package/dist/output.js +214 -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 +226 -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 +97 -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 +122 -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
@@ -1,15 +1,12 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.MANIFEST_SECRET_ENV = void 0;
7
4
  exports.collectManifestSecrets = collectManifestSecrets;
8
5
  exports.resolveManifestSecrets = resolveManifestSecrets;
9
6
  exports.formatSecretConflicts = formatSecretConflicts;
10
- exports.formatSecretSources = formatSecretSources;
7
+ exports.describeSecretSources = describeSecretSources;
11
8
  exports.reportManifestSecrets = reportManifestSecrets;
12
- const chalk_1 = __importDefault(require("chalk"));
9
+ const cli_1 = require("@antelopejs/core/cli");
13
10
  exports.MANIFEST_SECRET_ENV = {
14
11
  DMS_HTML_RENDER_SECRET: ["htmlRender", "serviceSecret"],
15
12
  DMS_OAUTH_RELAY_SECRET: ["oauth", "relaySecret"],
@@ -80,22 +77,29 @@ function resolveManifestSecrets(secrets, env = process.env) {
80
77
  function formatSecretConflicts(conflicts) {
81
78
  return conflicts.map(({ name, used, ignored }) => `${name}: ${ignored.join(", ")} publish${ignored.length === 1 ? "es" : ""} a different value than ${used}; using ${used}'s`);
82
79
  }
83
- function formatSecretSources(sources, manifestLabel = "manifest") {
84
- const width = Math.max(...SECRET_NAMES.map((name) => name.length));
85
- return SECRET_NAMES.map((name) => {
86
- const source = sources[name];
87
- const label = source === "manifest"
88
- ? manifestLabel
89
- : source === "not set"
90
- ? `not set (${UNSET_CONSEQUENCE[name]})`
91
- : source;
92
- return `${name.padEnd(width)} ${label}`;
93
- });
80
+ const SOURCE_LABELS = {
81
+ env: "the environment",
82
+ manifest: "the manifest",
83
+ };
84
+ function describeSecretSources(sources, manifestLabel = SOURCE_LABELS.manifest) {
85
+ const labels = { ...SOURCE_LABELS, manifest: manifestLabel };
86
+ const groups = ["env", "manifest"]
87
+ .map((source) => ({
88
+ names: SECRET_NAMES.filter((name) => sources[name] === source),
89
+ label: labels[source],
90
+ }))
91
+ .filter(({ names }) => names.length > 0)
92
+ .map(({ names, label }) => `${names.join(", ")} from ${label}`);
93
+ return groups.length > 0 ? groups.join("; ") : undefined;
94
94
  }
95
- function reportManifestSecrets(resolved, manifestLabel = "manifest", log = console.log) {
95
+ function reportManifestSecrets(resolved, fix, ui = (0, cli_1.getProcessUi)()) {
96
96
  for (const line of formatSecretConflicts(resolved.conflicts))
97
- log(`${chalk_1.default.yellow.bold("⚠")} ${chalk_1.default.yellow(line)}`);
98
- log(chalk_1.default.dim(" Server secrets:"));
99
- for (const line of formatSecretSources(resolved.sources, manifestLabel))
100
- log(chalk_1.default.dim(` ${line}`));
97
+ ui.message("warn", line);
98
+ const unset = SECRET_NAMES.filter((name) => resolved.sources[name] === "not set");
99
+ unset.forEach((name, index) => {
100
+ const isLast = index === unset.length - 1;
101
+ ui.message("warn", `${name} is not set: ${UNSET_CONSEQUENCE[name]}`, {
102
+ details: isLast ? [`${ui.symbols.levels.hint} ${fix(unset.length)}`] : [],
103
+ });
104
+ });
101
105
  }
@@ -1,11 +1,27 @@
1
+ import { CliError, type CliProblem } from "@antelopejs/core/cli";
1
2
  import { Manifest, ManifestModule } from "./workspace";
2
- export declare class ManifestUnauthorizedError extends Error {
3
+ export declare class ManifestUnauthorizedError extends CliError {
3
4
  readonly url: string;
4
5
  readonly status: number;
5
- constructor(url: string, status: number);
6
+ constructor(url: string, status: number, credentialSent: boolean);
6
7
  }
7
- export declare class ManifestRefusedError extends Error {
8
- constructor(reason: string);
8
+ export declare class BackendUnreachableError extends CliError {
9
+ readonly url: string;
10
+ readonly code?: string;
11
+ constructor(url: string, failure: unknown);
12
+ }
13
+ export declare function displayUrl(url: string | URL): string;
14
+ export declare function backendEndpoint(backendUrl: string, path: string): URL;
15
+ export declare function fetchFromBackend(backendUrl: string, url: URL, bootstrapSecret?: string): Promise<Response>;
16
+ export declare class ManifestRefusedError extends CliError {
17
+ constructor(problem: string | CliProblem);
18
+ }
19
+ export declare function describeManifestVersion(version: unknown): CliProblem;
20
+ export declare class BackendResponseError extends CliError {
21
+ constructor(subject: string, url: string | URL, response: Pick<Response, "status" | "statusText">);
22
+ }
23
+ export declare class NoCachedManifestError extends CliError {
24
+ constructor();
9
25
  }
10
26
  export declare function fetchManifest(backendUrl: string, clientUrl?: string, bootstrapSecret?: string): Promise<Manifest>;
11
27
  export interface ManifestCacheEntry {
@@ -13,12 +29,13 @@ export interface ManifestCacheEntry {
13
29
  fetchedAt: string;
14
30
  }
15
31
  export declare function readCachedManifest(workspaceDir: string): ManifestCacheEntry | undefined;
32
+ export type ManifestCacheUse = "fallback" | "offline" | "never";
16
33
  export interface ResolvedManifest {
17
34
  manifest: Manifest;
18
35
  fromCache: boolean;
19
36
  fetchedAt?: string;
20
37
  }
21
- export declare function resolveManifest(workspaceDir: string, backendUrl: string, offline: boolean, clientUrl?: string, bootstrapSecret?: string): Promise<ResolvedManifest>;
38
+ export declare function resolveManifest(workspaceDir: string, backendUrl: string, cacheUse: ManifestCacheUse, clientUrl?: string, bootstrapSecret?: string): Promise<ResolvedManifest>;
22
39
  export declare function assertCachedLayerPathsExist(modules: ServedWithPath[], fetchedAt?: string): void;
23
40
  export type ServedWithPath = ManifestModule & {
24
41
  path: string;
package/dist/manifest.js CHANGED
@@ -1,53 +1,192 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ManifestRefusedError = exports.ManifestUnauthorizedError = void 0;
3
+ exports.NoCachedManifestError = exports.BackendResponseError = exports.ManifestRefusedError = exports.BackendUnreachableError = exports.ManifestUnauthorizedError = void 0;
4
+ exports.displayUrl = displayUrl;
5
+ exports.backendEndpoint = backendEndpoint;
6
+ exports.fetchFromBackend = fetchFromBackend;
7
+ exports.describeManifestVersion = describeManifestVersion;
4
8
  exports.fetchManifest = fetchManifest;
5
9
  exports.readCachedManifest = readCachedManifest;
6
10
  exports.resolveManifest = resolveManifest;
7
11
  exports.assertCachedLayerPathsExist = assertCachedLayerPathsExist;
8
12
  const node_fs_1 = require("node:fs");
9
13
  const node_path_1 = require("node:path");
14
+ const cli_1 = require("@antelopejs/core/cli");
10
15
  const config_1 = require("./config");
16
+ const output_1 = require("./output");
11
17
  const workspace_1 = require("./workspace");
12
- class ManifestUnauthorizedError extends Error {
13
- constructor(url, status) {
14
- super(`Backend refused the layers request (${status}) at ${url}.\n` +
15
- " This DMS backend requires a bootstrap credential for its layer endpoints.\n" +
16
- " • Production/CI: set DMS_BOOTSTRAP_SECRET to the backend's frontend.bootstrapSecret.\n" +
17
- " • Local dev: run inside the antelope project started with `ajs project dev` —\n" +
18
- " the credential is read from .antelope/dms-dev.json automatically.");
18
+ class ManifestUnauthorizedError extends cli_1.CliError {
19
+ constructor(url, status, credentialSent) {
20
+ super({
21
+ title: credentialSent
22
+ ? `The backend refused the bootstrap credential (${status}) at ${url}`
23
+ : `The backend requires a bootstrap credential (${status}) at ${url}, and none was sent`,
24
+ fixes: [
25
+ "Production/CI: set DMS_BOOTSTRAP_SECRET to the backend's frontend.bootstrapSecret",
26
+ "Local dev: run inside the antelope project started with `ajs project dev`; the credential is read from .antelope/dms-dev.json automatically",
27
+ ],
28
+ });
19
29
  this.url = url;
20
30
  this.status = status;
21
31
  this.name = "ManifestUnauthorizedError";
22
32
  }
23
33
  }
24
34
  exports.ManifestUnauthorizedError = ManifestUnauthorizedError;
25
- class ManifestRefusedError extends Error {
26
- constructor(reason) {
27
- super(reason);
35
+ const UNREACHABLE_REASONS = {
36
+ ECONNREFUSED: "Connection refused",
37
+ ECONNRESET: "Connection reset",
38
+ ENOTFOUND: "Unknown host",
39
+ EAI_AGAIN: "Host name lookup failed",
40
+ ETIMEDOUT: "Connection timed out",
41
+ UND_ERR_CONNECT_TIMEOUT: "Connection timed out",
42
+ };
43
+ const UNKNOWN_HOST_CODES = ["ENOTFOUND", "EAI_AGAIN"];
44
+ class BackendUnreachableError extends cli_1.CliError {
45
+ constructor(url, failure) {
46
+ const cause = failure?.cause ?? failure;
47
+ const code = errorCode(cause);
48
+ const detail = code
49
+ ? (UNREACHABLE_REASONS[code] ?? errorMessage(cause))
50
+ : `The request failed: ${errorMessage(cause)}`;
51
+ const fix = code && UNKNOWN_HOST_CODES.includes(code)
52
+ ? `Check the host name '${new URL(url).hostname}' in -b <url> or DMS_API_BASE_URL`
53
+ : "Start the backend (`ajs project dev` for a local one), or pass its URL with -b <url>";
54
+ super({
55
+ title: `Cannot reach the DMS backend at ${url}`,
56
+ reason: `${code ? `${detail} (${code})` : detail}.`,
57
+ fixes: [fix],
58
+ }, { cause: failure });
59
+ this.url = url;
60
+ this.name = "BackendUnreachableError";
61
+ this.code = code;
62
+ }
63
+ }
64
+ exports.BackendUnreachableError = BackendUnreachableError;
65
+ function errorCode(err) {
66
+ const { code, errors } = (err ?? {});
67
+ if (typeof code === "string")
68
+ return code;
69
+ return Array.isArray(errors) ? errorCode(errors[0]) : undefined;
70
+ }
71
+ function errorMessage(err) {
72
+ return err instanceof Error ? err.message : String(err);
73
+ }
74
+ function displayUrl(url) {
75
+ const { origin, pathname } = new URL(url);
76
+ return `${origin}${pathname.replace(/\/$/, "")}`;
77
+ }
78
+ function backendEndpoint(backendUrl, path) {
79
+ try {
80
+ return new URL(`${(0, config_1.parseBackendUrl)(backendUrl)}${path}`);
81
+ }
82
+ catch (err) {
83
+ if (!(err instanceof config_1.UsageError))
84
+ throw err;
85
+ throw new cli_1.CliError({ ...err.problem, exitCode: cli_1.FAILURE_EXIT_CODE }, { cause: err });
86
+ }
87
+ }
88
+ async function fetchFromBackend(backendUrl, url, bootstrapSecret) {
89
+ let response;
90
+ try {
91
+ response = await fetch(url, { headers: (0, workspace_1.bootstrapHeaders)(bootstrapSecret) });
92
+ }
93
+ catch (err) {
94
+ throw new BackendUnreachableError(displayUrl(backendUrl), err);
95
+ }
96
+ if ((0, workspace_1.isUnauthorized)(response.status)) {
97
+ throw new ManifestUnauthorizedError(displayUrl(url), response.status, !!bootstrapSecret);
98
+ }
99
+ return response;
100
+ }
101
+ class ManifestRefusedError extends cli_1.CliError {
102
+ constructor(problem) {
103
+ super(typeof problem === "string" ? { title: problem } : problem);
28
104
  this.name = "ManifestRefusedError";
29
105
  }
30
106
  }
31
107
  exports.ManifestRefusedError = ManifestRefusedError;
108
+ const OWN_RELEASE = require("../package.json");
109
+ const BACKEND_PACKAGE = "@antelopejs/dms";
110
+ const LOADER_UPDATE_COMMAND = "ajs update dms";
111
+ function describeManifestVersion(version) {
112
+ const loader = `${OWN_RELEASE.name} ${OWN_RELEASE.version}`;
113
+ const supported = `This loader reads version ${workspace_1.FRONTEND_MANIFEST_VERSION}`;
114
+ if (typeof version !== "number") {
115
+ return {
116
+ title: "The backend served a frontend manifest without a protocol version",
117
+ reason: `${supported}; the answer does not look like one from ${BACKEND_PACKAGE}.`,
118
+ fixes: [
119
+ `Check that -b <url> points at a DMS backend running ${BACKEND_PACKAGE}`,
120
+ ],
121
+ };
122
+ }
123
+ const title = `The backend serves frontend manifest version ${version}, which ${loader} cannot read`;
124
+ if (version > workspace_1.FRONTEND_MANIFEST_VERSION) {
125
+ return {
126
+ title,
127
+ reason: `${supported}; the backend's ${BACKEND_PACKAGE} is newer than this loader.`,
128
+ fixes: [
129
+ `Upgrade the loader (${LOADER_UPDATE_COMMAND}) to a release that reads version ${version}`,
130
+ ],
131
+ };
132
+ }
133
+ return {
134
+ title,
135
+ reason: `${supported}; the backend's ${BACKEND_PACKAGE} is older than this loader.`,
136
+ fixes: [
137
+ `Upgrade ${BACKEND_PACKAGE} on the backend, or use a ${OWN_RELEASE.name} release that reads version ${version}`,
138
+ ],
139
+ };
140
+ }
141
+ class BackendResponseError extends cli_1.CliError {
142
+ constructor(subject, url, response) {
143
+ const { status, statusText } = response;
144
+ const answer = `HTTP ${status}${statusText ? ` ${statusText}` : ""}`;
145
+ const shown = displayUrl(url);
146
+ const problem = status >= 500
147
+ ? {
148
+ title: `The DMS backend failed to serve ${subject} (${answer})`,
149
+ reason: `${shown} answered ${status}: the backend failed on the request.`,
150
+ fixes: [
151
+ "Check the backend logs (the `ajs project dev` output for a local backend), then run again",
152
+ ],
153
+ }
154
+ : {
155
+ title: `The DMS backend did not serve ${subject} (${answer})`,
156
+ reason: `${shown} answered ${status}.`,
157
+ fixes: [
158
+ `Check that -b <url> points at a DMS backend running ${BACKEND_PACKAGE}`,
159
+ ],
160
+ };
161
+ super(problem);
162
+ this.name = "BackendResponseError";
163
+ }
164
+ }
165
+ exports.BackendResponseError = BackendResponseError;
166
+ class NoCachedManifestError extends cli_1.CliError {
167
+ constructor() {
168
+ super({
169
+ title: "No cached manifest for this workspace",
170
+ fixes: ["Run once with the backend reachable before using --offline"],
171
+ });
172
+ this.name = "NoCachedManifestError";
173
+ }
174
+ }
175
+ exports.NoCachedManifestError = NoCachedManifestError;
32
176
  async function fetchManifest(backendUrl, clientUrl, bootstrapSecret) {
33
- const frontendUrl = new URL(`${backendUrl}/dms/frontend`);
177
+ const frontendUrl = backendEndpoint(backendUrl, "/dms/frontend");
34
178
  frontendUrl.searchParams.set("renderer", "vue");
35
179
  frontendUrl.searchParams.set("rendererVersion", "3");
36
180
  if (clientUrl) {
37
181
  frontendUrl.searchParams.set("clientUrl", clientUrl);
38
182
  }
39
- const response = await fetch(frontendUrl, {
40
- headers: (0, workspace_1.bootstrapHeaders)(bootstrapSecret),
41
- });
42
- if ((0, workspace_1.isUnauthorized)(response.status)) {
43
- throw new ManifestUnauthorizedError(frontendUrl.href, response.status);
44
- }
183
+ const response = await fetchFromBackend(backendUrl, frontendUrl, bootstrapSecret);
45
184
  if (!response.ok) {
46
- throw new Error(`Failed to fetch manifest from ${frontendUrl.href} (${response.status})`);
185
+ throw new BackendResponseError("the frontend manifest", frontendUrl, response);
47
186
  }
48
187
  const manifest = (await response.json());
49
188
  if (manifest.version !== workspace_1.FRONTEND_MANIFEST_VERSION) {
50
- throw new ManifestRefusedError(`Unsupported frontend manifest version: ${String(manifest.version)}`);
189
+ throw new ManifestRefusedError(describeManifestVersion(manifest.version));
51
190
  }
52
191
  const incompatible = manifest.modules.filter((module) => module.renderer?.name !== "vue" || module.renderer?.version !== "3");
53
192
  if (incompatible.length) {
@@ -77,8 +216,8 @@ function writeCachedManifest(workspaceDir, manifest) {
77
216
  };
78
217
  (0, config_1.writeSecretBearingFile)((0, node_path_1.join)(workspaceDir, MANIFEST_CACHE_FILE), `${JSON.stringify(entry, null, 2)}\n`);
79
218
  }
80
- async function resolveManifest(workspaceDir, backendUrl, offline, clientUrl, bootstrapSecret) {
81
- if (!offline) {
219
+ async function resolveManifest(workspaceDir, backendUrl, cacheUse, clientUrl, bootstrapSecret) {
220
+ if (cacheUse !== "offline") {
82
221
  try {
83
222
  const manifest = await fetchManifest(backendUrl, clientUrl, bootstrapSecret);
84
223
  writeCachedManifest(workspaceDir, manifest);
@@ -92,15 +231,30 @@ async function resolveManifest(workspaceDir, backendUrl, offline, clientUrl, boo
92
231
  const cached = readCachedManifest(workspaceDir);
93
232
  if (!cached)
94
233
  throw err;
234
+ if (cacheUse === "never")
235
+ throw cacheNotUsed(err, backendUrl, cached);
95
236
  return fromCachedEntry(cached);
96
237
  }
97
238
  }
98
239
  const cached = readCachedManifest(workspaceDir);
99
- if (!cached) {
100
- throw new Error("No cached manifest for this workspace — run once with the backend reachable before using --offline.");
101
- }
240
+ if (!cached)
241
+ throw new NoCachedManifestError();
102
242
  return fromCachedEntry(cached);
103
243
  }
244
+ function cacheNotUsed(failure, backendUrl, cached) {
245
+ const problem = failure instanceof cli_1.CliError
246
+ ? failure.problem
247
+ : { title: errorMessage(failure) };
248
+ const cache = `A manifest${(0, output_1.cachedAge)(cached.fetchedAt)} exists; a build uses it only with --offline.`;
249
+ return new cli_1.CliError({
250
+ ...problem,
251
+ reason: problem.reason ? `${problem.reason} ${cache}` : cache,
252
+ fixes: [
253
+ ...(problem.fixes ?? []),
254
+ `Or build from the cache on purpose: ajs dms build -b ${displayUrl(backendUrl)} --offline`,
255
+ ],
256
+ }, { cause: failure });
257
+ }
104
258
  function fromCachedEntry(cached) {
105
259
  return {
106
260
  manifest: cached.manifest,
@@ -112,7 +266,9 @@ function assertCachedLayerPathsExist(modules, fetchedAt) {
112
266
  const missing = modules.filter((mod) => !(0, node_fs_1.existsSync)(mod.path));
113
267
  if (missing.length === 0)
114
268
  return;
115
- throw new Error(`Cached manifest${fetchedAt ? ` (fetched ${fetchedAt})` : ""} references layer paths that no longer exist:\n` +
116
- missing.map((mod) => ` - ${mod.name}: ${mod.path}`).join("\n") +
117
- "\nStart the backend and re-run to refresh the cache.");
269
+ throw new cli_1.CliError({
270
+ title: `The cached manifest${fetchedAt ? ` (fetched ${(0, output_1.formatAge)(fetchedAt)})` : ""} references layer paths that no longer exist`,
271
+ fixes: ["Start the backend and run again to refresh the cache"],
272
+ details: missing.map((mod) => `${mod.name}: ${mod.path}`),
273
+ });
118
274
  }
@@ -9,6 +9,7 @@ exports.writeFrontendModuleRegistry = writeFrontendModuleRegistry;
9
9
  exports.writeDmsMainCss = writeDmsMainCss;
10
10
  const node_fs_1 = require("node:fs");
11
11
  const node_path_1 = require("node:path");
12
+ const cli_1 = require("@antelopejs/core/cli");
12
13
  const config_1 = require("./config");
13
14
  const derived_outputs_1 = require("./derived-outputs");
14
15
  const layers_1 = require("./layers");
@@ -83,7 +84,7 @@ function collectAuthEstablishEndpoints(layers) {
83
84
  endpoints.add(endpoint);
84
85
  continue;
85
86
  }
86
- console.warn(`⚠ Ignoring malformed authEstablishEndpoints entry ${JSON.stringify(endpoint)} ` +
87
+ (0, cli_1.getProcessUi)().message("warn", `Ignoring malformed authEstablishEndpoints entry ${JSON.stringify(endpoint)} ` +
87
88
  `declared by ${layer.packageName ?? layer.path}: expected an absolute ` +
88
89
  "backend API path under /api/ with no query string.");
89
90
  }
@@ -0,0 +1,43 @@
1
+ import { type TaskHandle, type Ui } from "@antelopejs/core/cli";
2
+ import { CancelledError } from "./cancellation";
3
+ export declare function exitOnBrokenPipe(stream?: NodeJS.WriteStream): void;
4
+ export declare function isTerminalFeedback(): boolean;
5
+ export declare function isTerminalResult(): boolean;
6
+ export declare function writeFeedback(line: string): void;
7
+ export declare function writeInfo(line: string): void;
8
+ export declare function writeHeader(command: string, context: string[], ui?: Ui): void;
9
+ export declare function writeBlankLine(): void;
10
+ export declare function showPath(target: string, home?: string): string;
11
+ export declare function showWorkspace(dir: string, isTerminal?: boolean, home?: string, ui?: Ui): string;
12
+ export declare function workspaceId(dir: string, isTerminal: boolean): string;
13
+ export declare function formatSize(bytes: number): string;
14
+ export declare function formatAge(iso: string, now?: number): string;
15
+ export declare function cachedAge(fetchedAt: string | undefined): string;
16
+ export declare function failureDetails(error: unknown, ui?: Ui): string[];
17
+ export declare function endInterruptedTask(task: TaskHandle, error: unknown, stopped: string): void;
18
+ export declare function reportStopped(stop: CancelledError, ui?: Ui): void;
19
+ export interface ReadyLine {
20
+ label: string;
21
+ value: string;
22
+ isLink?: boolean;
23
+ isEssential?: boolean;
24
+ }
25
+ export interface ReadyBlock {
26
+ title: string;
27
+ lines: ReadyLine[];
28
+ footer: string;
29
+ }
30
+ export declare function formatReadyBlock(block: ReadyBlock, ui?: Ui, isQuiet?: boolean): string[];
31
+ export declare function writeReadyBlock(block: ReadyBlock, ui?: Ui): void;
32
+ declare const TIMED_LEVEL_COLORS: {
33
+ readonly success: "green";
34
+ readonly info: "blue";
35
+ readonly warn: "yellow";
36
+ };
37
+ type TimedLevel = keyof typeof TIMED_LEVEL_COLORS;
38
+ export interface TimedMessageOptions {
39
+ details?: readonly string[];
40
+ fixes?: readonly string[];
41
+ }
42
+ export declare function formatTimedMessage(level: TimedLevel, text: string, options?: TimedMessageOptions, ui?: Ui, now?: Date): string;
43
+ export {};
package/dist/output.js ADDED
@@ -0,0 +1,214 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.exitOnBrokenPipe = exitOnBrokenPipe;
4
+ exports.isTerminalFeedback = isTerminalFeedback;
5
+ exports.isTerminalResult = isTerminalResult;
6
+ exports.writeFeedback = writeFeedback;
7
+ exports.writeInfo = writeInfo;
8
+ exports.writeHeader = writeHeader;
9
+ exports.writeBlankLine = writeBlankLine;
10
+ exports.showPath = showPath;
11
+ exports.showWorkspace = showWorkspace;
12
+ exports.workspaceId = workspaceId;
13
+ exports.formatSize = formatSize;
14
+ exports.formatAge = formatAge;
15
+ exports.cachedAge = cachedAge;
16
+ exports.failureDetails = failureDetails;
17
+ exports.endInterruptedTask = endInterruptedTask;
18
+ exports.reportStopped = reportStopped;
19
+ exports.formatReadyBlock = formatReadyBlock;
20
+ exports.writeReadyBlock = writeReadyBlock;
21
+ exports.formatTimedMessage = formatTimedMessage;
22
+ const node_os_1 = require("node:os");
23
+ const node_path_1 = require("node:path");
24
+ const cli_1 = require("@antelopejs/core/cli");
25
+ const cancellation_1 = require("./cancellation");
26
+ const HOME_PREFIX = "~";
27
+ const CURRENT_DIRECTORY = ".";
28
+ const WORKSPACE_ID_LENGTH = 8;
29
+ const LINE_BREAK = /\r?\n/;
30
+ const CARRIAGE_RETURN = "\r";
31
+ const STOPPED_SYMBOLS = { unicode: "■", ascii: "x" };
32
+ const LINK_SYMBOLS = { unicode: "➜", ascii: ">" };
33
+ const BLOCK_INDENT = " ";
34
+ const LINK_GAP = " ";
35
+ const LABEL_SUFFIX = ":";
36
+ const TIME_LENGTH = 8;
37
+ const BYTES_PER_UNIT = 1024;
38
+ const SIZE_UNITS = ["B", "KB", "MB", "GB", "TB"];
39
+ const DECIMAL_SIZE_LIMIT = 10;
40
+ const MS_PER_MINUTE = 60_000;
41
+ const MINUTES_PER_HOUR = 60;
42
+ const HOURS_PER_DAY = 24;
43
+ function isUnicode(ui) {
44
+ return ui.symbols === cli_1.SYMBOL_SETS.unicode;
45
+ }
46
+ function exitOnBrokenPipe(stream = process.stdout) {
47
+ stream.on("error", (error) => {
48
+ if (error.code !== "EPIPE")
49
+ throw error;
50
+ process.exit(cli_1.SUCCESS_EXIT_CODE);
51
+ });
52
+ }
53
+ function isTerminalFeedback() {
54
+ return (0, cli_1.detectCapabilities)((0, cli_1.processCapabilityContext)()).terminals.feedback;
55
+ }
56
+ function isTerminalResult() {
57
+ return (0, cli_1.detectCapabilities)((0, cli_1.processCapabilityContext)()).terminals.result;
58
+ }
59
+ function writeFeedback(line) {
60
+ (0, cli_1.getProcessTasks)().write(process.stderr, `${line}\n`);
61
+ }
62
+ function writeInfo(line) {
63
+ if (!(0, cli_1.isQuietRun)())
64
+ writeFeedback(line);
65
+ }
66
+ function writeHeader(command, context, ui = (0, cli_1.getProcessUi)()) {
67
+ const title = ui.palette().bold(`ajs dms ${command}`);
68
+ writeInfo(`${title} ${context.join(ui.symbols.separator)}`);
69
+ }
70
+ function writeBlankLine() {
71
+ writeInfo("");
72
+ }
73
+ function showPath(target, home = (0, node_os_1.homedir)()) {
74
+ if ((0, node_path_1.resolve)(target) === process.cwd())
75
+ return CURRENT_DIRECTORY;
76
+ const shown = (0, cli_1.displayPath)(target);
77
+ if (shown !== target)
78
+ return shown;
79
+ const fromHome = (0, node_path_1.relative)(home, target);
80
+ const isInHome = fromHome !== "" && !(0, node_path_1.isAbsolute)(fromHome) && fromHome.split(node_path_1.sep)[0] !== "..";
81
+ return isInHome ? (0, node_path_1.join)(HOME_PREFIX, fromHome) : target;
82
+ }
83
+ function showWorkspace(dir, isTerminal = isTerminalFeedback(), home = (0, node_os_1.homedir)(), ui = (0, cli_1.getProcessUi)()) {
84
+ const shown = showPath(dir, home);
85
+ const id = (0, node_path_1.basename)(dir);
86
+ if (!isTerminal || id.length <= WORKSPACE_ID_LENGTH)
87
+ return shown;
88
+ return (0, node_path_1.join)((0, node_path_1.dirname)(shown), `${id.slice(0, WORKSPACE_ID_LENGTH)}${ui.symbols.ellipsis}`);
89
+ }
90
+ function workspaceId(dir, isTerminal) {
91
+ const id = (0, node_path_1.basename)(dir);
92
+ return isTerminal ? id.slice(0, WORKSPACE_ID_LENGTH) : id;
93
+ }
94
+ function formatSize(bytes) {
95
+ let value = bytes;
96
+ let unit = 0;
97
+ while (value >= BYTES_PER_UNIT && unit < SIZE_UNITS.length - 1) {
98
+ value /= BYTES_PER_UNIT;
99
+ unit += 1;
100
+ }
101
+ const shown = unit > 0 && value < DECIMAL_SIZE_LIMIT
102
+ ? String(Math.round(value * 10) / 10)
103
+ : String(Math.round(value));
104
+ return `${shown} ${SIZE_UNITS[unit]}`;
105
+ }
106
+ function formatAge(iso, now = Date.now()) {
107
+ const time = Date.parse(iso);
108
+ if (Number.isNaN(time))
109
+ return iso;
110
+ const minutes = Math.floor(Math.max(0, now - time) / MS_PER_MINUTE);
111
+ if (minutes < 1)
112
+ return "just now";
113
+ if (minutes < MINUTES_PER_HOUR)
114
+ return `${minutes} min ago`;
115
+ const hours = Math.floor(minutes / MINUTES_PER_HOUR);
116
+ if (hours < HOURS_PER_DAY)
117
+ return `${hours} h ago`;
118
+ const days = Math.floor(hours / HOURS_PER_DAY);
119
+ return `${days} ${days === 1 ? "day" : "days"} ago`;
120
+ }
121
+ function cachedAge(fetchedAt) {
122
+ return fetchedAt ? ` cached ${formatAge(fetchedAt)}` : "";
123
+ }
124
+ function describeProblem(error) {
125
+ if (error instanceof cli_1.CliError)
126
+ return error.problem;
127
+ const message = error instanceof Error ? error.message : String(error);
128
+ return { title: message.split(LINE_BREAK)[0] };
129
+ }
130
+ function failureDetails(error, ui = (0, cli_1.getProcessUi)()) {
131
+ const { title, reason, fixes = [], details = [] } = describeProblem(error);
132
+ const hint = ui.symbols.levels.hint;
133
+ const stack = (0, cli_1.isVerboseRun)() && error instanceof Error && !(error instanceof cli_1.CliError)
134
+ ? (error.stack ?? "").split(LINE_BREAK).slice(1)
135
+ : [];
136
+ return [
137
+ title,
138
+ ...(reason ? [reason] : []),
139
+ ...details,
140
+ ...stack.map((line) => line.trim()),
141
+ ...fixes.map((fix) => `${hint} ${fix}`),
142
+ ];
143
+ }
144
+ function lineStartAfter(signal) {
145
+ return signal === "SIGINT" && process.stderr.isTTY ? CARRIAGE_RETURN : "";
146
+ }
147
+ function endInterruptedTask(task, error, stopped) {
148
+ if (!(error instanceof cancellation_1.CancelledError))
149
+ return task.dismiss();
150
+ const lineStart = lineStartAfter(error.signal);
151
+ if (lineStart)
152
+ (0, cli_1.getProcessTasks)().write(process.stderr, lineStart);
153
+ task.skip(stopped);
154
+ }
155
+ function reportStopped(stop, ui = (0, cli_1.getProcessUi)()) {
156
+ const lineStart = lineStartAfter(stop.signal);
157
+ if ((0, cli_1.isQuietRun)()) {
158
+ if (lineStart)
159
+ writeFeedback("");
160
+ return;
161
+ }
162
+ const symbol = isUnicode(ui)
163
+ ? STOPPED_SYMBOLS.unicode
164
+ : STOPPED_SYMBOLS.ascii;
165
+ const text = [stop.stopped, ...stop.context].join(ui.symbols.separator);
166
+ writeFeedback(`${lineStart}${ui.palette().red(symbol)} ${text}`);
167
+ }
168
+ function formatReadyBlock(block, ui = (0, cli_1.getProcessUi)(), isQuiet = (0, cli_1.isQuietRun)()) {
169
+ const palette = ui.palette();
170
+ const arrow = isUnicode(ui) ? LINK_SYMBOLS.unicode : LINK_SYMBOLS.ascii;
171
+ const shown = isQuiet
172
+ ? block.lines.filter(({ isEssential }) => isEssential)
173
+ : block.lines;
174
+ const width = Math.max(...shown.map(({ label }) => label.length + LABEL_SUFFIX.length));
175
+ const lead = (isLink) => `${BLOCK_INDENT}${isLink ? palette.cyan(arrow) : " ".repeat(arrow.length)}${LINK_GAP}`;
176
+ const lines = shown.map(({ label, value, isLink }) => {
177
+ const shownLabel = `${label}${LABEL_SUFFIX}`.padEnd(width);
178
+ const shownValue = isLink ? palette.cyan(value) : value;
179
+ return `${lead(isLink)}${palette.dim(shownLabel)} ${shownValue}`;
180
+ });
181
+ if (isQuiet)
182
+ return lines;
183
+ return [
184
+ `${palette.green(ui.symbols.levels.success)} ${block.title}`,
185
+ "",
186
+ ...lines,
187
+ `${lead()}${palette.dim(block.footer)}`,
188
+ ];
189
+ }
190
+ function writeReadyBlock(block, ui = (0, cli_1.getProcessUi)()) {
191
+ for (const line of formatReadyBlock(block, ui))
192
+ writeFeedback(line);
193
+ writeInfo("");
194
+ }
195
+ const TIMED_LEVEL_COLORS = {
196
+ success: "green",
197
+ info: "blue",
198
+ warn: "yellow",
199
+ };
200
+ function clockTime(date) {
201
+ return date.toTimeString().slice(0, TIME_LENGTH);
202
+ }
203
+ function formatTimedMessage(level, text, options = {}, ui = (0, cli_1.getProcessUi)(), now = new Date()) {
204
+ const { details = [], fixes = [] } = options;
205
+ const palette = ui.palette();
206
+ const paint = palette[TIMED_LEVEL_COLORS[level]];
207
+ const hint = palette.cyan(ui.symbols.levels.hint);
208
+ const indent = " ".repeat(TIME_LENGTH + 1) + BLOCK_INDENT;
209
+ return [
210
+ `${palette.dim(clockTime(now))} ${paint(ui.symbols.levels[level])} ${text}`,
211
+ ...details.map((detail) => `${indent}${palette.dim(detail)}`),
212
+ ...fixes.map((fix) => `${indent}${hint} ${fix}`),
213
+ ].join("\n");
214
+ }
package/dist/ports.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export declare const PORT_FALLBACK_RANGE = 20;
2
+ export declare const MAX_TCP_PORT = 65535;
2
3
  export interface ReservedPort {
3
4
  port: number;
4
5
  release: () => Promise<void>;
5
6
  }
6
7
  export declare function reserveFreePort(preferred: number, range?: number): Promise<ReservedPort>;
8
+ export declare function reservePort(port: number): Promise<ReservedPort | undefined>;