diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index e27b2ce..9789919 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -12,6 +12,14 @@ updates:
labels:
- "dependencies"
+ - package-ecosystem: "npm"
+ directory: "/packages/core"
+ schedule:
+ interval: "weekly"
+ open-pull-requests-limit: 5
+ labels:
+ - "dependencies"
+
- package-ecosystem: "github-actions"
directory: "/"
schedule:
diff --git a/.github/workflows/deploy-use-cases-site.yml b/.github/workflows/deploy-use-cases-site.yml
index 42a4a0e..33dc2f8 100644
--- a/.github/workflows/deploy-use-cases-site.yml
+++ b/.github/workflows/deploy-use-cases-site.yml
@@ -12,7 +12,7 @@ on:
- "apps/use-cases-site/**"
- "package.json"
- "package-lock.json"
- - "samples/**"
+ - "packages/core/**"
push:
branches:
- main
@@ -21,7 +21,7 @@ on:
- "apps/use-cases-site/**"
- "package.json"
- "package-lock.json"
- - "samples/**"
+ - "packages/core/**"
permissions:
contents: read
diff --git a/.github/workflows/release-provenance.yml b/.github/workflows/release-provenance.yml
index 2c6c946..260b10c 100644
--- a/.github/workflows/release-provenance.yml
+++ b/.github/workflows/release-provenance.yml
@@ -60,12 +60,12 @@ jobs:
- name: Publish dry run
if: inputs.dry_run == 'true'
- run: npm publish --provenance --access public --dry-run
+ run: npm publish --workspace @workit/core --provenance --access public --dry-run
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish
if: inputs.dry_run == 'false'
- run: npm publish --provenance --access public
+ run: npm publish --workspace @workit/core --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 5debc4a..6cef8d7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,8 @@ out/
lib/
!benchmarks/articles/lib/
!benchmarks/articles/lib/**
+!packages/core/benchmarks/articles/lib/
+!packages/core/benchmarks/articles/lib/**
coverage/
.nyc_output/
mnt/
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..0aaad6b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,25 @@
+
+
+# Changelog
+
+## 0.1.5
+
+Move `@workit/core` to `packages/core` monorepo layout. No public API changes.
+
+This release prepares the repository for future WorkIt extensions while keeping
+the published package contract unchanged.
+
+- `@workit/core` source, tests, samples, benchmarks, evidence, and release
+ scripts now live under `packages/core`.
+- Root package is now a private workspace coordinator.
+- Existing install and import paths remain unchanged.
+- Existing package exports remain unchanged.
+- Root runtime bundle size remains unchanged.
+- Release provenance workflow now publishes the workspace package.
+- Use-cases site now resolves the real local package from `packages/core`.
+
+This is an infrastructure release only. New runtime capabilities are planned for
+the next minor line after the monorepo layout is validated in CI and npm.
diff --git a/README.md b/README.md
index 0440c4c..aa796e3 100644
--- a/README.md
+++ b/README.md
@@ -3,26 +3,22 @@ Author: Admilson B. F. Cossa
SPDX-License-Identifier: Apache-2.0
-->
-# WorkIt
+
+
+
-Structured concurrency for TypeScript systems that need owned async work:
-bounded parallelism, cancellation, cleanup, retries, timeouts, budgets,
-backpressure, worker offload, and observable task lifecycles.
+# WorkIt
-Native `Promise` is still the right tool for one asynchronous value. WorkIt is
-for the next step: a request, batch, agent run, provider race, stream, or
-background operation where related async tasks must live, fail, cancel, and
-clean up together.
+WorkIt is a TypeScript structured concurrency runtime for Node.js server
+runtimes.
[](LICENSE)
-[](package.json)
-[](package.json)
-[](#verified-evidence)
-[](benchmarks/articles/)
+[](https://www.npmjs.com/package/@workit/core)
+[](packages/core/package.json)
[](https://www.bestpractices.dev/projects/12807)
-Live examples:
-npm package:
+WorkIt owns related async work through scope, cancellation, cleanup, context,
+events, and child task lifecycles.
## Install
@@ -30,526 +26,92 @@ npm package:
npm install @workit/core
```
-WorkIt targets Node.js server runtimes today. Browser and edge runtimes resolve
-to an explicit unsupported-runtime boundary.
-
-## Quick Start
-
```ts
-import { work } from "@workit/core";
-
-const doubled = await work([1, 2, 3])
- .inParallel(2)
- .do(async (value, _ctx) => value * 2);
+import { run, work } from "@workit/core";
```
-The context parameter is available when the task needs cancellation, progress,
-budgets, or scoped resources. It can be ignored for plain transformations.
-
-## Why Ownership Matters
-
-Consider this batch helper:
-
-```ts
-type BatchEvent =
- | { type: "item:started"; item: T; attempt: number }
- | { type: "item:retried"; item: T; attempt: number; error: unknown }
- | { type: "item:completed"; item: T };
-
-type BatchOptions = {
- concurrency: number;
- retries: number;
- timeoutMs: number;
- signal: AbortSignal;
- events: { emit(event: BatchEvent): void };
- run: (item: T, options: { signal: AbortSignal }) => Promise;
-};
-
-function backoffMs(attempt: number): number {
- return Math.min(1000 * 2 ** (attempt - 1), 10_000);
-}
-
-function sleep(ms: number, signal?: AbortSignal): Promise {
- if (signal?.aborted) return Promise.reject(signal.reason);
-
- return new Promise((resolve, reject) => {
- let settled = false;
-
- const finish = (): void => {
- if (settled) return;
- settled = true;
- signal?.removeEventListener("abort", abort);
- resolve();
- };
-
- const abort = (): void => {
- if (settled) return;
- settled = true;
- clearTimeout(timer);
- reject(signal?.reason);
- };
+WorkIt is Apache-2.0 licensed. Contributions are welcome through issues and
+pull requests; please follow [`CONTRIBUTING.md`](CONTRIBUTING.md) and
+[`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).
- const timer = setTimeout(finish, ms);
- signal?.addEventListener("abort", abort, { once: true });
- });
-}
-
-async function runBatch(
- items: readonly T[],
- options: BatchOptions
-): Promise {
- const results = new Array(items.length);
- let nextIndex = 0;
-
- async function worker(): Promise {
- while (!options.signal.aborted) {
- const index = nextIndex++;
- if (index >= items.length) return;
-
- const item = items[index];
- for (let attempt = 1; attempt <= options.retries + 1; attempt++) {
- const timeout = AbortSignal.timeout(options.timeoutMs);
- const signal = AbortSignal.any([options.signal, timeout]);
-
- try {
- options.events.emit({ type: "item:started", item, attempt });
- results[index] = await options.run(item, { signal });
- options.events.emit({ type: "item:completed", item });
- break;
- } catch (error) {
- if (attempt > options.retries || options.signal.aborted) throw error;
- options.events.emit({ type: "item:retried", item, attempt, error });
- await sleep(backoffMs(attempt), options.signal);
- }
- }
- }
- }
+Live examples:
- await Promise.all(
- Array.from({ length: options.concurrency }, () => worker())
- );
+npm package:
- return results;
+Changelog: [CHANGELOG.md](CHANGELOG.md)
+
+## Versioning
+
+WorkIt follows semver with a stricter release discipline:
+
+- Patch releases, such as `0.1.x`, are for fixes, build/release hardening,
+ layout migrations, documentation, and evidence updates. They must not add new
+ public runtime APIs.
+- Minor releases, such as `0.2.0`, may add new subpaths or feature families when
+ they are backed by tests, evidence, package-consumer checks, and
+ documentation.
+- The root `@workit/core` import remains size-disciplined. New heavier
+ capabilities should live in subpaths or companion packages.
+- `1.0.0` will mark a frozen public API and long-term compatibility policy, not
+ a shortcut for credibility. Current `0.x` releases are validated and usable,
+ with changes managed through semver and release notes.
+
+## Citation
+
+If you use WorkIt in research, benchmarks, or reproducible artifacts, please
+cite the software release you used:
+
+```bibtex
+@software{workit2026,
+ author = {Admilson B. F. Cossa},
+ title = {WorkIt: A TypeScript Structured Concurrency Runtime for Node.js Server Runtimes},
+ year = {2026},
+ url = {https://github.com/WorkRuntime/workit},
+ version = {0.1.5},
+ license = {Apache-2.0}
}
```
-It covers bounded parallelism, timeout, parent cancellation, ordered results,
-typed events, and retry backoff. The lifecycle is still split across the queue,
-timeout signals, retry loop, event sink, and caller. Adding sibling
-cancellation, cleanup, budgets, partial results, or diagnostics extends the
-same ownership protocol in several places.
-
-With WorkIt, the ownership boundary is the API:
-
-```ts
-import { work } from "@workit/core";
-
-const results = await work(items)
- .inParallel(8)
- .withRetry(3)
- .withTimeout("5s")
- .do(async (item, ctx) => {
- ctx.report({ message: `processing ${item.id}` });
- return apiCall(item, { signal: ctx.signal });
- });
-```
-
-This gives the batch one lifecycle contract:
-
-- at most 8 items run at once
-- transient failures retry with cancel-aware backoff
-- each item has a 5 second timeout
-- every task receives the same cancellation model through `ctx.signal`
-- progress is a typed runtime event
-- queued and active work stop together when the owner is cancelled
-
-## What WorkIt Replaces
-
-WorkIt does not replace promises as values. It replaces repeated lifecycle
-orchestration around promises.
-
-| Existing pattern | Real limitation | Ownership contract | WorkIt primitive |
-|---|---|---:|---|
-| Hand-written concurrency queue | Queue, retry, timeout, and caller cancellation each own part of the lifecycle | no single owner | `work().inParallel()` / `run.pool()` |
-| Manual scope object with cancellation tokens | Works until every new feature must reimplement the same lifecycle rules | local convention | `group()` / `run.*` |
-| Provider race with `Promise.race()` | Losing calls keep running unless each branch is wired to cancellation | no | `run.race()` |
-| Retry loop with delayed backoff | Cancellation has to be remembered in every sleep and retry branch | no | `run.retry()` |
-| Request fan-out with `Promise.all()` | Sibling cancellation and cleanup are not part of the value contract | no | `group()` / `run.all()` |
-| Manual `try/finally` cleanup | Cleanup can hang or obscure the original failure without an explicit policy | partial | `run.bracket()` |
-| Async iterator prefetch | Producer control and consumer demand are easy to separate accidentally | partial | `work().stream()` |
-| Ad hoc token or cost counters | Nested work can charge the wrong owner without a shared context contract | partial | context budgets |
-| CPU loop with `AbortController` | Cooperative signals cannot preempt the main thread | no | `offload()` |
+## Repository Layout
-WorkIt's ownership contract is the combination of scope, cancellation reason,
-child task set, defer stack, context, and event stream.
+This repository uses a monorepo layout. The published package contract is still
+owned by `packages/core`.
-## Mental Model
-
-WorkIt creates a scope tree. A scope owns its tasks. When a foreground task
-fails, times out, or is cancelled, the scope cancels sibling work, waits for
-owned children, runs cleanup, emits lifecycle events, and then settles.
-
-```mermaid
-flowchart TD
- A[scope created] --> B[foreground tasks start]
- A --> C[background tasks start]
- B --> D{failure, timeout, or cancel}
- D -- no --> E[foreground values settle]
- D -- yes --> F[cancel siblings with typed reason]
- E --> G[await owned background tasks]
- F --> G
- G --> H[run defer stack and bracket cleanup]
- H --> I[scope closes]
- A -. explicit escape .-> J[detached task]
- J -. not awaited by scope .-> K[external owner required]
-```
-
-Rules:
-
-1. Every task runs inside exactly one scope.
-2. A scope owns cancellation, cleanup, context, child tasks, and events.
-3. Cancelling a scope aborts its signal and propagates a typed reason.
-4. A scope cannot close while non-detached children are still pending.
-5. `background` is owned and delays closure.
-6. `detached` is explicit and transfers ownership to the caller.
-
-## Core API
-
-| Need | Use |
+| Path | Purpose |
|---|---|
-| One owned operation with child tasks | `group(async (task) => ...)` |
-| Batch work over items | `work(items)` |
-| Bounded parallel task functions | `run.pool(concurrency, tasks)` |
-| Safer `Promise.all` / `race` / `any` | `run.all()`, `run.race()`, `run.any()` |
-| Retry, timeout, fallback, hedge | `run.retry()`, `run.timeout()`, `run.fallback()`, `run.hedge()` |
-| Resource safety | `run.bracket()` |
-| Critical sections | `run.uncancellable()` |
-| Backpressured streams | `work(items).stream()` |
-| Producer-consumer coordination | `@workit/core/channel` |
-| Worker-thread hard boundary | `@workit/core/worker` |
-| Diagnostics and snapshots | `@workit/core/diagnostics` |
-| OpenTelemetry bridge | `@workit/core/otel` |
-| Agent helper contracts | `@workit/core/ai` |
-
-## Common Use Cases
-
-These are short entry points. The full narrative and benchmark discussion live
-in [`articles/`](articles/).
-
-### Owned Request Fan-Out
-
-```ts
-import { group } from "@workit/core";
-
-const response = await group(async (task) => {
- const profile = task((ctx) => fetchProfile({ signal: ctx.signal }));
- const account = task((ctx) => fetchAccount({ signal: ctx.signal }));
-
- task.background(async (ctx) => {
- ctx.defer(() => flushAuditBuffer());
- await writeAuditEvent({ signal: ctx.signal });
- });
-
- return { profile: await profile, account: await account };
-});
-```
-
-If `profile` fails, the account and audit tasks are cancelled. The audit cleanup
-runs before the scope closes.
+| `packages/core` | Source, tests, samples, evidence, benchmarks, and release scripts for `@workit/core`. |
+| `apps/use-cases-site` | GitHub Pages site with executable WorkIt examples. |
+| `articles` | Public article drafts and released article materials. |
-### Provider Race
+## Package Contract
-```ts
-import { run } from "@workit/core";
-
-const result = await run.race([
- run.timeout((ctx) => primary.generate({ signal: ctx.signal }), "5s"),
- run.timeout((ctx) => backup.generate({ signal: ctx.signal }), "5s"),
-]);
-```
+The monorepo layout must not change how users install or import WorkIt.
-The first success wins. Losing branches receive `CancelReason { kind:
-"race_lost" }`.
+Stable consumer paths for this release line:
-### Retry With Timeout
-
-```ts
-import { run } from "@workit/core";
-
-const receipt = await run.retry(
- (ctx) =>
- run.timeout(
- (timeoutCtx) =>
- chargeCustomer(invoice, {
- signal: AbortSignal.any([ctx.signal, timeoutCtx.signal]),
- }),
- "5s"
- ),
- { retries: 3 }
-);
+```txt
+@workit/core
+@workit/core/ai
+@workit/core/channel
+@workit/core/diagnostics
+@workit/core/observability
+@workit/core/otel
+@workit/core/worker
```
-The retry policy, timeout, and caller cancellation share one owned execution
-path instead of living in separate helper layers.
-
-### Backpressured Stream
-
-```ts
-import { work } from "@workit/core";
+## Verification
-for await (const summary of work(documents())
- .inParallel(8)
- .map((doc, ctx) => summarize(doc, { signal: ctx.signal }))
- .stream()) {
- if (summary.enough) break;
-}
-```
-
-Breaking the loop cancels remaining in-flight work and stops pulling from the
-producer.
-
-### Budgeted Agent Work
-
-```ts
-import { CostBudget, TokenBudget, run, work } from "@workit/core";
-
-await run.context.with(CostBudget, { spent: 0, limit: 0.50, unit: "USD" }, () =>
- run.context.with(TokenBudget, { spent: 0, limit: 100_000, unit: "tokens" }, () =>
- work(chunks).inParallel(8).do(async (chunk, ctx) => {
- ctx.consume(TokenBudget, chunk.estimatedTokens);
- ctx.consume(CostBudget, chunk.estimatedCost);
- return embed(chunk, { signal: ctx.signal });
- })
- )
-);
-```
-
-Budget overrun cancels the scope that installed the budget.
-
-### Worker Boundary
-
-```ts
-import { offload } from "@workit/core/worker";
-
-const result = await offload(
- new URL("./cpu-worker.js", import.meta.url),
- "compute",
- input,
- { timeout: "2s" }
-);
-```
-
-`AbortController` cannot preempt a tight CPU loop on the main thread. Worker
-offload gives CPU-bound or plugin-like work a hard timeout boundary.
-
-## Worker Offload Boundary
-
-`offload()` is an execution boundary and a structured-clone boundary.
-
-Accepted worker inputs include primitives, arrays, plain objects, `Map`, `Set`,
-dates, regexps, buffers, and typed arrays.
-
-Rejected worker inputs include class instances, functions, symbols, custom
-prototype objects, inline or remote module URLs, and parent directory segments
-in worker paths.
-
-When `timeout` fires, WorkIt terminates the worker thread. This is different
-from cooperative `AbortSignal` cancellation inside the main JavaScript thread.
-
-## Runnable Samples
-
-| Sample | What it demonstrates |
-|---|---|
-| [`samples/progress-parallel.sample.js`](samples/progress-parallel.sample.js) | progress events during bounded parallel work |
-| [`samples/race-providers.sample.js`](samples/race-providers.sample.js) | provider race with loser cancellation |
-| [`samples/no-orphan.sample.js`](samples/no-orphan.sample.js) | owned background work waits before scope close |
-| [`samples/streaming-summarizer.sample.js`](samples/streaming-summarizer.sample.js) | streaming summarization with early stop |
-| [`samples/embed-bisection.sample.js`](samples/embed-bisection.sample.js) | bad-batch bisection for embedding pipelines |
-| [`samples/supervision.sample.js`](samples/supervision.sample.js) | supervised long-lived work |
-| [`samples/worker-offload.sample.js`](samples/worker-offload.sample.js) | worker timeout against non-cooperative CPU work |
-| [`samples/budget-rag.sample.js`](samples/budget-rag.sample.js) | request-scoped cost budget |
-| [`samples/logging-otel-bridge.sample.js`](samples/logging-otel-bridge.sample.js) | local events bridged to telemetry |
-
-## Verified Evidence
-
-WorkIt claims are tied to executable gates. The benchmark timings below are
-representative captured runs; the gates assert semantic invariants and budget
-thresholds, not exact milliseconds.
-
-| Evidence | Current result |
-|---|---:|
-| Unit tests | 214 passing |
-| Coverage gate | 100% statements, branches, functions, lines |
-| Runtime dependencies | 0 |
-| Article benchmark suite | 19/19 passing |
-| Core group import | 14,175 B minified / 4,835 B gzip |
-| Public bundle | 29,255 B minified / 9,694 B gzip |
-| Stream gate | 1,000,000 logical items with bounded producer growth |
-| Soak gate | 100,000 logical tasks with bounded concurrency |
-| Exporter stress | 100,000 events with bounded queue |
-
-Representative article-benchmark results:
-
-| Benchmark | Baseline | WorkIt |
-|---|---:|---:|
-| Provider race losers after winner | losers continue until their sleeps finish | losers cancelled in scope close |
-| Retry after cancellation | 7 extra attempts, 622 ms latency | 0 extra attempts, 1 ms latency |
-| Context `.with()` over 5,000 keys | 31.68 ms | 0.014 ms |
-| 1B-row source, take 25 | 281 items pulled | 40 items pulled |
-| Sampling volume | 1,300 events | 36 events |
-
-Run the main verification gate:
+Run the core gates from the repository root:
```sh
npm run verify
+npm run test:coverage
+npm run check:size
+npm run check:package-consumer
```
-`npm run verify` runs type-checking, header and test hygiene, unit tests,
-security checks, vulnerability audit, SBOM validation, API and bundle-size
-locks, runtime benchmarks, stream and soak gates, exporter stress,
-package-consumer fixtures, public-proof validation, worker-contract checks,
-release-policy checks, and `npm pack --dry-run`.
-
-Run the article benchmark suite:
-
-```sh
-npm run bench:articles
-```
-
-Run the curated publication evidence suite:
-
-```sh
-npm run test:evidence
-```
-
-Run verification commands sequentially when they depend on `dist/`. Some gates,
-including `npm run test:coverage` and `npm run verify`, rebuild or clean the
-compiled artifacts. Running them in parallel with `npm run bench:articles` can
-delete `dist/` while benchmark processes are importing it.
-
-Machine-readable reviewer evidence lives in
-[`benchmarks/public-proof.json`](benchmarks/public-proof.json), the article
-benchmark capture lives in
-[`benchmarks/results/articles.latest.json`](benchmarks/results/articles.latest.json),
-and the public claim ledger lives in
-[`evidence/claims.json`](evidence/claims.json).
-
-## Security And Release Integrity
-
-| Guarantee | Enforcement |
-|---|---|
-| Runtime core has no production dependencies | package metadata and security gate |
-| Core does not import networking modules | static no-network gate |
-| Published package includes an SBOM | CycloneDX SBOM generation and validation |
-| Release workflow uses provenance controls | release-policy gate |
-| Public API and bundle size are locked | API and size gates |
-| Consumer fixtures install the package artifact | package-consumer gate |
-
-Additional repository controls include pinned dev dependencies, vulnerability
-audit, SHA-pinned GitHub Actions, OSSF Scorecard workflow, CODEOWNERS,
-Dependabot, and signed release tag policy.
-
-Security reports should follow [`SECURITY.md`](SECURITY.md).
-
-## Runtime Support
-
-Supported:
-
-- Node.js `>=20.11`
-- ESM consumers
-- CommonJS consumers
-- strict TypeScript consumers
-- AWS Lambda-shaped handlers
-- Azure Functions-shaped handlers
-- Next.js route-shaped handlers
-- Express, Fastify, tRPC, and Vercel AI SDK fixtures
-
-Unsupported today:
-
-- browser client runtime
-- Cloudflare Workers
-- Next.js Edge / Vercel Edge
-
-Unsupported runtimes resolve to an explicit unsupported boundary. An edge-safe
-context runtime is future work.
-
-## When To Use Alternatives
-
-| Tool | Prefer it when | Prefer WorkIt when |
-|---|---|---|
-| Native `Promise` | One async value is enough | Work needs ownership, cancellation, cleanup, or diagnostics |
-| Manual scope object | The lifecycle is local and small enough to audit in one file | The lifecycle becomes a reusable cross-module contract |
-| `p-limit` | You only need a tiny semaphore | Bounded work also needs lifecycle semantics |
-| `p-map` | You need a focused concurrent map | Mapping needs retry, timeout, stream policy, or partial results |
-| RxJS | You are modelling rich event streams | You are modelling owned async task lifecycles |
-| Bottleneck | You need distributed reservoirs or rate limits | You need local structured concurrency |
-| Effection | You want structured concurrency via operations/generators | You want plain `async`/`await` task functions |
-| Effect-TS | You want a full effect system | You want owned async work without migrating to a DSL |
-
-These comparisons are about ownership and composition. Some libraries expose
-cancellation hooks or queue controls; WorkIt's claim is that cancellation,
-cleanup, retry, timeout, budgets, backpressure, and diagnostics compose under
-one owner.
-
-## Migration Notes
-
-These are orientation notes, not codemods. Keep the old tool when it owns the
-problem better.
-
-### From p-limit
-
-Use `run.pool()` or `work(items).inParallel(n)` when the semaphore also needs
-sibling cancellation, retry, timeout, cleanup, progress, or partial-result
-policy under one owner.
-
-### From p-map
-
-Use `work(items).inParallel(n).do(fn)` for concurrent maps that need the same
-lifecycle semantics as the caller. Keep `p-map` for a small one-file map where
-concurrency is the only concern.
-
-### From RxJS
-
-Keep RxJS for rich observable graphs. Use WorkIt when the problem is owned task
-lifecycle: request fan-out, provider racing, agent tools, bounded streams, or
-cleanup around async work.
-
-### From Bottleneck
-
-Keep Bottleneck for distributed reservoirs and external rate-limit state. Use
-WorkIt for local process ownership where bounded concurrency must compose with
-cancel, retry, timeout, budgets, and diagnostics.
-
-## Documentation
-
-| Resource | Purpose |
-|---|---|
-| [`articles/`](articles/) | Narrative articles with examples and benchmark discussion |
-| [`benchmarks/articles/`](benchmarks/articles/) | Reproducible article benchmark suite |
-| [`evidence/`](evidence/) | Machine-readable claim ledger and evidence policy |
-| [`tests/evidence/`](tests/evidence/) | Curated publication evidence proofs |
-| [`samples/`](samples/) | Runnable examples against the compiled package |
-| [`SECURITY.md`](SECURITY.md) | Security reporting and release integrity policy |
-
-## Contributing
-
-Please read [`CONTRIBUTING.md`](CONTRIBUTING.md) before opening a pull request.
-
-Before submitting code:
+Run the site gates from the repository root:
```sh
-npm run verify
-npm run test:coverage
-npm run bench:articles
-npm run test:evidence
+npm run site:build
```
-
-Run these commands sequentially. Several verification commands clean and rebuild
-`dist/`, while article benchmarks import the compiled package artifact.
-
-Bug reports should include the WorkIt version, Node.js version, reproduction
-code, and whether the failure occurs from source or the installed package.
-
-## License
-
-Apache-2.0. See [`LICENSE`](LICENSE).
diff --git a/apps/use-cases-site/package-lock.json b/apps/use-cases-site/package-lock.json
index c3b1a1f..d2ecf01 100644
--- a/apps/use-cases-site/package-lock.json
+++ b/apps/use-cases-site/package-lock.json
@@ -9,7 +9,7 @@
"version": "0.1.0",
"license": "Apache-2.0",
"dependencies": {
- "@workit/core": "file:../..",
+ "@workit/core": "file:../../packages/core",
"clsx": "2.1.1",
"lucide-react": "1.17.0",
"react": "19.2.7",
@@ -29,9 +29,9 @@
"@rollup/rollup-win32-x64-msvc": "4.61.0"
}
},
- "../..": {
+ "../../packages/core": {
"name": "@workit/core",
- "version": "0.1.3",
+ "version": "0.1.5",
"license": "Apache-2.0",
"devDependencies": {
"@opentelemetry/api": "1.9.1",
@@ -1617,7 +1617,7 @@
}
},
"node_modules/@workit/core": {
- "resolved": "../..",
+ "resolved": "../../packages/core",
"link": true
},
"node_modules/baseline-browser-mapping": {
diff --git a/apps/use-cases-site/package.json b/apps/use-cases-site/package.json
index 5231b02..1bc94f0 100644
--- a/apps/use-cases-site/package.json
+++ b/apps/use-cases-site/package.json
@@ -18,7 +18,7 @@
"preview": "vite preview"
},
"dependencies": {
- "@workit/core": "file:../..",
+ "@workit/core": "file:../../packages/core",
"clsx": "2.1.1",
"lucide-react": "1.17.0",
"react": "19.2.7",
diff --git a/apps/use-cases-site/scripts/generate-evidence.mjs b/apps/use-cases-site/scripts/generate-evidence.mjs
index 38f7027..2e21334 100644
--- a/apps/use-cases-site/scripts/generate-evidence.mjs
+++ b/apps/use-cases-site/scripts/generate-evidence.mjs
@@ -16,10 +16,10 @@ const repoRoot = resolve(siteRoot, "..", "..");
const outputPath = resolve(siteRoot, "src", "data", "generated", "evidence-snapshots.json");
const samples = [
- { id: "agent-tree-cancel", path: "samples/agent-tree-cancel.sample.js" },
- { id: "conversation-agent", path: "samples/conversation-agent.sample.js" },
- { id: "race-providers", path: "samples/race-providers.sample.js" },
- { id: "budget-rag", path: "samples/budget-rag.sample.js" },
+ { id: "agent-tree-cancel", path: "packages/core/samples/agent-tree-cancel.sample.js" },
+ { id: "conversation-agent", path: "packages/core/samples/conversation-agent.sample.js" },
+ { id: "race-providers", path: "packages/core/samples/race-providers.sample.js" },
+ { id: "budget-rag", path: "packages/core/samples/budget-rag.sample.js" },
];
if (process.platform === "win32") {
diff --git a/apps/use-cases-site/scripts/test-data-contract.mjs b/apps/use-cases-site/scripts/test-data-contract.mjs
index b76161e..c50c861 100644
--- a/apps/use-cases-site/scripts/test-data-contract.mjs
+++ b/apps/use-cases-site/scripts/test-data-contract.mjs
@@ -23,7 +23,7 @@ const exampleContracts = [
{
id: "vibe-coding-agent",
sampleId: "agent-tree-cancel",
- samplePath: "samples/agent-tree-cancel.sample.js",
+ samplePath: "packages/core/samples/agent-tree-cancel.sample.js",
liveReceipt: [
"runtime: @workit/core",
"sample: agent-tree-cancel",
@@ -37,7 +37,7 @@ const exampleContracts = [
{
id: "conversation-agent",
sampleId: "conversation-agent",
- samplePath: "samples/conversation-agent.sample.js",
+ samplePath: "packages/core/samples/conversation-agent.sample.js",
liveReceipt: [
"runtime: @workit/core",
"sample: conversation-agent",
@@ -51,7 +51,7 @@ const exampleContracts = [
{
id: "provider-fallback",
sampleId: "race-providers",
- samplePath: "samples/race-providers.sample.js",
+ samplePath: "packages/core/samples/race-providers.sample.js",
liveReceipt: [
"runtime: @workit/core",
"sample: race-providers",
@@ -63,7 +63,7 @@ const exampleContracts = [
{
id: "rag-pipeline",
sampleId: "budget-rag",
- samplePath: "samples/budget-rag.sample.js",
+ samplePath: "packages/core/samples/budget-rag.sample.js",
liveReceipt: [
"runtime: @workit/core",
"sample: budget-rag",
diff --git a/apps/use-cases-site/server/runners.mjs b/apps/use-cases-site/server/runners.mjs
index 0821872..57c503c 100644
--- a/apps/use-cases-site/server/runners.mjs
+++ b/apps/use-cases-site/server/runners.mjs
@@ -59,7 +59,7 @@ async function runAgentTree() {
`reason.tag: ${reason.tag}`,
`cleanups: ${cleanups.sort().join(", ")}`,
],
- code: await readSample("samples/agent-tree-cancel.sample.js"),
+ code: await readSample("packages/core/samples/agent-tree-cancel.sample.js"),
};
}
@@ -87,7 +87,7 @@ async function runProviderFallback() {
`winner: ${result.provider}`,
`cancelledProviders: ${cancelledProviders.sort().join(", ")}`,
],
- code: await readSample("samples/race-providers.sample.js"),
+ code: await readSample("packages/core/samples/race-providers.sample.js"),
};
}
@@ -153,7 +153,7 @@ async function runRagPipeline() {
`limit: ${finalBudget.limit}`,
`audit.sources: ${audits[0]?.sources ?? 0}`,
],
- code: await readSample("samples/budget-rag.sample.js"),
+ code: await readSample("packages/core/samples/budget-rag.sample.js"),
};
}
@@ -209,7 +209,7 @@ async function runConversationAgent() {
`memoryWrites: ${memoryWrites}`,
`cleanups: ${cleanups.sort().join(", ")}`,
],
- code: await readSample("samples/conversation-agent.sample.js"),
+ code: await readSample("packages/core/samples/conversation-agent.sample.js"),
};
}
diff --git a/apps/use-cases-site/src/data/generated/evidence-snapshots.json b/apps/use-cases-site/src/data/generated/evidence-snapshots.json
index cd30c45..4513cdd 100644
--- a/apps/use-cases-site/src/data/generated/evidence-snapshots.json
+++ b/apps/use-cases-site/src/data/generated/evidence-snapshots.json
@@ -3,7 +3,7 @@
"generatedBy": "apps/use-cases-site/scripts/generate-evidence.mjs",
"samples": {
"agent-tree-cancel": {
- "path": "samples/agent-tree-cancel.sample.js",
+ "path": "packages/core/samples/agent-tree-cancel.sample.js",
"result": {
"sample": "agent-tree-cancel",
"cancelled": [
@@ -24,7 +24,7 @@
"source": "/**\n * Agent tree cancellation sample.\n *\n * @author Admilson B. F. Cossa\n * SPDX-License-Identifier: Apache-2.0\n *\n * Demonstrates parent-scope cancellation across multiple in-flight tool tasks.\n * Each task receives the same typed cancellation reason and runs cleanup.\n */\n\nimport assert from \"node:assert/strict\";\nimport { CancellationError, run } from \"../dist/index.js\";\n\nconst cancelled = [];\nconst cleanups = [];\n\nawait run.scope(async (scope) => {\n const tools = [\"search\", \"browser\", \"code\"];\n const handles = tools.map((name) => scope.spawn(async (ctx) => {\n ctx.defer(() => cleanups.push(name));\n try {\n await sleep(1_000, ctx.signal);\n return name;\n } catch (err) {\n if (err instanceof CancellationError) {\n cancelled.push({ name, reason: err.reason });\n }\n throw err;\n }\n }, { name: `tool.${name}`, kind: \"tool\" }));\n\n await sleep(5, scope.signal);\n scope.cancel({ kind: \"manual\", tag: \"user_stopped_agent\" });\n await Promise.allSettled(handles);\n}, { name: \"agent.tree\" });\n\nassert.deepEqual(cancelled.map((item) => item.name).sort(), [\"browser\", \"code\", \"search\"]);\nassert.deepEqual(cleanups.sort(), [\"browser\", \"code\", \"search\"]);\nassert.ok(cancelled.every((item) => item.reason.tag === \"user_stopped_agent\"));\n\nprocess.stdout.write(`${JSON.stringify({\n sample: \"agent-tree-cancel\",\n cancelled: cancelled.map((item) => item.name).sort(),\n reason: cancelled[0]?.reason,\n cleanups: cleanups.sort(),\n})}\\n`);\n\nfunction sleep(ms, signal) {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\"abort\", () => {\n clearTimeout(timer);\n reject(signal.reason);\n }, { once: true });\n });\n}\n"
},
"conversation-agent": {
- "path": "samples/conversation-agent.sample.js",
+ "path": "packages/core/samples/conversation-agent.sample.js",
"result": {
"sample": "conversation-agent",
"tokens": [
@@ -47,7 +47,7 @@
"source": "/**\n * Conversation agent sample.\n *\n * @author Admilson B. F. Cossa\n * SPDX-License-Identifier: Apache-2.0\n *\n * Runs a chat turn with streaming, tool calls, memory write, and cleanup under\n * one WorkIt scope.\n */\n\nimport assert from \"node:assert/strict\";\nimport { run } from \"../dist/index.js\";\n\nconst tokens = [];\nconst cleanups = [];\nlet memoryWrites = 0;\n\nconst toolResults = await run.scope(async (scope) => {\n const stream = scope.spawn(async (ctx) => {\n ctx.defer(() => cleanups.push(\"stream\"));\n for (const token of [\"plan\", \"search\", \"edit\", \"reply\"]) {\n await sleep(1, ctx.signal);\n tokens.push(token);\n }\n return tokens.length;\n }, { name: \"llm.stream\", kind: \"llm\" });\n\n const search = scope.spawn(async (ctx) => {\n ctx.defer(() => cleanups.push(\"tools\"));\n await sleep(2, ctx.signal);\n return \"search:2\";\n }, { name: \"tool.search\", kind: \"tool\" });\n\n const repo = scope.spawn(async (ctx) => {\n await sleep(3, ctx.signal);\n return \"repo:clean\";\n }, { name: \"tool.repo\", kind: \"tool\" });\n\n const memory = scope.spawn(async (ctx) => {\n ctx.defer(() => cleanups.push(\"memory\"));\n await sleep(4, ctx.signal);\n memoryWrites++;\n return memoryWrites;\n }, { name: \"memory.write\", kind: \"io\" });\n\n const results = await Promise.all([search, repo]);\n await Promise.all([stream, memory]);\n return results;\n}, { name: \"chat.turn\" });\n\nassert.deepEqual(tokens, [\"plan\", \"search\", \"edit\", \"reply\"]);\nassert.deepEqual(toolResults, [\"search:2\", \"repo:clean\"]);\nassert.equal(memoryWrites, 1);\nassert.deepEqual(cleanups.sort(), [\"memory\", \"stream\", \"tools\"]);\n\nprocess.stdout.write(`${JSON.stringify({\n sample: \"conversation-agent\",\n tokens,\n toolResults,\n memoryWrites,\n cleanups: cleanups.sort(),\n})}\\n`);\n\nfunction sleep(ms, signal) {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\"abort\", () => {\n clearTimeout(timer);\n reject(signal.reason);\n }, { once: true });\n });\n}\n"
},
"race-providers": {
- "path": "samples/race-providers.sample.js",
+ "path": "packages/core/samples/race-providers.sample.js",
"result": {
"sample": "race-providers",
"winner": "anthropic",
@@ -59,7 +59,7 @@
"source": "/**\n * Provider race sample.\n *\n * @author Admilson B. F. Cossa\n * SPDX-License-Identifier: Apache-2.0\n *\n * Races three provider calls and cancels the losing requests through the shared\n * task signal.\n */\n\nimport assert from \"node:assert/strict\";\nimport { CancellationError, run } from \"../dist/index.js\";\n\nconst cancelledProviders = [];\n\nconst winner = await run.race([\n provider(\"openai\", 50),\n provider(\"anthropic\", 10),\n provider(\"gemini\", 80),\n]);\n\nassert.equal(winner.provider, \"anthropic\");\nassert.deepEqual(cancelledProviders.sort(), [\"gemini\", \"openai\"]);\n\nprocess.stdout.write(`${JSON.stringify({\n sample: \"race-providers\",\n winner: winner.provider,\n cancelledProviders: cancelledProviders.sort(),\n})}\\n`);\n\nfunction provider(name, latencyMs) {\n return async (ctx) => {\n try {\n await sleep(latencyMs, ctx.signal);\n return { provider: name, text: `${name}:ok` };\n } catch (err) {\n if (err instanceof CancellationError) cancelledProviders.push(name);\n throw err;\n }\n };\n}\n\nfunction sleep(ms, signal) {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(resolve, ms);\n signal.addEventListener(\"abort\", () => {\n clearTimeout(timer);\n reject(signal.reason);\n }, { once: true });\n });\n}\n"
},
"budget-rag": {
- "path": "samples/budget-rag.sample.js",
+ "path": "packages/core/samples/budget-rag.sample.js",
"result": {
"sample": "budget-rag",
"answer": "answer:keyword:structured concurrency",
diff --git a/apps/use-cases-site/src/data/useCases.ts b/apps/use-cases-site/src/data/useCases.ts
index bc3bb20..99799a2 100644
--- a/apps/use-cases-site/src/data/useCases.ts
+++ b/apps/use-cases-site/src/data/useCases.ts
@@ -169,13 +169,13 @@ export const useCases: UseCase[] = [
evidence: [
{
claim: "Parent cancellation reaches in-flight tool work.",
- path: "samples/agent-tree-cancel.sample.js",
+ path: "packages/core/samples/agent-tree-cancel.sample.js",
invariant: "search, browser, and code tasks all cancel with the same reason.",
status: "tracked",
},
{
claim: "Cleanup runs after cancellation.",
- path: "tests/unit/sanity.test.js",
+ path: "packages/core/tests/unit/sanity.test.js",
invariant: "deferred cleanup executes for cancelled scope work.",
status: "tracked",
},
@@ -282,13 +282,13 @@ export const useCases: UseCase[] = [
evidence: [
{
claim: "Streaming work can be scoped with the same owner as tool work.",
- path: "samples/conversation-agent.sample.js",
+ path: "packages/core/samples/conversation-agent.sample.js",
invariant: "stream, tool, memory, and cleanup work stay under one runtime owner.",
status: "tracked",
},
{
claim: "Agent trees expose cancellable child lifecycles.",
- path: "samples/agent-tree-cancel.sample.js",
+ path: "packages/core/samples/agent-tree-cancel.sample.js",
invariant: "in-flight child tasks receive the parent cancellation reason.",
status: "tracked",
},
@@ -379,7 +379,7 @@ export const useCases: UseCase[] = [
evidence: [
{
claim: "Provider race cancels losing requests.",
- path: "samples/race-providers.sample.js",
+ path: "packages/core/samples/race-providers.sample.js",
invariant: "anthropic wins and openai/gemini are cancelled.",
status: "tracked",
},
@@ -486,7 +486,7 @@ export const useCases: UseCase[] = [
evidence: [
{
claim: "RAG budget and background audit work remain attached to the request owner.",
- path: "samples/budget-rag.sample.js",
+ path: "packages/core/samples/budget-rag.sample.js",
invariant: "answer is produced, budget spent is 8, audit record is written.",
status: "tracked",
},
diff --git a/package-lock.json b/package-lock.json
index b8ab358..9f87e42 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,34 +1,16 @@
{
- "name": "@workit/core",
- "version": "0.1.4",
+ "name": "workit",
+ "version": "0.1.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "@workit/core",
- "version": "0.1.4",
+ "name": "workit",
+ "version": "0.1.5",
"license": "Apache-2.0",
- "devDependencies": {
- "@opentelemetry/api": "1.9.1",
- "@types/node": "25.6.1",
- "@vitest/coverage-v8": "4.1.5",
- "esbuild": "0.28.0",
- "fast-check": "4.7.0",
- "typescript": "6.0.3",
- "vitest": "4.1.5",
- "wrangler": "4.89.1"
- },
- "engines": {
- "node": ">=20.11"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.9.1"
- },
- "peerDependenciesMeta": {
- "@opentelemetry/api": {
- "optional": true
- }
- }
+ "workspaces": [
+ "packages/core"
+ ]
},
"node_modules/@babel/helper-string-parser": {
"version": "7.27.1",
@@ -225,40 +207,6 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
- "node_modules/@emnapi/core": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
- "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/wasi-threads": "1.2.1",
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@emnapi/runtime": {
- "version": "1.10.0",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz",
- "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
- "node_modules/@emnapi/wasi-threads": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz",
- "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
- }
- },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.28.0",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
@@ -1784,6 +1732,10 @@
"url": "https://opencollective.com/vitest"
}
},
+ "node_modules/@workit/core": {
+ "resolved": "packages/core",
+ "link": true
+ },
"node_modules/assertion-error": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
@@ -3498,6 +3450,32 @@
"@poppinss/exception": "^1.2.2",
"error-stack-parser-es": "^1.0.5"
}
+ },
+ "packages/core": {
+ "name": "@workit/core",
+ "version": "0.1.5",
+ "license": "Apache-2.0",
+ "devDependencies": {
+ "@opentelemetry/api": "1.9.1",
+ "@types/node": "25.6.1",
+ "@vitest/coverage-v8": "4.1.5",
+ "esbuild": "0.28.0",
+ "fast-check": "4.7.0",
+ "typescript": "6.0.3",
+ "vitest": "4.1.5",
+ "wrangler": "4.89.1"
+ },
+ "engines": {
+ "node": ">=20.11"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.9.1"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ }
+ }
}
}
}
diff --git a/package.json b/package.json
index 978f93f..e23ab24 100644
--- a/package.json
+++ b/package.json
@@ -1,157 +1,73 @@
{
- "name": "@workit/core",
- "version": "0.1.4",
- "description": "Structured concurrency runtime for TypeScript.",
+ "name": "workit",
+ "version": "0.1.5",
+ "private": true,
+ "description": "WorkIt monorepo.",
"type": "module",
- "private": false,
"license": "Apache-2.0",
"author": "Admilson B. F. Cossa",
- "repository": {
- "type": "git",
- "url": "https://github.com/WorkRuntime/workit"
- },
- "bugs": {
- "url": "https://github.com/WorkRuntime/workit/issues"
- },
- "homepage": "https://github.com/WorkRuntime/workit#readme",
- "main": "./dist/index.js",
- "types": "./dist/index.d.ts",
- "exports": {
- ".": {
- "types": "./dist/index.d.ts",
- "node": {
- "import": "./dist/index.js",
- "require": "./dist-cjs/index.cjs"
- },
- "default": "./dist/runtime/unsupported.js"
- },
- "./ai": {
- "types": "./dist/ai/index.d.ts",
- "node": {
- "import": "./dist/ai/index.js",
- "require": "./dist-cjs/ai/index.cjs"
- },
- "default": "./dist/runtime/unsupported.js"
- },
- "./channel": {
- "types": "./dist/channel/index.d.ts",
- "import": "./dist/channel/index.js",
- "require": "./dist-cjs/channel/index.cjs"
- },
- "./observability": {
- "types": "./dist/observability/index.d.ts",
- "import": "./dist/observability/index.js",
- "require": "./dist-cjs/observability/index.cjs"
- },
- "./diagnostics": {
- "types": "./dist/diagnostics/index.d.ts",
- "import": "./dist/diagnostics/index.js",
- "require": "./dist-cjs/diagnostics/index.cjs"
- },
- "./otel": {
- "types": "./dist/otel/index.d.ts",
- "import": "./dist/otel/index.js",
- "require": "./dist-cjs/otel/index.cjs"
- },
- "./worker": {
- "types": "./dist/worker/index.d.ts",
- "node": {
- "import": "./dist/worker/index.js"
- },
- "default": "./dist/runtime/unsupported.js"
- }
- },
- "sideEffects": false,
- "publishConfig": {
- "access": "public"
- },
- "files": [
- "CODE_OF_CONDUCT.md",
- "CONTRIBUTING.md",
- "dist",
- "dist-cjs",
- "SECURITY.md"
+ "workspaces": [
+ "packages/core"
],
"scripts": {
- "clean": "node -e \"const fs=require('node:fs'); for (const p of ['dist','dist-cjs','coverage']) fs.rmSync(p,{recursive:true,force:true});\"",
- "build": "npm run clean && tsc && node scripts/build-cjs.mjs && node scripts/generate-sbom.mjs",
- "typecheck": "tsc --noEmit",
- "check:no-network": "node scripts/check-no-network.mjs",
- "check:headers": "node scripts/check-file-headers.mjs",
- "check:security": "node scripts/check-security.mjs",
- "check:vulnerabilities": "node scripts/check-vulnerabilities.mjs",
- "check:sbom": "npm run build && node scripts/check-sbom.mjs",
- "check:tests": "node scripts/check-tests.mjs",
- "check:api": "npm run build && node scripts/check-api-surface.mjs",
- "check:size": "npm run build && node scripts/check-bundle-size.mjs",
- "report:size": "npm run build && node scripts/report-bundle-size.mjs",
- "check:benchmark": "npm run build && node scripts/check-benchmark.mjs",
- "check:context-performance": "npm run build && node scripts/check-context-performance.mjs",
- "check:1b": "npm run build && node scripts/check-1b-benchmark.mjs",
- "check:leak": "npm run build && node --expose-gc scripts/check-leak.mjs",
- "check:stream-memory": "npm run build && node --expose-gc scripts/check-stream-memory.mjs",
- "check:soak": "npm run build && node --expose-gc scripts/check-soak.mjs",
- "check:exporter-stress": "npm run build && node --expose-gc scripts/check-exporter-stress.mjs",
- "check:package-consumer": "npm run build && node scripts/check-package-consumer.mjs",
- "check:claims": "npm run build && node scripts/check-claim-fixtures.mjs",
- "check:public-proof": "node scripts/check-public-proof.mjs",
- "check:worker-contract": "node scripts/check-worker-contract-docs.mjs",
- "check:release-policy": "npm run build && node scripts/check-release-provenance.mjs",
- "check:release": "npm run build && node scripts/check-release-provenance.mjs --registry-dry-run",
- "bench:articles": "node benchmarks/articles/run-all.mjs",
- "bench:articles:repeated": "node benchmarks/articles/run-repeated.mjs",
+ "clean": "npm --workspace @workit/core run clean",
+ "build": "npm --workspace @workit/core run build",
+ "typecheck": "npm --workspace @workit/core run typecheck",
+ "check:no-network": "npm --workspace @workit/core run check:no-network",
+ "check:headers": "npm --workspace @workit/core run check:headers",
+ "check:security": "npm --workspace @workit/core run check:security",
+ "check:vulnerabilities": "npm --workspace @workit/core run check:vulnerabilities",
+ "check:sbom": "npm --workspace @workit/core run check:sbom",
+ "check:tests": "npm --workspace @workit/core run check:tests",
+ "check:api": "npm --workspace @workit/core run check:api",
+ "check:size": "npm --workspace @workit/core run check:size",
+ "report:size": "npm --workspace @workit/core run report:size",
+ "check:benchmark": "npm --workspace @workit/core run check:benchmark",
+ "check:context-performance": "npm --workspace @workit/core run check:context-performance",
+ "check:1b": "npm --workspace @workit/core run check:1b",
+ "check:leak": "npm --workspace @workit/core run check:leak",
+ "check:stream-memory": "npm --workspace @workit/core run check:stream-memory",
+ "check:soak": "npm --workspace @workit/core run check:soak",
+ "check:exporter-stress": "npm --workspace @workit/core run check:exporter-stress",
+ "check:package-consumer": "npm --workspace @workit/core run check:package-consumer",
+ "check:claims": "npm --workspace @workit/core run check:claims",
+ "check:public-proof": "npm --workspace @workit/core run check:public-proof",
+ "check:worker-contract": "npm --workspace @workit/core run check:worker-contract",
+ "check:release-policy": "npm --workspace @workit/core run check:release-policy",
+ "check:release": "npm --workspace @workit/core run check:release",
+ "bench:articles": "npm --workspace @workit/core run bench:articles",
+ "bench:articles:repeated": "npm --workspace @workit/core run bench:articles:repeated",
"site:dev": "npm --prefix apps/use-cases-site run dev",
"site:build": "npm --prefix apps/use-cases-site run build",
"site:preview": "npm --prefix apps/use-cases-site run preview",
- "test:evidence": "node tests/evidence/run-all.mjs",
- "test:property": "npm run build && vitest run tests/property",
- "pack:dry": "npm pack --dry-run --json",
- "sample:1b": "npm run build && node samples/1b-stream.sample.js",
- "sample:concurrency": "npm run build && node samples/concurrency-budget.sample.js",
- "sample:progress": "npm run build && node samples/progress-parallel.sample.js",
- "sample:cancel": "npm run build && node samples/cancel-reason.sample.js",
- "sample:timeout": "npm run build && node samples/timeout-stop.sample.js",
- "sample:no-orphan": "npm run build && node samples/no-orphan.sample.js",
- "sample:all": "npm run build && node samples/safer-promise-all.sample.js",
- "sample:agent": "npm run build && node samples/agent-tree-cancel.sample.js",
- "sample:race": "npm run build && node samples/race-providers.sample.js",
- "sample:rag": "npm run build && node samples/budget-rag.sample.js",
- "sample:batch": "npm run build && node samples/batch-upload.sample.js",
- "sample:stream": "npm run build && node samples/streaming-summarizer.sample.js",
- "sample:embed100k": "npm run build && node samples/embed-100k.sample.js",
- "sample:bisection": "npm run build && node samples/embed-bisection.sample.js",
- "sample:stt-disconnect": "npm run build && node samples/stt-disconnect.sample.js",
- "sample:supervise": "npm run build && node samples/supervision.sample.js",
- "sample:worker": "npm run build && node samples/worker-offload.sample.js",
- "sample:aws": "npm run build && node samples/aws-lambda-handler.sample.js",
- "sample:azure": "npm run build && node samples/azure-functions-handler.sample.js",
- "sample:next": "npm run build && node samples/next-server-route.sample.js",
- "sample:otel": "npm run build && node samples/otel-adapter.sample.js",
- "sample:logging": "npm run build && node samples/logging-otel-bridge.sample.js",
- "soak:24h": "npm run build && node --expose-gc scripts/soak-24h.mjs",
- "test": "npm run build && vitest run",
- "test:coverage": "npm run build && vitest run --coverage",
- "verify": "npm run typecheck && npm run check:no-network && npm run check:headers && npm run check:tests && npm test && npm run check:security && npm run check:vulnerabilities && npm run check:sbom && npm run check:api && npm run check:size && npm run check:benchmark && npm run check:context-performance && npm run check:1b && npm run check:leak && npm run check:stream-memory && npm run check:soak && npm run check:exporter-stress && npm run check:package-consumer && npm run check:claims && npm run check:public-proof && npm run check:worker-contract && npm run check:release-policy && npm run pack:dry"
- },
- "engines": {
- "node": ">=20.11"
- },
- "peerDependencies": {
- "@opentelemetry/api": "^1.9.1"
- },
- "peerDependenciesMeta": {
- "@opentelemetry/api": {
- "optional": true
- }
- },
- "devDependencies": {
- "@opentelemetry/api": "1.9.1",
- "@types/node": "25.6.1",
- "@vitest/coverage-v8": "4.1.5",
- "esbuild": "0.28.0",
- "fast-check": "4.7.0",
- "typescript": "6.0.3",
- "vitest": "4.1.5",
- "wrangler": "4.89.1"
+ "test:evidence": "npm --workspace @workit/core run test:evidence",
+ "test:property": "npm --workspace @workit/core run test:property",
+ "pack:dry": "npm --workspace @workit/core run pack:dry",
+ "sample:1b": "npm --workspace @workit/core run sample:1b",
+ "sample:concurrency": "npm --workspace @workit/core run sample:concurrency",
+ "sample:progress": "npm --workspace @workit/core run sample:progress",
+ "sample:cancel": "npm --workspace @workit/core run sample:cancel",
+ "sample:timeout": "npm --workspace @workit/core run sample:timeout",
+ "sample:no-orphan": "npm --workspace @workit/core run sample:no-orphan",
+ "sample:all": "npm --workspace @workit/core run sample:all",
+ "sample:agent": "npm --workspace @workit/core run sample:agent",
+ "sample:race": "npm --workspace @workit/core run sample:race",
+ "sample:rag": "npm --workspace @workit/core run sample:rag",
+ "sample:batch": "npm --workspace @workit/core run sample:batch",
+ "sample:stream": "npm --workspace @workit/core run sample:stream",
+ "sample:embed100k": "npm --workspace @workit/core run sample:embed100k",
+ "sample:bisection": "npm --workspace @workit/core run sample:bisection",
+ "sample:stt-disconnect": "npm --workspace @workit/core run sample:stt-disconnect",
+ "sample:supervise": "npm --workspace @workit/core run sample:supervise",
+ "sample:worker": "npm --workspace @workit/core run sample:worker",
+ "sample:aws": "npm --workspace @workit/core run sample:aws",
+ "sample:azure": "npm --workspace @workit/core run sample:azure",
+ "sample:next": "npm --workspace @workit/core run sample:next",
+ "sample:otel": "npm --workspace @workit/core run sample:otel",
+ "sample:logging": "npm --workspace @workit/core run sample:logging",
+ "soak:24h": "npm --workspace @workit/core run soak:24h",
+ "test": "npm --workspace @workit/core run test",
+ "test:coverage": "npm --workspace @workit/core run test:coverage",
+ "verify": "npm --workspace @workit/core run verify"
}
}
diff --git a/packages/core/CODE_OF_CONDUCT.md b/packages/core/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..a75fef8
--- /dev/null
+++ b/packages/core/CODE_OF_CONDUCT.md
@@ -0,0 +1,80 @@
+
+
+# WorkIt Code of Conduct
+
+## Purpose
+
+WorkIt is built for professional, reliable software work. The project expects
+technical disagreement to stay constructive, evidence-based, and respectful.
+
+## Scope
+
+This code of conduct applies to project spaces, including the repository,
+issues, pull requests, discussions, release channels, community chat, events,
+and any other official project forum.
+
+## Expected Behavior
+
+- Be respectful and direct.
+- Discuss ideas, designs, code, and evidence rather than attacking people.
+- Give and receive review feedback with care.
+- Assume good intent where reasonable, and ask for clarification before
+ escalating conflict.
+- Respect maintainers' time, project priorities, and release boundaries.
+- Disclose conflicts of interest when they affect project decisions.
+- Keep private information private.
+
+## Unacceptable Behavior
+
+- Harassment, threats, intimidation, stalking, or sustained disruption.
+- Discriminatory language or conduct.
+- Sexualized language, imagery, or unwanted attention.
+- Public or private sharing of another person's private information without
+ explicit permission.
+- Personal attacks, insults, or repeated bad-faith arguments.
+- Attempts to pressure maintainers into unsafe releases, hidden behavior, or
+ unsupported claims.
+- Spam, coordinated manipulation, or abuse of project infrastructure.
+
+## Reporting
+
+Report concerns through the private contact channel listed by the project
+maintainers. If the project is hosted on GitHub and private vulnerability or
+maintainer contact features are enabled, use those channels when appropriate.
+
+Reports should include:
+
+- the behavior or incident being reported
+- where and when it happened
+- relevant links or screenshots, if safe to share
+- whether immediate action is needed
+
+Maintainers should handle reports with confidentiality, minimize access to
+sensitive details, and avoid public disclosure unless required for safety or
+project integrity.
+
+## Enforcement
+
+Maintainers may take action proportional to the behavior and risk, including:
+
+- a private clarification or warning
+- moderation of comments or discussions
+- temporary or permanent restriction from project spaces
+- rejection or closure of issues, pull requests, or discussions
+- escalation to platform administrators when required
+
+Enforcement decisions should be based on observable behavior, project safety,
+and the need to keep collaboration productive.
+
+## No Retaliation
+
+Retaliation against a person who reports a concern in good faith is not
+acceptable. Maintainers should treat retaliation as a separate conduct issue.
+
+## Maintainer Responsibility
+
+Maintainers are responsible for applying this code consistently and for
+correcting their own behavior when they fall short of it.
diff --git a/packages/core/CONTRIBUTING.md b/packages/core/CONTRIBUTING.md
new file mode 100644
index 0000000..5715117
--- /dev/null
+++ b/packages/core/CONTRIBUTING.md
@@ -0,0 +1,43 @@
+
+# Contributing
+
+WorkIt accepts changes only when they preserve the runtime invariants and pass
+the full verification gate.
+
+## Development Contract
+
+- Keep the core package local-first: no network clients in core.
+- Keep runtime dependencies at zero.
+- Add or update tests before changing behavior.
+- Keep public API changes intentional and reflected in the API-surface gate.
+- Do not commit temporary tests, debug output, generated coverage, or private docs.
+- Do not claim browser, edge, or hosted cloud support unless an executable
+ fixture proves the real runtime path.
+
+## Verification
+
+Run before proposing a release-quality change:
+
+```sh
+npm run test:coverage
+npm run verify
+```
+
+Coverage must remain at 100% for statements, branches, functions, and lines.
+
+## Commit Style
+
+Use small scoped commits. Prefer prefixes such as:
+
+- `runtime:`
+- `observability:`
+- `ai:`
+- `samples:`
+- `tests:`
+- `release:`
+- `security:`
+
+Each commit should contain one coherent change and the tests needed to prove it.
diff --git a/packages/core/LICENSE b/packages/core/LICENSE
new file mode 100644
index 0000000..f450318
--- /dev/null
+++ b/packages/core/LICENSE
@@ -0,0 +1,183 @@
+Apache License
+Version 2.0, January 2004
+http://www.apache.org/licenses/
+
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+"License" shall mean the terms and conditions for use, reproduction, and
+distribution as defined by Sections 1 through 9 of this document.
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct or
+indirect, to cause the direction or management of such entity, whether by
+contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
+outstanding shares, or (iii) beneficial ownership of such entity.
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising
+permissions granted by this License.
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+"Object" form shall mean any form resulting from mechanical transformation or
+translation of a Source form, including but not limited to compiled object code,
+generated documentation, and conversions to other media types.
+
+"Work" shall mean the work of authorship, whether in Source or Object form, made
+available under the License, as indicated by a copyright notice that is included
+in or attached to the work (an example is provided in the Appendix below).
+
+"Derivative Works" shall mean any work, whether in Source or Object form, that
+is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative Works
+shall not include works that remain separable from, or merely link (or bind by
+name) to the interfaces of, the Work and Derivative Works thereof.
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative Works
+thereof, that is intentionally submitted to Licensor for inclusion in the Work by
+the copyright owner or by an individual or Legal Entity authorized to submit on
+behalf of the copyright owner. For the purposes of this definition, "submitted"
+means any form of electronic, verbal, or written communication sent to the
+Licensor or its representatives, including but not limited to communication on
+electronic mailing lists, source code control systems, and issue tracking systems
+that are managed by, or on behalf of, the Licensor for the purpose of discussing
+and improving the Work, but excluding communication that is conspicuously marked
+or otherwise designated in writing by the copyright owner as "Not a
+Contribution."
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently
+incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this
+License, each Contributor hereby grants to You a perpetual, worldwide,
+non-exclusive, no-charge, royalty-free, irrevocable copyright license to
+reproduce, prepare Derivative Works of, publicly display, publicly perform,
+sublicense, and distribute the Work and such Derivative Works in Source or Object
+form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License,
+each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section) patent
+license to make, have made, use, offer to sell, sell, import, and otherwise
+transfer the Work, where such license applies only to those patent claims
+licensable by such Contributor that are necessarily infringed by their
+Contribution(s) alone or by combination of their Contribution(s) with the Work to
+which such Contribution(s) was submitted. If You institute patent litigation
+against any entity (including a cross-claim or counterclaim in a lawsuit)
+alleging that the Work or a Contribution incorporated within the Work
+constitutes direct or contributory patent infringement, then any patent licenses
+granted to You under this License for that Work shall terminate as of the date
+such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or
+Derivative Works thereof in any medium, with or without modifications, and in
+Source or Object form, provided that You meet the following conditions:
+
+(a) You must give any other recipients of the Work or Derivative Works a copy of
+this License; and
+
+(b) You must cause any modified files to carry prominent notices stating that You
+changed the files; and
+
+(c) You must retain, in the Source form of any Derivative Works that You
+distribute, all copyright, patent, trademark, and attribution notices from the
+Source form of the Work, excluding those notices that do not pertain to any part
+of the Derivative Works; and
+
+(d) If the Work includes a "NOTICE" text file as part of its distribution, then
+any Derivative Works that You distribute must include a readable copy of the
+attribution notices contained within such NOTICE file, excluding those notices
+that do not pertain to any part of the Derivative Works, in at least one of the
+following places: within a NOTICE text file distributed as part of the Derivative
+Works; within the Source form or documentation, if provided along with the
+Derivative Works; or, within a display generated by the Derivative Works, if and
+wherever such third-party notices normally appear.
+
+The contents of the NOTICE file are for informational purposes only and do not
+modify the License. You may add Your own attribution notices within Derivative
+Works that You distribute, alongside or as an addendum to the NOTICE text from
+the Work, provided that such additional attribution notices cannot be construed
+as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction, or
+distribution of Your modifications, or for any such Derivative Works as a whole,
+provided Your use, reproduction, and distribution of the Work otherwise complies
+with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any
+Contribution intentionally submitted for inclusion in the Work by You to the
+Licensor shall be under the terms and conditions of this License, without any
+additional terms or conditions. Notwithstanding the above, nothing herein shall
+supersede or modify the terms of any separate license agreement you may have
+executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names,
+trademarks, service marks, or product names of the Licensor, except as required
+for reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
+writing, Licensor provides the Work (and each Contributor provides its
+Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+either express or implied, including, without limitation, any warranties or
+conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+PARTICULAR PURPOSE. You are solely responsible for determining the
+appropriateness of using or redistributing the Work and assume any risks
+associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether in
+tort (including negligence), contract, or otherwise, unless required by
+applicable law (such as deliberate and grossly negligent acts) or agreed to in
+writing, shall any Contributor be liable to You for damages, including any
+direct, indirect, special, incidental, or consequential damages of any character
+arising as a result of this License or out of the use or inability to use the
+Work (including but not limited to damages for loss of goodwill, work stoppage,
+computer failure or malfunction, or any and all other commercial damages or
+losses), even if such Contributor has been advised of the possibility of such
+damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work or
+Derivative Works thereof, You may choose to offer, and charge a fee for,
+acceptance of support, warranty, indemnity, or other liability obligations and/or
+rights consistent with this License. However, in accepting such obligations, You
+may act only on Your own behalf and on Your sole responsibility, not on behalf of
+any other Contributor, and only if You agree to indemnify, defend, and hold each
+Contributor harmless for any liability incurred by, or claims asserted against,
+such Contributor by reason of your accepting any such warranty or additional
+liability.
+
+END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own
+identifying information. (Don't include the brackets!) The text should be
+enclosed in the appropriate comment syntax for the file format. We also
+recommend that a file or class name and description of purpose be included on
+the same "printed page" as the copyright notice for easier identification within
+third-party archives.
+
+Copyright 2026 Admilson B. F. Cossa
+
+Licensed under the Apache License, Version 2.0 (the "License"); you may not use
+this file except in compliance with the License. You may obtain a copy of the
+License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software distributed
+under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+CONDITIONS OF ANY KIND, either express or implied. See the License for the
+specific language governing permissions and limitations under the License.
diff --git a/packages/core/README.md b/packages/core/README.md
new file mode 100644
index 0000000..f93f089
--- /dev/null
+++ b/packages/core/README.md
@@ -0,0 +1,588 @@
+
+
+# WorkIt
+
+[](LICENSE)
+[](package.json)
+[](package.json)
+[](#verified-evidence)
+[](benchmarks/articles/)
+[](https://www.bestpractices.dev/projects/12807)
+
+Structured concurrency for TypeScript systems that need owned async work:
+bounded parallelism, cancellation, cleanup, retries, timeouts, budgets,
+backpressure, worker offload, and observable task lifecycles.
+
+Native `Promise` is still the right tool for one asynchronous value. WorkIt is
+for the next step: a request, batch, agent run, provider race, stream, or
+background operation where related async tasks must live, fail, cancel, and
+clean up together.
+
+## Install
+
+```sh
+npm install @workit/core
+```
+
+WorkIt targets Node.js server runtimes today. Browser and edge runtimes resolve
+to an explicit unsupported-runtime boundary.
+
+Live examples:
+npm package:
+Changelog:
+
+## Quick Start
+
+```ts
+import { work } from "@workit/core";
+
+const doubled = await work([1, 2, 3])
+ .inParallel(2)
+ .do(async (value, _ctx) => value * 2);
+```
+
+The context parameter is available when the task needs cancellation, progress,
+budgets, or scoped resources. It can be ignored for plain transformations.
+
+## Why Ownership Matters
+
+Consider this batch helper:
+
+```ts
+type BatchEvent =
+ | { type: "item:started"; item: T; attempt: number }
+ | { type: "item:retried"; item: T; attempt: number; error: unknown }
+ | { type: "item:completed"; item: T };
+
+type BatchOptions = {
+ concurrency: number;
+ retries: number;
+ timeoutMs: number;
+ signal: AbortSignal;
+ events: { emit(event: BatchEvent): void };
+ run: (item: T, options: { signal: AbortSignal }) => Promise;
+};
+
+function backoffMs(attempt: number): number {
+ return Math.min(1000 * 2 ** (attempt - 1), 10_000);
+}
+
+function sleep(ms: number, signal?: AbortSignal): Promise {
+ if (signal?.aborted) return Promise.reject(signal.reason);
+
+ return new Promise((resolve, reject) => {
+ let settled = false;
+
+ const finish = (): void => {
+ if (settled) return;
+ settled = true;
+ signal?.removeEventListener("abort", abort);
+ resolve();
+ };
+
+ const abort = (): void => {
+ if (settled) return;
+ settled = true;
+ clearTimeout(timer);
+ reject(signal?.reason);
+ };
+
+ const timer = setTimeout(finish, ms);
+ signal?.addEventListener("abort", abort, { once: true });
+ });
+}
+
+async function runBatch(
+ items: readonly T[],
+ options: BatchOptions
+): Promise {
+ const results = new Array(items.length);
+ let nextIndex = 0;
+
+ async function worker(): Promise {
+ while (!options.signal.aborted) {
+ const index = nextIndex++;
+ if (index >= items.length) return;
+
+ const item = items[index];
+ for (let attempt = 1; attempt <= options.retries + 1; attempt++) {
+ const timeout = AbortSignal.timeout(options.timeoutMs);
+ const signal = AbortSignal.any([options.signal, timeout]);
+
+ try {
+ options.events.emit({ type: "item:started", item, attempt });
+ results[index] = await options.run(item, { signal });
+ options.events.emit({ type: "item:completed", item });
+ break;
+ } catch (error) {
+ if (attempt > options.retries || options.signal.aborted) throw error;
+ options.events.emit({ type: "item:retried", item, attempt, error });
+ await sleep(backoffMs(attempt), options.signal);
+ }
+ }
+ }
+ }
+
+ await Promise.all(
+ Array.from({ length: options.concurrency }, () => worker())
+ );
+
+ return results;
+}
+```
+
+It covers bounded parallelism, timeout, parent cancellation, ordered results,
+typed events, and retry backoff. The lifecycle is still split across the queue,
+timeout signals, retry loop, event sink, and caller. Adding sibling
+cancellation, cleanup, budgets, partial results, or diagnostics extends the
+same ownership protocol in several places.
+
+With WorkIt, the ownership boundary is the API:
+
+```ts
+import { work } from "@workit/core";
+
+const results = await work(items)
+ .inParallel(8)
+ .withRetry(3)
+ .withTimeout("5s")
+ .do(async (item, ctx) => {
+ ctx.report({ message: `processing ${item.id}` });
+ return apiCall(item, { signal: ctx.signal });
+ });
+```
+
+This gives the batch one lifecycle contract:
+
+- at most 8 items run at once
+- transient failures retry with cancel-aware backoff
+- each item has a 5 second timeout
+- every task receives the same cancellation model through `ctx.signal`
+- progress is a typed runtime event
+- queued and active work stop together when the owner is cancelled
+
+## What WorkIt Replaces
+
+WorkIt does not replace promises as values. It replaces repeated lifecycle
+orchestration around promises.
+
+| Existing pattern | Real limitation | Ownership contract | WorkIt primitive |
+|---|---|---:|---|
+| Hand-written concurrency queue | Queue, retry, timeout, and caller cancellation each own part of the lifecycle | no single owner | `work().inParallel()` / `run.pool()` |
+| Manual scope object with cancellation tokens | Works until every new feature must reimplement the same lifecycle rules | local convention | `group()` / `run.*` |
+| Provider race with `Promise.race()` | Losing calls keep running unless each branch is wired to cancellation | no | `run.race()` |
+| Retry loop with delayed backoff | Cancellation has to be remembered in every sleep and retry branch | no | `run.retry()` |
+| Request fan-out with `Promise.all()` | Sibling cancellation and cleanup are not part of the value contract | no | `group()` / `run.all()` |
+| Manual `try/finally` cleanup | Cleanup can hang or obscure the original failure without an explicit policy | partial | `run.bracket()` |
+| Async iterator prefetch | Producer control and consumer demand are easy to separate accidentally | partial | `work().stream()` |
+| Ad hoc token or cost counters | Nested work can charge the wrong owner without a shared context contract | partial | context budgets |
+| CPU loop with `AbortController` | Cooperative signals cannot preempt the main thread | no | `offload()` |
+
+WorkIt's ownership contract is the combination of scope, cancellation reason,
+child task set, defer stack, context, and event stream.
+
+## Mental Model
+
+WorkIt creates a scope tree. A scope owns its tasks. When a foreground task
+fails, times out, or is cancelled, the scope cancels sibling work, waits for
+owned children, runs cleanup, emits lifecycle events, and then settles.
+
+```mermaid
+flowchart TD
+ A[scope created] --> B[foreground tasks start]
+ A --> C[background tasks start]
+ B --> D{failure, timeout, or cancel}
+ D -- no --> E[foreground values settle]
+ D -- yes --> F[cancel siblings with typed reason]
+ E --> G[await owned background tasks]
+ F --> G
+ G --> H[run defer stack and bracket cleanup]
+ H --> I[scope closes]
+ A -. explicit escape .-> J[detached task]
+ J -. not awaited by scope .-> K[external owner required]
+```
+
+Rules:
+
+1. Every task runs inside exactly one scope.
+2. A scope owns cancellation, cleanup, context, child tasks, and events.
+3. Cancelling a scope aborts its signal and propagates a typed reason.
+4. A scope cannot close while non-detached children are still pending.
+5. `background` is owned and delays closure.
+6. `detached` is explicit and transfers ownership to the caller.
+
+## Core API
+
+| Need | Use |
+|---|---|
+| One owned operation with child tasks | `group(async (task) => ...)` |
+| Batch work over items | `work(items)` |
+| Bounded parallel task functions | `run.pool(concurrency, tasks)` |
+| Safer `Promise.all` / `race` / `any` | `run.all()`, `run.race()`, `run.any()` |
+| Retry, timeout, fallback, hedge | `run.retry()`, `run.timeout()`, `run.fallback()`, `run.hedge()` |
+| Resource safety | `run.bracket()` |
+| Critical sections | `run.uncancellable()` |
+| Backpressured streams | `work(items).stream()` |
+| Producer-consumer coordination | `@workit/core/channel` |
+| Worker-thread hard boundary | `@workit/core/worker` |
+| Diagnostics and snapshots | `@workit/core/diagnostics` |
+| OpenTelemetry bridge | `@workit/core/otel` |
+| Agent helper contracts | `@workit/core/ai` |
+
+## Common Use Cases
+
+These are short entry points. The full narrative and benchmark discussion live
+in the repository [`articles/`](https://github.com/WorkRuntime/workit/tree/main/articles).
+
+### Owned Request Fan-Out
+
+```ts
+import { group } from "@workit/core";
+
+const response = await group(async (task) => {
+ const profile = task((ctx) => fetchProfile({ signal: ctx.signal }));
+ const account = task((ctx) => fetchAccount({ signal: ctx.signal }));
+
+ task.background(async (ctx) => {
+ ctx.defer(() => flushAuditBuffer());
+ await writeAuditEvent({ signal: ctx.signal });
+ });
+
+ return { profile: await profile, account: await account };
+});
+```
+
+If `profile` fails, the account and audit tasks are cancelled. The audit cleanup
+runs before the scope closes.
+
+### Provider Race
+
+```ts
+import { run } from "@workit/core";
+
+const result = await run.race([
+ run.timeout((ctx) => primary.generate({ signal: ctx.signal }), "5s"),
+ run.timeout((ctx) => backup.generate({ signal: ctx.signal }), "5s"),
+]);
+```
+
+The first success wins. Losing branches receive `CancelReason { kind:
+"race_lost" }`.
+
+### Retry With Timeout
+
+```ts
+import { run } from "@workit/core";
+
+const receipt = await run.retry(
+ (ctx) =>
+ run.timeout(
+ (timeoutCtx) =>
+ chargeCustomer(invoice, {
+ signal: AbortSignal.any([ctx.signal, timeoutCtx.signal]),
+ }),
+ "5s"
+ ),
+ { retries: 3 }
+);
+```
+
+The retry policy, timeout, and caller cancellation share one owned execution
+path instead of living in separate helper layers.
+
+### Backpressured Stream
+
+```ts
+import { work } from "@workit/core";
+
+for await (const summary of work(documents())
+ .inParallel(8)
+ .map((doc, ctx) => summarize(doc, { signal: ctx.signal }))
+ .stream()) {
+ if (summary.enough) break;
+}
+```
+
+Breaking the loop cancels remaining in-flight work and stops pulling from the
+producer.
+
+### Budgeted Agent Work
+
+```ts
+import { CostBudget, TokenBudget, run, work } from "@workit/core";
+
+await run.context.with(CostBudget, { spent: 0, limit: 0.50, unit: "USD" }, () =>
+ run.context.with(TokenBudget, { spent: 0, limit: 100_000, unit: "tokens" }, () =>
+ work(chunks).inParallel(8).do(async (chunk, ctx) => {
+ ctx.consume(TokenBudget, chunk.estimatedTokens);
+ ctx.consume(CostBudget, chunk.estimatedCost);
+ return embed(chunk, { signal: ctx.signal });
+ })
+ )
+);
+```
+
+Budget overrun cancels the scope that installed the budget.
+
+### Worker Boundary
+
+```ts
+import { offload } from "@workit/core/worker";
+
+const result = await offload(
+ new URL("./cpu-worker.js", import.meta.url),
+ "compute",
+ input,
+ { timeout: "2s" }
+);
+```
+
+`AbortController` cannot preempt a tight CPU loop on the main thread. Worker
+offload gives CPU-bound or plugin-like work a hard timeout boundary.
+
+## Worker Offload Boundary
+
+`offload()` is an execution boundary and a structured-clone boundary.
+
+Accepted worker inputs include primitives, arrays, plain objects, `Map`, `Set`,
+dates, regexps, buffers, and typed arrays.
+
+Rejected worker inputs include class instances, functions, symbols, custom
+prototype objects, inline or remote module URLs, and parent directory segments
+in worker paths.
+
+When `timeout` fires, WorkIt terminates the worker thread. This is different
+from cooperative `AbortSignal` cancellation inside the main JavaScript thread.
+
+## Runnable Samples
+
+| Sample | What it demonstrates |
+|---|---|
+| [`samples/progress-parallel.sample.js`](samples/progress-parallel.sample.js) | progress events during bounded parallel work |
+| [`samples/race-providers.sample.js`](samples/race-providers.sample.js) | provider race with loser cancellation |
+| [`samples/no-orphan.sample.js`](samples/no-orphan.sample.js) | owned background work waits before scope close |
+| [`samples/streaming-summarizer.sample.js`](samples/streaming-summarizer.sample.js) | streaming summarization with early stop |
+| [`samples/embed-bisection.sample.js`](samples/embed-bisection.sample.js) | bad-batch bisection for embedding pipelines |
+| [`samples/supervision.sample.js`](samples/supervision.sample.js) | supervised long-lived work |
+| [`samples/worker-offload.sample.js`](samples/worker-offload.sample.js) | worker timeout against non-cooperative CPU work |
+| [`samples/budget-rag.sample.js`](samples/budget-rag.sample.js) | request-scoped cost budget |
+| [`samples/logging-otel-bridge.sample.js`](samples/logging-otel-bridge.sample.js) | local events bridged to telemetry |
+
+## Verified Evidence
+
+WorkIt claims are tied to executable gates. The benchmark timings below are
+representative captured runs; the gates assert semantic invariants and budget
+thresholds, not exact milliseconds.
+
+| Evidence | Current result |
+|---|---:|
+| Unit tests | 221 passing |
+| Coverage gate | 100% statements, branches, functions, lines |
+| Runtime dependencies | 0 |
+| Article benchmark suite | 19/19 passing |
+| Core group import | 14,175 B minified / 4,835 B gzip |
+| Public bundle | 29,255 B minified / 9,694 B gzip |
+| Stream gate | 1,000,000 logical items with bounded producer growth |
+| Soak gate | 100,000 logical tasks with bounded concurrency |
+| Exporter stress | 100,000 events with bounded queue |
+
+Representative article-benchmark results:
+
+| Benchmark | Baseline | WorkIt |
+|---|---:|---:|
+| Provider race losers after winner | losers continue until their sleeps finish | losers cancelled in scope close |
+| Retry after cancellation | 7 extra attempts, 622 ms latency | 0 extra attempts, 1 ms latency |
+| Context `.with()` over 5,000 keys | 31.68 ms | 0.014 ms |
+| 1B-row source, take 25 | 281 items pulled | 40 items pulled |
+| Sampling volume | 1,300 events | 36 events |
+
+Run the main verification gate:
+
+```sh
+npm run verify
+```
+
+`npm run verify` runs type-checking, header and test hygiene, unit tests,
+security checks, vulnerability audit, SBOM validation, API and bundle-size
+locks, runtime benchmarks, stream and soak gates, exporter stress,
+package-consumer fixtures, public-proof validation, worker-contract checks,
+release-policy checks, and `npm pack --dry-run`.
+
+Run the article benchmark suite:
+
+```sh
+npm run bench:articles
+```
+
+Run the curated publication evidence suite:
+
+```sh
+npm run test:evidence
+```
+
+Run verification commands sequentially when they depend on `dist/`. Some gates,
+including `npm run test:coverage` and `npm run verify`, rebuild or clean the
+compiled artifacts. Running them in parallel with `npm run bench:articles` can
+delete `dist/` while benchmark processes are importing it.
+
+Machine-readable reviewer evidence lives in
+[`benchmarks/public-proof.json`](benchmarks/public-proof.json), the article
+benchmark capture lives in
+[`benchmarks/results/articles.latest.json`](benchmarks/results/articles.latest.json),
+and the public claim ledger lives in
+[`evidence/claims.json`](evidence/claims.json).
+
+## Security And Release Integrity
+
+| Guarantee | Enforcement |
+|---|---|
+| Runtime core has no production dependencies | package metadata and security gate |
+| Core does not import networking modules | static no-network gate |
+| Published package includes an SBOM | CycloneDX SBOM generation and validation |
+| Release workflow uses provenance controls | release-policy gate |
+| Public API and bundle size are locked | API and size gates |
+| Consumer fixtures install the package artifact | package-consumer gate |
+
+Additional repository controls include pinned dev dependencies, vulnerability
+audit, SHA-pinned GitHub Actions, OSSF Scorecard workflow, CODEOWNERS,
+Dependabot, and signed release tag policy.
+
+Security reports should follow [`SECURITY.md`](SECURITY.md).
+
+## Runtime Support
+
+Supported:
+
+- Node.js `>=20.11`
+- ESM consumers
+- CommonJS consumers
+- strict TypeScript consumers
+- AWS Lambda-shaped handlers
+- Azure Functions-shaped handlers
+- Next.js route-shaped handlers
+- Express, Fastify, tRPC, and Vercel AI SDK fixtures
+
+Unsupported today:
+
+- browser client runtime
+- Cloudflare Workers
+- Next.js Edge / Vercel Edge
+
+Unsupported runtimes resolve to an explicit unsupported boundary. An edge-safe
+context runtime is future work.
+
+## When To Use Alternatives
+
+| Tool | Prefer it when | Prefer WorkIt when |
+|---|---|---|
+| Native `Promise` | One async value is enough | Work needs ownership, cancellation, cleanup, or diagnostics |
+| Manual scope object | The lifecycle is local and small enough to audit in one file | The lifecycle becomes a reusable cross-module contract |
+| `p-limit` | You only need a tiny semaphore | Bounded work also needs lifecycle semantics |
+| `p-map` | You need a focused concurrent map | Mapping needs retry, timeout, stream policy, or partial results |
+| RxJS | You are modelling rich event streams | You are modelling owned async task lifecycles |
+| Bottleneck | You need distributed reservoirs or rate limits | You need local structured concurrency |
+| Effection | You want structured concurrency via operations/generators | You want plain `async`/`await` task functions |
+| Effect-TS | You want a full effect system | You want owned async work without migrating to a DSL |
+
+These comparisons are about ownership and composition. Some libraries expose
+cancellation hooks or queue controls; WorkIt's claim is that cancellation,
+cleanup, retry, timeout, budgets, backpressure, and diagnostics compose under
+one owner.
+
+## Migration Notes
+
+These are orientation notes, not codemods. Keep the old tool when it owns the
+problem better.
+
+### From p-limit
+
+Use `run.pool()` or `work(items).inParallel(n)` when the semaphore also needs
+sibling cancellation, retry, timeout, cleanup, progress, or partial-result
+policy under one owner.
+
+### From p-map
+
+Use `work(items).inParallel(n).do(fn)` for concurrent maps that need the same
+lifecycle semantics as the caller. Keep `p-map` for a small one-file map where
+concurrency is the only concern.
+
+### From RxJS
+
+Keep RxJS for rich observable graphs. Use WorkIt when the problem is owned task
+lifecycle: request fan-out, provider racing, agent tools, bounded streams, or
+cleanup around async work.
+
+### From Bottleneck
+
+Keep Bottleneck for distributed reservoirs and external rate-limit state. Use
+WorkIt for local process ownership where bounded concurrency must compose with
+cancel, retry, timeout, budgets, and diagnostics.
+
+## Documentation
+
+| Resource | Purpose |
+|---|---|
+| [`articles/`](https://github.com/WorkRuntime/workit/tree/main/articles) | Narrative articles with examples and benchmark discussion |
+| [`benchmarks/articles/`](benchmarks/articles/) | Reproducible article benchmark suite |
+| [`evidence/`](evidence/) | Machine-readable claim ledger and evidence policy |
+| [`tests/evidence/`](tests/evidence/) | Curated publication evidence proofs |
+| [`samples/`](samples/) | Runnable examples against the compiled package |
+| [`SECURITY.md`](SECURITY.md) | Security reporting and release integrity policy |
+
+## Versioning
+
+WorkIt follows semver with a stricter release discipline:
+
+- Patch releases, such as `0.1.x`, are for fixes, build/release hardening,
+ layout migrations, documentation, and evidence updates. They must not add new
+ public runtime APIs.
+- Minor releases, such as `0.2.0`, may add new subpaths or feature families when
+ they are backed by tests, evidence, package-consumer checks, and
+ documentation.
+- The root `@workit/core` import remains size-disciplined. New heavier
+ capabilities should live in subpaths or companion packages.
+- `1.0.0` will mark a frozen public API and long-term compatibility policy, not
+ a shortcut for credibility. Current `0.x` releases are validated and usable,
+ with changes managed through semver and release notes.
+
+## Citation
+
+If you use WorkIt in research, benchmarks, or reproducible artifacts, please
+cite the software release you used:
+
+```bibtex
+@software{workit2026,
+ author = {Admilson B. F. Cossa},
+ title = {WorkIt: A TypeScript Structured Concurrency Runtime for Node.js Server Runtimes},
+ year = {2026},
+ url = {https://github.com/WorkRuntime/workit},
+ version = {0.1.5},
+ license = {Apache-2.0}
+}
+```
+
+## Contributing
+
+Please read [`CONTRIBUTING.md`](CONTRIBUTING.md) before opening a pull request.
+
+Before submitting code:
+
+```sh
+npm run verify
+npm run test:coverage
+npm run bench:articles
+npm run test:evidence
+```
+
+Run these commands sequentially. Several verification commands clean and rebuild
+`dist/`, while article benchmarks import the compiled package artifact.
+
+Bug reports should include the WorkIt version, Node.js version, reproduction
+code, and whether the failure occurs from source or the installed package.
+
+## License
+
+Apache-2.0. See [`LICENSE`](LICENSE).
diff --git a/packages/core/SECURITY.md b/packages/core/SECURITY.md
new file mode 100644
index 0000000..7c796bf
--- /dev/null
+++ b/packages/core/SECURITY.md
@@ -0,0 +1,130 @@
+
+# Security Policy
+
+## Supported Versions
+
+WorkIt is pre-release software. Security fixes apply to the current `0.x`
+development line until a stable support policy is published.
+
+## Reporting A Vulnerability
+
+Do not open a public issue for suspected vulnerabilities.
+
+Security contact: admilsoncossa@gmail.com
+
+PGP encryption: request the maintainer's current public key through the security
+contact before sending secrets, exploit details that include credentials, or
+tenant-sensitive material. Do not attach secrets to an unencrypted first report.
+
+Send a private report to the project maintainer with:
+
+- affected version or commit
+- operating system and Node.js version
+- minimal reproduction
+- impact assessment
+- whether secrets, tenant data, billing controls, or cancellation guarantees are affected
+
+The maintainer should acknowledge valid reports within 72 hours and publish a
+fix, mitigation, or status update as soon as practical.
+
+## Security Boundary
+
+WorkIt is a local structured-concurrency runtime. It does not authenticate
+users, authorize actions, encrypt payloads, or provide a durable workflow
+ledger. Applications remain responsible for tenant isolation, provider
+credentials, authorization, persistence, and external network policy.
+
+The core package must keep these guarantees:
+
+- zero runtime dependencies
+- no core networking imports or remote telemetry clients
+- bounded exporter queues for opt-in telemetry bridges
+- caller-owned telemetry sanitizers before events leave the process
+- no skipped or focused tests in release verification
+- 100% statement, branch, function, and line coverage
+- CycloneDX SBOM generation and validation
+- production dependency vulnerability audit
+- package dry-run inspection before publication
+
+## Release Provenance
+
+Public releases must be built from a clean worktree and published with npm
+provenance enabled. A release is not approved unless these commands pass:
+
+```sh
+npm run verify
+npm run test:coverage
+npm run check:vulnerabilities
+npm run check:sbom
+npm pack --dry-run --json
+```
+
+The provenance workflow is defined in `.github/workflows/release-provenance.yml`.
+Registry dry-runs and real publication must be triggered only from a signed
+release tag after the scoped release commit is clean and verified. The publish
+step runs:
+
+```sh
+npm publish --provenance --access public --dry-run
+```
+
+for dry runs, and:
+
+```sh
+npm publish --provenance --access public
+```
+
+for an approved release. The package must not publish source maps, local docs,
+tests, secrets, temporary files, debug output, or private agent instructions.
+
+Release tags must be signed. The release operator must create the version tag
+only after the scoped release commit is clean and verified:
+
+```sh
+git tag -s vX.Y.Z -m "Release vX.Y.Z"
+git tag -v vX.Y.Z
+```
+
+Unsigned release tags are not valid release evidence.
+
+## OpenSSF Best Practices
+
+The OpenSSF Best Practices badge is tracked as a public supply-chain hygiene
+process, not as a marketing badge. The project must not claim a passing badge
+until the external OpenSSF checklist is completed and the project entry exists.
+
+The process is documented in `OPENSSF-BEST-PRACTICES.md`.
+
+## Responsible Disclosure Scope
+
+Reports are in scope when they affect:
+
+- cancellation integrity
+- no-orphan guarantees
+- budget accounting or cost-cap bypass
+- context isolation across requests
+- telemetry exporter isolation
+- package contents or supply-chain integrity
+- worker-thread offload boundaries
+
+Worker-thread offload is an explicit local execution boundary. `offload()`
+accepts only local file URLs or paths controlled by the application; inline and remote module URLs
+are rejected before import, and parent directory traversal is
+rejected before the worker starts. Worker input must be plain structured-clone data.
+Primitives, arrays, plain objects, `Map`, `Set`, dates, regexps, buffers,
+and typed arrays are accepted. Functions, symbols, class instances, and objects
+with custom prototypes are rejected before worker startup. When `offload()` is
+given a timeout, WorkIt terminates the worker thread on timeout so
+non-cooperative worker code cannot keep running in-process. In-process helpers
+such as `run.uncancellable()` remain cooperative shields; JavaScript code that
+ignores abort signals cannot be forcibly stopped without a worker/process
+boundary.
+
+Out of scope:
+
+- vulnerabilities in downstream application code
+- denial-of-service claims that require intentionally unbounded user code inside a task
+- unsupported browser, edge, or Cloudflare Worker execution
diff --git a/benchmarks/articles/01-run-all-vs-promise-all.mjs b/packages/core/benchmarks/articles/01-run-all-vs-promise-all.mjs
similarity index 100%
rename from benchmarks/articles/01-run-all-vs-promise-all.mjs
rename to packages/core/benchmarks/articles/01-run-all-vs-promise-all.mjs
diff --git a/benchmarks/articles/02-run-race-vs-promise-race.mjs b/packages/core/benchmarks/articles/02-run-race-vs-promise-race.mjs
similarity index 100%
rename from benchmarks/articles/02-run-race-vs-promise-race.mjs
rename to packages/core/benchmarks/articles/02-run-race-vs-promise-race.mjs
diff --git a/benchmarks/articles/03-run-any-vs-promise-any.mjs b/packages/core/benchmarks/articles/03-run-any-vs-promise-any.mjs
similarity index 100%
rename from benchmarks/articles/03-run-any-vs-promise-any.mjs
rename to packages/core/benchmarks/articles/03-run-any-vs-promise-any.mjs
diff --git a/benchmarks/articles/04-pool-vs-semaphore.mjs b/packages/core/benchmarks/articles/04-pool-vs-semaphore.mjs
similarity index 100%
rename from benchmarks/articles/04-pool-vs-semaphore.mjs
rename to packages/core/benchmarks/articles/04-pool-vs-semaphore.mjs
diff --git a/benchmarks/articles/05-retry-on-cancel.mjs b/packages/core/benchmarks/articles/05-retry-on-cancel.mjs
similarity index 100%
rename from benchmarks/articles/05-retry-on-cancel.mjs
rename to packages/core/benchmarks/articles/05-retry-on-cancel.mjs
diff --git a/benchmarks/articles/06-hedge-tied-requests.mjs b/packages/core/benchmarks/articles/06-hedge-tied-requests.mjs
similarity index 100%
rename from benchmarks/articles/06-hedge-tied-requests.mjs
rename to packages/core/benchmarks/articles/06-hedge-tied-requests.mjs
diff --git a/benchmarks/articles/07-worker-hard-kill.mjs b/packages/core/benchmarks/articles/07-worker-hard-kill.mjs
similarity index 100%
rename from benchmarks/articles/07-worker-hard-kill.mjs
rename to packages/core/benchmarks/articles/07-worker-hard-kill.mjs
diff --git a/benchmarks/articles/08-uncancellable-shield.mjs b/packages/core/benchmarks/articles/08-uncancellable-shield.mjs
similarity index 100%
rename from benchmarks/articles/08-uncancellable-shield.mjs
rename to packages/core/benchmarks/articles/08-uncancellable-shield.mjs
diff --git a/benchmarks/articles/09-stream-1b-lazy.mjs b/packages/core/benchmarks/articles/09-stream-1b-lazy.mjs
similarity index 100%
rename from benchmarks/articles/09-stream-1b-lazy.mjs
rename to packages/core/benchmarks/articles/09-stream-1b-lazy.mjs
diff --git a/benchmarks/articles/10-stream-slow-consumer.mjs b/packages/core/benchmarks/articles/10-stream-slow-consumer.mjs
similarity index 100%
rename from benchmarks/articles/10-stream-slow-consumer.mjs
rename to packages/core/benchmarks/articles/10-stream-slow-consumer.mjs
diff --git a/benchmarks/articles/11-channel-contract.mjs b/packages/core/benchmarks/articles/11-channel-contract.mjs
similarity index 100%
rename from benchmarks/articles/11-channel-contract.mjs
rename to packages/core/benchmarks/articles/11-channel-contract.mjs
diff --git a/benchmarks/articles/12-bracket-vs-try-finally.mjs b/packages/core/benchmarks/articles/12-bracket-vs-try-finally.mjs
similarity index 100%
rename from benchmarks/articles/12-bracket-vs-try-finally.mjs
rename to packages/core/benchmarks/articles/12-bracket-vs-try-finally.mjs
diff --git a/benchmarks/articles/13-budget-atomicity-and-cancel.mjs b/packages/core/benchmarks/articles/13-budget-atomicity-and-cancel.mjs
similarity index 100%
rename from benchmarks/articles/13-budget-atomicity-and-cancel.mjs
rename to packages/core/benchmarks/articles/13-budget-atomicity-and-cancel.mjs
diff --git a/benchmarks/articles/14-context-overlay-perf.mjs b/packages/core/benchmarks/articles/14-context-overlay-perf.mjs
similarity index 100%
rename from benchmarks/articles/14-context-overlay-perf.mjs
rename to packages/core/benchmarks/articles/14-context-overlay-perf.mjs
diff --git a/benchmarks/articles/15-core-zero-network.mjs b/packages/core/benchmarks/articles/15-core-zero-network.mjs
similarity index 100%
rename from benchmarks/articles/15-core-zero-network.mjs
rename to packages/core/benchmarks/articles/15-core-zero-network.mjs
diff --git a/benchmarks/articles/16-sampling-and-aggregation.mjs b/packages/core/benchmarks/articles/16-sampling-and-aggregation.mjs
similarity index 100%
rename from benchmarks/articles/16-sampling-and-aggregation.mjs
rename to packages/core/benchmarks/articles/16-sampling-and-aggregation.mjs
diff --git a/benchmarks/articles/17-cardinality-safe-metrics.mjs b/packages/core/benchmarks/articles/17-cardinality-safe-metrics.mjs
similarity index 100%
rename from benchmarks/articles/17-cardinality-safe-metrics.mjs
rename to packages/core/benchmarks/articles/17-cardinality-safe-metrics.mjs
diff --git a/benchmarks/articles/18-diagnostics-finding-codes.mjs b/packages/core/benchmarks/articles/18-diagnostics-finding-codes.mjs
similarity index 100%
rename from benchmarks/articles/18-diagnostics-finding-codes.mjs
rename to packages/core/benchmarks/articles/18-diagnostics-finding-codes.mjs
diff --git a/benchmarks/articles/19-agent-scope.mjs b/packages/core/benchmarks/articles/19-agent-scope.mjs
similarity index 100%
rename from benchmarks/articles/19-agent-scope.mjs
rename to packages/core/benchmarks/articles/19-agent-scope.mjs
diff --git a/benchmarks/articles/README.md b/packages/core/benchmarks/articles/README.md
similarity index 100%
rename from benchmarks/articles/README.md
rename to packages/core/benchmarks/articles/README.md
diff --git a/benchmarks/articles/lib/baselines.mjs b/packages/core/benchmarks/articles/lib/baselines.mjs
similarity index 100%
rename from benchmarks/articles/lib/baselines.mjs
rename to packages/core/benchmarks/articles/lib/baselines.mjs
diff --git a/benchmarks/articles/lib/spinner.mjs b/packages/core/benchmarks/articles/lib/spinner.mjs
similarity index 100%
rename from benchmarks/articles/lib/spinner.mjs
rename to packages/core/benchmarks/articles/lib/spinner.mjs
diff --git a/benchmarks/articles/package.json b/packages/core/benchmarks/articles/package.json
similarity index 100%
rename from benchmarks/articles/package.json
rename to packages/core/benchmarks/articles/package.json
diff --git a/benchmarks/articles/run-all.mjs b/packages/core/benchmarks/articles/run-all.mjs
similarity index 100%
rename from benchmarks/articles/run-all.mjs
rename to packages/core/benchmarks/articles/run-all.mjs
diff --git a/benchmarks/articles/run-repeated.mjs b/packages/core/benchmarks/articles/run-repeated.mjs
similarity index 100%
rename from benchmarks/articles/run-repeated.mjs
rename to packages/core/benchmarks/articles/run-repeated.mjs
diff --git a/benchmarks/public-proof.json b/packages/core/benchmarks/public-proof.json
similarity index 100%
rename from benchmarks/public-proof.json
rename to packages/core/benchmarks/public-proof.json
diff --git a/benchmarks/results/articles.latest.json b/packages/core/benchmarks/results/articles.latest.json
similarity index 100%
rename from benchmarks/results/articles.latest.json
rename to packages/core/benchmarks/results/articles.latest.json
diff --git a/benchmarks/results/articles.repeated.json b/packages/core/benchmarks/results/articles.repeated.json
similarity index 100%
rename from benchmarks/results/articles.repeated.json
rename to packages/core/benchmarks/results/articles.repeated.json
diff --git a/evidence/README.md b/packages/core/evidence/README.md
similarity index 100%
rename from evidence/README.md
rename to packages/core/evidence/README.md
diff --git a/evidence/claims.json b/packages/core/evidence/claims.json
similarity index 100%
rename from evidence/claims.json
rename to packages/core/evidence/claims.json
diff --git a/packages/core/package.json b/packages/core/package.json
new file mode 100644
index 0000000..cdceab0
--- /dev/null
+++ b/packages/core/package.json
@@ -0,0 +1,154 @@
+{
+ "name": "@workit/core",
+ "version": "0.1.5",
+ "description": "Structured concurrency runtime for TypeScript.",
+ "type": "module",
+ "private": false,
+ "license": "Apache-2.0",
+ "author": "Admilson B. F. Cossa",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/WorkRuntime/workit"
+ },
+ "bugs": {
+ "url": "https://github.com/WorkRuntime/workit/issues"
+ },
+ "homepage": "https://github.com/WorkRuntime/workit#readme",
+ "main": "./dist/index.js",
+ "types": "./dist/index.d.ts",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "node": {
+ "import": "./dist/index.js",
+ "require": "./dist-cjs/index.cjs"
+ },
+ "default": "./dist/runtime/unsupported.js"
+ },
+ "./ai": {
+ "types": "./dist/ai/index.d.ts",
+ "node": {
+ "import": "./dist/ai/index.js",
+ "require": "./dist-cjs/ai/index.cjs"
+ },
+ "default": "./dist/runtime/unsupported.js"
+ },
+ "./channel": {
+ "types": "./dist/channel/index.d.ts",
+ "import": "./dist/channel/index.js",
+ "require": "./dist-cjs/channel/index.cjs"
+ },
+ "./observability": {
+ "types": "./dist/observability/index.d.ts",
+ "import": "./dist/observability/index.js",
+ "require": "./dist-cjs/observability/index.cjs"
+ },
+ "./diagnostics": {
+ "types": "./dist/diagnostics/index.d.ts",
+ "import": "./dist/diagnostics/index.js",
+ "require": "./dist-cjs/diagnostics/index.cjs"
+ },
+ "./otel": {
+ "types": "./dist/otel/index.d.ts",
+ "import": "./dist/otel/index.js",
+ "require": "./dist-cjs/otel/index.cjs"
+ },
+ "./worker": {
+ "types": "./dist/worker/index.d.ts",
+ "node": {
+ "import": "./dist/worker/index.js"
+ },
+ "default": "./dist/runtime/unsupported.js"
+ }
+ },
+ "sideEffects": false,
+ "publishConfig": {
+ "access": "public"
+ },
+ "files": [
+ "CODE_OF_CONDUCT.md",
+ "CONTRIBUTING.md",
+ "dist",
+ "dist-cjs",
+ "SECURITY.md"
+ ],
+ "scripts": {
+ "clean": "node -e \"const fs=require('node:fs'); for (const p of ['dist','dist-cjs','coverage']) fs.rmSync(p,{recursive:true,force:true});\"",
+ "build": "npm run clean && tsc && node scripts/build-cjs.mjs && node scripts/generate-sbom.mjs",
+ "typecheck": "tsc --noEmit",
+ "check:no-network": "node scripts/check-no-network.mjs",
+ "check:headers": "node scripts/check-file-headers.mjs",
+ "check:security": "node scripts/check-security.mjs",
+ "check:vulnerabilities": "node scripts/check-vulnerabilities.mjs",
+ "check:sbom": "npm run build && node scripts/check-sbom.mjs",
+ "check:tests": "node scripts/check-tests.mjs",
+ "check:api": "npm run build && node scripts/check-api-surface.mjs",
+ "check:size": "npm run build && node scripts/check-bundle-size.mjs",
+ "report:size": "npm run build && node scripts/report-bundle-size.mjs",
+ "check:benchmark": "npm run build && node scripts/check-benchmark.mjs",
+ "check:context-performance": "npm run build && node scripts/check-context-performance.mjs",
+ "check:1b": "npm run build && node scripts/check-1b-benchmark.mjs",
+ "check:leak": "npm run build && node --expose-gc scripts/check-leak.mjs",
+ "check:stream-memory": "npm run build && node --expose-gc scripts/check-stream-memory.mjs",
+ "check:soak": "npm run build && node --expose-gc scripts/check-soak.mjs",
+ "check:exporter-stress": "npm run build && node --expose-gc scripts/check-exporter-stress.mjs",
+ "check:package-consumer": "npm run build && node scripts/check-package-consumer.mjs",
+ "check:claims": "npm run build && node scripts/check-claim-fixtures.mjs",
+ "check:public-proof": "node scripts/check-public-proof.mjs",
+ "check:worker-contract": "node scripts/check-worker-contract-docs.mjs",
+ "check:release-policy": "npm run build && node scripts/check-release-provenance.mjs",
+ "check:release": "npm run build && node scripts/check-release-provenance.mjs --registry-dry-run",
+ "bench:articles": "node benchmarks/articles/run-all.mjs",
+ "bench:articles:repeated": "node benchmarks/articles/run-repeated.mjs",
+ "test:evidence": "node tests/evidence/run-all.mjs",
+ "test:property": "npm run build && vitest run tests/property",
+ "pack:dry": "npm run build && npm pack --dry-run --json",
+ "sample:1b": "npm run build && node samples/1b-stream.sample.js",
+ "sample:concurrency": "npm run build && node samples/concurrency-budget.sample.js",
+ "sample:progress": "npm run build && node samples/progress-parallel.sample.js",
+ "sample:cancel": "npm run build && node samples/cancel-reason.sample.js",
+ "sample:timeout": "npm run build && node samples/timeout-stop.sample.js",
+ "sample:no-orphan": "npm run build && node samples/no-orphan.sample.js",
+ "sample:all": "npm run build && node samples/safer-promise-all.sample.js",
+ "sample:agent": "npm run build && node samples/agent-tree-cancel.sample.js",
+ "sample:race": "npm run build && node samples/race-providers.sample.js",
+ "sample:rag": "npm run build && node samples/budget-rag.sample.js",
+ "sample:batch": "npm run build && node samples/batch-upload.sample.js",
+ "sample:stream": "npm run build && node samples/streaming-summarizer.sample.js",
+ "sample:embed100k": "npm run build && node samples/embed-100k.sample.js",
+ "sample:bisection": "npm run build && node samples/embed-bisection.sample.js",
+ "sample:stt-disconnect": "npm run build && node samples/stt-disconnect.sample.js",
+ "sample:supervise": "npm run build && node samples/supervision.sample.js",
+ "sample:worker": "npm run build && node samples/worker-offload.sample.js",
+ "sample:aws": "npm run build && node samples/aws-lambda-handler.sample.js",
+ "sample:azure": "npm run build && node samples/azure-functions-handler.sample.js",
+ "sample:next": "npm run build && node samples/next-server-route.sample.js",
+ "sample:otel": "npm run build && node samples/otel-adapter.sample.js",
+ "sample:logging": "npm run build && node samples/logging-otel-bridge.sample.js",
+ "soak:24h": "npm run build && node --expose-gc scripts/soak-24h.mjs",
+ "test": "npm run build && vitest run",
+ "test:coverage": "npm run build && vitest run --coverage",
+ "verify": "npm run typecheck && npm run check:no-network && npm run check:headers && npm run check:tests && npm test && npm run check:security && npm run check:vulnerabilities && npm run check:sbom && npm run check:api && npm run check:size && npm run check:benchmark && npm run check:context-performance && npm run check:1b && npm run check:leak && npm run check:stream-memory && npm run check:soak && npm run check:exporter-stress && npm run check:package-consumer && npm run check:claims && npm run check:public-proof && npm run check:worker-contract && npm run check:release-policy && npm run pack:dry"
+ },
+ "engines": {
+ "node": ">=20.11"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.9.1"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ }
+ },
+ "devDependencies": {
+ "@opentelemetry/api": "1.9.1",
+ "@types/node": "25.6.1",
+ "@vitest/coverage-v8": "4.1.5",
+ "esbuild": "0.28.0",
+ "fast-check": "4.7.0",
+ "typescript": "6.0.3",
+ "vitest": "4.1.5",
+ "wrangler": "4.89.1"
+ }
+}
diff --git a/samples/1b-stream.sample.js b/packages/core/samples/1b-stream.sample.js
similarity index 100%
rename from samples/1b-stream.sample.js
rename to packages/core/samples/1b-stream.sample.js
diff --git a/samples/agent-tree-cancel.sample.js b/packages/core/samples/agent-tree-cancel.sample.js
similarity index 100%
rename from samples/agent-tree-cancel.sample.js
rename to packages/core/samples/agent-tree-cancel.sample.js
diff --git a/samples/aws-lambda-handler.sample.js b/packages/core/samples/aws-lambda-handler.sample.js
similarity index 100%
rename from samples/aws-lambda-handler.sample.js
rename to packages/core/samples/aws-lambda-handler.sample.js
diff --git a/samples/azure-functions-handler.sample.js b/packages/core/samples/azure-functions-handler.sample.js
similarity index 100%
rename from samples/azure-functions-handler.sample.js
rename to packages/core/samples/azure-functions-handler.sample.js
diff --git a/samples/batch-upload.sample.js b/packages/core/samples/batch-upload.sample.js
similarity index 100%
rename from samples/batch-upload.sample.js
rename to packages/core/samples/batch-upload.sample.js
diff --git a/samples/budget-rag.sample.js b/packages/core/samples/budget-rag.sample.js
similarity index 100%
rename from samples/budget-rag.sample.js
rename to packages/core/samples/budget-rag.sample.js
diff --git a/samples/cancel-reason.sample.js b/packages/core/samples/cancel-reason.sample.js
similarity index 100%
rename from samples/cancel-reason.sample.js
rename to packages/core/samples/cancel-reason.sample.js
diff --git a/samples/claim-fixtures.mjs b/packages/core/samples/claim-fixtures.mjs
similarity index 100%
rename from samples/claim-fixtures.mjs
rename to packages/core/samples/claim-fixtures.mjs
diff --git a/samples/concurrency-budget.sample.js b/packages/core/samples/concurrency-budget.sample.js
similarity index 100%
rename from samples/concurrency-budget.sample.js
rename to packages/core/samples/concurrency-budget.sample.js
diff --git a/samples/conversation-agent.sample.js b/packages/core/samples/conversation-agent.sample.js
similarity index 100%
rename from samples/conversation-agent.sample.js
rename to packages/core/samples/conversation-agent.sample.js
diff --git a/samples/cpu-worker.sample-worker.js b/packages/core/samples/cpu-worker.sample-worker.js
similarity index 100%
rename from samples/cpu-worker.sample-worker.js
rename to packages/core/samples/cpu-worker.sample-worker.js
diff --git a/samples/embed-100k.sample.js b/packages/core/samples/embed-100k.sample.js
similarity index 100%
rename from samples/embed-100k.sample.js
rename to packages/core/samples/embed-100k.sample.js
diff --git a/samples/embed-bisection.sample.js b/packages/core/samples/embed-bisection.sample.js
similarity index 100%
rename from samples/embed-bisection.sample.js
rename to packages/core/samples/embed-bisection.sample.js
diff --git a/samples/logging-otel-bridge.sample.js b/packages/core/samples/logging-otel-bridge.sample.js
similarity index 100%
rename from samples/logging-otel-bridge.sample.js
rename to packages/core/samples/logging-otel-bridge.sample.js
diff --git a/samples/next-server-route.sample.js b/packages/core/samples/next-server-route.sample.js
similarity index 100%
rename from samples/next-server-route.sample.js
rename to packages/core/samples/next-server-route.sample.js
diff --git a/samples/no-orphan.sample.js b/packages/core/samples/no-orphan.sample.js
similarity index 100%
rename from samples/no-orphan.sample.js
rename to packages/core/samples/no-orphan.sample.js
diff --git a/samples/otel-adapter.sample.js b/packages/core/samples/otel-adapter.sample.js
similarity index 100%
rename from samples/otel-adapter.sample.js
rename to packages/core/samples/otel-adapter.sample.js
diff --git a/samples/progress-parallel.sample.js b/packages/core/samples/progress-parallel.sample.js
similarity index 100%
rename from samples/progress-parallel.sample.js
rename to packages/core/samples/progress-parallel.sample.js
diff --git a/samples/race-providers.sample.js b/packages/core/samples/race-providers.sample.js
similarity index 100%
rename from samples/race-providers.sample.js
rename to packages/core/samples/race-providers.sample.js
diff --git a/samples/safer-promise-all.sample.js b/packages/core/samples/safer-promise-all.sample.js
similarity index 100%
rename from samples/safer-promise-all.sample.js
rename to packages/core/samples/safer-promise-all.sample.js
diff --git a/samples/streaming-summarizer.sample.js b/packages/core/samples/streaming-summarizer.sample.js
similarity index 100%
rename from samples/streaming-summarizer.sample.js
rename to packages/core/samples/streaming-summarizer.sample.js
diff --git a/samples/stt-disconnect.sample.js b/packages/core/samples/stt-disconnect.sample.js
similarity index 100%
rename from samples/stt-disconnect.sample.js
rename to packages/core/samples/stt-disconnect.sample.js
diff --git a/samples/supervision.sample.js b/packages/core/samples/supervision.sample.js
similarity index 100%
rename from samples/supervision.sample.js
rename to packages/core/samples/supervision.sample.js
diff --git a/samples/timeout-stop.sample.js b/packages/core/samples/timeout-stop.sample.js
similarity index 100%
rename from samples/timeout-stop.sample.js
rename to packages/core/samples/timeout-stop.sample.js
diff --git a/samples/worker-offload.sample.js b/packages/core/samples/worker-offload.sample.js
similarity index 100%
rename from samples/worker-offload.sample.js
rename to packages/core/samples/worker-offload.sample.js
diff --git a/scripts/build-cjs.mjs b/packages/core/scripts/build-cjs.mjs
similarity index 100%
rename from scripts/build-cjs.mjs
rename to packages/core/scripts/build-cjs.mjs
diff --git a/scripts/check-1b-benchmark.mjs b/packages/core/scripts/check-1b-benchmark.mjs
similarity index 100%
rename from scripts/check-1b-benchmark.mjs
rename to packages/core/scripts/check-1b-benchmark.mjs
diff --git a/scripts/check-api-surface.mjs b/packages/core/scripts/check-api-surface.mjs
similarity index 100%
rename from scripts/check-api-surface.mjs
rename to packages/core/scripts/check-api-surface.mjs
diff --git a/scripts/check-benchmark.mjs b/packages/core/scripts/check-benchmark.mjs
similarity index 100%
rename from scripts/check-benchmark.mjs
rename to packages/core/scripts/check-benchmark.mjs
diff --git a/scripts/check-bundle-size.mjs b/packages/core/scripts/check-bundle-size.mjs
similarity index 100%
rename from scripts/check-bundle-size.mjs
rename to packages/core/scripts/check-bundle-size.mjs
diff --git a/scripts/check-claim-fixtures.mjs b/packages/core/scripts/check-claim-fixtures.mjs
similarity index 100%
rename from scripts/check-claim-fixtures.mjs
rename to packages/core/scripts/check-claim-fixtures.mjs
diff --git a/scripts/check-context-performance.mjs b/packages/core/scripts/check-context-performance.mjs
similarity index 100%
rename from scripts/check-context-performance.mjs
rename to packages/core/scripts/check-context-performance.mjs
diff --git a/scripts/check-exporter-stress.mjs b/packages/core/scripts/check-exporter-stress.mjs
similarity index 100%
rename from scripts/check-exporter-stress.mjs
rename to packages/core/scripts/check-exporter-stress.mjs
diff --git a/scripts/check-file-headers.mjs b/packages/core/scripts/check-file-headers.mjs
similarity index 100%
rename from scripts/check-file-headers.mjs
rename to packages/core/scripts/check-file-headers.mjs
diff --git a/scripts/check-leak.mjs b/packages/core/scripts/check-leak.mjs
similarity index 100%
rename from scripts/check-leak.mjs
rename to packages/core/scripts/check-leak.mjs
diff --git a/scripts/check-no-network.mjs b/packages/core/scripts/check-no-network.mjs
similarity index 100%
rename from scripts/check-no-network.mjs
rename to packages/core/scripts/check-no-network.mjs
diff --git a/scripts/check-package-consumer.mjs b/packages/core/scripts/check-package-consumer.mjs
similarity index 98%
rename from scripts/check-package-consumer.mjs
rename to packages/core/scripts/check-package-consumer.mjs
index b983944..ed3f59d 100644
--- a/scripts/check-package-consumer.mjs
+++ b/packages/core/scripts/check-package-consumer.mjs
@@ -17,14 +17,15 @@ import { promisify } from "node:util";
import { build } from "esbuild";
const execFileAsync = promisify(execFile);
-const ROOT = resolve(fileURLToPath(new URL("..", import.meta.url)));
-const tscCli = join(ROOT, "node_modules", "typescript", "bin", "tsc");
+const PACKAGE_ROOT = resolve(fileURLToPath(new URL("..", import.meta.url)));
+const REPO_ROOT = resolve(fileURLToPath(new URL("../../..", import.meta.url)));
+const tscCli = join(REPO_ROOT, "node_modules", "typescript", "bin", "tsc");
const bunCli = await findExecutable(["bun.exe", "bun"], [join(homedir(), ".bun", "bin", "bun.exe")]);
const denoCli = await findExecutable(["deno.exe", "deno"], [join(homedir(), ".deno", "bin", "deno.exe")]);
const wranglerCli = await findExecutable(
["wrangler.cmd", "wrangler"],
[
- join(ROOT, "node_modules", ".bin", "wrangler.cmd"),
+ join(REPO_ROOT, "node_modules", ".bin", "wrangler.cmd"),
join(homedir(), "node_modules", ".bin", "wrangler.cmd"),
]
);
@@ -37,7 +38,7 @@ const temp = await mkdtemp(join(tmpdir(), "workit-consumer-"));
try {
const { stdout } = await runNpm(["pack", "--json", "--pack-destination", temp], {
- cwd: ROOT,
+ cwd: PACKAGE_ROOT,
timeout: 120_000,
});
const [pack] = JSON.parse(stdout);
diff --git a/scripts/check-public-proof.mjs b/packages/core/scripts/check-public-proof.mjs
similarity index 100%
rename from scripts/check-public-proof.mjs
rename to packages/core/scripts/check-public-proof.mjs
diff --git a/scripts/check-release-provenance.mjs b/packages/core/scripts/check-release-provenance.mjs
similarity index 82%
rename from scripts/check-release-provenance.mjs
rename to packages/core/scripts/check-release-provenance.mjs
index c9456c5..70956c7 100644
--- a/scripts/check-release-provenance.mjs
+++ b/packages/core/scripts/check-release-provenance.mjs
@@ -13,16 +13,19 @@
import assert from "node:assert/strict";
import { execFile } from "node:child_process";
import { readFile } from "node:fs/promises";
+import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
+const packageRoot = new URL("../", import.meta.url);
+const repoRoot = new URL("../../../", import.meta.url);
const packageJson = JSON.parse(await readFile("package.json", "utf8"));
-const workflow = await readFile(".github/workflows/release-provenance.yml", "utf8");
+const workflow = await readFile(new URL(".github/workflows/release-provenance.yml", repoRoot), "utf8");
const security = await readFile("SECURITY.md", "utf8");
-const codeowners = await readRequiredFile(".github/CODEOWNERS");
-const allowedSigners = await readRequiredFile(".github/allowed_signers");
-const dependabot = await readRequiredFile(".github/dependabot.yml");
-const ci = await readRequiredFile(".github/workflows/ci.yml");
-const scorecard = await readRequiredFile(".github/workflows/scorecard.yml");
+const codeowners = await readRequiredFile(new URL(".github/CODEOWNERS", repoRoot));
+const allowedSigners = await readRequiredFile(new URL(".github/allowed_signers", repoRoot));
+const dependabot = await readRequiredFile(new URL(".github/dependabot.yml", repoRoot));
+const ci = await readRequiredFile(new URL(".github/workflows/ci.yml", repoRoot));
+const scorecard = await readRequiredFile(new URL(".github/workflows/scorecard.yml", repoRoot));
const requireRegistryDryRun = process.argv.includes("--registry-dry-run");
const execFileAsync = promisify(execFile);
@@ -49,7 +52,7 @@ assert.ok(packageJson.files.includes("SECURITY.md"), "published package must inc
assert.ok(packageJson.files.includes("CONTRIBUTING.md"), "published package must include CONTRIBUTING.md");
assert.match(workflow, /id-token:\s*write/u, "release workflow must allow OIDC id-token provenance");
assert.match(workflow, /attestations:\s*write/u, "release workflow must allow GitHub artifact attestations");
-assert.match(workflow, /npm publish --provenance --access public/u, "release workflow must publish with npm provenance");
+assert.match(workflow, /npm publish --workspace @workit\/core --provenance --access public/u, "release workflow must publish @workit/core with npm provenance");
assert.match(workflow, /npm run verify/u, "release workflow must run full verification before publish");
assert.match(workflow, /npm run test:coverage/u, "release workflow must run coverage before publish");
assert.match(workflow, /gpg\.ssh\.allowedSignersFile/u, "release workflow must configure SSH allowed signers before tag verification");
@@ -98,20 +101,21 @@ function assertShaPinnedActions(path, text) {
}
async function assertExistingTagsAreSigned() {
- const { stdout } = await execFileAsync("git", ["tag", "--list"]);
+ const { stdout } = await execFileAsync("git", ["tag", "--list"], { cwd: fileURLToPath(repoRoot) });
for (const tag of stdout.split(/\r?\n/u).filter(Boolean)) {
- await execFileAsync("git", ["tag", "-v", tag]);
+ await execFileAsync("git", ["tag", "-v", tag], { cwd: fileURLToPath(repoRoot) });
}
}
async function runNpm(args) {
if (process.env.npm_execpath !== undefined) {
await execFileAsync(process.execPath, [process.env.npm_execpath, ...args], {
+ cwd: fileURLToPath(packageRoot),
timeout: 120_000,
});
return;
}
const npmExecutable = process.platform === "win32" ? "npm.cmd" : "npm";
- await execFileAsync(npmExecutable, args, { timeout: 120_000 });
+ await execFileAsync(npmExecutable, args, { cwd: fileURLToPath(packageRoot), timeout: 120_000 });
}
diff --git a/scripts/check-sbom.mjs b/packages/core/scripts/check-sbom.mjs
similarity index 100%
rename from scripts/check-sbom.mjs
rename to packages/core/scripts/check-sbom.mjs
diff --git a/scripts/check-security.mjs b/packages/core/scripts/check-security.mjs
similarity index 85%
rename from scripts/check-security.mjs
rename to packages/core/scripts/check-security.mjs
index f83309b..4c5d0be 100644
--- a/scripts/check-security.mjs
+++ b/packages/core/scripts/check-security.mjs
@@ -14,18 +14,18 @@ import { join } from "node:path";
const failures = [];
const packageJson = JSON.parse(await readFile("package.json", "utf8"));
-const packageLock = JSON.parse(await readFile("package-lock.json", "utf8"));
+const packageLock = JSON.parse(await readFile("../../package-lock.json", "utf8"));
const tsconfig = JSON.parse(stripJsonComments(await readFile("tsconfig.json", "utf8")));
const securityPolicy = await readFile("SECURITY.md", "utf8");
-const rootLock = packageLock.packages?.[""] ?? {};
+const packageLockEntry = packageLock.packages?.["packages/core"] ?? {};
const runtimeDependencies = Object.keys(packageJson.dependencies ?? {});
if (runtimeDependencies.length > 0) {
failures.push(`Runtime dependencies must stay empty: ${runtimeDependencies.join(", ")}`);
}
-if (packageLock.name !== packageJson.name || rootLock.name !== packageJson.name) {
- failures.push("package-lock package name must match package.json");
+if (packageLockEntry.name !== packageJson.name) {
+ failures.push("package-lock workspace package name must match packages/core/package.json");
}
for (const [name, version] of Object.entries(packageJson.devDependencies ?? {})) {
@@ -34,12 +34,12 @@ for (const [name, version] of Object.entries(packageJson.devDependencies ?? {}))
}
}
-if (JSON.stringify(rootLock.devDependencies ?? {}) !== JSON.stringify(packageJson.devDependencies ?? {})) {
- failures.push("package-lock root devDependencies must match package.json exactly");
+if (JSON.stringify(packageLockEntry.devDependencies ?? {}) !== JSON.stringify(packageJson.devDependencies ?? {})) {
+ failures.push("package-lock workspace devDependencies must match packages/core/package.json exactly");
}
-if (JSON.stringify(rootLock.peerDependencies ?? {}) !== JSON.stringify(packageJson.peerDependencies ?? {})) {
- failures.push("package-lock root peerDependencies must match package.json exactly");
+if (JSON.stringify(packageLockEntry.peerDependencies ?? {}) !== JSON.stringify(packageJson.peerDependencies ?? {})) {
+ failures.push("package-lock workspace peerDependencies must match packages/core/package.json exactly");
}
for (const lifecycle of ["preinstall", "install", "postinstall"]) {
diff --git a/scripts/check-soak.mjs b/packages/core/scripts/check-soak.mjs
similarity index 100%
rename from scripts/check-soak.mjs
rename to packages/core/scripts/check-soak.mjs
diff --git a/scripts/check-stream-memory.mjs b/packages/core/scripts/check-stream-memory.mjs
similarity index 100%
rename from scripts/check-stream-memory.mjs
rename to packages/core/scripts/check-stream-memory.mjs
diff --git a/scripts/check-tests.mjs b/packages/core/scripts/check-tests.mjs
similarity index 100%
rename from scripts/check-tests.mjs
rename to packages/core/scripts/check-tests.mjs
diff --git a/scripts/check-vulnerabilities.mjs b/packages/core/scripts/check-vulnerabilities.mjs
similarity index 100%
rename from scripts/check-vulnerabilities.mjs
rename to packages/core/scripts/check-vulnerabilities.mjs
diff --git a/scripts/check-worker-contract-docs.mjs b/packages/core/scripts/check-worker-contract-docs.mjs
similarity index 100%
rename from scripts/check-worker-contract-docs.mjs
rename to packages/core/scripts/check-worker-contract-docs.mjs
diff --git a/scripts/generate-sbom.mjs b/packages/core/scripts/generate-sbom.mjs
similarity index 87%
rename from scripts/generate-sbom.mjs
rename to packages/core/scripts/generate-sbom.mjs
index c2ab485..76ffa9b 100644
--- a/scripts/generate-sbom.mjs
+++ b/packages/core/scripts/generate-sbom.mjs
@@ -14,8 +14,8 @@ import { createHash, randomUUID } from "node:crypto";
import { join } from "node:path";
const packageJson = JSON.parse(await readFile("package.json", "utf8"));
-const packageLock = JSON.parse(await readFile("package-lock.json", "utf8"));
-const rootLock = packageLock.packages?.[""] ?? {};
+const packageLock = JSON.parse(await readFile("../../package-lock.json", "utf8"));
+const packageLockEntry = packageLock.packages?.["packages/core"] ?? {};
const runtimeDependencies = Object.keys(packageJson.dependencies ?? {});
if (runtimeDependencies.length > 0) {
@@ -25,10 +25,10 @@ if (runtimeDependencies.length > 0) {
const bomRef = packagePurl(packageJson.name, packageJson.version);
const lockDigest = createHash("sha256")
.update(JSON.stringify({
- name: rootLock.name,
- version: rootLock.version,
- license: rootLock.license,
- peerDependencies: rootLock.peerDependencies ?? {},
+ name: packageLockEntry.name,
+ version: packageLockEntry.version,
+ license: packageLockEntry.license,
+ peerDependencies: packageLockEntry.peerDependencies ?? {},
}))
.digest("hex");
diff --git a/scripts/report-bundle-size.mjs b/packages/core/scripts/report-bundle-size.mjs
similarity index 100%
rename from scripts/report-bundle-size.mjs
rename to packages/core/scripts/report-bundle-size.mjs
diff --git a/scripts/soak-24h.mjs b/packages/core/scripts/soak-24h.mjs
similarity index 100%
rename from scripts/soak-24h.mjs
rename to packages/core/scripts/soak-24h.mjs
diff --git a/scripts/soak-runtime.mjs b/packages/core/scripts/soak-runtime.mjs
similarity index 100%
rename from scripts/soak-runtime.mjs
rename to packages/core/scripts/soak-runtime.mjs
diff --git a/src/ai/index.ts b/packages/core/src/ai/index.ts
similarity index 100%
rename from src/ai/index.ts
rename to packages/core/src/ai/index.ts
diff --git a/src/channel/index.ts b/packages/core/src/channel/index.ts
similarity index 100%
rename from src/channel/index.ts
rename to packages/core/src/channel/index.ts
diff --git a/src/diagnostics/index.ts b/packages/core/src/diagnostics/index.ts
similarity index 100%
rename from src/diagnostics/index.ts
rename to packages/core/src/diagnostics/index.ts
diff --git a/src/engine/context.ts b/packages/core/src/engine/context.ts
similarity index 100%
rename from src/engine/context.ts
rename to packages/core/src/engine/context.ts
diff --git a/src/engine/duration.ts b/packages/core/src/engine/duration.ts
similarity index 100%
rename from src/engine/duration.ts
rename to packages/core/src/engine/duration.ts
diff --git a/src/engine/event-bus.ts b/packages/core/src/engine/event-bus.ts
similarity index 100%
rename from src/engine/event-bus.ts
rename to packages/core/src/engine/event-bus.ts
diff --git a/src/engine/retry.ts b/packages/core/src/engine/retry.ts
similarity index 100%
rename from src/engine/retry.ts
rename to packages/core/src/engine/retry.ts
diff --git a/src/engine/scope.ts b/packages/core/src/engine/scope.ts
similarity index 100%
rename from src/engine/scope.ts
rename to packages/core/src/engine/scope.ts
diff --git a/src/engine/tree.ts b/packages/core/src/engine/tree.ts
similarity index 100%
rename from src/engine/tree.ts
rename to packages/core/src/engine/tree.ts
diff --git a/src/index.ts b/packages/core/src/index.ts
similarity index 100%
rename from src/index.ts
rename to packages/core/src/index.ts
diff --git a/src/observability/index.ts b/packages/core/src/observability/index.ts
similarity index 100%
rename from src/observability/index.ts
rename to packages/core/src/observability/index.ts
diff --git a/src/otel/index.ts b/packages/core/src/otel/index.ts
similarity index 100%
rename from src/otel/index.ts
rename to packages/core/src/otel/index.ts
diff --git a/src/run/index.ts b/packages/core/src/run/index.ts
similarity index 100%
rename from src/run/index.ts
rename to packages/core/src/run/index.ts
diff --git a/src/runtime/unsupported.ts b/packages/core/src/runtime/unsupported.ts
similarity index 100%
rename from src/runtime/unsupported.ts
rename to packages/core/src/runtime/unsupported.ts
diff --git a/src/types/index.ts b/packages/core/src/types/index.ts
similarity index 100%
rename from src/types/index.ts
rename to packages/core/src/types/index.ts
diff --git a/src/work/index.ts b/packages/core/src/work/index.ts
similarity index 100%
rename from src/work/index.ts
rename to packages/core/src/work/index.ts
diff --git a/src/worker/index.ts b/packages/core/src/worker/index.ts
similarity index 100%
rename from src/worker/index.ts
rename to packages/core/src/worker/index.ts
diff --git a/src/worker/module-url.ts b/packages/core/src/worker/module-url.ts
similarity index 100%
rename from src/worker/module-url.ts
rename to packages/core/src/worker/module-url.ts
diff --git a/src/worker/runner.ts b/packages/core/src/worker/runner.ts
similarity index 100%
rename from src/worker/runner.ts
rename to packages/core/src/worker/runner.ts
diff --git a/tests/evidence/correctness/runtime-contracts.mjs b/packages/core/tests/evidence/correctness/runtime-contracts.mjs
similarity index 100%
rename from tests/evidence/correctness/runtime-contracts.mjs
rename to packages/core/tests/evidence/correctness/runtime-contracts.mjs
diff --git a/tests/evidence/harness.mjs b/packages/core/tests/evidence/harness.mjs
similarity index 100%
rename from tests/evidence/harness.mjs
rename to packages/core/tests/evidence/harness.mjs
diff --git a/tests/evidence/lifecycle/owned-work.mjs b/packages/core/tests/evidence/lifecycle/owned-work.mjs
similarity index 100%
rename from tests/evidence/lifecycle/owned-work.mjs
rename to packages/core/tests/evidence/lifecycle/owned-work.mjs
diff --git a/tests/evidence/performance/benchmark-contracts.mjs b/packages/core/tests/evidence/performance/benchmark-contracts.mjs
similarity index 100%
rename from tests/evidence/performance/benchmark-contracts.mjs
rename to packages/core/tests/evidence/performance/benchmark-contracts.mjs
diff --git a/tests/evidence/release/release-integrity.mjs b/packages/core/tests/evidence/release/release-integrity.mjs
similarity index 100%
rename from tests/evidence/release/release-integrity.mjs
rename to packages/core/tests/evidence/release/release-integrity.mjs
diff --git a/tests/evidence/run-all.mjs b/packages/core/tests/evidence/run-all.mjs
similarity index 100%
rename from tests/evidence/run-all.mjs
rename to packages/core/tests/evidence/run-all.mjs
diff --git a/tests/evidence/security/cpu-spinner.mjs b/packages/core/tests/evidence/security/cpu-spinner.mjs
similarity index 100%
rename from tests/evidence/security/cpu-spinner.mjs
rename to packages/core/tests/evidence/security/cpu-spinner.mjs
diff --git a/tests/evidence/security/worker-boundary.mjs b/packages/core/tests/evidence/security/worker-boundary.mjs
similarity index 100%
rename from tests/evidence/security/worker-boundary.mjs
rename to packages/core/tests/evidence/security/worker-boundary.mjs
diff --git a/tests/property/core-invariants.property.test.js b/packages/core/tests/property/core-invariants.property.test.js
similarity index 100%
rename from tests/property/core-invariants.property.test.js
rename to packages/core/tests/property/core-invariants.property.test.js
diff --git a/tests/unit/ai.test.js b/packages/core/tests/unit/ai.test.js
similarity index 100%
rename from tests/unit/ai.test.js
rename to packages/core/tests/unit/ai.test.js
diff --git a/tests/unit/channel.test.js b/packages/core/tests/unit/channel.test.js
similarity index 100%
rename from tests/unit/channel.test.js
rename to packages/core/tests/unit/channel.test.js
diff --git a/tests/unit/claim-gaps.test.js b/packages/core/tests/unit/claim-gaps.test.js
similarity index 100%
rename from tests/unit/claim-gaps.test.js
rename to packages/core/tests/unit/claim-gaps.test.js
diff --git a/tests/unit/contracts.test.js b/packages/core/tests/unit/contracts.test.js
similarity index 100%
rename from tests/unit/contracts.test.js
rename to packages/core/tests/unit/contracts.test.js
diff --git a/tests/unit/coverage.test.js b/packages/core/tests/unit/coverage.test.js
similarity index 100%
rename from tests/unit/coverage.test.js
rename to packages/core/tests/unit/coverage.test.js
diff --git a/tests/unit/diagnostics.test.js b/packages/core/tests/unit/diagnostics.test.js
similarity index 100%
rename from tests/unit/diagnostics.test.js
rename to packages/core/tests/unit/diagnostics.test.js
diff --git a/tests/unit/event-bus.test.js b/packages/core/tests/unit/event-bus.test.js
similarity index 100%
rename from tests/unit/event-bus.test.js
rename to packages/core/tests/unit/event-bus.test.js
diff --git a/tests/unit/examples.test.js b/packages/core/tests/unit/examples.test.js
similarity index 100%
rename from tests/unit/examples.test.js
rename to packages/core/tests/unit/examples.test.js
diff --git a/tests/unit/invariants.test.js b/packages/core/tests/unit/invariants.test.js
similarity index 100%
rename from tests/unit/invariants.test.js
rename to packages/core/tests/unit/invariants.test.js
diff --git a/tests/unit/observability.test.js b/packages/core/tests/unit/observability.test.js
similarity index 100%
rename from tests/unit/observability.test.js
rename to packages/core/tests/unit/observability.test.js
diff --git a/tests/unit/otel.test.js b/packages/core/tests/unit/otel.test.js
similarity index 100%
rename from tests/unit/otel.test.js
rename to packages/core/tests/unit/otel.test.js
diff --git a/tests/unit/perf-contracts.test.js b/packages/core/tests/unit/perf-contracts.test.js
similarity index 100%
rename from tests/unit/perf-contracts.test.js
rename to packages/core/tests/unit/perf-contracts.test.js
diff --git a/tests/unit/run.test.js b/packages/core/tests/unit/run.test.js
similarity index 100%
rename from tests/unit/run.test.js
rename to packages/core/tests/unit/run.test.js
diff --git a/tests/unit/samples.test.js b/packages/core/tests/unit/samples.test.js
similarity index 100%
rename from tests/unit/samples.test.js
rename to packages/core/tests/unit/samples.test.js
diff --git a/tests/unit/sanity.test.js b/packages/core/tests/unit/sanity.test.js
similarity index 100%
rename from tests/unit/sanity.test.js
rename to packages/core/tests/unit/sanity.test.js
diff --git a/tests/unit/tree.test.js b/packages/core/tests/unit/tree.test.js
similarity index 100%
rename from tests/unit/tree.test.js
rename to packages/core/tests/unit/tree.test.js
diff --git a/tests/unit/work.test.js b/packages/core/tests/unit/work.test.js
similarity index 100%
rename from tests/unit/work.test.js
rename to packages/core/tests/unit/work.test.js
diff --git a/tests/unit/worker.test.js b/packages/core/tests/unit/worker.test.js
similarity index 100%
rename from tests/unit/worker.test.js
rename to packages/core/tests/unit/worker.test.js
diff --git a/tsconfig.json b/packages/core/tsconfig.json
similarity index 100%
rename from tsconfig.json
rename to packages/core/tsconfig.json
diff --git a/vitest.config.ts b/packages/core/vitest.config.ts
similarity index 100%
rename from vitest.config.ts
rename to packages/core/vitest.config.ts