@xyo-network/node 4.3.0 → 5.0.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/node",
3
- "version": "4.3.0",
3
+ "version": "5.0.0",
4
4
  "description": "Primary SDK for using XYO Protocol 2.0",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -28,29 +28,33 @@
28
28
  },
29
29
  "module": "dist/neutral/index.mjs",
30
30
  "types": "dist/neutral/index.d.ts",
31
+ "files": [
32
+ "dist",
33
+ "src"
34
+ ],
31
35
  "dependencies": {
32
- "@xyo-network/node-model": "^4.3.0",
33
- "@xyo-network/node-wrapper": "^4.3.0"
36
+ "@xyo-network/node-model": "^5.0.0",
37
+ "@xyo-network/node-wrapper": "^5.0.0"
34
38
  },
35
39
  "devDependencies": {
36
- "@xylabs/delay": "^4.15.0",
37
- "@xylabs/ts-scripts-yarn3": "^7.0.1",
38
- "@xylabs/tsconfig": "^7.0.1",
39
- "@xylabs/vitest-extended": "^4.15.0",
40
- "@xylabs/vitest-matchers": "^4.15.0",
41
- "@xyo-network/account": "^4.3.0",
42
- "@xyo-network/account-model": "^4.3.0",
43
- "@xyo-network/archivist-memory": "^4.3.0",
44
- "@xyo-network/archivist-model": "^4.3.0",
45
- "@xyo-network/diviner-archivist": "^4.3.0",
46
- "@xyo-network/diviner-huri": "^4.3.0",
47
- "@xyo-network/diviner-model": "^4.3.0",
48
- "@xyo-network/manifest-model": "^4.3.0",
49
- "@xyo-network/module-model": "^4.3.0",
50
- "@xyo-network/node-memory": "^4.3.0",
51
- "@xyo-network/payload": "^4.3.0",
52
- "@xyo-network/wallet": "^4.3.0",
53
- "@xyo-network/witness-adhoc": "^4.3.0",
40
+ "@xylabs/delay": "^5.0.0",
41
+ "@xylabs/ts-scripts-yarn3": "^7.0.2",
42
+ "@xylabs/tsconfig": "^7.0.2",
43
+ "@xylabs/vitest-extended": "^5.0.0",
44
+ "@xylabs/vitest-matchers": "^5.0.0",
45
+ "@xyo-network/account": "^5.0.0",
46
+ "@xyo-network/account-model": "^5.0.0",
47
+ "@xyo-network/archivist-memory": "^5.0.0",
48
+ "@xyo-network/archivist-model": "^5.0.0",
49
+ "@xyo-network/diviner-archivist": "^5.0.0",
50
+ "@xyo-network/diviner-huri": "^5.0.0",
51
+ "@xyo-network/diviner-model": "^5.0.0",
52
+ "@xyo-network/manifest-model": "^5.0.0",
53
+ "@xyo-network/module-model": "^5.0.0",
54
+ "@xyo-network/node-memory": "^5.0.0",
55
+ "@xyo-network/payload": "^5.0.0",
56
+ "@xyo-network/wallet": "^5.0.0",
57
+ "@xyo-network/witness-adhoc": "^5.0.0",
54
58
  "typescript": "^5.8.3",
55
59
  "vitest": "^3.2.4"
56
60
  },
