@honua/react 0.1.0-beta.0 → 0.1.1-beta.0

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.
Files changed (315) hide show
  1. package/contract/discovery.d.ts +231 -0
  2. package/contract/discovery.d.ts.map +1 -0
  3. package/contract/discovery.js +554 -0
  4. package/contract/edit-sketch.d.ts +8 -0
  5. package/contract/edit-sketch.d.ts.map +1 -1
  6. package/contract/edit-sketch.js +15 -0
  7. package/contract/edit-snapping.d.ts +160 -0
  8. package/contract/edit-snapping.d.ts.map +1 -0
  9. package/contract/edit-snapping.js +675 -0
  10. package/contract/generated/projjson-v0.7-crs-validator.d.ts +35 -0
  11. package/contract/generated/projjson-v0.7-crs-validator.d.ts.map +1 -0
  12. package/contract/generated/projjson-v0.7-crs-validator.js +3941 -0
  13. package/contract/index.d.ts +11 -2
  14. package/contract/index.d.ts.map +1 -1
  15. package/contract/index.js +4 -1
  16. package/contract/pmtiles.d.ts +31 -0
  17. package/contract/pmtiles.d.ts.map +1 -1
  18. package/contract/pmtiles.js +60 -20
  19. package/contract/protocol-module.d.ts +70 -0
  20. package/contract/protocol-module.d.ts.map +1 -0
  21. package/contract/protocol-module.js +1 -0
  22. package/contract/schema-envelope.d.ts +26 -0
  23. package/contract/schema-envelope.d.ts.map +1 -0
  24. package/contract/schema-envelope.js +25 -0
  25. package/contract/schema.d.ts +582 -0
  26. package/contract/schema.d.ts.map +1 -0
  27. package/contract/schema.js +2483 -0
  28. package/contract/source-capability-support.d.ts +6 -0
  29. package/contract/source-capability-support.d.ts.map +1 -0
  30. package/contract/source-capability-support.js +154 -0
  31. package/contract/source.d.ts +35 -17
  32. package/contract/source.d.ts.map +1 -1
  33. package/contract/source.js +267 -71
  34. package/contract/types.d.ts +140 -4
  35. package/contract/types.d.ts.map +1 -1
  36. package/contract/types.js +6 -14
  37. package/core/bounded-json.d.ts +4 -0
  38. package/core/bounded-json.d.ts.map +1 -0
  39. package/core/bounded-json.js +38 -0
  40. package/core/cache-state.d.ts +9 -0
  41. package/core/cache-state.d.ts.map +1 -1
  42. package/core/cache-state.js +10 -0
  43. package/core/capabilities-xml.d.ts +33 -0
  44. package/core/capabilities-xml.d.ts.map +1 -0
  45. package/core/capabilities-xml.js +331 -0
  46. package/core/client.d.ts +4 -1
  47. package/core/client.d.ts.map +1 -1
  48. package/core/client.js +115 -29
  49. package/core/error-classifications.d.ts +153 -0
  50. package/core/error-classifications.d.ts.map +1 -0
  51. package/core/error-classifications.js +140 -0
  52. package/core/error-code-registry.d.ts +158 -0
  53. package/core/error-code-registry.d.ts.map +1 -0
  54. package/core/error-code-registry.js +143 -0
  55. package/core/error-envelope.d.ts +68 -0
  56. package/core/error-envelope.d.ts.map +1 -0
  57. package/core/error-envelope.js +357 -0
  58. package/core/errors.d.ts +45 -29
  59. package/core/errors.d.ts.map +1 -1
  60. package/core/errors.js +97 -50
  61. package/core/geoparquet-sql.d.ts +30 -2
  62. package/core/geoparquet-sql.d.ts.map +1 -1
  63. package/core/geoparquet-sql.js +38 -3
  64. package/core/odata-key-path.d.ts +12 -0
  65. package/core/odata-key-path.d.ts.map +1 -0
  66. package/core/odata-key-path.js +28 -0
  67. package/core/odata-write-codec.d.ts +66 -0
  68. package/core/odata-write-codec.d.ts.map +1 -0
  69. package/core/odata-write-codec.js +1083 -0
  70. package/core/odata.d.ts +59 -7
  71. package/core/odata.d.ts.map +1 -1
  72. package/core/odata.js +431 -99
  73. package/core/ogc-features.d.ts.map +1 -1
  74. package/core/ogc-features.js +3 -0
  75. package/core/ogc-maps.d.ts +7 -0
  76. package/core/ogc-maps.d.ts.map +1 -1
  77. package/core/ogc-maps.js +46 -7
  78. package/core/ogc-processes.d.ts +2 -1
  79. package/core/ogc-processes.d.ts.map +1 -1
  80. package/core/ogc-processes.js +33 -5
  81. package/core/ogc-records.d.ts +9 -0
  82. package/core/ogc-records.d.ts.map +1 -1
  83. package/core/ogc-records.js +78 -29
  84. package/core/ogc-tiles.d.ts +10 -1
  85. package/core/ogc-tiles.d.ts.map +1 -1
  86. package/core/ogc-tiles.js +56 -17
  87. package/core/path-utils.d.ts +9 -0
  88. package/core/path-utils.d.ts.map +1 -1
  89. package/core/path-utils.js +19 -0
  90. package/core/protocol-transport.d.ts +1 -0
  91. package/core/protocol-transport.d.ts.map +1 -1
  92. package/core/request-pipeline.d.ts.map +1 -1
  93. package/core/request-pipeline.js +1 -1
  94. package/core/spatial-filter.d.ts +8 -4
  95. package/core/spatial-filter.d.ts.map +1 -1
  96. package/core/spatial-filter.js +206 -16
  97. package/core/stac-static.d.ts +6 -2
  98. package/core/stac-static.d.ts.map +1 -1
  99. package/core/stac-static.js +260 -69
  100. package/core/surfaces.d.ts.map +1 -1
  101. package/core/surfaces.js +69 -5
  102. package/core/types.d.ts +125 -7
  103. package/core/types.d.ts.map +1 -1
  104. package/core/wfs-capabilities.d.ts +2 -0
  105. package/core/wfs-capabilities.d.ts.map +1 -1
  106. package/core/wfs-capabilities.js +8 -1
  107. package/core/wfs.d.ts.map +1 -1
  108. package/core/wfs.js +10 -0
  109. package/core/wms-capabilities.d.ts +56 -84
  110. package/core/wms-capabilities.d.ts.map +1 -1
  111. package/core/wms-capabilities.js +212 -509
  112. package/core/wmts-capabilities.d.ts +62 -62
  113. package/core/wmts-capabilities.d.ts.map +1 -1
  114. package/core/wmts-capabilities.js +224 -371
  115. package/esri-compat/editor.d.ts +8 -0
  116. package/esri-compat/editor.d.ts.map +1 -1
  117. package/esri-compat/editor.js +13 -0
  118. package/esri-compat/feature-layer.d.ts +2 -0
  119. package/esri-compat/feature-layer.d.ts.map +1 -1
  120. package/esri-compat/feature-layer.js +1 -0
  121. package/esri-compat/layer-list.d.ts +11 -0
  122. package/esri-compat/layer-list.d.ts.map +1 -1
  123. package/esri-compat/layer-list.js +49 -0
  124. package/esri-compat/legend.d.ts +10 -0
  125. package/esri-compat/legend.d.ts.map +1 -1
  126. package/esri-compat/legend.js +27 -0
  127. package/esri-compat/portal.d.ts +197 -0
  128. package/esri-compat/portal.d.ts.map +1 -0
  129. package/esri-compat/portal.js +251 -0
  130. package/esri-compat/renderer-objects.d.ts +40 -0
  131. package/esri-compat/renderer-objects.d.ts.map +1 -0
  132. package/esri-compat/renderer-objects.js +62 -0
  133. package/esri-compat/sketch.d.ts +43 -0
  134. package/esri-compat/sketch.d.ts.map +1 -1
  135. package/esri-compat/sketch.js +46 -0
  136. package/esri-compat/snapping.d.ts +43 -0
  137. package/esri-compat/snapping.d.ts.map +1 -0
  138. package/esri-compat/snapping.js +45 -0
  139. package/esri-compat/url.d.ts.map +1 -1
  140. package/esri-compat/url.js +1 -1
  141. package/esri-compat/widget-host.d.ts +84 -0
  142. package/esri-compat/widget-host.d.ts.map +1 -0
  143. package/esri-compat/widget-host.js +159 -0
  144. package/filter-registry/index.d.ts +145 -0
  145. package/filter-registry/index.d.ts.map +1 -0
  146. package/filter-registry/index.js +531 -0
  147. package/generated-app/errors.d.ts +18 -2
  148. package/generated-app/errors.d.ts.map +1 -1
  149. package/generated-app/errors.js +44 -11
  150. package/map/automatic-mount-integration.d.ts +134 -0
  151. package/map/automatic-mount-integration.d.ts.map +1 -0
  152. package/map/automatic-mount-integration.js +305 -0
  153. package/map/automatic-source-strategy.d.ts +125 -0
  154. package/map/automatic-source-strategy.d.ts.map +1 -0
  155. package/map/automatic-source-strategy.js +494 -0
  156. package/map/data-to-map-bridge.d.ts +293 -0
  157. package/map/data-to-map-bridge.d.ts.map +1 -0
  158. package/map/data-to-map-bridge.js +817 -0
  159. package/map/geojson-projection.d.ts +33 -0
  160. package/map/geojson-projection.d.ts.map +1 -0
  161. package/map/geojson-projection.js +118 -0
  162. package/map/index.d.ts +14 -0
  163. package/map/index.d.ts.map +1 -1
  164. package/map/index.js +8 -0
  165. package/map/raster-source-spec.d.ts +30 -0
  166. package/map/raster-source-spec.d.ts.map +1 -0
  167. package/map/raster-source-spec.js +190 -0
  168. package/map/raster-source-strategy.d.ts +65 -0
  169. package/map/raster-source-strategy.d.ts.map +1 -0
  170. package/map/raster-source-strategy.js +232 -0
  171. package/map/source-to-maplibre.d.ts +91 -0
  172. package/map/source-to-maplibre.d.ts.map +1 -0
  173. package/map/source-to-maplibre.js +454 -0
  174. package/map/temporal-playback.d.ts +139 -0
  175. package/map/temporal-playback.d.ts.map +1 -0
  176. package/map/temporal-playback.js +247 -0
  177. package/package.json +14 -1
  178. package/query-planner/canonical.d.ts +7 -0
  179. package/query-planner/canonical.d.ts.map +1 -0
  180. package/query-planner/canonical.js +145 -0
  181. package/query-planner/cql2-json.d.ts +17 -0
  182. package/query-planner/cql2-json.d.ts.map +1 -0
  183. package/query-planner/cql2-json.js +646 -0
  184. package/query-planner/diagnostics.d.ts +35 -0
  185. package/query-planner/diagnostics.d.ts.map +1 -0
  186. package/query-planner/diagnostics.js +373 -0
  187. package/query-planner/duckdb.d.ts +79 -0
  188. package/query-planner/duckdb.d.ts.map +1 -0
  189. package/query-planner/duckdb.js +758 -0
  190. package/query-planner/execution-context.d.ts +5 -0
  191. package/query-planner/execution-context.d.ts.map +1 -0
  192. package/query-planner/execution-context.js +50 -0
  193. package/query-planner/executor.d.ts +5 -0
  194. package/query-planner/executor.d.ts.map +1 -0
  195. package/query-planner/executor.js +223 -0
  196. package/query-planner/geoservices.d.ts +91 -0
  197. package/query-planner/geoservices.d.ts.map +1 -0
  198. package/query-planner/geoservices.js +667 -0
  199. package/query-planner/grpc.d.ts +100 -0
  200. package/query-planner/grpc.d.ts.map +1 -0
  201. package/query-planner/grpc.js +736 -0
  202. package/query-planner/index.d.ts +44 -0
  203. package/query-planner/index.d.ts.map +1 -0
  204. package/query-planner/index.js +30 -0
  205. package/query-planner/ir.d.ts +20 -0
  206. package/query-planner/ir.d.ts.map +1 -0
  207. package/query-planner/ir.js +417 -0
  208. package/query-planner/local-aggregate.d.ts +5 -0
  209. package/query-planner/local-aggregate.d.ts.map +1 -0
  210. package/query-planner/local-aggregate.js +126 -0
  211. package/query-planner/odata.d.ts +48 -0
  212. package/query-planner/odata.d.ts.map +1 -0
  213. package/query-planner/odata.js +662 -0
  214. package/query-planner/ogc-compiler.d.ts +55 -0
  215. package/query-planner/ogc-compiler.d.ts.map +1 -0
  216. package/query-planner/ogc-compiler.js +480 -0
  217. package/query-planner/ogc-features.d.ts +53 -0
  218. package/query-planner/ogc-features.d.ts.map +1 -0
  219. package/query-planner/ogc-features.js +617 -0
  220. package/query-planner/planner.d.ts +17 -0
  221. package/query-planner/planner.d.ts.map +1 -0
  222. package/query-planner/planner.js +818 -0
  223. package/query-planner/resource.d.ts +94 -0
  224. package/query-planner/resource.d.ts.map +1 -0
  225. package/query-planner/resource.js +526 -0
  226. package/query-planner/semantic-canonical.d.ts +15 -0
  227. package/query-planner/semantic-canonical.d.ts.map +1 -0
  228. package/query-planner/semantic-canonical.js +62 -0
  229. package/query-planner/semantic-compat.d.ts +10 -0
  230. package/query-planner/semantic-compat.d.ts.map +1 -0
  231. package/query-planner/semantic-compat.js +35 -0
  232. package/query-planner/semantic-compiler.d.ts +64 -0
  233. package/query-planner/semantic-compiler.d.ts.map +1 -0
  234. package/query-planner/semantic-compiler.js +81 -0
  235. package/query-planner/semantic-literals.d.ts +30 -0
  236. package/query-planner/semantic-literals.d.ts.map +1 -0
  237. package/query-planner/semantic-literals.js +133 -0
  238. package/query-planner/semantic-types.d.ts +265 -0
  239. package/query-planner/semantic-types.d.ts.map +1 -0
  240. package/query-planner/semantic-types.js +1 -0
  241. package/query-planner/semantic.d.ts +54 -0
  242. package/query-planner/semantic.d.ts.map +1 -0
  243. package/query-planner/semantic.js +1421 -0
  244. package/query-planner/types.d.ts +560 -0
  245. package/query-planner/types.d.ts.map +1 -0
  246. package/query-planner/types.js +49 -0
  247. package/query-planner/wfs-semantic.d.ts +58 -0
  248. package/query-planner/wfs-semantic.d.ts.map +1 -0
  249. package/query-planner/wfs-semantic.js +547 -0
  250. package/query-planner/wfs.d.ts +6 -0
  251. package/query-planner/wfs.d.ts.map +1 -0
  252. package/query-planner/wfs.js +71 -0
  253. package/react/external-map.d.ts +67 -0
  254. package/react/external-map.d.ts.map +1 -0
  255. package/react/external-map.js +63 -0
  256. package/react/honua-map.d.ts.map +1 -1
  257. package/react/honua-map.js +3 -2
  258. package/react/honua-source-layer.d.ts +110 -0
  259. package/react/honua-source-layer.d.ts.map +1 -0
  260. package/react/honua-source-layer.js +101 -0
  261. package/react/hooks.d.ts +28 -0
  262. package/react/hooks.d.ts.map +1 -1
  263. package/react/hooks.js +26 -4
  264. package/react/index.d.ts +8 -0
  265. package/react/index.d.ts.map +1 -1
  266. package/react/index.js +5 -0
  267. package/react/query-cache.d.ts +26 -4
  268. package/react/query-cache.d.ts.map +1 -1
  269. package/react/query-cache.js +51 -8
  270. package/react/selection.d.ts +177 -0
  271. package/react/selection.d.ts.map +1 -0
  272. package/react/selection.js +0 -0
  273. package/react/use-mounted-source.d.ts +69 -0
  274. package/react/use-mounted-source.d.ts.map +1 -0
  275. package/react/use-mounted-source.js +346 -0
  276. package/runtime/edit-snapping.d.ts +107 -0
  277. package/runtime/edit-snapping.d.ts.map +1 -0
  278. package/runtime/edit-snapping.js +190 -0
  279. package/runtime/errors.d.ts +3 -2
  280. package/runtime/errors.d.ts.map +1 -1
  281. package/runtime/errors.js +19 -2
  282. package/runtime/index.d.ts +6 -0
  283. package/runtime/index.d.ts.map +1 -1
  284. package/runtime/index.js +3 -0
  285. package/runtime/maplibre-renderer.d.ts +41 -0
  286. package/runtime/maplibre-renderer.d.ts.map +1 -0
  287. package/runtime/maplibre-renderer.js +322 -0
  288. package/runtime/query-tiles.d.ts +2 -1
  289. package/runtime/query-tiles.d.ts.map +1 -1
  290. package/runtime/query-tiles.js +6 -2
  291. package/runtime/source-bridge.d.ts +4 -4
  292. package/runtime/source-bridge.d.ts.map +1 -1
  293. package/runtime/source-bridge.js +9 -56
  294. package/runtime/style-interactions.d.ts +3 -2
  295. package/runtime/style-interactions.d.ts.map +1 -1
  296. package/runtime/style-interactions.js +11 -3
  297. package/runtime/terra-draw-sketch.d.ts +275 -0
  298. package/runtime/terra-draw-sketch.d.ts.map +1 -0
  299. package/runtime/terra-draw-sketch.js +404 -0
  300. package/source-capability-registry.d.ts +1 -0
  301. package/source-capability-registry.js +1 -0
  302. package/source-capability-types.d.ts +186 -0
  303. package/source-capability-types.js +7 -0
  304. package/style/index.d.ts +2 -0
  305. package/style/index.d.ts.map +1 -1
  306. package/style/index.js +2 -0
  307. package/style/renderers.d.ts +326 -0
  308. package/style/renderers.d.ts.map +1 -0
  309. package/style/renderers.js +517 -0
  310. package/webmap/convert-renderer.d.ts +24 -3
  311. package/webmap/convert-renderer.d.ts.map +1 -1
  312. package/webmap/convert-renderer.js +93 -112
  313. package/webmap/index.d.ts +1 -1
  314. package/webmap/index.d.ts.map +1 -1
  315. package/webmap/index.js +1 -1
