blogwright 0.4.0-beta.4 → 0.4.0-beta.5

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/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. Entries arrive newest-first (the caller sorts).
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. Entries arrive newest-first (the caller sorts).
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
- return [colors.bold(`${'hash'.padEnd(13)} ${' '} ${'finished'.padEnd(9)} duration`), ...rows];
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;