@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,74 +1,104 @@
1
- const MAX_LONGITUDE = 540;
1
+ // src/geo/bounds.ts
2
+ var MAX_LONGITUDE = 540;
3
+ /**
4
+ * The `[west, east]` arc over `longitudes`, which wraps (west greater than
5
+ * east) when going round the antimeridian is narrower. Left plain when a value
6
+ * is no longitude, such as a projected coordinate.
7
+ */
2
8
  function longitudeArc(longitudes, west, east, mode) {
3
- if (east <= 180 && (mode === "explicit" || east - west <= 180)) {
4
- return [west, east];
5
- }
6
- const wrapped = /* @__PURE__ */ new Set();
7
- for (const longitude of longitudes) {
8
- if (longitude < -180 || longitude > MAX_LONGITUDE) return [west, east];
9
- wrapped.add(longitude > 180 ? longitude - 360 : longitude);
10
- }
11
- const sorted = [...wrapped].sort((x, y) => x - y);
12
- const min = sorted[0] ?? west;
13
- const max = sorted[sorted.length - 1] ?? east;
14
- let widest = 360 - (max - min);
15
- if (widest <= 0) return [min, max];
16
- let arc = [min, max];
17
- sorted.forEach((longitude, i) => {
18
- const gap = longitude - (sorted[i - 1] ?? longitude);
19
- if (gap > widest) {
20
- widest = gap;
21
- arc = [longitude, sorted[i - 1] ?? longitude];
22
- }
23
- });
24
- return arc;
9
+ if (east <= 180 && (mode === "explicit" || east - west <= 180)) return [west, east];
10
+ const wrapped = /* @__PURE__ */ new Set();
11
+ for (const longitude of longitudes) {
12
+ if (longitude < -180 || longitude > MAX_LONGITUDE) return [west, east];
13
+ wrapped.add(longitude > 180 ? longitude - 360 : longitude);
14
+ }
15
+ const sorted = [...wrapped].sort((x, y) => x - y);
16
+ const min = sorted[0] ?? west;
17
+ const max = sorted[sorted.length - 1] ?? east;
18
+ let widest = 360 - (max - min);
19
+ if (widest <= 0) return [min, max];
20
+ let arc = [min, max];
21
+ sorted.forEach((longitude, i) => {
22
+ const gap = longitude - (sorted[i - 1] ?? longitude);
23
+ if (gap > widest) {
24
+ widest = gap;
25
+ arc = [longitude, sorted[i - 1] ?? longitude];
26
+ }
27
+ });
28
+ return arc;
25
29
  }
