@buoy-gg/storage 1.7.2

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 (187) hide show
  1. package/README.md +607 -0
  2. package/lib/commonjs/index.js +34 -0
  3. package/lib/commonjs/package.json +1 -0
  4. package/lib/commonjs/preset.js +94 -0
  5. package/lib/commonjs/storage/components/DiffViewer/DiffOptionsPanel.js +356 -0
  6. package/lib/commonjs/storage/components/DiffViewer/TreeDiffViewer.js +29 -0
  7. package/lib/commonjs/storage/components/DiffViewer/components/DiffSummary.js +121 -0
  8. package/lib/commonjs/storage/components/DiffViewer/modes/ThemedSplitView.js +419 -0
  9. package/lib/commonjs/storage/components/DiffViewer/themes/diffThemes.js +122 -0
  10. package/lib/commonjs/storage/components/GameUIStorageBrowser.js +924 -0
  11. package/lib/commonjs/storage/components/GameUIStorageStats.js +746 -0
  12. package/lib/commonjs/storage/components/MMKVInstanceInfoPanel.js +257 -0
  13. package/lib/commonjs/storage/components/MMKVInstanceSelector.js +418 -0
  14. package/lib/commonjs/storage/components/SelectionActionBar.js +224 -0
  15. package/lib/commonjs/storage/components/StorageActionButtons.js +239 -0
  16. package/lib/commonjs/storage/components/StorageActions.js +192 -0
  17. package/lib/commonjs/storage/components/StorageBrowserMode.js +31 -0
  18. package/lib/commonjs/storage/components/StorageEventDetailContent.js +1025 -0
  19. package/lib/commonjs/storage/components/StorageEventFilterView.js +141 -0
  20. package/lib/commonjs/storage/components/StorageEventListener.js +357 -0
  21. package/lib/commonjs/storage/components/StorageEventsSection.js +24 -0
  22. package/lib/commonjs/storage/components/StorageFilterCards.js +345 -0
  23. package/lib/commonjs/storage/components/StorageFilterViewV2.js +42 -0
  24. package/lib/commonjs/storage/components/StorageKeyCard.js +516 -0
  25. package/lib/commonjs/storage/components/StorageKeyRow.js +356 -0
  26. package/lib/commonjs/storage/components/StorageKeySection.js +105 -0
  27. package/lib/commonjs/storage/components/StorageKeyStats.js +344 -0
  28. package/lib/commonjs/storage/components/StorageModalWithTabs.js +871 -0
  29. package/lib/commonjs/storage/components/StorageSection.js +43 -0
  30. package/lib/commonjs/storage/hooks/useAsyncStorageKeys.js +126 -0
  31. package/lib/commonjs/storage/hooks/useMMKVInstances.js +221 -0
  32. package/lib/commonjs/storage/hooks/useMMKVKeys.js +362 -0
  33. package/lib/commonjs/storage/hooks/useTickEverySecond.js +21 -0
  34. package/lib/commonjs/storage/index.js +148 -0
  35. package/lib/commonjs/storage/types.js +5 -0
  36. package/lib/commonjs/storage/utils/AsyncStorageListener.js +510 -0
  37. package/lib/commonjs/storage/utils/MMKVInstanceRegistry.js +202 -0
  38. package/lib/commonjs/storage/utils/MMKVListener.js +380 -0
  39. package/lib/commonjs/storage/utils/clearAllStorage.js +47 -0
  40. package/lib/commonjs/storage/utils/index.js +180 -0
  41. package/lib/commonjs/storage/utils/lineDiff.js +363 -0
  42. package/lib/commonjs/storage/utils/mmkvAvailability.js +62 -0
  43. package/lib/commonjs/storage/utils/mmkvTypeDetection.js +139 -0
  44. package/lib/commonjs/storage/utils/objectDiff.js +157 -0
  45. package/lib/commonjs/storage/utils/safeAsyncStorage.js +140 -0
  46. package/lib/commonjs/storage/utils/storageActionHelpers.js +46 -0
  47. package/lib/commonjs/storage/utils/storageQueryUtils.js +35 -0
  48. package/lib/commonjs/storage/utils/valueType.js +18 -0
  49. package/lib/module/index.js +7 -0
  50. package/lib/module/preset.js +89 -0
  51. package/lib/module/storage/components/DiffViewer/DiffOptionsPanel.js +352 -0
  52. package/lib/module/storage/components/DiffViewer/TreeDiffViewer.js +25 -0
  53. package/lib/module/storage/components/DiffViewer/components/DiffSummary.js +117 -0
  54. package/lib/module/storage/components/DiffViewer/modes/ThemedSplitView.js +415 -0
  55. package/lib/module/storage/components/DiffViewer/themes/diffThemes.js +118 -0
  56. package/lib/module/storage/components/GameUIStorageBrowser.js +922 -0
  57. package/lib/module/storage/components/GameUIStorageStats.js +742 -0
  58. package/lib/module/storage/components/MMKVInstanceInfoPanel.js +253 -0
  59. package/lib/module/storage/components/MMKVInstanceSelector.js +414 -0
  60. package/lib/module/storage/components/SelectionActionBar.js +221 -0
  61. package/lib/module/storage/components/StorageActionButtons.js +236 -0
  62. package/lib/module/storage/components/StorageActions.js +189 -0
  63. package/lib/module/storage/components/StorageBrowserMode.js +27 -0
  64. package/lib/module/storage/components/StorageEventDetailContent.js +1020 -0
  65. package/lib/module/storage/components/StorageEventFilterView.js +137 -0
  66. package/lib/module/storage/components/StorageEventListener.js +354 -0
  67. package/lib/module/storage/components/StorageEventsSection.js +20 -0
  68. package/lib/module/storage/components/StorageFilterCards.js +341 -0
  69. package/lib/module/storage/components/StorageFilterViewV2.js +38 -0
  70. package/lib/module/storage/components/StorageKeyCard.js +513 -0
  71. package/lib/module/storage/components/StorageKeyRow.js +353 -0
  72. package/lib/module/storage/components/StorageKeySection.js +101 -0
  73. package/lib/module/storage/components/StorageKeyStats.js +340 -0
  74. package/lib/module/storage/components/StorageModalWithTabs.js +867 -0
  75. package/lib/module/storage/components/StorageSection.js +40 -0
  76. package/lib/module/storage/hooks/useAsyncStorageKeys.js +121 -0
  77. package/lib/module/storage/hooks/useMMKVInstances.js +216 -0
  78. package/lib/module/storage/hooks/useMMKVKeys.js +359 -0
  79. package/lib/module/storage/hooks/useTickEverySecond.js +18 -0
  80. package/lib/module/storage/index.js +25 -0
  81. package/lib/module/storage/types.js +3 -0
  82. package/lib/module/storage/utils/AsyncStorageListener.js +500 -0
  83. package/lib/module/storage/utils/MMKVInstanceRegistry.js +196 -0
  84. package/lib/module/storage/utils/MMKVListener.js +367 -0
  85. package/lib/module/storage/utils/clearAllStorage.js +42 -0
  86. package/lib/module/storage/utils/index.js +22 -0
  87. package/lib/module/storage/utils/lineDiff.js +359 -0
  88. package/lib/module/storage/utils/mmkvAvailability.js +56 -0
  89. package/lib/module/storage/utils/mmkvTypeDetection.js +133 -0
  90. package/lib/module/storage/utils/objectDiff.js +153 -0
  91. package/lib/module/storage/utils/safeAsyncStorage.js +134 -0
  92. package/lib/module/storage/utils/storageActionHelpers.js +42 -0
  93. package/lib/module/storage/utils/storageQueryUtils.js +30 -0
  94. package/lib/module/storage/utils/valueType.js +14 -0
  95. package/lib/typescript/index.d.ts +3 -0
  96. package/lib/typescript/index.d.ts.map +1 -0
  97. package/lib/typescript/preset.d.ts +90 -0
  98. package/lib/typescript/preset.d.ts.map +1 -0
  99. package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts +18 -0
  100. package/lib/typescript/storage/components/DiffViewer/DiffOptionsPanel.d.ts.map +1 -0
  101. package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts +7 -0
  102. package/lib/typescript/storage/components/DiffViewer/TreeDiffViewer.d.ts.map +1 -0
  103. package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts +12 -0
  104. package/lib/typescript/storage/components/DiffViewer/components/DiffSummary.d.ts.map +1 -0
  105. package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts +13 -0
  106. package/lib/typescript/storage/components/DiffViewer/modes/ThemedSplitView.d.ts.map +1 -0
  107. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts +64 -0
  108. package/lib/typescript/storage/components/DiffViewer/themes/diffThemes.d.ts.map +1 -0
  109. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts +16 -0
  110. package/lib/typescript/storage/components/GameUIStorageBrowser.d.ts.map +1 -0
  111. package/lib/typescript/storage/components/GameUIStorageStats.d.ts +7 -0
  112. package/lib/typescript/storage/components/GameUIStorageStats.d.ts.map +1 -0
  113. package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts +42 -0
  114. package/lib/typescript/storage/components/MMKVInstanceInfoPanel.d.ts.map +1 -0
  115. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts +35 -0
  116. package/lib/typescript/storage/components/MMKVInstanceSelector.d.ts.map +1 -0
  117. package/lib/typescript/storage/components/SelectionActionBar.d.ts +21 -0
  118. package/lib/typescript/storage/components/SelectionActionBar.d.ts.map +1 -0
  119. package/lib/typescript/storage/components/StorageActionButtons.d.ts +21 -0
  120. package/lib/typescript/storage/components/StorageActionButtons.d.ts.map +1 -0
  121. package/lib/typescript/storage/components/StorageActions.d.ts +10 -0
  122. package/lib/typescript/storage/components/StorageActions.d.ts.map +1 -0
  123. package/lib/typescript/storage/components/StorageBrowserMode.d.ts +18 -0
  124. package/lib/typescript/storage/components/StorageBrowserMode.d.ts.map +1 -0
  125. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts +40 -0
  126. package/lib/typescript/storage/components/StorageEventDetailContent.d.ts.map +1 -0
  127. package/lib/typescript/storage/components/StorageEventFilterView.d.ts +11 -0
  128. package/lib/typescript/storage/components/StorageEventFilterView.d.ts.map +1 -0
  129. package/lib/typescript/storage/components/StorageEventListener.d.ts +6 -0
  130. package/lib/typescript/storage/components/StorageEventListener.d.ts.map +1 -0
  131. package/lib/typescript/storage/components/StorageEventsSection.d.ts +7 -0
  132. package/lib/typescript/storage/components/StorageEventsSection.d.ts.map +1 -0
  133. package/lib/typescript/storage/components/StorageFilterCards.d.ts +36 -0
  134. package/lib/typescript/storage/components/StorageFilterCards.d.ts.map +1 -0
  135. package/lib/typescript/storage/components/StorageFilterViewV2.d.ts +9 -0
  136. package/lib/typescript/storage/components/StorageFilterViewV2.d.ts.map +1 -0
  137. package/lib/typescript/storage/components/StorageKeyCard.d.ts +17 -0
  138. package/lib/typescript/storage/components/StorageKeyCard.d.ts.map +1 -0
  139. package/lib/typescript/storage/components/StorageKeyRow.d.ts +15 -0
  140. package/lib/typescript/storage/components/StorageKeyRow.d.ts.map +1 -0
  141. package/lib/typescript/storage/components/StorageKeySection.d.ts +25 -0
  142. package/lib/typescript/storage/components/StorageKeySection.d.ts.map +1 -0
  143. package/lib/typescript/storage/components/StorageKeyStats.d.ts +15 -0
  144. package/lib/typescript/storage/components/StorageKeyStats.d.ts.map +1 -0
  145. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts +13 -0
  146. package/lib/typescript/storage/components/StorageModalWithTabs.d.ts.map +1 -0
  147. package/lib/typescript/storage/components/StorageSection.d.ts +10 -0
  148. package/lib/typescript/storage/components/StorageSection.d.ts.map +1 -0
  149. package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts +10 -0
  150. package/lib/typescript/storage/hooks/useAsyncStorageKeys.d.ts.map +1 -0
  151. package/lib/typescript/storage/hooks/useMMKVInstances.d.ts +114 -0
  152. package/lib/typescript/storage/hooks/useMMKVInstances.d.ts.map +1 -0
  153. package/lib/typescript/storage/hooks/useMMKVKeys.d.ts +94 -0
  154. package/lib/typescript/storage/hooks/useMMKVKeys.d.ts.map +1 -0
  155. package/lib/typescript/storage/hooks/useTickEverySecond.d.ts +6 -0
  156. package/lib/typescript/storage/hooks/useTickEverySecond.d.ts.map +1 -0
  157. package/lib/typescript/storage/index.d.ts +15 -0
  158. package/lib/typescript/storage/index.d.ts.map +1 -0
  159. package/lib/typescript/storage/types.d.ts +41 -0
  160. package/lib/typescript/storage/types.d.ts.map +1 -0
  161. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts +195 -0
  162. package/lib/typescript/storage/utils/AsyncStorageListener.d.ts.map +1 -0
  163. package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts +224 -0
  164. package/lib/typescript/storage/utils/MMKVInstanceRegistry.d.ts.map +1 -0
  165. package/lib/typescript/storage/utils/MMKVListener.d.ts +218 -0
  166. package/lib/typescript/storage/utils/MMKVListener.d.ts.map +1 -0
  167. package/lib/typescript/storage/utils/clearAllStorage.d.ts +11 -0
  168. package/lib/typescript/storage/utils/clearAllStorage.d.ts.map +1 -0
  169. package/lib/typescript/storage/utils/index.d.ts +8 -0
  170. package/lib/typescript/storage/utils/index.d.ts.map +1 -0
  171. package/lib/typescript/storage/utils/lineDiff.d.ts +34 -0
  172. package/lib/typescript/storage/utils/lineDiff.d.ts.map +1 -0
  173. package/lib/typescript/storage/utils/mmkvAvailability.d.ts +23 -0
  174. package/lib/typescript/storage/utils/mmkvAvailability.d.ts.map +1 -0
  175. package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts +71 -0
  176. package/lib/typescript/storage/utils/mmkvTypeDetection.d.ts.map +1 -0
  177. package/lib/typescript/storage/utils/objectDiff.d.ts +35 -0
  178. package/lib/typescript/storage/utils/objectDiff.d.ts.map +1 -0
  179. package/lib/typescript/storage/utils/safeAsyncStorage.d.ts +56 -0
  180. package/lib/typescript/storage/utils/safeAsyncStorage.d.ts.map +1 -0
  181. package/lib/typescript/storage/utils/storageActionHelpers.d.ts +5 -0
  182. package/lib/typescript/storage/utils/storageActionHelpers.d.ts.map +1 -0
  183. package/lib/typescript/storage/utils/storageQueryUtils.d.ts +6 -0
  184. package/lib/typescript/storage/utils/storageQueryUtils.d.ts.map +1 -0
  185. package/lib/typescript/storage/utils/valueType.d.ts +3 -0
  186. package/lib/typescript/storage/utils/valueType.d.ts.map +1 -0
  187. package/package.json +68 -0
