holosphere 2.0.0-alpha0 → 2.0.0-alpha2

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 (88) hide show
  1. package/LICENSE +162 -38
  2. package/dist/cjs/holosphere.cjs +2 -0
  3. package/dist/cjs/holosphere.cjs.map +1 -0
  4. package/dist/esm/holosphere.js +56 -0
  5. package/dist/esm/holosphere.js.map +1 -0
  6. package/dist/index-CDfIuXew.js +15974 -0
  7. package/dist/index-CDfIuXew.js.map +1 -0
  8. package/dist/index-ifOgtDvd.cjs +3 -0
  9. package/dist/index-ifOgtDvd.cjs.map +1 -0
  10. package/dist/indexeddb-storage-CMW4qRQS.js +96 -0
  11. package/dist/indexeddb-storage-CMW4qRQS.js.map +1 -0
  12. package/dist/indexeddb-storage-DLZOgetM.cjs +2 -0
  13. package/dist/indexeddb-storage-DLZOgetM.cjs.map +1 -0
  14. package/dist/memory-storage-DQzcAZlf.js +47 -0
  15. package/dist/memory-storage-DQzcAZlf.js.map +1 -0
  16. package/dist/memory-storage-DmePEP2q.cjs +2 -0
  17. package/dist/memory-storage-DmePEP2q.cjs.map +1 -0
  18. package/dist/secp256k1-CP0ZkpAx.cjs +13 -0
  19. package/dist/secp256k1-CP0ZkpAx.cjs.map +1 -0
  20. package/dist/secp256k1-vOXp40Fx.js +2281 -0
  21. package/dist/secp256k1-vOXp40Fx.js.map +1 -0
  22. package/docs/FOSDEM_PROPOSAL.md +388 -0
  23. package/docs/LOCALFIRST.md +266 -0
  24. package/docs/contracts/api-interface.md +793 -0
  25. package/docs/data-model.md +476 -0
  26. package/docs/gun-async-usage.md +338 -0
  27. package/docs/plan.md +349 -0
  28. package/docs/quickstart.md +674 -0
  29. package/docs/research.md +362 -0
  30. package/docs/spec.md +244 -0
  31. package/docs/storage-backends.md +326 -0
  32. package/docs/tasks.md +947 -0
  33. package/package.json +1 -1
  34. package/tests/unit/ai/aggregation.test.js +0 -295
  35. package/tests/unit/ai/breakdown.test.js +0 -446
  36. package/tests/unit/ai/classifier.test.js +0 -294
  37. package/tests/unit/ai/council.test.js +0 -262
  38. package/tests/unit/ai/embeddings.test.js +0 -384
  39. package/tests/unit/ai/federation-ai.test.js +0 -344
  40. package/tests/unit/ai/h3-ai.test.js +0 -458
  41. package/tests/unit/ai/index.test.js +0 -304
  42. package/tests/unit/ai/json-ops.test.js +0 -307
  43. package/tests/unit/ai/llm-service.test.js +0 -390
  44. package/tests/unit/ai/nl-query.test.js +0 -383
  45. package/tests/unit/ai/relationships.test.js +0 -311
  46. package/tests/unit/ai/schema-extractor.test.js +0 -384
  47. package/tests/unit/ai/spatial.test.js +0 -279
  48. package/tests/unit/ai/tts.test.js +0 -279
  49. package/tests/unit/content.test.js +0 -332
  50. package/tests/unit/contract/core.test.js +0 -88
  51. package/tests/unit/contract/crypto.test.js +0 -198
  52. package/tests/unit/contract/data.test.js +0 -223
  53. package/tests/unit/contract/federation.test.js +0 -181
  54. package/tests/unit/contract/hierarchical.test.js +0 -113
  55. package/tests/unit/contract/schema.test.js +0 -114
  56. package/tests/unit/contract/social.test.js +0 -217
  57. package/tests/unit/contract/spatial.test.js +0 -110
  58. package/tests/unit/contract/subscriptions.test.js +0 -128
  59. package/tests/unit/contract/utils.test.js +0 -159
  60. package/tests/unit/core.test.js +0 -152
  61. package/tests/unit/crypto.test.js +0 -328
  62. package/tests/unit/federation.test.js +0 -234
  63. package/tests/unit/gun-async.test.js +0 -252
  64. package/tests/unit/hierarchical.test.js +0 -399
  65. package/tests/unit/integration/scenario-01-geographic-storage.test.js +0 -74
  66. package/tests/unit/integration/scenario-02-federation.test.js +0 -76
  67. package/tests/unit/integration/scenario-03-subscriptions.test.js +0 -102
  68. package/tests/unit/integration/scenario-04-validation.test.js +0 -129
  69. package/tests/unit/integration/scenario-05-hierarchy.test.js +0 -125
  70. package/tests/unit/integration/scenario-06-social.test.js +0 -135
  71. package/tests/unit/integration/scenario-07-persistence.test.js +0 -130
  72. package/tests/unit/integration/scenario-08-authorization.test.js +0 -161
  73. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +0 -139
  74. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +0 -357
  75. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +0 -410
  76. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +0 -719
  77. package/tests/unit/performance/benchmark.test.js +0 -85
  78. package/tests/unit/schema.test.js +0 -213
  79. package/tests/unit/spatial.test.js +0 -158
  80. package/tests/unit/storage.test.js +0 -195
  81. package/tests/unit/subscriptions.test.js +0 -328
  82. package/tests/unit/test-data-permanence-debug.js +0 -197
  83. package/tests/unit/test-data-permanence.js +0 -340
  84. package/tests/unit/test-key-persistence-fixed.js +0 -148
  85. package/tests/unit/test-key-persistence.js +0 -172
  86. package/tests/unit/test-relay-permanence.js +0 -376
  87. package/tests/unit/test-second-node.js +0 -95
  88. package/tests/unit/test-simple-write.js +0 -89