30
+ /**
31
+ * `[west, south, east, north]` over every position of a geometry, Feature or
32
+ * FeatureCollection; `null` when there is none. Altitude is ignored. When the
33
+ * longitudes are narrower going round the antimeridian, west is greater than
34
+ * east.
35
+ */
26
36
  function geoBounds(value, mode = "narrowest") {
27
- let bounds = null;
28
- const longitudes = /* @__PURE__ */ new Set();
29
- const extend = (longitude, latitude) => {
30
- longitudes.add(longitude);
31
- if (!bounds) {
32
- bounds = [longitude, latitude, longitude, latitude];
33
- return;
34
- }
35
- bounds[0] = Math.min(bounds[0], longitude);
36
- bounds[1] = Math.min(bounds[1], latitude);
37
- bounds[2] = Math.max(bounds[2], longitude);
38
- bounds[3] = Math.max(bounds[3], latitude);
39
- };
40
- const walkCoordinates = (node) => {
41
- if (!Array.isArray(node)) return;
42
- if (typeof node[0] === "number") {
43
- extend(node[0], node[1]);
44
- return;
45
- }
46
- for (const child of node) walkCoordinates(child);
47
- };
48
- const walk = (node) => {
49
- if (!node) return;
50
- if (node.type === "FeatureCollection") {
51
- for (const feature of node.features) walk(feature);
52
- } else if (node.type === "Feature") {
53
- walk(node.geometry);
54
- } else if (node.type === "GeometryCollection") {
55
- for (const geometry of node.geometries) walk(geometry);
56
- } else {
57
- walkCoordinates(node.coordinates);
58
- }
59
- };
60
- walk(value);
61
- if (!bounds) return null;
62
- const [west, south, east, north] = bounds;
63
- const [arcWest, arcEast] = longitudeArc(longitudes, west, east, mode);
64
- return [arcWest, south, arcEast, north];
37
+ const state = { box: null };
38
+ const longitudes = /* @__PURE__ */ new Set();
39
+ const extend = (position) => {
40
+ const [longitude, latitude] = position;
41
+ if (longitude === void 0 || latitude === void 0) return;
42
+ longitudes.add(longitude);
43
+ const box = state.box;
44
+ if (!box) {
45
+ state.box = [
46
+ longitude,
47
+ latitude,
48
+ longitude,
49
+ latitude
50
+ ];
51
+ return;
52
+ }
53
+ box[0] = Math.min(box[0], longitude);
54
+ box[1] = Math.min(box[1], latitude);
55
+ box[2] = Math.max(box[2], longitude);
56
+ box[3] = Math.max(box[3], latitude);
57
+ };
58
+ const walk = (node) => {
59
+ if (!node) return;
60
+ switch (node.type) {
61
+ case "FeatureCollection":
62
+ for (const feature of node.features) walk(feature);
63
+ break;
64
+ case "Feature":
65
+ walk(node.geometry);
66
+ break;
67
+ case "GeometryCollection":
68
+ for (const geometry of node.geometries) walk(geometry);
69
+ break;
70
+ case "Point":
71
+ extend(node.coordinates);
72
+ break;
73
+ case "MultiPoint":
74
+ case "LineString":
75
+ for (const position of node.coordinates) extend(position);
76
+ break;
77
+ case "MultiLineString":
78
+ case "Polygon":
79
+ for (const line of node.coordinates) for (const position of line) extend(position);
80
+ break;
81
+ case "MultiPolygon": for (const polygon of node.coordinates) for (const ring of polygon) for (const position of ring) extend(position);
82
+ }
83
+ };
84
+ walk(value);
85
+ if (!state.box) return null;
86
+ const [west, south, east, north] = state.box;
87
+ const [arcWest, arcEast] = longitudeArc(longitudes, west, east, mode);
88
+ return [
89
+ arcWest,
90
+ south,
91
+ arcEast,
92
+ north
93
+ ];
65
94
  }
95
+ /**
96
+ * Whether every corner is a valid longitude and latitude. MapLibre throws on a
97
+ * latitude outside ±90, so a box failing this is never fitted.
98
+ */
66
99
  function isLngLatBox(box) {
67
- if (!box) return false;
68
- const [west = 0, south = 0, east = 0, north = 0] = box;
69
- return Math.abs(west) <= 180 && Math.abs(east) <= 180 && Math.abs(south) <= 90 && Math.abs(north) <= 90;
100
+ if (!box) return false;
101
+ const [west = 0, south = 0, east = 0, north = 0] = box;
102
+ return Math.abs(west) <= 180 && Math.abs(east) <= 180 && Math.abs(south) <= 90 && Math.abs(north) <= 90;
70
103
  }
71
- export {
72
- geoBounds,
73
- isLngLatBox
74
- };
104
+ export { geoBounds, isLngLatBox };
@@ -1,121 +1,116 @@
1
1
  import { isEmpty } from "../form/validators.js";
2
+ import { isRecord } from "../guards.js";
2
3
  import { bboxToPolygon } from "./bbox.js";
3
4
  import { isGeoJsonValue } from "./geojson.js";
4
5
  import { geoAreaM2, hasSelfIntersection } from "./geometry-math.js";
