getbox 2.0.0 → 2.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/README.md CHANGED
@@ -14,7 +14,7 @@ npm install getbox
14
14
 
15
15
  ## Usage
16
16
 
17
- `getbox` has a very small API surface. You typically only need `box.get()` and optionally `static init` or the `factory` helper.
17
+ `getbox` has a very small API surface. You typically only need `box.get()` and optionally `Box.init()` or the `factory` helper.
18
18
 
19
19
  For an alternative pattern using AsyncLocalStorage where classes can resolve dependencies directly, see [getbox/context](./CONTEXT.md).
20
20
 
@@ -73,19 +73,35 @@ const service = box.get(UserService);
73
73
  service.createUser("Alice");
74
74
  ```
75
75
 
76
- `Box.init` is shorthand for writing the `static init` function yourself.
76
+ Use `Box.fn` to write the initializer manually when you need more control.
77
77
 
78
78
  ```ts
79
79
  class UserService {
80
80
  constructor(private db: Database, private logger: Logger) {}
81
81
 
82
- static init(box: Box) {
82
+ static init = Box.fn((box) => {
83
83
  return new UserService(box.get(Database), box.get(LoggerFactory));
84
- }
84
+ });
85
85
  }
86
86
  ```
87
87
 
88
- If `static init` is defined, it takes priority over the class constructor.
88
+ If a `static init` initializer is defined, it takes priority over the class constructor.
89
+
90
+ Set `static [boxCache] = false` to opt a class out of caching. The box will call the initializer on every `box.get()` instead of returning a cached instance.
91
+
92
+ ```ts
93
+ import { Box, boxCache } from "getbox";
94
+
95
+ class RequestContext {
96
+ timestamp = Date.now();
97
+ static [boxCache] = false;
98
+ }
99
+
100
+ const box = new Box();
101
+ const ctx1 = box.get(RequestContext);
102
+ const ctx2 = box.get(RequestContext);
103
+ console.log(ctx1 === ctx2); // false
104
+ ```
89
105
 
90
106
  ### Factory functions
91
107
 
@@ -195,6 +211,19 @@ const [db4] = box.new([Database]);
195
211
 
196
212
  > `box.get()` does not cache `computed` or `constant` values.
197
213
 
214
+ `Box` itself can be resolved as a dependency. `box.get(Box)` returns the current box instance.
215
+
216
+ ```ts
217
+ class ServiceLocator {
218
+ constructor(private box: Box) {}
219
+ static init = Box.init(ServiceLocator).get(Box);
220
+ }
221
+
222
+ const box = new Box();
223
+ const locator = box.get(ServiceLocator);
224
+ console.log(locator.box === box); // true
225
+ ```
226
+
198
227
  ## Mocking
199
228
 
200
229
  You can mock dependencies for testing using `Box.mock`.
package/dist/index.cjs CHANGED
@@ -1,7 +1,18 @@
1
1
 
2
2
  //#region src/index.ts
3
- const cacheSymbol = Symbol("Box.cache");
4
- const isClass = (fn) => fn.prototype !== void 0 && Function.prototype.toString.call(fn).startsWith("class");
3
+ /**
4
+ * Symbol key used to opt a constructor out of caching.
5
+ * When set to `false`, {@link Box.get} will not cache the resolved value.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * class MyService {
10
+ * static [boxCache] = false;
11
+ * static init = Box.init(MyService).get(Database);
12
+ * }
13
+ * ```
14
+ */
15
+ const boxCache = Symbol("Box.cache");
5
16
  /**
6
17
  * Creates a {@link Constructor} from a factory function.
7
18
  * The factory receives the box as an argument, allowing it to resolve other dependencies.
@@ -16,10 +27,7 @@ const isClass = (fn) => fn.prototype !== void 0 && Function.prototype.toString.c
16
27
  * ```
17
28
  */
18
29
  function factory(init) {
19
- return {
20
- [cacheSymbol]: true,
21
- init
22
- };
30
+ return { init: new Init(init) };
23
31
  }
24
32
  /**
25
33
  * Creates a {@link Constructor} that computes a value from the box without caching the result.
@@ -41,7 +49,10 @@ function factory(init) {
41
49
  * ```
42
50
  */
43
51
  function computed(init) {
44
- return { init };
52
+ return {
53
+ init: new Init(init),
54
+ [boxCache]: false
55
+ };
45
56
  }
