@ultimat3/core 5.0.1 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/type-pins.ts CHANGED
@@ -1,11 +1,11 @@
1
- // Compile-time pins for the actor-facts seam and the config surface. Source, not a `.test.ts`,
2
- // on purpose:
3
- // `tsconfig.json` excludes `src/**/*.test.ts`, so `tsc -b` never reads a test file and a
4
- // type-level assertion written there can never fail. This module emits nothing and exports
5
- // nothing anybody imports — a regression is a build error, the only enforcement that counts.
1
+ // Compile-time pins for the actor-facts seam, the config surface and the route vocabulary.
2
+ // Source, not a `.test.ts`, on purpose: `tsconfig.json` excludes `src/**/*.test.ts`, so `tsc -b`
3
+ // never reads a test file and a type-level assertion written there can never fail. This module
4
+ // emits nothing and exports nothing anybody imports a regression is a build error.
6
5
 
7
6
  import type { Actor, ActorFactMap, FactKeysOf, FactMapOf } from './actor';
8
7
  import type { AppConfigInput, DatabaseConfig } from './config';
8
+ import type { HydrateStrategy, OfflineStrategy, RenderMode } from './route-vocabulary';
9
9
 
10
10
  /** Fails to compile when `T` is anything but `true`. The whole mechanism. */
11
11
  type Assert<T extends true> = T;
@@ -98,3 +98,28 @@ type _DatabaseInputCarriesNoDeadField = Assert<
98
98
  ? true
99
99
  : false
100
100
  >;
