@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,104 +1,117 @@
1
+ import { isPlainObject, isRecord } from "../guards.js";
1
2
  import { createMicroStore } from "../store/micro-store.js";
3
+ // src/controller/saved-queries.ts
4
+ /** The key ra-core's `useSavedQueries` uses. */
2
5
  function savedQueriesKey(storeKey) {
3
- return `${storeKey}.savedQueries`;
6
+ return `${storeKey}.savedQueries`;
4
7
  }
5
- const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
6
- const isSortPayload = (value) => isRecord(value) && typeof value.field === "string" && (value.order === "ASC" || value.order === "DESC");
8
+ var isSortPayload = (value) => isRecord(value) && typeof value.field === "string" && (value.order === "ASC" || value.order === "DESC");
7
9
  function isSavedQueryValue(value) {
8
- return isRecord(value) && isRecord(value.filter) && isRecord(value.displayedFilters) && Object.values(value.displayedFilters).every(
9
- (flag) => typeof flag === "boolean"
10
- ) && Array.isArray(value.sort) && value.sort.every(isSortPayload) && typeof value.perPage === "number" && Number.isInteger(value.perPage) && value.perPage > 0;
10
+ return isRecord(value) && isRecord(value.filter) && isRecord(value.displayedFilters) && Object.values(value.displayedFilters).every((flag) => typeof flag === "boolean") && Array.isArray(value.sort) && value.sort.every(isSortPayload) && typeof value.perPage === "number" && Number.isInteger(value.perPage) && value.perPage > 0;
11
11
  }
12
+ /**
13
+ * Reads whatever storage holds: non-arrays give `[]`, malformed entries are
14
+ * dropped, labels are trimmed, and a repeated label keeps its first entry.
15
+ */
12
16
  function extractValidSavedQueries(raw) {
13
- if (!Array.isArray(raw)) return [];
14
- const seen = /* @__PURE__ */ new Set();
15
- const queries = [];
16
- for (const entry of raw) {
17
- if (!isRecord(entry) || typeof entry.label !== "string") continue;
18
- const label = entry.label.trim();
19
- if (!label || seen.has(label) || !isSavedQueryValue(entry.value)) continue;
20
- seen.add(label);
21
- queries.push({ label, value: entry.value });
22
- }
23
- return queries;
17
+ if (!Array.isArray(raw)) return [];
18
+ const seen = /* @__PURE__ */ new Set();
19
+ const queries = [];
20
+ for (const entry of raw) {
21
+ if (!isRecord(entry) || typeof entry.label !== "string") continue;
22
+ const label = entry.label.trim();
23
+ if (!label || seen.has(label) || !isSavedQueryValue(entry.value)) continue;
24
+ seen.add(label);
25
+ queries.push({
26
+ label,
27
+ value: entry.value
28
+ });
29
+ }
30
+ return queries;
24
31
  }
25
- const isPlainObject = (value) => {
26
- if (!isRecord(value)) return false;
27
- const proto = Object.getPrototypeOf(value);
28
- return proto === Object.prototype || proto === null;
29
- };
32
+ /**
33
+ * Structural equality; object key order is irrelevant, array order is not.
34
+ * Only plain objects, arrays and dates compare by content: any other class
35
+ * instance is equal to itself alone.
36
+ */
30
37
  function isDeepEqual(a, b) {
31
- if (Object.is(a, b)) return true;
32
- if (a instanceof Date || b instanceof Date) {
33
- return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
34
- }
35
- if (Array.isArray(a) || Array.isArray(b)) {
36
- return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, index) => isDeepEqual(item, b[index]));
37
- }
38
- if (!isPlainObject(a) || !isPlainObject(b)) return false;
39
- const keys = Object.keys(a);
40
- return keys.length === Object.keys(b).length && keys.every((key) => key in b && isDeepEqual(a[key], b[key]));
38
+ if (Object.is(a, b)) return true;
39
+ if (a instanceof Date || b instanceof Date) return a instanceof Date && b instanceof Date && a.getTime() === b.getTime();
40
+ if (Array.isArray(a) || Array.isArray(b)) return Array.isArray(a) && Array.isArray(b) && a.length === b.length && a.every((item, index) => isDeepEqual(item, b[index]));
41
+ if (!isPlainObject(a) || !isPlainObject(b)) return false;
42
+ const keys = Object.keys(a);
43
+ return keys.length === Object.keys(b).length && keys.every((key) => key in b && isDeepEqual(a[key], b[key]));
41
44
  }