46
57
  /**
47
58
  * Creates a {@link Constructor} that always resolves to the given constant value.
@@ -50,20 +61,25 @@ function computed(init) {
50
61
  * @example
51
62
  * ```ts
52
63
  * const ApiUrl = constant("https://api.example.com");
53
- * const port = box.get(ApiUrl); // "https://api.example.com"
64
+ * const url = box.get(ApiUrl); // "https://api.example.com"
54
65
  * ```
55
66
  */
56
67
  function constant(value) {
57
- return { init: () => value };
68
+ return {
69
+ init: new Init(() => value),
70
+ [boxCache]: false
71
+ };
58
72
  }
59
73
  /**
60
74
  * Dependency injection container that resolves and caches instances from
61
75
  * a {@link Constructor}.
62
76
  *
63
- * A constructor is a class with a `static init` function, a class with a
64
- * no-argument constructor, or a function-based constructor created by
77
+ * A constructor is a class with a `static init` property set to an initializer,
78
+ * a class with a no-argument constructor, or a function-based constructor created by
65
79
  * {@link factory}, {@link computed}, or {@link constant}.
66
80
  *
81
+ * `Box` can be resolved as a dependency — `box.get(Box)` returns the current instance.
82
+ *
67
83
  * @example
68
84
  * ```ts
69
85
  * class Database {
@@ -80,14 +96,15 @@ function constant(value) {
80
96
  * const db = box.get(Database);
81
97
  *
82
98
  * console.log(service.db === db); // true (cached)
99
+ * console.log(box.get(Box) === box); // true
83
100
  * ```
84
101
  */
