@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,45 +1,53 @@
1
+ // src/router/match.ts
2
+ /** Compile a pattern into a case-insensitive regex + ordered param names. */
1
3
  function compilePattern(pattern) {
2
- const paramNames = [];
3
- let regexpSource = `^${pattern.replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(/\\\*/g, "*").replace(/^\/*/, "/").replace(/\/+$/, "").replace(/\/:(\w+)/g, (_m, name) => {
4
- paramNames.push(name);
5
- return "/([^\\/]+)";
6
- })}`;
7
- if (pattern.endsWith("*")) {
8
- paramNames.push("*");
9
- regexpSource += pattern === "*" || pattern === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
10
- } else {
11
- regexpSource += "\\/*$";
12
- }
13
- return { matcher: new RegExp(regexpSource, "i"), paramNames };
4
+ const paramNames = [];
5
+ let regexpSource = `^${pattern.replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(/\\\*/g, "*").replace(/^\/*/, "/").replace(/\/+$/, "").replace(/\/:(\w+)/g, (_m, name) => {
6
+ paramNames.push(name);
7
+ return "/([^\\/]+)";
8
+ })}`;
9
+ if (pattern.endsWith("*")) {
10
+ paramNames.push("*");
11
+ regexpSource += pattern === "*" || pattern === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
12
+ } else regexpSource += "\\/*$";
13
+ return {
14
+ matcher: new RegExp(regexpSource, "i"),
15
+ paramNames
16
+ };
14
17
  }
18
+ /** Safe URI-decode: fall back to the raw value on a malformed sequence. */
15
19
  function safeDecode(value) {
16
- try {
17
- return decodeURIComponent(value);
18
- } catch {
19
- return value;
20
- }
20
+ try {
21
+ return decodeURIComponent(value);
22
+ } catch {
23
+ return value;
24
+ }
21
25
  }
26
+ /**
27
+ * Match `pathname` against `pattern`. Returns the captured params, the matched
28
+ * `pathname`, and `pathnameBase` (the match minus any splat remainder), or
29
+ * `null` when the pattern does not match.
30
+ */
22
31
  function matchPath(pattern, pathname) {
23
- const { matcher, paramNames } = compilePattern(pattern);
24
- const match = pathname.match(matcher);
25
- if (!match) return null;
26
- const matchedPathname = match[0];
27
- const hasSplat = paramNames[paramNames.length - 1] === "*";
28
- let pathnameBase = matchedPathname;
29
- const params = {};
30
- paramNames.forEach((name, index) => {
31
- if (name === "*") {
32
- const splat = match[index + 1] ?? "";
33
- pathnameBase = matchedPathname.slice(0, matchedPathname.length - splat.length).replace(/(.)\/+$/, "$1");
34
- params["*"] = splat;
35
- } else {
36
- params[name] = safeDecode(match[index + 1] ?? "");
37
- }
38
- });
39
- if (hasSplat && pathnameBase === "") pathnameBase = "/";
40
- const normalizedPathname = matchedPathname.length > 1 ? matchedPathname.replace(/\/+$/, "") : matchedPathname;
41
- return { params, pathname: normalizedPathname || "/", pathnameBase };
32
+ const { matcher, paramNames } = compilePattern(pattern);
33
+ const match = pathname.match(matcher);
34
+ if (!match) return null;
35
+ const matchedPathname = match[0];
36
+ const hasSplat = paramNames[paramNames.length - 1] === "*";
37
+ let pathnameBase = matchedPathname;
38
+ const params = {};
39
+ paramNames.forEach((name, index) => {
40
+ if (name === "*") {
41
+ const splat = match[index + 1] ?? "";
42
+ pathnameBase = matchedPathname.slice(0, matchedPathname.length - splat.length).replace(/(.)\/+$/, "$1");
43
+ params["*"] = splat;
44
+ } else params[name] = safeDecode(match[index + 1] ?? "");
45
+ });
46
+ if (hasSplat && pathnameBase === "") pathnameBase = "/";
47
+ return {
48
+ params,
49
+ pathname: (matchedPathname.length > 1 ? matchedPathname.replace(/\/+$/, "") : matchedPathname) || "/",
50
+ pathnameBase
51
+ };
42
52
  }
