@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,4 +1,7 @@
1
+ import type { StandardSchemaV1, StandardSchemaV1Issue } from "@tanstack/form-core";
2
+ import type { TranslateParamValues } from "../i18n/messages.js";
1
3
  import type { TranslatableMessage } from "./form-controller.js";
4
+ import { type ChoiceValueOf } from "./input-values.js";
2
5
  /**
3
6
  * What a validator may return on failure: a plain string (an i18n key or
4
7
  * pre-translated text) or a translatable payload with interpolation args.
@@ -8,35 +11,62 @@ import type { TranslatableMessage } from "./form-controller.js";
8
11
  export type ValidationErrorMessage = string | TranslatableMessage;
9
12
  /** A validator's verdict: nullish means the value passes. */
10
13
  export type ValidatorResult = ValidationErrorMessage | null | undefined;
14
+ /**
15
+ * The values ra's skip-empty gate lets into a validator's value slot next to
16
+ * the field's own type: the holes, and an unset multi-input's `[]`. Every
17
+ * built-in except `required` stands down on them, so a validator written for a
18
+ * `string` field is still legal on a field whose value is momentarily empty.
19
+ */
20
+ export type EmptyValue = null | undefined | readonly unknown[];
21
+ /**
22
+ * The third validator argument (ra's `props` slot): a field binding always
23
+ * fills `source` with the final source path and merges its `validatorMeta`
24
+ * (input props in the React layer) in. `source` is optional because a validator
25
+ * is also a plain function an app can call with no field behind it. The open
26
+ * bag is the boundary — Chelone cannot know what an input passes there.
27
+ */
28
+ export interface FieldValidatorMeta {
29
+ /** The FINAL field path the value came from, when a binding ran the stack. */
30
+ source?: string;
31
+ [key: string]: unknown;
32
+ }
11
33
  /**
12
34
  * The ra validator contract: `(value, allValues, meta)`. `meta` carries the
13
- * final source path plus whatever the binding merged in (`validatorMeta` —
14
- * the React layer passes input props there). Sync or async.
35
+ * final source path plus whatever the binding merged in (`validatorMeta` — the
36
+ * React layer passes input props there); it is optional so a validator that
37
+ * reads only the value can say only the value. Sync or async.
38
+ *
39
+ * Generic in the value and the values it is handed: `Validator<string, Post>`
40
+ * is a validator for a `Post`'s `string` field (its value slot also holds
41
+ * {@link EmptyValue}), and one written for the widest pair (`Validator`, the
42
+ * default) fits any field.
15
43
  */
16
- export type Validator = (value: unknown, values: unknown, meta: unknown) => ValidatorResult | Promise<ValidatorResult>;
44
+ export type Validator<TValue = unknown, TValues = unknown> = (value: TValue | EmptyValue, values: TValues, meta?: FieldValidatorMeta) => ValidatorResult | Promise<ValidatorResult>;
17
45
  /** `required()`'s return: a validator tagged for {@link isRequired} detection. */
