@xyo-network/module-abstract 2.66.9 → 2.67.0-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/cjs/AbstractModule.js +473 -36
  2. package/dist/cjs/AbstractModule.js.map +1 -1
  3. package/dist/cjs/AbstractModuleInstance.js +40 -0
  4. package/dist/cjs/AbstractModuleInstance.js.map +1 -0
  5. package/dist/cjs/Resolver/SimpleModuleResolver.js +1 -1
  6. package/dist/cjs/Resolver/SimpleModuleResolver.js.map +1 -1
  7. package/dist/cjs/index.js +1 -1
  8. package/dist/cjs/index.js.map +1 -1
  9. package/dist/esm/AbstractModule.js +437 -22
  10. package/dist/esm/AbstractModule.js.map +1 -1
  11. package/dist/esm/AbstractModuleInstance.js +35 -0
  12. package/dist/esm/AbstractModuleInstance.js.map +1 -0
  13. package/dist/esm/Resolver/SimpleModuleResolver.js +1 -1
  14. package/dist/esm/Resolver/SimpleModuleResolver.js.map +1 -1
  15. package/dist/esm/index.js +1 -1
  16. package/dist/esm/index.js.map +1 -1
  17. package/dist/types/AbstractModule.d.ts +69 -11
  18. package/dist/types/AbstractModule.d.ts.map +1 -1
  19. package/dist/types/AbstractModuleInstance.d.ts +14 -0
  20. package/dist/types/AbstractModuleInstance.d.ts.map +1 -0
  21. package/dist/types/Resolver/SimpleModuleResolver.d.ts.map +1 -1
  22. package/dist/types/index.d.ts +1 -1
  23. package/dist/types/index.d.ts.map +1 -1
  24. package/package.json +21 -21
  25. package/src/AbstractModule.ts +577 -27
  26. package/src/AbstractModuleInstance.ts +59 -0
  27. package/src/Resolver/SimpleModuleResolver.ts +11 -10
  28. package/src/index.ts +1 -1
  29. package/dist/cjs/AbstractIndirectModule.js +0 -492
  30. package/dist/cjs/AbstractIndirectModule.js.map +0 -1
  31. package/dist/docs.json +0 -36195
  32. package/dist/esm/AbstractIndirectModule.js +0 -446
  33. package/dist/esm/AbstractIndirectModule.js.map +0 -1
  34. package/dist/types/AbstractIndirectModule.d.ts +0 -76
  35. package/dist/types/AbstractIndirectModule.d.ts.map +0 -1
  36. package/src/AbstractIndirectModule.ts +0 -579