43
- export {
44
- matchPath
45
- };
53
+ export { matchPath };
@@ -1,40 +1,46 @@
1
+ // src/router/paths.ts
2
+ /**
3
+ * Collapse runs of `/` into a single `/`, WITHOUT touching a `scheme://`
4
+ * authority separator. Matches ra-core's `removeDoubleSlashes` for internal
5
+ * paths while staying safe on the rare absolute URL.
6
+ */
1
7
  function removeDoubleSlashes(path) {
2
- const schemeMatch = path.match(/^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//);
3
- if (schemeMatch) {
4
- const prefix = schemeMatch[0];
5
- return prefix + path.slice(prefix.length).replace(/\/\/+/g, "/");
6
- }
7
- return path.replace(/\/\/+/g, "/");
8
+ const schemeMatch = path.match(/^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//);
9
+ if (schemeMatch) {
10
+ const prefix = schemeMatch[0];
11
+ return prefix + path.slice(prefix.length).replace(/\/\/+/g, "/");
12
+ }
13
+ return path.replace(/\/\/+/g, "/");
8
14
  }
15
+ /**
16
+ * Prefix `path` with `basename`, collapsing the seam and any trailing basename
17
+ * slash. An empty basename is a no-op. The result is always slash-normalized.
18
+ */
9
19
  function joinBasename(path, basename = "") {
10
- const joined = basename ? `${basename}/${path}` : path;
11
- const collapsed = removeDoubleSlashes(joined);
12
- return collapsed.length > 1 ? collapsed.replace(/\/$/, "") : collapsed;
20
+ const collapsed = removeDoubleSlashes(basename ? `${basename}/${path}` : path);
21
+ return collapsed.length > 1 ? collapsed.replace(/\/$/, "") : collapsed;
13
22
  }
23
+ /**
24
+ * Build a route path for a link intent (parity §0a-11):
25
+ * `list`→`/res`, `create`→`/res/create`, `edit`→`/res/:id`, `show`→`/res/:id/show`.
26
+ * `id==null` on `edit`/`show` falls back to the list path. The id is
27
+ * `encodeURIComponent`d, and double slashes are removed from these four known
28
+ * intents. A custom string `type` is returned COMPLETELY verbatim — NO basename
29
+ * prefix, NO slash collapse — matching ra-core's `useCreatePath` `default`
30
+ * branch (the author owns the full, final path).
31
+ */
14
32
  function createPath(params, basename = "") {
15
- const { type, resource, id } = params;
16
- switch (type) {
17
- case "list":
18
- return joinBasename(`/${resource}`, basename);
19
- case "create":
20
- return joinBasename(`/${resource}/create`, basename);
21
- case "edit": {
22
- if (id == null) return joinBasename(`/${resource}`, basename);
23
- return joinBasename(`/${resource}/${encodeURIComponent(id)}`, basename);
24
- }
25
- case "show": {
26
- if (id == null) return joinBasename(`/${resource}`, basename);
27
- return joinBasename(
28
- `/${resource}/${encodeURIComponent(id)}/show`,
29
- basename
30
- );
31
- }
32
- default:
33
- return type;
34
- }
33
+ const { type, resource, id } = params;
34
+ switch (type) {
35
+ case "list": return joinBasename(`/${resource}`, basename);
36
+ case "create": return joinBasename(`/${resource}/create`, basename);
37
+ case "edit":
38
+ if (id == null) return joinBasename(`/${resource}`, basename);
39
+ return joinBasename(`/${resource}/${encodeURIComponent(id)}`, basename);
40
+ case "show":
41
+ if (id == null) return joinBasename(`/${resource}`, basename);
42
+ return joinBasename(`/${resource}/${encodeURIComponent(id)}/show`, basename);
43
+ default: return type;
44
+ }
35
45
  }
36
- export {
37
- createPath,
38
- joinBasename,
39
- removeDoubleSlashes
40
- };
46
+ export { createPath, joinBasename, removeDoubleSlashes };
@@ -13,6 +13,6 @@ export interface RecordFromLocationOptions {
13
13
  /**
14
14
  * The record at `state[stateSource]` if it is an object, else the JSON in the
15
15
  * `searchSource` param. Returns `null` for no param, invalid JSON, a repeated
16
- * param or a value that is not a plain object. Never throws.
16
+ * param or a value that is not an object. Never throws.
17
17
  */
18
18
  export declare function getRecordFromLocation(location: Pick<CheloneLocation, "state" | "searchParams">, options?: RecordFromLocationOptions): Record<string, unknown> | null;
@@ -1,19 +1,27 @@
1
- function isPlainObject(value) {
2
- return typeof value === "object" && value !== null && !Array.isArray(value);
3
- }
1
+ import { isRecord } from "../guards.js";
2
+ // src/router/record-from-location.ts
3
+ /**
4
+ * Read a seed record out of a location: the framework-free twin of ra-core's
5
+ * `useRecordFromLocation`. A router adapter passes its `CheloneLocation`;
6
+ * the clone link writes `?source=<json>`.
7
+ */
8
+ /**
9
+ * The record at `state[stateSource]` if it is an object, else the JSON in the
10
+ * `searchSource` param. Returns `null` for no param, invalid JSON, a repeated
11
+ * param or a value that is not an object. Never throws.
12
+ */
4
13
  function getRecordFromLocation(location, options = {}) {
5
- const { searchSource = "source", stateSource = "record" } = options;
6
- const fromState = isPlainObject(location.state) ? location.state[stateSource] : void 0;
7
- if (isPlainObject(fromState)) return fromState;
8
- const values = location.searchParams.getAll(searchSource);
9
- if (values.length !== 1) return null;
10
- try {
11
- const parsed = JSON.parse(values[0]);
12
- return isPlainObject(parsed) ? parsed : null;
13
- } catch {
14
- return null;
15
- }
14
+ const { searchSource = "source", stateSource = "record" } = options;
15
+ const fromState = isRecord(location.state) ? location.state[stateSource] : void 0;
16
+ if (isRecord(fromState)) return fromState;
17
+ const values = location.searchParams.getAll(searchSource);
18
+ const [value] = values;
19
+ if (values.length !== 1 || value === void 0) return null;
20
+ try {
21
+ const parsed = JSON.parse(value);
22
+ return isRecord(parsed) ? parsed : null;
23
+ } catch {
24
+ return null;
25
+ }
16
26
  }
17
- export {
18
- getRecordFromLocation
19
- };
27
+ export { getRecordFromLocation };
@@ -1,61 +1,57 @@
1
+ // src/store/adapters.ts
1
2
  function memoryStoreAdapter(initial = {}) {
2
- const map = new Map(Object.entries(initial));
3
- return {
4
- getItem: (key) => map.get(key),
5
- setItem: (key, value) => void map.set(key, value),
6
- removeItem: (key) => void map.delete(key)
7
- };
3
+ const map = new Map(Object.entries(initial));
4
+ return {
5
+ getItem: (key) => map.get(key),
6
+ setItem: (key, value) => void map.set(key, value),
7
+ removeItem: (key) => void map.delete(key)
8
+ };
8
9
  }
10
+ /** window/localStorage touched ONLY inside method bodies — module import is SSR-safe. */
9
11
  function storage() {
10
- if (typeof window === "undefined") return void 0;
11
- try {
12
- return window.localStorage;
13
- } catch {
14
- return void 0;
15
- }
12
+ if (typeof window === "undefined") return void 0;
13
+ try {
14
+ return window.localStorage;
15
+ } catch {
16
+ return;
17
+ }
16
18
  }
17
19
  function localStorageAdapter(options = {}) {
18
- const prefix = options.prefix ?? "chelone.";
19
- return {
20
- getItem(key) {
21
- const raw = storage()?.getItem(prefix + key);
22
- if (raw == null) return void 0;
23
- try {
24
- return JSON.parse(raw);
25
- } catch {
26
- return void 0;
27
- }
28
- },
29
- setItem(key, value) {
30
- try {
31
- storage()?.setItem(prefix + key, JSON.stringify(value));
32
- } catch {
33
- }
34
- },
35
- removeItem(key) {
36
- storage()?.removeItem(prefix + key);
37
- },
38
- subscribeExternal(onChange) {
39
- if (typeof window === "undefined") return () => {
40
- };
41
- const listener = (event) => {
42
- if (!event.key?.startsWith(prefix)) return;
43
- const key = event.key.slice(prefix.length);
44
- if (event.newValue == null) {
45
- onChange(key, void 0);
46
- return;
47
- }
48
- try {
49
- onChange(key, JSON.parse(event.newValue));
50
- } catch {
51
- }
52
- };
53
- window.addEventListener("storage", listener);
54
- return () => window.removeEventListener("storage", listener);
55
- }
56
- };
20
+ const prefix = options.prefix ?? "chelone.";
21
+ return {
22
+ getItem(key) {
23
+ const raw = storage()?.getItem(prefix + key);
24
+ if (raw == null) return void 0;
25
+ try {
26
+ return JSON.parse(raw);
27
+ } catch {
28
+ return;
29
+ }
30
+ },
31
+ setItem(key, value) {
32
+ try {
33
+ storage()?.setItem(prefix + key, JSON.stringify(value));
34
+ } catch {}
35
+ },
36
+ removeItem(key) {
37
+ storage()?.removeItem(prefix + key);
38
+ },
39
+ subscribeExternal(onChange) {
40
+ if (typeof window === "undefined") return () => {};
41
+ const listener = (event) => {
42
+ if (!event.key?.startsWith(prefix)) return;
43
+ const key = event.key.slice(prefix.length);
44
+ if (event.newValue == null) {
45
+ onChange(key, void 0);
46
+ return;
47
+ }
48
+ try {
49
+ onChange(key, JSON.parse(event.newValue));
50
+ } catch {}
51
+ };
52
+ window.addEventListener("storage", listener);
53
+ return () => window.removeEventListener("storage", listener);
54
+ }
55
+ };
57
56
  }
58
- export {
59
- localStorageAdapter,
60
- memoryStoreAdapter
61
- };
57
+ export { localStorageAdapter, memoryStoreAdapter };
@@ -1,61 +1,59 @@
1
+ // src/store/micro-store.ts
1
2
  function createMicroStore(initial, options) {
2
- let state = initial;
3
- let destroyed = false;
4
- let attachTeardown;
5
- const subscriptions = /* @__PURE__ */ new Set();
6
- const cleanups = /* @__PURE__ */ new Set();
7
- const detach = () => {
8
- const teardown = attachTeardown;
9
- attachTeardown = void 0;
10
- teardown?.();
11
- };
12
- return {
13
- getSnapshot: () => state,
14
- setState(update) {
15
- if (destroyed) return;
16
- const patch = typeof update === "function" ? update(state) : update;
17
- let changed = false;
18
- for (const [key, value] of Object.entries(patch)) {
19
- if (!Object.is(state[key], value)) {
20
- changed = true;
21
- break;
22
- }
23
- }
24
- if (!changed) return;
25
- state = { ...state, ...patch };
26
- for (const subscription of subscriptions) subscription.callback();
27
- },
28
- subscribe(callback) {
29
- if (destroyed) return () => {
30
- };
31
- const subscription = { callback };
32
- const isFirst = subscriptions.size === 0;
33
- subscriptions.add(subscription);
34
- if (isFirst && options?.onSubscribed) {
35
- attachTeardown = options.onSubscribed() ?? void 0;
36
- }
37
- return () => {
38
- if (!subscriptions.delete(subscription)) return;
39
- if (subscriptions.size === 0 && !destroyed) detach();
40
- };
41
- },
42
- destroy() {
43
- if (destroyed) return;
44
- destroyed = true;
45
- detach();
46
- for (const cleanup of cleanups) cleanup();
47
- cleanups.clear();
48
- subscriptions.clear();
49
- },
50
- onDestroy(cleanup) {
51
- if (destroyed) {
52
- cleanup();
53
- return;
54
- }
55
- cleanups.add(cleanup);
56
- }
57
- };
3
+ let state = initial;
4
+ let destroyed = false;
5
+ let attachTeardown;
6
+ const subscriptions = /* @__PURE__ */ new Set();
7
+ const cleanups = /* @__PURE__ */ new Set();
8
+ /** Runs the attach teardown at most once per attach cycle. */
9
+ const detach = () => {
10
+ const teardown = attachTeardown;
11
+ attachTeardown = void 0;
12
+ teardown?.();
13
+ };
14
+ return {
15
+ getSnapshot: () => state,
16
+ setState(update) {
17
+ if (destroyed) return;
18
+ const patch = typeof update === "function" ? update(state) : update;
19
+ let changed = false;
20
+ for (const key in patch) if (!Object.is(state[key], patch[key])) {
21
+ changed = true;
22
+ break;
23
+ }
24
+ if (!changed) return;
25
+ state = {
26
+ ...state,
27
+ ...patch
28
+ };
29
+ for (const subscription of subscriptions) subscription.callback();
30
+ },
31
+ subscribe(callback) {
32
+ if (destroyed) return () => {};
33
+ const subscription = { callback };
34
+ const isFirst = subscriptions.size === 0;
35
+ subscriptions.add(subscription);
36
+ if (isFirst && options?.onSubscribed) attachTeardown = options.onSubscribed() ?? void 0;
37
+ return () => {
38
+ if (!subscriptions.delete(subscription)) return;
39
+ if (subscriptions.size === 0 && !destroyed) detach();
40
+ };
41
+ },
42
+ destroy() {
43
+ if (destroyed) return;
44
+ destroyed = true;
45
+ detach();
46
+ for (const cleanup of cleanups) cleanup();
47
+ cleanups.clear();
48
+ subscriptions.clear();
49
+ },
50
+ onDestroy(cleanup) {
51
+ if (destroyed) {
52
+ cleanup();
53
+ return;
54
+ }
55
+ cleanups.add(cleanup);
56
+ }
57
+ };
58
58
  }
59
- export {
60
- createMicroStore
61
- };
59
+ export { createMicroStore };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `shallowEqual(a, b)`: shallow equality with `Object.is` semantics at the top
3
+ * level. Parity with ra-core's `shallowEqual`.
4
+ *
5
+ * - `Object.is`-equal inputs are equal, so `NaN` equals `NaN` and `0` differs
6
+ * from `-0`;
7
+ * - two objects or arrays are equal iff they have the same own enumerable keys
8
+ * and each value pair is `Object.is`-equal; nested values compare by
9
+ * reference;
10
+ * - a non-object, or `null`, that is not `Object.is`-equal is unequal.
11
+ */
12
+ export declare function shallowEqual(a: unknown, b: unknown): boolean;
@@ -0,0 +1,20 @@
1
+ // src/store/shallow-equal.ts
2
+ /**
3
+ * `shallowEqual(a, b)`: shallow equality with `Object.is` semantics at the top
4
+ * level. Parity with ra-core's `shallowEqual`.
5
+ *
6
+ * - `Object.is`-equal inputs are equal, so `NaN` equals `NaN` and `0` differs
7
+ * from `-0`;
8
+ * - two objects or arrays are equal iff they have the same own enumerable keys
9
+ * and each value pair is `Object.is`-equal; nested values compare by
10
+ * reference;
11
+ * - a non-object, or `null`, that is not `Object.is`-equal is unequal.
12
+ */
13
+ function shallowEqual(a, b) {
14
+ if (Object.is(a, b)) return true;
15
+ if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) return false;
16
+ const keysA = Object.keys(a);
17
+ if (keysA.length !== Object.keys(b).length) return false;
18
+ return keysA.every((key) => Object.hasOwn(b, key) && Object.is(Reflect.get(a, key), Reflect.get(b, key)));
19
+ }
20
+ export { shallowEqual };
@@ -1,59 +1,56 @@
1
+ // src/store/store.ts
1
2
  function createStore(adapter) {
2
- const cache = /* @__PURE__ */ new Map();
3
- const hydrated = /* @__PURE__ */ new Set();
4
- const subscribers = /* @__PURE__ */ new Map();
5
- const notify = (key) => {
6
- const set = subscribers.get(key);
7
- if (set) for (const cb of set) cb();
8
- };
9
- const apply = (key, value) => {
10
- hydrated.add(key);
11
- if (value === void 0) cache.delete(key);
12
- else cache.set(key, value);
13
- notify(key);
14
- };
15
- adapter?.subscribeExternal?.((key, value) => apply(key, value));
16
- return {
17
- getItem(key) {
18
- if (!hydrated.has(key) && adapter) {
19
- hydrated.add(key);
20
- const persisted = adapter.getItem(key);
21
- if (persisted !== void 0) cache.set(key, persisted);
22
- }
23
- return cache.get(key);
24
- },
25
- setItem(key, value) {
26
- hydrated.add(key);
27
- if (Object.is(cache.get(key), value)) return;
28
- cache.set(key, value);
29
- adapter?.setItem(key, value);
30
- notify(key);
31
- },
32
- removeItem(key) {
33
- hydrated.add(key);
34
- cache.delete(key);
35
- adapter?.removeItem(key);
36
- notify(key);
37
- },
38
- subscribe(key, callback) {
39
- let set = subscribers.get(key);
40
- if (!set) {
41
- set = /* @__PURE__ */ new Set();
42
- subscribers.set(key, set);
43
- }
44
- set.add(callback);
45
- return () => {
46
- set.delete(callback);
47
- if (set.size === 0 && subscribers.get(key) === set) {
48
- subscribers.delete(key);
49
- }
50
- };
51
- },
52
- keys() {
53
- return [...cache.keys()];
54
- }
55
- };
3
+ const cache = /* @__PURE__ */ new Map();
4
+ const hydrated = /* @__PURE__ */ new Set();
5
+ const subscribers = /* @__PURE__ */ new Map();
6
+ const notify = (key) => {
7
+ const set = subscribers.get(key);
8
+ if (set) for (const cb of set) cb();
9
+ };
10
+ const apply = (key, value) => {
11
+ hydrated.add(key);
12
+ if (value === void 0) cache.delete(key);
13
+ else cache.set(key, value);
14
+ notify(key);
15
+ };
16
+ adapter?.subscribeExternal?.((key, value) => apply(key, value));
17
+ return {
18
+ getItem(key) {
19
+ if (!hydrated.has(key) && adapter) {
20
+ hydrated.add(key);
21
+ const persisted = adapter.getItem(key);
22
+ if (persisted !== void 0) cache.set(key, persisted);
23
+ }
24
+ return cache.get(key);
25
+ },
26
+ setItem(key, value) {
27
+ hydrated.add(key);
28
+ if (Object.is(cache.get(key), value)) return;
29
+ cache.set(key, value);
30
+ adapter?.setItem(key, value);
31
+ notify(key);
32
+ },
33
+ removeItem(key) {
34
+ hydrated.add(key);
35
+ cache.delete(key);
36
+ adapter?.removeItem(key);
37
+ notify(key);
38
+ },
39
+ subscribe(key, callback) {
40
+ let set = subscribers.get(key);
41
+ if (!set) {
42
+ set = /* @__PURE__ */ new Set();
43
+ subscribers.set(key, set);
44
+ }
45
+ set.add(callback);
46
+ return () => {
47
+ set.delete(callback);
48
+ if (set.size === 0 && subscribers.get(key) === set) subscribers.delete(key);
49
+ };
50
+ },
51
+ keys() {
52
+ return [...cache.keys()];
53
+ }
54
+ };
56
55
  }
57
- export {
58
- createStore
59
- };
56
+ export { createStore };
@@ -0,0 +1,15 @@
1
+ /** Defaults both adapters' components share. They have no module of their own. */
2
+ /** Past this many columns the columns panel gets a search box. */
3
+ export declare const SEARCH_THRESHOLD = 5;
4
+ /** The longest params or result JSON the data provider devtools show for one call. */
5
+ export declare const DEVTOOLS_MAX_VALUE_LENGTH = 2000;
6
+ /** The store key the theme mode is persisted under when `storageKey` is not set. */
7
+ export declare const DEFAULT_THEME_STORAGE_KEY = "chelone-theme";
8
+ /** The DOM id of the app-bar title portal target. */
9
+ export declare const TITLE_PORTAL_ID = "chelone-title-portal";
10
+ /** How long an undoable notification stays before it commits, in milliseconds. */
11
+ export declare const UNDO_AUTO_HIDE_MS = 5000;
12
+ /** Max rows an export fetches when `maxResults` is not set. */
13
+ export declare const DEFAULT_EXPORT_MAX_RESULTS = 1000;
14
+ /** Viewports narrower than this, in pixels, count as mobile. */
15
+ export declare const MOBILE_BREAKPOINT = 768;
@@ -0,0 +1,17 @@
1
+ // src/ui-defaults.ts
2
+ /** Defaults both adapters' components share. They have no module of their own. */
3
+ /** Past this many columns the columns panel gets a search box. */
4
+ var SEARCH_THRESHOLD = 5;
5
+ /** The longest params or result JSON the data provider devtools show for one call. */
6
+ var DEVTOOLS_MAX_VALUE_LENGTH = 2e3;
7
+ /** The store key the theme mode is persisted under when `storageKey` is not set. */
8
+ var DEFAULT_THEME_STORAGE_KEY = "chelone-theme";
9
+ /** The DOM id of the app-bar title portal target. */
10
+ var TITLE_PORTAL_ID = "chelone-title-portal";
11
+ /** How long an undoable notification stays before it commits, in milliseconds. */
12
+ var UNDO_AUTO_HIDE_MS = 5e3;
13
+ /** Max rows an export fetches when `maxResults` is not set. */
14
+ var DEFAULT_EXPORT_MAX_RESULTS = 1e3;
15
+ /** Viewports narrower than this, in pixels, count as mobile. */
16
+ var MOBILE_BREAKPOINT = 768;
17
+ export { DEFAULT_EXPORT_MAX_RESULTS, DEFAULT_THEME_STORAGE_KEY, DEVTOOLS_MAX_VALUE_LENGTH, MOBILE_BREAKPOINT, SEARCH_THRESHOLD, TITLE_PORTAL_ID, UNDO_AUTO_HIDE_MS };