vapor-chamber 1.0.0 → 1.2.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.
Files changed (187) hide show
  1. package/README.md +97 -9
  2. package/ROADMAP.md +233 -0
  3. package/dist/alien-signals.d.ts +80 -0
  4. package/dist/alien-signals.d.ts.map +1 -0
  5. package/dist/alien-signals.js +22 -0
  6. package/dist/alien-signals.js.map +1 -0
  7. package/dist/chamber-BHo2PZnx.js +1297 -0
  8. package/dist/chamber-BHo2PZnx.js.map +1 -0
  9. package/dist/chamber-BLvYXQR8.js +518 -0
  10. package/dist/chamber-BLvYXQR8.js.map +1 -0
  11. package/dist/chamber-BUgOYOAc.js +1293 -0
  12. package/dist/chamber-BUgOYOAc.js.map +1 -0
  13. package/dist/chamber-B_ff1-af.js +518 -0
  14. package/dist/chamber-B_ff1-af.js.map +1 -0
  15. package/dist/chamber-BmLw2FDU.js +420 -0
  16. package/dist/chamber-BmLw2FDU.js.map +1 -0
  17. package/dist/chamber-BwyEE9vP.js +1320 -0
  18. package/dist/chamber-BwyEE9vP.js.map +1 -0
  19. package/dist/chamber-C0xS5NDI.js +420 -0
  20. package/dist/chamber-C0xS5NDI.js.map +1 -0
  21. package/dist/chamber-C1q3L1oU.js +420 -0
  22. package/dist/chamber-C1q3L1oU.js.map +1 -0
  23. package/dist/chamber-Cm7_vz_U.js +519 -0
  24. package/dist/chamber-Cm7_vz_U.js.map +1 -0
  25. package/dist/chamber-CoO1CLDg.js +518 -0
  26. package/dist/chamber-CoO1CLDg.js.map +1 -0
  27. package/dist/chamber-Vn0IZVna.js +518 -0
  28. package/dist/chamber-Vn0IZVna.js.map +1 -0
  29. package/dist/chamber-iK4VO3lw.js +420 -0
  30. package/dist/chamber-iK4VO3lw.js.map +1 -0
  31. package/dist/chamber-vapor-8R6iwINH.js +540 -0
  32. package/dist/chamber-vapor-8R6iwINH.js.map +1 -0
  33. package/dist/chamber-vapor-B5KwDQXN.js +539 -0
  34. package/dist/chamber-vapor-B5KwDQXN.js.map +1 -0
  35. package/dist/chamber-vapor-BNIYoRME.js +540 -0
  36. package/dist/chamber-vapor-BNIYoRME.js.map +1 -0
  37. package/dist/chamber-vapor-BYdGCAVC.js +540 -0
  38. package/dist/chamber-vapor-BYdGCAVC.js.map +1 -0
  39. package/dist/chamber-vapor-Bc-A5SID.js +540 -0
  40. package/dist/chamber-vapor-Bc-A5SID.js.map +1 -0
  41. package/dist/chamber-vapor-CQWCByx-.js +525 -0
  42. package/dist/chamber-vapor-CQWCByx-.js.map +1 -0
  43. package/dist/chamber-vapor-CrDvj19S.js +541 -0
  44. package/dist/chamber-vapor-CrDvj19S.js.map +1 -0
  45. package/dist/chamber-vapor-D7WY8gpo.js +540 -0
  46. package/dist/chamber-vapor-D7WY8gpo.js.map +1 -0
  47. package/dist/chamber-vapor-DzhknTIy.js +540 -0
  48. package/dist/chamber-vapor-DzhknTIy.js.map +1 -0
  49. package/dist/chamber-vapor-EH9hhveO.js +540 -0
  50. package/dist/chamber-vapor-EH9hhveO.js.map +1 -0
  51. package/dist/chamber-vapor-IpqC6fgk.js +540 -0
  52. package/dist/chamber-vapor-IpqC6fgk.js.map +1 -0
  53. package/dist/chamber-vapor-i1EIsWOg.js +540 -0
  54. package/dist/chamber-vapor-i1EIsWOg.js.map +1 -0
  55. package/dist/chamber-vapor-isZkw0sf.js +525 -0
  56. package/dist/chamber-vapor-isZkw0sf.js.map +1 -0
  57. package/dist/chamber-vapor.d.ts +104 -11
  58. package/dist/chamber-vapor.d.ts.map +1 -1
  59. package/dist/chamber.d.ts +104 -19
  60. package/dist/chamber.d.ts.map +1 -1
  61. package/dist/command-bus-DA3nlrS6.js +925 -0
  62. package/dist/command-bus-DA3nlrS6.js.map +1 -0
  63. package/dist/command-bus.d.ts +52 -4
  64. package/dist/command-bus.d.ts.map +1 -1
  65. package/dist/directives.d.ts.map +1 -1
  66. package/dist/directives.js +124 -214
  67. package/dist/directives.js.map +1 -0
  68. package/dist/fast-lane.d.ts +69 -0
  69. package/dist/fast-lane.d.ts.map +1 -0
  70. package/dist/fast-lane.js +47 -0
  71. package/dist/fast-lane.js.map +1 -0
  72. package/dist/form.d.ts +13 -1
  73. package/dist/form.d.ts.map +1 -1
  74. package/dist/http-cache.d.ts +14 -0
  75. package/dist/http-cache.d.ts.map +1 -0
  76. package/dist/http-query.d.ts +14 -0
  77. package/dist/http-query.d.ts.map +1 -0
  78. package/dist/http.d.ts +102 -3
  79. package/dist/http.d.ts.map +1 -1
  80. package/dist/iife-core.d.ts +72 -0
  81. package/dist/iife-core.d.ts.map +1 -0
  82. package/dist/iife-elements.d.ts +145 -0
  83. package/dist/iife-elements.d.ts.map +1 -0
  84. package/dist/iife.d.ts +50 -5
  85. package/dist/iife.d.ts.map +1 -1
  86. package/dist/iife.js +86 -83
  87. package/dist/iife.js.map +1 -0
  88. package/dist/index.d.ts +27 -9
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +1146 -89
  91. package/dist/index.js.map +1 -0
  92. package/dist/observable.d.ts +84 -0
  93. package/dist/observable.d.ts.map +1 -0
  94. package/dist/observable.js +47 -0
  95. package/dist/observable.js.map +1 -0
  96. package/dist/plugins-extra.d.ts.map +1 -1
  97. package/dist/plugins-io.d.ts +27 -0
  98. package/dist/plugins-io.d.ts.map +1 -1
  99. package/dist/plugins-schema.d.ts +93 -0
  100. package/dist/plugins-schema.d.ts.map +1 -0
  101. package/dist/plugins-schema.js +72 -0
  102. package/dist/plugins-schema.js.map +1 -0
  103. package/dist/schema.d.ts.map +1 -1
  104. package/dist/signal-BEmakAvP.js +913 -0
  105. package/dist/signal-BEmakAvP.js.map +1 -0
  106. package/dist/signal-C0JQhs2G.js +926 -0
  107. package/dist/signal-C0JQhs2G.js.map +1 -0
  108. package/dist/signal-C7iO4lNJ.js +957 -0
  109. package/dist/signal-C7iO4lNJ.js.map +1 -0
  110. package/dist/signal-CBFIjSiL.js +951 -0
  111. package/dist/signal-CBFIjSiL.js.map +1 -0
  112. package/dist/signal-D1SPpDTh.js +952 -0
  113. package/dist/signal-D1SPpDTh.js.map +1 -0
  114. package/dist/signal-DUZrcb-E.js +35 -0
  115. package/dist/signal-DUZrcb-E.js.map +1 -0
  116. package/dist/signal-eHOL-GNS.js +926 -0
  117. package/dist/signal-eHOL-GNS.js.map +1 -0
  118. package/dist/signal-pT4BgmiH.js +957 -0
  119. package/dist/signal-pT4BgmiH.js.map +1 -0
  120. package/dist/signal.d.ts +37 -0
  121. package/dist/signal.d.ts.map +1 -0
  122. package/dist/ssr.d.ts +100 -0
  123. package/dist/ssr.d.ts.map +1 -0
  124. package/dist/ssr.js +47 -0
  125. package/dist/ssr.js.map +1 -0
  126. package/dist/testing.d.ts.map +1 -1
  127. package/dist/transitions.d.ts +87 -0
  128. package/dist/transitions.d.ts.map +1 -0
  129. package/dist/transitions.js +95 -0
  130. package/dist/transitions.js.map +1 -0
  131. package/dist/transports-BHQkkVjt.js +699 -0
  132. package/dist/transports-BHQkkVjt.js.map +1 -0
  133. package/dist/transports-Bd_fqFva.js +732 -0
  134. package/dist/transports-Bd_fqFva.js.map +1 -0
  135. package/dist/transports-BhJ56Fgx.js +699 -0
  136. package/dist/transports-BhJ56Fgx.js.map +1 -0
  137. package/dist/transports-BuSZVG89.js +732 -0
  138. package/dist/transports-BuSZVG89.js.map +1 -0
  139. package/dist/transports-CMcmqEN-.js +742 -0
  140. package/dist/transports-CMcmqEN-.js.map +1 -0
  141. package/dist/transports-Ckkspzeb.js +700 -0
  142. package/dist/transports-Ckkspzeb.js.map +1 -0
  143. package/dist/transports-CtFiD_mr.js +699 -0
  144. package/dist/transports-CtFiD_mr.js.map +1 -0
  145. package/dist/transports-D4_d3u_d.js +699 -0
  146. package/dist/transports-D4_d3u_d.js.map +1 -0
  147. package/dist/transports-DKop5_zi.js +700 -0
  148. package/dist/transports-DKop5_zi.js.map +1 -0
  149. package/dist/transports-DN6DzZTU.js +732 -0
  150. package/dist/transports-DN6DzZTU.js.map +1 -0
  151. package/dist/transports-DTNi1DT6.js +732 -0
  152. package/dist/transports-DTNi1DT6.js.map +1 -0
  153. package/dist/transports.d.ts +45 -8
  154. package/dist/transports.d.ts.map +1 -1
  155. package/dist/transports.js +9 -249
  156. package/dist/transports.js.map +1 -0
  157. package/dist/utilities.d.ts.map +1 -1
  158. package/dist/vapor-chamber-core.iife.js +1293 -0
  159. package/dist/vapor-chamber-core.iife.js.map +1 -0
  160. package/dist/vapor-chamber-core.iife.min.js +1 -0
  161. package/dist/vapor-chamber-elements.iife.js +1385 -0
  162. package/dist/vapor-chamber-elements.iife.js.map +1 -0
  163. package/dist/vapor-chamber-elements.iife.min.js +1 -0
  164. package/dist/vapor-chamber.iife.js +682 -243
  165. package/dist/vapor-chamber.iife.js.map +1 -7
  166. package/dist/vapor-chamber.iife.min.js +1 -2
  167. package/dist/vite-hmr.d.ts +12 -0
  168. package/dist/vite-hmr.d.ts.map +1 -1
  169. package/dist/vite-hmr.js +52 -88
  170. package/dist/vite-hmr.js.map +1 -0
  171. package/package.json +64 -7
  172. package/scripts/build.mjs +108 -0
  173. package/scripts/check-size.mjs +67 -0
  174. package/dist/chamber-vapor.js +0 -112
  175. package/dist/chamber.js +0 -440
  176. package/dist/command-bus.js +0 -993
  177. package/dist/devtools.js +0 -155
  178. package/dist/form.js +0 -184
  179. package/dist/http.js +0 -251
  180. package/dist/plugins-core.js +0 -316
  181. package/dist/plugins-extra.js +0 -275
  182. package/dist/plugins-io.js +0 -171
  183. package/dist/plugins.js +0 -9
  184. package/dist/schema.js +0 -399
  185. package/dist/testing.js +0 -303
  186. package/dist/utilities.js +0 -119
  187. package/scripts/build-iife.mjs +0 -47
