@agentpulsehq/core 0.0.0-stage → 0.1.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 (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -2
  3. package/dist/adapters/express.d.ts +17 -0
  4. package/dist/adapters/express.d.ts.map +1 -0
  5. package/dist/adapters/express.js +265 -0
  6. package/dist/adapters/express.js.map +1 -0
  7. package/dist/adapters/hono.d.ts +10 -0
  8. package/dist/adapters/hono.d.ts.map +1 -0
  9. package/dist/adapters/hono.js +43 -0
  10. package/dist/adapters/hono.js.map +1 -0
  11. package/dist/adapters/next.d.ts +56 -0
  12. package/dist/adapters/next.d.ts.map +1 -0
  13. package/dist/adapters/next.js +187 -0
  14. package/dist/adapters/next.js.map +1 -0
  15. package/dist/converter.d.ts +79 -0
  16. package/dist/converter.d.ts.map +1 -0
  17. package/dist/converter.js +689 -0
  18. package/dist/converter.js.map +1 -0
  19. package/dist/engine.d.ts +54 -0
  20. package/dist/engine.d.ts.map +1 -0
  21. package/dist/engine.js +330 -0
  22. package/dist/engine.js.map +1 -0
  23. package/dist/index.d.ts +9 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +9 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/limits.d.ts +70 -0
  28. package/dist/limits.d.ts.map +1 -0
  29. package/dist/limits.js +117 -0
  30. package/dist/limits.js.map +1 -0
  31. package/dist/redact.d.ts +20 -0
  32. package/dist/redact.d.ts.map +1 -0
  33. package/dist/redact.js +86 -0
  34. package/dist/redact.js.map +1 -0
  35. package/dist/telemetry.d.ts +91 -0
  36. package/dist/telemetry.d.ts.map +1 -0
  37. package/dist/telemetry.js +261 -0
  38. package/dist/telemetry.js.map +1 -0
  39. package/dist/types.d.ts +97 -0
  40. package/dist/types.d.ts.map +1 -0
  41. package/dist/types.js +2 -0
  42. package/dist/types.js.map +1 -0
  43. package/package.json +79 -3
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 rolandtor19
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,96 @@
1
- # Temporary Holding Version
1
+ # @agentpulsehq/core
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Detect AI agents, serve them clean Markdown, declare [Content Signals](https://contentsignals.org) and
4
+ send telemetry to AgentPulse. Adapters for Hono, Express (also Connect and `node:http`) and Next.js 16.
5
+
6
+ ```bash
7
+ npm install @agentpulsehq/core
8
+ ```
9
+
10
+ Requires Node.js 22 or later. `next` is an optional peer dependency, needed only for `@agentpulsehq/core/next`.
11
+
12
+ ## Hono
13
+
14
+ ```ts
15
+ import { Hono } from "hono";
16
+ import { createIngestClient } from "@agentpulsehq/core";
17
+ import { agentPulse } from "@agentpulsehq/core/hono";
18
+
19
+ const telemetry = createIngestClient({ writeKey: process.env.AGENTPULSE_WRITE_KEY! });
20
+
21
+ const app = new Hono();
22
+ app.use("*", agentPulse({ contentSignals: { search: true, aiInput: true, aiTrain: false }, onEvent: telemetry.onEvent }));
23
+
24
+ export default app;
25
+ ```
26
+
27
+ On serverless platforms, flush pending events at the end of each request with `waitUntil(telemetry.flush())`.
28
+
29
+ ## Express
30
+
31
+ ```ts
32
+ import express from "express";
33
+ import { createIngestClient } from "@agentpulsehq/core";
34
+ import { agentPulse } from "@agentpulsehq/core/express";
35
+
36
+ const telemetry = createIngestClient({ writeKey: process.env.AGENTPULSE_WRITE_KEY! });
37
+
38
+ const app = express();
39
+ app.use(agentPulse({ onEvent: telemetry.onEvent }));
40
+
41
+ const server = app.listen(3000);
42
+ process.on("SIGTERM", () => server.close(() => void telemetry.shutdown()));
43
+ ```
44
+
45
+ ## Next.js 16
46
+
47
+ `proxy.ts` detects agents and rewrites their requests to a route handler that converts the page:
48
+
49
+ ```ts
50
+ // proxy.ts
51
+ import { createAgentPulseProxy } from "@agentpulsehq/core/next";
52
+ import { telemetry } from "./lib/agentpulse";
53
+
54
+ export const proxy = createAgentPulseProxy({ onEvent: telemetry.onEvent });
55
+
56
+ export const config = {
57
+ matcher: ["/((?!_next/static|_next/image|favicon.ico|agentpulse-markdown).*)"]
58
+ };
59
+ ```
60
+
61
+ ```ts
62
+ // app/agentpulse-markdown/[[...path]]/route.ts
63
+ import { createMarkdownRouteHandler } from "@agentpulsehq/core/next";
64
+ import { telemetry } from "../../../lib/agentpulse";
65
+
66
+ export const dynamic = "force-dynamic";
67
+
68
+ // internalOrigin: where this app is reachable from the server itself (never taken from the request).
69
+ export const GET = createMarkdownRouteHandler({
70
+ internalOrigin: process.env.AGENTPULSE_INTERNAL_ORIGIN!,
71
+ onEvent: telemetry.onEvent
72
+ });
73
+ ```
74
+
75
+ ## What it does
76
+
77
+ - **Markdown negotiation.** Requests that prefer `text/markdown` (RFC 9110 q-values) get the HTML page
78
+ converted to Markdown, with `x-markdown-tokens` / `x-original-tokens` estimates. Everyone else gets the
79
+ original HTML, with `Vary: Accept`.
80
+ - **Agent detection.** It matches the User-Agent against the catalog of documented agents in
81
+ [`@agentpulsehq/rules`](https://www.npmjs.com/package/@agentpulsehq/rules). With `AgentVerifier`, it
82
+ also checks the client IP against the ranges each operator publishes.
83
+ - **Content Signals.** Serve `contentSignalsRobotsTxt(signals)` from `/robots.txt`, which is where the
84
+ specification defines them. The `Content-Signal` response header is not standard, so it is opt-in
85
+ (`emitContentSignalHeader: true`).
86
+ - **Telemetry.** `IngestClient` batches events, retries with backoff and never throws into your app.
87
+ Paths are normalized and personal data is redacted before sending, and the client IP is never sent.
88
+ The write key belongs in a server environment variable: never ship it to a browser.
89
+
90
+ ## Without AgentPulse Cloud
91
+
92
+ `htmlToMarkdown()` and the adapters work without an account. Leave out `onEvent`, or pass your own callback.
93
+
94
+ ## License
95
+
96
+ MIT
@@ -0,0 +1,17 @@
1
+ import type { IncomingMessage, ServerResponse } from "node:http";
2
+ import type { AgentPulseOptions } from "../types.js";
3
+ /** Options for the Express adapter: `getClientIp` receives Node's `IncomingMessage` (Express's `req`). */
4
+ export type ExpressAgentPulseOptions = AgentPulseOptions<IncomingMessage>;
5
+ type NextFunction = (err?: unknown) => void;
6
+ /**
7
+ * AgentPulse middleware for Express (also Connect and plain `node:http`).
8
+ *
9
+ * Mount it **after** compression middleware (`app.use(compression())` first) so it sees the
10
+ * uncompressed HTML; compressed bodies are passed through untouched.
11
+ *
12
+ * For `getClientIp`, use the socket address (`req.socket.remoteAddress`) or, behind a trusted
13
+ * proxy, Express's `req.ip` with `app.set("trust proxy", …)` configured.
14
+ */
15
+ export declare function agentPulse(options?: ExpressAgentPulseOptions): (req: IncomingMessage, res: ServerResponse, next: NextFunction) => void;
16
+ export {};
17
+ //# sourceMappingURL=express.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAA2C,MAAM,WAAW,CAAC;AAE1G,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,0GAA0G;AAC1G,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,CAAC,eAAe,CAAC,CAAC;AAE1E,KAAK,YAAY,GAAG,CAAC,GAAG,CAAC,EAAE,OAAO,KAAK,IAAI,CAAC;AAM5C;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,wBAA6B,IAG1B,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,YAAY,KAAG,IAAI,CAW1G"}
@@ -0,0 +1,265 @@
1
+ import { createEngine, EMPTY_FIELDS } from "../engine.js";
2
+ /** Bytes buffered between the app's writes and the converter before `res.write` asks the app to wait. */
3
+ const HIGH_WATER_MARK = 64 * 1024;
4
+ /**
5
+ * AgentPulse middleware for Express (also Connect and plain `node:http`).
6
+ *
7
+ * Mount it **after** compression middleware (`app.use(compression())` first) so it sees the
8
+ * uncompressed HTML; compressed bodies are passed through untouched.
9
+ *
10
+ * For `getClientIp`, use the socket address (`req.socket.remoteAddress`) or, behind a trusted
11
+ * proxy, Express's `req.ip` with `app.set("trust proxy", …)` configured.
12
+ */
13
+ export function agentPulse(options = {}) {
14
+ const engine = createEngine(options);
15
+ return function agentPulseMiddleware(req, res, next) {
16
+ const state = engine.inspect({
17
+ userAgent: firstHeader(req.headers["user-agent"]),
18
+ accept: firstHeader(req.headers.accept),
19
+ clientIp: engine.clientIp(req),
20
+ method: req.method ?? "GET",
21
+ url: requestUrl(req)
22
+ });
23
+ intercept(res, engine, state);
24
+ next();
25
+ };
26
+ }
27
+ /**
28
+ * Patches `writeHead`/`write`/`end`. The decision is taken when headers are about to be sent:
29
+ * non-convertible responses go straight through; HTML that should become Markdown is diverted
30
+ * into a Web stream, run through the shared engine, and the result written to the real response.
31
+ */
32
+ function intercept(res, engine, state) {
33
+ const original = { writeHead: res.writeHead, write: res.write, end: res.end };
34
+ let decided = false;
35
+ let sink;
36
+ let fields = EMPTY_FIELDS;
37
+ let emitted = false;
38
+ const emitOnce = () => {
39
+ if (emitted)
40
+ return;
41
+ emitted = true;
42
+ engine.emit(state, fields);
43
+ };
44
+ res.once("finish", emitOnce);
45
+ res.once("close", emitOnce);
46
+ /** Folds explicit writeHead headers into the response, then decides whether to transform. */
47
+ const decide = (statusCode, args) => {
48
+ decided = true;
49
+ const statusMessage = typeof args[0] === "string" ? args[0] : undefined;
50
+ const headers = typeof args[0] === "string" ? args[1] : args[0];
51
+ applyHeaders(res, headers);
52
+ engine.decorate({ get: (name) => firstHeader(res.getHeader(name)), set: (name, value) => res.setHeader(name, value) }, state);
53
+ const transform = engine.shouldTransform(state, {
54
+ status: statusCode,
55
+ contentType: firstHeader(res.getHeader("content-type")) ?? "",
56
+ contentEncoding: firstHeader(res.getHeader("content-encoding")),
57
+ hasBody: true
58
+ });
59
+ if (transform) {
60
+ sink = createSink(res, original, engine, state, statusCode, statusMessage, (result) => {
61
+ fields = result;
62
+ });
63
+ }
64
+ return statusMessage;
65
+ };
66
+ res.writeHead = function patchedWriteHead(statusCode, ...args) {
67
+ if (decided)
68
+ return callOriginal(original.writeHead, this, [statusCode, ...args]);
69
+ const statusMessage = decide(statusCode, args);
70
+ if (sink) {
71
+ this.statusCode = statusCode;
72
+ return this;
73
+ }
74
+ // Headers passed to writeHead were folded into the response by decide().
75
+ return callOriginal(original.writeHead, this, statusMessage === undefined ? [statusCode] : [statusCode, statusMessage]);
76
+ };
77
+ res.write = function patchedWrite(...args) {
78
+ if (!decided)
79
+ decide(this.statusCode, []);
80
+ if (!sink)
81
+ return callOriginal(original.write, this, args);
82
+ const { chunk, encoding, callback } = writeArgs(args);
83
+ return sink.write(toBytes(chunk, encoding), callback);
84
+ };
85
+ res.end = function patchedEnd(...args) {
86
+ if (!decided)
87
+ decide(this.statusCode, []);
88
+ if (!sink)
89
+ return callOriginal(original.end, this, args);
90
+ const { chunk, encoding, callback } = writeArgs(args);
91
+ if (chunk !== undefined && chunk !== null)
92
+ sink.write(toBytes(chunk, encoding));
93
+ sink.end(callback);
94
+ return this;
95
+ };
96
+ }
97
+ /**
98
+ * Bridges the app's writes into a ReadableStream (with backpressure: `write` returns false and
99
+ * `drain` fires when the converter catches up), runs the engine and flushes its response.
100
+ */
101
+ function createSink(res, original, engine, state, statusCode, statusMessage, onFields) {
102
+ let controller;
103
+ let closed = false;
104
+ let discarding = false;
105
+ let waitingForDrain = false;
106
+ let flushed = false;
107
+ let endCallback;
108
+ const releaseApp = () => {
109
+ if (waitingForDrain) {
110
+ waitingForDrain = false;
111
+ res.emit("drain");
112
+ }
113
+ };
114
+ const body = new ReadableStream({
115
+ start(c) {
116
+ controller = c;
117
+ },
118
+ pull: releaseApp,
119
+ cancel() {
120
+ // The engine has read enough (truncation or cache hit): further app writes are discarded,
121
+ // and write() keeps returning false so well-behaved sources (pipe, pipeline) stop producing.
122
+ closed = true;
123
+ discarding = true;
124
+ }
125
+ }, new ByteLengthQueuingStrategy({ highWaterMark: HIGH_WATER_MARK }));
126
+ const htmlResponse = new Response(body, {
127
+ status: statusCode,
128
+ statusText: statusMessage,
129
+ headers: toWebHeaders(res)
130
+ });
131
+ engine
132
+ .transform(htmlResponse, state)
133
+ .then(async ({ response, fields }) => {
134
+ onFields(fields);
135
+ await flush(res, original, response);
136
+ callOriginal(original.end, res, endCallback ? [endCallback] : []);
137
+ flushed = true;
138
+ // Release anything still awaiting 'drain' (its writes are discarded): no dangling promises.
139
+ releaseApp();
140
+ })
141
+ .catch((err) => {
142
+ console.error("[AgentPulse] Failed to write the transformed response:", err);
143
+ res.destroy(err instanceof Error ? err : new Error(String(err)));
144
+ });
145
+ return {
146
+ write(bytes, callback) {
147
+ if (!closed && bytes.byteLength > 0)
148
+ controller.enqueue(bytes);
149
+ if (callback)
150
+ queueMicrotask(() => callback());
151
+ if (discarding || (!closed && (controller.desiredSize ?? 1) <= 0)) {
152
+ waitingForDrain = true;
153
+ return false;
154
+ }
155
+ return true;
156
+ },
157
+ end(callback) {
158
+ // The response may already be complete (truncated read): still honour the app's callback.
159
+ if (flushed) {
160
+ if (callback)
161
+ queueMicrotask(() => callback());
162
+ return;
163
+ }
164
+ endCallback = callback;
165
+ if (!closed) {
166
+ closed = true;
167
+ controller.close();
168
+ }
169
+ }
170
+ };
171
+ }
172
+ /** Replaces the response headers with the engine's and streams its body through the real methods. */
173
+ async function flush(res, original, response) {
174
+ for (const name of res.getHeaderNames())
175
+ res.removeHeader(name);
176
+ response.headers.forEach((value, name) => {
177
+ if (name !== "set-cookie")
178
+ res.setHeader(name, value);
179
+ });
180
+ const cookies = response.headers.getSetCookie();
181
+ if (cookies.length)
182
+ res.setHeader("set-cookie", cookies);
183
+ callOriginal(original.writeHead, res, response.statusText ? [response.status, response.statusText] : [response.status]);
184
+ if (!response.body)
185
+ return;
186
+ const reader = response.body.getReader();
187
+ while (true) {
188
+ const { done, value } = await reader.read();
189
+ if (done)
190
+ break;
191
+ if (res.destroyed) {
192
+ await reader.cancel();
193
+ break;
194
+ }
195
+ const ok = callOriginal(original.write, res, [value]);
196
+ if (!ok)
197
+ await new Promise((resolve) => res.once("drain", () => resolve()));
198
+ }
199
+ }
200
+ function toWebHeaders(res) {
201
+ const headers = new Headers();
202
+ for (const name of res.getHeaderNames()) {
203
+ const value = res.getHeader(name);
204
+ if (value === undefined)
205
+ continue;
206
+ for (const item of Array.isArray(value) ? value : [value])
207
+ headers.append(name, String(item));
208
+ }
209
+ return headers;
210
+ }
211
+ /** Applies headers given to `writeHead` (object or flat `[name, value, …]` array) via setHeader. */
212
+ function applyHeaders(res, headers) {
213
+ if (!headers || typeof headers !== "object")
214
+ return;
215
+ if (Array.isArray(headers)) {
216
+ const flat = headers.flat();
217
+ for (let i = 0; i + 1 < flat.length; i += 2)
218
+ res.setHeader(String(flat[i]), flat[i + 1]);
219
+ return;
220
+ }
221
+ for (const [name, value] of Object.entries(headers)) {
222
+ if (value !== undefined)
223
+ res.setHeader(name, value);
224
+ }
225
+ }
226
+ function writeArgs(args) {
227
+ const [chunk, second, third] = args;
228
+ if (typeof chunk === "function")
229
+ return { chunk: undefined, encoding: undefined, callback: chunk };
230
+ if (typeof second === "function")
231
+ return { chunk, encoding: undefined, callback: second };
232
+ return {
233
+ chunk,
234
+ encoding: typeof second === "string" ? second : undefined,
235
+ callback: typeof third === "function" ? third : undefined
236
+ };
237
+ }
238
+ function toBytes(chunk, encoding) {
239
+ if (chunk === undefined || chunk === null)
240
+ return new Uint8Array(0);
241
+ if (chunk instanceof Uint8Array)
242
+ return chunk;
243
+ return Buffer.from(String(chunk), encoding ?? "utf8");
244
+ }
245
+ function callOriginal(method, self, args) {
246
+ return method.apply(self, args);
247
+ }
248
+ function firstHeader(value) {
249
+ if (value === undefined)
250
+ return undefined;
251
+ return Array.isArray(value) ? value[0] : String(value);
252
+ }
253
+ /** Absolute URL for cache keys and telemetry (query stripped later). Express keeps the mount-free path in originalUrl. */
254
+ function requestUrl(req) {
255
+ const path = req.originalUrl ?? req.url ?? "/";
256
+ const encrypted = req.socket?.encrypted === true;
257
+ const host = firstHeader(req.headers.host) ?? "localhost";
258
+ try {
259
+ return new URL(path, `${encrypted ? "https" : "http"}://${host}`).toString();
260
+ }
261
+ catch {
262
+ return `http://localhost${path.startsWith("/") ? path : "/"}`;
263
+ }
264
+ }
265
+ //# sourceMappingURL=express.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import type { Context, MiddlewareHandler } from "hono";
2
+ import type { AgentPulseOptions } from "../types.js";
3
+ /** Options for the Hono adapter: `getClientIp` receives Hono's `Context`. */
4
+ export type HonoAgentPulseOptions = AgentPulseOptions<Context>;
5
+ /**
6
+ * AgentPulse middleware for Hono. All detection, negotiation and security logic lives in the
7
+ * shared engine; this adapter only maps Hono's request/response to it.
8
+ */
9
+ export declare function agentPulse(options?: HonoAgentPulseOptions): MiddlewareHandler;
10
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,iBAAiB,EAAE,MAAM,MAAM,CAAC;AAEvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD,6EAA6E;AAC7E,MAAM,MAAM,qBAAqB,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;AAE/D;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,GAAE,qBAA0B,GAAG,iBAAiB,CAgCjF"}
@@ -0,0 +1,43 @@
1
+ import { createEngine } from "../engine.js";
2
+ /**
3
+ * AgentPulse middleware for Hono. All detection, negotiation and security logic lives in the
4
+ * shared engine; this adapter only maps Hono's request/response to it.
5
+ */
6
+ export function agentPulse(options = {}) {
7
+ const engine = createEngine(options);
8
+ return async (c, next) => {
9
+ const state = engine.inspect({
10
+ userAgent: c.req.header("user-agent"),
11
+ accept: c.req.header("accept"),
12
+ clientIp: engine.clientIp(c),
13
+ method: c.req.method,
14
+ url: c.req.url
15
+ });
16
+ await next();
17
+ engine.decorate({ get: (name) => c.res.headers.get(name) ?? undefined, set: (name, value) => c.header(name, value) }, state);
18
+ let fields = { markdownDelivered: false };
19
+ const res = c.res;
20
+ if (engine.shouldTransform(state, {
21
+ status: res.status,
22
+ contentType: res.headers.get("content-type") ?? "",
23
+ contentEncoding: res.headers.get("content-encoding") ?? undefined,
24
+ hasBody: res.body !== null
25
+ })) {
26
+ const result = await engine.transform(res, state);
27
+ c.res = undefined; // Hono merges the old headers into a new response unless it is cleared first.
28
+ c.res = result.response;
29
+ fields = result.fields;
30
+ }
31
+ engine.emit(state, fields, (promise) => waitUntil(c, promise));
32
+ };
33
+ }
34
+ /** On edge runtimes, keep the isolate alive until telemetry is delivered; on Node it simply runs. */
35
+ function waitUntil(c, promise) {
36
+ try {
37
+ c.executionCtx.waitUntil(promise);
38
+ }
39
+ catch {
40
+ // No ExecutionContext (Node, Bun, tests): the promise runs detached.
41
+ }
42
+ }
43
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAM5C;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,UAAiC,EAAE;IAC5D,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,CAAC;IAErC,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC;YAC3B,SAAS,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,YAAY,CAAC;YACrC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC;YAC9B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC5B,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,MAAM;YACpB,GAAG,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG;SACf,CAAC,CAAC;QAEH,MAAM,IAAI,EAAE,CAAC;QAEb,MAAM,CAAC,QAAQ,CAAC,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,SAAS,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,KAAK,CAAC,CAAC;QAE7H,IAAI,MAAM,GAAG,EAAE,iBAAiB,EAAE,KAAK,EAAE,CAAC;QAC1C,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC;QAClB,IAAI,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE;YAChC,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,WAAW,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE;YAClD,eAAe,EAAE,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,IAAI,SAAS;YACjE,OAAO,EAAE,GAAG,CAAC,IAAI,KAAK,IAAI;SAC3B,CAAC,EAAE,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YAClD,CAAC,CAAC,GAAG,GAAG,SAAS,CAAC,CAAC,8EAA8E;YACjG,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC,QAAQ,CAAC;YACxB,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;QACzB,CAAC;QAED,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC;AACJ,CAAC;AAED,qGAAqG;AACrG,SAAS,SAAS,CAAC,CAAU,EAAE,OAAyB;IACtD,IAAI,CAAC;QACH,CAAC,CAAC,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,qEAAqE;IACvE,CAAC;AACH,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { NextResponse, type NextRequest, type NextFetchEvent } from "next/server.js";
2
+ import type { AgentPulseOptions } from "../types.js";
3
+ /**
4
+ * Next.js 16+ integration (App Router). Next's `proxy.ts` (formerly `middleware.ts`) runs before
5
+ * rendering and cannot read response bodies, so the work is split in two pieces that share the
6
+ * engine with the Hono and Express adapters (see docs/Brain "AgentPulse - Spike Next.js"):
7
+ *
8
+ * 1. `createAgentPulseProxy`: detects agents and rewrites Markdown requests to a Route Handler.
9
+ * 2. `createMarkdownRouteHandler`: fetches the page's HTML from the app itself, from a fixed
10
+ * internal origin, and converts it.
11
+ */
12
+ /** Default path of the Markdown Route Handler (`app/agentpulse-markdown/[[...path]]/route.ts`). */
13
+ export declare const DEFAULT_MARKDOWN_ROUTE = "/agentpulse-markdown";
14
+ /** Marks the handler's internal HTML fetch so the proxy never rewrites it again (no loops). */
15
+ export declare const INTERNAL_HEADER = "x-agentpulse-internal";
16
+ /** Request headers forwarded to the internal fetch so the Markdown matches what this user would see. */
17
+ export declare const DEFAULT_FORWARDED_HEADERS: string[];
18
+ export interface NextProxyOptions extends AgentPulseOptions<NextRequest> {
19
+ /** Must match the Route Handler's folder. Default: `/agentpulse-markdown`. */
20
+ markdownRoute?: string;
21
+ }
22
+ export interface NextMarkdownHandlerOptions extends AgentPulseOptions<Request> {
23
+ /**
24
+ * Where the app itself is reachable from the server, e.g. `http://127.0.0.1:3000`. **Required**
25
+ * and never derived from the request: using the Host header would let clients point the server
26
+ * at another host (SSRF).
27
+ */
28
+ internalOrigin: string;
29
+ /** Must match the Route Handler's folder and the proxy option. Default: `/agentpulse-markdown`. */
30
+ markdownRoute?: string;
31
+ /** Request headers forwarded to the internal fetch (allow-list). Default: cookie, authorization, accept-language, user-agent. */
32
+ forwardHeaders?: string[];
33
+ /**
34
+ * Cache-Control for Markdown responses. `"private"` (default) keeps shared caches (CDNs) from
35
+ * storing them: Next.js drops the proxy's `Vary: Accept` on HTML pages, so a CDN that ignores
36
+ * Vary could otherwise serve Markdown to browsers. `"inherit"` keeps the page's own header.
37
+ */
38
+ markdownCacheControl?: "private" | "inherit";
39
+ /** fetch implementation for the internal request (tests). Default: global fetch. */
40
+ fetch?: typeof globalThis.fetch;
41
+ }
42
+ /**
43
+ * `proxy.ts` handler. Rewrites requests that prefer Markdown to the Route Handler and adds
44
+ * `Vary` to the rest. HTML visits are reported to `onEvent` via `event.waitUntil`; Markdown
45
+ * visits are reported by the Route Handler (one event per request).
46
+ */
47
+ export declare function createAgentPulseProxy(options?: NextProxyOptions): (request: NextRequest, event?: NextFetchEvent) => NextResponse;
48
+ /**
49
+ * GET handler for `app/agentpulse-markdown/[[...path]]/route.ts`. Fetches the page's HTML from
50
+ * `internalOrigin` and returns its Markdown through the shared engine (cache, rate limit, IP
51
+ * verification, sanitization, telemetry).
52
+ */
53
+ export declare function createMarkdownRouteHandler(options: NextMarkdownHandlerOptions): (request: Request) => Promise<Response>;
54
+ /** Keeps freshness directives but forbids shared caches: drops public/s-maxage, adds private; no-store wins. */
55
+ export declare function privateCacheControl(value: string | null): string;
56
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,KAAK,WAAW,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAErD;;;;;;;;GAQG;AAEH,mGAAmG;AACnG,eAAO,MAAM,sBAAsB,yBAAyB,CAAC;AAE7D,+FAA+F;AAC/F,eAAO,MAAM,eAAe,0BAA0B,CAAC;AAEvD,wGAAwG;AACxG,eAAO,MAAM,yBAAyB,UAA+D,CAAC;AAEtG,MAAM,WAAW,gBAAiB,SAAQ,iBAAiB,CAAC,WAAW,CAAC;IACtE,8EAA8E;IAC9E,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,0BAA2B,SAAQ,iBAAiB,CAAC,OAAO,CAAC;IAC5E;;;;OAIG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,mGAAmG;IACnG,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iIAAiI;IACjI,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAC7C,oFAAoF;IACpF,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;CACjC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,gBAAqB,IAKlC,SAAS,WAAW,EAAE,QAAQ,cAAc,KAAG,YAAY,CAyB5F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,0BAA0B,IAQlD,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAmE/D;AAED,gHAAgH;AAChH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAWhE"}