@flighthq/geolocation 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/contract.d.ts +1 -0
- package/dist/contract.d.ts.map +1 -1
- package/dist/contract.js +1 -0
- package/dist/contract.js.map +1 -1
- package/dist/geolocation.d.ts +1 -4
- package/dist/geolocation.d.ts.map +1 -1
- package/dist/geolocation.js +32 -65
- package/dist/geolocation.js.map +1 -1
- package/dist/geolocationAccess.d.ts +14 -0
- package/dist/geolocationAccess.d.ts.map +1 -0
- package/dist/geolocationAccess.js +24 -0
- package/dist/geolocationAccess.js.map +1 -0
- 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 -2
- package/src/geolocation.test.ts +38 -85
- package/src/geolocationAccess.test.ts +56 -0
package/README.md
CHANGED
|
@@ -13,5 +13,5 @@ Import the supported application-facing API from `@flighthq/geolocation`. The `@
|
|
|
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/geolocation@0.5.
|
|
17
|
-
- [License](https://github.com/flighthq/flight/blob/
|
|
16
|
+
- [Source for @flighthq/geolocation@0.5.1-edge.638.d4dbd4e](https://github.com/flighthq/flight/tree/d4dbd4e347dc8be10b180c85a766ab50333dcb51/packages/geolocation)
|
|
17
|
+
- [License](https://github.com/flighthq/flight/blob/d4dbd4e347dc8be10b180c85a766ab50333dcb51/LICENSE.md)
|
package/dist/contract.d.ts
CHANGED
package/dist/contract.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
|
package/dist/contract.js
CHANGED
package/dist/contract.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,qBAAqB,CAAC"}
|
package/dist/geolocation.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { BackendExplanation } from '@flighthq/types/contract';
|
|
2
|
-
import type { GeolocationBackend, GeolocationErrorReason,
|
|
2
|
+
import type { GeolocationBackend, GeolocationErrorReason, GeolocationRequestOptions, GeoPosition, GeoPositionResult } from '@flighthq/types/contract';
|
|
3
3
|
export declare function clearGeolocationWatch(id: number): void;
|
|
4
4
|
export declare function createGeoPosition(): GeoPosition;
|
|
5
5
|
export declare function createWebGeolocationBackend(): GeolocationBackend;
|
|
@@ -7,12 +7,9 @@ export declare function explainGeolocationBackend(): BackendExplanation;
|
|
|
7
7
|
export declare function getCurrentGeoPosition(options?: Readonly<GeolocationRequestOptions>): Promise<GeoPosition | null>;
|
|
8
8
|
export declare function getCurrentGeoPositionResult(options?: Readonly<GeolocationRequestOptions>): Promise<GeoPositionResult>;
|
|
9
9
|
export declare function getGeolocationBackend(): GeolocationBackend;
|
|
10
|
-
export declare function getGeolocationPermission(): Promise<GeolocationPermissionState>;
|
|
11
10
|
export declare function installGeolocationHostBackend(backend: GeolocationBackend): void;
|
|
12
11
|
export declare function isGeolocationAvailable(): boolean;
|
|
13
12
|
export declare function observeGeolocationHostResult(operation: string, succeeded: boolean): void;
|
|
14
|
-
export declare function onGeolocationPermissionChange(listener: (state: GeolocationPermissionState) => void): () => void;
|
|
15
|
-
export declare function requestGeolocationPermission(): Promise<boolean>;
|
|
16
13
|
export declare function resetGeolocationBackendForTest(): void;
|
|
17
14
|
export declare function setGeolocationBackend(backend: GeolocationBackend | null): void;
|
|
18
15
|
export declare function watchGeolocationPosition(handler: (position: Readonly<GeoPosition>) => void, options?: Readonly<GeolocationRequestOptions>, onError?: (reason: GeolocationErrorReason) => void): number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geolocation.d.ts","sourceRoot":"","sources":["../src/geolocation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"geolocation.d.ts","sourceRoot":"","sources":["../src/geolocation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAEV,kBAAkB,EAClB,sBAAsB,EACtB,yBAAyB,EACzB,WAAW,EACX,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,qBAAqB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAEtD;AAED,wBAAgB,iBAAiB,IAAI,WAAW,CAY/C;AAKD,wBAAgB,2BAA2B,IAAI,kBAAkB,CAsFhE;AAED,wBAAgB,yBAAyB,IAAI,kBAAkB,CAa9D;AAED,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,yBAAyB,CAAC,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAEhH;AAED,wBAAgB,2BAA2B,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,yBAAyB,CAAC,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAErH;AAED,wBAAgB,qBAAqB,IAAI,kBAAkB,CAE1D;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAM/E;AAED,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,CAKxF;AAED,wBAAgB,8BAA8B,IAAI,IAAI,CAKrD;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,GAAG,IAAI,CAE9E;AAED,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,WAAW,CAAC,KAAK,IAAI,EAClD,OAAO,CAAC,EAAE,QAAQ,CAAC,yBAAyB,CAAC,EAC7C,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,sBAAsB,KAAK,IAAI,GACjD,MAAM,CAER"}
|
package/dist/geolocation.js
CHANGED
|
@@ -60,55 +60,29 @@ export function createWebGeolocationBackend() {
|
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
},
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
const status = await permissions.query({ name: 'geolocation' });
|
|
68
|
-
return status.state;
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// Fall through to prompt default.
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
return 'prompt';
|
|
63
|
+
isAvailable() {
|
|
64
|
+
if (typeof window !== 'undefined' && window.isSecureContext === false)
|
|
65
|
+
return false;
|
|
66
|
+
return getWebGeolocation() !== null;
|
|
75
67
|
},
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
68
|
+
// The web has no permission-request API: the prompt is raised as a side effect of a position
|
|
69
|
+
// read. Code 1 is a refusal; code 3 is an acquisition TIMEOUT and says nothing about the user, so
|
|
70
|
+
// it is reported as such rather than guessed at. 'dismissed' is reachable only on hosts that can
|
|
71
|
+
// actually observe a closed-undecided prompt, which the web cannot.
|
|
72
|
+
promptForAccess() {
|
|
73
|
+
return new Promise((resolve) => {
|
|
74
|
+
const geo = getWebGeolocation();
|
|
75
|
+
if (geo === null || typeof geo.getCurrentPosition !== 'function') {
|
|
76
|
+
resolve({ reason: 'runtime-unavailable' });
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
79
|
try {
|
|
80
|
-
|
|
81
|
-
return status.state === 'granted';
|
|
80
|
+
geo.getCurrentPosition(() => resolve({ reason: 'granted' }), (error) => resolve({ reason: mapWebAccessError(error) }));
|
|
82
81
|
}
|
|
83
82
|
catch {
|
|
84
|
-
|
|
83
|
+
resolve({ reason: 'operation-failed' });
|
|
85
84
|
}
|
|
86
|
-
}
|
|
87
|
-
return (await this.getCurrentPosition({})) !== null;
|
|
88
|
-
},
|
|
89
|
-
subscribePermission(listener) {
|
|
90
|
-
const permissions = typeof navigator !== 'undefined' ? (navigator.permissions ?? null) : null;
|
|
91
|
-
if (permissions === null || typeof permissions.query !== 'function')
|
|
92
|
-
return _noopUnsubscribe;
|
|
93
|
-
let status = null;
|
|
94
|
-
let handler = null;
|
|
95
|
-
permissions
|
|
96
|
-
.query({ name: 'geolocation' })
|
|
97
|
-
.then((s) => {
|
|
98
|
-
status = s;
|
|
99
|
-
handler = () => listener(s.state);
|
|
100
|
-
s.addEventListener('change', handler);
|
|
101
|
-
})
|
|
102
|
-
.catch(() => {
|
|
103
|
-
// Permissions API unavailable; subscription is a no-op.
|
|
104
85
|
});
|
|
105
|
-
return () => {
|
|
106
|
-
if (status !== null && handler !== null) {
|
|
107
|
-
status.removeEventListener('change', handler);
|
|
108
|
-
status = null;
|
|
109
|
-
handler = null;
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
86
|
},
|
|
113
87
|
watchPosition(listener, options, onError) {
|
|
114
88
|
const geo = getWebGeolocation();
|
|
@@ -146,9 +120,6 @@ export function getCurrentGeoPositionResult(options) {
|
|
|
146
120
|
export function getGeolocationBackend() {
|
|
147
121
|
return _custom ?? _host ?? _sentinel;
|
|
148
122
|
}
|
|
149
|
-
export function getGeolocationPermission() {
|
|
150
|
-
return getGeolocationBackend().getPermission();
|
|
151
|
-
}
|
|
152
123
|
export function installGeolocationHostBackend(backend) {
|
|
153
124
|
if (_host !== null) {
|
|
154
125
|
if (_host !== backend)
|
|
@@ -158,11 +129,7 @@ export function installGeolocationHostBackend(backend) {
|
|
|
158
129
|
_host = backend;
|
|
159
130
|
}
|
|
160
131
|
export function isGeolocationAvailable() {
|
|
161
|
-
|
|
162
|
-
return false;
|
|
163
|
-
if (typeof window !== 'undefined' && window.isSecureContext === false)
|
|
164
|
-
return false;
|
|
165
|
-
return typeof navigator.geolocation !== 'undefined' && navigator.geolocation !== null;
|
|
132
|
+
return getGeolocationBackend().isAvailable();
|
|
166
133
|
}
|
|
167
134
|
export function observeGeolocationHostResult(operation, succeeded) {
|
|
168
135
|
_hostObservation = {
|
|
@@ -170,12 +137,6 @@ export function observeGeolocationHostResult(operation, succeeded) {
|
|
|
170
137
|
viability: succeeded ? 'available' : 'runtime-api-unavailable',
|
|
171
138
|
};
|
|
172
139
|
}
|
|
173
|
-
export function onGeolocationPermissionChange(listener) {
|
|
174
|
-
return getGeolocationBackend().subscribePermission(listener);
|
|
175
|
-
}
|
|
176
|
-
export function requestGeolocationPermission() {
|
|
177
|
-
return getGeolocationBackend().requestPermission();
|
|
178
|
-
}
|
|
179
140
|
export function resetGeolocationBackendForTest() {
|
|
180
141
|
_custom = null;
|
|
181
142
|
_host = null;
|
|
@@ -193,7 +154,6 @@ let _host = null;
|
|
|
193
154
|
let _hostConflict = false;
|
|
194
155
|
let _hostObservation = null;
|
|
195
156
|
const _emptyOptions = {};
|
|
196
|
-
const _noopUnsubscribe = () => { };
|
|
197
157
|
const _sentinel = {
|
|
198
158
|
clearWatch() { },
|
|
199
159
|
getCurrentPosition() {
|
|
@@ -202,14 +162,11 @@ const _sentinel = {
|
|
|
202
162
|
getCurrentPositionResult() {
|
|
203
163
|
return Promise.resolve({ position: null, reason: 'unavailable' });
|
|
204
164
|
},
|
|
205
|
-
|
|
206
|
-
return
|
|
207
|
-
},
|
|
208
|
-
requestPermission() {
|
|
209
|
-
return Promise.resolve(false);
|
|
165
|
+
isAvailable() {
|
|
166
|
+
return false;
|
|
210
167
|
},
|
|
211
|
-
|
|
212
|
-
return
|
|
168
|
+
promptForAccess() {
|
|
169
|
+
return Promise.resolve({ reason: 'runtime-unavailable' });
|
|
213
170
|
},
|
|
214
171
|
watchPosition() {
|
|
215
172
|
return -1;
|
|
@@ -236,6 +193,16 @@ function mapWebPosition(position) {
|
|
|
236
193
|
timestamp: position.timestamp,
|
|
237
194
|
};
|
|
238
195
|
}
|
|
196
|
+
function mapWebAccessError(error) {
|
|
197
|
+
switch (error.code) {
|
|
198
|
+
case 1:
|
|
199
|
+
return 'denied';
|
|
200
|
+
case 3:
|
|
201
|
+
return 'timeout';
|
|
202
|
+
default:
|
|
203
|
+
return 'operation-failed';
|
|
204
|
+
}
|
|
205
|
+
}
|
|
239
206
|
function mapWebPositionError(error) {
|
|
240
207
|
switch (error.code) {
|
|
241
208
|
case 1:
|
package/dist/geolocation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geolocation.js","sourceRoot":"","sources":["../src/geolocation.ts"],"names":[],"mappings":"AAUA,MAAM,UAAU,qBAAqB,CAAC,EAAU;IAC9C,qBAAqB,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO;QACL,QAAQ,EAAE,CAAC;QACX,QAAQ,EAAE,CAAC;QACX,gBAAgB,EAAE,CAAC;QACnB,UAAU,EAAE,CAAC;QACb,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,CAAC;QACX,SAAS,EAAE,CAAC;QACZ,KAAK,EAAE,CAAC;QACR,SAAS,EAAE,CAAC;KACb,CAAC;AACJ,CAAC;AAED,gGAAgG;AAChG,oGAAoG;AACpG,qDAAqD;AACrD,MAAM,UAAU,2BAA2B;IACzC,OAAO;QACL,UAAU,CAAC,EAAE;YACX,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;YAChC,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,CAAC,UAAU,KAAK,UAAU;gBAAE,OAAO;YACjE,IAAI,CAAC;gBACH,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,+EAA+E;YACjF,CAAC;QACH,CAAC;QACD,kBAAkB,CAAC,OAAO;YACxB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC7B,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;gBAChC,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,CAAC,kBAAkB,KAAK,UAAU,EAAE,CAAC;oBACjE,OAAO,CAAC,IAAI,CAAC,CAAC;oBACd,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC;oBACH,GAAG,CAAC,kBAAkB,CACpB,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,EAC/C,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,EACnB,iBAAiB,CAAC,OAAO,CAAC,CAC3B,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,CAAC,IAAI,CAAC,CAAC;gBAChB,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,wBAAwB,CAAC,OAAO;YAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC7B,MAAM,GAAG,GAAG,iBAAiB,EAAE,CAAC;gBAChC,IAAI,GAAG,KAAK,IAAI,IAAI,OAAO,GAAG,CAAC,kBAAkB,KAAK,UAAU,EAAE,CAAC;oBACjE,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;oBACnD,OAAO;gBACT,CAAC;gBACD,IAAI,CAAC;oBACH,GAAG,CAAC,kBAAkB,CACpB,CAAC,QAAQ,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,cAAc,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAC3E,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC,EAC1E,iBAAiB,CAAC,OAAO,CAAC,CAC3B,CAAC;gBACJ,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;gBACrD,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,
|
|
1
|
+
{"version":3,"file":"geolocation.js","sourceRoot":"","sources":["../src/geolocation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GeolocationAccessOutcome, Host } from '@flighthq/types/contract';
|
|
2
|
+
/**
|
|
3
|
+
* Raises the platform's own location-access prompt through the host's geolocation provider.
|
|
4
|
+
*
|
|
5
|
+
* This is the mechanism half of the permission split: only the capability can raise its own prompt,
|
|
6
|
+
* so geolocation keeps it, while `@flighthq/permissions` owns the permission vocabulary and projects
|
|
7
|
+
* this outcome. Named for the intent — a native host implements it with its real permission API and
|
|
8
|
+
* never starts location services, rather than emulating the web's acquire-and-discard workaround.
|
|
9
|
+
*
|
|
10
|
+
* Reports `runtime-unavailable` rather than throwing when no provider is installed: an absent host
|
|
11
|
+
* capability is an expected outcome, not API misuse.
|
|
12
|
+
*/
|
|
13
|
+
export declare function promptForGeolocationAccess(host: Readonly<Host>): Promise<GeolocationAccessOutcome>;
|
|
14
|
+
//# sourceMappingURL=geolocationAccess.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geolocationAccess.d.ts","sourceRoot":"","sources":["../src/geolocationAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAE/E;;;;;;;;;;GAUG;AACH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,wBAAwB,CAAC,CAUxG"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Raises the platform's own location-access prompt through the host's geolocation provider.
|
|
3
|
+
*
|
|
4
|
+
* This is the mechanism half of the permission split: only the capability can raise its own prompt,
|
|
5
|
+
* so geolocation keeps it, while `@flighthq/permissions` owns the permission vocabulary and projects
|
|
6
|
+
* this outcome. Named for the intent — a native host implements it with its real permission API and
|
|
7
|
+
* never starts location services, rather than emulating the web's acquire-and-discard workaround.
|
|
8
|
+
*
|
|
9
|
+
* Reports `runtime-unavailable` rather than throwing when no provider is installed: an absent host
|
|
10
|
+
* capability is an expected outcome, not API misuse.
|
|
11
|
+
*/
|
|
12
|
+
export async function promptForGeolocationAccess(host) {
|
|
13
|
+
const backend = host.system?.geolocation;
|
|
14
|
+
if (backend === undefined || typeof backend.promptForAccess !== 'function') {
|
|
15
|
+
return { reason: 'runtime-unavailable' };
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
return await backend.promptForAccess();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return { reason: 'operation-failed' };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=geolocationAccess.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geolocationAccess.js","sourceRoot":"","sources":["../src/geolocationAccess.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,IAAoB;IACnE,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;IACzC,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,eAAe,KAAK,UAAU,EAAE,CAAC;QAC3E,OAAO,EAAE,MAAM,EAAE,qBAAqB,EAAE,CAAC;IAC3C,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC;IACzC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;IACxC,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { explainGeolocationBackend, clearGeolocationWatch, createGeoPosition, getCurrentGeoPosition, getCurrentGeoPositionResult,
|
|
1
|
+
export { explainGeolocationBackend, clearGeolocationWatch, createGeoPosition, getCurrentGeoPosition, getCurrentGeoPositionResult, isGeolocationAvailable, promptForGeolocationAccess, watchGeolocationPosition, } 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,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,2BAA2B,EAC3B,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,2BAA2B,EAC3B,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { explainGeolocationBackend, clearGeolocationWatch, createGeoPosition, getCurrentGeoPosition, getCurrentGeoPositionResult,
|
|
1
|
+
export { explainGeolocationBackend, clearGeolocationWatch, createGeoPosition, getCurrentGeoPosition, getCurrentGeoPositionResult, isGeolocationAvailable, promptForGeolocationAccess, watchGeolocationPosition, } 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,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,2BAA2B,EAC3B,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EACzB,qBAAqB,EACrB,iBAAiB,EACjB,qBAAqB,EACrB,2BAA2B,EAC3B,sBAAsB,EACtB,0BAA0B,EAC1B,wBAAwB,GACzB,MAAM,YAAY,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flighthq/geolocation",
|
|
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,7 +38,7 @@
|
|
|
38
38
|
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@flighthq/types": "0.5.
|
|
41
|
+
"@flighthq/types": "0.5.1-edge.638.d4dbd4e"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"typescript": "^5.3.0"
|
package/src/geolocation.test.ts
CHANGED
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
GeolocationBackend,
|
|
3
|
-
GeolocationErrorReason,
|
|
4
|
-
GeolocationPermissionState,
|
|
5
|
-
GeoPosition,
|
|
6
|
-
} from '@flighthq/types/contract';
|
|
1
|
+
import type { GeolocationBackend, GeolocationErrorReason, GeoPosition } from '@flighthq/types/contract';
|
|
7
2
|
|
|
8
3
|
import {
|
|
9
4
|
clearGeolocationWatch,
|
|
@@ -12,10 +7,7 @@ import {
|
|
|
12
7
|
getCurrentGeoPosition,
|
|
13
8
|
getCurrentGeoPositionResult,
|
|
14
9
|
getGeolocationBackend,
|
|
15
|
-
getGeolocationPermission,
|
|
16
10
|
isGeolocationAvailable,
|
|
17
|
-
onGeolocationPermissionChange,
|
|
18
|
-
requestGeolocationPermission,
|
|
19
11
|
setGeolocationBackend,
|
|
20
12
|
watchGeolocationPosition,
|
|
21
13
|
explainGeolocationBackend,
|
|
@@ -24,10 +16,11 @@ import {
|
|
|
24
16
|
resetGeolocationBackendForTest,
|
|
25
17
|
} from './geolocation';
|
|
26
18
|
|
|
27
|
-
function fakeBackend(): GeolocationBackend & { cleared: number[]; lastWatch: number } {
|
|
19
|
+
function fakeBackend(available: boolean = true): GeolocationBackend & { cleared: number[]; lastWatch: number } {
|
|
28
20
|
return {
|
|
29
21
|
cleared: [],
|
|
30
22
|
lastWatch: 0,
|
|
23
|
+
promptForAccess: () => Promise.resolve({ reason: 'granted' as const }),
|
|
31
24
|
clearWatch(id) {
|
|
32
25
|
this.cleared.push(id);
|
|
33
26
|
},
|
|
@@ -43,14 +36,8 @@ function fakeBackend(): GeolocationBackend & { cleared: number[]; lastWatch: num
|
|
|
43
36
|
position.longitude = 2;
|
|
44
37
|
return { position, reason: null };
|
|
45
38
|
},
|
|
46
|
-
|
|
47
|
-
return
|
|
48
|
-
},
|
|
49
|
-
async requestPermission() {
|
|
50
|
-
return true;
|
|
51
|
-
},
|
|
52
|
-
subscribePermission(_listener: (state: GeolocationPermissionState) => void) {
|
|
53
|
-
return () => {};
|
|
39
|
+
isAvailable() {
|
|
40
|
+
return available;
|
|
54
41
|
},
|
|
55
42
|
watchPosition(listener, _options, onError) {
|
|
56
43
|
const position = createGeoPosition();
|
|
@@ -62,7 +49,10 @@ function fakeBackend(): GeolocationBackend & { cleared: number[]; lastWatch: num
|
|
|
62
49
|
};
|
|
63
50
|
}
|
|
64
51
|
|
|
65
|
-
afterEach(() =>
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
resetGeolocationBackendForTest();
|
|
54
|
+
vi.unstubAllGlobals();
|
|
55
|
+
});
|
|
66
56
|
|
|
67
57
|
describe('clearGeolocationWatch', () => {
|
|
68
58
|
it('does not throw on the web backend in jsdom', () => {
|
|
@@ -99,13 +89,6 @@ describe('createWebGeolocationBackend', () => {
|
|
|
99
89
|
expect(await backend.getCurrentPosition({})).toBeNull();
|
|
100
90
|
expect(typeof backend.watchPosition(() => {}, {})).toBe('number');
|
|
101
91
|
expect(() => backend.clearWatch(-1)).not.toThrow();
|
|
102
|
-
expect(typeof (await backend.requestPermission())).toBe('boolean');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('getPermission returns a GeolocationPermissionState string', async () => {
|
|
106
|
-
const backend = createWebGeolocationBackend();
|
|
107
|
-
const state = await backend.getPermission();
|
|
108
|
-
expect(['granted', 'denied', 'prompt']).toContain(state);
|
|
109
92
|
});
|
|
110
93
|
|
|
111
94
|
it('getCurrentPositionResult returns unavailable reason when geolocation is absent', async () => {
|
|
@@ -115,13 +98,6 @@ describe('createWebGeolocationBackend', () => {
|
|
|
115
98
|
expect(result.reason).toBe('unavailable');
|
|
116
99
|
});
|
|
117
100
|
|
|
118
|
-
it('subscribePermission returns an unsubscribe function', () => {
|
|
119
|
-
const backend = createWebGeolocationBackend();
|
|
120
|
-
const unsubscribe = backend.subscribePermission(() => {});
|
|
121
|
-
expect(typeof unsubscribe).toBe('function');
|
|
122
|
-
expect(() => unsubscribe()).not.toThrow();
|
|
123
|
-
});
|
|
124
|
-
|
|
125
101
|
it('reads a host-provided floorLevel from coords', async () => {
|
|
126
102
|
const hadOwn = Object.prototype.hasOwnProperty.call(navigator, 'geolocation');
|
|
127
103
|
const original =
|
|
@@ -159,6 +135,20 @@ describe('createWebGeolocationBackend', () => {
|
|
|
159
135
|
}
|
|
160
136
|
}
|
|
161
137
|
});
|
|
138
|
+
|
|
139
|
+
it('reports availability only when geolocation exists in a secure context', () => {
|
|
140
|
+
const backend = createWebGeolocationBackend();
|
|
141
|
+
vi.stubGlobal('navigator', { geolocation: {} });
|
|
142
|
+
vi.stubGlobal('window', { isSecureContext: true });
|
|
143
|
+
expect(backend.isAvailable()).toBe(true);
|
|
144
|
+
|
|
145
|
+
vi.stubGlobal('window', { isSecureContext: false });
|
|
146
|
+
expect(backend.isAvailable()).toBe(false);
|
|
147
|
+
|
|
148
|
+
vi.stubGlobal('navigator', {});
|
|
149
|
+
vi.stubGlobal('window', { isSecureContext: true });
|
|
150
|
+
expect(backend.isAvailable()).toBe(false);
|
|
151
|
+
});
|
|
162
152
|
});
|
|
163
153
|
|
|
164
154
|
describe('explainGeolocationBackend', () => {
|
|
@@ -226,18 +216,6 @@ describe('getGeolocationBackend', () => {
|
|
|
226
216
|
});
|
|
227
217
|
});
|
|
228
218
|
|
|
229
|
-
describe('getGeolocationPermission', () => {
|
|
230
|
-
it('reflects the backend permission state', async () => {
|
|
231
|
-
setGeolocationBackend(fakeBackend());
|
|
232
|
-
expect(await getGeolocationPermission()).toBe('granted');
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('returns a GeolocationPermissionState string from the web backend', async () => {
|
|
236
|
-
const state = await getGeolocationPermission();
|
|
237
|
-
expect(['granted', 'denied', 'prompt']).toContain(state);
|
|
238
|
-
});
|
|
239
|
-
});
|
|
240
|
-
|
|
241
219
|
describe('installGeolocationHostBackend', () => {
|
|
242
220
|
afterEach(() => resetGeolocationBackendForTest());
|
|
243
221
|
|
|
@@ -258,12 +236,23 @@ describe('installGeolocationHostBackend', () => {
|
|
|
258
236
|
});
|
|
259
237
|
|
|
260
238
|
describe('isGeolocationAvailable', () => {
|
|
261
|
-
it('returns
|
|
262
|
-
|
|
239
|
+
it('returns the false sentinel even when browser geolocation exists', () => {
|
|
240
|
+
vi.stubGlobal('navigator', { geolocation: {} });
|
|
241
|
+
vi.stubGlobal('window', { isSecureContext: true });
|
|
242
|
+
expect(isGeolocationAvailable()).toBe(false);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('routes a custom available provider when DOM globals are absent', () => {
|
|
246
|
+
vi.stubGlobal('navigator', undefined);
|
|
247
|
+
vi.stubGlobal('window', undefined);
|
|
248
|
+
setGeolocationBackend(fakeBackend(true));
|
|
249
|
+
expect(isGeolocationAvailable()).toBe(true);
|
|
263
250
|
});
|
|
264
251
|
|
|
265
|
-
it('
|
|
266
|
-
|
|
252
|
+
it('routes an unavailable host provider when browser geolocation exists', () => {
|
|
253
|
+
vi.stubGlobal('navigator', { geolocation: {} });
|
|
254
|
+
vi.stubGlobal('window', { isSecureContext: true });
|
|
255
|
+
installGeolocationHostBackend(fakeBackend(false));
|
|
267
256
|
expect(isGeolocationAvailable()).toBe(false);
|
|
268
257
|
});
|
|
269
258
|
});
|
|
@@ -286,42 +275,6 @@ describe('observeGeolocationHostResult', () => {
|
|
|
286
275
|
});
|
|
287
276
|
});
|
|
288
277
|
|
|
289
|
-
describe('onGeolocationPermissionChange', () => {
|
|
290
|
-
it('returns an unsubscribe function', () => {
|
|
291
|
-
const unsubscribe = onGeolocationPermissionChange(() => {});
|
|
292
|
-
expect(typeof unsubscribe).toBe('function');
|
|
293
|
-
expect(() => unsubscribe()).not.toThrow();
|
|
294
|
-
});
|
|
295
|
-
|
|
296
|
-
it('uses the backend subscribePermission', () => {
|
|
297
|
-
let subscribed = false;
|
|
298
|
-
let unsubscribed = false;
|
|
299
|
-
const backend = fakeBackend();
|
|
300
|
-
backend.subscribePermission = (_listener) => {
|
|
301
|
-
subscribed = true;
|
|
302
|
-
return () => {
|
|
303
|
-
unsubscribed = true;
|
|
304
|
-
};
|
|
305
|
-
};
|
|
306
|
-
setGeolocationBackend(backend);
|
|
307
|
-
const unsub = onGeolocationPermissionChange(() => {});
|
|
308
|
-
expect(subscribed).toBe(true);
|
|
309
|
-
unsub();
|
|
310
|
-
expect(unsubscribed).toBe(true);
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
describe('requestGeolocationPermission', () => {
|
|
315
|
-
it('reflects the backend result', async () => {
|
|
316
|
-
setGeolocationBackend(fakeBackend());
|
|
317
|
-
expect(await requestGeolocationPermission()).toBe(true);
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
it('returns a boolean from the web backend without throwing', async () => {
|
|
321
|
-
expect(typeof (await requestGeolocationPermission())).toBe('boolean');
|
|
322
|
-
});
|
|
323
|
-
});
|
|
324
|
-
|
|
325
278
|
describe('resetGeolocationBackendForTest', () => {
|
|
326
279
|
it('clears all backend slots', () => {
|
|
327
280
|
setGeolocationBackend(fakeBackend());
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { GeolocationAccessOutcome, GeolocationBackend } from '@flighthq/types/contract';
|
|
2
|
+
|
|
3
|
+
import { promptForGeolocationAccess } from './geolocationAccess';
|
|
4
|
+
|
|
5
|
+
// A backend whose promptForAccess answer the test dictates. Only that member is exercised, so the
|
|
6
|
+
// rest of GeolocationBackend stays absent on purpose — a probe needing a whole backend would be
|
|
7
|
+
// testing the fake.
|
|
8
|
+
function backendAnswering(outcome: GeolocationAccessOutcome): GeolocationBackend {
|
|
9
|
+
return { promptForAccess: () => Promise.resolve(outcome) } as unknown as GeolocationBackend;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function hostWith(backend: GeolocationBackend | undefined): Parameters<typeof promptForGeolocationAccess>[0] {
|
|
13
|
+
return { system: backend === undefined ? {} : { geolocation: backend } } as unknown as Parameters<
|
|
14
|
+
typeof promptForGeolocationAccess
|
|
15
|
+
>[0];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('promptForGeolocationAccess', () => {
|
|
19
|
+
it('reports granted when the host grants access', async () => {
|
|
20
|
+
expect(await promptForGeolocationAccess(hostWith(backendAnswering({ reason: 'granted' })))).toEqual({
|
|
21
|
+
reason: 'granted',
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
// The three user-facing answers must stay distinct. `denied` means stop asking; `dismissed` means the
|
|
26
|
+
// prompt closed undecided; `timeout` means no fix arrived in time and says NOTHING about the user.
|
|
27
|
+
it('keeps denied, dismissed and timeout as three distinct outcomes', async () => {
|
|
28
|
+
const reasons = await Promise.all(
|
|
29
|
+
(['denied', 'dismissed', 'timeout'] as const).map(
|
|
30
|
+
async (reason) => (await promptForGeolocationAccess(hostWith(backendAnswering({ reason })))).reason,
|
|
31
|
+
),
|
|
32
|
+
);
|
|
33
|
+
expect(reasons).toEqual(['denied', 'dismissed', 'timeout']);
|
|
34
|
+
expect(new Set(reasons).size).toBe(3);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('reports runtime-unavailable when no provider is installed', async () => {
|
|
38
|
+
expect(await promptForGeolocationAccess(hostWith(undefined))).toEqual({ reason: 'runtime-unavailable' });
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('reports operation-failed when the provider throws', async () => {
|
|
42
|
+
const throwing = {
|
|
43
|
+
promptForAccess: () => Promise.reject(new Error('boom')),
|
|
44
|
+
} as unknown as GeolocationBackend;
|
|
45
|
+
expect(await promptForGeolocationAccess(hostWith(throwing))).toEqual({ reason: 'operation-failed' });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// A capability outcome, never permission vocabulary: G6 lets geolocation keep the mechanism only
|
|
49
|
+
// because what it returns is not a PermissionState. `state` must never appear on this type.
|
|
50
|
+
it('carries no permission state field on any arm', async () => {
|
|
51
|
+
for (const reason of ['granted', 'denied', 'dismissed', 'timeout', 'cleanup-failed'] as const) {
|
|
52
|
+
const outcome = await promptForGeolocationAccess(hostWith(backendAnswering({ reason })));
|
|
53
|
+
expect(Object.hasOwn(outcome, 'state')).toBe(false);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
});
|