@@ -1,579 +0,0 @@
1
- /* eslint-disable max-lines */
2
- import { assertEx } from '@xylabs/assert'
3
- import { exists } from '@xylabs/exists'
4
- import { HDWallet } from '@xyo-network/account'
5
- import { AccountInstance } from '@xyo-network/account-model'
6
- import { AddressPayload, AddressSchema } from '@xyo-network/address-payload-plugin'
7
- import { ArchivistInstance, asArchivistInstance } from '@xyo-network/archivist-model'
8
- import { BoundWitnessBuilder, QueryBoundWitness, QueryBoundWitnessBuilder, QueryBoundWitnessWrapper } from '@xyo-network/boundwitness-builder'
9
- import { BoundWitness } from '@xyo-network/boundwitness-model'
10
- import { ConfigPayload, ConfigSchema } from '@xyo-network/config-payload-plugin'
11
- import { handleError, handleErrorAsync } from '@xyo-network/error'
12
- import { ModuleManifestPayload, ModuleManifestPayloadSchema } from '@xyo-network/manifest-model'
13
- import {
14
- AccountModuleParams,
15
- AddressPreviousHashPayload,
16
- AddressPreviousHashSchema,
17
- CreatableModule,
18
- CreatableModuleFactory,
19
- duplicateModules,
20
- IndividualArchivistConfig,
21
- Module,
22
- ModuleAddressQuerySchema,
23
- ModuleBusyEventArgs,
24
- ModuleConfig,
25
- ModuleDescribeQuerySchema,
26
- ModuleDescription,
27
- ModuleDescriptionPayload,
28
- ModuleDescriptionSchema,
29
- ModuleDiscoverQuerySchema,
30
- ModuleEventData,
31
- ModuleFactory,
32
- ModuleFilter,
33
- ModuleFilterOptions,
34
- ModuleManifestQuerySchema,
35
- ModuleParams,
36
- ModuleQueriedEventArgs,
37
- ModuleQuery,
38
- ModuleQueryBase,
39
- ModuleQueryResult,
40
- ModuleSubscribeQuerySchema,
41
- SchemaString,
42
- serializableField,
43
- WalletModuleParams,
44
- } from '@xyo-network/module-model'
45
- import { PayloadBuilder } from '@xyo-network/payload-builder'
46
- import { ModuleError, Payload, Query } from '@xyo-network/payload-model'
47
- import { PayloadWrapper } from '@xyo-network/payload-wrapper'
48
- import { Promisable, PromiseEx } from '@xyo-network/promise'
49
- import { QueryPayload, QuerySchema } from '@xyo-network/query-payload-plugin'
50
- import { IdLogger } from '@xyo-network/shared'
51
- import compact from 'lodash/compact'
52
-
53
- import { BaseEmitter } from './BaseEmitter'
54
- import { ModuleErrorBuilder } from './Error'
55
- import { ModuleConfigQueryValidator, Queryable, SupportedQueryValidator } from './QueryValidator'
56
- import { CompositeModuleResolver } from './Resolver'
57
-
58
- /** @description Abstract class for modules that allow access only through querying and not through direct calls */
59
- export abstract class AbstractIndirectModule<TParams extends ModuleParams = ModuleParams, TEventData extends ModuleEventData = ModuleEventData>
60
- extends BaseEmitter<TParams, TEventData>
61
- implements Module<TParams, TEventData>, Module
62
- {
63
- static configSchemas: string[]
64
- static enableBusy = false
65
- static enableLazyLoad = false
66
-
67
- protected static privateConstructorKey = Date.now().toString()
68
-
69
- readonly downResolver = new CompositeModuleResolver()
70
- readonly upResolver = new CompositeModuleResolver()
71
-
72
- protected _account: AccountInstance | undefined = undefined
73
- protected readonly _baseModuleQueryAccountPaths: Record<ModuleQueryBase['schema'], string> = {
74
- [ModuleAddressQuerySchema]: '1',
75
- [ModuleDescribeQuerySchema]: '4',
76
- [ModuleDiscoverQuerySchema]: '2',
77
- [ModuleManifestQuerySchema]: '5',
78
- [ModuleSubscribeQuerySchema]: '3',
79
- }
80
- protected readonly _queryAccounts: Record<ModuleQueryBase['schema'], AccountInstance | undefined> = {
81
- [ModuleAddressQuerySchema]: undefined,
82
- [ModuleDescribeQuerySchema]: undefined,
83
- [ModuleDiscoverQuerySchema]: undefined,
84
- [ModuleManifestQuerySchema]: undefined,
85
- [ModuleSubscribeQuerySchema]: undefined,
86
- }
87
- protected _startPromise: Promisable<boolean> | undefined = undefined
88
- protected _started: Promisable<boolean> | undefined = undefined
89
- protected readonly moduleConfigQueryValidator: Queryable
90
- protected readonly supportedQueryValidator: Queryable
91
-
92
- private _busyCount = 0
93
-
94
- constructor(privateConstructorKey: string, params: TParams) {
95
- assertEx(AbstractIndirectModule.privateConstructorKey === privateConstructorKey, 'Use create function instead of constructor')
96
- // Clone params to prevent mutation of the incoming object
97
- const mutatedParams = { ...params } as TParams
98
- super(mutatedParams)
99
-
100
- this.supportedQueryValidator = new SupportedQueryValidator(this as Module).queryable
101
- this.moduleConfigQueryValidator = new ModuleConfigQueryValidator(mutatedParams?.config).queryable
102
- }
103
-
104
- static get configSchema(): string {
105
- return this.configSchemas[0]
106
- }
107
-
108
- get account() {
109
- return assertEx(this._account, 'Missing account')
110
- }
111
-
112
- get address() {
113
- return this.account.address
114
- }
115
-
116
- get allowAnonymous() {
117
- return !!this.config.security?.allowAnonymous
118
- }
119
-
120
- get config(): TParams['config'] {
121
- return this.params.config
122
- }
123
-
124
- get ephemeralQueryAccountEnabled(): boolean {
125
- return !!this.params.ephemeralQueryAccountEnabled
126
- }
127
-
128
- get queries(): string[] {
129
- return [ModuleDiscoverQuerySchema, ModuleAddressQuerySchema, ModuleSubscribeQuerySchema]
130
- }
131
-
132
- get queryAccountPaths(): Readonly<Record<Query['schema'], string | undefined>> {
133
- return { ...this._baseModuleQueryAccountPaths, ...this._queryAccountPaths }
134
- }
135
-
136
- get queryAccounts(): Readonly<Record<Query['schema'], AccountInstance | undefined>> {
137
- return this._queryAccounts
138
- }
139
-
140
- protected abstract get _queryAccountPaths(): Record<Query['schema'], string>
141
-
142
- static async create<TModule extends Module>(this: CreatableModule<TModule>, params?: TModule['params']) {
143
- if (!this.configSchemas || this.configSchemas.length === 0) {
144
- throw Error(`Missing configSchema [${params?.config?.schema}][${this.name}]`)
145
- }
146
- const schema: string = params?.config?.schema ?? this.configSchema
147
- const allowedSchemas: string[] = this.configSchemas
148
-
149
- assertEx(
150
- allowedSchemas.filter((allowedSchema) => allowedSchema === schema).length > 0,
151
- `Bad Config Schema [Received ${schema}] [Expected ${JSON.stringify(allowedSchemas)}]`,
152
- )
153
- const mutatedConfig: TModule['params']['config'] = { ...params?.config, schema }
154
- params?.logger?.debug(`config: ${JSON.stringify(mutatedConfig, null, 2)}`)
155
- const mutatedParams = { ...params, config: mutatedConfig } as TModule['params']
156
- const newModule = new this(AbstractIndirectModule.privateConstructorKey, mutatedParams)
157
- await newModule.loadAccount?.()
158
- if (!AbstractIndirectModule.enableLazyLoad) {
159
- await newModule.start?.()
160
- }
161
- return newModule
162
- }
163
-
164
- static factory<TModule extends Module>(this: CreatableModule<TModule>, params?: TModule['params']): CreatableModuleFactory<TModule> {
165
- return ModuleFactory.withParams(this, params)
166
- }
167
-
168
- async busy<R>(closure: () => Promise<R>) {
169
- if (AbstractIndirectModule.enableBusy) {
170
- if (this._busyCount <= 0) {
171
- this._busyCount = 0
172
- const args: ModuleBusyEventArgs = { busy: true, module: this }
173
- await this.emit('moduleBusy', args)
174
- }
175
- this._busyCount++
176
- try {
177
- return await closure()
178
- } finally {
179
- this._busyCount--
180
- if (this._busyCount <= 0) {
181
- this._busyCount = 0
182
- const args: ModuleBusyEventArgs = { busy: false, module: this }
183
- await this.emit('moduleBusy', args)
184
- }
185
- }
186
- } else {
187
- return closure()
188
- }
189
- }
190
-
191
- async loadAccount() {
192
- if (!this._account) {
193
- const activeLogger = this.params.logger ?? AbstractIndirectModule.defaultLogger
194
- let { account } = this.params as AccountModuleParams<TParams['config']>
195
- const { wallet, accountDerivationPath } = this.params as WalletModuleParams<TParams['config']>
196
- if (wallet) {
197
- account = assertEx(accountDerivationPath ? await wallet.derivePath(accountDerivationPath) : wallet, 'Failed to derive account from path')
198
- }
199
- this.params.logger = activeLogger ? new IdLogger(activeLogger, () => `0x${account.address}`) : undefined
200
- if (!account) console.warn(`AbstractModule.loadAccount: No account provided - Creating Random account [${this.config.schema}]`)
201
- this._account = account ?? (await HDWallet.random())
202
- }
203
- return this._account
204
- }
205
-
206
- previousHash(): Promisable<string | undefined> {
207
- return this.account.previousHash
208
- }
209
-
210
- async query<T extends QueryBoundWitness = QueryBoundWitness, TConfig extends ModuleConfig = ModuleConfig>(
211
- query: T,
212
- payloads?: Payload[],
213
- queryConfig?: TConfig,
214
- ): Promise<ModuleQueryResult> {
215
- return await this.busy(async () => {
216
- await this.started('throw')
217
- const result = await this.queryHandler(assertEx(QueryBoundWitnessWrapper.unwrap(query)), payloads, queryConfig)
218
-
219
- const args: ModuleQueriedEventArgs = { module: this, payloads, query, result }
220
- await this.emit('moduleQueried', args)
221
-
222
- return result
223
- })
224
- }
225
-
226
- queryable<T extends QueryBoundWitness = QueryBoundWitness, TConfig extends ModuleConfig = ModuleConfig>(
227
- query: T,
228
- payloads?: Payload[],
229
- queryConfig?: TConfig,
230
- ): boolean {
231
- if (!this.started('warn')) return false
232
- const configValidator = queryConfig
233
- ? new ModuleConfigQueryValidator(Object.assign({}, this.config, queryConfig)).queryable
234
- : this.moduleConfigQueryValidator
235
- const validators = [this.supportedQueryValidator, configValidator]
236
-
237
- return validators.every((validator) => validator(query, payloads))
238
- }
239
-
240
- async resolve(filter?: ModuleFilter, options?: ModuleFilterOptions): Promise<Module[]>
241
- async resolve(nameOrAddress: string, options?: ModuleFilterOptions): Promise<Module | undefined>
242
- async resolve(nameOrAddressOrFilter?: ModuleFilter | string, options?: ModuleFilterOptions): Promise<Module | Module[] | undefined> {
243
- const direction = options?.direction ?? 'all'
244
- const up = direction === 'up' || direction === 'all'
245
- const down = direction === 'down' || direction === 'all'
246
- switch (typeof nameOrAddressOrFilter) {
247
- case 'string': {
248
- return (
249
- (down ? await this.downResolver.resolve(nameOrAddressOrFilter) : undefined) ??
250
- (up ? await this.upResolver.resolve(nameOrAddressOrFilter) : undefined)
251
- )
252
- }
253
- default: {
254
- const filter: ModuleFilter | undefined = nameOrAddressOrFilter
255
- return [...(down ? await this.downResolver.resolve(filter) : []), ...(up ? await this.upResolver.resolve(filter) : [])].filter(
256
- duplicateModules,
257
- )
258
- }
259
- }
260
- }
261
-
262
- start(_timeout?: number): Promisable<boolean> {
263
- //using promise as mutex
264
- this._startPromise = this._startPromise ?? this.startHandler()
265
- return this._startPromise
266
- }
267
-
268
- async started(notStartedAction: 'error' | 'throw' | 'warn' | 'log' | 'none' = 'log', tryStart = true): Promise<boolean> {
269
- const started = await this._started
270
- if (started === true) {
271
- return true
272
- }
273
- if (!started) {
274
- //using promise as mutex
275
- this._started = (async () => {
276
- if (tryStart) {
277
- try {
278
- await this.start()
279
- return true
280
- } catch (ex) {
281
- handleError(ex, (error) => {
282
- this.logger?.warn(`Autostart of Module Failed: ${error.message})`)
283
- this._started = undefined
284
- })
285
- }
286
- }
287
- switch (notStartedAction) {
288
- case 'throw':
289
- throw Error(`Module not Started [${this.address}]`)
290
- case 'warn':
291
- this.logger?.warn('Module not started')
292
- break
293
- case 'error':
294
- this.logger?.error('Module not started')
295
- break
296
- case 'none':
297
- break
298
- case 'log':
299
- default: {
300
- this.logger?.log('Module not started')
301
- break
302
- }
303
- }
304
- return false
305
- })()
306
- }
307
- if (!this._started) {
308
- throw 'Failed to create start promise'
309
- }
310
- return await this._started
311
- }
312
-
313
- async stop(_timeout?: number): Promise<boolean> {
314
- return await this.busy(async () => {
315
- const result = await this.stopHandler()
316
- this._started = undefined
317
- this._startPromise = undefined
318
- return result
319
- })
320
- }
321
-
322
- protected bindHashes(hashes: string[], schema: SchemaString[], account?: AccountInstance) {
323
- const promise = new PromiseEx((resolve) => {
324
- const result = this.bindHashesInternal(hashes, schema, account)
325
- resolve?.(result)
326
- return result
327
- }, account)
328
- return promise
329
- }
330
-
331
- protected async bindHashesInternal(hashes: string[], schema: SchemaString[], account?: AccountInstance): Promise<BoundWitness> {
332
- const builder = new BoundWitnessBuilder().hashes(hashes, schema).witness(this.account)
333
- const result = (await (account ? builder.witness(account) : builder).build())[0]
334
- this.logger?.debug(`result: ${JSON.stringify(result, null, 2)}`)
335
- return result
336
- }
337
-
338
- protected bindQuery<T extends Query | PayloadWrapper<Query>>(
339
- query: T,
340
- payloads?: Payload[],
341
- account?: AccountInstance,
342
- ): PromiseEx<[QueryBoundWitness, Payload[], Payload[]], AccountInstance> {
343
- const promise = new PromiseEx<[QueryBoundWitness, Payload[], Payload[]], AccountInstance>(async (resolve) => {
344
- const result = await this.bindQueryInternal(query, payloads, account)
345
- resolve?.(result)
346
- return result
347
- }, account)
348
- return promise
349
- }
350
-
351
- protected async bindQueryInternal<T extends Query | PayloadWrapper<Query>>(
352
- query: T,
353
- payloads?: Payload[],
354
- account?: AccountInstance,
355
- ): Promise<[QueryBoundWitness, Payload[], Payload[]]> {
356
- const builder = new QueryBoundWitnessBuilder().payloads(payloads).witness(this.account).query(query)
357
- const result = await (account ? builder.witness(account) : builder).build()
358
- return result
359
- }
360
-
361
- protected async bindQueryResult<T extends Query | PayloadWrapper<Query>>(
362
- query: T,
363
- payloads: Payload[],
364
- additionalWitnesses: AccountInstance[] = [],
365
- errors?: ModuleError[],
366
- ): Promise<[ModuleQueryResult, AccountInstance[]]> {
367
- const builder = new BoundWitnessBuilder().payloads(payloads).errors(errors)
368
- const queryWitnessAccount = this.queryAccounts[query.schema as ModuleQueryBase['schema']]
369
- const witnesses = [this.account, queryWitnessAccount, ...additionalWitnesses].filter(exists)
370
- builder.witnesses(witnesses)
371
- const result: ModuleQueryResult = [(await builder.build())[0], payloads, errors ?? []]
372
- return [result, witnesses]
373
- }
374
-
375
- protected commitArchivist = () => this.getArchivist('commit')
376
-
377
- protected async describeHandler(): Promise<ModuleDescriptionPayload> {
378
- const description: ModuleDescriptionPayload = {
379
- address: this.address,
380
- queries: this.queries,
381
- schema: ModuleDescriptionSchema,
382
- }
383
- if (this.config.name) {
384
- description.name = this.config.name
385
- }
386
-
387
- const discover = await this.discoverHandler()
388
-
389
- description.children = compact(
390
- discover?.map((payload) => {
391
- const address = payload.schema === AddressSchema ? (payload as AddressPayload).address : undefined
392
- return address != this.address ? address : undefined
393
- }) ?? [],
394
- )
395
-
396
- return description
397
- }
398
-
399
- protected discoverHandler(): Promisable<Payload[]> {
400
- const config = this.config
401
- const address = new PayloadBuilder<AddressPayload>({ schema: AddressSchema }).fields({ address: this.address, name: this.config.name }).build()
402
- const queries = this.queries.map((query) => {
403
- return new PayloadBuilder<QueryPayload>({ schema: QuerySchema }).fields({ query }).build()
404
- })
405
- const configSchema: ConfigPayload = {
406
- config: config.schema,
407
- schema: ConfigSchema,
408
- }
409
- return compact([config, configSchema, address, ...queries])
410
- }
411
-
412
- protected async initializeQueryAccounts() {
413
- // Ensure distinct/unique wallet paths
414
- const paths = Object.values(this.queryAccountPaths).filter(exists)
415
- const distinctPaths = new Set<string>(paths)
416
- assertEx(distinctPaths.size === paths.length, `${this.config?.name ? this.config.name + ': ' : ''}Duplicate query account paths`)
417
- // Create an account for query this module supports
418
- const wallet = this.account as unknown as HDWallet
419
- if (wallet?.derivePath) {
420
- for (const key in this.queryAccountPaths) {
421
- if (Object.prototype.hasOwnProperty.call(this.queryAccountPaths, key)) {
422
- const query = key as ModuleQueryBase['schema']
423
- const queryAccountPath = this.queryAccountPaths[query]
424
- if (queryAccountPath) {
425
- this._queryAccounts[query] = await wallet.derivePath?.(queryAccountPath)
426
- }
427
- }
428
- }
429
- }
430
- }
431
-
432
- protected manifestHandler(): Promisable<ModuleManifestPayload> {
433
- const name = this.config.name ?? 'Anonymous'
434
- return { config: { name, ...this.config }, schema: ModuleManifestPayloadSchema }
435
- }
436
-
437
- protected moduleAddressHandler(): Promisable<AddressPreviousHashPayload[]> {
438
- // Return array of all addresses and their previous hash
439
- const queryAccounts = Object.entries(this.queryAccounts)
440
- .filter((value): value is [string, AccountInstance] => {
441
- return exists(value[1])
442
- })
443
- .map(([name, account]) => {
444
- const address = account.address
445
- const previousHash = account.previousHash
446
- return [
447
- { address, name, schema: AddressSchema },
448
- { address, previousHash, schema: AddressPreviousHashSchema },
449
- ]
450
- })
451
- const address = this.address
452
- const name = this.config.name
453
- const previousHash = this.address
454
- const moduleAccount = name ? { address, name, schema: AddressSchema } : { address, schema: AddressSchema }
455
- const moduleAccountPreviousHash = previousHash
456
- ? { address, previousHash, schema: AddressPreviousHashSchema }
457
- : { address, schema: AddressPreviousHashSchema }
458
- return [moduleAccount, moduleAccountPreviousHash, ...queryAccounts].flat()
459
- }
460
-
461
- protected async queryHandler<T extends QueryBoundWitness = QueryBoundWitness, TConfig extends ModuleConfig = ModuleConfig>(
462
- query: T,
463
- payloads?: Payload[],
464
- queryConfig?: TConfig,
465
- ): Promise<ModuleQueryResult> {
466
- await this.started('throw')
467
- const wrapper = QueryBoundWitnessWrapper.parseQuery<ModuleQuery>(query, payloads)
468
- if (!this.allowAnonymous) {
469
- if (query.addresses.length === 0) {
470
- console.warn(`Anonymous Queries not allowed, but running anyway [${this.config.name}], [${this.address}]`)
471
- }
472
- }
473
- const queryPayload = await wrapper.getQuery()
474
- assertEx(this.queryable(query, payloads, queryConfig))
475
- const resultPayloads: Payload[] = []
476
- const errorPayloads: ModuleError[] = []
477
- const queryAccount = this.ephemeralQueryAccountEnabled ? await HDWallet.random() : undefined
478
- try {
479
- switch (queryPayload.schema) {
480
- case ModuleManifestQuerySchema: {
481
- resultPayloads.push(await this.manifestHandler())
482
- break
483
- }
484
- case ModuleDiscoverQuerySchema: {
485
- resultPayloads.push(...(await this.discoverHandler()))
486
- break
487
- }
488
- case ModuleDescribeQuerySchema: {
489
- resultPayloads.push(await this.describeHandler())
490
- break
491
- }
492
- case ModuleAddressQuerySchema: {
493
- resultPayloads.push(...(await this.moduleAddressHandler()))
494
- break
495
- }
496
- case ModuleSubscribeQuerySchema: {
497
- this.subscribeHandler(queryAccount)
498
- break
499
- }
500
- default:
501
- console.error(`Unsupported Query [${(queryPayload as Payload).schema}]`)
502
- }
503
- } catch (ex) {
504
- await handleErrorAsync(ex, async (error) => {
505
- errorPayloads.push(
506
- new ModuleErrorBuilder()
507
- .sources([await wrapper.hashAsync()])
508
- .name(this.config.name ?? '<Unknown>')
509
- .query(query.schema)
510
- .message(error.message)
511
- .build(),
512
- )
513
- })
514
- }
515
- return (await this.bindQueryResult(queryPayload, resultPayloads, queryAccount ? [queryAccount] : [], errorPayloads))[0]
516
- }
517
-
518
- protected readArchivist = () => this.getArchivist('read')
519
-
520
- protected async startHandler(): Promise<boolean> {
521
- this.validateConfig()
522
- await this.initializeQueryAccounts()
523
- this._started = true
524
- return true
525
- }
526
-
527
- protected stopHandler(_timeout?: number): Promisable<boolean> {
528
- this._started = undefined
529
- return true
530
- }
531
-
532
- protected subscribeHandler(_queryAccount?: AccountInstance) {
533
- return
534
- }
535
-
536
- protected validateConfig(config?: unknown, parents: string[] = []): boolean {
537
- return Object.entries(config ?? this.config ?? {}).reduce((valid, [key, value]) => {
538
- switch (typeof value) {
539
- case 'function':
540
- this.logger?.warn(`Fields of type function not allowed in config [${parents?.join('.')}.${key}]`)
541
- return false
542
- case 'object': {
543
- if (Array.isArray(value)) {
544
- return (
545
- value.reduce((valid, value) => {
546
- return this.validateConfig(value, [...parents, key]) && valid
547
- }, true) && valid
548
- )
549
- }
550
-
551
- if (!serializableField(value)) {
552
- this.logger?.warn(`Fields that are not serializable to JSON are not allowed in config [${parents?.join('.')}.${key}]`)
553
- return false
554
- }
555
- return value ? this.validateConfig(value, [...parents, key]) && valid : true
556
- }
557
- default:
558
- return valid
559
- }
560
- }, true)
561
- }
562
-
563
- protected writeArchivist = () => this.getArchivist('write')
564
-
565
- private async getArchivist(kind: keyof IndividualArchivistConfig): Promise<ArchivistInstance | undefined> {
566
- if (!this.config.archivist) return undefined
567
- const filter =
568
- typeof this.config.archivist === 'string' || this.config.archivist instanceof String
569
- ? (this.config.archivist as string)
570
- : (this.config?.archivist?.[kind] as string)
571
- const resolved = await this.upResolver.resolve(filter)
572
- return asArchivistInstance(resolved)
573
- }
574
-
575
- abstract describe(): Promise<ModuleDescription>
576
- abstract discover(): Promisable<Payload[]>
577
- abstract manifest(): Promisable<ModuleManifestPayload>
578
- abstract moduleAddress(): Promisable<AddressPreviousHashPayload[]>
579
- }