@xyo-network/bridge-pub-sub 5.3.22 → 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/bridge-pub-sub",
3
- "version": "5.3.22",
3
+ "version": "5.3.24",
4
4
  "description": "Primary SDK for using XYO Protocol 2.0",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -30,68 +30,64 @@
30
30
  "types": "dist/neutral/index.d.ts",
31
31
  "files": [
32
32
  "dist",
33
- "src",
34
33
  "!**/*.bench.*",
35
34
  "!**/*.spec.*",
36
35
  "!**/*.test.*",
37
36
  "README.md"
38
37
  ],
39
38
  "dependencies": {
40
- "@xyo-network/account": "~5.3.22",
41
- "@xyo-network/archivist-model": "~5.3.22",
42
- "@xyo-network/boundwitness-model": "~5.3.22",
43
- "@xyo-network/bridge-abstract": "~5.3.22",
44
- "@xyo-network/bridge-model": "~5.3.22",
45
- "@xyo-network/config-payload-plugin": "~5.3.22",
46
- "@xyo-network/diviner-boundwitness-model": "~5.3.22",
47
- "@xyo-network/diviner-model": "~5.3.22",
48
- "@xyo-network/module-abstract": "~5.3.22",
49
- "@xyo-network/module-model": "~5.3.22",
50
- "@xyo-network/node-model": "~5.3.22",
51
- "@xyo-network/payload-builder": "~5.3.22",
52
- "@xyo-network/payload-model": "~5.3.22",
53
39
  "async-mutex": "~0.5.0",
54
- "lru-cache": "~11.2.7"
40
+ "lru-cache": "~11.2.7",
41
+ "@xyo-network/account": "~5.3.24",
42
+ "@xyo-network/archivist-model": "~5.3.24",
43
+ "@xyo-network/boundwitness-model": "~5.3.24",
44
+ "@xyo-network/bridge-model": "~5.3.24",
45
+ "@xyo-network/bridge-abstract": "~5.3.24",
46
+ "@xyo-network/config-payload-plugin": "~5.3.24",
47
+ "@xyo-network/module-model": "~5.3.24",
48
+ "@xyo-network/diviner-boundwitness-model": "~5.3.24",
49
+ "@xyo-network/node-model": "~5.3.24",
50
+ "@xyo-network/payload-model": "~5.3.24",
51
+ "@xyo-network/module-abstract": "~5.3.24",
52
+ "@xyo-network/payload-builder": "~5.3.24",
53
+ "@xyo-network/diviner-model": "~5.3.24"
55
54
  },
