@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,47 +1,43 @@
1
+ // src/notification/notification-store.ts
1
2
  function createNotificationStore() {
2
- let notifications = [];
3
- let nextId = 1;
4
- const subscribers = /* @__PURE__ */ new Set();
5
- const emit = () => {
6
- for (const cb of subscribers) cb();
7
- };
8
- return {
9
- notify(message, options = {}) {
10
- const id = nextId++;
11
- notifications = [
12
- ...notifications,
13
- {
14
- id,
15
- message,
16
- type: options.type ?? "info",
17
- messageArgs: options.messageArgs,
18
- undoable: options.undoable,
19
- autoHideDuration: options.autoHideDuration,
20
- undo: options.undo,
21
- onClose: options.onClose
22
- }
23
- ];
24
- emit();
25
- return id;
26
- },
27
- dismiss(id) {
28
- const next = notifications.filter((n) => n.id !== id);
29
- if (next.length === notifications.length) return;
30
- notifications = next;
31
- emit();
32
- },
33
- clear() {
34
- if (notifications.length === 0) return;
35
- notifications = [];
36
- emit();
37
- },
38
- getSnapshot: () => notifications,
39
- subscribe(callback) {
40
- subscribers.add(callback);
41
- return () => void subscribers.delete(callback);
42
- }
43
- };
3
+ let notifications = [];
4
+ let nextId = 1;
5
+ const subscribers = /* @__PURE__ */ new Set();
6
+ const emit = () => {
7
+ for (const cb of subscribers) cb();
8
+ };
9
+ return {
10
+ notify(message, options = {}) {
11
+ const id = nextId++;
12
+ notifications = [...notifications, {
13
+ id,
14
+ message,
15
+ type: options.type ?? "info",
16
+ messageArgs: options.messageArgs,
17
+ undoable: options.undoable,
18
+ autoHideDuration: options.autoHideDuration,
19
+ undo: options.undo,
20
+ onClose: options.onClose
21
+ }];
22
+ emit();
23
+ return id;
24
+ },
25
+ dismiss(id) {
26
+ const next = notifications.filter((n) => n.id !== id);
27
+ if (next.length === notifications.length) return;
28
+ notifications = next;
29
+ emit();
30
+ },
31
+ clear() {
32
+ if (notifications.length === 0) return;
33
+ notifications = [];
34
+ emit();
35
+ },
36
+ getSnapshot: () => notifications,
37
+ subscribe(callback) {
38
+ subscribers.add(callback);
39
+ return () => void subscribers.delete(callback);
40
+ }
41
+ };
44
42
  }
