@chelone/core 0.5.0 → 0.7.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 (253) hide show
  1. package/dist/assistant/assistant-session.d.ts +28 -0
  2. package/dist/assistant/assistant-session.js +208 -0
  3. package/dist/assistant/echo-transport.d.ts +7 -0
  4. package/dist/assistant/echo-transport.js +28 -0
  5. package/dist/assistant/http-transport.d.ts +10 -0
  6. package/dist/assistant/http-transport.js +98 -0
  7. package/dist/assistant/types.d.ts +54 -0
  8. package/dist/auth/access-control.d.ts +13 -5
  9. package/dist/auth/access-control.js +25 -21
  10. package/dist/auth/auth-options.d.ts +21 -17
  11. package/dist/auth/auth-options.js +80 -48
  12. package/dist/auth/permission-matrix.d.ts +12 -0
  13. package/dist/auth/permission-matrix.js +67 -0
  14. package/dist/auth/types.d.ts +14 -4
  15. package/dist/block-editor/block-document.d.ts +60 -0
  16. package/dist/block-editor/block-document.js +143 -0
  17. package/dist/block-editor/data-blocks.d.ts +63 -0
  18. package/dist/block-editor/data-blocks.js +117 -0
  19. package/dist/chart/chart-data.d.ts +25 -0
  20. package/dist/chart/chart-data.js +61 -0
  21. package/dist/command-menu/hotkey.js +58 -47
  22. package/dist/command-menu/recents.js +8 -11
  23. package/dist/command-menu/registry.d.ts +9 -5
  24. package/dist/command-menu/registry.js +36 -46
  25. package/dist/controller/calendar-dates.js +82 -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 +42 -0
  35. package/dist/controller/in-place-editor.js +42 -0
  36. package/dist/controller/infinite-list-controller.js +220 -190
  37. package/dist/controller/inspector.d.ts +49 -0
  38. package/dist/controller/inspector.js +64 -0
  39. package/dist/controller/job-monitor.d.ts +41 -0
  40. package/dist/controller/job-monitor.js +49 -0
  41. package/dist/controller/kanban-board.d.ts +2 -3
  42. package/dist/controller/kanban-board.js +43 -24
  43. package/dist/controller/list-controller.d.ts +5 -5
  44. package/dist/controller/list-controller.js +444 -400
  45. package/dist/controller/list-params.js +96 -87
  46. package/dist/controller/pivot-grid.d.ts +36 -0
  47. package/dist/controller/pivot-grid.js +99 -0
  48. package/dist/controller/prev-next.d.ts +2 -2
  49. package/dist/controller/prev-next.js +74 -54
  50. package/dist/controller/record-controllers.d.ts +14 -6
  51. package/dist/controller/record-controllers.js +288 -257
  52. package/dist/controller/record-diff.d.ts +21 -0
  53. package/dist/controller/record-diff.js +50 -0
  54. package/dist/controller/record-timeline.d.ts +35 -0
  55. package/dist/controller/record-timeline.js +48 -0
  56. package/dist/controller/record-tree.js +57 -47
  57. package/dist/controller/record-workflow.d.ts +38 -16
  58. package/dist/controller/record-workflow.js +85 -64
  59. package/dist/controller/reference-many-controller.js +198 -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 +2 -1
  65. package/dist/csv/export.js +43 -40
  66. package/dist/csv/import-controller.js +185 -169
  67. package/dist/csv/match-headers.js +29 -22
  68. package/dist/csv/parse-with.js +60 -54
  69. package/dist/csv/run-import.js +69 -72
  70. package/dist/csv/validate-rows.d.ts +1 -1
  71. package/dist/csv/validate-rows.js +65 -52
  72. package/dist/data/cache-ops.d.ts +5 -4
  73. package/dist/data/cache-ops.js +208 -190
  74. package/dist/data/combine-data-providers.js +25 -23
  75. package/dist/data/data-provider-log.d.ts +45 -0
  76. package/dist/data/data-provider-log.js +93 -0
  77. package/dist/data/errors.js +14 -10
  78. package/dist/data/fake-provider.d.ts +6 -3
  79. package/dist/data/fake-provider.js +129 -114
  80. package/dist/data/fetch-utils.js +61 -44
  81. package/dist/data/filter-branch.d.ts +5 -0
  82. package/dist/data/filter-branch.js +10 -0
  83. package/dist/data/get-many-aggregate.js +84 -76
  84. package/dist/data/http-error.js +17 -12
  85. package/dist/data/keys.js +50 -36
  86. package/dist/data/mutation-options.d.ts +14 -9
  87. package/dist/data/mutation-options.js +93 -109
  88. package/dist/data/query-options.js +73 -68
  89. package/dist/data/remove-empty.d.ts +3 -0
  90. package/dist/data/remove-empty.js +11 -0
  91. package/dist/data/undoable.d.ts +2 -2
  92. package/dist/data/undoable.js +190 -174
  93. package/dist/data/with-lifecycle-callbacks.d.ts +45 -17
  94. package/dist/data/with-lifecycle-callbacks.js +66 -52
  95. package/dist/environment/application-update-store.js +58 -55
  96. package/dist/environment/offline-store.js +21 -21
  97. package/dist/form/array-binding.d.ts +30 -11
  98. package/dist/form/array-binding.js +184 -166
  99. package/dist/form/choice-ids.d.ts +7 -0
  100. package/dist/form/choice-ids.js +11 -0
  101. package/dist/form/choices-controller.d.ts +3 -2
  102. package/dist/form/choices-controller.js +302 -275
  103. package/dist/form/date-format.d.ts +38 -0
  104. package/dist/form/date-format.js +89 -0
  105. package/dist/form/dirty-values.d.ts +2 -2
  106. package/dist/form/dirty-values.js +22 -11
  107. package/dist/form/duration.d.ts +22 -0
  108. package/dist/form/duration.js +49 -0
  109. package/dist/form/edit-create-form.d.ts +12 -5
  110. package/dist/form/edit-create-form.js +243 -188
  111. package/dist/form/event-value.d.ts +6 -0
  112. package/dist/form/event-value.js +17 -0
  113. package/dist/form/field-error.d.ts +14 -0
  114. package/dist/form/field-error.js +28 -0
  115. package/dist/form/field-name.d.ts +20 -0
  116. package/dist/form/field-name.js +6 -0
  117. package/dist/form/filter-form-values.js +63 -49
  118. package/dist/form/form-controller.d.ts +9 -1
  119. package/dist/form/form-controller.js +165 -160
  120. package/dist/form/form-groups.d.ts +4 -3
  121. package/dist/form/form-groups.js +79 -79
  122. package/dist/form/form-handle.d.ts +175 -0
  123. package/dist/form/form-handle.js +68 -0
  124. package/dist/form/input-binding.d.ts +56 -31
  125. package/dist/form/input-binding.js +169 -118
  126. package/dist/form/input-values.d.ts +130 -0
  127. package/dist/form/input-values.js +177 -0
  128. package/dist/form/json-text.js +51 -37
  129. package/dist/form/save-pipeline.d.ts +6 -8
  130. package/dist/form/save-pipeline.js +91 -82
  131. package/dist/form/set-form-values.d.ts +6 -0
  132. package/dist/form/set-form-values.js +10 -0
  133. package/dist/form/source.js +97 -65
  134. package/dist/form/text-of.d.ts +2 -0
  135. package/dist/form/text-of.js +7 -0
  136. package/dist/form/validators.d.ts +58 -19
  137. package/dist/form/validators.js +97 -71
  138. package/dist/geo/bbox.d.ts +5 -4
  139. package/dist/geo/bbox.js +69 -48
  140. package/dist/geo/bounds.d.ts +14 -3
  141. package/dist/geo/bounds.js +98 -68
  142. package/dist/geo/constraints.js +88 -93
  143. package/dist/geo/coordinates.js +31 -23
  144. package/dist/geo/editor.d.ts +13 -13
  145. package/dist/geo/editor.js +176 -153
  146. package/dist/geo/geocoding.d.ts +4 -4
  147. package/dist/geo/geocoding.js +38 -41
  148. package/dist/geo/geojson.d.ts +12 -74
  149. package/dist/geo/geojson.js +52 -40
  150. package/dist/geo/geometry-math.d.ts +4 -4
  151. package/dist/geo/geometry-math.js +100 -93
  152. package/dist/geo/index.d.ts +7 -6
  153. package/dist/geo/map-layers.d.ts +42 -7
  154. package/dist/geo/map-layers.js +88 -62
  155. package/dist/geo/map-style.js +31 -32
  156. package/dist/geo/messages.js +48 -45
  157. package/dist/geo/nominatim.js +88 -80
  158. package/dist/geo/osm-presets.d.ts +4 -3
  159. package/dist/geo/osm-presets.js +108 -86
  160. package/dist/geo/osm-tags.js +14 -12
  161. package/dist/geo/overpass.d.ts +14 -2
  162. package/dist/geo/overpass.js +83 -90
  163. package/dist/geo/shape-value.d.ts +9 -5
  164. package/dist/geo/shape-value.js +169 -146
  165. package/dist/geo/snap-to-roads.d.ts +3 -3
  166. package/dist/geo/snap-to-roads.js +55 -50
  167. package/dist/geo/throttle.js +36 -39
  168. package/dist/guards.d.ts +37 -0
  169. package/dist/guards.js +62 -0
  170. package/dist/i18n/default-messages.js +34 -29
  171. package/dist/i18n/english-messages.js +187 -189
  172. package/dist/i18n/i18n.d.ts +8 -2
  173. package/dist/i18n/i18n.js +47 -48
  174. package/dist/i18n/inflection.d.ts +34 -0
  175. package/dist/i18n/inflection.js +82 -0
  176. package/dist/i18n/messages.d.ts +10 -2
  177. package/dist/i18n/messages.js +17 -4
  178. package/dist/i18n/missing-keys.d.ts +19 -0
  179. package/dist/i18n/missing-keys.js +68 -0
  180. package/dist/i18n/translatable.d.ts +16 -1
  181. package/dist/i18n/translatable.js +40 -47
  182. package/dist/index.d.ts +96 -22
  183. package/dist/index.js +52 -257
  184. package/dist/json/deep-equal.d.ts +8 -0
  185. package/dist/json/deep-equal.js +24 -0
  186. package/dist/json/strip-json-comments.js +38 -42
  187. package/dist/noop.d.ts +2 -0
  188. package/dist/noop.js +4 -0
  189. package/dist/notification/error-message.d.ts +2 -0
  190. package/dist/notification/error-message.js +10 -0
  191. package/dist/notification/notification-store.d.ts +3 -2
  192. package/dist/notification/notification-store.js +41 -45
  193. package/dist/onboarding-tour/placement.d.ts +34 -0
  194. package/dist/onboarding-tour/placement.js +67 -0
  195. package/dist/onboarding-tour/tour-controller.d.ts +25 -0
  196. package/dist/onboarding-tour/tour-controller.js +50 -0
  197. package/dist/realtime/add-events-for-mutations.js +75 -81
  198. package/dist/realtime/announce-lock.d.ts +7 -0
  199. package/dist/realtime/announce-lock.js +18 -0
  200. package/dist/realtime/broadcast-channel-transport.d.ts +14 -0
  201. package/dist/realtime/broadcast-channel-transport.js +69 -0
  202. package/dist/realtime/copy-members.js +22 -14
  203. package/dist/realtime/in-memory-lock-provider.js +41 -60
  204. package/dist/realtime/live.js +95 -101
  205. package/dist/realtime/lock-options.js +16 -16
  206. package/dist/realtime/lock-session.js +91 -78
  207. package/dist/realtime/memory-transport.js +26 -67
  208. package/dist/realtime/presence.js +120 -122
  209. package/dist/realtime/realtime-data-provider.d.ts +10 -0
  210. package/dist/realtime/realtime-data-provider.js +45 -42
  211. package/dist/realtime/reconnect-backoff.d.ts +15 -0
  212. package/dist/realtime/reconnect-backoff.js +17 -0
  213. package/dist/realtime/sse-transport.d.ts +31 -0
  214. package/dist/realtime/sse-transport.js +235 -0
  215. package/dist/realtime/status-store.js +23 -28
  216. package/dist/realtime/topic-hub.d.ts +24 -0
  217. package/dist/realtime/topic-hub.js +66 -0
  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 +232 -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 +16 -1
  236. package/dist/rich-text/rich-text-extras.d.ts +132 -0
  237. package/dist/rich-text/rich-text-extras.js +179 -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 +62 -0
  241. package/dist/rich-text/rich-text.js +142 -0
  242. package/dist/router/location.d.ts +22 -4
  243. package/dist/router/location.js +41 -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/package.json +13 -11
