@antelopejs/dms-frontend 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/README.md +4 -4
  2. package/dist/commands/build-action.d.ts +8 -5
  3. package/dist/commands/build-action.js +13 -41
  4. package/dist/commands/build.js +9 -15
  5. package/dist/commands/dev-action.js +5 -1
  6. package/dist/commands/start-action.d.ts +2 -0
  7. package/dist/commands/start-action.js +36 -49
  8. package/dist/commands/start.js +8 -6
  9. package/dist/config.d.ts +2 -0
  10. package/dist/config.js +6 -0
  11. package/dist/derived-outputs.js +2 -2
  12. package/dist/fs-sync.d.ts +1 -0
  13. package/dist/fs-sync.js +6 -0
  14. package/dist/help.js +8 -4
  15. package/dist/index.js +4 -6
  16. package/dist/layer-watch.js +4 -0
  17. package/dist/layers.d.ts +1 -3
  18. package/dist/layers.js +1 -95
  19. package/dist/manifest.d.ts +8 -8
  20. package/dist/manifest.js +15 -39
  21. package/dist/materialize.js +6 -0
  22. package/dist/module-set.d.ts +14 -12
  23. package/dist/module-set.js +39 -31
  24. package/dist/private-values.d.ts +2 -2
  25. package/dist/private-values.js +1 -6
  26. package/dist/project-modules.d.ts +46 -0
  27. package/dist/project-modules.js +233 -0
  28. package/dist/workspace-setup.d.ts +3 -7
  29. package/dist/workspace-setup.js +41 -62
  30. package/dist/workspace.d.ts +2 -4
  31. package/dist/workspace.js +1 -1
  32. package/package.json +5 -7
  33. package/skills/dms-frontend/SKILL.md +2 -2
  34. package/templates/vue/client-chunking.mjs +61 -0
  35. package/templates/vue/email-renderer.ts +6 -1
  36. package/templates/vue/frontend-build-loader.ts +1 -1
  37. package/templates/vue/frontend-module.ts +24 -0
  38. package/templates/vue/main.ts +11 -2
  39. package/templates/vue/package.json +2 -2
  40. package/templates/vue/prepare-types.mjs +2 -7
  41. package/templates/vue/public-options.ts +19 -0
  42. package/templates/vue/server/auth/backend.mjs +20 -3
  43. package/templates/vue/server/auth/client-ip.mjs +64 -22
  44. package/templates/vue/server/auth/session.mjs +4 -11
  45. package/templates/vue/server/backend-proxies.mjs +13 -2
  46. package/templates/vue/server/backend-response.mjs +2 -13
  47. package/templates/vue/server/cli-report.mjs +42 -18
  48. package/templates/vue/server/client-manifest.mjs +22 -58
  49. package/templates/vue/server/dev-private-files.mjs +1 -1
  50. package/templates/vue/server/dev-styles.mjs +16 -5
  51. package/templates/vue/server/homepage.mjs +4 -1
  52. package/templates/vue/server/module-event.mjs +23 -8
  53. package/templates/vue/server/module-server.mjs +4 -4
  54. package/templates/vue/server/module-testing.mjs +5 -3
  55. package/templates/vue/server/proxy-compression.mjs +15 -8
  56. package/templates/vue/server/proxy-headers.mjs +12 -1
  57. package/templates/vue/server/proxy-redirects.mjs +66 -0
  58. package/templates/vue/server/proxy-transfers.mjs +84 -0
  59. package/templates/vue/server/public-endpoints.mjs +2 -2
  60. package/templates/vue/server/public-options.mjs +49 -0
  61. package/templates/vue/server/request-routing.mjs +20 -0
  62. package/templates/vue/server/web-body.mjs +24 -0
  63. package/templates/vue/server-module.ts +19 -7
  64. package/templates/vue/server.mjs +59 -49
  65. package/templates/vue/ssr-renderer.ts +19 -1
  66. package/templates/vue/vite.config.ts +72 -46
  67. package/templates/vue/vite.email.config.ts +6 -3
  68. package/templates/vue/vite.server.config.ts +5 -2
  69. package/dist/commands/snapshot-action.d.ts +0 -6
  70. package/dist/commands/snapshot-action.js +0 -53
  71. package/dist/commands/snapshot.d.ts +0 -2
  72. package/dist/commands/snapshot.js +0 -55
  73. package/dist/snapshot.d.ts +0 -25
  74. package/dist/snapshot.js +0 -90
  75. package/templates/vue/server/backend-wake.mjs +0 -129
