@setu-ts/kernel 0.0.0-stage → 0.10.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/AGENT_CARD.md +47 -0
- package/LICENSE +21 -0
- package/README.md +152 -2
- package/dist/application/application.d.ts +174 -0
- package/dist/application/application.d.ts.map +1 -0
- package/dist/application/application.js +1905 -0
- package/dist/application/application.js.map +1 -0
- package/dist/application/inject-body.d.ts +58 -0
- package/dist/application/inject-body.d.ts.map +1 -0
- package/dist/application/inject-body.js +82 -0
- package/dist/application/inject-body.js.map +1 -0
- package/dist/context/request-context.d.ts +32 -0
- package/dist/context/request-context.d.ts.map +1 -0
- package/dist/context/request-context.js +136 -0
- package/dist/context/request-context.js.map +1 -0
- package/dist/context/response.d.ts +60 -0
- package/dist/context/response.d.ts.map +1 -0
- package/dist/context/response.js +193 -0
- package/dist/context/response.js.map +1 -0
- package/dist/diagnostics/buffer.d.ts +143 -0
- package/dist/diagnostics/buffer.d.ts.map +1 -0
- package/dist/diagnostics/buffer.js +190 -0
- package/dist/diagnostics/buffer.js.map +1 -0
- package/dist/diagnostics/collector.d.ts +284 -0
- package/dist/diagnostics/collector.d.ts.map +1 -0
- package/dist/diagnostics/collector.js +810 -0
- package/dist/diagnostics/collector.js.map +1 -0
- package/dist/diagnostics/projection.d.ts +153 -0
- package/dist/diagnostics/projection.d.ts.map +1 -0
- package/dist/diagnostics/projection.js +226 -0
- package/dist/diagnostics/projection.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/lifecycle/lifecycle-manager.d.ts +136 -0
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -0
- package/dist/lifecycle/lifecycle-manager.js +206 -0
- package/dist/lifecycle/lifecycle-manager.js.map +1 -0
- package/dist/pipeline/execute-chain.d.ts +71 -0
- package/dist/pipeline/execute-chain.d.ts.map +1 -0
- package/dist/pipeline/execute-chain.js +104 -0
- package/dist/pipeline/execute-chain.js.map +1 -0
- package/dist/pipeline/middleware-pipeline.d.ts +68 -0
- package/dist/pipeline/middleware-pipeline.d.ts.map +1 -0
- package/dist/pipeline/middleware-pipeline.js +82 -0
- package/dist/pipeline/middleware-pipeline.js.map +1 -0
- package/dist/registry/plugin-resolver.d.ts +42 -0
- package/dist/registry/plugin-resolver.d.ts.map +1 -0
- package/dist/registry/plugin-resolver.js +140 -0
- package/dist/registry/plugin-resolver.js.map +1 -0
- package/dist/registry/service-registry.d.ts +110 -0
- package/dist/registry/service-registry.d.ts.map +1 -0
- package/dist/registry/service-registry.js +197 -0
- package/dist/registry/service-registry.js.map +1 -0
- package/dist/router/route-matcher.d.ts +85 -0
- package/dist/router/route-matcher.d.ts.map +1 -0
- package/dist/router/route-matcher.js +105 -0
- package/dist/router/route-matcher.js.map +1 -0
- package/dist/router/router.d.ts +130 -0
- package/dist/router/router.d.ts.map +1 -0
- package/dist/router/router.js +350 -0
- package/dist/router/router.js.map +1 -0
- package/package.json +49 -3
- package/src/application/application.ts +2326 -0
- package/src/application/inject-body.ts +130 -0
- package/src/context/request-context.ts +169 -0
- package/src/context/response.ts +246 -0
- package/src/diagnostics/buffer.ts +243 -0
- package/src/diagnostics/collector.ts +1030 -0
- package/src/diagnostics/projection.ts +322 -0
- package/src/index.ts +20 -0
- package/src/lifecycle/lifecycle-manager.ts +282 -0
- package/src/pipeline/execute-chain.ts +162 -0
- package/src/pipeline/middleware-pipeline.ts +129 -0
- package/src/registry/plugin-resolver.ts +185 -0
- package/src/registry/service-registry.ts +262 -0
- package/src/router/route-matcher.ts +134 -0
- package/src/router/router.ts +485 -0
package/AGENT_CARD.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @setu-ts/kernel — application lifecycle and routing
|
|
2
|
+
|
|
3
|
+
**Add it:** scaffolds already use `createApplication()` in `setu.config.ts`; there is no `setu add`
|
|
4
|
+
entry or capability provided by the kernel itself.
|
|
5
|
+
|
|
6
|
+
## Register a module's routes
|
|
7
|
+
|
|
8
|
+
```ts
|
|
9
|
+
import type { IRouterApi } from '@setu-ts/common';
|
|
10
|
+
|
|
11
|
+
export function registerGreetingRoutes(router: IRouterApi): void {
|
|
12
|
+
router.get('/greeting/:name', {
|
|
13
|
+
handler: (ctx) => ctx.response.json({ hello: ctx.params.name }),
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Exercise the configured application without a socket
|
|
19
|
+
|
|
20
|
+
Use the scaffold's constructed application:
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
await app.start();
|
|
24
|
+
try {
|
|
25
|
+
const response = await app.inject({ method: 'GET', url: '/greeting/Ada' });
|
|
26
|
+
if (response.statusCode !== 200) throw new Error(response.body ?? 'Request failed');
|
|
27
|
+
const greeting = response.json<{ hello: string }>();
|
|
28
|
+
} finally {
|
|
29
|
+
await app.stop();
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Behaviour the types do not show
|
|
34
|
+
|
|
35
|
+
- Plugins register during `app.start()`, in dependency order; their capabilities are absent
|
|
36
|
+
beforehand. Startup requires a provider for `CAPABILITIES.RUNTIME`.
|
|
37
|
+
- Starting without a port does not bind a socket; `app.start({ port: 3000 })` listens.
|
|
38
|
+
- Duplicate method/path registrations throw rather than replace the first route.
|
|
39
|
+
- Injection JSON-serializes plain object bodies; it drops bodies on GET and HEAD.
|
|
40
|
+
- `app.inject` refuses streaming responses; use `app.fetch` and read the response stream.
|
|
41
|
+
- Shutdown hooks run on `app.stop()`; repeated stops share the same shutdown operation.
|
|
42
|
+
|
|
43
|
+
## Other options
|
|
44
|
+
|
|
45
|
+
- `createApplication({ plugins })` pre-registers plugins; `app.register` adds one before startup.
|
|
46
|
+
- `app.unregister(name)` removes pending plugins; `app.hasPlugin(name)` checks exact names.
|
|
47
|
+
- `diagnostics: {}` opts into a read-only diagnostic reader; omitted, none is created.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Setu-TS Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,153 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @setu-ts/kernel
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Unhandled HTTP errors use the registered logger, or `runtime.reportError` when no logger is
|
|
4
|
+
registered. The last-resort runtime sink receives the serialized error, request id, method, and
|
|
5
|
+
path; the client still receives an opaque 500 response. An injected runtime may omit the sink.
|
|
6
|
+
|
|
7
|
+
Plugin kernel for the Setu-TS framework: plugin registry, service registry, middleware pipeline,
|
|
8
|
+
router, and application lifecycle.
|
|
9
|
+
|
|
10
|
+
This package is the framework's orchestration layer. It resolves plugin dependencies, builds the
|
|
11
|
+
middleware pipeline and router, validates environment variables, and dispatches requests through the
|
|
12
|
+
pipeline to route handlers. It owns no runtime-specific behavior — every runtime operation goes
|
|
13
|
+
through `IRuntimeServices` provided by the runtime plugin.
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
# npm (pnpm add / yarn add / bun add work the same way)
|
|
19
|
+
npm install @setu-ts/kernel
|
|
20
|
+
|
|
21
|
+
# Deno
|
|
22
|
+
deno add npm:@setu-ts/kernel
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## What's Inside
|
|
26
|
+
|
|
27
|
+
| Area | Exports |
|
|
28
|
+
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
+
| Application | `createApplication()`, `ApplicationOptions`, `IKernelApplication`, `InjectRequest`, `InjectResponse`, `app.diagnostics` reader (opt-in) |
|
|
30
|
+
| Plugin resolution | `resolvePluginOrder()` (internal), dependency topological sort, cycle detection |
|
|
31
|
+
| Service registry | `ServiceRegistry` (internal), single/multi/lazy-factory registrations, request-scoped children |
|
|
32
|
+
| Middleware | `MiddlewarePipeline` (internal), priority-ordered execution, short-circuit, double-next guard |
|
|
33
|
+
| Router | `Router` (internal), 7 verbs, route groups, static-over-param matching preference |
|
|
34
|
+
| Lifecycle | `LifecycleManager` (internal), init/bootstrap/shutdown (LIFO)/close + request/response/error |
|
|
35
|
+
|
|
36
|
+
Only the seven public exports listed above are part of the public API; all concrete classes are
|
|
37
|
+
internal.
|
|
38
|
+
|
|
39
|
+
<!-- version:history -->
|
|
40
|
+
|
|
41
|
+
## Kernel diagnostics (optional, since 0.8.0)
|
|
42
|
+
|
|
43
|
+
Pass `diagnostics` to `createApplication` to enable an optional, read-only view of application
|
|
44
|
+
composition and kernel execution, exposed as the pull-only `app.diagnostics` reader. An omitted
|
|
45
|
+
option allocates nothing — no collector, ring, or timer — and the property is absent.
|
|
46
|
+
|
|
47
|
+
```typescript
|
|
48
|
+
const app = createApplication({
|
|
49
|
+
plugins: [RuntimePlugin()],
|
|
50
|
+
// labels is the disclosure decision: a name appears ONLY when it exactly
|
|
51
|
+
// matches an allowlist entry; everything else is projected as opaque ids.
|
|
52
|
+
diagnostics: { labels: { plugins: ['catalog'], routes: ['/items'] } },
|
|
53
|
+
});
|
|
54
|
+
await app.start();
|
|
55
|
+
|
|
56
|
+
const snapshot = app.diagnostics!.snapshot(); // bounded composition view
|
|
57
|
+
let cursor = 0;
|
|
58
|
+
const poll = setInterval(() => {
|
|
59
|
+
// Poll non-destructively: each read returns the events after `cursor`
|
|
60
|
+
// (up to 128) and reports evicted records — and any discarded by a failed
|
|
61
|
+
// start — as `lost`. Other readers polling
|
|
62
|
+
// at their own cursors are unaffected — reads never consume.
|
|
63
|
+
const batch = app.diagnostics!.read(cursor, 128);
|
|
64
|
+
cursor = batch.next;
|
|
65
|
+
}, 1000);
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
A plugin node's `version` is the one exception to the allowlist: it is emitted whenever it passes a
|
|
69
|
+
bounded semver grammar, so enabling diagnostics discloses every registered plugin's version.
|
|
70
|
+
|
|
71
|
+
`snapshot()` and `read()` never resolve a lazy factory, invoke application code, or mutate state,
|
|
72
|
+
and everything they return is frozen. Timing is monotonic from runtime registration; records before
|
|
73
|
+
that carry `null` timings. Startup failure and final shutdown clear retained metadata — a reader
|
|
74
|
+
then sees only the coarse state, the failure code, and drop counters. Bounded by construction: 1,024
|
|
75
|
+
nodes / 4,096 edges / 256 KiB per snapshot, 1,024 events / 1,024 bytes per event, with
|
|
76
|
+
`droppedEvents` and `lost` reporting what did not fit. See
|
|
77
|
+
[`PUBLIC_API.md`](https://github.com/setu-ts/setu-ts/blob/main/PUBLIC_API.md#kernel-diagnostics-setu-tskernel--setu-tscommon)
|
|
78
|
+
for the full contract; `scripts/inspect-kernel.ts` in the repository is a runnable consumer.
|
|
79
|
+
|
|
80
|
+
## Usage
|
|
81
|
+
|
|
82
|
+
Create an application, register plugins, and start it:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { createApplication } from '@setu-ts/kernel';
|
|
86
|
+
import { RuntimePlugin } from '@setu-ts/runtime';
|
|
87
|
+
|
|
88
|
+
const app = createApplication({
|
|
89
|
+
plugins: [RuntimePlugin()],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
app.router.get('/health', (ctx) => ctx.response.json({ status: 'ok' }));
|
|
93
|
+
|
|
94
|
+
await app.start({ port: 3000 });
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Test without a server using `inject()`:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
const res = await app.inject({ method: 'GET', url: 'http://localhost/health' });
|
|
101
|
+
console.log(res.statusCode, res.json());
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Options
|
|
105
|
+
|
|
106
|
+
`createApplication(options?)` takes two options:
|
|
107
|
+
|
|
108
|
+
| Option | Type | Default | Description |
|
|
109
|
+
| ------------- | -------------------------- | -------- | ------------------------------------------------------------- |
|
|
110
|
+
| `plugins` | `IPlugin[]` | `[]` | Plugins registered before `start()` is called. |
|
|
111
|
+
| `diagnostics` | `KernelDiagnosticsOptions` | disabled | Opt-in kernel diagnostics; see the diagnostics section above. |
|
|
112
|
+
|
|
113
|
+
For plugin options needed before registration, see
|
|
114
|
+
[Configuration a plugin needs at construction](https://github.com/setu-ts/setu-ts/blob/main/packages/config-plugin/README.md#configuration-a-plugin-needs-at-construction).
|
|
115
|
+
|
|
116
|
+
Everything else is configured on the application itself: `app.register(plugin)` adds a plugin after
|
|
117
|
+
construction, `app.middleware.add()` installs global middleware, and `app.start({ port })` binds the
|
|
118
|
+
socket. `start()` with no `port` runs the full lifecycle without listening, which is what `inject()`
|
|
119
|
+
and the CLI's command discovery rely on.
|
|
120
|
+
|
|
121
|
+
## Rules
|
|
122
|
+
|
|
123
|
+
- No runtime-specific APIs — all timers, UUIDs, and clocks go through `IRuntimeServices`.
|
|
124
|
+
- No `console.*` — the kernel has no logger; it never logs.
|
|
125
|
+
- Listening requires both `CAPABILITIES.HTTP_ADAPTER` and a `port` option; otherwise `start()` skips
|
|
126
|
+
server creation (so `inject()` and tests need no server).
|
|
127
|
+
- The kernel emits only bare 404/500 JSON; error formatting belongs to the exceptions package.
|
|
128
|
+
- A runtime provider is mandatory — `start()` fails fast if no plugin provides
|
|
129
|
+
`CAPABILITIES.RUNTIME`.
|
|
130
|
+
- A failed `start()` can be corrected and retried only when it failed before any plugin registered
|
|
131
|
+
(plugin resolution). Once a plugin's `register()` has run, a second `start()` throws by name —
|
|
132
|
+
create a new application instead.
|
|
133
|
+
|
|
134
|
+
See the repository's
|
|
135
|
+
[`PUBLIC_API.md`](https://github.com/setu-ts/setu-ts/blob/main/PUBLIC_API.md#createapplication-setu-tskernel)
|
|
136
|
+
for the full API contract and
|
|
137
|
+
[`ARCHITECTURE.md`](https://github.com/setu-ts/setu-ts/blob/main/ARCHITECTURE.md) for how this
|
|
138
|
+
package fits the plugin architecture.
|
|
139
|
+
|
|
140
|
+
## Exports
|
|
141
|
+
|
|
142
|
+
| Export | Kind |
|
|
143
|
+
| ------------------------------- | --------- |
|
|
144
|
+
| `createApplication` | function |
|
|
145
|
+
| `ApplicationOptions` | interface |
|
|
146
|
+
| `IKernelApplication` | interface |
|
|
147
|
+
| `InjectRequest` | interface |
|
|
148
|
+
| `InjectResponse` | interface |
|
|
149
|
+
| `KernelDiagnosticsLabelOptions` | interface |
|
|
150
|
+
| `KernelDiagnosticsOptions` | interface |
|
|
151
|
+
|
|
152
|
+
Generated from the package barrel by `pnpm run docs:exports`; `pnpm run check:docs` fails when it
|
|
153
|
+
drifts.
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import type { IApplication, IPlugin } from '@setu-ts/common';
|
|
2
|
+
import { DiagnosticsCollector } from '../diagnostics/collector.ts';
|
|
3
|
+
import type { KernelDiagnosticsLabelOptions, KernelDiagnosticsOptions } from '../diagnostics/projection.ts';
|
|
4
|
+
export type { KernelDiagnosticsLabelOptions, KernelDiagnosticsOptions };
|
|
5
|
+
/**
|
|
6
|
+
* Returns an application's diagnostics collector, or `undefined` when
|
|
7
|
+
* diagnostics were not enabled.
|
|
8
|
+
*
|
|
9
|
+
* @param app - The application to look up
|
|
10
|
+
* @returns The collector, or `undefined`
|
|
11
|
+
* @internal Kernel-internal; never exported from `src/index.ts`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function collectorOf(app: IApplication): DiagnosticsCollector | undefined;
|
|
14
|
+
/** Options for {@linkcode createApplication}. */
|
|
15
|
+
export interface ApplicationOptions {
|
|
16
|
+
/** Plugins to pre-register before {@linkcode IApplication.start}. */
|
|
17
|
+
plugins?: IPlugin[];
|
|
18
|
+
/**
|
|
19
|
+
* Explicit kernel diagnostics activation. An omitted option leaves
|
|
20
|
+
* {@linkcode IApplication.diagnostics} absent and creates no collector,
|
|
21
|
+
* metadata mirror, ring buffer, or timer anywhere in the kernel; supplying
|
|
22
|
+
* an object (even `{}`) enables collection. Malformed label allowlists
|
|
23
|
+
* reject construction — the one diagnostics failure that may.
|
|
24
|
+
*
|
|
25
|
+
* @since 0.8.0
|
|
26
|
+
*/
|
|
27
|
+
diagnostics?: KernelDiagnosticsOptions;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Inject request shape for {@linkcode IKernelApplication.inject}.
|
|
31
|
+
*
|
|
32
|
+
* @since 0.1.0
|
|
33
|
+
*/
|
|
34
|
+
export interface InjectRequest {
|
|
35
|
+
/** Socket peer to carry into `IRequest.ip`; omitted by default. */
|
|
36
|
+
ip?: string;
|
|
37
|
+
/** HTTP method. */
|
|
38
|
+
method: string;
|
|
39
|
+
/** Full request URL. */
|
|
40
|
+
url: string;
|
|
41
|
+
/** Request headers. */
|
|
42
|
+
headers?: Record<string, string> | Headers;
|
|
43
|
+
/**
|
|
44
|
+
* Request body. Carried VERBATIM for every byte-ish shape, but the
|
|
45
|
+
* content-type default differs between them: a `Uint8Array` and an
|
|
46
|
+
* `ArrayBuffer` get NONE, since only the caller knows whether those bytes
|
|
47
|
+
* are multipart, JSON, or an image, while a `Blob` DECLARES its own and
|
|
48
|
+
* contributes a non-empty `.type` as the default — matching the platform,
|
|
49
|
+
* whose `new Request(url, { body: blob })` sets the header from it and omits
|
|
50
|
+
* it when the blob has none. A `URLSearchParams` is serialised with its own
|
|
51
|
+
* `toString()` and defaults the content type to
|
|
52
|
+
* `application/x-www-form-urlencoded`; a plain object is JSON-serialised and
|
|
53
|
+
* a bare `string` is carried as-is, both defaulting to `application/json`.
|
|
54
|
+
* An explicitly supplied content type always wins, and the default is
|
|
55
|
+
* written onto a COPY of `headers`, so a `Headers` instance reused across
|
|
56
|
+
* two injected requests is never mutated.
|
|
57
|
+
*
|
|
58
|
+
* Any other shape — an array, a `Date`, a class instance, a number — is
|
|
59
|
+
* REFUSED with a `TypeError` naming the received type, rather than silently
|
|
60
|
+
* JSON-stringified as before (which turned a `Uint8Array` into
|
|
61
|
+
* `{"0":97,…}` and every other non-string into `{}`).
|
|
62
|
+
*
|
|
63
|
+
* The last arm is deliberately `Record<string, unknown>`, not `object`:
|
|
64
|
+
* `object` admits arrays, `Date`s, streams and every class instance, so the
|
|
65
|
+
* union would go on accepting exactly the values that used to silently
|
|
66
|
+
* JSON-coerce.
|
|
67
|
+
*/
|
|
68
|
+
body?: string | Uint8Array | ArrayBuffer | Blob | URLSearchParams | Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Inject response shape returned by {@linkcode IKernelApplication.inject}.
|
|
72
|
+
*
|
|
73
|
+
* @since 0.1.0
|
|
74
|
+
*/
|
|
75
|
+
export interface InjectResponse {
|
|
76
|
+
/** Response status code. */
|
|
77
|
+
readonly statusCode: number;
|
|
78
|
+
/** Response headers. */
|
|
79
|
+
readonly headers: Headers;
|
|
80
|
+
/**
|
|
81
|
+
* Raw response body as text. A byte body (from `response.send(bytes)`) is
|
|
82
|
+
* UTF-8 decoded; `null` only when the response genuinely has no body.
|
|
83
|
+
*/
|
|
84
|
+
readonly body: string | null;
|
|
85
|
+
/**
|
|
86
|
+
* Parses the response body as JSON.
|
|
87
|
+
*
|
|
88
|
+
* @throws {Error} If the response has no body
|
|
89
|
+
* @throws {SyntaxError} If the body is not valid JSON
|
|
90
|
+
*/
|
|
91
|
+
json<T>(): T;
|
|
92
|
+
}
|
|
93
|
+
/** Kernel application extends IApplication with inject() capability. */
|
|
94
|
+
export interface IKernelApplication extends IApplication {
|
|
95
|
+
/**
|
|
96
|
+
* Synthesizes an incoming request and runs it through the full pipeline
|
|
97
|
+
* without requiring a listening server.
|
|
98
|
+
*
|
|
99
|
+
* @param request - The synthetic request
|
|
100
|
+
* @returns The inject response
|
|
101
|
+
*/
|
|
102
|
+
inject(request: InjectRequest): Promise<InjectResponse>;
|
|
103
|
+
/**
|
|
104
|
+
* Removes a pending plugin by name before the application starts.
|
|
105
|
+
*
|
|
106
|
+
* Plugins do not run until `start()`, so removing one here means its
|
|
107
|
+
* `register()` — and any eager side effect inside it, such as a database
|
|
108
|
+
* adapter's `connect()` — never happens at all. That is the one thing
|
|
109
|
+
* overriding a capability cannot do: an override replaces what consumers
|
|
110
|
+
* resolve, after the real plugin has already run.
|
|
111
|
+
*
|
|
112
|
+
* Removing a plugin another plugin declares in `dependencies` is not
|
|
113
|
+
* refused here; `start()` reports it — naming the dependent plugin and the unsatisfied
|
|
114
|
+
* capability, though not the removed plugin's own name when the two differ
|
|
115
|
+
* (`database-plugin` provides `database`).
|
|
116
|
+
*
|
|
117
|
+
* Removes **every** pending plugin carrying the name, not the first. Two may
|
|
118
|
+
* share one — the kernel refuses duplicates at `start()`, not at `register()`
|
|
119
|
+
* — and dropping one would leave the other running while the caller was told
|
|
120
|
+
* the name was gone.
|
|
121
|
+
*
|
|
122
|
+
* @param name - The plugin's `name`, as declared on `IPlugin`
|
|
123
|
+
* @returns `true` when at least one plugin was removed, `false` when none carried that name
|
|
124
|
+
* @throws {Error} If startup has begun — including after a `start()` that
|
|
125
|
+
* FAILED, since the plugins that ran before the failure cannot be un-run
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const app = createApp(); // the project's own composition root
|
|
129
|
+
* app.unregister('database'); // the real adapter never connects
|
|
130
|
+
* await app.start();
|
|
131
|
+
* ```
|
|
132
|
+
* @since 0.6.0
|
|
133
|
+
*/
|
|
134
|
+
unregister(name: string): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* Reports whether a plugin carrying this name is pending.
|
|
137
|
+
*
|
|
138
|
+
* A pure read — it neither resolves nor constructs anything. It exists so a
|
|
139
|
+
* caller applying several exclusions can validate the whole set BEFORE
|
|
140
|
+
* removing any of them: `unregister` mutates immediately, so removing as you
|
|
141
|
+
* go leaves earlier exclusions applied when a later name turns out to be
|
|
142
|
+
* misspelled, handing a caller that catches the error a silently altered
|
|
143
|
+
* application.
|
|
144
|
+
*
|
|
145
|
+
* Answers against the registered plugin list, which startup does NOT clear — so
|
|
146
|
+
* after `start()` it still reports `true` for a plugin that has already run.
|
|
147
|
+
* Its purpose is pre-`start()` validation, and `unregister` throws once any
|
|
148
|
+
* plugin has registered, so a later answer is not actionable either way.
|
|
149
|
+
*
|
|
150
|
+
* @param name - The plugin's `name`, as declared on `IPlugin`
|
|
151
|
+
* @returns `true` when at least one pending plugin carries that name
|
|
152
|
+
* @since 0.6.0
|
|
153
|
+
*/
|
|
154
|
+
hasPlugin(name: string): boolean;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Creates a new kernel application instance.
|
|
158
|
+
*
|
|
159
|
+
* @param options - Optional application configuration including pre-registered plugins
|
|
160
|
+
* @returns The kernel application instance with inject() capability
|
|
161
|
+
* @example
|
|
162
|
+
* ```typescript
|
|
163
|
+
* import { createApplication } from '@setu-ts/kernel';
|
|
164
|
+
*
|
|
165
|
+
* const app = createApplication({
|
|
166
|
+
* plugins: [RuntimePlugin()],
|
|
167
|
+
* });
|
|
168
|
+
*
|
|
169
|
+
* await app.start({ port: 3000 });
|
|
170
|
+
* ```
|
|
171
|
+
* @since 0.1.0
|
|
172
|
+
*/
|
|
173
|
+
export declare function createApplication(options?: ApplicationOptions): IKernelApplication;
|
|
174
|
+
//# sourceMappingURL=application.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../../src/application/application.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAMV,YAAY,EASZ,OAAO,EAYR,MAAM,iBAAiB,CAAC;AAgBzB,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,OAAO,KAAK,EACV,6BAA6B,EAC7B,wBAAwB,EACzB,MAAM,8BAA8B,CAAC;AAOtC,YAAY,EAAE,6BAA6B,EAAE,wBAAwB,EAAE,CAAC;AAkBxE;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,oBAAoB,GAAG,SAAS,CAE/E;AAED,iDAAiD;AACjD,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;IACpB;;;;;;;;OAQG;IACH,WAAW,CAAC,EAAE,wBAAwB,CAAC;CACxC;AAED;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,mEAAmE;IACnE,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,mBAAmB;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,wBAAwB;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC;IAC3C;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,IAAI,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,WAAW,GAAG,IAAI,GAAG,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC7F;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,4BAA4B;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wBAAwB;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B;;;;;OAKG;IACH,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC;CACd;AAED,wEAAwE;AACxE,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD;;;;;;OAMG;IACH,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAClC;;;;;;;;;;;;;;;;;;OAkBG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAClC;AAqgED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,kBAAkB,CAQlF"}
|