@buoy-gg/time-machine 7.0.17
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.
- package/LICENSE +58 -0
- package/README.md +55 -0
- package/lib/commonjs/components/TimeMachineBody.js +420 -0
- package/lib/commonjs/components/TimeMachineBody.js.map +1 -0
- package/lib/commonjs/components/TimeMachineModal.js +74 -0
- package/lib/commonjs/components/TimeMachineModal.js.map +1 -0
- package/lib/commonjs/hooks/useTimeMachine.js +24 -0
- package/lib/commonjs/hooks/useTimeMachine.js.map +1 -0
- package/lib/commonjs/index.js +64 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +45 -0
- package/lib/commonjs/preset.js.map +1 -0
- package/lib/commonjs/store/snapshotVault.js +83 -0
- package/lib/commonjs/store/snapshotVault.js.map +1 -0
- package/lib/commonjs/store/timeMachineStore.js +335 -0
- package/lib/commonjs/store/timeMachineStore.js.map +1 -0
- package/lib/commonjs/sync/timeMachineSyncAdapter.js +139 -0
- package/lib/commonjs/sync/timeMachineSyncAdapter.js.map +1 -0
- package/lib/module/components/TimeMachineBody.js +415 -0
- package/lib/module/components/TimeMachineBody.js.map +1 -0
- package/lib/module/components/TimeMachineModal.js +68 -0
- package/lib/module/components/TimeMachineModal.js.map +1 -0
- package/lib/module/hooks/useTimeMachine.js +19 -0
- package/lib/module/hooks/useTimeMachine.js.map +1 -0
- package/lib/module/index.js +29 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/preset.js +40 -0
- package/lib/module/preset.js.map +1 -0
- package/lib/module/store/snapshotVault.js +74 -0
- package/lib/module/store/snapshotVault.js.map +1 -0
- package/lib/module/store/timeMachineStore.js +327 -0
- package/lib/module/store/timeMachineStore.js.map +1 -0
- package/lib/module/sync/timeMachineSyncAdapter.js +134 -0
- package/lib/module/sync/timeMachineSyncAdapter.js.map +1 -0
- package/lib/typescript/components/TimeMachineBody.d.ts +2 -0
- package/lib/typescript/components/TimeMachineBody.d.ts.map +1 -0
- package/lib/typescript/components/TimeMachineModal.d.ts +12 -0
- package/lib/typescript/components/TimeMachineModal.d.ts.map +1 -0
- package/lib/typescript/hooks/useTimeMachine.d.ts +6 -0
- package/lib/typescript/hooks/useTimeMachine.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +18 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/preset.d.ts +38 -0
- package/lib/typescript/preset.d.ts.map +1 -0
- package/lib/typescript/store/snapshotVault.d.ts +46 -0
- package/lib/typescript/store/snapshotVault.d.ts.map +1 -0
- package/lib/typescript/store/timeMachineStore.d.ts +97 -0
- package/lib/typescript/store/timeMachineStore.d.ts.map +1 -0
- package/lib/typescript/sync/timeMachineSyncAdapter.d.ts +59 -0
- package/lib/typescript/sync/timeMachineSyncAdapter.d.ts.map +1 -0
- package/package.json +59 -0
- package/src/components/TimeMachineBody.tsx +455 -0
- package/src/components/TimeMachineModal.tsx +75 -0
- package/src/hooks/useTimeMachine.ts +26 -0
- package/src/index.tsx +45 -0
- package/src/preset.tsx +38 -0
- package/src/store/snapshotVault.ts +117 -0
- package/src/store/timeMachineStore.ts +368 -0
- package/src/sync/timeMachineSyncAdapter.ts +128 -0
|
@@ -0,0 +1,19 @@
|
|
|
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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["useEffect","useState","useSyncExternalStore","getStateSnapshotProviders","subscribeToStateSnapshotProviders","timeMachineStore","useTimeMachineState","subscribe","getState","useSnapshotProviders","providers","setProviders"],"sourceRoot":"../../../src","sources":["hooks/useTimeMachine.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,QAAQ,EAAEC,oBAAoB,QAAQ,OAAO;AACjE,SACEC,yBAAyB,EACzBC,iCAAiC,QAE5B,oBAAoB;AAC3B,SAASC,gBAAgB,QAAQ,2BAA2B;AAG5D,OAAO,SAASC,mBAAmBA,CAAA,EAAqB;EACtD,OAAOJ,oBAAoB,CAACG,gBAAgB,CAACE,SAAS,EAAE,MACtDF,gBAAgB,CAACG,QAAQ,CAAC,CAC5B,CAAC;AACH;;AAEA;AACA,OAAO,SAASC,oBAAoBA,CAAA,EAA4B;EAC9D,MAAM,CAACC,SAAS,EAAEC,YAAY,CAAC,GAAGV,QAAQ,CAACE,yBAAyB,CAAC;EACrEH,SAAS,CAAC,MAAM;IACdW,YAAY,CAACR,yBAAyB,CAAC,CAAC,CAAC;IACzC,OAAOC,iCAAiC,CAAC,MACvCO,YAAY,CAACR,yBAAyB,CAAC,CAAC,CAC1C,CAAC;EACH,CAAC,EAAE,EAAE,CAAC;EACN,OAAOO,SAAS;AAClB","ignoreList":[]}
|
|
@@ -0,0 +1,29 @@
|
|
|
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
|
+
// Tool preset for FloatingDevTools
|
|
16
|
+
export { timeMachineToolPreset, createTimeMachineTool } from "./preset";
|
|
17
|
+
|
|
18
|
+
// Components
|
|
19
|
+
export { TimeMachineModal, TimeMachineToolView } from "./components/TimeMachineModal";
|
|
20
|
+
|
|
21
|
+
// External-sync adapter (auto-registered by @buoy-gg/core)
|
|
22
|
+
export { timeMachineSyncAdapter } from "./sync/timeMachineSyncAdapter";
|
|
23
|
+
|
|
24
|
+
// Programmatic snapshot control (also used by tests / power users)
|
|
25
|
+
export { timeMachineStore, RESTORE_ORDER } from "./store/timeMachineStore";
|
|
26
|
+
|
|
27
|
+
// Buoy Desktop remote-mirror seam
|
|
28
|
+
export { setRemoteTimeMachineHandler, replaceTimeMachineFromSnapshot } from "./store/timeMachineStore";
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["timeMachineToolPreset","createTimeMachineTool","TimeMachineModal","TimeMachineToolView","timeMachineSyncAdapter","timeMachineStore","RESTORE_ORDER","setRemoteTimeMachineHandler","replaceTimeMachineFromSnapshot"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA,SAASA,qBAAqB,EAAEC,qBAAqB,QAAQ,UAAU;;AAEvE;AACA,SACEC,gBAAgB,EAChBC,mBAAmB,QAEd,+BAA+B;;AAEtC;AACA,SAASC,sBAAsB,QAAQ,+BAA+B;;AAEtE;AACA,SACEC,gBAAgB,EAChBC,aAAa,QAKR,0BAA0B;;AAEjC;AACA,SACEC,2BAA2B,EAC3BC,8BAA8B,QACzB,0BAA0B","ignoreList":[]}
|
|
@@ -0,0 +1,40 @@
|
|
|
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 { TimeMachineIcon, TIME_MACHINE_ICON_COLOR } from "@buoy-gg/shared-ui";
|
|
15
|
+
import { TimeMachineModal } from "./components/TimeMachineModal";
|
|
16
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
17
|
+
export const timeMachineToolPreset = {
|
|
18
|
+
id: "time-machine",
|
|
19
|
+
name: "TIME MACHINE",
|
|
20
|
+
description: "Snapshot & restore app state (storage, stores, query cache)",
|
|
21
|
+
slot: "both",
|
|
22
|
+
icon: ({
|
|
23
|
+
size,
|
|
24
|
+
color
|
|
25
|
+
}) => /*#__PURE__*/_jsx(TimeMachineIcon, {
|
|
26
|
+
size: size,
|
|
27
|
+
color: color || TIME_MACHINE_ICON_COLOR
|
|
28
|
+
}),
|
|
29
|
+
component: TimeMachineModal
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/** Create a custom Time Machine tool configuration. */
|
|
33
|
+
export function createTimeMachineTool(options = {}) {
|
|
34
|
+
return {
|
|
35
|
+
...timeMachineToolPreset,
|
|
36
|
+
name: options.name ?? timeMachineToolPreset.name,
|
|
37
|
+
description: options.description ?? timeMachineToolPreset.description
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=preset.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["TimeMachineIcon","TIME_MACHINE_ICON_COLOR","TimeMachineModal","jsx","_jsx","timeMachineToolPreset","id","name","description","slot","icon","size","color","component","createTimeMachineTool","options"],"sourceRoot":"../../src","sources":["preset.tsx"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,SACEA,eAAe,EACfC,uBAAuB,QAClB,oBAAoB;AAC3B,SAASC,gBAAgB,QAAQ,+BAA+B;AAAC,SAAAC,GAAA,IAAAC,IAAA;AAEjE,OAAO,MAAMC,qBAAqB,GAAG;EACnCC,EAAE,EAAE,cAAc;EAClBC,IAAI,EAAE,cAAc;EACpBC,WAAW,EAAE,6DAA6D;EAC1EC,IAAI,EAAE,MAAe;EACrBC,IAAI,EAAEA,CAAC;IAAEC,IAAI;IAAEC;EAAwC,CAAC,kBACtDR,IAAA,CAACJ,eAAe;IAACW,IAAI,EAAEA,IAAK;IAACC,KAAK,EAAEA,KAAK,IAAIX;EAAwB,CAAE,CACxE;EACDY,SAAS,EAAEX;AACb,CAAC;;AAED;AACA,OAAO,SAASY,qBAAqBA,CACnCC,OAAgD,GAAG,CAAC,CAAC,EACrD;EACA,OAAO;IACL,GAAGV,qBAAqB;IACxBE,IAAI,EAAEQ,OAAO,CAACR,IAAI,IAAIF,qBAAqB,CAACE,IAAI;IAChDC,WAAW,EAAEO,OAAO,CAACP,WAAW,IAAIH,qBAAqB,CAACG;EAC5D,CAAC;AACH","ignoreList":[]}
|
|
@@ -0,0 +1,74 @@
|
|
|
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
|
+
export async function renameSnapshot(id, name, metas) {
|
|
62
|
+
const next = metas.map(m => m.id === id ? {
|
|
63
|
+
...m,
|
|
64
|
+
name
|
|
65
|
+
} : m);
|
|
66
|
+
await saveIndex(next);
|
|
67
|
+
const stored = await loadSnapshot(id);
|
|
68
|
+
if (stored) {
|
|
69
|
+
stored.meta.name = name;
|
|
70
|
+
await persistentStorage.setItem(SNAP_PREFIX + id, JSON.stringify(stored));
|
|
71
|
+
}
|
|
72
|
+
return next;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=snapshotVault.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["persistentStorage","INDEX_KEY","SNAP_PREFIX","loadIndex","raw","getItem","parsed","JSON","parse","Array","isArray","saveIndex","metas","setItem","stringify","saveSnapshot","snapshot","serialized","meta","sizeBytes","length","id","next","filter","m","loadSnapshot","deleteSnapshot","removeItem","renameSnapshot","name","map","stored"],"sourceRoot":"../../../src","sources":["store/snapshotVault.ts"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Time Machine orchestrator — snapshot CRUD + capture/restore across every
|
|
5
|
+
* registered StateSnapshotProvider (storage, redux, zustand, jotai, query,
|
|
6
|
+
* plus any custom providers the app registers).
|
|
7
|
+
*
|
|
8
|
+
* Restore ordering is fixed: persisted storage FIRST (so store persist
|
|
9
|
+
* middleware writing through after an in-memory restore converges with what
|
|
10
|
+
* we just wrote), then redux → zustand → jotai, query LAST (restored queries
|
|
11
|
+
* may derive from store state). Custom providers run after, in data order.
|
|
12
|
+
*
|
|
13
|
+
* Two restore modes:
|
|
14
|
+
* - "live": in-place state replacement, capability-gated per source.
|
|
15
|
+
* - "reload": restore ONLY persisted storage, then reload the JS bundle —
|
|
16
|
+
* in-memory stores rebuild themselves from the restored storage.
|
|
17
|
+
* Baseline snapshots always use this path (clear + reload).
|
|
18
|
+
*/
|
|
19
|
+
import { getStateSnapshotProvider, getStateSnapshotProviders, scheduleReloadApp, superJsonDeserialize, superJsonSerialize } from "@buoy-gg/shared-ui";
|
|
20
|
+
import { deleteSnapshot, loadIndex, loadSnapshot, renameSnapshot, saveSnapshot } from "./snapshotVault";
|
|
21
|
+
export const RESTORE_ORDER = ["storage", "redux", "zustand", "jotai", "query"];
|
|
22
|
+
|
|
23
|
+
/** Provider capability row as mirrored to a remote dashboard. */
|
|
24
|
+
|
|
25
|
+
let remoteHandler = null;
|
|
26
|
+
function skippedResult(reason) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
applied: 0,
|
|
30
|
+
skipped: [{
|
|
31
|
+
id: "*",
|
|
32
|
+
reason
|
|
33
|
+
}],
|
|
34
|
+
warnings: []
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
let idCounter = 0;
|
|
38
|
+
function nextId() {
|
|
39
|
+
idCounter += 1;
|
|
40
|
+
return `snap_${Date.now().toString(36)}_${idCounter.toString(36)}`;
|
|
41
|
+
}
|
|
42
|
+
class TimeMachineStore {
|
|
43
|
+
state = {
|
|
44
|
+
snapshots: [],
|
|
45
|
+
hasLoaded: false,
|
|
46
|
+
busy: null,
|
|
47
|
+
lastOutcome: null,
|
|
48
|
+
remoteProviders: null
|
|
49
|
+
};
|
|
50
|
+
listeners = new Set();
|
|
51
|
+
loadPromise = null;
|
|
52
|
+
getState() {
|
|
53
|
+
return this.state;
|
|
54
|
+
}
|
|
55
|
+
subscribe = listener => {
|
|
56
|
+
this.listeners.add(listener);
|
|
57
|
+
void this.ensureLoaded();
|
|
58
|
+
return () => {
|
|
59
|
+
this.listeners.delete(listener);
|
|
60
|
+
};
|
|
61
|
+
};
|
|
62
|
+
setState(patch) {
|
|
63
|
+
this.state = {
|
|
64
|
+
...this.state,
|
|
65
|
+
...patch
|
|
66
|
+
};
|
|
67
|
+
this.listeners.forEach(l => {
|
|
68
|
+
try {
|
|
69
|
+
l();
|
|
70
|
+
} catch {
|
|
71
|
+
// listener errors must not break the store
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
ensureLoaded() {
|
|
76
|
+
if (remoteHandler) return Promise.resolve(); // mirror mode — device owns the vault
|
|
77
|
+
if (!this.loadPromise) {
|
|
78
|
+
this.loadPromise = loadIndex().then(snapshots => {
|
|
79
|
+
if (remoteHandler) return;
|
|
80
|
+
this.setState({
|
|
81
|
+
snapshots,
|
|
82
|
+
hasLoaded: true
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
return this.loadPromise;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** @internal Buoy Desktop: mirror the device's synced metadata payload. */
|
|
90
|
+
replaceFromSnapshot(payload) {
|
|
91
|
+
this.setState({
|
|
92
|
+
snapshots: payload.snapshots ?? [],
|
|
93
|
+
busy: payload.busy ?? null,
|
|
94
|
+
lastOutcome: payload.lastOutcome ?? null,
|
|
95
|
+
remoteProviders: payload.providers ?? [],
|
|
96
|
+
hasLoaded: true
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Capture all capturable sources (or a subset) into a named snapshot. */
|
|
101
|
+
async capture(name, sourceIds) {
|
|
102
|
+
if (remoteHandler) {
|
|
103
|
+
const result = await remoteHandler("capture", {
|
|
104
|
+
name,
|
|
105
|
+
sourceIds
|
|
106
|
+
});
|
|
107
|
+
return result.snapshot;
|
|
108
|
+
}
|
|
109
|
+
await this.ensureLoaded();
|
|
110
|
+
this.setState({
|
|
111
|
+
busy: "capture"
|
|
112
|
+
});
|
|
113
|
+
try {
|
|
114
|
+
const providers = getStateSnapshotProviders().filter(p => p.status().canCapture && (!sourceIds || sourceIds.includes(p.id)));
|
|
115
|
+
const data = {};
|
|
116
|
+
const sources = {};
|
|
117
|
+
for (const provider of providers) {
|
|
118
|
+
try {
|
|
119
|
+
const captured = await provider.capture();
|
|
120
|
+
data[provider.id] = superJsonSerialize(captured.data);
|
|
121
|
+
sources[provider.id] = {
|
|
122
|
+
warnings: captured.warnings
|
|
123
|
+
};
|
|
124
|
+
} catch (e) {
|
|
125
|
+
sources[provider.id] = {
|
|
126
|
+
warnings: [`capture failed: ${String(e)}`]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const snapshot = {
|
|
131
|
+
meta: {
|
|
132
|
+
id: nextId(),
|
|
133
|
+
name: name.trim() || "Untitled snapshot",
|
|
134
|
+
createdAt: Date.now(),
|
|
135
|
+
sizeBytes: 0,
|
|
136
|
+
sources
|
|
137
|
+
},
|
|
138
|
+
data
|
|
139
|
+
};
|
|
140
|
+
const snapshots = await saveSnapshot(snapshot, this.state.snapshots);
|
|
141
|
+
this.setState({
|
|
142
|
+
snapshots
|
|
143
|
+
});
|
|
144
|
+
return snapshot.meta;
|
|
145
|
+
} finally {
|
|
146
|
+
this.setState({
|
|
147
|
+
busy: null
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Capture the fresh-install baseline: an EMPTY snapshot whose restore
|
|
154
|
+
* wipes app state (clear + reload) — regardless of what state the app is
|
|
155
|
+
* in when the button is pressed.
|
|
156
|
+
*/
|
|
157
|
+
async captureBaseline() {
|
|
158
|
+
if (remoteHandler) {
|
|
159
|
+
const result = await remoteHandler("captureBaseline", {});
|
|
160
|
+
return result.snapshot;
|
|
161
|
+
}
|
|
162
|
+
await this.ensureLoaded();
|
|
163
|
+
const snapshot = {
|
|
164
|
+
meta: {
|
|
165
|
+
id: nextId(),
|
|
166
|
+
name: "Fresh install",
|
|
167
|
+
createdAt: Date.now(),
|
|
168
|
+
sizeBytes: 0,
|
|
169
|
+
baseline: true,
|
|
170
|
+
sources: {}
|
|
171
|
+
},
|
|
172
|
+
data: {}
|
|
173
|
+
};
|
|
174
|
+
const snapshots = await saveSnapshot(snapshot, this.state.snapshots);
|
|
175
|
+
this.setState({
|
|
176
|
+
snapshots
|
|
177
|
+
});
|
|
178
|
+
return snapshot.meta;
|
|
179
|
+
}
|
|
180
|
+
async restore(id, mode) {
|
|
181
|
+
if (remoteHandler) {
|
|
182
|
+
const outcome = await remoteHandler("restore", {
|
|
183
|
+
id,
|
|
184
|
+
mode
|
|
185
|
+
});
|
|
186
|
+
this.setState({
|
|
187
|
+
lastOutcome: outcome
|
|
188
|
+
});
|
|
189
|
+
return outcome;
|
|
190
|
+
}
|
|
191
|
+
await this.ensureLoaded();
|
|
192
|
+
this.setState({
|
|
193
|
+
busy: "restore"
|
|
194
|
+
});
|
|
195
|
+
try {
|
|
196
|
+
const stored = await loadSnapshot(id);
|
|
197
|
+
if (!stored) throw new Error(`No snapshot with id "${id}"`);
|
|
198
|
+
const outcome = {
|
|
199
|
+
snapshotId: id,
|
|
200
|
+
snapshotName: stored.meta.name,
|
|
201
|
+
mode,
|
|
202
|
+
at: Date.now(),
|
|
203
|
+
willReload: false,
|
|
204
|
+
results: {}
|
|
205
|
+
};
|
|
206
|
+
if (stored.meta.baseline) {
|
|
207
|
+
// Fresh install: wipe every clearable source, then reload so all
|
|
208
|
+
// in-memory state rebuilds from nothing.
|
|
209
|
+
for (const provider of getStateSnapshotProviders()) {
|
|
210
|
+
if (provider.clear) {
|
|
211
|
+
try {
|
|
212
|
+
outcome.results[provider.id] = await provider.clear();
|
|
213
|
+
} catch (e) {
|
|
214
|
+
outcome.results[provider.id] = skippedResult(String(e));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
outcome.willReload = this.scheduleReload("buoy time-machine baseline");
|
|
219
|
+
this.setState({
|
|
220
|
+
lastOutcome: outcome
|
|
221
|
+
});
|
|
222
|
+
return outcome;
|
|
223
|
+
}
|
|
224
|
+
const sourceIds = [...RESTORE_ORDER.filter(sid => sid in stored.data), ...Object.keys(stored.data).filter(sid => !RESTORE_ORDER.includes(sid))];
|
|
225
|
+
if (mode === "reload") {
|
|
226
|
+
// Storage only; everything in-memory rebuilds after the reload.
|
|
227
|
+
if (stored.data.storage) {
|
|
228
|
+
outcome.results.storage = await this.restoreSource("storage", stored.data.storage);
|
|
229
|
+
}
|
|
230
|
+
outcome.willReload = this.scheduleReload("buoy time-machine restore");
|
|
231
|
+
this.setState({
|
|
232
|
+
lastOutcome: outcome
|
|
233
|
+
});
|
|
234
|
+
return outcome;
|
|
235
|
+
}
|
|
236
|
+
for (const sid of sourceIds) {
|
|
237
|
+
outcome.results[sid] = await this.restoreSource(sid, stored.data[sid]);
|
|
238
|
+
}
|
|
239
|
+
this.setState({
|
|
240
|
+
lastOutcome: outcome
|
|
241
|
+
});
|
|
242
|
+
return outcome;
|
|
243
|
+
} finally {
|
|
244
|
+
this.setState({
|
|
245
|
+
busy: null
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
async restoreSource(sourceId, payload) {
|
|
250
|
+
const provider = getStateSnapshotProvider(sourceId);
|
|
251
|
+
if (!provider) {
|
|
252
|
+
return skippedResult("provider not available (package missing or not initialized)");
|
|
253
|
+
}
|
|
254
|
+
const status = provider.status();
|
|
255
|
+
if (!status.canRestore) {
|
|
256
|
+
return skippedResult(status.detail ?? "restore unavailable");
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
return await provider.restore(superJsonDeserialize(payload));
|
|
260
|
+
} catch (e) {
|
|
261
|
+
return skippedResult(String(e));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
scheduleReload(reason) {
|
|
265
|
+
try {
|
|
266
|
+
scheduleReloadApp({
|
|
267
|
+
reason
|
|
268
|
+
});
|
|
269
|
+
return true;
|
|
270
|
+
} catch {
|
|
271
|
+
// No reload mechanism (bare production build?) — restore still stands.
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async delete(id) {
|
|
276
|
+
if (remoteHandler) {
|
|
277
|
+
await remoteHandler("delete", {
|
|
278
|
+
id
|
|
279
|
+
});
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
await this.ensureLoaded();
|
|
283
|
+
const snapshots = await deleteSnapshot(id, this.state.snapshots);
|
|
284
|
+
this.setState({
|
|
285
|
+
snapshots
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
async rename(id, name) {
|
|
289
|
+
if (remoteHandler) {
|
|
290
|
+
await remoteHandler("rename", {
|
|
291
|
+
id,
|
|
292
|
+
name
|
|
293
|
+
});
|
|
294
|
+
return;
|
|
295
|
+
}
|
|
296
|
+
await this.ensureLoaded();
|
|
297
|
+
const snapshots = await renameSnapshot(id, name, this.state.snapshots);
|
|
298
|
+
this.setState({
|
|
299
|
+
snapshots
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
clearLastOutcome() {
|
|
303
|
+
this.setState({
|
|
304
|
+
lastOutcome: null
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
export const timeMachineStore = new TimeMachineStore();
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Buoy Desktop: route every Time Machine operation through a remote handler
|
|
312
|
+
* (the device's sync adapter actions) instead of running locally. Pass null
|
|
313
|
+
* to detach. While attached, feed device payloads to
|
|
314
|
+
* {@link replaceTimeMachineFromSnapshot}.
|
|
315
|
+
*/
|
|
316
|
+
export function setRemoteTimeMachineHandler(handler) {
|
|
317
|
+
remoteHandler = handler;
|
|
318
|
+
if (!handler) {
|
|
319
|
+
timeMachineStore.replaceFromSnapshot({});
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/** Buoy Desktop: mirror a device's synced Time Machine metadata payload. */
|
|
324
|
+
export function replaceTimeMachineFromSnapshot(payload) {
|
|
325
|
+
timeMachineStore.replaceFromSnapshot(payload);
|
|
326
|
+
}
|
|
327
|
+
//# sourceMappingURL=timeMachineStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getStateSnapshotProvider","getStateSnapshotProviders","scheduleReloadApp","superJsonDeserialize","superJsonSerialize","deleteSnapshot","loadIndex","loadSnapshot","renameSnapshot","saveSnapshot","RESTORE_ORDER","remoteHandler","skippedResult","reason","ok","applied","skipped","id","warnings","idCounter","nextId","Date","now","toString","TimeMachineStore","state","snapshots","hasLoaded","busy","lastOutcome","remoteProviders","listeners","Set","loadPromise","getState","subscribe","listener","add","ensureLoaded","delete","setState","patch","forEach","l","Promise","resolve","then","replaceFromSnapshot","payload","providers","capture","name","sourceIds","result","snapshot","filter","p","status","canCapture","includes","data","sources","provider","captured","e","String","meta","trim","createdAt","sizeBytes","captureBaseline","baseline","restore","mode","outcome","stored","Error","snapshotId","snapshotName","at","willReload","results","clear","scheduleReload","sid","Object","keys","storage","restoreSource","sourceId","canRestore","detail","rename","clearLastOutcome","timeMachineStore","setRemoteTimeMachineHandler","handler","replaceTimeMachineFromSnapshot"],"sourceRoot":"../../../src","sources":["store/timeMachineStore.ts"],"mappings":";;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SACEA,wBAAwB,EACxBC,yBAAyB,EACzBC,iBAAiB,EACjBC,oBAAoB,EACpBC,kBAAkB,QAGb,oBAAoB;AAC3B,SACEC,cAAc,EACdC,SAAS,EACTC,YAAY,EACZC,cAAc,EACdC,YAAY,QAGP,iBAAiB;AAExB,OAAO,MAAMC,aAAa,GAAG,CAAC,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,CAAC;;AAc9E;;AA2BA,IAAIC,aAAmC,GAAG,IAAI;AAE9C,SAASC,aAAaA,CAACC,MAAc,EAA8B;EACjE,OAAO;IAAEC,EAAE,EAAE,KAAK;IAAEC,OAAO,EAAE,CAAC;IAAEC,OAAO,EAAE,CAAC;MAAEC,EAAE,EAAE,GAAG;MAAEJ;IAAO,CAAC,CAAC;IAAEK,QAAQ,EAAE;EAAG,CAAC;AAChF;AAEA,IAAIC,SAAS,GAAG,CAAC;AACjB,SAASC,MAAMA,CAAA,EAAW;EACxBD,SAAS,IAAI,CAAC;EACd,OAAO,QAAQE,IAAI,CAACC,GAAG,CAAC,CAAC,CAACC,QAAQ,CAAC,EAAE,CAAC,IAAIJ,SAAS,CAACI,QAAQ,CAAC,EAAE,CAAC,EAAE;AACpE;AAEA,MAAMC,gBAAgB,CAAC;EACbC,KAAK,GAAqB;IAChCC,SAAS,EAAE,EAAE;IACbC,SAAS,EAAE,KAAK;IAChBC,IAAI,EAAE,IAAI;IACVC,WAAW,EAAE,IAAI;IACjBC,eAAe,EAAE;EACnB,CAAC;EACOC,SAAS,GAAG,IAAIC,GAAG,CAAa,CAAC;EACjCC,WAAW,GAAyB,IAAI;EAEhDC,QAAQA,CAAA,EAAqB;IAC3B,OAAO,IAAI,CAACT,KAAK;EACnB;EAEAU,SAAS,GAAIC,QAAoB,IAAmB;IAClD,IAAI,CAACL,SAAS,CAACM,GAAG,CAACD,QAAQ,CAAC;IAC5B,KAAK,IAAI,CAACE,YAAY,CAAC,CAAC;IACxB,OAAO,MAAM;MACX,IAAI,CAACP,SAAS,CAACQ,MAAM,CAACH,QAAQ,CAAC;IACjC,CAAC;EACH,CAAC;EAEOI,QAAQA,CAACC,KAAgC,EAAE;IACjD,IAAI,CAAChB,KAAK,GAAG;MAAE,GAAG,IAAI,CAACA,KAAK;MAAE,GAAGgB;IAAM,CAAC;IACxC,IAAI,CAACV,SAAS,CAACW,OAAO,CAAEC,CAAC,IAAK;MAC5B,IAAI;QACFA,CAAC,CAAC,CAAC;MACL,CAAC,CAAC,MAAM;QACN;MAAA;IAEJ,CAAC,CAAC;EACJ;EAEAL,YAAYA,CAAA,EAAkB;IAC5B,IAAI3B,aAAa,EAAE,OAAOiC,OAAO,CAACC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7C,IAAI,CAAC,IAAI,CAACZ,WAAW,EAAE;MACrB,IAAI,CAACA,WAAW,GAAG3B,SAAS,CAAC,CAAC,CAACwC,IAAI,CAAEpB,SAAS,IAAK;QACjD,IAAIf,aAAa,EAAE;QACnB,IAAI,CAAC6B,QAAQ,CAAC;UAAEd,SAAS;UAAEC,SAAS,EAAE;QAAK,CAAC,CAAC;MAC/C,CAAC,CAAC;IACJ;IACA,OAAO,IAAI,CAACM,WAAW;EACzB;;EAEA;EACAc,mBAAmBA,CAACC,OAKnB,EAAQ;IACP,IAAI,CAACR,QAAQ,CAAC;MACZd,SAAS,EAAEsB,OAAO,CAACtB,SAAS,IAAI,EAAE;MAClCE,IAAI,EAAEoB,OAAO,CAACpB,IAAI,IAAI,IAAI;MAC1BC,WAAW,EAAEmB,OAAO,CAACnB,WAAW,IAAI,IAAI;MACxCC,eAAe,EAAEkB,OAAO,CAACC,SAAS,IAAI,EAAE;MACxCtB,SAAS,EAAE;IACb,CAAC,CAAC;EACJ;;EAEA;EACA,MAAMuB,OAAOA,CAACC,IAAY,EAAEC,SAAoB,EAAyB;IACvE,IAAIzC,aAAa,EAAE;MACjB,MAAM0C,MAAM,GAAI,MAAM1C,aAAa,CAAC,SAAS,EAAE;QAAEwC,IAAI;QAAEC;MAAU,CAAC,CAEjE;MACD,OAAOC,MAAM,CAACC,QAAQ;IACxB;IACA,MAAM,IAAI,CAAChB,YAAY,CAAC,CAAC;IACzB,IAAI,CAACE,QAAQ,CAAC;MAAEZ,IAAI,EAAE;IAAU,CAAC,CAAC;IAClC,IAAI;MACF,MAAMqB,SAAS,GAAGhD,yBAAyB,CAAC,CAAC,CAACsD,MAAM,CACjDC,CAAC,IACAA,CAAC,CAACC,MAAM,CAAC,CAAC,CAACC,UAAU,KAAK,CAACN,SAAS,IAAIA,SAAS,CAACO,QAAQ,CAACH,CAAC,CAACvC,EAAE,CAAC,CACpE,CAAC;MACD,MAAM2C,IAAqC,GAAG,CAAC,CAAC;MAChD,MAAMC,OAAgC,GAAG,CAAC,CAAC;MAC3C,KAAK,MAAMC,QAAQ,IAAIb,SAAS,EAAE;QAChC,IAAI;UACF,MAAMc,QAAQ,GAAG,MAAMD,QAAQ,CAACZ,OAAO,CAAC,CAAC;UACzCU,IAAI,CAACE,QAAQ,CAAC7C,EAAE,CAAC,GAAGb,kBAAkB,CAAC2D,QAAQ,CAACH,IAAI,CAAC;UACrDC,OAAO,CAACC,QAAQ,CAAC7C,EAAE,CAAC,GAAG;YAAEC,QAAQ,EAAE6C,QAAQ,CAAC7C;UAAS,CAAC;QACxD,CAAC,CAAC,OAAO8C,CAAC,EAAE;UACVH,OAAO,CAACC,QAAQ,CAAC7C,EAAE,CAAC,GAAG;YACrBC,QAAQ,EAAE,CAAC,mBAAmB+C,MAAM,CAACD,CAAC,CAAC,EAAE;UAC3C,CAAC;QACH;MACF;MACA,MAAMV,QAAwB,GAAG;QAC/BY,IAAI,EAAE;UACJjD,EAAE,EAAEG,MAAM,CAAC,CAAC;UACZ+B,IAAI,EAAEA,IAAI,CAACgB,IAAI,CAAC,CAAC,IAAI,mBAAmB;UACxCC,SAAS,EAAE/C,IAAI,CAACC,GAAG,CAAC,CAAC;UACrB+C,SAAS,EAAE,CAAC;UACZR;QACF,CAAC;QACDD;MACF,CAAC;MACD,MAAMlC,SAAS,GAAG,MAAMjB,YAAY,CAAC6C,QAAQ,EAAE,IAAI,CAAC7B,KAAK,CAACC,SAAS,CAAC;MACpE,IAAI,CAACc,QAAQ,CAAC;QAAEd;MAAU,CAAC,CAAC;MAC5B,OAAO4B,QAAQ,CAACY,IAAI;IACtB,CAAC,SAAS;MACR,IAAI,CAAC1B,QAAQ,CAAC;QAAEZ,IAAI,EAAE;MAAK,CAAC,CAAC;IAC/B;EACF;;EAEA;AACF;AACA;AACA;AACA;EACE,MAAM0C,eAAeA,CAAA,EAA0B;IAC7C,IAAI3D,aAAa,EAAE;MACjB,MAAM0C,MAAM,GAAI,MAAM1C,aAAa,CAAC,iBAAiB,EAAE,CAAC,CAAC,CAExD;MACD,OAAO0C,MAAM,CAACC,QAAQ;IACxB;IACA,MAAM,IAAI,CAAChB,YAAY,CAAC,CAAC;IACzB,MAAMgB,QAAwB,GAAG;MAC/BY,IAAI,EAAE;QACJjD,EAAE,EAAEG,MAAM,CAAC,CAAC;QACZ+B,IAAI,EAAE,eAAe;QACrBiB,SAAS,EAAE/C,IAAI,CAACC,GAAG,CAAC,CAAC;QACrB+C,SAAS,EAAE,CAAC;QACZE,QAAQ,EAAE,IAAI;QACdV,OAAO,EAAE,CAAC;MACZ,CAAC;MACDD,IAAI,EAAE,CAAC;IACT,CAAC;IACD,MAAMlC,SAAS,GAAG,MAAMjB,YAAY,CAAC6C,QAAQ,EAAE,IAAI,CAAC7B,KAAK,CAACC,SAAS,CAAC;IACpE,IAAI,CAACc,QAAQ,CAAC;MAAEd;IAAU,CAAC,CAAC;IAC5B,OAAO4B,QAAQ,CAACY,IAAI;EACtB;EAEA,MAAMM,OAAOA,CAACvD,EAAU,EAAEwD,IAAiB,EAA2B;IACpE,IAAI9D,aAAa,EAAE;MACjB,MAAM+D,OAAO,GAAI,MAAM/D,aAAa,CAAC,SAAS,EAAE;QAAEM,EAAE;QAAEwD;MAAK,CAAC,CAAoB;MAChF,IAAI,CAACjC,QAAQ,CAAC;QAAEX,WAAW,EAAE6C;MAAQ,CAAC,CAAC;MACvC,OAAOA,OAAO;IAChB;IACA,MAAM,IAAI,CAACpC,YAAY,CAAC,CAAC;IACzB,IAAI,CAACE,QAAQ,CAAC;MAAEZ,IAAI,EAAE;IAAU,CAAC,CAAC;IAClC,IAAI;MACF,MAAM+C,MAAM,GAAG,MAAMpE,YAAY,CAACU,EAAE,CAAC;MACrC,IAAI,CAAC0D,MAAM,EAAE,MAAM,IAAIC,KAAK,CAAC,wBAAwB3D,EAAE,GAAG,CAAC;MAE3D,MAAMyD,OAAuB,GAAG;QAC9BG,UAAU,EAAE5D,EAAE;QACd6D,YAAY,EAAEH,MAAM,CAACT,IAAI,CAACf,IAAI;QAC9BsB,IAAI;QACJM,EAAE,EAAE1D,IAAI,CAACC,GAAG,CAAC,CAAC;QACd0D,UAAU,EAAE,KAAK;QACjBC,OAAO,EAAE,CAAC;MACZ,CAAC;MAED,IAAIN,MAAM,CAACT,IAAI,CAACK,QAAQ,EAAE;QACxB;QACA;QACA,KAAK,MAAMT,QAAQ,IAAI7D,yBAAyB,CAAC,CAAC,EAAE;UAClD,IAAI6D,QAAQ,CAACoB,KAAK,EAAE;YAClB,IAAI;cACFR,OAAO,CAACO,OAAO,CAACnB,QAAQ,CAAC7C,EAAE,CAAC,GAAG,MAAM6C,QAAQ,CAACoB,KAAK,CAAC,CAAC;YACvD,CAAC,CAAC,OAAOlB,CAAC,EAAE;cACVU,OAAO,CAACO,OAAO,CAACnB,QAAQ,CAAC7C,EAAE,CAAC,GAAGL,aAAa,CAACqD,MAAM,CAACD,CAAC,CAAC,CAAC;YACzD;UACF;QACF;QACAU,OAAO,CAACM,UAAU,GAAG,IAAI,CAACG,cAAc,CAAC,4BAA4B,CAAC;QACtE,IAAI,CAAC3C,QAAQ,CAAC;UAAEX,WAAW,EAAE6C;QAAQ,CAAC,CAAC;QACvC,OAAOA,OAAO;MAChB;MAEA,MAAMtB,SAAS,GAAG,CAChB,GAAG1C,aAAa,CAAC6C,MAAM,CAAE6B,GAAG,IAAKA,GAAG,IAAIT,MAAM,CAACf,IAAI,CAAC,EACpD,GAAGyB,MAAM,CAACC,IAAI,CAACX,MAAM,CAACf,IAAI,CAAC,CAACL,MAAM,CAAE6B,GAAG,IAAK,CAAC1E,aAAa,CAACiD,QAAQ,CAACyB,GAAG,CAAC,CAAC,CAC1E;MAED,IAAIX,IAAI,KAAK,QAAQ,EAAE;QACrB;QACA,IAAIE,MAAM,CAACf,IAAI,CAAC2B,OAAO,EAAE;UACvBb,OAAO,CAACO,OAAO,CAACM,OAAO,GAAG,MAAM,IAAI,CAACC,aAAa,CAChD,SAAS,EACTb,MAAM,CAACf,IAAI,CAAC2B,OACd,CAAC;QACH;QACAb,OAAO,CAACM,UAAU,GAAG,IAAI,CAACG,cAAc,CAAC,2BAA2B,CAAC;QACrE,IAAI,CAAC3C,QAAQ,CAAC;UAAEX,WAAW,EAAE6C;QAAQ,CAAC,CAAC;QACvC,OAAOA,OAAO;MAChB;MAEA,KAAK,MAAMU,GAAG,IAAIhC,SAAS,EAAE;QAC3BsB,OAAO,CAACO,OAAO,CAACG,GAAG,CAAC,GAAG,MAAM,IAAI,CAACI,aAAa,CAACJ,GAAG,EAAET,MAAM,CAACf,IAAI,CAACwB,GAAG,CAAC,CAAC;MACxE;MACA,IAAI,CAAC5C,QAAQ,CAAC;QAAEX,WAAW,EAAE6C;MAAQ,CAAC,CAAC;MACvC,OAAOA,OAAO;IAChB,CAAC,SAAS;MACR,IAAI,CAAClC,QAAQ,CAAC;QAAEZ,IAAI,EAAE;MAAK,CAAC,CAAC;IAC/B;EACF;EAEA,MAAc4D,aAAaA,CACzBC,QAAgB,EAChBzC,OAAwB,EACa;IACrC,MAAMc,QAAQ,GAAG9D,wBAAwB,CAACyF,QAAQ,CAAC;IACnD,IAAI,CAAC3B,QAAQ,EAAE;MACb,OAAOlD,aAAa,CAAC,6DAA6D,CAAC;IACrF;IACA,MAAM6C,MAAM,GAAGK,QAAQ,CAACL,MAAM,CAAC,CAAC;IAChC,IAAI,CAACA,MAAM,CAACiC,UAAU,EAAE;MACtB,OAAO9E,aAAa,CAAC6C,MAAM,CAACkC,MAAM,IAAI,qBAAqB,CAAC;IAC9D;IACA,IAAI;MACF,OAAO,MAAM7B,QAAQ,CAACU,OAAO,CAACrE,oBAAoB,CAAC6C,OAAO,CAAC,CAAC;IAC9D,CAAC,CAAC,OAAOgB,CAAC,EAAE;MACV,OAAOpD,aAAa,CAACqD,MAAM,CAACD,CAAC,CAAC,CAAC;IACjC;EACF;EAEQmB,cAAcA,CAACtE,MAAc,EAAW;IAC9C,IAAI;MACFX,iBAAiB,CAAC;QAAEW;MAAO,CAAC,CAAC;MAC7B,OAAO,IAAI;IACb,CAAC,CAAC,MAAM;MACN;MACA,OAAO,KAAK;IACd;EACF;EAEA,MAAM0B,MAAMA,CAACtB,EAAU,EAAiB;IACtC,IAAIN,aAAa,EAAE;MACjB,MAAMA,aAAa,CAAC,QAAQ,EAAE;QAAEM;MAAG,CAAC,CAAC;MACrC;IACF;IACA,MAAM,IAAI,CAACqB,YAAY,CAAC,CAAC;IACzB,MAAMZ,SAAS,GAAG,MAAMrB,cAAc,CAACY,EAAE,EAAE,IAAI,CAACQ,KAAK,CAACC,SAAS,CAAC;IAChE,IAAI,CAACc,QAAQ,CAAC;MAAEd;IAAU,CAAC,CAAC;EAC9B;EAEA,MAAMkE,MAAMA,CAAC3E,EAAU,EAAEkC,IAAY,EAAiB;IACpD,IAAIxC,aAAa,EAAE;MACjB,MAAMA,aAAa,CAAC,QAAQ,EAAE;QAAEM,EAAE;QAAEkC;MAAK,CAAC,CAAC;MAC3C;IACF;IACA,MAAM,IAAI,CAACb,YAAY,CAAC,CAAC;IACzB,MAAMZ,SAAS,GAAG,MAAMlB,cAAc,CAACS,EAAE,EAAEkC,IAAI,EAAE,IAAI,CAAC1B,KAAK,CAACC,SAAS,CAAC;IACtE,IAAI,CAACc,QAAQ,CAAC;MAAEd;IAAU,CAAC,CAAC;EAC9B;EAEAmE,gBAAgBA,CAAA,EAAS;IACvB,IAAI,CAACrD,QAAQ,CAAC;MAAEX,WAAW,EAAE;IAAK,CAAC,CAAC;EACtC;AACF;AAEA,OAAO,MAAMiE,gBAAgB,GAAG,IAAItE,gBAAgB,CAAC,CAAC;;AAEtD;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASuE,2BAA2BA,CAACC,OAA6B,EAAQ;EAC/ErF,aAAa,GAAGqF,OAAO;EACvB,IAAI,CAACA,OAAO,EAAE;IACZF,gBAAgB,CAAC/C,mBAAmB,CAAC,CAAC,CAAC,CAAC;EAC1C;AACF;;AAEA;AACA,OAAO,SAASkD,8BAA8BA,CAACjD,OAK9C,EAAQ;EACP8C,gBAAgB,CAAC/C,mBAAmB,CAACC,OAAO,CAAC;AAC/C","ignoreList":[]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Sync adapter for the Time Machine tool, consumed by @buoy-gg/external-sync's
|
|
5
|
+
* `useExternalSync` (structurally matches its ToolSyncAdapter interface so
|
|
6
|
+
* this package doesn't need a dependency on it).
|
|
7
|
+
*
|
|
8
|
+
* Snapshots stay ON-DEVICE — getSnapshot ships METADATA only (list, provider
|
|
9
|
+
* statuses, last restore outcome), far under the 2MB per-tool budget. Payload
|
|
10
|
+
* inspection goes through the `inspect` action (8MB action-result budget).
|
|
11
|
+
*
|
|
12
|
+
* The `restore` action uses the store's scheduleReloadApp path for reload
|
|
13
|
+
* mode, so the action RESULT is flushed to the broker before the JS realm
|
|
14
|
+
* dies — a remote caller always gets its answer.
|
|
15
|
+
*/
|
|
16
|
+
import { getStateSnapshotProviders, subscribeToStateSnapshotProviders, superJsonDeserialize } from "@buoy-gg/shared-ui";
|
|
17
|
+
import { timeMachineStore } from "../store/timeMachineStore";
|
|
18
|
+
import { loadSnapshot } from "../store/snapshotVault";
|
|
19
|
+
function providerStatuses() {
|
|
20
|
+
return getStateSnapshotProviders().map(p => {
|
|
21
|
+
const s = p.status();
|
|
22
|
+
return {
|
|
23
|
+
id: p.id,
|
|
24
|
+
label: p.label,
|
|
25
|
+
canCapture: s.canCapture,
|
|
26
|
+
canRestore: s.canRestore,
|
|
27
|
+
detail: s.detail
|
|
28
|
+
};
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function metadata() {
|
|
32
|
+
const {
|
|
33
|
+
snapshots,
|
|
34
|
+
busy,
|
|
35
|
+
lastOutcome
|
|
36
|
+
} = timeMachineStore.getState();
|
|
37
|
+
return {
|
|
38
|
+
snapshots,
|
|
39
|
+
busy,
|
|
40
|
+
lastOutcome,
|
|
41
|
+
providers: providerStatuses()
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export const timeMachineSyncAdapter = {
|
|
45
|
+
version: 1,
|
|
46
|
+
getSnapshot: () => metadata(),
|
|
47
|
+
subscribe: onChange => {
|
|
48
|
+
const unsubStore = timeMachineStore.subscribe(onChange);
|
|
49
|
+
const unsubProviders = subscribeToStateSnapshotProviders(onChange);
|
|
50
|
+
return () => {
|
|
51
|
+
unsubStore();
|
|
52
|
+
unsubProviders();
|
|
53
|
+
};
|
|
54
|
+
},
|
|
55
|
+
actions: {
|
|
56
|
+
list: () => metadata(),
|
|
57
|
+
capture: async params => {
|
|
58
|
+
const {
|
|
59
|
+
name = "",
|
|
60
|
+
sourceIds
|
|
61
|
+
} = params ?? {};
|
|
62
|
+
const meta = await timeMachineStore.capture(name, sourceIds);
|
|
63
|
+
return {
|
|
64
|
+
captured: true,
|
|
65
|
+
snapshot: meta
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
captureBaseline: async () => {
|
|
69
|
+
const meta = await timeMachineStore.captureBaseline();
|
|
70
|
+
return {
|
|
71
|
+
captured: true,
|
|
72
|
+
snapshot: meta
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
/** mode "live" (default) replaces state in place; "reload" restores
|
|
76
|
+
* persisted storage then reloads the JS bundle (always the path for
|
|
77
|
+
* baseline snapshots). */
|
|
78
|
+
restore: async params => {
|
|
79
|
+
const {
|
|
80
|
+
id,
|
|
81
|
+
mode = "live"
|
|
82
|
+
} = params ?? {};
|
|
83
|
+
if (!id) throw new Error("restore requires a snapshot `id`");
|
|
84
|
+
return await timeMachineStore.restore(id, mode);
|
|
85
|
+
},
|
|
86
|
+
delete: async params => {
|
|
87
|
+
const {
|
|
88
|
+
id
|
|
89
|
+
} = params ?? {};
|
|
90
|
+
if (!id) throw new Error("delete requires a snapshot `id`");
|
|
91
|
+
await timeMachineStore.delete(id);
|
|
92
|
+
return {
|
|
93
|
+
deleted: true,
|
|
94
|
+
id
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
rename: async params => {
|
|
98
|
+
const {
|
|
99
|
+
id,
|
|
100
|
+
name
|
|
101
|
+
} = params ?? {};
|
|
102
|
+
if (!id || !name) throw new Error("rename requires `id` and `name`");
|
|
103
|
+
await timeMachineStore.rename(id, name);
|
|
104
|
+
return {
|
|
105
|
+
renamed: true,
|
|
106
|
+
id,
|
|
107
|
+
name
|
|
108
|
+
};
|
|
109
|
+
},
|
|
110
|
+
/** One source's captured payload, deserialized — for remote inspection. */
|
|
111
|
+
inspect: async params => {
|
|
112
|
+
const {
|
|
113
|
+
id,
|
|
114
|
+
sourceId
|
|
115
|
+
} = params ?? {};
|
|
116
|
+
if (!id || !sourceId) {
|
|
117
|
+
throw new Error("inspect requires `id` and `sourceId`");
|
|
118
|
+
}
|
|
119
|
+
const stored = await loadSnapshot(id);
|
|
120
|
+
if (!stored) throw new Error(`No snapshot with id "${id}"`);
|
|
121
|
+
const payload = stored.data[sourceId];
|
|
122
|
+
if (!payload) {
|
|
123
|
+
throw new Error(`Snapshot "${id}" has no "${sourceId}" source`);
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
id,
|
|
127
|
+
sourceId,
|
|
128
|
+
warnings: stored.meta.sources[sourceId]?.warnings ?? [],
|
|
129
|
+
data: superJsonDeserialize(payload)
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
//# sourceMappingURL=timeMachineSyncAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["getStateSnapshotProviders","subscribeToStateSnapshotProviders","superJsonDeserialize","timeMachineStore","loadSnapshot","providerStatuses","map","p","s","status","id","label","canCapture","canRestore","detail","metadata","snapshots","busy","lastOutcome","getState","providers","timeMachineSyncAdapter","version","getSnapshot","subscribe","onChange","unsubStore","unsubProviders","actions","list","capture","params","name","sourceIds","meta","captured","snapshot","captureBaseline","restore","mode","Error","delete","deleted","rename","renamed","inspect","sourceId","stored","payload","data","warnings","sources"],"sourceRoot":"../../../src","sources":["sync/timeMachineSyncAdapter.ts"],"mappings":"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","ignoreList":[]}
|