@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,196 +1,203 @@
1
- import { QueryObserver } from "@tanstack/query-core";
2
1
  import { getManyReferenceOptions } from "../data/query-options.js";
3
2
  import { createMicroStore } from "../store/micro-store.js";
4
- import { createListController, createErrorAnnouncer, pageFlags, runSelectAll, DEFAULT_DEBOUNCE_MS, resetPageTarget } from "./list-controller.js";
5
- const DEFAULT_PER_PAGE = 25;
3
+ import { createErrorAnnouncer, createListController, pageFlags, resetPageTarget, runSelectAll } from "./list-controller.js";
4
+ import { QueryObserver } from "@tanstack/query-core";
5
+ // src/controller/reference-many-controller.ts
6
+ /** The `perPage` a reference list starts at when nothing is persisted. The single definition: the choices controller and Vue's `<ReferenceInput>` read this. */
7
+ var DEFAULT_REFERENCE_PER_PAGE = 25;
8
+ /**
9
+ * A server-paged list of the rows whose `target` equals `id`, read through
10
+ * `getManyReference`. Params and selection live in an inner list controller
11
+ * (which never fetches); this controller owns the data observer.
12
+ *
13
+ * Lifecycle (Task 4R) is the list controller's: construction is inert, the
14
+ * first subscriber acquires the inner controller and the observer, the last
15
+ * unsubscribe releases both. `observer.setOptions` runs only when the inner
16
+ * params change, never from the result subscription; out-of-range page
17
+ * resets go through the inner `setPage` and only on SETTLED results.
18
+ */
6
19
  function createReferenceManyController(ctx, reference, options) {
7
- const { target, id } = options;
8
- const targetValue = id ?? void 0;
9
- const storeKey = options.storeKey ?? (targetValue === void 0 ? false : `${reference}.${target}.${targetValue}`);
10
- const inner = createListController(
11
- { ...ctx, notifications: void 0 },
12
- reference,
13
- {
14
- perPage: options.perPage ?? DEFAULT_PER_PAGE,
15
- sort: options.sort,
16
- filterDefaultValues: options.filterDefaultValues,
17
- storeKey,
18
- enabled: false
19
- }
20
- );
21
- const initial = inner.getSnapshot();
22
- const store = createMicroStore(
23
- {
24
- ...initial,
25
- data: targetValue === void 0 ? [] : void 0,
26
- total: targetValue === void 0 ? 0 : void 0,
27
- pageInfo: void 0,
28
- isPending: targetValue !== void 0,
29
- isFetching: false,
30
- error: null,
31
- hasNextPage: targetValue === void 0 ? false : void 0,
32
- hasPreviousPage: targetValue === void 0 ? false : void 0
33
- },
34
- { onSubscribed: acquire }
35
- );
36
- let destroyed = false;
37
- store.onDestroy(() => {
38
- destroyed = true;
39
- clearDebounce();
40
- inner.destroy();
41
- });
42
- let observer = null;
43
- let reconfigure = null;
44
- const queryFor = (value, page, perPage) => {
45
- const s = store.getSnapshot();
46
- return getManyReferenceOptions(ctx, reference, {
47
- target,
48
- id: value,
49
- pagination: { page, perPage },
50
- // A single object when there is exactly one key (adapter compat).
51
- sort: s.sort.length === 1 ? s.sort[0] : s.sort,
52
- filter: { ...s.filter, ...options.filter ?? {} }
53
- });
54
- };
55
- const buildOptions = (value) => {
56
- const s = store.getSnapshot();
57
- return {
58
- ...queryFor(value, s.page, s.perPage),
59
- placeholderData: (prev) => prev,
60
- retry: false,
61
- enabled: options.enabled ?? true
62
- };
63
- };
64
- const mirror = () => {
65
- const next = inner.getSnapshot();
66
- const prev = store.getSnapshot();
67
- const queryChanged = next.page !== prev.page || next.perPage !== prev.perPage || next.sort !== prev.sort || next.filter !== prev.filter;
68
- store.setState({
69
- page: next.page,
70
- perPage: next.perPage,
71
- sort: next.sort,
72
- filter: next.filter,
73
- displayedFilters: next.displayedFilters,
74
- selectedIds: next.selectedIds
75
- });
76
- if (queryChanged) reconfigure?.();
77
- };
78
- const through = (action) => (...args) => {
79
- if (destroyed) return;
80
- action(...args);
81
- mirror();
82
- };
83
- let debounceTimer;
84
- function clearDebounce() {
85
- if (debounceTimer === void 0) return;
86
- clearTimeout(debounceTimer);
87
- debounceTimer = void 0;
88
- }
89
- const setFilters = (filter, displayedFilters, debounced = false) => {
90
- if (destroyed) return;
91
- clearDebounce();
92
- const apply = () => {
93
- inner.setFilters(filter, displayedFilters);
94
- mirror();
95
- };
96
- if (!debounced) {
97
- apply();
98
- return;
99
- }
100
- debounceTimer = setTimeout(() => {
101
- debounceTimer = void 0;
102
- apply();
103
- }, options.debounceMs ?? DEFAULT_DEBOUNCE_MS);
104
- };
105
- const setParams = (patch) => {
106
- if (patch.filter !== void 0) clearDebounce();
107
- through(inner.setParams)(patch);
108
- };
109
- const selectAll = async (opts) => {
110
- if (targetValue === void 0) return;
111
- return runSelectAll(
112
- ctx.notifications,
113
- async (limit) => {
114
- const result = await ctx.queryClient.fetchQuery({
115
- ...queryFor(targetValue, 1, limit),
116
- retry: false
117
- });
118
- return result.data;
119
- },
120
- (ids) => {
121
- inner.select(ids);
122
- mirror();
123
- },
124
- () => destroyed,
125
- opts?.limit
126
- );
127
- };
128
- const refetch = () => {
129
- if (destroyed) return;
130
- void observer?.refetch();
131
- };
132
- const maybeResetPage = () => {
133
- const resetTo = resetPageTarget(store.getSnapshot());
134
- if (resetTo === void 0) return;
135
- inner.setPage(resetTo);
136
- mirror();
137
- };
138
- const errors = createErrorAnnouncer(ctx.notifications);
139
- const onResult = (result) => {
140
- if (destroyed) return;
141
- const list = result.data;
142
- const { page, perPage } = store.getSnapshot();
143
- store.setState({
144
- data: list?.data,
145
- total: list?.total,
146
- pageInfo: list?.pageInfo,
147
- isPending: result.isPending,
148
- isFetching: result.isFetching,
149
- error: result.error ?? null,
150
- ...pageFlags(page, perPage, list?.total, list?.pageInfo)
151
- });
152
- errors.announce(result.error);
153
- maybeResetPage();
154
- };
155
- function acquire() {
156
- const unsubscribeInner = inner.subscribe(mirror);
157
- mirror();
158
- let unsubscribeObserver;
159
- if (targetValue !== void 0) {
160
- const value = targetValue;
161
- const attached = new QueryObserver(
162
- ctx.queryClient,
163
- buildOptions(value)
164
- );
165
- observer = attached;
166
- reconfigure = () => attached.setOptions(buildOptions(value));
167
- unsubscribeObserver = attached.subscribe(onResult);
168
- onResult(attached.getCurrentResult());
169
- }
170
- return () => {
171
- unsubscribeObserver?.();
172
- unsubscribeInner();
173
- clearDebounce();
174
- observer = null;
175
- reconfigure = null;
176
- };
177
- }
178
- return {
179
- ...store,
180
- setPage: through(inner.setPage),
181
- setPerPage: through(inner.setPerPage),
182
- setSort: through(inner.setSort),
183
- setFilters,
184
- setParams,
185
- showFilter: through(inner.showFilter),
186
- hideFilter: through(inner.hideFilter),
187
- select: through(inner.select),
188
- toggleItem: through(inner.toggleItem),
189
- unselectAll: through(inner.unselectAll),
190
- selectAll,
191
- refetch
192
- };
20
+ const { target, id } = options;
21
+ /** The value `target` must equal; undefined means "nothing to fetch". */
22
+ const targetValue = id ?? void 0;
23
+ const storeKey = options.storeKey ?? (targetValue === void 0 ? false : `${reference}.${target}.${targetValue}`);
24
+ const inner = createListController({
25
+ ...ctx,
26
+ notifications: void 0
27
+ }, reference, {
28
+ perPage: options.perPage ?? 25,
29
+ sort: options.sort,
30
+ filterDefaultValues: options.filterDefaultValues,
31
+ storeKey,
32
+ enabled: false
33
+ });
34
+ const initial = inner.getSnapshot();
35
+ const store = createMicroStore({
36
+ ...initial,
37
+ data: targetValue === void 0 ? [] : void 0,
38
+ total: targetValue === void 0 ? 0 : void 0,
39
+ pageInfo: void 0,
40
+ isPending: targetValue !== void 0,
41
+ isFetching: false,
42
+ error: null,
43
+ hasNextPage: targetValue === void 0 ? false : void 0,
44
+ hasPreviousPage: targetValue === void 0 ? false : void 0
45
+ }, { onSubscribed: acquire });
46
+ let destroyed = false;
47
+ store.onDestroy(() => {
48
+ destroyed = true;
49
+ clearDebounce();
50
+ inner.destroy();
51
+ });
52
+ /** Non-null exactly while attached to a record id. */
53
+ let observer = null;
54
+ /** Rebuilds the attached observer's options from current state. */
55
+ let reconfigure = null;
56
+ /** The one place sort and the permanent filter are folded into a query. */
57
+ const queryFor = (value, page, perPage) => {
58
+ const s = store.getSnapshot();
59
+ return getManyReferenceOptions(ctx, reference, {
60
+ target,
61
+ id: value,
62
+ pagination: {
63
+ page,
64
+ perPage
65
+ },
66
+ sort: s.sort.length === 1 ? s.sort[0] : s.sort,
67
+ filter: {
68
+ ...s.filter,
69
+ ...options.filter ?? {}
70
+ }
71
+ });
72
+ };
73
+ const buildOptions = (value) => {
74
+ const s = store.getSnapshot();
75
+ return {
76
+ ...queryFor(value, s.page, s.perPage),
77
+ placeholderData: (prev) => prev,
78
+ retry: false,
79
+ enabled: options.enabled ?? true
80
+ };
81
+ };
82
+ const mirror = () => {
83
+ const next = inner.getSnapshot();
84
+ const prev = store.getSnapshot();
85
+ const queryChanged = next.page !== prev.page || next.perPage !== prev.perPage || next.sort !== prev.sort || next.filter !== prev.filter;
86
+ store.setState({
87
+ page: next.page,
88
+ perPage: next.perPage,
89
+ sort: next.sort,
90
+ filter: next.filter,
91
+ displayedFilters: next.displayedFilters,
92
+ selectedIds: next.selectedIds
93
+ });
94
+ if (queryChanged) reconfigure?.();
95
+ };
96
+ /** Runs an inner action, then mirrors, so detached snapshots stay truthful. */
97
+ const through = (action) => (...args) => {
98
+ if (destroyed) return;
99
+ action(...args);
100
+ mirror();
101
+ };
102
+ let debounceTimer;
103
+ function clearDebounce() {
104
+ if (debounceTimer === void 0) return;
105
+ clearTimeout(debounceTimer);
106
+ debounceTimer = void 0;
107
+ }
108
+ const setFilters = (filter, displayedFilters, debounced = false) => {
109
+ if (destroyed) return;
110
+ clearDebounce();
111
+ const apply = () => {
112
+ inner.setFilters(filter, displayedFilters);
113
+ mirror();
114
+ };
115
+ if (!debounced) {
116
+ apply();
117
+ return;
118
+ }
119
+ debounceTimer = setTimeout(() => {
120
+ debounceTimer = void 0;
121
+ apply();
122
+ }, options.debounceMs ?? 500);
123
+ };
124
+ const setParams = (patch) => {
125
+ if (patch.filter !== void 0) clearDebounce();
126
+ through(inner.setParams)(patch);
127
+ };
128
+ const selectAll = async (opts) => {
129
+ if (targetValue === void 0) return;
130
+ return runSelectAll(ctx.notifications, async (limit) => {
131
+ return (await ctx.queryClient.fetchQuery({
132
+ ...queryFor(targetValue, 1, limit),
133
+ retry: false
134
+ })).data;
135
+ }, (ids) => {
136
+ inner.select(ids);
137
+ mirror();
138
+ }, () => destroyed, opts?.limit);
139
+ };
140
+ const refetch = () => {
141
+ if (destroyed) return;
142
+ observer?.refetch();
143
+ };
144
+ const maybeResetPage = () => {
145
+ const resetTo = resetPageTarget(store.getSnapshot());
146
+ if (resetTo === void 0) return;
147
+ inner.setPage(resetTo);
148
+ mirror();
149
+ };
150
+ const errors = createErrorAnnouncer(ctx.notifications);
151
+ const onResult = (result) => {
152
+ if (destroyed) return;
153
+ const list = result.data;
154
+ const { page, perPage } = store.getSnapshot();
155
+ store.setState({
156
+ data: list?.data,
157
+ total: list?.total,
158
+ pageInfo: list?.pageInfo,
159
+ isPending: result.isPending,
160
+ isFetching: result.isFetching,
161
+ error: result.error ?? null,
162
+ ...pageFlags(page, perPage, list?.total, list?.pageInfo)
163
+ });
164
+ errors.announce(result.error);
165
+ maybeResetPage();
166
+ };
167
+ function acquire() {
168
+ const unsubscribeInner = inner.subscribe(mirror);
169
+ mirror();
170
+ let unsubscribeObserver;
171
+ if (targetValue !== void 0) {
172
+ const value = targetValue;
173
+ const attached = new QueryObserver(ctx.queryClient, buildOptions(value));
174
+ observer = attached;
175
+ reconfigure = () => attached.setOptions(buildOptions(value));
176
+ unsubscribeObserver = attached.subscribe(onResult);
177
+ onResult(attached.getCurrentResult());
178
+ }
179
+ return () => {
180
+ unsubscribeObserver?.();
181
+ unsubscribeInner();
182
+ clearDebounce();
183
+ observer = null;
184
+ reconfigure = null;
185
+ };
186
+ }
187
+ return {
188
+ ...store,
189
+ setPage: through(inner.setPage),
190
+ setPerPage: through(inner.setPerPage),
191
+ setSort: through(inner.setSort),
192
+ setFilters,
193
+ setParams,
194
+ showFilter: through(inner.showFilter),
195
+ hideFilter: through(inner.hideFilter),
196
+ select: through(inner.select),
197
+ toggleItem: through(inner.toggleItem),
198
+ unselectAll: through(inner.unselectAll),
199
+ selectAll,
200
+ refetch
201
+ };
193
202
  }
