holosphere 1.1.19 → 1.1.21
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 +2 -173
- package/content.js +93 -119
- package/global.js +68 -64
- package/holosphere-bundle.esm.js +11609 -12218
- package/holosphere-bundle.js +11609 -12218
- package/holosphere-bundle.min.js +22 -32
- package/holosphere.d.ts +16 -19
- package/holosphere.js +17 -36
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -136,135 +136,21 @@ npm install holosphere
|
|
|
136
136
|
|
|
137
137
|
## Quick Start
|
|
138
138
|
|
|
139
|
-
### Node.js Usage
|
|
140
|
-
|
|
141
139
|
```javascript
|
|
142
140
|
import HoloSphere from 'holosphere';
|
|
143
141
|
|
|
144
|
-
// Initialize HoloSphere
|
|
142
|
+
// Initialize HoloSphere
|
|
145
143
|
const sphere = new HoloSphere('my-app');
|
|
146
144
|
|
|
147
|
-
//
|
|
148
|
-
sphere.configureRadisk({
|
|
149
|
-
file: './my-data', // Custom storage directory
|
|
150
|
-
radisk: true, // Enable disk persistence
|
|
151
|
-
retry: 3, // Retry failed operations
|
|
152
|
-
timeout: 5000 // Operation timeout
|
|
153
|
-
});
|
|
154
|
-
|
|
155
|
-
// Store data at a location (persisted to disk)
|
|
145
|
+
// Store data at a location
|
|
156
146
|
const holon = await sphere.getHolon(40.7128, -74.0060, 7); // NYC at resolution 7
|
|
157
147
|
await sphere.put(holon, 'observations', {
|
|
158
148
|
id: 'obs-001',
|
|
159
149
|
temperature: 22.5,
|
|
160
150
|
timestamp: Date.now()
|
|
161
151
|
});
|
|
162
|
-
|
|
163
|
-
// Retrieve data (from memory or disk)
|
|
164
|
-
const data = await sphere.get(holon, 'observations', 'obs-001');
|
|
165
|
-
console.log(data);
|
|
166
|
-
|
|
167
|
-
// Get radisk statistics
|
|
168
|
-
const stats = sphere.getRadiskStats();
|
|
169
|
-
console.log('Storage stats:', stats);
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### Browser Usage
|
|
173
|
-
|
|
174
|
-
HoloSphere works seamlessly in browsers with radisk persistence. The bundle automatically includes radisk support:
|
|
175
|
-
|
|
176
|
-
```html
|
|
177
|
-
<!DOCTYPE html>
|
|
178
|
-
<html>
|
|
179
|
-
<head>
|
|
180
|
-
<title>HoloSphere Browser Example</title>
|
|
181
|
-
</head>
|
|
182
|
-
<body>
|
|
183
|
-
<!-- Load HoloSphere Bundle -->
|
|
184
|
-
<script src="https://unpkg.com/holosphere@1.1.18/holosphere-bundle.js"></script>
|
|
185
|
-
|
|
186
|
-
<script>
|
|
187
|
-
// Radisk is enabled by default in browser
|
|
188
|
-
const sphere = new HoloSphere('browser-app');
|
|
189
|
-
|
|
190
|
-
// Store data (persisted via IndexedDB)
|
|
191
|
-
async function storeData() {
|
|
192
|
-
const holon = await sphere.getHolon(40.7128, -74.0060, 7);
|
|
193
|
-
await sphere.put(holon, 'observations', {
|
|
194
|
-
id: 'browser-obs-001',
|
|
195
|
-
temperature: 22.5,
|
|
196
|
-
timestamp: Date.now()
|
|
197
|
-
});
|
|
198
|
-
console.log('Data stored and persisted!');
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Retrieve data (from IndexedDB)
|
|
202
|
-
async function retrieveData() {
|
|
203
|
-
const holon = await sphere.getHolon(40.7128, -74.0060, 7);
|
|
204
|
-
const data = await sphere.get(holon, 'observations', 'browser-obs-001');
|
|
205
|
-
console.log('Retrieved data:', data);
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Check radisk status
|
|
209
|
-
const stats = sphere.getRadiskStats();
|
|
210
|
-
console.log('Radisk stats:', stats);
|
|
211
|
-
</script>
|
|
212
|
-
</body>
|
|
213
|
-
</html>
|
|
214
152
|
```
|
|
215
153
|
|
|
216
|
-
**Key Browser Features:**
|
|
217
|
-
- ✅ Radisk automatically included in bundle
|
|
218
|
-
- ✅ Data persists across page reloads via IndexedDB
|
|
219
|
-
- ✅ Works offline with local storage
|
|
220
|
-
- ✅ No additional configuration needed
|
|
221
|
-
- ✅ Compatible with all modern browsers
|
|
222
|
-
|
|
223
|
-
See `examples/radisk-browser-example.html` for a complete working example.
|
|
224
|
-
|
|
225
|
-
### Svelte/SvelteKit Usage
|
|
226
|
-
|
|
227
|
-
HoloSphere works seamlessly with Svelte and SvelteKit applications:
|
|
228
|
-
|
|
229
|
-
```svelte
|
|
230
|
-
<script>
|
|
231
|
-
import { onMount } from 'svelte';
|
|
232
|
-
import { browser } from '$app/environment'; // SvelteKit only
|
|
233
|
-
import HoloSphere from 'holosphere';
|
|
234
|
-
|
|
235
|
-
let hs = null;
|
|
236
|
-
|
|
237
|
-
async function initHoloSphere() {
|
|
238
|
-
// Client-side only for SvelteKit
|
|
239
|
-
if (typeof browser !== 'undefined' && !browser) return;
|
|
240
|
-
|
|
241
|
-
hs = new HoloSphere('my-app', false, null, {
|
|
242
|
-
radisk: true,
|
|
243
|
-
file: './app-radata'
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
onMount(() => {
|
|
248
|
-
initHoloSphere();
|
|
249
|
-
});
|
|
250
|
-
</script>
|
|
251
|
-
|
|
252
|
-
<main>
|
|
253
|
-
<button on:click={() => storeData()} disabled={!hs}>
|
|
254
|
-
Store Data
|
|
255
|
-
</button>
|
|
256
|
-
</main>
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
**Key Svelte Features:**
|
|
260
|
-
- ✅ Full Svelte and SvelteKit compatibility
|
|
261
|
-
- ✅ SSR-safe initialization
|
|
262
|
-
- ✅ Reactive data binding
|
|
263
|
-
- ✅ Component lifecycle management
|
|
264
|
-
- ✅ Persistent storage across navigation
|
|
265
|
-
|
|
266
|
-
See `examples/svelte-holosphere-example.svelte` and `examples/sveltekit-holosphere-example/+page.svelte` for complete examples.
|
|
267
|
-
|
|
268
154
|
## Real-World Examples
|
|
269
155
|
|
|
270
156
|
### Environmental Monitoring System
|
|
@@ -558,63 +444,6 @@ await holosphere.updateFederatedMessages('chat1', 'msg1', async (chatId, message
|
|
|
558
444
|
});
|
|
559
445
|
```
|
|
560
446
|
|
|
561
|
-
## Radisk Storage Configuration
|
|
562
|
-
|
|
563
|
-
HoloSphere uses GunDB's radisk module for persistent storage. Data is automatically saved to disk and can survive application restarts.
|
|
564
|
-
|
|
565
|
-
### Basic Configuration
|
|
566
|
-
|
|
567
|
-
```javascript
|
|
568
|
-
// Configure radisk with custom options
|
|
569
|
-
sphere.configureRadisk({
|
|
570
|
-
file: './my-data', // Storage directory
|
|
571
|
-
radisk: true, // Enable disk persistence
|
|
572
|
-
retry: 3, // Retry failed operations
|
|
573
|
-
timeout: 5000 // Operation timeout in ms
|
|
574
|
-
});
|
|
575
|
-
```
|
|
576
|
-
|
|
577
|
-
### Get Storage Statistics
|
|
578
|
-
|
|
579
|
-
```javascript
|
|
580
|
-
const stats = sphere.getRadiskStats();
|
|
581
|
-
console.log(stats);
|
|
582
|
-
// Output:
|
|
583
|
-
// {
|
|
584
|
-
// enabled: true,
|
|
585
|
-
// filePath: './my-data',
|
|
586
|
-
// retry: 3,
|
|
587
|
-
// timeout: 5000,
|
|
588
|
-
// until: null,
|
|
589
|
-
// peers: ['https://gun.holons.io/gun'],
|
|
590
|
-
// localStorage: false
|
|
591
|
-
// }
|
|
592
|
-
```
|
|
593
|
-
|
|
594
|
-
### Radisk Options
|
|
595
|
-
|
|
596
|
-
- `file`: Directory for storing data files (default: `'./radata'`)
|
|
597
|
-
- `radisk`: Enable/disable disk persistence (default: `true`)
|
|
598
|
-
- `retry`: Number of retries for failed operations (default: `3`)
|
|
599
|
-
- `timeout`: Operation timeout in milliseconds (default: `5000`)
|
|
600
|
-
- `until`: Timestamp until which to keep data (optional)
|
|
601
|
-
|
|
602
|
-
### Data Persistence
|
|
603
|
-
|
|
604
|
-
All data stored via HoloSphere is automatically persisted to disk:
|
|
605
|
-
|
|
606
|
-
```javascript
|
|
607
|
-
// This data will be saved to disk
|
|
608
|
-
await sphere.put(holon, 'environment', {
|
|
609
|
-
id: 'reading-001',
|
|
610
|
-
temperature: 22.5,
|
|
611
|
-
timestamp: Date.now()
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
// Data persists across application restarts
|
|
615
|
-
const data = await sphere.get(holon, 'environment', 'reading-001');
|
|
616
|
-
```
|
|
617
|
-
|
|
618
447
|
## Soul References
|
|
619
448
|
|
|
620
449
|
When using the default `useReferences: true` with propagation:
|
package/content.js
CHANGED
|
@@ -151,10 +151,7 @@ export async function put(holoInstance, holon, lens, data, password = null, opti
|
|
|
151
151
|
|
|
152
152
|
const putCallback = async (ack) => {
|
|
153
153
|
if (ack.err) {
|
|
154
|
-
|
|
155
|
-
// If it's an object, convert it to JSON string for better error reporting
|
|
156
|
-
const errorMessage = typeof ack.err === 'object' ? JSON.stringify(ack.err) : ack.err;
|
|
157
|
-
reject(new Error(errorMessage));
|
|
154
|
+
reject(new Error(ack.err));
|
|
158
155
|
} else {
|
|
159
156
|
// --- Start: Hologram Tracking Logic (for data *being put*, if it's a hologram) ---
|
|
160
157
|
if (isHologram) {
|
|
@@ -511,94 +508,110 @@ export async function getAll(holoInstance, holon, lens, password = null) {
|
|
|
511
508
|
|
|
512
509
|
return new Promise((resolve) => {
|
|
513
510
|
const output = new Map();
|
|
511
|
+
const pendingProcessing = [];
|
|
514
512
|
|
|
515
|
-
const
|
|
516
|
-
|
|
513
|
+
const dataPath = password ?
|
|
514
|
+
user.get('private').get(lens) :
|
|
515
|
+
holoInstance.gun.get(holoInstance.appname).get(holon).get(lens);
|
|
517
516
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
517
|
+
// PASS 1: Get shallow node to determine expected item count
|
|
518
|
+
dataPath.once((data) => {
|
|
519
|
+
if (!data) {
|
|
520
|
+
resolve([]);
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
521
523
|
|
|
522
|
-
|
|
523
|
-
|
|
524
|
+
// Filter out Gun metadata, null/deleted entries, and Gun soul references
|
|
525
|
+
const keys = Object.keys(data).filter(key => {
|
|
526
|
+
if (key === '_') return false;
|
|
527
|
+
const val = data[key];
|
|
528
|
+
if (val === null) return false;
|
|
529
|
+
// Filter out Gun graph references (sub-nodes)
|
|
530
|
+
if (typeof val === 'object' && val['#']) return false;
|
|
531
|
+
return true;
|
|
532
|
+
});
|
|
533
|
+
const expectedCount = keys.length;
|
|
534
|
+
|
|
535
|
+
if (expectedCount === 0) {
|
|
536
|
+
resolve([]);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// PASS 2: Use map().once() to iterate and get full item data
|
|
541
|
+
let receivedCount = 0;
|
|
542
|
+
|
|
543
|
+
dataPath.map().once(async (itemData, key) => {
|
|
544
|
+
if (!itemData || key === '_') {
|
|
545
|
+
receivedCount++;
|
|
546
|
+
if (receivedCount >= expectedCount) {
|
|
547
|
+
await Promise.all(pendingProcessing);
|
|
548
|
+
resolve(Array.from(output.values()));
|
|
549
|
+
}
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
const processingPromise = (async () => {
|
|
524
554
|
try {
|
|
525
|
-
const
|
|
526
|
-
|
|
527
|
-
maxDepth: 10,
|
|
528
|
-
currentDepth: 0
|
|
529
|
-
});
|
|
555
|
+
const parsed = await holoInstance.parse(itemData);
|
|
556
|
+
if (!parsed || !parsed.id) return;
|
|
530
557
|
|
|
531
|
-
if (
|
|
532
|
-
console.warn(`Broken hologram detected in getAll for key ${key}. Removing it...`);
|
|
533
|
-
|
|
558
|
+
if (holoInstance.isHologram(parsed)) {
|
|
534
559
|
try {
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
}
|
|
541
|
-
return; // Skip adding this item to output
|
|
542
|
-
}
|
|
560
|
+
const resolved = await holoInstance.resolveHologram(parsed, {
|
|
561
|
+
followHolograms: true,
|
|
562
|
+
maxDepth: 10,
|
|
563
|
+
currentDepth: 0
|
|
564
|
+
});
|
|
543
565
|
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
566
|
+
if (resolved === null) {
|
|
567
|
+
console.warn(`Broken hologram detected in getAll for key ${key}. Removing it...`);
|
|
568
|
+
try {
|
|
569
|
+
await holoInstance.delete(holon, lens, key, password);
|
|
570
|
+
} catch (cleanupError) {
|
|
571
|
+
console.error(`Failed to remove broken hologram at ${holon}/${lens}/${key}:`, cleanupError);
|
|
572
|
+
}
|
|
573
|
+
return;
|
|
550
574
|
}
|
|
551
|
-
|
|
552
|
-
|
|
575
|
+
|
|
576
|
+
if (resolved && resolved !== parsed) {
|
|
577
|
+
if (schema) {
|
|
578
|
+
const valid = holoInstance.validator.validate(schema, resolved);
|
|
579
|
+
if (valid || !holoInstance.strict) {
|
|
580
|
+
output.set(resolved.id, resolved);
|
|
581
|
+
}
|
|
582
|
+
} else {
|
|
583
|
+
output.set(resolved.id, resolved);
|
|
584
|
+
}
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
} catch (hologramError) {
|
|
588
|
+
console.error(`Error resolving hologram for key ${key}:`, hologramError);
|
|
589
|
+
return;
|
|
553
590
|
}
|
|
554
|
-
return;
|
|
555
591
|
}
|
|
556
|
-
} catch (hologramError) {
|
|
557
|
-
console.error(`Error resolving hologram for key ${key}:`, hologramError);
|
|
558
|
-
return; // Skip this item
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
592
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
593
|
+
if (schema) {
|
|
594
|
+
const valid = holoInstance.validator.validate(schema, parsed);
|
|
595
|
+
if (valid || !holoInstance.strict) {
|
|
596
|
+
output.set(parsed.id, parsed);
|
|
597
|
+
}
|
|
598
|
+
} else {
|
|
599
|
+
output.set(parsed.id, parsed);
|
|
600
|
+
}
|
|
601
|
+
} catch (error) {
|
|
602
|
+
console.error('Error processing data:', error);
|
|
566
603
|
}
|
|
567
|
-
}
|
|
568
|
-
output.set(parsed.id, parsed);
|
|
569
|
-
}
|
|
570
|
-
} catch (error) {
|
|
571
|
-
console.error('Error processing data:', error);
|
|
572
|
-
}
|
|
573
|
-
};
|
|
604
|
+
})();
|
|
574
605
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
resolve([]);
|
|
578
|
-
return;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
const initialPromises = [];
|
|
582
|
-
Object.keys(data)
|
|
583
|
-
.filter(key => key !== '_')
|
|
584
|
-
.forEach(key => {
|
|
585
|
-
initialPromises.push(processData(data[key], key));
|
|
586
|
-
});
|
|
606
|
+
pendingProcessing.push(processingPromise);
|
|
607
|
+
receivedCount++;
|
|
587
608
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
|
-
|
|
597
|
-
const dataPath = password ?
|
|
598
|
-
user.get('private').get(lens) :
|
|
599
|
-
holoInstance.gun.get(holoInstance.appname).get(holon).get(lens);
|
|
600
|
-
|
|
601
|
-
dataPath.once(handleData);
|
|
609
|
+
if (receivedCount >= expectedCount) {
|
|
610
|
+
await Promise.all(pendingProcessing);
|
|
611
|
+
resolve(Array.from(output.values()));
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
});
|
|
602
615
|
});
|
|
603
616
|
} catch (error) {
|
|
604
617
|
console.error('Error in getAll:', error);
|
|
@@ -642,57 +655,18 @@ export async function parse(holoInstance, rawData) {
|
|
|
642
655
|
} else if (rawData._) {
|
|
643
656
|
// Handle potential GunDB metadata remnants (attempt cleanup)
|
|
644
657
|
console.warn('Parsing raw Gun object with metadata (_) - attempting cleanup:', rawData);
|
|
645
|
-
|
|
646
|
-
// Enhanced cleanup for complex GunDB structures
|
|
647
658
|
const potentialData = Object.keys(rawData).reduce((acc, k) => {
|
|
648
659
|
if (k !== '_') {
|
|
649
|
-
|
|
650
|
-
if (rawData[k] && typeof rawData[k] === 'object' && rawData[k]._) {
|
|
651
|
-
// Recursively parse nested raw nodes
|
|
652
|
-
const parsedNested = parse(holoInstance, rawData[k]);
|
|
653
|
-
if (parsedNested !== null) {
|
|
654
|
-
acc[k] = parsedNested;
|
|
655
|
-
}
|
|
656
|
-
} else if (rawData[k] !== null) {
|
|
657
|
-
// Only include non-null values
|
|
658
|
-
acc[k] = rawData[k];
|
|
659
|
-
}
|
|
660
|
+
acc[k] = rawData[k];
|
|
660
661
|
}
|
|
661
662
|
return acc;
|
|
662
663
|
}, {});
|
|
663
|
-
|
|
664
664
|
if (Object.keys(potentialData).length === 0) {
|
|
665
|
-
console.warn('Raw Gun object had only metadata (_)
|
|
666
|
-
return null;
|
|
667
|
-
}
|
|
668
|
-
|
|
669
|
-
// Additional validation: check if the cleaned object has meaningful data
|
|
670
|
-
const hasValidData = Object.values(potentialData).some(value =>
|
|
671
|
-
value !== null && value !== undefined &&
|
|
672
|
-
(typeof value !== 'object' || Object.keys(value).length > 0)
|
|
673
|
-
);
|
|
674
|
-
|
|
675
|
-
if (!hasValidData) {
|
|
676
|
-
console.warn('Cleaned Gun object has no valid data, returning null.');
|
|
665
|
+
console.warn('Raw Gun object had only metadata (_), returning null.');
|
|
677
666
|
return null;
|
|
678
667
|
}
|
|
679
|
-
|
|
680
668
|
return potentialData; // Return cleaned-up object
|
|
681
669
|
} else {
|
|
682
|
-
// Check for objects that might be arrays of raw GunDB nodes
|
|
683
|
-
if (Array.isArray(rawData)) {
|
|
684
|
-
const cleanedArray = rawData
|
|
685
|
-
.map(item => parse(holoInstance, item))
|
|
686
|
-
.filter(item => item !== null);
|
|
687
|
-
|
|
688
|
-
if (cleanedArray.length === 0) {
|
|
689
|
-
console.warn('Array contained only invalid/raw GunDB nodes, returning null.');
|
|
690
|
-
return null;
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
return cleanedArray;
|
|
694
|
-
}
|
|
695
|
-
|
|
696
670
|
// Assume it's a regular plain object
|
|
697
671
|
return rawData;
|
|
698
672
|
}
|
package/global.js
CHANGED
|
@@ -320,86 +320,90 @@ export async function getAllGlobal(holoInstance, tableName, password = null) {
|
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
return new Promise((resolve) => {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
let timeout = setTimeout(() => {
|
|
326
|
-
if (!isResolved) {
|
|
327
|
-
isResolved = true;
|
|
328
|
-
resolve(output);
|
|
329
|
-
}
|
|
330
|
-
}, 5000);
|
|
323
|
+
const output = [];
|
|
324
|
+
const pendingProcessing = [];
|
|
331
325
|
|
|
332
|
-
const
|
|
326
|
+
const dataPath = password ?
|
|
327
|
+
user.get('private').get(tableName) :
|
|
328
|
+
holoInstance.gun.get(holoInstance.appname).get(tableName);
|
|
329
|
+
|
|
330
|
+
// PASS 1: Get shallow node to determine expected item count
|
|
331
|
+
dataPath.once((data) => {
|
|
333
332
|
if (!data) {
|
|
334
|
-
clearTimeout(timeout);
|
|
335
|
-
isResolved = true;
|
|
336
333
|
resolve([]);
|
|
337
334
|
return;
|
|
338
335
|
}
|
|
339
336
|
|
|
340
|
-
|
|
341
|
-
const
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
337
|
+
// Filter out Gun metadata, null/deleted entries, and Gun soul references
|
|
338
|
+
const keys = Object.keys(data).filter(key => {
|
|
339
|
+
if (key === '_') return false;
|
|
340
|
+
const val = data[key];
|
|
341
|
+
if (val === null) return false;
|
|
342
|
+
// Filter out Gun graph references (sub-nodes)
|
|
343
|
+
if (typeof val === 'object' && val['#']) return false;
|
|
344
|
+
return true;
|
|
345
|
+
});
|
|
346
|
+
const expectedCount = keys.length;
|
|
346
347
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
348
|
+
if (expectedCount === 0) {
|
|
349
|
+
resolve([]);
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
350
352
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const parsed = await holoInstance.parse(itemData); // Use instance's parse
|
|
354
|
-
if (parsed) {
|
|
355
|
-
// Check if this is a hologram that needs to be resolved
|
|
356
|
-
if (holoInstance.isHologram(parsed)) { // Use instance's isHologram
|
|
357
|
-
const resolved = await holoInstance.resolveHologram(parsed, { // Use instance's resolveHologram
|
|
358
|
-
followHolograms: true // Always follow holograms
|
|
359
|
-
});
|
|
353
|
+
// PASS 2: Use map().once() to iterate and get full item data
|
|
354
|
+
let receivedCount = 0;
|
|
360
355
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
356
|
+
dataPath.map().once(async (itemData, key) => {
|
|
357
|
+
if (!itemData || key === '_') {
|
|
358
|
+
receivedCount++;
|
|
359
|
+
if (receivedCount >= expectedCount) {
|
|
360
|
+
await Promise.all(pendingProcessing);
|
|
361
|
+
resolve(output);
|
|
362
|
+
}
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
370
365
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
366
|
+
const processingPromise = (async () => {
|
|
367
|
+
try {
|
|
368
|
+
const parsed = await holoInstance.parse(itemData);
|
|
369
|
+
if (!parsed) return;
|
|
370
|
+
|
|
371
|
+
if (holoInstance.isHologram(parsed)) {
|
|
372
|
+
const resolved = await holoInstance.resolveHologram(parsed, {
|
|
373
|
+
followHolograms: true
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
if (resolved === null) {
|
|
377
|
+
try {
|
|
378
|
+
await holoInstance.deleteGlobal(tableName, key, password);
|
|
379
|
+
} catch (deleteError) {
|
|
380
|
+
console.error(`Failed to delete invalid global hologram at ${tableName}/${key}:`, deleteError);
|
|
380
381
|
}
|
|
382
|
+
return;
|
|
381
383
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
+
|
|
385
|
+
if (resolved !== parsed) {
|
|
386
|
+
output.push(resolved);
|
|
387
|
+
} else {
|
|
388
|
+
output.push(parsed);
|
|
389
|
+
}
|
|
390
|
+
} else {
|
|
391
|
+
output.push(parsed);
|
|
384
392
|
}
|
|
393
|
+
} catch (error) {
|
|
394
|
+
console.error('Error parsing data:', error);
|
|
385
395
|
}
|
|
386
|
-
|
|
387
|
-
})
|
|
388
|
-
);
|
|
389
|
-
|
|
390
|
-
await Promise.all(promises);
|
|
391
|
-
clearTimeout(timeout);
|
|
392
|
-
if (!isResolved) {
|
|
393
|
-
isResolved = true;
|
|
394
|
-
resolve(output);
|
|
395
|
-
}
|
|
396
|
-
};
|
|
396
|
+
})();
|
|
397
397
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
holoInstance.gun.get(holoInstance.appname).get(tableName);
|
|
398
|
+
pendingProcessing.push(processingPromise);
|
|
399
|
+
receivedCount++;
|
|
401
400
|
|
|
402
|
-
|
|
401
|
+
if (receivedCount >= expectedCount) {
|
|
402
|
+
await Promise.all(pendingProcessing);
|
|
403
|
+
resolve(output);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
});
|
|
403
407
|
});
|
|
404
408
|
} catch (error) {
|
|
405
409
|
console.error('Error in getAllGlobal:', error);
|