@brainervirus/workit-core 8.4.0 → 8.4.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-core",
3
- "version": "8.4.0",
3
+ "version": "8.4.1",
4
4
  "private": false,
5
5
  "description": "Workit shared core — task, policy, evidence, review, decision, worker, and writer state for agentic coding workflows",
6
6
  "keywords": [
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env bun
2
+ // Release guard: every adapter dist/ must inline the workspace's own
3
+ // @brainervirus sources, never a registry copy. bun build resolves the nearest
4
+ // node_modules first, so an installed packages/<pkg>/node_modules/@brainervirus
5
+ // copy silently replaces the tagged source (8.0.0 shipped 7.7.0's core that
6
+ // way). Two checks:
7
+ // - installs: no packages/*/node_modules/@brainervirus/* entry, and every root
8
+ // node_modules/@brainervirus/* entry resolves inside packages/ (run before
9
+ // the build; it also covers minified bundles, which carry no path headers);
10
+ // - bundles: bun prefixes each inlined module with a `// <path>` header; a
11
+ // header under node_modules/@brainervirus/ names a stale copy, and every
12
+ // non-minified entry (with its split chunks) must inline core from
13
+ // packages/workit-core/src/.
14
+ // `--installs-only` runs only the first check (prepareCmd, before the build).
15
+ import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
16
+ import { dirname, join, relative, resolve, sep } from "node:path";
17
+ import { RELEASE_PACKAGES } from "./analyze-release-scope";
18
+
19
+ // Separator-agnostic: a Windows build may write `\` in module headers.
20
+ const REGISTRY_COPY = /^\/\/ (.*node_modules[\\/]@brainervirus[\\/].+)$/gmu;
21
+ const WORKSPACE_CORE = /^\/\/ packages[\\/]workit-core[\\/]src[\\/]/mu;
22
+ /** bun's unminified output averages well under 100 bytes a line; --minify is thousands. */
23
+ const MINIFIED_BYTES_PER_LINE = 1000;
24
+
25
+ const entries = (dir: string): string[] => (existsSync(dir) ? readdirSync(dir) : []);
26
+ /** `root`-relative path with `/` separators, for messages on every OS. */
27
+ const rel = (root: string, file: string): string => relative(root, file).split(sep).join("/");
28
+ /** Windows paths compare case-insensitively. */
29
+ const fold = (file: string): string => (process.platform === "win32" ? file.toLowerCase() : file);
30
+
31
+ /** Installed @brainervirus packages a build could resolve instead of workspace source. */
32
+ export function registryInstalls(root: string): string[] {
33
+ const nested = entries(join(root, "packages")).flatMap((pkg) => {
34
+ const scope = join(root, "packages", pkg, "node_modules", "@brainervirus");
35
+ return entries(scope).map((name) => rel(root, join(scope, name)));
36
+ });
37
+ const packages = fold(join(realpathSync(root), "packages") + sep);
38
+ const scope = join(root, "node_modules", "@brainervirus");
39
+ const hoisted = entries(scope)
40
+ .filter((name) => !fold(realpathSync(join(scope, name))).startsWith(packages))
41
+ .map((name) => rel(root, join(scope, name)));
42
+ return [...nested, ...hoisted];
43
+ }
44
+
45
+ function bundles(dir: string): string[] {
46
+ return entries(dir).flatMap((name) => {
47
+ const file = join(dir, name);
48
+ if (statSync(file).isDirectory()) return bundles(file);
49
+ return name.endsWith(".js") ? [file] : [];
50
+ });
51
+ }
52
+
53
+ export type BundleProblem = { bundle: string; problem: string };
54
+
55
+ /** Sibling chunks a split bundle imports (`from "./x.js"`, `import("./x.js")`). */
56
+ const LOCAL_CHUNK = /(?:\bfrom|\bimport\s*\()\s*["']\.\/([^"']+\.js)["']/gu;
57
+
58
+ /**
59
+ * Built adapter bundles under `root`, and what each one inlined wrongly. A
60
+ * code-split entry (codex's hook) inlines core through its chunks, so the
61
+ * core-header requirement applies to each entry together with the chunks it
62
+ * reaches; a chunk only another bundle imports is not an entry.
63
+ */
64
+ export function bundleSources(root: string): { scanned: string[]; problems: BundleProblem[] } {
65
+ const scanned = RELEASE_PACKAGES.filter((pkg) => pkg !== "workit-core").flatMap((pkg) =>
66
+ bundles(join(root, "packages", pkg, "dist")),
67
+ );
68
+ const text = new Map(scanned.map((file) => [file, readFileSync(file, "utf8")]));
69
+ const chunks = (file: string): string[] =>
70
+ [...(text.get(file) ?? "").matchAll(LOCAL_CHUNK)]
71
+ .map((m) => join(dirname(file), m[1]))
72
+ .filter((chunk) => text.has(chunk));
73
+ const imported = new Set(scanned.flatMap(chunks));
74
+ const reachesCore = (entry: string): boolean => {
75
+ const seen = new Set<string>();
76
+ const queue = [entry];
77
+ while (queue.length) {
78
+ const file = queue.shift() as string;
79
+ if (seen.has(file)) continue;
80
+ seen.add(file);
81
+ if (WORKSPACE_CORE.test(text.get(file) ?? "")) return true;
82
+ queue.push(...chunks(file));
83
+ }
84
+ return false;
85
+ };
86
+ const problems = scanned.flatMap((file): BundleProblem[] => {
87
+ const bundle = rel(root, file);
88
+ const body = text.get(file) ?? "";
89
+ const copies = [...body.matchAll(REGISTRY_COPY)].map((m) => m[1].replaceAll("\\", "/"));
90
+ if (copies.length)
91
+ return [{ bundle, problem: `${copies.length} registry-copy modules, e.g. ${copies[0]}` }];
92
+ const minified = body.length / (body.split("\n").length || 1) > MINIFIED_BYTES_PER_LINE;
93
+ if (minified || imported.has(file) || reachesCore(file)) return [];
94
+ const first = /^\s*\/\/ (\S.*)$/mu.exec(body)?.[1] ?? "none";
95
+ return [
96
+ {
97
+ bundle,
98
+ problem: `no \`// packages/workit-core/src/\` module header in it or its chunks (first header: ${first})`,
99
+ },
100
+ ];
101
+ });
102
+ return { scanned: scanned.map((file) => rel(root, file)), problems };
103
+ }
104
+
105
+ /** Every failure under `root` as printable lines; empty when the release may proceed. */
106
+ export function verifyBundleSources(root: string, options: { installsOnly?: boolean } = {}) {
107
+ const failures = registryInstalls(root).map(
108
+ (path) => `${path}: installed copy outside packages/ (bundles would inline it)`,
109
+ );
110
+ if (options.installsOnly) return { failures, scanned: 0 };
111
+ const { scanned, problems } = bundleSources(root);
112
+ if (scanned.length === 0)
113
+ failures.push(`no adapter bundles under packages/*/dist; run \`bun run build\` first`);
114
+ for (const { bundle, problem } of problems) failures.push(`${bundle}: ${problem}`);
115
+ return { failures, scanned: scanned.length };
116
+ }
117
+
118
+ if (import.meta.main) {
119
+ const args = process.argv.slice(2);
120
+ const installsOnly = args.includes("--installs-only");
121
+ const root = resolve(args.find((arg) => !arg.startsWith("--")) ?? process.cwd());
122
+ const { failures, scanned } = verifyBundleSources(root, { installsOnly });
123
+ if (failures.length) {
124
+ console.error("adapter bundles would not inline the workspace's own sources:");
125
+ for (const failure of failures) console.error(` - ${failure}`);
126
+ console.error("remove the copies and rebuild; see release.yml NPM_CONFIG_WORKSPACES_UPDATE");
127
+ process.exit(1);
128
+ }
129
+ console.log(
130
+ installsOnly
131
+ ? "no registry copies of workspace packages installed"
132
+ : `verified ${scanned} adapter bundles inline workspace sources`,
133
+ );
134
+ }