di-bag 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +280 -0
- package/dist/acquisition-context.d.ts +42 -0
- package/dist/acquisition-context.js +19 -0
- package/dist/acquisition-family.d.ts +32 -0
- package/dist/acquisition-family.js +135 -0
- package/dist/acquisition-mode.d.ts +34 -0
- package/dist/acquisition-mode.js +35 -0
- package/dist/acquisition.d.ts +44 -0
- package/dist/acquisition.js +395 -0
- package/dist/alias-types.d.ts +22 -0
- package/dist/alias-types.js +2 -0
- package/dist/aliases.d.ts +4 -0
- package/dist/aliases.js +24 -0
- package/dist/composition.d.ts +41 -0
- package/dist/composition.js +45 -0
- package/dist/contribution-types.d.ts +57 -0
- package/dist/contribution-types.js +2 -0
- package/dist/contributions.d.ts +3 -0
- package/dist/contributions.js +11 -0
- package/dist/dependency-references.d.ts +52 -0
- package/dist/dependency-references.js +53 -0
- package/dist/di-bag.d.ts +247 -0
- package/dist/di-bag.js +211 -0
- package/dist/errors.d.ts +66 -0
- package/dist/errors.js +89 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +10 -0
- package/dist/inspection.d.ts +36 -0
- package/dist/inspection.js +2 -0
- package/dist/lifetime-types.d.ts +214 -0
- package/dist/lifetime-types.js +2 -0
- package/dist/lifetime.d.ts +42 -0
- package/dist/lifetime.js +31 -0
- package/dist/module-types.d.ts +182 -0
- package/dist/module-types.js +2 -0
- package/dist/module.d.ts +45 -0
- package/dist/module.js +118 -0
- package/dist/node.d.ts +4 -0
- package/dist/node.js +22 -0
- package/dist/observers.d.ts +71 -0
- package/dist/observers.js +58 -0
- package/dist/persistent-map.d.ts +29 -0
- package/dist/persistent-map.js +146 -0
- package/dist/persistent-sequence.d.ts +9 -0
- package/dist/persistent-sequence.js +20 -0
- package/dist/plugins.d.ts +32 -0
- package/dist/plugins.js +82 -0
- package/dist/provider-execution.d.ts +59 -0
- package/dist/provider-execution.js +271 -0
- package/dist/provider-operations.d.ts +59 -0
- package/dist/provider-operations.js +38 -0
- package/dist/provider.d.ts +199 -0
- package/dist/provider.js +158 -0
- package/dist/registration.d.ts +32 -0
- package/dist/registration.js +45 -0
- package/dist/replacement-types.d.ts +30 -0
- package/dist/replacement-types.js +2 -0
- package/dist/runtime.d.ts +90 -0
- package/dist/runtime.js +428 -0
- package/dist/scope-selection.d.ts +6 -0
- package/dist/scope-selection.js +62 -0
- package/dist/scope-types.d.ts +58 -0
- package/dist/scope-types.js +2 -0
- package/dist/startup.d.ts +14 -0
- package/dist/startup.js +141 -0
- package/dist/token-types.d.ts +67 -0
- package/dist/token-types.js +2 -0
- package/dist/tokens.d.ts +47 -0
- package/dist/tokens.js +69 -0
- package/dist/types.d.ts +174 -0
- package/dist/types.js +2 -0
- package/package.json +64 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import type { ContributionConstraint, CheckedContributions, CompleteContributions, RenamedContribution, ModuleContributionConstraints } from './contribution-types';
|
|
2
|
+
import type { Module } from './module';
|
|
3
|
+
import type { Registrations } from './registration';
|
|
4
|
+
import type { Entry, Needs, RegistrationsFromEntries, ServicesOf, Singleton, Unsatisfied } from './types';
|
|
5
|
+
import type { MetadataKeyUnion, Provider, ProviderOutput, ProviderNamedDependencies, ProviderRegistrationMetadata, ProviderAcquisitionMetadata, ProviderAcquiredValue, ProviderGraphContract, ProviderRequiredTokens, ProviderOptionalTokens, ProviderCollectionTokens, BoundToken } from './provider';
|
|
6
|
+
import type { TokenDependencyContract, WrongToken, MissingToken } from './token-types';
|
|
7
|
+
import type { TokenBase, TokenKey, TokenService } from './tokens';
|
|
8
|
+
import type { LifetimeObligation, PrivateLifetimes, LexicalProvider, RenamedLifetimeObligation, EnclosedLifetimeObligation } from './lifetime-types';
|
|
9
|
+
export type NeedConstraint = LifetimeObligation | ContributionConstraint | {
|
|
10
|
+
readonly kind: 'all';
|
|
11
|
+
readonly token: TokenBase;
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: 'export' | 'external';
|
|
14
|
+
readonly consumer: string | symbol;
|
|
15
|
+
readonly needs: object;
|
|
16
|
+
} | {
|
|
17
|
+
readonly kind: 'token-export' | 'token-external' | 'optional-token-export' | 'optional-token-external';
|
|
18
|
+
readonly consumer: string | symbol;
|
|
19
|
+
readonly token: TokenBase;
|
|
20
|
+
} | {
|
|
21
|
+
readonly kind: 'opaque';
|
|
22
|
+
};
|
|
23
|
+
type WrongConstraint<C extends NeedConstraint, A extends Registrations> = C extends {
|
|
24
|
+
readonly needs: object;
|
|
25
|
+
readonly consumer: string | symbol;
|
|
26
|
+
} ? {
|
|
27
|
+
[K in keyof C['needs'] & keyof ServicesOf<A>]: ServicesOf<A>[K] extends C['needs'][K] ? never : C['consumer'];
|
|
28
|
+
}[keyof C['needs'] & keyof ServicesOf<A>] : never;
|
|
29
|
+
type MissingConstraint<C extends NeedConstraint, Available extends object> = C extends {
|
|
30
|
+
readonly needs: object;
|
|
31
|
+
} ? Exclude<keyof C['needs'], keyof Available> : never;
|
|
32
|
+
type WrongTokenConstraint<C, A extends Registrations> = C extends {
|
|
33
|
+
kind: 'contribution' | 'all';
|
|
34
|
+
} ? never : C extends {
|
|
35
|
+
token: infer T;
|
|
36
|
+
} ? WrongToken<T, A> : C extends {
|
|
37
|
+
kind: 'opaque';
|
|
38
|
+
} ? 'opaque' : never;
|
|
39
|
+
type MissingTokenConstraint<C, A extends Registrations> = C extends {
|
|
40
|
+
kind: 'contribution' | 'all' | 'optional-token-export' | 'optional-token-external';
|
|
41
|
+
} ? never : C extends {
|
|
42
|
+
token: infer T;
|
|
43
|
+
} ? MissingToken<T, A> : C extends {
|
|
44
|
+
kind: 'opaque';
|
|
45
|
+
} ? 'opaque' : never;
|
|
46
|
+
type ConstraintRelationships<C, A extends object> = C extends {
|
|
47
|
+
readonly consumer: infer Consumer;
|
|
48
|
+
readonly needs: infer N;
|
|
49
|
+
} ? {
|
|
50
|
+
[K in keyof N & keyof A]: A[K] extends N[K] ? never : {
|
|
51
|
+
consumer: Consumer;
|
|
52
|
+
dependency: K;
|
|
53
|
+
expected: N[K];
|
|
54
|
+
provided: A[K];
|
|
55
|
+
};
|
|
56
|
+
}[keyof N & keyof A] : never;
|
|
57
|
+
type MissingConstraintRelationships<C, A extends object> = C extends {
|
|
58
|
+
readonly consumer: infer Consumer;
|
|
59
|
+
readonly needs: infer N;
|
|
60
|
+
} ? {
|
|
61
|
+
[K in Exclude<keyof N, keyof A>]: {
|
|
62
|
+
consumer: Consumer;
|
|
63
|
+
dependency: K;
|
|
64
|
+
expected: N[K];
|
|
65
|
+
provided: undefined;
|
|
66
|
+
};
|
|
67
|
+
}[Exclude<keyof N, keyof A>] : never;
|
|
68
|
+
export type CheckedConstraints<C extends NeedConstraint, A extends Registrations> = [
|
|
69
|
+
WrongConstraint<C, A> | WrongTokenConstraint<C, A>
|
|
70
|
+
] extends [never] ? CheckedContributions<C, A> : Unsatisfied<'provided service does not satisfy its consumer dependency', {
|
|
71
|
+
tokens: WrongConstraint<C, A> | WrongTokenConstraint<C, A>;
|
|
72
|
+
relationships: ConstraintRelationships<C, ServicesOf<A>>;
|
|
73
|
+
}>;
|
|
74
|
+
export type IncrementalConstraints<C extends NeedConstraint, MC extends NeedConstraint, Old extends Registrations, Incoming extends Registrations> = [Extract<C | MC, {
|
|
75
|
+
kind: 'contribution' | 'all' | 'opaque' | 'lifetime';
|
|
76
|
+
}>] extends [never] ? unknown extends CheckedConstraints<C, Incoming> ? CheckedConstraints<MC, import('./types').OverrideRegistrations<Old, Incoming>> : CheckedConstraints<C, Incoming> : CheckedConstraints<C | MC, import('./types').OverrideRegistrations<Old, Incoming>>;
|
|
77
|
+
export type CompleteConstraints<C extends NeedConstraint, A extends Registrations> = [
|
|
78
|
+
MissingConstraint<C, ServicesOf<A>> | MissingTokenConstraint<C, A>
|
|
79
|
+
] extends [never] ? CompleteContributions<C, A> : Unsatisfied<'required service registrations are missing', {
|
|
80
|
+
missing: MissingConstraint<C, ServicesOf<A>> | MissingTokenConstraint<C, A>;
|
|
81
|
+
relationships: MissingConstraintRelationships<C, ServicesOf<A>>;
|
|
82
|
+
}>;
|
|
83
|
+
type Constraint<K extends string | symbol, N, Keys extends keyof N, Kind extends string> = [
|
|
84
|
+
Keys
|
|
85
|
+
] extends [never] ? never : {
|
|
86
|
+
readonly consumer: K;
|
|
87
|
+
readonly needs: Pick<N, Keys>;
|
|
88
|
+
readonly kind: Kind;
|
|
89
|
+
};
|
|
90
|
+
export type RegistrationConstraints<V extends Registrations[string], R extends Registrations, Public extends keyof R, K extends string | symbol = string | symbol> = Constraint<K, Needs<V>, Extract<keyof Needs<V>, Public>, 'export'> | Constraint<K, Needs<V>, Exclude<keyof Needs<V>, keyof R>, 'external'> | (ProviderCollectionTokens<V> extends infer T ? T extends TokenBase ? {
|
|
91
|
+
readonly kind: 'all';
|
|
92
|
+
readonly token: T;
|
|
93
|
+
} : never : never) | TokenConstraint<K, ProviderRequiredTokens<V>, R, Public> | TokenConstraint<K, ProviderOptionalTokens<V>, R, Public, true> | ([ProviderGraphContract<V>] extends [TokenDependencyContract<readonly TokenBase[], TokenBase, readonly TokenBase[]>] ? never : {
|
|
94
|
+
readonly kind: 'opaque';
|
|
95
|
+
});
|
|
96
|
+
/** Retained requirements of a module's public and private registrations. */
|
|
97
|
+
export type ModuleConstraints<R extends Registrations, Public extends keyof R> = {
|
|
98
|
+
[K in keyof R & (string | symbol)]: RegistrationConstraints<R[K], R, Public, K>;
|
|
99
|
+
}[keyof R & (string | symbol)] | PrivateLifetimes<R, Public>;
|
|
100
|
+
type TokenConstraint<K extends string | symbol, T, R, Public, Optional extends boolean = false> = T extends TokenBase ? TokenKey<T> extends Public ? {
|
|
101
|
+
readonly consumer: K;
|
|
102
|
+
readonly token: T;
|
|
103
|
+
readonly kind: Optional extends true ? 'optional-token-export' : 'token-export';
|
|
104
|
+
} : TokenKey<T> extends keyof R ? never : {
|
|
105
|
+
readonly consumer: K;
|
|
106
|
+
readonly token: T;
|
|
107
|
+
readonly kind: Optional extends true ? 'optional-token-external' : 'token-external';
|
|
108
|
+
} : never;
|
|
109
|
+
type Intersect<U> = (U extends unknown ? (value: U) => void : never) extends (value: infer I) => void ? I : never;
|
|
110
|
+
type External<C> = C extends {
|
|
111
|
+
kind: 'external';
|
|
112
|
+
needs: infer N;
|
|
113
|
+
} ? N : C extends {
|
|
114
|
+
kind: 'token-external';
|
|
115
|
+
token: infer T;
|
|
116
|
+
} ? Record<TokenKey<T>, TokenService<T>> : C extends {
|
|
117
|
+
kind: 'optional-token-external';
|
|
118
|
+
token: infer T;
|
|
119
|
+
} ? Partial<Record<TokenKey<T>, TokenService<T>>> : never;
|
|
120
|
+
export type ExternalRequirements<C> = [External<C>] extends [never] ? Readonly<{}> : Readonly<Intersect<External<C>>>;
|
|
121
|
+
type OutputFactory<O> = () => O;
|
|
122
|
+
export type PublicRegistrations<P extends object> = {
|
|
123
|
+
[K in keyof P]: () => P[K];
|
|
124
|
+
};
|
|
125
|
+
export type PublicProvider<R> = R extends Registrations[string] ? unknown extends ProviderNamedDependencies<R> ? R : [ProviderGraphContract<R>] extends [TokenDependencyContract<readonly TokenBase[], TokenBase, readonly TokenBase[]>] ? [Extract<ProviderGraphContract<R>, {
|
|
126
|
+
readonly lifetime: unknown;
|
|
127
|
+
} | {
|
|
128
|
+
readonly alias: PropertyKey;
|
|
129
|
+
}>] extends [never] ? [MetadataKeyUnion<ProviderRegistrationMetadata<R>> | BoundToken<R>] extends [never] ? ProviderAcquisitionMetadata<R> extends readonly [] ? [ProviderAcquiredValue<R>] extends [Awaited<ProviderOutput<R>>] ? [Awaited<ProviderOutput<R>>] extends [ProviderAcquiredValue<R>] ? OutputFactory<ProviderOutput<R>> : RetainedPublicProvider<R> : RetainedPublicProvider<R> : RetainedPublicProvider<R> : RetainedPublicProvider<R> : RetainedPublicProvider<R> : R : never;
|
|
130
|
+
type PublicGraph<G> = G extends TokenDependencyContract<readonly TokenBase[], TokenBase, readonly TokenBase[]> ? {
|
|
131
|
+
[K in keyof G]: K extends 'required' | 'optional' | 'all' ? readonly [] : G[K];
|
|
132
|
+
} : never;
|
|
133
|
+
type RetainedPublicProvider<R extends Registrations[string]> = Provider<OutputFactory<ProviderOutput<R>>, ProviderRegistrationMetadata<R> & object, ProviderAcquisitionMetadata<R>, PublicGraph<ProviderGraphContract<R>>, ProviderAcquiredValue<R>>;
|
|
134
|
+
/** Project registrations to dependency-free public descriptions while retaining behavioral contracts. */
|
|
135
|
+
export type PublicProviders<R extends object> = {
|
|
136
|
+
[K in keyof R]: PublicProvider<R[K]>;
|
|
137
|
+
};
|
|
138
|
+
/** Project selected module exports while retaining their lexical private graph where required. */
|
|
139
|
+
export type ModulePublicProviders<R extends Registrations, P extends keyof R> = {
|
|
140
|
+
[K in P]: LexicalProvider<PublicProvider<R[K]>, R, P, K>;
|
|
141
|
+
};
|
|
142
|
+
/** Rename one string key in an object contract. */
|
|
143
|
+
export type Renamed<P extends object, Old extends string, New extends string> = {
|
|
144
|
+
[K in keyof P as K extends Old ? New : K]: P[K];
|
|
145
|
+
};
|
|
146
|
+
export type RenamedConstraints<C extends NeedConstraint, Old extends string, New extends string> = C extends ContributionConstraint ? RenamedContribution<C, Old, New> : C extends LifetimeObligation ? RenamedLifetimeObligation<C, Old, New> : C extends {
|
|
147
|
+
readonly kind: 'export';
|
|
148
|
+
readonly consumer: string | symbol;
|
|
149
|
+
readonly needs: object;
|
|
150
|
+
} ? {
|
|
151
|
+
readonly consumer: C['consumer'];
|
|
152
|
+
readonly needs: Renamed<C['needs'], Old, New>;
|
|
153
|
+
readonly kind: 'export';
|
|
154
|
+
} : C;
|
|
155
|
+
export type RenameKeys<P, Old extends string, New extends string> = Singleton<Old> extends true ? Singleton<New> extends true ? Old extends keyof P ? New extends Exclude<keyof P, Old> ? InvalidRename : unknown : InvalidRename : InvalidRename : InvalidRename;
|
|
156
|
+
type InvalidRename = Unsatisfied<'renameExport requires an existing export and a noncolliding singleton string-literal name', {}>;
|
|
157
|
+
/**
|
|
158
|
+
* Re-scope every constraint a builder retained from installed modules and
|
|
159
|
+
* contributions when that builder seals into a module with exports `P`.
|
|
160
|
+
* Needs on an export stay checkable by the host; needs satisfied privately are
|
|
161
|
+
* final and drop; unsatisfied needs remain external requirements of the module.
|
|
162
|
+
*/
|
|
163
|
+
export type SealedConstraints<C extends NeedConstraint, R extends Registrations, P extends keyof R> = C extends ContributionConstraint ? ModuleContributionConstraints<C, R, P> : C extends LifetimeObligation ? EnclosedLifetimeObligation<C, R, P> : C extends {
|
|
164
|
+
readonly kind: 'export' | 'external';
|
|
165
|
+
readonly consumer: infer K extends string | symbol;
|
|
166
|
+
readonly needs: infer N extends object;
|
|
167
|
+
} ? Constraint<K, N, Extract<keyof N, P>, 'export'> | Constraint<K, N, Exclude<keyof N, keyof R>, 'external'> : C extends {
|
|
168
|
+
readonly kind: 'token-export' | 'token-external';
|
|
169
|
+
readonly consumer: infer K extends string | symbol;
|
|
170
|
+
readonly token: infer T;
|
|
171
|
+
} ? TokenConstraint<K, T, R, P> : C extends {
|
|
172
|
+
readonly kind: 'optional-token-export' | 'optional-token-external';
|
|
173
|
+
readonly consumer: infer K extends string | symbol;
|
|
174
|
+
readonly token: infer T;
|
|
175
|
+
} ? TokenConstraint<K, T, R, P, true> : C;
|
|
176
|
+
/** Every constraint a sealed module carries: its own registrations' needs plus re-scoped retained constraints. */
|
|
177
|
+
export type ModuleSealedConstraints<E extends Entry, C extends NeedConstraint, P extends keyof RegistrationsFromEntries<E>> = ModuleConstraints<RegistrationsFromEntries<E>, P> | SealedConstraints<C, RegistrationsFromEntries<E>, P>;
|
|
178
|
+
/** Extract a readonly map of services publicly exposed by a module. */
|
|
179
|
+
export type ModuleExportedServices<M> = M extends Module<infer P, infer _R, infer _C, infer _D> ? Readonly<P> : never;
|
|
180
|
+
/** Extract a readonly map of services a module requires from its host. */
|
|
181
|
+
export type ModuleRequiredServices<M> = M extends Module<infer _P, infer R, infer _C, infer _D> ? Readonly<R> : never;
|
|
182
|
+
export {};
|
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { BindingGraph, BindingKey, GraphDescription } from './runtime';
|
|
2
|
+
import type { Registrations } from './registration';
|
|
3
|
+
import type { NeedConstraint, PublicRegistrations, Renamed, RenamedConstraints, RenameKeys } from './module-types';
|
|
4
|
+
import type { RenamedLifetimeProviders } from './lifetime-types';
|
|
5
|
+
interface ModuleDescription {
|
|
6
|
+
/** The sealed graph: every binding that was retained when the builder sealed. */
|
|
7
|
+
readonly graph: GraphDescription;
|
|
8
|
+
/** Public slot -> original local name. Factory parameter names never change. */
|
|
9
|
+
readonly exports: ReadonlyMap<BindingKey, BindingKey>;
|
|
10
|
+
}
|
|
11
|
+
declare const moduleInvariant: unique symbol;
|
|
12
|
+
/**
|
|
13
|
+
* A sealed, non-resolving module with private registrations and selected public exports.
|
|
14
|
+
* Create modules through {@link DiBagApi.createBuilder} and {@link Builder.buildModule}; this
|
|
15
|
+
* type-only class has no public constructor.
|
|
16
|
+
*/
|
|
17
|
+
declare class Module<P extends object, R extends object, C extends NeedConstraint = never, D extends Registrations = PublicRegistrations<P>> {
|
|
18
|
+
private readonly nominal;
|
|
19
|
+
/** @internal */
|
|
20
|
+
readonly [moduleInvariant]: (value: [P, R, C, D]) => [P, R, C, D];
|
|
21
|
+
constructor(description: ModuleDescription);
|
|
22
|
+
/**
|
|
23
|
+
* Return a module view with one string-named export renamed.
|
|
24
|
+
* Factory dependency names and private identities remain unchanged.
|
|
25
|
+
* @param oldKey - An existing public string export.
|
|
26
|
+
* @param newKey - A noncolliding string-literal export name.
|
|
27
|
+
* @returns A new sealed module, or the same instance when both names are equal.
|
|
28
|
+
* @throws If runtime input names are invalid, absent, or collide.
|
|
29
|
+
*/
|
|
30
|
+
renameExport<const Old extends string, const New extends string>(oldKey: Old & RenameKeys<P, Old, New>, newKey: New & RenameKeys<P, Old, New>): Module<Renamed<P, Old, New>, R, RenamedConstraints<C, Old, New>, RenamedLifetimeProviders<D, Old, New>>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Seal a builder graph into a module. Runtime validation only: the selected
|
|
34
|
+
* keys must be a tuple of existing public names or typed tokens.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export declare function sealModule(graph: BindingGraph, keys: unknown): Module<never, never, never, never>;
|
|
38
|
+
/**
|
|
39
|
+
* Internal normalization: every install receives fresh binding identities at
|
|
40
|
+
* every nesting depth. Names resolve lexically: a binding's own local names
|
|
41
|
+
* (from an inner installation) win, then the module's public names, then the
|
|
42
|
+
* installing host's public slots.
|
|
43
|
+
*/
|
|
44
|
+
export declare function moduleGraph(value: object): GraphDescription;
|
|
45
|
+
export type { Module };
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sealModule = sealModule;
|
|
4
|
+
exports.moduleGraph = moduleGraph;
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const tokens_1 = require("./tokens");
|
|
7
|
+
const descriptions = new WeakMap();
|
|
8
|
+
/**
|
|
9
|
+
* A sealed, non-resolving module with private registrations and selected public exports.
|
|
10
|
+
* Create modules through {@link DiBagApi.createBuilder} and {@link Builder.buildModule}; this
|
|
11
|
+
* type-only class has no public constructor.
|
|
12
|
+
*/
|
|
13
|
+
class Module {
|
|
14
|
+
constructor(description) {
|
|
15
|
+
descriptions.set(this, { graph: description.graph, exports: new Map(description.exports) });
|
|
16
|
+
Object.freeze(this);
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Return a module view with one string-named export renamed.
|
|
20
|
+
* Factory dependency names and private identities remain unchanged.
|
|
21
|
+
* @param oldKey - An existing public string export.
|
|
22
|
+
* @param newKey - A noncolliding string-literal export name.
|
|
23
|
+
* @returns A new sealed module, or the same instance when both names are equal.
|
|
24
|
+
* @throws If runtime input names are invalid, absent, or collide.
|
|
25
|
+
*/
|
|
26
|
+
renameExport(oldKey, newKey) {
|
|
27
|
+
const description = descriptions.get(this);
|
|
28
|
+
if (typeof oldKey !== 'string' || !description.exports.has(oldKey))
|
|
29
|
+
throw (0, errors_1.libraryError)('DI_BAG_INVALID_EXPORT', 'renameExport requires an existing export', { operation: 'renameExport', oldKey, newKey });
|
|
30
|
+
if (typeof newKey !== 'string')
|
|
31
|
+
throw (0, errors_1.libraryError)('DI_BAG_INVALID_EXPORT', 'renameExport requires a string name', { operation: 'renameExport', oldKey, newKey });
|
|
32
|
+
if (oldKey === newKey)
|
|
33
|
+
return this;
|
|
34
|
+
if (description.exports.has(newKey))
|
|
35
|
+
throw (0, errors_1.libraryError)('DI_BAG_INVALID_EXPORT', `duplicate export: ${newKey}`, { operation: 'renameExport', oldKey, newKey });
|
|
36
|
+
const exports = new Map(description.exports);
|
|
37
|
+
const localName = exports.get(oldKey);
|
|
38
|
+
exports.delete(oldKey);
|
|
39
|
+
exports.set(newKey, localName);
|
|
40
|
+
return new Module({ graph: description.graph, exports });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Seal a builder graph into a module. Runtime validation only: the selected
|
|
45
|
+
* keys must be a tuple of existing public names or typed tokens.
|
|
46
|
+
* @internal
|
|
47
|
+
*/
|
|
48
|
+
function sealModule(graph, keys) {
|
|
49
|
+
if (!Array.isArray(keys))
|
|
50
|
+
throw (0, errors_1.libraryError)('DI_BAG_INVALID_EXPORT', 'buildModule requires a key tuple', { operation: 'buildModule' });
|
|
51
|
+
// Snapshot indexed entries before a custom iterator can substitute keys.
|
|
52
|
+
const selected = [];
|
|
53
|
+
const length = keys.length;
|
|
54
|
+
for (let index = 0; index < length; index++)
|
|
55
|
+
selected[index] = keys[index];
|
|
56
|
+
const exports = new Map();
|
|
57
|
+
for (const value of selected) {
|
|
58
|
+
const key = typeof value === 'string' ? value : (0, tokens_1.readTokenKey)(value);
|
|
59
|
+
if (!graph.hasPublic(key))
|
|
60
|
+
throw (0, errors_1.libraryError)('DI_BAG_INVALID_EXPORT', 'buildModule accepts existing names or typed tokens only', { operation: 'buildModule' });
|
|
61
|
+
exports.set(key, key);
|
|
62
|
+
}
|
|
63
|
+
return new Module({ graph: graph.describe(), exports });
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Internal normalization: every install receives fresh binding identities at
|
|
67
|
+
* every nesting depth. Names resolve lexically: a binding's own local names
|
|
68
|
+
* (from an inner installation) win, then the module's public names, then the
|
|
69
|
+
* installing host's public slots.
|
|
70
|
+
*/
|
|
71
|
+
function moduleGraph(value) {
|
|
72
|
+
const description = descriptions.get(value);
|
|
73
|
+
if (!description)
|
|
74
|
+
throw (0, errors_1.libraryError)('DI_BAG_INVALID_MODULE', 'installModule requires a genuine module', { operation: 'installModule' });
|
|
75
|
+
const { graph, exports } = description;
|
|
76
|
+
const ids = new Map();
|
|
77
|
+
for (const [id, binding] of graph.bindings)
|
|
78
|
+
ids.set(id, Symbol(binding.label));
|
|
79
|
+
const exportNames = new Map();
|
|
80
|
+
for (const [publicKey, localKey] of exports)
|
|
81
|
+
exportNames.set(localKey, publicKey);
|
|
82
|
+
// Every public name of the sealed graph, as seen by its own bindings.
|
|
83
|
+
const scopeNames = new Map();
|
|
84
|
+
for (const [key, id] of graph.publicSlots) {
|
|
85
|
+
const exported = exportNames.get(key);
|
|
86
|
+
scopeNames.set(key, exported === undefined ? { kind: 'private', id: ids.get(id) } : { kind: 'public', key: exported });
|
|
87
|
+
}
|
|
88
|
+
const remap = (ref) => ref.kind === 'private'
|
|
89
|
+
? { kind: 'private', id: ids.get(ref.id) ?? ref.id }
|
|
90
|
+
: scopeNames.get(ref.key) ?? ref;
|
|
91
|
+
// Share one lexical map per distinct inner scope so the graph deduplicates snapshots.
|
|
92
|
+
const nested = new Map();
|
|
93
|
+
const localNamesFor = (binding) => {
|
|
94
|
+
if (binding.localNames.size === 0)
|
|
95
|
+
return scopeNames;
|
|
96
|
+
let names = nested.get(binding.localNames);
|
|
97
|
+
if (!names) {
|
|
98
|
+
const merged = new Map(scopeNames);
|
|
99
|
+
for (const [name, ref] of binding.localNames)
|
|
100
|
+
merged.set(name, remap(ref));
|
|
101
|
+
names = merged;
|
|
102
|
+
nested.set(binding.localNames, names);
|
|
103
|
+
}
|
|
104
|
+
return names;
|
|
105
|
+
};
|
|
106
|
+
const bindings = new Map();
|
|
107
|
+
for (const [id, binding] of graph.bindings) {
|
|
108
|
+
const fresh = ids.get(id);
|
|
109
|
+
bindings.set(fresh, { id: fresh, label: binding.label, registration: binding.registration, localNames: localNamesFor(binding) });
|
|
110
|
+
}
|
|
111
|
+
const publicSlots = new Map();
|
|
112
|
+
for (const [publicKey, localKey] of exports)
|
|
113
|
+
publicSlots.set(publicKey, ids.get(graph.publicSlots.get(localKey)));
|
|
114
|
+
const contributions = new Map();
|
|
115
|
+
for (const [key, group] of graph.contributions ?? [])
|
|
116
|
+
contributions.set(key, group.map(id => ids.get(id)));
|
|
117
|
+
return { bindings, publicSlots, contributions };
|
|
118
|
+
}
|
package/dist/node.d.ts
ADDED
package/dist/node.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.DiBag = void 0;
|
|
18
|
+
const { isPromise } = require('node:util/types');
|
|
19
|
+
const di_bag_1 = require("./di-bag");
|
|
20
|
+
__exportStar(require("./index"), exports);
|
|
21
|
+
/** The Node/Bun facade preconfigured with the host's native-Promise classifier. */
|
|
22
|
+
exports.DiBag = di_bag_1.DiBag.withConfiguration({ runtime: { isNativePromise: isPromise } });
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { AcquisitionMetadataPresence } from './inspection';
|
|
2
|
+
import type { Lifetime } from './lifetime';
|
|
3
|
+
/** Identity shared by lifecycle events for one owning scope. */
|
|
4
|
+
export interface ScopeEventFields {
|
|
5
|
+
/** The scope that owns the transition. */
|
|
6
|
+
readonly scopeId: symbol;
|
|
7
|
+
/** The tracked parent, present only for child-scope events. */
|
|
8
|
+
readonly parentScopeId?: symbol;
|
|
9
|
+
}
|
|
10
|
+
/** Copied binding and acquisition details carried by acquisition and cleanup events. */
|
|
11
|
+
export interface AcquisitionEventFields {
|
|
12
|
+
readonly scopeId: symbol;
|
|
13
|
+
readonly bindingId: symbol;
|
|
14
|
+
readonly acquisitionId: symbol;
|
|
15
|
+
readonly label: string;
|
|
16
|
+
readonly lifetime: Lifetime;
|
|
17
|
+
readonly registrationMetadata: Readonly<object>;
|
|
18
|
+
readonly acquisitionMetadata: AcquisitionMetadataPresence<readonly unknown[]>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A frozen discriminated lifecycle transition emitted after the corresponding state change.
|
|
22
|
+
* Narrow on `kind` to access failure, cleanup outcome, or disposal-index fields.
|
|
23
|
+
*/
|
|
24
|
+
export type LifecycleEvent = (ScopeEventFields & {
|
|
25
|
+
readonly kind: 'scope-opened';
|
|
26
|
+
}) | (ScopeEventFields & {
|
|
27
|
+
readonly kind: 'scope-closing';
|
|
28
|
+
}) | (ScopeEventFields & {
|
|
29
|
+
readonly kind: 'scope-closed';
|
|
30
|
+
}) | (ScopeEventFields & {
|
|
31
|
+
readonly kind: 'scope-close-failed';
|
|
32
|
+
readonly error: unknown;
|
|
33
|
+
}) | (AcquisitionEventFields & {
|
|
34
|
+
readonly kind: 'acquisition-started';
|
|
35
|
+
}) | (AcquisitionEventFields & {
|
|
36
|
+
readonly kind: 'acquisition-ready';
|
|
37
|
+
}) | (AcquisitionEventFields & {
|
|
38
|
+
readonly kind: 'cleanup-started';
|
|
39
|
+
}) | (AcquisitionEventFields & {
|
|
40
|
+
readonly kind: 'acquisition-failed';
|
|
41
|
+
readonly error: unknown;
|
|
42
|
+
}) | (AcquisitionEventFields & {
|
|
43
|
+
readonly kind: 'cleanup-failed';
|
|
44
|
+
readonly error: unknown;
|
|
45
|
+
readonly disposalSequence: number;
|
|
46
|
+
}) | (AcquisitionEventFields & {
|
|
47
|
+
readonly kind: 'cleanup-completed';
|
|
48
|
+
readonly outcome: 'success' | 'failure';
|
|
49
|
+
});
|
|
50
|
+
/** A failure thrown or rejected by an observer together with its original event. */
|
|
51
|
+
export interface ObserverFailure {
|
|
52
|
+
readonly error: unknown;
|
|
53
|
+
readonly event: LifecycleEvent;
|
|
54
|
+
}
|
|
55
|
+
/** An asynchronous, non-gating callback for frozen lifecycle events. */
|
|
56
|
+
export type ObserverCallback = (this: void, event: LifecycleEvent) => unknown;
|
|
57
|
+
/** Reports failures from one observer's event callback; its own failures are consumed. */
|
|
58
|
+
export type ObserverErrorCallback = (this: void, failure: ObserverFailure) => unknown;
|
|
59
|
+
/** Both callbacks required by {@link DiBagApi.withConfiguration}. */
|
|
60
|
+
export interface ObserverOptions {
|
|
61
|
+
/** Receives events in transition and observer-registration order on a microtask queue. */
|
|
62
|
+
readonly onEvent: ObserverCallback;
|
|
63
|
+
/** Receives synchronous throws and rejected results from `onEvent`. */
|
|
64
|
+
readonly onError: ObserverErrorCallback;
|
|
65
|
+
}
|
|
66
|
+
export declare class LifecycleObservers {
|
|
67
|
+
private readonly callbacks;
|
|
68
|
+
private constructor();
|
|
69
|
+
static append(previous: LifecycleObservers | undefined, options: ObserverOptions): LifecycleObservers;
|
|
70
|
+
emit(event: LifecycleEvent): void;
|
|
71
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LifecycleObservers = void 0;
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
const then = (Promise.prototype.then);
|
|
6
|
+
const ignore = () => { };
|
|
7
|
+
/** Assimilate only callback results, always through a library-owned Promise. */
|
|
8
|
+
function monitor(result, failed) {
|
|
9
|
+
if ((typeof result !== 'object' || result === null) && typeof result !== 'function')
|
|
10
|
+
return;
|
|
11
|
+
const pending = new Promise(resolve => { resolve(result); });
|
|
12
|
+
then.call(pending, ignore, failed);
|
|
13
|
+
}
|
|
14
|
+
// One lazy queue preserves ordering when a callback observes multiple facades.
|
|
15
|
+
let queue;
|
|
16
|
+
class LifecycleObservers {
|
|
17
|
+
callbacks;
|
|
18
|
+
constructor(callbacks) {
|
|
19
|
+
this.callbacks = callbacks;
|
|
20
|
+
}
|
|
21
|
+
static append(previous, options) {
|
|
22
|
+
if (typeof options !== 'object' || options === null)
|
|
23
|
+
throw (0, errors_1.libraryTypeError)('DI_BAG_INVALID_CONFIGURATION', 'withConfiguration observers require onEvent and onError callbacks', { operation: 'withConfiguration' });
|
|
24
|
+
const { onEvent, onError } = options;
|
|
25
|
+
if (typeof onEvent !== 'function' || typeof onError !== 'function')
|
|
26
|
+
throw (0, errors_1.libraryTypeError)('DI_BAG_INVALID_CONFIGURATION', 'withConfiguration observers require onEvent and onError callbacks', { operation: 'withConfiguration' });
|
|
27
|
+
return new LifecycleObservers([...(previous?.callbacks ?? []), Object.freeze({ onEvent, onError })]);
|
|
28
|
+
}
|
|
29
|
+
emit(event) {
|
|
30
|
+
const delivery = { event: Object.freeze(event), callbacks: this.callbacks };
|
|
31
|
+
if (queue) {
|
|
32
|
+
queue.push(delivery);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
queue = [delivery];
|
|
36
|
+
queueMicrotask(() => {
|
|
37
|
+
// Detach this batch: reentrant transitions schedule another microtask.
|
|
38
|
+
const deliveries = queue;
|
|
39
|
+
queue = undefined;
|
|
40
|
+
for (const { event, callbacks } of deliveries)
|
|
41
|
+
for (const { onEvent, onError } of callbacks) {
|
|
42
|
+
const failed = (error) => {
|
|
43
|
+
try {
|
|
44
|
+
monitor(onError(Object.freeze({ error, event })), ignore);
|
|
45
|
+
}
|
|
46
|
+
catch { /* Error reporting must not recursively report itself. */ }
|
|
47
|
+
};
|
|
48
|
+
try {
|
|
49
|
+
monitor(onEvent(event), failed);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
failed(error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.LifecycleObservers = LifecycleObservers;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Private immutable hash trie. Versions share nodes, never map wrappers or caches. */
|
|
2
|
+
type Key = string | symbol;
|
|
3
|
+
type Leaf<V> = {
|
|
4
|
+
readonly kind: 'leaf';
|
|
5
|
+
readonly hash: number;
|
|
6
|
+
readonly key: Key;
|
|
7
|
+
readonly value: V;
|
|
8
|
+
};
|
|
9
|
+
type Collision<V> = {
|
|
10
|
+
readonly kind: 'collision';
|
|
11
|
+
readonly hash: number;
|
|
12
|
+
readonly entries: readonly Leaf<V>[];
|
|
13
|
+
};
|
|
14
|
+
type Branch<V> = {
|
|
15
|
+
readonly kind: 'branch';
|
|
16
|
+
readonly bitmap: number;
|
|
17
|
+
readonly children: readonly Node<V>[];
|
|
18
|
+
};
|
|
19
|
+
type Node<V> = Leaf<V> | Collision<V> | Branch<V>;
|
|
20
|
+
export declare class PersistentMap<V> {
|
|
21
|
+
private readonly root?;
|
|
22
|
+
constructor(root?: Node<V> | undefined);
|
|
23
|
+
get(key: Key): V | undefined;
|
|
24
|
+
has(key: Key): boolean;
|
|
25
|
+
set(key: Key, value: V): PersistentMap<V>;
|
|
26
|
+
delete(key: Key): PersistentMap<V>;
|
|
27
|
+
[Symbol.iterator](): IterableIterator<readonly [Key, V]>;
|
|
28
|
+
}
|
|
29
|
+
export {};
|