@capybara-hq/plugin-tools 0.1.0-rc.0 → 0.1.0-rc.2

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 CHANGED
@@ -14,8 +14,6 @@ npm install --save-dev @capybara-hq/plugin-tools
14
14
 
15
15
  ```bash
16
16
  capybara-plugin pack [<dir>...] [--all <root>] [--out <dir>]
17
- capybara-plugin pack --bootstrap <manifest> [--root <dir>] [--out <dir>] [--registry <url>]
18
- [--npm-token <token>]
19
17
  capybara-plugin check [--root <dir>] [--plugins <glob>] [--base <ref>]
20
18
  capybara-plugin sync-versions [--root <dir>] [--plugins <glob>]
21
19
  capybara-plugin marketplace --write|--check [--base <rev>] [--root <dir>] [--file <path>]
@@ -30,12 +28,8 @@ capybara-plugin release [--dry-run] [--provenance] [--out <dir>] [--auto] [--roo
30
28
  manifest written into `plugin.json`, LICENSE / NOTICE and the bundled dependencies' notices
31
29
  included. A plugin with an extension gets `"main"` and `"exports": { ".": "./dist/index.mjs" }`,
32
30
  so once installed it resolves by package name (a test loads `@capybara-hq/plugin-model-fake`
33
- from npm). `--all <root>` packs every plugin of a repository; `--bootstrap` packs the entries of
34
- a bootstrap manifest (workspace entries from the repository, npm entries downloaded from
35
- `--registry`, default `CAPYBARA_PACKAGES__NPM_REGISTRY` or the public registry, with
36
- `--npm-token` / `CAPYBARA_PACKAGES__NPM_TOKEN` sent to the registry origin only, tarballs capped
37
- at 50 MiB) and checks them against their pinned sha512 and their `package.json` name and version.
38
- Output: `artifacts/plugins`.
31
+ from npm). `--all <root>` packs every plugin of a repository. Output: `artifacts/plugins`. (The
32
+ bootstrap plugins a kernel pins are no longer packed here: a starting kernel downloads them.)
39
33
  - **check**: every plugin check, on the plugins under `<root>/plugins/*` (or `--plugins <glob>`, or
40
34
  the root itself when it is a plugin): the dependency boundary (a plugin depends only on
41
35
  `@capybara-hq/plugin-sdk` and third-party packages), extension state under multiple replicas (no
@@ -93,9 +87,15 @@ export default defineConfig({
93
87
  });
94
88
  ```
95
89
 
90
+ Dual-replica tests (`createDualReplicaHarness`) need nothing more: each replica is a child
91
+ process outside vitest, and `@capybara-hq/testing` runs it under its own tsx when an extension
92
+ entry is a TypeScript file (the plugin under test by path, `fakeExtensionEntry(...)`). Do not set
93
+ `NODE_OPTIONS: "--import tsx"` in the vitest config: it reaches every worker and child process
94
+ and hides packaging bugs.
95
+
96
96
  ## Library
97
97
 
98
- The same code as functions: `packPlugin`, `packBootstrap`, `pluginChecks` / `runChecks` and each
98
+ The same code as functions: `packPlugin`, `downloadPinnedNpmPackage`, `pluginChecks` / `runChecks` and each
99
99
  check (`checkDeps`, `checkPluginState`, `checkPluginPermissions`, `checkPluginPack`,
100
100
  `checkToolDesign`, `checkMigrations`, `checkAppliedMigrations`, `checkPluginTestImports`,
101
101
  `checkPluginReleaseMetadata`), `syncPluginVersions` (after `changeset version`),
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // The `capybara-plugin` bin: runs the compiled CLI (dist/cli.js, built by `tsc -b`). In this
3
- // repository, the root scripts (pnpm pack-plugin, pack-plugins, pack-bootstrap, release) run the
3
+ // repository, the root scripts (pnpm pack-plugin, pack-plugins, release) run the
4
4
  // TypeScript sources through tsx instead.
5
5
  import { existsSync } from "node:fs";
6
6
 
package/dist/cli.js CHANGED
@@ -2,8 +2,6 @@
2
2
  * `capybara-plugin`: pack, check, sync the versions of and release Capybara plugins.
3
3
  *
4
4
  * capybara-plugin pack [<dir>...] [--all <root>] [--out <dir>]
5
- * capybara-plugin pack --bootstrap <manifest> [--root <dir>] [--out <dir>] [--registry <url>]
6
- * [--npm-token <token>]
7
5
  * capybara-plugin check [--root <dir>] [--plugins <glob>] [--base <ref>]
8
6
  * capybara-plugin sync-versions [--root <dir>] [--plugins <glob>]
9
7
  * capybara-plugin marketplace --write|--check [--base <rev>] [--root <dir>] [--file <path>]
@@ -1,19 +1,12 @@
1
- export declare const PACK_USAGE: string;
1
+ export declare const PACK_USAGE = "capybara-plugin pack [<dir>...] [--all <root>] [--out <dir>]";
2
2
  /**
3
3
  * `capybara-plugin pack`: packs plugin directories into plugin tgz files.
4
4
  *
5
5
  * - `<dir>...`: the plugin directories to pack;
6
6
  * - `--all <root>`: also every plugin of the repository at `<root>` (`<root>/plugins/*`, or `<root>`
7
7
  * itself when it is a plugin);
8
- * - `--bootstrap <manifest>`: the bootstrap directory an image bakes: every entry of the bootstrap
9
- * manifest as `<name>-<version>.tgz`, checked against its pins (workspace entries' `path` is
10
- * relative to `--root`, default the current directory; npm entries are downloaded from
11
- * `--registry`, default `CAPYBARA_PACKAGES__NPM_REGISTRY` (the kernel's `packages.npmRegistry`)
12
- * or else the public npm registry, and must match their pinned sha512; `--npm-token`, default
13
- * `CAPYBARA_PACKAGES__NPM_TOKEN` (the kernel's `packages.npmToken`), is sent as a Bearer token to
14
- * the registry origin only);
15
- * - `--out <dir>`: output directory, default `artifacts/plugins` (`artifacts/bootstrap` with
16
- * `--bootstrap`) under the current directory. The last `--out` wins.
8
+ * - `--out <dir>`: output directory, default `artifacts/plugins` under the current directory. The
9
+ * last `--out` wins.
17
10
  *
18
11
  * Relative paths resolve against `cwd` (the current directory). Returns the exit code.
19
12
  */
