@xyo-network/module-abstract 2.92.6 → 2.92.8

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 (42) hide show
  1. package/dist/browser/AbstractModule.d.cts +19 -7
  2. package/dist/browser/AbstractModule.d.cts.map +1 -1
  3. package/dist/browser/AbstractModule.d.mts +19 -7
  4. package/dist/browser/AbstractModule.d.mts.map +1 -1
  5. package/dist/browser/AbstractModule.d.ts +19 -7
  6. package/dist/browser/AbstractModule.d.ts.map +1 -1
  7. package/dist/browser/AbstractModuleInstance.d.cts +2 -1
  8. package/dist/browser/AbstractModuleInstance.d.cts.map +1 -1
  9. package/dist/browser/AbstractModuleInstance.d.mts +2 -1
  10. package/dist/browser/AbstractModuleInstance.d.mts.map +1 -1
  11. package/dist/browser/AbstractModuleInstance.d.ts +2 -1
  12. package/dist/browser/AbstractModuleInstance.d.ts.map +1 -1
  13. package/dist/browser/determineAccount.d.cts.map +1 -1
  14. package/dist/browser/determineAccount.d.mts.map +1 -1
  15. package/dist/browser/determineAccount.d.ts.map +1 -1
  16. package/dist/browser/index.cjs +94 -38
  17. package/dist/browser/index.cjs.map +1 -1
  18. package/dist/browser/index.js +95 -39
  19. package/dist/browser/index.js.map +1 -1
  20. package/dist/node/AbstractModule.d.cts +19 -7
  21. package/dist/node/AbstractModule.d.cts.map +1 -1
  22. package/dist/node/AbstractModule.d.mts +19 -7
  23. package/dist/node/AbstractModule.d.mts.map +1 -1
  24. package/dist/node/AbstractModule.d.ts +19 -7
  25. package/dist/node/AbstractModule.d.ts.map +1 -1
  26. package/dist/node/AbstractModuleInstance.d.cts +2 -1
  27. package/dist/node/AbstractModuleInstance.d.cts.map +1 -1
  28. package/dist/node/AbstractModuleInstance.d.mts +2 -1
  29. package/dist/node/AbstractModuleInstance.d.mts.map +1 -1
  30. package/dist/node/AbstractModuleInstance.d.ts +2 -1
  31. package/dist/node/AbstractModuleInstance.d.ts.map +1 -1
  32. package/dist/node/determineAccount.d.cts.map +1 -1
  33. package/dist/node/determineAccount.d.mts.map +1 -1
  34. package/dist/node/determineAccount.d.ts.map +1 -1
  35. package/dist/node/index.cjs +101 -42
  36. package/dist/node/index.cjs.map +1 -1
  37. package/dist/node/index.js +102 -43
  38. package/dist/node/index.js.map +1 -1
  39. package/package.json +25 -25
  40. package/src/AbstractModule.ts +111 -44
  41. package/src/AbstractModuleInstance.ts +9 -2
  42. package/src/determineAccount.ts +6 -3
@@ -1,8 +1,9 @@
1
+ /* eslint-disable complexity */
1
2
  /* eslint-disable max-lines */
2
3
  import { assertEx } from '@xylabs/assert'
3
4
  import { handleError, handleErrorAsync } from '@xylabs/error'
4
5
  import { exists } from '@xylabs/exists'
5
- import { Hash } from '@xylabs/hex'
6
+ import { Address, Hash } from '@xylabs/hex'
6
7
  import { compact } from '@xylabs/lodash'
7
8
  import { ConsoleLogger, IdLogger, Logger, LogLevel } from '@xylabs/logger'
8
9
  import { Base } from '@xylabs/object'
