@entreprenoid/analytics 0.1.0 → 0.1.1

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/index.js CHANGED
@@ -1,6 +1,6 @@
1
- export { entreprenoid, renderInstallMd, renderLlmsFullTxt, renderLlmsTxt } from './chunk-5NZNPRMN.js';
2
- export { proxy } from './chunk-EVNUKN5A.js';
3
- export { observe } from './chunk-GKFYGAKF.js';
4
- export { BoundedQueue, CAMPAIGN_PARAMS, DEFAULTS, DEFAULT_TWIN_CACHE_CONTROL, DEFAULT_TWIN_CONTENT_TYPE, EntreprenoidConfigError, MARKDOWN_TYPES, MAX_BODY_BYTES, REDACTED, UNSET_SITE_ID, advertiseHeader, buildTwinResponse, createBreaker, createCollector, decideTwin, encodeBatch, fetchTransport, looksLikeSecret, newEventId, normalisePath, observeRequest, observeResponse, outcomeForStatus, parseAccept, prefersMarkdown, redact, redactPath, referrerOrigin, resolveConfig, runtimeName, safe, safeAsync, stripMdSuffix, twinPathFor } from './chunk-IHZCOC2U.js';
1
+ export { entreprenoid, renderInstallMd, renderLlmsFullTxt, renderLlmsTxt } from './chunk-P3NEQOKC.js';
2
+ export { proxy } from './chunk-SJTHL7HF.js';
3
+ export { observe } from './chunk-WNCAN2XS.js';
4
+ export { BoundedQueue, CAMPAIGN_PARAMS, DEFAULTS, DEFAULT_TWIN_CACHE_CONTROL, DEFAULT_TWIN_CONTENT_TYPE, EntreprenoidConfigError, MARKDOWN_TYPES, MAX_BODY_BYTES, REDACTED, UNSET_SITE_ID, advertiseHeader, buildTwinResponse, createBreaker, createCollector, decideTwin, encodeBatch, fetchTransport, looksLikeSecret, newEventId, normalisePath, observeRequest, observeResponse, outcomeForStatus, parseAccept, prefersMarkdown, redact, redactPath, referrerOrigin, resolveConfig, runtimeName, safe, safeAsync, stripMdSuffix, twinPathFor } from './chunk-IOMJAF33.js';
5
5
  //# sourceMappingURL=index.js.map
6
6
  //# sourceMappingURL=index.js.map
