holosphere 1.1.20 → 2.0.0-alpha1

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 (147) hide show
  1. package/.env.example +36 -0
  2. package/.eslintrc.json +16 -0
  3. package/.prettierrc.json +7 -0
  4. package/LICENSE +162 -38
  5. package/README.md +483 -367
  6. package/bin/holosphere-activitypub.js +158 -0
  7. package/cleanup-test-data.js +204 -0
  8. package/examples/demo.html +1333 -0
  9. package/examples/example-bot.js +197 -0
  10. package/package.json +47 -87
  11. package/scripts/check-bundle-size.js +54 -0
  12. package/scripts/check-quest-ids.js +77 -0
  13. package/scripts/import-holons.js +578 -0
  14. package/scripts/publish-to-relay.js +101 -0
  15. package/scripts/read-example.js +186 -0
  16. package/scripts/relay-diagnostic.js +59 -0
  17. package/scripts/relay-example.js +179 -0
  18. package/scripts/resync-to-relay.js +245 -0
  19. package/scripts/revert-import.js +196 -0
  20. package/scripts/test-hybrid-mode.js +108 -0
  21. package/scripts/test-local-storage.js +63 -0
  22. package/scripts/test-nostr-direct.js +55 -0
  23. package/scripts/test-read-data.js +45 -0
  24. package/scripts/test-write-read.js +63 -0
  25. package/scripts/verify-import.js +95 -0
  26. package/scripts/verify-relay-data.js +139 -0
  27. package/src/ai/aggregation.js +319 -0
  28. package/src/ai/breakdown.js +511 -0
  29. package/src/ai/classifier.js +217 -0
  30. package/src/ai/council.js +228 -0
  31. package/src/ai/embeddings.js +279 -0
  32. package/src/ai/federation-ai.js +324 -0
  33. package/src/ai/h3-ai.js +955 -0
  34. package/src/ai/index.js +112 -0
  35. package/src/ai/json-ops.js +225 -0
  36. package/src/ai/llm-service.js +205 -0
  37. package/src/ai/nl-query.js +223 -0
  38. package/src/ai/relationships.js +353 -0
  39. package/src/ai/schema-extractor.js +218 -0
  40. package/src/ai/spatial.js +293 -0
  41. package/src/ai/tts.js +194 -0
  42. package/src/content/social-protocols.js +168 -0
  43. package/src/core/holosphere.js +273 -0
  44. package/src/crypto/secp256k1.js +259 -0
  45. package/src/federation/discovery.js +334 -0
  46. package/src/federation/hologram.js +1042 -0
  47. package/src/federation/registry.js +386 -0
  48. package/src/hierarchical/upcast.js +110 -0
  49. package/src/index.js +2669 -0
  50. package/src/schema/validator.js +91 -0
  51. package/src/spatial/h3-operations.js +110 -0
  52. package/src/storage/backend-factory.js +125 -0
  53. package/src/storage/backend-interface.js +142 -0
  54. package/src/storage/backends/activitypub/server.js +653 -0
  55. package/src/storage/backends/activitypub-backend.js +272 -0
  56. package/src/storage/backends/gundb-backend.js +233 -0
  57. package/src/storage/backends/nostr-backend.js +136 -0
  58. package/src/storage/filesystem-storage-browser.js +41 -0
  59. package/src/storage/filesystem-storage.js +138 -0
  60. package/src/storage/global-tables.js +81 -0
  61. package/src/storage/gun-async.js +281 -0
  62. package/src/storage/gun-wrapper.js +221 -0
  63. package/src/storage/indexeddb-storage.js +122 -0
  64. package/src/storage/key-storage-simple.js +76 -0
  65. package/src/storage/key-storage.js +136 -0
  66. package/src/storage/memory-storage.js +59 -0
  67. package/src/storage/migration.js +338 -0
  68. package/src/storage/nostr-async.js +811 -0
  69. package/src/storage/nostr-client.js +939 -0
  70. package/src/storage/nostr-wrapper.js +211 -0
  71. package/src/storage/outbox-queue.js +208 -0
  72. package/src/storage/persistent-storage.js +109 -0
  73. package/src/storage/sync-service.js +164 -0
  74. package/src/subscriptions/manager.js +142 -0
  75. package/test-ai-real-api.js +202 -0
  76. package/tests/unit/ai/aggregation.test.js +295 -0
  77. package/tests/unit/ai/breakdown.test.js +446 -0
  78. package/tests/unit/ai/classifier.test.js +294 -0
  79. package/tests/unit/ai/council.test.js +262 -0
  80. package/tests/unit/ai/embeddings.test.js +384 -0
  81. package/tests/unit/ai/federation-ai.test.js +344 -0
  82. package/tests/unit/ai/h3-ai.test.js +458 -0
  83. package/tests/unit/ai/index.test.js +304 -0
  84. package/tests/unit/ai/json-ops.test.js +307 -0
  85. package/tests/unit/ai/llm-service.test.js +390 -0
  86. package/tests/unit/ai/nl-query.test.js +383 -0
  87. package/tests/unit/ai/relationships.test.js +311 -0
  88. package/tests/unit/ai/schema-extractor.test.js +384 -0
  89. package/tests/unit/ai/spatial.test.js +279 -0
  90. package/tests/unit/ai/tts.test.js +279 -0
  91. package/tests/unit/content.test.js +332 -0
  92. package/tests/unit/contract/core.test.js +88 -0
  93. package/tests/unit/contract/crypto.test.js +198 -0
  94. package/tests/unit/contract/data.test.js +223 -0
  95. package/tests/unit/contract/federation.test.js +181 -0
  96. package/tests/unit/contract/hierarchical.test.js +113 -0
  97. package/tests/unit/contract/schema.test.js +114 -0
  98. package/tests/unit/contract/social.test.js +217 -0
  99. package/tests/unit/contract/spatial.test.js +110 -0
  100. package/tests/unit/contract/subscriptions.test.js +128 -0
  101. package/tests/unit/contract/utils.test.js +159 -0
  102. package/tests/unit/core.test.js +152 -0
  103. package/tests/unit/crypto.test.js +328 -0
  104. package/tests/unit/federation.test.js +234 -0
  105. package/tests/unit/gun-async.test.js +252 -0
  106. package/tests/unit/hierarchical.test.js +399 -0
  107. package/tests/unit/integration/scenario-01-geographic-storage.test.js +74 -0
  108. package/tests/unit/integration/scenario-02-federation.test.js +76 -0
  109. package/tests/unit/integration/scenario-03-subscriptions.test.js +102 -0
  110. package/tests/unit/integration/scenario-04-validation.test.js +129 -0
  111. package/tests/unit/integration/scenario-05-hierarchy.test.js +125 -0
  112. package/tests/unit/integration/scenario-06-social.test.js +135 -0
  113. package/tests/unit/integration/scenario-07-persistence.test.js +130 -0
  114. package/tests/unit/integration/scenario-08-authorization.test.js +161 -0
  115. package/tests/unit/integration/scenario-09-cross-dimensional.test.js +139 -0
  116. package/tests/unit/integration/scenario-10-cross-holosphere-capabilities.test.js +357 -0
  117. package/tests/unit/integration/scenario-11-cross-holosphere-federation.test.js +410 -0
  118. package/tests/unit/integration/scenario-12-capability-federated-read.test.js +719 -0
  119. package/tests/unit/performance/benchmark.test.js +85 -0
  120. package/tests/unit/schema.test.js +213 -0
  121. package/tests/unit/spatial.test.js +158 -0
  122. package/tests/unit/storage.test.js +195 -0
  123. package/tests/unit/subscriptions.test.js +328 -0
  124. package/tests/unit/test-data-permanence-debug.js +197 -0
  125. package/tests/unit/test-data-permanence.js +340 -0
  126. package/tests/unit/test-key-persistence-fixed.js +148 -0
  127. package/tests/unit/test-key-persistence.js +172 -0
  128. package/tests/unit/test-relay-permanence.js +376 -0
  129. package/tests/unit/test-second-node.js +95 -0
  130. package/tests/unit/test-simple-write.js +89 -0
  131. package/vite.config.js +49 -0
  132. package/vitest.config.js +20 -0
  133. package/FEDERATION.md +0 -213
  134. package/compute.js +0 -298
  135. package/content.js +0 -980
  136. package/federation.js +0 -1234
  137. package/global.js +0 -736
  138. package/hexlib.js +0 -335
  139. package/hologram.js +0 -183
  140. package/holosphere-bundle.esm.js +0 -33256
  141. package/holosphere-bundle.js +0 -33287
  142. package/holosphere-bundle.min.js +0 -39
  143. package/holosphere.d.ts +0 -601
  144. package/holosphere.js +0 -719
  145. package/node.js +0 -246
  146. package/schema.js +0 -139
  147. package/utils.js +0 -302
