@flighthq/bitmap 0.5.0-next.1944.cd6b6e7 → 0.5.1-edge.638.d4dbd4e

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 (43) hide show
  1. package/README.md +2 -2
  2. package/dist/bitmapDraw.d.ts.map +1 -1
  3. package/dist/bitmapDraw.js +3 -2
  4. package/dist/bitmapDraw.js.map +1 -1
  5. package/dist/bitmapEncode.d.ts +3 -2
  6. package/dist/bitmapEncode.d.ts.map +1 -1
  7. package/dist/bitmapEncode.js +20 -14
  8. package/dist/bitmapEncode.js.map +1 -1
  9. package/dist/bitmapEncodeBackend.d.ts +11 -0
  10. package/dist/bitmapEncodeBackend.d.ts.map +1 -0
  11. package/dist/bitmapEncodeBackend.js +58 -0
  12. package/dist/bitmapEncodeBackend.js.map +1 -0
  13. package/dist/bitmapFrom.d.ts +6 -8
  14. package/dist/bitmapFrom.d.ts.map +1 -1
  15. package/dist/bitmapFrom.js +8 -56
  16. package/dist/bitmapFrom.js.map +1 -1
  17. package/dist/bitmapReadbackBackend.d.ts +7 -0
  18. package/dist/bitmapReadbackBackend.d.ts.map +1 -0
  19. package/dist/bitmapReadbackBackend.js +21 -0
  20. package/dist/bitmapReadbackBackend.js.map +1 -0
  21. package/dist/bitmapReadbackResolver.d.ts +8 -0
  22. package/dist/bitmapReadbackResolver.d.ts.map +1 -0
  23. package/dist/bitmapReadbackResolver.js +12 -0
  24. package/dist/bitmapReadbackResolver.js.map +1 -0
  25. package/dist/contract.d.ts +2 -0
  26. package/dist/contract.d.ts.map +1 -1
  27. package/dist/contract.js +2 -0
  28. package/dist/contract.js.map +1 -1
  29. package/dist/explainBitmapReadback.d.ts.map +1 -1
  30. package/dist/explainBitmapReadback.js +7 -36
  31. package/dist/explainBitmapReadback.js.map +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +1 -1
  35. package/dist/index.js.map +1 -1
  36. package/package.json +4 -4
  37. package/src/bitmapDraw.test.ts +111 -16
  38. package/src/bitmapEncode.test.ts +99 -13
  39. package/src/bitmapEncodeBackend.test.ts +146 -0
  40. package/src/bitmapFrom.test.ts +31 -44
  41. package/src/bitmapReadbackBackend.test.ts +69 -0
  42. package/src/bitmapReadbackResolver.test.ts +32 -0
  43. package/src/explainBitmapReadback.test.ts +74 -54
@@ -1,6 +1,13 @@
1
+ import type { BitmapReadbackBackend, BitmapReadbackBackendReason } from '@flighthq/types/contract';
1
2
  import { describe, expect, it, vi } from 'vitest';
2
3
 
4
+ import { createBitmap } from './bitmap';
3
5
  import { createBitmapFromImageSource } from './bitmapFrom';
6
+ import {
7
+ installBitmapReadbackHostBackend,
8
+ resetBitmapReadbackBackendForTest,
9
+ setBitmapReadbackBackend,
10
+ } from './bitmapReadbackBackend';
4
11
  import { explainBitmapReadback } from './explainBitmapReadback';
5
12
 
