@azure-net/kit 6.0.0-next.8 → 6.0.0-next.9
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/README.md +6 -3
- package/dist/shared/boundary-provider/CompositionRoot.d.ts +4 -4
- package/dist/shared/boundary-provider/CompositionRoot.js +4 -4
- package/dist/shared/boundary-provider/Provider.d.ts +3 -2
- package/dist/shared/boundary-provider/Provider.js +14 -13
- package/dist/shared/index.d.ts +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -342,8 +342,8 @@ URLs; external navigation should use browser location APIs.
|
|
|
342
342
|
## CompositionRoot
|
|
343
343
|
|
|
344
344
|
`CreateServiceContainer` remains unchanged. `CompositionRoot` is a lazy class
|
|
345
|
-
facade over it, with a unique internal container
|
|
346
|
-
scope; `
|
|
345
|
+
facade over it, with a unique symbol as its internal container identity. It can be created at module
|
|
346
|
+
scope; `resolve()` resolves against the current SSR request or browser
|
|
347
347
|
cache, never a request captured by the constructor.
|
|
348
348
|
|
|
349
349
|
```ts
|
|
@@ -361,7 +361,7 @@ export const Identity = new CompositionRoot({
|
|
|
361
361
|
});
|
|
362
362
|
|
|
363
363
|
// Inside a presenter factory or an active request:
|
|
364
|
-
const { Account } = Identity.
|
|
364
|
+
const { Account } = Identity.resolve();
|
|
365
365
|
```
|
|
366
366
|
|
|
367
367
|
Dependencies accept both other `CompositionRoot` instances and existing
|
|
@@ -370,3 +370,6 @@ Only registration metadata is wrapped; there is no extra proxy around services
|
|
|
370
370
|
and no new caching layer. Repeated resolution preserves existing container/service
|
|
371
371
|
identity. If explicit cleanup is needed, use `await cleanupProvider(Identity.name)`.
|
|
372
372
|
Declare roots once, rather than creating new roots per component render.
|
|
373
|
+
`name` is now a symbol, not a serializable string. Its description is for diagnostics
|
|
374
|
+
only; equal descriptions never share a cache. Existing string keys passed directly
|
|
375
|
+
to `CreateServiceContainer` and `cleanupProvider` remain supported.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type ProviderSettings, type ProviderWithType } from './Provider.js';
|
|
2
2
|
type Services = Record<string, () => unknown>;
|
|
3
3
|
type Dependency = ProviderWithType<Services> | {
|
|
4
|
-
readonly
|
|
4
|
+
readonly resolve: ProviderWithType<Services>;
|
|
5
5
|
};
|
|
6
6
|
type Dependencies = Record<string, Dependency>;
|
|
7
7
|
type Providers<D extends Dependencies> = {
|
|
8
8
|
[K in keyof D]: D[K] extends {
|
|
9
|
-
readonly
|
|
9
|
+
readonly resolve: infer P extends ProviderWithType<Services>;
|
|
10
10
|
} ? P : D[K] extends ProviderWithType<Services> ? D[K] : never;
|
|
11
11
|
};
|
|
12
12
|
export type CompositionRootSettings<T extends Services, D extends Dependencies = Record<never, never>> = Omit<ProviderSettings<T, Providers<D>>, 'dependsOn'> & {
|
|
@@ -14,8 +14,8 @@ export type CompositionRootSettings<T extends Services, D extends Dependencies =
|
|
|
14
14
|
};
|
|
15
15
|
/** Holds registration metadata only. Resolving always uses the current request's container. */
|
|
16
16
|
export declare class CompositionRoot<T extends Services, D extends Dependencies = Record<never, never>> {
|
|
17
|
-
readonly name:
|
|
18
|
-
readonly
|
|
17
|
+
readonly name: symbol;
|
|
18
|
+
readonly resolve: ProviderWithType<T>;
|
|
19
19
|
constructor(settings: CompositionRootSettings<T, D>);
|
|
20
20
|
}
|
|
21
21
|
export {};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { CreateServiceContainer } from './Provider.js';
|
|
2
|
-
const toProvider = (dependency) => typeof dependency === 'function' ? dependency : dependency.
|
|
2
|
+
const toProvider = (dependency) => typeof dependency === 'function' ? dependency : dependency.resolve;
|
|
3
3
|
/** Holds registration metadata only. Resolving always uses the current request's container. */
|
|
4
4
|
export class CompositionRoot {
|
|
5
|
-
name =
|
|
6
|
-
|
|
5
|
+
name = Symbol('@azure-net/kit:CompositionRoot');
|
|
6
|
+
resolve;
|
|
7
7
|
constructor(settings) {
|
|
8
8
|
const dependsOn = Object.fromEntries(Object.entries(settings.dependsOn ?? {}).map(([key, dependency]) => [key, toProvider(dependency)]));
|
|
9
|
-
this.
|
|
9
|
+
this.resolve = CreateServiceContainer(this.name, { ...settings, dependsOn });
|
|
10
10
|
}
|
|
11
11
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export type ProviderKey = string | symbol;
|
|
1
2
|
type ServiceFactory<T> = () => T;
|
|
2
3
|
type ServiceMap = Record<string, ServiceFactory<unknown>>;
|
|
3
4
|
type ResolvedServices<T extends ServiceMap> = {
|
|
@@ -16,6 +17,6 @@ export interface ProviderSettings<T extends ServiceMap, D extends Record<string,
|
|
|
16
17
|
boot?: (services: ResolvedServices<T>) => undefined;
|
|
17
18
|
register: ProviderFactory<T, D>;
|
|
18
19
|
}
|
|
19
|
-
export declare const CreateServiceContainer: <T extends ServiceMap, D extends Record<string, ProviderWithType<ServiceMap>>>(name:
|
|
20
|
-
export declare function cleanupProvider(name:
|
|
20
|
+
export declare const CreateServiceContainer: <T extends ServiceMap, D extends Record<string, ProviderWithType<ServiceMap>>>(name: ProviderKey, settings: ProviderSettings<T, D>) => ProviderWithType<T>;
|
|
21
|
+
export declare function cleanupProvider(name: ProviderKey): Promise<void>;
|
|
21
22
|
export {};
|
|
@@ -67,19 +67,20 @@ const getConstructionStack = () => {
|
|
|
67
67
|
const context = RequestContext.current();
|
|
68
68
|
return (getContextInternals(context).boundaryProviderConstructionStack ??= []);
|
|
69
69
|
};
|
|
70
|
-
const runWithCycleGuard = (
|
|
70
|
+
const runWithCycleGuard = (provider, member, callback) => {
|
|
71
71
|
const stack = getConstructionStack();
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
const cycleStart = stack.findIndex((entry) => entry.provider === provider && entry.member === member);
|
|
73
|
+
const entry = { provider, member };
|
|
74
|
+
if (cycleStart !== -1) {
|
|
75
|
+
const cyclePath = [...stack.slice(cycleStart), entry].map(({ provider, member }) => `${String(provider)}.${member}`).join(' -> ');
|
|
75
76
|
throw new AzureNetKitInternalError(`[BoundaryProvider] Circular provider dependency detected: ${cyclePath}`);
|
|
76
77
|
}
|
|
77
|
-
stack.push(
|
|
78
|
+
stack.push(entry);
|
|
78
79
|
try {
|
|
79
80
|
return callback();
|
|
80
81
|
}
|
|
81
82
|
finally {
|
|
82
|
-
const idx = stack.lastIndexOf(
|
|
83
|
+
const idx = stack.lastIndexOf(entry);
|
|
83
84
|
if (idx !== -1)
|
|
84
85
|
stack.splice(idx, 1);
|
|
85
86
|
}
|
|
@@ -109,7 +110,7 @@ export const CreateServiceContainer = (name, settings) => {
|
|
|
109
110
|
}
|
|
110
111
|
});
|
|
111
112
|
}
|
|
112
|
-
factories = runWithCycleGuard(
|
|
113
|
+
factories = runWithCycleGuard(name, 'register', () => register(deps));
|
|
113
114
|
return factories;
|
|
114
115
|
};
|
|
115
116
|
const providerProxy = new Proxy({}, {
|
|
@@ -125,15 +126,15 @@ export const CreateServiceContainer = (name, settings) => {
|
|
|
125
126
|
return undefined;
|
|
126
127
|
if (Reflect.has(target, key))
|
|
127
128
|
return Reflect.get(target, key, receiver);
|
|
128
|
-
throw new AzureNetKitInternalError(`[BoundaryProvider] Service '${key}' not found in provider '${name}'`);
|
|
129
|
+
throw new AzureNetKitInternalError(`[BoundaryProvider] Service '${key}' not found in provider '${String(name)}'`);
|
|
129
130
|
}
|
|
130
131
|
const factory = factories[key];
|
|
131
132
|
if (typeof factory !== 'function') {
|
|
132
133
|
throw new AzureNetKitInternalError('[BoundaryProvider] Factory must be a function');
|
|
133
134
|
}
|
|
134
|
-
const instance = runWithCycleGuard(
|
|
135
|
+
const instance = runWithCycleGuard(name, key, () => factory());
|
|
135
136
|
if (instance && typeof instance?.then === 'function') {
|
|
136
|
-
throw new AzureNetKitInternalError(`[BoundaryProvider] Service '${key}' in provider '${name}' returned Promise.`);
|
|
137
|
+
throw new AzureNetKitInternalError(`[BoundaryProvider] Service '${key}' in provider '${String(name)}' returned Promise.`);
|
|
137
138
|
}
|
|
138
139
|
cache.set(key, instance);
|
|
139
140
|
return instance;
|
|
@@ -172,7 +173,7 @@ export const CreateServiceContainer = (name, settings) => {
|
|
|
172
173
|
const bootResult = boot(providerProxy);
|
|
173
174
|
if (bootResult && typeof bootResult.then === 'function') {
|
|
174
175
|
void Promise.resolve(bootResult).catch(() => undefined);
|
|
175
|
-
throw new AzureNetKitInternalError(`[BoundaryProvider] Boot for provider '${name}' must be synchronous.`);
|
|
176
|
+
throw new AzureNetKitInternalError(`[BoundaryProvider] Boot for provider '${String(name)}' must be synchronous.`);
|
|
176
177
|
}
|
|
177
178
|
setBootFlag(name, true);
|
|
178
179
|
}
|
|
@@ -192,10 +193,10 @@ export async function cleanupProvider(name) {
|
|
|
192
193
|
if (service && typeof service === 'object' && 'dispose' in service) {
|
|
193
194
|
try {
|
|
194
195
|
const disposeResult = service.dispose();
|
|
195
|
-
promises.push(Promise.resolve(disposeResult).catch((err) => console.error(`Error disposing ${name}.${key}:`, err)));
|
|
196
|
+
promises.push(Promise.resolve(disposeResult).catch((err) => console.error(`Error disposing ${String(name)}.${key}:`, err)));
|
|
196
197
|
}
|
|
197
198
|
catch (err) {
|
|
198
|
-
console.error(`Error disposing ${name}.${key}:`, err);
|
|
199
|
+
console.error(`Error disposing ${String(name)}.${key}:`, err);
|
|
199
200
|
}
|
|
200
201
|
}
|
|
201
202
|
}
|
package/dist/shared/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { UniversalCookie, UniversalCookieInstance, UniversalNamedCookieInstance, type CookieOptions } from './cookie/index.js';
|
|
2
2
|
export { BackgroundTask } from './background-task/index.js';
|
|
3
3
|
export { ClassMirror } from './class-mirror/index.js';
|
|
4
|
-
export { CreateServiceContainer, cleanupProvider, type ProviderSettings } from './boundary-provider/index.js';
|
|
4
|
+
export { CreateServiceContainer, cleanupProvider, type ProviderSettings, type ProviderKey } from './boundary-provider/index.js';
|
|
5
5
|
export { CompositionRoot, type CompositionRootSettings } from './boundary-provider/index.js';
|
|
6
6
|
export * from './app/index.js';
|
|
7
7
|
export { useLogger, LoggerErrors, type ILoggerError, type ILoggerSettings } from './logger/index.js';
|
package/package.json
CHANGED