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/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.10",
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
- await holoSphere.deleteAll(testHolon, testLens);
44
- await holoSphere.deleteAllGlobal('testTable');
45
-
46
- // Close HoloSphere instances
47
- if (holoSphere) {
48
- await holoSphere.close();
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
- if (strictHoloSphere) {
51
- await strictHoloSphere.close();
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
- // Wait for connections to close
55
- await new Promise(resolve => setTimeout(resolve, 1000));
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: 'global', value: 111 };
206
-
207
- await holoSphere.putGlobal('testTable', testData, testPassword);
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: 'public_global', value: 222 };
215
-
216
- await holoSphere.putGlobal('testTable', testData);
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: 'global1', value: 1 },
225
- { id: 'global2', value: 2 }
247
+ { id: 'globalPrivate1', value: 1 },
248
+ { id: 'globalPrivate2', value: 2 }
226
249
  ];
227
250
 
228
- // Clean up any existing data first
229
- await holoSphere.deleteAllGlobal('testTable', testPassword);
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('testTable', data, testPassword);
255
+ await holoSphere.putGlobal(PRIVATE_GLOBAL_TABLE, data, testPassword);
234
256
  }
235
257
 
236
- // Wait a bit for data to settle
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('testTable', testPassword);
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: 'delete_global', value: 333 };
248
-
249
- await holoSphere.putGlobal('testTable', testData, testPassword);
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
  });
@@ -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
- // Verify global data exists
114
- const storedGlobalData = await holoSphere.getGlobal(testGlobalTable, globalData.id);
115
- expect(storedGlobalData).toBeDefined();
116
- console.log(storedGlobalData);
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
- // Verify global data is deleted
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
  });