@flighthq/webcam 0.5.1-edge.685.f72fc7f → 0.5.1-edge.730.f29a5aa
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/README.md +3 -3
- package/dist/contract.d.ts +1 -1
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -1
- package/dist/contract.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 +2 -6
- package/src/missing.test.ts +3 -0
- package/dist/mediaFileCapture.d.ts +0 -13
- package/dist/mediaFileCapture.d.ts.map +0 -1
- package/dist/mediaFileCapture.js +0 -163
- package/dist/mediaFileCapture.js.map +0 -1
- package/src/mediaFileCapture.test.ts +0 -207
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @flighthq/webcam
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Future explicit-host live webcam streaming, device enumeration, and stream lifecycle
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/webcam`. The `@fligh
|
|
|
13
13
|
This package is part of the locked-version Flight SDK graph. Applications may instead install and import `@flighthq/sdk` when package-level tree shaking is sufficient.
|
|
14
14
|
|
|
15
15
|
- [Flight project](https://github.com/flighthq/flight)
|
|
16
|
-
- [Source for @flighthq/webcam@0.5.1-edge.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/webcam@0.5.1-edge.730.f29a5aa](https://github.com/flighthq/flight/tree/f29a5aab5db192284ce5cadf08ffae2649a598ea/packages/webcam)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/f29a5aab5db192284ce5cadf08ffae2649a598ea/LICENSE.md)
|
package/dist/contract.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {};
|
|
2
2
|
//# sourceMappingURL=contract.d.ts.map
|
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
|
package/dist/contract.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export {};
|
|
2
2
|
//# sourceMappingURL=contract.js.map
|
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":""}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|
|
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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|
|
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":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/webcam",
|
|
3
|
-
"version": "0.5.1-edge.
|
|
3
|
+
"version": "0.5.1-edge.730.f29a5aa",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -37,13 +37,9 @@
|
|
|
37
37
|
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
|
-
"dependencies": {
|
|
41
|
-
"@flighthq/entity": "0.5.1-edge.685.f72fc7f",
|
|
42
|
-
"@flighthq/types": "0.5.1-edge.685.f72fc7f"
|
|
43
|
-
},
|
|
44
40
|
"devDependencies": {
|
|
45
41
|
"typescript": "^5.3.0"
|
|
46
42
|
},
|
|
47
|
-
"description": "
|
|
43
|
+
"description": "Future explicit-host live webcam streaming, device enumeration, and stream lifecycle",
|
|
48
44
|
"sideEffects": false
|
|
49
45
|
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { BackendExplanation } from '@flighthq/types/contract';
|
|
2
|
-
import type { MediaFileCaptureBackend, MediaFileCaptureOptions, MediaFileCapturePhoto, MediaFileCaptureVideo } from '@flighthq/types/contract';
|
|
3
|
-
export declare function createWebMediaFileCaptureBackend(): MediaFileCaptureBackend;
|
|
4
|
-
export declare function explainMediaFileCaptureBackend(): BackendExplanation;
|
|
5
|
-
export declare function getMediaFileCaptureBackend(): MediaFileCaptureBackend;
|
|
6
|
-
export declare function installMediaFileCaptureHostBackend(backend: MediaFileCaptureBackend): void;
|
|
7
|
-
export declare function observeMediaFileCaptureHostResult(operation: string, succeeded: boolean): void;
|
|
8
|
-
export declare function recordMediaFileCaptureVideo(options?: Readonly<MediaFileCaptureOptions>): Promise<MediaFileCaptureVideo | null>;
|
|
9
|
-
export declare function resetMediaFileCaptureBackendForTest(): void;
|
|
10
|
-
export declare function selectMediaFileCaptureImage(options?: Readonly<MediaFileCaptureOptions>): Promise<MediaFileCapturePhoto | null>;
|
|
11
|
-
export declare function setMediaFileCaptureBackend(backend: MediaFileCaptureBackend | null): void;
|
|
12
|
-
export declare function takeMediaFileCapturePhoto(options?: Readonly<MediaFileCaptureOptions>): Promise<MediaFileCapturePhoto | null>;
|
|
13
|
-
//# sourceMappingURL=mediaFileCapture.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mediaFileCapture.d.ts","sourceRoot":"","sources":["../src/mediaFileCapture.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EACV,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACtB,MAAM,0BAA0B,CAAC;AAKlC,wBAAgB,gCAAgC,IAAI,uBAAuB,CA0F1E;AAED,wBAAgB,8BAA8B,IAAI,kBAAkB,CAanE;AAED,wBAAgB,0BAA0B,IAAI,uBAAuB,CAEpE;AAED,wBAAgB,kCAAkC,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,CAMzF;AAED,wBAAgB,iCAAiC,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAK7F;AAGD,wBAAgB,2BAA2B,CACzC,OAAO,CAAC,EAAE,QAAQ,CAAC,uBAAuB,CAAC,GAC1C,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAEvC;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAK1D;AAGD,wBAAgB,2BAA2B,CACzC,OAAO,CAAC,EAAE,QAAQ,CAAC,uBAAuB,CAAC,GAC1C,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAEvC;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,uBAAuB,GAAG,IAAI,GAAG,IAAI,CAExF;AAGD,wBAAgB,yBAAyB,CACvC,OAAO,CAAC,EAAE,QAAQ,CAAC,uBAAuB,CAAC,GAC1C,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAEvC"}
|
package/dist/mediaFileCapture.js
DELETED
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
// Builds the default web backend over a transient <input type="file">. capture resolves to null when
|
|
2
|
-
// the document is absent (jsdom), the user cancels, or the file cannot be read — capture is not
|
|
3
|
-
// guaranteed. Real pixel dimensions are not decoded; width/height resolve to 0.
|
|
4
|
-
export function createWebMediaFileCaptureBackend() {
|
|
5
|
-
return {
|
|
6
|
-
capture(options) {
|
|
7
|
-
return new Promise((resolve) => {
|
|
8
|
-
if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
|
|
9
|
-
observeMediaFileCaptureHostResult('capture', false);
|
|
10
|
-
resolve(null);
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
try {
|
|
14
|
-
const input = document.createElement('input');
|
|
15
|
-
input.type = 'file';
|
|
16
|
-
input.accept = 'image/*';
|
|
17
|
-
if (options.source === 'camera')
|
|
18
|
-
input.capture = 'environment';
|
|
19
|
-
const settle = (value) => {
|
|
20
|
-
input.onchange = null;
|
|
21
|
-
input.oncancel = null;
|
|
22
|
-
observeMediaFileCaptureHostResult('capture', value !== null);
|
|
23
|
-
resolve(value);
|
|
24
|
-
};
|
|
25
|
-
input.onchange = () => {
|
|
26
|
-
const file = input.files?.[0] ?? null;
|
|
27
|
-
if (file === null) {
|
|
28
|
-
settle(null);
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
const reader = new FileReader();
|
|
32
|
-
reader.onload = () => {
|
|
33
|
-
settle({
|
|
34
|
-
dataUrl: typeof reader.result === 'string' ? reader.result : '',
|
|
35
|
-
width: 0,
|
|
36
|
-
height: 0,
|
|
37
|
-
format: file.type,
|
|
38
|
-
});
|
|
39
|
-
};
|
|
40
|
-
reader.onerror = () => settle(null);
|
|
41
|
-
reader.readAsDataURL(file);
|
|
42
|
-
};
|
|
43
|
-
input.oncancel = () => settle(null);
|
|
44
|
-
input.click();
|
|
45
|
-
}
|
|
46
|
-
catch {
|
|
47
|
-
observeMediaFileCaptureHostResult('capture', false);
|
|
48
|
-
resolve(null);
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
},
|
|
52
|
-
captureVideo(options) {
|
|
53
|
-
return new Promise((resolve) => {
|
|
54
|
-
if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
|
|
55
|
-
observeMediaFileCaptureHostResult('captureVideo', false);
|
|
56
|
-
resolve(null);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
try {
|
|
60
|
-
const input = document.createElement('input');
|
|
61
|
-
input.type = 'file';
|
|
62
|
-
input.accept = 'video/*';
|
|
63
|
-
if (options.source === 'camera')
|
|
64
|
-
input.capture = 'environment';
|
|
65
|
-
const settle = (value) => {
|
|
66
|
-
input.onchange = null;
|
|
67
|
-
input.oncancel = null;
|
|
68
|
-
observeMediaFileCaptureHostResult('captureVideo', value !== null);
|
|
69
|
-
resolve(value);
|
|
70
|
-
};
|
|
71
|
-
input.onchange = () => {
|
|
72
|
-
const file = input.files?.[0] ?? null;
|
|
73
|
-
if (file === null) {
|
|
74
|
-
settle(null);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
const reader = new FileReader();
|
|
78
|
-
reader.onload = () => {
|
|
79
|
-
settle({
|
|
80
|
-
dataUrl: typeof reader.result === 'string' ? reader.result : '',
|
|
81
|
-
duration: 0,
|
|
82
|
-
format: file.type,
|
|
83
|
-
});
|
|
84
|
-
};
|
|
85
|
-
reader.onerror = () => settle(null);
|
|
86
|
-
reader.readAsDataURL(file);
|
|
87
|
-
};
|
|
88
|
-
input.oncancel = () => settle(null);
|
|
89
|
-
input.click();
|
|
90
|
-
}
|
|
91
|
-
catch {
|
|
92
|
-
observeMediaFileCaptureHostResult('captureVideo', false);
|
|
93
|
-
resolve(null);
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
export function explainMediaFileCaptureBackend() {
|
|
100
|
-
if (_custom !== null) {
|
|
101
|
-
return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
|
|
102
|
-
}
|
|
103
|
-
if (_host !== null) {
|
|
104
|
-
return {
|
|
105
|
-
conflict: _hostConflict,
|
|
106
|
-
layer: 'host',
|
|
107
|
-
operation: _hostObservation !== null ? _hostObservation.operation : null,
|
|
108
|
-
viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
return { conflict: false, layer: 'host-not-enabled', operation: null, viability: 'unobserved' };
|
|
112
|
-
}
|
|
113
|
-
export function getMediaFileCaptureBackend() {
|
|
114
|
-
return _custom ?? _host ?? _sentinel;
|
|
115
|
-
}
|
|
116
|
-
export function installMediaFileCaptureHostBackend(backend) {
|
|
117
|
-
if (_host !== null) {
|
|
118
|
-
if (_host !== backend)
|
|
119
|
-
_hostConflict = true;
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
_host = backend;
|
|
123
|
-
}
|
|
124
|
-
export function observeMediaFileCaptureHostResult(operation, succeeded) {
|
|
125
|
-
_hostObservation = {
|
|
126
|
-
operation,
|
|
127
|
-
viability: succeeded ? 'available' : 'runtime-api-unavailable',
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
// Records a video from the device camera. Resolves null when cancelled, denied, or unavailable.
|
|
131
|
-
export function recordMediaFileCaptureVideo(options) {
|
|
132
|
-
return getMediaFileCaptureBackend().captureVideo({ ...options, source: 'camera' });
|
|
133
|
-
}
|
|
134
|
-
export function resetMediaFileCaptureBackendForTest() {
|
|
135
|
-
_custom = null;
|
|
136
|
-
_host = null;
|
|
137
|
-
_hostConflict = false;
|
|
138
|
-
_hostObservation = null;
|
|
139
|
-
}
|
|
140
|
-
// Picks an existing image from the photo library. Resolves null when cancelled or unavailable.
|
|
141
|
-
export function selectMediaFileCaptureImage(options) {
|
|
142
|
-
return getMediaFileCaptureBackend().capture({ ...options, source: 'photos' });
|
|
143
|
-
}
|
|
144
|
-
export function setMediaFileCaptureBackend(backend) {
|
|
145
|
-
_custom = backend;
|
|
146
|
-
}
|
|
147
|
-
// Captures a photo from the device camera. Resolves null when cancelled, denied, or unavailable.
|
|
148
|
-
export function takeMediaFileCapturePhoto(options) {
|
|
149
|
-
return getMediaFileCaptureBackend().capture({ ...options, source: 'camera' });
|
|
150
|
-
}
|
|
151
|
-
let _custom = null;
|
|
152
|
-
let _host = null;
|
|
153
|
-
let _hostConflict = false;
|
|
154
|
-
let _hostObservation = null;
|
|
155
|
-
const _sentinel = {
|
|
156
|
-
async capture() {
|
|
157
|
-
return null;
|
|
158
|
-
},
|
|
159
|
-
async captureVideo() {
|
|
160
|
-
return null;
|
|
161
|
-
},
|
|
162
|
-
};
|
|
163
|
-
//# sourceMappingURL=mediaFileCapture.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mediaFileCapture.js","sourceRoot":"","sources":["../src/mediaFileCapture.ts"],"names":[],"mappings":"AAQA,qGAAqG;AACrG,gGAAgG;AAChG,gFAAgF;AAChF,MAAM,UAAU,gCAAgC;IAC9C,OAAO;QACL,OAAO,CAAC,OAAO;YACb,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC7B,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,OAAO,QAAQ,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;oBACpF,iCAAiC,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;oBACpD,OAAO,CAAC,IAAI,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;oBAC9C,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;oBACpB,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;oBACzB,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ;wBAAE,KAAK,CAAC,OAAO,GAAG,aAAa,CAAC;oBAC/D,MAAM,MAAM,GAAG,CAAC,KAAmC,EAAQ,EAAE;wBAC3D,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;wBACtB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;wBACtB,iCAAiC,CAAC,SAAS,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC;wBAC7D,OAAO,CAAC,KAAK,CAAC,CAAC;oBACjB,CAAC,CAAC;oBACF,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE;wBACpB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;wBACtC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;4BAClB,MAAM,CAAC,IAAI,CAAC,CAAC;4BACb,OAAO;wBACT,CAAC;wBACD,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;wBAChC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE;4BACnB,MAAM,CAAC;gCACL,OAAO,EAAE,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;gCAC/D,KAAK,EAAE,CAAC;gCACR,MAAM,EAAE,CAAC;gCACT,MAAM,EAAE,IAAI,CAAC,IAAI;6BAClB,CAAC,CAAC;wBACL,CAAC,CAAC;wBACF,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;wBACpC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;oBAC7B,CAAC,CAAC;oBACF,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBACpC,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,CAAC;gBAAC,MAAM,CAAC;oBACP,iCAAiC,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;oBACpD,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,YAAY,CAAC,OAAO;YAClB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC7B,IAAI,OAAO,QAAQ,KAAK,WAAW,IAAI,OAAO,QAAQ,CAAC,aAAa,KAAK,UAAU,EAAE,CAAC;oBACpF,iCAAiC,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;oBACzD,OAAO,CAAC,IAAI,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,KAAK,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;oBAC9C,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC;oBACpB,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;oBACzB,IAAI,OAAO,CAAC,MAAM,KAAK,QAAQ;wBAAE,KAAK,CAAC,OAAO,GAAG,aAAa,CAAC;oBAC/D,MAAM,MAAM,GAAG,CAAC,KAAmC,EAAQ,EAAE;wBAC3D,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;wBACtB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;wBACtB,iCAAiC,CAAC,cAAc,EAAE,KAAK,KAAK,IAAI,CAAC,CAAC;wBAClE,OAAO,CAAC,KAAK,CAAC,CAAC;oBACjB,CAAC,CAAC;oBACF,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE;wBACpB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;wBACtC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;4BAClB,MAAM,CAAC,IAAI,CAAC,CAAC;4BACb,OAAO;wBACT,CAAC;wBACD,MAAM,MAAM,GAAG,IAAI,UAAU,EAAE,CAAC;wBAChC,MAAM,CAAC,MAAM,GAAG,GAAG,EAAE;4BACnB,MAAM,CAAC;gCACL,OAAO,EAAE,OAAO,MAAM,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;gCAC/D,QAAQ,EAAE,CAAC;gCACX,MAAM,EAAE,IAAI,CAAC,IAAI;6BAClB,CAAC,CAAC;wBACL,CAAC,CAAC;wBACF,MAAM,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;wBACpC,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;oBAC7B,CAAC,CAAC;oBACF,KAAK,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;oBACpC,KAAK,CAAC,KAAK,EAAE,CAAC;gBAChB,CAAC;gBAAC,MAAM,CAAC;oBACP,iCAAiC,CAAC,cAAc,EAAE,KAAK,CAAC,CAAC;oBACzD,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,8BAA8B;IAC5C,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACrB,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;IAChG,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO;YACL,QAAQ,EAAE,aAAa;YACvB,KAAK,EAAE,MAAM;YACb,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;YACxE,SAAS,EAAE,gBAAgB,KAAK,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY;SACjF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,kBAAkB,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC;AAClG,CAAC;AAED,MAAM,UAAU,0BAA0B;IACxC,OAAO,OAAO,IAAI,KAAK,IAAI,SAAS,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,OAAgC;IACjF,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,IAAI,KAAK,KAAK,OAAO;YAAE,aAAa,GAAG,IAAI,CAAC;QAC5C,OAAO;IACT,CAAC;IACD,KAAK,GAAG,OAAO,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,iCAAiC,CAAC,SAAiB,EAAE,SAAkB;IACrF,gBAAgB,GAAG;QACjB,SAAS;QACT,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,yBAAyB;KAC/D,CAAC;AACJ,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,2BAA2B,CACzC,OAA2C;IAE3C,OAAO,0BAA0B,EAAE,CAAC,YAAY,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AACrF,CAAC;AAED,MAAM,UAAU,mCAAmC;IACjD,OAAO,GAAG,IAAI,CAAC;IACf,KAAK,GAAG,IAAI,CAAC;IACb,aAAa,GAAG,KAAK,CAAC;IACtB,gBAAgB,GAAG,IAAI,CAAC;AAC1B,CAAC;AAED,+FAA+F;AAC/F,MAAM,UAAU,2BAA2B,CACzC,OAA2C;IAE3C,OAAO,0BAA0B,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,0BAA0B,CAAC,OAAuC;IAChF,OAAO,GAAG,OAAO,CAAC;AACpB,CAAC;AAED,iGAAiG;AACjG,MAAM,UAAU,yBAAyB,CACvC,OAA2C;IAE3C,OAAO,0BAA0B,EAAE,CAAC,OAAO,CAAC,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;AAChF,CAAC;AAED,IAAI,OAAO,GAAmC,IAAI,CAAC;AACnD,IAAI,KAAK,GAAmC,IAAI,CAAC;AACjD,IAAI,aAAa,GAAG,KAAK,CAAC;AAC1B,IAAI,gBAAgB,GAAqF,IAAI,CAAC;AAE9G,MAAM,SAAS,GAA4B;IACzC,KAAK,CAAC,OAAO;QACX,OAAO,IAAI,CAAC;IACd,CAAC;IACD,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAC"}
|
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
import type { MediaFileCaptureBackend, MediaFileCaptureOptions } from '@flighthq/types/contract';
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
createWebMediaFileCaptureBackend,
|
|
5
|
-
explainMediaFileCaptureBackend,
|
|
6
|
-
getMediaFileCaptureBackend,
|
|
7
|
-
installMediaFileCaptureHostBackend,
|
|
8
|
-
observeMediaFileCaptureHostResult,
|
|
9
|
-
recordMediaFileCaptureVideo,
|
|
10
|
-
resetMediaFileCaptureBackendForTest,
|
|
11
|
-
selectMediaFileCaptureImage,
|
|
12
|
-
setMediaFileCaptureBackend,
|
|
13
|
-
takeMediaFileCapturePhoto,
|
|
14
|
-
} from './mediaFileCapture';
|
|
15
|
-
|
|
16
|
-
function fakeBackend(): MediaFileCaptureBackend & { lastOptions: MediaFileCaptureOptions | null } {
|
|
17
|
-
return {
|
|
18
|
-
lastOptions: null,
|
|
19
|
-
async capture(options) {
|
|
20
|
-
this.lastOptions = { ...options };
|
|
21
|
-
return { dataUrl: 'data:image/png;base64,xx', width: 0, height: 0, format: 'image/png' };
|
|
22
|
-
},
|
|
23
|
-
async captureVideo(options) {
|
|
24
|
-
this.lastOptions = { ...options };
|
|
25
|
-
return { dataUrl: 'data:video/mp4;base64,xx', duration: 0, format: 'video/mp4' };
|
|
26
|
-
},
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
afterEach(() => setMediaFileCaptureBackend(null));
|
|
31
|
-
|
|
32
|
-
describe('createWebMediaFileCaptureBackend', () => {
|
|
33
|
-
afterEach(() => vi.restoreAllMocks());
|
|
34
|
-
|
|
35
|
-
for (const method of ['capture', 'captureVideo'] as const) {
|
|
36
|
-
it(`${method} settles with null and detaches handlers when the picker is dismissed`, async () => {
|
|
37
|
-
const created: HTMLInputElement[] = [];
|
|
38
|
-
const createElement = document.createElement.bind(document);
|
|
39
|
-
vi.spyOn(document, 'createElement').mockImplementation(((tag: string) => {
|
|
40
|
-
const element = createElement(tag);
|
|
41
|
-
if (tag === 'input') created.push(element as HTMLInputElement);
|
|
42
|
-
return element;
|
|
43
|
-
}) as typeof document.createElement);
|
|
44
|
-
|
|
45
|
-
const pending = createWebMediaFileCaptureBackend()[method]({ source: 'photos' });
|
|
46
|
-
created[0].dispatchEvent(new Event('cancel'));
|
|
47
|
-
|
|
48
|
-
await expect(
|
|
49
|
-
Promise.race([pending, new Promise<'pending'>((resolve) => setTimeout(() => resolve('pending'), 200))]),
|
|
50
|
-
).resolves.toBeNull();
|
|
51
|
-
expect(created[0].onchange).toBeNull();
|
|
52
|
-
expect(created[0].oncancel).toBeNull();
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
it('returns a backend whose capture yields a Promise without throwing synchronously', () => {
|
|
57
|
-
const backend = createWebMediaFileCaptureBackend();
|
|
58
|
-
expect(backend.capture({}) instanceof Promise).toBe(true);
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
describe('explainMediaFileCaptureBackend', () => {
|
|
63
|
-
afterEach(() => resetMediaFileCaptureBackendForTest());
|
|
64
|
-
|
|
65
|
-
it('reports host-not-enabled when no backend is installed', () => {
|
|
66
|
-
resetMediaFileCaptureBackendForTest();
|
|
67
|
-
const explanation = explainMediaFileCaptureBackend();
|
|
68
|
-
expect(explanation.layer).toBe('host-not-enabled');
|
|
69
|
-
expect(explanation.conflict).toBe(false);
|
|
70
|
-
expect(explanation.viability).toBe('unobserved');
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
it('reports custom layer when a custom backend is set', () => {
|
|
74
|
-
setMediaFileCaptureBackend(fakeBackend());
|
|
75
|
-
expect(explainMediaFileCaptureBackend().layer).toBe('custom');
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
it('reports host layer when a host backend is installed', () => {
|
|
79
|
-
installMediaFileCaptureHostBackend(fakeBackend());
|
|
80
|
-
expect(explainMediaFileCaptureBackend().layer).toBe('host');
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('reports conflict when two different host backends are installed', () => {
|
|
84
|
-
installMediaFileCaptureHostBackend(fakeBackend());
|
|
85
|
-
installMediaFileCaptureHostBackend(fakeBackend());
|
|
86
|
-
expect(explainMediaFileCaptureBackend().conflict).toBe(true);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe('getMediaFileCaptureBackend', () => {
|
|
91
|
-
it('falls back to a web backend', () => {
|
|
92
|
-
expect(getMediaFileCaptureBackend()).not.toBeNull();
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it('returns the registered backend', () => {
|
|
96
|
-
const backend = fakeBackend();
|
|
97
|
-
setMediaFileCaptureBackend(backend);
|
|
98
|
-
expect(getMediaFileCaptureBackend()).toBe(backend);
|
|
99
|
-
});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
describe('installMediaFileCaptureHostBackend', () => {
|
|
103
|
-
afterEach(() => resetMediaFileCaptureBackendForTest());
|
|
104
|
-
|
|
105
|
-
it('installs a host backend that getMediaFileCaptureBackend returns', () => {
|
|
106
|
-
const backend = fakeBackend();
|
|
107
|
-
installMediaFileCaptureHostBackend(backend);
|
|
108
|
-
expect(getMediaFileCaptureBackend()).toBe(backend);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('is first-host-wins: a second different backend sets conflict', () => {
|
|
112
|
-
const first = fakeBackend();
|
|
113
|
-
const second = fakeBackend();
|
|
114
|
-
installMediaFileCaptureHostBackend(first);
|
|
115
|
-
installMediaFileCaptureHostBackend(second);
|
|
116
|
-
expect(getMediaFileCaptureBackend()).toBe(first);
|
|
117
|
-
expect(explainMediaFileCaptureBackend().conflict).toBe(true);
|
|
118
|
-
});
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
describe('mediaFileCapture public API boundary', () => {
|
|
122
|
-
it('web backend has exactly capture and captureVideo methods', () => {
|
|
123
|
-
const backend = createWebMediaFileCaptureBackend();
|
|
124
|
-
const methods = Object.keys(backend).sort();
|
|
125
|
-
expect(methods).toEqual(['capture', 'captureVideo']);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it('sentinel backend has no requestPermission method', () => {
|
|
129
|
-
resetMediaFileCaptureBackendForTest();
|
|
130
|
-
setMediaFileCaptureBackend(null);
|
|
131
|
-
const sentinel = getMediaFileCaptureBackend();
|
|
132
|
-
expect('requestPermission' in sentinel).toBe(false);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe('observeMediaFileCaptureHostResult', () => {
|
|
137
|
-
afterEach(() => resetMediaFileCaptureBackendForTest());
|
|
138
|
-
|
|
139
|
-
it('records a successful observation', () => {
|
|
140
|
-
installMediaFileCaptureHostBackend(fakeBackend());
|
|
141
|
-
observeMediaFileCaptureHostResult('capture', true);
|
|
142
|
-
const explanation = explainMediaFileCaptureBackend();
|
|
143
|
-
expect(explanation.operation).toBe('capture');
|
|
144
|
-
expect(explanation.viability).toBe('available');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
it('records a failed observation', () => {
|
|
148
|
-
installMediaFileCaptureHostBackend(fakeBackend());
|
|
149
|
-
observeMediaFileCaptureHostResult('capture', false);
|
|
150
|
-
expect(explainMediaFileCaptureBackend().viability).toBe('runtime-api-unavailable');
|
|
151
|
-
});
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
describe('recordMediaFileCaptureVideo', () => {
|
|
155
|
-
it('captures video with the camera source', async () => {
|
|
156
|
-
const backend = fakeBackend();
|
|
157
|
-
setMediaFileCaptureBackend(backend);
|
|
158
|
-
const video = await recordMediaFileCaptureVideo({ maxDurationMs: 5000 });
|
|
159
|
-
expect(video).not.toBeNull();
|
|
160
|
-
expect(backend.lastOptions).toEqual({ maxDurationMs: 5000, source: 'camera' });
|
|
161
|
-
});
|
|
162
|
-
|
|
163
|
-
it('returns a Promise from the web backend without throwing', () => {
|
|
164
|
-
const backend = createWebMediaFileCaptureBackend();
|
|
165
|
-
expect(backend.captureVideo({}) instanceof Promise).toBe(true);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
describe('resetMediaFileCaptureBackendForTest', () => {
|
|
170
|
-
it('clears all backend slots', () => {
|
|
171
|
-
setMediaFileCaptureBackend(fakeBackend());
|
|
172
|
-
installMediaFileCaptureHostBackend(fakeBackend());
|
|
173
|
-
observeMediaFileCaptureHostResult('capture', true);
|
|
174
|
-
resetMediaFileCaptureBackendForTest();
|
|
175
|
-
expect(explainMediaFileCaptureBackend().layer).toBe('host-not-enabled');
|
|
176
|
-
expect(explainMediaFileCaptureBackend().conflict).toBe(false);
|
|
177
|
-
expect(explainMediaFileCaptureBackend().viability).toBe('unobserved');
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
describe('selectMediaFileCaptureImage', () => {
|
|
182
|
-
it('captures with the photos source', async () => {
|
|
183
|
-
const backend = fakeBackend();
|
|
184
|
-
setMediaFileCaptureBackend(backend);
|
|
185
|
-
const photo = await selectMediaFileCaptureImage({ quality: 0.5 });
|
|
186
|
-
expect(photo).not.toBeNull();
|
|
187
|
-
expect(backend.lastOptions).toEqual({ quality: 0.5, source: 'photos' });
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
describe('setMediaFileCaptureBackend', () => {
|
|
192
|
-
it('clears back to the web fallback when passed null', () => {
|
|
193
|
-
setMediaFileCaptureBackend(fakeBackend());
|
|
194
|
-
setMediaFileCaptureBackend(null);
|
|
195
|
-
expect(getMediaFileCaptureBackend()).not.toBeNull();
|
|
196
|
-
});
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
describe('takeMediaFileCapturePhoto', () => {
|
|
200
|
-
it('captures with the camera source', async () => {
|
|
201
|
-
const backend = fakeBackend();
|
|
202
|
-
setMediaFileCaptureBackend(backend);
|
|
203
|
-
const photo = await takeMediaFileCapturePhoto({ allowEditing: true });
|
|
204
|
-
expect(photo).not.toBeNull();
|
|
205
|
-
expect(backend.lastOptions).toEqual({ allowEditing: true, source: 'camera' });
|
|
206
|
-
});
|
|
207
|
-
});
|