@buoy-gg/shared-ui 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.
Files changed (207) hide show
  1. package/lib/commonjs/dataViewer/CyberpunkInput.js +3 -2
  2. package/lib/commonjs/dataViewer/DataTreeActionBar.js +3 -2
  3. package/lib/commonjs/dataViewer/DataTreeActionDock.js +2 -1
  4. package/lib/commonjs/dataViewer/DiffSummary.js +4 -3
  5. package/lib/commonjs/dataViewer/LiveExplorer.js +11 -10
  6. package/lib/commonjs/dataViewer/SplitDiffViewer.js +10 -9
  7. package/lib/commonjs/dataViewer/ThemedSplitView.js +12 -11
  8. package/lib/commonjs/dataViewer/ValueEditModal.js +6 -5
  9. package/lib/commonjs/dataViewer/VirtualizedDataExplorer.js +4 -3
  10. package/lib/commonjs/dataViewer/tree/TreeDiffViewer.js +15 -14
  11. package/lib/commonjs/hooks/safe-area-impl.js +1 -1
  12. package/lib/commonjs/icons/index.js +540 -1
  13. package/lib/commonjs/index.js +48 -0
  14. package/lib/commonjs/license/FreeAccountPrompt.js +2 -1
  15. package/lib/commonjs/license/LicenseEntryModal.js +2 -1
  16. package/lib/commonjs/sync/keyedArrays.js +209 -0
  17. package/lib/commonjs/sync/toolAttention.test.js +51 -0
  18. package/lib/commonjs/sync/toolUiBridge.js +58 -0
  19. package/lib/commonjs/sync/typedEdit.js +228 -0
  20. package/lib/commonjs/sync/valuePath.js +337 -0
  21. package/lib/commonjs/sync/valueShape.js +161 -0
  22. package/lib/commonjs/ui/components/ChangeRow.js +2 -1
  23. package/lib/commonjs/ui/components/ClipboardHintBanner.js +2 -1
  24. package/lib/commonjs/ui/components/CompactRow.js +3 -2
  25. package/lib/commonjs/ui/components/DataInspector.js +3 -2
  26. package/lib/commonjs/ui/components/DetailView.js +2 -1
  27. package/lib/commonjs/ui/components/DynamicFilterView.js +8 -7
  28. package/lib/commonjs/ui/components/EntityFilterView.js +9 -8
  29. package/lib/commonjs/ui/components/EventHistoryViewer/CompareBar.js +5 -4
  30. package/lib/commonjs/ui/components/EventHistoryViewer/DiffModeTabs.js +2 -1
  31. package/lib/commonjs/ui/components/EventHistoryViewer/EventPickerModal.js +5 -4
  32. package/lib/commonjs/ui/components/EventStepperFooter.js +4 -3
  33. package/lib/commonjs/ui/components/ExpandablePopover.js +48 -8
  34. package/lib/commonjs/ui/components/ExpandedInfoRow.js +3 -2
  35. package/lib/commonjs/ui/components/FilterComponents.js +2 -1
  36. package/lib/commonjs/ui/components/InfoRow.js +2 -1
  37. package/lib/commonjs/ui/components/Pill.js +2 -1
  38. package/lib/commonjs/ui/components/ToolFooter.js +3 -2
  39. package/lib/commonjs/ui/console/CyberpunkSectionButton.js +3 -2
  40. package/lib/commonjs/ui/fonts.js +12 -0
  41. package/lib/commonjs/ui/gameUI/components/GameUICollapsibleSection.js +4 -3
  42. package/lib/commonjs/ui/gameUI/components/GameUICompactStats.js +12 -11
  43. package/lib/commonjs/ui/gameUI/components/GameUIIssuesList.js +9 -8
  44. package/lib/commonjs/ui/gameUI/components/GameUIStatusHeader.js +4 -3
  45. package/lib/commonjs/ui/index.js +8 -0
  46. package/lib/commonjs/ui/toolTheme.js +4 -3
  47. package/lib/module/dataViewer/CyberpunkInput.js +3 -2
  48. package/lib/module/dataViewer/DataTreeActionBar.js +3 -2
  49. package/lib/module/dataViewer/DataTreeActionDock.js +2 -1
  50. package/lib/module/dataViewer/DiffSummary.js +4 -3
  51. package/lib/module/dataViewer/LiveExplorer.js +11 -11
  52. package/lib/module/dataViewer/SplitDiffViewer.js +10 -9
  53. package/lib/module/dataViewer/ThemedSplitView.js +12 -11
  54. package/lib/module/dataViewer/ValueEditModal.js +6 -5
  55. package/lib/module/dataViewer/VirtualizedDataExplorer.js +5 -4
  56. package/lib/module/dataViewer/tree/TreeDiffViewer.js +15 -15
  57. package/lib/module/hooks/safe-area-impl.js +1 -1
  58. package/lib/module/icons/index.js +29 -2
  59. package/lib/module/index.js +4 -0
  60. package/lib/module/license/FreeAccountPrompt.js +2 -2
  61. package/lib/module/license/LicenseEntryModal.js +3 -3
  62. package/lib/module/sync/keyedArrays.js +204 -0
  63. package/lib/module/sync/toolAttention.test.js +50 -0
  64. package/lib/module/sync/toolUiBridge.js +55 -0
  65. package/lib/module/sync/typedEdit.js +220 -0
  66. package/lib/module/sync/valuePath.js +330 -0
  67. package/lib/module/sync/valueShape.js +153 -0
  68. package/lib/module/ui/components/ChangeRow.js +2 -1
  69. package/lib/module/ui/components/ClipboardHintBanner.js +2 -1
  70. package/lib/module/ui/components/CompactRow.js +3 -2
  71. package/lib/module/ui/components/DataInspector.js +3 -2
  72. package/lib/module/ui/components/DetailView.js +2 -1
  73. package/lib/module/ui/components/DynamicFilterView.js +8 -7
  74. package/lib/module/ui/components/EntityFilterView.js +9 -8
  75. package/lib/module/ui/components/EventHistoryViewer/CompareBar.js +6 -5
  76. package/lib/module/ui/components/EventHistoryViewer/DiffModeTabs.js +3 -2
  77. package/lib/module/ui/components/EventHistoryViewer/EventPickerModal.js +6 -5
  78. package/lib/module/ui/components/EventStepperFooter.js +4 -3
  79. package/lib/module/ui/components/ExpandablePopover.js +48 -8
  80. package/lib/module/ui/components/ExpandedInfoRow.js +3 -2
  81. package/lib/module/ui/components/FilterComponents.js +2 -2
  82. package/lib/module/ui/components/InfoRow.js +2 -1
  83. package/lib/module/ui/components/Pill.js +2 -1
  84. package/lib/module/ui/components/ToolFooter.js +3 -2
  85. package/lib/module/ui/console/CyberpunkSectionButton.js +3 -2
  86. package/lib/module/ui/fonts.js +8 -0
  87. package/lib/module/ui/gameUI/components/GameUICollapsibleSection.js +4 -3
  88. package/lib/module/ui/gameUI/components/GameUICompactStats.js +12 -11
  89. package/lib/module/ui/gameUI/components/GameUIIssuesList.js +9 -8
  90. package/lib/module/ui/gameUI/components/GameUIStatusHeader.js +4 -3
  91. package/lib/module/ui/index.js +3 -0
  92. package/lib/module/ui/toolTheme.js +4 -4
  93. package/lib/typescript/commonjs/dataViewer/CyberpunkInput.d.ts.map +1 -1
  94. package/lib/typescript/commonjs/dataViewer/DataTreeActionBar.d.ts.map +1 -1
  95. package/lib/typescript/commonjs/dataViewer/DataTreeActionDock.d.ts.map +1 -1
  96. package/lib/typescript/commonjs/dataViewer/DiffSummary.d.ts.map +1 -1
  97. package/lib/typescript/commonjs/dataViewer/LiveExplorer.d.ts.map +1 -1
  98. package/lib/typescript/commonjs/dataViewer/SplitDiffViewer.d.ts.map +1 -1
  99. package/lib/typescript/commonjs/dataViewer/ThemedSplitView.d.ts.map +1 -1
  100. package/lib/typescript/commonjs/dataViewer/ValueEditModal.d.ts.map +1 -1
  101. package/lib/typescript/commonjs/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
  102. package/lib/typescript/commonjs/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
  103. package/lib/typescript/commonjs/hooks/safe-area-impl.d.ts +1 -1
  104. package/lib/typescript/commonjs/icons/index.d.ts +4 -1
  105. package/lib/typescript/commonjs/icons/index.d.ts.map +1 -1
  106. package/lib/typescript/commonjs/index.d.ts +4 -0
  107. package/lib/typescript/commonjs/index.d.ts.map +1 -1
  108. package/lib/typescript/commonjs/license/FreeAccountPrompt.d.ts.map +1 -1
  109. package/lib/typescript/commonjs/license/LicenseEntryModal.d.ts.map +1 -1
  110. package/lib/typescript/commonjs/sync/keyedArrays.d.ts +35 -0
  111. package/lib/typescript/commonjs/sync/keyedArrays.d.ts.map +1 -0
  112. package/lib/typescript/commonjs/sync/toolAttention.test.d.ts +2 -0
  113. package/lib/typescript/commonjs/sync/toolAttention.test.d.ts.map +1 -0
  114. package/lib/typescript/commonjs/sync/toolUiBridge.d.ts +25 -0
  115. package/lib/typescript/commonjs/sync/toolUiBridge.d.ts.map +1 -1
  116. package/lib/typescript/commonjs/sync/typedEdit.d.ts +70 -0
  117. package/lib/typescript/commonjs/sync/typedEdit.d.ts.map +1 -0
  118. package/lib/typescript/commonjs/sync/valuePath.d.ts +52 -0
  119. package/lib/typescript/commonjs/sync/valuePath.d.ts.map +1 -0
  120. package/lib/typescript/commonjs/sync/valueShape.d.ts +77 -0
  121. package/lib/typescript/commonjs/sync/valueShape.d.ts.map +1 -0
  122. package/lib/typescript/commonjs/ui/components/ChangeRow.d.ts.map +1 -1
  123. package/lib/typescript/commonjs/ui/components/ClipboardHintBanner.d.ts.map +1 -1
  124. package/lib/typescript/commonjs/ui/components/CompactRow.d.ts.map +1 -1
  125. package/lib/typescript/commonjs/ui/components/DataInspector.d.ts.map +1 -1
  126. package/lib/typescript/commonjs/ui/components/DetailView.d.ts.map +1 -1
  127. package/lib/typescript/commonjs/ui/components/DynamicFilterView.d.ts.map +1 -1
  128. package/lib/typescript/commonjs/ui/components/EntityFilterView.d.ts.map +1 -1
  129. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/CompareBar.d.ts.map +1 -1
  130. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/DiffModeTabs.d.ts.map +1 -1
  131. package/lib/typescript/commonjs/ui/components/EventHistoryViewer/EventPickerModal.d.ts.map +1 -1
  132. package/lib/typescript/commonjs/ui/components/EventStepperFooter.d.ts.map +1 -1
  133. package/lib/typescript/commonjs/ui/components/ExpandablePopover.d.ts +18 -1
  134. package/lib/typescript/commonjs/ui/components/ExpandablePopover.d.ts.map +1 -1
  135. package/lib/typescript/commonjs/ui/components/ExpandedInfoRow.d.ts.map +1 -1
  136. package/lib/typescript/commonjs/ui/components/FilterComponents.d.ts.map +1 -1
  137. package/lib/typescript/commonjs/ui/components/InfoRow.d.ts.map +1 -1
  138. package/lib/typescript/commonjs/ui/components/Pill.d.ts.map +1 -1
  139. package/lib/typescript/commonjs/ui/components/ToolFooter.d.ts.map +1 -1
  140. package/lib/typescript/commonjs/ui/console/CyberpunkSectionButton.d.ts.map +1 -1
  141. package/lib/typescript/commonjs/ui/fonts.d.ts +7 -0
  142. package/lib/typescript/commonjs/ui/fonts.d.ts.map +1 -0
  143. package/lib/typescript/commonjs/ui/gameUI/components/GameUICollapsibleSection.d.ts.map +1 -1
  144. package/lib/typescript/commonjs/ui/gameUI/components/GameUICompactStats.d.ts.map +1 -1
  145. package/lib/typescript/commonjs/ui/gameUI/components/GameUIIssuesList.d.ts.map +1 -1
  146. package/lib/typescript/commonjs/ui/gameUI/components/GameUIStatusHeader.d.ts.map +1 -1
  147. package/lib/typescript/commonjs/ui/index.d.ts +1 -0
  148. package/lib/typescript/commonjs/ui/index.d.ts.map +1 -1
  149. package/lib/typescript/commonjs/ui/toolTheme.d.ts.map +1 -1
  150. package/lib/typescript/module/dataViewer/CyberpunkInput.d.ts.map +1 -1
  151. package/lib/typescript/module/dataViewer/DataTreeActionBar.d.ts.map +1 -1
  152. package/lib/typescript/module/dataViewer/DataTreeActionDock.d.ts.map +1 -1
  153. package/lib/typescript/module/dataViewer/DiffSummary.d.ts.map +1 -1
  154. package/lib/typescript/module/dataViewer/LiveExplorer.d.ts.map +1 -1
  155. package/lib/typescript/module/dataViewer/SplitDiffViewer.d.ts.map +1 -1
  156. package/lib/typescript/module/dataViewer/ThemedSplitView.d.ts.map +1 -1
  157. package/lib/typescript/module/dataViewer/ValueEditModal.d.ts.map +1 -1
  158. package/lib/typescript/module/dataViewer/VirtualizedDataExplorer.d.ts.map +1 -1
  159. package/lib/typescript/module/dataViewer/tree/TreeDiffViewer.d.ts.map +1 -1
  160. package/lib/typescript/module/hooks/safe-area-impl.d.ts +1 -1
  161. package/lib/typescript/module/icons/index.d.ts +4 -1
  162. package/lib/typescript/module/icons/index.d.ts.map +1 -1
  163. package/lib/typescript/module/index.d.ts +4 -0
  164. package/lib/typescript/module/index.d.ts.map +1 -1
  165. package/lib/typescript/module/license/FreeAccountPrompt.d.ts.map +1 -1
  166. package/lib/typescript/module/license/LicenseEntryModal.d.ts.map +1 -1
  167. package/lib/typescript/module/sync/keyedArrays.d.ts +35 -0
  168. package/lib/typescript/module/sync/keyedArrays.d.ts.map +1 -0
  169. package/lib/typescript/module/sync/toolAttention.test.d.ts +2 -0
  170. package/lib/typescript/module/sync/toolAttention.test.d.ts.map +1 -0
  171. package/lib/typescript/module/sync/toolUiBridge.d.ts +25 -0
  172. package/lib/typescript/module/sync/toolUiBridge.d.ts.map +1 -1
  173. package/lib/typescript/module/sync/typedEdit.d.ts +70 -0
  174. package/lib/typescript/module/sync/typedEdit.d.ts.map +1 -0
  175. package/lib/typescript/module/sync/valuePath.d.ts +52 -0
  176. package/lib/typescript/module/sync/valuePath.d.ts.map +1 -0
  177. package/lib/typescript/module/sync/valueShape.d.ts +77 -0
  178. package/lib/typescript/module/sync/valueShape.d.ts.map +1 -0
  179. package/lib/typescript/module/ui/components/ChangeRow.d.ts.map +1 -1
  180. package/lib/typescript/module/ui/components/ClipboardHintBanner.d.ts.map +1 -1
  181. package/lib/typescript/module/ui/components/CompactRow.d.ts.map +1 -1
  182. package/lib/typescript/module/ui/components/DataInspector.d.ts.map +1 -1
  183. package/lib/typescript/module/ui/components/DetailView.d.ts.map +1 -1
  184. package/lib/typescript/module/ui/components/DynamicFilterView.d.ts.map +1 -1
  185. package/lib/typescript/module/ui/components/EntityFilterView.d.ts.map +1 -1
  186. package/lib/typescript/module/ui/components/EventHistoryViewer/CompareBar.d.ts.map +1 -1
  187. package/lib/typescript/module/ui/components/EventHistoryViewer/DiffModeTabs.d.ts.map +1 -1
  188. package/lib/typescript/module/ui/components/EventHistoryViewer/EventPickerModal.d.ts.map +1 -1
  189. package/lib/typescript/module/ui/components/EventStepperFooter.d.ts.map +1 -1
  190. package/lib/typescript/module/ui/components/ExpandablePopover.d.ts +18 -1
  191. package/lib/typescript/module/ui/components/ExpandablePopover.d.ts.map +1 -1
  192. package/lib/typescript/module/ui/components/ExpandedInfoRow.d.ts.map +1 -1
  193. package/lib/typescript/module/ui/components/FilterComponents.d.ts.map +1 -1
  194. package/lib/typescript/module/ui/components/InfoRow.d.ts.map +1 -1
  195. package/lib/typescript/module/ui/components/Pill.d.ts.map +1 -1
  196. package/lib/typescript/module/ui/components/ToolFooter.d.ts.map +1 -1
  197. package/lib/typescript/module/ui/console/CyberpunkSectionButton.d.ts.map +1 -1
  198. package/lib/typescript/module/ui/fonts.d.ts +7 -0
  199. package/lib/typescript/module/ui/fonts.d.ts.map +1 -0
  200. package/lib/typescript/module/ui/gameUI/components/GameUICollapsibleSection.d.ts.map +1 -1
  201. package/lib/typescript/module/ui/gameUI/components/GameUICompactStats.d.ts.map +1 -1
  202. package/lib/typescript/module/ui/gameUI/components/GameUIIssuesList.d.ts.map +1 -1
  203. package/lib/typescript/module/ui/gameUI/components/GameUIStatusHeader.d.ts.map +1 -1
  204. package/lib/typescript/module/ui/index.d.ts +1 -0
  205. package/lib/typescript/module/ui/index.d.ts.map +1 -1
  206. package/lib/typescript/module/ui/toolTheme.d.ts.map +1 -1
  207. package/package.json +4 -4
