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/federation.js
DELETED
|
@@ -1,1234 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Federation functionality for HoloSphere
|
|
3
|
-
* Provides methods for creating, managing, and using federated spaces
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import * as h3 from 'h3-js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Creates a federation relationship between two spaces
|
|
10
|
-
* Federation is bidirectional by default, and data propagation uses soul references by default.
|
|
11
|
-
*
|
|
12
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
13
|
-
* @param {string} spaceId1 - The first space ID
|
|
14
|
-
* @param {string} spaceId2 - The second space ID
|
|
15
|
-
* @param {string} [password1] - Optional password for the first space
|
|
16
|
-
* @param {string} [password2] - Optional password for the second space
|
|
17
|
-
* @param {boolean} [bidirectional=true] - Whether to set up bidirectional notifications (default: true)
|
|
18
|
-
* @param {object} [lensConfig] - Optional lens-specific configuration
|
|
19
|
-
* @param {string[]} [lensConfig.federate] - List of lenses to federate (default: all)
|
|
20
|
-
* @param {string[]} [lensConfig.notify] - List of lenses to notify (default: all)
|
|
21
|
-
* @returns {Promise<boolean>} - True if federation was created successfully
|
|
22
|
-
*/
|
|
23
|
-
export async function federate(holosphere, spaceId1, spaceId2, password1 = null, password2 = null, bidirectional = true, lensConfig = {}) {
|
|
24
|
-
if (!spaceId1 || !spaceId2) {
|
|
25
|
-
throw new Error('federate: Missing required space IDs');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// Prevent self-federation
|
|
29
|
-
if (spaceId1 === spaceId2) {
|
|
30
|
-
throw new Error('Cannot federate a space with itself');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Validate lens configuration
|
|
34
|
-
const { federate = [], notify = [] } = lensConfig;
|
|
35
|
-
if (!Array.isArray(federate) || !Array.isArray(notify)) {
|
|
36
|
-
throw new Error('federate: lensConfig.federate and lensConfig.notify must be arrays');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Use the provided lens configurations directly
|
|
40
|
-
const federateLenses = federate;
|
|
41
|
-
const notifyLenses = notify;
|
|
42
|
-
|
|
43
|
-
try {
|
|
44
|
-
// Get or create federation info for first space (A)
|
|
45
|
-
let fedInfo1 = null;
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
fedInfo1 = await holosphere.getGlobal('federation', spaceId1, password1);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (fedInfo1 == null) {
|
|
53
|
-
fedInfo1 = {
|
|
54
|
-
id: spaceId1,
|
|
55
|
-
name: spaceId1,
|
|
56
|
-
federation: [],
|
|
57
|
-
notify: [],
|
|
58
|
-
lensConfig: {}, // New field for lens-specific settings
|
|
59
|
-
timestamp: Date.now()
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Ensure arrays and lensConfig exist
|
|
64
|
-
if (!fedInfo1.federation) fedInfo1.federation = [];
|
|
65
|
-
if (!fedInfo1.notify) fedInfo1.notify = [];
|
|
66
|
-
if (!fedInfo1.lensConfig) fedInfo1.lensConfig = {};
|
|
67
|
-
|
|
68
|
-
// Add space2 to space1's federation list if not already present
|
|
69
|
-
if (!fedInfo1.federation.includes(spaceId2)) {
|
|
70
|
-
fedInfo1.federation.push(spaceId2);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Add space2 to space1's notify list if not already present
|
|
74
|
-
if (!fedInfo1.notify.includes(spaceId2)) {
|
|
75
|
-
fedInfo1.notify.push(spaceId2);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// Store lens configuration for space2
|
|
79
|
-
const newLensConfigsForSpace1 = { ...(fedInfo1.lensConfig || {}) }; // Shallow copy existing lensConfigs for space1
|
|
80
|
-
newLensConfigsForSpace1[spaceId2] = { // Add/update config for the target spaceId2
|
|
81
|
-
federate: [...federateLenses], // federateLenses & notifyLenses are from the main lensConfig parameter
|
|
82
|
-
notify: [...notifyLenses],
|
|
83
|
-
timestamp: Date.now()
|
|
84
|
-
};
|
|
85
|
-
fedInfo1.lensConfig = newLensConfigsForSpace1; // Assign the new/modified object back to fedInfo1.lensConfig
|
|
86
|
-
|
|
87
|
-
// Update timestamp
|
|
88
|
-
fedInfo1.timestamp = Date.now();
|
|
89
|
-
|
|
90
|
-
// Save updated federation info for space1
|
|
91
|
-
try {
|
|
92
|
-
await holosphere.putGlobal('federation', fedInfo1, password1);
|
|
93
|
-
} catch (error) {
|
|
94
|
-
throw new Error(`Failed to create federation: ${error.message}`);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// If bidirectional is true, handle space2 (B) as well
|
|
98
|
-
{
|
|
99
|
-
let fedInfo2 = null;
|
|
100
|
-
try {
|
|
101
|
-
fedInfo2 = await holosphere.getGlobal('federation', spaceId2, password2);
|
|
102
|
-
} catch (error) {
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
if (fedInfo2 == null) {
|
|
106
|
-
fedInfo2 = {
|
|
107
|
-
id: spaceId2,
|
|
108
|
-
name: spaceId2,
|
|
109
|
-
federation: [],
|
|
110
|
-
notify: [],
|
|
111
|
-
lensConfig: {}, // New field for lens-specific settings
|
|
112
|
-
timestamp: Date.now()
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Ensure arrays and lensConfig exist
|
|
117
|
-
if (!fedInfo2.federation) fedInfo2.federation = [];
|
|
118
|
-
if (!fedInfo2.notify) fedInfo2.notify = [];
|
|
119
|
-
if (!fedInfo2.lensConfig) fedInfo2.lensConfig = {};
|
|
120
|
-
|
|
121
|
-
// Add space1 to space2's federation list if bidirectional
|
|
122
|
-
if (bidirectional && !fedInfo2.federation.includes(spaceId1)) {
|
|
123
|
-
fedInfo2.federation.push(spaceId1);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Add space1 to space2's notify list if not already present
|
|
127
|
-
if (!fedInfo2.notify.includes(spaceId1)) {
|
|
128
|
-
fedInfo2.notify.push(spaceId1);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Store lens configuration for space1
|
|
132
|
-
fedInfo2.lensConfig[spaceId1] = {
|
|
133
|
-
federate: bidirectional ? [...federateLenses] : [], // Create a copy of the array
|
|
134
|
-
notify: bidirectional ? [...notifyLenses] : [], // Create a copy of the array
|
|
135
|
-
timestamp: Date.now()
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
// Update timestamp
|
|
139
|
-
fedInfo2.timestamp = Date.now();
|
|
140
|
-
|
|
141
|
-
// Save updated federation info for space2
|
|
142
|
-
try {
|
|
143
|
-
await holosphere.putGlobal('federation', fedInfo2, password2);
|
|
144
|
-
} catch (error) {
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Create federation metadata record
|
|
149
|
-
const federationMeta = {
|
|
150
|
-
id: `${spaceId1}_${spaceId2}`,
|
|
151
|
-
space1: spaceId1,
|
|
152
|
-
space2: spaceId2,
|
|
153
|
-
created: Date.now(),
|
|
154
|
-
status: 'active',
|
|
155
|
-
bidirectional: bidirectional,
|
|
156
|
-
lensConfig: {
|
|
157
|
-
federate: [...federateLenses], // Create a copy of the array
|
|
158
|
-
notify: [...notifyLenses] // Create a copy of the array
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
try {
|
|
163
|
-
await holosphere.putGlobal('federationMeta', federationMeta);
|
|
164
|
-
} catch (error) {
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
return true;
|
|
168
|
-
} catch (error) {
|
|
169
|
-
throw error;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Subscribes to federation notifications for a space
|
|
175
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
176
|
-
* @param {string} spaceId - The space ID to subscribe to
|
|
177
|
-
* @param {string} [password] - Optional password for the space
|
|
178
|
-
* @param {function} callback - The callback to execute on notifications
|
|
179
|
-
* @param {object} [options] - Subscription options
|
|
180
|
-
* @param {string[]} [options.lenses] - Specific lenses to subscribe to (default: all)
|
|
181
|
-
* @param {number} [options.throttle] - Throttle notifications in ms (default: 0)
|
|
182
|
-
* @returns {Promise<object>} - Subscription object with unsubscribe() method
|
|
183
|
-
*/
|
|
184
|
-
export async function subscribeFederation(holosphere, spaceId, password = null, callback, options = {}) {
|
|
185
|
-
if (!spaceId || !callback) {
|
|
186
|
-
throw new Error('subscribeFederation: Missing required parameters');
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const { lenses = ['*'], throttle = 0 } = options;
|
|
190
|
-
|
|
191
|
-
// Get federation info
|
|
192
|
-
const fedInfo = await holosphere.getGlobal('federation', spaceId, password);
|
|
193
|
-
if (!fedInfo) {
|
|
194
|
-
throw new Error('No federation info found for space');
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Create subscription for each federated space
|
|
198
|
-
const subscriptions = [];
|
|
199
|
-
let lastNotificationTime = {};
|
|
200
|
-
|
|
201
|
-
if (fedInfo.federation && fedInfo.federation.length > 0) {
|
|
202
|
-
for (const federatedSpace of fedInfo.federation) {
|
|
203
|
-
// For each lens specified (or all if '*')
|
|
204
|
-
for (const lens of lenses) {
|
|
205
|
-
try {
|
|
206
|
-
const sub = await holosphere.subscribe(federatedSpace, lens, async (data) => {
|
|
207
|
-
try {
|
|
208
|
-
// Skip if data is missing or not from federated space
|
|
209
|
-
if (!data || !data.id) return;
|
|
210
|
-
|
|
211
|
-
// Apply throttling if configured
|
|
212
|
-
const now = Date.now();
|
|
213
|
-
const key = `${federatedSpace}_${lens}_${data.id}`;
|
|
214
|
-
|
|
215
|
-
if (throttle > 0) {
|
|
216
|
-
if (lastNotificationTime[key] &&
|
|
217
|
-
(now - lastNotificationTime[key]) < throttle) {
|
|
218
|
-
return; // Skip this notification (throttled)
|
|
219
|
-
}
|
|
220
|
-
lastNotificationTime[key] = now;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Add federation metadata if not present
|
|
224
|
-
if (!data.federation) {
|
|
225
|
-
data.federation = {
|
|
226
|
-
origin: federatedSpace,
|
|
227
|
-
timestamp: now
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Execute callback with the data
|
|
232
|
-
await callback(data, federatedSpace, lens);
|
|
233
|
-
} catch (error) {
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
if (sub && typeof sub.unsubscribe === 'function') {
|
|
238
|
-
subscriptions.push(sub);
|
|
239
|
-
}
|
|
240
|
-
} catch (error) {
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
// Return combined subscription object
|
|
247
|
-
return {
|
|
248
|
-
unsubscribe: () => {
|
|
249
|
-
subscriptions.forEach(sub => {
|
|
250
|
-
try {
|
|
251
|
-
if (sub && typeof sub.unsubscribe === 'function') {
|
|
252
|
-
sub.unsubscribe();
|
|
253
|
-
}
|
|
254
|
-
} catch (error) {
|
|
255
|
-
}
|
|
256
|
-
});
|
|
257
|
-
// Clear the subscriptions array
|
|
258
|
-
subscriptions.length = 0;
|
|
259
|
-
// Clear throttling data
|
|
260
|
-
lastNotificationTime = {};
|
|
261
|
-
},
|
|
262
|
-
getSubscriptionCount: () => subscriptions.length
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Gets federation info for a space
|
|
268
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
269
|
-
* @param {string} spaceId - The space ID
|
|
270
|
-
* @param {string} [password] - Optional password for the space
|
|
271
|
-
* @returns {Promise<object|null>} - Federation info or null if not found
|
|
272
|
-
*/
|
|
273
|
-
export async function getFederation(holosphere, spaceId, password = null) {
|
|
274
|
-
if (!spaceId) {
|
|
275
|
-
throw new Error('getFederation: Missing space ID');
|
|
276
|
-
}
|
|
277
|
-
return await holosphere.getGlobal('federation', spaceId, password);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Retrieves the lens-specific configuration for a federation link between two spaces.
|
|
282
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
283
|
-
* @param {string} spaceId - The ID of the source space.
|
|
284
|
-
* @param {string} targetSpaceId - The ID of the target space in the federation link.
|
|
285
|
-
* @param {string} [password] - Optional password for the source space.
|
|
286
|
-
* @returns {Promise<object|null>} - An object with 'federate' and 'notify' arrays, or null if not found.
|
|
287
|
-
*/
|
|
288
|
-
export async function getFederatedConfig(holosphere, spaceId, targetSpaceId, password = null) {
|
|
289
|
-
if (!holosphere || !spaceId || !targetSpaceId) {
|
|
290
|
-
throw new Error('getFederatedConfig: Missing required parameters');
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
const fedInfo = await getFederation(holosphere, spaceId, password);
|
|
295
|
-
|
|
296
|
-
if (fedInfo && fedInfo.lensConfig && fedInfo.lensConfig[targetSpaceId]) {
|
|
297
|
-
return {
|
|
298
|
-
federate: fedInfo.lensConfig[targetSpaceId].federate || [],
|
|
299
|
-
notify: fedInfo.lensConfig[targetSpaceId].notify || []
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
return null; // Or return an empty config: { federate: [], notify: [] }
|
|
303
|
-
} catch (error) {
|
|
304
|
-
console.error(`Error getting federated config for ${spaceId} -> ${targetSpaceId}: ${error.message}`);
|
|
305
|
-
throw error;
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Removes a federation relationship between spaces
|
|
311
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
312
|
-
* @param {string} spaceId1 - The first space ID
|
|
313
|
-
* @param {string} spaceId2 - The second space ID
|
|
314
|
-
* @param {string} [password1] - Optional password for the first space
|
|
315
|
-
* @param {string} [password2] - Optional password for the second space
|
|
316
|
-
* @returns {Promise<boolean>} - True if federation was removed successfully
|
|
317
|
-
*/
|
|
318
|
-
export async function unfederate(holosphere, spaceId1, spaceId2, password1 = null, password2 = null) {
|
|
319
|
-
if (!spaceId1 || !spaceId2) {
|
|
320
|
-
throw new Error('unfederate: Missing required space IDs');
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
try {
|
|
324
|
-
// Get federation info for first space
|
|
325
|
-
let fedInfo1 = null;
|
|
326
|
-
try {
|
|
327
|
-
fedInfo1 = await holosphere.getGlobal('federation', spaceId1, password1);
|
|
328
|
-
} catch (error) {
|
|
329
|
-
console.error(`Error getting fedInfo1 for ${spaceId1} during unfederate: ${error.message}`);
|
|
330
|
-
// If we can't get fedInfo1, we can't modify it. Decide if this is a critical failure.
|
|
331
|
-
// For now, we'll let it proceed to attempt metadata cleanup, but a throw here might be valid.
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (!fedInfo1) {
|
|
335
|
-
// If fedInfo1 doesn't exist, log and proceed to metadata cleanup.
|
|
336
|
-
console.warn(`No federation info found for ${spaceId1}. Skipping its update.`);
|
|
337
|
-
} else {
|
|
338
|
-
// Ensure arrays exist
|
|
339
|
-
if (!fedInfo1.federation) fedInfo1.federation = [];
|
|
340
|
-
if (!fedInfo1.notify) fedInfo1.notify = [];
|
|
341
|
-
|
|
342
|
-
// Update first space federation info - remove from both federation and notify arrays
|
|
343
|
-
const originalFederationLength = fedInfo1.federation.length;
|
|
344
|
-
const originalNotifyLength = fedInfo1.notify.length;
|
|
345
|
-
|
|
346
|
-
fedInfo1.federation = fedInfo1.federation.filter(id => id !== spaceId2);
|
|
347
|
-
fedInfo1.notify = fedInfo1.notify.filter(id => id !== spaceId2);
|
|
348
|
-
fedInfo1.timestamp = Date.now();
|
|
349
|
-
|
|
350
|
-
console.log(`Unfederate: Removed ${spaceId2} from ${spaceId1}: federation ${originalFederationLength} -> ${fedInfo1.federation.length}, notify ${originalNotifyLength} -> ${fedInfo1.notify.length}`);
|
|
351
|
-
|
|
352
|
-
try {
|
|
353
|
-
await holosphere.putGlobal('federation', fedInfo1, password1);
|
|
354
|
-
} catch (error) {
|
|
355
|
-
console.error(`Failed to update fedInfo1 for ${spaceId1} during unfederate: ${error.message}`);
|
|
356
|
-
throw error; // RE-THROW to signal failure
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Update second space federation info (remove spaceId1 from spaceId2's notify list)
|
|
361
|
-
// This part is usually for full bidirectional unfederation cleanup.
|
|
362
|
-
// The original code only did this if password2 was provided.
|
|
363
|
-
if (password2) { // Retaining original condition for this block
|
|
364
|
-
let fedInfo2 = null;
|
|
365
|
-
try {
|
|
366
|
-
fedInfo2 = await holosphere.getGlobal('federation', spaceId2, password2);
|
|
367
|
-
} catch (error) {
|
|
368
|
-
console.error(`Error getting fedInfo2 for ${spaceId2} during unfederate: ${error.message}`);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
if (!fedInfo2 || !fedInfo2.notify) {
|
|
372
|
-
console.warn(`No notify array found for ${spaceId2} or fedInfo2 is null. Skipping its update.`);
|
|
373
|
-
} else {
|
|
374
|
-
fedInfo2.notify = fedInfo2.notify.filter(id => id !== spaceId1);
|
|
375
|
-
fedInfo2.timestamp = Date.now();
|
|
376
|
-
|
|
377
|
-
try {
|
|
378
|
-
await holosphere.putGlobal('federation', fedInfo2, password2);
|
|
379
|
-
} catch (error) {
|
|
380
|
-
console.error(`Failed to update fedInfo2 for ${spaceId2} during unfederate: ${error.message}`);
|
|
381
|
-
throw error; // RE-THROW to signal failure
|
|
382
|
-
}
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Update federation metadata
|
|
387
|
-
const metaId = `${spaceId1}_${spaceId2}`;
|
|
388
|
-
const altMetaId = `${spaceId2}_${spaceId1}`;
|
|
389
|
-
|
|
390
|
-
try {
|
|
391
|
-
const meta = await holosphere.getGlobal('federationMeta', metaId) ||
|
|
392
|
-
await holosphere.getGlobal('federationMeta', altMetaId);
|
|
393
|
-
|
|
394
|
-
if (meta) {
|
|
395
|
-
meta.status = 'inactive';
|
|
396
|
-
meta.endedAt = Date.now();
|
|
397
|
-
await holosphere.putGlobal('federationMeta', meta); // Not re-throwing here as it's metadata cleanup
|
|
398
|
-
}
|
|
399
|
-
} catch (error) {
|
|
400
|
-
console.warn(`Failed to update federationMeta during unfederate: ${error.message}`);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return true;
|
|
404
|
-
} catch (error) {
|
|
405
|
-
// This will catch errors re-thrown from putGlobal or from getGlobal if they occur before specific catches.
|
|
406
|
-
console.error(`Critical error during unfederate operation for ${spaceId1}-${spaceId2}: ${error.message}`);
|
|
407
|
-
throw error; // Ensure the main operation failure is propagated
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Removes a notification relationship between two spaces
|
|
413
|
-
* This removes spaceId2 from the notify list of spaceId1
|
|
414
|
-
*
|
|
415
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
416
|
-
* @param {string} spaceId1 - The space to modify (remove from its notify list)
|
|
417
|
-
* @param {string} spaceId2 - The space to be removed from notifications
|
|
418
|
-
* @param {string} [password1] - Optional password for the first space
|
|
419
|
-
* @returns {Promise<boolean>} - True if notification was removed successfully
|
|
420
|
-
*/
|
|
421
|
-
export async function removeNotify(holosphere, spaceId1, spaceId2, password1 = null) {
|
|
422
|
-
if (!spaceId1 || !spaceId2) {
|
|
423
|
-
throw new Error('removeNotify: Missing required space IDs');
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
try {
|
|
427
|
-
// Get federation info for space
|
|
428
|
-
let fedInfo = await holosphere.getGlobal('federation', spaceId1, password1);
|
|
429
|
-
|
|
430
|
-
if (!fedInfo) {
|
|
431
|
-
throw new Error(`No federation info found for ${spaceId1}`);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
// Ensure notify array exists
|
|
435
|
-
if (!fedInfo.notify) fedInfo.notify = [];
|
|
436
|
-
|
|
437
|
-
// Remove space2 from space1's notify list if present
|
|
438
|
-
if (fedInfo.notify.includes(spaceId2)) {
|
|
439
|
-
fedInfo.notify = fedInfo.notify.filter(id => id !== spaceId2);
|
|
440
|
-
|
|
441
|
-
// Update timestamp
|
|
442
|
-
fedInfo.timestamp = Date.now();
|
|
443
|
-
|
|
444
|
-
// Save updated federation info
|
|
445
|
-
await holosphere.putGlobal('federation', fedInfo, password1);
|
|
446
|
-
return true;
|
|
447
|
-
} else {
|
|
448
|
-
return false;
|
|
449
|
-
}
|
|
450
|
-
} catch (error) {
|
|
451
|
-
throw error;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
/**
|
|
456
|
-
* Get and combine data from local and federated sources.
|
|
457
|
-
* If `options.queryIds` is provided, fetches only those specific IDs using `get()`.
|
|
458
|
-
* Otherwise, falls back to fetching all data using `getAll()` (potentially inefficient).
|
|
459
|
-
*
|
|
460
|
-
* @param {HoloSphere} holosphere The HoloSphere instance
|
|
461
|
-
* @param {string} holon The local holon name (used as the space ID for federation info)
|
|
462
|
-
* @param {string} lens The lens to query
|
|
463
|
-
* @param {Object} options Options for data retrieval and aggregation
|
|
464
|
-
* @param {string[]} [options.queryIds] Optional array of specific item IDs to fetch.
|
|
465
|
-
* @param {boolean} [options.aggregate=false] Whether to aggregate results by ID
|
|
466
|
-
* @param {string} [options.idField='id'] The field to use as ID
|
|
467
|
-
* @param {string[]} [options.sumFields=[]] Fields to sum during aggregation
|
|
468
|
-
* @param {string[]} [options.concatArrays=[]] Array fields to concatenate during aggregation
|
|
469
|
-
* @param {boolean} [options.removeDuplicates=true] Whether to remove duplicates in concatenated arrays
|
|
470
|
-
* @param {Function} [options.mergeStrategy=null] Custom merge function for aggregation
|
|
471
|
-
* @param {boolean} [options.includeLocal=true] Whether to include local data
|
|
472
|
-
* @param {boolean} [options.includeFederated=true] Whether to include federated data
|
|
473
|
-
* @param {boolean} [options.resolveReferences=true] Whether to resolve federation references
|
|
474
|
-
* @param {number} [options.maxFederatedSpaces=-1] Maximum number of federated spaces to query (-1 for all)
|
|
475
|
-
* @param {number} [options.timeout=10000] Timeout in milliseconds for federated queries
|
|
476
|
-
* @returns {Promise<Array>} Combined array of local and federated data
|
|
477
|
-
*/
|
|
478
|
-
export async function getFederated(holosphere, holon, lens, options = {}) {
|
|
479
|
-
// Set default options and extract queryIds
|
|
480
|
-
const {
|
|
481
|
-
queryIds = null, // New option
|
|
482
|
-
aggregate = false,
|
|
483
|
-
idField = 'id',
|
|
484
|
-
sumFields = [],
|
|
485
|
-
concatArrays = [],
|
|
486
|
-
removeDuplicates = true,
|
|
487
|
-
mergeStrategy = null,
|
|
488
|
-
includeLocal = true,
|
|
489
|
-
includeFederated = true,
|
|
490
|
-
resolveReferences = true,
|
|
491
|
-
maxFederatedSpaces = -1,
|
|
492
|
-
timeout = 10000
|
|
493
|
-
} = options;
|
|
494
|
-
|
|
495
|
-
console.log(`resolveReferences option: ${resolveReferences}`);
|
|
496
|
-
console.log(`Querying specific IDs:`, queryIds ? queryIds.join(', ') : 'No (fetching all)');
|
|
497
|
-
|
|
498
|
-
// Validate required parameters
|
|
499
|
-
if (!holosphere || !holon || !lens) {
|
|
500
|
-
throw new Error('Missing required parameters: holosphere, holon, and lens are required');
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
// Get federation info for current space (using holon as spaceId)
|
|
504
|
-
const spaceId = holon;
|
|
505
|
-
const fedInfo = await getFederation(holosphere, spaceId);
|
|
506
|
-
|
|
507
|
-
// Initialize result array and track processed IDs to avoid duplicates/redundant fetches
|
|
508
|
-
const fetchedItems = new Map(); // Use Map to store fetched items by ID
|
|
509
|
-
const processedIds = new Set(); // Track IDs added to the final result
|
|
510
|
-
|
|
511
|
-
const fetchPromises = [];
|
|
512
|
-
|
|
513
|
-
// Determine list of spaces to query (local + federated)
|
|
514
|
-
let spacesToQuery = [];
|
|
515
|
-
if (includeLocal) {
|
|
516
|
-
spacesToQuery.push(holon); // Add local holon first
|
|
517
|
-
}
|
|
518
|
-
if (includeFederated && fedInfo && fedInfo.federation && fedInfo.federation.length > 0) {
|
|
519
|
-
const federatedSpaces = maxFederatedSpaces === -1 ? fedInfo.federation : fedInfo.federation.slice(0, maxFederatedSpaces);
|
|
520
|
-
spacesToQuery = spacesToQuery.concat(federatedSpaces);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
// Fetch data from all relevant spaces
|
|
524
|
-
for (const currentSpace of spacesToQuery) {
|
|
525
|
-
if (queryIds && Array.isArray(queryIds)) {
|
|
526
|
-
// --- Fetch specific IDs using holosphere.get ---
|
|
527
|
-
console.log(`Fetching specific IDs from ${currentSpace}: ${queryIds.join(', ')}`);
|
|
528
|
-
for (const itemId of queryIds) {
|
|
529
|
-
if (fetchedItems.has(itemId)) continue; // Skip if already fetched
|
|
530
|
-
fetchPromises.push(
|
|
531
|
-
holosphere.get(currentSpace, lens, itemId)
|
|
532
|
-
.then(item => {
|
|
533
|
-
if (item) {
|
|
534
|
-
fetchedItems.set(itemId, item);
|
|
535
|
-
}
|
|
536
|
-
})
|
|
537
|
-
.catch(err => console.warn(`Error fetching item ${itemId} from ${currentSpace}: ${err.message}`))
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
|
-
} else {
|
|
541
|
-
// --- Fetch all data using holosphere.getAll (Fallback - inefficient) ---
|
|
542
|
-
if(currentSpace === holon && includeLocal) { // Only warn once for local
|
|
543
|
-
console.warn(`getFederated: No queryIds provided. Falling back to fetching ALL items from ${currentSpace} using getAll. This can be inefficient.`);
|
|
544
|
-
}
|
|
545
|
-
console.log(`Fetching ALL items from ${currentSpace}`);
|
|
546
|
-
fetchPromises.push(
|
|
547
|
-
holosphere.getAll(currentSpace, lens)
|
|
548
|
-
.then(items => {
|
|
549
|
-
for (const item of items) {
|
|
550
|
-
if (item && item[idField] && !fetchedItems.has(item[idField])) {
|
|
551
|
-
fetchedItems.set(item[idField], item);
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
})
|
|
555
|
-
.catch(err => console.warn(`Error fetching all items from ${currentSpace}: ${err.message}`))
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// Wait for all fetches to complete
|
|
561
|
-
await Promise.all(fetchPromises);
|
|
562
|
-
|
|
563
|
-
// Convert Map values to array for processing
|
|
564
|
-
const result = Array.from(fetchedItems.values());
|
|
565
|
-
|
|
566
|
-
// Now resolve references if needed
|
|
567
|
-
if (resolveReferences && result.length > 0) {
|
|
568
|
-
console.log(`Resolving references for ${result.length} fetched items`);
|
|
569
|
-
|
|
570
|
-
for (let i = 0; i < result.length; i++) {
|
|
571
|
-
const item = result[i];
|
|
572
|
-
|
|
573
|
-
// Check for simplified reference (item with id and soul)
|
|
574
|
-
if (item.soul && item.id) {
|
|
575
|
-
console.log(`Found simple reference with soul: ${item.soul}`);
|
|
576
|
-
|
|
577
|
-
try {
|
|
578
|
-
// Parse the soul to get the components
|
|
579
|
-
const soulParts = item.soul.split('/');
|
|
580
|
-
if (soulParts.length >= 4) {
|
|
581
|
-
const originHolon = soulParts[1];
|
|
582
|
-
const originLens = soulParts[2];
|
|
583
|
-
const originKey = soulParts[3];
|
|
584
|
-
|
|
585
|
-
console.log(`Extracting from soul - holon: ${originHolon}, lens: ${originLens}, key: ${originKey}`);
|
|
586
|
-
|
|
587
|
-
// Get original data using the extracted path - always resolve references
|
|
588
|
-
const originalData = await holosphere.get(
|
|
589
|
-
originHolon,
|
|
590
|
-
originLens,
|
|
591
|
-
originKey,
|
|
592
|
-
null,
|
|
593
|
-
{ resolveReferences: true } // Always resolve nested references
|
|
594
|
-
);
|
|
595
|
-
|
|
596
|
-
console.log(`Original data found via soul path:`, JSON.stringify(originalData));
|
|
597
|
-
|
|
598
|
-
if (originalData) {
|
|
599
|
-
// Replace the reference with the original data
|
|
600
|
-
result[i] = {
|
|
601
|
-
...originalData,
|
|
602
|
-
_federation: {
|
|
603
|
-
isReference: true,
|
|
604
|
-
resolved: true,
|
|
605
|
-
soul: item.soul,
|
|
606
|
-
timestamp: Date.now()
|
|
607
|
-
}
|
|
608
|
-
};
|
|
609
|
-
} else {
|
|
610
|
-
// Instead of leaving the original reference, create an error object
|
|
611
|
-
result[i] = {
|
|
612
|
-
id: item.id,
|
|
613
|
-
_federation: {
|
|
614
|
-
isReference: true,
|
|
615
|
-
resolved: false,
|
|
616
|
-
soul: item.soul,
|
|
617
|
-
error: 'Referenced data not found',
|
|
618
|
-
timestamp: Date.now()
|
|
619
|
-
}
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
} else {
|
|
623
|
-
console.warn(`Soul doesn't match expected format: ${item.soul}`);
|
|
624
|
-
// Instead of leaving the original reference, create an error object
|
|
625
|
-
result[i] = {
|
|
626
|
-
id: item.id,
|
|
627
|
-
_federation: {
|
|
628
|
-
isReference: true,
|
|
629
|
-
resolved: false,
|
|
630
|
-
soul: item.soul,
|
|
631
|
-
error: 'Invalid soul format',
|
|
632
|
-
timestamp: Date.now()
|
|
633
|
-
}
|
|
634
|
-
};
|
|
635
|
-
}
|
|
636
|
-
} catch (refError) {
|
|
637
|
-
// Instead of leaving the original reference, create an error object
|
|
638
|
-
result[i] = {
|
|
639
|
-
id: item.id,
|
|
640
|
-
_federation: {
|
|
641
|
-
isReference: true,
|
|
642
|
-
resolved: false,
|
|
643
|
-
soul: item.soul,
|
|
644
|
-
error: refError.message || 'Error resolving reference',
|
|
645
|
-
timestamp: Date.now()
|
|
646
|
-
}
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
}
|
|
650
|
-
// For backward compatibility, check for old-style references
|
|
651
|
-
else if (item._federation && item._federation.isReference) {
|
|
652
|
-
console.log(`Found legacy reference: ${item._federation.origin}/${item._federation.lens}/${item[idField]}`);
|
|
653
|
-
|
|
654
|
-
try {
|
|
655
|
-
const reference = item._federation;
|
|
656
|
-
console.log(`Getting original data from ${reference.origin} / ${reference.lens} / ${item[idField]}`);
|
|
657
|
-
|
|
658
|
-
// Get original data
|
|
659
|
-
const originalData = await holosphere.get(
|
|
660
|
-
reference.origin,
|
|
661
|
-
reference.lens,
|
|
662
|
-
item[idField],
|
|
663
|
-
null,
|
|
664
|
-
{ resolveReferences: false } // Prevent infinite recursion
|
|
665
|
-
);
|
|
666
|
-
|
|
667
|
-
console.log(`Original data found:`, JSON.stringify(originalData));
|
|
668
|
-
|
|
669
|
-
if (originalData) {
|
|
670
|
-
// Add federation information to the resolved data
|
|
671
|
-
result[i] = {
|
|
672
|
-
...originalData,
|
|
673
|
-
_federation: {
|
|
674
|
-
...reference,
|
|
675
|
-
resolved: true,
|
|
676
|
-
timestamp: Date.now()
|
|
677
|
-
}
|
|
678
|
-
};
|
|
679
|
-
} else {
|
|
680
|
-
console.warn(`Could not resolve legacy reference: original data not found`);
|
|
681
|
-
}
|
|
682
|
-
} catch (refError) {
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
}
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
// Apply aggregation if requested
|
|
689
|
-
if (aggregate && result.length > 0) {
|
|
690
|
-
// Group items by ID
|
|
691
|
-
const groupedById = result.reduce((acc, item) => {
|
|
692
|
-
const id = item[idField];
|
|
693
|
-
if (!acc[id]) {
|
|
694
|
-
acc[id] = [];
|
|
695
|
-
}
|
|
696
|
-
acc[id].push(item);
|
|
697
|
-
return acc;
|
|
698
|
-
}, {});
|
|
699
|
-
|
|
700
|
-
// Aggregate each group
|
|
701
|
-
const aggregatedData = Object.values(groupedById).map(group => {
|
|
702
|
-
// If only one item in group, no aggregation needed
|
|
703
|
-
if (group.length === 1) return group[0];
|
|
704
|
-
|
|
705
|
-
// Use custom merge strategy if provided
|
|
706
|
-
if (mergeStrategy && typeof mergeStrategy === 'function') {
|
|
707
|
-
return mergeStrategy(group);
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// Default aggregation strategy
|
|
711
|
-
const base = { ...group[0] };
|
|
712
|
-
|
|
713
|
-
// Sum numeric fields
|
|
714
|
-
for (const field of sumFields) {
|
|
715
|
-
if (typeof base[field] === 'number') {
|
|
716
|
-
base[field] = group.reduce((sum, item) => sum + (Number(item[field]) || 0), 0);
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
// Concatenate array fields
|
|
721
|
-
for (const field of concatArrays) {
|
|
722
|
-
if (Array.isArray(base[field])) {
|
|
723
|
-
const allValues = group.reduce((all, item) => {
|
|
724
|
-
return Array.isArray(item[field]) ? [...all, ...item[field]] : all;
|
|
725
|
-
}, []);
|
|
726
|
-
|
|
727
|
-
// Remove duplicates if requested
|
|
728
|
-
base[field] = removeDuplicates ? Array.from(new Set(allValues)) : allValues;
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
// Add aggregation metadata
|
|
733
|
-
base._aggregated = {
|
|
734
|
-
count: group.length,
|
|
735
|
-
timestamp: Date.now()
|
|
736
|
-
};
|
|
737
|
-
|
|
738
|
-
return base;
|
|
739
|
-
});
|
|
740
|
-
|
|
741
|
-
return aggregatedData;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
return result;
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
/**
|
|
748
|
-
* Propagates data to federated spaces
|
|
749
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
750
|
-
* @param {string} holon - The holon identifier
|
|
751
|
-
* @param {string} lens - The lens identifier
|
|
752
|
-
* @param {object} data - The data to propagate
|
|
753
|
-
* @param {object} [options] - Propagation options
|
|
754
|
-
* @param {boolean} [options.useHolograms=true] - Use holograms for propagation (default: true)
|
|
755
|
-
* @param {string[]} [options.targetSpaces] - Specific target spaces to propagate to (defaults to all federated spaces)
|
|
756
|
-
* @param {string} [options.password] - Password for accessing the source holon (if needed)
|
|
757
|
-
* @param {boolean} [options.propagateToParents=true] - Whether to automatically propagate to parent hexagons (default: true)
|
|
758
|
-
* @param {number} [options.maxParentLevels=15] - Maximum number of parent levels to propagate to (default: 15)
|
|
759
|
-
* @returns {Promise<object>} - Result with success count and errors
|
|
760
|
-
*/
|
|
761
|
-
export async function propagate(holosphere, holon, lens, data, options = {}) {
|
|
762
|
-
if (!holosphere || !holon || !lens || !data) {
|
|
763
|
-
throw new Error('propagate: Missing required parameters');
|
|
764
|
-
}
|
|
765
|
-
// Default propagation options
|
|
766
|
-
const {
|
|
767
|
-
useHolograms = true,
|
|
768
|
-
targetSpaces = null,
|
|
769
|
-
password = null,
|
|
770
|
-
propagateToParents = true,
|
|
771
|
-
maxParentLevels = 15
|
|
772
|
-
} = options;
|
|
773
|
-
|
|
774
|
-
const result = {
|
|
775
|
-
success: 0,
|
|
776
|
-
errors: 0,
|
|
777
|
-
skipped: 0,
|
|
778
|
-
messages: [],
|
|
779
|
-
parentPropagation: {
|
|
780
|
-
success: 0,
|
|
781
|
-
errors: 0,
|
|
782
|
-
skipped: 0,
|
|
783
|
-
messages: []
|
|
784
|
-
}
|
|
785
|
-
};
|
|
786
|
-
|
|
787
|
-
try {
|
|
788
|
-
// ================================ FEDERATION PROPAGATION ================================
|
|
789
|
-
|
|
790
|
-
// Get federation info for this holon using getFederation
|
|
791
|
-
const fedInfo = await getFederation(holosphere, holon, password);
|
|
792
|
-
|
|
793
|
-
// Only perform federation propagation if there's valid federation info
|
|
794
|
-
if (fedInfo && fedInfo.federation && fedInfo.federation.length > 0 && fedInfo.notify && fedInfo.notify.length > 0) {
|
|
795
|
-
// Filter federation spaces to those in notify list
|
|
796
|
-
let spaces = fedInfo.notify;
|
|
797
|
-
|
|
798
|
-
// Further filter by targetSpaces if provided
|
|
799
|
-
if (targetSpaces && Array.isArray(targetSpaces) && targetSpaces.length > 0) {
|
|
800
|
-
spaces = spaces.filter(space => targetSpaces.includes(space));
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
if (spaces.length > 0) {
|
|
804
|
-
// Filter spaces based on lens configuration
|
|
805
|
-
spaces = spaces.filter(targetSpace => {
|
|
806
|
-
const spaceConfig = fedInfo.lensConfig?.[targetSpace];
|
|
807
|
-
if (!spaceConfig) {
|
|
808
|
-
result.messages.push(`No lens configuration for target space ${targetSpace}. Skipping propagation of lens '${lens}'.`);
|
|
809
|
-
result.skipped++;
|
|
810
|
-
return false;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
// Ensure .federate is an array before calling .includes
|
|
814
|
-
const federateLenses = Array.isArray(spaceConfig.federate) ? spaceConfig.federate : [];
|
|
815
|
-
|
|
816
|
-
const shouldFederate = federateLenses.includes('*') || federateLenses.includes(lens);
|
|
817
|
-
|
|
818
|
-
// Propagation now only depends on the 'federate' list configuration for the lens
|
|
819
|
-
const shouldPropagate = shouldFederate;
|
|
820
|
-
|
|
821
|
-
if (!shouldPropagate) {
|
|
822
|
-
result.messages.push(`Propagation of lens '${lens}' to target space ${targetSpace} skipped: lens not in 'federate' configuration.`);
|
|
823
|
-
result.skipped++;
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
return shouldPropagate;
|
|
827
|
-
});
|
|
828
|
-
|
|
829
|
-
if (spaces.length > 0) {
|
|
830
|
-
// Check if data is already a hologram
|
|
831
|
-
const isAlreadyHologram = holosphere.isHologram(data);
|
|
832
|
-
|
|
833
|
-
// For each target space, propagate the data
|
|
834
|
-
const propagatePromises = spaces.map(async (targetSpace) => {
|
|
835
|
-
try {
|
|
836
|
-
let payloadToPut;
|
|
837
|
-
const federationMeta = {
|
|
838
|
-
origin: holon, // The space from which this data is being propagated
|
|
839
|
-
sourceLens: lens, // The lens from which this data is being propagated
|
|
840
|
-
propagatedAt: Date.now(),
|
|
841
|
-
originalId: data.id
|
|
842
|
-
};
|
|
843
|
-
|
|
844
|
-
if (useHolograms && !isAlreadyHologram) {
|
|
845
|
-
// Create a new hologram referencing the original data
|
|
846
|
-
const newHologram = holosphere.createHologram(holon, lens, data);
|
|
847
|
-
payloadToPut = {
|
|
848
|
-
...newHologram, // This will be { id: data.id, soul: 'path/to/original' }
|
|
849
|
-
_federation: federationMeta
|
|
850
|
-
};
|
|
851
|
-
} else {
|
|
852
|
-
// Propagate existing data (could be a full object or an existing hologram)
|
|
853
|
-
// Make a shallow copy and update/add _federation metadata
|
|
854
|
-
payloadToPut = {
|
|
855
|
-
...data,
|
|
856
|
-
_federation: {
|
|
857
|
-
...(data._federation || {}), // Preserve existing _federation fields if any
|
|
858
|
-
...federationMeta // Add/overwrite with current propagation info
|
|
859
|
-
}
|
|
860
|
-
};
|
|
861
|
-
}
|
|
862
|
-
|
|
863
|
-
// Store in the target space with redirection disabled and no further auto-propagation
|
|
864
|
-
await holosphere.put(targetSpace, lens, payloadToPut, null, {
|
|
865
|
-
disableHologramRedirection: true,
|
|
866
|
-
autoPropagate: false
|
|
867
|
-
});
|
|
868
|
-
|
|
869
|
-
result.success++;
|
|
870
|
-
return true;
|
|
871
|
-
} catch (error) {
|
|
872
|
-
result.errors++;
|
|
873
|
-
result.messages.push(`Error propagating ${data.id} to ${targetSpace}: ${error.message}`);
|
|
874
|
-
return false;
|
|
875
|
-
}
|
|
876
|
-
});
|
|
877
|
-
|
|
878
|
-
await Promise.all(propagatePromises);
|
|
879
|
-
} else {
|
|
880
|
-
result.messages.push('No valid target spaces for federation propagation after lens filtering.');
|
|
881
|
-
}
|
|
882
|
-
} else {
|
|
883
|
-
result.messages.push('No valid target spaces found for federation propagation.');
|
|
884
|
-
}
|
|
885
|
-
} else {
|
|
886
|
-
result.messages.push(`No federation found for ${holon} or no notification targets available.`);
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
// ================================ PARENT PROPAGATION ================================
|
|
890
|
-
|
|
891
|
-
// Check if we should propagate to parent hexagons
|
|
892
|
-
if (propagateToParents) {
|
|
893
|
-
|
|
894
|
-
try {
|
|
895
|
-
// Validate if the holon is a proper H3 hexagon
|
|
896
|
-
// H3 hexagons should start with '8' and have a valid format
|
|
897
|
-
let holonResolution;
|
|
898
|
-
let isValidH3 = false;
|
|
899
|
-
|
|
900
|
-
// First check: H3 hexagons should start with '8' and be at least 15 characters long
|
|
901
|
-
if (typeof holon === 'string' && /^[8][0-9A-Fa-f]+$/.test(holon) && holon.length >= 15) {
|
|
902
|
-
try {
|
|
903
|
-
holonResolution = h3.getResolution(holon);
|
|
904
|
-
// Additional validation: resolution should be >= 0 and <= 15
|
|
905
|
-
if (holonResolution >= 0 && holonResolution <= 15) {
|
|
906
|
-
isValidH3 = true;
|
|
907
|
-
} else {
|
|
908
|
-
console.log(`[Federation] Holon ${holon} has invalid resolution: ${holonResolution}`);
|
|
909
|
-
}
|
|
910
|
-
} catch (error) {
|
|
911
|
-
console.log(`[Federation] Holon ${holon} failed H3 validation: ${error.message}`);
|
|
912
|
-
}
|
|
913
|
-
} else {
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
if (!isValidH3) {
|
|
917
|
-
result.parentPropagation.messages.push(`Holon ${holon} is not a valid H3 hexagon. Skipping parent propagation.`);
|
|
918
|
-
result.parentPropagation.skipped++;
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
if (isValidH3 && holonResolution !== undefined) {
|
|
922
|
-
// Get all parent hexagons up to the specified max levels
|
|
923
|
-
const parentHexagons = [];
|
|
924
|
-
let currentHolon = holon;
|
|
925
|
-
let currentRes = holonResolution;
|
|
926
|
-
let levelsProcessed = 0;
|
|
927
|
-
|
|
928
|
-
while (currentRes > 0 && levelsProcessed < maxParentLevels) {
|
|
929
|
-
try {
|
|
930
|
-
const parent = h3.cellToParent(currentHolon, currentRes - 1);
|
|
931
|
-
parentHexagons.push(parent);
|
|
932
|
-
currentHolon = parent;
|
|
933
|
-
currentRes--;
|
|
934
|
-
levelsProcessed++;
|
|
935
|
-
} catch (error) {
|
|
936
|
-
console.error(`[Federation] Error getting parent for ${currentHolon}: ${error.message}`);
|
|
937
|
-
result.parentPropagation.messages.push(`Error getting parent for ${currentHolon}: ${error.message}`);
|
|
938
|
-
result.parentPropagation.errors++;
|
|
939
|
-
break;
|
|
940
|
-
}
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
if (parentHexagons.length > 0) {
|
|
944
|
-
result.parentPropagation.messages.push(`Found ${parentHexagons.length} parent hexagons to propagate to: ${parentHexagons.join(', ')}`);
|
|
945
|
-
|
|
946
|
-
// Check if data is already a hologram (reuse from federation section)
|
|
947
|
-
const isAlreadyHologram = holosphere.isHologram(data);
|
|
948
|
-
|
|
949
|
-
// Propagate to each parent hexagon
|
|
950
|
-
const parentPropagatePromises = parentHexagons.map(async (parentHexagon) => {
|
|
951
|
-
try {
|
|
952
|
-
let payloadToPut;
|
|
953
|
-
const parentFederationMeta = {
|
|
954
|
-
origin: holon, // The original holon from which this data is being propagated
|
|
955
|
-
sourceLens: lens, // The lens from which this data is being propagated
|
|
956
|
-
propagatedAt: Date.now(),
|
|
957
|
-
originalId: data.id,
|
|
958
|
-
propagationType: 'parent', // Indicate this is parent propagation
|
|
959
|
-
parentLevel: holonResolution - h3.getResolution(parentHexagon) // How many levels up
|
|
960
|
-
};
|
|
961
|
-
|
|
962
|
-
if (useHolograms && !isAlreadyHologram) {
|
|
963
|
-
// Create a new hologram referencing the original data
|
|
964
|
-
const newHologram = holosphere.createHologram(holon, lens, data);
|
|
965
|
-
payloadToPut = {
|
|
966
|
-
...newHologram, // This will be { id: data.id, soul: 'path/to/original' }
|
|
967
|
-
_federation: parentFederationMeta
|
|
968
|
-
};
|
|
969
|
-
} else {
|
|
970
|
-
// Propagate existing data (could be a full object or an existing hologram)
|
|
971
|
-
// Make a shallow copy and update/add _federation metadata
|
|
972
|
-
payloadToPut = {
|
|
973
|
-
...data,
|
|
974
|
-
_federation: {
|
|
975
|
-
...(data._federation || {}), // Preserve existing _federation fields if any
|
|
976
|
-
...parentFederationMeta // Add/overwrite with current propagation info
|
|
977
|
-
}
|
|
978
|
-
};
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
// Store in the parent hexagon with redirection disabled and no further auto-propagation
|
|
982
|
-
await holosphere.put(parentHexagon, lens, payloadToPut, null, {
|
|
983
|
-
disableHologramRedirection: true,
|
|
984
|
-
autoPropagate: false
|
|
985
|
-
});
|
|
986
|
-
|
|
987
|
-
console.log(`[Federation] Successfully propagated to parent hexagon: ${parentHexagon}`);
|
|
988
|
-
result.parentPropagation.success++;
|
|
989
|
-
return true;
|
|
990
|
-
} catch (error) {
|
|
991
|
-
console.error(`[Federation] Error propagating ${data.id} to parent hexagon ${parentHexagon}: ${error.message}`);
|
|
992
|
-
result.parentPropagation.errors++;
|
|
993
|
-
result.parentPropagation.messages.push(`Error propagating ${data.id} to parent hexagon ${parentHexagon}: ${error.message}`);
|
|
994
|
-
return false;
|
|
995
|
-
}
|
|
996
|
-
});
|
|
997
|
-
|
|
998
|
-
await Promise.all(parentPropagatePromises);
|
|
999
|
-
} else {
|
|
1000
|
-
result.parentPropagation.messages.push(`No parent hexagons found for ${holon} (already at resolution 0 or max levels reached)`);
|
|
1001
|
-
result.parentPropagation.skipped++;
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
} catch (error) {
|
|
1005
|
-
console.error(`[Federation] Error during parent propagation: ${error.message}`);
|
|
1006
|
-
result.parentPropagation.errors++;
|
|
1007
|
-
result.parentPropagation.messages.push(`Error during parent propagation: ${error.message}`);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
// ================================ END PARENT PROPAGATION ================================
|
|
1012
|
-
|
|
1013
|
-
result.propagated = result.success > 0 || result.parentPropagation.success > 0;
|
|
1014
|
-
return result;
|
|
1015
|
-
} catch (error) {
|
|
1016
|
-
return {
|
|
1017
|
-
...result,
|
|
1018
|
-
error: error.message
|
|
1019
|
-
};
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
|
|
1023
|
-
/**
|
|
1024
|
-
* Tracks a federated message across different chats
|
|
1025
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
1026
|
-
* @param {string} originalChatId - The ID of the original chat
|
|
1027
|
-
* @param {string} messageId - The ID of the original message
|
|
1028
|
-
* @param {string} federatedChatId - The ID of the federated chat
|
|
1029
|
-
* @param {string} federatedMessageId - The ID of the message in the federated chat
|
|
1030
|
-
* @param {string} type - The type of message (e.g., 'quest', 'announcement')
|
|
1031
|
-
* @returns {Promise<void>}
|
|
1032
|
-
*/
|
|
1033
|
-
export async function federateMessage(holosphere, originalChatId, messageId, federatedChatId, federatedMessageId, type = 'generic') {
|
|
1034
|
-
const trackingKey = `${originalChatId}_${messageId}_fedmsgs`;
|
|
1035
|
-
const tracking = await holosphere.getGlobal('federation_messages', trackingKey) || {
|
|
1036
|
-
id: trackingKey,
|
|
1037
|
-
originalChatId,
|
|
1038
|
-
originalMessageId: messageId,
|
|
1039
|
-
type,
|
|
1040
|
-
messages: []
|
|
1041
|
-
};
|
|
1042
|
-
|
|
1043
|
-
// Update or add the federated message info
|
|
1044
|
-
const existingMsg = tracking.messages.find(m => m.chatId === federatedChatId);
|
|
1045
|
-
if (existingMsg) {
|
|
1046
|
-
existingMsg.messageId = federatedMessageId;
|
|
1047
|
-
existingMsg.timestamp = Date.now();
|
|
1048
|
-
} else {
|
|
1049
|
-
tracking.messages.push({
|
|
1050
|
-
chatId: federatedChatId,
|
|
1051
|
-
messageId: federatedMessageId,
|
|
1052
|
-
timestamp: Date.now()
|
|
1053
|
-
});
|
|
1054
|
-
}
|
|
1055
|
-
|
|
1056
|
-
await holosphere.putGlobal('federation_messages', tracking);
|
|
1057
|
-
}
|
|
1058
|
-
|
|
1059
|
-
/**
|
|
1060
|
-
* Gets all federated messages for a given original message
|
|
1061
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
1062
|
-
* @param {string} originalChatId - The ID of the original chat
|
|
1063
|
-
* @param {string} messageId - The ID of the original message
|
|
1064
|
-
* @returns {Promise<Object|null>} The tracking information for the message
|
|
1065
|
-
*/
|
|
1066
|
-
export async function getFederatedMessages(holosphere, originalChatId, messageId) {
|
|
1067
|
-
const trackingKey = `${originalChatId}_${messageId}_fedmsgs`;
|
|
1068
|
-
return await holosphere.getGlobal('federation_messages', trackingKey);
|
|
1069
|
-
}
|
|
1070
|
-
|
|
1071
|
-
/**
|
|
1072
|
-
* Updates a federated message across all federated chats
|
|
1073
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
1074
|
-
* @param {string} originalChatId - The ID of the original chat
|
|
1075
|
-
* @param {string} messageId - The ID of the original message
|
|
1076
|
-
* @param {Function} updateCallback - Function to update the message in each chat
|
|
1077
|
-
* @returns {Promise<void>}
|
|
1078
|
-
*/
|
|
1079
|
-
export async function updateFederatedMessages(holosphere, originalChatId, messageId, updateCallback) {
|
|
1080
|
-
const tracking = await getFederatedMessages(holosphere, originalChatId, messageId);
|
|
1081
|
-
if (!tracking?.messages) return;
|
|
1082
|
-
|
|
1083
|
-
for (const msg of tracking.messages) {
|
|
1084
|
-
try {
|
|
1085
|
-
await updateCallback(msg.chatId, msg.messageId);
|
|
1086
|
-
} catch (error) {
|
|
1087
|
-
}
|
|
1088
|
-
}
|
|
1089
|
-
}
|
|
1090
|
-
|
|
1091
|
-
/**
|
|
1092
|
-
* Resets all federation relationships for a space
|
|
1093
|
-
* @param {object} holosphere - The HoloSphere instance
|
|
1094
|
-
* @param {string} spaceId - The ID of the space to reset federation for
|
|
1095
|
-
* @param {string} [password] - Optional password for the space
|
|
1096
|
-
* @param {object} [options] - Reset options
|
|
1097
|
-
* @param {boolean} [options.notifyPartners=true] - Whether to notify federation partners about the reset
|
|
1098
|
-
* @param {string} [options.spaceName] - Optional name for the space (defaults to spaceId if not provided)
|
|
1099
|
-
* @returns {Promise<object>} - Result object with success/error info
|
|
1100
|
-
*/
|
|
1101
|
-
export async function resetFederation(holosphere, spaceId, password = null, options = {}) {
|
|
1102
|
-
if (!spaceId) {
|
|
1103
|
-
throw new Error('resetFederation: Missing required space ID');
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
const {
|
|
1107
|
-
notifyPartners = true,
|
|
1108
|
-
spaceName = null
|
|
1109
|
-
} = options;
|
|
1110
|
-
|
|
1111
|
-
const result = {
|
|
1112
|
-
success: false,
|
|
1113
|
-
federatedCount: 0,
|
|
1114
|
-
notifyCount: 0,
|
|
1115
|
-
partnersNotified: 0,
|
|
1116
|
-
errors: []
|
|
1117
|
-
};
|
|
1118
|
-
|
|
1119
|
-
try {
|
|
1120
|
-
// Get current federation info to know what we're clearing
|
|
1121
|
-
const fedInfo = await getFederation(holosphere, spaceId, password);
|
|
1122
|
-
|
|
1123
|
-
if (!fedInfo) {
|
|
1124
|
-
return {
|
|
1125
|
-
...result,
|
|
1126
|
-
success: true,
|
|
1127
|
-
message: 'No federation configuration found for this space'
|
|
1128
|
-
};
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
// Store counts for reporting
|
|
1132
|
-
result.federatedCount = fedInfo.federation?.length || 0;
|
|
1133
|
-
result.notifyCount = fedInfo.notify?.length || 0;
|
|
1134
|
-
|
|
1135
|
-
// Create empty federation record
|
|
1136
|
-
const emptyFedInfo = {
|
|
1137
|
-
id: spaceId,
|
|
1138
|
-
name: spaceName || spaceId,
|
|
1139
|
-
federation: [],
|
|
1140
|
-
notify: [],
|
|
1141
|
-
timestamp: Date.now()
|
|
1142
|
-
};
|
|
1143
|
-
|
|
1144
|
-
// Update federation record with empty lists
|
|
1145
|
-
await holosphere.putGlobal('federation', emptyFedInfo, password);
|
|
1146
|
-
|
|
1147
|
-
// Notify federation partners if requested
|
|
1148
|
-
if (notifyPartners && fedInfo.federation && fedInfo.federation.length > 0) {
|
|
1149
|
-
const updatePromises = fedInfo.federation.map(async (partnerSpace) => {
|
|
1150
|
-
try {
|
|
1151
|
-
// Get partner's federation info
|
|
1152
|
-
const partnerFedInfo = await getFederation(holosphere, partnerSpace);
|
|
1153
|
-
|
|
1154
|
-
if (partnerFedInfo) {
|
|
1155
|
-
// Remove this space from partner's federation list
|
|
1156
|
-
if (partnerFedInfo.federation) {
|
|
1157
|
-
partnerFedInfo.federation = partnerFedInfo.federation.filter(
|
|
1158
|
-
id => id !== spaceId.toString()
|
|
1159
|
-
);
|
|
1160
|
-
}
|
|
1161
|
-
|
|
1162
|
-
// Remove this space from partner's notify list
|
|
1163
|
-
if (partnerFedInfo.notify) {
|
|
1164
|
-
partnerFedInfo.notify = partnerFedInfo.notify.filter(
|
|
1165
|
-
id => id !== spaceId.toString()
|
|
1166
|
-
);
|
|
1167
|
-
}
|
|
1168
|
-
|
|
1169
|
-
partnerFedInfo.timestamp = Date.now();
|
|
1170
|
-
|
|
1171
|
-
// Save partner's updated federation info
|
|
1172
|
-
await holosphere.putGlobal('federation', partnerFedInfo);
|
|
1173
|
-
result.partnersNotified++;
|
|
1174
|
-
return true;
|
|
1175
|
-
}
|
|
1176
|
-
return false;
|
|
1177
|
-
} catch (error) {
|
|
1178
|
-
result.errors.push({
|
|
1179
|
-
partner: partnerSpace,
|
|
1180
|
-
error: error.message
|
|
1181
|
-
});
|
|
1182
|
-
return false;
|
|
1183
|
-
}
|
|
1184
|
-
});
|
|
1185
|
-
|
|
1186
|
-
await Promise.all(updatePromises);
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
// Update federation metadata records if they exist
|
|
1190
|
-
if (fedInfo.federation && fedInfo.federation.length > 0) {
|
|
1191
|
-
for (const partnerSpace of fedInfo.federation) {
|
|
1192
|
-
try {
|
|
1193
|
-
const metaId = `${spaceId}_${partnerSpace}`;
|
|
1194
|
-
const altMetaId = `${partnerSpace}_${spaceId}`;
|
|
1195
|
-
|
|
1196
|
-
const meta = await holosphere.getGlobal('federationMeta', metaId) ||
|
|
1197
|
-
await holosphere.getGlobal('federationMeta', altMetaId);
|
|
1198
|
-
|
|
1199
|
-
if (meta) {
|
|
1200
|
-
meta.status = 'inactive';
|
|
1201
|
-
meta.endedAt = Date.now();
|
|
1202
|
-
await holosphere.putGlobal('federationMeta', meta);
|
|
1203
|
-
}
|
|
1204
|
-
} catch (error) {
|
|
1205
|
-
}
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
|
|
1209
|
-
result.success = true;
|
|
1210
|
-
return result;
|
|
1211
|
-
} catch (error) {
|
|
1212
|
-
return {
|
|
1213
|
-
...result,
|
|
1214
|
-
success: false,
|
|
1215
|
-
error: error.message
|
|
1216
|
-
};
|
|
1217
|
-
}
|
|
1218
|
-
}
|
|
1219
|
-
|
|
1220
|
-
// Export all federation operations as default
|
|
1221
|
-
export default {
|
|
1222
|
-
federate,
|
|
1223
|
-
subscribeFederation,
|
|
1224
|
-
getFederation,
|
|
1225
|
-
getFederatedConfig,
|
|
1226
|
-
unfederate,
|
|
1227
|
-
removeNotify,
|
|
1228
|
-
getFederated,
|
|
1229
|
-
propagate,
|
|
1230
|
-
federateMessage,
|
|
1231
|
-
getFederatedMessages,
|
|
1232
|
-
updateFederatedMessages,
|
|
1233
|
-
resetFederation
|
|
1234
|
-
};
|