injeca 0.2.0 → 0.4.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.
@@ -0,0 +1,763 @@
1
+ import { Logg } from "@guiiai/logg";
2
+ /**
3
+ * The logger of a container. `warn` always writes. The other methods write the lifecycle trace, and injeca calls them only when the `logging` option of the container is `true`.
4
+ */
5
+ interface Logger {
6
+ provide: (name: string, dependencies: string[]) => void;
7
+ invoke: (dependencies: string[]) => void;
8
+ beforeRun: (name: string) => void;
9
+ run: (name: string, duration: number) => void;
10
+ hookOnStart: (name: string) => void;
11
+ hookOnStartComplete: (name: string, duration: number) => void;
12
+ hookOnStop: (name: string) => void;
13
+ hookOnStopComplete: (name: string, duration: number) => void;
14
+ running: () => void;
15
+ /** A call that injeca handled in another way than the caller asked, for example a re-entrant `dispose()` that resolved at once. */
16
+ warn: (message: string) => void;
17
+ }
18
+ interface LoggerOptions {
19
+ /**
20
+ * Writes the lifecycle trace: `provide`, `invoke`, builds, `start` and `stop` hooks, and `running`. It defaults to `false`.
21
+ * A child container uses the value of its parent, unless it sets its own. `warn` does not depend on it.
22
+ */
23
+ logging?: boolean;
24
+ /**
25
+ * The logger. It defaults to the logger of the parent container, then to `createDefaultLogger()`.
26
+ * To also silence the warnings, for example in tests, pass `createNoopLogger()`.
27
+ */
28
+ logger?: Logger;
29
+ }
30
+ declare function createDefaultLogger(): Logger;
31
+ declare function createLoggLogger(logg: Logg): Logger;
32
+ declare function createNoopLogger(): Logger;
33
+ /**
34
+ * The time source of a container. Pass a custom clock to control time in tests.
35
+ *
36
+ * The reconciler uses it for requeue delays and hook timeouts, and `timeout()` uses it for its signal. The work queue does not use it. It drains on microtasks.
37
+ */
38
+ interface Clock {
39
+ now: () => number;
40
+ /** Calls `callback` after `ms` milliseconds. Returns a function that cancels the call. */
41
+ setTimeout: (callback: () => void, ms: number) => () => void;
42
+ }
43
+ declare const systemClock: Clock;
44
+ type OwnerValue = string | number | boolean | null | readonly OwnerValue[] | {
45
+ readonly [key: string]: OwnerValue;
46
+ };
47
+ /** A serializable record that names the owner of a container. injeca does not interpret its fields. */
48
+ interface OwnerRecord {
49
+ readonly [key: string]: OwnerValue;
50
+ }
51
+ /**
52
+ * The phase of a service.
53
+ *
54
+ * - `declared`: a transaction reserved the service. Consumers cannot resolve it.
55
+ * - `pending`: the service waits until its required dependencies are available, or for `start()`. It runs no code.
56
+ * - `conflict`: an exclusive key has more than one declaration in one resolution area. It runs no code.
57
+ * - `building`: `build` runs. The instance is not visible.
58
+ * - `starting`: the instance is published. The `start` hooks run.
59
+ * - `retrying`: a `start` hook returned `requeue`. The service keeps its instance and its dependencies, and waits for the next attempt. It is not available.
60
+ * - `running`: the instance serves calls. Only this phase has a status.
61
+ * - `stopping`: the service waits until its holders release it. Then the `stop` hooks and the effect disposers run.
62
+ * - `failed`: a build failed, a hook returned `fail`, retries ended, or a timeout failed. `start()` or `replace()` moves the service out of it.
63
+ * - `withdrawn`: the registration is withdrawn and the instance is disposed.
64
+ */
65
+ type ServicePhase = 'declared' | 'pending' | 'conflict' | 'building' | 'starting' | 'retrying' | 'running' | 'stopping' | 'failed' | 'withdrawn';
66
+ /**
67
+ * The status of a running instance.
68
+ *
69
+ * - `ready`: the service works at full quality.
70
+ * - `degraded`: every call completes, at lower quality.
71
+ * - `unavailable`: the instance exists, but calls fail now.
72
+ */
73
+ type ServiceStatus = 'ready' | 'degraded' | 'unavailable';
74
+ /** One link of a cause chain. A remote plugin uses it to report a cause inside the plugin. */
75
+ interface StatusCause {
76
+ readonly key: string;
77
+ readonly owner: OwnerRecord;
78
+ readonly status: ServiceStatus;
79
+ readonly reason?: string;
80
+ readonly cause?: StatusCause;
81
+ }
82
+ /** The second argument of `setStatus` in its object form. */
83
+ interface SetStatusOptions {
84
+ readonly reason?: string;
85
+ readonly cause?: StatusCause;
86
+ }
87
+ /** The identity and the effective status of one resolved dependency. */
88
+ interface DependencyState {
89
+ /** The key name of the provider. For `firstAvailable`, it is the selected key. */
90
+ readonly provider: string;
91
+ readonly instanceId: string;
92
+ readonly status: ServiceStatus;
93
+ }
94
+ /** One dependency in the snapshot of a dependency hook. */
95
+ interface DependencySnapshot<T> extends DependencyState {
96
+ readonly value: T;
97
+ }
98
+ /** The difference of one dependency between the last handled snapshot and the current snapshot. `undefined` means no provider. */
99
+ interface DependencyChange {
100
+ /** The local name in `dependsOn`. */
101
+ readonly key: string;
102
+ readonly from: DependencyState | undefined;
103
+ readonly to: DependencyState | undefined;
104
+ }
105
+ /** The state that `watch` sends. `phase` is `undefined` when no provider exists for the key. */
106
+ interface ServiceState {
107
+ readonly phase: ServicePhase | undefined;
108
+ /** The effective status. It is `undefined` outside `running`. */
109
+ readonly status: ServiceStatus | undefined;
110
+ readonly instanceId: string | undefined;
111
+ readonly reason?: string;
112
+ readonly cause?: StatusCause;
113
+ }
114
+ /**
115
+ * One reason why a service is not `running`, or why it is not available.
116
+ *
117
+ * - `missing-provider`: no container in the resolution chain has a provider for a required key.
118
+ * - `failed-dependency`: the provider of a required key is in `failed`.
119
+ * - `pending-dependency`: the provider of a required key exists, but it does not run yet.
120
+ * - `unavailable-dependency`: the provider of a required key runs, but its effective status is `unavailable`. `cause` starts the cause chain.
121
+ * - `rejected-provider`: a resolution condition rejected the provider of a required key. `owner` and `metadata` name the provider.
122
+ * - `conflicting-dependency`: the provider of a required key is an exclusive key with more than one declaration.
123
+ * - `conflict`: the service itself is an exclusive key with more than one declaration. `owners` lists the owner of each declaration.
124
+ * - `failed`: the service itself is in `failed`.
125
+ * - `retrying`: a `start` hook of the service returned `requeue`. `attempt` is the attempt that requeued, and `reason` is its reason.
126
+ * - `not-started`: the container of the service is not started, and nothing demands the service.
127
+ */
128
+ type ServiceBlocker = {
129
+ readonly kind: 'missing-provider';
130
+ readonly dependency: string;
131
+ readonly key: string;
132
+ } | {
133
+ readonly kind: 'failed-dependency';
134
+ readonly dependency: string;
135
+ readonly key: string;
136
+ readonly error: unknown;
137
+ } | {
138
+ readonly kind: 'pending-dependency';
139
+ readonly dependency: string;
140
+ readonly key: string;
141
+ readonly phase: ServicePhase;
142
+ } | {
143
+ readonly kind: 'unavailable-dependency';
144
+ readonly dependency: string;
145
+ readonly key: string;
146
+ readonly cause: StatusCause;
147
+ } | {
148
+ readonly kind: 'rejected-provider';
149
+ readonly dependency: string;
150
+ readonly key: string;
151
+ readonly owner: OwnerRecord;
152
+ readonly metadata: Readonly<Record<string, unknown>>;
153
+ } | {
154
+ readonly kind: 'conflicting-dependency';
155
+ readonly dependency: string;
156
+ readonly key: string;
157
+ readonly owners: readonly OwnerRecord[];
158
+ } | {
159
+ readonly kind: 'conflict';
160
+ readonly key: string;
161
+ readonly owners: readonly OwnerRecord[];
162
+ } | {
163
+ readonly kind: 'failed';
164
+ readonly error: unknown;
165
+ } | {
166
+ readonly kind: 'retrying';
167
+ readonly attempt: number;
168
+ readonly reason?: string;
169
+ } | {
170
+ readonly kind: 'not-started';
171
+ };
172
+ /** Why a service or an invocation is not `running`, or why it is not available. `graph(container).explain()` returns it. */
173
+ interface ServiceExplanation {
174
+ readonly name: string;
175
+ readonly kind: 'service' | 'invocation';
176
+ readonly owner: OwnerRecord;
177
+ /** The phase of the service. It is `undefined` for a key without a service. Then `owner` is the owner of the container of the query. */
178
+ readonly phase: ServicePhase | undefined;
179
+ /** The effective status of a running service. */
180
+ readonly status?: ServiceStatus;
181
+ readonly reason?: string;
182
+ /** The cause chain of the effective status. */
183
+ readonly cause?: StatusCause;
184
+ readonly blockers: readonly ServiceBlocker[];
185
+ /**
186
+ * The new instance of a replacement in progress, from `replace()`, a transaction, or a make-before-break rebuild.
187
+ * The old instance serves until the new one completes `start`. Its phase and blockers tell why the swap did not happen yet.
188
+ */
189
+ readonly replacement?: ServiceExplanation;
190
+ }
191
+ declare const providedType: unique symbol;
192
+ declare const optionalType: unique symbol;
193
+ declare const fallbackType: unique symbol;
194
+ declare const conditionalType: unique symbol;
195
+ declare const optionalBrand: unique symbol;
196
+ declare const fallbackBrand: unique symbol;
197
+ declare const conditionBrand: unique symbol;
198
+ type DependencyMap = Record<string, any>;
199
+ /**
200
+ * A typed identity for a service. `provide` and `defineKey` return one.
201
+ */
202
+ interface ProvidedKey<Key, T, _D extends DependencyMap | undefined> {
203
+ key: Key;
204
+ /** `true` for a key from `defineKey(name, { exclusive: true })`. Only one provider can exist in its resolution area. */
205
+ readonly exclusive?: boolean;
206
+ /** Carries the value type. It has no value at run time. */
207
+ readonly [providedType]?: T;
208
+ }
209
+ /** A key name, a key from `defineKey`, or the result of `provide`. */
210
+ type KeyReference = string | ProvidedKey<string, unknown, DependencyMap | undefined>;
211
+ interface KeyOptions {
212
+ /** Makes an exclusive key. Two or more providers in one resolution area all go to `conflict`. */
213
+ readonly exclusive?: boolean;
214
+ }
215
+ /** A frozen record on a registration. Resolution conditions and graph queries read it. */
216
+ type ServiceMetadata = Readonly<Record<string, unknown>>;
217
+ /** The read-only information about a provider that a resolution condition gets. */
218
+ interface ProviderInfo {
219
+ /** The key name of the provider. */
220
+ readonly key: string;
221
+ /** The owner record of the container of the provider. */
222
+ readonly owner: OwnerRecord;
223
+ readonly metadata: ServiceMetadata;
224
+ }
225
+ /** A resolution condition from `where`. It must be pure and synchronous. A thrown error counts as `false`. */
226
+ type ResolutionCondition = (provider: ProviderInfo) => boolean;
227
+ type ProvidedBy<T> = string & {
228
+ __providedType?: T;
229
+ };
230
+ /** A dependency marker from `optional(key)`. A missing provider gives `undefined` and does not keep the dependent `pending`. */
231
+ interface OptionalDependency<T> {
232
+ readonly [optionalBrand]: true;
233
+ /** The names of the keys, in order. More than one name comes from `optional(firstAvailable(...))`. */
234
+ readonly dependencies: readonly string[];
235
+ /** The resolution condition from `where`, when the marker has one. It filters each candidate. */
236
+ readonly condition?: ResolutionCondition;
237
+ /** The resolution conditions of single candidates, from `firstAvailable(where(key, condition), ...)`, by key name. */
238
+ readonly candidateConditions?: Readonly<Record<string, ResolutionCondition>>;
239
+ /** Carries the value type. It has no value at run time. */
240
+ readonly [optionalType]?: T;
241
+ }
242
+ /** A required dependency marker from `where(key, condition)`. A provider that the condition rejects does not resolve. */
243
+ interface ConditionalDependency<T> {
244
+ readonly [conditionBrand]: true;
245
+ /** The name of the key. */
246
+ readonly dependencies: readonly string[];
247
+ readonly condition: ResolutionCondition;
248
+ /** Carries the value type. It has no value at run time. */
249
+ readonly [conditionalType]?: T;
250
+ }
251
+ /**
252
+ * A fallback dependency marker from `firstAvailable(...keys)`. It resolves to the first available provider in the list.
253
+ * A resolution condition filters the candidates. The selection is the first available candidate that passes.
254
+ */
255
+ interface FallbackDependency<T> {
256
+ readonly [fallbackBrand]: true;
257
+ /** The names of the keys, in order of preference. */
258
+ readonly dependencies: readonly string[];
259
+ /** The resolution condition from `where(firstAvailable(...), condition)`. It filters each candidate. */
260
+ readonly condition?: ResolutionCondition;
261
+ /** The resolution conditions of single candidates, from `firstAvailable(where(key, condition), ...)`, by key name. */
262
+ readonly candidateConditions?: Readonly<Record<string, ResolutionCondition>>;
263
+ /** Carries the value type. It has no value at run time. */
264
+ readonly [fallbackType]?: T;
265
+ }
266
+ /** One value of a `dependsOn` declaration: a name, a key, an optional marker, a fallback marker, or a condition marker. */
267
+ type DependencyDeclaration = string | ProvidedKey<string, unknown, DependencyMap | undefined> | OptionalDependency<unknown> | FallbackDependency<unknown> | ConditionalDependency<unknown>;
268
+ type DependencyDeclarations = Record<string, DependencyDeclaration>;
269
+ /** The resolved value of an optional dependency. */
270
+ interface OptionalValue<T> {
271
+ readonly value: T;
272
+ readonly status: ServiceStatus;
273
+ }
274
+ type InferProvided<T> = T extends ProvidedKey<any, infer P, any> ? P : T extends ProvidedBy<infer P> ? P : T extends ConditionalDependency<infer P> ? P : never;
275
+ /** A required dependency is its value. An optional dependency is `{ value, status }` or `undefined`. */
276
+ type ResolveDependencyDeclaration<Deps extends DependencyDeclarations> = { [K in keyof Deps]: Deps[K] extends OptionalDependency<infer T> ? OptionalValue<T> | undefined : Deps[K] extends FallbackDependency<infer T> ? T : Deps[K] extends ConditionalDependency<infer T> ? T : Deps[K] extends ProvidedKey<any, infer T, any> ? T : Deps[K] extends ProvidedBy<infer T> ? T : any; };
277
+ /** The hooks that `on` registers in `build` and in an invocation callback. */
278
+ type ServiceHook = 'start' | 'stop' | 'dependency-change' | 'dependency-status-change';
279
+ /** The hooks that run when a dependency changes. Their context has `dependsOn` and `changes`. */
280
+ type DependencyHook = 'dependency-change' | 'dependency-status-change';
281
+ interface DecisionOptions {
282
+ readonly reason?: string;
283
+ }
284
+ interface ExponentialBackoff {
285
+ readonly kind: 'exponential';
286
+ readonly initial: number;
287
+ readonly max: number;
288
+ readonly jitter?: number;
289
+ }
290
+ interface RequeueOptions extends DecisionOptions {
291
+ /** A fixed delay in milliseconds. */
292
+ readonly after?: number;
293
+ readonly backoff?: ExponentialBackoff;
294
+ readonly maxAttempts?: number;
295
+ }
296
+ /** The fields that injeca fills from the hook context. */
297
+ interface DecisionSource {
298
+ /** The name of the service, or of the invocation. */
299
+ readonly service: string;
300
+ readonly instanceId: string;
301
+ readonly hook: ServiceHook;
302
+ readonly attempt: number;
303
+ /** The time of the container clock when `decide` created the decision. */
304
+ readonly issuedAt: number;
305
+ }
306
+ interface HandledDecision extends DecisionSource, DecisionOptions {
307
+ readonly kind: 'handled';
308
+ }
309
+ interface RebuildDecision extends DecisionSource, DecisionOptions {
310
+ readonly kind: 'rebuild';
311
+ }
312
+ interface RequeueDecision extends DecisionSource, RequeueOptions {
313
+ readonly kind: 'requeue';
314
+ }
315
+ interface StopDecision extends DecisionSource, DecisionOptions {
316
+ readonly kind: 'stop';
317
+ }
318
+ interface FailDecision extends DecisionSource, DecisionOptions {
319
+ readonly kind: 'fail';
320
+ readonly error: unknown;
321
+ }
322
+ /**
323
+ * The value that a hook returns to tell injeca what to do next. Only `decide` in the hook context creates it.
324
+ */
325
+ type Decision = HandledDecision | RebuildDecision | RequeueDecision | StopDecision | FailDecision;
326
+ type DecisionKind = Decision['kind'];
327
+ /** The decision kinds that a hook accepts. */
328
+ type HookDecisionKind<H extends ServiceHook> = H extends 'start' ? 'handled' | 'requeue' | 'stop' | 'fail' : H extends 'stop' ? 'handled' | 'fail' : DecisionKind;
329
+ /** The decisions that a hook accepts. */
330
+ type DecisionFor<H extends ServiceHook> = Extract<Decision, {
331
+ readonly kind: HookDecisionKind<H>;
332
+ }>;
333
+ /**
334
+ * The value that a hook handler can return.
335
+ *
336
+ * A decision tells injeca what to do next. Any other value, for example the result of `app.listen()`, selects the default of the hook.
337
+ * So the shorthand `on('start', () => app.listen(port))` type-checks.
338
+ * The `decide` of each hook has only the methods that the hook accepts. A decision from another run fails at run time with `invalid-decision`.
339
+ */
340
+ type HookResult<_H extends ServiceHook = ServiceHook> = unknown;
341
+ /** All decision constructors. A hook context has only the constructors that its hook accepts. */
342
+ interface Decide {
343
+ readonly handled: (options?: DecisionOptions) => HandledDecision;
344
+ readonly rebuild: (options?: DecisionOptions) => RebuildDecision;
345
+ readonly requeue: (options?: RequeueOptions) => RequeueDecision;
346
+ readonly stop: (options?: DecisionOptions) => StopDecision;
347
+ readonly fail: (error: unknown, options?: DecisionOptions) => FailDecision;
348
+ }
349
+ /** The `decide` object of a hook context. A method that the hook does not accept is a type error. */
350
+ type DecideFor<H extends ServiceHook> = Pick<Decide, HookDecisionKind<H>>;
351
+ /** The context of an `onTimeout` handler. `hook` tells which hook timed out, and selects the methods of `decide`. */
352
+ type TimeoutContext = { readonly [H in 'start' | 'stop']: {
353
+ readonly hook: H;
354
+ /** The configured timeout in milliseconds. */
355
+ readonly timeoutMs: number;
356
+ /** The attempt of the hook run that timed out. */
357
+ readonly attempt: number;
358
+ /** The last decision of this hook for this instance. */
359
+ readonly previous: DecisionFor<H> | undefined;
360
+ readonly decide: DecideFor<H>;
361
+ }; }['start' | 'stop'];
362
+ /**
363
+ * Decides what happens when a `start` or `stop` hook times out. It runs synchronously.
364
+ *
365
+ * For a `start` timeout, a value that is not a decision or a promise gives `fail` with `hook-timeout`.
366
+ * For a `stop` timeout, it gives `handled`, so that the stop continues. A throw gives `fail` for both hooks.
367
+ */
368
+ type TimeoutHandler = (context: TimeoutContext) => unknown;
369
+ /** The timeouts of the `start` and `stop` hooks in milliseconds. A missing value means no timeout. */
370
+ interface HookTimeouts {
371
+ /** The time for all `start` hooks of one attempt. */
372
+ start?: number;
373
+ /** The time for all `stop` hooks and effect disposers of one instance. */
374
+ stop?: number;
375
+ }
376
+ /**
377
+ * The retry and timeout options of a service or a container. injeca has no built-in values.
378
+ *
379
+ * A service value overrides a container value. A container without a value uses the value of its nearest ancestor that has one.
380
+ */
381
+ interface PolicyOptions {
382
+ /**
383
+ * Limits the attempts of a hook that returns `requeue`. A `maxAttempts` on the decision overrides it.
384
+ * Without a limit, the requeue continues until the dependency or the container changes.
385
+ */
386
+ maxAttempts?: number;
387
+ /** The timeouts of the `start` and `stop` hooks. Each field overrides the same field of the container. */
388
+ timeouts?: HookTimeouts;
389
+ /** Decides what happens when a hook times out. Without it, a `start` timeout gives `fail` with `hook-timeout`, and a `stop` timeout counts as `handled`. */
390
+ onTimeout?: TimeoutHandler;
391
+ }
392
+ interface Container {
393
+ logger: Logger;
394
+ /** `true` when the container writes the lifecycle trace to `logger`. A child container reads it when it is created. */
395
+ logging: boolean;
396
+ readonly owner: OwnerRecord;
397
+ readonly parent: Container | undefined;
398
+ }
399
+ /** The options of `createContainer`. `maxAttempts`, `timeouts`, and `onTimeout` apply to each service of the container and of its descendants that does not set its own value. */
400
+ interface ContainerOptions extends LoggerOptions, PolicyOptions {
401
+ /** Makes a child container. It resolves keys from its own registrations and from those of its ancestors. */
402
+ parent?: Container;
403
+ /** A serializable owner record. It defaults to an empty record. */
404
+ owner?: OwnerRecord;
405
+ /** The time source. Only a root container reads it. A child container uses the clock of its root. */
406
+ clock?: Clock;
407
+ /**
408
+ * The random source for the backoff jitter. It returns a number from 0 up to, but not including, 1. It defaults to `Math.random`.
409
+ * Only a root container reads it. A child container uses the random source of its root.
410
+ */
411
+ random?: () => number;
412
+ /**
413
+ * The keys that do not resolve from the ancestors. The container and its descendants resolve them only from their own registrations.
414
+ * An isolated key also ends the resolution area of an exclusive key of an ancestor.
415
+ */
416
+ isolate?: readonly KeyReference[];
417
+ }
418
+ type HookDependency<V> = [Exclude<V, undefined>] extends [OptionalValue<infer T>] ? DependencySnapshot<T> | undefined : DependencySnapshot<V>;
419
+ /** The snapshot of a dependency hook: each dependency with its value, `instanceId`, and effective status. An optional dependency can be `undefined`. */
420
+ type HookDependencies<D> = D extends DependencyMap ? { readonly [K in keyof D]: HookDependency<D[K]>; } : Readonly<Record<string, DependencySnapshot<unknown> | undefined>>;
421
+ type Disposer = () => void | Promise<void>;
422
+ type EffectSetup = () => Disposer | void;
423
+ /** The context that each hook handler gets. */
424
+ interface HookContext<H extends ServiceHook> {
425
+ /** Creates the decisions that this hook accepts. Each decision is bound to this run. */
426
+ readonly decide: DecideFor<H>;
427
+ /** The number of the current run of this hook for this instance. The first run is `1`. A requeue adds one. */
428
+ readonly attempt: number;
429
+ /** The last decision of this hook for this instance. */
430
+ readonly previous: DecisionFor<H> | undefined;
431
+ /** Aborts when a newer target supersedes this run, or when the instance is disposed. */
432
+ readonly signal: AbortSignal;
433
+ }
434
+ /** The context of `dependency-change` and `dependency-status-change`. */
435
+ interface DependencyHookContext<H extends DependencyHook, D = undefined> extends HookContext<H> {
436
+ /** The current snapshot of all dependencies. Decide from it, not from one change only. */
437
+ readonly dependsOn: HookDependencies<D>;
438
+ /** The difference between the last handled snapshot and the current snapshot. */
439
+ readonly changes: readonly DependencyChange[];
440
+ }
441
+ type HookContextFor<H extends ServiceHook, D = undefined> = H extends DependencyHook ? DependencyHookContext<H, D> : HookContext<H>;
442
+ type HookHandler<H extends ServiceHook = ServiceHook, D = undefined> = (context: HookContextFor<H, D>) => HookResult<H> | Promise<HookResult<H>>;
443
+ /** The lifecycle members that `build` and an invocation callback get. */
444
+ interface LifecycleContext<D = undefined> {
445
+ /** Registers a hook. It is valid only while `build` or the callback runs. A later call throws `hook-outside-build`. */
446
+ on: <H extends ServiceHook>(hook: H, handler: HookHandler<H, D>) => void;
447
+ /** Runs `setup` and stores the returned disposer on the instance. */
448
+ effect: (setup: EffectSetup) => void;
449
+ /** Aborts when a newer target supersedes the build, or when the instance is disposed. */
450
+ signal: AbortSignal;
451
+ }
452
+ type BuildContext<D extends DependencyMap | undefined = undefined> = {
453
+ container: Container;
454
+ name: string;
455
+ /** The id of the instance that this build creates. */
456
+ instanceId: string;
457
+ /** The owner record of the consumer container, when the service has `perConsumer: true`. */
458
+ consumer?: OwnerRecord;
459
+ /**
460
+ * Sets the own status of the instance. It stays valid after `build` returns.
461
+ * After the instance is disposed, a call has no effect and goes to the observe stream.
462
+ */
463
+ setStatus: (status: ServiceStatus, reasonOrOptions?: string | SetStatusOptions) => void;
464
+ } & LifecycleContext<D> & (D extends undefined ? {
465
+ dependsOn?: unknown;
466
+ } : {
467
+ dependsOn: D;
468
+ });
469
+ type InvocationContext<D = undefined> = LifecycleContext<D>;
470
+ /** One service, invocation, or declared service in a graph query result. */
471
+ interface GraphService {
472
+ /** The key name of the service, or the name of the invocation. */
473
+ readonly key: string;
474
+ readonly kind: 'service' | 'invocation';
475
+ /** The owner record of the container of the service. */
476
+ readonly owner: OwnerRecord;
477
+ readonly phase: ServicePhase;
478
+ /** The effective status of a running service. */
479
+ readonly status?: ServiceStatus;
480
+ readonly instanceId?: string;
481
+ readonly metadata: ServiceMetadata;
482
+ readonly exclusive: boolean;
483
+ readonly perConsumer: boolean;
484
+ /** `true` for a registration that `provide(..., { export: true })` exports to the resolution area of the parent container. */
485
+ readonly exported: boolean;
486
+ /** The owner record of the consumer container of a `perConsumer` instance. */
487
+ readonly consumer?: OwnerRecord;
488
+ }
489
+ /**
490
+ * How a declared edge resolves now.
491
+ *
492
+ * - `resolved`: a running provider serves the edge.
493
+ * - `not-running`: a provider exists, but it does not run.
494
+ * - `missing`: no provider exists.
495
+ * - `rejected`: a resolution condition rejected the provider.
496
+ * - `conflict`: the provider is an exclusive key with more than one declaration.
497
+ */
498
+ type GraphResolution = 'resolved' | 'not-running' | 'missing' | 'rejected' | 'conflict';
499
+ /** One declared edge of a service. */
500
+ interface GraphDependency {
501
+ /** The local name in `dependsOn`. */
502
+ readonly name: string;
503
+ /** The key names of the edge. A `firstAvailable` edge has its list. */
504
+ readonly keys: readonly string[];
505
+ readonly optional: boolean;
506
+ readonly fallback: boolean;
507
+ /** `true` when the edge has a resolution condition from `where`. */
508
+ readonly conditional: boolean;
509
+ readonly resolution: GraphResolution;
510
+ /** The provider that the edge selects now. A `missing` edge has none. */
511
+ readonly provider?: GraphService;
512
+ }
513
+ /**
514
+ * A proposed change for `impact`.
515
+ *
516
+ * - `withdraw`: the service of `key` is withdrawn.
517
+ * - `replace`: the service of `key` gets a new instance.
518
+ * - `dispose`: `container` and its descendants are disposed.
519
+ */
520
+ type ImpactChange = {
521
+ readonly kind: 'withdraw';
522
+ readonly key: KeyReference;
523
+ } | {
524
+ readonly kind: 'replace';
525
+ readonly key: KeyReference;
526
+ } | {
527
+ readonly kind: 'dispose';
528
+ readonly container: Container;
529
+ };
530
+ /** The services that a proposed change affects. Each list has only services that have an instance now. */
531
+ interface ImpactResult {
532
+ /** The services that stop: the withdrawn services, and the dependents that lose a required dependency. */
533
+ readonly stopped: readonly GraphService[];
534
+ /** The dependents that rebuild, because a dependency changes and they have no `dependency-change` handler. */
535
+ readonly rebuilt: readonly GraphService[];
536
+ /** The dependents that get a `dependency-change` call. Their handler decides what happens. */
537
+ readonly notified: readonly GraphService[];
538
+ }
539
+ /**
540
+ * The graph queries of a container. They are pure reads and never change state.
541
+ *
542
+ * The queries cover the container and its descendants. A key resolves as for a consumer in the container: from the container, then from its ancestors.
543
+ */
544
+ interface Graph {
545
+ /** All services and invocations, with the declared services of open transactions. */
546
+ readonly services: () => readonly GraphService[];
547
+ /** The declared edges of the service of `key`. */
548
+ readonly dependencies: (key: KeyReference) => readonly GraphDependency[];
549
+ /** The services whose edges select the service of `key` now. */
550
+ readonly dependents: (key: KeyReference) => readonly GraphService[];
551
+ /**
552
+ * The topological order: each provider comes before its dependents.
553
+ * With `keys`, the order has only those services and their transitive dependencies. The members of one cycle come together, in registration order.
554
+ */
555
+ readonly order: (keys?: readonly KeyReference[]) => readonly GraphService[];
556
+ /** Each dependency cycle, as a list of services. */
557
+ readonly cycles: () => readonly (readonly GraphService[])[];
558
+ /** The services that a proposed change stops, rebuilds, or notifies. It runs on a copy of the graph. */
559
+ readonly impact: (change: ImpactChange) => ImpactResult;
560
+ /**
561
+ * Why the service of `key` is not `running`: missing, conflicting, rejected, failed, or unavailable dependencies.
562
+ * A key without a service gives `phase: undefined` and one `missing-provider` blocker.
563
+ */
564
+ readonly explain: (key: KeyReference) => ServiceExplanation;
565
+ }
566
+ /** The value type of a dependency declaration for `peek`. */
567
+ type PeekValue<D> = D extends OptionalDependency<infer T> ? T : D extends FallbackDependency<infer T> ? T : D extends ConditionalDependency<infer T> ? T : D extends ProvidedKey<string, infer T, DependencyMap | undefined> ? T : D extends ProvidedBy<infer T> ? T : unknown;
568
+ /**
569
+ * The result of `peek`: the value now, or the reason why no value is available now.
570
+ *
571
+ * - `resolved`: a running provider serves the key with the status `ready` or `degraded`.
572
+ * - `unavailable`: a running provider has the effective status `unavailable`. `reason` and `cause` tell why.
573
+ * - `not-running`: a provider exists, but it does not run. `phase` is its phase. A `failed` provider has `error`.
574
+ * - `rejected`: a resolution condition rejected the provider.
575
+ * - `conflict`: the key is exclusive and has more than one declaration. `owners` lists them.
576
+ * - `missing`: no provider exists.
577
+ *
578
+ * `key` is the key name of the provider. For `firstAvailable`, it is the selected key, or the first key when no key has a provider.
579
+ */
580
+ type PeekResult<T> = {
581
+ readonly kind: 'resolved';
582
+ readonly key: string;
583
+ readonly owner: OwnerRecord;
584
+ readonly value: T;
585
+ readonly status: Exclude<ServiceStatus, 'unavailable'>;
586
+ readonly instanceId: string;
587
+ } | {
588
+ readonly kind: 'unavailable';
589
+ readonly key: string;
590
+ readonly owner: OwnerRecord;
591
+ readonly instanceId: string;
592
+ readonly reason?: string;
593
+ readonly cause?: StatusCause;
594
+ } | {
595
+ readonly kind: 'not-running';
596
+ readonly key: string;
597
+ readonly owner: OwnerRecord;
598
+ readonly phase: ServicePhase;
599
+ readonly error?: unknown;
600
+ } | {
601
+ readonly kind: 'rejected';
602
+ readonly key: string;
603
+ readonly owner: OwnerRecord;
604
+ readonly metadata: ServiceMetadata;
605
+ } | {
606
+ readonly kind: 'conflict';
607
+ readonly key: string;
608
+ readonly owners: readonly OwnerRecord[];
609
+ } | {
610
+ readonly kind: 'missing';
611
+ readonly key: string;
612
+ };
613
+ /** A dependency declaration that `peek` reads. An `optional` marker has no effect. */
614
+ type PeekDependency = DependencyDeclaration;
615
+ /** The registration options that each object form of `provide` accepts. */
616
+ interface RegistrationOptions extends PolicyOptions {
617
+ /** A record that resolution conditions and graph queries read. injeca freezes a copy. */
618
+ metadata?: ServiceMetadata;
619
+ /**
620
+ * Runs `build` once for each consumer container. The build context then has `consumer`.
621
+ * Each instance disposes with its consumer container.
622
+ */
623
+ perConsumer?: boolean;
624
+ /**
625
+ * Exports the registration one level up. The registration stays in its container: its owner, its disposal, its dependencies, and its phases.
626
+ * It is also a candidate for consumers in the resolution area of the parent container: the parent and each descendant of the parent.
627
+ * For an exclusive key, it counts as a declaration in that area. On a root container, it has no effect.
628
+ */
629
+ export?: boolean;
630
+ }
631
+ type ProvideOptionObject<T, D extends DependencyMap | undefined = undefined> = {
632
+ build: (context: BuildContext<D>) => T | Promise<T>;
633
+ } & RegistrationOptions & (D extends undefined ? {
634
+ dependsOn?: Record<string, never>;
635
+ } : {
636
+ dependsOn: { [K in keyof D]: string; };
637
+ });
638
+ type ProvideOptionFunc<T, D extends DependencyMap | undefined = undefined> = (context: BuildContext<D>) => T | Promise<T>;
639
+ type ProvideOption<T, D extends DependencyMap | undefined = undefined> = ProvideOptionObject<T, D> | ProvideOptionFunc<T, D>;
640
+ interface ProvideOptionObjectWithKeys<T, Deps extends DependencyDeclarations> extends RegistrationOptions {
641
+ build: (context: BuildContext<ResolveDependencyDeclaration<Deps>>) => T | Promise<T>;
642
+ dependsOn: Deps;
643
+ }
644
+ type ProvideOptionWithKeys<T, Deps extends DependencyDeclarations> = ProvideOptionObjectWithKeys<T, Deps>;
645
+ type InvokeOptionObject<D extends DependencyMap | undefined = undefined> = {
646
+ callback: (dependencies: D, context: InvocationContext<D>) => void | Promise<void>;
647
+ } & (D extends undefined ? {
648
+ dependsOn?: Record<string, never>;
649
+ } : {
650
+ dependsOn: { [K in keyof D]: string; };
651
+ });
652
+ type InvokeOptionFunc<D extends DependencyMap | undefined = undefined> = (dependencies: D, context: InvocationContext<D>) => void | Promise<void>;
653
+ type InvokeOption<D extends DependencyMap | undefined = undefined> = InvokeOptionObject<D> | InvokeOptionFunc<D>;
654
+ interface InvokeOptionObjectWithKeys<Deps extends DependencyDeclarations> {
655
+ callback: (dependencies: ResolveDependencyDeclaration<Deps>, context: InvocationContext<ResolveDependencyDeclaration<Deps>>) => void | Promise<void>;
656
+ dependsOn: Deps;
657
+ }
658
+ type InvokeOptionWithKeys<Deps extends DependencyDeclarations> = InvokeOptionObjectWithKeys<Deps>;
659
+ /**
660
+ * The `replace()` of a registration. It takes the function shorthand, or the object form with or without `dependsOn`.
661
+ * An option without `metadata`, `maxAttempts`, `timeouts`, or `onTimeout` keeps that value of the registration. `perConsumer` does not change.
662
+ */
663
+ interface ReplaceFunction<T> {
664
+ <Deps extends DependencyDeclarations>(option: ProvideOptionWithKeys<T, Deps>, options?: ReplaceOptions): Promise<boolean>;
665
+ (option: ProvideOption<T, undefined>, options?: ReplaceOptions): Promise<boolean>;
666
+ }
667
+ /** The options of `replace()`. */
668
+ interface ReplaceOptions {
669
+ /**
670
+ * Cancels the pending replacement when the signal aborts, as a newer `replace` does. The old instance stays, and `replace()` resolves `false`.
671
+ * An aborted signal resolves `false` at once and changes nothing. After the swap, the signal has no effect.
672
+ */
673
+ signal?: AbortSignal;
674
+ }
675
+ /** The options of `withdraw()` of a registration or an invocation. */
676
+ interface WithdrawOptions {
677
+ /**
678
+ * Ends the wait when the signal aborts, and rejects with the reason. The withdrawal continues. There is no default.
679
+ * The service gets a `wait-aborted` record in the observe stream when it did not finish its withdrawal. An aborted signal still starts the withdrawal.
680
+ */
681
+ signal?: AbortSignal;
682
+ }
683
+ /**
684
+ * The result of `provide`: the typed key of the service, with `withdraw()` and `replace()`.
685
+ */
686
+ interface Registration<Key, T, D extends DependencyMap | undefined> extends ProvidedKey<Key, T, D> {
687
+ /**
688
+ * Withdraws the service. Dependents stop first. Then the service runs its `stop` hooks and effect disposers.
689
+ *
690
+ * Resolves `false` for a stale registration and does not change any service. Rejects with the teardown errors.
691
+ *
692
+ * A call from a cleanup that the withdrawal waits for cannot wait: of the service, of a consumer service of it, or of a service that holds it.
693
+ * injeca finds such a call by the signal of the cleanup, or by the synchronous part of the cleanup. It warns, sends a `reentrant-call` record, and resolves `true` at once. The withdrawal continues.
694
+ */
695
+ readonly withdraw: (options?: WithdrawOptions) => Promise<boolean>;
696
+ /**
697
+ * Replaces the instance of the service without a gap.
698
+ *
699
+ * The old instance keeps serving with its status while the new instance runs `build` and `start`. The swap is one batch.
700
+ * Resolves `true` after the swap. Resolves `false` for a stale registration, when a newer `replace` or a withdrawal supersedes this call, or when `options.signal` aborts before the swap.
701
+ * Rejects with the build or start error of the new instance, and the old instance does not change.
702
+ * Rejects with `replace-while-stopping` when the service is in `stopping`.
703
+ */
704
+ readonly replace: ReplaceFunction<T>;
705
+ }
706
+ /** The result of `invoke`. */
707
+ interface InvocationHandle {
708
+ /** The name of the invocation. Graph queries, for example `graph(container).dependencies(key)` and `explain(key)`, accept it. */
709
+ readonly key: string;
710
+ /** Withdraws the invocation. Resolves `false` when it is already withdrawn. Rejects with the teardown errors. `options` work as for `withdraw()` of a registration. */
711
+ readonly withdraw: (options?: WithdrawOptions) => Promise<boolean>;
712
+ }
713
+ interface DeclareOptions {
714
+ /** The declared edges. A later `tx.provide` without `dependsOn` uses them. */
715
+ dependsOn?: DependencyDeclarations;
716
+ /** An optional declaration does not need a `tx.provide`. Without one, the commit drops it. */
717
+ optional?: boolean;
718
+ }
719
+ /** The `provide` of a transaction. It takes a name or a key, as `provide` does. */
720
+ interface TransactionProvide {
721
+ <T, Key extends string, Deps extends DependencyDeclarations>(name: Key, option: ProvideOptionWithKeys<T, Deps>): Registration<Key, T, ResolveDependencyDeclaration<Deps>>;
722
+ <D extends DependencyMap | undefined, T = unknown, Key extends string = string>(name: Key, option: ProvideOption<T, D>): Registration<Key, T, D>;
723
+ <T, Key extends string, Deps extends DependencyDeclarations>(key: ProvidedKey<Key, T, DependencyMap | undefined>, option: ProvideOptionWithKeys<NoInfer<T>, Deps>): Registration<Key, T, ResolveDependencyDeclaration<Deps>>;
724
+ <T, Key extends string>(key: ProvidedKey<Key, T, DependencyMap | undefined>, option: ProvideOption<NoInfer<T>, undefined>): Registration<Key, T, undefined>;
725
+ }
726
+ /** The `replace` of a transaction. It takes the current registration of the key at the commit. */
727
+ interface TransactionReplace {
728
+ <T, Deps extends DependencyDeclarations>(key: ProvidedKey<string, T, DependencyMap | undefined> | string, option: ProvideOptionWithKeys<NoInfer<T>, Deps>): void;
729
+ <T>(key: ProvidedKey<string, T, DependencyMap | undefined> | string, option: ProvideOption<NoInfer<T>, undefined>): void;
730
+ }
731
+ /** The options of `tx.commit()`. */
732
+ interface TransactionCommitOptions {
733
+ /** Aborts the transaction when the signal aborts before the publish. */
734
+ signal?: AbortSignal;
735
+ }
736
+ /**
737
+ * A set of registrations that publish together. `transaction(container)` creates it.
738
+ *
739
+ * Nothing is visible to consumers before `commit()`. A failed build publishes nothing.
740
+ */
741
+ interface Transaction {
742
+ /** Reserves a service. Graph queries see it as `declared`. Consumers cannot resolve it. */
743
+ readonly declare: (key: KeyReference, options?: DeclareOptions) => void;
744
+ /** Fills a declared service, or adds a new service. The registration becomes current at the commit. */
745
+ readonly provide: TransactionProvide;
746
+ /** Replaces the current registration of `key` in the container at the commit, as `replace()` does. */
747
+ readonly replace: TransactionReplace;
748
+ /**
749
+ * Builds the services in dependency order, then publishes them in one batch. Then the `start` hooks run.
750
+ *
751
+ * A service whose required dependencies are not available goes to `pending`. That is not a failure.
752
+ * If one build fails, nothing publishes, no `start` hook runs, and the promise rejects with the build error.
753
+ * Resolves after the publish. It does not wait for the `start` hooks.
754
+ *
755
+ * When `options.signal` aborts before the publish, the transaction aborts and rolls back, and the commit rejects at once with the reason.
756
+ * An aborted signal rejects at once and builds nothing. After the publish, the signal has no effect.
757
+ */
758
+ readonly commit: (options?: TransactionCommitOptions) => Promise<void>;
759
+ /** Removes all declared and built services of the transaction. A build that settles later cannot publish. */
760
+ readonly abort: () => Promise<void>;
761
+ }
762
+ export { DecisionSource as $, PeekValue as A, OwnerRecord as At, LifecycleContext as B, systemClock as Bt, GraphDependency as C, ProviderInfo as Ct, ImpactResult as D, DependencyChange as Dt, ImpactChange as E, ServiceMetadata as Et, HookContext as F, ServiceState as Ft, TimeoutContext as G, createNoopLogger as Gt, ContainerOptions as H, LoggerOptions as Ht, HookContextFor as I, ServiceStatus as It, DecideFor as J, TimeoutHandler as K, HookDependencies as L, SetStatusOptions as Lt, DependencyHookContext as M, ServiceBlocker as Mt, Disposer as N, ServiceExplanation as Nt, PeekDependency as O, DependencySnapshot as Ot, EffectSetup as P, ServicePhase as Pt, DecisionOptions as Q, HookHandler as R, StatusCause as Rt, Graph as S, ProvidedKey as St, GraphService as T, ResolveDependencyDeclaration as Tt, HookTimeouts as U, createDefaultLogger as Ut, Container as V, Logger as Vt, PolicyOptions as W, createLoggLogger as Wt, DecisionFor as X, Decision as Y, DecisionKind as Z, Registration as _, KeyOptions as _t, TransactionReplace as a, HookResult as at, ReplaceOptions as b, OptionalValue as bt, InvokeOptionFunc as c, RequeueOptions as ct, InvokeOptionWithKeys as d, ConditionalDependency as dt, DependencyHook as et, ProvideOption as f, DependencyDeclaration as ft, ProvideOptionWithKeys as g, InferProvided as gt, ProvideOptionObjectWithKeys as h, FallbackDependency as ht, TransactionProvide as i, HookDecisionKind as it, BuildContext as j, OwnerValue as jt, PeekResult as k, DependencyState as kt, InvokeOptionObject as l, ServiceHook as lt, ProvideOptionObject as m, DependencyMap as mt, Transaction as n, FailDecision as nt, InvocationHandle as o, RebuildDecision as ot, ProvideOptionFunc as p, DependencyDeclarations as pt, Decide as q, TransactionCommitOptions as r, HandledDecision as rt, InvokeOption as s, RequeueDecision as st, DeclareOptions as t, ExponentialBackoff as tt, InvokeOptionObjectWithKeys as u, StopDecision as ut, RegistrationOptions as v, KeyReference as vt, GraphResolution as w, ResolutionCondition as wt, WithdrawOptions as x, ProvidedBy as xt, ReplaceFunction as y, OptionalDependency as yt, InvocationContext as z, Clock as zt };
763
+ //# sourceMappingURL=index-DTYp044J.d.mts.map