Skip to content

feat(concurrency): CSP-safe module workers + client async primitives + graduate toward Stable (#133, #134, #135) - #154

Merged
JosunLP merged 1 commit into
devfrom
feat/concurrency-stable-133-135
Jun 28, 2026
Merged

feat(concurrency): CSP-safe module workers + client async primitives + graduate toward Stable (#133, #134, #135)#154
JosunLP merged 1 commit into
devfrom
feat/concurrency-stable-133-135

Conversation

@JosunLP

@JosunLP JosunLP commented Jun 28, 2026

Copy link
Copy Markdown
Collaborator

Collected work for the three open concurrency tickets, landed in one branch and targeting dev.

Closes #133. Closes #134. Closes #135.

#134 — CSP-safe module workers (remove the mandatory 'unsafe-eval')

  • defineWorker() / defineRpcWorker() describe a pre-bundled worker module by URL on the main thread; exposeTask() / exposeRpc() wire that module up to the bQuery worker protocol inside the worker. Because the body is a real module loaded by URL — never a function revived with new Function(...) — module mode runs under a strict CSP with no 'unsafe-eval' and no blob: worker source.
  • Every factory now accepts either an inline handler (dynamic mode, opt-in, needs 'unsafe-eval') or a WorkerModule (module mode, default/recommended): runTask, createTaskWorker, createTaskPool, createRpcWorker, createRpcPool, callWorkerMethod, and the reactive wrappers — additive and non-breaking via the WorkerTaskSource / WorkerRpcSource unions.
  • isWorkerModule(), isModuleWorkerSupported(), and support.moduleWorker (module mode requires only the Worker constructor, not Blob/URL.createObjectURL).

#135 — Client async-concurrency primitives (UI scheduling)

  • suspense() — declarative async boundary aggregating promises and reactive async states (useAsyncData/useResource) into reactive pending / settled / error signals; retrigger option; dispose().
  • startTransition()[isPending, start]; start(scope) flips pending immediately then runs the scope on a low-priority schedule inside a batch, decoupling expensive updates from urgent input. A throwing scope is contained and reported (mirrors effect()), never escapes as an uncaught timer error.
  • deferred() — readonly signal that lags its source and coalesces rapid changes to throttle expensive derived UI.
  • Built on signals, zero-dependency, tree-shakeable; a distinct concern from worker concurrency. Pairs with SSR suspense streaming (renderToStreamSuspense/defer).

#133 — promote concurrency toward Stable

  • Guide Stability section: exit-criteria checklist, frozen surface, runtime boundary (browser-focused per non-goals), and a per-environment support matrix; intro + README notes; version history.
  • Serializable-handler constraint documented + enforced (dynamic mode) with a clear TaskWorkerSerializationError; module mode is exempt by design.

Zero-dependency & secure-by-default

No runtime dependencies added; each primitive is independently importable/tree-shakeable. Module mode is the documented CSP-clean default, resolving the 'unsafe-eval' requirement most at odds with bQuery's security posture.

Verification

  • Full suite 2811 pass / 0 fail (26 new in tests/concurrency-stable.test.ts: module task/RPC/pool execution with no blob/eval available, exposeTask/exposeRpc protocol, support detection, and the client primitives incl. throwing-scope containment, deferred coalescing, suspense promise/state aggregation).
  • tsc, eslint, bun run build, check:full-bundle (in sync), check:doc-exports (concurrency 37/37), and test:types all green.

Review

Ran an adversarial multi-agent review of the diff; applied the confirmed hardening: contain a throwing startTransition scope and report it via console.error instead of letting it escape as an uncaught timer/idle exception (mirrors effect()), with a regression test.

🤖 Generated with Claude Code

…+ graduate toward Stable (#133, #134, #135)

Collected work for the three open `concurrency` tickets, landed in one branch
and targeting `dev`.

Closes #133. Closes #134. Closes #135.

## #134 — CSP-safe module workers (remove the mandatory `'unsafe-eval'`)
- **`defineWorker()` / `defineRpcWorker()`** describe a pre-bundled worker
  module by URL on the main thread; **`exposeTask()` / `exposeRpc()`** wire that
  module up to the bQuery worker protocol inside the worker. Because the body is
  a real module loaded by URL — never a function revived with `new Function(...)`
  — module mode runs under a strict CSP with **no `'unsafe-eval'`** and no
  `blob:` worker source.
- Every factory now accepts EITHER an inline handler (dynamic mode, opt-in,
  needs `'unsafe-eval'`) OR a `WorkerModule` (module mode, default/recommended):
  `runTask`, `createTaskWorker`, `createTaskPool`, `createRpcWorker`,
  `createRpcPool`, `callWorkerMethod`, and the reactive wrappers — additive and
  non-breaking via the `WorkerTaskSource` / `WorkerRpcSource` unions.
- `isWorkerModule()`, `isModuleWorkerSupported()`, and `support.moduleWorker`
  (module mode requires only the `Worker` constructor, not Blob/objectURL).

## #135 — Client async-concurrency primitives (UI scheduling)
- **`suspense()`** — declarative async boundary aggregating promises and reactive
  async states (`useAsyncData`/`useResource`) into reactive `pending` /
  `settled` / `error` signals; `retrigger` option; `dispose()`.
- **`startTransition()`** — `[isPending, start]`; `start(scope)` flips pending
  immediately then runs the scope on a low-priority schedule inside a `batch`,
  decoupling expensive updates from urgent input. A throwing scope is contained
  and reported (mirrors `effect()`), never escapes as an uncaught timer error.
- **`deferred()`** — readonly signal that lags its source and coalesces rapid
  changes to throttle expensive derived UI.
- Built on signals, zero-dependency, tree-shakeable; distinct from worker
  concurrency. Pairs with SSR suspense streaming.

## #133 — promote `concurrency` toward Stable
- Guide **Stability** section: exit-criteria checklist, frozen surface, runtime
  boundary (browser-focused per non-goals), per-environment support matrix;
  intro + README notes; version history.
- Serializable-handler constraint documented + enforced (dynamic mode) with a
  clear `TaskWorkerSerializationError`; module mode is exempt by design.

## Zero-dependency & secure-by-default
No runtime dependencies added; each primitive is independently
importable/tree-shakeable. Module mode is the documented CSP-clean default,
resolving the `'unsafe-eval'` requirement most at odds with bQuery's security
posture.

## Verification
- Full suite 2811 pass / 0 fail (26 new in `tests/concurrency-stable.test.ts`:
  module task/RPC/pool execution with no blob/eval, `exposeTask`/`exposeRpc`
  protocol, support detection, and the client primitives incl. throwing-scope
  containment, deferred coalescing, suspense promise/state aggregation).
- `tsc`, `eslint`, `bun run build`, `check:full-bundle` (in sync),
  `check:doc-exports` (concurrency 37/37), `test:types` all green.

## Review
Ran an adversarial multi-agent review of the diff; applied the confirmed
hardening: contain a throwing `startTransition` scope and report it via
`console.error` instead of letting it escape as an uncaught timer/idle exception.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@github-actions github-actions Bot added docs Changes to the documentation tests Chenges to the tests concurrency labels Jun 28, 2026
@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5771f2d9-8323-4f4f-9795-b8d70857f5aa

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/concurrency-stable-133-135

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@JosunLP
JosunLP merged commit 8a14040 into dev Jun 28, 2026
9 checks passed
@JosunLP
JosunLP deleted the feat/concurrency-stable-133-135 branch June 28, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

concurrency docs Changes to the documentation tests Chenges to the tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant