@blockcast/fec-worker 0.1.0 → 0.3.0-main.10140e847083
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 +28 -23
- package/dist/block-order.d.ts +22 -0
- package/dist/block-order.d.ts.map +1 -0
- package/dist/block-order.js +48 -0
- package/dist/block-order.js.map +1 -0
- package/dist/fec-worker-client.d.ts +23 -20
- package/dist/fec-worker-client.d.ts.map +1 -1
- package/dist/fec-worker-client.js +102 -54
- package/dist/fec-worker-client.js.map +1 -1
- package/dist/fec-worker-types.d.ts +162 -88
- package/dist/fec-worker-types.d.ts.map +1 -1
- package/dist/fec-worker-types.js +2 -2
- package/dist/fec-worker.js +328 -195
- package/dist/fec-worker.js.map +4 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/shred-fec-worker.js +23 -2
- package/dist/shred-fec-worker.js.map +2 -2
- package/dist/shred-worker-types.d.ts +16 -1
- package/dist/shred-worker-types.d.ts.map +1 -1
- package/dist/shred-worker-types.js +16 -1
- package/dist/shred-worker-types.js.map +1 -1
- package/package.json +10 -8
- package/src/block-order.ts +59 -0
- package/src/fec-worker-client.ts +107 -56
- package/src/fec-worker-types.ts +161 -84
- package/src/fec-worker.ts +517 -468
- package/src/index.ts +3 -0
- package/src/shred-fec-worker.ts +34 -3
- package/src/shred-worker-types.ts +16 -1
- package/src/__tests__/fec-worker-alta.test.ts +0 -231
- package/src/__tests__/fec-worker-cleanup-integration.test.ts +0 -250
- package/src/__tests__/fec-worker-pending-queue.test.ts +0 -315
- package/src/__tests__/fec-worker-repair-size.test.ts +0 -1029
- package/src/__tests__/fec-worker-wasm-contract.test.ts +0 -84
- package/src/__tests__/shred-fec-worker.test.ts +0 -448
- package/src/fec-worker-client.test.ts +0 -530
- package/src/fec-worker-types.test.ts +0 -400
- package/src/shred-worker-client.test.ts +0 -182
- package/src/transfer.test.ts +0 -24
package/README.md
CHANGED
|
@@ -23,8 +23,7 @@ const client = new FecWorkerClient(workerUrl)
|
|
|
23
23
|
|
|
24
24
|
client.onFrame((data, meta) => {
|
|
25
25
|
// data is an ArrayBuffer transferred from the worker (zero-copy).
|
|
26
|
-
// meta tells you whether this frame was recovered via FEC
|
|
27
|
-
// configured) whether its source-packet trailer verified.
|
|
26
|
+
// meta tells you whether this frame was recovered via FEC.
|
|
28
27
|
decoder.decode(data) // hand off to WebCodecs / MSE
|
|
29
28
|
})
|
|
30
29
|
client.onSnapshot((stats) => fecPanel.render(stats))
|
|
@@ -38,14 +37,14 @@ const config: FecTrackConfig = {
|
|
|
38
37
|
resolution: { w: 1920, h: 1080 },
|
|
39
38
|
framerate: 30,
|
|
40
39
|
algorithm: "raptor",
|
|
41
|
-
|
|
40
|
+
blockProfile: "iso-ssbg1-v1", // catalog fec.blockProfile: required, never defaulted; the only profile (the retired "k-padded-v1" is refused)
|
|
41
|
+
k: 32, // the maximum SSB_length of a block
|
|
42
42
|
repairCount: 8,
|
|
43
43
|
symbolSize: 1312,
|
|
44
44
|
interleaveDepth: 30,
|
|
45
45
|
interleaveMs: 1000, // 30 frames @ 30 fps
|
|
46
46
|
deliveryWindowMs: 2000, // computeFecDeliveryWindowMs(...) from the catalog
|
|
47
47
|
fecMode: "subframe",
|
|
48
|
-
altaEnabled: false,
|
|
49
48
|
relayBlockSigEnabled: false,
|
|
50
49
|
}
|
|
51
50
|
client.configure(config, mmtWasmModule)
|
|
@@ -97,7 +96,7 @@ This is the **Worker-per-track FEC pipeline** architectural decision tracked in
|
|
|
97
96
|
|
|
98
97
|
Buffers cross the boundary via the `Transferable` list. After `postMessage`, the sender's `ArrayBuffer` is detached — touching it throws. This is the contract that makes zero-copy safe: there is no aliased view that could be mutated mid-decode.
|
|
99
98
|
|
|
100
|
-
`WebAssembly.Module` is structured-cloneable (not transferable) and is *cloned* (cheap — the underlying compiled module is shared across realms).
|
|
99
|
+
`WebAssembly.Module` is structured-cloneable (not transferable) and is *cloned* (cheap — the underlying compiled module is shared across realms).
|
|
101
100
|
|
|
102
101
|
Errors that escape the worker's `try/catch` (in either the sync command path or the async configure path) are emitted as `{type: "error", message}` events rather than thrown — the main thread surfaces them through `onError`.
|
|
103
102
|
|
|
@@ -109,16 +108,17 @@ All messages are typed as discriminated unions in `fec-worker-types.ts`. Both th
|
|
|
109
108
|
|
|
110
109
|
| `type` | Payload | Transferred |
|
|
111
110
|
|--------|---------|-------------|
|
|
112
|
-
| `configure` | `config: FecTrackConfig`, `wasmModule: WebAssembly.Module
|
|
113
|
-
| `feedSource` | `ssId: number`, `data: ArrayBuffer`, `ts: number
|
|
111
|
+
| `configure` | `config: FecTrackConfig`, `wasmModule: WebAssembly.Module` | none — WASM modules are structured-cloned |
|
|
112
|
+
| `feedSource` | `ssId: number`, `data: ArrayBuffer`, `ts: number` | `data` |
|
|
114
113
|
| `feedRepair` | `ssStart: number`, `ssbLength: number`, `rsId: number`, `data: ArrayBuffer`, `ts: number` | `data` |
|
|
115
114
|
| `relayBlockSig` | `sbn: number`, `sig: ArrayBuffer`, `hash: ArrayBuffer` | `sig`, `hash` |
|
|
116
115
|
| `cleanup` | `sbns: number[]` (all timed-out blocks covered by this coalesced barrier) | none |
|
|
116
|
+
| `subscribeBlockUpdates` | — (send only after `capabilities` lists it) | none |
|
|
117
117
|
| `dispose` | — | none |
|
|
118
118
|
|
|
119
|
-
`feedSource` carries the **
|
|
119
|
+
`feedSource` carries the **32-bit Source Symbol ID** from the MMTP Source FEC Payload ID (ISO 23008-1 §C.5.2), consecutive per FEC source packet and wrapping at 2^32, and as `data` the protected packet itself, at most T-2 bytes (ssbg_mode1, §C.3.3). A source belongs to the block a repair states; until one does, the Worker and the decoder hold it unplaced. Never derive a block from an SS_ID.
|
|
120
120
|
|
|
121
|
-
`feedRepair` carries the wire-level Repair FEC Payload ID fields directly (ISO 23008-1 §C.5.3): `ssStart` is the SS_ID of the first source symbol in the block, `ssbLength`
|
|
121
|
+
`feedRepair` carries the wire-level Repair FEC Payload ID fields directly (ISO 23008-1 §C.5.3): `ssStart` is the SS_ID of the first source symbol in the block (any uint32; the block key), `ssbLength` is the authored source-symbol count in `1..configured K`, and `rsId` is the repair index within the block. The block is `[ssStart, ssStart + ssbLength)` with K' = `ssbLength` and repair ESI = `ssbLength + rsId`; the configured K is only the maximum `ssbLength`; K is never inferred from repair traffic.
|
|
122
122
|
|
|
123
123
|
### Outbound — `FecWorkerEvent` (worker → main)
|
|
124
124
|
|
|
@@ -126,12 +126,14 @@ All messages are typed as discriminated unions in `fec-worker-types.ts`. Both th
|
|
|
126
126
|
|--------|---------|------------------|
|
|
127
127
|
| `frame` | `data: ArrayBuffer`, `meta: FrameMeta` | `data` |
|
|
128
128
|
| `snapshot` | `stats: FecTrackStats` | none (structured clone) |
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
129
|
+
| `capabilities` | `commands: FecWorkerOptionalCommand[]` (after `configure`) | none |
|
|
130
|
+
| `blockUpdate` | `block: FecBlockSnapshot` (only after `subscribeBlockUpdates`) | none |
|
|
131
131
|
| `cleanupComplete` | `sbns: number[]` | none |
|
|
132
132
|
| `error` | `message: string` | none |
|
|
133
133
|
|
|
134
|
-
`
|
|
134
|
+
The Worker posts `blockUpdate` only to a client that sent `subscribeBlockUpdates`: each one is a structured clone and a page-thread task, and the snapshot already counts every transition. `FecWorkerClient` subscribes when `onBlockUpdate` is registered and the Worker has announced the command. A Worker that predates the command posts `blockUpdate` unasked and is never sent it (BLO-36599).
|
|
135
|
+
|
|
136
|
+
`FrameMeta` carries `recovered: boolean` (this frame came out of the FEC decode, not the direct path), `sbn: number`, and for recovered frames `esi` / `ssbLength`. Source provenance is verified upstream at router admission by `@blockcast/mmt-manifest-verify`, not in the Worker.
|
|
135
137
|
|
|
136
138
|
## Lifecycle
|
|
137
139
|
|
|
@@ -139,11 +141,11 @@ All messages are typed as discriminated unions in `fec-worker-types.ts`. Both th
|
|
|
139
141
|
new FecWorkerClient(url)
|
|
140
142
|
│ worker spawned with { type: "module" }
|
|
141
143
|
▼
|
|
142
|
-
client.configure(config, wasmModule
|
|
143
|
-
│ worker initSyncs WASM, instantiates MmtFecDecoder(interleaveDepth,
|
|
144
|
+
client.configure(config, wasmModule)
|
|
145
|
+
│ worker initSyncs WASM, instantiates MmtFecDecoder(interleaveDepth, blockProfile),
|
|
146
|
+
│ refuses a decoder whose get_block_profile() differs,
|
|
144
147
|
│ immediately configures exact catalog K/T (no decoder defaults),
|
|
145
|
-
│
|
|
146
|
-
│ from catalog values, throws if any of K / D / interleaveMs ≤ 0,
|
|
148
|
+
│ rejects invalid geometry or negative spans; zero keeps in-band FEC,
|
|
147
149
|
│ starts the 200 ms snapshot timer, emits initial snapshot
|
|
148
150
|
▼
|
|
149
151
|
client.feedSource(...) / feedRepair(...) ◄── steady state
|
|
@@ -154,13 +156,15 @@ client.feedSource(...) / feedRepair(...) ◄── steady state
|
|
|
154
156
|
client.cleanup(sbn) ◄── per-block inactivity deadline
|
|
155
157
|
│ coalesces queued SBNs behind one worker queue-drain barrier
|
|
156
158
|
│ evicts blocks relative to max(sbns) − D × 3
|
|
157
|
-
│
|
|
159
|
+
│ retires exactly those SBNs via WASM cleanup_blocks_detailed(); a lost
|
|
160
|
+
│ block is never answered with a synthesised frame (sinks hold the last
|
|
161
|
+
│ good frame, per @blockcast/mmt-container conceal-timeline)
|
|
158
162
|
│ acknowledges every SBN in {type:"cleanupComplete", sbns}
|
|
159
163
|
│ any block still in "collecting" at eviction time is counted as failed
|
|
160
164
|
▼
|
|
161
165
|
client.dispose()
|
|
162
|
-
sends {type:"dispose"}, terminates worker. WASM decoder
|
|
163
|
-
|
|
166
|
+
sends {type:"dispose"}, terminates worker. The WASM decoder is
|
|
167
|
+
explicitly free()'d before terminate so its linear
|
|
164
168
|
memory is released (FinalizationRegistry would not run between
|
|
165
169
|
dispose and terminate). Idempotent.
|
|
166
170
|
```
|
|
@@ -179,14 +183,16 @@ The `snapshot` event delivers a `FecTrackStats` every 200 ms (catalog-derived; n
|
|
|
179
183
|
| `blocksComplete` | Block had all K source symbols without needing repair |
|
|
180
184
|
| `blocksRecovered` | Block was recovered using ≥1 repair symbol |
|
|
181
185
|
| `blocksFailed` | Block aged out before reaching K |
|
|
182
|
-
| `greenFillFrames` | Frames substituted with green-fill on unrecoverable loss |
|
|
183
186
|
| `recoveryRate` | `recovered / (recovered + failed)`, 0–100 |
|
|
184
187
|
| `avgRecoveryMs`, `maxRecoveryMs` | Rolling decode latency (capped at 1000 samples) |
|
|
185
|
-
| `liveSbn` |
|
|
188
|
+
| `liveSbn` | Newest accepted SS_ID or SS_Start, in RFC 1982 serial order |
|
|
189
|
+
| `repairSymbolSizeMismatch` | Repair symbols rejected before WASM because their byte length did not match `T` |
|
|
190
|
+
| `sourceDroppedRetired` / `sourceDroppedTerminal` / `sourceDroppedDuplicate` | Source symbols dropped before WASM, by **first matching** cause (see below) |
|
|
186
191
|
| `blocks` | Up to `max(D × 3, 24)` recent `FecBlockSnapshot`s for live visualization |
|
|
187
192
|
| `interleaveWindowBlocks` | `D` from config |
|
|
188
193
|
|
|
189
|
-
|
|
194
|
+
The three `sourceDropped*` counters split by cause so a dashboard can tell the failure modes apart. They are tested in a fixed order, each returning early — `Retired` → `Terminal` → `Duplicate` — so a symbol counts once, under the **first** cause that matches: a symbol that is both past retention and a repeat counts only as `Retired`, and `Terminal` absorbs every post-settle duplicate. A block that settles at K-1 source symbols (BLO-35597 defect 2) is explained by a late symbol past retention (`Retired`), a symbol for a block that had already settled — complete, recovered **or failed** (`Terminal`), or a repeated SS_ID (`Duplicate`). `Retired` and `Terminal` narrow the cause; they do not attribute it. `Duplicate` does: it cannot fire once a block settles — the `Terminal` check returns first, and `markBlockTerminal` retires the block's accepted-ESI set anyway — so a non-zero `Duplicate` is positive evidence that a repeated SS_ID reached the worker while the block was **still collecting** — a duplicate packet on the wire (an ESI is the SS_ID's serial distance from SS_Start, so no derivation can collide). It attributes the **timing**, not the origin. A symbol lost in transit never reaches the worker and increments nothing, so **all three reading zero is not evidence of a publisher defect** — wire loss produces an identical shape. Separate those with `FecBlockSnapshot.ssbLength`: below `k` is a genuinely short authored block, equal to `k` with fewer accepted is loss-or-late, undefined (no repair symbol yet) leaves both open. All three are cumulative across decoder epochs, so they stay comparable against the `sourceSymbols` denominator; only a reset zeroes them.
|
|
195
|
+
|
|
190
196
|
|
|
191
197
|
`FecTrackStats` is also the shape consumed downstream by `@blockcast/mmt-container`'s `CmcdProducer` and the diagnostics `fec-panel` — keep it stable when you extend it.
|
|
192
198
|
|
|
@@ -194,7 +200,6 @@ When ALTA is enabled, the snapshot also carries the four-state verification acco
|
|
|
194
200
|
|
|
195
201
|
- **`@blockcast/mmt-fec`** — the wrapped library. Container-agnostic RaptorQ decoder + block manager + reorder buffer. Lives on the main thread when you don't need a Worker; this package is the off-thread variant of the same decoder.
|
|
196
202
|
- **`@blockcast/mmt-container`** — the consumer that wires this Worker into the data path. `FecManager` instantiates one `FecWorkerClient` per track, routes MMTP source/repair payloads in, and pumps recovered frames into the MFU reassembler / fmp4 segmenter.
|
|
197
|
-
- **`@blockcast/mmt-alta-parse`** — ALTA trailer extraction. Used inside the Worker to parse the `[0xF0A1][len][auth]` trailer off recovered source symbols (publisher-stripped trailers don't appear on direct-source packets, which carry the trailer in band).
|
|
198
203
|
|
|
199
204
|
## Testing
|
|
200
205
|
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order of iso-ssbg1-v1 FEC blocks on the 32-bit SS_ID (ISO/IEC 23008-1:2023
|
|
3
|
+
* C.5.2/C.5.3; RFC 1982 serial arithmetic, SERIAL_BITS = 32; BLO-35597 iso:5).
|
|
4
|
+
* The K-stride (SBN * K) order of the retired k-padded-v1 profile is gone.
|
|
5
|
+
*/
|
|
6
|
+
/** Forward RFC 1982 distance from `from` to `to` on the 32-bit SS_ID, in 0..2^32-1. */
|
|
7
|
+
export declare function serialDistance(from: number, to: number): number;
|
|
8
|
+
/** Whether `a` precedes `b` under RFC 1982 serial comparison (SERIAL_BITS = 32). */
|
|
9
|
+
export declare function serialPrecedes(a: number, b: number): boolean;
|
|
10
|
+
/** Whether `ssId` is one of the `ssbLength` consecutive SS_IDs starting at `ssStart`, mod 2^32. */
|
|
11
|
+
export declare function isoBlockContains(ssStart: number, ssbLength: number, ssId: number): boolean;
|
|
12
|
+
/** Whether two SS_ID ranges `[start, start + length)` share any SS_ID, mod 2^32. */
|
|
13
|
+
export declare function isoBlocksOverlap(aStart: number, aLength: number, bStart: number, bLength: number): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* How many blocks SS_ID `a` lies behind SS_ID `b` (0 unless `a` precedes `b`):
|
|
16
|
+
* a lower bound, because an iso-ssbg1-v1 block holds at most `kMax` source
|
|
17
|
+
* symbols, so `n * kMax` SS_IDs always span at least `n` blocks. This is the
|
|
18
|
+
* one interleave-horizon metric every ISO receiver layer shares (decoder
|
|
19
|
+
* retention, worker retirement, ordered-relay settlement).
|
|
20
|
+
*/
|
|
21
|
+
export declare function isoBlocksBehind(a: number, b: number, kMax: number): number;
|
|
22
|
+
//# sourceMappingURL=block-order.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-order.d.ts","sourceRoot":"","sources":["../src/block-order.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAgBH,uFAAuF;AACvF,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED,oFAAoF;AACpF,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAG5D;AAED,mGAAmG;AACnG,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE1F;AAED,oFAAoF;AACpF,wBAAgB,gBAAgB,CAC/B,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACb,OAAO,CAET;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAK1E"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order of iso-ssbg1-v1 FEC blocks on the 32-bit SS_ID (ISO/IEC 23008-1:2023
|
|
3
|
+
* C.5.2/C.5.3; RFC 1982 serial arithmetic, SERIAL_BITS = 32; BLO-35597 iso:5).
|
|
4
|
+
* The K-stride (SBN * K) order of the retired k-padded-v1 profile is gone.
|
|
5
|
+
*/
|
|
6
|
+
const SPAN = 0x1_0000_0000;
|
|
7
|
+
const HALF = 0x8000_0000;
|
|
8
|
+
// --- iso-ssbg1-v1 (ISO/IEC 23008-1 C.3.3 ssbg_mode1; C.5.2/C.5.3) ---------
|
|
9
|
+
//
|
|
10
|
+
// Under the ISO profile SS_ID increments by one per FEC source packet and
|
|
11
|
+
// wraps at 2^32, and a block is the SSB_length consecutive SS_IDs
|
|
12
|
+
// `[SS_Start, SS_Start + SSB_length)`. Blocks are keyed and ordered by their
|
|
13
|
+
// SS_Start, never by an SBN derived from K: the catalog K is only the maximum
|
|
14
|
+
// block length (kMax). The canonical helpers are @blockcast/mmt-base
|
|
15
|
+
// `serial32Distance` / `serial32Precedes` / `isoBlockContains` /
|
|
16
|
+
// `isoBlocksBehindLowerBound`; this bundle carries its own copies (it does not
|
|
17
|
+
// depend on mmt-base), and block-order.test.ts pins them to the same vectors.
|
|
18
|
+
/** Forward RFC 1982 distance from `from` to `to` on the 32-bit SS_ID, in 0..2^32-1. */
|
|
19
|
+
export function serialDistance(from, to) {
|
|
20
|
+
return (to - from + SPAN) % SPAN;
|
|
21
|
+
}
|
|
22
|
+
/** Whether `a` precedes `b` under RFC 1982 serial comparison (SERIAL_BITS = 32). */
|
|
23
|
+
export function serialPrecedes(a, b) {
|
|
24
|
+
const distance = serialDistance(a, b);
|
|
25
|
+
return distance !== 0 && distance < HALF;
|
|
26
|
+
}
|
|
27
|
+
/** Whether `ssId` is one of the `ssbLength` consecutive SS_IDs starting at `ssStart`, mod 2^32. */
|
|
28
|
+
export function isoBlockContains(ssStart, ssbLength, ssId) {
|
|
29
|
+
return serialDistance(ssStart, ssId) < ssbLength;
|
|
30
|
+
}
|
|
31
|
+
/** Whether two SS_ID ranges `[start, start + length)` share any SS_ID, mod 2^32. */
|
|
32
|
+
export function isoBlocksOverlap(aStart, aLength, bStart, bLength) {
|
|
33
|
+
return isoBlockContains(aStart, aLength, bStart) || isoBlockContains(bStart, bLength, aStart);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* How many blocks SS_ID `a` lies behind SS_ID `b` (0 unless `a` precedes `b`):
|
|
37
|
+
* a lower bound, because an iso-ssbg1-v1 block holds at most `kMax` source
|
|
38
|
+
* symbols, so `n * kMax` SS_IDs always span at least `n` blocks. This is the
|
|
39
|
+
* one interleave-horizon metric every ISO receiver layer shares (decoder
|
|
40
|
+
* retention, worker retirement, ordered-relay settlement).
|
|
41
|
+
*/
|
|
42
|
+
export function isoBlocksBehind(a, b, kMax) {
|
|
43
|
+
if (!Number.isSafeInteger(kMax) || kMax <= 0) {
|
|
44
|
+
throw new Error(`iso-ssbg1-v1 block horizon needs a positive maximum K (got ${kMax})`);
|
|
45
|
+
}
|
|
46
|
+
return serialPrecedes(a, b) ? Math.floor(serialDistance(a, b) / kMax) : 0;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=block-order.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-order.js","sourceRoot":"","sources":["../src/block-order.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,IAAI,GAAG,aAAa,CAAC;AAC3B,MAAM,IAAI,GAAG,WAAW,CAAC;AAEzB,6EAA6E;AAC7E,EAAE;AACF,0EAA0E;AAC1E,kEAAkE;AAClE,6EAA6E;AAC7E,8EAA8E;AAC9E,qEAAqE;AACrE,iEAAiE;AACjE,+EAA+E;AAC/E,8EAA8E;AAE9E,uFAAuF;AACvF,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,EAAU;IACtD,OAAO,CAAC,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC;AAClC,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,cAAc,CAAC,CAAS,EAAE,CAAS;IAClD,MAAM,QAAQ,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACtC,OAAO,QAAQ,KAAK,CAAC,IAAI,QAAQ,GAAG,IAAI,CAAC;AAC1C,CAAC;AAED,mGAAmG;AACnG,MAAM,UAAU,gBAAgB,CAAC,OAAe,EAAE,SAAiB,EAAE,IAAY;IAChF,OAAO,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,SAAS,CAAC;AAClD,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,gBAAgB,CAC/B,MAAc,EACd,OAAe,EACf,MAAc,EACd,OAAe;IAEf,OAAO,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,IAAI,gBAAgB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAC/F,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,CAAS,EAAE,CAAS,EAAE,IAAY;IACjE,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,8DAA8D,IAAI,GAAG,CAAC,CAAC;IACxF,CAAC;IACD,OAAO,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC3E,CAAC"}
|
|
@@ -17,32 +17,24 @@ export declare class FecWorkerClient {
|
|
|
17
17
|
constructor(workerOrUrl: Worker | URL);
|
|
18
18
|
/**
|
|
19
19
|
* Configure the Worker with FEC parameters and WASM module.
|
|
20
|
+
* `interleaveMs: 0` is a valid no-span catalog value; in that mode the
|
|
21
|
+
* caller supplies a positive receiver-local `deliveryWindowMs`.
|
|
20
22
|
* wasmModule is transferred (not neutered — WebAssembly.Module is shareable).
|
|
21
|
-
* altaPublicKey is PKCS#8 SPKI DER bytes (self-describing — algorithm detected from OID).
|
|
22
|
-
* Transferred (detached from sender).
|
|
23
|
-
* altaWasmModule is structured-cloneable (NOT transferable — do not add to transfer list).
|
|
24
23
|
*/
|
|
25
|
-
configure(config: FecTrackConfig, wasmModule: WebAssembly.Module
|
|
24
|
+
configure(config: FecTrackConfig, wasmModule: WebAssembly.Module): void;
|
|
26
25
|
/**
|
|
27
26
|
* Feed a source symbol to the Worker. data ArrayBuffer is transferred (zero-copy).
|
|
28
|
-
* Caller must not access data after this call — it is
|
|
29
|
-
*
|
|
30
|
-
* Optional `alta` carries the detached ALTA authenticator bytes and the
|
|
31
|
-
* byte offset in `data` where the signed payload ends. The auth buffer is
|
|
32
|
-
* transferred zero-copy alongside `data`. Use {@link getMmtpAltaTrailer}
|
|
33
|
-
* upstream (mmtp-router) to extract it from the wire packet.
|
|
27
|
+
* Caller must not access data after this call — it is transferred with the
|
|
28
|
+
* task's symbol batch, before any later command of this client.
|
|
34
29
|
*/
|
|
35
|
-
feedSource(ssId: number, data: ArrayBuffer, ts: number
|
|
36
|
-
auth: ArrayBuffer;
|
|
37
|
-
payloadEnd: number;
|
|
38
|
-
}): void;
|
|
30
|
+
feedSource(ssId: number, data: ArrayBuffer, ts: number): void;
|
|
39
31
|
/**
|
|
40
32
|
* Feed a repair symbol to the Worker. data ArrayBuffer is transferred (zero-copy).
|
|
41
33
|
* Caller must not access data after this call — it is detached.
|
|
42
34
|
*
|
|
43
35
|
* Carries the wire-level Repair FEC Payload ID fields (ISO 23008-1 §C.5.3):
|
|
44
|
-
* - ssStart: SS_ID of first source symbol in the block (
|
|
45
|
-
* - ssbLength:
|
|
36
|
+
* - ssStart: SS_ID of first source symbol in the block (any uint32; the block key)
|
|
37
|
+
* - ssbLength: authored source-symbol count (1..configured K)
|
|
46
38
|
* - rsId: repair symbol index within the block
|
|
47
39
|
*/
|
|
48
40
|
feedRepair(ssStart: number, ssbLength: number, rsId: number, data: ArrayBuffer, ts: number): void;
|
|
@@ -51,7 +43,8 @@ export declare class FecWorkerClient {
|
|
|
51
43
|
*/
|
|
52
44
|
relayBlockSig(sbn: number, sig: ArrayBuffer, hash: ArrayBuffer): void;
|
|
53
45
|
/**
|
|
54
|
-
* Trigger cleanup after a block's inactivity deadline.
|
|
46
|
+
* Trigger cleanup after a block's inactivity deadline. `currentSbn` is the
|
|
47
|
+
* block key, the block's SS_Start.
|
|
55
48
|
*
|
|
56
49
|
* While one queue-drain barrier is in flight, later SBNs are coalesced into
|
|
57
50
|
* the next command without losing their individual acknowledgement identity.
|
|
@@ -63,14 +56,24 @@ export declare class FecWorkerClient {
|
|
|
63
56
|
onFrame(cb: (data: ArrayBuffer, meta: FrameMeta) => void): void;
|
|
64
57
|
/** Register callback for periodic stats snapshots (FEC-04). */
|
|
65
58
|
onSnapshot(cb: (stats: FecTrackStats) => void): void;
|
|
66
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Register callback for individual block state changes. The Worker posts
|
|
61
|
+
* them only once asked, so registering subscribes (after the Worker
|
|
62
|
+
* announces `subscribeBlockUpdates`). An older Worker that does not
|
|
63
|
+
* announce it posts them unasked.
|
|
64
|
+
*/
|
|
67
65
|
onBlockUpdate(cb: (block: FecBlockSnapshot) => void): void;
|
|
68
|
-
/** Register callback for green-fill frames. */
|
|
69
|
-
onGreenFill(cb: (sbn: number, data: ArrayBuffer) => void): void;
|
|
70
66
|
/** Register callback fired after the Worker drains all commands queued before cleanup. */
|
|
71
67
|
onCleanupComplete(cb: (sbn: number) => void): void;
|
|
72
68
|
/** Register callback for Worker errors. */
|
|
73
69
|
onError(cb: (message: string, code?: FecWorkerErrorCode) => void): void;
|
|
70
|
+
/**
|
|
71
|
+
* True once dispose() ran. A disposed client cannot decode, fill, or
|
|
72
|
+
* acknowledge anything, so routing symbols to it discards them; every
|
|
73
|
+
* command method (feedSource, feedRepair, relayBlockSig, cleanup, reset)
|
|
74
|
+
* throws rather than pretend otherwise. Callers check this first.
|
|
75
|
+
*/
|
|
76
|
+
get disposed(): boolean;
|
|
74
77
|
/**
|
|
75
78
|
* Dispose the Worker. Sends dispose command then terminates.
|
|
76
79
|
* Idempotent — safe to call multiple times.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fec-worker-client.d.ts","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"fec-worker-client.d.ts","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAIX,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,kBAAkB,EAClB,MAAM,uBAAuB,CAAC;AAE/B,qBAAa,eAAe;;IA2B3B;;;OAGG;gBACS,WAAW,EAAE,MAAM,GAAG,GAAG;IAgBrC;;;;;OAKG;IACH,SAAS,CACR,MAAM,EAAE,cAAc,EACtB,UAAU,EAAE,WAAW,CAAC,MAAM,GAC5B,IAAI;IAaP;;;;OAIG;IACH,UAAU,CACT,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,GACR,IAAI;IAKP;;;;;;;;OAQG;IACH,UAAU,CACT,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,WAAW,EACjB,EAAE,EAAE,MAAM,GACR,IAAI;IAoCP;;OAEG;IACH,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,IAAI;IASrE;;;;;;OAMG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAgBjC,8EAA8E;IAC9E,KAAK,IAAI,IAAI;IAoBb,+DAA+D;IAC/D,OAAO,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,KAAK,IAAI,GAAG,IAAI;IAI/D,+DAA+D;IAC/D,UAAU,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI;IAIpD;;;;;OAKG;IACH,aAAa,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IAkB1D,0FAA0F;IAC1F,iBAAiB,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIlD,2CAA2C;IAC3C,OAAO,CAAC,EAAE,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI;IAIvE;;;;;OAKG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,OAAO,IAAI,IAAI;CA6Df"}
|
|
@@ -12,7 +12,6 @@ export class FecWorkerClient {
|
|
|
12
12
|
#onFrame = null;
|
|
13
13
|
#onSnapshot = null;
|
|
14
14
|
#onBlockUpdate = null;
|
|
15
|
-
#onGreenFill = null;
|
|
16
15
|
#onCleanupComplete = null;
|
|
17
16
|
#onError = null;
|
|
18
17
|
#cleanupInFlight = null;
|
|
@@ -20,6 +19,20 @@ export class FecWorkerClient {
|
|
|
20
19
|
#pendingResets = 0;
|
|
21
20
|
#resetFailed = false;
|
|
22
21
|
#disposed = false;
|
|
22
|
+
// Symbols fed during the current task, sent as one feedSymbols message
|
|
23
|
+
// when the task's microtasks run (BLO-36599). A packet stream delivers many
|
|
24
|
+
// symbols per task, and each postMessage is a structured-clone round trip
|
|
25
|
+
// on the page thread; the batch boundary is the task, not a size or timer.
|
|
26
|
+
#pendingSymbols = [];
|
|
27
|
+
#pendingTransfer = [];
|
|
28
|
+
#flushQueued = false;
|
|
29
|
+
// Batching starts only once the Worker announced it accepts feedSymbols.
|
|
30
|
+
#batchSymbols = false;
|
|
31
|
+
// The Worker posts blockUpdate events only to a client that subscribed,
|
|
32
|
+
// which this client does once a callback is registered and the Worker
|
|
33
|
+
// announced the command (BLO-36599).
|
|
34
|
+
#blockUpdatesAnnounced = false;
|
|
35
|
+
#blockUpdatesSubscribed = false;
|
|
23
36
|
/**
|
|
24
37
|
* Create a FecWorkerClient.
|
|
25
38
|
* @param workerOrUrl - Either a Worker instance (for testing) or a URL to create one
|
|
@@ -41,93 +54,94 @@ export class FecWorkerClient {
|
|
|
41
54
|
}
|
|
42
55
|
/**
|
|
43
56
|
* Configure the Worker with FEC parameters and WASM module.
|
|
57
|
+
* `interleaveMs: 0` is a valid no-span catalog value; in that mode the
|
|
58
|
+
* caller supplies a positive receiver-local `deliveryWindowMs`.
|
|
44
59
|
* wasmModule is transferred (not neutered — WebAssembly.Module is shareable).
|
|
45
|
-
* altaPublicKey is PKCS#8 SPKI DER bytes (self-describing — algorithm detected from OID).
|
|
46
|
-
* Transferred (detached from sender).
|
|
47
|
-
* altaWasmModule is structured-cloneable (NOT transferable — do not add to transfer list).
|
|
48
60
|
*/
|
|
49
|
-
configure(config, wasmModule
|
|
61
|
+
configure(config, wasmModule) {
|
|
50
62
|
if (this.#disposed)
|
|
51
63
|
throw new Error("[FecWorkerClient] disposed");
|
|
64
|
+
this.#flushSymbols();
|
|
52
65
|
const cmd = {
|
|
53
66
|
type: "configure",
|
|
54
67
|
config,
|
|
55
68
|
wasmModule,
|
|
56
|
-
altaPublicKey,
|
|
57
|
-
altaWasmModule,
|
|
58
69
|
};
|
|
59
70
|
// WebAssembly.Module is structured-cloneable, NOT transferable.
|
|
60
71
|
// Pass it in the message body (Chrome clones it automatically).
|
|
61
|
-
|
|
62
|
-
const transferList = [];
|
|
63
|
-
if (altaPublicKey)
|
|
64
|
-
transferList.push(altaPublicKey);
|
|
65
|
-
// altaWasmModule is structured-cloneable — NOT added to transferList
|
|
66
|
-
this.#worker.postMessage(cmd, transferList);
|
|
72
|
+
this.#worker.postMessage(cmd);
|
|
67
73
|
}
|
|
68
74
|
/**
|
|
69
75
|
* Feed a source symbol to the Worker. data ArrayBuffer is transferred (zero-copy).
|
|
70
|
-
* Caller must not access data after this call — it is
|
|
71
|
-
*
|
|
72
|
-
* Optional `alta` carries the detached ALTA authenticator bytes and the
|
|
73
|
-
* byte offset in `data` where the signed payload ends. The auth buffer is
|
|
74
|
-
* transferred zero-copy alongside `data`. Use {@link getMmtpAltaTrailer}
|
|
75
|
-
* upstream (mmtp-router) to extract it from the wire packet.
|
|
76
|
+
* Caller must not access data after this call — it is transferred with the
|
|
77
|
+
* task's symbol batch, before any later command of this client.
|
|
76
78
|
*/
|
|
77
|
-
feedSource(ssId, data, ts
|
|
79
|
+
feedSource(ssId, data, ts) {
|
|
78
80
|
if (this.#disposed)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
? {
|
|
82
|
-
type: "feedSource",
|
|
83
|
-
ssId,
|
|
84
|
-
data,
|
|
85
|
-
ts,
|
|
86
|
-
altaAuth: alta.auth,
|
|
87
|
-
altaPayloadEnd: alta.payloadEnd,
|
|
88
|
-
}
|
|
89
|
-
: { type: "feedSource", ssId, data, ts };
|
|
90
|
-
const transfer = alta ? [data, alta.auth] : [data];
|
|
91
|
-
this.#worker.postMessage(cmd, transfer);
|
|
81
|
+
throw new Error("[FecWorkerClient] feedSource on a disposed client");
|
|
82
|
+
this.#queueSymbol({ type: "feedSource", ssId, data, ts }, data);
|
|
92
83
|
}
|
|
93
84
|
/**
|
|
94
85
|
* Feed a repair symbol to the Worker. data ArrayBuffer is transferred (zero-copy).
|
|
95
86
|
* Caller must not access data after this call — it is detached.
|
|
96
87
|
*
|
|
97
88
|
* Carries the wire-level Repair FEC Payload ID fields (ISO 23008-1 §C.5.3):
|
|
98
|
-
* - ssStart: SS_ID of first source symbol in the block (
|
|
99
|
-
* - ssbLength:
|
|
89
|
+
* - ssStart: SS_ID of first source symbol in the block (any uint32; the block key)
|
|
90
|
+
* - ssbLength: authored source-symbol count (1..configured K)
|
|
100
91
|
* - rsId: repair symbol index within the block
|
|
101
92
|
*/
|
|
102
93
|
feedRepair(ssStart, ssbLength, rsId, data, ts) {
|
|
103
94
|
if (this.#disposed)
|
|
95
|
+
throw new Error("[FecWorkerClient] feedRepair on a disposed client");
|
|
96
|
+
this.#queueSymbol({ type: "feedRepair", ssStart, ssbLength, rsId, data, ts }, data);
|
|
97
|
+
}
|
|
98
|
+
#queueSymbol(symbol, data) {
|
|
99
|
+
if (!this.#batchSymbols) {
|
|
100
|
+
this.#worker.postMessage(symbol, [data]);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
this.#pendingSymbols.push(symbol);
|
|
104
|
+
this.#pendingTransfer.push(data);
|
|
105
|
+
if (this.#flushQueued)
|
|
104
106
|
return;
|
|
105
|
-
this.#
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
107
|
+
this.#flushQueued = true;
|
|
108
|
+
queueMicrotask(() => {
|
|
109
|
+
this.#flushQueued = false;
|
|
110
|
+
this.#flushSymbols();
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Send the symbols fed so far. Every other command calls this first, so a
|
|
115
|
+
* cleanup barrier, reset or signature still follows all earlier symbols.
|
|
116
|
+
*/
|
|
117
|
+
#flushSymbols() {
|
|
118
|
+
if (this.#pendingSymbols.length === 0 || this.#disposed)
|
|
119
|
+
return;
|
|
120
|
+
const symbols = this.#pendingSymbols;
|
|
121
|
+
const transfer = this.#pendingTransfer;
|
|
122
|
+
this.#pendingSymbols = [];
|
|
123
|
+
this.#pendingTransfer = [];
|
|
124
|
+
this.#worker.postMessage({ type: "feedSymbols", symbols }, transfer);
|
|
113
125
|
}
|
|
114
126
|
/**
|
|
115
127
|
* Submit relay block signature for verification. sig and hash are transferred.
|
|
116
128
|
*/
|
|
117
129
|
relayBlockSig(sbn, sig, hash) {
|
|
118
130
|
if (this.#disposed)
|
|
119
|
-
|
|
131
|
+
throw new Error("[FecWorkerClient] relayBlockSig on a disposed client");
|
|
132
|
+
this.#flushSymbols();
|
|
120
133
|
this.#worker.postMessage({ type: "relayBlockSig", sbn, sig, hash }, [sig, hash]);
|
|
121
134
|
}
|
|
122
135
|
/**
|
|
123
|
-
* Trigger cleanup after a block's inactivity deadline.
|
|
136
|
+
* Trigger cleanup after a block's inactivity deadline. `currentSbn` is the
|
|
137
|
+
* block key, the block's SS_Start.
|
|
124
138
|
*
|
|
125
139
|
* While one queue-drain barrier is in flight, later SBNs are coalesced into
|
|
126
140
|
* the next command without losing their individual acknowledgement identity.
|
|
127
141
|
*/
|
|
128
142
|
cleanup(currentSbn) {
|
|
129
143
|
if (this.#disposed)
|
|
130
|
-
|
|
144
|
+
throw new Error("[FecWorkerClient] cleanup on a disposed client");
|
|
131
145
|
if (!Number.isInteger(currentSbn) ||
|
|
132
146
|
currentSbn < 0 ||
|
|
133
147
|
currentSbn > 0xffff_ffff) {
|
|
@@ -142,13 +156,17 @@ export class FecWorkerClient {
|
|
|
142
156
|
/** Reset this track's decoder epoch while preserving Worker configuration. */
|
|
143
157
|
reset() {
|
|
144
158
|
if (this.#disposed)
|
|
145
|
-
|
|
159
|
+
throw new Error("[FecWorkerClient] reset on a disposed client");
|
|
160
|
+
this.#flushSymbols();
|
|
146
161
|
this.#pendingResets++;
|
|
147
162
|
this.#cleanupInFlight = null;
|
|
148
163
|
this.#queuedCleanupSbns.clear();
|
|
149
164
|
this.#worker.postMessage({ type: "reset" });
|
|
150
165
|
}
|
|
151
166
|
#postCleanup(sbns) {
|
|
167
|
+
// The cleanup is a queue-drain barrier: every symbol fed before it is
|
|
168
|
+
// posted must reach the Worker first.
|
|
169
|
+
this.#flushSymbols();
|
|
152
170
|
this.#cleanupInFlight = sbns;
|
|
153
171
|
const orderedSbns = [...sbns].sort((a, b) => a - b);
|
|
154
172
|
this.#worker.postMessage({ type: "cleanup", sbns: orderedSbns });
|
|
@@ -161,13 +179,24 @@ export class FecWorkerClient {
|
|
|
161
179
|
onSnapshot(cb) {
|
|
162
180
|
this.#onSnapshot = cb;
|
|
163
181
|
}
|
|
164
|
-
/**
|
|
182
|
+
/**
|
|
183
|
+
* Register callback for individual block state changes. The Worker posts
|
|
184
|
+
* them only once asked, so registering subscribes (after the Worker
|
|
185
|
+
* announces `subscribeBlockUpdates`). An older Worker that does not
|
|
186
|
+
* announce it posts them unasked.
|
|
187
|
+
*/
|
|
165
188
|
onBlockUpdate(cb) {
|
|
166
189
|
this.#onBlockUpdate = cb;
|
|
190
|
+
this.#subscribeBlockUpdates();
|
|
167
191
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
192
|
+
#subscribeBlockUpdates() {
|
|
193
|
+
if (this.#disposed ||
|
|
194
|
+
this.#blockUpdatesSubscribed ||
|
|
195
|
+
!this.#blockUpdatesAnnounced ||
|
|
196
|
+
!this.#onBlockUpdate)
|
|
197
|
+
return;
|
|
198
|
+
this.#blockUpdatesSubscribed = true;
|
|
199
|
+
this.#worker.postMessage({ type: "subscribeBlockUpdates" });
|
|
171
200
|
}
|
|
172
201
|
/** Register callback fired after the Worker drains all commands queued before cleanup. */
|
|
173
202
|
onCleanupComplete(cb) {
|
|
@@ -177,6 +206,15 @@ export class FecWorkerClient {
|
|
|
177
206
|
onError(cb) {
|
|
178
207
|
this.#onError = cb;
|
|
179
208
|
}
|
|
209
|
+
/**
|
|
210
|
+
* True once dispose() ran. A disposed client cannot decode, fill, or
|
|
211
|
+
* acknowledge anything, so routing symbols to it discards them; every
|
|
212
|
+
* command method (feedSource, feedRepair, relayBlockSig, cleanup, reset)
|
|
213
|
+
* throws rather than pretend otherwise. Callers check this first.
|
|
214
|
+
*/
|
|
215
|
+
get disposed() {
|
|
216
|
+
return this.#disposed;
|
|
217
|
+
}
|
|
180
218
|
/**
|
|
181
219
|
* Dispose the Worker. Sends dispose command then terminates.
|
|
182
220
|
* Idempotent — safe to call multiple times.
|
|
@@ -185,12 +223,25 @@ export class FecWorkerClient {
|
|
|
185
223
|
if (this.#disposed)
|
|
186
224
|
return;
|
|
187
225
|
this.#disposed = true;
|
|
226
|
+
// A terminated Worker decodes nothing; symbols still batched are dropped.
|
|
227
|
+
this.#pendingSymbols = [];
|
|
228
|
+
this.#pendingTransfer = [];
|
|
188
229
|
this.#cleanupInFlight = null;
|
|
189
230
|
this.#queuedCleanupSbns.clear();
|
|
190
231
|
this.#worker.postMessage({ type: "dispose" });
|
|
191
232
|
this.#worker.terminate();
|
|
192
233
|
}
|
|
193
234
|
#handleEvent(event) {
|
|
235
|
+
if (event.type === "capabilities") {
|
|
236
|
+
if (Array.isArray(event.commands) && event.commands.includes("feedSymbols")) {
|
|
237
|
+
this.#batchSymbols = true;
|
|
238
|
+
}
|
|
239
|
+
if (Array.isArray(event.commands) && event.commands.includes("subscribeBlockUpdates")) {
|
|
240
|
+
this.#blockUpdatesAnnounced = true;
|
|
241
|
+
this.#subscribeBlockUpdates();
|
|
242
|
+
}
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
194
245
|
if (event.type === "resetComplete") {
|
|
195
246
|
this.#pendingResets = Math.max(0, this.#pendingResets - 1);
|
|
196
247
|
this.#resetFailed = false;
|
|
@@ -219,9 +270,6 @@ export class FecWorkerClient {
|
|
|
219
270
|
case "blockUpdate":
|
|
220
271
|
this.#onBlockUpdate?.(event.block);
|
|
221
272
|
break;
|
|
222
|
-
case "greenFill":
|
|
223
|
-
this.#onGreenFill?.(event.sbn, event.data);
|
|
224
|
-
break;
|
|
225
273
|
case "cleanupComplete":
|
|
226
274
|
for (const sbn of event.sbns) {
|
|
227
275
|
this.#onCleanupComplete?.(sbn);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fec-worker-client.js","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"fec-worker-client.js","sourceRoot":"","sources":["../src/fec-worker-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,OAAO,eAAe;IAClB,OAAO,CAAS;IACzB,QAAQ,GAA0D,IAAI,CAAC;IACvE,WAAW,GAA4C,IAAI,CAAC;IAC5D,cAAc,GAA+C,IAAI,CAAC;IAClE,kBAAkB,GAAmC,IAAI,CAAC;IAC1D,QAAQ,GAAkE,IAAI,CAAC;IAC/E,gBAAgB,GAAuB,IAAI,CAAC;IAC5C,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,cAAc,GAAG,CAAC,CAAC;IACnB,YAAY,GAAG,KAAK,CAAC;IACrB,SAAS,GAAG,KAAK,CAAC;IAClB,uEAAuE;IACvE,4EAA4E;IAC5E,0EAA0E;IAC1E,2EAA2E;IAC3E,eAAe,GAAuB,EAAE,CAAC;IACzC,gBAAgB,GAAmB,EAAE,CAAC;IACtC,YAAY,GAAG,KAAK,CAAC;IACrB,yEAAyE;IACzE,aAAa,GAAG,KAAK,CAAC;IACtB,wEAAwE;IACxE,sEAAsE;IACtE,qCAAqC;IACrC,sBAAsB,GAAG,KAAK,CAAC;IAC/B,uBAAuB,GAAG,KAAK,CAAC;IAEhC;;;OAGG;IACH,YAAY,WAAyB;QACpC,IAAI,WAAW,YAAY,GAAG,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,GAAG,IAAI,MAAM,CAAC,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC5D,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,OAAO,GAAG,WAAW,CAAC;QAC5B,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,SAAS,GAAG,CAAC,CAA+B,EAAE,EAAE,CAC5D,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,EAAE,EAAE;YAC5B,IAAI,CAAC,QAAQ,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QACzD,CAAC,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,cAAc,GAAG,CAAC,CAAC,EAAE,EAAE;YACnC,IAAI,CAAC,QAAQ,EAAE,CAAC,wBAAwB,CAAC,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;QAChE,CAAC,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,SAAS,CACR,MAAsB,EACtB,UAA8B;QAE9B,IAAI,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;QAClE,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,MAAM,GAAG,GAAqB;YAC7B,IAAI,EAAE,WAAW;YACjB,MAAM;YACN,UAAU;SACV,CAAC;QACF,gEAAgE;QAChE,gEAAgE;QAChE,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,UAAU,CACT,IAAY,EACZ,IAAiB,EACjB,EAAU;QAEV,IAAI,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACzF,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IACjE,CAAC;IAED;;;;;;;;OAQG;IACH,UAAU,CACT,OAAe,EACf,SAAiB,EACjB,IAAY,EACZ,IAAiB,EACjB,EAAU;QAEV,IAAI,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACzF,IAAI,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC;IACrF,CAAC;IAED,YAAY,CAAC,MAAwB,EAAE,IAAiB;QACvD,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;YACzC,OAAO;QACR,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QAC9B,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,cAAc,CAAC,GAAG,EAAE;YACnB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,IAAI,CAAC,aAAa,EAAE,CAAC;QACtB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,aAAa;QACZ,IAAI,IAAI,CAAC,eAAe,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAChE,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,CAAC;QACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC;QACvC,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC,OAAO,CAAC,WAAW,CACvB,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAA6B,EAC3D,QAAQ,CACR,CAAC;IACH,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,GAAW,EAAE,GAAgB,EAAE,IAAiB;QAC7D,IAAI,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAC5F,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,OAAO,CAAC,WAAW,CACvB,EAAE,IAAI,EAAE,eAAe,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAA6B,EACpE,CAAC,GAAG,EAAE,IAAI,CAAC,CACX,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,OAAO,CAAC,UAAkB;QACzB,IAAI,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;QACtF,IACC,CAAC,MAAM,CAAC,SAAS,CAAC,UAAU,CAAC;YAC7B,UAAU,GAAG,CAAC;YACd,UAAU,GAAG,WAAW,EACvB,CAAC;YACF,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;QACpE,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC3B,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACxC,OAAO;QACR,CAAC;QACD,IAAI,CAAC,YAAY,CAAC,IAAI,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,8EAA8E;IAC9E,KAAK;QACJ,IAAI,IAAI,CAAC,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACpF,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,cAAc,EAAE,CAAC;QACtB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,OAAO,EAA6B,CAAC,CAAC;IACxE,CAAC;IAED,YAAY,CAAC,IAAiB;QAC7B,sEAAsE;QACtE,sCAAsC;QACtC,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,MAAM,WAAW,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACpD,IAAI,CAAC,OAAO,CAAC,WAAW,CACvB,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAA6B,CACjE,CAAC;IACH,CAAC;IAED,+DAA+D;IAC/D,OAAO,CAAC,EAAgD;QACvD,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACpB,CAAC;IAED,+DAA+D;IAC/D,UAAU,CAAC,EAAkC;QAC5C,IAAI,CAAC,WAAW,GAAG,EAAE,CAAC;IACvB,CAAC;IAED;;;;;OAKG;IACH,aAAa,CAAC,EAAqC;QAClD,IAAI,CAAC,cAAc,GAAG,EAAE,CAAC;QACzB,IAAI,CAAC,sBAAsB,EAAE,CAAC;IAC/B,CAAC;IAED,sBAAsB;QACrB,IACC,IAAI,CAAC,SAAS;YACd,IAAI,CAAC,uBAAuB;YAC5B,CAAC,IAAI,CAAC,sBAAsB;YAC5B,CAAC,IAAI,CAAC,cAAc;YACnB,OAAO;QACT,IAAI,CAAC,uBAAuB,GAAG,IAAI,CAAC;QACpC,IAAI,CAAC,OAAO,CAAC,WAAW,CACvB,EAAE,IAAI,EAAE,uBAAuB,EAA6B,CAC5D,CAAC;IACH,CAAC;IAED,0FAA0F;IAC1F,iBAAiB,CAAC,EAAyB;QAC1C,IAAI,CAAC,kBAAkB,GAAG,EAAE,CAAC;IAC9B,CAAC;IAED,2CAA2C;IAC3C,OAAO,CAAC,EAAwD;QAC/D,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACpB,CAAC;IAED;;;;;OAKG;IACH,IAAI,QAAQ;QACX,OAAO,IAAI,CAAC,SAAS,CAAC;IACvB,CAAC;IAED;;;OAGG;IACH,OAAO;QACN,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,0EAA0E;QAC1E,IAAI,CAAC,eAAe,GAAG,EAAE,CAAC;QAC1B,IAAI,CAAC,gBAAgB,GAAG,EAAE,CAAC;QAC3B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC7B,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;QAChC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,SAAS,EAA6B,CAAC,CAAC;QACzE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;IAC1B,CAAC;IAED,YAAY,CAAC,KAAqB;QACjC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;YACnC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBAC7E,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC;YAC3B,CAAC;YACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,uBAAuB,CAAC,EAAE,CAAC;gBACvF,IAAI,CAAC,sBAAsB,GAAG,IAAI,CAAC;gBACnC,IAAI,CAAC,sBAAsB,EAAE,CAAC;YAC/B,CAAC;YACD,OAAO;QACR,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;YACpC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;YAC3D,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,OAAO;QACR,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC5B,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,EAAE,CAAC;gBACnC,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;gBAC3D,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YAC1B,CAAC;YACD,IAAI,KAAK,CAAC,IAAI;gBAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;;gBACtD,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACpC,OAAO;QACR,CAAC;QACD,IAAI,IAAI,CAAC,cAAc,GAAG,CAAC,IAAI,IAAI,CAAC,YAAY;YAAE,OAAO;QACzD,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,OAAO;gBACX,IAAI,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;gBACxC,MAAM;YACP,KAAK,UAAU;gBACd,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAChC,MAAM;YACP,KAAK,aAAa;gBACjB,IAAI,CAAC,cAAc,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBACnC,MAAM;YACP,KAAK,iBAAiB;gBACrB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC;oBAC9B,IAAI,CAAC,kBAAkB,EAAE,CAAC,GAAG,CAAC,CAAC;gBAChC,CAAC;gBACD,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;gBAC7B,IAAI,IAAI,CAAC,kBAAkB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;oBACtC,MAAM,IAAI,GAAG,IAAI,CAAC,kBAAkB,CAAC;oBACrC,IAAI,CAAC,kBAAkB,GAAG,IAAI,GAAG,EAAE,CAAC;oBACpC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;gBACzB,CAAC;gBACD,MAAM;QACR,CAAC;IACF,CAAC;CACD;AAED,SAAS,iBAAiB,CAAC,KAAiB;IAC3C,MAAM,KAAK,GAAG,CAAC,cAAc,CAAC,CAAC;IAC/B,IAAI,KAAK,CAAC,OAAO;QAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;QACpB,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAClC,IAAI,KAAK,CAAC,MAAM;YAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACtD,IAAI,KAAK,CAAC,KAAK;YAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACpD,KAAK,CAAC,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,KAAK,CAAC,KAAK,YAAY,KAAK,EAAE,CAAC;QAClC,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,EAAE,CAAC;YAClE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACjC,CAAC;QACD,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACtD,CAAC;SAAM,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;QAChC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AAED,SAAS,wBAAwB,CAAC,KAAmB;IACpD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;IACxB,MAAM,MAAM,GACX,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QAClB,IAAI,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5C,KAAK,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IAC/C,OAAO,4DAA4D,MAAM,EAAE,CAAC;AAC7E,CAAC"}
|