@buoy-gg/jotai 7.0.34 → 7.0.36
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/lib/commonjs/jotai/components/JotaiAtomBrowser.js +2 -2
- package/lib/commonjs/jotai/components/JotaiAtomDetailContent.js +10 -10
- package/lib/commonjs/jotai/components/JotaiAtomValueDetailContent.js +5 -5
- package/lib/commonjs/jotai/sync/jotaiSyncAdapter.js +161 -4
- package/lib/module/jotai/components/JotaiAtomBrowser.js +3 -3
- package/lib/module/jotai/components/JotaiAtomDetailContent.js +11 -11
- package/lib/module/jotai/components/JotaiAtomValueDetailContent.js +6 -6
- package/lib/module/jotai/sync/jotaiSyncAdapter.js +162 -5
- package/lib/typescript/jotai/components/JotaiAtomBrowser.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiAtomDetailContent.d.ts.map +1 -1
- package/lib/typescript/jotai/components/JotaiAtomValueDetailContent.d.ts.map +1 -1
- package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts +52 -2
- package/lib/typescript/jotai/sync/jotaiSyncAdapter.d.ts.map +1 -1
- package/package.json +3 -3
|
@@ -165,7 +165,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
165
165
|
absCountText: {
|
|
166
166
|
fontSize: 9,
|
|
167
167
|
fontWeight: "700",
|
|
168
|
-
fontFamily:
|
|
168
|
+
fontFamily: _sharedUi.monoFont
|
|
169
169
|
},
|
|
170
170
|
scrollContent: {
|
|
171
171
|
paddingTop: 8,
|
|
@@ -194,6 +194,6 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
194
194
|
fontSize: 10,
|
|
195
195
|
fontWeight: "700",
|
|
196
196
|
color: _sharedUi.night.color.accent,
|
|
197
|
-
fontFamily:
|
|
197
|
+
fontFamily: _sharedUi.monoFont
|
|
198
198
|
}
|
|
199
199
|
});
|
|
@@ -481,7 +481,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
481
481
|
valueLabel: {
|
|
482
482
|
fontSize: 10,
|
|
483
483
|
color: _sharedUi.night.color.textSecondary,
|
|
484
|
-
fontFamily:
|
|
484
|
+
fontFamily: _sharedUi.monoFont,
|
|
485
485
|
letterSpacing: 0.5,
|
|
486
486
|
fontWeight: "600"
|
|
487
487
|
},
|
|
@@ -498,7 +498,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
498
498
|
changeText: {
|
|
499
499
|
fontSize: 9,
|
|
500
500
|
fontWeight: "700",
|
|
501
|
-
fontFamily:
|
|
501
|
+
fontFamily: _sharedUi.monoFont,
|
|
502
502
|
letterSpacing: 0.3
|
|
503
503
|
},
|
|
504
504
|
typeBadge: {
|
|
@@ -511,7 +511,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
511
511
|
fontSize: 9,
|
|
512
512
|
fontWeight: "600",
|
|
513
513
|
color: _sharedUi.night.color.textTertiary,
|
|
514
|
-
fontFamily:
|
|
514
|
+
fontFamily: _sharedUi.monoFont
|
|
515
515
|
},
|
|
516
516
|
valueBox: {
|
|
517
517
|
// Inset code box — darker than the card, the night track color.
|
|
@@ -524,7 +524,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
524
524
|
valueText: {
|
|
525
525
|
fontSize: 12,
|
|
526
526
|
color: _sharedUi.night.color.text,
|
|
527
|
-
fontFamily:
|
|
527
|
+
fontFamily: _sharedUi.monoFont,
|
|
528
528
|
lineHeight: 18
|
|
529
529
|
},
|
|
530
530
|
diffContainer: {
|
|
@@ -542,7 +542,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
542
542
|
changeTextSmall: {
|
|
543
543
|
fontSize: 8,
|
|
544
544
|
fontWeight: "700",
|
|
545
|
-
fontFamily:
|
|
545
|
+
fontFamily: _sharedUi.monoFont
|
|
546
546
|
}
|
|
547
547
|
});
|
|
548
548
|
const infoStyles = _reactNative.StyleSheet.create({
|
|
@@ -588,7 +588,7 @@ const infoStyles = _reactNative.StyleSheet.create({
|
|
|
588
588
|
fontWeight: "600",
|
|
589
589
|
letterSpacing: 0.5,
|
|
590
590
|
color: _sharedUi.night.color.accent,
|
|
591
|
-
fontFamily:
|
|
591
|
+
fontFamily: _sharedUi.monoFont
|
|
592
592
|
},
|
|
593
593
|
cardContent: {
|
|
594
594
|
padding: 14,
|
|
@@ -613,7 +613,7 @@ const infoStyles = _reactNative.StyleSheet.create({
|
|
|
613
613
|
marginLeft: 12
|
|
614
614
|
},
|
|
615
615
|
infoValueMono: {
|
|
616
|
-
fontFamily:
|
|
616
|
+
fontFamily: _sharedUi.monoFont
|
|
617
617
|
},
|
|
618
618
|
badgeRow: {
|
|
619
619
|
flexDirection: "row",
|
|
@@ -673,7 +673,7 @@ const infoStyles = _reactNative.StyleSheet.create({
|
|
|
673
673
|
fontSize: 11,
|
|
674
674
|
fontWeight: "600",
|
|
675
675
|
color: _sharedUi.night.color.accent,
|
|
676
|
-
fontFamily:
|
|
676
|
+
fontFamily: _sharedUi.monoFont
|
|
677
677
|
},
|
|
678
678
|
countBadge: {
|
|
679
679
|
paddingHorizontal: 8,
|
|
@@ -685,7 +685,7 @@ const infoStyles = _reactNative.StyleSheet.create({
|
|
|
685
685
|
fontSize: 9,
|
|
686
686
|
fontWeight: "700",
|
|
687
687
|
color: _sharedUi.night.color.accent,
|
|
688
|
-
fontFamily:
|
|
688
|
+
fontFamily: _sharedUi.monoFont
|
|
689
689
|
},
|
|
690
690
|
actionsRow: {
|
|
691
691
|
flexDirection: "row",
|
|
@@ -704,7 +704,7 @@ const infoStyles = _reactNative.StyleSheet.create({
|
|
|
704
704
|
actionButtonText: {
|
|
705
705
|
fontSize: 10,
|
|
706
706
|
fontWeight: "700",
|
|
707
|
-
fontFamily:
|
|
707
|
+
fontFamily: _sharedUi.monoFont,
|
|
708
708
|
letterSpacing: 0.3
|
|
709
709
|
},
|
|
710
710
|
dataViewerContainer: {
|
|
@@ -364,7 +364,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
364
364
|
color: _sharedUi.night.color.accent,
|
|
365
365
|
letterSpacing: 0.5,
|
|
366
366
|
textTransform: "uppercase",
|
|
367
|
-
fontFamily:
|
|
367
|
+
fontFamily: _sharedUi.monoFont
|
|
368
368
|
},
|
|
369
369
|
rawButton: {
|
|
370
370
|
flexDirection: "row",
|
|
@@ -411,7 +411,7 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
411
411
|
borderBottomColor: _sharedUi.night.color.border,
|
|
412
412
|
letterSpacing: 0.5,
|
|
413
413
|
textTransform: "uppercase",
|
|
414
|
-
fontFamily:
|
|
414
|
+
fontFamily: _sharedUi.monoFont
|
|
415
415
|
},
|
|
416
416
|
infoBody: {
|
|
417
417
|
padding: 12,
|
|
@@ -424,13 +424,13 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
424
424
|
primitiveText: {
|
|
425
425
|
color: _sharedUi.night.color.text,
|
|
426
426
|
fontSize: 12,
|
|
427
|
-
fontFamily:
|
|
427
|
+
fontFamily: _sharedUi.monoFont,
|
|
428
428
|
padding: 14
|
|
429
429
|
},
|
|
430
430
|
valueTypeText: {
|
|
431
431
|
color: _sharedUi.night.color.textTertiary,
|
|
432
432
|
fontSize: 10,
|
|
433
|
-
fontFamily:
|
|
433
|
+
fontFamily: _sharedUi.monoFont,
|
|
434
434
|
marginLeft: 6
|
|
435
435
|
},
|
|
436
436
|
inlineLinkButton: {
|
|
@@ -439,6 +439,6 @@ const styles = _reactNative.StyleSheet.create({
|
|
|
439
439
|
inlineLinkText: {
|
|
440
440
|
fontSize: 10,
|
|
441
441
|
fontWeight: "600",
|
|
442
|
-
fontFamily:
|
|
442
|
+
fontFamily: _sharedUi.monoFont
|
|
443
443
|
}
|
|
444
444
|
});
|
|
@@ -12,6 +12,41 @@ var _sharedUi = require("@buoy-gg/shared-ui");
|
|
|
12
12
|
* snapshot. Dashboards fetch values via `getChangeDetail`.
|
|
13
13
|
*/
|
|
14
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
|
+
const isPlainObj = v => !!v && typeof v === "object" && !Array.isArray(v);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Merge a patch into an atom's value.
|
|
19
|
+
*
|
|
20
|
+
* An atom is not always an object — plenty hold a number, a string or a list —
|
|
21
|
+
* so a merge only means anything when BOTH sides are plain objects. Anything
|
|
22
|
+
* else replaces, which is what a caller asking to merge a scalar can only have
|
|
23
|
+
* meant. Arrays replace too: the id-addressed form has already been expanded to
|
|
24
|
+
* a whole array by the time this runs, so merging element-wise here would undo
|
|
25
|
+
* the deletion half of that expansion.
|
|
26
|
+
*/
|
|
27
|
+
function deepMergeInto(base, patch) {
|
|
28
|
+
if (!isPlainObj(base) || !isPlainObj(patch)) return patch;
|
|
29
|
+
const out = {
|
|
30
|
+
...base
|
|
31
|
+
};
|
|
32
|
+
for (const [k, v] of Object.entries(patch)) out[k] = k in base ? deepMergeInto(base[k], v) : v;
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Does this patch write into the list at `path`? Only then is an
|
|
38
|
+
* unchecked-shape warning about that list worth saying.
|
|
39
|
+
*/
|
|
40
|
+
function touchesList(patch, path) {
|
|
41
|
+
let cur = patch;
|
|
42
|
+
for (const seg of path.split(".")) {
|
|
43
|
+
if (!isPlainObj(cur)) return false;
|
|
44
|
+
if (!(seg in cur)) return false;
|
|
45
|
+
cur = cur[seg];
|
|
46
|
+
}
|
|
47
|
+
return Array.isArray(cur) ? cur.length > 0 : !!cur && typeof cur === "object";
|
|
48
|
+
}
|
|
49
|
+
|
|
15
50
|
/**
|
|
16
51
|
* Per-change wire cache — the store prepends and never mutates a recorded
|
|
17
52
|
* change, so identity is a correct, self-invalidating key.
|
|
@@ -66,7 +101,8 @@ const jotaiSyncAdapter = exports.jotaiSyncAdapter = (0, _sharedUi.createSyncAdap
|
|
|
66
101
|
*/
|
|
67
102
|
getAtomValue: params => {
|
|
68
103
|
const {
|
|
69
|
-
label
|
|
104
|
+
label,
|
|
105
|
+
path
|
|
70
106
|
} = (0, _sharedUi.readParams)(params);
|
|
71
107
|
if (!label) return {
|
|
72
108
|
found: false,
|
|
@@ -92,9 +128,32 @@ const jotaiSyncAdapter = exports.jotaiSyncAdapter = (0, _sharedUi.createSyncAdap
|
|
|
92
128
|
currentValue: VALUE_ON_DEVICE
|
|
93
129
|
};
|
|
94
130
|
}
|
|
131
|
+
// One slice instead of the whole atom, when asked. The reason to want it
|
|
132
|
+
// is the engine's 24,000-character result cap: a caller that never saw the
|
|
133
|
+
// field it means to edit is left inventing a shape.
|
|
134
|
+
if (path) {
|
|
135
|
+
const at = (0, _sharedUi.readPath)(currentValue, path);
|
|
136
|
+
if (!at.ok) return {
|
|
137
|
+
found: false,
|
|
138
|
+
reason: at.error
|
|
139
|
+
};
|
|
140
|
+
return {
|
|
141
|
+
found: true,
|
|
142
|
+
label,
|
|
143
|
+
path,
|
|
144
|
+
shape: (0, _sharedUi.shapeSummary)(at.value),
|
|
145
|
+
currentValue: at.value
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
// `shape` BEFORE `currentValue`: a result is truncated by slicing the
|
|
149
|
+
// encoded string, so what is serialised first is what survives the cut,
|
|
150
|
+
// and the sketch does not grow with the data.
|
|
95
151
|
return {
|
|
96
152
|
found: true,
|
|
97
153
|
label,
|
|
154
|
+
...((0, _sharedUi.shapeSummary)(currentValue) ? {
|
|
155
|
+
shape: (0, _sharedUi.shapeSummary)(currentValue)
|
|
156
|
+
} : {}),
|
|
98
157
|
currentValue: (0, _sharedUi.capDetail)(currentValue, _sharedUi.WIRE_DETAIL_LIMIT_BYTES)
|
|
99
158
|
};
|
|
100
159
|
},
|
|
@@ -120,10 +179,22 @@ const jotaiSyncAdapter = exports.jotaiSyncAdapter = (0, _sharedUi.createSyncAdap
|
|
|
120
179
|
value = undefined;
|
|
121
180
|
}
|
|
122
181
|
}
|
|
182
|
+
// The compact view is what a caller reaches for precisely because it
|
|
183
|
+
// does not want the data; the item shape of a list is the one thing it
|
|
184
|
+
// cannot infer without it, and it costs a line.
|
|
185
|
+
let shape;
|
|
186
|
+
try {
|
|
187
|
+
shape = (0, _sharedUi.shapeSummary)(a.getValue());
|
|
188
|
+
} catch {
|
|
189
|
+
shape = undefined;
|
|
190
|
+
}
|
|
123
191
|
return {
|
|
124
192
|
label: a.label,
|
|
125
193
|
changes: a.changeCount,
|
|
126
194
|
writable: typeof a.setValue === "function",
|
|
195
|
+
...(shape ? {
|
|
196
|
+
shape
|
|
197
|
+
} : {}),
|
|
127
198
|
...(includeValues ? {
|
|
128
199
|
currentValue: value
|
|
129
200
|
} : {})
|
|
@@ -136,14 +207,100 @@ const jotaiSyncAdapter = exports.jotaiSyncAdapter = (0, _sharedUi.createSyncAdap
|
|
|
136
207
|
includedValues: includeValues
|
|
137
208
|
};
|
|
138
209
|
},
|
|
139
|
-
/**
|
|
210
|
+
/**
|
|
211
|
+
* Write to a writable atom (throws for unknown/read-only atoms).
|
|
212
|
+
*
|
|
213
|
+
* THIS USED TO BE FOUR LINES WITH NO GUARD. `setAtom` took whatever it was
|
|
214
|
+
* given and wrote it: a string over a number, `null` over a list the screen
|
|
215
|
+
* maps, a one-item array over a fifty-item one — every failure mode the
|
|
216
|
+
* zustand and react-query adapters refuse, jotai accepted silently. It was
|
|
217
|
+
* the only writable state tool with no protection at all, and it had never
|
|
218
|
+
* had an eval case, so nothing had ever measured it.
|
|
219
|
+
*
|
|
220
|
+
* It now takes the same three forms as `zustand.setState`, backed by the
|
|
221
|
+
* same shared implementations:
|
|
222
|
+
* { value } replace (guarded unless force)
|
|
223
|
+
* { value, merge: true } merge a patch, id-addressed lists allowed
|
|
224
|
+
* { path, value } set one value; no depth to get wrong
|
|
225
|
+
*/
|
|
140
226
|
setAtom: params => {
|
|
141
227
|
const {
|
|
142
228
|
label,
|
|
143
|
-
value
|
|
229
|
+
value,
|
|
230
|
+
merge,
|
|
231
|
+
path,
|
|
232
|
+
force
|
|
144
233
|
} = (0, _sharedUi.readParams)(params);
|
|
145
234
|
if (!label) throw new Error("setAtom requires a `label`");
|
|
146
|
-
_jotaiStateStore.jotaiStateStore.
|
|
235
|
+
const atom = _jotaiStateStore.jotaiStateStore.getAtom(label);
|
|
236
|
+
let current;
|
|
237
|
+
try {
|
|
238
|
+
current = atom?.getValue();
|
|
239
|
+
} catch {
|
|
240
|
+
current = undefined;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// A replace with no guard is the old behaviour; keep it reachable via
|
|
244
|
+
// `force`, and keep it the ONLY way to get it.
|
|
245
|
+
let effective = value;
|
|
246
|
+
let keyedNotes = [];
|
|
247
|
+
let unchecked = [];
|
|
248
|
+
const merging = merge === true || path !== undefined;
|
|
249
|
+
if (path !== undefined) {
|
|
250
|
+
if (value === undefined) {
|
|
251
|
+
return {
|
|
252
|
+
ok: false,
|
|
253
|
+
error: `Refused: sent \`path\` without \`value\`. Say what to set "${path}" to.`
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const built = (0, _sharedUi.patchFromPath)(current, path, value);
|
|
257
|
+
if (!built.ok) return {
|
|
258
|
+
ok: false,
|
|
259
|
+
error: `Refused: ${built.error}`
|
|
260
|
+
};
|
|
261
|
+
effective = built.patch;
|
|
262
|
+
}
|
|
263
|
+
if (!force && merging && current !== undefined) {
|
|
264
|
+
const expanded = (0, _sharedUi.expandKeyedArrays)(current, effective);
|
|
265
|
+
// See keyedArrays.ts: an added item whose id disagrees with the key it
|
|
266
|
+
// was sent under is ambiguous, and only this layer can say so.
|
|
267
|
+
if (expanded.error) {
|
|
268
|
+
return {
|
|
269
|
+
ok: false,
|
|
270
|
+
error: `Refused: ${expanded.error}`
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
effective = expanded.patch;
|
|
274
|
+
keyedNotes = expanded.notes;
|
|
275
|
+
unchecked = (0, _sharedUi.unshapedLists)(current).filter(p => touchesList(effective, p));
|
|
276
|
+
const violations = (0, _sharedUi.typedEditViolations)(current, effective, {
|
|
277
|
+
noun: "atom"
|
|
278
|
+
});
|
|
279
|
+
if (violations.length) {
|
|
280
|
+
const suggestion = (0, _sharedUi.suggestedPatch)(effective, violations);
|
|
281
|
+
return {
|
|
282
|
+
ok: false,
|
|
283
|
+
error: (0, _sharedUi.refusalText)(violations, "atom", suggestion),
|
|
284
|
+
violations,
|
|
285
|
+
...(suggestion ? {
|
|
286
|
+
suggestedValue: suggestion
|
|
287
|
+
} : {})
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
_jotaiStateStore.jotaiStateStore.setAtomValue(label, merging ? deepMergeInto(current, effective) : effective);
|
|
292
|
+
return {
|
|
293
|
+
ok: true,
|
|
294
|
+
...(path !== undefined ? {
|
|
295
|
+
path
|
|
296
|
+
} : {}),
|
|
297
|
+
...(keyedNotes.length ? {
|
|
298
|
+
keyed: keyedNotes
|
|
299
|
+
} : {}),
|
|
300
|
+
...(unchecked.length ? {
|
|
301
|
+
unchecked: unchecked.map(p => `\`${p}\` was empty, so nothing knows what its items look like and the shape of what you added was NOT checked.`)
|
|
302
|
+
} : {})
|
|
303
|
+
};
|
|
147
304
|
}
|
|
148
305
|
}
|
|
149
306
|
});
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { useMemo } from "react";
|
|
13
13
|
import { View, Text, StyleSheet, ScrollView } from "react-native";
|
|
14
|
-
import { CompactRow, night, ToolEmptyState, resolveListEmptyState } from "@buoy-gg/shared-ui";
|
|
14
|
+
import { CompactRow, night, ToolEmptyState, resolveListEmptyState, monoFont } from "@buoy-gg/shared-ui";
|
|
15
15
|
import { jotaiStateStore } from "../utils/jotaiStateStore";
|
|
16
16
|
import { JOTAI_TEST_ID, atomRowTestID } from "../utils/testIDs";
|
|
17
17
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -161,7 +161,7 @@ const styles = StyleSheet.create({
|
|
|
161
161
|
absCountText: {
|
|
162
162
|
fontSize: 9,
|
|
163
163
|
fontWeight: "700",
|
|
164
|
-
fontFamily:
|
|
164
|
+
fontFamily: monoFont
|
|
165
165
|
},
|
|
166
166
|
scrollContent: {
|
|
167
167
|
paddingTop: 8,
|
|
@@ -190,6 +190,6 @@ const styles = StyleSheet.create({
|
|
|
190
190
|
fontSize: 10,
|
|
191
191
|
fontWeight: "700",
|
|
192
192
|
color: night.color.accent,
|
|
193
|
-
fontFamily:
|
|
193
|
+
fontFamily: monoFont
|
|
194
194
|
}
|
|
195
195
|
});
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import React, { memo, useState, useCallback, useMemo } from "react";
|
|
15
15
|
import { View, Text, StyleSheet, ScrollView } from "react-native";
|
|
16
|
-
import { buoyColors, night, EventStepperFooter, formatRelativeTime, parseValue, Info, Zap, FileText, Database, GitBranch, copyToClipboard, ProUpgradeModal, DiffModeTabs, CompareBar, DetailViewToggle } from "@buoy-gg/shared-ui";
|
|
16
|
+
import { buoyColors, night, EventStepperFooter, formatRelativeTime, parseValue, Info, Zap, FileText, Database, GitBranch, copyToClipboard, ProUpgradeModal, DiffModeTabs, CompareBar, DetailViewToggle, monoFont } from "@buoy-gg/shared-ui";
|
|
17
17
|
import { DataViewer, SplitDiffViewer, TreeDiffViewer, diffThemes } from "@buoy-gg/shared-ui/dataViewer";
|
|
18
18
|
import { useIsPro } from "@buoy-gg/license";
|
|
19
19
|
import { jotaiStateStore } from "../utils/jotaiStateStore";
|
|
@@ -476,7 +476,7 @@ const styles = StyleSheet.create({
|
|
|
476
476
|
valueLabel: {
|
|
477
477
|
fontSize: 10,
|
|
478
478
|
color: night.color.textSecondary,
|
|
479
|
-
fontFamily:
|
|
479
|
+
fontFamily: monoFont,
|
|
480
480
|
letterSpacing: 0.5,
|
|
481
481
|
fontWeight: "600"
|
|
482
482
|
},
|
|
@@ -493,7 +493,7 @@ const styles = StyleSheet.create({
|
|
|
493
493
|
changeText: {
|
|
494
494
|
fontSize: 9,
|
|
495
495
|
fontWeight: "700",
|
|
496
|
-
fontFamily:
|
|
496
|
+
fontFamily: monoFont,
|
|
497
497
|
letterSpacing: 0.3
|
|
498
498
|
},
|
|
499
499
|
typeBadge: {
|
|
@@ -506,7 +506,7 @@ const styles = StyleSheet.create({
|
|
|
506
506
|
fontSize: 9,
|
|
507
507
|
fontWeight: "600",
|
|
508
508
|
color: night.color.textTertiary,
|
|
509
|
-
fontFamily:
|
|
509
|
+
fontFamily: monoFont
|
|
510
510
|
},
|
|
511
511
|
valueBox: {
|
|
512
512
|
// Inset code box — darker than the card, the night track color.
|
|
@@ -519,7 +519,7 @@ const styles = StyleSheet.create({
|
|
|
519
519
|
valueText: {
|
|
520
520
|
fontSize: 12,
|
|
521
521
|
color: night.color.text,
|
|
522
|
-
fontFamily:
|
|
522
|
+
fontFamily: monoFont,
|
|
523
523
|
lineHeight: 18
|
|
524
524
|
},
|
|
525
525
|
diffContainer: {
|
|
@@ -537,7 +537,7 @@ const styles = StyleSheet.create({
|
|
|
537
537
|
changeTextSmall: {
|
|
538
538
|
fontSize: 8,
|
|
539
539
|
fontWeight: "700",
|
|
540
|
-
fontFamily:
|
|
540
|
+
fontFamily: monoFont
|
|
541
541
|
}
|
|
542
542
|
});
|
|
543
543
|
const infoStyles = StyleSheet.create({
|
|
@@ -583,7 +583,7 @@ const infoStyles = StyleSheet.create({
|
|
|
583
583
|
fontWeight: "600",
|
|
584
584
|
letterSpacing: 0.5,
|
|
585
585
|
color: night.color.accent,
|
|
586
|
-
fontFamily:
|
|
586
|
+
fontFamily: monoFont
|
|
587
587
|
},
|
|
588
588
|
cardContent: {
|
|
589
589
|
padding: 14,
|
|
@@ -608,7 +608,7 @@ const infoStyles = StyleSheet.create({
|
|
|
608
608
|
marginLeft: 12
|
|
609
609
|
},
|
|
610
610
|
infoValueMono: {
|
|
611
|
-
fontFamily:
|
|
611
|
+
fontFamily: monoFont
|
|
612
612
|
},
|
|
613
613
|
badgeRow: {
|
|
614
614
|
flexDirection: "row",
|
|
@@ -668,7 +668,7 @@ const infoStyles = StyleSheet.create({
|
|
|
668
668
|
fontSize: 11,
|
|
669
669
|
fontWeight: "600",
|
|
670
670
|
color: night.color.accent,
|
|
671
|
-
fontFamily:
|
|
671
|
+
fontFamily: monoFont
|
|
672
672
|
},
|
|
673
673
|
countBadge: {
|
|
674
674
|
paddingHorizontal: 8,
|
|
@@ -680,7 +680,7 @@ const infoStyles = StyleSheet.create({
|
|
|
680
680
|
fontSize: 9,
|
|
681
681
|
fontWeight: "700",
|
|
682
682
|
color: night.color.accent,
|
|
683
|
-
fontFamily:
|
|
683
|
+
fontFamily: monoFont
|
|
684
684
|
},
|
|
685
685
|
actionsRow: {
|
|
686
686
|
flexDirection: "row",
|
|
@@ -699,7 +699,7 @@ const infoStyles = StyleSheet.create({
|
|
|
699
699
|
actionButtonText: {
|
|
700
700
|
fontSize: 10,
|
|
701
701
|
fontWeight: "700",
|
|
702
|
-
fontFamily:
|
|
702
|
+
fontFamily: monoFont,
|
|
703
703
|
letterSpacing: 0.3
|
|
704
704
|
},
|
|
705
705
|
dataViewerContainer: {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
|
|
35
35
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
36
36
|
import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native";
|
|
37
|
-
import { buoyColors, ExpandedInfoRow, FileJson, night, PillBadge, parseValue } from "@buoy-gg/shared-ui";
|
|
37
|
+
import { buoyColors, ExpandedInfoRow, FileJson, night, PillBadge, parseValue, monoFont } from "@buoy-gg/shared-ui";
|
|
38
38
|
import { DataViewer, LiveExplorer, ValueEditModal, updateNestedDataByPath, deleteNestedDataByPath } from "@buoy-gg/shared-ui/dataViewer";
|
|
39
39
|
import { jotaiStateStore } from "../utils/jotaiStateStore";
|
|
40
40
|
import { JOTAI_TEST_ID, atomRowTestID } from "../utils/testIDs";
|
|
@@ -362,7 +362,7 @@ const styles = StyleSheet.create({
|
|
|
362
362
|
color: night.color.accent,
|
|
363
363
|
letterSpacing: 0.5,
|
|
364
364
|
textTransform: "uppercase",
|
|
365
|
-
fontFamily:
|
|
365
|
+
fontFamily: monoFont
|
|
366
366
|
},
|
|
367
367
|
rawButton: {
|
|
368
368
|
flexDirection: "row",
|
|
@@ -409,7 +409,7 @@ const styles = StyleSheet.create({
|
|
|
409
409
|
borderBottomColor: night.color.border,
|
|
410
410
|
letterSpacing: 0.5,
|
|
411
411
|
textTransform: "uppercase",
|
|
412
|
-
fontFamily:
|
|
412
|
+
fontFamily: monoFont
|
|
413
413
|
},
|
|
414
414
|
infoBody: {
|
|
415
415
|
padding: 12,
|
|
@@ -422,13 +422,13 @@ const styles = StyleSheet.create({
|
|
|
422
422
|
primitiveText: {
|
|
423
423
|
color: night.color.text,
|
|
424
424
|
fontSize: 12,
|
|
425
|
-
fontFamily:
|
|
425
|
+
fontFamily: monoFont,
|
|
426
426
|
padding: 14
|
|
427
427
|
},
|
|
428
428
|
valueTypeText: {
|
|
429
429
|
color: night.color.textTertiary,
|
|
430
430
|
fontSize: 10,
|
|
431
|
-
fontFamily:
|
|
431
|
+
fontFamily: monoFont,
|
|
432
432
|
marginLeft: 6
|
|
433
433
|
},
|
|
434
434
|
inlineLinkButton: {
|
|
@@ -437,6 +437,6 @@ const styles = StyleSheet.create({
|
|
|
437
437
|
inlineLinkText: {
|
|
438
438
|
fontSize: 10,
|
|
439
439
|
fontWeight: "600",
|
|
440
|
-
fontFamily:
|
|
440
|
+
fontFamily: monoFont
|
|
441
441
|
}
|
|
442
442
|
});
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
import { jotaiStateStore } from "../utils/jotaiStateStore";
|
|
4
|
-
import { WIRE_DETAIL_LIMIT_BYTES, createSyncAdapter, capDetail, detailByIdAction, memoByIdentity, onDeviceMarker, readParams, toWire } from "@buoy-gg/shared-ui";
|
|
4
|
+
import { WIRE_DETAIL_LIMIT_BYTES, createSyncAdapter, capDetail, detailByIdAction, expandKeyedArrays, memoByIdentity, onDeviceMarker, patchFromPath, readParams, readPath, refusalText, shapeSummary, suggestedPatch, toWire, typedEditViolations, unshapedLists } from "@buoy-gg/shared-ui";
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Sentinel used in WIRE-FORM snapshots in place of raw prevValue/nextValue.
|
|
@@ -9,6 +9,41 @@ import { WIRE_DETAIL_LIMIT_BYTES, createSyncAdapter, capDetail, detailByIdAction
|
|
|
9
9
|
* snapshot. Dashboards fetch values via `getChangeDetail`.
|
|
10
10
|
*/
|
|
11
11
|
export const VALUE_ON_DEVICE = onDeviceMarker("Value", "Atom values stay on the device — fetched on demand via getChangeDetail / getAtomValue.");
|
|
12
|
+
const isPlainObj = v => !!v && typeof v === "object" && !Array.isArray(v);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Merge a patch into an atom's value.
|
|
16
|
+
*
|
|
17
|
+
* An atom is not always an object — plenty hold a number, a string or a list —
|
|
18
|
+
* so a merge only means anything when BOTH sides are plain objects. Anything
|
|
19
|
+
* else replaces, which is what a caller asking to merge a scalar can only have
|
|
20
|
+
* meant. Arrays replace too: the id-addressed form has already been expanded to
|
|
21
|
+
* a whole array by the time this runs, so merging element-wise here would undo
|
|
22
|
+
* the deletion half of that expansion.
|
|
23
|
+
*/
|
|
24
|
+
function deepMergeInto(base, patch) {
|
|
25
|
+
if (!isPlainObj(base) || !isPlainObj(patch)) return patch;
|
|
26
|
+
const out = {
|
|
27
|
+
...base
|
|
28
|
+
};
|
|
29
|
+
for (const [k, v] of Object.entries(patch)) out[k] = k in base ? deepMergeInto(base[k], v) : v;
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Does this patch write into the list at `path`? Only then is an
|
|
35
|
+
* unchecked-shape warning about that list worth saying.
|
|
36
|
+
*/
|
|
37
|
+
function touchesList(patch, path) {
|
|
38
|
+
let cur = patch;
|
|
39
|
+
for (const seg of path.split(".")) {
|
|
40
|
+
if (!isPlainObj(cur)) return false;
|
|
41
|
+
if (!(seg in cur)) return false;
|
|
42
|
+
cur = cur[seg];
|
|
43
|
+
}
|
|
44
|
+
return Array.isArray(cur) ? cur.length > 0 : !!cur && typeof cur === "object";
|
|
45
|
+
}
|
|
46
|
+
|
|
12
47
|
/**
|
|
13
48
|
* Per-change wire cache — the store prepends and never mutates a recorded
|
|
14
49
|
* change, so identity is a correct, self-invalidating key.
|
|
@@ -63,7 +98,8 @@ export const jotaiSyncAdapter = createSyncAdapter({
|
|
|
63
98
|
*/
|
|
64
99
|
getAtomValue: params => {
|
|
65
100
|
const {
|
|
66
|
-
label
|
|
101
|
+
label,
|
|
102
|
+
path
|
|
67
103
|
} = readParams(params);
|
|
68
104
|
if (!label) return {
|
|
69
105
|
found: false,
|
|
@@ -89,9 +125,32 @@ export const jotaiSyncAdapter = createSyncAdapter({
|
|
|
89
125
|
currentValue: VALUE_ON_DEVICE
|
|
90
126
|
};
|
|
91
127
|
}
|
|
128
|
+
// One slice instead of the whole atom, when asked. The reason to want it
|
|
129
|
+
// is the engine's 24,000-character result cap: a caller that never saw the
|
|
130
|
+
// field it means to edit is left inventing a shape.
|
|
131
|
+
if (path) {
|
|
132
|
+
const at = readPath(currentValue, path);
|
|
133
|
+
if (!at.ok) return {
|
|
134
|
+
found: false,
|
|
135
|
+
reason: at.error
|
|
136
|
+
};
|
|
137
|
+
return {
|
|
138
|
+
found: true,
|
|
139
|
+
label,
|
|
140
|
+
path,
|
|
141
|
+
shape: shapeSummary(at.value),
|
|
142
|
+
currentValue: at.value
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// `shape` BEFORE `currentValue`: a result is truncated by slicing the
|
|
146
|
+
// encoded string, so what is serialised first is what survives the cut,
|
|
147
|
+
// and the sketch does not grow with the data.
|
|
92
148
|
return {
|
|
93
149
|
found: true,
|
|
94
150
|
label,
|
|
151
|
+
...(shapeSummary(currentValue) ? {
|
|
152
|
+
shape: shapeSummary(currentValue)
|
|
153
|
+
} : {}),
|
|
95
154
|
currentValue: capDetail(currentValue, WIRE_DETAIL_LIMIT_BYTES)
|
|
96
155
|
};
|
|
97
156
|
},
|
|
@@ -117,10 +176,22 @@ export const jotaiSyncAdapter = createSyncAdapter({
|
|
|
117
176
|
value = undefined;
|
|
118
177
|
}
|
|
119
178
|
}
|
|
179
|
+
// The compact view is what a caller reaches for precisely because it
|
|
180
|
+
// does not want the data; the item shape of a list is the one thing it
|
|
181
|
+
// cannot infer without it, and it costs a line.
|
|
182
|
+
let shape;
|
|
183
|
+
try {
|
|
184
|
+
shape = shapeSummary(a.getValue());
|
|
185
|
+
} catch {
|
|
186
|
+
shape = undefined;
|
|
187
|
+
}
|
|
120
188
|
return {
|
|
121
189
|
label: a.label,
|
|
122
190
|
changes: a.changeCount,
|
|
123
191
|
writable: typeof a.setValue === "function",
|
|
192
|
+
...(shape ? {
|
|
193
|
+
shape
|
|
194
|
+
} : {}),
|
|
124
195
|
...(includeValues ? {
|
|
125
196
|
currentValue: value
|
|
126
197
|
} : {})
|
|
@@ -133,14 +204,100 @@ export const jotaiSyncAdapter = createSyncAdapter({
|
|
|
133
204
|
includedValues: includeValues
|
|
134
205
|
};
|
|
135
206
|
},
|
|
136
|
-
/**
|
|
207
|
+
/**
|
|
208
|
+
* Write to a writable atom (throws for unknown/read-only atoms).
|
|
209
|
+
*
|
|
210
|
+
* THIS USED TO BE FOUR LINES WITH NO GUARD. `setAtom` took whatever it was
|
|
211
|
+
* given and wrote it: a string over a number, `null` over a list the screen
|
|
212
|
+
* maps, a one-item array over a fifty-item one — every failure mode the
|
|
213
|
+
* zustand and react-query adapters refuse, jotai accepted silently. It was
|
|
214
|
+
* the only writable state tool with no protection at all, and it had never
|
|
215
|
+
* had an eval case, so nothing had ever measured it.
|
|
216
|
+
*
|
|
217
|
+
* It now takes the same three forms as `zustand.setState`, backed by the
|
|
218
|
+
* same shared implementations:
|
|
219
|
+
* { value } replace (guarded unless force)
|
|
220
|
+
* { value, merge: true } merge a patch, id-addressed lists allowed
|
|
221
|
+
* { path, value } set one value; no depth to get wrong
|
|
222
|
+
*/
|
|
137
223
|
setAtom: params => {
|
|
138
224
|
const {
|
|
139
225
|
label,
|
|
140
|
-
value
|
|
226
|
+
value,
|
|
227
|
+
merge,
|
|
228
|
+
path,
|
|
229
|
+
force
|
|
141
230
|
} = readParams(params);
|
|
142
231
|
if (!label) throw new Error("setAtom requires a `label`");
|
|
143
|
-
jotaiStateStore.
|
|
232
|
+
const atom = jotaiStateStore.getAtom(label);
|
|
233
|
+
let current;
|
|
234
|
+
try {
|
|
235
|
+
current = atom?.getValue();
|
|
236
|
+
} catch {
|
|
237
|
+
current = undefined;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// A replace with no guard is the old behaviour; keep it reachable via
|
|
241
|
+
// `force`, and keep it the ONLY way to get it.
|
|
242
|
+
let effective = value;
|
|
243
|
+
let keyedNotes = [];
|
|
244
|
+
let unchecked = [];
|
|
245
|
+
const merging = merge === true || path !== undefined;
|
|
246
|
+
if (path !== undefined) {
|
|
247
|
+
if (value === undefined) {
|
|
248
|
+
return {
|
|
249
|
+
ok: false,
|
|
250
|
+
error: `Refused: sent \`path\` without \`value\`. Say what to set "${path}" to.`
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const built = patchFromPath(current, path, value);
|
|
254
|
+
if (!built.ok) return {
|
|
255
|
+
ok: false,
|
|
256
|
+
error: `Refused: ${built.error}`
|
|
257
|
+
};
|
|
258
|
+
effective = built.patch;
|
|
259
|
+
}
|
|
260
|
+
if (!force && merging && current !== undefined) {
|
|
261
|
+
const expanded = expandKeyedArrays(current, effective);
|
|
262
|
+
// See keyedArrays.ts: an added item whose id disagrees with the key it
|
|
263
|
+
// was sent under is ambiguous, and only this layer can say so.
|
|
264
|
+
if (expanded.error) {
|
|
265
|
+
return {
|
|
266
|
+
ok: false,
|
|
267
|
+
error: `Refused: ${expanded.error}`
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
effective = expanded.patch;
|
|
271
|
+
keyedNotes = expanded.notes;
|
|
272
|
+
unchecked = unshapedLists(current).filter(p => touchesList(effective, p));
|
|
273
|
+
const violations = typedEditViolations(current, effective, {
|
|
274
|
+
noun: "atom"
|
|
275
|
+
});
|
|
276
|
+
if (violations.length) {
|
|
277
|
+
const suggestion = suggestedPatch(effective, violations);
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
error: refusalText(violations, "atom", suggestion),
|
|
281
|
+
violations,
|
|
282
|
+
...(suggestion ? {
|
|
283
|
+
suggestedValue: suggestion
|
|
284
|
+
} : {})
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
jotaiStateStore.setAtomValue(label, merging ? deepMergeInto(current, effective) : effective);
|
|
289
|
+
return {
|
|
290
|
+
ok: true,
|
|
291
|
+
...(path !== undefined ? {
|
|
292
|
+
path
|
|
293
|
+
} : {}),
|
|
294
|
+
...(keyedNotes.length ? {
|
|
295
|
+
keyed: keyedNotes
|
|
296
|
+
} : {}),
|
|
297
|
+
...(unchecked.length ? {
|
|
298
|
+
unchecked: unchecked.map(p => `\`${p}\` was empty, so nothing knows what its items look like and the shape of what you added was NOT checked.`)
|
|
299
|
+
} : {})
|
|
300
|
+
};
|
|
144
301
|
}
|
|
145
302
|
}
|
|
146
303
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JotaiAtomBrowser.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomBrowser.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"JotaiAtomBrowser.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomBrowser.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAI9C,UAAU,qBAAqB;IAC7B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,kDAAkD;IAClD,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAuDD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,WAAW,EACX,UAAU,GACX,EAAE,qBAAqB,+BA4FvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JotaiAtomDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAA+C,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"JotaiAtomDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAA+C,MAAM,OAAO,CAAC;AA6BpE,OAAO,KAAK,EACV,eAAe,EAEf,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAwOlB,UAAU,2BAA2B;IACnC,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;OAGG;IACH,WAAW,CAAC,EAAE,mBAAmB,CAAC;CACnC;AAED,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,EACb,qBAA6B,EAC7B,WAAsB,GACvB,EAAE,2BAA2B,qBAkL7B;AAED,wBAAgB,qBAAqB,CAAC,EACpC,MAAM,EACN,OAAO,EACP,aAAa,EACb,aAAa,GACd,EAAE;IACD,MAAM,EAAE,eAAe,CAAC;IACxB,OAAO,EAAE,eAAe,EAAE,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACxC,4BAcA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"JotaiAtomValueDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomValueDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;
|
|
1
|
+
{"version":3,"file":"JotaiAtomValueDetailContent.d.ts","sourceRoot":"","sources":["../../../../src/jotai/components/JotaiAtomValueDetailContent.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AA2BH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAoE9C,UAAU,gCAAgC;IACxC,IAAI,EAAE,aAAa,CAAC;IACpB,kEAAkE;IAClE,aAAa,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED,wBAAgB,2BAA2B,CAAC,EAC1C,IAAI,EACJ,aAAa,GACd,EAAE,gCAAgC,+BA6OlC"}
|
|
@@ -51,12 +51,29 @@ export declare const jotaiSyncAdapter: import("@buoy-gg/shared-ui").SyncAdapter<
|
|
|
51
51
|
found: false;
|
|
52
52
|
reason: string;
|
|
53
53
|
label?: undefined;
|
|
54
|
+
path?: undefined;
|
|
55
|
+
shape?: undefined;
|
|
54
56
|
currentValue?: undefined;
|
|
55
57
|
} | {
|
|
56
58
|
found: true;
|
|
57
59
|
label: string;
|
|
60
|
+
path: string;
|
|
61
|
+
shape: {
|
|
62
|
+
shape: string;
|
|
63
|
+
lists?: import("@buoy-gg/shared-ui").ListShape[];
|
|
64
|
+
} | undefined;
|
|
58
65
|
currentValue: unknown;
|
|
59
66
|
reason?: undefined;
|
|
67
|
+
} | {
|
|
68
|
+
currentValue: unknown;
|
|
69
|
+
shape?: {
|
|
70
|
+
shape: string;
|
|
71
|
+
lists?: import("@buoy-gg/shared-ui").ListShape[];
|
|
72
|
+
} | undefined;
|
|
73
|
+
found: true;
|
|
74
|
+
label: string;
|
|
75
|
+
reason?: undefined;
|
|
76
|
+
path?: undefined;
|
|
60
77
|
};
|
|
61
78
|
/**
|
|
62
79
|
* Compact current-value reader for a remote driver (MCP/LLM): registered
|
|
@@ -67,6 +84,10 @@ export declare const jotaiSyncAdapter: import("@buoy-gg/shared-ui").SyncAdapter<
|
|
|
67
84
|
listAtoms: (params?: unknown) => {
|
|
68
85
|
atoms: {
|
|
69
86
|
currentValue?: unknown;
|
|
87
|
+
shape?: {
|
|
88
|
+
shape: string;
|
|
89
|
+
lists?: import("@buoy-gg/shared-ui").ListShape[];
|
|
90
|
+
} | undefined;
|
|
70
91
|
label: string;
|
|
71
92
|
changes: number;
|
|
72
93
|
writable: boolean;
|
|
@@ -75,7 +96,36 @@ export declare const jotaiSyncAdapter: import("@buoy-gg/shared-ui").SyncAdapter<
|
|
|
75
96
|
returned: number;
|
|
76
97
|
includedValues: boolean;
|
|
77
98
|
};
|
|
78
|
-
/**
|
|
79
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Write to a writable atom (throws for unknown/read-only atoms).
|
|
101
|
+
*
|
|
102
|
+
* THIS USED TO BE FOUR LINES WITH NO GUARD. `setAtom` took whatever it was
|
|
103
|
+
* given and wrote it: a string over a number, `null` over a list the screen
|
|
104
|
+
* maps, a one-item array over a fifty-item one — every failure mode the
|
|
105
|
+
* zustand and react-query adapters refuse, jotai accepted silently. It was
|
|
106
|
+
* the only writable state tool with no protection at all, and it had never
|
|
107
|
+
* had an eval case, so nothing had ever measured it.
|
|
108
|
+
*
|
|
109
|
+
* It now takes the same three forms as `zustand.setState`, backed by the
|
|
110
|
+
* same shared implementations:
|
|
111
|
+
* { value } replace (guarded unless force)
|
|
112
|
+
* { value, merge: true } merge a patch, id-addressed lists allowed
|
|
113
|
+
* { path, value } set one value; no depth to get wrong
|
|
114
|
+
*/
|
|
115
|
+
setAtom: (params?: unknown) => {
|
|
116
|
+
ok: false;
|
|
117
|
+
error: string;
|
|
118
|
+
} | {
|
|
119
|
+
suggestedValue?: Record<string, unknown> | undefined;
|
|
120
|
+
ok: false;
|
|
121
|
+
error: string;
|
|
122
|
+
violations: import("@buoy-gg/shared-ui").EditViolation[];
|
|
123
|
+
} | {
|
|
124
|
+
unchecked?: string[] | undefined;
|
|
125
|
+
keyed?: string[] | undefined;
|
|
126
|
+
path?: string | undefined;
|
|
127
|
+
ok: true;
|
|
128
|
+
error?: undefined;
|
|
129
|
+
};
|
|
80
130
|
}>;
|
|
81
131
|
//# sourceMappingURL=jotaiSyncAdapter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jotaiSyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/jotai/sync/jotaiSyncAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"jotaiSyncAdapter.d.ts","sourceRoot":"","sources":["../../../../src/jotai/sync/jotaiSyncAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAqBhD;;;;GAIG;AACH,eAAO,MAAM,eAAe;;;;EAG3B,CAAC;AAmFF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;IAiBzB;;;;OAIG;;;;;IAQH;;;OAGG;4BACqB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAsC/B;;;;;OAKG;yBACkB,OAAO;;;;;;;;;;;;;;;IAsC5B;;;;;;;;;;;;;;;OAeG;uBACgB,OAAO;;;;;;;;;;;;;;;EAiEF,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@buoy-gg/jotai",
|
|
3
|
-
"version": "7.0.
|
|
3
|
+
"version": "7.0.36",
|
|
4
4
|
"description": "Jotai devtools inside your React Native app — every atom write shows prev → next, with per-atom history & live values. Part of Buoy devtools.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"**/snapshotProvider.ts"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@buoy-gg/
|
|
33
|
-
"@buoy-gg/
|
|
32
|
+
"@buoy-gg/license": "^7.0.36",
|
|
33
|
+
"@buoy-gg/shared-ui": "^7.0.36"
|
|
34
34
|
},
|
|
35
35
|
"peerDependencies": {
|
|
36
36
|
"jotai": ">=2.0.0",
|