sparkforensics-server 0.2.3 → 0.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/package.json +1 -1
- package/public/assets/{AutoscalingChurn-Bf5TN-s_.js → AutoscalingChurn-BOiUUfuY.js} +1 -1
- package/public/assets/{CacheUtilization-DRf_FNrK.js → CacheUtilization-DwX7lisd.js} +1 -1
- package/public/assets/{CachingOpportunity-BTJzIhtI.js → CachingOpportunity-DUE3GKpx.js} +1 -1
- package/public/assets/{ColdStart-DnFgnD3n.js → ColdStart-ZHxPrJmI.js} +1 -1
- package/public/assets/{ConfigAudit-B5FsOdmu.js → ConfigAudit-BkVnq2XE.js} +1 -1
- package/public/assets/{CoreUsageArea-CTksBPuq.js → CoreUsageArea-DobrSXk2.js} +1 -1
- package/public/assets/{Dashboard-BFMsMuc_.js → Dashboard-y3_9iQJo.js} +1 -1
- package/public/assets/{DuplicatePlanSubtree-BCt-Guuh.js → DuplicatePlanSubtree-BSQZFeNq.js} +1 -1
- package/public/assets/{DurationHistogram-xRcnND4k.js → DurationHistogram-DlQv4ZCB.js} +1 -1
- package/public/assets/{EmptyState-CnTrSjtU.js → EmptyState-DqnSxTjJ.js} +1 -1
- package/public/assets/{ExecutorUtilization-CP21XlUq.js → ExecutorUtilization-CuQUxaCd.js} +1 -1
- package/public/assets/{GcPressure-BoEJsLjb.js → GcPressure-CLT5hkMJ.js} +1 -1
- package/public/assets/{ImpactEstimate-BHcqJkcZ.js → ImpactEstimate-Df_laG-n.js} +1 -1
- package/public/assets/{IncompleteRun-b9FWrOHn.js → IncompleteRun-CpIkS4XZ.js} +1 -1
- package/public/assets/{JobFailures-C5FkTq3O.js → JobFailures-DKbvYc4J.js} +1 -1
- package/public/assets/{MemoryUtilization-D2W4AB_f.js → MemoryUtilization-C5kHmXIb.js} +1 -1
- package/public/assets/{OverBroadcast-Btu3xorf.js → OverBroadcast-C5pF66aq.js} +1 -1
- package/public/assets/{PartitionSizing-yZWhLN0e.js → PartitionSizing-BvEJQ5M1.js} +1 -1
- package/public/assets/{PlanExplorer-DSk1ivS4.js → PlanExplorer-DjGTZifP.js} +1 -1
- package/public/assets/{PlanGraphRoute-C3iOzUdB.js → PlanGraphRoute-DoIGA7_H.js} +1 -1
- package/public/assets/{RetryWaste-B4436kr1.js → RetryWaste-C0znHd_w.js} +1 -1
- package/public/assets/{RowPagination-oeWB8gBm.js → RowPagination-2icPidMP.js} +1 -1
- package/public/assets/{ShuffleIO-DUiv8EJe.js → ShuffleIO-L_XSnJ_V.js} +1 -1
- package/public/assets/{Skew-D2lB1oQc.js → Skew-w4uV5H_g.js} +1 -1
- package/public/assets/{SlowHost-Qb_8aa-W.js → SlowHost-D5tWJ1UR.js} +1 -1
- package/public/assets/{SmallFiles-u6suTgfj.js → SmallFiles-CTtNiSOh.js} +1 -1
- package/public/assets/{SpeculationWaste-vUCniiFP.js → SpeculationWaste-xCKwjz2K.js} +1 -1
- package/public/assets/{Spill-BskXFX2e.js → Spill-BT8my24K.js} +1 -1
- package/public/assets/{StageFailed-D9b83Jvy.js → StageFailed-De3vVDbc.js} +1 -1
- package/public/assets/{StageFindingGroup-DagqHOUz.js → StageFindingGroup-R4OW-g4U.js} +1 -1
- package/public/assets/{StageHeader-DibMSunO.js → StageHeader-CGeWqC-0.js} +1 -1
- package/public/assets/{StagePill-Dt6V9yVw.js → StagePill-BwwKLcFl.js} +1 -1
- package/public/assets/{StageShape-CfdrDUZa.js → StageShape-D4LmPVyV.js} +1 -1
- package/public/assets/{StageSlowness-BoGLIYKR.js → StageSlowness-8-z6qn4Q.js} +1 -1
- package/public/assets/{Straggler-DU1WSU-m.js → Straggler-BIgxsowI.js} +1 -1
- package/public/assets/{TaskFailures-DkY6zBNJ.js → TaskFailures-B9lLt6Rw.js} +1 -1
- package/public/assets/{TinyTask-Lsnt5mJF.js → TinyTask-DYXks5sT.js} +1 -1
- package/public/assets/{UnderBroadcast-DxHc0lwy.js → UnderBroadcast-BWqWMpeG.js} +1 -1
- package/public/assets/{finding-anchor-Bl1hRK5E.js → finding-anchor-CDbHtqKT.js} +1 -1
- package/public/assets/{inbox-DHIFHqcn.js → inbox-M_EuWNiK.js} +1 -1
- package/public/assets/{index-U654cmB8.js → index-pnImGbgQ.js} +3 -3
- package/public/assets/{parser-worker-5a9aUx3o.js → parser-worker-C3BAO6z9.js} +4 -2
- package/public/assets/{select-CtKaJ5Hx.js → select-C-MZGNpb.js} +1 -1
- package/public/assets/{toggle-group-DB2VfFIx.js → toggle-group-C3nz7UQ_.js} +1 -1
- package/public/assets/{useRecentFiles-BiYdWs8J.js → useRecentFiles-0XuhS91q.js} +4 -4
- package/public/assets/{useSortMode-BhPky1lW.js → useSortMode-DHlKZeWv.js} +1 -1
- package/public/assets/zstd-worker-C1Jq9lfb.js +1 -0
- package/public/docs/404.html +2 -2
- package/public/docs/assets/{app.BczRxXPr.js → app.DsFQcynM.js} +1 -1
- package/public/docs/assets/chunks/@localSearchIndexroot.BEfYag5Q.js +1 -0
- package/public/docs/assets/chunks/{VPLocalSearchBox.DXvkTVnC.js → VPLocalSearchBox.oa8NbaiV.js} +1 -1
- package/public/docs/assets/chunks/{theme.ByUdwrLG.js → theme.Ca4dR9dL.js} +2 -2
- package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.B1vUqtry.js → contributor-guide_architecture_detector-contract.md.aQQfP7pj.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.sf9DU2bA.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_impact-estimation.md.DHVq1UPt.lean.js → contributor-guide_architecture_impact-estimation.md.sf9DU2bA.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_architecture_overview.md.BXQdgG5u.js +1 -0
- package/public/docs/assets/contributor-guide_architecture_overview.md.BXQdgG5u.lean.js +1 -0
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.C0SovZX8.js → contributor-guide_architecture_worker-protocol.md.P7d1vtEJ.js} +2 -2
- package/public/docs/assets/{contributor-guide_architecture_worker-protocol.md.C0SovZX8.lean.js → contributor-guide_architecture_worker-protocol.md.P7d1vtEJ.lean.js} +1 -1
- package/public/docs/assets/contributor-guide_testing.md.BU6vyA-9.js +1 -0
- package/public/docs/assets/contributor-guide_testing.md.BU6vyA-9.lean.js +1 -0
- package/public/docs/contributor-guide/architecture/board-widgets.html +3 -3
- package/public/docs/contributor-guide/architecture/detector-contract.html +5 -5
- package/public/docs/contributor-guide/architecture/drill-down.html +3 -3
- package/public/docs/contributor-guide/architecture/impact-estimation.html +5 -5
- package/public/docs/contributor-guide/architecture/index.html +3 -3
- package/public/docs/contributor-guide/architecture/overview.html +5 -5
- package/public/docs/contributor-guide/architecture/state-and-history.html +3 -3
- package/public/docs/contributor-guide/architecture/widget-rendering.html +3 -3
- package/public/docs/contributor-guide/architecture/worker-protocol.html +5 -5
- package/public/docs/contributor-guide/contributing.html +3 -3
- package/public/docs/contributor-guide/development-setup.html +3 -3
- package/public/docs/contributor-guide/testing.html +5 -5
- package/public/docs/hashmap.json +1 -1
- package/public/docs/index.html +3 -3
- package/public/docs/tuning-reference/anti-patterns.html +3 -3
- package/public/docs/tuning-reference/aqe.html +3 -3
- package/public/docs/tuning-reference/bottleneck-broadcast-sizing.html +3 -3
- package/public/docs/tuning-reference/bottleneck-cold-start.html +3 -3
- package/public/docs/tuning-reference/bottleneck-duplicate-plan-subtree.html +3 -3
- package/public/docs/tuning-reference/bottleneck-failures.html +3 -3
- package/public/docs/tuning-reference/bottleneck-gc.html +3 -3
- package/public/docs/tuning-reference/bottleneck-job-failure-rate.html +3 -3
- package/public/docs/tuning-reference/bottleneck-memory-utilization.html +3 -3
- package/public/docs/tuning-reference/bottleneck-retry-waste.html +3 -3
- package/public/docs/tuning-reference/bottleneck-shuffle.html +3 -3
- package/public/docs/tuning-reference/bottleneck-skew.html +3 -3
- package/public/docs/tuning-reference/bottleneck-slow-host.html +3 -3
- package/public/docs/tuning-reference/bottleneck-small-files.html +3 -3
- package/public/docs/tuning-reference/bottleneck-spill.html +3 -3
- package/public/docs/tuning-reference/bottleneck-straggler.html +3 -3
- package/public/docs/tuning-reference/bottleneck-tiny-tasks.html +3 -3
- package/public/docs/tuning-reference/bottleneck-utilization.html +3 -3
- package/public/docs/tuning-reference/caching.html +3 -3
- package/public/docs/tuning-reference/cluster-config.html +3 -3
- package/public/docs/tuning-reference/config.html +3 -3
- package/public/docs/tuning-reference/data-formats.html +3 -3
- package/public/docs/tuning-reference/index.html +3 -3
- package/public/docs/tuning-reference/intro.html +3 -3
- package/public/docs/tuning-reference/joins.html +3 -3
- package/public/docs/tuning-reference/memory-model.html +3 -3
- package/public/docs/tuning-reference/metrics.html +3 -3
- package/public/docs/tuning-reference/partitioning.html +3 -3
- package/public/docs/tuning-reference/pyspark.html +3 -3
- package/public/docs/tuning-reference/shuffle.html +3 -3
- package/public/docs/tuning-reference/spark-architecture.html +3 -3
- package/public/docs/tuning-reference/table-formats.html +3 -3
- package/public/docs/user-guide/alternative-log-retrieval.html +3 -3
- package/public/docs/user-guide/getting-started.html +3 -3
- package/public/docs/user-guide/mcp-tools.html +3 -3
- package/public/docs/user-guide/run-comparison.html +3 -3
- package/public/docs/user-guide/understanding-findings.html +3 -3
- package/public/index.html +1 -1
- package/vendor-core/impact-estimator.js +23 -3
- package/vendor-core/occupancy.js +8 -7
- package/vendor-core/parser-worker.js +31 -15
- package/vendor-core/plan-summary.js +1 -1
- package/vendor-core/stage-quantiles.js +53 -1
- package/vendor-core/types.js +5 -0
- package/vendor-core/zstd-worker-client.js +180 -0
- package/vendor-core/zstd-worker.js +103 -0
- package/public/docs/assets/chunks/@localSearchIndexroot.c2HfrwDg.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_impact-estimation.md.DHVq1UPt.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_overview.md.DkXdJ9jE.js +0 -1
- package/public/docs/assets/contributor-guide_architecture_overview.md.DkXdJ9jE.lean.js +0 -1
- package/public/docs/assets/contributor-guide_testing.md.BohuQWvl.js +0 -1
- package/public/docs/assets/contributor-guide_testing.md.BohuQWvl.lean.js +0 -1
- /package/public/docs/assets/{contributor-guide_architecture_detector-contract.md.B1vUqtry.lean.js → contributor-guide_architecture_detector-contract.md.aQQfP7pj.lean.js} +0 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// Parse-worker side of the zstd decompress worker (zstd-worker.ts). Builds a ZstdDecoderFactory
|
|
2
|
+
// for streamFile whose push() ships each compressed read slice to the decompress worker and
|
|
3
|
+
// returns while the worker decodes it, so the parse worker parses slice N's output while the
|
|
4
|
+
// decompress worker decodes slice N+1.
|
|
5
|
+
//
|
|
6
|
+
// Flow control is a window of input slices: push() resolves once fewer than `maxInFlight`
|
|
7
|
+
// slices are unacknowledged, and a slice is acknowledged only when its `consumed` reply is
|
|
8
|
+
// handled, which comes after all of its decoded chunks (each fed to onChunk from the message
|
|
9
|
+
// handler). So at most `maxInFlight` slices' output is ever queued on the parse worker, and a
|
|
10
|
+
// fast decompressor on a slow parse cannot grow memory without bound. The final push resolves
|
|
11
|
+
// only after every chunk was fed, which is what streamFile's callers need before they flush.
|
|
12
|
+
//
|
|
13
|
+
// Buffers move by transfer both ways: push() takes ownership of the slice it is given.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
// Three 512 KiB slices of a ~20x-compressed log keep about 30 MB of output queued at most,
|
|
18
|
+
// and give the decompress worker a slice of slack while the parse worker reads the next one.
|
|
19
|
+
export const MAX_IN_FLIGHT_SLICES = 3;
|
|
20
|
+
|
|
21
|
+
// The slice of the Worker API this client uses; tests pass a MessagePort adapter.
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
// `spawn` starts the decompress worker; it runs at most once, on the first zstd stream, and the
|
|
42
|
+
// worker then serves every later stream (the files of a rolling log) one at a time. When it
|
|
43
|
+
// cannot start (no nested workers, a blocked script, a crash), each stream is decoded by
|
|
44
|
+
// `fallback` on the calling thread instead, the path used before this worker existed.
|
|
45
|
+
export function createWorkerZstdDecoders(
|
|
46
|
+
spawn ,
|
|
47
|
+
fallback ,
|
|
48
|
+
{ maxInFlight = MAX_IN_FLIGHT_SLICES, onFallback } = {},
|
|
49
|
+
) {
|
|
50
|
+
let port = null;
|
|
51
|
+
let ready = null;
|
|
52
|
+
let active = null;
|
|
53
|
+
let nextId = 0;
|
|
54
|
+
|
|
55
|
+
const giveUp = (reason ) => {
|
|
56
|
+
port?.terminate?.();
|
|
57
|
+
port = null;
|
|
58
|
+
ready = Promise.resolve(false);
|
|
59
|
+
onFallback?.(reason);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const startWorker = () => new Promise((resolve) => {
|
|
63
|
+
let candidate ;
|
|
64
|
+
try {
|
|
65
|
+
candidate = spawn();
|
|
66
|
+
} catch (e) {
|
|
67
|
+
giveUp(`could not start the decompress worker: ${e instanceof Error ? e.message : String(e)}`);
|
|
68
|
+
resolve(false);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
let settled = false;
|
|
72
|
+
const settle = (ok , reason = '') => {
|
|
73
|
+
if (settled) return;
|
|
74
|
+
settled = true;
|
|
75
|
+
if (ok) port = candidate;
|
|
76
|
+
else {
|
|
77
|
+
candidate.terminate?.();
|
|
78
|
+
giveUp(reason);
|
|
79
|
+
}
|
|
80
|
+
resolve(ok);
|
|
81
|
+
};
|
|
82
|
+
candidate.onmessage = ({ data }) => {
|
|
83
|
+
if (data.type === 'ready') settle(true);
|
|
84
|
+
else if (active && 'id' in data && data.id === active.id) active.onReply(data);
|
|
85
|
+
};
|
|
86
|
+
candidate.onerror = (ev) => {
|
|
87
|
+
// Handled here: left alone, a nested worker's error also reaches the parse worker's own
|
|
88
|
+
// global handler and from there the page's "Worker crashed" path.
|
|
89
|
+
ev.preventDefault();
|
|
90
|
+
const message = ev.message || 'unknown error';
|
|
91
|
+
if (!settled) {
|
|
92
|
+
settle(false, `the decompress worker failed to load: ${message}`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const stream = active;
|
|
96
|
+
giveUp(`the decompress worker crashed: ${message}`);
|
|
97
|
+
stream?.fail(new Error(`Decompress worker crashed: ${message}`));
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
return (onChunk) => {
|
|
102
|
+
const id = nextId++;
|
|
103
|
+
let started = false;
|
|
104
|
+
let local = null;
|
|
105
|
+
let seq = 0;
|
|
106
|
+
let inFlight = 0;
|
|
107
|
+
let failure = null;
|
|
108
|
+
let wake = null;
|
|
109
|
+
|
|
110
|
+
const until = (done ) => new Promise ((resolve) => {
|
|
111
|
+
const check = () => {
|
|
112
|
+
if (!done()) return;
|
|
113
|
+
wake = null;
|
|
114
|
+
resolve();
|
|
115
|
+
};
|
|
116
|
+
wake = check;
|
|
117
|
+
check();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const stream = {
|
|
121
|
+
id,
|
|
122
|
+
onReply(msg) {
|
|
123
|
+
if (failure) return;
|
|
124
|
+
if (msg.type === 'chunk') {
|
|
125
|
+
try {
|
|
126
|
+
onChunk(new Uint8Array(msg.bytes, 0, msg.length));
|
|
127
|
+
} catch (e) {
|
|
128
|
+
port?.postMessage({ type: 'cancel', id }, []);
|
|
129
|
+
stream.fail(e instanceof Error ? e : new Error(String(e)));
|
|
130
|
+
}
|
|
131
|
+
} else if (msg.type === 'consumed') {
|
|
132
|
+
inFlight--;
|
|
133
|
+
wake?.();
|
|
134
|
+
} else if (msg.type === 'error') {
|
|
135
|
+
stream.fail(new Error(msg.message));
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
fail(err) {
|
|
139
|
+
if (failure) return;
|
|
140
|
+
failure = err;
|
|
141
|
+
if (active === stream) active = null;
|
|
142
|
+
wake?.();
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
const begin = async () => {
|
|
147
|
+
started = true;
|
|
148
|
+
ready ??= startWorker();
|
|
149
|
+
if (!(await ready) || !port) {
|
|
150
|
+
local = fallback(onChunk);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
active = stream;
|
|
154
|
+
port.postMessage({ type: 'start', id }, []);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
async push(chunk, final = false) {
|
|
159
|
+
if (!started) await begin();
|
|
160
|
+
if (local) return local.push(chunk, final);
|
|
161
|
+
if (failure) throw failure;
|
|
162
|
+
// Transfer the slice's own buffer when it spans all of it; copy a view of a larger one.
|
|
163
|
+
const bytes = chunk.byteOffset === 0 && chunk.byteLength === chunk.buffer.byteLength
|
|
164
|
+
? chunk.buffer
|
|
165
|
+
: chunk.slice().buffer;
|
|
166
|
+
if (!port) throw new Error('Decompress worker is gone');
|
|
167
|
+
port.postMessage({ type: 'data', id, seq: seq++, bytes, final }, [bytes]);
|
|
168
|
+
inFlight++;
|
|
169
|
+
await until(() => failure !== null || inFlight < (final ? 1 : maxInFlight));
|
|
170
|
+
if (failure) throw failure;
|
|
171
|
+
if (final && active === stream) active = null;
|
|
172
|
+
},
|
|
173
|
+
cancel() {
|
|
174
|
+
if (local || !started || failure) return;
|
|
175
|
+
port?.postMessage({ type: 'cancel', id }, []);
|
|
176
|
+
stream.fail(new Error('Decompression cancelled'));
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Decompress worker: runs the vendored fzstd off the parse worker's thread, so decompression and
|
|
2
|
+
// NDJSON parsing of a zstd log overlap instead of taking turns. The parse worker spawns it and
|
|
3
|
+
// drives it through zstd-worker-client.ts; both ends of the message protocol live in the types
|
|
4
|
+
// below, and zstd-worker-client.ts documents the flow control.
|
|
5
|
+
import { Decompress as ZstdDecompress } from './vendor/fzstd.js';
|
|
6
|
+
|
|
7
|
+
// Parse worker -> decompress worker. `seq` numbers a stream's input slices from 0.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
// Decompress worker -> parse worker. Every `chunk` for input slice `seq` is posted before that
|
|
14
|
+
// slice's `consumed`, and a stream's `error` is the last message it gets.
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
// fzstd emits one chunk per zstd block (128 KiB at most). Batching them into 1 MiB messages cuts
|
|
24
|
+
// the per-message cost on both threads about eight-fold.
|
|
25
|
+
export const OUTPUT_BATCH_BYTES = 1024 * 1024;
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
// The decompress side of the protocol, transport-free so tests can drive it over a
|
|
31
|
+
// MessageChannel. Holds at most one live stream: `start` replaces it, and a message for any
|
|
32
|
+
// other stream id (one already cancelled or failed) is dropped.
|
|
33
|
+
export function createZstdWorkerHandler(
|
|
34
|
+
post ,
|
|
35
|
+
batchBytes = OUTPUT_BATCH_BYTES,
|
|
36
|
+
) {
|
|
37
|
+
let streamId = -1;
|
|
38
|
+
let decoder = null;
|
|
39
|
+
let batch = new Uint8Array(batchBytes);
|
|
40
|
+
let batchUsed = 0;
|
|
41
|
+
|
|
42
|
+
// Hand `batch` over without copying it again: transfer its whole buffer with the filled
|
|
43
|
+
// length and start a fresh one. fzstd's chunk is a view of a buffer it reuses, so the copy
|
|
44
|
+
// into `batch` is the one copy this path cannot avoid.
|
|
45
|
+
const flush = () => {
|
|
46
|
+
if (batchUsed === 0) return;
|
|
47
|
+
const bytes = batch.buffer ;
|
|
48
|
+
post({ type: 'chunk', id: streamId, bytes, length: batchUsed }, [bytes]);
|
|
49
|
+
batch = new Uint8Array(batchBytes);
|
|
50
|
+
batchUsed = 0;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const collect = (chunk ) => {
|
|
54
|
+
let offset = 0;
|
|
55
|
+
while (offset < chunk.length) {
|
|
56
|
+
const n = Math.min(chunk.length - offset, batchBytes - batchUsed);
|
|
57
|
+
batch.set(chunk.subarray(offset, offset + n), batchUsed);
|
|
58
|
+
batchUsed += n;
|
|
59
|
+
offset += n;
|
|
60
|
+
if (batchUsed === batchBytes) flush();
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const reset = () => {
|
|
65
|
+
decoder = null;
|
|
66
|
+
batchUsed = 0;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return (msg) => {
|
|
70
|
+
if (msg.type === 'start') {
|
|
71
|
+
streamId = msg.id;
|
|
72
|
+
batchUsed = 0;
|
|
73
|
+
decoder = new (ZstdDecompress )(collect);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (msg.id !== streamId || !decoder) return;
|
|
77
|
+
if (msg.type === 'cancel') {
|
|
78
|
+
reset();
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
decoder.push(new Uint8Array(msg.bytes), msg.final);
|
|
83
|
+
flush();
|
|
84
|
+
} catch (e) {
|
|
85
|
+
reset();
|
|
86
|
+
post({ type: 'error', id: msg.id, message: e instanceof Error ? e.message : String(e) });
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
post({ type: 'consumed', id: msg.id, seq: msg.seq });
|
|
90
|
+
if (msg.final) reset();
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ─── Worker message bus (only active when running as a Web Worker) ──────────────
|
|
95
|
+
|
|
96
|
+
const isWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope;
|
|
97
|
+
|
|
98
|
+
if (isWorker) {
|
|
99
|
+
const scope = self ;
|
|
100
|
+
const handle = createZstdWorkerHandler((msg, transfer) => scope.postMessage(msg, transfer ?? []));
|
|
101
|
+
scope.onmessage = ({ data } ) => handle(data);
|
|
102
|
+
scope.postMessage({ type: 'ready' } );
|
|
103
|
+
}
|