@xylabs/creatable 5.0.14 → 5.0.16

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,22 +3,28 @@ 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';
7
- export declare class AbstractCreatable<TParams extends CreatableParams = CreatableParams, TEventData extends EventData = EventData> extends BaseEmitter<Partial<TParams>, TEventData> {
6
+ import type { CreatableInstance, CreatableName, CreatableParams, CreatableStatus, Labels, RequiredCreatableParams } from './model/index.ts';
7
+ export declare class AbstractCreatable<TParams extends CreatableParams = CreatableParams, TEventData extends EventData = EventData> extends BaseEmitter<Partial<TParams & RequiredCreatableParams>, TEventData> {
8
8
  defaultLogger?: Logger;
9
+ private _status;
10
+ private _statusMutex;
9
11
  private _validatedParams?;
10
- constructor(key: unknown, params: Partial<TParams>);
12
+ constructor(key: unknown, params: Partial<TParams & RequiredCreatableParams>);
11
13
  get name(): CreatableName;
12
- get params(): TParams;
14
+ get params(): TParams & RequiredCreatableParams;
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>;
16
20
  static paramsHandler<T extends CreatableInstance>(this: Creatable<T>, params?: Partial<T['params']>): Promisable<T['params']>;
17
21
  createHandler(): Promisable<void>;
18
- paramsValidator(params: Partial<TParams>): TParams;
22
+ paramsValidator(params: Partial<TParams & RequiredCreatableParams>): TParams & RequiredCreatableParams;
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":"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"}
@@ -1,7 +1,7 @@
1
1
  import type { Logger } from '@xylabs/logger';
2
2
  import type { Promisable } from '@xylabs/promise';
3
3
  import type { AbstractCreatable } from './AbstractCreatable.ts';
4
- import type { CreatableInstance, CreatableParams, Labels } from './model/index.ts';
4
+ import type { CreatableInstance, CreatableParams, Labels, RequiredCreatableParams } from './model/index.ts';
5
5
  export interface CreatableFactory<T extends CreatableInstance = CreatableInstance> extends Omit<Creatable<T>, 'create' | 'createHandler' | 'paramsHandler' | 'defaultLogger' | 'factory'> {
6
6
  create(this: CreatableFactory<T>, params?: Partial<T['params']>): Promise<T>;
7
7
  }
@@ -10,7 +10,7 @@ export interface Creatable<T extends CreatableInstance = CreatableInstance> {
10
10
  new (key: unknown, params: Partial<CreatableParams>): T & AbstractCreatable<T['params']>;
11
11
  create<T extends CreatableInstance>(this: Creatable<T>, params?: Partial<T['params']>): Promise<T>;
12
12
  createHandler<T extends CreatableInstance>(this: Creatable<T>, instance: T): Promisable<T>;
13
- paramsHandler<T extends CreatableInstance>(this: Creatable<T>, params?: Partial<T['params']>): Promisable<T['params']>;
13
+ paramsHandler<T extends CreatableInstance>(this: Creatable<T>, params?: Partial<T['params']>): Promisable<T['params'] & RequiredCreatableParams>;
14
14
  }
15
15
  export interface CreatableWithFactory<T extends CreatableInstance = CreatableInstance> extends Creatable<T> {
16
16
  factory<T extends CreatableInstance>(this: Creatable<T>, params?: Partial<T['params']>, labels?: Labels): CreatableFactory<T>;
@@ -1 +1 @@
1
- {"version":3,"file":"Creatable.d.ts","sourceRoot":"","sources":["../../src/Creatable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EACV,iBAAiB,EAAE,eAAe,EAAE,MAAM,EAC3C,MAAM,kBAAkB,CAAA;AAEzB,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB,CAC/E,SAAQ,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,GAAG,SAAS,CAAC;IAEtG,MAAM,CACJ,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EACzB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CAC7C;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB;IAExE,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,KAAI,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEvF,MAAM,CAAC,CAAC,SAAS,iBAAiB,EAChC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAE5C,aAAa,CAAC,CAAC,SAAS,iBAAiB,EACvC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,QAAQ,EAAE,CAAC,GACV,UAAU,CAAC,CAAC,CAAC,CAAA;IAEhB,aAAa,CAAC,CAAC,SAAS,iBAAiB,EACvC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;CAC9E;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IACzG,OAAO,CAAC,CAAC,SAAS,iBAAiB,EACjC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAA;CACxC;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,iBAAiB,MAC3C,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,UAI/C;AAED;;;;;GAKG;AAEH,wBAAgB,gBAAgB,KACtB,CAAC,SAAS,gBAAgB,EAAE,aAAa,CAAC,UAInD"}
1
+ {"version":3,"file":"Creatable.d.ts","sourceRoot":"","sources":["../../src/Creatable.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EACV,iBAAiB,EAAE,eAAe,EAAE,MAAM,EAC1C,uBAAuB,EACxB,MAAM,kBAAkB,CAAA;AAEzB,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB,CAC/E,SAAQ,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,GAAG,eAAe,GAAG,eAAe,GAAG,eAAe,GAAG,SAAS,CAAC;IAEtG,MAAM,CACJ,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,EACzB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CAC7C;AAED,MAAM,WAAW,SAAS,CAAC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB;IAExE,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,KAAI,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEvF,MAAM,CAAC,CAAC,SAAS,iBAAiB,EAChC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAE5C,aAAa,CAAC,CAAC,SAAS,iBAAiB,EACvC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,QAAQ,EAAE,CAAC,GACV,UAAU,CAAC,CAAC,CAAC,CAAA;IAEhB,aAAa,CAAC,CAAC,SAAS,iBAAiB,EACvC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,uBAAuB,CAAC,CAAA;CACxG;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,SAAS,iBAAiB,GAAG,iBAAiB,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IACzG,OAAO,CAAC,CAAC,SAAS,iBAAiB,EACjC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAC7B,MAAM,CAAC,EAAE,MAAM,GAAG,gBAAgB,CAAC,CAAC,CAAC,CAAA;CACxC;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,iBAAiB,MAC3C,CAAC,SAAS,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,UAI/C;AAED;;;;;GAKG;AAEH,wBAAgB,gBAAgB,KACtB,CAAC,SAAS,gBAAgB,EAAE,aAAa,CAAC,UAInD"}
@@ -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 RequiredCreatableParams,\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 & RequiredCreatableParams>, TEventData> {\n defaultLogger?: Logger\n\n private _status: CreatableStatus | null = null\n private _statusMutex = new Mutex()\n private _validatedParams?: TParams & RequiredCreatableParams\n\n constructor(key: unknown, params: Partial<TParams & RequiredCreatableParams>) {\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 & RequiredCreatableParams {\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 & RequiredCreatableParams>): TParams & RequiredCreatableParams {\n return { ...params, name: params.name ?? this.constructor.name } as TParams & RequiredCreatableParams\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 RequiredCreatableParams,\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'] & RequiredCreatableParams>\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":["isNumber","creatable","isNumber"]}
@@ -1,9 +1,11 @@
1
1
  import type { BaseClassName } from '@xylabs/base';
2
2
  import type { BaseEmitterParams } from '@xylabs/events';
3
- import type { CreatableStatusReporter } from './CreatableStatusReporter.ts';
3
+ import type { CreatableStatus, CreatableStatusReporter } from './CreatableStatusReporter.ts';
4
4
  export type CreatableName = Exclude<string, 'creatable-name-reserved-32546239486'> & BaseClassName;
5
- export interface CreatableParams extends BaseEmitterParams {
5
+ export interface RequiredCreatableParams<TAdditionalStatus extends CreatableStatus | void = void> extends BaseEmitterParams {
6
6
  name?: CreatableName;
7
- statusReporter?: CreatableStatusReporter;
7
+ statusReporter?: CreatableStatusReporter<TAdditionalStatus>;
8
+ }
9
+ export interface CreatableParams extends RequiredCreatableParams, BaseEmitterParams {
8
10
  }
9
11
  //# sourceMappingURL=CreatableParams.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"CreatableParams.d.ts","sourceRoot":"","sources":["../../../src/model/CreatableParams.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEvD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAE3E,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,qCAAqC,CAAC,GAAG,aAAa,CAAA;AAElG,MAAM,WAAW,eAAgB,SAAQ,iBAAiB;IACxD,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,cAAc,CAAC,EAAE,uBAAuB,CAAA;CACzC"}
1
+ {"version":3,"file":"CreatableParams.d.ts","sourceRoot":"","sources":["../../../src/model/CreatableParams.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAEvD,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAA;AAE5F,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,EAAE,qCAAqC,CAAC,GAAG,aAAa,CAAA;AAElG,MAAM,WAAW,uBAAuB,CAAC,iBAAiB,SAAS,eAAe,GAAG,IAAI,GAAG,IAAI,CAAE,SAAQ,iBAAiB;IACzH,IAAI,CAAC,EAAE,aAAa,CAAA;IACpB,cAAc,CAAC,EAAE,uBAAuB,CAAC,iBAAiB,CAAC,CAAA;CAC5D;AAED,MAAM,WAAW,eAAgB,SAAQ,uBAAuB,EAAE,iBAAiB;CAAG"}
@@ -1,7 +1,9 @@
1
1
  import type { BaseClassName } from '@xylabs/base';
2
- export type CreatableStatus = 'creating' | 'created' | 'starting' | 'started' | 'stopping' | 'stopped' | 'error';
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;
2
+ export type StandardCreatableStatus = 'creating' | 'created' | 'starting' | 'started' | 'stopping' | 'stopped' | 'error';
3
+ export type CreatableStatus<TAdditionalStatus extends void | string = void> = StandardCreatableStatus | (TAdditionalStatus extends void ? StandardCreatableStatus : TAdditionalStatus);
4
+ export interface CreatableStatusReporter<TAdditionalStatus extends void | string = void> {
5
+ report(name: BaseClassName, status: Exclude<CreatableStatus<TAdditionalStatus>, 'error'>, progress: number): void;
6
+ report(name: BaseClassName, status: Extract<CreatableStatus<TAdditionalStatus>, 'error'>, error: Error): void;
7
+ report(name: BaseClassName, status: CreatableStatus<TAdditionalStatus>): void;
6
8
  }
7
9
  //# 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,uBAAuB,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,GAAG,OAAO,CAAA;AACxH,MAAM,MAAM,eAAe,CAAC,iBAAiB,SAAS,IAAI,GAAG,MAAM,GAAG,IAAI,IACtE,uBAAuB,GAAG,CAAC,iBAAiB,SAAS,IAAI,GAAG,uBAAuB,GAAG,iBAAiB,CAAC,CAAA;AAE5G,MAAM,WAAW,uBAAuB,CAAC,iBAAiB,SAAS,IAAI,GAAG,MAAM,GAAG,IAAI;IACrF,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACjH,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,eAAe,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI,CAAA;IAC7G,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,eAAe,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAA;CAC9E"}
@@ -2,12 +2,12 @@ import type { EventData } from '@xylabs/events';
2
2
  import { Promisable } from '@xylabs/promise';
3
3
  import { AbstractCreatable } from '../AbstractCreatable.ts';
4
4
  import { Creatable } from '../Creatable.ts';
5
- import { CreatableInstance, CreatableParams } from '../model/index.ts';
5
+ import { CreatableInstance, CreatableParams, RequiredCreatableParams } from '../model/index.ts';
6
6
  export interface TestCreatableParams extends CreatableParams {
7
7
  testParam: string;
8
8
  }
9
9
  export declare class TestCreatable<TParams extends TestCreatableParams = TestCreatableParams, TEventData extends EventData = EventData> extends AbstractCreatable<TParams, TEventData> {
10
10
  static createHandler<T extends CreatableInstance>(this: Creatable<T>, instance: T): Promisable<T>;
11
- paramsValidator(params?: Partial<TParams>): TParams;
11
+ paramsValidator(params?: Partial<TParams & RequiredCreatableParams>): TParams;
12
12
  }
13
13
  //# sourceMappingURL=TestCreatable.spec.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TestCreatable.spec.d.ts","sourceRoot":"","sources":["../../../src/spec/TestCreatable.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAK5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAa,MAAM,iBAAiB,CAAA;AACtD,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAEtE,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,qBACa,aAAa,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB,EAAE,UAAU,SAAS,SAAS,GAAG,SAAS,CAC5H,SAAQ,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC;WAC9B,aAAa,CAAC,CAAC,SAAS,iBAAiB,EACvD,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,QAAQ,EAAE,CAAC,GACV,UAAU,CAAC,CAAC,CAAC;IAIP,eAAe,CAAC,MAAM,GAAE,OAAO,CAAC,OAAO,CAAM,GAAG,OAAO;CAMjE"}
1
+ {"version":3,"file":"TestCreatable.spec.d.ts","sourceRoot":"","sources":["../../../src/spec/TestCreatable.spec.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAK5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAA;AAC3D,OAAO,EAAE,SAAS,EAAa,MAAM,iBAAiB,CAAA;AACtD,OAAO,EACL,iBAAiB,EAAE,eAAe,EAAE,uBAAuB,EAC5D,MAAM,mBAAmB,CAAA;AAE1B,MAAM,WAAW,mBAAoB,SAAQ,eAAe;IAC1D,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,qBACa,aAAa,CAAC,OAAO,SAAS,mBAAmB,GAAG,mBAAmB,EAAE,UAAU,SAAS,SAAS,GAAG,SAAS,CAC5H,SAAQ,iBAAiB,CAAC,OAAO,EAAE,UAAU,CAAC;WAC9B,aAAa,CAAC,CAAC,SAAS,iBAAiB,EACvD,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,EAClB,QAAQ,EAAE,CAAC,GACV,UAAU,CAAC,CAAC,CAAC;IAIP,eAAe,CAAC,MAAM,GAAE,OAAO,CAAC,OAAO,GAAG,uBAAuB,CAAM,GAAG,OAAO;CAM3F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xylabs/creatable",
3
- "version": "5.0.14",
3
+ "version": "5.0.16",
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.14",
39
- "@xylabs/base": "~5.0.14",
40
- "@xylabs/events": "~5.0.14",
41
- "@xylabs/logger": "~5.0.14",
42
- "@xylabs/promise": "~5.0.14",
43
- "@xylabs/typeof": "~5.0.14"
38
+ "@xylabs/assert": "~5.0.16",
39
+ "@xylabs/base": "~5.0.16",
40
+ "@xylabs/events": "~5.0.16",
41
+ "@xylabs/logger": "~5.0.16",
42
+ "@xylabs/promise": "~5.0.16",
43
+ "@xylabs/typeof": "~5.0.16",
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,19 +13,23 @@ 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,
18
+ RequiredCreatableParams,
16
19
  } from './model/index.ts'
17
20
 
18
21
  const AbstractCreatableConstructorKey = Symbol.for('AbstractCreatableConstructor')
19
22
 
20
23
  @creatable()
21
24
  export class AbstractCreatable<TParams extends CreatableParams = CreatableParams,
22
- TEventData extends EventData = EventData> extends BaseEmitter<Partial<TParams>, TEventData> {
25
+ TEventData extends EventData = EventData> extends BaseEmitter<Partial<TParams & RequiredCreatableParams>, TEventData> {
23
26
  defaultLogger?: Logger
24
27
 
25
- private _validatedParams?: TParams
28
+ private _status: CreatableStatus | null = null
29
+ private _statusMutex = new Mutex()
30
+ private _validatedParams?: TParams & RequiredCreatableParams
26
31
 
27
- constructor(key: unknown, params: Partial<TParams>) {
32
+ constructor(key: unknown, params: Partial<TParams & RequiredCreatableParams>) {
28
33
  assertEx(key === AbstractCreatableConstructorKey, () => 'AbstractCreatable should not be instantiated directly, use the static create method instead')
29
34
  super(params)
30
35
  }
@@ -33,11 +38,19 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
33
38
  return this.params.name ?? this.constructor.name as CreatableName
34
39
  }
35
40
 
36
- override get params(): TParams {
41
+ override get params(): TParams & RequiredCreatableParams {
37
42
  this._validatedParams = this._validatedParams ?? this.paramsValidator(super.params)
38
43
  return this._validatedParams
39
44
  }
40
45
 
46
+ get startable() {
47
+ return this.status === 'created' || this.status === 'stopped'
48
+ }
49
+
50
+ get status(): CreatableStatus | null {
51
+ return this._status
52
+ }
53
+
41
54
  get statusReporter() {
42
55
  return this.params.statusReporter
43
56
  }
@@ -48,12 +61,12 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
48
61
  ): Promise<T> {
49
62
  const params = await this.paramsHandler(inParams)
50
63
  const name: CreatableName = params.name ?? this.name as CreatableName
51
- params.statusReporter?.report(name, 'creating')
52
64
  try {
53
65
  const instance = new this(AbstractCreatableConstructorKey, params)
66
+ instance.setStatus('creating')
54
67
  const initializedInstance = await this.createHandler(instance)
55
68
  await instance.createHandler()
56
- params.statusReporter?.report(name, 'created')
69
+ instance.setStatus('created')
57
70
  return initializedInstance
58
71
  } catch (ex) {
59
72
  params.statusReporter?.report(name, 'error', isError(ex) ? ex : new Error(`Error creating: ${name}`))
@@ -75,36 +88,58 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
75
88
  return { ...params }
76
89
  }
77
90
 
78
- createHandler(): Promisable<void> {}
91
+ createHandler(): Promisable<void> {
92
+ assertEx(this._status === 'creating', () => `createHandler can not be called [status = ${this.status}]`)
93
+ }
79
94
 
80
- paramsValidator(params: Partial<TParams>): TParams {
81
- return { ...params, name: params.name ?? this.constructor.name } as TParams
95
+ paramsValidator(params: Partial<TParams & RequiredCreatableParams>): TParams & RequiredCreatableParams {
96
+ return { ...params, name: params.name ?? this.constructor.name } as TParams & RequiredCreatableParams
82
97
  }
83
98
 
84
99
  async start(): Promise<boolean> {
85
100
  this._noOverride('start')
86
- try {
87
- this.statusReporter?.report(this.name, 'starting')
88
- await this.startHandler()
89
- this.statusReporter?.report(this.name, 'started')
101
+ if (this.status === 'started') {
90
102
  return true
91
- } catch (ex) {
92
- this.statusReporter?.report(this.name, 'error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))
93
- return false
94
103
  }
104
+ return await this._statusMutex.runExclusive(async () => {
105
+ try {
106
+ // check again in case it changed
107
+ if (this.status === 'started') {
108
+ return true
109
+ }
110
+ assertEx(this.startable, () => `Creatable [${this.name}] is not startable in status [${this.status}]`)
111
+ this.setStatus('starting')
112
+ await this.startHandler()
113
+ this.setStatus('started')
114
+ return true
115
+ } catch (ex) {
116
+ this.setStatus('error', isError(ex) ? ex : new Error(`Error starting: ${ex}`))
117
+ return false
118
+ }
119
+ })
95
120
  }
96
121
 
97
122
  async stop(): Promise<boolean> {
98
123
  this._noOverride('stop')
99
- try {
100
- this.statusReporter?.report(this.name, 'stopping')
101
- await this.stopHandler()
102
- this.statusReporter?.report(this.name, 'stopped')
124
+ if (this.status === 'stopped') {
103
125
  return true
104
- } catch (ex) {
105
- this.statusReporter?.report(this.name, 'error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))
106
- return false
107
126
  }
127
+ return await this._statusMutex.runExclusive(async () => {
128
+ try {
129
+ // check again in case it changed
130
+ if (this.status === 'stopped') {
131
+ return true
132
+ }
133
+ assertEx(this.status === 'started', () => `Creatable [${this.name}] is not stoppable in status [${this.status}]`)
134
+ this.setStatus('stopping')
135
+ await this.stopHandler()
136
+ this.setStatus('stopped')
137
+ return true
138
+ } catch (ex) {
139
+ this.setStatus('error', isError(ex) ? ex : new Error(`Error stopping: ${ex}`))
140
+ return false
141
+ }
142
+ })
108
143
  }
109
144
 
110
145
  protected _noOverride(functionName = getFunctionName(3)) {
@@ -115,6 +150,26 @@ export class AbstractCreatable<TParams extends CreatableParams = CreatableParams
115
150
  assertEx(thisFunc === rootFunc, () => `Override not allowed for [${functionName}] - override ${functionName}Handler instead`)
116
151
  }
117
152
 
153
+ protected setStatus(value: Exclude<CreatableStatus, 'error'>, progress?: number): void
154
+ protected setStatus(value: Extract<CreatableStatus, 'error'>, error?: Error): void
155
+ protected setStatus(value: CreatableStatus, progressOrError?: number | Error): void {
156
+ this._status = value
157
+ if (value !== null) {
158
+ if (value === 'error') {
159
+ if (isError(progressOrError)) {
160
+ this.statusReporter?.report(this.name, value, progressOrError)
161
+ return
162
+ }
163
+ } else {
164
+ if (isNumber(progressOrError)) {
165
+ this.statusReporter?.report(this.name, value, progressOrError)
166
+ return
167
+ }
168
+ }
169
+ this.statusReporter?.report(this.name, value)
170
+ }
171
+ }
172
+
118
173
  protected startHandler(): Promisable<void> {
119
174
  // when overriding this, throw an error on failure
120
175
  }
package/src/Creatable.ts CHANGED
@@ -4,6 +4,7 @@ import type { Promisable } from '@xylabs/promise'
4
4
  import type { AbstractCreatable } from './AbstractCreatable.ts'
5
5
  import type {
6
6
  CreatableInstance, CreatableParams, Labels,
7
+ RequiredCreatableParams,
7
8
  } from './model/index.ts'
8
9
 
9
10
  export interface CreatableFactory<T extends CreatableInstance = CreatableInstance>
@@ -30,7 +31,7 @@ export interface Creatable<T extends CreatableInstance = CreatableInstance> {
30
31
  ): Promisable<T>
31
32
 
32
33
  paramsHandler<T extends CreatableInstance>(
33
- this: Creatable<T>, params?: Partial<T['params']>): Promisable<T['params']>
34
+ this: Creatable<T>, params?: Partial<T['params']>): Promisable<T['params'] & RequiredCreatableParams>
34
35
  }
35
36
 
36
37
  export interface CreatableWithFactory<T extends CreatableInstance = CreatableInstance> extends Creatable<T> {
@@ -1,11 +1,13 @@
1
1
  import type { BaseClassName } from '@xylabs/base'
2
2
  import type { BaseEmitterParams } from '@xylabs/events'
3
3
 
4
- import type { CreatableStatusReporter } from './CreatableStatusReporter.ts'
4
+ import type { CreatableStatus, CreatableStatusReporter } from './CreatableStatusReporter.ts'
5
5
 
6
6
  export type CreatableName = Exclude<string, 'creatable-name-reserved-32546239486'> & BaseClassName
7
7
 
8
- export interface CreatableParams extends BaseEmitterParams {
8
+ export interface RequiredCreatableParams<TAdditionalStatus extends CreatableStatus | void = void> extends BaseEmitterParams {
9
9
  name?: CreatableName
10
- statusReporter?: CreatableStatusReporter
10
+ statusReporter?: CreatableStatusReporter<TAdditionalStatus>
11
11
  }
12
+
13
+ export interface CreatableParams extends RequiredCreatableParams, BaseEmitterParams {}
@@ -1,8 +1,11 @@
1
1
  import type { BaseClassName } from '@xylabs/base'
2
2
 
3
- export type CreatableStatus = 'creating' | 'created' | 'starting' | 'started' | 'stopping' | 'stopped' | 'error'
3
+ export type StandardCreatableStatus = 'creating' | 'created' | 'starting' | 'started' | 'stopping' | 'stopped' | 'error'
4
+ export type CreatableStatus<TAdditionalStatus extends void | string = void>
5
+ = StandardCreatableStatus | (TAdditionalStatus extends void ? StandardCreatableStatus : TAdditionalStatus)
4
6
 
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
7
+ export interface CreatableStatusReporter<TAdditionalStatus extends void | string = void> {
8
+ report(name: BaseClassName, status: Exclude<CreatableStatus<TAdditionalStatus>, 'error'>, progress: number): void
9
+ report(name: BaseClassName, status: Extract<CreatableStatus<TAdditionalStatus>, 'error'>, error: Error): void
10
+ report(name: BaseClassName, status: CreatableStatus<TAdditionalStatus>): void
8
11
  }
@@ -6,7 +6,9 @@ import {
6
6
 
7
7
  import { AbstractCreatable } from '../AbstractCreatable.ts'
8
8
  import { Creatable, creatable } from '../Creatable.ts'
9
- import { CreatableInstance, CreatableParams } from '../model/index.ts'
9
+ import {
10
+ CreatableInstance, CreatableParams, RequiredCreatableParams,
11
+ } from '../model/index.ts'
10
12
 
11
13
  export interface TestCreatableParams extends CreatableParams {
12
14
  testParam: string
@@ -22,7 +24,7 @@ export class TestCreatable<TParams extends TestCreatableParams = TestCreatablePa
22
24
  return instance
23
25
  }
24
26
 
25
- override paramsValidator(params: Partial<TParams> = {}): TParams {
27
+ override paramsValidator(params: Partial<TParams & RequiredCreatableParams> = {}): TParams {
26
28
  const result: TestCreatableParams = {
27
29
  ...params, name: params.name ?? this.constructor.name, testParam: 'yo',
28
30
  }
@@ -7,7 +7,7 @@ import {
7
7
  import {
8
8
  Creatable, creatable, creatableFactory,
9
9
  } from '../Creatable.ts'
10
- import { CreatableInstance } from '../model/index.ts'
10
+ import { CreatableInstance, RequiredCreatableParams } from '../model/index.ts'
11
11
  import { TestCreatable, TestCreatableParams } from './TestCreatable.spec.ts'
12
12
 
13
13
  interface TestCreatableParams2 extends TestCreatableParams {
@@ -24,7 +24,7 @@ class TestCreatable2<TParams extends TestCreatableParams2 = TestCreatableParams2
24
24
  return instance
25
25
  }
26
26
 
27
- override paramsValidator(params: Partial<TParams> = {}): TParams {
27
+ override paramsValidator(params: Partial<TParams & RequiredCreatableParams> = {}): TParams {
28
28
  const result: TestCreatableParams = {
29
29
  ...params, name: params.name ?? this.constructor.name, testParam: 'yo',
30
30
  }