package/README.md CHANGED
@@ -145,9 +145,74 @@ Sub-path exports avoid pulling in optional modules:
145
145
  'vapor-chamber/transports' → HTTP + WebSocket + SSE bridges only
146
146
  'vapor-chamber/directives' → v-command Vue directive only
147
147
  'vapor-chamber/vite' → Vite HMR plugin only
148
- 'vapor-chamber/iife' → IIFE bundle
148
+ 'vapor-chamber/fast-lane' → minimal-allocation dispatcher for real-real-hot loops (game ticks, trading data, audio, scroll). Not a bus — see docs/performance.md.
149
+ 'vapor-chamber/observable' → Symbol.observable interop — RxJS / xstream / callbag adapter
150
+ 'vapor-chamber/standard-schema'→ Standard Schema v1 validator plugin (Zod / Valibot / ArkType compatible)
151
+ 'vapor-chamber/alien-signals' → connector to use alien-signals as the underlying reactive primitive (non-Vue contexts)
152
+ 'vapor-chamber/iife' → IIFE bundle (full)
153
+ 'vapor-chamber/iife-core' → IIFE bundle (no Vapor custom-element, no Suspense paths)
154
+ 'vapor-chamber/iife-elements' → IIFE bundle (core + Vapor custom-element)
155
+ ```
156
+
157
+ ### IIFE / CDN variants
158
+
159
+ Three `<script>`-tag drop-ins, sized for distinct deployment shapes. Pick by
160
+ audience, not by feature checklist.
161
+
162
+ | Variant | Audience | File | Min | Brotli | Gzip |
163
+ |-----------|----------------------------------------------|---------------------------------------|-------|--------|--------|
164
+ | core | Sprinkled JS on server-rendered pages (Blade, Rails, Django, .NET MVC, WordPress). You dispatch user actions to a backend over HTTP. | `vapor-chamber-core.iife.js` | 23 KB | 6.1 KB | 6.8 KB |
165
+ | elements | Embeddable widgets (chat bubbles, checkout buttons, third-party drop-ins). You ship a `<vc-widget>` custom element. | `vapor-chamber-elements.iife.js` | 24 KB | 6.4 KB | 7.2 KB |
166
+ | full | SPAs that grew big enough to want everything (realtime, undo/redo, persistence, full Vapor surface). | `vapor-chamber.iife.js` | 32 KB | 8.7 KB | 9.8 KB |
167
+
168
+ **What's in each variant**
169
+
170
+ | Surface | core | elements | full |
171
+ |----------------------------------------------------------|:----:|:--------:|:----:|
172
+ | Bus (`createCommandBus`, `createAsyncCommandBus`) | ✅ | ✅ | ✅ |
173
+ | `createApp()`, `connect()` one-liner | ✅ | ✅ | ✅ |
174
+ | HTTP transport (`http`) | ✅ | ✅ | ✅ |
175
+ | Light plugins (logger, validator, debounce, throttle, retry, authGuard) | ✅ | ✅ | ✅ |
176
+ | `defineVaporCustomElement`, `defineWidget()` | ❌ | ✅ | ✅ |
177
+ | WebSocket / SSE (`ws`, `sse`) | ❌ | ❌ | ✅ |
178
+ | Heavy plugins (persist, sync, history, optimistic) | ❌ | ❌ | ✅ |
179
+ | `mount()` | ❌ | ❌ | ✅ |
180
+ | Full Vapor (`defineVaporComponent`, async/Suspense) | ❌ | ❌ | ✅ |
181
+
182
+ **Examples**
183
+
184
+ ```html
185
+ <!-- core: dispatch over HTTP, with CSRF auto-wired -->
186
+ <script src=".../vapor-chamber-core.iife.min.js"></script>
187
+ <script>
188
+ const { dispatch } = VaporChamber.connect({ endpoint: '/api/vc' });
189
+ document.getElementById('add').addEventListener('click',
190
+ () => dispatch('cartAdd', { id: 42 }));
191
+ </script>
149
192
  ```
150
193
 
194
+ ```html
195
+ <!-- elements: register a custom-element widget in one call -->
196
+ <script src=".../vapor-chamber-elements.iife.min.js"></script>
197
+ <script>
198
+ VaporChamber.defineWidget('vc-cart', {
199
+ props: { sku: String },
200
+ setup(props) { return () => h('span', `SKU ${props.sku}`); }
201
+ });
202
+ </script>
203
+ <vc-cart sku="ABC-123"></vc-cart>
204
+ ```
205
+
206
+ (Sizes measured against v1.2.0; min = `*.iife.min.js`. Brotli @ q=11, gzip @ -9.
207
+ Modern CDNs serve brotli to every supported browser, so brotli is the number
208
+ that hits the wire.) The build is driven by `scripts/build.mjs` (Vite programmatic
209
+ API) — single source, multiple outputs.
210
+
211
+ > **Variant contents are not under semver before v2.0.** While Vue 3.6 is in
212
+ > beta, the lib reserves the right to move APIs between IIFE variants. ESM
213
+ > consumers (the `vapor-chamber` main entry) get the full surface and are
214
+ > unaffected. See [ROADMAP.md](./ROADMAP.md).
215
+
151
216
  ---
152
217
 
153
218
  ## Install
@@ -156,7 +221,27 @@ Sub-path exports avoid pulling in optional modules:
156
221
  npm install vapor-chamber
157
222
  ```
