holosphere 1.1.11 → 1.1.13
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 +175 -26
- package/federation.js +252 -117
- package/global.js +174 -9
- package/holosphere-bundle.esm.js +33184 -0
- package/holosphere-bundle.js +33206 -0
- package/holosphere-bundle.min.js +38 -0
- package/holosphere.d.ts +15 -0
- package/holosphere.js +2 -2
- package/node.js +92 -7
- package/package.json +78 -3
- package/.cursor/rules/futura.mdc +0 -55
- package/examples/federation.js +0 -162
- package/examples/holograms.js +0 -175
- package/test/auth.test.js +0 -275
- package/test/delete.test.js +0 -229
- package/test/federation.test.js +0 -349
- package/test/hologram.test.js +0 -316
- package/test/holosphere.test.js +0 -354
- package/test/subscription.test.js +0 -364
package/federation.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* Provides methods for creating, managing, and using federated spaces
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import * as h3 from 'h3-js';
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Creates a federation relationship between two spaces
|
|
@@ -330,14 +331,24 @@ export async function unfederate(holosphere, spaceId1, spaceId2, password1 = nul
|
|
|
330
331
|
// For now, we'll let it proceed to attempt metadata cleanup, but a throw here might be valid.
|
|
331
332
|
}
|
|
332
333
|
|
|
333
|
-
if (!fedInfo1
|
|
334
|
-
// If fedInfo1
|
|
335
|
-
console.warn(`No federation
|
|
334
|
+
if (!fedInfo1) {
|
|
335
|
+
// If fedInfo1 doesn't exist, log and proceed to metadata cleanup.
|
|
336
|
+
console.warn(`No federation info found for ${spaceId1}. Skipping its update.`);
|
|
336
337
|
} else {
|
|
337
|
-
//
|
|
338
|
+
// Ensure arrays exist
|
|
339
|
+
if (!fedInfo1.federation) fedInfo1.federation = [];
|
|
340
|
+
if (!fedInfo1.notify) fedInfo1.notify = [];
|
|
341
|
+
|
|
342
|
+
// Update first space federation info - remove from both federation and notify arrays
|
|
343
|
+
const originalFederationLength = fedInfo1.federation.length;
|
|
344
|
+
const originalNotifyLength = fedInfo1.notify.length;
|
|
345
|
+
|
|
338
346
|
fedInfo1.federation = fedInfo1.federation.filter(id => id !== spaceId2);
|
|
347
|
+
fedInfo1.notify = fedInfo1.notify.filter(id => id !== spaceId2);
|
|
339
348
|
fedInfo1.timestamp = Date.now();
|
|
340
349
|
|
|
350
|
+
console.log(`Unfederate: Removed ${spaceId2} from ${spaceId1}: federation ${originalFederationLength} -> ${fedInfo1.federation.length}, notify ${originalNotifyLength} -> ${fedInfo1.notify.length}`);
|
|
351
|
+
|
|
341
352
|
try {
|
|
342
353
|
await holosphere.putGlobal('federation', fedInfo1, password1);
|
|
343
354
|
} catch (error) {
|
|
@@ -743,6 +754,8 @@ export async function getFederated(holosphere, holon, lens, options = {}) {
|
|
|
743
754
|
* @param {boolean} [options.useHolograms=true] - Use holograms for propagation (default: true)
|
|
744
755
|
* @param {string[]} [options.targetSpaces] - Specific target spaces to propagate to (defaults to all federated spaces)
|
|
745
756
|
* @param {string} [options.password] - Password for accessing the source holon (if needed)
|
|
757
|
+
* @param {boolean} [options.propagateToParents=true] - Whether to automatically propagate to parent hexagons (default: true)
|
|
758
|
+
* @param {number} [options.maxParentLevels=15] - Maximum number of parent levels to propagate to (default: 15)
|
|
746
759
|
* @returns {Promise<object>} - Result with success count and errors
|
|
747
760
|
*/
|
|
748
761
|
export async function propagate(holosphere, holon, lens, data, options = {}) {
|
|
@@ -750,147 +763,269 @@ export async function propagate(holosphere, holon, lens, data, options = {}) {
|
|
|
750
763
|
throw new Error('propagate: Missing required parameters');
|
|
751
764
|
}
|
|
752
765
|
// Default propagation options
|
|
753
|
-
const {
|
|
766
|
+
const {
|
|
767
|
+
useHolograms = true,
|
|
768
|
+
targetSpaces = null,
|
|
769
|
+
password = null,
|
|
770
|
+
propagateToParents = true,
|
|
771
|
+
maxParentLevels = 15
|
|
772
|
+
} = options;
|
|
754
773
|
|
|
755
774
|
const result = {
|
|
756
775
|
success: 0,
|
|
757
776
|
errors: 0,
|
|
758
777
|
skipped: 0,
|
|
759
|
-
messages: []
|
|
778
|
+
messages: [],
|
|
779
|
+
parentPropagation: {
|
|
780
|
+
success: 0,
|
|
781
|
+
errors: 0,
|
|
782
|
+
skipped: 0,
|
|
783
|
+
messages: []
|
|
784
|
+
}
|
|
760
785
|
};
|
|
761
786
|
|
|
762
787
|
try {
|
|
788
|
+
// ================================ FEDERATION PROPAGATION ================================
|
|
789
|
+
|
|
763
790
|
// Get federation info for this holon using getFederation
|
|
764
791
|
const fedInfo = await getFederation(holosphere, holon, password);
|
|
765
792
|
|
|
766
|
-
//
|
|
767
|
-
if (
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
message: `No federation found for ${holon}`
|
|
771
|
-
};
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// If no notification list or it's empty, return with message
|
|
775
|
-
if (!fedInfo.notify || fedInfo.notify.length === 0) {
|
|
776
|
-
return {
|
|
777
|
-
...result,
|
|
778
|
-
message: `No notification targets found for ${holon}`
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
// Filter federation spaces to those in notify list
|
|
783
|
-
let spaces = fedInfo.notify;
|
|
784
|
-
|
|
785
|
-
// Further filter by targetSpaces if provided
|
|
786
|
-
if (targetSpaces && Array.isArray(targetSpaces) && targetSpaces.length > 0) {
|
|
787
|
-
spaces = spaces.filter(space => targetSpaces.includes(space));
|
|
788
|
-
}
|
|
789
|
-
|
|
790
|
-
if (spaces.length === 0) {
|
|
791
|
-
return {
|
|
792
|
-
...result,
|
|
793
|
-
message: 'No valid target spaces found after filtering'
|
|
794
|
-
};
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
// Filter spaces based on lens configuration
|
|
798
|
-
spaces = spaces.filter(targetSpace => {
|
|
799
|
-
const spaceConfig = fedInfo.lensConfig?.[targetSpace];
|
|
800
|
-
if (!spaceConfig) {
|
|
801
|
-
result.messages.push(`No lens configuration for target space ${targetSpace}. Skipping propagation of lens '${lens}'.`);
|
|
802
|
-
result.skipped++;
|
|
803
|
-
return false;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
// Ensure .federate is an array before calling .includes
|
|
807
|
-
const federateLenses = Array.isArray(spaceConfig.federate) ? spaceConfig.federate : [];
|
|
808
|
-
|
|
809
|
-
const shouldFederate = federateLenses.includes('*') || federateLenses.includes(lens);
|
|
810
|
-
|
|
811
|
-
// Propagation now only depends on the 'federate' list configuration for the lens
|
|
812
|
-
const shouldPropagate = shouldFederate;
|
|
793
|
+
// Only perform federation propagation if there's valid federation info
|
|
794
|
+
if (fedInfo && fedInfo.federation && fedInfo.federation.length > 0 && fedInfo.notify && fedInfo.notify.length > 0) {
|
|
795
|
+
// Filter federation spaces to those in notify list
|
|
796
|
+
let spaces = fedInfo.notify;
|
|
813
797
|
|
|
814
|
-
if
|
|
815
|
-
|
|
816
|
-
|
|
798
|
+
// Further filter by targetSpaces if provided
|
|
799
|
+
if (targetSpaces && Array.isArray(targetSpaces) && targetSpaces.length > 0) {
|
|
800
|
+
spaces = spaces.filter(space => targetSpaces.includes(space));
|
|
817
801
|
}
|
|
818
802
|
|
|
819
|
-
|
|
820
|
-
|
|
803
|
+
if (spaces.length > 0) {
|
|
804
|
+
// Filter spaces based on lens configuration
|
|
805
|
+
spaces = spaces.filter(targetSpace => {
|
|
806
|
+
const spaceConfig = fedInfo.lensConfig?.[targetSpace];
|
|
807
|
+
if (!spaceConfig) {
|
|
808
|
+
result.messages.push(`No lens configuration for target space ${targetSpace}. Skipping propagation of lens '${lens}'.`);
|
|
809
|
+
result.skipped++;
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
821
812
|
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
if (result.skipped === 0 && fedInfo.notify && fedInfo.notify.length > 0) {
|
|
825
|
-
result.messages.push('No target spaces were configured for federation of this specific lens, out of available notification partners.');
|
|
826
|
-
} else if (fedInfo.notify && fedInfo.notify.length === 0) {
|
|
827
|
-
result.messages.push('No notification partners available to filter for lens propagation.');
|
|
828
|
-
}
|
|
829
|
-
return {
|
|
830
|
-
...result,
|
|
831
|
-
message: result.messages.join('; ') || 'No valid target spaces for propagation after lens filtering.'
|
|
832
|
-
};
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
// Check if data is already a hologram
|
|
836
|
-
const isAlreadyHologram = holosphere.isHologram(data);
|
|
813
|
+
// Ensure .federate is an array before calling .includes
|
|
814
|
+
const federateLenses = Array.isArray(spaceConfig.federate) ? spaceConfig.federate : [];
|
|
837
815
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
816
|
+
const shouldFederate = federateLenses.includes('*') || federateLenses.includes(lens);
|
|
817
|
+
|
|
818
|
+
// Propagation now only depends on the 'federate' list configuration for the lens
|
|
819
|
+
const shouldPropagate = shouldFederate;
|
|
820
|
+
|
|
821
|
+
if (!shouldPropagate) {
|
|
822
|
+
result.messages.push(`Propagation of lens '${lens}' to target space ${targetSpace} skipped: lens not in 'federate' configuration.`);
|
|
823
|
+
result.skipped++;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
return shouldPropagate;
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
if (spaces.length > 0) {
|
|
830
|
+
// Check if data is already a hologram
|
|
831
|
+
const isAlreadyHologram = holosphere.isHologram(data);
|
|
832
|
+
|
|
833
|
+
// For each target space, propagate the data
|
|
834
|
+
const propagatePromises = spaces.map(async (targetSpace) => {
|
|
835
|
+
try {
|
|
836
|
+
let payloadToPut;
|
|
837
|
+
const federationMeta = {
|
|
838
|
+
origin: holon, // The space from which this data is being propagated
|
|
839
|
+
sourceLens: lens, // The lens from which this data is being propagated
|
|
840
|
+
propagatedAt: Date.now(),
|
|
841
|
+
originalId: data.id
|
|
842
|
+
};
|
|
841
843
|
|
|
842
|
-
|
|
843
|
-
|
|
844
|
+
if (useHolograms && !isAlreadyHologram) {
|
|
845
|
+
// Create a new hologram referencing the original data
|
|
846
|
+
const newHologram = holosphere.createHologram(holon, lens, data);
|
|
847
|
+
payloadToPut = {
|
|
848
|
+
...newHologram, // This will be { id: data.id, soul: 'path/to/original' }
|
|
849
|
+
_federation: federationMeta
|
|
850
|
+
};
|
|
851
|
+
} else {
|
|
852
|
+
// Propagate existing data (could be a full object or an existing hologram)
|
|
853
|
+
// Make a shallow copy and update/add _federation metadata
|
|
854
|
+
payloadToPut = {
|
|
855
|
+
...data,
|
|
856
|
+
_federation: {
|
|
857
|
+
...(data._federation || {}), // Preserve existing _federation fields if any
|
|
858
|
+
...federationMeta // Add/overwrite with current propagation info
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
// Store in the target space with redirection disabled and no further auto-propagation
|
|
864
|
+
await holosphere.put(targetSpace, lens, payloadToPut, null, {
|
|
865
|
+
disableHologramRedirection: true,
|
|
866
|
+
autoPropagate: false
|
|
867
|
+
});
|
|
868
|
+
|
|
869
|
+
result.success++;
|
|
870
|
+
return true;
|
|
871
|
+
} catch (error) {
|
|
872
|
+
result.errors++;
|
|
873
|
+
result.messages.push(`Error propagating ${data.id} to ${targetSpace}: ${error.message}`);
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
await Promise.all(propagatePromises);
|
|
879
|
+
} else {
|
|
880
|
+
result.messages.push('No valid target spaces for federation propagation after lens filtering.');
|
|
881
|
+
}
|
|
882
|
+
} else {
|
|
883
|
+
result.messages.push('No valid target spaces found for federation propagation.');
|
|
884
|
+
}
|
|
885
|
+
} else {
|
|
886
|
+
result.messages.push(`No federation found for ${holon} or no notification targets available.`);
|
|
844
887
|
}
|
|
845
888
|
|
|
846
|
-
//
|
|
847
|
-
|
|
889
|
+
// ================================ PARENT PROPAGATION ================================
|
|
890
|
+
|
|
891
|
+
// Check if we should propagate to parent hexagons
|
|
892
|
+
if (propagateToParents) {
|
|
893
|
+
console.log(`[Federation] Starting parent propagation for holon: ${holon}`);
|
|
848
894
|
try {
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
} else {
|
|
865
|
-
// Propagate existing data (could be a full object or an existing hologram)
|
|
866
|
-
// Make a shallow copy and update/add _federation metadata
|
|
867
|
-
payloadToPut = {
|
|
868
|
-
...data,
|
|
869
|
-
_federation: {
|
|
870
|
-
...(data._federation || {}), // Preserve existing _federation fields if any
|
|
871
|
-
...federationMeta // Add/overwrite with current propagation info
|
|
895
|
+
// Validate if the holon is a proper H3 hexagon
|
|
896
|
+
// H3 hexagons should start with '8' and have a valid format
|
|
897
|
+
let holonResolution;
|
|
898
|
+
let isValidH3 = false;
|
|
899
|
+
|
|
900
|
+
// First check: H3 hexagons should start with '8' and be at least 15 characters long
|
|
901
|
+
if (typeof holon === 'string' && holon.startsWith('8') && holon.length >= 15) {
|
|
902
|
+
try {
|
|
903
|
+
holonResolution = h3.getResolution(holon);
|
|
904
|
+
// Additional validation: resolution should be >= 0 and <= 15
|
|
905
|
+
if (holonResolution >= 0 && holonResolution <= 15) {
|
|
906
|
+
isValidH3 = true;
|
|
907
|
+
console.log(`[Federation] Holon ${holon} is valid H3 hexagon with resolution: ${holonResolution}`);
|
|
908
|
+
} else {
|
|
909
|
+
console.log(`[Federation] Holon ${holon} has invalid resolution: ${holonResolution}`);
|
|
872
910
|
}
|
|
873
|
-
}
|
|
911
|
+
} catch (error) {
|
|
912
|
+
console.log(`[Federation] Holon ${holon} failed H3 validation: ${error.message}`);
|
|
913
|
+
}
|
|
914
|
+
} else {
|
|
915
|
+
console.log(`[Federation] Holon ${holon} is not a valid H3 hexagon: does not start with '8' or too short`);
|
|
874
916
|
}
|
|
875
917
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
}
|
|
918
|
+
if (!isValidH3) {
|
|
919
|
+
console.log(`[Federation] Skipping parent propagation for non-H3 holon: ${holon}`);
|
|
920
|
+
result.parentPropagation.messages.push(`Holon ${holon} is not a valid H3 hexagon. Skipping parent propagation.`);
|
|
921
|
+
result.parentPropagation.skipped++;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
if (isValidH3 && holonResolution !== undefined) {
|
|
925
|
+
// Get all parent hexagons up to the specified max levels
|
|
926
|
+
const parentHexagons = [];
|
|
927
|
+
let currentHolon = holon;
|
|
928
|
+
let currentRes = holonResolution;
|
|
929
|
+
let levelsProcessed = 0;
|
|
930
|
+
|
|
931
|
+
console.log(`[Federation] Getting parent hexagons for ${holon} (resolution ${holonResolution}) up to ${maxParentLevels} levels`);
|
|
932
|
+
|
|
933
|
+
while (currentRes > 0 && levelsProcessed < maxParentLevels) {
|
|
934
|
+
try {
|
|
935
|
+
const parent = h3.cellToParent(currentHolon, currentRes - 1);
|
|
936
|
+
parentHexagons.push(parent);
|
|
937
|
+
console.log(`[Federation] Found parent hexagon: ${parent} (resolution ${currentRes - 1})`);
|
|
938
|
+
currentHolon = parent;
|
|
939
|
+
currentRes--;
|
|
940
|
+
levelsProcessed++;
|
|
941
|
+
} catch (error) {
|
|
942
|
+
console.error(`[Federation] Error getting parent for ${currentHolon}: ${error.message}`);
|
|
943
|
+
result.parentPropagation.messages.push(`Error getting parent for ${currentHolon}: ${error.message}`);
|
|
944
|
+
result.parentPropagation.errors++;
|
|
945
|
+
break;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
if (parentHexagons.length > 0) {
|
|
950
|
+
console.log(`[Federation] Found ${parentHexagons.length} parent hexagons to propagate to: ${parentHexagons.join(', ')}`);
|
|
951
|
+
result.parentPropagation.messages.push(`Found ${parentHexagons.length} parent hexagons to propagate to: ${parentHexagons.join(', ')}`);
|
|
952
|
+
|
|
953
|
+
// Check if data is already a hologram (reuse from federation section)
|
|
954
|
+
const isAlreadyHologram = holosphere.isHologram(data);
|
|
955
|
+
console.log(`[Federation] Data is already hologram: ${isAlreadyHologram}`);
|
|
956
|
+
|
|
957
|
+
// Propagate to each parent hexagon
|
|
958
|
+
const parentPropagatePromises = parentHexagons.map(async (parentHexagon) => {
|
|
959
|
+
try {
|
|
960
|
+
console.log(`[Federation] Propagating to parent hexagon: ${parentHexagon}`);
|
|
961
|
+
let payloadToPut;
|
|
962
|
+
const parentFederationMeta = {
|
|
963
|
+
origin: holon, // The original holon from which this data is being propagated
|
|
964
|
+
sourceLens: lens, // The lens from which this data is being propagated
|
|
965
|
+
propagatedAt: Date.now(),
|
|
966
|
+
originalId: data.id,
|
|
967
|
+
propagationType: 'parent', // Indicate this is parent propagation
|
|
968
|
+
parentLevel: holonResolution - h3.getResolution(parentHexagon) // How many levels up
|
|
969
|
+
};
|
|
881
970
|
|
|
882
|
-
|
|
883
|
-
|
|
971
|
+
if (useHolograms && !isAlreadyHologram) {
|
|
972
|
+
// Create a new hologram referencing the original data
|
|
973
|
+
const newHologram = holosphere.createHologram(holon, lens, data);
|
|
974
|
+
console.log(`[Federation] Created hologram for parent propagation:`, newHologram);
|
|
975
|
+
payloadToPut = {
|
|
976
|
+
...newHologram, // This will be { id: data.id, soul: 'path/to/original' }
|
|
977
|
+
_federation: parentFederationMeta
|
|
978
|
+
};
|
|
979
|
+
} else {
|
|
980
|
+
// Propagate existing data (could be a full object or an existing hologram)
|
|
981
|
+
// Make a shallow copy and update/add _federation metadata
|
|
982
|
+
payloadToPut = {
|
|
983
|
+
...data,
|
|
984
|
+
_federation: {
|
|
985
|
+
...(data._federation || {}), // Preserve existing _federation fields if any
|
|
986
|
+
...parentFederationMeta // Add/overwrite with current propagation info
|
|
987
|
+
}
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
console.log(`[Federation] Storing in parent hexagon ${parentHexagon} with payload:`, payloadToPut);
|
|
992
|
+
|
|
993
|
+
// Store in the parent hexagon with redirection disabled and no further auto-propagation
|
|
994
|
+
await holosphere.put(parentHexagon, lens, payloadToPut, null, {
|
|
995
|
+
disableHologramRedirection: true,
|
|
996
|
+
autoPropagate: false
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
console.log(`[Federation] Successfully propagated to parent hexagon: ${parentHexagon}`);
|
|
1000
|
+
result.parentPropagation.success++;
|
|
1001
|
+
return true;
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
console.error(`[Federation] Error propagating ${data.id} to parent hexagon ${parentHexagon}: ${error.message}`);
|
|
1004
|
+
result.parentPropagation.errors++;
|
|
1005
|
+
result.parentPropagation.messages.push(`Error propagating ${data.id} to parent hexagon ${parentHexagon}: ${error.message}`);
|
|
1006
|
+
return false;
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
await Promise.all(parentPropagatePromises);
|
|
1011
|
+
} else {
|
|
1012
|
+
console.log(`[Federation] No parent hexagons found for ${holon} (already at resolution 0 or max levels reached)`);
|
|
1013
|
+
result.parentPropagation.messages.push(`No parent hexagons found for ${holon} (already at resolution 0 or max levels reached)`);
|
|
1014
|
+
result.parentPropagation.skipped++;
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
884
1017
|
} catch (error) {
|
|
885
|
-
|
|
886
|
-
result.
|
|
887
|
-
|
|
1018
|
+
console.error(`[Federation] Error during parent propagation: ${error.message}`);
|
|
1019
|
+
result.parentPropagation.errors++;
|
|
1020
|
+
result.parentPropagation.messages.push(`Error during parent propagation: ${error.message}`);
|
|
888
1021
|
}
|
|
889
|
-
}
|
|
1022
|
+
} else {
|
|
1023
|
+
console.log(`[Federation] Parent propagation disabled for holon: ${holon}`);
|
|
1024
|
+
}
|
|
890
1025
|
|
|
891
|
-
|
|
1026
|
+
// ================================ END PARENT PROPAGATION ================================
|
|
892
1027
|
|
|
893
|
-
result.propagated = result.success > 0;
|
|
1028
|
+
result.propagated = result.success > 0 || result.parentPropagation.success > 0;
|
|
894
1029
|
return result;
|
|
895
1030
|
} catch (error) {
|
|
896
1031
|
return {
|
package/global.js
CHANGED
|
@@ -63,11 +63,15 @@ export async function putGlobal(holoInstance, tableName, data, password = null)
|
|
|
63
63
|
|
|
64
64
|
return new Promise((resolve, reject) => {
|
|
65
65
|
try {
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
// Create a copy of data without the _meta field if it exists
|
|
67
|
+
let dataToStore = { ...data };
|
|
68
|
+
if (dataToStore._meta !== undefined) {
|
|
69
|
+
delete dataToStore._meta;
|
|
70
|
+
}
|
|
71
|
+
const payload = JSON.stringify(dataToStore);
|
|
72
|
+
|
|
73
|
+
// Check if the data being stored is a hologram
|
|
74
|
+
const isHologram = holoInstance.isHologram(dataToStore);
|
|
71
75
|
|
|
72
76
|
const dataPath = password ?
|
|
73
77
|
user.get('private').get(tableName) :
|
|
@@ -79,6 +83,27 @@ export async function putGlobal(holoInstance, tableName, data, password = null)
|
|
|
79
83
|
if (ack.err) {
|
|
80
84
|
reject(new Error(ack.err));
|
|
81
85
|
} else {
|
|
86
|
+
// --- Start: Hologram Tracking Logic (for data *being put*, if it's a hologram) ---
|
|
87
|
+
if (isHologram) {
|
|
88
|
+
try {
|
|
89
|
+
const storedDataSoulInfo = holoInstance.parseSoulPath(dataToStore.soul);
|
|
90
|
+
if (storedDataSoulInfo) {
|
|
91
|
+
const targetNodeRef = holoInstance.getNodeRef(dataToStore.soul); // Target of the data *being put*
|
|
92
|
+
// Soul of the hologram that was *actually stored* at tableName/data.id
|
|
93
|
+
const storedHologramInstanceSoul = `${holoInstance.appname}/${tableName}/${data.id}`;
|
|
94
|
+
|
|
95
|
+
targetNodeRef.get('_holograms').get(storedHologramInstanceSoul).put(true);
|
|
96
|
+
|
|
97
|
+
console.log(`Data (ID: ${data.id}) being put is a hologram. Added its instance soul ${storedHologramInstanceSoul} to its target ${dataToStore.soul}'s _holograms set.`);
|
|
98
|
+
} else {
|
|
99
|
+
console.warn(`Data (ID: ${data.id}) being put is a hologram, but could not parse its soul ${dataToStore.soul} for tracking.`);
|
|
100
|
+
}
|
|
101
|
+
} catch (trackingError) {
|
|
102
|
+
console.warn(`Error updating _holograms set for the target of the data being put (data ID: ${data.id}, soul: ${dataToStore.soul}):`, trackingError);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// --- End: Hologram Tracking Logic ---
|
|
106
|
+
|
|
82
107
|
resolve();
|
|
83
108
|
}
|
|
84
109
|
});
|
|
@@ -87,6 +112,27 @@ export async function putGlobal(holoInstance, tableName, data, password = null)
|
|
|
87
112
|
if (ack.err) {
|
|
88
113
|
reject(new Error(ack.err));
|
|
89
114
|
} else {
|
|
115
|
+
// --- Start: Hologram Tracking Logic (for data *being put*, if it's a hologram) ---
|
|
116
|
+
if (isHologram) {
|
|
117
|
+
try {
|
|
118
|
+
const storedDataSoulInfo = holoInstance.parseSoulPath(dataToStore.soul);
|
|
119
|
+
if (storedDataSoulInfo) {
|
|
120
|
+
const targetNodeRef = holoInstance.getNodeRef(dataToStore.soul); // Target of the data *being put*
|
|
121
|
+
// Soul of the hologram that was *actually stored* at tableName (without specific key)
|
|
122
|
+
const storedHologramInstanceSoul = `${holoInstance.appname}/${tableName}`;
|
|
123
|
+
|
|
124
|
+
targetNodeRef.get('_holograms').get(storedHologramInstanceSoul).put(true);
|
|
125
|
+
|
|
126
|
+
console.log(`Data being put is a hologram. Added its instance soul ${storedHologramInstanceSoul} to its target ${dataToStore.soul}'s _holograms set.`);
|
|
127
|
+
} else {
|
|
128
|
+
console.warn(`Data being put is a hologram, but could not parse its soul ${dataToStore.soul} for tracking.`);
|
|
129
|
+
}
|
|
130
|
+
} catch (trackingError) {
|
|
131
|
+
console.warn(`Error updating _holograms set for the target of the data being put (soul: ${dataToStore.soul}):`, trackingError);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// --- End: Hologram Tracking Logic ---
|
|
135
|
+
|
|
90
136
|
resolve();
|
|
91
137
|
}
|
|
92
138
|
});
|
|
@@ -421,11 +467,66 @@ export async function deleteGlobal(holoInstance, tableName, key, password = null
|
|
|
421
467
|
});
|
|
422
468
|
}
|
|
423
469
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
470
|
+
const dataPath = password ?
|
|
471
|
+
user.get('private').get(tableName).get(key) :
|
|
472
|
+
holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
|
|
473
|
+
|
|
474
|
+
// --- Start: Hologram Tracking Removal ---
|
|
475
|
+
let trackingRemovalPromise = Promise.resolve(); // Default to resolved promise
|
|
476
|
+
|
|
477
|
+
// 1. Get the data first to check if it's a hologram
|
|
478
|
+
const rawDataToDelete = await new Promise((resolve) => dataPath.once(resolve));
|
|
479
|
+
let dataToDelete = null;
|
|
480
|
+
try {
|
|
481
|
+
if (typeof rawDataToDelete === 'string') {
|
|
482
|
+
dataToDelete = JSON.parse(rawDataToDelete);
|
|
483
|
+
} else {
|
|
484
|
+
// Handle cases where it might already be an object (though likely string)
|
|
485
|
+
dataToDelete = rawDataToDelete;
|
|
486
|
+
}
|
|
487
|
+
} catch(e) {
|
|
488
|
+
console.warn("[deleteGlobal] Could not JSON parse data for deletion check:", rawDataToDelete, e);
|
|
489
|
+
dataToDelete = null; // Ensure it's null if parsing fails
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
// 2. If it is a hologram, try to remove its reference from the target
|
|
493
|
+
const isDataHologram = dataToDelete && holoInstance.isHologram(dataToDelete);
|
|
494
|
+
|
|
495
|
+
if (isDataHologram) {
|
|
496
|
+
try {
|
|
497
|
+
const targetSoul = dataToDelete.soul;
|
|
498
|
+
const targetSoulInfo = holoInstance.parseSoulPath(targetSoul);
|
|
499
|
+
|
|
500
|
+
if (targetSoulInfo) {
|
|
501
|
+
const targetNodeRef = holoInstance.getNodeRef(targetSoul);
|
|
502
|
+
const deletedHologramSoul = `${holoInstance.appname}/${tableName}/${key}`;
|
|
503
|
+
|
|
504
|
+
// Create a promise that resolves when the hologram is removed from the list
|
|
505
|
+
trackingRemovalPromise = new Promise((resolveTrack) => { // No reject needed, just warn on error
|
|
506
|
+
targetNodeRef.get('_holograms').get(deletedHologramSoul).put(null, (ack) => { // Remove the hologram entry completely
|
|
507
|
+
if (ack.err) {
|
|
508
|
+
console.warn(`[deleteGlobal] Error removing hologram ${deletedHologramSoul} from target ${targetSoul}:`, ack.err);
|
|
509
|
+
}
|
|
510
|
+
console.log(`Removed hologram ${deletedHologramSoul} from target ${targetSoul}'s _holograms list`);
|
|
511
|
+
resolveTrack(); // Resolve regardless of ack error to not block main delete
|
|
512
|
+
});
|
|
513
|
+
});
|
|
514
|
+
} else {
|
|
515
|
+
console.warn(`Could not parse target soul ${targetSoul} for hologram tracking removal during deleteGlobal.`);
|
|
516
|
+
}
|
|
517
|
+
} catch (trackingError) {
|
|
518
|
+
console.warn(`Error initiating hologram reference removal from target ${dataToDelete.soul} during deleteGlobal:`, trackingError);
|
|
519
|
+
// Ensure trackingRemovalPromise remains resolved if setup fails
|
|
520
|
+
trackingRemovalPromise = Promise.resolve();
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// --- End: Hologram Tracking Removal ---
|
|
524
|
+
|
|
525
|
+
// 3. Wait for the tracking removal attempt to be acknowledged
|
|
526
|
+
await trackingRemovalPromise;
|
|
428
527
|
|
|
528
|
+
// 4. Proceed with the actual deletion of the hologram node itself
|
|
529
|
+
return new Promise((resolve, reject) => {
|
|
429
530
|
// Request deletion
|
|
430
531
|
dataPath.put(null, ack => {
|
|
431
532
|
// console.log('deleteGlobal - Deletion acknowledgment:', ack); // Optional logging
|
|
@@ -522,6 +623,70 @@ export async function deleteAllGlobal(holoInstance, tableName, password = null)
|
|
|
522
623
|
}
|
|
523
624
|
|
|
524
625
|
const keys = Object.keys(data).filter(key => key !== '_');
|
|
626
|
+
|
|
627
|
+
// Process each key to handle holograms properly
|
|
628
|
+
for (const key of keys) {
|
|
629
|
+
try {
|
|
630
|
+
// Get the data to check if it's a hologram
|
|
631
|
+
const itemPath = password ?
|
|
632
|
+
user.get('private').get(tableName).get(key) :
|
|
633
|
+
holoInstance.gun.get(holoInstance.appname).get(tableName).get(key);
|
|
634
|
+
|
|
635
|
+
const rawDataToDelete = await new Promise((resolveItem) => itemPath.once(resolveItem));
|
|
636
|
+
let dataToDelete = null;
|
|
637
|
+
|
|
638
|
+
try {
|
|
639
|
+
if (typeof rawDataToDelete === 'string') {
|
|
640
|
+
dataToDelete = JSON.parse(rawDataToDelete);
|
|
641
|
+
} else {
|
|
642
|
+
dataToDelete = rawDataToDelete;
|
|
643
|
+
}
|
|
644
|
+
} catch(e) {
|
|
645
|
+
console.warn("[deleteAllGlobal] Could not JSON parse data for deletion check:", rawDataToDelete, e);
|
|
646
|
+
dataToDelete = null;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Check if it's a hologram and handle accordingly
|
|
650
|
+
const isDataHologram = dataToDelete && holoInstance.isHologram(dataToDelete);
|
|
651
|
+
|
|
652
|
+
if (isDataHologram) {
|
|
653
|
+
// Handle hologram deletion - remove from target's _holograms list
|
|
654
|
+
try {
|
|
655
|
+
const targetSoul = dataToDelete.soul;
|
|
656
|
+
const targetSoulInfo = holoInstance.parseSoulPath(targetSoul);
|
|
657
|
+
|
|
658
|
+
if (targetSoulInfo) {
|
|
659
|
+
const targetNodeRef = holoInstance.getNodeRef(targetSoul);
|
|
660
|
+
const deletedHologramSoul = `${holoInstance.appname}/${tableName}/${key}`;
|
|
661
|
+
|
|
662
|
+
// Remove the hologram from target's _holograms list
|
|
663
|
+
await new Promise((resolveTrack) => {
|
|
664
|
+
targetNodeRef.get('_holograms').get(deletedHologramSoul).put(null, (ack) => {
|
|
665
|
+
if (ack.err) {
|
|
666
|
+
console.warn(`[deleteAllGlobal] Error removing hologram ${deletedHologramSoul} from target ${targetSoul}:`, ack.err);
|
|
667
|
+
}
|
|
668
|
+
console.log(`Removed hologram ${deletedHologramSoul} from target ${targetSoul}'s _holograms list`);
|
|
669
|
+
resolveTrack();
|
|
670
|
+
});
|
|
671
|
+
});
|
|
672
|
+
} else {
|
|
673
|
+
console.warn(`Could not parse target soul ${targetSoul} for hologram tracking removal during deleteAllGlobal.`);
|
|
674
|
+
}
|
|
675
|
+
} catch (trackingError) {
|
|
676
|
+
console.warn(`Error removing hologram reference from target ${dataToDelete.soul} during deleteAllGlobal:`, trackingError);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
// Add to deletions set for tracking
|
|
681
|
+
deletions.add(key);
|
|
682
|
+
} catch (error) {
|
|
683
|
+
console.warn(`Error processing key ${key} during deleteAllGlobal:`, error);
|
|
684
|
+
// Still add to deletions set even if hologram processing failed
|
|
685
|
+
deletions.add(key);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
// Now delete all the items
|
|
525
690
|
const promises = keys.map(key =>
|
|
526
691
|
new Promise((resolveDelete, rejectDelete) => {
|
|
527
692
|
const deletePath = password ?
|