@rotorsoft/act-sse 1.2.2 → 1.2.4

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/README.md CHANGED
@@ -4,181 +4,60 @@
4
4
  [![NPM Downloads](https://img.shields.io/npm/dm/@rotorsoft/act-sse.svg)](https://www.npmjs.com/package/@rotorsoft/act-sse)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
6
6
 
7
- Incremental state broadcast over SSE for [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act) event-sourced apps. Zero dependencies.
7
+ _Incremental state broadcast over Server-Sent Events for [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act) event-sourced apps._
8
8
 
9
- Instead of sending full aggregate state after each action, `act-sse` forwards the domain patches that event handlers already compute — sending only what changed as version-keyed partials.
9
+ > [!WARNING]
10
+ > **This package is being deprecated.** Its surface lives on as the `@rotorsoft/act-http/sse` subpath of the [@rotorsoft/act-http](https://www.npmjs.com/package/@rotorsoft/act-http) umbrella package, which consolidates webhook and SSE integrations under one install.
11
+ >
12
+ > New projects: install `@rotorsoft/act-http` and import from the `/sse` subpath. Existing projects: migration is a one-import change (see below). This package will receive bug fixes only and be removed in a future release.
10
13
 
11
- ## Installation
14
+ ## Migration to `@rotorsoft/act-http/sse`
12
15
 
13
- ```sh
14
- npm install @rotorsoft/act-sse
15
- # or
16
- pnpm add @rotorsoft/act-sse
17
- ```
18
-
19
- **Requirements:** Node.js >= 22.18.0
20
-
21
- ## Architecture
22
-
23
- ```
24
- app.do() → Snapshot[] (each carries its event's domain patch)
25
-
26
-
27
- deriveState(snap) ← app-specific (overlay presence, deadlines, etc.)
28
- state._v = snap.event.version ← event store version is the single source of truth
29
-
30
-
31
- broadcast.publish(streamId, state, patches)
32
-
33
- └── version-key each patch → { "5": { count: 3 }, "6": { name: "updated" } }
34
- └── push to all SSE subscribers
35
-
36
-
37
- Client: applyPatchMessage(msg, cached)
38
-
39
- ├── contiguous → deep merge patches in version order
40
- ├── stale → skip (client already ahead)
41
- └── behind → invalidate + refetch (client missed versions)
42
- ```
43
-
44
- ## Wire Format
45
-
46
- ```typescript
47
- // Version-keyed domain patches
48
- // Keys = state version after that patch is applied
49
- // Values = domain patch (deep partial of state)
50
- {
51
- "5": { territories: { brazil: { armies: 3 } } },
52
- "6": { currentPlayerIndex: 2, phase: "reinforce" }
53
- }
54
- ```
16
+ `@rotorsoft/act-http/sse` is a verbatim copy of this package's surface — same classes, same functions, same wire format, same semantics. Migration is one import change per file:
55
17
 
56
- Multi-event commits produce multiple version-keyed entries. Version gaps trigger full state refetch on the client.
57
-
58
- ## Server Usage
59
-
60
- ```typescript
61
- import { BroadcastChannel } from "@rotorsoft/act-sse";
62
-
63
- const broadcast = new BroadcastChannel<MyAppState>();
64
-
65
- // After every app.do():
66
- const snaps = await app.do(action, target, payload);
67
- const snap = snaps.at(-1)!;
68
- const patches = snaps.map(s => s.patch).filter(Boolean);
69
- const state = deriveState(snap);
70
- broadcast.publish(streamId, state, patches);
71
-
72
- // For non-event state changes (e.g. presence overlay):
73
- broadcast.publishOverlay(streamId, { players: { pid: { connected: true } } });
74
-
75
- // SSE subscription (tRPC example):
76
- onStateChange: publicProcedure
77
- .input(z.object({ streamId: z.string() }))
78
- .subscription(async function* ({ input, signal }) {
79
- let resolve: (() => void) | null = null;
80
- let pending: PatchMessage | null = null;
81
-
82
- const cleanup = broadcast.subscribe(input.streamId, (msg) => {
83
- pending = msg;
84
- if (resolve) { resolve(); resolve = null; }
85
- });
86
-
87
- try {
88
- while (!signal?.aborted) {
89
- if (!pending) {
90
- await new Promise<void>((r) => {
91
- resolve = r;
92
- signal?.addEventListener("abort", () => r(), { once: true });
93
- });
94
- }
95
- if (signal?.aborted) break;
96
- if (pending) { const msg = pending; pending = null; yield msg; }
97
- }
98
- } finally {
99
- cleanup();
100
- }
101
- }),
18
+ ```diff
19
+ - import { BroadcastChannel, applyPatchMessage } from "@rotorsoft/act-sse";
20
+ + import { BroadcastChannel, applyPatchMessage } from "@rotorsoft/act-http/sse";
102
21
  ```
103
22
 
104
- ## Client Usage
23
+ Then:
105
24
 
106
- ```typescript
107
- import { applyPatchMessage } from "@rotorsoft/act-sse";
108
-
109
- // In your SSE onData handler (React Query example):
110
- onData: (msg) => {
111
- const cached = utils.getState.getData({ streamId });
112
- const result = applyPatchMessage(msg, cached);
113
-
114
- if (result.ok) {
115
- utils.getState.setData({ streamId }, result.state);
116
- } else if (result.reason === "behind") {
117
- // Client missed versions — trigger full refetch
118
- utils.getState.invalidate({ streamId });
119
- }
120
- // "stale" → no-op (client already has newer state from mutation response)
121
- }
25
+ ```bash
26
+ pnpm remove @rotorsoft/act-sse
27
+ pnpm add @rotorsoft/act-http
122
28
  ```
123
29
 
124
- ## API
125
-
126
- ### `BroadcastChannel<S>`
127
-
128
- Server-side broadcast manager with per-stream subscriber channels and LRU state cache.
129
-
130
- | Method | Description |
131
- |--------|-------------|
132
- | `publish(streamId, state, patches?)` | Cache state, version-key patches, push to subscribers |
133
- | `publishOverlay(streamId, patch)` | Same-version update (e.g. presence change) |
134
- | `subscribe(streamId, cb)` | Register subscriber, returns cleanup function |
135
- | `getState(streamId)` | Get cached state (for reconnects) |
136
- | `getSubscriberCount(streamId)` | Number of active subscribers |
137
- | `cache` | Direct access to `StateCache` instance |
30
+ The umbrella package keeps SSE and `webhook` as independent subpath exports (`@rotorsoft/act-http/sse` and `@rotorsoft/act-http/webhook`) — nothing in one depends on the other, so the bundle cost is the same. Future HTTP-adjacent integrations (OAuth refresh, signed-webhook senders, gRPC-web) will land as additional subpaths rather than separate packages.
138
31
 
139
- ### `applyPatchMessage(msg, cached)`
32
+ ## What this package does (legacy)
140
33
 
141
- Client-side patch applicator. Returns `{ ok: true, state }` or `{ ok: false, reason }`.
34
+ Instead of sending full aggregate state after each action, `act-sse` forwards the domain patches that event handlers already compute — sending only what changed as version-keyed partials. The wire format, server surface (`BroadcastChannel`, `PresenceTracker`, `StateCache`, `publishOverlay`), and client patch applicator (`applyPatchMessage`) are all preserved unchanged in `@rotorsoft/act-http/sse`.
142
35
 
143
- Reasons: `"stale"` (skip), `"behind"` (resync).
36
+ For the full reference (architecture diagram, wire format, server + client usage, version contract, bandwidth savings), see the [@rotorsoft/act-http README](https://github.com/Rotorsoft/act-root/blob/master/libs/act-http/README.md#sse--incremental-state-broadcast).
144
37
 
145
- ### `patch(original, patches)`
38
+ ## Installation (legacy)
146
39
 
147
- Browser-safe deep merge utility. Deep merges plain objects, replaces arrays and other non-mergeable types (Date, Map, Set, RegExp, TypedArrays). Deletes keys set to `null` or `undefined`.
40
+ Still works. New projects should use `@rotorsoft/act-http` instead.
148
41
 
149
- ### `StateCache<S>`
150
-
151
- Generic LRU cache. Methods: `get`, `set`, `delete`, `has`, `size`, `entries`.
152
-
153
- ### Types
154
-
155
- ```typescript
156
- type BroadcastState = Record<string, unknown> & { _v: number };
157
- type PatchMessage<S extends BroadcastState> = Record<number, DeepPartial<S>>;
158
- type Subscriber<S extends BroadcastState> = (msg: PatchMessage<S>) => void;
42
+ ```bash
43
+ pnpm add @rotorsoft/act-sse
159
44
  ```
160
45
 
161
- ## Version Contract
162
-
163
- The `_v` field **must** be set from `snap.event.version` — the event store's monotonic stream version. This is the single source of truth for ordering. No separate version counters.
46
+ ## Stability
164
47
 
165
- ## Bandwidth Savings
48
+ Public API governed by the [Act Stability Charter](../../STABILITY.md) — but this package is on the deprecation track. New work goes to `@rotorsoft/act-http/sse`.
166
49
 
167
- Typical savings for a game/collaborative app with ~5KB state:
50
+ ## Related packages
168
51
 
169
- | Action | Full (bytes) | Patch (bytes) | Savings |
170
- |--------|-------------|--------------|---------|
171
- | Single field change | ~5,000 | ~150 | 97% |
172
- | Multi-field update | ~5,000 | ~400 | 92% |
173
- | Presence toggle | ~5,000 | ~80 | 98% |
52
+ - **[@rotorsoft/act-http](https://www.npmjs.com/package/@rotorsoft/act-http)** **migrate here.** The umbrella package that now owns SSE + webhook integrations.
53
+ - **[@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act)** — core framework.
54
+ - **[@rotorsoft/act-patch](https://www.npmjs.com/package/@rotorsoft/act-patch)** immutable patch utility this package uses for state merging.
174
55
 
175
- ## Related
56
+ ## Documentation
176
57
 
177
- - [@rotorsoft/act](https://www.npmjs.com/package/@rotorsoft/act) - Core framework
178
- - [@rotorsoft/act-pg](https://www.npmjs.com/package/@rotorsoft/act-pg) - PostgreSQL adapter
179
- - [Documentation](https://rotorsoft.github.io/act-root/)
180
- - [Examples](https://github.com/rotorsoft/act-root/tree/master/packages)
58
+ - **[Real-time with SSE](https://github.com/Rotorsoft/act-root/blob/master/docs/docs/concepts/real-time.md)** the concept guide; same content applies to `@rotorsoft/act-http/sse`.
59
+ - **[@rotorsoft/act-http README](https://github.com/Rotorsoft/act-root/blob/master/libs/act-http/README.md)** full SSE reference at its new home.
181
60
 
182
61
  ## License
183
62
 
184
- [MIT](https://github.com/rotorsoft/act-root/blob/master/LICENSE)
63
+ MIT
@@ -0,0 +1 @@
1
+ {"fileNames":["../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es5.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2016.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.dom.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.dom.iterable.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.dom.asynciterable.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.webworker.importscripts.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.scripthost.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.core.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.collection.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.generator.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.promise.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2016.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.date.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.object.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.promise.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.array.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.object.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.symbol.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2019.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.date.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.promise.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2020.number.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.promise.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.weakref.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2021.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.array.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.error.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.intl.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.object.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.string.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.regexp.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2025.float16.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.esnext.disposable.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.decorators.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.decorators.legacy.d.ts","../../../node_modules/.pnpm/typescript@6.0.3/node_modules/typescript/lib/lib.es2022.full.d.ts","../../act-patch/dist/@types/types.d.ts","../../act-patch/dist/@types/delta.d.ts","../../act-patch/dist/@types/patch.d.ts","../../act-patch/dist/@types/index.d.ts","../src/types.ts","../src/apply-patch.ts","../src/state-cache.ts","../src/broadcast.ts","../src/presence.ts","../src/index.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/compatibility/iterators.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/globals.typedarray.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/buffer.buffer.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/globals.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/abortcontroller.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/blob.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/console.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/crypto.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/domexception.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/encoding.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/events.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/utility.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/header.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/readable.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/fetch.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/formdata.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/connector.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/client-stats.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/client.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/errors.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/dispatcher.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/global-dispatcher.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/global-origin.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/pool-stats.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/pool.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/handlers.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/balanced-pool.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/round-robin-pool.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/h2c-client.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/agent.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/mock-interceptor.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/mock-call-history.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/mock-agent.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/mock-client.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/mock-pool.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/snapshot-agent.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/mock-errors.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/proxy-agent.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/socks5-proxy-agent.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/env-http-proxy-agent.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/retry-handler.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/retry-agent.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/api.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/cache-interceptor.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/interceptors.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/util.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/cookies.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/patch.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/websocket.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/eventsource.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/diagnostics-channel.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/content-type.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/cache.d.ts","../../../node_modules/.pnpm/undici-types@7.24.6/node_modules/undici-types/index.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/fetch.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/importmeta.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/messaging.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/navigator.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/performance.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/storage.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/streams.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/timers.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/web-globals/url.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/assert.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/assert/strict.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/async_hooks.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/buffer.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/child_process.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/cluster.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/console.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/constants.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/crypto.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/dgram.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/diagnostics_channel.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/dns.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/dns/promises.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/domain.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/events.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/fs.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/fs/promises.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/http.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/http2.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/https.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/inspector.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/inspector.generated.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/inspector/promises.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/module.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/net.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/os.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/path.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/path/posix.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/path/win32.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/perf_hooks.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/process.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/punycode.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/querystring.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/quic.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/readline.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/readline/promises.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/repl.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/sea.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/sqlite.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/stream.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/stream/consumers.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/stream/iter.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/stream/promises.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/stream/web.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/string_decoder.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/test.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/test/reporters.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/timers.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/timers/promises.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/tls.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/trace_events.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/tty.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/url.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/util.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/util/types.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/v8.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/vm.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/wasi.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/worker_threads.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/zlib.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/zlib/iter.d.ts","../../../node_modules/.pnpm/@types+node@25.9.1/node_modules/@types/node/index.d.ts","../../../node_modules/.pnpm/@vitest+pretty-format@4.1.8/node_modules/@vitest/pretty-format/dist/index.d.ts","../../../node_modules/.pnpm/@vitest+utils@4.1.8/node_modules/@vitest/utils/dist/display.d.ts","../../../node_modules/.pnpm/@vitest+utils@4.1.8/node_modules/@vitest/utils/dist/types.d.ts","../../../node_modules/.pnpm/@vitest+utils@4.1.8/node_modules/@vitest/utils/dist/helpers.d.ts","../../../node_modules/.pnpm/@vitest+utils@4.1.8/node_modules/@vitest/utils/dist/timers.d.ts","../../../node_modules/.pnpm/@vitest+utils@4.1.8/node_modules/@vitest/utils/dist/index.d.ts","../../../node_modules/.pnpm/@vitest+utils@4.1.8/node_modules/@vitest/utils/dist/types.d-BCElaP-c.d.ts","../../../node_modules/.pnpm/@vitest+utils@4.1.8/node_modules/@vitest/utils/dist/diff.d.ts","../../../node_modules/.pnpm/@vitest+runner@4.1.8/node_modules/@vitest/runner/dist/tasks.d-DEYaIMIu.d.ts","../../../node_modules/.pnpm/@vitest+runner@4.1.8/node_modules/@vitest/runner/dist/index.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/traces.d.D2T_R8rx.d.ts","../../../node_modules/.pnpm/vite@8.0.16_@types+node@25.9.1_esbuild@0.27.2_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.9.0/node_modules/vite/types/hmrPayload.d.ts","../../../node_modules/.pnpm/vite@8.0.16_@types+node@25.9.1_esbuild@0.27.2_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.9.0/node_modules/vite/dist/node/chunks/moduleRunnerTransport.d.ts","../../../node_modules/.pnpm/vite@8.0.16_@types+node@25.9.1_esbuild@0.27.2_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.9.0/node_modules/vite/types/customEvent.d.ts","../../../node_modules/.pnpm/vite@8.0.16_@types+node@25.9.1_esbuild@0.27.2_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.9.0/node_modules/vite/types/hot.d.ts","../../../node_modules/.pnpm/vite@8.0.16_@types+node@25.9.1_esbuild@0.27.2_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.9.0/node_modules/vite/dist/node/module-runner.d.ts","../../../node_modules/.pnpm/@vitest+snapshot@4.1.8/node_modules/@vitest/snapshot/dist/environment.d-DOJxxZV9.d.ts","../../../node_modules/.pnpm/@vitest+snapshot@4.1.8/node_modules/@vitest/snapshot/dist/rawSnapshot.d-D_X3-62x.d.ts","../../../node_modules/.pnpm/@vitest+snapshot@4.1.8/node_modules/@vitest/snapshot/dist/index.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/config.d.A1h_Y6Jt.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/environment.d.CrsxCzP1.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/rpc.d.B_8sPU0w.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/worker.d.ZpHpO4yb.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/browser.d.BcoexmFG.d.ts","../../../node_modules/.pnpm/@vitest+spy@4.1.8/node_modules/@vitest/spy/optional-types.d.ts","../../../node_modules/.pnpm/@vitest+spy@4.1.8/node_modules/@vitest/spy/dist/index.d.ts","../../../node_modules/.pnpm/tinyrainbow@3.1.0/node_modules/tinyrainbow/dist/index.d.ts","../../../node_modules/.pnpm/@standard-schema+spec@1.1.0/node_modules/@standard-schema/spec/dist/index.d.ts","../../../node_modules/.pnpm/@types+deep-eql@4.0.2/node_modules/@types/deep-eql/index.d.ts","../../../node_modules/.pnpm/assertion-error@2.0.1/node_modules/assertion-error/index.d.ts","../../../node_modules/.pnpm/@types+chai@5.2.3/node_modules/@types/chai/index.d.ts","../../../node_modules/.pnpm/@vitest+expect@4.1.8/node_modules/@vitest/expect/dist/index.d.ts","../../../node_modules/.pnpm/@vitest+runner@4.1.8/node_modules/@vitest/runner/dist/utils.d.ts","../../../node_modules/.pnpm/tinybench@2.9.0/node_modules/tinybench/dist/index.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/benchmark.d.DAaHLpsq.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/global.d.DVsSRdQ5.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/optional-runtime-types.d.ts","../../../node_modules/.pnpm/@vitest+mocker@4.1.8_vite@8.0.16_@types+node@25.9.1_esbuild@0.27.2_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.9.0_/node_modules/@vitest/mocker/dist/types.d-BjI5eAwu.d.ts","../../../node_modules/.pnpm/@vitest+mocker@4.1.8_vite@8.0.16_@types+node@25.9.1_esbuild@0.27.2_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.9.0_/node_modules/@vitest/mocker/dist/index.d-B41z0AuW.d.ts","../../../node_modules/.pnpm/@vitest+mocker@4.1.8_vite@8.0.16_@types+node@25.9.1_esbuild@0.27.2_jiti@2.6.1_terser@5.46.1_tsx@4.22.4_yaml@2.9.0_/node_modules/@vitest/mocker/dist/index.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/suite.d.udJtyAgw.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/runners.d.ts","../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/utils.d.ts","../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/overloads.d.ts","../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/branding.d.ts","../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/messages.d.ts","../../../node_modules/.pnpm/expect-type@1.3.0/node_modules/expect-type/dist/index.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/dist/index.d.ts","../../../node_modules/.pnpm/vitest@4.1.8_@types+node@25.9.1_@vitest+coverage-v8@4.1.8_vite@8.0.16_@types+node@25.9._87965f5280639f5a2754f2fd84ad1804/node_modules/vitest/globals.d.ts"],"fileIdsList":[[66,78,142,150,154,157,159,160,161,173],[66,67,68,78,142,150,154,157,159,160,161,173],[78,142,150,154,157,159,160,161,173],[69,70,78,142,150,154,157,159,160,161,173],[69,70,72,78,142,150,154,157,159,160,161,173],[69,70,71,72,73,74,78,142,150,154,157,159,160,161,173],[70,78,142,150,154,157,159,160,161,173],[69,78,142,150,154,157,159,160,161,173],[78,142,150,154,157,159,160,161,173,229,230],[78,139,140,142,150,154,157,159,160,161,173],[78,141,142,150,154,157,159,160,161,173],[142,150,154,157,159,160,161,173],[78,142,150,154,157,159,160,161,173,182],[78,142,143,148,150,153,154,157,159,160,161,163,173,178,191],[78,142,143,144,150,153,154,157,159,160,161,173],[78,142,145,150,154,157,159,160,161,173,192],[78,142,146,147,150,154,157,159,160,161,164,173],[78,142,147,150,154,157,159,160,161,173,178,188],[78,142,148,150,153,154,157,159,160,161,163,173],[78,141,142,149,150,154,157,159,160,161,173],[78,142,150,151,154,157,159,160,161,173],[78,142,150,152,153,154,157,159,160,161,173],[78,141,142,150,153,154,157,159,160,161,173],[78,142,150,153,154,155,157,159,160,161,173,178,191],[78,142,150,153,154,155,157,159,160,161,173,178,180,182],[78,129,142,150,153,154,156,157,159,160,161,163,173,178,191],[78,142,150,153,154,156,157,159,160,161,163,173,178,188,191],[78,142,150,154,156,157,158,159,160,161,173,178,188,191],[76,77,78,79,80,81,82,83,84,85,86,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199],[78,142,150,153,154,157,159,160,161,173],[78,142,150,154,157,159,161,173],[78,142,150,154,157,159,160,161,162,173,191],[78,142,150,153,154,157,159,160,161,163,173,178],[78,142,150,154,157,159,160,161,164,173],[78,142,150,154,157,159,160,161,165,173],[78,142,150,153,154,157,159,160,161,168,173],[78,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198],[78,142,150,154,157,159,160,161,170,173],[78,142,150,154,157,159,160,161,171,173],[78,142,147,150,154,157,159,160,161,163,173,182],[78,142,150,153,154,157,159,160,161,173,174],[78,142,150,154,157,159,160,161,173,175,192,195],[78,142,150,153,154,157,159,160,161,173,178,181,182],[78,142,150,154,157,159,160,161,173,179,182],[78,142,150,154,157,159,160,161,173,180],[78,142,150,154,157,159,160,161,173,182,192],[78,142,150,154,157,159,160,161,173,183],[78,139,142,150,154,157,159,160,161,173,178,185,191],[78,142,150,154,157,159,160,161,173,178,184],[78,142,150,153,154,157,159,160,161,173,186,187],[78,142,150,154,157,159,160,161,173,186,187],[78,142,147,150,154,157,159,160,161,163,173,178,188],[78,142,150,154,157,159,160,161,173,189],[78,142,150,154,157,159,160,161,163,173,190],[78,142,150,154,156,157,159,160,161,171,173,191],[78,142,150,154,157,159,160,161,173,192,193],[78,142,147,150,154,157,159,160,161,173,193],[78,142,150,154,157,159,160,161,173,178,194],[78,142,150,154,157,159,160,161,162,173,195],[78,142,150,154,157,159,160,161,173,196],[78,142,145,150,154,157,159,160,161,173],[78,142,147,150,154,157,159,160,161,173],[78,142,150,154,157,159,160,161,173,192],[78,129,142,150,154,157,159,160,161,173],[78,142,150,154,157,159,160,161,173,191],[78,142,150,154,157,159,160,161,173,197],[78,142,150,154,157,159,160,161,168,173],[78,142,150,154,157,159,160,161,173,187],[78,129,142,150,153,154,155,157,159,160,161,168,173,178,182,191,194,195,197],[78,142,150,154,157,159,160,161,173,178,198],[78,142,150,154,157,159,160,161,173,180,199],[78,142,150,154,157,159,160,161,173,202,208,226,227,228,231],[78,142,150,154,157,159,160,161,173,238],[78,142,150,154,157,159,160,161,173,238,239],[78,142,150,154,157,159,160,161,173,206,208,209],[78,142,150,154,157,159,160,161,173,206,208],[78,142,150,154,157,159,160,161,173,206],[78,142,150,154,157,159,160,161,173,201,206,217,218],[78,142,150,154,157,159,160,161,173,201,206,217],[78,142,150,154,157,159,160,161,173,225],[78,142,150,154,157,159,160,161,173,201,207],[78,142,150,154,157,159,160,161,173,201],[78,142,150,154,157,159,160,161,173,203],[78,142,150,154,157,159,160,161,173,201,202,203,204,205],[78,142,150,154,157,159,160,161,173,244,245],[78,142,150,154,157,159,160,161,173,244,245,246,247],[78,142,150,154,157,159,160,161,173,244,246],[78,142,150,154,157,159,160,161,173,244],[78,93,96,99,100,142,150,154,157,159,160,161,173,191],[78,96,142,150,154,157,159,160,161,173,178,191],[78,96,100,142,150,154,157,159,160,161,173,191],[78,142,150,154,157,159,160,161,173,178],[78,90,142,150,154,157,159,160,161,173],[78,94,142,150,154,157,159,160,161,173],[78,92,93,96,142,150,154,157,159,160,161,173,191],[78,142,150,154,157,159,160,161,163,173,188],[78,142,150,154,157,159,160,161,173,200],[78,90,142,150,154,157,159,160,161,173,200],[78,92,96,142,150,154,157,159,160,161,163,173,191],[78,87,88,89,91,95,142,150,153,154,157,159,160,161,173,178,191],[78,96,105,113,142,150,154,157,159,160,161,173],[78,88,94,142,150,154,157,159,160,161,173],[78,96,123,124,142,150,154,157,159,160,161,173],[78,88,91,96,142,150,154,157,159,160,161,173,182,191,200],[78,96,142,150,154,157,159,160,161,173],[78,92,96,142,150,154,157,159,160,161,173,191],[78,87,142,150,154,157,159,160,161,173],[78,90,91,92,94,95,96,97,98,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,124,125,126,127,128,142,150,154,157,159,160,161,173],[78,96,116,119,142,150,154,157,159,160,161,173],[78,96,105,106,107,142,150,154,157,159,160,161,173],[78,94,96,106,108,142,150,154,157,159,160,161,173],[78,95,142,150,154,157,159,160,161,173],[78,88,90,96,142,150,154,157,159,160,161,173],[78,96,100,106,108,142,150,154,157,159,160,161,173],[78,100,142,150,154,157,159,160,161,173],[78,94,96,99,142,150,154,157,159,160,161,173,191],[78,88,92,96,105,142,150,154,157,159,160,161,173],[78,96,116,142,150,154,157,159,160,161,173],[78,108,142,150,154,157,159,160,161,173],[78,88,92,96,100,142,150,154,157,159,160,161,173],[78,90,96,123,142,150,154,157,159,160,161,173,182,197,200],[78,142,150,154,157,159,160,161,173,212],[78,142,150,154,157,159,160,161,173,212,213,214,215],[78,142,150,154,157,159,160,161,173,214],[78,142,150,154,157,159,160,161,173,210,233,234,236],[78,142,150,154,157,159,160,161,173,210,211,223,236],[78,142,150,154,157,159,160,161,173,201,208,210,211,219,236],[78,142,150,154,157,159,160,161,173,216],[78,142,150,154,157,159,160,161,173,201,210,211,219,232,235,236],[78,142,150,154,157,159,160,161,173,210,211,216,219,236],[78,142,150,154,157,159,160,161,173,210,233,234,235,236],[78,142,150,154,157,159,160,161,173,210,216,220,221,222,236],[78,142,150,154,157,159,160,161,173,201,206,208,210,211,216,219,220,221,222,223,224,226,232,233,234,235,236,237,240,241,242,243,248],[78,142,150,154,157,159,160,161,173,201,208,210,211,219,220,233,234,235,236,241],[78,142,150,154,157,159,160,161,173,249]],"fileInfos":[{"version":"bcd24271a113971ba9eb71ff8cb01bc6b0f872a85c23fdbe5d93065b375933cd","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f88bedbeb09c6f5a6645cb24c7c55f1aa22d19ae96c8e6959cbd8b85a707bc6","impliedFormat":1},{"version":"7fe93b39b810eadd916be8db880dd7f0f7012a5cc6ffb62de8f62a2117fa6f1f","impliedFormat":1},{"version":"bb0074cc08b84a2374af33d8bf044b80851ccc9e719a5e202eacf40db2c31600","impliedFormat":1},{"version":"1a7daebe4f45fb03d9ec53d60008fbf9ac45a697fdc89e4ce218bc94b94f94d6","impliedFormat":1},{"version":"f94b133a3cb14a288803be545ac2683e0d0ff6661bcd37e31aaaec54fc382aed","impliedFormat":1},{"version":"f59d0650799f8782fd74cf73c19223730c6d1b9198671b1c5b3a38e1188b5953","impliedFormat":1},{"version":"8a15b4607d9a499e2dbeed9ec0d3c0d7372c850b2d5f1fb259e8f6d41d468a84","impliedFormat":1},{"version":"26e0fe14baee4e127f4365d1ae0b276f400562e45e19e35fd2d4c296684715e6","impliedFormat":1},{"version":"d6b1eba8496bdd0eed6fc8a685768fe01b2da4a0388b5fe7df558290bffcf32f","affectsGlobalScope":true,"impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"7f57fc4404ff020bc45b9c620aff2b40f700b95fe31164024c453a5e3c163c54","impliedFormat":1},{"version":"2a2de5b9459b3fc44decd9ce6100b72f1b002ef523126c1d3d8b2a4a63d74d78","affectsGlobalScope":true,"impliedFormat":1},{"version":"f13f4b465c99041e912db5c44129a94588e1aafee35a50eab51044833f50b4ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"eadcffda2aa84802c73938e589b9e58248d74c59cb7fcbca6474e3435ac15504","affectsGlobalScope":true,"impliedFormat":1},{"version":"105ba8ff7ba746404fe1a2e189d1d3d2e0eb29a08c18dded791af02f29fb4711","affectsGlobalScope":true,"impliedFormat":1},{"version":"00343ca5b2e3d48fa5df1db6e32ea2a59afab09590274a6cccb1dbae82e60c7c","affectsGlobalScope":true,"impliedFormat":1},{"version":"ebd9f816d4002697cb2864bea1f0b70a103124e18a8cd9645eeccc09bdf80ab4","affectsGlobalScope":true,"impliedFormat":1},{"version":"2c1afac30a01772cd2a9a298a7ce7706b5892e447bb46bdbeef720f7b5da77ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"7b0225f483e4fa685625ebe43dd584bb7973bbd84e66a6ba7bbe175ee1048b4f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c0a4b8ac6ce74679c1da2b3795296f5896e31c38e888469a8e0f99dc3305de60","affectsGlobalScope":true,"impliedFormat":1},{"version":"3084a7b5f569088e0146533a00830e206565de65cae2239509168b11434cd84f","affectsGlobalScope":true,"impliedFormat":1},{"version":"c5079c53f0f141a0698faa903e76cb41cd664e3efb01cc17a5c46ec2eb0bef42","affectsGlobalScope":true,"impliedFormat":1},{"version":"32cafbc484dea6b0ab62cf8473182bbcb23020d70845b406f80b7526f38ae862","affectsGlobalScope":true,"impliedFormat":1},{"version":"fca4cdcb6d6c5ef18a869003d02c9f0fd95df8cfaf6eb431cd3376bc034cad36","affectsGlobalScope":true,"impliedFormat":1},{"version":"b93ec88115de9a9dc1b602291b85baf825c85666bf25985cc5f698073892b467","affectsGlobalScope":true,"impliedFormat":1},{"version":"f5c06dcc3fe849fcb297c247865a161f995cc29de7aa823afdd75aaaddc1419b","affectsGlobalScope":true,"impliedFormat":1},{"version":"b77e16112127a4b169ef0b8c3a4d730edf459c5f25fe52d5e436a6919206c4d7","affectsGlobalScope":true,"impliedFormat":1},{"version":"fbffd9337146eff822c7c00acbb78b01ea7ea23987f6c961eba689349e744f8c","affectsGlobalScope":true,"impliedFormat":1},{"version":"a995c0e49b721312f74fdfb89e4ba29bd9824c770bbb4021d74d2bf560e4c6bd","affectsGlobalScope":true,"impliedFormat":1},{"version":"c7b3542146734342e440a84b213384bfa188835537ddbda50d30766f0593aff9","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce6180fa19b1cccd07ee7f7dbb9a367ac19c0ed160573e4686425060b6df7f57","affectsGlobalScope":true,"impliedFormat":1},{"version":"3f02e2476bccb9dbe21280d6090f0df17d2f66b74711489415a8aa4df73c9675","affectsGlobalScope":true,"impliedFormat":1},{"version":"45e3ab34c1c013c8ab2dc1ba4c80c780744b13b5676800ae2e3be27ae862c40c","affectsGlobalScope":true,"impliedFormat":1},{"version":"805c86f6cca8d7702a62a844856dbaa2a3fd2abef0536e65d48732441dde5b5b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e42e397f1a5a77994f0185fd1466520691456c772d06bf843e5084ceb879a0ad","affectsGlobalScope":true,"impliedFormat":1},{"version":"f4c2b41f90c95b1c532ecc874bd3c111865793b23aebcc1c3cbbabcd5d76ffb0","affectsGlobalScope":true,"impliedFormat":1},{"version":"ab26191cfad5b66afa11b8bf935ef1cd88fabfcb28d30b2dfa6fad877d050332","affectsGlobalScope":true,"impliedFormat":1},{"version":"2088bc26531e38fb05eedac2951480db5309f6be3fa4a08d2221abb0f5b4200d","affectsGlobalScope":true,"impliedFormat":1},{"version":"cb9d366c425fea79716a8fb3af0d78e6b22ebbab3bd64d25063b42dc9f531c1e","affectsGlobalScope":true,"impliedFormat":1},{"version":"500934a8089c26d57ebdb688fc9757389bb6207a3c8f0674d68efa900d2abb34","affectsGlobalScope":true,"impliedFormat":1},{"version":"689da16f46e647cef0d64b0def88910e818a5877ca5379ede156ca3afb780ac3","affectsGlobalScope":true,"impliedFormat":1},{"version":"bc21cc8b6fee4f4c2440d08035b7ea3c06b3511314c8bab6bef7a92de58a2593","affectsGlobalScope":true,"impliedFormat":1},{"version":"7ca53d13d2957003abb47922a71866ba7cb2068f8d154877c596d63c359fed25","affectsGlobalScope":true,"impliedFormat":1},{"version":"54725f8c4df3d900cb4dac84b64689ce29548da0b4e9b7c2de61d41c79293611","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5594bc3076ac29e6c1ebda77939bc4c8833de72f654b6e376862c0473199323","affectsGlobalScope":true,"impliedFormat":1},{"version":"2f3eb332c2d73e729f3364fcc0c2b375e72a121e8157d25a82d67a138c83a95c","affectsGlobalScope":true,"impliedFormat":1},{"version":"6f4427f9642ce8d500970e4e69d1397f64072ab73b97e476b4002a646ac743b1","affectsGlobalScope":true,"impliedFormat":1},{"version":"48915f327cd1dea4d7bd358d9dc7732f58f9e1626a29cc0c05c8c692419d9bb7","affectsGlobalScope":true,"impliedFormat":1},{"version":"b7bf9377723203b5a6a4b920164df22d56a43f593269ba6ae1fdc97774b68855","affectsGlobalScope":true,"impliedFormat":1},{"version":"db9709688f82c9e5f65a119c64d835f906efe5f559d08b11642d56eb85b79357","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b25b8c874acd1a4cf8444c3617e037d444d19080ac9f634b405583fd10ce1f7","affectsGlobalScope":true,"impliedFormat":1},{"version":"37be57d7c90cf1f8112ee2636a068d8fd181289f82b744160ec56a7dc158a9f5","affectsGlobalScope":true,"impliedFormat":1},{"version":"a917a49ac94cd26b754ab84e113369a75d1a47a710661d7cd25e961cc797065f","affectsGlobalScope":true,"impliedFormat":1},{"version":"6d3261badeb7843d157ef3e6f5d1427d0eeb0af0cf9df84a62cfd29fd47ac86e","affectsGlobalScope":true,"impliedFormat":1},{"version":"195daca651dde22f2167ac0d0a05e215308119a3100f5e6268e8317d05a92526","affectsGlobalScope":true,"impliedFormat":1},{"version":"8b11e4285cd2bb164a4dc09248bdec69e9842517db4ca47c1ba913011e44ff2f","affectsGlobalScope":true,"impliedFormat":1},{"version":"0508571a52475e245b02bc50fa1394065a0a3d05277fbf5120c3784b85651799","affectsGlobalScope":true,"impliedFormat":1},{"version":"8f9af488f510c3015af3cc8c267a9e9d96c4dd38a1fdff0e11dc5a544711415b","affectsGlobalScope":true,"impliedFormat":1},{"version":"fc611fea8d30ea72c6bbfb599c9b4d393ce22e2f5bfef2172534781e7d138104","affectsGlobalScope":true,"impliedFormat":1},{"version":"f128dae7c44d8f35ee42e0a437000a57c9f06cc04f8b4fb42eebf44954d53dc8","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ecb8e347cb6b2a8927c09b86263663289418df375f5e68e11a0ae683776978f","affectsGlobalScope":true,"impliedFormat":1},{"version":"1ce14b81c5cc821994aa8ec1d42b220dd41b27fcc06373bce3958af7421b77d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3a048b3e9302ef9a34ef4ebb9aecfb28b66abb3bce577206a79fee559c230da","affectsGlobalScope":true,"impliedFormat":1},{"version":"01a30f9e8582b369075c0808df71121e6855cb06fd8d3d39511d9ebb66405205","impliedFormat":1},{"version":"adddb46115ea335eb856958251a6f5d38905da39ba9d4af6e801818de26df45c","impliedFormat":99},{"version":"09227d94665edb85fd40b293bca5a0a691d6071863ee43f0c2e979bd898f3c6d","impliedFormat":99},{"version":"0f87241ea10e949f45d5310162a649099246918a40ab2c11a2c99e561f8bd8d9","impliedFormat":99},{"version":"211a7a7c6be66a0504a246d268aeaa2faf4e3728cf2e3c40d9771518adb9f7d7","impliedFormat":99},{"version":"d150b509281060dbad33ac3f7a4242b3428320657f0aec8e7278d37d8849c10c","signature":"528241117504fac8802039906879b4195368392a66ffb13b8f9fd1b518735618","impliedFormat":99},{"version":"eceb67ccc9c9d5d1616b065504d780b4fc2f480adff073c6362ca957b320f531","signature":"6ebd4ce95e96b0fc1005e0e800db05a550a1b9d0990ba31c1edb6d24ee24aca3","impliedFormat":99},{"version":"04590593b4dfa6e8af32712e6c64681b3a075ad4979c18a4ac4a38a6d9259ab0","signature":"295489981f12851f4146c951dff875bb6b025a8e76abdc500bf553ede7ad147b","impliedFormat":99},{"version":"3ea9943ebae1065b44c7b1eddb723243470913e38ac2b22b3b71bec8eadf6a40","signature":"27c4490f6132ab8d0592c26cb80e0e2a3f4b4e77e4f086e019ebe960ad7d101b","impliedFormat":99},{"version":"4b41a1daba0e73597c9fb5b912345af0a4f6d47422759ee8752e2bb6a4382a36","signature":"d164c332ba86b2b745045e72c31900eef11b3314bb587ebde9969a8a9a45b602","impliedFormat":99},{"version":"210d002828c7fbc346d0323e555530758b21c6f31a13b36e4d89ec4d3cc548a7","signature":"289c37246b5fa7594c16642d7c42fc021747799f0970bbab6005d398168b3761","impliedFormat":99},{"version":"d153a11543fd884b596587ccd97aebbeed950b26933ee000f94009f1ab142848","affectsGlobalScope":true,"impliedFormat":1},{"version":"0ccdaa19852d25ecd84eec365c3bfa16e7859cadecf6e9ca6d0dbbbee439743f","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc2110f7decca6bfb9392e30421cfa1436479e4a6756e8fec6cbc22625d4f881","affectsGlobalScope":true,"impliedFormat":1},{"version":"096116f8fedc1765d5bd6ef360c257b4a9048e5415054b3bf3c41b07f8951b0b","affectsGlobalScope":true,"impliedFormat":1},{"version":"e5e01375c9e124a83b52ee4b3244ed1a4d214a6cfb54ac73e164a823a4a7860a","affectsGlobalScope":true,"impliedFormat":1},{"version":"f90ae2bbce1505e67f2f6502392e318f5714bae82d2d969185c4a6cecc8af2fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"4b58e207b93a8f1c88bbf2a95ddc686ac83962b13830fe8ad3f404ffc7051fb4","affectsGlobalScope":true,"impliedFormat":1},{"version":"1fefabcb2b06736a66d2904074d56268753654805e829989a46a0161cd8412c5","affectsGlobalScope":true,"impliedFormat":1},{"version":"9798340ffb0d067d69b1ae5b32faa17ab31b82466a3fc00d8f2f2df0c8554aaa","affectsGlobalScope":true,"impliedFormat":1},{"version":"c18a99f01eb788d849ad032b31cafd49de0b19e083fe775370834c5675d7df8e","affectsGlobalScope":true,"impliedFormat":1},{"version":"5247874c2a23b9a62d178ae84f2db6a1d54e6c9a2e7e057e178cc5eea13757fc","affectsGlobalScope":true,"impliedFormat":1},{"version":"cdcf9ea426ad970f96ac930cd176d5c69c6c24eebd9fc580e1572d6c6a88f62c","impliedFormat":1},{"version":"23cd712e2ce083d68afe69224587438e5914b457b8acf87073c22494d706a3d0","impliedFormat":1},{"version":"156a859e21ef3244d13afeeba4e49760a6afa035c149dda52f0c45ea8903b338","impliedFormat":1},{"version":"10ec5e82144dfac6f04fa5d1d6c11763b3e4dbbac6d99101427219ab3e2ae887","impliedFormat":1},{"version":"615754924717c0b1e293e083b83503c0a872717ad5aa60ed7f1a699eb1b4ea5c","impliedFormat":1},{"version":"14e9acf826baba0ef4b5665704084896e7bcc06f65a9ab13af7e93d27d6b7069","impliedFormat":1},{"version":"68834d631c8838c715f225509cfc3927913b9cc7a4870460b5b60c8dbdb99baf","impliedFormat":1},{"version":"21adf13435b9b748529c8cedf80f884e5130b9684188120a686cd2b26a2059c7","impliedFormat":1},{"version":"eec76bf6b9346f3f95fa402621b889489e96930e72295b0369022f332e9b4a6a","impliedFormat":1},{"version":"0ecd58f413f9bc3b7d4383eae31b0c8fc576985cd7404d6f99f8c643543ade74","impliedFormat":1},{"version":"ea6bc8de8b59f90a7a3960005fd01988f98fd0784e14bc6922dde2e93305ec7d","impliedFormat":1},{"version":"36107995674b29284a115e21a0618c4c2751b32a8766dd4cb3ba740308b16d59","impliedFormat":1},{"version":"914a0ae30d96d71915fc519ccb4efbf2b62c0ddfb3a3fc6129151076bc01dc60","impliedFormat":1},{"version":"9c32412007b5662fd34a8eb04292fb5314ec370d7016d1c2fb8aa193c807fe22","impliedFormat":1},{"version":"7fd1b31fd35876b0aa650811c25ec2c97a3c6387e5473eb18004bed86cdd76b6","impliedFormat":1},{"version":"4d327f7d72ad0918275cea3eee49a6a8dc8114ae1d5b7f3f5d0774de75f7439a","impliedFormat":1},{"version":"6ebe8ebb8659aaa9d1acbf3710d7dae3e923e97610238b9511c25dc39023a166","impliedFormat":1},{"version":"e85d7f8068f6a26710bff0cc8c0fc5e47f71089c3780fbede05857331d2ddec9","impliedFormat":1},{"version":"7befaf0e76b5671be1d47b77fcc65f2b0aad91cc26529df1904f4a7c46d216e9","impliedFormat":1},{"version":"0a60a292b89ca7218b8616f78e5bbd1c96b87e048849469cccb4355e98af959a","impliedFormat":1},{"version":"0b6e25234b4eec6ed96ab138d96eb70b135690d7dd01f3dd8a8ab291c35a683a","impliedFormat":1},{"version":"9666f2f84b985b62400d2e5ab0adae9ff44de9b2a34803c2c5bd3c8325b17dc0","impliedFormat":1},{"version":"40cd35c95e9cf22cfa5bd84e96408b6fcbca55295f4ff822390abb11afbc3dca","impliedFormat":1},{"version":"b1616b8959bf557feb16369c6124a97a0e74ed6f49d1df73bb4b9ddf68acf3f3","impliedFormat":1},{"version":"5b03a034c72146b61573aab280f295b015b9168470f2df05f6080a2122f9b4df","impliedFormat":1},{"version":"40b463c6766ca1b689bfcc46d26b5e295954f32ad43e37ee6953c0a677e4ae2b","impliedFormat":1},{"version":"249b9cab7f5d628b71308c7d9bb0a808b50b091e640ba3ed6e2d0516f4a8d91d","impliedFormat":1},{"version":"d33ce35e3f9cfcc1d94eca415bdd3bde94d5b153ffdd33e6c4455c029986c630","impliedFormat":1},{"version":"80aae6afc67faa5ac0b32b5b8bc8cc9f7fa299cff15cf09cc2e11fd28c6ae29e","impliedFormat":1},{"version":"f473cd2288991ff3221165dcf73cd5d24da30391f87e85b3dd4d0450c787a391","impliedFormat":1},{"version":"499e5b055a5aba1e1998f7311a6c441a369831c70905cc565ceac93c28083d53","impliedFormat":1},{"version":"8aee8b6d4f9f62cf3776cda1305fb18763e2aade7e13cea5bbe699112df85214","impliedFormat":1},{"version":"98498b101803bb3dde9f76a56e65c14b75db1cc8bec5f4db72be541570f74fc5","impliedFormat":1},{"version":"4dc59f6e1dbf3d5f66660fceabe6c174d3261b37b696ae1854f0dbaf255fc753","impliedFormat":1},{"version":"5d0375ca7310efb77e3ef18d068d53784faf62705e0ad04569597ae0e755c401","impliedFormat":1},{"version":"59af37caec41ecf7b2e76059c9672a49e682c1a2aa6f9d7dc78878f53aa284d6","impliedFormat":1},{"version":"addf417b9eb3f938fddf8d81e96393a165e4be0d4a8b6402292f9c634b1cb00d","impliedFormat":1},{"version":"436d7b4543b340b0f3eef4310d524242e41369b9652aa9c70428767c4dcac455","impliedFormat":1},{"version":"adf27937dba6af9f08a68c5b1d3fce0ca7d4b960c57e6d6c844e7d1a8e53adae","impliedFormat":1},{"version":"12950411eeab8563b349cb7959543d92d8d02c289ed893d78499a19becb5a8cc","impliedFormat":1},{"version":"2e85db9e6fd73cfa3d7f28e0ab6b55417ea18931423bd47b409a96e4a169e8e6","impliedFormat":1},{"version":"c46e079fe54c76f95c67fb89081b3e399da2c7d109e7dca8e4b58d83e332e605","impliedFormat":1},{"version":"114f493b30f364255290472111b5a4791d5902c308645670cd0401429cbc6930","impliedFormat":1},{"version":"c3f5289820990ab66b70c7fb5b63cb674001009ff84b13de40619619a9c8175f","affectsGlobalScope":true,"impliedFormat":1},{"version":"b3275d55fac10b799c9546804126239baf020d220136163f763b55a74e50e750","affectsGlobalScope":true,"impliedFormat":1},{"version":"fa68a0a3b7cb32c00e39ee3cd31f8f15b80cac97dce51b6ee7fc14a1e8deb30b","affectsGlobalScope":true,"impliedFormat":1},{"version":"1cf059eaf468efcc649f8cf6075d3cb98e9a35a0fe9c44419ec3d2f5428d7123","affectsGlobalScope":true,"impliedFormat":1},{"version":"6c36e755bced82df7fb6ce8169265d0a7bb046ab4e2cb6d0da0cb72b22033e89","affectsGlobalScope":true,"impliedFormat":1},{"version":"e7721c4f69f93c91360c26a0a84ee885997d748237ef78ef665b153e622b36c1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7a93de4ff8a63bafe62ba86b89af1df0ccb5e40bb85b0c67d6bbcfdcf96bf3d4","affectsGlobalScope":true,"impliedFormat":1},{"version":"90e85f9bc549dfe2b5749b45fe734144e96cd5d04b38eae244028794e142a77e","affectsGlobalScope":true,"impliedFormat":1},{"version":"e0a5deeb610b2a50a6350bd23df6490036a1773a8a71d70f2f9549ab009e67ee","affectsGlobalScope":true,"impliedFormat":1},{"version":"d2ae155afe8a01cc0ae612d99117cf8ef16692ba7c4366590156fdec1bcf2d8c","impliedFormat":1},{"version":"3f5e5d9be35913db9fea42a63f3df0b7e3c8703b97670a2125587b4dbbd56d7c","impliedFormat":1},{"version":"c8b8968311ec4e5e97b7b5fb8a65efaba455db9bdcfd7fff7fb15f6e317bfba0","impliedFormat":1},{"version":"57c23df0b5f7a8e26363a3849b0bc7763f6b241207157c8e40089d1df4116f35","affectsGlobalScope":true,"impliedFormat":1},{"version":"3b8bc0c17b54081b0878673989216229e575d67a10874e84566a21025a2461ee","impliedFormat":1},{"version":"5b0db5a58b73498792a29bfebc333438e61906fef75da898b410e24e52229e6f","impliedFormat":1},{"version":"dbe055b2b29a7bab2c1ca8f259436306adb43f469dca7e639a02cd3695d3f621","impliedFormat":1},{"version":"1678b04557dca52feab73cc67610918a7f5e25bfdba3e7fa081acd625d93106d","impliedFormat":1},{"version":"aecbf1d9e6a18dab7d92ef8a89a1444b47e1eb6134cb2bb776a26d55ff58c29a","impliedFormat":1},{"version":"2ea729503db9793f2691162fec3dd1118cab62e96d025f8eeb376d43ec293395","impliedFormat":1},{"version":"9ec87fea42b92894b0f209931a880789d43c3397d09dd99c631ae40a2f7071d1","impliedFormat":1},{"version":"c68e88cdfadfb6c8ba5fc38e58a3a166b0beae77b1f05b7d921150a32a5ffb8d","impliedFormat":1},{"version":"2bc7aa4fba46df0bd495425a7c8201437a7d465f83854fac859df2d67f664df3","impliedFormat":1},{"version":"41d17e1ad9a002feb11c8cdd2777e5bbc0cdb1e3f595d237e4dded0b6949983b","impliedFormat":1},{"version":"1fede9296beac11ce8e6b425396a1791f64341f2be85deebb6286faf6e16306e","affectsGlobalScope":true,"impliedFormat":1},{"version":"ce697b6a251d9cad53998c7fd3098072df883b525ec45d83530e434dc6d80dc6","impliedFormat":1},{"version":"719412f054e6ecc35489462c9a21bab0323d173a7d04e55b0ace4b5d86fbeb07","impliedFormat":1},{"version":"0eb5d0cbf09de5d34542b977fd6a933bb2e0817bffe8e1a541b2f1ad1b9af1ff","impliedFormat":1},{"version":"fac3e88881b35d3a757ed891ac912b2674792c25e2a1a74e1f5fbc72d19a9792","impliedFormat":1},{"version":"2c2bdaa1d8ead9f68628d6d9d250e46ee8e81aa4898b4769a36956ae15e060fe","impliedFormat":1},{"version":"c32c840c62d8bd7aeb3147aa6754cd2d922b990a6b6634530cb2ebdce5adc8e9","impliedFormat":1},{"version":"5ff4433a2deae4f85ab1377e90a7554ce6b47ae51c69a84ca30a6e22fae85834","impliedFormat":1},{"version":"82b91e4e42e6c41bc7fc1b6c2dc5eba6a2ba98375eb1f210e6ff6bba2d54177e","impliedFormat":1},{"version":"c1fa52b3d014001e8662fa2669d90ea15373958a288e3b83a3b621733d25292a","affectsGlobalScope":true,"impliedFormat":1},{"version":"cbed824fec91efefc7bbdcb8b43d1a531fdbebd0e2ef19481501ff365a93cb70","impliedFormat":1},{"version":"d0716593b3f2b0451bcf0c24cfa86dec2235c325c89f201934248b7c742715fc","impliedFormat":1},{"version":"ec501101c2a96133a6c695f934c8f6642149cc728571b29cbb7b770984c1088e","impliedFormat":1},{"version":"b214ebcf76c51b115453f69729ee8aa7b7f8eccdae2a922b568a45c2d7ff52f7","impliedFormat":1},{"version":"429c9cdfa7d126255779efd7e6d9057ced2d69c81859bbab32073bad52e9ba76","impliedFormat":1},{"version":"2991bca2cc0f0628a278df2a2ccdb8d6cbcb700f3761abbed62bba137d5b1790","impliedFormat":1},{"version":"5e66972e83eb4dc7123939bf816e6cbd9ad81af5552db1cab84e6bd9c64d2ecc","affectsGlobalScope":true,"impliedFormat":1},{"version":"230763250f20449fa7b3c9273e1967adb0023dc890d4be1553faca658ee65971","impliedFormat":1},{"version":"c3e9078b60cb329d1221f5878e88cecfa3e74460550e605a58fcfb41a66029ff","impliedFormat":1},{"version":"8413d0641f293aed551c7464615b770d34a02dedede889b9591172287d68e773","impliedFormat":1},{"version":"0ea59f7d3e51440baa64f429253759b106cfcbaf51e474cae606e02265b37cf8","impliedFormat":1},{"version":"bc18a1991ba681f03e13285fa1d7b99b03b67ee671b7bc936254467177543890","impliedFormat":1},{"version":"1b241e24f3227d078c06aeda6e050187ad59a4e591f4467abed44d92b084e08d","impliedFormat":1},{"version":"fa94bbf532b7af8f394b95fa310980d6e20bd2d4c871c6a6cb9f70f03750a44b","impliedFormat":1},{"version":"7fde0e1be5c8be204ffbf428abfcf01da2eb0f130e1bc3f539eb7275f4fd1f58","impliedFormat":1},{"version":"e284328553df5f425a5d33d36a0c3fa66b46af9d097cad6f4d2e8696dfdeb0f1","affectsGlobalScope":true,"impliedFormat":1},{"version":"7fa2214bb0d64701bc6f9ce8cde2fd2ff8c571e0b23065fa04a8a5a6beb91511","impliedFormat":1},{"version":"f36b3fbe2be150a9ca140da48593f21e6a8172004f92ddc549b43efec39f3e54","impliedFormat":1},{"version":"f1c93e046fb3d9b7f8249629f4b63dc068dd839b824dd0aa39a5e68476dc9420","impliedFormat":1},{"version":"016b29bf4926b80255a108c53a1451717350059da04fcae64d1075f5e93bbb39","impliedFormat":1},{"version":"841983e39bd4cbb463be385e92fda11057cab368bf27100a801c492f1d86cbaa","impliedFormat":1},{"version":"1c4f139ade4f6ebf45463505f8155173e5d7a5305e50e0aae0a5e712d6ff3b48","impliedFormat":1},{"version":"e16b319e5aca1031168de823c4946ff8e29629c4c8cc0ec0fcfe2a8ab2155043","impliedFormat":1},{"version":"e4156ddb25aa0e3b5303d372f26957b36778f0f6bbd4326359269873295e3058","affectsGlobalScope":true,"impliedFormat":1},{"version":"cc1b433a84cae05ddc5672d4823170af78606ad21ecef60dbc4570190cbf1357","impliedFormat":1},{"version":"9d3821bc75c59577e52643324cec92fc2145642e8d17cf7ee07a3181f21d985d","impliedFormat":1},{"version":"7f78cfb2b343838612c192cb251746e3a7c62ac7675726a47e130d9b213f6580","impliedFormat":1},{"version":"201db9cf1687fab1adf5282fcba861f382b32303dc4f67c89d59655e78a25461","impliedFormat":1},{"version":"2c3c5c0f54055e87640f5d233716fd889f3034fc7911d603b642369b0dbeb2a7","impliedFormat":1},{"version":"0a20eaf2e4b1e3c1e1f87f7bccb0c936375b23b022baeea750519b7c9bc6ce83","impliedFormat":1},{"version":"b484ec11ba00e3a2235562a41898d55372ccabe607986c6fa4f4aba72093749f","impliedFormat":1},{"version":"a16b91b27bd6b706c687c88cbc8a7d4ee98e5ed6043026d6b84bda923c0aed67","impliedFormat":1},{"version":"1c9e5b1a17b1fc9b3711fb36e0690421261ab2880f15b145155b5b2ba2ab6c2d","impliedFormat":1},{"version":"99ab6d0d660ce4d21efb52288a39fd35bb3f556980ec5463b1ae8f304a3bbc85","impliedFormat":1},{"version":"6eeded8c7e352be6e0efb83f4935ec752513c4d22043b52522b90849a49a3a11","impliedFormat":1},{"version":"6c1ad90050ffbb151cacc68e2d06ea1a26a945659391e32651f5d42b86fd7f2c","impliedFormat":1},{"version":"afa1c49f8e559e413d57343339db857d2a8159435cf9cf7d4deb41718fff1b88","impliedFormat":1},{"version":"6953d7597831d0860c7034cf4f0419687d263b6b98a4b32e37ce6d49615c36e2","impliedFormat":1},{"version":"3a582c6e8906f5b094ccf0de6cc6f4f8a54b05a34f52517aba5c9c7f704f6b28","impliedFormat":99},{"version":"0528f6d21f7a02d4092895090d2dd86104bd5a3e79eced96d5a1a7dd90943d17","impliedFormat":99},{"version":"b5ce343886d23392be9c8280e9f24a87f1d7d3667f6672c2fe4aa61fa4ece7d4","impliedFormat":99},{"version":"72ce5b734c05da85c85a6f6dc05823b051d6aa41acaedeeb1d17c72f3b4efa72","impliedFormat":99},{"version":"b0857bb28fd5236ace84280f79a25093f919fd0eff13e47cc26ea03de60a7294","impliedFormat":99},{"version":"5e43e0824f10cd8c48e7a8c5c673638488925a12c31f0f9e0957965c290eb14c","impliedFormat":99},{"version":"ef13c73d6157a32933c612d476c1524dd674cf5b9a88571d7d6a0d147544d529","impliedFormat":99},{"version":"49ab4f1d153a252779958fc87b700743d32b5ffa42addd70ae23ad3f429daa5c","impliedFormat":99},{"version":"53cf4076f42b29b8d411259d168d51b3a0274c42c8814e5b44dfa8803a35d4fc","impliedFormat":99},{"version":"a39461ee1f27cf3e6cfd63d21045713d26d521da55ea4d8efccb705f689e6dbb","impliedFormat":99},{"version":"61bb64660ee150f3ab618340e15cca0a81664801bede7c966ca0eca3a952fe63","impliedFormat":99},{"version":"3b89216a7e38a454985ad17bb2ff85792837dc812f2a89fa5f60ad0a2e216fa7","impliedFormat":99},{"version":"16fe60bb544cfedfd2b5bb2f7d0b3957be7978706d57d9f06edc9c0c8dbdba23","impliedFormat":99},{"version":"de4a612aa8f1704af486f701e21993c786ba7d8cfed55fffa6684026aea2346e","impliedFormat":99},{"version":"4e003c868b0d8f8ad200b96cbc653e18e513fa23e1c19c4fe3cc25d4394efc47","impliedFormat":99},{"version":"8887e70871f697fa42ad7cdf32168db60ec2d6760c173c97973e35377fe5d83b","impliedFormat":99},{"version":"42a12f2faa483c9b48195ed794d22698162274e755f6e07219c2351c4f08d732","impliedFormat":99},{"version":"ec0c42bb0f465e4993f2bc68a6ce9df9a2dcbc7b83e21748f82f1b69561938e3","impliedFormat":99},{"version":"f50ff37a9cbbe74475f426474d9827083c7c2c138a954d28f1690df338f69291","impliedFormat":99},{"version":"61fd6c17235d530c40f543dd7c40afab091d91c1ef890baeed30db6d82b04b28","impliedFormat":99},{"version":"bcbd3becd08b4515225880abea0dbfbbf0d1181ce3af8f18f72f61edbe4febfb","impliedFormat":99},{"version":"091767bc841f937654ed597d49e023ed59850355e746ae1a6f20ab31076ee1fb","impliedFormat":99},{"version":"19c6d6135af59693698d384050b45a8a049493500add442f58e4bd7c8a255ab6","impliedFormat":99},{"version":"6a0dba12d55314638a8c51108b20fe2f68f1364a619d098918bda91c22dec154","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"4ead13a482c539b77394b2a97e3b877b809eac596390371cea490286f53b996a","impliedFormat":99},{"version":"06db2f8ba1d1dfacf04529cb731081ab23f133f29c7608ebdfbcab356996827c","impliedFormat":99},{"version":"bdd14f07b4eca0b4b5203b85b8dbc4d084c749fa590bee5ea613e1641dcd3b29","impliedFormat":99},{"version":"427fe2004642504828c1476d0af4270e6ad4db6de78c0b5da3e4c5ca95052a99","impliedFormat":1},{"version":"2eeffcee5c1661ddca53353929558037b8cf305ffb86a803512982f99bcab50d","impliedFormat":99},{"version":"9afb4cb864d297e4092a79ee2871b5d3143ea14153f62ef0bb04ede25f432030","affectsGlobalScope":true,"impliedFormat":99},{"version":"5c935b7fc4ddc1410ea1cd7cd4e35ed106a6e4920dd27a9480a40fd224359dc3","affectsGlobalScope":true,"impliedFormat":99},{"version":"2b39c6cf59088713babbfc3e20ee85f1375d40e66953156fa658346b8346f24f","impliedFormat":99},{"version":"69bf2422313487956e4dacf049f30cb91b34968912058d244cb19e4baa24da97","impliedFormat":99},{"version":"6987dfb4b0c4e02112cc4e548e7a77b3d9ddfeffa8c8a2db13ceac361a4567d9","impliedFormat":99},{"version":"4ffba3c5848b4fe62ee59b754fd5f256ad9656a0db6d37b9a2a8cb40dfc7ac21","impliedFormat":99},{"version":"c76c02846ba7d40b9b3488f0e8d75d02cbdee2f0bc5fcd55dd3bd2e1457646ea","impliedFormat":99},{"version":"5e2ba3d18d78aebbde1f34bde356e41e9c76eeaeaeee56a37036596a9eff4211","impliedFormat":99},{"version":"8280ae8ccc0493b32d1742d585357ab9f0a508ea050af25a5a20d64010d0a5cf","impliedFormat":99},{"version":"7adfd9f9056ecd4ae6c65fde2a98654960c662714c73f048478959d04c09e144","impliedFormat":99},{"version":"32b35cf0dc3a1b1a7118b61c34ce2ad1a29695851679f9ec34e0776f2ece2a69","impliedFormat":99},{"version":"b413fbc6658fe2774f8bf9a15cf4c53e586fc38a2d5256b3b9647da242c14389","impliedFormat":99},{"version":"59e5e964b84fdb2378e9455e4e59405030e4ed2b4c6f891ce395f17796af3cbb","impliedFormat":99},{"version":"c30a41267fc04c6518b17e55dcb2b810f267af4314b0b6d7df1c33a76ce1b330","impliedFormat":1},{"version":"72422d0bac4076912385d0c10911b82e4694fc106e2d70added091f88f0824ba","impliedFormat":1},{"version":"da251b82c25bee1d93f9fd80c5a61d945da4f708ca21285541d7aff83ecb8200","impliedFormat":1},{"version":"64db14db2bf37ac089766fdb3c7e1160fabc10e9929bc2deeede7237e4419fc8","impliedFormat":1},{"version":"98b94085c9f78eba36d3d2314affe973e8994f99864b8708122750788825c771","impliedFormat":1},{"version":"90ba95a763101bb61b8a799731a2ed60b5016b8135c1a2d5186862d4b534d4a1","impliedFormat":99},{"version":"8d7cbeea0454e05a3cdf3370c5df267072c4f1dc6c48a45a9ad750d7890443d7","affectsGlobalScope":true,"impliedFormat":99}],"root":[[70,75]],"options":{"composite":true,"declaration":true,"declarationDir":"./@types","declarationMap":true,"emitDeclarationOnly":true,"erasableSyntaxOnly":true,"esModuleInterop":true,"module":199,"noUncheckedSideEffectImports":true,"outDir":"./","rootDir":"../src","skipLibCheck":true,"sourceMap":true,"strict":true,"target":9,"tsBuildInfoFile":"./.tsbuildinfo","verbatimModuleSyntax":true},"referencedMap":[[67,1],[69,2],[68,1],[66,3],[71,4],[73,5],[75,6],[74,3],[72,7],[70,8],[228,3],[231,9],[229,3],[139,10],[140,10],[141,11],[78,12],[142,13],[143,14],[144,15],[76,3],[145,16],[146,17],[147,18],[148,19],[149,20],[150,21],[151,21],[152,22],[153,23],[154,24],[155,25],[79,3],[77,3],[156,26],[157,27],[158,28],[200,29],[159,30],[160,31],[161,30],[162,32],[163,33],[164,34],[165,35],[166,35],[167,35],[168,36],[169,37],[170,38],[171,39],[172,40],[173,41],[174,41],[175,42],[176,3],[177,3],[178,43],[179,44],[180,45],[181,43],[182,46],[183,47],[184,48],[185,49],[186,50],[187,51],[188,52],[189,53],[190,54],[191,55],[192,56],[193,57],[194,58],[195,59],[196,60],[80,30],[81,3],[82,61],[83,62],[84,3],[85,63],[86,3],[130,64],[131,65],[132,66],[133,66],[134,67],[135,3],[136,13],[137,68],[138,65],[197,69],[198,70],[199,71],[232,72],[239,73],[240,74],[238,3],[201,3],[210,75],[209,76],[233,75],[217,77],[219,78],[218,79],[226,80],[225,3],[208,81],[202,82],[204,83],[206,84],[205,3],[207,82],[203,3],[230,3],[246,85],[248,86],[247,87],[245,88],[244,3],[234,3],[227,3],[63,3],[64,3],[12,3],[10,3],[11,3],[16,3],[15,3],[2,3],[17,3],[18,3],[19,3],[20,3],[21,3],[22,3],[23,3],[24,3],[3,3],[25,3],[26,3],[4,3],[27,3],[31,3],[28,3],[29,3],[30,3],[32,3],[33,3],[34,3],[5,3],[35,3],[36,3],[37,3],[38,3],[6,3],[42,3],[39,3],[40,3],[41,3],[43,3],[7,3],[44,3],[49,3],[50,3],[45,3],[46,3],[47,3],[48,3],[8,3],[54,3],[51,3],[52,3],[53,3],[55,3],[9,3],[56,3],[65,3],[57,3],[58,3],[60,3],[59,3],[61,3],[1,3],[62,3],[14,3],[13,3],[105,89],[118,90],[102,91],[119,92],[128,93],[93,94],[94,95],[92,96],[127,97],[122,98],[126,99],[96,100],[115,101],[95,102],[125,103],[90,104],[91,98],[97,105],[98,3],[104,106],[101,105],[88,107],[129,108],[120,109],[108,110],[107,105],[109,111],[112,112],[106,113],[110,114],[123,97],[99,115],[100,116],[113,117],[89,92],[117,118],[116,105],[103,116],[111,119],[114,120],[121,3],[87,3],[124,121],[213,122],[216,123],[214,122],[212,3],[215,124],[235,125],[224,126],[220,127],[221,77],[242,128],[236,129],[222,130],[241,131],[211,3],[223,132],[249,133],[243,134],[250,135],[237,3]],"latestChangedDtsFile":"./@types/index.d.ts","version":"6.0.3"}
@@ -0,0 +1,37 @@
1
+ import type { BroadcastState, PatchMessage } from "./types.js";
2
+ /**
3
+ * Result of applying a patch message to cached client state.
4
+ */
5
+ export type ApplyResult<S extends BroadcastState = BroadcastState> = {
6
+ ok: true;
7
+ state: S;
8
+ } | {
9
+ ok: false;
10
+ reason: "stale" | "behind";
11
+ };
12
+ /**
13
+ * Apply a version-keyed patch message to the client's cached state.
14
+ *
15
+ * ## Version logic
16
+ *
17
+ * - All patches older than cached → "stale" (client already ahead)
18
+ * - Gap between cached version and first patch → "behind" (client missed versions, must resync)
19
+ * - Contiguous from cached version → apply in order
20
+ *
21
+ * ## Usage (React Query)
22
+ *
23
+ * ```typescript
24
+ * onData: (msg) => {
25
+ * const cached = utils.get_state.get_data({ streamId });
26
+ * const result = applyPatchMessage(msg, cached);
27
+ * if (result.ok) {
28
+ * utils.get_state.setData({ streamId }, result.state);
29
+ * } else if (result.reason === "behind") {
30
+ * utils.get_state.invalidate({ streamId }); // trigger full refetch
31
+ * }
32
+ * // "stale" → no-op, client already has newer state
33
+ * }
34
+ * ```
35
+ */
36
+ export declare function applyPatchMessage<S extends BroadcastState>(msg: PatchMessage<S>, cached: S | null | undefined): ApplyResult<S>;
37
+ //# sourceMappingURL=apply-patch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-patch.d.ts","sourceRoot":"","sources":["../../src/apply-patch.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/D;;GAEG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAC7D;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GACtB;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,OAAO,GAAG,QAAQ,CAAA;CAAE,CAAC;AAE9C;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,SAAS,cAAc,EACxD,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,EACpB,MAAM,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GAC3B,WAAW,CAAC,CAAC,CAAC,CAuBhB"}
@@ -0,0 +1,70 @@
1
+ import { StateCache } from "./state-cache.js";
2
+ import type { BroadcastState, PatchMessage, Subscriber } from "./types.js";
3
+ /**
4
+ * Server-side broadcast channel for incremental state sync over SSE.
5
+ *
6
+ * Manages per-stream subscriber sets and an LRU state cache. When state
7
+ * changes, forwards domain patches (from event handlers) to all subscribers
8
+ * as version-keyed messages.
9
+ *
10
+ * ## Usage
11
+ *
12
+ * ```typescript
13
+ * const broadcast = new BroadcastChannel<MyState>();
14
+ *
15
+ * // After every app.do():
16
+ * const snaps = await app.do(...);
17
+ * const patches = snaps.map(s => s.patch).filter(Boolean);
18
+ * const state = deriveState(snaps.at(-1));
19
+ * broadcast.publish(streamId, state, patches);
20
+ *
21
+ * // In SSE subscription:
22
+ * const cleanup = broadcast.subscribe(streamId, (msg) => {
23
+ * pending = msg;
24
+ * resolve?.();
25
+ * });
26
+ *
27
+ * // Initial state for reconnects:
28
+ * const cached = broadcast.get_state(streamId);
29
+ * ```
30
+ *
31
+ * ## Version Contract
32
+ *
33
+ * The `_v` field on state MUST be set from `snap.event.version` (the event
34
+ * store's monotonic stream version) BEFORE calling `publish()`. This is the
35
+ * single source of truth for ordering — no separate version counters.
36
+ */
37
+ export declare class BroadcastChannel<S extends BroadcastState = BroadcastState> {
38
+ private channels;
39
+ private state_cache;
40
+ constructor(options?: {
41
+ cache_size?: number;
42
+ });
43
+ /**
44
+ * Publish domain patches from a commit.
45
+ * patches[i] corresponds to version baseV + i + 1.
46
+ *
47
+ * @param streamId - The event store stream ID
48
+ * @param state - Full state with `_v` set from `snap.event.version`
49
+ * @param patches - Array of domain patches, one per emitted event
50
+ */
51
+ publish(streamId: string, state: S, patches?: Partial<S>[]): PatchMessage<S>;
52
+ /**
53
+ * Publish a state update that doesn't change the event version
54
+ * (e.g. presence overlay, computed field refresh).
55
+ * Uses the same version as the cached state, single entry.
56
+ */
57
+ publish_overlay(streamId: string, overlay_patch: Partial<S>): PatchMessage<S> | undefined;
58
+ /**
59
+ * Subscribe to broadcast messages for a stream.
60
+ * Returns a cleanup function that removes the subscription.
61
+ */
62
+ subscribe(streamId: string, cb: Subscriber<S>): () => void;
63
+ /** Get the number of subscribers for a stream. */
64
+ get_subscriber_count(streamId: string): number;
65
+ /** Get the cached state for a stream (for reconnects / initial SSE yield). */
66
+ get_state(streamId: string): S | undefined;
67
+ /** Direct access to the state cache (for app-specific reads like presence). */
68
+ get cache(): StateCache<S>;
69
+ }
70
+ //# sourceMappingURL=broadcast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../../src/broadcast.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,gBAAgB,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc;IACrE,OAAO,CAAC,QAAQ,CAAyC;IACzD,OAAO,CAAC,WAAW,CAAgB;gBAEvB,OAAO,CAAC,EAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;IAI7C;;;;;;;OAOG;IACH,OAAO,CACL,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,CAAC,EACR,OAAO,GAAE,OAAO,CAAC,CAAC,CAAC,EAAO,GACzB,YAAY,CAAC,CAAC,CAAC;IAgBlB;;;;OAIG;IACH,eAAe,CACb,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC,GACxB,YAAY,CAAC,CAAC,CAAC,GAAG,SAAS;IAe9B;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAW1D,kDAAkD;IAClD,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI9C,8EAA8E;IAC9E,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAI1C,+EAA+E;IAC/E,IAAI,KAAK,IAAI,UAAU,CAAC,CAAC,CAAC,CAEzB;CACF"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * @module act-sse
4
+ *
5
+ * Incremental state broadcast over SSE for act event-sourced apps.
6
+ *
7
+ * Provides server-side broadcast with domain patch forwarding,
8
+ * an LRU state cache, presence tracking, and a client-side
9
+ * patch applicator with version validation and resync detection.
10
+ *
11
+ * ## Architecture
12
+ *
13
+ * ```
14
+ * app.do() → snapshots (each carries its domain patch)
15
+ * │
16
+ * ▼
17
+ * deriveState(snap) ← app-specific (overlay presence, deadlines, etc.)
18
+ * state._v = snap.event.version
19
+ * │
20
+ * ▼
21
+ * broadcast.publish(streamId, state, patches)
22
+ * │
23
+ * ├── version-key each patch: { [baseV+1]: patch1, [baseV+2]: patch2 }
24
+ * └── push to all SSE subscribers
25
+ * │
26
+ * ▼
27
+ * Client: applyPatchMessage(msg, cached)
28
+ * │
29
+ * ├── contiguous → deep-merge patches in version order
30
+ * ├── stale → skip (client already ahead)
31
+ * └── behind → resync (client missed versions)
32
+ * ```
33
+ *
34
+ * ## Version Contract
35
+ *
36
+ * `_v` is always the event store stream version (`snap.event.version`).
37
+ * No separate version counters. The event store is the single source of truth.
38
+ */
39
+ export { patch } from "@rotorsoft/act-patch";
40
+ export type { ApplyResult } from "./apply-patch.js";
41
+ export { applyPatchMessage } from "./apply-patch.js";
42
+ export { BroadcastChannel } from "./broadcast.js";
43
+ export { PresenceTracker } from "./presence.js";
44
+ export { StateCache } from "./state-cache.js";
45
+ export type { BroadcastState, PatchMessage, Subscriber } from "./types.js";
46
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Generic presence tracker — ref-counted online status per stream per identity.
3
+ *
4
+ * Supports multi-tab: each subscribe increments the ref count, each
5
+ * unsubscribe decrements it. An identity is considered online when
6
+ * ref count > 0.
7
+ *
8
+ * ## Usage
9
+ *
10
+ * ```typescript
11
+ * const presence = new PresenceTracker();
12
+ *
13
+ * // On SSE connect:
14
+ * presence.add(game_id, player_id);
15
+ *
16
+ * // On SSE disconnect:
17
+ * presence.remove(game_id, player_id);
18
+ *
19
+ * // Query:
20
+ * presence.get_online(game_id); // Set<string>
21
+ * ```
22
+ */
23
+ export declare class PresenceTracker {
24
+ private streams;
25
+ /** Increment ref count for an identity on a stream. */
26
+ add(streamId: string, identity_id: string): void;
27
+ /** Decrement ref count. Removes the identity when count reaches 0. */
28
+ remove(streamId: string, identity_id: string): void;
29
+ /** Get the set of online identity IDs for a stream. */
30
+ get_online(streamId: string): Set<string>;
31
+ /** Check if a specific identity is online for a stream. */
32
+ is_online(streamId: string, identity_id: string): boolean;
33
+ }
34
+ //# sourceMappingURL=presence.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presence.d.ts","sourceRoot":"","sources":["../../src/presence.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,OAAO,CAA0C;IAEzD,uDAAuD;IACvD,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IAMhD,sEAAsE;IACtE,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI;IASnD,uDAAuD;IACvD,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAKzC,2DAA2D;IAC3D,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO;CAG1D"}
@@ -0,0 +1,29 @@
1
+ import type { BroadcastState } from "./types.js";
2
+ /**
3
+ * Generic LRU cache for aggregate state objects.
4
+ *
5
+ * Keyed by stream ID. Each entry stores the full state (with `_v` from the
6
+ * event store). Used as the "previous state" baseline for computing patches,
7
+ * and as the fast path for reconnects.
8
+ *
9
+ * The cache is shared between the broadcast hot path and read queries.
10
+ * Projections should maintain their own cache to avoid double-apply bugs.
11
+ */
12
+ export declare class StateCache<S extends BroadcastState = BroadcastState> {
13
+ private cache;
14
+ private maxSize;
15
+ constructor(maxSize?: number);
16
+ /** Get a cached state, promoting it to MRU position. */
17
+ get(key: string): S | undefined;
18
+ /** Set a cached state, evicting the LRU entry if at capacity. */
19
+ set(key: string, state: S): void;
20
+ /** Remove a cached entry. */
21
+ delete(key: string): void;
22
+ /** Check if a key exists in the cache. */
23
+ has(key: string): boolean;
24
+ /** Current number of cached entries. */
25
+ get size(): number;
26
+ /** Direct access to the underlying map (for iteration). */
27
+ entries(): IterableIterator<[string, S]>;
28
+ }
29
+ //# sourceMappingURL=state-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state-cache.d.ts","sourceRoot":"","sources":["../../src/state-cache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;;;;;;GASG;AACH,qBAAa,UAAU,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc;IAC/D,OAAO,CAAC,KAAK,CAAwB;IACrC,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,SAAK;IAIxB,wDAAwD;IACxD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAU/B,iEAAiE;IACjE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAShC,6BAA6B;IAC7B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,0CAA0C;IAC1C,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,wCAAwC;IACxC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,2DAA2D;IAC3D,OAAO,IAAI,gBAAgB,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;CAGzC"}
@@ -0,0 +1,20 @@
1
+ import type { DeepPartial } from "@rotorsoft/act-patch";
2
+ /**
3
+ * Base constraint for state objects managed by the broadcast system.
4
+ * Apps extend this with their own domain state shape.
5
+ */
6
+ export type BroadcastState = Record<string, unknown> & {
7
+ /** Event store stream version — set by the broadcast layer from snap.event.version */
8
+ _v: number;
9
+ };
10
+ /**
11
+ * SSE message: version-keyed domain patches.
12
+ * Keys are stringified version numbers, values are domain patches (deep partials).
13
+ * Multi-event commits produce multiple version-keyed entries.
14
+ */
15
+ export type PatchMessage<S extends BroadcastState = BroadcastState> = Record<number, DeepPartial<S>>;
16
+ /**
17
+ * Subscriber callback — receives version-keyed patch messages.
18
+ */
19
+ export type Subscriber<S extends BroadcastState = BroadcastState> = (msg: PatchMessage<S>) => void;
20
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAExD;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACrD,sFAAsF;IACtF,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI,MAAM,CAC1E,MAAM,EACN,WAAW,CAAC,CAAC,CAAC,CACf,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI,CAClE,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,KACjB,IAAI,CAAC"}
package/dist/index.cjs ADDED
@@ -0,0 +1,204 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ BroadcastChannel: () => BroadcastChannel,
24
+ PresenceTracker: () => PresenceTracker,
25
+ StateCache: () => StateCache,
26
+ applyPatchMessage: () => applyPatchMessage,
27
+ patch: () => import_act_patch3.patch
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+ var import_act_patch3 = require("@rotorsoft/act-patch");
31
+
32
+ // src/apply-patch.ts
33
+ var import_act_patch = require("@rotorsoft/act-patch");
34
+ function applyPatchMessage(msg, cached) {
35
+ const cachedV = cached?._v ?? 0;
36
+ const versions = Object.keys(msg).map(Number).sort((a, b) => a - b);
37
+ if (!versions.length) return { ok: false, reason: "stale" };
38
+ const minV = versions[0];
39
+ const maxV = versions[versions.length - 1];
40
+ if (maxV <= cachedV) return { ok: false, reason: "stale" };
41
+ if (!cached || minV > cachedV + 1) return { ok: false, reason: "behind" };
42
+ let state = cached;
43
+ for (const v of versions) {
44
+ if (v <= cachedV) continue;
45
+ state = { ...(0, import_act_patch.patch)(state, msg[v]), _v: v };
46
+ }
47
+ return { ok: true, state };
48
+ }
49
+
50
+ // src/broadcast.ts
51
+ var import_act_patch2 = require("@rotorsoft/act-patch");
52
+
53
+ // src/state-cache.ts
54
+ var StateCache = class {
55
+ cache = /* @__PURE__ */ new Map();
56
+ maxSize;
57
+ constructor(maxSize = 50) {
58
+ this.maxSize = maxSize;
59
+ }
60
+ /** Get a cached state, promoting it to MRU position. */
61
+ get(key) {
62
+ const s = this.cache.get(key);
63
+ if (s) {
64
+ this.cache.delete(key);
65
+ this.cache.set(key, s);
66
+ }
67
+ return s;
68
+ }
69
+ /** Set a cached state, evicting the LRU entry if at capacity. */
70
+ set(key, state) {
71
+ this.cache.delete(key);
72
+ this.cache.set(key, state);
73
+ if (this.cache.size > this.maxSize) {
74
+ this.cache.delete(this.cache.keys().next().value);
75
+ }
76
+ }
77
+ /** Remove a cached entry. */
78
+ delete(key) {
79
+ this.cache.delete(key);
80
+ }
81
+ /** Check if a key exists in the cache. */
82
+ has(key) {
83
+ return this.cache.has(key);
84
+ }
85
+ /** Current number of cached entries. */
86
+ get size() {
87
+ return this.cache.size;
88
+ }
89
+ /** Direct access to the underlying map (for iteration). */
90
+ entries() {
91
+ return this.cache.entries();
92
+ }
93
+ };
94
+
95
+ // src/broadcast.ts
96
+ var BroadcastChannel = class {
97
+ channels = /* @__PURE__ */ new Map();
98
+ state_cache;
99
+ constructor(options) {
100
+ this.state_cache = new StateCache(options?.cache_size ?? 50);
101
+ }
102
+ /**
103
+ * Publish domain patches from a commit.
104
+ * patches[i] corresponds to version baseV + i + 1.
105
+ *
106
+ * @param streamId - The event store stream ID
107
+ * @param state - Full state with `_v` set from `snap.event.version`
108
+ * @param patches - Array of domain patches, one per emitted event
109
+ */
110
+ publish(streamId, state, patches = []) {
111
+ this.state_cache.set(streamId, state);
112
+ const baseV = state._v - patches.length;
113
+ const msg = {};
114
+ patches.forEach((p, i) => {
115
+ msg[baseV + i + 1] = p;
116
+ });
117
+ const subs = this.channels.get(streamId);
118
+ if (subs?.size) {
119
+ for (const cb of subs) cb(msg);
120
+ }
121
+ return msg;
122
+ }
123
+ /**
124
+ * Publish a state update that doesn't change the event version
125
+ * (e.g. presence overlay, computed field refresh).
126
+ * Uses the same version as the cached state, single entry.
127
+ */
128
+ publish_overlay(streamId, overlay_patch) {
129
+ const prev = this.state_cache.get(streamId);
130
+ if (!prev) return void 0;
131
+ const state = (0, import_act_patch2.patch)(prev, overlay_patch);
132
+ this.state_cache.set(streamId, state);
133
+ const msg = { [state._v]: overlay_patch };
134
+ const subs = this.channels.get(streamId);
135
+ if (subs?.size) {
136
+ for (const cb of subs) cb(msg);
137
+ }
138
+ return msg;
139
+ }
140
+ /**
141
+ * Subscribe to broadcast messages for a stream.
142
+ * Returns a cleanup function that removes the subscription.
143
+ */
144
+ subscribe(streamId, cb) {
145
+ if (!this.channels.has(streamId)) this.channels.set(streamId, /* @__PURE__ */ new Set());
146
+ this.channels.get(streamId).add(cb);
147
+ return () => {
148
+ this.channels.get(streamId)?.delete(cb);
149
+ if (this.channels.get(streamId)?.size === 0) {
150
+ this.channels.delete(streamId);
151
+ }
152
+ };
153
+ }
154
+ /** Get the number of subscribers for a stream. */
155
+ get_subscriber_count(streamId) {
156
+ return this.channels.get(streamId)?.size ?? 0;
157
+ }
158
+ /** Get the cached state for a stream (for reconnects / initial SSE yield). */
159
+ get_state(streamId) {
160
+ return this.state_cache.get(streamId);
161
+ }
162
+ /** Direct access to the state cache (for app-specific reads like presence). */
163
+ get cache() {
164
+ return this.state_cache;
165
+ }
166
+ };
167
+
168
+ // src/presence.ts
169
+ var PresenceTracker = class {
170
+ streams = /* @__PURE__ */ new Map();
171
+ /** Increment ref count for an identity on a stream. */
172
+ add(streamId, identity_id) {
173
+ if (!this.streams.has(streamId)) this.streams.set(streamId, /* @__PURE__ */ new Map());
174
+ const counts = this.streams.get(streamId);
175
+ counts.set(identity_id, (counts.get(identity_id) ?? 0) + 1);
176
+ }
177
+ /** Decrement ref count. Removes the identity when count reaches 0. */
178
+ remove(streamId, identity_id) {
179
+ const counts = this.streams.get(streamId);
180
+ if (!counts) return;
181
+ const n = (counts.get(identity_id) ?? 1) - 1;
182
+ if (n <= 0) counts.delete(identity_id);
183
+ else counts.set(identity_id, n);
184
+ if (counts.size === 0) this.streams.delete(streamId);
185
+ }
186
+ /** Get the set of online identity IDs for a stream. */
187
+ get_online(streamId) {
188
+ const counts = this.streams.get(streamId);
189
+ return counts ? new Set(counts.keys()) : /* @__PURE__ */ new Set();
190
+ }
191
+ /** Check if a specific identity is online for a stream. */
192
+ is_online(streamId, identity_id) {
193
+ return (this.streams.get(streamId)?.get(identity_id) ?? 0) > 0;
194
+ }
195
+ };
196
+ // Annotate the CommonJS export names for ESM import in node:
197
+ 0 && (module.exports = {
198
+ BroadcastChannel,
199
+ PresenceTracker,
200
+ StateCache,
201
+ applyPatchMessage,
202
+ patch
203
+ });
204
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/apply-patch.ts","../src/broadcast.ts","../src/state-cache.ts","../src/presence.ts"],"sourcesContent":["/**\n * @packageDocumentation\n * @module act-sse\n *\n * Incremental state broadcast over SSE for act event-sourced apps.\n *\n * Provides server-side broadcast with domain patch forwarding,\n * an LRU state cache, presence tracking, and a client-side\n * patch applicator with version validation and resync detection.\n *\n * ## Architecture\n *\n * ```\n * app.do() → snapshots (each carries its domain patch)\n * │\n * ▼\n * deriveState(snap) ← app-specific (overlay presence, deadlines, etc.)\n * state._v = snap.event.version\n * │\n * ▼\n * broadcast.publish(streamId, state, patches)\n * │\n * ├── version-key each patch: { [baseV+1]: patch1, [baseV+2]: patch2 }\n * └── push to all SSE subscribers\n * │\n * ▼\n * Client: applyPatchMessage(msg, cached)\n * │\n * ├── contiguous → deep-merge patches in version order\n * ├── stale → skip (client already ahead)\n * └── behind → resync (client missed versions)\n * ```\n *\n * ## Version Contract\n *\n * `_v` is always the event store stream version (`snap.event.version`).\n * No separate version counters. The event store is the single source of truth.\n */\n\nexport { patch } from \"@rotorsoft/act-patch\";\nexport type { ApplyResult } from \"./apply-patch.js\";\nexport { applyPatchMessage } from \"./apply-patch.js\";\nexport { BroadcastChannel } from \"./broadcast.js\";\nexport { PresenceTracker } from \"./presence.js\";\nexport { StateCache } from \"./state-cache.js\";\nexport type { BroadcastState, PatchMessage, Subscriber } from \"./types.js\";\n","import { patch as deep_merge } from \"@rotorsoft/act-patch\";\nimport type { BroadcastState, PatchMessage } from \"./types.js\";\n\n/**\n * Result of applying a patch message to cached client state.\n */\nexport type ApplyResult<S extends BroadcastState = BroadcastState> =\n | { ok: true; state: S }\n | { ok: false; reason: \"stale\" | \"behind\" };\n\n/**\n * Apply a version-keyed patch message to the client's cached state.\n *\n * ## Version logic\n *\n * - All patches older than cached → \"stale\" (client already ahead)\n * - Gap between cached version and first patch → \"behind\" (client missed versions, must resync)\n * - Contiguous from cached version → apply in order\n *\n * ## Usage (React Query)\n *\n * ```typescript\n * onData: (msg) => {\n * const cached = utils.get_state.get_data({ streamId });\n * const result = applyPatchMessage(msg, cached);\n * if (result.ok) {\n * utils.get_state.setData({ streamId }, result.state);\n * } else if (result.reason === \"behind\") {\n * utils.get_state.invalidate({ streamId }); // trigger full refetch\n * }\n * // \"stale\" → no-op, client already has newer state\n * }\n * ```\n */\nexport function applyPatchMessage<S extends BroadcastState>(\n msg: PatchMessage<S>,\n cached: S | null | undefined\n): ApplyResult<S> {\n const cachedV = cached?._v ?? 0;\n const versions = Object.keys(msg)\n .map(Number)\n .sort((a, b) => a - b);\n\n if (!versions.length) return { ok: false, reason: \"stale\" };\n\n const minV = versions[0];\n const maxV = versions[versions.length - 1];\n\n // All patches are older than what we have\n if (maxV <= cachedV) return { ok: false, reason: \"stale\" };\n // Gap — we missed versions\n if (!cached || minV > cachedV + 1) return { ok: false, reason: \"behind\" };\n\n // Apply patches in version order, skipping any we already have\n let state = cached;\n for (const v of versions) {\n if (v <= cachedV) continue; // already applied\n state = { ...deep_merge(state, msg[v]), _v: v } as S;\n }\n return { ok: true, state };\n}\n","import { patch as apply_patch } from \"@rotorsoft/act-patch\";\nimport { StateCache } from \"./state-cache.js\";\nimport type { BroadcastState, PatchMessage, Subscriber } from \"./types.js\";\n\n/**\n * Server-side broadcast channel for incremental state sync over SSE.\n *\n * Manages per-stream subscriber sets and an LRU state cache. When state\n * changes, forwards domain patches (from event handlers) to all subscribers\n * as version-keyed messages.\n *\n * ## Usage\n *\n * ```typescript\n * const broadcast = new BroadcastChannel<MyState>();\n *\n * // After every app.do():\n * const snaps = await app.do(...);\n * const patches = snaps.map(s => s.patch).filter(Boolean);\n * const state = deriveState(snaps.at(-1));\n * broadcast.publish(streamId, state, patches);\n *\n * // In SSE subscription:\n * const cleanup = broadcast.subscribe(streamId, (msg) => {\n * pending = msg;\n * resolve?.();\n * });\n *\n * // Initial state for reconnects:\n * const cached = broadcast.get_state(streamId);\n * ```\n *\n * ## Version Contract\n *\n * The `_v` field on state MUST be set from `snap.event.version` (the event\n * store's monotonic stream version) BEFORE calling `publish()`. This is the\n * single source of truth for ordering — no separate version counters.\n */\nexport class BroadcastChannel<S extends BroadcastState = BroadcastState> {\n private channels = new Map<string, Set<Subscriber<S>>>();\n private state_cache: StateCache<S>;\n\n constructor(options?: { cache_size?: number }) {\n this.state_cache = new StateCache<S>(options?.cache_size ?? 50);\n }\n\n /**\n * Publish domain patches from a commit.\n * patches[i] corresponds to version baseV + i + 1.\n *\n * @param streamId - The event store stream ID\n * @param state - Full state with `_v` set from `snap.event.version`\n * @param patches - Array of domain patches, one per emitted event\n */\n publish(\n streamId: string,\n state: S,\n patches: Partial<S>[] = []\n ): PatchMessage<S> {\n this.state_cache.set(streamId, state);\n\n const baseV = state._v - patches.length;\n const msg: PatchMessage<S> = {};\n patches.forEach((p, i) => {\n msg[baseV + i + 1] = p;\n });\n\n const subs = this.channels.get(streamId);\n if (subs?.size) {\n for (const cb of subs) cb(msg);\n }\n return msg;\n }\n\n /**\n * Publish a state update that doesn't change the event version\n * (e.g. presence overlay, computed field refresh).\n * Uses the same version as the cached state, single entry.\n */\n publish_overlay(\n streamId: string,\n overlay_patch: Partial<S>\n ): PatchMessage<S> | undefined {\n const prev = this.state_cache.get(streamId);\n if (!prev) return undefined;\n\n const state = apply_patch(prev, overlay_patch) as S;\n this.state_cache.set(streamId, state);\n\n const msg: PatchMessage<S> = { [state._v]: overlay_patch };\n const subs = this.channels.get(streamId);\n if (subs?.size) {\n for (const cb of subs) cb(msg);\n }\n return msg;\n }\n\n /**\n * Subscribe to broadcast messages for a stream.\n * Returns a cleanup function that removes the subscription.\n */\n subscribe(streamId: string, cb: Subscriber<S>): () => void {\n if (!this.channels.has(streamId)) this.channels.set(streamId, new Set());\n this.channels.get(streamId)!.add(cb);\n return () => {\n this.channels.get(streamId)?.delete(cb);\n if (this.channels.get(streamId)?.size === 0) {\n this.channels.delete(streamId);\n }\n };\n }\n\n /** Get the number of subscribers for a stream. */\n get_subscriber_count(streamId: string): number {\n return this.channels.get(streamId)?.size ?? 0;\n }\n\n /** Get the cached state for a stream (for reconnects / initial SSE yield). */\n get_state(streamId: string): S | undefined {\n return this.state_cache.get(streamId);\n }\n\n /** Direct access to the state cache (for app-specific reads like presence). */\n get cache(): StateCache<S> {\n return this.state_cache;\n }\n}\n","import type { BroadcastState } from \"./types.js\";\n\n/**\n * Generic LRU cache for aggregate state objects.\n *\n * Keyed by stream ID. Each entry stores the full state (with `_v` from the\n * event store). Used as the \"previous state\" baseline for computing patches,\n * and as the fast path for reconnects.\n *\n * The cache is shared between the broadcast hot path and read queries.\n * Projections should maintain their own cache to avoid double-apply bugs.\n */\nexport class StateCache<S extends BroadcastState = BroadcastState> {\n private cache = new Map<string, S>();\n private maxSize: number;\n\n constructor(maxSize = 50) {\n this.maxSize = maxSize;\n }\n\n /** Get a cached state, promoting it to MRU position. */\n get(key: string): S | undefined {\n const s = this.cache.get(key);\n if (s) {\n // Move to end (MRU)\n this.cache.delete(key);\n this.cache.set(key, s);\n }\n return s;\n }\n\n /** Set a cached state, evicting the LRU entry if at capacity. */\n set(key: string, state: S): void {\n this.cache.delete(key);\n this.cache.set(key, state);\n if (this.cache.size > this.maxSize) {\n // Size > max guarantees at least one entry exists\n this.cache.delete(this.cache.keys().next().value!);\n }\n }\n\n /** Remove a cached entry. */\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n /** Check if a key exists in the cache. */\n has(key: string): boolean {\n return this.cache.has(key);\n }\n\n /** Current number of cached entries. */\n get size(): number {\n return this.cache.size;\n }\n\n /** Direct access to the underlying map (for iteration). */\n entries(): IterableIterator<[string, S]> {\n return this.cache.entries();\n }\n}\n","/**\n * Generic presence tracker — ref-counted online status per stream per identity.\n *\n * Supports multi-tab: each subscribe increments the ref count, each\n * unsubscribe decrements it. An identity is considered online when\n * ref count > 0.\n *\n * ## Usage\n *\n * ```typescript\n * const presence = new PresenceTracker();\n *\n * // On SSE connect:\n * presence.add(game_id, player_id);\n *\n * // On SSE disconnect:\n * presence.remove(game_id, player_id);\n *\n * // Query:\n * presence.get_online(game_id); // Set<string>\n * ```\n */\nexport class PresenceTracker {\n private streams = new Map<string, Map<string, number>>();\n\n /** Increment ref count for an identity on a stream. */\n add(streamId: string, identity_id: string): void {\n if (!this.streams.has(streamId)) this.streams.set(streamId, new Map());\n const counts = this.streams.get(streamId)!;\n counts.set(identity_id, (counts.get(identity_id) ?? 0) + 1);\n }\n\n /** Decrement ref count. Removes the identity when count reaches 0. */\n remove(streamId: string, identity_id: string): void {\n const counts = this.streams.get(streamId);\n if (!counts) return;\n const n = (counts.get(identity_id) ?? 1) - 1;\n if (n <= 0) counts.delete(identity_id);\n else counts.set(identity_id, n);\n if (counts.size === 0) this.streams.delete(streamId);\n }\n\n /** Get the set of online identity IDs for a stream. */\n get_online(streamId: string): Set<string> {\n const counts = this.streams.get(streamId);\n return counts ? new Set(counts.keys()) : new Set();\n }\n\n /** Check if a specific identity is online for a stream. */\n is_online(streamId: string, identity_id: string): boolean {\n return (this.streams.get(streamId)?.get(identity_id) ?? 0) > 0;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuCA,IAAAA,oBAAsB;;;ACvCtB,uBAAoC;AAkC7B,SAAS,kBACd,KACA,QACgB;AAChB,QAAM,UAAU,QAAQ,MAAM;AAC9B,QAAM,WAAW,OAAO,KAAK,GAAG,EAC7B,IAAI,MAAM,EACV,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAEvB,MAAI,CAAC,SAAS,OAAQ,QAAO,EAAE,IAAI,OAAO,QAAQ,QAAQ;AAE1D,QAAM,OAAO,SAAS,CAAC;AACvB,QAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AAGzC,MAAI,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,QAAQ;AAEzD,MAAI,CAAC,UAAU,OAAO,UAAU,EAAG,QAAO,EAAE,IAAI,OAAO,QAAQ,SAAS;AAGxE,MAAI,QAAQ;AACZ,aAAW,KAAK,UAAU;AACxB,QAAI,KAAK,QAAS;AAClB,YAAQ,EAAE,OAAG,iBAAAC,OAAW,OAAO,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE;AAAA,EAChD;AACA,SAAO,EAAE,IAAI,MAAM,MAAM;AAC3B;;;AC5DA,IAAAC,oBAAqC;;;ACY9B,IAAM,aAAN,MAA4D;AAAA,EACzD,QAAQ,oBAAI,IAAe;AAAA,EAC3B;AAAA,EAER,YAAY,UAAU,IAAI;AACxB,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAGA,IAAI,KAA4B;AAC9B,UAAM,IAAI,KAAK,MAAM,IAAI,GAAG;AAC5B,QAAI,GAAG;AAEL,WAAK,MAAM,OAAO,GAAG;AACrB,WAAK,MAAM,IAAI,KAAK,CAAC;AAAA,IACvB;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,IAAI,KAAa,OAAgB;AAC/B,SAAK,MAAM,OAAO,GAAG;AACrB,SAAK,MAAM,IAAI,KAAK,KAAK;AACzB,QAAI,KAAK,MAAM,OAAO,KAAK,SAAS;AAElC,WAAK,MAAM,OAAO,KAAK,MAAM,KAAK,EAAE,KAAK,EAAE,KAAM;AAAA,IACnD;AAAA,EACF;AAAA;AAAA,EAGA,OAAO,KAAmB;AACxB,SAAK,MAAM,OAAO,GAAG;AAAA,EACvB;AAAA;AAAA,EAGA,IAAI,KAAsB;AACxB,WAAO,KAAK,MAAM,IAAI,GAAG;AAAA,EAC3B;AAAA;AAAA,EAGA,IAAI,OAAe;AACjB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA,EAGA,UAAyC;AACvC,WAAO,KAAK,MAAM,QAAQ;AAAA,EAC5B;AACF;;;ADtBO,IAAM,mBAAN,MAAkE;AAAA,EAC/D,WAAW,oBAAI,IAAgC;AAAA,EAC/C;AAAA,EAER,YAAY,SAAmC;AAC7C,SAAK,cAAc,IAAI,WAAc,SAAS,cAAc,EAAE;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,QACE,UACA,OACA,UAAwB,CAAC,GACR;AACjB,SAAK,YAAY,IAAI,UAAU,KAAK;AAEpC,UAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,UAAM,MAAuB,CAAC;AAC9B,YAAQ,QAAQ,CAAC,GAAG,MAAM;AACxB,UAAI,QAAQ,IAAI,CAAC,IAAI;AAAA,IACvB,CAAC;AAED,UAAM,OAAO,KAAK,SAAS,IAAI,QAAQ;AACvC,QAAI,MAAM,MAAM;AACd,iBAAW,MAAM,KAAM,IAAG,GAAG;AAAA,IAC/B;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,gBACE,UACA,eAC6B;AAC7B,UAAM,OAAO,KAAK,YAAY,IAAI,QAAQ;AAC1C,QAAI,CAAC,KAAM,QAAO;AAElB,UAAM,YAAQ,kBAAAC,OAAY,MAAM,aAAa;AAC7C,SAAK,YAAY,IAAI,UAAU,KAAK;AAEpC,UAAM,MAAuB,EAAE,CAAC,MAAM,EAAE,GAAG,cAAc;AACzD,UAAM,OAAO,KAAK,SAAS,IAAI,QAAQ;AACvC,QAAI,MAAM,MAAM;AACd,iBAAW,MAAM,KAAM,IAAG,GAAG;AAAA,IAC/B;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAU,UAAkB,IAA+B;AACzD,QAAI,CAAC,KAAK,SAAS,IAAI,QAAQ,EAAG,MAAK,SAAS,IAAI,UAAU,oBAAI,IAAI,CAAC;AACvE,SAAK,SAAS,IAAI,QAAQ,EAAG,IAAI,EAAE;AACnC,WAAO,MAAM;AACX,WAAK,SAAS,IAAI,QAAQ,GAAG,OAAO,EAAE;AACtC,UAAI,KAAK,SAAS,IAAI,QAAQ,GAAG,SAAS,GAAG;AAC3C,aAAK,SAAS,OAAO,QAAQ;AAAA,MAC/B;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAGA,qBAAqB,UAA0B;AAC7C,WAAO,KAAK,SAAS,IAAI,QAAQ,GAAG,QAAQ;AAAA,EAC9C;AAAA;AAAA,EAGA,UAAU,UAAiC;AACzC,WAAO,KAAK,YAAY,IAAI,QAAQ;AAAA,EACtC;AAAA;AAAA,EAGA,IAAI,QAAuB;AACzB,WAAO,KAAK;AAAA,EACd;AACF;;;AExGO,IAAM,kBAAN,MAAsB;AAAA,EACnB,UAAU,oBAAI,IAAiC;AAAA;AAAA,EAGvD,IAAI,UAAkB,aAA2B;AAC/C,QAAI,CAAC,KAAK,QAAQ,IAAI,QAAQ,EAAG,MAAK,QAAQ,IAAI,UAAU,oBAAI,IAAI,CAAC;AACrE,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,WAAO,IAAI,cAAc,OAAO,IAAI,WAAW,KAAK,KAAK,CAAC;AAAA,EAC5D;AAAA;AAAA,EAGA,OAAO,UAAkB,aAA2B;AAClD,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,QAAI,CAAC,OAAQ;AACb,UAAM,KAAK,OAAO,IAAI,WAAW,KAAK,KAAK;AAC3C,QAAI,KAAK,EAAG,QAAO,OAAO,WAAW;AAAA,QAChC,QAAO,IAAI,aAAa,CAAC;AAC9B,QAAI,OAAO,SAAS,EAAG,MAAK,QAAQ,OAAO,QAAQ;AAAA,EACrD;AAAA;AAAA,EAGA,WAAW,UAA+B;AACxC,UAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ;AACxC,WAAO,SAAS,IAAI,IAAI,OAAO,KAAK,CAAC,IAAI,oBAAI,IAAI;AAAA,EACnD;AAAA;AAAA,EAGA,UAAU,UAAkB,aAA8B;AACxD,YAAQ,KAAK,QAAQ,IAAI,QAAQ,GAAG,IAAI,WAAW,KAAK,KAAK;AAAA,EAC/D;AACF;","names":["import_act_patch","deep_merge","import_act_patch","apply_patch"]}
package/dist/index.js ADDED
@@ -0,0 +1,175 @@
1
+ // src/index.ts
2
+ import { patch } from "@rotorsoft/act-patch";
3
+
4
+ // src/apply-patch.ts
5
+ import { patch as deep_merge } from "@rotorsoft/act-patch";
6
+ function applyPatchMessage(msg, cached) {
7
+ const cachedV = cached?._v ?? 0;
8
+ const versions = Object.keys(msg).map(Number).sort((a, b) => a - b);
9
+ if (!versions.length) return { ok: false, reason: "stale" };
10
+ const minV = versions[0];
11
+ const maxV = versions[versions.length - 1];
12
+ if (maxV <= cachedV) return { ok: false, reason: "stale" };
13
+ if (!cached || minV > cachedV + 1) return { ok: false, reason: "behind" };
14
+ let state = cached;
15
+ for (const v of versions) {
16
+ if (v <= cachedV) continue;
17
+ state = { ...deep_merge(state, msg[v]), _v: v };
18
+ }
19
+ return { ok: true, state };
20
+ }
21
+
22
+ // src/broadcast.ts
23
+ import { patch as apply_patch } from "@rotorsoft/act-patch";
24
+
25
+ // src/state-cache.ts
26
+ var StateCache = class {
27
+ cache = /* @__PURE__ */ new Map();
28
+ maxSize;
29
+ constructor(maxSize = 50) {
30
+ this.maxSize = maxSize;
31
+ }
32
+ /** Get a cached state, promoting it to MRU position. */
33
+ get(key) {
34
+ const s = this.cache.get(key);
35
+ if (s) {
36
+ this.cache.delete(key);
37
+ this.cache.set(key, s);
38
+ }
39
+ return s;
40
+ }
41
+ /** Set a cached state, evicting the LRU entry if at capacity. */
42
+ set(key, state) {
43
+ this.cache.delete(key);
44
+ this.cache.set(key, state);
45
+ if (this.cache.size > this.maxSize) {
46
+ this.cache.delete(this.cache.keys().next().value);
47
+ }
48
+ }
49
+ /** Remove a cached entry. */
50
+ delete(key) {
51
+ this.cache.delete(key);
52
+ }
53
+ /** Check if a key exists in the cache. */
54
+ has(key) {
55
+ return this.cache.has(key);
56
+ }
57
+ /** Current number of cached entries. */
58
+ get size() {
59
+ return this.cache.size;
60
+ }
61
+ /** Direct access to the underlying map (for iteration). */
62
+ entries() {
63
+ return this.cache.entries();
64
+ }
65
+ };
66
+
67
+ // src/broadcast.ts
68
+ var BroadcastChannel = class {
69
+ channels = /* @__PURE__ */ new Map();
70
+ state_cache;
71
+ constructor(options) {
72
+ this.state_cache = new StateCache(options?.cache_size ?? 50);
73
+ }
74
+ /**
75
+ * Publish domain patches from a commit.
76
+ * patches[i] corresponds to version baseV + i + 1.
77
+ *
78
+ * @param streamId - The event store stream ID
79
+ * @param state - Full state with `_v` set from `snap.event.version`
80
+ * @param patches - Array of domain patches, one per emitted event
81
+ */
82
+ publish(streamId, state, patches = []) {
83
+ this.state_cache.set(streamId, state);
84
+ const baseV = state._v - patches.length;
85
+ const msg = {};
86
+ patches.forEach((p, i) => {
87
+ msg[baseV + i + 1] = p;
88
+ });
89
+ const subs = this.channels.get(streamId);
90
+ if (subs?.size) {
91
+ for (const cb of subs) cb(msg);
92
+ }
93
+ return msg;
94
+ }
95
+ /**
96
+ * Publish a state update that doesn't change the event version
97
+ * (e.g. presence overlay, computed field refresh).
98
+ * Uses the same version as the cached state, single entry.
99
+ */
100
+ publish_overlay(streamId, overlay_patch) {
101
+ const prev = this.state_cache.get(streamId);
102
+ if (!prev) return void 0;
103
+ const state = apply_patch(prev, overlay_patch);
104
+ this.state_cache.set(streamId, state);
105
+ const msg = { [state._v]: overlay_patch };
106
+ const subs = this.channels.get(streamId);
107
+ if (subs?.size) {
108
+ for (const cb of subs) cb(msg);
109
+ }
110
+ return msg;
111
+ }
112
+ /**
113
+ * Subscribe to broadcast messages for a stream.
114
+ * Returns a cleanup function that removes the subscription.
115
+ */
116
+ subscribe(streamId, cb) {
117
+ if (!this.channels.has(streamId)) this.channels.set(streamId, /* @__PURE__ */ new Set());
118
+ this.channels.get(streamId).add(cb);
119
+ return () => {
120
+ this.channels.get(streamId)?.delete(cb);
121
+ if (this.channels.get(streamId)?.size === 0) {
122
+ this.channels.delete(streamId);
123
+ }
124
+ };
125
+ }
126
+ /** Get the number of subscribers for a stream. */
127
+ get_subscriber_count(streamId) {
128
+ return this.channels.get(streamId)?.size ?? 0;
129
+ }
130
+ /** Get the cached state for a stream (for reconnects / initial SSE yield). */
131
+ get_state(streamId) {
132
+ return this.state_cache.get(streamId);
133
+ }
134
+ /** Direct access to the state cache (for app-specific reads like presence). */
135
+ get cache() {
136
+ return this.state_cache;
137
+ }
138
+ };
139
+
140
+ // src/presence.ts
141
+ var PresenceTracker = class {
142
+ streams = /* @__PURE__ */ new Map();
143
+ /** Increment ref count for an identity on a stream. */
144
+ add(streamId, identity_id) {
145
+ if (!this.streams.has(streamId)) this.streams.set(streamId, /* @__PURE__ */ new Map());
146
+ const counts = this.streams.get(streamId);
147
+ counts.set(identity_id, (counts.get(identity_id) ?? 0) + 1);
148
+ }
149
+ /** Decrement ref count. Removes the identity when count reaches 0. */
150
+ remove(streamId, identity_id) {
151
+ const counts = this.streams.get(streamId);
152
+ if (!counts) return;
153
+ const n = (counts.get(identity_id) ?? 1) - 1;
154
+ if (n <= 0) counts.delete(identity_id);
155
+ else counts.set(identity_id, n);
156
+ if (counts.size === 0) this.streams.delete(streamId);
157
+ }
158
+ /** Get the set of online identity IDs for a stream. */
159
+ get_online(streamId) {
160
+ const counts = this.streams.get(streamId);
161
+ return counts ? new Set(counts.keys()) : /* @__PURE__ */ new Set();
162
+ }
163
+ /** Check if a specific identity is online for a stream. */
164
+ is_online(streamId, identity_id) {
165
+ return (this.streams.get(streamId)?.get(identity_id) ?? 0) > 0;
166
+ }
167
+ };
168
+ export {
169
+ BroadcastChannel,
170
+ PresenceTracker,
171
+ StateCache,
172
+ applyPatchMessage,
173
+ patch
174
+ };
175
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/apply-patch.ts","../src/broadcast.ts","../src/state-cache.ts","../src/presence.ts"],"sourcesContent":["/**\n * @packageDocumentation\n * @module act-sse\n *\n * Incremental state broadcast over SSE for act event-sourced apps.\n *\n * Provides server-side broadcast with domain patch forwarding,\n * an LRU state cache, presence tracking, and a client-side\n * patch applicator with version validation and resync detection.\n *\n * ## Architecture\n *\n * ```\n * app.do() → snapshots (each carries its domain patch)\n * │\n * ▼\n * deriveState(snap) ← app-specific (overlay presence, deadlines, etc.)\n * state._v = snap.event.version\n * │\n * ▼\n * broadcast.publish(streamId, state, patches)\n * │\n * ├── version-key each patch: { [baseV+1]: patch1, [baseV+2]: patch2 }\n * └── push to all SSE subscribers\n * │\n * ▼\n * Client: applyPatchMessage(msg, cached)\n * │\n * ├── contiguous → deep-merge patches in version order\n * ├── stale → skip (client already ahead)\n * └── behind → resync (client missed versions)\n * ```\n *\n * ## Version Contract\n *\n * `_v` is always the event store stream version (`snap.event.version`).\n * No separate version counters. The event store is the single source of truth.\n */\n\nexport { patch } from \"@rotorsoft/act-patch\";\nexport type { ApplyResult } from \"./apply-patch.js\";\nexport { applyPatchMessage } from \"./apply-patch.js\";\nexport { BroadcastChannel } from \"./broadcast.js\";\nexport { PresenceTracker } from \"./presence.js\";\nexport { StateCache } from \"./state-cache.js\";\nexport type { BroadcastState, PatchMessage, Subscriber } from \"./types.js\";\n","import { patch as deep_merge } from \"@rotorsoft/act-patch\";\nimport type { BroadcastState, PatchMessage } from \"./types.js\";\n\n/**\n * Result of applying a patch message to cached client state.\n */\nexport type ApplyResult<S extends BroadcastState = BroadcastState> =\n | { ok: true; state: S }\n | { ok: false; reason: \"stale\" | \"behind\" };\n\n/**\n * Apply a version-keyed patch message to the client's cached state.\n *\n * ## Version logic\n *\n * - All patches older than cached → \"stale\" (client already ahead)\n * - Gap between cached version and first patch → \"behind\" (client missed versions, must resync)\n * - Contiguous from cached version → apply in order\n *\n * ## Usage (React Query)\n *\n * ```typescript\n * onData: (msg) => {\n * const cached = utils.get_state.get_data({ streamId });\n * const result = applyPatchMessage(msg, cached);\n * if (result.ok) {\n * utils.get_state.setData({ streamId }, result.state);\n * } else if (result.reason === \"behind\") {\n * utils.get_state.invalidate({ streamId }); // trigger full refetch\n * }\n * // \"stale\" → no-op, client already has newer state\n * }\n * ```\n */\nexport function applyPatchMessage<S extends BroadcastState>(\n msg: PatchMessage<S>,\n cached: S | null | undefined\n): ApplyResult<S> {\n const cachedV = cached?._v ?? 0;\n const versions = Object.keys(msg)\n .map(Number)\n .sort((a, b) => a - b);\n\n if (!versions.length) return { ok: false, reason: \"stale\" };\n\n const minV = versions[0];\n const maxV = versions[versions.length - 1];\n\n // All patches are older than what we have\n if (maxV <= cachedV) return { ok: false, reason: \"stale\" };\n // Gap — we missed versions\n if (!cached || minV > cachedV + 1) return { ok: false, reason: \"behind\" };\n\n // Apply patches in version order, skipping any we already have\n let state = cached;\n for (const v of versions) {\n if (v <= cachedV) continue; // already applied\n state = { ...deep_merge(state, msg[v]), _v: v } as S;\n }\n return { ok: true, state };\n}\n","import { patch as apply_patch } from \"@rotorsoft/act-patch\";\nimport { StateCache } from \"./state-cache.js\";\nimport type { BroadcastState, PatchMessage, Subscriber } from \"./types.js\";\n\n/**\n * Server-side broadcast channel for incremental state sync over SSE.\n *\n * Manages per-stream subscriber sets and an LRU state cache. When state\n * changes, forwards domain patches (from event handlers) to all subscribers\n * as version-keyed messages.\n *\n * ## Usage\n *\n * ```typescript\n * const broadcast = new BroadcastChannel<MyState>();\n *\n * // After every app.do():\n * const snaps = await app.do(...);\n * const patches = snaps.map(s => s.patch).filter(Boolean);\n * const state = deriveState(snaps.at(-1));\n * broadcast.publish(streamId, state, patches);\n *\n * // In SSE subscription:\n * const cleanup = broadcast.subscribe(streamId, (msg) => {\n * pending = msg;\n * resolve?.();\n * });\n *\n * // Initial state for reconnects:\n * const cached = broadcast.get_state(streamId);\n * ```\n *\n * ## Version Contract\n *\n * The `_v` field on state MUST be set from `snap.event.version` (the event\n * store's monotonic stream version) BEFORE calling `publish()`. This is the\n * single source of truth for ordering — no separate version counters.\n */\nexport class BroadcastChannel<S extends BroadcastState = BroadcastState> {\n private channels = new Map<string, Set<Subscriber<S>>>();\n private state_cache: StateCache<S>;\n\n constructor(options?: { cache_size?: number }) {\n this.state_cache = new StateCache<S>(options?.cache_size ?? 50);\n }\n\n /**\n * Publish domain patches from a commit.\n * patches[i] corresponds to version baseV + i + 1.\n *\n * @param streamId - The event store stream ID\n * @param state - Full state with `_v` set from `snap.event.version`\n * @param patches - Array of domain patches, one per emitted event\n */\n publish(\n streamId: string,\n state: S,\n patches: Partial<S>[] = []\n ): PatchMessage<S> {\n this.state_cache.set(streamId, state);\n\n const baseV = state._v - patches.length;\n const msg: PatchMessage<S> = {};\n patches.forEach((p, i) => {\n msg[baseV + i + 1] = p;\n });\n\n const subs = this.channels.get(streamId);\n if (subs?.size) {\n for (const cb of subs) cb(msg);\n }\n return msg;\n }\n\n /**\n * Publish a state update that doesn't change the event version\n * (e.g. presence overlay, computed field refresh).\n * Uses the same version as the cached state, single entry.\n */\n publish_overlay(\n streamId: string,\n overlay_patch: Partial<S>\n ): PatchMessage<S> | undefined {\n const prev = this.state_cache.get(streamId);\n if (!prev) return undefined;\n\n const state = apply_patch(prev, overlay_patch) as S;\n this.state_cache.set(streamId, state);\n\n const msg: PatchMessage<S> = { [state._v]: overlay_patch };\n const subs = this.channels.get(streamId);\n if (subs?.size) {\n for (const cb of subs) cb(msg);\n }\n return msg;\n }\n\n /**\n * Subscribe to broadcast messages for a stream.\n * Returns a cleanup function that removes the subscription.\n */\n subscribe(streamId: string, cb: Subscriber<S>): () => void {\n if (!this.channels.has(streamId)) this.channels.set(streamId, new Set());\n this.channels.get(streamId)!.add(cb);\n return () => {\n this.channels.get(streamId)?.delete(cb);\n if (this.channels.get(streamId)?.size === 0) {\n this.channels.delete(streamId);\n }\n };\n }\n\n /** Get the number of subscribers for a stream. */\n get_subscriber_count(streamId: string): number {\n return this.channels.get(streamId)?.size ?? 0;\n }\n\n /** Get the cached state for a stream (for reconnects / initial SSE yield). */\n get_state(streamId: string): S | undefined {\n return this.state_cache.get(streamId);\n }\n\n /** Direct access to the state cache (for app-specific reads like presence). */\n get cache(): StateCache<S> {\n return this.state_cache;\n }\n}\n","import type { BroadcastState } from \"./types.js\";\n\n/**\n * Generic LRU cache for aggregate state objects.\n *\n * Keyed by stream ID. Each entry stores the full state (with `_v` from the\n * event store). Used as the \"previous state\" baseline for computing patches,\n * and as the fast path for reconnects.\n *\n * The cache is shared between the broadcast hot path and read queries.\n * Projections should maintain their own cache to avoid double-apply bugs.\n */\nexport class StateCache<S extends BroadcastState = BroadcastState> {\n private cache = new Map<string, S>();\n private maxSize: number;\n\n constructor(maxSize = 50) {\n this.maxSize = maxSize;\n }\n\n /** Get a cached state, promoting it to MRU position. */\n get(key: string): S | undefined {\n const s = this.cache.get(key);\n if (s) {\n // Move to end (MRU)\n this.cache.delete(key);\n this.cache.set(key, s);\n }\n return s;\n }\n\n /** Set a cached state, evicting the LRU entry if at capacity. */\n set(key: string, state: S): void {\n this.cache.delete(key);\n this.cache.set(key, state);\n if (this.cache.size > this.maxSize) {\n // Size > max guarantees at least one entry exists\n this.cache.delete(this.cache.keys().next().value!);\n }\n }\n\n /** Remove a cached entry. */\n delete(key: string): void {\n this.cache.delete(key);\n }\n\n /** Check if a key exists in the cache. */\n has(key: string): boolean {\n return this.cache.has(key);\n }\n\n /** Current number of cached entries. */\n get size(): number {\n return this.cache.size;\n }\n\n /** Direct access to the underlying map (for iteration). */\n entries(): IterableIterator<[string, S]> {\n return this.cache.entries();\n }\n}\n","/**\n * Generic presence tracker — ref-counted online status per stream per identity.\n *\n * Supports multi-tab: each subscribe increments the ref count, each\n * unsubscribe decrements it. An identity is considered online when\n * ref count > 0.\n *\n * ## Usage\n *\n * ```typescript\n * const presence = new PresenceTracker();\n *\n * // On SSE connect:\n * presence.add(game_id, player_id);\n *\n * // On SSE disconnect:\n * presence.remove(game_id, player_id);\n *\n * // Query:\n * presence.get_online(game_id); // Set<string>\n * ```\n */\nexport class PresenceTracker {\n private streams = new Map<string, Map<string, number>>();\n\n /** Increment ref count for an identity on a stream. */\n add(streamId: string, identity_id: string): void {\n if (!this.streams.has(streamId)) this.streams.set(streamId, new Map());\n const counts = this.streams.get(streamId)!;\n counts.set(identity_id, (counts.get(identity_id) ?? 0) + 1);\n }\n\n /** Decrement ref count. Removes the identity when count reaches 0. */\n remove(streamId: string, identity_id: string): void {\n const counts = this.streams.get(streamId);\n if (!counts) return;\n const n = (counts.get(identity_id) ?? 1) - 1;\n if (n <= 0) counts.delete(identity_id);\n else counts.set(identity_id, n);\n if (counts.size === 0) this.streams.delete(streamId);\n }\n\n /** Get the set of online identity IDs for a stream. */\n get_online(streamId: string): Set<string> {\n const counts = this.streams.get(streamId);\n return counts ? new Set(counts.keys()) : new Set();\n }\n\n /** Check if a specific identity is online for a stream. */\n is_online(streamId: string, identity_id: string): boolean {\n return (this.streams.get(streamId)?.get(identity_id) ?? 0) > 0;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@rotorsoft/act-sse",
3
3
  "type": "module",
4
- "version": "1.2.2",
4
+ "version": "1.2.4",
5
5
  "description": "Incremental state broadcast over SSE for act apps",
6
6
  "keywords": [
7
7
  "typescript",
@@ -17,6 +17,10 @@
17
17
  "url": "git+https://github.com/rotorsoft/act-root.git",
18
18
  "directory": "libs/act-sse"
19
19
  },
20
+ "homepage": "https://github.com/rotorsoft/act-root/tree/master/libs/act-sse#readme",
21
+ "bugs": {
22
+ "url": "https://github.com/rotorsoft/act-root/issues"
23
+ },
20
24
  "files": [
21
25
  "dist"
22
26
  ],
@@ -32,13 +36,13 @@
32
36
  },
33
37
  "sideEffects": false,
34
38
  "engines": {
35
- "node": ">=22.18.0"
39
+ "node": ">=22.22.3"
36
40
  },
37
41
  "publishConfig": {
38
42
  "access": "public"
39
43
  },
40
44
  "dependencies": {
41
- "@rotorsoft/act-patch": "^1.2.1"
45
+ "@rotorsoft/act-patch": "^1.2.4"
42
46
  },
43
47
  "scripts": {
44
48
  "clean": "rm -rf dist",