6
+ // src/geo/constraints.ts
7
+ /**
8
+ * The geometry of each member of a geometry, Feature or FeatureCollection. A
9
+ * BBox is the rectangle it covers.
10
+ */
5
11
  function members(value) {
6
- const box = bboxToPolygon(value);
7
- if (box) return [box];
8
- if (!isGeoJsonValue(value)) return [];
9
- if (value.type === "FeatureCollection") {
10
- return value.features.flatMap((feature) => members(feature));
11
- }
12
- if (value.type === "Feature") return value.geometry ? [value.geometry] : [];
13
- return [value];
12
+ const box = bboxToPolygon(value);
13
+ if (box) return [box];
14
+ if (!isGeoJsonValue(value)) return [];
15
+ if (value.type === "FeatureCollection") return value.features.flatMap((feature) => members(feature));
16
+ if (value.type === "Feature") return value.geometry ? [value.geometry] : [];
17
+ return [value];
14
18
  }
15
19
  function ringVertices(ring) {
16
- const first = ring[0];
17
- const last = ring[ring.length - 1];
18
- const closed = ring.length > 1 && first !== void 0 && last !== void 0 && first[0] === last[0] && first[1] === last[1];
19
- return closed ? ring.length - 1 : ring.length;
20
+ const first = ring[0];
21
+ const last = ring[ring.length - 1];
22
+ return ring.length > 1 && first !== void 0 && last !== void 0 && first[0] === last[0] && first[1] === last[1] ? ring.length - 1 : ring.length;
20
23
  }
21
24
  function geometryVertices(geometry) {
22
- switch (geometry.type) {
23
- case "Point":
24
- return 1;
25
- case "MultiPoint":
26
- case "LineString":
27
- return geometry.coordinates.length;
28
- case "MultiLineString":
29
- return geometry.coordinates.reduce((sum, line) => sum + line.length, 0);
30
- case "Polygon":
31
- return geometry.coordinates.reduce(
32
- (sum, ring) => sum + ringVertices(ring),
33
- 0
34
- );
35
- case "MultiPolygon":
36
- return geometry.coordinates.reduce(
37
- (sum, polygon) => sum + polygon.reduce((inner, ring) => inner + ringVertices(ring), 0),
38
- 0
39
- );
40
- case "GeometryCollection":
41
- return geometry.geometries.reduce(
42
- (sum, part) => sum + geometryVertices(part),
43
- 0
44
- );
45
- }
25
+ switch (geometry.type) {
26
+ case "Point": return 1;
27
+ case "MultiPoint":
28
+ case "LineString": return geometry.coordinates.length;
29
+ case "MultiLineString": return geometry.coordinates.reduce((sum, line) => sum + line.length, 0);
30
+ case "Polygon": return geometry.coordinates.reduce((sum, ring) => sum + ringVertices(ring), 0);
31
+ case "MultiPolygon": return geometry.coordinates.reduce((sum, polygon) => sum + polygon.reduce((inner, ring) => inner + ringVertices(ring), 0), 0);
32
+ case "GeometryCollection": return geometry.geometries.reduce((sum, part) => sum + geometryVertices(part), 0);
33
+ }
46
34
  }
35
+ /**
36
+ * Vertices of a geometry, Feature, FeatureCollection or BBox. A polygon ring counts
37
+ * its distinct vertices, so a triangle is 3. `0` for anything else.
38
+ */
47
39
  function countVertices(value) {
48
- return members(value).reduce(
49
- (sum, geometry) => sum + geometryVertices(geometry),
50
- 0
51
- );
40
+ return members(value).reduce((sum, geometry) => sum + geometryVertices(geometry), 0);
52
41
  }
42
+ /** Area of a geometry, with the parts of a GeometryCollection added up. */
53
43
  function areaM2(geometry) {
54
- return geometry.type === "GeometryCollection" ? geometry.geometries.reduce((sum, part) => sum + areaM2(part), 0) : geoAreaM2(geometry);
44
+ return geometry.type === "GeometryCollection" ? geometry.geometries.reduce((sum, part) => sum + areaM2(part), 0) : geoAreaM2(geometry);
55
45
  }
