@buoy-gg/time-machine 7.0.40 → 7.0.42

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 (173) hide show
  1. package/README.md +4 -0
  2. package/lib/commonjs/components/HeaderActions.js +1 -91
  3. package/lib/commonjs/components/HeaderReloadButton.js +1 -58
  4. package/lib/commonjs/components/TimeMachineBody.js +1 -282
  5. package/lib/commonjs/components/TimeMachineModal.js +1 -74
  6. package/lib/commonjs/components/confirm.js +2 -38
  7. package/lib/commonjs/components/floating/NamePromptModal.js +1 -193
  8. package/lib/commonjs/components/floating/TimeMachineFloatingTool.js +1 -715
  9. package/lib/commonjs/components/floating/armedPoint.js +1 -92
  10. package/lib/commonjs/components/floating/barSettings.js +1 -66
  11. package/lib/commonjs/components/format.js +1 -24
  12. package/lib/commonjs/components/list/CaptureBar.js +1 -97
  13. package/lib/commonjs/components/list/RestoreOutcomeCard.js +1 -175
  14. package/lib/commonjs/components/list/SnapshotRow.js +1 -170
  15. package/lib/commonjs/components/list/SourcesStrip.js +1 -172
  16. package/lib/commonjs/components/list/UndoDeleteBar.js +1 -85
  17. package/lib/commonjs/components/list/WipeAllRow.js +1 -46
  18. package/lib/commonjs/components/preview/ItemDiffPanel.js +1 -79
  19. package/lib/commonjs/components/preview/PreviewFlow.js +1 -51
  20. package/lib/commonjs/components/preview/RestorePointNameRow.js +1 -83
  21. package/lib/commonjs/components/preview/RestorePreviewScreen.js +1 -1010
  22. package/lib/commonjs/components/preview/verdictMeta.js +1 -58
  23. package/lib/commonjs/components/sources.js +1 -83
  24. package/lib/commonjs/hooks/useTimeMachine.js +1 -24
  25. package/lib/commonjs/index.js +1 -161
  26. package/lib/commonjs/preset.js +1 -56
  27. package/lib/commonjs/store/routeRestore.js +1 -188
  28. package/lib/commonjs/store/snapshotVault.js +1 -186
  29. package/lib/commonjs/store/sourceIds.js +1 -9
  30. package/lib/commonjs/store/sourceSettings.js +1 -79
  31. package/lib/commonjs/store/timeMachineStore.js +1 -848
  32. package/lib/commonjs/sync/timeMachineSyncAdapter.js +1 -258
  33. package/lib/commonjs/ui/SwipeActionsRow.js +1 -178
  34. package/lib/commonjs/ui/primitives.js +1 -172
  35. package/lib/commonjs/ui/tmTheme.js +1 -118
  36. package/lib/module/components/HeaderActions.js +1 -85
  37. package/lib/module/components/HeaderReloadButton.js +1 -53
  38. package/lib/module/components/TimeMachineBody.js +1 -277
  39. package/lib/module/components/TimeMachineModal.js +1 -68
  40. package/lib/module/components/confirm.js +2 -33
  41. package/lib/module/components/floating/NamePromptModal.js +1 -188
  42. package/lib/module/components/floating/TimeMachineFloatingTool.js +1 -708
  43. package/lib/module/components/floating/armedPoint.js +1 -82
  44. package/lib/module/components/floating/barSettings.js +1 -57
  45. package/lib/module/components/format.js +1 -19
  46. package/lib/module/components/list/CaptureBar.js +1 -92
  47. package/lib/module/components/list/RestoreOutcomeCard.js +1 -169
  48. package/lib/module/components/list/SnapshotRow.js +1 -167
  49. package/lib/module/components/list/SourcesStrip.js +1 -169
  50. package/lib/module/components/list/UndoDeleteBar.js +1 -82
  51. package/lib/module/components/list/WipeAllRow.js +1 -41
  52. package/lib/module/components/preview/ItemDiffPanel.js +1 -74
  53. package/lib/module/components/preview/PreviewFlow.js +1 -46
  54. package/lib/module/components/preview/RestorePointNameRow.js +1 -78
  55. package/lib/module/components/preview/RestorePreviewScreen.js +1 -1005
  56. package/lib/module/components/preview/verdictMeta.js +1 -54
  57. package/lib/module/components/sources.js +1 -73
  58. package/lib/module/hooks/useTimeMachine.js +1 -19
  59. package/lib/module/index.js +1 -46
  60. package/lib/module/preset.js +1 -51
  61. package/lib/module/store/routeRestore.js +1 -176
  62. package/lib/module/store/snapshotVault.js +1 -170
  63. package/lib/module/store/sourceIds.js +1 -5
  64. package/lib/module/store/sourceSettings.js +1 -69
  65. package/lib/module/store/timeMachineStore.js +1 -839
  66. package/lib/module/sync/timeMachineSyncAdapter.js +1 -253
  67. package/lib/module/ui/SwipeActionsRow.js +1 -173
  68. package/lib/module/ui/primitives.js +1 -164
  69. package/lib/module/ui/tmTheme.js +1 -114
  70. package/lib/web/index.mjs +3 -0
  71. package/package.json +43 -10
  72. package/lib/commonjs/components/HeaderActions.js.map +0 -1
  73. package/lib/commonjs/components/HeaderReloadButton.js.map +0 -1
  74. package/lib/commonjs/components/TimeMachineBody.js.map +0 -1
  75. package/lib/commonjs/components/TimeMachineModal.js.map +0 -1
  76. package/lib/commonjs/components/confirm.js.map +0 -1
  77. package/lib/commonjs/components/floating/NamePromptModal.js.map +0 -1
  78. package/lib/commonjs/components/floating/TimeMachineFloatingTool.js.map +0 -1
  79. package/lib/commonjs/components/floating/armedPoint.js.map +0 -1
  80. package/lib/commonjs/components/floating/barSettings.js.map +0 -1
  81. package/lib/commonjs/components/format.js.map +0 -1
  82. package/lib/commonjs/components/list/CaptureBar.js.map +0 -1
  83. package/lib/commonjs/components/list/RestoreOutcomeCard.js.map +0 -1
  84. package/lib/commonjs/components/list/SnapshotRow.js.map +0 -1
  85. package/lib/commonjs/components/list/SourcesStrip.js.map +0 -1
  86. package/lib/commonjs/components/list/UndoDeleteBar.js.map +0 -1
  87. package/lib/commonjs/components/list/WipeAllRow.js.map +0 -1
  88. package/lib/commonjs/components/preview/ItemDiffPanel.js.map +0 -1
  89. package/lib/commonjs/components/preview/PreviewFlow.js.map +0 -1
  90. package/lib/commonjs/components/preview/RestorePointNameRow.js.map +0 -1
  91. package/lib/commonjs/components/preview/RestorePreviewScreen.js.map +0 -1
  92. package/lib/commonjs/components/preview/verdictMeta.js.map +0 -1
  93. package/lib/commonjs/components/sources.js.map +0 -1
  94. package/lib/commonjs/hooks/useTimeMachine.js.map +0 -1
  95. package/lib/commonjs/index.js.map +0 -1
  96. package/lib/commonjs/preset.js.map +0 -1
  97. package/lib/commonjs/store/routeRestore.js.map +0 -1
  98. package/lib/commonjs/store/snapshotVault.js.map +0 -1
  99. package/lib/commonjs/store/sourceIds.js.map +0 -1
  100. package/lib/commonjs/store/sourceSettings.js.map +0 -1
  101. package/lib/commonjs/store/timeMachineStore.js.map +0 -1
  102. package/lib/commonjs/sync/timeMachineSyncAdapter.js.map +0 -1
  103. package/lib/commonjs/ui/SwipeActionsRow.js.map +0 -1
  104. package/lib/commonjs/ui/primitives.js.map +0 -1
  105. package/lib/commonjs/ui/tmTheme.js.map +0 -1
  106. package/lib/module/components/HeaderActions.js.map +0 -1
  107. package/lib/module/components/HeaderReloadButton.js.map +0 -1
  108. package/lib/module/components/TimeMachineBody.js.map +0 -1
  109. package/lib/module/components/TimeMachineModal.js.map +0 -1
  110. package/lib/module/components/confirm.js.map +0 -1
  111. package/lib/module/components/floating/NamePromptModal.js.map +0 -1
  112. package/lib/module/components/floating/TimeMachineFloatingTool.js.map +0 -1
  113. package/lib/module/components/floating/armedPoint.js.map +0 -1
  114. package/lib/module/components/floating/barSettings.js.map +0 -1
  115. package/lib/module/components/format.js.map +0 -1
  116. package/lib/module/components/list/CaptureBar.js.map +0 -1
  117. package/lib/module/components/list/RestoreOutcomeCard.js.map +0 -1
  118. package/lib/module/components/list/SnapshotRow.js.map +0 -1
  119. package/lib/module/components/list/SourcesStrip.js.map +0 -1
  120. package/lib/module/components/list/UndoDeleteBar.js.map +0 -1
  121. package/lib/module/components/list/WipeAllRow.js.map +0 -1
  122. package/lib/module/components/preview/ItemDiffPanel.js.map +0 -1
  123. package/lib/module/components/preview/PreviewFlow.js.map +0 -1
  124. package/lib/module/components/preview/RestorePointNameRow.js.map +0 -1
  125. package/lib/module/components/preview/RestorePreviewScreen.js.map +0 -1
  126. package/lib/module/components/preview/verdictMeta.js.map +0 -1
  127. package/lib/module/components/sources.js.map +0 -1
  128. package/lib/module/hooks/useTimeMachine.js.map +0 -1
  129. package/lib/module/index.js.map +0 -1
  130. package/lib/module/preset.js.map +0 -1
  131. package/lib/module/store/routeRestore.js.map +0 -1
  132. package/lib/module/store/snapshotVault.js.map +0 -1
  133. package/lib/module/store/sourceIds.js.map +0 -1
  134. package/lib/module/store/sourceSettings.js.map +0 -1
  135. package/lib/module/store/timeMachineStore.js.map +0 -1
  136. package/lib/module/sync/timeMachineSyncAdapter.js.map +0 -1
  137. package/lib/module/ui/SwipeActionsRow.js.map +0 -1
  138. package/lib/module/ui/primitives.js.map +0 -1
  139. package/lib/module/ui/tmTheme.js.map +0 -1
  140. package/lib/typescript/components/HeaderActions.d.ts.map +0 -1
  141. package/lib/typescript/components/HeaderReloadButton.d.ts.map +0 -1
  142. package/lib/typescript/components/TimeMachineBody.d.ts.map +0 -1
  143. package/lib/typescript/components/TimeMachineModal.d.ts.map +0 -1
  144. package/lib/typescript/components/confirm.d.ts.map +0 -1
  145. package/lib/typescript/components/floating/NamePromptModal.d.ts.map +0 -1
  146. package/lib/typescript/components/floating/TimeMachineFloatingTool.d.ts.map +0 -1
  147. package/lib/typescript/components/floating/armedPoint.d.ts.map +0 -1
  148. package/lib/typescript/components/floating/barSettings.d.ts.map +0 -1
  149. package/lib/typescript/components/format.d.ts.map +0 -1
  150. package/lib/typescript/components/list/CaptureBar.d.ts.map +0 -1
  151. package/lib/typescript/components/list/RestoreOutcomeCard.d.ts.map +0 -1
  152. package/lib/typescript/components/list/SnapshotRow.d.ts.map +0 -1
  153. package/lib/typescript/components/list/SourcesStrip.d.ts.map +0 -1
  154. package/lib/typescript/components/list/UndoDeleteBar.d.ts.map +0 -1
  155. package/lib/typescript/components/list/WipeAllRow.d.ts.map +0 -1
  156. package/lib/typescript/components/preview/ItemDiffPanel.d.ts.map +0 -1
  157. package/lib/typescript/components/preview/PreviewFlow.d.ts.map +0 -1
  158. package/lib/typescript/components/preview/RestorePointNameRow.d.ts.map +0 -1
  159. package/lib/typescript/components/preview/RestorePreviewScreen.d.ts.map +0 -1
  160. package/lib/typescript/components/preview/verdictMeta.d.ts.map +0 -1
  161. package/lib/typescript/components/sources.d.ts.map +0 -1
  162. package/lib/typescript/hooks/useTimeMachine.d.ts.map +0 -1
  163. package/lib/typescript/index.d.ts.map +0 -1
  164. package/lib/typescript/preset.d.ts.map +0 -1
  165. package/lib/typescript/store/routeRestore.d.ts.map +0 -1
  166. package/lib/typescript/store/snapshotVault.d.ts.map +0 -1
  167. package/lib/typescript/store/sourceIds.d.ts.map +0 -1
  168. package/lib/typescript/store/sourceSettings.d.ts.map +0 -1
  169. package/lib/typescript/store/timeMachineStore.d.ts.map +0 -1
  170. package/lib/typescript/sync/timeMachineSyncAdapter.d.ts.map +0 -1
  171. package/lib/typescript/ui/SwipeActionsRow.d.ts.map +0 -1
  172. package/lib/typescript/ui/primitives.d.ts.map +0 -1
  173. package/lib/typescript/ui/tmTheme.d.ts.map +0 -1
