edfcore 0.3.21 → 0.3.23
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/decode/physical.d.ts.map +1 -1
- package/dist/decode/physical.js +24 -1
- package/dist/decode/physical.js.map +1 -1
- package/package.json +1 -1
- package/src/constants.ts +1 -1
- package/src/decode/physical.ts +32 -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.23
|
|
10
|
+
|
|
11
|
+
- **Repository only; the published package is byte-identical to 0.3.22.** `tests/scratch/` is
|
|
12
|
+
excluded from the vitest `include` and the tsconfig `include`.
|
|
13
|
+
- That directory holds throwaway reproductions written while chasing a defect, and `.gitignore`
|
|
14
|
+
already excluded it with the reason: they "assert whatever behaviour was current when they were
|
|
15
|
+
written, so committing them pins defects". Not committing them was not enough. An uncommitted
|
|
16
|
+
probe still sat inside the `tests/**` glob and the tsconfig `include`, so it joined the suite and
|
|
17
|
+
the typecheck — and `npm run check` is what `scripts/release.mjs` runs before it tags. A leftover
|
|
18
|
+
probe could fail a release, or pass one, on the strength of a file nobody meant to keep.
|
|
19
|
+
- Found while running two adversarial sweeps whose agents write probes there: a stale probe from
|
|
20
|
+
one broke the typecheck of an unrelated release. A probe is now only ever run by naming it.
|
|
21
|
+
|
|
22
|
+
## 0.3.22
|
|
23
|
+
|
|
24
|
+
- **Fixed** `toPhysical` inventing a header defect when called on an annotations channel.
|
|
25
|
+
`parseSignalHeaders` deliberately never runs `buildScale` over one — its physical and digital
|
|
26
|
+
fields describe nothing a caller may use, and checking them "would report a defect about a number
|
|
27
|
+
nobody may use" — so such a signal has no scale AND no diagnostic. `describeScalingFailure` was
|
|
28
|
+
applied to it anyway, re-running the four data-signal tests over those unused fields and
|
|
29
|
+
confidently naming a cause the header never evaluated.
|
|
30
|
+
- A channel declaring `0`/`0` was refused with **`DEGENERATE_PHYSICAL_RANGE`**, a message asserting
|
|
31
|
+
a header defect. The conventional `-1`/`1` one was refused with `SCALE_UNAVAILABLE` and the words
|
|
32
|
+
*"the header recorded the reason rather than the signal"*. Both sent the caller to a
|
|
33
|
+
`header.diagnostics` entry that does not exist, and neither ever said the actual reason.
|
|
34
|
+
- It now says what the channel is: its bytes are EDF+ TAL text rather than measurements, so no
|
|
35
|
+
scale was ever built for it. `describeScalingFailure` only ever names a cause `buildScale`
|
|
36
|
+
actually evaluated, which is what its own comment promises.
|
|
37
|
+
- The next step changed too. Every other scaling failure ends "decodeDigital() still works on this
|
|
38
|
+
signal", which is true for a data signal whose ranges are unusable and **false here** — decoding
|
|
39
|
+
TAL text as samples produces numbers that look exactly like a signal, the one failure this
|
|
40
|
+
package exists to prevent. For the annotations channel it points at `readAnnotations` instead.
|
|
41
|
+
- `physical-values.md` said "Each condition also appears in `header.diagnostics` at parse time".
|
|
42
|
+
That was false for every annotations channel; the exception is now written down beside the rule.
|
|
43
|
+
|
|
9
44
|
## 0.3.21
|
|
10
45
|
|
|
11
46
|
- **Fixed** a non-finite `maxMaterializeBytes` producing two different wrong diagnoses, neither
|
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.23";
|
|
113
113
|
//# sourceMappingURL=constants.d.ts.map
|
package/dist/constants.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"physical.d.ts","sourceRoot":"","sources":["../../src/decode/physical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAqB,SAAS,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"physical.d.ts","sourceRoot":"","sources":["../../src/decode/physical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAqB,SAAS,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEvD,YAAY,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAqLvD;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,SAAS,CAAC,MAAM,CAAC,EAC1B,GAAG,CAAC,EAAE,YAAY,EAClB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,YAAY,CAad;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG;IAClD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB,CAaA;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,OAAO,EAAE,UAAU,EACnB,GAAG,CAAC,EAAE,UAAU,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC3B,UAAU,CAsBZ"}
|
package/dist/decode/physical.js
CHANGED
|
@@ -36,6 +36,28 @@ function assertWithinBudget(requiredBytes, what, options) {
|
|
|
36
36
|
*/
|
|
37
37
|
function describeScalingFailure(signal) {
|
|
38
38
|
const digitalSpec = 'EDF+ additional specification 5: "Digital maximum must be larger than Digital minimum"';
|
|
39
|
+
// FIRST, and ahead of the four re-derived tests. `parseSignalHeaders` deliberately does not run
|
|
40
|
+
// `buildScale` over an annotations channel — its physical and digital fields describe nothing a
|
|
41
|
+
// caller may use, and checking them "would report a defect about a number nobody may use". So
|
|
42
|
+
// such a signal has no scale AND no diagnostic, and re-running the four tests over those unused
|
|
43
|
+
// fields named a cause the header never evaluated: an annotations channel declaring 0/0 was
|
|
44
|
+
// refused with DEGENERATE_PHYSICAL_RANGE asserting a header defect, and the conventional -1/1
|
|
45
|
+
// one with SCALE_UNAVAILABLE saying "the header recorded the reason" — sending the reader to a
|
|
46
|
+
// `header.diagnostics` entry that does not exist, in both cases. Neither ever said the real
|
|
47
|
+
// reason (fixed in 0.3.22).
|
|
48
|
+
if (signal.kind === 'annotations') {
|
|
49
|
+
return {
|
|
50
|
+
code: 'SCALE_UNAVAILABLE',
|
|
51
|
+
reason: "is this file's annotations channel: its bytes are EDF+ TAL text rather than " +
|
|
52
|
+
'measurements, so no scale was ever built for it and its physical fields describe ' +
|
|
53
|
+
'nothing',
|
|
54
|
+
specReference: 'EDF+ specification 2.2.4 (the EDF Annotations signal)',
|
|
55
|
+
// NOT "decodeDigital() still works on this signal". It does, and it produces numbers that
|
|
56
|
+
// look exactly like a signal — the one failure this package exists to prevent.
|
|
57
|
+
nextStep: 'Next: read this channel with readAnnotations(recording, records), and pass only ' +
|
|
58
|
+
'header.dataSignalIndices to a sample read.',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
39
61
|
if (signal.digitalMinimum === signal.digitalMaximum) {
|
|
40
62
|
return {
|
|
41
63
|
code: 'DEGENERATE_DIGITAL_RANGE',
|
|
@@ -82,7 +104,8 @@ function scalingError(signal) {
|
|
|
82
104
|
`physical minimum "${signal.raw.physicalMinimum}", physical maximum ` +
|
|
83
105
|
`"${signal.raw.physicalMaximum}", physical dimension "${signal.raw.physicalDimension}". ` +
|
|
84
106
|
(failure.specReference === undefined ? '' : `${failure.specReference}. `) +
|
|
85
|
-
|
|
107
|
+
(failure.nextStep ??
|
|
108
|
+
'Next: decodeDigital() still works on this signal; edfcore will not invent a gain.');
|
|
86
109
|
return new EdfScalingError(message, {
|
|
87
110
|
code: failure.code,
|
|
88
111
|
signalIndex: signal.index,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"physical.js","sourceRoot":"","sources":["../../src/decode/physical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,6BAA6B,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAM/D,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAC5B,MAAM,eAAe,GAAG,CAAC,CAAC;AAE1B,2FAA2F;AAC3F,MAAM,yBAAyB,GAAG,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"physical.js","sourceRoot":"","sources":["../../src/decode/physical.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
package/src/decode/physical.ts
CHANGED
|
@@ -27,6 +27,12 @@ interface ScalingFailure {
|
|
|
27
27
|
/** Completes the sentence `signal 7 "EMG Chin" ...`. */
|
|
28
28
|
readonly reason: string;
|
|
29
29
|
readonly specReference: string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* What to do instead. Defaults to "decodeDigital still works on this signal", which is true for
|
|
32
|
+
* every DATA signal whose header ranges are unusable — and false for the annotations channel,
|
|
33
|
+
* where decoding TAL text as samples is the exact mistake this package exists to prevent.
|
|
34
|
+
*/
|
|
35
|
+
readonly nextStep?: string;
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
function assertWithinBudget(
|
|
@@ -59,6 +65,30 @@ function assertWithinBudget(
|
|
|
59
65
|
function describeScalingFailure(signal: EdfSignal): ScalingFailure {
|
|
60
66
|
const digitalSpec =
|
|
61
67
|
'EDF+ additional specification 5: "Digital maximum must be larger than Digital minimum"';
|
|
68
|
+
// FIRST, and ahead of the four re-derived tests. `parseSignalHeaders` deliberately does not run
|
|
69
|
+
// `buildScale` over an annotations channel — its physical and digital fields describe nothing a
|
|
70
|
+
// caller may use, and checking them "would report a defect about a number nobody may use". So
|
|
71
|
+
// such a signal has no scale AND no diagnostic, and re-running the four tests over those unused
|
|
72
|
+
// fields named a cause the header never evaluated: an annotations channel declaring 0/0 was
|
|
73
|
+
// refused with DEGENERATE_PHYSICAL_RANGE asserting a header defect, and the conventional -1/1
|
|
74
|
+
// one with SCALE_UNAVAILABLE saying "the header recorded the reason" — sending the reader to a
|
|
75
|
+
// `header.diagnostics` entry that does not exist, in both cases. Neither ever said the real
|
|
76
|
+
// reason (fixed in 0.3.22).
|
|
77
|
+
if (signal.kind === 'annotations') {
|
|
78
|
+
return {
|
|
79
|
+
code: 'SCALE_UNAVAILABLE',
|
|
80
|
+
reason:
|
|
81
|
+
"is this file's annotations channel: its bytes are EDF+ TAL text rather than " +
|
|
82
|
+
'measurements, so no scale was ever built for it and its physical fields describe ' +
|
|
83
|
+
'nothing',
|
|
84
|
+
specReference: 'EDF+ specification 2.2.4 (the EDF Annotations signal)',
|
|
85
|
+
// NOT "decodeDigital() still works on this signal". It does, and it produces numbers that
|
|
86
|
+
// look exactly like a signal — the one failure this package exists to prevent.
|
|
87
|
+
nextStep:
|
|
88
|
+
'Next: read this channel with readAnnotations(recording, records), and pass only ' +
|
|
89
|
+
'header.dataSignalIndices to a sample read.',
|
|
90
|
+
};
|
|
91
|
+
}
|
|
62
92
|
if (signal.digitalMinimum === signal.digitalMaximum) {
|
|
63
93
|
return {
|
|
64
94
|
code: 'DEGENERATE_DIGITAL_RANGE',
|
|
@@ -111,7 +141,8 @@ function scalingError(signal: EdfSignal): EdfScalingError {
|
|
|
111
141
|
`physical minimum "${signal.raw.physicalMinimum}", physical maximum ` +
|
|
112
142
|
`"${signal.raw.physicalMaximum}", physical dimension "${signal.raw.physicalDimension}". ` +
|
|
113
143
|
(failure.specReference === undefined ? '' : `${failure.specReference}. `) +
|
|
114
|
-
|
|
144
|
+
(failure.nextStep ??
|
|
145
|
+
'Next: decodeDigital() still works on this signal; edfcore will not invent a gain.');
|
|
115
146
|
return new EdfScalingError(message, {
|
|
116
147
|
code: failure.code,
|
|
117
148
|
signalIndex: signal.index,
|