getbox 1.2.0 → 1.3.1

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/CONTEXT.md CHANGED
@@ -57,9 +57,7 @@ import { Box } from "getbox";
57
57
  class UserService {
58
58
  constructor(private db: Database, private logger: Logger) {}
59
59
 
60
- static init(box: Box) {
61
- return box.for(UserService).get(Database, LoggerFactory);
62
- }
60
+ static init = Box.init(UserService).get(Database, LoggerFactory);
63
61
  }
64
62
  ```
65
63
 
package/README.md CHANGED
@@ -6,8 +6,6 @@
6
6
 
7
7
  Callers know the type of the value they need, but not how it will be derived. The box resolves constructors lazily and caches instances automatically.
8
8
 
9
- For an alternative pattern using AsyncLocalStorage where classes can resolve dependencies directly in their constructors, see [getbox/context](./CONTEXT.md).
10
-
11
9
  ## Installation
12
10
 
13
11
  ```sh
@@ -16,7 +14,9 @@ npm install getbox
16
14
 
17
15
  ## Usage
18
16
 
19
- `getbox` has a very small API surface. You typically only need to use the `Box.get()` and optionally static init methods or the `factory` helper.
17
+ `getbox` has a very small API surface. You typically only need `box.get()` and optionally `static init` or the `factory` helper.
18
+
19
+ For an alternative pattern using AsyncLocalStorage where classes can resolve dependencies directly in their constructors, see [getbox/context](./CONTEXT.md).
20
20
 
21
21
  ### Create a class
22
22
 
