@chelone/core 0.6.0 → 0.9.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 (255) hide show
  1. package/dist/assistant/assistant-session.d.ts +6 -0
  2. package/dist/assistant/assistant-session.js +204 -181
  3. package/dist/assistant/echo-transport.js +26 -29
  4. package/dist/assistant/http-transport.d.ts +10 -0
  5. package/dist/assistant/http-transport.js +98 -0
  6. package/dist/assistant/types.d.ts +1 -7
  7. package/dist/auth/access-control.d.ts +13 -5
  8. package/dist/auth/access-control.js +25 -21
  9. package/dist/auth/auth-options.d.ts +23 -17
  10. package/dist/auth/auth-options.js +82 -48
  11. package/dist/auth/permission-matrix.d.ts +12 -0
  12. package/dist/auth/permission-matrix.js +67 -0
  13. package/dist/auth/types.d.ts +14 -4
  14. package/dist/block-editor/block-document.d.ts +64 -0
  15. package/dist/block-editor/block-document.js +147 -0
  16. package/dist/block-editor/data-blocks.d.ts +63 -0
  17. package/dist/block-editor/data-blocks.js +117 -0
  18. package/dist/chart/chart-data.js +46 -44
  19. package/dist/command-menu/hotkey.js +58 -47
  20. package/dist/command-menu/recents.d.ts +4 -0
  21. package/dist/command-menu/recents.js +12 -11
  22. package/dist/command-menu/registry.d.ts +9 -5
  23. package/dist/command-menu/registry.js +36 -46
  24. package/dist/controller/calendar-dates.d.ts +12 -0
  25. package/dist/controller/calendar-dates.js +94 -113
  26. package/dist/controller/calendar-events.js +93 -88
  27. package/dist/controller/client-list-controller.d.ts +2 -2
  28. package/dist/controller/client-list-controller.js +251 -254
  29. package/dist/controller/data-table-columns.d.ts +2 -0
  30. package/dist/controller/data-table-columns.js +50 -56
  31. package/dist/controller/deep-equal.d.ts +1 -0
  32. package/dist/controller/deep-equal.js +14 -0
  33. package/dist/controller/filter-toggle.js +36 -44
  34. package/dist/controller/in-place-editor.d.ts +43 -0
  35. package/dist/controller/in-place-editor.js +43 -0
  36. package/dist/controller/infinite-list-controller.js +219 -189
  37. package/dist/controller/inspector.d.ts +55 -0
  38. package/dist/controller/inspector.js +70 -0
  39. package/dist/controller/job-monitor.d.ts +10 -4
  40. package/dist/controller/job-monitor.js +46 -47
  41. package/dist/controller/kanban-board.js +45 -24
  42. package/dist/controller/list-controller.d.ts +8 -5
  43. package/dist/controller/list-controller.js +447 -400
  44. package/dist/controller/list-params.js +96 -87
  45. package/dist/controller/pivot-grid.d.ts +4 -4
  46. package/dist/controller/pivot-grid.js +89 -95
  47. package/dist/controller/prev-next.d.ts +2 -2
  48. package/dist/controller/prev-next.js +74 -54
  49. package/dist/controller/record-controllers.d.ts +14 -6
  50. package/dist/controller/record-controllers.js +288 -257
  51. package/dist/controller/record-diff.d.ts +21 -0
  52. package/dist/controller/record-diff.js +50 -0
  53. package/dist/controller/record-timeline.d.ts +5 -5
  54. package/dist/controller/record-timeline.js +42 -30
  55. package/dist/controller/record-tree.js +57 -47
  56. package/dist/controller/record-workflow.d.ts +38 -16
  57. package/dist/controller/record-workflow.js +85 -64
  58. package/dist/controller/reference-many-controller.d.ts +2 -0
  59. package/dist/controller/reference-many-controller.js +199 -192
  60. package/dist/controller/reference-one-controller.d.ts +1 -1
  61. package/dist/controller/reference-one-controller.js +63 -56
  62. package/dist/controller/saved-queries.js +104 -91
  63. package/dist/controller/translate-fallback.js +10 -6
  64. package/dist/csv/export.d.ts +4 -3
  65. package/dist/csv/export.js +43 -40
  66. package/dist/csv/import-controller.d.ts +4 -0
  67. package/dist/csv/import-controller.js +189 -169
  68. package/dist/csv/match-headers.js +29 -22
  69. package/dist/csv/parse-with.js +60 -54
  70. package/dist/csv/run-import.js +69 -72
  71. package/dist/csv/validate-rows.d.ts +1 -1
  72. package/dist/csv/validate-rows.js +65 -52
  73. package/dist/data/cache-ops.d.ts +5 -4
  74. package/dist/data/cache-ops.js +208 -188
  75. package/dist/data/combine-data-providers.js +25 -23
  76. package/dist/data/data-provider-log.d.ts +45 -0
  77. package/dist/data/data-provider-log.js +93 -0
  78. package/dist/data/errors.js +14 -10
  79. package/dist/data/fake-provider.d.ts +6 -3
  80. package/dist/data/fake-provider.js +135 -114
  81. package/dist/data/fetch-utils.js +61 -44
  82. package/dist/data/filter-branch.d.ts +5 -0
  83. package/dist/data/filter-branch.js +10 -0
  84. package/dist/data/get-many-aggregate.js +84 -76
  85. package/dist/data/http-error.js +17 -12
  86. package/dist/data/keys.js +50 -36
  87. package/dist/data/mutation-options.d.ts +14 -9
  88. package/dist/data/mutation-options.js +93 -109
  89. package/dist/data/query-options.js +73 -68
  90. package/dist/data/remove-empty.d.ts +3 -0
  91. package/dist/data/remove-empty.js +11 -0
  92. package/dist/data/undoable.d.ts +2 -2
  93. package/dist/data/undoable.js +190 -174
  94. package/dist/data/with-lifecycle-callbacks.d.ts +45 -17
  95. package/dist/data/with-lifecycle-callbacks.js +66 -52
  96. package/dist/environment/application-update-store.js +58 -55
  97. package/dist/environment/offline-store.js +21 -21
  98. package/dist/form/array-binding.d.ts +30 -11
  99. package/dist/form/array-binding.js +184 -166
  100. package/dist/form/choice-ids.d.ts +7 -0
  101. package/dist/form/choice-ids.js +11 -0
  102. package/dist/form/choices-controller.d.ts +22 -2
  103. package/dist/form/choices-controller.js +315 -275
  104. package/dist/form/date-format.d.ts +38 -0
  105. package/dist/form/date-format.js +89 -0
  106. package/dist/form/dirty-values.d.ts +2 -2
  107. package/dist/form/dirty-values.js +22 -11
  108. package/dist/form/duration.d.ts +22 -0
  109. package/dist/form/duration.js +49 -0
  110. package/dist/form/edit-create-form.d.ts +12 -5
  111. package/dist/form/edit-create-form.js +243 -188
  112. package/dist/form/event-value.d.ts +6 -0
  113. package/dist/form/event-value.js +17 -0
  114. package/dist/form/field-error.d.ts +14 -0
  115. package/dist/form/field-error.js +28 -0
  116. package/dist/form/field-name.d.ts +20 -0
  117. package/dist/form/field-name.js +6 -0
  118. package/dist/form/filter-form-values.js +63 -49
  119. package/dist/form/form-controller.d.ts +9 -1
  120. package/dist/form/form-controller.js +165 -160
  121. package/dist/form/form-groups.d.ts +4 -3
  122. package/dist/form/form-groups.js +79 -79
  123. package/dist/form/form-handle.d.ts +175 -0
  124. package/dist/form/form-handle.js +68 -0
  125. package/dist/form/input-binding.d.ts +56 -31
  126. package/dist/form/input-binding.js +171 -118
  127. package/dist/form/input-values.d.ts +140 -0
  128. package/dist/form/input-values.js +206 -0
  129. package/dist/form/json-text.d.ts +11 -0
  130. package/dist/form/json-text.js +62 -37
  131. package/dist/form/save-pipeline.d.ts +6 -8
  132. package/dist/form/save-pipeline.js +91 -82
  133. package/dist/form/set-form-values.d.ts +6 -0
  134. package/dist/form/set-form-values.js +10 -0
  135. package/dist/form/source.js +97 -65
  136. package/dist/form/text-of.d.ts +2 -0
  137. package/dist/form/text-of.js +7 -0
  138. package/dist/form/validators.d.ts +58 -19
  139. package/dist/form/validators.js +97 -71
  140. package/dist/geo/bbox.d.ts +5 -4
  141. package/dist/geo/bbox.js +69 -48
  142. package/dist/geo/bounds.d.ts +14 -3
  143. package/dist/geo/bounds.js +98 -68
  144. package/dist/geo/constraints.js +88 -93
  145. package/dist/geo/coordinates.d.ts +10 -0
  146. package/dist/geo/coordinates.js +44 -23
  147. package/dist/geo/editor.d.ts +13 -13
  148. package/dist/geo/editor.js +176 -153
  149. package/dist/geo/geocoding.d.ts +4 -4
  150. package/dist/geo/geocoding.js +38 -41
  151. package/dist/geo/geojson.d.ts +12 -74
  152. package/dist/geo/geojson.js +52 -40
  153. package/dist/geo/geometry-math.d.ts +4 -4
  154. package/dist/geo/geometry-math.js +100 -93
  155. package/dist/geo/index.d.ts +9 -8
  156. package/dist/geo/map-layers.d.ts +42 -7
  157. package/dist/geo/map-layers.js +88 -62
  158. package/dist/geo/map-style.d.ts +12 -0
  159. package/dist/geo/map-style.js +43 -32
  160. package/dist/geo/messages.js +48 -45
  161. package/dist/geo/nominatim.js +88 -80
  162. package/dist/geo/osm-presets.d.ts +4 -3
  163. package/dist/geo/osm-presets.js +108 -86
  164. package/dist/geo/osm-tags.js +14 -12
  165. package/dist/geo/overpass.d.ts +14 -2
  166. package/dist/geo/overpass.js +83 -90
  167. package/dist/geo/shape-value.d.ts +9 -5
  168. package/dist/geo/shape-value.js +169 -146
  169. package/dist/geo/snap-to-roads.d.ts +3 -3
  170. package/dist/geo/snap-to-roads.js +55 -50
  171. package/dist/geo/throttle.js +36 -39
  172. package/dist/guards.d.ts +44 -0
  173. package/dist/guards.js +77 -0
  174. package/dist/i18n/default-messages.js +34 -29
  175. package/dist/i18n/english-messages.d.ts +172 -0
  176. package/dist/i18n/english-messages.js +359 -189
  177. package/dist/i18n/i18n.d.ts +2 -2
  178. package/dist/i18n/i18n.js +47 -60
  179. package/dist/i18n/inflection.d.ts +34 -0
  180. package/dist/i18n/inflection.js +82 -0
  181. package/dist/i18n/messages.d.ts +10 -2
  182. package/dist/i18n/messages.js +17 -4
  183. package/dist/i18n/missing-keys.d.ts +4 -0
  184. package/dist/i18n/missing-keys.js +70 -62
  185. package/dist/i18n/translatable.d.ts +16 -1
  186. package/dist/i18n/translatable.js +40 -47
  187. package/dist/index.d.ts +87 -39
  188. package/dist/index.js +54 -301
  189. package/dist/json/deep-equal.d.ts +8 -0
  190. package/dist/json/deep-equal.js +24 -0
  191. package/dist/json/strip-json-comments.js +38 -42
  192. package/dist/noop.d.ts +2 -0
  193. package/dist/noop.js +4 -0
  194. package/dist/notification/error-message.d.ts +2 -0
  195. package/dist/notification/error-message.js +10 -0
  196. package/dist/notification/notification-store.d.ts +3 -2
  197. package/dist/notification/notification-store.js +41 -45
  198. package/dist/onboarding-tour/placement.d.ts +2 -0
  199. package/dist/onboarding-tour/placement.js +65 -78
  200. package/dist/onboarding-tour/tour-controller.js +46 -54
  201. package/dist/realtime/add-events-for-mutations.js +75 -81
  202. package/dist/realtime/announce-lock.js +16 -13
  203. package/dist/realtime/broadcast-channel-transport.js +65 -59
  204. package/dist/realtime/copy-members.js +22 -14
  205. package/dist/realtime/in-memory-lock-provider.js +40 -48
  206. package/dist/realtime/live.js +95 -101
  207. package/dist/realtime/lock-options.js +16 -16
  208. package/dist/realtime/lock-session.js +91 -78
  209. package/dist/realtime/memory-transport.js +25 -25
  210. package/dist/realtime/presence.js +120 -122
  211. package/dist/realtime/realtime-data-provider.d.ts +10 -0
  212. package/dist/realtime/realtime-data-provider.js +45 -42
  213. package/dist/realtime/reconnect-backoff.js +17 -0
  214. package/dist/realtime/sse-transport.d.ts +31 -0
  215. package/dist/realtime/sse-transport.js +235 -0
  216. package/dist/realtime/status-store.js +23 -28
  217. package/dist/realtime/topic-hub.js +62 -71
  218. package/dist/realtime/topics.js +10 -12
  219. package/dist/realtime/types.js +10 -12
  220. package/dist/realtime/unread-count.js +21 -21
  221. package/dist/realtime/websocket-transport.d.ts +32 -0
  222. package/dist/realtime/websocket-transport.js +306 -0
  223. package/dist/resource/describe-resource.d.ts +2 -0
  224. package/dist/resource/describe-resource.js +247 -219
  225. package/dist/resource/get-at-path.js +29 -14
  226. package/dist/resource/identifier.d.ts +18 -0
  227. package/dist/resource/identifier.js +27 -0
  228. package/dist/resource/record.d.ts +4 -0
  229. package/dist/resource/record.js +10 -0
  230. package/dist/resource/register.d.ts +7 -0
  231. package/dist/resource/registry-store.d.ts +45 -15
  232. package/dist/resource/registry-store.js +67 -60
  233. package/dist/resource/registry.d.ts +10 -0
  234. package/dist/resource/registry.js +8 -4
  235. package/dist/resource/source-path.d.ts +23 -2
  236. package/dist/rich-text/rich-text-extras.d.ts +134 -0
  237. package/dist/rich-text/rich-text-extras.js +181 -0
  238. package/dist/rich-text/rich-text-image.d.ts +124 -0
  239. package/dist/rich-text/rich-text-image.js +163 -0
  240. package/dist/rich-text/rich-text.d.ts +6 -0
  241. package/dist/rich-text/rich-text.js +125 -88
  242. package/dist/router/location.d.ts +24 -4
  243. package/dist/router/location.js +43 -18
  244. package/dist/router/match.js +47 -39
  245. package/dist/router/paths.js +40 -34
  246. package/dist/router/record-from-location.d.ts +1 -1
  247. package/dist/router/record-from-location.js +25 -17
  248. package/dist/store/adapters.js +51 -55
  249. package/dist/store/micro-store.js +57 -59
  250. package/dist/store/shallow-equal.d.ts +12 -0
  251. package/dist/store/shallow-equal.js +20 -0
  252. package/dist/store/store.js +54 -57
  253. package/dist/ui-defaults.d.ts +15 -0
  254. package/dist/ui-defaults.js +17 -0
  255. package/package.json +13 -11
