@honua/react 0.1.0-beta.0 → 0.1.2-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,71 @@
1
+ import { UNSUPPORTED_FES, compileSpatialFilter, compileWhere, serializeFes } from "../core/wfs-filter.js";
2
+ import { HonuaQueryPlanningError } from "./types.js";
3
+ export { compileSemanticWfsQuery } from "./wfs-semantic.js";
4
+ /** Compile canonical query IR to a deterministic WFS 2.0 GetFeature request. */
5
+ export function compileWfsQuery(source, query) {
6
+ if (source.protocol !== "wfs") {
7
+ throw new HonuaQueryPlanningError("unsupported-compiler", `wfs-2.0-get-feature-v1 does not compile protocol "${source.protocol}"`);
8
+ }
9
+ if (!source.typeName) {
10
+ throw new HonuaQueryPlanningError("invalid-query", `Source "${source.id}" requires locator.typeName for WFS planning`);
11
+ }
12
+ if (query.aggregation) {
13
+ throw new HonuaQueryPlanningError("unsupported-query", "WFS 2.0 has no portable remote aggregation; use degraded policy with an explicit bounded-local fallback");
14
+ }
15
+ if (query.spatialFilter && query.outSr !== undefined) {
16
+ throw new HonuaQueryPlanningError("unsupported-query", "WFS planning cannot use outSr to label input filter coordinates; stamp the input geometry through the typed WFS escape hatch");
17
+ }
18
+ // Keep this aligned with the current WFS adapter's reviewed default. A
19
+ // vendor-specific geometry property requires the typed WFS escape hatch
20
+ // until discovery carries that binding into both planner and executor.
21
+ const geometryProperty = "the_geom";
22
+ const filters = [];
23
+ if (query.where) {
24
+ const where = compileWhere(query.where.expression);
25
+ if (where === UNSUPPORTED_FES) {
26
+ throw new HonuaQueryPlanningError("unsupported-query", "The where clause is not expressible as FES 2.0");
27
+ }
28
+ if (where.kind !== "and" || where.operands.length > 0)
29
+ filters.push(where);
30
+ }
31
+ if (query.spatialFilter) {
32
+ const spatial = compileSpatialFilter({
33
+ geometry: query.spatialFilter.geometry,
34
+ geometryType: query.spatialFilter.geometryType,
35
+ ...(query.spatialFilter.spatialRel ? { spatialRel: query.spatialFilter.spatialRel } : {}),
36
+ }, {
37
+ geometryProperty,
38
+ });
39
+ if (spatial === UNSUPPORTED_FES) {
40
+ throw new HonuaQueryPlanningError("unsupported-query", "The spatial predicate is not expressible as FES 2.0");
41
+ }
42
+ filters.push(spatial);
43
+ }
44
+ let propertyName;
45
+ if (query.outFields && query.outFields.length > 0) {
46
+ const fields = [...query.outFields];
47
+ if (query.returnGeometry === false && fields.includes(geometryProperty)) {
48
+ throw new HonuaQueryPlanningError("unsupported-query", `WFS returnGeometry=false conflicts with outFields containing the geometry property "${geometryProperty}"`);
49
+ }
50
+ if (query.returnGeometry !== false && !fields.includes(geometryProperty))
51
+ fields.push(geometryProperty);
52
+ propertyName = fields;
53
+ }
54
+ else if (query.returnGeometry === false) {
55
+ throw new HonuaQueryPlanningError("unsupported-query", "WFS returnGeometry=false requires explicit outFields because propertyName cannot otherwise suppress geometry exactly");
56
+ }
57
+ return {
58
+ compiler: "wfs-2.0-get-feature-v1",
59
+ typeName: source.typeName,
60
+ ...(filters.length > 0 ? { filter: serializeFes(filters, { typeName: source.typeName }) } : {}),
61
+ ...(propertyName ? { propertyName } : {}),
62
+ ...(query.orderBy && query.orderBy.length > 0
63
+ ? {
64
+ sortBy: query.orderBy.map((sort) => `${sort.field} ${sort.direction === "desc" ? "DESC" : "ASC"}`).join(","),
65
+ }
66
+ : {}),
67
+ ...(query.pagination?.offset !== undefined ? { startIndex: query.pagination.offset } : {}),
68
+ ...(query.pagination?.limit !== undefined ? { count: query.pagination.limit } : {}),
69
+ ...(query.outSr !== undefined ? { srsName: String(query.outSr) } : {}),
70
+ };
71
+ }
@@ -0,0 +1,67 @@
1
+ /**
2
+ * External-map interop for `@honua/react` (REQ-001 of the react depth pass).
3
+ *
4
+ * Every map-attached hook/component in this package resolves its MapLibre map
5
+ * through {@link useHonuaMap}: an explicit prop/argument wins, then the nearest
6
+ * {@link HonuaMapProvider} (or enclosing `HonuaMap`, which publishes the same
7
+ * context). The map is duck-typed as the data-to-map bridge's
8
+ * {@link DataToMapLibreMap} — any real `maplibre-gl` `Map` instance satisfies
9
+ * it, including the instance exposed by `@vis.gl/react-maplibre`'s `useMap()`
10
+ * (`mapRef.getMap()`). `@vis.gl/react-maplibre` is **not** a dependency; the
11
+ * interop is purely structural.
12
+ *
13
+ * SSR-safe: no `window` / `document` access at module scope.
14
+ *
15
+ * @module
16
+ */
17
+ import { type ReactNode } from "react";
18
+ import type { DataToMapLibreMap } from "../map/data-to-map-bridge.js";
19
+ /**
20
+ * Context carrying the active, externally-visible MapLibre map instance.
21
+ * Populated by {@link HonuaMapProvider} and by `HonuaMap` (for its own map),
22
+ * so `useHonuaMap` always resolves the nearest enclosing map.
23
+ *
24
+ * @experimental
25
+ */
26
+ export declare const HonuaExternalMapContext: import("react").Context<DataToMapLibreMap | null>;
27
+ /** Props for {@link HonuaMapProvider}. @experimental */
28
+ export interface HonuaMapProviderProps {
29
+ /**
30
+ * The externally-owned MapLibre map (duck-typed). Pass `null`/`undefined`
31
+ * while the map is still being created — descendants simply wait.
32
+ */
33
+ map: DataToMapLibreMap | null | undefined;
34
+ children?: ReactNode;
35
+ }
36
+ /**
37
+ * Publish an externally-owned MapLibre map to the Honua map-attached hooks and
38
+ * components below it ({@link HonuaSourceLayer}, {@link useMountedSource},
39
+ * selection bindings). Use this when another library owns the map — e.g.
40
+ * `@vis.gl/react-maplibre`'s `<Map>` — and Honua should only mount data onto
41
+ * it. The provider never mutates or removes the map.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * function Overlay() {
46
+ * const { current } = useMap(); // @vis.gl/react-maplibre
47
+ * return (
48
+ * <HonuaMapProvider map={current?.getMap() ?? null}>
49
+ * <HonuaSourceLayer source={source} hover />
50
+ * </HonuaMapProvider>
51
+ * );
52
+ * }
53
+ * ```
54
+ *
55
+ * @experimental
56
+ */
57
+ export declare function HonuaMapProvider({ map, children }: HonuaMapProviderProps): ReactNode;
58
+ /**
59
+ * Resolve the MapLibre map a map-attached hook/component should target: the
60
+ * explicit `map` argument when given, otherwise the nearest enclosing
61
+ * {@link HonuaMapProvider} or `HonuaMap`. Returns `null` while no map is
62
+ * available (SSR, or before the map finished creating).
63
+ *
64
+ * @experimental
65
+ */
66
+ export declare function useHonuaMap(map?: DataToMapLibreMap | null): DataToMapLibreMap | null;
67
+ //# sourceMappingURL=external-map.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-map.d.ts","sourceRoot":"","sources":["../../../src/react/external-map.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,KAAK,SAAS,EAA6B,MAAM,OAAO,CAAC;AAElE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AAEtE;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,mDAAgD,CAAC;AAGrF,wDAAwD;AACxD,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,GAAG,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,CAAC;IAC1C,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,EAAE,qBAAqB,GAAG,SAAS,CAEpF;AAED;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,CAAC,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,GAAG,IAAI,CAGpF"}
@@ -0,0 +1,63 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ /**
3
+ * External-map interop for `@honua/react` (REQ-001 of the react depth pass).
4
+ *
5
+ * Every map-attached hook/component in this package resolves its MapLibre map
6
+ * through {@link useHonuaMap}: an explicit prop/argument wins, then the nearest
7
+ * {@link HonuaMapProvider} (or enclosing `HonuaMap`, which publishes the same
8
+ * context). The map is duck-typed as the data-to-map bridge's
9
+ * {@link DataToMapLibreMap} — any real `maplibre-gl` `Map` instance satisfies
10
+ * it, including the instance exposed by `@vis.gl/react-maplibre`'s `useMap()`
11
+ * (`mapRef.getMap()`). `@vis.gl/react-maplibre` is **not** a dependency; the
12
+ * interop is purely structural.
13
+ *
14
+ * SSR-safe: no `window` / `document` access at module scope.
15
+ *
16
+ * @module
17
+ */
18
+ import { createContext, useContext } from "react";
19
+ /**
20
+ * Context carrying the active, externally-visible MapLibre map instance.
21
+ * Populated by {@link HonuaMapProvider} and by `HonuaMap` (for its own map),
22
+ * so `useHonuaMap` always resolves the nearest enclosing map.
23
+ *
24
+ * @experimental
25
+ */
26
+ export const HonuaExternalMapContext = createContext(null);
27
+ HonuaExternalMapContext.displayName = "HonuaExternalMapContext";
28
+ /**
29
+ * Publish an externally-owned MapLibre map to the Honua map-attached hooks and
30
+ * components below it ({@link HonuaSourceLayer}, {@link useMountedSource},
31
+ * selection bindings). Use this when another library owns the map — e.g.
32
+ * `@vis.gl/react-maplibre`'s `<Map>` — and Honua should only mount data onto
33
+ * it. The provider never mutates or removes the map.
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * function Overlay() {
38
+ * const { current } = useMap(); // @vis.gl/react-maplibre
39
+ * return (
40
+ * <HonuaMapProvider map={current?.getMap() ?? null}>
41
+ * <HonuaSourceLayer source={source} hover />
42
+ * </HonuaMapProvider>
43
+ * );
44
+ * }
45
+ * ```
46
+ *
47
+ * @experimental
48
+ */
49
+ export function HonuaMapProvider({ map, children }) {
50
+ return _jsx(HonuaExternalMapContext.Provider, { value: map ?? null, children: children });
51
+ }
52
+ /**
53
+ * Resolve the MapLibre map a map-attached hook/component should target: the
54
+ * explicit `map` argument when given, otherwise the nearest enclosing
55
+ * {@link HonuaMapProvider} or `HonuaMap`. Returns `null` while no map is
56
+ * available (SSR, or before the map finished creating).
57
+ *
58
+ * @experimental
59
+ */
60
+ export function useHonuaMap(map) {
61
+ const contextMap = useContext(HonuaExternalMapContext);
62
+ return map ?? contextMap;
63
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"honua-map.d.ts","sourceRoot":"","sources":["../../../src/react/honua-map.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAA+B,MAAM,OAAO,CAAC;AAExF,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMhH,oFAAoF;AACpF,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,KAAK,OAAO,EAAE,kBAAkB,KAAK,WAAW,GAAG;QAAE,MAAM,IAAI,IAAI,CAAA;KAAE,CAAC;IAC3E,KAAK,EAAE,KAAK,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;CAC3D;AAID,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,OAAO,EAAE,eAAe,CAAC;IACzB;;;;OAIG;IACH,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC,CAAC;IAC5D;;;;OAIG;IACH,WAAW,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC;IACpD,wCAAwC;IACxC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC/C,uDAAuD;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAiGxD"}
1
+ {"version":3,"file":"honua-map.d.ts","sourceRoot":"","sources":["../../../src/react/honua-map.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,SAAS,EAA+B,MAAM,OAAO,CAAC;AAGxF,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAOhH,oFAAoF;AACpF,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC;IAChC,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,KAAK,OAAO,EAAE,kBAAkB,KAAK,WAAW,GAAG;QAAE,MAAM,IAAI,IAAI,CAAA;KAAE,CAAC;IAC3E,KAAK,EAAE,KAAK,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC;CAC3D;AAID,kCAAkC;AAClC,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,OAAO,EAAE,eAAe,CAAC;IACzB;;;;OAIG;IACH,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,WAAW,CAAC,CAAC,CAAC;IAC5D;;;;OAIG;IACH,WAAW,CAAC,EAAE,IAAI,CAAC,qBAAqB,EAAE,QAAQ,CAAC,CAAC;IACpD,wCAAwC;IACxC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,eAAe,KAAK,IAAI,CAAC;IAC/C,uDAAuD;IACvD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,CAwGxD"}
@@ -15,8 +15,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  */
16
16
  import { useEffect, useRef, useState } from "react";
17
17
  import { loadMapPackage } from "../runtime/index.js";
18
- import { useHonuaClient } from "./hooks.js";
19
18
  import { HonuaMapContext } from "./context.js";
19
+ import { HonuaExternalMapContext } from "./external-map.js";
20
+ import { useHonuaClient } from "./hooks.js";
20
21
  const EMPTY_STYLE = { version: 8, sources: {}, layers: [] };
21
22
  /**
22
23
  * Render a Honua-composed MapLibre map.
@@ -105,5 +106,5 @@ export function HonuaMap(props) {
105
106
  runtime.updatePackage(mapPackage).catch((error) => onError?.(error));
106
107
  // biome-ignore lint/correctness/useExhaustiveDependencies: onError is a callback prop; re-running on its identity is undesirable.
107
108
  }, [contextValue.runtime, mapPackage]);
108
- return (_jsxs(HonuaMapContext.Provider, { value: contextValue, children: [externalMap ? null : _jsx("div", { ref: containerRef, className: className, style: style, "data-honua-map": "" }), children] }));
109
+ return (_jsx(HonuaMapContext.Provider, { value: contextValue, children: _jsxs(HonuaExternalMapContext.Provider, { value: contextValue.map ?? null, children: [externalMap ? null : _jsx("div", { ref: containerRef, className: className, style: style, "data-honua-map": "" }), children] }) }));
109
110
  }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * {@link HonuaSourceLayer} — the declarative face of the data-to-map bridge
3
+ * (REQ-002 of the react depth pass): mount a contract `Source` as a styled,
4
+ * interactive MapLibre layer set inside any enclosing map (`HonuaMap`, a
5
+ * {@link HonuaMapProvider}-published external map, or an explicit `map` prop).
6
+ *
7
+ * Prop changes diff in place (`query` → `setFilter`, `renderer.paint/layout` →
8
+ * property setters); unmounting disposes every bridge-owned MapLibre resource;
9
+ * React 18/19 StrictMode double-mounts neither leak nor double-add (see
10
+ * {@link useMountedSource}).
11
+ *
12
+ * SSR-safe: no `window` / `document` access at module scope.
13
+ *
14
+ * @module
15
+ */
16
+ import type { ReactNode } from "react";
17
+ import type { Query, Source } from "../contract/types.js";
18
+ import type { DataToMapLibreMap, MountSourceOptions, MountedSourceDiagnostics, MountSourcePopupOptions } from "../map/data-to-map-bridge.js";
19
+ import type { Renderer } from "../style/renderers.js";
20
+ /**
21
+ * Declarative styling for a {@link HonuaSourceLayer}: the bridge's styling
22
+ * surface (geometry restriction, per-geometry paint/layout overrides, or a
23
+ * verbatim custom layer set). `paint` / `layout` changes are applied in place;
24
+ * `geometry` / `layers` changes remount the layer set.
25
+ *
26
+ * The `renderer` prop also accepts a first-class renderer object from
27
+ * `@honua/sdk-js/style` (`classBreaksRenderer`, `uniqueValueRenderer`,
28
+ * `heatmapRenderer`, `clusterRenderer`); changes diff through the handle's
29
+ * `setRenderer` without teardown.
30
+ *
31
+ * @experimental
32
+ */
33
+ export type HonuaSourceRenderer<T = Record<string, unknown>> = Pick<MountSourceOptions<T>, "geometry" | "layers" | "paint" | "layout">;
34
+ /** Props for {@link HonuaSourceLayer}. @experimental */
35
+ export interface HonuaSourceLayerProps<T = Record<string, unknown>> {
36
+ /** The contract `Source` to mount. `null` while it is still resolving. */
37
+ source: Source<T> | null | undefined;
38
+ /**
39
+ * Target map. Omit to resolve the nearest enclosing `HonuaMap` /
40
+ * {@link HonuaMapProvider} (external-map interop).
41
+ */
42
+ map?: DataToMapLibreMap | null;
43
+ /** Filter applied to the mounted data; changes diff via `setFilter`. */
44
+ query?: Readonly<Omit<Query<T>, "signal">>;
45
+ /**
46
+ * Styling: either a first-class renderer object from `@honua/sdk-js/style`
47
+ * (diffed via `setRenderer`) or a {@link HonuaSourceRenderer} bag of
48
+ * geometry restriction + paint/layout overrides or custom layers.
49
+ */
50
+ renderer?: HonuaSourceRenderer<T> | Renderer;
51
+ /** Open a popup on feature click (`factory` usually `() => new maplibregl.Popup()`). */
52
+ popup?: MountSourcePopupOptions;
53
+ /** Toggle hover feature-state on the mounted layers (bridge `hover` option). */
54
+ hover?: boolean | {
55
+ readonly stateKey?: string;
56
+ };
57
+ /**
58
+ * Share feature selection with the enclosing `HonuaSelectionProvider`:
59
+ * clicks toggle the shared selection and the shared selection is mirrored
60
+ * onto feature-state (default key `"selected"`). Also publishes hover to the
61
+ * provider when {@link hover} is enabled. No-op without a provider.
62
+ */
63
+ selection?: boolean | {
64
+ readonly stateKey?: string;
65
+ readonly multiSelect?: boolean;
66
+ };
67
+ /** When `false`, nothing is mounted. @default true */
68
+ enabled?: boolean;
69
+ /** Called with fresh diagnostics after the mount and each applied update. */
70
+ onDiagnostics?: (diagnostics: MountedSourceDiagnostics) => void;
71
+ /** Called when mounting or an update fails. */
72
+ onError?: (error: unknown) => void;
73
+ /** Force a bridge strategy (`"geojson"` / `"query-tiles"`). */
74
+ strategy?: MountSourceOptions<T>["strategy"];
75
+ /** MapLibre source id override. @default `honua-<descriptor id>` */
76
+ sourceId?: string;
77
+ /** Base layer id override; geometry suffixes are appended. */
78
+ layerId?: string;
79
+ /** Existing layer id to insert the bridge layers before. */
80
+ beforeId?: string;
81
+ /** GeoJSON materialization bound. @default 10000 */
82
+ maxGeoJsonFeatures?: number;
83
+ /** Dynamic query-tile descriptor enabling the tile strategy. */
84
+ queryTiles?: MountSourceOptions<T>["queryTiles"];
85
+ /** Vector `source-layer` for the tile strategy. */
86
+ sourceLayer?: string;
87
+ /** Attribution override. */
88
+ attribution?: string;
89
+ /** Fit the map to the mounted data. */
90
+ fitBounds?: MountSourceOptions<T>["fitBounds"];
91
+ }
92
+ /**
93
+ * Mount a contract `Source` through the data-to-map bridge, declaratively.
94
+ * Renders nothing itself.
95
+ *
96
+ * @example
97
+ * ```tsx
98
+ * <HonuaSourceLayer
99
+ * source={source}
100
+ * query={{ where: "STATUS = 'OPEN'" }}
101
+ * renderer={{ paint: { point: { "circle-color": "#38bdf8" } } }}
102
+ * hover
103
+ * selection
104
+ * />
105
+ * ```
106
+ *
107
+ * @experimental
108
+ */
109
+ export declare function HonuaSourceLayer<T = Record<string, unknown>>(props: HonuaSourceLayerProps<T>): ReactNode;
110
+ //# sourceMappingURL=honua-source-layer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"honua-source-layer.d.ts","sourceRoot":"","sources":["../../../src/react/honua-source-layer.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAKtD;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,IAAI,CACjE,kBAAkB,CAAC,CAAC,CAAC,EACrB,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,CAC3C,CAAC;AAYF,wDAAwD;AACxD,MAAM,WAAW,qBAAqB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAChE,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC;IACrC;;;OAGG;IACH,GAAG,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC;IAC/B,wEAAwE;IACxE,KAAK,CAAC,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC;IAC7C,wFAAwF;IACxF,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,gFAAgF;IAChF,KAAK,CAAC,EAAE,OAAO,GAAG;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,GAAG;QAAE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;IACrF,sDAAsD;IACtD,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,6EAA6E;IAC7E,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,wBAAwB,KAAK,IAAI,CAAC;IAChE,+CAA+C;IAC/C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACnC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC7C,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gEAAgE;IAChE,UAAU,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC;IACjD,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4BAA4B;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uCAAuC;IACvC,SAAS,CAAC,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;CAChD;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,qBAAqB,CAAC,CAAC,CAAC,GAAG,SAAS,CA+DxG"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * {@link HonuaSourceLayer} — the declarative face of the data-to-map bridge
3
+ * (REQ-002 of the react depth pass): mount a contract `Source` as a styled,
4
+ * interactive MapLibre layer set inside any enclosing map (`HonuaMap`, a
5
+ * {@link HonuaMapProvider}-published external map, or an explicit `map` prop).
6
+ *
7
+ * Prop changes diff in place (`query` → `setFilter`, `renderer.paint/layout` →
8
+ * property setters); unmounting disposes every bridge-owned MapLibre resource;
9
+ * React 18/19 StrictMode double-mounts neither leak nor double-add (see
10
+ * {@link useMountedSource}).
11
+ *
12
+ * SSR-safe: no `window` / `document` access at module scope.
13
+ *
14
+ * @module
15
+ */
16
+ import { useMemo } from "react";
17
+ import { useHonuaMap } from "./external-map.js";
18
+ import { useMapHoverBinding, useMapSelectionBinding } from "./selection.js";
19
+ import { useMountedSource } from "./use-mounted-source.js";
20
+ /** Recognize a first-class renderer object from `@honua/sdk-js/style`. */
21
+ function isRendererObject(value) {
22
+ return (typeof value === "object" &&
23
+ value !== null &&
24
+ typeof value.kind === "string" &&
25
+ typeof value.toMapLibre === "function");
26
+ }
27
+ /**
28
+ * Mount a contract `Source` through the data-to-map bridge, declaratively.
29
+ * Renders nothing itself.
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * <HonuaSourceLayer
34
+ * source={source}
35
+ * query={{ where: "STATUS = 'OPEN'" }}
36
+ * renderer={{ paint: { point: { "circle-color": "#38bdf8" } } }}
37
+ * hover
38
+ * selection
39
+ * />
40
+ * ```
41
+ *
42
+ * @experimental
43
+ */
44
+ export function HonuaSourceLayer(props) {
45
+ const map = useHonuaMap(props.map);
46
+ // The renderer prop carries either a first-class renderer object (routed to
47
+ // the bridge's `renderer` option, diffed via setRenderer) or the
48
+ // geometry-default styling bag (diffed via property setters).
49
+ const rendererObject = isRendererObject(props.renderer) ? props.renderer : undefined;
50
+ const styleRenderer = rendererObject === undefined ? props.renderer : undefined;
51
+ const { handle, diagnostics } = useMountedSource(map, props.source, {
52
+ enabled: props.enabled,
53
+ query: props.query,
54
+ renderer: rendererObject,
55
+ geometry: styleRenderer?.geometry,
56
+ layers: styleRenderer?.layers,
57
+ paint: styleRenderer?.paint,
58
+ layout: styleRenderer?.layout,
59
+ popup: props.popup,
60
+ hover: props.hover,
61
+ onDiagnostics: props.onDiagnostics,
62
+ onError: props.onError,
63
+ strategy: props.strategy,
64
+ sourceId: props.sourceId,
65
+ layerId: props.layerId,
66
+ beforeId: props.beforeId,
67
+ maxGeoJsonFeatures: props.maxGeoJsonFeatures,
68
+ queryTiles: props.queryTiles,
69
+ sourceLayer: props.sourceLayer,
70
+ attribution: props.attribution,
71
+ fitBounds: props.fitBounds,
72
+ });
73
+ // Interactive layers exclude the polygon outline companion, mirroring the
74
+ // bridge's own popup/hover wiring. Re-read after every applied update
75
+ // (`diagnostics` changes) — a structural setRenderer swap replaces the
76
+ // owned layer ids on the same handle.
77
+ // biome-ignore lint/correctness/useExhaustiveDependencies: `diagnostics` signals layer-set changes; ids are read from the handle getter.
78
+ const interactiveLayerIds = useMemo(() => (handle ? handle.layerIds.filter((id) => !id.endsWith("-polygon-outline")) : []), [handle, diagnostics]);
79
+ const selection = props.selection;
80
+ const selectionEnabled = Boolean(selection) && handle !== null;
81
+ // Bind against the handle's *resolved* source-layer — the explicit prop or
82
+ // the default the bridge derived from the query-tile descriptor. MapLibre
83
+ // scopes vector-tile feature-state by source-layer, so the binding must
84
+ // target the exact value the mounted layers render with.
85
+ const resolvedSourceLayer = handle?.sourceLayer ?? props.sourceLayer;
86
+ useMapSelectionBinding(map, {
87
+ sourceId: handle?.sourceId ?? "",
88
+ layerIds: interactiveLayerIds,
89
+ sourceLayer: resolvedSourceLayer,
90
+ stateKey: typeof selection === "object" ? selection.stateKey : undefined,
91
+ multiSelect: typeof selection === "object" ? selection.multiSelect : undefined,
92
+ enabled: selectionEnabled,
93
+ });
94
+ useMapHoverBinding(map, {
95
+ sourceId: handle?.sourceId ?? "",
96
+ layerIds: interactiveLayerIds,
97
+ sourceLayer: resolvedSourceLayer,
98
+ enabled: selectionEnabled && Boolean(props.hover),
99
+ });
100
+ return null;
101
+ }
package/react/hooks.d.ts CHANGED
@@ -56,6 +56,34 @@ export interface UseQueryOptions {
56
56
  enabled?: boolean;
57
57
  /** Use `source.queryAll()` (drain every page) instead of a single page. */
58
58
  drainAllPages?: boolean;
59
+ /**
60
+ * Stale-while-revalidate window in milliseconds. When a consumer (re)mounts
61
+ * or the query key changes and the cached result is at least this old, the
62
+ * cached data keeps rendering while a background refetch runs
63
+ * (`isFetching: true`, `data` preserved). `0` revalidates on every mount.
64
+ * Omitted keeps the historical behavior: cached results are served without
65
+ * revalidation until `refetch()` or an explicit invalidation.
66
+ *
67
+ * @experimental
68
+ */
69
+ staleTimeMs?: number;
70
+ /**
71
+ * Throw query errors during render so the nearest React error boundary
72
+ * receives them (typed errors such as `HonuaCapabilityNotSupportedError`
73
+ * arrive intact). Default `false`: errors are returned on the snapshot.
74
+ *
75
+ * @experimental
76
+ */
77
+ throwOnError?: boolean;
78
+ /**
79
+ * Suspense mode: while the first fetch (no cached data yet) is in flight the
80
+ * hook suspends, and errors are thrown to the nearest error boundary. Wrap
81
+ * consumers in `<Suspense fallback={…}>`. Background refetches do not
82
+ * suspend — cached data keeps rendering stale-while-revalidate style.
83
+ *
84
+ * @experimental
85
+ */
86
+ suspense?: boolean;
59
87
  }
60
88
  /** Result of {@link useQuery}: a stable snapshot plus a `refetch` trigger. */
61
89
  export interface UseQueryResult<T> extends QuerySnapshot<Result<T>> {
@@ -1 +1 @@
1
- {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/react/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,KAAK,EACL,MAAM,EACN,MAAM,EACN,gBAAgB,EAChB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAiC,MAAM,kBAAkB,CAAC;AAU3G;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAEpD;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAcD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAqB9D;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,8EAA8E;AAC9E,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACjE,8DAA8D;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,EACpC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAChB,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAAC,CAAC,CAAC,CA2CnB;AAED,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,OAAO,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;CACrD;AAED,yCAAyC;AACzC,MAAM,WAAW,qBAAsB,SAAQ,aAAa,CAAC,wBAAwB,CAAC;IACpF,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,qBAAqB,CAmC3F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,eAAe,GAAG,IAAI,CAEtD;AAED,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAAE,KAAK,IAAI,IAAI,CAAA;CAAE,GAAG;IAAE,MAAM,IAAI,IAAI,CAAA;CAAE,GAAG;IAAE,WAAW,IAAI,IAAI,CAAA;CAAE,CAAC;AAE9G;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,EAC/D,IAAI,GAAE,SAAS,OAAO,EAAO,GAC5B,IAAI,CASN"}
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../../../src/react/hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,OAAO,EACP,SAAS,EACT,KAAK,EACL,MAAM,EACN,MAAM,EACN,gBAAgB,EAChB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,yBAAyB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5F,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,aAAa,EAAiC,MAAM,kBAAkB,CAAC;AAU3G;;;;GAIG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAE5C;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,eAAe,CAEpD;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,SAAS,CAAC;IACd,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IACzC,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,aAAa,CAAC,EAAE,cAAc,CAAC;CAChC;AAcD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAqB9D;AAED,oCAAoC;AACpC,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;;OASG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,8EAA8E;AAC9E,MAAM,WAAW,cAAc,CAAC,CAAC,CAAE,SAAQ,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACjE,8DAA8D;IAC9D,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,QAAQ,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,IAAI,EACpC,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,EAChB,OAAO,GAAE,eAAoB,GAC5B,cAAc,CAAC,CAAC,CAAC,CAmEnB;AAED,2CAA2C;AAC3C,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yEAAyE;IACzE,OAAO,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,QAAQ,CAAC,CAAC;CACrD;AAED,yCAAyC;AACzC,MAAM,WAAW,qBAAsB,SAAQ,aAAa,CAAC,wBAAwB,CAAC;IACpF,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,sBAA2B,GAAG,qBAAqB,CAmC3F;AAED;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,eAAe,GAAG,IAAI,CAEtD;AAED,0EAA0E;AAC1E,MAAM,MAAM,eAAe,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAAE,KAAK,IAAI,IAAI,CAAA;CAAE,GAAG;IAAE,MAAM,IAAI,IAAI,CAAA;CAAE,GAAG;IAAE,WAAW,IAAI,IAAI,CAAA;CAAE,CAAC;AAE9G;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,CAAC,MAAM,eAAe,GAAG,SAAS,CAAC,GAAG,IAAI,GAAG,SAAS,EAC/D,IAAI,GAAE,SAAS,OAAO,EAAO,GAC5B,IAAI,CASN"}
package/react/hooks.js CHANGED
@@ -112,19 +112,41 @@ export function useQuery(source, query, options = {}) {
112
112
  const subscribe = useCallback((onStoreChange) => (key ? cache.subscribe(key, onStoreChange) : () => { }), [cache, key]);
113
113
  const getSnapshot = useCallback(() => (key ? cache.getSnapshot(key) : idleSnapshot()), [cache, key]);
114
114
  const snapshot = useSyncExternalStore(subscribe, getSnapshot, () => idleSnapshot());
115
+ const staleTimeMs = options.staleTimeMs;
116
+ // `key` already encodes source id + query + mode, so the fetcher (like the
117
+ // effects below) is intentionally keyed on it rather than on the identity of
118
+ // the `source` / `query` objects — an inline literal cannot thrash the cache.
119
+ // biome-ignore lint/correctness/useExhaustiveDependencies: `key` already encodes source id + query + mode.
120
+ const fetcher = useCallback((signal) => drainAllPages
121
+ ? source.queryAll({ ...query, signal })
122
+ : source.query({ ...query, signal }), [key]);
115
123
  // biome-ignore lint/correctness/useExhaustiveDependencies: `key` already encodes source id + query + mode.
116
124
  useEffect(() => {
117
125
  if (!key || !source)
118
126
  return;
119
- cache.ensure(key, (signal) => drainAllPages ? source.queryAll({ ...query, signal }) : source.query({ ...query, signal }));
120
- }, [cache, key]);
127
+ cache.ensure(key, fetcher, staleTimeMs !== undefined ? { staleTimeMs } : {});
128
+ }, [cache, key, fetcher, staleTimeMs]);
121
129
  // biome-ignore lint/correctness/useExhaustiveDependencies: `key` already encodes source id + query + mode.
122
130
  const refetch = useCallback(() => {
123
131
  if (!key || !source)
124
132
  return;
125
- cache.run(key, (signal) => drainAllPages ? source.queryAll({ ...query, signal }) : source.query({ ...query, signal }));
126
- }, [cache, key]);
133
+ cache.run(key, fetcher);
134
+ }, [cache, key, fetcher]);
127
135
  useDebugValue(snapshot.status);
136
+ if (key && source && (options.suspense || options.throwOnError) && snapshot.status === "error") {
137
+ // Surface the typed error (e.g. HonuaCapabilityNotSupportedError) to the
138
+ // nearest error boundary. `refetch()` from the boundary retries.
139
+ throw snapshot.error;
140
+ }
141
+ if (options.suspense && key && source && snapshot.data === undefined) {
142
+ // First load with no cached data: kick the fetch during render (idempotent
143
+ // — `ensure` is a no-op once the entry left `idle`, so StrictMode's double
144
+ // render cannot double-fetch) and suspend on its promise.
145
+ cache.ensure(key, fetcher, staleTimeMs !== undefined ? { staleTimeMs } : {});
146
+ const pending = cache.getPromise(key);
147
+ if (pending)
148
+ throw pending;
149
+ }
128
150
  return useMemo(() => ({ ...snapshot, refetch }), [snapshot, refetch]);
129
151
  }