package/dist/next.cjs CHANGED
@@ -569,7 +569,7 @@ function observeResponse(facts) {
569
569
 
570
570
  // src/runtime.ts
571
571
  var SDK_NAME = "@entreprenoid/analytics";
572
- var SDK_VERSION = "0.1.0";
572
+ var SDK_VERSION = "0.1.1";
573
573
  function runtimeName() {
574
574
  try {
575
575
  const g = globalThis;
package/dist/next.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core/breaker.ts","../src/core/config.ts","../src/core/encode.ts","../src/core/queue.ts","../src/core/safe.ts","../src/core/transport.ts","../src/core/collector.ts","../src/observe/redact.ts","../src/observe/request.ts","../src/observe/response.ts","../src/runtime.ts","../src/serve/accept.ts","../src/serve/twin.ts","../src/next.ts"],"names":[],"mappings":"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","file":"next.cjs","sourcesContent":["export type BreakerState = \"closed\" | \"open\" | \"half-open\";\n\nexport interface Breaker {\n /** May an attempt be made right now? */\n allow(): boolean;\n success(): void;\n failure(): void;\n readonly state: BreakerState;\n}\n\nexport interface BreakerOptions {\n failureThreshold?: number;\n openMs?: number;\n now?: () => number;\n}\n\n/**\n * Stop calling an endpoint that is not answering.\n *\n * ── What it is actually protecting ───────────────────────────────────────────\n * Not us -- the customer. When our ingest is down, every flush costs the host\n * process a DNS lookup, a connection attempt and a timeout. A fleet of customer\n * servers doing that every two seconds turns our outage into measurable latency\n * and socket pressure in their applications. The breaker makes an outage cost\n * approximately nothing on their side.\n *\n * ⚠️ **Only `retryable` outcomes count as failures.** A 400 means our payload is\n * wrong, which is our bug and must be loud in debug -- but it is not an outage,\n * and counting it would let one malformed field stop all telemetry for thirty\n * seconds at a time, forever, while the endpoint is perfectly healthy.\n *\n * ⚠️ While open, `record()` still ENQUEUES. The ring buffer bounds the memory,\n * and a thirty-second blip should not lose events the buffer would have held.\n * What the breaker stops is the network call, not the collection.\n */\nexport function createBreaker(options: BreakerOptions = {}): Breaker {\n const failureThreshold = options.failureThreshold ?? 5;\n const openMs = options.openMs ?? 30_000;\n const now = options.now ?? (() => Date.now());\n\n let consecutiveFailures = 0;\n let openedAt = 0;\n let state: BreakerState = \"closed\";\n\n return {\n get state() {\n return state;\n },\n allow(): boolean {\n if (state === \"closed\") return true;\n if (state === \"half-open\") return true;\n if (now() - openedAt >= openMs) {\n // One probe. If it fails we go straight back to open with a fresh\n // window, rather than letting a long outage produce a probe every\n // interval.\n state = \"half-open\";\n return true;\n }\n return false;\n },\n success(): void {\n consecutiveFailures = 0;\n state = \"closed\";\n },\n failure(): void {\n consecutiveFailures += 1;\n if (state === \"half-open\" || consecutiveFailures >= failureThreshold) {\n state = \"open\";\n openedAt = now();\n }\n },\n };\n}\n","/**\n * Turn options and environment into one frozen, validated configuration.\n *\n * Precedence, decided once here so no caller has to: explicit option, then\n * environment variable, then default. The same order `maxidomo-cli` settled on,\n * and for the same reason -- a precedence rule implemented twice is implemented\n * differently.\n */\n\n/**\n * What `isInternal` is shown. Structurally a subset of `ObservedRequest`, and\n * declared here rather than imported so config does not depend on the observer\n * that depends on it.\n */\nexport interface InternalTrafficContext {\n method: string;\n path: string;\n host?: string | undefined;\n userAgent?: string | undefined;\n}\n\nexport interface EntreprenoidConfig {\n ingestUrl?: string;\n /** ⚠️ Server-only. Never logged, never in a browser bundle. */\n serverKey?: string;\n siteId?: string;\n enabled?: boolean;\n debug?: boolean;\n batchSize?: number;\n flushIntervalMs?: number;\n maxQueueEvents?: number;\n maxBodyBytes?: number;\n requestTimeoutMs?: number;\n /**\n * Collapse identifiers out of a path: `/users/42` -> `/users/:id`.\n *\n * This is the main defence against both unbounded cardinality and per-user\n * values reaching analytics, and only the customer knows their routes.\n */\n routeTemplate?: (path: string) => string | undefined;\n /** Path SEGMENTS matching any of these become `[redacted]`. */\n redactPatterns?: readonly RegExp[];\n /**\n * ⚠️ **Default ON.** Replaces path segments that look like credentials --\n * uuids, cuids, JWTs, long hex, dense mixed-case strings -- with\n * `[redacted]`, so a site with tokens in its paths does not ship them here\n * merely because nobody configured `redactPatterns`.\n *\n * Set `false` to keep every segment verbatim. Doing so does NOT disable\n * `redactPatterns`; those are yours and always apply.\n *\n * See `observe/redact.ts` for what it does and does not catch, and why the\n * trade is made in the direction of redacting.\n */\n redactHighEntropyPaths?: boolean;\n /** Mark traffic the customer does not want counted or billed. */\n isInternal?: (request: InternalTrafficContext) => boolean;\n}\n\n/**\n * What the SDK sends when no site id was configured.\n *\n * ⚠️ Deliberately NOT a plausible id. Ingest replaces it from the credential,\n * so it never reaches storage -- but if it ever shows up somewhere, it should\n * read as \"nobody configured this\" rather than as a real site.\n */\nexport const UNSET_SITE_ID = \"unset\";\n\nexport type DisabledReason =\n | \"explicitly-disabled\"\n | \"missing-key\"\n | \"missing-url\"\n | \"browser-environment\";\n\nexport interface ResolvedConfig {\n readonly ingestUrl: string;\n readonly serverKey: string;\n readonly siteId: string;\n readonly debug: boolean;\n readonly batchSize: number;\n readonly flushIntervalMs: number;\n readonly maxQueueEvents: number;\n readonly maxBodyBytes: number;\n readonly requestTimeoutMs: number;\n readonly routeTemplate: ((path: string) => string | undefined) | undefined;\n readonly redactPatterns: readonly RegExp[];\n readonly redactHighEntropyPaths: boolean;\n readonly isInternal: ((request: InternalTrafficContext) => boolean) | undefined;\n /** Non-null means the collector is a transparent no-op. */\n readonly disabled: DisabledReason | null;\n}\n\nexport const DEFAULTS = {\n batchSize: 20,\n maxBatchSize: 100,\n flushIntervalMs: 2_000,\n maxQueueEvents: 1_000,\n maxBodyBytes: 512 * 1024,\n requestTimeoutMs: 2_000,\n} as const;\n\n/**\n * Environment names a customer might create that would put the server key in a\n * browser bundle.\n *\n * ⚠️ The likeliest real-world leak of a server credential is not a bad import.\n * It is somebody writing `NEXT_PUBLIC_ENTREPRENOID_SERVER_KEY` because that is\n * how they made the last environment variable work. Every one of these prefixes\n * means \"inline this into client-side JavaScript\", so a key under one of them\n * is already published.\n */\nconst PUBLIC_ENV_PREFIXES = [\"NEXT_PUBLIC_\", \"VITE_\", \"PUBLIC_\", \"REACT_APP_\", \"NUXT_PUBLIC_\"];\n\nexport class EntreprenoidConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EntreprenoidConfigError\";\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction boolFromEnv(raw: string | undefined): boolean | undefined {\n if (raw === undefined) return undefined;\n const v = raw.trim().toLowerCase();\n if (v === \"1\" || v === \"true\" || v === \"yes\") return true;\n if (v === \"0\" || v === \"false\" || v === \"no\") return false;\n return undefined;\n}\n\nexport function resolveConfig(\n options: EntreprenoidConfig = {},\n env: Record<string, string | undefined> = typeof process === \"undefined\"\n ? {}\n : (process.env as Record<string, string | undefined>),\n): ResolvedConfig {\n // ⚠️ The ONE place this package is allowed to throw, and it throws before any\n // request is served rather than during one. A leaked server credential is not\n // something to degrade gracefully around: the customer needs to know now, by\n // name, which variable is the problem.\n for (const prefix of PUBLIC_ENV_PREFIXES) {\n const name = `${prefix}ENTREPRENOID_SERVER_KEY`;\n if (env[name]) {\n throw new EntreprenoidConfigError(\n `${name} is set. The \"${prefix}\" prefix inlines a value into client-side JavaScript, ` +\n `so this key is already public. Revoke it, then set ENTREPRENOID_SERVER_KEY instead ` +\n `(no prefix) so it stays on the server.`,\n );\n }\n }\n\n const ingestUrl = options.ingestUrl ?? env[\"ENTREPRENOID_INGEST_URL\"] ?? \"\";\n const serverKey = options.serverKey ?? env[\"ENTREPRENOID_SERVER_KEY\"] ?? \"\";\n // ⚠️ **Defaulted, never empty, and this is a data-loss fix rather than a\n // nicety.**\n //\n // The wire schema requires `siteId` with min length 1, so an unset value\n // rejects the ENTIRE BATCH as `invalid_batch` -- and because the SDK fails\n // open, that rejection is silent. The symptom is an empty dashboard and a\n // perfectly healthy application, which is the hardest thing this product can\n // do to somebody. Found by a real install attempt\n // (`docs/design/install-findings-2026-09-16.md`), reproduced against the real\n // schema rather than assumed.\n //\n // ⚠️ The honest reason a default is CORRECT here, rather than a papering-over:\n // ingest OVERWRITES `siteId` from the credential, because a body value is a\n // request to write into somebody else's data. So the field is never trusted\n // and never stored as sent -- its only job is passing validation. Requiring a\n // customer to supply it correctly was asking them to get right something that\n // cannot be wrong, and punishing them silently for not.\n //\n // A customer may still set it, and the dashboard still shows it, because an\n // operator matching a log line to a site wants to see it.\n const siteId = options.siteId ?? env[\"ENTREPRENOID_SITE_ID\"] ?? UNSET_SITE_ID;\n const enabled = options.enabled ?? boolFromEnv(env[\"ENTREPRENOID_ENABLED\"]) ?? true;\n const debug = options.debug ?? boolFromEnv(env[\"ENTREPRENOID_DEBUG\"]) ?? false;\n\n let disabled: DisabledReason | null = null;\n if (typeof window !== \"undefined\") {\n // Belt and braces behind the `browser: null` export condition. If a bundler\n // that ignores export conditions got us here, collect nothing and -- above\n // all -- do not transmit the key.\n disabled = \"browser-environment\";\n } else if (!enabled) {\n disabled = \"explicitly-disabled\";\n } else if (!ingestUrl) {\n disabled = \"missing-url\";\n } else if (!serverKey) {\n disabled = \"missing-key\";\n }\n\n return Object.freeze({\n ingestUrl,\n serverKey,\n siteId,\n debug,\n batchSize: clamp(options.batchSize ?? DEFAULTS.batchSize, 1, DEFAULTS.maxBatchSize),\n flushIntervalMs: clamp(options.flushIntervalMs ?? DEFAULTS.flushIntervalMs, 100, 60_000),\n maxQueueEvents: clamp(options.maxQueueEvents ?? DEFAULTS.maxQueueEvents, 1, 100_000),\n maxBodyBytes: clamp(options.maxBodyBytes ?? DEFAULTS.maxBodyBytes, 1_024, DEFAULTS.maxBodyBytes),\n requestTimeoutMs: clamp(options.requestTimeoutMs ?? DEFAULTS.requestTimeoutMs, 100, 30_000),\n routeTemplate: options.routeTemplate,\n redactPatterns: options.redactPatterns ?? [],\n // ⚠️ Defaults to TRUE. An `?? true` that a refactor turns into `?? false`\n // is the entire bug returning, so `config.test.ts` asserts the default.\n redactHighEntropyPaths: options.redactHighEntropyPaths ?? true,\n isInternal: options.isInternal,\n disabled,\n });\n}\n\n/**\n * Redact anything key-shaped before it reaches a log line.\n *\n * The SDK must never log the server key (§8.2). Debug output is written by\n * people debugging, pasted into issues, and captured by log aggregators.\n */\nexport function redact(text: string): string {\n return text.replace(/\\bep_(live|test)_server_[A-Za-z0-9_-]+/g, \"ep_$1_server_[redacted]\");\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\n\n/**\n * Turn events into a request body, stopping before a byte ceiling.\n *\n * ── Why this is not `JSON.stringify({ events })` ─────────────────────────────\n * The ingest refuses a body over 512 KiB. Encoding the whole batch and then\n * checking its size means either sending something that will be refused, or\n * truncating a JSON document -- which produces a body that is not JSON at all\n * and fails in a way no error message explains.\n *\n * So events are measured one at a time and the encoder stops BEFORE the one\n * that would cross the line. The untaken events stay at the head of the queue\n * for the next flush.\n */\n\n/** The largest encoded body the ingest will accept, in bytes. */\nexport const MAX_BODY_BYTES = 512 * 1024;\n\nconst OPEN = '{\"events\":[';\nconst CLOSE = \"]}\";\n\nexport interface EncodedBatch {\n body: string;\n /** How many events from the front of the input are in `body`. */\n taken: number;\n /**\n * An event that can NEVER be sent because it alone exceeds the ceiling.\n *\n * ⚠️ This is head-of-line blocking, and it is the reason this field exists.\n * Without it, one oversized event sits at the front of the queue forever:\n * every flush encodes zero events, commits nothing, and the SDK goes\n * permanently silent with a full buffer and no error. The caller drops it and\n * counts it as dropped.\n */\n oversized: boolean;\n}\n\nconst byteLength =\n typeof TextEncoder === \"function\"\n ? (s: string): number => new TextEncoder().encode(s).length\n : // Node 18+ always has TextEncoder; this is here so the module cannot\n // throw at import time on an exotic runtime, which would take the host\n // application down with it.\n (s: string): number => s.length;\n\nexport function encodeBatch(\n events: readonly RequestEvent[],\n maxBytes: number = MAX_BODY_BYTES,\n): EncodedBatch {\n if (events.length === 0) {\n return { body: OPEN + CLOSE, taken: 0, oversized: false };\n }\n\n const overhead = byteLength(OPEN) + byteLength(CLOSE);\n let used = overhead;\n const parts: string[] = [];\n\n for (const event of events) {\n const encoded = JSON.stringify(event);\n // Every event after the first costs a comma.\n const cost = byteLength(encoded) + (parts.length > 0 ? 1 : 0);\n if (used + cost > maxBytes) break;\n parts.push(encoded);\n used += cost;\n }\n\n return {\n body: OPEN + parts.join(\",\") + CLOSE,\n taken: parts.length,\n oversized: parts.length === 0,\n };\n}\n","/**\n * A bounded ring buffer of pending events.\n *\n * ── Why bounded, and why drop-OLDEST ─────────────────────────────────────────\n * This queue lives in a customer's production process. An unbounded one turns\n * our outage into their out-of-memory kill, which is the single worst thing an\n * analytics package can do to a host application. So it has a hard ceiling.\n *\n * When the ceiling is reached the OLDEST event is discarded, not the newest.\n * During an outage the recent past is what a site owner is looking at; a buffer\n * that refused new events would preserve a frozen window from whenever the\n * outage began and discard everything since.\n *\n * ⚠️ **Drops are COUNTED and reported on the wire** (`sdk.dropped`). A silently\n * short count is worse than a visibly short one: the product's whole claim is\n * that it shows traffic other tools miss, so under-reporting without saying so\n * is the one failure mode that discredits the number rather than the outage.\n *\n * ── peek/commit rather than drain ────────────────────────────────────────────\n * A send can partially succeed: the encoder stops before the byte limit, so\n * fewer events go out than were offered. `peek` then `commit(n)` keeps the\n * untaken ones at the head with no re-insertion, which is both cheaper and\n * impossible to get out of order.\n */\nexport class BoundedQueue<T> {\n readonly capacity: number;\n #items: (T | undefined)[];\n #head = 0;\n #size = 0;\n #dropped = 0;\n\n constructor(capacity: number) {\n if (!Number.isInteger(capacity) || capacity < 1) {\n throw new TypeError(`capacity must be a positive integer, got ${capacity}`);\n }\n this.capacity = capacity;\n this.#items = new Array<T | undefined>(capacity);\n }\n\n get size(): number {\n return this.#size;\n }\n\n /** How many events have been discarded because the buffer was full. */\n get dropped(): number {\n return this.#dropped;\n }\n\n push(item: T): void {\n if (this.#size === this.capacity) {\n // Full: overwrite the oldest and advance the head past it.\n this.#items[this.#head] = item;\n this.#head = (this.#head + 1) % this.capacity;\n this.#dropped += 1;\n return;\n }\n this.#items[(this.#head + this.#size) % this.capacity] = item;\n this.#size += 1;\n }\n\n /** The first `max` items, without removing them. */\n peek(max: number): T[] {\n const n = Math.min(max, this.#size);\n const out: T[] = new Array<T>(n);\n for (let i = 0; i < n; i += 1) {\n out[i] = this.#items[(this.#head + i) % this.capacity] as T;\n }\n return out;\n }\n\n /** Remove the first `n` items. Called only after they are safely sent. */\n commit(n: number): void {\n const count = Math.min(n, this.#size);\n for (let i = 0; i < count; i += 1) {\n this.#items[(this.#head + i) % this.capacity] = undefined;\n }\n this.#head = (this.#head + count) % this.capacity;\n this.#size -= count;\n }\n\n /** Reset the drop counter, once the count has been reported on the wire. */\n clearDropped(): void {\n this.#dropped = 0;\n }\n}\n","/**\n * The wrapper every entry point into this package wears.\n *\n * ⚠️ **This is the most important twenty lines in the SDK.** Our code runs\n * inside other people's request handlers. A throw that escapes becomes a 500 on\n * a page we were only supposed to be counting -- the analytics package breaking\n * the application it measures, which is unforgivable in a way that a missing\n * metric is not.\n *\n * So every public surface is wrapped, and the wrapper swallows. It reports\n * through the debug channel when the customer asked for one, and is otherwise\n * silent: a warning printed on every request of a busy server is its own\n * outage.\n */\nexport function safe(fn: () => void, onError?: (error: unknown) => void): void {\n try {\n fn();\n } catch (error) {\n try {\n onError?.(error);\n } catch {\n // The error reporter threw. There is nowhere left to report that, and\n // trying would be the same bug one level up.\n }\n }\n}\n\n/** The async form, for flush paths. Returns a promise that never rejects. */\nexport async function safeAsync(\n fn: () => Promise<void>,\n onError?: (error: unknown) => void,\n): Promise<void> {\n try {\n await fn();\n } catch (error) {\n try {\n onError?.(error);\n } catch {\n // As above.\n }\n }\n}\n","/**\n * The seam between the collector and the network.\n *\n * Exists so the collector can be tested without a socket, and so a runtime\n * without `fetch` can supply its own. The three-way outcome below is the\n * important part of the contract.\n */\nexport type SendOutcome =\n | { outcome: \"accepted\"; status: number }\n /**\n * A 4xx that is not 408 or 429: our payload is wrong.\n *\n * ⚠️ **Drop the batch. Do not retry, and do not count it against the\n * breaker.** Retrying a body the server has already judged malformed just\n * sends it again, and treating it as an outage would let one bad field\n * silence all telemetry for thirty seconds at a time while the endpoint is\n * perfectly healthy. This is the same distinction maxidomo-app's\n * `ExpectedError` draws: a failure somebody wrote on purpose is not an\n * incident.\n */\n | { outcome: \"rejected\"; status: number }\n /** Network error, timeout, 408, 429 or 5xx. Retry, and count it. */\n | { outcome: \"retryable\"; status?: number; retryAfterMs?: number };\n\nexport interface Transport {\n send(\n body: string,\n headers: Readonly<Record<string, string>>,\n signal: AbortSignal,\n ): Promise<SendOutcome>;\n}\n\n/** Map an HTTP status onto the outcome taxonomy above. */\nexport function outcomeForStatus(status: number, retryAfterMs?: number): SendOutcome {\n if (status >= 200 && status < 300) return { outcome: \"accepted\", status };\n if (status === 408 || status === 429 || status >= 500) {\n return retryAfterMs === undefined\n ? { outcome: \"retryable\", status }\n : { outcome: \"retryable\", status, retryAfterMs };\n }\n return { outcome: \"rejected\", status };\n}\n\nfunction parseRetryAfter(value: string | null): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;\n const date = Date.parse(value);\n if (Number.isFinite(date)) return Math.max(0, date - Date.now());\n return undefined;\n}\n\n/**\n * The default transport: `fetch`, which every supported runtime has.\n *\n * ⚠️ It never throws. A thrown transport would reach the flush loop, and a\n * flush loop that can throw is one unhandled rejection away from taking a\n * customer's process down with it.\n */\nexport function fetchTransport(url: string): Transport {\n return {\n async send(body, headers, signal): Promise<SendOutcome> {\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body,\n signal,\n // Never follow a redirect: this request carries a bearer token, and\n // a redirect is an instruction from the network to send that\n // credential somewhere we did not choose.\n redirect: \"error\",\n keepalive: false,\n });\n return outcomeForStatus(\n response.status,\n parseRetryAfter(response.headers.get(\"retry-after\")),\n );\n } catch {\n // DNS failure, connection refused, TLS error, abort. All retryable, and\n // none of them is ever allowed to surface.\n return { outcome: \"retryable\" };\n }\n },\n };\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\nimport { createBreaker, type Breaker, type BreakerState } from \"./breaker.js\";\nimport { redact, type ResolvedConfig } from \"./config.js\";\nimport { encodeBatch } from \"./encode.js\";\nimport { BoundedQueue } from \"./queue.js\";\nimport { safeAsync } from \"./safe.js\";\nimport { fetchTransport, type SendOutcome, type Transport } from \"./transport.js\";\n\nexport interface CollectorStats {\n queued: number;\n dropped: number;\n sent: number;\n failed: number;\n breaker: BreakerState;\n}\n\nexport interface Collector {\n /**\n * Hand an event to the collector.\n *\n * ⚠️ **Synchronous, returns void, and never throws.** Those three properties\n * are the contract with the host application and each is asserted by a test.\n * It is deliberately impossible for a caller to await this: the moment an\n * adapter can await the collector, somebody will, and analytics will be on\n * the critical path.\n */\n record(event: RequestEvent): void;\n /** Attempt to send what is buffered. Never rejects. */\n flush(): Promise<void>;\n readonly stats: CollectorStats;\n /** Stop the timer. For tests and for a host that manages its own lifecycle. */\n close(): void;\n}\n\nexport interface CollectorDeps {\n transport?: Transport;\n breaker?: Breaker;\n /** Injected so backoff is deterministic under test. */\n random?: () => number;\n /** Injected so tests need no real timers. */\n setTimer?: (fn: () => void, ms: number) => { cancel(): void };\n log?: (message: string) => void;\n}\n\nconst MAX_RETRIES = 2;\n\nfunction defaultSetTimer(fn: () => void, ms: number): { cancel(): void } {\n const handle = setInterval(fn, ms);\n // ⚠️ `unref()` is not an optimisation, it is a correctness requirement.\n //\n // Without it an interval keeps Node's event loop alive, so any short-lived\n // process that imports the SDK -- a CLI, a migration script, a build step --\n // hangs for the flush interval before exiting. To the customer that presents\n // as \"the analytics package broke my build\", and they are right.\n //\n // `unref` is Node-only; a runtime without it simply keeps the timer, which is\n // correct for a long-lived server.\n (handle as unknown as { unref?: () => void }).unref?.();\n return { cancel: () => clearInterval(handle) };\n}\n\nexport function createCollector(config: ResolvedConfig, deps: CollectorDeps = {}): Collector {\n const log = deps.log ?? ((message: string) => console.warn(redact(message)));\n const debug = (message: string): void => {\n if (config.debug) log(`[entreprenoid] ${message}`);\n };\n\n // A disabled collector is a transparent no-op -- not a broken one, and not a\n // noisy one. Missing configuration in production is normal (a preview\n // deployment, a contributor's laptop), so it is reported once in debug and\n // never again.\n if (config.disabled) {\n debug(`collection disabled: ${config.disabled}`);\n const noop: Collector = {\n record: () => {},\n flush: async () => {},\n stats: { queued: 0, dropped: 0, sent: 0, failed: 0, breaker: \"closed\" },\n close: () => {},\n };\n return noop;\n }\n\n const queue = new BoundedQueue<RequestEvent>(config.maxQueueEvents);\n const transport = deps.transport ?? fetchTransport(config.ingestUrl);\n const breaker = deps.breaker ?? createBreaker();\n const random = deps.random ?? Math.random;\n const setTimer = deps.setTimer ?? defaultSetTimer;\n\n let sent = 0;\n let failed = 0;\n let inFlight: Promise<void> | null = null;\n\n const timer = setTimer(() => {\n void flush();\n }, config.flushIntervalMs);\n\n function headers(): Record<string, string> {\n return {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${config.serverKey}`,\n };\n }\n\n async function sendOnce(body: string): Promise<SendOutcome> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), config.requestTimeoutMs);\n (timeout as unknown as { unref?: () => void }).unref?.();\n try {\n return await transport.send(body, headers(), controller.signal);\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /** Full jitter. A fleet of customer servers must not retry in lockstep. */\n function backoffMs(attempt: number, retryAfterMs?: number): number {\n if (retryAfterMs !== undefined) return Math.min(retryAfterMs, 30_000);\n return random() * Math.min(5_000, 200 * 3 ** attempt);\n }\n\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n const t = setTimeout(resolve, ms);\n (t as unknown as { unref?: () => void }).unref?.();\n });\n\n async function flushOnce(): Promise<void> {\n if (queue.size === 0) return;\n if (!breaker.allow()) {\n debug(\"breaker open, skipping flush\");\n return;\n }\n\n const candidates = queue.peek(config.batchSize);\n const droppedSoFar = queue.dropped;\n if (droppedSoFar > 0 && candidates[0]) {\n // Report the shortfall on the wire rather than under-counting silently.\n candidates[0] = {\n ...candidates[0],\n sdk: { ...candidates[0].sdk, dropped: droppedSoFar },\n };\n }\n\n const encoded = encodeBatch(candidates, config.maxBodyBytes);\n\n if (encoded.oversized) {\n // One event alone exceeds the body limit and can never be sent. Drop it,\n // or it blocks the head of the queue forever and the SDK goes silent with\n // a full buffer and no error anywhere.\n queue.commit(1);\n debug(\"dropped one event larger than the body limit\");\n return;\n }\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {\n const result = await sendOnce(encoded.body);\n\n if (result.outcome === \"accepted\") {\n queue.commit(encoded.taken);\n if (droppedSoFar > 0) queue.clearDropped();\n sent += encoded.taken;\n breaker.success();\n return;\n }\n\n if (result.outcome === \"rejected\") {\n // Our payload is wrong. Retrying sends the same bad body again, and\n // counting it against the breaker would silence a healthy endpoint.\n queue.commit(encoded.taken);\n failed += encoded.taken;\n debug(`batch rejected with ${result.status}; dropped ${encoded.taken} event(s)`);\n return;\n }\n\n if (attempt === MAX_RETRIES) break;\n await sleep(backoffMs(attempt, result.retryAfterMs));\n }\n\n failed += encoded.taken;\n breaker.failure();\n debug(`batch failed after ${MAX_RETRIES + 1} attempt(s); ${queue.size} event(s) still buffered`);\n }\n\n function flush(): Promise<void> {\n // One flush at a time. Two concurrent flushes would both peek the same\n // head of the queue and send it twice -- which ingest deduplicates, but\n // only because every event carries an id. Not relying on that.\n if (inFlight) return inFlight;\n inFlight = safeAsync(flushOnce, (error) => debug(`flush failed: ${String(error)}`)).finally(\n () => {\n inFlight = null;\n },\n );\n return inFlight;\n }\n\n return {\n record(event: RequestEvent): void {\n // No try/catch needed for the push itself -- the ring buffer cannot throw\n // -- but the contract is \"never throws\" and a future edit here must not\n // be able to break it.\n try {\n queue.push(event);\n } catch {\n /* never allowed to surface */\n }\n },\n flush,\n get stats(): CollectorStats {\n return {\n queued: queue.size,\n dropped: queue.dropped,\n sent,\n failed,\n breaker: breaker.state,\n };\n },\n close(): void {\n timer.cancel();\n },\n };\n}\n","/**\n * Decide whether a path segment looks like a secret, and redact it by default.\n *\n * ── Why this is ON by default, when `redactPatterns` already exists ──────────\n * Because opt-in is what failed. `redactPatterns`, `routeTemplate` and\n * `isInternal` are all well designed and all opt-in, and an independent install\n * attempt (`docs/design/install-findings-2026-09-16.md`) found that the install\n * guide mentioned **none of the three**. The site in question puts single-use\n * capability tokens in its PATHS -- `/reveal/<token>`, `/join/<code>` -- so an\n * agent following the guide exactly would have POSTed live credential material\n * to our event store.\n *\n * The guide now has a step about it, and a step an agent can skip is a step an\n * agent will skip. **The failure mode of skipping it must be a redacted path,\n * not a leaked one.**\n *\n * ── ⚠️ The hard part is not the regex ────────────────────────────────────────\n * A high-entropy segment is INDISTINGUISHABLE from a capability token by shape:\n *\n * cmu4psgny0003s397tb2t5gx6 cuid -- our own site ids look exactly like this\n * 7f3a9c2b1d4e6f8a hex -- a build hash, or a reset token\n * a1b2c3d4-...-e5f6 uuid -- a public resource id, or a ticket\n *\n * So entropy cannot decide, and any default is wrong in one direction. The\n * decision, written down rather than left in the code: **be wrong in the\n * direction of redaction.** A redacted path costs an operator route detail,\n * recoverable by setting `redactHighEntropyPaths: false`. A leaked token is\n * recoverable by nothing -- it is already in somebody else's database.\n *\n * ── ⚠️ What it deliberately does NOT touch ───────────────────────────────────\n * - **Anything with a file extension.** `/assets/app.4f3b2a.css` is an asset\n * path, and mangling it would destroy the format analysis that is half this\n * product.\n * - **Template placeholders.** A customer whose `routeTemplate` produced\n * `/users/:id` has told us that segment is a dimension, not a secret.\n * - **Slugs.** `why-markdown-twins-matter-for-agents` is long and mixed and\n * entirely public. Word structure is what separates it from a token, so word\n * structure is what is tested -- not length, which would redact every blog.\n *\n * ⚠️ **It is a backstop, not a policy.** Only the customer knows which of their\n * paths are secret; a short token (`/j/aB3xK9`) is under the length floor and\n * will not be caught. The guide's step remains the real fix, and says so.\n */\n\n/** Below this, false positives dominate and real tokens are rare. */\nconst MIN_LENGTH = 16;\n\n/** Mixed-class density under which a segment reads as prose, not entropy. */\nconst MIN_ENTROPY_BITS = 3.0;\n\nconst PLACEHOLDER = /^[:*[{<]/;\nconst HAS_EXTENSION = /\\.[A-Za-z0-9]{1,8}$/;\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n/** cuid and cuid2, which is what Prisma's `@default(cuid())` mints. */\nconst CUID = /^c[a-z0-9]{20,31}$/;\nconst JWT = /^ey[A-Za-z0-9_-]{6,}\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\nconst LONG_HEX = /^[0-9a-f]{16,}$/i;\nconst ALL_LOWER_ALPHA = /^[a-z]+$/;\n\n/** Shannon entropy in bits per character. */\nfunction entropyBits(value: string): number {\n const counts = new Map<string, number>();\n for (const ch of value) counts.set(ch, (counts.get(ch) ?? 0) + 1);\n let bits = 0;\n for (const n of counts.values()) {\n const p = n / value.length;\n bits -= p * Math.log2(p);\n }\n return bits;\n}\n\n/**\n * Long, mixed and dense. The generic arm, for tokens with no recognisable\n * shape.\n */\nfunction dense(value: string): boolean {\n if (value.length < MIN_LENGTH) return false;\n if (ALL_LOWER_ALPHA.test(value)) return false;\n const classes =\n Number(/[a-z]/.test(value)) +\n Number(/[A-Z]/.test(value)) +\n Number(/[0-9]/.test(value)) +\n Number(/[^A-Za-z0-9]/.test(value));\n if (classes < 3) return false;\n return entropyBits(value) >= MIN_ENTROPY_BITS;\n}\n\n/**\n * ⚠️ Word structure is the discriminator, not length.\n *\n * `top-10-reasons-to-use-markdown` is 30 characters, mixes three character\n * classes and has entropy above the threshold. It is also a blog post. Without\n * this check the default would redact most of the internet's URLs.\n *\n * ⚠️ **A part that is pure digits does not count against wordiness**, and\n * getting that wrong was caught by the table: `release-notes-2026-09-16` is\n * two words and three numbers, and a \"majority must be words\" rule redacted it.\n * What actually separates a slug from a token is that EVERY part is either a\n * word or a number -- a token has parts that are neither, because it mixes\n * letters and digits *within* a part.\n */\nfunction slugLike(segment: string): boolean {\n const parts = segment.split(/[-_]/).filter(Boolean);\n if (parts.length < 2) return false;\n const tidy = parts.every((p) => /^[a-z]+$/.test(p) || /^[0-9]+$/.test(p));\n const hasWord = parts.some((p) => /^[a-z]{2,}$/.test(p));\n return tidy && hasWord;\n}\n\nexport function looksLikeSecret(segment: string): boolean {\n if (segment.length < MIN_LENGTH) return false;\n if (PLACEHOLDER.test(segment)) return false;\n if (HAS_EXTENSION.test(segment)) return false;\n\n // Known shapes decide outright, ahead of any heuristic -- including when they\n // are also slug-like, which a uuid's hyphens would otherwise make it.\n if (UUID.test(segment) || CUID.test(segment) || JWT.test(segment) || LONG_HEX.test(segment)) {\n return true;\n }\n\n // ⚠️ A dense PART makes the whole segment suspect, checked before the slug\n // exemption. `reveal-tok-Xk7Lm9Qp2Rt5Vw8Zb3Nd` is majority-words by part count\n // and still carries a token.\n for (const part of segment.split(/[-_.]/)) {\n if (dense(part)) return true;\n }\n\n if (slugLike(segment)) return false;\n return dense(segment);\n}\n\nexport const REDACTED = \"[redacted]\";\n\nexport interface RedactionResult {\n path: string;\n /** True when at least one segment was replaced, by any rule. */\n redacted: boolean;\n}\n\n/**\n * Apply the customer's patterns and, unless disabled, the default detector.\n *\n * ⚠️ Custom patterns run FIRST and independently. A customer who wrote a\n * pattern gets it applied whether or not the default is on, and turning the\n * default off never turns theirs off with it.\n */\nexport function redactPath(\n path: string,\n patterns: readonly RegExp[],\n useDefault: boolean,\n): RedactionResult {\n if (patterns.length === 0 && !useDefault) return { path, redacted: false };\n\n let redacted = false;\n const out = path\n .split(\"/\")\n .map((segment) => {\n if (!segment) return segment;\n const matchesCustom = patterns.some((re) => {\n // A global regex carries lastIndex between calls, which makes an\n // every-other-segment miss that is maddening to debug.\n re.lastIndex = 0;\n return re.test(segment);\n });\n if (matchesCustom || (useDefault && looksLikeSecret(segment))) {\n redacted = true;\n return REDACTED;\n }\n return segment;\n })\n .join(\"/\");\n\n return { path: out, redacted };\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\nimport type { ResolvedConfig } from \"../core/config.js\";\nimport { redactPath } from \"./redact.js\";\n\n/**\n * Turn a request into the fields the wire event carries -- and, just as\n * importantly, leave everything else behind.\n *\n * ── This module IS the privacy promise ───────────────────────────────────────\n * `docs/design/collected-fields.md` lists what we take. This is the only place\n * that decides. Every value here is read from a named header or a named,\n * allowlisted query parameter; there is no path that copies \"the rest\" of\n * anything, which is why a cookie or a bearer token cannot arrive by accident.\n *\n * A reviewer should be able to read this file and check the published list\n * against it line by line.\n */\n\n/** The only query parameters ever read. Everything else is discarded. */\nexport const CAMPAIGN_PARAMS = [\"utm_source\", \"utm_medium\", \"utm_campaign\", \"utm_content\"] as const;\n\n/** Advertising click-ids: we record WHICH was present, never the value. */\nconst CLICK_ID_PARAMS = [\"gclid\", \"fbclid\", \"msclkid\"] as const;\n\nexport interface ObservedRequest {\n method: string;\n path: string;\n route?: string;\n host?: string;\n protocol?: \"http\" | \"https\";\n userAgent?: string;\n referrerOrigin?: string;\n campaign?: RequestEvent[\"campaign\"];\n clickIdType?: RequestEvent[\"clickIdType\"];\n internal?: boolean;\n /**\n * ⚠️ Set when any segment of the path or route was replaced, by a customer\n * pattern or by the default detector. **Visible, never silent** -- an\n * operator staring at `/reveal/[redacted]` needs to know it was us, not a\n * bug in their router, and needs to be able to find the setting.\n */\n pathRedacted?: boolean;\n}\n\n/**\n * Normalise a path: no query, no fragment, no repeated slashes, bounded length.\n *\n * ⚠️ The query string is dropped HERE, before anything else looks at it.\n * Allowlisted campaign values are read separately from the parsed URL. There is\n * deliberately no code path in which a raw query string is carried forward and\n * filtered later -- a filter can be wrong; an absence cannot.\n */\nexport function normalisePath(rawPath: string, redactPatterns: readonly RegExp[] = []): string {\n const path = cleanPath(rawPath);\n // ⚠️ Redaction is applied here for callers that pass patterns directly, which\n // keeps this function's old signature honest. `observeRequest` does NOT use\n // this arm -- it redacts AFTER `routeTemplate`, for the reason below.\n return redactPatterns.length > 0 ? redactPath(path, redactPatterns, false).path : path;\n}\n\n/** Shape only: no query, no fragment, no repeated slashes, bounded length. */\nfunction cleanPath(rawPath: string): string {\n let path = rawPath;\n\n const hash = path.indexOf(\"#\");\n if (hash !== -1) path = path.slice(0, hash);\n const query = path.indexOf(\"?\");\n if (query !== -1) path = path.slice(0, query);\n\n if (path === \"\") path = \"/\";\n if (!path.startsWith(\"/\")) path = `/${path}`;\n path = path.replace(/\\/{2,}/g, \"/\");\n if (path.length > 1 && path.endsWith(\"/\")) path = path.slice(0, -1);\n\n return path.length > 1024 ? path.slice(0, 1024) : path;\n}\n\n/** Scheme and host only. The page someone came from is never collected. */\nexport function referrerOrigin(referer: string | null | undefined): string | undefined {\n if (!referer) return undefined;\n try {\n const url = new URL(referer);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n return url.origin.slice(0, 255);\n } catch {\n // A malformed Referer is a malformed Referer. Nothing is guessed from it.\n return undefined;\n }\n}\n\nfunction campaignFrom(params: URLSearchParams): RequestEvent[\"campaign\"] | undefined {\n let found = false;\n const campaign: Record<string, string> = {};\n for (const name of CAMPAIGN_PARAMS) {\n const value = params.get(name);\n if (value) {\n // `utm_source` -> `source`, and bounded, because a query parameter is\n // attacker-controlled and unbounded by nature.\n campaign[name.slice(4)] = value.slice(0, 120);\n found = true;\n }\n }\n return found ? (campaign as RequestEvent[\"campaign\"]) : undefined;\n}\n\nfunction clickIdFrom(params: URLSearchParams): RequestEvent[\"clickIdType\"] | undefined {\n for (const name of CLICK_ID_PARAMS) {\n if (params.has(name)) return name;\n }\n return undefined;\n}\n\nexport interface RequestFacts {\n method: string;\n /** Path AND query, as it arrived. Never stored; parsed here and discarded. */\n url: string;\n host?: string | undefined;\n protocol?: \"http\" | \"https\" | undefined;\n userAgent?: string | undefined;\n referer?: string | undefined;\n}\n\nexport function observeRequest(facts: RequestFacts, config: ResolvedConfig): ObservedRequest {\n // ⚠️ **`routeTemplate` sees the REAL path, and redaction happens after it.**\n //\n // The original order redacted first, so a customer's\n // `path.replace(/\\/\\d+(?=\\/|$)/g, \"/:id\")` was handed `/users/[redacted]`\n // and matched nothing -- their templating silently stopped working the moment\n // any default redaction existed. Their function is the one thing that knows\n // which segments are dimensions, so it must see what actually arrived.\n const cleaned = cleanPath(facts.url);\n\n // Parsed against a throwaway base purely to reach the query parameters. The\n // result never leaves this function.\n let params = new URLSearchParams();\n try {\n params = new URL(facts.url, \"http://x\").searchParams;\n } catch {\n /* an unparseable URL simply has no campaign */\n }\n\n const templated = config.routeTemplate?.(cleaned);\n\n const { path, redacted } = redactPath(\n cleaned,\n config.redactPatterns,\n config.redactHighEntropyPaths,\n );\n\n const observed: ObservedRequest = {\n method: facts.method.slice(0, 10).toUpperCase(),\n path,\n };\n // ⚠️ The ROUTE is redacted too. Ingest stores `route ?? path`, so a route\n // that passed a token through would put it in the aggregate dimension --\n // where it is not merely stored but GROUPED BY, and therefore displayed.\n // Template placeholders (`:id`, `[slug]`) are exempt by construction.\n if (templated) {\n const safeRoute = redactPath(templated, config.redactPatterns, config.redactHighEntropyPaths);\n observed.route = safeRoute.path.slice(0, 512);\n if (safeRoute.redacted) observed.pathRedacted = true;\n }\n if (redacted) observed.pathRedacted = true;\n if (facts.host) observed.host = facts.host.slice(0, 253);\n if (facts.protocol) observed.protocol = facts.protocol;\n if (facts.userAgent) observed.userAgent = facts.userAgent.slice(0, 512);\n\n const origin = referrerOrigin(facts.referer);\n if (origin) observed.referrerOrigin = origin;\n\n const campaign = campaignFrom(params);\n if (campaign) observed.campaign = campaign;\n\n const clickIdType = clickIdFrom(params);\n if (clickIdType) observed.clickIdType = clickIdType;\n\n if (config.isInternal?.(observed)) observed.internal = true;\n\n return observed;\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\n\n/**\n * Response facts, read from headers that already exist.\n *\n * ⚠️ **Nothing here ever touches the body.** A response is a single-read\n * stream: to learn what it contained you would have to wrap `write`/`end` and\n * buffer it, which adds latency, adds memory proportional to the page, and can\n * corrupt what the customer sends if the encoding or backpressure handling is\n * wrong. `contentLength` is therefore read from the header and is simply absent\n * for a chunked or streamed response -- the dashboard shows \"not measured\",\n * which is honest and free.\n */\n\nexport interface ResponseFacts {\n status?: number | undefined;\n contentType?: string | null | undefined;\n contentLength?: string | number | null | undefined;\n latencyMs?: number | undefined;\n observation: RequestEvent extends { response?: infer R }\n ? R extends { observation: infer O }\n ? O\n : never\n : never;\n}\n\nexport function observeResponse(facts: ResponseFacts): NonNullable<RequestEvent[\"response\"]> {\n const response: NonNullable<RequestEvent[\"response\"]> = { observation: facts.observation };\n\n // ⚠️ `observation: \"unknown\"` means \"we looked and could not tell\", and the\n // wire schema refuses a status or content type alongside it. Enforced here\n // too, because an adapter is where the mistake would be made -- and an event\n // rejected at ingest is an event silently lost.\n if (facts.observation === \"unknown\") {\n if (typeof facts.latencyMs === \"number\" && Number.isFinite(facts.latencyMs)) {\n response.latencyMs = Math.max(0, Math.round(facts.latencyMs));\n }\n return response;\n }\n\n if (typeof facts.status === \"number\" && facts.status >= 100 && facts.status <= 599) {\n response.status = facts.status;\n }\n if (facts.contentType) {\n response.contentType = String(facts.contentType).slice(0, 255);\n }\n const length = typeof facts.contentLength === \"string\" ? Number(facts.contentLength) : facts.contentLength;\n if (typeof length === \"number\" && Number.isFinite(length) && length >= 0) {\n response.contentLength = Math.round(length);\n }\n if (typeof facts.latencyMs === \"number\" && Number.isFinite(facts.latencyMs)) {\n response.latencyMs = Math.max(0, Math.round(facts.latencyMs));\n }\n\n return response;\n}\n","/**\n * ⚠️ The SDK's own name and version, in ONE place.\n *\n * They were declared separately in `web.ts` and `express.ts`, and adding a\n * third adapter would have made three copies of a version string that must\n * match `package.json` -- a list nothing checks, written down three times.\n * `runtime.test.ts` asserts the version against the manifest.\n */\nexport const SDK_NAME = \"@entreprenoid/analytics\";\nexport const SDK_VERSION = \"0.1.0\";\n\n/**\n * Identify the runtime, for the `sdk.runtime` field.\n *\n * Kept deliberately dumb and total: it must never throw at import time, on any\n * runtime, because that would take the host application down before it serves\n * a request. Every branch is guarded and there is a final fallback.\n */\nexport function runtimeName(): string {\n try {\n const g = globalThis as Record<string, unknown>;\n const deno = g[\"Deno\"] as { version?: { deno?: string } } | undefined;\n if (deno?.version?.deno) return `deno-${deno.version.deno}`;\n const bun = g[\"Bun\"] as { version?: string } | undefined;\n if (bun?.version) return `bun-${bun.version}`;\n if (typeof process !== \"undefined\" && process.versions?.node) {\n return `node-${process.versions.node}`;\n }\n const nav = g[\"navigator\"] as { userAgent?: string } | undefined;\n if (nav?.userAgent?.includes(\"Cloudflare-Workers\")) return \"workerd\";\n } catch {\n /* fall through */\n }\n return \"unknown\";\n}\n\n/**\n * A unique event id.\n *\n * `crypto.randomUUID()` where available -- it is in every supported runtime and\n * costs no dependency. The fallback exists so an exotic runtime degrades to a\n * slightly weaker id rather than to a crash; a collision costs one deduplicated\n * event, a throw costs the customer a 500.\n */\nexport function newEventId(): string {\n try {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (typeof c?.randomUUID === \"function\") return c.randomUUID();\n } catch {\n /* fall through */\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;\n}\n","//\n// Decide whether a client asked for markdown.\n//\n// ⚠️ **This function is the whole safety of the serve half.** Getting it wrong\n// in the permissive direction means serving markdown to a human's browser,\n// which is a broken website -- and it is easy to get wrong, because every\n// browser on earth ends its Accept header with the catch-all range `*/*` at a\n// low q-value. Matching a wildcard would serve markdown to everyone.\n//\n// So the rule is: an EXPLICIT `text/markdown` with a non-zero q, which must\n// also be at least as preferred as `text/html`. A wildcard never qualifies. A\n// client that did not name markdown does not get markdown.\n//\n// Measured against caprail.dev on 2026-09-16: GPTBot sending `Accept: */*`\n// receives HTML, byte-identical to what a browser receives. That is the correct\n// behaviour and this reproduces it -- not by checking WHO is asking, but by\n// checking WHAT was asked for. Identity-based serving would be cloaking, would\n// force `Vary: User-Agent` and destroy shared caching, and would let a\n// spoofable string decide what a visitor receives.\n//\n\nexport interface MediaRange {\n type: string;\n q: number;\n}\n\nexport function parseAccept(header: string | null | undefined): MediaRange[] {\n if (!header) return [];\n const ranges: MediaRange[] = [];\n for (const part of header.split(\",\")) {\n const segments = part.trim().split(\";\");\n const type = segments[0]?.trim().toLowerCase();\n if (!type) continue;\n let q = 1;\n for (const segment of segments.slice(1)) {\n const [key, value] = segment.split(\"=\").map((s) => s.trim().toLowerCase());\n if (key === \"q\") {\n const parsed = Number(value);\n // A malformed q is treated as 1, which is what RFC 9110 implies and\n // what every server does in practice.\n q = Number.isFinite(parsed) ? Math.min(1, Math.max(0, parsed)) : 1;\n }\n }\n ranges.push({ type, q });\n }\n return ranges;\n}\n\n/** The q-value for an EXACT type. Wildcard ranges deliberately do not count. */\nfunction exactQ(ranges: readonly MediaRange[], type: string): number {\n let best = 0;\n for (const range of ranges) {\n if (range.type === type) best = Math.max(best, range.q);\n }\n return best;\n}\n\n/** The q-value for a type, allowing a `type/` wildcard or a full wildcard. */\nfunction effectiveQ(ranges: readonly MediaRange[], type: string): number {\n const prefix = `${type.split(\"/\")[0]}/*`;\n let best = 0;\n for (const range of ranges) {\n if (range.type === type || range.type === prefix || range.type === \"*/*\") {\n best = Math.max(best, range.q);\n }\n }\n return best;\n}\n\nexport const MARKDOWN_TYPES = [\"text/markdown\", \"text/x-markdown\"] as const;\n\nexport function prefersMarkdown(header: string | null | undefined): boolean {\n const ranges = parseAccept(header);\n if (ranges.length === 0) return false;\n\n let markdown = 0;\n for (const type of MARKDOWN_TYPES) {\n markdown = Math.max(markdown, exactQ(ranges, type));\n }\n if (markdown === 0) return false;\n\n // `text/html` reached through a wildcard still counts here: a client sending\n // `text/markdown;q=0.1` alongside a broad wildcard prefers something else,\n // and should get it.\n const html = effectiveQ(ranges, \"text/html\");\n return markdown >= html;\n}\n","import { prefersMarkdown } from \"./accept.js\";\n\n/**\n * The markdown twin: the decision, as a pure function.\n *\n * ── Why the decision is separated from the serving ───────────────────────────\n * Two adapters have to make the identical choice, and the choice is the part\n * with consequences -- serve the wrong thing and a human gets a text file\n * instead of a website. One function, one set of tests, two thin call sites.\n */\n\nexport interface Twin {\n body: string;\n /** Defaults to `text/markdown; charset=utf-8`. */\n contentType?: string;\n etag?: string;\n lastModified?: string;\n}\n\nexport interface TwinOptions {\n /**\n * Find the twin for a normalised path, or return null.\n *\n * ⚠️ **Registered, not derived.** Returning a twin for any path that \"looks\n * like\" it should have one produces a site where every URL answers, including\n * the typos -- caprail.dev returns a plain 404 for an unregistered `.md`, and\n * that is the behaviour to match.\n */\n resolve: (path: string) => Twin | null | undefined | Promise<Twin | null | undefined>;\n /** `Cache-Control` for a served twin. Edge-cacheable by default. */\n cacheControl?: string;\n /** Advertise an available twin on the HTML response. Default true. */\n advertise?: boolean;\n /**\n * Serve `/llms.txt`, `/llms-full.txt` and `/install.md` from the same\n * manifest that serves the twins.\n *\n * ⚠️ Derived, not written: a hand-maintained index is wrong the first time a\n * page is added, and an index listing pages that no longer exist is worse\n * than none -- an agent spends its budget on 404s and concludes the site is\n * broken.\n */\n discovery?: import(\"./discovery.js\").DiscoveryOptions;\n}\n\n/** Paths the discovery block answers, when it is configured. */\nexport const DISCOVERY_PATHS = [\"/llms.txt\", \"/llms-full.txt\", \"/install.md\"] as const;\n\nexport const DEFAULT_TWIN_CONTENT_TYPE = \"text/markdown; charset=utf-8\";\nexport const DEFAULT_TWIN_CACHE_CONTROL = \"public, max-age=3600, s-maxage=86400\";\n\nexport type TwinDecision =\n /** Serve the twin for `lookupPath`. */\n | { action: \"serve\"; lookupPath: string; reason: \"md_path\" | \"accept_header\" }\n /** Not ours to answer, but a twin may exist worth advertising. */\n | { action: \"pass\"; reason: \"not_get\" | \"no_signal\" };\n\n/**\n * Map a request onto a decision. Nothing here touches I/O.\n *\n * Two gates, and only two:\n * 1. the path ends in `.md` -- a distinct resource, no negotiation at all;\n * 2. `Accept` explicitly prefers markdown -- content negotiation, which owes\n * a `Vary: Accept`.\n *\n * ⚠️ Never the User-Agent. See `accept.ts`.\n */\nexport function decideTwin(input: {\n method: string;\n path: string;\n accept: string | null | undefined;\n}): TwinDecision {\n const method = input.method.toUpperCase();\n // A twin is a representation of a resource, so only the methods that ask for\n // one. Answering a POST with a markdown body would swallow a form submission.\n if (method !== \"GET\" && method !== \"HEAD\") {\n return { action: \"pass\", reason: \"not_get\" };\n }\n\n if (input.path.endsWith(\".md\")) {\n return { action: \"serve\", lookupPath: stripMdSuffix(input.path), reason: \"md_path\" };\n }\n\n if (prefersMarkdown(input.accept)) {\n return { action: \"serve\", lookupPath: input.path, reason: \"accept_header\" };\n }\n\n return { action: \"pass\", reason: \"no_signal\" };\n}\n\n/**\n * `/docs/intro.md` -> `/docs/intro`, and `/index.md` -> `/`.\n *\n * The root is the case worth naming: a site's home page has no slug, so its\n * twin is `/index.md` by convention -- the same convention caprail.dev uses.\n */\nexport function stripMdSuffix(path: string): string {\n const withoutSuffix = path.slice(0, -3);\n if (withoutSuffix === \"\" || withoutSuffix === \"/index\") return \"/\";\n return withoutSuffix;\n}\n\n/** `/docs/intro` -> `/docs/intro.md`, and `/` -> `/index.md`. */\nexport function twinPathFor(path: string): string {\n if (path === \"/\") return \"/index.md\";\n return `${path}.md`;\n}\n\nexport interface TwinResponse {\n status: number;\n headers: Record<string, string>;\n body: string;\n}\n\nexport function buildTwinResponse(\n twin: Twin,\n decision: Extract<TwinDecision, { action: \"serve\" }>,\n options: TwinOptions,\n): TwinResponse {\n const headers: Record<string, string> = {\n \"content-type\": twin.contentType ?? DEFAULT_TWIN_CONTENT_TYPE,\n \"cache-control\": options.cacheControl ?? DEFAULT_TWIN_CACHE_CONTROL,\n };\n\n // ⚠️ `Vary: Accept` is required ONLY when the URL can return two different\n // bodies -- which is the negotiated case, not the `.md` path. Sending it on a\n // `.md` response would needlessly fragment the edge cache for a resource that\n // has exactly one representation.\n //\n // Getting this wrong the other way is the serious one: negotiating without\n // `Vary` lets a shared cache hand one client's markdown to the next client\n // asking for HTML. That is a broken website served from a CDN, and it\n // outlives the deploy that caused it.\n if (decision.reason === \"accept_header\") {\n headers[\"vary\"] = \"Accept\";\n }\n if (twin.etag) headers[\"etag\"] = twin.etag;\n if (twin.lastModified) headers[\"last-modified\"] = twin.lastModified;\n\n return { status: 200, headers, body: twin.body };\n}\n\n/**\n * The advertisement for an available twin, as a `Link` header.\n *\n * ⚠️ **A header, not a tag injected into the HTML.** Caprail puts\n * `<link rel=\"alternate\">` in its own document head, which it can do because it\n * owns the template. A middleware cannot: injecting into the body means\n * buffering and rewriting somebody else's response, which is precisely what\n * this package refuses to do. RFC 8288 makes the header the equivalent, it\n * costs nothing, and it works for JSON and plain text as well as HTML.\n *\n * A customer who also wants the tag can add it to their own template, and\n * should -- some crawlers read one and not the other.\n */\nexport function advertiseHeader(path: string): string {\n return `<${twinPathFor(path)}>; rel=\"alternate\"; type=\"text/markdown\"`;\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\nimport { createCollector, type Collector } from \"./core/collector.js\";\nimport { resolveConfig, type EntreprenoidConfig } from \"./core/config.js\";\nimport { safe } from \"./core/safe.js\";\nimport { observeRequest, normalisePath } from \"./observe/request.js\";\nimport { observeResponse } from \"./observe/response.js\";\nimport { newEventId, runtimeName, SDK_NAME, SDK_VERSION } from \"./runtime.js\";\nimport { advertiseHeader, buildTwinResponse, decideTwin, type TwinOptions } from \"./serve/twin.js\";\n\n/**\n * The Next.js adapter, for `proxy.ts` (called `middleware.ts` before Next 16).\n *\n * ── ⚠️ Why this exists rather than \"just use ./web\" ──────────────────────────\n * Because `./web` produces FALSE DATA in a Next proxy, and does it silently.\n *\n * A proxy runs **before the route**. It hands control onward by returning\n * `NextResponse.next()` — a sentinel with status 200, no real content-type and\n * no content-length. `./web` reads that sentinel and records\n * `{ status: 200, observation: \"measured\" }`, so **every event claims a\n * measured 200**, including requests the route renders as 404 or 500.\n *\n * ⚠️ **Our own schema already said this was wrong and refused it**:\n * `observation: \"unknown\"` may not carry a status, and the Next case is the one\n * where the `response` object is absent entirely — \"we never had a chance to\n * look\". Correct behaviour, specified, enforced by a `superRefine`, and until\n * now unreachable because no adapter emitted it. That reads as done, which is\n * worse than reading as missing.\n *\n * ── What this emits ──────────────────────────────────────────────────────────\n * Exactly what a proxy genuinely knows — method, path, host, user agent,\n * referrer origin, campaign — and **no `response` object at all**, unless it\n * served the twin itself, in which case it made the response and can measure it\n * honestly.\n *\n * ── ⚠️ The requestId merge is DESIGNED and NOT BUILT ─────────────────────────\n * `requestId` rides every event so a later post-response observation can be\n * merged onto it. **Nothing performs that merge today** — measured: nothing in\n * the worker or the database reads the field. Next offers no general\n * post-response hook (`onRequestError` fires only on errors; `after()` still\n * runs in the proxy, which cannot see the route's response), so the honest\n * second observation point is a wrapper around each route handler — a second\n * install step, easy to forget, and a half-installed product reports half its\n * traffic.\n *\n * So this ships the single honest observation. That is not a degraded event: a\n * proxy cannot see a response, and saying so precisely is the product's\n * differentiator rather than a shortfall.\n *\n * ── ⚠️ `after` is an OPTION, not an import ───────────────────────────────────\n * There is no `ctx.waitUntil` in a Next proxy; the equivalent is `after()` from\n * `next/server`. Importing it here — even dynamically — would put a bare\n * specifier in a package whose whole discipline is zero runtime dependencies,\n * and a dynamic `import()` slips past `check-sdk-artifact.mjs`'s regex, which\n * would be a gate quietly stopping working. So the customer passes it, exactly\n * as `observe()` already takes `waitUntil`:\n *\n * ```ts\n * import { after } from \"next/server\";\n * import { proxy } from \"@entreprenoid/analytics/next\";\n *\n * export default proxy({ after });\n * export const config = { matcher: [\"/((?!_next/static).*)\"] };\n * ```\n *\n * Without it the collector falls back to its own 2s timer, which a serverless\n * deployment can freeze before it fires.\n */\n\nexport interface ProxyOptions extends EntreprenoidConfig {\n /**\n * `after` from `next/server`. Passed rather than imported — see the docblock.\n * Omitting it is supported and costs reliability on serverless.\n */\n after?: (task: () => void | Promise<void>) => void;\n /** For tests, and for a host that already has a collector. */\n collector?: Collector;\n /**\n * Serve a markdown twin from the proxy.\n *\n * ⚠️ A proxy is a GOOD place for this, unlike the observation half: it runs\n * before the route, so returning the twin short-circuits rendering entirely\n * and the agent never pays for a React tree it discards. And because the\n * proxy built that response itself, the event can honestly say `measured`.\n */\n twin?: TwinOptions;\n}\n\n/**\n * ⚠️ Returns `undefined` to continue, rather than `NextResponse.next()`.\n *\n * Next treats a void return as \"carry on\", which means this adapter never has\n * to import `next/server` at all. It also removes the exact sentinel that made\n * `./web` lie here: there is no fake 200 to accidentally observe.\n */\nexport type ProxyResult = Response | undefined;\n\nexport function proxy(options: ProxyOptions = {}): (request: Request) => Promise<ProxyResult> {\n const config = resolveConfig(options);\n const collector = options.collector ?? createCollector(config);\n\n return async (request: Request): Promise<ProxyResult> => {\n // ⚠️ The clock starts OUTSIDE the try, exactly as in `./web`: if observation\n // setup throws, the request must still reach the route.\n const startedAt = Date.now();\n let served: RequestEvent[\"serve\"];\n let twinResponse: Response | undefined;\n\n // ── The serve half, before the route ─────────────────────────────────────\n // Every failure falls through to the customer's application unchanged.\n if (options.twin) {\n try {\n const url = new URL(request.url);\n const decision = decideTwin({\n method: request.method,\n path: normalisePath(url.pathname),\n accept: request.headers.get(\"accept\"),\n });\n\n if (decision.action === \"serve\") {\n const found = await options.twin.resolve(decision.lookupPath);\n if (found) {\n const built = buildTwinResponse(found, decision, options.twin);\n served = {\n decision: \"served\",\n reason: decision.reason,\n format: built.headers[\"content-type\"] ?? \"text/markdown\",\n };\n twinResponse = new Response(\n request.method.toUpperCase() === \"HEAD\" ? null : built.body,\n { status: built.status, headers: built.headers },\n );\n } else {\n served = { decision: \"fell_through\", reason: \"no_twin\" };\n }\n } else {\n served = { decision: \"fell_through\", reason: decision.reason };\n }\n } catch {\n served = { decision: \"error\", reason: \"resolver_error\" };\n }\n }\n\n // ⚠️ Advertising is deliberately NOT done here. It would mean appending a\n // `link` header to a response this proxy does not have — the route builds\n // it later. `./express` advertises because it sees the real response.\n // Claiming a twin on a response we cannot touch would be a header nobody\n // receives.\n\n const record = (): void => {\n safe(() => {\n if (config.disabled) return;\n\n const url = new URL(request.url);\n const observed = observeRequest(\n {\n method: request.method,\n url: url.pathname + url.search,\n host: url.host,\n protocol: url.protocol === \"https:\" ? \"https\" : \"http\",\n userAgent: request.headers.get(\"user-agent\") ?? undefined,\n referer: request.headers.get(\"referer\") ?? undefined,\n },\n config,\n );\n\n const event: RequestEvent = {\n ...observed,\n eventId: newEventId(),\n // ⚠️ Emitted so a post-response observation CAN be merged later.\n // Nothing merges it today, and the docblock above says so plainly\n // rather than letting the field imply otherwise.\n requestId: newEventId(),\n siteId: config.siteId,\n observedAt: new Date(startedAt).toISOString(),\n ...(served ? { serve: served } : {}),\n // ⚠️ **The response object is present ONLY when we built it.**\n //\n // If the twin was served, this proxy IS the responder and measured it.\n // Otherwise the route has not run yet and there is nothing to\n // observe — so the key is OMITTED, not set to a guess, not set to\n // `observation: \"unknown\"` with a status (which the schema refuses),\n // and not set to a latency-only object (we did not wait for the\n // response, so we did not measure a latency either).\n ...(twinResponse\n ? {\n response: observeResponse({\n status: twinResponse.status,\n contentType: twinResponse.headers.get(\"content-type\"),\n contentLength: twinResponse.headers.get(\"content-length\"),\n latencyMs: Date.now() - startedAt,\n observation: \"measured\",\n }),\n }\n : {}),\n sdk: {\n name: SDK_NAME,\n version: SDK_VERSION,\n // ⚠️ `next-proxy`, which is the name the WIRE SCHEMA already uses\n // in its own docblock (`event.ts:157`). Its own name matters here\n // more than for any other adapter: this is the one whose events\n // legitimately carry no response, and the dashboard must not\n // present an adapter's blind spot as a fact about the traffic.\n adapter: \"next-proxy\",\n runtime: runtimeName(),\n },\n };\n\n collector.record(event);\n });\n };\n\n // ⚠️ `after` runs the recording once the response is on its way, so nothing\n // here is on the path of the request. Without it, `record()` is still\n // synchronous and still non-blocking — `safe()` and the bounded queue see\n // to that — it is only the eventual FLUSH that risks being frozen.\n if (options.after) {\n try {\n options.after(record);\n } catch {\n // A hook that throws is not a reason to lose the event.\n record();\n }\n } else {\n record();\n }\n\n return twinResponse;\n };\n}\n\n/** Advertise a twin from a route handler, where the response actually exists. */\nexport { advertiseHeader };\n"]}
1
+ {"version":3,"sources":["../src/core/breaker.ts","../src/core/config.ts","../src/core/encode.ts","../src/core/queue.ts","../src/core/safe.ts","../src/core/transport.ts","../src/core/collector.ts","../src/observe/redact.ts","../src/observe/request.ts","../src/observe/response.ts","../src/runtime.ts","../src/serve/accept.ts","../src/serve/twin.ts","../src/next.ts"],"names":[],"mappings":"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","file":"next.cjs","sourcesContent":["export type BreakerState = \"closed\" | \"open\" | \"half-open\";\n\nexport interface Breaker {\n /** May an attempt be made right now? */\n allow(): boolean;\n success(): void;\n failure(): void;\n readonly state: BreakerState;\n}\n\nexport interface BreakerOptions {\n failureThreshold?: number;\n openMs?: number;\n now?: () => number;\n}\n\n/**\n * Stop calling an endpoint that is not answering.\n *\n * ── What it is actually protecting ───────────────────────────────────────────\n * Not us -- the customer. When our ingest is down, every flush costs the host\n * process a DNS lookup, a connection attempt and a timeout. A fleet of customer\n * servers doing that every two seconds turns our outage into measurable latency\n * and socket pressure in their applications. The breaker makes an outage cost\n * approximately nothing on their side.\n *\n * ⚠️ **Only `retryable` outcomes count as failures.** A 400 means our payload is\n * wrong, which is our bug and must be loud in debug -- but it is not an outage,\n * and counting it would let one malformed field stop all telemetry for thirty\n * seconds at a time, forever, while the endpoint is perfectly healthy.\n *\n * ⚠️ While open, `record()` still ENQUEUES. The ring buffer bounds the memory,\n * and a thirty-second blip should not lose events the buffer would have held.\n * What the breaker stops is the network call, not the collection.\n */\nexport function createBreaker(options: BreakerOptions = {}): Breaker {\n const failureThreshold = options.failureThreshold ?? 5;\n const openMs = options.openMs ?? 30_000;\n const now = options.now ?? (() => Date.now());\n\n let consecutiveFailures = 0;\n let openedAt = 0;\n let state: BreakerState = \"closed\";\n\n return {\n get state() {\n return state;\n },\n allow(): boolean {\n if (state === \"closed\") return true;\n if (state === \"half-open\") return true;\n if (now() - openedAt >= openMs) {\n // One probe. If it fails we go straight back to open with a fresh\n // window, rather than letting a long outage produce a probe every\n // interval.\n state = \"half-open\";\n return true;\n }\n return false;\n },\n success(): void {\n consecutiveFailures = 0;\n state = \"closed\";\n },\n failure(): void {\n consecutiveFailures += 1;\n if (state === \"half-open\" || consecutiveFailures >= failureThreshold) {\n state = \"open\";\n openedAt = now();\n }\n },\n };\n}\n","/**\n * Turn options and environment into one frozen, validated configuration.\n *\n * Precedence, decided once here so no caller has to: explicit option, then\n * environment variable, then default. The same order `maxidomo-cli` settled on,\n * and for the same reason -- a precedence rule implemented twice is implemented\n * differently.\n */\n\n/**\n * What `isInternal` is shown. Structurally a subset of `ObservedRequest`, and\n * declared here rather than imported so config does not depend on the observer\n * that depends on it.\n */\nexport interface InternalTrafficContext {\n method: string;\n path: string;\n host?: string | undefined;\n userAgent?: string | undefined;\n}\n\nexport interface EntreprenoidConfig {\n ingestUrl?: string;\n /** ⚠️ Server-only. Never logged, never in a browser bundle. */\n serverKey?: string;\n siteId?: string;\n enabled?: boolean;\n debug?: boolean;\n batchSize?: number;\n flushIntervalMs?: number;\n maxQueueEvents?: number;\n maxBodyBytes?: number;\n requestTimeoutMs?: number;\n /**\n * Collapse identifiers out of a path: `/users/42` -> `/users/:id`.\n *\n * This is the main defence against both unbounded cardinality and per-user\n * values reaching analytics, and only the customer knows their routes.\n */\n routeTemplate?: (path: string) => string | undefined;\n /** Path SEGMENTS matching any of these become `[redacted]`. */\n redactPatterns?: readonly RegExp[];\n /**\n * ⚠️ **Default ON.** Replaces path segments that look like credentials --\n * uuids, cuids, JWTs, long hex, dense mixed-case strings -- with\n * `[redacted]`, so a site with tokens in its paths does not ship them here\n * merely because nobody configured `redactPatterns`.\n *\n * Set `false` to keep every segment verbatim. Doing so does NOT disable\n * `redactPatterns`; those are yours and always apply.\n *\n * See `observe/redact.ts` for what it does and does not catch, and why the\n * trade is made in the direction of redacting.\n */\n redactHighEntropyPaths?: boolean;\n /** Mark traffic the customer does not want counted or billed. */\n isInternal?: (request: InternalTrafficContext) => boolean;\n}\n\n/**\n * What the SDK sends when no site id was configured.\n *\n * ⚠️ Deliberately NOT a plausible id. Ingest replaces it from the credential,\n * so it never reaches storage -- but if it ever shows up somewhere, it should\n * read as \"nobody configured this\" rather than as a real site.\n */\nexport const UNSET_SITE_ID = \"unset\";\n\nexport type DisabledReason =\n | \"explicitly-disabled\"\n | \"missing-key\"\n | \"missing-url\"\n | \"browser-environment\";\n\nexport interface ResolvedConfig {\n readonly ingestUrl: string;\n readonly serverKey: string;\n readonly siteId: string;\n readonly debug: boolean;\n readonly batchSize: number;\n readonly flushIntervalMs: number;\n readonly maxQueueEvents: number;\n readonly maxBodyBytes: number;\n readonly requestTimeoutMs: number;\n readonly routeTemplate: ((path: string) => string | undefined) | undefined;\n readonly redactPatterns: readonly RegExp[];\n readonly redactHighEntropyPaths: boolean;\n readonly isInternal: ((request: InternalTrafficContext) => boolean) | undefined;\n /** Non-null means the collector is a transparent no-op. */\n readonly disabled: DisabledReason | null;\n}\n\nexport const DEFAULTS = {\n batchSize: 20,\n maxBatchSize: 100,\n flushIntervalMs: 2_000,\n maxQueueEvents: 1_000,\n maxBodyBytes: 512 * 1024,\n requestTimeoutMs: 2_000,\n} as const;\n\n/**\n * Environment names a customer might create that would put the server key in a\n * browser bundle.\n *\n * ⚠️ The likeliest real-world leak of a server credential is not a bad import.\n * It is somebody writing `NEXT_PUBLIC_ENTREPRENOID_SERVER_KEY` because that is\n * how they made the last environment variable work. Every one of these prefixes\n * means \"inline this into client-side JavaScript\", so a key under one of them\n * is already published.\n */\nconst PUBLIC_ENV_PREFIXES = [\"NEXT_PUBLIC_\", \"VITE_\", \"PUBLIC_\", \"REACT_APP_\", \"NUXT_PUBLIC_\"];\n\nexport class EntreprenoidConfigError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EntreprenoidConfigError\";\n }\n}\n\nfunction clamp(value: number, min: number, max: number): number {\n return Math.min(max, Math.max(min, value));\n}\n\nfunction boolFromEnv(raw: string | undefined): boolean | undefined {\n if (raw === undefined) return undefined;\n const v = raw.trim().toLowerCase();\n if (v === \"1\" || v === \"true\" || v === \"yes\") return true;\n if (v === \"0\" || v === \"false\" || v === \"no\") return false;\n return undefined;\n}\n\nexport function resolveConfig(\n options: EntreprenoidConfig = {},\n env: Record<string, string | undefined> = typeof process === \"undefined\"\n ? {}\n : (process.env as Record<string, string | undefined>),\n): ResolvedConfig {\n // ⚠️ The ONE place this package is allowed to throw, and it throws before any\n // request is served rather than during one. A leaked server credential is not\n // something to degrade gracefully around: the customer needs to know now, by\n // name, which variable is the problem.\n for (const prefix of PUBLIC_ENV_PREFIXES) {\n const name = `${prefix}ENTREPRENOID_SERVER_KEY`;\n if (env[name]) {\n throw new EntreprenoidConfigError(\n `${name} is set. The \"${prefix}\" prefix inlines a value into client-side JavaScript, ` +\n `so this key is already public. Revoke it, then set ENTREPRENOID_SERVER_KEY instead ` +\n `(no prefix) so it stays on the server.`,\n );\n }\n }\n\n const ingestUrl = options.ingestUrl ?? env[\"ENTREPRENOID_INGEST_URL\"] ?? \"\";\n const serverKey = options.serverKey ?? env[\"ENTREPRENOID_SERVER_KEY\"] ?? \"\";\n // ⚠️ **Defaulted, never empty, and this is a data-loss fix rather than a\n // nicety.**\n //\n // The wire schema requires `siteId` with min length 1, so an unset value\n // rejects the ENTIRE BATCH as `invalid_batch` -- and because the SDK fails\n // open, that rejection is silent. The symptom is an empty dashboard and a\n // perfectly healthy application, which is the hardest thing this product can\n // do to somebody. Found by a real install attempt\n // (`docs/design/install-findings-2026-09-16.md`), reproduced against the real\n // schema rather than assumed.\n //\n // ⚠️ The honest reason a default is CORRECT here, rather than a papering-over:\n // ingest OVERWRITES `siteId` from the credential, because a body value is a\n // request to write into somebody else's data. So the field is never trusted\n // and never stored as sent -- its only job is passing validation. Requiring a\n // customer to supply it correctly was asking them to get right something that\n // cannot be wrong, and punishing them silently for not.\n //\n // A customer may still set it, and the dashboard still shows it, because an\n // operator matching a log line to a site wants to see it.\n const siteId = options.siteId ?? env[\"ENTREPRENOID_SITE_ID\"] ?? UNSET_SITE_ID;\n const enabled = options.enabled ?? boolFromEnv(env[\"ENTREPRENOID_ENABLED\"]) ?? true;\n const debug = options.debug ?? boolFromEnv(env[\"ENTREPRENOID_DEBUG\"]) ?? false;\n\n let disabled: DisabledReason | null = null;\n if (typeof window !== \"undefined\") {\n // Belt and braces behind the `browser: null` export condition. If a bundler\n // that ignores export conditions got us here, collect nothing and -- above\n // all -- do not transmit the key.\n disabled = \"browser-environment\";\n } else if (!enabled) {\n disabled = \"explicitly-disabled\";\n } else if (!ingestUrl) {\n disabled = \"missing-url\";\n } else if (!serverKey) {\n disabled = \"missing-key\";\n }\n\n return Object.freeze({\n ingestUrl,\n serverKey,\n siteId,\n debug,\n batchSize: clamp(options.batchSize ?? DEFAULTS.batchSize, 1, DEFAULTS.maxBatchSize),\n flushIntervalMs: clamp(options.flushIntervalMs ?? DEFAULTS.flushIntervalMs, 100, 60_000),\n maxQueueEvents: clamp(options.maxQueueEvents ?? DEFAULTS.maxQueueEvents, 1, 100_000),\n maxBodyBytes: clamp(options.maxBodyBytes ?? DEFAULTS.maxBodyBytes, 1_024, DEFAULTS.maxBodyBytes),\n requestTimeoutMs: clamp(options.requestTimeoutMs ?? DEFAULTS.requestTimeoutMs, 100, 30_000),\n routeTemplate: options.routeTemplate,\n redactPatterns: options.redactPatterns ?? [],\n // ⚠️ Defaults to TRUE. An `?? true` that a refactor turns into `?? false`\n // is the entire bug returning, so `config.test.ts` asserts the default.\n redactHighEntropyPaths: options.redactHighEntropyPaths ?? true,\n isInternal: options.isInternal,\n disabled,\n });\n}\n\n/**\n * Redact anything key-shaped before it reaches a log line.\n *\n * The SDK must never log the server key (§8.2). Debug output is written by\n * people debugging, pasted into issues, and captured by log aggregators.\n */\nexport function redact(text: string): string {\n return text.replace(/\\bep_(live|test)_server_[A-Za-z0-9_-]+/g, \"ep_$1_server_[redacted]\");\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\n\n/**\n * Turn events into a request body, stopping before a byte ceiling.\n *\n * ── Why this is not `JSON.stringify({ events })` ─────────────────────────────\n * The ingest refuses a body over 512 KiB. Encoding the whole batch and then\n * checking its size means either sending something that will be refused, or\n * truncating a JSON document -- which produces a body that is not JSON at all\n * and fails in a way no error message explains.\n *\n * So events are measured one at a time and the encoder stops BEFORE the one\n * that would cross the line. The untaken events stay at the head of the queue\n * for the next flush.\n */\n\n/** The largest encoded body the ingest will accept, in bytes. */\nexport const MAX_BODY_BYTES = 512 * 1024;\n\nconst OPEN = '{\"events\":[';\nconst CLOSE = \"]}\";\n\nexport interface EncodedBatch {\n body: string;\n /** How many events from the front of the input are in `body`. */\n taken: number;\n /**\n * An event that can NEVER be sent because it alone exceeds the ceiling.\n *\n * ⚠️ This is head-of-line blocking, and it is the reason this field exists.\n * Without it, one oversized event sits at the front of the queue forever:\n * every flush encodes zero events, commits nothing, and the SDK goes\n * permanently silent with a full buffer and no error. The caller drops it and\n * counts it as dropped.\n */\n oversized: boolean;\n}\n\nconst byteLength =\n typeof TextEncoder === \"function\"\n ? (s: string): number => new TextEncoder().encode(s).length\n : // Node 18+ always has TextEncoder; this is here so the module cannot\n // throw at import time on an exotic runtime, which would take the host\n // application down with it.\n (s: string): number => s.length;\n\nexport function encodeBatch(\n events: readonly RequestEvent[],\n maxBytes: number = MAX_BODY_BYTES,\n): EncodedBatch {\n if (events.length === 0) {\n return { body: OPEN + CLOSE, taken: 0, oversized: false };\n }\n\n const overhead = byteLength(OPEN) + byteLength(CLOSE);\n let used = overhead;\n const parts: string[] = [];\n\n for (const event of events) {\n const encoded = JSON.stringify(event);\n // Every event after the first costs a comma.\n const cost = byteLength(encoded) + (parts.length > 0 ? 1 : 0);\n if (used + cost > maxBytes) break;\n parts.push(encoded);\n used += cost;\n }\n\n return {\n body: OPEN + parts.join(\",\") + CLOSE,\n taken: parts.length,\n oversized: parts.length === 0,\n };\n}\n","/**\n * A bounded ring buffer of pending events.\n *\n * ── Why bounded, and why drop-OLDEST ─────────────────────────────────────────\n * This queue lives in a customer's production process. An unbounded one turns\n * our outage into their out-of-memory kill, which is the single worst thing an\n * analytics package can do to a host application. So it has a hard ceiling.\n *\n * When the ceiling is reached the OLDEST event is discarded, not the newest.\n * During an outage the recent past is what a site owner is looking at; a buffer\n * that refused new events would preserve a frozen window from whenever the\n * outage began and discard everything since.\n *\n * ⚠️ **Drops are COUNTED and reported on the wire** (`sdk.dropped`). A silently\n * short count is worse than a visibly short one: the product's whole claim is\n * that it shows traffic other tools miss, so under-reporting without saying so\n * is the one failure mode that discredits the number rather than the outage.\n *\n * ── peek/commit rather than drain ────────────────────────────────────────────\n * A send can partially succeed: the encoder stops before the byte limit, so\n * fewer events go out than were offered. `peek` then `commit(n)` keeps the\n * untaken ones at the head with no re-insertion, which is both cheaper and\n * impossible to get out of order.\n */\nexport class BoundedQueue<T> {\n readonly capacity: number;\n #items: (T | undefined)[];\n #head = 0;\n #size = 0;\n #dropped = 0;\n\n constructor(capacity: number) {\n if (!Number.isInteger(capacity) || capacity < 1) {\n throw new TypeError(`capacity must be a positive integer, got ${capacity}`);\n }\n this.capacity = capacity;\n this.#items = new Array<T | undefined>(capacity);\n }\n\n get size(): number {\n return this.#size;\n }\n\n /** How many events have been discarded because the buffer was full. */\n get dropped(): number {\n return this.#dropped;\n }\n\n push(item: T): void {\n if (this.#size === this.capacity) {\n // Full: overwrite the oldest and advance the head past it.\n this.#items[this.#head] = item;\n this.#head = (this.#head + 1) % this.capacity;\n this.#dropped += 1;\n return;\n }\n this.#items[(this.#head + this.#size) % this.capacity] = item;\n this.#size += 1;\n }\n\n /** The first `max` items, without removing them. */\n peek(max: number): T[] {\n const n = Math.min(max, this.#size);\n const out: T[] = new Array<T>(n);\n for (let i = 0; i < n; i += 1) {\n out[i] = this.#items[(this.#head + i) % this.capacity] as T;\n }\n return out;\n }\n\n /** Remove the first `n` items. Called only after they are safely sent. */\n commit(n: number): void {\n const count = Math.min(n, this.#size);\n for (let i = 0; i < count; i += 1) {\n this.#items[(this.#head + i) % this.capacity] = undefined;\n }\n this.#head = (this.#head + count) % this.capacity;\n this.#size -= count;\n }\n\n /** Reset the drop counter, once the count has been reported on the wire. */\n clearDropped(): void {\n this.#dropped = 0;\n }\n}\n","/**\n * The wrapper every entry point into this package wears.\n *\n * ⚠️ **This is the most important twenty lines in the SDK.** Our code runs\n * inside other people's request handlers. A throw that escapes becomes a 500 on\n * a page we were only supposed to be counting -- the analytics package breaking\n * the application it measures, which is unforgivable in a way that a missing\n * metric is not.\n *\n * So every public surface is wrapped, and the wrapper swallows. It reports\n * through the debug channel when the customer asked for one, and is otherwise\n * silent: a warning printed on every request of a busy server is its own\n * outage.\n */\nexport function safe(fn: () => void, onError?: (error: unknown) => void): void {\n try {\n fn();\n } catch (error) {\n try {\n onError?.(error);\n } catch {\n // The error reporter threw. There is nowhere left to report that, and\n // trying would be the same bug one level up.\n }\n }\n}\n\n/** The async form, for flush paths. Returns a promise that never rejects. */\nexport async function safeAsync(\n fn: () => Promise<void>,\n onError?: (error: unknown) => void,\n): Promise<void> {\n try {\n await fn();\n } catch (error) {\n try {\n onError?.(error);\n } catch {\n // As above.\n }\n }\n}\n","/**\n * The seam between the collector and the network.\n *\n * Exists so the collector can be tested without a socket, and so a runtime\n * without `fetch` can supply its own. The three-way outcome below is the\n * important part of the contract.\n */\nexport type SendOutcome =\n | { outcome: \"accepted\"; status: number }\n /**\n * A 4xx that is not 408 or 429: our payload is wrong.\n *\n * ⚠️ **Drop the batch. Do not retry, and do not count it against the\n * breaker.** Retrying a body the server has already judged malformed just\n * sends it again, and treating it as an outage would let one bad field\n * silence all telemetry for thirty seconds at a time while the endpoint is\n * perfectly healthy. This is the same distinction maxidomo-app's\n * `ExpectedError` draws: a failure somebody wrote on purpose is not an\n * incident.\n */\n | { outcome: \"rejected\"; status: number }\n /** Network error, timeout, 408, 429 or 5xx. Retry, and count it. */\n | { outcome: \"retryable\"; status?: number; retryAfterMs?: number };\n\nexport interface Transport {\n send(\n body: string,\n headers: Readonly<Record<string, string>>,\n signal: AbortSignal,\n ): Promise<SendOutcome>;\n}\n\n/** Map an HTTP status onto the outcome taxonomy above. */\nexport function outcomeForStatus(status: number, retryAfterMs?: number): SendOutcome {\n if (status >= 200 && status < 300) return { outcome: \"accepted\", status };\n if (status === 408 || status === 429 || status >= 500) {\n return retryAfterMs === undefined\n ? { outcome: \"retryable\", status }\n : { outcome: \"retryable\", status, retryAfterMs };\n }\n return { outcome: \"rejected\", status };\n}\n\nfunction parseRetryAfter(value: string | null): number | undefined {\n if (!value) return undefined;\n const seconds = Number(value);\n if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;\n const date = Date.parse(value);\n if (Number.isFinite(date)) return Math.max(0, date - Date.now());\n return undefined;\n}\n\n/**\n * The default transport: `fetch`, which every supported runtime has.\n *\n * ⚠️ It never throws. A thrown transport would reach the flush loop, and a\n * flush loop that can throw is one unhandled rejection away from taking a\n * customer's process down with it.\n */\nexport function fetchTransport(url: string): Transport {\n return {\n async send(body, headers, signal): Promise<SendOutcome> {\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body,\n signal,\n // Never follow a redirect: this request carries a bearer token, and\n // a redirect is an instruction from the network to send that\n // credential somewhere we did not choose.\n redirect: \"error\",\n keepalive: false,\n });\n return outcomeForStatus(\n response.status,\n parseRetryAfter(response.headers.get(\"retry-after\")),\n );\n } catch {\n // DNS failure, connection refused, TLS error, abort. All retryable, and\n // none of them is ever allowed to surface.\n return { outcome: \"retryable\" };\n }\n },\n };\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\nimport { createBreaker, type Breaker, type BreakerState } from \"./breaker.js\";\nimport { redact, type ResolvedConfig } from \"./config.js\";\nimport { encodeBatch } from \"./encode.js\";\nimport { BoundedQueue } from \"./queue.js\";\nimport { safeAsync } from \"./safe.js\";\nimport { fetchTransport, type SendOutcome, type Transport } from \"./transport.js\";\n\nexport interface CollectorStats {\n queued: number;\n dropped: number;\n sent: number;\n failed: number;\n breaker: BreakerState;\n}\n\nexport interface Collector {\n /**\n * Hand an event to the collector.\n *\n * ⚠️ **Synchronous, returns void, and never throws.** Those three properties\n * are the contract with the host application and each is asserted by a test.\n * It is deliberately impossible for a caller to await this: the moment an\n * adapter can await the collector, somebody will, and analytics will be on\n * the critical path.\n */\n record(event: RequestEvent): void;\n /** Attempt to send what is buffered. Never rejects. */\n flush(): Promise<void>;\n readonly stats: CollectorStats;\n /** Stop the timer. For tests and for a host that manages its own lifecycle. */\n close(): void;\n}\n\nexport interface CollectorDeps {\n transport?: Transport;\n breaker?: Breaker;\n /** Injected so backoff is deterministic under test. */\n random?: () => number;\n /** Injected so tests need no real timers. */\n setTimer?: (fn: () => void, ms: number) => { cancel(): void };\n log?: (message: string) => void;\n}\n\nconst MAX_RETRIES = 2;\n\nfunction defaultSetTimer(fn: () => void, ms: number): { cancel(): void } {\n const handle = setInterval(fn, ms);\n // ⚠️ `unref()` is not an optimisation, it is a correctness requirement.\n //\n // Without it an interval keeps Node's event loop alive, so any short-lived\n // process that imports the SDK -- a CLI, a migration script, a build step --\n // hangs for the flush interval before exiting. To the customer that presents\n // as \"the analytics package broke my build\", and they are right.\n //\n // `unref` is Node-only; a runtime without it simply keeps the timer, which is\n // correct for a long-lived server.\n (handle as unknown as { unref?: () => void }).unref?.();\n return { cancel: () => clearInterval(handle) };\n}\n\nexport function createCollector(config: ResolvedConfig, deps: CollectorDeps = {}): Collector {\n const log = deps.log ?? ((message: string) => console.warn(redact(message)));\n const debug = (message: string): void => {\n if (config.debug) log(`[entreprenoid] ${message}`);\n };\n\n // A disabled collector is a transparent no-op -- not a broken one, and not a\n // noisy one. Missing configuration in production is normal (a preview\n // deployment, a contributor's laptop), so it is reported once in debug and\n // never again.\n if (config.disabled) {\n debug(`collection disabled: ${config.disabled}`);\n const noop: Collector = {\n record: () => {},\n flush: async () => {},\n stats: { queued: 0, dropped: 0, sent: 0, failed: 0, breaker: \"closed\" },\n close: () => {},\n };\n return noop;\n }\n\n const queue = new BoundedQueue<RequestEvent>(config.maxQueueEvents);\n const transport = deps.transport ?? fetchTransport(config.ingestUrl);\n const breaker = deps.breaker ?? createBreaker();\n const random = deps.random ?? Math.random;\n const setTimer = deps.setTimer ?? defaultSetTimer;\n\n let sent = 0;\n let failed = 0;\n let inFlight: Promise<void> | null = null;\n\n const timer = setTimer(() => {\n void flush();\n }, config.flushIntervalMs);\n\n function headers(): Record<string, string> {\n return {\n \"content-type\": \"application/json\",\n authorization: `Bearer ${config.serverKey}`,\n };\n }\n\n async function sendOnce(body: string): Promise<SendOutcome> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), config.requestTimeoutMs);\n (timeout as unknown as { unref?: () => void }).unref?.();\n try {\n return await transport.send(body, headers(), controller.signal);\n } finally {\n clearTimeout(timeout);\n }\n }\n\n /** Full jitter. A fleet of customer servers must not retry in lockstep. */\n function backoffMs(attempt: number, retryAfterMs?: number): number {\n if (retryAfterMs !== undefined) return Math.min(retryAfterMs, 30_000);\n return random() * Math.min(5_000, 200 * 3 ** attempt);\n }\n\n const sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n const t = setTimeout(resolve, ms);\n (t as unknown as { unref?: () => void }).unref?.();\n });\n\n async function flushOnce(): Promise<void> {\n if (queue.size === 0) return;\n if (!breaker.allow()) {\n debug(\"breaker open, skipping flush\");\n return;\n }\n\n const candidates = queue.peek(config.batchSize);\n const droppedSoFar = queue.dropped;\n if (droppedSoFar > 0 && candidates[0]) {\n // Report the shortfall on the wire rather than under-counting silently.\n candidates[0] = {\n ...candidates[0],\n sdk: { ...candidates[0].sdk, dropped: droppedSoFar },\n };\n }\n\n const encoded = encodeBatch(candidates, config.maxBodyBytes);\n\n if (encoded.oversized) {\n // One event alone exceeds the body limit and can never be sent. Drop it,\n // or it blocks the head of the queue forever and the SDK goes silent with\n // a full buffer and no error anywhere.\n queue.commit(1);\n debug(\"dropped one event larger than the body limit\");\n return;\n }\n\n for (let attempt = 0; attempt <= MAX_RETRIES; attempt += 1) {\n const result = await sendOnce(encoded.body);\n\n if (result.outcome === \"accepted\") {\n queue.commit(encoded.taken);\n if (droppedSoFar > 0) queue.clearDropped();\n sent += encoded.taken;\n breaker.success();\n return;\n }\n\n if (result.outcome === \"rejected\") {\n // Our payload is wrong. Retrying sends the same bad body again, and\n // counting it against the breaker would silence a healthy endpoint.\n queue.commit(encoded.taken);\n failed += encoded.taken;\n debug(`batch rejected with ${result.status}; dropped ${encoded.taken} event(s)`);\n return;\n }\n\n if (attempt === MAX_RETRIES) break;\n await sleep(backoffMs(attempt, result.retryAfterMs));\n }\n\n failed += encoded.taken;\n breaker.failure();\n debug(`batch failed after ${MAX_RETRIES + 1} attempt(s); ${queue.size} event(s) still buffered`);\n }\n\n function flush(): Promise<void> {\n // One flush at a time. Two concurrent flushes would both peek the same\n // head of the queue and send it twice -- which ingest deduplicates, but\n // only because every event carries an id. Not relying on that.\n if (inFlight) return inFlight;\n inFlight = safeAsync(flushOnce, (error) => debug(`flush failed: ${String(error)}`)).finally(\n () => {\n inFlight = null;\n },\n );\n return inFlight;\n }\n\n return {\n record(event: RequestEvent): void {\n // No try/catch needed for the push itself -- the ring buffer cannot throw\n // -- but the contract is \"never throws\" and a future edit here must not\n // be able to break it.\n try {\n queue.push(event);\n } catch {\n /* never allowed to surface */\n }\n },\n flush,\n get stats(): CollectorStats {\n return {\n queued: queue.size,\n dropped: queue.dropped,\n sent,\n failed,\n breaker: breaker.state,\n };\n },\n close(): void {\n timer.cancel();\n },\n };\n}\n","/**\n * Decide whether a path segment looks like a secret, and redact it by default.\n *\n * ── Why this is ON by default, when `redactPatterns` already exists ──────────\n * Because opt-in is what failed. `redactPatterns`, `routeTemplate` and\n * `isInternal` are all well designed and all opt-in, and an independent install\n * attempt (`docs/design/install-findings-2026-09-16.md`) found that the install\n * guide mentioned **none of the three**. The site in question puts single-use\n * capability tokens in its PATHS -- `/reveal/<token>`, `/join/<code>` -- so an\n * agent following the guide exactly would have POSTed live credential material\n * to our event store.\n *\n * The guide now has a step about it, and a step an agent can skip is a step an\n * agent will skip. **The failure mode of skipping it must be a redacted path,\n * not a leaked one.**\n *\n * ── ⚠️ The hard part is not the regex ────────────────────────────────────────\n * A high-entropy segment is INDISTINGUISHABLE from a capability token by shape:\n *\n * cmu4psgny0003s397tb2t5gx6 cuid -- our own site ids look exactly like this\n * 7f3a9c2b1d4e6f8a hex -- a build hash, or a reset token\n * a1b2c3d4-...-e5f6 uuid -- a public resource id, or a ticket\n *\n * So entropy cannot decide, and any default is wrong in one direction. The\n * decision, written down rather than left in the code: **be wrong in the\n * direction of redaction.** A redacted path costs an operator route detail,\n * recoverable by setting `redactHighEntropyPaths: false`. A leaked token is\n * recoverable by nothing -- it is already in somebody else's database.\n *\n * ── ⚠️ What it deliberately does NOT touch ───────────────────────────────────\n * - **Anything with a file extension.** `/assets/app.4f3b2a.css` is an asset\n * path, and mangling it would destroy the format analysis that is half this\n * product.\n * - **Template placeholders.** A customer whose `routeTemplate` produced\n * `/users/:id` has told us that segment is a dimension, not a secret.\n * - **Slugs.** `why-markdown-twins-matter-for-agents` is long and mixed and\n * entirely public. Word structure is what separates it from a token, so word\n * structure is what is tested -- not length, which would redact every blog.\n *\n * ⚠️ **It is a backstop, not a policy.** Only the customer knows which of their\n * paths are secret; a short token (`/j/aB3xK9`) is under the length floor and\n * will not be caught. The guide's step remains the real fix, and says so.\n */\n\n/** Below this, false positives dominate and real tokens are rare. */\nconst MIN_LENGTH = 16;\n\n/** Mixed-class density under which a segment reads as prose, not entropy. */\nconst MIN_ENTROPY_BITS = 3.0;\n\nconst PLACEHOLDER = /^[:*[{<]/;\nconst HAS_EXTENSION = /\\.[A-Za-z0-9]{1,8}$/;\nconst UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n/** cuid and cuid2, which is what Prisma's `@default(cuid())` mints. */\nconst CUID = /^c[a-z0-9]{20,31}$/;\nconst JWT = /^ey[A-Za-z0-9_-]{6,}\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/;\nconst LONG_HEX = /^[0-9a-f]{16,}$/i;\nconst ALL_LOWER_ALPHA = /^[a-z]+$/;\n\n/** Shannon entropy in bits per character. */\nfunction entropyBits(value: string): number {\n const counts = new Map<string, number>();\n for (const ch of value) counts.set(ch, (counts.get(ch) ?? 0) + 1);\n let bits = 0;\n for (const n of counts.values()) {\n const p = n / value.length;\n bits -= p * Math.log2(p);\n }\n return bits;\n}\n\n/**\n * Long, mixed and dense. The generic arm, for tokens with no recognisable\n * shape.\n */\nfunction dense(value: string): boolean {\n if (value.length < MIN_LENGTH) return false;\n if (ALL_LOWER_ALPHA.test(value)) return false;\n const classes =\n Number(/[a-z]/.test(value)) +\n Number(/[A-Z]/.test(value)) +\n Number(/[0-9]/.test(value)) +\n Number(/[^A-Za-z0-9]/.test(value));\n if (classes < 3) return false;\n return entropyBits(value) >= MIN_ENTROPY_BITS;\n}\n\n/**\n * ⚠️ Word structure is the discriminator, not length.\n *\n * `top-10-reasons-to-use-markdown` is 30 characters, mixes three character\n * classes and has entropy above the threshold. It is also a blog post. Without\n * this check the default would redact most of the internet's URLs.\n *\n * ⚠️ **A part that is pure digits does not count against wordiness**, and\n * getting that wrong was caught by the table: `release-notes-2026-09-16` is\n * two words and three numbers, and a \"majority must be words\" rule redacted it.\n * What actually separates a slug from a token is that EVERY part is either a\n * word or a number -- a token has parts that are neither, because it mixes\n * letters and digits *within* a part.\n */\nfunction slugLike(segment: string): boolean {\n const parts = segment.split(/[-_]/).filter(Boolean);\n if (parts.length < 2) return false;\n const tidy = parts.every((p) => /^[a-z]+$/.test(p) || /^[0-9]+$/.test(p));\n const hasWord = parts.some((p) => /^[a-z]{2,}$/.test(p));\n return tidy && hasWord;\n}\n\nexport function looksLikeSecret(segment: string): boolean {\n if (segment.length < MIN_LENGTH) return false;\n if (PLACEHOLDER.test(segment)) return false;\n if (HAS_EXTENSION.test(segment)) return false;\n\n // Known shapes decide outright, ahead of any heuristic -- including when they\n // are also slug-like, which a uuid's hyphens would otherwise make it.\n if (UUID.test(segment) || CUID.test(segment) || JWT.test(segment) || LONG_HEX.test(segment)) {\n return true;\n }\n\n // ⚠️ A dense PART makes the whole segment suspect, checked before the slug\n // exemption. `reveal-tok-Xk7Lm9Qp2Rt5Vw8Zb3Nd` is majority-words by part count\n // and still carries a token.\n for (const part of segment.split(/[-_.]/)) {\n if (dense(part)) return true;\n }\n\n if (slugLike(segment)) return false;\n return dense(segment);\n}\n\nexport const REDACTED = \"[redacted]\";\n\nexport interface RedactionResult {\n path: string;\n /** True when at least one segment was replaced, by any rule. */\n redacted: boolean;\n}\n\n/**\n * Apply the customer's patterns and, unless disabled, the default detector.\n *\n * ⚠️ Custom patterns run FIRST and independently. A customer who wrote a\n * pattern gets it applied whether or not the default is on, and turning the\n * default off never turns theirs off with it.\n */\nexport function redactPath(\n path: string,\n patterns: readonly RegExp[],\n useDefault: boolean,\n): RedactionResult {\n if (patterns.length === 0 && !useDefault) return { path, redacted: false };\n\n let redacted = false;\n const out = path\n .split(\"/\")\n .map((segment) => {\n if (!segment) return segment;\n const matchesCustom = patterns.some((re) => {\n // A global regex carries lastIndex between calls, which makes an\n // every-other-segment miss that is maddening to debug.\n re.lastIndex = 0;\n return re.test(segment);\n });\n if (matchesCustom || (useDefault && looksLikeSecret(segment))) {\n redacted = true;\n return REDACTED;\n }\n return segment;\n })\n .join(\"/\");\n\n return { path: out, redacted };\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\nimport type { ResolvedConfig } from \"../core/config.js\";\nimport { redactPath } from \"./redact.js\";\n\n/**\n * Turn a request into the fields the wire event carries -- and, just as\n * importantly, leave everything else behind.\n *\n * ── This module IS the privacy promise ───────────────────────────────────────\n * `docs/design/collected-fields.md` lists what we take. This is the only place\n * that decides. Every value here is read from a named header or a named,\n * allowlisted query parameter; there is no path that copies \"the rest\" of\n * anything, which is why a cookie or a bearer token cannot arrive by accident.\n *\n * A reviewer should be able to read this file and check the published list\n * against it line by line.\n */\n\n/** The only query parameters ever read. Everything else is discarded. */\nexport const CAMPAIGN_PARAMS = [\"utm_source\", \"utm_medium\", \"utm_campaign\", \"utm_content\"] as const;\n\n/** Advertising click-ids: we record WHICH was present, never the value. */\nconst CLICK_ID_PARAMS = [\"gclid\", \"fbclid\", \"msclkid\"] as const;\n\nexport interface ObservedRequest {\n method: string;\n path: string;\n route?: string;\n host?: string;\n protocol?: \"http\" | \"https\";\n userAgent?: string;\n referrerOrigin?: string;\n campaign?: RequestEvent[\"campaign\"];\n clickIdType?: RequestEvent[\"clickIdType\"];\n internal?: boolean;\n /**\n * ⚠️ Set when any segment of the path or route was replaced, by a customer\n * pattern or by the default detector. **Visible, never silent** -- an\n * operator staring at `/reveal/[redacted]` needs to know it was us, not a\n * bug in their router, and needs to be able to find the setting.\n */\n pathRedacted?: boolean;\n}\n\n/**\n * Normalise a path: no query, no fragment, no repeated slashes, bounded length.\n *\n * ⚠️ The query string is dropped HERE, before anything else looks at it.\n * Allowlisted campaign values are read separately from the parsed URL. There is\n * deliberately no code path in which a raw query string is carried forward and\n * filtered later -- a filter can be wrong; an absence cannot.\n */\nexport function normalisePath(rawPath: string, redactPatterns: readonly RegExp[] = []): string {\n const path = cleanPath(rawPath);\n // ⚠️ Redaction is applied here for callers that pass patterns directly, which\n // keeps this function's old signature honest. `observeRequest` does NOT use\n // this arm -- it redacts AFTER `routeTemplate`, for the reason below.\n return redactPatterns.length > 0 ? redactPath(path, redactPatterns, false).path : path;\n}\n\n/** Shape only: no query, no fragment, no repeated slashes, bounded length. */\nfunction cleanPath(rawPath: string): string {\n let path = rawPath;\n\n const hash = path.indexOf(\"#\");\n if (hash !== -1) path = path.slice(0, hash);\n const query = path.indexOf(\"?\");\n if (query !== -1) path = path.slice(0, query);\n\n if (path === \"\") path = \"/\";\n if (!path.startsWith(\"/\")) path = `/${path}`;\n path = path.replace(/\\/{2,}/g, \"/\");\n if (path.length > 1 && path.endsWith(\"/\")) path = path.slice(0, -1);\n\n return path.length > 1024 ? path.slice(0, 1024) : path;\n}\n\n/** Scheme and host only. The page someone came from is never collected. */\nexport function referrerOrigin(referer: string | null | undefined): string | undefined {\n if (!referer) return undefined;\n try {\n const url = new URL(referer);\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") return undefined;\n return url.origin.slice(0, 255);\n } catch {\n // A malformed Referer is a malformed Referer. Nothing is guessed from it.\n return undefined;\n }\n}\n\nfunction campaignFrom(params: URLSearchParams): RequestEvent[\"campaign\"] | undefined {\n let found = false;\n const campaign: Record<string, string> = {};\n for (const name of CAMPAIGN_PARAMS) {\n const value = params.get(name);\n if (value) {\n // `utm_source` -> `source`, and bounded, because a query parameter is\n // attacker-controlled and unbounded by nature.\n campaign[name.slice(4)] = value.slice(0, 120);\n found = true;\n }\n }\n return found ? (campaign as RequestEvent[\"campaign\"]) : undefined;\n}\n\nfunction clickIdFrom(params: URLSearchParams): RequestEvent[\"clickIdType\"] | undefined {\n for (const name of CLICK_ID_PARAMS) {\n if (params.has(name)) return name;\n }\n return undefined;\n}\n\nexport interface RequestFacts {\n method: string;\n /** Path AND query, as it arrived. Never stored; parsed here and discarded. */\n url: string;\n host?: string | undefined;\n protocol?: \"http\" | \"https\" | undefined;\n userAgent?: string | undefined;\n referer?: string | undefined;\n}\n\nexport function observeRequest(facts: RequestFacts, config: ResolvedConfig): ObservedRequest {\n // ⚠️ **`routeTemplate` sees the REAL path, and redaction happens after it.**\n //\n // The original order redacted first, so a customer's\n // `path.replace(/\\/\\d+(?=\\/|$)/g, \"/:id\")` was handed `/users/[redacted]`\n // and matched nothing -- their templating silently stopped working the moment\n // any default redaction existed. Their function is the one thing that knows\n // which segments are dimensions, so it must see what actually arrived.\n const cleaned = cleanPath(facts.url);\n\n // Parsed against a throwaway base purely to reach the query parameters. The\n // result never leaves this function.\n let params = new URLSearchParams();\n try {\n params = new URL(facts.url, \"http://x\").searchParams;\n } catch {\n /* an unparseable URL simply has no campaign */\n }\n\n const templated = config.routeTemplate?.(cleaned);\n\n const { path, redacted } = redactPath(\n cleaned,\n config.redactPatterns,\n config.redactHighEntropyPaths,\n );\n\n const observed: ObservedRequest = {\n method: facts.method.slice(0, 10).toUpperCase(),\n path,\n };\n // ⚠️ The ROUTE is redacted too. Ingest stores `route ?? path`, so a route\n // that passed a token through would put it in the aggregate dimension --\n // where it is not merely stored but GROUPED BY, and therefore displayed.\n // Template placeholders (`:id`, `[slug]`) are exempt by construction.\n if (templated) {\n const safeRoute = redactPath(templated, config.redactPatterns, config.redactHighEntropyPaths);\n observed.route = safeRoute.path.slice(0, 512);\n if (safeRoute.redacted) observed.pathRedacted = true;\n }\n if (redacted) observed.pathRedacted = true;\n if (facts.host) observed.host = facts.host.slice(0, 253);\n if (facts.protocol) observed.protocol = facts.protocol;\n if (facts.userAgent) observed.userAgent = facts.userAgent.slice(0, 512);\n\n const origin = referrerOrigin(facts.referer);\n if (origin) observed.referrerOrigin = origin;\n\n const campaign = campaignFrom(params);\n if (campaign) observed.campaign = campaign;\n\n const clickIdType = clickIdFrom(params);\n if (clickIdType) observed.clickIdType = clickIdType;\n\n if (config.isInternal?.(observed)) observed.internal = true;\n\n return observed;\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\n\n/**\n * Response facts, read from headers that already exist.\n *\n * ⚠️ **Nothing here ever touches the body.** A response is a single-read\n * stream: to learn what it contained you would have to wrap `write`/`end` and\n * buffer it, which adds latency, adds memory proportional to the page, and can\n * corrupt what the customer sends if the encoding or backpressure handling is\n * wrong. `contentLength` is therefore read from the header and is simply absent\n * for a chunked or streamed response -- the dashboard shows \"not measured\",\n * which is honest and free.\n */\n\nexport interface ResponseFacts {\n status?: number | undefined;\n contentType?: string | null | undefined;\n contentLength?: string | number | null | undefined;\n latencyMs?: number | undefined;\n observation: RequestEvent extends { response?: infer R }\n ? R extends { observation: infer O }\n ? O\n : never\n : never;\n}\n\nexport function observeResponse(facts: ResponseFacts): NonNullable<RequestEvent[\"response\"]> {\n const response: NonNullable<RequestEvent[\"response\"]> = { observation: facts.observation };\n\n // ⚠️ `observation: \"unknown\"` means \"we looked and could not tell\", and the\n // wire schema refuses a status or content type alongside it. Enforced here\n // too, because an adapter is where the mistake would be made -- and an event\n // rejected at ingest is an event silently lost.\n if (facts.observation === \"unknown\") {\n if (typeof facts.latencyMs === \"number\" && Number.isFinite(facts.latencyMs)) {\n response.latencyMs = Math.max(0, Math.round(facts.latencyMs));\n }\n return response;\n }\n\n if (typeof facts.status === \"number\" && facts.status >= 100 && facts.status <= 599) {\n response.status = facts.status;\n }\n if (facts.contentType) {\n response.contentType = String(facts.contentType).slice(0, 255);\n }\n const length = typeof facts.contentLength === \"string\" ? Number(facts.contentLength) : facts.contentLength;\n if (typeof length === \"number\" && Number.isFinite(length) && length >= 0) {\n response.contentLength = Math.round(length);\n }\n if (typeof facts.latencyMs === \"number\" && Number.isFinite(facts.latencyMs)) {\n response.latencyMs = Math.max(0, Math.round(facts.latencyMs));\n }\n\n return response;\n}\n","/**\n * ⚠️ The SDK's own name and version, in ONE place.\n *\n * They were declared separately in `web.ts` and `express.ts`, and adding a\n * third adapter would have made three copies of a version string that must\n * match `package.json` -- a list nothing checks, written down three times.\n * `runtime.test.ts` asserts the version against the manifest.\n */\nexport const SDK_NAME = \"@entreprenoid/analytics\";\nexport const SDK_VERSION = \"0.1.1\";\n\n/**\n * Identify the runtime, for the `sdk.runtime` field.\n *\n * Kept deliberately dumb and total: it must never throw at import time, on any\n * runtime, because that would take the host application down before it serves\n * a request. Every branch is guarded and there is a final fallback.\n */\nexport function runtimeName(): string {\n try {\n const g = globalThis as Record<string, unknown>;\n const deno = g[\"Deno\"] as { version?: { deno?: string } } | undefined;\n if (deno?.version?.deno) return `deno-${deno.version.deno}`;\n const bun = g[\"Bun\"] as { version?: string } | undefined;\n if (bun?.version) return `bun-${bun.version}`;\n if (typeof process !== \"undefined\" && process.versions?.node) {\n return `node-${process.versions.node}`;\n }\n const nav = g[\"navigator\"] as { userAgent?: string } | undefined;\n if (nav?.userAgent?.includes(\"Cloudflare-Workers\")) return \"workerd\";\n } catch {\n /* fall through */\n }\n return \"unknown\";\n}\n\n/**\n * A unique event id.\n *\n * `crypto.randomUUID()` where available -- it is in every supported runtime and\n * costs no dependency. The fallback exists so an exotic runtime degrades to a\n * slightly weaker id rather than to a crash; a collision costs one deduplicated\n * event, a throw costs the customer a 500.\n */\nexport function newEventId(): string {\n try {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n if (typeof c?.randomUUID === \"function\") return c.randomUUID();\n } catch {\n /* fall through */\n }\n return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 12)}`;\n}\n","//\n// Decide whether a client asked for markdown.\n//\n// ⚠️ **This function is the whole safety of the serve half.** Getting it wrong\n// in the permissive direction means serving markdown to a human's browser,\n// which is a broken website -- and it is easy to get wrong, because every\n// browser on earth ends its Accept header with the catch-all range `*/*` at a\n// low q-value. Matching a wildcard would serve markdown to everyone.\n//\n// So the rule is: an EXPLICIT `text/markdown` with a non-zero q, which must\n// also be at least as preferred as `text/html`. A wildcard never qualifies. A\n// client that did not name markdown does not get markdown.\n//\n// Measured against caprail.dev on 2026-09-16: GPTBot sending `Accept: */*`\n// receives HTML, byte-identical to what a browser receives. That is the correct\n// behaviour and this reproduces it -- not by checking WHO is asking, but by\n// checking WHAT was asked for. Identity-based serving would be cloaking, would\n// force `Vary: User-Agent` and destroy shared caching, and would let a\n// spoofable string decide what a visitor receives.\n//\n\nexport interface MediaRange {\n type: string;\n q: number;\n}\n\nexport function parseAccept(header: string | null | undefined): MediaRange[] {\n if (!header) return [];\n const ranges: MediaRange[] = [];\n for (const part of header.split(\",\")) {\n const segments = part.trim().split(\";\");\n const type = segments[0]?.trim().toLowerCase();\n if (!type) continue;\n let q = 1;\n for (const segment of segments.slice(1)) {\n const [key, value] = segment.split(\"=\").map((s) => s.trim().toLowerCase());\n if (key === \"q\") {\n const parsed = Number(value);\n // A malformed q is treated as 1, which is what RFC 9110 implies and\n // what every server does in practice.\n q = Number.isFinite(parsed) ? Math.min(1, Math.max(0, parsed)) : 1;\n }\n }\n ranges.push({ type, q });\n }\n return ranges;\n}\n\n/** The q-value for an EXACT type. Wildcard ranges deliberately do not count. */\nfunction exactQ(ranges: readonly MediaRange[], type: string): number {\n let best = 0;\n for (const range of ranges) {\n if (range.type === type) best = Math.max(best, range.q);\n }\n return best;\n}\n\n/** The q-value for a type, allowing a `type/` wildcard or a full wildcard. */\nfunction effectiveQ(ranges: readonly MediaRange[], type: string): number {\n const prefix = `${type.split(\"/\")[0]}/*`;\n let best = 0;\n for (const range of ranges) {\n if (range.type === type || range.type === prefix || range.type === \"*/*\") {\n best = Math.max(best, range.q);\n }\n }\n return best;\n}\n\nexport const MARKDOWN_TYPES = [\"text/markdown\", \"text/x-markdown\"] as const;\n\nexport function prefersMarkdown(header: string | null | undefined): boolean {\n const ranges = parseAccept(header);\n if (ranges.length === 0) return false;\n\n let markdown = 0;\n for (const type of MARKDOWN_TYPES) {\n markdown = Math.max(markdown, exactQ(ranges, type));\n }\n if (markdown === 0) return false;\n\n // `text/html` reached through a wildcard still counts here: a client sending\n // `text/markdown;q=0.1` alongside a broad wildcard prefers something else,\n // and should get it.\n const html = effectiveQ(ranges, \"text/html\");\n return markdown >= html;\n}\n","import { prefersMarkdown } from \"./accept.js\";\n\n/**\n * The markdown twin: the decision, as a pure function.\n *\n * ── Why the decision is separated from the serving ───────────────────────────\n * Two adapters have to make the identical choice, and the choice is the part\n * with consequences -- serve the wrong thing and a human gets a text file\n * instead of a website. One function, one set of tests, two thin call sites.\n */\n\nexport interface Twin {\n body: string;\n /** Defaults to `text/markdown; charset=utf-8`. */\n contentType?: string;\n etag?: string;\n lastModified?: string;\n}\n\nexport interface TwinOptions {\n /**\n * Find the twin for a normalised path, or return null.\n *\n * ⚠️ **Registered, not derived.** Returning a twin for any path that \"looks\n * like\" it should have one produces a site where every URL answers, including\n * the typos -- caprail.dev returns a plain 404 for an unregistered `.md`, and\n * that is the behaviour to match.\n */\n resolve: (path: string) => Twin | null | undefined | Promise<Twin | null | undefined>;\n /** `Cache-Control` for a served twin. Edge-cacheable by default. */\n cacheControl?: string;\n /** Advertise an available twin on the HTML response. Default true. */\n advertise?: boolean;\n /**\n * Serve `/llms.txt`, `/llms-full.txt` and `/install.md` from the same\n * manifest that serves the twins.\n *\n * ⚠️ Derived, not written: a hand-maintained index is wrong the first time a\n * page is added, and an index listing pages that no longer exist is worse\n * than none -- an agent spends its budget on 404s and concludes the site is\n * broken.\n */\n discovery?: import(\"./discovery.js\").DiscoveryOptions;\n}\n\n/** Paths the discovery block answers, when it is configured. */\nexport const DISCOVERY_PATHS = [\"/llms.txt\", \"/llms-full.txt\", \"/install.md\"] as const;\n\nexport const DEFAULT_TWIN_CONTENT_TYPE = \"text/markdown; charset=utf-8\";\nexport const DEFAULT_TWIN_CACHE_CONTROL = \"public, max-age=3600, s-maxage=86400\";\n\nexport type TwinDecision =\n /** Serve the twin for `lookupPath`. */\n | { action: \"serve\"; lookupPath: string; reason: \"md_path\" | \"accept_header\" }\n /** Not ours to answer, but a twin may exist worth advertising. */\n | { action: \"pass\"; reason: \"not_get\" | \"no_signal\" };\n\n/**\n * Map a request onto a decision. Nothing here touches I/O.\n *\n * Two gates, and only two:\n * 1. the path ends in `.md` -- a distinct resource, no negotiation at all;\n * 2. `Accept` explicitly prefers markdown -- content negotiation, which owes\n * a `Vary: Accept`.\n *\n * ⚠️ Never the User-Agent. See `accept.ts`.\n */\nexport function decideTwin(input: {\n method: string;\n path: string;\n accept: string | null | undefined;\n}): TwinDecision {\n const method = input.method.toUpperCase();\n // A twin is a representation of a resource, so only the methods that ask for\n // one. Answering a POST with a markdown body would swallow a form submission.\n if (method !== \"GET\" && method !== \"HEAD\") {\n return { action: \"pass\", reason: \"not_get\" };\n }\n\n if (input.path.endsWith(\".md\")) {\n return { action: \"serve\", lookupPath: stripMdSuffix(input.path), reason: \"md_path\" };\n }\n\n if (prefersMarkdown(input.accept)) {\n return { action: \"serve\", lookupPath: input.path, reason: \"accept_header\" };\n }\n\n return { action: \"pass\", reason: \"no_signal\" };\n}\n\n/**\n * `/docs/intro.md` -> `/docs/intro`, and `/index.md` -> `/`.\n *\n * The root is the case worth naming: a site's home page has no slug, so its\n * twin is `/index.md` by convention -- the same convention caprail.dev uses.\n */\nexport function stripMdSuffix(path: string): string {\n const withoutSuffix = path.slice(0, -3);\n if (withoutSuffix === \"\" || withoutSuffix === \"/index\") return \"/\";\n return withoutSuffix;\n}\n\n/** `/docs/intro` -> `/docs/intro.md`, and `/` -> `/index.md`. */\nexport function twinPathFor(path: string): string {\n if (path === \"/\") return \"/index.md\";\n return `${path}.md`;\n}\n\nexport interface TwinResponse {\n status: number;\n headers: Record<string, string>;\n body: string;\n}\n\nexport function buildTwinResponse(\n twin: Twin,\n decision: Extract<TwinDecision, { action: \"serve\" }>,\n options: TwinOptions,\n): TwinResponse {\n const headers: Record<string, string> = {\n \"content-type\": twin.contentType ?? DEFAULT_TWIN_CONTENT_TYPE,\n \"cache-control\": options.cacheControl ?? DEFAULT_TWIN_CACHE_CONTROL,\n };\n\n // ⚠️ `Vary: Accept` is required ONLY when the URL can return two different\n // bodies -- which is the negotiated case, not the `.md` path. Sending it on a\n // `.md` response would needlessly fragment the edge cache for a resource that\n // has exactly one representation.\n //\n // Getting this wrong the other way is the serious one: negotiating without\n // `Vary` lets a shared cache hand one client's markdown to the next client\n // asking for HTML. That is a broken website served from a CDN, and it\n // outlives the deploy that caused it.\n if (decision.reason === \"accept_header\") {\n headers[\"vary\"] = \"Accept\";\n }\n if (twin.etag) headers[\"etag\"] = twin.etag;\n if (twin.lastModified) headers[\"last-modified\"] = twin.lastModified;\n\n return { status: 200, headers, body: twin.body };\n}\n\n/**\n * The advertisement for an available twin, as a `Link` header.\n *\n * ⚠️ **A header, not a tag injected into the HTML.** Caprail puts\n * `<link rel=\"alternate\">` in its own document head, which it can do because it\n * owns the template. A middleware cannot: injecting into the body means\n * buffering and rewriting somebody else's response, which is precisely what\n * this package refuses to do. RFC 8288 makes the header the equivalent, it\n * costs nothing, and it works for JSON and plain text as well as HTML.\n *\n * A customer who also wants the tag can add it to their own template, and\n * should -- some crawlers read one and not the other.\n */\nexport function advertiseHeader(path: string): string {\n return `<${twinPathFor(path)}>; rel=\"alternate\"; type=\"text/markdown\"`;\n}\n","import type { RequestEvent } from \"@entreprenoid/event-schema\";\nimport { createCollector, type Collector } from \"./core/collector.js\";\nimport { resolveConfig, type EntreprenoidConfig } from \"./core/config.js\";\nimport { safe } from \"./core/safe.js\";\nimport { observeRequest, normalisePath } from \"./observe/request.js\";\nimport { observeResponse } from \"./observe/response.js\";\nimport { newEventId, runtimeName, SDK_NAME, SDK_VERSION } from \"./runtime.js\";\nimport { advertiseHeader, buildTwinResponse, decideTwin, type TwinOptions } from \"./serve/twin.js\";\n\n/**\n * The Next.js adapter, for `proxy.ts` (called `middleware.ts` before Next 16).\n *\n * ── ⚠️ Why this exists rather than \"just use ./web\" ──────────────────────────\n * Because `./web` produces FALSE DATA in a Next proxy, and does it silently.\n *\n * A proxy runs **before the route**. It hands control onward by returning\n * `NextResponse.next()` — a sentinel with status 200, no real content-type and\n * no content-length. `./web` reads that sentinel and records\n * `{ status: 200, observation: \"measured\" }`, so **every event claims a\n * measured 200**, including requests the route renders as 404 or 500.\n *\n * ⚠️ **Our own schema already said this was wrong and refused it**:\n * `observation: \"unknown\"` may not carry a status, and the Next case is the one\n * where the `response` object is absent entirely — \"we never had a chance to\n * look\". Correct behaviour, specified, enforced by a `superRefine`, and until\n * now unreachable because no adapter emitted it. That reads as done, which is\n * worse than reading as missing.\n *\n * ── What this emits ──────────────────────────────────────────────────────────\n * Exactly what a proxy genuinely knows — method, path, host, user agent,\n * referrer origin, campaign — and **no `response` object at all**, unless it\n * served the twin itself, in which case it made the response and can measure it\n * honestly.\n *\n * ── ⚠️ The requestId merge is DESIGNED and NOT BUILT ─────────────────────────\n * `requestId` rides every event so a later post-response observation can be\n * merged onto it. **Nothing performs that merge today** — measured: nothing in\n * the worker or the database reads the field. Next offers no general\n * post-response hook (`onRequestError` fires only on errors; `after()` still\n * runs in the proxy, which cannot see the route's response), so the honest\n * second observation point is a wrapper around each route handler — a second\n * install step, easy to forget, and a half-installed product reports half its\n * traffic.\n *\n * So this ships the single honest observation. That is not a degraded event: a\n * proxy cannot see a response, and saying so precisely is the product's\n * differentiator rather than a shortfall.\n *\n * ── ⚠️ `after` is an OPTION, not an import ───────────────────────────────────\n * There is no `ctx.waitUntil` in a Next proxy; the equivalent is `after()` from\n * `next/server`. Importing it here — even dynamically — would put a bare\n * specifier in a package whose whole discipline is zero runtime dependencies,\n * and a dynamic `import()` slips past `check-sdk-artifact.mjs`'s regex, which\n * would be a gate quietly stopping working. So the customer passes it, exactly\n * as `observe()` already takes `waitUntil`:\n *\n * ```ts\n * import { after } from \"next/server\";\n * import { proxy } from \"@entreprenoid/analytics/next\";\n *\n * export default proxy({ after });\n * export const config = { matcher: [\"/((?!_next/static).*)\"] };\n * ```\n *\n * Without it the collector falls back to its own 2s timer, which a serverless\n * deployment can freeze before it fires.\n */\n\nexport interface ProxyOptions extends EntreprenoidConfig {\n /**\n * `after` from `next/server`. Passed rather than imported — see the docblock.\n * Omitting it is supported and costs reliability on serverless.\n */\n after?: (task: () => void | Promise<void>) => void;\n /** For tests, and for a host that already has a collector. */\n collector?: Collector;\n /**\n * Serve a markdown twin from the proxy.\n *\n * ⚠️ A proxy is a GOOD place for this, unlike the observation half: it runs\n * before the route, so returning the twin short-circuits rendering entirely\n * and the agent never pays for a React tree it discards. And because the\n * proxy built that response itself, the event can honestly say `measured`.\n */\n twin?: TwinOptions;\n}\n\n/**\n * ⚠️ Returns `undefined` to continue, rather than `NextResponse.next()`.\n *\n * Next treats a void return as \"carry on\", which means this adapter never has\n * to import `next/server` at all. It also removes the exact sentinel that made\n * `./web` lie here: there is no fake 200 to accidentally observe.\n */\nexport type ProxyResult = Response | undefined;\n\nexport function proxy(options: ProxyOptions = {}): (request: Request) => Promise<ProxyResult> {\n const config = resolveConfig(options);\n const collector = options.collector ?? createCollector(config);\n\n return async (request: Request): Promise<ProxyResult> => {\n // ⚠️ The clock starts OUTSIDE the try, exactly as in `./web`: if observation\n // setup throws, the request must still reach the route.\n const startedAt = Date.now();\n let served: RequestEvent[\"serve\"];\n let twinResponse: Response | undefined;\n\n // ── The serve half, before the route ─────────────────────────────────────\n // Every failure falls through to the customer's application unchanged.\n if (options.twin) {\n try {\n const url = new URL(request.url);\n const decision = decideTwin({\n method: request.method,\n path: normalisePath(url.pathname),\n accept: request.headers.get(\"accept\"),\n });\n\n if (decision.action === \"serve\") {\n const found = await options.twin.resolve(decision.lookupPath);\n if (found) {\n const built = buildTwinResponse(found, decision, options.twin);\n served = {\n decision: \"served\",\n reason: decision.reason,\n format: built.headers[\"content-type\"] ?? \"text/markdown\",\n };\n twinResponse = new Response(\n request.method.toUpperCase() === \"HEAD\" ? null : built.body,\n { status: built.status, headers: built.headers },\n );\n } else {\n served = { decision: \"fell_through\", reason: \"no_twin\" };\n }\n } else {\n served = { decision: \"fell_through\", reason: decision.reason };\n }\n } catch {\n served = { decision: \"error\", reason: \"resolver_error\" };\n }\n }\n\n // ⚠️ Advertising is deliberately NOT done here. It would mean appending a\n // `link` header to a response this proxy does not have — the route builds\n // it later. `./express` advertises because it sees the real response.\n // Claiming a twin on a response we cannot touch would be a header nobody\n // receives.\n\n const record = (): void => {\n safe(() => {\n if (config.disabled) return;\n\n const url = new URL(request.url);\n const observed = observeRequest(\n {\n method: request.method,\n url: url.pathname + url.search,\n host: url.host,\n protocol: url.protocol === \"https:\" ? \"https\" : \"http\",\n userAgent: request.headers.get(\"user-agent\") ?? undefined,\n referer: request.headers.get(\"referer\") ?? undefined,\n },\n config,\n );\n\n const event: RequestEvent = {\n ...observed,\n eventId: newEventId(),\n // ⚠️ Emitted so a post-response observation CAN be merged later.\n // Nothing merges it today, and the docblock above says so plainly\n // rather than letting the field imply otherwise.\n requestId: newEventId(),\n siteId: config.siteId,\n observedAt: new Date(startedAt).toISOString(),\n ...(served ? { serve: served } : {}),\n // ⚠️ **The response object is present ONLY when we built it.**\n //\n // If the twin was served, this proxy IS the responder and measured it.\n // Otherwise the route has not run yet and there is nothing to\n // observe — so the key is OMITTED, not set to a guess, not set to\n // `observation: \"unknown\"` with a status (which the schema refuses),\n // and not set to a latency-only object (we did not wait for the\n // response, so we did not measure a latency either).\n ...(twinResponse\n ? {\n response: observeResponse({\n status: twinResponse.status,\n contentType: twinResponse.headers.get(\"content-type\"),\n contentLength: twinResponse.headers.get(\"content-length\"),\n latencyMs: Date.now() - startedAt,\n observation: \"measured\",\n }),\n }\n : {}),\n sdk: {\n name: SDK_NAME,\n version: SDK_VERSION,\n // ⚠️ `next-proxy`, which is the name the WIRE SCHEMA already uses\n // in its own docblock (`event.ts:157`). Its own name matters here\n // more than for any other adapter: this is the one whose events\n // legitimately carry no response, and the dashboard must not\n // present an adapter's blind spot as a fact about the traffic.\n adapter: \"next-proxy\",\n runtime: runtimeName(),\n },\n };\n\n collector.record(event);\n });\n };\n\n // ⚠️ `after` runs the recording once the response is on its way, so nothing\n // here is on the path of the request. Without it, `record()` is still\n // synchronous and still non-blocking — `safe()` and the bounded queue see\n // to that — it is only the eventual FLUSH that risks being frozen.\n if (options.after) {\n try {\n options.after(record);\n } catch {\n // A hook that throws is not a reason to lose the event.\n record();\n }\n } else {\n record();\n }\n\n return twinResponse;\n };\n}\n\n/** Advertise a twin from a route handler, where the response actually exists. */\nexport { advertiseHeader };\n"]}
package/dist/next.js CHANGED
@@ -1,4 +1,4 @@
1
- export { proxy } from './chunk-EVNUKN5A.js';
2
- export { advertiseHeader } from './chunk-IHZCOC2U.js';
1
+ export { proxy } from './chunk-SJTHL7HF.js';
2
+ export { advertiseHeader } from './chunk-IOMJAF33.js';
3
3
  //# sourceMappingURL=next.js.map
4
4
  //# sourceMappingURL=next.js.map
package/dist/runtime.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * `runtime.test.ts` asserts the version against the manifest.
8
8
  */
9
9
  export declare const SDK_NAME = "@entreprenoid/analytics";
10
- export declare const SDK_VERSION = "0.1.0";
10
+ export declare const SDK_VERSION = "0.1.1";
11
11
  /**
12
12
  * Identify the runtime, for the `sdk.runtime` field.
13
13
  *
package/dist/web.cjs CHANGED
@@ -569,7 +569,7 @@ function observeResponse(facts) {
569
569
 
570
570
  // src/runtime.ts
571
571
  var SDK_NAME = "@entreprenoid/analytics";
572
- var SDK_VERSION = "0.1.0";
572
+ var SDK_VERSION = "0.1.1";
573
573
  function runtimeName() {
574
574
  try {
575
575
  const g = globalThis;