holosphere 2.0.0-alpha1 → 2.0.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.
Files changed (154) hide show
  1. package/dist/2019-D2OG2idw.js +6680 -0
  2. package/dist/2019-D2OG2idw.js.map +1 -0
  3. package/dist/2019-EION3wKo.cjs +8 -0
  4. package/dist/2019-EION3wKo.cjs.map +1 -0
  5. package/dist/_commonjsHelpers-C37NGDzP.cjs +2 -0
  6. package/dist/_commonjsHelpers-C37NGDzP.cjs.map +1 -0
  7. package/dist/_commonjsHelpers-CUmg6egw.js +7 -0
  8. package/dist/_commonjsHelpers-CUmg6egw.js.map +1 -0
  9. package/dist/browser-BSniCNqO.js +3058 -0
  10. package/dist/browser-BSniCNqO.js.map +1 -0
  11. package/dist/browser-Cq59Ij19.cjs +2 -0
  12. package/dist/browser-Cq59Ij19.cjs.map +1 -0
  13. package/dist/cjs/holosphere.cjs +2 -0
  14. package/dist/cjs/holosphere.cjs.map +1 -0
  15. package/dist/esm/holosphere.js +53 -0
  16. package/dist/esm/holosphere.js.map +1 -0
  17. package/dist/index-BB_vVJgv.cjs +5 -0
  18. package/dist/index-BB_vVJgv.cjs.map +1 -0
  19. package/dist/index-CBitK71M.cjs +12 -0
  20. package/dist/index-CBitK71M.cjs.map +1 -0
  21. package/dist/index-CV0eOogK.js +37423 -0
  22. package/dist/index-CV0eOogK.js.map +1 -0
  23. package/dist/index-Cz-PLCUR.js +15104 -0
  24. package/dist/index-Cz-PLCUR.js.map +1 -0
  25. package/dist/indexeddb-storage-CRsZyB2f.cjs +2 -0
  26. package/dist/indexeddb-storage-CRsZyB2f.cjs.map +1 -0
  27. package/dist/indexeddb-storage-DZaGlY_a.js +132 -0
  28. package/dist/indexeddb-storage-DZaGlY_a.js.map +1 -0
  29. package/dist/memory-storage-BkUi6sZG.js +51 -0
  30. package/dist/memory-storage-BkUi6sZG.js.map +1 -0
  31. package/dist/memory-storage-C0DuUsdY.cjs +2 -0
  32. package/dist/memory-storage-C0DuUsdY.cjs.map +1 -0
  33. package/dist/secp256k1-0kPdAVkK.cjs +12 -0
  34. package/dist/secp256k1-0kPdAVkK.cjs.map +1 -0
  35. package/dist/secp256k1-DN4FVXcv.js +1890 -0
  36. package/dist/secp256k1-DN4FVXcv.js.map +1 -0
  37. package/docs/CONTRACTS.md +797 -0
  38. package/docs/FOSDEM_PROPOSAL.md +388 -0
  39. package/docs/LOCALFIRST.md +266 -0
  40. package/docs/contracts/api-interface.md +793 -0
  41. package/docs/data-model.md +476 -0
  42. package/docs/gun-async-usage.md +338 -0
  43. package/docs/plan.md +349 -0
  44. package/docs/quickstart.md +674 -0
  45. package/docs/research.md +362 -0
  46. package/docs/spec.md +244 -0
  47. package/docs/storage-backends.md +326 -0
  48. package/docs/tasks.md +947 -0
  49. package/examples/demo.html +47 -0
  50. package/package.json +10 -5
  51. package/src/contracts/abis/Appreciative.json +1280 -0
  52. package/src/contracts/abis/AppreciativeFactory.json +101 -0
  53. package/src/contracts/abis/Bundle.json +1435 -0
  54. package/src/contracts/abis/BundleFactory.json +106 -0
  55. package/src/contracts/abis/Holon.json +881 -0
  56. package/src/contracts/abis/Holons.json +330 -0
  57. package/src/contracts/abis/Managed.json +1262 -0
  58. package/src/contracts/abis/ManagedFactory.json +149 -0
  59. package/src/contracts/abis/Membrane.json +261 -0
  60. package/src/contracts/abis/Splitter.json +1624 -0
  61. package/src/contracts/abis/SplitterFactory.json +220 -0
  62. package/src/contracts/abis/TestToken.json +321 -0
  63. package/src/contracts/abis/Zoned.json +1461 -0
  64. package/src/contracts/abis/ZonedFactory.json +154 -0
  65. package/src/contracts/chain-manager.js +375 -0
  66. package/src/contracts/deployer.js +443 -0
  67. package/src/contracts/event-listener.js +507 -0
  68. package/src/contracts/holon-contracts.js +344 -0
  69. package/src/contracts/index.js +83 -0
  70. package/src/contracts/networks.js +224 -0
  71. package/src/contracts/operations.js +670 -0
  72. package/src/contracts/queries.js +589 -0
  73. package/src/core/holosphere.js +453 -1
  74. package/src/crypto/nostr-utils.js +263 -0
  75. package/src/federation/handshake.js +455 -0
  76. package/src/federation/hologram.js +1 -1
  77. package/src/hierarchical/upcast.js +6 -5
  78. package/src/index.js +463 -1939
  79. package/src/lib/ai-methods.js +308 -0
  80. package/src/lib/contract-methods.js +293 -0
  81. package/src/lib/errors.js +23 -0
  82. package/src/lib/federation-methods.js +238 -0
  83. package/src/lib/index.js +26 -0
  84. package/src/spatial/h3-operations.js +2 -2
  85. package/src/storage/backends/gundb-backend.js +377 -46
  86. package/src/storage/global-tables.js +28 -1
  87. package/src/storage/gun-auth.js +303 -0
  88. package/src/storage/gun-federation.js +776 -0
  89. package/src/storage/gun-references.js +198 -0
  90. package/src/storage/gun-schema.js +291 -0
  91. package/src/storage/gun-wrapper.js +347 -31
  92. package/src/storage/indexeddb-storage.js +49 -11
  93. package/src/storage/memory-storage.js +5 -0
  94. package/src/storage/nostr-async.js +45 -23
  95. package/src/storage/nostr-client.js +11 -5
  96. package/src/storage/persistent-storage.js +6 -1
  97. package/src/storage/unified-storage.js +119 -0
  98. package/src/subscriptions/manager.js +1 -1
  99. package/types/index.d.ts +133 -0
  100. package/tests/unit/ai/aggregation.test.js +0 -295
  101. package/tests/unit/ai/breakdown.test.js +0 -446
  102. package/tests/unit/ai/classifier.test.js +0 -294
  103. package/tests/unit/ai/council.test.js +0 -262
  104. package/tests/unit/ai/embeddings.test.js +0 -384
  105. package/tests/unit/ai/federation-ai.test.js +0 -344
  106. package/tests/unit/ai/h3-ai.test.js +0 -458
  107. package/tests/unit/ai/index.test.js +0 -304
  108. package/tests/unit/ai/json-ops.test.js +0 -307
  109. package/tests/unit/ai/llm-service.test.js +0 -390
  110. package/tests/unit/ai/nl-query.test.js +0 -383
  111. package/tests/unit/ai/relationships.test.js +0 -311
  112. package/tests/unit/ai/schema-extractor.test.js +0 -384
  113. package/tests/unit/ai/spatial.test.js +0 -279
  114. package/tests/unit/ai/tts.test.js +0 -279
  115. package/tests/unit/content.test.js +0 -332
  116. package/tests/unit/contract/core.test.js +0 -88
  117. package/tests/unit/contract/crypto.test.js +0 -198
  118. package/tests/unit/contract/data.test.js +0 -223
  119. package/tests/unit/contract/federation.test.js +0 -181
  120. package/tests/unit/contract/hierarchical.test.js +0 -113
  121. package/tests/unit/contract/schema.test.js +0 -114
  122. package/tests/unit/contract/social.test.js +0 -217
  123. package/tests/unit/contract/spatial.test.js +0 -110
  124. package/tests/unit/contract/subscriptions.test.js +0 -128
  125. package/tests/unit/contract/utils.test.js +0 -159
  126. package/tests/unit/core.test.js +0 -152
  127. package/tests/unit/crypto.test.js +0 -328
  128. package/tests/unit/federation.test.js +0 -234
  129. package/tests/unit/gun-async.test.js +0 -252
  130. package/tests/unit/hierarchical.test.js +0 -399
  131. package/tests/unit/integration/scenario-01-geographic-storage.test.js +0 -74
  132. package/tests/unit/integration/scenario-02-federation.test.js +0 -76
  133. package/tests/unit/integration/scenario-03-subscriptions.test.js +0 -102
  134. package/tests/unit/integration/scenario-04-validation.test.js +0 -129
  135. package/tests/unit/integration/scenario-05-hierarchy.test.js +0 -125
  136. package/tests/unit/integration/scenario-06-social.test.js +0 -135
  137. package/tests/unit/integration/scenario-07-persistence.test.js +0 -130
  138. package/tests/unit/integration/scenario-08-authorization.test.js +0 -161
  139. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +0 -139
  140. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +0 -357
  141. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +0 -410
  142. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +0 -719
  143. package/tests/unit/performance/benchmark.test.js +0 -85
  144. package/tests/unit/schema.test.js +0 -213
  145. package/tests/unit/spatial.test.js +0 -158
  146. package/tests/unit/storage.test.js +0 -195
  147. package/tests/unit/subscriptions.test.js +0 -328
  148. package/tests/unit/test-data-permanence-debug.js +0 -197
  149. package/tests/unit/test-data-permanence.js +0 -340
  150. package/tests/unit/test-key-persistence-fixed.js +0 -148
  151. package/tests/unit/test-key-persistence.js +0 -172
  152. package/tests/unit/test-relay-permanence.js +0 -376
  153. package/tests/unit/test-second-node.js +0 -95
  154. package/tests/unit/test-simple-write.js +0 -89