@@ -33,20 +33,17 @@ export class Printer {
33
33
 
34
34
  ### Use in another class
35
35
 
36
- Retrieve instances by calling `box.get(Constructor)` within your class constructor or factory function.
36
+ The `static init` property tells the box what dependencies to pass when instantiating the class.
37
37
 
38
38
  ```ts
39
39
  // office.ts
40
- import { Box, factory } from "getbox";
40
+ import { Box } from "getbox";
41
41
  import { Printer } from "./printer";
42
42
 
43
43
  export class Office {
44
44
  constructor(public printer: Printer) {}
45
45
 
46
- static init(box: Box) {
47
- const printer = box.get(Printer);
48
- return new Office(printer);
49
- }
46
+ static init = Box.init(Office).get(Printer);
50
47
  }
51
48
  ```
52
49
 
@@ -106,14 +103,12 @@ export interface Logger {
106
103
  log(message: string): void;
107
104
  }
108
105
 
109
- export class ConsoleLogger implements Logger {
110
- log(message: string): void {
111
- console.log(`[LOG] ${message}`);
112
- }
113
- }
114
-
115
106
  const LoggerFactory = factory((box: Box): Logger => {
116
- return new ConsoleLogger();
107
+ return {
108
+ log(message: string): void {
109
+ console.log(`[LOG] ${message}`);
110
+ },
111
+ };
117
112
  });
118
113
  ```
119
114
 
@@ -125,10 +120,7 @@ import { Logger, LoggerFactory } from "./logger";
125
120
  export class UserService {
126
121
  constructor(private logger: Logger) {}
127
122
 
128
- static init(box: Box) {
129
- const logger = box.get(LoggerFactory);
130
- return new UserService(logger);
131
- }
123
+ static init = Box.init(UserService).get(LoggerFactory);
132
124
 
133
125
  createUser(name: string) {
134
126
  this.logger.log(`Creating user: ${name}`);
@@ -219,18 +211,15 @@ console.log(db === db2); // false (transient)
219
211
 
220
212
  ## Class constructors
221
213
 
222
- Use `box.for()` inside a class's `static init` method to resolve constructor dependencies automatically. The instance returned by the builder is cached or transient depending on whether the class is retrieved via `box.get()` or `box.new()`.
214
+ Use `Box.init` to allow resolving classes that have constructor parameters.
223
215
 
224
216
  ```ts
225
- import { Box, factory } from "getbox";
217
+ import { Box } from "getbox";
226
218
 
227
219
  class UserService {
228
220
  constructor(private db: Database, private logger: Logger) {}
229
221
 
230
- static init(box: Box) {
231
- // Create new instance with cached dependencies
232
- return box.for(UserService).get(Database, LoggerFactory);
233
- }
222
+ static init = Box.init(UserService).get(Database, LoggerFactory);
234
223
 
235
224
  createUser(name: string) {
236
225
  this.logger.log(`Creating user: ${name}`);
@@ -242,6 +231,34 @@ const service = box.get(UserService);
242
231
  service.createUser("Alice");
243
232
  ```
244
233
 
234
+ `Box.init` is shorthand for writing the `static init` method yourself.
235
+
236
+ ```ts
237
+ class UserService {
238
+ constructor(private db: Database, private logger: Logger) {}
239
+
240
+ static init(box: Box) {
241
+ return new UserService(box.get(Database), box.get(LoggerFactory));
242
+ }
243
+ }
244
+ ```
245
+
246
+ Use `box.for()` when you need custom logic alongside dependency resolution.
247
+
248
+ ```ts
249
+ class UserService {
250
+ constructor(private db: Database, private logger: Logger) {}
251
+
252
+ static init(box: Box) {
253
+ const logger = box.get(LoggerFactory);
254
+ logger.log("Initializing UserService");
255
+ return box.for(UserService).get(Database, LoggerFactory);
256
+ }
257
+ }
258
+ ```
259
+
260
+ Classes with no constructor parameters are resolved automatically without needing `static init`. If `static init` is defined, it takes priority over the default constructor.
261
+
245
262
  ## Mocking
246
263
 
247
264
  You can mock dependencies for testing using `Box.mock`. This is particularly useful with factories and interfaces.
@@ -17,7 +17,7 @@ declare function useBox(): Box;
17
17
  * Resolves a cached instance from the current {@link withBox} scope.
18
18
  * Shorthand for `useBox().get(constructor)`.
19
19
  */
20
- declare function resolve<T>(constructor: Constructor<T>): T;
20
+ declare function resolve<T extends Constructor<any>>(constructor: T): ConstructorInstanceType<T>;
21
21
  /**
22
22
  * Resolves multiple cached instances from the current {@link withBox} scope.
23
23
  * Shorthand for `useBox().all.get(constructors)`.
@@ -17,7 +17,7 @@ declare function useBox(): Box;
17
17
  * Resolves a cached instance from the current {@link withBox} scope.
18
18
  * Shorthand for `useBox().get(constructor)`.
19
19
  */
20
- declare function resolve<T>(constructor: Constructor<T>): T;
20
+ declare function resolve<T extends Constructor<any>>(constructor: T): ConstructorInstanceType<T>;
21
21
  /**
22
22
  * Resolves multiple cached instances from the current {@link withBox} scope.
23
23
  * Shorthand for `useBox().all.get(constructors)`.
package/dist/index.cjs CHANGED
@@ -16,7 +16,7 @@
16
16
  function factory(init) {
17
17
  return { init };
18
18
  }
19
- const noCacheSymbol = Symbol("Box constant");
19
+ const noCacheSymbol = Symbol("Box.noCache");
20
20
  /**
21
21
  * Creates a {@link Constructor} from a factory function whose result is never cached.
22
22
  * The factory is called on every resolution, always returning a fresh value
@@ -115,6 +115,21 @@ var Box = class {
115
115
  return new Construct(this, constructor);
116
116
  }
117
117
  /**
118
+ * Returns a {@link StaticConstruct} builder for defining a `static init` method.
119
+ * The result can be assigned directly to `static init`.
120
+ *
121
+ * @example
122
+ * ```ts
123
+ * class UserService {
124
+ * constructor(private db: Database, private logger: Logger) {}
125
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
126
+ * }
127
+ * ```
128
+ */
129
+ static init(constructor) {
130
+ return new StaticConstruct(constructor);
131
+ }
132
+ /**
118
133
  * Registers a mock value in the box's cache for a given constructor.
119
134
  * Useful for replacing dependencies in tests.
120
135
  */
@@ -197,10 +212,64 @@ var Construct = class {
197
212
  return new this.construct(...instances);
198
213
  }
199
214
  };
215
+ /**
216
+ * Builder for creating a `static init` function with constructor dependencies
217
+ * resolved from a {@link Box}.
218
+ */
219
+ var StaticConstruct = class {
220
+ constructor(construct) {
221
+ this.construct = construct;
222
+ }
223
+ /**
224
+ * Resolves each dependency as a new transient instance via {@link Box.new},
225
+ * meaning dependencies are not cached or shared.
226
+ * Returns a function compatible with `static init` that can be assigned directly.
227
+ *
228
+ * The returned instance is cached or transient depending on whether the
229
+ * class is retrieved via {@link Box.get} or {@link Box.new}.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * class UserService {
234
+ * constructor(private db: Database, private logger: Logger) {}
235
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
236
+ * }
237
+ * ```
238
+ */
239
+ new(...args) {
240
+ return (box) => {
241
+ const instances = args.map((arg) => box.new(arg));
242
+ return new this.construct(...instances);
243
+ };
244
+ }
245
+ /**
246
+ * Resolves each dependency as a cached instance via {@link Box.get},
247
+ * meaning dependencies are shared across the box.
248
+ * Returns a function compatible with `static init` that can be assigned directly.
249
+ *
250
+ * The returned instance is cached or transient depending on whether the
251
+ * class is retrieved via {@link Box.get} or {@link Box.new}.
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * class UserService {
256
+ * constructor(private db: Database, private logger: Logger) {}
257
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
258
+ * }
259
+ * ```
260
+ */
261
+ get(...args) {
262
+ return (box) => {
263
+ const instances = args.map((arg) => box.get(arg));
264
+ return new this.construct(...instances);
265
+ };
266
+ }
267
+ };
200
268
 
201
269
  //#endregion
202
270
  exports.Box = Box;
203
271
  exports.Construct = Construct;
272
+ exports.StaticConstruct = StaticConstruct;
204
273
  exports.constant = constant;
205
274
  exports.factory = factory;
206
275
  exports.transient = transient;
package/dist/index.d.cts CHANGED
@@ -10,7 +10,11 @@ type Constructor<T> = {
10
10
  new (): T;
11
11
  };
12
12
  /** Extracts the instance type from a {@link Constructor}. */
13
- type ConstructorInstanceType<T> = T extends Constructor<infer U> ? U : never;
13
+ type ConstructorInstanceType<T> = T extends {
14
+ init(box: Box): infer U;
15
+ } ? U : T extends {
16
+ new (): infer U;
17
+ } ? U : never;
14
18
  /**
15
19
  * Creates a {@link Constructor} from a factory function.
16
20
  * The factory receives the box as an argument, allowing it to resolve other dependencies.
@@ -86,12 +90,12 @@ declare class Box {
86
90
  * Creates a new (transient) instance without caching. Useful for instances
87
91
  * that should not be shared.
88
92
  */
89
- new<T>(constructor: Constructor<T>): T;
93
+ new<T extends Constructor<any>>(constructor: T): ConstructorInstanceType<T>;
90
94
  /**
91
95
  * Resolves an instance from the cache, or creates and caches a new one.
92
96
  * Subsequent calls with the same constructor return the cached instance.
93
97
  */
94
- get<T>(constructor: Constructor<T>): T;
98
+ get<T extends Constructor<any>>(constructor: T): ConstructorInstanceType<T>;
95
99
  /** Resolves multiple constructors at once. */
96
100
  readonly all: BoxAll;
97
101
  /**
@@ -103,6 +107,19 @@ declare class Box {
103
107
  * the class is retrieved via {@link Box.get} or kept transient via {@link Box.new}.
104
108
  */
105
109
  for<T extends ClassConstructor<any>>(constructor: T): Construct<T>;
110
+ /**
111
+ * Returns a {@link StaticConstruct} builder for defining a `static init` method.
112
+ * The result can be assigned directly to `static init`.
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * class UserService {
117
+ * constructor(private db: Database, private logger: Logger) {}
118
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
119
+ * }
120
+ * ```
121
+ */
122
+ static init<T extends ClassConstructor<any>>(constructor: T): StaticConstruct<T>;
106
123
  /**
107
124
  * Registers a mock value in the box's cache for a given constructor.
108
125
  * Useful for replacing dependencies in tests.
@@ -173,6 +190,48 @@ declare class Construct<T extends ClassConstructor<any>> {
173
190
  */
174
191
  get(...args: ClassConstructorArgs<T>): InstanceType<T>;
175
192
  }
193
+ /**
194
+ * Builder for creating a `static init` function with constructor dependencies
195
+ * resolved from a {@link Box}.
196
+ */
197
+ declare class StaticConstruct<T extends ClassConstructor<any>> {
198
+ private construct;
199
+ constructor(construct: T);
200
+ /**
201
+ * Resolves each dependency as a new transient instance via {@link Box.new},
202
+ * meaning dependencies are not cached or shared.
203
+ * Returns a function compatible with `static init` that can be assigned directly.
204
+ *
205
+ * The returned instance is cached or transient depending on whether the
206
+ * class is retrieved via {@link Box.get} or {@link Box.new}.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * class UserService {
211
+ * constructor(private db: Database, private logger: Logger) {}
212
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
213
+ * }
214
+ * ```
215
+ */
216
+ new(...args: ClassConstructorArgs<T>): (box: Box) => InstanceType<T>;
217
+ /**
218
+ * Resolves each dependency as a cached instance via {@link Box.get},
219
+ * meaning dependencies are shared across the box.
220
+ * Returns a function compatible with `static init` that can be assigned directly.
221
+ *
222
+ * The returned instance is cached or transient depending on whether the
223
+ * class is retrieved via {@link Box.get} or {@link Box.new}.
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * class UserService {
228
+ * constructor(private db: Database, private logger: Logger) {}
229
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
230
+ * }
231
+ * ```
232
+ */
233
+ get(...args: ClassConstructorArgs<T>): (box: Box) => InstanceType<T>;
234
+ }
176
235
  /** A class with any constructor signature. */
177
236
  type ClassConstructor<T> = {
178
237
  new (...args: any): T;
@@ -180,4 +239,4 @@ type ClassConstructor<T> = {
180
239
  /** Maps each constructor parameter to its corresponding {@link Constructor} type. */
181
240
  type ClassConstructorArgs<T extends ClassConstructor<any>, Args = ConstructorParameters<T>> = { [K in keyof Args]: Constructor<Args[K]> };
182
241
  //#endregion
183
- export { Box, Construct, Constructor, ConstructorInstanceType, constant, factory, transient };
242
+ export { Box, Construct, Constructor, ConstructorInstanceType, StaticConstruct, constant, factory, transient };
package/dist/index.d.mts CHANGED
@@ -10,7 +10,11 @@ type Constructor<T> = {
10
10
  new (): T;
11
11
  };
12
12
  /** Extracts the instance type from a {@link Constructor}. */
13
- type ConstructorInstanceType<T> = T extends Constructor<infer U> ? U : never;
13
+ type ConstructorInstanceType<T> = T extends {
14
+ init(box: Box): infer U;
15
+ } ? U : T extends {
16
+ new (): infer U;
17
+ } ? U : never;
14
18
  /**
15
19
  * Creates a {@link Constructor} from a factory function.
16
20
  * The factory receives the box as an argument, allowing it to resolve other dependencies.
@@ -86,12 +90,12 @@ declare class Box {
86
90
  * Creates a new (transient) instance without caching. Useful for instances
87
91
  * that should not be shared.
88
92
  */
89
- new<T>(constructor: Constructor<T>): T;
93
+ new<T extends Constructor<any>>(constructor: T): ConstructorInstanceType<T>;
90
94
  /**
91
95
  * Resolves an instance from the cache, or creates and caches a new one.
92
96
  * Subsequent calls with the same constructor return the cached instance.
93
97
  */
94
- get<T>(constructor: Constructor<T>): T;
98
+ get<T extends Constructor<any>>(constructor: T): ConstructorInstanceType<T>;
95
99
  /** Resolves multiple constructors at once. */
96
100
  readonly all: BoxAll;
97
101
  /**
@@ -103,6 +107,19 @@ declare class Box {
103
107
  * the class is retrieved via {@link Box.get} or kept transient via {@link Box.new}.
104
108
  */
105
109
  for<T extends ClassConstructor<any>>(constructor: T): Construct<T>;
110
+ /**
111
+ * Returns a {@link StaticConstruct} builder for defining a `static init` method.
112
+ * The result can be assigned directly to `static init`.
113
+ *
114
+ * @example
115
+ * ```ts
116
+ * class UserService {
117
+ * constructor(private db: Database, private logger: Logger) {}
118
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
119
+ * }
120
+ * ```
121
+ */
122
+ static init<T extends ClassConstructor<any>>(constructor: T): StaticConstruct<T>;
106
123
  /**
107
124
  * Registers a mock value in the box's cache for a given constructor.
108
125
  * Useful for replacing dependencies in tests.
@@ -173,6 +190,48 @@ declare class Construct<T extends ClassConstructor<any>> {
173
190
  */
174
191
  get(...args: ClassConstructorArgs<T>): InstanceType<T>;
175
192
  }
193
+ /**
194
+ * Builder for creating a `static init` function with constructor dependencies
195
+ * resolved from a {@link Box}.
196
+ */
197
+ declare class StaticConstruct<T extends ClassConstructor<any>> {
198
+ private construct;
199
+ constructor(construct: T);
200
+ /**
201
+ * Resolves each dependency as a new transient instance via {@link Box.new},
202
+ * meaning dependencies are not cached or shared.
203
+ * Returns a function compatible with `static init` that can be assigned directly.
204
+ *
205
+ * The returned instance is cached or transient depending on whether the
206
+ * class is retrieved via {@link Box.get} or {@link Box.new}.
207
+ *
208
+ * @example
209
+ * ```ts
210
+ * class UserService {
211
+ * constructor(private db: Database, private logger: Logger) {}
212
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
213
+ * }
214
+ * ```
215
+ */
216
+ new(...args: ClassConstructorArgs<T>): (box: Box) => InstanceType<T>;
217
+ /**
218
+ * Resolves each dependency as a cached instance via {@link Box.get},
219
+ * meaning dependencies are shared across the box.
220
+ * Returns a function compatible with `static init` that can be assigned directly.
221
+ *
222
+ * The returned instance is cached or transient depending on whether the
223
+ * class is retrieved via {@link Box.get} or {@link Box.new}.
224
+ *
225
+ * @example
226
+ * ```ts
227
+ * class UserService {
228
+ * constructor(private db: Database, private logger: Logger) {}
229
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
230
+ * }
231
+ * ```
232
+ */
233
+ get(...args: ClassConstructorArgs<T>): (box: Box) => InstanceType<T>;
234
+ }
176
235
  /** A class with any constructor signature. */
177
236
  type ClassConstructor<T> = {
178
237
  new (...args: any): T;
@@ -180,4 +239,4 @@ type ClassConstructor<T> = {
180
239
  /** Maps each constructor parameter to its corresponding {@link Constructor} type. */
181
240
  type ClassConstructorArgs<T extends ClassConstructor<any>, Args = ConstructorParameters<T>> = { [K in keyof Args]: Constructor<Args[K]> };
182
241
  //#endregion
183
- export { Box, Construct, Constructor, ConstructorInstanceType, constant, factory, transient };
242
+ export { Box, Construct, Constructor, ConstructorInstanceType, StaticConstruct, constant, factory, transient };
package/dist/index.mjs CHANGED
@@ -15,7 +15,7 @@
15
15
  function factory(init) {
16
16
  return { init };
17
17
  }
18
- const noCacheSymbol = Symbol("Box constant");
18
+ const noCacheSymbol = Symbol("Box.noCache");
19
19
  /**
20
20
  * Creates a {@link Constructor} from a factory function whose result is never cached.
21
21
  * The factory is called on every resolution, always returning a fresh value
@@ -114,6 +114,21 @@ var Box = class {
114
114
  return new Construct(this, constructor);
115
115
  }
116
116
  /**
117
+ * Returns a {@link StaticConstruct} builder for defining a `static init` method.
118
+ * The result can be assigned directly to `static init`.
119
+ *
120
+ * @example
121
+ * ```ts
122
+ * class UserService {
123
+ * constructor(private db: Database, private logger: Logger) {}
124
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
125
+ * }
126
+ * ```
127
+ */
128
+ static init(constructor) {
129
+ return new StaticConstruct(constructor);
130
+ }
131
+ /**
117
132
  * Registers a mock value in the box's cache for a given constructor.
118
133
  * Useful for replacing dependencies in tests.
119
134
  */
@@ -196,6 +211,59 @@ var Construct = class {
196
211
  return new this.construct(...instances);
197
212
  }
198
213
  };
214
+ /**
215
+ * Builder for creating a `static init` function with constructor dependencies
216
+ * resolved from a {@link Box}.
217
+ */
218
+ var StaticConstruct = class {
219
+ constructor(construct) {
220
+ this.construct = construct;
221
+ }
222
+ /**
223
+ * Resolves each dependency as a new transient instance via {@link Box.new},
224
+ * meaning dependencies are not cached or shared.
225
+ * Returns a function compatible with `static init` that can be assigned directly.
226
+ *
227
+ * The returned instance is cached or transient depending on whether the
228
+ * class is retrieved via {@link Box.get} or {@link Box.new}.
229
+ *
230
+ * @example
231
+ * ```ts
232
+ * class UserService {
233
+ * constructor(private db: Database, private logger: Logger) {}
234
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
235
+ * }
236
+ * ```
237
+ */
238
+ new(...args) {
239
+ return (box) => {
240
+ const instances = args.map((arg) => box.new(arg));
241
+ return new this.construct(...instances);
242
+ };
243
+ }
244
+ /**
245
+ * Resolves each dependency as a cached instance via {@link Box.get},
246
+ * meaning dependencies are shared across the box.
247
+ * Returns a function compatible with `static init` that can be assigned directly.
248
+ *
249
+ * The returned instance is cached or transient depending on whether the
250
+ * class is retrieved via {@link Box.get} or {@link Box.new}.
251
+ *
252
+ * @example
253
+ * ```ts
254
+ * class UserService {
255
+ * constructor(private db: Database, private logger: Logger) {}
256
+ * static init = Box.init(UserService).get(Database, LoggerFactory);
257
+ * }
258
+ * ```
259
+ */
260
+ get(...args) {
261
+ return (box) => {
262
+ const instances = args.map((arg) => box.get(arg));
263
+ return new this.construct(...instances);
264
+ };
265
+ }
266
+ };
199
267
 
200
268
  //#endregion
201
- export { Box, Construct, constant, factory, transient };
269
+ export { Box, Construct, StaticConstruct, constant, factory, transient };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getbox",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Lightweight dependency injection for TypeScript",
5
5
  "private": false,
6
6
  "main": "./dist/index.cjs",