@india-boundary-corrector/service-worker 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/index.global.js +12 -3
- package/dist/index.global.js.map +1 -1
- package/dist/worker.global.js +18 -6
- package/dist/worker.global.js.map +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @india-boundary-corrector/service-worker
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@india-boundary-corrector/service-worker)
|
|
4
|
+
|
|
3
5
|
Service worker that intercepts map tile requests and applies India boundary corrections automatically.
|
|
4
6
|
|
|
5
7
|
## Installation
|
package/dist/index.global.js
CHANGED
|
@@ -141,10 +141,10 @@ var IndiaBoundaryCorrector = (() => {
|
|
|
141
141
|
let pattern = template.replace(/[.*+?^${}()|[\]\\]/g, (char) => {
|
|
142
142
|
if (char === "{" || char === "}") return char;
|
|
143
143
|
return "\\" + char;
|
|
144
|
-
}).replace(/^https:\/\//, "https?://").replace(/^http:\/\//, "https?://").replace(/\{([a-z0-9])-([a-z0-9])\}/gi, (_, start, end) => `(\\{${start}-${end}\\}|[a-z0-9]+)`).replace(/\{(z|x|y|s|r)\}/gi, (_, name) => {
|
|
144
|
+
}).replace(/^https:\/\//, "https?://").replace(/^http:\/\//, "https?://").replace(/\{([a-z0-9])-([a-z0-9])\}/gi, (_, start, end) => `(\\{${start}-${end}\\}|\\{s\\}|[a-z0-9]+)`).replace(/\{(z|x|y|s|r)\}/gi, (_, name) => {
|
|
145
145
|
const lowerName = name.toLowerCase();
|
|
146
146
|
if (lowerName === "s") {
|
|
147
|
-
return "(\\{s\\}|[a-z0-9]+)";
|
|
147
|
+
return "(\\{s\\}|\\{[a-z0-9]-[a-z0-9]\\}|[a-z0-9]+)";
|
|
148
148
|
}
|
|
149
149
|
if (lowerName === "r") {
|
|
150
150
|
return "(\\{r\\}|@\\d+x)?";
|
|
@@ -178,6 +178,9 @@ var IndiaBoundaryCorrector = (() => {
|
|
|
178
178
|
if (!id || typeof id !== "string") {
|
|
179
179
|
throw new Error("LayerConfig requires a non-empty string id");
|
|
180
180
|
}
|
|
181
|
+
if (id.includes("/")) {
|
|
182
|
+
throw new Error(`LayerConfig id cannot contain slashes: "${id}"`);
|
|
183
|
+
}
|
|
181
184
|
this.id = id;
|
|
182
185
|
this.startZoom = startZoom;
|
|
183
186
|
this.zoomThreshold = zoomThreshold;
|
|
@@ -400,17 +403,23 @@ var IndiaBoundaryCorrector = (() => {
|
|
|
400
403
|
}
|
|
401
404
|
|
|
402
405
|
// ../data/version.js
|
|
403
|
-
var packageVersion = "0.0.
|
|
406
|
+
var packageVersion = "0.0.3";
|
|
404
407
|
|
|
405
408
|
// ../data/index.js
|
|
406
409
|
var import_meta = {};
|
|
407
410
|
var PACKAGE_NAME = "@india-boundary-corrector/data";
|
|
408
411
|
var PMTILES_FILENAME = "india_boundary_corrections.pmtiles";
|
|
409
412
|
var DEFAULT_CDN_URL = `https://unpkg.com/${PACKAGE_NAME}@${packageVersion}/${PMTILES_FILENAME}`;
|
|
413
|
+
function shouldUseCdnFallback(hostname) {
|
|
414
|
+
return hostname === "esm.sh";
|
|
415
|
+
}
|
|
410
416
|
function detectPmtilesUrl() {
|
|
411
417
|
try {
|
|
412
418
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
413
419
|
const moduleUrl = new URL(".", import_meta.url);
|
|
420
|
+
if (shouldUseCdnFallback(moduleUrl.hostname)) {
|
|
421
|
+
return DEFAULT_CDN_URL;
|
|
422
|
+
}
|
|
414
423
|
return new URL(PMTILES_FILENAME, moduleUrl).href;
|
|
415
424
|
}
|
|
416
425
|
} catch {
|
package/dist/index.global.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.js","../../layer-configs/src/configs.json","../../layer-configs/src/layerconfig.js","../../layer-configs/src/index.js","../../data/version.js","../../data/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","[\n {\n \"id\": \"cartodb-dark\",\n \"zoomThreshold\": 5,\n \"tileUrlTemplates\": [\n \"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_nolabels/{z}/{x}/{y}{r}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.5, \"10\": 2.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(40, 40, 40)\" }\n ]\n },\n {\n \"id\": \"cartodb-light\",\n \"startZoom\": 0,\n \"zoomThreshold\": 5,\n \"tileUrlTemplates\": [\n \"https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.25, \"2\": 0.25, \"3\": 0.5, \"4\": 0.75, \"5\": 1.0 },\n \"lineStyles\": [\n { \"color\": \"rgb(235, 214, 214)\", \"alpha\": 0.2, \"startZoom\": 6, \"widthFraction\": 5 },\n { \"color\": \"rgb(235, 214, 214)\" }\n ]\n },\n {\n \"id\": \"open-topo\",\n \"startZoom\": 4,\n \"zoomThreshold\": 4,\n \"tileUrlTemplates\": [\n \"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png\",\n \"https://tile.opentopomap.org/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"4\": 0.75, \"5\": 1.0, \"6\": 1.25, \"7\": 1.5, \"8\": 1.75, \"9\": 1.25, \"10\": 1.25, \"13\": 1.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(83, 83, 83)\", \"startZoom\": 7, \"endZoom\": 8, \"alpha\": 0.4, \"widthFraction\": 4 },\n { \"color\": \"rgb(83, 83, 83)\", \"endZoom\": 8 },\n { \"color\": \"rgb(140, 20, 180)\", \"startZoom\": 9, \"widthFraction\": 9, \"alpha\": 0.2 },\n { \"color\": \"rgb(140, 20, 180)\", \"startZoom\": 9 }\n ]\n },\n {\n \"id\": \"osm-carto\",\n \"startZoom\": 1,\n \"zoomThreshold\": 1,\n \"tileUrlTemplates\": [\n \"https://tile.openstreetmap.org/{z}/{x}/{y}.png\",\n \"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.5, \"2\": 0.6, \"3\": 0.7, \"4\": 1.0, \"10\": 3.75 },\n \"lineStyles\": [\n { \"color\": \"rgb(200, 180, 200)\" },\n { \"color\": \"rgb(160, 120, 160)\", \"widthFraction\": 0.333, \"dashArray\": [30, 2, 8, 2] }\n ]\n },\n {\n \"id\": \"osm-hot\",\n \"startZoom\": 2,\n \"zoomThreshold\": 2,\n \"tileUrlTemplates\": [\n \"https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"2\": 3.0, \"3\": 3.0, \"7\": 3.0, \"8\": 3.5, \"9\": 3.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(149, 175, 180)\" },\n { \"color\": \"rgb(89, 117, 123)\", \"widthFraction\": 0.33 }\n ]\n }\n]\n","/**\n * Convert a tile URL template to a regex pattern and capture group names.\n * Supports {z}, {x}, {y}, {s} (Leaflet subdomain), {a-c}/{1-4} (OpenLayers subdomain), and {r} (retina) placeholders.\n * @param {string} template - URL template like \"https://{s}.tile.example.com/{z}/{x}/{y}.png\"\n * @returns {{ pattern: RegExp, groups: string[] }}\n */\nfunction templateToRegex(template) {\n const groups = [];\n // Escape regex special chars, then replace placeholders\n let pattern = template\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n // Don't escape our placeholders\n if (char === '{' || char === '}') return char;\n return '\\\\' + char;\n })\n // Make protocol flexible (http/https)\n .replace(/^https:\\/\\//, 'https?://')\n .replace(/^http:\\/\\//, 'https?://')\n // Handle {a-c} or {1-4} etc (OpenLayers style subdomain)\n .replace(/\\{[a-z0-9]-[a-z0-9]\\}/gi, () => {\n groups.push('s');\n return '([a-z0-9]+)';\n })\n .replace(/\\{(z|x|y|s|r)\\}/gi, (_, name) => {\n const lowerName = name.toLowerCase();\n groups.push(lowerName);\n if (lowerName === 's') {\n // Subdomain: single letter or short string\n return '([a-z0-9]+)';\n }\n if (lowerName === 'r') {\n // Retina suffix: optional @2x or similar\n return '(@\\\\d+x)?';\n }\n // z, x, y: numeric\n return '(\\\\d+)';\n });\n \n // Allow optional query string at end\n return { pattern: new RegExp('^' + pattern + '(\\\\?.*)?$', 'i'), groups };\n}\n\n/**\n * Convert a tile URL template to a regex that matches the template itself.\n * @param {string} template - URL template like \"https://{s}.tile.example.com/{z}/{x}/{y}.png\"\n * @returns {RegExp}\n */\nfunction templateToTemplateRegex(template) {\n // Escape regex special chars, then replace placeholders with literal match\n let pattern = template\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n if (char === '{' || char === '}') return char;\n return '\\\\' + char;\n })\n // Make protocol flexible (http/https)\n .replace(/^https:\\/\\//, 'https?://')\n .replace(/^http:\\/\\//, 'https?://')\n // Handle {a-c} or {1-4} (OpenLayers style subdomain)\n .replace(/\\{([a-z0-9])-([a-z0-9])\\}/gi, (_, start, end) => `(\\\\{${start}-${end}\\\\}|[a-z0-9]+)`)\n .replace(/\\{(z|x|y|s|r)\\}/gi, (_, name) => {\n const lowerName = name.toLowerCase();\n if (lowerName === 's') {\n // Match {s} placeholder or actual subdomain\n return '(\\\\{s\\\\}|[a-z0-9]+)';\n }\n if (lowerName === 'r') {\n // Match {r} placeholder or actual retina or empty\n return '(\\\\{r\\\\}|@\\\\d+x)?';\n }\n // Match {z}, {x}, {y} placeholders\n return `\\\\{${lowerName}\\\\}`;\n });\n \n // Allow optional query string at end\n return new RegExp('^' + pattern + '(\\\\?.*)?$', 'i');\n}\n\n/**\n * Base class for layer configurations\n * \n * Supports separate styling for NE (Natural Earth) data at low zoom levels\n * and OSM data at higher zoom levels, split by zoomThreshold.\n */\nexport class LayerConfig {\n constructor({\n id,\n startZoom = 0,\n zoomThreshold = 5,\n // Tile URL templates for matching (e.g., \"https://{s}.tile.example.com/{z}/{x}/{y}.png\")\n tileUrlTemplates = [],\n // Line width stops: map of zoom level to line width (at least 2 entries)\n lineWidthStops = { 1: 0.5, 10: 2.5 },\n // Line styles array - each element describes a line to draw\n // { color: string, widthFraction?: number, dashArray?: number[], startZoom?: number, endZoom?: number }\n // Lines are drawn in array order. startZoom defaults to layerConfig startZoom, endZoom defaults to Infinity\n lineStyles = [{ color: 'green', widthFraction: 1.0 }],\n // Factor to multiply line width for deletion blur (default 1.5)\n // Higher values leave gaps where wiped lines meet existing lines\n // Lower values mean wiped lines show through\n delWidthFactor = 1.5,\n // Factor to extend add lines by (multiplied by deletion line width)\n // Helps cover gaps where deleted lines meet the new boundary\n // Set to 0 to disable extension\n lineExtensionFactor = 0.5,\n }) {\n if (!id || typeof id !== 'string') {\n throw new Error('LayerConfig requires a non-empty string id');\n }\n\n this.id = id;\n this.startZoom = startZoom;\n this.zoomThreshold = zoomThreshold;\n\n if (startZoom > zoomThreshold) {\n throw new Error(`LayerConfig \"${id}\": startZoom (${startZoom}) must be <= zoomThreshold (${zoomThreshold})`);\n }\n\n // Normalize to array\n const templates = Array.isArray(tileUrlTemplates) ? tileUrlTemplates : \n (tileUrlTemplates ? [tileUrlTemplates] : []);\n this.tileUrlTemplates = templates;\n \n // Pre-compile regex patterns for matching tile URLs (with actual coords)\n this._compiledPatterns = templates.map(t => templateToRegex(t));\n \n // Pre-compile regex patterns for matching template URLs (with {z}/{x}/{y} placeholders)\n this._templatePatterns = templates.map(t => templateToTemplateRegex(t));\n\n // Validate lineWidthStops\n if (!lineWidthStops || typeof lineWidthStops !== 'object' || Array.isArray(lineWidthStops)) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops must be an object`);\n }\n const stopKeys = Object.keys(lineWidthStops);\n if (stopKeys.length < 2) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops must have at least 2 entries`);\n }\n for (const key of stopKeys) {\n const zoom = Number(key);\n if (!Number.isInteger(zoom) || zoom < 0) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops keys must be non-negative integers, got \"${key}\"`);\n }\n if (typeof lineWidthStops[key] !== 'number' || lineWidthStops[key] <= 0) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops values must be positive numbers`);\n }\n }\n this.lineWidthStops = lineWidthStops;\n\n // Validate lineStyles\n if (!Array.isArray(lineStyles) || lineStyles.length === 0) {\n throw new Error(`LayerConfig \"${id}\": lineStyles must be a non-empty array`);\n }\n for (let i = 0; i < lineStyles.length; i++) {\n const style = lineStyles[i];\n if (!style || typeof style !== 'object') {\n throw new Error(`LayerConfig \"${id}\": lineStyles[${i}] must be an object`);\n }\n if (!style.color || typeof style.color !== 'string') {\n throw new Error(`LayerConfig \"${id}\": lineStyles[${i}].color must be a non-empty string`);\n }\n }\n \n // Line styles - normalize startZoom/endZoom defaults\n this.lineStyles = lineStyles.map(style => ({\n ...style,\n startZoom: style.startZoom ?? startZoom,\n endZoom: style.endZoom ?? Infinity,\n }));\n \n // Deletion width factor\n this.delWidthFactor = delWidthFactor;\n \n // Line extension factor\n this.lineExtensionFactor = lineExtensionFactor;\n }\n\n /**\n * Get line styles active at a given zoom level\n * @param {number} z - Zoom level\n * @returns {Array<{color: string, widthFraction?: number, dashArray?: number[]}>}\n */\n getLineStylesForZoom(z) {\n return this.lineStyles.filter(style => z >= style.startZoom && z <= style.endZoom);\n }\n\n /**\n * Check if this config matches the given template URLs (with {z}/{x}/{y} placeholders)\n * @param {string | string[]} templates - Single template URL or array of template URLs\n * @returns {boolean}\n */\n matchTemplate(templates) {\n if (this._templatePatterns.length === 0) return false;\n \n const urls = Array.isArray(templates) ? templates : [templates];\n if (urls.length === 0) return false;\n \n return urls.some(url => \n this._templatePatterns.some(pattern => pattern.test(url))\n );\n }\n\n /**\n * Check if this config matches the given tile URLs (with actual coordinates)\n * @param {string | string[]} tiles - Single tile URL or array of tile URLs\n * @returns {boolean}\n */\n matchTileUrl(tiles) {\n if (this._compiledPatterns.length === 0) return false;\n \n const urls = Array.isArray(tiles) ? tiles : [tiles];\n if (urls.length === 0) return false;\n \n return urls.some(url => \n this._compiledPatterns.some(({ pattern }) => pattern.test(url))\n );\n }\n\n /**\n * Extract tile coordinates (z, x, y) from a URL using this config's templates\n * @param {string} url - Tile URL to extract coordinates from\n * @returns {{ z: number, x: number, y: number } | null}\n */\n extractCoords(url) {\n for (const { pattern, groups } of this._compiledPatterns) {\n const match = url.match(pattern);\n if (match) {\n const result = {};\n for (let i = 0; i < groups.length; i++) {\n const name = groups[i];\n const value = match[i + 1];\n if (name === 'z' || name === 'x' || name === 'y') {\n result[name] = parseInt(value, 10);\n }\n }\n if ('z' in result && 'x' in result && 'y' in result) {\n return { z: result.z, x: result.x, y: result.y };\n }\n }\n }\n return null;\n }\n\n /**\n * Serialize the config to a plain object for postMessage\n * @returns {Object}\n */\n toJSON() {\n return {\n id: this.id,\n startZoom: this.startZoom,\n zoomThreshold: this.zoomThreshold,\n tileUrlTemplates: this.tileUrlTemplates,\n lineWidthStops: this.lineWidthStops,\n lineStyles: this.lineStyles,\n delWidthFactor: this.delWidthFactor,\n lineExtensionFactor: this.lineExtensionFactor,\n };\n }\n\n /**\n * Create a LayerConfig from a plain object (e.g., from postMessage)\n * @param {Object} obj\n * @returns {LayerConfig}\n */\n static fromJSON(obj) {\n return new LayerConfig(obj);\n }\n}\n\nexport default LayerConfig;\n","import configsJson from './configs.json' with { type: 'json' };\nimport { LayerConfig } from './layerconfig.js';\n\nexport { LayerConfig } from './layerconfig.js';\n\n/**\n * Layer configuration registry\n */\nexport class LayerConfigRegistry {\n constructor() {\n this.registry = {};\n }\n\n /**\n * Get a layer config by id\n */\n get(id) {\n return this.registry[id];\n }\n\n /**\n * Register a new layer config\n */\n register(config) {\n this.registry[config.id] = config;\n }\n\n /**\n * Remove a layer config by id\n */\n remove(id) {\n if (!this.registry[id]) return false;\n delete this.registry[id];\n return true;\n }\n\n /**\n * Detect layer config from tile URL templates (with {z}/{x}/{y} placeholders)\n * @param {string | string[]} templates - Single template URL or array of template URLs\n */\n detectFromTemplates(templates) {\n if (!templates || (Array.isArray(templates) && templates.length === 0)) return undefined;\n \n for (const config of Object.values(this.registry)) {\n if (config.matchTemplate(templates)) {\n return config;\n }\n }\n \n return undefined;\n }\n\n /**\n * Detect layer config from actual tile URLs (with numeric coordinates)\n * @param {string | string[]} urls - Single tile URL or array of tile URLs\n */\n detectFromTileUrls(urls) {\n if (!urls || (Array.isArray(urls) && urls.length === 0)) return undefined;\n \n for (const config of Object.values(this.registry)) {\n if (config.matchTileUrl(urls)) {\n return config;\n }\n }\n \n return undefined;\n }\n\n /**\n * Get all available layer config ids\n */\n getAvailableIds() {\n return Object.keys(this.registry);\n }\n\n /**\n * Create a new registry with all configs from this registry plus extra configs.\n * @param {LayerConfig[]} extraLayerConfigs - Additional configs to add\n * @returns {LayerConfigRegistry} A new registry with merged configs\n */\n createMergedRegistry(extraLayerConfigs) {\n const registry = new LayerConfigRegistry();\n \n for (const id of this.getAvailableIds()) {\n registry.register(this.get(id));\n }\n \n if (extraLayerConfigs && extraLayerConfigs.length > 0) {\n for (const config of extraLayerConfigs) {\n registry.register(config);\n }\n }\n \n return registry;\n }\n\n /**\n * Parse a tile URL into its components: layer config and coordinates\n * @param {string} url - Tile URL to parse\n * @returns {{ layerConfig: LayerConfig, coords: { z: number, x: number, y: number } } | null}\n */\n parseTileUrl(url) {\n // Check if URL matches any layer config\n const layerConfig = this.detectFromTileUrls([url]);\n if (!layerConfig) return null;\n \n // Extract tile coordinates using the matched config\n const coords = layerConfig.extractCoords(url);\n if (!coords) return null;\n \n return { layerConfig, coords };\n }\n}\n\n// Default registry with built-in configs loaded from JSON\nexport const layerConfigs = new LayerConfigRegistry();\nfor (const configData of configsJson) {\n layerConfigs.register(new LayerConfig(configData));\n}\n\n","// Auto-generated by scripts/generate-version.js - DO NOT EDIT\nexport const packageVersion = '0.0.1';\n","import { dataVersion } from './data_version.js';\nimport { packageVersion } from './version.js';\n\n// Package info for CDN URL construction\nconst PACKAGE_NAME = '@india-boundary-corrector/data';\nconst PMTILES_FILENAME = 'india_boundary_corrections.pmtiles';\n\n// Default CDN URL with pinned package version\nconst DEFAULT_CDN_URL = `https://unpkg.com/${PACKAGE_NAME}@${packageVersion}/${PMTILES_FILENAME}`;\n\n/**\n * Layer names in the PMTiles file\n */\nexport const layers = {\n toAddOsm: 'to-add-osm',\n toDelOsm: 'to-del-osm',\n toAddNe: 'to-add-ne',\n toDelNe: 'to-del-ne',\n};\n\n/**\n * Detect the PMTiles URL from various sources:\n * 1. import.meta.url (for ESM bundlers - most reliable)\n * 2. Fallback to unpkg CDN with pinned version\n * \n * Note: When this package is bundled into another bundle, import.meta.url\n * won't work and we fall back to the CDN URL. Users can override with\n * setPmtilesUrl() for self-hosted scenarios.\n */\nfunction detectPmtilesUrl() {\n // Try import.meta.url first (works in ESM environments)\n try {\n if (typeof import.meta !== 'undefined' && import.meta.url) {\n const moduleUrl = new URL('.', import.meta.url);\n return new URL(PMTILES_FILENAME, moduleUrl).href;\n }\n } catch {\n // import.meta not available (UMD/CJS/bundled)\n }\n\n // Fallback to CDN with pinned version\n // This ensures it works even when bundled into another package\n return DEFAULT_CDN_URL;\n}\n\n// Cache the detected URL\nlet cachedPmtilesUrl = null;\n\n/**\n * Get the URL for the PMTiles file.\n * \n * Detection priority:\n * 1. Manually set URL via setPmtilesUrl()\n * 2. import.meta.url (ESM environments)\n * 3. unpkg CDN fallback (pinned to current version)\n * \n * For self-hosted deployments or custom bundling scenarios,\n * use setPmtilesUrl().\n * \n * @returns {string} URL to the PMTiles file\n */\nexport function getPmtilesUrl() {\n if (cachedPmtilesUrl === null) {\n cachedPmtilesUrl = detectPmtilesUrl();\n }\n return cachedPmtilesUrl;\n}\n\n/**\n * Manually set the PMTiles URL.\n * Use this for self-hosted deployments or custom bundling scenarios.\n * \n * @param {string} url - The URL to the PMTiles file\n * \n * @example\n * // Self-hosted\n * setPmtilesUrl('/assets/india_boundary_corrections.pmtiles');\n * \n * @example\n * // Different CDN\n * setPmtilesUrl('https://my-cdn.com/india_boundary_corrections.pmtiles');\n */\nexport function setPmtilesUrl(url) {\n cachedPmtilesUrl = url;\n}\n\n/**\n * Get the data version string\n * @returns {string} Data version identifier\n */\nexport function getDataVersion() {\n return dataVersion;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.js","../../layer-configs/src/configs.json","../../layer-configs/src/layerconfig.js","../../layer-configs/src/index.js","../../data/version.js","../../data/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","[\n {\n \"id\": \"cartodb-dark\",\n \"zoomThreshold\": 5,\n \"tileUrlTemplates\": [\n \"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_all/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/dark_nolabels/{z}/{x}/{y}{r}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.5, \"10\": 2.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(40, 40, 40)\" }\n ]\n },\n {\n \"id\": \"cartodb-light\",\n \"startZoom\": 0,\n \"zoomThreshold\": 5,\n \"tileUrlTemplates\": [\n \"https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager_nolabels/{z}/{x}/{y}{r}.png\",\n \"https://{s}.basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png\",\n \"https://basemaps.cartocdn.com/rastertiles/voyager_labels_under/{z}/{x}/{y}{r}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.25, \"2\": 0.25, \"3\": 0.5, \"4\": 0.75, \"5\": 1.0 },\n \"lineStyles\": [\n { \"color\": \"rgb(235, 214, 214)\", \"alpha\": 0.2, \"startZoom\": 6, \"widthFraction\": 5 },\n { \"color\": \"rgb(235, 214, 214)\" }\n ]\n },\n {\n \"id\": \"open-topo\",\n \"startZoom\": 4,\n \"zoomThreshold\": 4,\n \"tileUrlTemplates\": [\n \"https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png\",\n \"https://tile.opentopomap.org/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"4\": 0.75, \"5\": 1.0, \"6\": 1.25, \"7\": 1.5, \"8\": 1.75, \"9\": 1.25, \"10\": 1.25, \"13\": 1.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(83, 83, 83)\", \"startZoom\": 7, \"endZoom\": 8, \"alpha\": 0.4, \"widthFraction\": 4 },\n { \"color\": \"rgb(83, 83, 83)\", \"endZoom\": 8 },\n { \"color\": \"rgb(140, 20, 180)\", \"startZoom\": 9, \"widthFraction\": 9, \"alpha\": 0.2 },\n { \"color\": \"rgb(140, 20, 180)\", \"startZoom\": 9 }\n ]\n },\n {\n \"id\": \"osm-carto\",\n \"startZoom\": 1,\n \"zoomThreshold\": 1,\n \"tileUrlTemplates\": [\n \"https://tile.openstreetmap.org/{z}/{x}/{y}.png\",\n \"https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"1\": 0.5, \"2\": 0.6, \"3\": 0.7, \"4\": 1.0, \"10\": 3.75 },\n \"lineStyles\": [\n { \"color\": \"rgb(200, 180, 200)\" },\n { \"color\": \"rgb(160, 120, 160)\", \"widthFraction\": 0.333, \"dashArray\": [30, 2, 8, 2] }\n ]\n },\n {\n \"id\": \"osm-hot\",\n \"startZoom\": 2,\n \"zoomThreshold\": 2,\n \"tileUrlTemplates\": [\n \"https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png\"\n ],\n \"lineWidthStops\": { \"2\": 3.0, \"3\": 3.0, \"7\": 3.0, \"8\": 3.5, \"9\": 3.5 },\n \"lineStyles\": [\n { \"color\": \"rgb(149, 175, 180)\" },\n { \"color\": \"rgb(89, 117, 123)\", \"widthFraction\": 0.33 }\n ]\n }\n]\n","/**\n * Convert a tile URL template to a regex pattern and capture group names.\n * Supports {z}, {x}, {y}, {s} (Leaflet subdomain), {a-c}/{1-4} (OpenLayers subdomain), and {r} (retina) placeholders.\n * @param {string} template - URL template like \"https://{s}.tile.example.com/{z}/{x}/{y}.png\"\n * @returns {{ pattern: RegExp, groups: string[] }}\n */\nfunction templateToRegex(template) {\n const groups = [];\n // Escape regex special chars, then replace placeholders\n let pattern = template\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n // Don't escape our placeholders\n if (char === '{' || char === '}') return char;\n return '\\\\' + char;\n })\n // Make protocol flexible (http/https)\n .replace(/^https:\\/\\//, 'https?://')\n .replace(/^http:\\/\\//, 'https?://')\n // Handle {a-c} or {1-4} etc (OpenLayers style subdomain)\n .replace(/\\{[a-z0-9]-[a-z0-9]\\}/gi, () => {\n groups.push('s');\n return '([a-z0-9]+)';\n })\n .replace(/\\{(z|x|y|s|r)\\}/gi, (_, name) => {\n const lowerName = name.toLowerCase();\n groups.push(lowerName);\n if (lowerName === 's') {\n // Subdomain: single letter or short string\n return '([a-z0-9]+)';\n }\n if (lowerName === 'r') {\n // Retina suffix: optional @2x or similar\n return '(@\\\\d+x)?';\n }\n // z, x, y: numeric\n return '(\\\\d+)';\n });\n \n // Allow optional query string at end\n return { pattern: new RegExp('^' + pattern + '(\\\\?.*)?$', 'i'), groups };\n}\n\n/**\n * Convert a tile URL template to a regex that matches the template itself.\n * @param {string} template - URL template like \"https://{s}.tile.example.com/{z}/{x}/{y}.png\"\n * @returns {RegExp}\n */\nfunction templateToTemplateRegex(template) {\n // Escape regex special chars, then replace placeholders with literal match\n let pattern = template\n .replace(/[.*+?^${}()|[\\]\\\\]/g, (char) => {\n if (char === '{' || char === '}') return char;\n return '\\\\' + char;\n })\n // Make protocol flexible (http/https)\n .replace(/^https:\\/\\//, 'https?://')\n .replace(/^http:\\/\\//, 'https?://')\n // Handle {a-c} or {1-4} (OpenLayers style subdomain)\n .replace(/\\{([a-z0-9])-([a-z0-9])\\}/gi, (_, start, end) => `(\\\\{${start}-${end}\\\\}|\\\\{s\\\\}|[a-z0-9]+)`)\n .replace(/\\{(z|x|y|s|r)\\}/gi, (_, name) => {\n const lowerName = name.toLowerCase();\n if (lowerName === 's') {\n // Match {s} placeholder, {a-c} style placeholder, or actual subdomain\n return '(\\\\{s\\\\}|\\\\{[a-z0-9]-[a-z0-9]\\\\}|[a-z0-9]+)';\n }\n if (lowerName === 'r') {\n // Match {r} placeholder or actual retina or empty\n return '(\\\\{r\\\\}|@\\\\d+x)?';\n }\n // Match {z}, {x}, {y} placeholders\n return `\\\\{${lowerName}\\\\}`;\n });\n \n // Allow optional query string at end\n return new RegExp('^' + pattern + '(\\\\?.*)?$', 'i');\n}\n\n/**\n * Base class for layer configurations\n * \n * Supports separate styling for NE (Natural Earth) data at low zoom levels\n * and OSM data at higher zoom levels, split by zoomThreshold.\n */\nexport class LayerConfig {\n constructor({\n id,\n startZoom = 0,\n zoomThreshold = 5,\n // Tile URL templates for matching (e.g., \"https://{s}.tile.example.com/{z}/{x}/{y}.png\")\n tileUrlTemplates = [],\n // Line width stops: map of zoom level to line width (at least 2 entries)\n lineWidthStops = { 1: 0.5, 10: 2.5 },\n // Line styles array - each element describes a line to draw\n // { color: string, widthFraction?: number, dashArray?: number[], startZoom?: number, endZoom?: number }\n // Lines are drawn in array order. startZoom defaults to layerConfig startZoom, endZoom defaults to Infinity\n lineStyles = [{ color: 'green', widthFraction: 1.0 }],\n // Factor to multiply line width for deletion blur (default 1.5)\n // Higher values leave gaps where wiped lines meet existing lines\n // Lower values mean wiped lines show through\n delWidthFactor = 1.5,\n // Factor to extend add lines by (multiplied by deletion line width)\n // Helps cover gaps where deleted lines meet the new boundary\n // Set to 0 to disable extension\n lineExtensionFactor = 0.5,\n }) {\n if (!id || typeof id !== 'string') {\n throw new Error('LayerConfig requires a non-empty string id');\n }\n if (id.includes('/')) {\n throw new Error(`LayerConfig id cannot contain slashes: \"${id}\"`);\n }\n\n this.id = id;\n this.startZoom = startZoom;\n this.zoomThreshold = zoomThreshold;\n\n if (startZoom > zoomThreshold) {\n throw new Error(`LayerConfig \"${id}\": startZoom (${startZoom}) must be <= zoomThreshold (${zoomThreshold})`);\n }\n\n // Normalize to array\n const templates = Array.isArray(tileUrlTemplates) ? tileUrlTemplates : \n (tileUrlTemplates ? [tileUrlTemplates] : []);\n this.tileUrlTemplates = templates;\n \n // Pre-compile regex patterns for matching tile URLs (with actual coords)\n this._compiledPatterns = templates.map(t => templateToRegex(t));\n \n // Pre-compile regex patterns for matching template URLs (with {z}/{x}/{y} placeholders)\n this._templatePatterns = templates.map(t => templateToTemplateRegex(t));\n\n // Validate lineWidthStops\n if (!lineWidthStops || typeof lineWidthStops !== 'object' || Array.isArray(lineWidthStops)) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops must be an object`);\n }\n const stopKeys = Object.keys(lineWidthStops);\n if (stopKeys.length < 2) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops must have at least 2 entries`);\n }\n for (const key of stopKeys) {\n const zoom = Number(key);\n if (!Number.isInteger(zoom) || zoom < 0) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops keys must be non-negative integers, got \"${key}\"`);\n }\n if (typeof lineWidthStops[key] !== 'number' || lineWidthStops[key] <= 0) {\n throw new Error(`LayerConfig \"${id}\": lineWidthStops values must be positive numbers`);\n }\n }\n this.lineWidthStops = lineWidthStops;\n\n // Validate lineStyles\n if (!Array.isArray(lineStyles) || lineStyles.length === 0) {\n throw new Error(`LayerConfig \"${id}\": lineStyles must be a non-empty array`);\n }\n for (let i = 0; i < lineStyles.length; i++) {\n const style = lineStyles[i];\n if (!style || typeof style !== 'object') {\n throw new Error(`LayerConfig \"${id}\": lineStyles[${i}] must be an object`);\n }\n if (!style.color || typeof style.color !== 'string') {\n throw new Error(`LayerConfig \"${id}\": lineStyles[${i}].color must be a non-empty string`);\n }\n }\n \n // Line styles - normalize startZoom/endZoom defaults\n this.lineStyles = lineStyles.map(style => ({\n ...style,\n startZoom: style.startZoom ?? startZoom,\n endZoom: style.endZoom ?? Infinity,\n }));\n \n // Deletion width factor\n this.delWidthFactor = delWidthFactor;\n \n // Line extension factor\n this.lineExtensionFactor = lineExtensionFactor;\n }\n\n /**\n * Get line styles active at a given zoom level\n * @param {number} z - Zoom level\n * @returns {Array<{color: string, widthFraction?: number, dashArray?: number[]}>}\n */\n getLineStylesForZoom(z) {\n return this.lineStyles.filter(style => z >= style.startZoom && z <= style.endZoom);\n }\n\n /**\n * Check if this config matches the given template URLs (with {z}/{x}/{y} placeholders)\n * @param {string | string[]} templates - Single template URL or array of template URLs\n * @returns {boolean}\n */\n matchTemplate(templates) {\n if (this._templatePatterns.length === 0) return false;\n \n const urls = Array.isArray(templates) ? templates : [templates];\n if (urls.length === 0) return false;\n \n return urls.some(url => \n this._templatePatterns.some(pattern => pattern.test(url))\n );\n }\n\n /**\n * Check if this config matches the given tile URLs (with actual coordinates)\n * @param {string | string[]} tiles - Single tile URL or array of tile URLs\n * @returns {boolean}\n */\n matchTileUrl(tiles) {\n if (this._compiledPatterns.length === 0) return false;\n \n const urls = Array.isArray(tiles) ? tiles : [tiles];\n if (urls.length === 0) return false;\n \n return urls.some(url => \n this._compiledPatterns.some(({ pattern }) => pattern.test(url))\n );\n }\n\n /**\n * Extract tile coordinates (z, x, y) from a URL using this config's templates\n * @param {string} url - Tile URL to extract coordinates from\n * @returns {{ z: number, x: number, y: number } | null}\n */\n extractCoords(url) {\n for (const { pattern, groups } of this._compiledPatterns) {\n const match = url.match(pattern);\n if (match) {\n const result = {};\n for (let i = 0; i < groups.length; i++) {\n const name = groups[i];\n const value = match[i + 1];\n if (name === 'z' || name === 'x' || name === 'y') {\n result[name] = parseInt(value, 10);\n }\n }\n if ('z' in result && 'x' in result && 'y' in result) {\n return { z: result.z, x: result.x, y: result.y };\n }\n }\n }\n return null;\n }\n\n /**\n * Serialize the config to a plain object for postMessage\n * @returns {Object}\n */\n toJSON() {\n return {\n id: this.id,\n startZoom: this.startZoom,\n zoomThreshold: this.zoomThreshold,\n tileUrlTemplates: this.tileUrlTemplates,\n lineWidthStops: this.lineWidthStops,\n lineStyles: this.lineStyles,\n delWidthFactor: this.delWidthFactor,\n lineExtensionFactor: this.lineExtensionFactor,\n };\n }\n\n /**\n * Create a LayerConfig from a plain object (e.g., from postMessage)\n * @param {Object} obj\n * @returns {LayerConfig}\n */\n static fromJSON(obj) {\n return new LayerConfig(obj);\n }\n}\n\nexport default LayerConfig;\n","import configsJson from './configs.json' with { type: 'json' };\nimport { LayerConfig } from './layerconfig.js';\n\nexport { LayerConfig } from './layerconfig.js';\n\n/**\n * Layer configuration registry\n */\nexport class LayerConfigRegistry {\n constructor() {\n this.registry = {};\n }\n\n /**\n * Get a layer config by id\n */\n get(id) {\n return this.registry[id];\n }\n\n /**\n * Register a new layer config\n */\n register(config) {\n this.registry[config.id] = config;\n }\n\n /**\n * Remove a layer config by id\n */\n remove(id) {\n if (!this.registry[id]) return false;\n delete this.registry[id];\n return true;\n }\n\n /**\n * Detect layer config from tile URL templates (with {z}/{x}/{y} placeholders)\n * @param {string | string[]} templates - Single template URL or array of template URLs\n */\n detectFromTemplates(templates) {\n if (!templates || (Array.isArray(templates) && templates.length === 0)) return undefined;\n \n for (const config of Object.values(this.registry)) {\n if (config.matchTemplate(templates)) {\n return config;\n }\n }\n \n return undefined;\n }\n\n /**\n * Detect layer config from actual tile URLs (with numeric coordinates)\n * @param {string | string[]} urls - Single tile URL or array of tile URLs\n */\n detectFromTileUrls(urls) {\n if (!urls || (Array.isArray(urls) && urls.length === 0)) return undefined;\n \n for (const config of Object.values(this.registry)) {\n if (config.matchTileUrl(urls)) {\n return config;\n }\n }\n \n return undefined;\n }\n\n /**\n * Get all available layer config ids\n */\n getAvailableIds() {\n return Object.keys(this.registry);\n }\n\n /**\n * Create a new registry with all configs from this registry plus extra configs.\n * @param {LayerConfig[]} extraLayerConfigs - Additional configs to add\n * @returns {LayerConfigRegistry} A new registry with merged configs\n */\n createMergedRegistry(extraLayerConfigs) {\n const registry = new LayerConfigRegistry();\n \n for (const id of this.getAvailableIds()) {\n registry.register(this.get(id));\n }\n \n if (extraLayerConfigs && extraLayerConfigs.length > 0) {\n for (const config of extraLayerConfigs) {\n registry.register(config);\n }\n }\n \n return registry;\n }\n\n /**\n * Parse a tile URL into its components: layer config and coordinates\n * @param {string} url - Tile URL to parse\n * @returns {{ layerConfig: LayerConfig, coords: { z: number, x: number, y: number } } | null}\n */\n parseTileUrl(url) {\n // Check if URL matches any layer config\n const layerConfig = this.detectFromTileUrls([url]);\n if (!layerConfig) return null;\n \n // Extract tile coordinates using the matched config\n const coords = layerConfig.extractCoords(url);\n if (!coords) return null;\n \n return { layerConfig, coords };\n }\n}\n\n// Default registry with built-in configs loaded from JSON\nexport const layerConfigs = new LayerConfigRegistry();\nfor (const configData of configsJson) {\n layerConfigs.register(new LayerConfig(configData));\n}\n\n","// Auto-generated by scripts/generate-version.js - DO NOT EDIT\nexport const packageVersion = '0.0.3';\n","import { dataVersion } from './data_version.js';\nimport { packageVersion } from './version.js';\n\n// Package info for CDN URL construction\nconst PACKAGE_NAME = '@india-boundary-corrector/data';\nconst PMTILES_FILENAME = 'india_boundary_corrections.pmtiles';\n\n// Default CDN URL with pinned package version\nconst DEFAULT_CDN_URL = `https://unpkg.com/${PACKAGE_NAME}@${packageVersion}/${PMTILES_FILENAME}`;\n\n/**\n * Layer names in the PMTiles file\n */\nexport const layers = {\n toAddOsm: 'to-add-osm',\n toDelOsm: 'to-del-osm',\n toAddNe: 'to-add-ne',\n toDelNe: 'to-del-ne',\n};\n\n/**\n * Check if a URL hostname should use CDN fallback.\n * Some CDNs like esm.sh don't host static files, so we need to fall back to unpkg.\n * @param {string} hostname - The hostname to check\n * @returns {boolean} True if CDN fallback should be used\n */\nexport function shouldUseCdnFallback(hostname) {\n // esm.sh doesn't host static files, only transforms JS modules\n return hostname === 'esm.sh';\n}\n\n/**\n * Detect the PMTiles URL from various sources:\n * 1. import.meta.url (for ESM bundlers - most reliable)\n * 2. Fallback to unpkg CDN with pinned version\n * \n * Note: When this package is bundled into another bundle, import.meta.url\n * won't work and we fall back to the CDN URL. Users can override with\n * setPmtilesUrl() for self-hosted scenarios.\n */\nfunction detectPmtilesUrl() {\n // Try import.meta.url first (works in ESM environments)\n try {\n if (typeof import.meta !== 'undefined' && import.meta.url) {\n const moduleUrl = new URL('.', import.meta.url);\n // Some CDNs don't host static files, fall back to unpkg\n if (shouldUseCdnFallback(moduleUrl.hostname)) {\n return DEFAULT_CDN_URL;\n }\n return new URL(PMTILES_FILENAME, moduleUrl).href;\n }\n } catch {\n // import.meta not available (UMD/CJS/bundled)\n }\n\n // Fallback to CDN with pinned version\n // This ensures it works even when bundled into another package\n return DEFAULT_CDN_URL;\n}\n\n// Cache the detected URL\nlet cachedPmtilesUrl = null;\n\n/**\n * Get the URL for the PMTiles file.\n * \n * Detection priority:\n * 1. Manually set URL via setPmtilesUrl()\n * 2. import.meta.url (ESM environments)\n * 3. unpkg CDN fallback (pinned to current version)\n * \n * For self-hosted deployments or custom bundling scenarios,\n * use setPmtilesUrl().\n * \n * @returns {string} URL to the PMTiles file\n */\nexport function getPmtilesUrl() {\n if (cachedPmtilesUrl === null) {\n cachedPmtilesUrl = detectPmtilesUrl();\n }\n return cachedPmtilesUrl;\n}\n\n/**\n * Manually set the PMTiles URL.\n * Use this for self-hosted deployments or custom bundling scenarios.\n * \n * @param {string} url - The URL to the PMTiles file\n * \n * @example\n * // Self-hosted\n * setPmtilesUrl('/assets/india_boundary_corrections.pmtiles');\n * \n * @example\n * // Different CDN\n * setPmtilesUrl('https://my-cdn.com/india_boundary_corrections.pmtiles');\n */\nexport function setPmtilesUrl(url) {\n cachedPmtilesUrl = url;\n}\n\n/**\n * Get the data version string\n * @returns {string} Data version identifier\n */\nexport function getDataVersion() {\n return dataVersion;\n}\n"],"mappings":"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","names":[]}
|
package/dist/worker.global.js
CHANGED
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var IndiaBoundaryCorrector = (() => {
|
|
3
3
|
// ../data/version.js
|
|
4
|
-
var packageVersion = "0.0.
|
|
4
|
+
var packageVersion = "0.0.3";
|
|
5
5
|
|
|
6
6
|
// ../data/index.js
|
|
7
7
|
var import_meta = {};
|
|
8
8
|
var PACKAGE_NAME = "@india-boundary-corrector/data";
|
|
9
9
|
var PMTILES_FILENAME = "india_boundary_corrections.pmtiles";
|
|
10
10
|
var DEFAULT_CDN_URL = `https://unpkg.com/${PACKAGE_NAME}@${packageVersion}/${PMTILES_FILENAME}`;
|
|
11
|
+
function shouldUseCdnFallback(hostname) {
|
|
12
|
+
return hostname === "esm.sh";
|
|
13
|
+
}
|
|
11
14
|
function detectPmtilesUrl() {
|
|
12
15
|
try {
|
|
13
16
|
if (typeof import_meta !== "undefined" && import_meta.url) {
|
|
14
17
|
const moduleUrl = new URL(".", import_meta.url);
|
|
18
|
+
if (shouldUseCdnFallback(moduleUrl.hostname)) {
|
|
19
|
+
return DEFAULT_CDN_URL;
|
|
20
|
+
}
|
|
15
21
|
return new URL(PMTILES_FILENAME, moduleUrl).href;
|
|
16
22
|
}
|
|
17
23
|
} catch {
|
|
@@ -137,10 +143,10 @@ var IndiaBoundaryCorrector = (() => {
|
|
|
137
143
|
let pattern = template.replace(/[.*+?^${}()|[\]\\]/g, (char) => {
|
|
138
144
|
if (char === "{" || char === "}") return char;
|
|
139
145
|
return "\\" + char;
|
|
140
|
-
}).replace(/^https:\/\//, "https?://").replace(/^http:\/\//, "https?://").replace(/\{([a-z0-9])-([a-z0-9])\}/gi, (_2, start, end) => `(\\{${start}-${end}\\}|[a-z0-9]+)`).replace(/\{(z|x|y|s|r)\}/gi, (_2, name) => {
|
|
146
|
+
}).replace(/^https:\/\//, "https?://").replace(/^http:\/\//, "https?://").replace(/\{([a-z0-9])-([a-z0-9])\}/gi, (_2, start, end) => `(\\{${start}-${end}\\}|\\{s\\}|[a-z0-9]+)`).replace(/\{(z|x|y|s|r)\}/gi, (_2, name) => {
|
|
141
147
|
const lowerName = name.toLowerCase();
|
|
142
148
|
if (lowerName === "s") {
|
|
143
|
-
return "(\\{s\\}|[a-z0-9]+)";
|
|
149
|
+
return "(\\{s\\}|\\{[a-z0-9]-[a-z0-9]\\}|[a-z0-9]+)";
|
|
144
150
|
}
|
|
145
151
|
if (lowerName === "r") {
|
|
146
152
|
return "(\\{r\\}|@\\d+x)?";
|
|
@@ -174,6 +180,9 @@ var IndiaBoundaryCorrector = (() => {
|
|
|
174
180
|
if (!id || typeof id !== "string") {
|
|
175
181
|
throw new Error("LayerConfig requires a non-empty string id");
|
|
176
182
|
}
|
|
183
|
+
if (id.includes("/")) {
|
|
184
|
+
throw new Error(`LayerConfig id cannot contain slashes: "${id}"`);
|
|
185
|
+
}
|
|
177
186
|
this.id = id;
|
|
178
187
|
this.startZoom = startZoom;
|
|
179
188
|
this.zoomThreshold = zoomThreshold;
|
|
@@ -2675,7 +2684,7 @@ var IndiaBoundaryCorrector = (() => {
|
|
|
2675
2684
|
if (!maskCanvas || maskCanvas.width !== tileSize2 || maskCanvas.height !== tileSize2) {
|
|
2676
2685
|
maskCanvas = new OffscreenCanvas(tileSize2, tileSize2);
|
|
2677
2686
|
}
|
|
2678
|
-
const maskCtx = maskCanvas.getContext("2d");
|
|
2687
|
+
const maskCtx = maskCanvas.getContext("2d", { willReadFrequently: true });
|
|
2679
2688
|
maskCtx.fillStyle = "black";
|
|
2680
2689
|
maskCtx.fillRect(0, 0, tileSize2, tileSize2);
|
|
2681
2690
|
maskCtx.strokeStyle = "white";
|
|
@@ -2877,8 +2886,11 @@ var IndiaBoundaryCorrector = (() => {
|
|
|
2877
2886
|
const useOsm = zoom >= zoomThreshold;
|
|
2878
2887
|
const addLayerName = useOsm ? "to-add-osm" : "to-add-ne";
|
|
2879
2888
|
const delLayerName = useOsm ? "to-del-osm" : "to-del-ne";
|
|
2880
|
-
|
|
2881
|
-
|
|
2889
|
+
if (!this._canvas || this._canvas.width !== tileSize2) {
|
|
2890
|
+
this._canvas = new OffscreenCanvas(tileSize2, tileSize2);
|
|
2891
|
+
}
|
|
2892
|
+
const canvas = this._canvas;
|
|
2893
|
+
const ctx = canvas.getContext("2d", { willReadFrequently: true });
|
|
2882
2894
|
const blob = new Blob([rasterTile]);
|
|
2883
2895
|
const imageBitmap = await createImageBitmap(blob);
|
|
2884
2896
|
ctx.drawImage(imageBitmap, 0, 0, tileSize2, tileSize2);
|