@@ -1,54 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * One place for the verdict → glyph/color/label mapping. Color is ALWAYS
5
- * paired with a distinct glyph (true minus U+2212, approx U+2248) — the
6
- * repo-wide accessibility convention for diff surfaces.
7
- */
8
-
9
- import { tm } from "../../ui/tmTheme";
10
- export const VERDICT_META = {
11
- added: {
12
- glyph: "+",
13
- color: tm.color.accent,
14
- label: "added",
15
- applies: true
16
- },
17
- removed: {
18
- glyph: "−",
19
- color: tm.color.danger,
20
- label: "removed",
21
- applies: true
22
- },
23
- changed: {
24
- glyph: "≈",
25
- color: tm.color.info,
26
- label: "changed",
27
- applies: true
28
- },
29
- "too-large": {
30
- glyph: "≈",
31
- color: tm.color.warning,
32
- label: "changed (large)",
33
- applies: true
34
- },
35
- unchanged: {
36
- glyph: "=",
37
- color: tm.color.labelTertiary,
38
- label: "unchanged",
39
- applies: false
40
- },
41
- "wont-apply": {
42
- glyph: "⊘",
43
- color: tm.color.warning,
44
- label: "won't apply",
45
- applies: false
46
- },
47
- recomputes: {
48
- glyph: "↻",
49
- color: tm.color.labelTertiary,
50
- label: "recomputes",
51
- applies: false
52
- }
53
- };
54
- //# sourceMappingURL=verdictMeta.js.map
1
+ "use strict";import{tm as l}from"../../ui/tmTheme";export const VERDICT_META={added:{glyph:"+",color:l.color.accent,label:"added",applies:true},removed:{glyph:"\u2212",color:l.color.danger,label:"removed",applies:true},changed:{glyph:"\u2248",color:l.color.info,label:"changed",applies:true},"too-large":{glyph:"\u2248",color:l.color.warning,label:"changed (large)",applies:true},unchanged:{glyph:"=",color:l.color.labelTertiary,label:"unchanged",applies:false},"wont-apply":{glyph:"\u2298",color:l.color.warning,label:"won't apply",applies:false},recomputes:{glyph:"\u21BB",color:l.color.labelTertiary,label:"recomputes",applies:false}};
@@ -1,73 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * What Time Machine can capture from, and how healthy each source is.
5
- *
6
- * On device these are live registry probes; on Buoy Desktop the same shapes
7
- * arrive mirrored in the sync payload. Route is folded in as a source even
8
- * though it is a device capability rather than a registered provider — from
9
- * the user's side "can this snapshot take me back to that screen?" is the
10
- * same question as "will my Redux store come back".
11
- */
12
-
13
- import { JotaiIcon, QueryIcon, ReduxIcon, RoutesIcon, StorageIcon, TimeMachineIcon, ZustandIcon } from "@buoy-gg/floating-tools-core";
14
- import { useSyncExternalStore } from "react";
15
- import { useSnapshotProviders, useTimeMachineState } from "../hooks/useTimeMachine";
16
- import { canNavigate } from "../store/routeRestore";
17
- import { ROUTE_SOURCE_ID } from "../store/sourceIds";
18
- import { getDisabledSources, subscribeSourceSettings } from "../store/sourceSettings";
19
- export { ROUTE_SOURCE_ID };
20
-
21
- /**
22
- * Buoy icons paint their knock-out parts with `fill: "bg"`, defaulting to a
23
- * near-black — so whatever surface an icon sits on has to be handed in as
24
- * `bgColor` or those cut-outs read as black holes (storage's floppy slot and
25
- * the route glyph are the two that use it).
26
- */
27
-
28
- const ICONS = {
29
- storage: StorageIcon,
30
- redux: ReduxIcon,
31
- zustand: ZustandIcon,
32
- jotai: JotaiIcon,
33
- query: QueryIcon,
34
- route: RoutesIcon
35
- };
36
- export function sourceIcon(id) {
37
- return ICONS[id] ?? TimeMachineIcon;
38
- }
39
- export function useSourceStatuses() {
40
- const {
41
- remoteProviders,
42
- remoteRoute
43
- } = useTimeMachineState();
44
- const localProviders = useSnapshotProviders();
45
- const disabled = useSyncExternalStore(subscribeSourceSettings, getDisabledSources, getDisabledSources);
46
- const providers = remoteProviders ? remoteProviders.map(p => ({
47
- ...p,
48
- enabled: p.enabled !== false
49
- })) : localProviders.map(p => ({
50
- id: p.id,
51
- label: p.label,
52
- ...p.status(),
53
- enabled: !disabled.has(p.id)
54
- }));
55
- const routeAvailable = remoteProviders ? remoteRoute?.available === true : canNavigate();
56
- return [...providers, {
57
- id: ROUTE_SOURCE_ID,
58
- label: "Route",
59
- canCapture: routeAvailable,
60
- canRestore: routeAvailable,
61
- enabled: remoteProviders ? remoteRoute?.enabled !== false : !disabled.has(ROUTE_SOURCE_ID),
62
- // No live pathname here: these screens only re-render on store changes,
63
- // so a route printed as status goes stale the moment you navigate with
64
- // the tool still open. Each restore point shows the route it holds.
65
- detail: routeAvailable ? "Recorded with every snapshot — a restore can take you back" : "No Expo Router detected — snapshots record no route"
66
- }];
67
- }
68
-
69
- /** A source is "attached" when a snapshot would actually pick it up. */
70
- export function isAttached(source) {
71
- return source.canCapture && source.enabled;
72
- }
73
- //# sourceMappingURL=sources.js.map
1
+ "use strict";import{JotaiIcon as u,QueryIcon as d,ReduxIcon as l,RoutesIcon as m,StorageIcon as b,TimeMachineIcon as p,ZustandIcon as S}from"@buoy-gg/floating-tools-core";import{useSyncExternalStore as f}from"react";import{useSnapshotProviders as I,useTimeMachineState as h}from"../hooks/useTimeMachine";import{canNavigate as R}from"../store/routeRestore";import{ROUTE_SOURCE_ID as r}from"../store/sourceIds";import{getDisabledSources as s,subscribeSourceSettings as v}from"../store/sourceSettings";export{r as ROUTE_SOURCE_ID};const x={storage:b,redux:l,zustand:S,jotai:u,query:d,route:m};export function sourceIcon(e){return x[e]??p}export function useSourceStatuses(){const{remoteProviders:e,remoteRoute:a}=h();const c=I();const n=f(v,s,s);const i=e?e.map(o=>({...o,enabled:o.enabled!==false})):c.map(o=>({id:o.id,label:o.label,...o.status(),enabled:!n.has(o.id)}));const t=e?a?.available===true:R();return[...i,{id:r,label:"Route",canCapture:t,canRestore:t,enabled:e?a?.enabled!==false:!n.has(r),detail:t?"Recorded with every snapshot \u2014 a restore can take you back":"No Expo Router detected \u2014 snapshots record no route"}]}export function isAttached(e){return e.canCapture&&e.enabled}
@@ -1,19 +1 @@
1
- "use strict";
2
-
3
- import { useEffect, useState, useSyncExternalStore } from "react";
4
- import { getStateSnapshotProviders, subscribeToStateSnapshotProviders } from "@buoy-gg/shared-ui";
5
- import { timeMachineStore } from "../store/timeMachineStore";
6
- export function useTimeMachineState() {
7
- return useSyncExternalStore(timeMachineStore.subscribe, () => timeMachineStore.getState());
8
- }
9
-
10
- /** Registered snapshot providers (query registers late, hence the subscribe). */
11
- export function useSnapshotProviders() {
12
- const [providers, setProviders] = useState(getStateSnapshotProviders);
13
- useEffect(() => {
14
- setProviders(getStateSnapshotProviders());
15
- return subscribeToStateSnapshotProviders(() => setProviders(getStateSnapshotProviders()));
16
- }, []);
17
- return providers;
18
- }
19
- //# sourceMappingURL=useTimeMachine.js.map
1
+ "use strict";import{useEffect as s,useState as i,useSyncExternalStore as n}from"react";import{getStateSnapshotProviders as e,subscribeToStateSnapshotProviders as u}from"@buoy-gg/shared-ui";import{timeMachineStore as r}from"../store/timeMachineStore";export function useTimeMachineState(){return n(r.subscribe,()=>r.getState())}export function useSnapshotProviders(){const[o,t]=i(e);s(()=>{t(e());return u(()=>t(e()))},[]);return o}
@@ -1,46 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * @buoy-gg/time-machine
5
- *
6
- * Time Machine — snapshot & restore app state for React Native DevTools.
7
- *
8
- * Capture the complete client-side state of the app (storage, Redux,
9
- * Zustand, Jotai, React Query) as a named snapshot; restore any snapshot in
10
- * one tap to iterate on multi-step test flows without rebuilding state.
11
- *
12
- * PUBLIC API - Only these exports are supported for external use.
13
- */
14
-
15
- import { armPendingRouteRestore as armPendingRouteRestoreOnLoad } from "./store/routeRestore";
16
-
17
- // Tool preset for FloatingDevTools
18
- export { timeMachineToolPreset, createTimeMachineTool } from "./preset";
19
-
20
- // Components
21
- export { TimeMachineModal, TimeMachineToolView } from "./components/TimeMachineModal";
22
-
23
- // The floating restore bar (one armed point, one tap to restore)
24
- export { openTimeMachineFloatingTool, TIME_MACHINE_FLOATING_TOOL_ID } from "./components/floating/TimeMachineFloatingTool";
25
-
26
- // External-sync adapter (auto-registered by @buoy-gg/core)
27
- export { timeMachineSyncAdapter } from "./sync/timeMachineSyncAdapter";
28
-
29
- // Programmatic snapshot control (also used by tests / power users)
30
- export { timeMachineStore, RESTORE_ORDER, SAFETY_SNAPSHOT_NAME } from "./store/timeMachineStore";
31
-
32
- // Route capture / restore ("also take me back to the screen I was on")
33
- export { armPendingRouteRestore, captureCurrentRoute, navigateToRoute, canNavigate as canRestoreRoute } from "./store/routeRestore";
34
-
35
- // Per-source on/off switches (the Sources strip); off = never captured or restored
36
- export { isSourceEnabled, setSourceEnabled, subscribeSourceSettings } from "./store/sourceSettings";
37
-
38
- // Buoy Desktop remote-mirror seam
39
- export { setRemoteTimeMachineHandler, replaceTimeMachineFromSnapshot } from "./store/timeMachineStore";
40
- // A reload-mode restore that also restores the route can't navigate itself —
41
- // the JS realm dies with the reload. It parks the route in storage; this picks
42
- // it up on the next boot. Core requires this module while mounting the
43
- // floating menu, which is the earliest point we're guaranteed to run.
44
- // Idempotent: repeated imports (or a host calling it explicitly) arm once.
45
- armPendingRouteRestoreOnLoad();
46
- //# sourceMappingURL=index.js.map
1
+ "use strict";import{armPendingRouteRestore as e}from"./store/routeRestore";export{timeMachineToolPreset,createTimeMachineTool}from"./preset";export{TimeMachineModal,TimeMachineToolView}from"./components/TimeMachineModal";export{openTimeMachineFloatingTool,TIME_MACHINE_FLOATING_TOOL_ID}from"./components/floating/TimeMachineFloatingTool";export{timeMachineSyncAdapter}from"./sync/timeMachineSyncAdapter";export{timeMachineStore,RESTORE_ORDER,SAFETY_SNAPSHOT_NAME}from"./store/timeMachineStore";export{armPendingRouteRestore,captureCurrentRoute,navigateToRoute,canNavigate as canRestoreRoute}from"./store/routeRestore";export{isSourceEnabled,setSourceEnabled,subscribeSourceSettings}from"./store/sourceSettings";export{setRemoteTimeMachineHandler,replaceTimeMachineFromSnapshot}from"./store/timeMachineStore";e();
@@ -1,51 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Pre-configured Time Machine tool for FloatingDevTools.
5
- *
6
- * @example
7
- * ```tsx
8
- * import { timeMachineToolPreset } from '@buoy-gg/time-machine';
9
- *
10
- * <FloatingDevTools apps={[timeMachineToolPreset]} />
11
- * ```
12
- */
13
-
14
- import { Platform } from "react-native";
15
- import { TimeMachineIcon, TIME_MACHINE_ICON_COLOR } from "@buoy-gg/shared-ui";
16
- import { TimeMachineBody } from "./components/TimeMachineBody";
17
- import { TimeMachineHeaderActions } from "./components/HeaderActions";
18
- import { jsx as _jsx } from "react/jsx-runtime";
19
- export const timeMachineToolPreset = {
20
- id: "time-machine",
21
- name: "TIME MACHINE",
22
- description: "Snapshot & restore app state (storage, stores, query cache)",
23
- slot: "both",
24
- icon: ({
25
- size,
26
- color,
27
- slot
28
- }) => /*#__PURE__*/_jsx(TimeMachineIcon, {
29
- size: slot === "dial" && Platform.OS !== "web" ? size * 1.5 : size,
30
- color: color || TIME_MACHINE_ICON_COLOR
31
- }),
32
- component: TimeMachineBody,
33
- // Declarative chrome: core's ToolModalShell provides the JsModal, the
34
- // managed header (screens override it via useToolHeader) and header search.
35
- modal: {
36
- subtitle: "snapshot & restore app state",
37
- searchable: true,
38
- searchPlaceholder: "Filter…",
39
- headerActions: /*#__PURE__*/_jsx(TimeMachineHeaderActions, {})
40
- }
41
- };
42
-
43
- /** Create a custom Time Machine tool configuration. */
44
- export function createTimeMachineTool(options = {}) {
45
- return {
46
- ...timeMachineToolPreset,
47
- name: options.name ?? timeMachineToolPreset.name,
48
- description: options.description ?? timeMachineToolPreset.description
49
- };
50
- }
51
- //# sourceMappingURL=preset.js.map
1
+ "use strict";import{Platform as i}from"react-native";import{TimeMachineIcon as a,TIME_MACHINE_ICON_COLOR as s}from"@buoy-gg/shared-ui";import{TimeMachineBody as c}from"./components/TimeMachineBody";import{TimeMachineHeaderActions as n}from"./components/HeaderActions";import{jsx as o}from"react/jsx-runtime";export const timeMachineToolPreset={id:"time-machine",name:"TIME MACHINE",description:"Snapshot & restore app state (storage, stores, query cache)",slot:"both",icon:({size:e,color:t,slot:r})=>o(a,{size:r==="dial"&&i.OS!=="web"?e*1.5:e,color:t||s}),component:c,modal:{subtitle:"snapshot & restore app state",searchable:true,searchPlaceholder:"Filter\u2026",headerActions:o(n,{})}};export function createTimeMachineTool(e={}){return{...timeMachineToolPreset,name:e.name??timeMachineToolPreset.name,description:e.description??timeMachineToolPreset.description}}
@@ -1,176 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Route capture & restore for Time Machine.
5
- *
6
- * A restore point is only half the story: state without the screen you were
7
- * on drops you into the right data on the wrong page. Every snapshot records
8
- * the route it was captured on, and a restore can navigate back to it.
9
- *
10
- * Two navigation paths, because a "reload" restore kills the JS realm:
11
- * - LIVE restore: navigate immediately once the sources are applied.
12
- * - RELOAD restore: persist the route BEFORE the reload is scheduled, then
13
- * {@link armPendingRouteRestore} picks it up on the next boot, waits for the
14
- * router to come online, and navigates.
15
- *
16
- * Everything here is best-effort: apps without expo-router capture no route,
17
- * so the UI simply never offers the option.
18
- */
19
- import { getSafeCurrentRoute, getSafeRouter, persistentStorage } from "@buoy-gg/shared-ui";
20
-
21
- /**
22
- * The route a snapshot was captured on. Only the URL is stored — imperative
23
- * params passed as objects (`navigate(path, { user })`) are not part of a
24
- * route and are not restored.
25
- */
26
-
27
- /** How a restore's route leg ended, reported back in the RestoreOutcome. */
28
-
29
- /**
30
- * Same `@react_buoy` family as the snapshot vault, so every tool's
31
- * isDevToolsStorageKey filter (and therefore every wipe/restore) leaves it
32
- * alone — a pending route must survive the storage restore that precedes the
33
- * reload it's waiting on.
34
- */
35
- const PENDING_KEY = "@react_buoy_time_machine:pending-route";
36
-
37
- /**
38
- * A pending route older than this belongs to a reload that never happened
39
- * (the app was killed, the user moved on). Boot-time navigation into a screen
40
- * someone left minutes ago is worse than doing nothing.
41
- */
42
- export const PENDING_ROUTE_TTL_MS = 60_000;
43
-
44
- /** How long to wait at boot for the router to come online. */
45
- const ROUTER_READY_TIMEOUT_MS = 8_000;
46
-
47
- /** Settle window after the router appears, so the initial route mounts first. */
48
- const ROUTER_SETTLE_MS = 250;
49
- function toSnapshotRoute(location) {
50
- return {
51
- pathname: location.pathname,
52
- href: location.href
53
- };
54
- }
55
-
56
- /** The route to stamp on a snapshot being captured, or null when unavailable. */
57
- export function captureCurrentRoute() {
58
- const current = getSafeCurrentRoute();
59
- return current ? toSnapshotRoute(current) : null;
60
- }
61
-
62
- /** The live route, for "you're already here" checks in the UI. */
63
- export function currentRoute() {
64
- return captureCurrentRoute();
65
- }
66
-
67
- /** True when imperative navigation is possible on this device. */
68
- export function canNavigate() {
69
- return getSafeRouter() != null;
70
- }
71
-
72
- /**
73
- * Navigate to a captured route now. Uses `navigate` (not `push`) so restoring
74
- * the same route twice doesn't stack duplicate screens.
75
- */
76
- export function navigateToRoute(route) {
77
- const base = {
78
- pathname: route.pathname,
79
- href: route.href
80
- };
81
- const router = getSafeRouter();
82
- if (!router?.navigate) {
83
- return {
84
- ...base,
85
- status: "unavailable",
86
- error: "expo-router is not available on this device"
87
- };
88
- }
89
- try {
90
- router.navigate(route.href);
91
- return {
92
- ...base,
93
- status: "navigated"
94
- };
95
- } catch (e) {
96
- return {
97
- ...base,
98
- status: "failed",
99
- error: String(e)
100
- };
101
- }
102
- }
103
-
104
- // ── Pending route (survives the reload) ──────────────────────────────────────
105
-
106
- export async function savePendingRoute(route, snapshotName) {
107
- const pending = {
108
- ...route,
109
- savedAt: Date.now(),
110
- ...(snapshotName ? {
111
- snapshotName
112
- } : {})
113
- };
114
- await persistentStorage.setItem(PENDING_KEY, JSON.stringify(pending));
115
- }
116
- export async function clearPendingRoute() {
117
- try {
118
- await persistentStorage.removeItem(PENDING_KEY);
119
- } catch {
120
- // best-effort — a stale key is dropped by the TTL check anyway
121
- }
122
- }
123
- async function loadPendingRoute() {
124
- try {
125
- const raw = await persistentStorage.getItem(PENDING_KEY);
126
- if (!raw) return null;
127
- const parsed = JSON.parse(raw);
128
- if (typeof parsed?.href !== "string" || !parsed.href) return null;
129
- if (typeof parsed.savedAt !== "number") return null;
130
- return {
131
- pathname: typeof parsed.pathname === "string" ? parsed.pathname : parsed.href,
132
- href: parsed.href,
133
- savedAt: parsed.savedAt,
134
- snapshotName: typeof parsed.snapshotName === "string" ? parsed.snapshotName : undefined
135
- };
136
- } catch {
137
- return null;
138
- }
139
- }
140
- let armed = false;
141
-
142
- /**
143
- * Boot-time consumer of a pending route: called once when the package loads
144
- * (core requires it while mounting the floating menu), it navigates to the
145
- * route a reload-mode restore asked for and clears the slot.
146
- *
147
- * Idempotent and self-limiting — the slot is cleared before navigating, so a
148
- * route that crashes the app on arrival can't put it in a boot loop.
149
- */
150
- export function armPendingRouteRestore() {
151
- if (armed) return;
152
- armed = true;
153
- void (async () => {
154
- const pending = await loadPendingRoute();
155
- if (!pending) return;
156
- // Clear FIRST: whatever happens next, this route is spent.
157
- await clearPendingRoute();
158
- if (Date.now() - pending.savedAt > PENDING_ROUTE_TTL_MS) return;
159
- const deadline = Date.now() + ROUTER_READY_TIMEOUT_MS;
160
- while (!canNavigate() && Date.now() < deadline) {
161
- await new Promise(r => setTimeout(r, 100));
162
- }
163
- if (!canNavigate()) return;
164
- await new Promise(r => setTimeout(r, ROUTER_SETTLE_MS));
165
- navigateToRoute({
166
- pathname: pending.pathname,
167
- href: pending.href
168
- });
169
- })();
170
- }
171
-
172
- /** @internal test seam — re-arms the boot-time consumer. */
173
- export function resetPendingRouteArm() {
174
- armed = false;
175
- }
176
- //# sourceMappingURL=routeRestore.js.map
1
+ "use strict";import{getSafeCurrentRoute as s,getSafeRouter as i,persistentStorage as r}from"@buoy-gg/shared-ui";const a="@react_buoy_time_machine:pending-route";export const PENDING_ROUTE_TTL_MS=6e4;const f=8e3;const c=250;function p(e){return{pathname:e.pathname,href:e.href}}export function captureCurrentRoute(){const e=s();return e?p(e):null}export function currentRoute(){return captureCurrentRoute()}export function canNavigate(){return i()!=null}export function navigateToRoute(e){const t={pathname:e.pathname,href:e.href};const n=i();if(!n?.navigate){return{...t,status:"unavailable",error:"expo-router is not available on this device"}}try{n.navigate(e.href);return{...t,status:"navigated"}}catch(u){return{...t,status:"failed",error:String(u)}}}export async function savePendingRoute(e,t){const n={...e,savedAt:Date.now(),...t?{snapshotName:t}:{}};await r.setItem(a,JSON.stringify(n))}export async function clearPendingRoute(){try{await r.removeItem(a)}catch{}}async function h(){try{const e=await r.getItem(a);if(!e)return null;const t=JSON.parse(e);if(typeof t?.href!=="string"||!t.href)return null;if(typeof t.savedAt!=="number")return null;return{pathname:typeof t.pathname==="string"?t.pathname:t.href,href:t.href,savedAt:t.savedAt,snapshotName:typeof t.snapshotName==="string"?t.snapshotName:void 0}}catch{return null}}let o=false;export function armPendingRouteRestore(){if(o)return;o=true;void(async()=>{const e=await h();if(!e)return;await clearPendingRoute();if(Date.now()-e.savedAt>PENDING_ROUTE_TTL_MS)return;const t=Date.now()+f;while(!canNavigate()&&Date.now()<t){await new Promise(n=>setTimeout(n,100))}if(!canNavigate())return;await new Promise(n=>setTimeout(n,c));navigateToRoute({pathname:e.pathname,href:e.href})})()}export function resetPendingRouteArm(){o=false}
@@ -1,170 +1 @@
1
- "use strict";
2
-
3
- /**
4
- * Snapshot persistence for Time Machine.
5
- *
6
- * Snapshots live ON-DEVICE in persistentStorage (FileSystem → AsyncStorage →
7
- * localStorage → Memory fallback): no size caps, survives restarts, and never
8
- * crosses the 2MB external-sync snapshot budget — only metadata does.
9
- *
10
- * Layout: ONE storage key per snapshot (persistentStorage's FileSystem
11
- * backend writes one file per key, and its multiGet is sequential — a single
12
- * big key beats many small ones), plus one index key holding the metadata
13
- * list for cheap listing.
14
- *
15
- * Keys use the `@react_buoy` family so every tool's isDevToolsStorageKey
16
- * filter (including our own storage provider) excludes the vault itself from
17
- * snapshots — a snapshot must never contain snapshots.
18
- */
19
- import { persistentStorage } from "@buoy-gg/shared-ui";
20
- const INDEX_KEY = "@react_buoy_time_machine:index";
21
- const SNAP_PREFIX = "@react_buoy_time_machine:snap:";
22
- export async function loadIndex() {
23
- try {
24
- const raw = await persistentStorage.getItem(INDEX_KEY);
25
- if (!raw) return [];
26
- const parsed = JSON.parse(raw);
27
- return Array.isArray(parsed) ? parsed : [];
28
- } catch {
29
- return [];
30
- }
31
- }
32
- async function saveIndex(metas) {
33
- await persistentStorage.setItem(INDEX_KEY, JSON.stringify(metas));
34
- }
35
-
36
- /** Persists the snapshot, stamps meta.sizeBytes, and updates the index. */
37
- export async function saveSnapshot(snapshot, metas) {
38
- const serialized = JSON.stringify(snapshot);
39
- snapshot.meta.sizeBytes = serialized.length;
40
- await persistentStorage.setItem(SNAP_PREFIX + snapshot.meta.id,
41
- // Re-stringify so the persisted copy carries the final sizeBytes.
42
- JSON.stringify(snapshot));
43
- const next = [snapshot.meta, ...metas.filter(m => m.id !== snapshot.meta.id)];
44
- await saveIndex(next);
45
- return next;
46
- }
47
- export async function loadSnapshot(id) {
48
- try {
49
- const raw = await persistentStorage.getItem(SNAP_PREFIX + id);
50
- return raw ? JSON.parse(raw) : null;
51
- } catch {
52
- return null;
53
- }
54
- }
55
- export async function deleteSnapshot(id, metas) {
56
- await persistentStorage.removeItem(SNAP_PREFIX + id);
57
- const next = metas.filter(m => m.id !== id);
58
- await saveIndex(next);
59
- return next;
60
- }
61
-
62
- /**
63
- * Rewrite one snapshot's metadata in BOTH places it lives — the index and the
64
- * snapshot's own record — so a reload can't resurrect the old value from
65
- * whichever one was missed.
66
- */
67
- async function patchMeta(id, patch, metas) {
68
- const apply = m => m.id === id ? patch(m) : m;
69
- const next = metas.map(apply);
70
- await persistentStorage.setItem(INDEX_KEY, JSON.stringify(next));
71
- const stored = await loadSnapshot(id);
72
- if (stored) {
73
- stored.meta = apply(stored.meta);
74
- await persistentStorage.setItem(SNAP_PREFIX + id, JSON.stringify(stored));
75
- }
76
- return next;
77
- }
78
-
79
- /** Set (or clear with null/empty) the "only these items" restore scope. */
80
- export function setSnapshotScope(id, scope, metas) {
81
- return patchMeta(id, m => {
82
- const next = {
83
- ...m
84
- };
85
- if (scope && scope.length > 0) next.scope = scope;else delete next.scope;
86
- return next;
87
- }, metas);
88
- }
89
-
90
- /**
91
- * Set (or clear with null/empty) the items a restore of this snapshot must
92
- * leave alone. Saving exclusions also clears any legacy `scope`: the two
93
- * answer the same question in opposite directions, and the one the user just
94
- * chose in the preview wins.
95
- */
96
- export function setSnapshotExclusions(id, excludedKeys, metas) {
97
- return patchMeta(id, m => {
98
- const next = {
99
- ...m
100
- };
101
- delete next.scope;
102
- if (excludedKeys && excludedKeys.length > 0) next.excludedKeys = excludedKeys;else delete next.excludedKeys;
103
- return next;
104
- }, metas);
105
- }
106
-
107
- /** Turn this point's route restore on or off — the row's route toggle. */
108
- export function setSnapshotRestoreRoute(id, restoreRoute, metas) {
109
- return patchMeta(id, m => ({
110
- ...m,
111
- restoreRoute
112
- }), metas);
113
- }
114
-
115
- /** Stamp a restore so the list can say which point you are currently in. */
116
- export function markSnapshotRestored(id, at, metas) {
117
- return patchMeta(id, m => ({
118
- ...m,
119
- lastRestoredAt: at
120
- }), metas);
121
- }
122
-
123
- /**
124
- * Copy a stored snapshot under a new id and name. The payload is copied by
125
- * reference into a fresh record — the vault serializes it on write, so the
126
- * two never share mutable state.
127
- */
128
- export async function duplicateSnapshot(id, name, metas) {
129
- const stored = await loadSnapshot(id);
130
- if (!stored) return null;
131
- const copy = {
132
- meta: {
133
- ...stored.meta,
134
- id: `${stored.meta.id}_copy_${Date.now().toString(36)}`,
135
- name,
136
- createdAt: Date.now(),
137
- lastRestoredAt: undefined
138
- },
139
- data: stored.data
140
- };
141
- delete copy.meta.lastRestoredAt;
142
- return {
143
- metas: await saveSnapshot(copy, metas),
144
- meta: copy.meta
145
- };
146
- }
147
-
148
- /** Read a whole record so a delete can be held in memory and undone. */
149
- export async function readSnapshot(id) {
150
- return loadSnapshot(id);
151
- }
152
-
153
- /** Put a held record back exactly as it was. */
154
- export async function restoreDeletedSnapshot(stored, metas) {
155
- return saveSnapshot(stored, metas);
156
- }
157
- export async function renameSnapshot(id, name, metas) {
158
- const next = metas.map(m => m.id === id ? {
159
- ...m,
160
- name
161
- } : m);
162
- await saveIndex(next);
163
- const stored = await loadSnapshot(id);
164
- if (stored) {
165
- stored.meta.name = name;
166
- await persistentStorage.setItem(SNAP_PREFIX + id, JSON.stringify(stored));
167
- }
168
- return next;
169
- }
170
- //# sourceMappingURL=snapshotVault.js.map
1
+ "use strict";import{persistentStorage as s}from"@buoy-gg/shared-ui";const p="@react_buoy_time_machine:index";const i="@react_buoy_time_machine:snap:";export async function loadIndex(){try{const t=await s.getItem(p);if(!t)return[];const e=JSON.parse(t);return Array.isArray(e)?e:[]}catch{return[]}}async function d(t){await s.setItem(p,JSON.stringify(t))}export async function saveSnapshot(t,e){const r=JSON.stringify(t);t.meta.sizeBytes=r.length;await s.setItem(i+t.meta.id,JSON.stringify(t));const n=[t.meta,...e.filter(a=>a.id!==t.meta.id)];await d(n);return n}export async function loadSnapshot(t){try{const e=await s.getItem(i+t);return e?JSON.parse(e):null}catch{return null}}export async function deleteSnapshot(t,e){await s.removeItem(i+t);const r=e.filter(n=>n.id!==t);await d(r);return r}async function c(t,e,r){const n=u=>u.id===t?e(u):u;const a=r.map(n);await s.setItem(p,JSON.stringify(a));const o=await loadSnapshot(t);if(o){o.meta=n(o.meta);await s.setItem(i+t,JSON.stringify(o))}return a}export function setSnapshotScope(t,e,r){return c(t,n=>{const a={...n};if(e&&e.length>0)a.scope=e;else delete a.scope;return a},r)}export function setSnapshotExclusions(t,e,r){return c(t,n=>{const a={...n};delete a.scope;if(e&&e.length>0)a.excludedKeys=e;else delete a.excludedKeys;return a},r)}export function setSnapshotRestoreRoute(t,e,r){return c(t,n=>({...n,restoreRoute:e}),r)}export function markSnapshotRestored(t,e,r){return c(t,n=>({...n,lastRestoredAt:e}),r)}export async function duplicateSnapshot(t,e,r){const n=await loadSnapshot(t);if(!n)return null;const a={meta:{...n.meta,id:`${n.meta.id}_copy_${Date.now().toString(36)}`,name:e,createdAt:Date.now(),lastRestoredAt:void 0},data:n.data};delete a.meta.lastRestoredAt;return{metas:await saveSnapshot(a,r),meta:a.meta}}export async function readSnapshot(t){return loadSnapshot(t)}export async function restoreDeletedSnapshot(t,e){return saveSnapshot(t,e)}export async function renameSnapshot(t,e,r){const n=r.map(o=>o.id===t?{...o,name:e}:o);await d(n);const a=await loadSnapshot(t);if(a){a.meta.name=e;await s.setItem(i+t,JSON.stringify(a))}return n}
@@ -1,5 +1 @@
1
- "use strict";
2
-
3
- /** Id of the route "source": a device capability, not a snapshot provider. */
4
- export const ROUTE_SOURCE_ID = "route";
5
- //# sourceMappingURL=sourceIds.js.map
1
+ "use strict";export const ROUTE_SOURCE_ID="route";