@vercube/di 1.2.1 → 1.3.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/dist/index.d.mts +220 -59
- package/dist/index.mjs +281 -12
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* This is base class for all property decorators used in application. Decorator class must extend this one.
|
|
4
4
|
* This class instance is created using IOC container so you can @Inject() things here.
|
|
5
5
|
*/
|
|
6
|
-
declare abstract class BaseDecorator<T = any, P = any> {
|
|
6
|
+
export declare abstract class BaseDecorator<T = any, P = any> {
|
|
7
7
|
/** Holds options object that is passed as 2nd argument in createDecorator() factory */
|
|
8
8
|
options: T;
|
|
9
9
|
/** Holds class instance that is decorated */
|
|
@@ -29,11 +29,11 @@ declare abstract class BaseDecorator<T = any, P = any> {
|
|
|
29
29
|
}
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/Domain/ContainerEvents.d.ts
|
|
32
|
-
type OnExpandedEvent = (serviceKeys: IOC.ServiceKey[]) => void;
|
|
32
|
+
export type OnExpandedEvent = (serviceKeys: IOC.ServiceKey[]) => void;
|
|
33
33
|
/**
|
|
34
34
|
* This class allows for container to listen on various IOC events.
|
|
35
35
|
*/
|
|
36
|
-
declare class ContainerEvents {
|
|
36
|
+
export declare class ContainerEvents {
|
|
37
37
|
private fOnExpanded;
|
|
38
38
|
/**
|
|
39
39
|
* Registers to container "onExpanded" event.
|
|
@@ -52,7 +52,7 @@ declare class ContainerEvents {
|
|
|
52
52
|
* This is new implementation of IOC Container. It mimics Inversify.js container a little bit but its
|
|
53
53
|
* simpler and (probably) more performant on larger scales.
|
|
54
54
|
*/
|
|
55
|
-
declare class Container {
|
|
55
|
+
export declare class Container {
|
|
56
56
|
protected fContext: string | undefined;
|
|
57
57
|
protected fLocked: boolean;
|
|
58
58
|
protected fDefaultParams: IOC.ContainerParams;
|
|
@@ -61,6 +61,7 @@ declare class Container {
|
|
|
61
61
|
protected fSingletonInstances: Map<IOC.ServiceKey, IOC.Instance>;
|
|
62
62
|
protected fInjectMethod: IOC.InjectMethod;
|
|
63
63
|
protected fContainerEvents: ContainerEvents;
|
|
64
|
+
protected fRevision: number;
|
|
64
65
|
/**
|
|
65
66
|
* Constructor for container.
|
|
66
67
|
* @param params initial params for container
|
|
@@ -92,6 +93,13 @@ declare class Container {
|
|
|
92
93
|
* @returns {string} the context of the container
|
|
93
94
|
*/
|
|
94
95
|
get context(): string | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* Counter that changes whenever a binding is added, replaced or first
|
|
98
|
+
* instantiated - that is, whenever a description of this container would
|
|
99
|
+
* come out different.
|
|
100
|
+
* @returns {number} the current revision
|
|
101
|
+
*/
|
|
102
|
+
get revision(): number;
|
|
95
103
|
/**
|
|
96
104
|
* Binds particular key to container in singleton scope. Multiple queries/injects of this
|
|
97
105
|
* service will always return the same instance.
|
|
@@ -230,7 +238,7 @@ declare class Container {
|
|
|
230
238
|
/**
|
|
231
239
|
* This module exports various types used in IOC system.
|
|
232
240
|
*/
|
|
233
|
-
declare namespace IOC {
|
|
241
|
+
export declare namespace IOC {
|
|
234
242
|
/**
|
|
235
243
|
* This is a key that we use to identify service. Symbol is new way of doing it, however we
|
|
236
244
|
* also keep standard/abstract classes for backward compatability.
|
|
@@ -337,7 +345,7 @@ declare namespace IOC {
|
|
|
337
345
|
* @param key key to inject
|
|
338
346
|
* @returns decorator
|
|
339
347
|
*/
|
|
340
|
-
declare function Inject(key: IOC.ServiceKey): Function;
|
|
348
|
+
export declare function Inject(key: IOC.ServiceKey): Function;
|
|
341
349
|
//#endregion
|
|
342
350
|
//#region src/Decorators/InjectOptional.d.ts
|
|
343
351
|
/**
|
|
@@ -345,7 +353,7 @@ declare function Inject(key: IOC.ServiceKey): Function;
|
|
|
345
353
|
* @param key key to inject
|
|
346
354
|
* @returns decorator
|
|
347
355
|
*/
|
|
348
|
-
declare function InjectOptional(key: IOC.ServiceKey): Function;
|
|
356
|
+
export declare function InjectOptional(key: IOC.ServiceKey): Function;
|
|
349
357
|
//#endregion
|
|
350
358
|
//#region src/Decorators/Init.d.ts
|
|
351
359
|
/**
|
|
@@ -353,7 +361,7 @@ declare function InjectOptional(key: IOC.ServiceKey): Function;
|
|
|
353
361
|
* Use this decorator to run initialization logic for runtime-loaded container dependencies.
|
|
354
362
|
* @returns {Function} Decorator function that creates an InitDecorator instance
|
|
355
363
|
*/
|
|
356
|
-
declare function Init(): Function;
|
|
364
|
+
export declare function Init(): Function;
|
|
357
365
|
//#endregion
|
|
358
366
|
//#region src/Decorators/Destroy.d.ts
|
|
359
367
|
/**
|
|
@@ -372,7 +380,7 @@ declare function Init(): Function;
|
|
|
372
380
|
* ```
|
|
373
381
|
* @returns {Function} Decorator function that creates a DestroyDecorator instance
|
|
374
382
|
*/
|
|
375
|
-
declare function Destroy(): Function;
|
|
383
|
+
export declare function Destroy(): Function;
|
|
376
384
|
//#endregion
|
|
377
385
|
//#region src/Decorators/Injectable.d.ts
|
|
378
386
|
/**
|
|
@@ -382,48 +390,95 @@ declare function Destroy(): Function;
|
|
|
382
390
|
* This decorator is intentionally a no-op at runtime - it exists solely as a
|
|
383
391
|
* compile-time marker that the build-time scanner can detect via AST analysis.
|
|
384
392
|
*/
|
|
385
|
-
declare function Injectable(): ClassDecorator;
|
|
393
|
+
export declare function Injectable(): ClassDecorator;
|
|
386
394
|
//#endregion
|
|
387
|
-
//#region src/
|
|
388
|
-
/**
|
|
389
|
-
* Global property name for the IOC devtools hook.
|
|
390
|
-
*/
|
|
391
|
-
declare const IOC_DEVTOOLS_HOOK_KEY = "__VERCUBE_DEVTOOLS_HOOK__";
|
|
392
|
-
/**
|
|
393
|
-
* A single service resolution record emitted when the container constructs an instance.
|
|
394
|
-
*/
|
|
395
|
-
interface IOCResolveRecord {
|
|
396
|
-
/** Service key that was resolved. */
|
|
397
|
-
key: IOC.ServiceKey;
|
|
398
|
-
/** Human readable name of the service key. */
|
|
399
|
-
name: string;
|
|
400
|
-
/** Factory type used to build the instance. */
|
|
401
|
-
type: IOC.ServiceFactoryType;
|
|
402
|
-
/** Context of the container that performed the resolution. */
|
|
403
|
-
context: string | undefined;
|
|
404
|
-
/** Timestamp (ms) before construction. */
|
|
405
|
-
start: number;
|
|
406
|
-
/** Timestamp (ms) after construction and injection. */
|
|
407
|
-
end: number;
|
|
408
|
-
}
|
|
395
|
+
//#region src/Types/DescribeTypes.d.ts
|
|
409
396
|
/**
|
|
410
|
-
*
|
|
397
|
+
* Types describing a container's contents.
|
|
398
|
+
*
|
|
399
|
+
* The description is built from binding metadata only. Nothing is resolved, so
|
|
400
|
+
* inspecting a container never constructs a service the application itself
|
|
401
|
+
* never asked for.
|
|
411
402
|
*/
|
|
412
|
-
|
|
413
|
-
/**
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
|
|
403
|
+
export declare namespace Describe {
|
|
404
|
+
/** How a binding produces its value. */
|
|
405
|
+
type ServiceKind = 'singleton' | 'transient' | 'instance';
|
|
406
|
+
/** One `@Inject` / `@InjectOptional` declaration. */
|
|
407
|
+
interface Dependency {
|
|
408
|
+
/** Id of the service this resolves to, or `unbound:Name` when nothing is bound. */
|
|
409
|
+
id: string;
|
|
410
|
+
/** Display name of the dependency key. */
|
|
411
|
+
name: string;
|
|
412
|
+
/** Property the dependency is injected into. */
|
|
413
|
+
property: string;
|
|
414
|
+
/** Whether the declaration was `@InjectOptional`. */
|
|
415
|
+
optional: boolean;
|
|
416
|
+
/** Whether something is actually bound under the key. */
|
|
417
|
+
bound: boolean;
|
|
418
|
+
}
|
|
419
|
+
/** A service in the container. */
|
|
420
|
+
interface ServiceNode {
|
|
421
|
+
/** Unique id. The display name, suffixed with `#n` when names collide. */
|
|
422
|
+
id: string;
|
|
423
|
+
/** Display name of the binding key. */
|
|
424
|
+
name: string;
|
|
425
|
+
kind: ServiceKind;
|
|
426
|
+
/**
|
|
427
|
+
* Category of the service.
|
|
428
|
+
*
|
|
429
|
+
* The container itself can only tell a class from a plain value; anything
|
|
430
|
+
* finer - controller, middleware, plugin - comes from the `annotate`
|
|
431
|
+
* callback, because only the framework layer knows what those are.
|
|
432
|
+
*/
|
|
433
|
+
role: string;
|
|
434
|
+
/** Implementation class name, when it differs from the key. */
|
|
435
|
+
implementation: string | null;
|
|
436
|
+
/** Whether an instance already exists. */
|
|
437
|
+
instantiated: boolean;
|
|
438
|
+
/** Whether the key is a symbol. */
|
|
439
|
+
symbol: boolean;
|
|
440
|
+
dependencies: Dependency[];
|
|
441
|
+
/** Number of services depending on this one. */
|
|
442
|
+
dependents: number;
|
|
443
|
+
/** Extra fields contributed by `annotate`. */
|
|
444
|
+
[extra: string]: unknown;
|
|
445
|
+
}
|
|
446
|
+
/** A directed dependency edge. */
|
|
447
|
+
interface ServiceEdge {
|
|
448
|
+
from: string;
|
|
449
|
+
to: string;
|
|
450
|
+
property: string;
|
|
451
|
+
optional: boolean;
|
|
452
|
+
}
|
|
453
|
+
/** Everything known about a container's bindings. */
|
|
454
|
+
interface ContainerDescription {
|
|
455
|
+
/** Container label, when one was given. */
|
|
456
|
+
context?: string;
|
|
457
|
+
nodes: ServiceNode[];
|
|
458
|
+
edges: ServiceEdge[];
|
|
459
|
+
/** Dependency cycles, each as the ordered ids forming the loop. */
|
|
460
|
+
cycles: string[][];
|
|
461
|
+
/** Number of services that were never instantiated. */
|
|
462
|
+
unusedCount: number;
|
|
463
|
+
}
|
|
464
|
+
/** What `annotate` is told about the node it is classifying. */
|
|
465
|
+
interface AnnotateContext {
|
|
466
|
+
key: IOC.ServiceKey;
|
|
467
|
+
def: Readonly<IOC.ServiceDef>;
|
|
468
|
+
/** Constructor behind the binding, or null for plain values. */
|
|
469
|
+
ctor: (Function & {
|
|
470
|
+
prototype: unknown;
|
|
471
|
+
}) | null;
|
|
472
|
+
}
|
|
473
|
+
/** Options for {@link describeContainer}. */
|
|
474
|
+
interface Options {
|
|
475
|
+
/**
|
|
476
|
+
* Refines a node before it is added to the description. Typically sets
|
|
477
|
+
* `role` and attaches framework-specific fields.
|
|
478
|
+
*/
|
|
479
|
+
annotate?: (node: ServiceNode, context: AnnotateContext) => void;
|
|
480
|
+
}
|
|
417
481
|
}
|
|
418
|
-
/**
|
|
419
|
-
* Installs or replaces the devtools hook.
|
|
420
|
-
* @param hook hook implementation, or `undefined` to uninstall
|
|
421
|
-
*/
|
|
422
|
-
declare function setIOCDevtoolsHook(hook: IOCDevtoolsHook | undefined): void;
|
|
423
|
-
/**
|
|
424
|
-
* Returns the currently installed devtools hook, if any.
|
|
425
|
-
*/
|
|
426
|
-
declare function getIOCDevtoolsHook(): IOCDevtoolsHook | undefined;
|
|
427
482
|
//#endregion
|
|
428
483
|
//#region src/Domain/Engine.d.ts
|
|
429
484
|
/**
|
|
@@ -468,13 +523,119 @@ declare function getDeps(instance: IOC.Instance): IClassDep[];
|
|
|
468
523
|
* @param method inject method, "lazy" queries dep during property access while "static" injects during class creation
|
|
469
524
|
*/
|
|
470
525
|
declare function injectDeps(container: Container, instance: IOC.Instance, method: IOC.InjectMethod): void;
|
|
471
|
-
declare const IOCEngine: {
|
|
526
|
+
export declare const IOCEngine: {
|
|
472
527
|
registerInject: typeof registerInject;
|
|
473
528
|
getEntryForClass: typeof getEntryForClass;
|
|
474
529
|
injectDeps: typeof injectDeps;
|
|
475
530
|
getDeps: typeof getDeps;
|
|
476
531
|
};
|
|
477
532
|
//#endregion
|
|
533
|
+
//#region src/Domain/Describe.d.ts
|
|
534
|
+
/**
|
|
535
|
+
* Produces a readable label for any service key.
|
|
536
|
+
*
|
|
537
|
+
* @param key - Service key from a `bind*` call
|
|
538
|
+
* @returns Display name for the key
|
|
539
|
+
*/
|
|
540
|
+
export declare function describeKey(key: IOC.ServiceKey): string;
|
|
541
|
+
/**
|
|
542
|
+
* Names the value a key was bound to, or `null` when it adds no information.
|
|
543
|
+
*
|
|
544
|
+
* @param key - Service key
|
|
545
|
+
* @param value - Bound implementation or instance
|
|
546
|
+
* @returns Implementation name, or null
|
|
547
|
+
*/
|
|
548
|
+
export declare function describeImplementation(key: IOC.ServiceKey, value: unknown): string | null;
|
|
549
|
+
/**
|
|
550
|
+
* Resolves the class constructor behind a service definition.
|
|
551
|
+
*
|
|
552
|
+
* @param def - Service definition from the container
|
|
553
|
+
* @returns The constructor, or null for plain values
|
|
554
|
+
*/
|
|
555
|
+
export declare function resolveConstructor(def: Readonly<IOC.ServiceDef>): (Function & {
|
|
556
|
+
prototype: unknown;
|
|
557
|
+
}) | null;
|
|
558
|
+
/**
|
|
559
|
+
* Collects every injection declaration of a class, inherited ones included.
|
|
560
|
+
*
|
|
561
|
+
* @param ctor - Class constructor to inspect
|
|
562
|
+
* @returns Dependency declarations, nearest prototype first
|
|
563
|
+
*/
|
|
564
|
+
export declare function collectClassDeps(ctor: (Function & {
|
|
565
|
+
prototype: unknown;
|
|
566
|
+
}) | null): IClassDep[];
|
|
567
|
+
/**
|
|
568
|
+
* Maps a binding's factory type onto its description kind.
|
|
569
|
+
*
|
|
570
|
+
* @param type - Factory type recorded by the container
|
|
571
|
+
* @returns The matching kind
|
|
572
|
+
*/
|
|
573
|
+
export declare function toServiceKind(type: IOC.ServiceFactoryType): Describe.ServiceKind;
|
|
574
|
+
/**
|
|
575
|
+
* Builds a serialisable description of a container.
|
|
576
|
+
*
|
|
577
|
+
* Reads `container.services`, which is binding metadata, never
|
|
578
|
+
* `getAllServices()`, which would construct every registered service. An
|
|
579
|
+
* inspector that instantiates what it inspects is not an inspector.
|
|
580
|
+
*
|
|
581
|
+
* @param container - The container to describe
|
|
582
|
+
* @param options - Optional annotation callback
|
|
583
|
+
* @returns Nodes, edges, cycles and the unused-service count
|
|
584
|
+
*/
|
|
585
|
+
export declare function describeContainer(container: Container, options?: Describe.Options): Describe.ContainerDescription;
|
|
586
|
+
//#endregion
|
|
587
|
+
//#region src/Domain/DevtoolsHook.d.ts
|
|
588
|
+
/**
|
|
589
|
+
* Global property name for the IOC observer registry.
|
|
590
|
+
*/
|
|
591
|
+
export declare const IOC_DEVTOOLS_HOOK_KEY = "__VERCUBE_DEVTOOLS_HOOK__";
|
|
592
|
+
/**
|
|
593
|
+
* A single service resolution record emitted when the container constructs an instance.
|
|
594
|
+
*/
|
|
595
|
+
export interface IOCResolveRecord {
|
|
596
|
+
/** Service key that was resolved. */
|
|
597
|
+
key: IOC.ServiceKey;
|
|
598
|
+
/** Human readable name of the service key. */
|
|
599
|
+
name: string;
|
|
600
|
+
/** Factory type used to build the instance. */
|
|
601
|
+
type: IOC.ServiceFactoryType;
|
|
602
|
+
/** Context of the container that performed the resolution. */
|
|
603
|
+
context: string | undefined;
|
|
604
|
+
/** Timestamp (ms) before construction. */
|
|
605
|
+
start: number;
|
|
606
|
+
/** Timestamp (ms) after construction and injection. */
|
|
607
|
+
end: number;
|
|
608
|
+
}
|
|
609
|
+
/**
|
|
610
|
+
* Optional hook for observing the IOC container.
|
|
611
|
+
*/
|
|
612
|
+
export interface IOCDevtoolsHook {
|
|
613
|
+
/** Called once for every {@link Container} instance after construction. */
|
|
614
|
+
onContainerCreated?: (container: Container) => void;
|
|
615
|
+
/** Called whenever the container constructs a new service instance. */
|
|
616
|
+
onResolved?: (record: IOCResolveRecord) => void;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Installs an observer.
|
|
620
|
+
*
|
|
621
|
+
* @param hook - The observer to install
|
|
622
|
+
* @returns A function that removes it again
|
|
623
|
+
*/
|
|
624
|
+
export declare function addIOCDevtoolsHook(hook: IOCDevtoolsHook): () => void;
|
|
625
|
+
/**
|
|
626
|
+
* Replaces every installed observer with `hook`, or removes them all.
|
|
627
|
+
*
|
|
628
|
+
* @param hook - The observer to install, or `undefined` to uninstall everything
|
|
629
|
+
* @deprecated Use {@link addIOCDevtoolsHook}, which does not evict other observers.
|
|
630
|
+
*/
|
|
631
|
+
export declare function setIOCDevtoolsHook(hook: IOCDevtoolsHook | undefined): void;
|
|
632
|
+
/**
|
|
633
|
+
* Returns the fan-out hook the container calls into.
|
|
634
|
+
*
|
|
635
|
+
* @returns The registry, or undefined when no observer is installed
|
|
636
|
+
*/
|
|
637
|
+
export declare function getIOCDevtoolsHook(): IOCDevtoolsHook | undefined;
|
|
638
|
+
//#endregion
|
|
478
639
|
//#region src/Utils/Utils.d.ts
|
|
479
640
|
/**
|
|
480
641
|
* This function generates new service key for particular service. Providing
|
|
@@ -484,18 +645,18 @@ declare const IOCEngine: {
|
|
|
484
645
|
* @param name name of the service
|
|
485
646
|
* @returns unique service identity
|
|
486
647
|
*/
|
|
487
|
-
declare function Identity(name: string): IOC.Identity;
|
|
648
|
+
export declare function Identity(name: string): IOC.Identity;
|
|
488
649
|
/**
|
|
489
650
|
* A simple type of constructor for class "T"
|
|
490
651
|
*/
|
|
491
|
-
interface IClassType<T> {
|
|
652
|
+
export interface IClassType<T> {
|
|
492
653
|
new (): T;
|
|
493
654
|
}
|
|
494
655
|
/**
|
|
495
656
|
* Holds decorator entry struct. It is saved in __decorators array for every class and holds
|
|
496
657
|
* various informations.
|
|
497
658
|
*/
|
|
498
|
-
interface IDecoratorEntry {
|
|
659
|
+
export interface IDecoratorEntry {
|
|
499
660
|
classType: any;
|
|
500
661
|
params: any;
|
|
501
662
|
target: any;
|
|
@@ -507,11 +668,11 @@ interface IDecoratorEntry {
|
|
|
507
668
|
* array that holds information about all decorators used in this class. It will be used later for
|
|
508
669
|
* turning those declarations for real code.
|
|
509
670
|
*/
|
|
510
|
-
interface IDecoratedPrototype {
|
|
671
|
+
export interface IDecoratedPrototype {
|
|
511
672
|
__decorators?: IDecoratorEntry[];
|
|
512
673
|
__metadata?: any;
|
|
513
674
|
}
|
|
514
|
-
interface IDecoratedInstance {
|
|
675
|
+
export interface IDecoratedInstance {
|
|
515
676
|
__decoratorInstances?: BaseDecorator<any>[];
|
|
516
677
|
}
|
|
517
678
|
/**
|
|
@@ -521,14 +682,14 @@ interface IDecoratedInstance {
|
|
|
521
682
|
* @param params custom options object that will be availalbe in "options" property of decorator class
|
|
522
683
|
* @return ES6 decorator function
|
|
523
684
|
*/
|
|
524
|
-
declare function createDecorator<P, T extends BaseDecorator<P>>(decoratorClass: IClassType<T>, params: P): Function;
|
|
685
|
+
export declare function createDecorator<P, T extends BaseDecorator<P>>(decoratorClass: IClassType<T>, params: P): Function;
|
|
525
686
|
/**
|
|
526
687
|
* This function initializes all registered decorators on particular instance. It must be called in order
|
|
527
688
|
* for decorator code work in particular class.
|
|
528
689
|
* @param target class instance to sue
|
|
529
690
|
* @param container IOC container for context
|
|
530
691
|
*/
|
|
531
|
-
declare function initializeDecorators(target: IDecoratedInstance, container: Container): void;
|
|
692
|
+
export declare function initializeDecorators(target: IDecoratedInstance, container: Container): void;
|
|
532
693
|
/**
|
|
533
694
|
* This function releases all decorators applied to target instance by calling their .destroyed()
|
|
534
695
|
* method. Its used to provide a way for cleanup for decorators. @see BaseDecorator.destroy()
|
|
@@ -536,18 +697,18 @@ declare function initializeDecorators(target: IDecoratedInstance, container: Con
|
|
|
536
697
|
* @param target instance of class that should have decorators cleaned up
|
|
537
698
|
* @param container ioc container
|
|
538
699
|
*/
|
|
539
|
-
declare function destroyDecorators(target: IDecoratedInstance, container: Container): void;
|
|
700
|
+
export declare function destroyDecorators(target: IDecoratedInstance, container: Container): void;
|
|
540
701
|
/**
|
|
541
702
|
* This function is responsible for preparing IOC container to work with all decorators. It simply
|
|
542
703
|
* initializes all decorators on all services registered.
|
|
543
704
|
* @param container IOC container
|
|
544
705
|
*/
|
|
545
|
-
declare function initializeContainer(container: Container): void;
|
|
706
|
+
export declare function initializeContainer(container: Container): void;
|
|
546
707
|
/**
|
|
547
708
|
* This function is responsible for preparing IOC container to work with all decorators. It simply
|
|
548
709
|
* initializes all decorators on all services registered.
|
|
549
710
|
* @param container IOC container
|
|
550
711
|
*/
|
|
551
|
-
declare function destroyContainer(container: Container): void;
|
|
712
|
+
export declare function destroyContainer(container: Container): void;
|
|
552
713
|
//#endregion
|
|
553
|
-
export {
|
|
714
|
+
export type { IClassDep, IClassMapEntry };
|
package/dist/index.mjs
CHANGED
|
@@ -154,23 +154,64 @@ var ContainerEvents = class {
|
|
|
154
154
|
//#endregion
|
|
155
155
|
//#region src/Domain/DevtoolsHook.ts
|
|
156
156
|
/**
|
|
157
|
-
* Global property name for the IOC
|
|
157
|
+
* Global property name for the IOC observer registry.
|
|
158
158
|
*/
|
|
159
159
|
const IOC_DEVTOOLS_HOOK_KEY = "__VERCUBE_DEVTOOLS_HOOK__";
|
|
160
|
-
let devtoolsHook = globalThis[IOC_DEVTOOLS_HOOK_KEY];
|
|
161
160
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
161
|
+
* Returns the global registry, creating it on first use.
|
|
162
|
+
*
|
|
163
|
+
* @returns The registry
|
|
164
|
+
*/
|
|
165
|
+
function registry() {
|
|
166
|
+
let current = globalThis[IOC_DEVTOOLS_HOOK_KEY];
|
|
167
|
+
if (!current) {
|
|
168
|
+
const observers = [];
|
|
169
|
+
current = {
|
|
170
|
+
observers,
|
|
171
|
+
onContainerCreated(container) {
|
|
172
|
+
for (const observer of observers) observer.onContainerCreated?.(container);
|
|
173
|
+
},
|
|
174
|
+
onResolved(record) {
|
|
175
|
+
for (const observer of observers) observer.onResolved?.(record);
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
globalThis[IOC_DEVTOOLS_HOOK_KEY] = current;
|
|
179
|
+
}
|
|
180
|
+
return current;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Installs an observer.
|
|
184
|
+
*
|
|
185
|
+
* @param hook - The observer to install
|
|
186
|
+
* @returns A function that removes it again
|
|
187
|
+
*/
|
|
188
|
+
function addIOCDevtoolsHook(hook) {
|
|
189
|
+
const observers = registry().observers;
|
|
190
|
+
observers.push(hook);
|
|
191
|
+
return () => {
|
|
192
|
+
const index = observers.indexOf(hook);
|
|
193
|
+
if (index !== -1) observers.splice(index, 1);
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Replaces every installed observer with `hook`, or removes them all.
|
|
198
|
+
*
|
|
199
|
+
* @param hook - The observer to install, or `undefined` to uninstall everything
|
|
200
|
+
* @deprecated Use {@link addIOCDevtoolsHook}, which does not evict other observers.
|
|
164
201
|
*/
|
|
165
202
|
function setIOCDevtoolsHook(hook) {
|
|
166
|
-
|
|
167
|
-
|
|
203
|
+
const current = registry();
|
|
204
|
+
current.observers.length = 0;
|
|
205
|
+
if (hook) current.observers.push(hook);
|
|
168
206
|
}
|
|
169
207
|
/**
|
|
170
|
-
* Returns the
|
|
208
|
+
* Returns the fan-out hook the container calls into.
|
|
209
|
+
*
|
|
210
|
+
* @returns The registry, or undefined when no observer is installed
|
|
171
211
|
*/
|
|
172
212
|
function getIOCDevtoolsHook() {
|
|
173
|
-
|
|
213
|
+
const current = globalThis[IOC_DEVTOOLS_HOOK_KEY];
|
|
214
|
+
return current && current.observers.length > 0 ? current : void 0;
|
|
174
215
|
}
|
|
175
216
|
//#endregion
|
|
176
217
|
//#region src/Domain/Container.ts
|
|
@@ -190,6 +231,7 @@ var Container = class Container {
|
|
|
190
231
|
fSingletonInstances = /* @__PURE__ */ new Map();
|
|
191
232
|
fInjectMethod = IOC.InjectMethod.STATIC;
|
|
192
233
|
fContainerEvents = new ContainerEvents();
|
|
234
|
+
fRevision = 0;
|
|
193
235
|
/**
|
|
194
236
|
* Constructor for container.
|
|
195
237
|
* @param params initial params for container
|
|
@@ -239,6 +281,15 @@ var Container = class Container {
|
|
|
239
281
|
return this.fContext;
|
|
240
282
|
}
|
|
241
283
|
/**
|
|
284
|
+
* Counter that changes whenever a binding is added, replaced or first
|
|
285
|
+
* instantiated - that is, whenever a description of this container would
|
|
286
|
+
* come out different.
|
|
287
|
+
* @returns {number} the current revision
|
|
288
|
+
*/
|
|
289
|
+
get revision() {
|
|
290
|
+
return this.fRevision;
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
242
293
|
* Binds particular key to container in singleton scope. Multiple queries/injects of this
|
|
243
294
|
* service will always return the same instance.
|
|
244
295
|
*
|
|
@@ -258,6 +309,7 @@ var Container = class Container {
|
|
|
258
309
|
this.fServices.delete(key);
|
|
259
310
|
}
|
|
260
311
|
this.fServices.set(key, newDef);
|
|
312
|
+
this.fRevision++;
|
|
261
313
|
this.fNewQueue.set(key, newDef);
|
|
262
314
|
}
|
|
263
315
|
/**
|
|
@@ -275,6 +327,7 @@ var Container = class Container {
|
|
|
275
327
|
const existingServiceDef = this.fServices.get(key);
|
|
276
328
|
if (existingServiceDef) this.internalDispose(existingServiceDef);
|
|
277
329
|
this.fServices.set(key, newDef);
|
|
330
|
+
this.fRevision++;
|
|
278
331
|
this.fNewQueue.set(key, newDef);
|
|
279
332
|
}
|
|
280
333
|
/**
|
|
@@ -294,6 +347,7 @@ var Container = class Container {
|
|
|
294
347
|
const existingServiceDef = this.fServices.get(key);
|
|
295
348
|
if (existingServiceDef) this.internalDispose(existingServiceDef);
|
|
296
349
|
this.fServices.set(key, newDef);
|
|
350
|
+
this.fRevision++;
|
|
297
351
|
this.fNewQueue.set(key, newDef);
|
|
298
352
|
}
|
|
299
353
|
/**
|
|
@@ -319,6 +373,7 @@ var Container = class Container {
|
|
|
319
373
|
const existingServiceDef = this.fServices.get(key);
|
|
320
374
|
if (existingServiceDef) this.internalDispose(existingServiceDef);
|
|
321
375
|
this.fServices.set(key, newDef);
|
|
376
|
+
this.fRevision++;
|
|
322
377
|
this.fNewQueue.set(key, newDef);
|
|
323
378
|
}
|
|
324
379
|
/**
|
|
@@ -457,7 +512,10 @@ var Container = class Container {
|
|
|
457
512
|
const start = onResolved ? performance.now() : 0;
|
|
458
513
|
const constructor = serviceDef.serviceValue;
|
|
459
514
|
const instance = new constructor();
|
|
460
|
-
if (singleton)
|
|
515
|
+
if (singleton) {
|
|
516
|
+
this.fSingletonInstances.set(serviceDef.serviceKey, instance);
|
|
517
|
+
this.fRevision++;
|
|
518
|
+
}
|
|
461
519
|
try {
|
|
462
520
|
this.internalProcessInjects(instance, this.fInjectMethod);
|
|
463
521
|
} catch (error) {
|
|
@@ -572,7 +630,7 @@ let IOC;
|
|
|
572
630
|
*/
|
|
573
631
|
const classMap = /* @__PURE__ */ new Map();
|
|
574
632
|
globalThis.__IOCClassMap = globalThis.__IOCClassMap ?? classMap;
|
|
575
|
-
const ROOT_PROTO = Object.getPrototypeOf({});
|
|
633
|
+
const ROOT_PROTO$1 = Object.getPrototypeOf({});
|
|
576
634
|
/**
|
|
577
635
|
* Retrieves the class dependency metadata map used for IOC injection purposes.
|
|
578
636
|
*
|
|
@@ -667,7 +725,7 @@ function injectDeps(container, instance, method) {
|
|
|
667
725
|
}
|
|
668
726
|
}
|
|
669
727
|
prototype = Object.getPrototypeOf(prototype);
|
|
670
|
-
} while (prototype && prototype !== ROOT_PROTO);
|
|
728
|
+
} while (prototype && prototype !== ROOT_PROTO$1);
|
|
671
729
|
}
|
|
672
730
|
const IOCEngine = {
|
|
673
731
|
registerInject,
|
|
@@ -770,4 +828,215 @@ function Injectable() {
|
|
|
770
828
|
return () => {};
|
|
771
829
|
}
|
|
772
830
|
//#endregion
|
|
773
|
-
|
|
831
|
+
//#region src/Domain/Describe.ts
|
|
832
|
+
/** Prototype of a plain object; the stop condition when walking prototype chains. */
|
|
833
|
+
const ROOT_PROTO = Object.getPrototypeOf({});
|
|
834
|
+
/**
|
|
835
|
+
* Produces a readable label for any service key.
|
|
836
|
+
*
|
|
837
|
+
* @param key - Service key from a `bind*` call
|
|
838
|
+
* @returns Display name for the key
|
|
839
|
+
*/
|
|
840
|
+
function describeKey(key) {
|
|
841
|
+
if (typeof key === "symbol") return key.description ?? "Symbol()";
|
|
842
|
+
if (typeof key === "string") return key;
|
|
843
|
+
if (typeof key === "function") return key.name || "Anonymous";
|
|
844
|
+
if (typeof key === "object" && key !== null) return key.constructor?.name ?? "Object";
|
|
845
|
+
return "Unknown";
|
|
846
|
+
}
|
|
847
|
+
/**
|
|
848
|
+
* Names the value a key was bound to, or `null` when it adds no information.
|
|
849
|
+
*
|
|
850
|
+
* @param key - Service key
|
|
851
|
+
* @param value - Bound implementation or instance
|
|
852
|
+
* @returns Implementation name, or null
|
|
853
|
+
*/
|
|
854
|
+
function describeImplementation(key, value) {
|
|
855
|
+
if (value === key) return null;
|
|
856
|
+
if (typeof value === "function") {
|
|
857
|
+
const name = value.name;
|
|
858
|
+
return name && name !== describeKey(key) ? name : null;
|
|
859
|
+
}
|
|
860
|
+
if (typeof value === "object" && value !== null) {
|
|
861
|
+
const name = value.constructor?.name;
|
|
862
|
+
return name && name !== describeKey(key) ? name : null;
|
|
863
|
+
}
|
|
864
|
+
return null;
|
|
865
|
+
}
|
|
866
|
+
/**
|
|
867
|
+
* Resolves the class constructor behind a service definition.
|
|
868
|
+
*
|
|
869
|
+
* @param def - Service definition from the container
|
|
870
|
+
* @returns The constructor, or null for plain values
|
|
871
|
+
*/
|
|
872
|
+
function resolveConstructor(def) {
|
|
873
|
+
const value = def.serviceValue;
|
|
874
|
+
if (typeof value === "function") return value;
|
|
875
|
+
if (typeof value === "object" && value !== null) {
|
|
876
|
+
const ctor = value.constructor;
|
|
877
|
+
if (ctor === Object || ctor === Array || typeof ctor !== "function") return null;
|
|
878
|
+
return ctor;
|
|
879
|
+
}
|
|
880
|
+
return null;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Collects every injection declaration of a class, inherited ones included.
|
|
884
|
+
*
|
|
885
|
+
* @param ctor - Class constructor to inspect
|
|
886
|
+
* @returns Dependency declarations, nearest prototype first
|
|
887
|
+
*/
|
|
888
|
+
function collectClassDeps(ctor) {
|
|
889
|
+
if (!ctor?.prototype) return [];
|
|
890
|
+
const deps = [];
|
|
891
|
+
const seen = /* @__PURE__ */ new Set();
|
|
892
|
+
let proto = ctor.prototype;
|
|
893
|
+
while (proto && proto !== ROOT_PROTO) {
|
|
894
|
+
const entry = IOCEngine.getEntryForClass({ prototype: proto });
|
|
895
|
+
for (const dep of entry?.deps ?? []) {
|
|
896
|
+
if (seen.has(dep.propertyName)) continue;
|
|
897
|
+
seen.add(dep.propertyName);
|
|
898
|
+
deps.push(dep);
|
|
899
|
+
}
|
|
900
|
+
proto = Object.getPrototypeOf(proto);
|
|
901
|
+
}
|
|
902
|
+
return deps;
|
|
903
|
+
}
|
|
904
|
+
/**
|
|
905
|
+
* Maps a binding's factory type onto its description kind.
|
|
906
|
+
*
|
|
907
|
+
* @param type - Factory type recorded by the container
|
|
908
|
+
* @returns The matching kind
|
|
909
|
+
*/
|
|
910
|
+
function toServiceKind(type) {
|
|
911
|
+
switch (type) {
|
|
912
|
+
case IOC.ServiceFactoryType.CLASS_SINGLETON: return "singleton";
|
|
913
|
+
case IOC.ServiceFactoryType.CLASS: return "transient";
|
|
914
|
+
default: return "instance";
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Builds a serialisable description of a container.
|
|
919
|
+
*
|
|
920
|
+
* Reads `container.services`, which is binding metadata, never
|
|
921
|
+
* `getAllServices()`, which would construct every registered service. An
|
|
922
|
+
* inspector that instantiates what it inspects is not an inspector.
|
|
923
|
+
*
|
|
924
|
+
* @param container - The container to describe
|
|
925
|
+
* @param options - Optional annotation callback
|
|
926
|
+
* @returns Nodes, edges, cycles and the unused-service count
|
|
927
|
+
*/
|
|
928
|
+
function describeContainer(container, options = {}) {
|
|
929
|
+
const services = container.services;
|
|
930
|
+
const ids = buildIdMap(services);
|
|
931
|
+
const nodes = [];
|
|
932
|
+
const edges = [];
|
|
933
|
+
const dependentCounts = /* @__PURE__ */ new Map();
|
|
934
|
+
for (const [key, def] of services) {
|
|
935
|
+
const id = ids.get(key);
|
|
936
|
+
const name = describeKey(key);
|
|
937
|
+
const ctor = resolveConstructor(def);
|
|
938
|
+
const dependencies = [];
|
|
939
|
+
for (const dep of collectClassDeps(ctor)) {
|
|
940
|
+
const targetId = ids.get(dep.dependency);
|
|
941
|
+
const targetName = describeKey(dep.dependency);
|
|
942
|
+
const optional = dep.type === IOC.DependencyType.OPTIONAL;
|
|
943
|
+
dependencies.push({
|
|
944
|
+
id: targetId ?? `unbound:${targetName}`,
|
|
945
|
+
name: targetName,
|
|
946
|
+
property: dep.propertyName,
|
|
947
|
+
optional,
|
|
948
|
+
bound: targetId !== void 0
|
|
949
|
+
});
|
|
950
|
+
if (targetId !== void 0) {
|
|
951
|
+
edges.push({
|
|
952
|
+
from: id,
|
|
953
|
+
to: targetId,
|
|
954
|
+
property: dep.propertyName,
|
|
955
|
+
optional
|
|
956
|
+
});
|
|
957
|
+
dependentCounts.set(targetId, (dependentCounts.get(targetId) ?? 0) + 1);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
const node = {
|
|
961
|
+
id,
|
|
962
|
+
name,
|
|
963
|
+
kind: toServiceKind(def.type),
|
|
964
|
+
role: ctor ? "service" : "value",
|
|
965
|
+
implementation: describeImplementation(key, def.serviceValue),
|
|
966
|
+
instantiated: def.type === IOC.ServiceFactoryType.INSTANCE || container.hasInstance(key),
|
|
967
|
+
symbol: typeof key === "symbol",
|
|
968
|
+
dependencies,
|
|
969
|
+
dependents: 0
|
|
970
|
+
};
|
|
971
|
+
options.annotate?.(node, {
|
|
972
|
+
key,
|
|
973
|
+
def,
|
|
974
|
+
ctor
|
|
975
|
+
});
|
|
976
|
+
nodes.push(node);
|
|
977
|
+
}
|
|
978
|
+
for (const node of nodes) node.dependents = dependentCounts.get(node.id) ?? 0;
|
|
979
|
+
return {
|
|
980
|
+
context: container.context,
|
|
981
|
+
nodes,
|
|
982
|
+
edges,
|
|
983
|
+
cycles: findCycles(nodes, edges),
|
|
984
|
+
unusedCount: nodes.filter((node) => !node.instantiated).length
|
|
985
|
+
};
|
|
986
|
+
}
|
|
987
|
+
/**
|
|
988
|
+
* Assigns a stable, unique id to every service key.
|
|
989
|
+
*
|
|
990
|
+
* @param services - The container's service map
|
|
991
|
+
* @returns Map of service key to unique id
|
|
992
|
+
*/
|
|
993
|
+
function buildIdMap(services) {
|
|
994
|
+
const ids = /* @__PURE__ */ new Map();
|
|
995
|
+
const used = /* @__PURE__ */ new Map();
|
|
996
|
+
for (const key of services.keys()) {
|
|
997
|
+
const base = describeKey(key);
|
|
998
|
+
const seen = used.get(base) ?? 0;
|
|
999
|
+
used.set(base, seen + 1);
|
|
1000
|
+
ids.set(key, seen === 0 ? base : `${base}#${seen}`);
|
|
1001
|
+
}
|
|
1002
|
+
return ids;
|
|
1003
|
+
}
|
|
1004
|
+
/**
|
|
1005
|
+
* Finds dependency cycles by depth-first search with colour marking.
|
|
1006
|
+
*
|
|
1007
|
+
* Each cycle is reported once, rotated to start at its lexicographically
|
|
1008
|
+
* smallest member so the same loop always serialises identically.
|
|
1009
|
+
*
|
|
1010
|
+
* @param nodes - Graph nodes
|
|
1011
|
+
* @param edges - Graph edges
|
|
1012
|
+
* @returns One ordered id list per cycle
|
|
1013
|
+
*/
|
|
1014
|
+
function findCycles(nodes, edges) {
|
|
1015
|
+
const adjacency = /* @__PURE__ */ new Map();
|
|
1016
|
+
for (const node of nodes) adjacency.set(node.id, []);
|
|
1017
|
+
for (const edge of edges) adjacency.get(edge.from)?.push(edge.to);
|
|
1018
|
+
const state = /* @__PURE__ */ new Map();
|
|
1019
|
+
const path = [];
|
|
1020
|
+
const found = /* @__PURE__ */ new Map();
|
|
1021
|
+
const visit = (id) => {
|
|
1022
|
+
state.set(id, "visiting");
|
|
1023
|
+
path.push(id);
|
|
1024
|
+
for (const next of adjacency.get(id) ?? []) {
|
|
1025
|
+
const nextState = state.get(next);
|
|
1026
|
+
if (nextState === "visiting") {
|
|
1027
|
+
const cycle = path.slice(path.indexOf(next));
|
|
1028
|
+
const smallest = cycle.indexOf([...cycle].sort()[0]);
|
|
1029
|
+
const normalised = [...cycle.slice(smallest), ...cycle.slice(0, smallest)];
|
|
1030
|
+
found.set(normalised.join(" "), normalised);
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
if (nextState === void 0) visit(next);
|
|
1034
|
+
}
|
|
1035
|
+
path.pop();
|
|
1036
|
+
state.set(id, "done");
|
|
1037
|
+
};
|
|
1038
|
+
for (const node of nodes) if (!state.has(node.id)) visit(node.id);
|
|
1039
|
+
return [...found.values()];
|
|
1040
|
+
}
|
|
1041
|
+
//#endregion
|
|
1042
|
+
export { BaseDecorator, Container, ContainerEvents, Destroy, IOC, IOCEngine, IOC_DEVTOOLS_HOOK_KEY, Identity, Init, Inject, InjectOptional, Injectable, addIOCDevtoolsHook, collectClassDeps, createDecorator, describeContainer, describeImplementation, describeKey, destroyContainer, destroyDecorators, getIOCDevtoolsHook, initializeContainer, initializeDecorators, resolveConstructor, setIOCDevtoolsHook, toServiceKind };
|