6
13
  function makeCanvas(width = 8, height = 4): HTMLCanvasElement {
@@ -10,75 +17,88 @@ function makeCanvas(width = 8, height = 4): HTMLCanvasElement {
10
17
  return canvas;
11
18
  }
12
19
 
20
+ afterEach(() => {
21
+ resetBitmapReadbackBackendForTest();
22
+ });
23
+
13
24
  describe('explainBitmapReadback', () => {
14
- it('reports a readable source as ok', () => {
15
- expect(explainBitmapReadback(makeCanvas(), 8, 4)).toEqual({ readable: true, reason: 'ok' });
16
- });
25
+ it('reports empty-size before selecting or touching a backend', () => {
26
+ const readBitmap = vi.fn(() => ({ bitmap: null, reason: 'tainted-source' as const }));
27
+ setBitmapReadbackBackend({ readBitmap });
17
28
 
18
- it('reports an empty capture without touching the source', () => {
19
29
  expect(explainBitmapReadback(makeCanvas(), 0, 4)).toEqual({ readable: false, reason: 'empty-size' });
20
30
  expect(explainBitmapReadback(makeCanvas(), 8, 0)).toEqual({ readable: false, reason: 'empty-size' });
21
31
  expect(explainBitmapReadback(makeCanvas(), -1, 4)).toEqual({ readable: false, reason: 'empty-size' });
32
+ expect(createBitmapFromImageSource(makeCanvas(), 8, -1)).toBeNull();
33
+ expect(readBitmap).not.toHaveBeenCalled();
22
34
  });
23
35
 
24
- it('reports a tainted source', () => {
25
- const spy = vi.spyOn(CanvasRenderingContext2D.prototype, 'getImageData').mockImplementation(() => {
26
- throw new DOMException('Tainted canvases may not be exported.', 'SecurityError');
27
- });
28
- try {
29
- expect(explainBitmapReadback(makeCanvas(), 8, 4)).toEqual({ readable: false, reason: 'tainted-source' });
30
- } finally {
31
- spy.mockRestore();
32
- }
33
- });
36
+ it('distinguishes an absent backend from a host with no canvas', () => {
37
+ const source = makeCanvas();
34
38
 
35
- it('prefers the reason knowable without drawing — an empty capture outranks a tainted source', () => {
36
- const spy = vi.spyOn(CanvasRenderingContext2D.prototype, 'getImageData').mockImplementation(() => {
37
- throw new DOMException('Tainted canvases may not be exported.', 'SecurityError');
39
+ expect(explainBitmapReadback(source, 8, 4)).toEqual({
40
+ readable: false,
41
+ reason: 'backend-not-installed',
38
42
  });
39
- try {
40
- expect(explainBitmapReadback(makeCanvas(), 0, 4).reason).toBe('empty-size');
41
- } finally {
42
- spy.mockRestore();
43
- }
43
+ expect(createBitmapFromImageSource(source, 8, 4)).toBeNull();
44
44
  });
45
45
 
46
- it('agrees with what createBitmapFromImageSource actually does', () => {
47
- // The pull query duplicates the constructor's failure conditions by design, so the pairing is the
48
- // thing worth pinning: readable exactly when a bitmap comes back.
49
- const canvas = makeCanvas();
50
- expect(explainBitmapReadback(canvas, 8, 4).readable).toBe(createBitmapFromImageSource(canvas, 8, 4) !== null);
51
- expect(explainBitmapReadback(canvas, 0, 4).readable).toBe(createBitmapFromImageSource(canvas, 0, 4) !== null);
46
+ it('treats custom selection as terminal when a hidden host would succeed', () => {
47
+ const hostBitmap = createBitmap(8, 4);
48
+ const hostRead = vi.fn<BitmapReadbackBackend['readBitmap']>((_source, _width, _height, mode) => ({
49
+ bitmap: mode === 'bitmap' ? hostBitmap : null,
50
+ reason: 'ok' as const,
51
+ }));
52
+ const customRead = vi.fn(() => ({ bitmap: null, reason: 'no-canvas' as const }));
53
+ installBitmapReadbackHostBackend({ readBitmap: hostRead });
54
+ setBitmapReadbackBackend({ readBitmap: customRead });
55
+ const source = makeCanvas();
52
56
 
53
- const spy = vi.spyOn(CanvasRenderingContext2D.prototype, 'getImageData').mockImplementation(() => {
54
- throw new DOMException('Tainted canvases may not be exported.', 'SecurityError');
55
- });
56
- try {
57
- expect(explainBitmapReadback(canvas, 8, 4).readable).toBe(createBitmapFromImageSource(canvas, 8, 4) !== null);
58
- } finally {
59
- spy.mockRestore();
60
- }
57
+ expect(createBitmapFromImageSource(source, 8, 4)).toBeNull();
58
+ expect(explainBitmapReadback(source, 8, 4)).toEqual({ readable: false, reason: 'no-canvas' });
59
+ expect(customRead).toHaveBeenCalledTimes(2);
60
+ expect(hostRead).not.toHaveBeenCalled();
61
61
  });
62
62
 
63
- it('never throws, whatever the source does', () => {
64
- const spy = vi.spyOn(CanvasRenderingContext2D.prototype, 'drawImage').mockImplementation(() => {
65
- throw new TypeError('unsupported source');
66
- });
67
- try {
68
- expect(() => explainBitmapReadback(makeCanvas(), 8, 4)).not.toThrow();
69
- expect(explainBitmapReadback(makeCanvas(), 8, 4).readable).toBe(false);
70
- } finally {
71
- spy.mockRestore();
72
- }
63
+ it('returns the exact success outcome and keeps constructor/explainer parity', () => {
64
+ const bitmap = createBitmap(8, 4);
65
+ const readBitmap: BitmapReadbackBackend['readBitmap'] = vi.fn((_source, _width, _height, mode) => ({
66
+ bitmap: mode === 'bitmap' ? bitmap : null,
67
+ reason: 'ok' as const,
68
+ }));
69
+ setBitmapReadbackBackend({ readBitmap });
70
+ const source = makeCanvas();
71
+
72
+ expect(createBitmapFromImageSource(source, 8, 4)).toBe(bitmap);
73
+ expect(explainBitmapReadback(source, 8, 4)).toEqual({ readable: true, reason: 'ok' });
74
+ expect(readBitmap).toHaveBeenNthCalledWith(1, source, 8, 4, 'bitmap');
75
+ expect(readBitmap).toHaveBeenNthCalledWith(2, source, 8, 4, 'probe');
73
76
  });
74
77
 
75
- it('allocates no bitmap — it reads one pixel, not the whole capture', () => {
76
- const spy = vi.spyOn(CanvasRenderingContext2D.prototype, 'getImageData');
77
- try {
78
- explainBitmapReadback(makeCanvas(1024, 1024), 1024, 1024);
79
- expect(spy).toHaveBeenCalledWith(0, 0, 1, 1);
80
- } finally {
81
- spy.mockRestore();
82
- }
78
+ it.each(['no-canvas', 'tainted-source'] satisfies readonly BitmapReadbackBackendReason[])(
79
+ 'preserves the exact expected-failure reason %s',
80
+ (reason) => {
81
+ const backend: BitmapReadbackBackend = { readBitmap: () => ({ bitmap: null, reason }) };
82
+ setBitmapReadbackBackend(backend);
83
+ const source = makeCanvas();
84
+
85
+ expect(createBitmapFromImageSource(source, 8, 4)).toBeNull();
86
+ expect(explainBitmapReadback(source, 8, 4)).toEqual({ readable: false, reason });
87
+ },
88
+ );
89
+
90
+ it('does not predict a full-read allocation fault from a successful probe', () => {
91
+ const fault = new RangeError('full bitmap allocation failed');
92
+ const backend: BitmapReadbackBackend = {
93
+ readBitmap(_source, _width, _height, mode) {
94
+ if (mode === 'bitmap') throw fault;
95
+ return { bitmap: null, reason: 'ok' };
96
+ },
97
+ };
98
+ setBitmapReadbackBackend(backend);
99
+ const source = makeCanvas();
100
+
101
+ expect(explainBitmapReadback(source, 8, 4)).toEqual({ readable: true, reason: 'ok' });
102
+ expect(() => createBitmapFromImageSource(source, 8, 4)).toThrow(fault);
83
103
  });
84
104
  });