holosphere 1.1.21 → 1.3.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/holosphere.js CHANGED
@@ -1,13 +1,12 @@
1
1
  /**
2
2
  * @module holosphere
3
- * @version 1.1.21
3
+ * @version 1.3.0
4
4
  * @description Holonic Geospatial Communication Infrastructure
5
5
  * @author Roberto Valenti
6
6
  * @license GPL-3.0-or-later
7
7
  */
8
8
 
9
9
  import * as h3 from 'h3-js';
10
- import OpenAI from 'openai';
11
10
  import Gun from 'gun'
12
11
  import Ajv2019 from 'ajv/dist/2019.js'
13
12
  import * as Federation from './federation.js';
@@ -19,34 +18,90 @@ import * as HologramOps from './hologram.js';
19
18
  import * as ComputeOps from './compute.js';
20
19
  import * as Utils from './utils.js';
21
20
 
21
+ // Named exports (v2-compatible)
22
+ import { nostrUtils } from './nostr-utils-shim.js';
23
+ import { subscriptions, buildLensPath } from './subscriptions-shim.js';
24
+ import { registry } from './registry-shim.js';
25
+ import * as handshake from './handshake-shim.js';
26
+
22
27
  // Define the version constant
23
- const HOLOSPHERE_VERSION = '1.1.21';
28
+ const HOLOSPHERE_VERSION = '1.3.0';
29
+ const version = HOLOSPHERE_VERSION;
24
30
 