158
223
 
159
- **Requirements:** Node.js ≥20.19.0 | Vue ≥3.5.0 (optional peer dep) | Vite 7/8 compatible
224
+ **Requirements:** Node.js ≥20.19.0 | Vue ≥3.5.0 (composables) or ≥3.6.0-beta.11 (full Vapor) — optional peer dep | Vite ≥7.0.0 + `@vitejs/plugin-vue` ≥5.0.0 (only required for the `vapor-chamber/vite` HMR plugin and Vapor SFC support)
225
+
226
+ > **Beta tracking:** this lib follows Vue 3.6 while it is in beta. The Vapor
227
+ > wrappers and the `useVaporCommand` / `useCommand` split are transitional
228
+ > surfaces that will realign once Vue 3.6 ships stable. See [ROADMAP.md](./ROADMAP.md)
229
+ > for what is stable today, what is transitional, and the v1.3 / v2 plan.
230
+ >
231
+ > **Performance & tuning:** see [docs/performance.md](./docs/performance.md)
232
+ > for what's optimized by default, the consumer-facing tuning knobs
233
+ > (`persist({ coalesce: true })`, `configureUid`, `configureSignal`), variant
234
+ > selection guide, and a benchmark snapshot.
235
+ >
236
+ > **Laravel integration:** see [docs/integrations/laravel.md](./docs/integrations/laravel.md)
237
+ > for the full backend deliverables list (route, controller, action classes,
238
+ > CSRF flows, Sanctum, Inertia coexistence, Filament panels, Reverb
239
+ > realtime, queued commands). Runnable PHP companions live in
240
+ > [examples/laravel-backend/](./examples/laravel-backend/).
241
+ >
242
+ > **API reference:** generate locally with `npm run docs` (TypeDoc → `docs/api/`).
243
+ > The generated site is `.gitignore`d so it stays fresh per release. Hosting it
244
+ > publicly (e.g. via GitHub Pages or Netlify) is on the v1.3 ROADMAP.
160
245
 