@@ -1,196 +1,202 @@
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
+ var DEFAULT_PER_PAGE = 25;
7
+ /**
8
+ * A server-paged list of the rows whose `target` equals `id`, read through
9
+ * `getManyReference`. Params and selection live in an inner list controller
10
+ * (which never fetches); this controller owns the data observer.
11
+ *
12
+ * Lifecycle (Task 4R) is the list controller's: construction is inert, the
13
+ * first subscriber acquires the inner controller and the observer, the last
14
+ * unsubscribe releases both. `observer.setOptions` runs only when the inner
15
+ * params change, never from the result subscription; out-of-range page
16
+ * resets go through the inner `setPage` and only on SETTLED results.
17
+ */
6
18
  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
- };
19
+ const { target, id } = options;
20
+ /** The value `target` must equal; undefined means "nothing to fetch". */
21
+ const targetValue = id ?? void 0;
22
+ const storeKey = options.storeKey ?? (targetValue === void 0 ? false : `${reference}.${target}.${targetValue}`);
23
+ const inner = createListController({
24
+ ...ctx,
25
+ notifications: void 0
26
+ }, reference, {
27
+ perPage: options.perPage ?? DEFAULT_PER_PAGE,
28
+ sort: options.sort,
29
+ filterDefaultValues: options.filterDefaultValues,
30
+ storeKey,
31
+ enabled: false
32
+ });
33
+ const initial = inner.getSnapshot();
34
+ const store = createMicroStore({
35
+ ...initial,
36
+ data: targetValue === void 0 ? [] : void 0,
37
+ total: targetValue === void 0 ? 0 : void 0,
38
+ pageInfo: void 0,
39
+ isPending: targetValue !== void 0,
40
+ isFetching: false,
41
+ error: null,
42
+ hasNextPage: targetValue === void 0 ? false : void 0,
43
+ hasPreviousPage: targetValue === void 0 ? false : void 0
44
+ }, { onSubscribed: acquire });
45
+ let destroyed = false;
46
+ store.onDestroy(() => {
47
+ destroyed = true;
48
+ clearDebounce();
49
+ inner.destroy();
50
+ });
51
+ /** Non-null exactly while attached to a record id. */
52
+ let observer = null;
53
+ /** Rebuilds the attached observer's options from current state. */
54
+ let reconfigure = null;
55
+ /** The one place sort and the permanent filter are folded into a query. */
56
+ const queryFor = (value, page, perPage) => {
57
+ const s = store.getSnapshot();
58
+ return getManyReferenceOptions(ctx, reference, {
59
+ target,
60
+ id: value,
61
+ pagination: {
62
+ page,
63
+ perPage
64
+ },
65
+ sort: s.sort.length === 1 ? s.sort[0] : s.sort,
66
+ filter: {
67
+ ...s.filter,
68
+ ...options.filter ?? {}
69
+ }
70
+ });
71
+ };
72
+ const buildOptions = (value) => {
73
+ const s = store.getSnapshot();
74
+ return {
75
+ ...queryFor(value, s.page, s.perPage),
76
+ placeholderData: (prev) => prev,
77
+ retry: false,
78
+ enabled: options.enabled ?? true
79
+ };
80
+ };
81
+ const mirror = () => {
82
+ const next = inner.getSnapshot();
83
+ const prev = store.getSnapshot();
84
+ const queryChanged = next.page !== prev.page || next.perPage !== prev.perPage || next.sort !== prev.sort || next.filter !== prev.filter;
85
+ store.setState({
86
+ page: next.page,
87
+ perPage: next.perPage,
88
+ sort: next.sort,
89
+ filter: next.filter,
90
+ displayedFilters: next.displayedFilters,
91
+ selectedIds: next.selectedIds
92
+ });
93
+ if (queryChanged) reconfigure?.();
94
+ };
95
+ /** Runs an inner action, then mirrors, so detached snapshots stay truthful. */
96
+ const through = (action) => (...args) => {
97
+ if (destroyed) return;
98
+ action(...args);
99
+ mirror();
100
+ };
101
+ let debounceTimer;
102
+ function clearDebounce() {
103
+ if (debounceTimer === void 0) return;
104
+ clearTimeout(debounceTimer);
105
+ debounceTimer = void 0;
106
+ }
107
+ const setFilters = (filter, displayedFilters, debounced = false) => {
108
+ if (destroyed) return;
109
+ clearDebounce();
110
+ const apply = () => {
111
+ inner.setFilters(filter, displayedFilters);
112
+ mirror();
113
+ };
114
+ if (!debounced) {
115
+ apply();
116
+ return;
117
+ }
118
+ debounceTimer = setTimeout(() => {
119
+ debounceTimer = void 0;
120
+ apply();
121
+ }, options.debounceMs ?? 500);
122
+ };
123
+ const setParams = (patch) => {
124
+ if (patch.filter !== void 0) clearDebounce();
125
+ through(inner.setParams)(patch);
126
+ };
127
+ const selectAll = async (opts) => {
128
+ if (targetValue === void 0) return;
129
+ return runSelectAll(ctx.notifications, async (limit) => {
130
+ return (await ctx.queryClient.fetchQuery({
131
+ ...queryFor(targetValue, 1, limit),
132
+ retry: false
133
+ })).data;
134
+ }, (ids) => {
135
+ inner.select(ids);
136
+ mirror();
137
+ }, () => destroyed, opts?.limit);
138
+ };
139
+ const refetch = () => {
140
+ if (destroyed) return;
141
+ observer?.refetch();
142
+ };
143
+ const maybeResetPage = () => {
144
+ const resetTo = resetPageTarget(store.getSnapshot());
145
+ if (resetTo === void 0) return;
146
+ inner.setPage(resetTo);
147
+ mirror();
148
+ };
149
+ const errors = createErrorAnnouncer(ctx.notifications);
150
+ const onResult = (result) => {
151
+ if (destroyed) return;
152
+ const list = result.data;
153
+ const { page, perPage } = store.getSnapshot();
154
+ store.setState({
155
+ data: list?.data,
156
+ total: list?.total,
157
+ pageInfo: list?.pageInfo,
158
+ isPending: result.isPending,
159
+ isFetching: result.isFetching,
160
+ error: result.error ?? null,
161
+ ...pageFlags(page, perPage, list?.total, list?.pageInfo)
162
+ });
163
+ errors.announce(result.error);
164
+ maybeResetPage();
165
+ };
166
+ function acquire() {
167
+ const unsubscribeInner = inner.subscribe(mirror);
168
+ mirror();
169
+ let unsubscribeObserver;
170
+ if (targetValue !== void 0) {
171
+ const value = targetValue;
172
+ const attached = new QueryObserver(ctx.queryClient, buildOptions(value));
173
+ observer = attached;
174
+ reconfigure = () => attached.setOptions(buildOptions(value));
175
+ unsubscribeObserver = attached.subscribe(onResult);
176
+ onResult(attached.getCurrentResult());
177
+ }
178
+ return () => {
179
+ unsubscribeObserver?.();
180
+ unsubscribeInner();
181
+ clearDebounce();
182
+ observer = null;
183
+ reconfigure = null;
184
+ };
185
+ }
186
+ return {
187
+ ...store,
188
+ setPage: through(inner.setPage),
189
+ setPerPage: through(inner.setPerPage),
190
+ setSort: through(inner.setSort),
191
+ setFilters,
192
+ setParams,
193
+ showFilter: through(inner.showFilter),
194
+ hideFilter: through(inner.hideFilter),
195
+ select: through(inner.select),
196
+ toggleItem: through(inner.toggleItem),
197
+ unselectAll: through(inner.unselectAll),
198
+ selectAll,
199
+ refetch
200
+ };
193
201
  }
194
- export {
195
- createReferenceManyController
196
- };
202
+ export { 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 };