di-bag 0.2.0 → 0.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/AGENTS.md +149 -0
- package/README.md +59 -46
- package/dist/acquisition-context.d.ts +8 -2
- package/dist/acquisition-mode.d.ts +9 -4
- package/dist/acquisition-mode.js +25 -7
- package/dist/acquisition.d.ts +2 -0
- package/dist/acquisition.js +9 -0
- package/dist/alias-types.d.ts +12 -3
- package/dist/composition-report.d.ts +3 -2
- package/dist/composition.d.ts +8 -2
- package/dist/contribution-types.d.ts +26 -18
- package/dist/dependency-references.d.ts +16 -4
- package/dist/di-bag.d.ts +320 -41
- package/dist/di-bag.js +86 -14
- package/dist/errors.d.ts +115 -8
- package/dist/errors.js +113 -12
- package/dist/index.d.ts +5 -5
- package/dist/index.js +2 -1
- package/dist/inspection.d.ts +21 -5
- package/dist/lifetime-types.d.ts +281 -180
- package/dist/lifetime.d.ts +4 -1
- package/dist/module-types.d.ts +87 -30
- package/dist/module.d.ts +18 -4
- package/dist/module.js +31 -7
- package/dist/observers.d.ts +25 -6
- package/dist/plugins.d.ts +17 -4
- package/dist/provider.d.ts +41 -10
- package/dist/provider.js +1 -0
- package/dist/registration.d.ts +8 -2
- package/dist/registration.js +4 -1
- package/dist/runtime.d.ts +12 -3
- package/dist/runtime.js +25 -8
- package/dist/scope-types.d.ts +20 -5
- package/dist/startup.d.ts +19 -1
- package/dist/startup.js +72 -11
- package/dist/token-types.d.ts +26 -8
- package/dist/tokens.d.ts +10 -2
- package/dist/tokens.js +2 -0
- package/dist/types.d.ts +68 -22
- package/docs/agent/api-card.md +337 -0
- package/docs/agent/errors.md +1042 -0
- package/docs/agent/recipes.md +290 -0
- package/package.json +11 -5
package/dist/lifetime-types.d.ts
CHANGED
|
@@ -1,223 +1,324 @@
|
|
|
1
1
|
import type { ContributionConstraint } from './contribution-types';
|
|
2
2
|
import type { Registration, Registrations } from './registration';
|
|
3
|
-
import type {
|
|
4
|
-
import type {
|
|
5
|
-
import type {
|
|
6
|
-
import type {
|
|
7
|
-
import type { CheckedConstraints, CompleteConstraints, NeedConstraint, Renamed } from './module-types';
|
|
8
|
-
type SiteText<S> = S extends {
|
|
9
|
-
readonly key: infer K;
|
|
10
|
-
} ? NameText<K> : S extends {
|
|
11
|
-
readonly kind: 'contribution';
|
|
12
|
-
} ? 'contribution' : never;
|
|
13
|
-
type CaptiveText<C> = C extends {
|
|
14
|
-
readonly root: infer R;
|
|
15
|
-
readonly dependency: infer D;
|
|
16
|
-
} ? `${SiteText<R>} -> ${SiteText<D>}` : never;
|
|
3
|
+
import type { ProviderGraphContract, ProviderNamedDependencies, ProviderRequiredTokens, ProviderOptionalTokens, ProviderCollectionTokens } from './provider';
|
|
4
|
+
import type { TokenBase, TokenKey } from './tokens';
|
|
5
|
+
import type { CheckDependencyCompatibility, CheckDependencyCompleteness, NameText, SeeErrors, Unsatisfied } from './types';
|
|
6
|
+
import type { CheckedConstraints, CompleteConstraints, NeedConstraint } from './module-types';
|
|
17
7
|
/**
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* resolves in the installing host, matching runtime lexical lookup.
|
|
8
|
+
* Where a sealed lifetime walk leaves its module: an export or external name the installing
|
|
9
|
+
* host resolves, a typed-token collection the host completes, or a private scoped dead end.
|
|
10
|
+
* @see https://dany-fedorov.github.io/di-bag/agent/errors.html#root-capture
|
|
22
11
|
*/
|
|
23
|
-
export type
|
|
24
|
-
readonly
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
readonly
|
|
12
|
+
export type Reach = {
|
|
13
|
+
readonly kind: 'export' | 'external';
|
|
14
|
+
readonly key: PropertyKey;
|
|
15
|
+
} | {
|
|
16
|
+
readonly kind: 'collection';
|
|
17
|
+
readonly key: symbol;
|
|
18
|
+
} | {
|
|
19
|
+
readonly kind: 'scoped';
|
|
20
|
+
readonly key: PropertyKey;
|
|
28
21
|
};
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
} ? WithExtras<C, LexicalContext<R, E> & {
|
|
37
|
-
readonly parent: Enclosed<P, Parent>;
|
|
38
|
-
}> : WithExtras<C, LexicalContext<R, E> & {
|
|
39
|
-
readonly parent: Parent;
|
|
40
|
-
}> : C;
|
|
41
|
-
/** Rename one public export at the chain end, where names meet the installing host. */
|
|
42
|
-
export type RenamedContext<C, Old extends string, New extends string> = C extends LexicalContext<infer R, infer E> ? C extends {
|
|
43
|
-
readonly parent: infer P extends LexicalContext;
|
|
44
|
-
} ? WithExtras<C, LexicalContext<R, E> & {
|
|
45
|
-
readonly parent: RenamedContext<P, Old, New>;
|
|
46
|
-
}> : WithExtras<C, LexicalContext<R, Renamed<E, Old, New>>> : C;
|
|
47
|
-
/** The plain scope of a sealed contribution, without its retained original registration. */
|
|
48
|
-
type Scope<C> = C extends LexicalContext<infer R, infer E> ? C extends {
|
|
49
|
-
readonly parent: infer P extends LexicalContext;
|
|
50
|
-
} ? LexicalContext<R, E> & {
|
|
51
|
-
readonly parent: P;
|
|
52
|
-
} : LexicalContext<R, E> : undefined;
|
|
53
|
-
type WithoutLexical<G> = G extends {
|
|
54
|
-
readonly lexical: unknown;
|
|
55
|
-
} ? Omit<G, 'lexical'> : G;
|
|
22
|
+
/**
|
|
23
|
+
* A compact seal-time lifetime record that replaces a module's private registrations:
|
|
24
|
+
* `root-reach` names a private strict root, `export-reach` an export the host checks as a root or
|
|
25
|
+
* walks through as a transient or alias, and `contribution-reach` a sealed contribution group
|
|
26
|
+
* that is checked as a root or walked by collecting roots. Each record carries one reach.
|
|
27
|
+
* @see https://dany-fedorov.github.io/di-bag/agent/errors.html#root-capture
|
|
28
|
+
*/
|
|
56
29
|
export type LifetimeObligation = {
|
|
57
|
-
readonly kind: '
|
|
58
|
-
readonly
|
|
59
|
-
readonly
|
|
30
|
+
readonly kind: 'root-reach';
|
|
31
|
+
readonly root: PropertyKey;
|
|
32
|
+
readonly reach: Reach;
|
|
33
|
+
} | {
|
|
34
|
+
readonly kind: 'export-reach';
|
|
35
|
+
readonly export: PropertyKey;
|
|
36
|
+
readonly reach: Reach;
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: 'contribution-reach';
|
|
39
|
+
readonly group: symbol;
|
|
40
|
+
readonly policy: 'root' | 'transient';
|
|
41
|
+
readonly reach: Reach;
|
|
60
42
|
};
|
|
61
|
-
type
|
|
62
|
-
readonly
|
|
43
|
+
type ContributionSite = {
|
|
44
|
+
readonly kind: 'contribution';
|
|
63
45
|
};
|
|
64
|
-
type
|
|
46
|
+
type SiteText<S> = S extends ContributionSite ? 'contribution' : NameText<S>;
|
|
47
|
+
type CaptiveText<C> = C extends {
|
|
48
|
+
readonly root: infer R;
|
|
49
|
+
readonly dependency: infer D;
|
|
50
|
+
} ? `${SiteText<R>} -> ${SiteText<D>}` : never;
|
|
51
|
+
type Members<V> = V extends infer T & {} ? T extends Registration ? T : never : never;
|
|
52
|
+
type Strict<T> = ProviderGraphContract<T> extends infer G ? G extends {
|
|
65
53
|
readonly lifetime: {
|
|
66
54
|
readonly kind: 'root';
|
|
67
|
-
readonly allowScopedDependencies: infer
|
|
55
|
+
readonly allowScopedDependencies: infer A;
|
|
68
56
|
};
|
|
69
|
-
} ? [
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
readonly source: K;
|
|
74
|
-
readonly context: LexicalContext<R, ExportMap<P>>;
|
|
75
|
-
} : never;
|
|
76
|
-
}[Exclude<keyof R, P>];
|
|
77
|
-
export type LexicalProvider<V extends Registration, R extends Registrations, P extends keyof R, K extends keyof R> = V extends infer T & {} ? T extends Registration ? [Extract<ProviderGraphContract<T>, {
|
|
57
|
+
} ? [A] extends [true] ? false : true : false : false;
|
|
58
|
+
type Carrying<T> = ProviderGraphContract<T> extends infer G ? G extends {
|
|
59
|
+
readonly alias: PropertyKey;
|
|
60
|
+
} | {
|
|
78
61
|
readonly lifetime: {
|
|
79
|
-
readonly kind: '
|
|
62
|
+
readonly kind: 'transient';
|
|
80
63
|
};
|
|
64
|
+
} ? true : false : false;
|
|
65
|
+
type StrictMembers<V> = Members<V> extends infer T ? T extends Registration ? true extends Strict<T> ? T : never : never : never;
|
|
66
|
+
type CarrierMembers<V> = Members<V> extends infer T ? T extends Registration ? true extends Strict<T> | Carrying<T> ? T : never : never : never;
|
|
67
|
+
type Dependencies<V> = V extends Registration ? keyof ProviderNamedDependencies<V> | TokenKey<ProviderRequiredTokens<V> | ProviderOptionalTokens<V>> : never;
|
|
68
|
+
type AliasKeys<V> = ProviderGraphContract<V> extends infer G ? G extends {
|
|
69
|
+
readonly alias: infer A;
|
|
70
|
+
} ? A : never : never;
|
|
71
|
+
type CollectionKeys<V> = ProviderCollectionTokens<V> extends infer T ? T extends TokenBase ? TokenKey<T> : never : never;
|
|
72
|
+
type Lifetimed<V> = V extends infer T & {} ? ProviderGraphContract<T> extends infer G ? G extends {
|
|
73
|
+
readonly lifetime: unknown;
|
|
74
|
+
} | {
|
|
75
|
+
readonly alias: unknown;
|
|
81
76
|
} | {
|
|
77
|
+
readonly sharedAlias: unknown;
|
|
78
|
+
} ? true : never : never : never;
|
|
79
|
+
type ContributionRegistrations<C> = C extends ContributionConstraint ? C['registration'] : never;
|
|
80
|
+
type NeedsLifetimeWalk<R extends Registrations, C> = [Extract<C, LifetimeObligation>] extends [never] ? Lifetimed<R[keyof R] | ContributionRegistrations<C>> : true;
|
|
81
|
+
type ExportReaches<C, K> = C extends {
|
|
82
|
+
readonly kind: 'export-reach';
|
|
83
|
+
readonly export: K;
|
|
84
|
+
readonly reach: infer X;
|
|
85
|
+
} ? X : never;
|
|
86
|
+
type Reached<R extends Registrations, P, C, D, Visited> = D extends P ? {
|
|
87
|
+
readonly kind: 'export';
|
|
88
|
+
readonly key: D;
|
|
89
|
+
} : D extends keyof R ? D extends Visited ? never : ReachTarget<R, P, C, R[D], D, Visited | D> : D extends PropertyKey ? {
|
|
90
|
+
readonly kind: 'external';
|
|
91
|
+
readonly key: D;
|
|
92
|
+
} : never;
|
|
93
|
+
type ReachTarget<R extends Registrations, P, C, V, D, Visited> = Members<V> extends infer T ? T extends Registration ? ProviderGraphContract<T> extends infer G ? G extends {
|
|
94
|
+
readonly kind: 'opaque';
|
|
95
|
+
} ? never : G extends {
|
|
82
96
|
readonly alias: PropertyKey;
|
|
83
|
-
}
|
|
84
|
-
readonly
|
|
85
|
-
readonly
|
|
86
|
-
readonly context: ModuleScope<R, P>;
|
|
97
|
+
} ? Reaches<R, P, C, T, D, Visited> : G extends {
|
|
98
|
+
readonly lifetime: {
|
|
99
|
+
readonly kind: 'root';
|
|
87
100
|
};
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
readonly source: infer K;
|
|
92
|
-
readonly context: infer L extends LexicalContext;
|
|
101
|
+
} ? never : G extends {
|
|
102
|
+
readonly lifetime: {
|
|
103
|
+
readonly kind: 'transient';
|
|
93
104
|
};
|
|
94
|
-
} ?
|
|
95
|
-
readonly
|
|
96
|
-
|
|
97
|
-
|
|
105
|
+
} ? Reaches<R, P, C, T, D, Visited> : {
|
|
106
|
+
readonly kind: 'scoped';
|
|
107
|
+
readonly key: D;
|
|
108
|
+
} : never : never : never;
|
|
109
|
+
type Reaches<R extends Registrations, P, C, V, K, Visited> = Reached<R, P, C, Dependencies<V> | AliasKeys<V>, Visited> | Follow<R, P, C, ExportReaches<C, K>, Visited> | (CollectionKeys<V> extends infer T ? T extends symbol ? {
|
|
110
|
+
readonly kind: 'collection';
|
|
111
|
+
readonly key: T;
|
|
112
|
+
} : never : never);
|
|
113
|
+
type Follow<R extends Registrations, P, C, X, Visited> = X extends {
|
|
114
|
+
readonly kind: 'export' | 'external';
|
|
115
|
+
readonly key: infer D;
|
|
116
|
+
} ? Reached<R, P, C, D, Visited> : X;
|
|
117
|
+
type AsRoot<Root, X> = X extends Reach ? {
|
|
118
|
+
readonly kind: 'root-reach';
|
|
119
|
+
readonly root: Root;
|
|
120
|
+
readonly reach: X;
|
|
121
|
+
} : never;
|
|
122
|
+
type AsExport<K, X> = X extends Reach ? {
|
|
123
|
+
readonly kind: 'export-reach';
|
|
124
|
+
readonly export: K;
|
|
125
|
+
readonly reach: X;
|
|
126
|
+
} : never;
|
|
127
|
+
type AsContribution<T, Policy, X> = X extends Reach ? {
|
|
128
|
+
readonly kind: 'contribution-reach';
|
|
129
|
+
readonly group: T;
|
|
130
|
+
readonly policy: Policy;
|
|
131
|
+
readonly reach: X;
|
|
132
|
+
} : never;
|
|
133
|
+
type PrivateRoots<R extends Registrations, P, C> = {
|
|
134
|
+
[K in Exclude<keyof R, P>]: [StrictMembers<R[K]>] extends [never] ? never : AsRoot<K, Reaches<R, P, C, StrictMembers<R[K]>, K, K>>;
|
|
135
|
+
}[Exclude<keyof R, P>];
|
|
136
|
+
type RetainedRoots<R extends Registrations, P, C, O = C> = O extends {
|
|
137
|
+
readonly kind: 'root-reach';
|
|
138
|
+
readonly root: infer Root;
|
|
139
|
+
readonly reach: infer X;
|
|
140
|
+
} ? AsRoot<Root, Follow<R, P, C, X, never>> : never;
|
|
141
|
+
type ExportObligations<R extends Registrations, P, C> = {
|
|
142
|
+
[K in P & keyof R]: [CarrierMembers<R[K]>] extends [never] ? never : AsExport<K, Reaches<R, P, C, CarrierMembers<R[K]>, K, K>>;
|
|
143
|
+
}[P & keyof R];
|
|
144
|
+
type ContributionPolicy<T> = true extends Strict<T> ? 'root' : ProviderGraphContract<T> extends infer G ? G extends {
|
|
145
|
+
readonly lifetime: {
|
|
146
|
+
readonly kind: 'transient';
|
|
147
|
+
};
|
|
148
|
+
} ? 'transient' : never : never;
|
|
149
|
+
type OwnContributions<R extends Registrations, P, C, I = Extract<C, ContributionConstraint>> = I extends ContributionConstraint ? Members<I['registration']> extends infer T ? T extends Registration ? ContributionPolicy<T> extends infer Policy ? Policy extends 'root' | 'transient' ? AsContribution<TokenKey<I['token']>, Policy, Reaches<R, P, C, T, never, never>> : never : never : never : never : never;
|
|
150
|
+
type RetainedContributions<R extends Registrations, P, C, O = C> = O extends {
|
|
151
|
+
readonly kind: 'contribution-reach';
|
|
152
|
+
readonly group: infer T;
|
|
153
|
+
readonly policy: infer Policy;
|
|
154
|
+
readonly reach: infer X;
|
|
155
|
+
} ? AsContribution<T, Policy, Follow<R, P, C, X, never>> : never;
|
|
156
|
+
type ContributionObligations<R extends Registrations, P, C> = OwnContributions<R, P, C> | RetainedContributions<R, P, C>;
|
|
157
|
+
type Scoped = {
|
|
158
|
+
readonly reach: {
|
|
159
|
+
readonly kind: 'scoped';
|
|
98
160
|
};
|
|
99
|
-
} : G;
|
|
100
|
-
export type RenamedLifetimeProvider<V extends Registration, Old extends string, New extends string> = V extends infer T & {} ? T extends Registration ? ProviderGraphContract<T> extends {
|
|
101
|
-
readonly lexical: unknown;
|
|
102
|
-
} ? Provider<ProviderFactory<T>, ProviderRegistrationMetadata<T> & object, ProviderAcquisitionMetadata<T>, RenamedGraph<ProviderGraphContract<T>, Old, New>, ProviderAcquiredValue<T>> : T : never : never;
|
|
103
|
-
/** Rename public lifetime-carrier registrations while preserving their lexical sources. */
|
|
104
|
-
export type RenamedLifetimeProviders<D extends Registrations, Old extends string, New extends string> = {
|
|
105
|
-
[K in keyof D as K extends Old ? New : K]: RenamedLifetimeProvider<D[K], Old, New>;
|
|
106
|
-
};
|
|
107
|
-
/** Rename a retained lifetime obligation's public export view. */
|
|
108
|
-
export type RenamedLifetimeObligation<C extends LifetimeObligation, Old extends string, New extends string> = {
|
|
109
|
-
readonly kind: 'lifetime';
|
|
110
|
-
readonly source: C['source'];
|
|
111
|
-
readonly context: RenamedContext<C['context'], Old, New>;
|
|
112
|
-
};
|
|
113
|
-
/** Re-scope a retained obligation when the builder holding it seals into a module. */
|
|
114
|
-
export type EnclosedLifetimeObligation<C extends LifetimeObligation, R extends Registrations, P extends PropertyKey> = {
|
|
115
|
-
readonly kind: 'lifetime';
|
|
116
|
-
readonly source: C['source'];
|
|
117
|
-
readonly context: Enclosed<C['context'], ModuleScope<R, P>>;
|
|
118
|
-
};
|
|
119
|
-
type PublicSite<K> = {
|
|
120
|
-
readonly kind: 'public';
|
|
121
|
-
readonly key: K;
|
|
122
|
-
};
|
|
123
|
-
type PrivateSite<C, K> = {
|
|
124
|
-
readonly kind: 'private';
|
|
125
|
-
readonly context: C;
|
|
126
|
-
readonly key: K;
|
|
127
161
|
};
|
|
128
|
-
type
|
|
162
|
+
type Unreplaceable<R extends Registrations, P, C> = PrivateRoots<R, P, C> | RetainedRoots<R, P, C> | Extract<ContributionObligations<R, P, C>, {
|
|
163
|
+
readonly policy: 'root';
|
|
164
|
+
}>;
|
|
165
|
+
type SealCaptives<R extends Registrations, P, C> = Extract<Unreplaceable<R, P, C>, Scoped>;
|
|
166
|
+
type SealCaptiveText<O> = O extends {
|
|
167
|
+
readonly reach: {
|
|
168
|
+
readonly key: infer D;
|
|
169
|
+
};
|
|
170
|
+
} ? `${O extends {
|
|
171
|
+
readonly root: infer Root;
|
|
172
|
+
} ? SiteText<Root> : 'contribution'} -> ${NameText<D>}` : never;
|
|
173
|
+
/** Every compact lifetime obligation a sealing builder retains for its installing host. */
|
|
174
|
+
export type SealedLifetimes<R extends Registrations, P extends PropertyKey, C> = [NeedsLifetimeWalk<R, C>] extends [never] ? never : unknown extends CheckDependencyCompatibility<R> ? Exclude<Unreplaceable<R, P, C>, Scoped> | ExportObligations<R, P, C> | Extract<ContributionObligations<R, P, C>, {
|
|
175
|
+
readonly policy: 'transient';
|
|
176
|
+
}> : never;
|
|
177
|
+
/** Reject sealing when a root the host cannot replace captures a scoped service of the same module. */
|
|
178
|
+
export type SealAdmission<R extends Registrations, P extends PropertyKey, C> = [NeedsLifetimeWalk<R, C>] extends [never] ? unknown : [SealCaptives<R, P, C>] extends [never] ? unknown : unknown extends CheckDependencyCompatibility<R> ? Unsatisfied<`root lifetime cannot capture scoped dependency: ${SealCaptiveText<SealCaptives<R, P, C>>}${SeeErrors<'root-capture'>}`, {
|
|
179
|
+
readonly captives: SealCaptives<R, P, C>;
|
|
180
|
+
}> : unknown;
|
|
181
|
+
type RenamedReach<X, Old, New> = X extends {
|
|
182
|
+
readonly kind: 'export';
|
|
183
|
+
readonly key: Old;
|
|
184
|
+
} ? {
|
|
185
|
+
readonly kind: 'export';
|
|
186
|
+
readonly key: New;
|
|
187
|
+
} : X;
|
|
188
|
+
/** Rename one export inside retained lifetime obligations. */
|
|
189
|
+
export type RenamedObligation<O, Old extends string, New extends string> = O extends {
|
|
190
|
+
readonly kind: 'export-reach';
|
|
191
|
+
readonly export: infer K;
|
|
192
|
+
readonly reach: infer X;
|
|
193
|
+
} ? {
|
|
194
|
+
readonly kind: 'export-reach';
|
|
195
|
+
readonly export: K extends Old ? New : K;
|
|
196
|
+
readonly reach: RenamedReach<X, Old, New>;
|
|
197
|
+
} : O extends {
|
|
198
|
+
readonly kind: 'root-reach';
|
|
199
|
+
readonly root: infer Root;
|
|
200
|
+
readonly reach: infer X;
|
|
201
|
+
} ? {
|
|
202
|
+
readonly kind: 'root-reach';
|
|
129
203
|
readonly root: Root;
|
|
130
|
-
readonly
|
|
204
|
+
readonly reach: RenamedReach<X, Old, New>;
|
|
205
|
+
} : O extends {
|
|
206
|
+
readonly kind: 'contribution-reach';
|
|
207
|
+
readonly group: infer T;
|
|
208
|
+
readonly policy: infer Policy;
|
|
209
|
+
readonly reach: infer X;
|
|
210
|
+
} ? {
|
|
211
|
+
readonly kind: 'contribution-reach';
|
|
212
|
+
readonly group: T;
|
|
213
|
+
readonly policy: Policy;
|
|
214
|
+
readonly reach: RenamedReach<X, Old, New>;
|
|
215
|
+
} : O;
|
|
216
|
+
/** Drop the obligations of replaced exports: the replacement brings its own graph. */
|
|
217
|
+
export type WithoutExportObligations<C, K> = [Extract<C, {
|
|
218
|
+
readonly kind: 'export-reach';
|
|
219
|
+
readonly export: K;
|
|
220
|
+
}>] extends [never] ? C : Exclude<C, {
|
|
221
|
+
readonly kind: 'export-reach';
|
|
222
|
+
readonly export: K;
|
|
223
|
+
}>;
|
|
224
|
+
type Captured<D> = {
|
|
225
|
+
readonly captured: D;
|
|
131
226
|
};
|
|
132
|
-
type
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
type
|
|
139
|
-
readonly
|
|
140
|
-
}
|
|
141
|
-
type
|
|
142
|
-
type
|
|
143
|
-
type WalkTarget<H extends Registrations, V extends Registration, C, Root, Site, Visited, G> = V extends infer T & {} ? T extends Registration ? ProviderGraphContract<T> extends infer PG ? PG extends {
|
|
227
|
+
type GroupReaches<C, T> = C extends {
|
|
228
|
+
readonly kind: 'contribution-reach';
|
|
229
|
+
readonly group: T;
|
|
230
|
+
readonly policy: 'transient';
|
|
231
|
+
readonly reach: infer X;
|
|
232
|
+
} ? X : never;
|
|
233
|
+
type Collected<T> = {
|
|
234
|
+
readonly collection: T;
|
|
235
|
+
};
|
|
236
|
+
type HostReach<R extends Registrations, C, D, Visited> = D extends keyof R ? D extends Visited ? never : HostTarget<R, C, R[D], D, Visited | D> : never;
|
|
237
|
+
type HostTarget<R extends Registrations, C, V, D, Visited> = Members<V> extends infer T ? T extends Registration ? ProviderGraphContract<T> extends infer G ? G extends {
|
|
144
238
|
readonly sharedAlias: {
|
|
145
|
-
readonly registrations: infer
|
|
239
|
+
readonly registrations: infer S extends Registrations;
|
|
146
240
|
readonly source: infer K;
|
|
147
241
|
};
|
|
148
|
-
} ?
|
|
242
|
+
} ? HostReach<S, C, K, never> : G extends {
|
|
149
243
|
readonly kind: 'opaque';
|
|
150
|
-
} ? never :
|
|
244
|
+
} ? never : G extends {
|
|
151
245
|
readonly alias: PropertyKey;
|
|
152
|
-
} ?
|
|
246
|
+
} ? HostReaches<R, C, T, D, Visited> : G extends {
|
|
153
247
|
readonly lifetime: {
|
|
154
248
|
readonly kind: 'root';
|
|
155
249
|
};
|
|
156
|
-
} ? never :
|
|
250
|
+
} ? never : G extends {
|
|
157
251
|
readonly lifetime: {
|
|
158
252
|
readonly kind: 'transient';
|
|
159
253
|
};
|
|
160
|
-
} ?
|
|
161
|
-
type
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
254
|
+
} ? HostReaches<R, C, T, D, Visited> : Captured<D> : never : never : never;
|
|
255
|
+
type HostReaches<R extends Registrations, C, V, K, Visited> = HostReach<R, C, Dependencies<V> | AliasKeys<V>, Visited> | HostFollow<R, C, ExportReaches<C, K>, Visited> | HostCollection<R, C, CollectionKeys<V>, Visited>;
|
|
256
|
+
type HostFollow<R extends Registrations, C, X, Visited> = X extends {
|
|
257
|
+
readonly kind: 'scoped';
|
|
258
|
+
readonly key: infer S;
|
|
259
|
+
} ? Captured<S> : X extends {
|
|
260
|
+
readonly kind: 'collection';
|
|
261
|
+
readonly key: infer T;
|
|
262
|
+
} ? HostCollection<R, C, T, Visited> : X extends {
|
|
263
|
+
readonly key: infer D;
|
|
264
|
+
} ? HostReach<R, C, D, Visited> : never;
|
|
265
|
+
type HostCollection<R extends Registrations, C, T, Visited> = T extends symbol ? Collected<T> extends Visited ? never : HostContributions<R, C, T, Visited | Collected<T>> | HostFollow<R, C, GroupReaches<C, T>, Visited | Collected<T>> : never;
|
|
266
|
+
type HostContributions<R extends Registrations, C, T, Visited, I = Extract<C, ContributionConstraint>> = I extends ContributionConstraint ? TokenKey<I['token']> extends T ? Members<I['registration']> extends infer M ? M extends Registration ? ProviderGraphContract<M> extends infer G ? G extends {
|
|
267
|
+
readonly kind: 'opaque';
|
|
268
|
+
} ? never : G extends {
|
|
269
|
+
readonly lifetime: {
|
|
270
|
+
readonly kind: 'root';
|
|
165
271
|
};
|
|
166
|
-
} ?
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
export type CheckedLifetimes<R extends Registrations, C extends NeedConstraint> = [
|
|
195
|
-
Captives<R, C>
|
|
196
|
-
] extends [never] ? unknown : unknown extends CheckDependencyCompatibility<R> & CheckDependencyCompleteness<R> & CheckedConstraints<C, R> & CompleteConstraints<C, R> ? Unsatisfied<`root lifetime cannot capture scoped dependency: ${CaptiveText<Captives<R, C>>}`, {
|
|
272
|
+
} ? never : G extends {
|
|
273
|
+
readonly lifetime: {
|
|
274
|
+
readonly kind: 'transient';
|
|
275
|
+
};
|
|
276
|
+
} ? HostReaches<R, C, M, never, Visited> : Captured<ContributionSite> : never : never : never : never : never;
|
|
277
|
+
type Captive<Root, X> = X extends Captured<infer D> ? {
|
|
278
|
+
readonly root: Root;
|
|
279
|
+
readonly dependency: D;
|
|
280
|
+
} : never;
|
|
281
|
+
type RootCaptives<R extends Registrations, C, Keys extends keyof R> = {
|
|
282
|
+
[K in Keys]: [StrictMembers<R[K]>] extends [never] ? never : Captive<K, HostReaches<R, C, StrictMembers<R[K]>, K, K>>;
|
|
283
|
+
}[Keys];
|
|
284
|
+
type ContributionRootCaptives<R extends Registrations, C, I = Extract<C, ContributionConstraint>> = I extends ContributionConstraint ? [StrictMembers<I['registration']>] extends [never] ? never : Captive<ContributionSite, HostReaches<R, C, StrictMembers<I['registration']>, never, never>> : never;
|
|
285
|
+
type ObligationCaptives<R extends Registrations, C, O = C> = O extends {
|
|
286
|
+
readonly kind: 'root-reach';
|
|
287
|
+
readonly root: infer Root;
|
|
288
|
+
readonly reach: infer X;
|
|
289
|
+
} ? Captive<Root, HostFollow<R, C, X, never>> : O extends {
|
|
290
|
+
readonly kind: 'contribution-reach';
|
|
291
|
+
readonly policy: 'root';
|
|
292
|
+
readonly reach: infer X;
|
|
293
|
+
} ? Captive<ContributionSite, HostFollow<R, C, X, never>> : never;
|
|
294
|
+
type Captives<R extends Registrations, C> = RootCaptives<R, C, keyof R> | ContributionRootCaptives<R, C> | ObligationCaptives<R, C>;
|
|
295
|
+
/**
|
|
296
|
+
* Reject strict root providers that transitively capture scoped dependencies.
|
|
297
|
+
* @see https://dany-fedorov.github.io/di-bag/agent/errors.html#root-capture
|
|
298
|
+
*/
|
|
299
|
+
export type CheckedLifetimes<R extends Registrations, C extends NeedConstraint> = [NeedsLifetimeWalk<R, C>] extends [never] ? unknown : [Captives<R, C>] extends [never] ? unknown : unknown extends CheckDependencyCompatibility<R> & CheckDependencyCompleteness<R> & CheckedConstraints<C, R> & CompleteConstraints<C, R> ? Unsatisfied<`root lifetime cannot capture scoped dependency: ${CaptiveText<Captives<R, C>>}${SeeErrors<'root-capture'>}`, {
|
|
197
300
|
readonly captives: Captives<R, C>;
|
|
198
301
|
}> : unknown;
|
|
199
|
-
type
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
type
|
|
205
|
-
|
|
302
|
+
type OverrideCaptives<R extends Registrations, O extends Registrations, C> = RootCaptives<R, C, keyof O & keyof R>;
|
|
303
|
+
/**
|
|
304
|
+
* Reject root providers introduced by a scope override when they capture scoped dependencies.
|
|
305
|
+
* @see https://dany-fedorov.github.io/di-bag/agent/errors.html#root-capture
|
|
306
|
+
*/
|
|
307
|
+
export type CheckedScopeLifetimes<R extends Registrations, O extends Registrations, C = never> = [NeedsLifetimeWalk<R, C>] extends [never] ? unknown : [OverrideCaptives<R, O, C>] extends [never] ? unknown : Unsatisfied<`root lifetime cannot capture scoped dependency: ${CaptiveText<OverrideCaptives<R, O, C>>}${SeeErrors<'root-capture'>}`, {
|
|
308
|
+
readonly captives: OverrideCaptives<R, O, C>;
|
|
309
|
+
}>;
|
|
310
|
+
type PolicyOf<R extends Registrations, K, Visited> = K extends keyof R ? K extends Visited ? never : PolicyTarget<R, R[K], Visited | K> : never;
|
|
311
|
+
type PolicyTarget<R extends Registrations, V, Visited> = ProviderGraphContract<V> extends infer G ? G extends {
|
|
206
312
|
readonly sharedAlias: {
|
|
207
313
|
readonly registrations: infer P extends Registrations;
|
|
208
314
|
readonly source: infer K;
|
|
209
315
|
};
|
|
210
|
-
} ?
|
|
211
|
-
readonly lexical: {
|
|
212
|
-
readonly source: infer S;
|
|
213
|
-
readonly context: infer L extends LexicalContext;
|
|
214
|
-
};
|
|
215
|
-
} ? S extends keyof L['registrations'] ? PolicyGraph<H, L['registrations'][S], Enclosed<L, C>, Visited> : never : G extends {
|
|
316
|
+
} ? PolicyOf<P, K, never> : G extends {
|
|
216
317
|
readonly alias: infer K;
|
|
217
|
-
} ?
|
|
318
|
+
} ? PolicyOf<R, K, Visited> : G extends {
|
|
218
319
|
readonly lifetime: {
|
|
219
|
-
readonly kind: infer
|
|
320
|
+
readonly kind: infer L;
|
|
220
321
|
};
|
|
221
|
-
} ?
|
|
222
|
-
export type CanonicalLifetime<R extends Registrations, K extends keyof R> =
|
|
322
|
+
} ? L : 'scoped' : never;
|
|
323
|
+
export type CanonicalLifetime<R extends Registrations, K extends keyof R> = PolicyOf<R, K, never>;
|
|
223
324
|
export {};
|
package/dist/lifetime.d.ts
CHANGED
|
@@ -2,7 +2,10 @@ import type { Registration } from './registration';
|
|
|
2
2
|
import type { Provider, ProviderFactory, RetainedMetadata, ProviderAcquisitionMetadata, ProviderGraphContract, ProviderAcquiredValue } from './provider';
|
|
3
3
|
import type { GraphContract } from './token-types';
|
|
4
4
|
import type { Singleton, Unsatisfied } from './types';
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* Cache at the ownership-family root, once per scope, or once per resolution.
|
|
7
|
+
* @see https://dany-fedorov.github.io/di-bag/guides/tutorial.html#choose-root-scoped-or-transient-caching
|
|
8
|
+
*/
|
|
6
9
|
export type Lifetime = 'root' | 'scoped' | 'transient';
|
|
7
10
|
export interface LifetimePolicy {
|
|
8
11
|
readonly kind: Lifetime;
|