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 +34 -5
- package/dist/index.cjs +73 -46
- package/dist/index.d.cts +57 -33
- package/dist/index.d.mts +57 -33
- package/dist/index.mjs +72 -47
- package/package.json +1 -1
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 `
|
|
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.
|
|
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
|
|
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
|
-
|
|
4
|
-
|
|
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 {
|
|
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
|
|
64
|
+
* const url = box.get(ApiUrl); // "https://api.example.com"
|
|
54
65
|
* ```
|
|
55
66
|
*/
|
|
56
67
|
function constant(value) {
|
|
57
|
-
return {
|
|
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`
|
|
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
|
|
90
|
-
if (
|
|
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
|
-
|
|
100
|
-
|
|
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 (
|
|
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 (
|
|
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
|
|
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(
|
|
125
|
-
return new StaticInit(
|
|
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
|
-
|
|
151
|
-
|
|
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(
|
|
155
|
-
this.
|
|
189
|
+
constructor(ctor) {
|
|
190
|
+
this.ctor = ctor;
|
|
156
191
|
}
|
|
157
192
|
/**
|
|
158
|
-
* Resolves each dependency as a new instance via {@link Box.new}
|
|
159
|
-
*
|
|
160
|
-
* Returns
|
|
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
|
-
*
|
|
181
|
-
* Returns
|
|
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`
|
|
5
|
-
*
|
|
6
|
-
* {@link
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
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
|
|
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>>(
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
*
|
|
142
|
-
* Returns
|
|
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<
|
|
182
|
+
new(...args: ClassConstructorArgs<C>): Init<InstanceType<C>>;
|
|
156
183
|
/**
|
|
157
|
-
* Resolves each dependency as a cached instance via {@link Box.get}
|
|
158
|
-
*
|
|
159
|
-
* Returns
|
|
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<
|
|
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<
|
|
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`
|
|
5
|
-
*
|
|
6
|
-
* {@link
|
|
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
|
|
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
|
|
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
|
|
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`
|
|
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
|
|
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>>(
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
*
|
|
142
|
-
* Returns
|
|
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<
|
|
182
|
+
new(...args: ClassConstructorArgs<C>): Init<InstanceType<C>>;
|
|
156
183
|
/**
|
|
157
|
-
* Resolves each dependency as a cached instance via {@link Box.get}
|
|
158
|
-
*
|
|
159
|
-
* Returns
|
|
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<
|
|
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<
|
|
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
|
-
|
|
3
|
-
|
|
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 {
|
|
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
|
|
63
|
+
* const url = box.get(ApiUrl); // "https://api.example.com"
|
|
53
64
|
* ```
|
|
54
65
|
*/
|
|
55
66
|
function constant(value) {
|
|
56
|
-
return {
|
|
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`
|
|
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
|
|
89
|
-
if (
|
|
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
|
-
|
|
99
|
-
|
|
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 (
|
|
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 (
|
|
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
|
|
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(
|
|
124
|
-
return new StaticInit(
|
|
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
|
-
|
|
150
|
-
|
|
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(
|
|
154
|
-
this.
|
|
188
|
+
constructor(ctor) {
|
|
189
|
+
this.ctor = ctor;
|
|
155
190
|
}
|
|
156
191
|
/**
|
|
157
|
-
* Resolves each dependency as a new instance via {@link Box.new}
|
|
158
|
-
*
|
|
159
|
-
* Returns
|
|
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
|
-
*
|
|
180
|
-
* Returns
|
|
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 };
|