@xylabs/creatable 5.0.14 → 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.
- package/dist/neutral/AbstractCreatable.d.ts +7 -1
- package/dist/neutral/AbstractCreatable.d.ts.map +1 -1
- package/dist/neutral/index.mjs +62 -17
- package/dist/neutral/index.mjs.map +1 -1
- package/dist/neutral/model/CreatableStatusReporter.d.ts +3 -2
- package/dist/neutral/model/CreatableStatusReporter.d.ts.map +1 -1
- package/package.json +8 -7
- package/src/AbstractCreatable.ts +72 -18
- package/src/model/CreatableStatusReporter.ts +3 -2
|
@@ -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;
|
|
1
|
+
{"version":3,"file":"AbstractCreatable.d.ts","sourceRoot":"","sources":["../../src/AbstractCreatable.ts"],"names":[],"mappings":"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"}
|
package/dist/neutral/index.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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":"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","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
|
|
5
|
-
report(name: BaseClassName, status: Extract<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, error
|
|
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,
|
|
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.
|
|
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.
|
|
39
|
-
"@xylabs/base": "~5.0.
|
|
40
|
-
"@xylabs/events": "~5.0.
|
|
41
|
-
"@xylabs/logger": "~5.0.
|
|
42
|
-
"@xylabs/promise": "~5.0.
|
|
43
|
-
"@xylabs/typeof": "~5.0.
|
|
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",
|
package/src/AbstractCreatable.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
7
|
-
report(name: BaseClassName, status: Extract<T extends void ? CreatableStatus : CreatableStatus | T, 'error'>, error
|
|
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
|
}
|