@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.
- package/README.md +82 -23
- package/dist/cancellation.d.ts +8 -0
- package/dist/cancellation.js +23 -0
- package/dist/child-output.d.ts +45 -0
- package/dist/child-output.js +183 -0
- package/dist/commands/build-action.d.ts +21 -0
- package/dist/commands/build-action.js +144 -0
- package/dist/commands/build.js +54 -62
- package/dist/commands/clean-action.d.ts +8 -0
- package/dist/commands/clean-action.js +97 -0
- package/dist/commands/clean.js +52 -39
- package/dist/commands/dev-action.d.ts +8 -0
- package/dist/commands/dev-action.js +120 -0
- package/dist/commands/dev.js +55 -115
- package/dist/commands/prepare-action.d.ts +8 -0
- package/dist/commands/prepare-action.js +73 -0
- package/dist/commands/prepare.js +55 -54
- package/dist/commands/start-action.d.ts +5 -0
- package/dist/commands/start-action.js +77 -0
- package/dist/commands/start.js +48 -48
- package/dist/commands/verify-source-action.d.ts +10 -0
- package/dist/commands/verify-source-action.js +104 -0
- package/dist/commands/verify-source.d.ts +0 -1
- package/dist/commands/verify-source.js +53 -31
- package/dist/commands/workspace-task.d.ts +2 -0
- package/dist/commands/workspace-task.js +26 -0
- package/dist/commands/workspaces-action.d.ts +25 -0
- package/dist/commands/workspaces-action.js +71 -0
- package/dist/commands/workspaces.d.ts +2 -0
- package/dist/commands/workspaces.js +60 -0
- package/dist/common.d.ts +1 -0
- package/dist/common.js +1 -0
- package/dist/config.d.ts +13 -4
- package/dist/config.js +115 -18
- package/dist/derived-outputs.d.ts +6 -1
- package/dist/derived-outputs.js +12 -3
- package/dist/discovery.d.ts +2 -1
- package/dist/discovery.js +28 -11
- package/dist/env-file.js +5 -2
- package/dist/help.d.ts +5 -0
- package/dist/help.js +70 -0
- package/dist/index.js +98 -38
- package/dist/layer-watch.d.ts +2 -1
- package/dist/layer-watch.js +129 -26
- package/dist/layers.d.ts +2 -2
- package/dist/layers.js +71 -23
- package/dist/manifest-secrets.d.ts +3 -2
- package/dist/manifest-secrets.js +25 -21
- package/dist/manifest.d.ts +22 -5
- package/dist/manifest.js +184 -28
- package/dist/materialize.js +2 -1
- package/dist/output.d.ts +41 -0
- package/dist/output.js +197 -0
- package/dist/ports.d.ts +2 -0
- package/dist/ports.js +23 -13
- package/dist/renderer-range.d.ts +3 -1
- package/dist/renderer-range.js +49 -17
- package/dist/server-process.d.ts +39 -0
- package/dist/server-process.js +221 -0
- package/dist/typecheck-output.d.ts +13 -0
- package/dist/typecheck-output.js +87 -0
- package/dist/update-check.d.ts +14 -2
- package/dist/update-check.js +95 -29
- package/dist/verify-source-result.d.ts +11 -0
- package/dist/verify-source-result.js +17 -0
- package/dist/verify-source-runner.js +266 -131
- package/dist/vite-output.d.ts +14 -0
- package/dist/vite-output.js +99 -0
- package/dist/workspace-setup.d.ts +17 -3
- package/dist/workspace-setup.js +119 -35
- package/dist/workspace.d.ts +8 -2
- package/dist/workspace.js +47 -12
- package/package.json +4 -12
- package/templates/vue/package.json +4 -0
- package/templates/vue/server/cli-report.mjs +91 -0
- package/templates/vue/server.mjs +18 -15
- package/templates/vue/vite.config.ts +8 -1
- package/templates/vue/vite.email.config.ts +5 -1
- package/dist/utils/cli-ui.d.ts +0 -27
- 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,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
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
|
-
|
|
21
|
-
|
|
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
|
|
96
|
-
|
|
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
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
115
|
-
|
|
116
|
-
|
|
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
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
25
|
+
restartHint?: string;
|
|
21
26
|
}
|
|
22
27
|
export declare const DERIVED_OUTPUTS: readonly DerivedOutput[];
|
package/dist/derived-outputs.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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",
|
package/dist/discovery.d.ts
CHANGED
|
@@ -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):
|
|
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
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
88
|
-
|
|
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
|
|
91
|
-
|
|
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
|
|
94
|
-
|
|
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 ??
|
|
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(
|
|
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
|
+
}
|