56
46
  function hasArea(geometry) {
57
- switch (geometry.type) {
58
- case "Polygon":
59
- case "MultiPolygon":
60
- return true;
61
- case "GeometryCollection":
62
- return geometry.geometries.some(hasArea);
63
- default:
64
- return false;
65
- }
47
+ switch (geometry.type) {
48
+ case "Polygon":
49
+ case "MultiPolygon": return true;
50
+ case "GeometryCollection": return geometry.geometries.some(hasArea);
51
+ default: return false;
52
+ }
66
53
  }
54
+ /**
55
+ * A Feature or FeatureCollection that `isGeoJsonValue` refuses. Its limits
56
+ * cannot be checked, so a constraint fails it instead of passing it. Any other
57
+ * value that is not GeoJSON is left to other validators.
58
+ */
67
59
  function isUnreadable(value) {
68
- if (typeof value !== "object" || value === null) return false;
69
- const { type } = value;
70
- return (type === "Feature" || type === "FeatureCollection") && !isGeoJsonValue(value);
60
+ return isRecord(value) && (value.type === "Feature" || value.type === "FeatureCollection") && !isGeoJsonValue(value);
71
61
  }
62
+ /**
63
+ * Each constraint skips empty values and a value that is not GeoJSON, fails a
64
+ * Feature or FeatureCollection it cannot read, and fails when any member of a
65
+ * FeatureCollection fails.
66
+ */
72
67
  function constraint(fails, failure) {
73
- return (value) => {
74
- if (isEmpty(value)) return void 0;
75
- if (isUnreadable(value)) return failure();
76
- return members(value).some(fails) ? failure() : void 0;
77
- };
68
+ return (value) => {
69
+ if (isEmpty(value)) return void 0;
70
+ if (isUnreadable(value)) return failure();
71
+ return members(value).some(fails) ? failure() : void 0;
72
+ };
78
73
  }
79
74
  function minVertices(min) {
80
- return constraint(
81
- (geometry) => geometryVertices(geometry) < min,
82
- () => ({ message: "chelone.validation.minVertices", args: { min } })
83
- );
75
+ return constraint((geometry) => geometryVertices(geometry) < min, () => ({
76
+ message: "chelone.validation.minVertices",
77
+ args: { min }
78
+ }));
84
79
  }
85
80
  function maxVertices(max) {
86
- return constraint(
87
- (geometry) => geometryVertices(geometry) > max,
88
- () => ({ message: "chelone.validation.maxVertices", args: { max } })
89
- );
81
+ return constraint((geometry) => geometryVertices(geometry) > max, () => ({
82
+ message: "chelone.validation.maxVertices",
83
+ args: { max }
84
+ }));
90
85
  }
86
+ /**
87
+ * Fails a member with a polygon whose area is under `min` square metres. A
88
+ * member with no polygon in it has no area and passes. `args.area` is rounded.
89
+ */
91
90
  function minAreaM2(min) {
92
- return (value) => {
93
- if (isEmpty(value)) return void 0;
94
- if (isUnreadable(value)) {
95
- return { message: "chelone.validation.minArea", args: { min, area: 0 } };
96
- }
97
- for (const geometry of members(value)) {
98
- if (!hasArea(geometry)) continue;
99
- const area = areaM2(geometry);
100
- if (area < min) {
101
- return {
102
- message: "chelone.validation.minArea",
103
- args: { min, area: Math.round(area) }
104
- };
105
- }
106
- }
107
- return void 0;
108
- };
91
+ return (value) => {
92
+ if (isEmpty(value)) return void 0;
93
+ if (isUnreadable(value)) return {
94
+ message: "chelone.validation.minArea",
95
+ args: {
96
+ min,
97
+ area: 0
98
+ }
99
+ };
100
+ for (const geometry of members(value)) {
101
+ if (!hasArea(geometry)) continue;
102
+ const area = areaM2(geometry);
103
+ if (area < min) return {
104
+ message: "chelone.validation.minArea",
105
+ args: {
106
+ min,
107
+ area: Math.round(area)
108
+ }
109
+ };
110
+ }
111
+ };
109
112
  }
