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 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
- ## Features
5
+ ## Installation
6
6
 
7
- - **Hierarchical Spatial Data**: Uses Uber's H3 spatial indexing system for efficient geospatial data organization
8
- - **Decentralized Storage**: Built on GunDB for peer-to-peer data storage and synchronization
9
- - **Schema Validation**: JSON Schema validation for data integrity
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
- ## License
11
+ ## Basic Usage
12
+
13
+ ```javascript
14
+ import HoloSphere from 'holosphere';
15
15
 
16
- GNU Lesser General Public License v3.0 (LGPL-3.0)
16
+ // Initialize HoloSphere
17
+ const holo = new HoloSphere('my-app');
17
18
 
18
- This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
19
+ // Optional: Initialize with OpenAI
20
+ const holoAI = new HoloSphere('my-app', 'your-openai-key');
21
+ ```
19
22
 
20
- This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
23
+ ## Core Features
21
24
 
22
- You should have received a copy of the GNU Lesser General Public License along with this library; if not, see <https://www.gnu.org/licenses/>.
25
+ ### User Management
23
26
 
24
- ### Key Points of LGPL-3.0
27
+ ```javascript
28
+ // Create a new user
29
+ await holo.createUser('username', 'password');
25
30
 
26
- - You can use this library in proprietary applications
27
- - Any modifications to the library itself must be distributed under LGPL
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
- ## Dependencies
34
+ // Logout
35
+ await holo.logout();
36
+ ```
33
37
 
34
- ### Core Dependencies
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
- ### Optional Dependencies
41
- - **langchain**: AI/LLM integration framework
42
- - **openai**: OpenAI API client (optional)
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
- ## Getting Started
51
+ // Set schema for a lens
52
+ await holo.setLensSchema('myLens', schema);
45
53
 
46
- ```bash
47
- npm install holosphere
54
+ // Get schema for a lens
55
+ const lensSchema = await holo.getLensSchema('myLens');
48
56
  ```
49
57
 
50
- ## Basic Usage
58
+ ### Global Data Operations
51
59
 
52
60
  ```javascript
53
- import { HoloSphere } from 'holosphere';
54
-
55
- // Initialize HoloSphere with your configuration
56
- const holo = new HoloSphere({
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
- // Create or access a hexagonal region
62
- const hex = await holo.getHex('8928308280fffff');
67
+ // Get all data from a table
68
+ const allSettings = await holo.getGlobalData('settings');
63
69
 
64
- // Store data in the region
65
- await hex.put({
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
- // Subscribe to changes
74
- hex.subscribe(data => {
75
- console.log('New data:', data);
76
- });
73
+ // Delete a global table
74
+ await holo.deleteGlobalData('settings');
77
75
  ```
78
76
 
79
- ## Advanced Features
80
-
81
- ### Spatial Queries
77
+ ### Hex Data Operations
82
78
 
