@xyo-network/module-abstract 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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xyo-network/module-abstract",
3
- "version": "5.3.20",
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,45 +30,72 @@
30
30
  "types": "dist/neutral/index.d.ts",
31
31
  "files": [
32
32
  "dist",
33
- "src",
34
33
  "!**/*.bench.*",
35
34
  "!**/*.spec.*",
36
- "!**/*.test.*"
35
+ "!**/*.test.*",
36
+ "README.md"
37
37
  ],
38
38
  "dependencies": {
39
- "@xyo-network/account": "~5.3.20",
40
- "@xyo-network/account-model": "~5.3.20",
41
- "@xyo-network/archivist-model": "~5.3.20",
42
- "@xyo-network/boundwitness-builder": "~5.3.20",
43
- "@xyo-network/boundwitness-model": "~5.3.20",
44
- "@xyo-network/boundwitness-wrapper": "~5.3.20",
45
- "@xyo-network/config-payload-plugin": "~5.3.20",
46
- "@xyo-network/manifest-model": "~5.3.20",
47
- "@xyo-network/module-model": "~5.3.20",
48
- "@xyo-network/module-resolver": "~5.3.20",
49
- "@xyo-network/node-model": "~5.3.20",
50
- "@xyo-network/payload-builder": "~5.3.20",
51
- "@xyo-network/payload-model": "~5.3.20",
52
- "@xyo-network/query-payload-plugin": "~5.3.20",
53
- "@xyo-network/wallet-model": "~5.3.20",
54
39
  "async-mutex": "~0.5.0",
55
- "lru-cache": "^11.2.7"
40
+ "lru-cache": "~11.2.7",
41
+ "@xyo-network/account-model": "~5.3.24",
42
+ "@xyo-network/account": "~5.3.24",
43
+ "@xyo-network/boundwitness-wrapper": "~5.3.24",
44
+ "@xyo-network/archivist-model": "~5.3.24",
45
+ "@xyo-network/boundwitness-model": "~5.3.24",
46
+ "@xyo-network/config-payload-plugin": "~5.3.24",
47
+ "@xyo-network/module-model": "~5.3.24",
48
+ "@xyo-network/manifest-model": "~5.3.24",
49
+ "@xyo-network/module-resolver": "~5.3.24",
50
+ "@xyo-network/node-model": "~5.3.24",
51
+ "@xyo-network/query-payload-plugin": "~5.3.24",
52
+ "@xyo-network/boundwitness-builder": "~5.3.24",
53
+ "@xyo-network/payload-model": "~5.3.24",
54
+ "@xyo-network/wallet-model": "~5.3.24",
55
+ "@xyo-network/payload-builder": "~5.3.24"
56
56
  },
57
57
  "devDependencies": {
58
- "@xylabs/sdk-js": "^5.0.90",
59
- "@xylabs/ts-scripts-common": "~7.5.6",
60
- "@xylabs/ts-scripts-yarn3": "~7.5.6",
61
- "@xylabs/tsconfig": "~7.5.6",
62
- "@xylabs/vitest-extended": "~5.0.90",
58
+ "@opentelemetry/api": "^1.9.1",
59
+ "@types/node": "^25.5.0",
60
+ "@xylabs/sdk-js": "^5.0.93",
61
+ "@xylabs/ts-scripts-common": "~7.6.16",
62
+ "@xylabs/ts-scripts-pnpm": "~7.6.16",
63
+ "@xylabs/tsconfig": "~7.6.16",
64
+ "@xylabs/vitest-extended": "~5.0.93",
65
+ "acorn": "^8.16.0",
66
+ "async-mutex": "~0.5.0",
67
+ "axios": "^1.14.0",
68
+ "esbuild": "^0.28.0",
69
+ "ethers": "^6.16.0",
70
+ "lru-cache": "~11.2.7",
71
+ "tslib": "^2.8.1",
63
72
  "typescript": "~5.9.3",
73
+ "vite": "^8.0.3",
64
74
  "vitest": "~4.1.2",
65
- "zod": "^4.3.6"
75
+ "zod": "^4.3.6",
76
+ "@xyo-network/account": "~5.3.24",
77
+ "@xyo-network/account-model": "~5.3.24",
78
+ "@xyo-network/archivist-model": "~5.3.24",
79
+ "@xyo-network/boundwitness-builder": "~5.3.24",
80
+ "@xyo-network/manifest-model": "~5.3.24",
81
+ "@xyo-network/boundwitness-model": "~5.3.24",
82
+ "@xyo-network/boundwitness-wrapper": "~5.3.24",
83
+ "@xyo-network/config-payload-plugin": "~5.3.24",
84
+ "@xyo-network/module-resolver": "~5.3.24",
85
+ "@xyo-network/node-model": "~5.3.24",
86
+ "@xyo-network/module-model": "~5.3.24",
87
+ "@xyo-network/payload-builder": "~5.3.24",
88
+ "@xyo-network/payload-model": "~5.3.24",
89
+ "@xyo-network/query-payload-plugin": "~5.3.24",
90
+ "@xyo-network/wallet-model": "~5.3.24"
66
91
  },
67
92
  "peerDependencies": {
68
93
  "@xylabs/sdk-js": "^5",
94
+ "ethers": "^6",
95
+ "tslib": "^2.8.1",
69
96
  "zod": "^4"
70
97
  },
71
98
  "publishConfig": {
72
99
  "access": "public"
73
100
  }