@@ -1,55 +0,0 @@
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.cmdSnapshot = cmdSnapshot;
37
- const commander_1 = require("commander");
38
- const config_1 = require("../config");
39
- const cli_1 = require("@antelopejs/core/cli");
40
- const SNAPSHOT_EXAMPLES = [
41
- {
42
- description: "With DMS_BOOTSTRAP_SECRET set, after a backend release",
43
- command: "ajs dms snapshot -b https://dms.example.com -o dms-snapshot",
44
- },
45
- ];
46
- function cmdSnapshot() {
47
- const command = new commander_1.Command("snapshot")
48
- .summary("Keep what a build needs from the backend, without its secrets")
49
- .description("Fetch the manifest and the layers archive the backend serves, and write them into a directory with every private value emptied, so that ajs dms build --from builds without the backend or any credential. Take it again when the backend's frontend modules change: start says when they no longer match.")
50
- .addOption(config_1.Options.backendUrl("Backend URL to take the snapshot from; required, as -b or the variable"))
51
- .addOption(new commander_1.Option("-o, --output <dir>", "Directory to write the snapshot to").default("dms-snapshot"))
52
- .addOption(config_1.Options.bootstrapSecret())
53
- .action(async (options) => (await Promise.resolve().then(() => __importStar(require("./snapshot-action")))).runSnapshot(options));
54
- return (0, cli_1.withExamples)(command, SNAPSHOT_EXAMPLES);
55
- }
@@ -1,25 +0,0 @@
1
- import type { Manifest, ManifestModule } from "./workspace";
2
- export declare const SNAPSHOT_FILE = "snapshot.json";
3
- export declare const SNAPSHOT_LAYERS_DIR = "layers";
4
- export declare const BUILD_ORIGIN_FILE = ".build-origin.json";
5
- declare const SNAPSHOT_FORMAT = 1;
6
- export interface FrontendSnapshot {
7
- format: typeof SNAPSHOT_FORMAT;
8
- backendUrl: string;
9
- takenAt: string;
10
- digest: string;
11
- manifest: Manifest;
12
- }
13
- export interface BuildOrigin {
14
- source: "snapshot" | "backend";
15
- backendUrl: string;
16
- takenAt: string;
17
- digest: string;
18
- authenticated: boolean;
19
- }
20
- export declare function manifestDigest(modules: readonly ManifestModule[]): string;
21
- export declare function writeSnapshot(dir: string, backendUrl: string, manifest: Manifest, layersSource: string): FrontendSnapshot;
22
- export declare function readSnapshot(dir: string): FrontendSnapshot;
23
- export declare function writeBuildOrigin(workspaceDir: string, origin: BuildOrigin): void;
24
- export declare function readBuildOrigin(workspaceDir: string): BuildOrigin | undefined;
25
- export {};
package/dist/snapshot.js DELETED
@@ -1,90 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.BUILD_ORIGIN_FILE = exports.SNAPSHOT_LAYERS_DIR = exports.SNAPSHOT_FILE = void 0;
4
- exports.manifestDigest = manifestDigest;
5
- exports.writeSnapshot = writeSnapshot;
6
- exports.readSnapshot = readSnapshot;
7
- exports.writeBuildOrigin = writeBuildOrigin;
8
- exports.readBuildOrigin = readBuildOrigin;
9
- const node_crypto_1 = require("node:crypto");
10
- const node_fs_1 = require("node:fs");
11
- const node_path_1 = require("node:path");
12
- const cli_1 = require("@antelopejs/core/cli");
13
- const config_1 = require("./config");
14
- const module_set_1 = require("./module-set");
15
- const private_values_1 = require("./private-values");
16
- exports.SNAPSHOT_FILE = "snapshot.json";
17
- exports.SNAPSHOT_LAYERS_DIR = "layers";
18
- exports.BUILD_ORIGIN_FILE = ".build-origin.json";
19
- const SNAPSHOT_FORMAT = 1;
20
- const DIGEST_LENGTH = 16;
21
- function manifestDigest(modules) {
22
- return (0, node_crypto_1.createHash)("sha256")
23
- .update((0, module_set_1.moduleSetFingerprint)(modules.map(({ path: _path, ...module }) => module)))
24
- .digest("hex")
25
- .slice(0, DIGEST_LENGTH);
26
- }
27
- function writeSnapshot(dir, backendUrl, manifest, layersSource) {
28
- const modules = (0, private_values_1.withoutPrivateValues)(manifest.modules).map(({ path: _path, ...module }) => module);
29
- const snapshot = {
30
- format: SNAPSHOT_FORMAT,
31
- backendUrl: (0, config_1.canonicalizeBackendUrl)(backendUrl),
32
- takenAt: new Date().toISOString(),
33
- digest: manifestDigest(modules),
34
- manifest: { pack: manifest.pack, modules },
35
- };
36
- (0, node_fs_1.mkdirSync)(dir, { recursive: true });
37
- const layers = (0, node_path_1.join)(dir, exports.SNAPSHOT_LAYERS_DIR);
38
- (0, node_fs_1.rmSync)(layers, { recursive: true, force: true });
39
- (0, node_fs_1.cpSync)(layersSource, layers, { recursive: true });
40
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(dir, exports.SNAPSHOT_FILE), `${JSON.stringify(snapshot, null, 2)}\n`);
41
- return snapshot;
42
- }
43
- function readSnapshot(dir) {
44
- const file = (0, node_path_1.join)(dir, exports.SNAPSHOT_FILE);
45
- const refresh = "Take one: ajs dms snapshot -b <backend url> -o <dir>";
46
- if (!(0, node_fs_1.existsSync)(file)) {
47
- throw new cli_1.CliError({
48
- title: `No snapshot in ${dir}`,
49
- reason: `${exports.SNAPSHOT_FILE} is missing.`,
50
- fixes: [refresh],
51
- });
52
- }
53
- let snapshot;
54
- try {
55
- snapshot = JSON.parse((0, node_fs_1.readFileSync)(file, "utf8"));
56
- }
57
- catch (error) {
58
- throw new cli_1.CliError({ title: `${file} is not valid JSON`, fixes: [refresh] }, { cause: error });
59
- }
60
- if (snapshot.format !== SNAPSHOT_FORMAT ||
61
- !Array.isArray(snapshot.manifest?.modules) ||
62
- typeof snapshot.backendUrl !== "string") {
63
- throw new cli_1.CliError({
64
- title: `${file} is not a snapshot this loader can build from`,
65
- reason: `It needs format ${SNAPSHOT_FORMAT}, a backend URL and a manifest.`,
66
- fixes: [refresh],
67
- });
68
- }
69
- if (!(0, node_fs_1.existsSync)((0, node_path_1.join)(dir, exports.SNAPSHOT_LAYERS_DIR))) {
70
- throw new cli_1.CliError({
71
- title: `The snapshot in ${dir} has no ${exports.SNAPSHOT_LAYERS_DIR} directory`,
72
- fixes: [refresh],
73
- });
74
- }
75
- return snapshot;
76
- }
77
- function writeBuildOrigin(workspaceDir, origin) {
78
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(workspaceDir, exports.BUILD_ORIGIN_FILE), `${JSON.stringify(origin, null, 2)}\n`);
79
- }
80
- function readBuildOrigin(workspaceDir) {
81
- try {
82
- const origin = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(workspaceDir, exports.BUILD_ORIGIN_FILE), "utf8"));
83
- return typeof origin.digest === "string"
84
- ? origin
85
- : undefined;
86
- }
87
- catch {
88
- return undefined;
89
- }
90
- }
@@ -1,129 +0,0 @@
1
- // Waiting out a backend that is waking from sleep or still starting.
2
- //
3
- // A platform that scales the backend to zero answers the first requests with
4
- // a 503 while it starts a replica, marked with the cold-start header and a
5
- // Retry-After in whole seconds; a DMS backend that listens before it is ready
6
- // answers 503 with a Retry-After too, without the header. A 503 with a
7
- // Retry-After in whole seconds is the signal, with or without the header; a
8
- // 503 without one goes out as it came. Only a GET or a HEAD is repeated; a
9
- // POST, PATCH or DELETE is sent once.
10
- //
11
- // The wait starts with the first waitable answer and lasts at most the
12
- // budget, the repeated requests included: one still waiting for its answer
13
- // when the budget runs out is aborted, and no request starts after it. It
14
- // ends at once when the client leaves.
15
- //
16
- // Lives apart from server.mjs so the file stays under the size the linter
17
- // allows.
18
-
19
- import { setTimeout as sleep } from "node:timers/promises";
20
-
21
- /** Header a platform marks the 503 of a backend it is waking with. */
22
- export const COLD_START_HEADER = "x-antelope-cold-start";
23
- const COLD_START_VALUE = "1";
24
- const UNAVAILABLE = 503;
25
- const RETRYABLE_METHODS = new Set(["GET", "HEAD"]);
26
- const DEFAULT_BUDGET_MS = 30_000;
27
- const MIN_DELAY_MS = 1_000;
28
- // Repeats after the first request, so a GET is sent 16 times at most. They
29
- // are capped as well as the time, so a marked answer with `Retry-After: 0`
30
- // cannot multiply a GET.
31
- const MAX_REPEATS = 15;
32
- const MAX_DELAY_MS = 5_000;
33
- const MS_PER_SECOND = 1_000;
34
- const WHOLE_SECONDS = /^\d+$/;
35
- const WHOLE_MILLISECONDS = /^\d+$/;
36
-
37
- /**
38
- * Whether `response` is a 503 asking to come back after a Retry-After in
39
- * whole seconds: a platform waking the backend (with the cold-start header)
40
- * or a backend still starting.
41
- */
42
- export function isWaitable(response) {
43
- return (
44
- response.status === UNAVAILABLE && WHOLE_SECONDS.test(retryAfter(response))
45
- );
46
- }
47
-
48
- function retryAfter(response) {
49
- return (response.headers.get("retry-after") ?? "").trim();
50
- }
51
-
52
- /**
53
- * How long a request may wait for a waking backend, from
54
- * DMS_BACKEND_WAKE_BUDGET_MS. 0 turns the wait off; anything but a whole
55
- * number of milliseconds keeps the default.
56
- */
57
- export function wakeBudgetMs(env = process.env) {
58
- const raw = (env.DMS_BACKEND_WAKE_BUDGET_MS ?? "").trim();
59
- return WHOLE_MILLISECONDS.test(raw) ? Number(raw) : DEFAULT_BUDGET_MS;
60
- }
61
-
62
- /** The pause a waitable answer's Retry-After asks for, kept between bounds. */
63
- function retryDelayMs(response) {
64
- return Math.min(
65
- MAX_DELAY_MS,
66
- Math.max(MIN_DELAY_MS, Number(retryAfter(response)) * MS_PER_SECOND),
67
- );
68
- }
69
-
70
- /**
71
- * What the client gets once the budget is spent: the last waitable answer's
72
- * status, Retry-After and cold-start header if it had one, without the body
73
- * its pause already discarded.
74
- */
75
- function expired(cold) {
76
- const headers = { "retry-after": retryAfter(cold) };
77
- if (cold.headers.get(COLD_START_HEADER) === COLD_START_VALUE)
78
- headers[COLD_START_HEADER] = COLD_START_VALUE;
79
- return new Response(null, { status: UNAVAILABLE, headers });
80
- }
81
-
82
- /**
83
- * Sends one repeat, aborted if no answer has come by `deadline`. Once the
84
- * answer has come the deadline no longer applies, so a ready backend's body
85
- * streams for as long as it needs; only the client leaving ends it.
86
- * Resolves to undefined when the deadline aborted the request.
87
- */
88
- async function repeat(url, init, deadline, now) {
89
- const late = new AbortController();
90
- const timer = setTimeout(() => late.abort(), Math.max(0, deadline - now()));
91
- const signal = init.signal
92
- ? AbortSignal.any([init.signal, late.signal])
93
- : late.signal;
94
- try {
95
- return await fetch(url, { ...init, signal });
96
- } catch (error) {
97
- if (late.signal.aborted && !init.signal?.aborted) return undefined;
98
- throw error;
99
- } finally {
100
- clearTimeout(timer);
101
- }
102
- }
103
-
104
- /**
105
- * `fetch`, waiting out waitable 503s to a GET or a HEAD until `budgetMs`
106
- * from the first of them is spent or MAX_REPEATS repeats are sent. Then the
107
- * caller gets the 503 with its `Retry-After` to relay. The client
108
- * leaving, through `init.signal`, ends the wait with its AbortError.
109
- */
110
- export async function fetchWaking(
111
- url,
112
- init = {},
113
- { budgetMs = wakeBudgetMs(), now = Date.now } = {},
114
- ) {
115
- const method = (init.method ?? "GET").toUpperCase();
116
- let response = await fetch(url, init);
117
- if (!RETRYABLE_METHODS.has(method)) return response;
118
- const deadline = now() + budgetMs;
119
- for (let repeats = 0; isWaitable(response); repeats += 1) {
120
- const cold = response;
121
- const delay = retryDelayMs(cold);
122
- if (repeats >= MAX_REPEATS || now() + delay > deadline) return cold;
123
- cold.body?.cancel().catch(() => {});
124
- await sleep(delay, undefined, { signal: init.signal });
125
- if (now() >= deadline) return expired(cold);
126
- response = (await repeat(url, init, deadline, now)) ?? expired(cold);
127
- }
128
- return response;
129
- }