@xyo-network/module-abstract 2.51.10

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 (151) hide show
  1. package/LICENSE +165 -0
  2. package/README.md +13 -0
  3. package/dist/cjs/AbstractModule.js +253 -0
  4. package/dist/cjs/AbstractModule.js.map +1 -0
  5. package/dist/cjs/BaseEmitter.js +40 -0
  6. package/dist/cjs/BaseEmitter.js.map +1 -0
  7. package/dist/cjs/Error.js +21 -0
  8. package/dist/cjs/Error.js.map +1 -0
  9. package/dist/cjs/IdLogger.js +38 -0
  10. package/dist/cjs/IdLogger.js.map +1 -0
  11. package/dist/cjs/ModuleWrapper.js +226 -0
  12. package/dist/cjs/ModuleWrapper.js.map +1 -0
  13. package/dist/cjs/Query/QueryBoundWitnessBuilder.js +25 -0
  14. package/dist/cjs/Query/QueryBoundWitnessBuilder.js.map +1 -0
  15. package/dist/cjs/Query/QueryBoundWitnessValidator.js +38 -0
  16. package/dist/cjs/Query/QueryBoundWitnessValidator.js.map +1 -0
  17. package/dist/cjs/Query/QueryBoundWitnessWrapper.js +47 -0
  18. package/dist/cjs/Query/QueryBoundWitnessWrapper.js.map +1 -0
  19. package/dist/cjs/Query/index.js +7 -0
  20. package/dist/cjs/Query/index.js.map +1 -0
  21. package/dist/cjs/QueryValidator/ModuleConfigQueryValidator.js +65 -0
  22. package/dist/cjs/QueryValidator/ModuleConfigQueryValidator.js.map +1 -0
  23. package/dist/cjs/QueryValidator/QueryValidator.js +3 -0
  24. package/dist/cjs/QueryValidator/QueryValidator.js.map +1 -0
  25. package/dist/cjs/QueryValidator/SupportedQueryValidator.js +20 -0
  26. package/dist/cjs/QueryValidator/SupportedQueryValidator.js.map +1 -0
  27. package/dist/cjs/QueryValidator/index.js +7 -0
  28. package/dist/cjs/QueryValidator/index.js.map +1 -0
  29. package/dist/cjs/Resolver/CompositeModuleResolver.js +66 -0
  30. package/dist/cjs/Resolver/CompositeModuleResolver.js.map +1 -0
  31. package/dist/cjs/Resolver/ResolverEventEmitter.js +38 -0
  32. package/dist/cjs/Resolver/ResolverEventEmitter.js.map +1 -0
  33. package/dist/cjs/Resolver/SimpleModuleResolver.js +91 -0
  34. package/dist/cjs/Resolver/SimpleModuleResolver.js.map +1 -0
  35. package/dist/cjs/Resolver/index.js +12 -0
  36. package/dist/cjs/Resolver/index.js.map +1 -0
  37. package/dist/cjs/index.js +12 -0
  38. package/dist/cjs/index.js.map +1 -0
  39. package/dist/cjs/lib/duplicateModules.js +17 -0
  40. package/dist/cjs/lib/duplicateModules.js.map +1 -0
  41. package/dist/cjs/lib/index.js +6 -0
  42. package/dist/cjs/lib/index.js.map +1 -0
  43. package/dist/cjs/lib/serializable.js +38 -0
  44. package/dist/cjs/lib/serializable.js.map +1 -0
  45. package/dist/docs.json +70567 -0
  46. package/dist/esm/AbstractModule.js +241 -0
  47. package/dist/esm/AbstractModule.js.map +1 -0
  48. package/dist/esm/BaseEmitter.js +37 -0
  49. package/dist/esm/BaseEmitter.js.map +1 -0
  50. package/dist/esm/Error.js +19 -0
  51. package/dist/esm/Error.js.map +1 -0
  52. package/dist/esm/IdLogger.js +31 -0
  53. package/dist/esm/IdLogger.js.map +1 -0
  54. package/dist/esm/ModuleWrapper.js +209 -0
  55. package/dist/esm/ModuleWrapper.js.map +1 -0
  56. package/dist/esm/Query/QueryBoundWitnessBuilder.js +22 -0
  57. package/dist/esm/Query/QueryBoundWitnessBuilder.js.map +1 -0
  58. package/dist/esm/Query/QueryBoundWitnessValidator.js +35 -0
  59. package/dist/esm/Query/QueryBoundWitnessValidator.js.map +1 -0
  60. package/dist/esm/Query/QueryBoundWitnessWrapper.js +41 -0
  61. package/dist/esm/Query/QueryBoundWitnessWrapper.js.map +1 -0
  62. package/dist/esm/Query/index.js +4 -0
  63. package/dist/esm/Query/index.js.map +1 -0
  64. package/dist/esm/QueryValidator/ModuleConfigQueryValidator.js +62 -0
  65. package/dist/esm/QueryValidator/ModuleConfigQueryValidator.js.map +1 -0
  66. package/dist/esm/QueryValidator/QueryValidator.js +2 -0
  67. package/dist/esm/QueryValidator/QueryValidator.js.map +1 -0
  68. package/dist/esm/QueryValidator/SupportedQueryValidator.js +16 -0
  69. package/dist/esm/QueryValidator/SupportedQueryValidator.js.map +1 -0
  70. package/dist/esm/QueryValidator/index.js +4 -0
  71. package/dist/esm/QueryValidator/index.js.map +1 -0
  72. package/dist/esm/Resolver/CompositeModuleResolver.js +60 -0
  73. package/dist/esm/Resolver/CompositeModuleResolver.js.map +1 -0
  74. package/dist/esm/Resolver/ResolverEventEmitter.js +33 -0
  75. package/dist/esm/Resolver/ResolverEventEmitter.js.map +1 -0
  76. package/dist/esm/Resolver/SimpleModuleResolver.js +84 -0
  77. package/dist/esm/Resolver/SimpleModuleResolver.js.map +1 -0
  78. package/dist/esm/Resolver/index.js +8 -0
  79. package/dist/esm/Resolver/index.js.map +1 -0
  80. package/dist/esm/index.js +9 -0
  81. package/dist/esm/index.js.map +1 -0
  82. package/dist/esm/lib/duplicateModules.js +13 -0
  83. package/dist/esm/lib/duplicateModules.js.map +1 -0
  84. package/dist/esm/lib/index.js +3 -0
  85. package/dist/esm/lib/index.js.map +1 -0
  86. package/dist/esm/lib/serializable.js +32 -0
  87. package/dist/esm/lib/serializable.js.map +1 -0
  88. package/dist/types/AbstractModule.d.ts +42 -0
  89. package/dist/types/AbstractModule.d.ts.map +1 -0
  90. package/dist/types/BaseEmitter.d.ts +16 -0
  91. package/dist/types/BaseEmitter.d.ts.map +1 -0
  92. package/dist/types/Error.d.ts +16 -0
  93. package/dist/types/Error.d.ts.map +1 -0
  94. package/dist/types/IdLogger.d.ts +14 -0
  95. package/dist/types/IdLogger.d.ts.map +1 -0
  96. package/dist/types/ModuleWrapper.d.ts +73 -0
  97. package/dist/types/ModuleWrapper.d.ts.map +1 -0
  98. package/dist/types/Query/QueryBoundWitnessBuilder.d.ts +12 -0
  99. package/dist/types/Query/QueryBoundWitnessBuilder.d.ts.map +1 -0
  100. package/dist/types/Query/QueryBoundWitnessValidator.d.ts +10 -0
  101. package/dist/types/Query/QueryBoundWitnessValidator.d.ts.map +1 -0
  102. package/dist/types/Query/QueryBoundWitnessWrapper.d.ts +16 -0
  103. package/dist/types/Query/QueryBoundWitnessWrapper.d.ts.map +1 -0
  104. package/dist/types/Query/index.d.ts +4 -0
  105. package/dist/types/Query/index.d.ts.map +1 -0
  106. package/dist/types/QueryValidator/ModuleConfigQueryValidator.d.ts +15 -0
  107. package/dist/types/QueryValidator/ModuleConfigQueryValidator.d.ts.map +1 -0
  108. package/dist/types/QueryValidator/QueryValidator.d.ts +7 -0
  109. package/dist/types/QueryValidator/QueryValidator.d.ts.map +1 -0
  110. package/dist/types/QueryValidator/SupportedQueryValidator.d.ts +18 -0
  111. package/dist/types/QueryValidator/SupportedQueryValidator.d.ts.map +1 -0
  112. package/dist/types/QueryValidator/index.d.ts +4 -0
  113. package/dist/types/QueryValidator/index.d.ts.map +1 -0
  114. package/dist/types/Resolver/CompositeModuleResolver.d.ts +16 -0
  115. package/dist/types/Resolver/CompositeModuleResolver.d.ts.map +1 -0
  116. package/dist/types/Resolver/ResolverEventEmitter.d.ts +10 -0
  117. package/dist/types/Resolver/ResolverEventEmitter.d.ts.map +1 -0
  118. package/dist/types/Resolver/SimpleModuleResolver.d.ts +19 -0
  119. package/dist/types/Resolver/SimpleModuleResolver.d.ts.map +1 -0
  120. package/dist/types/Resolver/index.d.ts +7 -0
  121. package/dist/types/Resolver/index.d.ts.map +1 -0
  122. package/dist/types/index.d.ts +9 -0
  123. package/dist/types/index.d.ts.map +1 -0
  124. package/dist/types/lib/duplicateModules.d.ts +32 -0
  125. package/dist/types/lib/duplicateModules.d.ts.map +1 -0
  126. package/dist/types/lib/index.d.ts +3 -0
  127. package/dist/types/lib/index.d.ts.map +1 -0
  128. package/dist/types/lib/serializable.d.ts +3 -0
  129. package/dist/types/lib/serializable.d.ts.map +1 -0
  130. package/package.json +73 -0
  131. package/src/AbstractModule.ts +307 -0
  132. package/src/BaseEmitter.ts +47 -0
  133. package/src/Error.ts +25 -0
  134. package/src/IdLogger.ts +37 -0
  135. package/src/ModuleWrapper.ts +330 -0
  136. package/src/Query/QueryBoundWitnessBuilder.ts +29 -0
  137. package/src/Query/QueryBoundWitnessValidator.ts +41 -0
  138. package/src/Query/QueryBoundWitnessWrapper.ts +55 -0
  139. package/src/Query/index.ts +3 -0
  140. package/src/QueryValidator/ModuleConfigQueryValidator.ts +67 -0
  141. package/src/QueryValidator/QueryValidator.ts +8 -0
  142. package/src/QueryValidator/SupportedQueryValidator.ts +22 -0
  143. package/src/QueryValidator/index.ts +3 -0
  144. package/src/Resolver/CompositeModuleResolver.ts +70 -0
  145. package/src/Resolver/ResolverEventEmitter.ts +46 -0
  146. package/src/Resolver/SimpleModuleResolver.ts +118 -0
  147. package/src/Resolver/index.ts +8 -0
  148. package/src/index.ts +8 -0
  149. package/src/lib/duplicateModules.ts +14 -0
  150. package/src/lib/index.ts +2 -0
  151. package/src/lib/serializable.ts +41 -0
