@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
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Pure date, date-time and time format and parse helpers shared by the React and
3
+ * Vue `<DateInput>`, `<DateTimeInput>` and `<TimeInput>`, so both adapters
4
+ * format and validate a value the same way.
5
+ */
6
+ import type { DateInputValue } from "./input-values.js";
7
+ /**
8
+ * Format a form value (string / ISO / `Date` / timestamp / "") to a bare
9
+ * "YYYY-MM-DD" string for `<input type="date">`. Returns `null` for nullish/empty.
10
+ */
11
+ export declare function formatDate(value: DateInputValue): string | null;
12
+ /** Whether a raw `<input type="date">` string is empty or a valid date. */
13
+ export declare function isValidDate(raw: string): boolean;
14
+ /**
15
+ * Format a form value (ISO string / `Date` / timestamp) to the local
16
+ * `datetime-local` display string. Empty → "" (keeps the input controlled).
17
+ */
18
+ export declare function formatDateTime(value: DateInputValue): string;
19
+ /**
20
+ * Parse a local `datetime-local` string back to an ISO 8601 string (browser
21
+ * timezone). Returns `null` for empty/unparseable so a required validator fires.
22
+ */
23
+ export declare function parseDateTimeToISO(value: string): string | null;
24
+ /** Whether a raw `datetime-local` string is empty or parses to a valid date. */
25
+ export declare function isValidDateTime(raw: string): boolean;
26
+ /**
27
+ * Format a form value (`Date` / "HH:mm" string) to the "HH:mm" display string.
28
+ * Empty → "" (keeps the input controlled).
29
+ */
30
+ export declare function formatTime(value: DateInputValue): string;
31
+ /**
32
+ * Parse a "HH:mm"/"HH:mm:ss" string to a `Date` (today at that local time), for
33
+ * dataProvider compatibility. Returns `null` for empty/unparseable so a required
34
+ * validator still fires.
35
+ */
36
+ export declare function parseTimeToDate(value: string): Date | null;
37
+ /** Whether a raw `<input type="time">` string is empty or a valid time. */
38
+ export declare function isValidTime(raw: string): boolean;
@@ -0,0 +1,89 @@
1
+ // src/form/date-format.ts
2
+ var leftPad2 = (value) => `00${value}`.slice(-2);
3
+ var leftPad4 = (value) => `0000${value}`.slice(-4);
4
+ var dateRegex = /^\d{4}-\d{2}-\d{2}$/;
5
+ /** Convert a `Date` to a local "YYYY-MM-DD" string, or "" for an invalid date. */
6
+ function dateToYmd(value) {
7
+ if (!(value instanceof Date) || Number.isNaN(value.getDate())) return "";
8
+ return `${value.getFullYear().toString()}-${leftPad2(value.getMonth() + 1)}-${leftPad2(value.getDate())}`;
9
+ }
10
+ /**
11
+ * Format a form value (string / ISO / `Date` / timestamp / "") to a bare
12
+ * "YYYY-MM-DD" string for `<input type="date">`. Returns `null` for nullish/empty.
13
+ */
14
+ function formatDate(value) {
15
+ if (value == null || value === "") return null;
16
+ if (value instanceof Date) return dateToYmd(value);
17
+ if (typeof value === "string" && dateRegex.test(value)) return value;
18
+ return dateToYmd(new Date(value));
19
+ }
20
+ /** Whether a raw `<input type="date">` string is empty or a valid date. */
21
+ function isValidDate(raw) {
22
+ if (raw === "") return true;
23
+ return dateRegex.test(raw) && !Number.isNaN(new Date(raw).getTime());
24
+ }
25
+ var dateTimeRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/;
26
+ /** Convert a `Date` to a local "yyyy-MM-ddThh:mm" string, or "" if invalid. */
27
+ function dateToLocalDateTime(value) {
28
+ if (!(value instanceof Date) || Number.isNaN(value.getDate())) return "";
29
+ return `${leftPad4(value.getFullYear())}-${leftPad2(value.getMonth() + 1)}-${leftPad2(value.getDate())}T${leftPad2(value.getHours())}:${leftPad2(value.getMinutes())}`;
30
+ }
31
+ /**
32
+ * Format a form value (ISO string / `Date` / timestamp) to the local
33
+ * `datetime-local` display string. Empty → "" (keeps the input controlled).
34
+ */
35
+ function formatDateTime(value) {
36
+ if (value == null || value === "") return "";
37
+ if (value instanceof Date) return dateToLocalDateTime(value);
38
+ if (typeof value === "string" && dateTimeRegex.test(value)) return value;
39
+ return dateToLocalDateTime(new Date(value));
40
+ }
41
+ /**
42
+ * Parse a local `datetime-local` string back to an ISO 8601 string (browser
43
+ * timezone). Returns `null` for empty/unparseable so a required validator fires.
44
+ */
45
+ function parseDateTimeToISO(value) {
46
+ if (value === "") return null;
47
+ const date = new Date(value);
48
+ return Number.isNaN(date.getTime()) ? null : date.toISOString();
49
+ }
50
+ /** Whether a raw `datetime-local` string is empty or parses to a valid date. */
51
+ function isValidDateTime(raw) {
52
+ if (raw === "") return true;
53
+ return !Number.isNaN(new Date(raw).getTime());
54
+ }
55
+ var timeRegex = /^\d{2}:\d{2}(:\d{2})?$/;
56
+ /** Convert a `Date` to a local "HH:mm" string, or "" if invalid. */
57
+ function dateToHm(value) {
58
+ if (!(value instanceof Date) || Number.isNaN(value.getDate())) return "";
59
+ return `${leftPad2(value.getHours())}:${leftPad2(value.getMinutes())}`;
60
+ }
61
+ /**
62
+ * Format a form value (`Date` / "HH:mm" string) to the "HH:mm" display string.
63
+ * Empty → "" (keeps the input controlled).
64
+ */
65
+ function formatTime(value) {
66
+ if (value == null || value === "") return "";
67
+ if (value instanceof Date) return dateToHm(value);
68
+ if (typeof value === "string" && timeRegex.test(value)) return value;
69
+ return dateToHm(new Date(value));
70
+ }
71
+ /**
72
+ * Parse a "HH:mm"/"HH:mm:ss" string to a `Date` (today at that local time), for
73
+ * dataProvider compatibility. Returns `null` for empty/unparseable so a required
74
+ * validator still fires.
75
+ */
76
+ function parseTimeToDate(value) {
77
+ if (timeRegex.test(value)) {
78
+ const [hh = "0", mm = "0", ss = "0"] = value.split(":");
79
+ const d = /* @__PURE__ */ new Date();
80
+ d.setHours(Number.parseInt(hh, 10), Number.parseInt(mm, 10), Number.parseInt(ss, 10), 0);
81
+ return d;
82
+ }
83
+ return null;
84
+ }
85
+ /** Whether a raw `<input type="time">` string is empty or a valid time. */
86
+ function isValidTime(raw) {
87
+ return raw === "" || timeRegex.test(raw);
88
+ }
89
+ export { formatDate, formatDateTime, formatTime, isValidDate, isValidDateTime, isValidTime, parseDateTimeToISO, parseTimeToDate };
@@ -9,6 +9,6 @@
9
9
  * `address.city` yields `address`. A field never touched, or only registered,
