-
Notifications
You must be signed in to change notification settings - Fork 0
/
Scope.ts
102 lines (89 loc) · 3.39 KB
/
Scope.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
/** Represents a scope for which certain dependencies are provided. */
import { AbstractClass, isObject } from './_internal'
const _scopeSymbol = Symbol()
/**
* Generates a base class for a class object that extends {@link Scope:type}.
* Classes that extend the returned base class should have a
* `private _: any` property (or any other private member) to ensure the scope has its own unique type.
*
* @group Scoping
*/
export function Scope({ name }: { name?: string } = {}): Scope.ScopeClass {
abstract class _ScopeClass {
/** @ignore */
static readonly [_scopeSymbol] = null
/** @ignore */
static readonly scopeTag?: symbol
static readonly inject: null
static get fullName() { return this.name + (name ? `(${name})` : '') }
}
return _ScopeClass
}
/**
* Used to define the lifetime of a resolved resource.
* An resource can be bound to a Scope, which allows the provided instance to be reused as long as the Scope is valid.
*
* A Scope can be added to a {@link Container}, often a child container, to indicate that the Scope is valid whenever requesting
* resources from that container.
*
* @see The {@link Scope | Scope()} function for implementing Scope.
*
* @group Scoping
*/
export interface Scope {
/** @ignore */
readonly [_scopeSymbol]: null
/** @ignore */
readonly scopeTag?: symbol
readonly fullName: string
/** @ignore prevent a Scope from being an InjectableClass */
readonly inject: null
}
/**
* @group Scoping
*/
export namespace Scope {
export function isScope(target: any): target is Scope {
return isObject(target) && _scopeSymbol in target
}
/** Class returned by {@link Scope}. Extend this to implement {@link Scope:type}. */
export interface ScopeClass extends AbstractClass<any, never>, Scope { }
/**
* If a resource is bound to this scope, its instance will be retained only withinh the container that requested it.
*
* All containers include the Local scope.
*/
export class Local extends Scope() { private _: any }
}
/**
* The default scope for top-level containers.
* Any instances provided for a resource bound to Singleton will live as long as the container they are provided to,
* or the root container if they are using a default injection.
*
* @group Scoping
*/
export class Singleton extends Scope() { private _: any }
/**
* A {@link Scope:type | Scope} or an arbitrarily-nested list of Scopes.
*
* @group Scoping
*/
export type ScopeList<Scp extends Scope = Scope> = Scp | readonly ScopeList<Scp>[]
export type NonEmptyScopeList<Scp extends Scope = Scope> = Scp | readonly [ScopeList<Scp>, ...readonly ScopeList<Scp>[]]
/**
* @group Scoping
*/
export namespace ScopeList {
/** Converts a {@link ScopeList} into a flat list of {@link Scope:type | Scope}[] */
export function flatten<Scp extends Scope>(scopes: ScopeList<Scp> | (() => ScopeList<Scp>) | null | undefined): Scp[] {
return (scopes == null) ? [] :
(isObject(scopes) && _scopeSymbol in scopes) ? [scopes] :
(typeof scopes == 'function') ? flatten(scopes()) :
scopes.flatMap(flatten) as Scp[]
}
export function isScopeList(target: any): target is ScopeList {
if (isObject(target) && _scopeSymbol in target) return true
if (target instanceof Array) return target.every(isScopeList)
return false
}
}