@india-boundary-corrector/service-worker 0.0.3 → 0.0.5
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 +48 -8
- package/dist/index.cjs +57 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.global.js +245 -66
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +57 -8
- package/dist/index.js.map +1 -1
- package/dist/india_boundary_corrections.pmtiles +0 -0
- package/dist/india_boundary_corrections.pmtiles.gz +0 -0
- package/dist/worker.global.js +481 -196
- package/dist/worker.global.js.map +1 -1
- package/package.json +4 -4
- package/src/constants.js +19 -0
- package/src/index.d.ts +12 -5
- package/src/index.js +66 -30
- package/src/worker.js +138 -80
package/README.md
CHANGED
|
@@ -17,9 +17,11 @@ npm install @india-boundary-corrector/service-worker
|
|
|
17
17
|
Create `sw.js` in your public directory:
|
|
18
18
|
|
|
19
19
|
```javascript
|
|
20
|
-
importScripts('https://
|
|
20
|
+
importScripts('https://cdn.jsdelivr.net/npm/@india-boundary-corrector/service-worker/dist/worker.global.js');
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
> **Note:** We use jsDelivr instead of unpkg because `importScripts()` does not follow HTTP redirects, and unpkg uses redirects for bare package URLs.
|
|
24
|
+
|
|
23
25
|
Or if bundling:
|
|
24
26
|
|
|
25
27
|
```javascript
|
|
@@ -43,13 +45,18 @@ The service worker intercepts tile requests transparently:
|
|
|
43
45
|
|
|
44
46
|
```javascript
|
|
45
47
|
// Leaflet
|
|
46
|
-
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
|
48
|
+
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
49
|
+
attribution: '© OpenStreetMap contributors'
|
|
50
|
+
}).addTo(map);
|
|
47
51
|
|
|
48
52
|
// OpenLayers
|
|
49
|
-
new TileLayer({ source: new XYZ({
|
|
53
|
+
new TileLayer({ source: new XYZ({
|
|
54
|
+
url: 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png',
|
|
55
|
+
attributions: '© OpenStreetMap contributors'
|
|
56
|
+
}) });
|
|
50
57
|
|
|
51
58
|
// MapLibre
|
|
52
|
-
{ type: 'raster', tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'] }
|
|
59
|
+
{ type: 'raster', tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], attribution: '© OpenStreetMap contributors' }
|
|
53
60
|
```
|
|
54
61
|
|
|
55
62
|
### With Custom Layer Config
|
|
@@ -83,13 +90,15 @@ For projects without a module bundler, you can use the CDN builds directly.
|
|
|
83
90
|
Create `sw.js` in your public directory. This file **must** be hosted on your own domain (service workers cannot be loaded from a CDN):
|
|
84
91
|
|
|
85
92
|
```javascript
|
|
86
|
-
importScripts('https://
|
|
93
|
+
importScripts('https://cdn.jsdelivr.net/npm/@india-boundary-corrector/service-worker/dist/worker.global.js');
|
|
87
94
|
```
|
|
88
95
|
|
|
96
|
+
> **Note:** We use jsDelivr instead of unpkg because `importScripts()` does not follow HTTP redirects, and unpkg uses redirects for bare package URLs.
|
|
97
|
+
|
|
89
98
|
### 2. Include the Script and Register
|
|
90
99
|
|
|
91
100
|
```html
|
|
92
|
-
<script src="https://
|
|
101
|
+
<script src="https://cdn.jsdelivr.net/npm/@india-boundary-corrector/service-worker/dist/index.global.js"></script>
|
|
93
102
|
<script>
|
|
94
103
|
// The library is available as IndiaBoundaryCorrector on the global window object
|
|
95
104
|
const { registerCorrectionServiceWorker, LayerConfig } = IndiaBoundaryCorrector;
|
|
@@ -98,7 +107,9 @@ importScripts('https://unpkg.com/@india-boundary-corrector/service-worker/dist/w
|
|
|
98
107
|
console.log('Service worker registered');
|
|
99
108
|
|
|
100
109
|
// Now any matching tile requests will be automatically corrected
|
|
101
|
-
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
|
|
110
|
+
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
111
|
+
attribution: '© OpenStreetMap contributors'
|
|
112
|
+
}).addTo(map);
|
|
102
113
|
});
|
|
103
114
|
</script>
|
|
104
115
|
```
|
|
@@ -106,7 +117,7 @@ importScripts('https://unpkg.com/@india-boundary-corrector/service-worker/dist/w
|
|
|
106
117
|
### With Custom Layer Config (IIFE)
|
|
107
118
|
|
|
108
119
|
```html
|
|
109
|
-
<script src="https://
|
|
120
|
+
<script src="https://cdn.jsdelivr.net/npm/@india-boundary-corrector/service-worker/dist/index.global.js"></script>
|
|
110
121
|
<script>
|
|
111
122
|
const { registerCorrectionServiceWorker, LayerConfig } = IndiaBoundaryCorrector;
|
|
112
123
|
|
|
@@ -133,9 +144,32 @@ Register the service worker and wait for it to take control.
|
|
|
133
144
|
| `options.scope` | string | Service worker scope |
|
|
134
145
|
| `options.pmtilesUrl` | string | PMTiles URL to use |
|
|
135
146
|
| `options.controllerTimeout` | number | Timeout for SW to take control (default: 3000ms) |
|
|
147
|
+
| `options.forceReinstall` | boolean | Unregister existing SW before registering (default: false) |
|
|
136
148
|
|
|
137
149
|
Returns: `Promise<CorrectionServiceWorker>`
|
|
138
150
|
|
|
151
|
+
#### PMTiles URL
|
|
152
|
+
|
|
153
|
+
**Important:** The service worker cannot auto-detect the PMTiles URL from `import.meta.url` or `document.currentScript` since it runs in a worker context. If you don't specify `pmtilesUrl`, it will default to fetching from the jsDelivr CDN.
|
|
154
|
+
|
|
155
|
+
For local development or self-hosted deployments, always specify the PMTiles URL:
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
const sw = await registerCorrectionServiceWorker('./sw.js', {
|
|
159
|
+
pmtilesUrl: '/path/to/india_boundary_corrections.pmtiles'
|
|
160
|
+
});
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
#### Development Mode
|
|
164
|
+
|
|
165
|
+
Use `forceReinstall: true` during development to ensure you always get a fresh service worker:
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
const sw = await registerCorrectionServiceWorker('./sw.js', {
|
|
169
|
+
forceReinstall: true // Useful when iterating on SW code
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
139
173
|
### `CorrectionServiceWorker`
|
|
140
174
|
|
|
141
175
|
#### Methods
|
|
@@ -149,6 +183,8 @@ Returns: `Promise<CorrectionServiceWorker>`
|
|
|
149
183
|
| `removeLayerConfig(id)` | `Promise<void>` | Remove a layer config |
|
|
150
184
|
| `setPmtilesUrl(url)` | `Promise<void>` | Set PMTiles URL |
|
|
151
185
|
| `setEnabled(enabled)` | `Promise<void>` | Enable/disable corrections |
|
|
186
|
+
| `setFallbackOnCorrectionFailure(fallback)` | `Promise<void>` | Set whether to return original tile if corrections fail |
|
|
187
|
+
| `setCacheMaxFeatures(count)` | `Promise<void>` | Set maximum features to cache |
|
|
152
188
|
| `clearCache()` | `Promise<void>` | Clear the tile cache |
|
|
153
189
|
| `getStatus()` | `Promise<Object>` | Get SW status |
|
|
154
190
|
| `resetConfig()` | `Promise<void>` | Reset to default pmtilesUrl and layer configs |
|
|
@@ -171,6 +207,10 @@ The SW scope must include the page that registers it. Typically:
|
|
|
171
207
|
- Place `sw.js` in your app's root directory
|
|
172
208
|
- Or specify a narrower scope if needed
|
|
173
209
|
|
|
210
|
+
## Bundling
|
|
211
|
+
|
|
212
|
+
If you're bundling your application (Rollup, Webpack, Vite, etc.), you may need to copy the PMTiles data file to your output directory. See **[Bundling the PMTiles Asset](../data/bundling-pmtiles.md)** for instructions.
|
|
213
|
+
|
|
174
214
|
## License
|
|
175
215
|
|
|
176
216
|
Unlicense
|
package/dist/index.cjs
CHANGED
|
@@ -24,22 +24,28 @@ __export(index_exports, {
|
|
|
24
24
|
LayerConfig: () => import_layer_configs.LayerConfig,
|
|
25
25
|
MessageTypes: () => MessageTypes,
|
|
26
26
|
getPmtilesUrl: () => import_data.getPmtilesUrl,
|
|
27
|
-
getWorkerImportSnippet: () => getWorkerImportSnippet,
|
|
28
27
|
layerConfigs: () => import_layer_configs.layerConfigs,
|
|
29
28
|
registerCorrectionServiceWorker: () => registerCorrectionServiceWorker
|
|
30
29
|
});
|
|
31
30
|
module.exports = __toCommonJS(index_exports);
|
|
32
31
|
var import_layer_configs = require("@india-boundary-corrector/layer-configs");
|
|
33
32
|
var import_data = require("@india-boundary-corrector/data");
|
|
33
|
+
|
|
34
|
+
// src/constants.js
|
|
34
35
|
var MessageTypes = {
|
|
35
36
|
ADD_LAYER_CONFIG: "ADD_LAYER_CONFIG",
|
|
36
37
|
REMOVE_LAYER_CONFIG: "REMOVE_LAYER_CONFIG",
|
|
37
38
|
SET_PMTILES_URL: "SET_PMTILES_URL",
|
|
38
39
|
SET_ENABLED: "SET_ENABLED",
|
|
40
|
+
SET_FALLBACK_ON_CORRECTION_FAILURE: "SET_FALLBACK_ON_CORRECTION_FAILURE",
|
|
41
|
+
SET_CACHE_MAX_FEATURES: "SET_CACHE_MAX_FEATURES",
|
|
39
42
|
CLEAR_CACHE: "CLEAR_CACHE",
|
|
40
43
|
GET_STATUS: "GET_STATUS",
|
|
41
|
-
RESET_CONFIG: "RESET_CONFIG"
|
|
44
|
+
RESET_CONFIG: "RESET_CONFIG",
|
|
45
|
+
CLAIM_CLIENTS: "CLAIM_CLIENTS"
|
|
42
46
|
};
|
|
47
|
+
|
|
48
|
+
// src/index.js
|
|
43
49
|
var CorrectionServiceWorker = class {
|
|
44
50
|
/**
|
|
45
51
|
* @param {string} workerUrl - URL to the service worker script
|
|
@@ -47,12 +53,14 @@ var CorrectionServiceWorker = class {
|
|
|
47
53
|
* @param {string} [options.scope] - Service worker scope (defaults to workerUrl directory)
|
|
48
54
|
* @param {string} [options.pmtilesUrl] - PMTiles URL to set after registration
|
|
49
55
|
* @param {number} [options.controllerTimeout=3000] - Timeout in ms to wait for SW to take control
|
|
56
|
+
* @param {boolean} [options.forceReinstall=false] - Unregister existing SW before registering (useful for dev)
|
|
50
57
|
*/
|
|
51
58
|
constructor(workerUrl, options = {}) {
|
|
52
59
|
this._workerUrl = workerUrl;
|
|
53
60
|
this._scope = options.scope;
|
|
54
61
|
this._pmtilesUrl = options.pmtilesUrl;
|
|
55
62
|
this._controllerTimeout = options.controllerTimeout ?? 3e3;
|
|
63
|
+
this._forceReinstall = options.forceReinstall ?? false;
|
|
56
64
|
this._registration = null;
|
|
57
65
|
}
|
|
58
66
|
/**
|
|
@@ -64,6 +72,12 @@ var CorrectionServiceWorker = class {
|
|
|
64
72
|
if (!("serviceWorker" in navigator)) {
|
|
65
73
|
throw new Error("Service workers not supported");
|
|
66
74
|
}
|
|
75
|
+
if (this._forceReinstall) {
|
|
76
|
+
const existingReg = await navigator.serviceWorker.getRegistration(this._scope);
|
|
77
|
+
if (existingReg) {
|
|
78
|
+
await existingReg.unregister();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
67
81
|
const regOptions = this._scope ? { scope: this._scope } : void 0;
|
|
68
82
|
this._registration = await navigator.serviceWorker.register(
|
|
69
83
|
this._workerUrl,
|
|
@@ -71,7 +85,8 @@ var CorrectionServiceWorker = class {
|
|
|
71
85
|
);
|
|
72
86
|
await navigator.serviceWorker.ready;
|
|
73
87
|
if (!navigator.serviceWorker.controller) {
|
|
74
|
-
|
|
88
|
+
const needsClaim = !!this._registration.active;
|
|
89
|
+
await this._waitForController(needsClaim);
|
|
75
90
|
}
|
|
76
91
|
await this.resetConfig();
|
|
77
92
|
if (this._pmtilesUrl) {
|
|
@@ -81,17 +96,32 @@ var CorrectionServiceWorker = class {
|
|
|
81
96
|
}
|
|
82
97
|
/**
|
|
83
98
|
* Wait for the service worker to take control of the page.
|
|
99
|
+
* @param {boolean} [requestClaim=false] - Whether to send a claim request to the active worker
|
|
84
100
|
* @returns {Promise<void>}
|
|
85
101
|
* @private
|
|
86
102
|
*/
|
|
87
|
-
async _waitForController() {
|
|
103
|
+
async _waitForController(requestClaim = false) {
|
|
104
|
+
if (navigator.serviceWorker.controller) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
88
107
|
return new Promise((resolve) => {
|
|
108
|
+
let timeoutId;
|
|
89
109
|
const onControllerChange = () => {
|
|
110
|
+
clearTimeout(timeoutId);
|
|
90
111
|
navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange);
|
|
91
112
|
resolve();
|
|
92
113
|
};
|
|
93
114
|
navigator.serviceWorker.addEventListener("controllerchange", onControllerChange);
|
|
94
|
-
|
|
115
|
+
if (requestClaim && this._registration?.active) {
|
|
116
|
+
const channel = new MessageChannel();
|
|
117
|
+
channel.port1.onmessage = () => {
|
|
118
|
+
};
|
|
119
|
+
this._registration.active.postMessage({ type: MessageTypes.CLAIM_CLIENTS }, [channel.port2]);
|
|
120
|
+
}
|
|
121
|
+
timeoutId = setTimeout(() => {
|
|
122
|
+
navigator.serviceWorker.removeEventListener("controllerchange", onControllerChange);
|
|
123
|
+
resolve();
|
|
124
|
+
}, this._controllerTimeout);
|
|
95
125
|
});
|
|
96
126
|
}
|
|
97
127
|
/**
|
|
@@ -185,6 +215,28 @@ var CorrectionServiceWorker = class {
|
|
|
185
215
|
enabled
|
|
186
216
|
});
|
|
187
217
|
}
|
|
218
|
+
/**
|
|
219
|
+
* Set whether to return original tile if corrections fail.
|
|
220
|
+
* @param {boolean} fallbackOnCorrectionFailure
|
|
221
|
+
* @returns {Promise<void>}
|
|
222
|
+
*/
|
|
223
|
+
async setFallbackOnCorrectionFailure(fallbackOnCorrectionFailure) {
|
|
224
|
+
await this.sendMessage({
|
|
225
|
+
type: MessageTypes.SET_FALLBACK_ON_CORRECTION_FAILURE,
|
|
226
|
+
fallbackOnCorrectionFailure
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Set maximum features to cache.
|
|
231
|
+
* @param {number} cacheMaxFeatures
|
|
232
|
+
* @returns {Promise<void>}
|
|
233
|
+
*/
|
|
234
|
+
async setCacheMaxFeatures(cacheMaxFeatures) {
|
|
235
|
+
await this.sendMessage({
|
|
236
|
+
type: MessageTypes.SET_CACHE_MAX_FEATURES,
|
|
237
|
+
cacheMaxFeatures
|
|
238
|
+
});
|
|
239
|
+
}
|
|
188
240
|
/**
|
|
189
241
|
* Clear the tile cache.
|
|
190
242
|
* @returns {Promise<void>}
|
|
@@ -219,16 +271,12 @@ async function registerCorrectionServiceWorker(workerUrl, options = {}) {
|
|
|
219
271
|
await sw.register();
|
|
220
272
|
return sw;
|
|
221
273
|
}
|
|
222
|
-
function getWorkerImportSnippet(workerGlobalUrl) {
|
|
223
|
-
return `importScripts('${workerGlobalUrl}');`;
|
|
224
|
-
}
|
|
225
274
|
// Annotate the CommonJS export names for ESM import in node:
|
|
226
275
|
0 && (module.exports = {
|
|
227
276
|
CorrectionServiceWorker,
|
|
228
277
|
LayerConfig,
|
|
229
278
|
MessageTypes,
|
|
230
279
|
getPmtilesUrl,
|
|
231
|
-
getWorkerImportSnippet,
|
|
232
280
|
layerConfigs,
|
|
233
281
|
registerCorrectionServiceWorker
|
|
234
282
|
});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.js"],"sourcesContent":["/**\n * Main entry point - exports utilities for registering the service worker\n * from the main thread.\n */\n\nexport { layerConfigs, LayerConfig } from '@india-boundary-corrector/layer-configs';\nexport { getPmtilesUrl } from '@india-boundary-corrector/data';\n\n/**\n * Message types for communication with service worker\n */\nexport const MessageTypes = {\n ADD_LAYER_CONFIG: 'ADD_LAYER_CONFIG',\n REMOVE_LAYER_CONFIG: 'REMOVE_LAYER_CONFIG',\n SET_PMTILES_URL: 'SET_PMTILES_URL',\n SET_ENABLED: 'SET_ENABLED',\n CLEAR_CACHE: 'CLEAR_CACHE',\n GET_STATUS: 'GET_STATUS',\n RESET_CONFIG: 'RESET_CONFIG',\n};\n\n/**\n * Controller for the boundary correction service worker.\n * Use this to register the service worker and communicate with it.\n */\nexport class CorrectionServiceWorker {\n /**\n * @param {string} workerUrl - URL to the service worker script\n * @param {Object} [options]\n * @param {string} [options.scope] - Service worker scope (defaults to workerUrl directory)\n * @param {string} [options.pmtilesUrl] - PMTiles URL to set after registration\n * @param {number} [options.controllerTimeout=3000] - Timeout in ms to wait for SW to take control\n */\n constructor(workerUrl, options = {}) {\n this._workerUrl = workerUrl;\n this._scope = options.scope;\n this._pmtilesUrl = options.pmtilesUrl;\n this._controllerTimeout = options.controllerTimeout ?? 3000;\n this._registration = null;\n }\n\n /**\n * Register the service worker and wait for it to take control.\n * @returns {Promise<CorrectionServiceWorker>} Returns this instance for chaining\n * @throws {Error} If service workers not supported or registration fails\n */\n async register() {\n if (!('serviceWorker' in navigator)) {\n throw new Error('Service workers not supported');\n }\n\n const regOptions = this._scope ? { scope: this._scope } : undefined;\n this._registration = await navigator.serviceWorker.register(\n this._workerUrl,\n regOptions\n );\n\n // Wait for the service worker to be ready\n await navigator.serviceWorker.ready;\n\n // Wait for controller if not already controlling\n if (!navigator.serviceWorker.controller) {\n await this._waitForController();\n }\n\n // Reset config to defaults when connecting to an existing service worker\n await this.resetConfig();\n\n // Set PMTiles URL if provided\n if (this._pmtilesUrl) {\n await this.setPmtilesUrl(this._pmtilesUrl);\n }\n\n return this;\n }\n\n /**\n * Wait for the service worker to take control of the page.\n * @returns {Promise<void>}\n * @private\n */\n async _waitForController() {\n return new Promise((resolve) => {\n const onControllerChange = () => {\n navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);\n resolve();\n };\n navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);\n // Timeout fallback - SW may already be controlling after registration\n setTimeout(resolve, this._controllerTimeout);\n });\n }\n\n /**\n * Check if the service worker is controlling the page.\n * @returns {boolean}\n */\n isControlling() {\n return !!navigator.serviceWorker.controller;\n }\n\n /**\n * Unregister the service worker.\n * @returns {Promise<boolean>}\n */\n async unregister() {\n if (this._registration) {\n return this._registration.unregister();\n }\n return false;\n }\n\n /**\n * Get the active service worker.\n * @returns {ServiceWorker|null}\n */\n getWorker() {\n return this._registration?.active ?? navigator.serviceWorker.controller;\n }\n\n /**\n * Send a message to the service worker.\n * @param {Object} message\n * @returns {Promise<any>}\n */\n async sendMessage(message) {\n const worker = this.getWorker();\n if (!worker) {\n throw new Error('Service worker not active');\n }\n\n return new Promise((resolve, reject) => {\n const channel = new MessageChannel();\n channel.port1.onmessage = (event) => {\n if (event.data.error) {\n reject(new Error(event.data.error));\n } else {\n resolve(event.data);\n }\n };\n worker.postMessage(message, [channel.port2]);\n });\n }\n\n /**\n * Add a layer config to the service worker.\n * @param {Object} layerConfig\n * @returns {Promise<void>}\n */\n async addLayerConfig(layerConfig) {\n // Use toJSON if available to properly serialize the config\n const serialized = typeof layerConfig.toJSON === 'function' \n ? layerConfig.toJSON() \n : layerConfig;\n await this.sendMessage({\n type: MessageTypes.ADD_LAYER_CONFIG,\n layerConfig: serialized,\n });\n }\n\n /**\n * Remove a layer config from the service worker.\n * @param {string} configId\n * @returns {Promise<void>}\n */\n async removeLayerConfig(configId) {\n await this.sendMessage({\n type: MessageTypes.REMOVE_LAYER_CONFIG,\n configId,\n });\n }\n\n /**\n * Set the PMTiles URL.\n * @param {string} pmtilesUrl\n * @returns {Promise<void>}\n */\n async setPmtilesUrl(pmtilesUrl) {\n await this.sendMessage({\n type: MessageTypes.SET_PMTILES_URL,\n pmtilesUrl,\n });\n }\n\n /**\n * Enable or disable the correction service.\n * @param {boolean} enabled\n * @returns {Promise<void>}\n */\n async setEnabled(enabled) {\n await this.sendMessage({\n type: MessageTypes.SET_ENABLED,\n enabled,\n });\n }\n\n /**\n * Clear the tile cache.\n * @returns {Promise<void>}\n */\n async clearCache() {\n await this.sendMessage({\n type: MessageTypes.CLEAR_CACHE,\n });\n }\n\n /**\n * Get the status of the service worker.\n * @returns {Promise<Object>}\n */\n async getStatus() {\n return this.sendMessage({\n type: MessageTypes.GET_STATUS,\n });\n }\n\n /**\n * Reset the service worker configuration to defaults.\n * Resets pmtilesUrl to default and restores default layer configs.\n * @returns {Promise<void>}\n */\n async resetConfig() {\n await this.sendMessage({\n type: MessageTypes.RESET_CONFIG,\n });\n }\n}\n\n/**\n * Register the correction service worker with simplified setup.\n * @param {string} workerUrl - URL to the service worker script\n * @param {Object} [options]\n * @param {string} [options.scope] - Service worker scope\n * @param {string} [options.pmtilesUrl] - PMTiles URL to set\n * @param {number} [options.controllerTimeout] - Timeout in ms to wait for SW control\n * @returns {Promise<CorrectionServiceWorker>}\n */\nexport async function registerCorrectionServiceWorker(workerUrl, options = {}) {\n const sw = new CorrectionServiceWorker(workerUrl, options);\n await sw.register();\n return sw;\n}\n\n/**\n * Get the importScripts snippet for a service worker file.\n * This can be used to create a minimal sw.js file.\n * @param {string} workerGlobalUrl - URL to the worker.global.js file\n * @returns {string} JavaScript code to put in sw.js\n * @example\n * // Create sw.js with:\n * // importScripts('https://unpkg.com/@india-boundary-corrector/service-worker/dist/worker.global.js');\n */\nexport function getWorkerImportSnippet(workerGlobalUrl) {\n return `importScripts('${workerGlobalUrl}');`;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.js","../src/constants.js"],"sourcesContent":["/**\n * Main entry point - exports utilities for registering the service worker\n * from the main thread.\n */\n\nexport { layerConfigs, LayerConfig } from '@india-boundary-corrector/layer-configs';\nexport { getPmtilesUrl } from '@india-boundary-corrector/data';\nimport { MessageTypes } from './constants.js';\nexport { MessageTypes };\n\n/**\n * Controller for the boundary correction service worker.\n * Use this to register the service worker and communicate with it.\n */\nexport class CorrectionServiceWorker {\n /**\n * @param {string} workerUrl - URL to the service worker script\n * @param {Object} [options]\n * @param {string} [options.scope] - Service worker scope (defaults to workerUrl directory)\n * @param {string} [options.pmtilesUrl] - PMTiles URL to set after registration\n * @param {number} [options.controllerTimeout=3000] - Timeout in ms to wait for SW to take control\n * @param {boolean} [options.forceReinstall=false] - Unregister existing SW before registering (useful for dev)\n */\n constructor(workerUrl, options = {}) {\n this._workerUrl = workerUrl;\n this._scope = options.scope;\n this._pmtilesUrl = options.pmtilesUrl;\n this._controllerTimeout = options.controllerTimeout ?? 3000;\n this._forceReinstall = options.forceReinstall ?? false;\n this._registration = null;\n }\n\n /**\n * Register the service worker and wait for it to take control.\n * @returns {Promise<CorrectionServiceWorker>} Returns this instance for chaining\n * @throws {Error} If service workers not supported or registration fails\n */\n async register() {\n if (!('serviceWorker' in navigator)) {\n throw new Error('Service workers not supported');\n }\n\n // Unregister existing SW if forceReinstall is set\n if (this._forceReinstall) {\n const existingReg = await navigator.serviceWorker.getRegistration(this._scope);\n if (existingReg) {\n await existingReg.unregister();\n }\n }\n\n const regOptions = this._scope ? { scope: this._scope } : undefined;\n this._registration = await navigator.serviceWorker.register(\n this._workerUrl,\n regOptions\n );\n\n // Wait for the service worker to be ready\n await navigator.serviceWorker.ready;\n\n // Ensure SW is controlling this page\n if (!navigator.serviceWorker.controller) {\n // If SW is active but not controlling (e.g., hard reload), request claim\n // Otherwise just wait for it to activate and take control\n const needsClaim = !!this._registration.active;\n await this._waitForController(needsClaim);\n }\n\n // Reset config to defaults when connecting to an existing service worker\n await this.resetConfig();\n\n // Set PMTiles URL if provided\n if (this._pmtilesUrl) {\n await this.setPmtilesUrl(this._pmtilesUrl);\n }\n\n return this;\n }\n\n /**\n * Wait for the service worker to take control of the page.\n * @param {boolean} [requestClaim=false] - Whether to send a claim request to the active worker\n * @returns {Promise<void>}\n * @private\n */\n async _waitForController(requestClaim = false) {\n // Check if already controlling (race condition)\n if (navigator.serviceWorker.controller) {\n return;\n }\n\n return new Promise((resolve) => {\n let timeoutId;\n const onControllerChange = () => {\n clearTimeout(timeoutId);\n navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);\n resolve();\n };\n navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);\n\n // If requested, send claim message to active worker\n if (requestClaim && this._registration?.active) {\n const channel = new MessageChannel();\n channel.port1.onmessage = () => {\n // Claim was called, controllerchange should fire soon\n };\n this._registration.active.postMessage({ type: MessageTypes.CLAIM_CLIENTS }, [channel.port2]);\n }\n\n // Timeout fallback\n timeoutId = setTimeout(() => {\n navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);\n resolve();\n }, this._controllerTimeout);\n });\n }\n\n /**\n * Check if the service worker is controlling the page.\n * @returns {boolean}\n */\n isControlling() {\n return !!navigator.serviceWorker.controller;\n }\n\n /**\n * Unregister the service worker.\n * @returns {Promise<boolean>}\n */\n async unregister() {\n if (this._registration) {\n return this._registration.unregister();\n }\n return false;\n }\n\n /**\n * Get the active service worker.\n * @returns {ServiceWorker|null}\n */\n getWorker() {\n return this._registration?.active ?? navigator.serviceWorker.controller;\n }\n\n /**\n * Send a message to the service worker.\n * @param {Object} message\n * @returns {Promise<any>}\n */\n async sendMessage(message) {\n const worker = this.getWorker();\n if (!worker) {\n throw new Error('Service worker not active');\n }\n\n return new Promise((resolve, reject) => {\n const channel = new MessageChannel();\n channel.port1.onmessage = (event) => {\n if (event.data.error) {\n reject(new Error(event.data.error));\n } else {\n resolve(event.data);\n }\n };\n worker.postMessage(message, [channel.port2]);\n });\n }\n\n /**\n * Add a layer config to the service worker.\n * @param {Object} layerConfig\n * @returns {Promise<void>}\n */\n async addLayerConfig(layerConfig) {\n // Use toJSON if available to properly serialize the config\n const serialized = typeof layerConfig.toJSON === 'function' \n ? layerConfig.toJSON() \n : layerConfig;\n await this.sendMessage({\n type: MessageTypes.ADD_LAYER_CONFIG,\n layerConfig: serialized,\n });\n }\n\n /**\n * Remove a layer config from the service worker.\n * @param {string} configId\n * @returns {Promise<void>}\n */\n async removeLayerConfig(configId) {\n await this.sendMessage({\n type: MessageTypes.REMOVE_LAYER_CONFIG,\n configId,\n });\n }\n\n /**\n * Set the PMTiles URL.\n * @param {string} pmtilesUrl\n * @returns {Promise<void>}\n */\n async setPmtilesUrl(pmtilesUrl) {\n await this.sendMessage({\n type: MessageTypes.SET_PMTILES_URL,\n pmtilesUrl,\n });\n }\n\n /**\n * Enable or disable the correction service.\n * @param {boolean} enabled\n * @returns {Promise<void>}\n */\n async setEnabled(enabled) {\n await this.sendMessage({\n type: MessageTypes.SET_ENABLED,\n enabled,\n });\n }\n\n /**\n * Set whether to return original tile if corrections fail.\n * @param {boolean} fallbackOnCorrectionFailure\n * @returns {Promise<void>}\n */\n async setFallbackOnCorrectionFailure(fallbackOnCorrectionFailure) {\n await this.sendMessage({\n type: MessageTypes.SET_FALLBACK_ON_CORRECTION_FAILURE,\n fallbackOnCorrectionFailure,\n });\n }\n\n /**\n * Set maximum features to cache.\n * @param {number} cacheMaxFeatures\n * @returns {Promise<void>}\n */\n async setCacheMaxFeatures(cacheMaxFeatures) {\n await this.sendMessage({\n type: MessageTypes.SET_CACHE_MAX_FEATURES,\n cacheMaxFeatures,\n });\n }\n\n /**\n * Clear the tile cache.\n * @returns {Promise<void>}\n */\n async clearCache() {\n await this.sendMessage({\n type: MessageTypes.CLEAR_CACHE,\n });\n }\n\n /**\n * Get the status of the service worker.\n * @returns {Promise<Object>}\n */\n async getStatus() {\n return this.sendMessage({\n type: MessageTypes.GET_STATUS,\n });\n }\n\n /**\n * Reset the service worker configuration to defaults.\n * Resets pmtilesUrl to default and restores default layer configs.\n * @returns {Promise<void>}\n */\n async resetConfig() {\n await this.sendMessage({\n type: MessageTypes.RESET_CONFIG,\n });\n }\n}\n\n/**\n * Register the correction service worker with simplified setup.\n * @param {string} workerUrl - URL to the service worker script\n * @param {Object} [options]\n * @param {string} [options.scope] - Service worker scope\n * @param {string} [options.pmtilesUrl] - PMTiles URL to set\n * @param {number} [options.controllerTimeout] - Timeout in ms to wait for SW control\n * @param {boolean} [options.forceReinstall] - Unregister existing SW before registering\n * @returns {Promise<CorrectionServiceWorker>}\n */\nexport async function registerCorrectionServiceWorker(workerUrl, options = {}) {\n const sw = new CorrectionServiceWorker(workerUrl, options);\n await sw.register();\n return sw;\n}\n\n","/**\n * Shared constants for service worker communication.\n */\n\n/**\n * Message types for communication between main thread and service worker.\n */\nexport const MessageTypes = {\n ADD_LAYER_CONFIG: 'ADD_LAYER_CONFIG',\n REMOVE_LAYER_CONFIG: 'REMOVE_LAYER_CONFIG',\n SET_PMTILES_URL: 'SET_PMTILES_URL',\n SET_ENABLED: 'SET_ENABLED',\n SET_FALLBACK_ON_CORRECTION_FAILURE: 'SET_FALLBACK_ON_CORRECTION_FAILURE',\n SET_CACHE_MAX_FEATURES: 'SET_CACHE_MAX_FEATURES',\n CLEAR_CACHE: 'CLEAR_CACHE',\n GET_STATUS: 'GET_STATUS',\n RESET_CONFIG: 'RESET_CONFIG',\n CLAIM_CLIENTS: 'CLAIM_CLIENTS',\n};\n"],"mappings":"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","names":[]}
|