feat(concurrency): CSP-safe module workers + client async primitives + graduate toward Stable (#133, #134, #135) - #154
Merged
Conversation
…+ 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]>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Collected work for the three open
concurrencytickets, landed in one branch and targetingdev.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 withnew Function(...)— module mode runs under a strict CSP with no'unsafe-eval'and noblob:worker source.'unsafe-eval') or aWorkerModule(module mode, default/recommended):runTask,createTaskWorker,createTaskPool,createRpcWorker,createRpcPool,callWorkerMethod, and the reactive wrappers — additive and non-breaking via theWorkerTaskSource/WorkerRpcSourceunions.isWorkerModule(),isModuleWorkerSupported(), andsupport.moduleWorker(module mode requires only theWorkerconstructor, notBlob/URL.createObjectURL).#135 — Client async-concurrency primitives (UI scheduling)
suspense()— declarative async boundary aggregating promises and reactive async states (useAsyncData/useResource) into reactivepending/settled/errorsignals;retriggeroption;dispose().startTransition()—[isPending, start];start(scope)flips pending immediately then runs the scope on a low-priority schedule inside abatch, decoupling expensive updates from urgent input. A throwing scope is contained and reported (mirrorseffect()), never escapes as an uncaught timer error.deferred()— readonly signal that lags its source and coalesces rapid changes to throttle expensive derived UI.renderToStreamSuspense/defer).#133 — promote
concurrencytoward StableTaskWorkerSerializationError; 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
tests/concurrency-stable.test.ts: module task/RPC/pool execution with no blob/eval available,exposeTask/exposeRpcprotocol, 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), andtest:typesall green.Review
Ran an adversarial multi-agent review of the diff; applied the confirmed hardening: contain a throwing
startTransitionscope and report it viaconsole.errorinstead of letting it escape as an uncaught timer/idle exception (mirrorseffect()), with a regression test.🤖 Generated with Claude Code