edfcore 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.
Files changed (188) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +319 -0
  3. package/dist/bytes/latin1.d.ts +43 -0
  4. package/dist/bytes/latin1.d.ts.map +1 -0
  5. package/dist/bytes/latin1.js +84 -0
  6. package/dist/bytes/latin1.js.map +1 -0
  7. package/dist/bytes/numbers.d.ts +45 -0
  8. package/dist/bytes/numbers.d.ts.map +1 -0
  9. package/dist/bytes/numbers.js +87 -0
  10. package/dist/bytes/numbers.js.map +1 -0
  11. package/dist/bytes/view.d.ts +27 -0
  12. package/dist/bytes/view.d.ts.map +1 -0
  13. package/dist/bytes/view.js +53 -0
  14. package/dist/bytes/view.js.map +1 -0
  15. package/dist/constants.d.ts +113 -0
  16. package/dist/constants.d.ts.map +1 -0
  17. package/dist/constants.js +83 -0
  18. package/dist/constants.js.map +1 -0
  19. package/dist/decode/digital.d.ts +49 -0
  20. package/dist/decode/digital.d.ts.map +1 -0
  21. package/dist/decode/digital.js +180 -0
  22. package/dist/decode/digital.js.map +1 -0
  23. package/dist/decode/physical.d.ts +37 -0
  24. package/dist/decode/physical.d.ts.map +1 -0
  25. package/dist/decode/physical.js +178 -0
  26. package/dist/decode/physical.js.map +1 -0
  27. package/dist/diagnostics/codes.d.ts +38 -0
  28. package/dist/diagnostics/codes.d.ts.map +1 -0
  29. package/dist/diagnostics/codes.js +134 -0
  30. package/dist/diagnostics/codes.js.map +1 -0
  31. package/dist/diagnostics/collector.d.ts +76 -0
  32. package/dist/diagnostics/collector.d.ts.map +1 -0
  33. package/dist/diagnostics/collector.js +95 -0
  34. package/dist/diagnostics/collector.js.map +1 -0
  35. package/dist/diagnostics/format.d.ts +21 -0
  36. package/dist/diagnostics/format.d.ts.map +1 -0
  37. package/dist/diagnostics/format.js +125 -0
  38. package/dist/diagnostics/format.js.map +1 -0
  39. package/dist/errors.d.ts +143 -0
  40. package/dist/errors.d.ts.map +1 -0
  41. package/dist/errors.js +135 -0
  42. package/dist/errors.js.map +1 -0
  43. package/dist/header/dates.d.ts +101 -0
  44. package/dist/header/dates.d.ts.map +1 -0
  45. package/dist/header/dates.js +378 -0
  46. package/dist/header/dates.js.map +1 -0
  47. package/dist/header/fields.d.ts +105 -0
  48. package/dist/header/fields.d.ts.map +1 -0
  49. package/dist/header/fields.js +363 -0
  50. package/dist/header/fields.js.map +1 -0
  51. package/dist/header/identification.d.ts +47 -0
  52. package/dist/header/identification.d.ts.map +1 -0
  53. package/dist/header/identification.js +156 -0
  54. package/dist/header/identification.js.map +1 -0
  55. package/dist/header/lookup.d.ts +33 -0
  56. package/dist/header/lookup.d.ts.map +1 -0
  57. package/dist/header/lookup.js +67 -0
  58. package/dist/header/lookup.js.map +1 -0
  59. package/dist/header/parse.d.ts +32 -0
  60. package/dist/header/parse.d.ts.map +1 -0
  61. package/dist/header/parse.js +319 -0
  62. package/dist/header/parse.js.map +1 -0
  63. package/dist/header/scale.d.ts +68 -0
  64. package/dist/header/scale.d.ts.map +1 -0
  65. package/dist/header/scale.js +154 -0
  66. package/dist/header/scale.js.map +1 -0
  67. package/dist/header/signals.d.ts +64 -0
  68. package/dist/header/signals.d.ts.map +1 -0
  69. package/dist/header/signals.js +374 -0
  70. package/dist/header/signals.js.map +1 -0
  71. package/dist/header/variant.d.ts +67 -0
  72. package/dist/header/variant.d.ts.map +1 -0
  73. package/dist/header/variant.js +180 -0
  74. package/dist/header/variant.js.map +1 -0
  75. package/dist/index.d.ts +46 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +59 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/inspect.d.ts +31 -0
  80. package/dist/inspect.d.ts.map +1 -0
  81. package/dist/inspect.js +165 -0
  82. package/dist/inspect.js.map +1 -0
  83. package/dist/io/blob.d.ts +14 -0
  84. package/dist/io/blob.d.ts.map +1 -0
  85. package/dist/io/blob.js +29 -0
  86. package/dist/io/blob.js.map +1 -0
  87. package/dist/io/bytes.d.ts +11 -0
  88. package/dist/io/bytes.d.ts.map +1 -0
  89. package/dist/io/bytes.js +26 -0
  90. package/dist/io/bytes.js.map +1 -0
  91. package/dist/io/cached.d.ts +19 -0
  92. package/dist/io/cached.d.ts.map +1 -0
  93. package/dist/io/cached.js +129 -0
  94. package/dist/io/cached.js.map +1 -0
  95. package/dist/io/http.d.ts +20 -0
  96. package/dist/io/http.d.ts.map +1 -0
  97. package/dist/io/http.js +225 -0
  98. package/dist/io/http.js.map +1 -0
  99. package/dist/io/read.d.ts +42 -0
  100. package/dist/io/read.d.ts.map +1 -0
  101. package/dist/io/read.js +119 -0
  102. package/dist/io/read.js.map +1 -0
  103. package/dist/io/source.d.ts +33 -0
  104. package/dist/io/source.d.ts.map +1 -0
  105. package/dist/io/source.js +72 -0
  106. package/dist/io/source.js.map +1 -0
  107. package/dist/node.d.ts +70 -0
  108. package/dist/node.d.ts.map +1 -0
  109. package/dist/node.js +109 -0
  110. package/dist/node.js.map +1 -0
  111. package/dist/record-index.d.ts +58 -0
  112. package/dist/record-index.d.ts.map +1 -0
  113. package/dist/record-index.js +290 -0
  114. package/dist/record-index.js.map +1 -0
  115. package/dist/recording.d.ts +57 -0
  116. package/dist/recording.d.ts.map +1 -0
  117. package/dist/recording.js +178 -0
  118. package/dist/recording.js.map +1 -0
  119. package/dist/tal/annotations.d.ts +36 -0
  120. package/dist/tal/annotations.d.ts.map +1 -0
  121. package/dist/tal/annotations.js +437 -0
  122. package/dist/tal/annotations.js.map +1 -0
  123. package/dist/tal/grammar.d.ts +111 -0
  124. package/dist/tal/grammar.d.ts.map +1 -0
  125. package/dist/tal/grammar.js +324 -0
  126. package/dist/tal/grammar.js.map +1 -0
  127. package/dist/tal/ticks.d.ts +69 -0
  128. package/dist/tal/ticks.d.ts.map +1 -0
  129. package/dist/tal/ticks.js +137 -0
  130. package/dist/tal/ticks.js.map +1 -0
  131. package/dist/time/segments.d.ts +33 -0
  132. package/dist/time/segments.d.ts.map +1 -0
  133. package/dist/time/segments.js +87 -0
  134. package/dist/time/segments.js.map +1 -0
  135. package/dist/time/timeline.d.ts +62 -0
  136. package/dist/time/timeline.d.ts.map +1 -0
  137. package/dist/time/timeline.js +227 -0
  138. package/dist/time/timeline.js.map +1 -0
  139. package/dist/time/window.d.ts +54 -0
  140. package/dist/time/window.d.ts.map +1 -0
  141. package/dist/time/window.js +232 -0
  142. package/dist/time/window.js.map +1 -0
  143. package/dist/types.d.ts +409 -0
  144. package/dist/types.d.ts.map +1 -0
  145. package/dist/types.js +16 -0
  146. package/dist/types.js.map +1 -0
  147. package/dist/validate.d.ts +53 -0
  148. package/dist/validate.d.ts.map +1 -0
  149. package/dist/validate.js +483 -0
  150. package/dist/validate.js.map +1 -0
  151. package/package.json +77 -0
  152. package/src/bytes/latin1.ts +87 -0
  153. package/src/bytes/numbers.ts +115 -0
  154. package/src/bytes/view.ts +59 -0
  155. package/src/constants.ts +96 -0
  156. package/src/decode/digital.ts +258 -0
  157. package/src/decode/physical.ts +235 -0
  158. package/src/diagnostics/codes.ts +209 -0
  159. package/src/diagnostics/collector.ts +129 -0
  160. package/src/diagnostics/format.ts +143 -0
  161. package/src/errors.ts +208 -0
  162. package/src/header/dates.ts +453 -0
  163. package/src/header/fields.ts +455 -0
  164. package/src/header/identification.ts +196 -0
  165. package/src/header/lookup.ts +81 -0
  166. package/src/header/parse.ts +398 -0
  167. package/src/header/scale.ts +268 -0
  168. package/src/header/signals.ts +550 -0
  169. package/src/header/variant.ts +239 -0
  170. package/src/index.ts +151 -0
  171. package/src/inspect.ts +181 -0
  172. package/src/io/blob.ts +31 -0
  173. package/src/io/bytes.ts +29 -0
  174. package/src/io/cached.ts +136 -0
  175. package/src/io/http.ts +299 -0
  176. package/src/io/read.ts +152 -0
  177. package/src/io/source.ts +87 -0
  178. package/src/node.ts +138 -0
  179. package/src/record-index.ts +389 -0
  180. package/src/recording.ts +234 -0
  181. package/src/tal/annotations.ts +574 -0
  182. package/src/tal/grammar.ts +540 -0
  183. package/src/tal/ticks.ts +172 -0
  184. package/src/time/segments.ts +109 -0
  185. package/src/time/timeline.ts +290 -0
  186. package/src/time/window.ts +304 -0
  187. package/src/types.ts +490 -0
  188. package/src/validate.ts +610 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob.js","sourceRoot":"","sources":["../../src/io/blob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/E,MAAM,UAAU,UAAU,CAAC,IAAc;IACvC,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC;IAE7B,OAAO;QACL,UAAU;QACV,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,MAAc,EAAE,OAAqB;YAC9D,cAAc,CAAC,OAAO,CAAC,CAAC;YACxB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;YAC5C,IAAI,MAAM,KAAK,CAAC;gBAAE,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;YAC3C,kEAAkE;YAClE,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YACvE,cAAc,CAAC,OAAO,CAAC,CAAC;YACxB,OAAO,eAAe,CAAC,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACjE,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The in-memory adapter.