45
- export {
46
- createNotificationStore
47
- };
43
+ export { createNotificationStore };
@@ -0,0 +1,34 @@
1
+ /** A box in viewport pixels. The adapters measure it; core touches no DOM. */
2
+ export interface TourRect {
3
+ top: number;
4
+ left: number;
5
+ width: number;
6
+ height: number;
7
+ }
8
+ export type TourPlacement = "auto" | "top" | "bottom" | "left" | "right";
9
+ type Side = Exclude<TourPlacement, "auto">;
10
+ export interface PlaceTourCardOptions {
11
+ /** `null` centres the card, for a step that points at nothing. */
12
+ target: TourRect | null;
13
+ card: {
14
+ width: number;
15
+ height: number;
16
+ };
17
+ viewport: {
18
+ width: number;
19
+ height: number;
20
+ };
21
+ placement: TourPlacement;
22
+ /** Gap between card and target, and the least distance to a viewport edge. */
23
+ margin?: number;
24
+ }
25
+ export interface TourCardPosition {
26
+ /** The card's top-left corner. */
27
+ top: number;
28
+ left: number;
29
+ side: Side;
30
+ }
31
+ export declare function placeTourCard({ target, card, viewport, placement, margin, }: PlaceTourCardOptions): TourCardPosition;
32
+ /** A CSS `clip-path` that fills the page except the target's rect. */
33
+ export declare function tourSpotlightPath(target: TourRect): string;
34
+ export {};
@@ -0,0 +1,67 @@
1
+ // src/onboarding-tour/placement.ts
2
+ var SIDES = [
3
+ "bottom",
4
+ "top",
5
+ "right",
6
+ "left"
7
+ ];
8
+ var OPPOSITE = {
9
+ top: "bottom",
10
+ bottom: "top",
11
+ left: "right",
12
+ right: "left"
13
+ };
14
+ function placeTourCard({ target, card, viewport, placement, margin = 12 }) {
15
+ if (!target) return {
16
+ top: (viewport.height - card.height) / 2,
17
+ left: (viewport.width - card.width) / 2,
18
+ side: placement === "auto" ? "bottom" : placement
19
+ };
20
+ const room = {
21
+ top: target.top,
22
+ bottom: viewport.height - (target.top + target.height),
23
+ left: target.left,
24
+ right: viewport.width - (target.left + target.width)
25
+ };
26
+ const needed = (side) => (side === "top" || side === "bottom" ? card.height : card.width) + margin * 2;
27
+ const fits = (side) => room[side] >= needed(side);
28
+ let side;
29
+ if (placement === "auto") {
30
+ const roomiest = (candidates) => candidates.reduce((best, next) => room[next] > room[best] ? next : best);
31
+ const fitting = SIDES.filter(fits);
32
+ side = roomiest(fitting.length > 0 ? fitting : SIDES);
33
+ } else side = !fits(placement) && fits(OPPOSITE[placement]) ? OPPOSITE[placement] : placement;
34
+ const centreX = target.left + target.width / 2 - card.width / 2;
35
+ const centreY = target.top + target.height / 2 - card.height / 2;
36
+ let top;
37
+ let left;
38
+ switch (side) {
39
+ case "bottom":
40
+ top = target.top + target.height + margin;
41
+ left = centreX;
42
+ break;
43
+ case "top":
44
+ top = target.top - margin - card.height;
45
+ left = centreX;
46
+ break;
47
+ case "right":
48
+ top = centreY;
49
+ left = target.left + target.width + margin;
50
+ break;
51
+ case "left":
52
+ top = centreY;
53
+ left = target.left - margin - card.width;
54
+ }
55
+ return {
56
+ top: Math.max(margin, Math.min(top, viewport.height - card.height - margin)),
57
+ left: Math.max(margin, Math.min(left, viewport.width - card.width - margin)),
58
+ side
59
+ };
60
+ }
61
+ /** A CSS `clip-path` that fills the page except the target's rect. */
62
+ function tourSpotlightPath(target) {
63
+ const right = target.left + target.width;
64
+ const bottom = target.top + target.height;
65
+ return `polygon(0% 0%, 0% 100%, ${target.left}px 100%, ${target.left}px ${target.top}px, ${right}px ${target.top}px, ${right}px ${bottom}px, ${target.left}px ${bottom}px, ${target.left}px 100%, 100% 100%, 100% 0%)`;
66
+ }
67
+ export { placeTourCard, tourSpotlightPath };
@@ -0,0 +1,25 @@
1
+ import { type MicroStore } from "../store/micro-store.js";
2
+ import type { CheloneStore } from "../store/store.js";
3
+ /** Where the onboarding tour keeps its done flag in a `CheloneStore`. */
4
+ export declare function onboardingTourKey(id: string): string;
5
+ export interface TourState {
6
+ active: boolean;
7
+ index: number;
8
+ }
9
+ export type TourEnd = "finished" | "skipped";
10
+ export interface TourControllerOptions {
11
+ id: string;
12
+ stepCount: number;
13
+ store: CheloneStore;
14
+ /** Open on the first subscribe while the done flag is not `true`. */
15
+ autoStart?: boolean;
16
+ onComplete?: (reason: TourEnd) => void;
17
+ }
18
+ export interface TourController extends MicroStore<TourState> {
19
+ start(): void;
20
+ next(): void;
21
+ back(): void;
22
+ skip(): void;
23
+ finish(): void;
24
+ }
25
+ export declare function createTourController({ id, stepCount, store, autoStart, onComplete, }: TourControllerOptions): TourController;
@@ -0,0 +1,50 @@
1
+ import { createMicroStore } from "../store/micro-store.js";
2
+ // src/onboarding-tour/tour-controller.ts
3
+ /** Where the onboarding tour keeps its done flag in a `CheloneStore`. */
4
+ function onboardingTourKey(id) {
5
+ return `onboarding-tour.${id}.completed`;
6
+ }
7
+ function createTourController({ id, stepCount, store, autoStart = true, onComplete }) {
8
+ const key = onboardingTourKey(id);
9
+ const isDone = () => store.getItem(key) === true;
10
+ const micro = createMicroStore({
11
+ active: false,
12
+ index: 0
13
+ }, { onSubscribed() {
14
+ const openIfDue = () => {
15
+ if (autoStart && !isDone() && !micro.getSnapshot().active) start();
16
+ };
17
+ openIfDue();
18
+ return store.subscribe(key, openIfDue);
19
+ } });
20
+ function start() {
21
+ if (stepCount < 1) return;
22
+ micro.setState({
23
+ active: true,
24
+ index: 0
25
+ });
26
+ }
27
+ function end(reason) {
28
+ if (!micro.getSnapshot().active) return;
29
+ micro.setState({ active: false });
30
+ store.setItem(key, true);
31
+ onComplete?.(reason);
32
+ }
33
+ return {
34
+ ...micro,
35
+ start,
36
+ next() {
37
+ const { active, index } = micro.getSnapshot();
38
+ if (!active) return;
39
+ if (index >= stepCount - 1) end("finished");
40
+ else micro.setState({ index: index + 1 });
41
+ },
42
+ back() {
43
+ const { active, index } = micro.getSnapshot();
44
+ if (active && index > 0) micro.setState({ index: index - 1 });
45
+ },
46
+ skip: () => end("skipped"),
47
+ finish: () => end("finished")
48
+ };
49
+ }
50
+ export { createTourController, onboardingTourKey };
@@ -1,85 +1,79 @@
1
1
  import { copyMembers } from "./copy-members.js";