110
113
  function noSelfIntersection() {
111
- return constraint(hasSelfIntersection, () => ({
112
- message: "chelone.validation.selfIntersection"
113
- }));
114
+ return constraint(hasSelfIntersection, () => ({ message: "chelone.validation.selfIntersection" }));
114
115
  }
115
- export {
116
- countVertices,
117
- maxVertices,
118
- minAreaM2,
119
- minVertices,
120
- noSelfIntersection
121
- };
116
+ export { countVertices, maxVertices, minAreaM2, minVertices, noSelfIntersection };
@@ -1,4 +1,14 @@
1
1
  import type { ValidationMessage, Validator } from "../form/validators.js";
2
+ /** How far north or south a latitude goes. */
3
+ export declare const LATITUDE_LIMIT = 90;
4
+ /** How far east or west a longitude goes. */
5
+ export declare const LONGITUDE_LIMIT = 180;
6
+ /**
7
+ * A longitude from a repeated copy of the world, back into -180..180. MapLibre
8
+ * tiles the world side by side, so a click east of the last copy reports a
9
+ * longitude past 180, by any number of turns.
10
+ */
11
+ export declare function wrapLongitude(longitude: number): number;
2
12
  /** A finite number from a number or a numeric string, else `null`. `0` is valid. */
3
13
  export declare function toCoordinate(value: unknown): number | null;
4
14
  /** Errors outside -90..90 (or on a non-number). Skips empty values. */
@@ -1,32 +1,53 @@
1
1
  import { isEmpty } from "../form/validators.js";
2
- const NUMERIC = /^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i;
2
+ // src/geo/coordinates.ts
3
+ var NUMERIC = /^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i;
4
+ /** How far north or south a latitude goes. */
5
+ var LATITUDE_LIMIT = 90;
6
+ /** How far east or west a longitude goes. */
7
+ var LONGITUDE_LIMIT = 180;
8
+ /**
9
+ * A longitude from a repeated copy of the world, back into -180..180. MapLibre
10
+ * tiles the world side by side, so a click east of the last copy reports a
11
+ * longitude past 180, by any number of turns.
12
+ */
13
+ function wrapLongitude(longitude) {
14
+ if (Math.abs(longitude) <= 180) return longitude;
15
+ return ((longitude + 180) % 360 + 360) % 360 - 180;
16
+ }
17
+ /** A finite number from a number or a numeric string, else `null`. `0` is valid. */
3
18
  function toCoordinate(value) {
4
- if (typeof value === "number") return Number.isFinite(value) ? value : null;
5
- if (typeof value !== "string") return null;
6
- const text = value.trim();
7
- if (!NUMERIC.test(text)) return null;
8
- const parsed = Number(text);
9
- return Number.isFinite(parsed) ? parsed : null;
19
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
20
+ if (typeof value !== "string") return null;
21
+ const text = value.trim();
22
+ if (!NUMERIC.test(text)) return null;
23
+ const parsed = Number(text);
24
+ return Number.isFinite(parsed) ? parsed : null;
10
25
  }
11
26
  function rangeValidator(min, max, message) {
12
- return (value, values) => {
13
- if (isEmpty(value)) return void 0;
14
- const coordinate = toCoordinate(value);
15
- if (coordinate !== null && coordinate >= min && coordinate <= max) {
16
- return void 0;
17
- }
18
- const args = { min, max };
19
- return typeof message === "function" ? message({ args, value, values }) : { message, args };
20
- };
27
+ return (value, values) => {
28
+ if (isEmpty(value)) return void 0;
29
+ const coordinate = toCoordinate(value);
30
+ if (coordinate !== null && coordinate >= min && coordinate <= max) return;
31
+ const args = {
32
+ min,
33
+ max
34
+ };
35
+ return typeof message === "function" ? message({
36
+ args,
37
+ value,
38
+ values
39
+ }) : {
40
+ message,
41
+ args
42
+ };
43
+ };
21
44
  }
