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