10
10
  * is left out.
11
11
  */
12
- export declare function getDirtyFormValues(values: Record<string, unknown>, fieldMeta: Record<string, {
12
+ export declare function getDirtyFormValues<TValues extends object>(values: TValues, fieldMeta: Record<string, {
13
13
  isDirty: boolean;
14
- } | undefined>): Record<string, unknown>;
14
+ } | undefined>): Partial<TValues>;
@@ -1,13 +1,24 @@
1
- const topLevelName = (path) => path.split(/[.[]/, 1)[0] ?? path;
1
+ // src/form/dirty-values.ts
2
+ /**
3
+ * Reads the touched fields off a form-core form, so both adapters report the
4
+ * same diff. form-core has no `dirtyFields` twin: its `fieldMeta` is keyed by
5
+ * leaf path and each entry carries a sticky `isDirty`.
6
+ */
7
+ /** `address.city` and `tags[0]` both belong to the top-level field. */
8
+ var topLevelName = (path) => path.split(/[.[]/, 1)[0] ?? path;
9
+ /**
10
+ * Returns `{ [topLevelName]: values[topLevelName] }` for every field path whose
11
+ * meta is dirty. A dirty nested path yields the whole top-level value, so
12
+ * `address.city` yields `address`. A field never touched, or only registered,
13
+ * is left out.
14
+ */
2
15
  function getDirtyFormValues(values, fieldMeta) {
3
- const dirty = {};
4
- for (const [path, meta] of Object.entries(fieldMeta)) {
5
- if (!meta?.isDirty) continue;
6
- const name = topLevelName(path);
7
- dirty[name] = values[name];
8
- }
9
- return dirty;
16
+ const dirty = {};
17
+ for (const [path, meta] of Object.entries(fieldMeta)) {
18
+ if (!meta?.isDirty) continue;
19
+ const name = topLevelName(path);
20
+ dirty[name] = values[name];
21
+ }
22
+ return dirty;
10
23
  }
11
- export {
12
- getDirtyFormValues
13
- };
24
+ export { getDirtyFormValues };
@@ -0,0 +1,22 @@
1
+ export interface ParsedDuration {
2
+ days?: number;
3
+ hours?: number;
4
+ minutes?: number;
5
+ seconds?: number;
6
+ }
7
+ /**
8
+ * Parses an ISO-8601 duration string (e.g. `PT2H30M`, `P1DT4H`) into a
9
+ * `ParsedDuration` object. Returns `null` for unparseable input.
10
+ */
11
+ export declare function parseIsoDuration(s: string): ParsedDuration | null;
12
+ /** Renders a `ParsedDuration` in compact human-readable form, e.g. `2h 30m`. */
13
+ export declare function compactDuration(p: ParsedDuration): string;
14
+ /**
15
+ * Renders a `ParsedDuration` in relative long-form, e.g. `2 hours 30 minutes`.
16
+ *
17
+ * Clean-room reimplementation of date-fns's `formatDuration` (the only shape
18
+ * `<DurationField displayFormat="relative">` needs), so chelone avoids a
19
+ * date-fns dependency for a single formatter. Emits English unit words with
20
+ * the usual singular/plural rule, space-joined in largest-to-smallest order.
21
+ */
22
+ export declare function relativeDuration(p: ParsedDuration): string;
@@ -0,0 +1,49 @@
1
+ // src/form/duration.ts
2
+ var ISO_RE = /^P(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?$/;
3
+ /**
4
+ * Parses an ISO-8601 duration string (e.g. `PT2H30M`, `P1DT4H`) into a
5
+ * `ParsedDuration` object. Returns `null` for unparseable input.
6
+ */
7
+ function parseIsoDuration(s) {
8
+ const m = s.match(ISO_RE);
9
+ if (!m) return null;
10
+ const [, d, h, mn, sc] = m;
11
+ const out = {};
12
+ if (d) out.days = +d;
13
+ if (h) out.hours = +h;
14
+ if (mn) out.minutes = +mn;
15
+ if (sc) out.seconds = +sc;
16
+ return out;
17
+ }
18
+ /** Renders a `ParsedDuration` in compact human-readable form, e.g. `2h 30m`. */
19
+ function compactDuration(p) {
20
+ const parts = [];
21
+ if (p.days) parts.push(`${p.days}d`);
22
+ if (p.hours) parts.push(`${p.hours}h`);
23
+ if (p.minutes) parts.push(`${p.minutes}m`);
24
+ if (p.seconds) parts.push(`${p.seconds}s`);
25
+ return parts.join(" ") || "0m";
26
+ }
27
+ /**
28
+ * Renders a `ParsedDuration` in relative long-form, e.g. `2 hours 30 minutes`.
29
+ *
30
+ * Clean-room reimplementation of date-fns's `formatDuration` (the only shape
31
+ * `<DurationField displayFormat="relative">` needs), so chelone avoids a
32
+ * date-fns dependency for a single formatter. Emits English unit words with
33
+ * the usual singular/plural rule, space-joined in largest-to-smallest order.
34
+ */
35
+ function relativeDuration(p) {
36
+ const units = [
37
+ ["days", "day"],
38
+ ["hours", "hour"],
39
+ ["minutes", "minute"],
40
+ ["seconds", "second"]
41
+ ];
42
+ const parts = [];
43
+ for (const [key, word] of units) {
44
+ const n = p[key];
45
+ if (n) parts.push(`${n} ${word}${n === 1 ? "" : "s"}`);
46
+ }
47
+ return parts.join(" ") || "0 seconds";
48
+ }
49
+ export { compactDuration, parseIsoDuration, relativeDuration };
@@ -1,8 +1,9 @@
1
- import { type CreateController, type CreateControllerState, type EditController, type EditControllerState, type RecordControllerContext, type RedirectRequest, type SaveTransform } from "../controller/record-controllers.js";
1
+ import { type CreateController, type CreateControllerState, type EditController, type EditControllerState, type RecordControllerContext, type RedirectRequest, type SaveOptions, type SaveTransform } from "../controller/record-controllers.js";
2
2
  import type { MutationMode } from "../data/mutation-options.js";
3
3
  import type { Identifier } from "../resource/identifier.js";
4
4
  import type { RecordOf, ResourceMap } from "../resource/registry.js";
5
5
  import { type CheloneFormApi, type CheloneFormState, type FormControllerOptions } from "./form-controller.js";
6
+ import { type ResourceFormHandle } from "./form-handle.js";
6
7
  /**
7
8
  * What the composed form controllers need: the P4 record-controller services
8
9
  * plus (optionally) the resource registry — a resource's zod schema becomes
@@ -65,20 +66,26 @@ export interface CreateFormControllerState<TRecord> extends CreateControllerStat
65
66
  * unsubscribe releases both. `getSnapshot` composes the two reference-stable
66
67
  * sub-snapshots with identity caching (uSES-safe).
67
68
  */
68
- export interface EditFormController<TRecord> {
69
+ export interface EditFormController<TRecord, TName extends string = string> {
69
70
  form: CheloneFormApi<Partial<TRecord>>;
71
+ /** The form as a handle tagged with the resource it edits. */
72
+ handle: ResourceFormHandle<TName, Partial<TRecord>>;
70
73
  /** The composed P4 edit controller — imperative `save` with call-time options lives here. */
71
74
  recordController: EditController<TRecord>;
72
- submit(): Promise<void>;
75
+ /** Validates, persists, reconciles. `options` apply to this one save, as `recordController.save`'s do. */
76
+ submit(options?: SaveOptions<TRecord>): Promise<void>;
73
77
  subscribe(listener: () => void): () => void;
74
78
  getSnapshot(): EditFormControllerState<TRecord>;
75
79
  destroy(): void;
76
80
  }
77
- export interface CreateFormController<TRecord> {
81
+ export interface CreateFormController<TRecord, TName extends string = string> {
78
82
  form: CheloneFormApi<Partial<TRecord>>;
83
+ /** The form as a handle tagged with the resource it creates. */
84
+ handle: ResourceFormHandle<TName, Partial<TRecord>>;
79
85
  /** The composed P4 create controller. */
80
86
  recordController: CreateController<TRecord>;
81
- submit(): Promise<void>;
87
+ /** Validates, persists, reconciles. `options` apply to this one save, as `recordController.save`'s do. */
88
+ submit(options?: SaveOptions<TRecord>): Promise<void>;
82
89
  subscribe(listener: () => void): () => void;
83
90
  getSnapshot(): CreateFormControllerState<TRecord>;
84
91
  destroy(): void;