@@ -0,0 +1,328 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import HoloSphere from '../../src/index.js';
3
+
4
+ describe('Unit: Subscriptions Module', () => {
5
+ let hs;
6
+ let testHolonId;
7
+
8
+ beforeEach(async () => {
9
+ hs = new HoloSphere({ relays: [], appName: 'test-subscriptions-unit' });
10
+ testHolonId = await hs.toHolon(37.7749, -122.4194, 9);
11
+ });
12
+
13
+ describe('Gun .on() listener attachment', () => {
14
+ it('should attach Gun listener on subscribe', () => {
15
+ const callback = vi.fn();
16
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
17
+
18
+ expect(subscription).toHaveProperty('unsubscribe');
19
+ expect(typeof subscription.unsubscribe).toBe('function');
20
+
21
+ subscription.unsubscribe();
22
+ });
23
+
24
+ it('should attach listener for specific holon and lens', () => {
25
+ const callback = vi.fn();
26
+ const subscription = hs.subscribe(testHolonId, 'specific-lens', callback);
27
+
28
+ expect(subscription).toBeDefined();
29
+ subscription.unsubscribe();
30
+ });
31
+
32
+ it('should support multiple subscriptions to same holon/lens', () => {
33
+ const callback1 = vi.fn();
34
+ const callback2 = vi.fn();
35
+
36
+ const sub1 = hs.subscribe(testHolonId, 'test', callback1);
37
+ const sub2 = hs.subscribe(testHolonId, 'test', callback2);
38
+
39
+ expect(sub1).toBeDefined();
40
+ expect(sub2).toBeDefined();
41
+
42
+ sub1.unsubscribe();
43
+ sub2.unsubscribe();
44
+ });
45
+
46
+ it('should support subscriptions to different lenses', () => {
47
+ const callback1 = vi.fn();
48
+ const callback2 = vi.fn();
49
+
50
+ const sub1 = hs.subscribe(testHolonId, 'lens1', callback1);
51
+ const sub2 = hs.subscribe(testHolonId, 'lens2', callback2);
52
+
53
+ expect(sub1).toBeDefined();
54
+ expect(sub2).toBeDefined();
55
+
56
+ sub1.unsubscribe();
57
+ sub2.unsubscribe();
58
+ });
59
+ });
60
+
61
+ describe('Callback invocation on data changes', () => {
62
+ it('should invoke callback on write', async () => {
63
+ const callback = vi.fn();
64
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
65
+
66
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'data' });
67
+ await new Promise(resolve => setTimeout(resolve, 100));
68
+
69
+ expect(callback).toHaveBeenCalled();
70
+ subscription.unsubscribe();
71
+ });
72
+
73
+ it('should invoke callback on update', async () => {
74
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'original' });
75
+
76
+ const callback = vi.fn();
77
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
78
+
79
+ await hs.update(testHolonId, 'test', 'item-1', { value: 'updated' });
80
+ await new Promise(resolve => setTimeout(resolve, 100));
81
+
82
+ expect(callback).toHaveBeenCalled();
83
+ subscription.unsubscribe();
84
+ });
85
+
86
+ it('should pass data to callback', async () => {
87
+ const callback = vi.fn();
88
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
89
+
90
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'test-data' });
91
+ await new Promise(resolve => setTimeout(resolve, 100));
92
+
93
+ expect(callback).toHaveBeenCalledWith(
94
+ expect.objectContaining({ id: 'item-1', value: 'test-data' }),
95
+ expect.any(String)
96
+ );
97
+
98
+ subscription.unsubscribe();
99
+ });
100
+
101
+ it('should pass key to callback', async () => {
102
+ const callback = vi.fn();
103
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
104
+
105
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'data' });
106
+ await new Promise(resolve => setTimeout(resolve, 100));
107
+
108
+ expect(callback).toHaveBeenCalledWith(
109
+ expect.any(Object),
110
+ expect.any(String)
111
+ );
112
+
113
+ subscription.unsubscribe();
114
+ });
115
+
116
+ it('should not invoke callback on other lens changes', async () => {
117
+ const callback = vi.fn();
118
+ const subscription = hs.subscribe(testHolonId, 'lens-a', callback);
119
+
120
+ await hs.write(testHolonId, 'lens-b', { id: 'item-1', value: 'data' });
121
+ await new Promise(resolve => setTimeout(resolve, 100));
122
+
123
+ expect(callback).not.toHaveBeenCalled();
124
+ subscription.unsubscribe();
125
+ });
126
+ });
127
+
128
+ describe('Throttling mechanism', () => {
129
+ it('should throttle callbacks when throttle option provided', async () => {
130
+ const callback = vi.fn();
131
+ const subscription = hs.subscribe(testHolonId, 'test', callback, {
132
+ throttle: 500 // 500ms throttle
133
+ });
134
+
135
+ // Rapid writes
136
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'v1' });
137
+ await hs.write(testHolonId, 'test', { id: 'item-2', value: 'v2' });
138
+ await hs.write(testHolonId, 'test', { id: 'item-3', value: 'v3' });
139
+
140
+ await new Promise(resolve => setTimeout(resolve, 100));
141
+
142
+ // Should be throttled (fewer calls than writes)
143
+ const callCount = callback.mock.calls.length;
144
+ expect(callCount).toBeLessThan(3);
145
+
146
+ subscription.unsubscribe();
147
+ });
148
+
149
+ it('should not throttle when throttle is 0', async () => {
150
+ const callback = vi.fn();
151
+ const subscription = hs.subscribe(testHolonId, 'test', callback, {
152
+ throttle: 0
153
+ });
154
+
155
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'v1' });
156
+ await hs.write(testHolonId, 'test', { id: 'item-2', value: 'v2' });
157
+
158
+ await new Promise(resolve => setTimeout(resolve, 100));
159
+
160
+ expect(callback.mock.calls.length).toBeGreaterThanOrEqual(2);
161
+ subscription.unsubscribe();
162
+ });
163
+
164
+ it('should respect throttle interval', async () => {
165
+ const callback = vi.fn();
166
+ const subscription = hs.subscribe(testHolonId, 'test', callback, {
167
+ throttle: 200
168
+ });
169
+
170
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'v1' });
171
+ await new Promise(resolve => setTimeout(resolve, 50));
172
+ await hs.write(testHolonId, 'test', { id: 'item-2', value: 'v2' });
173
+ await new Promise(resolve => setTimeout(resolve, 250));
174
+
175
+ // After throttle period, should allow next callback
176
+ expect(callback.mock.calls.length).toBeGreaterThan(0);
177
+ subscription.unsubscribe();
178
+ });
179
+ });
180
+
181
+ describe('Filter predicate application', () => {
182
+ it('should apply filter function', async () => {
183
+ const callback = vi.fn();
184
+ const subscription = hs.subscribe(testHolonId, 'test', callback, {
185
+ filter: (data) => data.value > 10
186
+ });
187
+
188
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 5 });
189
+ await hs.write(testHolonId, 'test', { id: 'item-2', value: 15 });
190
+
191
+ await new Promise(resolve => setTimeout(resolve, 100));
192
+
193
+ // Only item-2 should pass filter
194
+ const calls = callback.mock.calls;
195
+ const passedFilter = calls.some(call => call[0].value === 15);
196
+ const failedFilter = calls.some(call => call[0].value === 5);
197
+
198
+ expect(passedFilter).toBe(true);
199
+ expect(failedFilter).toBe(false);
200
+
201
+ subscription.unsubscribe();
202
+ });
203
+
204
+ it('should invoke callback only for data matching filter', async () => {
205
+ const callback = vi.fn();
206
+ const subscription = hs.subscribe(testHolonId, 'test', callback, {
207
+ filter: (data) => data.priority === 'high'
208
+ });
209
+
210
+ await hs.write(testHolonId, 'test', { id: 'item-1', priority: 'low' });
211
+ await hs.write(testHolonId, 'test', { id: 'item-2', priority: 'high' });
212
+ await hs.write(testHolonId, 'test', { id: 'item-3', priority: 'medium' });
213
+
214
+ await new Promise(resolve => setTimeout(resolve, 100));
215
+
216
+ const highPriorityCalls = callback.mock.calls.filter(
217
+ call => call[0].priority === 'high'
218
+ );
219
+
220
+ expect(highPriorityCalls.length).toBeGreaterThan(0);
221
+ subscription.unsubscribe();
222
+ });
223
+
224
+ it('should pass all data when no filter provided', async () => {
225
+ const callback = vi.fn();
226
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
227
+
228
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'a' });
229
+ await hs.write(testHolonId, 'test', { id: 'item-2', value: 'b' });
230
+
231
+ await new Promise(resolve => setTimeout(resolve, 100));
232
+
233
+ expect(callback.mock.calls.length).toBeGreaterThanOrEqual(2);
234
+ subscription.unsubscribe();
235
+ });
236
+ });
237
+
238
+ describe('Unsubscribe cleanup', () => {
239
+ it('should stop callbacks after unsubscribe', async () => {
240
+ const callback = vi.fn();
241
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
242
+
243
+ subscription.unsubscribe();
244
+
245
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'data' });
246
+ await new Promise(resolve => setTimeout(resolve, 100));
247
+
248
+ expect(callback).not.toHaveBeenCalled();
249
+ });
250
+
251
+ it('should remove Gun listener on unsubscribe', async () => {
252
+ const callback = vi.fn();
253
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
254
+
255
+ await hs.write(testHolonId, 'test', { id: 'before', value: 'before' });
256
+ await new Promise(resolve => setTimeout(resolve, 100));
257
+
258
+ const beforeCount = callback.mock.calls.length;
259
+
260
+ subscription.unsubscribe();
261
+
262
+ await hs.write(testHolonId, 'test', { id: 'after', value: 'after' });
263
+ await new Promise(resolve => setTimeout(resolve, 100));
264
+
265
+ expect(callback.mock.calls.length).toBe(beforeCount);
266
+ });
267
+
268
+ it('should handle multiple unsubscribe calls gracefully', () => {
269
+ const callback = vi.fn();
270
+ const subscription = hs.subscribe(testHolonId, 'test', callback);
271
+
272
+ subscription.unsubscribe();
273
+ subscription.unsubscribe();
274
+ subscription.unsubscribe();
275
+
276
+ // Should not throw error
277
+ });
278
+
279
+ it('should cleanup individual subscriptions independently', async () => {
280
+ const callback1 = vi.fn();
281
+ const callback2 = vi.fn();
282
+
283
+ const sub1 = hs.subscribe(testHolonId, 'test', callback1);
284
+ const sub2 = hs.subscribe(testHolonId, 'test', callback2);
285
+
286
+ sub1.unsubscribe();
287
+
288
+ // Small delay to ensure unsubscribe is fully processed
289
+ await new Promise(resolve => setTimeout(resolve, 10));
290
+
291
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 'data' });
292
+ await new Promise(resolve => setTimeout(resolve, 100));
293
+
294
+ expect(callback1).not.toHaveBeenCalled();
295
+ expect(callback2).toHaveBeenCalled();
296
+
297
+ sub2.unsubscribe();
298
+ });
299
+ });
300
+
301
+ describe('Subscription options', () => {
302
+ it('should accept includeFederated option', () => {
303
+ const callback = vi.fn();
304
+ const subscription = hs.subscribe(testHolonId, 'test', callback, {
305
+ includeFederated: true
306
+ });
307
+
308
+ expect(subscription).toBeDefined();
309
+ subscription.unsubscribe();
310
+ });
311
+
312
+ it('should combine throttle and filter options', async () => {
313
+ const callback = vi.fn();
314
+ const subscription = hs.subscribe(testHolonId, 'test', callback, {
315
+ throttle: 100,
316
+ filter: (data) => data.value > 5
317
+ });
318
+
319
+ await hs.write(testHolonId, 'test', { id: 'item-1', value: 3 });
320
+ await hs.write(testHolonId, 'test', { id: 'item-2', value: 10 });
321
+
322
+ await new Promise(resolve => setTimeout(resolve, 200));
323
+
324
+ expect(callback.mock.calls.length).toBeGreaterThan(0);
325
+ subscription.unsubscribe();
326
+ });
327
+ });
328
+ });
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Debug Data Permanence Test
3
+ * Helps identify where in the stack data flow is breaking
4
+ */
5
+
6
+ import { HoloSphere } from './dist/esm/holosphere.js';
7
+
8
+ // Relay configuration - use env vars or these defaults
9
+ const RELAYS = process.env.HOLOSPHERE_RELAYS?.split(',') || [
10
+ 'wss://relay.holons.io',
11
+ 'wss://relay.nostr.band',
12
+ ];
13
+
14
+ console.log('\n=== DATA PERMANENCE DEBUG TEST ===\n');
15
+
16
+ const config = {
17
+ appName: 'debug-test',
18
+ relays: RELAYS,
19
+ logLevel: 'DEBUG' // More verbose logging
20
+ };
21
+
22
+ console.log('Configuration:', JSON.stringify(config, null, 2));
23
+
24
+ const hs = new HoloSphere(config);
25
+
26
+ console.log(`\nInitialized HoloSphere`);
27
+ console.log(`Public Key: ${hs.client.publicKey}`);
28
+ console.log(`Private Key (first 8 chars): ${hs.client.privateKey.substring(0, 8)}...`);
29
+ console.log(`Relays: ${config.relays.join(', ')}`);
30
+
31
+ // Wait for initialization
32
+ await new Promise(resolve => setTimeout(resolve, 2000));
33
+
34
+ console.log('\n--- TEST 1: Write and immediate read ---');
35
+
36
+ const testData = {
37
+ id: 'test-123',
38
+ message: 'Hello, world!',
39
+ timestamp: Date.now()
40
+ };
41
+
42
+ const holonId = 'global://test';
43
+ const lensName = 'data';
44
+
45
+ console.log(`\nWriting data:`);
46
+ console.log(` Holon: ${holonId}`);
47
+ console.log(` Lens: ${lensName}`);
48
+ console.log(` Data:`, JSON.stringify(testData, null, 2));
49
+
50
+ try {
51
+ const writeResult = await hs.write(holonId, lensName, testData);
52
+ console.log(`\nWrite result: ${writeResult}`);
53
+
54
+ if (!writeResult) {
55
+ console.log('\n❌ Write failed - checking client state...');
56
+ console.log(`Client has ${hs.client._eventCache.size} cached events`);
57
+ }
58
+ } catch (error) {
59
+ console.error('\n❌ Write error:', error);
60
+ }
61
+
62
+ console.log('\n--- Checking client cache ---');
63
+ console.log(`Cache size: ${hs.client._eventCache.size}`);
64
+
65
+ // Print cache contents
66
+ let cacheIndex = 0;
67
+ for (const [key, value] of hs.client._eventCache.entries()) {
68
+ console.log(`\nCache entry ${++cacheIndex}:`);
69
+ console.log(` Key: ${key}`);
70
+ console.log(` Value:`, JSON.stringify(value, null, 2));
71
+ }
72
+
73
+ console.log('\n--- Checking persistent storage ---');
74
+ if (hs.client.persistentStorage) {
75
+ try {
76
+ const allEvents = await hs.client.persistentStorage.getAll('');
77
+ console.log(`Persistent storage has ${allEvents.length} events`);
78
+
79
+ for (let i = 0; i < allEvents.length; i++) {
80
+ const event = allEvents[i];
81
+ console.log(`\nEvent ${i + 1}:`);
82
+ console.log(` ID: ${event.id}`);
83
+ console.log(` Kind: ${event.kind}`);
84
+ console.log(` Created: ${new Date(event.created_at * 1000).toISOString()}`);
85
+ const dTag = event.tags.find(t => t[0] === 'd');
86
+ if (dTag) console.log(` D-tag: ${dTag[1]}`);
87
+ console.log(` Content preview: ${event.content.substring(0, 100)}...`);
88
+ }
89
+ } catch (error) {
90
+ console.log('Error reading persistent storage:', error);
91
+ }
92
+ } else {
93
+ console.log('No persistent storage initialized');
94
+ }
95
+
96
+ console.log('\n--- TEST 2: Read data back ---');
97
+
98
+ try {
99
+ console.log(`\nReading from:`);
100
+ console.log(` Holon: ${holonId}`);
101
+ console.log(` Lens: ${lensName}`);
102
+
103
+ const readData = await hs.read(holonId, lensName);
104
+
105
+ console.log(`\nRead result:`, JSON.stringify(readData, null, 2));
106
+
107
+ if (!readData || readData.length === 0) {
108
+ console.log('\n❌ No data returned');
109
+
110
+ // Try to understand why
111
+ console.log('\n--- Debugging read operation ---');
112
+
113
+ // Build the path manually
114
+ const path = `${config.appName}/${encodeURIComponent(holonId)}/${encodeURIComponent(lensName)}`;
115
+ console.log(`Expected path: ${path}`);
116
+
117
+ // Check what the filter would be
118
+ console.log(`\nFilter that would be used:`);
119
+ console.log(` kinds: [30000]`);
120
+ console.log(` limit: 100`);
121
+
122
+ // Check if we can find matching events in cache
123
+ console.log(`\nSearching cache for matching events...`);
124
+ let foundInCache = false;
125
+ for (const [key, value] of hs.client._eventCache.entries()) {
126
+ if (value.events) {
127
+ for (const event of value.events) {
128
+ const dTag = event.tags.find(t => t[0] === 'd');
129
+ if (dTag && dTag[1].startsWith(path)) {
130
+ console.log(`\n✓ Found matching event in cache!`);
131
+ console.log(` D-tag: ${dTag[1]}`);
132
+ console.log(` Content: ${event.content}`);
133
+ foundInCache = true;
134
+ }
135
+ }
136
+ }
137
+ }
138
+
139
+ if (!foundInCache) {
140
+ console.log('No matching events found in cache');
141
+ }
142
+ } else {
143
+ console.log(`\n✅ Found ${readData.length} record(s)`);
144
+ const match = readData.find(d => d.id === testData.id);
145
+ if (match) {
146
+ console.log('✅ Test data found in results!');
147
+ } else {
148
+ console.log('❌ Test data NOT in results');
149
+ }
150
+ }
151
+ } catch (error) {
152
+ console.error('\n❌ Read error:', error);
153
+ }
154
+
155
+ console.log('\n--- TEST 3: Direct client query ---');
156
+
157
+ try {
158
+ const path = `${config.appName}/${encodeURIComponent(holonId)}/${encodeURIComponent(lensName)}`;
159
+ console.log(`\nQuerying with filter:`);
160
+
161
+ const filter = {
162
+ kinds: [30000],
163
+ limit: 100
164
+ };
165
+
166
+ console.log(JSON.stringify(filter, null, 2));
167
+
168
+ const events = await hs.client.query(filter, { timeout: 5000 });
169
+
170
+ console.log(`\nQuery returned ${events.length} events`);
171
+
172
+ for (let i = 0; i < events.length; i++) {
173
+ const event = events[i];
174
+ console.log(`\nEvent ${i + 1}:`);
175
+ console.log(` ID: ${event.id.substring(0, 16)}...`);
176
+ console.log(` Kind: ${event.kind}`);
177
+ console.log(` Pubkey: ${event.pubkey.substring(0, 16)}...`);
178
+ const dTag = event.tags.find(t => t[0] === 'd');
179
+ if (dTag) console.log(` D-tag: ${dTag[1]}`);
180
+ console.log(` Content: ${event.content.substring(0, 100)}...`);
181
+ }
182
+
183
+ // Filter by path prefix
184
+ const matching = events.filter(event => {
185
+ const dTag = event.tags.find(t => t[0] === 'd');
186
+ return dTag && dTag[1] && dTag[1].startsWith(path);
187
+ });
188
+
189
+ console.log(`\n${matching.length} events match our path prefix`);
190
+
191
+ } catch (error) {
192
+ console.error('\n❌ Query error:', error);
193
+ }
194
+
195
+ console.log('\n\n=== DEBUG TEST COMPLETE ===\n');
196
+
197
+ setTimeout(() => process.exit(0), 1000);