83
79
  ```javascript
84
- // Find all hexagons within a radius
85
- const hexagons = await holo.findWithin({
86
- lat: 37.7749,
87
- lng: -122.4194,
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
- // Query data across multiple resolutions
92
- const aggregatedData = await holo.aggregate({
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
- ### AI Processing
89
+ // Get specific data by key
90
+ const item = await holo.getHexKey('hex123', 'observations', 'obs1');
100
91
 
101
- ```javascript
102
- // Process content with AI
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
- // Analyze trends across regions
111
- const analysis = await holo.analyzeRegion({
112
- hexIds: hexagons,
113
- timeRange: '7d',
114
- metrics: ['sentiment', 'topics']
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
- ### Voting and Governance
105
+ ### Encrypted Data
119
106
 
120
107
  ```javascript
121
- // Create a proposal
122
- const proposal = await hex.createProposal({
123
- title: 'Update Data Schema',
124
- description: 'Add new fields for environmental metrics',
125
- changes: {
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
- // Cast a vote
131
- await proposal.vote({
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
- ## Configuration
117
+ // Decrypt data
118
+ const decrypted = await holo.decrypt(encrypted, 'secret-key');
119
+ ```
138
120
 
139
- HoloSphere can be configured with various options:
121
+ ### Geospatial Operations
140
122
 
141
123
  ```javascript
142
- const config = {
143
- // Network configuration
144
- peers: ['https://peer1.example.com/gun', 'https://peer2.example.com/gun'],
145
-
146
- // Storage options
147
- storage: {
148
- type: 'indexeddb',
149
- namespace: 'my-app'
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
- const holo = new HoloSphere(config);
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
- ## Documentation
140
+ ### Real-time Subscriptions
170
141
 
171
- For detailed documentation, please visit our [documentation site](https://docs.holons.io).
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
- ## Contributing
150
+ ### Voting System
174
151
 
175
- We welcome contributions! Please see our [Contributing Guide](CONTRIBUTING.md) for details on:
176
- - Code of Conduct
177
- - Development setup
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
- ## Support
156
+ // Get aggregated votes for a hex
157
+ const results = holo.aggregateVotes(hexId, topic);
182
158
 
183
- - [GitHub Issues](https://github.com/liminalvillage/holosphere/issues)
184
- - [Discord Community](https://discord.gg/liminalvillage)
159
+ // Delegate voting power
160
+ await holo.delegateVote(userId, topic, delegateId);
185
161
 
186
- ## Roadmap
162
+ // Cast a vote
163
+ await holo.vote(userId, hexId, topic, voteChoice);
164
+ ```
187
165
 
188
- - [ ] Enhanced spatial indexing algorithms
189
- - [ ] Additional AI model integrations
190
- - [ ] Improved data replication strategies
191
- - [ ] Extended governance mechanisms
192
- - [ ] Mobile SDK development
166
+ ## License
193
167
 
194
- ## Citation
168
+ GPL-3.0-or-later
195
169
 
196
- If you use HoloSphere in your research, please cite:
170
+ ## Dependencies
197
171
 
198
- ```bibtex
199
- @software{holosphere2024,
200
- author = {Roberto Valenti},
201
- title = {HoloSphere: Decentralized Geospatial Data Management},
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
 
@@ -0,0 +1,12 @@
1
+ {
2
+ "presets": [
3
+ [
4
+ "@babel/preset-env",
5
+ {
6
+ "targets": {
7
+ "node": "current"
8
+ }
9
+ }
10
+ ]
11
+ ]
12
+ }
package/holosphere.d.ts CHANGED
@@ -1,20 +1,47 @@
1
1
  declare module 'holosphere' {
2
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>;
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.hexagonVotes = {}; // Initialize hexagonVotes
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 hex and lens.
86
- * @param {string} hex - The hex identifier.
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(hex, lens, content) {
91
- if (!hex || !lens || !content) return;
92
- console.error('Error in put:', hex, lens, content);
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(hex.toString()).get(lens).get(content.id).put(payload)
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 hashHex = hashArray.map(byte => byte.toString(16).padStart(2, "0")).join("");
116
- noderef = this.gun.get(lens).get(hashHex).put(payload)
117
- this.gun.get(hex.toString()).get(lens).get(hashHex).put(payload)
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(hex, lens, node) {
123
- this.gun.get(hex).get(lens).set(node)
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 hex = query[1];
133
+ let holon = query[1];
134
134
  let lens = query[2];
135
135
  let key = query[3];
136
- parsed = await this.getKey(hex, lens, key);
136
+ parsed = await this.getKey(holon, lens, key);
137
137
  } else if (data._ && data._['>']) {
138
- // This might be a GunDB node, try to get its value
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 GunDB node:', data);
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 hex and lens.
170
- * @param {string} hex - The hex identifier.
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(hex, lens) {
175
- if (!hex || !lens) {
176
- console.log('Wrong get:', hex, lens)
174
+ async get(holon, lens) {
175
+ if (!holon || !lens) {
176
+ console.log('Wrong get:', holon, lens)
177
177
  return;
178
178
  }
179
- // Wrap the GunDB operation in a promise
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(hex.toString()).get(lens).once((data, key) => {
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(hex.toString()).get(lens).map().once(async (itemdata, key) => {
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(hex).get(lens).get(key).put(null);
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 hex and lens.
229
- * @param {string} hex - The hex identifier.
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(hex, lens, key) {
234
+ async getKey(holon, lens, key) {
235
235
  return new Promise((resolve) => {
236
236
  // Use Gun to get the data
237
- this.gun.get(hex).get(lens).get(key).once((data, key) => {
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 gundb node from the specified hex and lens.
258
- * @param {string} hex - The hex identifier.
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(hex, lens, key) {
263
+ getNode(holon, lens, key) {
264
264
  // Use Gun to get the data
265
- return this.gun.get(hex).get(lens).get(key)
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
- * Computes summaries based on the content within a hex and lens.
272
- * @param {string} hex - The hex identifier.
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(hex, lens, operation) {
375
+ async compute(holon, lens, operation) {
277
376
 
278
- let res = h3.getResolution(hex);
377
+ let res = h3.getResolution(holon);
279
378
  if (res < 1 || res > 15) return;
280
379
  console.log(res)
281
- let parent = h3.cellToParent(hex, res - 1);
380
+ let parent = h3.cellToParent(holon, res - 1);
282
381
  let siblings = h3.cellToChildren(parent, res);
283
- console.log(hex, parent, siblings, res)
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 hex and lens.
317
- * @param {string} hex - The hex identifier.
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(hex, lens) {
419
+ async clearlens(holon, lens) {
321
420
  let entities = {};
322
421
 
323
422
  // Get list out of Gun
324
- this.gun.get(hex).get(lens).map().once((data, key) => {
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(hex).get(lens).put({ [key]: null })
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 hexagons recursively.
371
- * @param {string} hex - The current hex identifier.
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(hex, lens, content) {
377
- let res = h3.getResolution(hex)
475
+ async upcast(holon, lens, content) {
476
+ let res = h3.getResolution(holon)
378
477
  if (res == 0) {
379
- await this.putNode(hex, lens, content)
478
+ await this.putNode(holon, lens, content)
380
479
  return content
381
480
  }
382
481
  else {
383
- console.log('Upcasting ', hex, lens, content, res)
384
- await this.putNode(hex, lens, content)
385
- let parent = h3.cellToParent(hex, res - 1)
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 hexagon with a new report.
393
- * @param {string} id - The child hex identifier.
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 hex identifier.
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 hex identifier.
516
+ * @returns {Promise<string>} - The resulting holon identifier.
418
517
  */
419
- async getHex(lat, lng, resolution) {
518
+ async getHolon(lat, lng, resolution) {
420
519
  return h3.latLngToCell(lat, lng, resolution);
421
520
  }
422
521
 
423
522
  /**
424
- * Retrieves all containing hexagons at all scales for given coordinates.
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 hex identifiers.
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 hexagons at all scales for a given hex.
441
- * @param {string} hex - The hex identifier.
442
- * @returns {Array<string>} - List of hex identifiers.
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
- getHexScalespace(hex) {
543
+ getHolonScalespace(holon) {
445
544
  let list = []
446
- let res = h3.getResolution(hex)
545
+ let res = h3.getResolution(holon)
447
546
  for (let i = res; i >= 0; i--) {
448
- list.push(h3.cellToParent(hex, i))
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 hex and lens.
455
- * @param {string} hex - The hex identifier.
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(hex, lens, callback) {
460
- this.gun.get(hex).get(lens).map().on((data, key) => {
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 hex and topic.
492
- * @param {string} hexId - The hex identifier.
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(hexId, topic) {
497
- if (!this.hexagonVotes[hexId] || !this.hexagonVotes[hexId][topic]) {
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.hexagonVotes[hexId][topic];
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 hex.
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} hexId - The hex identifier.
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, hexId, topic, vote) {
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, hexId, topic, vote })
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.7",
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": "echo \"Error: no test specified\" && exit 1"
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
  }
@@ -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('test-app', {
8
- gunPeers: ['http://localhost:8765/gun'],
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
- test('initialization', () => {
13
- expect(holoSphere).toBeDefined();
14
- expect(holoSphere.users).toEqual({});
15
- expect(holoSphere.hexagonVotes).toEqual({});
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
- test('voting system', async () => {
19
- const userId = 'test-user';
20
- const hexId = '8928308280fffff';
21
- const topic = 'test-topic';
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
- await holoSphere.initializeUser(userId);
25
- await holoSphere.vote(userId, hexId, topic, vote);
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
- const votes = holoSphere.aggregateVotes(hexId, topic);
28
- expect(votes).toHaveProperty('yes', 1);
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
- // Add more tests...
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
  });