@pome-sh/cli 0.18.0 → 0.20.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/dist/build-info.json +3 -3
- package/dist/src/capture-server/index.js +1 -1
- package/dist/src/runner/mergeAdapterSignals.d.ts +23 -0
- package/dist/src/runner/mergeAdapterSignals.js +45 -3
- package/dist/src/scaffolds/mcp-loop/loop.js +3 -3
- package/node_modules/@hono/node-server/README.md +47 -1
- package/node_modules/@hono/node-server/dist/early-hints.cjs +35 -0
- package/node_modules/@hono/node-server/dist/early-hints.d.cts +16 -0
- package/node_modules/@hono/node-server/dist/early-hints.d.mts +16 -0
- package/node_modules/@hono/node-server/dist/early-hints.mjs +33 -0
- package/node_modules/@hono/node-server/dist/index.cjs +10 -6
- package/node_modules/@hono/node-server/dist/index.mjs +10 -6
- package/node_modules/@hono/node-server/package.json +14 -3
- package/node_modules/@pome-sh/sdk/dist/check-state-path.d.ts +71 -0
- package/node_modules/@pome-sh/sdk/dist/check-state-path.js +200 -0
- package/node_modules/@pome-sh/sdk/dist/check-state-path.js.map +1 -0
- package/node_modules/@pome-sh/sdk/dist/checks.d.ts +2 -0
- package/node_modules/@pome-sh/sdk/dist/checks.js +5 -0
- package/node_modules/@pome-sh/sdk/dist/checks.js.map +1 -1
- package/node_modules/@pome-sh/sdk/dist/recorder.d.ts +10 -1
- package/node_modules/@pome-sh/sdk/dist/recorder.js +11 -2
- package/node_modules/@pome-sh/sdk/dist/recorder.js.map +1 -1
- package/node_modules/@pome-sh/sdk/package.json +4 -4
- package/node_modules/@pome-sh/shared-types/dist/otel/event-schema.d.ts +111 -24
- package/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.js +3 -3
- package/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.d.ts +6 -3
- package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js +7 -2
- package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/otel/map-span.js +1 -1
- package/node_modules/@pome-sh/shared-types/dist/otel/map-span.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/otel/span-event.d.ts +68 -4
- package/node_modules/@pome-sh/shared-types/dist/otel/span-event.js +19 -5
- package/node_modules/@pome-sh/shared-types/dist/otel/span-event.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/dist/recorder-events.d.ts +60 -28
- package/node_modules/@pome-sh/shared-types/dist/recorder-events.js +81 -10
- package/node_modules/@pome-sh/shared-types/dist/recorder-events.js.map +1 -1
- package/node_modules/@pome-sh/shared-types/package.json +1 -1
- package/node_modules/@pome-sh/shared-types/trace-contract.json +49 -1
- package/node_modules/@pome-sh/twin-github/dist/src/check-issues.js +35 -9
- package/node_modules/@pome-sh/twin-github/dist/src/check-issues.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/check-pulls.js +21 -5
- package/node_modules/@pome-sh/twin-github/dist/src/check-pulls.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/check-repos.d.ts +3 -0
- package/node_modules/@pome-sh/twin-github/dist/src/check-repos.js +111 -6
- package/node_modules/@pome-sh/twin-github/dist/src/check-repos.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/check-state.d.ts +21 -1
- package/node_modules/@pome-sh/twin-github/dist/src/check-state.js +77 -10
- package/node_modules/@pome-sh/twin-github/dist/src/check-state.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/dist/src/checks.d.ts +2 -0
- package/node_modules/@pome-sh/twin-github/dist/src/checks.js +5 -1
- package/node_modules/@pome-sh/twin-github/dist/src/checks.js.map +1 -1
- package/node_modules/@pome-sh/twin-github/package.json +6 -6
- package/node_modules/@pome-sh/twin-gmail/CHANGELOG.md +36 -0
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js +19 -4
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js.map +1 -1
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js +15 -4
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js.map +1 -1
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js +19 -3
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js.map +1 -1
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.d.ts +26 -1
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js +52 -16
- package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js.map +1 -1
- package/node_modules/@pome-sh/twin-gmail/package.json +5 -5
- package/node_modules/@pome-sh/twin-linear/CHANGELOG.md +36 -0
- package/node_modules/@pome-sh/twin-linear/dist/src/check-comments.js +15 -3
- package/node_modules/@pome-sh/twin-linear/dist/src/check-comments.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/check-issues.js +31 -4
- package/node_modules/@pome-sh/twin-linear/dist/src/check-issues.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/dist/src/check-state.d.ts +27 -2
- package/node_modules/@pome-sh/twin-linear/dist/src/check-state.js +73 -20
- package/node_modules/@pome-sh/twin-linear/dist/src/check-state.js.map +1 -1
- package/node_modules/@pome-sh/twin-linear/package.json +5 -5
- package/node_modules/@pome-sh/twin-slack/dist/src/check-messages.js +28 -7
- package/node_modules/@pome-sh/twin-slack/dist/src/check-messages.js.map +1 -1
- package/node_modules/@pome-sh/twin-slack/dist/src/check-secrets.js +16 -3
- package/node_modules/@pome-sh/twin-slack/dist/src/check-secrets.js.map +1 -1
- package/node_modules/@pome-sh/twin-slack/dist/src/check-state.d.ts +26 -1
- package/node_modules/@pome-sh/twin-slack/dist/src/check-state.js +31 -4
- package/node_modules/@pome-sh/twin-slack/dist/src/check-state.js.map +1 -1
- package/node_modules/@pome-sh/twin-slack/package.json +6 -6
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.d.ts +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js +16 -0
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js +11 -3
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.d.ts +25 -1
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js +31 -3
- package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js.map +1 -1
- package/node_modules/@pome-sh/twin-stripe/package.json +6 -6
- package/node_modules/hono/dist/cjs/context.js +28 -13
- package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
- package/node_modules/hono/dist/cjs/hono-base.js +3 -2
- package/node_modules/hono/dist/cjs/jsx/base.js +27 -23
- package/node_modules/hono/dist/cjs/jsx/hooks/index.js +16 -13
- package/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
- package/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
- package/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -2
- package/node_modules/hono/dist/cjs/middleware/etag/index.js +1 -1
- package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
- package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
- package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
- package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
- package/node_modules/hono/dist/cjs/request.js +6 -8
- package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +55 -56
- package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +64 -85
- package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +13 -5
- package/node_modules/hono/dist/cjs/router.js +1 -1
- package/node_modules/hono/dist/cjs/utils/cookie.js +3 -2
- package/node_modules/hono/dist/cjs/utils/url.js +7 -7
- package/node_modules/hono/dist/context.js +28 -13
- package/node_modules/hono/dist/helper/proxy/index.js +4 -0
- package/node_modules/hono/dist/hono-base.js +3 -2
- package/node_modules/hono/dist/jsx/base.js +27 -23
- package/node_modules/hono/dist/jsx/hooks/index.js +16 -13
- package/node_modules/hono/dist/middleware/cache/index.js +103 -8
- package/node_modules/hono/dist/middleware/compress/index.js +5 -0
- package/node_modules/hono/dist/middleware/cors/index.js +2 -2
- package/node_modules/hono/dist/middleware/etag/index.js +1 -1
- package/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
- package/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
- package/node_modules/hono/dist/middleware/language/language.js +10 -6
- package/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
- package/node_modules/hono/dist/request.js +7 -9
- package/node_modules/hono/dist/router/reg-exp-router/node.js +55 -56
- package/node_modules/hono/dist/router/reg-exp-router/router.js +64 -85
- package/node_modules/hono/dist/router/reg-exp-router/trie.js +13 -5
- package/node_modules/hono/dist/router.js +1 -1
- package/node_modules/hono/dist/types/client/types.d.ts +1 -1
- package/node_modules/hono/dist/types/hono-base.d.ts +4 -3
- package/node_modules/hono/dist/types/jsx/base.d.ts +4 -2
- package/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
- package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
- package/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
- package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
- package/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
- package/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
- package/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
- package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
- package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
- package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
- package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +1 -1
- package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
- package/node_modules/hono/dist/types/router.d.ts +1 -1
- package/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
- package/node_modules/hono/dist/types/utils/url.d.ts +1 -0
- package/node_modules/hono/dist/utils/cookie.js +4 -3
- package/node_modules/hono/dist/utils/url.js +5 -6
- package/node_modules/hono/package.json +10 -3
- package/package.json +21 -21
package/dist/build-info.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"package": "pome-sh",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"git_sha": "
|
|
5
|
-
"build_time": "2026-08-
|
|
3
|
+
"version": "0.20.0",
|
|
4
|
+
"git_sha": "4f64f517734b8b43ebd4bb8968a388e6b089dc62",
|
|
5
|
+
"build_time": "2026-08-04T21:23:39.046Z"
|
|
6
6
|
}
|
|
@@ -127,7 +127,7 @@ function handleConnect({ req, clientSocket, head, writer, egressWriter, allowHos
|
|
|
127
127
|
const row = {
|
|
128
128
|
ts: new Date().toISOString(),
|
|
129
129
|
event_id: randomUUID(),
|
|
130
|
-
|
|
130
|
+
parent_event_id: null,
|
|
131
131
|
kind: "LlmCallEvent",
|
|
132
132
|
host,
|
|
133
133
|
port,
|
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
import { type Event } from "../types/shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* F-1200 — give every twin HTTP row the tool call that caused it.
|
|
4
|
+
*
|
|
5
|
+
* The twin writes `parent_event_id: null` because it runs in its own process
|
|
6
|
+
* and cannot know the agent-side `event_id`; the adapter knows the `event_id`
|
|
7
|
+
* but never sees the twin's tape. This merge is the one place both halves are
|
|
8
|
+
* in hand, so the join belongs here — and it is a pure data operation over two
|
|
9
|
+
* finished files, with no dependency on the order the two writers ran in.
|
|
10
|
+
*
|
|
11
|
+
* The join key is the SDK's `tool_use_id`, which since F-1200 is what the
|
|
12
|
+
* adapter stamps on `x-pome-correlation-id`. The twin persists that header as
|
|
13
|
+
* `correlation_id` ALWAYS and as `tool_call_id` only when it pins
|
|
14
|
+
* `stampToolCallId` (github's frozen tape shape) — so `correlation_id` is the
|
|
15
|
+
* key that works for every twin, with `tool_call_id` preferred when present
|
|
16
|
+
* because it is unambiguously the header rather than the request-id fallback.
|
|
17
|
+
*
|
|
18
|
+
* A row keeps its null parent when the id names no tool call: a pre-F-1200
|
|
19
|
+
* `tlc_…`, a `req_…` from the no-header fallback, or a direct REST call made
|
|
20
|
+
* outside any tool handler. An unresolvable parent is the honest answer there —
|
|
21
|
+
* inventing one would put twin calls under tools that did not make them.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveTwinHttpParents(rows: Event[]): Event[];
|
|
1
24
|
export declare function mergeAdapterSignalsIntoEvents(signalsPath: string, eventsJsonlPath: string): Promise<{
|
|
2
25
|
appended: number;
|
|
3
26
|
dropped: number;
|
|
@@ -24,6 +24,48 @@ import { redactEvent } from "../recorder/redaction.js";
|
|
|
24
24
|
// lines are dropped and counted; the caller can log the drop count. Existing
|
|
25
25
|
// events.jsonl rows that fail to parse are passed through unsorted at the
|
|
26
26
|
// head of the file so a corrupted in-flight write is never silently dropped.
|
|
27
|
+
/**
|
|
28
|
+
* F-1200 — give every twin HTTP row the tool call that caused it.
|
|
29
|
+
*
|
|
30
|
+
* The twin writes `parent_event_id: null` because it runs in its own process
|
|
31
|
+
* and cannot know the agent-side `event_id`; the adapter knows the `event_id`
|
|
32
|
+
* but never sees the twin's tape. This merge is the one place both halves are
|
|
33
|
+
* in hand, so the join belongs here — and it is a pure data operation over two
|
|
34
|
+
* finished files, with no dependency on the order the two writers ran in.
|
|
35
|
+
*
|
|
36
|
+
* The join key is the SDK's `tool_use_id`, which since F-1200 is what the
|
|
37
|
+
* adapter stamps on `x-pome-correlation-id`. The twin persists that header as
|
|
38
|
+
* `correlation_id` ALWAYS and as `tool_call_id` only when it pins
|
|
39
|
+
* `stampToolCallId` (github's frozen tape shape) — so `correlation_id` is the
|
|
40
|
+
* key that works for every twin, with `tool_call_id` preferred when present
|
|
41
|
+
* because it is unambiguously the header rather than the request-id fallback.
|
|
42
|
+
*
|
|
43
|
+
* A row keeps its null parent when the id names no tool call: a pre-F-1200
|
|
44
|
+
* `tlc_…`, a `req_…` from the no-header fallback, or a direct REST call made
|
|
45
|
+
* outside any tool handler. An unresolvable parent is the honest answer there —
|
|
46
|
+
* inventing one would put twin calls under tools that did not make them.
|
|
47
|
+
*/
|
|
48
|
+
export function resolveTwinHttpParents(rows) {
|
|
49
|
+
const eventIdByToolUseId = new Map();
|
|
50
|
+
for (const row of rows) {
|
|
51
|
+
if (row.kind === "ToolUseEvent")
|
|
52
|
+
eventIdByToolUseId.set(row.tool_use_id, row.event_id);
|
|
53
|
+
}
|
|
54
|
+
if (eventIdByToolUseId.size === 0)
|
|
55
|
+
return rows;
|
|
56
|
+
return rows.map((row) => {
|
|
57
|
+
if (row.kind !== "TwinHttpEvent")
|
|
58
|
+
return row;
|
|
59
|
+
// Never overwrite a parent the writer already established.
|
|
60
|
+
if (row.parent_event_id != null)
|
|
61
|
+
return row;
|
|
62
|
+
const causingToolUseId = row.tool_call_id ?? row.correlation_id ?? null;
|
|
63
|
+
if (causingToolUseId === null)
|
|
64
|
+
return row;
|
|
65
|
+
const parentEventId = eventIdByToolUseId.get(causingToolUseId);
|
|
66
|
+
return parentEventId === undefined ? row : { ...row, parent_event_id: parentEventId };
|
|
67
|
+
});
|
|
68
|
+
}
|
|
27
69
|
export async function mergeAdapterSignalsIntoEvents(signalsPath, eventsJsonlPath) {
|
|
28
70
|
let rawSignals;
|
|
29
71
|
try {
|
|
@@ -84,9 +126,9 @@ export async function mergeAdapterSignalsIntoEvents(signalsPath, eventsJsonlPath
|
|
|
84
126
|
unparseablePassthrough.push(line);
|
|
85
127
|
}
|
|
86
128
|
}
|
|
87
|
-
// Concat
|
|
88
|
-
// lexicographic compare.
|
|
89
|
-
const merged = eventRows.concat(signalRows);
|
|
129
|
+
// Concat, resolve twin-HTTP parentage, then stable sort by ts. ISO-8601 with
|
|
130
|
+
// `Z` sorts chronologically under lexicographic compare.
|
|
131
|
+
const merged = resolveTwinHttpParents(eventRows.concat(signalRows));
|
|
90
132
|
merged.sort((a, b) => (a.ts < b.ts ? -1 : a.ts > b.ts ? 1 : 0));
|
|
91
133
|
const sortedJsonl = merged.map((r) => JSON.stringify(r)).join("\n");
|
|
92
134
|
const head = unparseablePassthrough.length > 0 ? unparseablePassthrough.join("\n") + "\n" : "";
|
|
@@ -116,7 +116,7 @@ function emitToolUse(signalsPath, row) {
|
|
|
116
116
|
appendFileSync(signalsPath, JSON.stringify({
|
|
117
117
|
ts: new Date().toISOString(),
|
|
118
118
|
event_id: randomUUID(),
|
|
119
|
-
|
|
119
|
+
parent_event_id: null,
|
|
120
120
|
kind: "ToolUseEvent",
|
|
121
121
|
tool_use_id: row.tool_use_id,
|
|
122
122
|
tool_name: row.tool_name,
|
|
@@ -134,7 +134,7 @@ function emitToolResult(signalsPath, row) {
|
|
|
134
134
|
appendFileSync(signalsPath, JSON.stringify({
|
|
135
135
|
ts: new Date().toISOString(),
|
|
136
136
|
event_id: randomUUID(),
|
|
137
|
-
|
|
137
|
+
parent_event_id: null,
|
|
138
138
|
kind: "ToolResultEvent",
|
|
139
139
|
tool_use_id: row.tool_use_id,
|
|
140
140
|
output: row.output,
|
|
@@ -163,7 +163,7 @@ function emitLlmCall(signalsPath, row) {
|
|
|
163
163
|
appendFileSync(signalsPath, JSON.stringify({
|
|
164
164
|
ts: new Date().toISOString(),
|
|
165
165
|
event_id: randomUUID(),
|
|
166
|
-
|
|
166
|
+
parent_event_id: null,
|
|
167
167
|
kind: "LlmCallEvent",
|
|
168
168
|
host: row.host,
|
|
169
169
|
port: 443,
|
|
@@ -86,7 +86,7 @@ app.get(
|
|
|
86
86
|
}))
|
|
87
87
|
)
|
|
88
88
|
|
|
89
|
-
const wss = new WebSocketServer({ noServer: true })
|
|
89
|
+
const wss = new WebSocketServer({ noServer: true })
|
|
90
90
|
serve({
|
|
91
91
|
fetch: app.fetch,
|
|
92
92
|
websocket: { server: wss },
|
|
@@ -332,6 +332,52 @@ type Http2Bindings = {
|
|
|
332
332
|
}
|
|
333
333
|
```
|
|
334
334
|
|
|
335
|
+
## Early Hints Middleware
|
|
336
|
+
|
|
337
|
+
You can send HTTP 103 Early Hints to instruct browsers to preload or preconnect resources before the final response is prepared. The middleware is supported under Node.js bindings (HTTP/1.1 and HTTP/2).
|
|
338
|
+
|
|
339
|
+
### Usage
|
|
340
|
+
|
|
341
|
+
Import `earlyHints` from `@hono/node-server/early-hints`:
|
|
342
|
+
|
|
343
|
+
#### Static links
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import { serve } from '@hono/node-server'
|
|
347
|
+
import { earlyHints } from '@hono/node-server/early-hints'
|
|
348
|
+
import { Hono } from 'hono'
|
|
349
|
+
|
|
350
|
+
const app = new Hono()
|
|
351
|
+
|
|
352
|
+
app.use(
|
|
353
|
+
earlyHints({
|
|
354
|
+
link: '</styles.css>; rel=preload; as=style',
|
|
355
|
+
})
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
app.get('/', (c) => {
|
|
359
|
+
return c.html('<!DOCTYPE html><html><body><h1>Hello Hono!</h1></body></html>')
|
|
360
|
+
})
|
|
361
|
+
|
|
362
|
+
serve(app)
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### Dynamic links
|
|
366
|
+
|
|
367
|
+
```ts
|
|
368
|
+
app.use(
|
|
369
|
+
earlyHints({
|
|
370
|
+
link: (c) =>
|
|
371
|
+
c.req.query('theme') === 'dark'
|
|
372
|
+
? '</dark.css>; rel=preload; as=style'
|
|
373
|
+
: '</light.css>; rel=preload; as=style',
|
|
374
|
+
})
|
|
375
|
+
)
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
> [!NOTE]
|
|
379
|
+
> Early Hints are sent only for requests that look like document navigations. If `Sec-Fetch-Mode` or `Sec-Fetch-Dest` is present with a value other than `navigate` or `document`, for example a `fetch()` or XHR call from a browser, a subresource request, or an iframe navigation, the middleware skips the hints and continues to the handler. Requests without these headers, such as `curl` or `fetch()` from a JavaScript runtime, are treated as navigations and do receive Early Hints.
|
|
380
|
+
|
|
335
381
|
## Direct response from Node.js API
|
|
336
382
|
|
|
337
383
|
You can directly respond to the client from the Node.js API.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
//#region src/early-hints.ts
|
|
4
|
+
/**
|
|
5
|
+
* Early Hints middleware for Node.js
|
|
6
|
+
* Automatically sends a 103 Early Hints informational response with the specified Link header(s).
|
|
7
|
+
*
|
|
8
|
+
* @param options EarlyHintsOptions
|
|
9
|
+
* @returns MiddlewareHandler
|
|
10
|
+
*/
|
|
11
|
+
const earlyHints = (options) => {
|
|
12
|
+
let warned = false;
|
|
13
|
+
return async (c, next) => {
|
|
14
|
+
const mode = c.req.header("Sec-Fetch-Mode");
|
|
15
|
+
const dest = c.req.header("Sec-Fetch-Dest");
|
|
16
|
+
if (mode && mode !== "navigate" || dest && dest !== "document") return next();
|
|
17
|
+
const env = c.env || {};
|
|
18
|
+
const outgoing = (env.server ? env.server : env)?.outgoing;
|
|
19
|
+
if (typeof outgoing?.writeEarlyHints !== "function") {
|
|
20
|
+
if (!warned) {
|
|
21
|
+
console.warn("Early Hints Middleware is not supported because writeEarlyHints is not defined.");
|
|
22
|
+
warned = true;
|
|
23
|
+
}
|
|
24
|
+
return await next();
|
|
25
|
+
}
|
|
26
|
+
if (!outgoing.headersSent) {
|
|
27
|
+
const link = typeof options.link === "function" ? options.link(c) : options.link;
|
|
28
|
+
if (link !== void 0 && (Array.isArray(link) ? link.length > 0 : Boolean(link))) outgoing.writeEarlyHints({ link });
|
|
29
|
+
}
|
|
30
|
+
await next();
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
//#endregion
|
|
35
|
+
exports.earlyHints = earlyHints;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Context, Env, MiddlewareHandler } from "hono";
|
|
2
|
+
|
|
3
|
+
//#region src/early-hints.d.ts
|
|
4
|
+
type EarlyHintsOptions<E extends Env = Env> = {
|
|
5
|
+
link: string | string[] | ((c: Context<E>) => string | string[] | undefined);
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Early Hints middleware for Node.js
|
|
9
|
+
* Automatically sends a 103 Early Hints informational response with the specified Link header(s).
|
|
10
|
+
*
|
|
11
|
+
* @param options EarlyHintsOptions
|
|
12
|
+
* @returns MiddlewareHandler
|
|
13
|
+
*/
|
|
14
|
+
declare const earlyHints: <E extends Env = any>(options: EarlyHintsOptions<E>) => MiddlewareHandler<E>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { EarlyHintsOptions, earlyHints };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Context, Env, MiddlewareHandler } from "hono";
|
|
2
|
+
|
|
3
|
+
//#region src/early-hints.d.ts
|
|
4
|
+
type EarlyHintsOptions<E extends Env = Env> = {
|
|
5
|
+
link: string | string[] | ((c: Context<E>) => string | string[] | undefined);
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Early Hints middleware for Node.js
|
|
9
|
+
* Automatically sends a 103 Early Hints informational response with the specified Link header(s).
|
|
10
|
+
*
|
|
11
|
+
* @param options EarlyHintsOptions
|
|
12
|
+
* @returns MiddlewareHandler
|
|
13
|
+
*/
|
|
14
|
+
declare const earlyHints: <E extends Env = any>(options: EarlyHintsOptions<E>) => MiddlewareHandler<E>;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { EarlyHintsOptions, earlyHints };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/early-hints.ts
|
|
2
|
+
/**
|
|
3
|
+
* Early Hints middleware for Node.js
|
|
4
|
+
* Automatically sends a 103 Early Hints informational response with the specified Link header(s).
|
|
5
|
+
*
|
|
6
|
+
* @param options EarlyHintsOptions
|
|
7
|
+
* @returns MiddlewareHandler
|
|
8
|
+
*/
|
|
9
|
+
const earlyHints = (options) => {
|
|
10
|
+
let warned = false;
|
|
11
|
+
return async (c, next) => {
|
|
12
|
+
const mode = c.req.header("Sec-Fetch-Mode");
|
|
13
|
+
const dest = c.req.header("Sec-Fetch-Dest");
|
|
14
|
+
if (mode && mode !== "navigate" || dest && dest !== "document") return next();
|
|
15
|
+
const env = c.env || {};
|
|
16
|
+
const outgoing = (env.server ? env.server : env)?.outgoing;
|
|
17
|
+
if (typeof outgoing?.writeEarlyHints !== "function") {
|
|
18
|
+
if (!warned) {
|
|
19
|
+
console.warn("Early Hints Middleware is not supported because writeEarlyHints is not defined.");
|
|
20
|
+
warned = true;
|
|
21
|
+
}
|
|
22
|
+
return await next();
|
|
23
|
+
}
|
|
24
|
+
if (!outgoing.headersSent) {
|
|
25
|
+
const link = typeof options.link === "function" ? options.link(c) : options.link;
|
|
26
|
+
if (link !== void 0 && (Array.isArray(link) ? link.length > 0 : Boolean(link))) outgoing.writeEarlyHints({ link });
|
|
27
|
+
}
|
|
28
|
+
await next();
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { earlyHints };
|
|
@@ -193,8 +193,10 @@ const normalizeIncomingMethod = (method) => {
|
|
|
193
193
|
case "GET":
|
|
194
194
|
case "HEAD":
|
|
195
195
|
case "OPTIONS":
|
|
196
|
+
case "PATCH":
|
|
196
197
|
case "POST":
|
|
197
|
-
case "PUT":
|
|
198
|
+
case "PUT":
|
|
199
|
+
case "QUERY": return method;
|
|
198
200
|
}
|
|
199
201
|
const upper = method.toUpperCase();
|
|
200
202
|
switch (upper) {
|
|
@@ -492,16 +494,15 @@ var Response$1 = class Response$1 {
|
|
|
492
494
|
constructor(body, init) {
|
|
493
495
|
let headers;
|
|
494
496
|
this.#body = body;
|
|
495
|
-
if (init instanceof
|
|
497
|
+
if (init instanceof GlobalResponse) {
|
|
496
498
|
const cachedGlobalResponse = init[responseCache];
|
|
497
499
|
if (cachedGlobalResponse) {
|
|
498
500
|
this.#init = cachedGlobalResponse;
|
|
499
501
|
this[getResponseCache]();
|
|
500
502
|
return;
|
|
501
|
-
} else {
|
|
502
|
-
this.#init = init.#init;
|
|
503
|
-
headers = new Headers(init.headers);
|
|
504
503
|
}
|
|
504
|
+
this.#init = init instanceof Response$1 ? init.#init : init;
|
|
505
|
+
headers = new Headers(init.headers);
|
|
505
506
|
} else this.#init = init;
|
|
506
507
|
if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
|
|
507
508
|
init?.status || 200,
|
|
@@ -683,7 +684,10 @@ const drainIncoming = (incoming) => {
|
|
|
683
684
|
const forceClose = () => {
|
|
684
685
|
cleanup();
|
|
685
686
|
const socket = incoming.socket;
|
|
686
|
-
if (socket && !socket.destroyed)
|
|
687
|
+
if (socket && !socket.destroyed) {
|
|
688
|
+
if (typeof socket.destroySoon === "function") socket.destroySoon();
|
|
689
|
+
else if (typeof socket.destroy === "function") socket.destroy();
|
|
690
|
+
}
|
|
687
691
|
};
|
|
688
692
|
const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
|
|
689
693
|
timer.unref?.();
|
|
@@ -192,8 +192,10 @@ const normalizeIncomingMethod = (method) => {
|
|
|
192
192
|
case "GET":
|
|
193
193
|
case "HEAD":
|
|
194
194
|
case "OPTIONS":
|
|
195
|
+
case "PATCH":
|
|
195
196
|
case "POST":
|
|
196
|
-
case "PUT":
|
|
197
|
+
case "PUT":
|
|
198
|
+
case "QUERY": return method;
|
|
197
199
|
}
|
|
198
200
|
const upper = method.toUpperCase();
|
|
199
201
|
switch (upper) {
|
|
@@ -491,16 +493,15 @@ var Response$1 = class Response$1 {
|
|
|
491
493
|
constructor(body, init) {
|
|
492
494
|
let headers;
|
|
493
495
|
this.#body = body;
|
|
494
|
-
if (init instanceof
|
|
496
|
+
if (init instanceof GlobalResponse) {
|
|
495
497
|
const cachedGlobalResponse = init[responseCache];
|
|
496
498
|
if (cachedGlobalResponse) {
|
|
497
499
|
this.#init = cachedGlobalResponse;
|
|
498
500
|
this[getResponseCache]();
|
|
499
501
|
return;
|
|
500
|
-
} else {
|
|
501
|
-
this.#init = init.#init;
|
|
502
|
-
headers = new Headers(init.headers);
|
|
503
502
|
}
|
|
503
|
+
this.#init = init instanceof Response$1 ? init.#init : init;
|
|
504
|
+
headers = new Headers(init.headers);
|
|
504
505
|
} else this.#init = init;
|
|
505
506
|
if (body == null || typeof body === "string" || typeof body?.getReader !== "undefined" || body instanceof Blob || body instanceof Uint8Array) this[cacheKey] = [
|
|
506
507
|
init?.status || 200,
|
|
@@ -682,7 +683,10 @@ const drainIncoming = (incoming) => {
|
|
|
682
683
|
const forceClose = () => {
|
|
683
684
|
cleanup();
|
|
684
685
|
const socket = incoming.socket;
|
|
685
|
-
if (socket && !socket.destroyed)
|
|
686
|
+
if (socket && !socket.destroyed) {
|
|
687
|
+
if (typeof socket.destroySoon === "function") socket.destroySoon();
|
|
688
|
+
else if (typeof socket.destroy === "function") socket.destroy();
|
|
689
|
+
}
|
|
686
690
|
};
|
|
687
691
|
const timer = setTimeout(forceClose, DRAIN_TIMEOUT_MS);
|
|
688
692
|
timer.unref?.();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hono/node-server",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Node.js Adapter for Hono",
|
|
5
5
|
"main": "dist/index.mjs",
|
|
6
6
|
"type": "module",
|
|
@@ -48,6 +48,16 @@
|
|
|
48
48
|
"types": "./dist/conninfo.d.cts",
|
|
49
49
|
"default": "./dist/conninfo.cjs"
|
|
50
50
|
}
|
|
51
|
+
},
|
|
52
|
+
"./early-hints": {
|
|
53
|
+
"import": {
|
|
54
|
+
"types": "./dist/early-hints.d.mts",
|
|
55
|
+
"default": "./dist/early-hints.mjs"
|
|
56
|
+
},
|
|
57
|
+
"require": {
|
|
58
|
+
"types": "./dist/early-hints.d.cts",
|
|
59
|
+
"default": "./dist/early-hints.cjs"
|
|
60
|
+
}
|
|
51
61
|
}
|
|
52
62
|
},
|
|
53
63
|
"typesVersions": {
|
|
@@ -63,6 +73,9 @@
|
|
|
63
73
|
],
|
|
64
74
|
"conninfo": [
|
|
65
75
|
"./dist/conninfo.d.mts"
|
|
76
|
+
],
|
|
77
|
+
"early-hints": [
|
|
78
|
+
"./dist/early-hints.d.mts"
|
|
66
79
|
]
|
|
67
80
|
}
|
|
68
81
|
},
|
|
@@ -84,7 +97,6 @@
|
|
|
84
97
|
"devDependencies": {
|
|
85
98
|
"@hono/eslint-config": "^1.0.1",
|
|
86
99
|
"@types/node": "^20.10.0",
|
|
87
|
-
"@types/supertest": "^2.0.12",
|
|
88
100
|
"@types/ws": "^8.18.1",
|
|
89
101
|
"@whatwg-node/fetch": "^0.9.14",
|
|
90
102
|
"eslint": "^9.10.0",
|
|
@@ -92,7 +104,6 @@
|
|
|
92
104
|
"np": "^11.2.1",
|
|
93
105
|
"prettier": "^3.2.4",
|
|
94
106
|
"publint": "^0.3.18",
|
|
95
|
-
"supertest": "^7.2.2",
|
|
96
107
|
"tsdown": "^0.20.3",
|
|
97
108
|
"typescript": "^5.3.2",
|
|
98
109
|
"vitest": "^4.0.18",
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { CheckDefinition } from "./checks.js";
|
|
2
|
+
/**
|
|
3
|
+
* A pointer from the ROOT of the exported tree.
|
|
4
|
+
*
|
|
5
|
+
* Numbers are array indices and strings are object keys, but this function does
|
|
6
|
+
* not enforce that — the tree decides, at resolution time. Passing no segments
|
|
7
|
+
* returns `""`, which RFC 6901 defines as the whole document; a check with
|
|
8
|
+
* nothing narrower to name must OMIT `evidenceStatePaths` rather than cite the
|
|
9
|
+
* root, for the same reason it must omit rather than send `[]`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function statePath(...segments: readonly (string | number)[]): string;
|
|
12
|
+
/**
|
|
13
|
+
* A pointer relative to one a resolver already built.
|
|
14
|
+
*
|
|
15
|
+
* The twins' `resolve*` helpers walk the tree to find an entity and now hand
|
|
16
|
+
* back the pointer they walked; a check appends the field it went on to read.
|
|
17
|
+
* Written as its own function rather than string concatenation at 37 call sites
|
|
18
|
+
* because the escaping has to happen on the appended segments and NOT on the
|
|
19
|
+
* base, which already contains real `/` separators — concatenating by hand is
|
|
20
|
+
* how a `~1` ends up double-encoded.
|
|
21
|
+
*/
|
|
22
|
+
export declare function childStatePath(base: string, ...segments: readonly (string | number)[]): string;
|
|
23
|
+
/**
|
|
24
|
+
* What a pointer addresses in a tree, or `null` when it addresses nothing.
|
|
25
|
+
*
|
|
26
|
+
* `null` is a NORMAL answer and every caller must handle it: the state blob a
|
|
27
|
+
* report renders can be a different export from the one the check read (a
|
|
28
|
+
* re-run, a truncated upload, a snapshot predating a field). A consumer turns
|
|
29
|
+
* `null` into "no affordance", exactly as `findEventIndexForEvidence` turns an
|
|
30
|
+
* unresolvable event id into one.
|
|
31
|
+
*
|
|
32
|
+
* Wrapped in `{ value }` rather than returned bare because `undefined` and
|
|
33
|
+
* `null` are both legal JSON-ish values a tree can hold at a pointer, and a bare
|
|
34
|
+
* return could not tell "the path is absent" from "the path holds null" — the
|
|
35
|
+
* difference between no evidence and evidence that the field is empty.
|
|
36
|
+
*/
|
|
37
|
+
export declare function resolveStatePath(tree: unknown, pointer: string): {
|
|
38
|
+
value: unknown;
|
|
39
|
+
} | null;
|
|
40
|
+
export type StateCitationArm = "passing" | "failing";
|
|
41
|
+
export type StateCitationVerdict = {
|
|
42
|
+
kind: "cites";
|
|
43
|
+
} | {
|
|
44
|
+
kind: "declined";
|
|
45
|
+
} | {
|
|
46
|
+
kind: "uncited";
|
|
47
|
+
arm: StateCitationArm;
|
|
48
|
+
} | {
|
|
49
|
+
kind: "unresolvable";
|
|
50
|
+
arm: StateCitationArm;
|
|
51
|
+
pointer: string;
|
|
52
|
+
} | {
|
|
53
|
+
kind: "malformed";
|
|
54
|
+
arm: StateCitationArm;
|
|
55
|
+
detail: string;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Does this state-reading check say WHERE it looked, in a form that resolves?
|
|
59
|
+
*
|
|
60
|
+
* Probes BOTH arms, and that is deliberate. A citation present on the passing
|
|
61
|
+
* world and absent on the failing one is worse than no citation at all: its
|
|
62
|
+
* absence starts reading as a verdict class — "no evidence" would come to mean
|
|
63
|
+
* "this one failed" — and the reader has no way to know that is an accident of
|
|
64
|
+
* how the predicate was written. So a check must be able to say where it looked
|
|
65
|
+
* whether or not it liked what it found there.
|
|
66
|
+
*
|
|
67
|
+
* Only meaningful for `final` / `seed+final` checks. A `tape` check cites
|
|
68
|
+
* `evidenceEventIds` instead and is not this gate's business; callers filter on
|
|
69
|
+
* `substrate` before probing.
|
|
70
|
+
*/
|
|
71
|
+
export declare function probeStateCitation<TState, TArgs extends Record<string, string>>(def: CheckDefinition<TState, TArgs>, args: TArgs): StateCitationVerdict;
|