@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
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readWorkspaceRecords = readWorkspaceRecords;
4
+ exports.describeKey = describeKey;
5
+ exports.renderWorkspaces = renderWorkspaces;
6
+ exports.reportSkipped = reportSkipped;
7
+ exports.runWorkspaces = runWorkspaces;
8
+ const cli_1 = require("@antelopejs/core/cli");
9
+ const common_1 = require("../common");
10
+ const output_1 = require("../output");
11
+ function readWorkspaceRecords() {
12
+ const { workspaces, skipped } = (0, common_1.listWorkspaces)();
13
+ const records = workspaces.map((workspace) => {
14
+ const projectDir = (0, common_1.projectDirFromWorkspaceKey)(workspace.workspaceKey);
15
+ return {
16
+ id: (0, output_1.workspaceId)(workspace.dir, false),
17
+ dir: workspace.dir,
18
+ backendUrl: workspace.backendUrl,
19
+ key: projectDir ? { type: "project", path: projectDir } : { type: "url" },
20
+ sizeBytes: (0, common_1.directorySize)(workspace.dir),
21
+ lastUsedAt: workspace.lastUsedAt,
22
+ };
23
+ });
24
+ return { records, skipped };
25
+ }
26
+ function describeKey(record) {
27
+ return record.key.type === "project"
28
+ ? `project ${(0, output_1.showPath)(record.key.path)}`
29
+ : "url";
30
+ }
31
+ const TERMINAL_COLUMNS = [
32
+ { header: "id", value: (record) => (0, output_1.workspaceId)(record.dir, true) },
33
+ { header: "backend", value: (record) => record.backendUrl },
34
+ { header: "key", value: describeKey },
35
+ { header: "size", value: (record) => (0, output_1.formatSize)(record.sizeBytes) },
36
+ { header: "last used", value: (record) => (0, output_1.formatAge)(record.lastUsedAt) },
37
+ ];
38
+ const PIPED_COLUMNS = [
39
+ { header: "id", value: (record) => record.id },
40
+ { header: "backend", value: (record) => record.backendUrl },
41
+ { header: "key", value: (record) => record.key.type },
42
+ {
43
+ header: "project",
44
+ value: (record) => (record.key.type === "project" ? record.key.path : ""),
45
+ },
46
+ { header: "size", value: (record) => String(record.sizeBytes) },
47
+ { header: "last used", value: (record) => record.lastUsedAt },
48
+ { header: "dir", value: (record) => record.dir },
49
+ ];
50
+ function renderWorkspaces(ui, records, isTerminal) {
51
+ if (records.length === 0) {
52
+ ui.message("info", `No workspaces in ${(0, output_1.showPath)(common_1.DMS_FRONTEND_HOME)}`);
53
+ return;
54
+ }
55
+ ui.table(records, isTerminal ? TERMINAL_COLUMNS : PIPED_COLUMNS);
56
+ }
57
+ function reportSkipped(ui, skipped) {
58
+ for (const dir of skipped) {
59
+ ui.message("skip", `Skipped ${(0, output_1.showPath)(dir)} (not a workspace)`);
60
+ }
61
+ }
62
+ function runWorkspaces(options) {
63
+ const ui = (0, cli_1.getProcessUi)();
64
+ const { records, skipped } = readWorkspaceRecords();
65
+ (0, cli_1.writeData)(ui, {
66
+ data: records,
67
+ isJson: options.json,
68
+ render: (target) => renderWorkspaces(target, records, (0, output_1.isTerminalResult)()),
69
+ });
70
+ reportSkipped(ui, skipped);
71
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare function cmdWorkspaces(): Command;
@@ -0,0 +1,60 @@
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.cmdWorkspaces = cmdWorkspaces;
37
+ const commander_1 = require("commander");
38
+ const cli_1 = require("@antelopejs/core/cli");
39
+ const WORKSPACES_EXAMPLES = [
40
+ {
41
+ description: "On a terminal: an aligned table",
42
+ command: "ajs dms workspaces",
43
+ },
44
+ {
45
+ description: "In a script",
46
+ command: "ajs dms workspaces --json",
47
+ },
48
+ ];
49
+ function cmdWorkspaces() {
50
+ const command = new commander_1.Command("workspaces")
51
+ .summary("List generated workspaces")
52
+ .description("List generated workspaces, with their size and last use.")
53
+ .option("--json", "Print the list as one JSON document on stdout")
54
+ .addHelpText("after", `
55
+ Piped, it prints one tab-separated line per workspace, without a header:
56
+ id, backend URL, key type (url or project), project directory, size in bytes,
57
+ last use (ISO 8601) and workspace directory.`)
58
+ .action(async (options) => (await Promise.resolve().then(() => __importStar(require("./workspaces-action")))).runWorkspaces(options));
59
+ return (0, cli_1.withExamples)(command, WORKSPACES_EXAMPLES);
60
+ }
package/dist/common.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./cancellation";
1
2
  export * from "./config";
2
3
  export * from "./layers";
3
4
  export * from "./layer-aliases";
package/dist/common.js CHANGED
@@ -14,6 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./cancellation"), exports);
17
18
  __exportStar(require("./config"), exports);
18
19
  __exportStar(require("./layers"), exports);
19
20
  __exportStar(require("./layer-aliases"), exports);
package/dist/config.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { CliError, type CliProblem } from "@antelopejs/core/cli";
1
2
  import { Option } from "commander";
2
3
  import ignore from "ignore";
3
4
  import { type DiscoveryOptions } from "./discovery";
@@ -13,12 +14,20 @@ export declare const LAYER_COPY_BLOCKLIST: readonly string[];
13
14
  export declare const layerCopyIgnore: ignore.Ignore;
14
15
  export declare const PNPM_LIFECYCLE_SCRIPTS: readonly string[];
15
16
  export declare const TAILWIND_SOURCE_GLOB = "**/*.{vue,ts,tsx,js,jsx,mjs,cjs}";
17
+ export declare class UsageError extends CliError {
18
+ constructor(problem: Omit<CliProblem, "exitCode">);
19
+ }
20
+ export declare function parseBackendUrl(value: string): string;
21
+ export declare function requireBackendUrl(value: string | undefined): string;
22
+ export declare function parsePort(value: string): number;
23
+ export declare function flagOrEnv(flag: boolean | undefined, name: string): boolean;
16
24
  export declare const Options: {
17
- backendUrl: Option;
25
+ backendUrl(description: string): Option;
18
26
  port: Option;
19
- force: Option;
20
- readonly offline: Option;
21
- bootstrapSecret: Option;
27
+ force(description: string): Option;
28
+ offline(description: string): Option;
29
+ strict: Option;
30
+ bootstrapSecret(discovery?: string): Option;
22
31
  };
23
32
  export interface BootstrapDiscoveryOptions extends DiscoveryOptions {
24
33
  cwd?: string;
package/dist/config.js CHANGED
@@ -3,8 +3,12 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.Options = exports.TAILWIND_SOURCE_GLOB = exports.PNPM_LIFECYCLE_SCRIPTS = exports.layerCopyIgnore = exports.LAYER_COPY_BLOCKLIST = exports.AUTH_ESTABLISH_FILE = exports.FRONTEND_MODULE_ENTRY = exports.LAYERS_SUBDIR = exports.TEMPLATE_FILES = exports.WORKSPACE_DIR_MODE = exports.DEPS_HASH_FILE = exports.DMS_FRONTEND_HOME = void 0;
6
+ exports.Options = exports.UsageError = exports.TAILWIND_SOURCE_GLOB = exports.PNPM_LIFECYCLE_SCRIPTS = exports.layerCopyIgnore = exports.LAYER_COPY_BLOCKLIST = exports.AUTH_ESTABLISH_FILE = exports.FRONTEND_MODULE_ENTRY = exports.LAYERS_SUBDIR = exports.TEMPLATE_FILES = exports.WORKSPACE_DIR_MODE = exports.DEPS_HASH_FILE = exports.DMS_FRONTEND_HOME = void 0;
7
7
  exports.writeSecretBearingFile = writeSecretBearingFile;
8
+ exports.parseBackendUrl = parseBackendUrl;
9
+ exports.requireBackendUrl = requireBackendUrl;
10
+ exports.parsePort = parsePort;
11
+ exports.flagOrEnv = flagOrEnv;
8
12
  exports.normalizeBootstrapSecret = normalizeBootstrapSecret;
9
13
  exports.resolveSessionSecret = resolveSessionSecret;
10
14
  exports.resolveBootstrapSecret = resolveBootstrapSecret;
@@ -14,9 +18,11 @@ const node_crypto_1 = require("node:crypto");
14
18
  const node_fs_1 = require("node:fs");
15
19
  const node_os_1 = require("node:os");
16
20
  const node_path_1 = require("node:path");
21
+ const cli_1 = require("@antelopejs/core/cli");
17
22
  const commander_1 = require("commander");
18
23
  const ignore_1 = __importDefault(require("ignore"));
19
24
  const discovery_1 = require("./discovery");
25
+ const ports_1 = require("./ports");
20
26
  const ANTELOPEJS_HOME = (0, node_path_1.join)((0, node_os_1.homedir)(), ".antelopejs");
21
27
  exports.DMS_FRONTEND_HOME = (0, node_path_1.join)(ANTELOPEJS_HOME, "dms-frontend");
22
28
  exports.DEPS_HASH_FILE = ".deps-hash";
@@ -85,49 +91,140 @@ exports.PNPM_LIFECYCLE_SCRIPTS = [
85
91
  "postpack",
86
92
  ];
87
93
  exports.TAILWIND_SOURCE_GLOB = "**/*.{vue,ts,tsx,js,jsx,mjs,cjs}";
94
+ class UsageError extends cli_1.CliError {
95
+ constructor(problem) {
96
+ super({ ...problem, exitCode: cli_1.USAGE_EXIT_CODE });
97
+ this.name = "UsageError";
98
+ }
99
+ }
100
+ exports.UsageError = UsageError;
101
+ const BACKEND_URL_PROTOCOLS = ["http:", "https:"];
102
+ function parseBackendUrl(value) {
103
+ let protocol;
104
+ try {
105
+ protocol = new URL(value.trim()).protocol;
106
+ }
107
+ catch {
108
+ protocol = undefined;
109
+ }
110
+ if (protocol && BACKEND_URL_PROTOCOLS.includes(protocol))
111
+ return value;
112
+ throw new UsageError({
113
+ title: `Invalid backend URL '${value}'`,
114
+ fixes: [
115
+ value.includes("://")
116
+ ? "Use an http:// or https:// URL: -b http://localhost:5010"
117
+ : `Include the scheme: -b http://${value.trim()}`,
118
+ ],
119
+ });
120
+ }
121
+ function requireBackendUrl(value) {
122
+ if (value)
123
+ return parseBackendUrl(value);
124
+ throw new UsageError({
125
+ title: "Backend URL is required",
126
+ fixes: ["Pass -b <url> or set DMS_API_BASE_URL"],
127
+ });
128
+ }
129
+ function parsePort(value) {
130
+ const port = /^\d+$/.test(value.trim()) ? Number(value.trim()) : Number.NaN;
131
+ if (port >= 0 && port <= ports_1.MAX_TCP_PORT)
132
+ return port;
133
+ throw new UsageError({
134
+ title: `Invalid port '${value}'`,
135
+ fixes: [
136
+ `Pass a number between 1 and ${ports_1.MAX_TCP_PORT}, or 0 for any free port: -p 3001`,
137
+ ],
138
+ });
139
+ }
88
140
  function booleanFromEnv(name) {
89
141
  const value = process.env[name];
90
142
  if (value === undefined)
91
143
  return false;
92
144
  return !["", "0", "false", "no", "off"].includes(value.trim().toLowerCase());
93
145
  }
146
+ function flagOrEnv(flag, name) {
147
+ return flag ?? booleanFromEnv(name);
148
+ }
149
+ const BOOTSTRAP_SECRET_DESCRIPTION = "Credential for the backend's layer endpoints; prefer the variable, a command line is visible to other processes";
94
150
  exports.Options = {
95
- backendUrl: new commander_1.Option("-b, --backend-url <url>", "Backend DMS URL (when omitted, dev mode discovers it from the enclosing antelope project's .antelope/dev.json)").env("DMS_API_BASE_URL"),
96
- port: new commander_1.Option("-p, --port <port>", "Port to run on")
151
+ backendUrl(description) {
152
+ return new commander_1.Option("-b, --backend-url <url>", description).env("DMS_API_BASE_URL");
153
+ },
154
+ port: new commander_1.Option("-p, --port <port>", "Port to serve on")
97
155
  .default("3001")
98
156
  .env("PORT"),
99
- force: new commander_1.Option("-f, --force", "Force reinstall dependencies"),
100
- get offline() {
101
- return new commander_1.Option("--offline", "Skip the backend manifest fetch and reuse the last cached manifest (env: DMS_OFFLINE)").default(booleanFromEnv("DMS_OFFLINE"));
157
+ force(description) {
158
+ return new commander_1.Option("-f, --force", description);
159
+ },
160
+ offline(description) {
161
+ return new commander_1.Option("--offline", `${description} (env: DMS_OFFLINE)`);
162
+ },
163
+ strict: new commander_1.Option("--strict", "Exit 1 when the workspace cannot be prepared, and 2 when the backend URL is missing or invalid, instead of warning and exiting 0 (env: DMS_PREPARE_STRICT)"),
164
+ bootstrapSecret(discovery) {
165
+ return new commander_1.Option("--bootstrap-secret <secret>", discovery
166
+ ? `${BOOTSTRAP_SECRET_DESCRIPTION}. ${discovery}`
167
+ : BOOTSTRAP_SECRET_DESCRIPTION).env("DMS_BOOTSTRAP_SECRET");
102
168
  },
103
- bootstrapSecret: new commander_1.Option("--bootstrap-secret <secret>", "Credential presented to the backend's layer endpoints (env: DMS_BOOTSTRAP_SECRET, preferred — " +
104
- "a secret passed on the command line is visible to every process on the machine). In dev it is " +
105
- "discovered from the antelope project's .antelope/dms-dev.json.").env("DMS_BOOTSTRAP_SECRET"),
106
169
  };
107
170
  const HEADER_SAFE_CREDENTIAL = /^[\x21-\x7e]+$/;
171
+ const HEADER_UNSAFE_CHARACTER = /[^\x21-\x7e]/u;
172
+ const UNSAFE_CHARACTER_NAMES = {
173
+ " ": "a space",
174
+ "\t": "a tab",
175
+ "\r": "a line break",
176
+ "\n": "a line break",
177
+ };
178
+ const LAST_ASCII_CODE_POINT = 0x7f;
179
+ function describeUnsafeCharacter(value) {
180
+ const char = HEADER_UNSAFE_CHARACTER.exec(value)?.[0] ?? "";
181
+ const named = UNSAFE_CHARACTER_NAMES[char];
182
+ if (named)
183
+ return named;
184
+ return (char.codePointAt(0) ?? 0) > LAST_ASCII_CODE_POINT
185
+ ? "a non-ASCII character"
186
+ : "a control character";
187
+ }
108
188
  const DEFAULT_CREDENTIAL_SOURCE = "DMS_BOOTSTRAP_SECRET";
109
189
  function normalizeBootstrapSecret(value, source = DEFAULT_CREDENTIAL_SOURCE) {
110
190
  const trimmed = value?.trim();
111
191
  if (!trimmed)
112
192
  return undefined;
113
193
  if (!HEADER_SAFE_CREDENTIAL.test(trimmed)) {
114
- throw new Error("The bootstrap credential contains characters that cannot travel in an HTTP header.\n" +
115
- ` Check ${source} for a line break or a non-ASCII byte — a secret read from\n` +
116
- " a file commonly keeps its trailing newline.");
194
+ throw new UsageError({
195
+ title: "The bootstrap credential cannot travel in an HTTP header",
196
+ reason: `${source} contains ${describeUnsafeCharacter(trimmed)}.`,
197
+ fixes: [
198
+ "Use printable ASCII characters only, without spaces or line breaks",
199
+ ],
200
+ });
117
201
  }
118
202
  return trimmed;
119
203
  }
204
+ const SESSION_SECRET_MIN_LENGTH = 32;
120
205
  function resolveSessionSecret(mode, value = process.env.DMS_SESSION_SECRET) {
121
206
  if (value === undefined && mode === "dev") {
122
207
  return (0, node_crypto_1.randomBytes)(32).toString("hex");
123
208
  }
124
- if (value === undefined || value.length < 32) {
125
- throw new Error("DMS_SESSION_SECRET must contain at least 32 characters; " +
126
- (mode === "dev"
127
- ? "set it explicitly or omit it to generate an ephemeral dev secret"
128
- : "set it explicitly for build and start"));
209
+ if (value !== undefined && value.length >= SESSION_SECRET_MIN_LENGTH) {
210
+ return value;
129
211
  }
130
- return value;
212
+ throw new UsageError({
213
+ title: `DMS_SESSION_SECRET ${describeSessionSecret(value)}`,
214
+ reason: mode === "dev"
215
+ ? "dev generates an ephemeral secret only when the variable is not set at all."
216
+ : "build and start sign sessions with it, so it must stay the same across restarts.",
217
+ fixes: [
218
+ "Create one: openssl rand -hex 32, then set it in the environment or ./.env",
219
+ ],
220
+ });
221
+ }
222
+ function describeSessionSecret(value) {
223
+ if (value === undefined)
224
+ return "is not set";
225
+ if (value === "")
226
+ return "is empty";
227
+ return `is too short: ${value.length} characters, at least ${SESSION_SECRET_MIN_LENGTH} needed`;
131
228
  }
132
229
  function resolveBootstrapSecret(explicit, backendUrl, options = {}) {
133
230
  const normalized = normalizeBootstrapSecret(explicit);
@@ -11,12 +11,17 @@ export interface FrontendModuleRegistry {
11
11
  modules: FrontendModuleRegistryEntry[];
12
12
  }
13
13
  export declare function createFrontendModuleRegistry(workspaceDir: string, layers: ResolvedLayer[]): FrontendModuleRegistry;
14
+ export declare class InvalidLocaleFileError extends Error {
15
+ readonly path: string;
16
+ readonly reason: string;
17
+ constructor(path: string, reason: string);
18
+ }
14
19
  export declare function affectsLocaleMessages(relativePath: string): boolean;
15
20
  export declare function writeLocaleMessages(workspaceDir: string, registry: FrontendModuleRegistry): boolean;
16
21
  export interface DerivedOutput {
17
22
  name: string;
18
23
  affects(relativePath: string): boolean;
19
24
  write(workspaceDir: string, registry: FrontendModuleRegistry): boolean;
20
- restartNotice?: string;
25
+ restartHint?: string;
21
26
  }
22
27
  export declare const DERIVED_OUTPUTS: readonly DerivedOutput[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DERIVED_OUTPUTS = void 0;
3
+ exports.DERIVED_OUTPUTS = exports.InvalidLocaleFileError = void 0;
4
4
  exports.createFrontendModuleRegistry = createFrontendModuleRegistry;
5
5
  exports.affectsLocaleMessages = affectsLocaleMessages;
6
6
  exports.writeLocaleMessages = writeLocaleMessages;
@@ -120,13 +120,22 @@ function mergeLocaleMessages(source, existing) {
120
120
  return merged;
121
121
  }
122
122
  const LOCALE_DIRECTORY = "i18n/locales";
123
+ class InvalidLocaleFileError extends Error {
124
+ constructor(path, reason) {
125
+ super(`Invalid locale file ${path}: ${reason}`);
126
+ this.path = path;
127
+ this.reason = reason;
128
+ this.name = "InvalidLocaleFileError";
129
+ }
130
+ }
131
+ exports.InvalidLocaleFileError = InvalidLocaleFileError;
123
132
  function readLocaleFile(path) {
124
133
  try {
125
134
  return JSON.parse((0, node_fs_1.readFileSync)(path, "utf8"));
126
135
  }
127
136
  catch (error) {
128
137
  const reason = error instanceof Error ? error.message : String(error);
129
- throw new Error(`Invalid locale file ${path}: ${reason}`);
138
+ throw new InvalidLocaleFileError(path, reason);
130
139
  }
131
140
  }
132
141
  function readLocaleMessages(assets, registry) {
@@ -276,7 +285,7 @@ exports.DERIVED_OUTPUTS = [
276
285
  name: "type paths",
277
286
  affects: isLayerDirectory,
278
287
  write: writeFrontendTypePaths,
279
- restartNotice: "A layer directory was added or removed: restart 'ajs dms dev' to apply it. Vite reads the '#<layer>' aliases and the auto-imported directories at startup only.",
288
+ restartHint: "Restart ajs dms dev to apply it (Vite reads the '#<layer>' aliases and the auto-imported directories at startup)",
280
289
  },
281
290
  {
282
291
  name: "locale catalogs",
@@ -1,3 +1,4 @@
1
+ import type { CliProblem } from "@antelopejs/core/cli";
1
2
  export interface DevRegistryEndpoint {
2
3
  protocol: string;
3
4
  host: string;
@@ -38,7 +39,7 @@ export type DiscoveryResult = {
38
39
  projectDir: string;
39
40
  };
40
41
  export declare function discoverBackend(cwd: string, options?: DiscoveryOptions): DiscoveryResult;
41
- export declare function describeDiscoveryFailure(result: DiscoveryResult): string;
42
+ export declare function describeDiscoveryFailure(result: DiscoveryResult): CliProblem;
42
43
  export interface DevHandshake {
43
44
  pid: number;
44
45
  bootstrapSecret: string;
package/dist/discovery.js CHANGED
@@ -8,6 +8,7 @@ exports.describeDiscoveryFailure = describeDiscoveryFailure;
8
8
  exports.readDevBootstrapCredential = readDevBootstrapCredential;
9
9
  const node_fs_1 = require("node:fs");
10
10
  const node_path_1 = require("node:path");
11
+ const output_1 = require("./output");
11
12
  exports.DEV_REGISTRY_RELATIVE_PATH = (0, node_path_1.join)(".antelope", "dev.json");
12
13
  function isPidAlive(pid) {
13
14
  if (!Number.isInteger(pid) || pid <= 0)
@@ -79,21 +80,37 @@ function discoverBackend(cwd, options = {}) {
79
80
  function describeDiscoveryFailure(result) {
80
81
  switch (result.status) {
81
82
  case "not-found":
82
- return ("No backend URL provided and no running antelope project found.\n" +
83
- ` Searched for ${exports.DEV_REGISTRY_RELATIVE_PATH} from the current directory upward.\n` +
84
- " Either run this command inside an antelope project started with 'ajs project dev',\n" +
85
- " or pass the backend explicitly with -b <url> (env: DMS_API_BASE_URL).");
83
+ return {
84
+ title: "No backend URL provided and no running antelope project found",
85
+ reason: `Searched for ${exports.DEV_REGISTRY_RELATIVE_PATH} from the current directory upward.`,
86
+ fixes: [
87
+ "Run this command inside an antelope project started with 'ajs project dev'",
88
+ "Or pass the backend explicitly with -b <url> (env: DMS_API_BASE_URL)",
89
+ ],
90
+ };
86
91
  case "stale":
87
- return (`Found ${(0, node_path_1.join)(result.projectDir, exports.DEV_REGISTRY_RELATIVE_PATH)} but its process (pid ${result.pid}) is no longer running.\n` +
88
- " Start the backend with 'ajs project dev', or pass -b <url> explicitly.");
92
+ return {
93
+ title: `Found ${(0, output_1.showPath)((0, node_path_1.join)(result.projectDir, exports.DEV_REGISTRY_RELATIVE_PATH))} but its process (pid ${result.pid}) is no longer running`,
94
+ fixes: [
95
+ "Start the backend with 'ajs project dev', or pass -b <url> explicitly",
96
+ ],
97
+ };
89
98
  case "no-api-endpoint":
90
- return (`Found a running antelope project at ${result.projectDir} but it exposes no 'api' server endpoint.\n` +
91
- " Make sure the api module is loaded, or pass -b <url> explicitly.");
99
+ return {
100
+ title: `Found a running antelope project at ${(0, output_1.showPath)(result.projectDir)} but it exposes no 'api' server endpoint`,
101
+ fixes: [
102
+ "Make sure the api module is loaded, or pass -b <url> explicitly",
103
+ ],
104
+ };
92
105
  case "malformed":
93
- return (`Found ${(0, node_path_1.join)(result.projectDir, exports.DEV_REGISTRY_RELATIVE_PATH)} but could not parse it.\n` +
94
- " Restart the backend with 'ajs project dev' to rewrite it, or pass -b <url> explicitly.");
106
+ return {
107
+ title: `Found ${(0, output_1.showPath)((0, node_path_1.join)(result.projectDir, exports.DEV_REGISTRY_RELATIVE_PATH))} but could not parse it`,
108
+ fixes: [
109
+ "Restart the backend with 'ajs project dev' to rewrite it, or pass -b <url> explicitly",
110
+ ],
111
+ };
95
112
  case "found":
96
- return "";
113
+ return { title: "" };
97
114
  }
98
115
  }
99
116
  exports.DEV_HANDSHAKE_RELATIVE_PATH = (0, node_path_1.join)(".antelope", "dms-dev.json");
package/dist/env-file.js CHANGED
@@ -5,11 +5,14 @@ exports.loadProjectEnv = loadProjectEnv;
5
5
  const node_fs_1 = require("node:fs");
6
6
  const node_path_1 = require("node:path");
7
7
  const node_util_1 = require("node:util");
8
+ const cli_1 = require("@antelopejs/core/cli");
9
+ const output_1 = require("./output");
8
10
  exports.ENV_FILE_NAMES = [".env.local", ".env"];
9
11
  function loadProjectEnv(options = {}) {
10
12
  const cwd = options.cwd ?? process.cwd();
11
13
  const env = options.env ?? process.env;
12
- const onWarning = options.onWarning ?? ((message) => console.warn(message));
14
+ const onWarning = options.onWarning ??
15
+ ((message) => (0, cli_1.getProcessUi)().message("warn", message));
13
16
  const loaded = [];
14
17
  for (const name of exports.ENV_FILE_NAMES) {
15
18
  const file = (0, node_path_1.join)(cwd, name);
@@ -20,7 +23,7 @@ function loadProjectEnv(options = {}) {
20
23
  parsed = (0, node_util_1.parseEnv)((0, node_fs_1.readFileSync)(file, "utf-8"));
21
24
  }
22
25
  catch (err) {
23
- onWarning(`⚠ Ignoring ${file}: ${err?.message ?? err}`);
26
+ onWarning(`Ignoring ${(0, output_1.showPath)(file)}: ${err?.message ?? err}`);
24
27
  continue;
25
28
  }
26
29
  for (const [key, value] of Object.entries(parsed)) {
package/dist/help.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import type { Command } from "commander";
2
+ export declare const ENVIRONMENT_TOPIC = "environment";
3
+ export declare const ENVIRONMENT_VARIABLES: ReadonlyArray<readonly [string, string]>;
4
+ export declare function applyDmsHelp(program: Command): void;
5
+ export declare function formatEnvironmentHelp(width?: number): string;
package/dist/help.js ADDED
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ENVIRONMENT_VARIABLES = exports.ENVIRONMENT_TOPIC = void 0;
4
+ exports.applyDmsHelp = applyDmsHelp;
5
+ exports.formatEnvironmentHelp = formatEnvironmentHelp;
6
+ const cli_1 = require("@antelopejs/core/cli");
7
+ const env_file_1 = require("./env-file");
8
+ exports.ENVIRONMENT_TOPIC = "environment";
9
+ exports.ENVIRONMENT_VARIABLES = [
10
+ ["DMS_API_BASE_URL", "Backend URL, same as -b; clean ignores it"],
11
+ ["DMS_BOOTSTRAP_SECRET", "Same as --bootstrap-secret"],
12
+ [
13
+ "DMS_SESSION_SECRET",
14
+ "Session key, 32 characters or more; build and start need one, dev makes one when unset",
15
+ ],
16
+ ["DMS_OFFLINE", "Same as --offline, unless 0, false, no or off"],
17
+ ["DMS_PREPARE_STRICT", "Same as prepare --strict, read like DMS_OFFLINE"],
18
+ ["PORT", "Same as -p"],
19
+ ["HOST", "Address the server binds to (default: 0.0.0.0)"],
20
+ [
21
+ "DMS_COOKIE_SECURE",
22
+ "Secure cookies unless false (default: false in dev, true in start)",
23
+ ],
24
+ [
25
+ "DMS_TRUSTED_PROXY_HOPS",
26
+ "Trusted reverse-proxy hops, counted from the right (default: 0)",
27
+ ],
28
+ [
29
+ "DMS_HTML_RENDER_SECRET",
30
+ "Overrides the e-mail render secret the backend publishes",
31
+ ],
32
+ [
33
+ "DMS_OAUTH_RELAY_SECRET",
34
+ "Overrides the OAuth relay secret the backend publishes",
35
+ ],
36
+ [
37
+ "DMS_AUTH_ESTABLISH_ENDPOINTS",
38
+ "More backend endpoints /auth/establish may open a session from, comma-separated",
39
+ ],
40
+ ["DMS_CLIENT_BASE_URL", "Public frontend URL used in links and e-mails"],
41
+ ["NO_COLOR", "Same as --no-color"],
42
+ ["ANTELOPEJS_VERBOSE", "Same as --verbose"],
43
+ ["NO_UPDATE_NOTIFIER, CI", "Either one turns the update check off"],
44
+ ];
45
+ const ENVIRONMENT_NOTES = [
46
+ "A variable set in the shell wins over both files, and .env.local wins over .env. Only the current directory is read: the generated workspace never loads a .env of its own.",
47
+ "Workspaces live in ~/.antelopejs/dms-frontend, one per backend URL. dev without -b keys its own on the project directory instead; pass -b to share one with build and start. ajs dms workspaces lists them.",
48
+ ];
49
+ function subcommandTerm(command) {
50
+ const args = command.registeredArguments.map((argument) => argument.required ? `<${argument.name()}>` : `[${argument.name()}]`);
51
+ return [command.name(), ...args].join(" ");
52
+ }
53
+ function applyDmsHelp(program) {
54
+ (0, cli_1.applyHelpConventions)(program);
55
+ const apply = (command) => {
56
+ command.configureHelp({ ...command.configureHelp(), subcommandTerm });
57
+ command.commands.forEach(apply);
58
+ };
59
+ apply(program);
60
+ }
61
+ function formatEnvironmentHelp(width = (0, cli_1.helpWidth)()) {
62
+ const files = env_file_1.ENV_FILE_NAMES.map((name) => `./${name}`).join(", then ");
63
+ const nameWidth = Math.max(...exports.ENVIRONMENT_VARIABLES.map(([name]) => name.length));
64
+ return [
65
+ ...(0, cli_1.wrapText)(`Environment (read from the shell, then ${files})`, width),
66
+ "",
67
+ ...exports.ENVIRONMENT_VARIABLES.flatMap(([name, description]) => (0, cli_1.formatHelpItem)(name, nameWidth, description, width)),
68
+ ...ENVIRONMENT_NOTES.flatMap((note) => ["", ...(0, cli_1.wrapText)(note, width)]),
69
+ ].join("\n");
70
+ }