holosphere 1.1.19 → 2.0.0-alpha0

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 (146) hide show
  1. package/.env.example +36 -0
  2. package/.eslintrc.json +16 -0
  3. package/.prettierrc.json +7 -0
  4. package/README.md +476 -531
  5. package/bin/holosphere-activitypub.js +158 -0
  6. package/cleanup-test-data.js +204 -0
  7. package/examples/demo.html +1333 -0
  8. package/examples/example-bot.js +197 -0
  9. package/package.json +47 -87
  10. package/scripts/check-bundle-size.js +54 -0
  11. package/scripts/check-quest-ids.js +77 -0
  12. package/scripts/import-holons.js +578 -0
  13. package/scripts/publish-to-relay.js +101 -0
  14. package/scripts/read-example.js +186 -0
  15. package/scripts/relay-diagnostic.js +59 -0
  16. package/scripts/relay-example.js +179 -0
  17. package/scripts/resync-to-relay.js +245 -0
  18. package/scripts/revert-import.js +196 -0
  19. package/scripts/test-hybrid-mode.js +108 -0
  20. package/scripts/test-local-storage.js +63 -0
  21. package/scripts/test-nostr-direct.js +55 -0
  22. package/scripts/test-read-data.js +45 -0
  23. package/scripts/test-write-read.js +63 -0
  24. package/scripts/verify-import.js +95 -0
  25. package/scripts/verify-relay-data.js +139 -0
  26. package/src/ai/aggregation.js +319 -0
  27. package/src/ai/breakdown.js +511 -0
  28. package/src/ai/classifier.js +217 -0
  29. package/src/ai/council.js +228 -0
  30. package/src/ai/embeddings.js +279 -0
  31. package/src/ai/federation-ai.js +324 -0
  32. package/src/ai/h3-ai.js +955 -0
  33. package/src/ai/index.js +112 -0
  34. package/src/ai/json-ops.js +225 -0
  35. package/src/ai/llm-service.js +205 -0
  36. package/src/ai/nl-query.js +223 -0
  37. package/src/ai/relationships.js +353 -0
  38. package/src/ai/schema-extractor.js +218 -0
  39. package/src/ai/spatial.js +293 -0
  40. package/src/ai/tts.js +194 -0
  41. package/src/content/social-protocols.js +168 -0
  42. package/src/core/holosphere.js +273 -0
  43. package/src/crypto/secp256k1.js +259 -0
  44. package/src/federation/discovery.js +334 -0
  45. package/src/federation/hologram.js +1042 -0
  46. package/src/federation/registry.js +386 -0
  47. package/src/hierarchical/upcast.js +110 -0
  48. package/src/index.js +2669 -0
  49. package/src/schema/validator.js +91 -0
  50. package/src/spatial/h3-operations.js +110 -0
  51. package/src/storage/backend-factory.js +125 -0
  52. package/src/storage/backend-interface.js +142 -0
  53. package/src/storage/backends/activitypub/server.js +653 -0
  54. package/src/storage/backends/activitypub-backend.js +272 -0
  55. package/src/storage/backends/gundb-backend.js +233 -0
  56. package/src/storage/backends/nostr-backend.js +136 -0
  57. package/src/storage/filesystem-storage-browser.js +41 -0
  58. package/src/storage/filesystem-storage.js +138 -0
  59. package/src/storage/global-tables.js +81 -0
  60. package/src/storage/gun-async.js +281 -0
  61. package/src/storage/gun-wrapper.js +221 -0
  62. package/src/storage/indexeddb-storage.js +122 -0
  63. package/src/storage/key-storage-simple.js +76 -0
  64. package/src/storage/key-storage.js +136 -0
  65. package/src/storage/memory-storage.js +59 -0
  66. package/src/storage/migration.js +338 -0
  67. package/src/storage/nostr-async.js +811 -0
  68. package/src/storage/nostr-client.js +939 -0
  69. package/src/storage/nostr-wrapper.js +211 -0
  70. package/src/storage/outbox-queue.js +208 -0
  71. package/src/storage/persistent-storage.js +109 -0
  72. package/src/storage/sync-service.js +164 -0
  73. package/src/subscriptions/manager.js +142 -0
  74. package/test-ai-real-api.js +202 -0
  75. package/tests/unit/ai/aggregation.test.js +295 -0
  76. package/tests/unit/ai/breakdown.test.js +446 -0
  77. package/tests/unit/ai/classifier.test.js +294 -0
  78. package/tests/unit/ai/council.test.js +262 -0
  79. package/tests/unit/ai/embeddings.test.js +384 -0
  80. package/tests/unit/ai/federation-ai.test.js +344 -0
  81. package/tests/unit/ai/h3-ai.test.js +458 -0
  82. package/tests/unit/ai/index.test.js +304 -0
  83. package/tests/unit/ai/json-ops.test.js +307 -0
  84. package/tests/unit/ai/llm-service.test.js +390 -0
  85. package/tests/unit/ai/nl-query.test.js +383 -0
  86. package/tests/unit/ai/relationships.test.js +311 -0
  87. package/tests/unit/ai/schema-extractor.test.js +384 -0
  88. package/tests/unit/ai/spatial.test.js +279 -0
  89. package/tests/unit/ai/tts.test.js +279 -0
  90. package/tests/unit/content.test.js +332 -0
  91. package/tests/unit/contract/core.test.js +88 -0
  92. package/tests/unit/contract/crypto.test.js +198 -0
  93. package/tests/unit/contract/data.test.js +223 -0
  94. package/tests/unit/contract/federation.test.js +181 -0
  95. package/tests/unit/contract/hierarchical.test.js +113 -0
  96. package/tests/unit/contract/schema.test.js +114 -0
  97. package/tests/unit/contract/social.test.js +217 -0
  98. package/tests/unit/contract/spatial.test.js +110 -0
  99. package/tests/unit/contract/subscriptions.test.js +128 -0
  100. package/tests/unit/contract/utils.test.js +159 -0
  101. package/tests/unit/core.test.js +152 -0
  102. package/tests/unit/crypto.test.js +328 -0
  103. package/tests/unit/federation.test.js +234 -0
  104. package/tests/unit/gun-async.test.js +252 -0
  105. package/tests/unit/hierarchical.test.js +399 -0
  106. package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
  107. package/tests/unit/integration/scenario-02-federation.test.js +76 -0
  108. package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
  109. package/tests/unit/integration/scenario-04-validation.test.js +129 -0
  110. package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
  111. package/tests/unit/integration/scenario-06-social.test.js +135 -0
  112. package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
  113. package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
  114. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
  115. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
  116. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
  117. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
  118. package/tests/unit/performance/benchmark.test.js +85 -0
  119. package/tests/unit/schema.test.js +213 -0
  120. package/tests/unit/spatial.test.js +158 -0
  121. package/tests/unit/storage.test.js +195 -0
  122. package/tests/unit/subscriptions.test.js +328 -0
  123. package/tests/unit/test-data-permanence-debug.js +197 -0
  124. package/tests/unit/test-data-permanence.js +340 -0
  125. package/tests/unit/test-key-persistence-fixed.js +148 -0
  126. package/tests/unit/test-key-persistence.js +172 -0
  127. package/tests/unit/test-relay-permanence.js +376 -0
  128. package/tests/unit/test-second-node.js +95 -0
  129. package/tests/unit/test-simple-write.js +89 -0
  130. package/vite.config.js +49 -0
  131. package/vitest.config.js +20 -0
  132. package/FEDERATION.md +0 -213
  133. package/compute.js +0 -298
  134. package/content.js +0 -1022
  135. package/federation.js +0 -1234
  136. package/global.js +0 -736
  137. package/hexlib.js +0 -335
  138. package/hologram.js +0 -183
  139. package/holosphere-bundle.esm.js +0 -34549
  140. package/holosphere-bundle.js +0 -34580
  141. package/holosphere-bundle.min.js +0 -49
  142. package/holosphere.d.ts +0 -604
  143. package/holosphere.js +0 -739
  144. package/node.js +0 -246
  145. package/schema.js +0 -139
  146. package/utils.js +0 -302