2
2
  import { recordTopic, resourceTopic } from "./topics.js";
3
+ // src/realtime/add-events-for-mutations.ts
4
+ /**
5
+ * Publishes after each successful write, so other clients can refresh. The
6
+ * result waits for the publishes to settle but never fails because of them;
7
+ * a rejected publish goes to `onError`. Returns the type it was given, so a
8
+ * `RealtimeDataProvider` stays one.
9
+ */
3
10
  function addEventsForMutations(base, publisher, { onError } = {}) {
4
- const publishAll = async (events) => {
5
- await Promise.all(
6
- events.map(async ([topic, event]) => {
7
- try {
8
- await publisher.publish(topic, event);
9
- } catch (error) {
10
- try {
11
- onError?.(error);
12
- } catch {
13
- }
14
- }
15
- })
16
- );
17
- };
18
- const manyEvents = (type, resource, ids) => [
19
- [resourceTopic(resource), { type, payload: { ids } }],
20
- ...ids.map(
21
- (id) => [
22
- recordTopic(resource, id),
23
- { type, payload: { id } }
24
- ]
25
- )
26
- ];
27
- const wrapped = {
28
- ...copyMembers(base),
29
- create: async (resource, params) => {
30
- const result = await base.create(resource, params);
31
- await publishAll([
32
- [
33
- resourceTopic(resource),
34
- { type: "created", payload: { ids: [result.data.id] } }
35
- ]
36
- ]);
37
- return result;
38
- },
39
- update: async (resource, params) => {
40
- const result = await base.update(resource, params);
41
- await publishAll([
42
- [
43
- recordTopic(resource, params.id),
44
- { type: "updated", payload: { id: params.id, data: result.data } }
45
- ],
46
- [
47
- resourceTopic(resource),
48
- { type: "updated", payload: { ids: [params.id] } }
49
- ]
50
- ]);
51
- return result;
52
- },
53
- delete: async (resource, params) => {
54
- const result = await base.delete(resource, params);
55
- await publishAll([
56
- [
57
- recordTopic(resource, params.id),
58
- {
59
- type: "deleted",
60
- payload: { id: params.id, previousData: result.data }
61
- }
62
- ],
63
- [
64
- resourceTopic(resource),
65
- { type: "deleted", payload: { ids: [params.id] } }
66
- ]
67
- ]);
68
- return result;
69
- },
70
- updateMany: async (resource, params) => {
71
- const result = await base.updateMany(resource, params);
72
- await publishAll(manyEvents("updated", resource, result.data));
73
- return result;
74
- },
75
- deleteMany: async (resource, params) => {
76
- const result = await base.deleteMany(resource, params);
77
- await publishAll(manyEvents("deleted", resource, result.data));
78
- return result;
79
- }
80
- };
81
- return wrapped;
11
+ const publishAll = async (events) => {
12
+ await Promise.all(events.map(async ([topic, event]) => {
13
+ try {
14
+ await publisher.publish(topic, event);
15
+ } catch (error) {
16
+ try {
17
+ onError?.(error);
18
+ } catch {}
19
+ }
20
+ }));
21
+ };
22
+ const manyEvents = (type, resource, ids) => [[resourceTopic(resource), {
23
+ type,
24
+ payload: { ids }
25
+ }], ...ids.map((id) => [recordTopic(resource, id), {
26
+ type,
27
+ payload: { id }
28
+ }])];
29
+ return {
30
+ ...copyMembers(base),
31
+ create: async (resource, params) => {
32
+ const result = await base.create(resource, params);
33
+ await publishAll([[resourceTopic(resource), {
34
+ type: "created",
35
+ payload: { ids: [result.data.id] }
36
+ }]]);
37
+ return result;
38
+ },
39
+ update: async (resource, params) => {
40
+ const result = await base.update(resource, params);
41
+ await publishAll([[recordTopic(resource, params.id), {
42
+ type: "updated",
43
+ payload: {
44
+ id: params.id,
45
+ data: result.data
46
+ }
47
+ }], [resourceTopic(resource), {
48
+ type: "updated",
49
+ payload: { ids: [params.id] }
50
+ }]]);
51
+ return result;
52
+ },
53
+ delete: async (resource, params) => {
54
+ const result = await base.delete(resource, params);
55
+ await publishAll([[recordTopic(resource, params.id), {
56
+ type: "deleted",
57
+ payload: {
58
+ id: params.id,
59
+ previousData: result.data
60
+ }
61
+ }], [resourceTopic(resource), {
62
+ type: "deleted",
63
+ payload: { ids: [params.id] }
64
+ }]]);
65
+ return result;
66
+ },
67
+ updateMany: async (resource, params) => {
68
+ const result = await base.updateMany(resource, params);
69
+ await publishAll(manyEvents("updated", resource, result.data));
70
+ return result;
71
+ },
72
+ deleteMany: async (resource, params) => {
73
+ const result = await base.deleteMany(resource, params);
74
+ await publishAll(manyEvents("deleted", resource, result.data));
75
+ return result;
76
+ }
77
+ };
82
78
  }
