diff --git a/packages/bootstrap/package.json b/packages/bootstrap/package.json index ab6dddf88..b6d3d9047 100644 --- a/packages/bootstrap/package.json +++ b/packages/bootstrap/package.json @@ -15,20 +15,17 @@ "homepage": "https://workglow.dev", "scripts": { "watch": "concurrently -c 'auto' 'bun:watch-*'", - "watch-js": "concurrently -c 'auto' -n 'browser,node,bun' 'bun run watch-browser' 'bun run watch-node' 'bun run watch-bun'", + "watch-js": "concurrently -c 'auto' -n 'browser,node' 'bun run watch-browser' 'bun run watch-node'", "watch-browser": "bun build --watch --no-clear-screen --target=browser --sourcemap=external --packages=external --outdir ./dist ./src/browser.ts", "watch-node": "bun build --watch --no-clear-screen --target=node --sourcemap=external --packages=external --outdir ./dist ./src/node.ts", - "watch-bun": "bun build --watch --no-clear-screen --target=bun --sourcemap=external --packages=external --outdir ./dist ./src/bun.ts", "watch-types": "tsc --watch --preserveWatchOutput", - "build-package": "concurrently -c 'auto' -n 'browser,node,bun,types' 'bun run build-browser' 'bun run build-node' 'bun run build-bun' 'bun run build-types'", - "build-js": "concurrently -m 12 --timings -c 'auto' -n 'browser,node,bun' 'bun run build-browser' 'bun run build-node' 'bun run build-bun'", + "build-package": "concurrently -c 'auto' -n 'browser,node,types' 'bun run build-browser' 'bun run build-node' 'bun run build-types'", + "build-js": "concurrently -m 12 --timings -c 'auto' -n 'browser,node' 'bun run build-browser' 'bun run build-node'", "build-clean": "rm -fr dist/* tsconfig.tsbuildinfo", "build-browser": "bun build --target=browser --sourcemap=external --packages=external --outdir ./dist ./src/browser.ts", "build-node": "bun build --target=node --sourcemap=external --packages=external --outdir ./dist ./src/node.ts", - "build-bun": "bun build --target=bun --sourcemap=external --packages=external --outdir ./dist ./src/bun.ts", "build-types": "rm -f tsconfig.tsbuildinfo && tsgo", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", - "test": "bun test" + "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" }, "peerDependencies": { "@workglow/ai": "workspace:*", @@ -76,10 +73,6 @@ "types": "./dist/browser.d.ts", "import": "./dist/browser.js" }, - "bun": { - "types": "./dist/bun.d.ts", - "import": "./dist/bun.js" - }, "types": "./dist/node.d.ts", "import": "./dist/node.js" } diff --git a/packages/bootstrap/src/bun.ts b/packages/bootstrap/src/bun.ts deleted file mode 100644 index fc0d3716d..000000000 --- a/packages/bootstrap/src/bun.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @license - * Copyright 2026 Steven Roussey - * SPDX-License-Identifier: Apache-2.0 - */ - -// organize-imports-ignore - -export * from "./common"; diff --git a/packages/bootstrap/tsconfig.json b/packages/bootstrap/tsconfig.json index 18593a5e8..0696699c2 100644 --- a/packages/bootstrap/tsconfig.json +++ b/packages/bootstrap/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "../../tsconfig.json", "include": ["src/common.ts", "src/*/**/*"], - "files": ["./src/browser.ts", "./src/node.ts", "./src/bun.ts"], + "files": ["./src/browser.ts", "./src/node.ts"], "exclude": ["dist", "node_modules"], "compilerOptions": { "composite": true, diff --git a/packages/test/src/test/util/BootstrapReadme.test.ts b/packages/test/src/test/util/BootstrapReadme.test.ts index 1316e083b..a45def5e7 100644 --- a/packages/test/src/test/util/BootstrapReadme.test.ts +++ b/packages/test/src/test/util/BootstrapReadme.test.ts @@ -14,7 +14,13 @@ * `TaskGraphRunConfig` has a `context` key — so with a loosely typed `Input` * that object is an input override named `context`, the run keeps the global * registry, and `ctx.dispose()` tears down a registry the task never touched. - * Both halves are pinned below so the snippet cannot silently rot back. + * + * Two halves are pinned below. The first `describe` exercises both shapes at + * runtime, proving the documented one works and the old one does not. The + * second reads the two documents themselves, so a snippet that rots back is a + * test failure rather than prose nobody re-checks. The document cases scan + * every fenced block for the wrong shape, so a deliberate counter-example in + * the README would trip them — write the counter-example as prose instead. */ import { createOrchestrationContext } from "@workglow/bootstrap"; @@ -23,8 +29,30 @@ import { Task } from "@workglow/task-graph"; import type { ServiceRegistry } from "@workglow/util"; import { globalServiceRegistry } from "@workglow/util"; import type { DataPortSchema } from "@workglow/util/schema"; +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; import { describe, expect, it } from "vitest"; +const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "../../../../.."); +const README_PATH = join(REPO_ROOT, "packages/bootstrap/README.md"); +const SOURCE_PATH = join(REPO_ROOT, "packages/bootstrap/src/bootstrap/bootstrapWorkglow.ts"); + +/** The registry travelling in the run config — `run()`'s second argument. */ +const REGISTRY_IN_RUN_CONFIG = + /\.run\(\s*\{\s*\}\s*,\s*\{\s*registry:\s*\w+\.registry[\s,]*\}\s*\)/; +/** The shape that silently becomes an input override named `context`. */ +const CONTEXT_AS_INPUT = /\.run\(\s*\{\s*context\s*:/; + +/** + * Every fenced code block's body, whatever its info string. Selecting blocks by + * content rather than by the heading above them keeps a heading rename from + * making these cases pass vacuously. + */ +function fencedBlocks(markdown: string): string[] { + return [...markdown.matchAll(/^```[^\n]*\n([\s\S]*?)^```/gm)].map((match) => match[1]!); +} + interface RegistryProbeOutput extends TaskOutput { isGlobal: boolean; } @@ -93,3 +121,37 @@ describe("the @workglow/bootstrap README isolated-context example", () => { } }); }); + +describe("the documents the example is transcribed from", () => { + it("README documents the registry in the run config", () => { + const blocks = fencedBlocks(readFileSync(README_PATH, "utf8")).filter((block) => + block.includes("createOrchestrationContext(") + ); + + expect(blocks.length).toBeGreaterThan(0); + expect(blocks.some((block) => REGISTRY_IN_RUN_CONFIG.test(block))).toBe(true); + }); + + it("no README code block passes the context as an input override", () => { + // Code only: the surrounding prose deliberately discusses "an input named + // `context`" and has to stay legal. + const offenders = fencedBlocks(readFileSync(README_PATH, "utf8")).filter((block) => + CONTEXT_AS_INPUT.test(block) + ); + + expect(offenders).toEqual([]); + }); + + it("the createOrchestrationContext JSDoc shows the same shape", () => { + const source = readFileSync(SOURCE_PATH, "utf8"); + const jsdoc = source.match( + /\/\*\*([\s\S]*?)\*\/\s*export function createOrchestrationContext\b/ + ); + + expect(jsdoc).not.toBeNull(); + + const comment = jsdoc![1]!.replace(/^\s*\*/gm, ""); + expect(REGISTRY_IN_RUN_CONFIG.test(comment)).toBe(true); + expect(CONTEXT_AS_INPUT.test(comment)).toBe(false); + }); +});