@@ -0,0 +1,359 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Line-by-line diff computation for React Native
5
+ * Adapted from react-diff-viewer for object comparison
6
+ */
7
+
8
+ export let DiffType = /*#__PURE__*/function (DiffType) {
9
+ DiffType["DEFAULT"] = "unchanged";
10
+ DiffType["ADDED"] = "added";
11
+ DiffType["REMOVED"] = "removed";
12
+ DiffType["MODIFIED"] = "modified";
13
+ return DiffType;
14
+ }({});
15
+ /**
16
+ * Compute diff based on method - for word-level diff within lines
17
+ */
18
+ function computeDiffByMethod(oldStr, newStr, method) {
19
+ let oldParts;
20
+ let newParts;
21
+ switch (method) {
22
+ case "chars":
23
+ // Split into individual characters
24
+ oldParts = oldStr.split("");
25
+ newParts = newStr.split("");
26
+ break;
27
+ case "words":
28
+ // Split by word boundaries, keeping whitespace
29
+ oldParts = oldStr.match(/\S+|\s+/g) || [];
30
+ newParts = newStr.match(/\S+|\s+/g) || [];
31
+ break;
32
+ case "trimmedLines":
33
+ {
34
+ // For trimmedLines, compare without leading/trailing whitespace
35
+ const oldTrimmed = oldStr.trim();
36
+ const newTrimmed = newStr.trim();
37
+ // But still do word-level diff on the trimmed content
38
+ oldParts = oldTrimmed.match(/\S+|\s+/g) || [];
39
+ newParts = newTrimmed.match(/\S+|\s+/g) || [];
40
+ break;
41
+ }
42
+ case "lines":
43
+ default:
44
+ // For lines mode, don't do word diff - just show the whole line
45
+ return {
46
+ left: [{
47
+ value: oldStr,
48
+ type: DiffType.REMOVED
49
+ }],
50
+ right: [{
51
+ value: newStr,
52
+ type: DiffType.ADDED
53
+ }]
54
+ };
55
+ }
56
+
57
+ // Simple LCS-like algorithm for better diff
58
+ const left = [];
59
+ const right = [];
60
+ let i = 0,
61
+ j = 0;
62
+
63
+ // Find matching parts
64
+ while (i < oldParts.length && j < newParts.length) {
65
+ if (oldParts[i] === newParts[j]) {
66
+ // Parts match
67
+ left.push({
68
+ value: oldParts[i],
69
+ type: DiffType.DEFAULT
70
+ });
71
+ right.push({
72
+ value: newParts[j],
73
+ type: DiffType.DEFAULT
74
+ });
75
+ i++;
76
+ j++;
77
+ } else {
78
+ // Look ahead for matches
79
+ let foundMatch = false;
80
+
81
+ // Check if we can find newParts[j] in upcoming oldParts
82
+ for (let k = i + 1; k < Math.min(i + 5, oldParts.length); k++) {
83
+ if (oldParts[k] === newParts[j]) {
84
+ // Mark everything from i to k-1 as removed
85
+ for (let m = i; m < k; m++) {
86
+ left.push({
87
+ value: oldParts[m],
88
+ type: DiffType.REMOVED
89
+ });
90
+ }
91
+ i = k;
92
+ foundMatch = true;
93
+ break;
94
+ }
95
+ }
96
+ if (!foundMatch) {
97
+ // Check if we can find oldParts[i] in upcoming newParts
98
+ for (let k = j + 1; k < Math.min(j + 5, newParts.length); k++) {
99
+ if (newParts[k] === oldParts[i]) {
100
+ // Mark everything from j to k-1 as added
101
+ for (let m = j; m < k; m++) {
102
+ right.push({
103
+ value: newParts[m],
104
+ type: DiffType.ADDED
105
+ });
106
+ }
107
+ j = k;
108
+ foundMatch = true;
109
+ break;
110
+ }
111
+ }
112
+ }
113
+ if (!foundMatch) {
114
+ // No match found nearby, mark as changed
115
+ left.push({
116
+ value: oldParts[i],
117
+ type: DiffType.REMOVED
118
+ });
119
+ right.push({
120
+ value: newParts[j],
121
+ type: DiffType.ADDED
122
+ });
123
+ i++;
124
+ j++;
125
+ }
126
+ }
127
+ }
128
+
129
+ // Handle remaining parts
130
+ while (i < oldParts.length) {
131
+ left.push({
132
+ value: oldParts[i],
133
+ type: DiffType.REMOVED
134
+ });
135
+ i++;
136
+ }
137
+ while (j < newParts.length) {
138
+ right.push({
139
+ value: newParts[j],
140
+ type: DiffType.ADDED
141
+ });
142
+ j++;
143
+ }
144
+ return {
145
+ left,
146
+ right
147
+ };
148
+ }
149
+
150
+ /**
151
+ * Convert object to formatted JSON lines
152
+ */
153
+ function objectToLines(obj) {
154
+ if (obj === null || obj === undefined) {
155
+ return [String(obj)];
156
+ }
157
+ try {
158
+ // Pretty print JSON with 2 space indentation
159
+ const jsonStr = JSON.stringify(obj, null, 2);
160
+ // Split into lines and filter out empty lines that appear between array elements
161
+ const lines = jsonStr.split("\n");
162
+
163
+ // Remove empty lines but keep structure
164
+ const filteredLines = [];
165
+ for (let i = 0; i < lines.length; i++) {
166
+ const line = lines[i];
167
+ const trimmed = line.trim();
168
+
169
+ // Keep the line if it's not empty or if it's meaningful whitespace
170
+ if (trimmed !== "") {
171
+ filteredLines.push(line);
172
+ } else if (i === 0 || i === lines.length - 1) {
173
+ // Keep first and last empty lines if they exist (though they shouldn't)
174
+ filteredLines.push(line);
175
+ }
176
+ // Skip other empty lines
177
+ }
178
+ return filteredLines;
179
+ } catch {
180
+ return [String(obj)];
181
+ }
182
+ }
183
+ /**
184
+ * Apply showDiffOnly filter with context lines
185
+ */
186
+ function filterDiffWithContext(diffs, contextLines) {
187
+ if (contextLines < 0) return diffs;
188
+ const result = [];
189
+ const changedIndices = [];
190
+
191
+ // Find all changed lines
192
+ diffs.forEach((diff, idx) => {
193
+ if (diff.type !== DiffType.DEFAULT) {
194
+ changedIndices.push(idx);
195
+ }
196
+ });
197
+
198
+ // If no changes, return empty
199
+ if (changedIndices.length === 0) {
200
+ return [];
201
+ }
202
+
203
+ // Build ranges to include
204
+ const ranges = [];
205
+ let currentStart = Math.max(0, changedIndices[0] - contextLines);
206
+ let currentEnd = Math.min(diffs.length - 1, changedIndices[0] + contextLines);
207
+ for (let i = 1; i < changedIndices.length; i++) {
208
+ const idx = changedIndices[i];
209
+ const rangeStart = Math.max(0, idx - contextLines);
210
+ const rangeEnd = Math.min(diffs.length - 1, idx + contextLines);
211
+
212
+ // If ranges overlap or are adjacent, merge them
213
+ if (rangeStart <= currentEnd + 1) {
214
+ currentEnd = Math.max(currentEnd, rangeEnd);
215
+ } else {
216
+ // Save current range and start a new one
217
+ ranges.push([currentStart, currentEnd]);
218
+ currentStart = rangeStart;
219
+ currentEnd = rangeEnd;
220
+ }
221
+ }
222
+
223
+ // Don't forget the last range
224
+ ranges.push([currentStart, currentEnd]);
225
+
226
+ // Build result from ranges
227
+ ranges.forEach(([start, end]) => {
228
+ for (let i = start; i <= end; i++) {
229
+ result.push(diffs[i]);
230
+ }
231
+ });
232
+ return result;
233
+ }
234
+
235
+ /**
236
+ * Compare lines based on method
237
+ */
238
+ function compareLinesWithMethod(line1, line2, method) {
239
+ switch (method) {
240
+ case "trimmedLines":
241
+ return line1.trim() === line2.trim();
242
+ case "chars":
243
+ case "words":
244
+ case "lines":
245
+ default:
246
+ return line1 === line2;
247
+ }
248
+ }
249
+
250
+ /**
251
+ * Compute line-by-line diff between two objects
252
+ */
253
+ export function computeLineDiff(oldValue, newValue, options = {}) {
254
+ const {
255
+ compareMethod = "words",
256
+ disableWordDiff = false,
257
+ showDiffOnly = false,
258
+ contextLines = 3
259
+ } = options;
260
+ const oldLines = objectToLines(oldValue);
261
+ const newLines = objectToLines(newValue);
262
+ const result = [];
263
+ let leftLineNum = 1;
264
+ let rightLineNum = 1;
265
+
266
+ // Simple line diff algorithm (can be improved with LCS)
267
+ let i = 0,
268
+ j = 0;
269
+ while (i < oldLines.length || j < newLines.length) {
270
+ if (i >= oldLines.length) {
271
+ // Rest are additions
272
+ result.push({
273
+ rightLineNumber: rightLineNum++,
274
+ type: DiffType.ADDED,
275
+ rightContent: newLines[j],
276
+ rightRaw: newLines[j]
277
+ });
278
+ j++;
279
+ } else if (j >= newLines.length) {
280
+ // Rest are removals
281
+ result.push({
282
+ leftLineNumber: leftLineNum++,
283
+ type: DiffType.REMOVED,
284
+ leftContent: oldLines[i],
285
+ leftRaw: oldLines[i]
286
+ });
287
+ i++;
288
+ } else if (compareLinesWithMethod(oldLines[i], newLines[j], compareMethod === "trimmedLines" ? "trimmedLines" : "lines")) {
289
+ // Lines match (possibly after trimming if trimmedLines)
290
+ result.push({
291
+ leftLineNumber: leftLineNum++,
292
+ rightLineNumber: rightLineNum++,
293
+ type: DiffType.DEFAULT,
294
+ leftContent: oldLines[i],
295
+ rightContent: newLines[j],
296
+ leftRaw: oldLines[i],
297
+ rightRaw: newLines[j]
298
+ });
299
+ i++;
300
+ j++;
301
+ } else {
302
+ // Lines differ - check if it's a modification or separate add/remove
303
+ const oldTrimmed = oldLines[i].trim();
304
+ const newTrimmed = newLines[j].trim();
305
+
306
+ // Simple heuristic: if lines start similarly, treat as modification
307
+ if (oldTrimmed && newTrimmed && (oldTrimmed.startsWith(newTrimmed.substring(0, 3)) || newTrimmed.startsWith(oldTrimmed.substring(0, 3)))) {
308
+ // Treat as modification - compute word diff if enabled
309
+ if (!disableWordDiff && compareMethod !== "lines") {
310
+ const wordDiff = computeDiffByMethod(oldLines[i], newLines[j], compareMethod);
311
+ result.push({
312
+ leftLineNumber: leftLineNum++,
313
+ rightLineNumber: rightLineNum++,
314
+ type: DiffType.MODIFIED,
315
+ leftContent: wordDiff.left,
316
+ rightContent: wordDiff.right,
317
+ leftRaw: oldLines[i],
318
+ rightRaw: newLines[j]
319
+ });
320
+ } else {
321
+ // No word diff - just mark lines as different
322
+ result.push({
323
+ leftLineNumber: leftLineNum++,
324
+ rightLineNumber: rightLineNum++,
325
+ type: DiffType.MODIFIED,
326
+ leftContent: oldLines[i],
327
+ rightContent: newLines[j],
328
+ leftRaw: oldLines[i],
329
+ rightRaw: newLines[j]
330
+ });
331
+ }
332
+ i++;
333
+ j++;
334
+ } else {
335
+ // Treat as separate remove and add
336
+ result.push({
337
+ leftLineNumber: leftLineNum++,
338
+ type: DiffType.REMOVED,
339
+ leftContent: oldLines[i],
340
+ leftRaw: oldLines[i]
341
+ });
342
+ result.push({
343
+ rightLineNumber: rightLineNum++,
344
+ type: DiffType.ADDED,
345
+ rightContent: newLines[j],
346
+ rightRaw: newLines[j]
347
+ });
348
+ i++;
349
+ j++;
350
+ }
351
+ }
352
+ }
353
+
354
+ // Apply showDiffOnly filter if enabled
355
+ if (showDiffOnly) {
356
+ return filterDiffWithContext(result, contextLines);
357
+ }
358
+ return result;
359
+ }
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * MMKV Availability Detection
5
+ *
6
+ * Safely detects if react-native-mmkv is available in the current environment.
7
+ * This allows the app to work in Expo Go where native modules aren't available.
8
+ */
9
+
10
+ let _isMMKVAvailable = null;
11
+ let _MMKVClass = null;
12
+
13
+ /**
14
+ * Check if MMKV is available in the current environment
15
+ *
16
+ * @returns True if react-native-mmkv is installed and available
17
+ */
18
+ export function isMMKVAvailable() {
19
+ // Cache the result after first check
20
+ if (_isMMKVAvailable !== null) {
21
+ return _isMMKVAvailable;
22
+ }
23
+ try {
24
+ // Attempt to require MMKV
25
+ const MMKVModule = require('react-native-mmkv');
26
+
27
+ // v4 uses createMMKV factory function, v3 used MMKV class
28
+ // Try v4 API first (recommended), fall back to v3
29
+ _MMKVClass = MMKVModule.createMMKV || MMKVModule.MMKV;
30
+ _isMMKVAvailable = _MMKVClass !== undefined;
31
+ return _isMMKVAvailable;
32
+ } catch (error) {
33
+ // MMKV is not available (e.g., Expo Go, not installed)
34
+ _isMMKVAvailable = false;
35
+ return false;
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Get the MMKV class if available
41
+ *
42
+ * @returns MMKV class or null if not available
43
+ */
44
+ export function getMMKVClass() {
45
+ if (!isMMKVAvailable()) {
46
+ return null;
47
+ }
48
+ return _MMKVClass;
49
+ }
50
+
51
+ /**
52
+ * Get a user-friendly error message when MMKV is not available
53
+ */
54
+ export function getMMKVUnavailableMessage() {
55
+ return 'MMKV is not available in this environment. MMKV requires native modules and cannot run in Expo Go. Please use a development build or EAS Build.';
56
+ }
@@ -0,0 +1,133 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * MMKV Type Detection Utilities
5
+ *
6
+ * MMKV stores values with native types (string, number, boolean, ArrayBuffer).
7
+ * This module provides utilities to detect the type of a stored value by trying
8
+ * each getter sequentially.
9
+ */
10
+
11
+ // Use 'any' type for MMKV to avoid hard dependency on react-native-mmkv
12
+
13
+ /**
14
+ * Detect the type of a value stored in MMKV by trying each getter.
15
+ *
16
+ * Since MMKV doesn't provide a getType() method, we must try each getter
17
+ * (getString, getNumber, getBoolean, getBuffer) until one returns a value.
18
+ *
19
+ * Order matters: We try in order of most common types first.
20
+ *
21
+ * @param instance - MMKV instance to read from
22
+ * @param key - Storage key
23
+ * @returns Value and its detected type
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const instance = createMMKV();
28
+ * instance.set('name', 'John');
29
+ * instance.set('age', 30);
30
+ * instance.set('active', true);
31
+ *
32
+ * detectMMKVType(instance, 'name'); // { value: 'John', type: 'string' }
33
+ * detectMMKVType(instance, 'age'); // { value: 30, type: 'number' }
34
+ * detectMMKVType(instance, 'active'); // { value: true, type: 'boolean' }
35
+ * detectMMKVType(instance, 'missing');// { value: undefined, type: 'unknown' }
36
+ * ```
37
+ */
38
+ export function detectMMKVType(instance, key) {
39
+ // Try string (most common)
40
+ const stringValue = instance.getString(key);
41
+ if (stringValue !== undefined) {
42
+ return {
43
+ value: stringValue,
44
+ type: 'string'
45
+ };
46
+ }
47
+
48
+ // Try number
49
+ const numberValue = instance.getNumber(key);
50
+ if (numberValue !== undefined) {
51
+ return {
52
+ value: numberValue,
53
+ type: 'number'
54
+ };
55
+ }
56
+
57
+ // Try boolean
58
+ const booleanValue = instance.getBoolean(key);
59
+ if (booleanValue !== undefined) {
60
+ return {
61
+ value: booleanValue,
62
+ type: 'boolean'
63
+ };
64
+ }
65
+
66
+ // Try buffer (least common)
67
+ const bufferValue = instance.getBuffer(key);
68
+ if (bufferValue !== undefined) {
69
+ // Format buffer for display (don't return raw ArrayBuffer)
70
+ return {
71
+ value: `<ArrayBuffer ${bufferValue.byteLength} bytes>`,
72
+ type: 'buffer'
73
+ };
74
+ }
75
+
76
+ // Key doesn't exist or unknown type
77
+ return {
78
+ value: undefined,
79
+ type: 'unknown'
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Format an MMKV value for display in the UI.
85
+ *
86
+ * @param value - The value to format
87
+ * @param type - The detected type
88
+ * @returns Formatted string representation
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * formatMMKVValue('hello', 'string'); // '"hello"'
93
+ * formatMMKVValue(42, 'number'); // '42'
94
+ * formatMMKVValue(true, 'boolean'); // 'true'
95
+ * formatMMKVValue('<ArrayBuffer 1024 bytes>', 'buffer'); // '<ArrayBuffer 1024 bytes>'
96
+ * ```
97
+ */
98
+ export function formatMMKVValue(value, type) {
99
+ if (value === undefined) return 'undefined';
100
+ if (value === null) return 'null';
101
+ switch (type) {
102
+ case 'string':
103
+ return `"${value}"`;
104
+ case 'number':
105
+ return String(value);
106
+ case 'boolean':
107
+ return value ? 'true' : 'false';
108
+ case 'buffer':
109
+ // Value is already formatted as "<ArrayBuffer X bytes>"
110
+ return value;
111
+ case 'unknown':
112
+ return 'unknown';
113
+ default:
114
+ return String(value);
115
+ }
116
+ }
117
+
118
+ /**
119
+ * Check if a value type matches an expected type for validation.
120
+ *
121
+ * @param actualType - Detected type
122
+ * @param expectedType - Expected type from configuration
123
+ * @returns True if types match
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * isTypeMatch('string', 'string'); // true
128
+ * isTypeMatch('number', 'string'); // false
129
+ * ```
130
+ */
131
+ export function isTypeMatch(actualType, expectedType) {
132
+ return actualType === expectedType;
133
+ }
@@ -0,0 +1,153 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * Type guard to check if a value is a plain object (not array or null)
5
+ *
6
+ * @param obj - Value to check
7
+ * @returns True if the value is a plain object
8
+ */
9
+ function isObject(obj) {
10
+ return obj !== null && typeof obj === "object" && !Array.isArray(obj);
11
+ }
12
+
13
+ /**
14
+ * Type guard to check if a value is an array
15
+ *
16
+ * @param obj - Value to check
17
+ * @returns True if the value is an array
18
+ */
19
+ function isArray(obj) {
20
+ return Array.isArray(obj);
21
+ }
22
+
23
+ /**
24
+ * Recursively compare two values and collect differences
25
+ *
26
+ * This function handles deep comparison of objects, arrays, and primitive values,
27
+ * building a comprehensive diff that tracks the exact path of each change.
28
+ *
29
+ * @param oldVal - The original value to compare from
30
+ * @param newVal - The new value to compare to
31
+ * @param path - Current path in the object structure (for nested properties)
32
+ * @param diffs - Array to collect difference items
33
+ *
34
+ * @performance Uses recursive traversal with path tracking for memory efficiency
35
+ * @performance Handles large nested structures without stack overflow concerns for typical use cases
36
+ */
37
+ function compareValues(oldVal, newVal, path, diffs) {
38
+ // Both are objects
39
+ if (isObject(oldVal) && isObject(newVal)) {
40
+ const allKeys = new Set([...Object.keys(oldVal), ...Object.keys(newVal)]);
41
+ for (const key of allKeys) {
42
+ const newPath = [...path, key];
43
+ if (!(key in oldVal)) {
44
+ // Key was added
45
+ diffs.push({
46
+ type: "CREATE",
47
+ path: newPath,
48
+ value: newVal[key]
49
+ });
50
+ } else if (!(key in newVal)) {
51
+ // Key was removed
52
+ diffs.push({
53
+ type: "REMOVE",
54
+ path: newPath,
55
+ oldValue: oldVal[key]
56
+ });
57
+ } else {
58
+ // Key exists in both, compare values
59
+ compareValues(oldVal[key], newVal[key], newPath, diffs);
60
+ }
61
+ }
62
+ }
63
+ // Both are arrays
64
+ else if (isArray(oldVal) && isArray(newVal)) {
65
+ const maxLength = Math.max(oldVal.length, newVal.length);
66
+ for (let i = 0; i < maxLength; i++) {
67
+ const newPath = [...path, i];
68
+ if (i >= oldVal.length) {
69
+ // Item was added
70
+ diffs.push({
71
+ type: "CREATE",
72
+ path: newPath,
73
+ value: newVal[i]
74
+ });
75
+ } else if (i >= newVal.length) {
76
+ // Item was removed
77
+ diffs.push({
78
+ type: "REMOVE",
79
+ path: newPath,
80
+ oldValue: oldVal[i]
81
+ });
82
+ } else {
83
+ // Item exists in both, compare values
84
+ compareValues(oldVal[i], newVal[i], newPath, diffs);
85
+ }
86
+ }
87
+ }
88
+ // Values are different types or primitives
89
+ else if (oldVal !== newVal) {
90
+ diffs.push({
91
+ type: "CHANGE",
92
+ path,
93
+ oldValue: oldVal,
94
+ value: newVal
95
+ });
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Generate a comprehensive diff between two objects or values
101
+ *
102
+ * This function performs a deep comparison between two values and returns
103
+ * a detailed list of all differences, including additions, removals, and changes.
104
+ * Each difference includes the exact path where the change occurred.
105
+ *
106
+ * @param oldObj - The original object/value to compare from
107
+ * @param newObj - The new object/value to compare to
108
+ * @returns Array of DiffItem objects describing all differences
109
+ *
110
+ * @example
111
+ * ```typescript
112
+ * const oldData = { user: { name: "John", age: 30 }, items: [1, 2] };
113
+ * const newData = { user: { name: "Jane", age: 30 }, items: [1, 2, 3] };
114
+ *
115
+ * const diffs = objectDiff(oldData, newData);
116
+ * // Returns:
117
+ * // [
118
+ * // { type: "CHANGE", path: ["user", "name"], oldValue: "John", value: "Jane" },
119
+ * // { type: "CREATE", path: ["items", 2], value: 3 }
120
+ * // ]
121
+ * ```
122
+ *
123
+ * @performance Handles circular references and deep nesting efficiently
124
+ * @performance Uses Set for key deduplication to optimize comparison speed
125
+ */
126
+ export function objectDiff(oldObj, newObj) {
127
+ const diffs = [];
128
+
129
+ // Handle null/undefined cases
130
+ if (oldObj === newObj) {
131
+ return diffs;
132
+ }
133
+ if (oldObj === null || oldObj === undefined) {
134
+ if (newObj !== null && newObj !== undefined) {
135
+ diffs.push({
136
+ type: "CREATE",
137
+ path: [],
138
+ value: newObj
139
+ });
140
+ }
141
+ return diffs;
142
+ }
143
+ if (newObj === null || newObj === undefined) {
144
+ diffs.push({
145
+ type: "REMOVE",
146
+ path: [],
147
+ oldValue: oldObj
148
+ });
149
+ return diffs;
150
+ }
151
+ compareValues(oldObj, newObj, [], diffs);
152
+ return diffs;
153
+ }