@btravstack/core 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/LICENSE +21 -0
- package/README.md +150 -0
- package/dist/index.cjs +580 -0
- package/dist/index.d.cts +458 -0
- package/dist/index.d.mts +458 -0
- package/dist/index.mjs +567 -0
- package/package.json +75 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
import { AsyncResult, Result } from "unthrown";
|
|
2
|
+
import { AnyPort, Context, Module, PortClass, Scope, ServiceOf } from "@btravstack/di";
|
|
3
|
+
import { Env, Environment } from "@btravstack/config";
|
|
4
|
+
//#region src/clock.d.ts
|
|
5
|
+
type Clock = {
|
|
6
|
+
readonly now: () => number;
|
|
7
|
+
readonly sleep: (ms: number, signal?: AbortSignal) => AsyncResult<void, never>;
|
|
8
|
+
};
|
|
9
|
+
declare const systemClock: Clock;
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region src/units.d.ts
|
|
12
|
+
/**
|
|
13
|
+
* What the kernel opens per unit and `currentUnit()` reads: a small, fixed
|
|
14
|
+
* record of **data about this unit**, and never a service.
|
|
15
|
+
*
|
|
16
|
+
* `signal` is the same `AbortSignal` the work callback receives — aborted at
|
|
17
|
+
* the drain deadline, or at once on a path that skips the drain. It is here
|
|
18
|
+
* because the callback is not always where the work is: a middleware-shaped
|
|
19
|
+
* runtime opens the unit around a call whose arguments it does not own. A
|
|
20
|
+
* transport's own cancellation is a different clock, not this one.
|
|
21
|
+
*/
|
|
22
|
+
type UnitRecord = {
|
|
23
|
+
readonly unitId: string;
|
|
24
|
+
readonly traceId: string;
|
|
25
|
+
readonly tenantId: string | undefined;
|
|
26
|
+
readonly deadline: number | undefined;
|
|
27
|
+
readonly signal: AbortSignal;
|
|
28
|
+
};
|
|
29
|
+
declare const currentUnit: () => UnitRecord | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* What a runtime says about one piece of work as it submits it. `kind` is the
|
|
32
|
+
* category (`"http"`, `"tick"`, `"job"`); `id` identifies **this** unit.
|
|
33
|
+
*
|
|
34
|
+
* @remarks
|
|
35
|
+
* **`id` must be unique per unit unless a `traceId` is supplied**, because
|
|
36
|
+
* `traceId` defaults to it. A runtime passing a CATEGORY as the id — a route
|
|
37
|
+
* template such as `"POST /orders"` — gives every request the same trace id and
|
|
38
|
+
* silently defeats the ambient record. A route template is a `kind`.
|
|
39
|
+
*
|
|
40
|
+
* The kernel cannot check this, so uniqueness is the runtime's to guarantee.
|
|
41
|
+
* What it does guarantee is {@link UnitRecord}'s `unitId`, minted per unit;
|
|
42
|
+
* `traceId` is the CORRELATION id, which is why it is the one a runtime may
|
|
43
|
+
* supply — it carries an id from outside the process.
|
|
44
|
+
*/
|
|
45
|
+
type UnitMeta = {
|
|
46
|
+
readonly kind: string;
|
|
47
|
+
readonly id: string;
|
|
48
|
+
readonly traceId?: string;
|
|
49
|
+
readonly tenantId?: string;
|
|
50
|
+
readonly deadline?: number;
|
|
51
|
+
};
|
|
52
|
+
type UnitWork<T, E> = (signal: AbortSignal) => AsyncResult<T, E> | Promise<Result<T, E>> | Result<T, E>;
|
|
53
|
+
type UnitRegistry = {
|
|
54
|
+
readonly run: <T, E>(meta: UnitMeta, work: UnitWork<T, E>) => AsyncResult<T, E>;
|
|
55
|
+
readonly inFlight: () => number;
|
|
56
|
+
readonly closed: () => number;
|
|
57
|
+
readonly abortAll: () => void;
|
|
58
|
+
readonly awaitIdle: () => AsyncResult<void, never>;
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/runtime.d.ts
|
|
62
|
+
declare const RuntimeStartFailed_base: import("unthrown").TaggedErrorConstructor<"RuntimeStartFailed">;
|
|
63
|
+
declare class RuntimeStartFailed extends RuntimeStartFailed_base<{
|
|
64
|
+
readonly runtime: string;
|
|
65
|
+
readonly cause: unknown;
|
|
66
|
+
}> {
|
|
67
|
+
message: string;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Submit one piece of work as a **unit**: the kernel counts it towards the
|
|
71
|
+
* drain, hands it an `AbortSignal` and an ambient record, and gives the work's
|
|
72
|
+
* own `Result` straight back — mapping that outcome to a transport is the
|
|
73
|
+
* runtime's job, never the kernel's.
|
|
74
|
+
*
|
|
75
|
+
* @remarks
|
|
76
|
+
* **Everything the client must receive has to be flushed INSIDE `work`, never
|
|
77
|
+
* after the returned `AsyncResult` settles.** A unit closes the instant its
|
|
78
|
+
* `Result` settles, and an idle registry is the drain's permission to call
|
|
79
|
+
* `Serving.stop()` — so a runtime that writes after resolving is racing the
|
|
80
|
+
* transport being torn down.
|
|
81
|
+
*
|
|
82
|
+
* `UnitMeta.id` must be unique per unit unless a `traceId` is supplied — see
|
|
83
|
+
* {@link UnitMeta}.
|
|
84
|
+
*
|
|
85
|
+
* With a `StartOptions.unit` module in play, `work` runs only once the fork is
|
|
86
|
+
* built. A runtime that subscribes to an event from inside `work` must first
|
|
87
|
+
* check whether it has already fired.
|
|
88
|
+
*/
|
|
89
|
+
type RunUnit<Resolves extends AnyPort> = <T, E>(meta: UnitMeta, work: (ctx: Context<InstanceType<Resolves>>, signal: AbortSignal) => ReturnType<UnitWork<T, E>>) => AsyncResult<T, E>;
|
|
90
|
+
/**
|
|
91
|
+
* What a runtime is handed at `start`: the application services **and** the
|
|
92
|
+
* kernel's {@link RunUnit}. Handing it a bare `Context` would leave every
|
|
93
|
+
* runtime inventing its own unit tracking — the thing the kernel exists to own.
|
|
94
|
+
*
|
|
95
|
+
* @remarks
|
|
96
|
+
* The two contracts a runtime author owes, neither checkable by the kernel: a
|
|
97
|
+
* unit's response must be flushed **inside** the work callback (see
|
|
98
|
+
* {@link RunUnit}), and `UnitMeta.id` must be unique per unit unless a
|
|
99
|
+
* `traceId` is supplied (see {@link UnitMeta}).
|
|
100
|
+
*
|
|
101
|
+
* `ctx` is the **application** context: a port a `StartOptions.unit` module
|
|
102
|
+
* provides exists only while a unit is open, so a runtime naming it in
|
|
103
|
+
* `resolves` is rejected at `start`'s call site.
|
|
104
|
+
*/
|
|
105
|
+
type RuntimeHost<Resolves extends AnyPort> = {
|
|
106
|
+
readonly ctx: Context<InstanceType<Resolves>>;
|
|
107
|
+
readonly run: RunUnit<Resolves>;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* What a runtime is, once it is up — plus, optionally, what it wants to say
|
|
111
|
+
* about itself, read back through `RunningApp.runtimeInfo()`.
|
|
112
|
+
*
|
|
113
|
+
* `Info` is the runtime's own shape rather than a port number: a queue consumer
|
|
114
|
+
* has no port and might publish `{ queue, prefetch }`. It defaults to `never`,
|
|
115
|
+
* so `info` is unwritable for a runtime with nothing to publish.
|
|
116
|
+
*/
|
|
117
|
+
type Serving<Info = never> = {
|
|
118
|
+
readonly drain: (signal: AbortSignal) => AsyncResult<void, never>;
|
|
119
|
+
readonly stop: () => AsyncResult<void, never>;
|
|
120
|
+
readonly info?: Info;
|
|
121
|
+
};
|
|
122
|
+
type Runtime<Resolves extends AnyPort = never, Info = never> = {
|
|
123
|
+
readonly name: string;
|
|
124
|
+
readonly resolves: readonly Resolves[];
|
|
125
|
+
readonly start: (host: RuntimeHost<Resolves>) => AsyncResult<Serving<Info>, RuntimeStartFailed>;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* The port the kernel resolves its runtime from. A runtime is a **service the
|
|
129
|
+
* module provides**, not an option handed to `start`: a runtime package
|
|
130
|
+
* declares its own port over this one — `class HttpRuntime extends
|
|
131
|
+
* RuntimePort<Runtime<never, HttpInfo>> {}` — so it is built by di like
|
|
132
|
+
* everything else, and the kernel owns nothing but the lifecycle.
|
|
133
|
+
*
|
|
134
|
+
* Left generic on purpose: every runtime port is one id at runtime, since a
|
|
135
|
+
* process boots exactly one, while each carries its own `Resolves`/`Info` in
|
|
136
|
+
* the type.
|
|
137
|
+
*/
|
|
138
|
+
declare const RuntimePort: PortClass<"Runtime">;
|
|
139
|
+
/** The instance type every runtime port shares — `Extract` this from a module's exports to find its runtime. */
|
|
140
|
+
type RuntimeInstance = InstanceType<PortClass<"Runtime">>;
|
|
141
|
+
/** The `Runtime<Resolves, Info>` a module exports, or `never` when it exports none. */
|
|
142
|
+
type RuntimeOf<X> = ServiceOf<Extract<X, RuntimeInstance>>;
|
|
143
|
+
type RuntimeResolvesOf<X> = RuntimeOf<X> extends Runtime<infer Resolves, unknown> ? Resolves : never;
|
|
144
|
+
type RuntimeInfoOf<X> = RuntimeOf<X> extends Runtime<AnyPort, infer Info> ? Info : never;
|
|
145
|
+
/**
|
|
146
|
+
* `running`, but no later than the kernel's drain deadline — the primitive a
|
|
147
|
+
* `Serving.drain` needs when the work it awaits settles on somebody else's
|
|
148
|
+
* clock (Temporal's `shutdownForceTime`, a broker library's close) and so
|
|
149
|
+
* cannot honour `signal` itself.
|
|
150
|
+
*
|
|
151
|
+
* **The losing branch's `Result` is dropped**, which is the point rather than
|
|
152
|
+
* an oversight: once the deadline wins, the kernel has already moved on and
|
|
153
|
+
* the eventual outcome has no consumer left. What that costs is the runtime's
|
|
154
|
+
* own business — an un-acked AMQP delivery is redelivered, so abandoning one
|
|
155
|
+
* repeats work rather than losing it, while a Temporal activity is retried on
|
|
156
|
+
* another worker.
|
|
157
|
+
*
|
|
158
|
+
* Deliberately **`Clock`-agnostic**: there is no duration here, only a signal,
|
|
159
|
+
* so it behaves identically under `@btravstack/testing`'s fake clock. Racing
|
|
160
|
+
* work against a *timeout* is a different primitive and belongs on `Clock`
|
|
161
|
+
* (`drain.ts` uses `clock.sleep` for exactly that, so a fake clock can control
|
|
162
|
+
* it) — do not fold the two together.
|
|
163
|
+
*/
|
|
164
|
+
declare const releasedBy: (signal: AbortSignal, running: AsyncResult<void, never>) => AsyncResult<void, never>;
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region src/drain.d.ts
|
|
167
|
+
type DrainReport = {
|
|
168
|
+
/** Units in flight when the drain began. */
|
|
169
|
+
readonly inFlightAtStart: number;
|
|
170
|
+
/**
|
|
171
|
+
* Units that closed during the drain, counted from a monotonic total. It may
|
|
172
|
+
* exceed `inFlightAtStart` when in-flight work spawned more units — honest
|
|
173
|
+
* reporting, where `inFlightAtStart - abandoned` can go negative.
|
|
174
|
+
*/
|
|
175
|
+
readonly completed: number;
|
|
176
|
+
/** Units still open at the deadline. The exit-code decision reads this. */
|
|
177
|
+
readonly abandoned: number;
|
|
178
|
+
};
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/events.d.ts
|
|
181
|
+
type KernelEvent = {
|
|
182
|
+
readonly type: "building";
|
|
183
|
+
} | {
|
|
184
|
+
readonly type: "startFailed";
|
|
185
|
+
readonly cause: unknown;
|
|
186
|
+
} | {
|
|
187
|
+
readonly type: "serving";
|
|
188
|
+
readonly runtime: string;
|
|
189
|
+
/**
|
|
190
|
+
* Whatever the runtime published on `Serving.info`. `unknown` because the
|
|
191
|
+
* kernel does not know a runtime's `Info` at the event union — it is read
|
|
192
|
+
* off the module at `start`'s call site, not here — and a sink is
|
|
193
|
+
* serialising it anyway. A generic `KernelEvent<Info>` would infect
|
|
194
|
+
* `EventSink`, `stderrSink` and every adapter for one field none of them
|
|
195
|
+
* reads structurally.
|
|
196
|
+
*/
|
|
197
|
+
readonly info: unknown;
|
|
198
|
+
/**
|
|
199
|
+
* The port the kernel's own probe listener bound, `undefined` when probes
|
|
200
|
+
* are off. Its own field rather than folded into `info`: the probe server
|
|
201
|
+
* is the kernel's, not the runtime's, so publishing it as something the
|
|
202
|
+
* runtime said would be a small lie.
|
|
203
|
+
*/
|
|
204
|
+
readonly probePort: number | undefined;
|
|
205
|
+
} | {
|
|
206
|
+
readonly type: "draining";
|
|
207
|
+
readonly inFlight: number;
|
|
208
|
+
} | {
|
|
209
|
+
readonly type: "drained";
|
|
210
|
+
readonly report: DrainReport;
|
|
211
|
+
} | {
|
|
212
|
+
readonly type: "stopping";
|
|
213
|
+
} | {
|
|
214
|
+
readonly type: "exited";
|
|
215
|
+
} | {
|
|
216
|
+
readonly type: "teardownError";
|
|
217
|
+
readonly port: string;
|
|
218
|
+
readonly cause: unknown;
|
|
219
|
+
} | {
|
|
220
|
+
readonly type: "uncaught";
|
|
221
|
+
readonly cause: unknown;
|
|
222
|
+
};
|
|
223
|
+
type EventSink = (event: KernelEvent) => void;
|
|
224
|
+
declare const stderrSink: EventSink;
|
|
225
|
+
//#endregion
|
|
226
|
+
//#region src/observability.d.ts
|
|
227
|
+
/**
|
|
228
|
+
* The severity of one line, and the whole of the set: six levels, ordered, with
|
|
229
|
+
* no caller-defined additions — which is what lets `LOG_LEVEL` be validated at
|
|
230
|
+
* startup and `isEnabled` be a comparison rather than a lookup.
|
|
231
|
+
*/
|
|
232
|
+
type Level = "trace" | "debug" | "info" | "warn" | "error" | "fatal";
|
|
233
|
+
/** The levels in order, least severe first — what `isEnabled` compares through. */
|
|
234
|
+
declare const LEVELS: readonly Level[];
|
|
235
|
+
/**
|
|
236
|
+
* What a line, a span or a measurement carries besides itself: a flat record of
|
|
237
|
+
* scalars.
|
|
238
|
+
*
|
|
239
|
+
* Flat and scalar on purpose. A nested object is where a field's name stops
|
|
240
|
+
* being stable, and an `unknown` value is where a logger starts stringifying
|
|
241
|
+
* whatever it is handed — which is how a log call becomes the thing that
|
|
242
|
+
* throws. A failure has a channel of its own, `cause`.
|
|
243
|
+
*/
|
|
244
|
+
type Attributes = Readonly<Record<string, string | number | boolean | undefined>>;
|
|
245
|
+
/**
|
|
246
|
+
* Every method takes the same three arguments in the same order:
|
|
247
|
+
* `(message, attributes?, cause?)`.
|
|
248
|
+
*
|
|
249
|
+
* Uniform on purpose. A failure is not a property of severity — an `info` line
|
|
250
|
+
* reporting a recovered fault carries one too — so every level can take a
|
|
251
|
+
* `cause`, at the cost of `logger.error("boom", undefined, cause)` for a
|
|
252
|
+
* failure with nothing else to say.
|
|
253
|
+
*/
|
|
254
|
+
type LoggerService = {
|
|
255
|
+
readonly log: (level: Level, message: string, attributes?: Attributes, cause?: unknown) => void;
|
|
256
|
+
readonly trace: (message: string, attributes?: Attributes, cause?: unknown) => void;
|
|
257
|
+
readonly debug: (message: string, attributes?: Attributes, cause?: unknown) => void;
|
|
258
|
+
readonly info: (message: string, attributes?: Attributes, cause?: unknown) => void;
|
|
259
|
+
readonly warn: (message: string, attributes?: Attributes, cause?: unknown) => void;
|
|
260
|
+
/** `cause` is the failure itself — an `Error`, an `unthrown` `Err`'s error, a rejected value. */
|
|
261
|
+
readonly error: (message: string, attributes?: Attributes, cause?: unknown) => void;
|
|
262
|
+
readonly fatal: (message: string, attributes?: Attributes, cause?: unknown) => void;
|
|
263
|
+
/** A logger carrying `attributes` on every line it writes, on top of this one's. Never mutates this one. */
|
|
264
|
+
readonly with: (attributes: Attributes) => LoggerService;
|
|
265
|
+
/** Whether a line at `level` would be written — for a payload expensive enough to be worth not building. */
|
|
266
|
+
readonly isEnabled: (level: Level) => boolean;
|
|
267
|
+
};
|
|
268
|
+
declare const Logger_base: import("@btravstack/di").PortClass<"Logger">;
|
|
269
|
+
/**
|
|
270
|
+
* The application's logger, as a port — declared here and implemented in
|
|
271
|
+
* `@btravstack/observability`, because a contract every package may depend on
|
|
272
|
+
* has to be reachable without installing an implementation, and because the
|
|
273
|
+
* correlation an implementation stamps per line is the kernel's own ambient
|
|
274
|
+
* record.
|
|
275
|
+
*
|
|
276
|
+
* An implementation must not throw: a logger that throws turns an observability
|
|
277
|
+
* problem into an outage. Synchronous `void`, not an `AsyncResult` — thesis 6's
|
|
278
|
+
* one deliberate exemption, since a lost line is not a modeled error.
|
|
279
|
+
*/
|
|
280
|
+
declare class Logger extends Logger_base<LoggerService> {}
|
|
281
|
+
/** A span's outcome: unset until something says otherwise, then `ok` or `error`. */
|
|
282
|
+
declare const SPAN_STATUS: {
|
|
283
|
+
readonly unset: 0;
|
|
284
|
+
readonly ok: 1;
|
|
285
|
+
readonly error: 2;
|
|
286
|
+
};
|
|
287
|
+
/** The three codes {@link SPAN_STATUS} names, as the type a {@link Span} takes. */
|
|
288
|
+
type SpanStatusCode = (typeof SPAN_STATUS)[keyof typeof SPAN_STATUS];
|
|
289
|
+
/**
|
|
290
|
+
* One span, narrowed to what a framework package does with it: label it, say
|
|
291
|
+
* how it ended, end it. The numbers are OpenTelemetry's own, so an OTel span
|
|
292
|
+
* satisfies this structurally with no translation in between.
|
|
293
|
+
*/
|
|
294
|
+
type Span = {
|
|
295
|
+
readonly setAttributes: (attributes: Attributes) => unknown;
|
|
296
|
+
readonly setStatus: (status: {
|
|
297
|
+
readonly code: SpanStatusCode;
|
|
298
|
+
readonly message?: string;
|
|
299
|
+
}) => unknown;
|
|
300
|
+
readonly end: () => void;
|
|
301
|
+
};
|
|
302
|
+
/** What a {@link Tracer} does: start a span, by name. */
|
|
303
|
+
type TracerService = {
|
|
304
|
+
readonly startSpan: (name: string) => Span;
|
|
305
|
+
};
|
|
306
|
+
declare const Tracer_base: import("@btravstack/di").PortClass<"Tracer">;
|
|
307
|
+
/**
|
|
308
|
+
* The application's tracer, as a port — declared without naming OpenTelemetry,
|
|
309
|
+
* because a port typed as a vendor's type points the dependency arrow outwards
|
|
310
|
+
* and makes the whole family install that vendor.
|
|
311
|
+
*/
|
|
312
|
+
declare class Tracer extends Tracer_base<TracerService> {}
|
|
313
|
+
/** A monotonic count. */
|
|
314
|
+
type Counter = {
|
|
315
|
+
readonly add: (value: number, attributes?: Attributes) => void;
|
|
316
|
+
};
|
|
317
|
+
/** A distribution of measurements. */
|
|
318
|
+
type Histogram = {
|
|
319
|
+
readonly record: (value: number, attributes?: Attributes) => void;
|
|
320
|
+
};
|
|
321
|
+
/**
|
|
322
|
+
* What a {@link Meter} does: mint the two instruments a framework package needs
|
|
323
|
+
* — it counts what happened and measures how long it took. A gauge is something
|
|
324
|
+
* an application declares about its own domain, reaching the vendor's meter for
|
|
325
|
+
* it as it would any other adapter.
|
|
326
|
+
*/
|
|
327
|
+
type MeterService = {
|
|
328
|
+
readonly createCounter: (name: string, options?: {
|
|
329
|
+
readonly description?: string;
|
|
330
|
+
readonly unit?: string;
|
|
331
|
+
}) => Counter;
|
|
332
|
+
readonly createHistogram: (name: string, options?: {
|
|
333
|
+
readonly description?: string;
|
|
334
|
+
readonly unit?: string;
|
|
335
|
+
}) => Histogram;
|
|
336
|
+
};
|
|
337
|
+
declare const Meter_base: import("@btravstack/di").PortClass<"Meter">;
|
|
338
|
+
/** The application's meter, as a port. See {@link Tracer} for why it does not name a vendor. */
|
|
339
|
+
declare class Meter extends Meter_base<MeterService> {}
|
|
340
|
+
//#endregion
|
|
341
|
+
//#region src/phase.d.ts
|
|
342
|
+
type Phase = "building" | "starting" | "serving" | "draining" | "stopping" | "exited";
|
|
343
|
+
//#endregion
|
|
344
|
+
//#region src/start.d.ts
|
|
345
|
+
type TeardownError = {
|
|
346
|
+
readonly port: string;
|
|
347
|
+
readonly cause: unknown;
|
|
348
|
+
};
|
|
349
|
+
type ExitReport = {
|
|
350
|
+
readonly reason: "signal" | "runtimeStopped" | "uncaught";
|
|
351
|
+
readonly drain: DrainReport | undefined;
|
|
352
|
+
readonly teardownErrors: readonly TeardownError[];
|
|
353
|
+
readonly uptimeMs: number;
|
|
354
|
+
};
|
|
355
|
+
type StartOptions<UnitX = never, UnitNeeds = never> = {
|
|
356
|
+
/**
|
|
357
|
+
* The environment the graph is configured from, provided to it as the `Env`
|
|
358
|
+
* port and read for the kernel's own `PROBE_PORT`. Defaults to
|
|
359
|
+
* `process.env`.
|
|
360
|
+
*/
|
|
361
|
+
readonly env?: Environment;
|
|
362
|
+
/**
|
|
363
|
+
* A module forked around **every unit**: built as the unit opens, torn down
|
|
364
|
+
* as it closes — while the unit's ambient record is still open — reading
|
|
365
|
+
* anything the application context carries.
|
|
366
|
+
*
|
|
367
|
+
* A failing unit finaliser is reported as a `teardownError` event and never
|
|
368
|
+
* in `ExitReport.teardownErrors`. With this option the unit's work runs only
|
|
369
|
+
* once the fork is built, so a runtime that subscribes to an event from
|
|
370
|
+
* inside its work must be ready for it to have already fired.
|
|
371
|
+
*/
|
|
372
|
+
readonly unit?: Module<UnitX, never, UnitNeeds>;
|
|
373
|
+
readonly clock?: Clock;
|
|
374
|
+
readonly signals?: boolean;
|
|
375
|
+
/**
|
|
376
|
+
* The probe server's port. Unset, it is bound from `PROBE_PORT` in `env`
|
|
377
|
+
* (default `9000`); `false` disables the probe server.
|
|
378
|
+
*/
|
|
379
|
+
readonly probes?: {
|
|
380
|
+
readonly port: number;
|
|
381
|
+
} | false;
|
|
382
|
+
readonly preDrainDelayMs?: number;
|
|
383
|
+
readonly drainTimeoutMs?: number;
|
|
384
|
+
readonly onEvent?: EventSink;
|
|
385
|
+
};
|
|
386
|
+
type RunningApp<E, Info = never> = {
|
|
387
|
+
readonly exited: AsyncResult<ExitReport, E | RuntimeStartFailed>;
|
|
388
|
+
readonly stop: () => void;
|
|
389
|
+
readonly requestDrain: () => void;
|
|
390
|
+
readonly phase: () => Phase;
|
|
391
|
+
/**
|
|
392
|
+
* The predicate `/readyz` answers from — serving, and not forced unready by
|
|
393
|
+
* a drain or an uncaught exception — read synchronously, which the probe
|
|
394
|
+
* endpoint is not.
|
|
395
|
+
*/
|
|
396
|
+
readonly ready: () => boolean;
|
|
397
|
+
/**
|
|
398
|
+
* The port the probe server actually bound, once the bind attempt has
|
|
399
|
+
* settled; `undefined` when probes are disabled or the bind failed.
|
|
400
|
+
*/
|
|
401
|
+
readonly probePort: () => AsyncResult<number | undefined, never>;
|
|
402
|
+
/**
|
|
403
|
+
* Whatever the runtime published on `Serving.info` once it is serving;
|
|
404
|
+
* `undefined` when it publishes nothing or never reached `serving`.
|
|
405
|
+
*/
|
|
406
|
+
readonly runtimeInfo: () => AsyncResult<Info | undefined, never>;
|
|
407
|
+
};
|
|
408
|
+
/**
|
|
409
|
+
* The phantom marker `start`, `runMain` and `Boot` all intersect onto their
|
|
410
|
+
* `module` parameter: `unknown` — and invisible — when the module exports a
|
|
411
|
+
* runtime, its exports cover what that runtime resolves and they cover the
|
|
412
|
+
* unit module's needs; one of three sentences otherwise, printed at the call
|
|
413
|
+
* site as the parameter type the argument did not match.
|
|
414
|
+
*
|
|
415
|
+
* `unknown` is the satisfied arm because intersecting it leaves the module
|
|
416
|
+
* type untouched. A runtime's `resolves` is checked against the module's
|
|
417
|
+
* exports only, never the unit module's: `RuntimeHost.ctx` is the application
|
|
418
|
+
* context, so a unit-only port would resolve to nothing there.
|
|
419
|
+
*/
|
|
420
|
+
type StartGate<X, UnitNeeds = never> = [Extract<X, RuntimeInstance>] extends [never] ? "NO RUNTIME — the module exports no port declared over RuntimePort" : [InstanceType<RuntimeResolvesOf<X>>] extends [X] ? [Exclude<UnitNeeds, X | Scope | Env>] extends [never] ? unknown : "UNSATISFIED UNIT NEEDS — the unit module needs a port the module does not export" : "UNSATISFIED RUNTIME PORTS — the runtime resolves a port the module does not export";
|
|
421
|
+
declare const start: <X, E, UnitX = never, UnitNeeds = never>(module: Module<X, E, Scope | Env> & StartGate<X, UnitNeeds>, options?: StartOptions<UnitX, UnitNeeds>) => RunningApp<E, RuntimeInfoOf<X>>;
|
|
422
|
+
//#endregion
|
|
423
|
+
//#region src/run-main.d.ts
|
|
424
|
+
/**
|
|
425
|
+
* Boot a module and turn its outcome into a process exit code — the front
|
|
426
|
+
* door, and the one sanctioned place this package decides a process's fate.
|
|
427
|
+
* `start` composed with the wait for `exited`: use `start` instead when the
|
|
428
|
+
* `RunningApp` itself is wanted (a test, an embedder, a dev runner booting
|
|
429
|
+
* two applications — none of which may claim `process.exitCode`).
|
|
430
|
+
*
|
|
431
|
+
* `exit` is injectable and defaults to setting `process.exitCode`: `runMain`
|
|
432
|
+
* never calls `process.exit()`, so pending output is flushed, an embedding
|
|
433
|
+
* host keeps control of its own lifetime, and a test can observe the code
|
|
434
|
+
* without ending the run.
|
|
435
|
+
*
|
|
436
|
+
* | outcome | code |
|
|
437
|
+
* | --- | --- |
|
|
438
|
+
* | exited cleanly | `0` |
|
|
439
|
+
* | startup failure (a modeled `Err`) | `1` |
|
|
440
|
+
* | a configuration port that could not be bound (`ConfigInvalid`, `PROBE_PORT` included) | `78` |
|
|
441
|
+
* | drained with work abandoned | `2` |
|
|
442
|
+
* | exited with teardown errors | `2` |
|
|
443
|
+
* | stopped by an uncaught exception or unhandled rejection | `70` |
|
|
444
|
+
* | a defect | `70` |
|
|
445
|
+
*
|
|
446
|
+
* The two `70`s are the same statement reached through the two channels a bug
|
|
447
|
+
* can take. A crash takes precedence over abandoned work.
|
|
448
|
+
*
|
|
449
|
+
* @example
|
|
450
|
+
* ```ts
|
|
451
|
+
* // `OrderApi` imports the application next to `http()` and exports
|
|
452
|
+
* // `HttpRuntime`. `PORT`, `HOST` and `PROBE_PORT` are read inside the graph.
|
|
453
|
+
* await runMain(OrderApi);
|
|
454
|
+
* ```
|
|
455
|
+
*/
|
|
456
|
+
declare const runMain: <X, E, UnitX = never, UnitNeeds = never>(module: Module<X, E, Scope | Env> & StartGate<X, UnitNeeds>, options?: StartOptions<UnitX, UnitNeeds>, exit?: (code: number) => void) => Promise<void>;
|
|
457
|
+
//#endregion
|
|
458
|
+
export { type Attributes, type Clock, type Counter, type DrainReport, type EventSink, type ExitReport, type Histogram, type KernelEvent, LEVELS, type Level, Logger, type LoggerService, Meter, type MeterService, type Phase, type RunUnit, type RunningApp, type Runtime, type RuntimeHost, type RuntimeInfoOf, RuntimePort, RuntimeStartFailed, SPAN_STATUS, type Serving, type Span, type SpanStatusCode, type StartGate, type StartOptions, type TeardownError, Tracer, type TracerService, type UnitMeta, type UnitRecord, type UnitRegistry, type UnitWork, currentUnit, releasedBy, runMain, start, stderrSink, systemClock };
|