83
- export {
84
- addEventsForMutations
85
- };
79
+ export { addEventsForMutations };
@@ -0,0 +1,7 @@
1
+ import type { Lock, RealtimeTransport } from "./types.js";
2
+ /**
3
+ * Tells a publisher of a lock or an unlock, on the record's and the
4
+ * resource's topic. A failed publish is swallowed: the lock is already taken
5
+ * or released, so a failed announcement must not report it as failed.
6
+ */
7
+ export declare function announceLock(publisher: Pick<RealtimeTransport, "publish"> | undefined, type: "locked" | "unlocked", lock: Lock): Promise<void>;
@@ -0,0 +1,18 @@
1
+ import { lockResourceTopic, lockTopic } from "./topics.js";
2
+ // src/realtime/announce-lock.ts
3
+ /**
4
+ * Tells a publisher of a lock or an unlock, on the record's and the
5
+ * resource's topic. A failed publish is swallowed: the lock is already taken
6
+ * or released, so a failed announcement must not report it as failed.
7
+ */
8
+ async function announceLock(publisher, type, lock) {
9
+ if (!publisher) return;
10
+ const event = {
11
+ type,
12
+ payload: lock
13
+ };
14
+ try {
15
+ await Promise.all([publisher.publish(lockTopic(lock.resource, lock.recordId), event), publisher.publish(lockResourceTopic(lock.resource), event)]);
16
+ } catch {}
17
+ }
18
+ export { announceLock };
@@ -0,0 +1,14 @@
1
+ import type { RealtimeTransport, RealtimeTransportError } from "./types.js";
2
+ export interface BroadcastChannelTransportOptions {
3
+ /** The channel name; every tab that uses the same name shares events. */
4
+ channel: string;
5
+ /** Called for every transport error, like a listener from `onError()`. */
6
+ onError?: (error: RealtimeTransportError) => void;
7
+ }
8
+ /** A `RealtimeTransport` with every optional member, plus `close()`. */
9
+ export interface BroadcastChannelTransport extends Required<RealtimeTransport> {
10
+ /** Stops listening, closes the channel and sets `"disconnected"`. */
11
+ close(): void;
12
+ }
13
+ /** Carries realtime events between the tabs of one browser, with no server. */
14
+ export declare function broadcastChannelTransport({ channel, onError, }: BroadcastChannelTransportOptions): BroadcastChannelTransport;
@@ -0,0 +1,69 @@
1
+ import { createTopicHub } from "./topic-hub.js";
2
+ // src/realtime/broadcast-channel-transport.ts
3
+ function isEnvelope(value) {
4
+ if (typeof value !== "object" || value === null) return false;
5
+ if (!("topic" in value) || typeof value.topic !== "string") return false;
6
+ if (!("event" in value)) return false;
7
+ const { event } = value;
8
+ return typeof event === "object" && event !== null && "type" in event && typeof event.type === "string";
9
+ }
10
+ /** Carries realtime events between the tabs of one browser, with no server. */
11
+ function broadcastChannelTransport({ channel, onError }) {
12
+ if (typeof BroadcastChannel === "undefined") throw new Error("broadcastChannelTransport needs the global BroadcastChannel, which this environment does not have.");
13
+ const hub = createTopicHub({ onError });
14
+ const broadcast = new BroadcastChannel(channel);
15
+ let closed = false;
16
+ const onMessage = (message) => {
17
+ if (!isEnvelope(message.data)) {
18
+ hub.report({
19
+ kind: "parse_failed",
20
+ cause: message.data,
21
+ retrying: false
22
+ });
23
+ return;
24
+ }
25
+ const { topic, event } = message.data;
26
+ hub.dispatch({
27
+ payload: void 0,
28
+ ...event,
29
+ topic
30
+ });
31
+ };
32
+ broadcast.addEventListener("message", onMessage);
33
+ return {
34
+ subscribe: hub.subscribe,
35
+ async publish(topic, event) {
36
+ if (closed) throw new Error("broadcastChannelTransport is closed");
37
+ try {
38
+ broadcast.postMessage({
39
+ topic,
40
+ event
41
+ });
42
+ } catch (cause) {
43
+ hub.report({
44
+ kind: "send_failed",
45
+ topic,
46
+ cause,
47
+ retrying: false
48
+ });
49
+ throw cause;
50
+ }
51
+ hub.dispatch({
52
+ ...event,
53
+ topic
54
+ });
55
+ },
56
+ onReconnect: hub.onReconnect,
57
+ onStatusChange: hub.onStatusChange,
58
+ onError: hub.onError,
59
+ getStatus: hub.getStatus,
60
+ close() {
61
+ if (closed) return;
62
+ closed = true;
63
+ broadcast.removeEventListener("message", onMessage);
64
+ broadcast.close();
65
+ hub.setStatus("disconnected");
66
+ }
67
+ };
68
+ }
69
+ export { broadcastChannelTransport };
@@ -1,16 +1,24 @@
1
+ // src/realtime/copy-members.ts
2
+ /**
3
+ * A copy of `source` that keeps what a spread drops: methods on the prototype
4
+ * chain of a class instance, bound to `source` so they still see its state.
5
+ */
1
6
  function copyMembers(source) {
2
- const copy = { ...source };
3
- let proto = Object.getPrototypeOf(source);
4
- while (proto && proto !== Object.prototype) {
5
- for (const key of Reflect.ownKeys(proto)) {
6
- if (key === "constructor" || Object.hasOwn(copy, key)) continue;
7
- const { value } = Object.getOwnPropertyDescriptor(proto, key) ?? {};
8
- if (typeof value === "function") copy[key] = value.bind(source);
9
- }
10
- proto = Object.getPrototypeOf(proto);
11
- }
12
- return copy;
7
+ const copy = { ...source };
8
+ let proto = Object.getPrototypeOf(source);
9
+ while (proto && proto !== Object.prototype) {
10
+ for (const key of Reflect.ownKeys(proto)) {
11
+ if (key === "constructor" || Object.hasOwn(copy, key)) continue;
12
+ const { value } = Object.getOwnPropertyDescriptor(proto, key) ?? {};
13
+ if (typeof value === "function") Object.defineProperty(copy, key, {
14
+ value: value.bind(source),
15
+ writable: true,
16
+ enumerable: true,
17
+ configurable: true
18
+ });
19
+ }
20
+ proto = Object.getPrototypeOf(proto);
21
+ }
22
+ return copy;
13
23
  }
14
- export {
15
- copyMembers
16
- };
24
+ export { copyMembers };