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.
- package/.env.example +36 -0
- package/.eslintrc.json +16 -0
- package/.prettierrc.json +7 -0
- package/README.md +476 -531
- package/bin/holosphere-activitypub.js +158 -0
- package/cleanup-test-data.js +204 -0
- package/examples/demo.html +1333 -0
- package/examples/example-bot.js +197 -0
- package/package.json +47 -87
- package/scripts/check-bundle-size.js +54 -0
- package/scripts/check-quest-ids.js +77 -0
- package/scripts/import-holons.js +578 -0
- package/scripts/publish-to-relay.js +101 -0
- package/scripts/read-example.js +186 -0
- package/scripts/relay-diagnostic.js +59 -0
- package/scripts/relay-example.js +179 -0
- package/scripts/resync-to-relay.js +245 -0
- package/scripts/revert-import.js +196 -0
- package/scripts/test-hybrid-mode.js +108 -0
- package/scripts/test-local-storage.js +63 -0
- package/scripts/test-nostr-direct.js +55 -0
- package/scripts/test-read-data.js +45 -0
- package/scripts/test-write-read.js +63 -0
- package/scripts/verify-import.js +95 -0
- package/scripts/verify-relay-data.js +139 -0
- package/src/ai/aggregation.js +319 -0
- package/src/ai/breakdown.js +511 -0
- package/src/ai/classifier.js +217 -0
- package/src/ai/council.js +228 -0
- package/src/ai/embeddings.js +279 -0
- package/src/ai/federation-ai.js +324 -0
- package/src/ai/h3-ai.js +955 -0
- package/src/ai/index.js +112 -0
- package/src/ai/json-ops.js +225 -0
- package/src/ai/llm-service.js +205 -0
- package/src/ai/nl-query.js +223 -0
- package/src/ai/relationships.js +353 -0
- package/src/ai/schema-extractor.js +218 -0
- package/src/ai/spatial.js +293 -0
- package/src/ai/tts.js +194 -0
- package/src/content/social-protocols.js +168 -0
- package/src/core/holosphere.js +273 -0
- package/src/crypto/secp256k1.js +259 -0
- package/src/federation/discovery.js +334 -0
- package/src/federation/hologram.js +1042 -0
- package/src/federation/registry.js +386 -0
- package/src/hierarchical/upcast.js +110 -0
- package/src/index.js +2669 -0
- package/src/schema/validator.js +91 -0
- package/src/spatial/h3-operations.js +110 -0
- package/src/storage/backend-factory.js +125 -0
- package/src/storage/backend-interface.js +142 -0
- package/src/storage/backends/activitypub/server.js +653 -0
- package/src/storage/backends/activitypub-backend.js +272 -0
- package/src/storage/backends/gundb-backend.js +233 -0
- package/src/storage/backends/nostr-backend.js +136 -0
- package/src/storage/filesystem-storage-browser.js +41 -0
- package/src/storage/filesystem-storage.js +138 -0
- package/src/storage/global-tables.js +81 -0
- package/src/storage/gun-async.js +281 -0
- package/src/storage/gun-wrapper.js +221 -0
- package/src/storage/indexeddb-storage.js +122 -0
- package/src/storage/key-storage-simple.js +76 -0
- package/src/storage/key-storage.js +136 -0
- package/src/storage/memory-storage.js +59 -0
- package/src/storage/migration.js +338 -0
- package/src/storage/nostr-async.js +811 -0
- package/src/storage/nostr-client.js +939 -0
- package/src/storage/nostr-wrapper.js +211 -0
- package/src/storage/outbox-queue.js +208 -0
- package/src/storage/persistent-storage.js +109 -0
- package/src/storage/sync-service.js +164 -0
- package/src/subscriptions/manager.js +142 -0
- package/test-ai-real-api.js +202 -0
- package/tests/unit/ai/aggregation.test.js +295 -0
- package/tests/unit/ai/breakdown.test.js +446 -0
- package/tests/unit/ai/classifier.test.js +294 -0
- package/tests/unit/ai/council.test.js +262 -0
- package/tests/unit/ai/embeddings.test.js +384 -0
- package/tests/unit/ai/federation-ai.test.js +344 -0
- package/tests/unit/ai/h3-ai.test.js +458 -0
- package/tests/unit/ai/index.test.js +304 -0
- package/tests/unit/ai/json-ops.test.js +307 -0
- package/tests/unit/ai/llm-service.test.js +390 -0
- package/tests/unit/ai/nl-query.test.js +383 -0
- package/tests/unit/ai/relationships.test.js +311 -0
- package/tests/unit/ai/schema-extractor.test.js +384 -0
- package/tests/unit/ai/spatial.test.js +279 -0
- package/tests/unit/ai/tts.test.js +279 -0
- package/tests/unit/content.test.js +332 -0
- package/tests/unit/contract/core.test.js +88 -0
- package/tests/unit/contract/crypto.test.js +198 -0
- package/tests/unit/contract/data.test.js +223 -0
- package/tests/unit/contract/federation.test.js +181 -0
- package/tests/unit/contract/hierarchical.test.js +113 -0
- package/tests/unit/contract/schema.test.js +114 -0
- package/tests/unit/contract/social.test.js +217 -0
- package/tests/unit/contract/spatial.test.js +110 -0
- package/tests/unit/contract/subscriptions.test.js +128 -0
- package/tests/unit/contract/utils.test.js +159 -0
- package/tests/unit/core.test.js +152 -0
- package/tests/unit/crypto.test.js +328 -0
- package/tests/unit/federation.test.js +234 -0
- package/tests/unit/gun-async.test.js +252 -0
- package/tests/unit/hierarchical.test.js +399 -0
- package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
- package/tests/unit/integration/scenario-02-federation.test.js +76 -0
- package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
- package/tests/unit/integration/scenario-04-validation.test.js +129 -0
- package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
- package/tests/unit/integration/scenario-06-social.test.js +135 -0
- package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
- package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
- package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
- package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
- package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
- package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
- package/tests/unit/performance/benchmark.test.js +85 -0
- package/tests/unit/schema.test.js +213 -0
- package/tests/unit/spatial.test.js +158 -0
- package/tests/unit/storage.test.js +195 -0
- package/tests/unit/subscriptions.test.js +328 -0
- package/tests/unit/test-data-permanence-debug.js +197 -0
- package/tests/unit/test-data-permanence.js +340 -0
- package/tests/unit/test-key-persistence-fixed.js +148 -0
- package/tests/unit/test-key-persistence.js +172 -0
- package/tests/unit/test-relay-permanence.js +376 -0
- package/tests/unit/test-second-node.js +95 -0
- package/tests/unit/test-simple-write.js +89 -0
- package/vite.config.js +49 -0
- package/vitest.config.js +20 -0
- package/FEDERATION.md +0 -213
- package/compute.js +0 -298
- package/content.js +0 -1022
- package/federation.js +0 -1234
- package/global.js +0 -736
- package/hexlib.js +0 -335
- package/hologram.js +0 -183
- package/holosphere-bundle.esm.js +0 -34549
- package/holosphere-bundle.js +0 -34580
- package/holosphere-bundle.min.js +0 -49
- package/holosphere.d.ts +0 -604
- package/holosphere.js +0 -739
- package/node.js +0 -246
- package/schema.js +0 -139
- 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
|
-
};
|