@@ -0,0 +1,330 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import { Account } from '@xyo-network/account'
3
+ import { AccountInstance } from '@xyo-network/account-model'
4
+ import { AddressPayload, AddressSchema } from '@xyo-network/address-payload-plugin'
5
+ import { BoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
6
+ import { BaseParams } from '@xyo-network/core'
7
+ import { EventAnyListener, EventListener } from '@xyo-network/module-events'
8
+ import {
9
+ Module,
10
+ ModuleDescription,
11
+ ModuleDiscoverQuery,
12
+ ModuleDiscoverQuerySchema,
13
+ ModuleFilter,
14
+ ModuleQueryResult,
15
+ XyoQuery,
16
+ XyoQueryBoundWitness,
17
+ } from '@xyo-network/module-model'
18
+ import { XyoPayload, XyoPayloads } from '@xyo-network/payload-model'
19
+ import { PayloadWrapper } from '@xyo-network/payload-wrapper'
20
+ import { Promisable, PromiseEx } from '@xyo-network/promise'
21
+ import compact from 'lodash/compact'
22
+
23
+ import { BaseEmitter } from './BaseEmitter'
24
+ import { XyoError, XyoErrorSchema } from './Error'
25
+ import { duplicateModules } from './lib'
26
+ import { QueryBoundWitnessBuilder, QueryBoundWitnessWrapper } from './Query'
27
+
28
+ export interface WrapperError extends Error {
29
+ errors: (XyoError | null)[]
30
+ query: [XyoQueryBoundWitness, XyoPayloads]
31
+ result: ModuleQueryResult | undefined
32
+ }
33
+
34
+ export type ModuleConstructable<TModule extends Module = Module, TWrapper extends ModuleWrapper<TModule> = ModuleWrapper<TModule>> = {
35
+ new (params: ModuleWrapperParams): TWrapper
36
+ }
37
+
38
+ export function moduleConstructable<TModule extends Module = Module, TWrapper extends ModuleWrapper<TModule> = ModuleWrapper<TModule>>() {
39
+ return <U extends ModuleConstructable<TModule, TWrapper>>(constructor: U) => {
40
+ constructor
41
+ }
42
+ }
43
+
44
+ export type ModuleWrapperParams<TWrappedModule extends Module = Module> = BaseParams<{
45
+ account?: AccountInstance
46
+ module: TWrappedModule
47
+ }>
48
+
49
+ @moduleConstructable()
50
+ export class ModuleWrapper<TWrappedModule extends Module = Module>
51
+ extends BaseEmitter<TWrappedModule['params']>
52
+ implements Module<TWrappedModule['params']>
53
+ {
54
+ static requiredQueries: string[] = [ModuleDiscoverQuerySchema]
55
+
56
+ protected readonly wrapperParams: ModuleWrapperParams<TWrappedModule>
57
+
58
+ constructor(params: ModuleWrapperParams<TWrappedModule>) {
59
+ const mutatedParams = { ...params } as ModuleWrapperParams<TWrappedModule>
60
+ //unwrap it if already wrapped
61
+ const wrapper = params.module as unknown as ModuleWrapper<TWrappedModule>
62
+ if (wrapper.module) {
63
+ mutatedParams.module = wrapper.module
64
+ }
65
+
66
+ //set the root params to the wrapped module params
67
+ super(params.module.params)
68
+ this.wrapperParams = params
69
+ }
70
+
71
+ get account() {
72
+ return this.wrapperParams.account
73
+ }
74
+
75
+ get address() {
76
+ return this.module.address
77
+ }
78
+
79
+ get config(): TWrappedModule['config'] {
80
+ return this.module.config
81
+ }
82
+
83
+ get downResolver() {
84
+ return this.module.downResolver
85
+ }
86
+
87
+ get module() {
88
+ return this.wrapperParams.module
89
+ }
90
+
91
+ get queries(): string[] {
92
+ return this.module.queries
93
+ }
94
+
95
+ get upResolver() {
96
+ return this.module.upResolver
97
+ }
98
+
99
+ static canWrap(module?: Module) {
100
+ return !!module && this.missingRequiredQueries(module).length === 0
101
+ }
102
+
103
+ static hasRequiredQueries(module: Module) {
104
+ return this.missingRequiredQueries(module).length === 0
105
+ }
106
+
107
+ static missingRequiredQueries(module: Module): string[] {
108
+ const moduleQueries = module.queries
109
+ return compact(
110
+ this.requiredQueries.map((query) => {
111
+ return moduleQueries.find((item) => item === query) ? null : query
112
+ }),
113
+ )
114
+ }
115
+
116
+ static tryWrap(module?: Module, account?: AccountInstance): ModuleWrapper | undefined {
117
+ if (this.canWrap(module)) {
118
+ if (!account) {
119
+ this.defaultLogger?.info('Anonymous Module Wrapper Created')
120
+ }
121
+ return new ModuleWrapper({ account, module: module as Module })
122
+ }
123
+ }
124
+
125
+ static wrap(module?: Module, account?: AccountInstance): ModuleWrapper {
126
+ return assertEx(this.tryWrap(module, account), 'Unable to wrap module as ModuleWrapper')
127
+ }
128
+
129
+ override clearListeners(eventNames: keyof TWrappedModule['params']['eventData'] | keyof TWrappedModule['params']['eventData'][]) {
130
+ return this.module.clearListeners(eventNames)
131
+ }
132
+
133
+ async describe(): Promise<Promise<Promisable<ModuleDescription>>> {
134
+ const description: ModuleDescription = {
135
+ address: this.module.address,
136
+ queries: this.module.queries,
137
+ }
138
+ if (this.config.name) {
139
+ description.name = this.config.name
140
+ }
141
+
142
+ const discover = await this.discover()
143
+
144
+ description.children = compact(
145
+ discover?.map((payload) => {
146
+ const address = payload.schema === AddressSchema ? (payload as AddressPayload).address : undefined
147
+ return address != this.module.address ? address : undefined
148
+ }) ?? [],
149
+ )
150
+
151
+ return description
152
+ }
153
+
154
+ discover(): Promise<XyoPayload[]> {
155
+ const queryPayload = PayloadWrapper.parse<ModuleDiscoverQuery>({ schema: ModuleDiscoverQuerySchema })
156
+ return this.sendQuery(queryPayload)
157
+ }
158
+
159
+ override emit(
160
+ eventName: keyof TWrappedModule['params']['eventData'],
161
+ eventArgs?: TWrappedModule['params']['eventData'][keyof TWrappedModule['params']['eventData']],
162
+ ) {
163
+ return this.module.emit(eventName, eventArgs)
164
+ }
165
+
166
+ override emitSerial(
167
+ eventName: keyof TWrappedModule['params']['eventData'],
168
+ eventArgs?: TWrappedModule['params']['eventData'][keyof TWrappedModule['params']['eventData']],
169
+ ) {
170
+ return this.module.emitSerial(eventName, eventArgs)
171
+ }
172
+
173
+ override listenerCount(eventNames: keyof TWrappedModule['params']['eventData'] | keyof TWrappedModule['params']['eventData'][]) {
174
+ return this.module.listenerCount(eventNames)
175
+ }
176
+
177
+ override off(
178
+ eventNames: keyof TWrappedModule['params']['eventData'] | keyof TWrappedModule['params']['eventData'][],
179
+ listener: EventListener<TWrappedModule['params']['eventData']>,
180
+ ) {
181
+ return this.module.off(eventNames, listener)
182
+ }
183
+
184
+ override offAny(listener: EventAnyListener<TWrappedModule['params']['eventData']>) {
185
+ return this.module.offAny(listener)
186
+ }
187
+
188
+ override on(
189
+ eventNames: keyof TWrappedModule['params']['eventData'] | keyof TWrappedModule['params']['eventData'][],
190
+ listener: EventListener<TWrappedModule['params']['eventData']>,
191
+ ) {
192
+ return this.module.on(eventNames, listener)
193
+ }
194
+
195
+ override onAny(listener: EventAnyListener<TWrappedModule['params']['eventData']>) {
196
+ return this.module.onAny(listener)
197
+ }
198
+
199
+ override once(
200
+ eventNames: keyof TWrappedModule['params']['eventData'] | keyof TWrappedModule['params']['eventData'][],
201
+ listener: EventListener<TWrappedModule['params']['eventData']>,
202
+ ) {
203
+ return this.module.once(eventNames, listener)
204
+ }
205
+
206
+ async query<T extends XyoQueryBoundWitness = XyoQueryBoundWitness>(query: T, payloads?: XyoPayload[]): Promise<ModuleQueryResult> {
207
+ return await this.module.query(query, payloads)
208
+ }
209
+
210
+ queryable<T extends XyoQueryBoundWitness = XyoQueryBoundWitness>(query: T, payloads?: XyoPayload[]) {
211
+ return this.module.queryable(query, payloads)
212
+ }
213
+
214
+ async resolve<TModule extends Module = Module>(filter?: ModuleFilter): Promise<TModule[]>
215
+ async resolve<TModule extends Module = Module>(nameOrAddress: string): Promise<TModule | undefined>
216
+ async resolve<TModule extends Module = Module>(nameOrAddressOrFilter?: ModuleFilter | string): Promise<TModule | TModule[] | undefined> {
217
+ switch (typeof nameOrAddressOrFilter) {
218
+ case 'string': {
219
+ const byAddress = Account.isAddress(nameOrAddressOrFilter)
220
+ ? (await this.resolve<TModule>({ address: [nameOrAddressOrFilter] })).pop()
221
+ : undefined
222
+ return byAddress ?? (await this.resolve<TModule>({ name: [nameOrAddressOrFilter] })).pop()
223
+ }
224
+ default: {
225
+ const filter: ModuleFilter | undefined = nameOrAddressOrFilter
226
+ return [...(await this.module.downResolver.resolve<TModule>(filter)), ...(await this.module.upResolver.resolve<TModule>(filter))].filter(
227
+ duplicateModules,
228
+ )
229
+ }
230
+ }
231
+ }
232
+
233
+ /**
234
+ * Resolves the supplied filter into wrapped modules
235
+ * @example <caption>Example using ArchivistWrapper</caption>
236
+ * const filter = { address: [address] }
237
+ * const mods: ArchivistWrapper[] = await node.resolveWrapped(ArchivistWrapper, filter)
238
+ * @param wrapper The ModuleWrapper class (ArchivistWrapper,
239
+ * DivinerWrapper, etc.)
240
+ * @param filter The ModuleFilter
241
+ * @returns An array of ModuleWrapper instances corresponding to
242
+ * the underlying modules matching the supplied filter
243
+ */
244
+ async resolveWrapped<T extends ModuleWrapper<Module> = ModuleWrapper<Module>>(
245
+ wrapper: ModuleConstructable<Module, T>,
246
+ filter?: ModuleFilter,
247
+ ): Promise<T[]>
248
+ async resolveWrapped<T extends ModuleWrapper<Module> = ModuleWrapper<Module>>(
249
+ wrapper: ModuleConstructable<Module, T>,
250
+ nameOrAddress: string,
251
+ ): Promise<T | undefined>
252
+ async resolveWrapped<T extends ModuleWrapper<Module> = ModuleWrapper<Module>>(
253
+ wrapper: ModuleConstructable<Module, T>,
254
+ nameOrAddressOrFilter?: ModuleFilter | string,
255
+ ): Promise<T[] | T | undefined> {
256
+ switch (typeof nameOrAddressOrFilter) {
257
+ case 'string': {
258
+ const nameOrAddress: string = nameOrAddressOrFilter
259
+ const mod = await this.resolve<T['module']>(nameOrAddress)
260
+ return mod ? new wrapper({ account: this.account, module: mod }) : undefined
261
+ }
262
+ default: {
263
+ const filter: ModuleFilter | undefined = nameOrAddressOrFilter
264
+ return (await this.resolve<T['module']>(filter)).map((mod) => new wrapper({ account: this.account, module: mod }))
265
+ }
266
+ }
267
+ }
268
+
269
+ protected bindQuery<T extends XyoQuery | PayloadWrapper<XyoQuery>>(
270
+ query: T,
271
+ payloads?: XyoPayload[],
272
+ account: AccountInstance | undefined = this.account,
273
+ ): PromiseEx<[XyoQueryBoundWitness, XyoPayload[]], AccountInstance> {
274
+ const promise = new PromiseEx<[XyoQueryBoundWitness, XyoPayload[]], AccountInstance>((resolve) => {
275
+ const result = this.bindQueryInternal(query, payloads, account)
276
+ resolve?.(result)
277
+ return result
278
+ }, account)
279
+ return promise
280
+ }
281
+
282
+ protected bindQueryInternal<T extends XyoQuery | PayloadWrapper<XyoQuery>>(
283
+ query: T,
284
+ payloads?: XyoPayload[],
285
+ account: AccountInstance | undefined = this.account,
286
+ ): [XyoQueryBoundWitness, XyoPayload[]] {
287
+ const builder = new QueryBoundWitnessBuilder().payloads(payloads).query(query)
288
+ const result = (account ? builder.witness(account) : builder).build()
289
+ return result
290
+ }
291
+
292
+ protected filterErrors(query: [XyoQueryBoundWitness, XyoPayloads], result: ModuleQueryResult | undefined): (XyoError | null)[] {
293
+ return (result?.[1]?.filter((payload) => {
294
+ if (payload?.schema === XyoErrorSchema) {
295
+ const wrapper = new QueryBoundWitnessWrapper(query[0])
296
+ return payload.sources?.includes(wrapper.hash)
297
+ }
298
+ return false
299
+ }) ?? []) as XyoError[]
300
+ }
301
+
302
+ protected async sendQuery<T extends XyoQuery | PayloadWrapper<XyoQuery>>(queryPayload: T, payloads?: XyoPayloads): Promise<XyoPayload[]> {
303
+ //make sure we did not get wrapped payloads
304
+ const unwrappedPayloads = payloads?.map((payload) => assertEx(PayloadWrapper.unwrap(payload), 'Unable to parse payload'))
305
+ const unwrappedQueryPayload = assertEx(BoundWitnessWrapper.unwrap<XyoQueryBoundWitness>(queryPayload), 'Unable to parse queryPayload')
306
+
307
+ // Bind them
308
+ const query = await this.bindQuery(unwrappedQueryPayload, unwrappedPayloads)
309
+
310
+ // Send them off
311
+ const result = await this.module.query(query[0], query[1])
312
+
313
+ this.throwErrors(query, result)
314
+ return result[1]
315
+ }
316
+
317
+ protected throwErrors(query: [XyoQueryBoundWitness, XyoPayloads], result: ModuleQueryResult | undefined) {
318
+ const errors = this.filterErrors(query, result)
319
+ if (errors?.length > 0) {
320
+ const error: WrapperError = {
321
+ errors,
322
+ message: errors.reduce((message, error) => `${message}${message.length > 0 ? '|' : ''}${error?.message}`, ''),
323
+ name: 'XyoError',
324
+ query,
325
+ result,
326
+ }
327
+ throw error
328
+ }
329
+ }
330
+ }
@@ -0,0 +1,29 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import { BoundWitnessBuilder } from '@xyo-network/boundwitness-builder'
3
+ import { XyoQuery, XyoQueryBoundWitness, XyoQueryBoundWitnessSchema } from '@xyo-network/module-model'
4
+ import { PayloadSetPayload } from '@xyo-network/payload-model'
5
+ import { PayloadWrapper } from '@xyo-network/payload-wrapper'
6
+
7
+ export class QueryBoundWitnessBuilder<
8
+ TBoundWitness extends XyoQueryBoundWitness = XyoQueryBoundWitness,
9
+ TQuery extends XyoQuery = XyoQuery,
10
+ > extends BoundWitnessBuilder<TBoundWitness> {
11
+ private _query: PayloadWrapper<TQuery> | undefined
12
+ private _resultSet: PayloadWrapper<PayloadSetPayload> | undefined
13
+
14
+ override hashableFields(): TBoundWitness {
15
+ return { ...super.hashableFields(), query: assertEx(this._query?.hash, 'No Query Specified'), schema: XyoQueryBoundWitnessSchema }
16
+ }
17
+
18
+ query<T extends TQuery | PayloadWrapper<TQuery>>(query: T) {
19
+ this._query = PayloadWrapper.parse(query)
20
+ this.payload(this._query.payload)
21
+ return this
22
+ }
23
+
24
+ resultSet<T extends PayloadSetPayload | PayloadWrapper<PayloadSetPayload>>(payloadSet: T) {
25
+ this._resultSet = PayloadWrapper.parse(payloadSet)
26
+ this.payload(this._resultSet.payload)
27
+ return this
28
+ }
29
+ }
@@ -0,0 +1,41 @@
1
+ import { BoundWitnessValidator } from '@xyo-network/boundwitness-validator'
2
+ import { XyoQuery, XyoQueryBoundWitness, XyoQueryBoundWitnessSchema } from '@xyo-network/module-model'
3
+ import { PayloadWrapper } from '@xyo-network/payload-wrapper'
4
+
5
+ import { QueryBoundWitnessWrapper } from './QueryBoundWitnessWrapper'
6
+
7
+ export class QueryBoundWitnessValidator<T extends XyoQuery = XyoQuery> extends BoundWitnessValidator<XyoQueryBoundWitness> {
8
+ private _query: PayloadWrapper<T> | undefined
9
+
10
+ protected override get expectedSchema(): string {
11
+ return XyoQueryBoundWitnessSchema
12
+ }
13
+
14
+ static isQueryBoundWitnessValidator(obj: unknown) {
15
+ return (obj as QueryBoundWitnessValidator)?.constructor === QueryBoundWitnessValidator
16
+ }
17
+
18
+ override validate() {
19
+ return [
20
+ ...super.validate(),
21
+ // ...this.validateResultSet()
22
+ ]
23
+ }
24
+
25
+ validateResultSet() {
26
+ const errors: Error[] = []
27
+ const wrapper = new QueryBoundWitnessWrapper(this.obj)
28
+ const required = wrapper.resultSet.payload.required
29
+ if (required) {
30
+ Object.entries(required).forEach(([key, value]) => {
31
+ const found = wrapper.payloadSchemas.reduce((count, schema) => {
32
+ return count + (schema === key ? 1 : 0)
33
+ }, 0)
34
+ if (found !== value) {
35
+ errors.push(Error(`validateResultSet: Missing Schema [${key}:${found}:${value}]`))
36
+ }
37
+ })
38
+ }
39
+ return errors
40
+ }
41
+ }
@@ -0,0 +1,55 @@
1
+ import { assertEx } from '@xylabs/assert'
2
+ import { BoundWitnessWrapper } from '@xyo-network/boundwitness-wrapper'
3
+ import { XyoQuery, XyoQueryBoundWitness } from '@xyo-network/module-model'
4
+ import { PayloadSetPayload, XyoPayloads } from '@xyo-network/payload-model'
5
+ import { PayloadWrapper } from '@xyo-network/payload-wrapper'
6
+
7
+ import { QueryBoundWitnessValidator } from './QueryBoundWitnessValidator'
8
+
9
+ export class QueryBoundWitnessWrapper<T extends XyoQuery = XyoQuery> extends BoundWitnessWrapper<XyoQueryBoundWitness> {
10
+ private _query: PayloadWrapper<T> | undefined
11
+ private _resultSet: PayloadWrapper<PayloadSetPayload> | undefined
12
+
13
+ private isQueryBoundWitnessWrapper = true
14
+
15
+ override get errors() {
16
+ return new QueryBoundWitnessValidator(this.boundwitness).validate()
17
+ }
18
+
19
+ get query() {
20
+ return assertEx(
21
+ (this._query =
22
+ this._query ?? this.payloads[this.boundwitness.query] ? PayloadWrapper.parse<T>(this.payloads[this.boundwitness.query]) : undefined),
23
+ `Missing Query [${this.boundwitness}, ${JSON.stringify(this.payloads, null, 2)}]`,
24
+ )
25
+ }
26
+
27
+ get resultSet() {
28
+ const resultSetHash = this.boundwitness.resultSet
29
+ return assertEx(
30
+ (this._resultSet =
31
+ this._resultSet ??
32
+ (resultSetHash
33
+ ? this.payloads[resultSetHash]
34
+ ? PayloadWrapper.parse<PayloadSetPayload>(this.payloads[resultSetHash])
35
+ : undefined
36
+ : undefined)),
37
+ `Missing resultSet [${resultSetHash}, ${JSON.stringify(this.payloads, null, 2)}]`,
38
+ )
39
+ }
40
+
41
+ static parseQuery<T extends XyoQuery = XyoQuery>(obj: unknown, payloads?: XyoPayloads): QueryBoundWitnessWrapper<T> {
42
+ assertEx(!Array.isArray(obj), 'Array can not be converted to QueryBoundWitnessWrapper')
43
+ switch (typeof obj) {
44
+ case 'object': {
45
+ const castWrapper = obj as QueryBoundWitnessWrapper<T>
46
+ const wrapper = castWrapper?.isQueryBoundWitnessWrapper ? castWrapper : new QueryBoundWitnessWrapper<T>(obj as XyoQueryBoundWitness, payloads)
47
+ if (!wrapper.valid) {
48
+ console.warn(`Parsed invalid QueryBoundWitness ${JSON.stringify(wrapper.errors.map((error) => error.message))}`)
49
+ }
50
+ return wrapper
51
+ }
52
+ }
53
+ throw Error(`Unable to parse [${typeof obj}]`)
54
+ }
55
+ }
@@ -0,0 +1,3 @@
1
+ export * from './QueryBoundWitnessBuilder'
2
+ export * from './QueryBoundWitnessValidator'
3
+ export * from './QueryBoundWitnessWrapper'
@@ -0,0 +1,67 @@
1
+ import { AddressString, AnyConfigSchema, CosigningAddressSet, ModuleConfig, ModuleQuery, SchemaString } from '@xyo-network/module-model'
2
+
3
+ import { QueryBoundWitnessWrapper } from '../Query'
4
+ import { Queryable, QueryValidator } from './QueryValidator'
5
+
6
+ export type SortedPipedAddressesString = string
7
+
8
+ const delimiter = ''
9
+
10
+ export class ModuleConfigQueryValidator<TConfig extends AnyConfigSchema<ModuleConfig>> implements QueryValidator {
11
+ protected allowed: Record<SchemaString, SortedPipedAddressesString[]> = {}
12
+ protected disallowed: Record<SchemaString, AddressString[]> = {}
13
+ protected readonly hasAllowedRules: boolean
14
+ protected readonly hasDisallowedRules: boolean
15
+ protected readonly hasRules: boolean
16
+
17
+ constructor(config?: TConfig) {
18
+ if (config?.security?.allowed) {
19
+ Object.entries(config.security?.allowed).forEach(([schema, addresses]) => {
20
+ this.allowed[schema] = addresses.map(toAddressesString)
21
+ })
22
+ }
23
+ if (config?.security?.disallowed) {
24
+ Object.entries(config.security?.disallowed).forEach(([schema, addresses]) => {
25
+ this.disallowed[schema] = addresses.map(toAddressesString)
26
+ })
27
+ }
28
+ this.hasAllowedRules = Object.keys(this.allowed).length > 0
29
+ this.hasDisallowedRules = Object.keys(this.disallowed).length > 0
30
+ this.hasRules = this.hasAllowedRules || this.hasDisallowedRules
31
+ }
32
+
33
+ queryable: Queryable = (query, payloads) => {
34
+ if (!this.hasRules) return true
35
+ const addresses = query.addresses
36
+ if (!addresses.length) return false
37
+ const wrapper = QueryBoundWitnessWrapper.parseQuery<ModuleQuery>(query, payloads)
38
+ const schema = wrapper.query.schema
39
+ return this.queryAllowed(schema, addresses) && !this.queryDisallowed(schema, addresses)
40
+ }
41
+
42
+ protected queryAllowed = (schema: SchemaString, addresses: string[]): boolean => {
43
+ if (!this.hasAllowedRules) return true
44
+ // All cosigners must sign
45
+ if (addresses.length > 1) {
46
+ const signatories = toAddressesString(addresses)
47
+ const validCosigners = this.allowed?.[schema]?.includes(signatories)
48
+ if (validCosigners) return true
49
+ }
50
+ // OR all signers have to be allowed individually
51
+ return addresses.every((address) => this.allowed?.[schema]?.includes(address) || false)
52
+ }
53
+ protected queryDisallowed = (schema: SchemaString, addresses: string[]): boolean => {
54
+ if (!this.hasDisallowedRules) return false
55
+ return addresses.some((address) => this.disallowed?.[schema]?.includes(address))
56
+ }
57
+ }
58
+
59
+ // TODO: Handle 0x prefix
60
+ const toAddressesString = (addresses: string | CosigningAddressSet): SortedPipedAddressesString => {
61
+ return Array.isArray(addresses)
62
+ ? addresses
63
+ .sort()
64
+ .map((address) => address.toLowerCase())
65
+ .join(delimiter)
66
+ : addresses.toLowerCase()
67
+ }
@@ -0,0 +1,8 @@
1
+ import { XyoQueryBoundWitness } from '@xyo-network/module-model'
2
+ import { XyoPayload } from '@xyo-network/payload-model'
3
+
4
+ export type Queryable<T extends XyoQueryBoundWitness = XyoQueryBoundWitness> = (query: T, payloads?: XyoPayload[]) => boolean
5
+
6
+ export interface QueryValidator<T extends XyoQueryBoundWitness = XyoQueryBoundWitness> {
7
+ queryable: Queryable<T>
8
+ }
@@ -0,0 +1,22 @@
1
+ import { Module, ModuleQuery, XyoQueryBoundWitness } from '@xyo-network/module-model'
2
+ import { XyoPayload } from '@xyo-network/payload-model'
3
+
4
+ import { QueryBoundWitnessWrapper } from '../Query'
5
+ import { Queryable, QueryValidator } from './QueryValidator'
6
+
7
+ export const isQuerySupportedByModule = <T extends XyoQueryBoundWitness = XyoQueryBoundWitness>(
8
+ mod: Module,
9
+ query: T,
10
+ payloads?: XyoPayload[],
11
+ ): boolean => {
12
+ const wrapper = QueryBoundWitnessWrapper.parseQuery<ModuleQuery>(query, payloads)
13
+ const schema = wrapper.query.schema
14
+ return mod.queries.includes(schema)
15
+ }
16
+
17
+ export class SupportedQueryValidator implements QueryValidator {
18
+ constructor(protected readonly mod: Module) {}
19
+ queryable: Queryable = (query, payloads) => {
20
+ return isQuerySupportedByModule(this.mod, query, payloads)
21
+ }
22
+ }
@@ -0,0 +1,3 @@
1
+ export * from './ModuleConfigQueryValidator'
2
+ export * from './QueryValidator'
3
+ export * from './SupportedQueryValidator'
@@ -0,0 +1,70 @@
1
+ import { fulfilled } from '@xylabs/promise'
2
+ import { Module, ModuleFilter, ModuleRepository, ModuleResolver } from '@xyo-network/module-model'
3
+
4
+ import { duplicateModules } from '../lib'
5
+ import { SimpleModuleResolver } from './SimpleModuleResolver'
6
+
7
+ export class CompositeModuleResolver implements ModuleRepository, ModuleResolver {
8
+ protected resolvers: ModuleResolver[] = []
9
+ private localResolver: SimpleModuleResolver
10
+
11
+ constructor() {
12
+ const localResolver = new SimpleModuleResolver()
13
+ this.addResolver(localResolver)
14
+ this.localResolver = localResolver
15
+ }
16
+
17
+ get isModuleResolver() {
18
+ return true
19
+ }
20
+
21
+ add(module: Module): this
22
+ add(module: Module[]): this
23
+ add(module: Module | Module[]): this {
24
+ if (Array.isArray(module)) {
25
+ module.forEach((module) => this.addSingleModule(module))
26
+ } else {
27
+ this.addSingleModule(module)
28
+ }
29
+ return this
30
+ }
31
+
32
+ addResolver(resolver: ModuleResolver): this {
33
+ this.resolvers.push(resolver)
34
+ return this
35
+ }
36
+
37
+ remove(addressOrName: string | string[]): this {
38
+ if (Array.isArray(addressOrName)) {
39
+ addressOrName.forEach((address) => this.removeSingleModule(address))
40
+ } else {
41
+ this.removeSingleModule(addressOrName)
42
+ }
43
+ return this
44
+ }
45
+
46
+ removeResolver(resolver: ModuleResolver): this {
47
+ this.resolvers = this.resolvers.filter((item) => item !== resolver)
48
+ return this
49
+ }
50
+
51
+ async resolve<T extends Module = Module>(filter?: ModuleFilter): Promise<T[]> {
52
+ const modules = this.resolvers.map((resolver) => resolver.resolve(filter))
53
+ const settled = await Promise.allSettled(modules)
54
+ const result = settled
55
+ .filter(fulfilled)
56
+ .map((r) => r.value)
57
+ .flat()
58
+ .filter(duplicateModules)
59
+ return result as T[]
60
+ }
61
+
62
+ private addSingleModule(module?: Module) {
63
+ if (module) {
64
+ this.localResolver.add(module)
65
+ }
66
+ }
67
+ private removeSingleModule(addressOrName: string) {
68
+ this.localResolver.remove(addressOrName)
69
+ }
70
+ }
@@ -0,0 +1,46 @@
1
+ import { Module, ModuleFilter, ModuleResolver } from '@xyo-network/module-model'
2
+
3
+ export interface ModuleResolvedEventArgs {
4
+ filter?: ModuleFilter
5
+ module: Module
6
+ }
7
+
8
+ export interface ResolverEventEmitter {
9
+ on(event: 'moduleResolved', listener: (args: ModuleResolvedEventArgs) => void): void
10
+ }
11
+
12
+ type ListenerFunction = (args: ModuleResolvedEventArgs) => void
13
+
14
+ const getMixin = <T extends ModuleResolver = ModuleResolver>(resolver: T) => {
15
+ const listeners: ListenerFunction[] = []
16
+ const emit = (event: 'moduleResolved', args: ModuleResolvedEventArgs): boolean => {
17
+ if (listeners.length < 1) return false
18
+ listeners.map((listener) => listener(args))
19
+ return true
20
+ }
21
+ const onModuleResolved = (module: Module, filter?: ModuleFilter) => {
22
+ const args = { filter, module }
23
+ emit('moduleResolved', args)
24
+ }
25
+ const { resolve } = resolver
26
+ function originalResolve(filter?: ModuleFilter) {
27
+ return resolve.bind(resolver)(filter)
28
+ }
29
+
30
+ return {
31
+ on: (event: 'moduleResolved', listener: (args: ModuleResolvedEventArgs) => void) => {
32
+ listeners.push(listener)
33
+ },
34
+ resolve: async (filter?: ModuleFilter): Promise<Module[]> => {
35
+ const modules: Module[] = await originalResolve(filter)
36
+ await Promise.allSettled(modules.map((mod) => onModuleResolved(mod, filter)))
37
+ return modules
38
+ },
39
+ }
40
+ }
41
+
42
+ export const mixinResolverEventEmitter = <T extends ModuleResolver = ModuleResolver>(resolver: T): T & ResolverEventEmitter => {
43
+ const mixin = getMixin(resolver)
44
+ const ret = Object.assign(resolver, mixin)
45
+ return ret
46
+ }