74
- }
101
+ }
@@ -1,672 +0,0 @@
1
- /* eslint-disable max-lines */
2
- import type {
3
- Address, CreatableInstance, Hash, Logger,
4
- Promisable,
5
- } from '@xylabs/sdk-js'
6
- import {
7
- AbstractCreatable,
8
- assertEx,
9
- ConsoleLogger, exists,
10
- forget,
11
- globallyUnique,
12
- handleErrorAsync,
13
- IdLogger,
14
- isDefined, isObject, isString, isUndefined, LevelLogger,
15
- LogLevel,
16
- PromiseEx,
17
- } from '@xylabs/sdk-js'
18
- import { Account } from '@xyo-network/account'
19
- import { type AccountInstance, isAccountInstance } from '@xyo-network/account-model'
20
- import type { ArchivistInstance } from '@xyo-network/archivist-model'
21
- import { asArchivistInstance } from '@xyo-network/archivist-model'
22
- import { BoundWitnessBuilder, QueryBoundWitnessBuilder } from '@xyo-network/boundwitness-builder'
23
- import type { BoundWitness, QueryBoundWitness } from '@xyo-network/boundwitness-model'
24
- import { isQueryBoundWitness } from '@xyo-network/boundwitness-model'
25
- import { QueryBoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
26
- import type { ConfigPayload } from '@xyo-network/config-payload-plugin'
27
- import { ConfigSchema } from '@xyo-network/config-payload-plugin'
28
- import type { ModuleManifestPayload } from '@xyo-network/manifest-model'
29
- import type {
30
- AddressPayload,
31
- AddressPreviousHashPayload,
32
- ArchivingModuleConfig,
33
- AttachableModuleInstance,
34
- CreatableModule,
35
- CreatableModuleFactory,
36
- CreatableModuleInstance,
37
- Labels,
38
- ModuleBusyEventArgs,
39
- ModuleConfig,
40
- ModuleDescriptionPayload,
41
- ModuleDetailsError,
42
- ModuleEventData,
43
- ModuleManifestQuery,
44
- ModuleQueriedEventArgs,
45
- ModuleQueries,
46
- ModuleQueryHandlerResult,
47
- ModuleQueryResult,
48
- ModuleResolverInstance,
49
- QueryableModule,
50
- QueryableModuleParams,
51
- } from '@xyo-network/module-model'
52
- import {
53
- AddressPreviousHashSchema,
54
- AddressSchema,
55
- creatableModule,
56
- DeadModuleError,
57
- isModuleName,
58
- isSerializable,
59
- ModuleAddressQuerySchema,
60
- ModuleConfigSchema,
61
- ModuleDescriptionSchema,
62
- ModuleFactory,
63
- ModuleManifestQuerySchema,
64
- ModuleStateQuerySchema,
65
- ModuleSubscribeQuerySchema,
66
- ObjectResolverPriority,
67
- } from '@xyo-network/module-model'
68
- import { PayloadBuilder } from '@xyo-network/payload-builder'
69
- import {
70
- asSchema,
71
- type ModuleError, type Payload, type Query, type Schema,
72
- } from '@xyo-network/payload-model'
73
- import { QuerySchema } from '@xyo-network/query-payload-plugin'
74
- import type { WalletInstance } from '@xyo-network/wallet-model'
75
- import { Mutex } from 'async-mutex'
76
- import { LRUCache } from 'lru-cache'
77
-
78
- import { determineAccount } from './determineAccount.ts'
79
- import { ModuleErrorBuilder } from './Error.ts'
80
- import type { Queryable } from './QueryValidator/index.ts'
81
- import { ModuleConfigQueryValidator, SupportedQueryValidator } from './QueryValidator/index.ts'
82
-
83
- export const DefaultModuleQueries = [ModuleAddressQuerySchema, ModuleSubscribeQuerySchema, ModuleManifestQuerySchema, ModuleStateQuerySchema]
84
-
85
- creatableModule()
86
- export abstract class AbstractModule<TParams extends QueryableModuleParams = QueryableModuleParams, TEventData extends ModuleEventData = ModuleEventData>
87
- extends AbstractCreatable<TParams, TEventData>
88
- implements QueryableModule<TParams, TEventData> {
89
- static readonly allowRandomAccount: boolean = true
90
- static readonly configSchemas: Schema[] = [ModuleConfigSchema]
91
- static readonly defaultConfigSchema: Schema = ModuleConfigSchema
92
- // eslint-disable-next-line sonarjs/public-static-readonly
93
- static override defaultLogger: Logger = new ConsoleLogger(LogLevel.warn)
94
- // eslint-disable-next-line sonarjs/public-static-readonly
95
- static enableLazyLoad = false
96
- static readonly labels: Labels = {}
97
- static readonly uniqueName = globallyUnique('AbstractModule', AbstractModule, 'xyo')
98
-
99
- protected static privateConstructorKey = Date.now().toString()
100
-
101
- protected _account: AccountInstance | undefined
102
-
103
- // cache manifest based on maxDepth
104
- protected _cachedManifests = new LRUCache<number, ModuleManifestPayload>({ max: 10, ttl: 1000 * 60 * 5 })
105
-
106
- protected _globalReentrancyMutex: Mutex | undefined
107
-
108
- protected _lastError?: ModuleDetailsError
109
-
110
- protected _moduleConfigQueryValidator: Queryable | undefined
111
- protected _supportedQueryValidator: Queryable | undefined
112
-
113
- private _busyCount = 0
114
- private _logger: Logger | undefined | null = undefined
115
-
116
- get account() {
117
- return assertEx(this._account, () => 'Missing account')
118
- }
119
-
120
- get additionalSigners(): AccountInstance[] {
121
- return this.params.additionalSigners ?? []
122
- }
123
-
124
- get address() {
125
- return this.account.address
126
- }
127
-
128
- get allowAnonymous() {
129
- return !!this.config.security?.allowAnonymous
130
- }
131
-
132
- get allowNameResolution() {
133
- return this.params.allowNameResolution ?? true
134
- }
135
-
136
- get archiving(): ArchivingModuleConfig['archiving'] | undefined {
137
- return this.config.archiving
138
- }
139
-
140
- get archivist() {
141
- return this.config.archivist
142
- }
143
-
144
- get config(): TParams['config'] & { schema: Schema } {
145
- return { ...this.params.config, schema: this.params.config.schema ?? ModuleConfigSchema }
146
- }
147
-
148
- get dead() {
149
- return this.status === 'error'
150
- }
151
-
152
- get ephemeralQueryAccountEnabled(): boolean {
153
- return !!this.params.ephemeralQueryAccountEnabled
154
- }
155
-
156
- get globalReentrancyMutex() {
157
- this._globalReentrancyMutex = this._globalReentrancyMutex ?? (this.reentrancy?.scope === 'global' ? new Mutex() : undefined)
158
- return this._globalReentrancyMutex
159
- }
160
-
161
- get id() {
162
- return this.modName ?? this.address
163
- }
164
-
165
- override get logger() {
166
- // we use null to prevent a second round of not creating a logger
167
- if (isUndefined(this._logger)) {
168
- const logLevel = this.config.logLevel
169
- const newLogger = this._logger ?? (this.params?.logger ? new IdLogger(this.params.logger, () => `${this.constructor.name}[${this.id}]`) : null)
170
- this._logger = (isObject(newLogger) && isDefined(logLevel)) ? new LevelLogger(newLogger, logLevel) : newLogger
171
- }
172
- return this._logger ?? undefined
173
- }
174
-
175
- get modName() {
176
- return this.config.name
177
- }
178
-
179
- get priority() {
180
- return ObjectResolverPriority.Normal
181
- }
182
-
183
- get queries(): Schema[] {
184
- return [...DefaultModuleQueries]
185
- }
186
-
187
- get reentrancy() {
188
- return this.config.reentrancy
189
- }
190
-
191
- override get statusReporter() {
192
- return this.params.statusReporter
193
- }
194
-
195
- get timestamp() {
196
- return this.config.timestamp ?? false
197
- }
198
-
199
- protected get moduleConfigQueryValidator(): Queryable {
200
- return assertEx(this._moduleConfigQueryValidator, () => 'ModuleConfigQueryValidator not initialized')
201
- }
202
-
203
- protected get supportedQueryValidator(): Queryable {
204
- return assertEx(this._supportedQueryValidator, () => 'SupportedQueryValidator not initialized')
205
- }
206
-
207
- abstract get downResolver(): ModuleResolverInstance
208
-
209
- abstract get upResolver(): ModuleResolverInstance
210
-
211
- static _getRootFunction(funcName: string) {
212
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
213
- let anyThis = this as any
214
- while (anyThis.__proto__[funcName]) {
215
- anyThis = anyThis.__proto__
216
- }
217
- return anyThis[funcName]
218
- }
219
-
220
- static _noOverride(functionName: string) {
221
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
222
- const thisFunc = (this as any)[functionName]
223
-
224
- const rootFunc = this._getRootFunction(functionName)
225
- assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)
226
- }
227
-
228
- static override async createHandler<T extends CreatableInstance>(
229
- inInstance: T,
230
- ) {
231
- const instance = (await super.createHandler(inInstance))
232
- if (instance instanceof AbstractModule) {
233
- if (this.configSchemas.length === 0) {
234
- throw new Error(`No allowed config schemas for [${this.name}]`)
235
- }
236
-
237
- const schema: Schema = instance.config.schema ?? this.defaultConfigSchema
238
- const allowedSchemas: Schema[] = this.configSchemas
239
-
240
- assertEx(this.isAllowedSchema(schema), () => `Bad Config Schema [Received ${schema}] [Expected ${JSON.stringify(allowedSchemas)}]`)
241
- } else {
242
- throw new TypeError(`Invalid instance type [${instance.constructor.name}] for [${this.name}]`)
243
- }
244
-
245
- return instance
246
- }
247
-
248
- static async determineAccount(params: {
249
- account?: AccountInstance | 'random'
250
- accountPath?: string
251
- wallet?: WalletInstance
252
- }): Promise<AccountInstance> {
253
- return await determineAccount(params, this.allowRandomAccount)
254
- }
255
-
256
- static factory<TModule extends CreatableModuleInstance>(
257
- this: CreatableModule<TModule>,
258
- params?: Partial<TModule['params']>,
259
- ): CreatableModuleFactory<TModule> {
260
- return ModuleFactory.withParams<TModule>(this, params)
261
- }
262
-
263
- static isAllowedSchema(schema: Schema): boolean {
264
- return this.configSchemas.includes(schema)
265
- }
266
-
267
- static override async paramsHandler<T extends AttachableModuleInstance<QueryableModuleParams, ModuleEventData>>(
268
- inParams: Partial<T['params']> = {},
269
- ) {
270
- const superParams = await super.paramsHandler(inParams)
271
- const params = {
272
- ...superParams,
273
- account: await this.determineAccount(superParams),
274
- config: { schema: this.defaultConfigSchema, ...superParams.config },
275
- logger: superParams.logger ?? this.defaultLogger,
276
- } as T['params']
277
- return params
278
- }
279
-
280
- // eslint-disable-next-line sonarjs/no-identical-functions
281
- _getRootFunction(funcName: string) {
282
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
283
- let anyThis = this as any
284
- while (anyThis.__proto__[funcName]) {
285
- anyThis = anyThis.__proto__
286
- }
287
- return anyThis[funcName]
288
- }
289
-
290
- async busy<R>(closure: () => Promise<R>) {
291
- if (this._busyCount <= 0) {
292
- this._busyCount = 0
293
- const args: ModuleBusyEventArgs = { busy: true, mod: this }
294
- await this.emit('moduleBusy', args)
295
- }
296
- this._busyCount++
297
- try {
298
- return await closure()
299
- } finally {
300
- this._busyCount--
301
- if (this._busyCount <= 0) {
302
- this._busyCount = 0
303
- const args: ModuleBusyEventArgs = { busy: false, mod: this }
304
- await this.emit('moduleBusy', args)
305
- }
306
- }
307
- }
308
-
309
- override async createHandler() {
310
- await super.createHandler()
311
- assertEx(this.name === undefined || isModuleName(this.name), () => `Invalid module name: ${this.name}`)
312
-
313
- if (this.params.account === 'random') {
314
- this._account = await Account.random()
315
- } else if (isAccountInstance(this.params.account)) {
316
- this._account = this.params.account
317
- }
318
-
319
- assertEx(isAccountInstance(this._account), () => `Invalid account instance: ${this._account}`)
320
-
321
- this._supportedQueryValidator = new SupportedQueryValidator(this as QueryableModule).queryable
322
- this._moduleConfigQueryValidator = new ModuleConfigQueryValidator(this.config).queryable
323
-
324
- if (!AbstractModule.enableLazyLoad) {
325
- setTimeout(() => forget(this.start()), 200)
326
- }
327
- }
328
-
329
- override emit<TEventName extends keyof TEventData = keyof TEventData, TEventArgs extends TEventData[TEventName] = TEventData[TEventName]>(
330
- eventName: TEventName,
331
- eventArgs: TEventArgs,
332
- ) {
333
- return super.emit(eventName, eventArgs)
334
- }
335
-
336
- isSupportedQuery(query: Schema, assert: boolean | string = false): boolean {
337
- // check if ever supported
338
- if (!this.queries.includes(query)) {
339
- if (assert !== false) {
340
- assertEx(false, () => `Query not supported [${isString(assert) ? assert : query}] on [${this.modName}, ${this.address}] (queries list)`)
341
- }
342
- return false
343
- }
344
- // check if config allows it
345
- const supported = Array.isArray(this.config.allowedQueries) ? this.config.allowedQueries.includes(query) : true
346
- if (assert !== false) {
347
- assertEx(supported, () => `Query not supported [${isString(assert) ? assert : query}] on [${this.modName}, ${this.address}] (config)`)
348
- }
349
- return supported
350
- }
351
-
352
- previousHash(): Promisable<string | undefined> {
353
- this._checkDead()
354
- return this.account.previousHash
355
- }
356
-
357
- async query<T extends QueryBoundWitness = QueryBoundWitness, TConfig extends ModuleConfig = ModuleConfig>(
358
- query: T,
359
- payloads?: Payload[],
360
- queryConfig?: TConfig,
361
- ): Promise<ModuleQueryResult> {
362
- this._checkDead()
363
- this._noOverride('query')
364
- return await this.spanAsync('query', async () => {
365
- const sourceQuery = assertEx(isQueryBoundWitness(query) ? query : undefined, () => 'Unable to parse query')
366
- return await this.busy(async () => {
367
- const resultPayloads: Payload[] = []
368
- const errorPayloads: ModuleError[] = []
369
- const queryAccount = this.ephemeralQueryAccountEnabled ? await Account.random() : undefined
370
-
371
- try {
372
- await this.startedAsync('throw')
373
- if (!this.allowAnonymous && query.addresses.length === 0) {
374
- throw new Error(`Anonymous Queries not allowed, but running anyway [${this.modName}], [${this.address}]`)
375
- }
376
- if (queryConfig?.allowedQueries) {
377
- assertEx(queryConfig?.allowedQueries.includes(sourceQuery.schema), () => `Query not allowed [${sourceQuery.schema}]`)
378
- }
379
- resultPayloads.push(...(await this.queryHandler(sourceQuery, payloads, queryConfig)))
380
- } catch (ex) {
381
- await handleErrorAsync(ex, async (err) => {
382
- const error = err as ModuleDetailsError
383
- this._lastError = error
384
- // this.status = 'dead'
385
- errorPayloads.push(
386
- new ModuleErrorBuilder()
387
- .meta({ $sources: [await PayloadBuilder.dataHash(sourceQuery)] })
388
- .name(this.modName ?? '<Unknown>')
389
- .query(sourceQuery.schema)
390
- .details(error.details)
391
- .message(error.message)
392
- .build(),
393
- )
394
- })
395
- }
396
- if (this.timestamp) {
397
- const timestamp = { schema: asSchema('network.xyo.timestamp', true), timestamp: Date.now() }
398
- resultPayloads.push(timestamp)
399
- }
400
- const result = await this.bindQueryResult(sourceQuery, resultPayloads, queryAccount ? [queryAccount] : [], errorPayloads)
401
- const args: ModuleQueriedEventArgs = {
402
- mod: this, payloads, query: sourceQuery, result,
403
- }
404
- await this.emit('moduleQueried', args)
405
- return result
406
- })
407
- }, { timeBudgetLimit: 200 })
408
- }
409
-
410
- async queryable<T extends QueryBoundWitness = QueryBoundWitness, TConfig extends ModuleConfig = ModuleConfig>(
411
- query: T,
412
- payloads?: Payload[],
413
- queryConfig?: TConfig,
414
- ): Promise<boolean> {
415
- if (this.dead) {
416
- return false
417
- }
418
- if (!(this.started('warn'))) return false
419
- const configValidator
420
- = queryConfig ? new ModuleConfigQueryValidator(Object.assign({}, this.config, queryConfig)).queryable : this.moduleConfigQueryValidator
421
- const validators = [this.supportedQueryValidator, configValidator]
422
-
423
- const results = await Promise.all(validators.map(validator => validator(query, payloads)))
424
- for (const result of results) {
425
- if (!result) {
426
- return false
427
- }
428
- }
429
- return true
430
- }
431
-
432
- protected _checkDead() {
433
- if (this.dead) {
434
- throw new DeadModuleError(this.id, this._lastError)
435
- }
436
- }
437
-
438
- protected async archivistInstance(): Promise<ArchivistInstance | undefined>
439
- protected async archivistInstance(required: true): Promise<ArchivistInstance>
440
- protected async archivistInstance(required = false): Promise<ArchivistInstance | undefined> {
441
- const archivist = this.archivist
442
- if (isUndefined(archivist)) {
443
- if (required) {
444
- throw new Error('No archivist specified')
445
- }
446
- return undefined
447
- }
448
- const resolved = (await this.upResolver.resolve(archivist)) ?? (await this.downResolver.resolve(archivist))
449
- if (required) {
450
- assertEx(resolved, () => `Unable to resolve archivist [${archivist}]`)
451
- }
452
- return resolved ? asArchivistInstance(resolved, () => `Specified archivist is not an Archivist [${archivist}]`) : undefined
453
- }
454
-
455
- protected bindHashes(hashes: Hash[], schema: Schema[], account?: AccountInstance) {
456
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
457
- return new PromiseEx((resolve) => {
458
- const result = this.bindHashesInternal(hashes, schema, account)
459
- resolve?.(result)
460
- return result
461
- }, account)
462
- }
463
-
464
- protected async bindHashesInternal(hashes: Hash[], schema: Schema[], account: AccountInstance = this.account): Promise<BoundWitness> {
465
- const builder = new BoundWitnessBuilder().hashes(hashes, schema).signer(account)
466
- const result: BoundWitness = (await builder.build())[0]
467
- this.logger?.debug(`result: ${JSON.stringify(result, null, 2)}`)
468
- return result
469
- }
470
-
471
- protected bindQuery<T extends Query>(
472
- query: T,
473
- payloads?: Payload[],
474
- account?: AccountInstance,
475
- additionalSigners?: AccountInstance[],
476
- ): PromiseEx<[QueryBoundWitness, Payload[], Payload[]], AccountInstance> {
477
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
478
- return new PromiseEx<[QueryBoundWitness, Payload[], Payload[]], AccountInstance>(async (resolve) => {
479
- const result = await this.bindQueryInternal(query, payloads, account, additionalSigners)
480
- resolve?.(result)
481
- return result
482
- }, account)
483
- }
484
-
485
- protected async bindQueryInternal<T extends Query>(
486
- query: T,
487
- payloads?: Payload[],
488
- account: AccountInstance = this.account,
489
- additionalSigners: AccountInstance[] = [],
490
- ): Promise<[QueryBoundWitness, Payload[], Payload[]]> {
491
- const accounts = [account, ...additionalSigners].filter(exists)
492
- const builder = new QueryBoundWitnessBuilder().payloads(payloads).signers(accounts).query(query)
493
-
494
- const [bw, payloadsOut, errors] = await builder.build()
495
- return [bw, [...payloadsOut], errors]
496
- }
497
-
498
- protected async bindQueryResult<T extends Query>(
499
- query: T,
500
- payloads: Payload[],
501
- additionalWitnesses: AccountInstance[] = [],
502
- errors?: ModuleError[],
503
- ): Promise<ModuleQueryResult> {
504
- const queryDataHash = await PayloadBuilder.dataHash(query)
505
- const builder = new BoundWitnessBuilder().payloads(payloads).errors(errors).sourceQuery(queryDataHash)
506
- const witnesses = [this.account, ...additionalWitnesses].filter(exists)
507
- builder.signers(witnesses)
508
- const result = [
509
- PayloadBuilder.omitPrivateStorageMeta((await builder.build())[0]),
510
- PayloadBuilder.omitPrivateStorageMeta(payloads),
511
- PayloadBuilder.omitPrivateStorageMeta(errors ?? []),
512
- ] as ModuleQueryResult
513
- if (this.archiving && this.isAllowedArchivingQuery(query.schema)) {
514
- forget(this.storeToArchivists(result.flat()))
515
- }
516
- return result
517
- }
518
-
519
- protected generateConfigAndAddress(_maxDepth?: number): Promisable<Payload[]> {
520
- const config = this.config
521
- const address: AddressPayload = { schema: AddressSchema, address: this.address }
522
- const queries = this.queries.map((query) => {
523
- return { schema: QuerySchema, query }
524
- })
525
- const configSchema: ConfigPayload = {
526
- config: config.schema,
527
- schema: ConfigSchema,
528
- }
529
- return ([config, configSchema, address, ...queries]).filter(exists)
530
- }
531
-
532
- protected async generateDescribe(): Promise<ModuleDescriptionPayload> {
533
- const description: ModuleDescriptionPayload = {
534
- address: this.address,
535
- name: this.modName ?? `Unnamed ${this.constructor.name}`,
536
- queries: this.queries,
537
- schema: ModuleDescriptionSchema,
538
- }
539
-
540
- const discover = await this.generateConfigAndAddress()
541
-
542
- description.children = (
543
- discover?.map((payload) => {
544
- const address = payload.schema === AddressSchema ? (payload as AddressPayload).address : undefined
545
- return address == this.address ? undefined : address
546
- }) ?? []
547
- ).filter(exists)
548
-
549
- return description
550
- }
551
-
552
- /** @deprecated use archivistInstance() instead */
553
- protected async getArchivist(): Promise<ArchivistInstance | undefined> {
554
- return await this.archivistInstance()
555
- }
556
-
557
- protected isAllowedArchivingQuery(schema: Schema): boolean {
558
- const queries = this.archiving?.queries
559
- if (queries) {
560
- return queries.includes(schema)
561
- }
562
- return true
563
- }
564
-
565
- protected manifestHandler(_maxDepth: number = 1, _ignoreAddresses: Address[] = []): Promisable<ModuleManifestPayload> {
566
- throw new Error('Not supported')
567
- }
568
-
569
- protected moduleAddressHandler(): Promisable<(AddressPreviousHashPayload | AddressPayload)[]> {
570
- const address = this.address
571
- const name = this.modName
572
- const previousHash = this.account.previousHash
573
- const moduleAccount = isDefined(name)
574
- ? {
575
- address, name, schema: AddressSchema,
576
- }
577
- : { address, schema: AddressSchema }
578
- const moduleAccountPreviousHash = isDefined(previousHash)
579
- ? {
580
- address, previousHash, schema: AddressPreviousHashSchema,
581
- }
582
- : { address, schema: AddressSchema }
583
- return [moduleAccount, moduleAccountPreviousHash]
584
- }
585
-
586
- protected async queryHandler<T extends QueryBoundWitness = QueryBoundWitness, TConfig extends ModuleConfig = ModuleConfig>(
587
- query: T,
588
- payloads?: Payload[],
589
- queryConfig?: TConfig,
590
- ): Promise<ModuleQueryHandlerResult> {
591
- await this.startedAsync('throw')
592
- const wrapper = QueryBoundWitnessWrapper.parseQuery<ModuleQueries>(query, payloads)
593
- const queryPayload = await wrapper.getQuery()
594
- assertEx(await this.queryable(query, payloads, queryConfig))
595
- const resultPayloads: Payload[] = []
596
- switch (queryPayload.schema) {
597
- case ModuleManifestQuerySchema: {
598
- const typedQueryPayload = queryPayload as ModuleManifestQuery
599
- resultPayloads.push(await this.manifestHandler(typedQueryPayload.maxDepth))
600
- break
601
- }
602
- case ModuleAddressQuerySchema: {
603
- resultPayloads.push(...(await this.moduleAddressHandler()))
604
- break
605
- }
606
- case ModuleStateQuerySchema: {
607
- resultPayloads.push(...(await this.stateHandler()))
608
- break
609
- }
610
- case ModuleSubscribeQuerySchema: {
611
- this.subscribeHandler()
612
- break
613
- }
614
- default: {
615
- throw new Error(`Unsupported Query [${(queryPayload as Payload).schema}]`)
616
- }
617
- }
618
- return PayloadBuilder.omitPrivateStorageMeta(resultPayloads) as ModuleQueryHandlerResult
619
- }
620
-
621
- protected override async startHandler(): Promise<void> {
622
- this.validateConfig()
623
- await super.startHandler()
624
- }
625
-
626
- protected async stateHandler(): Promise<Payload[]> {
627
- return [await this.manifestHandler(), ...(await this.generateConfigAndAddress()), await this.generateDescribe()]
628
- }
629
-
630
- protected override async stopHandler(): Promise<void> {
631
- await super.stopHandler()
632
- this._startPromise = undefined
633
- }
634
-
635
- protected subscribeHandler() {
636
- return
637
- }
638
-
639
- protected validateConfig(config?: unknown, parents: string[] = []): boolean {
640
- // eslint-disable-next-line unicorn/no-array-reduce
641
- return Object.entries(config ?? this.config ?? {}).reduce((valid, [key, value]) => {
642
- switch (typeof value) {
643
- case 'function': {
644
- this.logger?.warn(`Fields of type function not allowed in config [${parents?.join('.')}.${key}]`)
645
- return false
646
- }
647
- case 'object': {
648
- if (Array.isArray(value)) {
649
- return (
650
- // eslint-disable-next-line unicorn/no-array-reduce
651
- value.reduce((valid, value) => {
652
- return this.validateConfig(value, [...parents, key]) && valid
653
- }, true) && valid
654
- )
655
- }
656
-
657
- if (!isSerializable(value)) {
658
- this.logger?.warn(`Fields that are not serializable to JSON are not allowed in config [${parents?.join('.')}.${key}]`)
659
- return false
660
- }
661
- return value ? this.validateConfig(value, [...parents, key]) && valid : true
662
- }
663
- default: {
664
- return valid
665
- }
666
- }
667
- }, true)
668
- }
669
-
670
- protected abstract certifyParents(): Promise<Payload[]>
671
- protected abstract storeToArchivists(payloads: Payload[]): Promise<Payload[]>
672
- }
@@ -1,357 +0,0 @@
1
- import type {
2
- Address, Promisable,
3
- TypeCheck,
4
- TypedValue,
5
- } from '@xylabs/sdk-js'
6
- import {
7
- exists, globallyUnique, isDefined,
8
- } from '@xylabs/sdk-js'
9
- import type { AccountInstance } from '@xyo-network/account-model'
10
- import type { ArchivistInstance } from '@xyo-network/archivist-model'
11
- import { asArchivistInstance } from '@xyo-network/archivist-model'
12
- import type { ModuleManifestPayload } from '@xyo-network/manifest-model'
13
- import { ModuleManifestPayloadSchema } from '@xyo-network/manifest-model'
14
- import type {
15
- AddressPayload,
16
- AddressPreviousHashPayload,
17
- AttachableModuleInstance,
18
- ModuleEventData,
19
- ModuleFilterOptions,
20
- ModuleIdentifier,
21
- ModuleInstance,
22
- ModuleInstanceParams,
23
- ModuleManifestQuery,
24
- ModuleName,
25
- ModuleNameResolver,
26
- ModuleQueryResult,
27
- ModuleStateQuery,
28
- ObjectFilterOptions,
29
- ResolveHelperConfig,
30
- } from '@xyo-network/module-model'
31
- import {
32
- duplicateModules,
33
- ModuleManifestQuerySchema,
34
- ModuleStateQuerySchema,
35
- resolveAll,
36
- resolveAllDown,
37
- resolveAllUp,
38
- ResolveHelper,
39
- resolvePathToInstance,
40
- } from '@xyo-network/module-model'
41
- import { CompositeModuleResolver } from '@xyo-network/module-resolver'
42
- import type { NodeInstance } from '@xyo-network/node-model'
43
- import { asNodeInstance } from '@xyo-network/node-model'
44
- import type { Payload, Query } from '@xyo-network/payload-model'
45
-
46
- import { AbstractModule } from './AbstractModule.ts'
47
-
48
- function filterIdentity<T extends TypedValue>(mod?: ModuleInstance, identityFunc?: TypeCheck<T>): T
49
- function filterIdentity<T extends TypedValue>(mods?: ModuleInstance[], identityFunc?: TypeCheck<T>): T[]
50
- function filterIdentity<T extends TypedValue>(mod?: ModuleInstance | ModuleInstance[], identityFunc?: TypeCheck<T>) {
51
- if (Array.isArray(mod)) {
52
- if (identityFunc) {
53
- return mod.map(m => identityFunc(m)).filter(exists)
54
- }
55
- return mod
56
- }
57
- return (mod ? (identityFunc ? identityFunc(mod) : true) : false) ? mod : undefined
58
- }
59
-
60
- export abstract class AbstractModuleInstance<TParams extends ModuleInstanceParams = ModuleInstanceParams, TEventData extends ModuleEventData = ModuleEventData>
61
- extends AbstractModule<TParams, TEventData>
62
- implements AttachableModuleInstance<TParams, TEventData>, ModuleNameResolver {
63
- static override readonly uniqueName = globallyUnique('AbstractModuleInstance', AbstractModuleInstance, 'xyo')
64
-
65
- // switches between old and new resolution system
66
- static readonly useNewResolver = false
67
-
68
- private _downResolver?: CompositeModuleResolver
69
- private _parents: NodeInstance[] = []
70
- private _privateResolver?: CompositeModuleResolver
71
- private _upResolver?: CompositeModuleResolver
72
-
73
- get downResolver() {
74
- this._downResolver
75
- = this._downResolver
76
- ?? new CompositeModuleResolver({
77
- allowNameResolution: this.allowNameResolution,
78
- moduleIdentifierTransformers: this.params.moduleIdentifierTransformers,
79
- root: this,
80
- })
81
- return this._downResolver
82
- }
83
-
84
- override get modName() {
85
- return super.modName
86
- }
87
-
88
- get moduleIdentifierTransformers() {
89
- return this.params.moduleIdentifierTransformers ?? ResolveHelper.transformers
90
- }
91
-
92
- get privateResolver() {
93
- this._privateResolver
94
- = this._privateResolver
95
- ?? new CompositeModuleResolver({
96
- allowNameResolution: this.allowNameResolution,
97
- moduleIdentifierTransformers: this.params.moduleIdentifierTransformers,
98
- root: this,
99
- })
100
- return this._privateResolver
101
- }
102
-
103
- get root() {
104
- return this
105
- }
106
-
107
- get timeBudget() {
108
- return this.config.timeBudget
109
- }
110
-
111
- get upResolver() {
112
- this._upResolver
113
- = this._upResolver
114
- ?? new CompositeModuleResolver({
115
- allowNameResolution: this.allowNameResolution,
116
- moduleIdentifierTransformers: this.params.moduleIdentifierTransformers,
117
- root: this,
118
- })
119
- return this._upResolver
120
- }
121
-
122
- addParent(mod: ModuleInstance) {
123
- const existingEntry = this._parents.find(parent => parent.address === mod.address)
124
- if (!existingEntry) {
125
- this._parents.push(asNodeInstance(mod, 'Only NodeInstances can be parents', { required: true }))
126
- }
127
- }
128
-
129
- async certifyParents(): Promise<Payload[]> {
130
- const parents = await this.parents()
131
- return (
132
- await Promise.all(
133
- parents.map(async (parent) => {
134
- const [bw, payloads, errors] = await parent.certifyQuery(this.address)
135
- return errors.length === 0 ? [bw, ...payloads] : []
136
- }),
137
- )
138
- ).flat()
139
- }
140
-
141
- override async createHandler() {
142
- await super.createHandler()
143
- const addToResolvers = this.params.addToResolvers ?? true
144
- if (addToResolvers) {
145
- this.upResolver.add(this)
146
- this.downResolver.add(this)
147
- }
148
- }
149
-
150
- manifest(maxDepth?: number): Promise<ModuleManifestPayload> {
151
- this._checkDead()
152
- return this.busy(async () => {
153
- return await this.manifestHandler(maxDepth)
154
- })
155
- }
156
-
157
- async manifestQuery(account: AccountInstance, maxDepth?: number): Promise<ModuleQueryResult<ModuleManifestPayload>> {
158
- const queryPayload: ModuleManifestQuery = { schema: ModuleManifestQuerySchema, ...(maxDepth === undefined ? {} : { maxDepth }) }
159
- return await this.sendQueryRaw<ModuleManifestQuery, Payload, ModuleManifestPayload>(queryPayload, undefined, account)
160
- }
161
-
162
- moduleAddress(): Promise<(AddressPayload | AddressPreviousHashPayload)[]> {
163
- this._checkDead()
164
- return this.busy(async () => {
165
- return await this.moduleAddressHandler()
166
- })
167
- }
168
-
169
- parents(): Promisable<NodeInstance[]> {
170
- return this._parents
171
- }
172
-
173
- privateChildren(): Promisable<ModuleInstance[]> {
174
- return [...this.params.privateChildren ?? []]
175
- }
176
-
177
- publicChildren(): Promisable<ModuleInstance[]> {
178
- return [...this.params.publicChildren ?? []]
179
- }
180
-
181
- removeParent(address: Address) {
182
- this._parents = this._parents.filter(item => item.address !== address)
183
- }
184
-
185
- async resolve(): Promise<ModuleInstance[]>
186
- async resolve<T extends ModuleInstance = ModuleInstance>(all: '*', options?: ModuleFilterOptions<T>): Promise<T[]>
187
- async resolve<T extends ModuleInstance = ModuleInstance>(id: ModuleIdentifier, options?: ModuleFilterOptions<T>): Promise<T | undefined>
188
- async resolve<T extends ModuleInstance = ModuleInstance>(
189
- id: ModuleIdentifier = '*',
190
- options: ModuleFilterOptions<T> = {},
191
- ): Promise<T | T[] | undefined> {
192
- if (AbstractModuleInstance.useNewResolver) {
193
- if (id === '*') {
194
- const { maxDepth = 10, direction } = options
195
- if (direction === 'down') {
196
- return filterIdentity<T>((await resolveAllDown(this, maxDepth)) ?? [], options.identity)
197
- }
198
- if (direction === 'up') {
199
- return filterIdentity<T>(await resolveAllUp(this, maxDepth) ?? [], options.identity)
200
- }
201
- return filterIdentity<T>(await resolveAll(this, maxDepth) ?? [], options.identity)
202
- } else if (typeof id === 'string') {
203
- return filterIdentity<T>(await resolvePathToInstance(this, id, true), options.identity)
204
- } else {
205
- throw new TypeError('Invalid id type')
206
- }
207
- } else {
208
- const config: ResolveHelperConfig = {
209
- address: this.address,
210
- dead: this.dead,
211
- downResolver: this.downResolver,
212
- logger: this.logger,
213
- mod: this,
214
- transformers: this.moduleIdentifierTransformers,
215
- upResolver: this.upResolver,
216
- }
217
- if (id === '*') {
218
- return filterIdentity<T>(await ResolveHelper.resolve(config, '*', options) ?? [], options.identity)
219
- }
220
- return filterIdentity<T>(await ResolveHelper.resolve(config, id, options), options.identity)
221
- }
222
- }
223
-
224
- resolveIdentifier(id: ModuleIdentifier, options?: ObjectFilterOptions): Promise<Address | undefined> {
225
- const { direction = 'all' } = options ?? {}
226
- switch (direction) {
227
- case 'down': {
228
- return this.downResolver.resolveIdentifier(id, options)
229
- }
230
- default: {
231
- const mutatedOptions = { ...options, direction: 'all' } as ObjectFilterOptions
232
- return this.upResolver.resolveIdentifier(id, mutatedOptions)
233
- }
234
- }
235
- }
236
-
237
- async resolvePrivate<T extends ModuleInstance = ModuleInstance>(all: '*', options?: ModuleFilterOptions<T>): Promise<T[]>
238
- async resolvePrivate<T extends ModuleInstance = ModuleInstance>(id: ModuleIdentifier, options?: ModuleFilterOptions<T>): Promise<T | undefined>
239
- async resolvePrivate<T extends ModuleInstance = ModuleInstance>(
240
- id: ModuleIdentifier = '*',
241
- options: ModuleFilterOptions<T> = {},
242
- ): Promise<T | T[] | undefined> {
243
- return (
244
- (await this.privateResolver.resolve(id, options))
245
- ?? (await this.upResolver.resolve(id, options))
246
- ?? (await this.downResolver.resolve(id, options))
247
- )
248
- }
249
-
250
- async siblings(): Promise<ModuleInstance[]> {
251
- return (await Promise.all((await this.parents()).map(parent => parent.publicChildren()))).flat().filter(duplicateModules)
252
- }
253
-
254
- state() {
255
- this._checkDead()
256
- return this.busy(async () => {
257
- return await this.stateHandler()
258
- })
259
- }
260
-
261
- async stateQuery(account: AccountInstance): Promise<ModuleQueryResult> {
262
- const queryPayload: ModuleStateQuery = { schema: ModuleStateQuerySchema }
263
- return await this.sendQueryRaw(queryPayload, undefined, account)
264
- }
265
-
266
- subscribe(_queryAccount?: AccountInstance) {
267
- this._checkDead()
268
- return this.subscribeHandler()
269
- }
270
-
271
- protected override async manifestHandler(maxDepth: number = 1, _ignoreAddresses: Address[] = []): Promise<ModuleManifestPayload> {
272
- const cachedResult = this._cachedManifests.get(maxDepth)
273
- if (cachedResult) {
274
- return cachedResult
275
- }
276
- const modName = this.modName ?? '<Anonymous>'
277
- const children = await this.publicChildren()
278
- const childAddressToName: Record<Address, ModuleName | null> = {}
279
- for (const child of children) {
280
- if (child.address !== this.address) {
281
- childAddressToName[child.address] = child.modName ?? null
282
- }
283
- }
284
- const result = {
285
- config: { name: modName, ...this.config },
286
- name: modName,
287
- schema: ModuleManifestPayloadSchema,
288
- status: { address: this.address, children: childAddressToName },
289
- }
290
- this._cachedManifests.set(maxDepth, result)
291
- return result
292
- }
293
-
294
- protected async resolveArchivingArchivists(): Promise<ArchivistInstance[]> {
295
- const archivists = this.archiving?.archivists
296
- if (!archivists) return []
297
- const resolved = await Promise.all(archivists.map(archivist => this.resolve(archivist)))
298
- return (resolved.map(mod => asArchivistInstance(mod))).filter(exists)
299
- }
300
-
301
- protected async sendQuery<T extends Query, P extends Payload = Payload, R extends Payload = Payload>(
302
- queryPayload: T,
303
- payloads?: P[],
304
- account?: AccountInstance,
305
- ): Promise<R[]> {
306
- const queryResults = await this.sendQueryRaw(queryPayload, payloads, account)
307
- const [, resultPayloads, errors] = queryResults
308
-
309
- /* TODO: Figure out what to do with the returning BW. Should we store them in a queue in case the caller wants to see them? */
310
-
311
- if (isDefined(errors) && errors.length > 0) {
312
- /* TODO: Figure out how to rollup multiple Errors */
313
- throw errors[0]
314
- }
315
-
316
- return resultPayloads as R[]
317
- }
318
-
319
- protected async sendQueryRaw<T extends Query, P extends Payload = Payload, R extends Payload = Payload>(
320
- queryPayload: T,
321
- payloads?: P[],
322
- account?: AccountInstance,
323
- ): Promise<ModuleQueryResult<R>> {
324
- // Bind them
325
- const query = await this.bindQuery(queryPayload, payloads, account, this.additionalSigners)
326
-
327
- // Send them off
328
- return (await this.query(query[0], query[1])) as ModuleQueryResult<R>
329
- }
330
-
331
- protected override startHandler() {
332
- this._checkDead()
333
- return this.busy(async () => {
334
- if (this.status === 'started' || this.status === 'creating') {
335
- return
336
- }
337
- await super.startHandler()
338
- })
339
- }
340
-
341
- protected async storeToArchivists(payloads: Payload[]): Promise<Payload[]> {
342
- try {
343
- const archivists = await this.resolveArchivingArchivists()
344
- return (
345
- await Promise.all(
346
- archivists.map((archivist) => {
347
- return archivist.insert?.(payloads)
348
- }),
349
- )
350
- ).map(([bw]) => bw)
351
- } catch (ex) {
352
- const error = ex as Error
353
- this.logger?.error(`Error storing to archivists: ${error.message}`)
354
- return []
355
- }
356
- }
357
- }
package/src/Error.ts DELETED
@@ -1,44 +0,0 @@
1
- import type { Hash, JsonValue } from '@xylabs/sdk-js'
2
- import { PayloadBuilder } from '@xyo-network/payload-builder'
3
- import type { ModuleError, Schema } from '@xyo-network/payload-model'
4
- import { ModuleErrorSchema } from '@xyo-network/payload-model'
5
-
6
- export class ModuleErrorBuilder extends PayloadBuilder<ModuleError> {
7
- _details?: JsonValue
8
- _message?: string
9
- _name?: string
10
- _query?: Hash | Schema
11
- constructor() {
12
- super({ schema: ModuleErrorSchema })
13
- }
14
-
15
- override build(): ModuleError {
16
- this.fields({
17
- details: this._details,
18
- message: this._message,
19
- name: this._name,
20
- query: this._query,
21
- })
22
- return super.build()
23
- }
24
-
25
- details(details?: JsonValue) {
26
- this._details = details
27
- return this
28
- }
29
-
30
- message(message: string) {
31
- this._message = message
32
- return this
33
- }
34
-
35
- name(name: string) {
36
- this._name = name
37
- return this
38
- }
39
-
40
- query(query: Hash | Schema) {
41
- this._query = query
42
- return this
43
- }
44
- }
@@ -1,21 +0,0 @@
1
- import type {
2
- CreatableName, CreatableStatus, Logger,
3
- } from '@xylabs/sdk-js'
4
- import type { ModuleStatusReporter } from '@xyo-network/module-model'
5
-
6
- export class LoggerModuleStatusReporter implements ModuleStatusReporter {
7
- protected logger: Logger
8
-
9
- protected statusMap: Record<CreatableName, CreatableStatus> = {}
10
-
11
- constructor(logger: Logger) {
12
- this.logger = logger
13
- }
14
-
15
- report(name: CreatableName, status: CreatableStatus, progress?: number | Error): void {
16
- this.statusMap[name] = status
17
- const starting = (Object.entries(this.statusMap).map(([, value]): number => value === 'starting' ? 1 : 0)).reduce((a, b) => a + b, 0)
18
- const started = (Object.entries(this.statusMap).map(([, value]): number => value === 'started' ? 1 : 0)).reduce((a, b) => a + b, 0)
19
- this.logger.log(`${started}/${starting + started} ${name} status: ${status}`, { progress })
20
- }
21
- }
@@ -1,75 +0,0 @@
1
- import type { Address } from '@xylabs/sdk-js'
2
- import { QueryBoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
3
- import type {
4
- AnyConfigSchema, CosigningAddressSet, ModuleConfig, ModuleQueries,
5
- } from '@xyo-network/module-model'
6
- import { asSchema, type Schema } from '@xyo-network/payload-model'
7
-
8
- import type { Queryable, QueryValidator } from './QueryValidator.ts'
9
-
10
- export type SortedPipedAddressesString = string
11
-
12
- const delimiter = ''
13
-
14
- export class ModuleConfigQueryValidator<TConfig extends AnyConfigSchema<ModuleConfig>> implements QueryValidator {
15
- protected allowed: Record<Schema, SortedPipedAddressesString[]> = {}
16
- protected disallowed: Record<Schema, SortedPipedAddressesString[]> = {}
17
- protected readonly hasAllowedRules: boolean
18
- protected readonly hasDisallowedRules: boolean
19
- protected readonly hasRules: boolean
20
-
21
- constructor(config?: TConfig) {
22
- if (config?.security?.allowed) {
23
- for (const [schema, addresses] of Object.entries(config.security?.allowed)) {
24
- const typedSchema = asSchema(schema, true)
25
- this.allowed[typedSchema] = addresses.map(toAddressesString)
26
- }
27
- }
28
- if (config?.security?.disallowed) {
29
- for (const [schema, addresses] of Object.entries(config.security?.disallowed)) {
30
- const typedSchema = asSchema(schema, true)
31
- this.disallowed[typedSchema] = addresses.map(toAddressesString)
32
- }
33
- }
34
- this.hasAllowedRules = Object.keys(this.allowed).length > 0
35
- this.hasDisallowedRules = Object.keys(this.disallowed).length > 0
36
- this.hasRules = this.hasAllowedRules || this.hasDisallowedRules
37
- }
38
-
39
- queryable: Queryable = async (query, payloads) => {
40
- if (!this.hasRules) return true
41
- const addresses = query.addresses
42
- if (addresses.length === 0) return false
43
- const wrapper = QueryBoundWitnessWrapper.parseQuery<ModuleQueries>(query, payloads)
44
- const schema = (await wrapper.getQuery()).schema
45
- return this.queryAllowed(schema, addresses) && !this.queryDisallowed(schema, addresses)
46
- }
47
-
48
- protected queryAllowed = (schema: Schema, addresses: Address[]): boolean => {
49
- if (!this.hasAllowedRules) return true
50
- // All cosigners must sign
51
- if (addresses.length > 1) {
52
- const signatories = toAddressesString(addresses)
53
- const validCosigners = this.allowed?.[schema]?.includes(signatories)
54
- if (validCosigners) return true
55
- }
56
- // OR all signers have to be allowed individually
57
- return addresses.every(address => this.allowed?.[schema]?.includes(address) || false)
58
- }
59
-
60
- protected queryDisallowed = (schema: Schema, addresses: string[]): boolean => {
61
- if (!this.hasDisallowedRules) return false
62
- return addresses.some(address => this.disallowed?.[schema]?.includes(address))
63
- }
64
- }
65
-
66
- // TODO: Handle 0x prefix
67
- const toAddressesString = (addresses: string | CosigningAddressSet): SortedPipedAddressesString => {
68
- return Array.isArray(addresses)
69
- ? addresses
70
- // eslint-disable-next-line sonarjs/no-alphabetical-sort
71
- .toSorted()
72
- .map(address => address.toLowerCase())
73
- .join(delimiter)
74
- : addresses.toLowerCase()
75
- }
@@ -1,9 +0,0 @@
1
- import type { Promisable } from '@xylabs/sdk-js'
2
- import type { QueryBoundWitness } from '@xyo-network/boundwitness-model'
3
- import type { Payload } from '@xyo-network/payload-model'
4
-
5
- export type Queryable<T extends QueryBoundWitness = QueryBoundWitness> = (query: T, payloads?: Payload[]) => Promisable<boolean>
6
-
7
- export interface QueryValidator<T extends QueryBoundWitness = QueryBoundWitness> {
8
- queryable: Queryable<T>
9
- }
@@ -1,27 +0,0 @@
1
- import type { QueryBoundWitness } from '@xyo-network/boundwitness-model'
2
- import { QueryBoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
3
- import type { ModuleQueries, QueryableModule } from '@xyo-network/module-model'
4
- import type { Payload } from '@xyo-network/payload-model'
5
-
6
- import type { Queryable, QueryValidator } from './QueryValidator.ts'
7
-
8
- export const isQuerySupportedByModule = async <T extends QueryBoundWitness = QueryBoundWitness>(
9
- mod: QueryableModule,
10
- query: T,
11
- payloads?: Payload[],
12
- ): Promise<boolean> => {
13
- const wrapper = QueryBoundWitnessWrapper.parseQuery<ModuleQueries>(query, payloads)
14
- const schema = (await wrapper.getQuery()).schema
15
- return mod.queries.includes(schema)
16
- }
17
-
18
- export class SupportedQueryValidator implements QueryValidator {
19
- protected readonly mod: QueryableModule
20
- constructor(mod: QueryableModule) {
21
- this.mod = mod
22
- }
23
-
24
- queryable: Queryable = (query, payloads) => {
25
- return isQuerySupportedByModule(this.mod, query, payloads)
26
- }
27
- }
@@ -1,3 +0,0 @@
1
- export * from './ModuleConfigQueryValidator.ts'
2
- export * from './QueryValidator.ts'
3
- export * from './SupportedQueryValidator.ts'
@@ -1,53 +0,0 @@
1
- import {
2
- assertEx,
3
- isDefined, isString, isUndefined,
4
- } from '@xylabs/sdk-js'
5
- import { Account } from '@xyo-network/account'
6
- import type { AccountInstance } from '@xyo-network/account-model'
7
- import type { WalletInstance } from '@xyo-network/wallet-model'
8
-
9
- export interface DetermineAccountFromAccountParams {
10
- account: AccountInstance | 'random'
11
- accountPath?: never
12
- wallet?: never
13
- }
14
-
15
- export interface DetermineAccountFromWalletParams {
16
- account?: never
17
- accountPath?: string
18
- wallet: WalletInstance
19
- }
20
-
21
- export interface DetermineRandomParams {}
22
-
23
- export type DetermineAccountParams = DetermineAccountFromAccountParams | DetermineAccountFromWalletParams | DetermineRandomParams
24
-
25
- const isDetermineAccountFromAccountParams = (params: DetermineAccountParams): params is DetermineAccountFromAccountParams => {
26
- assertEx(isUndefined((params as DetermineAccountFromWalletParams).accountPath), () => 'accountPath may not be provided when account is provided')
27
- return isDefined((params as DetermineAccountFromAccountParams).account)
28
- }
29
-
30
- const isDetermineAccountFromWalletParams = (params: DetermineAccountParams): params is DetermineAccountFromWalletParams => {
31
- return isDefined((params as DetermineAccountFromWalletParams).wallet)
32
- }
33
-
34
- export async function determineAccount(params: DetermineAccountParams, allowRandomAccount = true): Promise<AccountInstance> {
35
- if (isDetermineAccountFromAccountParams(params)) {
36
- if (params.account === 'random') {
37
- assertEx(allowRandomAccount, () => 'Random address not allowed')
38
- return await Account.random()
39
- }
40
- return params.account
41
- }
42
-
43
- if (isDetermineAccountFromWalletParams(params)) {
44
- return assertEx(
45
- isString(params.accountPath) ? await params.wallet.derivePath(params.accountPath) : params.wallet,
46
- () => 'Failed to derive account from path',
47
- )
48
- }
49
-
50
- // this should eventually be removed/thrown
51
- console.warn('AbstractModule.determineAccount: No account or wallet provided - Creating Random account')
52
- return await Account.random()
53
- }
package/src/index.ts DELETED
@@ -1,5 +0,0 @@
1
- export * from './AbstractModule.ts'
2
- export * from './AbstractModuleInstance.ts'
3
- export * from './Error.ts'
4
- export * from './LoggerModuleStatusReporter.ts'
5
- export * from './QueryValidator/index.ts'