@xyo-network/module-abstract 3.18.9 → 4.0.0

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.
@@ -1,31 +1,35 @@
1
1
  // src/AbstractModule.ts
2
2
  import { assertEx as assertEx2 } from "@xylabs/assert";
3
- import { Base, globallyUnique } from "@xylabs/base";
3
+ import { globallyUnique } from "@xylabs/base";
4
+ import { AbstractCreatable } from "@xylabs/creatable";
4
5
  import { handleError, handleErrorAsync } from "@xylabs/error";
5
6
  import { exists } from "@xylabs/exists";
6
7
  import { forget } from "@xylabs/forget";
7
8
  import {
8
9
  ConsoleLogger,
9
10
  IdLogger,
11
+ LevelLogger,
10
12
  LogLevel
11
13
  } from "@xylabs/logger";
12
14
  import { PromiseEx } from "@xylabs/promise";
13
15
  import { spanAsync } from "@xylabs/telemetry";
14
16
  import {
15
17
  isDefined,
18
+ isObject,
16
19
  isString,
17
20
  isUndefined
18
21
  } from "@xylabs/typeof";
19
22
  import { Account as Account2 } from "@xyo-network/account";
23
+ import { isAccountInstance } from "@xyo-network/account-model";
20
24
  import { asArchivistInstance } from "@xyo-network/archivist-model";
21
25
  import { BoundWitnessBuilder, QueryBoundWitnessBuilder } from "@xyo-network/boundwitness-builder";
22
26
  import { isQueryBoundWitness } from "@xyo-network/boundwitness-model";
23
27
  import { QueryBoundWitnessWrapper as QueryBoundWitnessWrapper3 } from "@xyo-network/boundwitness-wrapper";
24
28
  import { ConfigSchema } from "@xyo-network/config-payload-plugin";
