holosphere 1.1.10 → 1.1.12
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/.cursor/rules/futura.mdc +55 -0
- package/FEDERATION.md +17 -17
- package/compute.js +289 -0
- package/content.js +946 -0
- package/examples/federation.js +98 -90
- package/examples/hologram-updates-example.js +106 -0
- package/examples/{references.js → holograms.js} +49 -51
- package/federation.js +427 -245
- package/global.js +725 -0
- package/hologram.js +156 -0
- package/holosphere.d.ts +109 -7
- package/holosphere.js +172 -1565
- package/node.js +240 -0
- package/package.json +2 -5
- package/schema.js +132 -0
- package/test/auth.test.js +55 -37
- package/test/delete.test.js +15 -12
- package/test/federation.test.js +179 -0
- package/test/hologram-deletion.test.js +197 -0
- package/test/hologram-updates-return.test.js +166 -0
- package/test/hologram-updates.test.js +143 -0
- package/test/hologram.test.js +316 -0
- package/test/meta-strip.test.js +159 -0
- package/test/parent-propagation.test.js +138 -0
- package/test/subscription.test.js +105 -70
- package/utils.js +290 -0
- package/test/reference.test.js +0 -211
package/node.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// holo_node.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Stores a specific gun node in a given holon and lens.
|
|
5
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
6
|
+
* @param {string} holon - The holon identifier.
|
|
7
|
+
* @param {string} lens - The lens under which to store the node.
|
|
8
|
+
* @param {object} data - The node to store.
|
|
9
|
+
*/
|
|
10
|
+
export async function putNode(holoInstance, holon, lens, data) {
|
|
11
|
+
if (!holon || !lens || !data) {
|
|
12
|
+
throw new Error('putNode: Missing required parameters');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
try {
|
|
17
|
+
// Remove isHologram field before storing - NO LONGER NEEDED
|
|
18
|
+
// if (data && data.isHologram !== undefined) {
|
|
19
|
+
// delete data.isHologram;
|
|
20
|
+
// }
|
|
21
|
+
|
|
22
|
+
// Check if the data being stored is a hologram
|
|
23
|
+
const isHologram = data.value && holoInstance.isHologram(data.value);
|
|
24
|
+
|
|
25
|
+
holoInstance.gun.get(holoInstance.appname)
|
|
26
|
+
.get(holon)
|
|
27
|
+
.get(lens)
|
|
28
|
+
.get('value') // Store at 'value' key
|
|
29
|
+
.put(data.value, ack => { // Store the value directly
|
|
30
|
+
if (ack.err) {
|
|
31
|
+
reject(new Error(ack.err));
|
|
32
|
+
} else {
|
|
33
|
+
// --- Start: Hologram Tracking Logic (for data *being put*, if it's a hologram) ---
|
|
34
|
+
if (isHologram) {
|
|
35
|
+
try {
|
|
36
|
+
const storedDataSoulInfo = holoInstance.parseSoulPath(data.value.soul);
|
|
37
|
+
if (storedDataSoulInfo) {
|
|
38
|
+
const targetNodeRef = holoInstance.getNodeRef(data.value.soul); // Target of the data *being put*
|
|
39
|
+
// Soul of the hologram that was *actually stored* at holon/lens/value
|
|
40
|
+
const storedHologramInstanceSoul = `${holoInstance.appname}/${holon}/${lens}/value`;
|
|
41
|
+
|
|
42
|
+
targetNodeRef.get('_holograms').get(storedHologramInstanceSoul).put(true);
|
|
43
|
+
|
|
44
|
+
console.log(`Data (ID: ${data.id}) being put is a hologram. Added its instance soul ${storedHologramInstanceSoul} to its target ${data.value.soul}'s _holograms set.`);
|
|
45
|
+
} else {
|
|
46
|
+
console.warn(`Data (ID: ${data.id}) being put is a hologram, but could not parse its soul ${data.value.soul} for tracking.`);
|
|
47
|
+
}
|
|
48
|
+
} catch (trackingError) {
|
|
49
|
+
console.warn(`Error updating _holograms set for the target of the data being put (data ID: ${data.id}, soul: ${data.value.soul}):`, trackingError);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// --- End: Hologram Tracking Logic ---
|
|
53
|
+
|
|
54
|
+
resolve(true);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
} catch (error) {
|
|
58
|
+
reject(error);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Retrieves a specific gun node from the specified holon and lens.
|
|
65
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
66
|
+
* @param {string} holon - The holon identifier.
|
|
67
|
+
* @param {string} lens - The lens identifier.
|
|
68
|
+
* @param {string} key - The specific key to retrieve.
|
|
69
|
+
* @returns {Promise<any>} - The retrieved node or null if not found.
|
|
70
|
+
*/
|
|
71
|
+
export async function getNode(holoInstance, holon, lens, key) {
|
|
72
|
+
if (!holon || !lens || !key) {
|
|
73
|
+
throw new Error('getNode: Missing required parameters');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
try {
|
|
78
|
+
holoInstance.gun.get(holoInstance.appname)
|
|
79
|
+
.get(holon)
|
|
80
|
+
.get(lens)
|
|
81
|
+
.get(key)
|
|
82
|
+
.once((data) => {
|
|
83
|
+
if (!data) {
|
|
84
|
+
resolve(null);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
resolve(data); // Return the data directly
|
|
88
|
+
});
|
|
89
|
+
} catch (error) {
|
|
90
|
+
reject(error);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Retrieves a Gun node reference using its soul path
|
|
97
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
98
|
+
* @param {string} soul - The soul path of the node
|
|
99
|
+
* @returns {Gun.ChainReference} - The Gun node reference
|
|
100
|
+
*/
|
|
101
|
+
export function getNodeRef(holoInstance, soul) {
|
|
102
|
+
if (typeof soul !== 'string' || !soul) {
|
|
103
|
+
throw new Error('getNodeRef: Invalid soul parameter');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const parts = soul.split('/').filter(part => {
|
|
107
|
+
if (!part.trim() || /[<>:"/\\|?*]/.test(part)) { // Escaped backslash for regex
|
|
108
|
+
throw new Error('getNodeRef: Invalid path segment');
|
|
109
|
+
}
|
|
110
|
+
return part.trim();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
if (parts.length === 0) {
|
|
114
|
+
throw new Error('getNodeRef: Invalid soul format');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let ref = holoInstance.gun.get(holoInstance.appname);
|
|
118
|
+
parts.forEach(part => {
|
|
119
|
+
ref = ref.get(part);
|
|
120
|
+
});
|
|
121
|
+
return ref;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Retrieves a node directly using its soul path
|
|
126
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
127
|
+
* @param {string} soul - The soul path of the node
|
|
128
|
+
* @returns {Promise<any>} - The retrieved node or null if not found.
|
|
129
|
+
*/
|
|
130
|
+
export async function getNodeBySoul(holoInstance, soul) {
|
|
131
|
+
if (!soul) {
|
|
132
|
+
throw new Error('getNodeBySoul: Missing soul parameter');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
console.log(`getNodeBySoul: Accessing soul ${soul}`);
|
|
136
|
+
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
try {
|
|
139
|
+
const ref = getNodeRef(holoInstance, soul); // Use the exported getNodeRef
|
|
140
|
+
ref.once((data) => {
|
|
141
|
+
console.log(`getNodeBySoul: Retrieved data:`, data);
|
|
142
|
+
if (!data) {
|
|
143
|
+
resolve(null);
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
resolve(data); // Return the data directly
|
|
147
|
+
});
|
|
148
|
+
} catch (error) {
|
|
149
|
+
console.error(`getNodeBySoul error:`, error);
|
|
150
|
+
reject(error);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Deletes a specific gun node from a given holon and lens.
|
|
157
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
158
|
+
* @param {string} holon - The holon identifier.
|
|
159
|
+
* @param {string} lens - The lens identifier.
|
|
160
|
+
* @param {string} key - The key of the node to delete.
|
|
161
|
+
* @returns {Promise<boolean>} - Returns true if successful
|
|
162
|
+
*/
|
|
163
|
+
export async function deleteNode(holoInstance, holon, lens, key) {
|
|
164
|
+
if (!holon || !lens || !key) {
|
|
165
|
+
throw new Error('deleteNode: Missing required parameters');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const dataPath = holoInstance.gun.get(holoInstance.appname).get(holon).get(lens).get(key);
|
|
170
|
+
|
|
171
|
+
// --- Start: Hologram Tracking Removal ---
|
|
172
|
+
let trackingRemovalPromise = Promise.resolve(); // Default to resolved promise
|
|
173
|
+
|
|
174
|
+
// 1. Get the data first to check if it's a hologram
|
|
175
|
+
const rawDataToDelete = await new Promise((resolve) => dataPath.once(resolve));
|
|
176
|
+
let dataToDelete = null;
|
|
177
|
+
try {
|
|
178
|
+
if (typeof rawDataToDelete === 'string') {
|
|
179
|
+
dataToDelete = JSON.parse(rawDataToDelete);
|
|
180
|
+
} else {
|
|
181
|
+
// Handle cases where it might already be an object (though likely string)
|
|
182
|
+
dataToDelete = rawDataToDelete;
|
|
183
|
+
}
|
|
184
|
+
} catch(e) {
|
|
185
|
+
console.warn("[deleteNode] Could not JSON parse data for deletion check:", rawDataToDelete, e);
|
|
186
|
+
dataToDelete = null; // Ensure it's null if parsing fails
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// 2. If it is a hologram, try to remove its reference from the target
|
|
190
|
+
const isDataHologram = dataToDelete && holoInstance.isHologram(dataToDelete);
|
|
191
|
+
|
|
192
|
+
if (isDataHologram) {
|
|
193
|
+
try {
|
|
194
|
+
const targetSoul = dataToDelete.soul;
|
|
195
|
+
const targetSoulInfo = holoInstance.parseSoulPath(targetSoul);
|
|
196
|
+
|
|
197
|
+
if (targetSoulInfo) {
|
|
198
|
+
const targetNodeRef = holoInstance.getNodeRef(targetSoul);
|
|
199
|
+
// putNode stores at the 'value' key, not at the data.id key
|
|
200
|
+
const deletedHologramSoul = `${holoInstance.appname}/${holon}/${lens}/value`;
|
|
201
|
+
|
|
202
|
+
// Create a promise that resolves when the hologram is removed from the list
|
|
203
|
+
trackingRemovalPromise = new Promise((resolveTrack) => { // No reject needed, just warn on error
|
|
204
|
+
targetNodeRef.get('_holograms').get(deletedHologramSoul).put(null, (ack) => { // Remove the hologram entry completely
|
|
205
|
+
if (ack.err) {
|
|
206
|
+
console.warn(`[deleteNode] Error removing hologram ${deletedHologramSoul} from target ${targetSoul}:`, ack.err);
|
|
207
|
+
}
|
|
208
|
+
console.log(`Removed hologram ${deletedHologramSoul} from target ${targetSoul}'s _holograms list`);
|
|
209
|
+
resolveTrack(); // Resolve regardless of ack error to not block main delete
|
|
210
|
+
});
|
|
211
|
+
});
|
|
212
|
+
} else {
|
|
213
|
+
console.warn(`Could not parse target soul ${targetSoul} for hologram tracking removal during deleteNode.`);
|
|
214
|
+
}
|
|
215
|
+
} catch (trackingError) {
|
|
216
|
+
console.warn(`Error initiating hologram reference removal from target ${dataToDelete.soul} during deleteNode:`, trackingError);
|
|
217
|
+
// Ensure trackingRemovalPromise remains resolved if setup fails
|
|
218
|
+
trackingRemovalPromise = Promise.resolve();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// --- End: Hologram Tracking Removal ---
|
|
222
|
+
|
|
223
|
+
// 3. Wait for the tracking removal attempt to be acknowledged
|
|
224
|
+
await trackingRemovalPromise;
|
|
225
|
+
|
|
226
|
+
// 4. Proceed with the actual deletion of the hologram node itself
|
|
227
|
+
return new Promise((resolve, reject) => {
|
|
228
|
+
dataPath.put(null, ack => {
|
|
229
|
+
if (ack.err) {
|
|
230
|
+
reject(new Error(ack.err));
|
|
231
|
+
} else {
|
|
232
|
+
resolve(true);
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
} catch (error) {
|
|
237
|
+
console.error('Error in deleteNode:', error);
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "holosphere",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.12",
|
|
4
4
|
"description": "Holonic Geospatial Communication Infrastructure",
|
|
5
5
|
"main": "holosphere.js",
|
|
6
6
|
"types": "holosphere.d.ts",
|
|
@@ -24,9 +24,6 @@
|
|
|
24
24
|
},
|
|
25
25
|
"jest": {
|
|
26
26
|
"testEnvironment": "node",
|
|
27
|
-
"transform": {}
|
|
28
|
-
"moduleNameMapper": {
|
|
29
|
-
"^(\\.{1,2}/.*)\\.js$": "$1"
|
|
30
|
-
}
|
|
27
|
+
"transform": {}
|
|
31
28
|
}
|
|
32
29
|
}
|
package/schema.js
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
// holo_schema.js
|
|
2
|
+
|
|
3
|
+
import Ajv2019 from 'ajv/dist/2019.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Sets the JSON schema for a specific lens.
|
|
7
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
8
|
+
* @param {string} lens - The lens identifier.
|
|
9
|
+
* @param {object} schema - The JSON schema to set.
|
|
10
|
+
* @returns {Promise<boolean>} - Resolves when the schema is set.
|
|
11
|
+
*/
|
|
12
|
+
export async function setSchema(holoInstance, lens, schema) {
|
|
13
|
+
if (!lens || !schema) {
|
|
14
|
+
throw new Error('setSchema: Missing required parameters');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Basic schema validation
|
|
18
|
+
if (!schema.type || typeof schema.type !== 'string') {
|
|
19
|
+
throw new Error('setSchema: Schema must have a type field');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const metaSchema = {
|
|
23
|
+
type: 'object',
|
|
24
|
+
required: ['type', 'properties'],
|
|
25
|
+
properties: {
|
|
26
|
+
type: { type: 'string' },
|
|
27
|
+
properties: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
additionalProperties: {
|
|
30
|
+
type: 'object',
|
|
31
|
+
required: ['type'],
|
|
32
|
+
properties: {
|
|
33
|
+
type: { type: 'string' }
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
required: {
|
|
38
|
+
type: 'array',
|
|
39
|
+
items: { type: 'string' }
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Use the validator from the instance
|
|
45
|
+
const valid = holoInstance.validator.validate(metaSchema, schema);
|
|
46
|
+
if (!valid) {
|
|
47
|
+
throw new Error(`Invalid schema structure: ${JSON.stringify(holoInstance.validator.errors)}`);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (!schema.properties || typeof schema.properties !== 'object') {
|
|
51
|
+
throw new Error('Schema must have properties in strict mode');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (!schema.required || !Array.isArray(schema.required) || schema.required.length === 0) {
|
|
55
|
+
throw new Error('Schema must have required fields in strict mode');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Store schema in global table with lens as key using instance's method
|
|
59
|
+
await holoInstance.putGlobal('schemas', {
|
|
60
|
+
id: lens,
|
|
61
|
+
schema: schema,
|
|
62
|
+
timestamp: Date.now()
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Update the instance's cache with the new schema
|
|
66
|
+
holoInstance.schemaCache.set(lens, {
|
|
67
|
+
schema,
|
|
68
|
+
timestamp: Date.now()
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Retrieves the JSON schema for a specific lens.
|
|
76
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
77
|
+
* @param {string} lens - The lens identifier.
|
|
78
|
+
* @param {object} [options] - Additional options
|
|
79
|
+
* @param {boolean} [options.useCache=true] - Whether to use the schema cache
|
|
80
|
+
* @param {number} [options.maxCacheAge=3600000] - Maximum cache age in milliseconds (default: 1 hour)
|
|
81
|
+
* @returns {Promise<object|null>} - The retrieved schema or null if not found.
|
|
82
|
+
*/
|
|
83
|
+
export async function getSchema(holoInstance, lens, options = {}) {
|
|
84
|
+
if (!lens) {
|
|
85
|
+
throw new Error('getSchema: Missing lens parameter');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const { useCache = true, maxCacheAge = 3600000 } = options;
|
|
89
|
+
|
|
90
|
+
// Check instance's cache first if enabled
|
|
91
|
+
if (useCache && holoInstance.schemaCache.has(lens)) {
|
|
92
|
+
const cached = holoInstance.schemaCache.get(lens);
|
|
93
|
+
const cacheAge = Date.now() - cached.timestamp;
|
|
94
|
+
|
|
95
|
+
// Use cache if it's fresh enough
|
|
96
|
+
if (cacheAge < maxCacheAge) {
|
|
97
|
+
return cached.schema;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Cache miss or expired, fetch from storage using instance's method
|
|
102
|
+
const schemaData = await holoInstance.getGlobal('schemas', lens);
|
|
103
|
+
|
|
104
|
+
if (!schemaData || !schemaData.schema) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Update instance's cache with fetched schema
|
|
109
|
+
holoInstance.schemaCache.set(lens, {
|
|
110
|
+
schema: schemaData.schema,
|
|
111
|
+
timestamp: Date.now()
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return schemaData.schema;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Clears the schema cache or a specific schema from the cache.
|
|
119
|
+
* @param {HoloSphere} holoInstance - The HoloSphere instance.
|
|
120
|
+
* @param {string} [lens] - Optional lens to clear from cache. If not provided, clears entire cache.
|
|
121
|
+
* @returns {boolean} - Returns true if successful
|
|
122
|
+
*/
|
|
123
|
+
export function clearSchemaCache(holoInstance, lens = null) {
|
|
124
|
+
if (lens) {
|
|
125
|
+
// Clear specific schema from instance's cache
|
|
126
|
+
return holoInstance.schemaCache.delete(lens);
|
|
127
|
+
} else {
|
|
128
|
+
// Clear entire instance's cache
|
|
129
|
+
holoInstance.schemaCache.clear();
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
}
|
package/test/auth.test.js
CHANGED
|
@@ -10,6 +10,8 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
10
10
|
const testPassword = 'TestPass123!';
|
|
11
11
|
const testHolon = 'test-holon';
|
|
12
12
|
const testLens = 'test-lens';
|
|
13
|
+
const PUBLIC_GLOBAL_TABLE = 'publicTestTable'; // For public global data
|
|
14
|
+
const PRIVATE_GLOBAL_TABLE = 'veryPrivateGlobalTable'; // For all private global data tests
|
|
13
15
|
|
|
14
16
|
beforeAll(async () => {
|
|
15
17
|
holoSphere = new HoloSphere('test-app', false, null);
|
|
@@ -21,6 +23,8 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
21
23
|
beforeEach(async () => {
|
|
22
24
|
// Clean state before each test
|
|
23
25
|
await holoSphere.deleteAll(testHolon, testLens);
|
|
26
|
+
await holoSphere.deleteAllGlobal(PUBLIC_GLOBAL_TABLE);
|
|
27
|
+
await holoSphere.deleteAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword);
|
|
24
28
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
25
29
|
});
|
|
26
30
|
|
|
@@ -40,19 +44,41 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
40
44
|
|
|
41
45
|
afterAll(async () => {
|
|
42
46
|
// Clean up all test data
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
try {
|
|
48
|
+
if (holoSphere) {
|
|
49
|
+
await holoSphere.deleteAll(testHolon, testLens);
|
|
50
|
+
await holoSphere.deleteAllGlobal(PUBLIC_GLOBAL_TABLE);
|
|
51
|
+
await holoSphere.deleteAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword);
|
|
52
|
+
}
|
|
53
|
+
if (strictHoloSphere) {
|
|
54
|
+
await strictHoloSphere.deleteAll(testHolon, testLens);
|
|
55
|
+
await strictHoloSphere.deleteAllGlobal(PUBLIC_GLOBAL_TABLE);
|
|
56
|
+
await strictHoloSphere.deleteAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error('Error during afterAll data cleanup:', error);
|
|
49
60
|
}
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
|
|
62
|
+
// Close HoloSphere instances
|
|
63
|
+
try {
|
|
64
|
+
if (holoSphere) {
|
|
65
|
+
console.log('Closing non-strict HoloSphere instance...');
|
|
66
|
+
await holoSphere.close();
|
|
67
|
+
console.log('Non-strict HoloSphere instance closed.');
|
|
68
|
+
}
|
|
69
|
+
if (strictHoloSphere) {
|
|
70
|
+
console.log('Closing strict HoloSphere instance...');
|
|
71
|
+
await strictHoloSphere.close();
|
|
72
|
+
console.log('Strict HoloSphere instance closed.');
|
|
73
|
+
}
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error('Error during afterAll close:', error);
|
|
52
76
|
}
|
|
53
|
-
|
|
54
|
-
//
|
|
55
|
-
|
|
77
|
+
|
|
78
|
+
// Add a slightly longer, more explicit wait after close calls
|
|
79
|
+
console.log('Waiting extra time for cleanup...');
|
|
80
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
81
|
+
console.log('Finished afterAll.');
|
|
56
82
|
});
|
|
57
83
|
|
|
58
84
|
describe('Authentication System', () => {
|
|
@@ -81,6 +107,7 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
81
107
|
const testData = { id: 'test2', value: 'private-data' };
|
|
82
108
|
|
|
83
109
|
// Store data with correct password
|
|
110
|
+
await new Promise(resolve => setTimeout(resolve, 100)); // Added delay
|
|
84
111
|
await holoSphere.put(testHolon, testLens, testData, testPassword);
|
|
85
112
|
|
|
86
113
|
// Try to retrieve with wrong password
|
|
@@ -202,41 +229,35 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
202
229
|
|
|
203
230
|
describe('Global Data Operations', () => {
|
|
204
231
|
it('should handle private global data', async () => {
|
|
205
|
-
const testData = { id: '
|
|
206
|
-
|
|
207
|
-
await holoSphere.
|
|
208
|
-
|
|
209
|
-
const result = await holoSphere.getGlobal('testTable', testData.id, testPassword);
|
|
232
|
+
const testData = { id: 'globalPrivateItem', value: 111 };
|
|
233
|
+
await holoSphere.putGlobal(PRIVATE_GLOBAL_TABLE, testData, testPassword);
|
|
234
|
+
const result = await holoSphere.getGlobal(PRIVATE_GLOBAL_TABLE, testData.id, testPassword);
|
|
210
235
|
expect(result).toEqual(testData);
|
|
211
236
|
});
|
|
212
237
|
|
|
213
238
|
it('should handle public global data', async () => {
|
|
214
|
-
const testData = { id: '
|
|
215
|
-
|
|
216
|
-
await holoSphere.
|
|
217
|
-
|
|
218
|
-
const result = await holoSphere.getGlobal('testTable', testData.id);
|
|
239
|
+
const testData = { id: 'publicGlobalItem', value: 222 };
|
|
240
|
+
await holoSphere.putGlobal(PUBLIC_GLOBAL_TABLE, testData);
|
|
241
|
+
const result = await holoSphere.getGlobal(PUBLIC_GLOBAL_TABLE, testData.id);
|
|
219
242
|
expect(result).toEqual(testData);
|
|
220
243
|
});
|
|
221
244
|
|
|
222
245
|
it('should handle getAllGlobal with private data', async () => {
|
|
223
246
|
const testData = [
|
|
224
|
-
{ id: '
|
|
225
|
-
{ id: '
|
|
247
|
+
{ id: 'globalPrivate1', value: 1 },
|
|
248
|
+
{ id: 'globalPrivate2', value: 2 }
|
|
226
249
|
];
|
|
227
250
|
|
|
228
|
-
// Clean up
|
|
229
|
-
await holoSphere.deleteAllGlobal(
|
|
251
|
+
// Clean up before this specific test (already done in beforeEach, but good for clarity)
|
|
252
|
+
// await holoSphere.deleteAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword); // This might be re-enabled later if tests pass without it
|
|
230
253
|
|
|
231
|
-
// Store each item
|
|
232
254
|
for (const data of testData) {
|
|
233
|
-
await holoSphere.putGlobal(
|
|
255
|
+
await holoSphere.putGlobal(PRIVATE_GLOBAL_TABLE, data, testPassword);
|
|
234
256
|
}
|
|
235
257
|
|
|
236
|
-
|
|
237
|
-
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
258
|
+
await new Promise(resolve => setTimeout(resolve, 1000)); // Settle time
|
|
238
259
|
|
|
239
|
-
const results = await holoSphere.getAllGlobal(
|
|
260
|
+
const results = await holoSphere.getAllGlobal(PRIVATE_GLOBAL_TABLE, testPassword);
|
|
240
261
|
expect(results.length).toBe(testData.length);
|
|
241
262
|
for (const data of testData) {
|
|
242
263
|
expect(results).toContainEqual(expect.objectContaining(data));
|
|
@@ -244,13 +265,10 @@ describe('HoloSphere Authentication and Authorization', () => {
|
|
|
244
265
|
}, 10000);
|
|
245
266
|
|
|
246
267
|
it('should handle deleteGlobal with private data', async () => {
|
|
247
|
-
const testData = { id: '
|
|
248
|
-
|
|
249
|
-
await holoSphere.
|
|
250
|
-
|
|
251
|
-
await holoSphere.deleteGlobal('testTable', testData.id, testPassword);
|
|
252
|
-
|
|
253
|
-
const result = await holoSphere.getGlobal('testTable', testData.id, testPassword);
|
|
268
|
+
const testData = { id: 'deleteGlobalPrivate', value: 333 };
|
|
269
|
+
await holoSphere.putGlobal(PRIVATE_GLOBAL_TABLE, testData, testPassword);
|
|
270
|
+
await holoSphere.deleteGlobal(PRIVATE_GLOBAL_TABLE, testData.id, testPassword);
|
|
271
|
+
const result = await holoSphere.getGlobal(PRIVATE_GLOBAL_TABLE, testData.id, testPassword);
|
|
254
272
|
expect(result).toBeNull();
|
|
255
273
|
});
|
|
256
274
|
});
|
package/test/delete.test.js
CHANGED
|
@@ -4,6 +4,9 @@ import { jest } from '@jest/globals';
|
|
|
4
4
|
// Configure Jest
|
|
5
5
|
jest.setTimeout(30000); // 30 second timeout
|
|
6
6
|
|
|
7
|
+
// Utility to wait for GunDB propagation
|
|
8
|
+
const waitForGun = (delay = 250) => new Promise(resolve => setTimeout(resolve, delay));
|
|
9
|
+
|
|
7
10
|
describe('HoloSphere Deletion Tests', () => {
|
|
8
11
|
const testAppName = 'test-app-deletion';
|
|
9
12
|
const testHolon = 'testHolonDeletion';
|
|
@@ -106,21 +109,21 @@ describe('HoloSphere Deletion Tests', () => {
|
|
|
106
109
|
test('should delete global items properly', async () => {
|
|
107
110
|
// Create global test data
|
|
108
111
|
const globalData = { id: 'global-delete-test', value: 'global delete me' };
|
|
109
|
-
|
|
110
|
-
// Store global data
|
|
112
|
+
|
|
113
|
+
// 1. Store global data
|
|
111
114
|
await holoSphere.putGlobal(testGlobalTable, globalData);
|
|
112
|
-
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
expect(storedGlobalData.value).toBe(globalData.value);
|
|
118
|
-
|
|
119
|
-
// Delete global data
|
|
115
|
+
|
|
116
|
+
// 2. Wait significantly for put to settle
|
|
117
|
+
await waitForGun(1500); // Generous wait after put
|
|
118
|
+
|
|
119
|
+
// 3. Delete global data
|
|
120
120
|
const deleteResult = await holoSphere.deleteGlobal(testGlobalTable, globalData.id);
|
|
121
121
|
expect(deleteResult).toBe(true);
|
|
122
|
-
|
|
123
|
-
//
|
|
122
|
+
|
|
123
|
+
// 4. Wait for delete to settle
|
|
124
|
+
await waitForGun(500); // Wait after delete
|
|
125
|
+
|
|
126
|
+
// 5. Verify global data is deleted
|
|
124
127
|
const deletedGlobalData = await holoSphere.getGlobal(testGlobalTable, globalData.id);
|
|
125
128
|
expect(deletedGlobalData).toBeNull();
|
|
126
129
|
});
|