@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.
Files changed (151) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/src/capture-server/index.js +1 -1
  3. package/dist/src/runner/mergeAdapterSignals.d.ts +23 -0
  4. package/dist/src/runner/mergeAdapterSignals.js +45 -3
  5. package/dist/src/scaffolds/mcp-loop/loop.js +3 -3
  6. package/node_modules/@hono/node-server/README.md +47 -1
  7. package/node_modules/@hono/node-server/dist/early-hints.cjs +35 -0
  8. package/node_modules/@hono/node-server/dist/early-hints.d.cts +16 -0
  9. package/node_modules/@hono/node-server/dist/early-hints.d.mts +16 -0
  10. package/node_modules/@hono/node-server/dist/early-hints.mjs +33 -0
  11. package/node_modules/@hono/node-server/dist/index.cjs +10 -6
  12. package/node_modules/@hono/node-server/dist/index.mjs +10 -6
  13. package/node_modules/@hono/node-server/package.json +14 -3
  14. package/node_modules/@pome-sh/sdk/dist/check-state-path.d.ts +71 -0
  15. package/node_modules/@pome-sh/sdk/dist/check-state-path.js +200 -0
  16. package/node_modules/@pome-sh/sdk/dist/check-state-path.js.map +1 -0
  17. package/node_modules/@pome-sh/sdk/dist/checks.d.ts +2 -0
  18. package/node_modules/@pome-sh/sdk/dist/checks.js +5 -0
  19. package/node_modules/@pome-sh/sdk/dist/checks.js.map +1 -1
  20. package/node_modules/@pome-sh/sdk/dist/recorder.d.ts +10 -1
  21. package/node_modules/@pome-sh/sdk/dist/recorder.js +11 -2
  22. package/node_modules/@pome-sh/sdk/dist/recorder.js.map +1 -1
  23. package/node_modules/@pome-sh/sdk/package.json +4 -4
  24. package/node_modules/@pome-sh/shared-types/dist/otel/event-schema.d.ts +111 -24
  25. package/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.js +3 -3
  26. package/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.js.map +1 -1
  27. package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.d.ts +6 -3
  28. package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js +7 -2
  29. package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js.map +1 -1
  30. package/node_modules/@pome-sh/shared-types/dist/otel/map-span.js +1 -1
  31. package/node_modules/@pome-sh/shared-types/dist/otel/map-span.js.map +1 -1
  32. package/node_modules/@pome-sh/shared-types/dist/otel/span-event.d.ts +68 -4
  33. package/node_modules/@pome-sh/shared-types/dist/otel/span-event.js +19 -5
  34. package/node_modules/@pome-sh/shared-types/dist/otel/span-event.js.map +1 -1
  35. package/node_modules/@pome-sh/shared-types/dist/recorder-events.d.ts +60 -28
  36. package/node_modules/@pome-sh/shared-types/dist/recorder-events.js +81 -10
  37. package/node_modules/@pome-sh/shared-types/dist/recorder-events.js.map +1 -1
  38. package/node_modules/@pome-sh/shared-types/package.json +1 -1
  39. package/node_modules/@pome-sh/shared-types/trace-contract.json +49 -1
  40. package/node_modules/@pome-sh/twin-github/dist/src/check-issues.js +35 -9
  41. package/node_modules/@pome-sh/twin-github/dist/src/check-issues.js.map +1 -1
  42. package/node_modules/@pome-sh/twin-github/dist/src/check-pulls.js +21 -5
  43. package/node_modules/@pome-sh/twin-github/dist/src/check-pulls.js.map +1 -1
  44. package/node_modules/@pome-sh/twin-github/dist/src/check-repos.d.ts +3 -0
  45. package/node_modules/@pome-sh/twin-github/dist/src/check-repos.js +111 -6
  46. package/node_modules/@pome-sh/twin-github/dist/src/check-repos.js.map +1 -1
  47. package/node_modules/@pome-sh/twin-github/dist/src/check-state.d.ts +21 -1
  48. package/node_modules/@pome-sh/twin-github/dist/src/check-state.js +77 -10
  49. package/node_modules/@pome-sh/twin-github/dist/src/check-state.js.map +1 -1
  50. package/node_modules/@pome-sh/twin-github/dist/src/checks.d.ts +2 -0
  51. package/node_modules/@pome-sh/twin-github/dist/src/checks.js +5 -1
  52. package/node_modules/@pome-sh/twin-github/dist/src/checks.js.map +1 -1
  53. package/node_modules/@pome-sh/twin-github/package.json +6 -6
  54. package/node_modules/@pome-sh/twin-gmail/CHANGELOG.md +36 -0
  55. package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js +19 -4
  56. package/node_modules/@pome-sh/twin-gmail/dist/src/check-drafts.js.map +1 -1
  57. package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js +15 -4
  58. package/node_modules/@pome-sh/twin-gmail/dist/src/check-labels.js.map +1 -1
  59. package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js +19 -3
  60. package/node_modules/@pome-sh/twin-gmail/dist/src/check-messages.js.map +1 -1
  61. package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.d.ts +26 -1
  62. package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js +52 -16
  63. package/node_modules/@pome-sh/twin-gmail/dist/src/check-state.js.map +1 -1
  64. package/node_modules/@pome-sh/twin-gmail/package.json +5 -5
  65. package/node_modules/@pome-sh/twin-linear/CHANGELOG.md +36 -0
  66. package/node_modules/@pome-sh/twin-linear/dist/src/check-comments.js +15 -3
  67. package/node_modules/@pome-sh/twin-linear/dist/src/check-comments.js.map +1 -1
  68. package/node_modules/@pome-sh/twin-linear/dist/src/check-issues.js +31 -4
  69. package/node_modules/@pome-sh/twin-linear/dist/src/check-issues.js.map +1 -1
  70. package/node_modules/@pome-sh/twin-linear/dist/src/check-state.d.ts +27 -2
  71. package/node_modules/@pome-sh/twin-linear/dist/src/check-state.js +73 -20
  72. package/node_modules/@pome-sh/twin-linear/dist/src/check-state.js.map +1 -1
  73. package/node_modules/@pome-sh/twin-linear/package.json +5 -5
  74. package/node_modules/@pome-sh/twin-slack/dist/src/check-messages.js +28 -7
  75. package/node_modules/@pome-sh/twin-slack/dist/src/check-messages.js.map +1 -1
  76. package/node_modules/@pome-sh/twin-slack/dist/src/check-secrets.js +16 -3
  77. package/node_modules/@pome-sh/twin-slack/dist/src/check-secrets.js.map +1 -1
  78. package/node_modules/@pome-sh/twin-slack/dist/src/check-state.d.ts +26 -1
  79. package/node_modules/@pome-sh/twin-slack/dist/src/check-state.js +31 -4
  80. package/node_modules/@pome-sh/twin-slack/dist/src/check-state.js.map +1 -1
  81. package/node_modules/@pome-sh/twin-slack/package.json +6 -6
  82. package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.d.ts +1 -1
  83. package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js +16 -0
  84. package/node_modules/@pome-sh/twin-stripe/dist/src/check-payments.js.map +1 -1
  85. package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js +11 -3
  86. package/node_modules/@pome-sh/twin-stripe/dist/src/check-refunds.js.map +1 -1
  87. package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.d.ts +25 -1
  88. package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js +31 -3
  89. package/node_modules/@pome-sh/twin-stripe/dist/src/check-state.js.map +1 -1
  90. package/node_modules/@pome-sh/twin-stripe/package.json +6 -6
  91. package/node_modules/hono/dist/cjs/context.js +28 -13
  92. package/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
  93. package/node_modules/hono/dist/cjs/hono-base.js +3 -2
  94. package/node_modules/hono/dist/cjs/jsx/base.js +27 -23
  95. package/node_modules/hono/dist/cjs/jsx/hooks/index.js +16 -13
  96. package/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
  97. package/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
  98. package/node_modules/hono/dist/cjs/middleware/cors/index.js +2 -2
  99. package/node_modules/hono/dist/cjs/middleware/etag/index.js +1 -1
  100. package/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
  101. package/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
  102. package/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
  103. package/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
  104. package/node_modules/hono/dist/cjs/request.js +6 -8
  105. package/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +55 -56
  106. package/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +64 -85
  107. package/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +13 -5
  108. package/node_modules/hono/dist/cjs/router.js +1 -1
  109. package/node_modules/hono/dist/cjs/utils/cookie.js +3 -2
  110. package/node_modules/hono/dist/cjs/utils/url.js +7 -7
  111. package/node_modules/hono/dist/context.js +28 -13
  112. package/node_modules/hono/dist/helper/proxy/index.js +4 -0
  113. package/node_modules/hono/dist/hono-base.js +3 -2
  114. package/node_modules/hono/dist/jsx/base.js +27 -23
  115. package/node_modules/hono/dist/jsx/hooks/index.js +16 -13
  116. package/node_modules/hono/dist/middleware/cache/index.js +103 -8
  117. package/node_modules/hono/dist/middleware/compress/index.js +5 -0
  118. package/node_modules/hono/dist/middleware/cors/index.js +2 -2
  119. package/node_modules/hono/dist/middleware/etag/index.js +1 -1
  120. package/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
  121. package/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
  122. package/node_modules/hono/dist/middleware/language/language.js +10 -6
  123. package/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
  124. package/node_modules/hono/dist/request.js +7 -9
  125. package/node_modules/hono/dist/router/reg-exp-router/node.js +55 -56
  126. package/node_modules/hono/dist/router/reg-exp-router/router.js +64 -85
  127. package/node_modules/hono/dist/router/reg-exp-router/trie.js +13 -5
  128. package/node_modules/hono/dist/router.js +1 -1
  129. package/node_modules/hono/dist/types/client/types.d.ts +1 -1
  130. package/node_modules/hono/dist/types/hono-base.d.ts +4 -3
  131. package/node_modules/hono/dist/types/jsx/base.d.ts +4 -2
  132. package/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
  133. package/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
  134. package/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
  135. package/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
  136. package/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
  137. package/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
  138. package/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
  139. package/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
  140. package/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
  141. package/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
  142. package/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
  143. package/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +1 -1
  144. package/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
  145. package/node_modules/hono/dist/types/router.d.ts +1 -1
  146. package/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
  147. package/node_modules/hono/dist/types/utils/url.d.ts +1 -0
  148. package/node_modules/hono/dist/utils/cookie.js +4 -3
  149. package/node_modules/hono/dist/utils/url.js +5 -6
  150. package/node_modules/hono/package.json +10 -3
  151. package/package.json +21 -21
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "pome-sh",
3
- "version": "0.18.0",
4
- "git_sha": "285d48dcd654088387b40e8d36a7a2bf8ffbb599",
5
- "build_time": "2026-08-03T13:39:43.286Z"
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
- parent_id: null,
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 + stable sort by ts. ISO-8601 with `Z` sorts chronologically under
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
- parent_id: null,
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
- parent_id: null,
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
- parent_id: null,
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 }) // important to create with `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": return method;
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 Response$1) {
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) socket.destroySoon();
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": return method;
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 Response$1) {
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) socket.destroySoon();
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.10",
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;