holosphere 1.0.7 → 1.0.8
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/README.md +119 -149
- package/babel.config.json +12 -0
- package/holosphere.d.ts +39 -12
- package/holosphere.js +179 -82
- package/package.json +13 -2
- package/test/holosphere.test.js +184 -17
package/README.md
CHANGED
|
@@ -2,205 +2,175 @@
|
|
|
2
2
|
|
|
3
3
|
HoloSphere is a decentralized geospatial data management system that combines hierarchical hexagonal tiling (H3) with distributed data storage (GunDB) and AI-powered processing of information.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- **AI Processing**: Extensible AI-powered content analysis and summarization across hexagonal regions
|
|
11
|
-
- **Voting System**: Built-in delegation and voting mechanisms for collaborative decision-making
|
|
12
|
-
- **Multi-scale Operations**: Automatic content propagation across different spatial resolutions
|
|
7
|
+
```bash
|
|
8
|
+
npm install holosphere
|
|
9
|
+
```
|
|
13
10
|
|
|
14
|
-
##
|
|
11
|
+
## Basic Usage
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
import HoloSphere from 'holosphere';
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
// Initialize HoloSphere
|
|
17
|
+
const holo = new HoloSphere('my-app');
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
// Optional: Initialize with OpenAI
|
|
20
|
+
const holoAI = new HoloSphere('my-app', 'your-openai-key');
|
|
21
|
+
```
|
|
19
22
|
|
|
20
|
-
|
|
23
|
+
## Core Features
|
|
21
24
|
|
|
22
|
-
|
|
25
|
+
### User Management
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
```javascript
|
|
28
|
+
// Create a new user
|
|
29
|
+
await holo.createUser('username', 'password');
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- Applications that use the library don't have to be LGPL
|
|
29
|
-
- If you modify the library, you must make the source code available
|
|
30
|
-
- You must provide a way for users to relink with a modified version of the library
|
|
31
|
+
// Login
|
|
32
|
+
await holo.login('username', 'password');
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
// Logout
|
|
35
|
+
await holo.logout();
|
|
36
|
+
```
|
|
33
37
|
|
|
34
|
-
###
|
|
35
|
-
- **h3-js**: Uber's H3 geospatial indexing system
|
|
36
|
-
- **gun**: Decentralized graph database
|
|
37
|
-
- **ajv**: JSON Schema validation
|
|
38
|
-
- **lodash**: Utility functions
|
|
38
|
+
### Schema Operations
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
```javascript
|
|
41
|
+
// Define a schema for a lens
|
|
42
|
+
const schema = {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
id: { type: 'string' },
|
|
46
|
+
data: { type: 'string' }
|
|
47
|
+
},
|
|
48
|
+
required: ['id', 'data']
|
|
49
|
+
};
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
// Set schema for a lens
|
|
52
|
+
await holo.setLensSchema('myLens', schema);
|
|
45
53
|
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
// Get schema for a lens
|
|
55
|
+
const lensSchema = await holo.getLensSchema('myLens');
|
|
48
56
|
```
|
|
49
57
|
|
|
50
|
-
|
|
58
|
+
### Global Data Operations
|
|
51
59
|
|
|
52
60
|
```javascript
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
peers: ['http://localhost:8765/gun'],
|
|
58
|
-
aiProvider: 'local' // or 'openai', 'azure', etc.
|
|
61
|
+
// Store data globally
|
|
62
|
+
await holo.putGlobalData('settings', {
|
|
63
|
+
id: 'config1',
|
|
64
|
+
theme: 'dark'
|
|
59
65
|
});
|
|
60
66
|
|
|
61
|
-
//
|
|
62
|
-
const
|
|
67
|
+
// Get all data from a table
|
|
68
|
+
const allSettings = await holo.getGlobalData('settings');
|
|
63
69
|
|
|
64
|
-
//
|
|
65
|
-
await
|
|
66
|
-
type: 'observation',
|
|
67
|
-
data: {
|
|
68
|
-
temperature: 22.5,
|
|
69
|
-
timestamp: Date.now()
|
|
70
|
-
}
|
|
71
|
-
});
|
|
70
|
+
// Get specific data by key
|
|
71
|
+
const config = await holo.getGlobalDataKey('settings', 'config1');
|
|
72
72
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
console.log('New data:', data);
|
|
76
|
-
});
|
|
73
|
+
// Delete a global table
|
|
74
|
+
await holo.deleteGlobalData('settings');
|
|
77
75
|
```
|
|
78
76
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
### Spatial Queries
|
|
77
|
+
### Hex Data Operations
|
|
82
78
|
|
|
83
79
|
```javascript
|
|
84
|
-
//
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
radius: 5000 // meters
|
|
80
|
+
// Store data in a hex
|
|
81
|
+
await holo.putHexData('hex123', 'observations', {
|
|
82
|
+
id: 'obs1',
|
|
83
|
+
data: 'value'
|
|
89
84
|
});
|
|
90
85
|
|
|
91
|
-
//
|
|
92
|
-
const
|
|
93
|
-
hexIds: hexagons,
|
|
94
|
-
resolution: 7,
|
|
95
|
-
method: 'average'
|
|
96
|
-
});
|
|
97
|
-
```
|
|
86
|
+
// Get all data from a hex/lens
|
|
87
|
+
const data = await holo.getHexData('hex123', 'observations');
|
|
98
88
|
|
|
99
|
-
|
|
89
|
+
// Get specific data by key
|
|
90
|
+
const item = await holo.getHexKey('hex123', 'observations', 'obs1');
|
|
100
91
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
const summary = await hex.processContent({
|
|
104
|
-
processor: 'summarize',
|
|
105
|
-
options: {
|
|
106
|
-
maxLength: 100
|
|
107
|
-
}
|
|
108
|
-
});
|
|
92
|
+
// Get raw GunDB node
|
|
93
|
+
const node = await holo.getHexNode('hex123', 'observations', 'obs1');
|
|
109
94
|
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
95
|
+
// Delete specific data
|
|
96
|
+
await holo.deleteHexData('hex123', 'observations', 'obs1');
|
|
97
|
+
|
|
98
|
+
// Delete a node by tag
|
|
99
|
+
await holo.deleteNode('nodeId', 'tag');
|
|
100
|
+
|
|
101
|
+
// Clear all data in a lens
|
|
102
|
+
await holo.clearlens('hex123', 'observations');
|
|
116
103
|
```
|
|
117
104
|
|
|
118
|
-
###
|
|
105
|
+
### Encrypted Data
|
|
119
106
|
|
|
120
107
|
```javascript
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
schema: newSchemaDefinition
|
|
127
|
-
}
|
|
128
|
-
});
|
|
108
|
+
// Store encrypted data (requires authenticated user)
|
|
109
|
+
await holo.putHexData('hex123', 'private', {
|
|
110
|
+
id: 'secret1',
|
|
111
|
+
data: 'sensitive'
|
|
112
|
+
}, true, 'encryption-key');
|
|
129
113
|
|
|
130
|
-
//
|
|
131
|
-
await
|
|
132
|
-
support: true,
|
|
133
|
-
weight: 1.0
|
|
134
|
-
});
|
|
135
|
-
```
|
|
114
|
+
// Encrypt specific data
|
|
115
|
+
const encrypted = await holo.encrypt(data, 'secret-key');
|
|
136
116
|
|
|
137
|
-
|
|
117
|
+
// Decrypt data
|
|
118
|
+
const decrypted = await holo.decrypt(encrypted, 'secret-key');
|
|
119
|
+
```
|
|
138
120
|
|
|
139
|
-
|
|
121
|
+
### Geospatial Operations
|
|
140
122
|
|
|
141
123
|
```javascript
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
},
|
|
151
|
-
|
|
152
|
-
// AI processing configuration
|
|
153
|
-
ai: {
|
|
154
|
-
provider: 'local',
|
|
155
|
-
model: 'gpt-3.5-turbo',
|
|
156
|
-
apiKey: process.env.AI_API_KEY
|
|
157
|
-
},
|
|
158
|
-
|
|
159
|
-
// Schema validation
|
|
160
|
-
schema: {
|
|
161
|
-
strict: true,
|
|
162
|
-
customValidators: {}
|
|
163
|
-
}
|
|
164
|
-
};
|
|
124
|
+
// Get hex from coordinates
|
|
125
|
+
const hex = await holo.getHex(37.7749, -74.0060, 7);
|
|
126
|
+
|
|
127
|
+
// Get all scales for coordinates
|
|
128
|
+
const scales = holo.getScalespace(37.7749, -74.0060);
|
|
129
|
+
|
|
130
|
+
// Get all parent hexes
|
|
131
|
+
const parents = holo.getHexScalespace('hex123');
|
|
165
132
|
|
|
166
|
-
|
|
133
|
+
// Compute operations on hex data
|
|
134
|
+
await holo.compute('hex123', 'observations', 'summarize');
|
|
135
|
+
|
|
136
|
+
// Upcast data to parent hexes
|
|
137
|
+
await holo.upcast('hex123', 'observations', data);
|
|
167
138
|
```
|
|
168
139
|
|
|
169
|
-
|
|
140
|
+
### Real-time Subscriptions
|
|
170
141
|
|
|
171
|
-
|
|
142
|
+
```javascript
|
|
143
|
+
// Subscribe to changes
|
|
144
|
+
holo.subscribe('hex123', 'observations', (data, key) => {
|
|
145
|
+
console.log('Updated data:', data);
|
|
146
|
+
console.log('Key:', key);
|
|
147
|
+
});
|
|
148
|
+
```
|
|
172
149
|
|
|
173
|
-
|
|
150
|
+
### Voting System
|
|
174
151
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
- Testing guidelines
|
|
179
|
-
- Pull request process
|
|
152
|
+
```javascript
|
|
153
|
+
// Get final vote (including delegations)
|
|
154
|
+
const finalVote = holo.getFinalVote(userId, topic, votes);
|
|
180
155
|
|
|
181
|
-
|
|
156
|
+
// Get aggregated votes for a hex
|
|
157
|
+
const results = holo.aggregateVotes(hexId, topic);
|
|
182
158
|
|
|
183
|
-
|
|
184
|
-
|
|
159
|
+
// Delegate voting power
|
|
160
|
+
await holo.delegateVote(userId, topic, delegateId);
|
|
185
161
|
|
|
186
|
-
|
|
162
|
+
// Cast a vote
|
|
163
|
+
await holo.vote(userId, hexId, topic, voteChoice);
|
|
164
|
+
```
|
|
187
165
|
|
|
188
|
-
|
|
189
|
-
- [ ] Additional AI model integrations
|
|
190
|
-
- [ ] Improved data replication strategies
|
|
191
|
-
- [ ] Extended governance mechanisms
|
|
192
|
-
- [ ] Mobile SDK development
|
|
166
|
+
## License
|
|
193
167
|
|
|
194
|
-
|
|
168
|
+
GPL-3.0-or-later
|
|
195
169
|
|
|
196
|
-
|
|
170
|
+
## Dependencies
|
|
197
171
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
year = {2024},
|
|
203
|
-
url = {https://github.com/holosphere/holosphere}
|
|
204
|
-
}
|
|
205
|
-
```
|
|
172
|
+
- h3-js: Uber's H3 geospatial indexing system
|
|
173
|
+
- gun: Decentralized graph database
|
|
174
|
+
- ajv: JSON Schema validation
|
|
175
|
+
- openai: OpenAI API client (optional)
|
|
206
176
|
|
package/holosphere.d.ts
CHANGED
|
@@ -1,20 +1,47 @@
|
|
|
1
1
|
declare module 'holosphere' {
|
|
2
2
|
export default class HoloSphere {
|
|
3
|
-
constructor(
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
constructor(appName: string, openaikey?: string | null);
|
|
4
|
+
|
|
5
|
+
// User Management
|
|
6
|
+
createUser(username: string, password: string): Promise<object>;
|
|
7
|
+
login(username: string, password: string): Promise<object>;
|
|
8
|
+
logout(): Promise<void>;
|
|
9
|
+
|
|
10
|
+
// Schema Operations
|
|
11
|
+
setLensSchema(lens: string, schema: object): Promise<void>;
|
|
12
|
+
getLensSchema(lens: string): Promise<object | null>;
|
|
13
|
+
|
|
14
|
+
// Encryption Operations
|
|
15
|
+
encrypt(data: any, secret: string): Promise<string>;
|
|
16
|
+
decrypt(encryptedData: string, secret: string): Promise<any>;
|
|
17
|
+
|
|
18
|
+
// Global Data Operations
|
|
19
|
+
putGlobalData(tableName: string, data: object): Promise<void>;
|
|
20
|
+
getGlobalData(tableName: string): Promise<object | null>;
|
|
21
|
+
getGlobalDataKey(tableName: string, key: string): Promise<object | null>;
|
|
22
|
+
deleteGlobalData(tableName: string): Promise<void>;
|
|
23
|
+
|
|
24
|
+
// Hex Data Operations
|
|
25
|
+
putHexData(hexId: string, lens: string, content: object, encrypt?: boolean, secret?: string | null): Promise<void>;
|
|
26
|
+
getHexData(hexId: string, lens: string, secret?: string | null): Promise<Array<any>>;
|
|
27
|
+
getHexKey(hexId: string, lens: string, key: string): Promise<any | null>;
|
|
28
|
+
getHexNode(hexId: string, lens: string, key: string): Promise<any>;
|
|
29
|
+
deleteHexData(hexId: string, lens: string, contentId: string): Promise<void>;
|
|
30
|
+
deleteNode(nodeId: string, tag: string): Promise<void>;
|
|
31
|
+
clearlens(hex: string, lens: string): Promise<void>;
|
|
32
|
+
|
|
33
|
+
// Geospatial Operations
|
|
15
34
|
getHex(lat: number, lng: number, resolution: number): Promise<string>;
|
|
16
35
|
getScalespace(lat: number, lng: number): string[];
|
|
17
36
|
getHexScalespace(hex: string): string[];
|
|
37
|
+
compute(hex: string, lens: string, operation: string): Promise<void>;
|
|
38
|
+
upcast(hex: string, lens: string, content: any): Promise<any>;
|
|
39
|
+
|
|
40
|
+
// Subscription
|
|
41
|
+
subscribe(hex: string, lens: string, callback: (data: any, key: string) => void): void;
|
|
42
|
+
|
|
43
|
+
// Voting System
|
|
44
|
+
getFinalVote(userId: string, topic: string, votes: object, visited?: Set<string>): string | null;
|
|
18
45
|
aggregateVotes(hexId: string, topic: string): object;
|
|
19
46
|
delegateVote(userId: string, topic: string, delegateTo: string): Promise<void>;
|
|
20
47
|
vote(userId: string, hexId: string, topic: string, vote: string): Promise<void>;
|
package/holosphere.js
CHANGED
|
@@ -12,7 +12,7 @@ class HoloSphere {
|
|
|
12
12
|
constructor(appname, openaikey = null) {
|
|
13
13
|
this.validator = new Ajv2019({ allErrors: false, strict: false });
|
|
14
14
|
this.gun = Gun({
|
|
15
|
-
peers: ['https://59.src.eco/gun'],
|
|
15
|
+
peers: ['http://gun.holons.io','https://59.src.eco/gun'],
|
|
16
16
|
axe: false,
|
|
17
17
|
// uuid: (content) => { // generate a unique id for each node
|
|
18
18
|
// console.log('uuid', content);
|
|
@@ -21,7 +21,7 @@ class HoloSphere {
|
|
|
21
21
|
|
|
22
22
|
this.gun = this.gun.get(appname)
|
|
23
23
|
this.users = {}; // Initialize users
|
|
24
|
-
this.
|
|
24
|
+
this.holonagonVotes = {}; // Initialize holonagonVotes
|
|
25
25
|
|
|
26
26
|
if (openaikey != null) {
|
|
27
27
|
this.openai = new OpenAI({
|
|
@@ -82,14 +82,14 @@ class HoloSphere {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
* Stores content in the specified
|
|
86
|
-
* @param {string}
|
|
85
|
+
* Stores content in the specified holon and lens.
|
|
86
|
+
* @param {string} holon - The holon identifier.
|
|
87
87
|
* @param {string} lens - The lens under which to store the content.
|
|
88
88
|
* @param {object} content - The content to store.
|
|
89
89
|
*/
|
|
90
|
-
async put(
|
|
91
|
-
if (!
|
|
92
|
-
console.error('Error in put:',
|
|
90
|
+
async put(holon, lens, content) {
|
|
91
|
+
if (!holon || !lens || !content) return;
|
|
92
|
+
console.error('Error in put:', holon, lens, content);
|
|
93
93
|
// Retrieve the schema for the lens
|
|
94
94
|
let schema = await this.getSchema(lens)
|
|
95
95
|
if (schema) {
|
|
@@ -108,19 +108,19 @@ class HoloSphere {
|
|
|
108
108
|
|
|
109
109
|
if (content.id) { //use the user-defined id. Important to be able to send updates using put
|
|
110
110
|
noderef = this.gun.get(lens).get(content.id).put(payload)
|
|
111
|
-
this.gun.get(
|
|
111
|
+
this.gun.get(holon.toString()).get(lens).get(content.id).put(payload)
|
|
112
112
|
} else { // create a content-addressable reference like IPFS. Note: no updates possible using put
|
|
113
113
|
const hashBuffer = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(payload));
|
|
114
114
|
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
115
|
-
const
|
|
116
|
-
noderef = this.gun.get(lens).get(
|
|
117
|
-
this.gun.get(
|
|
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)
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
async putNode(
|
|
123
|
-
this.gun.get(
|
|
122
|
+
async putNode(holon, lens, node) {
|
|
123
|
+
this.gun.get(holon).get(lens).set(node)
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
async parse(data) {
|
|
@@ -130,12 +130,12 @@ class HoloSphere {
|
|
|
130
130
|
if (data._ && data._["#"]) {
|
|
131
131
|
// If the data is a reference, fetch the actual content
|
|
132
132
|
let query = data._['#'].split('/');
|
|
133
|
-
let
|
|
133
|
+
let holon = query[1];
|
|
134
134
|
let lens = query[2];
|
|
135
135
|
let key = query[3];
|
|
136
|
-
parsed = await this.getKey(
|
|
136
|
+
parsed = await this.getKey(holon, lens, key);
|
|
137
137
|
} else if (data._ && data._['>']) {
|
|
138
|
-
// This might be a
|
|
138
|
+
// This might be a gun node, try to get its value
|
|
139
139
|
const nodeValue = Object.values(data).find(v => typeof v !== 'object' && v !== '_');
|
|
140
140
|
if (nodeValue) {
|
|
141
141
|
try {
|
|
@@ -145,7 +145,7 @@ class HoloSphere {
|
|
|
145
145
|
parsed = nodeValue; // return the raw data
|
|
146
146
|
}
|
|
147
147
|
} else {
|
|
148
|
-
console.log('Unable to parse
|
|
148
|
+
console.log('Unable to parse gun node:', data);
|
|
149
149
|
parsed = data; // return the original data
|
|
150
150
|
}
|
|
151
151
|
} else {
|
|
@@ -166,17 +166,17 @@ class HoloSphere {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
|
-
* Retrieves content from the specified
|
|
170
|
-
* @param {string}
|
|
169
|
+
* Retrieves content from the specified holon and lens.
|
|
170
|
+
* @param {string} holon - The holon identifier.
|
|
171
171
|
* @param {string} lens - The lens from which to retrieve content.
|
|
172
172
|
* @returns {Promise<Array<object>>} - The retrieved content.
|
|
173
173
|
*/
|
|
174
|
-
async get(
|
|
175
|
-
if (!
|
|
176
|
-
console.log('Wrong get:',
|
|
174
|
+
async get(holon, lens) {
|
|
175
|
+
if (!holon || !lens) {
|
|
176
|
+
console.log('Wrong get:', holon, lens)
|
|
177
177
|
return;
|
|
178
178
|
}
|
|
179
|
-
// Wrap the
|
|
179
|
+
// Wrap the gun operation in a promise
|
|
180
180
|
//retrieve lens schema
|
|
181
181
|
const schema = await this.getSchema(lens);
|
|
182
182
|
|
|
@@ -188,11 +188,11 @@ class HoloSphere {
|
|
|
188
188
|
return new Promise(async (resolve, reject) => {
|
|
189
189
|
let output = []
|
|
190
190
|
let counter = 0
|
|
191
|
-
this.gun.get(
|
|
191
|
+
this.gun.get(holon.toString()).get(lens).once((data, key) => {
|
|
192
192
|
if (data) {
|
|
193
193
|
const maplenght = Object.keys(data).length - 1
|
|
194
194
|
console.log('Map length:', maplenght)
|
|
195
|
-
this.gun.get(
|
|
195
|
+
this.gun.get(holon.toString()).get(lens).map().once(async (itemdata, key) => {
|
|
196
196
|
counter += 1
|
|
197
197
|
if (itemdata) {
|
|
198
198
|
let parsed = await this.parse (itemdata)
|
|
@@ -202,7 +202,7 @@ class HoloSphere {
|
|
|
202
202
|
let valid = this.validator.validate(schema, parsed);
|
|
203
203
|
if (!valid || parsed == null || parsed == undefined) {
|
|
204
204
|
console.log('Removing Invalid content:', this.validator.errors);
|
|
205
|
-
this.gun.get(
|
|
205
|
+
this.gun.get(holon).get(lens).get(key).put(null);
|
|
206
206
|
|
|
207
207
|
} else {
|
|
208
208
|
output.push(parsed);
|
|
@@ -225,16 +225,16 @@ class HoloSphere {
|
|
|
225
225
|
}
|
|
226
226
|
|
|
227
227
|
/**
|
|
228
|
-
* Retrieves a specific key from the specified
|
|
229
|
-
* @param {string}
|
|
228
|
+
* Retrieves a specific key from the specified holon and lens.
|
|
229
|
+
* @param {string} holon - The holon identifier.
|
|
230
230
|
* @param {string} lens - The lens from which to retrieve the key.
|
|
231
231
|
* @param {string} key - The specific key to retrieve.
|
|
232
232
|
* @returns {Promise<object|null>} - The retrieved content or null if not found.
|
|
233
233
|
*/
|
|
234
|
-
async getKey(
|
|
234
|
+
async getKey(holon, lens, key) {
|
|
235
235
|
return new Promise((resolve) => {
|
|
236
236
|
// Use Gun to get the data
|
|
237
|
-
this.gun.get(
|
|
237
|
+
this.gun.get(holon).get(lens).get(key).once((data, key) => {
|
|
238
238
|
if (data) {
|
|
239
239
|
console.log('Data getting parsed:', data)
|
|
240
240
|
try {
|
|
@@ -254,33 +254,132 @@ class HoloSphere {
|
|
|
254
254
|
}
|
|
255
255
|
|
|
256
256
|
/**
|
|
257
|
-
* Retrieves a specific
|
|
258
|
-
* @param {string}
|
|
257
|
+
* Retrieves a specific gun node from the specified holon and lens.
|
|
258
|
+
* @param {string} holon - The holon identifier.
|
|
259
259
|
* @param {string} lens - The lens from which to retrieve the key.
|
|
260
260
|
* @param {string} key - The specific key to retrieve.
|
|
261
261
|
* @returns {Promise<object|null>} - The retrieved content or null if not found.
|
|
262
262
|
*/
|
|
263
|
-
getNode(
|
|
263
|
+
getNode(holon, lens, key) {
|
|
264
264
|
// Use Gun to get the data
|
|
265
|
-
return this.gun.get(
|
|
265
|
+
return this.gun.get(holon).get(lens).get(key)
|
|
266
266
|
}
|
|
267
267
|
|
|
268
|
+
//GLOBAL FUNCTIONS
|
|
269
|
+
async deleteNode(nodeId, tag) {
|
|
270
|
+
await this.gun.get(nodeId).get(tag).put(null)
|
|
271
|
+
}
|
|
272
|
+
// ================================ GLOBAL FUNCTIONS ================================
|
|
273
|
+
/**
|
|
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>}
|
|
278
|
+
*/
|
|
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
|
+
}
|
|
285
|
+
|
|
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 => {
|
|
296
|
+
if (ack.err) {
|
|
297
|
+
reject(new Error(ack.err));
|
|
298
|
+
} else {
|
|
299
|
+
resolve();
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
268
305
|
|
|
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.
|
|
311
|
+
*/
|
|
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);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
const parsed = JSON.parse(data);
|
|
321
|
+
resolve(parsed);
|
|
322
|
+
} catch (e) {
|
|
323
|
+
resolve(null);
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
}
|
|
269
328
|
|
|
270
329
|
/**
|
|
271
|
-
*
|
|
272
|
-
* @param {string}
|
|
330
|
+
* Retrieves all data from a global table.
|
|
331
|
+
* @param {string} tableName - The table name to retrieve data from.
|
|
332
|
+
* @returns {Promise<object|null>} - The parsed data from the table or null if not found.
|
|
333
|
+
*/
|
|
334
|
+
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
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Deletes an entire global table.
|
|
355
|
+
* @param {string} table - The table name to delete.
|
|
356
|
+
* @returns {Promise<void>}
|
|
357
|
+
*/
|
|
358
|
+
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();
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// ================================ COMPUTE FUNCTIONS ================================
|
|
369
|
+
/**
|
|
370
|
+
* Computes summaries based on the content within a holon and lens.
|
|
371
|
+
* @param {string} holon - The holon identifier.
|
|
273
372
|
* @param {string} lens - The lens to compute.
|
|
274
373
|
* @param {string} operation - The operation to perform.
|
|
275
374
|
*/
|
|
276
|
-
async compute(
|
|
375
|
+
async compute(holon, lens, operation) {
|
|
277
376
|
|
|
278
|
-
let res = h3.getResolution(
|
|
377
|
+
let res = h3.getResolution(holon);
|
|
279
378
|
if (res < 1 || res > 15) return;
|
|
280
379
|
console.log(res)
|
|
281
|
-
let parent = h3.cellToParent(
|
|
380
|
+
let parent = h3.cellToParent(holon, res - 1);
|
|
282
381
|
let siblings = h3.cellToChildren(parent, res);
|
|
283
|
-
console.log(
|
|
382
|
+
console.log(holon, parent, siblings, res)
|
|
284
383
|
|
|
285
384
|
let content = [];
|
|
286
385
|
let promises = [];
|
|
@@ -313,18 +412,18 @@ class HoloSphere {
|
|
|
313
412
|
}
|
|
314
413
|
|
|
315
414
|
/**
|
|
316
|
-
* Clears all entities under a specific
|
|
317
|
-
* @param {string}
|
|
415
|
+
* Clears all entities under a specific holon and lens.
|
|
416
|
+
* @param {string} holon - The holon identifier.
|
|
318
417
|
* @param {string} lens - The lens to clear.
|
|
319
418
|
*/
|
|
320
|
-
async clearlens(
|
|
419
|
+
async clearlens(holon, lens) {
|
|
321
420
|
let entities = {};
|
|
322
421
|
|
|
323
422
|
// Get list out of Gun
|
|
324
|
-
this.gun.get(
|
|
423
|
+
this.gun.get(holon).get(lens).map().once((data, key) => {
|
|
325
424
|
//entities = data;
|
|
326
425
|
//const id = Object.keys(entities)[0] // since this would be in object form, you can manipulate it as you would like.
|
|
327
|
-
this.gun.get(
|
|
426
|
+
this.gun.get(holon).get(lens).put({ [key]: null })
|
|
328
427
|
})
|
|
329
428
|
}
|
|
330
429
|
|
|
@@ -367,30 +466,30 @@ class HoloSphere {
|
|
|
367
466
|
}
|
|
368
467
|
|
|
369
468
|
/**
|
|
370
|
-
* Upcasts content to parent
|
|
371
|
-
* @param {string}
|
|
469
|
+
* Upcasts content to parent holonagons recursively.
|
|
470
|
+
* @param {string} holon - The current holon identifier.
|
|
372
471
|
* @param {string} lens - The lens under which to upcast.
|
|
373
472
|
* @param {object} content - The content to upcast.
|
|
374
473
|
* @returns {Promise<object>} - The upcasted content.
|
|
375
474
|
*/
|
|
376
|
-
async upcast(
|
|
377
|
-
let res = h3.getResolution(
|
|
475
|
+
async upcast(holon, lens, content) {
|
|
476
|
+
let res = h3.getResolution(holon)
|
|
378
477
|
if (res == 0) {
|
|
379
|
-
await this.putNode(
|
|
478
|
+
await this.putNode(holon, lens, content)
|
|
380
479
|
return content
|
|
381
480
|
}
|
|
382
481
|
else {
|
|
383
|
-
console.log('Upcasting ',
|
|
384
|
-
await this.putNode(
|
|
385
|
-
let parent = h3.cellToParent(
|
|
482
|
+
console.log('Upcasting ', holon, lens, content, res)
|
|
483
|
+
await this.putNode(holon, lens, content)
|
|
484
|
+
let parent = h3.cellToParent(holon, res - 1)
|
|
386
485
|
return this.upcast(parent, lens, content)
|
|
387
486
|
}
|
|
388
487
|
}
|
|
389
488
|
|
|
390
489
|
|
|
391
490
|
/**
|
|
392
|
-
* Updates the parent
|
|
393
|
-
* @param {string} id - The child
|
|
491
|
+
* Updates the parent holonagon with a new report.
|
|
492
|
+
* @param {string} id - The child holon identifier.
|
|
394
493
|
* @param {string} report - The report to update.
|
|
395
494
|
* @returns {Promise<object>} - The updated parent information.
|
|
396
495
|
*/
|
|
@@ -410,21 +509,21 @@ class HoloSphere {
|
|
|
410
509
|
|
|
411
510
|
|
|
412
511
|
/**
|
|
413
|
-
* Converts latitude and longitude to a
|
|
512
|
+
* Converts latitude and longitude to a holon identifier.
|
|
414
513
|
* @param {number} lat - The latitude.
|
|
415
514
|
* @param {number} lng - The longitude.
|
|
416
515
|
* @param {number} resolution - The resolution level.
|
|
417
|
-
* @returns {Promise<string>} - The resulting
|
|
516
|
+
* @returns {Promise<string>} - The resulting holon identifier.
|
|
418
517
|
*/
|
|
419
|
-
async
|
|
518
|
+
async getHolon(lat, lng, resolution) {
|
|
420
519
|
return h3.latLngToCell(lat, lng, resolution);
|
|
421
520
|
}
|
|
422
521
|
|
|
423
522
|
/**
|
|
424
|
-
* Retrieves all containing
|
|
523
|
+
* Retrieves all containing holonagons at all scales for given coordinates.
|
|
425
524
|
* @param {number} lat - The latitude.
|
|
426
525
|
* @param {number} lng - The longitude.
|
|
427
|
-
* @returns {Array<string>} - List of
|
|
526
|
+
* @returns {Array<string>} - List of holon identifiers.
|
|
428
527
|
*/
|
|
429
528
|
getScalespace(lat, lng) {
|
|
430
529
|
let list = []
|
|
@@ -437,31 +536,32 @@ class HoloSphere {
|
|
|
437
536
|
}
|
|
438
537
|
|
|
439
538
|
/**
|
|
440
|
-
* Retrieves all containing
|
|
441
|
-
* @param {string}
|
|
442
|
-
* @returns {Array<string>} - List of
|
|
539
|
+
* Retrieves all containing holonagons at all scales for a given holon.
|
|
540
|
+
* @param {string} holon - The holon identifier.
|
|
541
|
+
* @returns {Array<string>} - List of holon identifiers.
|
|
443
542
|
*/
|
|
444
|
-
|
|
543
|
+
getHolonScalespace(holon) {
|
|
445
544
|
let list = []
|
|
446
|
-
let res = h3.getResolution(
|
|
545
|
+
let res = h3.getResolution(holon)
|
|
447
546
|
for (let i = res; i >= 0; i--) {
|
|
448
|
-
list.push(h3.cellToParent(
|
|
547
|
+
list.push(h3.cellToParent(holon, i))
|
|
449
548
|
}
|
|
450
549
|
return list
|
|
451
550
|
}
|
|
452
551
|
|
|
453
552
|
/**
|
|
454
|
-
* Subscribes to changes in a specific
|
|
455
|
-
* @param {string}
|
|
553
|
+
* Subscribes to changes in a specific holon and lens.
|
|
554
|
+
* @param {string} holon - The holon identifier.
|
|
456
555
|
* @param {string} lens - The lens to subscribe to.
|
|
457
556
|
* @param {function} callback - The callback to execute on changes.
|
|
458
557
|
*/
|
|
459
|
-
subscribe(
|
|
460
|
-
this.gun.get(
|
|
558
|
+
subscribe(holon, lens, callback) {
|
|
559
|
+
this.gun.get(holon).get(lens).map().on((data, key) => {
|
|
461
560
|
callback(data, key)
|
|
462
561
|
})
|
|
463
562
|
}
|
|
464
563
|
|
|
564
|
+
// ================================ GOVERNANCE FUNCTIONS ================================
|
|
465
565
|
/**
|
|
466
566
|
* Retrieves the final vote for a user, considering delegations.
|
|
467
567
|
* @param {string} userId - The user's identifier.
|
|
@@ -488,16 +588,16 @@ class HoloSphere {
|
|
|
488
588
|
}
|
|
489
589
|
|
|
490
590
|
/**
|
|
491
|
-
* Aggregates votes for a specific
|
|
492
|
-
* @param {string}
|
|
591
|
+
* Aggregates votes for a specific holon and topic.
|
|
592
|
+
* @param {string} holonId - The holon identifier.
|
|
493
593
|
* @param {string} topic - The voting topic.
|
|
494
594
|
* @returns {object} - Aggregated vote counts.
|
|
495
595
|
*/
|
|
496
|
-
aggregateVotes(
|
|
497
|
-
if (!this.
|
|
596
|
+
aggregateVotes(holonId, topic) {
|
|
597
|
+
if (!this.holonagonVotes[holonId] || !this.holonagonVotes[holonId][topic]) {
|
|
498
598
|
return {}; // Handle undefined votes
|
|
499
599
|
}
|
|
500
|
-
const votes = this.
|
|
600
|
+
const votes = this.holonagonVotes[holonId][topic];
|
|
501
601
|
const aggregatedVotes = {};
|
|
502
602
|
|
|
503
603
|
Object.keys(votes).forEach(userId => {
|
|
@@ -526,24 +626,21 @@ class HoloSphere {
|
|
|
526
626
|
}
|
|
527
627
|
|
|
528
628
|
/**
|
|
529
|
-
* Casts a vote for a user on a specific topic and
|
|
629
|
+
* Casts a vote for a user on a specific topic and holon.
|
|
530
630
|
* @param {string} userId - The user's identifier.
|
|
531
|
-
* @param {string}
|
|
631
|
+
* @param {string} holonId - The holon identifier.
|
|
532
632
|
* @param {string} topic - The voting topic.
|
|
533
633
|
* @param {string} vote - The vote choice.
|
|
534
634
|
*/
|
|
535
|
-
async vote(userId,
|
|
635
|
+
async vote(userId, holonId, topic, vote) {
|
|
536
636
|
const response = await fetch('/vote', {
|
|
537
637
|
method: 'POST',
|
|
538
638
|
headers: { 'Content-Type': 'application/json' },
|
|
539
|
-
body: JSON.stringify({ userId,
|
|
639
|
+
body: JSON.stringify({ userId, holonId, topic, vote })
|
|
540
640
|
});
|
|
541
641
|
alert(await response.text());
|
|
542
642
|
}
|
|
543
643
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
644
|
}
|
|
548
645
|
|
|
549
646
|
export default HoloSphere;
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "holosphere",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "Holonic Geospatial Communication Infrastructure",
|
|
5
5
|
"main": "holosphere.js",
|
|
6
6
|
"types": "holosphere.d.ts",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"test": "
|
|
9
|
+
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
|
10
10
|
},
|
|
11
11
|
"author": "Roberto Valenti",
|
|
12
12
|
"license": "GPL-3.0-or-later",
|
|
@@ -15,5 +15,16 @@
|
|
|
15
15
|
"gun": "^0.2020.1240",
|
|
16
16
|
"h3-js": "^4.1.0",
|
|
17
17
|
"openai": "^4.29.2"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"jest": "^29.7.0",
|
|
21
|
+
"jest-environment-node": "^29.7.0"
|
|
22
|
+
},
|
|
23
|
+
"jest": {
|
|
24
|
+
"testEnvironment": "node",
|
|
25
|
+
"transform": {},
|
|
26
|
+
"moduleNameMapper": {
|
|
27
|
+
"^(\\.{1,2}/.*)\\.js$": "$1"
|
|
28
|
+
}
|
|
18
29
|
}
|
|
19
30
|
}
|
package/test/holosphere.test.js
CHANGED
|
@@ -1,32 +1,199 @@
|
|
|
1
1
|
import HoloSphere from '../holosphere.js';
|
|
2
|
+
import * as h3 from 'h3-js';
|
|
2
3
|
|
|
3
4
|
describe('HoloSphere', () => {
|
|
4
5
|
let holoSphere;
|
|
5
|
-
|
|
6
|
+
const testAppName = 'test-app';
|
|
7
|
+
|
|
6
8
|
beforeEach(() => {
|
|
7
|
-
holoSphere = new HoloSphere(
|
|
8
|
-
|
|
9
|
+
holoSphere = new HoloSphere(testAppName);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('Constructor', () => {
|
|
13
|
+
test('should create instance with app name', () => {
|
|
14
|
+
expect(holoSphere).toBeInstanceOf(HoloSphere);
|
|
15
|
+
expect(holoSphere.gunDb).toBeDefined();
|
|
16
|
+
expect(holoSphere.validator).toBeDefined();
|
|
17
|
+
expect(holoSphere.aiClient).toBeUndefined();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('should initialize with OpenAI when key provided', () => {
|
|
21
|
+
const hsWithAI = new HoloSphere(testAppName, 'fake-key');
|
|
22
|
+
expect(hsWithAI.aiClient).toBeDefined();
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('Schema Operations', () => {
|
|
27
|
+
const testLens = 'testLens';
|
|
28
|
+
const validSchema = {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
id: { type: 'string' },
|
|
32
|
+
data: { type: 'string' }
|
|
33
|
+
},
|
|
34
|
+
required: ['id', 'data']
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
test('should set and get lens schema', async () => {
|
|
38
|
+
await holoSphere.setLensSchema(testLens, validSchema);
|
|
39
|
+
const result = await holoSphere.getLensSchema(testLens);
|
|
40
|
+
expect(result).toEqual(validSchema);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should validate data against schema', async () => {
|
|
44
|
+
const validData = { id: 'test123', data: 'test data' };
|
|
45
|
+
const invalidData = { id: 123, data: ['wrong type'] };
|
|
46
|
+
|
|
47
|
+
await holoSphere.setLensSchema(testLens, validSchema);
|
|
48
|
+
|
|
49
|
+
const validResult = holoSphere.validator.validate(validSchema, validData);
|
|
50
|
+
expect(validResult).toBe(true);
|
|
51
|
+
|
|
52
|
+
const invalidResult = holoSphere.validator.validate(validSchema, invalidData);
|
|
53
|
+
expect(invalidResult).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe('Data Operations', () => {
|
|
58
|
+
const testHex = h3.latLngToCell(40.7128, -74.0060, 7);
|
|
59
|
+
const testLens = 'testLens';
|
|
60
|
+
const testContent = { id: '123', data: 'test' };
|
|
61
|
+
|
|
62
|
+
test('should put and get hex data', async () => {
|
|
63
|
+
await holoSphere.putHexData(testHex, testLens, testContent);
|
|
64
|
+
const result = await holoSphere.getHexData(testHex, testLens);
|
|
65
|
+
expect(Array.isArray(result)).toBeTruthy();
|
|
66
|
+
expect(result.some(item => item.id === testContent.id)).toBeTruthy();
|
|
67
|
+
}, 10000);
|
|
68
|
+
|
|
69
|
+
test('should get hex key', async () => {
|
|
70
|
+
await holoSphere.putHexData(testHex, testLens, testContent);
|
|
71
|
+
const result = await holoSphere.getHexKey(testHex, testLens, testContent.id);
|
|
72
|
+
expect(result).toBeDefined();
|
|
73
|
+
expect(result.id).toBe(testContent.id);
|
|
74
|
+
}, 10000);
|
|
75
|
+
|
|
76
|
+
test('should delete hex data', async () => {
|
|
77
|
+
await holoSphere.putHexData(testHex, testLens, testContent);
|
|
78
|
+
await holoSphere.deleteHexData(testHex, testLens, testContent.id);
|
|
79
|
+
const result = await holoSphere.getHexKey(testHex, testLens, testContent.id);
|
|
80
|
+
expect(result).toBeNull();
|
|
81
|
+
}, 10000);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe('Global Data Operations', () => {
|
|
85
|
+
const tableName = 'testTable';
|
|
86
|
+
const testData = { id: 'test1', value: 'testValue' };
|
|
87
|
+
|
|
88
|
+
test('should put and get global data', async () => {
|
|
89
|
+
await holoSphere.putGlobalData(tableName, testData);
|
|
90
|
+
// Add delay to allow Gun to process
|
|
91
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
92
|
+
|
|
93
|
+
const result = await holoSphere.getGlobalData(tableName);
|
|
94
|
+
expect(result).toBeDefined();
|
|
95
|
+
if (result) {
|
|
96
|
+
expect(result.id).toBe(testData.id);
|
|
97
|
+
}
|
|
98
|
+
}, 15000);
|
|
99
|
+
|
|
100
|
+
test('should get global data key', async () => {
|
|
101
|
+
await holoSphere.putGlobalData(tableName, testData);
|
|
102
|
+
// Add delay to allow Gun to process
|
|
103
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
104
|
+
|
|
105
|
+
const result = await holoSphere.getGlobalDataKey(tableName, testData.id);
|
|
106
|
+
expect(result).toBeDefined();
|
|
107
|
+
if (result) {
|
|
108
|
+
expect(result.id).toBe(testData.id);
|
|
109
|
+
}
|
|
110
|
+
}, 15000);
|
|
111
|
+
|
|
112
|
+
test('should delete global data', async () => {
|
|
113
|
+
await holoSphere.putGlobalData(tableName, testData);
|
|
114
|
+
// Add delay to allow Gun to process
|
|
115
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
116
|
+
|
|
117
|
+
await holoSphere.deleteGlobalData(tableName);
|
|
118
|
+
// Add delay to allow Gun to process
|
|
119
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
120
|
+
|
|
121
|
+
const result = await holoSphere.getGlobalData(tableName);
|
|
122
|
+
expect(result).toBeNull();
|
|
123
|
+
}, 15000);
|
|
124
|
+
|
|
125
|
+
afterEach(async () => {
|
|
126
|
+
// Clean up after each test
|
|
127
|
+
await holoSphere.deleteGlobalData(tableName);
|
|
128
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
9
129
|
});
|
|
10
130
|
});
|
|
11
131
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
132
|
+
describe('Encryption Operations', () => {
|
|
133
|
+
const testSecret = 'secret123';
|
|
134
|
+
const testData = { message: 'secret message' };
|
|
135
|
+
|
|
136
|
+
test('should encrypt and decrypt data', async () => {
|
|
137
|
+
const encrypted = await holoSphere.encrypt(testData, testSecret);
|
|
138
|
+
expect(encrypted).toBeDefined();
|
|
139
|
+
|
|
140
|
+
const decrypted = await holoSphere.decrypt(encrypted, testSecret);
|
|
141
|
+
expect(decrypted).toEqual(testData);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('should handle encrypted hex data', async () => {
|
|
145
|
+
const testHex = h3.latLngToCell(40.7128, -74.0060, 7);
|
|
146
|
+
const testLens = 'testLens';
|
|
147
|
+
const testContent = { id: 'test123', data: 'secret data' };
|
|
148
|
+
|
|
149
|
+
// Create and login test user
|
|
150
|
+
try {
|
|
151
|
+
await holoSphere.createUser('testuser', 'testpass');
|
|
152
|
+
await holoSphere.login('testuser', 'testpass');
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.log('User already exists or login failed');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await holoSphere.putHexData(testHex, testLens, testContent, true, testSecret);
|
|
158
|
+
const result = await holoSphere.getHexData(testHex, testLens, testSecret);
|
|
159
|
+
|
|
160
|
+
expect(Array.isArray(result)).toBeTruthy();
|
|
161
|
+
await holoSphere.logout();
|
|
162
|
+
}, 15000);
|
|
16
163
|
});
|
|
17
164
|
|
|
18
|
-
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const vote = 'yes';
|
|
165
|
+
describe('Geospatial Operations', () => {
|
|
166
|
+
const lat = 40.7128;
|
|
167
|
+
const lng = -74.0060;
|
|
168
|
+
const resolution = 7;
|
|
23
169
|
|
|
24
|
-
|
|
25
|
-
|
|
170
|
+
test('should get hex from coordinates', async () => {
|
|
171
|
+
const hex = await holoSphere.getHex(lat, lng, resolution);
|
|
172
|
+
expect(hex).toBeDefined();
|
|
173
|
+
expect(typeof hex).toBe('string');
|
|
174
|
+
});
|
|
26
175
|
|
|
27
|
-
|
|
28
|
-
|
|
176
|
+
test('should get scalespace from coordinates', () => {
|
|
177
|
+
const scales = holoSphere.getScalespace(lat, lng);
|
|
178
|
+
expect(Array.isArray(scales)).toBeTruthy();
|
|
179
|
+
expect(scales.length).toBe(15);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('should get hex scalespace', () => {
|
|
183
|
+
const hex = h3.latLngToCell(lat, lng, resolution);
|
|
184
|
+
const scales = holoSphere.getHexScalespace(hex);
|
|
185
|
+
expect(Array.isArray(scales)).toBeTruthy();
|
|
186
|
+
expect(scales.length).toBe(resolution + 1);
|
|
187
|
+
});
|
|
29
188
|
});
|
|
30
189
|
|
|
31
|
-
|
|
190
|
+
afterAll(async () => {
|
|
191
|
+
// Clean up test data
|
|
192
|
+
const testLens = 'testLens';
|
|
193
|
+
const testHex = h3.latLngToCell(40.7128, -74.0060, 7);
|
|
194
|
+
await holoSphere.clearlens(testHex, testLens);
|
|
195
|
+
|
|
196
|
+
// Allow time for Gun to process
|
|
197
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
198
|
+
});
|
|
32
199
|
});
|