130
152
  /**
package/react/index.d.ts CHANGED
@@ -39,4 +39,12 @@ export { HonuaContext, HonuaMapContext } from "./context.js";
39
39
  export type { HonuaContextValue, HonuaMapContextValue } from "./context.js";
40
40
  export { HonuaQueryCache, stableQueryHash, idleSnapshot } from "./query-cache.js";
41
41
  export type { QueryStatus, QuerySnapshot, QueryFetcher } from "./query-cache.js";
42
+ export { HonuaExternalMapContext, HonuaMapProvider, useHonuaMap } from "./external-map.js";
43
+ export type { HonuaMapProviderProps } from "./external-map.js";
44
+ export { useMountedSource } from "./use-mounted-source.js";
45
+ export type { UseMountedSourceOptions, UseMountedSourceResult } from "./use-mounted-source.js";
46
+ export { HonuaSourceLayer } from "./honua-source-layer.js";
47
+ export type { HonuaSourceLayerProps, HonuaSourceRenderer } from "./honua-source-layer.js";
48
+ export { HonuaSelectionContext, HonuaSelectionProvider, HonuaSelectionStore, useHover, useMapHoverBinding, useMapSelectionBinding, useSelection, } from "./selection.js";
49
+ export type { HonuaSelectionProviderProps, HonuaSelectionSnapshot, MapHoverBindingOptions, MapSelectionBindingOptions, UseHoverResult, UseSelectionResult, } from "./selection.js";
42
50
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,eAAe,EACf,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAClF,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/react/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,OAAO,EACL,cAAc,EACd,kBAAkB,EAClB,UAAU,EACV,QAAQ,EACR,eAAe,EACf,aAAa,EACb,WAAW,GACZ,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,sBAAsB,EACtB,qBAAqB,EACrB,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAC1F,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,YAAY,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC7D,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE5E,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAClF,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAIjF,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC3F,YAAY,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAE/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAE/F,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,YAAY,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE1F,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,QAAQ,EACR,kBAAkB,EAClB,sBAAsB,EACtB,YAAY,GACb,MAAM,gBAAgB,CAAC;AACxB,YAAY,EACV,2BAA2B,EAC3B,sBAAsB,EACtB,sBAAsB,EACtB,0BAA0B,EAC1B,cAAc,EACd,kBAAkB,GACnB,MAAM,gBAAgB,CAAC"}
package/react/index.js CHANGED
@@ -32,3 +32,8 @@ export { HonuaLayer } from "./honua-layer.js";
32
32
  export { HonuaPopup } from "./honua-popup.js";
33
33
  export { HonuaContext, HonuaMapContext } from "./context.js";
34
34
  export { HonuaQueryCache, stableQueryHash, idleSnapshot } from "./query-cache.js";
35
+ // ── React depth pass (issue #498) — all @experimental ─────────────
36
+ export { HonuaExternalMapContext, HonuaMapProvider, useHonuaMap } from "./external-map.js";
37
+ export { useMountedSource } from "./use-mounted-source.js";
38
+ export { HonuaSourceLayer } from "./honua-source-layer.js";
39
+ export { HonuaSelectionContext, HonuaSelectionProvider, HonuaSelectionStore, useHover, useMapHoverBinding, useMapSelectionBinding, useSelection, } from "./selection.js";
@@ -27,6 +27,13 @@ export interface QuerySnapshot<T> {
27
27
  readonly isLoading: boolean;
28
28
  /** True whenever a fetch is in flight, including background refetches. */