@@ -1,85 +0,0 @@
1
- /**
2
- * Performance Benchmarks
3
- * Tests initialization, write, read, subscription overhead, hologram resolution
4
- */
5
-
6
- import { describe, it, expect, beforeAll } from 'vitest';
7
- import HoloSphere from '../../../src/index.js';
8
-
9
- describe('Performance Benchmarks', () => {
10
- let hs;
11
-
12
- beforeAll(() => {
13
- hs = new HoloSphere({ relays: [], appName: 'perf-test', logLevel: 'ERROR' });
14
- });
15
-
16
- it('initialization should complete in <50ms (without crypto)', () => {
17
- const start = performance.now();
18
- const instance = new HoloSphere({ relays: [], appName: 'test', logLevel: 'ERROR' });
19
- const duration = performance.now() - start;
20
-
21
- expect(instance).toBeDefined();
22
- expect(duration).toBeLessThan(50);
23
- console.log(`✓ Initialization: ${duration.toFixed(2)}ms`);
24
- });
25
-
26
- it('write operation should complete in <100ms (avg)', async () => {
27
- const holonId = await hs.toHolon(37.7749, -122.4194, 9);
28
- const iterations = 10;
29
- const durations = [];
30
-
31
- for (let i = 0; i < iterations; i++) {
32
- const start = performance.now();
33
- await hs.write(holonId, 'perf', { id: `item-${i}`, value: i });
34
- const duration = performance.now() - start;
35
- durations.push(duration);
36
- }
37
-
38
- const avg = durations.reduce((a, b) => a + b, 0) / iterations;
39
- expect(avg).toBeLessThan(100);
40
- console.log(`✓ Write (avg): ${avg.toFixed(2)}ms`);
41
- });
42
-
43
- it('read operation should complete in <10ms (avg)', async () => {
44
- const holonId = await hs.toHolon(37.7749, -122.4194, 9);
45
- const iterations = 10;
46
- const durations = [];
47
-
48
- // Pre-write data
49
- await hs.write(holonId, 'perf', { id: 'read-test', value: 42 });
50
-
51
- for (let i = 0; i < iterations; i++) {
52
- const start = performance.now();
53
- await hs.read(holonId, 'perf', 'read-test');
54
- const duration = performance.now() - start;
55
- durations.push(duration);
56
- }
57
-
58
- const avg = durations.reduce((a, b) => a + b, 0) / iterations;
59
- expect(avg).toBeLessThan(10);
60
- console.log(`✓ Read (avg): ${avg.toFixed(2)}ms`);
61
- });
62
-
63
- it('subscription overhead should be <5ms', () => {
64
- const holonId = '8928342e20fffff'; // Pre-computed H3
65
- const callback = () => {};
66
-
67
- const start = performance.now();
68
- const sub = hs.subscribe(holonId, 'perf', callback);
69
- const duration = performance.now() - start;
70
-
71
- expect(duration).toBeLessThan(5);
72
- console.log(`✓ Subscription overhead: ${duration.toFixed(2)}ms`);
73
-
74
- sub.unsubscribe();
75
- });
76
-
77
- it('H3 conversion should complete in <5ms', async () => {
78
- const start = performance.now();
79
- await hs.toHolon(37.7749, -122.4194, 9);
80
- const duration = performance.now() - start;
81
-
82
- expect(duration).toBeLessThan(5);
83
- console.log(`✓ H3 conversion: ${duration.toFixed(2)}ms`);
84
- });
85
- });
@@ -1,213 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import HoloSphere from '../../src/index.js';
3
-
4
- describe('Unit: Schema Module', () => {
5
- let hs;
6
-
7
- beforeEach(() => {
8
- hs = new HoloSphere({ relays: [], appName: 'test-schema-unit' });
9
- });
10
-
11
- describe('JSON Schema 2019 validation', () => {
12
- it('should validate JSON Schema 2019 format', async () => {
13
- const schema = {
14
- $schema: 'https://json-schema.org/draft/2019-09/schema',
15
- type: 'object',
16
- properties: {
17
- id: { type: 'string' },
18
- value: { type: 'number' }
19
- },
20
- required: ['id', 'value']
21
- };
22
-
23
- // Schema should be valid
24
- await expect(
25
- hs.setSchema('test', 'test://schema/valid')
26
- ).resolves.toBeUndefined();
27
- });
28
-
29
- it('should reject invalid schema format', async () => {
30
- await expect(
31
- hs.setSchema('test', { invalid: 'not a valid schema' })
32
- ).rejects.toThrow('ValidationError');
33
- });
34
-
35
- it('should support schema type validation', async () => {
36
- const holon = await hs.toHolon(37.7749, -122.4194, 9);
37
- await hs.setSchema('typed', {
38
- type: 'object',
39
- properties: {
40
- id: { type: 'string' },
41
- value: { type: 'number' }
42
- },
43
- required: ['id', 'value']
44
- }, true);
45
-
46
- // Type validation should work in strict mode
47
- await expect(
48
- hs.write(holon, 'typed', {
49
- id: 'test',
50
- value: 'not-a-number' // Should fail if schema requires number
51
- }, { strict: true })
52
- ).rejects.toThrow('ValidationError');
53
- });
54
- });
55
-
56
- describe('Ajv validator compilation', () => {
57
- it('should compile schema using Ajv', async () => {
58
- await hs.setSchema('test', 'test://schema/compile');
59
-
60
- // Schema should be compiled and ready for validation
61
- const schema = await hs.getSchema('test');
62
- expect(schema).not.toBe(null);
63
- });
64
-
65
- it('should reuse compiled validator for same schema', async () => {
66
- const holon = await hs.toHolon(37.7749, -122.4194, 9);
67
- await hs.setSchema('cached', 'test://schema/cache');
68
-
69
- // Multiple validations should use cached compiled schema
70
- await hs.write(holon, 'cached', { id: 'item-1', value: 1 });
71
- await hs.write(holon, 'cached', { id: 'item-2', value: 2 });
72
- await hs.write(holon, 'cached', { id: 'item-3', value: 3 });
73
- });
74
- });
75
-
76
- describe('Schema caching (1-hour TTL)', () => {
77
- it('should cache schema after first fetch', async () => {
78
- await hs.setSchema('cache-test', 'test://schema/ttl');
79
-
80
- const schema1 = await hs.getSchema('cache-test');
81
- const schema2 = await hs.getSchema('cache-test');
82
-
83
- // Should return same cached schema
84
- expect(schema1).toEqual(schema2);
85
- });
86
-
87
- it('should use cached schema for validation', async () => {
88
- const holon = await hs.toHolon(37.7749, -122.4194, 9);
89
- await hs.setSchema('cached', 'test://schema/cached');
90
-
91
- // First write compiles schema
92
- await hs.write(holon, 'cached', { id: 'item-1', value: 1 });
93
-
94
- // Subsequent writes use cached compiled schema
95
- await hs.write(holon, 'cached', { id: 'item-2', value: 2 });
96
- });
97
-
98
- it('should respect 1-hour cache TTL', async () => {
99
- // Note: Actual TTL testing would require mocking time
100
- await hs.setSchema('ttl-test', 'test://schema/ttl');
101
-
102
- const schema = await hs.getSchema('ttl-test');
103
- expect(schema).not.toBe(null);
104
- });
105
- });
106
-
107
- describe('Cache expiration and recompilation', () => {
108
- it('should allow cache invalidation via clearSchema', async () => {
109
- await hs.setSchema('clear-test', 'test://schema/clear');
110
-
111
- const before = await hs.getSchema('clear-test');
112
- expect(before).not.toBe(null);
113
-
114
- await hs.clearSchema('clear-test');
115
-
116
- const after = await hs.getSchema('clear-test');
117
- expect(after).toBe(null);
118
- });
119
-
120
- it('should recompile after cache expiration', async () => {
121
- await hs.setSchema('recompile', 'test://schema/recompile');
122
-
123
- // After clearing, should recompile on next use
124
- await hs.clearSchema('recompile');
125
- await hs.setSchema('recompile', 'test://schema/recompile');
126
-
127
- const schema = await hs.getSchema('recompile');
128
- expect(schema).not.toBe(null);
129
- });
130
- });
131
-
132
- describe('Strict vs permissive mode', () => {
133
- it('should allow invalid data in permissive mode', async () => {
134
- const holon = await hs.toHolon(37.7749, -122.4194, 9);
135
- await hs.setSchema('permissive', 'test://schema/permissive', false);
136
-
137
- // Invalid data should succeed with warning (not thrown)
138
- const result = await hs.write(holon, 'permissive', {
139
- id: 'invalid',
140
- value: 'not-a-number'
141
- });
142
-
143
- expect(result).toBe(true);
144
- });
145
-
146
- it('should reject invalid data in strict mode', async () => {
147
- const holon = await hs.toHolon(37.7749, -122.4194, 9);
148
- await hs.setSchema('strict', {
149
- type: 'object',
150
- properties: {
151
- id: { type: 'string' },
152
- value: { type: 'number' }
153
- },
154
- required: ['id', 'value']
155
- }, true);
156
-
157
- await expect(
158
- hs.write(holon, 'strict', {
159
- id: 'invalid',
160
- value: 'not-a-number'
161
- }, { strict: true })
162
- ).rejects.toThrow('ValidationError');
163
- });
164
-
165
- it('should log warnings in permissive mode', async () => {
166
- const holon = await hs.toHolon(37.7749, -122.4194, 9);
167
- await hs.setSchema('log-test', 'test://schema/log', false);
168
-
169
- // Should log warning but not throw
170
- const result = await hs.write(holon, 'log-test', {
171
- id: 'invalid',
172
- invalid: 'field'
173
- });
174
-
175
- expect(result).toBe(true);
176
- });
177
-
178
- it('should include Ajv errors in ValidationError', async () => {
179
- const holon = await hs.toHolon(37.7749, -122.4194, 9);
180
- await hs.setSchema('error-test', 'test://schema/error', true);
181
-
182
- try {
183
- await hs.write(holon, 'error-test', {
184
- id: 'bad',
185
- value: 'invalid'
186
- }, { strict: true });
187
- } catch (err) {
188
- expect(err.constructor.name).toBe('ValidationError');
189
- expect(err).toHaveProperty('errors');
190
- expect(Array.isArray(err.errors)).toBe(true);
191
- }
192
- });
193
- });
194
-
195
- describe('Schema URI handling', () => {
196
- it('should fetch schema from URI', async () => {
197
- await hs.setSchema('uri-test', 'https://example.com/schema.json');
198
- // Should attempt to fetch schema
199
- });
200
-
201
- it('should handle schema fetch errors', async () => {
202
- // Test that invalid schema objects are rejected
203
- await expect(
204
- hs.setSchema('fail-test', null)
205
- ).rejects.toThrow(Error);
206
- });
207
-
208
- it('should support local schema URIs', async () => {
209
- await hs.setSchema('local-test', 'file://./schemas/local.json');
210
- // Should handle local file URIs
211
- });
212
- });
213
- });
@@ -1,158 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import HoloSphere from '../../src/index.js';
3
-
4
- describe('Unit: Spatial Module', () => {
5
- const hs = new HoloSphere({ relays: [], appName: 'test-spatial-unit' });
6
-
7
- describe('H3 validation', () => {
8
- it('should validate H3 format starts with 8', () => {
9
- expect(hs.isValidH3('8928342e20fffff')).toBe(true);
10
- expect(hs.isValidH3('7928342e20fffff')).toBe(false);
11
- });
12
-
13
- it('should validate H3 format has at least 15 characters', () => {
14
- expect(hs.isValidH3('8928342e20fffff')).toBe(true);
15
- expect(hs.isValidH3('8928342e20f')).toBe(false);
16
- expect(hs.isValidH3('8928342')).toBe(false);
17
- });
18
-
19
- it('should validate H3 contains only hex characters', () => {
20
- expect(hs.isValidH3('8928342e20fffff')).toBe(true);
21
- expect(hs.isValidH3('8928342g20fffff')).toBe(false);
22
- expect(hs.isValidH3('8928342!20fffff')).toBe(false);
23
- });
24
-
25
- it('should reject non-string inputs', () => {
26
- expect(hs.isValidH3(123)).toBe(false);
27
- expect(hs.isValidH3(null)).toBe(false);
28
- expect(hs.isValidH3(undefined)).toBe(false);
29
- expect(hs.isValidH3({})).toBe(false);
30
- expect(hs.isValidH3([])).toBe(false);
31
- });
32
- });
33
-
34
- describe('Coordinate to H3 conversion', () => {
35
- it('should convert valid coordinates to H3', async () => {
36
- const h3 = await hs.toHolon(37.7749, -122.4194, 9);
37
- expect(typeof h3).toBe('string');
38
- expect(hs.isValidH3(h3)).toBe(true);
39
- });
40
-
41
- it('should produce consistent results for same coordinates', async () => {
42
- const h3a = await hs.toHolon(37.7749, -122.4194, 9);
43
- const h3b = await hs.toHolon(37.7749, -122.4194, 9);
44
- expect(h3a).toBe(h3b);
45
- });
46
-
47
- it('should produce different H3 for different resolutions', async () => {
48
- const h3res7 = await hs.toHolon(37.7749, -122.4194, 7);
49
- const h3res9 = await hs.toHolon(37.7749, -122.4194, 9);
50
- expect(h3res7).not.toBe(h3res9);
51
- });
52
-
53
- it('should handle resolution 0 (largest)', async () => {
54
- const h3 = await hs.toHolon(37.7749, -122.4194, 0);
55
- expect(hs.isValidH3(h3)).toBe(true);
56
- });
57
-
58
- it('should handle resolution 15 (smallest)', async () => {
59
- const h3 = await hs.toHolon(37.7749, -122.4194, 15);
60
- expect(hs.isValidH3(h3)).toBe(true);
61
- });
62
-
63
- it('should throw RangeError for invalid latitude', async () => {
64
- await expect(hs.toHolon(91, -122.4194, 9)).rejects.toThrow(RangeError);
65
- await expect(hs.toHolon(-91, -122.4194, 9)).rejects.toThrow(RangeError);
66
- });
67
-
68
- it('should throw RangeError for invalid longitude', async () => {
69
- await expect(hs.toHolon(37.7749, 181, 9)).rejects.toThrow(RangeError);
70
- await expect(hs.toHolon(37.7749, -181, 9)).rejects.toThrow(RangeError);
71
- });
72
-
73
- it('should throw RangeError for invalid resolution', async () => {
74
- await expect(hs.toHolon(37.7749, -122.4194, -1)).rejects.toThrow(RangeError);
75
- await expect(hs.toHolon(37.7749, -122.4194, 16)).rejects.toThrow(RangeError);
76
- });
77
- });
78
-
79
- describe('Parent/child hierarchy navigation', () => {
80
- it('should get parents for valid H3', async () => {
81
- const h3 = await hs.toHolon(37.7749, -122.4194, 9);
82
- const parents = await hs.getParents(h3);
83
- expect(Array.isArray(parents)).toBe(true);
84
- expect(parents.length).toBeGreaterThan(0);
85
- });
86
-
87
- it('should get parents up to specified maxResolution', async () => {
88
- const h3 = await hs.toHolon(37.7749, -122.4194, 9);
89
- const parents = await hs.getParents(h3, 5);
90
- expect(Array.isArray(parents)).toBe(true);
91
- // Should stop at resolution 5
92
- });
93
-
94
- it('should return empty array for resolution 0 (no parents)', async () => {
95
- const h3 = await hs.toHolon(37.7749, -122.4194, 0);
96
- const parents = await hs.getParents(h3);
97
- expect(parents).toEqual([]);
98
- });
99
-
100
- it('should get 7 children for valid H3', async () => {
101
- const h3 = await hs.toHolon(37.7749, -122.4194, 9);
102
- const children = await hs.getChildren(h3);
103
- expect(Array.isArray(children)).toBe(true);
104
- expect(children.length).toBe(7);
105
- });
106
-
107
- it('should throw Error for resolution 15 (no children)', async () => {
108
- const h3 = await hs.toHolon(37.7749, -122.4194, 15);
109
- await expect(hs.getChildren(h3)).rejects.toThrow(Error);
110
- });
111
-
112
- it('should validate parent-child relationships', async () => {
113
- const parent = await hs.toHolon(37.7749, -122.4194, 7);
114
- const children = await hs.getChildren(parent);
115
-
116
- // One of the children should contain our test point at resolution 8
117
- expect(children.length).toBe(7);
118
- });
119
- });
120
-
121
- describe('Resolution extraction', () => {
122
- it('should extract resolution from H3 ID', async () => {
123
- const h3res5 = await hs.toHolon(37.7749, -122.4194, 5);
124
- const h3res9 = await hs.toHolon(37.7749, -122.4194, 9);
125
- const h3res12 = await hs.toHolon(37.7749, -122.4194, 12);
126
-
127
- // getResolution should be available if implemented
128
- if (typeof hs.getResolution === 'function') {
129
- expect(hs.getResolution(h3res5)).toBe(5);
130
- expect(hs.getResolution(h3res9)).toBe(9);
131
- expect(hs.getResolution(h3res12)).toBe(12);
132
- }
133
- });
134
- });
135
-
136
- describe('Edge cases', () => {
137
- it('should handle coordinates at poles', async () => {
138
- const northPole = await hs.toHolon(89.9, 0, 5);
139
- const southPole = await hs.toHolon(-89.9, 0, 5);
140
-
141
- expect(hs.isValidH3(northPole)).toBe(true);
142
- expect(hs.isValidH3(southPole)).toBe(true);
143
- });
144
-
145
- it('should handle coordinates at dateline', async () => {
146
- const east = await hs.toHolon(0, 179.9, 5);
147
- const west = await hs.toHolon(0, -179.9, 5);
148
-
149
- expect(hs.isValidH3(east)).toBe(true);
150
- expect(hs.isValidH3(west)).toBe(true);
151
- });
152
-
153
- it('should handle coordinates at equator and prime meridian', async () => {
154
- const origin = await hs.toHolon(0, 0, 9);
155
- expect(hs.isValidH3(origin)).toBe(true);
156
- });
157
- });
158
- });
@@ -1,195 +0,0 @@
1
- import { describe, it, expect, beforeEach } from 'vitest';
2
- import HoloSphere from '../../src/index.js';
3
-
4
- describe('Unit: Storage Module', () => {
5
- let hs;
6
- let testHolonId;
7
-
8
- beforeEach(async () => {
9
- hs = new HoloSphere({ relays: [], appName: 'test-storage-unit' });
10
- testHolonId = await hs.toHolon(37.7749, -122.4194, 9);
11
- });
12
-
13
- describe('Path construction', () => {
14
- it('should construct path with appname/holon/lens/key format', async () => {
15
- // Internal path construction (implementation detail)
16
- // Path should follow format: appname/holon/lens/key
17
- await hs.write(testHolonId, 'test-lens', { id: 'test-key', data: 'value' });
18
-
19
- const result = await hs.read(testHolonId, 'test-lens', 'test-key');
20
- expect(result).not.toBe(null);
21
- });
22
-
23
- it('should encode special characters in paths', async () => {
24
- const lensWithSpaces = 'my test lens';
25
- const keyWithSpecial = 'key!@#$%';
26
-
27
- await hs.write(testHolonId, lensWithSpaces, {
28
- id: keyWithSpecial,
29
- data: 'test'
30
- });
31
-
32
- const result = await hs.read(testHolonId, lensWithSpaces, keyWithSpecial);
33
- expect(result).not.toBe(null);
34
- });
35
-
36
- it('should handle unicode characters in paths', async () => {
37
- const unicodeLens = '测试镜头';
38
- const unicodeKey = 'キー001';
39
-
40
- await hs.write(testHolonId, unicodeLens, {
41
- id: unicodeKey,
42
- data: 'unicode test'
43
- });
44
-
45
- const result = await hs.read(testHolonId, unicodeLens, unicodeKey);
46
- expect(result).not.toBe(null);
47
- });
48
- });
49
-
50
- describe('Gun graph operations', () => {
51
- it('should perform Gun .put() for write', async () => {
52
- const success = await hs.write(testHolonId, 'test', {
53
- id: 'item-1',
54
- value: 'data'
55
- });
56
-
57
- expect(success).toBe(true);
58
- });
59
-
60
- it('should perform Gun .once() for read', async () => {
61
- await hs.write(testHolonId, 'test', { id: 'item-1', value: 'data' });
62
-
63
- const result = await hs.read(testHolonId, 'test', 'item-1');
64
- expect(result).toMatchObject({ id: 'item-1', value: 'data' });
65
- });
66
-
67
- it('should return null for non-existent data', async () => {
68
- const result = await hs.read(testHolonId, 'nonexistent', 'nonexistent');
69
- expect(result).toBe(null);
70
- });
71
-
72
- it('should perform Gun .put(null) for delete (tombstone)', async () => {
73
- await hs.write(testHolonId, 'test', { id: 'item-1', value: 'data' });
74
-
75
- const deleted = await hs.delete(testHolonId, 'test', 'item-1');
76
- expect(deleted).toBe(true);
77
-
78
- const result = await hs.read(testHolonId, 'test', 'item-1');
79
- expect(result).toBe(null);
80
- });
81
-
82
- it('should perform Gun .on() for subscribe', () => {
83
- const callback = () => {};
84
- const subscription = hs.subscribe(testHolonId, 'test', callback);
85
-
86
- expect(subscription).toHaveProperty('unsubscribe');
87
- subscription.unsubscribe();
88
- });
89
- });
90
-
91
- describe('Radisk persistence', () => {
92
- it('should persist data with radisk enabled', async () => {
93
- const hsWithRadisk = new HoloSphere({
94
- appName: 'radisk-test',
95
- radisk: true
96
- });
97
-
98
- const holon = await hsWithRadisk.toHolon(37.7749, -122.4194, 9);
99
- await hsWithRadisk.write(holon, 'persist', {
100
- id: 'persist-1',
101
- value: 'persisted'
102
- });
103
-
104
- // Data should be written to disk via radisk
105
- const result = await hsWithRadisk.read(holon, 'persist', 'persist-1');
106
- expect(result).not.toBe(null);
107
- });
108
-
109
- it('should work without radisk when disabled', async () => {
110
- const hsNoRadisk = new HoloSphere({
111
- appName: 'no-radisk-test',
112
- radisk: false
113
- });
114
-
115
- const holon = await hsNoRadisk.toHolon(37.7749, -122.4194, 9);
116
- await hsNoRadisk.write(holon, 'memory', {
117
- id: 'mem-1',
118
- value: 'in-memory'
119
- });
120
-
121
- const result = await hsNoRadisk.read(holon, 'memory', 'mem-1');
122
- expect(result).not.toBe(null);
123
- });
124
- });
125
-
126
- describe('Path encoding', () => {
127
- it('should encode forward slashes in lens names', async () => {
128
- const lensWithSlash = 'category/subcategory';
129
- await hs.write(testHolonId, lensWithSlash, {
130
- id: 'item-1',
131
- data: 'test'
132
- });
133
-
134
- const result = await hs.read(testHolonId, lensWithSlash, 'item-1');
135
- expect(result).not.toBe(null);
136
- });
137
-
138
- it('should encode dots in keys', async () => {
139
- await hs.write(testHolonId, 'test', {
140
- id: 'item.with.dots',
141
- data: 'test'
142
- });
143
-
144
- const result = await hs.read(testHolonId, 'test', 'item.with.dots');
145
- expect(result).not.toBe(null);
146
- });
147
-
148
- it('should handle empty strings gracefully', async () => {
149
- await expect(
150
- hs.write(testHolonId, '', { id: 'test' })
151
- ).rejects.toThrow(Error);
152
- });
153
- });
154
-
155
- describe('Error handling', () => {
156
- it('should handle Gun connection errors', async () => {
157
- // Test with invalid configuration
158
- const hsInvalid = new HoloSphere({
159
- appName: 'invalid-test',
160
- peers: ['https://nonexistent.invalid/gun']
161
- });
162
-
163
- // Should still work locally with radisk
164
- const holon = await hsInvalid.toHolon(37.7749, -122.4194, 9);
165
- const success = await hsInvalid.write(holon, 'test', {
166
- id: 'test-1',
167
- data: 'local'
168
- });
169
-
170
- // May succeed locally even if peer connection fails
171
- expect(typeof success).toBe('boolean');
172
- });
173
-
174
- it('should handle malformed data gracefully', async () => {
175
- // Gun should handle various data types
176
- await hs.write(testHolonId, 'test', {
177
- id: 'complex',
178
- nested: { deep: { object: 'value' } },
179
- array: [1, 2, 3],
180
- number: 42,
181
- boolean: true
182
- });
183
-
184
- const result = await hs.read(testHolonId, 'test', 'complex');
185
- expect(result).not.toBe(null);
186
- });
187
-
188
- it('should throw ValidationError on invalid input', async () => {
189
- // Should throw ValidationError with empty holon ID
190
- await expect(
191
- hs.write('', 'test', { id: 'test' })
192
- ).rejects.toThrow('holonId must be a non-empty string');
193
- });
194
- });
195
- });