18
- export interface RequiredValidator {
19
- (value: unknown, values: unknown, meta: unknown): ValidatorResult | Promise<ValidatorResult>;
46
+ export interface RequiredValidator<TValue = unknown, TValues = unknown> {
47
+ (value: TValue | EmptyValue, values: TValues, meta?: FieldValidatorMeta): ValidatorResult | Promise<ValidatorResult>;
20
48
  readonly isRequired: true;
21
49
  }
50
+ /** A validator, or the `false`/nullish ra passes when a rule is switched off. */
51
+ export type ValidatorInput<TValue = unknown, TValues = unknown> = Validator<TValue, TValues> | false | null | undefined;
52
+ /** One `validate` slot: a single validator or a list of them (ra call style). */
53
+ export type ValidatorSlot<TValue = unknown, TValues = unknown> = ValidatorInput<TValue, TValues> | ReadonlyArray<ValidatorInput<TValue, TValues>>;
22
54
  /** Params handed to a message factory when the error message is a function. */
23
- export interface ValidationMessageParams {
24
- args: Record<string, unknown> | undefined;
25
- value: unknown;
26
- values: unknown;
55
+ export interface ValidationMessageParams<TValue = unknown, TValues = unknown> {
56
+ args: TranslateParamValues | undefined;
57
+ value: TValue | EmptyValue;
58
+ values: TValues;
27
59
  }
28
60
  /** Builds an error message dynamically from the failing value/args. */
29
- export type MessageFactory = (params: ValidationMessageParams) => ValidationErrorMessage;
61
+ export type MessageFactory<TValue = unknown, TValues = unknown> = (params: ValidationMessageParams<TValue, TValues>) => ValidationErrorMessage;
30
62
  /** A built-in's `message` option: an i18n key/string or a factory. */
31
- export type ValidationMessage = string | MessageFactory;
63
+ export type ValidationMessage<TValue = unknown, TValues = unknown> = string | MessageFactory<TValue, TValues>;
32
64
  /**
33
65
  * ra's emptiness notion — the skip-empty gate every built-in except
34
66
  * `required` applies: `undefined`, `null`, `''` and `[]` are empty; `0`,
35
67
  * `false` and whitespace are values.
36
68
  */
37
69
  export declare function isEmpty(value: unknown): boolean;
38
- /** Loose input accepted by {@link composeValidators} and {@link isRequired}. */
39
- type ValidatorInput = Validator | false | null | undefined;
40
70
  /**
41
71
  * Composes validators into one (ra contract): runs them SEQUENTIALLY,
42
72
  * short-circuits on the first error, and stays synchronous while every
@@ -48,13 +78,13 @@ type ValidatorInput = Validator | false | null | undefined;
48
78
  * options rather than react-hook-form rules, so fresh identities per render
49
79
  * cannot re-trigger anything.
50
80
  */
51
- export declare function composeValidators(...validators: ReadonlyArray<ValidatorInput | ReadonlyArray<ValidatorInput>>): Validator;
81
+ export declare function composeValidators<TValue = unknown, TValues = unknown>(...validators: ReadonlyArray<ValidatorSlot<TValue, TValues>>): Validator<TValue, TValues>;
52
82
  /**
53
83
  * Errors when the value is empty. The ONLY built-in that fails on emptiness —
54
84
  * every other one skips empty values so optional fields stay optional.
55
85
  * The returned validator is tagged `.isRequired` for {@link isRequired}.
56
86
  */
57
- export declare function required(message?: ValidationMessage): RequiredValidator;
87
+ export declare function required<TValue = unknown, TValues = unknown>(message?: ValidationMessage<TValue, TValues>): RequiredValidator<TValue, TValues>;
58
88
  /** Errors when a string/array value is shorter than `min`. Skips empty values. */
59
89
  export declare function minLength(min: number, message?: ValidationMessage): Validator;
60
90
  /** Errors when a string/array value is longer than `max`. Skips empty values. */
@@ -72,12 +102,21 @@ export declare function number(message?: ValidationMessage): Validator;
72
102
  export declare function regex(pattern: RegExp, message?: ValidationMessage): Validator;
73
103
  /** Errors when the value is not a valid email address. Skips empty values. */
74
104
  export declare function email(message?: ValidationMessage): Validator;
75
- /** Errors when the value is not one of `list`. Skips empty values. */
76
- export declare function choices(list: readonly unknown[], message?: ValidationMessage): Validator;
105
+ /**
106
+ * Errors when the value is not one of `list`. Skips empty values.
107
+ *
108
+ * Generic in the choices: {@link ChoiceValueOf} maps the list to the value the
109
+ * field holds for one (a `{id, name}` pair stores its `id`), so
110
+ * `choices([{ id: 1, name: 'm' }])` is a validator for a number field.
111
+ */
112
+ export declare function choices<TChoice>(list: readonly TChoice[], message?: ValidationMessage<ChoiceValueOf<TChoice>>): Validator<ChoiceValueOf<TChoice>>;
77
113
  /**
78
114
  * Detects requiredness from the RAW `validate` prop — a single validator or
79
115
  * any array member tagged `.isRequired === true` (the `required()` tag).
80
116
  * Inputs use this to render the required marker without running validation.
81
117
  */
82
- export declare function isRequired(validate: ValidatorInput | ReadonlyArray<ValidatorInput>): boolean;
83
- export {};
118
+ export declare function isRequired<TValue = unknown, TValues = unknown>(validate: ValidatorSlot<TValue, TValues>): boolean;
119
+ /** The validator stack a field binding runs: schema issues or a validator error. */
120
+ export type FieldValidatorStackResult = ValidatorResult | readonly StandardSchemaV1Issue[];
121
+ /** A field schema outcome: issues, or a thenable of them (an async schema). */
122
+ export type SchemaOutcome = ReturnType<StandardSchemaV1["~standard"]["validate"]>;
@@ -1,99 +1,125 @@
1
- import { isThenable } from "./save-pipeline.js";
2
- const EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
1
+ import { isThenable } from "../guards.js";
2
+ import { choiceIdOf } from "./input-values.js";
3
+ // src/form/validators.ts
4
+ var EMAIL_REGEX = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
5
+ /**
6
+ * ra's emptiness notion — the skip-empty gate every built-in except
7
+ * `required` applies: `undefined`, `null`, `''` and `[]` are empty; `0`,
8
+ * `false` and whitespace are values.
9
+ */
3
10
  function isEmpty(value) {
4
- return value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
11
+ return value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
5
12
  }
13
+ /** Resolves a {@link ValidationMessage} into the error payload (ra's getMessage). */
6
14
  function getMessage(message, args, value, values) {
7
- if (typeof message === "function") return message({ args, value, values });
8
- return args ? { message, args } : message;
15
+ if (typeof message === "function") return message({
16
+ args,
17
+ value,
18
+ values
19
+ });
20
+ return args ? {
21
+ message,
22
+ args
23
+ } : message;
9
24
  }
25
+ /**
26
+ * Composes validators into one (ra contract): runs them SEQUENTIALLY,
27
+ * short-circuits on the first error, and stays synchronous while every
28
+ * consulted validator is synchronous — an async member switches the tail of
29
+ * the run (and only that run) to promise chaining. Accepts a rest list or a
30
+ * single array (ra call style); non-function members are filtered out.
31
+ *
32
+ * Unlike ra there is no memoization: bindings hold validators in mutable
33
+ * options rather than react-hook-form rules, so fresh identities per render
34
+ * cannot re-trigger anything.
35
+ */
10
36
  function composeValidators(...validators) {
11
- const head = validators[0];
12
- const list = (Array.isArray(head) ? head : validators).filter(
13
- (candidate) => typeof candidate === "function"
14
- );
15
- const runFrom = (start, value, values, meta) => {
16
- for (let index = start; index < list.length; index++) {
17
- const validator = list[index];
18
- const result = validator(value, values, meta);
19
- if (isThenable(result)) {
20
- return Promise.resolve(result).then(
21
- (resolved) => resolved ? resolved : runFrom(index + 1, value, values, meta)
22
- );
23
- }
24
- if (result) return result;
25
- }
26
- return void 0;
27
- };
28
- return (value, values, meta) => runFrom(0, value, values, meta);
37
+ const head = validators[0];
38
+ const list = (Array.isArray(head) ? head : validators).filter((candidate) => typeof candidate === "function");
39
+ const runFrom = (start, value, values, meta) => {
40
+ for (let index = start; index < list.length; index++) {
41
+ const validator = list[index];
42
+ if (validator === void 0) continue;
43
+ const result = validator(value, values, meta);
44
+ if (isThenable(result)) return Promise.resolve(result).then((resolved) => resolved ? resolved : runFrom(index + 1, value, values, meta));
45
+ if (result) return result;
46
+ }
47
+ };
48
+ return (value, values, meta) => runFrom(0, value, values, meta);
29
49
  }
50
+ /**
51
+ * Errors when the value is empty. The ONLY built-in that fails on emptiness —
52
+ * every other one skips empty values so optional fields stay optional.
53
+ * The returned validator is tagged `.isRequired` for {@link isRequired}.
54
+ */
30
55
  function required(message = "chelone.validation.required") {
31
- return Object.assign(
32
- (value, values, _meta) => isEmpty(value) ? getMessage(message, void 0, value, values) : void 0,
33
- { isRequired: true }
34
- );
56
+ return Object.assign((value, values) => isEmpty(value) ? getMessage(message, void 0, value, values) : void 0, { isRequired: true });
35
57
  }
58
+ /** The length of a string/array value; `undefined` for anything else (skip). */
36
59
  function lengthOf(value) {
37
- if (typeof value === "string" || Array.isArray(value)) return value.length;
38
- return void 0;
60
+ if (typeof value === "string" || Array.isArray(value)) return value.length;
39
61
  }
62
+ /** Errors when a string/array value is shorter than `min`. Skips empty values. */
40
63
  function minLength(min, message = "chelone.validation.minLength") {
41
- return (value, values) => {
42
- if (isEmpty(value)) return void 0;
43
- const length = lengthOf(value);
44
- return length !== void 0 && length < min ? getMessage(message, { min }, value, values) : void 0;
45
- };
64
+ return (value, values) => {
65
+ if (isEmpty(value)) return void 0;
66
+ const length = lengthOf(value);
67
+ return length !== void 0 && length < min ? getMessage(message, { min }, value, values) : void 0;
68
+ };
46
69
  }
70
+ /** Errors when a string/array value is longer than `max`. Skips empty values. */
47
71
  function maxLength(max, message = "chelone.validation.maxLength") {
48
- return (value, values) => {
49
- if (isEmpty(value)) return void 0;
50
- const length = lengthOf(value);
51
- return length !== void 0 && length > max ? getMessage(message, { max }, value, values) : void 0;
52
- };
72
+ return (value, values) => {
73
+ if (isEmpty(value)) return void 0;
74
+ const length = lengthOf(value);
75
+ return length !== void 0 && length > max ? getMessage(message, { max }, value, values) : void 0;
76
+ };
53
77
  }
78
+ /** Errors when the value is below `min` (`0` IS validated). Skips empty values. */
54
79
  function minValue(min, message = "chelone.validation.minValue") {
55
- return (value, values) => !isEmpty(value) && value < min ? getMessage(message, { min }, value, values) : void 0;
80
+ return (value, values) => !isEmpty(value) && Number(value) < min ? getMessage(message, { min }, value, values) : void 0;
56
81
  }
82
+ /** Errors when the value is above `max`. Skips empty values. */
57
83
  function maxValue(max, message = "chelone.validation.maxValue") {
58
- return (value, values) => !isEmpty(value) && value > max ? getMessage(message, { max }, value, values) : void 0;
84
+ return (value, values) => !isEmpty(value) && Number(value) > max ? getMessage(message, { max }, value, values) : void 0;
59
85
  }
86
+ /** Errors when the value is not coercible to a number. Skips empty values. */
60
87
  function number(message = "chelone.validation.number") {
61
- return (value, values) => !isEmpty(value) && Number.isNaN(Number(value)) ? getMessage(message, void 0, value, values) : void 0;
88
+ return (value, values) => !isEmpty(value) && Number.isNaN(Number(value)) ? getMessage(message, void 0, value, values) : void 0;
62
89
  }
90
+ /**
91
+ * Errors when a STRING value does not match `pattern` (non-strings skip, ra
92
+ * parity). The pattern rides the error args stringified for interpolation.
93
+ */
63
94
  function regex(pattern, message = "chelone.validation.regex") {
64
- return (value, values) => !isEmpty(value) && typeof value === "string" && !pattern.test(value) ? getMessage(message, { pattern: String(pattern) }, value, values) : void 0;
95
+ return (value, values) => !isEmpty(value) && typeof value === "string" && !pattern.test(value) ? getMessage(message, { pattern: String(pattern) }, value, values) : void 0;
65
96
  }
97
+ /** Errors when the value is not a valid email address. Skips empty values. */
66
98
  function email(message = "chelone.validation.email") {
67
- return regex(EMAIL_REGEX, message);
99
+ return regex(EMAIL_REGEX, message);
68
100
  }
101
+ /**
102
+ * Errors when the value is not one of `list`. Skips empty values.
103
+ *
104
+ * Generic in the choices: {@link ChoiceValueOf} maps the list to the value the
105
+ * field holds for one (a `{id, name}` pair stores its `id`), so
106
+ * `choices([{ id: 1, name: 'm' }])` is a validator for a number field.
107
+ */
69
108
  function choices(list, message = "chelone.validation.oneOf") {
70
- return (value, values) => !isEmpty(value) && !list.includes(value) ? getMessage(
71
- message,
72
- { options: list.map(String).join(", ") },
73
- value,
74
- values
75
- ) : void 0;
109
+ const options = list.map(choiceIdOf);
110
+ return (value, values) => !isEmpty(value) && !options.includes(value) ? getMessage(message, { options: options.map(String).join(", ") }, value, values) : void 0;
76
111
  }
112
+ /**
113
+ * Detects requiredness from the RAW `validate` prop — a single validator or
114
+ * any array member tagged `.isRequired === true` (the `required()` tag).
115
+ * Inputs use this to render the required marker without running validation.
116
+ */
77
117
  function isRequired(validate) {
78
- if (!validate) return false;
79
- if (Array.isArray(validate)) {
80
- return validate.some(
81
- (candidate) => typeof candidate === "function" && candidate.isRequired === true
82
- );
83
- }
84
- return validate.isRequired === true;
118
+ if (Array.isArray(validate)) return validate.some((candidate) => typeof candidate === "function" && hasRequiredTag(candidate));
119
+ return typeof validate === "function" && hasRequiredTag(validate);
85
120
  }
86
- export {
87
- choices,
88
- composeValidators,
89
- email,
90
- isEmpty,
91
- isRequired,
92
- maxLength,
93
- maxValue,
94
- minLength,
95
- minValue,
96
- number,
97
- regex,
98
- required
99
- };
121
+ /** The `required()` tag, read through an `in` check rather than a cast. */
122
+ function hasRequiredTag(validator) {
123
+ return "isRequired" in validator && validator.isRequired === true;
124
+ }
125
+ export { choices, composeValidators, email, isEmpty, isRequired, maxLength, maxValue, minLength, minValue, number, regex, required };
@@ -1,15 +1,16 @@
1
- import type { GeoBBox, GeoGeometry, GeoPolygon } from "./geojson.js";
1
+ import type { Geometry, Polygon } from "geojson";
2
+ import type { LngLatBox } from "./bounds.js";
2
3
  /**
3
4
  * The closed rectangle ring of a bbox; `null` unless it is four finite numbers.
4
5
  * A box with west greater than east crosses 180: its ring runs east of 180.
5
6
  */
6
- export declare function bboxToPolygon(bbox: unknown): GeoPolygon | null;
7
+ export declare function bboxToPolygon(bbox: unknown): Polygon | null;
7
8
  /** The box around a Polygon, west greater than east across 180; `null` for any other geometry. */
8
- export declare function polygonToBBox(geometry: GeoGeometry): GeoBBox | null;
9
+ export declare function polygonToBBox(geometry: Geometry): LngLatBox | null;
9
10
  /**
10
11
  * Builds a polygon-to-bbox function whose box has width over height equal to
11
12
  * `ratio`. The box grows around the old centre, never shrinks. A zero width,
12
13
  * a zero height or a `ratio` that is not a positive finite number yields the
13
14
  * plain bbox.
14
15
  */
15
- export declare function aspectLockedBBox(ratio: number): (geometry: GeoGeometry) => GeoBBox | null;
16
+ export declare function aspectLockedBBox(ratio: number): (geometry: Geometry) => LngLatBox | null;
package/dist/geo/bbox.js CHANGED
@@ -1,56 +1,77 @@
1
1
  import { geoBounds } from "./bounds.js";
2
2
  import { isGeoBBox } from "./geojson.js";
3
+ // src/geo/bbox.ts
4
+ /**
5
+ * The closed rectangle ring of a bbox; `null` unless it is four finite numbers.
6
+ * A box with west greater than east crosses 180: its ring runs east of 180.
7
+ */
3
8
  function bboxToPolygon(bbox) {
4
- if (!isGeoBBox(bbox)) return null;
5
- const [west, south, rawEast, north] = bbox;
6
- const east = west > rawEast ? rawEast + 360 : rawEast;
7
- return {
8
- type: "Polygon",
9
- coordinates: [
10
- [
11
- [west, south],
12
- [east, south],
13
- [east, north],
14
- [west, north],
15
- [west, south]
16
- ]
17
- ]
18
- };
9
+ if (!isGeoBBox(bbox)) return null;
10
+ const [west, south, rawEast, north] = bbox;
11
+ const east = west > rawEast ? rawEast + 360 : rawEast;
12
+ return {
13
+ type: "Polygon",
14
+ coordinates: [[
15
+ [west, south],
16
+ [east, south],
17
+ [east, north],
18
+ [west, north],
19
+ [west, south]
20
+ ]]
21
+ };
19
22
  }
23
+ /** The box around a Polygon, west greater than east across 180; `null` for any other geometry. */
20
24
  function polygonToBBox(geometry) {
21
- return geometry.type === "Polygon" ? geoBounds(geometry, "explicit") : null;
25
+ return geometry.type === "Polygon" ? geoBounds(geometry, "explicit") : null;
22
26
  }
23
- const RATIO_TOLERANCE = 1e-9;
24
- const wrapLongitude = (longitude) => longitude > 180 ? longitude - 360 : longitude < -180 ? longitude + 360 : longitude;
27
+ var RATIO_TOLERANCE = 1e-9;
28
+ var wrapLongitude = (longitude) => longitude > 180 ? longitude - 360 : longitude < -180 ? longitude + 360 : longitude;
29
+ /**
30
+ * Builds a polygon-to-bbox function whose box has width over height equal to
31
+ * `ratio`. The box grows around the old centre, never shrinks. A zero width,
32
+ * a zero height or a `ratio` that is not a positive finite number yields the
33
+ * plain bbox.
34
+ */
25
35
  function aspectLockedBBox(ratio) {
26
- return (geometry) => {
27
- const bbox = polygonToBBox(geometry);
28
- if (!bbox || !Number.isFinite(ratio) || ratio <= 0) return bbox;
29
- const [west, south, east, north] = bbox;
30
- const crossing = west > east;
31
- const unwrappedEast = crossing ? east + 360 : east;
32
- const width = unwrappedEast - west;
33
- const height = north - south;
34
- if (width <= 0 || height <= 0) return bbox;
35
- const current = width / height;
36
- if (Math.abs(current - ratio) <= RATIO_TOLERANCE * ratio) return bbox;
37
- if (current < ratio) {
38
- const grow2 = (height * ratio - width) / 2;
39
- if (!crossing) return [west - grow2, south, east + grow2, north];
40
- if (width + 2 * grow2 >= 360) return [-180, south, 180, north];
41
- return [
42
- wrapLongitude(west - grow2),
43
- south,
44
- wrapLongitude(unwrappedEast + grow2),
45
- north
46
- ];
47
- }
48
- const grow = (width / ratio - height) / 2;
49
- return [west, south - grow, east, north + grow];
50
- };
36
+ return (geometry) => {
37
+ const bbox = polygonToBBox(geometry);
38
+ if (!bbox || !Number.isFinite(ratio) || ratio <= 0) return bbox;
39
+ const [west, south, east, north] = bbox;
40
+ const crossing = west > east;
41
+ const unwrappedEast = crossing ? east + 360 : east;
42
+ const width = unwrappedEast - west;
43
+ const height = north - south;
44
+ if (width <= 0 || height <= 0) return bbox;
45
+ const current = width / height;
46
+ if (Math.abs(current - ratio) <= RATIO_TOLERANCE * ratio) return bbox;
47
+ if (current < ratio) {
48
+ const grow = (height * ratio - width) / 2;
49
+ if (!crossing) return [
50
+ west - grow,
51
+ south,
52
+ east + grow,
53
+ north
54
+ ];
55
+ if (width + 2 * grow >= 360) return [
56
+ -180,
57
+ south,
58
+ 180,
59
+ north
60
+ ];
61
+ return [
62
+ wrapLongitude(west - grow),
63
+ south,
64
+ wrapLongitude(unwrappedEast + grow),
65
+ north
66
+ ];
67
+ }
68
+ const grow = (width / ratio - height) / 2;
69
+ return [
70
+ west,
71
+ south - grow,
72
+ east,
73
+ north + grow
74
+ ];
75
+ };
51
76
  }
52
- export {
53
- aspectLockedBBox,
54
- bboxToPolygon,
55
- polygonToBBox
56
- };
77
+ export { aspectLockedBBox, bboxToPolygon, polygonToBBox };
@@ -1,4 +1,15 @@
1
- import type { GeoBBox, GeoJsonValue } from "./geojson.js";
1
+ import type { Feature, FeatureCollection, Geometry } from "geojson";
2
+ import type { JsonObject } from "../guards.js";
3
+ /**
4
+ * `[west, south, east, north]`. Not geojson's `BBox`, which also allows six
5
+ * numbers.
6
+ */
7
+ export type LngLatBox = [
8
+ west: number,
9
+ south: number,
10
+ east: number,
11
+ north: number
12
+ ];
2
13
  /**
3
14
  * How `geoBounds` picks west and east. `narrowest` takes whichever arc is
4
15
  * narrower, as for framing features. `explicit` goes across 180 only when a
@@ -12,9 +23,9 @@ export type CrossingMode = "narrowest" | "explicit";
12
23
  * longitudes are narrower going round the antimeridian, west is greater than
13
24
  * east.
14
25
  */
15
- export declare function geoBounds(value: GeoJsonValue, mode?: CrossingMode): GeoBBox | null;
26
+ export declare function geoBounds(value: Geometry | Feature<Geometry | null, JsonObject | null> | FeatureCollection<Geometry | null, JsonObject | null>, mode?: CrossingMode): LngLatBox | null;
16
27
  /**
17
28
  * Whether every corner is a valid longitude and latitude. MapLibre throws on a
18
29
  * latitude outside ±90, so a box failing this is never fitted.
19
30
  */
20
- export declare function isLngLatBox(box: readonly number[] | null): box is [number, number, number, number];
31
+ export declare function isLngLatBox(box: readonly number[] | null): box is LngLatBox;