45
+ /** Errors outside -90..90 (or on a non-number). Skips empty values. */
22
46
  function latitude(message = "chelone.validation.latitude") {
23
- return rangeValidator(-90, 90, message);
47
+ return rangeValidator(-90, 90, message);
24
48
  }
49
+ /** Errors outside -180..180 (or on a non-number). Skips empty values. */
25
50
  function longitude(message = "chelone.validation.longitude") {
26
- return rangeValidator(-180, 180, message);
51
+ return rangeValidator(-180, 180, message);
27
52
  }
28
- export {
29
- latitude,
30
- longitude,
31
- toCoordinate
32
- };
53
+ export { LATITUDE_LIMIT, LONGITUDE_LIMIT, latitude, longitude, toCoordinate, wrapLongitude };
@@ -1,5 +1,11 @@
1
- import type { GeoFeature } from "./geojson.js";
1
+ import type { Feature, Geometry, LineString, Point, Polygon } from "geojson";
2
+ import { type JsonObject } from "../guards.js";
2
3
  import type { ShapeStyle } from "./map-layers.js";
4
+ type EditorGeometry = Point | LineString | Polygon;
5
+ /** A feature as the editor takes it: a drawable geometry, JSON properties and an id. */
6
+ export type EditorFeature = Feature<EditorGeometry, JsonObject> & {
7
+ id: string | number;
8
+ };
3
9
  export declare const EDITOR_PROPERTIES: {
4
10
  readonly mode: "mode";
5
11
  readonly currentlyDrawing: "currentlyDrawing";
@@ -28,25 +34,19 @@ export declare const EDITOR_PROPERTIES: {
28
34
  * decimals, cut to longitude and latitude, and wrapped. The source is not
29
35
  * rewritten.
30
36
  */
31
- export declare function toEditorFeatures(features: GeoFeature[], getId: () => string | number): GeoFeature[];
37
+ export declare function toEditorFeatures(features: Feature<Geometry | null, JsonObject | null>[], getId: () => string | number): EditorFeature[];
32
38
  /** What `getSnapshot()` of the editor returns, as far as core reads it. */
33
- export interface EditorSnapshotFeature {
34
- type?: "Feature";
35
- id?: string | number;
36
- geometry: {
37
- type: string;
38
- coordinates?: unknown;
39
- } | null;
40
- properties: object | null;
41
- }
39
+ export type EditorSnapshotFeature = Feature<EditorGeometry, JsonObject>;
42
40
  /**
43
41
  * The user's features in an editor snapshot: its helper features (selection
44
42
  * points, midpoints, closing points, snapping points and a feature still being
45
43
  * drawn) are dropped, and `id` and `properties` come back from `chelone:id`
46
44
  * and `chelone:properties`. A feature the editor made has no `id`, and keeps
47
- * its properties but for the editor's own.
45
+ * its properties but for the editor's own. A feature with a coordinate that is
46
+ * not a finite number, or a `chelone:properties` that is not a JSON object, is
47
+ * left out.
48
48
  */
49
- export declare function fromEditorFeatures(snapshot: readonly EditorSnapshotFeature[]): GeoFeature[];
49
+ export declare function fromEditorFeatures(snapshot: readonly EditorSnapshotFeature[]): Feature<Geometry | null, JsonObject | null>[];
50
50
  type EditorModeStyle = Record<string, string | number>;
51
51
  /** The `styles` option of each drawing mode, by mode name. */
52
52
  export type EditorStyles = Record<"point" | "linestring" | "polygon" | "rectangle" | "circle" | "render" | "select", EditorModeStyle>;