@mxweb/classable 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.0] - 2026-01-17
9
+
10
+ ### Added
11
+
12
+ - **Type Utilities**
13
+ - `UnitClass<T>` - Class constructor with no arguments
14
+ - `ClassType<T, Args>` - Class constructor with specific arguments
15
+ - `AbstractClassType<T, Args>` - Abstract class constructor
16
+ - `AnyClass<T>` - Class constructor with any arguments
17
+ - `AnyAbstractClass<T>` - Abstract class with any arguments
18
+ - `AnyConstructor` - Union type for any constructor
19
+ - `Classable<T, Args, Runtime>` - Class or resolver configuration
20
+ - `ClassableByResolver<T, Args, Runtime>` - Resolver configuration interface
21
+ - `ThisExtended<Extend>` - Utility for extending Placeholder
22
+ - `StaticExtended<Extend, T, Args>` - Utility for static class extension
23
+
24
+ - **classable API**
25
+ - `classable.is(fn)` - Type guard for class constructors
26
+ - `classable.isAbstract(fn)` - Type guard for abstract classes
27
+ - `classable.isResolver(obj)` - Type guard for resolver objects
28
+ - `classable.create(cls, runtime?)` - Instance creation with sync/async resolver support
29
+ - `classable.toResolver(cls)` - Convert class to resolver format
30
+ - `classable.withResolve(base, resolve)` - Create resolver with custom resolve function
31
+ - `classable.wrap(cls, wrapper)` - Apply decorators/middleware to classes
32
+ - `classable.getTarget(cls)` - Extract target class from Classable
33
+ - `classable.getDescriptor(cls)` - Get metadata (type and target name)
34
+
35
+ - **Utilities**
36
+ - `Placeholder` class - Marker for unresolved bindings
37
+ - `placeholder` - Pre-configured placeholder resolver
38
+
39
+ - **Documentation**
40
+ - Full JSDoc documentation for all types and methods
41
+ - Comprehensive README with examples
42
+ - MIT License
43
+
44
+ [1.0.0]: https://github.com/mxwebio/mxweb-classable/releases/tag/v1.0.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 MXWeb
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,337 @@
1
+ # @mxweb/classable
2
+
3
+ A TypeScript utility library for working with classes and deferred instantiation through resolver patterns. Provides type-safe class manipulation, dependency injection patterns, and lazy initialization support.
4
+
5
+ ## ESM-only (by design)
6
+
7
+ Classable relies on the semantic identity of classes:
8
+
9
+ - native `class` syntax
10
+ - stable constructor identity
11
+ - intact static surfaces
12
+ - predictable module graph
13
+
14
+ CommonJS transforms classes and breaks these guarantees at runtime.
15
+
16
+ - This is not a compatibility limitation.
17
+ - It is a design requirement.
18
+
19
+ ## Installation
20
+
21
+ ```bash
22
+ npm install @mxweb/classable
23
+ # or
24
+ yarn add @mxweb/classable
25
+ # or
26
+ pnpm add @mxweb/classable
27
+ ```
28
+
29
+ ## Features
30
+
31
+ - 🎯 **Type-safe class utilities** - Full TypeScript support with precise type inference
32
+ - 🔄 **Resolver pattern** - Separate class instantiation from argument resolution
33
+ - ⏳ **Async support** - Handle async dependency resolution seamlessly
34
+ - 🎁 **Class wrapping** - Apply decorators, mixins, and middleware to classes
35
+ - 🔍 **Type guards** - Runtime checks with TypeScript type narrowing
36
+ - 📦 **Zero dependencies** - Lightweight and self-contained
37
+
38
+ ## Quick Start
39
+
40
+ ```typescript
41
+ import { classable, Classable, ClassableByResolver } from '@mxweb/classable';
42
+
43
+ // Basic class instantiation
44
+ class Logger {
45
+ log(msg: string) {
46
+ console.log(msg);
47
+ }
48
+ }
49
+
50
+ const logger = classable.create(Logger);
51
+ logger.log('Hello!');
52
+ ```
53
+
54
+ ## Core Concepts
55
+
56
+ ### Classable
57
+
58
+ A `Classable` is either a plain class constructor or a resolver configuration object:
59
+
60
+ ```typescript
61
+ import { Classable, ClassableByResolver } from '@mxweb/classable';
62
+
63
+ class User {
64
+ constructor(public name: string, public age: number) {}
65
+ }
66
+
67
+ // Plain class
68
+ const cls: Classable<User> = User;
69
+
70
+ // Resolver with dependency injection
71
+ const resolver: ClassableByResolver<User, [string, number], AppContext> = {
72
+ target: User,
73
+ resolve: (ctx) => [ctx.config.userName, ctx.config.userAge]
74
+ };
75
+ ```
76
+
77
+ ### ClassableByResolver
78
+
79
+ A configuration object that separates class instantiation from argument resolution:
80
+
81
+ ```typescript
82
+ interface ClassableByResolver<InstanceType, Args, Runtime> {
83
+ target: ClassType<InstanceType, Args>; // The class to instantiate
84
+ resolve: (runtime: Runtime) => Args | Promise<Args>; // Argument resolver
85
+ }
86
+ ```
87
+
88
+ ## API Reference
89
+
90
+ ### Type Utilities
91
+
92
+ | Type | Description |
93
+ |------|-------------|
94
+ | `UnitClass<T>` | Class constructor with no arguments |
95
+ | `ClassType<T, Args>` | Class constructor with specific arguments |
96
+ | `AbstractClassType<T, Args>` | Abstract class constructor |
97
+ | `AnyClass<T>` | Class constructor with any arguments |
98
+ | `AnyAbstractClass<T>` | Abstract class with any arguments |
99
+ | `AnyConstructor` | Any class or abstract class |
100
+ | `Classable<T, Args, Runtime>` | Class or resolver configuration |
101
+ | `ClassableByResolver<T, Args, Runtime>` | Resolver configuration |
102
+
103
+ ### `classable` API
104
+
105
+ #### `classable.is(fn)`
106
+
107
+ Checks if a value is a class constructor.
108
+
109
+ ```typescript
110
+ class MyClass {}
111
+ classable.is(MyClass); // true
112
+ classable.is(() => {}); // false
113
+ classable.is({}); // false
114
+ ```
115
+
116
+ #### `classable.isAbstract(fn)`
117
+
118
+ Checks if a value is an abstract class constructor.
119
+
120
+ ```typescript
121
+ abstract class BaseService {}
122
+ classable.isAbstract(BaseService); // true
123
+ classable.isAbstract(MyClass); // false
124
+ ```
125
+
126
+ #### `classable.isResolver(obj)`
127
+
128
+ Checks if a value is a `ClassableByResolver` object.
129
+
130
+ ```typescript
131
+ const resolver = { target: User, resolve: () => ['John', 30] };
132
+ classable.isResolver(resolver); // true
133
+ classable.isResolver(User); // false
134
+ ```
135
+
136
+ #### `classable.create(cls, runtime?)`
137
+
138
+ Creates an instance from a class or resolver. Handles both sync and async resolvers.
139
+
140
+ ```typescript
141
+ // Plain class
142
+ const logger = classable.create(Logger);
143
+
144
+ // Sync resolver
145
+ const user = classable.create({
146
+ target: User,
147
+ resolve: (ctx) => [ctx.name, ctx.age]
148
+ }, context);
149
+
150
+ // Async resolver
151
+ const user = await classable.create({
152
+ target: User,
153
+ resolve: async (ctx) => {
154
+ const data = await fetchUserData();
155
+ return [data.name, data.age];
156
+ }
157
+ }, context);
158
+ ```
159
+
160
+ #### `classable.toResolver(cls)`
161
+
162
+ Converts a class constructor to a resolver configuration.
163
+
164
+ ```typescript
165
+ const resolver = classable.toResolver(User);
166
+ // { target: User, resolve: () => [] }
167
+ ```
168
+
169
+ #### `classable.getTarget(cls)`
170
+
171
+ Extracts the target class from a Classable.
172
+
173
+ ```typescript
174
+ const target = classable.getTarget(resolver); // User class
175
+ const target2 = classable.getTarget(User); // User class
176
+ ```
177
+
178
+ #### `classable.withResolve(base, resolve)`
179
+
180
+ Creates a new resolver with a custom resolve function.
181
+
182
+ ```typescript
183
+ const customResolver = classable.withResolve(User, (ctx) => {
184
+ return [ctx.name, ctx.age];
185
+ });
186
+ ```
187
+
188
+ #### `classable.wrap(cls, wrapper)`
189
+
190
+ Wraps a class or resolver's target with a transformation function.
191
+
192
+ ```typescript
193
+ const timestampedLogger = classable.wrap(Logger, (Target) => {
194
+ return class extends Target {
195
+ log(msg: string) {
196
+ super.log(`[${new Date().toISOString()}] ${msg}`);
197
+ }
198
+ };
199
+ });
200
+ ```
201
+
202
+ #### `classable.getDescriptor(cls)`
203
+
204
+ Returns metadata about the classable.
205
+
206
+ ```typescript
207
+ classable.getDescriptor(User);
208
+ // { type: "class", target: "User" }
209
+
210
+ classable.getDescriptor(resolver);
211
+ // { type: "resolver", target: "User" }
212
+ ```
213
+
214
+ ### Placeholder
215
+
216
+ A utility class for marking unresolved bindings:
217
+
218
+ ```typescript
219
+ import { classable } from '@mxweb/classable';
220
+
221
+ // Use as default value
222
+ class Container {
223
+ private bindings = new Map();
224
+
225
+ register(key: string, cls = classable.placeholder) {
226
+ this.bindings.set(key, cls);
227
+ }
228
+ }
229
+ ```
230
+
231
+ ## Advanced Usage
232
+
233
+ ### Dependency Injection Pattern
234
+
235
+ ```typescript
236
+ interface AppContext {
237
+ db: Database;
238
+ config: Config;
239
+ }
240
+
241
+ class UserService {
242
+ constructor(
243
+ private db: Database,
244
+ private maxUsers: number
245
+ ) {}
246
+ }
247
+
248
+ const userServiceResolver: ClassableByResolver<
249
+ UserService,
250
+ [Database, number],
251
+ AppContext
252
+ > = {
253
+ target: UserService,
254
+ resolve: (ctx) => [ctx.db, ctx.config.maxUsers]
255
+ };
256
+
257
+ // Create with context
258
+ const context: AppContext = { db: new Database(), config: { maxUsers: 100 } };
259
+ const service = classable.create(userServiceResolver, context);
260
+ ```
261
+
262
+ ### Async Dependency Resolution
263
+
264
+ ```typescript
265
+ const asyncResolver: ClassableByResolver<User, [string], DbContext> = {
266
+ target: User,
267
+ resolve: async (ctx) => {
268
+ const userData = await ctx.db.fetchUser(ctx.userId);
269
+ return [userData.name];
270
+ }
271
+ };
272
+
273
+ // Returns Promise<User>
274
+ const user = await classable.create(asyncResolver, dbContext);
275
+ ```
276
+
277
+ ### Class Middleware/Decorators
278
+
279
+ ```typescript
280
+ // Add logging to all methods
281
+ const withLogging = classable.wrap(MyService, (Target) => {
282
+ return class extends Target {
283
+ constructor(...args: any[]) {
284
+ super(...args);
285
+ console.log(`Created ${Target.name}`);
286
+ }
287
+ };
288
+ });
289
+
290
+ // Chain multiple wrappers
291
+ const enhanced = classable.wrap(
292
+ classable.wrap(MyService, withLogging),
293
+ withMetrics
294
+ );
295
+ ```
296
+
297
+ ### Type Guards with Narrowing
298
+
299
+ ```typescript
300
+ function processClassable(input: unknown) {
301
+ if (classable.isResolver(input)) {
302
+ // TypeScript knows input has 'target' and 'resolve'
303
+ console.log(`Resolver for: ${input.target.name}`);
304
+ } else if (classable.is(input)) {
305
+ // TypeScript knows input is a class
306
+ console.log(`Class: ${input.name}`);
307
+ }
308
+ }
309
+ ```
310
+
311
+ ## TypeScript Support
312
+
313
+ This library is written in TypeScript and provides full type inference:
314
+
315
+ ```typescript
316
+ // Types are automatically inferred
317
+ const resolver = {
318
+ target: User,
319
+ resolve: (ctx: AppContext) => [ctx.name, ctx.age] as [string, number]
320
+ };
321
+
322
+ // Return type is correctly inferred as User
323
+ const user = classable.create(resolver, context);
324
+
325
+ // Async resolver returns Promise<User>
326
+ const asyncResolver = {
327
+ target: User,
328
+ resolve: async (ctx: AppContext) => {
329
+ return [await getName(), await getAge()] as [string, number];
330
+ }
331
+ };
332
+ const asyncUser = classable.create(asyncResolver, context); // Promise<User>
333
+ ```
334
+
335
+ ## License
336
+
337
+ MIT
@@ -0,0 +1,225 @@
1
+ /**
2
+ * Represents a class constructor that takes no arguments.
3
+ * @template InstanceType - The type of instance created by this constructor.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * class Logger {}
8
+ * const LoggerClass: UnitClass<Logger> = Logger;
9
+ * ```
10
+ */
11
+ export type UnitClass<InstanceType> = new () => InstanceType;
12
+ /**
13
+ * Represents a class constructor with specific constructor arguments.
14
+ * @template InstanceType - The type of instance created by this constructor.
15
+ * @template Args - Tuple type of constructor arguments, defaults to empty array.
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * class User {
20
+ * constructor(name: string, age: number) {}
21
+ * }
22
+ * const UserClass: ClassType<User, [string, number]> = User;
23
+ * ```
24
+ */
25
+ export type ClassType<InstanceType, Args extends any[] = []> = new (...args: Args) => InstanceType;
26
+ /**
27
+ * Represents an abstract class constructor.
28
+ * @template InstanceType - The type of instance this abstract class represents.
29
+ * @template Args - Tuple type of constructor arguments, defaults to empty array.
30
+ *
31
+ * @example
32
+ * ```typescript
33
+ * abstract class BaseService {
34
+ * abstract execute(): void;
35
+ * }
36
+ * const ServiceClass: AbstractClassType<BaseService> = BaseService;
37
+ * ```
38
+ */
39
+ export type AbstractClassType<InstanceType, Args extends any[] = []> = abstract new (...args: Args) => InstanceType;
40
+ /**
41
+ * Shorthand for a class constructor with any arguments.
42
+ * @template T - The type of instance created by this constructor.
43
+ */
44
+ export type AnyClass<T> = ClassType<T, any[]>;
45
+ /**
46
+ * Shorthand for an abstract class constructor with any arguments.
47
+ * @template T - The type of instance this abstract class represents.
48
+ */
49
+ export type AnyAbstractClass<T> = AbstractClassType<T, any[]>;
50
+ /**
51
+ * Union type representing any constructor (class or abstract class).
52
+ */
53
+ export type AnyConstructor = ClassType<any, any[]> | AbstractClassType<any, any[]>;
54
+ /**
55
+ * Configuration object for deferred class instantiation with dependency resolution.
56
+ *
57
+ * This interface allows you to separate class instantiation from argument resolution,
58
+ * enabling lazy evaluation and runtime dependency injection.
59
+ *
60
+ * @template InstanceType - The type of instance to be created.
61
+ * @template Args - Tuple type of constructor arguments.
62
+ * @template Runtime - Optional runtime context type passed to the resolver.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * // Sync resolver
67
+ * const userResolver: ClassableByResolver<User, [string, number], AppContext> = {
68
+ * target: User,
69
+ * resolve: (ctx) => [ctx.config.name, ctx.config.age]
70
+ * };
71
+ *
72
+ * // Async resolver
73
+ * const asyncResolver: ClassableByResolver<User, [string], DbContext> = {
74
+ * target: User,
75
+ * resolve: async (ctx) => {
76
+ * const name = await ctx.db.fetchName();
77
+ * return [name];
78
+ * }
79
+ * };
80
+ * ```
81
+ */
82
+ export interface ClassableByResolver<InstanceType, Args extends any[] = [], Runtime = never> {
83
+ /** The target class constructor to instantiate. */
84
+ target: ClassType<InstanceType, Args>;
85
+ /**
86
+ * Function that resolves constructor arguments.
87
+ * Can return arguments synchronously or as a Promise.
88
+ */
89
+ resolve: (...args: Runtime extends never ? [] : [runtime: Runtime]) => Args | Promise<Args>;
90
+ }
91
+ /**
92
+ * Union type representing either a direct class constructor or a resolver configuration.
93
+ *
94
+ * Use this type when a function accepts both plain classes and resolver objects.
95
+ *
96
+ * @template InstanceType - The type of instance to be created.
97
+ * @template Args - Tuple type of constructor arguments.
98
+ * @template Runtime - Optional runtime context type for resolvers.
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * function register<T>(cls: Classable<T>) {
103
+ * // Accepts both User and { target: User, resolve: () => [...] }
104
+ * }
105
+ *
106
+ * register(User);
107
+ * register({ target: User, resolve: () => ["John", 30] });
108
+ * ```
109
+ */
110
+ export type Classable<InstanceType, Args extends any[] = [], Runtime = never> = ClassType<InstanceType, Args> | ClassableByResolver<InstanceType, Args, Runtime>;
111
+ /**
112
+ * A placeholder class used as a marker for unresolved or pending class registrations.
113
+ * Useful in dependency injection containers or lazy initialization patterns.
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * class Container {
118
+ * private bindings = new Map<string, Classable<any>>();
119
+ *
120
+ * register(key: string, cls: Classable<any> = classable.placeholder) {
121
+ * this.bindings.set(key, cls);
122
+ * }
123
+ * }
124
+ * ```
125
+ */
126
+ export declare class Placeholder {
127
+ }
128
+ /**
129
+ * Pre-configured placeholder resolver that creates an empty Placeholder instance.
130
+ * Use this as a default value when a classable binding is not yet configured.
131
+ */
132
+ export declare const placeholder: Readonly<{
133
+ target: typeof Placeholder;
134
+ resolve: () => readonly [];
135
+ }>;
136
+ /**
137
+ * Utility type for extending the Placeholder class in type definitions.
138
+ * Useful for creating typed placeholders with additional properties.
139
+ *
140
+ * @template Extend - Additional properties to add to the Placeholder type.
141
+ */
142
+ export type ThisExtended<Extend> = Placeholder & Extend;
143
+ /**
144
+ * Utility type for creating a static class type with additional properties.
145
+ *
146
+ * @template Extend - Static properties to add to the class.
147
+ * @template InstanceType - The instance type of the class.
148
+ * @template Args - Constructor argument types.
149
+ */
150
+ export type StaticExtended<Extend, InstanceType = any, Args extends any[] = []> = ClassType<InstanceType, Args> & Extend;
151
+ /**
152
+ * Interface defining all available methods on the `classable` API object.
153
+ * This provides type-safe access to class manipulation utilities.
154
+ */
155
+ export interface ClassableAPI {
156
+ /** Reference to the Placeholder class constructor. */
157
+ Placeholder: ClassType<Placeholder>;
158
+ /** Pre-configured placeholder resolver instance. */
159
+ placeholder: ClassableByResolver<Placeholder>;
160
+ /** Checks if a value is a class constructor. */
161
+ is: (fn: unknown) => fn is AnyClass<any>;
162
+ /** Checks if a value is an abstract class constructor. */
163
+ isAbstract: (fn: unknown) => fn is AnyAbstractClass<any>;
164
+ /** Checks if a value is a ClassableByResolver object. */
165
+ isResolver: <InstanceType, Args extends any[] = [], Runtime = never>(obj: unknown) => obj is ClassableByResolver<InstanceType, Args, Runtime>;
166
+ /** 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>;
168
+ /** Extracts the target class from a Classable (class or resolver). */
169
+ getTarget: <Target>(cls: Classable<Target, any[], any>) => Target;
170
+ /** 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>;
172
+ /** Wraps a class with a transformation function. */
173
+ wrap<T>(cls: ClassType<T>, wrapper: (target: ClassType<T>) => ClassType<T>): ClassType<T>;
174
+ /** Wraps a resolver's target with a transformation function. */
175
+ wrap<T, A extends any[], R>(cls: ClassableByResolver<T, A, R>, wrapper: (target: ClassType<T>) => ClassType<T>): ClassableByResolver<T, A, R>;
176
+ /** Returns metadata about the classable (type and target name). */
177
+ getDescriptor: (cls: Classable<any, any[], any>) => {
178
+ type: "class" | "resolver";
179
+ target: string;
180
+ };
181
+ /** Creates an instance from a plain class. */
182
+ create<InstanceType>(cls: ClassType<InstanceType>): InstanceType;
183
+ /** Creates an instance from a resolver with sync resolve function. */
184
+ create<InstanceType, Args extends any[], Runtime>(cls: ClassableByResolver<InstanceType, Args, Runtime> & {
185
+ resolve: (runtime: Runtime) => Args;
186
+ }, runtime: Runtime): InstanceType;
187
+ /** Creates an instance from a resolver with async resolve function. */
188
+ create<InstanceType, Args extends any[], Runtime>(cls: ClassableByResolver<InstanceType, Args, Runtime> & {
189
+ resolve: (runtime: Runtime) => Promise<Args>;
190
+ }, runtime: Runtime): Promise<InstanceType>;
191
+ }
192
+ /**
193
+ * The main classable API object providing utilities for working with classes and resolvers.
194
+ *
195
+ * This frozen object contains methods for:
196
+ * - Type checking (`is`, `isAbstract`, `isResolver`)
197
+ * - Converting between formats (`toResolver`, `withResolve`)
198
+ * - Instance creation (`create`)
199
+ * - Class manipulation (`wrap`, `getTarget`, `getDescriptor`)
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * // Check if something is a class
204
+ * if (classable.is(MyClass)) {
205
+ * const instance = classable.create(MyClass);
206
+ * }
207
+ *
208
+ * // Create instance with resolver
209
+ * const resolver = {
210
+ * target: User,
211
+ * resolve: (ctx: AppContext) => [ctx.userName, ctx.userAge]
212
+ * };
213
+ * const user = classable.create(resolver, appContext);
214
+ *
215
+ * // Wrap a class with middleware
216
+ * const wrapped = classable.wrap(Logger, (Target) => {
217
+ * return class extends Target {
218
+ * log(msg: string) {
219
+ * super.log(`[${Date.now()}] ${msg}`);
220
+ * }
221
+ * };
222
+ * });
223
+ * ```
224
+ */
225
+ export declare const classable: Readonly<ClassableAPI>;
package/dist/index.mjs ADDED
@@ -0,0 +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};
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@mxweb/classable",
3
+ "version": "1.0.0",
4
+ "description": "A class-first abstraction for defining logic as resolvable units without runtime assumptions.",
5
+ "license": "MIT",
6
+ "keywords": [
7
+ "classable",
8
+ "class-first",
9
+ "logic-units",
10
+ "declarative",
11
+ "resolver",
12
+ "meta-programming",
13
+ "framework-agnostic",
14
+ "esm-only"
15
+ ],
16
+ "type": "module",
17
+ "module": "dist/index.js",
18
+ "types": "dist/index.d.ts",
19
+ "author": "MxWeb Team <mxwebio@gmail.com>",
20
+ "homepage": "https://edge.mxweb.io/classable",
21
+ "sideEffects": false,
22
+ "publishConfig": {
23
+ "access": "public"
24
+ },
25
+ "repository": {
26
+ "url": "https://github.com/mxwebio/mxweb-classable"
27
+ },
28
+ "exports": {
29
+ ".": {
30
+ "import": "./dist/index.js",
31
+ "types": "./dist/index.d.ts"
32
+ }
33
+ },
34
+ "scripts": {
35
+ "clean": "rimraf dist",
36
+ "build": "yarn clean && yarn lint && rollup -c",
37
+ "build:watch": "rollup -c -w",
38
+ "lint": "eslint \"src/**/*.{ts,tsx}\"",
39
+ "lint:fix": "eslint \"src/**/*.{ts,tsx}\" --fix",
40
+ "format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
41
+ "format:check": "prettier --check \"src/**/*.{ts,tsx,json,md}\""
42
+ },
43
+ "devDependencies": {
44
+ "@babel/core": "^7.28.6",
45
+ "@rollup/plugin-terser": "^0.4.4",
46
+ "@rollup/plugin-typescript": "^12.3.0",
47
+ "@types/node": "^20",
48
+ "@typescript-eslint/eslint-plugin": "^8.53.0",
49
+ "@typescript-eslint/parser": "^8.53.0",
50
+ "eslint": "^9.39.2",
51
+ "eslint-config-prettier": "^10.1.8",
52
+ "eslint-plugin-prettier": "^5.5.5",
53
+ "glob": "^13.0.0",
54
+ "prettier": "^3.8.0",
55
+ "rimraf": "^6.1.2",
56
+ "rollup": "^4.55.1",
57
+ "tslib": "^2.8.1",
58
+ "typescript": "^5"
59
+ },
60
+ "engines": {
61
+ "node": ">=14.0.0"
62
+ },
63
+ "files": [
64
+ "dist",
65
+ "README.md",
66
+ "CHANGELOG.md",
67
+ "LICENSE"
68
+ ]
69
+ }