holosphere 1.3.0-alpha0 → 1.3.0-alpha4

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.
@@ -1,9 +1,9 @@
1
1
  /**
2
- * HoloSphere ESM Bundle v1.3.0-alpha0
2
+ * HoloSphere ESM Bundle v1.3.0-alpha4
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-alpha0/holosphere-bundle.esm.js';
6
+ * import HoloSphere from 'https://unpkg.com/holosphere@1.3.0-alpha4/holosphere-bundle.esm.js';
7
7
  * const hs = new HoloSphere('myapp');
8
8
  */
9
9
  var __create = Object.create;
@@ -7054,7 +7054,7 @@ var require_core = __commonJS({
7054
7054
  constructor(opts = {}) {
7055
7055
  this.schemas = {};
7056
7056
  this.refs = {};
7057
- this.formats = {};
7057
+ this.formats = /* @__PURE__ */ Object.create(null);
7058
7058
  this._compilations = /* @__PURE__ */ new Set();
7059
7059
  this._loading = {};
7060
7060
  this._cache = /* @__PURE__ */ new Map();
@@ -7835,6 +7835,7 @@ var require_pattern = __commonJS({
7835
7835
  "use strict";
7836
7836
  Object.defineProperty(exports, "__esModule", { value: true });
7837
7837
  var code_1 = require_code2();
7838
+ var util_1 = require_util();
7838
7839
  var codegen_1 = require_codegen();
7839
7840
  var error = {
7840
7841
  message: ({ schemaCode }) => (0, codegen_1.str)`must match pattern "${schemaCode}"`,
@@ -7847,10 +7848,18 @@ var require_pattern = __commonJS({
7847
7848
  $data: true,
7848
7849
  error,
7849
7850
  code(cxt) {
7850
- const { data, $data, schema, schemaCode, it } = cxt;
7851
+ const { gen, data, $data, schema, schemaCode, it } = cxt;
7851
7852
  const u = it.opts.unicodeRegExp ? "u" : "";
7852
- const regExp = $data ? (0, codegen_1._)`(new RegExp(${schemaCode}, ${u}))` : (0, code_1.usePattern)(cxt, schema);
7853
- cxt.fail$data((0, codegen_1._)`!${regExp}.test(${data})`);
7853
+ if ($data) {
7854
+ const { regExp } = it.opts.code;
7855
+ const regExpCode = regExp.code === "new RegExp" ? (0, codegen_1._)`new RegExp` : (0, util_1.useFunc)(gen, regExp);
7856
+ const valid = gen.let("valid");
7857
+ gen.try(() => gen.assign(valid, (0, codegen_1._)`${regExpCode}(${schemaCode}, ${u}).test(${data})`), () => gen.assign(valid, false));
7858
+ cxt.fail$data((0, codegen_1._)`!${valid}`);
7859
+ } else {
7860
+ const regExp = (0, code_1.usePattern)(cxt, schema);
7861
+ cxt.fail$data((0, codegen_1._)`!${regExp}.test(${data})`);
7862
+ }
7854
7863
  }
7855
7864
  };
7856
7865
  exports.default = def;
@@ -10061,7 +10070,7 @@ var require__ = __commonJS({
10061
10070
  var discriminator_1 = require_discriminator();
10062
10071
  var json_schema_2019_09_1 = require_json_schema_2019_09();
10063
10072
  var META_SCHEMA_ID = "https://json-schema.org/draft/2019-09/schema";
10064
- var Ajv20193 = class extends core_1.default {
10073
+ var Ajv20192 = class extends core_1.default {
10065
10074
  constructor(opts = {}) {
10066
10075
  super({
10067
10076
  ...opts,
@@ -10091,11 +10100,11 @@ var require__ = __commonJS({
10091
10100
  return this.opts.defaultMeta = super.defaultMeta() || (this.getSchema(META_SCHEMA_ID) ? META_SCHEMA_ID : void 0);
10092
10101
  }
10093
10102
  };
10094
- exports.Ajv2019 = Ajv20193;
10095
- module.exports = exports = Ajv20193;
10096
- module.exports.Ajv2019 = Ajv20193;
10103
+ exports.Ajv2019 = Ajv20192;
10104
+ module.exports = exports = Ajv20192;
10105
+ module.exports.Ajv2019 = Ajv20192;
10097
10106
  Object.defineProperty(exports, "__esModule", { value: true });
10098
- exports.default = Ajv20193;
10107
+ exports.default = Ajv20192;
10099
10108
  var validate_1 = require_validate();
10100
10109
  Object.defineProperty(exports, "KeywordCxt", { enumerable: true, get: function() {
10101
10110
  return validate_1.KeywordCxt;
@@ -24197,9 +24206,139 @@ function degsToRads(deg) {
24197
24206
 
24198
24207
  // holosphere.js
24199
24208
  var import_gun = __toESM(require_browser(), 1);
24200
- var import__2 = __toESM(require__(), 1);
24209
+ var import__ = __toESM(require__(), 1);
24210
+
24211
+ // hologram.js
24212
+ function createHologram(holoInstance, holon, lens, data) {
24213
+ if (isHologram(data)) {
24214
+ return {
24215
+ id: data.id,
24216
+ soul: data.soul
24217
+ };
24218
+ }
24219
+ if (!holon || !lens || !data || !data.id) {
24220
+ throw new Error("createHologram: Missing required parameters for non-hologram data");
24221
+ }
24222
+ const soul = `${holoInstance.appname}/${holon}/${lens}/${data.id}`;
24223
+ return {
24224
+ id: data.id,
24225
+ soul
24226
+ };
24227
+ }
24228
+ function parseSoulPath(soul) {
24229
+ if (typeof soul !== "string") return null;
24230
+ const parts = soul.split("/");
24231
+ if (parts.length < 4) return null;
24232
+ const key = parts.slice(3).join("/");
24233
+ return {
24234
+ appname: parts[0],
24235
+ holon: parts[1],
24236
+ lens: parts[2],
24237
+ key
24238
+ };
24239
+ }
24240
+ function isHologram(data) {
24241
+ if (!data || typeof data !== "object") {
24242
+ return false;
24243
+ }
24244
+ if (data.id && typeof data.soul === "string" && data.soul.length > 0) {
24245
+ if (data.soul.includes("/")) {
24246
+ return true;
24247
+ }
24248
+ }
24249
+ return false;
24250
+ }
24251
+ function attachHologramMeta(originalData, hologramSoul) {
24252
+ const parts = parseSoulPath(hologramSoul);
24253
+ return {
24254
+ ...originalData,
24255
+ _hologram: {
24256
+ isHologram: true,
24257
+ soul: hologramSoul,
24258
+ sourceHolon: parts?.holon ?? null,
24259
+ sourceLens: parts?.lens ?? null,
24260
+ sourceKey: parts?.key ?? null,
24261
+ resolvedAt: Date.now()
24262
+ }
24263
+ };
24264
+ }
24265
+ async function resolveHologram(holoInstance, hologram, options = {}) {
24266
+ if (!isHologram(hologram)) {
24267
+ return hologram;
24268
+ }
24269
+ const {
24270
+ followHolograms = true,
24271
+ visited = /* @__PURE__ */ new Set(),
24272
+ maxDepth = 10,
24273
+ currentDepth = 0
24274
+ } = options;
24275
+ if (currentDepth >= maxDepth) {
24276
+ console.warn(`!!! Maximum resolution depth (${maxDepth}) reached for soul: ${hologram.soul}. Stopping resolution.`);
24277
+ return null;
24278
+ }
24279
+ if (hologram.soul && visited.has(hologram.soul)) {
24280
+ console.warn(`!!! CIRCULAR hologram detected for soul: ${hologram.soul}. Breaking loop.`);
24281
+ return null;
24282
+ }
24283
+ try {
24284
+ if (hologram.soul) {
24285
+ const soulInfo = parseSoulPath(hologram.soul);
24286
+ if (!soulInfo) {
24287
+ console.warn(`Invalid soul format: ${hologram.soul}`);
24288
+ return null;
24289
+ }
24290
+ const nextVisited = new Set(visited);
24291
+ nextVisited.add(hologram.soul);
24292
+ const originalData = await holoInstance.get(
24293
+ soulInfo.holon,
24294
+ soulInfo.lens,
24295
+ soulInfo.key,
24296
+ null,
24297
+ {
24298
+ resolveHolograms: followHolograms,
24299
+ visited: nextVisited,
24300
+ maxDepth,
24301
+ currentDepth: currentDepth + 1
24302
+ }
24303
+ );
24304
+ if (originalData && !originalData._invalidHologram) {
24305
+ return attachHologramMeta(originalData, hologram.soul);
24306
+ } else {
24307
+ console.warn(`!!! Original data NOT FOUND for soul: ${hologram.soul}. Removing broken hologram.`);
24308
+ return null;
24309
+ }
24310
+ } else {
24311
+ console.warn("!!! resolveHologram called with object missing soul:", hologram);
24312
+ return null;
24313
+ }
24314
+ } catch (error) {
24315
+ if (error.message?.startsWith("CIRCULAR_REFERENCE")) {
24316
+ console.warn(`!!! Circular reference detected during hologram resolution: ${error.message}`);
24317
+ return null;
24318
+ }
24319
+ console.error(`!!! Error resolving hologram: ${error.message}`, error);
24320
+ return null;
24321
+ }
24322
+ }
24201
24323
 
24202
24324
  // federation.js
24325
+ async function getHolonName(holosphere, space) {
24326
+ if (!holosphere || !space) return null;
24327
+ try {
24328
+ const settings = await holosphere.get(space, "settings", space);
24329
+ if (!settings) return null;
24330
+ if (Array.isArray(settings)) {
24331
+ const found = settings.find((s) => s && typeof s.name === "string" && s.name.trim() !== "");
24332
+ return found ? found.name : null;
24333
+ }
24334
+ if (typeof settings.name === "string" && settings.name.trim() !== "") {
24335
+ return settings.name;
24336
+ }
24337
+ return null;
24338
+ } catch {
24339
+ return null;
24340
+ }
24341
+ }
24203
24342
  async function federate(holosphere, spaceId1, spaceId2, password1 = null, password2 = null, bidirectional = true, lensConfig = {}) {
24204
24343
  if (!spaceId1 || !spaceId2) {
24205
24344
  throw new Error("federate: Missing required space IDs");
@@ -24207,110 +24346,85 @@ async function federate(holosphere, spaceId1, spaceId2, password1 = null, passwo
24207
24346
  if (spaceId1 === spaceId2) {
24208
24347
  throw new Error("Cannot federate a space with itself");
24209
24348
  }
24210
- const { federate: federate2 = [], notify = [] } = lensConfig;
24211
- if (!Array.isArray(federate2) || !Array.isArray(notify)) {
24212
- throw new Error("federate: lensConfig.federate and lensConfig.notify must be arrays");
24213
- }
24214
- const federateLenses = federate2;
24215
- const notifyLenses = notify;
24349
+ const { inbound = [], outbound = [] } = lensConfig;
24350
+ if (!Array.isArray(inbound) || !Array.isArray(outbound)) {
24351
+ throw new Error("federate: lensConfig.inbound and lensConfig.outbound must be arrays");
24352
+ }
24353
+ const applyDirection = (fedInfo, partnerId, partnerInbound, partnerOutbound) => {
24354
+ if (!fedInfo.federated) fedInfo.federated = [];
24355
+ if (!fedInfo.inbound) fedInfo.inbound = [];
24356
+ if (!fedInfo.outbound) fedInfo.outbound = [];
24357
+ if (!fedInfo.lensConfig) fedInfo.lensConfig = {};
24358
+ if (!fedInfo.federated.includes(partnerId)) {
24359
+ fedInfo.federated.push(partnerId);
24360
+ }
24361
+ const hasInbound = partnerInbound.length > 0;
24362
+ const hasOutbound = partnerOutbound.length > 0;
24363
+ if (hasInbound && !fedInfo.inbound.includes(partnerId)) {
24364
+ fedInfo.inbound.push(partnerId);
24365
+ } else if (!hasInbound) {
24366
+ fedInfo.inbound = fedInfo.inbound.filter((id) => id !== partnerId);
24367
+ }
24368
+ if (hasOutbound && !fedInfo.outbound.includes(partnerId)) {
24369
+ fedInfo.outbound.push(partnerId);
24370
+ } else if (!hasOutbound) {
24371
+ fedInfo.outbound = fedInfo.outbound.filter((id) => id !== partnerId);
24372
+ }
24373
+ fedInfo.lensConfig[partnerId] = {
24374
+ inbound: [...partnerInbound],
24375
+ outbound: [...partnerOutbound],
24376
+ timestamp: Date.now()
24377
+ };
24378
+ fedInfo.timestamp = Date.now();
24379
+ };
24216
24380
  try {
24217
24381
  let fedInfo1 = null;
24218
24382
  try {
24219
24383
  fedInfo1 = await holosphere.getGlobal("federation", spaceId1, password1);
24220
- } catch (error) {
24384
+ } catch {
24221
24385
  }
24222
24386
  if (fedInfo1 == null) {
24223
24387
  fedInfo1 = {
24224
24388
  id: spaceId1,
24225
24389
  name: spaceId1,
24226
- federation: [],
24227
- notify: [],
24390
+ federated: [],
24391
+ inbound: [],
24392
+ outbound: [],
24228
24393
  lensConfig: {},
24229
- // New field for lens-specific settings
24394
+ partnerNames: {},
24230
24395
  timestamp: Date.now()
24231
24396
  };
24232
24397
  }
24233
- if (!fedInfo1.federation) fedInfo1.federation = [];
24234
- if (!fedInfo1.notify) fedInfo1.notify = [];
24235
- if (!fedInfo1.lensConfig) fedInfo1.lensConfig = {};
24236
- if (!fedInfo1.federation.includes(spaceId2)) {
24237
- fedInfo1.federation.push(spaceId2);
24238
- }
24239
- if (!fedInfo1.notify.includes(spaceId2)) {
24240
- fedInfo1.notify.push(spaceId2);
24241
- }
24242
- const newLensConfigsForSpace1 = { ...fedInfo1.lensConfig || {} };
24243
- newLensConfigsForSpace1[spaceId2] = {
24244
- // Add/update config for the target spaceId2
24245
- federate: [...federateLenses],
24246
- // federateLenses & notifyLenses are from the main lensConfig parameter
24247
- notify: [...notifyLenses],
24248
- timestamp: Date.now()
24249
- };
24250
- fedInfo1.lensConfig = newLensConfigsForSpace1;
24251
- fedInfo1.timestamp = Date.now();
24398
+ applyDirection(fedInfo1, spaceId2, inbound, outbound);
24252
24399
  try {
24253
24400
  await holosphere.putGlobal("federation", fedInfo1, password1);
24254
24401
  } catch (error) {
24255
24402
  throw new Error(`Failed to create federation: ${error.message}`);
24256
24403
  }
24257
- {
24404
+ if (bidirectional) {
24258
24405
  let fedInfo2 = null;
24259
24406
  try {
24260
24407
  fedInfo2 = await holosphere.getGlobal("federation", spaceId2, password2);
24261
- } catch (error) {
24408
+ } catch {
24262
24409
  }
24263
24410
  if (fedInfo2 == null) {
24264
24411
  fedInfo2 = {
24265
24412
  id: spaceId2,
24266
24413
  name: spaceId2,
24267
- federation: [],
24268
- notify: [],
24414
+ federated: [],
24415
+ inbound: [],
24416
+ outbound: [],
24269
24417
  lensConfig: {},
24270
- // New field for lens-specific settings
24418
+ partnerNames: {},
24271
24419
  timestamp: Date.now()
24272
24420
  };
24273
24421
  }
24274
- if (!fedInfo2.federation) fedInfo2.federation = [];
24275
- if (!fedInfo2.notify) fedInfo2.notify = [];
24276
- if (!fedInfo2.lensConfig) fedInfo2.lensConfig = {};
24277
- if (bidirectional && !fedInfo2.federation.includes(spaceId1)) {
24278
- fedInfo2.federation.push(spaceId1);
24279
- }
24280
- if (!fedInfo2.notify.includes(spaceId1)) {
24281
- fedInfo2.notify.push(spaceId1);
24282
- }
24283
- fedInfo2.lensConfig[spaceId1] = {
24284
- federate: bidirectional ? [...federateLenses] : [],
24285
- // Create a copy of the array
24286
- notify: bidirectional ? [...notifyLenses] : [],
24287
- // Create a copy of the array
24288
- timestamp: Date.now()
24289
- };
24290
- fedInfo2.timestamp = Date.now();
24422
+ applyDirection(fedInfo2, spaceId1, outbound, inbound);
24291
24423
  try {
24292
24424
  await holosphere.putGlobal("federation", fedInfo2, password2);
24293
- } catch (error) {
24425
+ } catch {
24294
24426
  }
24295
24427
  }
24296
- const federationMeta = {
24297
- id: `${spaceId1}_${spaceId2}`,
24298
- space1: spaceId1,
24299
- space2: spaceId2,
24300
- created: Date.now(),
24301
- status: "active",
24302
- bidirectional,
24303
- lensConfig: {
24304
- federate: [...federateLenses],
24305
- // Create a copy of the array
24306
- notify: [...notifyLenses]
24307
- // Create a copy of the array
24308
- }
24309
- };
24310
- try {
24311
- await holosphere.putGlobal("federationMeta", federationMeta);
24312
- } catch (error) {
24313
- }
24314
24428
  return true;
24315
24429
  } catch (error) {
24316
24430
  throw error;
@@ -24327,8 +24441,15 @@ async function subscribeFederation(holosphere, spaceId, password = null, callbac
24327
24441
  }
24328
24442
  const subscriptions2 = [];
24329
24443
  let lastNotificationTime = {};
24330
- if (fedInfo.federation && fedInfo.federation.length > 0) {
24331
- for (const federatedSpace of fedInfo.federation) {
24444
+ const partnerNames = /* @__PURE__ */ new Map();
24445
+ if (fedInfo.inbound && fedInfo.inbound.length > 0) {
24446
+ await Promise.all(
24447
+ fedInfo.inbound.map(async (space) => {
24448
+ const name = await getHolonName(holosphere, space);
24449
+ partnerNames.set(space, name);
24450
+ })
24451
+ );
24452
+ for (const federatedSpace of fedInfo.inbound) {
24332
24453
  for (const lens of lenses) {
24333
24454
  try {
24334
24455
  const sub = await holosphere.subscribe(federatedSpace, lens, async (data) => {
@@ -24342,10 +24463,13 @@ async function subscribeFederation(holosphere, spaceId, password = null, callbac
24342
24463
  }
24343
24464
  lastNotificationTime[key] = now;
24344
24465
  }
24345
- if (!data.federation) {
24346
- data.federation = {
24466
+ if (!data._federation) {
24467
+ const partnerName = partnerNames.get(federatedSpace);
24468
+ data._federation = {
24347
24469
  origin: federatedSpace,
24348
- timestamp: now
24470
+ sourceLens: lens,
24471
+ timestamp: now,
24472
+ ...partnerName ? { originName: partnerName } : {}
24349
24473
  };
24350
24474
  }
24351
24475
  await callback(data, federatedSpace, lens);
@@ -24390,8 +24514,8 @@ async function getFederatedConfig(holosphere, spaceId, targetSpaceId, password =
24390
24514
  const fedInfo = await getFederation(holosphere, spaceId, password);
24391
24515
  if (fedInfo && fedInfo.lensConfig && fedInfo.lensConfig[targetSpaceId]) {
24392
24516
  return {
24393
- federate: fedInfo.lensConfig[targetSpaceId].federate || [],
24394
- notify: fedInfo.lensConfig[targetSpaceId].notify || []
24517
+ inbound: fedInfo.lensConfig[targetSpaceId].inbound || [],
24518
+ outbound: fedInfo.lensConfig[targetSpaceId].outbound || []
24395
24519
  };
24396
24520
  }
24397
24521
  return null;
@@ -24414,14 +24538,20 @@ async function unfederate(holosphere, spaceId1, spaceId2, password1 = null, pass
24414
24538
  if (!fedInfo1) {
24415
24539
  console.warn(`No federation info found for ${spaceId1}. Skipping its update.`);
24416
24540
  } else {
24417
- if (!fedInfo1.federation) fedInfo1.federation = [];
24418
- if (!fedInfo1.notify) fedInfo1.notify = [];
24419
- const originalFederationLength = fedInfo1.federation.length;
24420
- const originalNotifyLength = fedInfo1.notify.length;
24421
- fedInfo1.federation = fedInfo1.federation.filter((id) => id !== spaceId2);
24422
- fedInfo1.notify = fedInfo1.notify.filter((id) => id !== spaceId2);
24541
+ if (!fedInfo1.federated) fedInfo1.federated = [];
24542
+ if (!fedInfo1.inbound) fedInfo1.inbound = [];
24543
+ if (!fedInfo1.outbound) fedInfo1.outbound = [];
24544
+ if (!fedInfo1.lensConfig) fedInfo1.lensConfig = {};
24545
+ if (!fedInfo1.partnerNames) fedInfo1.partnerNames = {};
24546
+ const beforeIn = fedInfo1.inbound.length;
24547
+ const beforeOut = fedInfo1.outbound.length;
24548
+ fedInfo1.federated = fedInfo1.federated.filter((id) => id !== spaceId2);
24549
+ fedInfo1.inbound = fedInfo1.inbound.filter((id) => id !== spaceId2);
24550
+ fedInfo1.outbound = fedInfo1.outbound.filter((id) => id !== spaceId2);
24551
+ delete fedInfo1.lensConfig[spaceId2];
24552
+ delete fedInfo1.partnerNames[spaceId2];
24423
24553
  fedInfo1.timestamp = Date.now();
24424
- console.log(`Unfederate: Removed ${spaceId2} from ${spaceId1}: federation ${originalFederationLength} -> ${fedInfo1.federation.length}, notify ${originalNotifyLength} -> ${fedInfo1.notify.length}`);
24554
+ console.log(`Unfederate: removed ${spaceId2} from ${spaceId1}: inbound ${beforeIn} -> ${fedInfo1.inbound.length}, outbound ${beforeOut} -> ${fedInfo1.outbound.length}`);
24425
24555
  try {
24426
24556
  await holosphere.putGlobal("federation", fedInfo1, password1);
24427
24557
  } catch (error) {
@@ -24436,10 +24566,19 @@ async function unfederate(holosphere, spaceId1, spaceId2, password1 = null, pass
24436
24566
  } catch (error) {
24437
24567
  console.error(`Error getting fedInfo2 for ${spaceId2} during unfederate: ${error.message}`);
24438
24568
  }
24439
- if (!fedInfo2 || !fedInfo2.notify) {
24440
- console.warn(`No notify array found for ${spaceId2} or fedInfo2 is null. Skipping its update.`);
24569
+ if (!fedInfo2) {
24570
+ console.warn(`No federation info found for ${spaceId2}. Skipping its update.`);
24441
24571
  } else {
24442
- fedInfo2.notify = fedInfo2.notify.filter((id) => id !== spaceId1);
24572
+ if (!fedInfo2.federated) fedInfo2.federated = [];
24573
+ if (!fedInfo2.inbound) fedInfo2.inbound = [];
24574
+ if (!fedInfo2.outbound) fedInfo2.outbound = [];
24575
+ if (!fedInfo2.lensConfig) fedInfo2.lensConfig = {};
24576
+ if (!fedInfo2.partnerNames) fedInfo2.partnerNames = {};
24577
+ fedInfo2.federated = fedInfo2.federated.filter((id) => id !== spaceId1);
24578
+ fedInfo2.inbound = fedInfo2.inbound.filter((id) => id !== spaceId1);
24579
+ fedInfo2.outbound = fedInfo2.outbound.filter((id) => id !== spaceId1);
24580
+ delete fedInfo2.lensConfig[spaceId1];
24581
+ delete fedInfo2.partnerNames[spaceId1];
24443
24582
  fedInfo2.timestamp = Date.now();
24444
24583
  try {
24445
24584
  await holosphere.putGlobal("federation", fedInfo2, password2);
@@ -24472,19 +24611,19 @@ async function removeNotify(holosphere, spaceId1, spaceId2, password1 = null) {
24472
24611
  throw new Error("removeNotify: Missing required space IDs");
24473
24612
  }
24474
24613
  try {
24475
- let fedInfo = await holosphere.getGlobal("federation", spaceId1, password1);
24614
+ const fedInfo = await holosphere.getGlobal("federation", spaceId1, password1);
24476
24615
  if (!fedInfo) {
24477
24616
  throw new Error(`No federation info found for ${spaceId1}`);
24478
24617
  }
24479
- if (!fedInfo.notify) fedInfo.notify = [];
24480
- if (fedInfo.notify.includes(spaceId2)) {
24481
- fedInfo.notify = fedInfo.notify.filter((id) => id !== spaceId2);
24482
- fedInfo.timestamp = Date.now();
24483
- await holosphere.putGlobal("federation", fedInfo, password1);
24484
- return true;
24485
- } else {
24486
- return false;
24618
+ if (!fedInfo.outbound) fedInfo.outbound = [];
24619
+ if (!fedInfo.outbound.includes(spaceId2)) return false;
24620
+ fedInfo.outbound = fedInfo.outbound.filter((id) => id !== spaceId2);
24621
+ if (fedInfo.lensConfig?.[spaceId2]) {
24622
+ fedInfo.lensConfig[spaceId2].outbound = [];
24487
24623
  }
24624
+ fedInfo.timestamp = Date.now();
24625
+ await holosphere.putGlobal("federation", fedInfo, password1);
24626
+ return true;
24488
24627
  } catch (error) {
24489
24628
  throw error;
24490
24629
  }
@@ -24519,10 +24658,29 @@ async function getFederated(holosphere, holon, lens, options = {}) {
24519
24658
  if (includeLocal) {
24520
24659
  spacesToQuery.push(holon);
24521
24660
  }
24522
- if (includeFederated && fedInfo && fedInfo.federation && fedInfo.federation.length > 0) {
24523
- const federatedSpaces = maxFederatedSpaces === -1 ? fedInfo.federation : fedInfo.federation.slice(0, maxFederatedSpaces);
24661
+ if (includeFederated && fedInfo && fedInfo.inbound && fedInfo.inbound.length > 0) {
24662
+ const federatedSpaces = maxFederatedSpaces === -1 ? fedInfo.inbound : fedInfo.inbound.slice(0, maxFederatedSpaces);
24524
24663
  spacesToQuery = spacesToQuery.concat(federatedSpaces);
24525
24664
  }
24665
+ const remoteSpaces = spacesToQuery.filter((s) => s !== holon);
24666
+ const spaceNames = /* @__PURE__ */ new Map();
24667
+ await Promise.all(
24668
+ remoteSpaces.map(async (space) => {
24669
+ const name = await getHolonName(holosphere, space);
24670
+ spaceNames.set(space, name);
24671
+ })
24672
+ );
24673
+ const tagWithSource = (item, space) => {
24674
+ if (!item || space === holon) return item;
24675
+ const originName = spaceNames.get(space);
24676
+ const fed = {
24677
+ ...item._federation || {},
24678
+ origin: space,
24679
+ sourceLens: lens
24680
+ };
24681
+ if (originName) fed.originName = originName;
24682
+ return { ...item, _federation: fed };
24683
+ };
24526
24684
  for (const currentSpace of spacesToQuery) {
24527
24685
  if (queryIds && Array.isArray(queryIds)) {
24528
24686
  console.log(`Fetching specific IDs from ${currentSpace}: ${queryIds.join(", ")}`);
@@ -24531,7 +24689,7 @@ async function getFederated(holosphere, holon, lens, options = {}) {
24531
24689
  fetchPromises.push(
24532
24690
  holosphere.get(currentSpace, lens, itemId).then((item) => {
24533
24691
  if (item) {
24534
- fetchedItems.set(itemId, item);
24692
+ fetchedItems.set(itemId, tagWithSource(item, currentSpace));
24535
24693
  }
24536
24694
  }).catch((err) => console.warn(`Error fetching item ${itemId} from ${currentSpace}: ${err.message}`))
24537
24695
  );
@@ -24545,7 +24703,7 @@ async function getFederated(holosphere, holon, lens, options = {}) {
24545
24703
  holosphere.getAll(currentSpace, lens).then((items) => {
24546
24704
  for (const item of items) {
24547
24705
  if (item && item[idField] && !fetchedItems.has(item[idField])) {
24548
- fetchedItems.set(item[idField], item);
24706
+ fetchedItems.set(item[idField], tagWithSource(item, currentSpace));
24549
24707
  }
24550
24708
  }
24551
24709
  }).catch((err) => console.warn(`Error fetching all items from ${currentSpace}: ${err.message}`))
@@ -24577,24 +24735,29 @@ async function getFederated(holosphere, holon, lens, options = {}) {
24577
24735
  );
24578
24736
  console.log(`Original data found via soul path:`, JSON.stringify(originalData));
24579
24737
  if (originalData) {
24580
- result[i] = {
24581
- ...originalData,
24582
- _federation: {
24583
- isReference: true,
24584
- resolved: true,
24585
- soul: item.soul,
24586
- timestamp: Date.now()
24738
+ const withMeta = attachHologramMeta(originalData, item.soul);
24739
+ if (withMeta._hologram?.sourceHolon) {
24740
+ let sourceHolonName = spaceNames.get(withMeta._hologram.sourceHolon);
24741
+ if (sourceHolonName === void 0) {
24742
+ sourceHolonName = await getHolonName(holosphere, withMeta._hologram.sourceHolon);
24743
+ spaceNames.set(withMeta._hologram.sourceHolon, sourceHolonName);
24587
24744
  }
24588
- };
24745
+ if (sourceHolonName) {
24746
+ withMeta._hologram = {
24747
+ ...withMeta._hologram,
24748
+ sourceHolonName
24749
+ };
24750
+ }
24751
+ }
24752
+ result[i] = withMeta;
24589
24753
  } else {
24590
24754
  result[i] = {
24591
24755
  id: item.id,
24592
- _federation: {
24593
- isReference: true,
24594
- resolved: false,
24756
+ _hologram: {
24757
+ isHologram: false,
24595
24758
  soul: item.soul,
24596
24759
  error: "Referenced data not found",
24597
- timestamp: Date.now()
24760
+ resolvedAt: Date.now()
24598
24761
  }
24599
24762
  };
24600
24763
  }
@@ -24602,55 +24765,25 @@ async function getFederated(holosphere, holon, lens, options = {}) {
24602
24765
  console.warn(`Soul doesn't match expected format: ${item.soul}`);
24603
24766
  result[i] = {
24604
24767
  id: item.id,
24605
- _federation: {
24606
- isReference: true,
24607
- resolved: false,
24768
+ _hologram: {
24769
+ isHologram: false,
24608
24770
  soul: item.soul,
24609
24771
  error: "Invalid soul format",
24610
- timestamp: Date.now()
24772
+ resolvedAt: Date.now()
24611
24773
  }
24612
24774
  };
24613
24775
  }
24614
24776
  } catch (refError) {
24615
24777
  result[i] = {
24616
24778
  id: item.id,
24617
- _federation: {
24618
- isReference: true,
24619
- resolved: false,
24779
+ _hologram: {
24780
+ isHologram: false,
24620
24781
  soul: item.soul,
24621
24782
  error: refError.message || "Error resolving reference",
24622
- timestamp: Date.now()
24783
+ resolvedAt: Date.now()
24623
24784
  }
24624
24785
  };
24625
24786
  }
24626
- } else if (item._federation && item._federation.isReference) {
24627
- console.log(`Found legacy reference: ${item._federation.origin}/${item._federation.lens}/${item[idField]}`);
24628
- try {
24629
- const reference = item._federation;
24630
- console.log(`Getting original data from ${reference.origin} / ${reference.lens} / ${item[idField]}`);
24631
- const originalData = await holosphere.get(
24632
- reference.origin,
24633
- reference.lens,
24634
- item[idField],
24635
- null,
24636
- { resolveReferences: false }
24637
- // Prevent infinite recursion
24638
- );
24639
- console.log(`Original data found:`, JSON.stringify(originalData));
24640
- if (originalData) {
24641
- result[i] = {
24642
- ...originalData,
24643
- _federation: {
24644
- ...reference,
24645
- resolved: true,
24646
- timestamp: Date.now()
24647
- }
24648
- };
24649
- } else {
24650
- console.warn(`Could not resolve legacy reference: original data not found`);
24651
- }
24652
- } catch (refError) {
24653
- }
24654
24787
  }
24655
24788
  }
24656
24789
  }
@@ -24717,8 +24850,8 @@ async function propagate(holosphere, holon, lens, data, options = {}) {
24717
24850
  };
24718
24851
  try {
24719
24852
  const fedInfo = await getFederation(holosphere, holon, password);
24720
- if (fedInfo && fedInfo.federation && fedInfo.federation.length > 0 && fedInfo.notify && fedInfo.notify.length > 0) {
24721
- let spaces = fedInfo.notify;
24853
+ if (fedInfo && fedInfo.outbound && fedInfo.outbound.length > 0) {
24854
+ let spaces = fedInfo.outbound;
24722
24855
  if (targetSpaces && Array.isArray(targetSpaces) && targetSpaces.length > 0) {
24723
24856
  spaces = spaces.filter((space) => targetSpaces.includes(space));
24724
24857
  }
@@ -24730,17 +24863,17 @@ async function propagate(holosphere, holon, lens, data, options = {}) {
24730
24863
  result.skipped++;
24731
24864
  return false;
24732
24865
  }
24733
- const federateLenses = Array.isArray(spaceConfig.federate) ? spaceConfig.federate : [];
24734
- const shouldFederate = federateLenses.includes("*") || federateLenses.includes(lens);
24735
- const shouldPropagate = shouldFederate;
24866
+ const outboundLenses = Array.isArray(spaceConfig.outbound) ? spaceConfig.outbound : [];
24867
+ const shouldPropagate = outboundLenses.includes("*") || outboundLenses.includes(lens);
24736
24868
  if (!shouldPropagate) {
24737
- result.messages.push(`Propagation of lens '${lens}' to target space ${targetSpace} skipped: lens not in 'federate' configuration.`);
24869
+ result.messages.push(`Propagation of lens '${lens}' to target space ${targetSpace} skipped: lens not in 'outbound' configuration.`);
24738
24870
  result.skipped++;
24739
24871
  }
24740
24872
  return shouldPropagate;
24741
24873
  });
24742
24874
  if (spaces.length > 0) {
24743
24875
  const isAlreadyHologram = holosphere.isHologram(data);
24876
+ const ownName = await getHolonName(holosphere, holon);
24744
24877
  const propagatePromises = spaces.map(async (targetSpace) => {
24745
24878
  try {
24746
24879
  let payloadToPut;
@@ -24750,7 +24883,8 @@ async function propagate(holosphere, holon, lens, data, options = {}) {
24750
24883
  sourceLens: lens,
24751
24884
  // The lens from which this data is being propagated
24752
24885
  propagatedAt: Date.now(),
24753
- originalId: data.id
24886
+ originalId: data.id,
24887
+ ...ownName ? { originName: ownName } : {}
24754
24888
  };
24755
24889
  if (useHolograms && !isAlreadyHologram) {
24756
24890
  const newHologram = holosphere.createHologram(holon, lens, data);
@@ -24835,6 +24969,7 @@ async function propagate(holosphere, holon, lens, data, options = {}) {
24835
24969
  if (parentHexagons.length > 0) {
24836
24970
  result.parentPropagation.messages.push(`Found ${parentHexagons.length} parent hexagons to propagate to: ${parentHexagons.join(", ")}`);
24837
24971
  const isAlreadyHologram = holosphere.isHologram(data);
24972
+ const ownNameParent = await getHolonName(holosphere, holon);
24838
24973
  const parentPropagatePromises = parentHexagons.map(async (parentHexagon) => {
24839
24974
  try {
24840
24975
  let payloadToPut;
@@ -24847,8 +24982,9 @@ async function propagate(holosphere, holon, lens, data, options = {}) {
24847
24982
  originalId: data.id,
24848
24983
  propagationType: "parent",
24849
24984
  // Indicate this is parent propagation
24850
- parentLevel: holonResolution - getResolution(parentHexagon)
24985
+ parentLevel: holonResolution - getResolution(parentHexagon),
24851
24986
  // How many levels up
24987
+ ...ownNameParent ? { originName: ownNameParent } : {}
24852
24988
  };
24853
24989
  if (useHolograms && !isAlreadyHologram) {
24854
24990
  const newHologram = holosphere.createHologram(holon, lens, data);
@@ -24963,60 +25099,64 @@ async function resetFederation(holosphere, spaceId, password = null, options = {
24963
25099
  message: "No federation configuration found for this space"
24964
25100
  };
24965
25101
  }
24966
- result.federatedCount = fedInfo.federation?.length || 0;
24967
- result.notifyCount = fedInfo.notify?.length || 0;
25102
+ const allPartners = fedInfo.federated || [];
25103
+ result.federatedCount = allPartners.length;
25104
+ result.inboundCount = fedInfo.inbound?.length || 0;
25105
+ result.outboundCount = fedInfo.outbound?.length || 0;
24968
25106
  const emptyFedInfo = {
24969
25107
  id: spaceId,
24970
25108
  name: spaceName || spaceId,
24971
- federation: [],
24972
- notify: [],
25109
+ federated: [],
25110
+ inbound: [],
25111
+ outbound: [],
25112
+ lensConfig: {},
25113
+ partnerNames: {},
24973
25114
  timestamp: Date.now()
24974
25115
  };
24975
25116
  await holosphere.putGlobal("federation", emptyFedInfo, password);
24976
- if (notifyPartners && fedInfo.federation && fedInfo.federation.length > 0) {
24977
- const updatePromises = fedInfo.federation.map(async (partnerSpace) => {
25117
+ if (notifyPartners && allPartners.length > 0) {
25118
+ const updatePromises = allPartners.map(async (partnerSpace) => {
24978
25119
  try {
24979
25120
  const partnerFedInfo = await getFederation(holosphere, partnerSpace);
24980
- if (partnerFedInfo) {
24981
- if (partnerFedInfo.federation) {
24982
- partnerFedInfo.federation = partnerFedInfo.federation.filter(
24983
- (id) => id !== spaceId.toString()
24984
- );
24985
- }
24986
- if (partnerFedInfo.notify) {
24987
- partnerFedInfo.notify = partnerFedInfo.notify.filter(
24988
- (id) => id !== spaceId.toString()
24989
- );
24990
- }
24991
- partnerFedInfo.timestamp = Date.now();
24992
- await holosphere.putGlobal("federation", partnerFedInfo);
24993
- result.partnersNotified++;
24994
- return true;
25121
+ if (!partnerFedInfo) return false;
25122
+ const sid = spaceId.toString();
25123
+ if (partnerFedInfo.federated) {
25124
+ partnerFedInfo.federated = partnerFedInfo.federated.filter((id) => id !== sid);
24995
25125
  }
24996
- return false;
25126
+ if (partnerFedInfo.inbound) {
25127
+ partnerFedInfo.inbound = partnerFedInfo.inbound.filter((id) => id !== sid);
25128
+ }
25129
+ if (partnerFedInfo.outbound) {
25130
+ partnerFedInfo.outbound = partnerFedInfo.outbound.filter((id) => id !== sid);
25131
+ }
25132
+ if (partnerFedInfo.lensConfig) {
25133
+ delete partnerFedInfo.lensConfig[sid];
25134
+ }
25135
+ if (partnerFedInfo.partnerNames) {
25136
+ delete partnerFedInfo.partnerNames[sid];
25137
+ }
25138
+ partnerFedInfo.timestamp = Date.now();
25139
+ await holosphere.putGlobal("federation", partnerFedInfo);
25140
+ result.partnersNotified++;
25141
+ return true;
24997
25142
  } catch (error) {
24998
- result.errors.push({
24999
- partner: partnerSpace,
25000
- error: error.message
25001
- });
25143
+ result.errors.push({ partner: partnerSpace, error: error.message });
25002
25144
  return false;
25003
25145
  }
25004
25146
  });
25005
25147
  await Promise.all(updatePromises);
25006
25148
  }
25007
- if (fedInfo.federation && fedInfo.federation.length > 0) {
25008
- for (const partnerSpace of fedInfo.federation) {
25009
- try {
25010
- const metaId = `${spaceId}_${partnerSpace}`;
25011
- const altMetaId = `${partnerSpace}_${spaceId}`;
25012
- const meta = await holosphere.getGlobal("federationMeta", metaId) || await holosphere.getGlobal("federationMeta", altMetaId);
25013
- if (meta) {
25014
- meta.status = "inactive";
25015
- meta.endedAt = Date.now();
25016
- await holosphere.putGlobal("federationMeta", meta);
25017
- }
25018
- } catch (error) {
25149
+ for (const partnerSpace of allPartners) {
25150
+ try {
25151
+ const metaId = `${spaceId}_${partnerSpace}`;
25152
+ const altMetaId = `${partnerSpace}_${spaceId}`;
25153
+ const meta = await holosphere.getGlobal("federationMeta", metaId) || await holosphere.getGlobal("federationMeta", altMetaId);
25154
+ if (meta) {
25155
+ meta.status = "inactive";
25156
+ meta.endedAt = Date.now();
25157
+ await holosphere.putGlobal("federationMeta", meta);
25019
25158
  }
25159
+ } catch {
25020
25160
  }
25021
25161
  }
25022
25162
  result.success = true;
@@ -25031,7 +25171,6 @@ async function resetFederation(holosphere, spaceId, password = null, options = {
25031
25171
  }
25032
25172
 
25033
25173
  // schema.js
25034
- var import__ = __toESM(require__(), 1);
25035
25174
  async function setSchema(holoInstance, lens, schema) {
25036
25175
  if (!lens || !schema) {
25037
25176
  throw new Error("setSchema: Missing required parameters");
@@ -25113,6 +25252,45 @@ function clearSchemaCache(holoInstance, lens = null) {
25113
25252
  }
25114
25253
 
25115
25254
  // content.js
25255
+ function sanitizeForStorage(value, path = "", seen = /* @__PURE__ */ new WeakSet(), warnings = []) {
25256
+ if (value === null) return null;
25257
+ const t = typeof value;
25258
+ if (t === "number") {
25259
+ if (!Number.isFinite(value)) {
25260
+ warnings.push(`${path || "<root>"}: ${value} (non-finite number)`);
25261
+ return void 0;
25262
+ }
25263
+ return value;
25264
+ }
25265
+ if (t === "string" || t === "boolean") return value;
25266
+ if (t === "undefined" || t === "function" || t === "symbol" || t === "bigint") {
25267
+ warnings.push(`${path || "<root>"}: ${t}`);
25268
+ return void 0;
25269
+ }
25270
+ if (t === "object") {
25271
+ if (seen.has(value)) {
25272
+ warnings.push(`${path || "<root>"}: circular reference`);
25273
+ return void 0;
25274
+ }
25275
+ seen.add(value);
25276
+ if (Array.isArray(value)) {
25277
+ const out2 = [];
25278
+ for (let i = 0; i < value.length; i++) {
25279
+ const cleaned = sanitizeForStorage(value[i], `${path}[${i}]`, seen, warnings);
25280
+ out2.push(cleaned === void 0 ? null : cleaned);
25281
+ }
25282
+ return out2;
25283
+ }
25284
+ const out = {};
25285
+ for (const k of Object.keys(value)) {
25286
+ const cleaned = sanitizeForStorage(value[k], path ? `${path}.${k}` : k, seen, warnings);
25287
+ if (cleaned !== void 0) out[k] = cleaned;
25288
+ }
25289
+ return out;
25290
+ }
25291
+ warnings.push(`${path || "<root>"}: unsupported type ${t}`);
25292
+ return void 0;
25293
+ }
25116
25294
  async function put(holoInstance, holon, lens, data, password = null, options = {}) {
25117
25295
  if (!data) {
25118
25296
  throw new Error("put: Missing required data parameter");
@@ -25210,10 +25388,16 @@ async function put(holoInstance, holon, lens, data, password = null, options = {
25210
25388
  }
25211
25389
  return new Promise((resolve, reject) => {
25212
25390
  try {
25213
- let dataToStore = { ...data };
25214
- if (dataToStore._meta !== void 0) {
25215
- delete dataToStore._meta;
25391
+ const sanitizeWarnings = [];
25392
+ let dataToStore = sanitizeForStorage(data, "", /* @__PURE__ */ new WeakSet(), sanitizeWarnings) || {};
25393
+ if (sanitizeWarnings.length > 0) {
25394
+ console.warn(
25395
+ `holosphere.put: sanitized ${sanitizeWarnings.length} field(s) at ${targetHolon}/${targetLens}/${targetKey} (id=${data.id}):`,
25396
+ sanitizeWarnings
25397
+ );
25216
25398
  }
25399
+ if (dataToStore._meta !== void 0) delete dataToStore._meta;
25400
+ if (dataToStore._hologram !== void 0) delete dataToStore._hologram;
25217
25401
  const payload = JSON.stringify(dataToStore);
25218
25402
  const putCallback = async (ack) => {
25219
25403
  if (ack.err) {
@@ -25503,7 +25687,8 @@ async function getAll(holoInstance, holon, lens, password = null) {
25503
25687
  const output = /* @__PURE__ */ new Map();
25504
25688
  const pendingProcessing = [];
25505
25689
  const dataPath = password ? user.get("private").get(lens) : holoInstance.gun.get(holoInstance.appname).get(holon).get(lens);
25506
- dataPath.once((data) => {
25690
+ const shallowOnce = () => new Promise((res) => dataPath.once((d) => res(d)));
25691
+ const processShallow = (data) => {
25507
25692
  if (!data) {
25508
25693
  resolve([]);
25509
25694
  return;
@@ -25520,72 +25705,75 @@ async function getAll(holoInstance, holon, lens, password = null) {
25520
25705
  resolve([]);
25521
25706
  return;
25522
25707
  }
25523
- let receivedCount = 0;
25524
- dataPath.map().once(async (itemData, key) => {
25525
- if (!itemData || key === "_") {
25526
- receivedCount++;
25527
- if (receivedCount >= expectedCount) {
25528
- await Promise.all(pendingProcessing);
25529
- resolve(Array.from(output.values()));
25530
- }
25531
- return;
25532
- }
25533
- const processingPromise = (async () => {
25534
- try {
25535
- const parsed = await holoInstance.parse(itemData);
25536
- if (!parsed || !parsed.id) return;
25537
- if (holoInstance.isHologram(parsed)) {
25538
- try {
25539
- const resolved = await holoInstance.resolveHologram(parsed, {
25540
- followHolograms: true,
25541
- maxDepth: 10,
25542
- currentDepth: 0
25543
- });
25544
- if (resolved === null) {
25545
- console.warn(`Broken hologram detected in getAll for key ${key}. Removing it...`);
25546
- try {
25547
- await holoInstance.delete(holon, lens, key, password);
25548
- } catch (cleanupError) {
25549
- console.error(`Failed to remove broken hologram at ${holon}/${lens}/${key}:`, cleanupError);
25550
- }
25551
- return;
25708
+ const processItem = async (itemData, key) => {
25709
+ if (!itemData) return;
25710
+ try {
25711
+ const parsed = await holoInstance.parse(itemData);
25712
+ if (!parsed || !parsed.id) return;
25713
+ if (holoInstance.isHologram(parsed)) {
25714
+ try {
25715
+ const resolved = await holoInstance.resolveHologram(parsed, {
25716
+ followHolograms: true,
25717
+ maxDepth: 10,
25718
+ currentDepth: 0
25719
+ });
25720
+ if (resolved === null) {
25721
+ console.warn(`Broken hologram detected in getAll for key ${key}. Removing it...`);
25722
+ try {
25723
+ await holoInstance.delete(holon, lens, key, password);
25724
+ } catch (cleanupError) {
25725
+ console.error(`Failed to remove broken hologram at ${holon}/${lens}/${key}:`, cleanupError);
25552
25726
  }
25553
- if (resolved && resolved !== parsed) {
25554
- if (schema) {
25555
- const valid = holoInstance.validator.validate(schema, resolved);
25556
- if (valid || !holoInstance.strict) {
25557
- output.set(resolved.id, resolved);
25558
- }
25559
- } else {
25727
+ return;
25728
+ }
25729
+ if (resolved && resolved !== parsed) {
25730
+ if (schema) {
25731
+ const valid = holoInstance.validator.validate(schema, resolved);
25732
+ if (valid || !holoInstance.strict) {
25560
25733
  output.set(resolved.id, resolved);
25561
25734
  }
25562
- return;
25735
+ } else {
25736
+ output.set(resolved.id, resolved);
25563
25737
  }
25564
- } catch (hologramError) {
25565
- console.error(`Error resolving hologram for key ${key}:`, hologramError);
25566
25738
  return;
25567
25739
  }
25740
+ } catch (hologramError) {
25741
+ console.error(`Error resolving hologram for key ${key}:`, hologramError);
25742
+ return;
25568
25743
  }
25569
- if (schema) {
25570
- const valid = holoInstance.validator.validate(schema, parsed);
25571
- if (valid || !holoInstance.strict) {
25572
- output.set(parsed.id, parsed);
25573
- }
25574
- } else {
25744
+ }
25745
+ if (schema) {
25746
+ const valid = holoInstance.validator.validate(schema, parsed);
25747
+ if (valid || !holoInstance.strict) {
25575
25748
  output.set(parsed.id, parsed);
25576
25749
  }
25577
- } catch (error) {
25578
- console.error("Error processing data:", error);
25750
+ } else {
25751
+ output.set(parsed.id, parsed);
25579
25752
  }
25580
- })();
25581
- pendingProcessing.push(processingPromise);
25582
- receivedCount++;
25583
- if (receivedCount >= expectedCount) {
25584
- await Promise.all(pendingProcessing);
25585
- resolve(Array.from(output.values()));
25753
+ } catch (error) {
25754
+ console.error("Error processing data:", error);
25586
25755
  }
25587
- });
25588
- });
25756
+ };
25757
+ Promise.all(keys.map((key) => {
25758
+ const inline = data[key];
25759
+ if (typeof inline !== "object" || inline === null) {
25760
+ return processItem(inline, key);
25761
+ }
25762
+ return new Promise((resolveItem) => {
25763
+ dataPath.get(key).once((itemData) => {
25764
+ processItem(itemData, key).then(resolveItem, resolveItem);
25765
+ });
25766
+ });
25767
+ })).then(() => resolve(Array.from(output.values())));
25768
+ };
25769
+ (async () => {
25770
+ let data = await shallowOnce();
25771
+ if (!data) {
25772
+ await new Promise((r) => setTimeout(r, 1500));
25773
+ data = await shallowOnce();
25774
+ }
25775
+ processShallow(data);
25776
+ })();
25589
25777
  });
25590
25778
  } catch (error) {
25591
25779
  console.error("Error in getAll:", error);
@@ -26064,6 +26252,9 @@ async function putGlobal(holoInstance, tableName, data, password = null) {
26064
26252
  if (dataToStore._meta !== void 0) {
26065
26253
  delete dataToStore._meta;
26066
26254
  }
26255
+ if (dataToStore._hologram !== void 0) {
26256
+ delete dataToStore._hologram;
26257
+ }
26067
26258
  const payload = JSON.stringify(dataToStore);
26068
26259
  const isHologram2 = holoInstance.isHologram(dataToStore);
26069
26260
  const dataPath = password ? user.get("private").get(tableName) : holoInstance.gun.get(holoInstance.appname).get(tableName);
@@ -26264,7 +26455,8 @@ async function getAllGlobal(holoInstance, tableName, password = null) {
26264
26455
  const output = [];
26265
26456
  const pendingProcessing = [];
26266
26457
  const dataPath = password ? user.get("private").get(tableName) : holoInstance.gun.get(holoInstance.appname).get(tableName);
26267
- dataPath.once((data) => {
26458
+ const shallowOnce = () => new Promise((res) => dataPath.once((d) => res(d)));
26459
+ const processShallow = (data) => {
26268
26460
  if (!data) {
26269
26461
  resolve([]);
26270
26462
  return;
@@ -26281,52 +26473,55 @@ async function getAllGlobal(holoInstance, tableName, password = null) {
26281
26473
  resolve([]);
26282
26474
  return;
26283
26475
  }
26284
- let receivedCount = 0;
26285
- dataPath.map().once(async (itemData, key) => {
26286
- if (!itemData || key === "_") {
26287
- receivedCount++;
26288
- if (receivedCount >= expectedCount) {
26289
- await Promise.all(pendingProcessing);
26290
- resolve(output);
26291
- }
26292
- return;
26293
- }
26294
- const processingPromise = (async () => {
26295
- try {
26296
- const parsed = await holoInstance.parse(itemData);
26297
- if (!parsed) return;
26298
- if (holoInstance.isHologram(parsed)) {
26299
- const resolved = await holoInstance.resolveHologram(parsed, {
26300
- followHolograms: true
26301
- });
26302
- if (resolved === null) {
26303
- try {
26304
- await holoInstance.deleteGlobal(tableName, key, password);
26305
- } catch (deleteError) {
26306
- console.error(`Failed to delete invalid global hologram at ${tableName}/${key}:`, deleteError);
26307
- }
26308
- return;
26309
- }
26310
- if (resolved !== parsed) {
26311
- output.push(resolved);
26312
- } else {
26313
- output.push(parsed);
26476
+ const processItem = async (itemData, key) => {
26477
+ if (!itemData) return;
26478
+ try {
26479
+ const parsed = await holoInstance.parse(itemData);
26480
+ if (!parsed) return;
26481
+ if (holoInstance.isHologram(parsed)) {
26482
+ const resolved = await holoInstance.resolveHologram(parsed, {
26483
+ followHolograms: true
26484
+ });
26485
+ if (resolved === null) {
26486
+ try {
26487
+ await holoInstance.deleteGlobal(tableName, key, password);
26488
+ } catch (deleteError) {
26489
+ console.error(`Failed to delete invalid global hologram at ${tableName}/${key}:`, deleteError);
26314
26490
  }
26491
+ return;
26492
+ }
26493
+ if (resolved !== parsed) {
26494
+ output.push(resolved);
26315
26495
  } else {
26316
26496
  output.push(parsed);
26317
26497
  }
26318
- } catch (error) {
26319
- console.error("Error parsing data:", error);
26498
+ } else {
26499
+ output.push(parsed);
26320
26500
  }
26321
- })();
26322
- pendingProcessing.push(processingPromise);
26323
- receivedCount++;
26324
- if (receivedCount >= expectedCount) {
26325
- await Promise.all(pendingProcessing);
26326
- resolve(output);
26501
+ } catch (error) {
26502
+ console.error("Error parsing data:", error);
26327
26503
  }
26328
- });
26329
- });
26504
+ };
26505
+ Promise.all(keys.map((key) => {
26506
+ const inline = data[key];
26507
+ if (typeof inline !== "object" || inline === null) {
26508
+ return processItem(inline, key);
26509
+ }
26510
+ return new Promise((resolveItem) => {
26511
+ dataPath.get(key).once((itemData) => {
26512
+ processItem(itemData, key).then(resolveItem, resolveItem);
26513
+ });
26514
+ });
26515
+ })).then(() => resolve(output));
26516
+ };
26517
+ (async () => {
26518
+ let data = await shallowOnce();
26519
+ if (!data) {
26520
+ await new Promise((r) => setTimeout(r, 1500));
26521
+ data = await shallowOnce();
26522
+ }
26523
+ processShallow(data);
26524
+ })();
26330
26525
  });
26331
26526
  } catch (error) {
26332
26527
  console.error("Error in getAllGlobal:", error);
@@ -26616,116 +26811,6 @@ async function subscribeGlobal(holoInstance, tableName, key, callback, options =
26616
26811
  };
26617
26812
  }
26618
26813
 
26619
- // hologram.js
26620
- function createHologram(holoInstance, holon, lens, data) {
26621
- if (isHologram(data)) {
26622
- return {
26623
- id: data.id,
26624
- soul: data.soul
26625
- };
26626
- }
26627
- if (!holon || !lens || !data || !data.id) {
26628
- throw new Error("createHologram: Missing required parameters for non-hologram data");
26629
- }
26630
- const soul = `${holoInstance.appname}/${holon}/${lens}/${data.id}`;
26631
- return {
26632
- id: data.id,
26633
- soul
26634
- };
26635
- }
26636
- function parseSoulPath(soul) {
26637
- if (typeof soul !== "string") return null;
26638
- const parts = soul.split("/");
26639
- if (parts.length < 4) return null;
26640
- const key = parts.slice(3).join("/");
26641
- return {
26642
- appname: parts[0],
26643
- holon: parts[1],
26644
- lens: parts[2],
26645
- key
26646
- };
26647
- }
26648
- function isHologram(data) {
26649
- if (!data || typeof data !== "object") {
26650
- return false;
26651
- }
26652
- if (data.id && typeof data.soul === "string" && data.soul.length > 0) {
26653
- if (data.soul.includes("/")) {
26654
- return true;
26655
- }
26656
- }
26657
- return false;
26658
- }
26659
- async function resolveHologram(holoInstance, hologram, options = {}) {
26660
- if (!isHologram(hologram)) {
26661
- return hologram;
26662
- }
26663
- const {
26664
- followHolograms = true,
26665
- visited = /* @__PURE__ */ new Set(),
26666
- maxDepth = 10,
26667
- currentDepth = 0
26668
- } = options;
26669
- if (currentDepth >= maxDepth) {
26670
- console.warn(`!!! Maximum resolution depth (${maxDepth}) reached for soul: ${hologram.soul}. Stopping resolution.`);
26671
- return null;
26672
- }
26673
- if (hologram.soul && visited.has(hologram.soul)) {
26674
- console.warn(`!!! CIRCULAR hologram detected for soul: ${hologram.soul}. Breaking loop.`);
26675
- return null;
26676
- }
26677
- try {
26678
- if (hologram.soul) {
26679
- const soulInfo = parseSoulPath(hologram.soul);
26680
- if (!soulInfo) {
26681
- console.warn(`Invalid soul format: ${hologram.soul}`);
26682
- return null;
26683
- }
26684
- const nextVisited = new Set(visited);
26685
- nextVisited.add(hologram.soul);
26686
- const originalData = await holoInstance.get(
26687
- soulInfo.holon,
26688
- soulInfo.lens,
26689
- soulInfo.key,
26690
- null,
26691
- {
26692
- resolveHolograms: followHolograms,
26693
- visited: nextVisited,
26694
- maxDepth,
26695
- currentDepth: currentDepth + 1
26696
- }
26697
- );
26698
- if (originalData && !originalData._invalidHologram) {
26699
- return {
26700
- ...originalData,
26701
- _meta: {
26702
- ...originalData._meta || {},
26703
- // Preserve original _meta
26704
- resolvedFromHologram: true,
26705
- // This is now the primary indicator
26706
- hologramSoul: hologram.soul,
26707
- // Clarified meta field
26708
- resolutionDepth: currentDepth
26709
- }
26710
- };
26711
- } else {
26712
- console.warn(`!!! Original data NOT FOUND for soul: ${hologram.soul}. Removing broken hologram.`);
26713
- return null;
26714
- }
26715
- } else {
26716
- console.warn("!!! resolveHologram called with object missing soul:", hologram);
26717
- return null;
26718
- }
26719
- } catch (error) {
26720
- if (error.message?.startsWith("CIRCULAR_REFERENCE")) {
26721
- console.warn(`!!! Circular reference detected during hologram resolution: ${error.message}`);
26722
- return null;
26723
- }
26724
- console.error(`!!! Error resolving hologram: ${error.message}`, error);
26725
- return null;
26726
- }
26727
- }
26728
-
26729
26814
  // compute.js
26730
26815
  async function computeHierarchy(holoInstance, holon, lens, options, maxLevels = 15, password = null) {
26731
26816
  let currentHolon = holon;
@@ -30199,7 +30284,7 @@ var HoloSphere = class {
30199
30284
  this._privateKey = null;
30200
30285
  }
30201
30286
  console.log("HoloSphere v" + HOLOSPHERE_VERSION);
30202
- this.validator = new import__2.default({
30287
+ this.validator = new import__.default({
30203
30288
  allErrors: true,
30204
30289
  strict: false,
30205
30290
  validateSchema: true
@@ -30351,6 +30436,9 @@ var HoloSphere = class {
30351
30436
  async resolveHologram(hologram, options = {}) {
30352
30437
  return resolveHologram(this, hologram, options);
30353
30438
  }
30439
+ attachHologramMeta(originalData, hologramSoul) {
30440
+ return attachHologramMeta(originalData, hologramSoul);
30441
+ }
30354
30442
  // ================================ COMPUTE FUNCTIONS ================================
30355
30443
  async computeHierarchy(holon, lens, options, maxLevels = 15, password = null) {
30356
30444
  return computeHierarchy(this, holon, lens, options, maxLevels, password);
@@ -30393,41 +30481,37 @@ var HoloSphere = class {
30393
30481
  return federate(this, holonId1, holonId2, password1, password2, bidirectional, lensConfig);
30394
30482
  }
30395
30483
  /**
30396
- * v2-compatible federation creation.
30397
- * Maps v2 options to v1 federate() call.
30484
+ * Convenience wrapper around federate() for the common bidirectional case.
30398
30485
  * @param {string} sourceHolon - Source holon ID
30399
30486
  * @param {string} targetHolon - Target holon ID
30400
30487
  * @param {object} [options] - Federation options
30401
- * @param {object} [options.lensConfig] - Lens configuration
30402
- * @param {string} [options.partnerName] - Name of the partner holon
30403
- * @param {boolean} [options.skipPropagation] - Skip data propagation
30488
+ * @param {object} [options.lensConfig] - Lens config from sourceHolon's perspective
30489
+ * @param {string[]} [options.lensConfig.inbound] - Lenses sourceHolon receives from targetHolon
30490
+ * @param {string[]} [options.lensConfig.outbound] - Lenses sourceHolon sends to targetHolon
30491
+ * @param {string} [options.partnerName] - Display name for the partner
30404
30492
  * @returns {Promise<boolean>}
30405
30493
  */
30406
30494
  async federateHolon(sourceHolon, targetHolon, options = {}) {
30407
30495
  const lensConfig = options.lensConfig || {};
30408
- const lenses = lensConfig.lenses || lensConfig.outbound || lensConfig.federate || [];
30409
- if (options.partnerName) {
30496
+ const inbound = Array.isArray(lensConfig.inbound) ? lensConfig.inbound : [];
30497
+ const outbound = Array.isArray(lensConfig.outbound) ? lensConfig.outbound : [];
30498
+ const ok = await federate(this, sourceHolon, targetHolon, null, null, true, {
30499
+ inbound,
30500
+ outbound
30501
+ });
30502
+ if (ok && options.partnerName) {
30410
30503
  try {
30411
- const fedInfo = await this.getFederation(sourceHolon) || {
30412
- id: sourceHolon,
30413
- name: sourceHolon,
30414
- federation: [],
30415
- notify: [],
30416
- lensConfig: {},
30417
- partnerNames: {},
30418
- timestamp: Date.now()
30419
- };
30420
- if (!fedInfo.partnerNames) fedInfo.partnerNames = {};
30421
- fedInfo.partnerNames[targetHolon] = options.partnerName;
30422
- await this.putGlobal("federation", fedInfo);
30504
+ const fedInfo = await this.getFederation(sourceHolon);
30505
+ if (fedInfo) {
30506
+ if (!fedInfo.partnerNames) fedInfo.partnerNames = {};
30507
+ fedInfo.partnerNames[targetHolon] = options.partnerName;
30508
+ await this.putGlobal("federation", fedInfo);
30509
+ }
30423
30510
  } catch (e) {
30424
30511
  console.warn("Failed to store partner name:", e.message);
30425
30512
  }
30426
30513
  }
30427
- return federate(this, sourceHolon, targetHolon, null, null, true, {
30428
- federate: lenses,
30429
- notify: lenses
30430
- });
30514
+ return ok;
30431
30515
  }
30432
30516
  /**
30433
30517
  * v2-compatible federation removal.