194
- export {
195
- createReferenceManyController
196
- };
203
+ export { DEFAULT_REFERENCE_PER_PAGE, createReferenceManyController };
@@ -23,7 +23,7 @@ export interface ReferenceOneControllerState<TRecord> {
23
23
  /** True during any fetch, the first one included. */
24
24
  isFetching: boolean;
25
25
  /** The provider error of the last fetch, else null. */
26
- error: unknown;
26
+ error: Error | null;
27
27
  }
28
28
  export interface ReferenceOneController<TRecord> extends MicroStore<ReferenceOneControllerState<TRecord>> {
29
29
  refetch(): void;
@@ -1,60 +1,67 @@
1
- import { QueryObserver } from "@tanstack/query-core";
2
1
  import { getManyReferenceOptions } from "../data/query-options.js";
3
2
  import { createMicroStore } from "../store/micro-store.js";
3
+ import { QueryObserver } from "@tanstack/query-core";
4
+ // src/controller/reference-one-controller.ts
5
+ /**
6
+ * The first row whose `target` equals `id`, read through `getManyReference`
7
+ * as a page of one. Errors are exposed in state and never notified: the field
8
+ * shows an error node instead.
9
+ *
10
+ * Lifecycle (Task 4R): construction is inert; the observer is acquired on the
11
+ * first subscriber and released on the last unsubscribe.
12
+ */
4
13
  function createReferenceOneController(ctx, reference, options) {
5
- const targetValue = options.id ?? void 0;
6
- const store = createMicroStore(
7
- {
8
- referenceRecord: void 0,
9
- isPending: targetValue !== void 0,
10
- isFetching: false,
11
- error: null
12
- },
13
- { onSubscribed: acquire }
14
- );
15
- let destroyed = false;
16
- store.onDestroy(() => {
17
- destroyed = true;
18
- });
19
- let observer = null;
20
- const onResult = (result) => {
21
- store.setState({
22
- referenceRecord: result.data?.data[0],
23
- isPending: result.isPending,
24
- isFetching: result.isFetching,
25
- error: result.error ?? null
26
- });
27
- };
28
- function acquire() {
29
- if (targetValue === void 0) return void 0;
30
- const attached = new QueryObserver(
31
- ctx.queryClient,
32
- {
33
- ...getManyReferenceOptions(ctx, reference, {
34
- target: options.target,
35
- id: targetValue,
36
- pagination: { page: 1, perPage: 1 },
37
- ...options.sort === void 0 ? {} : { sort: options.sort },
38
- filter: { ...options.filter ?? {} }
39
- }),
40
- retry: false,
41
- enabled: options.enabled ?? true
42
- }
43
- );
44
- observer = attached;
45
- const unsubscribe = attached.subscribe(onResult);
46
- onResult(attached.getCurrentResult());
47
- return () => {
48
- unsubscribe();
49
- observer = null;
50
- };
51
- }
52
- const refetch = () => {
53
- if (destroyed) return;
54
- void observer?.refetch();
55
- };
56
- return { ...store, refetch };
14
+ const targetValue = options.id ?? void 0;
15
+ const store = createMicroStore({
16
+ referenceRecord: void 0,
17
+ isPending: targetValue !== void 0,
18
+ isFetching: false,
19
+ error: null
20
+ }, { onSubscribed: acquire });
21
+ let destroyed = false;
22
+ store.onDestroy(() => {
23
+ destroyed = true;
24
+ });
25
+ let observer = null;
26
+ const onResult = (result) => {
27
+ store.setState({
28
+ referenceRecord: result.data?.data[0],
29
+ isPending: result.isPending,
30
+ isFetching: result.isFetching,
31
+ error: result.error ?? null
32
+ });
33
+ };
34
+ function acquire() {
35
+ if (targetValue === void 0) return void 0;
36
+ const attached = new QueryObserver(ctx.queryClient, {
37
+ ...getManyReferenceOptions(ctx, reference, {
38
+ target: options.target,
39
+ id: targetValue,
40
+ pagination: {
41
+ page: 1,
42
+ perPage: 1
43
+ },
44
+ ...options.sort === void 0 ? {} : { sort: options.sort },
45
+ filter: { ...options.filter ?? {} }
46
+ }),
47
+ retry: false,
48
+ enabled: options.enabled ?? true
49
+ });
50
+ observer = attached;
51
+ const unsubscribe = attached.subscribe(onResult);
52
+ onResult(attached.getCurrentResult());
53
+ return () => {
54
+ unsubscribe();
55
+ observer = null;
56
+ };
57
+ }
58
+ const refetch = () => {
59
+ if (destroyed) return;
60
+ observer?.refetch();
61
+ };
62
+ return {
63
+ ...store,
64
+ refetch
65
+ };
57
66
  }
58
- export {
59
- createReferenceOneController
60
- };
67
+ export { createReferenceOneController };