45
+ /** Whether `current` list params are the saved query. `displayedFilters` is ignored. */
42
46
  function isSavedQueryActive(query, current) {
43
- return query.perPage === current.perPage && isDeepEqual(query.sort, current.sort) && isDeepEqual(query.filter, current.filter);
47
+ return query.perPage === current.perPage && isDeepEqual(query.sort, current.sort) && isDeepEqual(query.filter, current.filter);
44
48
  }
49
+ /**
50
+ * Named filter/sort sets persisted in the KV store.
51
+ *
52
+ * Lifecycle (Task 4R): construction is inert. The first subscriber subscribes
53
+ * to the store key and re-reads it; the last unsubscribe releases it. Detached,
54
+ * a write still updates local state, and the next attach picks up whatever the
55
+ * store holds.
56
+ */
45
57
  function createSavedQueriesController(ctx, resource, options = {}) {
46
- const key = savedQueriesKey(options.storeKey ?? resource);
47
- const kv = ctx.store;
48
- let lastRaw = kv?.getItem(key);
49
- const store = createMicroStore(
50
- { queries: extractValidSavedQueries(lastRaw) },
51
- {
52
- onSubscribed: () => {
53
- if (!kv) return;
54
- resync();
55
- return kv.subscribe(key, resync);
56
- }
57
- }
58
- );
59
- function resync() {
60
- if (!kv) return;
61
- const raw = kv.getItem(key);
62
- if (raw === lastRaw) return;
63
- lastRaw = raw;
64
- const queries = extractValidSavedQueries(raw);
65
- if (isDeepEqual(queries, store.getSnapshot().queries)) return;
66
- store.setState({ queries });
67
- }
68
- const current = () => {
69
- resync();
70
- return store.getSnapshot().queries;
71
- };
72
- const write = (queries) => {
73
- if (!kv) {
74
- store.setState({ queries });
75
- return;
76
- }
77
- kv.setItem(key, queries);
78
- resync();
79
- };
80
- const save = (label, value) => {
81
- const trimmed = label.trim();
82
- if (!trimmed) return;
83
- if (!isSavedQueryValue(value)) return;
84
- const next = { label: trimmed, value: structuredClone(value) };
85
- const queries = current();
86
- const index = queries.findIndex((query) => query.label === trimmed);
87
- write(
88
- index === -1 ? [...queries, next] : queries.map((query, i) => i === index ? next : query)
89
- );
90
- };
91
- const remove = (label) => {
92
- const trimmed = label.trim();
93
- const queries = current();
94
- if (!queries.some((query) => query.label === trimmed)) return;
95
- write(queries.filter((query) => query.label !== trimmed));
96
- };
97
- return { ...store, save, remove };
58
+ const key = savedQueriesKey(options.storeKey ?? resource);
59
+ const kv = ctx.store;
60
+ /** The raw value the state was last derived from, to skip no-op resyncs. */
61
+ let lastRaw = kv?.getItem(key);
62
+ const store = createMicroStore({ queries: extractValidSavedQueries(lastRaw) }, { onSubscribed: () => {
63
+ if (!kv) return;
64
+ resync();
65
+ return kv.subscribe(key, resync);
66
+ } });
67
+ /** Brings state in line with the store; a no-op when nothing changed. */
68
+ function resync() {
69
+ if (!kv) return;
70
+ const raw = kv.getItem(key);
71
+ if (raw === lastRaw) return;
72
+ lastRaw = raw;
73
+ const queries = extractValidSavedQueries(raw);
74
+ if (isDeepEqual(queries, store.getSnapshot().queries)) return;
75
+ store.setState({ queries });
76
+ }
77
+ /**
78
+ * Detached, nothing keeps state current, so every action starts from the
79
+ * store: a stale snapshot would write over another controller's queries.
80
+ */
81
+ const current = () => {
82
+ resync();
83
+ return store.getSnapshot().queries;
84
+ };
85
+ const write = (queries) => {
86
+ if (!kv) {
87
+ store.setState({ queries });
88
+ return;
89
+ }
90
+ kv.setItem(key, queries);
91
+ resync();
92
+ };
93
+ const save = (label, value) => {
94
+ const trimmed = label.trim();
95
+ if (!trimmed) return;
96
+ if (!isSavedQueryValue(value)) return;
97
+ const next = {
98
+ label: trimmed,
99
+ value: structuredClone(value)
100
+ };
101
+ const queries = current();
102
+ const index = queries.findIndex((query) => query.label === trimmed);
103
+ write(index === -1 ? [...queries, next] : queries.map((query, i) => i === index ? next : query));
104
+ };
105
+ const remove = (label) => {
106
+ const trimmed = label.trim();
107
+ const queries = current();
108
+ if (!queries.some((query) => query.label === trimmed)) return;
109
+ write(queries.filter((query) => query.label !== trimmed));
110
+ };
111
+ return {
112
+ ...store,
113
+ save,
114
+ remove
115
+ };
98
116
  }
