blogwright 0.3.3 → 0.4.0-beta.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 +11 -11
- package/agent/agent-manifest.json +1 -1
- package/agent/server.js +37 -19
- package/dist/adapters/fetch-ping.d.ts +1 -1
- package/dist/adapters/fetch-ping.js +3 -3
- package/dist/adapters/node-module-loader.d.ts +11 -0
- package/dist/adapters/node-module-loader.js +146 -0
- package/dist/adapters/process-package-manager.d.ts +41 -0
- package/dist/adapters/process-package-manager.js +116 -0
- package/dist/adapters/process-vcs.d.ts +5 -4
- package/dist/adapters/process-vcs.js +6 -5
- package/dist/agent-package.d.ts +1 -1
- package/dist/agent-package.js +4 -3
- package/dist/bin.js +13 -2
- package/dist/cli.d.ts +68 -1
- package/dist/cli.js +270 -88
- package/dist/commands.d.ts +70 -3
- package/dist/commands.js +177 -30
- package/dist/config-block.d.ts +34 -0
- package/dist/config-block.js +262 -0
- package/dist/context.d.ts +76 -6
- package/dist/context.js +98 -18
- package/dist/deploy.d.ts +2 -2
- package/dist/deploy.js +12 -12
- package/dist/graph.d.ts +27 -16
- package/dist/graph.js +1 -1
- package/dist/init.d.ts +37 -3
- package/dist/init.js +146 -22
- package/dist/known-commands.d.ts +63 -0
- package/dist/known-commands.js +78 -0
- package/dist/microvms.d.ts +2 -2
- package/dist/microvms.js +3 -3
- package/dist/nodes.d.ts +5 -3
- package/dist/nodes.js +97 -30
- package/dist/plugin-commands.d.ts +298 -0
- package/dist/plugin-commands.js +990 -0
- package/dist/plugins.d.ts +194 -0
- package/dist/plugins.js +523 -0
- package/dist/ports.d.ts +89 -1
- package/dist/render.d.ts +55 -0
- package/dist/render.js +89 -1
- package/dist/repo.d.ts +3 -3
- package/dist/repo.js +8 -8
- package/dist/seo.d.ts +1 -1
- package/dist/seo.js +1 -1
- package/package.json +6 -6
package/dist/render.d.ts
CHANGED
|
@@ -20,6 +20,26 @@ export interface StatusEntry {
|
|
|
20
20
|
}
|
|
21
21
|
/** The pretty drift tree for `status` (the plain form keeps the classic lines). */
|
|
22
22
|
export declare function renderStatusTree(entries: StatusEntry[]): string[];
|
|
23
|
+
/**
|
|
24
|
+
* Leveled-logger surface {@link logStatusEntries} reports through - both the
|
|
25
|
+
* CLI's own `Logger` (`packages/cli/src/logger.ts`) and core's
|
|
26
|
+
* `PluginLogger` (`blogwright-core`'s `plugin.ts`) satisfy it structurally,
|
|
27
|
+
* so this module needs neither type imported.
|
|
28
|
+
*/
|
|
29
|
+
export interface StatusLogger {
|
|
30
|
+
info(msg: string): void;
|
|
31
|
+
warn(msg: string): void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Report `entries` through `logger`, choosing the pretty tree ({@link
|
|
35
|
+
* renderStatusTree}) on an interactive terminal or the plain, CI-stable form
|
|
36
|
+
* otherwise: a `present`/`missing` line via `logger.info`, or a `read
|
|
37
|
+
* failed` line via `logger.warn` for an entry whose `read()` threw. Shared
|
|
38
|
+
* by the CLI's own `status` command (`commands.ts`) and a plugin's generic
|
|
39
|
+
* `status` verb (`plugin-commands.ts`), so the two never carry two
|
|
40
|
+
* near-identical copies of the same render branch.
|
|
41
|
+
*/
|
|
42
|
+
export declare function logStatusEntries(entries: StatusEntry[], pretty: boolean, logger: StatusLogger): void;
|
|
23
43
|
export interface HistoryEntry {
|
|
24
44
|
hash: string;
|
|
25
45
|
status: 'succeeded' | 'failed';
|
|
@@ -31,3 +51,38 @@ export interface HistoryEntry {
|
|
|
31
51
|
* the newest success. Entries arrive newest-first (the caller sorts).
|
|
32
52
|
*/
|
|
33
53
|
export declare function renderHistoryTable(entries: HistoryEntry[], now: number): string[];
|
|
54
|
+
/** One installed plugin's row in `blogwright plugin list`. */
|
|
55
|
+
export interface PluginListRow {
|
|
56
|
+
/** The CLI namespace the plugin claims - `Plugin.name`. */
|
|
57
|
+
namespace: string;
|
|
58
|
+
/** The npm package the plugin was loaded from. */
|
|
59
|
+
packageName: string;
|
|
60
|
+
/** From the package's own `package.json`; `undefined` when it declares none. */
|
|
61
|
+
version?: string | undefined;
|
|
62
|
+
/** The single top-level config key the plugin owns; `undefined` when it owns none. */
|
|
63
|
+
configKey?: string | undefined;
|
|
64
|
+
}
|
|
65
|
+
/** One plugin that failed to load, as `discover` reported it. */
|
|
66
|
+
interface PluginListFailure {
|
|
67
|
+
packageName: string;
|
|
68
|
+
reason: string;
|
|
69
|
+
}
|
|
70
|
+
/** What `blogwright plugin list` has to show: the plugins that loaded, and the ones that did not. */
|
|
71
|
+
export interface PluginListing {
|
|
72
|
+
rows: readonly PluginListRow[];
|
|
73
|
+
failures: readonly PluginListFailure[];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The `blogwright plugin list` listing, in the two forms every renderer in
|
|
77
|
+
* this module offers: an aligned table under a bold header on a TTY, and the
|
|
78
|
+
* same columns single-space separated otherwise. The plain form is the stable
|
|
79
|
+
* contract for CI logs and agents - the same split `history` makes
|
|
80
|
+
* (`commands.ts`), which is why the column set is identical in both and only
|
|
81
|
+
* the padding and the colour differ.
|
|
82
|
+
*
|
|
83
|
+
* Emits NOTHING for a listing with no rows and no failures, so a repo with no
|
|
84
|
+
* plugins installed gets its caller's empty-state line and not a header over
|
|
85
|
+
* an empty table.
|
|
86
|
+
*/
|
|
87
|
+
export declare function renderPluginList(listing: PluginListing, pretty: boolean): string[];
|
|
88
|
+
export {};
|
package/dist/render.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
* Pure presentation helpers: data in, lines out
|
|
2
|
+
* Pure presentation helpers: data in, lines out - the caller writes them.
|
|
3
3
|
* Every renderer takes a `pretty` flag; the plain form is stable, line-oriented
|
|
4
4
|
* output for CI systems and agents, the pretty form is for humans on a TTY.
|
|
5
5
|
*/
|
|
@@ -60,6 +60,31 @@ export function renderStatusTree(entries) {
|
|
|
60
60
|
return `${connector} ${STATUS_MARKS[entry.state]} ${entry.title}${detail}`;
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Report `entries` through `logger`, choosing the pretty tree ({@link
|
|
65
|
+
* renderStatusTree}) on an interactive terminal or the plain, CI-stable form
|
|
66
|
+
* otherwise: a `present`/`missing` line via `logger.info`, or a `read
|
|
67
|
+
* failed` line via `logger.warn` for an entry whose `read()` threw. Shared
|
|
68
|
+
* by the CLI's own `status` command (`commands.ts`) and a plugin's generic
|
|
69
|
+
* `status` verb (`plugin-commands.ts`), so the two never carry two
|
|
70
|
+
* near-identical copies of the same render branch.
|
|
71
|
+
*/
|
|
72
|
+
export function logStatusEntries(entries, pretty, logger) {
|
|
73
|
+
if (pretty) {
|
|
74
|
+
for (const line of renderStatusTree(entries))
|
|
75
|
+
logger.info(line);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// The plain form is the stable contract for CI logs and agents.
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
if (entry.state === 'error') {
|
|
81
|
+
logger.warn(`${entry.title}: read failed (${entry.detail})`);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const mark = entry.state === 'present' ? colors.green('present') : colors.yellow('missing');
|
|
85
|
+
logger.info(` ${mark} ${entry.title} ${entry.detail ? colors.dim(entry.detail) : ''}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
63
88
|
/**
|
|
64
89
|
* The pretty deployment table for `history`: relative times, a live marker on
|
|
65
90
|
* the newest success. Entries arrive newest-first (the caller sorts).
|
|
@@ -78,3 +103,66 @@ export function renderHistoryTable(entries, now) {
|
|
|
78
103
|
});
|
|
79
104
|
return [colors.bold(`${'hash'.padEnd(13)} ${' '} ${'finished'.padEnd(9)} duration`), ...rows];
|
|
80
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* Printed in place of a cell the plugin genuinely has no value for. An
|
|
108
|
+
* explicit marker, never an empty cell: plain output is column-per-line and
|
|
109
|
+
* whitespace-separated, so a blank cell would silently shift every column
|
|
110
|
+
* after it for whatever is parsing the line.
|
|
111
|
+
*/
|
|
112
|
+
const NO_CONFIG_KEY = '(none)';
|
|
113
|
+
/** Printed for a plugin whose own `package.json` declares no `version` field at all. */
|
|
114
|
+
const UNKNOWN_VERSION = '(unknown)';
|
|
115
|
+
/** Column headers, in the order {@link pluginListCells} emits the cells. */
|
|
116
|
+
const PLUGIN_LIST_HEADERS = ['namespace', 'package', 'version', 'configKey'];
|
|
117
|
+
/** Heading the failure lines are printed under, in both forms. */
|
|
118
|
+
const FAILED_TO_LOAD_HEADING = 'failed to load:';
|
|
119
|
+
function pluginListCells(row) {
|
|
120
|
+
return [
|
|
121
|
+
row.namespace,
|
|
122
|
+
row.packageName,
|
|
123
|
+
row.version ?? UNKNOWN_VERSION,
|
|
124
|
+
row.configKey ?? NO_CONFIG_KEY,
|
|
125
|
+
];
|
|
126
|
+
}
|
|
127
|
+
/** Pad every cell but the last to the widest value in its column. */
|
|
128
|
+
function alignCells(cells, widths) {
|
|
129
|
+
return cells
|
|
130
|
+
.map((cell, i) => (i === cells.length - 1 ? cell : cell.padEnd(widths[i] ?? 0)))
|
|
131
|
+
.join(' ')
|
|
132
|
+
.trimEnd();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* The `blogwright plugin list` listing, in the two forms every renderer in
|
|
136
|
+
* this module offers: an aligned table under a bold header on a TTY, and the
|
|
137
|
+
* same columns single-space separated otherwise. The plain form is the stable
|
|
138
|
+
* contract for CI logs and agents - the same split `history` makes
|
|
139
|
+
* (`commands.ts`), which is why the column set is identical in both and only
|
|
140
|
+
* the padding and the colour differ.
|
|
141
|
+
*
|
|
142
|
+
* Emits NOTHING for a listing with no rows and no failures, so a repo with no
|
|
143
|
+
* plugins installed gets its caller's empty-state line and not a header over
|
|
144
|
+
* an empty table.
|
|
145
|
+
*/
|
|
146
|
+
export function renderPluginList(listing, pretty) {
|
|
147
|
+
const lines = [];
|
|
148
|
+
if (listing.rows.length > 0) {
|
|
149
|
+
const cells = listing.rows.map(pluginListCells);
|
|
150
|
+
if (pretty) {
|
|
151
|
+
const widths = PLUGIN_LIST_HEADERS.map((header, i) => Math.max(header.length, ...cells.map((row) => (row[i] ?? '').length)));
|
|
152
|
+
lines.push(colors.bold(alignCells(PLUGIN_LIST_HEADERS, widths)));
|
|
153
|
+
lines.push(...cells.map((row) => alignCells(row, widths)));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
lines.push(PLUGIN_LIST_HEADERS.join(' '));
|
|
157
|
+
lines.push(...cells.map((row) => row.join(' ')));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (listing.failures.length > 0) {
|
|
161
|
+
lines.push(pretty ? colors.bold(FAILED_TO_LOAD_HEADING) : FAILED_TO_LOAD_HEADING);
|
|
162
|
+
// The same `<package>: <reason>` shape `--help` already prints a failed
|
|
163
|
+
// plugin in (`cli.ts`'s `renderPluginFailure`), and for the same reason:
|
|
164
|
+
// the reason is `discover`'s own message, never an `Error.stack`.
|
|
165
|
+
lines.push(...listing.failures.map((failure) => `${failure.packageName}: ${failure.reason}`));
|
|
166
|
+
}
|
|
167
|
+
return lines;
|
|
168
|
+
}
|
package/dist/repo.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Deploy source packaging: choose the repository files to ship and build a
|
|
3
|
-
* deterministic zip of them. Pure domain
|
|
3
|
+
* deterministic zip of them. Pure domain - the VCS listing and file contents
|
|
4
4
|
* arrive through ports (see ports.ts; the process adapter owns jj/git).
|
|
5
5
|
*/
|
|
6
6
|
import { type FileSystem } from 'blogwright-core';
|
|
@@ -8,7 +8,7 @@ import type { Ports } from './ports.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* Filename injected into the source zip carrying the build revision. The site build
|
|
10
10
|
* runs in a MicroVM from this zip (no `.git`), so `astro.config` reads the hash from
|
|
11
|
-
* this file
|
|
11
|
+
* this file - see the repo's astro.config.mjs.
|
|
12
12
|
*/
|
|
13
13
|
export declare const COMMIT_FILE = ".commit-hash";
|
|
14
14
|
/**
|
|
@@ -19,7 +19,7 @@ export declare const COMMIT_FILE = ".commit-hash";
|
|
|
19
19
|
export declare function listRepoFiles(ports: Pick<Ports, 'vcs' | 'fs'>, cwd: string, ignore: string[], include?: string[]): Promise<string[]>;
|
|
20
20
|
/**
|
|
21
21
|
* Build a deterministic zip of the given files (read relative to cwd). Extra entries
|
|
22
|
-
* (path → text content) are added in memory
|
|
22
|
+
* (path → text content) are added in memory - used to inject build metadata such as
|
|
23
23
|
* the commit hash without touching the working tree.
|
|
24
24
|
*/
|
|
25
25
|
export declare function buildRepoZip(fs: FileSystem, cwd: string, files: string[], extra?: Record<string, string>): Promise<Uint8Array>;
|
package/dist/repo.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Deploy source packaging: choose the repository files to ship and build a
|
|
3
|
-
* deterministic zip of them. Pure domain
|
|
3
|
+
* deterministic zip of them. Pure domain - the VCS listing and file contents
|
|
4
4
|
* arrive through ports (see ports.ts; the process adapter owns jj/git).
|
|
5
5
|
*/
|
|
6
6
|
import { zipSync } from 'fflate';
|
|
7
|
-
import { FileNotFoundError } from 'blogwright-core';
|
|
8
|
-
/** Fixed timestamp for reproducible zips
|
|
9
|
-
const ZIP_MTIME =
|
|
7
|
+
import { FileNotFoundError, REPRODUCIBLE_ZIP_MTIME } from 'blogwright-core';
|
|
8
|
+
/** Fixed timestamp for reproducible zips. See {@link REPRODUCIBLE_ZIP_MTIME} for why it is not UTC-constructed. */
|
|
9
|
+
const ZIP_MTIME = REPRODUCIBLE_ZIP_MTIME;
|
|
10
10
|
/**
|
|
11
11
|
* Filename injected into the source zip carrying the build revision. The site build
|
|
12
12
|
* runs in a MicroVM from this zip (no `.git`), so `astro.config` reads the hash from
|
|
13
|
-
* this file
|
|
13
|
+
* this file - see the repo's astro.config.mjs.
|
|
14
14
|
*/
|
|
15
15
|
export const COMMIT_FILE = '.commit-hash';
|
|
16
16
|
/**
|
|
@@ -20,7 +20,7 @@ export const COMMIT_FILE = '.commit-hash';
|
|
|
20
20
|
*/
|
|
21
21
|
export async function listRepoFiles(ports, cwd, ignore, include = []) {
|
|
22
22
|
const files = await ports.vcs.listFiles(cwd);
|
|
23
|
-
// An ignore entry matches an exact path or a directory boundary
|
|
23
|
+
// An ignore entry matches an exact path or a directory boundary - "dist"
|
|
24
24
|
// drops dist and dist/**, never dist-notes.md (files silently missing from
|
|
25
25
|
// the deployed site are painful to trace back to a prefix collision).
|
|
26
26
|
const matches = (f, entry) => {
|
|
@@ -54,12 +54,12 @@ async function includedFiles(fs, cwd, entry) {
|
|
|
54
54
|
catch (err) {
|
|
55
55
|
if (err instanceof FileNotFoundError && (await fs.exists(abs)))
|
|
56
56
|
return [rel]; // a single file
|
|
57
|
-
throw new Error(`sourceInclude path "${entry}" is missing or empty
|
|
57
|
+
throw new Error(`sourceInclude path "${entry}" is missing or empty - run the pre-deploy build that produces it before deploying`, { cause: err });
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* Build a deterministic zip of the given files (read relative to cwd). Extra entries
|
|
62
|
-
* (path → text content) are added in memory
|
|
62
|
+
* (path → text content) are added in memory - used to inject build metadata such as
|
|
63
63
|
* the commit hash without touching the working tree.
|
|
64
64
|
*/
|
|
65
65
|
export async function buildRepoZip(fs, cwd, files, extra = {}) {
|
package/dist/seo.d.ts
CHANGED
|
@@ -10,6 +10,6 @@ export interface SeoDirectives {
|
|
|
10
10
|
* Resolve robots.txt / sitemap.xml directives for a deploy from the environment + config.
|
|
11
11
|
* `baseUrl` is the canonical origin the site is served from (e.g. https://example.com).
|
|
12
12
|
* Defaults are environment-aware: production is indexable with a sitemap; every other
|
|
13
|
-
* environment blocks crawlers and skips the sitemap
|
|
13
|
+
* environment blocks crawlers and skips the sitemap - all overridable via config.seo.
|
|
14
14
|
*/
|
|
15
15
|
export declare function resolveSeo(ctx: OpsContext, baseUrl: string | undefined): SeoDirectives;
|
package/dist/seo.js
CHANGED
|
@@ -11,7 +11,7 @@ function indexRobots(sitemapUrl) {
|
|
|
11
11
|
* Resolve robots.txt / sitemap.xml directives for a deploy from the environment + config.
|
|
12
12
|
* `baseUrl` is the canonical origin the site is served from (e.g. https://example.com).
|
|
13
13
|
* Defaults are environment-aware: production is indexable with a sitemap; every other
|
|
14
|
-
* environment blocks crawlers and skips the sitemap
|
|
14
|
+
* environment blocks crawlers and skips the sitemap - all overridable via config.seo.
|
|
15
15
|
*/
|
|
16
16
|
export function resolveSeo(ctx, baseUrl) {
|
|
17
17
|
const isProd = !ctx.preview && ctx.env === 'production';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blogwright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-beta.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"blogwright": "./dist/bin.js",
|
|
@@ -17,16 +17,16 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"
|
|
21
|
-
"blogwright-
|
|
22
|
-
"
|
|
20
|
+
"blogwright-core": "0.4.0-beta.0",
|
|
21
|
+
"blogwright-pds": "0.4.0-beta.0",
|
|
22
|
+
"fflate": "^0.8.3"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
+
"blogwright-build-agent": "0.1.0",
|
|
25
26
|
"@types/node": "^26.1.0",
|
|
26
27
|
"oxlint": "^1.72.0",
|
|
27
28
|
"typescript": "^6.0.3",
|
|
28
|
-
"vitest": "^4.1.9"
|
|
29
|
-
"blogwright-build-agent": "0.1.0"
|
|
29
|
+
"vitest": "^4.1.9"
|
|
30
30
|
},
|
|
31
31
|
"description": "Full operations for a blog site on AWS: S3 + CloudFront hosting with builds in a Lambda MicroVM, PR previews, GitHub-OIDC CI deploys, and standard.site (AT Protocol) publishing",
|
|
32
32
|
"keywords": [
|