@chelone/core 0.6.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 (242) 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 +21 -17
  10. package/dist/auth/auth-options.js +80 -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 +60 -0
  15. package/dist/block-editor/block-document.js +143 -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.js +8 -11
  21. package/dist/command-menu/registry.d.ts +9 -5
  22. package/dist/command-menu/registry.js +36 -46
  23. package/dist/controller/calendar-dates.js +82 -113
  24. package/dist/controller/calendar-events.js +93 -88
  25. package/dist/controller/client-list-controller.d.ts +2 -2
  26. package/dist/controller/client-list-controller.js +251 -254
  27. package/dist/controller/data-table-columns.d.ts +2 -0
  28. package/dist/controller/data-table-columns.js +50 -56
  29. package/dist/controller/deep-equal.d.ts +1 -0
  30. package/dist/controller/deep-equal.js +14 -0
  31. package/dist/controller/filter-toggle.js +36 -44
  32. package/dist/controller/in-place-editor.d.ts +42 -0
  33. package/dist/controller/in-place-editor.js +42 -0
  34. package/dist/controller/infinite-list-controller.js +219 -189
  35. package/dist/controller/inspector.d.ts +49 -0
  36. package/dist/controller/inspector.js +64 -0
  37. package/dist/controller/job-monitor.d.ts +3 -4
  38. package/dist/controller/job-monitor.js +37 -47
  39. package/dist/controller/kanban-board.d.ts +2 -3
  40. package/dist/controller/kanban-board.js +43 -24
  41. package/dist/controller/list-controller.d.ts +5 -5
  42. package/dist/controller/list-controller.js +444 -400
  43. package/dist/controller/list-params.js +96 -87
  44. package/dist/controller/pivot-grid.js +89 -95
  45. package/dist/controller/prev-next.d.ts +2 -2
  46. package/dist/controller/prev-next.js +74 -54
  47. package/dist/controller/record-controllers.d.ts +14 -6
  48. package/dist/controller/record-controllers.js +288 -257
  49. package/dist/controller/record-diff.d.ts +21 -0
  50. package/dist/controller/record-diff.js +50 -0
  51. package/dist/controller/record-timeline.js +42 -30
  52. package/dist/controller/record-tree.js +57 -47
  53. package/dist/controller/record-workflow.d.ts +38 -16
  54. package/dist/controller/record-workflow.js +85 -64
  55. package/dist/controller/reference-many-controller.js +198 -192
  56. package/dist/controller/reference-one-controller.d.ts +1 -1
  57. package/dist/controller/reference-one-controller.js +63 -56
  58. package/dist/controller/saved-queries.js +104 -91
  59. package/dist/controller/translate-fallback.js +10 -6
  60. package/dist/csv/export.d.ts +2 -1
  61. package/dist/csv/export.js +43 -40
  62. package/dist/csv/import-controller.js +185 -169
  63. package/dist/csv/match-headers.js +29 -22
  64. package/dist/csv/parse-with.js +60 -54
  65. package/dist/csv/run-import.js +69 -72
  66. package/dist/csv/validate-rows.d.ts +1 -1
  67. package/dist/csv/validate-rows.js +65 -52
  68. package/dist/data/cache-ops.d.ts +5 -4
  69. package/dist/data/cache-ops.js +208 -188
  70. package/dist/data/combine-data-providers.js +25 -23
  71. package/dist/data/data-provider-log.d.ts +45 -0
  72. package/dist/data/data-provider-log.js +93 -0
  73. package/dist/data/errors.js +14 -10
  74. package/dist/data/fake-provider.d.ts +6 -3
  75. package/dist/data/fake-provider.js +129 -114
  76. package/dist/data/fetch-utils.js +61 -44
  77. package/dist/data/filter-branch.d.ts +5 -0
  78. package/dist/data/filter-branch.js +10 -0
  79. package/dist/data/get-many-aggregate.js +84 -76
  80. package/dist/data/http-error.js +17 -12
  81. package/dist/data/keys.js +50 -36
  82. package/dist/data/mutation-options.d.ts +14 -9
  83. package/dist/data/mutation-options.js +93 -109
  84. package/dist/data/query-options.js +73 -68
  85. package/dist/data/remove-empty.d.ts +3 -0
  86. package/dist/data/remove-empty.js +11 -0
  87. package/dist/data/undoable.d.ts +2 -2
  88. package/dist/data/undoable.js +190 -174
  89. package/dist/data/with-lifecycle-callbacks.d.ts +45 -17
  90. package/dist/data/with-lifecycle-callbacks.js +66 -52
  91. package/dist/environment/application-update-store.js +58 -55
  92. package/dist/environment/offline-store.js +21 -21
  93. package/dist/form/array-binding.d.ts +30 -11
  94. package/dist/form/array-binding.js +184 -166
  95. package/dist/form/choice-ids.d.ts +7 -0
  96. package/dist/form/choice-ids.js +11 -0
  97. package/dist/form/choices-controller.d.ts +3 -2
  98. package/dist/form/choices-controller.js +302 -275
  99. package/dist/form/date-format.d.ts +38 -0
  100. package/dist/form/date-format.js +89 -0
  101. package/dist/form/dirty-values.d.ts +2 -2
  102. package/dist/form/dirty-values.js +22 -11
  103. package/dist/form/duration.d.ts +22 -0
  104. package/dist/form/duration.js +49 -0
  105. package/dist/form/edit-create-form.d.ts +12 -5
  106. package/dist/form/edit-create-form.js +243 -188
  107. package/dist/form/event-value.d.ts +6 -0
  108. package/dist/form/event-value.js +17 -0
  109. package/dist/form/field-error.d.ts +14 -0
  110. package/dist/form/field-error.js +28 -0
  111. package/dist/form/field-name.d.ts +20 -0
  112. package/dist/form/field-name.js +6 -0
  113. package/dist/form/filter-form-values.js +63 -49
  114. package/dist/form/form-controller.d.ts +9 -1
  115. package/dist/form/form-controller.js +165 -160
  116. package/dist/form/form-groups.d.ts +4 -3
  117. package/dist/form/form-groups.js +79 -79
  118. package/dist/form/form-handle.d.ts +175 -0
  119. package/dist/form/form-handle.js +68 -0
  120. package/dist/form/input-binding.d.ts +56 -31
  121. package/dist/form/input-binding.js +169 -118
  122. package/dist/form/input-values.d.ts +130 -0
  123. package/dist/form/input-values.js +177 -0
  124. package/dist/form/json-text.js +51 -37
  125. package/dist/form/save-pipeline.d.ts +6 -8
  126. package/dist/form/save-pipeline.js +91 -82
  127. package/dist/form/set-form-values.d.ts +6 -0
  128. package/dist/form/set-form-values.js +10 -0
  129. package/dist/form/source.js +97 -65
  130. package/dist/form/text-of.d.ts +2 -0
  131. package/dist/form/text-of.js +7 -0
  132. package/dist/form/validators.d.ts +58 -19
  133. package/dist/form/validators.js +97 -71
  134. package/dist/geo/bbox.d.ts +5 -4
  135. package/dist/geo/bbox.js +69 -48
  136. package/dist/geo/bounds.d.ts +14 -3
  137. package/dist/geo/bounds.js +98 -68
  138. package/dist/geo/constraints.js +88 -93
  139. package/dist/geo/coordinates.js +31 -23
  140. package/dist/geo/editor.d.ts +13 -13
  141. package/dist/geo/editor.js +176 -153
  142. package/dist/geo/geocoding.d.ts +4 -4
  143. package/dist/geo/geocoding.js +38 -41
  144. package/dist/geo/geojson.d.ts +12 -74
  145. package/dist/geo/geojson.js +52 -40
  146. package/dist/geo/geometry-math.d.ts +4 -4
  147. package/dist/geo/geometry-math.js +100 -93
  148. package/dist/geo/index.d.ts +7 -6
  149. package/dist/geo/map-layers.d.ts +42 -7
  150. package/dist/geo/map-layers.js +88 -62
  151. package/dist/geo/map-style.js +31 -32
  152. package/dist/geo/messages.js +48 -45
  153. package/dist/geo/nominatim.js +88 -80
  154. package/dist/geo/osm-presets.d.ts +4 -3
  155. package/dist/geo/osm-presets.js +108 -86
  156. package/dist/geo/osm-tags.js +14 -12
  157. package/dist/geo/overpass.d.ts +14 -2
  158. package/dist/geo/overpass.js +83 -90
  159. package/dist/geo/shape-value.d.ts +9 -5
  160. package/dist/geo/shape-value.js +169 -146
  161. package/dist/geo/snap-to-roads.d.ts +3 -3
  162. package/dist/geo/snap-to-roads.js +55 -50
  163. package/dist/geo/throttle.js +36 -39
  164. package/dist/guards.d.ts +37 -0
  165. package/dist/guards.js +62 -0
  166. package/dist/i18n/default-messages.js +34 -29
  167. package/dist/i18n/english-messages.js +187 -189
  168. package/dist/i18n/i18n.d.ts +2 -2
  169. package/dist/i18n/i18n.js +47 -60
  170. package/dist/i18n/inflection.d.ts +34 -0
  171. package/dist/i18n/inflection.js +82 -0
  172. package/dist/i18n/messages.d.ts +10 -2
  173. package/dist/i18n/messages.js +17 -4
  174. package/dist/i18n/missing-keys.js +66 -62
  175. package/dist/i18n/translatable.d.ts +16 -1
  176. package/dist/i18n/translatable.js +40 -47
  177. package/dist/index.d.ts +71 -24
  178. package/dist/index.js +40 -288
  179. package/dist/json/deep-equal.d.ts +8 -0
  180. package/dist/json/deep-equal.js +24 -0
  181. package/dist/json/strip-json-comments.js +38 -42
  182. package/dist/noop.d.ts +2 -0
  183. package/dist/noop.js +4 -0
  184. package/dist/notification/error-message.d.ts +2 -0
  185. package/dist/notification/error-message.js +10 -0
  186. package/dist/notification/notification-store.d.ts +3 -2
  187. package/dist/notification/notification-store.js +41 -45
  188. package/dist/onboarding-tour/placement.js +63 -78
  189. package/dist/onboarding-tour/tour-controller.js +46 -54
  190. package/dist/realtime/add-events-for-mutations.js +75 -81
  191. package/dist/realtime/announce-lock.js +16 -13
  192. package/dist/realtime/broadcast-channel-transport.js +65 -59
  193. package/dist/realtime/copy-members.js +22 -14
  194. package/dist/realtime/in-memory-lock-provider.js +40 -48
  195. package/dist/realtime/live.js +95 -101
  196. package/dist/realtime/lock-options.js +16 -16
  197. package/dist/realtime/lock-session.js +91 -78
  198. package/dist/realtime/memory-transport.js +25 -25
  199. package/dist/realtime/presence.js +120 -122
  200. package/dist/realtime/realtime-data-provider.d.ts +10 -0
  201. package/dist/realtime/realtime-data-provider.js +45 -42
  202. package/dist/realtime/reconnect-backoff.js +17 -0
  203. package/dist/realtime/sse-transport.d.ts +31 -0
  204. package/dist/realtime/sse-transport.js +235 -0
  205. package/dist/realtime/status-store.js +23 -28
  206. package/dist/realtime/topic-hub.js +62 -71
  207. package/dist/realtime/topics.js +10 -12
  208. package/dist/realtime/types.js +10 -12
  209. package/dist/realtime/unread-count.js +21 -21
  210. package/dist/realtime/websocket-transport.d.ts +32 -0
  211. package/dist/realtime/websocket-transport.js +306 -0
  212. package/dist/resource/describe-resource.d.ts +2 -0
  213. package/dist/resource/describe-resource.js +232 -219
  214. package/dist/resource/get-at-path.js +29 -14
  215. package/dist/resource/identifier.d.ts +18 -0
  216. package/dist/resource/identifier.js +27 -0
  217. package/dist/resource/record.d.ts +4 -0
  218. package/dist/resource/record.js +10 -0
  219. package/dist/resource/register.d.ts +7 -0
  220. package/dist/resource/registry-store.d.ts +45 -15
  221. package/dist/resource/registry-store.js +67 -60
  222. package/dist/resource/registry.d.ts +10 -0
  223. package/dist/resource/registry.js +8 -4
  224. package/dist/resource/source-path.d.ts +16 -1
  225. package/dist/rich-text/rich-text-extras.d.ts +132 -0
  226. package/dist/rich-text/rich-text-extras.js +179 -0
  227. package/dist/rich-text/rich-text-image.d.ts +124 -0
  228. package/dist/rich-text/rich-text-image.js +163 -0
  229. package/dist/rich-text/rich-text.d.ts +6 -0
  230. package/dist/rich-text/rich-text.js +125 -88
  231. package/dist/router/location.d.ts +22 -4
  232. package/dist/router/location.js +41 -18
  233. package/dist/router/match.js +47 -39
  234. package/dist/router/paths.js +40 -34
  235. package/dist/router/record-from-location.d.ts +1 -1
  236. package/dist/router/record-from-location.js +25 -17
  237. package/dist/store/adapters.js +51 -55
  238. package/dist/store/micro-store.js +57 -59
  239. package/dist/store/shallow-equal.d.ts +12 -0
  240. package/dist/store/shallow-equal.js +20 -0
  241. package/dist/store/store.js +54 -57
  242. 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 };