holosphere 1.3.0-alpha7 → 1.3.0-alpha8

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
@@ -38,12 +38,41 @@ const WRITE_TIMEOUT_MS = 5000;
38
38
  *
39
39
  * The first responder wins — both branches are idempotent so a late
40
40
  * `.once()` callback after timeout is harmlessly ignored.
41
+ *
42
+ * Pass `{ awaitNetwork: true }` for cold reads that MUST reach the network —
43
+ * notably cross-holon hologram soul resolution. Gun's `.once()` fires
44
+ * synchronously with whatever is in the local graph and never re-fires; for a
45
+ * node we never subscribed to (another holon's lens), that first value is
46
+ * `undefined` even though a peer is about to deliver the real data a few ms
47
+ * later. Accepting that `undefined` as "not found" is exactly what produced
48
+ * the `Could not resolve hologram soul: … (target not present locally)` false
49
+ * negatives on freshly-loaded federated data. In `awaitNetwork` mode we use
50
+ * `.on()` instead, skip the cold `undefined`, and settle once the value
51
+ * syncs — while still honouring an explicit `null` (a real tombstone) and the
52
+ * deadline (for nodes no peer ever answers). The common, same-holon read path
53
+ * keeps the fast `.once()` behaviour so empty/missing local reads resolve
54
+ * immediately instead of blocking for the full deadline.
41
55
  */
42
- function onceWithTimeout(node, timeoutMs = READ_TIMEOUT_MS) {
56
+ function onceWithTimeout(node, timeoutMs = READ_TIMEOUT_MS, { awaitNetwork = false } = {}) {
57
+ if (!awaitNetwork) {
58
+ return new Promise((resolve) => {
59
+ let done = false;
60
+ const finish = (v) => { if (!done) { done = true; resolve(v); } };
61
+ node.once((data) => finish(data));
62
+ if (timeoutMs > 0) {
63
+ setTimeout(() => finish(null), timeoutMs);
64
+ }
65
+ });
66
+ }
67
+
43
68
  return new Promise((resolve) => {
44
69
  let done = false;
45
- const finish = (v) => { if (!done) { done = true; resolve(v); } };
46
- node.once((data) => finish(data));
70
+ const detach = () => { try { if (node.off) node.off(); } catch { /* ignore */ } };
71
+ const finish = (v) => { if (!done) { done = true; detach(); resolve(v); } };
72
+ node.on((data) => {
73
+ if (data === undefined) return; // cold/not-known yet — wait for a peer
74
+ finish(data); // defined value OR explicit null tombstone — definitive
75
+ });
47
76
  if (timeoutMs > 0) {
48
77
  setTimeout(() => finish(null), timeoutMs);
49
78
  }
@@ -535,6 +564,11 @@ export async function get(holoInstance, holon, lens, key, password = null, optio
535
564
  validationOptions = {},
536
565
  visited,
537
566
  timeout = READ_TIMEOUT_MS,
567
+ // Network-aware read: skip Gun's cold synchronous `undefined` and wait
568
+ // (up to `timeout`) for a peer to answer. Used by hologram soul
569
+ // resolution so cross-holon reads aren't misreported as missing. See
570
+ // `onceWithTimeout`. Off by default to keep same-holon reads fast.
571
+ awaitNetwork = false,
538
572
  // `_deleted: true` is the soft-tombstone convention used by the bot,
539
573
  // the web dashboard, and the MCP council tools. Pre-this fix the
540
574
  // library was unaware of it and every caller filtered defensively.
@@ -661,7 +695,7 @@ export async function get(holoInstance, holon, lens, key, password = null, optio
661
695
  // `.once()` wrapped in a deadline — cold-path reads (peer offline,
662
696
  // never-written key) used to hang forever otherwise. After
663
697
  // `timeout` ms with no Gun response we treat it as "not found".
664
- onceWithTimeout(dataPath, timeout).then(handleData);
698
+ onceWithTimeout(dataPath, timeout, { awaitNetwork }).then(handleData);
665
699
  });
666
700
  } catch (error) {
667
701
  console.error('Error in get:', error);
package/hologram.js CHANGED
@@ -168,7 +168,13 @@ export async function resolveHologram(holoInstance, hologram, options = {}) {
168
168
  resolveHolograms: followHolograms,
169
169
  visited: nextVisited,
170
170
  maxDepth: maxDepth,
171
- currentDepth: currentDepth + 1
171
+ currentDepth: currentDepth + 1,
172
+ // The soul almost always points at a DIFFERENT holon's lens
173
+ // this peer never subscribed to, so the node is cold: Gun's
174
+ // `.once()` would fire `undefined` synchronously and we'd
175
+ // wrongly report "target not present locally". Wait for the
176
+ // network round-trip instead.
177
+ awaitNetwork: true
172
178
  }
173
179
  );
174
180
 
@@ -1,9 +1,9 @@
1
1
  /**
2
- * HoloSphere ESM Bundle v1.3.0-alpha7
2
+ * HoloSphere ESM Bundle v1.3.0-alpha8
3
3
  * ES6 Module version with all dependencies bundled
4
4
  *
5
5
  * Usage:
6
- * import HoloSphere from 'https://unpkg.com/holosphere@1.3.0-alpha7/holosphere-bundle.esm.js';
6
+ * import HoloSphere from 'https://unpkg.com/holosphere@1.3.0-alpha8/holosphere-bundle.esm.js';
7
7
  * const hs = new HoloSphere('myapp');
8
8
  */
9
9
  var __create = Object.create;
@@ -24385,7 +24385,13 @@ async function resolveHologram(holoInstance, hologram, options = {}) {
24385
24385
  resolveHolograms: followHolograms,
24386
24386
  visited: nextVisited,
24387
24387
  maxDepth,
24388
- currentDepth: currentDepth + 1
24388
+ currentDepth: currentDepth + 1,
24389
+ // The soul almost always points at a DIFFERENT holon's lens
24390
+ // this peer never subscribed to, so the node is cold: Gun's
24391
+ // `.once()` would fire `undefined` synchronously and we'd
24392
+ // wrongly report "target not present locally". Wait for the
24393
+ // network round-trip instead.
24394
+ awaitNetwork: true
24389
24395
  }
24390
24396
  );
24391
24397
  if (originalData && !originalData._invalidHologram) {
@@ -25366,16 +25372,41 @@ function clearSchemaCache(holoInstance, lens = null) {
25366
25372
  // content.js
25367
25373
  var READ_TIMEOUT_MS = 8e3;
25368
25374
  var WRITE_TIMEOUT_MS = 5e3;
25369
- function onceWithTimeout(node, timeoutMs = READ_TIMEOUT_MS) {
25375
+ function onceWithTimeout(node, timeoutMs = READ_TIMEOUT_MS, { awaitNetwork = false } = {}) {
25376
+ if (!awaitNetwork) {
25377
+ return new Promise((resolve) => {
25378
+ let done = false;
25379
+ const finish = (v) => {
25380
+ if (!done) {
25381
+ done = true;
25382
+ resolve(v);
25383
+ }
25384
+ };
25385
+ node.once((data) => finish(data));
25386
+ if (timeoutMs > 0) {
25387
+ setTimeout(() => finish(null), timeoutMs);
25388
+ }
25389
+ });
25390
+ }
25370
25391
  return new Promise((resolve) => {
25371
25392
  let done = false;
25393
+ const detach = () => {
25394
+ try {
25395
+ if (node.off) node.off();
25396
+ } catch {
25397
+ }
25398
+ };
25372
25399
  const finish = (v) => {
25373
25400
  if (!done) {
25374
25401
  done = true;
25402
+ detach();
25375
25403
  resolve(v);
25376
25404
  }
25377
25405
  };
25378
- node.once((data) => finish(data));
25406
+ node.on((data) => {
25407
+ if (data === void 0) return;
25408
+ finish(data);
25409
+ });
25379
25410
  if (timeoutMs > 0) {
25380
25411
  setTimeout(() => finish(null), timeoutMs);
25381
25412
  }
@@ -25716,6 +25747,11 @@ async function get(holoInstance, holon, lens, key, password = null, options = {}
25716
25747
  validationOptions = {},
25717
25748
  visited,
25718
25749
  timeout = READ_TIMEOUT_MS,
25750
+ // Network-aware read: skip Gun's cold synchronous `undefined` and wait
25751
+ // (up to `timeout`) for a peer to answer. Used by hologram soul
25752
+ // resolution so cross-holon reads aren't misreported as missing. See
25753
+ // `onceWithTimeout`. Off by default to keep same-holon reads fast.
25754
+ awaitNetwork = false,
25719
25755
  // `_deleted: true` is the soft-tombstone convention used by the bot,
25720
25756
  // the web dashboard, and the MCP council tools. Pre-this fix the
25721
25757
  // library was unaware of it and every caller filtered defensively.
@@ -25801,7 +25837,7 @@ async function get(holoInstance, holon, lens, key, password = null, options = {}
25801
25837
  }
25802
25838
  };
25803
25839
  const dataPath = password ? user.get("private").get(lens).get(key) : holoInstance.gun.get(holoInstance.appname).get(holon).get(lens).get(key);
25804
- onceWithTimeout(dataPath, timeout).then(handleData);
25840
+ onceWithTimeout(dataPath, timeout, { awaitNetwork }).then(handleData);
25805
25841
  });
25806
25842
  } catch (error) {
25807
25843
  console.error("Error in get:", error);
@@ -27234,7 +27270,11 @@ function subscribe(holoInstance, holon, lens, callback) {
27234
27270
  try {
27235
27271
  let parsed = await holoInstance.parse(data);
27236
27272
  if (parsed && holoInstance.isHologram(parsed)) {
27273
+ const hologramSoul = parsed.soul;
27237
27274
  const resolved = await holoInstance.resolveHologram(parsed, { followHolograms: true });
27275
+ if (resolved === null) {
27276
+ console.warn(`Hologram at ${holon}/${lens}/${key} did not resolve (soul=${hologramSoul}); skipping.`);
27277
+ }
27238
27278
  if (resolved !== parsed) {
27239
27279
  parsed = resolved;
27240
27280
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * HoloSphere Bundle v1.3.0-alpha7
2
+ * HoloSphere Bundle v1.3.0-alpha8
3
3
  * Holonic Geospatial Communication Infrastructure
4
4
  *
5
5
  * Includes:
@@ -9,7 +9,7 @@
9
9
  * - Ajv (JSON schema validation)
10
10
  *
11
11
  * Usage:
12
- * <script src="https://unpkg.com/holosphere@1.3.0-alpha7/holosphere-bundle.js"></script>
12
+ * <script src="https://unpkg.com/holosphere@1.3.0-alpha8/holosphere-bundle.js"></script>
13
13
  * <script>
14
14
  * const hs = new HoloSphere('myapp');
15
15
  * </script>
@@ -24411,7 +24411,13 @@ var HoloSphere = (() => {
24411
24411
  resolveHolograms: followHolograms,
24412
24412
  visited: nextVisited,
24413
24413
  maxDepth,
24414
- currentDepth: currentDepth + 1
24414
+ currentDepth: currentDepth + 1,
24415
+ // The soul almost always points at a DIFFERENT holon's lens
24416
+ // this peer never subscribed to, so the node is cold: Gun's
24417
+ // `.once()` would fire `undefined` synchronously and we'd
24418
+ // wrongly report "target not present locally". Wait for the
24419
+ // network round-trip instead.
24420
+ awaitNetwork: true
24415
24421
  }
24416
24422
  );
24417
24423
  if (originalData && !originalData._invalidHologram) {
@@ -25392,16 +25398,41 @@ var HoloSphere = (() => {
25392
25398
  // content.js
25393
25399
  var READ_TIMEOUT_MS = 8e3;
25394
25400
  var WRITE_TIMEOUT_MS = 5e3;
25395
- function onceWithTimeout(node, timeoutMs = READ_TIMEOUT_MS) {
25401
+ function onceWithTimeout(node, timeoutMs = READ_TIMEOUT_MS, { awaitNetwork = false } = {}) {
25402
+ if (!awaitNetwork) {
25403
+ return new Promise((resolve) => {
25404
+ let done = false;
25405
+ const finish = (v) => {
25406
+ if (!done) {
25407
+ done = true;
25408
+ resolve(v);
25409
+ }
25410
+ };
25411
+ node.once((data) => finish(data));
25412
+ if (timeoutMs > 0) {
25413
+ setTimeout(() => finish(null), timeoutMs);
25414
+ }
25415
+ });
25416
+ }
25396
25417
  return new Promise((resolve) => {
25397
25418
  let done = false;
25419
+ const detach = () => {
25420
+ try {
25421
+ if (node.off) node.off();
25422
+ } catch {
25423
+ }
25424
+ };
25398
25425
  const finish = (v) => {
25399
25426
  if (!done) {
25400
25427
  done = true;
25428
+ detach();
25401
25429
  resolve(v);
25402
25430
  }
25403
25431
  };
25404
- node.once((data) => finish(data));
25432
+ node.on((data) => {
25433
+ if (data === void 0) return;
25434
+ finish(data);
25435
+ });
25405
25436
  if (timeoutMs > 0) {
25406
25437
  setTimeout(() => finish(null), timeoutMs);
25407
25438
  }
@@ -25742,6 +25773,11 @@ var HoloSphere = (() => {
25742
25773
  validationOptions = {},
25743
25774
  visited,
25744
25775
  timeout = READ_TIMEOUT_MS,
25776
+ // Network-aware read: skip Gun's cold synchronous `undefined` and wait
25777
+ // (up to `timeout`) for a peer to answer. Used by hologram soul
25778
+ // resolution so cross-holon reads aren't misreported as missing. See
25779
+ // `onceWithTimeout`. Off by default to keep same-holon reads fast.
25780
+ awaitNetwork = false,
25745
25781
  // `_deleted: true` is the soft-tombstone convention used by the bot,
25746
25782
  // the web dashboard, and the MCP council tools. Pre-this fix the
25747
25783
  // library was unaware of it and every caller filtered defensively.
@@ -25827,7 +25863,7 @@ var HoloSphere = (() => {
25827
25863
  }
25828
25864
  };
25829
25865
  const dataPath = password ? user.get("private").get(lens).get(key) : holoInstance.gun.get(holoInstance.appname).get(holon).get(lens).get(key);
25830
- onceWithTimeout(dataPath, timeout).then(handleData);
25866
+ onceWithTimeout(dataPath, timeout, { awaitNetwork }).then(handleData);
25831
25867
  });
25832
25868
  } catch (error) {
25833
25869
  console.error("Error in get:", error);
@@ -27260,7 +27296,11 @@ var HoloSphere = (() => {
27260
27296
  try {
27261
27297
  let parsed = await holoInstance.parse(data);
27262
27298
  if (parsed && holoInstance.isHologram(parsed)) {
27299
+ const hologramSoul = parsed.soul;
27263
27300
  const resolved = await holoInstance.resolveHologram(parsed, { followHolograms: true });
27301
+ if (resolved === null) {
27302
+ console.warn(`Hologram at ${holon}/${lens}/${key} did not resolve (soul=${hologramSoul}); skipping.`);
27303
+ }
27264
27304
  if (resolved !== parsed) {
27265
27305
  parsed = resolved;
27266
27306
  }