@optimizely/ocp-local-env 1.0.0-beta.4

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 (160) hide show
  1. package/README.md +165 -0
  2. package/dist/package.json +104 -0
  3. package/dist/public/bundle.da978bb5437cd82e6d37.js +3 -0
  4. package/dist/public/bundle.da978bb5437cd82e6d37.js.LICENSE.txt +49 -0
  5. package/dist/public/bundle.da978bb5437cd82e6d37.js.map +1 -0
  6. package/dist/public/index.html +1 -0
  7. package/dist/src/cli.d.ts +2 -0
  8. package/dist/src/cli.js +88 -0
  9. package/dist/src/cli.js.map +1 -0
  10. package/dist/src/executor/FunctionExecutor.d.ts +56 -0
  11. package/dist/src/executor/FunctionExecutor.js +175 -0
  12. package/dist/src/executor/FunctionExecutor.js.map +1 -0
  13. package/dist/src/executor/JobExecutor.d.ts +60 -0
  14. package/dist/src/executor/JobExecutor.js +203 -0
  15. package/dist/src/executor/JobExecutor.js.map +1 -0
  16. package/dist/src/executor/LifecycleExecutor.d.ts +45 -0
  17. package/dist/src/executor/LifecycleExecutor.js +153 -0
  18. package/dist/src/executor/LifecycleExecutor.js.map +1 -0
  19. package/dist/src/executor/watcher.d.ts +63 -0
  20. package/dist/src/executor/watcher.js +213 -0
  21. package/dist/src/executor/watcher.js.map +1 -0
  22. package/dist/src/functions/hello.d.ts +4 -0
  23. package/dist/src/functions/hello.js +8 -0
  24. package/dist/src/functions/hello.js.map +1 -0
  25. package/dist/src/index.d.ts +1 -0
  26. package/dist/src/index.js +9 -0
  27. package/dist/src/index.js.map +1 -0
  28. package/dist/src/jobs/dailyJob.d.ts +4 -0
  29. package/dist/src/jobs/dailyJob.js +8 -0
  30. package/dist/src/jobs/dailyJob.js.map +1 -0
  31. package/dist/src/local_engine/LocalNotifier.d.ts +10 -0
  32. package/dist/src/local_engine/LocalNotifier.js +26 -0
  33. package/dist/src/local_engine/LocalNotifier.js.map +1 -0
  34. package/dist/src/local_engine/local-engine-child-base.d.ts +79 -0
  35. package/dist/src/local_engine/local-engine-child-base.js +304 -0
  36. package/dist/src/local_engine/local-engine-child-base.js.map +1 -0
  37. package/dist/src/local_engine/local-engine-client.d.ts +80 -0
  38. package/dist/src/local_engine/local-engine-client.js +333 -0
  39. package/dist/src/local_engine/local-engine-client.js.map +1 -0
  40. package/dist/src/local_engine/local-engine-types.d.ts +132 -0
  41. package/dist/src/local_engine/local-engine-types.js +6 -0
  42. package/dist/src/local_engine/local-engine-types.js.map +1 -0
  43. package/dist/src/local_engine/local-engine-unified.d.ts +40 -0
  44. package/dist/src/local_engine/local-engine-unified.js +406 -0
  45. package/dist/src/local_engine/local-engine-unified.js.map +1 -0
  46. package/dist/src/local_engine/local-engine-utils.d.ts +70 -0
  47. package/dist/src/local_engine/local-engine-utils.js +192 -0
  48. package/dist/src/local_engine/local-engine-utils.js.map +1 -0
  49. package/dist/src/local_engine/localSDKConfig.d.ts +30 -0
  50. package/dist/src/local_engine/localSDKConfig.js +392 -0
  51. package/dist/src/local_engine/localSDKConfig.js.map +1 -0
  52. package/dist/src/local_engine/storage/LocalConfigStore.d.ts +56 -0
  53. package/dist/src/local_engine/storage/LocalConfigStore.js +129 -0
  54. package/dist/src/local_engine/storage/LocalConfigStore.js.map +1 -0
  55. package/dist/src/local_engine/storage/LocalJobStore.d.ts +110 -0
  56. package/dist/src/local_engine/storage/LocalJobStore.js +239 -0
  57. package/dist/src/local_engine/storage/LocalJobStore.js.map +1 -0
  58. package/dist/src/local_engine/storage/LocalKVStore.d.ts +105 -0
  59. package/dist/src/local_engine/storage/LocalKVStore.js +1002 -0
  60. package/dist/src/local_engine/storage/LocalKVStore.js.map +1 -0
  61. package/dist/src/local_engine/storage/LocalNotificationStore.d.ts +27 -0
  62. package/dist/src/local_engine/storage/LocalNotificationStore.js +125 -0
  63. package/dist/src/local_engine/storage/LocalNotificationStore.js.map +1 -0
  64. package/dist/src/local_engine/storage/LocalSecretsStore.d.ts +114 -0
  65. package/dist/src/local_engine/storage/LocalSecretsStore.js +319 -0
  66. package/dist/src/local_engine/storage/LocalSecretsStore.js.map +1 -0
  67. package/dist/src/local_engine/storage/LocalSettingsStore.d.ts +161 -0
  68. package/dist/src/local_engine/storage/LocalSettingsStore.js +417 -0
  69. package/dist/src/local_engine/storage/LocalSettingsStore.js.map +1 -0
  70. package/dist/src/local_engine/storage/NumberSet.d.ts +21 -0
  71. package/dist/src/local_engine/storage/NumberSet.js +32 -0
  72. package/dist/src/local_engine/storage/NumberSet.js.map +1 -0
  73. package/dist/src/local_engine/storage/StringSet.d.ts +21 -0
  74. package/dist/src/local_engine/storage/StringSet.js +32 -0
  75. package/dist/src/local_engine/storage/StringSet.js.map +1 -0
  76. package/dist/src/local_engine/types.d.ts +52 -0
  77. package/dist/src/local_engine/types.js +6 -0
  78. package/dist/src/local_engine/types.js.map +1 -0
  79. package/dist/src/local_engine/utils.d.ts +31 -0
  80. package/dist/src/local_engine/utils.js +126 -0
  81. package/dist/src/local_engine/utils.js.map +1 -0
  82. package/dist/src/logging/LogManager.d.ts +89 -0
  83. package/dist/src/logging/LogManager.js +237 -0
  84. package/dist/src/logging/LogManager.js.map +1 -0
  85. package/dist/src/server/api/functions.d.ts +7 -0
  86. package/dist/src/server/api/functions.js +80 -0
  87. package/dist/src/server/api/functions.js.map +1 -0
  88. package/dist/src/server/api/jobs.d.ts +8 -0
  89. package/dist/src/server/api/jobs.js +242 -0
  90. package/dist/src/server/api/jobs.js.map +1 -0
  91. package/dist/src/server/api/lifecycle.d.ts +6 -0
  92. package/dist/src/server/api/lifecycle.js +73 -0
  93. package/dist/src/server/api/lifecycle.js.map +1 -0
  94. package/dist/src/server/api/settings.d.ts +6 -0
  95. package/dist/src/server/api/settings.js +117 -0
  96. package/dist/src/server/api/settings.js.map +1 -0
  97. package/dist/src/server/api/stores.d.ts +2 -0
  98. package/dist/src/server/api/stores.js +341 -0
  99. package/dist/src/server/api/stores.js.map +1 -0
  100. package/dist/src/server/api/v1.d.ts +10 -0
  101. package/dist/src/server/api/v1.js +711 -0
  102. package/dist/src/server/api/v1.js.map +1 -0
  103. package/dist/src/server/api.d.ts +8 -0
  104. package/dist/src/server/api.js +154 -0
  105. package/dist/src/server/api.js.map +1 -0
  106. package/dist/src/server/app-discovery.d.ts +5 -0
  107. package/dist/src/server/app-discovery.js +81 -0
  108. package/dist/src/server/app-discovery.js.map +1 -0
  109. package/dist/src/server/config.d.ts +21 -0
  110. package/dist/src/server/config.js +100 -0
  111. package/dist/src/server/config.js.map +1 -0
  112. package/dist/src/server/websocket.d.ts +0 -0
  113. package/dist/src/server/websocket.js +2 -0
  114. package/dist/src/server/websocket.js.map +1 -0
  115. package/dist/src/server.d.ts +2 -0
  116. package/dist/src/server.js +546 -0
  117. package/dist/src/server.js.map +1 -0
  118. package/dist/src/types/app.d.ts +155 -0
  119. package/dist/src/types/app.js +24 -0
  120. package/dist/src/types/app.js.map +1 -0
  121. package/dist/src/types/kvstore.d.ts +320 -0
  122. package/dist/src/types/kvstore.js +5 -0
  123. package/dist/src/types/kvstore.js.map +1 -0
  124. package/dist/src/ui/components/App.d.ts +6 -0
  125. package/dist/src/ui/components/App.js +255 -0
  126. package/dist/src/ui/components/App.js.map +1 -0
  127. package/dist/src/ui/components/FunctionsView.d.ts +6 -0
  128. package/dist/src/ui/components/FunctionsView.js +217 -0
  129. package/dist/src/ui/components/FunctionsView.js.map +1 -0
  130. package/dist/src/ui/components/JobsView.d.ts +6 -0
  131. package/dist/src/ui/components/JobsView.js +257 -0
  132. package/dist/src/ui/components/JobsView.js.map +1 -0
  133. package/dist/src/ui/components/KVStoreViewer.d.ts +11 -0
  134. package/dist/src/ui/components/KVStoreViewer.js +168 -0
  135. package/dist/src/ui/components/KVStoreViewer.js.map +1 -0
  136. package/dist/src/ui/components/NotificationViewer.d.ts +16 -0
  137. package/dist/src/ui/components/NotificationViewer.js +69 -0
  138. package/dist/src/ui/components/NotificationViewer.js.map +1 -0
  139. package/dist/src/ui/components/SecretsStoreViewer.d.ts +11 -0
  140. package/dist/src/ui/components/SecretsStoreViewer.js +179 -0
  141. package/dist/src/ui/components/SecretsStoreViewer.js.map +1 -0
  142. package/dist/src/ui/components/SettingsStoreViewer.d.ts +24 -0
  143. package/dist/src/ui/components/SettingsStoreViewer.js +132 -0
  144. package/dist/src/ui/components/SettingsStoreViewer.js.map +1 -0
  145. package/dist/src/ui/components/StoreViewer.d.ts +16 -0
  146. package/dist/src/ui/components/StoreViewer.js +86 -0
  147. package/dist/src/ui/components/StoreViewer.js.map +1 -0
  148. package/dist/src/ui/components/TabbedConsole.d.ts +15 -0
  149. package/dist/src/ui/components/TabbedConsole.js +93 -0
  150. package/dist/src/ui/components/TabbedConsole.js.map +1 -0
  151. package/dist/src/ui/components/common/DataTree.d.ts +15 -0
  152. package/dist/src/ui/components/common/DataTree.js +95 -0
  153. package/dist/src/ui/components/common/DataTree.js.map +1 -0
  154. package/dist/src/ui/components/common/EyeIcon.d.ts +11 -0
  155. package/dist/src/ui/components/common/EyeIcon.js +11 -0
  156. package/dist/src/ui/components/common/EyeIcon.js.map +1 -0
  157. package/dist/src/ui/index.d.ts +1 -0
  158. package/dist/src/ui/index.js +20 -0
  159. package/dist/src/ui/index.js.map +1 -0
  160. package/package.json +104 -0