161
246
  ## Quick Start
162
247
 
@@ -821,9 +906,9 @@ Dispatch multiple commands as a unit. Stops on the first failure by default:
821
906
 
822
907
  ```typescript
823
908
  const result = bus.dispatchBatch([
824
- { action: 'cartAdd', target: cart, payload: item },
825
- { action: 'totalsUpdate', target: cart },
826
- { action: 'analyticsTrack', target: session, payload: item },
909
+ { action: 'cartAdd', target: cart, payload: item },
910
+ { action: 'totalsUpdate', target: cart },
911
+ { action: 'telemetryLog', target: session, payload: item },
827
912
  ]);
828
913
 
829
914
  if (result.ok) {
@@ -913,14 +998,16 @@ const { dispatch, loading, lastError } = useCommand();
913
998
  ### defineVaporCommand
914
999
 
915
1000
  Zero-overhead dispatch for hot paths — no reactive `loading`/`lastError` signals created.
916
- Ideal for GA4 tracking, scroll events, debounced search, fire-and-forget patterns:
1001
+ Ideal for fire-and-forget patterns: telemetry events, scroll-position sampling,
1002
+ debounced search, autosave — anywhere reactive loading state would be wasted overhead:
917
1003
 
918
1004
  ```vue
919
1005
  <script setup vapor>
920
1006
  import { defineVaporCommand } from 'vapor-chamber';
921
1007
 
