holosphere 1.1.10 → 1.1.11

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,560 @@
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
+ // Remove isHologram field before storing - NO LONGER NEEDED
67
+ // if (data && data.isHologram !== undefined) {
68
+ // delete data.isHologram;
69
+ // }
70
+ const payload = JSON.stringify(data);
71
+
72
+ const dataPath = password ?
73
+ user.get('private').get(tableName) :
74
+ holoInstance.gun.get(holoInstance.appname).get(tableName);
75
+
76
+ if (data.id) {
77
+ const itemPath = dataPath.get(data.id);
78
+ itemPath.put(payload, ack => {
79
+ if (ack.err) {
80
+ reject(new Error(ack.err));
81
+ } else {
82
+ resolve();
83
+ }
84
+ });
85
+ } else {
86
+ dataPath.put(payload, ack => {
87
+ if (ack.err) {
88
+ reject(new Error(ack.err));
89
+ } else {
90
+ resolve();
91
+ }
92
+ });
93
+ }
94
+ } catch (error) {
95
+ reject(error);
96
+ }
97
+ });
98
+ } catch (error) {
99
+ console.error('Error in putGlobal:', error);
100
+ throw error;
101
+ }
102
+ }
103
+
104
+ /**
105
+ * Retrieves a specific key from a global table.
106
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
107
+ * @param {string} tableName - The table name to retrieve from.
108
+ * @param {string} key - The key to retrieve.
109
+ * @param {string} [password] - Optional password for private holon.
110
+ * @returns {Promise<object|null>} - The parsed data for the key or null if not found.
111
+ */
112
+ export async function getGlobal(holoInstance, tableName, key, password = null) {
113
+ try {
114
+ let user = null;
115
+ if (password) {
116
+ user = holoInstance.gun.user();
117
+ await new Promise((resolve, reject) => {
118
+ const userNameString = holoInstance.userName(tableName);
119
+ user.auth(userNameString, password, (authAck) => {
120
+ if (authAck.err) {
121
+ // If auth fails, try to create the user
122
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
123
+ user.create(userNameString, password, (createAck) => {
124
+ if (createAck.err) {
125
+ // Check if error is "User already created"
126
+ if (createAck.err.includes("already created")) {
127
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
128
+ const freshUser = holoInstance.gun.user(); // Get a new user object
129
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
130
+ if (secondAuthAck.err) {
131
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
132
+ } else {
133
+ resolve();
134
+ }
135
+ });
136
+ } else {
137
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
138
+ }
139
+ } else {
140
+ // After successful creation, authenticate again
141
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
142
+ user.auth(userNameString, password, (secondAuthAck) => {
143
+ if (secondAuthAck.err) {
144
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
145
+ } else {
146
+ resolve();
147
+ }
148
+ });
149
+ }
150
+ });
151
+ } else {
152
+ resolve(); // Auth successful
153
+ }
154
+ });
155
+ });
156
+ }
157
+
158
+ return new Promise(async (resolve) => {
159
+ const handleData = async (data) => {
160
+ if (!data) {
161
+ resolve(null);
162
+ return;
163
+ }
164
+
165
+ try {
166
+ // The data should be a stringified JSON from putGlobal
167
+ const parsed = await holoInstance.parse(data); // Use instance's parse
168
+
169
+ if (!parsed) {
170
+ resolve(null);
171
+ return;
172
+ }
173
+
174
+ // Check if this is a hologram that needs to be resolved
175
+ if (holoInstance.isHologram(parsed)) { // Use instance's isHologram
176
+ const resolved = await holoInstance.resolveHologram(parsed, { // Use instance's resolveHologram
177
+ followHolograms: true // Always follow holograms
178
+ });
179
+
180
+ if (resolved === null) {
181
+ console.log(`Hologram at ${tableName}/${key} points to non-existent data. Deleting hologram.`);
182
+ try {
183
+ await holoInstance.deleteGlobal(tableName, key, password); // Use instance's deleteGlobal
184
+ } catch (deleteError) {
185
+ console.error(`Failed to delete invalid global hologram at ${tableName}/${key}:`, deleteError);
186
+ }
187
+ resolve(null); // Return null as the hologram is invalid
188
+ return;
189
+ }
190
+
191
+ if (resolved !== parsed) {
192
+ // Hologram was resolved successfully
193
+ resolve(resolved);
194
+ return;
195
+ }
196
+ }
197
+
198
+ resolve(parsed);
199
+ } catch (e) {
200
+ console.error('Error parsing data in getGlobal:', e);
201
+ resolve(null);
202
+ }
203
+ };
204
+
205
+ const dataPath = password ?
206
+ user.get('private').get(tableName) :
207
+ holoInstance.gun.get(holoInstance.appname).get(tableName);
208
+
209
+ const itemPath = dataPath.get(key);
210
+ itemPath.once(handleData);
211
+ });
212
+ } catch (error) {
213
+ console.error('Error in getGlobal:', error);
214
+ return null;
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Retrieves all data from a global table.
220
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
221
+ * @param {string} tableName - The table name to retrieve data from.
222
+ * @param {string} [password] - Optional password for private holon.
223
+ * @returns {Promise<Array<object>>} - The parsed data from the table as an array.
224
+ */
225
+ export async function getAllGlobal(holoInstance, tableName, password = null) {
226
+ if (!tableName) {
227
+ throw new Error('getAllGlobal: Missing table name parameter');
228
+ }
229
+
230
+ try {
231
+ let user = null;
232
+ if (password) {
233
+ user = holoInstance.gun.user();
234
+ await new Promise((resolve, reject) => {
235
+ const userNameString = holoInstance.userName(tableName);
236
+ user.auth(userNameString, password, (authAck) => {
237
+ if (authAck.err) {
238
+ // If auth fails, try to create the user
239
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
240
+ user.create(userNameString, password, (createAck) => {
241
+ if (createAck.err) {
242
+ // Check if error is "User already created"
243
+ if (createAck.err.includes("already created")) {
244
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
245
+ const freshUser = holoInstance.gun.user(); // Get a new user object
246
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
247
+ if (secondAuthAck.err) {
248
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
249
+ } else {
250
+ resolve();
251
+ }
252
+ });
253
+ } else {
254
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
255
+ }
256
+ } else {
257
+ // After successful creation, authenticate again
258
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
259
+ user.auth(userNameString, password, (secondAuthAck) => {
260
+ if (secondAuthAck.err) {
261
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
262
+ } else {
263
+ resolve();
264
+ }
265
+ });
266
+ }
267
+ });
268
+ } else {
269
+ resolve(); // Auth successful
270
+ }
271
+ });
272
+ });
273
+ }
274
+
275
+ return new Promise((resolve) => {
276
+ let output = [];
277
+ let isResolved = false;
278
+ let timeout = setTimeout(() => {
279
+ if (!isResolved) {
280
+ isResolved = true;
281
+ resolve(output);
282
+ }
283
+ }, 5000);
284
+
285
+ const handleData = async (data) => {
286
+ if (!data) {
287
+ clearTimeout(timeout);
288
+ isResolved = true;
289
+ resolve([]);
290
+ return;
291
+ }
292
+
293
+ const keys = Object.keys(data).filter(key => key !== '_');
294
+ const promises = keys.map(key =>
295
+ new Promise(async (resolveItem) => {
296
+ const itemPath = password ?
297
+ user.get('private').get(tableName).get(key) :
298
+ holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
299
+
300
+ const itemData = await new Promise(resolveData => {
301
+ itemPath.once(resolveData);
302
+ });
303
+
304
+ if (itemData) {
305
+ try {
306
+ const parsed = await holoInstance.parse(itemData); // Use instance's parse
307
+ if (parsed) {
308
+ // Check if this is a hologram that needs to be resolved
309
+ if (holoInstance.isHologram(parsed)) { // Use instance's isHologram
310
+ const resolved = await holoInstance.resolveHologram(parsed, { // Use instance's resolveHologram
311
+ followHolograms: true // Always follow holograms
312
+ });
313
+
314
+ if (resolved === null) {
315
+ console.log(`Hologram at ${tableName}/${key} points to non-existent data. Deleting hologram.`);
316
+ try {
317
+ await holoInstance.deleteGlobal(tableName, key, password); // Use instance's deleteGlobal
318
+ } catch (deleteError) {
319
+ console.error(`Failed to delete invalid global hologram at ${tableName}/${key}:`, deleteError);
320
+ }
321
+ resolveItem();
322
+ return;
323
+ }
324
+
325
+ if (resolved !== parsed) {
326
+ // Hologram was resolved successfully
327
+ output.push(resolved);
328
+ } else {
329
+ // If resolution didn't change it (e.g., circular ref guard), push original parsed (which is a hologram)
330
+ output.push(parsed);
331
+ }
332
+ } else {
333
+ output.push(parsed);
334
+ }
335
+ }
336
+ } catch (error) {
337
+ console.error('Error parsing data:', error);
338
+ }
339
+ }
340
+ resolveItem();
341
+ })
342
+ );
343
+
344
+ await Promise.all(promises);
345
+ clearTimeout(timeout);
346
+ if (!isResolved) {
347
+ isResolved = true;
348
+ resolve(output);
349
+ }
350
+ };
351
+
352
+ const dataPath = password ?
353
+ user.get('private').get(tableName) :
354
+ holoInstance.gun.get(holoInstance.appname).get(tableName);
355
+
356
+ dataPath.once(handleData);
357
+ });
358
+ } catch (error) {
359
+ console.error('Error in getAllGlobal:', error);
360
+ return [];
361
+ }
362
+ }
363
+
364
+ /**
365
+ * Deletes a specific key from a global table.
366
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
367
+ * @param {string} tableName - The table name to delete from.
368
+ * @param {string} key - The key to delete.
369
+ * @param {string} [password] - Optional password for private holon.
370
+ * @returns {Promise<boolean>}
371
+ */
372
+ export async function deleteGlobal(holoInstance, tableName, key, password = null) {
373
+ if (!tableName || !key) {
374
+ throw new Error('deleteGlobal: Missing required parameters');
375
+ }
376
+
377
+ try {
378
+ // console.log('deleteGlobal - Starting deletion:', { tableName, key, hasPassword: !!password }); // Optional logging
379
+
380
+ let user = null;
381
+ if (password) {
382
+ user = holoInstance.gun.user();
383
+ await new Promise((resolve, reject) => {
384
+ const userNameString = holoInstance.userName(tableName);
385
+ user.auth(userNameString, password, (authAck) => {
386
+ if (authAck.err) {
387
+ // If auth fails, try to create the user
388
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
389
+ user.create(userNameString, password, (createAck) => {
390
+ if (createAck.err) {
391
+ // Check if error is "User already created"
392
+ if (createAck.err.includes("already created")) {
393
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
394
+ const freshUser = holoInstance.gun.user(); // Get a new user object
395
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
396
+ if (secondAuthAck.err) {
397
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
398
+ } else {
399
+ resolve();
400
+ }
401
+ });
402
+ } else {
403
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
404
+ }
405
+ } else {
406
+ // After successful creation, authenticate again
407
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
408
+ user.auth(userNameString, password, (secondAuthAck) => {
409
+ if (secondAuthAck.err) {
410
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
411
+ } else {
412
+ resolve();
413
+ }
414
+ });
415
+ }
416
+ });
417
+ } else {
418
+ resolve(); // Auth successful
419
+ }
420
+ });
421
+ });
422
+ }
423
+
424
+ return new Promise((resolve, reject) => {
425
+ const dataPath = password ?
426
+ user.get('private').get(tableName).get(key) :
427
+ holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
428
+
429
+ // Request deletion
430
+ dataPath.put(null, ack => {
431
+ // console.log('deleteGlobal - Deletion acknowledgment:', ack); // Optional logging
432
+ if (ack.err) {
433
+ console.error('deleteGlobal - Deletion error:', ack.err);
434
+ reject(new Error(ack.err));
435
+ } else {
436
+ // Resolve directly on success, like deleteFunc
437
+ resolve(true);
438
+ }
439
+ });
440
+ });
441
+ } catch (error) {
442
+ console.error('Error in deleteGlobal:', error);
443
+ throw error;
444
+ }
445
+ }
446
+
447
+ /**
448
+ * Deletes an entire global table.
449
+ * @param {HoloSphere} holoInstance - The HoloSphere instance.
450
+ * @param {string} tableName - The table name to delete.
451
+ * @param {string} [password] - Optional password for private holon.
452
+ * @returns {Promise<boolean>}
453
+ */
454
+ export async function deleteAllGlobal(holoInstance, tableName, password = null) {
455
+ if (!tableName) {
456
+ throw new Error('deleteAllGlobal: Missing table name parameter');
457
+ }
458
+
459
+ try {
460
+ let user = null;
461
+ if (password) {
462
+ user = holoInstance.gun.user();
463
+ await new Promise((resolve, reject) => {
464
+ const userNameString = holoInstance.userName(tableName);
465
+ user.auth(userNameString, password, (authAck) => {
466
+ if (authAck.err) {
467
+ // If auth fails, try to create the user
468
+ console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
469
+ user.create(userNameString, password, (createAck) => {
470
+ if (createAck.err) {
471
+ // Check if error is "User already created"
472
+ if (createAck.err.includes("already created")) {
473
+ console.log(`User ${userNameString} already existed, re-attempting auth with fresh user object.`);
474
+ const freshUser = holoInstance.gun.user(); // Get a new user object
475
+ freshUser.auth(userNameString, password, (secondAuthAck) => {
476
+ if (secondAuthAck.err) {
477
+ reject(new Error(`Failed to auth with fresh user object after create attempt (user existed): ${secondAuthAck.err}`));
478
+ } else {
479
+ resolve();
480
+ }
481
+ });
482
+ } else {
483
+ reject(new Error(`Failed to create user ${userNameString}: ${createAck.err}`));
484
+ }
485
+ } else {
486
+ // After successful creation, authenticate again
487
+ console.log(`User ${userNameString} created successfully, attempting auth...`);
488
+ user.auth(userNameString, password, (secondAuthAck) => {
489
+ if (secondAuthAck.err) {
490
+ reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
491
+ } else {
492
+ resolve();
493
+ }
494
+ });
495
+ }
496
+ });
497
+ } else {
498
+ resolve(); // Auth successful
499
+ }
500
+ });
501
+ });
502
+ }
503
+
504
+ return new Promise((resolve, reject) => {
505
+ try {
506
+ const deletions = new Set();
507
+ let timeout = setTimeout(() => {
508
+ if (deletions.size === 0) {
509
+ resolve(true); // No data to delete
510
+ }
511
+ }, 5000);
512
+
513
+ const dataPath = password ?
514
+ user.get('private').get(tableName) :
515
+ holoInstance.gun.get(holoInstance.appname).get(tableName);
516
+
517
+ dataPath.once(async (data) => {
518
+ if (!data) {
519
+ clearTimeout(timeout);
520
+ resolve(true);
521
+ return;
522
+ }
523
+
524
+ const keys = Object.keys(data).filter(key => key !== '_');
525
+ const promises = keys.map(key =>
526
+ new Promise((resolveDelete, rejectDelete) => {
527
+ const deletePath = password ?
528
+ user.get('private').get(tableName).get(key) :
529
+ holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
530
+
531
+ deletePath.put(null, ack => {
532
+ if (ack.err) {
533
+ console.error(`Failed to delete ${key}:`, ack.err);
534
+ rejectDelete(new Error(ack.err));
535
+ } else {
536
+ resolveDelete();
537
+ }
538
+ });
539
+ })
540
+ );
541
+
542
+ try {
543
+ await Promise.all(promises);
544
+ // Finally delete the table itself
545
+ dataPath.put(null);
546
+ clearTimeout(timeout);
547
+ resolve(true);
548
+ } catch (error) {
549
+ reject(error);
550
+ }
551
+ });
552
+ } catch (error) {
553
+ reject(error);
554
+ }
555
+ });
556
+ } catch (error) {
557
+ console.error('Error in deleteAllGlobal:', error);
558
+ throw error;
559
+ }
560
+ }