edfcore 0.3.77 → 0.3.79
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/CHANGELOG.md +35 -0
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/io/cached.d.ts.map +1 -1
- package/dist/io/cached.js +67 -6
- package/dist/io/cached.js.map +1 -1
- package/dist/sample-grid.d.ts.map +1 -1
- package/dist/sample-grid.js +7 -1
- package/dist/sample-grid.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +1 -1
- package/src/io/cached.ts +78 -7
- package/src/sample-grid.ts +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,41 @@ alone does not tell you whether you were affected.
|
|
|
6
6
|
edfcore is pre-1.0. Patch releases have carried behaviour changes where the old behaviour was a
|
|
7
7
|
defect; those are called out below.
|
|
8
8
|
|
|
9
|
+
## 0.3.79
|
|
10
|
+
|
|
11
|
+
- **Fixed** `cachedSource` leaving an aborting caller pending for the whole underlying block read.
|
|
12
|
+
- 0.3.43 stopped one reader's signal cancelling a block other readers were waiting on — right, and
|
|
13
|
+
unchanged — and justified it by saying `read` "already polls each caller's own signal before and
|
|
14
|
+
after `Promise.all`, so an aborting caller still rejects promptly". The only poll that can fire
|
|
15
|
+
is the one **after** the gather. So the caller's promise settled when the bytes it no longer
|
|
16
|
+
wanted arrived, and with the signal no longer reaching the source, nothing was watching it at
|
|
17
|
+
all. A viewer aborting a scrolled-past window held that promise for the full fetch.
|
|
18
|
+
- `read` now races the caller's own signal against the gather. The shared block read is untouched:
|
|
19
|
+
other readers still get it, and losing the race leaves nothing dangling, because the block
|
|
20
|
+
promises are already attached inside `blockFor`.
|
|
21
|
+
- `AbortSignalLike` is `{ aborted: boolean }` and nothing more, so a signal that carries no
|
|
22
|
+
`addEventListener` cannot be watched and the post-gather poll remains the answer for it. A real
|
|
23
|
+
`AbortSignal` — what callers actually pass — is watched and rejects the moment it fires, with the
|
|
24
|
+
same `AbortError` `throwIfAborted` produces, so nothing branching on `error.name` can tell which
|
|
25
|
+
route rejected it.
|
|
26
|
+
|
|
27
|
+
## 0.3.78
|
|
28
|
+
|
|
29
|
+
- **Fixed** `sample-grid.ts` sending a reader to `onsetTicks` where `sample-locate.ts` sends them to
|
|
30
|
+
`onsetTicksFromFirstRecord`, for the identical refusal.
|
|
31
|
+
- Both modules refuse an annotations channel with the same sentence — it holds TAL text, so it has
|
|
32
|
+
no sample grid — and then name different fields to use instead. The grid puts sample 0 at
|
|
33
|
+
`t = 0`, which is the start of record 0: the **rebased** axis. `onsetTicks` is on the header's
|
|
34
|
+
timebase, and the two differ by the sub-second offset record 0's timekeeping TAL may declare.
|
|
35
|
+
- On a file with a 0.25 s offset, `gridSampleStartTicks(signal, 4, d)` is 10000000 and the event
|
|
36
|
+
written at that instant reports `onsetTicks` 12500000. The reader was sent to the field that
|
|
37
|
+
does not line up with the numbers the module they had just called returns. `sample-locate.ts`
|
|
38
|
+
had it right.
|
|
39
|
+
- The guard reads both refusals out of the source and requires them to name the same field. Its
|
|
40
|
+
first version passed with the bug reinstated, because the explanatory comment above the message
|
|
41
|
+
names both fields and the slice picked it up — it now strips comment lines before matching, and
|
|
42
|
+
says so, since that is the only reason it works.
|
|
43
|
+
|
|
9
44
|
## 0.3.77
|
|
10
45
|
|
|
11
46
|
- **Fixed** `validateRecording`'s scan-budget refusal offering advice that does not work on the
|
package/dist/constants.d.ts
CHANGED
|
@@ -109,5 +109,5 @@ export declare const SIGNAL_FIELD_BLOCK_OFFSETS: {
|
|
|
109
109
|
readonly reserved: 224;
|
|
110
110
|
};
|
|
111
111
|
/** Published package version. Kept in sync with package.json by a test. */
|
|
112
|
-
export declare const VERSION = "0.3.
|
|
112
|
+
export declare const VERSION = "0.3.79";
|
|
113
113
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
package/dist/io/cached.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cached.d.ts","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"cached.d.ts","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAmB,UAAU,EAAE,YAAY,EAAe,MAAM,aAAa,CAAC;AAiE1F,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,UAAU,CAqKnF"}
|
package/dist/io/cached.js
CHANGED
|
@@ -30,6 +30,41 @@ function passThrough(source) {
|
|
|
30
30
|
},
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
|
+
function isWatchable(signal) {
|
|
34
|
+
const candidate = signal;
|
|
35
|
+
return (typeof candidate?.addEventListener === 'function' &&
|
|
36
|
+
typeof candidate.removeEventListener === 'function');
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* A promise that rejects when `signal` aborts, plus the teardown for its listener.
|
|
40
|
+
*
|
|
41
|
+
* `undefined` when the signal cannot be watched, which is the caller's cue to fall back to polling.
|
|
42
|
+
* The rejection is the same `AbortError` `throwIfAborted` produces, so a caller branching on
|
|
43
|
+
* `error.name` cannot tell which route rejected it.
|
|
44
|
+
*/
|
|
45
|
+
function watchSignal(signal) {
|
|
46
|
+
if (signal === undefined || !isWatchable(signal))
|
|
47
|
+
return undefined;
|
|
48
|
+
let dispose = () => { };
|
|
49
|
+
const aborted = new Promise((_resolve, reject) => {
|
|
50
|
+
const onAbort = () => {
|
|
51
|
+
const error = new Error('The read was aborted through options.signal.');
|
|
52
|
+
error.name = 'AbortError';
|
|
53
|
+
reject(error);
|
|
54
|
+
};
|
|
55
|
+
if (signal.aborted) {
|
|
56
|
+
onAbort();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
signal.addEventListener('abort', onAbort);
|
|
60
|
+
dispose = () => {
|
|
61
|
+
signal.removeEventListener('abort', onAbort);
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
// Losing the race must not surface as an unhandled rejection.
|
|
65
|
+
aborted.catch(() => { });
|
|
66
|
+
return { aborted, dispose: () => dispose() };
|
|
67
|
+
}
|
|
33
68
|
export function cachedSource(source, options) {
|
|
34
69
|
const byteLength = source.byteLength;
|
|
35
70
|
const maxBytes = Math.max(0, Math.floor(requireFiniteOption(options?.maxBytes, 'maxBytes', DEFAULT_MAX_BYTES)));
|
|
@@ -77,11 +112,17 @@ export function cachedSource(source, options) {
|
|
|
77
112
|
* it: a blank panel and no error anywhere. Which reader died depended on which touched the
|
|
78
113
|
* block first (fixed in 0.3.43).
|
|
79
114
|
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
115
|
+
* The caller no longer decides for anyone else, and the cost is that an abort does not tear down
|
|
116
|
+
* the underlying request — the right trade for a read whose result other readers are waiting on:
|
|
117
|
+
* the bytes are valid and already paid for, so they are admitted to the cache.
|
|
118
|
+
*
|
|
119
|
+
* This used to claim that the polls around `Promise.all` left an aborting caller rejecting
|
|
120
|
+
* "promptly". They did not: the only poll that can fire is the one AFTER the gather, so the
|
|
121
|
+
* caller's promise stayed pending for the whole underlying block read and settled only when the
|
|
122
|
+
* bytes it no longer wanted arrived. With the signal no longer reaching the source, nothing else
|
|
123
|
+
* was watching it. `read` now RACES the caller's own signal against the gather when the signal
|
|
124
|
+
* can be watched, so it rejects at abort time while the shared read continues untouched — which
|
|
125
|
+
* is what 0.3.43 decided, rather than what it wrote down (fixed in 0.3.79).
|
|
85
126
|
*/
|
|
86
127
|
async function fetchBlock(index, options) {
|
|
87
128
|
const start = index * blockBytes;
|
|
@@ -136,7 +177,27 @@ export function cachedSource(source, options) {
|
|
|
136
177
|
for (let index = firstBlock; index <= lastBlock; index += 1) {
|
|
137
178
|
pending.push(blockFor(index, options));
|
|
138
179
|
}
|
|
139
|
-
|
|
180
|
+
/*
|
|
181
|
+
* Raced, not merely awaited. `AbortSignalLike` is `{ aborted: boolean }` and nothing more, so
|
|
182
|
+
* a signal that carries no `addEventListener` cannot be watched and the post-gather poll
|
|
183
|
+
* below is still the only answer for it. A real `AbortSignal` — what every caller in
|
|
184
|
+
* practice passes — is watched, and rejects the moment it fires.
|
|
185
|
+
*
|
|
186
|
+
* The gather is NOT cancelled either way: `pending` holds the shared block promises, other
|
|
187
|
+
* readers are waiting on them, and their rejections must not become unhandled. They are
|
|
188
|
+
* already attached inside `blockFor`, so losing the race leaves nothing dangling.
|
|
189
|
+
*/
|
|
190
|
+
const watch = watchSignal(options?.signal);
|
|
191
|
+
let resolved;
|
|
192
|
+
try {
|
|
193
|
+
resolved =
|
|
194
|
+
watch === undefined
|
|
195
|
+
? await Promise.all(pending)
|
|
196
|
+
: await Promise.race([Promise.all(pending), watch.aborted]);
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
watch?.dispose();
|
|
200
|
+
}
|
|
140
201
|
throwIfAborted(options);
|
|
141
202
|
const out = new Uint8Array(length);
|
|
142
203
|
for (const [position, block] of resolved.entries()) {
|
package/dist/io/cached.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cached.js","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE/E,MAAM,mBAAmB,GAAG,IAAI,GAAG,IAAI,CAAC;AACxC,MAAM,iBAAiB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAE3C,yFAAyF;AACzF,SAAS,WAAW,CAAC,MAAkB;IACrC,OAAO;QACL,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,KAAK,CAAC,IAAI,CAAC,MAAc,EAAE,MAAc,EAAE,OAAqB;YAC9D,OAAO,eAAe,CAAC,MAAM,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrF,CAAC;QACD,KAAK,CAAC,KAAK;YACT,MAAM,MAAM,CAAC,KAAK,EAAE,EAAE,CAAC;QACzB,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAkB,EAAE,OAAsB;IACrE,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;IACrC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,iBAAiB,CAAC,CAAC,CAClF,CAAC;IACF,MAAM,mBAAmB,GAAG,IAAI,CAAC,GAAG,CAClC,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,mBAAmB,CAAC,CAAC,CACxF,CAAC;IACF,0FAA0F;IAC1F,6FAA6F;IAC7F,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,mBAAmB,EAAE,QAAQ,CAAC,CAAC;IAC3D,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,WAAW,CAAC,MAAM,CAAC,CAAC;IAE/C,wFAAwF;IACxF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAA+B,CAAC;IACxD,IAAI,WAAW,GAAG,CAAC,CAAC;IAEpB,SAAS,KAAK;QACZ,2FAA2F;QAC3F,0CAA0C;QAC1C,KAAK,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,IAAI,MAAM,EAAE,CAAC;YACpC,IAAI,WAAW,IAAI,QAAQ;gBAAE,OAAO;YACpC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACrB,WAAW,IAAI,KAAK,CAAC,UAAU,CAAC;QAClC,CAAC;IACH,CAAC;IAED,gGAAgG;IAChG,iGAAiG;IACjG,+FAA+F;IAC/F,qFAAqF;IACrF,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,SAAS,KAAK,CAAC,KAAa,EAAE,KAAiB;QAC7C,IAAI,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC;YAAE,OAAO;QACxC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACzB,WAAW,IAAI,KAAK,CAAC,UAAU,CAAC;QAChC,KAAK,EAAE,CAAC;IACV,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"cached.js","sourceRoot":"","sources":["../../src/io/cached.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sample-grid.d.ts","sourceRoot":"","sources":["../src/sample-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"sample-grid.d.ts","sourceRoot":"","sources":["../src/sample-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAgC/D;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,MAAM,EACf,mBAAmB,EAAE,MAAM,GAC1B,iBAAiB,CAoBnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,MAAM,GAC1B,MAAM,CAcR;AAED,+EAA+E;AAC/E,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,MAAM,EACnB,mBAAmB,EAAE,MAAM,GAC1B,MAAM,CAKR"}
|
package/dist/sample-grid.js
CHANGED
|
@@ -46,7 +46,13 @@ function assertGrid(signal, recordDurationTicks) {
|
|
|
46
46
|
if (signal.kind === 'annotations') {
|
|
47
47
|
throw new RangeError(`signal ${signal.index} (${JSON.stringify(signal.label)}) is an annotations channel, ` +
|
|
48
48
|
'whose region holds TAL text rather than samples, so it has no sample grid. Next: use ' +
|
|
49
|
-
|
|
49
|
+
// `onsetTicksFromFirstRecord`, not `onsetTicks`. This grid puts sample 0 at t = 0, which
|
|
50
|
+
// is the start of record 0 — the rebased axis — and `onsetTicks` is on the HEADER's
|
|
51
|
+
// timebase. They differ by the sub-second offset record 0's timekeeping TAL may declare:
|
|
52
|
+
// on a file with a 0.25 s offset, `gridSampleStartTicks(signal, 4, d)` is 10000000 and the
|
|
53
|
+
// event at that instant reports `onsetTicks` 12500000. `sample-locate.ts` names the right
|
|
54
|
+
// one for the identical refusal (fixed in 0.3.78).
|
|
55
|
+
'onsetTicksFromFirstRecord on the annotations themselves.');
|
|
50
56
|
}
|
|
51
57
|
if (signal.samplesPerRecord <= 0) {
|
|
52
58
|
throw new RangeError(`signal ${signal.index} (${JSON.stringify(signal.label)}) declares ` +
|
package/dist/sample-grid.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sample-grid.js","sourceRoot":"","sources":["../src/sample-grid.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGhD,SAAS,UAAU,CAAC,MAAiB,EAAE,mBAA2B;IAChE,IAAI,MAAM,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;QAClC,MAAM,IAAI,UAAU,CAClB,UAAU,MAAM,CAAC,KAAK,KAAK,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B;YACpF,uFAAuF;YACvF,
|
|
1
|
+
{"version":3,"file":"sample-grid.js","sourceRoot":"","sources":["../src/sample-grid.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
package/src/io/cached.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { requireFiniteOption } from '../options.js';
|
|
19
|
-
import type { ByteSource, CacheOptions, ReadOptions } from '../types.js';
|
|
19
|
+
import type { AbortSignalLike, ByteSource, CacheOptions, ReadOptions } from '../types.js';
|
|
20
20
|
import { assertExactRead, assertReadRange, throwIfAborted } from './source.js';
|
|
21
21
|
|
|
22
22
|
const DEFAULT_BLOCK_BYTES = 1024 * 1024;
|
|
@@ -35,6 +35,52 @@ function passThrough(source: ByteSource): ByteSource {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/** The shape a real `AbortSignal` has and `AbortSignalLike` does not promise. */
|
|
39
|
+
interface WatchableSignal {
|
|
40
|
+
addEventListener(type: 'abort', listener: () => void): void;
|
|
41
|
+
removeEventListener(type: 'abort', listener: () => void): void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isWatchable(signal: unknown): signal is AbortSignalLike & WatchableSignal {
|
|
45
|
+
const candidate = signal as { addEventListener?: unknown; removeEventListener?: unknown } | null;
|
|
46
|
+
return (
|
|
47
|
+
typeof candidate?.addEventListener === 'function' &&
|
|
48
|
+
typeof candidate.removeEventListener === 'function'
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A promise that rejects when `signal` aborts, plus the teardown for its listener.
|
|
54
|
+
*
|
|
55
|
+
* `undefined` when the signal cannot be watched, which is the caller's cue to fall back to polling.
|
|
56
|
+
* The rejection is the same `AbortError` `throwIfAborted` produces, so a caller branching on
|
|
57
|
+
* `error.name` cannot tell which route rejected it.
|
|
58
|
+
*/
|
|
59
|
+
function watchSignal(
|
|
60
|
+
signal: AbortSignalLike | undefined,
|
|
61
|
+
): { aborted: Promise<never>; dispose: () => void } | undefined {
|
|
62
|
+
if (signal === undefined || !isWatchable(signal)) return undefined;
|
|
63
|
+
let dispose = (): void => {};
|
|
64
|
+
const aborted = new Promise<never>((_resolve, reject) => {
|
|
65
|
+
const onAbort = (): void => {
|
|
66
|
+
const error = new Error('The read was aborted through options.signal.');
|
|
67
|
+
error.name = 'AbortError';
|
|
68
|
+
reject(error);
|
|
69
|
+
};
|
|
70
|
+
if (signal.aborted) {
|
|
71
|
+
onAbort();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
signal.addEventListener('abort', onAbort);
|
|
75
|
+
dispose = () => {
|
|
76
|
+
signal.removeEventListener('abort', onAbort);
|
|
77
|
+
};
|
|
78
|
+
});
|
|
79
|
+
// Losing the race must not surface as an unhandled rejection.
|
|
80
|
+
aborted.catch(() => {});
|
|
81
|
+
return { aborted, dispose: () => dispose() };
|
|
82
|
+
}
|
|
83
|
+
|
|
38
84
|
export function cachedSource(source: ByteSource, options?: CacheOptions): ByteSource {
|
|
39
85
|
const byteLength = source.byteLength;
|
|
40
86
|
const maxBytes = Math.max(
|
|
@@ -90,11 +136,17 @@ export function cachedSource(source: ByteSource, options?: CacheOptions): ByteSo
|
|
|
90
136
|
* it: a blank panel and no error anywhere. Which reader died depended on which touched the
|
|
91
137
|
* block first (fixed in 0.3.43).
|
|
92
138
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
139
|
+
* The caller no longer decides for anyone else, and the cost is that an abort does not tear down
|
|
140
|
+
* the underlying request — the right trade for a read whose result other readers are waiting on:
|
|
141
|
+
* the bytes are valid and already paid for, so they are admitted to the cache.
|
|
142
|
+
*
|
|
143
|
+
* This used to claim that the polls around `Promise.all` left an aborting caller rejecting
|
|
144
|
+
* "promptly". They did not: the only poll that can fire is the one AFTER the gather, so the
|
|
145
|
+
* caller's promise stayed pending for the whole underlying block read and settled only when the
|
|
146
|
+
* bytes it no longer wanted arrived. With the signal no longer reaching the source, nothing else
|
|
147
|
+
* was watching it. `read` now RACES the caller's own signal against the gather when the signal
|
|
148
|
+
* can be watched, so it rejects at abort time while the shared read continues untouched — which
|
|
149
|
+
* is what 0.3.43 decided, rather than what it wrote down (fixed in 0.3.79).
|
|
98
150
|
*/
|
|
99
151
|
async function fetchBlock(index: number, options?: ReadOptions): Promise<Uint8Array> {
|
|
100
152
|
const start = index * blockBytes;
|
|
@@ -152,7 +204,26 @@ export function cachedSource(source: ByteSource, options?: CacheOptions): ByteSo
|
|
|
152
204
|
for (let index = firstBlock; index <= lastBlock; index += 1) {
|
|
153
205
|
pending.push(blockFor(index, options));
|
|
154
206
|
}
|
|
155
|
-
|
|
207
|
+
/*
|
|
208
|
+
* Raced, not merely awaited. `AbortSignalLike` is `{ aborted: boolean }` and nothing more, so
|
|
209
|
+
* a signal that carries no `addEventListener` cannot be watched and the post-gather poll
|
|
210
|
+
* below is still the only answer for it. A real `AbortSignal` — what every caller in
|
|
211
|
+
* practice passes — is watched, and rejects the moment it fires.
|
|
212
|
+
*
|
|
213
|
+
* The gather is NOT cancelled either way: `pending` holds the shared block promises, other
|
|
214
|
+
* readers are waiting on them, and their rejections must not become unhandled. They are
|
|
215
|
+
* already attached inside `blockFor`, so losing the race leaves nothing dangling.
|
|
216
|
+
*/
|
|
217
|
+
const watch = watchSignal(options?.signal);
|
|
218
|
+
let resolved: Uint8Array[];
|
|
219
|
+
try {
|
|
220
|
+
resolved =
|
|
221
|
+
watch === undefined
|
|
222
|
+
? await Promise.all(pending)
|
|
223
|
+
: await Promise.race([Promise.all(pending), watch.aborted]);
|
|
224
|
+
} finally {
|
|
225
|
+
watch?.dispose();
|
|
226
|
+
}
|
|
156
227
|
throwIfAborted(options);
|
|
157
228
|
|
|
158
229
|
const out = new Uint8Array(length);
|
package/src/sample-grid.ts
CHANGED
|
@@ -50,7 +50,13 @@ function assertGrid(signal: EdfSignal, recordDurationTicks: bigint): void {
|
|
|
50
50
|
throw new RangeError(
|
|
51
51
|
`signal ${signal.index} (${JSON.stringify(signal.label)}) is an annotations channel, ` +
|
|
52
52
|
'whose region holds TAL text rather than samples, so it has no sample grid. Next: use ' +
|
|
53
|
-
|
|
53
|
+
// `onsetTicksFromFirstRecord`, not `onsetTicks`. This grid puts sample 0 at t = 0, which
|
|
54
|
+
// is the start of record 0 — the rebased axis — and `onsetTicks` is on the HEADER's
|
|
55
|
+
// timebase. They differ by the sub-second offset record 0's timekeeping TAL may declare:
|
|
56
|
+
// on a file with a 0.25 s offset, `gridSampleStartTicks(signal, 4, d)` is 10000000 and the
|
|
57
|
+
// event at that instant reports `onsetTicks` 12500000. `sample-locate.ts` names the right
|
|
58
|
+
// one for the identical refusal (fixed in 0.3.78).
|
|
59
|
+
'onsetTicksFromFirstRecord on the annotations themselves.',
|
|
54
60
|
);
|
|
55
61
|
}
|
|
56
62
|
if (signal.samplesPerRecord <= 0) {
|