package/dist/pack/cli.js CHANGED
@@ -1,27 +1,16 @@
1
1
  import { relative, resolve } from "node:path";
2
2
  import { parseArgs } from "node:util";
3
3
  import { discoverPlugins } from "../discover.js";
4
- import { packBootstrap } from "./bootstrap.js";
5
- import { DEFAULT_NPM_REGISTRY } from "./npm.js";
6
4
  import { packPlugin } from "./pack.js";
7
- export const PACK_USAGE = "capybara-plugin pack [<dir>...] [--all <root>] [--out <dir>]\n" +
8
- " capybara-plugin pack --bootstrap <manifest> [--root <dir>] [--out <dir>] [--registry <url>]\n" +
9
- " [--npm-token <token>]";
5
+ export const PACK_USAGE = "capybara-plugin pack [<dir>...] [--all <root>] [--out <dir>]";
10
6
  /**
11
7
  * `capybara-plugin pack`: packs plugin directories into plugin tgz files.
12
8
  *
13
9
  * - `<dir>...`: the plugin directories to pack;
14
10
  * - `--all <root>`: also every plugin of the repository at `<root>` (`<root>/plugins/*`, or `<root>`
15
11
  * itself when it is a plugin);
16
- * - `--bootstrap <manifest>`: the bootstrap directory an image bakes: every entry of the bootstrap
17
- * manifest as `<name>-<version>.tgz`, checked against its pins (workspace entries' `path` is
18
- * relative to `--root`, default the current directory; npm entries are downloaded from
19
- * `--registry`, default `CAPYBARA_PACKAGES__NPM_REGISTRY` (the kernel's `packages.npmRegistry`)
20
- * or else the public npm registry, and must match their pinned sha512; `--npm-token`, default
21
- * `CAPYBARA_PACKAGES__NPM_TOKEN` (the kernel's `packages.npmToken`), is sent as a Bearer token to
22
- * the registry origin only);
23
- * - `--out <dir>`: output directory, default `artifacts/plugins` (`artifacts/bootstrap` with
24
- * `--bootstrap`) under the current directory. The last `--out` wins.
12
+ * - `--out <dir>`: output directory, default `artifacts/plugins` under the current directory. The
13
+ * last `--out` wins.
25
14
  *
26
15
  * Relative paths resolve against `cwd` (the current directory). Returns the exit code.
27
16
  */
@@ -33,10 +22,6 @@ export async function packCommand(args, cwd) {
33
22
  help: { type: "boolean", short: "h", default: false },
34
23
  all: { type: "string" },
35
24
  out: { type: "string" },
36
- bootstrap: { type: "string" },
37
- root: { type: "string" },
38
- registry: { type: "string" },
39
- "npm-token": { type: "string" },
40
25
  },
41
26
  });