56
55
  "devDependencies": {
57
56
  "@opentelemetry/api": "^1.9.1",
58
57
  "@types/node": "^25.5.0",
59
- "@xylabs/sdk-js": "^5.0.91",
60
- "@xylabs/ts-scripts-common": "~7.6.8",
61
- "@xylabs/ts-scripts-yarn3": "~7.6.8",
62
- "@xylabs/tsconfig": "~7.6.8",
63
- "@xylabs/vitest-extended": "~5.0.91",
64
- "@xyo-network/account": "~5.3.22",
65
- "@xyo-network/archivist-memory": "~5.3.22",
66
- "@xyo-network/archivist-model": "~5.3.22",
67
- "@xyo-network/boundwitness-model": "~5.3.22",
68
- "@xyo-network/bridge-abstract": "~5.3.22",
69
- "@xyo-network/bridge-model": "~5.3.22",
70
- "@xyo-network/config-payload-plugin": "~5.3.22",
71
- "@xyo-network/diviner-boundwitness-memory": "~5.3.22",
72
- "@xyo-network/diviner-boundwitness-model": "~5.3.22",
73
- "@xyo-network/diviner-model": "~5.3.22",
74
- "@xyo-network/module-abstract": "~5.3.22",
75
- "@xyo-network/module-model": "~5.3.22",
76
- "@xyo-network/node-memory": "~5.3.22",
77
- "@xyo-network/node-model": "~5.3.22",
78
- "@xyo-network/payload-builder": "~5.3.22",
79
- "@xyo-network/payload-model": "~5.3.22",
80
- "@xyo-network/payload-wrapper": "~5.3.22",
58
+ "@xylabs/sdk-js": "^5.0.93",
59
+ "@xylabs/ts-scripts-common": "~7.6.16",
60
+ "@xylabs/ts-scripts-pnpm": "~7.6.16",
61
+ "@xylabs/tsconfig": "~7.6.16",
62
+ "@xylabs/vitest-extended": "~5.0.93",
81
63
  "acorn": "^8.16.0",
82
64
  "async-mutex": "~0.5.0",
83
65
  "axios": "^1.14.0",
84
- "cosmiconfig": "^9.0.1",
85
- "esbuild": "^0.27.4",
86
- "eslint": "^10.1.0",
66
+ "esbuild": "^0.28.0",
87
67
  "ethers": "^6.16.0",
88
- "lru-cache": "^11.2.7",
89
- "rollup": "^4.60.1",
68
+ "lru-cache": "~11.2.7",
90
69
  "tslib": "^2.8.1",
91
70
  "typescript": "~5.9.3",
92
71
  "vite": "^8.0.3",
93
72
  "vitest": "~4.1.2",
94
- "zod": "^4.3.6"
73
+ "zod": "^4.3.6",
74
+ "@xyo-network/account": "~5.3.24",
75
+ "@xyo-network/archivist-memory": "~5.3.24",
76
+ "@xyo-network/archivist-model": "~5.3.24",
77
+ "@xyo-network/boundwitness-model": "~5.3.24",
78
+ "@xyo-network/bridge-model": "~5.3.24",
79
+ "@xyo-network/bridge-abstract": "~5.3.24",
80
+ "@xyo-network/config-payload-plugin": "~5.3.24",
81
+ "@xyo-network/diviner-boundwitness-memory": "~5.3.24",
82
+ "@xyo-network/diviner-model": "~5.3.24",
83
+ "@xyo-network/diviner-boundwitness-model": "~5.3.24",
84
+ "@xyo-network/node-model": "~5.3.24",
85
+ "@xyo-network/node-memory": "~5.3.24",
86
+ "@xyo-network/module-model": "~5.3.24",
87
+ "@xyo-network/module-abstract": "~5.3.24",
88
+ "@xyo-network/payload-builder": "~5.3.24",
89
+ "@xyo-network/payload-model": "~5.3.24",
90
+ "@xyo-network/payload-wrapper": "~5.3.24"
95
91
  },