@@ -0,0 +1,57 @@
1
+ import '@xylabs/vitest-extended'
2
+
3
+ import type { NodeManifestPayload } from '@xyo-network/manifest-model'
4
+ import { MemoryNode } from '@xyo-network/node-memory'
5
+ import { AdhocWitness, AdhocWitnessConfigSchema } from '@xyo-network/witness-adhoc'
6
+ import {
7
+ describe, expect, it,
8
+ } from 'vitest'
9
+
10
+ /**
11
+ * @group node
12
+ * @group module
13
+ */
14
+
15
+ describe('MemoryNode', () => {
16
+ // AbstractModule.defaultLogger = new ConsoleLogger(LogLevel.log)
17
+ it('Creates MemoryNode from Manifest', async () => {
18
+ const memoryNode = await MemoryNode.create({
19
+ account: 'random',
20
+ config: { name: 'MemoryNode', schema: 'network.xyo.node.config' },
21
+ })
22
+ const privateWitnesses = [
23
+ await AdhocWitness.create({ account: 'random', config: { name: 'PrivateWitness1', schema: AdhocWitnessConfigSchema } }),
24
+ await AdhocWitness.create({ account: 'random', config: { name: 'PrivateWitness2', schema: AdhocWitnessConfigSchema } }),
25
+ await AdhocWitness.create({ account: 'random', config: { name: 'PrivateWitness3', schema: AdhocWitnessConfigSchema } }),
26
+ ]
27
+ const publicWitnesses = [
28
+ await AdhocWitness.create({ account: 'random', config: { name: 'PublicWitness1', schema: AdhocWitnessConfigSchema } }),
29
+ await AdhocWitness.create({ account: 'random', config: { name: 'PublicWitness2', schema: AdhocWitnessConfigSchema } }),
30
+ await AdhocWitness.create({ account: 'random', config: { name: 'PublicWitness3', schema: AdhocWitnessConfigSchema } }),
31
+ await AdhocWitness.create({ account: 'random', config: { name: 'PublicWitness4', schema: AdhocWitnessConfigSchema } }),
32
+ ]
33
+ await Promise.all(
34
+ publicWitnesses.map(async (witness) => {
35
+ await memoryNode.register(witness)
36
+ await memoryNode.attach(witness.address, true)
37
+ }),
38
+ )
39
+ await Promise.all(
40
+ privateWitnesses.map(async (witness) => {
41
+ await memoryNode.register(witness)
42
+ await memoryNode.attach(witness.address, false)
43
+ }),
44
+ )
45
+ const publicModules = await memoryNode.resolve('*')
46
+ expect(publicModules).toBeArrayOfSize(8)
47
+ /* const privateModules = await memoryNode.resolve('*', { visibility: 'private' })
48
+ expect(privateModules).toBeArrayOfSize(3)
49
+ const allModules = await memoryNode.resolve('*', { visibility: 'all' })
50
+ expect(allModules).toBeArrayOfSize(8) */
51
+
52
+ const manifest = (await memoryNode.manifest()) as NodeManifestPayload
53
+ // console.log(`manifest: ${toSafeJsonString(manifest, 10)}`)
54
+ expect(manifest.modules?.public ?? []).toBeArrayOfSize(4)
55
+ // expect(manifest.modules?.private).toBeArrayOfSize(3)
56
+ })
57
+ })
@@ -0,0 +1,434 @@
1
+ import { delay } from '@xylabs/delay'
2
+ import { matchers } from '@xylabs/vitest-matchers'
3
+ import type { AccountInstance } from '@xyo-network/account-model'
4
+ import { MemoryArchivist } from '@xyo-network/archivist-memory'
5
+ import type { AttachableArchivistInstance } from '@xyo-network/archivist-model'
6
+ import type {
7
+ AddressPayload,
8
+ AttachableModuleInstance,
9
+ Module,
10
+ ModuleDescription,
11
+ ModuleDescriptionPayload,
12
+ } from '@xyo-network/module-model'
13
+ import {
14
+ AddressSchema,
15
+ ModuleDescriptionSchema,
16
+ } from '@xyo-network/module-model'
17
+ import { MemoryNode, MemoryNodeHelper } from '@xyo-network/node-memory'
18
+ import type { ModuleAttachedEventArgs } from '@xyo-network/node-model'
19
+ import { NodeConfigSchema } from '@xyo-network/node-model'
20
+ import type { Payload } from '@xyo-network/payload'
21
+ import { isPayloadOfSchemaType } from '@xyo-network/payload'
22
+ import { HDWallet } from '@xyo-network/wallet'
23
+ import {
24
+ beforeAll,
25
+ beforeEach,
26
+ describe, expect, it,
27
+ } from 'vitest'
28
+
29
+ expect.extend(matchers)
30
+
31
+ /**
32
+ * @group node
33
+ * @group module
34
+ */
35
+
36
+ describe('MemoryNode', () => {
37
+ let testAccount1: AccountInstance
38
+ let testAccount2: AccountInstance
39
+ let testAccount3: AccountInstance
40
+ let testAccount4: AccountInstance
41
+ const archivistConfig = { schema: MemoryArchivist.defaultConfigSchema }
42
+ const nodeConfig = { schema: NodeConfigSchema }
43
+ let node: MemoryNode
44
+ beforeAll(async () => {
45
+ testAccount1 = await HDWallet.fromPhrase('cushion student broken thing poet mistake item dutch traffic gloom awful still')
46
+ testAccount2 = await HDWallet.fromPhrase('siren tenant achieve enough tone roof album champion tiny civil lottery hundred')
47
+ testAccount3 = await HDWallet.fromPhrase('person wheat floor tumble pond develop sauce attract neither toilet build enrich')
48
+ testAccount4 = await HDWallet.fromPhrase('kit sound script century margin into guilt region engine garment lab rifle')
49
+ })
50
+ beforeEach(async () => {
51
+ const nodeModule = await MemoryNode.create({ account: testAccount1, config: nodeConfig })
52
+ node = nodeModule
53
+ })
54
+ /* describe('create', () => {
55
+ describe('with autoAttachExternallyResolved true', () => {
56
+ it('attaches external modules to internal resolver', async () => {
57
+ // Arrange
58
+ // Create a MemoryNode with no modules in the internal
59
+ // resolver and one module in the external resolver
60
+ const resolver = new CompositeModuleResolver({})
61
+ const internalResolver = new CompositeModuleResolver({})
62
+ const params: MemoryNodeParams = {
63
+ config: { schema: NodeConfigSchema },
64
+ internalResolver,
65
+ resolver,
66
+ }
67
+ const module = await MemoryArchivist.create()
68
+ const filter = { address: [module.address] }
69
+ resolver.add(module)
70
+ // No modules exist in internal resolver
71
+ expect(await internalResolver.resolve(filter)).toBeArrayOfSize(0)
72
+ // Module exists in external resolver
73
+ expect(await resolver.resolve(filter)).toBeArrayOfSize(1)
74
+ const node = await MemoryNode.create(params)
75
+ // No modules are attached
76
+ expect(await node.attached()).toBeArrayOfSize(0)
77
+
78
+ // Act
79
+ // Query for unattached module (by address) that exists in
80
+ // external resolver
81
+ expect(await node.resolve(filter)).toBeArrayOfSize(1)
82
+
83
+ // Assert
84
+ // Module is now attached
85
+ expect(await node.attached()).toBeArrayOfSize(1)
86
+ // Module exists in internal resolver
87
+ expect(await internalResolver.resolve(filter)).toBeArrayOfSize(1)
88
+ // Module still exists in external resolver
89
+ expect(await resolver.resolve(filter)).toBeArrayOfSize(1)
90
+ })
91
+ })
92
+ }) */
93
+ describe('register', () => {
94
+ it('registers module', async () => {
95
+ const mod = await MemoryArchivist.create({ account: 'random' })
96
+ await node.register(mod)
97
+ })
98
+ })
99
+ describe('registered', () => {
100
+ describe('with no modules registered', () => {
101
+ it('returns empty array', async () => {
102
+ const result = await node.registered()
103
+ expect(result).toBeArrayOfSize(0)
104
+ })
105
+ })
106
+ describe('with modules registered', () => {
107
+ let mod: AttachableModuleInstance
108
+ beforeEach(async () => {
109
+ mod = await MemoryArchivist.create({ account: 'random' })
110
+ await node.register(mod)
111
+ })
112
+ it('lists addresses of registered modules', async () => {
113
+ const result = await node.registered()
114
+ expect(result).toBeArrayOfSize(1)
115
+ expect(result).toEqual([mod.address])
116
+ })
117
+ })
118
+ })
119
+ describe('attach', () => {
120
+ let mod: AttachableModuleInstance
121
+ beforeEach(async () => {
122
+ mod = await MemoryArchivist.create({ account: 'random' })
123
+ await node.register(mod)
124
+ })
125
+ it('attaches module', async () => {
126
+ await node.attach(mod.address, true)
127
+ })
128
+ it('emits event on module attach', async () => {
129
+ let eventDone = false
130
+ return await new Promise<void>((resolve, reject) => {
131
+ node.on('moduleAttached', (args) => {
132
+ const { mod } = args as ModuleAttachedEventArgs
133
+ expect(mod).toBeObject()
134
+ expect(mod.address).toBe(mod.address)
135
+ expect(mod).toBe(mod)
136
+ eventDone = true
137
+ })
138
+ node
139
+ .attach(mod.address, true)
140
+ .then(async () => {
141
+ // wait for up to 5 seconds
142
+ let waitFrames = 50
143
+ while (waitFrames) {
144
+ if (eventDone) {
145
+ resolve()
146
+ return
147
+ }
148
+ await delay(100)
149
+ waitFrames--
150
+ }
151
+ reject('Event not fired [within 5 seconds]')
152
+ })
153
+ .catch(() => {
154
+ reject('Attach failed')
155
+ })
156
+ })
157
+ })
158
+ })
159
+ describe('attached', () => {
160
+ let mod: AttachableModuleInstance
161
+ beforeEach(async () => {
162
+ mod = await MemoryArchivist.create({ account: 'random' })
163
+ await node.register(mod)
164
+ })
165
+ describe('with no modules attached', () => {
166
+ it('returns empty array', async () => {
167
+ const result = await node.attached()
168
+ expect(result).toBeArrayOfSize(0)
169
+ })
170
+ })
171
+ describe('with modules attached', () => {
172
+ it('lists addresses of attached modules', async () => {
173
+ await node.attach(mod.address, true)
174
+ const result = await node.attached()
175
+ expect(result.length).toBe(1)
176
+ expect(result).toEqual([mod.address])
177
+ })
178
+ })
179
+ })
180
+ describe('detach', () => {
181
+ let mod: AttachableModuleInstance
182
+ beforeEach(async () => {
183
+ mod = await MemoryArchivist.create({ account: 'random' })
184
+ await node.register(mod)
185
+ await node.attach(mod.address, true)
186
+ })
187
+ it('deregisters existing module', async () => {
188
+ await node.detach(mod.address)
189
+ })
190
+ /* it('allows deregistering non-existent mod', () => {
191
+ node.detach('4a15a6c96665931b76c1d2a587ea1132dbfdc266')
192
+ }) */
193
+ })
194
+ describe('registeredModules', () => {
195
+ let mod: AttachableModuleInstance
196
+ beforeEach(async () => {
197
+ mod = await MemoryArchivist.create({ account: 'random' })
198
+ })
199
+ describe('with no mods registered', () => {
200
+ it('returns empty array', () => {
201
+ const mods = node.registeredModules()
202
+ expect(mods).toBeArrayOfSize(0)
203
+ })
204
+ })
205
+ describe('with modules registered', () => {
206
+ it('returns registered modules', async () => {
207
+ await node.register(mod)
208
+ const mods = node.registeredModules()
209
+ expect(mods).toBeArrayOfSize(1)
210
+ expect(mods).toContain(mod)
211
+ })
212
+ })
213
+ })
214
+ describe('unregister', () => {
215
+ it('un-registers module', async () => {
216
+ const mod = await MemoryArchivist.create({ account: 'random' })
217
+ await node.register(mod)
218
+ expect(node.registeredModules()).toContain(mod)
219
+ await node.unregister(mod)
220
+ expect(node.registeredModules()).not.toContain(mod)
221
+ })
222
+ })
223
+ describe('description', () => {
224
+ const validateModuleDescription = (description?: ModuleDescription) => {
225
+ expect(description).toBeObject()
226
+ expect(description?.address).toBeString()
227
+ expect(description?.queries).toBeArray()
228
+ description?.queries.map((query) => {
229
+ expect(query).toBeString()
230
+ })
231
+ }
232
+ describe('node without child modules', () => {
233
+ it('describes node alone', async () => {
234
+ const description = (await node.state()).find(isPayloadOfSchemaType<ModuleDescriptionPayload>(ModuleDescriptionSchema))
235
+ validateModuleDescription(description)
236
+ expect(description?.children).toBeArrayOfSize(0)
237
+ })
238
+ it('serializes to JSON consistently', async () => {
239
+ const description = (await node.state()).find(isPayloadOfSchemaType<ModuleDescriptionPayload>(ModuleDescriptionSchema))
240
+ expect(prettyPrintDescription(description)).toMatchSnapshot()
241
+ })
242
+ })
243
+ describe('node with child modules', () => {
244
+ beforeEach(async () => {
245
+ const mods = await Promise.all([
246
+ MemoryArchivist.create({ account: testAccount2, config: { ...archivistConfig, name: 'testAccount2' } }),
247
+ MemoryArchivist.create({ account: testAccount3, config: { ...archivistConfig, name: 'testAccount3' } }),
248
+ ])
249
+ await Promise.all(
250
+ mods.map(async (mod) => {
251
+ await node.register(mod)
252
+ expect(await node.attach(mod.address, true)).toEqual(mod.address)
253
+ expect(await node.detach(mod.address)).toEqual(mod.address)
254
+ if (mod.modName) {
255
+ expect(await node.attach(mod.modName, true)).toEqual(mod.address)
256
+ expect(await node.detach(mod.modName)).toEqual(mod.address)
257
+ }
258
+ expect(await node.attach(mod.address, true)).toEqual(mod.address)
259
+ }),
260
+ )
261
+ })
262
+ it('describes node and child mods', async () => {
263
+ const description = (await node.state()).find(isPayloadOfSchemaType<ModuleDescriptionPayload>(ModuleDescriptionSchema))
264
+ validateModuleDescription(description)
265
+ expect(description?.children).toBeArrayOfSize(2)
266
+ // description.children?.map(validateModuleDescription)
267
+ })
268
+ it('serializes to JSON consistently', async () => {
269
+ const description = (await node.state()).find(isPayloadOfSchemaType<ModuleDescriptionPayload>(ModuleDescriptionSchema))
270
+ expect(prettyPrintDescription(description)).toMatchSnapshot()
271
+ })
272
+ })
273
+ describe('node with nested nodes and mods', () => {
274
+ beforeEach(async () => {
275
+ const nestedNode = await MemoryNode.create({ account: testAccount2, config: nodeConfig })
276
+ const nestedModules: AttachableArchivistInstance[] = [await MemoryArchivist.create({ account: testAccount3, config: archivistConfig })]
277
+ await Promise.all(
278
+ nestedModules.map(async (mod) => {
279
+ await nestedNode.register?.(mod)
280
+ await nestedNode.attach(mod.address, true)
281
+ }),
282
+ )
283
+ const rootModules: AttachableModuleInstance[] = [await MemoryArchivist.create({ account: testAccount4, config: archivistConfig })]
284
+ rootModules.push(nestedNode)
285
+ await Promise.all(
286
+ rootModules.map(async (mod) => {
287
+ await node.register(mod)
288
+ await node.attach(mod.address, true)
289
+ }),
290
+ )
291
+ })
292
+ it('describes node and all nested nodes and child modules', async () => {
293
+ const memoryNode = await MemoryNode.create({ account: 'random' })
294
+ const archivist1 = await MemoryArchivist.create({ account: 'random' })
295
+ const archivist2 = await MemoryArchivist.create({ account: 'random' })
296
+ await memoryNode.register(archivist1)
297
+ await memoryNode.attach(archivist1.address, true)
298
+ await memoryNode.register(archivist2)
299
+ await memoryNode.attach(archivist2.address, true)
300
+ console.log('status:', memoryNode.status)
301
+ const description = (await memoryNode.state()).find(isPayloadOfSchemaType<ModuleDescriptionPayload>(ModuleDescriptionSchema))
302
+ validateModuleDescription(description)
303
+ expect(description?.children).toBeArrayOfSize(2)
304
+ // description.children?.map(validateModuleDescription)
305
+ })
306
+ it('serializes to JSON consistently', async () => {
307
+ const description = (await node.state()).find(isPayloadOfSchemaType<ModuleDescriptionPayload>(ModuleDescriptionSchema))
308
+ expect(prettyPrintDescription(description)).toMatchSnapshot()
309
+ })
310
+ it('clone-all', async () => {
311
+ const newNode = await MemoryNodeHelper.attachToNewNode(node, '*')
312
+ const newNodeChildren = await newNode.publicChildren()
313
+ const nodeChildren = await node.publicChildren()
314
+ expect(newNodeChildren.length).toEqual(nodeChildren.length)
315
+ expect(newNodeChildren.includes(nodeChildren[0])).toBe(true)
316
+ })
317
+ it('clone-one (public)', async () => {
318
+ const mod = await MemoryArchivist.create({ account: 'random', config: { ...archivistConfig, name: 'CloneModule' } })
319
+ await node.register(mod)
320
+ await node.attach(mod.address, true)
321
+
322
+ const newNode = await MemoryNodeHelper.attachToNewNode(node, 'CloneModule')
323
+ const newNodeChild = await newNode.resolve('CloneModule')
324
+ const nodeChild = await node.resolve('CloneModule', { maxDepth: 1 })
325
+ expect(newNodeChild?.id).toEqual(nodeChild?.id)
326
+ expect(newNodeChild?.address).toEqual(nodeChild?.address)
327
+ expect(newNodeChild).toEqual(nodeChild)
328
+ })
329
+ it('clone-one (private)', async () => {
330
+ const mod = await MemoryArchivist.create({ account: 'random', config: { ...archivistConfig, name: 'CloneModulePrivate' } })
331
+ await node.register(mod)
332
+ await node.attach(mod.address)
333
+
334
+ try {
335
+ // this should except
336
+ await MemoryNodeHelper.attachToNewNode(node, 'CloneModulePrivate')
337
+ expect(false).toBeTrue()
338
+ } catch (e) {
339
+ expect(e).toBeInstanceOf(Error)
340
+ }
341
+ })
342
+ })
343
+ })
344
+ describe('discover', () => {
345
+ const archivistConfig = { schema: MemoryArchivist.defaultConfigSchema }
346
+ const validateStateResponse = (mod: Module, response: Payload[]) => {
347
+ expect(response).toBeArray()
348
+ const address = response.find(p => p.schema === AddressSchema) as AddressPayload
349
+ expect(address).toBeObject()
350
+ expect(address.address).toEqual(mod.address)
351
+ const config = response.find(p => p.schema === mod.config.schema)
352
+ expect(config).toBeObject()
353
+ expect(config?.schema).toBe(mod.config.schema)
354
+ const queries = response.filter(p => mod.queries.includes(p.schema))
355
+ expect(queries.length).toBeGreaterThanOrEqual(0)
356
+ for (const query of queries) {
357
+ expect(query).toBeObject()
358
+ }
359
+ }
360
+ describe('node without child modules', () => {
361
+ it('describes node alone', async () => {
362
+ const state = await node.state()
363
+ validateStateResponse(node, state)
364
+ })
365
+ })
366
+ describe('node with child modules', () => {
367
+ it('describes node and child modules', async () => {
368
+ const memoryNode = await MemoryNode.create({ account: 'random' })
369
+ const modules = await Promise.all([
370
+ MemoryArchivist.create({ account: testAccount2, config: archivistConfig }),
371
+ MemoryArchivist.create({ account: testAccount3, config: archivistConfig }),
372
+ ])
373
+ await Promise.all(
374
+ modules.map(async (mod) => {
375
+ await memoryNode.register(mod)
376
+ await memoryNode.attach(mod.address, true)
377
+ }),
378
+ )
379
+ const state = await memoryNode.state()
380
+
381
+ const address0 = state.find(p => p.schema === AddressSchema && (p as AddressPayload).address === modules[0].address) as AddressPayload
382
+ expect(address0).toBeObject()
383
+ const address1 = state.find(p => p.schema === AddressSchema && (p as AddressPayload).address === modules[1].address) as AddressPayload
384
+ expect(address1).toBeObject()
385
+ })
386
+ })
387
+ describe('node with nested nodes and modules', () => {
388
+ beforeEach(async () => {
389
+ node = await MemoryNode.create({ account: 'random' })
390
+ const attachEvents: Module[] = []
391
+ node.on('moduleAttached', (args) => {
392
+ const { mod } = args as ModuleAttachedEventArgs
393
+ attachEvents.push(mod)
394
+ })
395
+ const nestedNode = await MemoryNode.create({ account: testAccount2, config: nodeConfig })
396
+ const nestedModules = [await MemoryArchivist.create({ account: testAccount3, config: archivistConfig })]
397
+ await Promise.all(
398
+ nestedModules.map(async (mod) => {
399
+ await nestedNode.register(mod)
400
+ await nestedNode.attach(mod.address, true)
401
+ }),
402
+ )
403
+ const rootModules: AttachableModuleInstance[] = [await MemoryArchivist.create({ account: testAccount4, config: archivistConfig })]
404
+ rootModules.push(nestedNode)
405
+ await Promise.all(
406
+ rootModules.map(async (mod) => {
407
+ await node.register(mod)
408
+ await node.attach(mod.address, true)
409
+ }),
410
+ )
411
+ expect(attachEvents.includes(nestedNode)).toBeTrue()
412
+ expect(attachEvents.includes(node)).toBeFalse()
413
+ expect(nestedModules.length).toBe(1)
414
+ for (const nestedModule of nestedModules) {
415
+ expect(attachEvents.includes(nestedModule)).toBeTrue()
416
+ }
417
+ expect(rootModules.length).toBe(2)
418
+ for (const rootModule of rootModules) {
419
+ expect(attachEvents.includes(rootModule)).toBeTrue()
420
+ }
421
+ const eventAddresses = attachEvents.map(mod => mod.address)
422
+ expect(eventAddresses.length).toBe(3)
423
+ })
424
+ it('describes node and all nested nodes and child modules', async () => {
425
+ const state = await node.state()
426
+ validateStateResponse(node, state)
427
+ })
428
+ })
429
+ })
430
+ })
431
+
432
+ const prettyPrintDescription = (description?: ModuleDescription) => {
433
+ return JSON.stringify(description, null, 2)
434
+ }
@@ -0,0 +1,115 @@
1
+ /* eslint-disable max-statements */
2
+ import '@xylabs/vitest-extended'
3
+
4
+ import { MemoryArchivist, MemoryArchivistConfigSchema } from '@xyo-network/archivist-memory'
5
+ import { asArchivistInstance } from '@xyo-network/archivist-model'
6
+ import { ArchivistPayloadDiviner, ArchivistPayloadDivinerConfigSchema } from '@xyo-network/diviner-archivist'
7
+ import type { HuriPayload } from '@xyo-network/diviner-huri'
8
+ import { HuriSchema } from '@xyo-network/diviner-huri'
9
+ import { asDivinerInstance } from '@xyo-network/diviner-model'
10
+ import { MemoryNode } from '@xyo-network/node-memory'
11
+ import type { Payload } from '@xyo-network/payload'
12
+ import { PayloadBuilder, PayloadSchema } from '@xyo-network/payload'
13
+ import {
14
+ describe, expect,
15
+ it,
16
+ } from 'vitest'
17
+
18
+ /**
19
+ * @group node
20
+ * @group module
21
+ */
22
+
23
+ describe('MemoryNode', () => {
24
+ it('WithArchivistAndDiviner', async () => {
25
+ const node = await MemoryNode.create({ account: 'random' })
26
+ const archivist = await MemoryArchivist.create({
27
+ account: 'random',
28
+ config: { name: 'Archivist', schema: MemoryArchivistConfigSchema },
29
+ })
30
+
31
+ await node.register(archivist)
32
+ await node.attach(archivist.address, true)
33
+
34
+ const privateArchivist = await MemoryArchivist.create({
35
+ account: 'random',
36
+ config: { name: 'PrivateArchivist', schema: MemoryArchivistConfigSchema },
37
+ })
38
+
39
+ await node.register(privateArchivist)
40
+ await node.attach(privateArchivist.address, false)
41
+
42
+ const diviner = await ArchivistPayloadDiviner.create({
43
+ account: 'random',
44
+ config: { archivist: archivist.address, schema: ArchivistPayloadDivinerConfigSchema },
45
+ })
46
+
47
+ await node.register(diviner)
48
+ await node.attach(diviner.address, true)
49
+
50
+ expect(await node.registered()).toBeArrayOfSize(3)
51
+ expect(await node.attached()).toBeArrayOfSize(3)
52
+
53
+ const foundArchivist = asArchivistInstance(await node.resolve(archivist.address))
54
+ expect(foundArchivist).toBeDefined()
55
+ const foundNamedArchivist = asArchivistInstance(await node.resolve('Archivist'))
56
+ expect(foundNamedArchivist).toBeDefined()
57
+ expect(foundArchivist?.address).toBe(archivist.address)
58
+ const testPayload = new PayloadBuilder<Payload<{ schema: PayloadSchema; test: boolean }>>({ schema: PayloadSchema })
59
+ .fields({ test: true })
60
+ .build()
61
+
62
+ await foundArchivist?.insert([testPayload])
63
+
64
+ const payloads = await foundArchivist?.all?.()
65
+ expect(payloads?.length).toBe(1)
66
+
67
+ if (payloads && payloads[0]) {
68
+ const huri = await PayloadBuilder.dataHash(payloads[0])
69
+ const huriPayload: HuriPayload = { huri: [huri], schema: HuriSchema }
70
+ const mod = await node.resolve(diviner.address)
71
+ const foundDiviner = asDivinerInstance(mod)
72
+ expect(foundDiviner).toBeDefined()
73
+ if (foundDiviner) {
74
+ const payloads = await foundDiviner.divine([huriPayload])
75
+ expect(payloads?.length).toBe(1)
76
+ expect(payloads[0]).toBeDefined()
77
+ if (payloads?.length === 1 && payloads[0]) {
78
+ expect(await PayloadBuilder.dataHash(payloads[0])).toBe(huri)
79
+ }
80
+ }
81
+ }
82
+
83
+ expect((await node.resolve('*', { direction: 'up' })).length).toBe(1)
84
+ expect((await node.resolve('*', { direction: 'up', maxDepth: 0 })).length).toBe(1)
85
+ expect((await node.resolve('*', { direction: 'up', maxDepth: 1 })).length).toBe(1)
86
+
87
+ const nodeDown = await node.resolve('*', { direction: 'down' })
88
+ expect(nodeDown.length).toBe(3)
89
+ expect(nodeDown.find(mod => mod.address === node.address)).toBeDefined()
90
+ expect(nodeDown.find(mod => mod.address === archivist.address)).toBeDefined()
91
+ expect(nodeDown.find(mod => mod.address === diviner.address)).toBeDefined()
92
+
93
+ expect((await node.resolvePrivate('*', { direction: 'down' })).length).toBe(1)
94
+ expect((await node.resolve('*', { direction: 'down', maxDepth: 0 })).length).toBe(1)
95
+ expect((await node.resolve('*', { direction: 'down', maxDepth: 1 })).length).toBe(3)
96
+
97
+ // this should be 3 here and not 4 since node is at the top and asking it to resolve its privates
98
+ // from outside should not include the private cousin
99
+ expect((await node.resolve('*', { direction: 'all' })).length).toBe(3)
100
+
101
+ const archivistUp = await archivist.resolve('*', { direction: 'up' })
102
+
103
+ expect(archivistUp.find(mod => mod.address === node.address)).toBeDefined()
104
+ expect(archivistUp.find(mod => mod.address === archivist.address)).toBeDefined()
105
+ expect(archivistUp.find(mod => mod.address === diviner.address)).toBeDefined()
106
+ // this is 4 here since it will include the one private cousin
107
+ expect(archivistUp.length).toBe(4)
108
+
109
+ expect((await archivist.resolve('*', { direction: 'up', maxDepth: 1 })).length).toBe(2)
110
+ expect((await archivist.resolve('*', { direction: 'down' })).length).toBe(1)
111
+
112
+ // this is 4 here since it will include the one private cousin
113
+ expect((await archivist.resolve('*', { direction: 'all' })).length).toBe(4)
114
+ })
115
+ })
@@ -0,0 +1,68 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`MemoryNode > description > node with child modules > serializes to JSON consistently 1`] = `
4
+ "{
5
+ "address": "a1df01bbdaa05f2294cdd6fbd92aace3086fe408",
6
+ "queries": [
7
+ "network.xyo.query.node.attach",
8
+ "network.xyo.query.node.certify",
9
+ "network.xyo.query.node.detach",
10
+ "network.xyo.query.node.attached",
11
+ "network.xyo.query.node.registered",
12
+ "network.xyo.query.module.address",
13
+ "network.xyo.query.module.subscribe",
14
+ "network.xyo.query.module.manifest",
15
+ "network.xyo.query.module.state"
16
+ ],
17
+ "schema": "network.xyo.module.description",
18
+ "name": "Unnamed MemoryNode",
19
+ "children": [
20
+ "e8cae43b8ef15e6e2166b7560b2df802ac28fe53",
21
+ "ee84477649f67bcda3c604ad361313bdd9976c0f"
22
+ ]
23
+ }"
24
+ `;
25
+
26
+ exports[`MemoryNode > description > node with nested nodes and mods > serializes to JSON consistently 1`] = `
27
+ "{
28
+ "address": "a1df01bbdaa05f2294cdd6fbd92aace3086fe408",
29
+ "queries": [
30
+ "network.xyo.query.node.attach",
31
+ "network.xyo.query.node.certify",
32
+ "network.xyo.query.node.detach",
33
+ "network.xyo.query.node.attached",
34
+ "network.xyo.query.node.registered",
35
+ "network.xyo.query.module.address",
36
+ "network.xyo.query.module.subscribe",
37
+ "network.xyo.query.module.manifest",
38
+ "network.xyo.query.module.state"
39
+ ],
40
+ "schema": "network.xyo.module.description",
41
+ "name": "Unnamed MemoryNode",
42
+ "children": [
43
+ "4c3e913823664e68ce252e256b8c442172dbec4a",
44
+ "e8cae43b8ef15e6e2166b7560b2df802ac28fe53",
45
+ "ee84477649f67bcda3c604ad361313bdd9976c0f"
46
+ ]
47
+ }"
48
+ `;
49
+
50
+ exports[`MemoryNode > description > node without child modules > serializes to JSON consistently 1`] = `
51
+ "{
52
+ "address": "a1df01bbdaa05f2294cdd6fbd92aace3086fe408",
53
+ "queries": [
54
+ "network.xyo.query.node.attach",
55
+ "network.xyo.query.node.certify",
56
+ "network.xyo.query.node.detach",
57
+ "network.xyo.query.node.attached",
58
+ "network.xyo.query.node.registered",
59
+ "network.xyo.query.module.address",
60
+ "network.xyo.query.module.subscribe",
61
+ "network.xyo.query.module.manifest",
62
+ "network.xyo.query.module.state"
63
+ ],
64
+ "schema": "network.xyo.module.description",
65
+ "name": "Unnamed MemoryNode",
66
+ "children": []
67
+ }"
68
+ `;
@@ -0,0 +1,42 @@
1
+ /* eslint-disable unicorn/no-useless-undefined */
2
+ import '@xylabs/vitest-extended'
3
+
4
+ import { Account } from '@xyo-network/account'
5
+ import { asModuleInstance } from '@xyo-network/module-model'
6
+ import { MemoryNode } from '@xyo-network/node-memory'
7
+ import {
8
+ describe, expect, test,
9
+ } from 'vitest'
10
+
11
+ // eslint-disable-next-line no-restricted-imports
12
+ import {
13
+ asNodeInstance, NodeConfigSchema, NodeWrapper,
14
+ } from '../index.ts'
15
+
16
+ /**
17
+ * @group node
18
+ * @group module
19
+ */
20
+
21
+ describe('identity check (as)', () => {
22
+ test('asModuleInstance', async () => {
23
+ const node = await MemoryNode.create({ account: 'random', config: { schema: NodeConfigSchema } })
24
+ expect(asModuleInstance(node)).toBeDefined()
25
+ expect(asModuleInstance(null)).toBeUndefined()
26
+ expect(asModuleInstance(undefined)).toBeUndefined()
27
+ expect(asModuleInstance({})).toBeUndefined()
28
+
29
+ const wrapper = NodeWrapper.wrap(node, await Account.random())
30
+ expect(asModuleInstance(wrapper)).toBeDefined()
31
+ })
32
+ test('isNodeInstance', async () => {
33
+ const node = await MemoryNode.create({ account: 'random', config: { schema: NodeConfigSchema } })
34
+ expect(asNodeInstance(node)).toBeDefined()
35
+ expect(asNodeInstance(null)).toBeUndefined()
36
+ expect(asNodeInstance(undefined)).toBeUndefined()
37
+ expect(asNodeInstance({})).toBeUndefined()
38
+
39
+ const wrapper = NodeWrapper.wrap(node, await Account.random())
40
+ expect(asNodeInstance(wrapper)).toBeDefined()
41
+ })
42
+ })
@@ -0,0 +1,42 @@
1
+ /* eslint-disable unicorn/no-useless-undefined */
2
+ import '@xylabs/vitest-extended'
3
+
4
+ import { Account } from '@xyo-network/account'
5
+ import { isModuleInstance } from '@xyo-network/module-model'
6
+ import { MemoryNode } from '@xyo-network/node-memory'
7
+ import {
8
+ describe, expect, test,
9
+ } from 'vitest'
10
+
11
+ // eslint-disable-next-line no-restricted-imports
12
+ import {
13
+ isNodeInstance, NodeConfigSchema, NodeWrapper,
14
+ } from '../index.ts'
15
+
16
+ /**
17
+ * @group node
18
+ * @group module
19
+ */
20
+
21
+ describe('identity check (is)', () => {
22
+ test('isModuleInstance', async () => {
23
+ const node = await MemoryNode.create({ account: 'random', config: { schema: NodeConfigSchema } })
24
+ expect(isModuleInstance(node)).toBeTrue()
25
+ expect(isModuleInstance(null)).toBeFalse()
26
+ expect(isModuleInstance(undefined)).toBeFalse()
27
+ expect(isModuleInstance({})).toBeFalse()
28
+
29
+ const wrapper = NodeWrapper.wrap(node, await Account.random())
30
+ expect(isModuleInstance(wrapper)).toBeTrue()
31
+ })
32
+ test('isNodeInstance', async () => {
33
+ const node = await MemoryNode.create({ account: 'random', config: { schema: NodeConfigSchema } })
34
+ expect(isNodeInstance(node)).toBeTrue()
35
+ expect(isNodeInstance(null)).toBeFalse()
36
+ expect(isNodeInstance(undefined)).toBeFalse()
37
+ expect(isNodeInstance({})).toBeFalse()
38
+
39
+ const wrapper = NodeWrapper.wrap(node, await Account.random())
40
+ expect(isNodeInstance(wrapper)).toBeTrue()
41
+ })
42
+ })
package/xy.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { XyTsupConfig } from '@xylabs/ts-scripts-yarn3'
2
- const config: XyTsupConfig = {
3
- compile: {
4
- browser: {},
5
- neutral: { src: true },
6
- node: {},
7
- },
8
- }
9
-
10
- export default config
package/xyo-config.js DELETED
@@ -1 +0,0 @@
1
- module.exports = { version: 1 }