25
31
  class HoloSphere {
26
32
  /**
27
33
  * Initializes a new instance of the HoloSphere class.
28
- * @param {string} appname - The name of the application.
29
- * @param {boolean} [strict=false] - Whether to enforce strict schema validation.
30
- * @param {string|null} [openaikey=null] - The OpenAI API key.
31
- * @param {object} [gunOptions={}] - Optional Gun constructor options (e.g., peers, localStorage, radisk).
32
- */
33
- constructor(appname, strict = false, openaikey = null, gunOptions = {}) {
34
+ * Supports both v1 positional args and v2 config object.
35
+ *
36
+ * v1: new HoloSphere(appname, strict, openaikey, gunOptions)
37
+ * v2: new HoloSphere({ appName, privateKey, backend, nostr: { peers, relays } })
38
+ *
39
+ * @param {string|object} appnameOrConfig - App name string (v1) or config object (v2).
40
+ * @param {boolean} [strict=false] - Whether to enforce strict schema validation (v1 only).
41
+ * @param {string|null} [openaikey=null] - The OpenAI API key (v1 only).
42
+ * @param {object} [gunOptions={}] - Optional Gun constructor options (v1 only).
43
+ */
44
+ constructor(appnameOrConfig, strict = false, openaikey = null, gunOptions = {}) {
45
+ // Detect v2-style config object
46
+ if (typeof appnameOrConfig === 'object' && appnameOrConfig !== null) {
47
+ const config = appnameOrConfig;
48
+ this.config = config;
49
+ this.appname = config.appName || config.appname || 'holosphere';
50
+ this.strict = config.strict || false;
51
+ this._privateKey = config.privateKey || null;
52
+
53
+ // Derive public key from private key
54
+ if (this._privateKey) {
55
+ try {
56
+ const pubHex = nostrUtils.getPublicKeyFromBytes
57
+ ? nostrUtils.getPublicKeyFromBytes(this._privateKey)
58
+ : nostrUtils.getPublicKey(
59
+ typeof this._privateKey === 'string'
60
+ ? this._privateKey
61
+ : nostrUtils.bytesToHex(this._privateKey)
62
+ );
63
+ this.client = { publicKey: pubHex };
64
+ } catch (e) {
65
+ console.warn('Failed to derive public key from private key:', e.message);
66
+ this.client = { publicKey: '' };
67
+ }
68
+ } else {
69
+ this.client = { publicKey: '' };
70
+ }
71
+
72
+ // Map nostr relay/peer config to GunDB peers
73
+ const relays = config.nostr?.relays || config.nostr?.peers || [];
74
+ if (relays.length > 0) {
75
+ const gunPeers = relays.map(r =>
76
+ r.replace('wss://', 'https://').replace('ws://', 'http://') + '/gun'
77
+ );
78
+ gunOptions = { peers: gunPeers, ...gunOptions };
79
+ }
80
+
81
+ openaikey = config.openaiKey || config.openaikey || null;
82
+ } else {
83
+ // v1-style positional args
84
+ this.appname = appnameOrConfig;
85
+ this.config = { appName: appnameOrConfig };
86
+ this.client = { publicKey: '' };
87
+ this.strict = strict;
88
+ this._privateKey = null;
89
+ }
90
+
34
91
  console.log('HoloSphere v' + HOLOSPHERE_VERSION);
35
- this.appname = appname
36
- this.strict = strict;
92
+
37
93
  this.validator = new Ajv2019({
38
94
  allErrors: true,
39
- strict: false, // Keep this false to avoid Ajv strict mode issues
40
- validateSchema: true // Always validate schemas
95
+ strict: false,
96
+ validateSchema: true
41
97
  });
42
98
 
43
-
44
99
  // Define default Gun options with radisk enabled
45
100
  const defaultGunOptions = {
46
101
  peers: ['https://gun.holons.io/gun'],
47
102
  axe: false,
48
- radisk: true, // Enable radisk storage by default
49
- file: './holosphere' // Default directory for radisk storage
103
+ radisk: true,
104
+ file: './holosphere'
50
105
  };
51
106
 
52
107
  // In browser environment, disable localStorage when radisk is enabled
@@ -59,20 +114,28 @@ class HoloSphere {
59
114
  console.log("Initializing Gun with options:", finalGunOptions);
60
115
 
61
116
  // Use provided Gun instance or create new one with final options
62
- this.gun = Gun(finalGunOptions); // Pass the merged options
63
-
117
+ this.gun = Gun(finalGunOptions);
64
118
 
65
- if (openaikey != null) {
66
- this.openai = new OpenAI({
67
- apiKey: openaikey,
68
- });
69
- }
119
+ // OpenAI is optional - callers can set this.openai directly if needed
120
+ this.openai = null;
70
121
 
71
122
  // Initialize subscriptions
72
123
  this.subscriptions = {};
73
-
124
+
74
125
  // Initialize schema cache
75
126
  this.schemaCache = new Map();
127
+
128
+ // Initialize allowed authors set (for canWrite)
129
+ this._allowedAuthors = new Set();
130
+ }
131
+
132
+ /**
133
+ * Waits for the HoloSphere instance to be ready.
134
+ * GunDB connects eagerly, so this resolves immediately.
135
+ * @returns {Promise<HoloSphere>} - The ready instance
136
+ */
137
+ async ready() {
138
+ return this;
76
139
  }
77
140
 
78
141
  getGun() {
@@ -81,37 +144,15 @@ class HoloSphere {
81
144
 
82
145
  // ================================ SCHEMA FUNCTIONS ================================
83
146
 
84
- /**
85
- * Sets the JSON schema for a specific lens.
86
- * @param {string} lens - The lens identifier.
87
- * @param {object} schema - The JSON schema to set.
88
- * @returns {Promise} - Resolves when the schema is set.
89
- */
90
147
  async setSchema(lens, schema) {
91
- // Delegate to the external function
92
148
  return SchemaOps.setSchema(this, lens, schema);
93
149
  }
94
150
 
95
- /**
96
- * Retrieves the JSON schema for a specific lens.
97
- * @param {string} lens - The lens identifier.
98
- * @param {object} [options] - Additional options
99
- * @param {boolean} [options.useCache=true] - Whether to use the schema cache
100
- * @param {number} [options.maxCacheAge=3600000] - Maximum cache age in milliseconds (default: 1 hour)
101
- * @returns {Promise<object|null>} - The retrieved schema or null if not found.
102
- */
103
151
  async getSchema(lens, options = {}) {
104
- // Delegate to the external function
105
152
  return SchemaOps.getSchema(this, lens, options);
106
153
  }
107
154
 
108
- /**
109
- * Clears the schema cache or a specific schema from the cache.
110
- * @param {string} [lens] - Optional lens to clear from cache. If not provided, clears entire cache.
111
- * @returns {boolean} - Returns true if successful
112
- */
113
155
  clearSchemaCache(lens = null) {
114
- // Delegate to the external function
115
156
  return SchemaOps.clearSchemaCache(this, lens);
116
157
  }
117
158
 
@@ -119,458 +160,271 @@ class HoloSphere {
119
160
 
120
161
  /**
121
162
  * Stores content in the specified holon and lens.
122
- * @param {string} holon - The holon identifier.
123
- * @param {string} lens - The lens under which to store the content.
124
- * @param {object} data - The data to store.
125
- * @param {string} [password] - Optional password for private holon.
126
- * @param {object} [options] - Additional options
127
- * @param {boolean} [options.autoPropagate=true] - Whether to automatically propagate to federated holons (default: true)
128
- * @param {object} [options.propagationOptions] - Options to pass to propagate
129
- * @param {boolean} [options.propagationOptions.useReferences=true] - Whether to use references instead of duplicating data
130
- * @returns {Promise<boolean>} - Returns true if successful, false if there was an error
131
- */
132
- async put(holon, lens, data, password = null, options = {}) {
133
- // Delegate to the external function
163
+ * Supports both v1 and v2 calling conventions:
164
+ * v1: put(holon, lens, data, password, options)
165
+ * v2: put(holon, lens, data, { actingAs }) or put(holon, lens, data)
166
+ */
167
+ async put(holon, lens, data, passwordOrOptions = null, options = {}) {
168
+ let password = null;
169
+ if (typeof passwordOrOptions === 'object' && passwordOrOptions !== null) {
170
+ // v2-style: 4th arg is options object (e.g., { actingAs })
171
+ options = passwordOrOptions;
172
+ password = options.password || null;
173
+ } else {
174
+ // v1-style: 4th arg is password string
175
+ password = passwordOrOptions;
176
+ }
134
177
  return ContentOps.put(this, holon, lens, data, password, options);
135
178
  }
136
179
 
137
180
  /**
138
181
  * Retrieves content from the specified holon and lens.
139
- * @param {string} holon - The holon identifier.
140
- * @param {string} lens - The lens from which to retrieve content.
141
- * @param {string} key - The specific key to retrieve.
142
- * @param {string} [password] - Optional password for private holon.
143
- * @param {object} [options] - Additional options
144
- * @param {boolean} [options.resolveReferences=true] - Whether to automatically resolve federation references
145
- * @returns {Promise<object|null>} - The retrieved content or null if not found.
146
- */
147
- async get(holon, lens, key, password = null, options = {}) {
148
- // Delegate to the external function
182
+ * Supports both v1 and v2 calling conventions:
183
+ * v1: get(holon, lens, key, password, options)
184
+ * v2: get(holon, lens) or get(holon, lens, key)
185
+ */
186
+ async get(holon, lens, key = null, password = null, options = {}) {
187
+ if (key === null || key === undefined) {
188
+ // v2-style 2-arg call: get entire lens (return first/only item)
189
+ const items = await ContentOps.getAll(this, holon, lens, null);
190
+ return items && items.length > 0 ? items[0] : null;
191
+ }
149
192
  return ContentOps.get(this, holon, lens, key, password, options);
150
193
  }
151
194
 
152
- /**
153
- * Retrieves all content from the specified holon and lens.
154
- * @param {string} holon - The holon identifier.
155
- * @param {string} lens - The lens from which to retrieve content.
156
- * @param {string} [password] - Optional password for private holon.
157
- * @returns {Promise<Array<object>>} - The retrieved content.
158
- */
159
195
  async getAll(holon, lens, password = null) {
160
- // Delegate to the external function
161
196
  return ContentOps.getAll(this, holon, lens, password);
162
197
  }
163
198
 
164
- /**
165
- * Parses data from GunDB, handling various data formats and references.
166
- * @param {*} data - The data to parse, could be a string, object, or GunDB reference.
167
- * @returns {Promise<object>} - The parsed data.
168
- */
169
199
  async parse(rawData) {
170
- // Delegate to the external function
171
200
  return ContentOps.parse(this, rawData);
172
201
  }
173
202
 
174
- /**
175
- * Deletes a specific key from a given holon and lens.
176
- * @param {string} holon - The holon identifier.
177
- * @param {string} lens - The lens from which to delete the key.
178
- * @param {string} key - The specific key to delete.
179
- * @param {string} [password] - Optional password for private holon.
180
- * @returns {Promise<boolean>} - Returns true if successful
181
- */
182
203
  async delete(holon, lens, key, password = null) {
183
- // Delegate to the external function (renamed to deleteFunc in module)
184
204
  return ContentOps.deleteFunc(this, holon, lens, key, password);
185
205
  }
186
206
 
187
- /**
188
- * Deletes all keys from a given holon and lens.
189
- * @param {string} holon - The holon identifier.
190
- * @param {string} lens - The lens from which to delete all keys.
191
- * @param {string} [password] - Optional password for private holon.
192
- * @returns {Promise<boolean>} - Returns true if successful
193
- */
194
207
  async deleteAll(holon, lens, password = null) {
195
- // Delegate to the external function
196
208
  return ContentOps.deleteAll(this, holon, lens, password);
197
209
  }
198
210
 
199
211
  // ================================ NODE FUNCTIONS ================================
200
212
 
201
-
202
- /**
203
- * Stores a specific gun node in a given holon and lens.
204
- * @param {string} holon - The holon identifier.
205
- * @param {string} lens - The lens under which to store the node.
206
- * @param {object} data - The node to store.
207
- */
208
213
  async putNode(holon, lens, data) {
209
- // Delegate to the external function
210
214
  return NodeOps.putNode(this, holon, lens, data);
211
215
  }
212
216
 
213
- /**
214
- * Retrieves a specific gun node from the specified holon and lens.
215
- * @param {string} holon - The holon identifier.
216
- * @param {string} lens - The lens identifier.
217
- * @param {string} key - The specific key to retrieve.
218
- * @returns {Promise<any>} - The retrieved node or null if not found.
219
- */
220
217
  async getNode(holon, lens, key) {
221
- // Delegate to the external function
222
218
  return NodeOps.getNode(this, holon, lens, key);
223
219
  }
224
220
 
225
- /**
226
- * Retrieves a Gun node reference using its soul path
227
- * @param {string} soul - The soul path of the node
228
- * @returns {Gun.ChainReference} - The Gun node reference
229
- */
230
221
  getNodeRef(soul) {
231
- // Delegate to the external function
232
222
  return NodeOps.getNodeRef(this, soul);
233
223
  }
234
224
 
235
- /**
236
- * Retrieves a node directly using its soul path
237
- * @param {string} soul - The soul path of the node
238
- * @returns {Promise<any>} - The retrieved node or null if not found.
239
- */
240
225
  async getNodeBySoul(soul) {
241
- // Delegate to the external function
242
226
  return NodeOps.getNodeBySoul(this, soul);
243
227
  }
244
228
 
245
- /**
246
- * Deletes a specific gun node from a given holon and lens.
247
- * @param {string} holon - The holon identifier.
248
- * @param {string} lens - The lens identifier.
249
- * @param {string} key - The key of the node to delete.
250
- * @returns {Promise<boolean>} - Returns true if successful
251
- */
252
229
  async deleteNode(holon, lens, key) {
253
- // Delegate to the external function
254
230
  return NodeOps.deleteNode(this, holon, lens, key);
255
231
  }
256
232
 
257
233
  // ================================ GLOBAL FUNCTIONS ================================
258
- /**
259
- * Stores data in a global (non-holon-specific) table.
260
- * @param {string} tableName - The table name to store data in.
261
- * @param {object} data - The data to store. If it has an 'id' field, it will be used as the key.
262
- * @param {string} [password] - Optional password for private holon.
263
- * @returns {Promise<void>}
264
- */
234
+
265
235
  async putGlobal(tableName, data, password = null) {
266
- // Delegate to the external function
267
236
  return GlobalOps.putGlobal(this, tableName, data, password);
268
237
  }
269
238
 
270
239
  /**
271
- * Retrieves a specific key from a global table.
272
- * @param {string} tableName - The table name to retrieve from.
273
- * @param {string} key - The key to retrieve.
274
- * @param {string} [password] - Optional password for private holon.
275
- * @returns {Promise<object|null>} - The parsed data for the key or null if not found.
240
+ * v2-compatible alias for putGlobal (no password param)
276
241
  */
242
+ async writeGlobal(tableName, data) {
243
+ return GlobalOps.putGlobal(this, tableName, data, null);
244
+ }
245
+
277
246
  async getGlobal(tableName, key, password = null) {
278
- // Delegate to the external function
279
247
  return GlobalOps.getGlobal(this, tableName, key, password);
280
248
  }
281
249
 
282
- /**
283
- * Retrieves all data from a global table.
284
- * @param {string} tableName - The table name to retrieve data from.
285
- * @param {string} [password] - Optional password for private holon.
286
- * @returns {Promise<Array<object>>} - The parsed data from the table as an array.
287
- */
288
250
  async getAllGlobal(tableName, password = null) {
289
- // Delegate to the external function
290
251
  return GlobalOps.getAllGlobal(this, tableName, password);
291
252
  }
292
253
 
293
- /**
294
- * Deletes a specific key from a global table.
295
- * @param {string} tableName - The table name to delete from.
296
- * @param {string} key - The key to delete.
297
- * @param {string} [password] - Optional password for private holon.
298
- * @returns {Promise<boolean>}
299
- */
300
254
  async deleteGlobal(tableName, key, password = null) {
301
- // Delegate to the external function
302
255
  return GlobalOps.deleteGlobal(this, tableName, key, password);
303
256
  }
304
257
 
305
- /**
306
- * Deletes an entire global table.
307
- * @param {string} tableName - The table name to delete.
308
- * @param {string} [password] - Optional password for private holon.
309
- * @returns {Promise<boolean>}
310
- */
311
258
  async deleteAllGlobal(tableName, password = null) {
312
- // Delegate to the external function
313
259
  return GlobalOps.deleteAllGlobal(this, tableName, password);
314
260
  }
315
261
 
316
- // ================================ REFERENCE FUNCTIONS ================================
317
-
318
262
  /**
319
- * Creates a soul hologram object for a data item
320
- * @param {string} holon - The holon where the original data is stored
321
- * @param {string} lens - The lens where the original data is stored
322
- * @param {object} data - The data to create a hologram for
323
- * @returns {object} - A hologram object with id and soul
263
+ * Subscribe to real-time changes in a global table.
264
+ * v2-compatible: subscribeGlobal(lens, key, callback, options)
324
265
  */
266
+ async subscribeGlobal(lens, keyOrCallback, callbackOrOptions, options = {}) {
267
+ let key, callback;
268
+ if (typeof keyOrCallback === 'function') {
269
+ callback = keyOrCallback;
270
+ key = null;
271
+ options = callbackOrOptions || {};
272
+ } else {
273
+ key = keyOrCallback;
274
+ callback = callbackOrOptions;
275
+ }
276
+ return GlobalOps.subscribeGlobal(this, lens, key, callback, options);
277
+ }
278
+
279
+ // ================================ REFERENCE FUNCTIONS ================================
280
+
325
281
  createHologram(holon, lens, data) {
326
- // Delegate to the external function
327
282
  return HologramOps.createHologram(this, holon, lens, data);
328
283
  }
329
-
330
- /**
331
- * Parses a soul path into its components
332
- * @param {string} soul - The soul path to parse
333
- * @returns {object|null} - The parsed components or null if invalid format
334
- */
284
+
335
285
  parseSoulPath(soul) {
336
- // Delegate to the external function (doesn't need instance)
337
286
  return HologramOps.parseSoulPath(soul);
338
287
  }
339
-
340
- /**
341
- * Checks if an object is a hologram
342
- * @param {object} data - The data to check
343
- * @returns {boolean} - True if the object is a hologram
344
- */
288
+
345
289
  isHologram(data) {
346
- // Delegate to the external function (doesn't need instance)
347
290
  return HologramOps.isHologram(data);
348
291
  }
349
-
350
- /**
351
- * Resolves a hologram to its actual data
352
- * @param {object} hologram - The hologram to resolve
353
- * @param {object} [options] - Optional parameters
354
- * @param {boolean} [options.followHolograms=true] - Whether to follow nested holograms
355
- * @param {Set<string>} [options.visited] - Internal use: Tracks visited souls to prevent loops
356
- * @returns {Promise<object|null>} - The resolved data, null if resolution failed due to target not found, or the original hologram for circular/invalid cases.
357
- */
292
+
358
293
  async resolveHologram(hologram, options = {}) {
359
- // Delegate to the external function
360
294
  return HologramOps.resolveHologram(this, hologram, options);
361
295
  }
362
296
 
363
297
  // ================================ COMPUTE FUNCTIONS ================================
364
- /**
365
- * Computes operations across multiple layers up the hierarchy
366
- * @param {string} holon - Starting holon identifier
367
- * @param {string} lens - The lens to compute
368
- * @param {object} options - Computation options
369
- * @param {number} [maxLevels=15] - Maximum levels to compute up
370
- * @param {string} [password] - Optional password for private holons
371
- */
298
+
372
299
  async computeHierarchy(holon, lens, options, maxLevels = 15, password = null) {
373
- // Delegate to the external function
374
300
  return ComputeOps.computeHierarchy(this, holon, lens, options, maxLevels, password);
375
301
  }
376
302
 
377
- /**
378
- * Computes operations on content within a holon and lens for one layer up.
379
- * @param {string} holon - The holon identifier.
380
- * @param {string} lens - The lens to compute.
381
- * @param {object} options - Computation options
382
- * @param {string} options.operation - The operation to perform ('summarize', 'aggregate', 'concatenate')
383
- * @param {string[]} [options.fields] - Fields to perform operation on
384
- * @param {string} [options.targetField] - Field to store the result in
385
- * @param {string} [password] - Optional password for private holons
386
- * @throws {Error} If parameters are invalid or missing
387
- */
388
303
  async compute(holon, lens, options, password = null) {
389
- // Delegate to the external function
390
304
  return ComputeOps.compute(this, holon, lens, options, password);
391
305
  }
392
306
 
393
- /**
394
- * Summarizes provided history text using OpenAI.
395
- * @param {string} history - The history text to summarize.
396
- * @returns {Promise<string>} - The summarized text.
397
- */
398
307
  async summarize(history) {
399
- // Delegate to the external function
400
308
  return ComputeOps.summarize(this, history);
401
309
  }
402
310
 
403
- /**
404
- * Upcasts content to parent holonagons recursively using references.
405
- * @param {string} holon - The current holon identifier.
406
- * @param {string} lens - The lens under which to upcast.
407
- * @param {object} content - The content to upcast.
408
- * @param {number} [maxLevels=15] - Maximum levels to upcast.
409
- * @returns {Promise<object>} - The original content.
410
- */
411
311
  async upcast(holon, lens, content, maxLevels = 15) {
412
- // Delegate to the external function
413
312
  return ComputeOps.upcast(this, holon, lens, content, maxLevels);
414
313
  }
415
314
 
416
- /**
417
- * Updates the parent holon with a new report.
418
- * @param {string} id - The child holon identifier.
419
- * @param {string} report - The report to update.
420
- * @returns {Promise<object>} - The updated parent information.
421
- */
422
315
  async updateParent(id, report) {
423
- // Delegate to the external function
424
316
  return ComputeOps.updateParent(this, id, report);
425
317
  }
426
318
 
427
- /**
428
- * Propagates data to federated holons
429
- * @param {string} holon - The holon identifier
430
- * @param {string} lens - The lens identifier
431
- * @param {object} data - The data to propagate
432
- * @param {object} [options] - Propagation options
433
- * @returns {Promise<object>} - Result with success count and errors
434
- */
435
319
  async propagate(holon, lens, data, options = {}) {
436
320
  return Federation.propagate(this, holon, lens, data, options);
437
321
  }
438
322
 
439
- /**
440
- * Converts latitude and longitude to a holon identifier.
441
- * @param {number} lat - The latitude.
442
- * @param {number} lng - The longitude.
443
- * @param {number} resolution - The resolution level.
444
- * @returns {Promise<string>} - The resulting holon identifier.
445
- */
446
323
  async getHolon(lat, lng, resolution) {
447
- // Delegate to the external function
448
324
  return Utils.getHolon(lat, lng, resolution);
449
325
  }
450
326
 
451
- /**
452
- * Retrieves all containing holonagons at all scales for given coordinates.
453
- * @param {number} lat - The latitude.
454
- * @param {number} lng - The longitude.
455
- * @returns {Array<string>} - List of holon identifiers.
456
- */
457
327
  getScalespace(lat, lng) {
458
- // Delegate to the external function
459
328
  return Utils.getScalespace(lat, lng);
460
329
  }
461
330
 
462
- /**
463
- * Retrieves all containing holonagons at all scales for a given holon.
464
- * @param {string} holon - The holon identifier.
465
- * @returns {Array<string>} - List of holon identifiers.
466
- */
467
331
  getHolonScalespace(holon) {
468
- // Delegate to the external function
469
332
  return Utils.getHolonScalespace(holon);
470
333
  }
471
334
 
472
- /**
473
- * Subscribes to changes in a specific holon and lens.
474
- * @param {string} holon - The holon identifier.
475
- * @param {string} lens - The lens to subscribe to.
476
- * @param {function} callback - The callback to execute on changes.
477
- * @returns {Promise<object>} - Subscription object with unsubscribe method
478
- */
479
335
  async subscribe(holon, lens, callback) {
480
- // Delegate to the external function
481
336
  return Utils.subscribe(this, holon, lens, callback);
482
337
  }
483
338
 
484
- /**
485
- * Notifies subscribers about data changes
486
- * @param {object} data - The data to notify about
487
- * @private
488
- */
489
339
  notifySubscribers(data) {
490
- // Delegate to the external function
491
340
  return Utils.notifySubscribers(this, data);
492
341
  }
493
342
 
494
- // Add ID generation method
495
343
  generateId() {
496
- // Delegate to the external function
497
344
  return Utils.generateId();
498
345
  }
499
346
 
500
347
  // ================================ FEDERATION FUNCTIONS ================================
501
348
 
502
- /**
503
- * Creates a federation relationship between two holons
504
- * @param {string} holonId1 - The first holon ID
505
- * @param {string} holonId2 - The second holon ID
506
- * @param {string} [password1] - Optional password for the first holon
507
- * @param {string} [password2] - Optional password for the second holon
508
- * @param {boolean} [bidirectional=true] - Whether to set up bidirectional notifications automatically
509
- * @param {object} [lensConfig] - Optional lens-specific configuration
510
- * @param {string[]} [lensConfig.federate] - List of lenses to federate (default: all)
511
- * @param {string[]} [lensConfig.notify] - List of lenses to notify (default: all)
512
- * @returns {Promise<boolean>} - True if federation was created successfully
513
- */
514
349
  async federate(holonId1, holonId2, password1 = null, password2 = null, bidirectional = true, lensConfig = {}) {
515
350
  return Federation.federate(this, holonId1, holonId2, password1, password2, bidirectional, lensConfig);
516
351
  }
517
352
 
518
353
  /**
519
- * Subscribes to federation notifications for a holon
520
- * @param {string} holonId - The holon ID to subscribe to
521
- * @param {string} password - Password for the holon
522
- * @param {function} callback - The callback to execute on notifications
523
- * @param {object} [options] - Subscription options
524
- * @param {string[]} [options.lenses] - Specific lenses to subscribe to (default: all)
525
- * @param {number} [options.throttle] - Throttle notifications in ms (default: 0)
526
- * @returns {Promise<object>} - Subscription object with unsubscribe() method
354
+ * v2-compatible federation creation.
355
+ * Maps v2 options to v1 federate() call.
356
+ * @param {string} sourceHolon - Source holon ID
357
+ * @param {string} targetHolon - Target holon ID
358
+ * @param {object} [options] - Federation options
359
+ * @param {object} [options.lensConfig] - Lens configuration
360
+ * @param {string} [options.partnerName] - Name of the partner holon
361
+ * @param {boolean} [options.skipPropagation] - Skip data propagation
362
+ * @returns {Promise<boolean>}
363
+ */
364
+ async federateHolon(sourceHolon, targetHolon, options = {}) {
365
+ const lensConfig = options.lensConfig || {};
366
+ const lenses = lensConfig.lenses || lensConfig.outbound || lensConfig.federate || [];
367
+
368
+ // Store partner name if provided
369
+ if (options.partnerName) {
370
+ try {
371
+ const fedInfo = await this.getFederation(sourceHolon) || {
372
+ id: sourceHolon, name: sourceHolon,
373
+ federation: [], notify: [], lensConfig: {}, partnerNames: {}, timestamp: Date.now()
374
+ };
375
+ if (!fedInfo.partnerNames) fedInfo.partnerNames = {};
376
+ fedInfo.partnerNames[targetHolon] = options.partnerName;
377
+ await this.putGlobal('federation', fedInfo);
378
+ } catch (e) {
379
+ console.warn('Failed to store partner name:', e.message);
380
+ }
381
+ }
382
+
383
+ return Federation.federate(this, sourceHolon, targetHolon, null, null, true, {
384
+ federate: lenses,
385
+ notify: lenses
386
+ });
387
+ }
388
+
389
+ /**
390
+ * v2-compatible federation removal.
391
+ * @param {string} sourceHolon - Source holon ID
392
+ * @param {string} targetHolon - Target holon ID
393
+ * @returns {Promise<boolean>}
527
394
  */
395
+ async unfederateHolon(sourceHolon, targetHolon) {
396
+ return Federation.unfederate(this, sourceHolon, targetHolon, null, null);
397
+ }
398
+
528
399
  async subscribeFederation(holonId, password, callback, options = {}) {
529
400
  return Federation.subscribeFederation(this, holonId, password, callback, options);
530
401
  }
531
402
 
532
403
  /**
533
- * Gets federation info for a holon
534
- * @param {string} holonId - The holon ID
535
- * @param {string} [password] - Optional password for the holon
536
- * @returns {Promise<object|null>} - Federation info or null if not found
404
+ * Gets federation info for a holon.
405
+ * Returns v2-compatible shape with `federated`, `lensConfig`, `partnerNames` fields.
537
406
  */
538
407
  async getFederation(holonId, password = null) {
539
- return Federation.getFederation(this, holonId, password);
408
+ const result = await Federation.getFederation(this, holonId, password);
409
+ if (!result) return { federated: [], lensConfig: {}, partnerNames: {} };
410
+
411
+ // Add v2-compatible fields alongside existing v1 fields
412
+ if (!result.federated) result.federated = result.federation || [];
413
+ if (!result.partnerNames) result.partnerNames = {};
414
+ // Ensure lensConfig exists (v1 already stores this)
415
+ if (!result.lensConfig) result.lensConfig = {};
416
+
417
+ return result;
540
418
  }
541
-
542
- /**
543
- * Retrieves the lens-specific configuration for a federation link between two holons.
544
- * @param {string} holonId - The ID of the source holon.
545
- * @param {string} targetHolonId - The ID of the target holon in the federation link.
546
- * @param {string} [password] - Optional password for the source holon.
547
- * @returns {Promise<object|null>} - An object with 'federate' and 'notify' arrays, or null if not found.
548
- */
419
+
549
420
  async getFederatedConfig(holonId, targetHolonId, password = null) {
550
421
  return Federation.getFederatedConfig(this, holonId, targetHolonId, password);
551
422
  }
552
423
 
553
- /**
554
- * Removes a federation relationship between holons
555
- * @param {string} holonId1 - The first holon ID
556
- * @param {string} holonId2 - The second holon ID
557
- * @param {string} password1 - Password for the first holon
558
- * @param {string} [password2] - Optional password for the second holon
559
- * @returns {Promise<boolean>} - True if federation was removed successfully
560
- */
561
424
  async unfederate(holonId1, holonId2, password1, password2 = null) {
562
425
  return await Federation.unfederate(this, holonId1, holonId2, password1, password2);
563
426
  }
564
427
 
565
- /**
566
- * Removes a notification relationship between two spaces
567
- * This removes spaceId2 from the notify list of spaceId1
568
- *
569
- * @param {string} holonId1 - The space to modify (remove from its notify list)
570
- * @param {string} holonId2 - The space to be removed from notifications
571
- * @param {string} [password1] - Optional password for the first space
572
- * @returns {Promise<boolean>} - True if notification was removed successfully
573
- */
574
428
  async removeNotify(holonId1, holonId2, password1 = null) {
575
429
  console.log(`HoloSphere.removeNotify called: ${holonId1}, ${holonId2}`);
576
430
  try {
@@ -583,99 +437,96 @@ class HoloSphere {
583
437
  }
584
438
  }
585
439
 
586
- /**
587
- * Get and aggregate data from federated holons
588
- * @param {string} holon The holon name
589
- * @param {string} lens The lens name
590
- * @param {Object} options Options for retrieval and aggregation
591
- * @returns {Promise<Array>} Combined array of local and federated data
592
- */
593
440
  async getFederated(holon, lens, options = {}) {
594
441
  return Federation.getFederated(this, holon, lens, options);
595
442
  }
596
443
 
597
- /**
598
- * Tracks a federated message across different chats
599
- * @param {string} originalChatId - The ID of the original chat
600
- * @param {string} messageId - The ID of the original message
601
- * @param {string} federatedChatId - The ID of the federated chat
602
- * @param {string} federatedMessageId - The ID of the message in the federated chat
603
- * @param {string} type - The type of message (e.g., 'quest', 'announcement')
604
- * @returns {Promise<void>}
605
- */
606
444
  async federateMessage(originalChatId, messageId, federatedChatId, federatedMessageId, type = 'generic') {
607
445
  return Federation.federateMessage(this, originalChatId, messageId, federatedChatId, federatedMessageId, type);
608
446
  }
609
447
 
610
- /**
611
- * Gets all federated messages for a given original message
612
- * @param {string} originalChatId - The ID of the original chat
613
- * @param {string} messageId - The ID of the original message
614
- * @returns {Promise<Object|null>} The tracking information for the message
615
- */
616
448
  async getFederatedMessages(originalChatId, messageId) {
617
449
  return Federation.getFederatedMessages(this, originalChatId, messageId);
618
450
  }
619
451
 
620
- /**
621
- * Updates a federated message across all federated chats
622
- * @param {string} originalChatId - The ID of the original chat
623
- * @param {string} messageId - The ID of the original message
624
- * @param {Function} updateCallback - Function to update the message in each chat
625
- * @returns {Promise<void>}
626
- */
627
452
  async updateFederatedMessages(originalChatId, messageId, updateCallback) {
628
453
  return Federation.updateFederatedMessages(this, originalChatId, messageId, updateCallback);
629
454
  }
630
455
 
456
+ async resetFederation(holonId, password = null) {
457
+ return Federation.resetFederation(this, holonId, password);
458
+ }
459
+
460
+ // ================================ AUTHORIZATION FUNCTIONS ================================
461
+
631
462
  /**
632
- * Resets the federation settings for a holon
463
+ * Check if a public key can write to a holon/lens.
633
464
  * @param {string} holonId - The holon ID
634
- * @param {string} [password] - Optional password for the holon
635
- * @returns {Promise<boolean>} - True if federation was reset successfully
465
+ * @param {string} lensName - The lens name
466
+ * @param {string} actingAs - The public key attempting to write
467
+ * @param {object} [options] - Additional options
468
+ * @returns {Promise<{ canWrite: boolean, reason: string, accessType: string }>}
636
469
  */
637
- async resetFederation(holonId, password = null) {
638
- return Federation.resetFederation(this, holonId, password);
470
+ async canWrite(holonId, lensName, actingAs, options = {}) {
471
+ // Owner always has access
472
+ if (actingAs === this.client?.publicKey || actingAs === holonId) {
473
+ return { canWrite: true, reason: 'owner', accessType: 'owner' };
474
+ }
475
+
476
+ // Check allowed authors
477
+ if (this._allowedAuthors.has(actingAs)) {
478
+ return { canWrite: true, reason: 'allowed_author', accessType: 'allowed' };
479
+ }
480
+
481
+ // Check federation
482
+ try {
483
+ const fed = await Federation.getFederation(this, holonId);
484
+ if (fed && fed.federation && fed.federation.includes(actingAs)) {
485
+ return { canWrite: true, reason: 'federated', accessType: 'federation' };
486
+ }
487
+ } catch (e) { /* ignore */ }
488
+
489
+ return { canWrite: false, reason: 'not_authorized', accessType: 'none' };
639
490
  }
640
491
 
641
- // ================================ END FEDERATION FUNCTIONS ================================
642
492
  /**
643
- * Closes the HoloSphere instance and cleans up resources.
644
- * @returns {Promise<void>}
493
+ * Add a public key to the allowed authors list.
494
+ * @param {string} pubkey - The public key to allow
645
495
  */
646
- async close() {
647
- // Delegate to the external function
648
- return Utils.close(this);
496
+ addAllowedAuthor(pubkey) {
497
+ this._allowedAuthors.add(pubkey);
649
498
  }
650
499
 
651
500
  /**
652
- * Creates a namespaced username for Gun authentication
653
- * @private
654
- * @param {string} holonId - The holon ID
655
- * @returns {string} - Namespaced username
501
+ * Remove a public key from the allowed authors list.
502
+ * @param {string} pubkey - The public key to remove
656
503
  */
657
- userName(holonId) {
658
- // Delegate to the external function
659
- return Utils.userName(this, holonId);
504
+ removeAllowedAuthor(pubkey) {
505
+ this._allowedAuthors.delete(pubkey);
660
506
  }
661
507
 
662
508
  /**
663
- * Returns the current version of the HoloSphere library.
664
- * @returns {string} The library version.
509
+ * List all allowed authors.
510
+ * @returns {string[]}
665
511
  */
512
+ listAllowedAuthors() {
513
+ return Array.from(this._allowedAuthors);
514
+ }
515
+
516
+ // ================================ END FEDERATION FUNCTIONS ================================
517
+
518
+ async close() {
519
+ return Utils.close(this);
520
+ }
521
+
522
+ userName(holonId) {
523
+ return Utils.userName(this, holonId);
524
+ }
525
+
666
526
  getVersion() {
667
527
  return HOLOSPHERE_VERSION;
668
528
  }
669
529
 
670
- /**
671
- * Configures radisk storage options for GunDB.
672
- * @param {object} options - Radisk configuration options
673
- * @param {string} [options.file='./radata'] - Directory for radisk storage
674
- * @param {boolean} [options.radisk=true] - Whether to enable radisk storage
675
- * @param {number} [options.until] - Timestamp until which to keep data
676
- * @param {number} [options.retry] - Number of retries for failed operations
677
- * @param {number} [options.timeout] - Timeout for operations in milliseconds
678
- */
679
530
  configureRadisk(options = {}) {
680
531
  const defaultOptions = {
681
532
  file: './radata',
@@ -684,9 +535,9 @@ class HoloSphere {
684
535
  retry: 3,
685
536
  timeout: 5000
686
537
  };
687
-
538
+
688
539
  const radiskOptions = { ...defaultOptions, ...options };
689
-
540
+
690
541
  if (this.gun && this.gun._.opt) {
691
542
  Object.assign(this.gun._.opt, radiskOptions);
692
543
  console.log("Radisk configuration updated:", radiskOptions);
@@ -695,15 +546,11 @@ class HoloSphere {
695
546
  }
696
547
  }
697
548
 
698
- /**
699
- * Gets radisk storage statistics and information.
700
- * @returns {object} Radisk statistics including file path, enabled status, and storage info
701
- */
702
549
  getRadiskStats() {
703
550
  if (!this.gun || !this.gun._.opt) {
704
551
  return { error: "Gun instance not available" };
705
552
  }
706
-
553
+
707
554
  const options = this.gun._.opt;
708
555
  return {
709
556
  enabled: options.radisk || false,
@@ -717,4 +564,6 @@ class HoloSphere {
717
564
  }
718
565
  }
719
566
 
567
+ // Default and named exports (v2-compatible)
720
568
  export default HoloSphere;
569
+ export { HoloSphere, handshake, nostrUtils, subscriptions, buildLensPath, registry, version };