3
+ *
4
+ * Layer 5. Zero-copy by construction: a read hands back a `subarray` view over the caller's own
5
+ * buffer. That is safe precisely because it is the caller's buffer — the "returned array is
6
+ * owned by the caller" rule exists to stop an adapter handing out a view into state it retains,
7
+ * and this adapter retains nothing the caller does not already hold.
8
+ */
9
+ import type { ByteSource } from '../types.js';
10
+ export declare function byteSource(bytes: ArrayBuffer | Uint8Array): ByteSource;
11
+ //# sourceMappingURL=bytes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/io/bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAe,MAAM,aAAa,CAAC;AAG3D,wBAAgB,UAAU,CAAC,KAAK,EAAE,WAAW,GAAG,UAAU,GAAG,UAAU,CAgBtE"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The in-memory adapter.
3
+ *
4
+ * Layer 5. Zero-copy by construction: a read hands back a `subarray` view over the caller's own
5
+ * buffer. That is safe precisely because it is the caller's buffer — the "returned array is
6
+ * owned by the caller" rule exists to stop an adapter handing out a view into state it retains,
7
+ * and this adapter retains nothing the caller does not already hold.
8
+ */
9
+ import { assertExactRead, assertReadRange, throwIfAborted } from './source.js';
10
+ export function byteSource(bytes) {
11
+ // `ArrayBuffer.isView` rather than `instanceof Uint8Array`: `instanceof` is false for a view
12
+ // that crossed a realm boundary (a worker, an iframe), and this is a public entry point.
13
+ const view = ArrayBuffer.isView(bytes) ? bytes : new Uint8Array(bytes);
14
+ const byteLength = view.byteLength;
15
+ return {
16
+ byteLength,
17
+ async read(offset, length, options) {
18
+ throwIfAborted(options);
19
+ assertReadRange(offset, length, byteLength);
20
+ // `subarray` is relative to this view, so a Uint8Array handed in with a non-zero
21
+ // byteOffset over a larger buffer is respected with no offset arithmetic here.
22
+ return assertExactRead(view.subarray(offset, offset + length), offset, length);
23
+ },
24
+ };
25
+ }
26
+ //# sourceMappingURL=bytes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bytes.js","sourceRoot":"","sources":["../../src/io/bytes.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/E,MAAM,UAAU,UAAU,CAAC,KAA+B;IACxD,6FAA6F;IAC7F,yFAAyF;IACzF,MAAM,IAAI,GAAe,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IACnF,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,CAAC;IAEnC,OAAO;QACL,UAAU;QACV,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,MAAc,EAAE,OAAqB;YAC9D,cAAc,CAAC,OAAO,CAAC,CAAC;YACxB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;YAC5C,iFAAiF;YACjF,+EAA+E;YAC/E,OAAO,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACjF,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The only cache in edfcore: opt-in, visible at the call site, and removed by deleting one
3
+ * wrapper from the expression that built the source.
4
+ *
5
+ * Layer 5. A block-aligned LRU over an arbitrary `ByteSource`. Two properties matter more than
6
+ * the hit rate:
7
+ *
8
+ * 1. A read returns a COPY, never a view into a retained block. `types.ts` gives the returned
9
+ * array to the caller, and a caller who writes into it must not be able to corrupt what the
10
+ * next reader sees.
11
+ * 2. Concurrent reads wanting the same block issue ONE underlying read. Over HTTP the
12
+ * difference between one request and eight is the difference between usable and not.
13
+ *
14
+ * Block boundaries are byte-aligned, not record-aligned: this module is format-independent by
15
+ * construction and has no access to a header, so there is no record size to align to.
16
+ */
17
+ import type { ByteSource, CacheOptions } from '../types.js';
18
+ export declare function cachedSource(source: ByteSource, options?: CacheOptions): ByteSource;
19
+ //# sourceMappingURL=cached.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cached.d.ts","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAmBzE,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,UAAU,CAmGnF"}
@@ -0,0 +1,129 @@
1
+ /**
2
+ * The only cache in edfcore: opt-in, visible at the call site, and removed by deleting one
3
+ * wrapper from the expression that built the source.
4
+ *
5
+ * Layer 5. A block-aligned LRU over an arbitrary `ByteSource`. Two properties matter more than
6
+ * the hit rate:
7
+ *
8
+ * 1. A read returns a COPY, never a view into a retained block. `types.ts` gives the returned
9
+ * array to the caller, and a caller who writes into it must not be able to corrupt what the
10
+ * next reader sees.
11
+ * 2. Concurrent reads wanting the same block issue ONE underlying read. Over HTTP the
12
+ * difference between one request and eight is the difference between usable and not.
13
+ *
14
+ * Block boundaries are byte-aligned, not record-aligned: this module is format-independent by
15
+ * construction and has no access to a header, so there is no record size to align to.
16
+ */
17
+ import { assertExactRead, assertReadRange, throwIfAborted } from './source.js';
18
+ const DEFAULT_BLOCK_BYTES = 1024 * 1024;
19
+ const DEFAULT_MAX_BYTES = 64 * 1024 * 1024;
20
+ /** Delegation with the contract still enforced, for configurations that cannot cache. */
21
+ function passThrough(source) {
22
+ return {
23
+ byteLength: source.byteLength,
24
+ async read(offset, length, options) {
25
+ return assertExactRead(await source.read(offset, length, options), offset, length);
26
+ },
27
+ async close() {
28
+ await source.close?.();
29
+ },
30
+ };
31
+ }
32
+ export function cachedSource(source, options) {
33
+ const byteLength = source.byteLength;
34
+ const maxBytes = Math.max(0, Math.floor(options?.maxBytes ?? DEFAULT_MAX_BYTES));
35
+ const requestedBlockBytes = Math.max(1, Math.floor(options?.blockBytes ?? DEFAULT_BLOCK_BYTES));
36
+ // A block wider than the whole budget would evict itself on every insert, so the block is
37
+ // clamped to the budget rather than the cache being left in a state that can never hold one.
38
+ const blockBytes = Math.min(requestedBlockBytes, maxBytes);
39
+ if (blockBytes < 1)
40
+ return passThrough(source);
41
+ /** Insertion order is LRU order, oldest first; a hit re-inserts to move to the back. */
42
+ const blocks = new Map();
43
+ const inflight = new Map();
44
+ let cachedBytes = 0;
45
+ function evict() {
46
+ // Deleting during Map iteration is well defined and never revisits an entry, so this walks
47
+ // strictly from the oldest block forward.
48
+ for (const [index, block] of blocks) {
49
+ if (cachedBytes <= maxBytes)
50
+ return;
51
+ blocks.delete(index);
52
+ cachedBytes -= block.byteLength;
53
+ }
54
+ }
55
+ function admit(index, block) {
56
+ if (blocks.has(index))
57
+ return;
58
+ blocks.set(index, block);
59
+ cachedBytes += block.byteLength;
60
+ evict();
61
+ }
62
+ async function fetchBlock(index, options) {
63
+ const start = index * blockBytes;
64
+ const length = Math.min(blockBytes, byteLength - start);
65
+ return assertExactRead(await source.read(start, length, options), start, length);
66
+ }
67
+ function blockFor(index, options) {
68
+ const cached = blocks.get(index);
69
+ if (cached !== undefined) {
70
+ blocks.delete(index);
71
+ blocks.set(index, cached);
72
+ return Promise.resolve(cached);
73
+ }
74
+ const pending = inflight.get(index);
75
+ // Deduped readers share the first caller's options, which is inherent to issuing one read
76
+ // for all of them.
77
+ if (pending !== undefined)
78
+ return pending;
79
+ const started = fetchBlock(index, options)
80
+ .then((block) => {
81
+ admit(index, block);
82
+ return block;
83
+ })
84
+ .finally(() => {
85
+ inflight.delete(index);
86
+ });
87
+ inflight.set(index, started);
88
+ return started;
89
+ }
90
+ return {
91
+ byteLength,
92
+ async read(offset, length, options) {
93
+ throwIfAborted(options);
94
+ assertReadRange(offset, length, byteLength);
95
+ if (length === 0)
96
+ return new Uint8Array(0);
97
+ // A read wider than the entire budget cannot benefit from the cache and would evict every
98
+ // block on its way through, so it goes straight to the source.
99
+ if (length > maxBytes) {
100
+ return assertExactRead(await source.read(offset, length, options), offset, length);
101
+ }
102
+ const firstBlock = Math.floor(offset / blockBytes);
103
+ const lastBlock = Math.floor((offset + length - 1) / blockBytes);
104
+ const pending = [];
105
+ for (let index = firstBlock; index <= lastBlock; index += 1) {
106
+ pending.push(blockFor(index, options));
107
+ }
108
+ const resolved = await Promise.all(pending);
109
+ throwIfAborted(options);
110
+ const out = new Uint8Array(length);
111
+ for (const [position, block] of resolved.entries()) {
112
+ const blockStart = (firstBlock + position) * blockBytes;
113
+ const from = Math.max(offset, blockStart);
114
+ const to = Math.min(offset + length, blockStart + block.byteLength);
115
+ if (to <= from)
116
+ continue;
117
+ out.set(block.subarray(from - blockStart, to - blockStart), from - offset);
118
+ }
119
+ return assertExactRead(out, offset, length);
120
+ },
121
+ async close() {
122
+ blocks.clear();
123
+ inflight.clear();
124
+ cachedBytes = 0;
125
+ await source.close?.();
126
+ },
127
+ };
128
+ }
129
+ //# sourceMappingURL=cached.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cached.js","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The HTTP Range adapter.
3
+ *
4
+ * Layer 5. Turns a URL into random access without downloading the recording, which is the
5
+ * whole reason a 13 GiB BDF can be opened in a browser tab.
6
+ *
7
+ * Three things here are load-bearing and easy to get wrong:
8
+ *
9
+ * 1. A byte range is INCLUSIVE at both ends. `bytes=0-0` is one byte.
10
+ * 2. A `200 OK` answer to a Range request means the server ignored the header and is sending
11
+ * the whole resource. That is refused by default — silently buffering gigabytes because a
12
+ * CDN is misconfigured is exactly the kind of invisible cost this library exists to refuse.
13
+ * 3. The source length must be known before any read, because random access is meaningless
14
+ * without it. Three ways are tried, cheapest first, and failing to find one is fatal.
15
+ */
16
+ import type { ByteSource, HttpSourceOptions } from '../types.js';
17
+ export declare function httpSource(url: string | {
18
+ readonly href: string;
19
+ }, options?: HttpSourceOptions): Promise<ByteSource>;
20
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/io/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAEV,UAAU,EAGV,iBAAiB,EAElB,MAAM,aAAa,CAAC;AA0MrB,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EACvC,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,UAAU,CAAC,CAgErB"}
@@ -0,0 +1,225 @@
1
+ /**
2
+ * The HTTP Range adapter.
3
+ *
4
+ * Layer 5. Turns a URL into random access without downloading the recording, which is the
5
+ * whole reason a 13 GiB BDF can be opened in a browser tab.
6
+ *
7
+ * Three things here are load-bearing and easy to get wrong:
8
+ *
9
+ * 1. A byte range is INCLUSIVE at both ends. `bytes=0-0` is one byte.
10
+ * 2. A `200 OK` answer to a Range request means the server ignored the header and is sending
11
+ * the whole resource. That is refused by default — silently buffering gigabytes because a
12
+ * CDN is misconfigured is exactly the kind of invisible cost this library exists to refuse.
13
+ * 3. The source length must be known before any read, because random access is meaningless
14
+ * without it. Three ways are tried, cheapest first, and failing to find one is fatal.
15
+ */
16
+ import { EdfSourceError } from '../errors.js';
17
+ import { assertExactRead, assertReadRange, throwIfAborted } from './source.js';
18
+ /** Small enough to stay polite to a shared origin, large enough to hide latency. */
19
+ const DEFAULT_MAX_CONCURRENCY = 4;
20
+ const HTTP_PARTIAL_CONTENT = 206;
21
+ const HTTP_OK = 200;
22
+ function createGate(limit) {
23
+ let active = 0;
24
+ const waiting = [];
25
+ return {
26
+ async acquire() {
27
+ if (active < limit) {
28
+ active += 1;
29
+ return;
30
+ }
31
+ await new Promise((resolve) => {
32
+ waiting.push(resolve);
33
+ });
34
+ // The releaser transferred its slot to us, so `active` is deliberately left unchanged.
35
+ },
36
+ release() {
37
+ const next = waiting.shift();
38
+ if (next !== undefined) {
39
+ next();
40
+ return;
41
+ }
42
+ active -= 1;
43
+ },
44
+ };
45
+ }
46
+ /**
47
+ * A real `Headers` lookup is case-insensitive, but `HttpResponseLike` is structural and a
48
+ * hand-written test double usually is not. Both spellings are tried.
49
+ */
50
+ function headerOf(response, name) {
51
+ return response.headers.get(name) ?? response.headers.get(name.toLowerCase());
52
+ }
53
+ function parseNonNegativeInteger(text) {
54
+ if (text === null)
55
+ return undefined;
56
+ const trimmed = text.trim();
57
+ if (!/^\d+$/.test(trimmed))
58
+ return undefined;
59
+ const value = Number(trimmed);
60
+ return Number.isSafeInteger(value) ? value : undefined;
61
+ }
62
+ /** `Content-Range: bytes 0-0/12345` -> 12345. A `/*` total is unknown, not zero. */
63
+ function totalFromContentRange(value) {
64
+ if (value === null)
65
+ return undefined;
66
+ const slash = value.lastIndexOf('/');
67
+ if (slash < 0)
68
+ return undefined;
69
+ return parseNonNegativeInteger(value.slice(slash + 1));
70
+ }
71
+ function isSuccess(status) {
72
+ return status >= 200 && status < 300;
73
+ }
74
+ function hrefOf(url) {
75
+ return typeof url === 'string' ? url : url.href;
76
+ }
77
+ function resolveFetch(options) {
78
+ const provided = options?.fetch;
79
+ if (provided !== undefined)
80
+ return provided;
81
+ // `fetch` cannot be named as a global without the DOM lib, so it is reached structurally.
82
+ const ambient = globalThis.fetch;
83
+ if (ambient !== undefined)
84
+ return ambient;
85
+ throw new EdfSourceError('httpSource() found no fetch implementation: this runtime does not expose globalThis.fetch ' +
86
+ 'and options.fetch was not given. Next: pass options.fetch with any function matching ' +
87
+ 'FetchLike.', { offset: 0, requestedLength: 0 });
88
+ }
89
+ /**
90
+ * `FetchLike` deliberately does not name `signal`: naming it would pull in the real DOM
91
+ * `AbortSignal` by parameter contravariance, the exact dependency the shims exist to avoid. It
92
+ * is still handed to the implementation at runtime.
93
+ *
94
+ * It is attached only when it carries `addEventListener`, i.e. when it genuinely is an
95
+ * `AbortSignal`. The platform `fetch` throws a `TypeError` on anything else, and a caller who
96
+ * passed a bare `{ aborted }` shim is still served by the `throwIfAborted` polls around the
97
+ * request.
98
+ */
99
+ function attachSignal(init, signal) {
100
+ if (signal === undefined)
101
+ return;
102
+ if (typeof signal.addEventListener !== 'function')
103
+ return;
104
+ init.signal = signal;
105
+ }
106
+ function request(fetchImpl, href, headers, method, signal) {
107
+ const init = { headers, method };
108
+ attachSignal(init, signal);
109
+ return fetchImpl(href, init);
110
+ }
111
+ function rangeIgnoredError(href, offset, length) {
112
+ const lastByte = offset + length - 1;
113
+ return new EdfSourceError(`The server answered ${HTTP_OK} OK instead of ${HTTP_PARTIAL_CONTENT} Partial Content for ` +
114
+ `Range bytes=${offset}-${lastByte} on ${href}, so it ignored the Range header and is ` +
115
+ 'sending the whole resource (HTTP_RANGE_IGNORED). edfcore will not silently buffer a ' +
116
+ 'recording nobody asked for. Next: serve the file from an origin or CDN that supports ' +
117
+ 'byte ranges, or pass allowFullDownload: true to fetch it once and serve reads from ' +
118
+ 'memory.', { offset, requestedLength: length });
119
+ }
120
+ async function resolveSource(fetchImpl, href, baseHeaders, options) {
121
+ const signal = options?.signal;
122
+ const declared = options?.byteLength;
123
+ if (declared !== undefined) {
124
+ if (!Number.isSafeInteger(declared) || declared < 0) {
125
+ throw new EdfSourceError(`httpSource() was given options.byteLength ${declared}, which is not a non-negative ` +
126
+ 'safe integer. Next: pass the real resource size in bytes, or omit it and let ' +
127
+ 'edfcore probe for it.', { offset: 0, requestedLength: 0 });
128
+ }
129
+ return { byteLength: declared, body: undefined };
130
+ }
131
+ try {
132
+ const head = await request(fetchImpl, href, baseHeaders, 'HEAD', signal);
133
+ if (isSuccess(head.status)) {
134
+ const length = parseNonNegativeInteger(headerOf(head, 'Content-Length'));
135
+ if (length !== undefined)
136
+ return { byteLength: length, body: undefined };
137
+ }
138
+ }
139
+ catch {
140
+ // A rejected or forbidden HEAD is common (CORS, some object stores). Fall through to the
141
+ // one-byte range probe rather than failing on it.
142
+ }
143
+ const probeHeaders = { ...baseHeaders, Range: 'bytes=0-0' };
144
+ const probe = await request(fetchImpl, href, probeHeaders, 'GET', signal);
145
+ if (probe.status === HTTP_PARTIAL_CONTENT) {
146
+ const total = totalFromContentRange(headerOf(probe, 'Content-Range'));
147
+ if (total !== undefined)
148
+ return { byteLength: total, body: undefined };
149
+ }
150
+ else if (probe.status === HTTP_OK) {
151
+ // The probe already committed the server to sending everything. Refusing now costs the
152
+ // caller nothing, and accepting means one download instead of two.
153
+ if (options?.allowFullDownload !== true)
154
+ throw rangeIgnoredError(href, 0, 1);
155
+ const body = new Uint8Array(await probe.arrayBuffer());
156
+ return { byteLength: body.byteLength, body };
157
+ }
158
+ else if (!isSuccess(probe.status)) {
159
+ throw new EdfSourceError(`httpSource() could not read ${href}: the server answered HTTP ${probe.status} to a ` +
160
+ 'Range probe. Next: check the URL, its authentication headers and its CORS policy.', { offset: 0, requestedLength: 1 });
161
+ }
162
+ throw new EdfSourceError(`httpSource() could not determine the size of ${href}: HEAD returned no usable ` +
163
+ 'Content-Length and a Range probe returned no Content-Range total, so no byte offset can ' +
164
+ 'be addressed and random access is impossible. Next: pass options.byteLength if you know ' +
165
+ 'the size, or serve the file from an origin that reports one.', { offset: 0, requestedLength: 0 });
166
+ }
167
+ export async function httpSource(url, options) {
168
+ const href = hrefOf(url);
169
+ const fetchImpl = resolveFetch(options);
170
+ const baseHeaders = { ...options?.headers };
171
+ const gate = createGate(Math.max(1, Math.floor(options?.maxConcurrency ?? DEFAULT_MAX_CONCURRENCY)));
172
+ const resolved = await resolveSource(fetchImpl, href, baseHeaders, options);
173
+ const byteLength = resolved.byteLength;
174
+ /** Set once, and only ever when the server ignored Range and the caller allowed it. */
175
+ let fullBody = resolved.body;
176
+ async function fetchRange(offset, length, readOptions) {
177
+ const signal = readOptions?.signal ?? options?.signal;
178
+ // INCLUSIVE end: `bytes=0-0` is one byte, so the last addressed byte is offset+length-1.
179
+ const headers = { ...baseHeaders, Range: `bytes=${offset}-${offset + length - 1}` };
180
+ await gate.acquire();
181
+ try {
182
+ throwIfAborted(readOptions);
183
+ const response = await request(fetchImpl, href, headers, 'GET', signal);
184
+ if (response.status === HTTP_PARTIAL_CONTENT) {
185
+ const bytes = new Uint8Array(await response.arrayBuffer());
186
+ return assertExactRead(bytes, offset, length);
187
+ }
188
+ if (response.status === HTTP_OK) {
189
+ if (options?.allowFullDownload !== true)
190
+ throw rangeIgnoredError(href, offset, length);
191
+ const body = new Uint8Array(await response.arrayBuffer());
192
+ // A concurrent read may have won the race; either copy is the same resource, so keep
193
+ // whichever landed first and let this one be collected.
194
+ if (fullBody === undefined)
195
+ fullBody = body;
196
+ return sliceFullBody(fullBody, offset, length);
197
+ }
198
+ throw new EdfSourceError(`Reading bytes ${offset}..${offset + length - 1} of ${href} failed: the server ` +
199
+ `answered HTTP ${response.status}. Next: check the URL, its authentication headers ` +
200
+ 'and whether a signed URL has expired.', { offset, requestedLength: length });
201
+ }
202
+ finally {
203
+ gate.release();
204
+ }
205
+ }
206
+ return {
207
+ byteLength,
208
+ async read(offset, length, readOptions) {
209
+ throwIfAborted(readOptions);
210
+ assertReadRange(offset, length, byteLength);
211
+ if (length === 0)
212
+ return new Uint8Array(0);
213
+ if (fullBody !== undefined)
214
+ return sliceFullBody(fullBody, offset, length);
215
+ const bytes = await fetchRange(offset, length, readOptions);
216
+ throwIfAborted(readOptions);
217
+ return bytes;
218
+ },
219
+ };
220
+ }
221
+ /** `slice`, not `subarray`: the buffered body is retained state and the caller owns its result. */
222
+ function sliceFullBody(body, offset, length) {
223
+ return assertExactRead(body.slice(offset, offset + length), offset, length);
224
+ }
225
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/io/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Header prefetch and record-range translation.
3
+ *
4
+ * Layer 6. Nothing here interprets a byte: this module decides WHICH bytes to ask a `ByteSource`
5
+ * for, and hands them to the pure layer unchanged. Two rules are the whole file.
6
+ *
7
+ * 1. A header costs exactly TWO source reads — 256 bytes to learn the signal count, then the
8
+ * remaining `256 * ns` as one range. Never one read per signal block, and never a speculative
9
+ * read of a size the file has not stated.
10
+ * 2. The unit of I/O is the RECORD RANGE, never the channel range. `readRecordBytes` issues one
11
+ * contiguous read covering every signal over the requested records, and de-interleaving
12
+ * happens in memory afterwards. There is no cheap single-channel read in EDF — ten seconds of
13
+ * one channel out of thirty is a 27x overread spread over ten requests, against a single
14
+ * 153,600-byte read for all thirty — and this API says so instead of hiding it.
15
+ *
16
+ * The exact-length contract is re-verified here even though every bundled adapter already checks
17
+ * it, because a `ByteSource` may be the caller's own and a silently short read is
18
+ * indistinguishable from a truncated file.
19
+ */
20
+ import type { ByteSource, EdfHeader, OpenOptions, ReadOptions, RecordRange } from '../types.js';
21
+ /**
22
+ * Parse the header of `source`, reading it in exactly two ranges.
23
+ *
24
+ * Both reads are clamped to the source length so that a file too short for the header it declares
25
+ * reaches `parseHeader` and is reported as `SOURCE_TOO_SMALL` — a file defect — instead of
26
+ * surfacing as an `EdfSourceError` about a range past the end, which would blame the source for
27
+ * the file's problem.
28
+ */
29
+ export declare function readHeader(source: ByteSource, options?: OpenOptions): Promise<EdfHeader>;
30
+ /**
31
+ * The bytes of a record range: ONE contiguous read covering every signal.
32
+ *
33
+ * The returned buffer is exactly `records.count * header.recordByteLength` bytes and begins at
34
+ * record `records.start`, which is precisely what `decodeDigital` and `decodeAnnotations` demand
35
+ * — pass it to them unsliced.
36
+ *
37
+ * A zero-record range issues no read at all. A zero-length HTTP range is not expressible (`bytes=
38
+ * n--1`), and there is nothing to fetch, so returning the empty buffer is both cheaper and more
39
+ * honest than asking for it.
40
+ */
41
+ export declare function readRecordBytes(source: ByteSource, header: EdfHeader, records: RecordRange, options?: ReadOptions): Promise<Uint8Array>;
42
+ //# sourceMappingURL=read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/io/read.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAYH,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAoBhG;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAwB9F;AAyCD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,UAAU,CAAC,CAYrB"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Header prefetch and record-range translation.
3
+ *
4
+ * Layer 6. Nothing here interprets a byte: this module decides WHICH bytes to ask a `ByteSource`
5
+ * for, and hands them to the pure layer unchanged. Two rules are the whole file.
6
+ *
7
+ * 1. A header costs exactly TWO source reads — 256 bytes to learn the signal count, then the
8
+ * remaining `256 * ns` as one range. Never one read per signal block, and never a speculative
9
+ * read of a size the file has not stated.
10
+ * 2. The unit of I/O is the RECORD RANGE, never the channel range. `readRecordBytes` issues one
11
+ * contiguous read covering every signal over the requested records, and de-interleaving
12
+ * happens in memory afterwards. There is no cheap single-channel read in EDF — ten seconds of
13
+ * one channel out of thirty is a 27x overread spread over ten requests, against a single
14
+ * 153,600-byte read for all thirty — and this API says so instead of hiding it.
15
+ *
16
+ * The exact-length contract is re-verified here even though every bundled adapter already checks
17
+ * it, because a `ByteSource` may be the caller's own and a silently short read is
18
+ * indistinguishable from a truncated file.
19
+ */
20
+ import { parseEdfInteger } from '../bytes/numbers.js';
21
+ import { readAsciiField } from '../bytes/view.js';
22
+ import { DEFAULT_MAX_MATERIALIZE_BYTES, EDF_HEADER_BLOCK_BYTES, EDF_MAX_SIGNAL_COUNT, HEADER_FIELDS, } from '../constants.js';
23
+ import { EdfBudgetError, EdfRangeError } from '../errors.js';
24
+ import { parseHeader } from '../header/parse.js';
25
+ import { assertExactRead } from './source.js';
26
+ /**
27
+ * The signal count, read leniently and reported to nobody.
28
+ *
29
+ * This is a PREFETCH HINT and nothing else: it decides how many bytes the second read asks for.
30
+ * Every judgement about the field — the 1..9999 range, the layout, which diagnostic a bad value
31
+ * deserves and in which order — belongs to `header/parse.ts`, which owns the pinned check order.
32
+ * Returning `undefined` here simply means the second read is skipped and `parseHeader` gets the
33
+ * 256 bytes we have, so it can produce the right error rather than this module guessing at one.
34
+ */
35
+ function signalCountHint(fixedHeader) {
36
+ if (fixedHeader.length < EDF_HEADER_BLOCK_BYTES)
37
+ return undefined;
38
+ const { offset, length } = HEADER_FIELDS.signalCount;
39
+ const parse = parseEdfInteger(readAsciiField(fixedHeader, offset, length));
40
+ if (!parse.ok || parse.value < 1 || parse.value > EDF_MAX_SIGNAL_COUNT)
41
+ return undefined;
42
+ return parse.value;
43
+ }
44
+ /**
45
+ * Parse the header of `source`, reading it in exactly two ranges.
46
+ *
47
+ * Both reads are clamped to the source length so that a file too short for the header it declares
48
+ * reaches `parseHeader` and is reported as `SOURCE_TOO_SMALL` — a file defect — instead of
49
+ * surfacing as an `EdfSourceError` about a range past the end, which would blame the source for
50
+ * the file's problem.
51
+ */
52
+ export async function readHeader(source, options) {
53
+ const sourceByteLength = source.byteLength;
54
+ const firstLength = Math.min(EDF_HEADER_BLOCK_BYTES, sourceByteLength);
55
+ const fixedHeader = assertExactRead(await source.read(0, firstLength, options), 0, firstLength);
56
+ const signalCount = signalCountHint(fixedHeader);
57
+ if (signalCount === undefined)
58
+ return parseHeader(fixedHeader, sourceByteLength, options);
59
+ const headerByteLength = EDF_HEADER_BLOCK_BYTES * (signalCount + 1);
60
+ const remaining = Math.min(headerByteLength, sourceByteLength) - firstLength;
61
+ if (remaining <= 0)
62
+ return parseHeader(fixedHeader, sourceByteLength, options);
63
+ const rest = assertExactRead(await source.read(firstLength, remaining, options), firstLength, remaining);
64
+ // One contiguous buffer, because `parseHeader` addresses per-signal fields by absolute offset.
65
+ // At most 256 * 10000 bytes, so this allocation needs no budget of its own.
66
+ const headerBytes = new Uint8Array(firstLength + remaining);
67
+ headerBytes.set(fixedHeader, 0);
68
+ headerBytes.set(rest, firstLength);
69
+ return parseHeader(headerBytes, sourceByteLength, options);
70
+ }
71
+ function describeRange(range) {
72
+ return `{ start: ${range.start}, count: ${range.count} }`;
73
+ }
74
+ function assertRecordRange(header, records) {
75
+ const available = { start: 0, count: header.recordCount };
76
+ const startValid = Number.isSafeInteger(records.start) && records.start >= 0;
77
+ const countValid = Number.isSafeInteger(records.count) && records.count >= 0;
78
+ if (startValid && countValid && records.start + records.count <= header.recordCount)
79
+ return;
80
+ throw new EdfRangeError(`readRecordBytes(): records ${describeRange(records)} is not inside the ` +
81
+ `${header.recordCount} data records this file contains. Next: clamp the range against ` +
82
+ 'header.recordCount, or call index.locate(seconds) to find a record index for a time.', { requested: records, available });
83
+ }
84
+ /**
85
+ * Refused BEFORE the allocation, never during it.
86
+ *
87
+ * A record range is the one read in edfcore whose size the caller controls directly, so it is the
88
+ * one that can take a browser tab down by honest arithmetic. A typed error naming both numbers
89
+ * beats an out-of-memory crash with no attribution.
90
+ */
91
+ function assertWithinBudget(requiredBytes, records, options) {
92
+ const budgetBytes = options?.maxMaterializeBytes ?? DEFAULT_MAX_MATERIALIZE_BYTES;
93
+ if (requiredBytes <= budgetBytes)
94
+ return;
95
+ throw new EdfBudgetError(`Reading records ${describeRange(records)} needs a ${requiredBytes}-byte buffer, above the ` +
96
+ `${budgetBytes}-byte maxMaterializeBytes budget, so the read was refused before anything ` +
97
+ 'was allocated. Next: read fewer records per call, or raise options.maxMaterializeBytes.', { requiredBytes, budgetBytes });
98
+ }
99
+ /**
100
+ * The bytes of a record range: ONE contiguous read covering every signal.
101
+ *
102
+ * The returned buffer is exactly `records.count * header.recordByteLength` bytes and begins at
103
+ * record `records.start`, which is precisely what `decodeDigital` and `decodeAnnotations` demand
104
+ * — pass it to them unsliced.
105
+ *
106
+ * A zero-record range issues no read at all. A zero-length HTTP range is not expressible (`bytes=
107
+ * n--1`), and there is nothing to fetch, so returning the empty buffer is both cheaper and more
108
+ * honest than asking for it.
109
+ */
110
+ export async function readRecordBytes(source, header, records, options) {
111
+ assertRecordRange(header, records);
112
+ const byteLength = records.count * header.recordByteLength;
113
+ if (byteLength === 0)
114
+ return new Uint8Array(0);
115
+ assertWithinBudget(byteLength, records, options);
116
+ const byteOffset = header.headerByteLength + records.start * header.recordByteLength;
117
+ return assertExactRead(await source.read(byteOffset, byteLength, options), byteOffset, byteLength);
118
+ }
119
+ //# sourceMappingURL=read.js.map