@@ -0,0 +1,494 @@
1
+ /**
2
+ * Explainable, dependency-free automatic Source -> MapLibre strategy selection.
3
+ *
4
+ * Planning is pure and renderer-neutral. Mounting consumes only the minimal
5
+ * injected MapLibre host surface and delegates to the focused feature/raster
6
+ * adapters where possible.
7
+ */
8
+ import { HonuaSdkError, mergeHonuaErrorContext } from "../core/error-envelope.js";
9
+ import { canonicalStringify, toJsonValue } from "../query-planner/canonical.js";
10
+ import { queryIrSourceIdentity } from "../query-planner/ir.js";
11
+ import { hashQueryPlanV1 } from "../query-planner/planner.js";
12
+ import { isSafeMapLibreRasterEndpoint } from "./raster-source-spec.js";
13
+ import { HonuaMapLibreRasterStrategyError, mountRasterSourceToMapLibre, projectRasterSourceToMapLibre, } from "./raster-source-strategy.js";
14
+ import { mountSourceToMapLibre, } from "./source-to-maplibre.js";
15
+ export const AUTOMATIC_MAPLIBRE_PLAN_KIND = "honua.maplibre-source-plan";
16
+ export const AUTOMATIC_MAPLIBRE_PLAN_VERSION = "1.0";
17
+ export const MAX_AUTOMATIC_GEOJSON_FEATURES = 100_000;
18
+ export class HonuaAutomaticMapLibreStrategyError extends HonuaSdkError {
19
+ code;
20
+ detail;
21
+ constructor(code, message, detail, options = {}) {
22
+ super(AUTOMATIC_MAPLIBRE_ERROR_CODES[code], message, {
23
+ ...options,
24
+ context: mergeHonuaErrorContext(detail, options.context),
25
+ });
26
+ this.code = code;
27
+ this.detail = detail;
28
+ this.name = "HonuaAutomaticMapLibreStrategyError";
29
+ }
30
+ }
31
+ const AUTOMATIC_MAPLIBRE_ERROR_CODES = {
32
+ "no-eligible-strategy": "map.automatic-strategy.no-eligible-strategy",
33
+ "stale-plan": "map.automatic-strategy.stale-plan",
34
+ "source-conflict": "map.automatic-strategy.source-conflict",
35
+ "layer-conflict": "map.automatic-strategy.layer-conflict",
36
+ "map-mutation-failed": "map.automatic-strategy.map-mutation-failed",
37
+ cancelled: "map.automatic-strategy.cancelled",
38
+ disposed: "map.automatic-strategy.disposed",
39
+ };
40
+ const STRATEGIES = [
41
+ "dynamic-query-tiles",
42
+ "pmtiles-vector",
43
+ "pmtiles-raster",
44
+ "vector-tiles",
45
+ "native-raster-tiles",
46
+ "wms-raster",
47
+ "wmts-raster",
48
+ "geojson-query",
49
+ ];
50
+ /** Explain every strategy and select the first exact eligible candidate. */
51
+ export function explainAutomaticSourceToMapLibre(source, options = {}) {
52
+ const descriptor = source.descriptor;
53
+ const stale = isStale(options.evidence);
54
+ const candidates = STRATEGIES.map((strategy) => evaluateCandidate(strategy, source, options, stale));
55
+ const selected = options.override
56
+ ? candidates.find((candidate) => candidate.strategy === options.override && candidate.eligible)
57
+ : candidates.find((candidate) => candidate.eligible);
58
+ const diagnostics = candidates.map((candidate) => Object.freeze({
59
+ code: candidate.eligible && candidate !== selected ? "eligible-not-selected" : candidate.reason,
60
+ severity: candidate.eligible && candidate === selected ? "info" : candidate.eligible ? "warning" : "error",
61
+ strategy: candidate.strategy,
62
+ message: candidate.eligible && candidate !== selected
63
+ ? `${candidate.message} A higher-priority exact strategy was selected.`
64
+ : candidate.message,
65
+ }));
66
+ if (options.override && !selected) {
67
+ diagnostics.push(Object.freeze({
68
+ code: "override-mismatch",
69
+ severity: "error",
70
+ strategy: options.override,
71
+ message: `The explicit ${options.override} override is not eligible; no fallback was selected.`,
72
+ }));
73
+ }
74
+ const sourceId = options.sourceId ?? `honua-${safeId(descriptor.id)}`;
75
+ const native = selected ? nativeProjection(selected.strategy, source, options, sourceId) : undefined;
76
+ const queryPlan = options.queryPlan;
77
+ const observedAt = options.evidence?.observedAt;
78
+ return Object.freeze({
79
+ kind: AUTOMATIC_MAPLIBRE_PLAN_KIND,
80
+ version: AUTOMATIC_MAPLIBRE_PLAN_VERSION,
81
+ id: `${AUTOMATIC_MAPLIBRE_PLAN_VERSION}:${safeId(descriptor.id)}:${selected?.strategy ?? "unsupported"}`,
82
+ sourceId,
83
+ protocol: descriptor.protocol,
84
+ ...(selected ? { selected } : {}),
85
+ candidates: Object.freeze(candidates),
86
+ diagnostics: Object.freeze(diagnostics),
87
+ ...(options.queryTileSource?.bounds ? { bounds: options.queryTileSource.bounds } : {}),
88
+ freshness: Object.freeze({
89
+ mode: descriptor.analytics?.freshness?.mode ?? (descriptor.protocol === "pmtiles" ? "snapshot" : "source-owned"),
90
+ ...(observedAt ? { observedAt } : {}),
91
+ ...(options.evidence ? { maxAgeMs: options.evidence.maxAgeMs } : {}),
92
+ stale,
93
+ }),
94
+ provenance: Object.freeze({
95
+ endpoint: credentialFreeEndpoint(descriptor.locator.url),
96
+ sourceVersion: queryPlan?.ir.source.sourceVersion,
97
+ schemaVersion: queryPlan?.ir.source.schemaVersion,
98
+ authorizationScope: Object.freeze([...(queryPlan?.ir.source.authorizationScope ?? [])]),
99
+ queryPlanId: queryPlan?.id,
100
+ queryPlanFingerprint: queryPlan?.fingerprint,
101
+ }),
102
+ cache: selected?.strategy === "geojson-query"
103
+ ? "query-plan-bypass"
104
+ : selected?.strategy.startsWith("pmtiles")
105
+ ? "immutable-archive"
106
+ : "source-owned",
107
+ ...(native ? { source: native.source, layers: native.layers } : { layers: Object.freeze([]) }),
108
+ });
109
+ }
110
+ /** Mount an explained plan through one cross-strategy lifecycle. */
111
+ export async function mountAutomaticSourceToMapLibre(map, source, plan, options = {}) {
112
+ assertPlanContext(source, plan, options);
113
+ if (options.signal?.aborted) {
114
+ throw new HonuaAutomaticMapLibreStrategyError("cancelled", "Automatic MapLibre mounting was cancelled before mutation.", { sourceId: plan.sourceId });
115
+ }
116
+ const selected = plan.selected;
117
+ if (!selected) {
118
+ throw new HonuaAutomaticMapLibreStrategyError("no-eligible-strategy", "The strategy plan has no eligible selection.", {
119
+ sourceId: plan.sourceId,
120
+ diagnostics: plan.diagnostics,
121
+ });
122
+ }
123
+ if (selected.strategy === "geojson-query") {
124
+ if (!options.queryPlan) {
125
+ throw new HonuaAutomaticMapLibreStrategyError("no-eligible-strategy", "GeoJSON mounting requires its accepted query plan.");
126
+ }
127
+ const mounted = await mountSourceToMapLibre(map, source, options.queryPlan, options);
128
+ let cancelled = false;
129
+ return {
130
+ strategy: selected.strategy,
131
+ sourceId: mounted.sourceId,
132
+ layerIds: mounted.layerIds,
133
+ get state() {
134
+ return cancelled ? "cancelled" : mounted.state;
135
+ },
136
+ get diagnostics() {
137
+ return plan.diagnostics;
138
+ },
139
+ ready: Promise.resolve(plan),
140
+ async refresh(refreshOptions = {}) {
141
+ await mounted.refresh(refreshOptions);
142
+ return plan;
143
+ },
144
+ cancel() {
145
+ if (mounted.state === "disposed")
146
+ return;
147
+ cancelled = true;
148
+ mounted.dispose();
149
+ },
150
+ dispose() {
151
+ mounted.dispose();
152
+ },
153
+ };
154
+ }
155
+ if (isRasterStrategy(selected.strategy)) {
156
+ const mounted = mountRasterSourceToMapLibre(map, source.descriptor, options);
157
+ return staticLifecycle(plan, mounted.sourceId, [mounted.layerId], () => mounted.dispose());
158
+ }
159
+ return mountNative(map, plan, options.beforeId);
160
+ }
161
+ function evaluateCandidate(strategy, source, options, stale) {
162
+ const dataPath = strategy === "geojson-query" ? "materialized" : "native";
163
+ const peers = strategy.startsWith("pmtiles") ? ["maplibre-gl", "pmtiles"] : ["maplibre-gl"];
164
+ const reject = (reason, message) => Object.freeze({
165
+ strategy,
166
+ eligible: false,
167
+ reason,
168
+ fidelity: "unsupported",
169
+ dataPath,
170
+ requiredPeers: peers,
171
+ message,
172
+ });
173
+ if (stale)
174
+ return reject("stale-evidence", "Caller-supplied strategy evidence is stale.");
175
+ if (!sameStrings(source.capabilities, source.descriptor.capabilities)) {
176
+ return reject("capability-mismatch", "Runtime and descriptor capability evidence do not match.");
177
+ }
178
+ if (!supportedCrs(source.descriptor.locator.srsName)) {
179
+ return reject("unsupported-crs", `CRS ${String(source.descriptor.locator.srsName)} is not WebMercator/WGS84 compatible.`);
180
+ }
181
+ if (strategy === "geojson-query") {
182
+ if (!source.capabilities.has("query"))
183
+ return reject("capability-mismatch", "The source does not advertise query.");
184
+ const plan = options.queryPlan;
185
+ if (!plan)
186
+ return reject("missing-query-plan", "No accepted feature query plan was supplied.");
187
+ if (!queryPlanMatchesSource(source, plan)) {
188
+ return reject("plan-context-mismatch", "The accepted query plan does not match current source identity or capabilities.");
189
+ }
190
+ const step = plan.steps[0];
191
+ if (plan.steps.length !== 1 ||
192
+ plan.ir.query.aggregation !== undefined ||
193
+ plan.ir.query.returnGeometry === false ||
194
+ step?.engine !== "remote" ||
195
+ step.operation !== "query") {
196
+ return reject("unsupported-query-plan", "The accepted plan is not one remote geometry-bearing feature query.");
197
+ }
198
+ const limit = plan.ir.query.pagination?.limit;
199
+ if (!Number.isSafeInteger(limit) || (limit ?? 0) < 1 || (limit ?? 0) > MAX_AUTOMATIC_GEOJSON_FEATURES) {
200
+ return reject("unbounded-materialization", `GeoJSON materialization requires a 1-${MAX_AUTOMATIC_GEOJSON_FEATURES} feature limit.`);
201
+ }
202
+ }
203
+ else if (strategy === "dynamic-query-tiles") {
204
+ if (!options.queryTileSource)
205
+ return reject("missing-metadata", "No accepted dynamic query-tile source spec was supplied.");
206
+ if (!source.capabilities.has("tiles"))
207
+ return reject("capability-mismatch", "The source does not advertise tiles.");
208
+ if (!safeNativeSpec(options.queryTileSource))
209
+ return reject("unsafe-url", "The query-tile source contains an unsafe URL.");
210
+ if (!nonEmpty(options.sourceLayer))
211
+ return reject("missing-metadata", "Vector rendering requires sourceLayer metadata.");
212
+ }
213
+ else if (strategy === "vector-tiles") {
214
+ if (source.descriptor.protocol !== "maplibre-vector" && source.descriptor.protocol !== "ogc-tiles") {
215
+ return reject("protocol-mismatch", "The protocol is not a native vector-tile protocol.");
216
+ }
217
+ if (!source.capabilities.has("tiles"))
218
+ return reject("capability-mismatch", "The source does not advertise tiles.");
219
+ if (!safeHttpUrl(source.descriptor.locator.url))
220
+ return reject("unsafe-url", "The vector tile URL is unsafe.");
221
+ if (!nonEmpty(options.sourceLayer))
222
+ return reject("missing-metadata", "Vector rendering requires sourceLayer metadata.");
223
+ }
224
+ else if (strategy === "pmtiles-vector" || strategy === "pmtiles-raster") {
225
+ if (source.descriptor.protocol !== "pmtiles")
226
+ return reject("protocol-mismatch", "The protocol is not PMTiles.");
227
+ const expected = strategy === "pmtiles-vector" ? "vector" : "raster";
228
+ if (options.pmtilesType !== expected)
229
+ return reject("missing-metadata", `Accepted PMTiles ${expected} payload evidence is required.`);
230
+ if (!source.capabilities.has("tiles"))
231
+ return reject("capability-mismatch", "The source does not advertise tiles.");
232
+ if (!safePmtilesUrl(source.descriptor.locator.url))
233
+ return reject("unsafe-url", "The PMTiles archive URL is unsafe.");
234
+ if (expected === "vector" && !nonEmpty(options.sourceLayer))
235
+ return reject("missing-metadata", "Vector PMTiles requires sourceLayer metadata.");
236
+ }
237
+ else {
238
+ const protocol = strategy === "native-raster-tiles" ? "maplibre-raster" : strategy === "wms-raster" ? "wms" : "wmts";
239
+ if (source.descriptor.protocol !== protocol)
240
+ return reject("protocol-mismatch", `The protocol is not ${protocol}.`);
241
+ if (!source.capabilities.has("tiles") || !source.capabilities.has("render")) {
242
+ return reject("capability-mismatch", "Raster selection requires tiles and render capabilities.");
243
+ }
244
+ if (!safeHttpUrl(source.descriptor.locator.url))
245
+ return reject("unsafe-url", "The raster endpoint URL is unsafe.");
246
+ try {
247
+ projectRasterSourceToMapLibre(source.descriptor, options);
248
+ }
249
+ catch (error) {
250
+ if (error instanceof HonuaMapLibreRasterStrategyError && error.code === "invalid-option") {
251
+ return reject("invalid-option", error.message);
252
+ }
253
+ return reject("missing-metadata", `Metadata required by ${strategy} is missing or invalid.`);
254
+ }
255
+ }
256
+ return Object.freeze({
257
+ strategy,
258
+ eligible: true,
259
+ reason: "selected",
260
+ fidelity: "exact",
261
+ dataPath,
262
+ requiredPeers: peers,
263
+ message: `Exact ${strategy} strategy is eligible.`,
264
+ });
265
+ }
266
+ function nativeProjection(strategy, source, options, sourceId) {
267
+ if (strategy === "geojson-query")
268
+ return undefined;
269
+ if (isRasterStrategy(strategy)) {
270
+ const projection = projectRasterSourceToMapLibre(source.descriptor, options);
271
+ return {
272
+ source: Object.freeze({ ...projection.source }),
273
+ layers: Object.freeze([projection.layer]),
274
+ };
275
+ }
276
+ const vector = strategy !== "pmtiles-raster";
277
+ let spec;
278
+ if (strategy === "dynamic-query-tiles") {
279
+ spec = Object.freeze({ ...options.queryTileSource });
280
+ }
281
+ else if (strategy.startsWith("pmtiles")) {
282
+ spec = Object.freeze({
283
+ type: vector ? "vector" : "raster",
284
+ url: toPmtilesUrl(source.descriptor.locator.url),
285
+ ...(source.descriptor.attribution ? { attribution: source.descriptor.attribution } : {}),
286
+ });
287
+ }
288
+ else {
289
+ spec = Object.freeze({
290
+ type: "vector",
291
+ tiles: Object.freeze([source.descriptor.locator.url]),
292
+ ...(source.descriptor.attribution ? { attribution: source.descriptor.attribution } : {}),
293
+ });
294
+ }
295
+ const layerId = options.layerId ?? `${sourceId}-${vector ? "features" : "raster"}`;
296
+ const layer = Object.freeze({
297
+ id: layerId,
298
+ type: vector ? "fill" : "raster",
299
+ source: sourceId,
300
+ ...(vector ? { "source-layer": options.sourceLayer } : {}),
301
+ ...(options.minzoom !== undefined ? { minzoom: options.minzoom } : {}),
302
+ ...(options.maxzoom !== undefined ? { maxzoom: options.maxzoom } : {}),
303
+ ...(options.paint ? { paint: { ...options.paint } } : {}),
304
+ ...(options.layout ? { layout: { ...options.layout } } : {}),
305
+ metadata: { "honua:strategy": strategy },
306
+ });
307
+ return { source: spec, layers: Object.freeze([layer]) };
308
+ }
309
+ function mountNative(map, plan, beforeId) {
310
+ if (!plan.source)
311
+ throw new HonuaAutomaticMapLibreStrategyError("no-eligible-strategy", "Native source projection is missing.");
312
+ if (map.getSource(plan.sourceId) !== undefined)
313
+ throw new HonuaAutomaticMapLibreStrategyError("source-conflict", `Source ${plan.sourceId} already exists.`);
314
+ const layerIds = plan.layers.map((layer) => String(layer.id));
315
+ for (const layerId of layerIds) {
316
+ if (map.getLayer(layerId) !== undefined)
317
+ throw new HonuaAutomaticMapLibreStrategyError("layer-conflict", `Layer ${layerId} already exists.`);
318
+ }
319
+ const attempted = [];
320
+ try {
321
+ map.addSource(plan.sourceId, plan.source);
322
+ for (const layer of plan.layers) {
323
+ attempted.push(String(layer.id));
324
+ map.addLayer(layer, beforeId);
325
+ }
326
+ }
327
+ catch (cause) {
328
+ for (const layerId of [...attempted].reverse()) {
329
+ try {
330
+ if (map.getLayer(layerId) !== undefined)
331
+ map.removeLayer(layerId);
332
+ }
333
+ catch {
334
+ /* best effort rollback */
335
+ }
336
+ }
337
+ try {
338
+ if (map.getSource(plan.sourceId) !== undefined)
339
+ map.removeSource(plan.sourceId);
340
+ }
341
+ catch {
342
+ /* best effort rollback */
343
+ }
344
+ throw new HonuaAutomaticMapLibreStrategyError("map-mutation-failed", `Failed to mount ${plan.sourceId} transactionally.`, { sourceId: plan.sourceId }, { cause });
345
+ }
346
+ return staticLifecycle(plan, plan.sourceId, layerIds, () => {
347
+ const failures = [];
348
+ for (const layerId of [...layerIds].reverse()) {
349
+ try {
350
+ if (map.getLayer(layerId) !== undefined)
351
+ map.removeLayer(layerId);
352
+ }
353
+ catch (error) {
354
+ failures.push(error);
355
+ }
356
+ }
357
+ try {
358
+ if (map.getSource(plan.sourceId) !== undefined)
359
+ map.removeSource(plan.sourceId);
360
+ }
361
+ catch (error) {
362
+ failures.push(error);
363
+ }
364
+ if (failures.length === 1)
365
+ throw failures[0];
366
+ if (failures.length > 1)
367
+ throw new AggregateError(failures, "Automatic MapLibre native cleanup failed");
368
+ });
369
+ }
370
+ function staticLifecycle(plan, sourceId, layerIds, cleanup) {
371
+ let state = "ready";
372
+ const dispose = () => {
373
+ if (state === "disposed" || state === "cancelled")
374
+ return;
375
+ state = "disposed";
376
+ cleanup();
377
+ };
378
+ return {
379
+ strategy: plan.selected?.strategy ?? "geojson-query",
380
+ sourceId,
381
+ layerIds,
382
+ get state() {
383
+ return state;
384
+ },
385
+ diagnostics: plan.diagnostics,
386
+ ready: Promise.resolve(plan),
387
+ async refresh() {
388
+ if (state === "disposed" || state === "cancelled")
389
+ throw new HonuaAutomaticMapLibreStrategyError("disposed", "Cannot refresh a disposed automatic mount.");
390
+ return plan;
391
+ },
392
+ cancel() {
393
+ if (state === "disposed" || state === "cancelled")
394
+ return;
395
+ state = "cancelled";
396
+ cleanup();
397
+ },
398
+ dispose,
399
+ };
400
+ }
401
+ function assertPlanContext(source, plan, options) {
402
+ const current = explainAutomaticSourceToMapLibre(source, options);
403
+ if (plan.kind !== AUTOMATIC_MAPLIBRE_PLAN_KIND ||
404
+ plan.version !== AUTOMATIC_MAPLIBRE_PLAN_VERSION ||
405
+ plan.id !== current.id ||
406
+ plan.sourceId !== current.sourceId ||
407
+ plan.protocol !== source.descriptor.protocol ||
408
+ plan.selected?.strategy !== current.selected?.strategy ||
409
+ plan.provenance.endpoint !== current.provenance.endpoint ||
410
+ plan.provenance.queryPlanFingerprint !== current.provenance.queryPlanFingerprint ||
411
+ canonicalStringify(toJsonValue(plan.source ?? null)) !== canonicalStringify(toJsonValue(current.source ?? null)) ||
412
+ canonicalStringify(toJsonValue(plan.layers)) !== canonicalStringify(toJsonValue(current.layers))) {
413
+ throw new HonuaAutomaticMapLibreStrategyError("stale-plan", "The automatic strategy plan no longer matches its source and policy context.");
414
+ }
415
+ }
416
+ function isRasterStrategy(strategy) {
417
+ return strategy === "native-raster-tiles" || strategy === "wms-raster" || strategy === "wmts-raster";
418
+ }
419
+ function queryPlanMatchesSource(source, plan) {
420
+ try {
421
+ if (hashQueryPlanV1(plan) !== plan.fingerprint)
422
+ return false;
423
+ const identity = queryIrSourceIdentity(source.descriptor, {
424
+ schemaVersion: plan.ir.source.schemaVersion,
425
+ sourceVersion: plan.ir.source.sourceVersion,
426
+ authorizationScope: plan.ir.source.authorizationScope,
427
+ });
428
+ return (canonicalStringify(toJsonValue(identity)) === canonicalStringify(toJsonValue(plan.ir.source)) &&
429
+ sameStrings(source.capabilities, plan.ir.source.capabilities));
430
+ }
431
+ catch {
432
+ return false;
433
+ }
434
+ }
435
+ function sameStrings(left, right) {
436
+ const a = [...left].sort();
437
+ const b = [...right].sort();
438
+ return a.length === b.length && a.every((value, index) => value === b[index]);
439
+ }
440
+ function isStale(evidence) {
441
+ if (!evidence || !Number.isFinite(evidence.maxAgeMs) || evidence.maxAgeMs < 0)
442
+ return evidence !== undefined;
443
+ const observed = Date.parse(evidence.observedAt);
444
+ const now = Date.parse(evidence.now);
445
+ return !Number.isFinite(observed) || !Number.isFinite(now) || now - observed > evidence.maxAgeMs;
446
+ }
447
+ function supportedCrs(crs) {
448
+ if (crs === undefined)
449
+ return true;
450
+ const normalized = String(crs).toUpperCase();
451
+ return (normalized === "4326" ||
452
+ normalized === "3857" ||
453
+ normalized.includes("EPSG::4326") ||
454
+ normalized.includes("EPSG:4326") ||
455
+ normalized.includes("EPSG::3857") ||
456
+ normalized.includes("EPSG:3857") ||
457
+ normalized.includes("CRS84") ||
458
+ normalized === "WEBMERCATORQUAD");
459
+ }
460
+ function safeNativeSpec(spec) {
461
+ const urls = [...(spec.tiles ?? []), ...(spec.url ? [spec.url] : [])];
462
+ return urls.length > 0 && urls.every(safeHttpUrl);
463
+ }
464
+ function safePmtilesUrl(value) {
465
+ return safeHttpUrl(value.startsWith("pmtiles://") ? value.slice("pmtiles://".length) : value);
466
+ }
467
+ function safeHttpUrl(value) {
468
+ return isSafeMapLibreRasterEndpoint(value);
469
+ }
470
+ function credentialFreeEndpoint(value) {
471
+ const raw = value.startsWith("pmtiles://") ? value.slice("pmtiles://".length) : value;
472
+ try {
473
+ const url = new URL(raw);
474
+ return `${url.origin}${url.pathname}`;
475
+ }
476
+ catch {
477
+ return "invalid";
478
+ }
479
+ }
480
+ function toPmtilesUrl(value) {
481
+ return value.startsWith("pmtiles://") ? value : `pmtiles://${value}`;
482
+ }
483
+ function nonEmpty(value) {
484
+ return typeof value === "string" && value.trim().length > 0;
485
+ }
486
+ function safeId(value) {
487
+ const normalized = value
488
+ .toLowerCase()
489
+ .replace(/[^a-z0-9]+/g, "-")
490
+ .split("-")
491
+ .filter(Boolean)
492
+ .join("-");
493
+ return normalized || "source";
494
+ }