turbodata 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +27 -0
- package/README.md +215 -0
- package/dist/compression.d.ts +8 -0
- package/dist/compression.d.ts.map +1 -0
- package/dist/compression.js +19 -0
- package/dist/compression.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/io.d.ts +37 -0
- package/dist/io.d.ts.map +1 -0
- package/dist/io.js +174 -0
- package/dist/io.js.map +1 -0
- package/dist/iterators/message_heap.d.ts +17 -0
- package/dist/iterators/message_heap.d.ts.map +1 -0
- package/dist/iterators/message_heap.js +83 -0
- package/dist/iterators/message_heap.js.map +1 -0
- package/dist/iterators/preloaded_topics_group_iterator.d.ts +38 -0
- package/dist/iterators/preloaded_topics_group_iterator.d.ts.map +1 -0
- package/dist/iterators/preloaded_topics_group_iterator.js +109 -0
- package/dist/iterators/preloaded_topics_group_iterator.js.map +1 -0
- package/dist/iterators/topics_group_iterator.d.ts +45 -0
- package/dist/iterators/topics_group_iterator.d.ts.map +1 -0
- package/dist/iterators/topics_group_iterator.js +132 -0
- package/dist/iterators/topics_group_iterator.js.map +1 -0
- package/dist/loaded_bytes.d.ts +12 -0
- package/dist/loaded_bytes.d.ts.map +1 -0
- package/dist/loaded_bytes.js +43 -0
- package/dist/loaded_bytes.js.map +1 -0
- package/dist/msgpack.d.ts +10 -0
- package/dist/msgpack.d.ts.map +1 -0
- package/dist/msgpack.js +44 -0
- package/dist/msgpack.js.map +1 -0
- package/dist/multi_reader.d.ts +56 -0
- package/dist/multi_reader.d.ts.map +1 -0
- package/dist/multi_reader.js +281 -0
- package/dist/multi_reader.js.map +1 -0
- package/dist/read_fetcher.d.ts +9 -0
- package/dist/read_fetcher.d.ts.map +1 -0
- package/dist/read_fetcher.js +59 -0
- package/dist/read_fetcher.js.map +1 -0
- package/dist/read_options.d.ts +40 -0
- package/dist/read_options.d.ts.map +1 -0
- package/dist/read_options.js +16 -0
- package/dist/read_options.js.map +1 -0
- package/dist/read_planner.d.ts +37 -0
- package/dist/read_planner.d.ts.map +1 -0
- package/dist/read_planner.js +110 -0
- package/dist/read_planner.js.map +1 -0
- package/dist/read_source.d.ts +19 -0
- package/dist/read_source.d.ts.map +1 -0
- package/dist/read_source.js +15 -0
- package/dist/read_source.js.map +1 -0
- package/dist/read_strategy.d.ts +52 -0
- package/dist/read_strategy.d.ts.map +1 -0
- package/dist/read_strategy.js +70 -0
- package/dist/read_strategy.js.map +1 -0
- package/dist/reader.d.ts +209 -0
- package/dist/reader.d.ts.map +1 -0
- package/dist/reader.js +649 -0
- package/dist/reader.js.map +1 -0
- package/dist/sample.d.ts +41 -0
- package/dist/sample.d.ts.map +1 -0
- package/dist/sample.js +528 -0
- package/dist/sample.js.map +1 -0
- package/dist/sort_and_filter.d.ts +25 -0
- package/dist/sort_and_filter.d.ts.map +1 -0
- package/dist/sort_and_filter.js +126 -0
- package/dist/sort_and_filter.js.map +1 -0
- package/dist/sources/blob.d.ts +8 -0
- package/dist/sources/blob.d.ts.map +1 -0
- package/dist/sources/blob.js +38 -0
- package/dist/sources/blob.js.map +1 -0
- package/dist/sources/http.d.ts +17 -0
- package/dist/sources/http.d.ts.map +1 -0
- package/dist/sources/http.js +90 -0
- package/dist/sources/http.js.map +1 -0
- package/dist/types.d.ts +75 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +29 -0
- package/dist/types.js.map +1 -0
- package/package.json +61 -0
- package/src/compression.ts +28 -0
- package/src/index.ts +70 -0
- package/src/io.ts +214 -0
- package/src/iterators/message_heap.ts +104 -0
- package/src/iterators/preloaded_topics_group_iterator.ts +143 -0
- package/src/iterators/topics_group_iterator.ts +180 -0
- package/src/loaded_bytes.ts +61 -0
- package/src/msgpack.ts +46 -0
- package/src/multi_reader.ts +337 -0
- package/src/read_fetcher.ts +74 -0
- package/src/read_options.ts +58 -0
- package/src/read_planner.ts +153 -0
- package/src/read_source.ts +33 -0
- package/src/read_strategy.ts +111 -0
- package/src/reader.ts +910 -0
- package/src/sample.ts +715 -0
- package/src/sort_and_filter.ts +167 -0
- package/src/sources/blob.ts +52 -0
- package/src/sources/http.ts +121 -0
- package/src/types.ts +110 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Copyright 2026 Wanjia He
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
// sortAndFilter: pure version that turns a decoded IndexChunk into a flat,
|
|
15
|
+
// timestamp-ordered list of kept (topicId, timestamp, offsetInChunk) plus
|
|
16
|
+
// per-message lengths. Mirrors go/sort_and_filter.go.
|
|
17
|
+
//
|
|
18
|
+
// The "sort" is by per-message OffsetInChunk; the writer interleaves messages
|
|
19
|
+
// in timestamp order, so offset order == timestamp order within a chunk. We
|
|
20
|
+
// preserve that invariant rather than re-sorting by timestamp explicitly.
|
|
21
|
+
import { MessageHeap } from "./iterators/message_heap.js";
|
|
22
|
+
export function sortAndFilter(topicIndexes, totalLen, topicIds, startTimestamp, endTimestamp, videoDecodable = false) {
|
|
23
|
+
let total = 0;
|
|
24
|
+
for (const ti of topicIndexes) {
|
|
25
|
+
total += ti.messageIndexes.length;
|
|
26
|
+
}
|
|
27
|
+
if (total === 0) {
|
|
28
|
+
return { msgs: [], lens: [] };
|
|
29
|
+
}
|
|
30
|
+
const sorted = new Array(total);
|
|
31
|
+
let writeIdx = 0;
|
|
32
|
+
if (topicIndexes.length === 1) {
|
|
33
|
+
// Fast path: single topic, messages are already in offset order.
|
|
34
|
+
const ti = topicIndexes[0];
|
|
35
|
+
for (const mi of ti.messageIndexes) {
|
|
36
|
+
sorted[writeIdx++] = { topicId: ti.id, mi };
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// K-way merge by offsetInChunk across topics, via a min-heap.
|
|
41
|
+
// Mirrors go/sort_and_filter.go's MessageIndexHeap-based merge: O(N log K)
|
|
42
|
+
// regardless of how many topics share a group.
|
|
43
|
+
const heap = new MessageHeap(false);
|
|
44
|
+
for (let t = 0; t < topicIndexes.length; t++) {
|
|
45
|
+
const mis = topicIndexes[t].messageIndexes;
|
|
46
|
+
if (mis.length === 0) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
heap.push(mis[0].offsetInChunk, { topicSlot: t, cursor: 0 });
|
|
50
|
+
}
|
|
51
|
+
while (heap.size > 0) {
|
|
52
|
+
const top = heap.pop();
|
|
53
|
+
const { topicSlot, cursor } = top.value;
|
|
54
|
+
const ti = topicIndexes[topicSlot];
|
|
55
|
+
sorted[writeIdx++] = { topicId: ti.id, mi: ti.messageIndexes[cursor] };
|
|
56
|
+
const next = cursor + 1;
|
|
57
|
+
if (next < ti.messageIndexes.length) {
|
|
58
|
+
heap.push(ti.messageIndexes[next].offsetInChunk, {
|
|
59
|
+
topicSlot,
|
|
60
|
+
cursor: next,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Per-entry lengths from offset deltas.
|
|
66
|
+
const allLens = new Array(sorted.length);
|
|
67
|
+
for (let i = 0; i < sorted.length - 1; i++) {
|
|
68
|
+
allLens[i] = sorted[i + 1].mi.offsetInChunk - sorted[i].mi.offsetInChunk;
|
|
69
|
+
}
|
|
70
|
+
allLens[allLens.length - 1] =
|
|
71
|
+
totalLen - sorted[sorted.length - 1].mi.offsetInChunk;
|
|
72
|
+
// For video-decodable groups, snap the lower bound back to the anchoring
|
|
73
|
+
// key frame so the kept sequence is decodable cold.
|
|
74
|
+
let effectiveStart = startTimestamp;
|
|
75
|
+
if (videoDecodable) {
|
|
76
|
+
effectiveStart = keyFrameStart(topicIndexes, startTimestamp);
|
|
77
|
+
}
|
|
78
|
+
// Filter by topic + timestamp range.
|
|
79
|
+
const msgs = [];
|
|
80
|
+
const lens = [];
|
|
81
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
82
|
+
const item = sorted[i];
|
|
83
|
+
if (!topicIds.has(item.topicId)) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const ts = item.mi.timestamp;
|
|
87
|
+
if (ts < effectiveStart || ts > endTimestamp) {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
msgs.push({
|
|
91
|
+
timestamp: ts,
|
|
92
|
+
topicId: item.topicId,
|
|
93
|
+
offsetInChunk: item.mi.offsetInChunk,
|
|
94
|
+
});
|
|
95
|
+
lens.push(allLens[i]);
|
|
96
|
+
}
|
|
97
|
+
return { msgs, lens };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Timestamp of the latest key frame whose timestamp is <= startTimestamp, or
|
|
101
|
+
* startTimestamp unchanged if no such key frame exists in this chunk. Mirrors
|
|
102
|
+
* py key_frame_start / go keyFrameStart.
|
|
103
|
+
*
|
|
104
|
+
* Video-decodable groups always hold exactly one topic. keyFrameIndexes are
|
|
105
|
+
* ascending positions into that topic's timestamp-ordered messageIndexes.
|
|
106
|
+
*/
|
|
107
|
+
export function keyFrameStart(topicIndexes, startTimestamp) {
|
|
108
|
+
if (topicIndexes.length === 0) {
|
|
109
|
+
return startTimestamp;
|
|
110
|
+
}
|
|
111
|
+
const ti = topicIndexes[0];
|
|
112
|
+
let anchor = startTimestamp;
|
|
113
|
+
for (const kfIdx of ti.keyFrameIndexes) {
|
|
114
|
+
const ts = ti.messageIndexes[kfIdx].timestamp;
|
|
115
|
+
if (ts > startTimestamp) {
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
anchor = ts;
|
|
119
|
+
}
|
|
120
|
+
return anchor;
|
|
121
|
+
}
|
|
122
|
+
/** Convenience: sort+filter directly from a decoded IndexChunk. */
|
|
123
|
+
export function sortAndFilterChunk(chunk, topicIds, startTimestamp, endTimestamp) {
|
|
124
|
+
return sortAndFilter(chunk.topicIndexes, chunk.uncompressedLen, topicIds, startTimestamp, endTimestamp);
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=sort_and_filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sort_and_filter.js","sourceRoot":"","sources":["../src/sort_and_filter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ReadSource } from "../read_source.js";
|
|
2
|
+
export declare class BlobReadSource implements ReadSource {
|
|
3
|
+
private readonly blob;
|
|
4
|
+
constructor(blob: Blob);
|
|
5
|
+
size(): Promise<bigint>;
|
|
6
|
+
read(offset: bigint, length: bigint): Promise<Uint8Array>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=blob.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../../src/sources/blob.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,qBAAa,cAAe,YAAW,UAAU;IAC/C,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAO;gBAEhB,IAAI,EAAE,IAAI;IAIhB,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAIvB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAmBhE"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Copyright 2026 Wanjia He
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
export class BlobReadSource {
|
|
15
|
+
blob;
|
|
16
|
+
constructor(blob) {
|
|
17
|
+
this.blob = blob;
|
|
18
|
+
}
|
|
19
|
+
async size() {
|
|
20
|
+
return BigInt(this.blob.size);
|
|
21
|
+
}
|
|
22
|
+
async read(offset, length) {
|
|
23
|
+
// Blob.slice takes number offsets. We accept that browsers can't host a
|
|
24
|
+
// single Blob > 2^53 bytes anyway; clamp via Number() and validate.
|
|
25
|
+
const start = Number(offset);
|
|
26
|
+
const end = Number(offset + length);
|
|
27
|
+
if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end)) {
|
|
28
|
+
throw new Error(`BlobReadSource.read: offset/length exceed Number.MAX_SAFE_INTEGER (offset=${offset}, length=${length})`);
|
|
29
|
+
}
|
|
30
|
+
const slice = this.blob.slice(start, end);
|
|
31
|
+
const buf = await slice.arrayBuffer();
|
|
32
|
+
if (buf.byteLength !== Number(length)) {
|
|
33
|
+
throw new Error(`BlobReadSource.read: short read at offset ${offset}, expected ${length}, got ${buf.byteLength}`);
|
|
34
|
+
}
|
|
35
|
+
return new Uint8Array(buf);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=blob.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blob.js","sourceRoot":"","sources":["../../src/sources/blob.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,EAAE;AACF,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,iDAAiD;AACjD,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AASjC,MAAM,OAAO,cAAc;IACR,IAAI,CAAO;IAE5B,YAAY,IAAU;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,KAAK,CAAC,IAAI;QACR,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,MAAc;QACvC,wEAAwE;QACxE,oEAAoE;QACpE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7B,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/D,MAAM,IAAI,KAAK,CACb,6EAA6E,MAAM,YAAY,MAAM,GAAG,CACzG,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,WAAW,EAAE,CAAC;QACtC,IAAI,GAAG,CAAC,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CACb,6CAA6C,MAAM,cAAc,MAAM,SAAS,GAAG,CAAC,UAAU,EAAE,CACjG,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReadSource } from "../read_source.js";
|
|
2
|
+
export interface HttpRangeReadSourceOptions {
|
|
3
|
+
/** Custom fetch implementation (e.g. for tests or auth headers). */
|
|
4
|
+
fetch?: typeof fetch;
|
|
5
|
+
/** Extra request init applied to every request (e.g. credentials, headers). */
|
|
6
|
+
init?: RequestInit;
|
|
7
|
+
}
|
|
8
|
+
export declare class HttpRangeReadSource implements ReadSource {
|
|
9
|
+
private readonly url;
|
|
10
|
+
private readonly fetchImpl;
|
|
11
|
+
private readonly init;
|
|
12
|
+
private cachedSize;
|
|
13
|
+
constructor(url: string, opts?: HttpRangeReadSourceOptions);
|
|
14
|
+
size(): Promise<bigint>;
|
|
15
|
+
read(offset: bigint, length: bigint): Promise<Uint8Array>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/sources/http.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAEpD,MAAM,WAAW,0BAA0B;IACzC,oEAAoE;IACpE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,+EAA+E;IAC/E,IAAI,CAAC,EAAE,WAAW,CAAC;CACpB;AAED,qBAAa,mBAAoB,YAAW,UAAU;IACpD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;IACzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAc;IACnC,OAAO,CAAC,UAAU,CAAqB;gBAE3B,GAAG,EAAE,MAAM,EAAE,IAAI,GAAE,0BAA+B;IAMxD,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC;IAkDvB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAyBhE"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Copyright 2026 Wanjia He
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
export class HttpRangeReadSource {
|
|
15
|
+
url;
|
|
16
|
+
fetchImpl;
|
|
17
|
+
init;
|
|
18
|
+
cachedSize;
|
|
19
|
+
constructor(url, opts = {}) {
|
|
20
|
+
this.url = url;
|
|
21
|
+
this.fetchImpl = opts.fetch ?? fetch.bind(globalThis);
|
|
22
|
+
this.init = opts.init ?? {};
|
|
23
|
+
}
|
|
24
|
+
async size() {
|
|
25
|
+
if (this.cachedSize !== undefined) {
|
|
26
|
+
return this.cachedSize;
|
|
27
|
+
}
|
|
28
|
+
// Try HEAD first.
|
|
29
|
+
let resp;
|
|
30
|
+
try {
|
|
31
|
+
resp = await this.fetchImpl(this.url, { ...this.init, method: "HEAD" });
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
throw new Error(`HttpRangeReadSource: HEAD failed for ${this.url}: ${err.message}`);
|
|
35
|
+
}
|
|
36
|
+
if (resp.ok) {
|
|
37
|
+
const cl = resp.headers.get("content-length");
|
|
38
|
+
if (cl !== null) {
|
|
39
|
+
const v = BigInt(cl);
|
|
40
|
+
this.cachedSize = v;
|
|
41
|
+
return v;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Fallback: a 1-byte ranged GET, parse Content-Range total.
|
|
45
|
+
const probe = await this.fetchImpl(this.url, {
|
|
46
|
+
...this.init,
|
|
47
|
+
method: "GET",
|
|
48
|
+
headers: { ...(this.init.headers ?? {}), Range: "bytes=0-0" },
|
|
49
|
+
});
|
|
50
|
+
if (!probe.ok && probe.status !== 206) {
|
|
51
|
+
throw new Error(`HttpRangeReadSource: probe GET failed: ${probe.status} ${probe.statusText}`);
|
|
52
|
+
}
|
|
53
|
+
const cr = probe.headers.get("content-range");
|
|
54
|
+
if (cr === null) {
|
|
55
|
+
throw new Error(`HttpRangeReadSource: server returned no Content-Length or Content-Range`);
|
|
56
|
+
}
|
|
57
|
+
// Format: "bytes 0-0/12345"
|
|
58
|
+
const m = /\/(\d+)$/.exec(cr);
|
|
59
|
+
if (m === null) {
|
|
60
|
+
throw new Error(`HttpRangeReadSource: malformed Content-Range: ${cr}`);
|
|
61
|
+
}
|
|
62
|
+
const v = BigInt(m[1]);
|
|
63
|
+
this.cachedSize = v;
|
|
64
|
+
// Drain the probe body so the connection can be reused.
|
|
65
|
+
await probe.arrayBuffer();
|
|
66
|
+
return v;
|
|
67
|
+
}
|
|
68
|
+
async read(offset, length) {
|
|
69
|
+
if (length <= 0n) {
|
|
70
|
+
return new Uint8Array(0);
|
|
71
|
+
}
|
|
72
|
+
const end = offset + length - 1n;
|
|
73
|
+
const headers = new Headers(this.init.headers);
|
|
74
|
+
headers.set("Range", `bytes=${offset.toString()}-${end.toString()}`);
|
|
75
|
+
const resp = await this.fetchImpl(this.url, {
|
|
76
|
+
...this.init,
|
|
77
|
+
method: "GET",
|
|
78
|
+
headers,
|
|
79
|
+
});
|
|
80
|
+
if (!resp.ok && resp.status !== 206) {
|
|
81
|
+
throw new Error(`HttpRangeReadSource: GET range ${offset}-${end} failed: ${resp.status} ${resp.statusText}`);
|
|
82
|
+
}
|
|
83
|
+
const buf = new Uint8Array(await resp.arrayBuffer());
|
|
84
|
+
if (BigInt(buf.byteLength) !== length) {
|
|
85
|
+
throw new Error(`HttpRangeReadSource: short read at ${offset}, expected ${length}, got ${buf.byteLength}`);
|
|
86
|
+
}
|
|
87
|
+
return buf;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=http.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/sources/http.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export interface Footer {
|
|
2
|
+
/** uncompressed: int64. Length of the (zstd-compressed) summary block. */
|
|
3
|
+
summaryLen: bigint;
|
|
4
|
+
/** Always equals "7URB0" for valid files. */
|
|
5
|
+
magic: Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
export interface Summary {
|
|
8
|
+
topicsInfos: TopicsInfo[];
|
|
9
|
+
}
|
|
10
|
+
export interface TopicsInfo {
|
|
11
|
+
topicMetadatas: TopicMetadata[];
|
|
12
|
+
indexChunkInfoList: IndexChunkInfo[];
|
|
13
|
+
/**
|
|
14
|
+
* int64. Total length of all index chunks for this topic group, in bytes
|
|
15
|
+
* (compressed). Used by the reader to compute the length of the *last*
|
|
16
|
+
* index chunk in the group.
|
|
17
|
+
*/
|
|
18
|
+
totalLen: bigint;
|
|
19
|
+
}
|
|
20
|
+
export interface TopicMetadata {
|
|
21
|
+
/** uint16 */
|
|
22
|
+
id: number;
|
|
23
|
+
name: string;
|
|
24
|
+
/**
|
|
25
|
+
* Decoded msgpack map. Int64 values are decoded as bigint (via
|
|
26
|
+
* `useBigInt64: true`); smaller integers stay as number.
|
|
27
|
+
*/
|
|
28
|
+
metadata: Map<string, unknown>;
|
|
29
|
+
/** uint32. Number of messages written into this topic. */
|
|
30
|
+
messageCount: number;
|
|
31
|
+
}
|
|
32
|
+
export interface IndexChunkInfo {
|
|
33
|
+
/** int64 */
|
|
34
|
+
startTimestamp: bigint;
|
|
35
|
+
/** int64 */
|
|
36
|
+
endTimestamp: bigint;
|
|
37
|
+
/** int64. Absolute file offset where the (compressed) index chunk starts. */
|
|
38
|
+
offset: bigint;
|
|
39
|
+
}
|
|
40
|
+
export interface IndexChunk {
|
|
41
|
+
topicIndexes: TopicIndex[];
|
|
42
|
+
/** int64. Absolute file offset of this index chunk's data. */
|
|
43
|
+
chunkOffset: bigint;
|
|
44
|
+
/** int64. On-disk (possibly compressed) data length in bytes. */
|
|
45
|
+
chunkLen: bigint;
|
|
46
|
+
/** int64. Decompressed data length, used to compute the last message's length. */
|
|
47
|
+
uncompressedLen: bigint;
|
|
48
|
+
}
|
|
49
|
+
export interface TopicIndex {
|
|
50
|
+
/** uint16 */
|
|
51
|
+
id: number;
|
|
52
|
+
messageIndexes: MessageIndex[];
|
|
53
|
+
/**
|
|
54
|
+
* uint32 entries: ascending positions into `messageIndexes` marking key
|
|
55
|
+
* frames. Consumed by the video-decodable read/sample paths to anchor GOPs.
|
|
56
|
+
*/
|
|
57
|
+
keyFrameIndexes: Uint32Array;
|
|
58
|
+
}
|
|
59
|
+
export interface MessageIndex {
|
|
60
|
+
/** int64 */
|
|
61
|
+
timestamp: bigint;
|
|
62
|
+
/** int64. Offset relative to the start of the (uncompressed) data chunk. */
|
|
63
|
+
offsetInChunk: bigint;
|
|
64
|
+
}
|
|
65
|
+
/** The 5 magic bytes that close every turbodata file: "7URB0". */
|
|
66
|
+
export declare const MAGIC: Uint8Array;
|
|
67
|
+
/** Footer is fixed at 13 bytes: 8 (summaryLen) + 5 (magic). */
|
|
68
|
+
export declare const FOOTER_LEN = 13;
|
|
69
|
+
export declare const META_KEY_SCHEMA_NAME = "schema_name";
|
|
70
|
+
export declare const META_KEY_SCHEMA_ENCODING = "schema_encoding";
|
|
71
|
+
export declare const META_KEY_SCHEMA_DATA = "schema_data";
|
|
72
|
+
export declare const META_KEY_COMPRESSED = "__td_is_compressed";
|
|
73
|
+
export declare const META_KEY_VIDEO = "__td_is_video";
|
|
74
|
+
export declare const META_KEY_CHUNK_CONFIG = "__td_chunk_config";
|
|
75
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAoBA,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,KAAK,EAAE,UAAU,CAAC;CACnB;AAED,MAAM,WAAW,OAAO;IACtB,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,UAAU;IACzB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,kBAAkB,EAAE,cAAc,EAAE,CAAC;IACrC;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,aAAa;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY;IACZ,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY;IACZ,YAAY,EAAE,MAAM,CAAC;IACrB,6EAA6E;IAC7E,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,UAAU,EAAE,CAAC;IAC3B,8DAA8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,UAAU;IACzB,aAAa;IACb,EAAE,EAAE,MAAM,CAAC;IACX,cAAc,EAAE,YAAY,EAAE,CAAC;IAC/B;;;OAGG;IACH,eAAe,EAAE,WAAW,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IAC3B,YAAY;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,kEAAkE;AAClE,eAAO,MAAM,KAAK,EAAE,UAA2D,CAAC;AAEhF,+DAA+D;AAC/D,eAAO,MAAM,UAAU,KAAK,CAAC;AAG7B,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAClD,eAAO,MAAM,wBAAwB,oBAAoB,CAAC;AAC1D,eAAO,MAAM,oBAAoB,gBAAgB,CAAC;AAMlD,eAAO,MAAM,mBAAmB,uBAAuB,CAAC;AACxD,eAAO,MAAM,cAAc,kBAAkB,CAAC;AAC9C,eAAO,MAAM,qBAAqB,sBAAsB,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Copyright 2026 Wanjia He
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
/** The 5 magic bytes that close every turbodata file: "7URB0". */
|
|
15
|
+
export const MAGIC = new Uint8Array([0x37, 0x55, 0x52, 0x42, 0x30]);
|
|
16
|
+
/** Footer is fixed at 13 bytes: 8 (summaryLen) + 5 (magic). */
|
|
17
|
+
export const FOOTER_LEN = 13;
|
|
18
|
+
// Common per-topic schema metadata keys. SCHEMA_DATA holds raw schema bytes.
|
|
19
|
+
export const META_KEY_SCHEMA_NAME = "schema_name";
|
|
20
|
+
export const META_KEY_SCHEMA_ENCODING = "schema_encoding";
|
|
21
|
+
export const META_KEY_SCHEMA_DATA = "schema_data";
|
|
22
|
+
// Internal per-topic metadata keys used to carry write-format control flags
|
|
23
|
+
// alongside caller-supplied metadata. The __td_ prefix namespaces them away
|
|
24
|
+
// from user keys. The reader reads these back to drive decompression and
|
|
25
|
+
// video-aware sampling.
|
|
26
|
+
export const META_KEY_COMPRESSED = "__td_is_compressed";
|
|
27
|
+
export const META_KEY_VIDEO = "__td_is_video";
|
|
28
|
+
export const META_KEY_CHUNK_CONFIG = "__td_chunk_config";
|
|
29
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,EAAE;AACF,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,iDAAiD;AACjD,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAgFjC,kEAAkE;AAClE,MAAM,CAAC,MAAM,KAAK,GAAe,IAAI,UAAU,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;AAEhF,+DAA+D;AAC/D,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,CAAC;AAE7B,6EAA6E;AAC7E,MAAM,CAAC,MAAM,oBAAoB,GAAG,aAAa,CAAC;AAClD,MAAM,CAAC,MAAM,wBAAwB,GAAG,iBAAiB,CAAC;AAC1D,MAAM,CAAC,MAAM,oBAAoB,GAAG,aAAa,CAAC;AAElD,4EAA4E;AAC5E,4EAA4E;AAC5E,yEAAyE;AACzE,wBAAwB;AACxB,MAAM,CAAC,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AACxD,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAC;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,mBAAmB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "turbodata",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Browser TypeScript reader for the turbodata format.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"author": "Wanjia He",
|
|
7
|
+
"homepage": "https://github.com/opheadacheh/turbodata#readme",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/opheadacheh/turbodata.git",
|
|
11
|
+
"directory": "ts"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/opheadacheh/turbodata/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"turbodata",
|
|
18
|
+
"container-format",
|
|
19
|
+
"timeseries",
|
|
20
|
+
"robotics",
|
|
21
|
+
"mcap",
|
|
22
|
+
"msgpack",
|
|
23
|
+
"zstd"
|
|
24
|
+
],
|
|
25
|
+
"type": "module",
|
|
26
|
+
"main": "dist/index.js",
|
|
27
|
+
"module": "dist/index.js",
|
|
28
|
+
"types": "dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"src",
|
|
38
|
+
"README.md",
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"NOTICE"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsc -p tsconfig.build.json",
|
|
44
|
+
"test": "vitest run",
|
|
45
|
+
"test:watch": "vitest",
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"example:read": "tsx examples/read.ts",
|
|
48
|
+
"example:sample": "tsx examples/sample.ts",
|
|
49
|
+
"example:multiread": "tsx examples/multiread.ts"
|
|
50
|
+
},
|
|
51
|
+
"dependencies": {
|
|
52
|
+
"@msgpack/msgpack": "^3.0.0",
|
|
53
|
+
"fzstd": "^0.1.1"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/node": "^20.11.0",
|
|
57
|
+
"tsx": "^4.7.0",
|
|
58
|
+
"typescript": "^5.4.0",
|
|
59
|
+
"vitest": "^4.1.8"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Copyright 2026 Wanjia He
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
// Pluggable zstd decompression. Default backend is fzstd (pure JS, sync).
|
|
16
|
+
|
|
17
|
+
import { decompress as fzstdDecompress } from "fzstd";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Decompress a zstd-compressed buffer. Must accept the exact bytes produced
|
|
21
|
+
* by github.com/klauspost/compress/zstd's EncodeAll (i.e. the standard zstd
|
|
22
|
+
* frame format).
|
|
23
|
+
*/
|
|
24
|
+
export type Decompressor = (compressed: Uint8Array) => Uint8Array;
|
|
25
|
+
|
|
26
|
+
export const defaultDecompressor: Decompressor = (compressed) => {
|
|
27
|
+
return fzstdDecompress(compressed);
|
|
28
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Copyright 2026 Wanjia He
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
// you may not use this file except in compliance with the License.
|
|
5
|
+
// You may obtain a copy of the License at
|
|
6
|
+
//
|
|
7
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
//
|
|
9
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
// See the License for the specific language governing permissions and
|
|
13
|
+
// limitations under the License.
|
|
14
|
+
|
|
15
|
+
// Public API surface.
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
Reader,
|
|
19
|
+
SampleValidationError,
|
|
20
|
+
TopicRemapError,
|
|
21
|
+
DEFAULT_SAMPLE_STRATEGY,
|
|
22
|
+
linSpaceTimestamps,
|
|
23
|
+
} from "./reader.js";
|
|
24
|
+
export type {
|
|
25
|
+
Frame,
|
|
26
|
+
Message,
|
|
27
|
+
ReaderOptions,
|
|
28
|
+
SampleQuery,
|
|
29
|
+
SampleResult,
|
|
30
|
+
SampleOptions,
|
|
31
|
+
} from "./reader.js";
|
|
32
|
+
|
|
33
|
+
export { MultiReader, VideoSourcesOverlapError } from "./multi_reader.js";
|
|
34
|
+
|
|
35
|
+
export type { ReadOptions, Order } from "./read_options.js";
|
|
36
|
+
export { MAX_INT64 } from "./read_options.js";
|
|
37
|
+
|
|
38
|
+
export type { ReadStrategy } from "./read_strategy.js";
|
|
39
|
+
export {
|
|
40
|
+
StrategyForLatency,
|
|
41
|
+
StrategyForMoney,
|
|
42
|
+
StrategyForBlended,
|
|
43
|
+
} from "./read_strategy.js";
|
|
44
|
+
|
|
45
|
+
export type { ReadSource } from "./read_source.js";
|
|
46
|
+
export { BlobReadSource } from "./sources/blob.js";
|
|
47
|
+
export { HttpRangeReadSource } from "./sources/http.js";
|
|
48
|
+
export type { HttpRangeReadSourceOptions } from "./sources/http.js";
|
|
49
|
+
|
|
50
|
+
export type { Decompressor } from "./compression.js";
|
|
51
|
+
export { defaultDecompressor } from "./compression.js";
|
|
52
|
+
|
|
53
|
+
export type {
|
|
54
|
+
Footer,
|
|
55
|
+
Summary,
|
|
56
|
+
TopicsInfo,
|
|
57
|
+
TopicMetadata,
|
|
58
|
+
IndexChunkInfo,
|
|
59
|
+
IndexChunk,
|
|
60
|
+
TopicIndex,
|
|
61
|
+
MessageIndex,
|
|
62
|
+
} from "./types.js";
|
|
63
|
+
export {
|
|
64
|
+
META_KEY_COMPRESSED,
|
|
65
|
+
META_KEY_SCHEMA_NAME,
|
|
66
|
+
META_KEY_SCHEMA_ENCODING,
|
|
67
|
+
META_KEY_SCHEMA_DATA,
|
|
68
|
+
META_KEY_VIDEO,
|
|
69
|
+
META_KEY_CHUNK_CONFIG,
|
|
70
|
+
} from "./types.js";
|