@flighthq/image 0.3.1-next.975.01aea94 → 0.4.0-edge.1908.751021c
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/dist/imageBackend.d.ts +5 -1
- package/dist/imageBackend.d.ts.map +1 -1
- package/dist/imageBackend.js +45 -19
- package/dist/imageBackend.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/imageBackend.test.ts +86 -1
- package/src/imageResourceFrom.test.ts +3 -0
package/dist/imageBackend.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
import type { ImageBackend } from '@flighthq/types/contract';
|
|
1
|
+
import type { BackendExplanation, ImageBackend } from '@flighthq/types/contract';
|
|
2
2
|
export declare function createWebImageBackend(): ImageBackend;
|
|
3
|
+
export declare function explainImageBackend(): BackendExplanation;
|
|
3
4
|
export declare function getImageBackend(): ImageBackend;
|
|
5
|
+
export declare function installImageHostBackend(backend: ImageBackend): void;
|
|
6
|
+
export declare function observeImageHostResult(operation: string, succeeded: boolean): void;
|
|
7
|
+
export declare function resetImageBackendForTest(): void;
|
|
4
8
|
export declare function setImageBackend(backend: ImageBackend | null): void;
|
|
5
9
|
//# sourceMappingURL=imageBackend.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageBackend.d.ts","sourceRoot":"","sources":["../src/imageBackend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAS,YAAY,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"imageBackend.d.ts","sourceRoot":"","sources":["../src/imageBackend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAS,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAIxF,wBAAgB,qBAAqB,IAAI,YAAY,CA4BpD;AAED,wBAAgB,mBAAmB,IAAI,kBAAkB,CAaxD;AAED,wBAAgB,eAAe,IAAI,YAAY,CAE9C;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAMnE;AAED,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAKlF;AAED,wBAAgB,wBAAwB,IAAI,IAAI,CAK/C;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,GAAG,IAAI,CAElE"}
|
package/dist/imageBackend.js
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
1
|
import { createImageResourceFromImageElement } from './imageResourceFrom';
|
|
2
|
-
// Builds the default web backend over `Image` + `decode()`. Created lazily by `getImageBackend` — no
|
|
3
|
-
// DOM is touched at import time, so importing the package has no side effect.
|
|
4
|
-
//
|
|
5
|
-
// ★ THIS BODY IS THE PREVIOUS `loadImageResourceFromUrl` MOVED VERBATIM, INCLUDING ITS KNOWN ABORT
|
|
6
|
-
// RACE: aborting rejects the caller and clears `src`, but the underlying load may already be in
|
|
7
|
-
// flight and is not guaranteed to stop. That defect is preserved deliberately rather than repaired
|
|
8
|
-
// here — this change is the seam, and fixing behaviour while relocating it would make the seam
|
|
9
|
-
// unreviewable against the approval that authorised it.
|
|
10
2
|
export function createWebImageBackend() {
|
|
11
3
|
return {
|
|
12
4
|
async loadImageFromUrl(url, crossOrigin, signal) {
|
|
@@ -15,8 +7,6 @@ export function createWebImageBackend() {
|
|
|
15
7
|
if (crossOrigin !== undefined)
|
|
16
8
|
img.crossOrigin = crossOrigin;
|
|
17
9
|
img.src = url;
|
|
18
|
-
// Wire abort to cancel the pending decode and reject with the signal's reason. Always remove the
|
|
19
|
-
// listener when the race settles so a long-lived signal does not retain the image and closure.
|
|
20
10
|
if (signal !== undefined) {
|
|
21
11
|
let rejectAbort = () => { };
|
|
22
12
|
const abortPromise = new Promise((_, reject) => {
|
|
@@ -41,17 +31,53 @@ export function createWebImageBackend() {
|
|
|
41
31
|
},
|
|
42
32
|
};
|
|
43
33
|
}
|
|
44
|
-
|
|
34
|
+
export function explainImageBackend() {
|
|
35
|
+
if (_custom !== null) {
|
|
36
|
+
return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
|
|
37
|
+
}
|
|
38
|
+
if (_host !== null) {
|
|
39
|
+
return {
|
|
40
|
+
conflict: _hostConflict,
|
|
41
|
+
layer: 'host',
|
|
42
|
+
operation: _hostObservation !== null ? _hostObservation.operation : null,
|
|
43
|
+
viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return { conflict: false, layer: 'host-not-enabled', operation: null, viability: 'unobserved' };
|
|
47
|
+
}
|
|
45
48
|
export function getImageBackend() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
return _custom ?? _host ?? _sentinel;
|
|
50
|
+
}
|
|
51
|
+
export function installImageHostBackend(backend) {
|
|
52
|
+
if (_host !== null) {
|
|
53
|
+
if (_host !== backend)
|
|
54
|
+
_hostConflict = true;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
_host = backend;
|
|
58
|
+
}
|
|
59
|
+
export function observeImageHostResult(operation, succeeded) {
|
|
60
|
+
_hostObservation = {
|
|
61
|
+
operation,
|
|
62
|
+
viability: succeeded ? 'available' : 'runtime-api-unavailable',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function resetImageBackendForTest() {
|
|
66
|
+
_custom = null;
|
|
67
|
+
_host = null;
|
|
68
|
+
_hostConflict = false;
|
|
69
|
+
_hostObservation = null;
|
|
49
70
|
}
|
|
50
|
-
// Installs a native host image backend; pass null to fall back to the lazy web default. Resettable on
|
|
51
|
-
// purpose: a seam a caller cannot restore is a one-way door, and a test that swapped the backend could
|
|
52
|
-
// not put it back.
|
|
53
71
|
export function setImageBackend(backend) {
|
|
54
|
-
|
|
72
|
+
_custom = backend;
|
|
55
73
|
}
|
|
56
|
-
|
|
74
|
+
const _sentinel = {
|
|
75
|
+
loadImageFromUrl(_url, _crossOrigin, _signal) {
|
|
76
|
+
return Promise.reject(new Error('No image backend installed. Call enableHostWebImage() or setImageBackend() first.'));
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
let _custom = null;
|
|
80
|
+
let _host = null;
|
|
81
|
+
let _hostConflict = false;
|
|
82
|
+
let _hostObservation = null;
|
|
57
83
|
//# sourceMappingURL=imageBackend.js.map
|
package/dist/imageBackend.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imageBackend.js","sourceRoot":"","sources":["../src/imageBackend.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mCAAmC,EAAE,MAAM,qBAAqB,CAAC;AAE1E,
|
|
1
|
+
{"version":3,"file":"imageBackend.js","sourceRoot":"","sources":["../src/imageBackend.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { cloneImageResource, createCompressedImage, createEmbeddedImageResourceReference, createExternalImageResourceReference, createImageResource, createImageResourceFailure, createImageResourceFromBitmap, createImageResourceFromCanvas, createImageResourceFromImageBitmap, createImageResourceFromImageElement, disableImageBitmapComposition, enableImageBitmapComposition, explainImageResourceReferenceResolution, isImageResourceEmpty, isImageUrlSameOrigin, loadImageResourceFromBase64, loadImageResourceFromBlob, loadImageResourceFromBytes, loadImageResourceFromUrl, resetFailedImageResourceReference, resolveImageResourceReference, } from './contract';
|
|
1
|
+
export { explainImageBackend, cloneImageResource, createCompressedImage, createEmbeddedImageResourceReference, createExternalImageResourceReference, createImageResource, createImageResourceFailure, createImageResourceFromBitmap, createImageResourceFromCanvas, createImageResourceFromImageBitmap, createImageResourceFromImageElement, disableImageBitmapComposition, enableImageBitmapComposition, explainImageResourceReferenceResolution, isImageResourceEmpty, isImageUrlSameOrigin, loadImageResourceFromBase64, loadImageResourceFromBlob, loadImageResourceFromBytes, loadImageResourceFromUrl, resetFailedImageResourceReference, resolveImageResourceReference, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,oCAAoC,EACpC,oCAAoC,EACpC,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,6BAA6B,EAC7B,kCAAkC,EAClC,mCAAmC,EACnC,6BAA6B,EAC7B,4BAA4B,EAC5B,uCAAuC,EACvC,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,iCAAiC,EACjC,6BAA6B,GAC9B,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,oCAAoC,EACpC,oCAAoC,EACpC,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,6BAA6B,EAC7B,kCAAkC,EAClC,mCAAmC,EACnC,6BAA6B,EAC7B,4BAA4B,EAC5B,uCAAuC,EACvC,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,iCAAiC,EACjC,6BAA6B,GAC9B,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { cloneImageResource, createCompressedImage, createEmbeddedImageResourceReference, createExternalImageResourceReference, createImageResource, createImageResourceFailure, createImageResourceFromBitmap, createImageResourceFromCanvas, createImageResourceFromImageBitmap, createImageResourceFromImageElement, disableImageBitmapComposition, enableImageBitmapComposition, explainImageResourceReferenceResolution, isImageResourceEmpty, isImageUrlSameOrigin, loadImageResourceFromBase64, loadImageResourceFromBlob, loadImageResourceFromBytes, loadImageResourceFromUrl, resetFailedImageResourceReference, resolveImageResourceReference, } from './contract';
|
|
1
|
+
export { explainImageBackend, cloneImageResource, createCompressedImage, createEmbeddedImageResourceReference, createExternalImageResourceReference, createImageResource, createImageResourceFailure, createImageResourceFromBitmap, createImageResourceFromCanvas, createImageResourceFromImageBitmap, createImageResourceFromImageElement, disableImageBitmapComposition, enableImageBitmapComposition, explainImageResourceReferenceResolution, isImageResourceEmpty, isImageUrlSameOrigin, loadImageResourceFromBase64, loadImageResourceFromBlob, loadImageResourceFromBytes, loadImageResourceFromUrl, resetFailedImageResourceReference, resolveImageResourceReference, } from './contract';
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAClB,qBAAqB,EACrB,oCAAoC,EACpC,oCAAoC,EACpC,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,6BAA6B,EAC7B,kCAAkC,EAClC,mCAAmC,EACnC,6BAA6B,EAC7B,4BAA4B,EAC5B,uCAAuC,EACvC,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,iCAAiC,EACjC,6BAA6B,GAC9B,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,qBAAqB,EACrB,oCAAoC,EACpC,oCAAoC,EACpC,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,6BAA6B,EAC7B,kCAAkC,EAClC,mCAAmC,EACnC,6BAA6B,EAC7B,4BAA4B,EAC5B,uCAAuC,EACvC,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,iCAAiC,EACjC,6BAA6B,GAC9B,MAAM,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/image",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-edge.1908.751021c",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/entity": "0.
|
|
42
|
-
"@flighthq/image-codec": "0.
|
|
43
|
-
"@flighthq/types": "0.
|
|
41
|
+
"@flighthq/entity": "0.4.0-edge.1908.751021c",
|
|
42
|
+
"@flighthq/image-codec": "0.4.0-edge.1908.751021c",
|
|
43
|
+
"@flighthq/types": "0.4.0-edge.1908.751021c"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"typescript": "^5.3.0"
|
package/src/imageBackend.test.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import type { Image, ImageBackend } from '@flighthq/types/contract';
|
|
2
2
|
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
createWebImageBackend,
|
|
6
|
+
getImageBackend,
|
|
7
|
+
setImageBackend,
|
|
8
|
+
explainImageBackend,
|
|
9
|
+
installImageHostBackend,
|
|
10
|
+
observeImageHostResult,
|
|
11
|
+
resetImageBackendForTest,
|
|
12
|
+
} from './imageBackend';
|
|
5
13
|
import { loadImageResourceFromUrl } from './imageResourceFrom';
|
|
6
14
|
|
|
7
15
|
// Every test that installs a backend must put it back, or a later test sees a stale one.
|
|
@@ -32,6 +40,34 @@ describe('createWebImageBackend', () => {
|
|
|
32
40
|
});
|
|
33
41
|
});
|
|
34
42
|
|
|
43
|
+
describe('explainImageBackend', () => {
|
|
44
|
+
afterEach(() => resetImageBackendForTest());
|
|
45
|
+
|
|
46
|
+
it('reports host-not-enabled when no backend is installed', () => {
|
|
47
|
+
resetImageBackendForTest();
|
|
48
|
+
const explanation = explainImageBackend();
|
|
49
|
+
expect(explanation.layer).toBe('host-not-enabled');
|
|
50
|
+
expect(explanation.conflict).toBe(false);
|
|
51
|
+
expect(explanation.viability).toBe('unobserved');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('reports custom layer when a custom backend is set', () => {
|
|
55
|
+
setImageBackend(getImageBackend());
|
|
56
|
+
expect(explainImageBackend().layer).toBe('custom');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('reports host layer when a host backend is installed', () => {
|
|
60
|
+
installImageHostBackend(getImageBackend());
|
|
61
|
+
expect(explainImageBackend().layer).toBe('host');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('reports conflict when two different host backends are installed', () => {
|
|
65
|
+
installImageHostBackend({ ...getImageBackend() });
|
|
66
|
+
installImageHostBackend({ ...getImageBackend() });
|
|
67
|
+
expect(explainImageBackend().conflict).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
35
71
|
describe('getImageBackend', () => {
|
|
36
72
|
it('defaults lazily to a web backend rather than being registered at import', () => {
|
|
37
73
|
expect(typeof getImageBackend().loadImageFromUrl).toBe('function');
|
|
@@ -48,6 +84,55 @@ describe('getImageBackend', () => {
|
|
|
48
84
|
});
|
|
49
85
|
});
|
|
50
86
|
|
|
87
|
+
describe('installImageHostBackend', () => {
|
|
88
|
+
afterEach(() => resetImageBackendForTest());
|
|
89
|
+
|
|
90
|
+
it('installs a host backend that getImageBackend returns', () => {
|
|
91
|
+
const backend = getImageBackend();
|
|
92
|
+
installImageHostBackend(backend);
|
|
93
|
+
expect(getImageBackend()).toBe(backend);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('is first-host-wins: a second different backend sets conflict', () => {
|
|
97
|
+
const first = { ...getImageBackend() };
|
|
98
|
+
const second = { ...getImageBackend() };
|
|
99
|
+
installImageHostBackend(first);
|
|
100
|
+
installImageHostBackend(second);
|
|
101
|
+
expect(getImageBackend()).toBe(first);
|
|
102
|
+
expect(explainImageBackend().conflict).toBe(true);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('observeImageHostResult', () => {
|
|
107
|
+
afterEach(() => resetImageBackendForTest());
|
|
108
|
+
|
|
109
|
+
it('records a successful observation', () => {
|
|
110
|
+
installImageHostBackend(getImageBackend());
|
|
111
|
+
observeImageHostResult('loadImageFromUrl', true);
|
|
112
|
+
const explanation = explainImageBackend();
|
|
113
|
+
expect(explanation.operation).toBe('loadImageFromUrl');
|
|
114
|
+
expect(explanation.viability).toBe('available');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('records a failed observation', () => {
|
|
118
|
+
installImageHostBackend(getImageBackend());
|
|
119
|
+
observeImageHostResult('loadImageFromUrl', false);
|
|
120
|
+
expect(explainImageBackend().viability).toBe('runtime-api-unavailable');
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe('resetImageBackendForTest', () => {
|
|
125
|
+
it('clears all backend slots', () => {
|
|
126
|
+
setImageBackend(getImageBackend());
|
|
127
|
+
installImageHostBackend(getImageBackend());
|
|
128
|
+
observeImageHostResult('loadImageFromUrl', true);
|
|
129
|
+
resetImageBackendForTest();
|
|
130
|
+
expect(explainImageBackend().layer).toBe('host-not-enabled');
|
|
131
|
+
expect(explainImageBackend().conflict).toBe(false);
|
|
132
|
+
expect(explainImageBackend().viability).toBe('unobserved');
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
51
136
|
describe('setImageBackend', () => {
|
|
52
137
|
it('restores a working web default when passed null, rather than leaving a one-way door', () => {
|
|
53
138
|
const { backend } = recordingBackend();
|
|
@@ -2,6 +2,7 @@ import { createEntity } from '@flighthq/entity/contract';
|
|
|
2
2
|
import type { Bitmap } from '@flighthq/types/contract';
|
|
3
3
|
import { BitmapTextureSourceKind } from '@flighthq/types/contract';
|
|
4
4
|
|
|
5
|
+
import { createWebImageBackend, resetImageBackendForTest, setImageBackend } from './imageBackend';
|
|
5
6
|
import { createImageResource } from './imageResource';
|
|
6
7
|
import {
|
|
7
8
|
createImageResourceFromBitmap,
|
|
@@ -17,11 +18,13 @@ import {
|
|
|
17
18
|
|
|
18
19
|
// Stub img.decode() so async load functions resolve immediately in jsdom.
|
|
19
20
|
beforeEach(() => {
|
|
21
|
+
setImageBackend(createWebImageBackend());
|
|
20
22
|
HTMLImageElement.prototype.decode = vi.fn().mockResolvedValue(undefined);
|
|
21
23
|
});
|
|
22
24
|
|
|
23
25
|
afterEach(() => {
|
|
24
26
|
vi.restoreAllMocks();
|
|
27
|
+
resetImageBackendForTest();
|
|
25
28
|
delete (HTMLImageElement.prototype as Partial<HTMLImageElement>).decode;
|
|
26
29
|
});
|
|
27
30
|
|