29
29
  readonly isFetching: boolean;
30
+ /**
31
+ * Epoch milliseconds of the last successful fetch, `undefined` until one
32
+ * resolves. Drives stale-while-revalidate decisions.
33
+ *
34
+ * @experimental
35
+ */
36
+ readonly dataUpdatedAt?: number;
30
37
  }
31
38
  /**
32
39
  * Returns the shared idle snapshot. Exposed so the SSR / disabled-query paths
@@ -50,11 +57,26 @@ export declare class HonuaQueryCache {
50
57
  */
51
58
  subscribe(key: string, listener: () => void): () => void;
52
59
  /**
53
- * Kick off a fetch for `key` only when it has never resolved and is not
54
- * already in flight. This is the idempotent entry point hooks call from an
55
- * effect; StrictMode's double-invoke therefore does not double-fetch.
60
+ * Kick off a fetch for `key` when it has never resolved and is not already
61
+ * in flight. This is the idempotent entry point hooks call from an effect;
62
+ * StrictMode's double-invoke therefore does not double-fetch.
63
+ *
64
+ * With `options.staleTimeMs`, a resolved entry whose data is at least that
65
+ * old is additionally revalidated in the background (stale-while-revalidate:
66
+ * the cached data stays served while `isFetching` is true). `0` revalidates
67
+ * on every ensure; omitted keeps the historical never-revalidate behavior.
68
+ */
69
+ ensure<T>(key: string, fetcher: QueryFetcher<T>, options?: {
70
+ staleTimeMs?: number;
71
+ }): void;
72
+ /**
73
+ * The promise of the in-flight run for `key`, or `undefined` when no fetch
74
+ * is running. Suspense-mode hooks throw this promise while the first load is
75
+ * pending; it never rejects (errors land on the snapshot instead).
76
+ *
77
+ * @experimental
56
78
  */