25
- import { ModuleBaseEmitter } from "@xyo-network/module-event-emitter";
26
29
  import {
27
30
  AddressPreviousHashSchema,
28
31
  AddressSchema,
32
+ creatableModule,
29
33
  DeadModuleError,
30
34
  isModuleName,
31
35
  isSerializable,
@@ -177,7 +181,8 @@ var SupportedQueryValidator = class {
177
181
 
178
182
  // src/AbstractModule.ts
179
183
  var MODULE_NOT_STARTED = "Module not Started";
180
- var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
184
+ creatableModule();
185
+ var AbstractModule = class _AbstractModule extends AbstractCreatable {
181
186
  static allowRandomAccount = true;
182
187
  static configSchemas = [ModuleConfigSchema];
183
188
  static defaultConfigSchema = ModuleConfigSchema;
@@ -191,23 +196,14 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
191
196
  _account;
192
197
  // cache manifest based on maxDepth
193
198
  _cachedManifests = new LRUCache({ max: 10, ttl: 1e3 * 60 * 5 });
194
- _globalReentrancyMutex = void 0;
199
+ _globalReentrancyMutex;
195
200
  _lastError;
196
- _startPromise = void 0;
197
- _started = void 0;
198
- moduleConfigQueryValidator;
199
- supportedQueryValidator;
201
+ _moduleConfigQueryValidator;
202
+ _startPromise;
203
+ _supportedQueryValidator;
200
204
  _busyCount = 0;
201
205
  _logger = void 0;
202
- _status = "stopped";
203
- constructor(privateConstructorKey, params, account) {
204
- assertEx2(_AbstractModule.privateConstructorKey === privateConstructorKey, () => "Use create function instead of constructor");
205
- const mutatedParams = { ...params };
206
- super(mutatedParams);
207
- this._account = account;
208
- this.supportedQueryValidator = new SupportedQueryValidator(this).queryable;
209
- this.moduleConfigQueryValidator = new ModuleConfigQueryValidator(mutatedParams?.config).queryable;
210
- }
206
+ _status = "creating";
211
207
  get account() {
212
208
  return assertEx2(this._account, () => "Missing account");
213
209
  }
@@ -215,7 +211,7 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
215
211
  return this.params.additionalSigners ?? [];
216
212
  }
217
213
  get address() {
218
- return this._account?.address;
214
+ return this.account.address;
219
215
  }
220
216
  get allowAnonymous() {
221
217
  return !!this.config.security?.allowAnonymous;
@@ -230,10 +226,10 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
230
226
  return this.config.archivist;
231
227
  }
232
228
  get config() {
233
- return this.params.config;
229
+ return { ...this.params.config, schema: this.params.config.schema ?? ModuleConfigSchema };
234
230
  }
235
231
  get dead() {
236
- return this.status === "dead";
232
+ return this.status === "error";
237
233
  }
238
234
  get ephemeralQueryAccountEnabled() {
239
235
  return !!this.params.ephemeralQueryAccountEnabled;
@@ -246,9 +242,12 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
246
242
  return this.modName ?? this.address;
247
243
  }
248
244
  get logger() {
249
- const logLevel = this.config.logLevel;
250
- this._logger = this._logger ?? this.params?.logger ?? (isDefined(logLevel) ? new ConsoleLogger(logLevel) : Base.defaultLogger);
251
- return this._logger;
245
+ if (isUndefined(this._logger)) {
246
+ const logLevel = this.config.logLevel;
247
+ const newLogger = this._logger ?? (this.params?.logger ? new IdLogger(this.params.logger, () => `${this.constructor.name}[${this.id}]`) : null);
248
+ this._logger = isObject(newLogger) && isDefined(logLevel) ? new LevelLogger(newLogger, logLevel) : newLogger;
249
+ }
250
+ return this._logger ?? void 0;
252
251
  }
253
252
  get modName() {
254
253
  return this.config.name;
@@ -271,11 +270,19 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
271
270
  get timestamp() {
272
271
  return this.config.timestamp ?? false;
273
272
  }
273
+ get moduleConfigQueryValidator() {
274
+ return assertEx2(this._moduleConfigQueryValidator, () => "ModuleConfigQueryValidator not initialized");
275
+ }
274
276
  set status(value) {
275
- if (this._status !== "dead") {
276
- this._status = value;
277
+ this._status = value;
278
+ if (value === "error") {
279
+ this.statusReporter?.report(`${this.constructor.name}:${this.id}`, value, new Error("Module status changed to error"));
280
+ } else {
281
+ this.statusReporter?.report(`${this.constructor.name}:${this.id}`, value, 100);
277
282
  }
278
- this.statusReporter?.reportStatus(`${this.constructor.name}:${this.id}`, value);
283
+ }
284
+ get supportedQueryValidator() {
285
+ return assertEx2(this._supportedQueryValidator, () => "SupportedQueryValidator not initialized");
279
286
  }
280
287
  static _getRootFunction(funcName) {
281
288
  let anyThis = this;
@@ -289,28 +296,19 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
289
296
  const rootFunc = this._getRootFunction(functionName);
290
297
  assertEx2(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`);
291
298
  }
292
- static async create(params) {
293
- this._noOverride("create");
294
- if (this.configSchemas.length === 0) {
295
- throw new Error(`Missing configSchema [${params?.config?.schema}][${this.name}]`);
296
- }
297
- assertEx2(params?.config?.name === void 0 || isModuleName(params.config.name), () => `Invalid module name: ${params?.config?.name}`);
298
- const { account } = params ?? {};
299
- const schema = params?.config?.schema ?? this.defaultConfigSchema;
300
- const allowedSchemas = this.configSchemas;
301
- assertEx2(allowedSchemas.includes(schema), () => `Bad Config Schema [Received ${schema}] [Expected ${JSON.stringify(allowedSchemas)}]`);
302
- const mutatedConfig = { ...params?.config, schema };
303
- params?.logger?.debug(`config: ${JSON.stringify(mutatedConfig, null, 2)}`);
304
- const mutatedParams = { ...params, config: mutatedConfig };
305
- const activeLogger = params?.logger ?? _AbstractModule.defaultLogger;
306
- const generatedAccount = await _AbstractModule.determineAccount({ account });
307
- const address = generatedAccount.address;
308
- mutatedParams.logger = new IdLogger(activeLogger, () => `0x${address}`);
309
- const newModule = new this(_AbstractModule.privateConstructorKey, mutatedParams, generatedAccount, address);
310
- if (!_AbstractModule.enableLazyLoad) {
311
- await newModule.start?.();
299
+ static async createHandler(inInstance) {
300
+ const instance = await super.createHandler(inInstance);
301
+ if (instance instanceof _AbstractModule) {
302
+ if (this.configSchemas.length === 0) {
303
+ throw new Error(`No allowed config schemas for [${this.name}]`);
304
+ }
305
+ const schema = instance.config.schema ?? this.defaultConfigSchema;
306
+ const allowedSchemas = this.configSchemas;
307
+ assertEx2(this.isAllowedSchema(schema), () => `Bad Config Schema [Received ${schema}] [Expected ${JSON.stringify(allowedSchemas)}]`);
308
+ } else {
309
+ throw new TypeError(`Invalid instance type [${instance.constructor.name}] for [${this.name}]`);
312
310
  }
313
- return newModule;
311
+ return instance;
314
312
  }
315
313
  static async determineAccount(params) {
316
314
  return await determineAccount(params, this.allowRandomAccount);
@@ -318,6 +316,19 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
318
316
  static factory(params) {
319
317
  return ModuleFactory.withParams(this, params);
320
318
  }
319
+ static isAllowedSchema(schema) {
320
+ return this.configSchemas.includes(schema);
321
+ }
322
+ static async paramsHandler(inParams = {}) {
323
+ const superParams = await super.paramsHandler(inParams);
324
+ const params = {
325
+ ...superParams,
326
+ account: await this.determineAccount(superParams),
327
+ config: { schema: this.defaultConfigSchema, ...superParams.config },
328
+ logger: superParams.logger ?? this.defaultLogger
329
+ };
330
+ return params;
331
+ }
321
332
  // eslint-disable-next-line sonarjs/no-identical-functions
322
333
  _getRootFunction(funcName) {
323
334
  let anyThis = this;
@@ -344,6 +355,21 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
344
355
  }
345
356
  }
346
357
  }
358
+ async createHandler() {
359
+ await super.createHandler();
360
+ assertEx2(this.name === void 0 || isModuleName(this.name), () => `Invalid module name: ${this.name}`);
361
+ if (this.params.account === "random") {
362
+ this._account = await Account2.random();
363
+ } else if (isAccountInstance(this.params.account)) {
364
+ this._account = this.params.account;
365
+ }
366
+ assertEx2(isAccountInstance(this._account), () => `Invalid account instance: ${this._account}`);
367
+ this._supportedQueryValidator = new SupportedQueryValidator(this).queryable;
368
+ this._moduleConfigQueryValidator = new ModuleConfigQueryValidator(this.config).queryable;
369
+ if (!_AbstractModule.enableLazyLoad) {
370
+ await this.start?.();
371
+ }
372
+ }
347
373
  emit(eventName, eventArgs) {
348
374
  return super.emit(eventName, eventArgs);
349
375
  }
@@ -422,40 +448,28 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
422
448
  }
423
449
  return true;
424
450
  }
425
- async start(timeout) {
426
- this._noOverride("start");
427
- this.status = "starting";
428
- let startingTime = 0;
429
- const startingStatus = setInterval(() => {
430
- startingTime += 1e3;
431
- this.statusReporter?.reportStatus(`${this.constructor.name}:${this.id}`, "starting", startingTime);
432
- }, 1e3);
433
- this._startPromise = this._startPromise ?? await this.startHandler(timeout);
434
- const result = await this._startPromise;
435
- this.status = result ? "started" : "dead";
436
- clearInterval(startingStatus);
437
- return result;
438
- }
439
451
  async started(notStartedAction = "log", tryStart = true) {
440
- if (isDefined(this._started) && await this._started === true) {
452
+ if (isString(this.status) && this.status === "started") {
441
453
  return true;
442
454
  }
443
- if (isUndefined(this._started)) {
444
- this._started = (async () => {
455
+ if (this.status === "created" || this.status === "stopped") {
456
+ this._startPromise = this._startPromise ?? (async () => {
445
457
  if (tryStart) {
446
458
  try {
447
459
  await this.start();
448
460
  return true;
449
461
  } catch (ex) {
450
462
  handleError(ex, (error) => {
451
- this.logger?.warn(`Autostart of Module Failed: ${error.message})`);
452
- this._started = void 0;
463
+ this.status = "error";
464
+ this.logger?.warn(`Autostart of Module Failed: ${error.message}`);
453
465
  });
466
+ } finally {
467
+ this._startPromise = void 0;
454
468
  }
455
469
  }
456
470
  switch (notStartedAction) {
457
471
  case "throw": {
458
- throw new Error(`${MODULE_NOT_STARTED} [${this.address}]`);
472
+ throw new Error(`${MODULE_NOT_STARTED} [${this.address}] current state: ${this.status}`);
459
473
  }
460
474
  case "warn": {
461
475
  this.logger?.warn(MODULE_NOT_STARTED);
@@ -476,34 +490,16 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
476
490
  return false;
477
491
  })();
478
492
  }
479
- if (isUndefined(this._started)) {
480
- throw "Failed to create start promise";
493
+ if (isUndefined(this._startPromise)) {
494
+ throw new Error(`Failed to create start promise: ${this.status}`);
481
495
  }
482
- return await this._started;
483
- }
484
- async stop(_timeout) {
485
- this._noOverride("stop");
486
- return await spanAsync("start", async () => {
487
- return await this.busy(async () => {
488
- const result = await this.stopHandler();
489
- this._started = void 0;
490
- this._startPromise = void 0;
491
- this.status = result ? "stopped" : "dead";
492
- return result;
493
- });
494
- }, this.tracer);
496
+ return await this._startPromise;
495
497
  }
496
498
  _checkDead() {
497
499
  if (this.dead) {
498
500
  throw new DeadModuleError(this.id, this._lastError);
499
501
  }
500
502
  }
501
- // eslint-disable-next-line sonarjs/no-identical-functions
502
- _noOverride(functionName) {
503
- const thisFunc = this[functionName];
504
- const rootFunc = this._getRootFunction(functionName);
505
- assertEx2(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`);
506
- }
507
503
  async archivistInstance(required = false) {
508
504
  const archivist = this.archivist;
509
505
  if (isUndefined(archivist)) {
@@ -644,18 +640,16 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
644
640
  }
645
641
  return PayloadBuilder2.omitPrivateStorageMeta(resultPayloads);
646
642
  }
647
- async startHandler(_timeout) {
643
+ async startHandler() {
648
644
  this.validateConfig();
649
- await Promise.resolve();
650
- this._started = true;
651
- return true;
645
+ await super.startHandler();
652
646
  }
653
647
  async stateHandler() {
654
648
  return [await this.manifestHandler(), ...await this.generateConfigAndAddress(), await this.generateDescribe()];
655
649
  }
656
- stopHandler(_timeout) {
657
- this._started = void 0;
658
- return true;
650
+ async stopHandler() {
651
+ await super.stopHandler();
652
+ this._startPromise = void 0;
659
653
  }
660
654
  subscribeHandler() {
661
655
  return;
@@ -691,7 +685,6 @@ var AbstractModule = class _AbstractModule extends ModuleBaseEmitter {
691
685
  };
692
686
 
693
687
  // src/AbstractModuleInstance.ts
694
- import { assertEx as assertEx3 } from "@xylabs/assert";
695
688
  import { globallyUnique as globallyUnique2 } from "@xylabs/base";
696
689
  import { exists as exists2 } from "@xylabs/exists";
697
690
  import { isDefined as isDefined2 } from "@xylabs/typeof";
@@ -726,16 +719,6 @@ var AbstractModuleInstance = class _AbstractModuleInstance extends AbstractModul
726
719
  _parents = [];
727
720
  _privateResolver;
728
721
  _upResolver;
729
- constructor(privateConstructorKey, params, account) {
730
- assertEx3(AbstractModule.privateConstructorKey === privateConstructorKey, () => "Use create function instead of constructor");
731
- const mutatedParams = { ...params };
732
- const addToResolvers = mutatedParams.addToResolvers ?? true;
733
- super(privateConstructorKey, mutatedParams, account);
734
- if (addToResolvers) {
735
- this.upResolver.add(this);
736
- this.downResolver.add(this);
737
- }
738
- }
739
722
  get downResolver() {
740
723
  this._downResolver = this._downResolver ?? new CompositeModuleResolver({
741
724
  allowNameResolution: this.allowNameResolution,
@@ -772,7 +755,7 @@ var AbstractModuleInstance = class _AbstractModuleInstance extends AbstractModul
772
755
  addParent(mod) {
773
756
  const existingEntry = this._parents.find((parent) => parent.address === mod.address);
774
757
  if (!existingEntry) {
775
- this._parents.push(asNodeInstance(mod, "Only NodeInstances can be parents"));
758
+ this._parents.push(asNodeInstance(mod, "Only NodeInstances can be parents", { required: true }));
776
759
  }
777
760
  }
778
761
  async certifyParents() {
@@ -784,6 +767,16 @@ var AbstractModuleInstance = class _AbstractModuleInstance extends AbstractModul
784
767
  })
785
768
  )).flat();
786
769
  }
770
+ async createHandler() {
771
+ this.status = "creating";
772
+ await super.createHandler();
773
+ const addToResolvers = this.params.addToResolvers ?? true;
774
+ if (addToResolvers) {
775
+ this.upResolver.add(this);
776
+ this.downResolver.add(this);
777
+ }
778
+ this.status = "created";
779
+ }
787
780
  manifest(maxDepth) {
788
781
  this._checkDead();
789
782
  return this.busy(async () => {
@@ -862,12 +855,6 @@ var AbstractModuleInstance = class _AbstractModuleInstance extends AbstractModul
862
855
  async siblings() {
863
856
  return (await Promise.all((await this.parents()).map((parent) => parent.publicChildren()))).flat().filter(duplicateModules);
864
857
  }
865
- /* override start(_timeout?: number): Promisable<boolean> {
866
- if (this.parents.length === 0) {
867
- this.logger.warn(`Module is being started without being attached to a parent: ${this.id} [${this.address}]`)
868
- }
869
- return super.start()
870
- } */
871
858
  state() {
872
859
  this._checkDead();
873
860
  return this.busy(async () => {
@@ -897,6 +884,7 @@ var AbstractModuleInstance = class _AbstractModuleInstance extends AbstractModul
897
884
  }
898
885
  const result = {
899
886
  config: { name: modName, ...this.config },
887
+ name: modName,
900
888
  schema: ModuleManifestPayloadSchema,
901
889
  status: { address: this.address, children: childAddressToName }
902
890
  };
@@ -921,6 +909,17 @@ var AbstractModuleInstance = class _AbstractModuleInstance extends AbstractModul
921
909
  const query = await this.bindQuery(queryPayload, payloads, account, this.additionalSigners);
922
910
  return await this.query(query[0], query[1]);
923
911
  }
912
+ startHandler() {
913
+ this._checkDead();
914
+ return this.busy(async () => {
915
+ if (this.status === "started" || this.status === "creating") {
916
+ return;
917
+ }
918
+ this.status = "starting";
919
+ await super.startHandler();
920
+ this.status = "started";
921
+ });
922
+ }
924
923
  async storeToArchivists(payloads) {
925
924
  try {
926
925
  const archivists = await this.resolveArchivingArchivists();
@@ -944,7 +943,7 @@ var LoggerModuleStatusReporter = class {
944
943
  constructor(logger) {
945
944
  this.logger = logger;
946
945
  }
947
- reportStatus(name, status, progress) {
946
+ report(name, status, progress) {
948
947
  this.statusMap[name] = status;
949
948
  const starting = Object.entries(this.statusMap).map(([, value]) => value === "starting" ? 1 : 0).reduce((a, b) => a + b, 0);
950
949
  const started = Object.entries(this.statusMap).map(([, value]) => value === "started" ? 1 : 0).reduce((a, b) => a + b, 0);