@flighthq/platform 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.
- package/README.md +2 -2
- 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/dist/platform.d.ts +12 -20
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +22 -116
- package/dist/platform.js.map +1 -1
- package/package.json +3 -3
- package/src/platform.test.ts +52 -470
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/platform`. The `@fli
|
|
|
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/platform@0.5.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/platform@0.5.1-edge.638.d4dbd4e](https://github.com/flighthq/flight/tree/d4dbd4e347dc8be10b180c85a766ab50333dcb51/packages/platform)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/d4dbd4e347dc8be10b180c85a766ab50333dcb51/LICENSE.md)
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { comparePlatformVersions, getPlatformEngine, getPlatformInfo, getPlatformKind, getPlatformName, getPlatformRuntime, isPlatformDesktop, isPlatformMobile, isPlatformNative, isPlatformTouch, isPlatformVersionAtLeast, isPlatformWeb, } 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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { comparePlatformVersions, getPlatformEngine, getPlatformInfo, getPlatformKind, getPlatformName, getPlatformRuntime, isPlatformDesktop, isPlatformMobile, isPlatformNative, isPlatformTouch, isPlatformVersionAtLeast, isPlatformWeb, } 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,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,aAAa,GACd,MAAM,YAAY,CAAC"}
|
package/dist/platform.d.ts
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { PlatformBackend, PlatformEngine, PlatformInfo, PlatformKind, PlatformName, PlatformRuntime } from '@flighthq/types/contract';
|
|
1
|
+
import type { HasSystemPlatform, PlatformEngine, PlatformInfo, PlatformKind, PlatformName, PlatformRuntime } from '@flighthq/types/contract';
|
|
3
2
|
export declare function comparePlatformVersions(a: string, b: string): -1 | 0 | 1;
|
|
4
3
|
export declare function createPlatformInfo(): PlatformInfo;
|
|
5
|
-
export declare function
|
|
6
|
-
export declare function
|
|
7
|
-
export declare function
|
|
8
|
-
export declare function
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function
|
|
12
|
-
export declare function
|
|
13
|
-
export declare function
|
|
14
|
-
export declare function
|
|
15
|
-
export declare function
|
|
16
|
-
export declare function isPlatformNative(): boolean;
|
|
17
|
-
export declare function isPlatformTouch(): boolean;
|
|
18
|
-
export declare function isPlatformVersionAtLeast(minimum: string): boolean;
|
|
19
|
-
export declare function isPlatformWeb(): boolean;
|
|
20
|
-
export declare function observePlatformHostResult(operation: string, succeeded: boolean): void;
|
|
21
|
-
export declare function resetPlatformBackendForTest(): void;
|
|
22
|
-
export declare function setPlatformBackend(backend: PlatformBackend | null): void;
|
|
4
|
+
export declare function getPlatformEngine(host: HasSystemPlatform): PlatformEngine;
|
|
5
|
+
export declare function getPlatformInfo(host: HasSystemPlatform, out: PlatformInfo): PlatformInfo;
|
|
6
|
+
export declare function getPlatformKind(host: HasSystemPlatform): PlatformKind;
|
|
7
|
+
export declare function getPlatformName(host: HasSystemPlatform): PlatformName;
|
|
8
|
+
export declare function getPlatformRuntime(host: HasSystemPlatform): PlatformRuntime;
|
|
9
|
+
export declare function isPlatformDesktop(host: HasSystemPlatform): boolean;
|
|
10
|
+
export declare function isPlatformMobile(host: HasSystemPlatform): boolean;
|
|
11
|
+
export declare function isPlatformNative(host: HasSystemPlatform): boolean;
|
|
12
|
+
export declare function isPlatformTouch(host: HasSystemPlatform): boolean;
|
|
13
|
+
export declare function isPlatformVersionAtLeast(host: HasSystemPlatform, minimum: string): boolean;
|
|
14
|
+
export declare function isPlatformWeb(host: HasSystemPlatform): boolean;
|
|
23
15
|
//# sourceMappingURL=platform.d.ts.map
|
package/dist/platform.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAKlC,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAcxE;AAGD,wBAAgB,kBAAkB,IAAI,YAAY,CAiBjD;AAID,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,cAAc,CAEzE;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,EAAE,GAAG,EAAE,YAAY,GAAG,YAAY,CAExF;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAErE;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,YAAY,CAErE;AAID,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,eAAe,CAE3E;AAGD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAElE;AAGD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAEjE;AAID,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAGjE;AAGD,wBAAgB,eAAe,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAEhE;AAKD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAI1F;AAGD,wBAAgB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAE9D"}
|
package/dist/platform.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { detectEndianness, parseUserAgentArch, parseUserAgentEngine, parseUserAgentEngineVersion, parseUserAgentKind, parseUserAgentName, parseUserAgentPointerWidth, parseUserAgentRuntime, parseUserAgentVersion, } from '@flighthq/useragent/contract';
|
|
2
1
|
// Compares two dotted version strings numerically, segment by segment. Returns -1, 0, or 1.
|
|
3
2
|
// Non-numeric trailing segments are ignored; '' sorts lowest. '' compared with '' returns 0.
|
|
4
3
|
// Example: comparePlatformVersions('10.15.7', '10.15.6') === 1.
|
|
@@ -39,153 +38,60 @@ export function createPlatformInfo() {
|
|
|
39
38
|
version: '',
|
|
40
39
|
};
|
|
41
40
|
}
|
|
42
|
-
// The fixed point of the suite: a host backend that identifies the running platform. Resolves to the
|
|
43
|
-
// registered native backend, or a lazily-created web backend so there is always an answer.
|
|
44
|
-
export function createWebPlatformBackend() {
|
|
45
|
-
return { getInfo: getWebPlatformInfo };
|
|
46
|
-
}
|
|
47
|
-
export function explainPlatformBackend() {
|
|
48
|
-
if (_custom !== null) {
|
|
49
|
-
return { conflict: _hostConflict, layer: 'custom', operation: null, viability: 'unobserved' };
|
|
50
|
-
}
|
|
51
|
-
if (_host !== null) {
|
|
52
|
-
return {
|
|
53
|
-
conflict: _hostConflict,
|
|
54
|
-
layer: 'host',
|
|
55
|
-
operation: _hostObservation !== null ? _hostObservation.operation : null,
|
|
56
|
-
viability: _hostObservation !== null ? _hostObservation.viability : 'unobserved',
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
return { conflict: false, layer: 'host-not-enabled', operation: null, viability: 'unobserved' };
|
|
60
|
-
}
|
|
61
|
-
// The active platform backend. Capability packages call their own get*Backend; this is the root
|
|
62
|
-
// one for environment identification.
|
|
63
|
-
export function getPlatformBackend() {
|
|
64
|
-
return _custom ?? _host ?? _sentinel;
|
|
65
|
-
}
|
|
66
41
|
// The browser rendering engine — 'blink' | 'gecko' | 'webkit' | 'unknown'. Convenience over
|
|
67
42
|
// getPlatformInfo. 'unknown' on native hosts where no browser engine is present.
|
|
68
|
-
export function getPlatformEngine() {
|
|
69
|
-
return getPlatformInfo(_scratch).engine;
|
|
43
|
+
export function getPlatformEngine(host) {
|
|
44
|
+
return getPlatformInfo(host, _scratch).engine;
|
|
70
45
|
}
|
|
71
46
|
// Fills `out` with the running platform's identity and returns it. Cheap; reads the active backend.
|
|
72
|
-
export function getPlatformInfo(out) {
|
|
73
|
-
return
|
|
47
|
+
export function getPlatformInfo(host, out) {
|
|
48
|
+
return host.system.platform.getInfo(out);
|
|
74
49
|
}
|
|
75
50
|
// The platform family — 'desktop' | 'mobile' | 'web' | 'unknown'. Convenience over getPlatformInfo.
|
|
76
|
-
export function getPlatformKind() {
|
|
77
|
-
return getPlatformInfo(_scratch).kind;
|
|
51
|
+
export function getPlatformKind(host) {
|
|
52
|
+
return getPlatformInfo(host, _scratch).kind;
|
|
78
53
|
}
|
|
79
54
|
// The specific OS/runtime name — 'windows' | 'macos' | 'ios' | 'android' | 'linux' | 'web' | 'unknown'.
|
|
80
|
-
export function getPlatformName() {
|
|
81
|
-
return getPlatformInfo(_scratch).name;
|
|
55
|
+
export function getPlatformName(host) {
|
|
56
|
+
return getPlatformInfo(host, _scratch).name;
|
|
82
57
|
}
|
|
83
58
|
// The host shell / runtime environment — 'web' | 'electron' | 'tauri' | 'capacitor' | 'native' |
|
|
84
59
|
// 'unknown'. Convenience over getPlatformInfo. Distinguishes plain web from a host shell.
|
|
85
|
-
export function getPlatformRuntime() {
|
|
86
|
-
return getPlatformInfo(_scratch).runtime;
|
|
87
|
-
}
|
|
88
|
-
export function installPlatformHostBackend(backend) {
|
|
89
|
-
if (_host !== null) {
|
|
90
|
-
if (_host !== backend)
|
|
91
|
-
_hostConflict = true;
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
_host = backend;
|
|
60
|
+
export function getPlatformRuntime(host) {
|
|
61
|
+
return getPlatformInfo(host, _scratch).runtime;
|
|
95
62
|
}
|
|
96
63
|
// True on a desktop host (Electron/Tauri/native window shell). False on mobile and plain web.
|
|
97
|
-
export function isPlatformDesktop() {
|
|
98
|
-
return getPlatformKind() === 'desktop';
|
|
64
|
+
export function isPlatformDesktop(host) {
|
|
65
|
+
return getPlatformKind(host) === 'desktop';
|
|
99
66
|
}
|
|
100
67
|
// True on a mobile host (iOS/Android via Capacitor or a native shell).
|
|
101
|
-
export function isPlatformMobile() {
|
|
102
|
-
return getPlatformKind() === 'mobile';
|
|
68
|
+
export function isPlatformMobile(host) {
|
|
69
|
+
return getPlatformKind(host) === 'mobile';
|
|
103
70
|
}
|
|
104
71
|
// True when the app is running inside a host shell (Electron/Tauri/Capacitor/native), not a plain
|
|
105
72
|
// browser page. Convenience over `getPlatformRuntime() !== 'web' && !== 'unknown'`.
|
|
106
|
-
export function isPlatformNative() {
|
|
107
|
-
const runtime = getPlatformRuntime();
|
|
73
|
+
export function isPlatformNative(host) {
|
|
74
|
+
const runtime = getPlatformRuntime(host);
|
|
108
75
|
return runtime !== 'web' && runtime !== 'unknown';
|
|
109
76
|
}
|
|
110
77
|
// True on a touch-primary device, independent of desktop/mobile classification.
|
|
111
|
-
export function isPlatformTouch() {
|
|
112
|
-
return getPlatformInfo(_scratch).isTouch;
|
|
78
|
+
export function isPlatformTouch(host) {
|
|
79
|
+
return getPlatformInfo(host, _scratch).isTouch;
|
|
113
80
|
}
|
|
114
81
|
// True when the running platform's OS version is at or above `minimum`. Reads the live version via
|
|
115
82
|
// getPlatformInfo. Returns false when the version is '' (unknown). The comparison is numeric and
|
|
116
83
|
// segment-wise (see comparePlatformVersions).
|
|
117
|
-
export function isPlatformVersionAtLeast(minimum) {
|
|
118
|
-
const version = getPlatformInfo(_scratch).version;
|
|
84
|
+
export function isPlatformVersionAtLeast(host, minimum) {
|
|
85
|
+
const version = getPlatformInfo(host, _scratch).version;
|
|
119
86
|
if (version === '')
|
|
120
87
|
return false;
|
|
121
88
|
return comparePlatformVersions(version, minimum) >= 0;
|
|
122
89
|
}
|
|
123
90
|
// True when running as a plain web page with no native host registered.
|
|
124
|
-
export function isPlatformWeb() {
|
|
125
|
-
return getPlatformKind() === 'web';
|
|
126
|
-
}
|
|
127
|
-
export function observePlatformHostResult(operation, succeeded) {
|
|
128
|
-
_hostObservation = {
|
|
129
|
-
operation,
|
|
130
|
-
viability: succeeded ? 'available' : 'runtime-api-unavailable',
|
|
131
|
-
};
|
|
91
|
+
export function isPlatformWeb(host) {
|
|
92
|
+
return getPlatformKind(host) === 'web';
|
|
132
93
|
}
|
|
133
|
-
export function resetPlatformBackendForTest() {
|
|
134
|
-
_custom = null;
|
|
135
|
-
_host = null;
|
|
136
|
-
_hostConflict = false;
|
|
137
|
-
_hostObservation = null;
|
|
138
|
-
}
|
|
139
|
-
// Installs a native host backend (Electron/Tauri/Capacitor/native). Pass null to fall back to
|
|
140
|
-
// the host or sentinel layer.
|
|
141
|
-
export function setPlatformBackend(backend) {
|
|
142
|
-
_custom = backend;
|
|
143
|
-
}
|
|
144
|
-
let _custom = null;
|
|
145
|
-
let _host = null;
|
|
146
|
-
let _hostConflict = false;
|
|
147
|
-
let _hostObservation = null;
|
|
148
94
|
// Single-threaded JS no-alloc scratch for scalar convenience reads (getPlatformKind, etc.).
|
|
149
95
|
// Rust/native mirror uses a per-call local or thread-local instead.
|
|
150
96
|
const _scratch = createPlatformInfo();
|
|
151
|
-
const _sentinel = {
|
|
152
|
-
getInfo(out) {
|
|
153
|
-
out.arch = '';
|
|
154
|
-
out.distro = '';
|
|
155
|
-
out.distroVersion = '';
|
|
156
|
-
out.endianness = 'unknown';
|
|
157
|
-
out.engine = 'unknown';
|
|
158
|
-
out.engineVersion = '';
|
|
159
|
-
out.isTouch = false;
|
|
160
|
-
out.kind = 'unknown';
|
|
161
|
-
out.locale = '';
|
|
162
|
-
out.name = 'unknown';
|
|
163
|
-
out.osBuild = '';
|
|
164
|
-
out.pointerWidth = -1;
|
|
165
|
-
out.runtime = 'unknown';
|
|
166
|
-
out.version = '';
|
|
167
|
-
return out;
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
function getWebPlatformInfo(out) {
|
|
171
|
-
const nav = typeof navigator !== 'undefined' ? navigator : null;
|
|
172
|
-
const ua = nav?.userAgent ?? '';
|
|
173
|
-
out.name = parseUserAgentName(ua);
|
|
174
|
-
out.kind = parseUserAgentKind(out.name);
|
|
175
|
-
out.version = parseUserAgentVersion(ua, out.name);
|
|
176
|
-
out.arch = parseUserAgentArch(ua);
|
|
177
|
-
out.locale = nav?.language ?? '';
|
|
178
|
-
out.isTouch =
|
|
179
|
-
typeof navigator !== 'undefined' && 'maxTouchPoints' in navigator ? navigator.maxTouchPoints > 0 : false;
|
|
180
|
-
out.runtime = parseUserAgentRuntime(typeof window !== 'undefined' ? window : null);
|
|
181
|
-
out.engine = parseUserAgentEngine(ua);
|
|
182
|
-
out.engineVersion = parseUserAgentEngineVersion(ua, out.engine);
|
|
183
|
-
out.endianness = detectEndianness();
|
|
184
|
-
out.pointerWidth = parseUserAgentPointerWidth(out.arch);
|
|
185
|
-
// osBuild, distro, distroVersion are native-only; web always returns ''.
|
|
186
|
-
out.osBuild = '';
|
|
187
|
-
out.distro = '';
|
|
188
|
-
out.distroVersion = '';
|
|
189
|
-
return out;
|
|
190
|
-
}
|
|
191
97
|
//# sourceMappingURL=platform.js.map
|
package/dist/platform.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AASA,
|
|
1
|
+
{"version":3,"file":"platform.js","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/platform",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1-edge.638.d4dbd4e",
|
|
4
4
|
"author": "Joshua Granick and other contributors",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/types": "0.5.
|
|
42
|
-
"@flighthq/useragent": "0.5.
|
|
41
|
+
"@flighthq/types": "0.5.1-edge.638.d4dbd4e",
|
|
42
|
+
"@flighthq/useragent": "0.5.1-edge.638.d4dbd4e"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"typescript": "^5.3.0"
|
package/src/platform.test.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type { PlatformBackend, PlatformInfo } from '@flighthq/types/contract';
|
|
1
|
+
import type { HasSystemPlatform, PlatformBackend, PlatformInfo } from '@flighthq/types/contract';
|
|
2
2
|
|
|
3
|
+
import * as platformContract from './platform';
|
|
3
4
|
import {
|
|
4
5
|
comparePlatformVersions,
|
|
5
6
|
createPlatformInfo,
|
|
6
|
-
createWebPlatformBackend,
|
|
7
|
-
getPlatformBackend,
|
|
8
7
|
getPlatformEngine,
|
|
9
8
|
getPlatformInfo,
|
|
10
9
|
getPlatformKind,
|
|
@@ -16,11 +15,6 @@ import {
|
|
|
16
15
|
isPlatformTouch,
|
|
17
16
|
isPlatformVersionAtLeast,
|
|
18
17
|
isPlatformWeb,
|
|
19
|
-
setPlatformBackend,
|
|
20
|
-
explainPlatformBackend,
|
|
21
|
-
installPlatformHostBackend,
|
|
22
|
-
observePlatformHostResult,
|
|
23
|
-
resetPlatformBackendForTest,
|
|
24
18
|
} from './platform';
|
|
25
19
|
|
|
26
20
|
function fakeBackend(info: Partial<PlatformInfo>): PlatformBackend {
|
|
@@ -32,7 +26,9 @@ function fakeBackend(info: Partial<PlatformInfo>): PlatformBackend {
|
|
|
32
26
|
};
|
|
33
27
|
}
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
function fakeHost(info: Partial<PlatformInfo>): HasSystemPlatform {
|
|
30
|
+
return { system: { platform: fakeBackend(info) } };
|
|
31
|
+
}
|
|
36
32
|
|
|
37
33
|
describe('comparePlatformVersions', () => {
|
|
38
34
|
it('returns 0 for identical strings', () => {
|
|
@@ -92,571 +88,157 @@ describe('createPlatformInfo', () => {
|
|
|
92
88
|
});
|
|
93
89
|
});
|
|
94
90
|
|
|
95
|
-
describe('createWebPlatformBackend', () => {
|
|
96
|
-
it('produces a backend that fills the out info', () => {
|
|
97
|
-
const out = createPlatformInfo();
|
|
98
|
-
const result = createWebPlatformBackend().getInfo(out);
|
|
99
|
-
expect(result).toBe(out);
|
|
100
|
-
expect(typeof result.name).toBe('string');
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
it('sets engine to a known canonical value', () => {
|
|
104
|
-
const out = createPlatformInfo();
|
|
105
|
-
createWebPlatformBackend().getInfo(out);
|
|
106
|
-
expect(['blink', 'gecko', 'webkit', 'unknown']).toContain(out.engine);
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('sets runtime to a known canonical value', () => {
|
|
110
|
-
const out = createPlatformInfo();
|
|
111
|
-
createWebPlatformBackend().getInfo(out);
|
|
112
|
-
expect(['web', 'electron', 'tauri', 'capacitor', 'native', 'unknown']).toContain(out.runtime);
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
it('sets kind to a known canonical value', () => {
|
|
116
|
-
const out = createPlatformInfo();
|
|
117
|
-
createWebPlatformBackend().getInfo(out);
|
|
118
|
-
expect(['desktop', 'mobile', 'web', 'unknown']).toContain(out.kind);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
it('sets name to a known canonical value', () => {
|
|
122
|
-
const out = createPlatformInfo();
|
|
123
|
-
createWebPlatformBackend().getInfo(out);
|
|
124
|
-
expect(['web', 'windows', 'macos', 'linux', 'ios', 'android', 'unknown']).toContain(out.name);
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it('sets endianness to a known canonical value', () => {
|
|
128
|
-
const out = createPlatformInfo();
|
|
129
|
-
createWebPlatformBackend().getInfo(out);
|
|
130
|
-
expect(['little', 'big', 'unknown']).toContain(out.endianness);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it('sets pointerWidth to a known canonical value', () => {
|
|
134
|
-
const out = createPlatformInfo();
|
|
135
|
-
createWebPlatformBackend().getInfo(out);
|
|
136
|
-
expect([-1, 32, 64]).toContain(out.pointerWidth);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('sets osBuild, distro, distroVersion to empty string on web', () => {
|
|
140
|
-
const out = createPlatformInfo();
|
|
141
|
-
createWebPlatformBackend().getInfo(out);
|
|
142
|
-
expect(out.osBuild).toBe('');
|
|
143
|
-
expect(out.distro).toBe('');
|
|
144
|
-
expect(out.distroVersion).toBe('');
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
describe('explainPlatformBackend', () => {
|
|
149
|
-
afterEach(() => resetPlatformBackendForTest());
|
|
150
|
-
|
|
151
|
-
it('reports host-not-enabled when no backend is installed', () => {
|
|
152
|
-
resetPlatformBackendForTest();
|
|
153
|
-
const explanation = explainPlatformBackend();
|
|
154
|
-
expect(explanation.layer).toBe('host-not-enabled');
|
|
155
|
-
expect(explanation.conflict).toBe(false);
|
|
156
|
-
expect(explanation.viability).toBe('unobserved');
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('reports custom layer when a custom backend is set', () => {
|
|
160
|
-
setPlatformBackend(fakeBackend({}));
|
|
161
|
-
expect(explainPlatformBackend().layer).toBe('custom');
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it('reports host layer when a host backend is installed', () => {
|
|
165
|
-
installPlatformHostBackend(fakeBackend({}));
|
|
166
|
-
expect(explainPlatformBackend().layer).toBe('host');
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
it('reports conflict when two different host backends are installed', () => {
|
|
170
|
-
installPlatformHostBackend(fakeBackend({}));
|
|
171
|
-
installPlatformHostBackend(fakeBackend({}));
|
|
172
|
-
expect(explainPlatformBackend().conflict).toBe(true);
|
|
173
|
-
});
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
describe('getPlatformBackend', () => {
|
|
177
|
-
it('falls back to a web backend when none is registered', () => {
|
|
178
|
-
expect(getPlatformBackend()).not.toBeNull();
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('returns the registered backend', () => {
|
|
182
|
-
const backend = fakeBackend({ kind: 'desktop', name: 'windows' });
|
|
183
|
-
setPlatformBackend(backend);
|
|
184
|
-
expect(getPlatformBackend()).toBe(backend);
|
|
185
|
-
});
|
|
186
|
-
});
|
|
187
|
-
|
|
188
91
|
describe('getPlatformEngine', () => {
|
|
189
|
-
it('returns the engine from the
|
|
190
|
-
|
|
191
|
-
expect(getPlatformEngine()).toBe('blink');
|
|
92
|
+
it('returns the engine from the host backend', () => {
|
|
93
|
+
expect(getPlatformEngine(fakeHost({ engine: 'blink' }))).toBe('blink');
|
|
192
94
|
});
|
|
193
95
|
|
|
194
96
|
it('returns gecko when set', () => {
|
|
195
|
-
|
|
196
|
-
expect(getPlatformEngine()).toBe('gecko');
|
|
97
|
+
expect(getPlatformEngine(fakeHost({ engine: 'gecko' }))).toBe('gecko');
|
|
197
98
|
});
|
|
198
99
|
|
|
199
100
|
it('returns webkit when set', () => {
|
|
200
|
-
|
|
201
|
-
expect(getPlatformEngine()).toBe('webkit');
|
|
101
|
+
expect(getPlatformEngine(fakeHost({ engine: 'webkit' }))).toBe('webkit');
|
|
202
102
|
});
|
|
203
103
|
|
|
204
104
|
it('returns unknown for native backends', () => {
|
|
205
|
-
|
|
206
|
-
expect(getPlatformEngine()).toBe('unknown');
|
|
105
|
+
expect(getPlatformEngine(fakeHost({ engine: 'unknown' }))).toBe('unknown');
|
|
207
106
|
});
|
|
208
107
|
});
|
|
209
108
|
|
|
210
109
|
describe('getPlatformInfo', () => {
|
|
211
110
|
it('fills and returns the out parameter', () => {
|
|
212
|
-
|
|
111
|
+
const host = fakeHost({ arch: 'arm64', kind: 'mobile', name: 'ios' });
|
|
213
112
|
const out = createPlatformInfo();
|
|
214
|
-
expect(getPlatformInfo(out)).toBe(out);
|
|
113
|
+
expect(getPlatformInfo(host, out)).toBe(out);
|
|
215
114
|
expect(out.name).toBe('ios');
|
|
216
115
|
expect(out.arch).toBe('arm64');
|
|
217
116
|
});
|
|
218
117
|
});
|
|
219
118
|
|
|
220
119
|
describe('getPlatformKind', () => {
|
|
221
|
-
it('returns the
|
|
222
|
-
|
|
223
|
-
expect(getPlatformKind()).toBe('desktop');
|
|
120
|
+
it('returns the host backend kind', () => {
|
|
121
|
+
expect(getPlatformKind(fakeHost({ kind: 'desktop' }))).toBe('desktop');
|
|
224
122
|
});
|
|
225
123
|
});
|
|
226
124
|
|
|
227
125
|
describe('getPlatformName', () => {
|
|
228
|
-
it('returns the
|
|
229
|
-
|
|
230
|
-
expect(getPlatformName()).toBe('macos');
|
|
126
|
+
it('returns the host backend name', () => {
|
|
127
|
+
expect(getPlatformName(fakeHost({ name: 'macos' }))).toBe('macos');
|
|
231
128
|
});
|
|
232
129
|
});
|
|
233
130
|
|
|
234
131
|
describe('getPlatformRuntime', () => {
|
|
235
132
|
it('returns web when no host shell is detected', () => {
|
|
236
|
-
|
|
237
|
-
expect(getPlatformRuntime()).toBe('web');
|
|
133
|
+
expect(getPlatformRuntime(fakeHost({ runtime: 'web' }))).toBe('web');
|
|
238
134
|
});
|
|
239
135
|
|
|
240
136
|
it('returns electron when set', () => {
|
|
241
|
-
|
|
242
|
-
expect(getPlatformRuntime()).toBe('electron');
|
|
137
|
+
expect(getPlatformRuntime(fakeHost({ runtime: 'electron' }))).toBe('electron');
|
|
243
138
|
});
|
|
244
139
|
|
|
245
140
|
it('returns tauri when set', () => {
|
|
246
|
-
|
|
247
|
-
expect(getPlatformRuntime()).toBe('tauri');
|
|
141
|
+
expect(getPlatformRuntime(fakeHost({ runtime: 'tauri' }))).toBe('tauri');
|
|
248
142
|
});
|
|
249
143
|
|
|
250
144
|
it('returns capacitor when set', () => {
|
|
251
|
-
|
|
252
|
-
expect(getPlatformRuntime()).toBe('capacitor');
|
|
145
|
+
expect(getPlatformRuntime(fakeHost({ runtime: 'capacitor' }))).toBe('capacitor');
|
|
253
146
|
});
|
|
254
147
|
|
|
255
148
|
it('returns native when set by a native backend', () => {
|
|
256
|
-
|
|
257
|
-
expect(getPlatformRuntime()).toBe('native');
|
|
258
|
-
});
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
describe('installPlatformHostBackend', () => {
|
|
262
|
-
afterEach(() => resetPlatformBackendForTest());
|
|
263
|
-
|
|
264
|
-
it('installs a host backend that getPlatformBackend returns', () => {
|
|
265
|
-
const backend = fakeBackend({});
|
|
266
|
-
installPlatformHostBackend(backend);
|
|
267
|
-
expect(getPlatformBackend()).toBe(backend);
|
|
268
|
-
});
|
|
269
|
-
|
|
270
|
-
it('is first-host-wins: a second different backend sets conflict', () => {
|
|
271
|
-
const first = fakeBackend({});
|
|
272
|
-
const second = fakeBackend({});
|
|
273
|
-
installPlatformHostBackend(first);
|
|
274
|
-
installPlatformHostBackend(second);
|
|
275
|
-
expect(getPlatformBackend()).toBe(first);
|
|
276
|
-
expect(explainPlatformBackend().conflict).toBe(true);
|
|
149
|
+
expect(getPlatformRuntime(fakeHost({ runtime: 'native' }))).toBe('native');
|
|
277
150
|
});
|
|
278
151
|
});
|
|
279
152
|
|
|
280
153
|
describe('isPlatformDesktop', () => {
|
|
281
154
|
it('is true only for desktop kind', () => {
|
|
282
|
-
|
|
283
|
-
expect(isPlatformDesktop()).toBe(
|
|
284
|
-
setPlatformBackend(fakeBackend({ kind: 'web' }));
|
|
285
|
-
expect(isPlatformDesktop()).toBe(false);
|
|
155
|
+
expect(isPlatformDesktop(fakeHost({ kind: 'desktop' }))).toBe(true);
|
|
156
|
+
expect(isPlatformDesktop(fakeHost({ kind: 'web' }))).toBe(false);
|
|
286
157
|
});
|
|
287
158
|
});
|
|
288
159
|
|
|
289
160
|
describe('isPlatformMobile', () => {
|
|
290
161
|
it('is true only for mobile kind', () => {
|
|
291
|
-
|
|
292
|
-
expect(isPlatformMobile()).toBe(true);
|
|
162
|
+
expect(isPlatformMobile(fakeHost({ kind: 'mobile' }))).toBe(true);
|
|
293
163
|
});
|
|
294
164
|
});
|
|
295
165
|
|
|
296
166
|
describe('isPlatformNative', () => {
|
|
297
167
|
it('is true for electron runtime', () => {
|
|
298
|
-
|
|
299
|
-
expect(isPlatformNative()).toBe(true);
|
|
168
|
+
expect(isPlatformNative(fakeHost({ runtime: 'electron' }))).toBe(true);
|
|
300
169
|
});
|
|
301
170
|
|
|
302
171
|
it('is true for tauri runtime', () => {
|
|
303
|
-
|
|
304
|
-
expect(isPlatformNative()).toBe(true);
|
|
172
|
+
expect(isPlatformNative(fakeHost({ runtime: 'tauri' }))).toBe(true);
|
|
305
173
|
});
|
|
306
174
|
|
|
307
175
|
it('is true for capacitor runtime', () => {
|
|
308
|
-
|
|
309
|
-
expect(isPlatformNative()).toBe(true);
|
|
176
|
+
expect(isPlatformNative(fakeHost({ runtime: 'capacitor' }))).toBe(true);
|
|
310
177
|
});
|
|
311
178
|
|
|
312
179
|
it('is true for native runtime', () => {
|
|
313
|
-
|
|
314
|
-
expect(isPlatformNative()).toBe(true);
|
|
180
|
+
expect(isPlatformNative(fakeHost({ runtime: 'native' }))).toBe(true);
|
|
315
181
|
});
|
|
316
182
|
|
|
317
183
|
it('is false for web runtime', () => {
|
|
318
|
-
|
|
319
|
-
expect(isPlatformNative()).toBe(false);
|
|
184
|
+
expect(isPlatformNative(fakeHost({ runtime: 'web' }))).toBe(false);
|
|
320
185
|
});
|
|
321
186
|
|
|
322
187
|
it('is false for unknown runtime', () => {
|
|
323
|
-
|
|
324
|
-
expect(isPlatformNative()).toBe(false);
|
|
188
|
+
expect(isPlatformNative(fakeHost({ runtime: 'unknown' }))).toBe(false);
|
|
325
189
|
});
|
|
326
190
|
});
|
|
327
191
|
|
|
328
192
|
describe('isPlatformTouch', () => {
|
|
329
193
|
it('reflects the backend isTouch flag', () => {
|
|
330
|
-
|
|
331
|
-
expect(isPlatformTouch()).toBe(true);
|
|
194
|
+
expect(isPlatformTouch(fakeHost({ isTouch: true }))).toBe(true);
|
|
332
195
|
});
|
|
333
196
|
});
|
|
334
197
|
|
|
335
198
|
describe('isPlatformVersionAtLeast', () => {
|
|
336
199
|
it('is true when version equals minimum', () => {
|
|
337
|
-
|
|
338
|
-
expect(isPlatformVersionAtLeast('14.0')).toBe(true);
|
|
200
|
+
expect(isPlatformVersionAtLeast(fakeHost({ version: '14.0' }), '14.0')).toBe(true);
|
|
339
201
|
});
|
|
340
202
|
|
|
341
203
|
it('is true when version exceeds minimum', () => {
|
|
342
|
-
|
|
343
|
-
expect(isPlatformVersionAtLeast('14.0')).toBe(true);
|
|
204
|
+
expect(isPlatformVersionAtLeast(fakeHost({ version: '15.0' }), '14.0')).toBe(true);
|
|
344
205
|
});
|
|
345
206
|
|
|
346
207
|
it('is false when version is below minimum', () => {
|
|
347
|
-
|
|
348
|
-
expect(isPlatformVersionAtLeast('14.0')).toBe(false);
|
|
208
|
+
expect(isPlatformVersionAtLeast(fakeHost({ version: '13.0' }), '14.0')).toBe(false);
|
|
349
209
|
});
|
|
350
210
|
|
|
351
211
|
it('is false when version is empty (unknown)', () => {
|
|
352
|
-
|
|
353
|
-
expect(isPlatformVersionAtLeast('1.0')).toBe(false);
|
|
212
|
+
expect(isPlatformVersionAtLeast(fakeHost({ version: '' }), '1.0')).toBe(false);
|
|
354
213
|
});
|
|
355
214
|
|
|
356
215
|
it('handles patch-level comparison', () => {
|
|
357
|
-
|
|
358
|
-
expect(isPlatformVersionAtLeast('10.15.
|
|
359
|
-
expect(isPlatformVersionAtLeast('10.15.7')).toBe(
|
|
360
|
-
expect(isPlatformVersionAtLeast('10.15.8')).toBe(false);
|
|
216
|
+
expect(isPlatformVersionAtLeast(fakeHost({ version: '10.15.7' }), '10.15.6')).toBe(true);
|
|
217
|
+
expect(isPlatformVersionAtLeast(fakeHost({ version: '10.15.7' }), '10.15.7')).toBe(true);
|
|
218
|
+
expect(isPlatformVersionAtLeast(fakeHost({ version: '10.15.7' }), '10.15.8')).toBe(false);
|
|
361
219
|
});
|
|
362
220
|
});
|
|
363
221
|
|
|
364
222
|
describe('isPlatformWeb', () => {
|
|
365
223
|
it('is true only for web kind', () => {
|
|
366
|
-
|
|
367
|
-
expect(isPlatformWeb()).toBe(true);
|
|
368
|
-
});
|
|
369
|
-
});
|
|
370
|
-
|
|
371
|
-
describe('observePlatformHostResult', () => {
|
|
372
|
-
afterEach(() => resetPlatformBackendForTest());
|
|
373
|
-
|
|
374
|
-
it('records a successful observation', () => {
|
|
375
|
-
installPlatformHostBackend(fakeBackend({}));
|
|
376
|
-
observePlatformHostResult('getInfo', true);
|
|
377
|
-
const explanation = explainPlatformBackend();
|
|
378
|
-
expect(explanation.operation).toBe('getInfo');
|
|
379
|
-
expect(explanation.viability).toBe('available');
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
it('records a failed observation', () => {
|
|
383
|
-
installPlatformHostBackend(fakeBackend({}));
|
|
384
|
-
observePlatformHostResult('getInfo', false);
|
|
385
|
-
expect(explainPlatformBackend().viability).toBe('runtime-api-unavailable');
|
|
386
|
-
});
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
describe('resetPlatformBackendForTest', () => {
|
|
390
|
-
it('clears all backend slots', () => {
|
|
391
|
-
setPlatformBackend(fakeBackend({}));
|
|
392
|
-
installPlatformHostBackend(fakeBackend({}));
|
|
393
|
-
observePlatformHostResult('getInfo', true);
|
|
394
|
-
resetPlatformBackendForTest();
|
|
395
|
-
expect(explainPlatformBackend().layer).toBe('host-not-enabled');
|
|
396
|
-
expect(explainPlatformBackend().conflict).toBe(false);
|
|
397
|
-
expect(explainPlatformBackend().viability).toBe('unobserved');
|
|
224
|
+
expect(isPlatformWeb(fakeHost({ kind: 'web' }))).toBe(true);
|
|
398
225
|
});
|
|
399
226
|
});
|
|
400
227
|
|
|
401
|
-
describe('
|
|
402
|
-
it('
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
// Helper: temporarily replace navigator.userAgent for a block (jsdom only).
|
|
413
|
-
function withUserAgent(ua: string, fn: () => void): void {
|
|
414
|
-
const original = navigator.userAgent;
|
|
415
|
-
Object.defineProperty(navigator, 'userAgent', { configurable: true, value: ua });
|
|
416
|
-
try {
|
|
417
|
-
fn();
|
|
418
|
-
} finally {
|
|
419
|
-
Object.defineProperty(navigator, 'userAgent', { configurable: true, value: original });
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
describe('arch detection', () => {
|
|
424
|
-
it('detects arm64 from aarch64', () => {
|
|
425
|
-
withUserAgent('Mozilla/5.0 (Linux; aarch64) AppleWebKit/537.36', () => {
|
|
426
|
-
const out = createPlatformInfo();
|
|
427
|
-
createWebPlatformBackend().getInfo(out);
|
|
428
|
-
expect(out.arch).toBe('arm64');
|
|
429
|
-
});
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
it('detects arm64 from arm64 token', () => {
|
|
433
|
-
withUserAgent('Mozilla/5.0 (iPhone; CPU arm64)', () => {
|
|
434
|
-
const out = createPlatformInfo();
|
|
435
|
-
createWebPlatformBackend().getInfo(out);
|
|
436
|
-
expect(out.arch).toBe('arm64');
|
|
437
|
-
});
|
|
438
|
-
});
|
|
439
|
-
|
|
440
|
-
it('detects x64 from Win64', () => {
|
|
441
|
-
withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', () => {
|
|
442
|
-
const out = createPlatformInfo();
|
|
443
|
-
createWebPlatformBackend().getInfo(out);
|
|
444
|
-
expect(out.arch).toBe('x64');
|
|
445
|
-
});
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
it('detects x64 from WOW64', () => {
|
|
449
|
-
withUserAgent('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36', () => {
|
|
450
|
-
const out = createPlatformInfo();
|
|
451
|
-
createWebPlatformBackend().getInfo(out);
|
|
452
|
-
expect(out.arch).toBe('x64');
|
|
453
|
-
});
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
it('returns empty string when arch is undetectable', () => {
|
|
457
|
-
withUserAgent('Mozilla/5.0 (X11; Linux) AppleWebKit/537.36', () => {
|
|
458
|
-
const out = createPlatformInfo();
|
|
459
|
-
createWebPlatformBackend().getInfo(out);
|
|
460
|
-
expect(out.arch).toBe('');
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
describe('canonical token normalization', () => {
|
|
466
|
-
const KNOWN_NAMES = ['web', 'windows', 'macos', 'linux', 'ios', 'android', 'unknown'];
|
|
467
|
-
const KNOWN_KINDS = ['desktop', 'mobile', 'web', 'unknown'];
|
|
468
|
-
const KNOWN_RUNTIMES = ['web', 'electron', 'tauri', 'capacitor', 'native', 'unknown'];
|
|
469
|
-
const KNOWN_ENGINES = ['blink', 'gecko', 'webkit', 'unknown'];
|
|
470
|
-
const KNOWN_ENDIANNESSES = ['little', 'big', 'unknown'];
|
|
471
|
-
const KNOWN_POINTER_WIDTHS = [-1, 32, 64];
|
|
472
|
-
|
|
473
|
-
const TEST_UAS = [
|
|
474
|
-
// Windows Chrome
|
|
475
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
476
|
-
// macOS Safari
|
|
477
|
-
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15',
|
|
478
|
-
// Firefox
|
|
479
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0',
|
|
480
|
-
// iOS Safari
|
|
481
|
-
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X) AppleWebKit/605.1.15',
|
|
482
|
-
// Android Chrome
|
|
483
|
-
'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36',
|
|
228
|
+
describe('R3 boundary', () => {
|
|
229
|
+
it('exports no ambient-state API (setPlatformBackend, getPlatformBackend, etc.)', () => {
|
|
230
|
+
const exports = Object.keys(platformContract);
|
|
231
|
+
const deletedSymbols = [
|
|
232
|
+
'createWebPlatformBackend',
|
|
233
|
+
'explainPlatformBackend',
|
|
234
|
+
'getPlatformBackend',
|
|
235
|
+
'installPlatformHostBackend',
|
|
236
|
+
'observePlatformHostResult',
|
|
237
|
+
'resetPlatformBackendForTest',
|
|
238
|
+
'setPlatformBackend',
|
|
484
239
|
];
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
it(`only emits known canonical tokens for UA: ${ua.slice(0, 60)}…`, () => {
|
|
488
|
-
withUserAgent(ua, () => {
|
|
489
|
-
const out = createPlatformInfo();
|
|
490
|
-
createWebPlatformBackend().getInfo(out);
|
|
491
|
-
expect(KNOWN_NAMES).toContain(out.name);
|
|
492
|
-
expect(KNOWN_KINDS).toContain(out.kind);
|
|
493
|
-
expect(KNOWN_RUNTIMES).toContain(out.runtime);
|
|
494
|
-
expect(KNOWN_ENGINES).toContain(out.engine);
|
|
495
|
-
expect(KNOWN_ENDIANNESSES).toContain(out.endianness);
|
|
496
|
-
expect(KNOWN_POINTER_WIDTHS).toContain(out.pointerWidth);
|
|
497
|
-
expect(typeof out.version).toBe('string');
|
|
498
|
-
expect(typeof out.engineVersion).toBe('string');
|
|
499
|
-
expect(typeof out.arch).toBe('string');
|
|
500
|
-
});
|
|
501
|
-
});
|
|
240
|
+
for (const symbol of deletedSymbols) {
|
|
241
|
+
expect(exports).not.toContain(symbol);
|
|
502
242
|
}
|
|
503
243
|
});
|
|
504
|
-
|
|
505
|
-
describe('endianness detection', () => {
|
|
506
|
-
it('returns a known canonical value', () => {
|
|
507
|
-
const out = createPlatformInfo();
|
|
508
|
-
createWebPlatformBackend().getInfo(out);
|
|
509
|
-
expect(['little', 'big', 'unknown']).toContain(out.endianness);
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
it('returns little on jsdom (x64 host)', () => {
|
|
513
|
-
// jsdom runs on Node.js on x64 hardware — always little-endian.
|
|
514
|
-
const out = createPlatformInfo();
|
|
515
|
-
createWebPlatformBackend().getInfo(out);
|
|
516
|
-
expect(out.endianness).toBe('little');
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
describe('engine detection', () => {
|
|
521
|
-
it('detects blink from Chrome UA', () => {
|
|
522
|
-
withUserAgent(
|
|
523
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
524
|
-
() => {
|
|
525
|
-
const out = createPlatformInfo();
|
|
526
|
-
createWebPlatformBackend().getInfo(out);
|
|
527
|
-
expect(out.engine).toBe('blink');
|
|
528
|
-
},
|
|
529
|
-
);
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
it('detects gecko from Firefox UA', () => {
|
|
533
|
-
withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0', () => {
|
|
534
|
-
const out = createPlatformInfo();
|
|
535
|
-
createWebPlatformBackend().getInfo(out);
|
|
536
|
-
expect(out.engine).toBe('gecko');
|
|
537
|
-
});
|
|
538
|
-
});
|
|
539
|
-
|
|
540
|
-
it('detects webkit from Safari UA', () => {
|
|
541
|
-
withUserAgent(
|
|
542
|
-
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15',
|
|
543
|
-
() => {
|
|
544
|
-
const out = createPlatformInfo();
|
|
545
|
-
createWebPlatformBackend().getInfo(out);
|
|
546
|
-
expect(out.engine).toBe('webkit');
|
|
547
|
-
},
|
|
548
|
-
);
|
|
549
|
-
});
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
describe('engineVersion detection', () => {
|
|
553
|
-
it('extracts Firefox version', () => {
|
|
554
|
-
withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0', () => {
|
|
555
|
-
const out = createPlatformInfo();
|
|
556
|
-
createWebPlatformBackend().getInfo(out);
|
|
557
|
-
expect(out.engineVersion).toBe('120.0');
|
|
558
|
-
});
|
|
559
|
-
});
|
|
560
|
-
|
|
561
|
-
it('extracts Chrome version', () => {
|
|
562
|
-
withUserAgent(
|
|
563
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.109 Safari/537.36',
|
|
564
|
-
() => {
|
|
565
|
-
const out = createPlatformInfo();
|
|
566
|
-
createWebPlatformBackend().getInfo(out);
|
|
567
|
-
expect(out.engineVersion).toBe('120.0.6099.109');
|
|
568
|
-
},
|
|
569
|
-
);
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
it('extracts Edge version (Edg/ token)', () => {
|
|
573
|
-
withUserAgent(
|
|
574
|
-
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.2210.133',
|
|
575
|
-
() => {
|
|
576
|
-
const out = createPlatformInfo();
|
|
577
|
-
createWebPlatformBackend().getInfo(out);
|
|
578
|
-
expect(out.engineVersion).toBe('120.0.2210.133');
|
|
579
|
-
},
|
|
580
|
-
);
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
it('extracts Safari version from Version/ token', () => {
|
|
584
|
-
withUserAgent(
|
|
585
|
-
'Mozilla/5.0 (Macintosh; Intel Mac OS X 13_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Safari/605.1.15',
|
|
586
|
-
() => {
|
|
587
|
-
const out = createPlatformInfo();
|
|
588
|
-
createWebPlatformBackend().getInfo(out);
|
|
589
|
-
expect(out.engineVersion).toBe('16.0');
|
|
590
|
-
},
|
|
591
|
-
);
|
|
592
|
-
});
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
describe('pointerWidth detection', () => {
|
|
596
|
-
it('returns 64 for x64 arch', () => {
|
|
597
|
-
withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36', () => {
|
|
598
|
-
const out = createPlatformInfo();
|
|
599
|
-
createWebPlatformBackend().getInfo(out);
|
|
600
|
-
expect(out.pointerWidth).toBe(64);
|
|
601
|
-
});
|
|
602
|
-
});
|
|
603
|
-
|
|
604
|
-
it('returns 64 for arm64 arch', () => {
|
|
605
|
-
withUserAgent('Mozilla/5.0 (Linux; aarch64) AppleWebKit/537.36', () => {
|
|
606
|
-
const out = createPlatformInfo();
|
|
607
|
-
createWebPlatformBackend().getInfo(out);
|
|
608
|
-
expect(out.pointerWidth).toBe(64);
|
|
609
|
-
});
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
it('returns -1 when arch is undetectable', () => {
|
|
613
|
-
withUserAgent('Mozilla/5.0 (X11; Linux) AppleWebKit/537.36', () => {
|
|
614
|
-
const out = createPlatformInfo();
|
|
615
|
-
createWebPlatformBackend().getInfo(out);
|
|
616
|
-
expect(out.pointerWidth).toBe(-1);
|
|
617
|
-
});
|
|
618
|
-
});
|
|
619
|
-
});
|
|
620
|
-
|
|
621
|
-
describe('version detection', () => {
|
|
622
|
-
it('parses Windows version from NT string', () => {
|
|
623
|
-
withUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64)', () => {
|
|
624
|
-
const out = createPlatformInfo();
|
|
625
|
-
createWebPlatformBackend().getInfo(out);
|
|
626
|
-
expect(out.version).toBe('10.0');
|
|
627
|
-
});
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
it('parses macOS version with underscore separators', () => {
|
|
631
|
-
withUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)', () => {
|
|
632
|
-
const out = createPlatformInfo();
|
|
633
|
-
createWebPlatformBackend().getInfo(out);
|
|
634
|
-
expect(out.version).toBe('10.15.7');
|
|
635
|
-
});
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
it('parses iOS version', () => {
|
|
639
|
-
withUserAgent('Mozilla/5.0 (iPhone; CPU iPhone OS 17_4_1 like Mac OS X)', () => {
|
|
640
|
-
const out = createPlatformInfo();
|
|
641
|
-
createWebPlatformBackend().getInfo(out);
|
|
642
|
-
expect(out.version).toBe('17.4.1');
|
|
643
|
-
});
|
|
644
|
-
});
|
|
645
|
-
|
|
646
|
-
it('parses Android version', () => {
|
|
647
|
-
withUserAgent('Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36', () => {
|
|
648
|
-
const out = createPlatformInfo();
|
|
649
|
-
createWebPlatformBackend().getInfo(out);
|
|
650
|
-
expect(out.version).toBe('14');
|
|
651
|
-
});
|
|
652
|
-
});
|
|
653
|
-
|
|
654
|
-
it('returns empty string when version is undetectable', () => {
|
|
655
|
-
withUserAgent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36', () => {
|
|
656
|
-
const out = createPlatformInfo();
|
|
657
|
-
createWebPlatformBackend().getInfo(out);
|
|
658
|
-
expect(out.version).toBe('');
|
|
659
|
-
});
|
|
660
|
-
});
|
|
661
|
-
});
|
|
662
244
|
});
|