@mxweb/classable 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/README.md +112 -2
- package/dist/index.d.ts +166 -10
- package/dist/index.mjs +1 -1
- package/package.json +6 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [1.1.0] - 2026-01-17
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Type Utilities**
|
|
13
|
+
- `Readonlyable<T>` - Utility type that accepts both mutable and readonly versions, useful for `Object.freeze()` or `as const`
|
|
14
|
+
- `InstanceByStatic<T, Method, Args, Runtime>` - Type for classes using static factory methods instead of direct constructor instantiation
|
|
15
|
+
- `ClassableSelector<T, Args, Runtime>` - Selector function type for choosing a classable from a list with custom logic
|
|
16
|
+
|
|
17
|
+
- **classable API**
|
|
18
|
+
- `classable.from(def, runtime?)` - Create an instance via a static factory method definition
|
|
19
|
+
- `classable.select(selector)` - Higher-order function to create a selector that picks a classable based on custom criteria
|
|
20
|
+
- `classable.placeholderInstance` - Pre-configured placeholder using InstanceByStatic pattern
|
|
21
|
+
|
|
22
|
+
- **Placeholder**
|
|
23
|
+
- Added `Placeholder.getInstance()` static method for factory pattern support
|
|
24
|
+
|
|
25
|
+
### Changed
|
|
26
|
+
|
|
27
|
+
- Updated `Args` type parameter across all APIs to use `Readonlyable<any[]>` for flexible readonly/mutable array support
|
|
28
|
+
- `ClassableByResolver.target` now uses `[...Args]` spread to convert readonly arrays to mutable
|
|
29
|
+
- `Classable` type now properly handles readonly arrays
|
|
30
|
+
- `StaticExtended` type updated to support `Readonlyable<any[]>`
|
|
31
|
+
|
|
8
32
|
## [1.0.0] - 2026-01-17
|
|
9
33
|
|
|
10
34
|
### Added
|
|
@@ -41,4 +65,5 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
41
65
|
- Comprehensive README with examples
|
|
42
66
|
- MIT License
|
|
43
67
|
|
|
68
|
+
[1.1.0]: https://github.com/mxwebio/mxweb-classable/releases/tag/v1.1.0
|
|
44
69
|
[1.0.0]: https://github.com/mxwebio/mxweb-classable/releases/tag/v1.0.0
|
package/README.md
CHANGED
|
@@ -85,12 +85,30 @@ interface ClassableByResolver<InstanceType, Args, Runtime> {
|
|
|
85
85
|
}
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
+
### Readonlyable
|
|
89
|
+
|
|
90
|
+
All `Args` type parameters accept both mutable and readonly arrays, so you can use `Object.freeze()` or `as const` without type errors:
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
// Both work seamlessly
|
|
94
|
+
const mutableResolver = {
|
|
95
|
+
target: User,
|
|
96
|
+
resolve: () => ['John', 30] // mutable array
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const frozenResolver = Object.freeze({
|
|
100
|
+
target: User,
|
|
101
|
+
resolve: () => ['John', 30] as const // readonly array
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
88
105
|
## API Reference
|
|
89
106
|
|
|
90
107
|
### Type Utilities
|
|
91
108
|
|
|
92
109
|
| Type | Description |
|
|
93
110
|
|------|-------------|
|
|
111
|
+
| `Readonlyable<T>` | Accepts both mutable and readonly versions of a type |
|
|
94
112
|
| `UnitClass<T>` | Class constructor with no arguments |
|
|
95
113
|
| `ClassType<T, Args>` | Class constructor with specific arguments |
|
|
96
114
|
| `AbstractClassType<T, Args>` | Abstract class constructor |
|
|
@@ -99,6 +117,8 @@ interface ClassableByResolver<InstanceType, Args, Runtime> {
|
|
|
99
117
|
| `AnyConstructor` | Any class or abstract class |
|
|
100
118
|
| `Classable<T, Args, Runtime>` | Class or resolver configuration |
|
|
101
119
|
| `ClassableByResolver<T, Args, Runtime>` | Resolver configuration |
|
|
120
|
+
| `InstanceByStatic<T, Method, Args, Runtime>` | Static factory method pattern |
|
|
121
|
+
| `ClassableSelector<T, Args, Runtime>` | Selector function for choosing classables |
|
|
102
122
|
|
|
103
123
|
### `classable` API
|
|
104
124
|
|
|
@@ -211,14 +231,57 @@ classable.getDescriptor(resolver);
|
|
|
211
231
|
// { type: "resolver", target: "User" }
|
|
212
232
|
```
|
|
213
233
|
|
|
234
|
+
#### `classable.from(def, runtime?)`
|
|
235
|
+
|
|
236
|
+
Creates an instance from a static factory method definition.
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
class Cache {
|
|
240
|
+
private constructor(private ttl: number) {}
|
|
241
|
+
|
|
242
|
+
static create(ttl: number): Cache {
|
|
243
|
+
return new Cache(ttl);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const cacheInstance = classable.from({
|
|
248
|
+
target: Cache,
|
|
249
|
+
selector: () => ({ method: "create", args: [3600] })
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
// With runtime context
|
|
253
|
+
const dynamicCache = classable.from({
|
|
254
|
+
target: Cache,
|
|
255
|
+
selector: (ctx) => ({ method: "create", args: [ctx.cacheTTL] })
|
|
256
|
+
}, appContext);
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
#### `classable.select(selector)`
|
|
260
|
+
|
|
261
|
+
Creates a selector function that chooses a classable from a list based on custom logic.
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
// Simple selector
|
|
265
|
+
const pickFirst = classable.select((...classes) => {
|
|
266
|
+
return [classes[0], []];
|
|
267
|
+
});
|
|
268
|
+
const [selected, args] = pickFirst(ServiceA, ServiceB);
|
|
269
|
+
|
|
270
|
+
// With runtime context
|
|
271
|
+
const pickByEnv = classable.select<Logger, [], Env>((env, ...loggers) => {
|
|
272
|
+
return env.isDev ? [loggers[0], []] : [loggers[1], []];
|
|
273
|
+
});
|
|
274
|
+
const [logger, loggerArgs] = pickByEnv(devEnv, DevLogger, ProdLogger);
|
|
275
|
+
```
|
|
276
|
+
|
|
214
277
|
### Placeholder
|
|
215
278
|
|
|
216
|
-
|
|
279
|
+
Utility classes for marking unresolved bindings:
|
|
217
280
|
|
|
218
281
|
```typescript
|
|
219
282
|
import { classable } from '@mxweb/classable';
|
|
220
283
|
|
|
221
|
-
// Use as default value
|
|
284
|
+
// Use placeholder resolver as default value
|
|
222
285
|
class Container {
|
|
223
286
|
private bindings = new Map();
|
|
224
287
|
|
|
@@ -226,10 +289,51 @@ class Container {
|
|
|
226
289
|
this.bindings.set(key, cls);
|
|
227
290
|
}
|
|
228
291
|
}
|
|
292
|
+
|
|
293
|
+
// Use placeholderInstance for static factory pattern
|
|
294
|
+
const instance = classable.from(classable.placeholderInstance);
|
|
295
|
+
// Returns new Placeholder via getInstance()
|
|
229
296
|
```
|
|
230
297
|
|
|
231
298
|
## Advanced Usage
|
|
232
299
|
|
|
300
|
+
### Static Factory Method Pattern
|
|
301
|
+
|
|
302
|
+
Use `InstanceByStatic` for classes that use static factory methods instead of direct instantiation:
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { classable, InstanceByStatic } from '@mxweb/classable';
|
|
306
|
+
|
|
307
|
+
class Database {
|
|
308
|
+
private constructor(private connectionString: string) {}
|
|
309
|
+
|
|
310
|
+
static connect(connectionString: string): Database {
|
|
311
|
+
return new Database(connectionString);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
static createInMemory(): Database {
|
|
315
|
+
return new Database(':memory:');
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Define static factory configuration
|
|
320
|
+
const dbDef: InstanceByStatic<Database, 'connect', [string]> = {
|
|
321
|
+
target: Database,
|
|
322
|
+
selector: () => ({ method: 'connect', args: ['postgres://localhost'] })
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
// Create instance via factory method
|
|
326
|
+
const db = classable.from(dbDef);
|
|
327
|
+
|
|
328
|
+
// With runtime context for dynamic selection
|
|
329
|
+
const dynamicDb = classable.from({
|
|
330
|
+
target: Database,
|
|
331
|
+
selector: (ctx) => ctx.isTest
|
|
332
|
+
? { method: 'createInMemory', args: [] }
|
|
333
|
+
: { method: 'connect', args: [ctx.dbUrl] }
|
|
334
|
+
}, appContext);
|
|
335
|
+
```
|
|
336
|
+
|
|
233
337
|
### Dependency Injection Pattern
|
|
234
338
|
|
|
235
339
|
```typescript
|
|
@@ -332,6 +436,12 @@ const asyncResolver = {
|
|
|
332
436
|
const asyncUser = classable.create(asyncResolver, context); // Promise<User>
|
|
333
437
|
```
|
|
334
438
|
|
|
439
|
+
## Documentation
|
|
440
|
+
|
|
441
|
+
For detailed documentation, guides, and API reference, visit:
|
|
442
|
+
|
|
443
|
+
[https://edge.mxweb.io/classable](https://edge.mxweb.io/classable)
|
|
444
|
+
|
|
335
445
|
## License
|
|
336
446
|
|
|
337
447
|
MIT
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility type that accepts both mutable and readonly versions of a type.
|
|
3
|
+
*
|
|
4
|
+
* This is useful for accepting values from `Object.freeze()` or `as const`
|
|
5
|
+
* without forcing users to always use readonly modifiers.
|
|
6
|
+
*
|
|
7
|
+
* @template T - The base type to make flexible.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* // Accepts both mutable and readonly arrays
|
|
12
|
+
* function process(items: Readonlyable<string[]>) { ... }
|
|
13
|
+
*
|
|
14
|
+
* process(["a", "b"]); // mutable array - OK
|
|
15
|
+
* process(["a", "b"] as const); // readonly array - OK
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export type Readonlyable<T> = T extends Readonly<infer U> ? U | Readonly<U> : T | Readonly<T>;
|
|
1
19
|
/**
|
|
2
20
|
* Represents a class constructor that takes no arguments.
|
|
3
21
|
* @template InstanceType - The type of instance created by this constructor.
|
|
@@ -79,9 +97,9 @@ export type AnyConstructor = ClassType<any, any[]> | AbstractClassType<any, any[
|
|
|
79
97
|
* };
|
|
80
98
|
* ```
|
|
81
99
|
*/
|
|
82
|
-
export interface ClassableByResolver<InstanceType, Args extends any[] = [], Runtime = never> {
|
|
100
|
+
export interface ClassableByResolver<InstanceType, Args extends Readonlyable<any[]> = [], Runtime = never> {
|
|
83
101
|
/** The target class constructor to instantiate. */
|
|
84
|
-
target: ClassType<InstanceType, Args>;
|
|
102
|
+
target: ClassType<InstanceType, [...Args]>;
|
|
85
103
|
/**
|
|
86
104
|
* Function that resolves constructor arguments.
|
|
87
105
|
* Can return arguments synchronously or as a Promise.
|
|
@@ -107,7 +125,7 @@ export interface ClassableByResolver<InstanceType, Args extends any[] = [], Runt
|
|
|
107
125
|
* register({ target: User, resolve: () => ["John", 30] });
|
|
108
126
|
* ```
|
|
109
127
|
*/
|
|
110
|
-
export type Classable<InstanceType, Args extends any[] = [], Runtime = never> = ClassType<InstanceType, Args> | ClassableByResolver<InstanceType, Args, Runtime>;
|
|
128
|
+
export type Classable<InstanceType, Args extends Readonlyable<any[]> = [], Runtime = never> = ClassType<InstanceType, [...Args]> | ClassableByResolver<InstanceType, Args, Runtime>;
|
|
111
129
|
/**
|
|
112
130
|
* A placeholder class used as a marker for unresolved or pending class registrations.
|
|
113
131
|
* Useful in dependency injection containers or lazy initialization patterns.
|
|
@@ -124,6 +142,7 @@ export type Classable<InstanceType, Args extends any[] = [], Runtime = never> =
|
|
|
124
142
|
* ```
|
|
125
143
|
*/
|
|
126
144
|
export declare class Placeholder {
|
|
145
|
+
static getInstance(): Placeholder;
|
|
127
146
|
}
|
|
128
147
|
/**
|
|
129
148
|
* Pre-configured placeholder resolver that creates an empty Placeholder instance.
|
|
@@ -147,7 +166,91 @@ export type ThisExtended<Extend> = Placeholder & Extend;
|
|
|
147
166
|
* @template InstanceType - The instance type of the class.
|
|
148
167
|
* @template Args - Constructor argument types.
|
|
149
168
|
*/
|
|
150
|
-
export type StaticExtended<Extend, InstanceType = any, Args extends any[] = []> = ClassType<InstanceType, Args> & Extend;
|
|
169
|
+
export type StaticExtended<Extend, InstanceType = any, Args extends Readonlyable<any[]> = []> = ClassType<InstanceType, [...Args]> & Extend;
|
|
170
|
+
/**
|
|
171
|
+
* Represents a class whose instances are created via a static factory method.
|
|
172
|
+
*
|
|
173
|
+
* This type is used for classes that expose a static method (e.g., `factory`, `create`)
|
|
174
|
+
* for instantiation instead of using `new` directly.
|
|
175
|
+
*
|
|
176
|
+
* @template InstanceType - The type of instance created by the static method.
|
|
177
|
+
* @template Method - The name of the static method to call (defaults to "factory").
|
|
178
|
+
* @template Args - Tuple type of arguments passed to the static method.
|
|
179
|
+
* @template Runtime - Optional runtime context type passed to the selector.
|
|
180
|
+
*
|
|
181
|
+
* @remarks
|
|
182
|
+
* - The constructor MAY exist and MAY take arguments.
|
|
183
|
+
* - Constructor arguments are considered an implementation detail
|
|
184
|
+
* and are intentionally typed as `any[]`.
|
|
185
|
+
* - Consumers MUST NOT rely on `new`.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* class UserService {
|
|
190
|
+
* private constructor(private db: Database) {}
|
|
191
|
+
*
|
|
192
|
+
* static factory(db: Database): UserService {
|
|
193
|
+
* return new UserService(db);
|
|
194
|
+
* }
|
|
195
|
+
* }
|
|
196
|
+
*
|
|
197
|
+
* const def: InstanceByStatic<UserService, "factory", [Database]> = {
|
|
198
|
+
* target: UserService,
|
|
199
|
+
* selector: () => ({ method: "factory", args: [new Database()] })
|
|
200
|
+
* };
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
export type InstanceByStatic<InstanceType, Method extends string = "factory", Args extends Readonlyable<any[]> = [], Runtime = never> = {
|
|
204
|
+
target: StaticExtended<{
|
|
205
|
+
[K in Method]: (...args: Args) => InstanceType;
|
|
206
|
+
}, InstanceType, any[]>;
|
|
207
|
+
selector: (...args: Runtime extends never ? [] : [runtime: Runtime]) => {
|
|
208
|
+
method: Method;
|
|
209
|
+
args: Args;
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* Pre-configured placeholder instance using static factory method pattern.
|
|
214
|
+
* Use this as a default value for InstanceByStatic bindings.
|
|
215
|
+
*
|
|
216
|
+
* @example
|
|
217
|
+
* ```typescript
|
|
218
|
+
* const instance = classable.from(classable.placeholderInstance);
|
|
219
|
+
* // Returns new Placeholder instance via getInstance()
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
export declare const placeholderInstance: Readonly<{
|
|
223
|
+
target: typeof Placeholder;
|
|
224
|
+
selector: () => {
|
|
225
|
+
method: string;
|
|
226
|
+
args: [];
|
|
227
|
+
};
|
|
228
|
+
}>;
|
|
229
|
+
/**
|
|
230
|
+
* A selector function that chooses and returns a classable with its arguments.
|
|
231
|
+
*
|
|
232
|
+
* This type represents a function that takes a list of classables (and optionally a runtime context)
|
|
233
|
+
* and returns the selected classable along with its constructor arguments.
|
|
234
|
+
*
|
|
235
|
+
* @template InstanceType - The type of instance to be created.
|
|
236
|
+
* @template Args - Tuple type of constructor arguments.
|
|
237
|
+
* @template Runtime - Optional runtime context type for dependency resolution.
|
|
238
|
+
*
|
|
239
|
+
* @returns A tuple of [Classable, Args] or a Promise resolving to the same.
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```typescript
|
|
243
|
+
* const selectByEnv: ClassableSelector<Logger, [], AppContext> = (runtime, ...classes) => {
|
|
244
|
+
* const LoggerClass = runtime.isDev ? DevLogger : ProdLogger;
|
|
245
|
+
* return [LoggerClass, []];
|
|
246
|
+
* };
|
|
247
|
+
*
|
|
248
|
+
* const selectFirst: ClassableSelector<Service, [string]> = (...classes) => {
|
|
249
|
+
* return [classes[0] as Classable<Service, [string]>, ["default"]];
|
|
250
|
+
* };
|
|
251
|
+
* ```
|
|
252
|
+
*/
|
|
253
|
+
export type ClassableSelector<InstanceType, Args extends Readonlyable<any[]> = [], Runtime = never> = (...args: Runtime extends never ? Array<Classable<any, any[]>> : [runtime: Runtime, ...Array<Classable<any, any[], Runtime>>]) => [Classable<InstanceType, Args>, Args] | Promise<[Classable<InstanceType, Args>, Args]>;
|
|
151
254
|
/**
|
|
152
255
|
* Interface defining all available methods on the `classable` API object.
|
|
153
256
|
* This provides type-safe access to class manipulation utilities.
|
|
@@ -156,19 +259,21 @@ export interface ClassableAPI {
|
|
|
156
259
|
/** Reference to the Placeholder class constructor. */
|
|
157
260
|
Placeholder: ClassType<Placeholder>;
|
|
158
261
|
/** Pre-configured placeholder resolver instance. */
|
|
159
|
-
placeholder: ClassableByResolver<Placeholder>;
|
|
262
|
+
placeholder: ClassableByResolver<Placeholder, readonly []>;
|
|
263
|
+
/** Pre-configured placeholder instance using InstanceByStatic pattern. */
|
|
264
|
+
placeholderInstance: InstanceByStatic<Placeholder, "getInstance", []>;
|
|
160
265
|
/** Checks if a value is a class constructor. */
|
|
161
266
|
is: (fn: unknown) => fn is AnyClass<any>;
|
|
162
267
|
/** Checks if a value is an abstract class constructor. */
|
|
163
268
|
isAbstract: (fn: unknown) => fn is AnyAbstractClass<any>;
|
|
164
269
|
/** Checks if a value is a ClassableByResolver object. */
|
|
165
|
-
isResolver: <InstanceType, Args extends any[] = [], Runtime = never>(obj: unknown) => obj is ClassableByResolver<InstanceType, Args, Runtime>;
|
|
270
|
+
isResolver: <InstanceType, Args extends Readonlyable<any[]> = [], Runtime = never>(obj: unknown) => obj is ClassableByResolver<InstanceType, Args, Runtime>;
|
|
166
271
|
/** Converts a class constructor to a resolver configuration. */
|
|
167
|
-
toResolver<T, A extends any[] = [], R = never>(cls: ClassType<T, A>, runtime?: R): ClassableByResolver<T, [], R>;
|
|
272
|
+
toResolver<T, A extends Readonlyable<any[]> = [], R = never>(cls: ClassType<T, [...A]>, runtime?: R): ClassableByResolver<T, [], R>;
|
|
168
273
|
/** Extracts the target class from a Classable (class or resolver). */
|
|
169
274
|
getTarget: <Target>(cls: Classable<Target, any[], any>) => Target;
|
|
170
275
|
/** Creates a new resolver with a custom resolve function. */
|
|
171
|
-
withResolve: <InstanceType, Args extends any[] = [], Runtime = never>(base: Classable<InstanceType, Args, Runtime>, resolve: ClassableByResolver<InstanceType, Args, Runtime>["resolve"]) => ClassableByResolver<InstanceType, Args, Runtime>;
|
|
276
|
+
withResolve: <InstanceType, Args extends Readonlyable<any[]> = [], Runtime = never>(base: Classable<InstanceType, Args, Runtime>, resolve: ClassableByResolver<InstanceType, Args, Runtime>["resolve"]) => ClassableByResolver<InstanceType, Args, Runtime>;
|
|
172
277
|
/** Wraps a class with a transformation function. */
|
|
173
278
|
wrap<T>(cls: ClassType<T>, wrapper: (target: ClassType<T>) => ClassType<T>): ClassType<T>;
|
|
174
279
|
/** Wraps a resolver's target with a transformation function. */
|
|
@@ -181,13 +286,64 @@ export interface ClassableAPI {
|
|
|
181
286
|
/** Creates an instance from a plain class. */
|
|
182
287
|
create<InstanceType>(cls: ClassType<InstanceType>): InstanceType;
|
|
183
288
|
/** Creates an instance from a resolver with sync resolve function. */
|
|
184
|
-
create<InstanceType, Args extends any[]
|
|
289
|
+
create<InstanceType, Args extends Readonlyable<any[]>, Runtime>(cls: ClassableByResolver<InstanceType, Args, Runtime> & {
|
|
185
290
|
resolve: (runtime: Runtime) => Args;
|
|
186
291
|
}, runtime: Runtime): InstanceType;
|
|
187
292
|
/** Creates an instance from a resolver with async resolve function. */
|
|
188
|
-
create<InstanceType, Args extends any[]
|
|
293
|
+
create<InstanceType, Args extends Readonlyable<any[]>, Runtime>(cls: ClassableByResolver<InstanceType, Args, Runtime> & {
|
|
189
294
|
resolve: (runtime: Runtime) => Promise<Args>;
|
|
190
295
|
}, runtime: Runtime): Promise<InstanceType>;
|
|
296
|
+
/**
|
|
297
|
+
* Creates an instance from a static factory method definition.
|
|
298
|
+
*
|
|
299
|
+
* This method invokes the static method specified in the `InstanceByStatic` definition
|
|
300
|
+
* to create an instance, allowing for factory pattern implementations.
|
|
301
|
+
*
|
|
302
|
+
* @template InstanceType - The type of instance created.
|
|
303
|
+
* @template Method - The static method name to invoke.
|
|
304
|
+
* @template Args - Arguments passed to the static method.
|
|
305
|
+
* @template Runtime - Optional runtime context type.
|
|
306
|
+
* @param def - The static factory definition containing target class and selector.
|
|
307
|
+
* @param runtime - Optional runtime context passed to the selector.
|
|
308
|
+
* @returns The created instance.
|
|
309
|
+
*
|
|
310
|
+
* @example
|
|
311
|
+
* ```typescript
|
|
312
|
+
* class Cache {
|
|
313
|
+
* static create(ttl: number): Cache { return new Cache(ttl); }
|
|
314
|
+
* }
|
|
315
|
+
*
|
|
316
|
+
* const instance = classable.from({
|
|
317
|
+
* target: Cache,
|
|
318
|
+
* selector: () => ({ method: "create", args: [3600] })
|
|
319
|
+
* });
|
|
320
|
+
* ```
|
|
321
|
+
*/
|
|
322
|
+
from: <InstanceType, Method extends string = "factory", Args extends Readonlyable<any[]> = [], Runtime = never>(def: InstanceByStatic<InstanceType, Method, Args, Runtime>, runtime?: Runtime) => InstanceType;
|
|
323
|
+
/**
|
|
324
|
+
* Creates a selector function that chooses a classable from a list based on custom logic.
|
|
325
|
+
*
|
|
326
|
+
* This higher-order function takes a selection strategy and returns a function
|
|
327
|
+
* that can be called with classables to select and instantiate the appropriate one.
|
|
328
|
+
*
|
|
329
|
+
* @template InstanceType - The type of instance to be created.
|
|
330
|
+
* @template Args - Constructor arguments for the selected classable.
|
|
331
|
+
* @template Runtime - Optional runtime context type.
|
|
332
|
+
* @param select - The selector function that implements the selection logic.
|
|
333
|
+
* @returns A function that accepts classables and returns the selection result.
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* ```typescript
|
|
337
|
+
* const pickFirst = classable.select((cls1, cls2) => [cls1, []]);
|
|
338
|
+
* const [selected, args] = pickFirst(ServiceA, ServiceB);
|
|
339
|
+
*
|
|
340
|
+
* // With runtime context
|
|
341
|
+
* const pickByEnv = classable.select<Logger, [], Env>((env, ...loggers) => {
|
|
342
|
+
* return env.isDev ? [loggers[0], []] : [loggers[1], []];
|
|
343
|
+
* });
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
select: <InstanceType, Args extends Readonlyable<any[]> = [], Runtime = never>(select: ClassableSelector<InstanceType, Args, Runtime>) => (...args: Runtime extends never ? [...classes: Array<Classable<any, any[], Runtime>>] : [runtime: Runtime, ...classes: Array<Classable<any, any[], Runtime>>]) => ReturnType<ClassableSelector<InstanceType, Args, Runtime>>;
|
|
191
347
|
}
|
|
192
348
|
/**
|
|
193
349
|
* The main classable API object providing utilities for working with classes and resolvers.
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
function e(e,t){return"object"==typeof e&&null!==e&&Object.prototype.hasOwnProperty.call(e,t)}class t{}const r=Object.freeze({target:t,resolve:()=>[]}),o=Object.freeze({Placeholder:t,placeholder:r,is:e=>"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e)),isAbstract:e=>"function"==typeof e&&/^abstract\s+class\s/.test(Function.prototype.toString.call(e)),isResolver:t=>e(t,"target")&&o.is(t.target)&&e(t,"resolve")&&"function"==typeof t.resolve,toResolver:e=>o.isResolver(e)?e:{target:e,resolve:()=>[]},create:(e,t)=>{if(o.isResolver(e)){const r=e.resolve(...void 0===t?[]:[t]);return r instanceof Promise?r.then(t=>new e.target(...t)):new e.target(...r)}return new e(...[])},getTarget:e=>o.isResolver(e)?e.target:e,withResolve:(e,t)=>({target:(o.isResolver(e)?e:o.toResolver(e)).target,resolve:t}),wrap:(e,t)=>o.isResolver(e)?{target:t(e.target),resolve:e.resolve}:t(e),getDescriptor:e=>o.isResolver(e)?{type:"resolver",target:e.target.name}:{type:"class",target:e.name}});export{t as Placeholder,o as classable,r as placeholder};
|
|
1
|
+
function e(e,t){return"object"==typeof e&&null!==e&&Object.prototype.hasOwnProperty.call(e,t)}class t{static getInstance(){return new t}}const r=Object.freeze({target:t,resolve:()=>[]}),s=Object.freeze({target:t,selector:()=>({method:"getInstance",args:[]})}),o=Object.freeze({Placeholder:t,placeholder:r,placeholderInstance:s,is:e=>"function"==typeof e&&/^class\s/.test(Function.prototype.toString.call(e)),isAbstract:e=>"function"==typeof e&&/^abstract\s+class\s/.test(Function.prototype.toString.call(e)),isResolver:t=>e(t,"target")&&o.is(t.target)&&e(t,"resolve")&&"function"==typeof t.resolve,toResolver:e=>o.isResolver(e)?e:{target:e,resolve:()=>[]},create:(e,t)=>{if(o.isResolver(e)){const r=e.resolve(...void 0===t?[]:[t]);return r instanceof Promise?r.then(t=>new e.target(...t)):new e.target(...r)}return new e(...[])},getTarget:e=>o.isResolver(e)?e.target:e,withResolve:(e,t)=>({target:(o.isResolver(e)?e:o.toResolver(e)).target,resolve:t}),wrap:(e,t)=>o.isResolver(e)?{target:t(e.target),resolve:e.resolve}:t(e),getDescriptor:e=>o.isResolver(e)?{type:"resolver",target:e.target.name}:{type:"class",target:e.name},from:(e,t)=>{const{target:r,selector:s}=e,{method:o,args:a}=s(...void 0===t?[]:[t]);return r[o](...a)},select:e=>(...t)=>e(...t)});export{t as Placeholder,o as classable,r as placeholder,s as placeholderInstance};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mxweb/classable",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "A class-first abstraction for defining logic as resolvable units without runtime assumptions.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"resolver",
|
|
12
12
|
"meta-programming",
|
|
13
13
|
"framework-agnostic",
|
|
14
|
-
"esm-only"
|
|
14
|
+
"esm-only",
|
|
15
|
+
"static-factory",
|
|
16
|
+
"dependency-injection"
|
|
15
17
|
],
|
|
16
18
|
"type": "module",
|
|
17
19
|
"module": "dist/index.js",
|
|
@@ -38,7 +40,8 @@
|
|
|
38
40
|
"lint": "eslint \"src/**/*.{ts,tsx}\"",
|
|
39
41
|
"lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
|
|
40
42
|
"format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
|
|
41
|
-
"format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\""
|
|
43
|
+
"format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\"",
|
|
44
|
+
"prepublishOnly": "yarn build"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
47
|
"@babel/core": "^7.28.6",
|