blogwright 0.4.0-beta.4 → 0.4.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.
- package/README.md +13 -6
- package/agent/agent-manifest.json +1 -1
- package/agent/server.js +944 -54
- package/dist/cli.js +11 -1
- package/dist/commands.d.ts +10 -1
- package/dist/commands.js +198 -8
- package/dist/deploy.d.ts +47 -1
- package/dist/deploy.js +60 -3
- package/dist/init.js +136 -9
- package/dist/nodes.d.ts +8 -0
- package/dist/nodes.js +690 -58
- package/dist/render.d.ts +13 -1
- package/dist/render.js +16 -3
- package/dist/web-functions.d.ts +123 -0
- package/dist/web-functions.js +548 -0
- package/package.json +4 -4
package/dist/render.d.ts
CHANGED
|
@@ -45,10 +45,22 @@ export interface HistoryEntry {
|
|
|
45
45
|
status: 'succeeded' | 'failed';
|
|
46
46
|
finishedAt: string;
|
|
47
47
|
durationMs: number;
|
|
48
|
+
/** The Web Function revision recorded for the hash (SSR mode only). */
|
|
49
|
+
webFunction?: {
|
|
50
|
+
revisionId: string;
|
|
51
|
+
} | undefined;
|
|
48
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Printed in the revision column for a hash with no recorded revision - never
|
|
55
|
+
* a blank cell, so plain output stays whitespace-separated column-per-field.
|
|
56
|
+
*/
|
|
57
|
+
export declare const NO_REVISION = "-";
|
|
58
|
+
/** Whether `history` shows the revision column: once any hash recorded one. */
|
|
59
|
+
export declare function historyHasRevisions(entries: readonly HistoryEntry[]): boolean;
|
|
49
60
|
/**
|
|
50
61
|
* The pretty deployment table for `history`: relative times, a live marker on
|
|
51
|
-
* the newest success
|
|
62
|
+
* the newest success, and - in SSR mode - the revision each hash recorded.
|
|
63
|
+
* Entries arrive newest-first (the caller sorts).
|
|
52
64
|
*/
|
|
53
65
|
export declare function renderHistoryTable(entries: HistoryEntry[], now: number): string[];
|
|
54
66
|
/** One installed plugin's row in `blogwright plugin list`. */
|
package/dist/render.js
CHANGED
|
@@ -85,11 +85,22 @@ export function logStatusEntries(entries, pretty, logger) {
|
|
|
85
85
|
logger.info(` ${mark} ${entry.title} ${entry.detail ? colors.dim(entry.detail) : ''}`);
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Printed in the revision column for a hash with no recorded revision - never
|
|
90
|
+
* a blank cell, so plain output stays whitespace-separated column-per-field.
|
|
91
|
+
*/
|
|
92
|
+
export const NO_REVISION = '-';
|
|
93
|
+
/** Whether `history` shows the revision column: once any hash recorded one. */
|
|
94
|
+
export function historyHasRevisions(entries) {
|
|
95
|
+
return entries.some((e) => e.webFunction);
|
|
96
|
+
}
|
|
88
97
|
/**
|
|
89
98
|
* The pretty deployment table for `history`: relative times, a live marker on
|
|
90
|
-
* the newest success
|
|
99
|
+
* the newest success, and - in SSR mode - the revision each hash recorded.
|
|
100
|
+
* Entries arrive newest-first (the caller sorts).
|
|
91
101
|
*/
|
|
92
102
|
export function renderHistoryTable(entries, now) {
|
|
103
|
+
const revisions = historyHasRevisions(entries);
|
|
93
104
|
const liveIndex = entries.findIndex((e) => e.status === 'succeeded');
|
|
94
105
|
const rows = entries.map((e, i) => {
|
|
95
106
|
const mark = e.status === 'succeeded' ? colors.green('✓') : colors.red('✗');
|
|
@@ -97,11 +108,13 @@ export function renderHistoryTable(entries, now) {
|
|
|
97
108
|
e.hash.padEnd(13),
|
|
98
109
|
mark,
|
|
99
110
|
formatAgo(now - Date.parse(e.finishedAt)).padEnd(9),
|
|
100
|
-
formatDuration(e.durationMs).padEnd(7),
|
|
111
|
+
formatDuration(e.durationMs).padEnd(revisions ? 8 : 7),
|
|
112
|
+
...(revisions ? [e.webFunction?.revisionId ?? NO_REVISION] : []),
|
|
101
113
|
].join(' ');
|
|
102
114
|
return i === liveIndex ? `${cells} ${colors.cyan('← live')}` : cells;
|
|
103
115
|
});
|
|
104
|
-
|
|
116
|
+
const header = `${'hash'.padEnd(13)} ${' '} ${'finished'.padEnd(9)} duration${revisions ? ' revision' : ''}`;
|
|
117
|
+
return [colors.bold(header), ...rows];
|
|
105
118
|
}
|
|
106
119
|
/**
|
|
107
120
|
* Printed in place of a cell the plugin genuinely has no value for. An
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SSR deploy contract: build-result reading, revision fingerprints, the
|
|
3
|
+
* signed endpoint probes, publishing a revision, the restore path when a
|
|
4
|
+
* revision is Active but never serves, and rollback from a recorded artifact.
|
|
5
|
+
*
|
|
6
|
+
* `commands.ts` orchestrates; this module owns the sequence. Every cloud call
|
|
7
|
+
* goes through `ctx.clients`, so the whole contract is covered by transport
|
|
8
|
+
* mocks - `floci` does not emulate `lambda-web`.
|
|
9
|
+
*/
|
|
10
|
+
import { type RawResponse, type SsrConfig } from 'blogwright-core';
|
|
11
|
+
import type { OpsContext } from './context.js';
|
|
12
|
+
import { type DeployManifest, type InvalidationSummary } from './deploy.js';
|
|
13
|
+
/** The environment's `ssr` block, or a refusal naming what is missing. */
|
|
14
|
+
export declare function requireSsr(ctx: OpsContext): SsrConfig;
|
|
15
|
+
/** What the build agent wrote to `web/results/<hash>.json`. */
|
|
16
|
+
export interface WebFunctionBuildResult {
|
|
17
|
+
artifactKey: string;
|
|
18
|
+
artifactVersionId: string;
|
|
19
|
+
artifactSha256: string;
|
|
20
|
+
artifactSize: number;
|
|
21
|
+
staleStaticKeys: string[];
|
|
22
|
+
}
|
|
23
|
+
/** The revision-scoped settings, exactly as they are sent to the service. */
|
|
24
|
+
export interface RevisionSettings {
|
|
25
|
+
entryPoint: string;
|
|
26
|
+
env: Record<string, string>;
|
|
27
|
+
timeoutSeconds: number;
|
|
28
|
+
maxConcurrencyPerEnvironment: number;
|
|
29
|
+
}
|
|
30
|
+
/** The tag the fingerprint is stored under in a revision's `description`. */
|
|
31
|
+
export declare const FINGERPRINT_PREFIX = "blogwright:";
|
|
32
|
+
/**
|
|
33
|
+
* The revision-scoped settings as sent: the resolved environment carries
|
|
34
|
+
* `NODE_ENV=production` unless `ssr.env` overrides it, and
|
|
35
|
+
* `AWS_LAMBDA_NODEJS_ENTRYPOINT` whenever the entry point is not the runtime's
|
|
36
|
+
* `index.js` default. With `ssr.shim` set, the entry point is the generated
|
|
37
|
+
* shim, which starts `ssr.entryPoint` itself.
|
|
38
|
+
*/
|
|
39
|
+
export declare function revisionSettings(ctx: OpsContext): RevisionSettings;
|
|
40
|
+
/**
|
|
41
|
+
* The revision fingerprint: a sha256 over the artifact digest, the runtime,
|
|
42
|
+
* the execution role, the log group and the revision-scoped settings as they
|
|
43
|
+
* will be sent. Two deploys with the same fingerprint produce the same
|
|
44
|
+
* revision, so the second creates nothing.
|
|
45
|
+
*/
|
|
46
|
+
export declare function revisionFingerprint(ctx: OpsContext, artifactSha256: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* True for the one signal that distinguishes an authorization refusal from an
|
|
49
|
+
* application answer: Lambda omits `lambda-web-error-type` when the caller may
|
|
50
|
+
* not invoke the endpoint at all.
|
|
51
|
+
*/
|
|
52
|
+
export declare function isInvokeAuthorizationFailure(response: RawResponse): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The mandatory preflight, run BEFORE anything is built or published. Any
|
|
55
|
+
* answer from the application passes, including the placeholder's 503; only a
|
|
56
|
+
* caller that cannot invoke the endpoint at all stops here.
|
|
57
|
+
*/
|
|
58
|
+
export declare function preflightWebFunction(ctx: OpsContext): Promise<void>;
|
|
59
|
+
/** Read the agent's result sidecar for a hash. */
|
|
60
|
+
export declare function readWebBuildResult(ctx: OpsContext, hash: string): Promise<WebFunctionBuildResult>;
|
|
61
|
+
/**
|
|
62
|
+
* Wait for the endpoint to answer below 500; anything else retries. The
|
|
63
|
+
* pacing is a parameter so tests can pin a retry boundary without waiting out
|
|
64
|
+
* the production window.
|
|
65
|
+
*/
|
|
66
|
+
export interface ProbePacing {
|
|
67
|
+
intervalMs?: number | undefined;
|
|
68
|
+
timeoutMs?: number | undefined;
|
|
69
|
+
}
|
|
70
|
+
/** The manifest's `webFunction` block, written only for a serving revision. */
|
|
71
|
+
export interface WebFunctionManifest {
|
|
72
|
+
revisionId: string;
|
|
73
|
+
revisionArn: string;
|
|
74
|
+
fingerprint: string;
|
|
75
|
+
artifactKey: string;
|
|
76
|
+
artifactVersionId: string;
|
|
77
|
+
artifactSha256: string;
|
|
78
|
+
settings: RevisionSettings;
|
|
79
|
+
endpointName: string;
|
|
80
|
+
endpointDomainName: string;
|
|
81
|
+
}
|
|
82
|
+
/** Timing/identity a manifest needs, independent of whether a build ran. */
|
|
83
|
+
export interface ManifestSource {
|
|
84
|
+
hash: string;
|
|
85
|
+
sourceKey: string;
|
|
86
|
+
startedAt: Date;
|
|
87
|
+
microvmId: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Record an outcome. The manifest is written only when the outcome is known,
|
|
91
|
+
* and a failed one keeps the hash's last verified `webFunction` block.
|
|
92
|
+
*/
|
|
93
|
+
export declare function recordManifest(ctx: OpsContext, source: ManifestSource, status: 'succeeded' | 'failed', webFunction?: WebFunctionManifest, message?: string): Promise<DeployManifest>;
|
|
94
|
+
export interface PublishOutcome {
|
|
95
|
+
manifest: DeployManifest;
|
|
96
|
+
/** False when the fingerprint already served and no revision was created. */
|
|
97
|
+
created: boolean;
|
|
98
|
+
revisionId: string;
|
|
99
|
+
invalidation: InvalidationSummary;
|
|
100
|
+
staleKeysDeleted: number;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Publish a revision for a finished build: compare fingerprints, capture what
|
|
104
|
+
* is serving, create and verify, restore on a failed readiness probe, and only
|
|
105
|
+
* then touch the cache and the stale site keys.
|
|
106
|
+
*/
|
|
107
|
+
export declare function publishWebRevision(ctx: OpsContext, source: ManifestSource, result: WebFunctionBuildResult, pacing?: ProbePacing): Promise<PublishOutcome>;
|
|
108
|
+
/**
|
|
109
|
+
* Rollback's artifact path: reproduce a recorded revision from the artifact
|
|
110
|
+
* version and settings in that hash's manifest, without a rebuild. Skips
|
|
111
|
+
* creation when the recorded fingerprint is already what serves, and probes
|
|
112
|
+
* and invalidates exactly as a deploy does.
|
|
113
|
+
*/
|
|
114
|
+
export declare function publishRecordedRevision(ctx: OpsContext, source: ManifestSource, recorded: WebFunctionManifest, pacing?: ProbePacing): Promise<PublishOutcome>;
|
|
115
|
+
/**
|
|
116
|
+
* `seo` is inert in SSR mode - the application owns robots.txt and
|
|
117
|
+
* sitemap.xml - so a written, non-`off` value warns once per deploy. The check
|
|
118
|
+
* reads the RAW config document: the merged defaults are `auto`, so reading
|
|
119
|
+
* the merged config would warn on every SSR deploy.
|
|
120
|
+
*/
|
|
121
|
+
export declare function warnAboutSsrSeo(ctx: OpsContext): void;
|
|
122
|
+
/** Refuse a preview command on an environment that configures SSR. */
|
|
123
|
+
export declare function assertNotSsrForPreviews(ctx: OpsContext): void;
|