@india-boundary-corrector/maplibre-protocol 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
@@ -19,9 +19,9 @@ npm install @india-boundary-corrector/maplibre-protocol maplibre-gl
19
19
  No bundler required! Just include the script and use the global `IndiaBoundaryCorrector`:
20
20
 
21
21
  ```html
22
- <link rel="stylesheet" href="https://unpkg.com/maplibre-gl@5.0.1/dist/maplibre-gl.css" />
23
- <script src="https://unpkg.com/maplibre-gl@5.0.1/dist/maplibre-gl.js"></script>
24
- <script src="https://unpkg.com/@india-boundary-corrector/maplibre-protocol/dist/index.global.js"></script>
22
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@5.0.1/dist/maplibre-gl.css" />
23
+ <script src="https://cdn.jsdelivr.net/npm/maplibre-gl@5.0.1/dist/maplibre-gl.js"></script>
24
+ <script src="https://cdn.jsdelivr.net/npm/@india-boundary-corrector/maplibre-protocol/dist/index.global.js"></script>
25
25
 
26
26
  <div id="map" style="height: 400px;"></div>
27
27
 
@@ -124,7 +124,8 @@ const map = new maplibregl.Map({
124
124
  type: 'raster',
125
125
  tiles: ['ibc://https://tile.openstreetmap.de/{z}/{x}/{y}.png'],
126
126
  // Or explicit: tiles: ['ibc://osm-de@https://tile.openstreetmap.de/{z}/{x}/{y}.png']
127
- tileSize: 256
127
+ tileSize: 256,
128
+ attribution: '© OpenStreetMap contributors'
128
129
  }
129
130
  },
130
131
  layers: [
@@ -159,7 +160,7 @@ Convenience function to create and register a protocol.
159
160
  |-----------|------|-------------|
160
161
  | `maplibregl` | object | MapLibre GL namespace |
161
162
  | `options.pmtilesUrl` | string | URL to PMTiles file |
162
- | `options.tileSize` | number | Tile size (default: 256) |
163
+ | `options.fallbackOnCorrectionFailure` | boolean | Return original tile if corrections fail (default: true) |
163
164
 
164
165
  Returns: `CorrectionProtocol`
165
166
 
@@ -203,6 +204,10 @@ protocol.on('correctionerror', (e) => {
203
204
  | `coords` | object | Tile coordinates `{ z, x, y }` |
204
205
  | `tileUrl` | string | URL of the tile being loaded |
205
206
 
207
+ ## Bundling
208
+
209
+ 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.
210
+
206
211
  ## License
207
212
 
208
213
  Unlicense
package/dist/index.cjs CHANGED
@@ -22,7 +22,6 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CorrectionProtocol: () => CorrectionProtocol,
24
24
  LayerConfig: () => import_layer_configs2.LayerConfig,
25
- fetchAndFixTile: () => fetchAndFixTile,
26
25
  getPmtilesUrl: () => import_data2.getPmtilesUrl,
27
26
  layerConfigs: () => import_layer_configs2.layerConfigs,
28
27
  parseCorrectionsUrl: () => parseCorrectionsUrl,
@@ -80,27 +79,16 @@ function parseCorrectionsUrl(url, registry) {
80
79
  y: coords?.y
81
80
  };
82
81
  }
83
- async function fetchAndFixTile(tileUrl, z, x, y, tileFixer, layerConfig, tileSize, options = {}) {
84
- const { data, correctionsFailed, correctionsError } = await tileFixer.fetchAndFixTile(
85
- tileUrl,
86
- z,
87
- x,
88
- y,
89
- layerConfig,
90
- { tileSize, signal: options.signal }
91
- );
92
- return { data, correctionsFailed, correctionsError };
93
- }
94
82
  var CorrectionProtocol = class {
95
83
  /**
96
84
  * @param {Object} [options]
97
85
  * @param {string} [options.pmtilesUrl] - URL to PMTiles file (defaults to CDN)
98
- * @param {number} [options.tileSize=256] - Tile size in pixels
86
+ * @param {boolean} [options.fallbackOnCorrectionFailure=true] - Return original tile if corrections fail
99
87
  */
100
88
  constructor(options = {}) {
101
89
  this._pmtilesUrl = options.pmtilesUrl ?? (0, import_data.getPmtilesUrl)();
102
- this._tileSize = options.tileSize ?? 256;
103
- this._tileFixer = new import_tilefixer.BoundaryCorrector(this._pmtilesUrl);
90
+ this._fallbackOnCorrectionFailure = options.fallbackOnCorrectionFailure ?? true;
91
+ this._tileFixer = import_tilefixer.TileFixer.getOrCreate(this._pmtilesUrl);
104
92
  this._registry = import_layer_configs.layerConfigs.createMergedRegistry();
105
93
  this._listeners = /* @__PURE__ */ new Map();
106
94
  this._loadFn = this._createLoadFunction();
@@ -198,28 +186,27 @@ var CorrectionProtocol = class {
198
186
  } else {
199
187
  layerConfig = self._registry.detectFromTileUrls([tileUrl]);
200
188
  }
201
- try {
202
- const result = await fetchAndFixTile(
203
- tileUrl,
204
- z,
205
- x,
206
- y,
207
- self._tileFixer,
208
- layerConfig,
209
- self._tileSize,
210
- { signal: abortController?.signal }
211
- );
212
- if (result.correctionsFailed && result.correctionsError?.name !== "AbortError") {
213
- console.warn("[CorrectionProtocol] Corrections fetch failed:", result.correctionsError);
214
- self._emit("correctionerror", { error: result.correctionsError, coords: { z, x, y }, tileUrl });
215
- }
216
- return { data: result.data };
217
- } catch (err) {
218
- if (err.name === "AbortError") {
219
- throw err;
220
- }
221
- throw err;
189
+ const fetchOptions = {
190
+ signal: abortController?.signal,
191
+ credentials: params.credentials ?? "same-origin"
192
+ };
193
+ if (params.credentials === "include") {
194
+ fetchOptions.mode = "cors";
195
+ }
196
+ const { data, correctionsFailed, correctionsError } = await self._tileFixer.fetchAndFixTile(
197
+ tileUrl,
198
+ z,
199
+ x,
200
+ y,
201
+ layerConfig,
202
+ fetchOptions,
203
+ self._fallbackOnCorrectionFailure
204
+ );
205
+ if (correctionsFailed && correctionsError?.name !== "AbortError") {
206
+ console.warn("[CorrectionProtocol] Corrections fetch failed:", correctionsError);
207
+ self._emit("correctionerror", { error: correctionsError, coords: { z, x, y }, tileUrl });
222
208
  }
209
+ return { data };
223
210
  };
224
211
  }
225
212
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.js"],"sourcesContent":["import { getPmtilesUrl } from '@india-boundary-corrector/data';\nimport { layerConfigs } from '@india-boundary-corrector/layer-configs';\nimport { BoundaryCorrector as TileFixer } from '@india-boundary-corrector/tilefixer';\n\n// Re-export for convenience\nexport { layerConfigs, LayerConfig } from '@india-boundary-corrector/layer-configs';\nexport { getPmtilesUrl } from '@india-boundary-corrector/data';\n\nconst PROTOCOL_PREFIX = 'ibc';\n\n/**\n * Extract tile coordinates from a URL using generic z/x/y pattern matching.\n * Handles standard tile URL patterns including retina suffixes (@2x, @3x, etc.).\n * \n * @param {string} url - The tile URL to parse\n * @returns {{ z: number, x: number, y: number } | null} Parsed coordinates or null if not found\n */\nfunction extractTileCoordsFromUrl(url) {\n try {\n const urlObj = new URL(url);\n const pathParts = urlObj.pathname.split('/').filter(p => p.length > 0);\n \n // Find z/x/y pattern - typically last 3 numeric segments\n for (let i = pathParts.length - 1; i >= 2; i--) {\n // Remove extension and retina suffix (e.g., \"5@2x.png\" -> \"5\")\n const yPart = pathParts[i].replace(/(@\\d+x)?\\.[^.]+$/, '');\n const xPart = pathParts[i - 1];\n const zPart = pathParts[i - 2];\n \n if (/^\\d+$/.test(zPart) && /^\\d+$/.test(xPart) && /^\\d+$/.test(yPart)) {\n return {\n z: parseInt(zPart, 10),\n x: parseInt(xPart, 10),\n y: parseInt(yPart, 10)\n };\n }\n }\n } catch {\n // Invalid URL\n }\n return null;\n}\n\n/**\n * Parse an ibc:// URL.\n * Format: ibc://[configId@]originalUrl\n * Examples:\n * ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png\n * ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png\n * \n * @param {string} url - The full URL with ibc:// prefix\n * @param {import('@india-boundary-corrector/layer-configs').LayerConfigRegistry} registry - Registry to use for parsing\n * @returns {{ configId: string|null, tileUrl: string, z: number|undefined, x: number|undefined, y: number|undefined }}\n */\nfunction parseCorrectionsUrl(url, registry) {\n // Remove protocol prefix\n const withoutProtocol = url.replace(`${PROTOCOL_PREFIX}://`, '');\n \n // Check for configId@url format\n let configId = null;\n let tileUrl = withoutProtocol;\n \n const atIndex = withoutProtocol.indexOf('@');\n const slashIndex = withoutProtocol.indexOf('/');\n // Config ID exists if @ comes before first / (or if there's no /)\n if (atIndex > 0 && (slashIndex === -1 || atIndex < slashIndex)) {\n // Has configId prefix\n configId = withoutProtocol.substring(0, atIndex);\n tileUrl = withoutProtocol.substring(atIndex + 1);\n }\n \n // If configId is explicit, use generic parsing (URL may not match config's patterns)\n // Otherwise, use registry detection only (unregistered URLs won't get corrections)\n let coords = null;\n if (configId) {\n coords = extractTileCoordsFromUrl(tileUrl);\n } else {\n const parsed = registry.parseTileUrl(tileUrl);\n coords = parsed?.coords ?? null;\n }\n \n return { \n configId, \n tileUrl, \n z: coords?.z, \n x: coords?.x, \n y: coords?.y \n };\n}\n\n/**\n * Fetch and fix a tile for MapLibre protocol.\n * Extracted for testability.\n * @param {string} tileUrl - URL of the raster tile\n * @param {number} z - Zoom level\n * @param {number} x - Tile X coordinate\n * @param {number} y - Tile Y coordinate\n * @param {TileFixer} tileFixer - TileFixer instance\n * @param {Object} layerConfig - Layer configuration (can be null)\n * @param {number} tileSize - Tile size in pixels\n * @param {Object} [options] - Fetch options\n * @param {AbortSignal} [options.signal] - Abort signal\n * @returns {Promise<{data: ArrayBuffer, correctionsFailed: boolean, correctionsError: Error|null}>}\n */\nasync function fetchAndFixTile(tileUrl, z, x, y, tileFixer, layerConfig, tileSize, options = {}) {\n const { data, correctionsFailed, correctionsError } = await tileFixer.fetchAndFixTile(\n tileUrl, z, x, y, layerConfig, { tileSize, signal: options.signal }\n );\n return { data, correctionsFailed, correctionsError };\n}\n\n/**\n * India boundary corrections protocol for MapLibre GL.\n * \n * Usage:\n * const protocol = new CorrectionProtocol();\n * protocol.register(maplibregl);\n * \n * // In your style:\n * tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png']\n * // Or with explicit config:\n * tiles: ['ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png']\n */\nexport class CorrectionProtocol {\n /**\n * @param {Object} [options]\n * @param {string} [options.pmtilesUrl] - URL to PMTiles file (defaults to CDN)\n * @param {number} [options.tileSize=256] - Tile size in pixels\n */\n constructor(options = {}) {\n this._pmtilesUrl = options.pmtilesUrl ?? getPmtilesUrl();\n this._tileSize = options.tileSize ?? 256;\n this._tileFixer = new TileFixer(this._pmtilesUrl);\n this._registry = layerConfigs.createMergedRegistry();\n /** @type {Map<string, Set<Function>>} */\n this._listeners = new Map();\n \n this._loadFn = this._createLoadFunction();\n }\n\n /**\n * Add a listener for an event.\n * @param {'correctionerror'} event - Event name\n * @param {Function} listener - Callback function receiving event data\n * @returns {this}\n */\n on(event, listener) {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set());\n }\n this._listeners.get(event).add(listener);\n return this;\n }\n\n /**\n * Remove an event listener.\n * @param {'correctionerror'} event - Event name\n * @param {Function} listener - Callback to remove\n * @returns {this}\n */\n off(event, listener) {\n this._listeners.get(event)?.delete(listener);\n return this;\n }\n\n /**\n * Emit an event to all listeners.\n * @param {string} event - Event name\n * @param {Object} data - Event data\n * @private\n */\n _emit(event, data) {\n this._listeners.get(event)?.forEach(fn => fn(data));\n }\n\n /**\n * Add a custom layer config to the registry.\n * @param {Object} layerConfig - LayerConfig to add\n * @returns {this}\n */\n addLayerConfig(layerConfig) {\n this._registry.register(layerConfig);\n return this;\n }\n\n /**\n * Get the registry.\n * @returns {LayerConfigRegistry}\n */\n getRegistry() {\n return this._registry;\n }\n\n /**\n * Get the TileFixer instance.\n * @returns {TileFixer}\n */\n getTileFixer() {\n return this._tileFixer;\n }\n\n /**\n * Register the protocol with MapLibre GL.\n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @returns {this}\n */\n register(maplibregl) {\n maplibregl.addProtocol(PROTOCOL_PREFIX, this._loadFn);\n return this;\n }\n\n /**\n * Unregister the protocol from MapLibre GL.\n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @returns {this}\n */\n unregister(maplibregl) {\n maplibregl.removeProtocol(PROTOCOL_PREFIX);\n return this;\n }\n\n /**\n * Create the protocol load function.\n * @returns {Function}\n * @private\n */\n _createLoadFunction() {\n const self = this;\n \n return async (params, abortController) => {\n const { configId, tileUrl, z, x, y } = parseCorrectionsUrl(params.url, self._registry);\n \n // Validate parsed coordinates\n if (z === undefined || x === undefined || y === undefined) {\n console.warn(`[CorrectionProtocol] Could not parse tile coordinates from URL: ${params.url}, falling back to original`);\n const response = await fetch(tileUrl, { signal: abortController?.signal });\n return { data: await response.arrayBuffer() };\n }\n \n // Resolve layer config\n let layerConfig;\n if (configId) {\n layerConfig = self._registry.get(configId);\n } else {\n layerConfig = self._registry.detectFromTileUrls([tileUrl]);\n }\n \n try {\n const result = await fetchAndFixTile(\n tileUrl,\n z, \n x,\n y,\n self._tileFixer,\n layerConfig,\n self._tileSize,\n { signal: abortController?.signal }\n );\n \n if (result.correctionsFailed && result.correctionsError?.name !== 'AbortError') {\n console.warn('[CorrectionProtocol] Corrections fetch failed:', result.correctionsError);\n self._emit('correctionerror', { error: result.correctionsError, coords: { z, x, y }, tileUrl });\n }\n \n return { data: result.data };\n } catch (err) {\n // Don't catch AbortError - let it propagate\n if (err.name === 'AbortError') {\n throw err;\n }\n // Re-throw other errors (tile fetch failures, processing errors)\n // correctionerror is only for PMTiles/correction failures (handled via correctionsFailed)\n throw err;\n }\n };\n }\n}\n\n/**\n * Create and register a correction protocol with MapLibre GL.\n * \n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @param {Object} [options] - Protocol options\n * @param {string} [options.pmtilesUrl] - URL to PMTiles file\n * @param {number} [options.tileSize=256] - Tile size in pixels\n * @returns {CorrectionProtocol}\n * \n * @example\n * import maplibregl from 'maplibre-gl';\n * import { registerCorrectionProtocol } from '@india-boundary-corrector/maplibre-protocol';\n * \n * const protocol = registerCorrectionProtocol(maplibregl);\n * \n * // Use in style:\n * const map = new maplibregl.Map({\n * container: 'map',\n * style: {\n * sources: {\n * osm: {\n * type: 'raster',\n * tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png'],\n * tileSize: 256\n * }\n * },\n * layers: [{ id: 'osm', type: 'raster', source: 'osm' }]\n * }\n * });\n */\nexport function registerCorrectionProtocol(maplibregl, options = {}) {\n const protocol = new CorrectionProtocol(options);\n return protocol.register(maplibregl);\n}\n\n// Export for testing\nexport { parseCorrectionsUrl, fetchAndFixTile };\n"],"mappings":"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","names":["import_layer_configs","import_data","TileFixer"]}
1
+ {"version":3,"sources":["../src/index.js"],"sourcesContent":["import { getPmtilesUrl } from '@india-boundary-corrector/data';\nimport { layerConfigs } from '@india-boundary-corrector/layer-configs';\nimport { TileFixer } from '@india-boundary-corrector/tilefixer';\n\n// Re-export for convenience\nexport { layerConfigs, LayerConfig } from '@india-boundary-corrector/layer-configs';\nexport { getPmtilesUrl } from '@india-boundary-corrector/data';\n\nconst PROTOCOL_PREFIX = 'ibc';\n\n/**\n * Extract tile coordinates from a URL using generic z/x/y pattern matching.\n * Handles standard tile URL patterns including retina suffixes (@2x, @3x, etc.).\n * \n * @param {string} url - The tile URL to parse\n * @returns {{ z: number, x: number, y: number } | null} Parsed coordinates or null if not found\n */\nfunction extractTileCoordsFromUrl(url) {\n try {\n const urlObj = new URL(url);\n const pathParts = urlObj.pathname.split('/').filter(p => p.length > 0);\n \n // Find z/x/y pattern - typically last 3 numeric segments\n for (let i = pathParts.length - 1; i >= 2; i--) {\n // Remove extension and retina suffix (e.g., \"5@2x.png\" -> \"5\")\n const yPart = pathParts[i].replace(/(@\\d+x)?\\.[^.]+$/, '');\n const xPart = pathParts[i - 1];\n const zPart = pathParts[i - 2];\n \n if (/^\\d+$/.test(zPart) && /^\\d+$/.test(xPart) && /^\\d+$/.test(yPart)) {\n return {\n z: parseInt(zPart, 10),\n x: parseInt(xPart, 10),\n y: parseInt(yPart, 10)\n };\n }\n }\n } catch {\n // Invalid URL\n }\n return null;\n}\n\n/**\n * Parse an ibc:// URL.\n * Format: ibc://[configId@]originalUrl\n * Examples:\n * ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png\n * ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png\n * \n * @param {string} url - The full URL with ibc:// prefix\n * @param {import('@india-boundary-corrector/layer-configs').LayerConfigRegistry} registry - Registry to use for parsing\n * @returns {{ configId: string|null, tileUrl: string, z: number|undefined, x: number|undefined, y: number|undefined }}\n */\nfunction parseCorrectionsUrl(url, registry) {\n // Remove protocol prefix\n const withoutProtocol = url.replace(`${PROTOCOL_PREFIX}://`, '');\n \n // Check for configId@url format\n let configId = null;\n let tileUrl = withoutProtocol;\n \n const atIndex = withoutProtocol.indexOf('@');\n const slashIndex = withoutProtocol.indexOf('/');\n // Config ID exists if @ comes before first / (or if there's no /)\n if (atIndex > 0 && (slashIndex === -1 || atIndex < slashIndex)) {\n // Has configId prefix\n configId = withoutProtocol.substring(0, atIndex);\n tileUrl = withoutProtocol.substring(atIndex + 1);\n }\n \n // If configId is explicit, use generic parsing (URL may not match config's patterns)\n // Otherwise, use registry detection only (unregistered URLs won't get corrections)\n let coords = null;\n if (configId) {\n coords = extractTileCoordsFromUrl(tileUrl);\n } else {\n const parsed = registry.parseTileUrl(tileUrl);\n coords = parsed?.coords ?? null;\n }\n \n return { \n configId, \n tileUrl, \n z: coords?.z, \n x: coords?.x, \n y: coords?.y \n };\n}\n\n/**\n * India boundary corrections protocol for MapLibre GL.\n * \n * Usage:\n * const protocol = new CorrectionProtocol();\n * protocol.register(maplibregl);\n * \n * // In your style:\n * tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png']\n * // Or with explicit config:\n * tiles: ['ibc://osm-carto@https://tile.openstreetmap.org/{z}/{x}/{y}.png']\n */\nexport class CorrectionProtocol {\n /**\n * @param {Object} [options]\n * @param {string} [options.pmtilesUrl] - URL to PMTiles file (defaults to CDN)\n * @param {boolean} [options.fallbackOnCorrectionFailure=true] - Return original tile if corrections fail\n */\n constructor(options = {}) {\n this._pmtilesUrl = options.pmtilesUrl ?? getPmtilesUrl();\n this._fallbackOnCorrectionFailure = options.fallbackOnCorrectionFailure ?? true;\n this._tileFixer = TileFixer.getOrCreate(this._pmtilesUrl);\n this._registry = layerConfigs.createMergedRegistry();\n /** @type {Map<string, Set<Function>>} */\n this._listeners = new Map();\n \n this._loadFn = this._createLoadFunction();\n }\n\n /**\n * Add a listener for an event.\n * @param {'correctionerror'} event - Event name\n * @param {Function} listener - Callback function receiving event data\n * @returns {this}\n */\n on(event, listener) {\n if (!this._listeners.has(event)) {\n this._listeners.set(event, new Set());\n }\n this._listeners.get(event).add(listener);\n return this;\n }\n\n /**\n * Remove an event listener.\n * @param {'correctionerror'} event - Event name\n * @param {Function} listener - Callback to remove\n * @returns {this}\n */\n off(event, listener) {\n this._listeners.get(event)?.delete(listener);\n return this;\n }\n\n /**\n * Emit an event to all listeners.\n * @param {string} event - Event name\n * @param {Object} data - Event data\n * @private\n */\n _emit(event, data) {\n this._listeners.get(event)?.forEach(fn => fn(data));\n }\n\n /**\n * Add a custom layer config to the registry.\n * @param {Object} layerConfig - LayerConfig to add\n * @returns {this}\n */\n addLayerConfig(layerConfig) {\n this._registry.register(layerConfig);\n return this;\n }\n\n /**\n * Get the registry.\n * @returns {LayerConfigRegistry}\n */\n getRegistry() {\n return this._registry;\n }\n\n /**\n * Get the TileFixer instance.\n * @returns {TileFixer}\n */\n getTileFixer() {\n return this._tileFixer;\n }\n\n /**\n * Register the protocol with MapLibre GL.\n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @returns {this}\n */\n register(maplibregl) {\n maplibregl.addProtocol(PROTOCOL_PREFIX, this._loadFn);\n return this;\n }\n\n /**\n * Unregister the protocol from MapLibre GL.\n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @returns {this}\n */\n unregister(maplibregl) {\n maplibregl.removeProtocol(PROTOCOL_PREFIX);\n return this;\n }\n\n /**\n * Create the protocol load function.\n * @returns {Function}\n * @private\n */\n _createLoadFunction() {\n const self = this;\n \n return async (params, abortController) => {\n const { configId, tileUrl, z, x, y } = parseCorrectionsUrl(params.url, self._registry);\n \n // Validate parsed coordinates\n if (z === undefined || x === undefined || y === undefined) {\n console.warn(`[CorrectionProtocol] Could not parse tile coordinates from URL: ${params.url}, falling back to original`);\n const response = await fetch(tileUrl, { signal: abortController?.signal });\n return { data: await response.arrayBuffer() };\n }\n \n // Resolve layer config\n let layerConfig;\n if (configId) {\n layerConfig = self._registry.get(configId);\n } else {\n layerConfig = self._registry.detectFromTileUrls([tileUrl]);\n }\n \n // Build fetch options from request parameters\n // MapLibre's default is same-origin for credentials\n const fetchOptions = {\n signal: abortController?.signal,\n credentials: params.credentials ?? 'same-origin',\n };\n // Set mode to cors if credentials are explicitly set to include\n if (params.credentials === 'include') {\n fetchOptions.mode = 'cors';\n }\n \n const { data, correctionsFailed, correctionsError } = await self._tileFixer.fetchAndFixTile(\n tileUrl, z, x, y, layerConfig, fetchOptions, self._fallbackOnCorrectionFailure\n );\n \n if (correctionsFailed && correctionsError?.name !== 'AbortError') {\n console.warn('[CorrectionProtocol] Corrections fetch failed:', correctionsError);\n self._emit('correctionerror', { error: correctionsError, coords: { z, x, y }, tileUrl });\n }\n \n return { data };\n };\n }\n}\n\n/**\n * Create and register a correction protocol with MapLibre GL.\n * \n * @param {typeof import('maplibre-gl')} maplibregl - MapLibre GL namespace\n * @param {Object} [options] - Protocol options\n * @param {string} [options.pmtilesUrl] - URL to PMTiles file\n * @param {boolean} [options.fallbackOnCorrectionFailure=true] - Return original tile if corrections fail\n * @returns {CorrectionProtocol}\n * \n * @example\n * import maplibregl from 'maplibre-gl';\n * import { registerCorrectionProtocol } from '@india-boundary-corrector/maplibre-protocol';\n * \n * const protocol = registerCorrectionProtocol(maplibregl);\n * \n * // Use in style:\n * const map = new maplibregl.Map({\n * container: 'map',\n * style: {\n * sources: {\n * osm: {\n * type: 'raster',\n * tiles: ['ibc://https://tile.openstreetmap.org/{z}/{x}/{y}.png'],\n * tileSize: 256,\n * attribution: '© OpenStreetMap contributors'\n * }\n * },\n * layers: [{ id: 'osm', type: 'raster', source: 'osm' }]\n * }\n * });\n */\nexport function registerCorrectionProtocol(maplibregl, options = {}) {\n const protocol = new CorrectionProtocol(options);\n return protocol.register(maplibregl);\n}\n\n// Export for testing\nexport { parseCorrectionsUrl };\n"],"mappings":"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","names":["import_layer_configs","import_data"]}