@buoy-gg/jotai 7.0.35 → 7.0.38
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/README.md +32 -5
- package/lib/commonjs/index.js +1 -112
- package/lib/commonjs/jotai/components/JotaiAtomBrowser.js +1 -199
- package/lib/commonjs/jotai/components/JotaiAtomChangeItem.js +1 -85
- package/lib/commonjs/jotai/components/JotaiAtomDetailContent.js +1 -715
- package/lib/commonjs/jotai/components/JotaiAtomValueDetailContent.js +1 -444
- package/lib/commonjs/jotai/components/JotaiEventFilterView.js +1 -43
- package/lib/commonjs/jotai/components/JotaiIcon.js +1 -18
- package/lib/commonjs/jotai/components/JotaiModal.js +1 -743
- package/lib/commonjs/jotai/hooks/useJotaiAtomChanges.js +1 -67
- package/lib/commonjs/jotai/sync/jotaiSyncAdapter.js +1 -149
- package/lib/commonjs/jotai/utils/jotaiStateStore.js +1 -321
- package/lib/commonjs/jotai/utils/snapshotProvider.js +1 -215
- package/lib/commonjs/jotai/utils/testIDs.js +1 -90
- package/lib/commonjs/jotai/utils/watchAtoms.js +1 -162
- package/lib/commonjs/jotai/utils/writeSemantics.js +1 -47
- package/lib/commonjs/preset.js +1 -90
- package/lib/module/index.js +1 -85
- package/lib/module/jotai/components/JotaiAtomBrowser.js +1 -195
- package/lib/module/jotai/components/JotaiAtomChangeItem.js +1 -81
- package/lib/module/jotai/components/JotaiAtomDetailContent.js +1 -710
- package/lib/module/jotai/components/JotaiAtomValueDetailContent.js +1 -442
- package/lib/module/jotai/components/JotaiEventFilterView.js +1 -39
- package/lib/module/jotai/components/JotaiIcon.js +1 -11
- package/lib/module/jotai/components/JotaiModal.js +1 -738
- package/lib/module/jotai/hooks/useJotaiAtomChanges.js +1 -63
- package/lib/module/jotai/sync/jotaiSyncAdapter.js +1 -146
- package/lib/module/jotai/utils/jotaiStateStore.js +1 -317
- package/lib/module/jotai/utils/snapshotProvider.js +1 -214
- package/lib/module/jotai/utils/testIDs.js +1 -82
- package/lib/module/jotai/utils/watchAtoms.js +1 -157
- package/lib/module/jotai/utils/writeSemantics.js +1 -43
- package/lib/module/preset.js +1 -87
- package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts +52 -2
- package/package.json +4 -4
- package/lib/typescript/index.d.ts.map +0 -1
- package/lib/typescript/jotai/components/JotaiAtomBrowser.d.ts.map +0 -1
- package/lib/typescript/jotai/components/JotaiAtomChangeItem.d.ts.map +0 -1
- package/lib/typescript/jotai/components/JotaiAtomDetailContent.d.ts.map +0 -1
- package/lib/typescript/jotai/components/JotaiAtomValueDetailContent.d.ts.map +0 -1
- package/lib/typescript/jotai/components/JotaiEventFilterView.d.ts.map +0 -1
- package/lib/typescript/jotai/components/JotaiIcon.d.ts.map +0 -1
- package/lib/typescript/jotai/components/JotaiModal.d.ts.map +0 -1
- package/lib/typescript/jotai/hooks/useJotaiAtomChanges.d.ts.map +0 -1
- package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts.map +0 -1
- package/lib/typescript/jotai/types/index.d.ts.map +0 -1
- package/lib/typescript/jotai/utils/jotaiStateStore.d.ts.map +0 -1
- package/lib/typescript/jotai/utils/snapshotProvider.d.ts.map +0 -1
- package/lib/typescript/jotai/utils/testIDs.d.ts.map +0 -1
- package/lib/typescript/jotai/utils/watchAtoms.d.ts.map +0 -1
- package/lib/typescript/jotai/utils/writeSemantics.d.ts.map +0 -1
- package/lib/typescript/preset.d.ts.map +0 -1
|
@@ -1,67 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.useJotaiAtomChanges = useJotaiAtomChanges;
|
|
7
|
-
var _react = require("react");
|
|
8
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
|
-
var _jotaiStateStore = require("../utils/jotaiStateStore");
|
|
10
|
-
/**
|
|
11
|
-
* Hook for consuming Jotai atom changes from the store
|
|
12
|
-
*
|
|
13
|
-
* The subscribe / filter / pause / clear plumbing is shared `useChangeLog`;
|
|
14
|
-
* this file owns the jotai-specific filter, stats and atom registry.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
function filterChanges(changes, filter) {
|
|
18
|
-
let filtered = changes;
|
|
19
|
-
if (filter.searchText) {
|
|
20
|
-
const search = filter.searchText.toLowerCase();
|
|
21
|
-
filtered = filtered.filter(c => c.atomLabel.toLowerCase().includes(search) || c.valuePreview.toLowerCase().includes(search) || c.changedKeys.some(k => k.toLowerCase().includes(search)));
|
|
22
|
-
}
|
|
23
|
-
if (filter.atomLabels && filter.atomLabels.length > 0) {
|
|
24
|
-
filtered = filtered.filter(c => filter.atomLabels.includes(c.atomLabel));
|
|
25
|
-
}
|
|
26
|
-
if (filter.onlyWithChanges) {
|
|
27
|
-
filtered = filtered.filter(c => c.hasValueChange);
|
|
28
|
-
}
|
|
29
|
-
return filtered;
|
|
30
|
-
}
|
|
31
|
-
const readAtoms = () => _jotaiStateStore.jotaiStateStore.getAtoms();
|
|
32
|
-
const subscribeToAtoms = listener => _jotaiStateStore.jotaiStateStore.subscribeToAtoms(listener);
|
|
33
|
-
function useJotaiAtomChanges() {
|
|
34
|
-
const log = (0, _sharedUi.useChangeLog)(_jotaiStateStore.jotaiStateStore, {
|
|
35
|
-
filterItems: filterChanges
|
|
36
|
-
});
|
|
37
|
-
const atoms = (0, _sharedUi.useSubscribedValue)(readAtoms, subscribeToAtoms);
|
|
38
|
-
const stats = (0, _react.useMemo)(() => {
|
|
39
|
-
const total = log.items.length;
|
|
40
|
-
const withChanges = log.items.filter(c => c.hasValueChange).length;
|
|
41
|
-
return {
|
|
42
|
-
totalChanges: total,
|
|
43
|
-
changesWithValueChange: withChanges,
|
|
44
|
-
changesWithoutValueChange: total - withChanges,
|
|
45
|
-
atomCount: atoms.length
|
|
46
|
-
};
|
|
47
|
-
}, [log.items, atoms]);
|
|
48
|
-
const atomLabels = (0, _react.useMemo)(() => {
|
|
49
|
-
return _jotaiStateStore.jotaiStateStore.getUniqueAtomLabels();
|
|
50
|
-
}, [atoms]);
|
|
51
|
-
const getChangeById = (0, _react.useCallback)(id => {
|
|
52
|
-
return _jotaiStateStore.jotaiStateStore.getAtomChangeById(id);
|
|
53
|
-
}, []);
|
|
54
|
-
return {
|
|
55
|
-
atomChanges: log.items,
|
|
56
|
-
filteredChanges: log.filteredItems,
|
|
57
|
-
filter: log.filter,
|
|
58
|
-
setFilter: log.setFilter,
|
|
59
|
-
stats,
|
|
60
|
-
atoms,
|
|
61
|
-
clearChanges: log.clear,
|
|
62
|
-
isEnabled: log.isEnabled,
|
|
63
|
-
toggleCapture: log.toggleCapture,
|
|
64
|
-
atomLabels,
|
|
65
|
-
getChangeById
|
|
66
|
-
};
|
|
67
|
-
}
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.useJotaiAtomChanges=useJotaiAtomChanges;var _react=require("react"),_sharedUi=require("@buoy-gg/shared-ui"),_jotaiStateStore=require("../utils/jotaiStateStore");function filterChanges(e,t){let a=e;if(t.searchText){const e=t.searchText.toLowerCase();a=a.filter(t=>t.atomLabel.toLowerCase().includes(e)||t.valuePreview.toLowerCase().includes(e)||t.changedKeys.some(t=>t.toLowerCase().includes(e)))}return t.atomLabels&&t.atomLabels.length>0&&(a=a.filter(e=>t.atomLabels.includes(e.atomLabel))),t.onlyWithChanges&&(a=a.filter(e=>e.hasValueChange)),a}const readAtoms=()=>_jotaiStateStore.jotaiStateStore.getAtoms(),subscribeToAtoms=e=>_jotaiStateStore.jotaiStateStore.subscribeToAtoms(e);function useJotaiAtomChanges(){const e=(0,_sharedUi.useChangeLog)(_jotaiStateStore.jotaiStateStore,{filterItems:filterChanges}),t=(0,_sharedUi.useSubscribedValue)(readAtoms,subscribeToAtoms),a=(0,_react.useMemo)(()=>{const a=e.items.length,o=e.items.filter(e=>e.hasValueChange).length;return{totalChanges:a,changesWithValueChange:o,changesWithoutValueChange:a-o,atomCount:t.length}},[e.items,t]),o=(0,_react.useMemo)(()=>_jotaiStateStore.jotaiStateStore.getUniqueAtomLabels(),[t]),s=(0,_react.useCallback)(e=>_jotaiStateStore.jotaiStateStore.getAtomChangeById(e),[]);return{atomChanges:e.items,filteredChanges:e.filteredItems,filter:e.filter,setFilter:e.setFilter,stats:a,atoms:t,clearChanges:_jotaiStateStore.jotaiStateStore.clearAtomChanges.bind(_jotaiStateStore.jotaiStateStore),isEnabled:e.isEnabled,toggleCapture:e.toggleCapture,atomLabels:o,getChangeById:s}}
|
|
@@ -1,149 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.jotaiSyncAdapter = exports.VALUE_ON_DEVICE = void 0;
|
|
7
|
-
var _jotaiStateStore = require("../utils/jotaiStateStore");
|
|
8
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
9
|
-
/**
|
|
10
|
-
* Sentinel used in WIRE-FORM snapshots in place of raw prevValue/nextValue.
|
|
11
|
-
* Same hazard as redux/zustand: up to 200 changes × 2 values on every
|
|
12
|
-
* snapshot. Dashboards fetch values via `getChangeDetail`.
|
|
13
|
-
*/
|
|
14
|
-
const VALUE_ON_DEVICE = exports.VALUE_ON_DEVICE = (0, _sharedUi.onDeviceMarker)("Value", "Atom values stay on the device — fetched on demand via getChangeDetail / getAtomValue.");
|
|
15
|
-
/**
|
|
16
|
-
* Per-change wire cache — the store prepends and never mutates a recorded
|
|
17
|
-
* change, so identity is a correct, self-invalidating key.
|
|
18
|
-
*/
|
|
19
|
-
const toWireChange = (0, _sharedUi.memoByIdentity)(change => ({
|
|
20
|
-
...change,
|
|
21
|
-
prevValue: VALUE_ON_DEVICE,
|
|
22
|
-
nextValue: VALUE_ON_DEVICE
|
|
23
|
-
}));
|
|
24
|
-
const toWireValue = value => (0, _sharedUi.toWire)(value, VALUE_ON_DEVICE);
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Sync adapter for the jotai tool, consumed by @buoy-gg/external-sync's
|
|
28
|
-
* `useExternalSync`.
|
|
29
|
-
*
|
|
30
|
-
* Atom changes are captured by watchAtoms(), independent of whether a
|
|
31
|
-
* dashboard is watching — subscribing here only streams what the store
|
|
32
|
-
* already records. The snapshot carries both the change timeline and the
|
|
33
|
-
* atom registry (with each atom's current value) so the dashboard can render
|
|
34
|
-
* the atom browser.
|
|
35
|
-
*
|
|
36
|
-
* v2: change-log prevValue/nextValue are replaced with on-device markers.
|
|
37
|
-
* Dashboards fetch values via `getChangeDetail`. Oversized / unserializable
|
|
38
|
-
* `atoms[].currentValue` uses the same marker and is fetched via `getAtomValue`.
|
|
39
|
-
*/
|
|
40
|
-
const jotaiSyncAdapter = exports.jotaiSyncAdapter = (0, _sharedUi.createSyncAdapter)({
|
|
41
|
-
version: 2,
|
|
42
|
-
getSnapshot: () => ({
|
|
43
|
-
changes: _jotaiStateStore.jotaiStateStore.getAtomChanges().map(toWireChange),
|
|
44
|
-
atoms: _jotaiStateStore.jotaiStateStore.getAtomSnapshots().map(atom => ({
|
|
45
|
-
...atom,
|
|
46
|
-
currentValue: toWireValue(atom.currentValue)
|
|
47
|
-
}))
|
|
48
|
-
}),
|
|
49
|
-
subscribe: [onChange => _jotaiStateStore.jotaiStateStore.subscribe(onChange), onChange => _jotaiStateStore.jotaiStateStore.subscribeToAtoms(onChange)],
|
|
50
|
-
actions: {
|
|
51
|
-
clearEvents: () => {
|
|
52
|
-
_jotaiStateStore.jotaiStateStore.clearAtomChanges();
|
|
53
|
-
},
|
|
54
|
-
/**
|
|
55
|
-
* On-demand detail: one change's real prevValue/nextValue.
|
|
56
|
-
* The per-snapshot stream stays value-free; this is the explicit,
|
|
57
|
-
* size-guarded channel for the detail pane.
|
|
58
|
-
*/
|
|
59
|
-
getChangeDetail: (0, _sharedUi.detailByIdAction)(id => _jotaiStateStore.jotaiStateStore.getAtomChangeById(id), (change, cap) => ({
|
|
60
|
-
prevValue: cap(change.prevValue),
|
|
61
|
-
nextValue: cap(change.nextValue)
|
|
62
|
-
})),
|
|
63
|
-
/**
|
|
64
|
-
* On-demand current value for one atom. Wire-form snapshots replace
|
|
65
|
-
* oversized / unserializable `currentValue` with VALUE_ON_DEVICE.
|
|
66
|
-
*/
|
|
67
|
-
getAtomValue: params => {
|
|
68
|
-
const {
|
|
69
|
-
label
|
|
70
|
-
} = (0, _sharedUi.readParams)(params);
|
|
71
|
-
if (!label) return {
|
|
72
|
-
found: false,
|
|
73
|
-
reason: "missing label"
|
|
74
|
-
};
|
|
75
|
-
const atom = _jotaiStateStore.jotaiStateStore.getAtom(label);
|
|
76
|
-
if (!atom) return {
|
|
77
|
-
found: false,
|
|
78
|
-
reason: "unknown label"
|
|
79
|
-
};
|
|
80
|
-
let currentValue;
|
|
81
|
-
try {
|
|
82
|
-
currentValue = atom.getValue();
|
|
83
|
-
} catch {
|
|
84
|
-
currentValue = undefined;
|
|
85
|
-
}
|
|
86
|
-
try {
|
|
87
|
-
JSON.stringify(currentValue);
|
|
88
|
-
} catch {
|
|
89
|
-
return {
|
|
90
|
-
found: true,
|
|
91
|
-
label,
|
|
92
|
-
currentValue: VALUE_ON_DEVICE
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
return {
|
|
96
|
-
found: true,
|
|
97
|
-
label,
|
|
98
|
-
currentValue: (0, _sharedUi.capDetail)(currentValue, _sharedUi.WIRE_DETAIL_LIMIT_BYTES)
|
|
99
|
-
};
|
|
100
|
-
},
|
|
101
|
-
/**
|
|
102
|
-
* Compact current-value reader for a remote driver (MCP/LLM): registered
|
|
103
|
-
* atoms with light metadata; each atom's `currentValue` stays on the device
|
|
104
|
-
* unless `includeValues` is set. `writable` tells the caller which atoms
|
|
105
|
-
* can be set via `setAtom`.
|
|
106
|
-
*/
|
|
107
|
-
listAtoms: params => {
|
|
108
|
-
const {
|
|
109
|
-
includeValues = false,
|
|
110
|
-
limit
|
|
111
|
-
} = (0, _sharedUi.readParams)(params);
|
|
112
|
-
const all = _jotaiStateStore.jotaiStateStore.getAtoms();
|
|
113
|
-
const capped = typeof limit === "number" && limit > 0 ? all.slice(0, limit) : all;
|
|
114
|
-
const atoms = capped.map(a => {
|
|
115
|
-
let value;
|
|
116
|
-
if (includeValues) {
|
|
117
|
-
try {
|
|
118
|
-
value = a.getValue();
|
|
119
|
-
} catch {
|
|
120
|
-
value = undefined;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return {
|
|
124
|
-
label: a.label,
|
|
125
|
-
changes: a.changeCount,
|
|
126
|
-
writable: typeof a.setValue === "function",
|
|
127
|
-
...(includeValues ? {
|
|
128
|
-
currentValue: value
|
|
129
|
-
} : {})
|
|
130
|
-
};
|
|
131
|
-
});
|
|
132
|
-
return {
|
|
133
|
-
atoms,
|
|
134
|
-
total: all.length,
|
|
135
|
-
returned: atoms.length,
|
|
136
|
-
includedValues: includeValues
|
|
137
|
-
};
|
|
138
|
-
},
|
|
139
|
-
/** Write a value to a writable atom (throws for unknown/read-only atoms). */
|
|
140
|
-
setAtom: params => {
|
|
141
|
-
const {
|
|
142
|
-
label,
|
|
143
|
-
value
|
|
144
|
-
} = (0, _sharedUi.readParams)(params);
|
|
145
|
-
if (!label) throw new Error("setAtom requires a `label`");
|
|
146
|
-
_jotaiStateStore.jotaiStateStore.setAtomValue(label, value);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
});
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.jotaiSyncAdapter=exports.VALUE_ON_DEVICE=void 0;var _jotaiStateStore=require("../utils/jotaiStateStore"),_sharedUi=require("@buoy-gg/shared-ui");const VALUE_ON_DEVICE=exports.VALUE_ON_DEVICE=(0,_sharedUi.onDeviceMarker)("Value","Atom values stay on the device — fetched on demand via getChangeDetail / getAtomValue."),isPlainObj=e=>!!e&&"object"==typeof e&&!Array.isArray(e);function deepMergeInto(e,t){if(!isPlainObj(e)||!isPlainObj(t))return t;const a={...e};for(const[r,o]of Object.entries(t))a[r]=r in e?deepMergeInto(e[r],o):o;return a}function touchesList(e,t){let a=e;for(const e of t.split(".")){if(!isPlainObj(a))return!1;if(!(e in a))return!1;a=a[e]}return Array.isArray(a)?a.length>0:!!a&&"object"==typeof a}const toWireChange=(0,_sharedUi.memoByIdentity)(e=>({...e,prevValue:VALUE_ON_DEVICE,nextValue:VALUE_ON_DEVICE})),toWireValue=e=>(0,_sharedUi.toWire)(e,VALUE_ON_DEVICE),jotaiSyncAdapter=exports.jotaiSyncAdapter=(0,_sharedUi.createSyncAdapter)({version:2,getSnapshot:()=>({changes:_jotaiStateStore.jotaiStateStore.getAtomChanges().map(toWireChange),atoms:_jotaiStateStore.jotaiStateStore.getAtomSnapshots().map(e=>({...e,currentValue:toWireValue(e.currentValue)}))}),subscribe:[e=>_jotaiStateStore.jotaiStateStore.subscribe(e),e=>_jotaiStateStore.jotaiStateStore.subscribeToAtoms(e)],actions:{clearEvents:()=>{_jotaiStateStore.jotaiStateStore.clearAtomChanges()},getChangeDetail:(0,_sharedUi.detailByIdAction)(e=>_jotaiStateStore.jotaiStateStore.getAtomChangeById(e),(e,t)=>({prevValue:t(e.prevValue),nextValue:t(e.nextValue)})),getAtomValue:e=>{const{label:t,path:a}=(0,_sharedUi.readParams)(e);if(!t)return{found:!1,reason:"missing label"};const r=_jotaiStateStore.jotaiStateStore.getAtom(t);if(!r)return{found:!1,reason:"unknown label"};let o;try{o=r.getValue()}catch{o=void 0}try{JSON.stringify(o)}catch{return{found:!0,label:t,currentValue:VALUE_ON_DEVICE}}if(a){const e=(0,_sharedUi.readPath)(o,a);return e.ok?{found:!0,label:t,path:a,shape:(0,_sharedUi.shapeSummary)(e.value),currentValue:e.value}:{found:!1,reason:e.error}}return{found:!0,label:t,...(0,_sharedUi.shapeSummary)(o)?{shape:(0,_sharedUi.shapeSummary)(o)}:{},currentValue:(0,_sharedUi.capDetail)(o,_sharedUi.WIRE_DETAIL_LIMIT_BYTES)}},listAtoms:e=>{const{includeValues:t=!1,limit:a}=(0,_sharedUi.readParams)(e),r=_jotaiStateStore.jotaiStateStore.getAtoms(),o=("number"==typeof a&&a>0?r.slice(0,a):r).map(e=>{let a,r;if(t)try{a=e.getValue()}catch{a=void 0}try{r=(0,_sharedUi.shapeSummary)(e.getValue())}catch{r=void 0}return{label:e.label,changes:e.changeCount,writable:"function"==typeof e.setValue,...r?{shape:r}:{},...t?{currentValue:a}:{}}});return{atoms:o,total:r.length,returned:o.length,includedValues:t}},setAtom:e=>{const{label:t,value:a,merge:r,path:o,force:i}=(0,_sharedUi.readParams)(e);if(!t)throw new Error("setAtom requires a `label`");const s=_jotaiStateStore.jotaiStateStore.getAtom(t);let n;try{n=s?.getValue()}catch{n=void 0}let u=a,l=[],d=[];const h=!0===r||void 0!==o;if(void 0!==o){if(void 0===a)return{ok:!1,error:`Refused: sent \`path\` without \`value\`. Say what to set "${o}" to.`};const e=(0,_sharedUi.patchFromPath)(n,o,a);if(!e.ok)return{ok:!1,error:`Refused: ${e.error}`};u=e.patch}if(!i&&h&&void 0!==n){const e=(0,_sharedUi.expandKeyedArrays)(n,u);if(e.error)return{ok:!1,error:`Refused: ${e.error}`};u=e.patch,l=e.notes,d=(0,_sharedUi.unshapedLists)(n).filter(e=>touchesList(u,e));const t=(0,_sharedUi.typedEditViolations)(n,u,{noun:"atom"});if(t.length){const e=(0,_sharedUi.suggestedPatch)(u,t);return{ok:!1,error:(0,_sharedUi.refusalText)(t,"atom",e),violations:t,...e?{suggestedValue:e}:{}}}}return _jotaiStateStore.jotaiStateStore.setAtomValue(t,h?deepMergeInto(n,u):u),{ok:!0,...void 0!==o?{path:o}:{},...l.length?{keyed:l}:{},...d.length?{unchecked:d.map(e=>`\`${e}\` was empty, so nothing knows what its items look like and the shape of what you added was NOT checked.`)}:{}}}}});
|
|
@@ -1,321 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
exports.jotaiStateStore = void 0;
|
|
7
|
-
var _sharedUi = require("@buoy-gg/shared-ui");
|
|
8
|
-
/**
|
|
9
|
-
* Jotai state store — captures and stores Jotai atom changes
|
|
10
|
-
*
|
|
11
|
-
* Mirrors the architecture of zustandStateStore.ts from @buoy-gg/zustand
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
// ============================================
|
|
15
|
-
// Atom Color Palette
|
|
16
|
-
// ============================================
|
|
17
|
-
|
|
18
|
-
const ATOM_COLORS = {
|
|
19
|
-
count: "#10B981",
|
|
20
|
-
// emerald
|
|
21
|
-
auth: "#8B5CF6",
|
|
22
|
-
// purple
|
|
23
|
-
user: "#3B82F6",
|
|
24
|
-
// blue
|
|
25
|
-
cart: "#EC4899",
|
|
26
|
-
// pink
|
|
27
|
-
app: "#6366F1",
|
|
28
|
-
// indigo
|
|
29
|
-
ui: "#F59E0B",
|
|
30
|
-
// amber
|
|
31
|
-
settings: "#14B8A6",
|
|
32
|
-
// teal
|
|
33
|
-
theme: "#06B6D4",
|
|
34
|
-
// cyan
|
|
35
|
-
nav: "#F97316",
|
|
36
|
-
// orange
|
|
37
|
-
form: "#EF4444",
|
|
38
|
-
// red
|
|
39
|
-
modal: "#A855F7",
|
|
40
|
-
// violet
|
|
41
|
-
filter: "#84CC16" // lime
|
|
42
|
-
};
|
|
43
|
-
function getAtomColor(label) {
|
|
44
|
-
return (0, _sharedUi.entityColor)(label, ATOM_COLORS);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// ============================================
|
|
48
|
-
// Helper Functions
|
|
49
|
-
// ============================================
|
|
50
|
-
|
|
51
|
-
function formatValuePreview(value, maxLength = 40) {
|
|
52
|
-
return (0, _sharedUi.formatValuePreview)(value, {
|
|
53
|
-
maxLength,
|
|
54
|
-
functionLabel: "(fn)"
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
const getValueDiffSummary = _sharedUi.shallowDiffSummary;
|
|
58
|
-
|
|
59
|
-
// ============================================
|
|
60
|
-
// Jotai State Store
|
|
61
|
-
// ============================================
|
|
62
|
-
|
|
63
|
-
class JotaiStateStore extends _sharedUi.ChangeLog {
|
|
64
|
-
atoms = new Map();
|
|
65
|
-
atomListeners = new Set();
|
|
66
|
-
|
|
67
|
-
// ---- Change Tracking ----
|
|
68
|
-
|
|
69
|
-
addAtomChange(params) {
|
|
70
|
-
if (!this.canRecord()) return;
|
|
71
|
-
const {
|
|
72
|
-
atomLabel,
|
|
73
|
-
prevValue,
|
|
74
|
-
nextValue,
|
|
75
|
-
category = "write"
|
|
76
|
-
} = params;
|
|
77
|
-
const hasValueChange = prevValue !== nextValue;
|
|
78
|
-
const {
|
|
79
|
-
summary,
|
|
80
|
-
changedKeys,
|
|
81
|
-
changedCount
|
|
82
|
-
} = getValueDiffSummary(prevValue, nextValue);
|
|
83
|
-
const valuePreview = formatValuePreview(nextValue);
|
|
84
|
-
const change = {
|
|
85
|
-
id: this.nextId(),
|
|
86
|
-
atomLabel,
|
|
87
|
-
timestamp: Date.now(),
|
|
88
|
-
prevValue,
|
|
89
|
-
nextValue,
|
|
90
|
-
hasValueChange,
|
|
91
|
-
category,
|
|
92
|
-
changedKeys,
|
|
93
|
-
changedKeysCount: changedCount,
|
|
94
|
-
diffSummary: summary,
|
|
95
|
-
valuePreview,
|
|
96
|
-
isSlowUpdate: false
|
|
97
|
-
};
|
|
98
|
-
this.push(change);
|
|
99
|
-
const atomInfo = this.atoms.get(atomLabel);
|
|
100
|
-
if (atomInfo) {
|
|
101
|
-
atomInfo.changeCount++;
|
|
102
|
-
}
|
|
103
|
-
this.notifyListeners();
|
|
104
|
-
this.notifyAtomListeners();
|
|
105
|
-
}
|
|
106
|
-
getAtomChanges() {
|
|
107
|
-
return this.getAll();
|
|
108
|
-
}
|
|
109
|
-
getAtomChangeById(id) {
|
|
110
|
-
return this.getById(id);
|
|
111
|
-
}
|
|
112
|
-
clearAtomChanges() {
|
|
113
|
-
for (const atom of this.atoms.values()) {
|
|
114
|
-
atom.changeCount = 0;
|
|
115
|
-
}
|
|
116
|
-
this.clear();
|
|
117
|
-
this.notifyAtomListeners();
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// ---- Remote Mirror Mode ----
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Serializable snapshot of the tracked atoms (registry metadata plus each
|
|
124
|
-
* atom's current value). Used by the sync adapter on the device side —
|
|
125
|
-
* the live `getValue` handles can't go over the wire.
|
|
126
|
-
*/
|
|
127
|
-
getAtomSnapshots() {
|
|
128
|
-
return Array.from(this.atoms.values()).map(atom => {
|
|
129
|
-
let currentValue;
|
|
130
|
-
try {
|
|
131
|
-
currentValue = atom.getValue();
|
|
132
|
-
} catch {
|
|
133
|
-
currentValue = undefined;
|
|
134
|
-
}
|
|
135
|
-
return {
|
|
136
|
-
label: atom.label,
|
|
137
|
-
changeCount: atom.changeCount,
|
|
138
|
-
color: atom.color,
|
|
139
|
-
currentValue,
|
|
140
|
-
writeKind: atom.writeKind
|
|
141
|
-
};
|
|
142
|
-
});
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* Replace the entire mirror contents from a synced device snapshot. The
|
|
147
|
-
* rebuilt registry entries get stub `getValue` handles serving the
|
|
148
|
-
* snapshotted value. Respects the enabled flag so the UI's pause button
|
|
149
|
-
* freezes the mirror.
|
|
150
|
-
*/
|
|
151
|
-
replaceFromSnapshot(changes, atomSnapshots) {
|
|
152
|
-
if (!this.getEnabled()) return;
|
|
153
|
-
|
|
154
|
-
// Preserve values a remote mirror already hydrated — wire-form snapshots
|
|
155
|
-
// always carry markers, and a later snapshot must not blank the detail pane.
|
|
156
|
-
const hydrated = new Map();
|
|
157
|
-
for (const existing of this.items) {
|
|
158
|
-
const prev = existing.prevValue;
|
|
159
|
-
if (!prev || prev.__buoyValueOnDevice !== true) {
|
|
160
|
-
hydrated.set(existing.id, existing);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
const merged = changes.slice(0, this.max).map(change => {
|
|
164
|
-
const kept = hydrated.get(change.id);
|
|
165
|
-
const incoming = change.prevValue;
|
|
166
|
-
return kept && incoming?.__buoyValueOnDevice === true ? {
|
|
167
|
-
...change,
|
|
168
|
-
prevValue: kept.prevValue,
|
|
169
|
-
nextValue: kept.nextValue
|
|
170
|
-
} : change;
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Preserve currentValue a remote mirror already hydrated — oversized
|
|
174
|
-
// atoms arrive as VALUE_ON_DEVICE markers on every snapshot.
|
|
175
|
-
const hydratedAtoms = new Map();
|
|
176
|
-
for (const [label, atom] of this.atoms) {
|
|
177
|
-
try {
|
|
178
|
-
const value = atom.getValue();
|
|
179
|
-
const marker = value;
|
|
180
|
-
if (!marker || marker.__buoyValueOnDevice !== true) {
|
|
181
|
-
hydratedAtoms.set(label, value);
|
|
182
|
-
}
|
|
183
|
-
} catch {
|
|
184
|
-
// ignore
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
this.atoms = new Map(atomSnapshots.map(snapshot => {
|
|
188
|
-
const incoming = snapshot.currentValue;
|
|
189
|
-
const kept = hydratedAtoms.get(snapshot.label);
|
|
190
|
-
const currentValue = kept !== undefined && incoming?.__buoyValueOnDevice === true ? kept : snapshot.currentValue;
|
|
191
|
-
return [snapshot.label, {
|
|
192
|
-
label: snapshot.label,
|
|
193
|
-
changeCount: snapshot.changeCount,
|
|
194
|
-
color: snapshot.color,
|
|
195
|
-
getValue: () => currentValue,
|
|
196
|
-
// No setValue: the mirror has no wire back to the device from
|
|
197
|
-
// this entry (yet) — the detail screen renders read-only there.
|
|
198
|
-
writeKind: snapshot.writeKind
|
|
199
|
-
}];
|
|
200
|
-
}));
|
|
201
|
-
this.setItems(merged);
|
|
202
|
-
this.notifyAtomListeners();
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* Hydrate one mirrored change with real values fetched on demand from the
|
|
207
|
-
* device via the adapter's `getChangeDetail`. Remote-mirror (desktop) only.
|
|
208
|
-
*/
|
|
209
|
-
hydrateChange(id, patch) {
|
|
210
|
-
this.hydrateById(id, patch);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Hydrate one mirrored atom's currentValue fetched on demand from the
|
|
215
|
-
* device via the adapter's `getAtomValue`. Remote-mirror (desktop) only.
|
|
216
|
-
*/
|
|
217
|
-
hydrateAtomValue(label, currentValue) {
|
|
218
|
-
const atom = this.atoms.get(label);
|
|
219
|
-
if (!atom) return;
|
|
220
|
-
this.atoms.set(label, {
|
|
221
|
-
...atom,
|
|
222
|
-
getValue: () => currentValue
|
|
223
|
-
});
|
|
224
|
-
this.notifyAtomListeners();
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// ---- Atom Registry ----
|
|
228
|
-
|
|
229
|
-
registerAtom(label, getValue, setValue, writeKind) {
|
|
230
|
-
if (this.atoms.has(label)) return;
|
|
231
|
-
const atomInfo = {
|
|
232
|
-
label,
|
|
233
|
-
changeCount: 0,
|
|
234
|
-
color: getAtomColor(label),
|
|
235
|
-
getValue,
|
|
236
|
-
setValue,
|
|
237
|
-
writeKind
|
|
238
|
-
};
|
|
239
|
-
this.atoms.set(label, atomInfo);
|
|
240
|
-
this.notifyAtomListeners();
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Write a value to a registered atom (remote write from the dashboard/MCP).
|
|
245
|
-
* Throws if the atom is unknown or read-only (derived atoms have no setter).
|
|
246
|
-
*/
|
|
247
|
-
setAtomValue(label, value) {
|
|
248
|
-
const atom = this.atoms.get(label);
|
|
249
|
-
if (!atom) {
|
|
250
|
-
throw new Error(`Atom "${label}" is not registered`);
|
|
251
|
-
}
|
|
252
|
-
if (!atom.setValue) {
|
|
253
|
-
throw new Error(`Atom "${label}" is read-only (derived) — cannot set`);
|
|
254
|
-
}
|
|
255
|
-
atom.setValue(value);
|
|
256
|
-
}
|
|
257
|
-
unregisterAtom(label) {
|
|
258
|
-
this.atoms.delete(label);
|
|
259
|
-
this.notifyAtomListeners();
|
|
260
|
-
}
|
|
261
|
-
getAtoms() {
|
|
262
|
-
return Array.from(this.atoms.values());
|
|
263
|
-
}
|
|
264
|
-
getAtom(label) {
|
|
265
|
-
return this.atoms.get(label);
|
|
266
|
-
}
|
|
267
|
-
getAtomColor(label) {
|
|
268
|
-
return this.atoms.get(label)?.color ?? getAtomColor(label);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// ---- Filtering ----
|
|
272
|
-
|
|
273
|
-
filterAtomChanges(filter) {
|
|
274
|
-
let filtered = this.getAll();
|
|
275
|
-
if (filter.searchText) {
|
|
276
|
-
const search = filter.searchText.toLowerCase();
|
|
277
|
-
filtered = filtered.filter(c => c.atomLabel.toLowerCase().includes(search) || c.valuePreview.toLowerCase().includes(search) || c.changedKeys.some(k => k.toLowerCase().includes(search)));
|
|
278
|
-
}
|
|
279
|
-
if (filter.atomLabels && filter.atomLabels.length > 0) {
|
|
280
|
-
filtered = filtered.filter(c => filter.atomLabels.includes(c.atomLabel));
|
|
281
|
-
}
|
|
282
|
-
if (filter.onlyWithChanges) {
|
|
283
|
-
filtered = filtered.filter(c => c.hasValueChange);
|
|
284
|
-
}
|
|
285
|
-
return filtered;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
// ---- Stats ----
|
|
289
|
-
|
|
290
|
-
getStats() {
|
|
291
|
-
const total = this.items.length;
|
|
292
|
-
const withChanges = this.items.filter(c => c.hasValueChange).length;
|
|
293
|
-
return {
|
|
294
|
-
totalChanges: total,
|
|
295
|
-
changesWithValueChange: withChanges,
|
|
296
|
-
changesWithoutValueChange: total - withChanges,
|
|
297
|
-
atomCount: this.atoms.size
|
|
298
|
-
};
|
|
299
|
-
}
|
|
300
|
-
getUniqueAtomLabels() {
|
|
301
|
-
return Array.from(this.atoms.keys()).sort();
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// ---- Enable / Disable ----
|
|
305
|
-
|
|
306
|
-
setMaxChanges(max) {
|
|
307
|
-
this.setMax(max);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
// ---- Subscriptions ----
|
|
311
|
-
|
|
312
|
-
subscribeToAtoms(listener) {
|
|
313
|
-
this.atomListeners.add(listener);
|
|
314
|
-
return () => this.atomListeners.delete(listener);
|
|
315
|
-
}
|
|
316
|
-
notifyAtomListeners() {
|
|
317
|
-
const atoms = this.getAtoms();
|
|
318
|
-
this.atomListeners.forEach(listener => listener(atoms));
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
const jotaiStateStore = exports.jotaiStateStore = new JotaiStateStore();
|
|
1
|
+
"use strict";Object.defineProperty(exports,"__esModule",{value:!0}),exports.jotaiStateStore=void 0;var _sharedUi=require("@buoy-gg/shared-ui");const ATOM_COLORS={count:"#10B981",auth:"#8B5CF6",user:"#3B82F6",cart:"#EC4899",app:"#6366F1",ui:"#F59E0B",settings:"#14B8A6",theme:"#06B6D4",nav:"#F97316",form:"#EF4444",modal:"#A855F7",filter:"#84CC16"};function getAtomColor(e){return(0,_sharedUi.entityColor)(e,ATOM_COLORS)}function formatValuePreview(e,t=40){return(0,_sharedUi.formatValuePreview)(e,{maxLength:t,functionLabel:"(fn)"})}const getValueDiffSummary=_sharedUi.shallowDiffSummary;class JotaiStateStore extends _sharedUi.ChangeLog{atoms=new Map;atomListeners=new Set;addAtomChange(e){if(!this.canRecord())return;const{atomLabel:t,prevValue:a,nextValue:s,category:o="write"}=e,r=a!==s,{summary:n,changedKeys:i,changedCount:l}=getValueDiffSummary(a,s),h=formatValuePreview(s),u={id:this.nextId(),atomLabel:t,timestamp:Date.now(),prevValue:a,nextValue:s,hasValueChange:r,category:o,changedKeys:i,changedKeysCount:l,diffSummary:n,valuePreview:h,isSlowUpdate:!1};this.push(u);const m=this.atoms.get(t);m&&m.changeCount++,this.notifyListeners(),this.notifyAtomListeners()}getAtomChanges(){return this.getAll()}getAtomChangeById(e){return this.getById(e)}clearAtomChanges(){for(const e of this.atoms.values())e.changeCount=0;this.clear(),this.notifyAtomListeners()}getAtomSnapshots(){return Array.from(this.atoms.values()).map(e=>{let t;try{t=e.getValue()}catch{t=void 0}return{label:e.label,changeCount:e.changeCount,color:e.color,currentValue:t,writeKind:e.writeKind}})}replaceFromSnapshot(e,t){if(!this.getEnabled())return;const a=new Map;for(const e of this.items){const t=e.prevValue;t&&!0===t.__buoyValueOnDevice||a.set(e.id,e)}const s=e.slice(0,this.max).map(e=>{const t=a.get(e.id),s=e.prevValue;return t&&!0===s?.__buoyValueOnDevice?{...e,prevValue:t.prevValue,nextValue:t.nextValue}:e}),o=new Map;for(const[e,t]of this.atoms)try{const a=t.getValue();a&&!0===a.__buoyValueOnDevice||o.set(e,a)}catch{}this.atoms=new Map(t.map(e=>{const t=e.currentValue,a=o.get(e.label),s=void 0!==a&&!0===t?.__buoyValueOnDevice?a:e.currentValue;return[e.label,{label:e.label,changeCount:e.changeCount,color:e.color,getValue:()=>s,writeKind:e.writeKind}]})),this.setItems(s),this.notifyAtomListeners()}hydrateChange(e,t){this.hydrateById(e,t)}hydrateAtomValue(e,t){const a=this.atoms.get(e);a&&(this.atoms.set(e,{...a,getValue:()=>t}),this.notifyAtomListeners())}registerAtom(e,t,a,s){if(this.atoms.has(e))return;const o={label:e,changeCount:0,color:getAtomColor(e),getValue:t,setValue:a,writeKind:s};this.atoms.set(e,o),this.notifyAtomListeners()}setAtomValue(e,t){const a=this.atoms.get(e);if(!a)throw new Error(`Atom "${e}" is not registered`);if(!a.setValue)throw new Error(`Atom "${e}" is read-only (derived) — cannot set`);a.setValue(t)}unregisterAtom(e){this.atoms.delete(e),this.notifyAtomListeners()}getAtoms(){return Array.from(this.atoms.values())}getAtom(e){return this.atoms.get(e)}getAtomColor(e){return this.atoms.get(e)?.color??getAtomColor(e)}filterAtomChanges(e){let t=this.getAll();if(e.searchText){const a=e.searchText.toLowerCase();t=t.filter(e=>e.atomLabel.toLowerCase().includes(a)||e.valuePreview.toLowerCase().includes(a)||e.changedKeys.some(e=>e.toLowerCase().includes(a)))}return e.atomLabels&&e.atomLabels.length>0&&(t=t.filter(t=>e.atomLabels.includes(t.atomLabel))),e.onlyWithChanges&&(t=t.filter(e=>e.hasValueChange)),t}getStats(){const e=this.items.length,t=this.items.filter(e=>e.hasValueChange).length;return{totalChanges:e,changesWithValueChange:t,changesWithoutValueChange:e-t,atomCount:this.atoms.size}}getUniqueAtomLabels(){return Array.from(this.atoms.keys()).sort()}setMaxChanges(e){this.setMax(e)}subscribeToAtoms(e){return this.atomListeners.add(e),()=>this.atomListeners.delete(e)}notifyAtomListeners(){const e=this.getAtoms();this.atomListeners.forEach(t=>t(e))}}const jotaiStateStore=exports.jotaiStateStore=new JotaiStateStore;
|