57
- ensure<T>(key: string, fetcher: QueryFetcher<T>): void;
79
+ getPromise(key: string): Promise<unknown> | undefined;
58
80
  /**
59
81
  * Force a (re)fetch for `key`, superseding any in-flight request. The last
60
82
  * resolved `data` is preserved while the refetch runs.
@@ -1 +1 @@
1
- {"version":3,"file":"query-cache.d.ts","sourceRoot":"","sources":["../../../src/react/query-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,gDAAgD;AAChD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnE;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;CAC9B;AAkBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,CAElD;AAED,qFAAqF;AACrF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAElE;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IAEzD,6EAA6E;IAC7E,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC;IAI7C;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IA+BxD;;;;OAIG;IACH,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IAOtD;;;OAGG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IA2CnD,sEAAsE;IACtE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAS7B,gEAAgE;IAChE,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,KAAK;CAMd;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD"}
1
+ {"version":3,"file":"query-cache.d.ts","sourceRoot":"","sources":["../../../src/react/query-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,gDAAgD;AAChD,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnE;;;;GAIG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAC5B,0EAA0E;IAC1E,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAoBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,CAAC,KAAK,aAAa,CAAC,CAAC,CAAC,CAElD;AAED,qFAAqF;AACrF,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAElE;;;GAGG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiC;IAEzD,6EAA6E;IAC7E,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC,CAAC;IAI7C;;;;OAIG;IACH,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAgCxD;;;;;;;;;OASG;IACH,MAAM,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE;QAAE,WAAW,CAAC,EAAE,MAAM,CAAA;KAAO,GAAG,IAAI;IAgB9F;;;;;;OAMG;IACH,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,SAAS;IAKrD;;;OAGG;IACH,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,IAAI;IA8CnD,sEAAsE;IACtE,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAS7B,gEAAgE;IAChE,KAAK,IAAI,IAAI;IASb,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,KAAK;CAMd;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAEtD"}