package/global.js DELETED
@@ -1,736 +0,0 @@
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") || createAck.err.includes("already being 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 or being created, 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
- console.log(`Auth still failed after user existed check: ${secondAuthAck.err}. Resolving anyway for test operations.`);
38
- resolve(); // Resolve anyway to allow test operations
39
- } else {
40
- resolve();
41
- }
42
- });
43
- } else {
44
- console.log(`Create user error (resolving anyway for operations): ${createAck.err}`);
45
- resolve(); // Resolve anyway to allow test operations
46
- }
47
- } else {
48
- // After successful creation, authenticate again
49
- console.log(`User ${userNameString} created successfully, attempting auth...`);
50
- user.auth(userNameString, password, (secondAuthAck) => {
51
- if (secondAuthAck.err) {
52
- reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
53
- } else {
54
- resolve();
55
- }
56
- });
57
- }
58
- });
59
- } else {
60
- resolve(); // Auth successful
61
- }
62
- });
63
- });
64
- }
65
-
66
- return new Promise((resolve, reject) => {
67
- try {
68
- // Create a copy of data without the _meta field if it exists
69
- let dataToStore = { ...data };
70
- if (dataToStore._meta !== undefined) {
71
- delete dataToStore._meta;
72
- }
73
- const payload = JSON.stringify(dataToStore);
74
-
75
- // Check if the data being stored is a hologram
76
- const isHologram = holoInstance.isHologram(dataToStore);
77
-
78
- const dataPath = password ?
79
- user.get('private').get(tableName) :
80
- holoInstance.gun.get(holoInstance.appname).get(tableName);
81
-
82
- if (data.id) {
83
- const itemPath = dataPath.get(data.id);
84
- itemPath.put(payload, ack => {
85
- if (ack.err) {
86
- reject(new Error(ack.err));
87
- } else {
88
- // --- Start: Hologram Tracking Logic (for data *being put*, if it's a hologram) ---
89
- if (isHologram) {
90
- try {
91
- const storedDataSoulInfo = holoInstance.parseSoulPath(dataToStore.soul);
92
- if (storedDataSoulInfo) {
93
- const targetNodeRef = holoInstance.getNodeRef(dataToStore.soul); // Target of the data *being put*
94
- // Soul of the hologram that was *actually stored* at tableName/data.id
95
- const storedHologramInstanceSoul = `${holoInstance.appname}/${tableName}/${data.id}`;
96
-
97
- targetNodeRef.get('_holograms').get(storedHologramInstanceSoul).put(true);
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
- } else {
126
- console.warn(`Data being put is a hologram, but could not parse its soul ${dataToStore.soul} for tracking.`);
127
- }
128
- } catch (trackingError) {
129
- console.warn(`Error updating _holograms set for the target of the data being put (soul: ${dataToStore.soul}):`, trackingError);
130
- }
131
- }
132
- // --- End: Hologram Tracking Logic ---
133
-
134
- resolve();
135
- }
136
- });
137
- }
138
- } catch (error) {
139
- reject(error);
140
- }
141
- });
142
- } catch (error) {
143
- console.error('Error in putGlobal:', error);
144
- throw error;
145
- }
146
- }
147
-
148
- /**
149
- * Retrieves a specific key from a global table.
150
- * @param {HoloSphere} holoInstance - The HoloSphere instance.
151
- * @param {string} tableName - The table name to retrieve from.
152
- * @param {string} key - The key to retrieve.
153
- * @param {string} [password] - Optional password for private holon.
154
- * @returns {Promise<object|null>} - The parsed data for the key or null if not found.
155
- */
156
- export async function getGlobal(holoInstance, tableName, key, password = null) {
157
- try {
158
- let user = null;
159
- if (password) {
160
- user = holoInstance.gun.user();
161
- await new Promise((resolve, reject) => {
162
- const userNameString = holoInstance.userName(tableName);
163
- user.auth(userNameString, password, (authAck) => {
164
- if (authAck.err) {
165
- // If auth fails, try to create the user
166
- console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
167
- user.create(userNameString, password, (createAck) => {
168
- if (createAck.err) {
169
- // Check if error is "User already created"
170
- if (createAck.err.includes("already created") || createAck.err.includes("already being created")) {
171
- console.log(`User ${userNameString} already existed or being created, re-attempting auth with fresh user object.`);
172
- const freshUser = holoInstance.gun.user(); // Get a new user object
173
- freshUser.auth(userNameString, password, (secondAuthAck) => {
174
- if (secondAuthAck.err) {
175
- console.log(`Auth still failed after user existed check: ${secondAuthAck.err}. Resolving anyway for test operations.`);
176
- resolve(); // Resolve anyway to allow test operations
177
- } else {
178
- resolve();
179
- }
180
- });
181
- } else {
182
- console.log(`Create user error (resolving anyway for operations): ${createAck.err}`);
183
- resolve(); // Resolve anyway to allow test operations
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
- try {
228
- await holoInstance.deleteGlobal(tableName, key, password); // Use instance's deleteGlobal
229
- } catch (deleteError) {
230
- console.error(`Failed to delete invalid global hologram at ${tableName}/${key}:`, deleteError);
231
- }
232
- resolve(null); // Return null as the hologram is invalid
233
- return;
234
- }
235
-
236
- if (resolved !== parsed) {
237
- // Hologram was resolved successfully
238
- resolve(resolved);
239
- return;
240
- }
241
- }
242
-
243
- resolve(parsed);
244
- } catch (e) {
245
- console.error('Error parsing data in getGlobal:', e);
246
- resolve(null);
247
- }
248
- };
249
-
250
- const dataPath = password ?
251
- user.get('private').get(tableName) :
252
- holoInstance.gun.get(holoInstance.appname).get(tableName);
253
-
254
- const itemPath = dataPath.get(key);
255
- itemPath.once(handleData);
256
- });
257
- } catch (error) {
258
- console.error('Error in getGlobal:', error);
259
- return null;
260
- }
261
- }
262
-
263
- /**
264
- * Retrieves all data from a global table.
265
- * @param {HoloSphere} holoInstance - The HoloSphere instance.
266
- * @param {string} tableName - The table name to retrieve data from.
267
- * @param {string} [password] - Optional password for private holon.
268
- * @returns {Promise<Array<object>>} - The parsed data from the table as an array.
269
- */
270
- export async function getAllGlobal(holoInstance, tableName, password = null) {
271
- if (!tableName) {
272
- throw new Error('getAllGlobal: Missing table name parameter');
273
- }
274
-
275
- try {
276
- let user = null;
277
- if (password) {
278
- user = holoInstance.gun.user();
279
- await new Promise((resolve, reject) => {
280
- const userNameString = holoInstance.userName(tableName);
281
- user.auth(userNameString, password, (authAck) => {
282
- if (authAck.err) {
283
- // If auth fails, try to create the user
284
- console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
285
- user.create(userNameString, password, (createAck) => {
286
- if (createAck.err) {
287
- // Check if error is "User already created"
288
- if (createAck.err.includes("already created") || createAck.err.includes("already being created")) {
289
- console.log(`User ${userNameString} already existed or being created, re-attempting auth with fresh user object.`);
290
- const freshUser = holoInstance.gun.user(); // Get a new user object
291
- freshUser.auth(userNameString, password, (secondAuthAck) => {
292
- if (secondAuthAck.err) {
293
- console.log(`Auth still failed after user existed check: ${secondAuthAck.err}. Resolving anyway for test operations.`);
294
- resolve(); // Resolve anyway to allow test operations
295
- } else {
296
- resolve();
297
- }
298
- });
299
- } else {
300
- console.log(`Create user error (resolving anyway for operations): ${createAck.err}`);
301
- resolve(); // Resolve anyway to allow test operations
302
- }
303
- } else {
304
- // After successful creation, authenticate again
305
- console.log(`User ${userNameString} created successfully, attempting auth...`);
306
- user.auth(userNameString, password, (secondAuthAck) => {
307
- if (secondAuthAck.err) {
308
- reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
309
- } else {
310
- resolve();
311
- }
312
- });
313
- }
314
- });
315
- } else {
316
- resolve(); // Auth successful
317
- }
318
- });
319
- });
320
- }
321
-
322
- return new Promise((resolve) => {
323
- let output = [];
324
- let isResolved = false;
325
- let timeout = setTimeout(() => {
326
- if (!isResolved) {
327
- isResolved = true;
328
- resolve(output);
329
- }
330
- }, 5000);
331
-
332
- const handleData = async (data) => {
333
- if (!data) {
334
- clearTimeout(timeout);
335
- isResolved = true;
336
- resolve([]);
337
- return;
338
- }
339
-
340
- const keys = Object.keys(data).filter(key => key !== '_');
341
- const promises = keys.map(key =>
342
- new Promise(async (resolveItem) => {
343
- const itemPath = password ?
344
- user.get('private').get(tableName).get(key) :
345
- holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
346
-
347
- const itemData = await new Promise(resolveData => {
348
- itemPath.once(resolveData);
349
- });
350
-
351
- if (itemData) {
352
- try {
353
- const parsed = await holoInstance.parse(itemData); // Use instance's parse
354
- if (parsed) {
355
- // Check if this is a hologram that needs to be resolved
356
- if (holoInstance.isHologram(parsed)) { // Use instance's isHologram
357
- const resolved = await holoInstance.resolveHologram(parsed, { // Use instance's resolveHologram
358
- followHolograms: true // Always follow holograms
359
- });
360
-
361
- if (resolved === null) {
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") || createAck.err.includes("already being created")) {
439
- console.log(`User ${userNameString} already existed or being created, 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
- console.log(`Auth still failed after user existed check: ${secondAuthAck.err}. Resolving anyway for test operations.`);
444
- resolve(); // Resolve anyway to allow test operations
445
- } else {
446
- resolve();
447
- }
448
- });
449
- } else {
450
- console.log(`Create user error (resolving anyway for operations): ${createAck.err}`);
451
- resolve(); // Resolve anyway to allow test operations
452
- }
453
- } else {
454
- // After successful creation, authenticate again
455
- console.log(`User ${userNameString} created successfully, attempting auth...`);
456
- user.auth(userNameString, password, (secondAuthAck) => {
457
- if (secondAuthAck.err) {
458
- reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
459
- } else {
460
- resolve();
461
- }
462
- });
463
- }
464
- });
465
- } else {
466
- resolve(); // Auth successful
467
- }
468
- });
469
- });
470
- }
471
-
472
- const dataPath = password ?
473
- user.get('private').get(tableName).get(key) :
474
- holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
475
-
476
- // --- Start: Hologram Tracking Removal ---
477
- let trackingRemovalPromise = Promise.resolve(); // Default to resolved promise
478
-
479
- // 1. Get the data first to check if it's a hologram
480
- const rawDataToDelete = await new Promise((resolve) => dataPath.once(resolve));
481
- let dataToDelete = null;
482
- try {
483
- if (typeof rawDataToDelete === 'string') {
484
- dataToDelete = JSON.parse(rawDataToDelete);
485
- } else {
486
- // Handle cases where it might already be an object (though likely string)
487
- dataToDelete = rawDataToDelete;
488
- }
489
- } catch(e) {
490
- console.warn("[deleteGlobal] Could not JSON parse data for deletion check:", rawDataToDelete, e);
491
- dataToDelete = null; // Ensure it's null if parsing fails
492
- }
493
-
494
- // 2. If it is a hologram, try to remove its reference from the target
495
- const isDataHologram = dataToDelete && holoInstance.isHologram(dataToDelete);
496
-
497
- if (isDataHologram) {
498
- try {
499
- const targetSoul = dataToDelete.soul;
500
- const targetSoulInfo = holoInstance.parseSoulPath(targetSoul);
501
-
502
- if (targetSoulInfo) {
503
- const targetNodeRef = holoInstance.getNodeRef(targetSoul);
504
- const deletedHologramSoul = `${holoInstance.appname}/${tableName}/${key}`;
505
-
506
- // Create a promise that resolves when the hologram is removed from the list
507
- trackingRemovalPromise = new Promise((resolveTrack) => { // No reject needed, just warn on error
508
- targetNodeRef.get('_holograms').get(deletedHologramSoul).put(null, (ack) => { // Remove the hologram entry completely
509
- if (ack.err) {
510
- console.warn(`[deleteGlobal] Error removing hologram ${deletedHologramSoul} from target ${targetSoul}:`, ack.err);
511
- }
512
- resolveTrack(); // Resolve regardless of ack error to not block main delete
513
- });
514
- });
515
- } else {
516
- console.warn(`Could not parse target soul ${targetSoul} for hologram tracking removal during deleteGlobal.`);
517
- }
518
- } catch (trackingError) {
519
- console.warn(`Error initiating hologram reference removal from target ${dataToDelete.soul} during deleteGlobal:`, trackingError);
520
- // Ensure trackingRemovalPromise remains resolved if setup fails
521
- trackingRemovalPromise = Promise.resolve();
522
- }
523
- }
524
- // --- End: Hologram Tracking Removal ---
525
-
526
- // 3. Wait for the tracking removal attempt to be acknowledged
527
- await trackingRemovalPromise;
528
-
529
- // 4. Proceed with the actual deletion of the hologram node itself
530
- return new Promise((resolve, reject) => {
531
- // Request deletion
532
- dataPath.put(null, ack => {
533
- // console.log('deleteGlobal - Deletion acknowledgment:', ack); // Optional logging
534
- if (ack.err) {
535
- console.error('deleteGlobal - Deletion error:', ack.err);
536
- reject(new Error(ack.err));
537
- } else {
538
- // Resolve directly on success, like deleteFunc
539
- resolve(true);
540
- }
541
- });
542
- });
543
- } catch (error) {
544
- console.error('Error in deleteGlobal:', error);
545
- throw error;
546
- }
547
- }
548
-
549
- /**
550
- * Deletes an entire global table.
551
- * @param {HoloSphere} holoInstance - The HoloSphere instance.
552
- * @param {string} tableName - The table name to delete.
553
- * @param {string} [password] - Optional password for private holon.
554
- * @returns {Promise<boolean>}
555
- */
556
- export async function deleteAllGlobal(holoInstance, tableName, password = null) {
557
- if (!tableName) {
558
- throw new Error('deleteAllGlobal: Missing table name parameter');
559
- }
560
-
561
- try {
562
- let user = null;
563
- if (password) {
564
- user = holoInstance.gun.user();
565
- await new Promise((resolve, reject) => {
566
- const userNameString = holoInstance.userName(tableName);
567
- user.auth(userNameString, password, (authAck) => {
568
- if (authAck.err) {
569
- // If auth fails, try to create the user
570
- console.log(`Initial auth failed for ${userNameString}, attempting to create...`);
571
- user.create(userNameString, password, (createAck) => {
572
- if (createAck.err) {
573
- // Check if error is "User already created"
574
- if (createAck.err.includes("already created") || createAck.err.includes("already being created")) {
575
- console.log(`User ${userNameString} already existed or being created, re-attempting auth with fresh user object.`);
576
- const freshUser = holoInstance.gun.user(); // Get a new user object
577
- freshUser.auth(userNameString, password, (secondAuthAck) => {
578
- if (secondAuthAck.err) {
579
- console.log(`Auth still failed after user existed check: ${secondAuthAck.err}. Resolving anyway for test cleanup.`);
580
- resolve(); // Resolve anyway to allow test cleanup
581
- } else {
582
- resolve();
583
- }
584
- });
585
- } else {
586
- console.log(`Create user error (resolving anyway for cleanup): ${createAck.err}`);
587
- resolve(); // Resolve anyway to allow test cleanup
588
- }
589
- } else {
590
- // After successful creation, authenticate again
591
- console.log(`User ${userNameString} created successfully, attempting auth...`);
592
- user.auth(userNameString, password, (secondAuthAck) => {
593
- if (secondAuthAck.err) {
594
- reject(new Error(`Failed to auth after create for ${userNameString}: ${secondAuthAck.err}`));
595
- } else {
596
- resolve();
597
- }
598
- });
599
- }
600
- });
601
- } else {
602
- resolve(); // Auth successful
603
- }
604
- });
605
- });
606
- }
607
-
608
- return new Promise((resolve, reject) => {
609
- try {
610
- const deletions = new Set();
611
- let timeout = setTimeout(() => {
612
- if (deletions.size === 0) {
613
- resolve(true); // No data to delete
614
- }
615
- }, 5000);
616
-
617
- const dataPath = password ?
618
- user.get('private').get(tableName) :
619
- holoInstance.gun.get(holoInstance.appname).get(tableName);
620
-
621
- dataPath.once(async (data) => {
622
- if (!data) {
623
- clearTimeout(timeout);
624
- resolve(true);
625
- return;
626
- }
627
-
628
- const keys = Object.keys(data).filter(key => key !== '_');
629
-
630
- // Process each key to handle holograms properly
631
- for (const key of keys) {
632
- try {
633
- // Get the data to check if it's a hologram
634
- const itemPath = password ?
635
- user.get('private').get(tableName).get(key) :
636
- holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
637
-
638
- const rawDataToDelete = await new Promise((resolveItem) => itemPath.once(resolveItem));
639
- let dataToDelete = null;
640
-
641
- try {
642
- if (typeof rawDataToDelete === 'string') {
643
- dataToDelete = JSON.parse(rawDataToDelete);
644
- } else {
645
- dataToDelete = rawDataToDelete;
646
- }
647
- } catch(e) {
648
- console.warn("[deleteAllGlobal] Could not JSON parse data for deletion check:", rawDataToDelete, e);
649
- dataToDelete = null;
650
- }
651
-
652
- // Check if it's a hologram and handle accordingly
653
- const isDataHologram = dataToDelete && holoInstance.isHologram(dataToDelete);
654
-
655
- if (isDataHologram) {
656
- // Handle hologram deletion - remove from target's _holograms list
657
- try {
658
- const targetSoul = dataToDelete.soul;
659
- const targetSoulInfo = holoInstance.parseSoulPath(targetSoul);
660
-
661
- if (targetSoulInfo) {
662
- const targetNodeRef = holoInstance.getNodeRef(targetSoul);
663
- const deletedHologramSoul = `${holoInstance.appname}/${tableName}/${key}`;
664
-
665
- // Remove the hologram from target's _holograms list
666
- await new Promise((resolveTrack) => {
667
- targetNodeRef.get('_holograms').get(deletedHologramSoul).put(null, (ack) => {
668
- if (ack.err) {
669
- console.warn(`[deleteAllGlobal] Error removing hologram ${deletedHologramSoul} from target ${targetSoul}:`, ack.err);
670
- }
671
- resolveTrack();
672
- });
673
- });
674
- } else {
675
- console.warn(`Could not parse target soul ${targetSoul} for hologram tracking removal during deleteAllGlobal.`);
676
- }
677
- } catch (trackingError) {
678
- console.warn(`Error removing hologram reference from target ${dataToDelete.soul} during deleteAllGlobal:`, trackingError);
679
- }
680
- }
681
-
682
- // Add to deletions set for tracking
683
- deletions.add(key);
684
- } catch (error) {
685
- console.warn(`Error processing key ${key} during deleteAllGlobal:`, error);
686
- // Still add to deletions set even if hologram processing failed
687
- deletions.add(key);
688
- }
689
- }
690
-
691
- // Now delete all the items
692
- const promises = keys.map(key =>
693
- new Promise((resolveDelete, rejectDelete) => {
694
- const deletePath = password ?
695
- user.get('private').get(tableName).get(key) :
696
- holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
697
-
698
- deletePath.put(null, ack => {
699
- if (ack.err) {
700
- console.error(`Failed to delete ${key}:`, ack.err);
701
- rejectDelete(new Error(ack.err));
702
- } else {
703
- resolveDelete();
704
- }
705
- });
706
- })
707
- );
708
-
709
- try {
710
- await Promise.all(promises);
711
- // Finally delete the table itself
712
- dataPath.put(null);
713
- clearTimeout(timeout);
714
- resolve(true);
715
- } catch (error) {
716
- reject(error);
717
- }
718
- });
719
- } catch (error) {
720
- reject(error);
721
- }
722
- });
723
- } catch (error) {
724
- console.error('Error in deleteAllGlobal:', error);
725
- throw error;
726
- }
727
- }
728
-
729
- // Export all global operations as default
730
- export default {
731
- putGlobal,
732
- getGlobal,
733
- getAllGlobal,
734
- deleteGlobal,
735
- deleteAllGlobal
736
- };