@@ -0,0 +1,337 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.parsePath = parsePath;
7
+ exports.patchFromPath = patchFromPath;
8
+ exports.readPath = readPath;
9
+ var _keyedArrays = require("./keyedArrays.js");
10
+ /**
11
+ * Addressing a single value inside cached data or a store — one implementation,
12
+ * shared by every adapter that reads or merges.
13
+ *
14
+ * WHY. The most common measured write failure is not a wrong value, it is a
15
+ * wrong DEPTH: ten of eleven `query` failures on the bench sent `{name: "x"}`
16
+ * for a payload shaped `{item: {name}}`. That is not a missing instruction — the
17
+ * system prompt says, verbatim, "if the value is {item:{name}} send
18
+ * {"item":{"name":"test123"}}, never {name}" — and models that had just called
19
+ * `getQueryData` still flattened it. The rule is being read past, not missed.
20
+ *
21
+ * So stop asking. `path: "item.name", value: "x"` has no depth to get wrong:
22
+ * there is exactly one way to write it and the adapter builds the nested patch.
23
+ * The same syntax on the READ side returns one slice instead of a payload that
24
+ * gets cut off at the engine's 24,000-character cap, which is the other half of
25
+ * the same problem — a caller cannot write a shape it was never able to see.
26
+ *
27
+ * Lists are addressed by IDENTITY, never by position. `lines[0]` is resolved to
28
+ * the id of whatever is at index 0 right now and rewritten as an id-addressed
29
+ * edit, because between the read and the write an item may have been removed and
30
+ * index 0 is then a different row. A list whose items carry no stable id is
31
+ * refused rather than guessed at — see keyedArrays.ts for why silently picking a
32
+ * row is the failure mode that loses data.
33
+ */
34
+
35
+ const isPlainObject = v => typeof v === "object" && v !== null && !Array.isArray(v);
36
+ /**
37
+ * Where a field of this name actually lives, as a dotted path — breadth-first,
38
+ * capped so a big payload cannot stall the walk.
39
+ *
40
+ * A wrong `path` is not the same mistake as a wrong depth, and `path` does not
41
+ * prevent it. Measured on TWO cases in one run: grok wrote
42
+ * `path: "item.name"` against the flat PokéAPI payload `{name, id, stats, …}`,
43
+ * having generalised the shape from a different screen it had seen. The value it
44
+ * wanted was at `name`, one segment, right there. A refusal that says so is a
45
+ * one-turn recovery; a refusal that only lists the available fields makes the
46
+ * caller re-derive it.
47
+ */
48
+ function findFieldPath(obj, key, maxNodes = 2000) {
49
+ const queue = [{
50
+ node: obj,
51
+ path: ""
52
+ }];
53
+ let seen = 0;
54
+ while (queue.length && seen < maxNodes) {
55
+ const {
56
+ node,
57
+ path
58
+ } = queue.shift();
59
+ seen++;
60
+ if (Array.isArray(node)) {
61
+ node.slice(0, 20).forEach((v, i) => queue.push({
62
+ node: v,
63
+ path: `${path}[${i}]`
64
+ }));
65
+ } else if (isPlainObject(node)) {
66
+ for (const [k, v] of Object.entries(node)) {
67
+ const p = path ? `${path}.${k}` : k;
68
+ if (k === key) return p;
69
+ if (isPlainObject(v) || Array.isArray(v)) queue.push({
70
+ node: v,
71
+ path: p
72
+ });
73
+ }
74
+ }
75
+ }
76
+ return undefined;
77
+ }
78
+
79
+ /**
80
+ * "No field X here" plus, when the field exists somewhere else, the path that
81
+ * would have worked. `leaf` is the segment the caller was ultimately after.
82
+ */
83
+ function missingFieldError(root, at, key, available, leaf) {
84
+ const elsewhere = findFieldPath(root, leaf ?? key);
85
+ const has = available.slice(0, 12).join(", ") || "(no fields)";
86
+ return elsewhere ? `No field "${key}" at "${at}" — did you mean path "${elsewhere}"? "${at}" has: ${has}.` : `No field "${key}" at "${at}". It has: ${has}.`;
87
+ }
88
+
89
+ /** Read a dotted field path inside one list item. */
90
+ function fieldOf(item, field) {
91
+ let cur = item;
92
+ for (const seg of field.split(".")) {
93
+ if (!isPlainObject(cur)) return undefined;
94
+ cur = cur[seg];
95
+ }
96
+ return cur;
97
+ }
98
+
99
+ /**
100
+ * `lines[0].addonIds`, `item.name`, `results[name=pikachu].url` → segments.
101
+ *
102
+ * Returns undefined for syntax it does not understand rather than doing
103
+ * something approximate with it.
104
+ */
105
+ function parsePath(path) {
106
+ if (!path.trim()) return undefined;
107
+ const out = [];
108
+ // Walk the string rather than splitting on ".", because a [field=value]
109
+ // selector may legitimately contain a dot ("results[url=a.b/c]").
110
+ let i = 0;
111
+ while (i < path.length) {
112
+ if (path[i] === ".") {
113
+ i++;
114
+ continue;
115
+ }
116
+ if (path[i] === "[") {
117
+ const end = path.indexOf("]", i);
118
+ if (end === -1) return undefined;
119
+ const inner = path.slice(i + 1, end);
120
+ if (/^\d+$/.test(inner)) out.push({
121
+ kind: "index",
122
+ index: Number(inner)
123
+ });else {
124
+ const eq = inner.indexOf("=");
125
+ if (eq <= 0) return undefined;
126
+ out.push({
127
+ kind: "match",
128
+ field: inner.slice(0, eq).trim(),
129
+ value: inner.slice(eq + 1).trim()
130
+ });
131
+ }
132
+ i = end + 1;
133
+ continue;
134
+ }
135
+ let j = i;
136
+ while (j < path.length && path[j] !== "." && path[j] !== "[") j++;
137
+ const key = path.slice(i, j);
138
+ if (!key) return undefined;
139
+ out.push({
140
+ kind: "key",
141
+ key
142
+ });
143
+ i = j;
144
+ }
145
+ return out.length ? out : undefined;
146
+ }
147
+
148
+ /** Read the value at `path`, or undefined if any step of it does not exist. */
149
+ function readPath(value, path) {
150
+ const segs = parsePath(path);
151
+ if (!segs) return {
152
+ ok: false,
153
+ error: `Could not read the path "${path}". Use dotted keys with optional [0] or [field=value], like "item.name" or "lines[lineId=seed-1].qty".`
154
+ };
155
+ let cur = value;
156
+ const walked = [];
157
+ for (const seg of segs) {
158
+ if (cur === null || cur === undefined) {
159
+ return {
160
+ ok: false,
161
+ error: `"${walked.join(".") || "(root)"}" is ${cur === null ? "null" : "not set"}, so there is nothing at "${path}".`
162
+ };
163
+ }
164
+ if (seg.kind === "key") {
165
+ if (!isPlainObject(cur)) return {
166
+ ok: false,
167
+ error: `"${walked.join(".") || "(root)"}" is not an object, so it has no field "${seg.key}".`
168
+ };
169
+ if (!(seg.key in cur)) {
170
+ const leafSeg = segs[segs.length - 1];
171
+ return {
172
+ ok: false,
173
+ error: missingFieldError(value, walked.join(".") || "(root)", seg.key, Object.keys(cur), leafSeg?.kind === "key" ? leafSeg.key : undefined)
174
+ };
175
+ }
176
+ cur = cur[seg.key];
177
+ walked.push(seg.key);
178
+ } else if (seg.kind === "index") {
179
+ if (!Array.isArray(cur)) return {
180
+ ok: false,
181
+ error: `"${walked.join(".") || "(root)"}" is not a list, so [${seg.index}] means nothing.`
182
+ };
183
+ cur = cur[seg.index];
184
+ walked.push(`[${seg.index}]`);
185
+ } else {
186
+ if (!Array.isArray(cur)) return {
187
+ ok: false,
188
+ error: `"${walked.join(".") || "(root)"}" is not a list, so [${seg.field}=${seg.value}] means nothing.`
189
+ };
190
+ const hit = cur.find(el => isPlainObject(el) && String(fieldOf(el, seg.field)) === seg.value);
191
+ if (hit === undefined) return {
192
+ ok: false,
193
+ error: `No item with ${seg.field}="${seg.value}" in "${walked.join(".") || "(root)"}".`
194
+ };
195
+ cur = hit;
196
+ walked.push(`[${seg.field}=${seg.value}]`);
197
+ }
198
+ }
199
+ return {
200
+ ok: true,
201
+ value: cur
202
+ };
203
+ }
204
+
205
+ /**
206
+ * Build the merge patch that sets `path` to `value`, against the data as it is
207
+ * now.
208
+ *
209
+ * The result is an ordinary merge patch, so it goes through the SAME typed-edit
210
+ * guard as a hand-written one and gains no new powers — `path` is a way to say
211
+ * the same thing without being able to get the depth wrong, not a way around
212
+ * the rules. A list step becomes the id-addressed form, so the write stays
213
+ * proof against a truncated read.
214
+ */
215
+ function patchFromPath(current, path, value) {
216
+ const segs = parsePath(path);
217
+ if (!segs) return {
218
+ ok: false,
219
+ error: `Could not read the path "${path}". Use dotted keys with optional [0] or [field=value], like "item.name" or "lines[lineId=seed-1].qty".`
220
+ };
221
+ if (segs[0]?.kind !== "key") return {
222
+ ok: false,
223
+ error: `A path must start with a field name, not a list index — "${path}" does not say which list.`
224
+ };
225
+ const build = (node, at) => {
226
+ if (at >= segs.length) return {
227
+ ok: true,
228
+ patch: value
229
+ };
230
+ const seg = segs[at];
231
+ if (seg.kind === "key") {
232
+ // A field that does not exist is left for the typed-edit guard, which owns
233
+ // that decision — EXCEPT when there are more segments to walk, because
234
+ // then the caller is describing a shape that is not there at all and the
235
+ // guard's message ("no field named `item`") would not say where the value
236
+ // it wanted actually lives.
237
+ if (isPlainObject(node) && !(seg.key in node) && at + 1 < segs.length) {
238
+ const leafSeg = segs[segs.length - 1];
239
+ return {
240
+ ok: false,
241
+ error: missingFieldError(current, pathUpTo(segs, at), seg.key, Object.keys(node), leafSeg?.kind === "key" ? leafSeg.key : undefined)
242
+ };
243
+ }
244
+ const child = isPlainObject(node) ? node[seg.key] : undefined;
245
+ const inner = build(child, at + 1);
246
+ return inner.ok ? {
247
+ ok: true,
248
+ patch: {
249
+ [seg.key]: inner.patch
250
+ }
251
+ } : inner;
252
+ }
253
+ if (!Array.isArray(node)) {
254
+ return {
255
+ ok: false,
256
+ error: `"${pathUpTo(segs, at)}" is not a list, so "${path}" cannot be resolved. Read it first — \`shape\` on the read says what each field holds.`
257
+ };
258
+ }
259
+ const index = seg.kind === "index" ? seg.index : node.findIndex(el => isPlainObject(el) && String(fieldOf(el, seg.field)) === seg.value);
260
+ const item = index >= 0 ? node[index] : undefined;
261
+ const missing = seg.kind === "index" ? index < 0 || index >= node.length : index < 0;
262
+ if (missing) {
263
+ return {
264
+ ok: false,
265
+ error: seg.kind === "index" ? `"${pathUpTo(segs, at)}" has ${node.length} item(s), so there is nothing at [${seg.index}].` : `No item with ${seg.field}="${seg.value}" in "${pathUpTo(segs, at)}".`
266
+ };
267
+ }
268
+ /**
269
+ * A list of PRIMITIVES, addressed by index — `tags[0]` on
270
+ * `["alpha","beta"]`.
271
+ *
272
+ * The guard below used to be `!isPlainObject(item)`, which folded this in
273
+ * with a genuinely out-of-range index and answered `"tags" has 2 item(s),
274
+ * so there is nothing at [0]` — a sentence that contradicts itself, for the
275
+ * commonest index there is. A model reading the data, seeing two items and
276
+ * being told [0] and [1] are both empty has nowhere left to go.
277
+ *
278
+ * There is nothing to descend into on a string, so the index IS the leaf:
279
+ * rebuild the list with that one element replaced, exactly as the no-stable-id
280
+ * branch below does. Anything deeper genuinely cannot be resolved, and says so.
281
+ */
282
+ if (!isPlainObject(item)) {
283
+ if (at < segs.length - 1) {
284
+ return {
285
+ ok: false,
286
+ error: `"${pathUpTo(segs, at)}[${seg.kind === "index" ? seg.index : `${seg.field}=${seg.value}`}]" holds ${item === null ? "null" : typeof item}, not an object, so "${path}" cannot go any deeper. Set the item itself.`
287
+ };
288
+ }
289
+ const replaced = node.slice();
290
+ replaced[index] = value;
291
+ return {
292
+ ok: true,
293
+ patch: replaced
294
+ };
295
+ }
296
+ const inner = build(item, at + 1);
297
+ if (!inner.ok) return inner;
298
+ const idField = (0, _keyedArrays.identityField)(node);
299
+ // Positions move; ids do not. Prefer the addressed form whichever way the
300
+ // caller named the row.
301
+ if (idField) return {
302
+ ok: true,
303
+ patch: {
304
+ [String(item[idField])]: inner.patch
305
+ }
306
+ };
307
+
308
+ // No stable id — a Pokémon's `stats` are `{stat: {name}, base_stat}`, with
309
+ // nothing flat and unique on the item itself, and that is an ordinary shape
310
+ // rather than a broken one. Rebuild the whole list here instead of refusing.
311
+ //
312
+ // This is safe in a way the SAME array coming from a model is not: we are
313
+ // holding the current list in memory, so nothing is being reconstructed from
314
+ // a truncated read and no row can be dropped by omission. The caller still
315
+ // only named one value. What it cannot be is proof against the list changing
316
+ // between this call and the write, which addressing by id would be — so the
317
+ // id form stays preferred wherever one exists.
318
+ const next = node.slice();
319
+ next[index] = isPlainObject(inner.patch) ? {
320
+ ...item,
321
+ ...inner.patch
322
+ } : inner.patch;
323
+ return {
324
+ ok: true,
325
+ patch: next
326
+ };
327
+ };
328
+ const built = build(current, 0);
329
+ if (!built.ok) return built;
330
+ return {
331
+ ok: true,
332
+ patch: built.patch
333
+ };
334
+ }
335
+ function pathUpTo(segs, at) {
336
+ return segs.slice(0, at).map(s => s.kind === "key" ? s.key : s.kind === "index" ? `[${s.index}]` : `[${s.field}=${s.value}]`).join(".").replace(/\.\[/g, "[") || "(root)";
337
+ }
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.listShapes = listShapes;
7
+ exports.shapeOf = shapeOf;
8
+ exports.shapeSummary = shapeSummary;
9
+ exports.shapesWereTruncated = shapesWereTruncated;
10
+ exports.unshapedLists = unshapedLists;
11
+ /**
12
+ * A compact type sketch of a runtime value — one implementation, shared by every
13
+ * adapter that hands data to a model.
14
+ *
15
+ * WHY. A model cannot write a well-formed item into a list it has never seen the
16
+ * shape of, and we were not showing it. Measured on `cart/bag-still-zustand`: the
17
+ * rendered prompt is 83,000 characters and the words `lineId`, `itemId`,
18
+ * `sizeId`, `addonIds` and `unitPrice` appear in it ZERO times. Every model
19
+ * "failing" that case invented a line shape because inventing was the only move
20
+ * available. The zustand typed-edit guard would have caught the invention — but
21
+ * only on a list that already holds an item, since it builds its template from
22
+ * `list[0]`. On an EMPTY list there is no template, the guard has nothing to
23
+ * compare against, and the invented shape is written silently.
24
+ *
25
+ * So the sketch has two jobs, and the second is the one that pays:
26
+ *
27
+ * 1. Say what the items in a list look like, so an addition can match.
28
+ * 2. Say when NOTHING knows — an empty list's item shape is not merely
29
+ * unread, it is unknowable from runtime state, and the honest answer is to
30
+ * admit that rather than let a guess through unremarked.
31
+ *
32
+ * It is deliberately tiny. A sketch of a 120-line bag is the same size as a
33
+ * sketch of a 2-line one, so unlike the data it survives the engine's 24,000-
34
+ * character result cap — which is the whole point, because the cases where the
35
+ * model most needs the shape are exactly the ones where the data got cut off.
36
+ */
37
+
38
+ const isPlainObject = v => typeof v === "object" && v !== null && !Array.isArray(v);
39
+
40
+ /** Beyond this the sketch stops being cheaper to read than the data. */
41
+ const MAX_DEPTH = 3;
42
+ const MAX_KEYS = 24;
43
+ /**
44
+ * Hard caps on `listShapes`. MAX_DEPTH bounds how DEEP it goes; nothing bounded
45
+ * how WIDE it went, and it walks every entry of every object at each permitted
46
+ * level. A store holding a few hundred keyed records therefore produced a
47
+ * "compact" sketch larger than the data it was standing in for — in a value
48
+ * that rides in every prompt. Depth is not a size budget.
49
+ */
50
+ const MAX_LIST_PATHS = 40;
51
+ const MAX_ENTRIES_VISITED = 600;
52
+
53
+ /**
54
+ * `{lineId: string, qty: number, addonIds: string[]}` — a one-line type for a
55
+ * value, built from the value itself.
56
+ *
57
+ * An array is described by its FIRST item, because a list whose items disagree
58
+ * is a list nothing can safely add to and the guard will say so anyway. An empty
59
+ * array is `unknown[]`, which is the true answer and the one that matters: it
60
+ * tells a reader that adding to this list is unchecked.
61
+ */
62
+ function shapeOf(value, depth = 0) {
63
+ if (value === null) return "null";
64
+ if (Array.isArray(value)) {
65
+ if (!value.length) return "unknown[]";
66
+ if (depth >= MAX_DEPTH) return "…[]";
67
+ const inner = shapeOf(value[0], depth + 1);
68
+ return inner.includes(" ") || inner.endsWith("]") ? `(${inner})[]` : `${inner}[]`;
69
+ }
70
+ if (isPlainObject(value)) {
71
+ if (depth >= MAX_DEPTH) return "{…}";
72
+ const keys = Object.keys(value);
73
+ const shown = keys.slice(0, MAX_KEYS);
74
+ const body = shown.map(k => `${k}: ${shapeOf(value[k], depth + 1)}`).join(", ");
75
+ const more = keys.length > shown.length ? `, +${keys.length - shown.length} more` : "";
76
+ return `{${body}${more}}`;
77
+ }
78
+ const t = typeof value;
79
+ return t === "undefined" ? "undefined" : t === "function" ? "function" : t;
80
+ }
81
+ /**
82
+ * Every list inside a value, with the shape of what goes in it.
83
+ *
84
+ * This is what a caller needs before adding to a list, and it is the part that
85
+ * cannot be recovered from a truncated read of the data itself.
86
+ *
87
+ * A list with `item: undefined` is the warning case: it is empty, so its item
88
+ * shape exists nowhere in the running app — not in the store, not in the
89
+ * persisted copy, not in this sketch. Anything added to it goes in unchecked.
90
+ */
91
+ function listShapes(value, path = "", out = [], depth = 0, budget = {
92
+ visited: 0
93
+ }) {
94
+ if (depth > MAX_DEPTH) return out;
95
+ // Two independent stops: enough lists described, or enough of the value
96
+ // walked. The second is what a wide-but-shallow store hits.
97
+ if (out.length >= MAX_LIST_PATHS || budget.visited >= MAX_ENTRIES_VISITED) return out;
98
+ if (Array.isArray(value)) {
99
+ const first = value[0];
100
+ out.push({
101
+ path: path || "(root)",
102
+ length: value.length,
103
+ ...(value.length ? {
104
+ item: shapeOf(first)
105
+ } : {})
106
+ });
107
+ // One level into the items, so `lines[].addonIds` is reachable too.
108
+ if (value.length && isPlainObject(first)) {
109
+ for (const [k, v] of Object.entries(first)) {
110
+ if (++budget.visited >= MAX_ENTRIES_VISITED) break;
111
+ listShapes(v, path ? `${path}[].${k}` : `[].${k}`, out, depth + 1, budget);
112
+ }
113
+ }
114
+ return out;
115
+ }
116
+ if (isPlainObject(value)) {
117
+ for (const [k, v] of Object.entries(value)) {
118
+ if (++budget.visited >= MAX_ENTRIES_VISITED) break;
119
+ listShapes(v, path ? `${path}.${k}` : k, out, depth + 1, budget);
120
+ }
121
+ }
122
+ return out;
123
+ }
124
+
125
+ /** True when a sketch stopped early, so a caller can say so rather than imply completeness. */
126
+ function shapesWereTruncated(value) {
127
+ const budget = {
128
+ visited: 0
129
+ };
130
+ const out = listShapes(value, "", [], 0, budget);
131
+ return out.length >= MAX_LIST_PATHS || budget.visited >= MAX_ENTRIES_VISITED;
132
+ }
133
+
134
+ /**
135
+ * The lists in a value that a caller could add to WITHOUT any way to know the
136
+ * item shape. Empty by definition on a healthy value; non-empty means a write
137
+ * into one of these paths cannot be checked by anything.
138
+ */
139
+ function unshapedLists(value) {
140
+ return listShapes(value).filter(l => l.item === undefined).map(l => l.path);
141
+ }
142
+
143
+ /**
144
+ * The sketch an adapter attaches to a read: the value's own type, plus the item
145
+ * type of every list in it.
146
+ *
147
+ * Returns undefined when there is nothing worth saying (a scalar, or an object
148
+ * holding no lists) so adapters can spread it and add no noise.
149
+ */
150
+ function shapeSummary(value) {
151
+ if (value === undefined) return undefined;
152
+ const lists = listShapes(value);
153
+ const shape = shapeOf(value);
154
+ if (!lists.length && !isPlainObject(value) && !Array.isArray(value)) return undefined;
155
+ return lists.length ? {
156
+ shape,
157
+ lists
158
+ } : {
159
+ shape
160
+ };
161
+ }
@@ -10,6 +10,7 @@ var _gameUIColors = require("../gameUI/constants/gameUIColors.js");
10
10
  var _CompactRow = require("./CompactRow.js");
11
11
  var _index = require("../../icons/index.js");
12
12
  var _RelativeTime = require("../../utils/time/RelativeTime.js");
13
+ var _fonts = require("../fonts.js");
13
14
  var _jsxRuntime = require("react/jsx-runtime");
14
15
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
15
16
  /**
@@ -102,7 +103,7 @@ const styles = _reactNative.StyleSheet.create({
102
103
  badgeText: {
103
104
  fontSize: 11,
104
105
  fontWeight: "600",
105
- fontFamily: "monospace"
106
+ fontFamily: _fonts.monoFont
106
107
  },
107
108
  iconBadge: {
108
109
  paddingHorizontal: 4
@@ -7,6 +7,7 @@ exports.ClipboardHintBanner = void 0;
7
7
  var _react = require("react");
8
8
  var _reactNative = require("react-native");
9
9
  var _index = require("../gameUI/index.js");
10
+ var _fonts = require("../fonts.js");
10
11
  var _jsxRuntime = require("react/jsx-runtime");
11
12
  /**
12
13
  * ClipboardHintBanner - A hint banner that shows users how to fix iOS Simulator clipboard sync
@@ -129,7 +130,7 @@ const styles = _reactNative.StyleSheet.create({
129
130
  textAlign: "center"
130
131
  },
131
132
  menuPath: {
132
- fontFamily: "monospace",
133
+ fontFamily: _fonts.monoFont,
133
134
  fontSize: 12,
134
135
  color: _index.gameUIColors.primary,
135
136
  fontWeight: "600"
@@ -7,6 +7,7 @@ exports.CompactRow = CompactRow;
7
7
  var _reactNative = require("react-native");
8
8
  var _index = require("../../icons/index.js");
9
9
  var _nightTheme = require("../night/nightTheme.js");
10
+ var _fonts = require("../fonts.js");
10
11
  var _jsxRuntime = require("react/jsx-runtime");
11
12
  function CompactRow({
12
13
  statusDotColor,
@@ -225,7 +226,7 @@ const styles = _reactNative.StyleSheet.create({
225
226
  paddingHorizontal: 12
226
227
  },
227
228
  queryHash: {
228
- fontFamily: "monospace",
229
+ fontFamily: _fonts.monoFont,
229
230
  fontSize: 12,
230
231
  color: _nightTheme.night.color.text,
231
232
  lineHeight: 16
@@ -269,6 +270,6 @@ const styles = _reactNative.StyleSheet.create({
269
270
  bottomRightText: {
270
271
  fontSize: 9,
271
272
  color: _nightTheme.night.color.textTertiary,
272
- fontFamily: "monospace"
273
+ fontFamily: _fonts.monoFont
273
274
  }
274
275
  });
@@ -9,6 +9,7 @@ var _reactNative = require("react-native");
9
9
  var _index = require("../../icons/index.js");
10
10
  var _index2 = require("../gameUI/index.js");
11
11
  var _CopyButton = require("./CopyButton.js");
12
+ var _fonts = require("../fonts.js");
12
13
  var _jsxRuntime = require("react/jsx-runtime");
13
14
  function DataInspector({
14
15
  data,
@@ -268,14 +269,14 @@ const styles = _reactNative.StyleSheet.create({
268
269
  maxHeight: 300
269
270
  },
270
271
  codeText: {
271
- fontFamily: "monospace",
272
+ fontFamily: _fonts.monoFont,
272
273
  fontSize: 11,
273
274
  color: _index2.gameUIColors.text,
274
275
  padding: 12,
275
276
  lineHeight: 16
276
277
  },
277
278
  editor: {
278
- fontFamily: "monospace",
279
+ fontFamily: _fonts.monoFont,
279
280
  fontSize: 11,
280
281
  color: _index2.gameUIColors.text,
281
282
  padding: 12,
@@ -9,6 +9,7 @@ var _reactNative = require("react-native");
9
9
  var _index = require("../gameUI/index.js");
10
10
  var _ModalHeader = require("./ModalHeader.js");
11
11
  var _CopyButton = require("./CopyButton.js");
12
+ var _fonts = require("../fonts.js");
12
13
  var _jsxRuntime = require("react/jsx-runtime");
13
14
  function DetailView({
14
15
  children,
@@ -297,7 +298,7 @@ const styles = _reactNative.StyleSheet.create({
297
298
  },
298
299
  dataText: {
299
300
  fontSize: 12,
300
- fontFamily: "monospace",
301
+ fontFamily: _fonts.monoFont,
301
302
  color: _index.gameUIColors.text
302
303
  },
303
304
  statusBar: {
@@ -12,6 +12,7 @@ var _FilterComponents = require("./FilterComponents.js");
12
12
  var _useFilterManager = require("../../hooks/useFilterManager.js");
13
13
  var _gameUIColors = require("../gameUI/constants/gameUIColors.js");
14
14
  var _nightTheme = require("../night/nightTheme.js");
15
+ var _fonts = require("../fonts.js");
15
16
  var _jsxRuntime = require("react/jsx-runtime");
16
17
  function DynamicFilterView({
17
18
  sections = [],
@@ -493,7 +494,7 @@ const styles = _reactNative.StyleSheet.create({
493
494
  fontSize: 14,
494
495
  fontWeight: "700",
495
496
  color: _nightTheme.night.color.text,
496
- fontFamily: "monospace",
497
+ fontFamily: _fonts.monoFont,
497
498
  marginTop: 3,
498
499
  marginBottom: 1,
499
500
  textShadowColor: "rgba(0, 0, 0, 0.2)",
@@ -507,7 +508,7 @@ const styles = _reactNative.StyleSheet.create({
507
508
  fontSize: 11,
508
509
  fontWeight: "500",
509
510
  color: _nightTheme.night.color.textTertiary,
510
- fontFamily: "monospace",
511
+ fontFamily: _fonts.monoFont,
511
512
  marginTop: 3,
512
513
  marginBottom: 1
513
514
  },
@@ -551,7 +552,7 @@ const styles = _reactNative.StyleSheet.create({
551
552
  fontSize: 13,
552
553
  fontWeight: "700",
553
554
  letterSpacing: 0.3,
554
- fontFamily: "monospace"
555
+ fontFamily: _fonts.monoFont
555
556
  },
556
557
  activeFiltersSection: {
557
558
  backgroundColor: _nightTheme.night.color.surface,
@@ -690,7 +691,7 @@ const styles = _reactNative.StyleSheet.create({
690
691
  flex: 1,
691
692
  fontSize: 11,
692
693
  color: _nightTheme.night.color.text,
693
- fontFamily: "monospace",
694
+ fontFamily: _fonts.monoFont,
694
695
  marginRight: 8
695
696
  },
696
697
  howItWorksSection: {
@@ -709,7 +710,7 @@ const styles = _reactNative.StyleSheet.create({
709
710
  marginBottom: 12,
710
711
  marginTop: 8,
711
712
  paddingHorizontal: 16,
712
- fontFamily: "monospace"
713
+ fontFamily: _fonts.monoFont
713
714
  },
714
715
  examplesContainer: {
715
716
  paddingTop: 8,
@@ -722,14 +723,14 @@ const styles = _reactNative.StyleSheet.create({
722
723
  fontSize: 10,
723
724
  fontWeight: "600",
724
725
  color: _nightTheme.night.color.textTertiary,
725
- fontFamily: "monospace",
726
+ fontFamily: _fonts.monoFont,
726
727
  letterSpacing: 0.5,
727
728
  marginBottom: 6
728
729
  },
729
730
  exampleItem: {
730
731
  fontSize: 10,
731
732
  color: _nightTheme.night.color.textTertiary,
732
- fontFamily: "monospace",
733
+ fontFamily: _fonts.monoFont,
733
734
  lineHeight: 16
734
735
  },
735
736
  previewSection: {