holosphere 1.1.21 → 1.3.0-alpha3

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/content.js CHANGED
@@ -142,11 +142,15 @@ export async function put(holoInstance, holon, lens, data, password = null, opti
142
142
 
143
143
  return new Promise((resolve, reject) => {
144
144
  try {
145
- // Create a copy of data without the _meta field if it exists
145
+ // Create a copy of data, stripping read-side envelopes that
146
+ // must never be persisted (they're attached at resolution time).
146
147
  let dataToStore = { ...data };
147
148
  if (dataToStore._meta !== undefined) {
148
149
  delete dataToStore._meta;
149
150
  }
151
+ if (dataToStore._hologram !== undefined) {
152
+ delete dataToStore._hologram;
153
+ }
150
154
  const payload = JSON.stringify(dataToStore); // The data being stored
151
155
 
152
156
  const putCallback = async (ack) => {
@@ -514,8 +518,12 @@ export async function getAll(holoInstance, holon, lens, password = null) {
514
518
  user.get('private').get(lens) :
515
519
  holoInstance.gun.get(holoInstance.appname).get(holon).get(lens);
516
520
 
517
- // PASS 1: Get shallow node to determine expected item count
518
- dataPath.once((data) => {
521
+ // PASS 1: Get shallow node to determine expected item count.
522
+ // Retry once if empty — Gun's .once() reads from local cache, which
523
+ // may be cold immediately after startup before peers have synced.
524
+ const shallowOnce = () => new Promise((res) => dataPath.once((d) => res(d)));
525
+
526
+ const processShallow = (data) => {
519
527
  if (!data) {
520
528
  resolve([]);
521
529
  return;
@@ -537,81 +545,90 @@ export async function getAll(holoInstance, holon, lens, password = null) {
537
545
  return;
538
546
  }
539
547
 
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
- }
548
+ // PASS 2: iterate explicitly over the filtered keys.
549
+ // Using dataPath.map().once() here is unsafe when the parent
550
+ // node has tombstoned siblings (null values): map() fires for
551
+ // every child, and a null sibling's callback can satisfy
552
+ // receivedCount before real items are processed, resolving [].
553
+ const processItem = async (itemData, key) => {
554
+ if (!itemData) return;
555
+ try {
556
+ const parsed = await holoInstance.parse(itemData);
557
+ if (!parsed || !parsed.id) return;
552
558
 
553
- const processingPromise = (async () => {
554
- try {
555
- const parsed = await holoInstance.parse(itemData);
556
- if (!parsed || !parsed.id) return;
557
-
558
- if (holoInstance.isHologram(parsed)) {
559
- try {
560
- const resolved = await holoInstance.resolveHologram(parsed, {
561
- followHolograms: true,
562
- maxDepth: 10,
563
- currentDepth: 0
564
- });
559
+ if (holoInstance.isHologram(parsed)) {
560
+ try {
561
+ const resolved = await holoInstance.resolveHologram(parsed, {
562
+ followHolograms: true,
563
+ maxDepth: 10,
564
+ currentDepth: 0
565
+ });
565
566
 
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;
567
+ if (resolved === null) {
568
+ console.warn(`Broken hologram detected in getAll for key ${key}. Removing it...`);
569
+ try {
570
+ await holoInstance.delete(holon, lens, key, password);
571
+ } catch (cleanupError) {
572
+ console.error(`Failed to remove broken hologram at ${holon}/${lens}/${key}:`, cleanupError);
574
573
  }
574
+ return;
575
+ }
575
576
 
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 {
577
+ if (resolved && resolved !== parsed) {
578
+ if (schema) {
579
+ const valid = holoInstance.validator.validate(schema, resolved);
580
+ if (valid || !holoInstance.strict) {
583
581
  output.set(resolved.id, resolved);
584
582
  }
585
- return;
583
+ } else {
584
+ output.set(resolved.id, resolved);
586
585
  }
587
- } catch (hologramError) {
588
- console.error(`Error resolving hologram for key ${key}:`, hologramError);
589
586
  return;
590
587
  }
588
+ } catch (hologramError) {
589
+ console.error(`Error resolving hologram for key ${key}:`, hologramError);
590
+ return;
591
591
  }
592
+ }
592
593
 
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 {
594
+ if (schema) {
595
+ const valid = holoInstance.validator.validate(schema, parsed);
596
+ if (valid || !holoInstance.strict) {
599
597
  output.set(parsed.id, parsed);
600
598
  }
601
- } catch (error) {
602
- console.error('Error processing data:', error);
599
+ } else {
600
+ output.set(parsed.id, parsed);
603
601
  }
604
- })();
605
-
606
- pendingProcessing.push(processingPromise);
607
- receivedCount++;
602
+ } catch (error) {
603
+ console.error('Error processing data:', error);
604
+ }
605
+ };
608
606
 
609
- if (receivedCount >= expectedCount) {
610
- await Promise.all(pendingProcessing);
611
- resolve(Array.from(output.values()));
607
+ Promise.all(keys.map((key) => {
608
+ const inline = data[key];
609
+ // If shallow already inlined the leaf (holosphere stores
610
+ // payloads as JSON strings), process it directly. This
611
+ // avoids a redundant round-trip and the map() race.
612
+ if (typeof inline !== 'object' || inline === null) {
613
+ return processItem(inline, key);
612
614
  }
613
- });
614
- });
615
+ // Otherwise fetch the leaf — sub-graph reference path.
616
+ return new Promise((resolveItem) => {
617
+ dataPath.get(key).once((itemData) => {
618
+ processItem(itemData, key).then(resolveItem, resolveItem);
619
+ });
620
+ });
621
+ })).then(() => resolve(Array.from(output.values())));
622
+ };
623
+
624
+ (async () => {
625
+ let data = await shallowOnce();
626
+ if (!data) {
627
+ await new Promise(r => setTimeout(r, 1500));
628
+ data = await shallowOnce();
629
+ }
630
+ processShallow(data);
631
+ })();
615
632
  });
616
633
  } catch (error) {
617
634
  console.error('Error in getAll:', error);