@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.
Files changed (5) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +809 -0
  3. package/dist/index.js +2182 -0
  4. package/index.d.ts +1683 -0
  5. 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
+ }