@xylabs/creatable 5.0.13 → 5.0.15

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.
@@ -3,13 +3,17 @@ import { BaseEmitter } from '@xylabs/events';
3
3
  import { type Logger } from '@xylabs/logger';
4
4
  import type { Promisable } from '@xylabs/promise';
5
5
  import { type Creatable, CreatableFactory } from './Creatable.ts';
6
- import type { CreatableInstance, CreatableName, CreatableParams, Labels } from './model/index.ts';
6
+ import type { CreatableInstance, CreatableName, CreatableParams, CreatableStatus, Labels } from './model/index.ts';
7
7
  export declare class AbstractCreatable<TParams extends CreatableParams = CreatableParams, TEventData extends EventData = EventData> extends BaseEmitter<Partial<TParams>, TEventData> {
8
8
  defaultLogger?: Logger;
9
+ private _status;
10
+ private _statusMutex;
9
11
  private _validatedParams?;
10
12
  constructor(key: unknown, params: Partial<TParams>);
11
13
  get name(): CreatableName;
12
14
  get params(): TParams;
15
+ get startable(): boolean;
16
+ get status(): CreatableStatus | null;
13
17
  get statusReporter(): import("./model/CreatableStatusReporter.ts").CreatableStatusReporter<void> | undefined;
14
18
  static create<T extends CreatableInstance>(this: Creatable<T>, inParams?: Partial<T['params']>): Promise<T>;
15
19
  static createHandler<T extends CreatableInstance>(this: Creatable<T>, instance: T): Promisable<T>;
@@ -19,6 +23,8 @@ export declare class AbstractCreatable<TParams extends CreatableParams = Creatab
19
23
  start(): Promise<boolean>;
20
24
  stop(): Promise<boolean>;
21
25
  protected _noOverride(functionName?: string): void;
26
+ protected setStatus(value: Exclude<CreatableStatus, 'error'>, progress?: number): void;
27
+ protected setStatus(value: Extract<CreatableStatus, 'error'>, error?: Error): void;
22
28
  protected startHandler(): Promisable<void>;
23
29
  protected stopHandler(): Promisable<void>;
24
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractCreatable.d.ts","sourceRoot":"","sources":["../../src/AbstractCreatable.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAGjD,OAAO,EACL,KAAK,SAAS,EAAa,gBAAgB,EAC5C,MAAM,gBAAgB,CAAA;AAGvB,OAAO,KAAK,EACV,iBAAiB,EAAE,aAAa,EAAE,eAAe,EACjD,MAAM,EACP,MAAM,kBAAkB,CAAA;AAIzB,qBACa,iBAAiB,CAAC,OAAO,SAAS,eAAe,GAAG,eAAe,EAC9E,UAAU,SAAS,SAAS,GAAG,SAAS,CAAE,SAAQ,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,OAAO,CAAC,gBAAgB,CAAC,CAAS;gBAEtB,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC;IAKlD,IAAI,IAAI,IAAI,aAAa,CAExB;IAED,IAAa,MAAM,IAAI,OAAO,CAG7B;IAED,IAAI,cAAc,2FAEjB;WAEY,MAAM,CAAC,CAAC,SAAS,iBAAiB,EAC7C,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,QAAQ,GAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAM,GAClC,OAAO,CAAC,CAAC,CAAC;IAgBb,MAAM,CAAC,aAAa,CAAC,CAAC,SAAS,iBAAiB,EAC9C,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,QAAQ,EAAE,CAAC,GACV,UAAU,CAAC,CAAC,CAAC;IAIhB,MAAM,CAAC,aAAa,CAAC,CAAC,SAAS,iBAAiB,EAC9C,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,MAAM,GAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAM,GAChC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAI1B,aAAa,IAAI,UAAU,CAAC,IAAI,CAAC;IAEjC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAI5C,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAazB,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAa9B,SAAS,CAAC,WAAW,CAAC,YAAY,SAAqB;IAQvD,SAAS,CAAC,YAAY,IAAI,UAAU,CAAC,IAAI,CAAC;IAI1C,SAAS,CAAC,WAAW,IAAI,UAAU,CAAC,IAAI,CAAC;CAG1C;AAED,qBACa,4BAA4B,CAAC,OAAO,SAAS,eAAe,GAAG,eAAe,EACzF,UAAU,SAAS,SAAS,GAAG,SAAS,CAAE,SAAQ,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC;IACxF,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,iBAAiB,EACxC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAC7B,MAAM,CAAC,EAAE,MAAM,GACd,gBAAgB,CAAC,CAAC,CAAC;CAGvB"}
1
+ {"version":3,"file":"AbstractCreatable.d.ts","sourceRoot":"","sources":["../../src/AbstractCreatable.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAIjD,OAAO,EACL,KAAK,SAAS,EAAa,gBAAgB,EAC5C,MAAM,gBAAgB,CAAA;AAGvB,OAAO,KAAK,EACV,iBAAiB,EAAE,aAAa,EAAE,eAAe,EACjD,eAAe,EACf,MAAM,EACP,MAAM,kBAAkB,CAAA;AAIzB,qBACa,iBAAiB,CAAC,OAAO,SAAS,eAAe,GAAG,eAAe,EAC9E,UAAU,SAAS,SAAS,GAAG,SAAS,CAAE,SAAQ,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;IAC3F,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,OAAO,CAAC,OAAO,CAA+B;IAC9C,OAAO,CAAC,YAAY,CAAc;IAClC,OAAO,CAAC,gBAAgB,CAAC,CAAS;gBAEtB,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC;IAKlD,IAAI,IAAI,IAAI,aAAa,CAExB;IAED,IAAa,MAAM,IAAI,OAAO,CAG7B;IAED,IAAI,SAAS,YAEZ;IAED,IAAI,MAAM,IAAI,eAAe,GAAG,IAAI,CAEnC;IAED,IAAI,cAAc,2FAEjB;WAEY,MAAM,CAAC,CAAC,SAAS,iBAAiB,EAC7C,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,QAAQ,GAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAM,GAClC,OAAO,CAAC,CAAC,CAAC;IAgBb,MAAM,CAAC,aAAa,CAAC,CAAC,SAAS,iBAAiB,EAC9C,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,QAAQ,EAAE,CAAC,GACV,UAAU,CAAC,CAAC,CAAC;IAIhB,MAAM,CAAC,aAAa,CAAC,CAAC,SAAS,iBAAiB,EAC9C,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,MAAM,GAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAM,GAChC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAI1B,aAAa,IAAI,UAAU,CAAC,IAAI,CAAC;IAIjC,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO;IAI5C,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;IAuBzB,IAAI,IAAI,OAAO,CAAC,OAAO,CAAC;IAuB9B,SAAS,CAAC,WAAW,CAAC,YAAY,SAAqB;IAQvD,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI;IACtF,SAAS,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,eAAe,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI;IAmBlF,SAAS,CAAC,YAAY,IAAI,UAAU,CAAC,IAAI,CAAC;IAI1C,SAAS,CAAC,WAAW,IAAI,UAAU,CAAC,IAAI,CAAC;CAG1C;AAED,qBACa,4BAA4B,CAAC,OAAO,SAAS,eAAe,GAAG,eAAe,EACzF,UAAU,SAAS,SAAS,GAAG,SAAS,CAAE,SAAQ,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC;IACxF,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,iBAAiB,EACxC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAC7B,MAAM,CAAC,EAAE,MAAM,GACd,gBAAgB,CAAC,CAAC,CAAC;CAGvB"}
@@ -12,7 +12,8 @@ var __decorateClass = (decorators, target, key, kind) => {
12
12
  // src/AbstractCreatable.ts
13
13
  import { assertEx } from "@xylabs/assert";
14
14
  import { BaseEmitter } from "@xylabs/events";
15
- import { isError } from "@xylabs/typeof";
15
+ import { isError, isNumber as isNumber2 } from "@xylabs/typeof";
16
+ import { Mutex } from "async-mutex";
16
17
 
17
18
  // src/Creatable.ts
18
19
  function creatable() {
@@ -78,6 +79,8 @@ function getRootFunction(obj, funcName) {
78
79
  var AbstractCreatableConstructorKey = Symbol.for("AbstractCreatableConstructor");
79
80
  var AbstractCreatable = class extends BaseEmitter {
80
81
  defaultLogger;
82
+ _status = null;
83
+ _statusMutex = new Mutex();
81
84
  _validatedParams;
82
85
  constructor(key, params) {
83
86
  assertEx(key === AbstractCreatableConstructorKey, () => "AbstractCreatable should not be instantiated directly, use the static create method instead");
@@ -90,18 +93,24 @@ var AbstractCreatable = class extends BaseEmitter {
90
93
  this._validatedParams = this._validatedParams ?? this.paramsValidator(super.params);
91
94
  return this._validatedParams;
92
95
  }
96
+ get startable() {
97
+ return this.status === "created" || this.status === "stopped";
98
+ }
99
+ get status() {
100
+ return this._status;
101
+ }
93
102
  get statusReporter() {
94
103
  return this.params.statusReporter;
95
104
  }
96
105
  static async create(inParams = {}) {
97
106
  const params = await this.paramsHandler(inParams);
98
107
  const name = params.name ?? this.name;
99
- params.statusReporter?.report(name, "creating");
100
108
  try {
101
109
  const instance = new this(AbstractCreatableConstructorKey, params);
110
+ instance.setStatus("creating");
102
111
  const initializedInstance = await this.createHandler(instance);
103
112
  await instance.createHandler();
104
- params.statusReporter?.report(name, "created");
113
+ instance.setStatus("created");
105
114
  return initializedInstance;
106
115
  } catch (ex) {
107
116
  params.statusReporter?.report(name, "error", isError(ex) ? ex : new Error(`Error creating: ${name}`));
@@ -115,39 +124,75 @@ var AbstractCreatable = class extends BaseEmitter {
115
124
  return { ...params };
116
125
  }
117
126
  createHandler() {
127
+ assertEx(this._status === "creating", () => `createHandler can not be called [status = ${this.status}]`);
118
128
  }
119
129
  paramsValidator(params) {
120
130
  return { ...params, name: params.name ?? this.constructor.name };
121
131
  }
122
132
  async start() {
123
133
  this._noOverride("start");
124
- try {
125
- this.statusReporter?.report(this.name, "starting");
126
- await this.startHandler();
127
- this.statusReporter?.report(this.name, "started");
134
+ if (this.status === "started") {
128
135
  return true;
129
- } catch (ex) {
130
- this.statusReporter?.report(this.name, "error", isError(ex) ? ex : new Error(`Error stopping: ${ex}`));
131
- return false;
132
136
  }
137
+ return await this._statusMutex.runExclusive(async () => {
138
+ try {
139
+ if (this.status === "started") {
140
+ return true;
141
+ }
142
+ assertEx(this.startable, () => `Creatable [${this.name}] is not startable in status [${this.status}]`);
143
+ this.setStatus("starting");
144
+ await this.startHandler();
145
+ this.setStatus("started");
146
+ return true;
147
+ } catch (ex) {
148
+ this.setStatus("error", isError(ex) ? ex : new Error(`Error starting: ${ex}`));
149
+ return false;
150
+ }
151
+ });
133
152
  }
134
153
  async stop() {
135
154
  this._noOverride("stop");
136
- try {
137
- this.statusReporter?.report(this.name, "stopping");
138
- await this.stopHandler();
139
- this.statusReporter?.report(this.name, "stopped");
155
+ if (this.status === "stopped") {
140
156
  return true;
141
- } catch (ex) {
142
- this.statusReporter?.report(this.name, "error", isError(ex) ? ex : new Error(`Error stopping: ${ex}`));
143
- return false;
144
157
  }
158
+ return await this._statusMutex.runExclusive(async () => {
159
+ try {
160
+ if (this.status === "stopped") {
161
+ return true;
162
+ }
163
+ assertEx(this.status === "started", () => `Creatable [${this.name}] is not stoppable in status [${this.status}]`);
164
+ this.setStatus("stopping");
165
+ await this.stopHandler();
166
+ this.setStatus("stopped");
167
+ return true;
168
+ } catch (ex) {
169
+ this.setStatus("error", isError(ex) ? ex : new Error(`Error stopping: ${ex}`));
170
+ return false;
171
+ }
172
+ });
145
173
  }
146
174
  _noOverride(functionName = getFunctionName(3)) {
147
175
  const thisFunc = this[functionName];
148
176
  const rootFunc = getRootFunction(this, functionName);
149
177
  assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`);
150
178
  }
179
+ setStatus(value, progressOrError) {
180
+ this._status = value;
181
+ if (value !== null) {
182
+ if (value === "error") {
183
+ if (isError(progressOrError)) {
184
+ this.statusReporter?.report(this.name, value, progressOrError);
185
+ return;
186
+ }
187
+ } else {
188
+ if (isNumber2(progressOrError)) {
189
+ this.statusReporter?.report(this.name, value, progressOrError);
190
+ return;
191
+ }
192
+ }
193
+ this.statusReporter?.report(this.name, value);
194
+ }
195
+ }
151
196
  startHandler() {
152
197
  }
153
198
  stopHandler() {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/AbstractCreatable.ts","../../src/Creatable.ts","../../src/Factory.ts","../../src/lib/getFunctionName.ts","../../src/lib/getRootFunction.ts","../../src/model/Labels.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport type { EventData } from '@xylabs/events'\nimport { BaseEmitter } from '@xylabs/events'\nimport { type Logger } from '@xylabs/logger'\nimport type { Promisable } from '@xylabs/promise'\nimport { isError } from '@xylabs/typeof'\n\nimport {\n type Creatable, creatable, CreatableFactory,\n} from './Creatable.ts'\nimport { Factory } from './Factory.ts'\nimport { getFunctionName, getRootFunction } from './lib/index.ts'\nimport type {\n CreatableInstance, CreatableName, CreatableParams,\n Labels,\n} from './model/index.ts'\n\nconst AbstractCreatableConstructorKey = Symbol.for('AbstractCreatableConstructor')\n\n@creatable()\nexport class AbstractCreatable<TParams extends CreatableParams = CreatableParams,\n TEventData extends EventData = EventData> extends BaseEmitter<Partial<TParams>, TEventData> {\n defaultLogger?: Logger\n\n private _validatedParams?: TParams\n\n constructor(key: unknown, params: Partial<TParams>) {\n assertEx(key === AbstractCreatableConstructorKey, () => 'AbstractCreatable should not be instantiated directly, use the static create method instead')\n super(params)\n }\n\n get name(): CreatableName {\n return this.params.name ?? this.constructor.name as CreatableName\n }\n\n override get params(): TParams {\n this._validatedParams = this._validatedParams ?? this.paramsValidator(super.params)\n return this._validatedParams\n }\n\n get statusReporter() {\n return this.params.statusReporter\n }\n\n static async create<T extends CreatableInstance>(\n this: Creatable<T>,\n inParams: Partial<T['params']> = {},\n ): Promise<T> {\n const params = await this.paramsHandler(inParams)\n const name: CreatableName = params.name ?? this.name as CreatableName\n params.statusReporter?.report(name, 'creating')\n try {\n const instance = new this(AbstractCreatableConstructorKey, params)\n const initializedInstance = await this.createHandler(instance)\n await instance.createHandler()\n params.statusReporter?.report(name, 'created')\n return initializedInstance\n } catch (ex) {\n params.statusReporter?.report(name, 'error', isError(ex) ? ex : new Error(`Error creating: ${name}`))\n throw isError(ex) ? ex : new Error(`Error creating: ${name}`)\n }\n }\n\n static createHandler<T extends CreatableInstance>(\n this: Creatable<T>,\n instance: T,\n ): Promisable<T> {\n return instance\n }\n\n static paramsHandler<T extends CreatableInstance>(\n this: Creatable<T>,\n params: Partial<T['params']> = {},\n ): Promisable<T['params']> {\n return { ...params }\n }\n\n createHandler(): Promisable<void> {}\n\n paramsValidator(params: Partial<TParams>): TParams {\n return { ...params, name: params.name ?? this.constructor.name } as TParams\n }\n\n async start(): Promise<boolean> {\n this._noOverride('start')\n try {\n this.statusReporter?.report(this.name, 'starting')\n await this.startHandler()\n this.statusReporter?.report(this.name, 'started')\n return true\n } catch (ex) {\n this.statusReporter?.report(this.name, 'error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))\n return false\n }\n }\n\n async stop(): Promise<boolean> {\n this._noOverride('stop')\n try {\n this.statusReporter?.report(this.name, 'stopping')\n await this.stopHandler()\n this.statusReporter?.report(this.name, 'stopped')\n return true\n } catch (ex) {\n this.statusReporter?.report(this.name, 'error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))\n return false\n }\n }\n\n protected _noOverride(functionName = getFunctionName(3)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const thisFunc = (this as any)[functionName]\n\n const rootFunc = getRootFunction(this, functionName)\n assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)\n }\n\n protected startHandler(): Promisable<void> {\n // when overriding this, throw an error on failure\n }\n\n protected stopHandler(): Promisable<void> {\n // when overriding this, throw an error on failure\n }\n}\n\n@creatable()\nexport class AbstractCreatableWithFactory<TParams extends CreatableParams = CreatableParams,\n TEventData extends EventData = EventData> extends AbstractCreatable<TParams, TEventData> {\n static factory<T extends CreatableInstance>(\n this: Creatable<T>,\n params?: Partial<T['params']>,\n labels?: Labels,\n ): CreatableFactory<T> {\n return Factory.withParams<T>(this, params, labels)\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { Promisable } from '@xylabs/promise'\n\nimport type { AbstractCreatable } from './AbstractCreatable.ts'\nimport type {\n CreatableInstance, CreatableParams, Labels,\n} from './model/index.ts'\n\nexport interface CreatableFactory<T extends CreatableInstance = CreatableInstance>\n extends Omit<Creatable<T>, 'create' | 'createHandler' | 'paramsHandler' | 'defaultLogger' | 'factory'> {\n\n create(\n this: CreatableFactory<T>,\n params?: Partial<T['params']>): Promise<T>\n}\n\nexport interface Creatable<T extends CreatableInstance = CreatableInstance> {\n\n defaultLogger?: Logger\n\n new(key: unknown, params: Partial<CreatableParams>): T & AbstractCreatable<T['params']>\n\n create<T extends CreatableInstance>(\n this: Creatable<T>,\n params?: Partial<T['params']>): Promise<T>\n\n createHandler<T extends CreatableInstance>(\n this: Creatable<T>,\n instance: T\n ): Promisable<T>\n\n paramsHandler<T extends CreatableInstance>(\n this: Creatable<T>, params?: Partial<T['params']>): Promisable<T['params']>\n}\n\nexport interface CreatableWithFactory<T extends CreatableInstance = CreatableInstance> extends Creatable<T> {\n factory<T extends CreatableInstance>(\n this: Creatable<T>,\n params?: Partial<T['params']>,\n labels?: Labels): CreatableFactory<T>\n}\n\n/**\n * Class annotation to be used to decorate Modules which support\n * an asynchronous creation pattern\n * @returns The decorated Module requiring it implement the members\n * of the CreatableModule as statics properties/methods\n */\nexport function creatable<T extends CreatableInstance>() {\n return <U extends Creatable<T>>(constructor: U) => {\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n constructor\n }\n}\n\n/**\n * Class annotation to be used to decorate Modules which support\n * an asynchronous creation factory pattern\n * @returns The decorated Module requiring it implement the members\n * of the CreatableModule as statics properties/methods\n */\n\nexport function creatableFactory() {\n return <U extends CreatableFactory>(constructor: U) => {\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n constructor\n }\n}\n","import { type Creatable, type CreatableFactory } from './Creatable.ts'\nimport type {\n CreatableInstance, Labels, WithOptionalLabels,\n} from './model/index.ts'\n\nexport class Factory<T extends CreatableInstance = CreatableInstance> implements CreatableFactory<T> {\n creatable: Creatable<T>\n\n defaultParams?: Partial<T['params']>\n\n labels?: Labels\n\n constructor(\n creatable: Creatable<T>,\n params?: Partial<T['params']>,\n labels: Labels = {},\n ) {\n this.creatable = creatable\n this.defaultParams = params\n this.labels = Object.assign({}, (creatable as WithOptionalLabels).labels ?? {}, labels ?? {})\n }\n\n static withParams<T extends CreatableInstance>(\n creatableModule: Creatable<T>,\n params?: Partial<T['params']>,\n labels: Labels = {},\n ) {\n return new Factory<T>(creatableModule, params, labels)\n }\n\n create(params?: Partial<T['params']>): Promise<T> {\n const mergedParams: T['params'] = {\n ...this.defaultParams,\n ...params,\n } as T['params']\n return this.creatable.create<T>(mergedParams)\n }\n}\n","import { isNumber } from '@xylabs/typeof'\n\nexport function getFunctionName(depth = 2) {\n const error = new Error('Stack')\n let newIndex: number | undefined\n const stackParts = error.stack?.split('\\n')[depth]?.split(' ')\n const funcName\n = stackParts?.find((item, index) => {\n if (item.length > 0 && item !== 'at') {\n // check if constructor\n if (item === 'new') {\n newIndex = index\n }\n return true\n }\n }) ?? '<unknown>'\n return isNumber(newIndex) ? `${funcName} ${stackParts?.[newIndex + 1]}` : funcName\n}\n","export function getRootFunction(obj: unknown, funcName: string) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let anyObj = obj as any\n while (anyObj.__proto__?.[funcName]) {\n anyObj = anyObj.__proto__\n }\n return anyObj[funcName]\n}\n","/**\n * Object used to represent labels identifying a resource.\n */\nexport interface Labels {\n [key: string]: string | undefined\n}\n\n/**\n * Interface for objects that have labels.\n */\nexport interface WithLabels<T extends Labels = Labels> {\n labels: T\n}\n\n/**\n * Interface for objects that have labels.\n */\nexport interface WithOptionalLabels<T extends Labels = Labels> {\n labels?: T\n}\n\n/**\n * Returns true if the source object has all the labels from the required set\n * @param source Source object to check against\n * @param required Set of labels to check for in source\n * @returns True of the source object has all the labels from the required set\n */\nexport const hasAllLabels = (source?: Labels, required?: Labels): boolean => {\n if (!required) return true\n return Object.entries(required).every(([key, value]) => {\n return source?.hasOwnProperty(key as keyof typeof source) && source?.[key as keyof typeof source] === value\n })\n}\n"],"mappings":"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","names":["creatable"]}
1
+ {"version":3,"sources":["../../src/AbstractCreatable.ts","../../src/Creatable.ts","../../src/Factory.ts","../../src/lib/getFunctionName.ts","../../src/lib/getRootFunction.ts","../../src/model/Labels.ts"],"sourcesContent":["import { assertEx } from '@xylabs/assert'\nimport type { EventData } from '@xylabs/events'\nimport { BaseEmitter } from '@xylabs/events'\nimport { type Logger } from '@xylabs/logger'\nimport type { Promisable } from '@xylabs/promise'\nimport { isError, isNumber } from '@xylabs/typeof'\nimport { Mutex } from 'async-mutex'\n\nimport {\n type Creatable, creatable, CreatableFactory,\n} from './Creatable.ts'\nimport { Factory } from './Factory.ts'\nimport { getFunctionName, getRootFunction } from './lib/index.ts'\nimport type {\n CreatableInstance, CreatableName, CreatableParams,\n CreatableStatus,\n Labels,\n} from './model/index.ts'\n\nconst AbstractCreatableConstructorKey = Symbol.for('AbstractCreatableConstructor')\n\n@creatable()\nexport class AbstractCreatable<TParams extends CreatableParams = CreatableParams,\n TEventData extends EventData = EventData> extends BaseEmitter<Partial<TParams>, TEventData> {\n defaultLogger?: Logger\n\n private _status: CreatableStatus | null = null\n private _statusMutex = new Mutex()\n private _validatedParams?: TParams\n\n constructor(key: unknown, params: Partial<TParams>) {\n assertEx(key === AbstractCreatableConstructorKey, () => 'AbstractCreatable should not be instantiated directly, use the static create method instead')\n super(params)\n }\n\n get name(): CreatableName {\n return this.params.name ?? this.constructor.name as CreatableName\n }\n\n override get params(): TParams {\n this._validatedParams = this._validatedParams ?? this.paramsValidator(super.params)\n return this._validatedParams\n }\n\n get startable() {\n return this.status === 'created' || this.status === 'stopped'\n }\n\n get status(): CreatableStatus | null {\n return this._status\n }\n\n get statusReporter() {\n return this.params.statusReporter\n }\n\n static async create<T extends CreatableInstance>(\n this: Creatable<T>,\n inParams: Partial<T['params']> = {},\n ): Promise<T> {\n const params = await this.paramsHandler(inParams)\n const name: CreatableName = params.name ?? this.name as CreatableName\n try {\n const instance = new this(AbstractCreatableConstructorKey, params)\n instance.setStatus('creating')\n const initializedInstance = await this.createHandler(instance)\n await instance.createHandler()\n instance.setStatus('created')\n return initializedInstance\n } catch (ex) {\n params.statusReporter?.report(name, 'error', isError(ex) ? ex : new Error(`Error creating: ${name}`))\n throw isError(ex) ? ex : new Error(`Error creating: ${name}`)\n }\n }\n\n static createHandler<T extends CreatableInstance>(\n this: Creatable<T>,\n instance: T,\n ): Promisable<T> {\n return instance\n }\n\n static paramsHandler<T extends CreatableInstance>(\n this: Creatable<T>,\n params: Partial<T['params']> = {},\n ): Promisable<T['params']> {\n return { ...params }\n }\n\n createHandler(): Promisable<void> {\n assertEx(this._status === 'creating', () => `createHandler can not be called [status = ${this.status}]`)\n }\n\n paramsValidator(params: Partial<TParams>): TParams {\n return { ...params, name: params.name ?? this.constructor.name } as TParams\n }\n\n async start(): Promise<boolean> {\n this._noOverride('start')\n if (this.status === 'started') {\n return true\n }\n return await this._statusMutex.runExclusive(async () => {\n try {\n // check again in case it changed\n if (this.status === 'started') {\n return true\n }\n assertEx(this.startable, () => `Creatable [${this.name}] is not startable in status [${this.status}]`)\n this.setStatus('starting')\n await this.startHandler()\n this.setStatus('started')\n return true\n } catch (ex) {\n this.setStatus('error', isError(ex) ? ex : new Error(`Error starting: ${ex}`))\n return false\n }\n })\n }\n\n async stop(): Promise<boolean> {\n this._noOverride('stop')\n if (this.status === 'stopped') {\n return true\n }\n return await this._statusMutex.runExclusive(async () => {\n try {\n // check again in case it changed\n if (this.status === 'stopped') {\n return true\n }\n assertEx(this.status === 'started', () => `Creatable [${this.name}] is not stoppable in status [${this.status}]`)\n this.setStatus('stopping')\n await this.stopHandler()\n this.setStatus('stopped')\n return true\n } catch (ex) {\n this.setStatus('error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))\n return false\n }\n })\n }\n\n protected _noOverride(functionName = getFunctionName(3)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const thisFunc = (this as any)[functionName]\n\n const rootFunc = getRootFunction(this, functionName)\n assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)\n }\n\n protected setStatus(value: Exclude<CreatableStatus, 'error'>, progress?: number): void\n protected setStatus(value: Extract<CreatableStatus, 'error'>, error?: Error): void\n protected setStatus(value: CreatableStatus, progressOrError?: number | Error): void {\n this._status = value\n if (value !== null) {\n if (value === 'error') {\n if (isError(progressOrError)) {\n this.statusReporter?.report(this.name, value, progressOrError)\n return\n }\n } else {\n if (isNumber(progressOrError)) {\n this.statusReporter?.report(this.name, value, progressOrError)\n return\n }\n }\n this.statusReporter?.report(this.name, value)\n }\n }\n\n protected startHandler(): Promisable<void> {\n // when overriding this, throw an error on failure\n }\n\n protected stopHandler(): Promisable<void> {\n // when overriding this, throw an error on failure\n }\n}\n\n@creatable()\nexport class AbstractCreatableWithFactory<TParams extends CreatableParams = CreatableParams,\n TEventData extends EventData = EventData> extends AbstractCreatable<TParams, TEventData> {\n static factory<T extends CreatableInstance>(\n this: Creatable<T>,\n params?: Partial<T['params']>,\n labels?: Labels,\n ): CreatableFactory<T> {\n return Factory.withParams<T>(this, params, labels)\n }\n}\n","import type { Logger } from '@xylabs/logger'\nimport type { Promisable } from '@xylabs/promise'\n\nimport type { AbstractCreatable } from './AbstractCreatable.ts'\nimport type {\n CreatableInstance, CreatableParams, Labels,\n} from './model/index.ts'\n\nexport interface CreatableFactory<T extends CreatableInstance = CreatableInstance>\n extends Omit<Creatable<T>, 'create' | 'createHandler' | 'paramsHandler' | 'defaultLogger' | 'factory'> {\n\n create(\n this: CreatableFactory<T>,\n params?: Partial<T['params']>): Promise<T>\n}\n\nexport interface Creatable<T extends CreatableInstance = CreatableInstance> {\n\n defaultLogger?: Logger\n\n new(key: unknown, params: Partial<CreatableParams>): T & AbstractCreatable<T['params']>\n\n create<T extends CreatableInstance>(\n this: Creatable<T>,\n params?: Partial<T['params']>): Promise<T>\n\n createHandler<T extends CreatableInstance>(\n this: Creatable<T>,\n instance: T\n ): Promisable<T>\n\n paramsHandler<T extends CreatableInstance>(\n this: Creatable<T>, params?: Partial<T['params']>): Promisable<T['params']>\n}\n\nexport interface CreatableWithFactory<T extends CreatableInstance = CreatableInstance> extends Creatable<T> {\n factory<T extends CreatableInstance>(\n this: Creatable<T>,\n params?: Partial<T['params']>,\n labels?: Labels): CreatableFactory<T>\n}\n\n/**\n * Class annotation to be used to decorate Modules which support\n * an asynchronous creation pattern\n * @returns The decorated Module requiring it implement the members\n * of the CreatableModule as statics properties/methods\n */\nexport function creatable<T extends CreatableInstance>() {\n return <U extends Creatable<T>>(constructor: U) => {\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n constructor\n }\n}\n\n/**\n * Class annotation to be used to decorate Modules which support\n * an asynchronous creation factory pattern\n * @returns The decorated Module requiring it implement the members\n * of the CreatableModule as statics properties/methods\n */\n\nexport function creatableFactory() {\n return <U extends CreatableFactory>(constructor: U) => {\n // eslint-disable-next-line @typescript-eslint/no-unused-expressions\n constructor\n }\n}\n","import { type Creatable, type CreatableFactory } from './Creatable.ts'\nimport type {\n CreatableInstance, Labels, WithOptionalLabels,\n} from './model/index.ts'\n\nexport class Factory<T extends CreatableInstance = CreatableInstance> implements CreatableFactory<T> {\n creatable: Creatable<T>\n\n defaultParams?: Partial<T['params']>\n\n labels?: Labels\n\n constructor(\n creatable: Creatable<T>,\n params?: Partial<T['params']>,\n labels: Labels = {},\n ) {\n this.creatable = creatable\n this.defaultParams = params\n this.labels = Object.assign({}, (creatable as WithOptionalLabels).labels ?? {}, labels ?? {})\n }\n\n static withParams<T extends CreatableInstance>(\n creatableModule: Creatable<T>,\n params?: Partial<T['params']>,\n labels: Labels = {},\n ) {\n return new Factory<T>(creatableModule, params, labels)\n }\n\n create(params?: Partial<T['params']>): Promise<T> {\n const mergedParams: T['params'] = {\n ...this.defaultParams,\n ...params,\n } as T['params']\n return this.creatable.create<T>(mergedParams)\n }\n}\n","import { isNumber } from '@xylabs/typeof'\n\nexport function getFunctionName(depth = 2) {\n const error = new Error('Stack')\n let newIndex: number | undefined\n const stackParts = error.stack?.split('\\n')[depth]?.split(' ')\n const funcName\n = stackParts?.find((item, index) => {\n if (item.length > 0 && item !== 'at') {\n // check if constructor\n if (item === 'new') {\n newIndex = index\n }\n return true\n }\n }) ?? '<unknown>'\n return isNumber(newIndex) ? `${funcName} ${stackParts?.[newIndex + 1]}` : funcName\n}\n","export function getRootFunction(obj: unknown, funcName: string) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let anyObj = obj as any\n while (anyObj.__proto__?.[funcName]) {\n anyObj = anyObj.__proto__\n }\n return anyObj[funcName]\n}\n","/**\n * Object used to represent labels identifying a resource.\n */\nexport interface Labels {\n [key: string]: string | undefined\n}\n\n/**\n * Interface for objects that have labels.\n */\nexport interface WithLabels<T extends Labels = Labels> {\n labels: T\n}\n\n/**\n * Interface for objects that have labels.\n */\nexport interface WithOptionalLabels<T extends Labels = Labels> {\n labels?: T\n}\n\n/**\n * Returns true if the source object has all the labels from the required set\n * @param source Source object to check against\n * @param required Set of labels to check for in source\n * @returns True of the source object has all the labels from the required set\n */\nexport const hasAllLabels = (source?: Labels, required?: Labels): boolean => {\n if (!required) return true\n return Object.entries(required).every(([key, value]) => {\n return source?.hasOwnProperty(key as keyof typeof source) && source?.[key as keyof typeof source] === value\n })\n}\n"],"mappings":";;;;;;;;;;;;AAAA,SAAS,gBAAgB;AAEzB,SAAS,mBAAmB;AAG5B,SAAS,SAAS,YAAAA,iBAAgB;AAClC,SAAS,aAAa;;;AC0Cf,SAAS,YAAyC;AACvD,SAAO,CAAyB,gBAAmB;AAEjD;AAAA,EACF;AACF;AASO,SAAS,mBAAmB;AACjC,SAAO,CAA6B,gBAAmB;AAErD;AAAA,EACF;AACF;;;AC9DO,IAAM,UAAN,MAAM,SAAwF;AAAA,EACnG;AAAA,EAEA;AAAA,EAEA;AAAA,EAEA,YACEC,YACA,QACA,SAAiB,CAAC,GAClB;AACA,SAAK,YAAYA;AACjB,SAAK,gBAAgB;AACrB,SAAK,SAAS,OAAO,OAAO,CAAC,GAAIA,WAAiC,UAAU,CAAC,GAAG,UAAU,CAAC,CAAC;AAAA,EAC9F;AAAA,EAEA,OAAO,WACL,iBACA,QACA,SAAiB,CAAC,GAClB;AACA,WAAO,IAAI,SAAW,iBAAiB,QAAQ,MAAM;AAAA,EACvD;AAAA,EAEA,OAAO,QAA2C;AAChD,UAAM,eAA4B;AAAA,MAChC,GAAG,KAAK;AAAA,MACR,GAAG;AAAA,IACL;AACA,WAAO,KAAK,UAAU,OAAU,YAAY;AAAA,EAC9C;AACF;;;ACrCA,SAAS,gBAAgB;AAElB,SAAS,gBAAgB,QAAQ,GAAG;AACzC,QAAM,QAAQ,IAAI,MAAM,OAAO;AAC/B,MAAI;AACJ,QAAM,aAAa,MAAM,OAAO,MAAM,IAAI,EAAE,KAAK,GAAG,MAAM,GAAG;AAC7D,QAAM,WACF,YAAY,KAAK,CAAC,MAAM,UAAU;AAClC,QAAI,KAAK,SAAS,KAAK,SAAS,MAAM;AAEpC,UAAI,SAAS,OAAO;AAClB,mBAAW;AAAA,MACb;AACA,aAAO;AAAA,IACT;AAAA,EACF,CAAC,KAAK;AACR,SAAO,SAAS,QAAQ,IAAI,GAAG,QAAQ,IAAI,aAAa,WAAW,CAAC,CAAC,KAAK;AAC5E;;;ACjBO,SAAS,gBAAgB,KAAc,UAAkB;AAE9D,MAAI,SAAS;AACb,SAAO,OAAO,YAAY,QAAQ,GAAG;AACnC,aAAS,OAAO;AAAA,EAClB;AACA,SAAO,OAAO,QAAQ;AACxB;;;AJYA,IAAM,kCAAkC,OAAO,IAAI,8BAA8B;AAG1E,IAAM,oBAAN,cAC6C,YAA0C;AAAA,EAC5F;AAAA,EAEQ,UAAkC;AAAA,EAClC,eAAe,IAAI,MAAM;AAAA,EACzB;AAAA,EAER,YAAY,KAAc,QAA0B;AAClD,aAAS,QAAQ,iCAAiC,MAAM,6FAA6F;AACrJ,UAAM,MAAM;AAAA,EACd;AAAA,EAEA,IAAI,OAAsB;AACxB,WAAO,KAAK,OAAO,QAAQ,KAAK,YAAY;AAAA,EAC9C;AAAA,EAEA,IAAa,SAAkB;AAC7B,SAAK,mBAAmB,KAAK,oBAAoB,KAAK,gBAAgB,MAAM,MAAM;AAClF,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,YAAY;AACd,WAAO,KAAK,WAAW,aAAa,KAAK,WAAW;AAAA,EACtD;AAAA,EAEA,IAAI,SAAiC;AACnC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,iBAAiB;AACnB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAEA,aAAa,OAEX,WAAiC,CAAC,GACtB;AACZ,UAAM,SAAS,MAAM,KAAK,cAAc,QAAQ;AAChD,UAAM,OAAsB,OAAO,QAAQ,KAAK;AAChD,QAAI;AACF,YAAM,WAAW,IAAI,KAAK,iCAAiC,MAAM;AACjE,eAAS,UAAU,UAAU;AAC7B,YAAM,sBAAsB,MAAM,KAAK,cAAc,QAAQ;AAC7D,YAAM,SAAS,cAAc;AAC7B,eAAS,UAAU,SAAS;AAC5B,aAAO;AAAA,IACT,SAAS,IAAI;AACX,aAAO,gBAAgB,OAAO,MAAM,SAAS,QAAQ,EAAE,IAAI,KAAK,IAAI,MAAM,mBAAmB,IAAI,EAAE,CAAC;AACpG,YAAM,QAAQ,EAAE,IAAI,KAAK,IAAI,MAAM,mBAAmB,IAAI,EAAE;AAAA,IAC9D;AAAA,EACF;AAAA,EAEA,OAAO,cAEL,UACe;AACf,WAAO;AAAA,EACT;AAAA,EAEA,OAAO,cAEL,SAA+B,CAAC,GACP;AACzB,WAAO,EAAE,GAAG,OAAO;AAAA,EACrB;AAAA,EAEA,gBAAkC;AAChC,aAAS,KAAK,YAAY,YAAY,MAAM,6CAA6C,KAAK,MAAM,GAAG;AAAA,EACzG;AAAA,EAEA,gBAAgB,QAAmC;AACjD,WAAO,EAAE,GAAG,QAAQ,MAAM,OAAO,QAAQ,KAAK,YAAY,KAAK;AAAA,EACjE;AAAA,EAEA,MAAM,QAA0B;AAC9B,SAAK,YAAY,OAAO;AACxB,QAAI,KAAK,WAAW,WAAW;AAC7B,aAAO;AAAA,IACT;AACA,WAAO,MAAM,KAAK,aAAa,aAAa,YAAY;AACtD,UAAI;AAEF,YAAI,KAAK,WAAW,WAAW;AAC7B,iBAAO;AAAA,QACT;AACA,iBAAS,KAAK,WAAW,MAAM,cAAc,KAAK,IAAI,iCAAiC,KAAK,MAAM,GAAG;AACrG,aAAK,UAAU,UAAU;AACzB,cAAM,KAAK,aAAa;AACxB,aAAK,UAAU,SAAS;AACxB,eAAO;AAAA,MACT,SAAS,IAAI;AACX,aAAK,UAAU,SAAS,QAAQ,EAAE,IAAI,KAAK,IAAI,MAAM,mBAAmB,EAAE,EAAE,CAAC;AAC7E,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,OAAyB;AAC7B,SAAK,YAAY,MAAM;AACvB,QAAI,KAAK,WAAW,WAAW;AAC7B,aAAO;AAAA,IACT;AACA,WAAO,MAAM,KAAK,aAAa,aAAa,YAAY;AACtD,UAAI;AAEF,YAAI,KAAK,WAAW,WAAW;AAC7B,iBAAO;AAAA,QACT;AACA,iBAAS,KAAK,WAAW,WAAW,MAAM,cAAc,KAAK,IAAI,iCAAiC,KAAK,MAAM,GAAG;AAChH,aAAK,UAAU,UAAU;AACzB,cAAM,KAAK,YAAY;AACvB,aAAK,UAAU,SAAS;AACxB,eAAO;AAAA,MACT,SAAS,IAAI;AACX,aAAK,UAAU,SAAS,QAAQ,EAAE,IAAI,KAAK,IAAI,MAAM,mBAAmB,EAAE,EAAE,CAAC;AAC7E,eAAO;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEU,YAAY,eAAe,gBAAgB,CAAC,GAAG;AAEvD,UAAM,WAAY,KAAa,YAAY;AAE3C,UAAM,WAAW,gBAAgB,MAAM,YAAY;AACnD,aAAS,aAAa,UAAU,MAAM,6BAA6B,YAAY,gBAAgB,YAAY,iBAAiB;AAAA,EAC9H;AAAA,EAIU,UAAU,OAAwB,iBAAwC;AAClF,SAAK,UAAU;AACf,QAAI,UAAU,MAAM;AAClB,UAAI,UAAU,SAAS;AACrB,YAAI,QAAQ,eAAe,GAAG;AAC5B,eAAK,gBAAgB,OAAO,KAAK,MAAM,OAAO,eAAe;AAC7D;AAAA,QACF;AAAA,MACF,OAAO;AACL,YAAIC,UAAS,eAAe,GAAG;AAC7B,eAAK,gBAAgB,OAAO,KAAK,MAAM,OAAO,eAAe;AAC7D;AAAA,QACF;AAAA,MACF;AACA,WAAK,gBAAgB,OAAO,KAAK,MAAM,KAAK;AAAA,IAC9C;AAAA,EACF;AAAA,EAEU,eAAiC;AAAA,EAE3C;AAAA,EAEU,cAAgC;AAAA,EAE1C;AACF;AA5Ja,oBAAN;AAAA,EADN,UAAU;AAAA,GACE;AA+JN,IAAM,+BAAN,cAC6C,kBAAuC;AAAA,EACzF,OAAO,QAEL,QACA,QACqB;AACrB,WAAO,QAAQ,WAAc,MAAM,QAAQ,MAAM;AAAA,EACnD;AACF;AATa,+BAAN;AAAA,EADN,UAAU;AAAA,GACE;;;AK1JN,IAAM,eAAe,CAAC,QAAiB,aAA+B;AAC3E,MAAI,CAAC,SAAU,QAAO;AACtB,SAAO,OAAO,QAAQ,QAAQ,EAAE,MAAM,CAAC,CAAC,KAAK,KAAK,MAAM;AACtD,WAAO,QAAQ,eAAe,GAA0B,KAAK,SAAS,GAA0B,MAAM;AAAA,EACxG,CAAC;AACH;","names":["isNumber","creatable","isNumber"]}
@@ -1,7 +1,8 @@
1
1
  import type { BaseClassName } from '@xylabs/base';
2
2
  export type CreatableStatus = 'creating' | 'created' | 'starting' | 'started' | 'stopping' | 'stopped' | 'error';
3
3
  export interface CreatableStatusReporter<T extends void | string = void> {
4
- report(name: BaseClassName, status: Exclude<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, progress?: number): void;
5
- report(name: BaseClassName, status: Extract<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, error?: Error): void;
4
+ report(name: BaseClassName, status: Exclude<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, progress: number): void;
5
+ report(name: BaseClassName, status: Extract<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, error: Error): void;
6
+ report(name: BaseClassName, status: T extends void ? CreatableStatus : CreatableStatus | T): void;
6
7
  }
7
8
  //# sourceMappingURL=CreatableStatusReporter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreatableStatusReporter.d.ts","sourceRoot":"","sources":["../../../src/model/CreatableStatusReporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAA;AAEhH,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,IAAI,GAAG,MAAM,GAAG,IAAI;IACrE,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,eAAe,GAAG,eAAe,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtI,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,eAAe,GAAG,eAAe,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,KAAK,GAAG,IAAI,CAAA;CACnI"}
1
+ {"version":3,"file":"CreatableStatusReporter.d.ts","sourceRoot":"","sources":["../../../src/model/CreatableStatusReporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAEjD,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAA;AAEhH,MAAM,WAAW,uBAAuB,CAAC,CAAC,SAAS,IAAI,GAAG,MAAM,GAAG,IAAI;IACrE,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,eAAe,GAAG,eAAe,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACrI,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,SAAS,IAAI,GAAG,eAAe,GAAG,eAAe,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IACjI,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,SAAS,IAAI,GAAG,eAAe,GAAG,eAAe,GAAG,CAAC,GAAG,IAAI,CAAA;CAClG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xylabs/creatable",
3
- "version": "5.0.13",
3
+ "version": "5.0.15",
4
4
  "description": "Base functionality used throughout XY Labs TypeScript/JavaScript libraries",
5
5
  "homepage": "https://xyo.network",
6
6
  "bugs": {
@@ -35,12 +35,13 @@
35
35
  "src"
36
36
  ],
37
37
  "dependencies": {
38
- "@xylabs/assert": "~5.0.13",
39
- "@xylabs/base": "~5.0.13",
40
- "@xylabs/events": "~5.0.13",
41
- "@xylabs/logger": "~5.0.13",
42
- "@xylabs/promise": "~5.0.13",
43
- "@xylabs/typeof": "~5.0.13"
38
+ "@xylabs/assert": "~5.0.15",
39
+ "@xylabs/base": "~5.0.15",
40
+ "@xylabs/events": "~5.0.15",
41
+ "@xylabs/logger": "~5.0.15",
42
+ "@xylabs/promise": "~5.0.15",
43
+ "@xylabs/typeof": "~5.0.15",
44
+ "async-mutex": "~0.5.0"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@xylabs/ts-scripts-yarn3": "~7.1.8",
@@ -3,7 +3,8 @@ import type { EventData } from '@xylabs/events'
3
3
  import { BaseEmitter } from '@xylabs/events'
4
4
  import { type Logger } from '@xylabs/logger'
5
5
  import type { Promisable } from '@xylabs/promise'
6
- import { isError } from '@xylabs/typeof'
6
+ import { isError, isNumber } from '@xylabs/typeof'
7
+ import { Mutex } from 'async-mutex'
7
8
 
8
9
  import {
9
10
  type Creatable, creatable, CreatableFactory,
@@ -12,6 +13,7 @@ import { Factory } from './Factory.ts'
12
13
  import { getFunctionName, getRootFunction } from './lib/index.ts'
13
14
  import type {
14
15
  CreatableInstance, CreatableName, CreatableParams,
16
+ CreatableStatus,
15
17
  Labels,
16
18
  } from './model/index.ts'
17
19
 
@@ -22,6 +24,8 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
22
24
  TEventData extends EventData = EventData> extends BaseEmitter<Partial<TParams>, TEventData> {
23
25
  defaultLogger?: Logger
24
26
 
27
+ private _status: CreatableStatus | null = null
28
+ private _statusMutex = new Mutex()
25
29
  private _validatedParams?: TParams
26
30
 
27
31
  constructor(key: unknown, params: Partial<TParams>) {
@@ -38,6 +42,14 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
38
42
  return this._validatedParams
39
43
  }
40
44
 
45
+ get startable() {
46
+ return this.status === 'created' || this.status === 'stopped'
47
+ }
48
+
49
+ get status(): CreatableStatus | null {
50
+ return this._status
51
+ }
52
+
41
53
  get statusReporter() {
42
54
  return this.params.statusReporter
43
55
  }
@@ -48,12 +60,12 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
48
60
  ): Promise<T> {
49
61
  const params = await this.paramsHandler(inParams)
50
62
  const name: CreatableName = params.name ?? this.name as CreatableName
51
- params.statusReporter?.report(name, 'creating')
52
63
  try {
53
64
  const instance = new this(AbstractCreatableConstructorKey, params)
65
+ instance.setStatus('creating')
54
66
  const initializedInstance = await this.createHandler(instance)
55
67
  await instance.createHandler()
56
- params.statusReporter?.report(name, 'created')
68
+ instance.setStatus('created')
57
69
  return initializedInstance
58
70
  } catch (ex) {
59
71
  params.statusReporter?.report(name, 'error', isError(ex) ? ex : new Error(`Error creating: ${name}`))
@@ -75,7 +87,9 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
75
87
  return { ...params }
76
88
  }
77
89
 
78
- createHandler(): Promisable<void> {}
90
+ createHandler(): Promisable<void> {
91
+ assertEx(this._status === 'creating', () => `createHandler can not be called [status = ${this.status}]`)
92
+ }
79
93
 
80
94
  paramsValidator(params: Partial<TParams>): TParams {
81
95
  return { ...params, name: params.name ?? this.constructor.name } as TParams
@@ -83,28 +97,48 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
83
97
 
84
98
  async start(): Promise<boolean> {
85
99
  this._noOverride('start')
86
- try {
87
- this.statusReporter?.report(this.name, 'starting')
88
- await this.startHandler()
89
- this.statusReporter?.report(this.name, 'started')
100
+ if (this.status === 'started') {
90
101
  return true
91
- } catch (ex) {
92
- this.statusReporter?.report(this.name, 'error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))
93
- return false
94
102
  }
103
+ return await this._statusMutex.runExclusive(async () => {
104
+ try {
105
+ // check again in case it changed
106
+ if (this.status === 'started') {
107
+ return true
108
+ }
109
+ assertEx(this.startable, () => `Creatable [${this.name}] is not startable in status [${this.status}]`)
110
+ this.setStatus('starting')
111
+ await this.startHandler()
112
+ this.setStatus('started')
113
+ return true
114
+ } catch (ex) {
115
+ this.setStatus('error', isError(ex) ? ex : new Error(`Error starting: ${ex}`))
116
+ return false
117
+ }
118
+ })
95
119
  }
96
120
 
97
121
  async stop(): Promise<boolean> {
98
122
  this._noOverride('stop')
99
- try {
100
- this.statusReporter?.report(this.name, 'stopping')
101
- await this.stopHandler()
102
- this.statusReporter?.report(this.name, 'stopped')
123
+ if (this.status === 'stopped') {
103
124
  return true
104
- } catch (ex) {
105
- this.statusReporter?.report(this.name, 'error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))
106
- return false
107
125
  }
126
+ return await this._statusMutex.runExclusive(async () => {
127
+ try {
128
+ // check again in case it changed
129
+ if (this.status === 'stopped') {
130
+ return true
131
+ }
132
+ assertEx(this.status === 'started', () => `Creatable [${this.name}] is not stoppable in status [${this.status}]`)
133
+ this.setStatus('stopping')
134
+ await this.stopHandler()
135
+ this.setStatus('stopped')
136
+ return true
137
+ } catch (ex) {
138
+ this.setStatus('error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))
139
+ return false
140
+ }
141
+ })
108
142
  }
109
143
 
110
144
  protected _noOverride(functionName = getFunctionName(3)) {
@@ -115,6 +149,26 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
115
149
  assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)
116
150
  }
117
151
 
152
+ protected setStatus(value: Exclude<CreatableStatus, 'error'>, progress?: number): void
153
+ protected setStatus(value: Extract<CreatableStatus, 'error'>, error?: Error): void
154
+ protected setStatus(value: CreatableStatus, progressOrError?: number | Error): void {
155
+ this._status = value
156
+ if (value !== null) {
157
+ if (value === 'error') {
158
+ if (isError(progressOrError)) {
159
+ this.statusReporter?.report(this.name, value, progressOrError)
160
+ return
161
+ }
162
+ } else {
163
+ if (isNumber(progressOrError)) {
164
+ this.statusReporter?.report(this.name, value, progressOrError)
165
+ return
166
+ }
167
+ }
168
+ this.statusReporter?.report(this.name, value)
169
+ }
170
+ }
171
+
118
172
  protected startHandler(): Promisable<void> {
119
173
  // when overriding this, throw an error on failure
120
174
  }
@@ -3,6 +3,7 @@ import type { BaseClassName } from '@xylabs/base'
3
3
  export type CreatableStatus = 'creating' | 'created' | 'starting' | 'started' | 'stopping' | 'stopped' | 'error'
4
4
 
5
5
  export interface CreatableStatusReporter<T extends void | string = void> {
6
- report(name: BaseClassName, status: Exclude<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, progress?: number): void
7
- report(name: BaseClassName, status: Extract<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, error?: Error): void
6
+ report(name: BaseClassName, status: Exclude<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, progress: number): void
7
+ report(name: BaseClassName, status: Extract<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, error: Error): void
8
+ report(name: BaseClassName, status: T extends void ? CreatableStatus : CreatableStatus | T): void
8
9
  }