holosphere 1.0.4 → 1.0.5
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.d.ts +22 -0
- package/{index.js → holosphere.js} +207 -182
- package/package.json +2 -2
package/holosphere.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
declare module 'holosphere' {
|
|
2
|
+
export default class HoloSphere {
|
|
3
|
+
constructor(name: string, openaikey?: string | null);
|
|
4
|
+
subscribe(hex: string, lense: string, callback: (item: any, key: string) => void): void;
|
|
5
|
+
put(hex: string, lense: string, content: any): Promise<void>;
|
|
6
|
+
delete(id: string, tag: string): Promise<void>;
|
|
7
|
+
get(hex: string, lense: string): Promise<any[]>;
|
|
8
|
+
getKey(hex: string, lense: string, key: string): Promise<any | null>;
|
|
9
|
+
getNode(hex: string, lense: string, key: string): Promise<any | null>;
|
|
10
|
+
compute(hex: string, lense: string, operation: string): Promise<void>;
|
|
11
|
+
clearlense(hex: string, lense: string): Promise<void>;
|
|
12
|
+
summarize(history: string): Promise<string>;
|
|
13
|
+
upcast(hex: string, lense: string, content: any): Promise<any>;
|
|
14
|
+
updateParent(id: string, report: string): Promise<any>;
|
|
15
|
+
getHex(lat: number, lng: number, resolution: number): Promise<string>;
|
|
16
|
+
getScalespace(lat: number, lng: number): string[];
|
|
17
|
+
getHexScalespace(hex: string): string[];
|
|
18
|
+
aggregateVotes(hexId: string, topic: string): object;
|
|
19
|
+
delegateVote(userId: string, topic: string, delegateTo: string): Promise<void>;
|
|
20
|
+
vote(userId: string, hexId: string, topic: string, vote: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -4,10 +4,15 @@ import Gun from 'gun'
|
|
|
4
4
|
import Ajv2019 from 'ajv/dist/2019.js'
|
|
5
5
|
|
|
6
6
|
class HoloSphere {
|
|
7
|
+
/**
|
|
8
|
+
* Initializes a new instance of the HoloSphere class.
|
|
9
|
+
* @param {string} appname - The name of the application.
|
|
10
|
+
* @param {string|null} openaikey - The OpenAI API key.
|
|
11
|
+
*/
|
|
7
12
|
constructor(appname, openaikey = null) {
|
|
8
13
|
this.validator = new Ajv2019({ allErrors: false, strict: false });
|
|
9
14
|
this.gun = Gun({
|
|
10
|
-
peers: ['
|
|
15
|
+
peers: ['https://59.src.eco/gun'],
|
|
11
16
|
axe: false,
|
|
12
17
|
// uuid: (content) => { // generate a unique id for each node
|
|
13
18
|
// console.log('uuid', content);
|
|
@@ -15,119 +20,22 @@ class HoloSphere {
|
|
|
15
20
|
});
|
|
16
21
|
|
|
17
22
|
this.gun = this.gun.get(appname)
|
|
23
|
+
this.users = {}; // Initialize users
|
|
24
|
+
this.hexagonVotes = {}; // Initialize hexagonVotes
|
|
18
25
|
|
|
19
26
|
if (openaikey != null) {
|
|
20
27
|
this.openai = new OpenAI({
|
|
21
28
|
apiKey: openaikey,
|
|
22
29
|
});
|
|
23
30
|
}
|
|
24
|
-
|
|
25
|
-
//this.bot.command('sethex', async (ctx) => { this.setHex(ctx) }) TODO: MOVE HERE FROM SETTINGS
|
|
26
|
-
|
|
27
|
-
// this.bot.command('resethex', async (ctx) => {
|
|
28
|
-
// let chatID = ctx.message.chat.id;
|
|
29
|
-
// let hex = (await this.db.get('settings', chatID)).hex
|
|
30
|
-
// this.delete(hex, ctx.message.text.split(' ')[1])
|
|
31
|
-
// })
|
|
32
|
-
|
|
33
|
-
// this.bot.command('get', async (ctx) => {
|
|
34
|
-
// const chatID = ctx.message.chat.id;
|
|
35
|
-
// const lense = ctx.message.text.split(' ')[1];
|
|
36
|
-
// if (!lense) {
|
|
37
|
-
// return ctx.reply('Please specify a tag.');
|
|
38
|
-
// }
|
|
39
|
-
// let hex = (await this.db.get('settings', chatID)).hex
|
|
40
|
-
// //let hex = settings.hex
|
|
41
|
-
// console.log('hex', hex)
|
|
42
|
-
|
|
43
|
-
// let data = await this.get(ctx, hex, lense)
|
|
44
|
-
|
|
45
|
-
// })
|
|
46
|
-
|
|
47
|
-
// this.bot.command('gethex', async (ctx) => {
|
|
48
|
-
// let settings = await this.db.get('settings', chatID)
|
|
49
|
-
// let id = settings.hex ? settings.hex : 'Hex not set, use /sethex'
|
|
50
|
-
// ctx.reply(id)
|
|
51
|
-
// })
|
|
52
|
-
|
|
53
|
-
// this.bot.command('compute', async (ctx) => {
|
|
54
|
-
// const chatID = ctx.message.chat.id;
|
|
55
|
-
// let operation = ctx.message.text.split(' ')[1];
|
|
56
|
-
// if (operation != 'sum') {
|
|
57
|
-
// ctx.reply('Operation not implemented')
|
|
58
|
-
// return
|
|
59
|
-
// }
|
|
60
|
-
// let lense = ctx.message.text.split(' ')[2]
|
|
61
|
-
// if (!lense) {
|
|
62
|
-
// ctx.reply('Please specify a lense where to perform the operation ')
|
|
63
|
-
// return
|
|
64
|
-
// }
|
|
65
|
-
// let hex = (await this.db.get('settings', chatID)).hex
|
|
66
|
-
// await this.compute(hex, lense, operation)
|
|
67
|
-
|
|
68
|
-
// // let parentInfo = await this.getCellInfo(parent)
|
|
69
|
-
// // parentInfo.wisdom[id] = report
|
|
70
|
-
// // //update summary
|
|
71
|
-
// // let summary = await this.summarize(Object.values(parentInfo.wisdom).join('\n'))
|
|
72
|
-
// // parentInfo.summary = summary
|
|
73
|
-
|
|
74
|
-
// // await this.db.put('cell', parentInfo)
|
|
75
|
-
// // return parentInfo
|
|
76
|
-
|
|
77
|
-
// // let content = await this.db.getAll(hex+'/tags')
|
|
78
|
-
// })
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// this.bot.command('cast', async (ctx) => {
|
|
84
|
-
// if (!ctx.message.reply_to_message) {
|
|
85
|
-
// return ctx.reply('Please reply to a message you want to tag.');
|
|
86
|
-
// }
|
|
87
|
-
// const tags = ctx.message.text.split(' ').slice(1);
|
|
88
|
-
// if (tags.length === 0) {
|
|
89
|
-
// return ctx.reply('Please provide at least one tag.');
|
|
90
|
-
// }
|
|
91
|
-
|
|
92
|
-
// const messageID = ctx.message.reply_to_message.message_id;
|
|
93
|
-
// const chatID = ctx.message.chat.id;
|
|
94
|
-
// const messageContent = ctx.message.reply_to_message.text;
|
|
95
|
-
// let settings = await this.db.get('settings', chatID)
|
|
96
|
-
// let id = settings.hex ? settings.hex : 'Hex not set, use /sethex'
|
|
97
|
-
// //create root node for the item
|
|
98
|
-
// let node = await this.gun.get(chatID + '/' + messageID).put({ id: chatID + '/' + messageID, content: messageContent })
|
|
99
|
-
// for (let tag of tags) {
|
|
100
|
-
// await this.gun.get(id).get(tag).set(node)
|
|
101
|
-
// this.upcast(id, tag, node)
|
|
102
|
-
// }
|
|
103
|
-
// })
|
|
104
|
-
|
|
105
|
-
// this.bot.command('publish', async (ctx) => {
|
|
106
|
-
// console.log(ctx.message)
|
|
107
|
-
// if (!ctx.message.reply_to_message) {
|
|
108
|
-
// return ctx.reply('Please reply to a message you want to tag.');
|
|
109
|
-
// }
|
|
110
|
-
// const tags = ctx.message.text.split(' ').slice(1);
|
|
111
|
-
// if (tags.length === 0) {
|
|
112
|
-
// return ctx.reply('Please provide at least one tag.');
|
|
113
|
-
// }
|
|
114
|
-
|
|
115
|
-
// const messageID = ctx.message.reply_to_message.message_id;
|
|
116
|
-
// const chatID = ctx.message.chat.id;
|
|
117
|
-
// const messageContent = ctx.message.reply_to_message.text;
|
|
118
|
-
// let settings = await this.db.get('settings', chatID)
|
|
119
|
-
// let hex = settings.hex
|
|
120
|
-
|
|
121
|
-
// for (let tag of tags) {
|
|
122
|
-
// let node = await this.gun.get(chatID + '/' + messageID).put({ id: chatID + '/' + messageID, content: messageContent })
|
|
123
|
-
// await this.put(hex, tag, node)
|
|
124
|
-
// }
|
|
125
|
-
|
|
126
|
-
// ctx.reply('Tag published.');
|
|
127
|
-
// });
|
|
128
|
-
|
|
129
31
|
}
|
|
130
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Sets the JSON schema for a specific lense.
|
|
35
|
+
* @param {string} lense - The lense identifier.
|
|
36
|
+
* @param {object} schema - The JSON schema to set.
|
|
37
|
+
* @returns {Promise} - Resolves when the schema is set.
|
|
38
|
+
*/
|
|
131
39
|
async setSchema(lense, schema) {
|
|
132
40
|
return new Promise((resolve, reject) => {
|
|
133
41
|
this.gun.get(lense).get('schema').put(JSON.stringify(schema), ack => {
|
|
@@ -141,6 +49,11 @@ class HoloSphere {
|
|
|
141
49
|
})
|
|
142
50
|
}
|
|
143
51
|
|
|
52
|
+
/**
|
|
53
|
+
* Retrieves the JSON schema for a specific lense.
|
|
54
|
+
* @param {string} lense - The lense identifier.
|
|
55
|
+
* @returns {Promise<object|null>} - The retrieved schema or null if not found.
|
|
56
|
+
*/
|
|
144
57
|
async getSchema(lense) {
|
|
145
58
|
return new Promise((resolve) => {
|
|
146
59
|
this.gun.get(lense).get('schema').once(data => {
|
|
@@ -159,28 +72,21 @@ class HoloSphere {
|
|
|
159
72
|
})
|
|
160
73
|
})
|
|
161
74
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
this.db.gun.get('settings').get(chatID).put(hex)
|
|
168
|
-
return hex
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
async getHexContent(ctx) {
|
|
172
|
-
const chatID = ctx.message.chat.id;
|
|
173
|
-
let settings = await this.getSettings(chatID)
|
|
174
|
-
let hex = settings.hex
|
|
175
|
-
let content = await this.db.getAll(hex + '/tags')
|
|
176
|
-
//console.log(content)
|
|
177
|
-
return content ? content[0].id : 'not found'
|
|
178
|
-
}
|
|
179
|
-
|
|
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
|
+
*/
|
|
180
80
|
async delete(id, tag) {
|
|
181
81
|
await this.gun.get(id).get(tag).put(null)
|
|
182
82
|
}
|
|
183
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Stores content in the specified hex and lense.
|
|
86
|
+
* @param {string} hex - The hex identifier.
|
|
87
|
+
* @param {string} lense - The lense under which to store the content.
|
|
88
|
+
* @param {object} content - The content to store.
|
|
89
|
+
*/
|
|
184
90
|
async put(hex, lense, content) {
|
|
185
91
|
// Retrieve the schema for the lense
|
|
186
92
|
let schema = await this.getSchema(lense)
|
|
@@ -202,28 +108,25 @@ class HoloSphere {
|
|
|
202
108
|
noderef = this.gun.get(lense).get(content.id).put(payload)
|
|
203
109
|
this.gun.get(hex.toString()).get(lense).get(content.id).put(payload)
|
|
204
110
|
} else { // create a content-addressable reference like IPFS. Note: no updates possible using put
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
noderef = this.gun.get(lense).get(hash).put(payload)
|
|
211
|
-
this.gun.get(hex.toString()).get(lense).get(hash).put(payload)
|
|
111
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload));
|
|
112
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
113
|
+
const hashHex = hashArray.map(byte => byte.toString(16).padStart(2, "0")).join("");
|
|
114
|
+
noderef = this.gun.get(lense).get(hashHex).put(payload)
|
|
115
|
+
this.gun.get(hex.toString()).get(lense).get(hashHex).put(payload)
|
|
212
116
|
}
|
|
213
117
|
|
|
214
|
-
|
|
215
|
-
// this.gun.get(hex.toString()).get(lense).set(noderef, ack => {
|
|
216
|
-
// if (ack.err) {
|
|
217
|
-
// reject(new Error('Failed to add content: ' + ack.err));
|
|
218
|
-
// } else {
|
|
219
|
-
// console.log('Content added successfully under tag:', lense);
|
|
220
|
-
// resolve(ack);
|
|
221
|
-
// }
|
|
222
|
-
// });
|
|
223
|
-
// });
|
|
118
|
+
}
|
|
224
119
|
|
|
120
|
+
async putNode(hex, lense, node) {
|
|
121
|
+
this.gun.get(hex).get(lense).set(node)
|
|
225
122
|
}
|
|
226
123
|
|
|
124
|
+
/**
|
|
125
|
+
* Retrieves content from the specified hex and lense.
|
|
126
|
+
* @param {string} hex - The hex identifier.
|
|
127
|
+
* @param {string} lense - The lense from which to retrieve content.
|
|
128
|
+
* @returns {Promise<Array<object>>} - The retrieved content.
|
|
129
|
+
*/
|
|
227
130
|
async get(hex, lense) {
|
|
228
131
|
// Wrap the GunDB operation in a promise
|
|
229
132
|
//retrieve lense schema
|
|
@@ -244,11 +147,16 @@ class HoloSphere {
|
|
|
244
147
|
this.gun.get(hex.toString()).get(lense).map().once(async (itemdata, key) => {
|
|
245
148
|
counter += 1
|
|
246
149
|
if (itemdata) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
150
|
+
if (itemdata._["#"]) {
|
|
151
|
+
|
|
152
|
+
// If the data is a reference, fetch the actual content
|
|
153
|
+
let query = itemdata._['#'].split('/')
|
|
154
|
+
let hex = query[1]
|
|
155
|
+
let lense = query[2]
|
|
156
|
+
let key = query[3]
|
|
157
|
+
itemdata = await this.getNode(hex, lense, key)
|
|
158
|
+
|
|
159
|
+
}
|
|
252
160
|
var parsed = {}
|
|
253
161
|
try {
|
|
254
162
|
parsed = JSON.parse(itemdata);
|
|
@@ -283,8 +191,47 @@ class HoloSphere {
|
|
|
283
191
|
);
|
|
284
192
|
}
|
|
285
193
|
|
|
286
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Retrieves a specific key from the specified hex and lense.
|
|
196
|
+
* @param {string} hex - The hex identifier.
|
|
197
|
+
* @param {string} lense - The lense from which to retrieve the key.
|
|
198
|
+
* @param {string} key - The specific key to retrieve.
|
|
199
|
+
* @returns {Promise<object|null>} - The retrieved content or null if not found.
|
|
200
|
+
*/
|
|
201
|
+
async getKey(hex, lense, key) {
|
|
202
|
+
return new Promise((resolve) => {
|
|
203
|
+
// Use Gun to get the data
|
|
204
|
+
this.gun.get(hex).get(lense).get(key).once((data, key) => {
|
|
205
|
+
if (data) {
|
|
206
|
+
resolve(JSON.parse(data)); // Resolve the promise with the data if data is found
|
|
207
|
+
} else {
|
|
208
|
+
resolve(null); // Reject the promise if no data is found
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Retrieves a specific gundb node from the specified hex and lense.
|
|
217
|
+
* @param {string} hex - The hex identifier.
|
|
218
|
+
* @param {string} lense - The lense from which to retrieve the key.
|
|
219
|
+
* @param {string} key - The specific key to retrieve.
|
|
220
|
+
* @returns {Promise<object|null>} - The retrieved content or null if not found.
|
|
221
|
+
*/
|
|
222
|
+
getNode(hex, lense, key) {
|
|
223
|
+
// Use Gun to get the data
|
|
224
|
+
return this.gun.get(hex).get(lense).get(key)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
|
|
287
228
|
|
|
229
|
+
/**
|
|
230
|
+
* Computes summaries based on the content within a hex and lense.
|
|
231
|
+
* @param {string} hex - The hex identifier.
|
|
232
|
+
* @param {string} lense - The lense to compute.
|
|
233
|
+
* @param {string} operation - The operation to perform.
|
|
234
|
+
*/
|
|
288
235
|
async compute(hex, lense, operation) {
|
|
289
236
|
|
|
290
237
|
let res = h3.getResolution(hex);
|
|
@@ -304,7 +251,7 @@ class HoloSphere {
|
|
|
304
251
|
resolve(); // Resolve the promise to prevent it from hanging
|
|
305
252
|
}, 1000); // Timeout of 5 seconds
|
|
306
253
|
|
|
307
|
-
this.
|
|
254
|
+
this.gun.get(siblings[i]).get(lense).map().once((data, key) => {
|
|
308
255
|
clearTimeout(timeout); // Clear the timeout if data is received
|
|
309
256
|
if (data) {
|
|
310
257
|
content.push(data.content);
|
|
@@ -324,6 +271,11 @@ class HoloSphere {
|
|
|
324
271
|
this.compute(parent, lense, operation)
|
|
325
272
|
}
|
|
326
273
|
|
|
274
|
+
/**
|
|
275
|
+
* Clears all entities under a specific hex and lense.
|
|
276
|
+
* @param {string} hex - The hex identifier.
|
|
277
|
+
* @param {string} lense - The lense to clear.
|
|
278
|
+
*/
|
|
327
279
|
async clearlense(hex, lense) {
|
|
328
280
|
let entities = {};
|
|
329
281
|
|
|
@@ -336,6 +288,11 @@ class HoloSphere {
|
|
|
336
288
|
}
|
|
337
289
|
|
|
338
290
|
|
|
291
|
+
/**
|
|
292
|
+
* Summarizes provided history text using OpenAI.
|
|
293
|
+
* @param {string} history - The history text to summarize.
|
|
294
|
+
* @returns {Promise<string>} - The summarized text.
|
|
295
|
+
*/
|
|
339
296
|
async summarize(history) {
|
|
340
297
|
if (!this.openai) {
|
|
341
298
|
return 'OpenAI not initialized, please specify the API key in the constructor.'
|
|
@@ -368,21 +325,34 @@ class HoloSphere {
|
|
|
368
325
|
return summary
|
|
369
326
|
}
|
|
370
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Upcasts content to parent hexagons recursively.
|
|
330
|
+
* @param {string} hex - The current hex identifier.
|
|
331
|
+
* @param {string} lense - The lense under which to upcast.
|
|
332
|
+
* @param {object} content - The content to upcast.
|
|
333
|
+
* @returns {Promise<object>} - The upcasted content.
|
|
334
|
+
*/
|
|
371
335
|
async upcast(hex, lense, content) {
|
|
372
336
|
let res = h3.getResolution(hex)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
console.log('Upcasting ', hex, lense, content)
|
|
376
|
-
let parent = h3.cellToParent(hex, res - 1)
|
|
377
|
-
await this.put(parent, lense, content)
|
|
378
|
-
if (res == 0)
|
|
337
|
+
if (res == 0) {
|
|
338
|
+
await this.putNode(hex, lense, content)
|
|
379
339
|
return content
|
|
380
|
-
|
|
340
|
+
}
|
|
341
|
+
else {
|
|
342
|
+
console.log('Upcasting ', hex, lense, content, res)
|
|
343
|
+
await this.putNode(hex, lense, content)
|
|
344
|
+
let parent = h3.cellToParent(hex, res - 1)
|
|
381
345
|
return this.upcast(parent, lense, content)
|
|
346
|
+
}
|
|
382
347
|
}
|
|
383
348
|
|
|
384
349
|
|
|
385
|
-
|
|
350
|
+
/**
|
|
351
|
+
* Updates the parent hexagon with a new report.
|
|
352
|
+
* @param {string} id - The child hex identifier.
|
|
353
|
+
* @param {string} report - The report to update.
|
|
354
|
+
* @returns {Promise<object>} - The updated parent information.
|
|
355
|
+
*/
|
|
386
356
|
async updateParent(id, report) {
|
|
387
357
|
let cellinfo = await this.getCellInfo(id)
|
|
388
358
|
let res = h3.getResolution(id)
|
|
@@ -398,11 +368,23 @@ class HoloSphere {
|
|
|
398
368
|
}
|
|
399
369
|
|
|
400
370
|
|
|
371
|
+
/**
|
|
372
|
+
* Converts latitude and longitude to a hex identifier.
|
|
373
|
+
* @param {number} lat - The latitude.
|
|
374
|
+
* @param {number} lng - The longitude.
|
|
375
|
+
* @param {number} resolution - The resolution level.
|
|
376
|
+
* @returns {Promise<string>} - The resulting hex identifier.
|
|
377
|
+
*/
|
|
401
378
|
async getHex(lat, lng, resolution) {
|
|
402
379
|
return h3.latLngToCell(lat, lng, resolution);
|
|
403
380
|
}
|
|
404
381
|
|
|
405
|
-
|
|
382
|
+
/**
|
|
383
|
+
* Retrieves all containing hexagons at all scales for given coordinates.
|
|
384
|
+
* @param {number} lat - The latitude.
|
|
385
|
+
* @param {number} lng - The longitude.
|
|
386
|
+
* @returns {Array<string>} - List of hex identifiers.
|
|
387
|
+
*/
|
|
406
388
|
getScalespace(lat, lng) {
|
|
407
389
|
let list = []
|
|
408
390
|
let cell = h3.latLngToCell(lat, lng, 14);
|
|
@@ -413,7 +395,11 @@ class HoloSphere {
|
|
|
413
395
|
return list
|
|
414
396
|
}
|
|
415
397
|
|
|
416
|
-
|
|
398
|
+
/**
|
|
399
|
+
* Retrieves all containing hexagons at all scales for a given hex.
|
|
400
|
+
* @param {string} hex - The hex identifier.
|
|
401
|
+
* @returns {Array<string>} - List of hex identifiers.
|
|
402
|
+
*/
|
|
417
403
|
getHexScalespace(hex) {
|
|
418
404
|
let list = []
|
|
419
405
|
let res = h3.getResolution(hex)
|
|
@@ -423,43 +409,73 @@ class HoloSphere {
|
|
|
423
409
|
return list
|
|
424
410
|
}
|
|
425
411
|
|
|
412
|
+
/**
|
|
413
|
+
* Subscribes to changes in a specific hex and lense.
|
|
414
|
+
* @param {string} hex - The hex identifier.
|
|
415
|
+
* @param {string} lense - The lense to subscribe to.
|
|
416
|
+
* @param {function} callback - The callback to execute on changes.
|
|
417
|
+
*/
|
|
426
418
|
subscribe(hex, lense, callback) {
|
|
427
419
|
this.gun.get(hex).get(lense).map().on((data, key) => {
|
|
428
420
|
callback(data, key)
|
|
429
421
|
})
|
|
430
422
|
}
|
|
431
423
|
|
|
432
|
-
|
|
424
|
+
/**
|
|
425
|
+
* Retrieves the final vote for a user, considering delegations.
|
|
426
|
+
* @param {string} userId - The user's identifier.
|
|
427
|
+
* @param {string} topic - The voting topic.
|
|
428
|
+
* @param {object} votes - The current votes.
|
|
429
|
+
* @param {Set<string>} [visited=new Set()] - Set of visited users to prevent cycles.
|
|
430
|
+
* @returns {string|null} - The final vote or null if not found.
|
|
431
|
+
*/
|
|
432
|
+
getFinalVote(userId, topic, votes, visited = new Set()) {
|
|
433
|
+
if (this.users[userId]) { // Added this.users
|
|
434
|
+
if (visited.has(userId)) {
|
|
435
|
+
return null; // Avoid circular delegations
|
|
436
|
+
}
|
|
437
|
+
visited.add(userId);
|
|
433
438
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
visited.add(userId);
|
|
439
|
+
const delegation = this.users[userId].delegations[topic];
|
|
440
|
+
if (delegation && votes[delegation] === undefined) {
|
|
441
|
+
return this.getFinalVote(delegation, topic, votes, visited); // Prefixed with this
|
|
442
|
+
}
|
|
439
443
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
+
return votes[userId] !== undefined ? votes[userId] : null;
|
|
445
|
+
}
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
444
448
|
|
|
445
|
-
|
|
446
|
-
|
|
449
|
+
/**
|
|
450
|
+
* Aggregates votes for a specific hex and topic.
|
|
451
|
+
* @param {string} hexId - The hex identifier.
|
|
452
|
+
* @param {string} topic - The voting topic.
|
|
453
|
+
* @returns {object} - Aggregated vote counts.
|
|
454
|
+
*/
|
|
455
|
+
aggregateVotes(hexId, topic) {
|
|
456
|
+
if (!this.hexagonVotes[hexId] || !this.hexagonVotes[hexId][topic]) {
|
|
457
|
+
return {}; // Handle undefined votes
|
|
458
|
+
}
|
|
459
|
+
const votes = this.hexagonVotes[hexId][topic];
|
|
460
|
+
const aggregatedVotes = {};
|
|
447
461
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
462
|
+
Object.keys(votes).forEach(userId => {
|
|
463
|
+
const finalVote = this.getFinalVote(userId, topic, votes); // Prefixed with this
|
|
464
|
+
if (finalVote !== null) {
|
|
465
|
+
aggregatedVotes[finalVote] = (aggregatedVotes[finalVote] || 0) + 1;
|
|
466
|
+
}
|
|
467
|
+
});
|
|
451
468
|
|
|
452
|
-
|
|
453
|
-
const finalVote = getFinalVote(userId, topic, votes);
|
|
454
|
-
if (finalVote !== null) {
|
|
455
|
-
aggregatedVotes[finalVote] = (aggregatedVotes[finalVote] || 0) + 1;
|
|
469
|
+
return aggregatedVotes;
|
|
456
470
|
}
|
|
457
|
-
});
|
|
458
471
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
472
|
+
/**
|
|
473
|
+
* Delegates a user's vote to another user.
|
|
474
|
+
* @param {string} userId - The user's identifier.
|
|
475
|
+
* @param {string} topic - The voting topic.
|
|
476
|
+
* @param {string} delegateTo - The user to delegate the vote to.
|
|
477
|
+
*/
|
|
478
|
+
async delegateVote(userId, topic, delegateTo) {
|
|
463
479
|
const response = await fetch('/delegate', {
|
|
464
480
|
method: 'POST',
|
|
465
481
|
headers: { 'Content-Type': 'application/json' },
|
|
@@ -468,6 +484,13 @@ async delegateVote(userId, topic, delegateTo) {
|
|
|
468
484
|
alert(await response.text());
|
|
469
485
|
}
|
|
470
486
|
|
|
487
|
+
/**
|
|
488
|
+
* Casts a vote for a user on a specific topic and hex.
|
|
489
|
+
* @param {string} userId - The user's identifier.
|
|
490
|
+
* @param {string} hexId - The hex identifier.
|
|
491
|
+
* @param {string} topic - The voting topic.
|
|
492
|
+
* @param {string} vote - The vote choice.
|
|
493
|
+
*/
|
|
471
494
|
async vote(userId, hexId, topic, vote) {
|
|
472
495
|
const response = await fetch('/vote', {
|
|
473
496
|
method: 'POST',
|
|
@@ -478,6 +501,8 @@ async delegateVote(userId, topic, delegateTo) {
|
|
|
478
501
|
}
|
|
479
502
|
|
|
480
503
|
|
|
504
|
+
|
|
505
|
+
|
|
481
506
|
}
|
|
482
507
|
|
|
483
508
|
export default HoloSphere;
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "holosphere",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Holonic Geospatial Communication Infrastructure based on h3.js and gun.js",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "holosphere.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "echo \"Error: no test specified\" && exit 1"
|