@@ -21,6 +22,7 @@ import {
21
22
  AddressPreviousHashSchema,
22
23
  CreatableModule,
23
24
  CreatableModuleFactory,
25
+ DeadModuleError,
24
26
  duplicateModules,
25
27
  isModuleName,
26
28
  Module,
@@ -44,6 +46,7 @@ import {
44
46
  ModuleQueryHandlerResult,
45
47
  ModuleQueryResult,
46
48
  ModuleStateQuerySchema,
49
+ ModuleStatus,
47
50
  ModuleSubscribeQuerySchema,
48
51
  serializableField,
49
52
  } from '@xyo-network/module-model'
@@ -69,8 +72,8 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
69
72
 
70
73
  protected static privateConstructorKey = Date.now().toString()
71
74
 
72
- readonly downResolver: Omit<CompositeModuleResolver, 'resolve'> = new CompositeModuleResolver()
73
- readonly upResolver: Omit<CompositeModuleResolver, 'resolve>'> = new CompositeModuleResolver()
75
+ readonly downResolver = new CompositeModuleResolver()
76
+ readonly upResolver = new CompositeModuleResolver()
74
77
 
75
78
  protected _account: AccountInstance | undefined = undefined
76
79
  protected readonly _baseModuleQueryAccountPaths: Record<ModuleQueries['schema'], string> = {
@@ -81,6 +84,7 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
81
84
  [ModuleStateQuerySchema]: '6',
82
85
  [ModuleSubscribeQuerySchema]: '3',
83
86
  }
87
+ protected _lastError?: Error
84
88
  protected readonly _queryAccounts: Record<ModuleQueries['schema'], AccountInstance | undefined> = {
85
89
  [ModuleAddressQuerySchema]: undefined,
86
90
  [ModuleDescribeQuerySchema]: undefined,
@@ -95,9 +99,10 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
95
99
  protected readonly supportedQueryValidator: Queryable
96
100
 
97
101
  private _busyCount = 0
102
+ private _status: ModuleStatus = 'stopped'
98
103
 
99
104
  constructor(privateConstructorKey: string, params: TParams, account: AccountInstance) {
100
- assertEx(AbstractModule.privateConstructorKey === privateConstructorKey, 'Use create function instead of constructor')
105
+ assertEx(AbstractModule.privateConstructorKey === privateConstructorKey, () => 'Use create function instead of constructor')
101
106
  // Clone params to prevent mutation of the incoming object
102
107
  const mutatedParams = { ...params } as TParams
103
108
  super(mutatedParams)
@@ -113,7 +118,7 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
113
118
  }
114
119
 
115
120
  get account() {
116
- return assertEx(this._account, 'Missing account')
121
+ return assertEx(this._account, () => 'Missing account')
117
122
  }
118
123
 
119
124
  get address() {
@@ -128,6 +133,10 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
128
133
  return this.params.config
129
134
  }
130
135
 
136
+ get dead() {
137
+ return this.status === 'dead'
138
+ }
139
+
131
140
  get ephemeralQueryAccountEnabled(): boolean {
132
141
  return !!this.params.ephemeralQueryAccountEnabled
133
142
  }
@@ -155,6 +164,10 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
155
164
  return this._queryAccounts
156
165
  }
157
166
 
167
+ get status() {
168
+ return this._status
169
+ }
170
+
158
171
  get timestamp() {
159
172
  return this.config.timestamp ?? false
160
173
  }
@@ -167,6 +180,12 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
167
180
  return this.params?.logger ?? AbstractModule.defaultLogger ?? Base.defaultLogger
168
181
  }
169
182
 
183
+ protected set status(value: ModuleStatus) {
184
+ if (this._status !== 'dead') {
185
+ this._status = value
186
+ }
187
+ }
188
+
170
189
  protected abstract get _queryAccountPaths(): Record<Query['schema'], string>
171
190
 