99
- export {
100
- createSavedQueriesController,
101
- extractValidSavedQueries,
102
- isSavedQueryActive,
103
- savedQueriesKey
104
- };
117
+ export { createSavedQueriesController, extractValidSavedQueries, isSavedQueryActive, savedQueriesKey };
@@ -1,8 +1,12 @@
1
+ // src/controller/translate-fallback.ts
2
+ /**
3
+ * Resource-scoped notification key with global fallback. A translator that
4
+ * returns the key unchanged (Chelone i18n's missing-key behavior) means
5
+ * "not translated" — fall back to the global key.
6
+ */
1
7
  function resolveNotificationKey(translate, resource, event) {
2
- const scoped = `resources.${resource}.notifications.${event}`;
3
- if (translate && translate(scoped) !== scoped) return scoped;
4
- return `chelone.notification.${event}`;
8
+ const scoped = `resources.${resource}.notifications.${event}`;
9
+ if (translate && translate(scoped) !== scoped) return scoped;
10
+ return `chelone.notification.${event}`;
5
11
  }
6
- export {
7
- resolveNotificationKey
8
- };
12
+ export { resolveNotificationKey };
@@ -19,6 +19,7 @@
19
19
  */
20
20
  import type { DataProvider } from "../data/types.js";
21
21
  import type { LooseRecord } from "../resource/record.js";
22
+ import type { CsvRow } from "./types.js";
22
23
  /**
23
24
  * Turns a page of records into a downloaded file. Called by the export buttons
24
25
  * with the fetched rows, the raw dataProvider (for follow-up fetches), and the
@@ -30,7 +31,7 @@ export type Exporter<TRecord extends LooseRecord = LooseRecord> = (data: TRecord
30
31
  * in first-seen order — so a sparse record still lines up under the right
31
32
  * header. Returns an empty string for an empty dataset.
32
33
  */
