holosphere 1.1.20 → 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 +483 -367
- 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 -980
- 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 -33256
- package/holosphere-bundle.js +0 -33287
- package/holosphere-bundle.min.js +0 -39
- package/holosphere.d.ts +0 -601
- package/holosphere.js +0 -719
- package/node.js +0 -246
- package/schema.js +0 -139
- package/utils.js +0 -302
package/node.js
DELETED
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
// holo_node.js
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Stores a specific gun node in a given holon and lens.
|
|
5
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
6
|
-
* @param {string} holon - The holon identifier.
|
|
7
|
-
* @param {string} lens - The lens under which to store the node.
|
|
8
|
-
* @param {object} data - The node to store.
|
|
9
|
-
*/
|
|
10
|
-
export async function putNode(holoInstance, holon, lens, data) {
|
|
11
|
-
if (!holon || !lens || !data) {
|
|
12
|
-
throw new Error('putNode: Missing required parameters');
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return new Promise((resolve, reject) => {
|
|
16
|
-
try {
|
|
17
|
-
// Remove isHologram field before storing - NO LONGER NEEDED
|
|
18
|
-
// if (data && data.isHologram !== undefined) {
|
|
19
|
-
// delete data.isHologram;
|
|
20
|
-
// }
|
|
21
|
-
|
|
22
|
-
// Check if the data being stored is a hologram
|
|
23
|
-
const isHologram = data.value && holoInstance.isHologram(data.value);
|
|
24
|
-
|
|
25
|
-
holoInstance.gun.get(holoInstance.appname)
|
|
26
|
-
.get(holon)
|
|
27
|
-
.get(lens)
|
|
28
|
-
.get('value') // Store at 'value' key
|
|
29
|
-
.put(data.value, ack => { // Store the value directly
|
|
30
|
-
if (ack.err) {
|
|
31
|
-
reject(new Error(ack.err));
|
|
32
|
-
} else {
|
|
33
|
-
// --- Start: Hologram Tracking Logic (for data *being put*, if it's a hologram) ---
|
|
34
|
-
if (isHologram) {
|
|
35
|
-
try {
|
|
36
|
-
const storedDataSoulInfo = holoInstance.parseSoulPath(data.value.soul);
|
|
37
|
-
if (storedDataSoulInfo) {
|
|
38
|
-
const targetNodeRef = holoInstance.getNodeRef(data.value.soul); // Target of the data *being put*
|
|
39
|
-
// Soul of the hologram that was *actually stored* at holon/lens/value
|
|
40
|
-
const storedHologramInstanceSoul = `${holoInstance.appname}/${holon}/${lens}/value`;
|
|
41
|
-
|
|
42
|
-
targetNodeRef.get('_holograms').get(storedHologramInstanceSoul).put(true);
|
|
43
|
-
} else {
|
|
44
|
-
console.warn(`Data (ID: ${data.id}) being put is a hologram, but could not parse its soul ${data.value.soul} for tracking.`);
|
|
45
|
-
}
|
|
46
|
-
} catch (trackingError) {
|
|
47
|
-
console.warn(`Error updating _holograms set for the target of the data being put (data ID: ${data.id}, soul: ${data.value.soul}):`, trackingError);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
// --- End: Hologram Tracking Logic ---
|
|
51
|
-
|
|
52
|
-
resolve(true);
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
} catch (error) {
|
|
56
|
-
reject(error);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Retrieves a specific gun node from the specified holon and lens.
|
|
63
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
64
|
-
* @param {string} holon - The holon identifier.
|
|
65
|
-
* @param {string} lens - The lens identifier.
|
|
66
|
-
* @param {string} key - The specific key to retrieve.
|
|
67
|
-
* @returns {Promise<any>} - The retrieved node or null if not found.
|
|
68
|
-
*/
|
|
69
|
-
export async function getNode(holoInstance, holon, lens, key) {
|
|
70
|
-
if (!holon || !lens || !key) {
|
|
71
|
-
throw new Error('getNode: Missing required parameters');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
try {
|
|
76
|
-
holoInstance.gun.get(holoInstance.appname)
|
|
77
|
-
.get(holon)
|
|
78
|
-
.get(lens)
|
|
79
|
-
.get(key)
|
|
80
|
-
.once((data) => {
|
|
81
|
-
if (!data) {
|
|
82
|
-
resolve(null);
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
resolve(data); // Return the data directly
|
|
86
|
-
});
|
|
87
|
-
} catch (error) {
|
|
88
|
-
reject(error);
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Retrieves a Gun node reference using its soul path
|
|
95
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
96
|
-
* @param {string} soul - The soul path of the node
|
|
97
|
-
* @returns {Gun.ChainReference} - The Gun node reference
|
|
98
|
-
*/
|
|
99
|
-
export function getNodeRef(holoInstance, soul) {
|
|
100
|
-
if (typeof soul !== 'string' || !soul) {
|
|
101
|
-
throw new Error('getNodeRef: Invalid soul parameter');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
const parts = soul.split('/').filter(part => {
|
|
105
|
-
if (!part.trim() || /[<>:"/\\|?*]/.test(part)) { // Escaped backslash for regex
|
|
106
|
-
throw new Error('getNodeRef: Invalid path segment');
|
|
107
|
-
}
|
|
108
|
-
return part.trim();
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
if (parts.length === 0) {
|
|
112
|
-
throw new Error('getNodeRef: Invalid soul format');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
let ref = holoInstance.gun.get(holoInstance.appname);
|
|
116
|
-
parts.forEach(part => {
|
|
117
|
-
ref = ref.get(part);
|
|
118
|
-
});
|
|
119
|
-
return ref;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* Retrieves a node directly using its soul path
|
|
124
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
125
|
-
* @param {string} soul - The soul path of the node
|
|
126
|
-
* @returns {Promise<any>} - The retrieved node or null if not found.
|
|
127
|
-
*/
|
|
128
|
-
export async function getNodeBySoul(holoInstance, soul) {
|
|
129
|
-
if (!soul) {
|
|
130
|
-
throw new Error('getNodeBySoul: Missing soul parameter');
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
console.log(`getNodeBySoul: Accessing soul ${soul}`);
|
|
134
|
-
|
|
135
|
-
return new Promise((resolve, reject) => {
|
|
136
|
-
try {
|
|
137
|
-
const ref = getNodeRef(holoInstance, soul); // Use the exported getNodeRef
|
|
138
|
-
ref.once((data) => {
|
|
139
|
-
console.log(`getNodeBySoul: Retrieved data:`, data);
|
|
140
|
-
if (!data) {
|
|
141
|
-
resolve(null);
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
resolve(data); // Return the data directly
|
|
145
|
-
});
|
|
146
|
-
} catch (error) {
|
|
147
|
-
console.error(`getNodeBySoul error:`, error);
|
|
148
|
-
reject(error);
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Deletes a specific gun node from a given holon and lens.
|
|
155
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
156
|
-
* @param {string} holon - The holon identifier.
|
|
157
|
-
* @param {string} lens - The lens identifier.
|
|
158
|
-
* @param {string} key - The key of the node to delete.
|
|
159
|
-
* @returns {Promise<boolean>} - Returns true if successful
|
|
160
|
-
*/
|
|
161
|
-
export async function deleteNode(holoInstance, holon, lens, key) {
|
|
162
|
-
if (!holon || !lens || !key) {
|
|
163
|
-
throw new Error('deleteNode: Missing required parameters');
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
try {
|
|
167
|
-
const dataPath = holoInstance.gun.get(holoInstance.appname).get(holon).get(lens).get(key);
|
|
168
|
-
|
|
169
|
-
// --- Start: Hologram Tracking Removal ---
|
|
170
|
-
let trackingRemovalPromise = Promise.resolve(); // Default to resolved promise
|
|
171
|
-
|
|
172
|
-
// 1. Get the data first to check if it's a hologram
|
|
173
|
-
const rawDataToDelete = await new Promise((resolve) => dataPath.once(resolve));
|
|
174
|
-
let dataToDelete = null;
|
|
175
|
-
try {
|
|
176
|
-
if (typeof rawDataToDelete === 'string') {
|
|
177
|
-
dataToDelete = JSON.parse(rawDataToDelete);
|
|
178
|
-
} else {
|
|
179
|
-
// Handle cases where it might already be an object (though likely string)
|
|
180
|
-
dataToDelete = rawDataToDelete;
|
|
181
|
-
}
|
|
182
|
-
} catch(e) {
|
|
183
|
-
console.warn("[deleteNode] Could not JSON parse data for deletion check:", rawDataToDelete, e);
|
|
184
|
-
dataToDelete = null; // Ensure it's null if parsing fails
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
// 2. If it is a hologram, try to remove its reference from the target
|
|
188
|
-
const isDataHologram = dataToDelete && holoInstance.isHologram(dataToDelete);
|
|
189
|
-
|
|
190
|
-
if (isDataHologram) {
|
|
191
|
-
try {
|
|
192
|
-
const targetSoul = dataToDelete.soul;
|
|
193
|
-
const targetSoulInfo = holoInstance.parseSoulPath(targetSoul);
|
|
194
|
-
|
|
195
|
-
if (targetSoulInfo) {
|
|
196
|
-
const targetNodeRef = holoInstance.getNodeRef(targetSoul);
|
|
197
|
-
// putNode stores at the 'value' key, not at the data.id key
|
|
198
|
-
const deletedHologramSoul = `${holoInstance.appname}/${holon}/${lens}/value`;
|
|
199
|
-
|
|
200
|
-
// Create a promise that resolves when the hologram is removed from the list
|
|
201
|
-
trackingRemovalPromise = new Promise((resolveTrack) => { // No reject needed, just warn on error
|
|
202
|
-
targetNodeRef.get('_holograms').get(deletedHologramSoul).put(null, (ack) => { // Remove the hologram entry completely
|
|
203
|
-
if (ack.err) {
|
|
204
|
-
console.warn(`[deleteNode] Error removing hologram ${deletedHologramSoul} from target ${targetSoul}:`, ack.err);
|
|
205
|
-
}
|
|
206
|
-
resolveTrack(); // Resolve regardless of ack error to not block main delete
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
} else {
|
|
210
|
-
console.warn(`Could not parse target soul ${targetSoul} for hologram tracking removal during deleteNode.`);
|
|
211
|
-
}
|
|
212
|
-
} catch (trackingError) {
|
|
213
|
-
console.warn(`Error initiating hologram reference removal from target ${dataToDelete.soul} during deleteNode:`, trackingError);
|
|
214
|
-
// Ensure trackingRemovalPromise remains resolved if setup fails
|
|
215
|
-
trackingRemovalPromise = Promise.resolve();
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
// --- End: Hologram Tracking Removal ---
|
|
219
|
-
|
|
220
|
-
// 3. Wait for the tracking removal attempt to be acknowledged
|
|
221
|
-
await trackingRemovalPromise;
|
|
222
|
-
|
|
223
|
-
// 4. Proceed with the actual deletion of the hologram node itself
|
|
224
|
-
return new Promise((resolve, reject) => {
|
|
225
|
-
dataPath.put(null, ack => {
|
|
226
|
-
if (ack.err) {
|
|
227
|
-
reject(new Error(ack.err));
|
|
228
|
-
} else {
|
|
229
|
-
resolve(true);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
} catch (error) {
|
|
234
|
-
console.error('Error in deleteNode:', error);
|
|
235
|
-
throw error;
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Export all node operations as default
|
|
240
|
-
export default {
|
|
241
|
-
putNode,
|
|
242
|
-
getNode,
|
|
243
|
-
getNodeRef,
|
|
244
|
-
getNodeBySoul,
|
|
245
|
-
deleteNode
|
|
246
|
-
};
|
package/schema.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
// holo_schema.js
|
|
2
|
-
|
|
3
|
-
import Ajv2019 from 'ajv/dist/2019.js';
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Sets the JSON schema for a specific lens.
|
|
7
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
8
|
-
* @param {string} lens - The lens identifier.
|
|
9
|
-
* @param {object} schema - The JSON schema to set.
|
|
10
|
-
* @returns {Promise<boolean>} - Resolves when the schema is set.
|
|
11
|
-
*/
|
|
12
|
-
export async function setSchema(holoInstance, lens, schema) {
|
|
13
|
-
if (!lens || !schema) {
|
|
14
|
-
throw new Error('setSchema: Missing required parameters');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// Basic schema validation
|
|
18
|
-
if (!schema.type || typeof schema.type !== 'string') {
|
|
19
|
-
throw new Error('setSchema: Schema must have a type field');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const metaSchema = {
|
|
23
|
-
type: 'object',
|
|
24
|
-
required: ['type', 'properties'],
|
|
25
|
-
properties: {
|
|
26
|
-
type: { type: 'string' },
|
|
27
|
-
properties: {
|
|
28
|
-
type: 'object',
|
|
29
|
-
additionalProperties: {
|
|
30
|
-
type: 'object',
|
|
31
|
-
required: ['type'],
|
|
32
|
-
properties: {
|
|
33
|
-
type: { type: 'string' }
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
required: {
|
|
38
|
-
type: 'array',
|
|
39
|
-
items: { type: 'string' }
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// Use the validator from the instance
|
|
45
|
-
const valid = holoInstance.validator.validate(metaSchema, schema);
|
|
46
|
-
if (!valid) {
|
|
47
|
-
throw new Error(`Invalid schema structure: ${JSON.stringify(holoInstance.validator.errors)}`);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (!schema.properties || typeof schema.properties !== 'object') {
|
|
51
|
-
throw new Error('Schema must have properties in strict mode');
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (!schema.required || !Array.isArray(schema.required) || schema.required.length === 0) {
|
|
55
|
-
throw new Error('Schema must have required fields in strict mode');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Store schema in global table with lens as key using instance's method
|
|
59
|
-
await holoInstance.putGlobal('schemas', {
|
|
60
|
-
id: lens,
|
|
61
|
-
schema: schema,
|
|
62
|
-
timestamp: Date.now()
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Update the instance's cache with the new schema
|
|
66
|
-
holoInstance.schemaCache.set(lens, {
|
|
67
|
-
schema,
|
|
68
|
-
timestamp: Date.now()
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Retrieves the JSON schema for a specific lens.
|
|
76
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
77
|
-
* @param {string} lens - The lens identifier.
|
|
78
|
-
* @param {object} [options] - Additional options
|
|
79
|
-
* @param {boolean} [options.useCache=true] - Whether to use the schema cache
|
|
80
|
-
* @param {number} [options.maxCacheAge=3600000] - Maximum cache age in milliseconds (default: 1 hour)
|
|
81
|
-
* @returns {Promise<object|null>} - The retrieved schema or null if not found.
|
|
82
|
-
*/
|
|
83
|
-
export async function getSchema(holoInstance, lens, options = {}) {
|
|
84
|
-
if (!lens) {
|
|
85
|
-
throw new Error('getSchema: Missing lens parameter');
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const { useCache = true, maxCacheAge = 3600000 } = options;
|
|
89
|
-
|
|
90
|
-
// Check instance's cache first if enabled
|
|
91
|
-
if (useCache && holoInstance.schemaCache.has(lens)) {
|
|
92
|
-
const cached = holoInstance.schemaCache.get(lens);
|
|
93
|
-
const cacheAge = Date.now() - cached.timestamp;
|
|
94
|
-
|
|
95
|
-
// Use cache if it's fresh enough
|
|
96
|
-
if (cacheAge < maxCacheAge) {
|
|
97
|
-
return cached.schema;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Cache miss or expired, fetch from storage using instance's method
|
|
102
|
-
const schemaData = await holoInstance.getGlobal('schemas', lens);
|
|
103
|
-
|
|
104
|
-
if (!schemaData || !schemaData.schema) {
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Update instance's cache with fetched schema
|
|
109
|
-
holoInstance.schemaCache.set(lens, {
|
|
110
|
-
schema: schemaData.schema,
|
|
111
|
-
timestamp: Date.now()
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
return schemaData.schema;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Clears the schema cache or a specific schema from the cache.
|
|
119
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
120
|
-
* @param {string} [lens] - Optional lens to clear from cache. If not provided, clears entire cache.
|
|
121
|
-
* @returns {boolean} - Returns true if successful
|
|
122
|
-
*/
|
|
123
|
-
export function clearSchemaCache(holoInstance, lens = null) {
|
|
124
|
-
if (lens) {
|
|
125
|
-
// Clear specific schema from instance's cache
|
|
126
|
-
return holoInstance.schemaCache.delete(lens);
|
|
127
|
-
} else {
|
|
128
|
-
// Clear entire instance's cache
|
|
129
|
-
holoInstance.schemaCache.clear();
|
|
130
|
-
return true;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Export all schema operations as default
|
|
135
|
-
export default {
|
|
136
|
-
setSchema,
|
|
137
|
-
getSchema,
|
|
138
|
-
clearSchemaCache
|
|
139
|
-
};
|
package/utils.js
DELETED
|
@@ -1,302 +0,0 @@
|
|
|
1
|
-
// holo_utils.js
|
|
2
|
-
import * as h3 from 'h3-js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Converts latitude and longitude to a holon identifier.
|
|
6
|
-
* @param {number} lat - The latitude.
|
|
7
|
-
* @param {number} lng - The longitude.
|
|
8
|
-
* @param {number} resolution - The resolution level.
|
|
9
|
-
* @returns {Promise<string>} - The resulting holon identifier.
|
|
10
|
-
*/
|
|
11
|
-
export async function getHolon(lat, lng, resolution) { // Doesn't need holoInstance
|
|
12
|
-
return h3.latLngToCell(lat, lng, resolution);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Retrieves all containing holonagons at all scales for given coordinates.
|
|
17
|
-
* @param {number} lat - The latitude.
|
|
18
|
-
* @param {number} lng - The longitude.
|
|
19
|
-
* @returns {Array<string>} - List of holon identifiers.
|
|
20
|
-
*/
|
|
21
|
-
export function getScalespace(lat, lng) { // Doesn't need holoInstance
|
|
22
|
-
let list = []
|
|
23
|
-
let cell = h3.latLngToCell(lat, lng, 14);
|
|
24
|
-
list.push(cell)
|
|
25
|
-
for (let i = 13; i >= 0; i--) {
|
|
26
|
-
list.push(h3.cellToParent(cell, i))
|
|
27
|
-
}
|
|
28
|
-
return list
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Retrieves all containing holonagons at all scales for a given holon.
|
|
33
|
-
* @param {string} holon - The holon identifier.
|
|
34
|
-
* @returns {Array<string>} - List of holon identifiers.
|
|
35
|
-
*/
|
|
36
|
-
export function getHolonScalespace(holon) { // Doesn't need holoInstance
|
|
37
|
-
let list = []
|
|
38
|
-
let res = h3.getResolution(holon)
|
|
39
|
-
for (let i = res; i >= 0; i--) {
|
|
40
|
-
list.push(h3.cellToParent(holon, i))
|
|
41
|
-
}
|
|
42
|
-
return list
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Subscribes to changes in a specific holon and lens.
|
|
47
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
48
|
-
* @param {string} holon - The holon identifier.
|
|
49
|
-
* @param {string} lens - The lens to subscribe to.
|
|
50
|
-
* @param {function} callback - The callback to execute on changes.
|
|
51
|
-
* @returns {Promise<object>} - Subscription object with unsubscribe method
|
|
52
|
-
*/
|
|
53
|
-
export async function subscribe(holoInstance, holon, lens, callback) {
|
|
54
|
-
if (!holon || !lens) {
|
|
55
|
-
throw new Error('subscribe: Missing holon or lens parameters:', holon, lens);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
if (!callback || typeof callback !== 'function') {
|
|
59
|
-
throw new Error('subscribe: Callback must be a function');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
const subscriptionId = holoInstance.generateId(); // Use instance's generateId
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
// Get the Gun chain up to the map()
|
|
66
|
-
const mapChain = holoInstance.gun.get(holoInstance.appname).get(holon).get(lens).map();
|
|
67
|
-
|
|
68
|
-
// Create the subscription by calling .on() on the map chain
|
|
69
|
-
const gunListener = mapChain.on(async (data, key) => { // Renamed variable
|
|
70
|
-
// Check if subscription ID still exists (might have been unsubscribed)
|
|
71
|
-
if (!holoInstance.subscriptions[subscriptionId]) {
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
if (data) {
|
|
76
|
-
try {
|
|
77
|
-
let parsed = await holoInstance.parse(data);
|
|
78
|
-
if (parsed && holoInstance.isHologram(parsed)) {
|
|
79
|
-
const resolved = await holoInstance.resolveHologram(parsed, { followHolograms: true });
|
|
80
|
-
if (resolved !== parsed) {
|
|
81
|
-
parsed = resolved;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// Check again if subscription ID still exists before calling callback
|
|
86
|
-
if (holoInstance.subscriptions[subscriptionId]) {
|
|
87
|
-
callback(parsed, key);
|
|
88
|
-
}
|
|
89
|
-
} catch (error) {
|
|
90
|
-
console.error('Error processing subscribed data:', error);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// Store the subscription with its ID on the instance
|
|
96
|
-
holoInstance.subscriptions[subscriptionId] = {
|
|
97
|
-
id: subscriptionId,
|
|
98
|
-
holon,
|
|
99
|
-
lens,
|
|
100
|
-
callback,
|
|
101
|
-
mapChain: mapChain, // Store the map chain
|
|
102
|
-
gunListener: gunListener // Store the listener too (optional, maybe needed for close?)
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// Return an object with unsubscribe method
|
|
106
|
-
return {
|
|
107
|
-
unsubscribe: async () => {
|
|
108
|
-
const sub = holoInstance.subscriptions[subscriptionId];
|
|
109
|
-
if (!sub) {
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
// Turn off the Gun subscription using the stored mapChain reference
|
|
115
|
-
if (sub.mapChain) { // Check if mapChain exists
|
|
116
|
-
sub.mapChain.off(); // Call off() on the chain where .on() was attached
|
|
117
|
-
// Optional: Add delay back? Let's omit for now.
|
|
118
|
-
// await new Promise(res => setTimeout(res, 50));
|
|
119
|
-
} // We might not need to call off() on gunListener explicitly
|
|
120
|
-
|
|
121
|
-
// Remove from subscriptions object AFTER turning off listener
|
|
122
|
-
delete holoInstance.subscriptions[subscriptionId];
|
|
123
|
-
} catch (error) {
|
|
124
|
-
console.error(`Error during unsubscribe logic for ${subscriptionId}:`, error);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
};
|
|
128
|
-
} catch (error) {
|
|
129
|
-
console.error('Error creating subscription:', error);
|
|
130
|
-
throw error;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Notifies subscribers about data changes
|
|
136
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
137
|
-
* @param {object} data - The data to notify about
|
|
138
|
-
* @private
|
|
139
|
-
*/
|
|
140
|
-
export function notifySubscribers(holoInstance, data) {
|
|
141
|
-
if (!data || !data.holon || !data.lens) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
Object.values(holoInstance.subscriptions).forEach(subscription => {
|
|
147
|
-
if (subscription.holon === data.holon &&
|
|
148
|
-
subscription.lens === data.lens) {
|
|
149
|
-
try {
|
|
150
|
-
if (subscription.callback && typeof subscription.callback === 'function') {
|
|
151
|
-
subscription.callback(data);
|
|
152
|
-
}
|
|
153
|
-
} catch (error) {
|
|
154
|
-
console.warn('Error in subscription callback:', error);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
});
|
|
158
|
-
} catch (error) {
|
|
159
|
-
console.warn('Error notifying subscribers:', error);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
// Add ID generation method
|
|
164
|
-
export function generateId() { // Doesn't need holoInstance
|
|
165
|
-
return Date.now().toString(10) + Math.random().toString(2);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Closes the HoloSphere instance and cleans up resources.
|
|
170
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
171
|
-
* @returns {Promise<void>}
|
|
172
|
-
*/
|
|
173
|
-
export async function close(holoInstance) {
|
|
174
|
-
try {
|
|
175
|
-
if (holoInstance.gun) {
|
|
176
|
-
// Unsubscribe from all subscriptions
|
|
177
|
-
const subscriptionIds = Object.keys(holoInstance.subscriptions);
|
|
178
|
-
for (const id of subscriptionIds) {
|
|
179
|
-
try {
|
|
180
|
-
const subscription = holoInstance.subscriptions[id];
|
|
181
|
-
if (subscription) {
|
|
182
|
-
// Turn off the Gun subscription using the stored mapChain reference
|
|
183
|
-
if (subscription.mapChain) {
|
|
184
|
-
subscription.mapChain.off();
|
|
185
|
-
} // Also turn off listener directly? Might be redundant.
|
|
186
|
-
// if (subscription.gunListener) {
|
|
187
|
-
// subscription.gunListener.off();
|
|
188
|
-
// }
|
|
189
|
-
}
|
|
190
|
-
} catch (error) {
|
|
191
|
-
console.warn(`Error cleaning up subscription ${id}:`, error);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
// Clear subscriptions
|
|
196
|
-
holoInstance.subscriptions = {};
|
|
197
|
-
|
|
198
|
-
// Clear schema cache using instance method
|
|
199
|
-
holoInstance.clearSchemaCache();
|
|
200
|
-
|
|
201
|
-
// Close Gun connections
|
|
202
|
-
if (holoInstance.gun.back) {
|
|
203
|
-
try {
|
|
204
|
-
// Clean up mesh connections
|
|
205
|
-
const mesh = holoInstance.gun.back('opt.mesh');
|
|
206
|
-
if (mesh) {
|
|
207
|
-
// Clean up mesh.hear
|
|
208
|
-
if (mesh.hear) {
|
|
209
|
-
try {
|
|
210
|
-
// Safely clear mesh.hear without modifying function properties
|
|
211
|
-
const hearKeys = Object.keys(mesh.hear);
|
|
212
|
-
for (const key of hearKeys) {
|
|
213
|
-
// Check if it's an array before trying to clear it
|
|
214
|
-
if (Array.isArray(mesh.hear[key])) {
|
|
215
|
-
mesh.hear[key] = [];
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Create a new empty object for mesh.hear
|
|
220
|
-
// Only if mesh.hear is not a function
|
|
221
|
-
if (typeof mesh.hear !== 'function') {
|
|
222
|
-
mesh.hear = {};
|
|
223
|
-
}
|
|
224
|
-
} catch (meshError) {
|
|
225
|
-
console.warn('Error cleaning up Gun mesh hear:', meshError);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Close any open sockets in the mesh
|
|
230
|
-
if (mesh.way) {
|
|
231
|
-
try {
|
|
232
|
-
Object.values(mesh.way).forEach(connection => {
|
|
233
|
-
if (connection && connection.wire && connection.wire.close) {
|
|
234
|
-
connection.wire.close();
|
|
235
|
-
}
|
|
236
|
-
});
|
|
237
|
-
} catch (sockError) {
|
|
238
|
-
console.warn('Error closing mesh sockets:', sockError);
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
// Clear the peers list
|
|
243
|
-
if (mesh.opt && mesh.opt.peers) {
|
|
244
|
-
mesh.opt.peers = {};
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Attempt to clean up any TCP connections
|
|
249
|
-
if (holoInstance.gun.back('opt.web')) {
|
|
250
|
-
try {
|
|
251
|
-
const server = holoInstance.gun.back('opt.web');
|
|
252
|
-
if (server && server.close) {
|
|
253
|
-
server.close();
|
|
254
|
-
}
|
|
255
|
-
} catch (webError) {
|
|
256
|
-
console.warn('Error closing web server:', webError);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
} catch (error) {
|
|
260
|
-
console.warn('Error accessing Gun mesh:', error);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
// Clear all Gun instance listeners
|
|
265
|
-
try {
|
|
266
|
-
holoInstance.gun.off();
|
|
267
|
-
} catch (error) {
|
|
268
|
-
console.warn('Error turning off Gun listeners:', error);
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Wait a moment for cleanup to complete
|
|
272
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
console.log('HoloSphere instance closed successfully');
|
|
276
|
-
} catch (error) {
|
|
277
|
-
console.error('Error closing HoloSphere instance:', error);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Creates a namespaced username for Gun authentication
|
|
283
|
-
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
284
|
-
* @param {string} holonId - The holon ID
|
|
285
|
-
* @returns {string} - Namespaced username
|
|
286
|
-
*/
|
|
287
|
-
export function userName(holoInstance, holonId) {
|
|
288
|
-
if (!holonId) return null;
|
|
289
|
-
return `${holoInstance.appname}:${holonId}`;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Export all utility operations as default
|
|
293
|
-
export default {
|
|
294
|
-
getHolon,
|
|
295
|
-
getScalespace,
|
|
296
|
-
getHolonScalespace,
|
|
297
|
-
subscribe,
|
|
298
|
-
notifySubscribers,
|
|
299
|
-
generateId,
|
|
300
|
-
close,
|
|
301
|
-
userName
|
|
302
|
-
};
|