96
92
  "peerDependencies": {
97
93
  "@xylabs/sdk-js": "^5",
@@ -101,4 +97,4 @@
101
97
  "publishConfig": {
102
98
  "access": "public"
103
99
  }
104
- }
100
+ }
@@ -1,12 +0,0 @@
1
- import type { BaseParams, Promisable } from '@xylabs/sdk-js'
2
- import { Base } from '@xylabs/sdk-js'
3
- import type { ModuleInstance } from '@xyo-network/module-model'
4
-
5
- export type ModuleHostParams<THostedInstance extends ModuleInstance = ModuleInstance> = BaseParams<{
6
- mod: THostedInstance
7
- }>
8
-
9
- export abstract class AbstractModuleHost<TParams extends ModuleHostParams = ModuleHostParams> extends Base<TParams> {
10
- abstract start(): Promisable<void>
11
- abstract stop(): Promisable<void>
12
- }
@@ -1 +0,0 @@
1
- export * from './AbstractModuleHost.ts'
@@ -1,163 +0,0 @@
1
- import type { Address, TypeCheck } from '@xylabs/sdk-js'
2
- import { assertEx, Base } from '@xylabs/sdk-js'
3
- import type { ArchivistInstance } from '@xyo-network/archivist-model'
4
- import { isArchivistInstance } from '@xyo-network/archivist-model'
5
- import type { BoundWitness, QueryBoundWitness } from '@xyo-network/boundwitness-model'
6
- import type { BoundWitnessDivinerParams, BoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model'
7
- import type { DivinerInstance } from '@xyo-network/diviner-model'
8
- import { isDivinerInstance } from '@xyo-network/diviner-model'
9
- import type {
10
- ModuleConfig, ModuleIdentifier, ModuleInstance,
11
- } from '@xyo-network/module-model'
12
- import { ResolveHelper } from '@xyo-network/module-model'
13
- import type { Sequence } from '@xyo-network/payload-model'
14
- import { SequenceConstants } from '@xyo-network/payload-model'
15
- import { Mutex } from 'async-mutex'
16
- import { LRUCache } from 'lru-cache'
17
-
18
- import type { AsyncQueryBusParams } from './model/index.ts'
19
-
20
- const POLLING_FREQUENCY_MIN = 100 as const
21
- const POLLING_FREQUENCY_MAX = 60_000 as const
22
- const POLLING_FREQUENCY_DEFAULT = 1000 as const
23
-
24
- export class AsyncQueryBusBase<TParams extends AsyncQueryBusParams = AsyncQueryBusParams> extends Base<TParams> {
25
- protected _lastState?: LRUCache<Address, Sequence>
26
- protected _targetConfigs: Record<Address, ModuleConfig> = {}
27
- protected _targetQueries: Record<Address, string[]> = {}
28
-
29
- private _lastResolveFailure: Record<ModuleIdentifier, number> = {}
30
- private _queriesArchivist?: ArchivistInstance
31
- private _queriesDiviner?: DivinerInstance<BoundWitnessDivinerParams, BoundWitnessDivinerQueryPayload, QueryBoundWitness>
32
- private _reResolveDelay = 1000 * 5 // 5 seconds
33
- private _resolveMutex = new Mutex()
34
- private _responsesArchivist?: ArchivistInstance
35
- private _responsesDiviner?: DivinerInstance<BoundWitnessDivinerParams, BoundWitnessDivinerQueryPayload, BoundWitness>
36
-
37
- constructor(params: TParams) {
38
- super(params)
39
- }
40
-
41
- get config(): TParams['config'] {
42
- return this.params.config
43
- }
44
-
45
- get pollFrequency(): number {
46
- const frequency = this.config?.pollFrequency ?? POLLING_FREQUENCY_DEFAULT
47
- if (frequency < POLLING_FREQUENCY_MIN || frequency > POLLING_FREQUENCY_MAX) {
48
- return POLLING_FREQUENCY_DEFAULT
49
- }
50
- return frequency
51
- }
52
-
53
- get rootModule() {
54
- return this.params.rootModule
55
- }
56
-
57
- /**
58
- * A cache of the last offset of the Diviner process per address
59
- */
60
- protected get lastState(): LRUCache<Address, Sequence> {
61
- const requiredConfig = { max: 1000, ttl: 0 }
62
- this._lastState = this._lastState ?? new LRUCache<Address, Sequence>(requiredConfig)
63
- return this._lastState
64
- }
65
-
66
- async queriesArchivist() {
67
- return await this._resolveMutex.runExclusive(async () => {
68
- this._queriesArchivist
69
- = this._queriesArchivist
70
- ?? (await this.resolve(
71
- assertEx(this.config?.intersect?.queries?.archivist, () => 'No queries Archivist defined'),
72
- isArchivistInstance,
73
- ))
74
- return this._queriesArchivist
75
- })
76
- }
77
-
78
- async queriesDiviner() {
79
- return await this._resolveMutex.runExclusive(async () => {
80
- this._queriesDiviner
81
- = this._queriesDiviner
82
- ?? ((await this.resolve(
83
- assertEx(this.config?.intersect?.queries?.boundWitnessDiviner, () => 'No queries Diviner defined'),
84
- isDivinerInstance,
85
- )) as DivinerInstance<BoundWitnessDivinerParams, BoundWitnessDivinerQueryPayload, QueryBoundWitness>)
86
- return this._queriesDiviner
87
- })
88
- }
89
-
90
- async responsesArchivist() {
91
- return await this._resolveMutex.runExclusive(async () => {
92
- this._responsesArchivist
93
- = this._responsesArchivist
94
- ?? (await this.resolve(
95
- assertEx(this.config?.intersect?.responses?.archivist, () => 'No responses Archivist defined'),
96
- isArchivistInstance,
97
- ))
98
- return this._responsesArchivist
99
- })
100
- }
101
-
102
- async responsesDiviner() {
103
- return await this._resolveMutex.runExclusive(async () => {
104
- this._responsesDiviner
105
- = this._responsesDiviner
106
- ?? ((await this.resolve(
107
- assertEx(this.config?.intersect?.responses?.boundWitnessDiviner, () => 'No responses Diviner defined'),
108
- isDivinerInstance,
109
- )) as DivinerInstance<BoundWitnessDivinerParams, BoundWitnessDivinerQueryPayload, BoundWitness>)
110
- return this._responsesDiviner
111
- })
112
- }
113
-
114
- /**
115
- * Commit the internal state of the process. This is similar
116
- * to a transaction completion in a database and should only be called
117
- * when results have been successfully persisted to the appropriate
118
- * external stores.
119
- * @param address The module address to commit the state for
120
- * @param nextState The state to commit
121
- */
122
- protected async commitState(address: Address, nextState: Sequence) {
123
- await Promise.resolve()
124
- // TODO: Offload to Archivist/Diviner instead of in-memory
125
- const lastState = this.lastState.get(address)
126
- if (lastState && lastState >= nextState) return
127
- this.lastState.set(address, nextState)
128
- }
129
-
130
- /**
131
- * Retrieves the last state of the process. Used to recover state after
132
- * preemptions, reboots, etc.
133
- */
134
- protected async retrieveState(address: Address): Promise<Sequence> {
135
- await Promise.resolve()
136
- const state = this.lastState.get(address)
137
- if (state === undefined) {
138
- const newState = SequenceConstants.minLocalSequence
139
- this.lastState.set(address, newState)
140
- return newState
141
- } else {
142
- return state
143
- }
144
- }
145
-
146
- private async resolve<T extends ModuleInstance>(id: ModuleIdentifier, typeCheck: TypeCheck<T>): Promise<T | undefined> {
147
- if (Date.now() - (this._lastResolveFailure[id] ?? 0) < this._reResolveDelay) {
148
- return
149
- }
150
- this._lastResolveFailure[id] = Date.now()
151
- const resolved = await ResolveHelper.resolveModuleIdentifier(this.rootModule, id)
152
- if (resolved) {
153
- if (typeCheck(resolved)) {
154
- delete this._lastResolveFailure[id]
155
- return resolved
156
- } else {
157
- this.logger?.warn(`Unable to resolve responsesDiviner as correct type [${id}][${resolved?.constructor?.name}]: ${resolved.id}`)
158
- }
159
- } else {
160
- this.logger?.debug(`Unable to resolve queriesArchivist [${id}] [${await ResolveHelper.traceModuleIdentifier(this.rootModule, id)}]`)
161
- }
162
- }
163
- }
@@ -1,190 +0,0 @@
1
- import type { Address, Hash } from '@xylabs/sdk-js'
2
- import {
3
- assertEx, clearTimeoutEx, delay, forget, setTimeoutEx,
4
- } from '@xylabs/sdk-js'
5
- import type { QueryBoundWitness } from '@xyo-network/boundwitness-model'
6
- import { isBoundWitness } from '@xyo-network/boundwitness-model'
7
- import type { BoundWitnessDivinerQueryPayload } from '@xyo-network/diviner-boundwitness-model'
8
- import { BoundWitnessDivinerQuerySchema } from '@xyo-network/diviner-boundwitness-model'
9
- import type { CacheConfig, ModuleQueryResult } from '@xyo-network/module-model'
10
- import { PayloadBuilder } from '@xyo-network/payload-builder'
11
- import {
12
- asSchema,
13
- type ModuleError, type Payload, type WithSources,
14
- } from '@xyo-network/payload-model'
15
- import { LRUCache } from 'lru-cache'
16
-
17
- import { AsyncQueryBusBase } from './AsyncQueryBusBase.ts'
18
- import type { AsyncQueryBusClientParams } from './model/index.ts'
19
- import { Pending } from './model/index.ts'
20
-
21
- export class AsyncQueryBusClient<TParams extends AsyncQueryBusClientParams = AsyncQueryBusClientParams> extends AsyncQueryBusBase<TParams> {
22
- protected _queryCache?: LRUCache<Hash, Pending | ModuleQueryResult>
23
- private _pollCount = 0
24
- private _pollId?: string
25
-
26
- constructor(params: TParams) {
27
- super(params)
28
- }
29
-
30
- get queryCacheConfig(): LRUCache.Options<Hash, Pending | ModuleQueryResult, unknown> {
31
- const queryCacheConfig: CacheConfig | undefined = this.config?.queryCache === true ? {} : this.config?.queryCache
32
- return {
33
- max: 100, ttl: 1000 * 60, ...queryCacheConfig,
34
- }
35
- }
36
-
37
- get started() {
38
- return !!this._pollId
39
- }
40
-
41
- /**
42
- * A cache of queries that have been issued
43
- */
44
- protected get queryCache(): LRUCache<Hash, Pending | ModuleQueryResult> {
45
- const config = this.queryCacheConfig
46
- const requiredConfig = { noUpdateTTL: false, ttlAutopurge: true }
47
- this._queryCache = this._queryCache ?? new LRUCache<Hash, Pending | ModuleQueryResult>({ ...config, ...requiredConfig })
48
- return this._queryCache
49
- }
50
-
51
- listeningAddresses() {
52
- return this._queryCache?.keys()
53
- }
54
-
55
- async send(address: Address, query: QueryBoundWitness, payloads?: Payload[] | undefined): Promise<ModuleQueryResult> {
56
- this.logger?.debug(`Begin issuing query to: ${address}`)
57
- const routedQuery = { ...query, $destination: [address] }
58
- // console.log('queryArchivist - calling')
59
- const queryArchivist = assertEx(
60
- await this.queriesArchivist(),
61
- () => `Unable to contact queriesArchivist [${this.config?.intersect?.queries?.archivist}]`,
62
- )
63
- // console.log('queryArchivist')
64
-
65
- // TODO: Should we always re-hash to true up timestamps? We can't
66
- // re-sign correctly so we would lose that information if we did and
67
- // would also be replying to consumers with a different query hash than
68
- // they sent us (which might be OK since it reflect the chain of custody)
69
- // Revisit this once we have proxy module support as they are another
70
- // intermediary to consider.
71
- const routedQueryHash = await PayloadBuilder.dataHash(routedQuery)
72
- this.logger?.debug(`Issuing query: ${routedQueryHash} to: ${address}`)
73
- // If there was data associated with the query, add it to the insert
74
- const data = payloads ? [routedQuery, ...payloads] : [routedQuery]
75
- const insertResult = await queryArchivist.insert?.(data)
76
- this.logger?.debug(`Issued query: ${routedQueryHash} to: ${address}`)
77
- this.queryCache.set(routedQueryHash, Pending)
78
- if (!insertResult) throw new Error('Unable to issue query to queryArchivist')
79
- return new Promise<ModuleQueryResult>((resolve, reject) => {
80
- this.logger?.debug(`Polling for response to query: ${routedQueryHash}`)
81
- let nextDelay = 100
82
- const pollForResponse = async () => {
83
- try {
84
- this.start()
85
- let response = this.queryCache.get(routedQueryHash)
86
- // Poll for response until cache key expires (response timed out)
87
- while (response !== undefined) {
88
- // console.log('polling...')
89
- // Wait a bit
90
- await delay(nextDelay)
91
- // Check the status of the response
92
- response = this.queryCache.get(routedQueryHash)
93
- // If status is no longer pending that means we received a response
94
- if (response && response !== Pending) {
95
- this.logger?.log(`Returning response to query: ${routedQueryHash}`)
96
- resolve(response)
97
- return
98
- }
99
- // back off the polling frequency
100
- nextDelay = Math.floor(nextDelay * 1.2)
101
- // cap it at 1000ms
102
- if (nextDelay > 1000) nextDelay = 1000
103
- }
104
- // If we got here waiting for a response timed out
105
- this.logger?.error('Timeout waiting for query response')
106
- // Resolve with error to match what a local module would do if it were to error
107
- // TODO: BW Builder/Sign result as this module?
108
- const error: WithSources<ModuleError> = {
109
- message: 'Timeout waiting for query response',
110
- query: asSchema('network.xyo.boundwitness', true),
111
- schema: asSchema('network.xyo.error.module', true),
112
- $sources: [routedQueryHash],
113
- }
114
- reject(error)
115
- return
116
- } finally {
117
- this.stop()
118
- }
119
- }
120
- forget(pollForResponse())
121
- })
122
- }
123
-
124
- /**
125
- * Runs the background divine process on a loop with a delay
126
- * specified by the `config.pollFrequency`
127
- */
128
- private poll() {
129
- this._pollId = setTimeoutEx(async () => {
130
- try {
131
- await this.processIncomingResponses()
132
- } catch (e) {
133
- this.logger?.error?.(`Error in main loop: ${e}`)
134
- } finally {
135
- if (this._pollId) clearTimeoutEx(this._pollId)
136
- this._pollId = undefined
137
- this.poll()
138
- }
139
- }, this.pollFrequency)
140
- }
141
-
142
- /**
143
- * Background process for processing incoming responses to previously issued queries
144
- */
145
- private processIncomingResponses = async () => {
146
- const responseArchivist = await this.responsesArchivist()
147
- if (responseArchivist) {
148
- const responseBoundWitnessDiviner = await this.responsesDiviner()
149
- if (responseBoundWitnessDiviner) {
150
- const pendingCommands = [...this.queryCache.entries()].filter(([_, status]) => status === Pending)
151
- // TODO: Do in throttled batches
152
- await Promise.allSettled(
153
- pendingCommands.map(async ([sourceQuery, status]) => {
154
- if (status === Pending) {
155
- const divinerQuery: BoundWitnessDivinerQueryPayload = {
156
- limit: 1, order: 'desc', schema: BoundWitnessDivinerQuerySchema, sourceQuery,
157
- }
158
- const result = await responseBoundWitnessDiviner.divine([divinerQuery])
159
- if (result && result.length > 0) {
160
- const response = result.find(isBoundWitness)
161
- if (response && (response as unknown as { $sourceQuery: string })?.$sourceQuery === sourceQuery) {
162
- this.logger?.debug(`Found response to query: ${sourceQuery}`)
163
- // Get any payloads associated with the response
164
- const payloads: Payload[] = response.payload_hashes?.length > 0 ? await responseArchivist.get(response.payload_hashes) : []
165
- this.queryCache.set(sourceQuery, [response, payloads, []])
166
- }
167
- }
168
- }
169
- }),
170
- )
171
- }
172
- }
173
- }
174
-
175
- private start() {
176
- if (this._pollCount === 0) {
177
- this.poll()
178
- }
179
- this._pollCount++
180
- }
181
-
182
- private stop() {
183
- this._pollCount--
184
- if (this._pollCount <= 0) {
185
- if (this._pollId) clearTimeoutEx(this._pollId)
186
- this._pollId = undefined
187
- this._pollCount = 0
188
- }
189
- }
190
- }