922
- const { dispatch } = defineVaporCommand('analyticsTrack', (cmd) => {
923
- gtag('event', cmd.target.event, cmd.target.params);
1008
+ const { dispatch } = defineVaporCommand('telemetryEvent', (cmd) => {
1009
+ // forward to whatever metrics / analytics SDK you use
1010
+ sendMetric(cmd.target.name, cmd.target.params);
924
1011
  });
925
1012
 
926
1013
  // Fire-and-forget — no reactive overhead in the alien-signals graph
@@ -1245,7 +1332,8 @@ See the [`examples/`](./examples) folder for complete, runnable examples:
1245
1332
 
1246
1333
  | Composable | Description |
1247
1334
  |------------|-------------|
1248
- | `useCommand()` | Dispatch with reactive loading/error state |
1335
+ | `useCommand()` | Dispatch with reactive loading/error state (per-call signals) |
1336
+ | `useSharedCommandState(options?)` | Aggregate `isAnyLoading` + `errors` ring buffer, **shared** across every subscriber on the same bus. Use for toolbars, status bars, global spinners. Saves ~2 signal nodes per subscribing component. |
1249
1337
  | `useVaporCommand()` | Vapor-safe composable with dispatch, register, on, loading/error, auto-cleanup |
1250
1338
  | `defineVaporCommand(action, handler, options?)` | Zero-overhead dispatch for hot paths |
1251
1339
  | `useCommandState(initial, handlers)` | State managed by commands |
package/ROADMAP.md ADDED
@@ -0,0 +1,233 @@
1
+ # Roadmap
2
+
3
+ This project tracks Vue 3.6 while it is in beta. That has direct consequences
4
+ for what's stable, what's transitional, and what will change once Vue 3.6
5
+ ships stable. This file is the source of truth for that distinction.
6
+
7
+ Last reviewed against **Vue 3.6.0-beta.11** (released 2026-05-07).
8
+
9
+ ---
10
+
11
+ ## Current posture: beta territory
12
+
13
+ - **Peer dependency:** `vue: ">=3.5.0 || >=3.6.0-beta.11"`. The lib supports
14
+ Vue 3.5 (composables only) and Vue 3.6 betas (full Vapor surface).
15
+ - **Vapor APIs are still moving.** `defineVaporCustomElement`, `defineVaporComponent`,
16
+ `defineVaporAsyncComponent` are stable in shape but their underlying behavior
17
+ has shifted between beta.10 and beta.11 (generics inference, emits/attrs split,
18
+ custom-element fallback in shadowRoot:false trees). The lib's wrappers are
19
+ pass-through, so consumers inherit each beta's improvements without code
20
+ changes — but the wrappers themselves exist precisely because the API is
21
+ not yet final.
22
+ - **The lib's value during beta** is graceful degradation (`null` returns when
23
+ Vue's API is absent or not yet present), version probing (`isVaporAvailable`),
24
+ and a stable surface for consumers to code against while Vue itself iterates.
25
+
26
+ ## What is stable, regardless of Vue's beta cycle
27
+
28
+ These layers are framework-agnostic and will not change shape across
29
+ Vue 3.6 stable:
30
+
31
+ - **Command bus** — `createCommandBus`, `createAsyncCommandBus`, plugins,
32
+ hooks, before-hooks, wildcard listeners, request/response, batch, query,
33
+ emit, meta, BusError, introspection.
34
+ - **Transports** — HTTP, WebSocket, SSE bridges. Independent of Vue.
35
+ - **Plugins** — logger, validator, history, debounce, throttle, authGuard,
36
+ optimistic, retry, persist, sync, cache, circuitBreaker, rateLimit, metrics.
37
+ - **Schema / LLM layer** — bus → tool-call adapters for Anthropic / OpenAI.
38
+ - **Form bus** — reactive form state with async validation.
39
+ - **HTTP client** — fetch wrapper with CSRF, interceptors, dedup.
40
+ - **Testing utilities** — createTestBus, snapshot, time-travel.
41
+ - **`defineVaporCommand`** — the zero-overhead command dispatch primitive
42
+ has no Vue equivalent and stays.
43
+ - **IIFE distribution** — three sized variants (core / elements / full)
44
+ matching Vue's tree-shake axes. Stable shape.
45
+
46
+ ## What is transitional and will realign post-3.6-stable
47
+
48
+ Everything below exists primarily to bridge the 3.5→3.6 gap. None will be
49
+ removed silently — each gets a deprecation cycle with a working escape hatch.
50
+
51
+ ### `useVaporCommand` and `useCommand` will converge
52
+
53
+ The split exists because pre-3.6, `getCurrentInstance()`-based cleanup fails
54
+ in Vapor components. By Vue 3.6 stable, `onScopeDispose` works in every
55
+ component context, so `useCommand` can be Vapor-safe on its own.
56
+
57
+ **Plan (v1.3 or v2):** Fold `useVaporCommand` into `useCommand`. Single
58
+ composable, `onScopeDispose`-only cleanup. `useVaporCommand` becomes a
59
+ deprecated re-export of `useCommand` for one minor cycle, then removed.
60
+
61
+ **Removes:** ~60 lines of duplicated logic, plus the "which one do I use?"
62
+ question from the docs.
63
+
64
+ ### Thin Vapor wrappers will become opt-in via build flag
65
+
66
+ `defineVaporComponent`, `defineVaporCustomElement`, `defineVaporAsyncComponent`,
67
+ and `createVaporChamberApp` exist to provide a `null`-returning safety surface
68
+ when Vue's API is not present. After Vue 3.6 stable, that null path is dead
69
+ code for any consumer who has Vue ≥ 3.6 in their dependency tree.
70
+
71
+ **Plan (v1.3 cutover):** Ship two flavors from one source via Vite build flag
72
+ + `package.json` conditional exports.
73
+
74
+ ```
75
+ src/wrapper.ts:
76
+ const HAS_NATIVE_VAPOR = /* #__PURE__ */ __VAPOR_NATIVE__;
77
+ export function defineVaporComponent(options) {
78
+ if (HAS_NATIVE_VAPOR) return options; // DCE drops this branch
79
+ const fn = getDefineVaporComponentFn();
80
+ if (!fn) return null;
81
+ return fn(options);
82
+ }
83
+
84
+ scripts/build.mjs:
85
+ build with __VAPOR_NATIVE__ = false → dist/index.js (legacy/beta)
86
+ build with __VAPOR_NATIVE__ = true → dist/index.modern.js (3.6 stable+)
87
+
88
+ package.json#exports:
89
+ ".": {
90
+ "vue36": "./dist/index.modern.js",
91
+ "import": "./dist/index.js",
92
+ "types": "./dist/index.d.ts"
93
+ }
94
+ ```
95
+
96
+ Consumers on Vue 3.6 stable add `vue36` to their Vite `resolve.conditions`
97
+ once and the wrapper bodies + entire feature-detection registry in
98
+ `chamber.ts` (`getDefineVaporComponentFn`, `_defineVaporCustomElementFn`, etc.)
99
+ are tree-shaken to zero.
100
+
101
+ **No source split, no API breakage.** The wrapper functions still exist by
102
+ name in both flavors — the modern flavor just inlines them as identity calls.
103
+
104
+ ### Runtime feature-detection registry will shrink
105
+
106
+ `chamber.ts` currently maintains a registry of probed Vue functions
107
+ (`_defineVaporCustomElementFn`, `_vueOnScopeDispose`, `_vueOnUnmounted`,
108
+ `_vueOnActivated`, `_vueOnDeactivated`, etc.). Each entry exists because the
109
+ specific Vue version may or may not have it.
110
+
111
+ **Plan (post-3.6-stable):** When `vue36` build flavor is active, the registry
112
+ collapses to a direct `import { onScopeDispose, ... } from 'vue'`. No more
113
+ property probing, no more null guards. Still tree-shaken when unused.
114
+
115
+ ### `createVaporChamberApp` will become a soft-deprecated convenience
116
+
117
+ It throws nicer than `createVaporApp` would when Vue Vapor is absent. Useful
118
+ during beta for discoverability. Post-stable, point users at `import { createVaporApp } from 'vue'` directly.
119
+
120
+ **Plan:** JSDoc `@deprecated` in v1.3, working through v2.
121
+
122
+ ## Variant contents are not under semver before v2.0
123
+
124
+ The IIFE variants (`core`, `elements`, `full`) are split along **audience /
125
+ deployment-shape** axes — sprinkled JS, embeddable widgets, kitchen-sink SPAs.
126
+ While Vue 3.6 is in beta, the lib reserves the right to move APIs between
127
+ variants. Concretely:
128
+
129
+ - An API that lives in `core` today may move to `full` in a later v1.x release
130
+ if usage data or audience clarification suggests it doesn't fit the variant's
131
+ identity. Example: WebSocket / SSE bridges moved out of `core` in v1.2.0
132
+ because realtime is a different deployment shape than sprinkled-JS.
133
+ - A new API may appear in `core` that wasn't there before, if it's idiomatic
134
+ for the audience. Example: `connect()` was added in v1.2.0 as a one-liner
135
+ for the sprinkled-JS audience.
136
+ - ESM consumers (the `vapor-chamber` main entry) are unaffected — the main
137
+ entry exposes the union of all variants and obeys strict semver.
138
+
139
+ This contract relaxes at v2.0: once Vue 3.6 ships stable and consumer
140
+ deployment patterns are observable, variant boundaries become semver-stable.
141
+ Until then, treat IIFE variant *names* as stable but variant *contents* as
142
+ beta-era refinement.
143
+
144
+ If you pin to a specific variant's API surface, do so against `dist/` in your
145
+ own infrastructure, not the public CDN. The full surface is always in `full`.
146
+
147
+ ## Two doorways: general bus and fast lane
148
+
149
+ The lib ships **two dispatch paths** under the same package, with deliberately
150
+ different shapes:
151
+
152
+ - **`createCommandBus()` — general purpose.** Command envelope, CommandResult,
153
+ plugin chain, before/after hooks, listeners (exact + wildcard), schema,
154
+ batch with rollback, request/response, AbortController, persist/sync/retry,
155
+ HTTP/WS/SSE transports, Vapor wrappers. Ergonomics-first. Use for app-level
156
+ commands.
157
+ - **`createFastLane()` (`vapor-chamber/fast-lane`) — real-real-hot path.**
158
+ Strips everything: no envelope, no result, no plugins, no hooks, no
159
+ wildcards, no abort. Just `compile(action, handler)` returning a
160
+ callable, plus `on`/`emit` for fan-out. Use for per-frame game ticks,
161
+ trading data feeds, audio buffer processing, scroll/mousemove sampling,
162
+ physics steps. ~36× faster than `bus.dispatch` (25,400 vs 700 ops/sec on
163
+ the 10k-dispatch bench).
164
+
165
+ The two are not interchangeable. The fast lane is **not** a faster bus —
166
+ it's a different tool for a different workload. Don't reach for it because
167
+ it's faster; reach for it because you've measured the general bus as a
168
+ bottleneck on a hot loop.
169
+
170
+ See [docs/performance.md](./docs/performance.md) for the full positioning,
171
+ benchmark numbers, and decision tree.
172
+
173
+ ## What is not on the roadmap
174
+
175
+ - **Directives in Vapor.** The Vue team has consistently signaled directives
176
+ remain a VDOM-only feature. The lib's `createDirectivePlugin` will stay as
177
+ a VDOM helper; no Vapor-port effort planned. Directives are not in the
178
+ growth path.
179
+ - **Forking Vue internals.** The lib intentionally wraps Vue's public API
180
+ and detects features at runtime. Bundling polyfills or forking compiler
181
+ output is out of scope.
182
+ - **A full SFC-aware HMR replacement.** `vite-hmr.ts` will keep tracking
183
+ `@vitejs/plugin-vue` rather than re-implementing HMR.
184
+
185
+ ## Version targets
186
+
187
+ | Version | Trigger | Headline |
188
+ |---------|----------------------------------|--------------------------------------------------------------------------|
189
+ | v1.2.x | Vue 3.6.0-beta.11+ | Beta-aligned: docs, build pipeline, IIFE split, regression tests, V8-aligned hot path, listener bucketing, persist coalescing |
190
+ | v1.3.0 | First Vue 3.6 RC or stable | Build-flag wrapper elimination; `vue36` conditional export; soft-deprecations begin; Rolldown migration if stable in Vite 8; `AbortController` extensions (request/respond, dispatchBatch, child signals, WS/SSE bridge propagation); protocol-aware `createEchoBridge` for Laravel Reverb / Echo (channels / private / presence) |
191
+ | v2.0.0 | One minor cycle after 3.6 stable | Drop `useVaporCommand` (folded into `useCommand`); registry collapse; remove wrappers' null path |
192
+
193
+ ## Vite + plugin-vue alignment
194
+
195
+ The library is currently aligned to **Vite ≥ 7.0.0** and **@vitejs/plugin-vue
196
+ ≥ 5.0.0**. Both are declared as optional peerDependencies — they only matter
197
+ if a consumer uses the `vapor-chamber/vite` HMR plugin or compiles Vue SFCs
198
+ that target Vapor mode.
199
+
200
+ **Tracking forward:**
201
+
202
+ - **Vite 8 + Rolldown.** Vite 8 (expected late 2026) is anticipated to ship
203
+ with Rolldown — a Rust-based Rollup successor — as the default bundler. The
204
+ build pipeline ([scripts/build.mjs](./scripts/build.mjs)) uses Vite's
205
+ programmatic `build()` API which is stable across Rolldown's migration; no
206
+ source changes are anticipated. We'll re-measure IIFE sizes after the swap
207
+ and update README numbers if they shift materially.
208
+ - **plugin-vue 6.x.** Expected alongside Vue 3.6 stable. Will be tested
209
+ before bumping the peerDep range.
210
+ - **Lightning CSS.** Vite's CSS pipeline doesn't affect vapor-chamber (the
211
+ lib emits no CSS), so no action needed.
212
+
213
+ Versioning is semver-strict: the v2 changes only happen behind a major bump
214
+ because the deprecations land first in v1.3 with at least one release cycle
215
+ of warnings.
216
+
217
+ ## How to read this file
218
+
219
+ If you're a consumer choosing between APIs in this lib:
220
+
221
+ - **Stable today, stable in v2:** the "stable, regardless of Vue's beta cycle"
222
+ list above. Use freely.
223
+ - **Working today, will be reshaped in v2:** the "transitional" list. Use, but
224
+ expect a deprecation cycle. The escape hatch will always exist for one minor
225
+ before removal.
226
+ - **Avoid:** anything not listed above is internal. The `_*` prefixed and
227
+ `getXxxFn()` exports in `chamber.ts` are explicitly internal.
228
+
229
+ If you're contributing: the build-flag wrapper-elimination work is the single
230
+ biggest pending change. It's blocked on Vue 3.6 RC — no need to land it in beta.
231
+
232
+ For performance characteristics, optimization philosophy, and tuning options
233
+ see [docs/performance.md](./docs/performance.md).
@@ -0,0 +1,80 @@
1
+ /**
2
+ * vapor-chamber — alien-signals connector.
3
+ *
4
+ * Bridges [alien-signals](https://github.com/stackblitz/alien-signals)'
5
+ * function-call API to vapor-chamber's `.value`-style `Signal` interface.
6
+ *
7
+ * ## Why this exists
8
+ *
9
+ * Vue 3.6's `ref()` is itself a port of alien-signals' algorithm
10
+ * ([vuejs/core#12349](https://github.com/vuejs/core/pull/12349)) — so when
11
+ * vapor-chamber auto-detects `vue.ref`, you're already on alien-signals
12
+ * under the hood. This connector is for **non-Vue consumers** who want
13
+ * the same fine-grained reactivity:
14
+ *
15
+ * • SSR / Node services that don't import Vue
16
+ * • Web Workers / service workers
17
+ * • Embedded widgets shipping without Vue
18
+ * • Any context where you want push-pull reactivity but Vue's full
19
+ * runtime is overkill
20
+ *
21
+ * ## No runtime dep
22
+ *
23
+ * The connector takes alien-signals' `signal` function as an argument
24
+ * rather than importing it. Consumers install `alien-signals` themselves;
25
+ * vapor-chamber stays Vue-agnostic on the runtime side.
26
+ *
27
+ * @example
28
+ * import { signal as alienSignal } from 'alien-signals';
29
+ * import { configureAlienSignals } from 'vapor-chamber/alien-signals';
30
+ *
31
+ * configureAlienSignals(alienSignal);
32
+ *
33
+ * // From this point on, every vapor-chamber signal() call wraps an
34
+ * // alien-signal under the hood. useCommand, useSharedCommandState, the
35
+ * // FormBus signals — all backed by alien-signals' propagation algorithm.
36
+ */
37
+ import { type CreateSignal } from './signal';
38
+ /**
39
+ * The shape alien-signals' `signal` function exposes. Both reading
40
+ * (`s()`) and writing (`s(value)`) go through the same callable.
41
+ *
42
+ * Defined locally so the connector has no `import 'alien-signals'`
43
+ * dependency — consumers feed in the function from their own install.
44
+ */
45
+ export type AlienSignalFn = <T>(initial?: T) => {
46
+ /** Read */ (): T;
47
+ /** Write */ (next: T): T;
48
+ };
49
+ /**
50
+ * Build a `CreateSignal` adapter from alien-signals' `signal` function.
51
+ * The returned function is what `configureSignal()` expects — it produces
52
+ * vapor-chamber-style `{ value }` objects backed by an alien-signal.
53
+ *
54
+ * Use this when you want manual control. For the typical case, prefer
55
+ * {@link configureAlienSignals} which installs the adapter directly.
56
+ *
57
+ * @example
58
+ * import { signal as alienSignal } from 'alien-signals';
59
+ * import { configureSignal } from 'vapor-chamber';
60
+ * import { alienSignalAdapter } from 'vapor-chamber/alien-signals';
61
+ *
62
+ * configureSignal(alienSignalAdapter(alienSignal));
63
+ */
64
+ export declare function alienSignalAdapter(alienSignal: AlienSignalFn): CreateSignal;
65
+ /**
66
+ * Install alien-signals as the underlying reactive primitive for every
67
+ * vapor-chamber signal. Call once at app startup, before any `signal()`,
68
+ * `useCommand()`, `useSharedCommandState()`, or `createFormBus()` call.
69
+ *
70
+ * @example
71
+ * import { signal as alienSignal } from 'alien-signals';
72
+ * import { configureAlienSignals } from 'vapor-chamber/alien-signals';
73
+ *
74
+ * configureAlienSignals(alienSignal);
75
+ *
76
+ * // Now use vapor-chamber composables / signal() normally — they're
77
+ * // backed by alien-signals' push-pull propagation.
78
+ */
79
+ export declare function configureAlienSignals(alienSignal: AlienSignalFn): void;
80
+ //# sourceMappingURL=alien-signals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alien-signals.d.ts","sourceRoot":"","sources":["../src/alien-signals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,EAAgC,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAE3E;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK;IAC9C,WAAW,CAAC,IAAI,CAAC,CAAC;IAClB,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,aAAa,GAAG,YAAY,CAQ3E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CAAC,WAAW,EAAE,aAAa,GAAG,IAAI,CAEtE"}
@@ -0,0 +1,22 @@
1
+ import { c as configureSignal } from "./signal-DUZrcb-E.js";
2
+ function alienSignalAdapter(alienSignal) {
3
+ return (initial) => {
4
+ const s = alienSignal(initial);
5
+ return {
6
+ get value() {
7
+ return s();
8
+ },
9
+ set value(next) {
10
+ s(next);
11
+ }
12
+ };
13
+ };
14
+ }
15
+ function configureAlienSignals(alienSignal) {
16
+ configureSignal(alienSignalAdapter(alienSignal));
17
+ }
18
+ export {
19
+ alienSignalAdapter,
20
+ configureAlienSignals
21
+ };
22
+ //# sourceMappingURL=alien-signals.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alien-signals.js","sources":["../src/alien-signals.ts"],"sourcesContent":["/**\n * vapor-chamber — alien-signals connector.\n *\n * Bridges [alien-signals](https://github.com/stackblitz/alien-signals)'\n * function-call API to vapor-chamber's `.value`-style `Signal` interface.\n *\n * ## Why this exists\n *\n * Vue 3.6's `ref()` is itself a port of alien-signals' algorithm\n * ([vuejs/core#12349](https://github.com/vuejs/core/pull/12349)) — so when\n * vapor-chamber auto-detects `vue.ref`, you're already on alien-signals\n * under the hood. This connector is for **non-Vue consumers** who want\n * the same fine-grained reactivity:\n *\n * • SSR / Node services that don't import Vue\n * • Web Workers / service workers\n * • Embedded widgets shipping without Vue\n * • Any context where you want push-pull reactivity but Vue's full\n * runtime is overkill\n *\n * ## No runtime dep\n *\n * The connector takes alien-signals' `signal` function as an argument\n * rather than importing it. Consumers install `alien-signals` themselves;\n * vapor-chamber stays Vue-agnostic on the runtime side.\n *\n * @example\n * import { signal as alienSignal } from 'alien-signals';\n * import { configureAlienSignals } from 'vapor-chamber/alien-signals';\n *\n * configureAlienSignals(alienSignal);\n *\n * // From this point on, every vapor-chamber signal() call wraps an\n * // alien-signal under the hood. useCommand, useSharedCommandState, the\n * // FormBus signals — all backed by alien-signals' propagation algorithm.\n */\n\nimport { configureSignal, type Signal, type CreateSignal } from './signal';\n\n/**\n * The shape alien-signals' `signal` function exposes. Both reading\n * (`s()`) and writing (`s(value)`) go through the same callable.\n *\n * Defined locally so the connector has no `import 'alien-signals'`\n * dependency — consumers feed in the function from their own install.\n */\nexport type AlienSignalFn = <T>(initial?: T) => {\n /** Read */ (): T;\n /** Write */ (next: T): T;\n};\n\n/**\n * Build a `CreateSignal` adapter from alien-signals' `signal` function.\n * The returned function is what `configureSignal()` expects — it produces\n * vapor-chamber-style `{ value }` objects backed by an alien-signal.\n *\n * Use this when you want manual control. For the typical case, prefer\n * {@link configureAlienSignals} which installs the adapter directly.\n *\n * @example\n * import { signal as alienSignal } from 'alien-signals';\n * import { configureSignal } from 'vapor-chamber';\n * import { alienSignalAdapter } from 'vapor-chamber/alien-signals';\n *\n * configureSignal(alienSignalAdapter(alienSignal));\n */\nexport function alienSignalAdapter(alienSignal: AlienSignalFn): CreateSignal {\n return <T>(initial: T): Signal<T> => {\n const s = alienSignal<T>(initial);\n return {\n get value(): T { return s() as T; },\n set value(next: T) { s(next); },\n };\n };\n}\n\n/**\n * Install alien-signals as the underlying reactive primitive for every\n * vapor-chamber signal. Call once at app startup, before any `signal()`,\n * `useCommand()`, `useSharedCommandState()`, or `createFormBus()` call.\n *\n * @example\n * import { signal as alienSignal } from 'alien-signals';\n * import { configureAlienSignals } from 'vapor-chamber/alien-signals';\n *\n * configureAlienSignals(alienSignal);\n *\n * // Now use vapor-chamber composables / signal() normally — they're\n * // backed by alien-signals' push-pull propagation.\n */\nexport function configureAlienSignals(alienSignal: AlienSignalFn): void {\n configureSignal(alienSignalAdapter(alienSignal));\n}\n"],"names":[],"mappings":";AAkEO,SAAS,mBAAmB,aAA0C;AAC3E,SAAO,CAAI,YAA0B;AACnC,UAAM,IAAI,YAAe,OAAO;AAChC,WAAO;AAAA,MACL,IAAI,QAAW;AAAE,eAAO,EAAA;AAAA,MAAU;AAAA,MAClC,IAAI,MAAM,MAAS;AAAE,UAAE,IAAI;AAAA,MAAG;AAAA,IAAA;AAAA,EAElC;AACF;AAgBO,SAAS,sBAAsB,aAAkC;AACtE,kBAAgB,mBAAmB,WAAW,CAAC;AACjD;"}