vapor-chamber 1.29.0 → 1.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +32 -0
- package/README.md +5 -5
- package/ROADMAP.md +1 -1
- package/dist/LICENSE.txt +3 -2
- package/dist/bounds.d.ts +9 -0
- package/dist/bounds.d.ts.map +1 -1
- package/dist/bounds.js +4 -1
- package/dist/bounds.js.map +1 -1
- package/dist/chamber-vapor.d.ts.map +1 -1
- package/dist/chamber-vapor.js +3 -2
- package/dist/chamber-vapor.js.map +1 -1
- package/dist/chamber.d.ts +6 -1
- package/dist/chamber.d.ts.map +1 -1
- package/dist/chamber.js +21 -12
- package/dist/chamber.js.map +1 -1
- package/dist/command-bus.d.ts +11 -0
- package/dist/command-bus.d.ts.map +1 -1
- package/dist/command-bus.js +19 -5
- package/dist/command-bus.js.map +1 -1
- package/dist/directives.d.ts.map +1 -1
- package/dist/directives2.js +8 -8
- package/dist/directives2.js.map +1 -1
- package/dist/fast-lane.d.ts.map +1 -1
- package/dist/fast-lane.js +19 -4
- package/dist/fast-lane.js.map +1 -1
- package/dist/form.d.ts.map +1 -1
- package/dist/http-cache.d.ts +6 -3
- package/dist/http-cache.d.ts.map +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +8 -9
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +13 -33
- package/dist/mcp.js.map +1 -1
- package/dist/outbox.js +3 -3
- package/dist/outbox.js.map +1 -1
- package/dist/plugins-extra.d.ts.map +1 -1
- package/dist/poll-with.d.ts.map +1 -1
- package/dist/router/table.d.ts.map +1 -1
- package/dist/router/url.d.ts.map +1 -1
- package/dist/router.js +6 -3
- package/dist/router.js.map +1 -1
- package/dist/schema.d.ts +3 -34
- package/dist/schema.d.ts.map +1 -1
- package/dist/src.js +100 -41
- package/dist/src.js.map +1 -1
- package/dist/ssr.d.ts +16 -4
- package/dist/ssr.d.ts.map +1 -1
- package/dist/ssr.js +2 -2
- package/dist/ssr2.js +12 -1
- package/dist/ssr2.js.map +1 -1
- package/dist/store-base.d.ts.map +1 -1
- package/dist/store-base.js +1 -0
- package/dist/store-base.js.map +1 -1
- package/dist/stream-parser.d.ts +2 -0
- package/dist/stream-parser.d.ts.map +1 -1
- package/dist/stream-parser.js +12 -0
- package/dist/stream-parser.js.map +1 -1
- package/dist/testing.d.ts.map +1 -1
- package/dist/tool-schema.d.ts +32 -0
- package/dist/tool-schema.d.ts.map +1 -0
- package/dist/tool-schema.js +34 -0
- package/dist/tool-schema.js.map +1 -0
- package/dist/transports.d.ts.map +1 -1
- package/dist/transports2.js +13 -5
- package/dist/transports2.js.map +1 -1
- package/dist/vapor-chamber-core.iife.js +31 -3
- package/dist/vapor-chamber-core.iife.js.map +1 -1
- package/dist/vapor-chamber-core.iife.min.js +1 -1
- package/dist/vapor-chamber-elements.iife.js +31 -3
- package/dist/vapor-chamber-elements.iife.js.map +1 -1
- package/dist/vapor-chamber-elements.iife.min.js +1 -1
- package/dist/vapor-chamber.iife.js +64 -22
- package/dist/vapor-chamber.iife.js.map +1 -1
- package/dist/vapor-chamber.iife.min.js +1 -1
- package/dist/vapor.js +2 -2
- package/dist/vue.js +2 -2
- package/package.json +1 -1
- package/scripts/check-size.mjs +19 -3
- package/src/bounds.ts +12 -0
- package/src/chamber-vapor.ts +5 -2
- package/src/chamber.ts +42 -11
- package/src/command-bus.ts +40 -5
- package/src/directives.ts +9 -6
- package/src/fast-lane.ts +24 -5
- package/src/form.ts +9 -0
- package/src/http-cache.ts +14 -12
- package/src/http.ts +8 -3
- package/src/index.ts +1 -0
- package/src/mcp.ts +17 -36
- package/src/outbox.ts +3 -3
- package/src/plugins-extra.ts +72 -11
- package/src/poll-with.ts +17 -2
- package/src/router/table.ts +8 -2
- package/src/router/url.ts +2 -1
- package/src/schema.ts +15 -35
- package/src/ssr.ts +23 -4
- package/src/store-base.ts +5 -0
- package/src/stream-parser.ts +12 -0
- package/src/testing.ts +2 -1
- package/src/tool-schema.ts +53 -0
- package/src/transports.ts +24 -7
- package/dist/index.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,38 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## v1.30.0 - 2026-10-11
|
|
6
|
+
|
|
7
|
+
- Fixed the HTTP client's cache answering one request with another's response when only the headers differed. The cache key was the URL alone, so after a sign-in as another user one client served the first user's `GET /me` to the second, and a language switch got the earlier language. The key now holds the request's headers, as the dedupe key already did, and `invalidateCache` still matches by URL. A header that changes on every request, such as a trace id, makes those reads miss the cache.
|
|
8
|
+
- Added `serializeForScript` to `vapor-chamber/ssr` and the root. The SSR docs embedded dehydrated commands with `JSON.stringify` inside a `<script>`, which leaves `</script>` in a target or payload as it is, so user text could close the element and run script. `serializeForScript` escapes `<`, `>`, `&`, U+2028 and U+2029. Its output is still JSON and parses to the same value. The docs now use it.
|
|
9
|
+
- Fixed `idempotent()` cancelling a duplicate that never aborted. A concurrent duplicate joined the first dispatch's run, so the first caller's abort cancelled the handler and settled both. Each caller's abort now settles that caller alone, as the HTTP client's joined GETs already do. The run is cancelled once every caller holding it has aborted, and a caller with no signal holds it until it lands.
|
|
10
|
+
- Fixed `cache()` storing an answer that an `invalidate()` or `clear()` overtook. A read still running when the app invalidated its key stored its answer from before the write, so later reads were served it until the TTL ran out. That read still answers its own caller, but is no longer stored. The HTTP client's cache already closes this window the same way.
|
|
11
|
+
- Fixed `pollWith` ending a running job as failed when the monitor answered a 503 or 429, or any answer declaring `Retry-After`. The finished state was then never read. Those answers now wait and poll again, within `maxWait`. A verdict, an abort or a lost reply still ends the job.
|
|
12
|
+
- Fixed `createStreamParser` refusing every empty array: `[]`, `{"items":[]}` and `[1,[]]` all ended with "Unexpected end: unclosed structure", while `{}` parsed. `]` is accepted directly after `[`, and a trailing comma (`[1,]`) is still refused.
|
|
13
|
+
- Fixed `commandKey` giving every circular target the same key, and likewise every target holding a BigInt. JSON could not serialize them, and the fallback `String(target)` is `[object Object]` for all of them. So `idempotent`, `cache`, `serialize` and `supersede` collapsed two different commands into one, and the second never ran. A BigInt is now tagged in the key, and a circular target keys by its identity. A target still keys by its value otherwise: `1` and `'1'` share a key, as before. Size: the Blade consumer bundle grows 60 B brotli (8,374 -> 8,434), and the IIFEs grow raw/brotli by full +155/+5, core +141/+65 and elements +141/+68 B.
|
|
14
|
+
- Added a development warning for an action name that holds `:`. `commandKey` joins the action and the target with `:`, so `('a:b', 'c')` and `('a', 'b:c')` share a key. Escaping the name on every call measured 25-30% of a primitive key (docs/V8-RULES.md rule 3), so it is a naming rule instead. The warning is folded out of production builds and costs nothing there.
|
|
15
|
+
- Fixed `createFormBus` showing errors for values it no longer held after `reset()` during `submit()`. A reset while the async rules ran was followed by their verdict on the discarded values. When those were valid, `onSubmit` sent them. A reset now cancels a submit that has not reached `onSubmit`: its validation is dropped, `onSubmit` does not run, and `submit()` returns `false`. A submit already in `onSubmit` is unchanged.
|
|
16
|
+
- Fixed typed `int` route params past `Number.MAX_SAFE_INTEGER` being rounded to another number. `/orders/9007199254740993` reached the loader as order 9007199254740992, and `resolve()` built links to it. Such a path param no longer fits `int`, so the URL falls through to the next row, and a typed query param reads its declared default, as a malformed value does. Declare the param `string` to receive it whole.
|
|
17
|
+
- Fixed `circuitBreaker` never opening again after a failed half-open probe. Past `resetTimeout` the circuit lets one dispatch through to probe the backend. A failed probe left it half-open, which is not open, so every later dispatch went to the failing backend until one succeeded. A failed probe now opens the circuit for another `resetTimeout` and calls `onOpen` again.
|
|
18
|
+
- Fixed `rateLimit({ max: 0 })` making the outbox flush in a loop. The refusal computed `retryIn` from the oldest dispatch in the window, and an empty window has none, so `retryIn` was `NaN`. The outbox read that as a declared wait and flushed again with no delay, re-saving its queue each time (31 times in 50 ms, measured). With no slot to free, the refusal now declares no `retryIn`, and the record waits for the next flush. Size, with the fix above: full IIFE +34/+10 B raw/brotli; core and elements are unchanged.
|
|
19
|
+
- Fixed `createWsBridge` treating a replaced socket's close as its own. After `disconnect()` then `connect()`, the first socket's close event arrives late, and `connect()` had already cleared the intentional mark. With `reconnect: false` it failed the commands queued for the new socket with `lost:reply` and emptied the queue. With `reconnect: true` it set `connected` to false under an open socket and scheduled a reconnect. A replaced socket's close now only calls `onDisconnect`.
|
|
20
|
+
- Fixed the example Laravel controller (`examples/laravel-backend/VaporChamberController.php`) answering one user's command with another user's response. Its Idempotency-Key replay was keyed by command and key alone, and the `idempotent()` plugin's default key is the command itself, so two users adding the same product within the TTL sent the same key: the second user's add never ran and returned the first user's cart. The key is now scoped to the user (else the session, else the address) and hashed. Hashing also keeps a key that carries a payload within the 255-character key column of the `database` cache store, Laravel's default, where a longer key failed the insert on MySQL and PostgreSQL.
|
|
21
|
+
- Fixed the example controller answering 500 `internal_error` to the exceptions Laravel's own handler answers with a status: `abort(404)` and other HTTP exceptions (now their status and headers, so `abort(429, ..., ['Retry-After' => 30])` carries its wait), `AuthenticationException` (401 `unauthenticated`), a policy's own status (`denyAsNotFound()` now answers 404 `not_found` instead of 403, which said the resource exists), and a `ValidationException` given its own status.
|
|
22
|
+
- Updated the development dependencies within their ranges: vite 8.3.4, @vitejs/plugin-vue 6.0.10, vue-tsc 3.3.13, playwright 1.64.0, happy-dom 20.14.6, @types/node 26.6.5, and astro 7.3.8 in the Astro example. Vue stays on 3.6.0-rc.10 until its rc.11 alignment, and TypeScript on 6.0.3 until vue-tsc supports 7.
|
|
23
|
+
- Fixed `v-vc-command` leaving its control busy for good when the `v-vc-optimistic` function threw. The optimistic call ran before the `try` whose `finally` releases the control, so `vc-loading`, `aria-disabled` and the in-flight guard stayed and every later press was swallowed. A throw is now that press's failure: `vc-error`, announced, and the control is released.
|
|
24
|
+
- Fixed a store with `undo: true` keeping every undone step for its lifetime. A redo reads only the newest undone steps, and `history` drops its redo list on a new action, so older ones could never be redone, yet each held two whole states: 1,000 rounds of an action and its undo with 80 KB states held 89 MB, against 29 MB without the undos. They are now bounded like the 256-step ring. The bound runs on the undo path; an action's path is unchanged.
|
|
25
|
+
- Fixed the MCP handler giving no answer to a tool whose value JSON cannot hold (a BigInt id, a cycle). The encoding threw outside the call's `try`, so the whole reply rejected, and over stdio the client's request was never answered. It is now that call's error result, with its id.
|
|
26
|
+
- Fixed `createWsBridge` stranding queued commands behind one whose envelope JSON cannot hold (a BigInt in the payload). Queued while disconnected, it threw out of the flush on reconnect: the commands behind it were already off the queue and never sent, and `onConnect` was skipped. A command is now encoded before it is queued or sent, so such a command fails its own dispatch, as it already did on an open socket. Each envelope is still encoded once.
|
|
27
|
+
- Fixed `toTools()`, `toAnthropicTools` and `toOpenAITools` emitting a field typed `'any'` as `{ type: 'any' }`, which is not a JSON Schema type and made the whole tool schema invalid. It is now `{}`, as `busToMcpTools` already emits it. The property type in `AnthropicTool` and `OpenAITool` is now `{ type?: string }`.
|
|
28
|
+
- Fixed `createSseBridge` leaking its first stream when `install()` ran twice. The first EventSource stayed open, out of `dispose()`'s reach. A second install now replaces it.
|
|
29
|
+
- Fixed an unsubscribe on `createFastLane({ removal: 'snapshot' })` reaching other registrations. It removed every registration of the same function, and one kept from before `remove()` or `clear()` removed a later registration. It now removes its own, as the default live mode does. The emit path is unchanged.
|
|
30
|
+
- Fixed `createTestBus().inspect()` always reporting no `responderActions`. It now lists the actions `respond()` registered, as the real bus does.
|
|
31
|
+
- Fixed a declared wait that no timer can keep (`retryIn` of NaN, Infinity or below 0) being taken as one. The outbox, the async bus's retry and `pollWith` read any number as a wait, so a plugin or app failure declaring NaN or Infinity made the outbox re-flush with no wait, forever (32 saves in 50 ms), and a NaN made the bus's retry re-send at once instead of backing off. All three now apply one rule (`isDelay`: a number from 0 to the timer maximum), on their failure paths.
|
|
32
|
+
- Fixed `useCommand`, `useCommandQuery` and `useVaporAsyncCommand` with dispatches overlapping. `loading` was written per dispatch, so the first to settle set it false while another still ran: a button bound to it read idle mid-request. `useCommandQuery`'s `data` took every answer in the order it landed, so an older query answering last overwrote the latest (search-as-you-type showed the results for "ab" under "abc"). `loading` now holds while any dispatch is in flight, and `data` is the latest query's answer. `lastError` stays the last failure to arrive, so an older failure is not hidden. The counts are made at a composable's first dispatch, so creating one runs the code it ran in 1.29. Measured against 1.29 (`npm run ab`, two call lengths): creation reads no difference, and dispatch through `useCommand` reads 4.5-6.2 ns faster. A dispatch that never settles now keeps `loading` true.
|
|
33
|
+
- `toTools()`, `toAnthropicTools` and `toOpenAITools` build their schemas with the generator `busToMcpTools` uses, so the three agree. They now declare `required`: `target` and `payload` when the action has them, and each typed field (an `'any'` field stays optional). A model was told nothing was required, and the validator then refused what it left out. The output changes shape: a test that compares it exactly will see `required`.
|
|
34
|
+
- Fixed `fromToolCall` handing a handler `payload: undefined` when a tool call left out the payload its action declares. Validation skips an absent payload, which stays app code's choice on `dispatch()`; a model's input is not app code. Such a call is now checked as an empty payload, so the validator names the missing fields.
|
|
35
|
+
- Size against 1.29, raw/brotli: the full IIFE +404/+100 B, core +191/+79, elements +191/+76. The Blade consumer bundle grows 60 B brotli, all `commandKey` (above).
|
|
36
|
+
|
|
5
37
|
## v1.29.0 - 2026-10-08
|
|
6
38
|
|
|
7
39
|
- Added the root's whole surface to `vapor-chamber/vue` and `vapor-chamber/vapor`: the bus, plugins, transports and the HTTP client. A Vue app now imports from one entry, `import { createCommandBus, useCommand } from 'vapor-chamber/vue'`. They are the root's own functions, so code that imports the bus from the root is unchanged and shares the same bus.
|
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ so each action has one place to read and to test.
|
|
|
43
43
|
- **Vue <!-- vc:vueAligned -->3.6.0-rc.10<!-- /vc:vueAligned --> aligned**: signals, `onScopeDispose`, `getCurrentScope`, alien-signals internals. Tracked per release in the [CHANGELOG](CHANGELOG.md)
|
|
44
44
|
- **No runtime dependency**. `alien-signals` is an optional peer, installed only by apps that use the `vapor-chamber/alien-signals` connector. Unimported modules tree-shake to zero
|
|
45
45
|
- **ESM-only**, plus three IIFE `<script>` drop-ins for no-bundler pages
|
|
46
|
-
- **<!-- vc:covStatements -->100.0<!-- /vc:covStatements -->% coverage on all four axes** - statements, branches, functions and lines, across **<!-- vc:tests -->
|
|
46
|
+
- **<!-- vc:covStatements -->100.0<!-- /vc:covStatements -->% coverage on all four axes** - statements, branches, functions and lines, across **<!-- vc:tests -->3658<!-- /vc:tests --> tests** in <!-- vc:testFiles -->349<!-- /vc:testFiles --> files ([full table](docs/COVERAGE.md)). Every branch in the measured surface is taken by a test
|
|
47
47
|
|
|
48
48
|
## Contents
|
|
49
49
|
|
|
@@ -250,7 +250,7 @@ as its own chunk, the first time it renders one.
|
|
|
250
250
|
**A pure-Vapor app can skip the vDOM renderer entirely** (experimental, v1.x).
|
|
251
251
|
`vapor-chamber/router/vapor` exports the same `RouterOutlet` name built from Vapor's own helpers,
|
|
252
252
|
so rendering a route needs no `vaporInteropPlugin`. Take the startup chunk of a Vite production
|
|
253
|
-
build. It comes out **<!-- vc:outletSaving -->22.
|
|
253
|
+
build. It comes out **<!-- vc:outletSaving -->22.08<!-- /vc:outletSaving --> KB brotli / <!-- vc:outletSavingRaw -->70.4<!-- /vc:outletSavingRaw --> KB raw** smaller than the same
|
|
254
254
|
app rendering through the vDOM outlet plus interop. The same harness derives that baseline
|
|
255
255
|
(`tests/vapor/vapor-outlet-size.test.ts`).
|
|
256
256
|
|
|
@@ -1402,7 +1402,7 @@ Three `<script>`-tag drop-ins. Pick by audience, not feature checklist.
|
|
|
1402
1402
|
|---|---|--:|--:|--:|
|
|
1403
1403
|
| **core** | Sprinkled JS on server-rendered pages (Blade, Rails, Django, WordPress). You dispatch user actions to a backend over HTTP. | <!-- vc:sizeIifeCoreRaw -->32.4<!-- /vc:sizeIifeCoreRaw --> KB | <!-- vc:sizeIifeCore -->10.2<!-- /vc:sizeIifeCore --> KB | <!-- vc:sizeIifeCoreGzip -->11.2<!-- /vc:sizeIifeCoreGzip --> KB |
|
|
1404
1404
|
| **elements** | Embeddable widgets (chat bubbles, checkout buttons, third-party drop-ins). You ship a `<vc-widget>` custom element. | <!-- vc:sizeIifeElementsRaw -->34.0<!-- /vc:sizeIifeElementsRaw --> KB | <!-- vc:sizeIifeElements -->10.7<!-- /vc:sizeIifeElements --> KB | <!-- vc:sizeIifeElementsGzip -->11.7<!-- /vc:sizeIifeElementsGzip --> KB |
|
|
1405
|
-
| **full** | SPAs that grew big enough to want everything (realtime, undo/redo, persistence, full Vapor surface). | <!-- vc:sizeIifeFullRaw -->46.9<!-- /vc:sizeIifeFullRaw --> KB | <!-- vc:sizeIifeFull -->14.7<!-- /vc:sizeIifeFull --> KB | <!-- vc:sizeIifeFullGzip -->16.
|
|
1405
|
+
| **full** | SPAs that grew big enough to want everything (realtime, undo/redo, persistence, full Vapor surface). | <!-- vc:sizeIifeFullRaw -->46.9<!-- /vc:sizeIifeFullRaw --> KB | <!-- vc:sizeIifeFull -->14.7<!-- /vc:sizeIifeFull --> KB | <!-- vc:sizeIifeFullGzip -->16.1<!-- /vc:sizeIifeFullGzip --> KB |
|
|
1406
1406
|
|
|
1407
1407
|
<details>
|
|
1408
1408
|
<summary><b>What's in each variant</b>, plus drop-in examples</summary>
|
|
@@ -1521,8 +1521,8 @@ Everything else is optional and tree-shaken when unimported.
|
|
|
1521
1521
|
form.ts, schema.ts, devtools.ts, directives.ts, vite-hmr.ts
|
|
1522
1522
|
```
|
|
1523
1523
|
|
|
1524
|
-
**Coverage:** <!-- vc:covStatements -->100.0<!-- /vc:covStatements -->% statements, <!-- vc:covBranches -->100.0<!-- /vc:covBranches -->% branches, <!-- vc:covFunctions -->100.0<!-- /vc:covFunctions -->% functions, <!-- vc:covLines -->100.0<!-- /vc:covLines -->% lines across **<!-- vc:tests -->
|
|
1525
|
-
(<!-- vc:testFiles -->
|
|
1524
|
+
**Coverage:** <!-- vc:covStatements -->100.0<!-- /vc:covStatements -->% statements, <!-- vc:covBranches -->100.0<!-- /vc:covBranches -->% branches, <!-- vc:covFunctions -->100.0<!-- /vc:covFunctions -->% functions, <!-- vc:covLines -->100.0<!-- /vc:covLines -->% lines across **<!-- vc:tests -->3658<!-- /vc:tests --> tests**
|
|
1525
|
+
(<!-- vc:testFiles -->349<!-- /vc:testFiles --> files). Per-file table:
|
|
1526
1526
|
[docs/COVERAGE.md](docs/COVERAGE.md). Run `npm run test:coverage` for live numbers.
|
|
1527
1527
|
|
|
1528
1528
|
## Testing
|
package/ROADMAP.md
CHANGED
|
@@ -543,7 +543,7 @@ What this file still owns, because the whitepaper's appendix B does not:
|
|
|
543
543
|
| v1.13.0 | rc.3 alignment | Tracking bump + docs: `configureVue()` promoted from no-bundler escape hatch to the recommended deterministic Vapor wiring for all consumers. No API change. |
|
|
544
544
|
| v1.16.0 | rc.5 alignment | Tracking bump, plus one real contract change: the transition bridge's `phase` / `dispose` became **non-enumerable**, so `{ ...bridge }` no longer carries them. Direct access and destructuring are unaffected. The change exists because `v-bind="t"`, the documented binding, was spreading both into the DOM as attributes. |
|
|
545
545
|
| v1.17.0 | rc.6 alignment | Two measured perf wins (wildcard fan-out 1.14-1.32x, router active-link stamping 1.77-1.86x), both 0 B. **New subpath `vapor-chamber/vapor`** (no new capability: a build-time wiring channel replacing the probe, wired set measured, custom-element/interop opt-in). Plus one real fix: `vapor-chamber/vue` never passed `hasInjectionContext`. So in a **production bundle** (probe dead) the rc.4 KeepAlive guard fell back to `getCurrentInstance()` and went inert, and `useCommandHistory`/`useCommandError` recorded commands dispatched into a deactivated view. Also: wildcard listeners match on a prefix computed at `on()` time (1.14-1.32x on wildcard fan-out, 0 B brotli, `'*'` unaffected). `configureVue()` documented as MERGING, so a Vapor app adds one name rather than re-enumerating eight. And one roadmap fact corrected: all four wrapped Vapor APIs are statically importable from `vue`, and were at rc.5. |
|
|
546
|
-
| v1.18.0 | rc.6 window | **New subpath `vapor-chamber/router/vapor`**: a Vapor-native `RouterOutlet`, experimental. A genuinely new render surface, so it overrides the "new capability parks until after 3.6 stable" posture deliberately and on evidence, per the maturation posture under "Posture" above. The RC window is the runway, and an outlet built over N alignment cycles arrives at stable already hardened. Gated on a measured number before any of it was built: **<!-- vc:outletSaving -->22.
|
|
546
|
+
| v1.18.0 | rc.6 window | **New subpath `vapor-chamber/router/vapor`**: a Vapor-native `RouterOutlet`, experimental. A genuinely new render surface, so it overrides the "new capability parks until after 3.6 stable" posture deliberately and on evidence, per the maturation posture under "Posture" above. The RC window is the runway, and an outlet built over N alignment cycles arrives at stable already hardened. Gated on a measured number before any of it was built: **<!-- vc:outletSaving -->22.08<!-- /vc:outletSaving --> KB brotli / <!-- vc:outletSavingRaw -->70.4<!-- /vc:outletSavingRaw --> KB raw** saved versus the same app rendering through the vDOM outlet plus interop, re-derived per run from a Vite production build by `tests/vapor/vapor-outlet-size.test.ts`. The guard holds two limits: the saving stays >= <!-- vc:outletFloor -->15.0<!-- /vc:outletFloor --> KB, and the Vapor outlet's own machinery over a router-without-outlet floor stays <= <!-- vc:outletOwnArmCeiling -->5.0<!-- /vc:outletOwnArmCeiling --> KB (**measured <!-- vc:outletOwnArm -->4.71<!-- /vc:outletOwnArm --> KB**). It is written to fail loudly if a later RC erodes either, and that failure is a decision trigger, not a threshold to raise. (Until the rc.8 cycle it was one esbuild-measured 19.5 KB bar on the difference, retired after firing twice on improvements: see the whitepaper's rc.8 row.) Contract changes: one additive `RouterErrorCode` (`mode_mismatch`), and route components on this outlet must be `defineVaporComponent` output. Blade rows still need the vDOM outlet. **Plus one BREAKING change to `vapor-chamber/router`**: it no longer builds an http client. So a `{ url }` route table now needs `http` and blade rows need `fetchBlade`, and `routerHttp()` and `bladeFetcher()` ship as the new `vapor-chamber/router/remote` subpath. Two lines for the affected setups, nothing for a generated table with no blade rows. That is the primary setup, and it was paying 3.4 KB brotli for features it never called. Taken in the RC window on the same maturation logic as the outlet above: near-zero adoption now, and the cost of the break only rises. `./router` drops 12.4 -> 9.6 KB brotli. Three more additive codes: `redirect_loop`, `no_router`, `http_unconfigured`. **Plus a new subpath `vapor-chamber/store`**: `defineChamberStore`, experimental, <!-- vc:sizeStore -->2.2<!-- /vc:sizeStore --> KB brotli, importing `vue` and nothing else. Store actions are commands, so `persist`, `history`, `sync`, `optimistic`, `idempotent`, `serialize` and the devtools timeline apply to store state with no store-specific code. URL-worthy fields delegate to the router rather than mirroring it. Lands with a deliberate reversal of whitepaper section 6 recorded in that section, for PACKAGE scope only: the bus still stores no state. No contract change for anyone not importing it. |
|
|
547
547
|
| v1.19.0 | rc.7 alignment | Tracking bump (peer `>=3.6.0-rc.7`). All 50 rc.7 commits read at source, every one pass-through. The cycle carried code anyway, because it also read every module in `src`: twenty-three defects, each reproduced before it was fixed. The largest class: five shipped plugins (`logger`, `history`, `circuitBreaker`, `metrics`, `persist`) read `next()`'s result as a `CommandResult`. On the async bus that is a Promise, so each took the wrong branch in silence. Now one shared rule, `src/settled.ts`. Numeric options that failed open on a bad value (NaN) now fall back to their documented default. Contract changes, all small: a second form on one bus now throws instead of taking the first one over (forms take an `id` prefix, default `'form'`, so a single form dispatches the names it always did). One additive `RouterErrorCode`, `cyclic_parent`. `configureVue` added to the `full` and `elements` IIFE namespaces. And the ESM build's DEV flag now folds in a consumer's production build, so dev-only diagnostic strings stop shipping: 14,037 -> 12,764 B raw and 4,453 -> 3,999 B brotli on a real Vite app build, about 10%. That held for one-chunk apps. In a code-split app, a chunk that imported DEV kept its strings until v1.20.0 derived DEV per module. |
|
|
548
548
|
| v1.20.0 | rc.8 alignment | Tracking bump (peer `>=3.6.0-rc.8`), plus one contract NARROWING, taken as a minor under the pre-stable policy below. `retry()`'s default no longer retries an error carrying an HTTP status unless it is 408, 429 or 5xx. So `retry()` stacked in front of the HTTP bridge stops re-sending a 422. A handler that throws a 4xx-status error and relied on `retry()` re-running it passes its own `isRetryable`. Additive: `classifyError` and `isRetryableStatus` exported from the root. `vaporChamberWire()` from `vapor-chamber/vite`, a build-only Vite plugin that wires Vue into an app importing the root, with no import changed. `vcCommandVapor` from `vapor-chamber/directives`, `v-vc:command` for Vapor components (the plugin's install-time Vapor warning is gone). Also in this release: `useSharedCommandState().isLoading(action, target?)`. A plugin's throw is a `plugin:failed:plugin` result, and a sealed bus refuses `clear()`. `dispose()` settles waiting `request()`s, and the sync `request()` honours its signal. A before-hook's throw is a `core:refused:hook` result, and `history()` records an async redo once. The ESM build's DEV is derived per module. Sizes measured as they ship (Vite production builds, about 10% lower). `alien-signals` is an optional peer. `dispose()` runs each plugin's `dispose()`, and `retry()` has one. What an undo handler or a redo dispatches carries origin `undo` / `redo` and is never recorded. The TestBus's `request()`/`respond()` are real. |
|
|
549
549
|
| v2.0.0 | One minor cycle after 3.6 stable | Stable-landing realignment: finalize the identity decision (Vapor-first vs bus-first). The `vue36` flavor + registry collapse were withdrawn at rc.3 (superseded by `configureVue()`, under 0.9 KB at stake: see "What is transitional"). `useVaporCommand`->`useCommand` shipped early in v1.7.0. See the checklist below. |
|
package/dist/LICENSE.txt
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
vapor-chamber v1.
|
|
1
|
+
vapor-chamber v1.30.0 - licence information for the distributed JavaScript
|
|
2
2
|
==============================================================================
|
|
3
3
|
|
|
4
4
|
Every JavaScript file in this directory carries the one-line reference
|
|
5
5
|
"/*! See LICENSE.txt */". This file is that reference.
|
|
6
6
|
|
|
7
|
-
These
|
|
7
|
+
These 63 files:
|
|
8
8
|
|
|
9
9
|
a11y.js
|
|
10
10
|
alien-signals.js
|
|
@@ -51,6 +51,7 @@ These 62 files:
|
|
|
51
51
|
store.js
|
|
52
52
|
store/core.js
|
|
53
53
|
stream-parser.js
|
|
54
|
+
tool-schema.js
|
|
54
55
|
transitions.js
|
|
55
56
|
transitions/vapor.js
|
|
56
57
|
transitions2.js
|
package/dist/bounds.d.ts
CHANGED
|
@@ -67,4 +67,13 @@ export declare const MAX_TIMEOUT_MS = 2147483647;
|
|
|
67
67
|
* Delays pass `max: MAX_TIMEOUT_MS`.
|
|
68
68
|
*/
|
|
69
69
|
export declare function countOption(value: unknown, fallback: number, min?: number, max?: number): number;
|
|
70
|
+
/**
|
|
71
|
+
* True for a wait a timer can keep: a number from 0 to MAX_TIMEOUT_MS. The
|
|
72
|
+
* rule every reader of a declared `retryIn` applies (the async bus's retry,
|
|
73
|
+
* the outbox, pollWith), so the wait is checked where it is read, whoever
|
|
74
|
+
* declared it. `typeof === 'number'` let NaN and Infinity through: a timer
|
|
75
|
+
* fires at once on both, and the outbox re-flushed in a loop on either
|
|
76
|
+
* (tests/retry-in-unusable.test.ts).
|
|
77
|
+
*/
|
|
78
|
+
export declare function isDelay(value: unknown): value is number;
|
|
70
79
|
//# sourceMappingURL=bounds.d.ts.map
|
package/dist/bounds.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounds.d.ts","sourceRoot":"","sources":["../src/bounds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH;wEACwE;AACxE,eAAO,MAAM,cAAc,aAAgB,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAI,EAAE,GAAG,SAA2B,GAAG,MAAM,CAG7G"}
|
|
1
|
+
{"version":3,"file":"bounds.d.ts","sourceRoot":"","sources":["../src/bounds.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH;wEACwE;AACxE,eAAO,MAAM,cAAc,aAAgB,CAAC;AAE5C;;;;;;;;;;;;GAYG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,SAAI,EAAE,GAAG,SAA2B,GAAG,MAAM,CAG7G;AAED;;;;;;;GAOG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAEvD"}
|
package/dist/bounds.js
CHANGED
|
@@ -4,4 +4,7 @@ function countOption(value, fallback, min = 0, max = Number.POSITIVE_INFINITY) {
|
|
|
4
4
|
const n = Number(value);
|
|
5
5
|
return Number.isNaN(n) ? fallback : Math.min(max, Math.max(min, Math.trunc(n)));
|
|
6
6
|
}
|
|
7
|
-
|
|
7
|
+
function isDelay(value) {
|
|
8
|
+
return typeof value === "number" && value >= 0 && value <= 2147483647;
|
|
9
|
+
}
|
|
10
|
+
export { countOption as n, isDelay as r, MAX_TIMEOUT_MS as t };
|
package/dist/bounds.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bounds.js","names":[],"sources":["../src/bounds.ts"],"sourcesContent":["/**\n * vapor-chamber - the one rule for a numeric option.\n *\n * Internal. Every cap in this library is a number a caller supplies, and a\n * caller's number is frequently not one: `Number(localStorage.getItem(...))`, a\n * parsed env var, a JSON field, a form value. The failure is never loud.\n *\n * COMPARISON DIRECTION IS THE WHOLE STORY, and it decides which way a bad value\n * fails. Every comparison against NaN is false, so:\n *\n * length < max gate on RECORDING NaN -> records nothing FAILS SAFE\n * length > max gate on EVICTING NaN -> evicts nothing FAILS OPEN\n * count >= max gate on REFUSING NaN -> refuses nothing FAILS OPEN\n *\n * Two of the three are unbounded growth or a defeated guard, and they are the\n * two that are idiomatic to write. Measured through the public API before this\n * module existed: `history({ maxSize: NaN })` kept 500 entries against a cap of\n * 50, `metrics({ maxSize: NaN })` kept 1500 against 1000,\n * `circuitBreaker({ threshold: NaN })` sat closed through 20 straight failures,\n * and `StreamParser({ maxDepth: NaN })` accepted 5,000 levels of nesting\n * without once firing the depth guard that exists to stop exactly that.\n *\n * WHY A MODULE AND NOT `Math.max(0, raw | 0)` AT EACH SITE. That idiom was\n * already applied by hand at five sites, and this sweep found seven more it had\n * never reached - the same shape as the arrow sweep that left 48 arrows in 15\n * files and the glyph sweep that left four in shipped strings. A rule spelled\n * out at each call site is a rule that is missing wherever nobody remembered it.\n *\n * It is also subtly wrong twice over. `Infinity | 0` is 0, so those five sites\n * turned \"no bound\" into \"store nothing\" - the opposite of the request. And\n * `| 0` truncates to 32 bits, so a cap of 3_000_000_000 silently went negative.\n * Both disappear here: `Math.trunc(Infinity)` is `Infinity` and `Math.min`\n * carries it through, so the unbounded case needs no branch of its own.\n *\n * ONE SITE DELIBERATELY DOES NOT USE THIS, and saying so here is the point of\n * saying it at all - an exception nobody records is just a gap. `command-bus.ts`\n * keeps its inline `| 0` for `maxBufferSize`, because importing this module into\n * the one file every consumer pulls costs 50 B brotli in the minimal Blade\n * bundle `esm-treeshake.test.ts` gates, which breaches that ceiling on its own.\n * `| 0` already handles the NaN case there; what it gives up is `Infinity`,\n * recorded at that call site.\n *\n * WHY A BAD OPTION FALLS BACK TO THE DEFAULT rather than to zero. The five\n * hand-clamped sites folded NaN to 0 on the argument that storing nothing is\n * \"bounded, and loud rather than silent\". Written out per site, that argument\n * collapses: an outbox that queues nothing DISCARDS the commands it exists to\n * hold; `idempotent` that remembers nothing DOUBLE-EXECUTES commands declared\n * idempotent; a transition that times out in 1ms removes the animation instead\n * of the hang. None of those is louder than the unbounded failure - only\n * costlier. A NaN option is indistinguishable in intent from a missing one (it\n * is what a failed config read produces), so it now behaves like a missing one.\n */\n\n/** The largest delay `setTimeout` accepts. Past it a timer fires IMMEDIATELY\n * rather than never, turning a long backoff into no backoff at all. */\nexport const MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Normalize a caller-supplied count, size, depth, attempt cap or delay.\n *\n * - NaN, or anything that is not a number -> `fallback`, the documented default\n * - below `min` -> `min`, which catches negatives and -Infinity together\n * - above `max` -> `max`; left at `Infinity`, \"no bound\" passes through intact\n * - fractional -> truncated toward zero\n *\n * `min` defaults to 0 because a cap of zero is meaningful (store nothing) and\n * distinct from a missing option. Sites where zero is nonsense - an attempt\n * count, a line length, a circuit threshold - pass `min: 1` and say why.\n * Delays pass `max: MAX_TIMEOUT_MS`.\n */\nexport function countOption(value: unknown, fallback: number, min = 0, max = Number.POSITIVE_INFINITY): number {\n const n = Number(value);\n return Number.isNaN(n) ? fallback : Math.min(max, Math.max(min, Math.trunc(n)));\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,IAAa,iBAAiB;;;;;;;;;;;;;;AAe9B,SAAgB,YAAY,OAAgB,UAAkB,MAAM,GAAG,MAAM,OAAO,mBAA2B;CAC7G,MAAM,IAAI,OAAO,KAAK;CACtB,OAAO,OAAO,MAAM,CAAC,IAAI,WAAW,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC;AAChF"}
|
|
1
|
+
{"version":3,"file":"bounds.js","names":[],"sources":["../src/bounds.ts"],"sourcesContent":["/**\n * vapor-chamber - the one rule for a numeric option.\n *\n * Internal. Every cap in this library is a number a caller supplies, and a\n * caller's number is frequently not one: `Number(localStorage.getItem(...))`, a\n * parsed env var, a JSON field, a form value. The failure is never loud.\n *\n * COMPARISON DIRECTION IS THE WHOLE STORY, and it decides which way a bad value\n * fails. Every comparison against NaN is false, so:\n *\n * length < max gate on RECORDING NaN -> records nothing FAILS SAFE\n * length > max gate on EVICTING NaN -> evicts nothing FAILS OPEN\n * count >= max gate on REFUSING NaN -> refuses nothing FAILS OPEN\n *\n * Two of the three are unbounded growth or a defeated guard, and they are the\n * two that are idiomatic to write. Measured through the public API before this\n * module existed: `history({ maxSize: NaN })` kept 500 entries against a cap of\n * 50, `metrics({ maxSize: NaN })` kept 1500 against 1000,\n * `circuitBreaker({ threshold: NaN })` sat closed through 20 straight failures,\n * and `StreamParser({ maxDepth: NaN })` accepted 5,000 levels of nesting\n * without once firing the depth guard that exists to stop exactly that.\n *\n * WHY A MODULE AND NOT `Math.max(0, raw | 0)` AT EACH SITE. That idiom was\n * already applied by hand at five sites, and this sweep found seven more it had\n * never reached - the same shape as the arrow sweep that left 48 arrows in 15\n * files and the glyph sweep that left four in shipped strings. A rule spelled\n * out at each call site is a rule that is missing wherever nobody remembered it.\n *\n * It is also subtly wrong twice over. `Infinity | 0` is 0, so those five sites\n * turned \"no bound\" into \"store nothing\" - the opposite of the request. And\n * `| 0` truncates to 32 bits, so a cap of 3_000_000_000 silently went negative.\n * Both disappear here: `Math.trunc(Infinity)` is `Infinity` and `Math.min`\n * carries it through, so the unbounded case needs no branch of its own.\n *\n * ONE SITE DELIBERATELY DOES NOT USE THIS, and saying so here is the point of\n * saying it at all - an exception nobody records is just a gap. `command-bus.ts`\n * keeps its inline `| 0` for `maxBufferSize`, because importing this module into\n * the one file every consumer pulls costs 50 B brotli in the minimal Blade\n * bundle `esm-treeshake.test.ts` gates, which breaches that ceiling on its own.\n * `| 0` already handles the NaN case there; what it gives up is `Infinity`,\n * recorded at that call site.\n *\n * WHY A BAD OPTION FALLS BACK TO THE DEFAULT rather than to zero. The five\n * hand-clamped sites folded NaN to 0 on the argument that storing nothing is\n * \"bounded, and loud rather than silent\". Written out per site, that argument\n * collapses: an outbox that queues nothing DISCARDS the commands it exists to\n * hold; `idempotent` that remembers nothing DOUBLE-EXECUTES commands declared\n * idempotent; a transition that times out in 1ms removes the animation instead\n * of the hang. None of those is louder than the unbounded failure - only\n * costlier. A NaN option is indistinguishable in intent from a missing one (it\n * is what a failed config read produces), so it now behaves like a missing one.\n */\n\n/** The largest delay `setTimeout` accepts. Past it a timer fires IMMEDIATELY\n * rather than never, turning a long backoff into no backoff at all. */\nexport const MAX_TIMEOUT_MS = 2_147_483_647;\n\n/**\n * Normalize a caller-supplied count, size, depth, attempt cap or delay.\n *\n * - NaN, or anything that is not a number -> `fallback`, the documented default\n * - below `min` -> `min`, which catches negatives and -Infinity together\n * - above `max` -> `max`; left at `Infinity`, \"no bound\" passes through intact\n * - fractional -> truncated toward zero\n *\n * `min` defaults to 0 because a cap of zero is meaningful (store nothing) and\n * distinct from a missing option. Sites where zero is nonsense - an attempt\n * count, a line length, a circuit threshold - pass `min: 1` and say why.\n * Delays pass `max: MAX_TIMEOUT_MS`.\n */\nexport function countOption(value: unknown, fallback: number, min = 0, max = Number.POSITIVE_INFINITY): number {\n const n = Number(value);\n return Number.isNaN(n) ? fallback : Math.min(max, Math.max(min, Math.trunc(n)));\n}\n\n/**\n * True for a wait a timer can keep: a number from 0 to MAX_TIMEOUT_MS. The\n * rule every reader of a declared `retryIn` applies (the async bus's retry,\n * the outbox, pollWith), so the wait is checked where it is read, whoever\n * declared it. `typeof === 'number'` let NaN and Infinity through: a timer\n * fires at once on both, and the outbox re-flushed in a loop on either\n * (tests/retry-in-unusable.test.ts).\n */\nexport function isDelay(value: unknown): value is number {\n return typeof value === 'number' && value >= 0 && value <= MAX_TIMEOUT_MS;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,IAAa,iBAAiB;;;;;;;;;;;;;;AAe9B,SAAgB,YAAY,OAAgB,UAAkB,MAAM,GAAG,MAAM,OAAO,mBAA2B;CAC7G,MAAM,IAAI,OAAO,KAAK;CACtB,OAAO,OAAO,MAAM,CAAC,IAAI,WAAW,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC;AAChF;;;;;;;;;AAUA,SAAgB,QAAQ,OAAiC;CACvD,OAAO,OAAO,UAAU,YAAY,SAAS,KAAK,SAAA;AACpD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chamber-vapor.d.ts","sourceRoot":"","sources":["../src/chamber-vapor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAaH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGlG,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAyBzE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAG,GAAG,EAC9C,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,IAAI,CAaN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,IAAI,GAAG,GAAG,IAAI,CAElD;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAOlG;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAOvE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,GAAG,GAAG,EAC/C,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,MAAM,GACxC,CAAC,GAAG,IAAI,CAOV;AAMD,mFAAmF;AACnF,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AAE9F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,GAAG,UAAU,EAC/D,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC;uBAcN,GAAG,YAAY,GAAG,KAAG,QAAQ,CAAC,CAAC,CAAC;;EAS3D;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE;IAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;CAAE;
|
|
1
|
+
{"version":3,"file":"chamber-vapor.d.ts","sourceRoot":"","sources":["../src/chamber-vapor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAaH,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGlG,OAAO,EAAc,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAyBzE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,GAAG,GAAG,EAC9C,aAAa,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAClC,IAAI,CAaN;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,qBAAqB,IAAI,GAAG,GAAG,IAAI,CAElD;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,wBAAwB,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAOlG;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,oBAAoB,CAAC,CAAC,GAAG,GAAG,EAAE,OAAO,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,CAOvE;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,yBAAyB,CAAC,CAAC,GAAG,GAAG,EAC/C,MAAM,EAAE,CAAC,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,MAAM,GACxC,CAAC,GAAG,IAAI,CAOV;AAMD,mFAAmF;AACnF,MAAM,MAAM,mBAAmB,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,eAAe,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;AAE9F;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,GAAG,UAAU,EAC/D,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC;uBAcN,GAAG,YAAY,GAAG,KAAG,QAAQ,CAAC,CAAC,CAAC;;EAS3D;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE;IAAE,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,KAAK,OAAO,CAAC,aAAa,CAAC,CAAA;CAAE;uBAalG,MAAM,UAAU,GAAG,YAAY,GAAG,KAAG,OAAO,CAAC,aAAa,CAAC;;;;EAgC5F"}
|
package/dist/chamber-vapor.js
CHANGED
|
@@ -58,9 +58,10 @@ function useVaporAsyncCommand(asyncBus) {
|
|
|
58
58
|
const bus = resolveBus(asyncBus);
|
|
59
59
|
const loading = signal(false);
|
|
60
60
|
const lastError = signal(null);
|
|
61
|
+
let n = 0;
|
|
61
62
|
async function dispatch(action, target, payload) {
|
|
62
63
|
try {
|
|
63
|
-
loading.value = true;
|
|
64
|
+
if (n++ === 0) loading.value = true;
|
|
64
65
|
lastError.value = null;
|
|
65
66
|
const result = await untracked(() => bus.dispatch(action, target, payload));
|
|
66
67
|
if (!result.ok) lastError.value = result.error ?? null;
|
|
@@ -70,7 +71,7 @@ function useVaporAsyncCommand(asyncBus) {
|
|
|
70
71
|
lastError.value = error;
|
|
71
72
|
return errResult(error);
|
|
72
73
|
} finally {
|
|
73
|
-
loading.value = false;
|
|
74
|
+
if (--n === 0) loading.value = false;
|
|
74
75
|
}
|
|
75
76
|
}
|
|
76
77
|
function dispose() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chamber-vapor.js","names":["_errResult"],"sources":["../src/chamber-vapor.ts"],"sourcesContent":["/**\n * vapor-chamber - Vue 3.6+ Vapor-specific API\n *\n * This file renders nothing: it forwards Vue's own define* functions, so\n * rendering-side work (attrs fallthrough, interop, hydration) lands below it.\n * The wrappers and createVaporChamberApp take an opt-in return generic\n * (`<T = any>`) and `object`-typed options: importing Vue's Vapor types here\n * would put a hard `vue` type dependency on the main barrel and break Vue-less\n * command-bus consumers, so callers opt in: `defineVaporComponent<MyComp>(opts)`.\n *\n * Separated from chamber.ts to keep the core composable module CDCC-compliant.\n */\n\nimport {\n signal,\n tryAutoCleanup,\n untracked,\n getVaporAppFn,\n getVaporInteropRef,\n getDefineVaporCustomElementFn,\n getDefineVaporComponentFn,\n getDefineVaporAsyncComponentFn,\n vueDetectionHint,\n} from './chamber';\nimport type { BaseBus, CommandBus, Handler, RegisterOptions, CommandResult } from './command-bus';\nimport { _errResult } from './command-bus';\nimport { DEV } from './dev';\nimport { resolveBus, type BusOption, type ResultOn } from './shared-bus';\n\n/**\n * Why the `defineVapor*` wrappers warn instead of just returning null.\n *\n * Each returns `null` when the Vapor runtime is absent, and callers are\n * documented to check. But a bare `null` is the same failure shape that hid a\n * real bug for several releases: `tryKeepAliveHooks` guarded on an accessor\n * that silently answers \"no\" under Vapor, so a feature was inert with nothing\n * to see (rc.4 cycle - see tests/keepalive-input-scope-fixture.test.ts). Silent\n * negatives are how detection bugs survive.\n *\n * So the null stays - throwing would break the documented contract and the\n * tests that assert it - but it is not quiet. The call sites gate on\n * `DEV`, so the whole call (and every byte of message text) folds out of the\n * production IIFE builds; this helper is then unreferenced and tree-shaken.\n */\nfunction devWarnNoVapor(api: string): void {\n console.warn(\n `[vapor-chamber] ${api}() returned null - Vue 3.6+ with Vapor mode was not detected, ` +\n `so the component was NOT created. ${vueDetectionHint()} ` +\n 'For VDOM mode use the matching define* helper from vue instead.',\n );\n}\n\n/**\n * Create a Vapor app instance with vapor-chamber ready.\n * Requires Vue 3.6+. Throws if Vapor is not available.\n *\n * Pass-through over Vue's createVaporApp - scope-ID handling, HMR app-instance\n * refresh, and setup() error recovery are Vue runtime behavior (per-beta detail:\n * CHANGELOG / whitepaper alignment log).\n *\n * `rootProps` are Vue's raw props, passed through unchanged: a plain value is\n * the value, and a FUNCTION is a getter that Vue calls for the value. So a\n * callback is wrapped, `{ onSave: () => onSave }`; passed bare it is called\n * and the component receives what it returned. (A function set on a\n * `defineWidget` element property is the opposite: it arrives as itself.)\n * Pinned by tests/vapor/vapor-subpath-wiring.test.ts.\n *\n * @example\n * import { createVaporChamberApp } from 'vapor-chamber';\n * import App from './App.vue';\n * createVaporChamberApp(App).mount('#app');\n *\n * @example Root props\n * createVaporChamberApp(App, { title: 'Cart', onSave: () => onSave }).mount('#app');\n */\nexport function createVaporChamberApp<TApp = any>(\n rootComponent: object,\n rootProps?: Record<string, unknown>,\n): TApp {\n const fn = getVaporAppFn();\n if (!fn) {\n // The bare \"Vapor required\" message was the same whether Vue was absent,\n // present-without-Vapor, or present-but-unreachable - three different\n // problems with three different fixes. vueDetectionHint() names which.\n throw new Error(\n // The diagnosis ships (vueDetectionHint says why); the advice is DEV's.\n `[vapor-chamber] Vue 3.6+ with Vapor mode required. ${vueDetectionHint()}` +\n (DEV ? ' For VDOM mode, use createApp() from vue instead.' : '')\n );\n }\n return fn(rootComponent, rootProps) as TApp;\n}\n\n/**\n * Returns the vaporInteropPlugin if available (Vue 3.6+).\n * Use this to enable mixed Vapor/VDOM component trees.\n *\n * Pass-through over Vue's plugin. The returned reference is safe to hold\n * across HMR cycles (Vue does not mutate the bridge).\n *\n * @example\n * import { createApp } from 'vue';\n * import { getVaporInteropPlugin } from 'vapor-chamber';\n * const plugin = getVaporInteropPlugin();\n * if (plugin) createApp(App).use(plugin).mount('#app');\n */\nexport function getVaporInteropPlugin(): any | null {\n return getVaporInteropRef();\n}\n\n// ---------------------------------------------------------------------------\n// Vapor Custom Elements (Vue 3.6 - defineVaporCustomElement introduced in 3.6.0-alpha.4, #14017)\n// ---------------------------------------------------------------------------\n\n/**\n * defineVaporCustomElement - create a custom element backed by Vapor rendering.\n *\n * Wraps Vue's `defineVaporCustomElement()` (introduced in 3.6.0-alpha.4, #14017). The generated custom\n * element uses Vapor's compiler-optimized rendering instead of the VDOM, giving\n * zero-overhead DOM updates inside shadow DOM. Safe to call with a reused\n * options object, and children re-render on reactive prop changes (beta.14+;\n * per-beta detail: CHANGELOG / whitepaper alignment log).\n *\n * Returns null if the Vapor runtime (Vue 3.6+) is not detected - check before calling\n * `customElements.define()`.\n *\n * @example\n * import { defineVaporCustomElement } from 'vapor-chamber';\n * const MyEl = defineVaporCustomElement({\n * props: { label: String },\n * // A Vapor setup() returns a BLOCK - real DOM nodes - not a render\n * // function or h() output. An SFC's compiler builds this from its template.\n * setup(props) {\n * const span = document.createElement('span');\n * span.textContent = String(props.label);\n * return span;\n * }\n * });\n * if (MyEl) customElements.define('vc-greeting', MyEl);\n */\nexport function defineVaporCustomElement<T = any>(options: object, extraOptions?: object): T | null {\n const fn = getDefineVaporCustomElementFn();\n if (!fn) {\n if (DEV) devWarnNoVapor(\"defineVaporCustomElement\");\n return null;\n }\n return (extraOptions !== undefined ? fn(options, extraOptions) : fn(options)) as T;\n}\n\n/**\n * defineVaporComponent - define a Vapor component with proper type inference.\n *\n * Wraps Vue's `defineVaporComponent()` (typed since 3.6.0-alpha.5, #13831). Use this to get full\n * TypeScript inference for props, emits, and slots in Vapor components.\n * Pass-through - emits/$attrs routing, v-once interop, scope IDs, and the\n * compiler optimizations are Vue behavior (per-beta detail: CHANGELOG /\n * whitepaper alignment log).\n *\n * Returns null if the Vapor runtime (Vue 3.6+) is not detected.\n *\n * @example\n * import { defineVaporComponent } from 'vapor-chamber';\n * const Comp = defineVaporComponent({\n * props: { count: Number },\n * emits: ['change'],\n * // Returns DOM nodes (a block), not h(): see defineVaporCustomElement above.\n * setup(props) {\n * const div = document.createElement('div');\n * div.textContent = `Count: ${props.count}`;\n * return div;\n * }\n * });\n */\nexport function defineVaporComponent<T = any>(options: object): T | null {\n const fn = getDefineVaporComponentFn();\n if (!fn) {\n if (DEV) devWarnNoVapor(\"defineVaporComponent\");\n return null;\n }\n return fn(options) as T;\n}\n\n/**\n * defineVaporAsyncComponent - define an async Vapor component for lazy loading.\n *\n * Wraps Vue's `defineVaporAsyncComponent()` (introduced in 3.6.0-alpha.3, #13059). Async Vapor\n * components are cached by VaporKeepAlive and hydrate under VDOM Suspense.\n * The loading placeholder receives the deferred component's props and slots\n * (beta.14+) - render a skeleton matching the final shape. Per-beta detail:\n * CHANGELOG / whitepaper alignment log.\n *\n * Returns null if the Vapor runtime (Vue 3.6+) is not detected.\n *\n * @example\n * import { defineVaporAsyncComponent } from 'vapor-chamber';\n * const AsyncPanel = defineVaporAsyncComponent(() => import('./Panel.vue'));\n */\nexport function defineVaporAsyncComponent<T = any>(\n loader: (() => Promise<unknown>) | object,\n): T | null {\n const fn = getDefineVaporAsyncComponentFn();\n if (!fn) {\n if (DEV) devWarnNoVapor(\"defineVaporAsyncComponent\");\n return null;\n }\n return fn(loader) as T;\n}\n\n// ---------------------------------------------------------------------------\n// defineVaporCommand\n// ---------------------------------------------------------------------------\n\n/** `defineVaporCommand`'s options: how the handler registers, and on which bus. */\nexport type VaporCommandOptions<B extends BaseBus = BaseBus> = RegisterOptions & BusOption<B>;\n\n/**\n * defineVaporCommand - zero-overhead command for hot paths in Vapor mode.\n *\n * Unlike useCommand(), this skips reactive loading/error signal creation.\n * Ideal for high-frequency, fire-and-forget patterns where reactive\n * loading/error state isn't needed: scroll-position tracking, mousemove\n * sampling, telemetry / metrics events, debounced search, autosave.\n *\n * @example\n * const { dispatch } = defineVaporCommand('telemetryEvent', (cmd) => {\n * // forward to whatever metrics / analytics SDK you use\n * sendMetric(cmd.target.name, cmd.target.params);\n * });\n * dispatch({ name: 'page_view', params: { page: '/landing' } });\n */\nexport function defineVaporCommand<B extends BaseBus = CommandBus>(\n action: string,\n handler: Handler,\n options?: VaporCommandOptions<B>\n) {\n const bus = resolveBus(options?.bus);\n // The options go to register() as they are: it reads the register fields,\n // and an undo's stored copy keeps `bus`, which nothing reads.\n const unregister = bus.register(action, handler, options);\n\n // `untracked`, like every other composable's dispatch. This one was the gap,\n // and the worst place for it: a dispatch is an ACTION, not a read, and this\n // helper is documented for hot paths in Vapor mode - i.e. the call most\n // likely to be made from inside a render effect, where the handler's reads\n // become that effect's dependencies and the component re-renders on state it\n // never mentions. Pass-through when Vue is absent, so the \"zero reactive\n // overhead\" claim above is unaffected for non-Vue consumers.\n function dispatch(target: any, payload?: any): ResultOn<B> {\n return untracked(() => bus.dispatch(action, target, payload)) as ResultOn<B>;\n }\n\n function dispose() { unregister(); }\n\n tryAutoCleanup(dispose);\n\n return { dispatch, dispose };\n}\n\n// ---------------------------------------------------------------------------\n// useVaporAsyncCommand\n// ---------------------------------------------------------------------------\n\n/**\n * useVaporAsyncCommand - async-aware command dispatch for Vapor components\n * used inside Suspense boundaries.\n *\n * Vue 3.6.0-beta.10 introduced proper async component hydration under VDOM\n * Suspense. This composable wraps an AsyncCommandBus dispatch with reactive\n * loading/error state, making it safe for `<script setup vapor>` components\n * that await async operations. Error-boundary rendering and scheduler-flush\n * behavior around async components are Vue runtime concerns (per-beta detail:\n * CHANGELOG / whitepaper alignment log).\n *\n * The dispatch function returns a Promise<CommandResult>, matching the\n * AsyncCommandBus interface. Use this when your commands hit async transports\n * (HTTP bridge, WS bridge) and you need awaitable results.\n *\n * Vue 3.6.0-rc.8: an awaiting `<script setup vapor>` compiles to an\n * `async setup()` that uses runtime-core's `withAsyncContext` and returns its\n * template as a render closure, and it registers with the NEAREST `<Suspense>`.\n * Composables called after the `await` - this one, `useCommand()` - arm their\n * cleanup on the restored component scope, including when the app unmounts\n * during the await. Pinned by tests/async-vapor-setup-fixture.test.ts.\n *\n * @example\n * // In a <script setup vapor> component under <Suspense>:\n * import { useVaporAsyncCommand } from 'vapor-chamber';\n * const { dispatch, loading, lastError } = useVaporAsyncCommand(asyncBus);\n * const result = await dispatch('orderCreate', { items: cart });\n */\nexport function useVaporAsyncCommand(asyncBus?: { dispatch: (action: string, target: any, payload?: any) => Promise<CommandResult> }) {\n const bus: any = resolveBus(asyncBus as BaseBus | undefined);\n const loading = signal(false);\n const lastError = signal<Error | null>(null);\n\n // Intentionally hand-rolled, NOT routed through the shared runDispatch() that\n // useCommand / useCommandQuery use. A single async/await is\n // ~1.2x leaner on the dispatch wrapper than runDispatch's .then-chain (measured);\n // this is the awaited HTTP/WS path, so keep it lean. Do not \"consolidate\" into\n // runDispatch - the consistency isn't worth the wrapper overhead here.\n async function dispatch(action: string, target: any, payload?: any): Promise<CommandResult> {\n try {\n // Both opening writes are inside the try: a sync subscriber that throws\n // on either becomes a failed result and `loading` ends false (pinned by\n // tests/vapor-async-throwing-subscriber.test.ts).\n loading.value = true;\n lastError.value = null;\n // Untracked around the SYNCHRONOUS entry only - once the dispatch\n // suspends, the caller's effect has finished and there is no subscriber\n // left to leak into (pinned by the \"synchronous entry is the whole\n // exposure\" test). Same scope as runDispatch's, one call, so the leaner\n // hand-rolled wrapper above stays leaner.\n const result = await untracked(() => bus.dispatch(action, target, payload));\n if (!result.ok) lastError.value = result.error ?? null;\n return result;\n } catch (e) {\n const error = e as Error;\n lastError.value = error;\n return _errResult(error);\n } finally {\n loading.value = false;\n }\n }\n\n // Dispatch-only composable (no register/on, unlike useCommand) - there are no\n // subscriptions to tear down. Kept as a no-op for return-shape symmetry with the\n // other composables (callers may destructure `dispose`).\n function dispose() {}\n\n tryAutoCleanup(dispose);\n\n return { dispatch, loading, lastError, dispose };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,SAAS,eAAe,KAAmB;CACzC,QAAQ,KACN,mBAAmB,IAAI,kGACgB,iBAAiB,EAAE,iEAE5D;AACF;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,sBACd,eACA,WACM;CACN,MAAM,KAAK,cAAc;CACzB,IAAI,CAAC,IAIH,MAAM,IAAI,MAER,sDAAsD,iBAAiB,OACtE,MAAM,sDAAsD,GAC/D;CAEF,OAAO,GAAG,eAAe,SAAS;AACpC;;;;;;;;;;;;;;AAeA,SAAgB,wBAAoC;CAClD,OAAO,mBAAmB;AAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,yBAAkC,SAAiB,cAAiC;CAClG,MAAM,KAAK,8BAA8B;CACzC,IAAI,CAAC,IAAI;EACP,IAAI,KAAK,eAAe,0BAA0B;EAClD,OAAO;CACT;CACA,OAAQ,iBAAiB,KAAA,IAAY,GAAG,SAAS,YAAY,IAAI,GAAG,OAAO;AAC7E;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,qBAA8B,SAA2B;CACvE,MAAM,KAAK,0BAA0B;CACrC,IAAI,CAAC,IAAI;EACP,IAAI,KAAK,eAAe,sBAAsB;EAC9C,OAAO;CACT;CACA,OAAO,GAAG,OAAO;AACnB;;;;;;;;;;;;;;;;AAiBA,SAAgB,0BACd,QACU;CACV,MAAM,KAAK,+BAA+B;CAC1C,IAAI,CAAC,IAAI;EACP,IAAI,KAAK,eAAe,2BAA2B;EACnD,OAAO;CACT;CACA,OAAO,GAAG,MAAM;AAClB;;;;;;;;;;;;;;;;AAwBA,SAAgB,mBACd,QACA,SACA,SACA;CACA,MAAM,MAAM,WAAW,SAAS,GAAG;CAGnC,MAAM,aAAa,IAAI,SAAS,QAAQ,SAAS,OAAO;CASxD,SAAS,SAAS,QAAa,SAA4B;EACzD,OAAO,gBAAgB,IAAI,SAAS,QAAQ,QAAQ,OAAO,CAAC;CAC9D;CAEA,SAAS,UAAU;EAAE,WAAW;CAAG;CAEnC,eAAe,OAAO;CAEtB,OAAO;EAAE;EAAU;CAAQ;AAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,qBAAqB,UAAiG;CACpI,MAAM,MAAW,WAAW,QAA+B;CAC3D,MAAM,UAAU,OAAO,KAAK;CAC5B,MAAM,YAAY,OAAqB,IAAI;CAO3C,eAAe,SAAS,QAAgB,QAAa,SAAuC;EAC1F,IAAI;GAIF,QAAQ,QAAQ;GAChB,UAAU,QAAQ;GAMlB,MAAM,SAAS,MAAM,gBAAgB,IAAI,SAAS,QAAQ,QAAQ,OAAO,CAAC;GAC1E,IAAI,CAAC,OAAO,IAAI,UAAU,QAAQ,OAAO,SAAS;GAClD,OAAO;EACT,SAAS,GAAG;GACV,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,OAAOA,UAAW,KAAK;EACzB,UAAU;GACR,QAAQ,QAAQ;EAClB;CACF;CAKA,SAAS,UAAU,CAAC;CAEpB,eAAe,OAAO;CAEtB,OAAO;EAAE;EAAU;EAAS;EAAW;CAAQ;AACjD"}
|
|
1
|
+
{"version":3,"file":"chamber-vapor.js","names":["_errResult"],"sources":["../src/chamber-vapor.ts"],"sourcesContent":["/**\n * vapor-chamber - Vue 3.6+ Vapor-specific API\n *\n * This file renders nothing: it forwards Vue's own define* functions, so\n * rendering-side work (attrs fallthrough, interop, hydration) lands below it.\n * The wrappers and createVaporChamberApp take an opt-in return generic\n * (`<T = any>`) and `object`-typed options: importing Vue's Vapor types here\n * would put a hard `vue` type dependency on the main barrel and break Vue-less\n * command-bus consumers, so callers opt in: `defineVaporComponent<MyComp>(opts)`.\n *\n * Separated from chamber.ts to keep the core composable module CDCC-compliant.\n */\n\nimport {\n signal,\n tryAutoCleanup,\n untracked,\n getVaporAppFn,\n getVaporInteropRef,\n getDefineVaporCustomElementFn,\n getDefineVaporComponentFn,\n getDefineVaporAsyncComponentFn,\n vueDetectionHint,\n} from './chamber';\nimport type { BaseBus, CommandBus, Handler, RegisterOptions, CommandResult } from './command-bus';\nimport { _errResult } from './command-bus';\nimport { DEV } from './dev';\nimport { resolveBus, type BusOption, type ResultOn } from './shared-bus';\n\n/**\n * Why the `defineVapor*` wrappers warn instead of just returning null.\n *\n * Each returns `null` when the Vapor runtime is absent, and callers are\n * documented to check. But a bare `null` is the same failure shape that hid a\n * real bug for several releases: `tryKeepAliveHooks` guarded on an accessor\n * that silently answers \"no\" under Vapor, so a feature was inert with nothing\n * to see (rc.4 cycle - see tests/keepalive-input-scope-fixture.test.ts). Silent\n * negatives are how detection bugs survive.\n *\n * So the null stays - throwing would break the documented contract and the\n * tests that assert it - but it is not quiet. The call sites gate on\n * `DEV`, so the whole call (and every byte of message text) folds out of the\n * production IIFE builds; this helper is then unreferenced and tree-shaken.\n */\nfunction devWarnNoVapor(api: string): void {\n console.warn(\n `[vapor-chamber] ${api}() returned null - Vue 3.6+ with Vapor mode was not detected, ` +\n `so the component was NOT created. ${vueDetectionHint()} ` +\n 'For VDOM mode use the matching define* helper from vue instead.',\n );\n}\n\n/**\n * Create a Vapor app instance with vapor-chamber ready.\n * Requires Vue 3.6+. Throws if Vapor is not available.\n *\n * Pass-through over Vue's createVaporApp - scope-ID handling, HMR app-instance\n * refresh, and setup() error recovery are Vue runtime behavior (per-beta detail:\n * CHANGELOG / whitepaper alignment log).\n *\n * `rootProps` are Vue's raw props, passed through unchanged: a plain value is\n * the value, and a FUNCTION is a getter that Vue calls for the value. So a\n * callback is wrapped, `{ onSave: () => onSave }`; passed bare it is called\n * and the component receives what it returned. (A function set on a\n * `defineWidget` element property is the opposite: it arrives as itself.)\n * Pinned by tests/vapor/vapor-subpath-wiring.test.ts.\n *\n * @example\n * import { createVaporChamberApp } from 'vapor-chamber';\n * import App from './App.vue';\n * createVaporChamberApp(App).mount('#app');\n *\n * @example Root props\n * createVaporChamberApp(App, { title: 'Cart', onSave: () => onSave }).mount('#app');\n */\nexport function createVaporChamberApp<TApp = any>(\n rootComponent: object,\n rootProps?: Record<string, unknown>,\n): TApp {\n const fn = getVaporAppFn();\n if (!fn) {\n // The bare \"Vapor required\" message was the same whether Vue was absent,\n // present-without-Vapor, or present-but-unreachable - three different\n // problems with three different fixes. vueDetectionHint() names which.\n throw new Error(\n // The diagnosis ships (vueDetectionHint says why); the advice is DEV's.\n `[vapor-chamber] Vue 3.6+ with Vapor mode required. ${vueDetectionHint()}` +\n (DEV ? ' For VDOM mode, use createApp() from vue instead.' : '')\n );\n }\n return fn(rootComponent, rootProps) as TApp;\n}\n\n/**\n * Returns the vaporInteropPlugin if available (Vue 3.6+).\n * Use this to enable mixed Vapor/VDOM component trees.\n *\n * Pass-through over Vue's plugin. The returned reference is safe to hold\n * across HMR cycles (Vue does not mutate the bridge).\n *\n * @example\n * import { createApp } from 'vue';\n * import { getVaporInteropPlugin } from 'vapor-chamber';\n * const plugin = getVaporInteropPlugin();\n * if (plugin) createApp(App).use(plugin).mount('#app');\n */\nexport function getVaporInteropPlugin(): any | null {\n return getVaporInteropRef();\n}\n\n// ---------------------------------------------------------------------------\n// Vapor Custom Elements (Vue 3.6 - defineVaporCustomElement introduced in 3.6.0-alpha.4, #14017)\n// ---------------------------------------------------------------------------\n\n/**\n * defineVaporCustomElement - create a custom element backed by Vapor rendering.\n *\n * Wraps Vue's `defineVaporCustomElement()` (introduced in 3.6.0-alpha.4, #14017). The generated custom\n * element uses Vapor's compiler-optimized rendering instead of the VDOM, giving\n * zero-overhead DOM updates inside shadow DOM. Safe to call with a reused\n * options object, and children re-render on reactive prop changes (beta.14+;\n * per-beta detail: CHANGELOG / whitepaper alignment log).\n *\n * Returns null if the Vapor runtime (Vue 3.6+) is not detected - check before calling\n * `customElements.define()`.\n *\n * @example\n * import { defineVaporCustomElement } from 'vapor-chamber';\n * const MyEl = defineVaporCustomElement({\n * props: { label: String },\n * // A Vapor setup() returns a BLOCK - real DOM nodes - not a render\n * // function or h() output. An SFC's compiler builds this from its template.\n * setup(props) {\n * const span = document.createElement('span');\n * span.textContent = String(props.label);\n * return span;\n * }\n * });\n * if (MyEl) customElements.define('vc-greeting', MyEl);\n */\nexport function defineVaporCustomElement<T = any>(options: object, extraOptions?: object): T | null {\n const fn = getDefineVaporCustomElementFn();\n if (!fn) {\n if (DEV) devWarnNoVapor(\"defineVaporCustomElement\");\n return null;\n }\n return (extraOptions !== undefined ? fn(options, extraOptions) : fn(options)) as T;\n}\n\n/**\n * defineVaporComponent - define a Vapor component with proper type inference.\n *\n * Wraps Vue's `defineVaporComponent()` (typed since 3.6.0-alpha.5, #13831). Use this to get full\n * TypeScript inference for props, emits, and slots in Vapor components.\n * Pass-through - emits/$attrs routing, v-once interop, scope IDs, and the\n * compiler optimizations are Vue behavior (per-beta detail: CHANGELOG /\n * whitepaper alignment log).\n *\n * Returns null if the Vapor runtime (Vue 3.6+) is not detected.\n *\n * @example\n * import { defineVaporComponent } from 'vapor-chamber';\n * const Comp = defineVaporComponent({\n * props: { count: Number },\n * emits: ['change'],\n * // Returns DOM nodes (a block), not h(): see defineVaporCustomElement above.\n * setup(props) {\n * const div = document.createElement('div');\n * div.textContent = `Count: ${props.count}`;\n * return div;\n * }\n * });\n */\nexport function defineVaporComponent<T = any>(options: object): T | null {\n const fn = getDefineVaporComponentFn();\n if (!fn) {\n if (DEV) devWarnNoVapor(\"defineVaporComponent\");\n return null;\n }\n return fn(options) as T;\n}\n\n/**\n * defineVaporAsyncComponent - define an async Vapor component for lazy loading.\n *\n * Wraps Vue's `defineVaporAsyncComponent()` (introduced in 3.6.0-alpha.3, #13059). Async Vapor\n * components are cached by VaporKeepAlive and hydrate under VDOM Suspense.\n * The loading placeholder receives the deferred component's props and slots\n * (beta.14+) - render a skeleton matching the final shape. Per-beta detail:\n * CHANGELOG / whitepaper alignment log.\n *\n * Returns null if the Vapor runtime (Vue 3.6+) is not detected.\n *\n * @example\n * import { defineVaporAsyncComponent } from 'vapor-chamber';\n * const AsyncPanel = defineVaporAsyncComponent(() => import('./Panel.vue'));\n */\nexport function defineVaporAsyncComponent<T = any>(\n loader: (() => Promise<unknown>) | object,\n): T | null {\n const fn = getDefineVaporAsyncComponentFn();\n if (!fn) {\n if (DEV) devWarnNoVapor(\"defineVaporAsyncComponent\");\n return null;\n }\n return fn(loader) as T;\n}\n\n// ---------------------------------------------------------------------------\n// defineVaporCommand\n// ---------------------------------------------------------------------------\n\n/** `defineVaporCommand`'s options: how the handler registers, and on which bus. */\nexport type VaporCommandOptions<B extends BaseBus = BaseBus> = RegisterOptions & BusOption<B>;\n\n/**\n * defineVaporCommand - zero-overhead command for hot paths in Vapor mode.\n *\n * Unlike useCommand(), this skips reactive loading/error signal creation.\n * Ideal for high-frequency, fire-and-forget patterns where reactive\n * loading/error state isn't needed: scroll-position tracking, mousemove\n * sampling, telemetry / metrics events, debounced search, autosave.\n *\n * @example\n * const { dispatch } = defineVaporCommand('telemetryEvent', (cmd) => {\n * // forward to whatever metrics / analytics SDK you use\n * sendMetric(cmd.target.name, cmd.target.params);\n * });\n * dispatch({ name: 'page_view', params: { page: '/landing' } });\n */\nexport function defineVaporCommand<B extends BaseBus = CommandBus>(\n action: string,\n handler: Handler,\n options?: VaporCommandOptions<B>\n) {\n const bus = resolveBus(options?.bus);\n // The options go to register() as they are: it reads the register fields,\n // and an undo's stored copy keeps `bus`, which nothing reads.\n const unregister = bus.register(action, handler, options);\n\n // `untracked`, like every other composable's dispatch. This one was the gap,\n // and the worst place for it: a dispatch is an ACTION, not a read, and this\n // helper is documented for hot paths in Vapor mode - i.e. the call most\n // likely to be made from inside a render effect, where the handler's reads\n // become that effect's dependencies and the component re-renders on state it\n // never mentions. Pass-through when Vue is absent, so the \"zero reactive\n // overhead\" claim above is unaffected for non-Vue consumers.\n function dispatch(target: any, payload?: any): ResultOn<B> {\n return untracked(() => bus.dispatch(action, target, payload)) as ResultOn<B>;\n }\n\n function dispose() { unregister(); }\n\n tryAutoCleanup(dispose);\n\n return { dispatch, dispose };\n}\n\n// ---------------------------------------------------------------------------\n// useVaporAsyncCommand\n// ---------------------------------------------------------------------------\n\n/**\n * useVaporAsyncCommand - async-aware command dispatch for Vapor components\n * used inside Suspense boundaries.\n *\n * Vue 3.6.0-beta.10 introduced proper async component hydration under VDOM\n * Suspense. This composable wraps an AsyncCommandBus dispatch with reactive\n * loading/error state, making it safe for `<script setup vapor>` components\n * that await async operations. Error-boundary rendering and scheduler-flush\n * behavior around async components are Vue runtime concerns (per-beta detail:\n * CHANGELOG / whitepaper alignment log).\n *\n * The dispatch function returns a Promise<CommandResult>, matching the\n * AsyncCommandBus interface. Use this when your commands hit async transports\n * (HTTP bridge, WS bridge) and you need awaitable results.\n *\n * Vue 3.6.0-rc.8: an awaiting `<script setup vapor>` compiles to an\n * `async setup()` that uses runtime-core's `withAsyncContext` and returns its\n * template as a render closure, and it registers with the NEAREST `<Suspense>`.\n * Composables called after the `await` - this one, `useCommand()` - arm their\n * cleanup on the restored component scope, including when the app unmounts\n * during the await. Pinned by tests/async-vapor-setup-fixture.test.ts.\n *\n * @example\n * // In a <script setup vapor> component under <Suspense>:\n * import { useVaporAsyncCommand } from 'vapor-chamber';\n * const { dispatch, loading, lastError } = useVaporAsyncCommand(asyncBus);\n * const result = await dispatch('orderCreate', { items: cart });\n */\nexport function useVaporAsyncCommand(asyncBus?: { dispatch: (action: string, target: any, payload?: any) => Promise<CommandResult> }) {\n const bus: any = resolveBus(asyncBus as BaseBus | undefined);\n const loading = signal(false);\n const lastError = signal<Error | null>(null);\n // In flight: `loading` is true while any dispatch is, as runDispatch's\n // (tests/composable-overlap.test.ts).\n let n = 0;\n\n // Intentionally hand-rolled, NOT routed through the shared runDispatch() that\n // useCommand / useCommandQuery use. A single async/await is\n // ~1.2x leaner on the dispatch wrapper than runDispatch's .then-chain (measured);\n // this is the awaited HTTP/WS path, so keep it lean. Do not \"consolidate\" into\n // runDispatch - the consistency isn't worth the wrapper overhead here.\n async function dispatch(action: string, target: any, payload?: any): Promise<CommandResult> {\n try {\n // Both opening writes are inside the try: a sync subscriber that throws\n // on either becomes a failed result and `loading` ends false (pinned by\n // tests/vapor-async-throwing-subscriber.test.ts).\n if (n++ === 0) loading.value = true;\n lastError.value = null;\n // Untracked around the SYNCHRONOUS entry only - once the dispatch\n // suspends, the caller's effect has finished and there is no subscriber\n // left to leak into (pinned by the \"synchronous entry is the whole\n // exposure\" test). Same scope as runDispatch's, one call, so the leaner\n // hand-rolled wrapper above stays leaner.\n const result = await untracked(() => bus.dispatch(action, target, payload));\n if (!result.ok) lastError.value = result.error ?? null;\n return result;\n } catch (e) {\n const error = e as Error;\n lastError.value = error;\n return _errResult(error);\n } finally {\n if (--n === 0) loading.value = false;\n }\n }\n\n // Dispatch-only composable (no register/on, unlike useCommand) - there are no\n // subscriptions to tear down. Kept as a no-op for return-shape symmetry with the\n // other composables (callers may destructure `dispose`).\n function dispose() {}\n\n tryAutoCleanup(dispose);\n\n return { dispatch, loading, lastError, dispose };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,SAAS,eAAe,KAAmB;CACzC,QAAQ,KACN,mBAAmB,IAAI,kGACgB,iBAAiB,EAAE,iEAE5D;AACF;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,SAAgB,sBACd,eACA,WACM;CACN,MAAM,KAAK,cAAc;CACzB,IAAI,CAAC,IAIH,MAAM,IAAI,MAER,sDAAsD,iBAAiB,OACtE,MAAM,sDAAsD,GAC/D;CAEF,OAAO,GAAG,eAAe,SAAS;AACpC;;;;;;;;;;;;;;AAeA,SAAgB,wBAAoC;CAClD,OAAO,mBAAmB;AAC5B;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,SAAgB,yBAAkC,SAAiB,cAAiC;CAClG,MAAM,KAAK,8BAA8B;CACzC,IAAI,CAAC,IAAI;EACP,IAAI,KAAK,eAAe,0BAA0B;EAClD,OAAO;CACT;CACA,OAAQ,iBAAiB,KAAA,IAAY,GAAG,SAAS,YAAY,IAAI,GAAG,OAAO;AAC7E;;;;;;;;;;;;;;;;;;;;;;;;;AA0BA,SAAgB,qBAA8B,SAA2B;CACvE,MAAM,KAAK,0BAA0B;CACrC,IAAI,CAAC,IAAI;EACP,IAAI,KAAK,eAAe,sBAAsB;EAC9C,OAAO;CACT;CACA,OAAO,GAAG,OAAO;AACnB;;;;;;;;;;;;;;;;AAiBA,SAAgB,0BACd,QACU;CACV,MAAM,KAAK,+BAA+B;CAC1C,IAAI,CAAC,IAAI;EACP,IAAI,KAAK,eAAe,2BAA2B;EACnD,OAAO;CACT;CACA,OAAO,GAAG,MAAM;AAClB;;;;;;;;;;;;;;;;AAwBA,SAAgB,mBACd,QACA,SACA,SACA;CACA,MAAM,MAAM,WAAW,SAAS,GAAG;CAGnC,MAAM,aAAa,IAAI,SAAS,QAAQ,SAAS,OAAO;CASxD,SAAS,SAAS,QAAa,SAA4B;EACzD,OAAO,gBAAgB,IAAI,SAAS,QAAQ,QAAQ,OAAO,CAAC;CAC9D;CAEA,SAAS,UAAU;EAAE,WAAW;CAAG;CAEnC,eAAe,OAAO;CAEtB,OAAO;EAAE;EAAU;CAAQ;AAC7B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,qBAAqB,UAAiG;CACpI,MAAM,MAAW,WAAW,QAA+B;CAC3D,MAAM,UAAU,OAAO,KAAK;CAC5B,MAAM,YAAY,OAAqB,IAAI;CAG3C,IAAI,IAAI;CAOR,eAAe,SAAS,QAAgB,QAAa,SAAuC;EAC1F,IAAI;GAIF,IAAI,QAAQ,GAAG,QAAQ,QAAQ;GAC/B,UAAU,QAAQ;GAMlB,MAAM,SAAS,MAAM,gBAAgB,IAAI,SAAS,QAAQ,QAAQ,OAAO,CAAC;GAC1E,IAAI,CAAC,OAAO,IAAI,UAAU,QAAQ,OAAO,SAAS;GAClD,OAAO;EACT,SAAS,GAAG;GACV,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,OAAOA,UAAW,KAAK;EACzB,UAAU;GACR,IAAI,EAAE,MAAM,GAAG,QAAQ,QAAQ;EACjC;CACF;CAKA,SAAS,UAAU,CAAC;CAEpB,eAAe,OAAO;CAEtB,OAAO;EAAE;EAAU;EAAS;EAAW;CAAQ;AACjD"}
|
package/dist/chamber.d.ts
CHANGED
|
@@ -212,8 +212,13 @@ export declare function tryAutoCleanup(disposeFn: () => void): void;
|
|
|
212
212
|
* @internal - used by composables that manage bus subscriptions.
|
|
213
213
|
*/
|
|
214
214
|
export declare function tryKeepAliveHooks(onPause: () => void, onResume: () => void): void;
|
|
215
|
+
/** @internal A composable's in-flight count and its latest dispatch. */
|
|
216
|
+
export type DispatchCounts = {
|
|
217
|
+
n: number;
|
|
218
|
+
seq: number;
|
|
219
|
+
};
|
|
215
220
|
/** @internal */
|
|
216
|
-
export declare function runDispatch(bus: CommandBus, query: boolean, action: string, target: unknown, payload: unknown, loading: Signal<boolean>, lastError: Signal<Error | null>, onSuccess?: (value: any) => void): CommandResult | Promise<CommandResult>;
|
|
221
|
+
export declare function runDispatch(bus: CommandBus, query: boolean, action: string, target: unknown, payload: unknown, loading: Signal<boolean>, lastError: Signal<Error | null>, st: DispatchCounts, onSuccess?: (value: any) => void): CommandResult | Promise<CommandResult>;
|
|
217
222
|
/**
|
|
218
223
|
* useCommand - reactive command dispatch with optional bus subscriptions.
|
|
219
224
|
*
|
package/dist/chamber.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chamber.d.ts","sourceRoot":"","sources":["../src/chamber.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAAyE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAC,KAAK,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,KAAK,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,eAAe,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AACrS,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAGnD,OAAO,EAAE,aAAa,EAAE,eAAe,EAAc,aAAa,EAAE,KAAK,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,cAAc,CAAC;AAgDxH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAgBvC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,CAAC;AAC3B,OAAO,EAAE,MAAM,EAAE,CAAC;AA4IlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAI9C;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,KAAK,EAAE,MAAM,IAAI,EAAE,UAAU,UAAQ,GAAG,IAAI,CAI3F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAY3C;AAmND;;;;;;;;GAQG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC,CAIzD;AASD;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAkBzC;AAED,gDAAgD;AAChD,wBAAgB,aAAa,IAAI,GAAG,CAA8B;AAClE,gDAAgD;AAChD,wBAAgB,kBAAkB,IAAI,GAAG,CAAmC;AAC5E,gDAAgD;AAChD,wBAAgB,6BAA6B,IAAI,GAAG,CAAwC;AAC5F,gDAAgD;AAChD,wBAAgB,yBAAyB,IAAI,GAAG,CAAoC;AACpF,gDAAgD;AAChD,wBAAgB,8BAA8B,IAAI,GAAG,CAAyC;AAC9F;wEACwE;AACxE,wBAAgB,eAAe,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK;IAAE,KAAK,EAAE,CAAC,CAAA;CAAE,CAAC,GAAG,IAAI,CAA0B;AAM/F;;;;;;;;;;;;;;;;;GAiBG;AAEH,MAAM,WAAW,cAAc;CAAG;AAElC;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,MAAM,cAAc,CAAC,SAAS,CAAC,KAAK,CAAC,GACjE,UAAU,GACV;KAAG,CAAC,IAAI,MAAM,cAAc,GAAG,cAAc,CAAC,CAAC,CAAC;CAAE,CAAC;AAGvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,eAAe,EAAE,CAAC;AACzD,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AAMpC;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG,IAAI,CAwB1D;AAMD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,IAAI,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI,CAOjF;
|
|
1
|
+
{"version":3,"file":"chamber.d.ts","sourceRoot":"","sources":["../src/chamber.ts"],"names":[],"mappings":"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"}
|
package/dist/chamber.js
CHANGED
|
@@ -162,10 +162,11 @@ function tryKeepAliveHooks(onPause, onResume) {
|
|
|
162
162
|
_vueOnDeactivated?.(onPause);
|
|
163
163
|
_vueOnActivated?.(onResume);
|
|
164
164
|
}
|
|
165
|
-
function runDispatch(bus, query, action, target, payload, loading, lastError, onSuccess) {
|
|
165
|
+
function runDispatch(bus, query, action, target, payload, loading, lastError, st, onSuccess) {
|
|
166
166
|
let result;
|
|
167
|
+
const mine = ++st.seq;
|
|
167
168
|
try {
|
|
168
|
-
loading.value = true;
|
|
169
|
+
if (st.n++ === 0) loading.value = true;
|
|
169
170
|
lastError.value = null;
|
|
170
171
|
warnProbePath();
|
|
171
172
|
_pause();
|
|
@@ -175,35 +176,39 @@ function runDispatch(bus, query, action, target, payload, loading, lastError, on
|
|
|
175
176
|
_reset();
|
|
176
177
|
}
|
|
177
178
|
} catch (e) {
|
|
178
|
-
loading.value = false;
|
|
179
|
+
if (--st.n === 0) loading.value = false;
|
|
179
180
|
const error = e;
|
|
180
181
|
lastError.value = error;
|
|
181
182
|
const failed = errResult(error);
|
|
182
183
|
return _isAsyncBus(bus) ? Promise.resolve(failed) : failed;
|
|
183
184
|
}
|
|
184
185
|
if (result && typeof result.then === "function") return result.then((r) => {
|
|
185
|
-
loading.value = false;
|
|
186
|
-
if (r.ok)
|
|
187
|
-
else
|
|
186
|
+
if (--st.n === 0) loading.value = false;
|
|
187
|
+
if (!r.ok) lastError.value = r.error ?? null;
|
|
188
|
+
else if (mine === st.seq) onSuccess?.(r.value);
|
|
188
189
|
return r;
|
|
189
190
|
}, (e) => {
|
|
190
|
-
loading.value = false;
|
|
191
|
+
if (--st.n === 0) loading.value = false;
|
|
191
192
|
lastError.value = e;
|
|
192
193
|
return errResult(e);
|
|
193
194
|
});
|
|
194
|
-
loading.value = false;
|
|
195
|
-
if (result.ok)
|
|
196
|
-
else
|
|
195
|
+
if (--st.n === 0) loading.value = false;
|
|
196
|
+
if (!result.ok) lastError.value = result.error ?? null;
|
|
197
|
+
else if (mine === st.seq) onSuccess?.(result.value);
|
|
197
198
|
return result;
|
|
198
199
|
}
|
|
199
200
|
function useCommand(options) {
|
|
200
201
|
const bus = resolveBus(options?.bus);
|
|
201
202
|
const loading = signal(false);
|
|
202
203
|
const lastError = signal(null);
|
|
204
|
+
var counts;
|
|
203
205
|
const listeners = [];
|
|
204
206
|
const registered = [];
|
|
205
207
|
function dispatch(action, target, payload) {
|
|
206
|
-
return runDispatch(bus, false, action, target, payload, loading, lastError
|
|
208
|
+
return runDispatch(bus, false, action, target, payload, loading, lastError, counts ??= {
|
|
209
|
+
n: 0,
|
|
210
|
+
seq: 0
|
|
211
|
+
});
|
|
207
212
|
}
|
|
208
213
|
function register(action, handler, opts) {
|
|
209
214
|
const reg = {
|
|
@@ -541,11 +546,15 @@ function useCommandQuery(options) {
|
|
|
541
546
|
const data = signal(null);
|
|
542
547
|
const loading = signal(false);
|
|
543
548
|
const lastError = signal(null);
|
|
549
|
+
var counts;
|
|
544
550
|
const onData = (value) => {
|
|
545
551
|
data.value = value;
|
|
546
552
|
};
|
|
547
553
|
function query(action, target, payload) {
|
|
548
|
-
return runDispatch(bus, true, action, target, payload, loading, lastError,
|
|
554
|
+
return runDispatch(bus, true, action, target, payload, loading, lastError, counts ??= {
|
|
555
|
+
n: 0,
|
|
556
|
+
seq: 0
|
|
557
|
+
}, onData);
|
|
549
558
|
}
|
|
550
559
|
return {
|
|
551
560
|
query,
|