@@ -1,198 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import HoloSphere from '../../../src/index.js';
3
-
4
- describe('Contract: Cryptographic Operations', () => {
5
- let hs;
6
- let privateKey;
7
- let publicKey;
8
-
9
- beforeEach(async () => {
10
- hs = new HoloSphere({ relays: [], appName: 'test-crypto' });
11
- // Generate valid secp256k1 keys
12
- privateKey = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
13
- publicKey = await hs.getPublicKey(privateKey);
14
- });
15
-
16
- describe('sign()', () => {
17
- it('should sign object content and return hex signature', async () => {
18
- const content = { message: 'Hello, HoloSphere!' };
19
- const signature = await hs.sign(content, privateKey);
20
-
21
- expect(typeof signature).toBe('string');
22
- expect(signature).toMatch(/^[0-9a-f]+$/);
23
- });
24
-
25
- it('should sign string content', async () => {
26
- const content = 'Hello, HoloSphere!';
27
- const signature = await hs.sign(content, privateKey);
28
-
29
- expect(typeof signature).toBe('string');
30
- expect(signature).toMatch(/^[0-9a-f]+$/);
31
- });
32
-
33
- it('should throw Error for invalid private key', async () => {
34
- await expect(
35
- hs.sign({ message: 'Test' }, 'invalid-key')
36
- ).rejects.toThrow(Error);
37
- });
38
-
39
- it('should lazy load crypto module on first call', async () => {
40
- // First call should trigger lazy load
41
- const signature = await hs.sign({ message: 'Test' }, privateKey);
42
- expect(typeof signature).toBe('string');
43
- });
44
- });
45
-
46
- describe('verify()', () => {
47
- it('should return true for valid signature', async () => {
48
- const content = { message: 'Hello, HoloSphere!' };
49
- const signature = await hs.sign(content, privateKey);
50
-
51
- const isValid = await hs.verify(content, signature, publicKey);
52
- expect(isValid).toBe(true);
53
- });
54
-
55
- it('should return false for tampered content', async () => {
56
- const content = { message: 'Hello, HoloSphere!' };
57
- const signature = await hs.sign(content, privateKey);
58
-
59
- const tamperedContent = { message: 'Tampered message' };
60
- const isValid = await hs.verify(tamperedContent, signature, publicKey);
61
- expect(isValid).toBe(false);
62
- });
63
-
64
- it('should return false for invalid signature', async () => {
65
- const content = { message: 'Hello, HoloSphere!' };
66
- const invalidSignature = 'invalid';
67
-
68
- const isValid = await hs.verify(content, invalidSignature, publicKey);
69
- expect(isValid).toBe(false);
70
- });
71
-
72
- it('should return false for wrong public key', async () => {
73
- const content = { message: 'Hello, HoloSphere!' };
74
- const signature = await hs.sign(content, privateKey);
75
-
76
- const wrongPublicKey = '04' + 'fedcba9876543210'.repeat(8);
77
- const isValid = await hs.verify(content, signature, wrongPublicKey);
78
- expect(isValid).toBe(false);
79
- });
80
- });
81
-
82
- describe('issueCapability()', () => {
83
- it('should issue capability token with permissions', async () => {
84
- const token = await hs.issueCapability(
85
- ['read', 'write'],
86
- { holonId: '8928342e20fffff', lensName: 'temperature' },
87
- publicKey
88
- );
89
-
90
- expect(typeof token).toBe('string');
91
- expect(token.length).toBeGreaterThan(0);
92
- });
93
-
94
- it('should accept expiresIn option', async () => {
95
- const token = await hs.issueCapability(
96
- ['read'],
97
- { holonId: '8928342e20fffff', lensName: 'temperature' },
98
- publicKey,
99
- { expiresIn: 86400000 } // 24 hours
100
- );
101
-
102
- expect(typeof token).toBe('string');
103
- });
104
-
105
- it('should accept issuerKey option', async () => {
106
- const token = await hs.issueCapability(
107
- ['delete'],
108
- { holonId: '8928342e20fffff', lensName: 'temperature' },
109
- publicKey,
110
- { issuerKey: privateKey }
111
- );
112
-
113
- expect(typeof token).toBe('string');
114
- });
115
-
116
- it('should throw Error for invalid issuer key', async () => {
117
- await expect(
118
- hs.issueCapability(
119
- ['read'],
120
- { holonId: '8928342e20fffff', lensName: 'temperature' },
121
- publicKey,
122
- { issuerKey: 'invalid-key' }
123
- )
124
- ).rejects.toThrow(Error);
125
- });
126
-
127
- it('should support multiple permissions', async () => {
128
- const token = await hs.issueCapability(
129
- ['read', 'write', 'delete'],
130
- { holonId: '8928342e20fffff', lensName: 'temperature' },
131
- publicKey
132
- );
133
-
134
- expect(typeof token).toBe('string');
135
- });
136
- });
137
-
138
- describe('verifyCapability()', () => {
139
- it('should return true for valid token with required permission', async () => {
140
- const token = await hs.issueCapability(
141
- ['read', 'write', 'delete'],
142
- { holonId: '8928342e20fffff', lensName: 'temperature' },
143
- publicKey
144
- );
145
-
146
- const isValid = await hs.verifyCapability(
147
- token,
148
- 'delete',
149
- { holonId: '8928342e20fffff', lensName: 'temperature' }
150
- );
151
-
152
- expect(isValid).toBe(true);
153
- });
154
-
155
- it('should return false for token without required permission', async () => {
156
- const token = await hs.issueCapability(
157
- ['read', 'write'],
158
- { holonId: '8928342e20fffff', lensName: 'temperature' },
159
- publicKey
160
- );
161
-
162
- const isValid = await hs.verifyCapability(
163
- token,
164
- 'delete',
165
- { holonId: '8928342e20fffff', lensName: 'temperature' }
166
- );
167
-
168
- expect(isValid).toBe(false);
169
- });
170
-
171
- it('should return false for expired token', async () => {
172
- const token = await hs.issueCapability(
173
- ['read'],
174
- { holonId: '8928342e20fffff', lensName: 'temperature' },
175
- publicKey,
176
- { expiresIn: -1000 } // Already expired
177
- );
178
-
179
- const isValid = await hs.verifyCapability(
180
- token,
181
- 'read',
182
- { holonId: '8928342e20fffff', lensName: 'temperature' }
183
- );
184
-
185
- expect(isValid).toBe(false);
186
- });
187
-
188
- it('should return false for invalid token format', async () => {
189
- const isValid = await hs.verifyCapability(
190
- 'invalid-token',
191
- 'read',
192
- { holonId: '8928342e20fffff', lensName: 'temperature' }
193
- );
194
-
195
- expect(isValid).toBe(false);
196
- });
197
- });
198
- });
@@ -1,223 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import HoloSphere from '../../../src/index.js';
3
-
4
- describe('Contract: Data Operations (CRUD)', () => {
5
- let hs;
6
- let testHolonId;
7
-
8
- beforeEach(async () => {
9
- hs = new HoloSphere({ relays: [], appName: 'test-data' });
10
- testHolonId = await hs.toHolon(37.7749, -122.4194, 9);
11
- });
12
-
13
- describe('write()', () => {
14
- it('should write data with explicit ID', async () => {
15
- const success = await hs.write(testHolonId, 'temperature', {
16
- id: 'sensor-001',
17
- value: 72.5,
18
- unit: 'F'
19
- });
20
- expect(success).toBe(true);
21
- });
22
-
23
- it('should write data without ID (auto-generated)', async () => {
24
- const success = await hs.write(testHolonId, 'temperature', {
25
- value: 73.0,
26
- unit: 'F'
27
- });
28
- expect(success).toBe(true);
29
- });
30
-
31
- it('should accept options parameter', async () => {
32
- const success = await hs.write(
33
- testHolonId,
34
- 'temperature',
35
- { id: 'sensor-002', value: 74.0 },
36
- { validate: false, strict: false }
37
- );
38
- expect(success).toBe(true);
39
- });
40
-
41
- it('should throw ValidationError in strict mode with invalid data', async () => {
42
- const schema = {
43
- type: 'object',
44
- properties: {
45
- id: { type: 'string' },
46
- value: { type: 'number' }
47
- },
48
- required: ['id', 'value']
49
- };
50
- await hs.setSchema('temperature', schema, true);
51
- await expect(
52
- hs.write(
53
- testHolonId,
54
- 'temperature',
55
- { id: 'bad', value: 'invalid' },
56
- { strict: true }
57
- )
58
- ).rejects.toThrow('ValidationError');
59
- });
60
-
61
- it('should throw AuthorizationError with invalid capability token', async () => {
62
- await expect(
63
- hs.write(
64
- testHolonId,
65
- 'temperature',
66
- { id: 'sensor-003', value: 75.0 },
67
- { capability: 'invalid-token' }
68
- )
69
- ).rejects.toThrow('AuthorizationError');
70
- });
71
-
72
- it('should throw ValidationError with invalid holonId', async () => {
73
- // Test with invalid holonId to trigger validation error
74
- await expect(
75
- hs.write('', 'temperature', { id: 'x' })
76
- ).rejects.toThrow('ValidationError');
77
- });
78
- });
79
-
80
- describe('read()', () => {
81
- it('should read specific data item by ID', async () => {
82
- await hs.write(testHolonId, 'temperature', {
83
- id: 'sensor-001',
84
- value: 72.5
85
- });
86
-
87
- const data = await hs.read(testHolonId, 'temperature', 'sensor-001');
88
- expect(data).toMatchObject({ id: 'sensor-001', value: 72.5 });
89
- });
90
-
91
- it('should read all items in lens when dataId omitted', async () => {
92
- await hs.write(testHolonId, 'temperature', { id: 'sensor-001', value: 72.5 });
93
- await hs.write(testHolonId, 'temperature', { id: 'sensor-002', value: 73.0 });
94
-
95
- const data = await hs.read(testHolonId, 'temperature');
96
- expect(Array.isArray(data)).toBe(true);
97
- expect(data.length).toBeGreaterThanOrEqual(2);
98
- });
99
-
100
- it('should return null for non-existent data', async () => {
101
- const data = await hs.read(testHolonId, 'temperature', 'nonexistent');
102
- expect(data).toBe(null);
103
- });
104
-
105
- it('should throw Error for invalid holonId', async () => {
106
- await expect(hs.read('', 'temperature')).rejects.toThrow(Error);
107
- });
108
-
109
- it('should throw Error for invalid lensName', async () => {
110
- await expect(hs.read(testHolonId, '')).rejects.toThrow(Error);
111
- });
112
- });
113
-
114
- describe('update()', () => {
115
- it('should update existing data by merging updates', async () => {
116
- await hs.write(testHolonId, 'temperature', {
117
- id: 'sensor-001',
118
- value: 72.5,
119
- unit: 'F'
120
- });
121
-
122
- const success = await hs.update(
123
- testHolonId,
124
- 'temperature',
125
- 'sensor-001',
126
- { value: 73.0 }
127
- );
128
- expect(success).toBe(true);
129
-
130
- const updated = await hs.read(testHolonId, 'temperature', 'sensor-001');
131
- expect(updated.value).toBe(73.0);
132
- expect(updated.unit).toBe('F'); // Original field preserved
133
- });
134
-
135
- it('should return false for non-existent item', async () => {
136
- const success = await hs.update(
137
- testHolonId,
138
- 'temperature',
139
- 'nonexistent',
140
- { value: 99 }
141
- );
142
- expect(success).toBe(false);
143
- });
144
-
145
- it('should accept options parameter', async () => {
146
- await hs.write(testHolonId, 'temperature', { id: 'sensor-001', value: 72 });
147
-
148
- const success = await hs.update(
149
- testHolonId,
150
- 'temperature',
151
- 'sensor-001',
152
- { value: 73 },
153
- { validate: true, strict: false }
154
- );
155
- expect(success).toBe(true);
156
- });
157
-
158
- it('should throw ValidationError in strict mode', async () => {
159
- const schema = {
160
- type: 'object',
161
- properties: {
162
- id: { type: 'string' },
163
- value: { type: 'number' }
164
- },
165
- required: ['id', 'value']
166
- };
167
- await hs.setSchema('temperature', schema, true);
168
- await hs.write(testHolonId, 'temperature', { id: 'sensor-001', value: 72 });
169
-
170
- await expect(
171
- hs.update(
172
- testHolonId,
173
- 'temperature',
174
- 'sensor-001',
175
- { value: 'invalid' },
176
- { strict: true }
177
- )
178
- ).rejects.toThrow('ValidationError');
179
- });
180
- });
181
-
182
- describe('delete()', () => {
183
- it('should delete own data without capability token', async () => {
184
- await hs.write(testHolonId, 'temperature', { id: 'sensor-001', value: 72 });
185
-
186
- const success = await hs.delete(testHolonId, 'temperature', 'sensor-001');
187
- expect(success).toBe(true);
188
-
189
- const deleted = await hs.read(testHolonId, 'temperature', 'sensor-001');
190
- expect(deleted).toBe(null);
191
- });
192
-
193
- it('should return false when deleting non-existent data', async () => {
194
- // Deleting non-existent data returns false
195
- const success = await hs.delete(testHolonId, 'temperature', 'others-data');
196
- expect(success).toBe(false);
197
- });
198
-
199
- it('should delete with valid capability token', async () => {
200
- // First create the data to delete
201
- await hs.write(testHolonId, 'temperature', { id: 'sensor-001', value: 72 });
202
-
203
- const token = await hs.issueCapability(
204
- ['delete'],
205
- { holonId: testHolonId, lensName: 'temperature' },
206
- hs.client.publicKey
207
- );
208
-
209
- const success = await hs.delete(
210
- testHolonId,
211
- 'temperature',
212
- 'sensor-001',
213
- { capability: token }
214
- );
215
- expect(success).toBe(true);
216
- });
217
-
218
- it('should return false on delete failure', async () => {
219
- const success = await hs.delete(testHolonId, 'temperature', 'nonexistent');
220
- expect(success).toBe(false);
221
- });
222
- });
223
- });
@@ -1,181 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import HoloSphere from '../../../src/index.js';
3
-
4
- describe('Contract: Federation Operations', () => {
5
- let hs;
6
- let sourceHolon;
7
- let targetHolon;
8
-
9
- beforeEach(async () => {
10
- hs = new HoloSphere({ relays: [], appName: 'test-federation' });
11
- sourceHolon = await hs.toHolon(37.7749, -122.4194, 9);
12
- targetHolon = await hs.toHolon(37.7749, -122.4194, 7);
13
- });
14
-
15
- describe('federate()', () => {
16
- it('should establish federation between two holons', async () => {
17
- const success = await hs.federate(sourceHolon, targetHolon, 'events');
18
- expect(success).toBe(true);
19
- });
20
-
21
- it('should accept direction option: outbound', async () => {
22
- const success = await hs.federate(
23
- sourceHolon,
24
- targetHolon,
25
- 'events',
26
- { direction: 'outbound' }
27
- );
28
- expect(success).toBe(true);
29
- });
30
-
31
- it('should accept direction option: inbound', async () => {
32
- const success = await hs.federate(
33
- sourceHolon,
34
- targetHolon,
35
- 'events',
36
- { direction: 'inbound' }
37
- );
38
- expect(success).toBe(true);
39
- });
40
-
41
- it('should accept direction option: bidirectional', async () => {
42
- const success = await hs.federate(
43
- sourceHolon,
44
- targetHolon,
45
- 'events',
46
- { direction: 'bidirectional' }
47
- );
48
- expect(success).toBe(true);
49
- });
50
-
51
- it('should accept mode option: reference (default)', async () => {
52
- const success = await hs.federate(
53
- sourceHolon,
54
- targetHolon,
55
- 'events',
56
- { mode: 'reference' }
57
- );
58
- expect(success).toBe(true);
59
- });
60
-
61
- it('should accept mode option: copy', async () => {
62
- const success = await hs.federate(
63
- sourceHolon,
64
- targetHolon,
65
- 'events',
66
- { mode: 'copy' }
67
- );
68
- expect(success).toBe(true);
69
- });
70
-
71
- it('should accept filter function option', async () => {
72
- const success = await hs.federate(
73
- sourceHolon,
74
- targetHolon,
75
- 'events',
76
- { filter: (data) => data.priority === 'high' }
77
- );
78
- expect(success).toBe(true);
79
- });
80
-
81
- it('should throw Error when source equals target', async () => {
82
- await expect(
83
- hs.federate(sourceHolon, sourceHolon, 'events')
84
- ).rejects.toThrow(Error);
85
- });
86
-
87
- it('should succeed even with non-standard holonId', async () => {
88
- // Federation setup writes config data, which succeeds even for non-H3 IDs
89
- const success = await hs.federate('invalid', targetHolon, 'events');
90
- expect(success).toBe(true);
91
- });
92
-
93
- it('should support cross-dimensional federation (geographic to noospheric)', async () => {
94
- const noospheric = 'nostr://topic/climate';
95
- const success = await hs.federate(sourceHolon, noospheric, 'temperature');
96
- expect(success).toBe(true);
97
- });
98
- });
99
-
100
- describe('getFederatedData()', () => {
101
- it('should return combined local and federated data', async () => {
102
- await hs.write(sourceHolon, 'events', { id: 'local-1', name: 'Local Event' });
103
- await hs.federate(sourceHolon, targetHolon, 'events');
104
-
105
- const data = await hs.getFederatedData(targetHolon, 'events');
106
- expect(Array.isArray(data)).toBe(true);
107
- expect(data.length).toBeGreaterThanOrEqual(1);
108
- });
109
-
110
- it('should accept resolveHolograms option (default: true)', async () => {
111
- await hs.federate(sourceHolon, targetHolon, 'events');
112
-
113
- const data = await hs.getFederatedData(
114
- targetHolon,
115
- 'events',
116
- { resolveHolograms: true }
117
- );
118
- expect(Array.isArray(data)).toBe(true);
119
- });
120
-
121
- it('should accept deduplicate option (default: true)', async () => {
122
- await hs.federate(sourceHolon, targetHolon, 'events');
123
-
124
- const data = await hs.getFederatedData(
125
- targetHolon,
126
- 'events',
127
- { deduplicate: true }
128
- );
129
- expect(Array.isArray(data)).toBe(true);
130
- });
131
-
132
- it('should return empty array if no data exists', async () => {
133
- const data = await hs.getFederatedData(targetHolon, 'nonexistent');
134
- expect(data).toEqual([]);
135
- });
136
-
137
- it('should include _meta field with source holon for federated data', async () => {
138
- await hs.write(sourceHolon, 'events', { id: 'event-1', name: 'Test' });
139
- await hs.federate(sourceHolon, targetHolon, 'events');
140
-
141
- const data = await hs.getFederatedData(targetHolon, 'events');
142
- const federatedItem = data.find(item => item._meta);
143
-
144
- if (federatedItem) {
145
- expect(federatedItem._meta).toHaveProperty('source');
146
- }
147
- });
148
-
149
- it('should return local-only data for non-federated holon', async () => {
150
- await hs.write(sourceHolon, 'events', { id: 'local-1', name: 'Local' });
151
-
152
- const data = await hs.getFederatedData(sourceHolon, 'events');
153
- expect(Array.isArray(data)).toBe(true);
154
- });
155
- });
156
-
157
- describe('unfederate()', () => {
158
- it('should remove federation relationship', async () => {
159
- await hs.federate(sourceHolon, targetHolon, 'events');
160
-
161
- const success = await hs.unfederate(sourceHolon, targetHolon, 'events');
162
- expect(success).toBe(true);
163
- });
164
-
165
- it('should be idempotent (no error if not federated)', async () => {
166
- const success = await hs.unfederate(sourceHolon, targetHolon, 'events');
167
- expect(success).toBe(true);
168
- });
169
-
170
- it('should keep existing holograms after unfederate', async () => {
171
- await hs.write(sourceHolon, 'events', { id: 'event-1', name: 'Test' });
172
- await hs.federate(sourceHolon, targetHolon, 'events');
173
- await hs.unfederate(sourceHolon, targetHolon, 'events');
174
-
175
- // Unfederate removes federation config but doesn't delete already-propagated holograms
176
- const data = await hs.getFederatedData(targetHolon, 'events');
177
- const hasFederatedData = data.some(item => item._meta?.source === sourceHolon);
178
- expect(hasFederatedData).toBe(true); // Holograms remain
179
- });
180
- });
181
- });