@setu-ts/kernel 0.9.0 → 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/README.md +13 -5
- package/dist/application/application.d.ts +2 -0
- package/dist/application/application.d.ts.map +1 -1
- package/dist/application/application.js +54 -11
- package/dist/application/application.js.map +1 -1
- package/dist/application/inject-body.d.ts.map +1 -1
- package/dist/application/inject-body.js +2 -2
- package/dist/application/inject-body.js.map +1 -1
- package/dist/context/response.js.map +1 -1
- package/dist/diagnostics/buffer.d.ts.map +1 -1
- package/dist/diagnostics/buffer.js +9 -4
- package/dist/diagnostics/buffer.js.map +1 -1
- package/dist/diagnostics/collector.d.ts.map +1 -1
- package/dist/diagnostics/collector.js +1 -3
- package/dist/diagnostics/collector.js.map +1 -1
- package/dist/lifecycle/lifecycle-manager.d.ts.map +1 -1
- package/dist/lifecycle/lifecycle-manager.js.map +1 -1
- package/dist/pipeline/execute-chain.d.ts.map +1 -1
- package/dist/pipeline/execute-chain.js.map +1 -1
- package/dist/pipeline/middleware-pipeline.d.ts.map +1 -1
- package/dist/pipeline/middleware-pipeline.js.map +1 -1
- package/dist/registry/service-registry.d.ts.map +1 -1
- package/dist/registry/service-registry.js.map +1 -1
- package/dist/router/route-matcher.d.ts.map +1 -1
- package/dist/router/route-matcher.js +4 -1
- package/dist/router/route-matcher.js.map +1 -1
- package/package.json +9 -8
- package/src/application/application.ts +86 -59
- package/src/application/inject-body.ts +8 -9
- package/src/context/response.ts +2 -10
- package/src/diagnostics/buffer.ts +20 -18
- package/src/diagnostics/collector.ts +19 -20
- package/src/lifecycle/lifecycle-manager.ts +1 -6
- package/src/pipeline/execute-chain.ts +1 -3
- package/src/pipeline/middleware-pipeline.ts +1 -3
- package/src/registry/service-registry.ts +4 -9
- package/src/router/route-matcher.ts +15 -12
package/src/context/response.ts
CHANGED
|
@@ -198,11 +198,7 @@ class BufferedResponseSnapshot implements Extract<ResponseSnapshot, { readonly s
|
|
|
198
198
|
readonly status: number;
|
|
199
199
|
readonly body: Uint8Array | string | null;
|
|
200
200
|
|
|
201
|
-
constructor(
|
|
202
|
-
response: ResponseBuilder,
|
|
203
|
-
status: number,
|
|
204
|
-
body: Uint8Array | string | null,
|
|
205
|
-
) {
|
|
201
|
+
constructor(response: ResponseBuilder, status: number, body: Uint8Array | string | null) {
|
|
206
202
|
this.#response = response;
|
|
207
203
|
this.status = status;
|
|
208
204
|
this.body = body;
|
|
@@ -224,11 +220,7 @@ class StreamingResponseSnapshot implements Extract<ResponseSnapshot, { readonly
|
|
|
224
220
|
readonly status: number;
|
|
225
221
|
readonly body: ReadableStream<Uint8Array>;
|
|
226
222
|
|
|
227
|
-
constructor(
|
|
228
|
-
response: ResponseBuilder,
|
|
229
|
-
status: number,
|
|
230
|
-
body: ReadableStream<Uint8Array>,
|
|
231
|
-
) {
|
|
223
|
+
constructor(response: ResponseBuilder, status: number, body: ReadableStream<Uint8Array>) {
|
|
232
224
|
this.#response = response;
|
|
233
225
|
this.status = status;
|
|
234
226
|
this.body = body;
|
|
@@ -59,19 +59,21 @@ const EVENT_SKELETON_BYTES = 220;
|
|
|
59
59
|
* @returns `true` when the encoded event is within {@linkcode MAX_EVENT_BYTES}
|
|
60
60
|
* @since 0.8.0
|
|
61
61
|
*/
|
|
62
|
-
export function eventWithinByteCap(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
fieldLengths.
|
|
74
|
-
fieldLengths.
|
|
62
|
+
export function eventWithinByteCap(fieldLengths: {
|
|
63
|
+
readonly operationId: number;
|
|
64
|
+
readonly parentOperationId: number;
|
|
65
|
+
readonly nodeId: number;
|
|
66
|
+
readonly traceId: number;
|
|
67
|
+
readonly spanId: number;
|
|
68
|
+
readonly statusCode: boolean;
|
|
69
|
+
}): boolean {
|
|
70
|
+
const bounded =
|
|
71
|
+
EVENT_SKELETON_BYTES +
|
|
72
|
+
fieldLengths.operationId +
|
|
73
|
+
fieldLengths.parentOperationId +
|
|
74
|
+
fieldLengths.nodeId +
|
|
75
|
+
fieldLengths.traceId +
|
|
76
|
+
fieldLengths.spanId +
|
|
75
77
|
(fieldLengths.statusCode ? 21 : 0);
|
|
76
78
|
return bounded <= MAX_EVENT_BYTES;
|
|
77
79
|
}
|
|
@@ -111,15 +113,15 @@ export function validateReadCursor(
|
|
|
111
113
|
limit: number | undefined,
|
|
112
114
|
lastSequence: number,
|
|
113
115
|
): { after: number; limit: number } {
|
|
114
|
-
if (
|
|
115
|
-
typeof after !== 'number' || !Number.isSafeInteger(after) || after < 0
|
|
116
|
-
) {
|
|
116
|
+
if (typeof after !== 'number' || !Number.isSafeInteger(after) || after < 0) {
|
|
117
117
|
throw new RangeError('Invalid diagnostics cursor: expected a non-negative safe integer.');
|
|
118
118
|
}
|
|
119
119
|
const effectiveLimit = limit ?? MAX_READ_LIMIT;
|
|
120
120
|
if (
|
|
121
|
-
typeof effectiveLimit !== 'number' ||
|
|
122
|
-
effectiveLimit
|
|
121
|
+
typeof effectiveLimit !== 'number' ||
|
|
122
|
+
!Number.isSafeInteger(effectiveLimit) ||
|
|
123
|
+
effectiveLimit < 1 ||
|
|
124
|
+
effectiveLimit > MAX_READ_LIMIT
|
|
123
125
|
) {
|
|
124
126
|
throw new RangeError(
|
|
125
127
|
`Invalid diagnostics read limit: expected an integer from 1 to ${MAX_READ_LIMIT}.`,
|
|
@@ -508,9 +508,8 @@ export class DiagnosticsCollector implements IDiagnosticsSource {
|
|
|
508
508
|
stages.push(stage);
|
|
509
509
|
}
|
|
510
510
|
this.#routeMiddlewareNodes.set(event.entryIndex, stages);
|
|
511
|
-
const ownerNode =
|
|
512
|
-
? undefined
|
|
513
|
-
: this.#pluginNodesByName.get(event.owner);
|
|
511
|
+
const ownerNode =
|
|
512
|
+
event.owner === undefined ? undefined : this.#pluginNodesByName.get(event.owner);
|
|
514
513
|
if (ownerNode !== undefined) {
|
|
515
514
|
this.#addEdge(ownerNode.id, id, 'owns');
|
|
516
515
|
}
|
|
@@ -631,9 +630,10 @@ export class DiagnosticsCollector implements IDiagnosticsSource {
|
|
|
631
630
|
startedAtMs: number | null;
|
|
632
631
|
durationMs: number | null;
|
|
633
632
|
}): void {
|
|
634
|
-
const node =
|
|
635
|
-
|
|
636
|
-
|
|
633
|
+
const node =
|
|
634
|
+
parts.scope === 'global'
|
|
635
|
+
? this.#globalMiddlewareNodes[parts.position]?.id
|
|
636
|
+
: this.#routeMiddlewareNodes.get(parts.routeEntryIndex ?? -1)?.[parts.position]?.id;
|
|
637
637
|
this.#emitEvent({
|
|
638
638
|
kind: 'middleware',
|
|
639
639
|
stage: parts.scope,
|
|
@@ -797,11 +797,7 @@ export class DiagnosticsCollector implements IDiagnosticsSource {
|
|
|
797
797
|
this.#snapshotDirty = true;
|
|
798
798
|
}
|
|
799
799
|
|
|
800
|
-
#nextNodeId(
|
|
801
|
-
prefix: string,
|
|
802
|
-
current: number,
|
|
803
|
-
assign: (next: number) => void,
|
|
804
|
-
): string | undefined {
|
|
800
|
+
#nextNodeId(prefix: string, current: number, assign: (next: number) => void): string | undefined {
|
|
805
801
|
// Fixed v1 limit: stop ADDING topology at 1,024 nodes and report the
|
|
806
802
|
// truncation rather than retaining unbounded metadata.
|
|
807
803
|
if (this.#nodes.length >= MAX_NODES) {
|
|
@@ -926,12 +922,14 @@ export class DiagnosticsCollector implements IDiagnosticsSource {
|
|
|
926
922
|
}
|
|
927
923
|
// Identifiers are carried only when they are well-formed and non-zero —
|
|
928
924
|
// the W3C all-zero ids mean "invalid", not "anonymous".
|
|
929
|
-
const traceId =
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
925
|
+
const traceId =
|
|
926
|
+
TRACE_ID_PATTERN.test(context.traceId) && NON_ZERO.test(context.traceId)
|
|
927
|
+
? context.traceId
|
|
928
|
+
: undefined;
|
|
929
|
+
const spanId =
|
|
930
|
+
SPAN_ID_PATTERN.test(context.spanId) && NON_ZERO.test(context.spanId)
|
|
931
|
+
? context.spanId
|
|
932
|
+
: undefined;
|
|
935
933
|
return {
|
|
936
934
|
...(traceId !== undefined ? { traceId } : {}),
|
|
937
935
|
...(spanId !== undefined ? { spanId } : {}),
|
|
@@ -967,9 +965,10 @@ export class DiagnosticsCollector implements IDiagnosticsSource {
|
|
|
967
965
|
// A status is recorded as the application set it — any finite number,
|
|
968
966
|
// ranged or not — but a non-finite one is omitted, for the same reason a
|
|
969
967
|
// non-finite priority is: it would serialize to `null` in a `number` field.
|
|
970
|
-
const statusCode =
|
|
971
|
-
|
|
972
|
-
|
|
968
|
+
const statusCode =
|
|
969
|
+
parts.statusCode !== undefined && Number.isFinite(parts.statusCode)
|
|
970
|
+
? parts.statusCode
|
|
971
|
+
: undefined;
|
|
973
972
|
const parentLength = parts.parentOperationId?.length ?? 0;
|
|
974
973
|
const withinCap = eventWithinByteCap({
|
|
975
974
|
operationId: parts.operationId.length,
|
|
@@ -22,12 +22,7 @@ type ErrorHook = (error: Error, ctx: IRequestContext) => void | Promise<void>;
|
|
|
22
22
|
* @since 0.8.0
|
|
23
23
|
*/
|
|
24
24
|
export type LifecycleHookPhase =
|
|
25
|
-
|
|
26
|
-
| 'init'
|
|
27
|
-
| 'bootstrap'
|
|
28
|
-
| 'stopping'
|
|
29
|
-
| 'shutdown'
|
|
30
|
-
| 'close';
|
|
25
|
+
'register-hook' | 'init' | 'bootstrap' | 'stopping' | 'shutdown' | 'close';
|
|
31
26
|
|
|
32
27
|
/**
|
|
33
28
|
* One hook invocation observed with its fixed phase plus execution ordinal —
|
|
@@ -123,9 +123,7 @@ export async function executeChain(
|
|
|
123
123
|
// anonymous function — so `??` never fell back and the message read
|
|
124
124
|
// "…in middleware " with a blank name. `||` is the correct operator.
|
|
125
125
|
const label = names?.[position] || fn.name || '<anonymous>';
|
|
126
|
-
throw new Error(
|
|
127
|
-
`next() called multiple times in middleware ${label}`,
|
|
128
|
-
);
|
|
126
|
+
throw new Error(`next() called multiple times in middleware ${label}`);
|
|
129
127
|
}
|
|
130
128
|
nextCalled = true;
|
|
131
129
|
if (observer !== undefined && responseEnded()) {
|
|
@@ -71,9 +71,7 @@ export class MiddlewarePipeline implements IMiddlewareApi {
|
|
|
71
71
|
if (this.#compiled !== null) {
|
|
72
72
|
return this.#compiled;
|
|
73
73
|
}
|
|
74
|
-
const sorted = [...this.#entries].sort(
|
|
75
|
-
(a, b) => a.priority - b.priority || a.index - b.index,
|
|
76
|
-
);
|
|
74
|
+
const sorted = [...this.#entries].sort((a, b) => a.priority - b.priority || a.index - b.index);
|
|
77
75
|
this.#compiled = sorted.map((entry) => entry.fn);
|
|
78
76
|
this.#compiledNames = sorted.map((entry) => entry.name);
|
|
79
77
|
this.#compiledDescriptors = sorted.map((entry, position) => ({
|
|
@@ -162,17 +162,11 @@ export class ServiceRegistry implements IServiceRegistry {
|
|
|
162
162
|
}
|
|
163
163
|
|
|
164
164
|
/** Installs the application-owned callback for startup-time mutations. */
|
|
165
|
-
setObserver(
|
|
166
|
-
observer: (kind: 'override' | 'unregister', token: CapabilityToken) => void,
|
|
167
|
-
): void {
|
|
165
|
+
setObserver(observer: (kind: 'override' | 'unregister', token: CapabilityToken) => void): void {
|
|
168
166
|
this.#observer = observer;
|
|
169
167
|
}
|
|
170
168
|
|
|
171
|
-
register<T extends object>(
|
|
172
|
-
token: CapabilityToken,
|
|
173
|
-
service: T,
|
|
174
|
-
options?: RegisterOptions,
|
|
175
|
-
): void {
|
|
169
|
+
register<T extends object>(token: CapabilityToken, service: T, options?: RegisterOptions): void {
|
|
176
170
|
this.#store(token, { instance: service }, options);
|
|
177
171
|
}
|
|
178
172
|
|
|
@@ -187,7 +181,8 @@ export class ServiceRegistry implements IServiceRegistry {
|
|
|
187
181
|
get<T extends object>(token: CapabilityToken): T {
|
|
188
182
|
const registration = this.#lookup(token);
|
|
189
183
|
if (registration === undefined) {
|
|
190
|
-
const advice =
|
|
184
|
+
const advice =
|
|
185
|
+
this.#missExplanation?.(token) ??
|
|
191
186
|
'Register a plugin that provides it, or check the token spelling against CAPABILITIES.';
|
|
192
187
|
throw new Error(`No service registered for capability '${token}'. ${advice}`);
|
|
193
188
|
}
|
|
@@ -53,18 +53,21 @@ export function parsePattern(pattern: string): readonly Segment[] {
|
|
|
53
53
|
if (normalized === '/') {
|
|
54
54
|
return [{ type: 'static', value: '' }];
|
|
55
55
|
}
|
|
56
|
-
return normalized
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
return normalized
|
|
57
|
+
.slice(1)
|
|
58
|
+
.split('/')
|
|
59
|
+
.map((part) => {
|
|
60
|
+
if (part.startsWith(':')) {
|
|
61
|
+
return { type: 'param', name: part.slice(1) };
|
|
62
|
+
}
|
|
63
|
+
// Exactly `*`, never a segment merely CONTAINING one: `/a*` is not a pattern
|
|
64
|
+
// any first-party plugin registers, and treating it as a wildcard would lower
|
|
65
|
+
// the specificity of a path that matches literally.
|
|
66
|
+
if (part === '*') {
|
|
67
|
+
return { type: 'wildcard' };
|
|
68
|
+
}
|
|
69
|
+
return { type: 'static', value: part };
|
|
70
|
+
});
|
|
68
71
|
}
|
|
69
72
|
|
|
70
73
|
/**
|