@@ -0,0 +1,1002 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.LocalKVStore = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const NumberSet_1 = require("./NumberSet");
10
+ const StringSet_1 = require("./StringSet");
11
+ /**
12
+ * Local implementation of KVStore that persists data to disk
13
+ * Follows the pattern of LocalSettingsStore with singleton per app directory
14
+ */
15
+ class LocalKVStore {
16
+ constructor(appRootDir, storeFileName = "kvstore.json") {
17
+ this.appRootDir = appRootDir;
18
+ this.storeFileName = storeFileName;
19
+ this.configDirPath = path_1.default.join(appRootDir, ".ocp-local");
20
+ this.kvStorePath = path_1.default.join(this.configDirPath, storeFileName);
21
+ // Initialize store file if it doesn't exist
22
+ this.ensureStoreFileExists();
23
+ }
24
+ /**
25
+ * Get or create a singleton instance for the given app directory
26
+ */
27
+ static getInstance(appRootDir, storeFileName) {
28
+ const normalizedPath = path_1.default.resolve(appRootDir);
29
+ const instanceKey = `${normalizedPath}:${storeFileName || "kvstore.json"}`;
30
+ if (!this.instances.has(instanceKey)) {
31
+ this.instances.set(instanceKey, new LocalKVStore(normalizedPath, storeFileName));
32
+ }
33
+ return this.instances.get(instanceKey);
34
+ }
35
+ /**
36
+ * Clear singleton instance for testing
37
+ */
38
+ static clearInstance(appRootDir, storeFileName) {
39
+ const normalizedPath = path_1.default.resolve(appRootDir);
40
+ const instanceKey = `${normalizedPath}:${storeFileName || "kvstore.json"}`;
41
+ this.instances.delete(instanceKey);
42
+ }
43
+ /**
44
+ * Clear all singleton instances for testing
45
+ */
46
+ static clearAllInstances() {
47
+ this.instances.clear();
48
+ }
49
+ /**
50
+ * Ensure store file exists with empty structure
51
+ */
52
+ ensureStoreFileExists() {
53
+ try {
54
+ // Ensure the .ocp-local directory exists
55
+ if (!fs_1.default.existsSync(this.configDirPath)) {
56
+ fs_1.default.mkdirSync(this.configDirPath, { recursive: true });
57
+ }
58
+ // Create empty store file if it doesn't exist
59
+ if (!fs_1.default.existsSync(this.kvStorePath)) {
60
+ this.saveDataToDisk({});
61
+ }
62
+ }
63
+ catch (error) {
64
+ console.warn(`Failed to ensure KV store file exists at ${this.kvStorePath}:`, error);
65
+ }
66
+ }
67
+ /**
68
+ * Load data from disk and clean up expired entries
69
+ */
70
+ loadDataFromDisk() {
71
+ try {
72
+ if (!fs_1.default.existsSync(this.kvStorePath)) {
73
+ return {};
74
+ }
75
+ const storeData = fs_1.default.readFileSync(this.kvStorePath, "utf-8");
76
+ const data = JSON.parse(storeData);
77
+ // Clean up expired entries
78
+ const now = Date.now();
79
+ const cleanedData = {};
80
+ for (const [key, item] of Object.entries(data)) {
81
+ if (!item.expiresAt || item.expiresAt > now) {
82
+ cleanedData[key] = item;
83
+ }
84
+ }
85
+ // Save cleaned data if any entries were removed
86
+ if (Object.keys(cleanedData).length !== Object.keys(data).length) {
87
+ this.saveDataToDisk(cleanedData);
88
+ }
89
+ return cleanedData;
90
+ }
91
+ catch (error) {
92
+ console.warn(`Failed to load KV store from ${this.kvStorePath}:`, error);
93
+ return {};
94
+ }
95
+ }
96
+ /**
97
+ * Save data to disk atomically
98
+ */
99
+ saveDataToDisk(data) {
100
+ try {
101
+ // Ensure the .ocp-local directory exists
102
+ if (!fs_1.default.existsSync(this.configDirPath)) {
103
+ fs_1.default.mkdirSync(this.configDirPath, { recursive: true });
104
+ }
105
+ // Serialize data with custom handling for NumberSet and StringSet
106
+ const serializedData = JSON.stringify(data, (key, value) => {
107
+ if (value instanceof NumberSet_1.NumberSet) {
108
+ return { _type: "NumberSet", values: Array.from(value) };
109
+ }
110
+ if (value instanceof StringSet_1.StringSet) {
111
+ return { _type: "StringSet", values: Array.from(value) };
112
+ }
113
+ return value;
114
+ }, 2);
115
+ // Write to temp file first, then rename for atomicity
116
+ const tempPath = `${this.kvStorePath}.tmp`;
117
+ fs_1.default.writeFileSync(tempPath, serializedData, "utf-8");
118
+ fs_1.default.renameSync(tempPath, this.kvStorePath);
119
+ }
120
+ catch (error) {
121
+ console.error(`Failed to save KV store to ${this.kvStorePath}:`, error);
122
+ throw error;
123
+ }
124
+ }
125
+ /**
126
+ * Deserialize data with custom handling for NumberSet and StringSet
127
+ */
128
+ deserializeValue(value) {
129
+ if (value && typeof value === "object" && value._type) {
130
+ switch (value._type) {
131
+ case "NumberSet":
132
+ return new NumberSet_1.NumberSet(value.values);
133
+ case "StringSet":
134
+ return new StringSet_1.StringSet(value.values);
135
+ }
136
+ }
137
+ if (Array.isArray(value)) {
138
+ return value.map((item) => this.deserializeValue(item));
139
+ }
140
+ if (value && typeof value === "object") {
141
+ const result = {};
142
+ for (const [k, v] of Object.entries(value)) {
143
+ result[k] = this.deserializeValue(v);
144
+ }
145
+ return result;
146
+ }
147
+ return value;
148
+ }
149
+ /**
150
+ * Get stored item with deserialization
151
+ */
152
+ getStoredItem(key) {
153
+ const data = this.loadDataFromDisk();
154
+ const item = data[key];
155
+ if (!item) {
156
+ return null;
157
+ }
158
+ // Deserialize the data
159
+ const deserializedData = this.deserializeValue(item.data);
160
+ return {
161
+ ...item,
162
+ data: deserializedData,
163
+ };
164
+ }
165
+ /**
166
+ * Store item with optional TTL
167
+ */
168
+ storeItem(key, data, options) {
169
+ const storeData = this.loadDataFromDisk();
170
+ const item = { data };
171
+ if (options?.ttl !== undefined) {
172
+ item.ttl = options.ttl;
173
+ item.expiresAt = Date.now() + options.ttl * 1000;
174
+ }
175
+ storeData[key] = item;
176
+ this.saveDataToDisk(storeData);
177
+ }
178
+ // ===================================================================================================================
179
+ // Basic Operations
180
+ // ===================================================================================================================
181
+ async get(key, fields) {
182
+ console.log(`[LocalKVStore] GET request: key="${key}", fields=${JSON.stringify(fields)}`);
183
+ try {
184
+ const item = this.getStoredItem(key);
185
+ if (!item) {
186
+ console.log(`[LocalKVStore] GET result: key="${key}" -> {}`);
187
+ return {};
188
+ }
189
+ if (fields && fields.length > 0) {
190
+ // Return only requested fields
191
+ const filteredData = {};
192
+ fields.forEach((field) => {
193
+ if (item.data[field] !== undefined) {
194
+ filteredData[field] = item.data[field];
195
+ }
196
+ });
197
+ console.log(`[LocalKVStore] GET result: key="${key}" -> ${JSON.stringify(filteredData)}`);
198
+ return filteredData;
199
+ }
200
+ else {
201
+ // Return all data
202
+ console.log(`[LocalKVStore] GET result: key="${key}" -> ${JSON.stringify(item.data)}`);
203
+ return item.data;
204
+ }
205
+ }
206
+ catch (error) {
207
+ console.warn(`[LocalKVStore] GET failed: key="${key}":`, error);
208
+ return {};
209
+ }
210
+ }
211
+ async put(key, value, options) {
212
+ console.log(`[LocalKVStore] PUT request: key="${key}", value=${JSON.stringify(value)}`);
213
+ try {
214
+ // Get previous value
215
+ const previousItem = this.getStoredItem(key);
216
+ const previousValue = previousItem ? previousItem.data : {};
217
+ // Store new value
218
+ this.storeItem(key, value, options);
219
+ console.log(`[LocalKVStore] PUT completed: key="${key}"`);
220
+ return previousValue;
221
+ }
222
+ catch (error) {
223
+ console.warn(`[LocalKVStore] PUT failed: key="${key}":`, error);
224
+ throw error;
225
+ }
226
+ }
227
+ async patch(key, valueOrUpdater, options) {
228
+ console.log(`[LocalKVStore] PATCH request: key="${key}"`);
229
+ try {
230
+ // Get current value
231
+ const currentItem = this.getStoredItem(key);
232
+ const currentValue = currentItem ? currentItem.data : {};
233
+ let newValue;
234
+ if (typeof valueOrUpdater === "function") {
235
+ // Updater function
236
+ const updater = valueOrUpdater;
237
+ try {
238
+ // Try with options first (KVPatchUpdaterWithOptions)
239
+ const currentOptions = { ttl: currentItem?.ttl };
240
+ newValue = updater(currentValue, currentOptions);
241
+ }
242
+ catch (error) {
243
+ // Fall back to regular PatchUpdater
244
+ newValue = updater(currentValue);
245
+ }
246
+ }
247
+ else {
248
+ // Value object - merge with current
249
+ newValue = { ...currentValue, ...valueOrUpdater };
250
+ }
251
+ // Store updated value
252
+ this.storeItem(key, newValue, options);
253
+ console.log(`[LocalKVStore] PATCH completed: key="${key}"`);
254
+ return currentValue;
255
+ }
256
+ catch (error) {
257
+ console.warn(`[LocalKVStore] PATCH failed: key="${key}":`, error);
258
+ throw error;
259
+ }
260
+ }
261
+ async delete(key, fields) {
262
+ console.log(`[LocalKVStore] DELETE request: key="${key}", fields=${JSON.stringify(fields)}`);
263
+ try {
264
+ const storeData = this.loadDataFromDisk();
265
+ const item = storeData[key];
266
+ if (!item) {
267
+ console.log(`[LocalKVStore] DELETE result: key="${key}" -> {} (not found)`);
268
+ return {};
269
+ }
270
+ const deserializedData = this.deserializeValue(item.data);
271
+ if (!fields || fields.length === 0) {
272
+ // Delete entire object
273
+ delete storeData[key];
274
+ this.saveDataToDisk(storeData);
275
+ console.log(`[LocalKVStore] DELETE completed: deleted entire key="${key}"`);
276
+ return deserializedData;
277
+ }
278
+ else {
279
+ // Delete specific fields
280
+ const deletedFields = {};
281
+ fields.forEach((field) => {
282
+ if (item.data[field] !== undefined) {
283
+ deletedFields[field] = item.data[field];
284
+ delete item.data[field];
285
+ }
286
+ });
287
+ this.saveDataToDisk(storeData);
288
+ console.log(`[LocalKVStore] DELETE completed: deleted fields ${JSON.stringify(fields)} from key="${key}"`);
289
+ return this.deserializeValue(deletedFields);
290
+ }
291
+ }
292
+ catch (error) {
293
+ console.warn(`[LocalKVStore] DELETE failed: key="${key}":`, error);
294
+ throw error;
295
+ }
296
+ }
297
+ async exists(key) {
298
+ console.log(`[LocalKVStore] EXISTS request: key="${key}"`);
299
+ try {
300
+ const item = this.getStoredItem(key);
301
+ const exists = item !== null;
302
+ console.log(`[LocalKVStore] EXISTS result: key="${key}" -> ${exists}`);
303
+ return exists;
304
+ }
305
+ catch (error) {
306
+ console.warn(`[LocalKVStore] EXISTS failed: key="${key}":`, error);
307
+ return false;
308
+ }
309
+ }
310
+ // ===================================================================================================================
311
+ // Numeric Operations
312
+ // ===================================================================================================================
313
+ async increment(key, field, amount = 1, options) {
314
+ console.log(`[LocalKVStore] INCREMENT request: key="${key}", field="${field}", amount=${amount}`);
315
+ return this.patch(key, (previous) => {
316
+ const currentValue = previous[field];
317
+ if (currentValue !== undefined && typeof currentValue !== "number") {
318
+ throw new Error(`Field "${field}" is not a number`);
319
+ }
320
+ const newValue = (currentValue || 0) + amount;
321
+ return {
322
+ ...previous,
323
+ [field]: newValue,
324
+ };
325
+ }, options).then(() => {
326
+ // Return the new value
327
+ return this.get(key, [field]).then((result) => result[field]);
328
+ });
329
+ }
330
+ async incrementMulti(key, fieldAmounts, options) {
331
+ console.log(`[LocalKVStore] INCREMENT_MULTI request: key="${key}", fieldAmounts=${JSON.stringify(fieldAmounts)}`);
332
+ return this.patch(key, (previous) => {
333
+ const updated = { ...previous };
334
+ for (const [field, amount] of Object.entries(fieldAmounts)) {
335
+ const currentValue = updated[field];
336
+ if (currentValue !== undefined && typeof currentValue !== "number") {
337
+ throw new Error(`Field "${field}" is not a number`);
338
+ }
339
+ updated[field] = (currentValue || 0) + amount;
340
+ }
341
+ return updated;
342
+ }, options).then(() => {
343
+ // Return the new values
344
+ return this.get(key, Object.keys(fieldAmounts)).then((result) => {
345
+ const newValues = {};
346
+ for (const field of Object.keys(fieldAmounts)) {
347
+ newValues[field] = result[field];
348
+ }
349
+ return newValues;
350
+ });
351
+ });
352
+ }
353
+ // ===================================================================================================================
354
+ // List Operations
355
+ // ===================================================================================================================
356
+ async shift(key, field) {
357
+ console.log(`[LocalKVStore] SHIFT request: key="${key}", field="${field}"`);
358
+ let shiftedElement = undefined;
359
+ await this.patch(key, (previous) => {
360
+ const currentValue = previous[field];
361
+ if (currentValue === undefined) {
362
+ return previous;
363
+ }
364
+ if (!Array.isArray(currentValue)) {
365
+ throw new Error(`Field "${field}" is not a list`);
366
+ }
367
+ if (currentValue.length === 0) {
368
+ return previous;
369
+ }
370
+ // Capture the first element before removing it
371
+ shiftedElement = currentValue[0];
372
+ // Remove first element
373
+ const updatedList = [...currentValue.slice(1)];
374
+ return {
375
+ ...previous,
376
+ [field]: updatedList,
377
+ };
378
+ });
379
+ console.log(`[LocalKVStore] SHIFT result: key="${key}", field="${field}" -> ${JSON.stringify(shiftedElement)}`);
380
+ return shiftedElement;
381
+ }
382
+ async shiftMulti(key, fieldCounts) {
383
+ console.log(`[LocalKVStore] SHIFT_MULTI request: key="${key}", fieldCounts=${JSON.stringify(fieldCounts)}`);
384
+ const shiftedItems = {};
385
+ return this.patch(key, (previous) => {
386
+ const updated = { ...previous };
387
+ for (const [field, count] of Object.entries(fieldCounts)) {
388
+ const currentValue = updated[field];
389
+ if (currentValue === undefined) {
390
+ shiftedItems[field] = [];
391
+ continue;
392
+ }
393
+ if (!Array.isArray(currentValue)) {
394
+ throw new Error(`Field "${field}" is not a list`);
395
+ }
396
+ const actualCount = Math.min(count, currentValue.length);
397
+ shiftedItems[field] = currentValue.slice(0, actualCount);
398
+ updated[field] = currentValue.slice(actualCount);
399
+ }
400
+ return updated;
401
+ }).then(() => shiftedItems);
402
+ }
403
+ async unshift(key, field, value) {
404
+ console.log(`[LocalKVStore] UNSHIFT request: key="${key}", field="${field}", value=${JSON.stringify(value)}`);
405
+ await this.patch(key, (previous) => {
406
+ const currentValue = previous[field];
407
+ if (currentValue === undefined) {
408
+ // Create new list with the value
409
+ return {
410
+ ...previous,
411
+ [field]: [value],
412
+ };
413
+ }
414
+ if (!Array.isArray(currentValue)) {
415
+ throw new Error(`Field "${field}" is not a list`);
416
+ }
417
+ // Prepend to existing list
418
+ return {
419
+ ...previous,
420
+ [field]: [value, ...currentValue],
421
+ };
422
+ });
423
+ }
424
+ async unshiftMulti(key, fieldValues) {
425
+ console.log(`[LocalKVStore] UNSHIFT_MULTI request: key="${key}", fieldValues=${JSON.stringify(fieldValues)}`);
426
+ await this.patch(key, (previous) => {
427
+ const updated = { ...previous };
428
+ for (const [field, values] of Object.entries(fieldValues)) {
429
+ const currentValue = updated[field];
430
+ if (currentValue === undefined) {
431
+ // Create new list with the values
432
+ updated[field] = [...values];
433
+ }
434
+ else {
435
+ if (!Array.isArray(currentValue)) {
436
+ throw new Error(`Field "${field}" is not a list`);
437
+ }
438
+ // Prepend to existing list
439
+ updated[field] = [...values, ...currentValue];
440
+ }
441
+ }
442
+ return updated;
443
+ });
444
+ }
445
+ async peek(key, field) {
446
+ console.log(`[LocalKVStore] PEEK request: key="${key}", field="${field}"`);
447
+ try {
448
+ const item = this.getStoredItem(key);
449
+ if (!item) {
450
+ console.log(`[LocalKVStore] PEEK result: key="${key}" -> undefined (key not found)`);
451
+ return undefined;
452
+ }
453
+ const fieldValue = item.data[field];
454
+ if (fieldValue === undefined) {
455
+ console.log(`[LocalKVStore] PEEK result: key="${key}", field="${field}" -> undefined (field not found)`);
456
+ return undefined;
457
+ }
458
+ if (!Array.isArray(fieldValue)) {
459
+ console.log(`[LocalKVStore] PEEK result: key="${key}", field="${field}" -> undefined (not a list)`);
460
+ return undefined;
461
+ }
462
+ if (fieldValue.length === 0) {
463
+ console.log(`[LocalKVStore] PEEK result: key="${key}", field="${field}" -> undefined (empty list)`);
464
+ return undefined;
465
+ }
466
+ const firstElement = fieldValue[0];
467
+ console.log(`[LocalKVStore] PEEK result: key="${key}", field="${field}" -> ${JSON.stringify(firstElement)}`);
468
+ return firstElement;
469
+ }
470
+ catch (error) {
471
+ console.warn(`[LocalKVStore] PEEK failed: key="${key}", field="${field}":`, error);
472
+ return undefined;
473
+ }
474
+ }
475
+ async peekMulti(key, fieldCounts) {
476
+ console.log(`[LocalKVStore] PEEK_MULTI request: key="${key}", fieldCounts=${JSON.stringify(fieldCounts)}`);
477
+ const result = {};
478
+ try {
479
+ const item = this.getStoredItem(key);
480
+ if (!item) {
481
+ // Return empty arrays for all fields
482
+ for (const field of Object.keys(fieldCounts)) {
483
+ result[field] = [];
484
+ }
485
+ return result;
486
+ }
487
+ for (const [field, count] of Object.entries(fieldCounts)) {
488
+ const fieldValue = item.data[field];
489
+ if (fieldValue === undefined || !Array.isArray(fieldValue)) {
490
+ result[field] = [];
491
+ continue;
492
+ }
493
+ const actualCount = Math.min(count, fieldValue.length);
494
+ result[field] = fieldValue.slice(0, actualCount);
495
+ }
496
+ console.log(`[LocalKVStore] PEEK_MULTI result: key="${key}" -> ${JSON.stringify(result)}`);
497
+ return result;
498
+ }
499
+ catch (error) {
500
+ console.warn(`[LocalKVStore] PEEK_MULTI failed: key="${key}":`, error);
501
+ // Return empty arrays for all fields on error
502
+ for (const field of Object.keys(fieldCounts)) {
503
+ result[field] = [];
504
+ }
505
+ return result;
506
+ }
507
+ }
508
+ async append(key, field, value) {
509
+ console.log(`[LocalKVStore] APPEND request: key="${key}", field="${field}", value=${JSON.stringify(value)}`);
510
+ await this.patch(key, (previous) => {
511
+ const currentValue = previous[field];
512
+ if (currentValue === undefined) {
513
+ // Create new list with the value
514
+ return {
515
+ ...previous,
516
+ [field]: [value],
517
+ };
518
+ }
519
+ if (!Array.isArray(currentValue)) {
520
+ throw new Error(`Field "${field}" is not a list`);
521
+ }
522
+ // Append to existing list
523
+ return {
524
+ ...previous,
525
+ [field]: [...currentValue, value],
526
+ };
527
+ });
528
+ }
529
+ async appendMulti(key, fieldValues) {
530
+ console.log(`[LocalKVStore] APPEND_MULTI request: key="${key}", fieldValues=${JSON.stringify(fieldValues)}`);
531
+ await this.patch(key, (previous) => {
532
+ const updated = { ...previous };
533
+ for (const [field, values] of Object.entries(fieldValues)) {
534
+ const currentValue = updated[field];
535
+ if (currentValue === undefined) {
536
+ // Create new list with the values
537
+ updated[field] = [...values];
538
+ }
539
+ else {
540
+ if (!Array.isArray(currentValue)) {
541
+ throw new Error(`Field "${field}" is not a list`);
542
+ }
543
+ // Append to existing list
544
+ updated[field] = [...currentValue, ...values];
545
+ }
546
+ }
547
+ return updated;
548
+ });
549
+ }
550
+ // ===================================================================================================================
551
+ // NumberSet Operations
552
+ // ===================================================================================================================
553
+ async addNumber(key, field, value) {
554
+ console.log(`[LocalKVStore] ADD_NUMBER request: key="${key}", field="${field}", value=${value}`);
555
+ let wasAdded = false;
556
+ await this.patch(key, (previous) => {
557
+ const currentValue = previous[field];
558
+ if (currentValue === undefined) {
559
+ // Create new NumberSet with the value
560
+ const newSet = new NumberSet_1.NumberSet([value]);
561
+ wasAdded = true;
562
+ return {
563
+ ...previous,
564
+ [field]: newSet,
565
+ };
566
+ }
567
+ if (!(currentValue instanceof NumberSet_1.NumberSet)) {
568
+ throw new Error(`Field "${field}" is not a NumberSet`);
569
+ }
570
+ // Check if value already exists
571
+ wasAdded = !currentValue.has(value);
572
+ if (wasAdded) {
573
+ // Create new NumberSet with added value
574
+ const newSet = new NumberSet_1.NumberSet(currentValue);
575
+ newSet.add(value);
576
+ return {
577
+ ...previous,
578
+ [field]: newSet,
579
+ };
580
+ }
581
+ return previous;
582
+ });
583
+ console.log(`[LocalKVStore] ADD_NUMBER result: key="${key}", field="${field}" -> ${wasAdded}`);
584
+ return wasAdded;
585
+ }
586
+ async addNumberMulti(key, fieldValues) {
587
+ console.log(`[LocalKVStore] ADD_NUMBER_MULTI request: key="${key}", fieldValues=${JSON.stringify(fieldValues)}`);
588
+ const addedValues = {};
589
+ await this.patch(key, (previous) => {
590
+ const updated = { ...previous };
591
+ for (const [field, values] of Object.entries(fieldValues)) {
592
+ const currentValue = updated[field];
593
+ const newlyAdded = new NumberSet_1.NumberSet();
594
+ if (currentValue === undefined) {
595
+ // Create new NumberSet with all values
596
+ const newSet = new NumberSet_1.NumberSet(values);
597
+ updated[field] = newSet;
598
+ addedValues[field] = new NumberSet_1.NumberSet(values);
599
+ }
600
+ else {
601
+ if (!(currentValue instanceof NumberSet_1.NumberSet)) {
602
+ throw new Error(`Field "${field}" is not a NumberSet`);
603
+ }
604
+ // Create new NumberSet and track newly added values
605
+ const newSet = new NumberSet_1.NumberSet(currentValue);
606
+ for (const value of values) {
607
+ if (!newSet.has(value)) {
608
+ newSet.add(value);
609
+ newlyAdded.add(value);
610
+ }
611
+ }
612
+ updated[field] = newSet;
613
+ addedValues[field] = newlyAdded;
614
+ }
615
+ }
616
+ return updated;
617
+ });
618
+ console.log(`[LocalKVStore] ADD_NUMBER_MULTI result: key="${key}" -> ${JSON.stringify(addedValues)}`);
619
+ return addedValues;
620
+ }
621
+ async removeNumber(key, field, value) {
622
+ console.log(`[LocalKVStore] REMOVE_NUMBER request: key="${key}", field="${field}", value=${value}`);
623
+ let wasRemoved = false;
624
+ await this.patch(key, (previous) => {
625
+ const currentValue = previous[field];
626
+ if (currentValue === undefined) {
627
+ return previous;
628
+ }
629
+ if (!(currentValue instanceof NumberSet_1.NumberSet)) {
630
+ throw new Error(`Field "${field}" is not a NumberSet`);
631
+ }
632
+ // Check if value exists
633
+ wasRemoved = currentValue.has(value);
634
+ if (wasRemoved) {
635
+ // Create new NumberSet with removed value
636
+ const newSet = new NumberSet_1.NumberSet(currentValue);
637
+ newSet.delete(value);
638
+ return {
639
+ ...previous,
640
+ [field]: newSet,
641
+ };
642
+ }
643
+ return previous;
644
+ });
645
+ console.log(`[LocalKVStore] REMOVE_NUMBER result: key="${key}", field="${field}" -> ${wasRemoved}`);
646
+ return wasRemoved;
647
+ }
648
+ async removeNumberMulti(key, fieldValues) {
649
+ console.log(`[LocalKVStore] REMOVE_NUMBER_MULTI request: key="${key}", fieldValues=${JSON.stringify(fieldValues)}`);
650
+ const removedValues = {};
651
+ await this.patch(key, (previous) => {
652
+ const updated = { ...previous };
653
+ for (const [field, values] of Object.entries(fieldValues)) {
654
+ const currentValue = updated[field];
655
+ const actuallyRemoved = new NumberSet_1.NumberSet();
656
+ if (currentValue === undefined) {
657
+ removedValues[field] = actuallyRemoved;
658
+ continue;
659
+ }
660
+ if (!(currentValue instanceof NumberSet_1.NumberSet)) {
661
+ throw new Error(`Field "${field}" is not a NumberSet`);
662
+ }
663
+ // Create new NumberSet and track removed values
664
+ const newSet = new NumberSet_1.NumberSet(currentValue);
665
+ for (const value of values) {
666
+ if (newSet.has(value)) {
667
+ newSet.delete(value);
668
+ actuallyRemoved.add(value);
669
+ }
670
+ }
671
+ updated[field] = newSet;
672
+ removedValues[field] = actuallyRemoved;
673
+ }
674
+ return updated;
675
+ });
676
+ console.log(`[LocalKVStore] REMOVE_NUMBER_MULTI result: key="${key}" -> ${JSON.stringify(removedValues)}`);
677
+ return removedValues;
678
+ }
679
+ async hasNumber(key, field, value) {
680
+ console.log(`[LocalKVStore] HAS_NUMBER request: key="${key}", field="${field}", value=${value}`);
681
+ try {
682
+ const item = this.getStoredItem(key);
683
+ if (!item) {
684
+ console.log(`[LocalKVStore] HAS_NUMBER result: key="${key}" -> false (key not found)`);
685
+ return false;
686
+ }
687
+ const fieldValue = item.data[field];
688
+ if (fieldValue === undefined) {
689
+ console.log(`[LocalKVStore] HAS_NUMBER result: key="${key}", field="${field}" -> false (field not found)`);
690
+ return false;
691
+ }
692
+ if (!(fieldValue instanceof NumberSet_1.NumberSet)) {
693
+ throw new Error(`Field "${field}" is not a NumberSet`);
694
+ }
695
+ const hasValue = fieldValue.has(value);
696
+ console.log(`[LocalKVStore] HAS_NUMBER result: key="${key}", field="${field}" -> ${hasValue}`);
697
+ return hasValue;
698
+ }
699
+ catch (error) {
700
+ console.warn(`[LocalKVStore] HAS_NUMBER failed: key="${key}", field="${field}":`, error);
701
+ throw error;
702
+ }
703
+ }
704
+ async hasNumberMulti(key, fieldValues) {
705
+ console.log(`[LocalKVStore] HAS_NUMBER_MULTI request: key="${key}", fieldValues=${JSON.stringify(fieldValues)}`);
706
+ const result = {};
707
+ try {
708
+ const item = this.getStoredItem(key);
709
+ if (!item) {
710
+ // Return empty NumberSets for all fields
711
+ for (const field of Object.keys(fieldValues)) {
712
+ result[field] = new NumberSet_1.NumberSet();
713
+ }
714
+ return result;
715
+ }
716
+ for (const [field, values] of Object.entries(fieldValues)) {
717
+ const fieldValue = item.data[field];
718
+ const existingValues = new NumberSet_1.NumberSet();
719
+ if (fieldValue === undefined) {
720
+ result[field] = existingValues;
721
+ continue;
722
+ }
723
+ if (!(fieldValue instanceof NumberSet_1.NumberSet)) {
724
+ throw new Error(`Field "${field}" is not a NumberSet`);
725
+ }
726
+ // Check which values exist
727
+ for (const value of values) {
728
+ if (fieldValue.has(value)) {
729
+ existingValues.add(value);
730
+ }
731
+ }
732
+ result[field] = existingValues;
733
+ }
734
+ console.log(`[LocalKVStore] HAS_NUMBER_MULTI result: key="${key}" -> ${JSON.stringify(result)}`);
735
+ return result;
736
+ }
737
+ catch (error) {
738
+ console.warn(`[LocalKVStore] HAS_NUMBER_MULTI failed: key="${key}":`, error);
739
+ throw error;
740
+ }
741
+ }
742
+ // ===================================================================================================================
743
+ // StringSet Operations
744
+ // ===================================================================================================================
745
+ async addString(key, field, value) {
746
+ console.log(`[LocalKVStore] ADD_STRING request: key="${key}", field="${field}", value="${value}"`);
747
+ let wasAdded = false;
748
+ await this.patch(key, (previous) => {
749
+ const currentValue = previous[field];
750
+ if (currentValue === undefined) {
751
+ // Create new StringSet with the value
752
+ const newSet = new StringSet_1.StringSet([value]);
753
+ wasAdded = true;
754
+ return {
755
+ ...previous,
756
+ [field]: newSet,
757
+ };
758
+ }
759
+ if (!(currentValue instanceof StringSet_1.StringSet)) {
760
+ throw new Error(`Field "${field}" is not a StringSet`);
761
+ }
762
+ // Check if value already exists
763
+ wasAdded = !currentValue.has(value);
764
+ if (wasAdded) {
765
+ // Create new StringSet with added value
766
+ const newSet = new StringSet_1.StringSet(currentValue);
767
+ newSet.add(value);
768
+ return {
769
+ ...previous,
770
+ [field]: newSet,
771
+ };
772
+ }
773
+ return previous;
774
+ });
775
+ console.log(`[LocalKVStore] ADD_STRING result: key="${key}", field="${field}" -> ${wasAdded}`);
776
+ return wasAdded;
777
+ }
778
+ async addStringMulti(key, fieldValues) {
779
+ console.log(`[LocalKVStore] ADD_STRING_MULTI request: key="${key}", fieldValues=${JSON.stringify(fieldValues)}`);
780
+ const addedValues = {};
781
+ await this.patch(key, (previous) => {
782
+ const updated = { ...previous };
783
+ for (const [field, values] of Object.entries(fieldValues)) {
784
+ const currentValue = updated[field];
785
+ const newlyAdded = new StringSet_1.StringSet();
786
+ if (currentValue === undefined) {
787
+ // Create new StringSet with all values
788
+ const newSet = new StringSet_1.StringSet(values);
789
+ updated[field] = newSet;
790
+ addedValues[field] = new StringSet_1.StringSet(values);
791
+ }
792
+ else {
793
+ if (!(currentValue instanceof StringSet_1.StringSet)) {
794
+ throw new Error(`Field "${field}" is not a StringSet`);
795
+ }
796
+ // Create new StringSet and track newly added values
797
+ const newSet = new StringSet_1.StringSet(currentValue);
798
+ for (const value of values) {
799
+ if (!newSet.has(value)) {
800
+ newSet.add(value);
801
+ newlyAdded.add(value);
802
+ }
803
+ }
804
+ updated[field] = newSet;
805
+ addedValues[field] = newlyAdded;
806
+ }
807
+ }
808
+ return updated;
809
+ });
810
+ console.log(`[LocalKVStore] ADD_STRING_MULTI result: key="${key}" -> ${JSON.stringify(addedValues)}`);
811
+ return addedValues;
812
+ }
813
+ async removeString(key, field, value) {
814
+ console.log(`[LocalKVStore] REMOVE_STRING request: key="${key}", field="${field}", value="${value}"`);
815
+ let wasRemoved = false;
816
+ await this.patch(key, (previous) => {
817
+ const currentValue = previous[field];
818
+ if (currentValue === undefined) {
819
+ return previous;
820
+ }
821
+ if (!(currentValue instanceof StringSet_1.StringSet)) {
822
+ throw new Error(`Field "${field}" is not a StringSet`);
823
+ }
824
+ // Check if value exists
825
+ wasRemoved = currentValue.has(value);
826
+ if (wasRemoved) {
827
+ // Create new StringSet with removed value
828
+ const newSet = new StringSet_1.StringSet(currentValue);
829
+ newSet.delete(value);
830
+ return {
831
+ ...previous,
832
+ [field]: newSet,
833
+ };
834
+ }
835
+ return previous;
836
+ });
837
+ console.log(`[LocalKVStore] REMOVE_STRING result: key="${key}", field="${field}" -> ${wasRemoved}`);
838
+ return wasRemoved;
839
+ }
840
+ async removeStringMulti(key, fieldValues) {
841
+ console.log(`[LocalKVStore] REMOVE_STRING_MULTI request: key="${key}", fieldValues=${JSON.stringify(fieldValues)}`);
842
+ const removedValues = {};
843
+ await this.patch(key, (previous) => {
844
+ const updated = { ...previous };
845
+ for (const [field, values] of Object.entries(fieldValues)) {
846
+ const currentValue = updated[field];
847
+ const actuallyRemoved = new StringSet_1.StringSet();
848
+ if (currentValue === undefined) {
849
+ removedValues[field] = actuallyRemoved;
850
+ continue;
851
+ }
852
+ if (!(currentValue instanceof StringSet_1.StringSet)) {
853
+ throw new Error(`Field "${field}" is not a StringSet`);
854
+ }
855
+ // Create new StringSet and track removed values
856
+ const newSet = new StringSet_1.StringSet(currentValue);
857
+ for (const value of values) {
858
+ if (newSet.has(value)) {
859
+ newSet.delete(value);
860
+ actuallyRemoved.add(value);
861
+ }
862
+ }
863
+ updated[field] = newSet;
864
+ removedValues[field] = actuallyRemoved;
865
+ }
866
+ return updated;
867
+ });
868
+ console.log(`[LocalKVStore] REMOVE_STRING_MULTI result: key="${key}" -> ${JSON.stringify(removedValues)}`);
869
+ return removedValues;
870
+ }
871
+ async hasString(key, field, value) {
872
+ console.log(`[LocalKVStore] HAS_STRING request: key="${key}", field="${field}", value="${value}"`);
873
+ try {
874
+ const item = this.getStoredItem(key);
875
+ if (!item) {
876
+ console.log(`[LocalKVStore] HAS_STRING result: key="${key}" -> false (key not found)`);
877
+ return false;
878
+ }
879
+ const fieldValue = item.data[field];
880
+ if (fieldValue === undefined) {
881
+ console.log(`[LocalKVStore] HAS_STRING result: key="${key}", field="${field}" -> false (field not found)`);
882
+ return false;
883
+ }
884
+ if (!(fieldValue instanceof StringSet_1.StringSet)) {
885
+ throw new Error(`Field "${field}" is not a StringSet`);
886
+ }
887
+ const hasValue = fieldValue.has(value);
888
+ console.log(`[LocalKVStore] HAS_STRING result: key="${key}", field="${field}" -> ${hasValue}`);
889
+ return hasValue;
890
+ }
891
+ catch (error) {
892
+ console.warn(`[LocalKVStore] HAS_STRING failed: key="${key}", field="${field}":`, error);
893
+ throw error;
894
+ }
895
+ }
896
+ async hasStringMulti(key, fieldValues) {
897
+ console.log(`[LocalKVStore] HAS_STRING_MULTI request: key="${key}", fieldValues=${JSON.stringify(fieldValues)}`);
898
+ const result = {};
899
+ try {
900
+ const item = this.getStoredItem(key);
901
+ if (!item) {
902
+ // Return empty StringSets for all fields
903
+ for (const field of Object.keys(fieldValues)) {
904
+ result[field] = new StringSet_1.StringSet();
905
+ }
906
+ return result;
907
+ }
908
+ for (const [field, values] of Object.entries(fieldValues)) {
909
+ const fieldValue = item.data[field];
910
+ const existingValues = new StringSet_1.StringSet();
911
+ if (fieldValue === undefined) {
912
+ result[field] = existingValues;
913
+ continue;
914
+ }
915
+ if (!(fieldValue instanceof StringSet_1.StringSet)) {
916
+ throw new Error(`Field "${field}" is not a StringSet`);
917
+ }
918
+ // Check which values exist
919
+ for (const value of values) {
920
+ if (fieldValue.has(value)) {
921
+ existingValues.add(value);
922
+ }
923
+ }
924
+ result[field] = existingValues;
925
+ }
926
+ console.log(`[LocalKVStore] HAS_STRING_MULTI result: key="${key}" -> ${JSON.stringify(result)}`);
927
+ return result;
928
+ }
929
+ catch (error) {
930
+ console.warn(`[LocalKVStore] HAS_STRING_MULTI failed: key="${key}":`, error);
931
+ throw error;
932
+ }
933
+ }
934
+ // ===================================================================================================================
935
+ // Helper Methods for API
936
+ // ===================================================================================================================
937
+ /**
938
+ * Get all stored data as a simple key-value object for the UI
939
+ */
940
+ getAllData() {
941
+ try {
942
+ const storeData = this.loadDataFromDisk();
943
+ const result = {};
944
+ for (const [key, item] of Object.entries(storeData)) {
945
+ result[key] = this.deserializeValue(item.data);
946
+ }
947
+ return result;
948
+ }
949
+ catch (error) {
950
+ console.warn("Failed to get all KV data:", error);
951
+ return {};
952
+ }
953
+ }
954
+ /**
955
+ * Set a simple key-value pair (for UI convenience)
956
+ */
957
+ async set(key, value) {
958
+ await this.put(key, value);
959
+ }
960
+ /**
961
+ * Get a simple value by key (for UI convenience)
962
+ */
963
+ async getValue(key) {
964
+ const result = await this.get(key);
965
+ return result.value;
966
+ }
967
+ /**
968
+ * Delete a key entirely (for UI convenience)
969
+ */
970
+ async deleteKey(key) {
971
+ const storeData = this.loadDataFromDisk();
972
+ if (key in storeData) {
973
+ delete storeData[key];
974
+ this.saveDataToDisk(storeData);
975
+ return true;
976
+ }
977
+ return false;
978
+ }
979
+ /**
980
+ * Get all keys
981
+ */
982
+ getAllKeys() {
983
+ try {
984
+ const storeData = this.loadDataFromDisk();
985
+ return Object.keys(storeData);
986
+ }
987
+ catch (error) {
988
+ console.warn("Failed to get all KV keys:", error);
989
+ return [];
990
+ }
991
+ }
992
+ /**
993
+ * Get a shared instance for shared KV store (global across installations of the same app)
994
+ */
995
+ static getSharedInstance(appRootDir) {
996
+ // Use the same app directory but with a different filename for shared storage
997
+ return LocalKVStore.getInstance(appRootDir, "shared-kvstore.json");
998
+ }
999
+ }
1000
+ exports.LocalKVStore = LocalKVStore;
1001
+ LocalKVStore.instances = new Map();
1002
+ //# sourceMappingURL=LocalKVStore.js.map