85
- var Box = class {
102
+ var Box = class Box {
86
103
  cache = /* @__PURE__ */ new Map();
87
104
  new(arg) {
88
105
  if (Array.isArray(arg)) return arg.map((c) => this.new(c));
89
- if (typeof arg === "function") return "init" in arg ? arg.init(this) : new arg();
90
- if ("init" in arg && !isClass(arg.init)) return arg.init(this);
106
+ if (typeof arg === "function") return arg.init instanceof Init ? arg.init.fn(this) : new arg();
107
+ if (arg.init instanceof Init) return arg.init.fn(this);
91
108
  const result = {};
92
109
  for (const [key, constructor] of Object.entries(arg)) result[key] = this.new(constructor);
93
110
  return result;
@@ -96,14 +113,15 @@ var Box = class {
96
113
  if (Array.isArray(arg)) return arg.map((c) => this.get(c));
97
114
  if (typeof arg === "function") {
98
115
  if (this.cache.has(arg)) return this.cache.get(arg);
99
- const value = "init" in arg ? arg.init(this) : new arg();
100
- this.cache.set(arg, value);
116
+ if (arg === Box) return this;
117
+ const value = arg.init instanceof Init ? arg.init.fn(this) : new arg();
118
+ if (arg[boxCache] !== false) this.cache.set(arg, value);
101
119
  return value;
102
120
  }
103
- if ("init" in arg && !isClass(arg.init)) {
121
+ if (arg.init instanceof Init) {
104
122
  if (this.cache.has(arg)) return this.cache.get(arg);
105
- const value = arg.init(this);
106
- if (cacheSymbol in arg) this.cache.set(arg, value);
123
+ const value = arg.init.fn(this);
124
+ if (arg[boxCache] !== false) this.cache.set(arg, value);
107
125
  return value;
108
126
  }
109
127
  const result = {};
@@ -111,7 +129,7 @@ var Box = class {
111
129
  return result;
112
130
  }
113
131
  /**
114
- * Returns a `static init` function builder.
132
+ * Returns an initializer builder for a class constructor.
115
133
  *
116
134
  * @example
117
135
  * ```ts
@@ -121,8 +139,22 @@ var Box = class {
121
139
  * }
122
140
  * ```
123
141
  */
124
- static init(constructor) {
125
- return new StaticInit(constructor);
142
+ static init(ctor) {
143
+ return new StaticInit(ctor);
144
+ }
145
+ /**
146
+ * Returns an initializer from a factory function.
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * class UserService {
151
+ * constructor(private db: Database) {}
152
+ * static init = Box.fn((box) => new UserService(box.get(Database)));
153
+ * }
154
+ * ```
155
+ */
156
+ static fn(fn) {
157
+ return new Init(fn);
126
158
  }
127
159
  /**
128
160
  * Registers a mock value in the box's cache for a given constructor.
@@ -146,21 +178,21 @@ var Box = class {
146
178
  return box.cache.delete(constructor);
147
179
  }
148
180
  };
149
- /**
150
- * Builder for creating a `static init` function with constructor dependencies
151
- * resolved from a {@link Box}.
152
- */
181
+ /** An initializer wrapping a factory function for use as a {@link Constructor}. */
182
+ var Init = class {
183
+ constructor(fn) {
184
+ this.fn = fn;
185
+ }
186
+ };
187
+ /** Builder that creates an initializer with dependencies resolved from a {@link Box}. */
153
188
  var StaticInit = class {
154
- constructor(construct) {
155
- this.construct = construct;
189
+ constructor(ctor) {
190
+ this.ctor = ctor;
156
191
  }
157
192
  /**
158
- * Resolves each dependency as a new instance via {@link Box.new},
159
- * meaning dependencies are not cached or shared.
160
- * Returns a function compatible with `static init` that can be assigned directly.
161
- *
162
- * The returned instance is cached or new depending on whether the
163
- * class is retrieved via {@link Box.get} or {@link Box.new}.
193
+ * Resolves each dependency as a new instance via {@link Box.new}.
194
+ * Dependencies are not cached or shared.
195
+ * Returns an initializer.
164
196
  *
165
197
  * @example
166
198
  * ```ts
@@ -171,17 +203,12 @@ var StaticInit = class {
171
203
  * ```
172
204
  */
173
205
  new(...args) {
174
- return (box) => {
175
- return new this.construct(...box.new(args));
176
- };
206
+ return new Init((box) => new this.ctor(...box.new(args)));
177
207
  }
178
208
  /**
179
- * Resolves each dependency as a cached instance via {@link Box.get},
180
- * meaning dependencies are shared across the box.
181
- * Returns a function compatible with `static init` that can be assigned directly.
182
- *
183
- * The returned instance is cached or new depending on whether the
184
- * class is retrieved via {@link Box.get} or {@link Box.new}.
209
+ * Resolves each dependency as a cached instance via {@link Box.get}.
210
+ * Dependencies are cached and shared.
211
+ * Returns an initializer.
185
212
  *
186
213
  * @example
187
214
  * ```ts
@@ -192,15 +219,15 @@ var StaticInit = class {
192
219
  * ```
193
220
  */
194
221
  get(...args) {
195
- return (box) => {
196
- return new this.construct(...box.get(args));
197
- };
222
+ return new Init((box) => new this.ctor(...box.get(args)));
198
223
  }
199
224
  };
200
225
 
201
226
  //#endregion
202
227
  exports.Box = Box;
228
+ exports.Init = Init;
203
229
  exports.StaticInit = StaticInit;
230
+ exports.boxCache = boxCache;
204
231
  exports.computed = computed;
205
232
  exports.constant = constant;
206
233
  exports.factory = factory;
package/dist/index.d.cts CHANGED
@@ -1,18 +1,31 @@
1
1
  //#region src/index.d.ts
2
+ /**
3
+ * Symbol key used to opt a constructor out of caching.
4
+ * When set to `false`, {@link Box.get} will not cache the resolved value.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * class MyService {
9
+ * static [boxCache] = false;
10
+ * static init = Box.init(MyService).get(Database);
11
+ * }
12
+ * ```
13
+ */
14
+ declare const boxCache: unique symbol;
2
15
  /**
3
16
  * A type that can be resolved by a {@link Box}. Either a class with a
4
- * `static init` function that returns an instance, a class with a
5
- * no-argument constructor, or a function-based constructor created by
6
- * {@link factory}, {@link computed}, or {@link constant}.
17
+ * `static init` property set to an initializer, a class with a no-argument
18
+ * constructor, or a function-based constructor created by {@link factory},
19
+ * {@link computed}, or {@link constant}.
7
20
  */
8
21
  type Constructor<T> = {
9
- init(box: Box): T;
22
+ init: Init<T>;
10
23
  } | {
11
24
  new (): T;
12
25
  };
13
26
  /** Extracts the instance type from a {@link Constructor}. */
14
27
  type ConstructorInstanceType<T> = T extends {
15
- init(box: Box): infer U;
28
+ init: Init<infer U>;
16
29
  } ? U : T extends {
17
30
  new (): infer U;
18
31
  } ? U : never;
@@ -57,7 +70,7 @@ declare function computed<T>(init: (box: Box) => T): Constructor<T>;
57
70
  * @example
58
71
  * ```ts
59
72
  * const ApiUrl = constant("https://api.example.com");
60
- * const port = box.get(ApiUrl); // "https://api.example.com"
73
+ * const url = box.get(ApiUrl); // "https://api.example.com"
61
74
  * ```
62
75
  */
63
76
  declare function constant<const T>(value: T): Constructor<T>;
@@ -65,10 +78,12 @@ declare function constant<const T>(value: T): Constructor<T>;
65
78
  * Dependency injection container that resolves and caches instances from
66
79
  * a {@link Constructor}.
67
80
  *
68
- * A constructor is a class with a `static init` function, a class with a
69
- * no-argument constructor, or a function-based constructor created by
81
+ * A constructor is a class with a `static init` property set to an initializer,
82
+ * a class with a no-argument constructor, or a function-based constructor created by
70
83
  * {@link factory}, {@link computed}, or {@link constant}.
71
84
  *
85
+ * `Box` can be resolved as a dependency — `box.get(Box)` returns the current instance.
86
+ *
72
87
  * @example
73
88
  * ```ts
74
89
  * class Database {
@@ -85,6 +100,7 @@ declare function constant<const T>(value: T): Constructor<T>;
85
100
  * const db = box.get(Database);
86
101
  *
87
102
  * console.log(service.db === db); // true (cached)
103
+ * console.log(box.get(Box) === box); // true
88
104
  * ```
89
105
  */
90
106
  declare class Box {
@@ -105,7 +121,7 @@ declare class Box {
105
121
  get<const T extends Constructor<any>[]>(constructors: T): { [K in keyof T]: ConstructorInstanceType<T[K]> };
106
122
  get<T extends Record<string, Constructor<any>>>(constructors: T): { [K in keyof T]: ConstructorInstanceType<T[K]> };
107
123
  /**
108
- * Returns a `static init` function builder.
124
+ * Returns an initializer builder for a class constructor.
109
125
  *
110
126
  * @example
111
127
  * ```ts
@@ -115,7 +131,19 @@ declare class Box {
115
131
  * }
116
132
  * ```
117
133
  */
118
- static init<T extends ClassConstructor<any>>(constructor: T): StaticInit<T>;
134
+ static init<T extends ClassConstructor<any>>(ctor: T): StaticInit<T>;
135
+ /**
136
+ * Returns an initializer from a factory function.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * class UserService {
141
+ * constructor(private db: Database) {}
142
+ * static init = Box.fn((box) => new UserService(box.get(Database)));
143
+ * }
144
+ * ```
145
+ */
146
+ static fn<T>(fn: (box: Box) => T): Init<T>;
119
147
  /**
120
148
  * Registers a mock value in the box's cache for a given constructor.
121
149
  * Useful for replacing dependencies in tests.
@@ -129,20 +157,19 @@ declare class Box {
129
157
  */
130
158
  static clear<T>(box: Box, constructor?: Constructor<T>): boolean;
131
159
  }
132
- /**
133
- * Builder for creating a `static init` function with constructor dependencies
134
- * resolved from a {@link Box}.
135
- */
136
- declare class StaticInit<T extends ClassConstructor<any>> {
137
- private construct;
138
- constructor(construct: T);
160
+ /** An initializer wrapping a factory function for use as a {@link Constructor}. */
161
+ declare class Init<T> {
162
+ readonly fn: (box: Box) => T;
163
+ constructor(fn: (box: Box) => T);
164
+ }
165
+ /** Builder that creates an initializer with dependencies resolved from a {@link Box}. */
166
+ declare class StaticInit<C extends ClassConstructor<any>> {
167
+ private ctor;
168
+ constructor(ctor: C);
139
169
  /**
140
- * Resolves each dependency as a new instance via {@link Box.new},
141
- * meaning dependencies are not cached or shared.
142
- * Returns a function compatible with `static init` that can be assigned directly.
143
- *
144
- * The returned instance is cached or new depending on whether the
145
- * class is retrieved via {@link Box.get} or {@link Box.new}.
170
+ * Resolves each dependency as a new instance via {@link Box.new}.
171
+ * Dependencies are not cached or shared.
172
+ * Returns an initializer.
146
173
  *
147
174
  * @example
148
175
  * ```ts
@@ -152,14 +179,11 @@ declare class StaticInit<T extends ClassConstructor<any>> {
152
179
  * }
153
180
  * ```
154
181
  */
155
- new(...args: ClassConstructorArgs<T>): (box: Box) => InstanceType<T>;
182
+ new(...args: ClassConstructorArgs<C>): Init<InstanceType<C>>;
156
183
  /**
157
- * Resolves each dependency as a cached instance via {@link Box.get},
158
- * meaning dependencies are shared across the box.
159
- * Returns a function compatible with `static init` that can be assigned directly.
160
- *
161
- * The returned instance is cached or new depending on whether the
162
- * class is retrieved via {@link Box.get} or {@link Box.new}.
184
+ * Resolves each dependency as a cached instance via {@link Box.get}.
185
+ * Dependencies are cached and shared.
186
+ * Returns an initializer.
163
187
  *
164
188
  * @example
165
189
  * ```ts
@@ -169,13 +193,13 @@ declare class StaticInit<T extends ClassConstructor<any>> {
169
193
  * }
170
194
  * ```
171
195
  */
172
- get(...args: ClassConstructorArgs<T>): (box: Box) => InstanceType<T>;
196
+ get(...args: ClassConstructorArgs<C>): Init<InstanceType<C>>;
173
197
  }
174
198
  /** A class with any constructor signature. */
175
199
  type ClassConstructor<T> = {
176
200
  new (...args: any): T;
177
201
  };
178
202
  /** Maps each constructor parameter to its corresponding {@link Constructor} type. */
179
- type ClassConstructorArgs<T extends ClassConstructor<any>, Args = ConstructorParameters<T>> = { [K in keyof Args]: Constructor<Args[K]> };
203
+ type ClassConstructorArgs<C extends ClassConstructor<any>, Args = ConstructorParameters<C>> = { [K in keyof Args]: Constructor<Args[K]> };
180
204
  //#endregion
181
- export { Box, Constructor, ConstructorInstanceType, StaticInit, computed, constant, factory };
205
+ export { Box, Constructor, ConstructorInstanceType, Init, StaticInit, boxCache, computed, constant, factory };
package/dist/index.d.mts CHANGED
@@ -1,18 +1,31 @@
1
1
  //#region src/index.d.ts
2
+ /**
3
+ * Symbol key used to opt a constructor out of caching.
4
+ * When set to `false`, {@link Box.get} will not cache the resolved value.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * class MyService {
9
+ * static [boxCache] = false;
10
+ * static init = Box.init(MyService).get(Database);
11
+ * }
12
+ * ```
13
+ */
14
+ declare const boxCache: unique symbol;
2
15
  /**
3
16
  * A type that can be resolved by a {@link Box}. Either a class with a
4
- * `static init` function that returns an instance, a class with a
5
- * no-argument constructor, or a function-based constructor created by
6
- * {@link factory}, {@link computed}, or {@link constant}.
17
+ * `static init` property set to an initializer, a class with a no-argument
18
+ * constructor, or a function-based constructor created by {@link factory},
19
+ * {@link computed}, or {@link constant}.
7
20
  */
8
21
  type Constructor<T> = {
9
- init(box: Box): T;
22
+ init: Init<T>;
10
23
  } | {
11
24
  new (): T;
12
25
  };
13
26
  /** Extracts the instance type from a {@link Constructor}. */
14
27
  type ConstructorInstanceType<T> = T extends {
15
- init(box: Box): infer U;
28
+ init: Init<infer U>;
16
29
  } ? U : T extends {
17
30
  new (): infer U;
18
31
  } ? U : never;
@@ -57,7 +70,7 @@ declare function computed<T>(init: (box: Box) => T): Constructor<T>;
57
70
  * @example
58
71
  * ```ts
59
72
  * const ApiUrl = constant("https://api.example.com");
60
- * const port = box.get(ApiUrl); // "https://api.example.com"
73
+ * const url = box.get(ApiUrl); // "https://api.example.com"
61
74
  * ```
62
75
  */
63
76
  declare function constant<const T>(value: T): Constructor<T>;
@@ -65,10 +78,12 @@ declare function constant<const T>(value: T): Constructor<T>;
65
78
  * Dependency injection container that resolves and caches instances from
66
79
  * a {@link Constructor}.
67
80
  *
68
- * A constructor is a class with a `static init` function, a class with a
69
- * no-argument constructor, or a function-based constructor created by
81
+ * A constructor is a class with a `static init` property set to an initializer,
82
+ * a class with a no-argument constructor, or a function-based constructor created by
70
83
  * {@link factory}, {@link computed}, or {@link constant}.
71
84
  *
85
+ * `Box` can be resolved as a dependency — `box.get(Box)` returns the current instance.
86
+ *
72
87
  * @example
73
88
  * ```ts
74
89
  * class Database {
@@ -85,6 +100,7 @@ declare function constant<const T>(value: T): Constructor<T>;
85
100
  * const db = box.get(Database);
86
101
  *
87
102
  * console.log(service.db === db); // true (cached)
103
+ * console.log(box.get(Box) === box); // true
88
104
  * ```
89
105
  */
90
106
  declare class Box {
@@ -105,7 +121,7 @@ declare class Box {
105
121
  get<const T extends Constructor<any>[]>(constructors: T): { [K in keyof T]: ConstructorInstanceType<T[K]> };
106
122
  get<T extends Record<string, Constructor<any>>>(constructors: T): { [K in keyof T]: ConstructorInstanceType<T[K]> };
107
123
  /**
108
- * Returns a `static init` function builder.
124
+ * Returns an initializer builder for a class constructor.
109
125
  *
110
126
  * @example
111
127
  * ```ts
@@ -115,7 +131,19 @@ declare class Box {
115
131
  * }
116
132
  * ```
117
133
  */
118
- static init<T extends ClassConstructor<any>>(constructor: T): StaticInit<T>;
134
+ static init<T extends ClassConstructor<any>>(ctor: T): StaticInit<T>;
135
+ /**
136
+ * Returns an initializer from a factory function.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * class UserService {
141
+ * constructor(private db: Database) {}
142
+ * static init = Box.fn((box) => new UserService(box.get(Database)));
143
+ * }
144
+ * ```
145
+ */
146
+ static fn<T>(fn: (box: Box) => T): Init<T>;
119
147
  /**
120
148
  * Registers a mock value in the box's cache for a given constructor.
121
149
  * Useful for replacing dependencies in tests.
@@ -129,20 +157,19 @@ declare class Box {
129
157
  */
130
158
  static clear<T>(box: Box, constructor?: Constructor<T>): boolean;
131
159
  }
132
- /**
133
- * Builder for creating a `static init` function with constructor dependencies
134
- * resolved from a {@link Box}.
135
- */
136
- declare class StaticInit<T extends ClassConstructor<any>> {
137
- private construct;
138
- constructor(construct: T);
160
+ /** An initializer wrapping a factory function for use as a {@link Constructor}. */
161
+ declare class Init<T> {
162
+ readonly fn: (box: Box) => T;
163
+ constructor(fn: (box: Box) => T);
164
+ }
165
+ /** Builder that creates an initializer with dependencies resolved from a {@link Box}. */
166
+ declare class StaticInit<C extends ClassConstructor<any>> {
167
+ private ctor;
168
+ constructor(ctor: C);
139
169
  /**
140
- * Resolves each dependency as a new instance via {@link Box.new},
141
- * meaning dependencies are not cached or shared.
142
- * Returns a function compatible with `static init` that can be assigned directly.
143
- *
144
- * The returned instance is cached or new depending on whether the
145
- * class is retrieved via {@link Box.get} or {@link Box.new}.
170
+ * Resolves each dependency as a new instance via {@link Box.new}.
171
+ * Dependencies are not cached or shared.
172
+ * Returns an initializer.
146
173
  *
147
174
  * @example
148
175
  * ```ts
@@ -152,14 +179,11 @@ declare class StaticInit<T extends ClassConstructor<any>> {
152
179
  * }
153
180
  * ```
154
181
  */
155
- new(...args: ClassConstructorArgs<T>): (box: Box) => InstanceType<T>;
182
+ new(...args: ClassConstructorArgs<C>): Init<InstanceType<C>>;
156
183
  /**
157
- * Resolves each dependency as a cached instance via {@link Box.get},
158
- * meaning dependencies are shared across the box.
159
- * Returns a function compatible with `static init` that can be assigned directly.
160
- *
161
- * The returned instance is cached or new depending on whether the
162
- * class is retrieved via {@link Box.get} or {@link Box.new}.
184
+ * Resolves each dependency as a cached instance via {@link Box.get}.
185
+ * Dependencies are cached and shared.
186
+ * Returns an initializer.
163
187
  *
164
188
  * @example
165
189
  * ```ts
@@ -169,13 +193,13 @@ declare class StaticInit<T extends ClassConstructor<any>> {
169
193
  * }
170
194
  * ```
171
195
  */
172
- get(...args: ClassConstructorArgs<T>): (box: Box) => InstanceType<T>;
196
+ get(...args: ClassConstructorArgs<C>): Init<InstanceType<C>>;
173
197
  }
174
198
  /** A class with any constructor signature. */
175
199
  type ClassConstructor<T> = {
176
200
  new (...args: any): T;
177
201
  };
178
202
  /** Maps each constructor parameter to its corresponding {@link Constructor} type. */
179
- type ClassConstructorArgs<T extends ClassConstructor<any>, Args = ConstructorParameters<T>> = { [K in keyof Args]: Constructor<Args[K]> };
203
+ type ClassConstructorArgs<C extends ClassConstructor<any>, Args = ConstructorParameters<C>> = { [K in keyof Args]: Constructor<Args[K]> };
180
204
  //#endregion
181
- export { Box, Constructor, ConstructorInstanceType, StaticInit, computed, constant, factory };
205
+ export { Box, Constructor, ConstructorInstanceType, Init, StaticInit, boxCache, computed, constant, factory };
package/dist/index.mjs CHANGED
@@ -1,6 +1,17 @@
1
1
  //#region src/index.ts
2
- const cacheSymbol = Symbol("Box.cache");
3
- const isClass = (fn) => fn.prototype !== void 0 && Function.prototype.toString.call(fn).startsWith("class");
2
+ /**
3
+ * Symbol key used to opt a constructor out of caching.
4
+ * When set to `false`, {@link Box.get} will not cache the resolved value.
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * class MyService {
9
+ * static [boxCache] = false;
10
+ * static init = Box.init(MyService).get(Database);
11
+ * }
12
+ * ```
13
+ */
14
+ const boxCache = Symbol("Box.cache");
4
15
  /**
5
16
  * Creates a {@link Constructor} from a factory function.
6
17
  * The factory receives the box as an argument, allowing it to resolve other dependencies.
@@ -15,10 +26,7 @@ const isClass = (fn) => fn.prototype !== void 0 && Function.prototype.toString.c
15
26
  * ```
16
27
  */
17
28
  function factory(init) {
18
- return {
19
- [cacheSymbol]: true,
20
- init
21
- };
29
+ return { init: new Init(init) };
22
30
  }
23
31
  /**
24
32
  * Creates a {@link Constructor} that computes a value from the box without caching the result.
@@ -40,7 +48,10 @@ function factory(init) {
40
48
  * ```
41
49
  */
42
50
  function computed(init) {
43
- return { init };
51
+ return {
52
+ init: new Init(init),
53
+ [boxCache]: false
54
+ };
44
55
  }
45
56
  /**
46
57
  * Creates a {@link Constructor} that always resolves to the given constant value.
@@ -49,20 +60,25 @@ function computed(init) {
49
60
  * @example
50
61
  * ```ts
51
62
  * const ApiUrl = constant("https://api.example.com");
52
- * const port = box.get(ApiUrl); // "https://api.example.com"
63
+ * const url = box.get(ApiUrl); // "https://api.example.com"
53
64
  * ```
54
65
  */
55
66
  function constant(value) {
56
- return { init: () => value };
67
+ return {
68
+ init: new Init(() => value),
69
+ [boxCache]: false
70
+ };
57
71
  }
58
72
  /**
59
73
  * Dependency injection container that resolves and caches instances from
60
74
  * a {@link Constructor}.
61
75
  *
62
- * A constructor is a class with a `static init` function, a class with a
63
- * no-argument constructor, or a function-based constructor created by
76
+ * A constructor is a class with a `static init` property set to an initializer,
77
+ * a class with a no-argument constructor, or a function-based constructor created by
64
78
  * {@link factory}, {@link computed}, or {@link constant}.
65
79
  *
80
+ * `Box` can be resolved as a dependency — `box.get(Box)` returns the current instance.
81
+ *
66
82
  * @example
67
83
  * ```ts
68
84
  * class Database {
@@ -79,14 +95,15 @@ function constant(value) {
79
95
  * const db = box.get(Database);
80
96
  *
81
97
  * console.log(service.db === db); // true (cached)
98
+ * console.log(box.get(Box) === box); // true
82
99
  * ```
83
100
  */
84
- var Box = class {
101
+ var Box = class Box {
85
102
  cache = /* @__PURE__ */ new Map();
86
103
  new(arg) {
87
104
  if (Array.isArray(arg)) return arg.map((c) => this.new(c));
88
- if (typeof arg === "function") return "init" in arg ? arg.init(this) : new arg();
89
- if ("init" in arg && !isClass(arg.init)) return arg.init(this);
105
+ if (typeof arg === "function") return arg.init instanceof Init ? arg.init.fn(this) : new arg();
106
+ if (arg.init instanceof Init) return arg.init.fn(this);
90
107
  const result = {};
91
108
  for (const [key, constructor] of Object.entries(arg)) result[key] = this.new(constructor);
92
109
  return result;
@@ -95,14 +112,15 @@ var Box = class {
95
112
  if (Array.isArray(arg)) return arg.map((c) => this.get(c));
96
113
  if (typeof arg === "function") {
97
114
  if (this.cache.has(arg)) return this.cache.get(arg);
98
- const value = "init" in arg ? arg.init(this) : new arg();
99
- this.cache.set(arg, value);
115
+ if (arg === Box) return this;
116
+ const value = arg.init instanceof Init ? arg.init.fn(this) : new arg();
117
+ if (arg[boxCache] !== false) this.cache.set(arg, value);
100
118
  return value;
101
119
  }
102
- if ("init" in arg && !isClass(arg.init)) {
120
+ if (arg.init instanceof Init) {
103
121
  if (this.cache.has(arg)) return this.cache.get(arg);
104
- const value = arg.init(this);
105
- if (cacheSymbol in arg) this.cache.set(arg, value);
122
+ const value = arg.init.fn(this);
123
+ if (arg[boxCache] !== false) this.cache.set(arg, value);
106
124
  return value;
107
125
  }
108
126
  const result = {};
@@ -110,7 +128,7 @@ var Box = class {
110
128
  return result;
111
129
  }
112
130
  /**
113
- * Returns a `static init` function builder.
131
+ * Returns an initializer builder for a class constructor.
114
132
  *
115
133
  * @example
116
134
  * ```ts
@@ -120,8 +138,22 @@ var Box = class {
120
138
  * }
121
139
  * ```
122
140
  */
123
- static init(constructor) {
124
- return new StaticInit(constructor);
141
+ static init(ctor) {
142
+ return new StaticInit(ctor);
143
+ }
144
+ /**
145
+ * Returns an initializer from a factory function.
146
+ *
147
+ * @example
148
+ * ```ts
149
+ * class UserService {
150
+ * constructor(private db: Database) {}
151
+ * static init = Box.fn((box) => new UserService(box.get(Database)));
152
+ * }
153
+ * ```
154
+ */
155
+ static fn(fn) {
156
+ return new Init(fn);
125
157
  }
126
158
  /**
127
159
  * Registers a mock value in the box's cache for a given constructor.
@@ -145,21 +177,21 @@ var Box = class {
145
177
  return box.cache.delete(constructor);
146
178
  }
147
179
  };
148
- /**
149
- * Builder for creating a `static init` function with constructor dependencies
150
- * resolved from a {@link Box}.
151
- */
180
+ /** An initializer wrapping a factory function for use as a {@link Constructor}. */
181
+ var Init = class {
182
+ constructor(fn) {
183
+ this.fn = fn;
184
+ }
185
+ };
186
+ /** Builder that creates an initializer with dependencies resolved from a {@link Box}. */
152
187
  var StaticInit = class {
153
- constructor(construct) {
154
- this.construct = construct;
188
+ constructor(ctor) {
189
+ this.ctor = ctor;
155
190
  }
156
191
  /**
157
- * Resolves each dependency as a new instance via {@link Box.new},
158
- * meaning dependencies are not cached or shared.
159
- * Returns a function compatible with `static init` that can be assigned directly.
160
- *
161
- * The returned instance is cached or new depending on whether the
162
- * class is retrieved via {@link Box.get} or {@link Box.new}.
192
+ * Resolves each dependency as a new instance via {@link Box.new}.
193
+ * Dependencies are not cached or shared.
194
+ * Returns an initializer.
163
195
  *
164
196
  * @example
165
197
  * ```ts
@@ -170,17 +202,12 @@ var StaticInit = class {
170
202
  * ```
171
203
  */
172
204
  new(...args) {
173
- return (box) => {
174
- return new this.construct(...box.new(args));
175
- };
205
+ return new Init((box) => new this.ctor(...box.new(args)));
176
206
  }
177
207
  /**
178
- * Resolves each dependency as a cached instance via {@link Box.get},
179
- * meaning dependencies are shared across the box.
180
- * Returns a function compatible with `static init` that can be assigned directly.
181
- *
182
- * The returned instance is cached or new depending on whether the
183
- * class is retrieved via {@link Box.get} or {@link Box.new}.
208
+ * Resolves each dependency as a cached instance via {@link Box.get}.
209
+ * Dependencies are cached and shared.
210
+ * Returns an initializer.
184
211
  *
185
212
  * @example
186
213
  * ```ts
@@ -191,11 +218,9 @@ var StaticInit = class {
191
218
  * ```
192
219
  */
193
220
  get(...args) {
194
- return (box) => {
195
- return new this.construct(...box.get(args));
196
- };
221
+ return new Init((box) => new this.ctor(...box.get(args)));
197
222
  }
198
223
  };
199
224
 
200
225
  //#endregion
201
- export { Box, StaticInit, computed, constant, factory };
226
+ export { Box, Init, StaticInit, boxCache, computed, constant, factory };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "getbox",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Lightweight dependency injection for TypeScript",
5
5
  "private": false,
6
6
  "main": "./dist/index.cjs",