holosphere 1.0.8 → 1.1.0

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
@@ -7,22 +7,25 @@ class HoloSphere {
7
7
  /**
8
8
  * Initializes a new instance of the HoloSphere class.
9
9
  * @param {string} appname - The name of the application.
10
+ * @param {boolean} strict - Whether to enforce strict schema validation.
10
11
  * @param {string|null} openaikey - The OpenAI API key.
11
12
  */
12
- constructor(appname, openaikey = null) {
13
- this.validator = new Ajv2019({ allErrors: false, strict: false });
13
+ constructor(appname, strict = false, openaikey = null) {
14
+ this.appname = appname
15
+ this.strict = strict;
16
+ this.validator = new Ajv2019({
17
+ allErrors: true,
18
+ strict: false, // Keep this false to avoid Ajv strict mode issues
19
+ validateSchema: true // Always validate schemas
20
+ });
14
21
  this.gun = Gun({
15
- peers: ['http://gun.holons.io','https://59.src.eco/gun'],
22
+ peers: ['http://gun.holons.io', 'https://59.src.eco/gun'],
16
23
  axe: false,
17
24
  // uuid: (content) => { // generate a unique id for each node
18
25
  // console.log('uuid', content);
19
26
  // return content;}
20
27
  });
21
28
 
22
- this.gun = this.gun.get(appname)
23
- this.users = {}; // Initialize users
24
- this.holonagonVotes = {}; // Initialize holonagonVotes
25
-
26
29
  if (openaikey != null) {
27
30
  this.openai = new OpenAI({
28
31
  apiKey: openaikey,
@@ -30,6 +33,8 @@ class HoloSphere {
30
33
  }
31
34
  }
32
35
 
36
+ // ================================ SCHEMA FUNCTIONS ================================
37
+
33
38
  /**
34
39
  * Sets the JSON schema for a specific lens.
35
40
  * @param {string} lens - The lens identifier.
@@ -37,16 +42,84 @@ class HoloSphere {
37
42
  * @returns {Promise} - Resolves when the schema is set.
38
43
  */
39
44
  async setSchema(lens, schema) {
40
- return new Promise((resolve, reject) => {
41
- this.gun.get(lens).get('schema').put(JSON.stringify(schema), ack => {
42
- if (ack.err) {
43
- resolve(new Error('Failed to add schema: ' + ack.err));
44
- } else {
45
- console.log('Schema added successfully under lens:', lens);
46
- resolve(ack);
45
+ if (!lens || !schema) {
46
+ console.error('setSchema: Missing required parameters');
47
+ return false;
48
+ }
49
+
50
+ // Basic schema validation - check for required fields
51
+ if (!schema.type || typeof schema.type !== 'string') {
52
+ console.error('setSchema: Schema must have a type field');
53
+ return false;
54
+ }
55
+
56
+ if (this.strict) {
57
+ try {
58
+ // Validate schema against JSON Schema meta-schema
59
+ const metaSchema = {
60
+ type: 'object',
61
+ required: ['type', 'properties'],
62
+ properties: {
63
+ type: { type: 'string' },
64
+ properties: {
65
+ type: 'object',
66
+ additionalProperties: {
67
+ type: 'object',
68
+ required: ['type'],
69
+ properties: {
70
+ type: { type: 'string' }
71
+ }
72
+ }
73
+ },
74
+ required: {
75
+ type: 'array',
76
+ items: { type: 'string' }
77
+ }
78
+ }
79
+ };
80
+
81
+ const valid = this.validator.validate(metaSchema, schema);
82
+ if (!valid) {
83
+ console.error('setSchema: Invalid schema structure:', this.validator.errors);
84
+ return false;
47
85
  }
48
- })
49
- })
86
+
87
+ // Additional strict mode checks
88
+ if (!schema.properties || typeof schema.properties !== 'object') {
89
+ console.error('setSchema: Schema must have properties in strict mode');
90
+ return false;
91
+ }
92
+
93
+ if (!schema.required || !Array.isArray(schema.required) || schema.required.length === 0) {
94
+ console.error('setSchema: Schema must have required fields in strict mode');
95
+ return false;
96
+ }
97
+ } catch (error) {
98
+ console.error('setSchema: Schema validation error:', error);
99
+ return false;
100
+ }
101
+ }
102
+
103
+ return new Promise((resolve) => {
104
+ try {
105
+ const schemaString = JSON.stringify(schema);
106
+ this.gun.get(this.appname)
107
+ .get(lens)
108
+ .get('schema')
109
+ .put(schemaString, ack => {
110
+ if (ack.err) {
111
+ console.error('Failed to add schema:', ack.err);
112
+ resolve(false);
113
+ } else {
114
+ console.log('Schema added successfully for lens:', lens);
115
+ resolve(true);
116
+ }
117
+ });
118
+ } catch (error) {
119
+ console.error('setSchema: Error stringifying schema:', error);
120
+ resolve(false);
121
+ }
122
+ });
50
123
  }
51
124
 
52
125
  /**
@@ -55,114 +128,106 @@ class HoloSphere {
55
128
  * @returns {Promise<object|null>} - The retrieved schema or null if not found.
56
129
  */
57
130
  async getSchema(lens) {
131
+ if (!lens) {
132
+ console.error('getSchema: Missing lens parameter');
133
+ return null;
134
+ }
135
+
58
136
  return new Promise((resolve) => {
59
- this.gun.get(lens).get('schema').once(data => {
60
- if (data) {
61
- let parsed;
62
- try {
63
- parsed = JSON.parse(data);
137
+ this.gun.get(this.appname)
138
+ .get(lens)
139
+ .get('schema')
140
+ .once(data => {
141
+ if (!data) {
142
+ resolve(null);
143
+ return;
64
144
  }
65
- catch (e) {
66
- resolve(null)
145
+
146
+ try {
147
+ // If data is already a string, parse it
148
+ if (typeof data === 'string') {
149
+ resolve(JSON.parse(data));
150
+ }
151
+ // If data is an object with a string value (GunDB format)
152
+ else if (typeof data === 'object' && data !== null) {
153
+ const schemaStr = Object.values(data).find(v =>
154
+ typeof v === 'string' && v.includes('"type":'));
155
+ if (schemaStr) {
156
+ resolve(JSON.parse(schemaStr));
157
+ } else {
158
+ resolve(null);
159
+ }
160
+ } else {
161
+ resolve(null);
162
+ }
163
+ } catch (error) {
164
+ console.error('getSchema: Error parsing schema:', error);
165
+ resolve(null);
67
166
  }
68
- resolve(parsed);
69
- } else {
70
- resolve(null);
71
- }
72
- })
73
- })
74
- }
75
- /**
76
- * Deletes a specific tag from a given ID.
77
- * @param {string} id - The identifier from which to delete the tag.
78
- * @param {string} tag - The tag to delete.
79
- */
80
- async delete(id, tag) {
81
- await this.gun.get(id).get(tag).put(null)
167
+ });
168
+ });
82
169
  }
83
170
 
171
+ // ================================ CONTENT FUNCTIONS ================================
172
+
84
173
  /**
85
174
  * Stores content in the specified holon and lens.
86
175
  * @param {string} holon - The holon identifier.
87
176
  * @param {string} lens - The lens under which to store the content.
88
- * @param {object} content - The content to store.
177
+ * @param {object} data - The data to store.
178
+ * @returns {Promise<boolean>} - Returns true if successful, false if there was an error
89
179
  */
90
- async put(holon, lens, content) {
91
- if (!holon || !lens || !content) return;
92
- console.error('Error in put:', holon, lens, content);
93
- // Retrieve the schema for the lens
94
- let schema = await this.getSchema(lens)
95
- if (schema) {
96
- // Validate the content against the schema
97
- const valid = this.validator.validate(schema, content);
98
- if (!valid) {
99
- console.error('Not committing invalid content:', this.validator.errors);
100
- return null;
101
- }
180
+ async put(holon, lens, data) {
181
+ if (!holon || !lens || !data) {
182
+ console.error('put: Missing required parameters:', { holon, lens, data });
183
+ return false;
102
184
  }
103
185
 
104
- // Create a node for the content
105
- const payload = JSON.stringify(content);
106
-
107
- let noderef;
108
-
109
- if (content.id) { //use the user-defined id. Important to be able to send updates using put
110
- noderef = this.gun.get(lens).get(content.id).put(payload)
111
- this.gun.get(holon.toString()).get(lens).get(content.id).put(payload)
112
- } else { // create a content-addressable reference like IPFS. Note: no updates possible using put
113
- const hashBuffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload));
114
- const hashArray = Array.from(new Uint8Array(hashBuffer));
115
- const hashholon = hashArray.map(byte => byte.toString(16).padStart(2, "0")).join("");
116
- noderef = this.gun.get(lens).get(hashholon).put(payload)
117
- this.gun.get(holon.toString()).get(lens).get(hashholon).put(payload)
186
+ if (!data.id) {
187
+ console.error('put: Data must have an id field');
188
+ return false;
118
189
  }
119
190
 
120
- }
121
-
122
- async putNode(holon, lens, node) {
123
- this.gun.get(holon).get(lens).set(node)
124
- }
125
-
126
- async parse(data) {
127
- let parsed = {};
128
-
129
- if (typeof data === 'object' && data !== null) {
130
- if (data._ && data._["#"]) {
131
- // If the data is a reference, fetch the actual content
132
- let query = data._['#'].split('/');
133
- let holon = query[1];
134
- let lens = query[2];
135
- let key = query[3];
136
- parsed = await this.getKey(holon, lens, key);
137
- } else if (data._ && data._['>']) {
138
- // This might be a gun node, try to get its value
139
- const nodeValue = Object.values(data).find(v => typeof v !== 'object' && v !== '_');
140
- if (nodeValue) {
141
- try {
142
- parsed = JSON.parse(nodeValue);
143
- } catch (e) {
144
- console.log('Invalid JSON in node value:', nodeValue);
145
- parsed = nodeValue; // return the raw data
146
- }
147
- } else {
148
- console.log('Unable to parse gun node:', data);
149
- parsed = data; // return the original data
150
- }
151
- } else {
152
- // Treat it as regular data
153
- parsed = data;
154
- }
155
- } else {
156
- // If it's not an object, try parsing it as JSON
191
+ // Strict validation of schema and data
192
+ const schema = await this.getSchema(lens);
193
+ if (schema) {
157
194
  try {
158
- parsed = JSON.parse(data);
159
- } catch (e) {
160
- console.log('Invalid JSON:', data);
161
- parsed = data; // return the raw data
195
+ const valid = this.validator.validate(schema, data);
196
+ if (!valid) {
197
+ const errors = this.validator.errors;
198
+ console.error('put: Schema validation failed:', errors);
199
+ return false;
200
+ }
201
+ } catch (error) {
202
+ console.error('put: Schema validation error:', error);
203
+ return false;
162
204
  }
205
+ } else if (this.strict) {
206
+ console.error('put: Schema required in strict mode for lens:', lens);
207
+ return false;
163
208
  }
164
209
 
165
- return parsed;
210
+ return new Promise((resolve) => {
211
+ try {
212
+ const payload = JSON.stringify(data);
213
+
214
+ this.gun.get(this.appname)
215
+ .get(holon)
216
+ .get(lens)
217
+ .get(data.id)
218
+ .put(payload, ack => {
219
+ if (ack.err) {
220
+ console.error("Error adding data to GunDB:", ack.err);
221
+ resolve(false);
222
+ } else {
223
+ resolve(true);
224
+ }
225
+ });
226
+ } catch (error) {
227
+ console.error('Error in put operation:', error);
228
+ resolve(false);
229
+ }
230
+ });
166
231
  }
167
232
 
168
233
  /**
@@ -171,160 +236,324 @@ class HoloSphere {
171
236
  * @param {string} lens - The lens from which to retrieve content.
172
237
  * @returns {Promise<Array<object>>} - The retrieved content.
173
238
  */
174
- async get(holon, lens) {
239
+ async getAll(holon, lens) {
175
240
  if (!holon || !lens) {
176
- console.log('Wrong get:', holon, lens)
177
- return;
241
+ console.error('getAll: Missing required parameters:', { holon, lens });
242
+ return [];
178
243
  }
179
- // Wrap the gun operation in a promise
180
- //retrieve lens schema
181
- const schema = await this.getSchema(lens);
182
244
 
183
- if (!schema) {
184
- console.log('The schema for "' + lens + '" is not defined');
185
- // return null; // No schema found, return null if strict about it
245
+ const schema = await this.getSchema(lens);
246
+ if (!schema && this.strict) {
247
+ console.error('getAll: Schema required in strict mode for lens:', lens);
248
+ return [];
186
249
  }
187
250
 
188
- return new Promise(async (resolve, reject) => {
189
- let output = []
190
- let counter = 0
191
- this.gun.get(holon.toString()).get(lens).once((data, key) => {
192
- if (data) {
193
- const maplenght = Object.keys(data).length - 1
194
- console.log('Map length:', maplenght)
195
- this.gun.get(holon.toString()).get(lens).map().once(async (itemdata, key) => {
196
- counter += 1
197
- if (itemdata) {
198
- let parsed = await this.parse (itemdata)
199
-
251
+ return new Promise((resolve) => {
252
+ let output = [];
253
+ let counter = 0;
254
+
255
+ this.gun.get(this.appname).get(holon).get(lens).once((data, key) => {
256
+ if (!data) {
257
+ resolve(output);
258
+ return;
259
+ }
200
260
 
261
+ const mapLength = Object.keys(data).length - 1;
262
+
263
+ this.gun.get(this.appname).get(holon).get(lens).map().once(async (itemdata, key) => {
264
+ counter += 1;
265
+ if (itemdata) {
266
+ try {
267
+ const parsed = JSON.parse(itemdata);
268
+
201
269
  if (schema) {
202
- let valid = this.validator.validate(schema, parsed);
203
- if (!valid || parsed == null || parsed == undefined) {
204
- console.log('Removing Invalid content:', this.validator.errors);
205
- this.gun.get(holon).get(lens).get(key).put(null);
206
-
270
+ const valid = this.validator.validate(schema, parsed);
271
+ if (valid) {
272
+ output.push(parsed);
273
+ } else if (this.strict) {
274
+ console.warn('Invalid data removed:', key, this.validator.errors);
275
+ await this.delete(holon, lens, key);
207
276
  } else {
277
+ console.warn('Invalid data found:', key, this.validator.errors);
208
278
  output.push(parsed);
209
279
  }
210
- }
211
- else {
280
+ } else {
212
281
  output.push(parsed);
213
282
  }
283
+ } catch (error) {
284
+ console.error('Error parsing data:', error);
285
+ if (this.strict) {
286
+ await this.delete(holon, lens, key);
287
+ }
214
288
  }
289
+ }
215
290
 
216
- if (counter == maplenght) {
217
- resolve(output);
218
- }
291
+ if (counter === mapLength) {
292
+ resolve(output);
219
293
  }
220
- );
221
- } else resolve(output)
222
- })
223
- }
224
- );
294
+ });
295
+ });
296
+ });
225
297
  }
226
298
 
227
- /**
299
+ /**
228
300
  * Retrieves a specific key from the specified holon and lens.
229
301
  * @param {string} holon - The holon identifier.
230
302
  * @param {string} lens - The lens from which to retrieve the key.
231
303
  * @param {string} key - The specific key to retrieve.
232
304
  * @returns {Promise<object|null>} - The retrieved content or null if not found.
233
305
  */
234
- async getKey(holon, lens, key) {
306
+ async get(holon, lens, key) {
307
+ if (!holon || !lens || !key) {
308
+ console.error('get: Missing required parameters:', { holon, lens, key });
309
+ return null;
310
+ }
311
+
312
+ // Get schema for validation
313
+ const schema = await this.getSchema(lens);
314
+
235
315
  return new Promise((resolve) => {
236
- // Use Gun to get the data
237
- this.gun.get(holon).get(lens).get(key).once((data, key) => {
238
- if (data) {
239
- console.log('Data getting parsed:', data)
316
+ let timeout = setTimeout(() => {
317
+ console.warn('get: Operation timed out');
318
+ resolve(null);
319
+ }, 5000); // 5 second timeout
320
+
321
+ this.gun.get(this.appname)
322
+ .get(holon)
323
+ .get(lens)
324
+ .get(key)
325
+ .once((data) => {
326
+ clearTimeout(timeout);
327
+
328
+ if (!data) {
329
+ resolve(null);
330
+ return;
331
+ }
332
+
240
333
  try {
241
- let parsed = JSON.parse(data); // Resolve the promise with the data if data is found
334
+ const parsed = JSON.parse(data);
335
+
336
+ // Validate against schema if one exists
337
+ if (schema) {
338
+ const valid = this.validator.validate(schema, parsed);
339
+ if (!valid) {
340
+ console.error('get: Invalid data according to schema:', this.validator.errors);
341
+ if (this.strict) {
342
+ resolve(null);
343
+ return;
344
+ }
345
+ }
346
+ }
347
+
242
348
  resolve(parsed);
349
+ } catch (error) {
350
+ console.error('Error parsing data:', error);
351
+ resolve(null);
243
352
  }
244
- catch (e) {
245
- resolve(data)
353
+ });
354
+ });
355
+ }
356
+
357
+ /**
358
+ * Deletes a specific key from a given holon and lens.
359
+ * @param {string} holon - The holon identifier.
360
+ * @param {string} lens - The lens from which to delete the key.
361
+ * @param {string} key - The specific key to delete.
362
+ */
363
+ async delete (holon, lens, key) {
364
+ return new Promise((resolve, reject) => {
365
+ this.gun.get(this.appname).get(holon).get(lens).get(key).put(null, ack => {
366
+ if (ack.err) {
367
+ resolve(ack.err);
368
+ } else {
369
+ resolve(ack.ok);
246
370
  }
247
-
248
- } else {
249
- resolve(null); // Reject the promise if no data is found
371
+ });
372
+ });
373
+ }
374
+
375
+ /**
376
+ * Deletes all keys from a given holon and lens.
377
+ * @param {string} holon - The holon identifier.
378
+ * @param {string} lens - The lens from which to delete all keys.
379
+ * @returns {Promise<boolean>} - Returns true if successful, false if there was an error
380
+ */
381
+ async deleteAll(holon, lens) {
382
+ if (!holon || !lens) {
383
+ console.error('deleteAll: Missing holon or lens parameter');
384
+ return false;
385
+ }
386
+
387
+ return new Promise((resolve) => {
388
+ let deletionPromises = [];
389
+
390
+ // First get all the data to find keys to delete
391
+ this.gun.get(this.appname).get(holon).get(lens).once((data) => {
392
+ if (!data) {
393
+ resolve(true); // Nothing to delete
394
+ return;
250
395
  }
396
+
397
+ // Get all keys except Gun's metadata key '_'
398
+ const keys = Object.keys(data).filter(key => key !== '_');
399
+
400
+ // Create deletion promises for each key
401
+ keys.forEach(key => {
402
+ deletionPromises.push(
403
+ new Promise((resolveDelete) => {
404
+ this.gun.get(this.appname).get(holon).get(lens).get(key).put(null, ack => {
405
+ resolveDelete(!!ack.ok); // Convert to boolean
406
+ });
407
+ })
408
+ );
409
+ });
410
+
411
+ // Wait for all deletions to complete
412
+ Promise.all(deletionPromises)
413
+ .then(results => {
414
+ const allSuccessful = results.every(result => result === true);
415
+ resolve(allSuccessful);
416
+ })
417
+ .catch(error => {
418
+ console.error('Error in deleteAll:', error);
419
+ resolve(false);
420
+ });
251
421
  });
252
422
  });
253
-
254
423
  }
255
424
 
425
+ // ================================ NODE FUNCTIONS ================================
426
+
427
+
428
+ /**
429
+ * Stores a specific gun node in a given holon and lens.
430
+ * @param {string} holon - The holon identifier.
431
+ * @param {string} lens - The lens under which to store the node.
432
+ * @param {object} node - The node to store.
433
+ */
434
+ async putNode(holon, lens, node) {
435
+ this.gun.get(this.appname).get(holon).get(lens).put(node)
436
+ }
437
+
256
438
  /**
257
439
  * Retrieves a specific gun node from the specified holon and lens.
258
440
  * @param {string} holon - The holon identifier.
259
- * @param {string} lens - The lens from which to retrieve the key.
441
+ * @param {string} lens - The lens identifier.
260
442
  * @param {string} key - The specific key to retrieve.
261
- * @returns {Promise<object|null>} - The retrieved content or null if not found.
443
+ * @returns {Promise<object|null>} - The retrieved node or null if not found.
262
444
  */
263
- getNode(holon, lens, key) {
264
- // Use Gun to get the data
265
- return this.gun.get(holon).get(lens).get(key)
266
- }
445
+ async getNode(holon, lens, key) {
446
+ if (!holon || !lens || !key) {
447
+ console.error('getNode: Missing required parameters');
448
+ return null;
449
+ }
267
450
 
268
- //GLOBAL FUNCTIONS
269
- async deleteNode(nodeId, tag) {
270
- await this.gun.get(nodeId).get(tag).put(null)
451
+ return new Promise((resolve) => {
452
+ let timeout = setTimeout(() => {
453
+ console.warn('getNode: Operation timed out');
454
+ resolve(null);
455
+ }, 5000);
456
+
457
+ this.gun.get(this.appname)
458
+ .get(holon)
459
+ .get(lens)
460
+ .get(key)
461
+ .once((data) => {
462
+ clearTimeout(timeout);
463
+ resolve(data || null);
464
+ });
465
+ });
271
466
  }
272
- // ================================ GLOBAL FUNCTIONS ================================
467
+
273
468
  /**
274
- * Stores data in a global (non-holon-specific) table.
275
- * @param {string} table - The table name to store data in.
276
- * @param {object} data - The data to store. If it has an 'id' field, it will be used as the key.
277
- * @returns {Promise<void>}
469
+ * Deletes a specific gun node from a given holon and lens.
470
+ * @param {string} holon - The holon identifier.
471
+ * @param {string} lens - The lens identifier.
472
+ * @param {string} key - The key of the node to delete.
473
+ * @returns {Promise<boolean>} - Returns true if successful
278
474
  */
279
- async putGlobal(tableName, data) {
280
- return new Promise((resolve, reject) => {
281
- if (!tableName || !data) {
282
- reject(new Error('Table name and data are required'));
283
- return;
284
- }
475
+ async deleteNode(holon, lens, key) {
476
+ if (!holon || !lens || !key) {
477
+ console.error('deleteNode: Missing required parameters');
478
+ return false;
479
+ }
285
480
 
286
- if (data.id) {
287
- this.gun.get(tableName).get(data.id).put(JSON.stringify(data), ack => {
288
- if (ack.err) {
289
- reject(new Error(ack.err));
290
- } else {
291
- resolve();
292
- }
293
- });
294
- } else {
295
- this.gun.get(tableName).put(JSON.stringify(data), ack => {
481
+ return new Promise((resolve) => {
482
+ this.gun.get(this.appname)
483
+ .get(holon)
484
+ .get(lens)
485
+ .get(key)
486
+ .put(null, ack => {
296
487
  if (ack.err) {
297
- reject(new Error(ack.err));
488
+ console.error('deleteNode: Error deleting node:', ack.err);
489
+ resolve(false);
298
490
  } else {
299
- resolve();
491
+ resolve(true);
300
492
  }
301
493
  });
302
- }
303
494
  });
304
495
  }
305
496
 
306
- /**
307
- * Retrieves a specific key from a global table.
308
- * @param {string} tableName - The table name to retrieve from.
309
- * @param {string} key - The key to retrieve.
310
- * @returns {Promise<object|null>} - The parsed data for the key or null if not found.
497
+ // ================================ GLOBAL FUNCTIONS ================================
498
+ /**
499
+ * Stores data in a global (non-holon-specific) table.
500
+ * @param {string} tableName - The table name to store data in.
501
+ * @param {object} data - The data to store. If it has an 'id' field, it will be used as the key.
502
+ * @returns {Promise<void>}
311
503
  */
312
- async getGlobal(tableName, key) {
313
- return new Promise((resolve) => {
314
- this.gun.get(tableName).get(key).once((data) => {
315
- if (!data) {
316
- resolve(null);
504
+ async putGlobal(tableName, data) {
505
+
506
+ return new Promise((resolve, reject) => {
507
+ if (!tableName || !data) {
508
+ reject(new Error('Table name and data are required'));
317
509
  return;
318
510
  }
319
- try {
320
- const parsed = JSON.parse(data);
321
- resolve(parsed);
322
- } catch (e) {
323
- resolve(null);
511
+
512
+
513
+ if (data.id) {
514
+ this.gun.get(this.appname).get(tableName).get(data.id).put(JSON.stringify(data), ack => {
515
+ if (ack.err) {
516
+ reject(new Error(ack.err));
517
+ } else {
518
+ resolve();
519
+ }
520
+ });
521
+ } else {
522
+ this.gun.get(this.appname).get(tableName).put(JSON.stringify(data), ack => {
523
+ if (ack.err) {
524
+ reject(new Error(ack.err));
525
+ } else {
526
+ resolve();
527
+ }
528
+ });
324
529
  }
325
530
  });
326
- });
327
- }
531
+ }
532
+
533
+ /**
534
+ * Retrieves a specific key from a global table.
535
+ * @param {string} tableName - The table name to retrieve from.
536
+ * @param {string} key - The key to retrieve.
537
+ * @returns {Promise<object|null>} - The parsed data for the key or null if not found.
538
+ */
539
+ async getGlobal(tableName, key) {
540
+ return new Promise((resolve) => {
541
+ this.gun.get(this.appname).get(tableName).get(key).once((data) => {
542
+ if (!data) {
543
+ resolve(null);
544
+ return;
545
+ }
546
+ try {
547
+ const parsed = this.parse(data);
548
+ resolve(parsed);
549
+ } catch (e) {
550
+ resolve(null);
551
+ }
552
+ });
553
+ });
554
+ }
555
+
556
+
328
557
 
329
558
  /**
330
559
  * Retrieves all data from a global table.
@@ -332,38 +561,56 @@ class HoloSphere {
332
561
  * @returns {Promise<object|null>} - The parsed data from the table or null if not found.
333
562
  */
334
563
  async getAllGlobal(tableName) {
335
- return new Promise((resolve) => {
336
- this.gun.get(tableName).once((data) => {
337
- if (!data) {
338
- resolve(null);
339
- return;
340
- }
341
- try {
342
- const parsed = JSON.parse(data);
343
- resolve(parsed);
344
- } catch (e) {
345
- resolve(null);
346
- }
347
- });
348
- });
349
- }
564
+ return new Promise(async (resolve, reject) => {
565
+ let output = []
566
+ let counter = 0
567
+ this.gun.get(tableName.toString()).once((data, key) => {
568
+ if (data) {
569
+ const maplenght = Object.keys(data).length - 1
570
+ this.gun.get(tableName.toString()).map().once(async (itemdata, key) => {
571
+ counter += 1
572
+ if (itemdata) {
573
+ let parsed = await this.parse(itemdata)
574
+ output.push(parsed);
575
+ }
350
576
 
351
-
577
+ if (counter == maplenght) {
578
+ resolve(output);
579
+ }
580
+ }
581
+ );
582
+ } else resolve(output)
583
+ })
584
+ }
585
+ )
586
+ }
587
+
588
+ /**
589
+ * Deletes a specific key from a global table.
590
+ * @param {string} tableName - The table name to delete from.
591
+ * @param {string} key - The key to delete.
592
+ * @returns {Promise<void>}
593
+ */
594
+ async deleteGlobal(tableName, key) {
595
+ await this.gun.get(this.appname).get(tableName).get(key).put(null)
596
+ }
352
597
 
353
598
  /**
354
599
  * Deletes an entire global table.
355
- * @param {string} table - The table name to delete.
600
+ * @param {string} tableName - The table name to delete.
356
601
  * @returns {Promise<void>}
357
602
  */
358
603
  async deleteAllGlobal(tableName) {
359
-
360
- return new Promise((resolve) => {
361
- this.gun.get(tableName).map().put(null)
362
- this.gun.get(tableName).put(null, ack => {
363
- resolve();
604
+
605
+ return new Promise((resolve) => {
606
+ this.gun.get(this.appname).get(tableName).map().put(null).once(
607
+ (data, key) => this.gun.get(this.appname).get(tableName).get(key).put(null)
608
+ )
609
+ this.gun.get(this.appname).get(tableName).put({}, ack => {
610
+ resolve();
611
+ });
364
612
  });
365
- });
366
- }
613
+ }
367
614
 
368
615
  // ================================ COMPUTE FUNCTIONS ================================
369
616
  /**
@@ -374,8 +621,8 @@ class HoloSphere {
374
621
  */
375
622
  async compute(holon, lens, operation) {
376
623
 
377
- let res = h3.getResolution(holon);
378
- if (res < 1 || res > 15) return;
624
+ let res = h3.getResolution(holon);
625
+ if(res < 1 || res > 15) return;
379
626
  console.log(res)
380
627
  let parent = h3.cellToParent(holon, res - 1);
381
628
  let siblings = h3.cellToChildren(parent, res);
@@ -391,7 +638,7 @@ class HoloSphere {
391
638
  resolve(); // Resolve the promise to prevent it from hanging
392
639
  }, 1000); // Timeout of 5 seconds
393
640
 
394
- this.gun.get(siblings[i]).get(lens).map().once((data, key) => {
641
+ this.gun.get(this.appname).get(siblings[i]).get(lens).map().once((data, key) => {
395
642
  clearTimeout(timeout); // Clear the timeout if data is received
396
643
  if (data) {
397
644
  content.push(data.content);
@@ -405,7 +652,7 @@ class HoloSphere {
405
652
  console.log('Content:', content);
406
653
  let computed = await this.summarize(content.join('\n'))
407
654
  console.log('Computed:', computed)
408
- let node = await this.gun.get(parent + '_summary').put({ id: parent + '_summary', content: computed })
655
+ let node = await this.gun.get(this.appname).get(parent + '_summary').put({ id: parent + '_summary', content: computed })
409
656
 
410
657
  this.put(parent, lens, node);
411
658
  this.compute(parent, lens, operation)
@@ -420,10 +667,10 @@ class HoloSphere {
420
667
  let entities = {};
421
668
 
422
669
  // Get list out of Gun
423
- this.gun.get(holon).get(lens).map().once((data, key) => {
670
+ this.gun.get(this.appname).get(holon).get(lens).map().once((data, key) => {
424
671
  //entities = data;
425
672
  //const id = Object.keys(entities)[0] // since this would be in object form, you can manipulate it as you would like.
426
- this.gun.get(holon).get(lens).put({ [key]: null })
673
+ this.gun.get(this.appname).get(holon).get(lens).put({ [key]: null })
427
674
  })
428
675
  }
429
676
 
@@ -556,91 +803,10 @@ class HoloSphere {
556
803
  * @param {function} callback - The callback to execute on changes.
557
804
  */
558
805
  subscribe(holon, lens, callback) {
559
- this.gun.get(holon).get(lens).map().on((data, key) => {
806
+ this.gun.get(this.appname).get(holon).get(lens).map().on((data, key) => {
560
807
  callback(data, key)
561
808
  })
562
809
  }
563
-
564
- // ================================ GOVERNANCE FUNCTIONS ================================
565
- /**
566
- * Retrieves the final vote for a user, considering delegations.
567
- * @param {string} userId - The user's identifier.
568
- * @param {string} topic - The voting topic.
569
- * @param {object} votes - The current votes.
570
- * @param {Set<string>} [visited=new Set()] - Set of visited users to prevent cycles.
571
- * @returns {string|null} - The final vote or null if not found.
572
- */
573
- getFinalVote(userId, topic, votes, visited = new Set()) {
574
- if (this.users[userId]) { // Added this.users
575
- if (visited.has(userId)) {
576
- return null; // Avoid circular delegations
577
- }
578
- visited.add(userId);
579
-
580
- const delegation = this.users[userId].delegations[topic];
581
- if (delegation && votes[delegation] === undefined) {
582
- return this.getFinalVote(delegation, topic, votes, visited); // Prefixed with this
583
- }
584
-
585
- return votes[userId] !== undefined ? votes[userId] : null;
586
- }
587
- return null;
588
- }
589
-
590
- /**
591
- * Aggregates votes for a specific holon and topic.
592
- * @param {string} holonId - The holon identifier.
593
- * @param {string} topic - The voting topic.
594
- * @returns {object} - Aggregated vote counts.
595
- */
596
- aggregateVotes(holonId, topic) {
597
- if (!this.holonagonVotes[holonId] || !this.holonagonVotes[holonId][topic]) {
598
- return {}; // Handle undefined votes
599
- }
600
- const votes = this.holonagonVotes[holonId][topic];
601
- const aggregatedVotes = {};
602
-
603
- Object.keys(votes).forEach(userId => {
604
- const finalVote = this.getFinalVote(userId, topic, votes); // Prefixed with this
605
- if (finalVote !== null) {
606
- aggregatedVotes[finalVote] = (aggregatedVotes[finalVote] || 0) + 1;
607
- }
608
- });
609
-
610
- return aggregatedVotes;
611
- }
612
-
613
- /**
614
- * Delegates a user's vote to another user.
615
- * @param {string} userId - The user's identifier.
616
- * @param {string} topic - The voting topic.
617
- * @param {string} delegateTo - The user to delegate the vote to.
618
- */
619
- async delegateVote(userId, topic, delegateTo) {
620
- const response = await fetch('/delegate', {
621
- method: 'POST',
622
- headers: { 'Content-Type': 'application/json' },
623
- body: JSON.stringify({ userId, topic, delegateTo })
624
- });
625
- alert(await response.text());
626
- }
627
-
628
- /**
629
- * Casts a vote for a user on a specific topic and holon.
630
- * @param {string} userId - The user's identifier.
631
- * @param {string} holonId - The holon identifier.
632
- * @param {string} topic - The voting topic.
633
- * @param {string} vote - The vote choice.
634
- */
635
- async vote(userId, holonId, topic, vote) {
636
- const response = await fetch('/vote', {
637
- method: 'POST',
638
- headers: { 'Content-Type': 'application/json' },
639
- body: JSON.stringify({ userId, holonId, topic, vote })
640
- });
641
- alert(await response.text());
642
- }
643
-
644
810
  }
645
811
 
646
812
  export default HoloSphere;