172
191
  static _getRootFunction(funcName: string) {
@@ -183,7 +202,7 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
183
202
  const thisFunc = (this as any)[functionName]
184
203
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
185
204
  const rootFunc = this._getRootFunction(functionName)
186
- assertEx(thisFunc === rootFunc, `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)
205
+ assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)
187
206
  }
188
207
 
189
208
  static async create<TModule extends ModuleInstance>(
@@ -195,7 +214,7 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
195
214
  throw new Error(`Missing configSchema [${params?.config?.schema}][${this.name}]`)
196
215
  }
197
216
 
198
- assertEx(params?.config?.name === undefined || isModuleName(params.config.name), `Invalid module name: ${params?.config?.name}`)
217
+ assertEx(params?.config?.name === undefined || isModuleName(params.config.name), () => `Invalid module name: ${params?.config?.name}`)
199
218
 
200
219
  const { account } = params ?? {}
201
220
 
@@ -244,14 +263,6 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
244
263
  return anyThis[funcName]
245
264
  }
246
265
 
247
- _noOverride(functionName: string) {
248
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
249
- const thisFunc = (this as any)[functionName]
250
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
251
- const rootFunc = this._getRootFunction(functionName)
252
- assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)
253
- }
254
-
255
266
  async busy<R>(closure: () => Promise<R>) {
256
267
  if (this._busyCount <= 0) {
257
268
  this._busyCount = 0
@@ -279,6 +290,7 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
279
290
  }
280
291
 
281
292
  previousHash(): Promisable<string | undefined> {
293
+ this._checkDead()
282
294
  return this.account.previousHash
283
295
  }
284
296
 
@@ -287,8 +299,9 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
287
299
  payloads?: Payload[],
288
300
  queryConfig?: TConfig,
289
301
  ): Promise<ModuleQueryResult> {
302
+ this._checkDead()
290
303
  this._noOverride('query')
291
- const sourceQuery = await PayloadBuilder.build(assertEx(QueryBoundWitnessWrapper.unwrap(query), 'Invalid query'))
304
+ const sourceQuery = await PayloadBuilder.build(assertEx(QueryBoundWitnessWrapper.unwrap(query), () => 'Invalid query'))
292
305
  return await this.busy(async () => {
293
306
  const resultPayloads: Payload[] = []
294
307
  const errorPayloads: ModuleError[] = []
@@ -298,9 +311,14 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
298
311
  if (!this.allowAnonymous && query.addresses.length === 0) {
299
312
  throw new Error(`Anonymous Queries not allowed, but running anyway [${this.config.name}], [${this.address}]`)
300
313
  }
314
+ if (queryConfig?.allowedQueries) {
315
+ assertEx(queryConfig?.allowedQueries.includes(sourceQuery.schema), () => `Query not allowed [${sourceQuery.schema}]`)
316
+ }
301
317
  resultPayloads.push(...(await this.queryHandler(sourceQuery, payloads, queryConfig)))
302
318
  } catch (ex) {
303
319
  await handleErrorAsync(ex, async (error) => {
320
+ this._lastError = error
321
+ this.status = 'dead'
304
322
  errorPayloads.push(
305
323
  await new ModuleErrorBuilder()
306
324
  .sources([sourceQuery.$hash])
@@ -327,6 +345,9 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
327
345
  payloads?: Payload[],
328
346
  queryConfig?: TConfig,
329
347
  ): Promise<boolean> {
348
+ if (this.dead) {
349
+ return false
350
+ }
330
351
  if (!(await this.started('warn'))) return false
331
352
  const configValidator =
332
353
  queryConfig ? new ModuleConfigQueryValidator(Object.assign({}, this.config, queryConfig)).queryable : this.moduleConfigQueryValidator
@@ -335,10 +356,15 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
335
356
  return validators.every((validator) => validator(query, payloads))
336
357
  }
337
358
 
338
- async resolve<T extends ModuleInstance = ModuleInstance>(filter?: ModuleFilter, options?: ModuleFilterOptions<T>): Promise<T[]>
359
+ /** @deprecated do not pass undefined. If trying to get all, pass '*' */
360
+ async resolve(): Promise<ModuleInstance[]>
361
+ async resolve<T extends ModuleInstance = ModuleInstance>(all: '*', options?: ModuleFilterOptions<T>): Promise<T[]>
362
+ async resolve<T extends ModuleInstance = ModuleInstance>(filter: ModuleFilter, options?: ModuleFilterOptions<T>): Promise<T[]>
339
363
  async resolve<T extends ModuleInstance = ModuleInstance>(id: ModuleIdentifier, options?: ModuleFilterOptions<T>): Promise<T | undefined>
364
+ /** @deprecated use '*' if trying to resolve all */
365
+ async resolve<T extends ModuleInstance = ModuleInstance>(filter?: ModuleFilter, options?: ModuleFilterOptions<T>): Promise<T[]>
340
366
  async resolve<T extends ModuleInstance = ModuleInstance>(
341
- idOrFilter?: ModuleFilter<T> | ModuleIdentifier,
367
+ idOrFilter: ModuleFilter<T> | ModuleIdentifier = '*',
342
368
  { required = 'log', ...options }: ModuleFilterOptions<T> = {},
343
369
  ): Promise<T | T[] | undefined> {
344
370
  const childOptions = { ...options, required: false }
@@ -346,45 +372,48 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
346
372
  const up = direction === 'up' || direction === 'all'
347
373
  const down = direction === 'down' || direction === 'all'
348
374
  let result: T | T[] | undefined
349
- switch (typeof idOrFilter) {
350
- case 'string': {
351
- result =
352
- (down ? await (this.downResolver as CompositeModuleResolver).resolve<T>(idOrFilter, childOptions) : undefined) ??
353
- (up ? await (this.upResolver as CompositeModuleResolver).resolve<T>(idOrFilter, childOptions) : undefined)
354
- break
355
- }
356
- default: {
357
- const filter: ModuleFilter<T> | undefined = idOrFilter
358
- result = [
359
- ...(down ? await (this.downResolver as CompositeModuleResolver).resolve<T>(filter, childOptions) : []),
360
- ...(up ? await (this.upResolver as CompositeModuleResolver).resolve<T>(filter, childOptions) : []),
361
- ].filter(duplicateModules)
362
- break
375
+ if (idOrFilter === '*') {
376
+ if (this.dead) {
377
+ return []
363
378
  }
364
- }
365
- if (required && (result === undefined || (Array.isArray(result) && result.length > 0))) {
366
- switch (required) {
367
- case 'warn': {
368
- this.logger.warn('resolve failed', idOrFilter)
369
- break
370
- }
371
- case 'log': {
372
- this.logger.log('resolve failed', idOrFilter)
379
+ return [
380
+ ...(down ? await (this.downResolver as CompositeModuleResolver).resolve<T>('*', childOptions) : []),
381
+ ...(up ? await (this.upResolver as CompositeModuleResolver).resolve<T>('*', childOptions) : []),
382
+ ].filter(duplicateModules)
383
+ } else {
384
+ switch (typeof idOrFilter) {
385
+ case 'string': {
386
+ if (this.dead) {
387
+ return undefined
388
+ }
389
+ result =
390
+ (down ? await (this.downResolver as CompositeModuleResolver).resolve<T>(idOrFilter, childOptions) : undefined) ??
391
+ (up ? await (this.upResolver as CompositeModuleResolver).resolve<T>(idOrFilter, childOptions) : undefined)
373
392
  break
374
393
  }
375
394
  default: {
376
- this.logger.error('resolve failed', idOrFilter)
395
+ if (this.dead) {
396
+ return []
397
+ }
398
+ const filter: ModuleFilter<T> | undefined = idOrFilter
399
+ result = [
400
+ ...(down ? await (this.downResolver as CompositeModuleResolver).resolve<T>(filter, childOptions) : []),
401
+ ...(up ? await (this.upResolver as CompositeModuleResolver).resolve<T>(filter, childOptions) : []),
402
+ ].filter(duplicateModules)
377
403
  break
378
404
  }
379
405
  }
380
406
  }
407
+ this.validateRequiredResolve(required, result, idOrFilter)
381
408
  return result
382
409
  }
383
410
 
384
411
  start(_timeout?: number): Promisable<boolean> {
385
412
  //using promise as mutex
386
413
  this._startPromise = this._startPromise ?? this.startHandler()
387
- return this._startPromise
414
+ const result = this._startPromise
415
+ this.status = result ? 'started' : 'dead'
416
+ return result
388
417
  }
389
418
 
390
419
  async started(notStartedAction: 'error' | 'throw' | 'warn' | 'log' | 'none' = 'log', tryStart = true): Promise<boolean> {
@@ -440,10 +469,25 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
440
469
  const result = await this.stopHandler()
441
470
  this._started = undefined
442
471
  this._startPromise = undefined
472
+ this.status = result ? 'stopped' : 'dead'
443
473
  return result
444
474
  })
445
475
  }
446
476
 
477
+ protected _checkDead() {
478
+ if (this.dead) {
479
+ throw new DeadModuleError(this.id, this._lastError)
480
+ }
481
+ }
482
+
483
+ protected _noOverride(functionName: string) {
484
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
485
+ const thisFunc = (this as any)[functionName]
486
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
487
+ const rootFunc = this._getRootFunction(functionName)
488
+ assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)
489
+ }
490
+
447
491
  protected bindHashes(hashes: Hash[], schema: Schema[], account?: AccountInstance) {
448
492
  // eslint-disable-next-line @typescript-eslint/no-misused-promises
449
493
  const promise = new PromiseEx((resolve) => {
@@ -571,7 +615,7 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
571
615
  }
572
616
  }
573
617
 
574
- protected manifestHandler(_depth?: number, _ignoreAddresses?: string[]): Promisable<ModuleManifestPayload> {
618
+ protected manifestHandler(_depth?: number, _ignoreAddresses?: Address[]): Promisable<ModuleManifestPayload> {
575
619
  const name = this.config.name ?? 'Anonymous'
576
620
  return { config: { name, ...this.config }, schema: ModuleManifestPayloadSchema, status: { address: this.address } }
577
621
  }
@@ -710,4 +754,27 @@ export abstract class AbstractModule<TParams extends ModuleParams = ModuleParams
710
754
  }
711
755
  }, true)
712
756
  }
757
+
758
+ private validateRequiredResolve(
759
+ required: boolean | 'warn' | 'log',
760
+ result: ModuleInstance[] | ModuleInstance | undefined,
761
+ idOrFilter: ModuleIdentifier | ModuleFilter,
762
+ ) {
763
+ if (required && (result === undefined || (Array.isArray(result) && result.length > 0))) {
764
+ switch (required) {
765
+ case 'warn': {
766
+ this.logger.warn('resolve failed', idOrFilter)
767
+ break
768
+ }
769
+ case 'log': {
770
+ this.logger.log('resolve failed', idOrFilter)
771
+ break
772
+ }
773
+ default: {
774
+ this.logger.error('resolve failed', idOrFilter)
775
+ break
776
+ }
777
+ }
778
+ }
779
+ }
713
780
  }
@@ -1,4 +1,5 @@
1
1
  import { assertEx } from '@xylabs/assert'
2
+ import { Address } from '@xylabs/hex'
2
3
  import { AccountInstance } from '@xyo-network/account-model'
3
4
  import { ModuleManifestPayload } from '@xyo-network/manifest-model'
4
5
  import { AddressPreviousHashPayload, Module, ModuleDescriptionPayload, ModuleEventData, ModuleParams } from '@xyo-network/module-model'
@@ -11,7 +12,7 @@ export abstract class AbstractModuleInstance<TParams extends ModuleParams = Modu
11
12
  implements Module<TParams, TEventData>
12
13
  {
13
14
  constructor(privateConstructorKey: string, params: TParams, account: AccountInstance) {
14
- assertEx(AbstractModule.privateConstructorKey === privateConstructorKey, 'Use create function instead of constructor')
15
+ assertEx(AbstractModule.privateConstructorKey === privateConstructorKey, () => 'Use create function instead of constructor')
15
16
  // Clone params to prevent mutation of the incoming object
16
17
  const mutatedParams = { ...params } as TParams
17
18
  super(privateConstructorKey, mutatedParams, account)
@@ -20,36 +21,42 @@ export abstract class AbstractModuleInstance<TParams extends ModuleParams = Modu
20
21
  }
21
22
 
22
23
  describe(): Promise<ModuleDescriptionPayload> {
24
+ this._checkDead()
23
25
  return this.busy(async () => {
24
26
  return await this.describeHandler()
25
27
  })
26
28
  }
27
29
 
28
30
  discover(): Promise<Payload[]> {
31
+ this._checkDead()
29
32
  return this.busy(async () => {
30
33
  return await this.discoverHandler()
31
34
  })
32
35
  }
33
36
 
34
- manifest(maxDepth?: number, ignoreAddresses?: string[]): Promise<ModuleManifestPayload> {
37
+ manifest(maxDepth?: number, ignoreAddresses?: Address[]): Promise<ModuleManifestPayload> {
38
+ this._checkDead()
35
39
  return this.busy(async () => {
36
40
  return await this.manifestHandler(maxDepth, ignoreAddresses)
37
41
  })
38
42
  }
39
43
 
40
44
  moduleAddress(): Promise<AddressPreviousHashPayload[]> {
45
+ this._checkDead()
41
46
  return this.busy(async () => {
42
47
  return await this.moduleAddressHandler()
43
48
  })
44
49
  }
45
50
 
46
51
  state() {
52
+ this._checkDead()
47
53
  return this.busy(async () => {
48
54
  return await this.stateHandler()
49
55
  })
50
56
  }
51
57
 
52
58
  subscribe(_queryAccount?: AccountInstance) {
59
+ this._checkDead()
53
60
  return this.subscribeHandler()
54
61
  }
55
62
  }
@@ -18,7 +18,7 @@ export interface DetermineRandomParams {}
18
18
  export type DetermineAccountParams = DetermineAccountFromAccountParams | DetermineAccountFromWalletParams | DetermineRandomParams
19
19
 
20
20
  const isDetermineAccountFromAccountParams = (params: DetermineAccountParams): params is DetermineAccountFromAccountParams => {
21
- assertEx(!(params as DetermineAccountFromWalletParams).accountPath, 'accountPath may not be provided when account is provided')
21
+ assertEx(!(params as DetermineAccountFromWalletParams).accountPath, () => 'accountPath may not be provided when account is provided')
22
22
  return !!(params as DetermineAccountFromAccountParams).account
23
23
  }
24
24
 
@@ -29,14 +29,17 @@ const isDetermineAccountFromWalletParams = (params: DetermineAccountParams): par
29
29
  export async function determineAccount(params: DetermineAccountParams, allowRandomAccount = true): Promise<AccountInstance> {
30
30
  if (isDetermineAccountFromAccountParams(params)) {
31
31
  if (params.account === 'random') {
32
- assertEx(allowRandomAccount, 'Random address not allowed')
32
+ assertEx(allowRandomAccount, () => 'Random address not allowed')
33
33
  return Account.randomSync()
34
34
  }
35
35
  return params.account
36
36
  }
37
37
 
38
38
  if (isDetermineAccountFromWalletParams(params)) {
39
- return assertEx(params.accountPath ? await params.wallet.derivePath(params.accountPath) : params.wallet, 'Failed to derive account from path')
39
+ return assertEx(
40
+ params.accountPath ? await params.wallet.derivePath(params.accountPath) : params.wallet,
41
+ () => 'Failed to derive account from path',
42
+ )
40
43
  }
41
44
 
42
45
  //this should eventually be removed/thrown