@k8slens/injectable 26.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/LICENSE +22 -0
- package/README.md +809 -0
- package/dist/index.js +2182 -0
- package/index.d.ts +1683 -0
- package/package.json +53 -0
package/index.d.ts
ADDED
|
@@ -0,0 +1,1683 @@
|
|
|
1
|
+
/// <reference types="jest" />
|
|
2
|
+
|
|
3
|
+
// "Any function shape" — the canonical constraint for a v2 factory.
|
|
4
|
+
// Uses `any` deliberately: `(...args: unknown[]) => unknown` is not a valid
|
|
5
|
+
// universal function constraint (concrete functions don't extend it).
|
|
6
|
+
export type Factory = (...args: any[]) => any;
|
|
7
|
+
|
|
8
|
+
// v1-shape stub for v1 targets: (di, param) => instance
|
|
9
|
+
type OverrideV1ShapeOldStyle = <
|
|
10
|
+
InjectionInstance extends InjectionTokenInstance,
|
|
11
|
+
InjectionTokenInstance,
|
|
12
|
+
InstantiationParam,
|
|
13
|
+
>(
|
|
14
|
+
injectable:
|
|
15
|
+
| InjectionToken<InjectionInstance, InstantiationParam>
|
|
16
|
+
| Injectable<InjectionInstance, InjectionTokenInstance, InstantiationParam>,
|
|
17
|
+
instantiateStub: Instantiate<InjectionInstance, InstantiationParam>,
|
|
18
|
+
) => void;
|
|
19
|
+
|
|
20
|
+
// v1-shape stub for v2 targets (cross-compat): (di, ...params) => instance
|
|
21
|
+
type OverrideV1ShapeForInjectable2 = <F extends Factory>(
|
|
22
|
+
alias: Alias2<F>,
|
|
23
|
+
instantiateStub: (
|
|
24
|
+
di: DiContainerForInjection2,
|
|
25
|
+
...params: Parameters<F>
|
|
26
|
+
) => ReturnType<F>,
|
|
27
|
+
) => void;
|
|
28
|
+
|
|
29
|
+
// v2-shape stub for v2 targets: (di) => (...params) => instance
|
|
30
|
+
export type OverrideInjectable2 = <F extends Factory>(
|
|
31
|
+
alias: Alias2<F>,
|
|
32
|
+
instantiateStub: (di: DiContainerForInjection2) => F,
|
|
33
|
+
) => void;
|
|
34
|
+
|
|
35
|
+
// v2-shape stub for v1 targets (cross-compat): (di) => (param) => instance
|
|
36
|
+
type Override2V2ShapeForOldStyle = <
|
|
37
|
+
InjectionInstance extends InjectionTokenInstance,
|
|
38
|
+
InjectionTokenInstance,
|
|
39
|
+
InstantiationParam,
|
|
40
|
+
>(
|
|
41
|
+
injectable:
|
|
42
|
+
| InjectionToken<InjectionInstance, InstantiationParam>
|
|
43
|
+
| Injectable<InjectionInstance, InjectionTokenInstance, InstantiationParam>,
|
|
44
|
+
instantiateStub: (
|
|
45
|
+
di: DiContainerForInjection,
|
|
46
|
+
) => (param: InstantiationParam) => InjectionInstance,
|
|
47
|
+
) => void;
|
|
48
|
+
|
|
49
|
+
// Cross-compat override: v1-shape stub works on any target.
|
|
50
|
+
export type Override = OverrideV1ShapeOldStyle & OverrideV1ShapeForInjectable2;
|
|
51
|
+
|
|
52
|
+
// Cross-compat override2: v2-shape stub works on any target.
|
|
53
|
+
export type Override2 = OverrideInjectable2 & Override2V2ShapeForOldStyle;
|
|
54
|
+
|
|
55
|
+
// ---- validate ----
|
|
56
|
+
//
|
|
57
|
+
// Checks every registered injectable2's declared consumptions against what is
|
|
58
|
+
// actually registered, without instantiating anything: a token declared 'one'
|
|
59
|
+
// or 'one-or-many' must have at least one implementation, counting those
|
|
60
|
+
// registered against any of its `.for()` derivatives. Upper bounds need no
|
|
61
|
+
// check here, registering having already rejected a second implementation.
|
|
62
|
+
//
|
|
63
|
+
// All violations are reported in one thrown error, so a composition root is
|
|
64
|
+
// fixed in one pass rather than one registration at a time.
|
|
65
|
+
|
|
66
|
+
export interface ValidationReportInjectables {
|
|
67
|
+
count: number;
|
|
68
|
+
ids: string[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface UnverifiableConsumption {
|
|
72
|
+
injectableId: string;
|
|
73
|
+
consumptionId: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface ValidationReport {
|
|
77
|
+
// Injectables whose consumptions were checked — every injectable2.
|
|
78
|
+
verifiedInjectables: ValidationReportInjectables;
|
|
79
|
+
// v1 injectables, which declare nothing and so cannot be checked.
|
|
80
|
+
unverifiedInjectables: ValidationReportInjectables;
|
|
81
|
+
// Declared v1 tokens: carrying no cardinality, they have no arity to check,
|
|
82
|
+
// and remain a runtime concern.
|
|
83
|
+
unverifiableConsumptions: UnverifiableConsumption[];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface DiContainer extends DiContainerForInjection {
|
|
87
|
+
validate: () => ValidationReport;
|
|
88
|
+
inject2: Inject2;
|
|
89
|
+
injectMany2: InjectMany2;
|
|
90
|
+
injectMaybe2: InjectMaybe2;
|
|
91
|
+
injectWithMeta2: InjectWithMeta2;
|
|
92
|
+
injectManyWithMeta2: InjectManyWithMeta2;
|
|
93
|
+
injectMaybeWithMeta2: InjectMaybeWithMeta2;
|
|
94
|
+
|
|
95
|
+
purge: Purge;
|
|
96
|
+
purgeAllButOverrides: () => void;
|
|
97
|
+
|
|
98
|
+
permitSideEffects: (alias?: Alias) => void;
|
|
99
|
+
|
|
100
|
+
override: Override;
|
|
101
|
+
override2: Override2;
|
|
102
|
+
earlyOverride: Override;
|
|
103
|
+
earlyOverride2: Override2;
|
|
104
|
+
unoverride<F extends Factory>(alias: Alias2<F>): void;
|
|
105
|
+
unoverride(alias: Alias1): void;
|
|
106
|
+
|
|
107
|
+
register(...injectables: (Alias | InjectableBunch)[]): void;
|
|
108
|
+
|
|
109
|
+
deregister(...injectables: (Alias | InjectableBunch)[]): void;
|
|
110
|
+
|
|
111
|
+
getNumberOfInstances: () => Record<string, number>;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export type Instantiate<InjectionInstance, InstantiationParam = void> = (di: DiContainerForInjection, param: InstantiationParam) => InjectionInstance;
|
|
115
|
+
|
|
116
|
+
export interface InjectionToken<
|
|
117
|
+
InjectionInstance = any,
|
|
118
|
+
InstantiationParam = void,
|
|
119
|
+
SpecificInjectionTokenFactory extends (
|
|
120
|
+
...args: any[]
|
|
121
|
+
) => SpecificInjectionToken<InjectionInstance, InstantiationParam> = (
|
|
122
|
+
id: string,
|
|
123
|
+
) => SpecificInjectionToken<InjectionInstance, InstantiationParam>,
|
|
124
|
+
> {
|
|
125
|
+
readonly aliasType: "injection-token";
|
|
126
|
+
template: InjectionInstance;
|
|
127
|
+
instantiationParameter: InstantiationParam;
|
|
128
|
+
key: Symbol;
|
|
129
|
+
id: string;
|
|
130
|
+
for: SpecificInjectionTokenFactory;
|
|
131
|
+
maxCacheSize?: number;
|
|
132
|
+
// Every token carries the initial tag 'injectionToken' plus any tags given
|
|
133
|
+
// at creation; `.for()` children inherit the general token's tags. Optional
|
|
134
|
+
// because the built-in machinery tokens are untagged.
|
|
135
|
+
tags?: string[];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface SpecificInjectionToken<
|
|
139
|
+
InjectionInstance = any,
|
|
140
|
+
InstantiationParam = void,
|
|
141
|
+
SpecificInjectionTokenFactory extends (
|
|
142
|
+
...args: any[]
|
|
143
|
+
) => SpecificInjectionToken<InjectionInstance, InstantiationParam> = (
|
|
144
|
+
id: string,
|
|
145
|
+
) => SpecificInjectionToken<InjectionInstance, InstantiationParam>,
|
|
146
|
+
> extends InjectionToken<
|
|
147
|
+
InjectionInstance,
|
|
148
|
+
InstantiationParam,
|
|
149
|
+
SpecificInjectionTokenFactory
|
|
150
|
+
> {
|
|
151
|
+
speciality: any;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export interface Injectable<
|
|
155
|
+
InjectionInstance extends InjectionTokenInstance = any,
|
|
156
|
+
InjectionTokenInstance = InjectionInstance,
|
|
157
|
+
InstantiationParam = void,
|
|
158
|
+
> {
|
|
159
|
+
readonly aliasType: "injectable";
|
|
160
|
+
readonly id: string;
|
|
161
|
+
readonly causesSideEffects?: boolean;
|
|
162
|
+
readonly injectionToken?: InjectionToken<
|
|
163
|
+
InjectionTokenInstance,
|
|
164
|
+
InstantiationParam
|
|
165
|
+
>;
|
|
166
|
+
readonly instantiate: Instantiate<InjectionInstance, InstantiationParam>;
|
|
167
|
+
readonly lifecycle: Lifecycle<InstantiationParam>;
|
|
168
|
+
readonly tags?: any[];
|
|
169
|
+
readonly maxCacheSize?: number;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export type GetInjectableOptionsWithoutInstantiationParameter<I extends TI, TI> = Omit<Injectable<I, TI>, "lifecycle" | "instantiate" | "aliasType"> & {
|
|
173
|
+
readonly instantiate: (di: DiContainerForInjection, param: void) => I;
|
|
174
|
+
readonly lifecycle?: Lifecycle;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export type GetInjectableOptionsWithInstantiationParameter<I extends TI, TI, P> = Omit<Injectable<I, TI, P>, "instantiate" | "aliasType"> & {
|
|
178
|
+
readonly instantiate: (di: DiContainerForInjection, param: P) => I;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export interface GetInjectable{
|
|
182
|
+
<I extends TI, TI>(options: GetInjectableOptionsWithoutInstantiationParameter<I, TI>): Injectable<I, TI>;
|
|
183
|
+
<I extends TI, TI, P>(options: GetInjectableOptionsWithInstantiationParameter<I, TI, P>): Injectable<I, TI, P>;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export const getInjectable: GetInjectable;
|
|
187
|
+
|
|
188
|
+
export type InjectableBunch<InjectableConfig extends object = object> = InjectableConfig;
|
|
189
|
+
|
|
190
|
+
export function getInjectableBunch<Type extends object>(
|
|
191
|
+
bunch: Type,
|
|
192
|
+
): InjectableBunch<Type>;
|
|
193
|
+
|
|
194
|
+
export function getInjectionToken<
|
|
195
|
+
InjectionInstance,
|
|
196
|
+
InstantiationParam = void,
|
|
197
|
+
SpecificInjectionTokenFactory extends (
|
|
198
|
+
...args: any[]
|
|
199
|
+
) => SpecificInjectionToken<InjectionInstance, InstantiationParam> = (
|
|
200
|
+
id: string,
|
|
201
|
+
) => SpecificInjectionToken<InjectionInstance, InstantiationParam>,
|
|
202
|
+
>(options: {
|
|
203
|
+
id: string;
|
|
204
|
+
// Must be pure and deterministic: `.for()` memoizes by specifier and skips
|
|
205
|
+
// calling the factory again for a specifier it has already seen.
|
|
206
|
+
specificInjectionTokenFactory?: SpecificInjectionTokenFactory;
|
|
207
|
+
target?: object;
|
|
208
|
+
maxCacheSize?: number;
|
|
209
|
+
tags?: string[];
|
|
210
|
+
}): InjectionToken<
|
|
211
|
+
InjectionInstance,
|
|
212
|
+
InstantiationParam,
|
|
213
|
+
SpecificInjectionTokenFactory
|
|
214
|
+
>;
|
|
215
|
+
|
|
216
|
+
export function getSpecificInjectionToken<
|
|
217
|
+
InjectionInstance,
|
|
218
|
+
InstantiationParam = void,
|
|
219
|
+
SpecificInjectionTokenFactory extends (
|
|
220
|
+
...args: any[]
|
|
221
|
+
) => SpecificInjectionToken<InjectionInstance, InstantiationParam> = (
|
|
222
|
+
id: string,
|
|
223
|
+
) => SpecificInjectionToken<InjectionInstance, InstantiationParam>,
|
|
224
|
+
>(options: {
|
|
225
|
+
id: string;
|
|
226
|
+
speciality: any;
|
|
227
|
+
tags?: string[];
|
|
228
|
+
}): SpecificInjectionToken<
|
|
229
|
+
InjectionInstance,
|
|
230
|
+
InstantiationParam,
|
|
231
|
+
SpecificInjectionTokenFactory
|
|
232
|
+
>;
|
|
233
|
+
|
|
234
|
+
// The initial tag carried by every injection token (v1 and v2), permitting
|
|
235
|
+
// tag-keyed decorators to target any token.
|
|
236
|
+
export const injectionTokenTag: 'injectionToken';
|
|
237
|
+
|
|
238
|
+
export type InjectWithoutParameter = <InjectionInstance>(
|
|
239
|
+
alias:
|
|
240
|
+
| Injectable<InjectionInstance, unknown>
|
|
241
|
+
| InjectionToken<InjectionInstance>,
|
|
242
|
+
) => InjectionInstance;
|
|
243
|
+
|
|
244
|
+
export type InjectWithParameter = <InjectionInstance, InstantiationParam>(
|
|
245
|
+
alias:
|
|
246
|
+
| Injectable<InjectionInstance, unknown, InstantiationParam>
|
|
247
|
+
| InjectionToken<InjectionInstance, InstantiationParam>,
|
|
248
|
+
param: InstantiationParam,
|
|
249
|
+
) => InjectionInstance;
|
|
250
|
+
|
|
251
|
+
// Only tokens declared 'one' are injectable singly; 'zero-or-one' goes through
|
|
252
|
+
// `injectMaybe`, the many-cardinalities through `injectMany`. Injectables carry
|
|
253
|
+
// no cardinality and are always injectable directly.
|
|
254
|
+
export type InjectInjectable2 = <F extends Factory>(
|
|
255
|
+
alias: Injectable2<F> | Token2<F, any, undefined, 'one'>,
|
|
256
|
+
...params: Parameters<F>
|
|
257
|
+
) => ReturnType<F>;
|
|
258
|
+
|
|
259
|
+
export type Inject = InjectWithoutParameter & InjectWithParameter & InjectInjectable2;
|
|
260
|
+
|
|
261
|
+
// Factory-returning inject — handles all aliases (v1 and v2).
|
|
262
|
+
// v2 aliases return the native factory F; v1 aliases return a synthesized factory.
|
|
263
|
+
export interface Inject2 {
|
|
264
|
+
<F extends Factory>(alias: Injectable2<F>): F;
|
|
265
|
+
<F extends Factory>(alias: Token2<F, any, undefined, 'one'>): F;
|
|
266
|
+
<I>(alias: Injectable<I, any> | InjectionToken<I>): () => I;
|
|
267
|
+
<I, P>(alias: Injectable<I, any, P> | InjectionToken<I, P>): (...params: [P]) => I;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
type TuplePrefix<T extends any[]> = T extends [infer First, ...infer Rest]
|
|
271
|
+
? [] | [First, ...TuplePrefix<Rest>]
|
|
272
|
+
: [];
|
|
273
|
+
|
|
274
|
+
type PurgeAll = () => void;
|
|
275
|
+
|
|
276
|
+
type PurgeInjectable2 = <F extends Factory>(
|
|
277
|
+
alias: Alias2<F>,
|
|
278
|
+
...keyParts: TuplePrefix<Parameters<F>>
|
|
279
|
+
) => void;
|
|
280
|
+
|
|
281
|
+
type PurgeWithoutParameter = <I>(
|
|
282
|
+
alias: Injectable<I, any> | InjectionToken<I>,
|
|
283
|
+
) => void;
|
|
284
|
+
|
|
285
|
+
type PurgeWithParameter = <I, P>(
|
|
286
|
+
alias: Injectable<I, any, P> | InjectionToken<I, P>,
|
|
287
|
+
...keyParts: [] | [P]
|
|
288
|
+
) => void;
|
|
289
|
+
|
|
290
|
+
export type Purge = PurgeAll & PurgeInjectable2 & PurgeWithoutParameter & PurgeWithParameter;
|
|
291
|
+
|
|
292
|
+
export type SpecificInjectWithoutParameter<InjectionInstance> = (
|
|
293
|
+
alias:
|
|
294
|
+
| Injectable<InjectionInstance, unknown>
|
|
295
|
+
| InjectionToken<InjectionInstance>,
|
|
296
|
+
) => InjectionInstance;
|
|
297
|
+
|
|
298
|
+
export type SpecificInjectWithParameter<InjectionInstance, InstantiationParam> =
|
|
299
|
+
(
|
|
300
|
+
alias:
|
|
301
|
+
| Injectable<InjectionInstance, unknown, InstantiationParam>
|
|
302
|
+
| InjectionToken<InjectionInstance, InstantiationParam>,
|
|
303
|
+
param: InstantiationParam,
|
|
304
|
+
) => InjectionInstance;
|
|
305
|
+
|
|
306
|
+
export type SpecificInject<InjectionInstance, InstantiationParam> =
|
|
307
|
+
InstantiationParam extends void
|
|
308
|
+
? SpecificInjectWithoutParameter<InjectionInstance>
|
|
309
|
+
: SpecificInjectWithParameter<InjectionInstance, InstantiationParam>;
|
|
310
|
+
|
|
311
|
+
export interface InjectMany {
|
|
312
|
+
// The element type comes from the token's many-factory, but the result is
|
|
313
|
+
// normalized to a plain array: the exact consumption shape (one-or-many's
|
|
314
|
+
// non-empty tuple) is injectMany2's factory-returning domain, and widening
|
|
315
|
+
// here keeps this a non-breaking fix for v1 consumers.
|
|
316
|
+
<F extends Factory, MF extends ManyFactory<F>>(
|
|
317
|
+
alias: Token2<F, MF, any, 'zero-or-many' | 'one-or-many'>,
|
|
318
|
+
...params: Parameters<F>
|
|
319
|
+
): ReturnType<MF> extends (infer R)[] ? R[] : never;
|
|
320
|
+
|
|
321
|
+
<InjectionInstance>(
|
|
322
|
+
alias:
|
|
323
|
+
| Injectable<InjectionInstance, unknown>
|
|
324
|
+
| InjectionToken<InjectionInstance, void, any>,
|
|
325
|
+
): InjectionInstance[];
|
|
326
|
+
|
|
327
|
+
<InjectionInstance, InstantiationParam>(
|
|
328
|
+
alias:
|
|
329
|
+
| Injectable<InjectionInstance, unknown, InstantiationParam>
|
|
330
|
+
| InjectionToken<InjectionInstance, InstantiationParam, any>,
|
|
331
|
+
param: InstantiationParam,
|
|
332
|
+
): InjectionInstance[];
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export type Meta = {
|
|
336
|
+
id: string;
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
export type InjectionInstanceWithMeta<InjectionInstance> = {
|
|
340
|
+
instance: InjectionInstance;
|
|
341
|
+
meta: Meta;
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
export interface InjectManyWithMeta {
|
|
345
|
+
<F extends Factory, MF extends ManyFactory<F>>(
|
|
346
|
+
alias: Token2<F, MF, any, 'zero-or-many' | 'one-or-many'>,
|
|
347
|
+
...params: Parameters<F>
|
|
348
|
+
): InjectionInstanceWithMeta<
|
|
349
|
+
ReturnType<MF> extends (infer R)[] ? R : never
|
|
350
|
+
>[];
|
|
351
|
+
|
|
352
|
+
<InjectionInstance>(
|
|
353
|
+
alias:
|
|
354
|
+
| Injectable<InjectionInstance, unknown>
|
|
355
|
+
| InjectionToken<InjectionInstance, void, any>,
|
|
356
|
+
): InjectionInstanceWithMeta<InjectionInstance>[];
|
|
357
|
+
|
|
358
|
+
<InjectionInstance, InstantiationParam>(
|
|
359
|
+
alias:
|
|
360
|
+
| Injectable<InjectionInstance, unknown, InstantiationParam>
|
|
361
|
+
| InjectionToken<InjectionInstance, InstantiationParam, any>,
|
|
362
|
+
param: InstantiationParam,
|
|
363
|
+
): InjectionInstanceWithMeta<InjectionInstance>[];
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export interface InjectWithMeta {
|
|
367
|
+
<F extends Factory>(
|
|
368
|
+
alias: Injectable2<F> | Token2<F, any, undefined, 'one'>,
|
|
369
|
+
...params: Parameters<F>
|
|
370
|
+
): InjectionInstanceWithMeta<ReturnType<F>>;
|
|
371
|
+
|
|
372
|
+
<InjectionInstance>(
|
|
373
|
+
alias:
|
|
374
|
+
| Injectable<InjectionInstance, unknown>
|
|
375
|
+
| InjectionToken<InjectionInstance, void, any>,
|
|
376
|
+
): InjectionInstanceWithMeta<InjectionInstance>;
|
|
377
|
+
|
|
378
|
+
<InjectionInstance, InstantiationParam>(
|
|
379
|
+
alias:
|
|
380
|
+
| Injectable<InjectionInstance, unknown, InstantiationParam>
|
|
381
|
+
| InjectionToken<InjectionInstance, InstantiationParam, any>,
|
|
382
|
+
param: InstantiationParam,
|
|
383
|
+
): InjectionInstanceWithMeta<InjectionInstance>;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export interface DiContainerForInjection {
|
|
387
|
+
inject: Inject;
|
|
388
|
+
injectMany: InjectMany;
|
|
389
|
+
injectWithMeta: InjectWithMeta;
|
|
390
|
+
injectManyWithMeta: InjectManyWithMeta;
|
|
391
|
+
injectFactory: Inject2;
|
|
392
|
+
|
|
393
|
+
register(...injectables: (Alias | InjectableBunch)[]): void;
|
|
394
|
+
|
|
395
|
+
deregister(...injectables: (Alias | InjectableBunch)[]): void;
|
|
396
|
+
|
|
397
|
+
sourceNamespace: string | undefined;
|
|
398
|
+
|
|
399
|
+
purge: Purge;
|
|
400
|
+
|
|
401
|
+
hasRegistrations: (alias: Alias) => boolean;
|
|
402
|
+
|
|
403
|
+
registeredInLocalScope: (alias: Alias) => boolean;
|
|
404
|
+
|
|
405
|
+
registeredInLocalScopeSubtree: (alias: Alias) => boolean;
|
|
406
|
+
|
|
407
|
+
getNumberOfRegistrations: (alias: Alias) => number;
|
|
408
|
+
|
|
409
|
+
getNumberOfInstances: () => Record<string, number>;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export interface Lifecycle<InstantiationParam = void> {
|
|
413
|
+
getInstanceKey: (di: DiContainer, params: InstantiationParam) => any;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
declare const storedInstanceKey: unique symbol;
|
|
417
|
+
declare const nonStoredInstanceKey: unique symbol;
|
|
418
|
+
|
|
419
|
+
export const lifecycleEnum: {
|
|
420
|
+
singleton: {
|
|
421
|
+
getInstanceKey: (di: DiContainer) => typeof storedInstanceKey;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
keyedSingleton<InstantiationParam>(
|
|
425
|
+
options: Lifecycle<InstantiationParam>,
|
|
426
|
+
): typeof options;
|
|
427
|
+
|
|
428
|
+
transient: {
|
|
429
|
+
getInstanceKey: (di: DiContainer) => typeof nonStoredInstanceKey;
|
|
430
|
+
};
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
type RegistrationCallback = (injectable: Injectable<any, any, any>) => void;
|
|
434
|
+
|
|
435
|
+
// --- injectionDecoratorToken ---
|
|
436
|
+
//
|
|
437
|
+
// Abstract token for decorating injection. Decorators must be registered
|
|
438
|
+
// against a target via `.for(target)` where target is an Injectable, Injectable2,
|
|
439
|
+
// InjectionToken, or InjectionToken2.
|
|
440
|
+
//
|
|
441
|
+
// The decorator function receives the bound inject for that alias:
|
|
442
|
+
// inject => (...params) => decoratedInstance
|
|
443
|
+
// where inject: (...params) => instance.
|
|
444
|
+
//
|
|
445
|
+
// This decorator does not respect lifecycle — it is called on every `di.inject`.
|
|
446
|
+
|
|
447
|
+
export type InjectionDecoratorForInjectable2<F extends Factory = Factory> =
|
|
448
|
+
() => (inject: (...params: Parameters<F>) => ReturnType<F>)
|
|
449
|
+
=> (...params: Parameters<F>) => ReturnType<F>;
|
|
450
|
+
|
|
451
|
+
export interface InjectionDecoratorSpecificFactory {
|
|
452
|
+
<F extends Factory>(
|
|
453
|
+
target: Alias2<F>,
|
|
454
|
+
): SpecificToken2<InjectionDecoratorForInjectable2<F>>;
|
|
455
|
+
|
|
456
|
+
<InjectionInstance, InstantiationParam = void>(
|
|
457
|
+
target: Injectable<InjectionInstance, any, InstantiationParam> | InjectionToken<InjectionInstance, InstantiationParam>,
|
|
458
|
+
): SpecificToken2<() => (inject: Factory) => Factory>;
|
|
459
|
+
|
|
460
|
+
// Tag-keyed dispatch: an injection decorator targeting a string tag fires
|
|
461
|
+
// for every injectable whose `tags` array contains that tag — but ONLY when
|
|
462
|
+
// `di.inject` is called with a concrete injectable as the alias. Token-aliases
|
|
463
|
+
// (`di.inject(someToken)`) do NOT trigger tag-keyed injection-decorators.
|
|
464
|
+
// Weak typing is intentional — the tag is a documentation string.
|
|
465
|
+
(tag: string): SpecificToken2<() => (inject: Factory) => Factory>;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
export const injectionDecoratorToken: Token2<
|
|
469
|
+
Factory,
|
|
470
|
+
ManyFactory,
|
|
471
|
+
InjectionDecoratorSpecificFactory,
|
|
472
|
+
'zero-or-many'
|
|
473
|
+
>;
|
|
474
|
+
|
|
475
|
+
// --- instantiationDecoratorToken ---
|
|
476
|
+
//
|
|
477
|
+
// Abstract token for decorating instantiation. Decorators must be registered
|
|
478
|
+
// against a target via `.for(target)` where target is an Injectable, Injectable2,
|
|
479
|
+
// InjectionToken, or InjectionToken2.
|
|
480
|
+
//
|
|
481
|
+
// The decorator function wraps the instantiate function:
|
|
482
|
+
// instantiate => (di, ...params) => decoratedInstance (v1 injectables)
|
|
483
|
+
// instantiate => (di) => (...params) => decoratedInstance (v2 injectables)
|
|
484
|
+
//
|
|
485
|
+
// This decorator respects the lifecycle of the injectables.
|
|
486
|
+
|
|
487
|
+
export type InstantiationDecoratorForInjectable2<F extends Factory = Factory> =
|
|
488
|
+
() => (instantiate: (di: DiContainerForInjection) => F)
|
|
489
|
+
=> (di: DiContainerForInjection) => F;
|
|
490
|
+
|
|
491
|
+
export type InstantiationDecoratorForTags<InjectionInstance, InstantiationParams extends any[]> = (di: DiContainerForInjection, ...params: InstantiationParams) => InjectionInstance;
|
|
492
|
+
|
|
493
|
+
export interface InstantiationDecoratorSpecificFactory {
|
|
494
|
+
<F extends Factory>(
|
|
495
|
+
target: Alias2<F>,
|
|
496
|
+
): SpecificToken2<InstantiationDecoratorForInjectable2<F>>;
|
|
497
|
+
|
|
498
|
+
<InjectionInstance, InstantiationParam = void>(
|
|
499
|
+
target: Injectable<InjectionInstance, any, InstantiationParam> | InjectionToken<InjectionInstance, InstantiationParam>,
|
|
500
|
+
): SpecificToken2<() => (instantiate: Instantiate<InjectionInstance, InstantiationParam>) => Instantiate<InjectionInstance, InstantiationParam>>;
|
|
501
|
+
|
|
502
|
+
// Tag-keyed dispatch: an instantiation decorator targeting a string tag
|
|
503
|
+
// fires for every injectable whose `tags` array contains that tag.
|
|
504
|
+
(tag: string): SpecificToken2<<InjectionInstance, InstantiationParams extends any[]>() =>
|
|
505
|
+
(instantiate: InstantiationDecoratorForTags<InjectionInstance, InstantiationParams>) =>
|
|
506
|
+
InstantiationDecoratorForTags<InjectionInstance, InstantiationParams>>;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
export const instantiationDecoratorToken: Token2<
|
|
510
|
+
Factory,
|
|
511
|
+
ManyFactory,
|
|
512
|
+
InstantiationDecoratorSpecificFactory,
|
|
513
|
+
'zero-or-many'
|
|
514
|
+
>;
|
|
515
|
+
|
|
516
|
+
// --- registrationDecoratorToken ---
|
|
517
|
+
//
|
|
518
|
+
// Abstract token for decorating registration. Decorators must be registered
|
|
519
|
+
// against a target via `.for(target)` where target is an Injectable, Injectable2,
|
|
520
|
+
// InjectionToken, or InjectionToken2. The decorator receives the bound register
|
|
521
|
+
// call and may call it (to proceed), skip it (to prevent registration), or
|
|
522
|
+
// store it for deferred invocation.
|
|
523
|
+
|
|
524
|
+
export type RegistrationDecorator = () =>
|
|
525
|
+
(register: (injectable: Injectable<any, any, any> | Injectable2) => void)
|
|
526
|
+
=> (injectable: Injectable<any, any, any> | Injectable2) => void;
|
|
527
|
+
|
|
528
|
+
export interface RegistrationDecoratorSpecificFactory {
|
|
529
|
+
<F extends Factory>(
|
|
530
|
+
target: Alias2<F>,
|
|
531
|
+
): SpecificToken2<RegistrationDecorator>;
|
|
532
|
+
|
|
533
|
+
<InjectionInstance, InstantiationParam = void>(
|
|
534
|
+
target: Injectable<InjectionInstance, any, InstantiationParam> | InjectionToken<InjectionInstance, InstantiationParam>,
|
|
535
|
+
): SpecificToken2<RegistrationDecorator>;
|
|
536
|
+
|
|
537
|
+
// Tag-keyed dispatch: a registration decorator targeting a string tag
|
|
538
|
+
// fires for every injectable whose `tags` array contains that tag.
|
|
539
|
+
// Weak typing is intentional — the tag is a documentation string, not a
|
|
540
|
+
// type witness.
|
|
541
|
+
(tag: string): SpecificToken2<RegistrationDecorator>;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
export const registrationDecoratorToken: Token2<
|
|
545
|
+
Factory,
|
|
546
|
+
ManyFactory,
|
|
547
|
+
RegistrationDecoratorSpecificFactory,
|
|
548
|
+
'zero-or-many'
|
|
549
|
+
>;
|
|
550
|
+
|
|
551
|
+
export const deregistrationDecoratorToken: Token2<
|
|
552
|
+
Factory,
|
|
553
|
+
ManyFactory,
|
|
554
|
+
RegistrationDecoratorSpecificFactory,
|
|
555
|
+
'zero-or-many'
|
|
556
|
+
>;
|
|
557
|
+
|
|
558
|
+
export const registrationCallbackToken: RegistrationCallback;
|
|
559
|
+
export const deregistrationCallbackToken: RegistrationCallback;
|
|
560
|
+
|
|
561
|
+
// --- instancePurgeCallbackToken ---
|
|
562
|
+
//
|
|
563
|
+
// Abstract base token. Callbacks must be registered against a specifier-
|
|
564
|
+
// scoped token produced by `.for(target)` — there is no untargeted variant.
|
|
565
|
+
// Targets are `Injectable2<F>` or `Token2<F>`; old-style targets
|
|
566
|
+
// are not supported. Firing happens when a cached instance of an injectable2
|
|
567
|
+
// is evicted by `di.purge(...)`, `di.deregister(...)`, or LRU eviction.
|
|
568
|
+
//
|
|
569
|
+
// The scoped token's Factory is `() => Callback` — parameterless so it
|
|
570
|
+
// resolves as a singleton per callback injectable (one cached entry under
|
|
571
|
+
// the singleton key, not one per evicted instance). The returned Callback
|
|
572
|
+
// is CURRIED `(payload) => Factory` — mirroring the Override `(di) => F`
|
|
573
|
+
// trick — so free generics in `Factory` survive to the inner arrow. The
|
|
574
|
+
// inner Factory's return value is discarded at runtime; it is required
|
|
575
|
+
// only to keep Factory intact as a covariant return position.
|
|
576
|
+
//
|
|
577
|
+
// Payload is an object for readability.
|
|
578
|
+
|
|
579
|
+
export type InstancePurgeCallbackForInjectable2<F extends Factory = Factory> =
|
|
580
|
+
() => (payload: { instance: ReturnType<F> }) => F;
|
|
581
|
+
|
|
582
|
+
export interface InstancePurgeCallbackSpecificFactory {
|
|
583
|
+
<F extends Factory>(
|
|
584
|
+
target: Alias2<F>,
|
|
585
|
+
): SpecificToken2<InstancePurgeCallbackForInjectable2<F>>;
|
|
586
|
+
|
|
587
|
+
// Tag-keyed dispatch: a purge callback targeting a string tag fires for
|
|
588
|
+
// every injectable whose `tags` array contains that tag.
|
|
589
|
+
// Weak typing is intentional — the tag is a documentation string.
|
|
590
|
+
(tag: string): SpecificToken2<InstancePurgeCallbackForInjectable2<Factory>>;
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
export const instancePurgeCallbackToken: Token2<
|
|
594
|
+
Factory,
|
|
595
|
+
ManyFactory,
|
|
596
|
+
InstancePurgeCallbackSpecificFactory,
|
|
597
|
+
'zero-or-many'
|
|
598
|
+
>;
|
|
599
|
+
|
|
600
|
+
// --- preInjectCallbackToken ---
|
|
601
|
+
//
|
|
602
|
+
// Abstract base token. Callbacks must be registered against a specifier-
|
|
603
|
+
// scoped token produced by `.for(target)` — targets are any alias
|
|
604
|
+
// (injectable or token, v1 or v2) or a string tag. The callback fires once
|
|
605
|
+
// before every matching inject operation — before alias resolution and
|
|
606
|
+
// before any failure check, so a callback may register implementations that
|
|
607
|
+
// the same operation then observes, and it fires even when nothing is
|
|
608
|
+
// registered for the alias. `di.injectMany` fires it once for the whole
|
|
609
|
+
// call with the token alias; resolving the elements does not fire it again.
|
|
610
|
+
//
|
|
611
|
+
// The callback also receives the injecting party — the injectable whose
|
|
612
|
+
// `instantiate` made the call, or the container root for injects made
|
|
613
|
+
// directly on `di`.
|
|
614
|
+
//
|
|
615
|
+
// The scoped token's Factory is `() => PreInjectCallback` — parameterless
|
|
616
|
+
// so it resolves as a singleton per callback injectable.
|
|
617
|
+
|
|
618
|
+
export type PreInjectCallbackKind = 'inject' | 'injectMany';
|
|
619
|
+
|
|
620
|
+
// The container itself, which stands in as the injecting party for injects
|
|
621
|
+
// made directly on `di` rather than from within an `instantiate`.
|
|
622
|
+
export interface ContainerRoot {
|
|
623
|
+
id: string;
|
|
624
|
+
aliasType: 'container';
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
export type PreInjectCallback = (
|
|
628
|
+
alias: Alias,
|
|
629
|
+
kind: PreInjectCallbackKind,
|
|
630
|
+
injectingInjectable: Alias | ContainerRoot,
|
|
631
|
+
) => void;
|
|
632
|
+
|
|
633
|
+
export interface PreInjectCallbackSpecificFactory {
|
|
634
|
+
(target: Alias): SpecificToken2<() => PreInjectCallback>;
|
|
635
|
+
|
|
636
|
+
// Tag-keyed dispatch: a pre-inject callback targeting a string tag fires
|
|
637
|
+
// for every inject operation whose alias (or a token in its chain)
|
|
638
|
+
// carries the tag. Weak typing is intentional — the tag is a
|
|
639
|
+
// documentation string.
|
|
640
|
+
(tag: string): SpecificToken2<() => PreInjectCallback>;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
export const preInjectCallbackToken: Token2<
|
|
644
|
+
Factory,
|
|
645
|
+
ManyFactory,
|
|
646
|
+
PreInjectCallbackSpecificFactory,
|
|
647
|
+
'zero-or-many'
|
|
648
|
+
>;
|
|
649
|
+
|
|
650
|
+
export const isInjectable: (
|
|
651
|
+
thing: unknown,
|
|
652
|
+
) => thing is Injectable<unknown, unknown, unknown>;
|
|
653
|
+
export const isInjectableBunch: (
|
|
654
|
+
thing: unknown,
|
|
655
|
+
) => thing is InjectableBunch;
|
|
656
|
+
export const isInjectionToken: (
|
|
657
|
+
thing: unknown,
|
|
658
|
+
) => thing is InjectionToken<unknown, unknown>;
|
|
659
|
+
|
|
660
|
+
export function createContainer(containerId: string): DiContainer;
|
|
661
|
+
|
|
662
|
+
export function getKeyedSingletonCompositeKey<T extends [...unknown[]]>(
|
|
663
|
+
...keys: T
|
|
664
|
+
): { keys: T };
|
|
665
|
+
|
|
666
|
+
export type TypedSpecifier<SpecifierName extends string = string, Typing extends object = {}> =
|
|
667
|
+
SpecifierName
|
|
668
|
+
& [Typing];
|
|
669
|
+
|
|
670
|
+
export type TypedSpecifierWithType<TypeName extends string, T = unknown> = TypedSpecifier<string, { [K in TypeName]: T }>;
|
|
671
|
+
|
|
672
|
+
export type TypedSpecifierType<TypeName extends string, Specifier extends TypedSpecifierWithType<TypeName>> =
|
|
673
|
+
Specifier extends TypedSpecifier<string, infer Typing extends Record<TypeName, unknown>>
|
|
674
|
+
? Typing[TypeName]
|
|
675
|
+
: never;
|
|
676
|
+
|
|
677
|
+
export function getTypedSpecifier
|
|
678
|
+
<Typing extends object>():
|
|
679
|
+
<SpecifierName extends string>(specifier: SpecifierName) =>
|
|
680
|
+
TypedSpecifier<SpecifierName, Typing>;
|
|
681
|
+
|
|
682
|
+
// ---- Injectable2 / InjectionToken2 ----
|
|
683
|
+
|
|
684
|
+
export interface Injectable2<F extends Factory = Factory> {
|
|
685
|
+
readonly aliasType: 'injectable2';
|
|
686
|
+
readonly id: string;
|
|
687
|
+
readonly instantiate: (di: DiContainerForInjection2) => F;
|
|
688
|
+
// Stored at the wider Factory shape so an injectable's F can stay narrower
|
|
689
|
+
// than its token's F. The construction-time `F extends TF` constraint on
|
|
690
|
+
// `getInjectable2` keeps the relationship sound; precise per-injectable
|
|
691
|
+
// token typing isn't observably useful here (runtime reads only token-level
|
|
692
|
+
// fields like `.id`, `.abstract`, `.maxCacheSize`).
|
|
693
|
+
readonly injectionToken?: Token2<Factory, any, any>;
|
|
694
|
+
readonly consumptions?: ReadonlyArray<Consumption>;
|
|
695
|
+
readonly transient?: boolean;
|
|
696
|
+
readonly causesSideEffects?: boolean;
|
|
697
|
+
readonly tags?: string[];
|
|
698
|
+
readonly maxCacheSize?: number;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// ---- Consumptions ----
|
|
702
|
+
//
|
|
703
|
+
// An injectable2 declares the injection tokens it may inject. The declaration
|
|
704
|
+
// is enforced twice over: `instantiate`'s `di` only accepts what was declared,
|
|
705
|
+
// and the container checks every inject at runtime — which is what catches
|
|
706
|
+
// plain-JS callers and tokens that merely have the same shape as a declared
|
|
707
|
+
// one. Injecting another injectable by reference needs no declaration: that
|
|
708
|
+
// already implies a dependency on wherever the injectable lives.
|
|
709
|
+
//
|
|
710
|
+
// Absence means "injects no tokens": with nothing declared, `Cons` is `never`
|
|
711
|
+
// and no token is injectable.
|
|
712
|
+
|
|
713
|
+
export type Consumption =
|
|
714
|
+
| Token2<any, any, any, any>
|
|
715
|
+
| InjectionToken<any, any, any>;
|
|
716
|
+
|
|
717
|
+
// The `.for()` derivatives of a declared token are covered by declaring the
|
|
718
|
+
// token itself — otherwise a specifier only known at runtime could not be
|
|
719
|
+
// declared at all. One level suffices: the default `.for()` factory is
|
|
720
|
+
// self-similar, so a child's own `for` yields the same shape again.
|
|
721
|
+
type NotAny<T> = 0 extends 1 & T ? never : T;
|
|
722
|
+
|
|
723
|
+
export type ConsumptionChildOf<C> = C extends {
|
|
724
|
+
for: (...args: any[]) => infer Child;
|
|
725
|
+
}
|
|
726
|
+
? NotAny<Child> extends { speciality: any }
|
|
727
|
+
? NotAny<Child>
|
|
728
|
+
: never
|
|
729
|
+
: never;
|
|
730
|
+
|
|
731
|
+
type Consumable<Cons extends Consumption> =
|
|
732
|
+
| Cons
|
|
733
|
+
| ConsumptionChildOf<Cons>;
|
|
734
|
+
|
|
735
|
+
// The injection surface of `instantiate`'s `di`: the same cardinality gating as
|
|
736
|
+
// the container's, narrowed to what the injectable declared. Injectables pass
|
|
737
|
+
// freely, matching the runtime exemption.
|
|
738
|
+
export interface ConsumptionInject2<Cons extends Consumption> {
|
|
739
|
+
<F extends Factory>(alias: Injectable2<F>): F;
|
|
740
|
+
<F extends Factory>(
|
|
741
|
+
alias: Consumable<Cons> & Token2<F, any, undefined, 'one'>,
|
|
742
|
+
): F;
|
|
743
|
+
<I>(alias: Injectable<I, any> | (Consumable<Cons> & InjectionToken<I>)): () => I;
|
|
744
|
+
<I, P>(
|
|
745
|
+
alias:
|
|
746
|
+
| Injectable<I, any, P>
|
|
747
|
+
| (Consumable<Cons> & InjectionToken<I, P>),
|
|
748
|
+
): (...params: [P]) => I;
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
export interface ConsumptionInjectMany2<Cons extends Consumption> {
|
|
752
|
+
<F extends Factory, MF extends ManyFactory<F>>(
|
|
753
|
+
alias: Consumable<Cons> &
|
|
754
|
+
Token2<F, MF, any, 'zero-or-many' | 'one-or-many'>,
|
|
755
|
+
): MF;
|
|
756
|
+
<I>(alias: Consumable<Cons> & InjectionToken<I>): () => I[];
|
|
757
|
+
<I, P>(
|
|
758
|
+
alias: Consumable<Cons> & InjectionToken<I, P>,
|
|
759
|
+
): (...params: P extends any[] ? P : [P]) => I[];
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
export interface ConsumptionInjectMaybe2<Cons extends Consumption> {
|
|
763
|
+
<F extends Factory, MF extends (...args: Parameters<F>) => ReturnType<F> | undefined>(
|
|
764
|
+
alias: Consumable<Cons> & Token2<F, MF, undefined, 'zero-or-one'>,
|
|
765
|
+
): MF;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// The withMeta pair mirrors InjectWithMeta2 / InjectManyWithMeta2 with the
|
|
769
|
+
// same consumption gating as the plain variants — the runtime enforces
|
|
770
|
+
// declarations on these paths too.
|
|
771
|
+
export interface ConsumptionInjectWithMeta2<Cons extends Consumption> {
|
|
772
|
+
<F extends Factory>(alias: Injectable2<F>): ToWithMetaFactory<F>;
|
|
773
|
+
<F extends Factory, WF>(
|
|
774
|
+
alias: Consumable<Cons> & Token2<F, any, undefined, 'one', WF>,
|
|
775
|
+
): WF;
|
|
776
|
+
<I>(
|
|
777
|
+
alias: Injectable<I, any> | (Consumable<Cons> & InjectionToken<I>),
|
|
778
|
+
): () => InjectionInstanceWithMeta<I>;
|
|
779
|
+
<I, P>(
|
|
780
|
+
alias: Injectable<I, any, P> | (Consumable<Cons> & InjectionToken<I, P>),
|
|
781
|
+
): (...params: P extends any[] ? P : [P]) => InjectionInstanceWithMeta<I>;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
export interface ConsumptionInjectManyWithMeta2<Cons extends Consumption> {
|
|
785
|
+
<F extends Factory, WMF>(
|
|
786
|
+
alias: Consumable<Cons> &
|
|
787
|
+
Token2<F, any, any, 'zero-or-many' | 'one-or-many', any, WMF>,
|
|
788
|
+
): WMF;
|
|
789
|
+
<I>(
|
|
790
|
+
alias: Consumable<Cons> & InjectionToken<I>,
|
|
791
|
+
): () => InjectionInstanceWithMeta<I>[];
|
|
792
|
+
<I, P>(alias: Consumable<Cons> & InjectionToken<I, P>): (
|
|
793
|
+
...params: P extends any[] ? P : [P]
|
|
794
|
+
) => InjectionInstanceWithMeta<I>[];
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
export interface ConsumptionInjectMaybeWithMeta2<Cons extends Consumption> {
|
|
798
|
+
<F extends Factory, WMF>(
|
|
799
|
+
alias: Consumable<Cons> &
|
|
800
|
+
Token2<F, any, undefined, 'zero-or-one', any, WMF>,
|
|
801
|
+
): WMF;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/** @deprecated Renamed to ConsumptionInject2 — "scoped" clashes with the container's registration scopes. */
|
|
805
|
+
export type ScopedInject2<Cons extends Consumption> = ConsumptionInject2<Cons>;
|
|
806
|
+
/** @deprecated Renamed to ConsumptionInjectMany2 — "scoped" clashes with the container's registration scopes. */
|
|
807
|
+
export type ScopedInjectMany2<Cons extends Consumption> =
|
|
808
|
+
ConsumptionInjectMany2<Cons>;
|
|
809
|
+
/** @deprecated Renamed to ConsumptionInjectMaybe2 — "scoped" clashes with the container's registration scopes. */
|
|
810
|
+
export type ScopedInjectMaybe2<Cons extends Consumption> =
|
|
811
|
+
ConsumptionInjectMaybe2<Cons>;
|
|
812
|
+
|
|
813
|
+
// `instantiate`'s di. Leave the parameter unannotated so it is typed
|
|
814
|
+
// contextually from the `consumptions` array; annotate it with this when
|
|
815
|
+
// `instantiate` is written as a named function elsewhere.
|
|
816
|
+
export interface ConsumptionDi<Cons extends Consumption = never>
|
|
817
|
+
extends Omit<
|
|
818
|
+
DiContainerForInjection2,
|
|
819
|
+
| 'inject'
|
|
820
|
+
| 'injectMany'
|
|
821
|
+
| 'injectMaybe'
|
|
822
|
+
| 'injectWithMeta'
|
|
823
|
+
| 'injectManyWithMeta'
|
|
824
|
+
| 'injectMaybeWithMeta'
|
|
825
|
+
> {
|
|
826
|
+
inject: ConsumptionInject2<Cons>;
|
|
827
|
+
injectMany: ConsumptionInjectMany2<Cons>;
|
|
828
|
+
injectMaybe: ConsumptionInjectMaybe2<Cons>;
|
|
829
|
+
injectWithMeta: ConsumptionInjectWithMeta2<Cons>;
|
|
830
|
+
injectManyWithMeta: ConsumptionInjectManyWithMeta2<Cons>;
|
|
831
|
+
injectMaybeWithMeta: ConsumptionInjectMaybeWithMeta2<Cons>;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// With injectionToken: F is the injectable's actual factory (kept narrow so
|
|
835
|
+
// `di.inject2(injectable)` returns the narrow factory), TF is the token's
|
|
836
|
+
// factory contract. The `F extends TF` constraint verifies the implementation
|
|
837
|
+
// satisfies the contract.
|
|
838
|
+
export function getInjectable2<
|
|
839
|
+
F extends TF,
|
|
840
|
+
TF extends Factory,
|
|
841
|
+
const Cons extends Consumption = never,
|
|
842
|
+
>(options: {
|
|
843
|
+
readonly id: string;
|
|
844
|
+
readonly consumptions?: ReadonlyArray<Cons>;
|
|
845
|
+
readonly instantiate: (di: ConsumptionDi<Cons>) => F;
|
|
846
|
+
readonly injectionToken: Token2<TF, any, undefined, any>;
|
|
847
|
+
readonly transient?: boolean;
|
|
848
|
+
readonly causesSideEffects?: boolean;
|
|
849
|
+
readonly tags?: string[];
|
|
850
|
+
readonly maxCacheSize?: number;
|
|
851
|
+
}): Injectable2<F>;
|
|
852
|
+
|
|
853
|
+
// Without injectionToken: infer F from instantiate, so callers of di.inject /
|
|
854
|
+
// useInject / useInject2 still get precise parameter and return types.
|
|
855
|
+
export function getInjectable2<
|
|
856
|
+
F extends Factory,
|
|
857
|
+
const Cons extends Consumption = never,
|
|
858
|
+
>(options: {
|
|
859
|
+
readonly id: string;
|
|
860
|
+
readonly consumptions?: ReadonlyArray<Cons>;
|
|
861
|
+
readonly instantiate: (di: ConsumptionDi<Cons>) => F;
|
|
862
|
+
readonly transient?: boolean;
|
|
863
|
+
readonly causesSideEffects?: boolean;
|
|
864
|
+
readonly tags?: string[];
|
|
865
|
+
readonly maxCacheSize?: number;
|
|
866
|
+
}): Injectable2<F>;
|
|
867
|
+
|
|
868
|
+
export type ManyFactory<F extends Factory = Factory> = F extends (...args: infer P) => infer R
|
|
869
|
+
? (...args: P) => R[]
|
|
870
|
+
: never;
|
|
871
|
+
|
|
872
|
+
// ---- Cardinality ----
|
|
873
|
+
//
|
|
874
|
+
// Every v2 injection token declares how many implementations it expects, which
|
|
875
|
+
// decides the one consumption API that accepts it: `inject` for 'one',
|
|
876
|
+
// `injectMaybe` for 'zero-or-one', `injectMany` for the two many-cardinalities.
|
|
877
|
+
// Upper bounds ('one', 'zero-or-one' permit at most one implementation) are
|
|
878
|
+
// enforced when registering; lower bounds ('one', 'one-or-many' require at
|
|
879
|
+
// least one) by `di.validate()`.
|
|
880
|
+
|
|
881
|
+
export type Cardinality = 'one' | 'zero-or-one' | 'zero-or-many' | 'one-or-many';
|
|
882
|
+
|
|
883
|
+
// The consumption shape of a 'zero-or-one' token: the instance, or undefined
|
|
884
|
+
// when nothing is registered. Presence is resolved per call, so a token whose
|
|
885
|
+
// implementation is registered later starts yielding it. Note this cannot
|
|
886
|
+
// distinguish "nothing registered" from "the factory returned undefined".
|
|
887
|
+
export type MaybeResultFactory<F extends Factory = Factory> = F extends (...args: infer P) => infer R
|
|
888
|
+
? (...args: P) => R | undefined
|
|
889
|
+
: never;
|
|
890
|
+
|
|
891
|
+
// The consumption shape of a 'one-or-many' token: a non-empty array, so
|
|
892
|
+
// indexing the first element needs no defensive check.
|
|
893
|
+
export type NonEmptyManyFactory<F extends Factory = Factory> = F extends (...args: infer P) => infer R
|
|
894
|
+
? (...args: P) => [R, ...R[]]
|
|
895
|
+
: never;
|
|
896
|
+
|
|
897
|
+
// Constraint for the many-factory slot on the token interfaces: wide enough to
|
|
898
|
+
// hold every cardinality's consumption shape. Each creator overload narrows it
|
|
899
|
+
// to the shape its cardinality actually permits, so a token can never be built
|
|
900
|
+
// carrying a shape its cardinality disallows.
|
|
901
|
+
export type AnyConsumptionFactory<F extends Factory> = (
|
|
902
|
+
...args: Parameters<F>
|
|
903
|
+
) => ReturnType<F>[] | ReturnType<F> | undefined;
|
|
904
|
+
|
|
905
|
+
export type DefaultConsumptionFactory<C extends Cardinality, F extends Factory> =
|
|
906
|
+
C extends 'zero-or-one'
|
|
907
|
+
? MaybeResultFactory<F>
|
|
908
|
+
: C extends 'one-or-many'
|
|
909
|
+
? NonEmptyManyFactory<F>
|
|
910
|
+
: ManyFactory<F>;
|
|
911
|
+
|
|
912
|
+
// Default with-meta consumption shapes derived from the base/many factory:
|
|
913
|
+
// the same parameters, the result (or each element) wrapped in
|
|
914
|
+
// InjectionInstanceWithMeta. Like every utility-type derivation, these
|
|
915
|
+
// collapse a generic factory to its constraint — a token whose withMeta
|
|
916
|
+
// consumers must keep a generic declares its with-meta slots explicitly
|
|
917
|
+
// instead (see InjectionToken2Base). Both are `any`-proofed: deriving over
|
|
918
|
+
// `any` (wide positions such as `Consumption`'s Token2<any, ...>)
|
|
919
|
+
// would otherwise produce `(...args: unknown[]) => ...`, whose contravariant
|
|
920
|
+
// parameters REJECT every concrete token — the opposite of wide.
|
|
921
|
+
export type DefaultWithMetaFactory<F> = 0 extends 1 & F
|
|
922
|
+
? any
|
|
923
|
+
: ToWithMetaFactory<F>;
|
|
924
|
+
|
|
925
|
+
// Cardinality-shaped, exactly like the MF slot it derives from: an
|
|
926
|
+
// array-returning MF (the many-cardinalities, and 'one') yields the
|
|
927
|
+
// injectManyWithMeta shape, a MaybeResultFactory ('zero-or-one') yields the
|
|
928
|
+
// injectMaybeWithMeta shape. The trailing `MF extends any` distributes over
|
|
929
|
+
// the default MF slot — a bare `Token2<F>` leaves MF as the
|
|
930
|
+
// ManyFactory | MaybeResultFactory union, and deriving non-distributively
|
|
931
|
+
// would collapse the shape to nonsense, making the bare annotation reject
|
|
932
|
+
// every concrete token.
|
|
933
|
+
export type DefaultWithMetaConsumptionFactory<MF extends Factory> = 0 extends 1 &
|
|
934
|
+
MF
|
|
935
|
+
? any
|
|
936
|
+
: MF extends any
|
|
937
|
+
? ReturnType<MF> extends (infer R)[]
|
|
938
|
+
? (...args: Parameters<MF>) => InjectionInstanceWithMeta<R>[]
|
|
939
|
+
: (
|
|
940
|
+
...args: Parameters<MF>
|
|
941
|
+
) =>
|
|
942
|
+
| InjectionInstanceWithMeta<Exclude<ReturnType<MF>, undefined>>
|
|
943
|
+
| undefined
|
|
944
|
+
: never;
|
|
945
|
+
|
|
946
|
+
export interface InjectionToken2Base<
|
|
947
|
+
F extends Factory = Factory,
|
|
948
|
+
MF extends AnyConsumptionFactory<F> = ManyFactory<F> | MaybeResultFactory<F>,
|
|
949
|
+
C extends Cardinality = Cardinality,
|
|
950
|
+
WF = DefaultWithMetaFactory<F>,
|
|
951
|
+
WMF = DefaultWithMetaConsumptionFactory<MF>,
|
|
952
|
+
> {
|
|
953
|
+
readonly aliasType: 'injection-token2';
|
|
954
|
+
template: F;
|
|
955
|
+
manyTemplate: MF;
|
|
956
|
+
// With-meta consumption shapes: what injectWithMeta / injectManyWithMeta
|
|
957
|
+
// (and their factory-returning forms) hand back for this token. Purely
|
|
958
|
+
// type-level anchors — never set at runtime — defaulted by derivation from
|
|
959
|
+
// F / MF, which collapses a generic factory to its constraint. To keep a
|
|
960
|
+
// generic through the withMeta consumers, declare the slots explicitly by
|
|
961
|
+
// creating the token through the slots-bag creator overload, which stamps
|
|
962
|
+
// them on the returned token:
|
|
963
|
+
// const token = getInjectionToken2<{
|
|
964
|
+
// singleFactory: GF;
|
|
965
|
+
// manyFactory: GMF;
|
|
966
|
+
// singleMetaFactory: <T>(value: T) => InjectionInstanceWithMeta<T>;
|
|
967
|
+
// manyMetaFactory: <T>(value: T) => InjectionInstanceWithMeta<T>[];
|
|
968
|
+
// }>({ id: 'some-id', cardinality: 'zero-or-many' })();
|
|
969
|
+
// (On a token built positionally, a cast to the bag annotation does the
|
|
970
|
+
// same — a cast, not an assignment, since a monomorphic default-derived
|
|
971
|
+
// shape can't be assigned to a generic one; honest either way because the
|
|
972
|
+
// runtime is parametric.)
|
|
973
|
+
withMetaTemplate: WF;
|
|
974
|
+
withMetaConsumptionTemplate: WMF;
|
|
975
|
+
key: Symbol;
|
|
976
|
+
id: string;
|
|
977
|
+
// Declared arity. The creators always stamp a literal, so a token annotated
|
|
978
|
+
// without one (`Token2<F>`, C = the whole union) means "a token of
|
|
979
|
+
// some cardinality" — it is accepted by wide positions like `register` but
|
|
980
|
+
// cannot be consumed until narrowed. The property is optional because the
|
|
981
|
+
// runtime is plain JS and nothing type-level should assume its presence.
|
|
982
|
+
cardinality?: C;
|
|
983
|
+
maxCacheSize?: number;
|
|
984
|
+
// Every token carries the initial tag 'injectionToken' plus any tags given
|
|
985
|
+
// at creation; `.for()` children inherit the general token's tags. Optional
|
|
986
|
+
// because the built-in machinery tokens are untagged.
|
|
987
|
+
tags?: string[];
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// ---- Named type-parameter slots (the annotation form) ----
|
|
991
|
+
//
|
|
992
|
+
// InjectionToken2 and SpecificInjectionToken2 take a single object of named
|
|
993
|
+
// slots as their only type argument:
|
|
994
|
+
//
|
|
995
|
+
// InjectionToken2<{ singleFactory: F; cardinality: 'one' }>
|
|
996
|
+
//
|
|
997
|
+
// `singleFactory` is mandatory; every other slot is optional with the
|
|
998
|
+
// defaults each extractor below spells, so a bag names only what it
|
|
999
|
+
// customizes — immune to positional-order concerns, and new slots can be
|
|
1000
|
+
// added without breaking anything. Exception: a GENERIC (or overloaded)
|
|
1001
|
+
// singleFactory makes the consumption slot (`manyFactory` or
|
|
1002
|
+
// `maybeFactory`), `singleMetaFactory` and the consumption meta slot
|
|
1003
|
+
// (`manyMetaFactory` or `maybeMetaFactory`) mandatory as well, because their
|
|
1004
|
+
// derived defaults would silently collapse it (see Token2SingleFactoryBound
|
|
1005
|
+
// below) — spell the collapsed
|
|
1006
|
+
// `unknown`-product shape deliberately when that is what is wanted. Unknown
|
|
1007
|
+
// keys are rejected (see Token2ExactSlots below). The slots are uncorrelated
|
|
1008
|
+
// (e.g. `manyFactory` is not checked against `singleFactory`'s parameters):
|
|
1009
|
+
// cross-slot conformance is the creators' job; annotations state intent.
|
|
1010
|
+
// "Any specific-token factory" — what a `.for()` slot may hold, with no
|
|
1011
|
+
// correlation to a particular contract. The single point of truth for the
|
|
1012
|
+
// uncorrelated shape; the creators' SF bounds spell the F-correlated
|
|
1013
|
+
// variant, where the correlation is the point.
|
|
1014
|
+
type AnySpecificTokenFactory = (
|
|
1015
|
+
...args: any[]
|
|
1016
|
+
) => SpecificToken2<any, any, any, any>;
|
|
1017
|
+
|
|
1018
|
+
export interface InjectionToken2Slots {
|
|
1019
|
+
singleFactory?: Factory;
|
|
1020
|
+
// The consumption slot is a cardinality-honest PAIR: `manyFactory` for
|
|
1021
|
+
// 'one' and the many-cardinalities, `maybeFactory` for 'zero-or-one' — a
|
|
1022
|
+
// maybe-factory is not a many-factory, so it does not borrow the name. A
|
|
1023
|
+
// bag carries at most one member of the pair, and a pinned cardinality
|
|
1024
|
+
// must agree with the member used (see Token2ExactSlots below). The same
|
|
1025
|
+
// split applies to the meta pair below.
|
|
1026
|
+
manyFactory?: Factory;
|
|
1027
|
+
maybeFactory?: Factory;
|
|
1028
|
+
cardinality?: Cardinality;
|
|
1029
|
+
// Function-bounded, unlike the internal WF/WMF parameters (which stay
|
|
1030
|
+
// unconstrained for provability): a bag is always spelled by hand, and a
|
|
1031
|
+
// non-function meta slot is always a mistake.
|
|
1032
|
+
singleMetaFactory?: Factory;
|
|
1033
|
+
manyMetaFactory?: Factory;
|
|
1034
|
+
maybeMetaFactory?: Factory;
|
|
1035
|
+
specificTokenFactory?: undefined | AnySpecificTokenFactory;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// Detects a generic function type: rebuilding the signature from
|
|
1039
|
+
// Parameters<>/ReturnType<> collapses type parameters to their constraints,
|
|
1040
|
+
// and the collapsed signature is assignable back to F only when F had no
|
|
1041
|
+
// type parameters to lose. A non-generic factory that deliberately produces
|
|
1042
|
+
// `unknown` (e.g. `() => unknown`) rebuilds to itself, so it is NOT flagged.
|
|
1043
|
+
// OVERLOADED factories are flagged as well — deliberately: the rebuild sees
|
|
1044
|
+
// only the last call signature, which is exactly what the derived defaults
|
|
1045
|
+
// would silently reduce an overloaded contract to, so the explicit-slots
|
|
1046
|
+
// mandate protects them for the same reason it protects generics.
|
|
1047
|
+
type IsGenericFactory<F extends Factory> = 0 extends 1 & F
|
|
1048
|
+
? false
|
|
1049
|
+
: ((...args: Parameters<F>) => ReturnType<F>) extends F
|
|
1050
|
+
? false
|
|
1051
|
+
: true;
|
|
1052
|
+
|
|
1053
|
+
// The bound the bag's own `singleFactory` value must meet: normally just
|
|
1054
|
+
// Factory, but a GENERIC singleFactory in a bag that does not also spell
|
|
1055
|
+
// every derived factory slot (manyFactory and both meta slots, whose
|
|
1056
|
+
// defaults would silently collapse the generic to its constraint) is bounded
|
|
1057
|
+
// by an error literal instead, so the constraint fails right on the bag with
|
|
1058
|
+
// the requirement spelled out in the compiler output. Naming a collapsed
|
|
1059
|
+
// `unknown`-product shape deliberately satisfies the requirement; having the
|
|
1060
|
+
// collapse happen silently does not.
|
|
1061
|
+
type Token2SingleFactoryBound<Bag> = Bag extends {
|
|
1062
|
+
singleFactory: infer F extends Factory;
|
|
1063
|
+
}
|
|
1064
|
+
? IsGenericFactory<F> extends true
|
|
1065
|
+
? Bag extends { singleMetaFactory: unknown } &
|
|
1066
|
+
({ manyFactory: Factory } | { maybeFactory: Factory }) &
|
|
1067
|
+
({ manyMetaFactory: unknown } | { maybeMetaFactory: unknown })
|
|
1068
|
+
? Factory
|
|
1069
|
+
: 'ERROR: a generic (or overloaded) singleFactory requires explicit manyFactory (or maybeFactory), singleMetaFactory and manyMetaFactory (or maybeMetaFactory) slots — their derived defaults would collapse it'
|
|
1070
|
+
: Factory
|
|
1071
|
+
: Factory;
|
|
1072
|
+
|
|
1073
|
+
// The slots-bag constraint: a bag whose every key exists on
|
|
1074
|
+
// InjectionToken2Slots with a conforming value, which carries the mandatory
|
|
1075
|
+
// `singleFactory` (a bag without the contract factory is a mistake, not a
|
|
1076
|
+
// wide annotation — the positional form covers "any factory"), and whose
|
|
1077
|
+
// generic-singleFactory obligations hold (see Token2SingleFactoryBound).
|
|
1078
|
+
// Self-referential on purpose — a plain `extends Partial<InjectionToken2Slots>`
|
|
1079
|
+
// bound would let a typo'd key (`cardinalty`) pass silently, since extra
|
|
1080
|
+
// properties satisfy an all-optional object type; mapping the bag's own keys
|
|
1081
|
+
// makes an unknown key demand `never` and fail the constraint instead. The
|
|
1082
|
+
// generic-obligation conditional lives in the mapped type's VALUE position
|
|
1083
|
+
// (and the presence requirement is a plain intersection) because a
|
|
1084
|
+
// conditional over the bag directly in the type parameter's own constraint
|
|
1085
|
+
// is a circular constraint (TS2313); mapped-type values are evaluated
|
|
1086
|
+
// lazily, which is what makes the self-reference legal.
|
|
1087
|
+
// The bounds a consumption-pair key's value must meet: normally Factory,
|
|
1088
|
+
// but carrying the rival pair member, or a member disagreeing with the
|
|
1089
|
+
// bag's pinned cardinality, bounds the key by an error literal instead —
|
|
1090
|
+
// the constraint then fails right on the offending key with the rule in
|
|
1091
|
+
// the compiler output. A cardinality that is a union, or absent, pins
|
|
1092
|
+
// nothing (a specific-token leaf may inherit its arity).
|
|
1093
|
+
type Token2ManyKeyBound<
|
|
1094
|
+
Bag,
|
|
1095
|
+
RivalKey extends 'maybeFactory' | 'maybeMetaFactory',
|
|
1096
|
+
> = Bag extends Record<RivalKey, unknown>
|
|
1097
|
+
? 'ERROR: a bag takes either the many- or the maybe- member of a consumption-slot pair, not both'
|
|
1098
|
+
: Bag extends { cardinality: infer C extends Cardinality }
|
|
1099
|
+
? [C] extends ['zero-or-one']
|
|
1100
|
+
? "ERROR: a 'zero-or-one' bag takes maybeFactory / maybeMetaFactory, not the many- slots"
|
|
1101
|
+
: Factory
|
|
1102
|
+
: Factory;
|
|
1103
|
+
|
|
1104
|
+
type Token2MaybeKeyBound<
|
|
1105
|
+
Bag,
|
|
1106
|
+
RivalKey extends 'manyFactory' | 'manyMetaFactory',
|
|
1107
|
+
> = Bag extends Record<RivalKey, unknown>
|
|
1108
|
+
? 'ERROR: a bag takes either the many- or the maybe- member of a consumption-slot pair, not both'
|
|
1109
|
+
: Bag extends { cardinality: infer C extends Cardinality }
|
|
1110
|
+
? [C] extends ['one' | 'zero-or-many' | 'one-or-many']
|
|
1111
|
+
? "ERROR: maybeFactory / maybeMetaFactory are the 'zero-or-one' consumption slots — a many-cardinality bag takes the many- slots"
|
|
1112
|
+
: Factory
|
|
1113
|
+
: Factory;
|
|
1114
|
+
|
|
1115
|
+
type Token2ExactSlots<Bag> = {
|
|
1116
|
+
[K in keyof Bag]: K extends 'singleFactory'
|
|
1117
|
+
? Token2SingleFactoryBound<Bag>
|
|
1118
|
+
: // specificTokenFactory is the one slot where `undefined` is a real
|
|
1119
|
+
// value (the concrete pin), so it keeps it explicitly...
|
|
1120
|
+
K extends 'specificTokenFactory'
|
|
1121
|
+
? undefined | AnySpecificTokenFactory
|
|
1122
|
+
: K extends 'manyFactory'
|
|
1123
|
+
? Token2ManyKeyBound<Bag, 'maybeFactory'>
|
|
1124
|
+
: K extends 'manyMetaFactory'
|
|
1125
|
+
? Token2ManyKeyBound<Bag, 'maybeMetaFactory'>
|
|
1126
|
+
: K extends 'maybeFactory'
|
|
1127
|
+
? Token2MaybeKeyBound<Bag, 'manyFactory'>
|
|
1128
|
+
: K extends 'maybeMetaFactory'
|
|
1129
|
+
? Token2MaybeKeyBound<Bag, 'manyMetaFactory'>
|
|
1130
|
+
: K extends keyof InjectionToken2Slots
|
|
1131
|
+
? // ...while every other present key must carry a real value —
|
|
1132
|
+
// `singleMetaFactory: undefined` would otherwise silence the
|
|
1133
|
+
// generic-collapse mandate.
|
|
1134
|
+
NonNullable<InjectionToken2Slots[K]>
|
|
1135
|
+
: never;
|
|
1136
|
+
} & { singleFactory: Factory };
|
|
1137
|
+
|
|
1138
|
+
// ---- Slot extractors ----
|
|
1139
|
+
//
|
|
1140
|
+
// Each maps either form of the first type argument to one effective slot:
|
|
1141
|
+
// the positional branch hands the value through (or the positional default),
|
|
1142
|
+
// the bag branches extract the key or fall back to that same default. All of
|
|
1143
|
+
// them are DELIBERATELY naked distributive conditionals with the checked
|
|
1144
|
+
// parameter appearing in the first branch: inside a distributive true branch
|
|
1145
|
+
// TS substitutes `X & Factory` for X, which keeps the whole conditional
|
|
1146
|
+
// provably Factory-bounded (so it can instantiate InjectionToken2Base and
|
|
1147
|
+
// sit inside Parameters<>/ReturnType<>) while staying transparent to
|
|
1148
|
+
// inference — a tuple guard or an `infer`-rebound branch would be opaque to
|
|
1149
|
+
// one or the other, and the machinery signatures below infer F through
|
|
1150
|
+
// these. For a concrete positional F every extractor resolves to exactly
|
|
1151
|
+
// what the pre-slots declaration spelled, so existing annotations keep their
|
|
1152
|
+
// exact types.
|
|
1153
|
+
type Token2SingleFactorySlot<X> = X extends Factory
|
|
1154
|
+
? X
|
|
1155
|
+
: X extends { singleFactory: infer F extends Factory }
|
|
1156
|
+
? F
|
|
1157
|
+
: Factory;
|
|
1158
|
+
|
|
1159
|
+
// The consumption slot reads whichever pair member the bag carries —
|
|
1160
|
+
// `manyFactory` or `maybeFactory` — and a bag that pins `cardinality`
|
|
1161
|
+
// without either derives the consumption shape from the cardinality,
|
|
1162
|
+
// exactly like the creators and the per-cardinality aliases do.
|
|
1163
|
+
type Token2ConsumptionFactorySlot<X> = X extends Factory
|
|
1164
|
+
? ManyFactory<X> | MaybeResultFactory<X>
|
|
1165
|
+
: X extends { manyFactory: infer F extends Factory }
|
|
1166
|
+
? F
|
|
1167
|
+
: X extends { maybeFactory: infer F extends Factory }
|
|
1168
|
+
? F
|
|
1169
|
+
: X extends { cardinality: infer C extends Cardinality }
|
|
1170
|
+
? DefaultConsumptionFactory<C, Token2SingleFactorySlot<X>>
|
|
1171
|
+
: ManyFactory<Token2SingleFactorySlot<X>> | MaybeResultFactory<Token2SingleFactorySlot<X>>;
|
|
1172
|
+
|
|
1173
|
+
type Token2CardinalitySlot<X> = X extends Factory
|
|
1174
|
+
? Cardinality
|
|
1175
|
+
: X extends { cardinality: infer C extends Cardinality }
|
|
1176
|
+
? C
|
|
1177
|
+
: Cardinality;
|
|
1178
|
+
|
|
1179
|
+
type Token2SingleMetaFactorySlot<X> = X extends Factory
|
|
1180
|
+
? DefaultWithMetaFactory<X>
|
|
1181
|
+
: X extends { singleMetaFactory: infer F }
|
|
1182
|
+
? F
|
|
1183
|
+
: DefaultWithMetaFactory<Token2SingleFactorySlot<X>>;
|
|
1184
|
+
|
|
1185
|
+
type Token2ConsumptionMetaFactorySlot<X, MF extends Factory> = X extends Factory
|
|
1186
|
+
? DefaultWithMetaConsumptionFactory<MF>
|
|
1187
|
+
: X extends { manyMetaFactory: infer F }
|
|
1188
|
+
? F
|
|
1189
|
+
: X extends { maybeMetaFactory: infer F }
|
|
1190
|
+
? F
|
|
1191
|
+
: DefaultWithMetaConsumptionFactory<MF>;
|
|
1192
|
+
|
|
1193
|
+
// The specific-token-factory slot decides abstractness, and its
|
|
1194
|
+
// key-omitted fallback differs per alias — `any` (wide, "abstract or not")
|
|
1195
|
+
// for InjectionToken2, `undefined` (concrete) for SpecificInjectionToken2 —
|
|
1196
|
+
// mirroring their positional defaults, so the fallback is a parameter.
|
|
1197
|
+
type Token2SpecificFactorySlot<X, Fallback> = X extends Factory
|
|
1198
|
+
? Fallback
|
|
1199
|
+
: X extends {
|
|
1200
|
+
specificTokenFactory: infer F extends undefined | AnySpecificTokenFactory;
|
|
1201
|
+
}
|
|
1202
|
+
? F
|
|
1203
|
+
: Fallback;
|
|
1204
|
+
|
|
1205
|
+
// `for` — and abstractness — exist only when a real `specificInjectionTokenFactory`
|
|
1206
|
+
// was given: `getInjectionToken2<F>(options)()` (no factory) instantiates
|
|
1207
|
+
// SpecificFactory as `undefined`, so the resulting token has no `.for` at all
|
|
1208
|
+
// (a compile error to access, not merely `for: undefined`) and is not
|
|
1209
|
+
// abstract; a real factory value makes it both have `.for()` and be abstract
|
|
1210
|
+
// — a token only ever needs a factory when it's meant to be resolved
|
|
1211
|
+
// exclusively through `.for()`. This must stay a naked conditional, not the
|
|
1212
|
+
// usual `[X] extends [Y]` tuple guard against distributing over unions — a
|
|
1213
|
+
// naked conditional distributes `any` into the union of both branches, which
|
|
1214
|
+
// is what lets wide positions that must accept both a for-less token and an
|
|
1215
|
+
// abstract, for-bearing one (e.g. `injectMany`, typed
|
|
1216
|
+
// `Token2<F, any, any, C>`) keep doing so, while positions that must
|
|
1217
|
+
// reject abstract tokens (e.g. `di.inject`, `getInjectable2`'s
|
|
1218
|
+
// `injectionToken`) pin `SpecificFactory` to `undefined` instead of `any`.
|
|
1219
|
+
// `undefined`, not `never`, is the sentinel: a naked conditional collapses to
|
|
1220
|
+
// `never` whenever the checked type is `never` itself, which would make a
|
|
1221
|
+
// for-less token's type useless.
|
|
1222
|
+
//
|
|
1223
|
+
// INTERNAL, positional: the machinery signatures' form — inference of F/MF/
|
|
1224
|
+
// WMF flows through naked parameter positions, which a bag cannot offer.
|
|
1225
|
+
// Annotations and creators go through the public bag-only aliases below.
|
|
1226
|
+
type Token2<
|
|
1227
|
+
F extends Factory = Factory,
|
|
1228
|
+
// Bound `Factory`, not `AnyConsumptionFactory<F>`: F may now be a slots
|
|
1229
|
+
// bag, and the extractor standing in for it is opaque to the constraint
|
|
1230
|
+
// solver — a correlated bound here is unprovable at every generic call
|
|
1231
|
+
// site. The creators still enforce the real consumption shape;
|
|
1232
|
+
// InjectionToken2Base keeps the strict bound for direct use.
|
|
1233
|
+
MF extends Factory = Token2ConsumptionFactorySlot<F>,
|
|
1234
|
+
// The positional fallback is `any`, not `undefined`: a bare
|
|
1235
|
+
// `Token2<F>` means "a token of some cardinality, abstract or
|
|
1236
|
+
// not" — the same wide, don't-care reading `any` gets everywhere else
|
|
1237
|
+
// (`injectMany`, `Consumption`, `Alias2`). Positions that must pin one
|
|
1238
|
+
// state spell it out explicitly instead of relying on this default. The
|
|
1239
|
+
// returned token's F in the bound is `any`, uncorrelated with this alias's
|
|
1240
|
+
// F, for the same provability reason as MF's bound; the creators keep the
|
|
1241
|
+
// correlated constraint.
|
|
1242
|
+
SpecificFactory extends
|
|
1243
|
+
| undefined
|
|
1244
|
+
| AnySpecificTokenFactory = Token2SpecificFactorySlot<F, any>,
|
|
1245
|
+
C extends Cardinality = Token2CardinalitySlot<F>,
|
|
1246
|
+
WF = Token2SingleMetaFactorySlot<F>,
|
|
1247
|
+
WMF = Token2ConsumptionMetaFactorySlot<F, MF>,
|
|
1248
|
+
> = SpecificFactory extends undefined
|
|
1249
|
+
? InjectionToken2Base<Token2SingleFactorySlot<F>, MF, C, WF, WMF> & {
|
|
1250
|
+
readonly __abstract?: never;
|
|
1251
|
+
}
|
|
1252
|
+
: InjectionToken2Base<Token2SingleFactorySlot<F>, MF, C, WF, WMF> & {
|
|
1253
|
+
readonly __abstract: true;
|
|
1254
|
+
for: SpecificFactory;
|
|
1255
|
+
};
|
|
1256
|
+
|
|
1257
|
+
// Defaults to no factory: the speciality-aware overloads of `getInjectionToken2`
|
|
1258
|
+
// below and the DI-machinery per-target factories (e.g.
|
|
1259
|
+
// `instantiationDecoratorToken`'s `.for(target)`) build leaves this way, with
|
|
1260
|
+
// no further `.for()` of their own. A specific token that *does* carry its
|
|
1261
|
+
// own factory (nested specificity) is itself abstract — see the comment on
|
|
1262
|
+
// `InjectionToken2`. Every `.for()` factory must return something with
|
|
1263
|
+
// `speciality` — enforced by `InjectionToken2`'s own SpecificFactory bound
|
|
1264
|
+
// above — so that `buildToken`'s memoization-by-speciality has something to
|
|
1265
|
+
// key on.
|
|
1266
|
+
// INTERNAL, positional — see Token2 above; the speciality-carrying variant.
|
|
1267
|
+
type SpecificToken2<
|
|
1268
|
+
F extends Factory = Factory,
|
|
1269
|
+
// Bound `Factory` for the same reason as on InjectionToken2 above.
|
|
1270
|
+
MF extends Factory = Token2ConsumptionFactorySlot<F>,
|
|
1271
|
+
SpecificFactory extends
|
|
1272
|
+
| undefined
|
|
1273
|
+
| AnySpecificTokenFactory = Token2SpecificFactorySlot<F, undefined>,
|
|
1274
|
+
C extends Cardinality = Token2CardinalitySlot<F>,
|
|
1275
|
+
WF = Token2SingleMetaFactorySlot<F>,
|
|
1276
|
+
WMF = Token2ConsumptionMetaFactorySlot<F, MF>,
|
|
1277
|
+
> = Token2<F, MF, SpecificFactory, C, WF, WMF> & { speciality: any };
|
|
1278
|
+
|
|
1279
|
+
// ---- The public annotation aliases: bag-only ----
|
|
1280
|
+
//
|
|
1281
|
+
// The positional tail is retired: InjectionToken2 and
|
|
1282
|
+
// SpecificInjectionToken2 take exactly one type argument — the named-slots
|
|
1283
|
+
// bag (see InjectionToken2Slots and the mandate rules above). The bare
|
|
1284
|
+
// default is the widest legal bag, so a plain `InjectionToken2` still reads
|
|
1285
|
+
// "some token of some cardinality". Resolution delegates to the internal
|
|
1286
|
+
// positional aliases through the slot extractors, so the public aliases
|
|
1287
|
+
// and the machinery resolve through the one shared body.
|
|
1288
|
+
export type InjectionToken2<
|
|
1289
|
+
Bag extends Token2ExactSlots<Bag> = { singleFactory: Factory },
|
|
1290
|
+
> = Token2<
|
|
1291
|
+
Token2SingleFactorySlot<Bag>,
|
|
1292
|
+
Token2ConsumptionFactorySlot<Bag>,
|
|
1293
|
+
Token2SpecificFactorySlot<Bag, any>,
|
|
1294
|
+
Token2CardinalitySlot<Bag>,
|
|
1295
|
+
Token2SingleMetaFactorySlot<Bag>,
|
|
1296
|
+
Token2ConsumptionMetaFactorySlot<Bag, Token2ConsumptionFactorySlot<Bag>>
|
|
1297
|
+
>;
|
|
1298
|
+
|
|
1299
|
+
// The bag's omitted specificTokenFactory falls back to `undefined` here (a
|
|
1300
|
+
// concrete leaf), mirroring what the speciality-carrying creator overloads
|
|
1301
|
+
// build, rather than InjectionToken2's wide `any`.
|
|
1302
|
+
export type SpecificInjectionToken2<
|
|
1303
|
+
Bag extends Token2ExactSlots<Bag> = { singleFactory: Factory },
|
|
1304
|
+
> = SpecificToken2<
|
|
1305
|
+
Token2SingleFactorySlot<Bag>,
|
|
1306
|
+
Token2ConsumptionFactorySlot<Bag>,
|
|
1307
|
+
Token2SpecificFactorySlot<Bag, undefined>,
|
|
1308
|
+
Token2CardinalitySlot<Bag>,
|
|
1309
|
+
Token2SingleMetaFactorySlot<Bag>,
|
|
1310
|
+
Token2ConsumptionMetaFactorySlot<Bag, Token2ConsumptionFactorySlot<Bag>>
|
|
1311
|
+
>;
|
|
1312
|
+
|
|
1313
|
+
export interface GetInjectionToken2Options<SpecificFactory> {
|
|
1314
|
+
id: string;
|
|
1315
|
+
// Must be pure and deterministic: `.for()` memoizes by specifier and skips
|
|
1316
|
+
// calling the factory again for a specifier it has already seen.
|
|
1317
|
+
specificInjectionTokenFactory?: SpecificFactory;
|
|
1318
|
+
target?: object;
|
|
1319
|
+
maxCacheSize?: number;
|
|
1320
|
+
tags?: string[];
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// Same fields again, minus the factory entirely — for the
|
|
1324
|
+
// `getInjectionToken2<F>(options)(specificInjectionTokenFactory)`
|
|
1325
|
+
// overloads below, where the factory is curried as its own, later call
|
|
1326
|
+
// instead of an options property.
|
|
1327
|
+
export interface GetInjectionToken2OptionsWithoutFactory {
|
|
1328
|
+
id: string;
|
|
1329
|
+
target?: object;
|
|
1330
|
+
maxCacheSize?: number;
|
|
1331
|
+
tags?: string[];
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// ---- Slots-bag creator overloads ----
|
|
1335
|
+
//
|
|
1336
|
+
// The creator takes the named-slots bag as its sole type argument:
|
|
1337
|
+
//
|
|
1338
|
+
// getInjectionToken2<{
|
|
1339
|
+
// singleFactory: <T>(value: T) => T;
|
|
1340
|
+
// manyFactory: <T>(value: T) => T[];
|
|
1341
|
+
// singleMetaFactory: <T>(value: T) => InjectionInstanceWithMeta<T>;
|
|
1342
|
+
// manyMetaFactory: <T>(value: T) => InjectionInstanceWithMeta<T>[];
|
|
1343
|
+
// }>({ id: 'some-id', cardinality: 'zero-or-many' })();
|
|
1344
|
+
//
|
|
1345
|
+
// The slots — the with-meta shapes included — are stamped on the returned
|
|
1346
|
+
// token: they are pure type-level anchors with no runtime counterpart.
|
|
1347
|
+
//
|
|
1348
|
+
// The creator bag carries only what cannot be inferred, so two slots the
|
|
1349
|
+
// annotation bags accept are rejected here: `cardinality` comes from the
|
|
1350
|
+
// runtime option — a per-overload literal discriminant below, exactly like
|
|
1351
|
+
// the positional creator and for the same reason (a C *inferred* from the
|
|
1352
|
+
// option's literal alongside an explicit type argument widens to the whole
|
|
1353
|
+
// union) — and `specificTokenFactory` comes from the trailing call, its
|
|
1354
|
+
// type inferred from the factory value, same as the positional creator.
|
|
1355
|
+
type Token2CreatorExactSlots<Bag> = Token2ExactSlots<Bag> & {
|
|
1356
|
+
cardinality?: never;
|
|
1357
|
+
specificTokenFactory?: never;
|
|
1358
|
+
};
|
|
1359
|
+
|
|
1360
|
+
// The gate each per-cardinality overload puts on its runtime `cardinality`
|
|
1361
|
+
// option: the overload's own literal — unless the bag carries the wrong
|
|
1362
|
+
// member of a consumption-slot pair for that cardinality (a creator bag has
|
|
1363
|
+
// no cardinality key, so this correlation cannot live in the bag
|
|
1364
|
+
// constraint), or a member whose shape disagrees with the cardinality's
|
|
1365
|
+
// consumption shape. For 'zero-or-one' the shape check REQUIRES `undefined`
|
|
1366
|
+
// in the maybeFactory's result, not merely permitting it — the factory is
|
|
1367
|
+
// handed back verbatim, and a 'zero-or-one' token yields nothing when no
|
|
1368
|
+
// implementation is registered.
|
|
1369
|
+
type Token2CreatorCardinality<Bag, C extends Cardinality> = Bag extends {
|
|
1370
|
+
singleFactory: infer F extends Factory;
|
|
1371
|
+
}
|
|
1372
|
+
? [C] extends ['zero-or-one']
|
|
1373
|
+
? Bag extends { manyFactory: unknown } | { manyMetaFactory: unknown }
|
|
1374
|
+
? never
|
|
1375
|
+
: Bag extends { maybeFactory: infer MF extends Factory }
|
|
1376
|
+
? MF extends MaybeResultFactory<F>
|
|
1377
|
+
? undefined extends ReturnType<MF>
|
|
1378
|
+
? C
|
|
1379
|
+
: never
|
|
1380
|
+
: never
|
|
1381
|
+
: C
|
|
1382
|
+
: Bag extends { maybeFactory: unknown } | { maybeMetaFactory: unknown }
|
|
1383
|
+
? never
|
|
1384
|
+
: Bag extends { manyFactory: infer MF extends Factory }
|
|
1385
|
+
? MF extends DefaultConsumptionFactory<C, F>
|
|
1386
|
+
? C
|
|
1387
|
+
: never
|
|
1388
|
+
: C
|
|
1389
|
+
: never;
|
|
1390
|
+
|
|
1391
|
+
// What the bag creator's trailing call hands back: the same resolution the
|
|
1392
|
+
// annotation forms produce (built on InjectionToken2Base directly, since a
|
|
1393
|
+
// computed bag intersection cannot be proven against InjectionToken2's own
|
|
1394
|
+
// self-referential bound), with the overload's cardinality and the trailing
|
|
1395
|
+
// call's factory — or undefined — filling the slots the creator bag does
|
|
1396
|
+
// not carry. An omitted manyFactory defaults to the cardinality's own
|
|
1397
|
+
// consumption shape, like the positional creator's per-cardinality
|
|
1398
|
+
// overloads.
|
|
1399
|
+
// An omitted manyFactory defaults per cardinality: the overload's literal
|
|
1400
|
+
// picks its consumption shape, and the wide-C case (the no-cardinality
|
|
1401
|
+
// speciality overload) falls back to the positional creator's own wide
|
|
1402
|
+
// default, so a leaf declared through either form is the identical type.
|
|
1403
|
+
type Token2FromSlots<Bag, C extends Cardinality, SF> = (
|
|
1404
|
+
Bag extends { manyFactory: infer MF extends Factory }
|
|
1405
|
+
? MF
|
|
1406
|
+
: Bag extends { maybeFactory: infer MF extends Factory }
|
|
1407
|
+
? MF
|
|
1408
|
+
: Cardinality extends C
|
|
1409
|
+
?
|
|
1410
|
+
| ManyFactory<Token2SingleFactorySlot<Bag>>
|
|
1411
|
+
| MaybeResultFactory<Token2SingleFactorySlot<Bag>>
|
|
1412
|
+
: DefaultConsumptionFactory<C, Token2SingleFactorySlot<Bag>>
|
|
1413
|
+
) extends infer MF extends Factory
|
|
1414
|
+
? InjectionToken2Base<
|
|
1415
|
+
Token2SingleFactorySlot<Bag>,
|
|
1416
|
+
MF,
|
|
1417
|
+
C,
|
|
1418
|
+
Token2SingleMetaFactorySlot<Bag>,
|
|
1419
|
+
Token2ConsumptionMetaFactorySlot<Bag, MF>
|
|
1420
|
+
> &
|
|
1421
|
+
(SF extends undefined
|
|
1422
|
+
? { readonly __abstract?: never }
|
|
1423
|
+
: { readonly __abstract: true; for: SF })
|
|
1424
|
+
: never;
|
|
1425
|
+
|
|
1426
|
+
// The trailing call's SF bound is correlated with the bag's own contract —
|
|
1427
|
+
// `.for()` must produce leaves of the very contract the root declares, like
|
|
1428
|
+
// the positional InjectionToken2FactoryCall. Unlike the token ALIASES'
|
|
1429
|
+
// uncorrelated bounds (unprovable through the extractors at generic call
|
|
1430
|
+
// sites), these interfaces only ever instantiate with a concrete Bag — a
|
|
1431
|
+
// creator call site — where the extractor resolves and the correlation is
|
|
1432
|
+
// checkable.
|
|
1433
|
+
// Calling with no arguments gives a token with no `.for` at all — see the
|
|
1434
|
+
// comment on Token2; calling with a factory value keeps that factory's own
|
|
1435
|
+
// generic signature intact in `SF`. Two genuine overloads, not one optional
|
|
1436
|
+
// parameter — an optional slot collapses a generic factory's signature —
|
|
1437
|
+
// and `SF` must NOT have a default: a default doubles as the contextual
|
|
1438
|
+
// signature for the factory argument whenever this call sits inside another
|
|
1439
|
+
// factory that is itself being inferred (a multi-level `.for()` family),
|
|
1440
|
+
// and TS instantiates a generic factory against that non-generic contextual
|
|
1441
|
+
// signature, erasing its type parameters to `any` and every level's
|
|
1442
|
+
// per-specifier narrowing with them.
|
|
1443
|
+
export interface InjectionToken2FactoryCallFromSlots<
|
|
1444
|
+
Bag,
|
|
1445
|
+
C extends Cardinality,
|
|
1446
|
+
> {
|
|
1447
|
+
(): Token2FromSlots<Bag, C, undefined>;
|
|
1448
|
+
|
|
1449
|
+
<
|
|
1450
|
+
SF extends (
|
|
1451
|
+
...args: any[]
|
|
1452
|
+
) => SpecificToken2<Token2SingleFactorySlot<Bag>, any, any, any>,
|
|
1453
|
+
>(
|
|
1454
|
+
specificInjectionTokenFactory: SF,
|
|
1455
|
+
): Token2FromSlots<Bag, C, SF>;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
export interface SpecificInjectionToken2FactoryCallFromSlots<
|
|
1459
|
+
Bag,
|
|
1460
|
+
C extends Cardinality,
|
|
1461
|
+
> {
|
|
1462
|
+
(): Token2FromSlots<Bag, C, undefined> & { speciality: any };
|
|
1463
|
+
|
|
1464
|
+
<
|
|
1465
|
+
SF extends (
|
|
1466
|
+
...args: any[]
|
|
1467
|
+
) => SpecificToken2<Token2SingleFactorySlot<Bag>, any, any, any>,
|
|
1468
|
+
>(
|
|
1469
|
+
specificInjectionTokenFactory: SF,
|
|
1470
|
+
): Token2FromSlots<Bag, C, SF> & { speciality: any };
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1474
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1475
|
+
cardinality: Token2CreatorCardinality<Bag, 'one'>;
|
|
1476
|
+
},
|
|
1477
|
+
): InjectionToken2FactoryCallFromSlots<Bag, 'one'>;
|
|
1478
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1479
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1480
|
+
cardinality: Token2CreatorCardinality<Bag, 'zero-or-one'>;
|
|
1481
|
+
},
|
|
1482
|
+
): InjectionToken2FactoryCallFromSlots<Bag, 'zero-or-one'>;
|
|
1483
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1484
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1485
|
+
cardinality: Token2CreatorCardinality<Bag, 'zero-or-many'>;
|
|
1486
|
+
},
|
|
1487
|
+
): InjectionToken2FactoryCallFromSlots<Bag, 'zero-or-many'>;
|
|
1488
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1489
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1490
|
+
cardinality: Token2CreatorCardinality<Bag, 'one-or-many'>;
|
|
1491
|
+
},
|
|
1492
|
+
): InjectionToken2FactoryCallFromSlots<Bag, 'one-or-many'>;
|
|
1493
|
+
|
|
1494
|
+
// The speciality-carrying bag overloads mirror the positional speciality
|
|
1495
|
+
// overloads, including the no-cardinality variant for leaves that inherit
|
|
1496
|
+
// their arity from the `.for()` of the general token that produced them.
|
|
1497
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1498
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1499
|
+
speciality: any;
|
|
1500
|
+
cardinality: Token2CreatorCardinality<Bag, 'one'>;
|
|
1501
|
+
},
|
|
1502
|
+
): SpecificInjectionToken2FactoryCallFromSlots<Bag, 'one'>;
|
|
1503
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1504
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1505
|
+
speciality: any;
|
|
1506
|
+
cardinality: Token2CreatorCardinality<Bag, 'zero-or-one'>;
|
|
1507
|
+
},
|
|
1508
|
+
): SpecificInjectionToken2FactoryCallFromSlots<Bag, 'zero-or-one'>;
|
|
1509
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1510
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1511
|
+
speciality: any;
|
|
1512
|
+
cardinality: Token2CreatorCardinality<Bag, 'zero-or-many'>;
|
|
1513
|
+
},
|
|
1514
|
+
): SpecificInjectionToken2FactoryCallFromSlots<Bag, 'zero-or-many'>;
|
|
1515
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1516
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1517
|
+
speciality: any;
|
|
1518
|
+
cardinality: Token2CreatorCardinality<Bag, 'one-or-many'>;
|
|
1519
|
+
},
|
|
1520
|
+
): SpecificInjectionToken2FactoryCallFromSlots<Bag, 'one-or-many'>;
|
|
1521
|
+
export function getInjectionToken2<Bag extends Token2CreatorExactSlots<Bag>>(
|
|
1522
|
+
options: GetInjectionToken2OptionsWithoutFactory & {
|
|
1523
|
+
speciality: any;
|
|
1524
|
+
cardinality?: undefined;
|
|
1525
|
+
},
|
|
1526
|
+
): SpecificInjectionToken2FactoryCallFromSlots<Bag, Cardinality>;
|
|
1527
|
+
|
|
1528
|
+
// ---- Per-cardinality annotation aliases ----
|
|
1529
|
+
//
|
|
1530
|
+
// Hand-written annotations would otherwise have to spell the middle slots to
|
|
1531
|
+
// reach the trailing cardinality. `undefined` for the factory slot, not
|
|
1532
|
+
// `any`: these annotate a concrete (non-abstract, no `.for()`) token of a
|
|
1533
|
+
// given cardinality — the shape a plain `getInjectionToken2(options)()` call
|
|
1534
|
+
// produces, e.g. the tokens exported by the extension-for-mobx package. `any`
|
|
1535
|
+
// would make the alias satisfy *both* branches of `InjectionToken2`'s
|
|
1536
|
+
// conditional simultaneously, which fails narrow positions that require
|
|
1537
|
+
// `SpecificFactory extends undefined` (e.g. `di.inject`/`di.inject2`) — see
|
|
1538
|
+
// the comment on `InjectionToken2` for why a wide position needs `any`
|
|
1539
|
+
// specifically, not this alias.
|
|
1540
|
+
|
|
1541
|
+
export type SingleInjectionToken2<
|
|
1542
|
+
F extends Factory,
|
|
1543
|
+
MF extends AnyConsumptionFactory<F> = ManyFactory<F>,
|
|
1544
|
+
> = Token2<F, MF, undefined, 'one'>;
|
|
1545
|
+
|
|
1546
|
+
export type MaybeInjectionToken2<
|
|
1547
|
+
F extends Factory,
|
|
1548
|
+
MF extends AnyConsumptionFactory<F> = MaybeResultFactory<F>,
|
|
1549
|
+
> = Token2<F, MF, undefined, 'zero-or-one'>;
|
|
1550
|
+
|
|
1551
|
+
export type ManyInjectionToken2<
|
|
1552
|
+
F extends Factory,
|
|
1553
|
+
MF extends AnyConsumptionFactory<F> = ManyFactory<F>,
|
|
1554
|
+
> = Token2<F, MF, undefined, 'zero-or-many'>;
|
|
1555
|
+
|
|
1556
|
+
export type NonEmptyManyInjectionToken2<
|
|
1557
|
+
F extends Factory,
|
|
1558
|
+
MF extends AnyConsumptionFactory<F> = NonEmptyManyFactory<F>,
|
|
1559
|
+
> = Token2<F, MF, undefined, 'one-or-many'>;
|
|
1560
|
+
|
|
1561
|
+
// "Any DI alias" — any injectable or any token (v1 or v2). InjectableBunch
|
|
1562
|
+
// is excluded because it's a registration bundle, not a single alias.
|
|
1563
|
+
export type Alias1 =
|
|
1564
|
+
| Injectable<any, any, any>
|
|
1565
|
+
| InjectionToken<any, any, any>;
|
|
1566
|
+
|
|
1567
|
+
export type Alias2<F extends Factory = Factory> =
|
|
1568
|
+
| Injectable2<F>
|
|
1569
|
+
| Token2<F, any, any>;
|
|
1570
|
+
|
|
1571
|
+
export type Alias = Alias1 | Alias2;
|
|
1572
|
+
|
|
1573
|
+
// ---- DiContainerForInjection2 (new-style minimalDi) ----
|
|
1574
|
+
|
|
1575
|
+
export interface DiContainerForInjection2 {
|
|
1576
|
+
inject: Inject2;
|
|
1577
|
+
injectMany: InjectMany2;
|
|
1578
|
+
injectMaybe: InjectMaybe2;
|
|
1579
|
+
injectWithMeta: InjectWithMeta2;
|
|
1580
|
+
injectManyWithMeta: InjectManyWithMeta2;
|
|
1581
|
+
injectMaybeWithMeta: InjectMaybeWithMeta2;
|
|
1582
|
+
|
|
1583
|
+
register(...injectables: (Alias | InjectableBunch)[]): void;
|
|
1584
|
+
|
|
1585
|
+
deregister(...injectables: (Alias | InjectableBunch)[]): void;
|
|
1586
|
+
|
|
1587
|
+
sourceNamespace: string | undefined;
|
|
1588
|
+
|
|
1589
|
+
purge: Purge;
|
|
1590
|
+
|
|
1591
|
+
hasRegistrations: HasRegistrations2;
|
|
1592
|
+
|
|
1593
|
+
registeredInLocalScope: (alias: Alias) => boolean;
|
|
1594
|
+
|
|
1595
|
+
registeredInLocalScopeSubtree: (alias: Alias) => boolean;
|
|
1596
|
+
|
|
1597
|
+
getNumberOfRegistrations: (alias: Alias) => number;
|
|
1598
|
+
|
|
1599
|
+
getNumberOfInstances: () => Record<string, number>;
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
export interface HasRegistrations2 {
|
|
1603
|
+
<F extends Factory>(
|
|
1604
|
+
alias: Injectable2<F> | Token2<F, any, any, any>,
|
|
1605
|
+
): boolean;
|
|
1606
|
+
<I extends TI, TI, P>(
|
|
1607
|
+
alias: Injectable<I, TI, P> | InjectionToken<TI, P>,
|
|
1608
|
+
): boolean;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// Factory-returning injectMaybe — accepts only tokens declared 'zero-or-one'
|
|
1612
|
+
// and returns the token's maybe-factory verbatim, so a generic factory keeps
|
|
1613
|
+
// its generic. On the root container this is `injectMaybe2`, alongside the
|
|
1614
|
+
// other factory-returning members; inside an `instantiate` it is `injectMaybe`,
|
|
1615
|
+
// where every member is factory-returning.
|
|
1616
|
+
export interface InjectMaybe2 {
|
|
1617
|
+
<F extends Factory, MF extends (...args: Parameters<F>) => ReturnType<F> | undefined>(
|
|
1618
|
+
alias: Token2<F, MF, undefined, 'zero-or-one'>,
|
|
1619
|
+
): MF;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// The with-meta sibling of injectMaybe2: returns the token's with-meta
|
|
1623
|
+
// consumption template verbatim — for a 'zero-or-one' token that defaults to
|
|
1624
|
+
// `(...args) => InjectionInstanceWithMeta<R> | undefined`, derived from the
|
|
1625
|
+
// maybe-factory (the WMF slot is cardinality-shaped, like the MF slot it
|
|
1626
|
+
// derives from).
|
|
1627
|
+
export interface InjectMaybeWithMeta2 {
|
|
1628
|
+
<F extends Factory, WMF>(
|
|
1629
|
+
alias: Token2<F, any, undefined, 'zero-or-one', any, WMF>,
|
|
1630
|
+
): WMF;
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
// Factory-returning injectMany — v2 returns the token's many-factory (generics
|
|
1634
|
+
// preserved), v1 returns a synthesized many-factory. Only the two
|
|
1635
|
+
// many-cardinalities are accepted.
|
|
1636
|
+
export interface InjectMany2 {
|
|
1637
|
+
<F extends Factory, MF extends ManyFactory<F>>(
|
|
1638
|
+
alias: Token2<F, MF, any, 'zero-or-many' | 'one-or-many'>,
|
|
1639
|
+
): MF;
|
|
1640
|
+
<I>(alias: InjectionToken<I>): () => I[];
|
|
1641
|
+
<I, P>(alias: InjectionToken<I, P>): (...params: P extends any[] ? P : [P]) => I[];
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
// Helper: transforms a factory's return type to InjectionInstanceWithMeta<R>
|
|
1645
|
+
// For non-generic factories this auto-derives correctly.
|
|
1646
|
+
// For generic factories, TypeScript loses the generic (returns unknown).
|
|
1647
|
+
// In that case, users should use inject/injectMany (non-meta) for generic types.
|
|
1648
|
+
export type ToWithMetaFactory<F> = F extends (...args: infer P) => infer R
|
|
1649
|
+
? (...args: P) => InjectionInstanceWithMeta<R>
|
|
1650
|
+
: never;
|
|
1651
|
+
|
|
1652
|
+
export type ToWithMetaManyFactory<F> = F extends (...args: infer P) => infer R
|
|
1653
|
+
? (...args: P) => InjectionInstanceWithMeta<R>[]
|
|
1654
|
+
: never;
|
|
1655
|
+
|
|
1656
|
+
export interface InjectWithMeta2 {
|
|
1657
|
+
<F extends Factory>(alias: Injectable2<F>): ToWithMetaFactory<F>;
|
|
1658
|
+
// Returns the token's with-meta template verbatim, so an explicitly
|
|
1659
|
+
// declared generic shape survives; the default is ToWithMetaFactory<F>.
|
|
1660
|
+
<F extends Factory, WF>(
|
|
1661
|
+
alias: Token2<F, any, undefined, 'one', WF>,
|
|
1662
|
+
): WF;
|
|
1663
|
+
<I>(alias: Injectable<I, any> | InjectionToken<I>): () => InjectionInstanceWithMeta<I>;
|
|
1664
|
+
<I, P>(alias: Injectable<I, any, P> | InjectionToken<I, P>): (...params: P extends any[] ? P : [P]) => InjectionInstanceWithMeta<I>;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
export interface InjectManyWithMeta2 {
|
|
1668
|
+
// Returns the token's with-meta many-template verbatim, so an explicitly
|
|
1669
|
+
// declared generic shape survives; the default derives from the token's
|
|
1670
|
+
// many-factory, which is what lets a custom multi-factory narrow it.
|
|
1671
|
+
<F extends Factory, WMF>(
|
|
1672
|
+
alias: Token2<
|
|
1673
|
+
F,
|
|
1674
|
+
any,
|
|
1675
|
+
any,
|
|
1676
|
+
'zero-or-many' | 'one-or-many',
|
|
1677
|
+
any,
|
|
1678
|
+
WMF
|
|
1679
|
+
>,
|
|
1680
|
+
): WMF;
|
|
1681
|
+
<I>(alias: InjectionToken<I>): () => InjectionInstanceWithMeta<I>[];
|
|
1682
|
+
<I, P>(alias: InjectionToken<I, P>): (...params: P extends any[] ? P : [P]) => InjectionInstanceWithMeta<I>[];
|
|
1683
|
+
}
|