@metamask-previews/geolocation-controller 1.0.0-preview-8bfa290fb → 2.0.0-preview-0a30e47
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/CHANGELOG.md +15 -1
- package/dist/{GeolocationController-method-action-types.d.mts → GeolocationController-method-action-types.d.ts} +2 -2
- package/dist/GeolocationController-method-action-types.d.ts.map +1 -0
- package/dist/{GeolocationController-method-action-types.mjs → GeolocationController-method-action-types.js} +1 -1
- package/dist/GeolocationController-method-action-types.js.map +1 -0
- package/dist/{GeolocationController.d.cts → GeolocationController.d.ts} +8 -8
- package/dist/GeolocationController.d.ts.map +1 -0
- package/dist/{GeolocationController.mjs → GeolocationController.js} +40 -48
- package/dist/GeolocationController.js.map +1 -0
- package/dist/geolocation-api-service/{geolocation-api-service-method-action-types.d.cts → geolocation-api-service-method-action-types.d.ts} +2 -2
- package/dist/geolocation-api-service/geolocation-api-service-method-action-types.d.ts.map +1 -0
- package/dist/geolocation-api-service/{geolocation-api-service-method-action-types.mjs → geolocation-api-service-method-action-types.js} +1 -1
- package/dist/geolocation-api-service/geolocation-api-service-method-action-types.js.map +1 -0
- package/dist/geolocation-api-service/{geolocation-api-service.d.cts → geolocation-api-service.d.ts} +6 -6
- package/dist/geolocation-api-service/geolocation-api-service.d.ts.map +1 -0
- package/dist/geolocation-api-service/{geolocation-api-service.mjs → geolocation-api-service.js} +73 -77
- package/dist/geolocation-api-service/geolocation-api-service.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/{types.d.mts → types.d.ts} +1 -1
- package/dist/types.d.ts.map +1 -0
- package/dist/{types.mjs → types.js} +1 -1
- package/dist/types.js.map +1 -0
- package/package.json +15 -19
- package/dist/GeolocationController-method-action-types.cjs +0 -7
- package/dist/GeolocationController-method-action-types.cjs.map +0 -1
- package/dist/GeolocationController-method-action-types.d.cts +0 -54
- package/dist/GeolocationController-method-action-types.d.cts.map +0 -1
- package/dist/GeolocationController-method-action-types.d.mts.map +0 -1
- package/dist/GeolocationController-method-action-types.mjs.map +0 -1
- package/dist/GeolocationController.cjs +0 -214
- package/dist/GeolocationController.cjs.map +0 -1
- package/dist/GeolocationController.d.cts.map +0 -1
- package/dist/GeolocationController.d.mts +0 -140
- package/dist/GeolocationController.d.mts.map +0 -1
- package/dist/GeolocationController.mjs.map +0 -1
- package/dist/geolocation-api-service/geolocation-api-service-method-action-types.cjs +0 -7
- package/dist/geolocation-api-service/geolocation-api-service-method-action-types.cjs.map +0 -1
- package/dist/geolocation-api-service/geolocation-api-service-method-action-types.d.cts.map +0 -1
- package/dist/geolocation-api-service/geolocation-api-service-method-action-types.d.mts +0 -42
- package/dist/geolocation-api-service/geolocation-api-service-method-action-types.d.mts.map +0 -1
- package/dist/geolocation-api-service/geolocation-api-service-method-action-types.mjs.map +0 -1
- package/dist/geolocation-api-service/geolocation-api-service.cjs +0 -305
- package/dist/geolocation-api-service/geolocation-api-service.cjs.map +0 -1
- package/dist/geolocation-api-service/geolocation-api-service.d.cts.map +0 -1
- package/dist/geolocation-api-service/geolocation-api-service.d.mts +0 -175
- package/dist/geolocation-api-service/geolocation-api-service.d.mts.map +0 -1
- package/dist/geolocation-api-service/geolocation-api-service.mjs.map +0 -1
- package/dist/index.cjs +0 -14
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -9
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.mts +0 -9
- package/dist/index.d.mts.map +0 -1
- package/dist/index.mjs +0 -4
- package/dist/index.mjs.map +0 -1
- package/dist/types.cjs +0 -13
- package/dist/types.cjs.map +0 -1
- package/dist/types.d.cts +0 -13
- package/dist/types.d.cts.map +0 -1
- package/dist/types.d.mts.map +0 -1
- package/dist/types.mjs.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.0]
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **BREAKING:** Drop CommonJS support ([#9536](https://github.com/MetaMask/core/pull/9536))
|
|
15
|
+
- This package is now ESM-only, but can still be used in CommonJS projects via `require(esm)` in modern Node.js versions (22+), or dynamic imports in older Node.js versions.
|
|
16
|
+
- **BREAKING:** Bump minimum Node.js version to 22 ([#9976](https://github.com/MetaMask/core/pull/9976))
|
|
17
|
+
- **BREAKING:** Bump TypeScript target to ES2022 ([#10019](https://github.com/MetaMask/core/pull/10019))
|
|
18
|
+
- This package now ships ES2022 code, requiring a compatible modern environment or bundler configuration to consume.
|
|
19
|
+
- Bump `@metamask/base-controller` from `^9.1.0` to `^10.0.0` ([#10160](https://github.com/MetaMask/core/pull/10160))
|
|
20
|
+
- Bump `@metamask/controller-utils` from `^12.3.0` to `^13.0.0` ([#10160](https://github.com/MetaMask/core/pull/10160))
|
|
21
|
+
- Bump `@metamask/messenger` from `^2.0.0` to `^3.0.0` ([#10160](https://github.com/MetaMask/core/pull/10160))
|
|
22
|
+
|
|
10
23
|
## [1.0.0]
|
|
11
24
|
|
|
12
25
|
### Changed
|
|
@@ -45,7 +58,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
45
58
|
|
|
46
59
|
- Initial release ([#8037](https://github.com/MetaMask/core/pull/8037))
|
|
47
60
|
|
|
48
|
-
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/geolocation-controller@
|
|
61
|
+
[Unreleased]: https://github.com/MetaMask/core/compare/@metamask/geolocation-controller@2.0.0...HEAD
|
|
62
|
+
[2.0.0]: https://github.com/MetaMask/core/compare/@metamask/geolocation-controller@1.0.0...@metamask/geolocation-controller@2.0.0
|
|
49
63
|
[1.0.0]: https://github.com/MetaMask/core/compare/@metamask/geolocation-controller@0.1.3...@metamask/geolocation-controller@1.0.0
|
|
50
64
|
[0.1.3]: https://github.com/MetaMask/core/compare/@metamask/geolocation-controller@0.1.2...@metamask/geolocation-controller@0.1.3
|
|
51
65
|
[0.1.2]: https://github.com/MetaMask/core/compare/@metamask/geolocation-controller@0.1.1...@metamask/geolocation-controller@0.1.2
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* This file is auto generated.
|
|
3
3
|
* Do not edit manually.
|
|
4
4
|
*/
|
|
5
|
-
import type { GeolocationController } from
|
|
5
|
+
import type { GeolocationController } from './GeolocationController.js';
|
|
6
6
|
/**
|
|
7
7
|
* Returns the geolocation code. Delegates to the
|
|
8
8
|
* {@link GeolocationApiService} for network fetching and caching, then
|
|
@@ -51,4 +51,4 @@ export type GeolocationControllerRefreshGeolocationAction = {
|
|
|
51
51
|
* Union of all GeolocationController action types.
|
|
52
52
|
*/
|
|
53
53
|
export type GeolocationControllerMethodActions = GeolocationControllerGetGeolocationAction | GeolocationControllerGetGeolocationDataAction | GeolocationControllerRefreshGeolocationAction;
|
|
54
|
-
//# sourceMappingURL=GeolocationController-method-action-types.d.
|
|
54
|
+
//# sourceMappingURL=GeolocationController-method-action-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GeolocationController-method-action-types.d.ts","sourceRoot":"","sources":["../src/GeolocationController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE;;;;;;;;;GASG;AACH,MAAM,MAAM,yCAAyC,GAAG;IACtD,IAAI,EAAE,sCAAsC,CAAC;IAC7C,OAAO,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;CAClD,CAAC;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,6CAA6C,GAAG;IAC1D,IAAI,EAAE,0CAA0C,CAAC;IACjD,OAAO,EAAE,qBAAqB,CAAC,oBAAoB,CAAC,CAAC;CACtD,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,6CAA6C,GAAG;IAC1D,IAAI,EAAE,0CAA0C,CAAC;IACjD,OAAO,EAAE,qBAAqB,CAAC,oBAAoB,CAAC,CAAC;CACtD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,kCAAkC,GAC1C,yCAAyC,GACzC,6CAA6C,GAC7C,6CAA6C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GeolocationController-method-action-types.js","sourceRoot":"","sources":["../src/GeolocationController-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { GeolocationController } from './GeolocationController.js';\n\n/**\n * Returns the geolocation code. Delegates to the\n * {@link GeolocationApiService} for network fetching and caching, then\n * updates controller state with the result.\n *\n * Best-effort: if the fetch fails, the last known location code (or\n * {@link UNKNOWN_LOCATION}) is returned rather than throwing.\n *\n * @returns The ISO 3166-2 location code string.\n */\nexport type GeolocationControllerGetGeolocationAction = {\n type: `GeolocationController:getGeolocation`;\n handler: GeolocationController['getGeolocation'];\n};\n\n/**\n * Returns the country, region, and timezone for the current client.\n * Delegates to the {@link GeolocationApiService} for network fetching and\n * caching, then updates controller state with the result.\n *\n * Unlike {@link getGeolocation}, this rejects when resolution fails instead\n * of returning a stale value, so callers can distinguish a fresh result from\n * a failed lookup (and, for example, omit location rather than enrich with a\n * previous session's data).\n *\n * @returns The geolocation data, where each field is `null` when it could\n * not be determined.\n * @throws When the geolocation service fails to resolve.\n */\nexport type GeolocationControllerGetGeolocationDataAction = {\n type: `GeolocationController:getGeolocationData`;\n handler: GeolocationController['getGeolocationData'];\n};\n\n/**\n * Forces a fresh geolocation fetch, bypassing the service's cache.\n *\n * Best-effort: if the fetch fails, the last known location code (or\n * {@link UNKNOWN_LOCATION}) is returned rather than throwing.\n *\n * @returns The ISO 3166-2 location code string.\n */\nexport type GeolocationControllerRefreshGeolocationAction = {\n type: `GeolocationController:refreshGeolocation`;\n handler: GeolocationController['refreshGeolocation'];\n};\n\n/**\n * Union of all GeolocationController action types.\n */\nexport type GeolocationControllerMethodActions =\n | GeolocationControllerGetGeolocationAction\n | GeolocationControllerGetGeolocationDataAction\n | GeolocationControllerRefreshGeolocationAction;\n"]}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type { ControllerGetStateAction, ControllerStateChangeEvent } from
|
|
2
|
-
import { BaseController } from
|
|
3
|
-
import type { Messenger } from
|
|
4
|
-
import type { GeolocationApiServiceFetchGeolocationDataAction } from
|
|
5
|
-
import type { GeolocationData } from
|
|
6
|
-
import type { GeolocationControllerMethodActions } from
|
|
7
|
-
import type { GeolocationRequestStatus } from
|
|
1
|
+
import type { ControllerGetStateAction, ControllerStateChangeEvent } from '@metamask/base-controller';
|
|
2
|
+
import { BaseController } from '@metamask/base-controller';
|
|
3
|
+
import type { Messenger } from '@metamask/messenger';
|
|
4
|
+
import type { GeolocationApiServiceFetchGeolocationDataAction } from './geolocation-api-service/geolocation-api-service-method-action-types.js';
|
|
5
|
+
import type { GeolocationData } from './geolocation-api-service/geolocation-api-service.js';
|
|
6
|
+
import type { GeolocationControllerMethodActions } from './GeolocationController-method-action-types.js';
|
|
7
|
+
import type { GeolocationRequestStatus } from './types.js';
|
|
8
8
|
/**
|
|
9
9
|
* The name of the {@link GeolocationController}, used to namespace the
|
|
10
10
|
* controller's actions and events and to namespace the controller's state data
|
|
@@ -137,4 +137,4 @@ export declare class GeolocationController extends BaseController<typeof control
|
|
|
137
137
|
refreshGeolocation(): Promise<string>;
|
|
138
138
|
}
|
|
139
139
|
export {};
|
|
140
|
-
//# sourceMappingURL=GeolocationController.d.
|
|
140
|
+
//# sourceMappingURL=GeolocationController.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GeolocationController.d.ts","sourceRoot":"","sources":["../src/GeolocationController.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,wBAAwB,EACxB,0BAA0B,EAE3B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,KAAK,EAAE,+CAA+C,EAAE,MAAM,0EAA0E,CAAC;AAChJ,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sDAAsD,CAAC;AAM5F,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,gDAAgD,CAAC;AACzG,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D;;;;GAIG;AACH,eAAO,MAAM,cAAc,0BAA0B,CAAC;AAEtD;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAAG;IACvC,kGAAkG;IAClG,QAAQ,EAAE,MAAM,CAAC;IACjB,kFAAkF;IAClF,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,yGAAyG;IACzG,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,yDAAyD;IACzD,MAAM,EAAE,wBAAwB,CAAC;IACjC,iFAAiF;IACjF,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,4DAA4D;IAC5D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAkDF;;;;;;;GAOG;AACH,wBAAgB,oCAAoC,IAAI,0BAA0B,CAQjF;AAQD;;GAEG;AACH,MAAM,MAAM,mCAAmC,GAAG,wBAAwB,CACxE,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GACpC,mCAAmC,GACnC,kCAAkC,CAAC;AAEvC;;GAEG;AACH,KAAK,cAAc,GAAG,+CAA+C,CAAC;AAEtE;;GAEG;AACH,MAAM,MAAM,qCAAqC,GAAG,0BAA0B,CAC5E,OAAO,cAAc,EACrB,0BAA0B,CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,qCAAqC,CAAC;AAEhF;;;GAGG;AACH,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,8BAA8B,GAAG,SAAS,CACpD,OAAO,cAAc,EACrB,4BAA4B,GAAG,cAAc,EAC7C,2BAA2B,GAAG,aAAa,CAC5C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,wDAAwD;IACxD,SAAS,EAAE,8BAA8B,CAAC;IAC1C,sCAAsC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC,0BAA0B,CAAC,CAAC;CAC7C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,qBAAa,qBAAsB,SAAQ,cAAc,CACvD,OAAO,cAAc,EACrB,0BAA0B,EAC1B,8BAA8B,CAC/B;;IACC;;;;;;;OAOG;IACH,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,4BAA4B,EAY7D;IAED;;;;;;;;;OASG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,CAOtC;IAED;;;;;;;;;;;;;OAaG;IACG,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC,CAEnD;IAED;;;;;;;OAOG;IACG,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAU1C;CAgDF"}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
-
};
|
|
6
|
-
var _GeolocationController_instances, _GeolocationController_fetchAndUpdate;
|
|
7
|
-
import { BaseController } from "@metamask/base-controller";
|
|
8
|
-
import { getUnknownGeolocationData, toLocationCode, UNKNOWN_LOCATION } from "./geolocation-api-service/geolocation-api-service.mjs";
|
|
1
|
+
import { BaseController } from '@metamask/base-controller';
|
|
2
|
+
import { getUnknownGeolocationData, toLocationCode, UNKNOWN_LOCATION, } from './geolocation-api-service/geolocation-api-service.js';
|
|
9
3
|
/**
|
|
10
4
|
* The name of the {@link GeolocationController}, used to namespace the
|
|
11
5
|
* controller's actions and events and to namespace the controller's state data
|
|
@@ -108,7 +102,6 @@ export class GeolocationController extends BaseController {
|
|
|
108
102
|
name: controllerName,
|
|
109
103
|
state: { ...getDefaultGeolocationControllerState(), ...state },
|
|
110
104
|
});
|
|
111
|
-
_GeolocationController_instances.add(this);
|
|
112
105
|
this.messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
113
106
|
}
|
|
114
107
|
/**
|
|
@@ -123,7 +116,7 @@ export class GeolocationController extends BaseController {
|
|
|
123
116
|
*/
|
|
124
117
|
async getGeolocation() {
|
|
125
118
|
try {
|
|
126
|
-
await
|
|
119
|
+
await this.#fetchAndUpdate();
|
|
127
120
|
}
|
|
128
121
|
catch {
|
|
129
122
|
// Best-effort: fall back to the last known location code below.
|
|
@@ -145,7 +138,7 @@ export class GeolocationController extends BaseController {
|
|
|
145
138
|
* @throws When the geolocation service fails to resolve.
|
|
146
139
|
*/
|
|
147
140
|
async getGeolocationData() {
|
|
148
|
-
return
|
|
141
|
+
return this.#fetchAndUpdate();
|
|
149
142
|
}
|
|
150
143
|
/**
|
|
151
144
|
* Forces a fresh geolocation fetch, bypassing the service's cache.
|
|
@@ -160,50 +153,49 @@ export class GeolocationController extends BaseController {
|
|
|
160
153
|
draft.lastFetchedAt = null;
|
|
161
154
|
});
|
|
162
155
|
try {
|
|
163
|
-
await
|
|
156
|
+
await this.#fetchAndUpdate({ bypassCache: true });
|
|
164
157
|
}
|
|
165
158
|
catch {
|
|
166
159
|
// Best-effort: fall back to the last known location code below.
|
|
167
160
|
}
|
|
168
161
|
return this.state.location;
|
|
169
162
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
*/
|
|
182
|
-
async function _GeolocationController_fetchAndUpdate(options) {
|
|
183
|
-
this.update((draft) => {
|
|
184
|
-
draft.status = 'loading';
|
|
185
|
-
draft.error = null;
|
|
186
|
-
});
|
|
187
|
-
try {
|
|
188
|
-
const geolocation = await this.messenger.call('GeolocationApiService:fetchGeolocationData', options);
|
|
163
|
+
/**
|
|
164
|
+
* Calls the geolocation service and updates controller state with the
|
|
165
|
+
* result.
|
|
166
|
+
*
|
|
167
|
+
* @param options - Options forwarded to the service.
|
|
168
|
+
* @param options.bypassCache - When true, the service skips its TTL cache.
|
|
169
|
+
* @returns The resolved geolocation data.
|
|
170
|
+
* @throws Re-throws the service error after recording it in state, so
|
|
171
|
+
* callers can react to a failed lookup instead of receiving a stale value.
|
|
172
|
+
*/
|
|
173
|
+
async #fetchAndUpdate(options) {
|
|
189
174
|
this.update((draft) => {
|
|
190
|
-
draft.
|
|
191
|
-
draft.country = geolocation.country;
|
|
192
|
-
draft.region = geolocation.region;
|
|
193
|
-
draft.timezone = geolocation.timezone;
|
|
194
|
-
draft.status = 'complete';
|
|
195
|
-
draft.lastFetchedAt = Date.now();
|
|
175
|
+
draft.status = 'loading';
|
|
196
176
|
draft.error = null;
|
|
197
177
|
});
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
178
|
+
try {
|
|
179
|
+
const geolocation = await this.messenger.call('GeolocationApiService:fetchGeolocationData', options);
|
|
180
|
+
this.update((draft) => {
|
|
181
|
+
draft.location = toLocationCode(geolocation);
|
|
182
|
+
draft.country = geolocation.country;
|
|
183
|
+
draft.region = geolocation.region;
|
|
184
|
+
draft.timezone = geolocation.timezone;
|
|
185
|
+
draft.status = 'complete';
|
|
186
|
+
draft.lastFetchedAt = Date.now();
|
|
187
|
+
draft.error = null;
|
|
188
|
+
});
|
|
189
|
+
return geolocation;
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
193
|
+
this.update((draft) => {
|
|
194
|
+
draft.status = 'error';
|
|
195
|
+
draft.error = message;
|
|
196
|
+
});
|
|
197
|
+
throw error;
|
|
198
|
+
}
|
|
207
199
|
}
|
|
208
|
-
}
|
|
209
|
-
//# sourceMappingURL=GeolocationController.
|
|
200
|
+
}
|
|
201
|
+
//# sourceMappingURL=GeolocationController.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GeolocationController.js","sourceRoot":"","sources":["../src/GeolocationController.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAK3D,OAAO,EACL,yBAAyB,EACzB,cAAc,EACd,gBAAgB,GACjB,MAAM,sDAAsD,CAAC;AAI9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,uBAAuB,CAAC;AAsBtD;;GAEG;AACH,MAAM,6BAA6B,GAAG;IACpC,QAAQ,EAAE;QACR,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,OAAO,EAAE;QACP,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,MAAM,EAAE;QACN,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,MAAM,EAAE;QACN,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,IAAI;KACf;IACD,aAAa,EAAE;QACb,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,KAAK;KAChB;IACD,KAAK,EAAE;QACL,OAAO,EAAE,KAAK;QACd,sBAAsB,EAAE,IAAI;QAC5B,kBAAkB,EAAE,IAAI;QACxB,QAAQ,EAAE,KAAK;KAChB;CACkD,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,UAAU,oCAAoC;IAClD,OAAO;QACL,QAAQ,EAAE,gBAAgB;QAC1B,GAAG,yBAAyB,EAAE;QAC9B,MAAM,EAAE,MAAM;QACd,aAAa,EAAE,IAAI;QACnB,KAAK,EAAE,IAAI;KACZ,CAAC;AACJ,CAAC;AAED,MAAM,yBAAyB,GAAG;IAChC,gBAAgB;IAChB,oBAAoB;IACpB,oBAAoB;CACZ,CAAC;AA6DX;;;;;;;;;;GAUG;AACH,MAAM,OAAO,qBAAsB,SAAQ,cAI1C;IACC;;;;;;;OAOG;IACH,YAAY,EAAE,SAAS,EAAE,KAAK,EAAgC;QAC5D,KAAK,CAAC;YACJ,SAAS;YACT,QAAQ,EAAE,6BAA6B;YACvC,IAAI,EAAE,cAAc;YACpB,KAAK,EAAE,EAAE,GAAG,oCAAoC,EAAE,EAAE,GAAG,KAAK,EAAE;SAC/D,CAAC,CAAC;QAEH,IAAI,CAAC,SAAS,CAAC,4BAA4B,CACzC,IAAI,EACJ,yBAAyB,CAC1B,CAAC;IACJ,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,cAAc;QAClB,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,EAAE,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,kBAAkB;QACtB,OAAO,IAAI,CAAC,eAAe,EAAE,CAAC;IAChC,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,kBAAkB;QACtB,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,eAAe,CAAC,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,CAAC;QAAC,MAAM,CAAC;YACP,gEAAgE;QAClE,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;IAC7B,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK,CAAC,eAAe,CAAC,OAErB;QACC,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACpB,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;YACzB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;QACrB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,CAC3C,4CAA4C,EAC5C,OAAO,CACR,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,CAAC,QAAQ,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;gBAC7C,KAAK,CAAC,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC;gBACpC,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC;gBAClC,KAAK,CAAC,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC;gBACtC,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC;gBAC1B,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBACjC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;YACrB,CAAC,CAAC,CAAC;YAEH,OAAO,WAAW,CAAC;QACrB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAEvE,IAAI,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;gBACpB,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;gBACvB,KAAK,CAAC,KAAK,GAAG,OAAO,CAAC;YACxB,CAAC,CAAC,CAAC;YAEH,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF","sourcesContent":["import type {\n ControllerGetStateAction,\n ControllerStateChangeEvent,\n StateMetadata,\n} from '@metamask/base-controller';\nimport { BaseController } from '@metamask/base-controller';\nimport type { Messenger } from '@metamask/messenger';\n\nimport type { GeolocationApiServiceFetchGeolocationDataAction } from './geolocation-api-service/geolocation-api-service-method-action-types.js';\nimport type { GeolocationData } from './geolocation-api-service/geolocation-api-service.js';\nimport {\n getUnknownGeolocationData,\n toLocationCode,\n UNKNOWN_LOCATION,\n} from './geolocation-api-service/geolocation-api-service.js';\nimport type { GeolocationControllerMethodActions } from './GeolocationController-method-action-types.js';\nimport type { GeolocationRequestStatus } from './types.js';\n\n/**\n * The name of the {@link GeolocationController}, used to namespace the\n * controller's actions and events and to namespace the controller's state data\n * when composed with other controllers.\n */\nexport const controllerName = 'GeolocationController';\n\n/**\n * State for the {@link GeolocationController}.\n */\nexport type GeolocationControllerState = {\n /** ISO 3166-2 location code (e.g. \"US\", \"US-NY\", \"CA-ON\"), or \"UNKNOWN\" if not yet determined. */\n location: string;\n /** ISO 3166-1 alpha-2 country code (e.g. \"US\"), or null if not yet determined. */\n country: string | null;\n /** ISO 3166-2 subdivision code without the country prefix (e.g. \"NY\"), or null if not yet determined. */\n region: string | null;\n /** IANA time zone name (e.g. \"America/Los_Angeles\"), or null if not yet determined. */\n timezone: string | null;\n /** Current status of the geolocation fetch lifecycle. */\n status: GeolocationRequestStatus;\n /** Epoch milliseconds of the last successful fetch, or null if never fetched. */\n lastFetchedAt: number | null;\n /** Last error message, or null if no error has occurred. */\n error: string | null;\n};\n\n/**\n * The metadata for each property in {@link GeolocationControllerState}.\n */\nconst geolocationControllerMetadata = {\n location: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n country: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n region: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n timezone: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n status: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: true,\n },\n lastFetchedAt: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n error: {\n persist: false,\n includeInDebugSnapshot: true,\n includeInStateLogs: true,\n usedInUi: false,\n },\n} satisfies StateMetadata<GeolocationControllerState>;\n\n/**\n * Constructs the default {@link GeolocationController} state. This allows\n * consumers to provide a partial state object when initializing the controller\n * and also helps in constructing complete state objects for this controller in\n * tests.\n *\n * @returns The default {@link GeolocationController} state.\n */\nexport function getDefaultGeolocationControllerState(): GeolocationControllerState {\n return {\n location: UNKNOWN_LOCATION,\n ...getUnknownGeolocationData(),\n status: 'idle',\n lastFetchedAt: null,\n error: null,\n };\n}\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'getGeolocation',\n 'getGeolocationData',\n 'refreshGeolocation',\n] as const;\n\n/**\n * Retrieves the state of the {@link GeolocationController}.\n */\nexport type GeolocationControllerGetStateAction = ControllerGetStateAction<\n typeof controllerName,\n GeolocationControllerState\n>;\n\n/**\n * Actions that {@link GeolocationControllerMessenger} exposes to other consumers.\n */\nexport type GeolocationControllerActions =\n | GeolocationControllerGetStateAction\n | GeolocationControllerMethodActions;\n\n/**\n * Actions from other messengers that {@link GeolocationControllerMessenger} calls.\n */\ntype AllowedActions = GeolocationApiServiceFetchGeolocationDataAction;\n\n/**\n * Published when the state of {@link GeolocationController} changes.\n */\nexport type GeolocationControllerStateChangeEvent = ControllerStateChangeEvent<\n typeof controllerName,\n GeolocationControllerState\n>;\n\n/**\n * Events that {@link GeolocationControllerMessenger} exposes to other consumers.\n */\nexport type GeolocationControllerEvents = GeolocationControllerStateChangeEvent;\n\n/**\n * Events from other messengers that {@link GeolocationControllerMessenger}\n * subscribes to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link GeolocationController}.\n */\nexport type GeolocationControllerMessenger = Messenger<\n typeof controllerName,\n GeolocationControllerActions | AllowedActions,\n GeolocationControllerEvents | AllowedEvents\n>;\n\n/**\n * Options for constructing the {@link GeolocationController}.\n */\nexport type GeolocationControllerOptions = {\n /** The messenger for inter-controller communication. */\n messenger: GeolocationControllerMessenger;\n /** Optional partial initial state. */\n state?: Partial<GeolocationControllerState>;\n};\n\n/**\n * GeolocationController manages UI-facing geolocation state by delegating\n * the actual API interaction to {@link GeolocationApiService} via the\n * messenger.\n *\n * The service (registered externally as\n * `GeolocationApiService:fetchGeolocation`) handles HTTP requests, response\n * validation, TTL caching, and promise deduplication. This controller focuses\n * on state lifecycle (`idle` -> `loading` -> `complete` | `error`) and\n * exposes `getGeolocation` / `refreshGeolocation` as messenger actions.\n */\nexport class GeolocationController extends BaseController<\n typeof controllerName,\n GeolocationControllerState,\n GeolocationControllerMessenger\n> {\n /**\n * Constructs a new {@link GeolocationController}.\n *\n * @param args - The arguments to this controller.\n * @param args.messenger - The messenger suited for this controller. Must\n * have a `GeolocationApiService:fetchGeolocation` action handler registered.\n * @param args.state - Optional partial initial state.\n */\n constructor({ messenger, state }: GeolocationControllerOptions) {\n super({\n messenger,\n metadata: geolocationControllerMetadata,\n name: controllerName,\n state: { ...getDefaultGeolocationControllerState(), ...state },\n });\n\n this.messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Returns the geolocation code. Delegates to the\n * {@link GeolocationApiService} for network fetching and caching, then\n * updates controller state with the result.\n *\n * Best-effort: if the fetch fails, the last known location code (or\n * {@link UNKNOWN_LOCATION}) is returned rather than throwing.\n *\n * @returns The ISO 3166-2 location code string.\n */\n async getGeolocation(): Promise<string> {\n try {\n await this.#fetchAndUpdate();\n } catch {\n // Best-effort: fall back to the last known location code below.\n }\n return this.state.location;\n }\n\n /**\n * Returns the country, region, and timezone for the current client.\n * Delegates to the {@link GeolocationApiService} for network fetching and\n * caching, then updates controller state with the result.\n *\n * Unlike {@link getGeolocation}, this rejects when resolution fails instead\n * of returning a stale value, so callers can distinguish a fresh result from\n * a failed lookup (and, for example, omit location rather than enrich with a\n * previous session's data).\n *\n * @returns The geolocation data, where each field is `null` when it could\n * not be determined.\n * @throws When the geolocation service fails to resolve.\n */\n async getGeolocationData(): Promise<GeolocationData> {\n return this.#fetchAndUpdate();\n }\n\n /**\n * Forces a fresh geolocation fetch, bypassing the service's cache.\n *\n * Best-effort: if the fetch fails, the last known location code (or\n * {@link UNKNOWN_LOCATION}) is returned rather than throwing.\n *\n * @returns The ISO 3166-2 location code string.\n */\n async refreshGeolocation(): Promise<string> {\n this.update((draft) => {\n draft.lastFetchedAt = null;\n });\n try {\n await this.#fetchAndUpdate({ bypassCache: true });\n } catch {\n // Best-effort: fall back to the last known location code below.\n }\n return this.state.location;\n }\n\n /**\n * Calls the geolocation service and updates controller state with the\n * result.\n *\n * @param options - Options forwarded to the service.\n * @param options.bypassCache - When true, the service skips its TTL cache.\n * @returns The resolved geolocation data.\n * @throws Re-throws the service error after recording it in state, so\n * callers can react to a failed lookup instead of receiving a stale value.\n */\n async #fetchAndUpdate(options?: {\n bypassCache?: boolean;\n }): Promise<GeolocationData> {\n this.update((draft) => {\n draft.status = 'loading';\n draft.error = null;\n });\n\n try {\n const geolocation = await this.messenger.call(\n 'GeolocationApiService:fetchGeolocationData',\n options,\n );\n\n this.update((draft) => {\n draft.location = toLocationCode(geolocation);\n draft.country = geolocation.country;\n draft.region = geolocation.region;\n draft.timezone = geolocation.timezone;\n draft.status = 'complete';\n draft.lastFetchedAt = Date.now();\n draft.error = null;\n });\n\n return geolocation;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n\n this.update((draft) => {\n draft.status = 'error';\n draft.error = message;\n });\n\n throw error;\n }\n }\n}\n"]}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* This file is auto generated.
|
|
3
3
|
* Do not edit manually.
|
|
4
4
|
*/
|
|
5
|
-
import type { GeolocationApiService } from
|
|
5
|
+
import type { GeolocationApiService } from './geolocation-api-service.js';
|
|
6
6
|
/**
|
|
7
7
|
* Returns the geolocation code. Serves from cache when the TTL has not
|
|
8
8
|
* expired, otherwise performs a network fetch. Concurrent callers are
|
|
@@ -39,4 +39,4 @@ export type GeolocationApiServiceFetchGeolocationDataAction = {
|
|
|
39
39
|
* Union of all GeolocationApiService action types.
|
|
40
40
|
*/
|
|
41
41
|
export type GeolocationApiServiceMethodActions = GeolocationApiServiceFetchGeolocationAction | GeolocationApiServiceFetchGeolocationDataAction;
|
|
42
|
-
//# sourceMappingURL=geolocation-api-service-method-action-types.d.
|
|
42
|
+
//# sourceMappingURL=geolocation-api-service-method-action-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geolocation-api-service-method-action-types.d.ts","sourceRoot":"","sources":["../../src/geolocation-api-service/geolocation-api-service-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAE1E;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,2CAA2C,GAAG;IACxD,IAAI,EAAE,wCAAwC,CAAC;IAC/C,OAAO,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;CACpD,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,+CAA+C,GAAG;IAC5D,IAAI,EAAE,4CAA4C,CAAC;IACnD,OAAO,EAAE,qBAAqB,CAAC,sBAAsB,CAAC,CAAC;CACxD,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,kCAAkC,GAC1C,2CAA2C,GAC3C,+CAA+C,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geolocation-api-service-method-action-types.js","sourceRoot":"","sources":["../../src/geolocation-api-service/geolocation-api-service-method-action-types.ts"],"names":[],"mappings":"AAAA;;;GAGG","sourcesContent":["/**\n * This file is auto generated.\n * Do not edit manually.\n */\n\nimport type { GeolocationApiService } from './geolocation-api-service.js';\n\n/**\n * Returns the geolocation code. Serves from cache when the TTL has not\n * expired, otherwise performs a network fetch. Concurrent callers are\n * deduplicated to a single in-flight request.\n *\n * @param options - Optional fetch options.\n * @param options.bypassCache - When true, invalidates the TTL cache. If a\n * request is already in-flight it will be reused (deduplication always\n * applies).\n * @returns An ISO 3166-2 location code (e.g. `US`, `US-NY`, `CA-ON`), or\n * {@link UNKNOWN_LOCATION} when the API returns an empty or invalid body.\n */\nexport type GeolocationApiServiceFetchGeolocationAction = {\n type: `GeolocationApiService:fetchGeolocation`;\n handler: GeolocationApiService['fetchGeolocation'];\n};\n\n/**\n * Returns the country, region, and timezone for the current client. Serves\n * from cache when the TTL has not expired, otherwise performs a network\n * fetch. Concurrent callers are deduplicated to a single in-flight request.\n *\n * @param options - Optional fetch options.\n * @param options.bypassCache - When true, invalidates the TTL cache. If a\n * request is already in-flight it will be reused (deduplication always\n * applies).\n * @returns The geolocation data, where each field is `null` when the API\n * omits it or returns a value that fails validation.\n */\nexport type GeolocationApiServiceFetchGeolocationDataAction = {\n type: `GeolocationApiService:fetchGeolocationData`;\n handler: GeolocationApiService['fetchGeolocationData'];\n};\n\n/**\n * Union of all GeolocationApiService action types.\n */\nexport type GeolocationApiServiceMethodActions =\n | GeolocationApiServiceFetchGeolocationAction\n | GeolocationApiServiceFetchGeolocationDataAction;\n"]}
|
package/dist/geolocation-api-service/{geolocation-api-service.d.cts → geolocation-api-service.d.ts}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { CreateServicePolicyOptions, ServicePolicy } from
|
|
2
|
-
import type { Messenger } from
|
|
3
|
-
import type { IDisposable } from
|
|
4
|
-
import { Env } from
|
|
5
|
-
import type { GeolocationApiServiceMethodActions } from
|
|
1
|
+
import type { CreateServicePolicyOptions, ServicePolicy } from '@metamask/controller-utils';
|
|
2
|
+
import type { Messenger } from '@metamask/messenger';
|
|
3
|
+
import type { IDisposable } from 'cockatiel';
|
|
4
|
+
import { Env } from '../types.js';
|
|
5
|
+
import type { GeolocationApiServiceMethodActions } from './geolocation-api-service-method-action-types.js';
|
|
6
6
|
/**
|
|
7
7
|
* The name of the {@link GeolocationApiService}, used to namespace the
|
|
8
8
|
* service's actions and events.
|
|
@@ -172,4 +172,4 @@ export declare class GeolocationApiService {
|
|
|
172
172
|
fetchGeolocationData(options?: FetchGeolocationOptions): Promise<GeolocationData>;
|
|
173
173
|
}
|
|
174
174
|
export {};
|
|
175
|
-
//# sourceMappingURL=geolocation-api-service.d.
|
|
175
|
+
//# sourceMappingURL=geolocation-api-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geolocation-api-service.d.ts","sourceRoot":"","sources":["../../src/geolocation-api-service/geolocation-api-service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,aAAa,EACd,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,kDAAkD,CAAC;AAc3G;;;GAGG;AACH,eAAO,MAAM,WAAW,0BAA0B,CAAC;AAEnD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,YAAY,CAAC;AAE1C;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,yDAAyD;IACzD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,0EAA0E;IAC1E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,wDAAwD;IACxD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,yBAAyB,IAAI,eAAe,CAE3D;AAUD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAU5D;AASD;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG,kCAAkC,CAAC;AAE9E;;;GAGG;AACH,KAAK,cAAc,GAAG,KAAK,CAAC;AAE5B;;GAEG;AACH,MAAM,MAAM,2BAA2B,GAAG,KAAK,CAAC;AAEhD;;;GAGG;AACH,KAAK,aAAa,GAAG,KAAK,CAAC;AAE3B;;;GAGG;AACH,MAAM,MAAM,8BAA8B,GAAG,SAAS,CACpD,OAAO,WAAW,EAClB,4BAA4B,GAAG,cAAc,EAC7C,2BAA2B,GAAG,aAAa,CAC5C,CAAC;AA+EF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,sFAAsF;IACtF,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,qBAAa,qBAAqB;;IAChC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,OAAO,WAAW,CAAC;IAuBlC;;;;;;;;;;;;OAYG;IACH,YAAY,EACV,SAAS,EACT,GAAa,EACb,KAAK,EAAE,aAAgC,EACvC,KAAK,EACL,aAAkB,GACnB,EAAE;QACD,SAAS,EAAE,8BAA8B,CAAC;QAC1C,GAAG,CAAC,EAAE,GAAG,CAAC;QACV,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;QACrB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,aAAa,CAAC,EAAE,0BAA0B,CAAC;KAC5C,EAYA;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAEtE;IAED;;;;;;;OAOG;IACH,OAAO,CAAC,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,WAAW,CAEtE;IAED;;;;;;;OAOG;IACH,UAAU,CACR,QAAQ,EAAE,UAAU,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,GACnD,WAAW,CAEb;IAED;;;;;;;;;;;OAWG;IACG,gBAAgB,CAAC,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAEzE;IAED;;;;;;;;;;;OAWG;IACG,oBAAoB,CACxB,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,eAAe,CAAC,CAqB1B;CAoDF"}
|
package/dist/geolocation-api-service/{geolocation-api-service.mjs → geolocation-api-service.js}
RENAMED
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _GeolocationApiService_instances, _GeolocationApiService_messenger, _GeolocationApiService_fetch, _GeolocationApiService_url, _GeolocationApiService_ttlMs, _GeolocationApiService_policy, _GeolocationApiService_cachedGeolocation, _GeolocationApiService_lastFetchedAt, _GeolocationApiService_fetchPromise, _GeolocationApiService_isCacheValid, _GeolocationApiService_performFetch;
|
|
13
|
-
import { createServicePolicy, HttpError } from "@metamask/controller-utils";
|
|
14
|
-
import { Env } from "../types.mjs";
|
|
1
|
+
import { createServicePolicy, HttpError } from '@metamask/controller-utils';
|
|
2
|
+
import { Env } from '../types.js';
|
|
15
3
|
const DEFAULT_TTL_MS = 5 * 60 * 1000;
|
|
16
4
|
const ENDPOINT_PATH = '/v2/geolocation';
|
|
17
5
|
const COUNTRY_PATTERN = /^[A-Z]{2}$/u;
|
|
@@ -148,6 +136,23 @@ function parseGeolocationResponse(raw) {
|
|
|
148
136
|
* packages can call them directly.
|
|
149
137
|
*/
|
|
150
138
|
export class GeolocationApiService {
|
|
139
|
+
/**
|
|
140
|
+
* The name of the service.
|
|
141
|
+
*/
|
|
142
|
+
name;
|
|
143
|
+
#messenger;
|
|
144
|
+
#fetch;
|
|
145
|
+
#url;
|
|
146
|
+
#ttlMs;
|
|
147
|
+
/**
|
|
148
|
+
* The policy that wraps each HTTP request.
|
|
149
|
+
*
|
|
150
|
+
* @see {@link createServicePolicy}
|
|
151
|
+
*/
|
|
152
|
+
#policy;
|
|
153
|
+
#cachedGeolocation = getUnknownGeolocationData();
|
|
154
|
+
#lastFetchedAt = null;
|
|
155
|
+
#fetchPromise = null;
|
|
151
156
|
/**
|
|
152
157
|
* Constructs a new {@link GeolocationApiService}.
|
|
153
158
|
*
|
|
@@ -162,27 +167,13 @@ export class GeolocationApiService {
|
|
|
162
167
|
* is used to wrap each request. See {@link CreateServicePolicyOptions}.
|
|
163
168
|
*/
|
|
164
169
|
constructor({ messenger, env = Env.PRD, fetch: fetchFunction = globalThis.fetch, ttlMs, policyOptions = {}, }) {
|
|
165
|
-
_GeolocationApiService_instances.add(this);
|
|
166
|
-
_GeolocationApiService_messenger.set(this, void 0);
|
|
167
|
-
_GeolocationApiService_fetch.set(this, void 0);
|
|
168
|
-
_GeolocationApiService_url.set(this, void 0);
|
|
169
|
-
_GeolocationApiService_ttlMs.set(this, void 0);
|
|
170
|
-
/**
|
|
171
|
-
* The policy that wraps each HTTP request.
|
|
172
|
-
*
|
|
173
|
-
* @see {@link createServicePolicy}
|
|
174
|
-
*/
|
|
175
|
-
_GeolocationApiService_policy.set(this, void 0);
|
|
176
|
-
_GeolocationApiService_cachedGeolocation.set(this, getUnknownGeolocationData());
|
|
177
|
-
_GeolocationApiService_lastFetchedAt.set(this, null);
|
|
178
|
-
_GeolocationApiService_fetchPromise.set(this, null);
|
|
179
170
|
this.name = serviceName;
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
171
|
+
this.#messenger = messenger;
|
|
172
|
+
this.#url = getGeolocationUrl(env);
|
|
173
|
+
this.#fetch = fetchFunction;
|
|
174
|
+
this.#ttlMs = ttlMs ?? DEFAULT_TTL_MS;
|
|
175
|
+
this.#policy = createServicePolicy(policyOptions);
|
|
176
|
+
this.#messenger.registerMethodActionHandlers(this, MESSENGER_EXPOSED_METHODS);
|
|
186
177
|
}
|
|
187
178
|
/**
|
|
188
179
|
* Registers a handler that will be called after a request returns a 5xx
|
|
@@ -193,7 +184,7 @@ export class GeolocationApiService {
|
|
|
193
184
|
* @see {@link createServicePolicy}
|
|
194
185
|
*/
|
|
195
186
|
onRetry(listener) {
|
|
196
|
-
return
|
|
187
|
+
return this.#policy.onRetry(listener);
|
|
197
188
|
}
|
|
198
189
|
/**
|
|
199
190
|
* Registers a handler that will be called after a set number of retry rounds
|
|
@@ -204,7 +195,7 @@ export class GeolocationApiService {
|
|
|
204
195
|
* @see {@link createServicePolicy}
|
|
205
196
|
*/
|
|
206
197
|
onBreak(listener) {
|
|
207
|
-
return
|
|
198
|
+
return this.#policy.onBreak(listener);
|
|
208
199
|
}
|
|
209
200
|
/**
|
|
210
201
|
* Registers a handler that will be called when requests are consistently
|
|
@@ -215,7 +206,7 @@ export class GeolocationApiService {
|
|
|
215
206
|
* @returns An object that can be used to unregister the handler.
|
|
216
207
|
*/
|
|
217
208
|
onDegraded(listener) {
|
|
218
|
-
return
|
|
209
|
+
return this.#policy.onDegraded(listener);
|
|
219
210
|
}
|
|
220
211
|
/**
|
|
221
212
|
* Returns the geolocation code. Serves from cache when the TTL has not
|
|
@@ -246,54 +237,59 @@ export class GeolocationApiService {
|
|
|
246
237
|
*/
|
|
247
238
|
async fetchGeolocationData(options) {
|
|
248
239
|
if (options?.bypassCache) {
|
|
249
|
-
|
|
240
|
+
this.#lastFetchedAt = null;
|
|
250
241
|
}
|
|
251
|
-
if (
|
|
252
|
-
return
|
|
242
|
+
if (this.#isCacheValid()) {
|
|
243
|
+
return this.#cachedGeolocation;
|
|
253
244
|
}
|
|
254
|
-
if (
|
|
255
|
-
return
|
|
245
|
+
if (this.#fetchPromise) {
|
|
246
|
+
return this.#fetchPromise;
|
|
256
247
|
}
|
|
257
|
-
const promise =
|
|
258
|
-
|
|
248
|
+
const promise = this.#performFetch();
|
|
249
|
+
this.#fetchPromise = promise;
|
|
259
250
|
try {
|
|
260
251
|
return await promise;
|
|
261
252
|
}
|
|
262
253
|
finally {
|
|
263
|
-
|
|
254
|
+
this.#fetchPromise = null;
|
|
264
255
|
}
|
|
265
256
|
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Checks whether the cached geolocation is still within the TTL window.
|
|
259
|
+
*
|
|
260
|
+
* @returns True if the cache is valid.
|
|
261
|
+
*/
|
|
262
|
+
#isCacheValid() {
|
|
263
|
+
return (this.#lastFetchedAt !== null &&
|
|
264
|
+
Date.now() - this.#lastFetchedAt < this.#ttlMs);
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Performs the actual HTTP fetch, wrapped in the service policy for automatic
|
|
268
|
+
* retry and circuit-breaking, and validates the response.
|
|
269
|
+
*
|
|
270
|
+
* @returns The validated geolocation data.
|
|
271
|
+
*/
|
|
272
|
+
async #performFetch() {
|
|
273
|
+
const response = await this.#policy.execute(async () => {
|
|
274
|
+
const localResponse = await this.#fetch(this.#url);
|
|
275
|
+
if (!localResponse.ok) {
|
|
276
|
+
throw new HttpError(localResponse.status, `Geolocation fetch failed: ${localResponse.status}`);
|
|
277
|
+
}
|
|
278
|
+
return localResponse;
|
|
279
|
+
});
|
|
280
|
+
const geolocation = parseGeolocationResponse((await response.text()).trim());
|
|
281
|
+
// Cache whenever at least one field resolved. A partially-known result
|
|
282
|
+
// (e.g. timezone without a valid country) is still worth caching so we do
|
|
283
|
+
// not re-fetch it within the TTL window; only a fully-unknown response is
|
|
284
|
+
// left uncached so it can be retried.
|
|
285
|
+
const hasKnownField = geolocation.country !== null ||
|
|
286
|
+
geolocation.region !== null ||
|
|
287
|
+
geolocation.timezone !== null;
|
|
288
|
+
if (hasKnownField) {
|
|
289
|
+
this.#cachedGeolocation = geolocation;
|
|
290
|
+
this.#lastFetchedAt = Date.now();
|
|
282
291
|
}
|
|
283
|
-
return
|
|
284
|
-
});
|
|
285
|
-
const geolocation = parseGeolocationResponse((await response.text()).trim());
|
|
286
|
-
// Cache whenever at least one field resolved. A partially-known result
|
|
287
|
-
// (e.g. timezone without a valid country) is still worth caching so we do
|
|
288
|
-
// not re-fetch it within the TTL window; only a fully-unknown response is
|
|
289
|
-
// left uncached so it can be retried.
|
|
290
|
-
const hasKnownField = geolocation.country !== null ||
|
|
291
|
-
geolocation.region !== null ||
|
|
292
|
-
geolocation.timezone !== null;
|
|
293
|
-
if (hasKnownField) {
|
|
294
|
-
__classPrivateFieldSet(this, _GeolocationApiService_cachedGeolocation, geolocation, "f");
|
|
295
|
-
__classPrivateFieldSet(this, _GeolocationApiService_lastFetchedAt, Date.now(), "f");
|
|
292
|
+
return geolocation;
|
|
296
293
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
//# sourceMappingURL=geolocation-api-service.mjs.map
|
|
294
|
+
}
|
|
295
|
+
//# sourceMappingURL=geolocation-api-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geolocation-api-service.js","sourceRoot":"","sources":["../../src/geolocation-api-service/geolocation-api-service.ts"],"names":[],"mappings":"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","sourcesContent":["import type {\n CreateServicePolicyOptions,\n ServicePolicy,\n} from '@metamask/controller-utils';\nimport { createServicePolicy, HttpError } from '@metamask/controller-utils';\nimport type { Messenger } from '@metamask/messenger';\nimport type { IDisposable } from 'cockatiel';\n\nimport { Env } from '../types.js';\nimport type { GeolocationApiServiceMethodActions } from './geolocation-api-service-method-action-types.js';\n\nconst DEFAULT_TTL_MS = 5 * 60 * 1000;\n\nconst ENDPOINT_PATH = '/v2/geolocation';\n\nconst COUNTRY_PATTERN = /^[A-Z]{2}$/u;\n\nconst REGION_PATTERN = /^[A-Z0-9]{1,3}$/u;\n\nconst TIMEZONE_PATTERN = /^[A-Za-z][A-Za-z0-9_+-]*(?:\\/[A-Za-z0-9_+-]+)*$/u;\n\n// === GENERAL ===\n\n/**\n * The name of the {@link GeolocationApiService}, used to namespace the\n * service's actions and events.\n */\nexport const serviceName = 'GeolocationApiService';\n\n/**\n * Sentinel value used when the geolocation has not been determined yet or when\n * the API returns an empty / invalid response.\n */\nexport const UNKNOWN_LOCATION = 'UNKNOWN';\n\n/**\n * Geolocation details returned by the geolocation API.\n *\n * Each field is `null` when the API omits it or returns a value that fails\n * validation.\n */\nexport type GeolocationData = {\n /** ISO 3166-1 alpha-2 country code (e.g. `US`, `FR`). */\n country: string | null;\n /** ISO 3166-2 subdivision code without the country prefix (e.g. `WA`). */\n region: string | null;\n /** IANA time zone name (e.g. `America/Los_Angeles`). */\n timezone: string | null;\n};\n\n/**\n * Constructs a {@link GeolocationData} with no known fields.\n *\n * @returns Geolocation data where every field is `null`.\n */\nexport function getUnknownGeolocationData(): GeolocationData {\n return { country: null, region: null, timezone: null };\n}\n\n/**\n * Country codes for which the location code includes the region. This mirrors\n * the legacy `v1` geolocation endpoint, which only appended the subdivision for\n * the United States and Canada (e.g. `US-NY`, `CA-ON`) and returned the country\n * alone for everywhere else.\n */\nconst REGION_APPENDED_COUNTRIES = new Set(['US', 'CA']);\n\n/**\n * Converts geolocation data to a location code.\n *\n * To preserve backwards compatibility with the legacy `v1` endpoint, the region\n * is appended only for {@link REGION_APPENDED_COUNTRIES} (e.g. `US-NY`,\n * `CA-ON`); all other countries return the country code alone (e.g. `FR`), even\n * when a region is known.\n *\n * @param data - The geolocation data to convert.\n * @returns The location code (e.g. `US-NY`, `FR`), or\n * {@link UNKNOWN_LOCATION} when the country is unknown.\n */\nexport function toLocationCode(data: GeolocationData): string {\n if (data.country === null) {\n return UNKNOWN_LOCATION;\n }\n\n if (data.region !== null && REGION_APPENDED_COUNTRIES.has(data.country)) {\n return `${data.country}-${data.region}`;\n }\n\n return data.country;\n}\n\n// === MESSENGER ===\n\nconst MESSENGER_EXPOSED_METHODS = [\n 'fetchGeolocation',\n 'fetchGeolocationData',\n] as const;\n\n/**\n * Actions that {@link GeolocationApiService} exposes to other consumers.\n */\nexport type GeolocationApiServiceActions = GeolocationApiServiceMethodActions;\n\n/**\n * Actions from other messengers that {@link GeolocationApiServiceMessenger}\n * calls.\n */\ntype AllowedActions = never;\n\n/**\n * Events that {@link GeolocationApiService} exposes to other consumers.\n */\nexport type GeolocationApiServiceEvents = never;\n\n/**\n * Events from other messengers that {@link GeolocationApiService} subscribes\n * to.\n */\ntype AllowedEvents = never;\n\n/**\n * The messenger restricted to actions and events accessed by\n * {@link GeolocationApiService}.\n */\nexport type GeolocationApiServiceMessenger = Messenger<\n typeof serviceName,\n GeolocationApiServiceActions | AllowedActions,\n GeolocationApiServiceEvents | AllowedEvents\n>;\n\n// === SERVICE DEFINITION ===\n\n/**\n * Returns the base URL for the geolocation API for the given environment.\n *\n * Served by API Platform's `geolocation-api` service, not the legacy\n * Ramps-owned `on-ramp` endpoint this previously pointed to. API Platform has\n * not yet provisioned a dedicated UAT deployment for this service, so UAT\n * temporarily resolves to the production URL until one exists.\n *\n * @param env - The environment to get the URL for.\n * @returns The full URL for the geolocation endpoint.\n */\nfunction getGeolocationUrl(env: Env): string {\n const envPrefix = env === Env.DEV ? 'dev-' : '';\n return `https://geolocation.${envPrefix}api.cx.metamask.io${ENDPOINT_PATH}`;\n}\n\n/**\n * Reads a string field from a parsed response body, keeping it only when it\n * matches the expected format.\n *\n * @param body - The parsed response body.\n * @param field - The name of the field to read.\n * @param pattern - The pattern the field value must match.\n * @returns The trimmed field value, or `null` when it is missing or invalid.\n */\nfunction readValidatedField(\n body: Record<string, unknown>,\n field: string,\n pattern: RegExp,\n): string | null {\n const value = body[field];\n\n if (typeof value !== 'string') {\n return null;\n }\n\n const trimmed = value.trim();\n\n return pattern.test(trimmed) ? trimmed : null;\n}\n\n/**\n * Parses and validates the geolocation API response body.\n *\n * The endpoint is expected to return JSON such as\n * `{\"country\":\"US\",\"region\":\"WA\",\"timezone\":\"America/Los_Angeles\"}`. Anything\n * that cannot be parsed, or any individual field that fails validation, is\n * reported as unknown rather than throwing, so that consumers can keep working\n * with partial or missing data.\n *\n * @param raw - The raw response body.\n * @returns The validated geolocation data.\n */\nfunction parseGeolocationResponse(raw: string): GeolocationData {\n let body: unknown;\n\n try {\n body = JSON.parse(raw);\n } catch {\n return getUnknownGeolocationData();\n }\n\n if (typeof body !== 'object' || body === null || Array.isArray(body)) {\n return getUnknownGeolocationData();\n }\n\n const record = body as Record<string, unknown>;\n\n return {\n country: readValidatedField(record, 'country', COUNTRY_PATTERN),\n region: readValidatedField(record, 'region', REGION_PATTERN),\n timezone: readValidatedField(record, 'timezone', TIMEZONE_PATTERN),\n };\n}\n\n/**\n * Options accepted by {@link GeolocationApiService.fetchGeolocation}.\n */\nexport type FetchGeolocationOptions = {\n /** When true, the TTL cache is invalidated so the next request fetches fresh data. */\n bypassCache?: boolean;\n};\n\n/**\n * Low-level data service that fetches geolocation details from the geolocation\n * API.\n *\n * Responsibilities:\n * - HTTP request to the geolocation endpoint (wrapped in a service policy)\n * - Response validation of the country, region, and timezone fields\n * - TTL-based in-memory cache\n * - Promise deduplication (concurrent callers share a single in-flight request)\n *\n * This class is intentionally not a controller: it does not manage UI state.\n * Its {@link fetchGeolocation} and {@link fetchGeolocationData} methods are\n * automatically registered on the messenger so that controllers and other\n * packages can call them directly.\n */\nexport class GeolocationApiService {\n /**\n * The name of the service.\n */\n readonly name: typeof serviceName;\n\n readonly #messenger: GeolocationApiServiceMessenger;\n\n readonly #fetch: typeof globalThis.fetch;\n\n readonly #url: string;\n\n readonly #ttlMs: number;\n\n /**\n * The policy that wraps each HTTP request.\n *\n * @see {@link createServicePolicy}\n */\n readonly #policy: ServicePolicy;\n\n #cachedGeolocation: GeolocationData = getUnknownGeolocationData();\n\n #lastFetchedAt: number | null = null;\n\n #fetchPromise: Promise<GeolocationData> | null = null;\n\n /**\n * Constructs a new {@link GeolocationApiService}.\n *\n * @param args - The constructor arguments.\n * @param args.messenger - The messenger suited for this service.\n * @param args.env - The environment to determine the correct API endpoint.\n * Defaults to PRD.\n * @param args.fetch - A function that can be used to make an HTTP request.\n * Defaults to the global fetch.\n * @param args.ttlMs - Cache TTL in milliseconds. Defaults to 5 minutes.\n * @param args.policyOptions - Options to pass to `createServicePolicy`, which\n * is used to wrap each request. See {@link CreateServicePolicyOptions}.\n */\n constructor({\n messenger,\n env = Env.PRD,\n fetch: fetchFunction = globalThis.fetch,\n ttlMs,\n policyOptions = {},\n }: {\n messenger: GeolocationApiServiceMessenger;\n env?: Env;\n fetch?: typeof fetch;\n ttlMs?: number;\n policyOptions?: CreateServicePolicyOptions;\n }) {\n this.name = serviceName;\n this.#messenger = messenger;\n this.#url = getGeolocationUrl(env);\n this.#fetch = fetchFunction;\n this.#ttlMs = ttlMs ?? DEFAULT_TTL_MS;\n this.#policy = createServicePolicy(policyOptions);\n\n this.#messenger.registerMethodActionHandlers(\n this,\n MESSENGER_EXPOSED_METHODS,\n );\n }\n\n /**\n * Registers a handler that will be called after a request returns a 5xx\n * response, causing a retry.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler.\n * @see {@link createServicePolicy}\n */\n onRetry(listener: Parameters<ServicePolicy['onRetry']>[0]): IDisposable {\n return this.#policy.onRetry(listener);\n }\n\n /**\n * Registers a handler that will be called after a set number of retry rounds\n * prove that requests to the API endpoint consistently return a 5xx response.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler.\n * @see {@link createServicePolicy}\n */\n onBreak(listener: Parameters<ServicePolicy['onBreak']>[0]): IDisposable {\n return this.#policy.onBreak(listener);\n }\n\n /**\n * Registers a handler that will be called when requests are consistently\n * failing or when a successful request takes longer than the degraded\n * threshold.\n *\n * @param listener - The handler to be called.\n * @returns An object that can be used to unregister the handler.\n */\n onDegraded(\n listener: Parameters<ServicePolicy['onDegraded']>[0],\n ): IDisposable {\n return this.#policy.onDegraded(listener);\n }\n\n /**\n * Returns the geolocation code. Serves from cache when the TTL has not\n * expired, otherwise performs a network fetch. Concurrent callers are\n * deduplicated to a single in-flight request.\n *\n * @param options - Optional fetch options.\n * @param options.bypassCache - When true, invalidates the TTL cache. If a\n * request is already in-flight it will be reused (deduplication always\n * applies).\n * @returns An ISO 3166-2 location code (e.g. `US`, `US-NY`, `CA-ON`), or\n * {@link UNKNOWN_LOCATION} when the API returns an empty or invalid body.\n */\n async fetchGeolocation(options?: FetchGeolocationOptions): Promise<string> {\n return toLocationCode(await this.fetchGeolocationData(options));\n }\n\n /**\n * Returns the country, region, and timezone for the current client. Serves\n * from cache when the TTL has not expired, otherwise performs a network\n * fetch. Concurrent callers are deduplicated to a single in-flight request.\n *\n * @param options - Optional fetch options.\n * @param options.bypassCache - When true, invalidates the TTL cache. If a\n * request is already in-flight it will be reused (deduplication always\n * applies).\n * @returns The geolocation data, where each field is `null` when the API\n * omits it or returns a value that fails validation.\n */\n async fetchGeolocationData(\n options?: FetchGeolocationOptions,\n ): Promise<GeolocationData> {\n if (options?.bypassCache) {\n this.#lastFetchedAt = null;\n }\n\n if (this.#isCacheValid()) {\n return this.#cachedGeolocation;\n }\n\n if (this.#fetchPromise) {\n return this.#fetchPromise;\n }\n\n const promise = this.#performFetch();\n this.#fetchPromise = promise;\n\n try {\n return await promise;\n } finally {\n this.#fetchPromise = null;\n }\n }\n\n /**\n * Checks whether the cached geolocation is still within the TTL window.\n *\n * @returns True if the cache is valid.\n */\n #isCacheValid(): boolean {\n return (\n this.#lastFetchedAt !== null &&\n Date.now() - this.#lastFetchedAt < this.#ttlMs\n );\n }\n\n /**\n * Performs the actual HTTP fetch, wrapped in the service policy for automatic\n * retry and circuit-breaking, and validates the response.\n *\n * @returns The validated geolocation data.\n */\n async #performFetch(): Promise<GeolocationData> {\n const response = await this.#policy.execute(async () => {\n const localResponse = await this.#fetch(this.#url);\n if (!localResponse.ok) {\n throw new HttpError(\n localResponse.status,\n `Geolocation fetch failed: ${localResponse.status}`,\n );\n }\n return localResponse;\n });\n\n const geolocation = parseGeolocationResponse(\n (await response.text()).trim(),\n );\n\n // Cache whenever at least one field resolved. A partially-known result\n // (e.g. timezone without a valid country) is still worth caching so we do\n // not re-fetch it within the TTL window; only a fully-unknown response is\n // left uncached so it can be retried.\n const hasKnownField =\n geolocation.country !== null ||\n geolocation.region !== null ||\n geolocation.timezone !== null;\n\n if (hasKnownField) {\n this.#cachedGeolocation = geolocation;\n this.#lastFetchedAt = Date.now();\n }\n\n return geolocation;\n }\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type { GeolocationControllerState, GeolocationControllerGetStateAction, GeolocationControllerActions, GeolocationControllerStateChangeEvent, GeolocationControllerEvents, GeolocationControllerMessenger, GeolocationControllerOptions, } from './GeolocationController.js';
|
|
2
|
+
export type { GeolocationControllerGetGeolocationAction, GeolocationControllerGetGeolocationDataAction, GeolocationControllerRefreshGeolocationAction, } from './GeolocationController-method-action-types.js';
|
|
3
|
+
export type { GeolocationRequestStatus } from './types.js';
|
|
4
|
+
export { Env } from './types.js';
|
|
5
|
+
export { GeolocationController, getDefaultGeolocationControllerState, } from './GeolocationController.js';
|
|
6
|
+
export { GeolocationApiService, getUnknownGeolocationData, toLocationCode, UNKNOWN_LOCATION, } from './geolocation-api-service/geolocation-api-service.js';
|
|
7
|
+
export type { GeolocationApiServiceMessenger, GeolocationApiServiceActions, GeolocationApiServiceEvents, FetchGeolocationOptions, GeolocationData, } from './geolocation-api-service/geolocation-api-service.js';
|
|
8
|
+
export type { GeolocationApiServiceFetchGeolocationAction, GeolocationApiServiceFetchGeolocationDataAction, } from './geolocation-api-service/geolocation-api-service-method-action-types.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,0BAA0B,EAC1B,mCAAmC,EACnC,4BAA4B,EAC5B,qCAAqC,EACrC,2BAA2B,EAC3B,8BAA8B,EAC9B,4BAA4B,GAC7B,MAAM,4BAA4B,CAAC;AACpC,YAAY,EACV,yCAAyC,EACzC,6CAA6C,EAC7C,6CAA6C,GAC9C,MAAM,gDAAgD,CAAC;AACxD,YAAY,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EACL,qBAAqB,EACrB,oCAAoC,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,cAAc,EACd,gBAAgB,GACjB,MAAM,sDAAsD,CAAC;AAC9D,YAAY,EACV,8BAA8B,EAC9B,4BAA4B,EAC5B,2BAA2B,EAC3B,uBAAuB,EACvB,eAAe,GAChB,MAAM,sDAAsD,CAAC;AAC9D,YAAY,EACV,2CAA2C,EAC3C,+CAA+C,GAChD,MAAM,0EAA0E,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { Env } from './types.js';
|
|
2
|
+
export { GeolocationController, getDefaultGeolocationControllerState, } from './GeolocationController.js';
|
|
3
|
+
export { GeolocationApiService, getUnknownGeolocationData, toLocationCode, UNKNOWN_LOCATION, } from './geolocation-api-service/geolocation-api-service.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAeA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EACL,qBAAqB,EACrB,oCAAoC,GACrC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,qBAAqB,EACrB,yBAAyB,EACzB,cAAc,EACd,gBAAgB,GACjB,MAAM,sDAAsD,CAAC","sourcesContent":["export type {\n GeolocationControllerState,\n GeolocationControllerGetStateAction,\n GeolocationControllerActions,\n GeolocationControllerStateChangeEvent,\n GeolocationControllerEvents,\n GeolocationControllerMessenger,\n GeolocationControllerOptions,\n} from './GeolocationController.js';\nexport type {\n GeolocationControllerGetGeolocationAction,\n GeolocationControllerGetGeolocationDataAction,\n GeolocationControllerRefreshGeolocationAction,\n} from './GeolocationController-method-action-types.js';\nexport type { GeolocationRequestStatus } from './types.js';\nexport { Env } from './types.js';\nexport {\n GeolocationController,\n getDefaultGeolocationControllerState,\n} from './GeolocationController.js';\nexport {\n GeolocationApiService,\n getUnknownGeolocationData,\n toLocationCode,\n UNKNOWN_LOCATION,\n} from './geolocation-api-service/geolocation-api-service.js';\nexport type {\n GeolocationApiServiceMessenger,\n GeolocationApiServiceActions,\n GeolocationApiServiceEvents,\n FetchGeolocationOptions,\n GeolocationData,\n} from './geolocation-api-service/geolocation-api-service.js';\nexport type {\n GeolocationApiServiceFetchGeolocationAction,\n GeolocationApiServiceFetchGeolocationDataAction,\n} from './geolocation-api-service/geolocation-api-service-method-action-types.js';\n"]}
|