holosphere
Version:
Holonic Geospatial Communication Infrastructure
183 lines (163 loc) • 7.11 kB
JavaScript
// holo_hologram.js
/**
* Creates a soul hologram object for a data item
* @param {HoloSphere} holoInstance - The HoloSphere instance.
* @param {string} holon - The holon where the original data is stored
* @param {string} lens - The lens where the original data is stored
* @param {object} data - The data to create a hologram for
* @returns {object} - A hologram object with id and soul
*/
export function createHologram(holoInstance, holon, lens, data) {
// Check if the input data is already a hologram
if (isHologram(data)) {
// If it is, just return its existing ID and Soul, ignoring the provided holon/lens
return {
id: data.id,
soul: data.soul
};
}
// Original logic: Input data is not a hologram, create a new soul path
if (!holon || !lens || !data || !data.id) {
throw new Error('createHologram: Missing required parameters for non-hologram data');
}
const soul = `${holoInstance.appname}/${holon}/${lens}/${data.id}`;
return {
id: data.id,
soul: soul
};
}
/**
* Parses a soul path into its components.
* @param {string} soul - The soul path (e.g., "app/holon/lens/key").
* @returns {object|null} - An object with appname, holon, lens, key, or null if invalid.
*/
export function parseSoulPath(soul) {
if (typeof soul !== 'string') return null;
const parts = soul.split('/');
if (parts.length < 4) return null; // Must have at least app/holon/lens/key
// Reconstruct key if it contained slashes (though generally disallowed by getNodeRef validation)
const key = parts.slice(3).join('/');
return {
appname: parts[0],
holon: parts[1],
lens: parts[2],
key: key
};
}
/**
* Checks if an object is a hologram.
* This function checks for the presence and type of `id` and `soul` properties,
* suitable for identifying holograms represented as plain JavaScript objects.
* It also performs a basic check that the `soul` contains '/' as expected for a path.
* @param {object | null | undefined} data - The data to check.
* @returns {boolean} - True if the object is considered a hologram.
*/
export function isHologram(data) {
if (!data || typeof data !== 'object') {
return false;
}
// Basic check: Does it have an 'id' and a 'soul' which is a non-empty string?
if (data.id && typeof data.soul === 'string' && data.soul.length > 0) {
// Optional stricter check: Does the soul look like a valid path?
// This prevents objects like { id: 1, soul: "hello" } from being counted.
// We can use a simplified check here or rely on parseSoulPath failing later.
if (data.soul.includes('/')) { // Simple check for path structure
return true;
}
}
return false;
}
/**
* Resolves a hologram to its actual data
* @param {HoloSphere} holoInstance - The HoloSphere instance.
* @param {object} hologram - The hologram to resolve
* @param {object} [options] - Optional parameters
* @param {boolean} [options.followHolograms=true] - Whether to follow nested holograms
* @param {Set<string>} [options.visited] - Internal use: Tracks visited souls to prevent loops
* @param {number} [options.maxDepth=10] - Maximum resolution depth to prevent infinite loops
* @param {number} [options.currentDepth=0] - Current resolution depth
* @returns {Promise<object|null>} - The resolved data, null if resolution failed due to target not found, or the original hologram for circular/invalid cases.
*/
export async function resolveHologram(holoInstance, hologram, options = {}) {
if (!isHologram(hologram)) {
return hologram; // Not a hologram, return as is
}
const {
followHolograms = true,
visited = new Set(),
maxDepth = 10,
currentDepth = 0
} = options;
// Check depth limit first
if (currentDepth >= maxDepth) {
console.warn(`!!! Maximum resolution depth (${maxDepth}) reached for soul: ${hologram.soul}. Stopping resolution.`);
return null;
}
// Check for circular hologram
if (hologram.soul && visited.has(hologram.soul)) {
console.warn(`!!! CIRCULAR hologram detected for soul: ${hologram.soul}. Breaking loop.`);
return null;
}
try {
// Handle direct soul hologram
if (hologram.soul) {
const soulInfo = parseSoulPath(hologram.soul);
if (!soulInfo) {
console.warn(`Invalid soul format: ${hologram.soul}`);
return null;
}
// Add current soul to visited set for THIS resolution path
const nextVisited = new Set(visited);
nextVisited.add(hologram.soul);
// Only log when actually resolving a hologram for debugging if needed
// console.log(`Resolving hologram: ${hologram.soul}`);
// Get original data with increased depth
const originalData = await holoInstance.get(
soulInfo.holon,
soulInfo.lens,
soulInfo.key,
null,
{
resolveHolograms: followHolograms,
visited: nextVisited,
maxDepth: maxDepth,
currentDepth: currentDepth + 1
}
);
if (originalData && !originalData._invalidHologram) {
// Structure for the returned object - isHologram (top-level) is removed
return {
...originalData,
_meta: {
...(originalData._meta || {}), // Preserve original _meta
resolvedFromHologram: true, // This is now the primary indicator
hologramSoul: hologram.soul, // Clarified meta field
resolutionDepth: currentDepth
}
};
} else {
console.warn(`!!! Original data NOT FOUND for soul: ${hologram.soul}. Removing broken hologram.`);
// Return null to indicate the hologram should be removed
return null;
}
} else {
// Should not happen if isHologram() passed
console.warn('!!! resolveHologram called with object missing soul:', hologram);
return null;
}
} catch (error) {
if (error.message?.startsWith('CIRCULAR_REFERENCE')) {
console.warn(`!!! Circular reference detected during hologram resolution: ${error.message}`);
return null;
}
console.error(`!!! Error resolving hologram: ${error.message}`, error);
return null; // Return null on error to prevent loops
}
}
// Export all hologram operations as default
export default {
createHologram,
parseSoulPath,
isHologram,
resolveHologram
};