33
- export declare function recordsToCSV(data: readonly LooseRecord[]): string;
34
+ export declare function recordsToCSV(data: readonly CsvRow[]): string;
34
35
  /**
35
36
  * Trigger a browser download of `csv` as `<filename>.csv`. A DOM-guarded no-op
36
37
  * when there is no `document` (SSR / non-jsdom test env) so callers never crash
@@ -1,47 +1,50 @@
1
+ // src/csv/export.ts
2
+ /** Escape one CSV cell: wrap in quotes + double any embedded quote when needed. */
1
3
  function csvCell(value) {
2
- if (value == null) return "";
3
- const raw = typeof value === "object" ? JSON.stringify(value) : String(value);
4
- if (/[",\n\r]/.test(raw)) {
5
- return `"${raw.replace(/"/g, '""')}"`;
6
- }
7
- return raw;
4
+ if (value == null) return "";
5
+ const raw = typeof value === "object" ? JSON.stringify(value) : String(value);
6
+ if (/[",\n\r]/.test(raw)) return `"${raw.replace(/"/g, "\"\"")}"`;
7
+ return raw;
8
8
  }
9
+ /**
10
+ * Serialize records to CSV text. Columns are the UNION of every record's keys,
11
+ * in first-seen order — so a sparse record still lines up under the right
12
+ * header. Returns an empty string for an empty dataset.
13
+ */
9
14
  function recordsToCSV(data) {
10
- if (data.length === 0) return "";
11
- const columns = [];
12
- const seen = /* @__PURE__ */ new Set();
13
- for (const record of data) {
14
- for (const key of Object.keys(record)) {
15
- if (!seen.has(key)) {
16
- seen.add(key);
17
- columns.push(key);
18
- }
19
- }
20
- }
21
- const header = columns.map(csvCell).join(",");
22
- const rows = data.map(
23
- (record) => columns.map((col) => csvCell(record[col])).join(",")
24
- );
25
- return [header, ...rows].join("\n");
15
+ if (data.length === 0) return "";
16
+ const columns = [];
17
+ const seen = /* @__PURE__ */ new Set();
18
+ for (const record of data) for (const key of Object.keys(record)) if (!seen.has(key)) {
19
+ seen.add(key);
20
+ columns.push(key);
21
+ }
22
+ return [columns.map(csvCell).join(","), ...data.map((record) => columns.map((col) => csvCell(record[col])).join(","))].join("\n");
26
23
  }
24
+ /**
25
+ * Trigger a browser download of `csv` as `<filename>.csv`. A DOM-guarded no-op
26
+ * when there is no `document` (SSR / non-jsdom test env) so callers never crash
27
+ * off-DOM.
28
+ */
27
29
  function downloadCSV(csv, filename) {
28
- if (typeof document === "undefined") return;
29
- const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
30
- const url = URL.createObjectURL(blob);
31
- const link = document.createElement("a");
32
- link.href = url;
33
- link.download = `${filename}.csv`;
34
- link.style.display = "none";
35
- document.body.appendChild(link);
36
- link.click();
37
- document.body.removeChild(link);
38
- setTimeout(() => URL.revokeObjectURL(url), 0);
30
+ if (typeof document === "undefined") return;
31
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8" });
32
+ const url = URL.createObjectURL(blob);
33
+ const link = document.createElement("a");
34
+ link.href = url;
35
+ link.download = `${filename}.csv`;
36
+ link.style.display = "none";
37
+ document.body.appendChild(link);
38
+ link.click();
39
+ document.body.removeChild(link);
40
+ setTimeout(() => URL.revokeObjectURL(url), 0);
39
41
  }
40
- const defaultExporter = (data, _dataProvider, resource) => {
41
- downloadCSV(recordsToCSV(data), resource);
42
- };
43
- export {
44
- defaultExporter,
45
- downloadCSV,
46
- recordsToCSV
42
+ /**
43
+ * The built-in exporter: serialize `data` to CSV (union-of-keys columns) and
44
+ * download it as `<resource>.csv`. Apps needing nested/related data pass a
45
+ * custom `exporter` prop instead.
46
+ */
47
+ var defaultExporter = (data, _dataProvider, resource) => {
48
+ downloadCSV(recordsToCSV(data), resource);
47
49
  };
50
+ export { defaultExporter, downloadCSV, recordsToCSV };