@xyo-network/bridge-pub-sub 5.3.20 → 5.3.24

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.
@@ -1,287 +0,0 @@
1
- import {
2
- Address, assertEx,
3
- exists,
4
- forget,
5
- isAddress, toSafeJsonString,
6
- } from '@xylabs/sdk-js'
7
- import { AbstractBridge } from '@xyo-network/bridge-abstract'
8
- import {
9
- BridgeExposeOptions,
10
- BridgeModule,
11
- BridgeUnexposeOptions,
12
- QueryFulfillFinishedEventArgs,
13
- QueryFulfillStartedEventArgs,
14
- QuerySendFinishedEventArgs,
15
- QuerySendStartedEventArgs,
16
- } from '@xyo-network/bridge-model'
17
- import {
18
- AddressPayload,
19
- AddressSchema,
20
- creatableModule,
21
- ModuleFilterOptions,
22
- ModuleIdentifier,
23
- ModuleInstance,
24
- resolveAddressToInstance,
25
- resolveAddressToInstanceUp,
26
- ResolveHelper,
27
- } from '@xyo-network/module-model'
28
- import { asNodeInstance } from '@xyo-network/node-model'
29
- import { isPayloadOfSchemaType, Schema } from '@xyo-network/payload-model'
30
- import { Mutex } from 'async-mutex'
31
- import { LRUCache } from 'lru-cache'
32
-
33
- import { AsyncQueryBusClient, AsyncQueryBusHost } from './AsyncQueryBus/index.ts'
34
- import { PubSubBridgeConfigSchema } from './Config.ts'
35
- import { PubSubBridgeParams } from './Params.ts'
36
- import { PubSubBridgeModuleResolver } from './PubSubBridgeModuleResolver.ts'
37
-
38
- const moduleName = 'PubSubBridge'
39
-
40
- @creatableModule()
41
- export class PubSubBridge<TParams extends PubSubBridgeParams = PubSubBridgeParams> extends AbstractBridge<TParams> implements BridgeModule<TParams> {
42
- static override readonly configSchemas: Schema[] = [...super.configSchemas, PubSubBridgeConfigSchema]
43
- static override readonly defaultConfigSchema: Schema = PubSubBridgeConfigSchema
44
-
45
- protected _configRootAddress: Address = '' as Address
46
- protected _configStateStoreArchivist: string = ''
47
- protected _configStateStoreBoundWitnessDiviner: string = ''
48
- protected _exposedAddresses: Address[] = []
49
- protected _lastState?: LRUCache<string, number>
50
-
51
- private _busClient?: AsyncQueryBusClient
52
- private _busHost?: AsyncQueryBusHost
53
- private _discoverRootsMutex = new Mutex()
54
- private _resolver?: PubSubBridgeModuleResolver
55
-
56
- override get resolver(): PubSubBridgeModuleResolver {
57
- this._resolver
58
- = this._resolver
59
- ?? new PubSubBridgeModuleResolver({
60
- additionalSigners: this.additionalSigners,
61
- archiving: { ...this.archiving, resolveArchivists: this.resolveArchivingArchivists.bind(this) },
62
- bridge: this,
63
- busClient: assertEx(this.busClient(), () => 'busClient not configured'),
64
- onQuerySendFinished: (args: Omit<QuerySendFinishedEventArgs, 'mod'>) => {
65
- forget(this.emit('querySendFinished', { mod: this, ...args }))
66
- },
67
- onQuerySendStarted: (args: Omit<QuerySendStartedEventArgs, 'mod'>) => {
68
- forget(this.emit('querySendStarted', { mod: this, ...args }))
69
- },
70
- root: this,
71
- wrapperAccount: this.account,
72
- })
73
- return this._resolver
74
- }
75
-
76
- protected get moduleName() {
77
- return this.modName ?? moduleName
78
- }
79
-
80
- async connect(id: ModuleIdentifier, maxDepth = 5): Promise<Address | undefined> {
81
- const transformedId = assertEx(await ResolveHelper.transformModuleIdentifier(id), () => `Unable to transform module identifier: ${id}`)
82
- // check if already connected
83
- const existingInstance = await this.resolve<ModuleInstance>(transformedId)
84
- if (existingInstance) {
85
- return existingInstance.address
86
- }
87
-
88
- // use the resolver to create the proxy instance
89
- const [instance] = await this.resolver.resolveHandler<ModuleInstance>(id)
90
- return await this.connectInstance(instance, maxDepth)
91
- }
92
-
93
- async disconnect(id: ModuleIdentifier): Promise<Address | undefined> {
94
- const transformedId = assertEx(await ResolveHelper.transformModuleIdentifier(id), () => `Unable to transform module identifier: ${id}`)
95
- const instance = await this.resolve<ModuleInstance>(transformedId)
96
- if (instance) {
97
- this.downResolver.remove(instance.address)
98
- return instance.address
99
- }
100
- }
101
-
102
- async exposeChild(mod: ModuleInstance, options?: BridgeExposeOptions | undefined): Promise<ModuleInstance[]> {
103
- const { maxDepth = 5 } = options ?? {}
104
- console.log(`exposeChild: ${mod.address} ${mod?.id} ${maxDepth}`)
105
- const host = assertEx(this.busHost(), () => 'Not configured as a host')
106
- host.expose(mod)
107
- const children = maxDepth > 0 ? ((await mod.publicChildren?.()) ?? []) : []
108
- this.logger?.log(`childrenToExpose [${mod.id}][${mod.address}]: ${toSafeJsonString(children.map(child => child.id))}`)
109
- const exposedChildren = (await Promise.all(children.map(child => this.exposeChild(child, { maxDepth: maxDepth - 1, required: false }))))
110
- .flat()
111
- .filter(exists)
112
- const allExposed = [mod, ...exposedChildren]
113
-
114
- for (const exposedMod of allExposed) this.logger?.log(`exposed: ${exposedMod.address} [${mod.id}]`)
115
-
116
- return allExposed
117
- }
118
-
119
- async exposeHandler(address: Address, options?: BridgeExposeOptions | undefined): Promise<ModuleInstance[]> {
120
- const { required = true } = options ?? {}
121
- const mod = await resolveAddressToInstanceUp(this, address)
122
- console.log(`exposeHandler: ${address} ${mod?.id}`)
123
- if (required && !mod) {
124
- throw new Error(`Unable to find required module: ${address}`)
125
- }
126
- if (mod) {
127
- return this.exposeChild(mod, options)
128
- }
129
- return []
130
- }
131
-
132
- exposedHandler(): Address[] {
133
- const exposedSet = this.busHost()?.exposedAddresses
134
- return exposedSet ? [...exposedSet] : []
135
- }
136
-
137
- async getRoots(force?: boolean): Promise<ModuleInstance[]> {
138
- return await this._discoverRootsMutex.runExclusive(async () => {
139
- if (this._roots === undefined || force) {
140
- const rootAddresses = (
141
- await Promise.all(
142
- (this.config.roots ?? []).map(async (id) => {
143
- try {
144
- return await ResolveHelper.transformModuleIdentifier(id)
145
- } catch (ex) {
146
- this.logger?.warn('Unable to transform module identifier:', id, ex)
147
- return
148
- }
149
- }),
150
- )
151
- ).filter(exists)
152
- const rootInstances = (
153
- await Promise.all(
154
- rootAddresses.map(async (root) => {
155
- try {
156
- return await this.resolver.resolveHandler<ModuleInstance>(root)
157
- } catch (ex) {
158
- this.logger?.warn('Unable to resolve root:', root, ex)
159
- return
160
- }
161
- }),
162
- )
163
- )
164
- .flat()
165
- .filter(exists)
166
- for (const instance of rootInstances) {
167
- this.downResolver.add(instance)
168
- }
169
- this._roots = rootInstances
170
- }
171
- return this._roots
172
- })
173
- }
174
-
175
- /** @deprecated do not pass undefined. If trying to get all, pass '*' */
176
- override async resolve(): Promise<ModuleInstance[]>
177
- override async resolve<T extends ModuleInstance = ModuleInstance>(all: '*', options?: ModuleFilterOptions<T>): Promise<T[]>
178
- override async resolve<T extends ModuleInstance = ModuleInstance>(id: ModuleIdentifier, options?: ModuleFilterOptions<T>): Promise<T | undefined>
179
-
180
- override async resolve<T extends ModuleInstance = ModuleInstance>(
181
- id: ModuleIdentifier = '*',
182
- options: ModuleFilterOptions<T> = {},
183
- ): Promise<T | T[] | undefined> {
184
- const roots = (this._roots ?? []) as T[]
185
- const workingSet = (options.direction === 'up' ? [this as ModuleInstance] : [...roots, this]) as T[]
186
- if (id === '*') {
187
- const remainingDepth = (options.maxDepth ?? 1) - 1
188
- return remainingDepth <= 0
189
- ? workingSet
190
- : (
191
- [...workingSet, ...(await Promise.all(roots.map(mod => mod.resolve('*', { ...options, maxDepth: remainingDepth })))).flat()]
192
- )
193
- }
194
- switch (typeof id) {
195
- case 'string': {
196
- const parts = id.split(':')
197
- const first = assertEx(parts.shift(), () => 'Missing first part')
198
- const firstInstance: ModuleInstance | undefined
199
- = isAddress(first)
200
- ? ((await resolveAddressToInstance(this, first, undefined, [], options.direction)) as T)
201
- : this._roots?.find(mod => mod.id === first)
202
- return (parts.length === 0 ? firstInstance : firstInstance?.resolve(parts.join(':'), options)) as T | undefined
203
- }
204
- default: {
205
- return
206
- }
207
- }
208
- }
209
-
210
- override async startHandler() {
211
- this.busHost()?.start()
212
- await super.startHandler()
213
- }
214
-
215
- async unexposeHandler(id: ModuleIdentifier, options?: BridgeUnexposeOptions | undefined): Promise<ModuleInstance[]> {
216
- const { maxDepth = 2, required = true } = options ?? {}
217
- const host = assertEx(this.busHost(), () => 'Not configured as a host')
218
- const mod = await host.unexpose(id, required)
219
- if (mod) {
220
- const children = maxDepth > 0 ? ((await mod.publicChildren?.()) ?? []) : []
221
- const exposedChildren = (
222
- await Promise.all(children.map(child => this.unexposeHandler(child.address, { maxDepth: maxDepth - 1, required: false })))
223
- )
224
- .flat()
225
- .filter(exists)
226
- return [mod, ...exposedChildren]
227
- }
228
- return []
229
- }
230
-
231
- protected busClient() {
232
- if (!this._busClient && this.config.client) {
233
- this._busClient = new AsyncQueryBusClient({
234
- config: this.config.client,
235
- logger: this.logger,
236
- rootModule: this,
237
- })
238
- }
239
- return this._busClient
240
- }
241
-
242
- protected busHost() {
243
- if (!this._busHost && this.config.host) {
244
- this._busHost = new AsyncQueryBusHost({
245
- config: this.config.host,
246
- logger: this.logger,
247
- onQueryFulfillFinished: (args: Omit<QueryFulfillFinishedEventArgs, 'mod'>) => {
248
- if (this.archiving && this.isAllowedArchivingQuery(args.query.schema)) {
249
- forget(this.storeToArchivists(args.result?.flat() ?? []))
250
- }
251
- forget(this.emit('queryFulfillFinished', { mod: this, ...args }))
252
- },
253
- onQueryFulfillStarted: (args: Omit<QueryFulfillStartedEventArgs, 'mod'>) => {
254
- if (this.archiving && this.isAllowedArchivingQuery(args.query.schema)) {
255
- forget(this.storeToArchivists([args.query, ...(args.payloads ?? [])]))
256
- }
257
- forget(this.emit('queryFulfillStarted', { mod: this, ...args }))
258
- },
259
- rootModule: this,
260
- })
261
- }
262
- return this._busHost
263
- }
264
-
265
- protected async connectInstance(instance?: ModuleInstance, maxDepth = 5): Promise<Address | undefined> {
266
- if (instance) {
267
- this.downResolver.add(instance)
268
- if (maxDepth > 0) {
269
- const node = asNodeInstance(instance)
270
- if (node) {
271
- const state = await node.state()
272
- const children = (state?.filter(isPayloadOfSchemaType<AddressPayload>(AddressSchema)).map(s => s.address) ?? []).filter(
273
- a => a !== instance.address,
274
- )
275
- await Promise.all(children.map(child => this.connect(child, maxDepth - 1)))
276
- }
277
- }
278
- this.logger?.log(`Connect: ${instance.id}`)
279
- return instance.address
280
- }
281
- }
282
-
283
- protected override async stopHandler() {
284
- await super.stopHandler()
285
- this.busHost()?.stop()
286
- }
287
- }
@@ -1,78 +0,0 @@
1
- import type { Address, CreatableName } from '@xylabs/sdk-js'
2
- import { assertEx, isAddress } from '@xylabs/sdk-js'
3
- import { Account } from '@xyo-network/account'
4
- import type { BridgeModuleResolverParams } from '@xyo-network/bridge-abstract'
5
- import { AbstractBridgeModuleResolver, wrapModuleWithType } from '@xyo-network/bridge-abstract'
6
- import type { ConfigPayload } from '@xyo-network/config-payload-plugin'
7
- import { ConfigSchema } from '@xyo-network/config-payload-plugin'
8
- import type {
9
- ModuleConfig,
10
- ModuleFilterOptions,
11
- ModuleIdentifier,
12
- ModuleInstance,
13
- } from '@xyo-network/module-model'
14
- import {
15
- asModuleInstance,
16
- ModuleConfigSchema,
17
- ResolveHelper,
18
- } from '@xyo-network/module-model'
19
- import { Mutex } from 'async-mutex'
20
- import { LRUCache } from 'lru-cache'
21
-
22
- import type { AsyncQueryBusClient, AsyncQueryBusModuleProxyParams } from './AsyncQueryBus/index.ts'
23
- import { AsyncQueryBusModuleProxy } from './AsyncQueryBus/index.ts'
24
-
25
- export interface PubSubBridgeModuleResolverParams extends BridgeModuleResolverParams {
26
- busClient: AsyncQueryBusClient
27
- }
28
-
29
- export class PubSubBridgeModuleResolver extends AbstractBridgeModuleResolver<PubSubBridgeModuleResolverParams> {
30
- protected _resolvedCache = new LRUCache<Address, ModuleInstance>({ max: 1000 })
31
- protected _resolvedCacheMutex = new Mutex()
32
-
33
- override async resolveHandler<T extends ModuleInstance = ModuleInstance>(id: ModuleIdentifier, options?: ModuleFilterOptions<T>): Promise<T[]> {
34
- const parentResult = await super.resolveHandler(id, options)
35
- if (parentResult.length > 0) {
36
- return parentResult
37
- }
38
- const idParts = id.split(':')
39
- const untransformedFirstPart = assertEx(idParts.shift(), () => 'Missing module identifier')
40
- const firstPart = await ResolveHelper.transformModuleIdentifier(untransformedFirstPart)
41
- assertEx(isAddress(firstPart), () => `Invalid module address: ${firstPart}`)
42
- const remainderParts = idParts.join(':')
43
- const instance: T = await this._resolvedCacheMutex.runExclusive(async () => {
44
- const cachedMod = this._resolvedCache.get(firstPart as Address)
45
- if (cachedMod) {
46
- const result = idParts.length <= 0 ? cachedMod : cachedMod.resolve(remainderParts, { ...options, maxDepth: (options?.maxDepth ?? 5) - 1 })
47
- return result as T
48
- }
49
- const account = await Account.random()
50
- const finalParams: AsyncQueryBusModuleProxyParams = {
51
- name: 'PubSubBridgeModuleResolver' as CreatableName,
52
- account,
53
- archiving: this.params.archiving,
54
- busClient: this.params.busClient,
55
- config: { schema: ModuleConfigSchema },
56
- host: this,
57
- moduleAddress: firstPart as Address,
58
- onQuerySendFinished: this.params.onQuerySendFinished,
59
- onQuerySendStarted: this.params.onQuerySendStarted,
60
- }
61
- const proxy = await AsyncQueryBusModuleProxy.create(finalParams)
62
- const state = await proxy.state()
63
- const configSchema = (state.find(payload => payload.schema === ConfigSchema) as ConfigPayload | undefined)?.config
64
- const config = assertEx(
65
- state.find(payload => payload.schema === configSchema),
66
- () => 'Unable to locate config',
67
- ) as ModuleConfig
68
- proxy.setConfig(config)
69
- await proxy.start?.()
70
- const wrapped = wrapModuleWithType(proxy, account) as unknown as T
71
- assertEx(asModuleInstance<T>(wrapped, {}), () => `Failed to asModuleInstance [${id}]`)
72
- this._resolvedCache.set(wrapped.address, wrapped)
73
- return wrapped as ModuleInstance as T
74
- })
75
- const result = remainderParts.length > 0 ? await instance.resolve(remainderParts, options) : instance
76
- return result ? [result] : []
77
- }
78
- }
package/src/Schema.ts DELETED
@@ -1,4 +0,0 @@
1
- import { asSchema } from '@xyo-network/payload-model'
2
-
3
- export const PubSubBridgeSchema = asSchema('network.xyo.bridge.pubsub', true)
4
- export type PubSubBridgeSchema = typeof PubSubBridgeSchema
package/src/index.ts DELETED
@@ -1,7 +0,0 @@
1
- export * from './AbstractModuleHost/index.ts'
2
- export * from './AsyncQueryBus/index.ts'
3
- export * from './Config.ts'
4
- export * from './Params.ts'
5
- export * from './PubSubBridge.ts'
6
- export * from './PubSubBridgeModuleResolver.ts'
7
- export * from './Schema.ts'