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,125 @@
1
+ /**
2
+ * Rendering diagnostics for humans.
3
+ *
4
+ * Layer 1. Layout only: by the message contract a diagnostic's own message already names the
5
+ * field, the raw bytes as written, the rule and the next step, so this module adds structure —
6
+ * severity marker, code, location, the bytes as hex — and invents no wording.
7
+ *
8
+ * Output is deterministic and asserted as such: no locale-sensitive number or date formatting,
9
+ * no iteration over an unordered collection, and no ANSI escapes unless `color` is requested.
10
+ */
11
+ const INDENT = ' ';
12
+ /** A report is a summary, not a hex dump; longer runs are elided with a count. */
13
+ const MAX_RAW_BYTES_SHOWN = 24;
14
+ const ANSI_RESET = '\u001b[0m';
15
+ const ANSI_DIM = '\u001b[2m';
16
+ const SEVERITY_COLORS = {
17
+ error: '\u001b[31m',
18
+ warning: '\u001b[33m',
19
+ info: '\u001b[36m',
20
+ };
21
+ /**
22
+ * A multi-line report, one block per diagnostic. Returns `''` for an empty list so the result
23
+ * can be concatenated into a larger report without a stray blank line.
24
+ */
25
+ export function formatDiagnostics(diagnostics, options) {
26
+ const color = options?.color === true;
27
+ const shown = resolveLimit(options?.maxItems, diagnostics.length);
28
+ const lines = [];
29
+ for (let i = 0; i < shown; i++) {
30
+ const diagnostic = diagnostics[i];
31
+ // i < shown <= diagnostics.length, so this only satisfies noUncheckedIndexedAccess.
32
+ if (diagnostic === undefined)
33
+ continue;
34
+ appendDiagnostic(lines, diagnostic, color);
35
+ }
36
+ const hidden = diagnostics.length - shown;
37
+ if (hidden > 0)
38
+ lines.push(paint(`... and ${hidden} more`, ANSI_DIM, color));
39
+ return lines.join('\n');
40
+ }
41
+ function resolveLimit(maxItems, total) {
42
+ if (maxItems === undefined || !Number.isFinite(maxItems))
43
+ return total;
44
+ return Math.max(0, Math.min(total, Math.floor(maxItems)));
45
+ }
46
+ function appendDiagnostic(lines, diagnostic, color) {
47
+ const marker = paint(`${diagnostic.severity} [${diagnostic.code}]`, SEVERITY_COLORS[diagnostic.severity], color);
48
+ const messageLines = diagnostic.message.split('\n');
49
+ const first = messageLines[0] ?? '';
50
+ lines.push(`${marker} ${first}`.trimEnd());
51
+ for (let i = 1; i < messageLines.length; i++) {
52
+ const line = messageLines[i];
53
+ if (line === undefined)
54
+ continue;
55
+ lines.push(`${INDENT}${line.trim()}`);
56
+ }
57
+ const location = locationOf(diagnostic);
58
+ if (location !== undefined)
59
+ detail(lines, location, color);
60
+ if (diagnostic.raw !== undefined)
61
+ detail(lines, `raw: ${quote(diagnostic.raw)}`, color);
62
+ if (diagnostic.rawBytes !== undefined && diagnostic.rawBytes.length > 0) {
63
+ detail(lines, `bytes: ${hexDump(diagnostic.rawBytes)}`, color);
64
+ }
65
+ if (diagnostic.expected !== undefined)
66
+ detail(lines, `expected: ${diagnostic.expected}`, color);
67
+ if (diagnostic.actual !== undefined)
68
+ detail(lines, `actual: ${diagnostic.actual}`, color);
69
+ if (diagnostic.specReference !== undefined) {
70
+ detail(lines, `spec: ${diagnostic.specReference}`, color);
71
+ }
72
+ }
73
+ function detail(lines, text, color) {
74
+ lines.push(`${INDENT}${paint(text, ANSI_DIM, color)}`);
75
+ }
76
+ function locationOf(diagnostic) {
77
+ const parts = [];
78
+ if (diagnostic.byteOffset !== undefined) {
79
+ parts.push(diagnostic.byteLength === undefined
80
+ ? `byte offset ${diagnostic.byteOffset}`
81
+ : `byte offset ${diagnostic.byteOffset} (${diagnostic.byteLength} bytes)`);
82
+ }
83
+ if (diagnostic.field !== undefined)
84
+ parts.push(diagnostic.field);
85
+ if (diagnostic.signalIndex !== undefined)
86
+ parts.push(`signal ${diagnostic.signalIndex}`);
87
+ if (diagnostic.recordIndex !== undefined)
88
+ parts.push(`record ${diagnostic.recordIndex}`);
89
+ return parts.length === 0 ? undefined : `at ${parts.join(', ')}`;
90
+ }
91
+ /** `30 20 20 20 |0 |`. Built byte by byte — TextDecoder is banned outside `src/tal/`. */
92
+ function hexDump(bytes) {
93
+ const shown = bytes.subarray(0, MAX_RAW_BYTES_SHOWN);
94
+ const hex = [];
95
+ let ascii = '';
96
+ for (const byte of shown) {
97
+ hex.push(byte.toString(16).padStart(2, '0'));
98
+ ascii += isPrintableAscii(byte) ? String.fromCharCode(byte) : '.';
99
+ }
100
+ const elided = bytes.length - shown.length;
101
+ return `${hex.join(' ')} |${ascii}|${elided > 0 ? ` +${elided} more` : ''}`;
102
+ }
103
+ function isPrintableAscii(byte) {
104
+ return byte >= 0x20 && byte <= 0x7e;
105
+ }
106
+ /** Keeps every entry on one line: control and non-ASCII characters become escapes, not bytes. */
107
+ function quote(value) {
108
+ let out = '"';
109
+ for (const char of value) {
110
+ const code = char.codePointAt(0) ?? 0;
111
+ if (char === '"' || char === '\\')
112
+ out += `\\${char}`;
113
+ else if (isPrintableAscii(code))
114
+ out += char;
115
+ else if (code <= 0xff)
116
+ out += `\\x${code.toString(16).padStart(2, '0')}`;
117
+ else
118
+ out += `\\u{${code.toString(16)}}`;
119
+ }
120
+ return `${out}"`;
121
+ }
122
+ function paint(text, color, enabled) {
123
+ return enabled ? `${color}${text}${ANSI_RESET}` : text;
124
+ }
125
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/diagnostics/format.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,143 @@
1
+ /**
2
+ * The error hierarchy.
3
+ *
4
+ * Layer 1. One rule decides which of these you get: if edfcore cannot proceed without
5
+ * inventing something, it throws; if it can proceed truthfully, it records an `EdfDiagnostic`.
6
+ * There is no third category, and there is no `console` call anywhere in this package.
7
+ *
8
+ * `edfErrorKind` exists so consumers can discriminate without `instanceof`, which fails across
9
+ * realms — an iframe, a worker, or two copies of the package in one dependency tree.
10
+ */
11
+ import type { EdfDiagnostic, EdfDiagnosticCode, RecordRange } from './types.js';
12
+ export type EdfErrorKind = 'format' | 'scaling' | 'range' | 'source' | 'budget' | 'channel';
13
+ export declare abstract class EdfError extends Error {
14
+ abstract readonly edfErrorKind: EdfErrorKind;
15
+ constructor(message: string, options?: {
16
+ cause?: unknown;
17
+ });
18
+ }
19
+ /**
20
+ * Every concrete error edfcore throws.
21
+ *
22
+ * This exists so `edfErrorKind` actually discriminates in TypeScript. `EdfError` alone
23
+ * declares only the kind, so narrowing against the abstract class leaves the extra fields
24
+ * (`code`, `budgetBytes`, `matchingIndices`, …) unreachable and forces a cast at every call
25
+ * site. Switching on `edfErrorKind` over this union reaches them without one.
26
+ */
27
+ export type AnyEdfError = EdfFormatError | EdfScalingError | EdfRangeError | EdfSourceError | EdfBudgetError | EdfAmbiguousChannelError | EdfChannelNotFoundError;
28
+ /**
29
+ * Returns true for any error thrown by edfcore, across realms.
30
+ *
31
+ * Checks the `edfErrorKind` brand rather than `instanceof`, which fails whenever the error
32
+ * crossed a realm boundary — an iframe, a worker, or two copies of the package in one
33
+ * dependency tree.
34
+ *
35
+ * Note that a handful of caller-error paths deliberately throw a plain `RangeError` instead
36
+ * (asking for records that cannot exist, or handing an annotation channel to a sample read).
37
+ * Those are bugs in the calling code rather than problems with the file, and this returns
38
+ * false for them.
39
+ */
40
+ export declare function isEdfError(value: unknown): value is AnyEdfError;
41
+ export interface EdfFormatErrorInit {
42
+ readonly code: EdfDiagnosticCode;
43
+ readonly diagnostic?: EdfDiagnostic;
44
+ readonly field?: string;
45
+ readonly byteOffset?: number;
46
+ readonly signalIndex?: number;
47
+ readonly recordIndex?: number;
48
+ readonly cause?: unknown;
49
+ }
50
+ /**
51
+ * The file is wrong. Also what `strict: true` throws, carrying the diagnostic that would
52
+ * otherwise have been collected.
53
+ */
54
+ export declare class EdfFormatError extends EdfError {
55
+ readonly edfErrorKind: "format";
56
+ readonly code: EdfDiagnosticCode;
57
+ readonly diagnostic: EdfDiagnostic | undefined;
58
+ readonly field: string | undefined;
59
+ readonly byteOffset: number | undefined;
60
+ readonly signalIndex: number | undefined;
61
+ readonly recordIndex: number | undefined;
62
+ constructor(message: string, init: EdfFormatErrorInit);
63
+ }
64
+ /**
65
+ * Physical units are unavailable for one signal, because the header's ranges do not define a
66
+ * scale. `decodeDigital` still works on that signal — edfcore will not invent a gain.
67
+ */
68
+ export declare class EdfScalingError extends EdfError {
69
+ readonly edfErrorKind: "scaling";
70
+ readonly code: EdfDiagnosticCode;
71
+ readonly signalIndex: number;
72
+ readonly label: string;
73
+ constructor(message: string, init: {
74
+ code: EdfDiagnosticCode;
75
+ signalIndex: number;
76
+ label: string;
77
+ cause?: unknown;
78
+ });
79
+ }
80
+ /** Your bug, not the file's: you asked for records that do not exist. */
81
+ export declare class EdfRangeError extends EdfError {
82
+ readonly edfErrorKind: "range";
83
+ readonly requested: RecordRange;
84
+ readonly available: RecordRange;
85
+ constructor(message: string, init: {
86
+ requested: RecordRange;
87
+ available: RecordRange;
88
+ cause?: unknown;
89
+ });
90
+ }
91
+ /** A `ByteSource` broke its contract: it returned a different number of bytes than asked. */
92
+ export declare class EdfSourceError extends EdfError {
93
+ readonly edfErrorKind: "source";
94
+ readonly offset: number;
95
+ readonly requestedLength: number;
96
+ readonly receivedLength: number | undefined;
97
+ constructor(message: string, init: {
98
+ offset: number;
99
+ requestedLength: number;
100
+ receivedLength?: number | undefined;
101
+ cause?: unknown;
102
+ });
103
+ }
104
+ /**
105
+ * An allocation was refused before it happened. Float64 physical output is four times the
106
+ * on-disk size for EDF, so without this one honest call can take down a browser tab.
107
+ */
108
+ export declare class EdfBudgetError extends EdfError {
109
+ readonly edfErrorKind: "budget";
110
+ readonly requiredBytes: number;
111
+ readonly budgetBytes: number;
112
+ readonly optionName: "maxMaterializeBytes";
113
+ constructor(message: string, init: {
114
+ requiredBytes: number;
115
+ budgetBytes: number;
116
+ cause?: unknown;
117
+ });
118
+ }
119
+ /**
120
+ * Two or more signals share the requested label. Real files do this: CHB-MIT ships `T8-P8`
121
+ * twice. Silently returning the first is how the wrong channel ends up in a paper.
122
+ */
123
+ export declare class EdfAmbiguousChannelError extends EdfError {
124
+ readonly edfErrorKind: "channel";
125
+ readonly label: string;
126
+ readonly matchingIndices: readonly number[];
127
+ constructor(message: string, init: {
128
+ label: string;
129
+ matchingIndices: readonly number[];
130
+ cause?: unknown;
131
+ });
132
+ }
133
+ export declare class EdfChannelNotFoundError extends EdfError {
134
+ readonly edfErrorKind: "channel";
135
+ readonly selector: string | number;
136
+ readonly availableLabels: readonly string[];
137
+ constructor(message: string, init: {
138
+ selector: string | number;
139
+ availableLabels: readonly string[];
140
+ cause?: unknown;
141
+ });
142
+ }
143
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/errors.js ADDED
@@ -0,0 +1,135 @@
1
+ /**
2
+ * The error hierarchy.
3
+ *
4
+ * Layer 1. One rule decides which of these you get: if edfcore cannot proceed without
5
+ * inventing something, it throws; if it can proceed truthfully, it records an `EdfDiagnostic`.
6
+ * There is no third category, and there is no `console` call anywhere in this package.
7
+ *
8
+ * `edfErrorKind` exists so consumers can discriminate without `instanceof`, which fails across
9
+ * realms — an iframe, a worker, or two copies of the package in one dependency tree.
10
+ */
11
+ export class EdfError extends Error {
12
+ constructor(message, options) {
13
+ super(message, options);
14
+ this.name = new.target.name;
15
+ }
16
+ }
17
+ /**
18
+ * Returns true for any error thrown by edfcore, across realms.
19
+ *
20
+ * Checks the `edfErrorKind` brand rather than `instanceof`, which fails whenever the error
21
+ * crossed a realm boundary — an iframe, a worker, or two copies of the package in one
22
+ * dependency tree.
23
+ *
24
+ * Note that a handful of caller-error paths deliberately throw a plain `RangeError` instead
25
+ * (asking for records that cannot exist, or handing an annotation channel to a sample read).
26
+ * Those are bugs in the calling code rather than problems with the file, and this returns
27
+ * false for them.
28
+ */
29
+ export function isEdfError(value) {
30
+ return (typeof value === 'object' &&
31
+ value !== null &&
32
+ typeof value.edfErrorKind === 'string');
33
+ }
34
+ /**
35
+ * The file is wrong. Also what `strict: true` throws, carrying the diagnostic that would
36
+ * otherwise have been collected.
37
+ */
38
+ export class EdfFormatError extends EdfError {
39
+ edfErrorKind = 'format';
40
+ code;
41
+ diagnostic;
42
+ field;
43
+ byteOffset;
44
+ signalIndex;
45
+ recordIndex;
46
+ constructor(message, init) {
47
+ super(message, { cause: init.cause });
48
+ this.code = init.code;
49
+ this.diagnostic = init.diagnostic;
50
+ this.field = init.field ?? init.diagnostic?.field;
51
+ this.byteOffset = init.byteOffset ?? init.diagnostic?.byteOffset;
52
+ this.signalIndex = init.signalIndex ?? init.diagnostic?.signalIndex;
53
+ this.recordIndex = init.recordIndex ?? init.diagnostic?.recordIndex;
54
+ }
55
+ }
56
+ /**
57
+ * Physical units are unavailable for one signal, because the header's ranges do not define a
58
+ * scale. `decodeDigital` still works on that signal — edfcore will not invent a gain.
59
+ */
60
+ export class EdfScalingError extends EdfError {
61
+ edfErrorKind = 'scaling';
62
+ code;
63
+ signalIndex;
64
+ label;
65
+ constructor(message, init) {
66
+ super(message, { cause: init.cause });
67
+ this.code = init.code;
68
+ this.signalIndex = init.signalIndex;
69
+ this.label = init.label;
70
+ }
71
+ }
72
+ /** Your bug, not the file's: you asked for records that do not exist. */
73
+ export class EdfRangeError extends EdfError {
74
+ edfErrorKind = 'range';
75
+ requested;
76
+ available;
77
+ constructor(message, init) {
78
+ super(message, { cause: init.cause });
79
+ this.requested = init.requested;
80
+ this.available = init.available;
81
+ }
82
+ }
83
+ /** A `ByteSource` broke its contract: it returned a different number of bytes than asked. */
84
+ export class EdfSourceError extends EdfError {
85
+ edfErrorKind = 'source';
86
+ offset;
87
+ requestedLength;
88
+ receivedLength;
89
+ constructor(message, init) {
90
+ super(message, { cause: init.cause });
91
+ this.offset = init.offset;
92
+ this.requestedLength = init.requestedLength;
93
+ this.receivedLength = init.receivedLength;
94
+ }
95
+ }
96
+ /**
97
+ * An allocation was refused before it happened. Float64 physical output is four times the
98
+ * on-disk size for EDF, so without this one honest call can take down a browser tab.
99
+ */
100
+ export class EdfBudgetError extends EdfError {
101
+ edfErrorKind = 'budget';
102
+ requiredBytes;
103
+ budgetBytes;
104
+ optionName = 'maxMaterializeBytes';
105
+ constructor(message, init) {
106
+ super(message, { cause: init.cause });
107
+ this.requiredBytes = init.requiredBytes;
108
+ this.budgetBytes = init.budgetBytes;
109
+ }
110
+ }
111
+ /**
112
+ * Two or more signals share the requested label. Real files do this: CHB-MIT ships `T8-P8`
113
+ * twice. Silently returning the first is how the wrong channel ends up in a paper.
114
+ */
115
+ export class EdfAmbiguousChannelError extends EdfError {
116
+ edfErrorKind = 'channel';
117
+ label;
118
+ matchingIndices;
119
+ constructor(message, init) {
120
+ super(message, { cause: init.cause });
121
+ this.label = init.label;
122
+ this.matchingIndices = init.matchingIndices;
123
+ }
124
+ }
125
+ export class EdfChannelNotFoundError extends EdfError {
126
+ edfErrorKind = 'channel';
127
+ selector;
128
+ availableLabels;
129
+ constructor(message, init) {
130
+ super(message, { cause: init.cause });
131
+ this.selector = init.selector;
132
+ this.availableLabels = init.availableLabels;
133
+ }
134
+ }
135
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Every date rule in EDF, in one place.
3
+ *
4
+ * Layer 2. Sole owner of: the `dd.mm.yy` header startdate, the `hh.mm.ss` clock, the
5
+ * `dd-MMM-yyyy` subfield date used by the patient birthdate and the recording-identification
6
+ * `Startdate`, the 1985-2084 two-digit-year rule, the literal `'yy'` post-2084 escape, and the
7
+ * resolution between the two dates a file can carry.
8
+ *
9
+ * A JavaScript `Date` is never constructed. EDF stores local time at the patient with no
10
+ * timezone, so a `Date` would silently apply the reader's zone — worst exactly at DST
11
+ * boundaries — and `lib: ["ES2022"]` gives us no reason to reach for one anyway. Month lengths
12
+ * and leap years are arithmetic, and `'31.02.99'` must never quietly become 3 March.
13
+ *
14
+ * Separators and stray spaces are tolerated (`' 2. 8.51'` is in the EDF FAQ). A tolerated
15
+ * deviation is reported through `conformant` on the parse result rather than as a diagnostic:
16
+ * the vocabulary in `diagnostics/codes.ts` has no code for a date that parsed but was written
17
+ * oddly, and inventing one is not this module's call.
18
+ */
19
+ import type { DiagnosticSink } from '../diagnostics/collector.js';
20
+ import type { EdfCalendarDate, EdfClockTime, EdfStartTime } from '../types.js';
21
+ export declare function isLeapYear(year: number): boolean;
22
+ export declare function daysInMonth(year: number, month: number): number;
23
+ export declare function isValidCalendarDate(date: EdfCalendarDate): boolean;
24
+ export declare function calendarDatesEqual(a: EdfCalendarDate, b: EdfCalendarDate): boolean;
25
+ /** `'1951-08-02'`. Used in diagnostic messages and by `formatStartTimeNaive`. */
26
+ export declare function formatCalendarDate(date: EdfCalendarDate): string;
27
+ /** `'09:00:00'`. */
28
+ export declare function formatClockTime(clock: EdfClockTime): string;
29
+ export declare function resolveTwoDigitYear(twoDigitYear: number): number;
30
+ /**
31
+ * `parsed` — a full date. `yearEscape` — the year position held the literal `'yy'`, so only the
32
+ * recording-identification `Startdate` knows the year. `unparseable` — nothing usable.
33
+ */
34
+ export type HeaderStartDateStatus = 'parsed' | 'yearEscape' | 'unparseable';
35
+ export interface HeaderStartDateParse {
36
+ readonly status: HeaderStartDateStatus;
37
+ /** Present only when `status === 'parsed'`. */
38
+ readonly date: EdfCalendarDate | undefined;
39
+ /** Present when the day and month parsed, including under the year escape. */
40
+ readonly day: number | undefined;
41
+ readonly month: number | undefined;
42
+ /** The 1985-2084 rule was applied to a two-digit year. */
43
+ readonly clippedYear: boolean;
44
+ /** The field is exactly `dd.mm.yy`. False means it parsed only because we tolerate. */
45
+ readonly conformant: boolean;
46
+ readonly raw: string;
47
+ }
48
+ /**
49
+ * The `dd.mm.yy` startdate at offset 168.
50
+ *
51
+ * A four-digit year is accepted where it fits (`'2.8.1951'`), because a writer that spells the
52
+ * year out is unambiguous and the two-digit rule would be a downgrade. Three digits are
53
+ * corruption, not a convention, and are refused.
54
+ */
55
+ export declare function parseHeaderStartDate(raw: string): HeaderStartDateParse;
56
+ export interface HeaderStartTimeParse {
57
+ readonly clock: EdfClockTime | undefined;
58
+ /** The field is exactly `hh.mm.ss`. */
59
+ readonly conformant: boolean;
60
+ readonly raw: string;
61
+ }
62
+ /** The `hh.mm.ss` starttime at offset 176. Whole seconds only — EDF has no finer field. */
63
+ export declare function parseHeaderStartTime(raw: string): HeaderStartTimeParse;
64
+ export interface SubfieldDateParse {
65
+ readonly date: EdfCalendarDate | undefined;
66
+ /** The text is exactly `dd-MMM-yyyy` with an uppercase English month, and names a real day. */
67
+ readonly conformant: boolean;
68
+ }
69
+ /**
70
+ * The `dd-MMM-yyyy` subfield date — `'02-AUG-1951'` — used by the patient birthdate and the
71
+ * recording-identification `Startdate`.
72
+ *
73
+ * The month is accepted case-insensitively and a two-digit numeric month is accepted too, both
74
+ * marked non-conformant. The year must be four digits: this field exists precisely to be
75
+ * unambiguous, so applying the two-digit rule to it would throw away the one thing it is for.
76
+ */
77
+ export declare function parseSubfieldDate(text: string): SubfieldDateParse;
78
+ export interface StartTimeInput {
79
+ /** The raw eight bytes of the startdate field, as text, padding included. */
80
+ readonly rawStartDate: string;
81
+ /** The raw eight bytes of the starttime field, as text. */
82
+ readonly rawStartTime: string;
83
+ /** From `parseRecordingId`. The only unambiguous four-digit year a file can carry. */
84
+ readonly recordingIdDate: EdfCalendarDate | undefined;
85
+ }
86
+ /**
87
+ * Build the whole `EdfStartTime`, including the cross-field resolution.
88
+ *
89
+ * The recording-identification date wins when both exist, because its year is unambiguous —
90
+ * but a disagreement is always reported and both dates stay on the result, so no winner is
91
+ * picked silently. `dateSource` says which one `resolvedDate` came from.
92
+ */
93
+ export declare function resolveStartTime(input: StartTimeInput, sink: DiagnosticSink): EdfStartTime;
94
+ /**
95
+ * `'1951-08-02T09:00:00.000'` — no zone designator, because EDF has no zone.
96
+ *
97
+ * The milliseconds are always `.000`: the header stores whole seconds, and the sub-second start
98
+ * of an EDF+ recording lives in record 0's timekeeping TAL, not here.
99
+ */
100
+ export declare function formatStartTimeNaive(startTime: EdfStartTime): string | undefined;
101
+ //# sourceMappingURL=dates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dates.d.ts","sourceRoot":"","sources":["../../src/header/dates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAmD/E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG/D;AAQD,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAKlE;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,eAAe,GAAG,OAAO,CAElF;AAED,iFAAiF;AACjF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAEhE;AAED,oBAAoB;AACpB,wBAAgB,eAAe,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAE3D;AAgCD,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAIhE;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,YAAY,GAAG,aAAa,CAAC;AAE5E,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IACvC,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,CAAC;IAC3C,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,0DAA0D;IAC1D,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B,uFAAuF;IACvF,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAcD;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAmCtE;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,YAAY,GAAG,SAAS,CAAC;IACzC,uCAAuC;IACvC,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,2FAA2F;AAC3F,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,oBAAoB,CAgBtE;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,SAAS,CAAC;IAC3C,+FAA+F;IAC/F,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,iBAAiB,CAoBjE;AAED,MAAM,WAAW,cAAc;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,2DAA2D;IAC3D,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,eAAe,EAAE,eAAe,GAAG,SAAS,CAAC;CACvD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,cAAc,GAAG,YAAY,CA6I1F;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAIhF"}