@@ -1,39 +1,50 @@
1
+ // src/geo/map-style.ts
2
+ /** MapLibre has no `{s}` support, so a template with one becomes three URLs. */
1
3
  function expandTileUrl(url) {
2
- if (!url.includes("{s}")) return [url];
3
- return ["a", "b", "c"].map((host) => url.replaceAll("{s}", host));
4
+ if (!url.includes("{s}")) return [url];
5
+ return [
6
+ "a",
7
+ "b",
8
+ "c"
9
+ ].map((host) => url.replaceAll("{s}", host));
4
10
  }
5
- function rasterTileStyle({
6
- tileUrl,
7
- attribution,
8
- maxZoom = 19
9
- }) {
10
- if (!tileUrl) throw new TypeError("rasterTileStyle: tileUrl is required");
11
- if (!attribution) {
12
- throw new TypeError("rasterTileStyle: attribution is required");
13
- }
14
- return {
15
- version: 8,
16
- sources: {
17
- basemap: {
18
- type: "raster",
19
- tiles: expandTileUrl(tileUrl),
20
- tileSize: 256,
21
- maxzoom: maxZoom,
22
- attribution
23
- }
24
- },
25
- layers: [{ id: "basemap", type: "raster", source: "basemap" }]
26
- };
11
+ function rasterTileStyle({ tileUrl, attribution, maxZoom = 19 }) {
12
+ if (!tileUrl) throw new TypeError("rasterTileStyle: tileUrl is required");
13
+ if (!attribution) throw new TypeError("rasterTileStyle: attribution is required");
14
+ return {
15
+ version: 8,
16
+ sources: { basemap: {
17
+ type: "raster",
18
+ tiles: expandTileUrl(tileUrl),
19
+ tileSize: 256,
20
+ maxzoom: maxZoom,
21
+ attribution
22
+ } },
23
+ layers: [{
24
+ id: "basemap",
25
+ type: "raster",
26
+ source: "basemap"
27
+ }]
28
+ };
27
29
  }
30
+ /** The zoom a map view builds when the app passes none. */
31
+ var DEFAULT_MAP_ZOOM = 13;
32
+ /** The height in pixels a map view builds when the app passes none. */
33
+ var DEFAULT_MAP_HEIGHT = 300;
34
+ /** The pixels a bounds fit keeps free around the box when the app passes none. */
35
+ var DEFAULT_FIT_PADDING = 20;
36
+ /** The highest zoom a bounds fit may reach when the app passes none. */
37
+ var DEFAULT_MAX_FIT_ZOOM = 18;
38
+ /** The pin fill a map marker uses when the app passes no color. */
39
+ var DEFAULT_PIN_COLOR = "#dc2626";
40
+ /** The SVG namespace, for building a pin element without a map library. */
41
+ var PIN_NS = "http://www.w3.org/2000/svg";
42
+ /** `[lat, lng]` (what components take) to MapLibre's `[lng, lat]`. */
28
43
  function latLngToLngLat(latLng) {
29
- return [latLng[1], latLng[0]];
44
+ return [latLng[1], latLng[0]];
30
45
  }
46
+ /** MapLibre's `[lng, lat]` to `[lat, lng]`. */
31
47
  function lngLatToLatLng(lngLat) {
32
- return [lngLat[1], lngLat[0]];
48
+ return [lngLat[1], lngLat[0]];
33
49
  }
34
- export {
35
- expandTileUrl,
36
- latLngToLngLat,
37
- lngLatToLatLng,
38
- rasterTileStyle
39
- };
50
+ export { DEFAULT_FIT_PADDING, DEFAULT_MAP_HEIGHT, DEFAULT_MAP_ZOOM, DEFAULT_MAX_FIT_ZOOM, DEFAULT_PIN_COLOR, PIN_NS, expandTileUrl, latLngToLngLat, lngLatToLatLng, rasterTileStyle };
@@ -1,47 +1,50 @@
1
1
  import { defineMessages } from "../i18n/messages.js";
2
- const mapMessages = defineMessages({
3
- "chelone.map.load_failed": "The map could not be loaded",
4
- "chelone.map.no_coordinates": "No coordinates",
5
- "chelone.map.no_geometry": "No geometry",
6
- "chelone.map.shape_not_shown": "Part of this shape cannot be shown on the map",
7
- "chelone.map.snap_failed": "The line could not be snapped to roads",
8
- "chelone.map.toolbar": "Map tools",
9
- "chelone.map.tool.draw_point": "Draw a point",
10
- "chelone.map.tool.draw_line": "Draw a line",
11
- "chelone.map.tool.draw_polygon": "Draw a polygon",
12
- "chelone.map.tool.draw_rectangle": "Draw a rectangle",
13
- "chelone.map.tool.draw_circle": "Draw a circle",
14
- "chelone.map.tool.edit": "Edit",
15
- "chelone.map.tool.move": "Move",
16
- "chelone.map.tool.delete": "Delete",
17
- "chelone.map.tool.cut": "Cut a hole",
18
- "chelone.map.search_placeholder": "Search for a place",
19
- "chelone.map.search_button": "Search",
20
- "chelone.map.searching": "Searching...",
21
- "chelone.map.no_results": "No results",
22
- "chelone.map.search_failed": "The search failed, try again shortly",
23
- "chelone.map.simplify.tolerance": "Tolerance: {value}",
24
- "chelone.map.simplify.default": "Default",
25
- "chelone.map.simplify.high": "High",
26
- "chelone.map.osm.subtract": "Subtract OpenStreetMap areas",
27
- "chelone.map.osm.add": "Add OpenStreetMap areas",
28
- "chelone.map.osm.no_sources": "No OpenStreetMap sources configured",
29
- "chelone.map.osm.no_polygon": "Draw a polygon first",
30
- "chelone.map.osm.no_match": "No matching OpenStreetMap area found",
31
- "chelone.map.osm.covered": "The polygon is already covered",
32
- "chelone.map.osm.subtracted": "Subtracted {area}",
33
- "chelone.map.osm.added": "Added {area}",
34
- "chelone.map.osm.would_split": "The result has more than one piece, so the polygon was left as it was",
35
- "chelone.map.osm.union_empty": "The union is empty",
36
- "chelone.map.osm.error": "OpenStreetMap request failed",
37
- "chelone.map.osm.rate_limited": "OpenStreetMap is rate limiting requests, try again shortly",
38
- "chelone.validation.latitude": "Must be a latitude between -90 and 90",
39
- "chelone.validation.longitude": "Must be a longitude between -180 and 180",
40
- "chelone.validation.minVertices": "Must have at least {min} vertices",
41
- "chelone.validation.maxVertices": "Must have {max} vertices or fewer",
42
- "chelone.validation.minArea": "Area {area} is below the minimum of {min}",
43
- "chelone.validation.selfIntersection": "Must not cross itself"
2
+ // src/geo/messages.ts
3
+ /**
4
+ * English text for the map add-on. Spread next to `defaultMessages`:
5
+ * `{ ...defaultMessages, ...mapMessages }`. Shares no key with it.
6
+ */
7
+ var mapMessages = defineMessages({
8
+ "chelone.map.load_failed": "The map could not be loaded",
9
+ "chelone.map.no_coordinates": "No coordinates",
10
+ "chelone.map.no_geometry": "No geometry",
11
+ "chelone.map.shape_not_shown": "Part of this shape cannot be shown on the map",
12
+ "chelone.map.snap_failed": "The line could not be snapped to roads",
13
+ "chelone.map.toolbar": "Map tools",
14
+ "chelone.map.tool.draw_point": "Draw a point",
15
+ "chelone.map.tool.draw_line": "Draw a line",
16
+ "chelone.map.tool.draw_polygon": "Draw a polygon",
17
+ "chelone.map.tool.draw_rectangle": "Draw a rectangle",
18
+ "chelone.map.tool.draw_circle": "Draw a circle",
19
+ "chelone.map.tool.edit": "Edit",
20
+ "chelone.map.tool.move": "Move",
21
+ "chelone.map.tool.delete": "Delete",
22
+ "chelone.map.tool.cut": "Cut a hole",
23
+ "chelone.map.search_placeholder": "Search for a place",
24
+ "chelone.map.search_button": "Search",
25
+ "chelone.map.searching": "Searching...",
26
+ "chelone.map.no_results": "No results",
27
+ "chelone.map.search_failed": "The search failed, try again shortly",
28
+ "chelone.map.simplify.tolerance": "Tolerance: {value}",
29
+ "chelone.map.simplify.default": "Default",
30
+ "chelone.map.simplify.high": "High",
31
+ "chelone.map.osm.subtract": "Subtract OpenStreetMap areas",
32
+ "chelone.map.osm.add": "Add OpenStreetMap areas",
33
+ "chelone.map.osm.no_sources": "No OpenStreetMap sources configured",
34
+ "chelone.map.osm.no_polygon": "Draw a polygon first",
35
+ "chelone.map.osm.no_match": "No matching OpenStreetMap area found",
36
+ "chelone.map.osm.covered": "The polygon is already covered",
37
+ "chelone.map.osm.subtracted": "Subtracted {area}",
38
+ "chelone.map.osm.added": "Added {area}",
39
+ "chelone.map.osm.would_split": "The result has more than one piece, so the polygon was left as it was",
40
+ "chelone.map.osm.union_empty": "The union is empty",
41
+ "chelone.map.osm.error": "OpenStreetMap request failed",
42
+ "chelone.map.osm.rate_limited": "OpenStreetMap is rate limiting requests, try again shortly",
43
+ "chelone.validation.latitude": "Must be a latitude between -90 and 90",
44
+ "chelone.validation.longitude": "Must be a longitude between -180 and 180",
45
+ "chelone.validation.minVertices": "Must have at least {min} vertices",
46
+ "chelone.validation.maxVertices": "Must have {max} vertices or fewer",
47
+ "chelone.validation.minArea": "Area {area} is below the minimum of {min}",
48
+ "chelone.validation.selfIntersection": "Must not cross itself"
44
49
  });
45
- export {
46
- mapMessages
47
- };
50
+ export { mapMessages };
@@ -1,87 +1,95 @@
1
+ import { isRecord } from "../guards.js";
1
2
  import { createThrottle } from "./throttle.js";
3
+ // src/geo/nominatim.ts
4
+ /** `Number(null)` and `Number("")` are 0; a missing coordinate must not be. */
2
5
  function toNumber(value) {
3
- if (typeof value === "number") return value;
4
- if (typeof value === "string" && value.trim() !== "") return Number(value);
5
- return Number.NaN;
6
+ if (typeof value === "number") return value;
7
+ if (typeof value === "string" && value.trim() !== "") return Number(value);
8
+ return NaN;
6
9
  }
7
10
  function toResult(hit) {
8
- if (typeof hit.display_name !== "string") return null;
9
- const lat = toNumber(hit.lat);
10
- const lng = toNumber(hit.lon);
11
- if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null;
12
- const result = { displayName: hit.display_name, lat, lng };
13
- if (Array.isArray(hit.boundingbox) && hit.boundingbox.length === 4) {
14
- const [minLat, maxLat, minLon, maxLon] = hit.boundingbox.map(toNumber);
15
- const bbox = [minLon, minLat, maxLon, maxLat];
16
- if (bbox.every(Number.isFinite)) result.bbox = bbox;
17
- }
18
- if (typeof hit.type === "string") result.type = hit.type;
19
- result.raw = hit;
20
- return result;
11
+ if (typeof hit.display_name !== "string") return null;
12
+ const lat = toNumber(hit.lat);
13
+ const lng = toNumber(hit.lon);
14
+ if (!Number.isFinite(lat) || !Number.isFinite(lng)) return null;
15
+ const result = {
16
+ displayName: hit.display_name,
17
+ lat,
18
+ lng
19
+ };
20
+ if (Array.isArray(hit.boundingbox) && hit.boundingbox.length === 4) {
21
+ const [minLat = NaN, maxLat = NaN, minLon = NaN, maxLon = NaN] = hit.boundingbox.map(toNumber);
22
+ const bbox = [
23
+ minLon,
24
+ minLat,
25
+ maxLon,
26
+ maxLat
27
+ ];
28
+ if (bbox.every(Number.isFinite)) result.bbox = bbox;
29
+ }
30
+ if (typeof hit.type === "string") result.type = hit.type;
31
+ result.raw = hit;
32
+ return result;
21
33
  }
22
34
  function createNominatimProvider(options) {
23
- const { endpoint } = options;
24
- if (typeof endpoint !== "string" || endpoint.trim() === "") {
25
- throw new TypeError(
26
- 'createNominatimProvider: the "endpoint" option is required'
27
- );
28
- }
29
- const base = endpoint.trim().replace(/\/+$/, "");
30
- const language = options.language ?? "en";
31
- const doFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
32
- const throttle = createThrottle({
33
- minIntervalMs: options.minIntervalMs ?? 1e3,
34
- now: options.now,
35
- sleep: options.sleep
36
- });
37
- const get = async (path, params, signal) => {
38
- await throttle.wait(signal);
39
- return doFetch(`${base}/${path}?${params}`, {
40
- headers: { "Accept-Language": language },
41
- signal
42
- });
43
- };
44
- return {
45
- name: "nominatim",
46
- cacheKey: `nominatim|${base}|${language}`,
47
- typeahead: options.typeahead ?? false,
48
- async search(query, searchOptions = {}) {
49
- const { signal, limit = 10, countryCodes, viewBox } = searchOptions;
50
- const params = new URLSearchParams({
51
- q: query,
52
- format: "json",
53
- limit: String(limit)
54
- });
55
- if (countryCodes?.length) {
56
- params.set("countrycodes", countryCodes.join(","));
57
- }
58
- if (viewBox) params.set("viewbox", viewBox.join(","));
59
- const response = await get("search", params, signal);
60
- if (!response.ok) throw new Error(`Nominatim search ${response.status}`);
61
- const hits = await response.json();
62
- if (!Array.isArray(hits)) return [];
63
- return hits.flatMap((hit) => toResult(hit) ?? []);
64
- },
65
- async reverse(lat, lng, reverseOptions = {}) {
66
- const { signal } = reverseOptions;
67
- const params = new URLSearchParams({
68
- lat: String(lat),
69
- lon: String(lng),
70
- format: "json"
71
- });
72
- try {
73
- const response = await get("reverse", params, signal);
74
- if (!response.ok) return null;
75
- const body = await response.json();
76
- if (!body || body.error) return null;
77
- return toResult(body);
78
- } catch (error) {
79
- if (signal?.aborted) throw error;
80
- return null;
81
- }
82
- }
83
- };
35
+ const { endpoint } = options;
36
+ if (typeof endpoint !== "string" || endpoint.trim() === "") throw new TypeError("createNominatimProvider: the \"endpoint\" option is required");
37
+ const base = endpoint.trim().replace(/\/+$/, "");
38
+ const language = options.language ?? "en";
39
+ const doFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
40
+ const throttle = createThrottle({
41
+ minIntervalMs: options.minIntervalMs ?? 1e3,
42
+ now: options.now,
43
+ sleep: options.sleep
44
+ });
45
+ const get = async (path, params, signal) => {
46
+ await throttle.wait(signal);
47
+ return doFetch(`${base}/${path}?${params}`, {
48
+ headers: { "Accept-Language": language },
49
+ signal
50
+ });
51
+ };
52
+ return {
53
+ name: "nominatim",
54
+ cacheKey: `nominatim|${base}|${language}`,
55
+ typeahead: options.typeahead ?? false,
56
+ async search(query, searchOptions = {}) {
57
+ const { signal, limit = 10, countryCodes, viewBox } = searchOptions;
58
+ const params = new URLSearchParams({
59
+ q: query,
60
+ format: "json",
61
+ limit: String(limit)
62
+ });
63
+ if (countryCodes?.length) params.set("countrycodes", countryCodes.join(","));
64
+ if (viewBox) params.set("viewbox", viewBox.join(","));
65
+ const response = await get("search", params, signal);
66
+ if (!response.ok) throw new Error(`Nominatim search ${response.status}`);
67
+ const hits = await response.json();
68
+ if (!Array.isArray(hits)) return [];
69
+ return hits.flatMap((hit) => {
70
+ if (!isRecord(hit)) return [];
71
+ const result = toResult(hit);
72
+ return result ? [result] : [];
73
+ });
74
+ },
75
+ async reverse(lat, lng, reverseOptions = {}) {
76
+ const { signal } = reverseOptions;
77
+ const params = new URLSearchParams({
78
+ lat: String(lat),
79
+ lon: String(lng),
80
+ format: "json"
81
+ });
82
+ try {
83
+ const response = await get("reverse", params, signal);
84
+ if (!response.ok) return null;
85
+ const body = await response.json();
86
+ if (!isRecord(body) || body.error) return null;
87
+ return toResult(body);
88
+ } catch (error) {
89
+ if (signal?.aborted) throw error;
90
+ return null;
91
+ }
92
+ }
93
+ };
84
94
  }
85
- export {
86
- createNominatimProvider
87
- };
95
+ export { createNominatimProvider };
@@ -1,4 +1,5 @@
1
- import type { GeoBBox } from "./geojson.js";
1
+ import type { JsonObject } from "../guards.js";
2
+ import type { LngLatBox } from "./bounds.js";
2
3
  import type { OsmTagFilter, OsmTagInput } from "./osm-tags.js";
3
4
  export type OsmElementType = "way" | "relation";
4
5
  export interface OsmPreset {
@@ -74,7 +75,7 @@ export type OsmPresetName = keyof typeof OSM_PRESETS;
74
75
  * The first of `names` with a filter that accepts the feature's tags, so an
75
76
  * adapter can read its `bufferLinesMeters`; `null` when none does.
76
77
  */
77
- export declare function findPresetForTags(tags: Readonly<Record<string, unknown>>, names: readonly OsmPresetName[]): OsmPreset | null;
78
+ export declare function findPresetForTags(tags: Readonly<JsonObject>, names: readonly OsmPresetName[]): OsmPreset | null;
78
79
  export interface OverpassQuerySources {
79
80
  presets?: readonly OsmPresetName[];
80
81
  tags?: readonly OsmTagInput[];
@@ -83,4 +84,4 @@ export interface OverpassQuerySources {
83
84
  * Overpass QL for every feature matching a preset or a tag inside `bbox`
84
85
  * (`[west, south, east, north]`, written south, west, north, east).
85
86
  */
86
- export declare function buildOverpassQuery(sources: OverpassQuerySources, bbox: GeoBBox): string;
87
+ export declare function buildOverpassQuery(sources: OverpassQuerySources, bbox: LngLatBox): string;
@@ -1,98 +1,120 @@
1
1
  import { tagToFilter } from "./osm-tags.js";
2
- const anyOf = (key) => ({ filters: [{ key, any: true }] });
3
- const OSM_PRESETS = {
4
- water: {
5
- filters: [
6
- { key: "natural", values: ["water", "bay", "strait"] },
7
- { key: "waterway", any: true }
8
- ]
9
- },
10
- buildings: anyOf("building"),
11
- forest: {
12
- filters: [
13
- { key: "natural", values: ["wood", "forest"] },
14
- { key: "landuse", value: "forest" }
15
- ]
16
- },
17
- roads: {
18
- filters: [
19
- { key: "highway", values: ["motorway", "trunk", "primary", "secondary"] }
20
- ],
21
- bufferLinesMeters: 15,
22
- elementTypes: ["way"]
23
- },
24
- natural: anyOf("natural"),
25
- building: anyOf("building"),
26
- landuse: anyOf("landuse"),
27
- amenity: anyOf("amenity"),
28
- leisure: anyOf("leisure"),
29
- boundary: anyOf("boundary"),
30
- place: anyOf("place"),
31
- man_made: anyOf("man_made"),
32
- shop: anyOf("shop"),
33
- tourism: anyOf("tourism"),
34
- historic: anyOf("historic"),
35
- highway: anyOf("highway"),
36
- railway: { ...anyOf("railway"), bufferLinesMeters: 5, elementTypes: ["way"] },
37
- waterway: { ...anyOf("waterway"), bufferLinesMeters: 5 },
38
- barrier: { ...anyOf("barrier"), bufferLinesMeters: 3 },
39
- power: { ...anyOf("power"), bufferLinesMeters: 3 }
2
+ // src/geo/osm-presets.ts
3
+ var anyOf = (key) => ({ filters: [{
4
+ key,
5
+ any: true
6
+ }] });
7
+ var OSM_PRESETS = {
8
+ water: { filters: [{
9
+ key: "natural",
10
+ values: [
11
+ "water",
12
+ "bay",
13
+ "strait"
14
+ ]
15
+ }, {
16
+ key: "waterway",
17
+ any: true
18
+ }] },
19
+ buildings: anyOf("building"),
20
+ forest: { filters: [{
21
+ key: "natural",
22
+ values: ["wood", "forest"]
23
+ }, {
24
+ key: "landuse",
25
+ value: "forest"
26
+ }] },
27
+ roads: {
28
+ filters: [{
29
+ key: "highway",
30
+ values: [
31
+ "motorway",
32
+ "trunk",
33
+ "primary",
34
+ "secondary"
35
+ ]
36
+ }],
37
+ bufferLinesMeters: 15,
38
+ elementTypes: ["way"]
39
+ },
40
+ natural: anyOf("natural"),
41
+ building: anyOf("building"),
42
+ landuse: anyOf("landuse"),
43
+ amenity: anyOf("amenity"),
44
+ leisure: anyOf("leisure"),
45
+ boundary: anyOf("boundary"),
46
+ place: anyOf("place"),
47
+ man_made: anyOf("man_made"),
48
+ shop: anyOf("shop"),
49
+ tourism: anyOf("tourism"),
50
+ historic: anyOf("historic"),
51
+ highway: anyOf("highway"),
52
+ railway: {
53
+ ...anyOf("railway"),
54
+ bufferLinesMeters: 5,
55
+ elementTypes: ["way"]
56
+ },
57
+ waterway: {
58
+ ...anyOf("waterway"),
59
+ bufferLinesMeters: 5
60
+ },
61
+ barrier: {
62
+ ...anyOf("barrier"),
63
+ bufferLinesMeters: 3
64
+ },
65
+ power: {
66
+ ...anyOf("power"),
67
+ bufferLinesMeters: 3
68
+ }
40
69
  };
41
70
  function accepts(filter, tags) {
42
- const raw = tags[filter.key];
43
- if (raw === void 0 || raw === null) return false;
44
- if ("any" in filter) return true;
45
- const actual = String(raw);
46
- return "values" in filter ? filter.values.includes(actual) : filter.value === actual;
71
+ const raw = tags[filter.key];
72
+ if (raw === void 0 || raw === null) return false;
73
+ if ("any" in filter) return true;
74
+ const actual = String(raw);
75
+ return "values" in filter ? filter.values.includes(actual) : filter.value === actual;
47
76
  }
77
+ /**
78
+ * The first of `names` with a filter that accepts the feature's tags, so an
79
+ * adapter can read its `bufferLinesMeters`; `null` when none does.
80
+ */
48
81
  function findPresetForTags(tags, names) {
49
- for (const name of names) {
50
- const preset = OSM_PRESETS[name];
51
- if (preset.filters.some((filter) => accepts(filter, tags))) return preset;
52
- }
53
- return null;
82
+ for (const name of names) {
83
+ const preset = OSM_PRESETS[name];
84
+ if (preset.filters.some((filter) => accepts(filter, tags))) return preset;
85
+ }
86
+ return null;
54
87
  }
88
+ /** Escapes for an Overpass string; a newline cannot be escaped, so it throws. */
55
89
  function quote(text) {
56
- if (/[\r\n]/.test(text)) {
57
- throw new TypeError("An OSM tag cannot contain a newline");
58
- }
59
- return `"${text.replace(/[\\"]/g, "\\$&")}"`;
90
+ if (/[\r\n]/.test(text)) throw new TypeError("An OSM tag cannot contain a newline");
91
+ return `"${text.replace(/[\\"]/g, "\\$&")}"`;
60
92
  }
61
- const regexLiteral = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
93
+ /** A regex alternative must match its text literally. */
94
+ var regexLiteral = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
62
95
  function filterToSelector(filter) {
63
- const key = quote(filter.key);
64
- if ("any" in filter) return `[${key}]`;
65
- if ("values" in filter) {
66
- const alternatives = filter.values.map(regexLiteral).join("|");
67
- return `[${key}~${quote(`^(${alternatives})$`)}]`;
68
- }
69
- return `[${key}=${quote(filter.value)}]`;
96
+ const key = quote(filter.key);
97
+ if ("any" in filter) return `[${key}]`;
98
+ if ("values" in filter) return `[${key}~${quote(`^(${filter.values.map(regexLiteral).join("|")})$`)}]`;
99
+ return `[${key}=${quote(filter.value)}]`;
70
100
  }
101
+ /**
102
+ * Overpass QL for every feature matching a preset or a tag inside `bbox`
103
+ * (`[west, south, east, north]`, written south, west, north, east).
104
+ */
71
105
  function buildOverpassQuery(sources, bbox) {
72
- const [west, south, east, north] = bbox;
73
- const area = `(${south},${west},${north},${east})`;
74
- const clauses = [];
75
- const add = (filter, types) => {
76
- const selector = filterToSelector(filter);
77
- for (const type of types) clauses.push(` ${type}${selector}${area};`);
78
- };
79
- for (const name of sources.presets ?? []) {
80
- const preset = OSM_PRESETS[name];
81
- for (const filter of preset.filters) {
82
- add(filter, preset.elementTypes ?? ["way", "relation"]);
83
- }
84
- }
85
- for (const tag of sources.tags ?? [])
86
- add(tagToFilter(tag), ["way", "relation"]);
87
- const union = clauses.length ? `(
88
- ${clauses.join("\n")}
89
- );` : "();";
90
- return `[out:json][timeout:25];
91
- ${union}
92
- out geom;`;
106
+ const [west, south, east, north] = bbox;
107
+ const area = `(${south},${west},${north},${east})`;
108
+ const clauses = [];
109
+ const add = (filter, types) => {
110
+ const selector = filterToSelector(filter);
111
+ for (const type of types) clauses.push(` ${type}${selector}${area};`);
112
+ };
113
+ for (const name of sources.presets ?? []) {
114
+ const preset = OSM_PRESETS[name];
115
+ for (const filter of preset.filters) add(filter, preset.elementTypes ?? ["way", "relation"]);
116
+ }
117
+ for (const tag of sources.tags ?? []) add(tagToFilter(tag), ["way", "relation"]);
118
+ return `[out:json][timeout:25];\n${clauses.length ? `(\n${clauses.join("\n")}\n);` : "();"}\nout geom;`;
93
119
  }
94
- export {
95
- OSM_PRESETS,
96
- buildOverpassQuery,
97
- findPresetForTags
98
- };
120
+ export { OSM_PRESETS, buildOverpassQuery, findPresetForTags };
@@ -1,14 +1,16 @@
1
+ // src/geo/osm-tags.ts
2
+ /** `natural=water` is `{ key, value }`; `building=*` matches any value. */
1
3
  function tagToFilter(tag) {
2
- const separator = tag.indexOf("=");
3
- const key = separator < 0 ? "" : tag.slice(0, separator).trim();
4
- const value = separator < 0 ? "" : tag.slice(separator + 1).trim();
5
- if (!key || !value) {
6
- throw new TypeError(
7
- `Invalid OSM tag "${tag}": expected "key=value" or "key=*"`
8
- );
9
- }
10
- return value === "*" ? { key, any: true } : { key, value };
4
+ const separator = tag.indexOf("=");
5
+ const key = separator < 0 ? "" : tag.slice(0, separator).trim();
6
+ const value = separator < 0 ? "" : tag.slice(separator + 1).trim();
7
+ if (!key || !value) throw new TypeError(`Invalid OSM tag "${tag}": expected "key=value" or "key=*"`);
8
+ return value === "*" ? {
9
+ key,
10
+ any: true
11
+ } : {
12
+ key,
13
+ value
14
+ };
11
15
  }
12
- export {
13
- tagToFilter
14
- };
16
+ export { tagToFilter };