@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 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://unpkg.com/@india-boundary-corrector/service-worker/dist/worker.global.js');
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').addTo(map);
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({ url: 'https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png' }) });
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://unpkg.com/@india-boundary-corrector/service-worker/dist/worker.global.js');
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://unpkg.com/@india-boundary-corrector/service-worker/dist/index.global.js"></script>
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').addTo(map);
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://unpkg.com/@india-boundary-corrector/service-worker/dist/index.global.js"></script>
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
- await this._waitForController();
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
- setTimeout(resolve, this._controllerTimeout);
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
  });
@@ -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":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,2BAA0C;AAC1C,kBAA8B;AAKvB,IAAM,eAAe;AAAA,EAC1B,kBAAkB;AAAA,EAClB,qBAAqB;AAAA,EACrB,iBAAiB;AAAA,EACjB,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAChB;AAMO,IAAM,0BAAN,MAA8B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQnC,YAAY,WAAW,UAAU,CAAC,GAAG;AACnC,SAAK,aAAa;AAClB,SAAK,SAAS,QAAQ;AACtB,SAAK,cAAc,QAAQ;AAC3B,SAAK,qBAAqB,QAAQ,qBAAqB;AACvD,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW;AACf,QAAI,EAAE,mBAAmB,YAAY;AACnC,YAAM,IAAI,MAAM,+BAA+B;AAAA,IACjD;AAEA,UAAM,aAAa,KAAK,SAAS,EAAE,OAAO,KAAK,OAAO,IAAI;AAC1D,SAAK,gBAAgB,MAAM,UAAU,cAAc;AAAA,MACjD,KAAK;AAAA,MACL;AAAA,IACF;AAGA,UAAM,UAAU,cAAc;AAG9B,QAAI,CAAC,UAAU,cAAc,YAAY;AACvC,YAAM,KAAK,mBAAmB;AAAA,IAChC;AAGA,UAAM,KAAK,YAAY;AAGvB,QAAI,KAAK,aAAa;AACpB,YAAM,KAAK,cAAc,KAAK,WAAW;AAAA,IAC3C;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,qBAAqB;AACzB,WAAO,IAAI,QAAQ,CAAC,YAAY;AAC9B,YAAM,qBAAqB,MAAM;AAC/B,kBAAU,cAAc,oBAAoB,oBAAoB,kBAAkB;AAClF,gBAAQ;AAAA,MACV;AACA,gBAAU,cAAc,iBAAiB,oBAAoB,kBAAkB;AAE/E,iBAAW,SAAS,KAAK,kBAAkB;AAAA,IAC7C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB;AACd,WAAO,CAAC,CAAC,UAAU,cAAc;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa;AACjB,QAAI,KAAK,eAAe;AACtB,aAAO,KAAK,cAAc,WAAW;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,YAAY;AACV,WAAO,KAAK,eAAe,UAAU,UAAU,cAAc;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,YAAY,SAAS;AACzB,UAAM,SAAS,KAAK,UAAU;AAC9B,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,2BAA2B;AAAA,IAC7C;AAEA,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,YAAM,UAAU,IAAI,eAAe;AACnC,cAAQ,MAAM,YAAY,CAAC,UAAU;AACnC,YAAI,MAAM,KAAK,OAAO;AACpB,iBAAO,IAAI,MAAM,MAAM,KAAK,KAAK,CAAC;AAAA,QACpC,OAAO;AACL,kBAAQ,MAAM,IAAI;AAAA,QACpB;AAAA,MACF;AACA,aAAO,YAAY,SAAS,CAAC,QAAQ,KAAK,CAAC;AAAA,IAC7C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eAAe,aAAa;AAEhC,UAAM,aAAa,OAAO,YAAY,WAAW,aAC7C,YAAY,OAAO,IACnB;AACJ,UAAM,KAAK,YAAY;AAAA,MACrB,MAAM,aAAa;AAAA,MACnB,aAAa;AAAA,IACf,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBAAkB,UAAU;AAChC,UAAM,KAAK,YAAY;AAAA,MACrB,MAAM,aAAa;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAc,YAAY;AAC9B,UAAM,KAAK,YAAY;AAAA,MACrB,MAAM,aAAa;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WAAW,SAAS;AACxB,UAAM,KAAK,YAAY;AAAA,MACrB,MAAM,aAAa;AAAA,MACnB;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAa;AACjB,UAAM,KAAK,YAAY;AAAA,MACrB,MAAM,aAAa;AAAA,IACrB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY;AAChB,WAAO,KAAK,YAAY;AAAA,MACtB,MAAM,aAAa;AAAA,IACrB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,cAAc;AAClB,UAAM,KAAK,YAAY;AAAA,MACrB,MAAM,aAAa;AAAA,IACrB,CAAC;AAAA,EACH;AACF;AAWA,eAAsB,gCAAgC,WAAW,UAAU,CAAC,GAAG;AAC7E,QAAM,KAAK,IAAI,wBAAwB,WAAW,OAAO;AACzD,QAAM,GAAG,SAAS;AAClB,SAAO;AACT;AAWO,SAAS,uBAAuB,iBAAiB;AACtD,SAAO,kBAAkB,eAAe;AAC1C;","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":[]}