holosphere 1.1.10 → 1.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/global.js ADDED
@@ -0,0 +1,725 @@
1
+ // holo_global.js
2
+
3
+ /**
4
+ * Stores data in a global (non-holon-specific) table.
5
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
6
+ * @param {string} tableName - The table name to store data in.
7
+ * @param {object} data - The data to store. If it has an 'id' field, it will be used as the key.
8
+ * @param {string} [password] - Optional password for private holon.
9
+ * @returns {Promise<void>}
10
+ */
11
+ export async function putGlobal(holoInstance, tableName, data, password = null) {
12
+ try {
13
+ if (!tableName || !data) {
14
+ throw new Error('Table name and data are required');
15
+ }
16
+
17
+ let user = null;
18
+ if (password) {
19
+ user = holoInstance.gun.user();
20
+ await new Promise((resolve, reject) => {
21
+ const userNameString = holoInstance.userName(tableName);
22
+ user.auth(userNameString, password, (authAck) => {
23
+ if (authAck.err) {
24
+ // If auth fails, try to create the user
25
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
26
+ user.create(userNameString, password, (createAck) => {
27
+ if (createAck.err) {
28
+ // Check if error is "User already created"
29
+ if (createAck.err.includes("already created")) {
30
+ // This means user exists but password might be wrong, or some other issue
31
+ // Proceed with auth again, it might have been a temporary glitch or race.
32
+ // Or, it could be that the password is indeed wrong.
33
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
34
+ const freshUser = holoInstance.gun.user(); // Get a new user object
35
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
36
+ if (secondAuthAck.err) {
37
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
38
+ } else {
39
+ resolve();
40
+ }
41
+ });
42
+ } else {
43
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
44
+ }
45
+ } else {
46
+ // After successful creation, authenticate again
47
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
48
+ user.auth(userNameString, password, (secondAuthAck) => {
49
+ if (secondAuthAck.err) {
50
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
51
+ } else {
52
+ resolve();
53
+ }
54
+ });
55
+ }
56
+ });
57
+ } else {
58
+ resolve(); // Auth successful
59
+ }
60
+ });
61
+ });
62
+ }
63
+
64
+ return new Promise((resolve, reject) => {
65
+ try {
66
+ // Create a copy of data without the _meta field if it exists
67
+ let dataToStore = { ...data };
68
+ if (dataToStore._meta !== undefined) {
69
+ delete dataToStore._meta;
70
+ }
71
+ const payload = JSON.stringify(dataToStore);
72
+
73
+ // Check if the data being stored is a hologram
74
+ const isHologram = holoInstance.isHologram(dataToStore);
75
+
76
+ const dataPath = password ?
77
+ user.get('private').get(tableName) :
78
+ holoInstance.gun.get(holoInstance.appname).get(tableName);
79
+
80
+ if (data.id) {
81
+ const itemPath = dataPath.get(data.id);
82
+ itemPath.put(payload, ack => {
83
+ if (ack.err) {
84
+ reject(new Error(ack.err));
85
+ } else {
86
+ // --- Start: Hologram Tracking Logic (for data *being put*, if it's a hologram) ---
87
+ if (isHologram) {
88
+ try {
89
+ const storedDataSoulInfo = holoInstance.parseSoulPath(dataToStore.soul);
90
+ if (storedDataSoulInfo) {
91
+ const targetNodeRef = holoInstance.getNodeRef(dataToStore.soul); // Target of the data *being put*
92
+ // Soul of the hologram that was *actually stored* at tableName/data.id
93
+ const storedHologramInstanceSoul = `${holoInstance.appname}/${tableName}/${data.id}`;
94
+
95
+ targetNodeRef.get('_holograms').get(storedHologramInstanceSoul).put(true);
96
+
97
+ console.log(`Data (ID: ${data.id}) being put is a hologram. Added its instance soul ${storedHologramInstanceSoul} to its target ${dataToStore.soul}'s _holograms set.`);
98
+ } else {
99
+ console.warn(`Data (ID: ${data.id}) being put is a hologram, but could not parse its soul ${dataToStore.soul} for tracking.`);
100
+ }
101
+ } catch (trackingError) {
102
+ console.warn(`Error updating _holograms set for the target of the data being put (data ID: ${data.id}, soul: ${dataToStore.soul}):`, trackingError);
103
+ }
104
+ }
105
+ // --- End: Hologram Tracking Logic ---
106
+
107
+ resolve();
108
+ }
109
+ });
110
+ } else {
111
+ dataPath.put(payload, ack => {
112
+ if (ack.err) {
113
+ reject(new Error(ack.err));
114
+ } else {
115
+ // --- Start: Hologram Tracking Logic (for data *being put*, if it's a hologram) ---
116
+ if (isHologram) {
117
+ try {
118
+ const storedDataSoulInfo = holoInstance.parseSoulPath(dataToStore.soul);
119
+ if (storedDataSoulInfo) {
120
+ const targetNodeRef = holoInstance.getNodeRef(dataToStore.soul); // Target of the data *being put*
121
+ // Soul of the hologram that was *actually stored* at tableName (without specific key)
122
+ const storedHologramInstanceSoul = `${holoInstance.appname}/${tableName}`;
123
+
124
+ targetNodeRef.get('_holograms').get(storedHologramInstanceSoul).put(true);
125
+
126
+ console.log(`Data being put is a hologram. Added its instance soul ${storedHologramInstanceSoul} to its target ${dataToStore.soul}'s _holograms set.`);
127
+ } else {
128
+ console.warn(`Data being put is a hologram, but could not parse its soul ${dataToStore.soul} for tracking.`);
129
+ }
130
+ } catch (trackingError) {
131
+ console.warn(`Error updating _holograms set for the target of the data being put (soul: ${dataToStore.soul}):`, trackingError);
132
+ }
133
+ }
134
+ // --- End: Hologram Tracking Logic ---
135
+
136
+ resolve();
137
+ }
138
+ });
139
+ }
140
+ } catch (error) {
141
+ reject(error);
142
+ }
143
+ });
144
+ } catch (error) {
145
+ console.error('Error in putGlobal:', error);
146
+ throw error;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Retrieves a specific key from a global table.
152
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
153
+ * @param {string} tableName - The table name to retrieve from.
154
+ * @param {string} key - The key to retrieve.
155
+ * @param {string} [password] - Optional password for private holon.
156
+ * @returns {Promise<object|null>} - The parsed data for the key or null if not found.
157
+ */
158
+ export async function getGlobal(holoInstance, tableName, key, password = null) {
159
+ try {
160
+ let user = null;
161
+ if (password) {
162
+ user = holoInstance.gun.user();
163
+ await new Promise((resolve, reject) => {
164
+ const userNameString = holoInstance.userName(tableName);
165
+ user.auth(userNameString, password, (authAck) => {
166
+ if (authAck.err) {
167
+ // If auth fails, try to create the user
168
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
169
+ user.create(userNameString, password, (createAck) => {
170
+ if (createAck.err) {
171
+ // Check if error is "User already created"
172
+ if (createAck.err.includes("already created")) {
173
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
174
+ const freshUser = holoInstance.gun.user(); // Get a new user object
175
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
176
+ if (secondAuthAck.err) {
177
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
178
+ } else {
179
+ resolve();
180
+ }
181
+ });
182
+ } else {
183
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
184
+ }
185
+ } else {
186
+ // After successful creation, authenticate again
187
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
188
+ user.auth(userNameString, password, (secondAuthAck) => {
189
+ if (secondAuthAck.err) {
190
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
191
+ } else {
192
+ resolve();
193
+ }
194
+ });
195
+ }
196
+ });
197
+ } else {
198
+ resolve(); // Auth successful
199
+ }
200
+ });
201
+ });
202
+ }
203
+
204
+ return new Promise(async (resolve) => {
205
+ const handleData = async (data) => {
206
+ if (!data) {
207
+ resolve(null);
208
+ return;
209
+ }
210
+
211
+ try {
212
+ // The data should be a stringified JSON from putGlobal
213
+ const parsed = await holoInstance.parse(data); // Use instance's parse
214
+
215
+ if (!parsed) {
216
+ resolve(null);
217
+ return;
218
+ }
219
+
220
+ // Check if this is a hologram that needs to be resolved
221
+ if (holoInstance.isHologram(parsed)) { // Use instance's isHologram
222
+ const resolved = await holoInstance.resolveHologram(parsed, { // Use instance's resolveHologram
223
+ followHolograms: true // Always follow holograms
224
+ });
225
+
226
+ if (resolved === null) {
227
+ console.log(`Hologram at ${tableName}/${key} points to non-existent data. Deleting hologram.`);
228
+ try {
229
+ await holoInstance.deleteGlobal(tableName, key, password); // Use instance's deleteGlobal
230
+ } catch (deleteError) {
231
+ console.error(`Failed to delete invalid global hologram at ${tableName}/${key}:`, deleteError);
232
+ }
233
+ resolve(null); // Return null as the hologram is invalid
234
+ return;
235
+ }
236
+
237
+ if (resolved !== parsed) {
238
+ // Hologram was resolved successfully
239
+ resolve(resolved);
240
+ return;
241
+ }
242
+ }
243
+
244
+ resolve(parsed);
245
+ } catch (e) {
246
+ console.error('Error parsing data in getGlobal:', e);
247
+ resolve(null);
248
+ }
249
+ };
250
+
251
+ const dataPath = password ?
252
+ user.get('private').get(tableName) :
253
+ holoInstance.gun.get(holoInstance.appname).get(tableName);
254
+
255
+ const itemPath = dataPath.get(key);
256
+ itemPath.once(handleData);
257
+ });
258
+ } catch (error) {
259
+ console.error('Error in getGlobal:', error);
260
+ return null;
261
+ }
262
+ }
263
+
264
+ /**
265
+ * Retrieves all data from a global table.
266
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
267
+ * @param {string} tableName - The table name to retrieve data from.
268
+ * @param {string} [password] - Optional password for private holon.
269
+ * @returns {Promise<Array<object>>} - The parsed data from the table as an array.
270
+ */
271
+ export async function getAllGlobal(holoInstance, tableName, password = null) {
272
+ if (!tableName) {
273
+ throw new Error('getAllGlobal: Missing table name parameter');
274
+ }
275
+
276
+ try {
277
+ let user = null;
278
+ if (password) {
279
+ user = holoInstance.gun.user();
280
+ await new Promise((resolve, reject) => {
281
+ const userNameString = holoInstance.userName(tableName);
282
+ user.auth(userNameString, password, (authAck) => {
283
+ if (authAck.err) {
284
+ // If auth fails, try to create the user
285
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
286
+ user.create(userNameString, password, (createAck) => {
287
+ if (createAck.err) {
288
+ // Check if error is "User already created"
289
+ if (createAck.err.includes("already created")) {
290
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
291
+ const freshUser = holoInstance.gun.user(); // Get a new user object
292
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
293
+ if (secondAuthAck.err) {
294
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
295
+ } else {
296
+ resolve();
297
+ }
298
+ });
299
+ } else {
300
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
301
+ }
302
+ } else {
303
+ // After successful creation, authenticate again
304
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
305
+ user.auth(userNameString, password, (secondAuthAck) => {
306
+ if (secondAuthAck.err) {
307
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
308
+ } else {
309
+ resolve();
310
+ }
311
+ });
312
+ }
313
+ });
314
+ } else {
315
+ resolve(); // Auth successful
316
+ }
317
+ });
318
+ });
319
+ }
320
+
321
+ return new Promise((resolve) => {
322
+ let output = [];
323
+ let isResolved = false;
324
+ let timeout = setTimeout(() => {
325
+ if (!isResolved) {
326
+ isResolved = true;
327
+ resolve(output);
328
+ }
329
+ }, 5000);
330
+
331
+ const handleData = async (data) => {
332
+ if (!data) {
333
+ clearTimeout(timeout);
334
+ isResolved = true;
335
+ resolve([]);
336
+ return;
337
+ }
338
+
339
+ const keys = Object.keys(data).filter(key => key !== '_');
340
+ const promises = keys.map(key =>
341
+ new Promise(async (resolveItem) => {
342
+ const itemPath = password ?
343
+ user.get('private').get(tableName).get(key) :
344
+ holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
345
+
346
+ const itemData = await new Promise(resolveData => {
347
+ itemPath.once(resolveData);
348
+ });
349
+
350
+ if (itemData) {
351
+ try {
352
+ const parsed = await holoInstance.parse(itemData); // Use instance's parse
353
+ if (parsed) {
354
+ // Check if this is a hologram that needs to be resolved
355
+ if (holoInstance.isHologram(parsed)) { // Use instance's isHologram
356
+ const resolved = await holoInstance.resolveHologram(parsed, { // Use instance's resolveHologram
357
+ followHolograms: true // Always follow holograms
358
+ });
359
+
360
+ if (resolved === null) {
361
+ console.log(`Hologram at ${tableName}/${key} points to non-existent data. Deleting hologram.`);
362
+ try {
363
+ await holoInstance.deleteGlobal(tableName, key, password); // Use instance's deleteGlobal
364
+ } catch (deleteError) {
365
+ console.error(`Failed to delete invalid global hologram at ${tableName}/${key}:`, deleteError);
366
+ }
367
+ resolveItem();
368
+ return;
369
+ }
370
+
371
+ if (resolved !== parsed) {
372
+ // Hologram was resolved successfully
373
+ output.push(resolved);
374
+ } else {
375
+ // If resolution didn't change it (e.g., circular ref guard), push original parsed (which is a hologram)
376
+ output.push(parsed);
377
+ }
378
+ } else {
379
+ output.push(parsed);
380
+ }
381
+ }
382
+ } catch (error) {
383
+ console.error('Error parsing data:', error);
384
+ }
385
+ }
386
+ resolveItem();
387
+ })
388
+ );
389
+
390
+ await Promise.all(promises);
391
+ clearTimeout(timeout);
392
+ if (!isResolved) {
393
+ isResolved = true;
394
+ resolve(output);
395
+ }
396
+ };
397
+
398
+ const dataPath = password ?
399
+ user.get('private').get(tableName) :
400
+ holoInstance.gun.get(holoInstance.appname).get(tableName);
401
+
402
+ dataPath.once(handleData);
403
+ });
404
+ } catch (error) {
405
+ console.error('Error in getAllGlobal:', error);
406
+ return [];
407
+ }
408
+ }
409
+
410
+ /**
411
+ * Deletes a specific key from a global table.
412
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
413
+ * @param {string} tableName - The table name to delete from.
414
+ * @param {string} key - The key to delete.
415
+ * @param {string} [password] - Optional password for private holon.
416
+ * @returns {Promise<boolean>}
417
+ */
418
+ export async function deleteGlobal(holoInstance, tableName, key, password = null) {
419
+ if (!tableName || !key) {
420
+ throw new Error('deleteGlobal: Missing required parameters');
421
+ }
422
+
423
+ try {
424
+ // console.log('deleteGlobal - Starting deletion:', { tableName, key, hasPassword: !!password }); // Optional logging
425
+
426
+ let user = null;
427
+ if (password) {
428
+ user = holoInstance.gun.user();
429
+ await new Promise((resolve, reject) => {
430
+ const userNameString = holoInstance.userName(tableName);
431
+ user.auth(userNameString, password, (authAck) => {
432
+ if (authAck.err) {
433
+ // If auth fails, try to create the user
434
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
435
+ user.create(userNameString, password, (createAck) => {
436
+ if (createAck.err) {
437
+ // Check if error is "User already created"
438
+ if (createAck.err.includes("already created")) {
439
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
440
+ const freshUser = holoInstance.gun.user(); // Get a new user object
441
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
442
+ if (secondAuthAck.err) {
443
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
444
+ } else {
445
+ resolve();
446
+ }
447
+ });
448
+ } else {
449
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
450
+ }
451
+ } else {
452
+ // After successful creation, authenticate again
453
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
454
+ user.auth(userNameString, password, (secondAuthAck) => {
455
+ if (secondAuthAck.err) {
456
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
457
+ } else {
458
+ resolve();
459
+ }
460
+ });
461
+ }
462
+ });
463
+ } else {
464
+ resolve(); // Auth successful
465
+ }
466
+ });
467
+ });
468
+ }
469
+
470
+ const dataPath = password ?
471
+ user.get('private').get(tableName).get(key) :
472
+ holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
473
+
474
+ // --- Start: Hologram Tracking Removal ---
475
+ let trackingRemovalPromise = Promise.resolve(); // Default to resolved promise
476
+
477
+ // 1. Get the data first to check if it's a hologram
478
+ const rawDataToDelete = await new Promise((resolve) => dataPath.once(resolve));
479
+ let dataToDelete = null;
480
+ try {
481
+ if (typeof rawDataToDelete === 'string') {
482
+ dataToDelete = JSON.parse(rawDataToDelete);
483
+ } else {
484
+ // Handle cases where it might already be an object (though likely string)
485
+ dataToDelete = rawDataToDelete;
486
+ }
487
+ } catch(e) {
488
+ console.warn("[deleteGlobal] Could not JSON parse data for deletion check:", rawDataToDelete, e);
489
+ dataToDelete = null; // Ensure it's null if parsing fails
490
+ }
491
+
492
+ // 2. If it is a hologram, try to remove its reference from the target
493
+ const isDataHologram = dataToDelete && holoInstance.isHologram(dataToDelete);
494
+
495
+ if (isDataHologram) {
496
+ try {
497
+ const targetSoul = dataToDelete.soul;
498
+ const targetSoulInfo = holoInstance.parseSoulPath(targetSoul);
499
+
500
+ if (targetSoulInfo) {
501
+ const targetNodeRef = holoInstance.getNodeRef(targetSoul);
502
+ const deletedHologramSoul = `${holoInstance.appname}/${tableName}/${key}`;
503
+
504
+ // Create a promise that resolves when the hologram is removed from the list
505
+ trackingRemovalPromise = new Promise((resolveTrack) => { // No reject needed, just warn on error
506
+ targetNodeRef.get('_holograms').get(deletedHologramSoul).put(null, (ack) => { // Remove the hologram entry completely
507
+ if (ack.err) {
508
+ console.warn(`[deleteGlobal] Error removing hologram ${deletedHologramSoul} from target ${targetSoul}:`, ack.err);
509
+ }
510
+ console.log(`Removed hologram ${deletedHologramSoul} from target ${targetSoul}'s _holograms list`);
511
+ resolveTrack(); // Resolve regardless of ack error to not block main delete
512
+ });
513
+ });
514
+ } else {
515
+ console.warn(`Could not parse target soul ${targetSoul} for hologram tracking removal during deleteGlobal.`);
516
+ }
517
+ } catch (trackingError) {
518
+ console.warn(`Error initiating hologram reference removal from target ${dataToDelete.soul} during deleteGlobal:`, trackingError);
519
+ // Ensure trackingRemovalPromise remains resolved if setup fails
520
+ trackingRemovalPromise = Promise.resolve();
521
+ }
522
+ }
523
+ // --- End: Hologram Tracking Removal ---
524
+
525
+ // 3. Wait for the tracking removal attempt to be acknowledged
526
+ await trackingRemovalPromise;
527
+
528
+ // 4. Proceed with the actual deletion of the hologram node itself
529
+ return new Promise((resolve, reject) => {
530
+ // Request deletion
531
+ dataPath.put(null, ack => {
532
+ // console.log('deleteGlobal - Deletion acknowledgment:', ack); // Optional logging
533
+ if (ack.err) {
534
+ console.error('deleteGlobal - Deletion error:', ack.err);
535
+ reject(new Error(ack.err));
536
+ } else {
537
+ // Resolve directly on success, like deleteFunc
538
+ resolve(true);
539
+ }
540
+ });
541
+ });
542
+ } catch (error) {
543
+ console.error('Error in deleteGlobal:', error);
544
+ throw error;
545
+ }
546
+ }
547
+
548
+ /**
549
+ * Deletes an entire global table.
550
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
551
+ * @param {string} tableName - The table name to delete.
552
+ * @param {string} [password] - Optional password for private holon.
553
+ * @returns {Promise<boolean>}
554
+ */
555
+ export async function deleteAllGlobal(holoInstance, tableName, password = null) {
556
+ if (!tableName) {
557
+ throw new Error('deleteAllGlobal: Missing table name parameter');
558
+ }
559
+
560
+ try {
561
+ let user = null;
562
+ if (password) {
563
+ user = holoInstance.gun.user();
564
+ await new Promise((resolve, reject) => {
565
+ const userNameString = holoInstance.userName(tableName);
566
+ user.auth(userNameString, password, (authAck) => {
567
+ if (authAck.err) {
568
+ // If auth fails, try to create the user
569
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
570
+ user.create(userNameString, password, (createAck) => {
571
+ if (createAck.err) {
572
+ // Check if error is "User already created"
573
+ if (createAck.err.includes("already created")) {
574
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
575
+ const freshUser = holoInstance.gun.user(); // Get a new user object
576
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
577
+ if (secondAuthAck.err) {
578
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
579
+ } else {
580
+ resolve();
581
+ }
582
+ });
583
+ } else {
584
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
585
+ }
586
+ } else {
587
+ // After successful creation, authenticate again
588
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
589
+ user.auth(userNameString, password, (secondAuthAck) => {
590
+ if (secondAuthAck.err) {
591
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
592
+ } else {
593
+ resolve();
594
+ }
595
+ });
596
+ }
597
+ });
598
+ } else {
599
+ resolve(); // Auth successful
600
+ }
601
+ });
602
+ });
603
+ }
604
+
605
+ return new Promise((resolve, reject) => {
606
+ try {
607
+ const deletions = new Set();
608
+ let timeout = setTimeout(() => {
609
+ if (deletions.size === 0) {
610
+ resolve(true); // No data to delete
611
+ }
612
+ }, 5000);
613
+
614
+ const dataPath = password ?
615
+ user.get('private').get(tableName) :
616
+ holoInstance.gun.get(holoInstance.appname).get(tableName);
617
+
618
+ dataPath.once(async (data) => {
619
+ if (!data) {
620
+ clearTimeout(timeout);
621
+ resolve(true);
622
+ return;
623
+ }
624
+
625
+ const keys = Object.keys(data).filter(key => key !== '_');
626
+
627
+ // Process each key to handle holograms properly
628
+ for (const key of keys) {
629
+ try {
630
+ // Get the data to check if it's a hologram
631
+ const itemPath = password ?
632
+ user.get('private').get(tableName).get(key) :
633
+ holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
634
+
635
+ const rawDataToDelete = await new Promise((resolveItem) => itemPath.once(resolveItem));
636
+ let dataToDelete = null;
637
+
638
+ try {
639
+ if (typeof rawDataToDelete === 'string') {
640
+ dataToDelete = JSON.parse(rawDataToDelete);
641
+ } else {
642
+ dataToDelete = rawDataToDelete;
643
+ }
644
+ } catch(e) {
645
+ console.warn("[deleteAllGlobal] Could not JSON parse data for deletion check:", rawDataToDelete, e);
646
+ dataToDelete = null;
647
+ }
648
+
649
+ // Check if it's a hologram and handle accordingly
650
+ const isDataHologram = dataToDelete && holoInstance.isHologram(dataToDelete);
651
+
652
+ if (isDataHologram) {
653
+ // Handle hologram deletion - remove from target's _holograms list
654
+ try {
655
+ const targetSoul = dataToDelete.soul;
656
+ const targetSoulInfo = holoInstance.parseSoulPath(targetSoul);
657
+
658
+ if (targetSoulInfo) {
659
+ const targetNodeRef = holoInstance.getNodeRef(targetSoul);
660
+ const deletedHologramSoul = `${holoInstance.appname}/${tableName}/${key}`;
661
+
662
+ // Remove the hologram from target's _holograms list
663
+ await new Promise((resolveTrack) => {
664
+ targetNodeRef.get('_holograms').get(deletedHologramSoul).put(null, (ack) => {
665
+ if (ack.err) {
666
+ console.warn(`[deleteAllGlobal] Error removing hologram ${deletedHologramSoul} from target ${targetSoul}:`, ack.err);
667
+ }
668
+ console.log(`Removed hologram ${deletedHologramSoul} from target ${targetSoul}'s _holograms list`);
669
+ resolveTrack();
670
+ });
671
+ });
672
+ } else {
673
+ console.warn(`Could not parse target soul ${targetSoul} for hologram tracking removal during deleteAllGlobal.`);
674
+ }
675
+ } catch (trackingError) {
676
+ console.warn(`Error removing hologram reference from target ${dataToDelete.soul} during deleteAllGlobal:`, trackingError);
677
+ }
678
+ }
679
+
680
+ // Add to deletions set for tracking
681
+ deletions.add(key);
682
+ } catch (error) {
683
+ console.warn(`Error processing key ${key} during deleteAllGlobal:`, error);
684
+ // Still add to deletions set even if hologram processing failed
685
+ deletions.add(key);
686
+ }
687
+ }
688
+
689
+ // Now delete all the items
690
+ const promises = keys.map(key =>
691
+ new Promise((resolveDelete, rejectDelete) => {
692
+ const deletePath = password ?
693
+ user.get('private').get(tableName).get(key) :
694
+ holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
695
+
696
+ deletePath.put(null, ack => {
697
+ if (ack.err) {
698
+ console.error(`Failed to delete ${key}:`, ack.err);
699
+ rejectDelete(new Error(ack.err));
700
+ } else {
701
+ resolveDelete();
702
+ }
703
+ });
704
+ })
705
+ );
706
+
707
+ try {
708
+ await Promise.all(promises);
709
+ // Finally delete the table itself
710
+ dataPath.put(null);
711
+ clearTimeout(timeout);
712
+ resolve(true);
713
+ } catch (error) {
714
+ reject(error);
715
+ }
716
+ });
717
+ } catch (error) {
718
+ reject(error);
719
+ }
720
+ });
721
+ } catch (error) {
722
+ console.error('Error in deleteAllGlobal:', error);
723
+ throw error;
724
+ }
725
+ }