42
27
  if (values.help) {
@@ -48,28 +33,6 @@ export async function packCommand(args, cwd) {
48
33
  const rel = relative(cwd, p);
49
34
  return rel === "" ? "." : rel.startsWith("..") ? p : rel;
50
35
  };
51
- if (values.bootstrap !== undefined) {
52
- if (values.all !== undefined || positionals.length > 0) {
53
- return usage("--bootstrap takes no plugin directory");
54
- }
55
- try {
56
- const root = values.root === undefined ? cwd : resolve(cwd, values.root);
57
- const manifest = resolve(cwd, values.bootstrap);
58
- const registry = values.registry ?? (process.env.CAPYBARA_PACKAGES__NPM_REGISTRY || DEFAULT_NPM_REGISTRY);
59
- const token = values["npm-token"] ?? process.env.CAPYBARA_PACKAGES__NPM_TOKEN;
60
- for (const p of await packBootstrap(manifest, root, outOf("artifacts/bootstrap"), {
61
- registry,
62
- ...(token !== undefined && token !== "" && { token }),
63
- })) {
64
- console.log(`${shown(p.file)} ${p.integrity}`);
65
- }
66
- return 0;
67
- }
68
- catch (e) {
69
- console.error(e instanceof Error ? e.message : String(e));
70
- return 1;
71
- }
72
- }
73
36
  const plugins = positionals.map((d) => resolve(cwd, d));
74
37
  if (values.all !== undefined) {
75
38
  const root = resolve(cwd, values.all);
@@ -1,4 +1,3 @@
1
- export { type BootstrapPack, packBootstrap } from "./bootstrap.js";
2
1
  export { PACK_USAGE, packCommand } from "./cli.js";
3
2
  export { assertNpmTarballLabel, DEFAULT_NPM_REGISTRY, downloadPinnedNpmPackage, fetchNpmTarball, NPM_TARBALL_MAX_BYTES, type NpmFetchOptions, NpmPackageError, type NpmPackageErrorKind, } from "./npm.js";
4
3
  export { METADATA_FIELDS, NOTICES_FILE, type PackResult, packPlugin, STAGING_PREFIX, } from "./pack.js";
@@ -1,4 +1,3 @@
1
- export { packBootstrap } from "./bootstrap.js";
2
1
  export { PACK_USAGE, packCommand } from "./cli.js";
3
2
  export { assertNpmTarballLabel, DEFAULT_NPM_REGISTRY, downloadPinnedNpmPackage, fetchNpmTarball, NPM_TARBALL_MAX_BYTES, NpmPackageError, } from "./npm.js";
4
3
  export { METADATA_FIELDS, NOTICES_FILE, packPlugin, STAGING_PREFIX, } from "./pack.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capybara-hq/plugin-tools",
3
- "version": "0.1.0-rc.0",
3
+ "version": "0.1.0-rc.2",
4
4
  "description": "Capybara plugin toolchain: the capybara-plugin CLI to pack, check and release Capybara plugins",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -51,12 +51,12 @@
51
51
  "esbuild": "0.28.2",
52
52
  "oxc-parser": "^0.154.0",
53
53
  "tar-stream": "3.2.2",
54
- "@capybara-hq/kernel": "^0.1.0-rc.0",
54
+ "@capybara-hq/kernel": "^0.1.0-rc.1",
55
55
  "@capybara-hq/plugin-sdk": "^0.1.0-rc.0"
56
56
  },
57
57
  "devDependencies": {
58
58
  "zod": "^4.6.7",
59
- "@capybara-hq/testing": "0.1.0-rc.0"
59
+ "@capybara-hq/testing": "0.1.0-rc.2"
60
60
  },
61
61
  "scripts": {
62
62
  "build": "tsc -b"
@@ -1,25 +0,0 @@
1
- import { type NpmFetchOptions } from "./npm.js";
2
- /** One package {@link packBootstrap} wrote. */
3
- export interface BootstrapPack {
4
- name: string;
5
- version: string;
6
- file: string;
7
- integrity: string;
8
- }
9
- /**
10
- * Produces the bootstrap directory the image bakes (package ecosystem spec §7): every entry of the
11
- * bootstrap manifest (`packages/kernel/bootstrap.json`) as `<outDir>/<name>-<version>.tgz`. A
12
- * `source: "workspace"` entry is packed from `<repoRoot>/<path>` with {@link packPlugin}
13
- * (byte-reproducible) and must equal the entry: the plugin name, the version and the pinned sha512,
14
- * otherwise this throws naming the plugin and writes nothing for it (the image build fails rather
15
- * than baking a package `capybara bootstrap` would refuse). A `source: "npm"` entry is downloaded
16
- * from `opts.registry` (default the public npm registry, the kernel's `packages.npmRegistry`
17
- * default; `opts.token` goes to the registry origin only) and written only when its bytes match
18
- * the pinned sha512 and its package.json names the entry's package and version; a tampered,
19
- * mislabelled or oversized (`bootstrap.too_large`) tarball or an unpublished package or version
20
- * throws naming the plugin. So npm entries need the network at
21
- * build time only: init installs from the baked directory and never downloads.
22
- */
23
- export declare function packBootstrap(manifestFile: string, repoRoot: string, outDir: string, opts?: {
24
- registry?: string;
25
- } & NpmFetchOptions): Promise<BootstrapPack[]>;
@@ -1,70 +0,0 @@
1
- import { copyFile, mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
- import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
- import { bootstrapFileName, parseBootstrapManifest } from "@capybara-hq/plugin-sdk";
5
- import { integrityOf } from "../integrity.js";
6
- import { DEFAULT_NPM_REGISTRY, downloadPinnedNpmPackage } from "./npm.js";
7
- import { packPlugin } from "./pack.js";
8
- /**
9
- * Produces the bootstrap directory the image bakes (package ecosystem spec §7): every entry of the
10
- * bootstrap manifest (`packages/kernel/bootstrap.json`) as `<outDir>/<name>-<version>.tgz`. A
11
- * `source: "workspace"` entry is packed from `<repoRoot>/<path>` with {@link packPlugin}
12
- * (byte-reproducible) and must equal the entry: the plugin name, the version and the pinned sha512,
13
- * otherwise this throws naming the plugin and writes nothing for it (the image build fails rather
14
- * than baking a package `capybara bootstrap` would refuse). A `source: "npm"` entry is downloaded
15
- * from `opts.registry` (default the public npm registry, the kernel's `packages.npmRegistry`
16
- * default; `opts.token` goes to the registry origin only) and written only when its bytes match
17
- * the pinned sha512 and its package.json names the entry's package and version; a tampered,
18
- * mislabelled or oversized (`bootstrap.too_large`) tarball or an unpublished package or version
19
- * throws naming the plugin. So npm entries need the network at
20
- * build time only: init installs from the baked directory and never downloads.
21
- */
22
- export async function packBootstrap(manifestFile, repoRoot, outDir, opts = {}) {
23
- const parsed = parseBootstrapManifest(JSON.parse(await readFile(manifestFile, "utf8")));
24
- if ("errors" in parsed) {
25
- throw new Error(`invalid bootstrap manifest ${manifestFile}: ${parsed.errors.join("; ")}`);
26
- }
27
- await mkdir(outDir, { recursive: true });
28
- const staging = await mkdtemp(join(tmpdir(), "pack-bootstrap-"));
29
- const out = [];
30
- try {
31
- for (const e of parsed.plugins) {
32
- if (e.source === "npm") {
33
- let bytes;
34
- try {
35
- bytes = await downloadPinnedNpmPackage(e, opts.registry ?? DEFAULT_NPM_REGISTRY, {
36
- ...(opts.token !== undefined && { token: opts.token }),
37
- ...(opts.maxBytes !== undefined && { maxBytes: opts.maxBytes }),
38
- });
39
- }
40
- catch (err) {
41
- throw new Error(`${e.name}: ${err instanceof Error ? err.message : String(err)}`, {
42
- cause: err,
43
- });
44
- }
45
- const file = join(outDir, bootstrapFileName(e));
46
- await writeFile(file, bytes);
47
- out.push({ name: e.name, version: e.version, file, integrity: e.integrity });
48
- continue;
49
- }
50
- const r = await packPlugin(join(repoRoot, e.path), staging);
51
- const integrity = integrityOf(new Uint8Array(await readFile(r.file)));
52
- const problems = [
53
- r.manifest.name !== e.name && `${e.path} packs the plugin "${r.manifest.name}"`,
54
- r.manifest.version !== e.version &&
55
- `version is ${e.version} but ${e.path} packs ${r.manifest.version}`,
56
- integrity !== e.integrity && `integrity is ${e.integrity} but ${e.path} packs ${integrity}`,
57
- ].filter((p) => typeof p === "string");
58
- if (problems.length > 0) {
59
- throw new Error(`${e.name}: ${problems.join("; ")}; the bootstrap manifest is stale (pnpm check:bootstrap --write)`);
60
- }
61
- const file = join(outDir, bootstrapFileName(e));
62
- await copyFile(r.file, file);
63
- out.push({ name: e.name, version: e.version, file, integrity });
64
- }
65
- }
66
- finally {
67
- await rm(staging, { recursive: true, force: true });
68
- }
69
- return out;
70
- }