101
+
102
+ /**
103
+ * Mutual assignability, not one-way. The tuples are load-bearing: a bare `A extends B` distributes
104
+ * over a union and answers `true` for every member separately, so it cannot see a widening — which
105
+ * is the only thing these three pins are looking for.
106
+ */
107
+ type Exact<A, B> = [A] extends [B] ? ([B] extends [A] ? true : false) : false;
108
+
109
+ /**
110
+ * Each route vocabulary's union must stay DERIVED from its array. `(typeof ARRAY)[number]` is what
111
+ * makes the pair unable to disagree, and it is one careless edit from being a hand-written union
112
+ * again — which is the shape six packages shipped until `route-vocabulary.ts` existed. Restating
113
+ * the members here is a pin, not a copy: nothing imports these, and a member added to the array
114
+ * without a word in the changelog is a build error rather than a silent widening five packages
115
+ * inherit through a re-export.
116
+ */
117
+ type _RenderModeIsItsArray = Assert<Exact<RenderMode, 'static' | 'isr' | 'ssr' | 'stream'>>;
118
+
119
+ type _OfflineStrategyIsItsArray = Assert<
120
+ Exact<OfflineStrategy, 'precache' | 'runtime' | 'network-only'>
121
+ >;
122
+
123
+ type _HydrateStrategyIsItsArray = Assert<
124
+ Exact<HydrateStrategy, 'idle' | 'visible' | 'interaction' | 'never'>
125
+ >;
@@ -1,283 +0,0 @@
1
- // Single responsibility: turning a baseline or extended sequential Huffman JPEG (SOF0/SOF1) into
2
- // RGBA — the marker walk, the entropy-coded scan, the inverse DCT and the sample planes. What each
3
- // segment DECLARES, and which codings are refused by name, is `jpeg-headers.ts`; this file is the
4
- // algorithm those declarations describe.
5
-
6
- import { imageDecodeFailed } from './errors';
7
- import {
8
- assertSupportedCoding,
9
- type Component,
10
- type Frame,
11
- hex,
12
- isAdobe,
13
- readFrame,
14
- readQuantTables,
15
- readScanHeader,
16
- readU16,
17
- type ScanComponent,
18
- } from './jpeg-headers';
19
- import { type HuffmanTable, JpegBitReader, readHuffmanTables } from './jpeg-huffman';
20
- import { ycbcrToRgb, ZIGZAG } from './jpeg-tables';
21
- import { type Raster, rasterFrom } from './raster';
22
-
23
- const SQRT2 = Math.SQRT2;
24
-
25
- /** Reused across every block of every image: the decoder is synchronous and single-threaded. */
26
- const COEF = new Int32Array(64);
27
- const WORK = new Float32Array(64);
28
-
29
- /** libjpeg's AAN float butterfly, in place over the 8 samples `step` apart from `base`. */
30
- function idct1d(v: Float32Array, base: number, step: number): void {
31
- const s0 = v[base] ?? 0;
32
- const s1 = v[base + step] ?? 0;
33
- const s2 = v[base + step * 2] ?? 0;
34
- const s3 = v[base + step * 3] ?? 0;
35
- const s4 = v[base + step * 4] ?? 0;
36
- const s5 = v[base + step * 5] ?? 0;
37
- const s6 = v[base + step * 6] ?? 0;
38
- const s7 = v[base + step * 7] ?? 0;
39
- const e10 = s0 + s4;
40
- const e11 = s0 - s4;
41
- const e13 = s2 + s6;
42
- const e12 = (s2 - s6) * SQRT2 - e13;
43
- const t0 = e10 + e13;
44
- const t3 = e10 - e13;
45
- const t1 = e11 + e12;
46
- const t2 = e11 - e12;
47
- const z13 = s5 + s3;
48
- const z10 = s5 - s3;
49
- const z11 = s1 + s7;
50
- const z12 = s1 - s7;
51
- const t7 = z11 + z13;
52
- const t11 = (z11 - z13) * SQRT2;
53
- const z5 = (z10 + z12) * 1.847759065;
54
- const t10 = 1.0823922 * z12 - z5;
55
- const t12 = -2.61312593 * z10 + z5;
56
- const t6 = t12 - t7;
57
- const t5 = t11 - t6;
58
- const t4 = t10 + t5;
59
- v[base] = t0 + t7;
60
- v[base + step * 7] = t0 - t7;
61
- v[base + step] = t1 + t6;
62
- v[base + step * 6] = t1 - t6;
63
- v[base + step * 2] = t2 + t5;
64
- v[base + step * 5] = t2 - t5;
65
- v[base + step * 4] = t3 + t4;
66
- v[base + step * 3] = t3 - t4;
67
- }
68
-
69
- /** `Uint8ClampedArray` is the level shift: it rounds and clamps to 0-255 on every store. */
70
- function writeBlock(comp: Component, at: number, flat: boolean): void {
71
- const { samples, stride } = comp;
72
- if (flat) {
73
- const value = (COEF[0] ?? 0) * (comp.dequant[0] ?? 0) + 128;
74
- for (let r = 0; r < 8; r += 1) samples.fill(value, at + r * stride, at + r * stride + 8);
75
- return;
76
- }
77
- for (let i = 0; i < 64; i += 1) WORK[i] = (COEF[i] ?? 0) * (comp.dequant[i] ?? 0);
78
- for (let c = 0; c < 8; c += 1) idct1d(WORK, c, 8);
79
- for (let r = 0; r < 8; r += 1) idct1d(WORK, r * 8, 1);
80
- for (let r = 0; r < 8; r += 1) {
81
- const row = at + r * stride;
82
- for (let c = 0; c < 8; c += 1) samples[row + c] = (WORK[r * 8 + c] ?? 0) + 128;
83
- }
84
- }
85
-
86
- function decodeBlock(reader: JpegBitReader, scan: ScanComponent, row: number, col: number): void {
87
- const { comp } = scan;
88
- if (row >= comp.blocksPerColumn || col >= comp.blocksPerLine) {
89
- throw imageDecodeFailed(`block ${col},${row} falls outside component ${comp.id}`, { row, col });
90
- }
91
- COEF.fill(0);
92
- const size = reader.decode(scan.dc);
93
- if (size > 15) {
94
- throw imageDecodeFailed(`a DC coefficient claims ${size} magnitude bits, over the 15 allowed`);
95
- }
96
- comp.pred += reader.receiveAndExtend(size);
97
- COEF[0] = comp.pred;
98
- let k = 1;
99
- let last = 0;
100
- while (k < 64) {
101
- const rs = reader.decode(scan.ac);
102
- const bits = rs & 15;
103
- const run = rs >> 4;
104
- if (bits === 0) {
105
- if (run !== 15) break; // 0x00 is end-of-block; 0xF0 is a run of 16 zeros
106
- k += 16;
107
- continue;
108
- }
109
- k += run;
110
- if (k > 63) {
111
- throw imageDecodeFailed(`an AC run overruns block ${col},${row} of component ${comp.id}`, {
112
- row,
113
- col,
114
- });
115
- }
116
- COEF[ZIGZAG[k] ?? 0] = reader.receiveAndExtend(bits);
117
- last = k;
118
- k += 1;
119
- }
120
- writeBlock(comp, row * 8 * comp.stride + col * 8, last === 0);
121
- }
122
-
123
- /** Leaves the walk at the next marker, or at the end when the file carries no EOI. */
124
- function skipToMarker(bytes: Uint8Array, from: number): number {
125
- for (let at = from; at + 1 < bytes.length; at += 1) {
126
- if ((bytes[at] ?? 0) === 0xff && (bytes[at + 1] ?? 0) !== 0x00) return at;
127
- }
128
- return bytes.length;
129
- }
130
-
131
- /** One scan's entropy-coded data, from the byte after its header to the marker that ends it. */
132
- function decodeScan(
133
- bytes: Uint8Array,
134
- start: number,
135
- seg: Uint8Array,
136
- frame: Frame,
137
- quant: ReadonlyArray<Float32Array | undefined>,
138
- dcTables: ReadonlyArray<HuffmanTable | undefined>,
139
- acTables: ReadonlyArray<HuffmanTable | undefined>,
140
- restartInterval: number,
141
- ): number {
142
- const scan: readonly ScanComponent[] = readScanHeader(seg, frame, quant, dcTables, acTables);
143
- const reader = new JpegBitReader(bytes, start);
144
- const single = scan.length === 1 ? scan[0] : undefined;
145
- // A non-interleaved scan walks the component's own blocks, which for a subsampled component is
146
- // fewer than its MCU-padded plane holds; an interleaved one walks whole MCUs.
147
- const perLine =
148
- single === undefined
149
- ? frame.mcusPerLine
150
- : Math.ceil(Math.ceil((frame.width * single.comp.h) / frame.maxH) / 8);
151
- const perColumn =
152
- single === undefined
153
- ? frame.mcusPerColumn
154
- : Math.ceil(Math.ceil((frame.height * single.comp.v) / frame.maxV) / 8);
155
- for (let n = 0; n < perLine * perColumn; n += 1) {
156
- if (restartInterval > 0 && n > 0 && n % restartInterval === 0) {
157
- if (!reader.restart()) {
158
- throw imageDecodeFailed(`the scan omits the restart marker due after ${n} units`, { n });
159
- }
160
- for (const entry of scan) entry.comp.pred = 0;
161
- }
162
- const row = (n / perLine) | 0;
163
- const col = n % perLine;
164
- if (single !== undefined) {
165
- decodeBlock(reader, single, row, col);
166
- continue;
167
- }
168
- for (const entry of scan) {
169
- for (let v = 0; v < entry.comp.v; v += 1) {
170
- for (let h = 0; h < entry.comp.h; h += 1) {
171
- decodeBlock(reader, entry, row * entry.comp.v + v, col * entry.comp.h + h);
172
- }
173
- }
174
- }
175
- }
176
- return skipToMarker(bytes, reader.position);
177
- }
178
-
179
- /**
180
- * Sample planes to RGBA, cropped to the declared size: the MCU-padded edge columns and rows exist
181
- * only so the last block is whole, and a decoder that returns them reports the wrong dimensions.
182
- * Chroma is upsampled by replication, which is what `h`/`v` below the maxima mean.
183
- */
184
- function toRaster(frame: Frame, adobeTransform: number): Raster {
185
- const { width, height, components, maxH, maxV } = frame;
186
- const pixels = new Uint8ClampedArray(width * height * 4);
187
- const luma = components[0];
188
- if (luma === undefined) throw imageDecodeFailed('the frame declares no components');
189
- const cb = components[1];
190
- const cr = components[2];
191
- // Adobe transform 0 over three components means the samples already ARE R, G and B.
192
- const alreadyRgb = adobeTransform === 0;
193
- for (let y = 0; y < height; y += 1) {
194
- const lumaRow = (((y * luma.v) / maxV) | 0) * luma.stride;
195
- let out = y * width * 4;
196
- if (cb === undefined || cr === undefined) {
197
- for (let x = 0; x < width; x += 1) {
198
- const grey = luma.samples[lumaRow + (((x * luma.h) / maxH) | 0)] ?? 0;
199
- pixels[out] = grey;
200
- pixels[out + 1] = grey;
201
- pixels[out + 2] = grey;
202
- pixels[out + 3] = 255;
203
- out += 4;
204
- }
205
- continue;
206
- }
207
- const cbRow = (((y * cb.v) / maxV) | 0) * cb.stride;
208
- const crRow = (((y * cr.v) / maxV) | 0) * cr.stride;
209
- for (let x = 0; x < width; x += 1) {
210
- const a = luma.samples[lumaRow + (((x * luma.h) / maxH) | 0)] ?? 0;
211
- const b = cb.samples[cbRow + (((x * cb.h) / maxH) | 0)] ?? 0;
212
- const c = cr.samples[crRow + (((x * cr.h) / maxH) | 0)] ?? 0;
213
- if (alreadyRgb) {
214
- pixels[out] = a;
215
- pixels[out + 1] = b;
216
- pixels[out + 2] = c;
217
- } else {
218
- const [r, g, blue] = ycbcrToRgb(a, b, c);
219
- pixels[out] = r;
220
- pixels[out + 1] = g;
221
- pixels[out + 2] = blue;
222
- }
223
- pixels[out + 3] = 255;
224
- out += 4;
225
- }
226
- }
227
- return rasterFrom(width, height, pixels);
228
- }
229
-
230
- /** JPEG bytes to RGBA. Baseline and extended sequential only; everything else is named and refused. */
231
- export function decodeJpeg(bytes: Uint8Array): Raster {
232
- if ((bytes[0] ?? 0) !== 0xff || (bytes[1] ?? 0) !== 0xd8) {
233
- throw imageDecodeFailed('the bytes do not open with a JPEG SOI marker (FF D8)', {
234
- first: `${hex(bytes[0])} ${hex(bytes[1])}`,
235
- });
236
- }
237
- const quant: Array<Float32Array | undefined> = [];
238
- const dcTables: Array<HuffmanTable | undefined> = [];
239
- const acTables: Array<HuffmanTable | undefined> = [];
240
- let frame: Frame | undefined;
241
- let restartInterval = 0;
242
- let adobeTransform = -1;
243
- let offset = 2;
244
- while (offset + 1 < bytes.length) {
245
- if ((bytes[offset] ?? 0) !== 0xff) {
246
- throw imageDecodeFailed(
247
- `expected a marker at byte ${offset}, found 0x${hex(bytes[offset])}`,
248
- {
249
- offset,
250
- },
251
- );
252
- }
253
- while (bytes[offset + 1] === 0xff) offset += 1; // fill bytes between segments
254
- const marker = bytes[offset + 1];
255
- if (marker === undefined) throw imageDecodeFailed('the file ends inside a marker');
256
- offset += 2;
257
- if (marker === 0xd9) break; // EOI
258
- if (marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) continue; // no payload
259
- assertSupportedCoding(marker);
260
- const length = readU16(bytes, offset);
261
- if (length < 2 || offset + length > bytes.length) {
262
- throw imageDecodeFailed(
263
- `segment FF${hex(marker)} declares ${length} bytes but ${bytes.length - offset} remain`,
264
- { marker: `FF${hex(marker)}`, length },
265
- );
266
- }
267
- const seg = bytes.subarray(offset + 2, offset + length);
268
- offset += length;
269
- if (marker === 0xdb) readQuantTables(seg, quant);
270
- else if (marker === 0xc4) readHuffmanTables(seg, dcTables, acTables);
271
- else if (marker === 0xc0 || marker === 0xc1) frame = readFrame(seg, marker);
272
- else if (marker === 0xdd) restartInterval = readU16(seg, 0);
273
- else if (marker === 0xee && isAdobe(seg)) adobeTransform = seg[11] ?? adobeTransform;
274
- else if (marker === 0xda) {
275
- if (frame === undefined) {
276
- throw imageDecodeFailed('a scan (SOS) arrives before any frame header (SOF)');
277
- }
278
- offset = decodeScan(bytes, offset, seg, frame, quant, dcTables, acTables, restartInterval);
279
- }
280
- }
281
- if (frame === undefined) throw imageDecodeFailed('the file carries no frame header (SOF)');
282
- return toRaster(frame, adobeTransform);
283
- }