@particle-academy/fancy-connector-core 0.7.0 → 0.8.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/CHANGELOG.md +76 -0
- package/dist/index.cjs +120 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +122 -3
- package/dist/index.d.ts +122 -3
- package/dist/index.js +114 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/chain.ts","../src/compat.ts","../src/delivery.ts","../src/errors.ts","../src/faker.ts","../src/mode.ts","../src/connection.ts","../src/client.ts","../src/drift.ts","../src/idempotency.ts","../src/lease.ts","../src/metrics.ts","../src/mime.ts","../src/probe.ts","../src/text.ts","../src/render.ts","../src/seam.ts","../src/webhook.ts","../src/trigger.ts"],"sourcesContent":["/**\n * `@particle-academy/fancy-connector-core` — the runtime under every Fancy\n * connector.\n *\n * ## What this is\n *\n * The reusable guts of talking to a third-party API: modes and estates,\n * connections, one call path, a failure taxonomy that knows the difference\n * between *it never arrived* and *nobody can tell*, deterministic fakers,\n * webhook verification, pure versioned rendering, chain posting, declared metric\n * shapes, credential-free probes, and API-drift reporting.\n *\n * ## What this is NOT, and never will be\n *\n * It does not own a gate. Approval, liveness, the approved-bytes comparison,\n * consent, second review and every journal belong to the host — because each is\n * enforced in ONE place and every connector inherits it from the dispatch path\n * rather than implementing it. `seam.ts` states this in full.\n *\n * Three consequences that are asserted by tests rather than promised in prose:\n *\n * - **Nothing here reads `process.env`.** Credentials are arguments.\n * - **Nothing here retries an ambiguous failure** on a connector that has not\n * declared repeating a request harmless.\n * - **Nothing here phones home.** There is no telemetry, no central service, and\n * no URL this package contacts that a connector did not name.\n */\n\nexport * from \"./chain\";\nexport * from \"./compat\";\nexport * from \"./client\";\nexport * from \"./connection\";\nexport * from \"./delivery\";\nexport * from \"./drift\";\nexport * from \"./errors\";\nexport * from \"./faker\";\nexport * from \"./idempotency\";\nexport * from \"./lease\";\nexport * from \"./metrics\";\nexport * from \"./mime\";\nexport * from \"./mode\";\nexport * from \"./probe\";\nexport * from \"./render\";\nexport * from \"./seam\";\nexport * from \"./text\";\nexport * from \"./trigger\";\nexport * from \"./webhook\";\n","/**\n * Posting a sequence as a CHAIN — the bug that is silent in every direction.\n *\n * ## What went wrong in the reference implementation\n *\n * A renderer split long copy into `(1/3)`, `(2/3)`, `(3/3)` and the connector\n * posted each segment **top-level**, on two providers. Numbered like a thread,\n * connected to nothing. Nothing threw; the send reported success; the numbering\n * made it look deliberate, so a reader assumes the thread exists and they missed\n * it.\n *\n * It was unreachable by any test, because the chaining lived inside a loop that\n * needed a live session. So the chain builder here **takes the post function as\n * an argument** — which is the entire reason this is a module rather than four\n * lines inside a connector.\n *\n * ## The rule it encodes, which is one line and easy to reverse\n *\n * > **`root` is fixed at the top of the chain and never moves. `parent` advances\n * > to whatever was just posted.**\n *\n * Reverse them and every message attaches to the first: a fan, not a thread —\n * and the provider's response looks identical either way, which is why this\n * needs a test rather than care.\n *\n * ## What it deliberately does NOT do\n *\n * No retry. Retry wraps ONE request (see `delivery.ts`); wrapping the chain\n * would re-post every earlier segment when a later one failed, turning a partial\n * send into a duplicated one. A partial chain is reported with what it did post,\n * so a person can see exactly where it stopped.\n */\n\n/**\n * A reference to something already posted, as the provider identifies it.\n *\n * Opaque to this module: AT Protocol needs a uri **and** a cid, Mastodon needs\n * one status id, Telegram needs a message id and a chat. A shape naming one\n * provider's model would be wrong for the next one, so the chain carries\n * whatever the poster returned and never reads inside it.\n *\n * **`object`, not `Record<string, string | number>`** — and the difference is\n * not pedantry. An ordinary interface does not satisfy an index signature\n * unless it declares one, so `interface PostRef { uri: string; cid: string }`\n * did NOT satisfy the old constraint. The reference consumer worked around it\n * with `postChain<T & ChainRef>` plus two `as unknown as` casts, and could not\n * use `ChainOutcome<T>` in their own signatures at all — because the only other\n * fix was to add an index signature to a type used across their codebase in\n * order to satisfy a generic used in one place, which weakens the type\n * everywhere to please one call site.\n *\n * A constraint everyone casts past is not enforcing anything; it is just making\n * the casts. `object` still rejects the mistake worth rejecting — a primitive\n * as a reference — and accepts every real one.\n */\nexport type ChainRef = object;\n\nexport type ChainLinks<Ref extends ChainRef> = {\n /** The top of the conversation. Fixed for the whole chain. */\n root: Ref;\n /** The message immediately above this one. Advances every step. */\n parent: Ref;\n};\n\nexport type ChainOutcome<Ref extends ChainRef> = {\n /** Everything that was posted, in order. */\n posted: Ref[];\n /** The failure that stopped it, if one did. */\n failed?: { index: number; error: unknown };\n};\n\n/**\n * Post `segments` as a connected chain, optionally starting inside an existing\n * conversation.\n *\n * `post` is given the text and the links for this position — `undefined` for a\n * top-level message — and returns the provider's reference to what it created.\n *\n * **Stops at the first failure and reports what it posted.** Continuing would\n * produce a thread with a hole in it, and unwinding is not available: nothing\n * here can delete a public message, and pretending otherwise would be worse than\n * the hole.\n */\nexport async function postChain<Ref extends ChainRef>(\n segments: string[],\n answering: ChainLinks<Ref> | undefined,\n post: (text: string, links: ChainLinks<Ref> | undefined, index: number) => Promise<Ref>,\n): Promise<ChainOutcome<Ref>> {\n const posted: Ref[] = [];\n let root = answering?.root;\n let parent = answering?.parent;\n\n for (const [index, text] of segments.entries()) {\n let made: Ref;\n\n try {\n made = await post(text, root && parent ? { root, parent } : undefined, index);\n } catch (error) {\n return { posted, failed: { index, error } };\n }\n\n posted.push(made);\n // The first thing WE post becomes the root of our own chain — unless we are\n // already inside somebody else's, in which case theirs stays the root.\n root = root ?? made;\n parent = made;\n }\n\n return { posted };\n}\n","/**\n * The connector API version — what makes two release clocks safe.\n *\n * ## The problem this exists for\n *\n * The core and the connector catalogue are separate repositories, so they\n * release on separate clocks. That is deliberate: a provider changing its API is\n * a connector fix and must not wait on a core release. But it creates a hazard\n * that is worse here than in an ordinary package pair, because **connectors ship\n * as vendored source**:\n *\n * > A consumer copies a connector into their project. Six months later they\n * > upgrade the core. Their copy of the connector was written against a core two\n * > minors older, and pre-1.0 minors are allowed to break.\n *\n * Nothing in a version range catches that, because the vendored copy has no\n * manifest of its own — it is just files in someone else's repository. And the\n * failure is the quiet kind: the connector still compiles, still runs, and does\n * something subtly different.\n *\n * ## The fix: a second number that moves far more slowly\n *\n * The core's package version tracks everything — fixes, additions, internal\n * changes. `CONNECTOR_API_VERSION` tracks only one thing: **the surface a\n * connector is written against.** Adding a module, fixing a classifier, or\n * improving a message does not move it. Changing what a `Connector` must\n * implement, renaming a field a connector declares, or altering what `render`\n * returns does.\n *\n * So a core can go 0.3 → 0.9 without a single vendored connector caring, and the\n * one release that does matter announces itself in a number a connector can\n * check at import time rather than discover at run time.\n *\n * ## The support window, and why it is two rather than one\n *\n * The core accepts the current version **and the one before it**. One would mean\n * every consumer has to re-vendor every connector on the same day the core ships\n * — which nobody does, so in practice they would pin the core instead and stop\n * getting fixes. Two gives a real window: the catalogue re-issues its connectors\n * on the new version, consumers re-vendor when they next touch that connector,\n * and the old shape keeps working until the version after next.\n *\n * More than two would be a promise we cannot keep, because it means maintaining\n * a compatibility shim for a shape nobody has read in a year.\n */\n\n/**\n * The connector surface this core implements.\n *\n * **Bump this ONLY for a change a connector's own source can see**, and say in\n * the changelog what a connector author must DO. If nothing in `connectors/`\n * would have to change, it is not a connector API change.\n */\nexport const CONNECTOR_API_VERSION = 1;\n\n/**\n * Versions this core will run.\n *\n * Ordered newest first, purely so the error message reads naturally.\n */\nexport const SUPPORTED_CONNECTOR_API: readonly number[] = [1];\n\nexport class ConnectorApiMismatch extends Error {\n readonly connector: string;\n\n readonly declared: number;\n\n constructor(message: string, connector: string, declared: number) {\n super(message);\n this.name = \"ConnectorApiMismatch\";\n this.connector = connector;\n this.declared = declared;\n }\n}\n\n/**\n * Refuse a connector written against a surface this core does not implement.\n *\n * **Called at registration, not at call time.** A mismatch discovered on the\n * first real request is a mismatch discovered in production; discovered when the\n * catalogue is assembled, it is a build failure with a name attached.\n *\n * The message names the direction, because the two cases need opposite actions\n * and are trivially confused:\n *\n * - **connector NEWER than the core** → upgrade the core.\n * - **connector OLDER than the window** → re-vendor the connector.\n *\n * \"Incompatible versions\" sends someone to read source; naming which side is\n * behind sends them to the command.\n */\nexport function assertConnectorApi(connectorId: string, declared: number): void {\n if (SUPPORTED_CONNECTOR_API.includes(declared)) return;\n\n const supported = SUPPORTED_CONNECTOR_API.join(\", \");\n\n throw new ConnectorApiMismatch(\n declared > CONNECTOR_API_VERSION\n ? `\"${connectorId}\" was written against connector API ${declared}, and this core implements ${supported}. ` +\n \"The CONNECTOR is newer: upgrade @particle-academy/fancy-connector-core. Vendoring a connector ahead of \" +\n \"the core it needs is the one direction that cannot be made to work by trying.\"\n : `\"${connectorId}\" was written against connector API ${declared}, which is older than anything this core ` +\n `still runs (${supported}). The CONNECTOR is behind: move it to a release written for this core — ` +\n `upgrade its package (@particle-academy/${connectorId}-js, particle-academy/${connectorId}-php), or ` +\n \"regenerate a vendored copy from that release; its CHANGELOG names the connector API each release targets. \" +\n \"Nothing is adapted automatically, because guessing what a two-version-old connector meant is how a \" +\n \"connector quietly starts doing something else.\",\n connectorId,\n declared,\n );\n}\n\n/**\n * Whether a core version satisfies a connector's declared minimum.\n *\n * Exists so a registry or a CLI can answer *before* vendoring, which is the only\n * moment the answer is cheap. Compares the numeric prefix of each dotted\n * segment, so a prerelease suffix does not make `0.4.0-rc.1` sort below `0.3.9`\n * in a way nobody expected.\n */\nexport function satisfiesMinimum(coreVersion: string, minimum: string): boolean {\n const parts = (value: string): number[] =>\n value\n .split(\".\")\n .slice(0, 3)\n .map((segment) => Number.parseInt(segment, 10) || 0);\n\n const [a = 0, b = 0, c = 0] = parts(coreVersion);\n const [x = 0, y = 0, z = 0] = parts(minimum);\n\n if (a !== x) return a > x;\n if (b !== y) return b > y;\n\n return c >= z;\n}\n","/**\n * Failure classification — the one decision every retry gets wrong.\n *\n * ## The tension\n *\n * A network hiccup should not lose a call. And a retry after a request that\n * **succeeded and whose response was lost** is a second write — the exact\n * failure an idempotency gate exists to prevent, arriving through the door\n * marked *reliability*.\n *\n * Nearly every retry helper reconciles those the same way and loses: it catches\n * an error, waits, and tries again, because from the caller's side all failures\n * look alike. They are not alike.\n *\n * | what happened | did the provider receive it? | retry? |\n * |---|---|---|\n * | DNS failure, connection refused, socket reset before send | **no** | always safe |\n * | HTTP 429 / 5xx | **yes**, and it explicitly refused | safe — it says it did nothing |\n * | timeout, aborted request, unrecognised error | **unknown** | only where the provider is idempotent |\n * | HTTP 4xx that is not 429 | **yes**, and the answer was a real no | never |\n *\n * That third row is the whole design. A timeout looks exactly like a failure and\n * may have been a success.\n *\n * ## Why this is a separate axis from the error CLASS\n *\n * The first version of this runtime carried `retryable` as a fixed boolean on\n * each error class, and `ConnectorTransient` covered both a 5xx *and* a thrown\n * transport. Those are opposite cases: a 5xx is the provider telling you it did\n * nothing, and a thrown transport may be a socket that closed after the bytes\n * went out. Marking both `retryable = true` meant an ambiguous failure on a\n * connector with no idempotency key was retried — a silent double write, on the\n * path whose entire job is not producing one.\n *\n * So the primitive is the **kind**, and retryability is a function of the kind\n * AND what the connector declared about repeating a request. `error.retryable`\n * survives and now answers the narrower question — *is this safe to retry\n * whatever the connector is?* — which makes an old caller conservative rather\n * than wrong.\n */\n\n/** Why a call failed, in the only terms that decide whether to try again. */\nexport type FailureKind =\n /** Never reached the provider. A second attempt cannot duplicate anything. */\n | \"unreachable\"\n /** The provider answered, and its answer was \"not now\". It did nothing. */\n | \"refused-explicitly\"\n /** It may or may not have been acted on. Nobody can tell. */\n | \"ambiguous\"\n /** The provider answered and the answer was a real, permanent no. */\n | \"rejected\";\n\n/**\n * How a connector behaves when a call goes wrong, and how fast it may be used.\n *\n * **`idempotent` is the load-bearing field**, and it is the only thing that\n * makes an ambiguous failure safe to retry. A connector that has not thought\n * about it gets `false`, and an ambiguous failure is then reported for a person\n * instead of retried into a double write.\n */\nexport type DeliveryDeclaration = {\n idempotent: boolean;\n /**\n * Why it is or is not — **cited, not asserted**.\n *\n * `idempotent: true` with no reason is the one claim whose failure is a public\n * double write, so the reason names the mechanism (`Idempotency-Key`,\n * `com.atproto.repo.createRecord` having none) rather than restating the flag.\n */\n why: string;\n /** Smallest gap between two calls on this connector. */\n minIntervalMs: number;\n /**\n * Whose number `minIntervalMs` is.\n *\n * A confident figure nobody can cite is worse than an honest one that is too\n * slow, because the honest one gets revised when evidence turns up and the\n * confident one gets quoted as a platform fact.\n */\n rateSource: \"documented\" | \"self-imposed\";\n /** Where the numbers came from. See `citation.ts`. */\n citation?: Citation;\n};\n\n/**\n * Where a declared fact was read, and when.\n *\n * A citation with no date is an assertion wearing a URL. The date is what lets a\n * drift check say *this was true eight months ago and nobody has looked since*,\n * which is a different and more useful statement than *this is true*.\n */\nexport type Citation = {\n /** The provider's own documentation for this fact. */\n url: string;\n /** ISO date the URL was actually read by a person or a check. */\n readOn: string;\n /** The sentence the fact rests on, quoted. Optional but strongly wanted. */\n quote?: string;\n};\n\nexport type Classified = {\n kind: FailureKind;\n detail: string;\n /** Seconds the provider asked us to wait, when it said so. */\n retryAfter?: number;\n};\n\n/**\n * Codes that prove the request never left, or left and was refused at the door.\n *\n * Matched on codes rather than message text, because message text is not an API\n * and differs between Node, Bun, Deno and an edge runtime.\n */\nconst UNREACHABLE_CODES = new Set([\n // Every code here must PROVE the request never reached an application:\n // nothing listening, no route, or a name that never resolved. If a code can\n // also occur after the peer received and acted on the request, it belongs in\n // AMBIGUOUS_CODES -- `shouldRetry` retries `unreachable` without consulting\n // `idempotent`, so a wrong entry here is a duplicate send, not a slow one.\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"EAI_AGAIN\",\n \"EHOSTUNREACH\",\n \"ENETUNREACH\",\n \"ERR_SOCKET_CONNECTION_TIMEOUT\",\n]);\n\n/** Codes and names that mean *we stopped waiting*, which is not the same as *it did not happen*. */\nconst AMBIGUOUS_CODES = new Set([\n // A peer sends RST whenever it tears the connection down -- INCLUDING after\n // it has received and acted on the request but before the response comes\n // back. EPIPE is the same story from our side: we wrote to a socket the peer\n // had already closed, and whether an earlier complete request was processed\n // first is not knowable from here. Node surfaces the safe case and the unsafe\n // case as the same code, so the code cannot carry the claim.\n //\n // These two sat in UNREACHABLE_CODES until 0.3.0, which made them\n // unconditionally retryable and produced three sends of connectors that had\n // declared `idempotent: false`. Same defect as the 5xx / thrown-transport\n // split this module already fixed once, and a stated exception to a rule that\n // should have covered them.\n \"ECONNRESET\",\n \"EPIPE\",\n \"ETIMEDOUT\",\n \"UND_ERR_HEADERS_TIMEOUT\",\n \"UND_ERR_BODY_TIMEOUT\",\n \"UND_ERR_CONNECT_TIMEOUT\",\n]);\n\nconst AMBIGUOUS_NAMES = new Set([\"AbortError\", \"TimeoutError\"]);\n\n/**\n * Classify a thrown transport failure.\n *\n * **An unrecognised error falls to `ambiguous`, never to `unreachable`.** That\n * asymmetry is deliberate: an unknown failure treated as unreachable would be\n * retried, and the one thing worse than a lost call is two calls nobody asked\n * for. Guessing in the safe-looking direction is how this goes wrong.\n */\nexport function classifyError(error: unknown): Classified {\n // An error that already CARRIES a classification is authoritative, and this\n // check has to be first.\n //\n // `httpFailure()` attaches one so a connector can declare what counts as a\n // failure for its provider — Telegram's `200 OK` with `{\"ok\": false}` is a\n // real refusal, and only the connector knows that. Re-deriving from scratch\n // finds no Node error code on such an error and answers `ambiguous`: \"nobody\n // can tell\", about a `400` the provider was explicit about.\n //\n // `deliver()` has always read `error.classified` before falling back here, so\n // the retry path was never wrong. This was worse in a quieter way — a host\n // calling `classifyError` DIRECTLY, which is a reasonable thing to do with an\n // exported classifier, was told nobody could tell. Reported by the reference\n // consumer, who was careful to distinguish the two and nearly filed it as a\n // retry bug. The PHP twin already did this; the two runtimes disagreed.\n const attached = (error as { classified?: Classified } | null)?.classified;\n if (attached && typeof attached === \"object\" && typeof attached.kind === \"string\") return attached;\n\n const detail = error instanceof Error ? error.message : String(error);\n const code =\n (error as { code?: string })?.code ?? (error as { cause?: { code?: string } })?.cause?.code ?? \"\";\n const name = error instanceof Error ? error.name : \"\";\n\n if (UNREACHABLE_CODES.has(code)) return { kind: \"unreachable\", detail: `${code}: ${detail}` };\n if (AMBIGUOUS_NAMES.has(name) || AMBIGUOUS_CODES.has(code)) {\n return { kind: \"ambiguous\", detail: `${name || code}: ${detail}` };\n }\n\n return { kind: \"ambiguous\", detail };\n}\n\n/**\n * Classify an HTTP status.\n *\n * A status means the provider answered, so it knows what it did. 429 and 5xx are\n * \"not now\"; everything else in the 4xx range is a real no, and retrying a real\n * no just spends someone's rate limit on the same rejection.\n *\n * 429 is decided **before** the 4xx sweep, because it is a 4xx and is the one\n * 4xx worth retrying — the other ordering marks every throttle permanent and\n * turns a busy minute into a failed run.\n */\nexport function classifyStatus(status: number, body = \"\", retryAfterHeader?: string | null): Classified {\n const detail = `${status}${body ? `: ${body.slice(0, 200)}` : \"\"}`;\n const seconds = retryAfterHeader === undefined || retryAfterHeader === null ? NaN : Number(retryAfterHeader);\n\n if (status === 429 || status >= 500) {\n return {\n kind: \"refused-explicitly\",\n detail,\n ...(Number.isFinite(seconds) ? { retryAfter: seconds } : {}),\n };\n }\n\n return { kind: \"rejected\", detail };\n}\n\nexport type RetryPolicy = {\n /** Total attempts, including the first. */\n attempts: number;\n /** First backoff in ms. Doubles each attempt. */\n baseDelayMs: number;\n /** Never wait longer than this between attempts. */\n maxDelayMs: number;\n /**\n * Whether the provider makes a repeated request harmless.\n *\n * `false` is the correct default for any connector that has not proven\n * otherwise, and \"proven\" means a test that publishes twice and compares the\n * result — not a comment.\n */\n idempotent: boolean;\n};\n\nexport const DEFAULT_RETRY: RetryPolicy = {\n attempts: 3,\n baseDelayMs: 500,\n maxDelayMs: 8_000,\n idempotent: false,\n};\n\n/**\n * May this failure be retried?\n *\n * The whole rule, in four lines, in one place. A connector that classified its\n * own statuses would eventually decide some 4xx was worth retrying, and the\n * reason retries are safe at all is that this decision is made once.\n */\nexport function shouldRetry(kind: FailureKind, policy: Pick<RetryPolicy, \"idempotent\">): boolean {\n if (kind === \"rejected\") return false;\n if (kind === \"unreachable\" || kind === \"refused-explicitly\") return true;\n\n return policy.idempotent;\n}\n\n/**\n * True where a retry is safe **whatever the connector is** — i.e. without\n * needing to know whether repeating the request is harmless.\n *\n * This is what `ConnectorError.retryable` answers, and it is deliberately the\n * narrow question. A caller that has an idempotency declaration should ask\n * `shouldRetry` instead and get the extra case.\n */\nexport function isUnconditionallyRetryable(kind: FailureKind): boolean {\n return kind === \"unreachable\" || kind === \"refused-explicitly\";\n}\n\nexport type Attempt = {\n attempt: number;\n kind: FailureKind;\n detail: string;\n /** How long we waited before the NEXT attempt. Absent on the last. */\n waitedMs?: number;\n};\n\nexport type DeliveryOutcome<T> = {\n ok: boolean;\n value?: T;\n /** Every attempt that failed, in order. Empty when the first one worked. */\n attempts: Attempt[];\n /** Why it stopped, when it failed. Written for a person who has to act. */\n gaveUp?: string;\n /** The classification of the last failure, so a host can route on it. */\n kind?: FailureKind;\n /**\n * The last failure **as it was thrown** — absent when the call worked.\n *\n * `kind` and `gaveUp` are what `deliver()` concluded; this is what it\n * concluded it FROM. Until 0.5.0 an outcome carried only the conclusions, so\n * everything the error knew beyond them — the HTTP status, the provider's own\n * code, the class it was raised as — stopped here, and every failed\n * `callConnector` reached its host with none of it. See `failureFrom()` in\n * `client.ts`.\n *\n * `unknown`, because `send` may throw anything.\n */\n error?: unknown;\n};\n\n/**\n * The message a person reads when an ambiguous failure could not be retried.\n *\n * Exported because a host will want to recognise it, and because the wording is\n * load-bearing: it has to say *go and look*, not *it failed*. Those prompt\n * different actions and only one of them is correct.\n */\nexport const AMBIGUOUS_REFUSAL =\n \"The request may or may not have gone through, and this connector offers no way to repeat it \" +\n \"harmlessly. Refusing to try again — a duplicate is worse than a call that needs doing by hand. \" +\n \"Check the provider before re-running.\";\n\n/**\n * Run ONE request, retrying only where retrying is provably safe.\n *\n * ## Retry wraps ONE request, never a sequence\n *\n * Wrapping a multi-post publish would re-send every earlier segment of a thread\n * when a later one failed — turning a partial send into a duplicated one. So the\n * unit is a single request and a chain composes above it, not below.\n *\n * `send` must throw on failure. An error carrying a `classified` property (see\n * `httpFailure`) is trusted; anything else is classified here. That indirection\n * lets each connector decide what counts as a failure for it — a Telegram\n * `200 OK` with `{\"ok\": false}` is a failure and only the connector knows that —\n * while the decision about *retrying* stays in one place for all of them.\n *\n * `sleep` is injectable so a test proves the real backoff schedule without\n * waiting for it. A test that actually slept would be slow enough that somebody\n * would eventually shorten the delays to speed it up, and then the thing under\n * test would be the shortened version.\n */\nexport async function deliver<T>(\n send: (attempt: number) => Promise<T>,\n policy: RetryPolicy = DEFAULT_RETRY,\n sleep: (ms: number) => Promise<void> = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n): Promise<DeliveryOutcome<T>> {\n const attempts: Attempt[] = [];\n const budget = Math.max(1, policy.attempts);\n\n for (let attempt = 1; attempt <= budget; attempt++) {\n try {\n const value = await send(attempt);\n\n return { ok: true, value, attempts };\n } catch (error) {\n const classified = (error as { classified?: Classified }).classified ?? classifyError(error);\n\n if (!shouldRetry(classified.kind, policy)) {\n attempts.push({ attempt, kind: classified.kind, detail: classified.detail });\n\n return {\n ok: false,\n attempts,\n kind: classified.kind,\n gaveUp:\n classified.kind === \"ambiguous\"\n ? `${AMBIGUOUS_REFUSAL} (${classified.detail})`\n : classified.detail,\n error,\n };\n }\n\n if (attempt >= budget) {\n attempts.push({ attempt, kind: classified.kind, detail: classified.detail });\n\n return {\n ok: false,\n attempts,\n kind: classified.kind,\n gaveUp: `Gave up after ${attempt} attempts. ${classified.detail}`,\n error,\n };\n }\n\n // Exponential, but the provider's own number wins when it gave one. Ours\n // is a guess; theirs is an instruction, and ignoring it is how a rate\n // limit becomes a ban.\n const backoff = Math.min(policy.baseDelayMs * 2 ** (attempt - 1), policy.maxDelayMs);\n const waitedMs = classified.retryAfter ? Math.max(backoff, classified.retryAfter * 1000) : backoff;\n attempts.push({ attempt, kind: classified.kind, detail: classified.detail, waitedMs });\n await sleep(waitedMs);\n }\n }\n\n return { ok: false, attempts, gaveUp: \"Exhausted every attempt.\" };\n}\n\n/* ── Rate floors ──────────────────────────────────────────────────────────── */\n\nconst lastCallAt = new Map<string, number>();\n\n/**\n * Wait until this channel may be used again.\n *\n * In-process only, and that is the correct scope for a host running on one\n * machine. A host that fans out across processes owns the coordination, because\n * the package cannot see the other senders and pretending otherwise would be a\n * limiter that reports success while doing nothing.\n */\nexport async function respectRate(\n channel: string,\n minIntervalMs: number,\n now: () => number = () => Date.now(),\n sleep: (ms: number) => Promise<void> = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n): Promise<{ waitedMs: number }> {\n if (minIntervalMs <= 0) return { waitedMs: 0 };\n\n const previous = lastCallAt.get(channel);\n const at = now();\n const waitedMs = previous === undefined ? 0 : Math.max(0, previous + minIntervalMs - at);\n\n if (waitedMs > 0) await sleep(waitedMs);\n lastCallAt.set(channel, at + waitedMs);\n\n return { waitedMs };\n}\n\n/** Only for tests — the map is module-global and would leak between them. */\nexport function resetRateState(): void {\n lastCallAt.clear();\n}\n","/**\n * The connector error taxonomy.\n *\n * A host that retries needs to know WHICH KIND of failure it is looking at.\n * \"Something went wrong\" is not enough information to choose between retrying,\n * pausing for a person, and stopping — so every failure is classified at the\n * point it is known, which is inside the connector, not three frames up where\n * the type has already been lost.\n *\n * | class | kind | retry? |\n * |---|---|---|\n * | `ConnectorConfigError` | `rejected` | never — nothing about a second attempt changes an unset key |\n * | `ConnectorAuthError` | `rejected` | never — the credential is wrong; hammering it locks accounts |\n * | `ConnectorRateLimited` | `refused-explicitly` | yes, after `retryAfter` |\n * | `ConnectorTransient` | `refused-explicitly` | yes — a 5xx is the provider saying it did nothing |\n * | `ConnectorAmbiguous` | `ambiguous` | **only** where the connector is idempotent |\n * | `ConnectorUnreachable` | `unreachable` | yes — it never arrived |\n * | `ConnectorRequestError` | `rejected` | never — a 4xx we caused fails the same way twice |\n *\n * ## What changed, and why it is a fix rather than a refactor\n *\n * `ConnectorTransient` used to mean *5xx OR a thrown transport*, with\n * `retryable = true`. Those are opposite cases. A 5xx is the provider reporting\n * that it did nothing; a thrown transport may be a socket that closed after the\n * bytes went out. Retrying the second on a connector with no idempotency key is\n * a silent double write — the failure this whole layer exists to prevent.\n *\n * So the two are separate classes now, `kind` is the primitive, and `retryable`\n * answers the narrow question *is this safe whatever the connector is?* An older\n * caller reading `.retryable` therefore becomes conservative rather than wrong.\n */\n\nimport {\n classifyError,\n classifyStatus,\n isUnconditionallyRetryable,\n type Attempt,\n type Classified,\n type FailureKind,\n} from \"./delivery\";\n\nexport type ConnectorErrorContext = {\n service: string;\n operation: string;\n /** HTTP status when there was one. */\n status?: number;\n /** The provider's own error code, when it publishes one. */\n providerCode?: string;\n};\n\n/** Base for everything below, so a host can catch the family in one clause. */\nexport class ConnectorError extends Error {\n readonly service: string;\n\n readonly operation: string;\n\n readonly status?: number;\n\n readonly providerCode?: string;\n\n /**\n * Every failed attempt of the call, in order. Set on the error a call throws\n * (`callConnector`), absent on one built anywhere else.\n *\n * `declare`, not a field: it is attached with `Object.defineProperty` when the\n * call gives up, so an error that did not end a call carries no such property.\n */\n declare readonly attempts?: Attempt[];\n\n /**\n * What the call declared. With `attempts`, it separates \"never allowed to\n * retry\" from \"retries ran out\". Same presence rule as `attempts`.\n */\n declare readonly idempotent?: boolean;\n\n /**\n * What kind of failure this is. The primitive every retry decision reads.\n *\n * Defaults to `ambiguous` on the base class deliberately: an unclassified\n * failure must not be assumed safe.\n */\n readonly kind: FailureKind = \"ambiguous\";\n\n /**\n * `options.cause` is the standard `Error` cause — what this error was built\n * from. A failed call's error carries the classified error there, and a thrown\n * transport's carries the original exception, so nothing below is lost when\n * the message is rewritten for a person.\n */\n constructor(message: string, ctx: ConnectorErrorContext, options?: ErrorOptions) {\n // Only pass options that HAVE a cause: `{ cause: undefined }` still installs\n // an own `cause` property, which reads as \"caused by undefined\".\n super(message, options?.cause === undefined ? undefined : { cause: options.cause });\n this.name = new.target.name;\n this.service = ctx.service;\n this.operation = ctx.operation;\n this.status = ctx.status;\n this.providerCode = ctx.providerCode;\n }\n\n /**\n * Safe to retry **without knowing anything about the connector**.\n *\n * Ambiguous failures answer `false` here even though they may be retryable on\n * an idempotent connector — ask `shouldRetry(error.kind, { idempotent })` for\n * that. This getter is the conservative half on purpose.\n */\n get retryable(): boolean {\n return isUnconditionallyRetryable(this.kind);\n }\n\n /** The classification, in the shape `deliver()` reads off a thrown error. */\n get classified(): Classified {\n return { kind: this.kind, detail: this.message };\n }\n}\n\n/**\n * A required piece of configuration is missing or unusable.\n *\n * The message must name the exact key the consumer has to set. \"Stripe is not\n * configured\" sends someone reading source; \"no `secretKey` on the `stripe`\n * connection\" sends them to the line.\n */\nexport class ConnectorConfigError extends ConnectorError {\n override readonly kind: FailureKind = \"rejected\";\n}\n\n/** The provider rejected the credential. Retrying cannot help. */\nexport class ConnectorAuthError extends ConnectorError {\n override readonly kind: FailureKind = \"rejected\";\n}\n\n/** The provider asked us to slow down. It did nothing, and said so. */\nexport class ConnectorRateLimited extends ConnectorError {\n override readonly kind: FailureKind = \"refused-explicitly\";\n\n /** Seconds to wait, when the provider said. */\n readonly retryAfter?: number;\n\n constructor(message: string, ctx: ConnectorErrorContext & { retryAfter?: number }, options?: ErrorOptions) {\n super(message, ctx, options);\n this.retryAfter = ctx.retryAfter;\n }\n\n override get classified(): Classified {\n return {\n kind: this.kind,\n detail: this.message,\n ...(this.retryAfter === undefined ? {} : { retryAfter: this.retryAfter }),\n };\n }\n}\n\n/**\n * A 5xx. The provider answered, and its answer was \"not now\".\n *\n * Named for what it is rather than for how it feels: this is an *explicit\n * refusal*, which is why it is always safe to repeat. A timeout feels the same\n * to the caller and is not the same thing — see `ConnectorAmbiguous`.\n */\nexport class ConnectorTransient extends ConnectorError {\n override readonly kind: FailureKind = \"refused-explicitly\";\n}\n\n/**\n * The request never reached the provider — DNS, refused connection, a socket\n * closed before anything went out.\n *\n * Always safe to repeat, and the only transport-level failure of which that is\n * true.\n */\nexport class ConnectorUnreachable extends ConnectorError {\n override readonly kind: FailureKind = \"unreachable\";\n}\n\n/**\n * Nobody can tell whether the provider acted on it. A timeout, an abort, an\n * error nothing recognises.\n *\n * **Retryable only where the connector declared that repeating a request is\n * harmless.** On anything else this is reported for a person, because the\n * alternative is a duplicate that nobody will ever trace back to a bug.\n */\nexport class ConnectorAmbiguous extends ConnectorError {\n override readonly kind: FailureKind = \"ambiguous\";\n}\n\n/** A 4xx we caused. The same request will fail the same way. */\nexport class ConnectorRequestError extends ConnectorError {\n override readonly kind: FailureKind = \"rejected\";\n}\n\n/**\n * Classify an HTTP response into the taxonomy.\n *\n * 429 before the 4xx sweep, deliberately — see `classifyStatus`.\n */\nexport function classifyHttp(\n status: number,\n ctx: ConnectorErrorContext,\n body: string,\n retryAfter?: number,\n): ConnectorError {\n const detail = body.trim() === \"\" ? \"\" : ` — ${truncate(body, 400)}`;\n const where = `${ctx.service}.${ctx.operation}`;\n\n if (status === 429) {\n return new ConnectorRateLimited(`${where}: rate limited by the provider${detail}`, {\n ...ctx,\n status,\n ...(retryAfter === undefined ? {} : { retryAfter }),\n });\n }\n\n if (status === 401 || status === 403) {\n return new ConnectorAuthError(\n `${where}: the provider rejected the credential (${status})${detail}. ` +\n \"Check the credentials and that they match the mode you are running in — \" +\n \"a live key in sandbox, or the reverse, fails exactly like this.\",\n { ...ctx, status },\n );\n }\n\n if (status >= 500) {\n return new ConnectorTransient(`${where}: provider returned ${status}${detail}`, { ...ctx, status });\n }\n\n return new ConnectorRequestError(`${where}: request rejected with ${status}${detail}`, { ...ctx, status });\n}\n\n/**\n * Turn a thrown transport failure into the right class.\n *\n * The default is `ConnectorAmbiguous`, not `ConnectorTransient`. That single\n * default is the difference between \"a flaky network costs us a retry\" and \"a\n * flaky network costs someone a duplicate charge\".\n */\nexport function classifyThrown(cause: unknown, ctx: ConnectorErrorContext): ConnectorError {\n const classified = classifyError(cause);\n const message = `${ctx.service}.${ctx.operation}: ${classified.detail}`;\n\n // The original goes on as `cause`, as the PHP twin has always passed it as\n // `$previous`: a Node error code or an undici stack is exactly what someone\n // debugging an ambiguous failure needs, and the message keeps only its text.\n return classified.kind === \"unreachable\"\n ? new ConnectorUnreachable(message, ctx, { cause })\n : new ConnectorAmbiguous(message, ctx, { cause });\n}\n\n/**\n * An HTTP failure carrying its classification, for connectors that decide what\n * counts as a failure themselves.\n *\n * Telegram answers `200 OK` with `{\"ok\": false}` for a real refusal, so its\n * connector has to raise the failure itself — and when it does, it must be\n * classified the same way every other failure is. `httpFailure(400, …)` is how\n * it says \"this is a real no\" without inventing a second vocabulary.\n */\nexport function httpFailure(status: number, body: string, retryAfter?: string | null): Error {\n const classified = classifyStatus(status, body, retryAfter);\n const error = new Error(classified.detail) as Error & { classified: Classified };\n error.classified = classified;\n\n return error;\n}\n\nfunction truncate(value: string, max: number): string {\n return value.length <= max ? value : `${value.slice(0, max)}…`;\n}\n","/**\n * The faker layer — every connector ships one, always.\n *\n * ## Why every connector, and not just the ones without a sandbox\n *\n * A sandbox still needs an account, a key, a network, and a provider that is\n * up. That is four ways for someone evaluating the kit to get nothing working,\n * before they have learned anything about the kit. A faker removes all four:\n * vendor a node, drop it on a canvas, press run, see a shaped result.\n *\n * It is also what makes the golden fixtures honest. Fixtures run on both\n * runtimes and are the publish gate; if they needed a network they would either\n * be skipped in CI or be flaky, and a flaky gate is one people learn to\n * re-run rather than read.\n *\n * ## Deterministic, not random\n *\n * Same inputs, same output — always. A faker that returns a fresh uuid every\n * call cannot be asserted on, so its fixtures degrade to \"it did not throw\",\n * which is the assertion that catches nothing. Values here are derived from a\n * seed built out of the service, the operation and the request, so two runs\n * agree and two runtimes can be compared to each other.\n *\n * The values are obviously fake ON PURPOSE — `fake_`-prefixed ids, `example.test`\n * hosts, round numbers. Nobody should ever look at a faked result and wonder\n * whether it moved real money.\n */\n\n/** A faker: one function per service, switching on the operation. */\nexport type ConnectorFaker = (operation: string, request: FakeRequest) => unknown;\n\nexport type FakeRequest = {\n /** The node's resolved config. */\n config: Record<string, unknown>;\n /** The value on the incoming wire. */\n input?: unknown;\n /** Deterministic helpers, seeded from service + operation + config. */\n fake: FakeValues;\n};\n\n/**\n * Deterministic value helpers handed to a faker.\n *\n * Small on purpose. A faker's job is to return the SHAPE the provider returns —\n * the field names a downstream node will reference — not to simulate the\n * provider's business logic.\n */\nexport type FakeValues = {\n /** A stable id with the provider's usual prefix: `id(\"ch\")` → `ch_fake_1a2b3c`. */\n id: (prefix: string) => string;\n /** A stable integer in `[min, max]`. */\n int: (min: number, max: number) => number;\n /** Pick a stable element of a list. */\n pick: <T>(options: readonly T[]) => T;\n /** A fixed ISO-8601 instant, offset by whole seconds. Never `Date.now()`. */\n timestamp: (offsetSeconds?: number) => string;\n /** A stable lowercase hex string of `length` characters. */\n hex: (length: number) => string;\n};\n\n/**\n * The instant every faker counts from.\n *\n * A constant rather than the clock, because a fixture asserting on `createdAt`\n * must not start failing tomorrow. Chosen to be recognisably synthetic.\n */\nexport const FAKE_EPOCH = \"2026-01-01T00:00:00.000Z\";\n\n/** FNV-1a. Small, dependency-free, and stable across both runtimes. */\nexport function seedFrom(...parts: unknown[]): number {\n const text = parts.map((part) => (typeof part === \"string\" ? part : stableJson(part))).join(\"|\");\n let hash = 0x811c9dc5;\n\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n // 32-bit FNV prime multiply, kept in range without BigInt.\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n\n return hash >>> 0;\n}\n\n/**\n * Key order must not change a seed.\n *\n * `JSON.stringify` preserves insertion order, so `{a,b}` and `{b,a}` would hash\n * differently and the \"same inputs, same output\" promise would hold only for\n * objects that happened to be built in the same order — which is exactly the\n * kind of almost-true that survives review and fails in a fixture months later.\n */\nfunction stableJson(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stableJson).join(\",\")}]`;\n\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableJson(v)}`);\n\n return `{${entries.join(\",\")}}`;\n}\n\n/** Build the deterministic helpers for one faked call. */\nexport function fakeValues(seed: number): FakeValues {\n // A tiny xorshift, advanced per call so successive helpers differ while the\n // whole sequence stays a pure function of the seed.\n let state = seed || 0x9e3779b9;\n const next = (): number => {\n state ^= state << 13;\n state >>>= 0;\n state ^= state >>> 17;\n state ^= state << 5;\n state >>>= 0;\n\n return state;\n };\n\n const hex = (length: number): string => {\n let out = \"\";\n while (out.length < length) out += next().toString(16).padStart(8, \"0\");\n\n return out.slice(0, length);\n };\n\n return {\n hex,\n id: (prefix: string) => `${prefix}_fake_${hex(12)}`,\n int: (min: number, max: number) => min + (next() % Math.max(1, max - min + 1)),\n pick: <T>(options: readonly T[]): T => options[next() % options.length] as T,\n timestamp: (offsetSeconds = 0) =>\n new Date(Date.parse(FAKE_EPOCH) + offsetSeconds * 1000).toISOString(),\n };\n}\n\n/** Build the request handed to a faker, with helpers already seeded. */\nexport function fakeRequest(\n service: string,\n operation: string,\n config: Record<string, unknown>,\n input?: unknown,\n): FakeRequest {\n return { config, input, fake: fakeValues(seedFrom(service, operation, config)) };\n}\n","/**\n * Which copy of a provider a connector talks to — and who decides.\n *\n * Three modes, and the third is the one that makes this marketplace usable:\n *\n * | mode | talks to | needs credentials | needs network |\n * |-----------|-----------------------------|-------------------|---------------|\n * | `live` | the provider, for real | yes | yes |\n * | `sandbox` | the provider's test estate | yes (test ones) | yes |\n * | `fake` | the node's own faker | no | no |\n *\n * ## The environment is the DEFAULT, never the constraint\n *\n * A locally-hosted project defaults to sandbox. It is not *pinned* to sandbox:\n * an author who explicitly asks for `live` on their laptop gets `live`, because\n * the alternative — an environment that silently overrides a stated intention —\n * produces the worst outcome available here, which is a workflow that reports\n * success while having charged nobody. Equally, `sandbox` set explicitly stays\n * sandbox in production, which is how you stage a connector before cutting it\n * over.\n *\n * So: explicit beats the connection, the connection beats the environment.\n *\n * ## Why \"fake\" is a MODE and not a test double\n *\n * Every connector ships a faker (see `faker.ts`), and it is reachable the same\n * way in a test, in a demo and on a laptop with no credentials. Making it a\n * separate mechanism — a mock injected only under test — would mean the code\n * path a consumer develops against is not the one that runs, and the fixtures\n * would prove nothing about the executor.\n */\n\n/** The three environments a connector node can run against. */\nexport type ConnectorMode = \"fake\" | \"sandbox\" | \"live\";\n\n/** What a node's `mode` config field may say. `auto` means \"let the rules decide\". */\nexport type RequestedMode = ConnectorMode | \"auto\";\n\nexport const CONNECTOR_MODES: ConnectorMode[] = [\"fake\", \"sandbox\", \"live\"];\n\n/**\n * How a provider exposes its test estate — the four shapes that actually exist.\n *\n * This is data, not per-node code, because getting it wrong is the difference\n * between hitting a test ledger and hitting a real one. Verified per provider in\n * `.ai/plans/fancy-flow-connector-nodes.md`; a node declares which of the four\n * it is and the core does the rest.\n *\n * - `credential` — same base URL, a test-scoped key selects the estate\n * (Stripe's `sk_test_…`). The trap: pointing a live key at it works, so the\n * only guard is the key itself.\n * - `base-url` — a different host entirely (PayPal's `api-m.sandbox.paypal.com`).\n * - `separate-account` — a distinct tenant you must create, often with its own\n * login host (Salesforce sandbox orgs on `test.salesforce.com`).\n * - `restricted-reach` — there is no separate estate at all; what the provider\n * offers instead is an **account-level restriction on who can see the\n * result**. A Meta app in Development Mode; an unaudited TikTok app that can\n * only post `SELF_ONLY`. Same credentials, same endpoints, same estate — only\n * the audience changes, and nothing in a request selects it.\n *\n * **This is the dangerous one**, and it is why it is not folded into `none`.\n * It is the shape most likely to be mistaken for a sandbox, because it looks\n * exactly like a successful post that nobody can see: `ok: true`, a real id,\n * no audience. `none` says \"there is nothing\"; this says \"there is something,\n * it is not selectable from here, and success does not mean reach\".\n * - `none` — the provider has no test estate. `sandbox` is then not offered at\n * all, and the honest choices are `fake` and `live`.\n * - `unverified` — **nobody has checked yet.** A real state, and the type has to\n * carry it: this is the field where being wrong sends someone to a live\n * estate believing it is a test one, so \"researched but not verified\" must not\n * be forced to pick one of the other five. It belongs on a provider declared\n * `implemented: false`; on one declared `implemented: true` it is a finding,\n * and `providerProblems()` reports it.\n */\nexport type SandboxKind =\n | \"credential\"\n | \"base-url\"\n | \"separate-account\"\n | \"restricted-reach\"\n | \"none\"\n | \"unverified\";\n\n/** Every value, as data, for a host validating input that crossed a JSON boundary. */\nexport const SANDBOX_KINDS: readonly SandboxKind[] = [\n \"credential\",\n \"base-url\",\n \"separate-account\",\n \"restricted-reach\",\n \"none\",\n \"unverified\",\n];\n\n/**\n * Kinds a caller can actually SELECT with `mode: \"sandbox\"`.\n *\n * The other three are not a smaller version of the same thing - each is a\n * different answer to \"what test estate is there\", and none of them gives the\n * resolver anything to point at:\n *\n * - `none` - the provider has no test estate. Honest, and common.\n * - `restricted-reach` - there IS a safer way to run, but it is an\n * account-level setting on the provider's side, not something a request can\n * choose. Selecting it here would be a lie.\n * - `unverified` - nobody has checked. Refusing is the safe direction, because\n * the cost of guessing wrong is a workflow pointed at a live estate.\n */\nconst SELECTABLE_SANDBOX: ReadonlySet<SandboxKind> = new Set<SandboxKind>([\n \"credential\",\n \"base-url\",\n \"separate-account\",\n]);\n\nexport function sandboxIsSelectable(kind: SandboxKind): boolean {\n return SELECTABLE_SANDBOX.has(kind);\n}\n\n/**\n * Why `mode: \"sandbox\"` cannot be honoured for this kind, in the words the\n * person reading it needs.\n *\n * Three reasons, three messages. Collapsing them into \"no sandbox available\"\n * would hide the one that matters: `restricted-reach` is not an absence, it is\n * a different mechanism with a trap in it.\n */\nexport function sandboxRefusal(service: string, kind: SandboxKind): string {\n if (kind === \"restricted-reach\") {\n return (\n service +\n \" has no separate test estate. What it has instead is an ACCOUNT-LEVEL reach restriction - an unreviewed \" +\n \"app that posts only to its own developers, or only privately. Same credentials, same endpoints, same \" +\n \"estate: only the audience changes, and nothing in a request selects it. That makes it the sandbox shape \" +\n \"most likely to be mistaken for a real one, because a restricted call looks exactly like a successful \" +\n 'one - ok, a real id, and no audience. Use \"fake\" to develop without touching the provider, or \"live\" ' +\n \"and confirm reach on the provider's own surface, which is the only place it is visible.\"\n );\n }\n\n if (kind === \"unverified\") {\n return (\n \"Nobody has verified what test estate \" +\n service +\n ' offers, so \"sandbox\" cannot be honoured - the resolver would be guessing, and the cost of guessing ' +\n \"wrong here is a workflow pointed at a live estate while somebody believes it is a test one. Find out \" +\n 'and declare it, or use \"fake\".'\n );\n }\n\n return (\n service +\n ' has no sandbox estate, so \"sandbox\" cannot be honoured. ' +\n 'Use \"fake\" to develop without credentials, or \"live\" to talk to the provider.'\n );\n}\n\n/** The host's view of where it is running. Kept tiny so it is trivial to fake. */\nexport type HostEnvironment = {\n /** True only in a real production deployment. Everything else is \"local\". */\n production: boolean;\n};\n\nexport type ModeInputs = {\n /** What the node's config asked for. `undefined` / `\"auto\"` means \"decide\". */\n requested?: RequestedMode | null;\n /** A mode pinned on the CONNECTION, if the host pinned one. */\n connectionMode?: ConnectorMode | null;\n /** Whether the provider has a test estate at all. */\n sandbox: SandboxKind;\n /** Whether the connection actually carries credentials for the sandbox estate. */\n hasSandboxCredentials: boolean;\n environment: HostEnvironment;\n};\n\n/**\n * Resolve the mode a connector call runs in.\n *\n * Deliberately a pure function of stated facts, so both runtimes can be held to\n * the same table of cases and a host can unit-test its own wiring without a\n * network or a provider.\n */\nexport function resolveConnectorMode(inputs: ModeInputs): ConnectorMode {\n const { requested, connectionMode, sandbox, hasSandboxCredentials, environment } = inputs;\n\n // 1. An explicit ask wins everywhere. This is the rule that makes the\n // environment a default rather than a cage.\n if (requested && requested !== \"auto\") {\n if (requested === \"sandbox\" && !sandboxIsSelectable(sandbox)) {\n throw new ConnectorModeError(sandboxRefusal(\"This connector's provider\", sandbox));\n }\n\n return requested;\n }\n\n // 2. A mode pinned on the connection. One place per service, which is the\n // whole reason connections exist as a separate thing.\n if (connectionMode) return connectionMode;\n\n // 3. Otherwise the environment decides.\n if (environment.production) return \"live\";\n\n // Local: prefer the provider's own test estate when it exists AND is wired.\n // Falling through to `fake` when it is not is what makes a freshly vendored\n // node runnable with no setup at all — the difference between a marketplace\n // you can try and one you can only read about.\n return sandboxIsSelectable(sandbox) && hasSandboxCredentials ? \"sandbox\" : \"fake\";\n}\n\n/** Raised when a requested mode cannot be honoured. Never downgraded silently. */\nexport class ConnectorModeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConnectorModeError\";\n }\n}\n\n/** True when the mode reaches the provider over the network. */\nexport function isRemote(mode: ConnectorMode): boolean {\n return mode !== \"fake\";\n}\n","/**\n * A CONNECTION — credentials plus an environment choice — configured once per\n * service and referenced by every node that uses it.\n *\n * ## Why this is not per-node config\n *\n * The obvious design is to put an API key field on each node. It is also wrong,\n * for three reasons that only show up later:\n *\n * 1. **Rotation.** A key on twelve nodes is twelve edits and one you will miss.\n * 2. **The sandbox/live switch has to live somewhere singular.** If each node\n * carries its own, a graph can be half in the test estate and half in the\n * real one, and nothing about the canvas shows it.\n * 3. **Credentials would be in the graph.** A `WorkflowSchema` is plain JSON\n * that gets exported, committed, MCP'd to an agent and pasted into issues.\n * Nothing secret may ever be in it.\n *\n * So a node's config stores a **connection id** — an opaque, non-secret name\n * like `\"stripe\"` or `\"stripe-eu\"` — and the host resolves it to credentials at\n * run time. This is the same arrangement fancy-flow already uses for LLM clients\n * and `fancy-git` uses for providers: the node cannot reach for a credential,\n * the host has to hand one over.\n *\n * ```ts\n * registerConnectionHost({\n * environment: { production: process.env.NODE_ENV === \"production\" },\n * connections: {\n * stripe: {\n * service: \"stripe\",\n * live: { secretKey: process.env.STRIPE_SECRET_KEY },\n * sandbox: { secretKey: process.env.STRIPE_TEST_SECRET_KEY },\n * },\n * },\n * });\n * ```\n *\n * Note what is NOT here: no key, no token, no placeholder that looks like one.\n * Credentials are configuration, and configuration comes from the consumer's\n * environment.\n */\n\nimport { ConnectorConfigError } from \"./errors\";\nimport {\n ConnectorModeError,\n resolveConnectorMode,\n sandboxIsSelectable,\n sandboxRefusal,\n type ConnectorMode,\n type HostEnvironment,\n type RequestedMode,\n type SandboxKind,\n} from \"./mode\";\n\n/**\n * Credentials for one estate. Values are opaque strings the node's auth\n * strategy understands — `secretKey`, `clientId`/`clientSecret`, `token`.\n */\nexport type ConnectionCredentials = Record<string, string | undefined>;\n\n/** One configured connection to one service. */\nexport type ConnectionSpec = {\n /** The service this connection is for — must match the node's `connector.service`. */\n service: string;\n /** Human label, shown in the picker. Defaults to the id. */\n label?: string;\n /**\n * Pin this connection to a mode. Optional, and usually absent: leaving it\n * unset is what lets the environment supply the default.\n */\n mode?: ConnectorMode;\n live?: ConnectionCredentials;\n sandbox?: ConnectionCredentials;\n /**\n * Override the base URL per mode. Only needed for a `separate-account`\n * provider whose tenant has its own host (a Salesforce instance URL); the\n * `base-url` providers carry theirs in the node's service descriptor.\n */\n baseUrl?: Partial<Record<ConnectorMode, string>>;\n};\n\nexport type ConnectionHost = {\n environment: HostEnvironment;\n connections: Record<string, ConnectionSpec>;\n};\n\nlet host: ConnectionHost | null = null;\n\n/**\n * Install the host's connections. Returns an unregister function.\n *\n * One registration for the whole app, matching how a person thinks about it:\n * you authorize Stripe once, then every node that needs Stripe already has it.\n */\nexport function registerConnectionHost(next: ConnectionHost | null): () => void {\n host = next;\n\n return () => {\n if (host === next) host = null;\n };\n}\n\nexport function getConnectionHost(): ConnectionHost | null {\n return host;\n}\n\n/** Connection ids the host has configured for a service — feeds the picker. */\nexport function connectionsFor(service: string): Array<{ id: string; label: string }> {\n const current = getConnectionHost();\n if (!current) return [];\n\n return Object.entries(current.connections)\n .filter(([, spec]) => spec.service === service)\n .map(([id, spec]) => ({ id, label: spec.label ?? id }));\n}\n\n/** What a node's executor actually needs, once everything has been resolved. */\nexport type ResolvedConnection = {\n id: string;\n service: string;\n mode: ConnectorMode;\n /** Empty in `fake` mode — there is nothing to authenticate against. */\n credentials: ConnectionCredentials;\n /** Undefined in `fake` mode. */\n baseUrl?: string;\n};\n\nexport type ResolveOptions = {\n service: string;\n operation: string;\n /** The connection id from host config. Defaults to the service name. */\n connectionId?: string | null;\n /** The mode the caller asked for. */\n requested?: RequestedMode | null;\n /**\n * Credentials handed straight in, bypassing the registry entirely.\n *\n * The primary path for a host that already owns credential storage and has no\n * wish to hand a package a second copy: it passes them per call, this resolver\n * does not consult any global, and there is nothing to initialise. The\n * registry below remains for hosts that prefer to configure once.\n *\n * An EMPTY object is not the same as `null`: it means \"the caller supplied\n * credentials and there are none\", which fails the `requires` check loudly\n * rather than falling through to the registry and silently using someone\n * else's.\n */\n credentials?: ConnectionCredentials | null;\n sandbox: SandboxKind;\n /** Base URL per mode, from the node's service descriptor. */\n baseUrls?: Partial<Record<ConnectorMode, string>>;\n /** Credential keys that must be present for a remote call. */\n requires?: string[];\n};\n\n/**\n * Resolve the connection and mode for one call.\n *\n * ## `fake` needs no host at all\n *\n * A freshly vendored connector runs before anything is configured, and that is\n * deliberate: it is the difference between a marketplace someone can try and\n * one they can only read about. So the no-host case resolves to `fake` rather\n * than throwing — UNLESS the author explicitly asked for a remote mode, in\n * which case the ask is honoured by failing loudly instead of quietly doing\n * something else.\n */\nexport function resolveConnection(options: ResolveOptions): ResolvedConnection {\n const { service, operation, sandbox } = options;\n const requestedMode = options.requested ?? \"auto\";\n\n // Checked FIRST, before anything about the host, because it is a static fact\n // about the SERVICE. Asking for a sandbox that does not exist should say so\n // whether or not a connection happens to be configured — otherwise the same\n // mistake produces two different errors depending on unrelated state, and the\n // more useful one is the one you never see.\n if (requestedMode === \"sandbox\" && !sandboxIsSelectable(sandbox)) {\n throw new ConnectorModeError(sandboxRefusal(service, sandbox));\n }\n\n const id = options.connectionId?.trim() || service;\n\n // Explicit credentials win over everything. A host that passes them has\n // already made every decision this resolver would otherwise make, and reading\n // a global on top of that is how two sources of truth start disagreeing.\n if (options.credentials) {\n // Asking for the faker wins even here — an explicit `fake` is a statement,\n // and credentials sitting in the same call do not override it. Nothing\n // secret is carried into the fake path.\n if (requestedMode === \"fake\") return { id, service, mode: \"fake\", credentials: {} };\n\n // Otherwise: supplying credentials IS the statement that a real call is\n // intended, so `auto` resolves to `live` rather than consulting an\n // environment this resolver was told nothing about. `sandbox` is honoured\n // as asked.\n const mode: ConnectorMode = requestedMode === \"auto\" ? \"live\" : requestedMode;\n const missing = (options.requires ?? []).filter((key) => !options.credentials?.[key]);\n\n if (missing.length > 0) {\n throw new ConnectorConfigError(\n `${service}.${operation} was given credentials with no ${missing.join(\", \")}. ` +\n \"Nothing is inferred from an incomplete credential set — a partial one is a configuration mistake, \" +\n \"and quietly falling back to another source is how the wrong account gets written to.\",\n { service, operation },\n );\n }\n\n const explicitBase = options.baseUrls?.[mode];\n\n return {\n id,\n service,\n mode,\n credentials: options.credentials,\n ...(explicitBase ? { baseUrl: explicitBase } : {}),\n };\n }\n\n const current = getConnectionHost();\n const spec = current?.connections[id];\n\n if (spec && spec.service !== service) {\n throw new ConnectorConfigError(\n `connection \"${id}\" is configured for the \"${spec.service}\" service, but this node needs \"${service}\".`,\n { service, operation },\n );\n }\n\n if (!current || !spec) {\n if (requestedMode !== \"auto\" && requestedMode !== \"fake\") {\n throw new ConnectorConfigError(\n `no \"${id}\" connection is registered, so ${service}.${operation} cannot run in \"${requestedMode}\" mode. ` +\n \"Call registerConnectionHost({ connections: { \" +\n `${id}: { service: \"${service}\", live: { … } } } }) — the node has no credentials of its own ` +\n 'and must not invent any. Set the node\\'s mode to \"fake\" to develop without one.',\n { service, operation },\n );\n }\n\n return { id, service, mode: \"fake\", credentials: {} };\n }\n\n const sandboxCreds = spec.sandbox ?? {};\n const mode = resolveConnectorMode({\n requested: requestedMode,\n connectionMode: spec.mode ?? null,\n sandbox,\n hasSandboxCredentials: hasAll(sandboxCreds, options.requires ?? []),\n environment: current.environment,\n });\n\n if (mode === \"fake\") {\n return { id, service, mode, credentials: {} };\n }\n\n const credentials = (mode === \"sandbox\" ? spec.sandbox : spec.live) ?? {};\n const missing = (options.requires ?? []).filter((key) => !credentials[key]);\n\n if (missing.length > 0) {\n // Never silently degrade to the faker here. A run that \"succeeded\" because\n // it quietly stopped talking to the provider is the worst failure this\n // whole design exists to prevent — it is green, it is wrong, and nothing\n // reports it.\n throw new ConnectorConfigError(\n `connection \"${id}\" has no ${missing.join(\", \")} for ${mode} mode. ` +\n `Set connections.${id}.${mode}.${missing[0]}, or run this node in \"fake\" mode.`,\n { service, operation },\n );\n }\n\n const baseUrl = spec.baseUrl?.[mode] ?? options.baseUrls?.[mode];\n\n return { id, service, mode, credentials, ...(baseUrl ? { baseUrl } : {}) };\n}\n\nfunction hasAll(creds: ConnectionCredentials, keys: string[]): boolean {\n return keys.length > 0 && keys.every((key) => Boolean(creds[key]));\n}\n","/**\n * The one call path every connector uses.\n *\n * A connector never chooses between \"call the provider\" and \"call the faker\" —\n * it describes the request it wants and hands it here. That is the point: the\n * code path a consumer develops against with no credentials is the same code\n * path that runs in production, so fixtures prove something about the connector\n * rather than about a mock.\n *\n * ```ts\n * const charge = await callConnector(STRIPE, {\n * operation: \"charge_create\",\n * credentials,\n * request: { method: \"POST\", path: \"/v1/charges\", form: { amount, currency } },\n * idempotencyKey,\n * idempotent: true,\n * });\n * ```\n *\n * ## What changed from the first version of this runtime, and why\n *\n * The old loop caught a thrown transport, called it transient, and retried it.\n * A thrown transport includes a **timeout**, which may be a request the provider\n * received and acted on — so on a connector with no idempotency key that retry\n * is a silent double write. Retrying is now a function of the classification AND\n * the connector's declared idempotency, and `idempotent` defaults to `false`\n * because a connector that has not thought about it must not be assumed safe.\n *\n * See `delivery.ts` for the full table.\n */\n\nimport {\n DEFAULT_RETRY,\n deliver,\n type Attempt,\n type Classified,\n type DeliveryOutcome,\n type FailureKind,\n} from \"./delivery\";\nimport {\n classifyHttp,\n classifyThrown,\n ConnectorAmbiguous,\n ConnectorAuthError,\n ConnectorConfigError,\n ConnectorError,\n ConnectorRateLimited,\n ConnectorRequestError,\n ConnectorTransient,\n ConnectorUnreachable,\n type ConnectorErrorContext,\n} from \"./errors\";\nimport { fakeRequest, type ConnectorFaker } from \"./faker\";\nimport type { ConnectorMode, RequestedMode, SandboxKind } from \"./mode\";\nimport {\n resolveConnection,\n type ConnectionCredentials,\n type ResolvedConnection,\n} from \"./connection\";\n\n/**\n * Everything true of a SERVICE rather than of one operation.\n *\n * One per provider, declared once and shared by that provider's connectors. It\n * is data — base URLs, required credential keys, how the sandbox is selected —\n * so that facts verified against provider documentation live in exactly one\n * reviewable place instead of being retyped into each connector.\n */\nexport type ServiceDescriptor = {\n service: string;\n title: string;\n sandbox: SandboxKind;\n /**\n * Base URL per mode. A `credential`-sandbox provider gives the same URL for\n * both, which is itself worth writing down: it is why a live key pointed at\n * \"sandbox\" quietly reaches production.\n */\n baseUrls: Partial<Record<ConnectorMode, string>>;\n /** Credential keys a remote call cannot proceed without. */\n requires: string[];\n /**\n * Apply the provider's auth scheme to an outgoing request.\n *\n * A FUNCTION rather than a declarative header name, because there is no common\n * shape: the key can be a header (under any of a dozen names), a Basic\n * username with a blank password, a query parameter, a body field, or a URL\n * path segment — and several providers need more than one at once. It receives\n * the resolved mode too, because for some providers auth and estate are the\n * same decision expressed in the URL.\n */\n authorize: (\n credentials: ConnectionCredentials,\n request: PreparedRequest,\n mode: ConnectorMode,\n ) => void | Promise<void>;\n /** The service's faker. Required — see `faker.ts`. */\n faker: ConnectorFaker;\n /** Header the provider uses for idempotency, when it has one. */\n idempotencyHeader?: string;\n /**\n * Where this provider puts its OWN error code on a failed response, when it\n * publishes one — read only for a status of 400 or above, and carried as\n * `error.providerCode`.\n *\n * **Declared per service, never guessed.** The shapes do not agree, and the\n * near-misses are the dangerous part: Bluesky's `error` is a code\n * (`\"AuthenticationRequired\"`), Mastodon's `error` is a sentence\n * (`\"The access token is invalid\"`), Discord's code is a number under `code`,\n * and some providers use a header. A generic reader would publish Mastodon's\n * sentence as a code — the plausible answer that is wrong. With no\n * declaration the field is absent, which says *nobody said where to look*.\n *\n * Return a string or an integer (carried as its decimal string); anything\n * else, a blank string, or a throw, leaves the code absent. **A throw never\n * reaches the call**: a code is a detail on an answer the status already gave,\n * and a reader choking on an HTML error page must not turn an explicit `401`\n * into an unclassified failure.\n */\n providerCodeFrom?: (response: TransportResponse) => string | number | null | undefined;\n};\n\nexport type ConnectorRequest = {\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n query?: Record<string, string | number | boolean | undefined>;\n headers?: Record<string, string>;\n /** JSON body. Mutually exclusive with `form`. */\n json?: unknown;\n /** `application/x-www-form-urlencoded` body — what several payment APIs want. */\n form?: Record<string, string | number | boolean | undefined>;\n};\n\nexport type PreparedRequest = {\n method: string;\n url: string;\n headers: Record<string, string>;\n body?: string;\n};\n\n/** The HTTP seam. A host may swap it; nothing here assumes a browser or Node. */\nexport type Transport = (request: PreparedRequest) => Promise<TransportResponse>;\n\nexport type TransportResponse = {\n status: number;\n headers: Record<string, string>;\n body: string;\n};\n\nexport type CallOptions = {\n operation: string;\n /**\n * Credentials, passed IN.\n *\n * **Nothing in this package reads `process.env`.** A host stores values\n * wherever it stores values — a gitignored file behind a whitelist, a secret\n * manager, a Laravel config — and hands them over per call. A package that\n * reached for the environment itself would bypass that discipline entirely,\n * and a test in this repo asserts that it does not.\n *\n * Omit to resolve through a registered connection host instead; see\n * `connection.ts`.\n */\n credentials?: ConnectionCredentials;\n /** Which estate to use. Resolved from the connection host when omitted. */\n // `RequestedMode`, not `ConnectorMode`: this value is passed straight to\n // `resolveConnection`, which takes the wider type, and `resolveConnectorMode`\n // handles `\"auto\"` explicitly. Typed narrowly, the most common value a caller\n // has -- `\"auto\"` is the defaultConfig of every connector node -- was the one\n // it could not pass, while working perfectly at runtime.\n mode?: RequestedMode;\n /** Connection id, for the registered-host path. */\n connectionId?: string | null;\n /**\n * The caller's own config for this call.\n *\n * Passed to the faker so its output is deterministic, and read for\n * `connection` and `mode` when those were not given explicitly — which is what\n * a workflow node stores them in. Explicit arguments always win; this is a\n * convenience for a host whose configuration IS a config object, not a second\n * source of truth.\n */\n config?: Record<string, unknown>;\n input?: unknown;\n request: ConnectorRequest;\n /**\n * Value for the provider's idempotency header. Derive it from the run and the\n * step, never a fresh uuid: the entire point is that a RETRY carries the same\n * key. See `idempotency.ts`.\n */\n idempotencyKey?: string;\n /**\n * Whether repeating this exact request is harmless.\n *\n * **Defaults to `false`.** The only thing that makes an ambiguous failure\n * retryable, so it is opt-in and has to be true because the provider makes it\n * true — not because a retry would be convenient.\n */\n idempotent?: boolean;\n /** Retry budget for retryable failures. Total attempts, including the first. */\n attempts?: number;\n /** Override the transport for this call. Otherwise the registered one, then `fetch`. */\n transport?: Transport;\n /**\n * Called for each FAILED attempt once the call has finished, so a host can\n * journal what actually happened. A call that worked on the third try is a\n * different operational fact from one that worked immediately.\n *\n * A callback rather than a field on the result, because the result is a\n * published shape and this is a diagnostic — see `ConnectorResult`.\n */\n onAttempt?: (attempt: Attempt) => void;\n};\n\n/**\n * What a connector call returns, alongside the provider's own payload.\n *\n * The mode is REPORTED, never inferred by the caller. A call that emitted the\n * provider's data alone would leave every downstream reader — a human, an agent,\n * a log — unable to tell a faked result from a real one, which is the single\n * most important fact about a connector run.\n *\n * **Retry history is deliberately NOT here.** It was, briefly, and the flow\n * node marketplace's cross-runtime fixtures caught it immediately: this object\n * is published on a node's output port and referenced by templates and agents,\n * and PHP's `ConnectorResult::toArray()` does not carry it. Two runtimes\n * publishing different shapes is the exact drift those fixtures exist to\n * prevent, and a diagnostic field is a poor reason to break it. Pass\n * `onAttempt` to observe retries.\n */\nexport type ConnectorResult<T = unknown> = {\n data: T;\n /** Which estate this actually ran against. Always reported, never inferred. */\n mode: ConnectorMode;\n connection: string;\n};\n\nlet registeredTransport: Transport | null = null;\n\n/** Install a transport. Only needed if the runtime has no global `fetch`. */\nexport function registerTransport(next: Transport | null): void {\n registeredTransport = next;\n}\n\nexport async function callConnector<T = unknown>(\n service: ServiceDescriptor,\n options: CallOptions,\n): Promise<ConnectorResult<T>> {\n const config = options.config ?? {};\n const connection = resolveConnection({\n service: service.service,\n operation: options.operation,\n connectionId: options.connectionId ?? readString(config, \"connection\"),\n requested: options.mode ?? (readString(config, \"mode\") as RequestedMode | null),\n credentials: options.credentials ?? null,\n sandbox: service.sandbox,\n baseUrls: service.baseUrls,\n requires: service.requires,\n });\n\n if (connection.mode === \"fake\") {\n return {\n data: service.faker(\n options.operation,\n fakeRequest(service.service, options.operation, config, options.input),\n ) as T,\n mode: \"fake\",\n connection: connection.id,\n };\n }\n\n const ctx: ConnectorErrorContext = { service: service.service, operation: options.operation };\n const prepared = await prepare(service, connection, options);\n const send = options.transport ?? registeredTransport ?? fetchTransport;\n\n const outcome = await deliver<unknown>(\n async () => {\n let response: TransportResponse;\n\n try {\n response = await send(prepared);\n } catch (cause) {\n // classifyThrown decides unreachable vs ambiguous. It never returns a\n // 5xx-shaped \"transient\", because nothing here reached the provider.\n throw classifyThrown(cause, ctx);\n }\n\n if (response.status < 400) {\n return response.body.trim() === \"\" ? null : parseJson(response.body, ctx);\n }\n\n const providerCode = readProviderCode(service, response);\n\n throw classifyHttp(\n response.status,\n providerCode === undefined ? ctx : { ...ctx, providerCode },\n response.body,\n readRetryAfter(response.headers),\n );\n },\n {\n ...DEFAULT_RETRY,\n attempts: options.attempts ?? DEFAULT_RETRY.attempts,\n idempotent: options.idempotent ?? false,\n },\n );\n\n if (options.onAttempt) {\n for (const attempt of outcome.attempts) options.onAttempt(attempt);\n }\n\n if (!outcome.ok) {\n throw failureFrom(outcome, ctx, options.idempotent ?? false);\n }\n\n return { data: outcome.value as T, mode: connection.mode, connection: connection.id };\n}\n\n/**\n * Turn an exhausted delivery into the error a host sees.\n *\n * An ambiguous failure on a non-idempotent connector gets a message that says\n * *go and look*, because that is the action. \"Request failed\" would send someone\n * to re-run it, which is the one thing that must not happen.\n *\n * ## What the provider said survives the rewrite\n *\n * The message is rewritten for a person; nothing else is. The last failure's\n * HTTP `status` and `providerCode` are copied across, and the failure itself is\n * the standard `cause`.\n *\n * Until 0.5.0 this built a bare `ConnectorError` from `ctx` alone, because the\n * outcome never carried the error — so every failed call, on every host, arrived\n * with no status while its message quoted one. The nightly connector probes ask a\n * real provider to refuse an impossible credential and read `error.status` to\n * see the refusal; they reported \"failed before any status arrived\" for every\n * provider, every night, against providers that had answered exactly right.\n *\n * ## The class is the one the failure was classified as\n *\n * A `401` throws `ConnectorAuthError`, a `429` throws `ConnectorRateLimited`\n * with its `retryAfter`, and every other failure the class its `kind` names —\n * the table at the top of `errors.ts`, which a host catching by class was\n * already entitled to. `kind` alone cannot tell an auth failure from any other\n * `rejected`, or a throttle from a 5xx, so those two come from the classified\n * error; the rest are the same answer either way. This is exactly what the PHP\n * twin's `failureFrom()` does, which until 0.5.0 mapped by kind alone and threw\n * a `ConnectorRequestException` for a `401`.\n */\nfunction failureFrom(\n outcome: DeliveryOutcome<unknown>,\n ctx: ConnectorErrorContext,\n idempotent: boolean,\n): ConnectorError {\n const last = outcome.attempts.at(-1);\n const kind: FailureKind = outcome.kind ?? last?.kind ?? \"ambiguous\";\n const message = outcome.gaveUp ?? `${ctx.service}.${ctx.operation} failed.`;\n const cause = outcome.error;\n const said = cause instanceof ConnectorError ? cause : undefined;\n const context: ConnectorErrorContext = {\n ...ctx,\n ...(said?.status === undefined ? {} : { status: said.status }),\n ...(said?.providerCode === undefined ? {} : { providerCode: said.providerCode }),\n };\n const error = failureOfClass(kind, said, message, context, cause === undefined ? undefined : { cause });\n\n // `kind` is readonly on the class; this is the one place that knows the\n // aggregate answer across attempts, so it is set here rather than guessed.\n Object.defineProperty(error, \"kind\", { value: kind, enumerable: false });\n Object.defineProperty(error, \"attempts\", { value: outcome.attempts, enumerable: false });\n Object.defineProperty(error, \"idempotent\", { value: idempotent, enumerable: false });\n\n return error;\n}\n\n/**\n * The taxonomy class for a failure: the classified error's own class where\n * `kind` cannot express it (auth, rate limit), the class `kind` names otherwise.\n *\n * The subclass is trusted only when it AGREES with the aggregate kind. It always\n * does for anything `callConnector` raises; a disagreement would mean the class\n * describes a different failure from the one being reported.\n */\nfunction failureOfClass(\n kind: FailureKind,\n said: ConnectorError | undefined,\n message: string,\n context: ConnectorErrorContext,\n options: ErrorOptions | undefined,\n): ConnectorError {\n if (said?.kind === kind && said instanceof ConnectorAuthError) {\n return new ConnectorAuthError(message, context, options);\n }\n\n if (said?.kind === kind && said instanceof ConnectorRateLimited) {\n const retryAfter = said.retryAfter === undefined ? {} : { retryAfter: said.retryAfter };\n\n return new ConnectorRateLimited(message, { ...context, ...retryAfter }, options);\n }\n\n switch (kind) {\n case \"unreachable\":\n return new ConnectorUnreachable(message, context, options);\n case \"refused-explicitly\":\n return new ConnectorTransient(message, context, options);\n case \"rejected\":\n return new ConnectorRequestError(message, context, options);\n case \"ambiguous\":\n return new ConnectorAmbiguous(message, context, options);\n }\n}\n\nasync function prepare(\n service: ServiceDescriptor,\n connection: ResolvedConnection,\n options: CallOptions,\n): Promise<PreparedRequest> {\n const base = connection.baseUrl ?? service.baseUrls[connection.mode];\n\n if (!base) {\n throw new ConnectorConfigError(\n `${service.service}: no base URL for \"${connection.mode}\" mode. ` +\n \"The service descriptor must declare one per mode it supports.\",\n { service: service.service, operation: options.operation },\n );\n }\n\n const { method, path, query, headers, json, form } = options.request;\n const url = new URL(path.replace(/^\\/+/, \"\"), base.endsWith(\"/\") ? base : `${base}/`);\n\n for (const [key, value] of Object.entries(query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n\n const prepared: PreparedRequest = {\n method,\n url: url.toString(),\n headers: { Accept: \"application/json\", ...headers },\n };\n\n if (form) {\n prepared.headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n prepared.body = encodeForm(form);\n } else if (json !== undefined) {\n prepared.headers[\"Content-Type\"] = \"application/json\";\n prepared.body = JSON.stringify(json);\n }\n\n if (options.idempotencyKey && service.idempotencyHeader) {\n prepared.headers[service.idempotencyHeader] = options.idempotencyKey;\n }\n\n await service.authorize(connection.credentials, prepared, connection.mode);\n\n return prepared;\n}\n\n/**\n * Form encoding, including the bracketed nesting several payment APIs use\n * (`metadata[order_id]=7`). Flat maps pass through unchanged.\n */\nfunction encodeForm(form: Record<string, unknown>): string {\n const pairs: string[] = [];\n\n const walk = (prefix: string, value: unknown): void => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => walk(`${prefix}[${index}]`, item));\n\n return;\n }\n\n if (typeof value === \"object\") {\n for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {\n walk(prefix === \"\" ? key : `${prefix}[${key}]`, nested);\n }\n\n return;\n }\n\n pairs.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(String(value))}`);\n };\n\n walk(\"\", form);\n\n return pairs.join(\"&\");\n}\n\nconst fetchTransport: Transport = async (request) => {\n const globalFetch = (globalThis as { fetch?: typeof fetch }).fetch;\n\n if (!globalFetch) {\n throw new Error(\n \"no global fetch in this runtime — call registerTransport() with one before running a connector remotely.\",\n );\n }\n\n const response = await globalFetch(request.url, {\n method: request.method,\n headers: request.headers,\n ...(request.body === undefined ? {} : { body: request.body }),\n });\n\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n return { status: response.status, headers, body: await response.text() };\n};\n\nfunction parseJson(body: string, ctx: ConnectorErrorContext): unknown {\n try {\n return JSON.parse(body);\n } catch {\n // A body that is not JSON on a 2xx is a REJECTION, not something to retry:\n // the same request will produce the same unparseable body.\n const error = new ConnectorError(\n `${ctx.service}.${ctx.operation}: the provider returned a body that is not JSON.`,\n ctx,\n );\n Object.defineProperty(error, \"kind\", { value: \"rejected\", enumerable: false });\n\n throw error;\n }\n}\n\n/**\n * The service's declared provider code, or absent. Never throws — see\n * `ServiceDescriptor.providerCodeFrom` for why a reader's failure must not\n * become the call's.\n */\nfunction readProviderCode(service: ServiceDescriptor, response: TransportResponse): string | undefined {\n if (!service.providerCodeFrom) return undefined;\n\n let code: unknown;\n\n try {\n code = service.providerCodeFrom(response);\n } catch {\n return undefined;\n }\n\n if (typeof code === \"number\") return Number.isSafeInteger(code) ? String(code) : undefined;\n\n return typeof code === \"string\" && code.trim() !== \"\" ? code : undefined;\n}\n\nfunction readRetryAfter(headers: Record<string, string>): number | undefined {\n const raw = headers[\"retry-after\"];\n if (!raw) return undefined;\n const seconds = Number(raw);\n\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n\nexport type { Classified };\n\nfunction readString(config: Record<string, unknown>, key: string): string | null {\n const value = config[key];\n\n return typeof value === \"string\" && value.trim() !== \"\" ? value.trim() : null;\n}\n","/**\n * API drift — being told the provider changed, rather than discovering it.\n *\n * ## The failure this addresses\n *\n * A connector's worst failure is silent. The provider changes its API and\n * nothing says so until something breaks in production — and by then the person\n * who wrote the connector has moved on, the reason it worked is gone, and the\n * first symptom is usually a 400 that reads like bad input.\n *\n * ## What the system DOES on drift: it REPORTS. It never adapts.\n *\n * This is the load-bearing decision and it is deliberate.\n *\n * A connector that reshapes itself around a changed API is worse than one that\n * fails loudly, for three reasons that compound:\n *\n * 1. **It cannot be reviewed.** The bytes that go out stop being a function of\n * the source someone read. A host that guarantees \"what was approved is what\n * was sent\" cannot make that guarantee about code that rewrote itself.\n * 2. **Its behaviour depends on WHEN it ran**, because it depends on what the\n * spec said at that moment. Two runs of the same workflow on the same input\n * can differ, and nothing in the run record explains why.\n * 3. **Adaptation is only ever a guess at intent.** A renamed field might be a\n * rename or might be a different field with a similar name; a removed one\n * might be gone or moved. Getting it wrong writes wrong data confidently.\n *\n * ## And it never changes RUNTIME behaviour either\n *\n * A drift check runs out of band — on a schedule, in CI, on demand. It does not\n * fail a call that is working. If the spec changed and the call still succeeds,\n * failing the run would be a self-inflicted outage caused by a document. The\n * only thing that changes runtime behaviour is the provider actually refusing,\n * which the error taxonomy already handles.\n *\n * So: **drift reports; the provider decides; a person acts.**\n *\n * ## Two detection strategies, because most providers publish nothing\n *\n * 1. **Specs**, where a provider publishes a machine-readable one at a stable\n * URL. Checked against the connector's declared slice — see `ApiContract`.\n * 2. **Recorded shapes**, where it does not. A fixture of the response fields a\n * connector reads, compared against a live response on a schedule. Field\n * NAMES only; values are never recorded, because a recorded response from a\n * real account is a data leak wearing a test fixture's clothes.\n *\n * The second is the fallback and is designed anyway, because it is the majority\n * case: of the providers surveyed for this package, the ones publishing a\n * maintained spec are a minority, and publishing one is not the same as\n * maintaining it — `slackapi/slack-api-specs` has not been touched since 2021.\n */\n\n/* ── What a connector declares it depends on ─────────────────────────────── */\n\n/**\n * One operation a connector calls.\n *\n * The `path` is the TEMPLATE, matching the provider's own documentation, so it\n * can be looked up in a spec: `/v1/charges/{charge}`, not the interpolated URL.\n */\nexport type ContractOperation = {\n /** Stable name, matching the connector's own operation id. */\n operation: string;\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n /** Request fields the connector SENDS. Drift here breaks writes. */\n sends?: string[];\n /** Response fields the connector READS. Drift here breaks reads, silently. */\n reads?: string[];\n};\n\n/**\n * Where a provider's machine-readable description lives.\n *\n * `kind` matters because \"machine-readable\" is not one format. AT Protocol\n * publishes **lexicons**, not OpenAPI, and a checker that assumed OpenAPI would\n * report every AT Protocol connector as unspecified — which is a different and\n * much worse answer than \"specified, in another format\".\n */\nexport type SpecSource =\n | { kind: \"openapi\"; url: string; note?: string }\n | { kind: \"lexicon\"; url: string; note?: string }\n | {\n kind: \"none\";\n /**\n * Why there is no spec, and what the fallback is. Required — \"none\" with\n * no reason is indistinguishable from \"nobody looked\".\n */\n note: string;\n };\n\n/** Everything a connector declares about the API it was written against. */\nexport type ApiContract = {\n connector: string;\n /** Base URL the operations hang off, for a human reading the report. */\n baseUrl?: string;\n spec: SpecSource;\n operations: ContractOperation[];\n /** When a person last read the provider's documentation for this contract. */\n reviewedOn: string;\n};\n\n/* ── What a check produces ───────────────────────────────────────────────── */\n\nexport type DriftFinding = {\n /**\n * `missing-operation` — the path or method is gone. A write will start failing.\n * `missing-request-field` — something we send is no longer accepted.\n * `missing-response-field` — something we read is gone. **Silent**: the code\n * keeps running and produces nothing, which is the dangerous one.\n * `unreadable-spec` — the spec URL moved or stopped parsing. Not drift in the\n * API, but drift in our ability to see it, and it must not read as \"clean\".\n * `stale-review` — nobody has looked at this contract in a long time.\n */\n kind:\n | \"missing-operation\"\n | \"missing-request-field\"\n | \"missing-response-field\"\n | \"unreadable-spec\"\n | \"stale-review\";\n operation?: string;\n detail: string;\n};\n\nexport type DriftReport = {\n connector: string;\n checkedAt: string;\n /** `unchecked` is a real outcome and never counts as clean. */\n outcome: \"clean\" | \"drifted\" | \"unchecked\";\n findings: DriftFinding[];\n /** How the check was done, so a report can be read without the code. */\n method: \"openapi\" | \"lexicon\" | \"recorded-shape\" | \"none\";\n};\n\n/* ── OpenAPI, read narrowly and on purpose ───────────────────────────────── */\n\n/**\n * The subset of OpenAPI this needs.\n *\n * Deliberately not a full parser and deliberately not a dependency. The general\n * problem — diff two OpenAPI documents — is large, and we do not have it. We\n * have a much smaller one: *for the operations this connector actually calls,\n * do the path, the method, and the fields we send and read still exist?*\n *\n * That projection is ours by definition, it is a few dozen lines, and it does\n * not drag a schema-validation library into every consumer's tree. A full diff\n * would also report thousands of changes to parts of the API nobody here calls,\n * which is the reliable way to make a drift report unread.\n */\ntype OpenApiDocument = {\n paths?: Record<string, Record<string, unknown>>;\n components?: { schemas?: Record<string, unknown> };\n};\n\n/** Resolve a local `$ref`. Remote refs are not followed — see `resolveRef`. */\nfunction resolveRef(document: OpenApiDocument, node: unknown, depth = 0): unknown {\n if (depth > 8 || node === null || typeof node !== \"object\") return node;\n\n const ref = (node as { $ref?: string }).$ref;\n if (typeof ref !== \"string\") return node;\n\n // Only same-document refs. A remote `$ref` would mean fetching arbitrary URLs\n // from a document we do not control, which is a request-forgery surface for a\n // check that runs on a schedule with nobody watching.\n if (!ref.startsWith(\"#/\")) return node;\n\n let current: unknown = document;\n for (const segment of ref.slice(2).split(\"/\")) {\n if (current === null || typeof current !== \"object\") return node;\n current = (current as Record<string, unknown>)[segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\")];\n }\n\n return resolveRef(document, current, depth + 1);\n}\n\n/** Property names on a schema, following local refs and one level of composition. */\nfunction propertiesOf(document: OpenApiDocument, schema: unknown, depth = 0): Set<string> {\n const names = new Set<string>();\n const resolved = resolveRef(document, schema, depth) as Record<string, unknown> | null;\n\n if (!resolved || typeof resolved !== \"object\" || depth > 6) return names;\n\n const properties = resolveRef(document, resolved.properties, depth + 1);\n if (properties && typeof properties === \"object\") {\n for (const key of Object.keys(properties as Record<string, unknown>)) names.add(key);\n }\n\n // `data` / `items` wrappers are near-universal and a connector reads through\n // them, so an envelope must not read as \"the field is gone\".\n for (const key of [\"items\", \"additionalProperties\"]) {\n const nested = resolved[key];\n if (nested) for (const name of propertiesOf(document, nested, depth + 1)) names.add(name);\n }\n\n for (const key of [\"allOf\", \"oneOf\", \"anyOf\"]) {\n const list = resolved[key];\n if (Array.isArray(list)) {\n for (const entry of list) for (const name of propertiesOf(document, entry, depth + 1)) names.add(name);\n }\n }\n\n return names;\n}\n\nfunction requestSchemaOf(document: OpenApiDocument, operation: Record<string, unknown>): unknown {\n const body = resolveRef(document, operation.requestBody) as\n | { content?: Record<string, { schema?: unknown }> }\n | undefined;\n\n if (!body?.content) return undefined;\n\n for (const media of Object.values(body.content)) {\n if (media?.schema) return media.schema;\n }\n\n return undefined;\n}\n\nfunction responseSchemaOf(document: OpenApiDocument, operation: Record<string, unknown>): unknown {\n const responses = resolveRef(document, operation.responses) as Record<string, unknown> | undefined;\n if (!responses) return undefined;\n\n for (const status of Object.keys(responses)) {\n if (!/^2\\d\\d$/.test(status) && status !== \"default\") continue;\n const response = resolveRef(document, responses[status]) as\n | { content?: Record<string, { schema?: unknown }> }\n | undefined;\n if (!response?.content) continue;\n\n for (const media of Object.values(response.content)) {\n if (media?.schema) return media.schema;\n }\n }\n\n return undefined;\n}\n\n/**\n * Check a contract against an OpenAPI document already fetched by the caller.\n *\n * The FETCH is the caller's, not this function's — so the check is a pure\n * function of two documents and can be tested against a fixture with no network,\n * and so a host chooses its own HTTP client, cache and proxy. A checker that\n * fetched would also be a checker that phones out on a schedule, which is\n * exactly the thing several hosts of this package refuse to have.\n */\nexport function checkAgainstOpenApi(contract: ApiContract, document: unknown): DriftReport {\n const checkedAt = new Date().toISOString();\n const findings: DriftFinding[] = [];\n\n if (!document || typeof document !== \"object\" || !(document as OpenApiDocument).paths) {\n return {\n connector: contract.connector,\n checkedAt,\n outcome: \"unchecked\",\n method: \"openapi\",\n findings: [\n {\n kind: \"unreadable-spec\",\n detail:\n \"the document has no `paths`, so it is not an OpenAPI description we can read. Reported as UNCHECKED \" +\n \"rather than clean — a checker that cannot see is not a checker that saw nothing wrong.\",\n },\n ],\n };\n }\n\n const spec = document as OpenApiDocument;\n\n for (const operation of contract.operations) {\n const path = spec.paths?.[operation.path];\n\n if (!path) {\n findings.push({\n kind: \"missing-operation\",\n operation: operation.operation,\n detail: `${operation.method} ${operation.path} is not in the spec at all. The path moved or was removed.`,\n });\n continue;\n }\n\n const method = path[operation.method.toLowerCase()] as Record<string, unknown> | undefined;\n\n if (!method) {\n findings.push({\n kind: \"missing-operation\",\n operation: operation.operation,\n detail: `${operation.path} exists but no longer accepts ${operation.method}.`,\n });\n continue;\n }\n\n if (operation.sends?.length) {\n const accepted = propertiesOf(spec, requestSchemaOf(spec, method));\n // An empty schema means \"we could not read it\", not \"it accepts nothing\".\n if (accepted.size > 0) {\n for (const field of operation.sends) {\n if (!accepted.has(field)) {\n findings.push({\n kind: \"missing-request-field\",\n operation: operation.operation,\n detail: `sends \\`${field}\\`, which the spec no longer lists on ${operation.method} ${operation.path}.`,\n });\n }\n }\n }\n }\n\n if (operation.reads?.length) {\n const returned = propertiesOf(spec, responseSchemaOf(spec, method));\n if (returned.size > 0) {\n for (const field of operation.reads) {\n if (!returned.has(field)) {\n findings.push({\n kind: \"missing-response-field\",\n operation: operation.operation,\n detail:\n `reads \\`${field}\\`, which the spec no longer returns from ${operation.method} ${operation.path}. ` +\n \"This is the silent kind: the code keeps running and produces nothing.\",\n });\n }\n }\n }\n }\n }\n\n findings.push(...staleReviewFindings(contract, checkedAt));\n\n return {\n connector: contract.connector,\n checkedAt,\n outcome: findings.some((finding) => finding.kind !== \"stale-review\") ? \"drifted\" : \"clean\",\n method: \"openapi\",\n findings,\n };\n}\n\n/* ── The no-spec fallback: recorded shapes ───────────────────────────────── */\n\n/**\n * The field NAMES a connector saw in a real response, per operation.\n *\n * Names only. A recorded response body from a real account is a data leak\n * wearing a test fixture's clothes, and the thing being checked is the shape.\n */\nexport type RecordedShape = {\n connector: string;\n recordedOn: string;\n operations: Record<string, string[]>;\n};\n\n/**\n * The field names present in a response, flattened to dotted paths.\n *\n * Arrays collapse to their first element's shape: a response with three items\n * has one shape, and recording `items.0.id`, `items.1.id`, `items.2.id` would\n * make the fixture depend on how much data the account happened to have.\n */\nexport function shapeOf(value: unknown, prefix = \"\", depth = 0): string[] {\n if (depth > 6 || value === null || typeof value !== \"object\") return prefix ? [prefix] : [];\n\n if (Array.isArray(value)) {\n return value.length === 0 ? [`${prefix}[]`] : shapeOf(value[0], `${prefix}[]`, depth + 1);\n }\n\n const names: string[] = [];\n for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {\n const path = prefix ? `${prefix}.${key}` : key;\n names.push(...shapeOf(nested, path, depth + 1));\n }\n\n return names.sort();\n}\n\n/**\n * Compare a live response's shape against what was recorded.\n *\n * **Only fields the connector READS are findings.** A provider adding fields is\n * normal and healthy; reporting it would bury the one line that matters under\n * every additive release, and a drift report nobody reads is worse than none\n * because it is evidence somebody is watching.\n */\nexport function checkAgainstRecordedShape(\n contract: ApiContract,\n operation: string,\n liveResponse: unknown,\n): DriftReport {\n const checkedAt = new Date().toISOString();\n const declared = contract.operations.find((entry) => entry.operation === operation);\n const present = new Set(shapeOf(liveResponse));\n const findings: DriftFinding[] = [];\n\n for (const field of declared?.reads ?? []) {\n // A dotted path matches its own name or any array-bearing variant of it, so\n // a connector reading `data.id` still matches `data[].id`.\n const found = [...present].some((name) => name === field || name.replace(/\\[\\]/g, \"\") === field);\n\n if (!found) {\n findings.push({\n kind: \"missing-response-field\",\n operation,\n detail:\n `reads \\`${field}\\`, which the live response no longer carries. Present instead: ` +\n `${[...present].slice(0, 20).join(\", \")}${present.size > 20 ? \", …\" : \"\"}`,\n });\n }\n }\n\n findings.push(...staleReviewFindings(contract, checkedAt));\n\n return {\n connector: contract.connector,\n checkedAt,\n outcome: findings.some((finding) => finding.kind !== \"stale-review\") ? \"drifted\" : \"clean\",\n method: \"recorded-shape\",\n findings,\n };\n}\n\n/** How long a contract may go unreviewed before that is itself a finding. */\nexport const REVIEW_STALE_DAYS = 180;\n\nfunction staleReviewFindings(contract: ApiContract, checkedAt: string): DriftFinding[] {\n const reviewed = Date.parse(contract.reviewedOn);\n if (!Number.isFinite(reviewed)) {\n return [{ kind: \"stale-review\", detail: `reviewedOn \"${contract.reviewedOn}\" is not a date.` }];\n }\n\n const days = Math.floor((Date.parse(checkedAt) - reviewed) / 86_400_000);\n\n return days > REVIEW_STALE_DAYS\n ? [\n {\n kind: \"stale-review\",\n detail:\n `nobody has read this provider's documentation in ${days} days. Not drift — an absence of looking, ` +\n \"which is the state a silent break grows in.\",\n },\n ]\n : [];\n}\n\n/**\n * A contract whose provider publishes nothing, checked as far as it can be.\n *\n * Reported as `unchecked` with the reason, never `clean`. Those are different\n * claims and the difference is the entire value of a report.\n */\nexport function unchecked(contract: ApiContract): DriftReport {\n const checkedAt = new Date().toISOString();\n\n return {\n connector: contract.connector,\n checkedAt,\n outcome: \"unchecked\",\n method: \"none\",\n findings: [\n {\n kind: \"unreadable-spec\",\n detail:\n contract.spec.kind === \"none\"\n ? contract.spec.note\n : \"no spec document was supplied to the checker, so nothing was compared.\",\n },\n ...staleReviewFindings(contract, checkedAt),\n ],\n };\n}\n","import { ConnectorConfigError, type ConnectorErrorContext } from \"./errors\";\n\n/**\n * A run, a position inside it, and how many times that position has been tried.\n *\n * Declared STRUCTURALLY rather than imported, and that is the whole point: this\n * package must be usable by a host that has never heard of a workflow engine.\n * `fancy-flow`'s `RunIdentity` class satisfies this shape exactly, so a flow\n * node passes `ctx.run` straight in with no adapter and no dependency in either\n * direction — and a host with its own notion of \"the same logical attempt\"\n * implements five members and gets the same guarantees.\n *\n * The identity is more than `(run, step)` for a reason: a step legitimately runs\n * many times in one run — once per loop iteration, once per nested invocation —\n * so the key carries the PATH that led to it. `attempt` is carried and\n * deliberately NOT part of the key, because a retry must produce the same key.\n */\nexport type RunIdentity = {\n /** Stable for the whole run: same across retries, resumes, workers and hosts. */\n readonly runKey: string;\n /** 1-based attempt of THIS logical step. Never part of the key. */\n readonly attempt: number;\n /** ISO-8601 instant of attempt 1 of this step. */\n readonly firstAttemptAt: string;\n /** The identity of one execution of one step, stable across its retries. */\n stepKey(stepId: string, occurrence?: number | null): string;\n /** May this attempt reuse the key and still be deduplicated? */\n isReplaySafe(windowSeconds: number | null | undefined, now?: Date | string): boolean;\n};\n\n/**\n * The idempotency key a writing connector sends, and when it must refuse to.\n *\n * ## What the engine now provides\n *\n * `fancy-flow` 0.46.0 carries a **run identity** on the execution context, so a\n * node can derive a key that is the same on every retry of one logical step and\n * different for every other execution of the same node.\n * `ctx.run.stepKey(ctx.node.id)` is that key. The equivalent landed in\n * `fancy-flow-php` 0.19.0 and the Python runtime, all three pinned by\n * `shared/flow-run-identity` in `@particle-academy/fancy-conformance`.\n *\n * Before that, neither engine could produce one, and both obvious substitutes\n * were worse than sending nothing:\n *\n * - **the node id alone** is stable across retries and also across RUNS, so two\n * legitimate payments share a key and the provider silently collapses the\n * second into the first — a payment that never happened, reported as success;\n * - **a fresh random value** is unique per run and also per ATTEMPT, so a retry\n * creates a second charge.\n *\n * So this used to return `null` and every writing node emitted a warning and\n * sent no key, staying pinned to one attempt. That is no longer the case where\n * a host supplies an identity — which durable runs do automatically.\n *\n * ## The part that is still a judgement call: the provider's window\n *\n * Providers forget idempotency keys. Stripe's window is **24 hours**. Past it,\n * resending the key creates a second charge and sending a fresh one creates a\n * second charge — there is no safe third option — so `idempotencyKeyFor`\n * **throws** rather than choosing. A loud stuck run that a person reconciles\n * beats a silent double write nobody ever sees.\n *\n * A first attempt is never refused, however long the run was parked: nothing\n * was sent on an earlier attempt, so there is nothing for the provider to have\n * forgotten. That is what lets an approval sit for a week and then charge.\n */\n\n/** Stripe's window, and the default for a provider that has not said otherwise. */\nexport const DEFAULT_IDEMPOTENCY_WINDOW_SECONDS = 24 * 60 * 60;\n\n/**\n * Longest key the strictest provider in the catalogue accepts.\n *\n * Stripe caps `Idempotency-Key` at 255 characters. A deep subflow path could\n * exceed that, and a provider rejecting the header is a 400 that looks like a\n * bug in the request body.\n */\nexport const MAX_IDEMPOTENCY_KEY_LENGTH = 255;\n\n/** Raised when a retry can no longer be made safe. Never retryable. */\nexport class ConnectorIdempotencyExpiredError extends ConnectorConfigError {}\n\n/** The run identity a host published, or `null`. Checked in order. */\nexport function runIdentityFrom(ctx: unknown): RunIdentity | null {\n const bag = ctx as { run?: RunIdentity } | null;\n\n return bag?.run ?? null;\n}\n\n/**\n * Where a host with no engine support may still publish a run key.\n *\n * Kept because a consumer on an older `fancy-flow` has no `ctx.run`, and\n * seeding `__runKey` into the run's initial inputs is the documented fallback.\n * It gives a per-run key with no attempt information, so the window check below\n * treats it as a first attempt — correct for a host that does not retry, and\n * the reason the engine-supplied identity is strictly better.\n */\nexport function runKeyFrom(ctx: unknown): string | null {\n const identity = runIdentityFrom(ctx);\n if (identity) return identity.runKey;\n\n const bag = ctx as Record<string, any> | null;\n const seeded = bag?.inputs?.__runKey;\n\n return typeof seeded === \"string\" && seeded.trim() !== \"\" ? seeded.trim() : null;\n}\n\nexport type IdempotencyOptions = {\n /**\n * How long the provider remembers a key. `null` means it never forgets; `0`\n * means it does not dedupe at all, so no retry may reuse one.\n */\n windowSeconds?: number | null;\n /** Distinguishes repeated executions of one node at the same level. */\n occurrence?: number | null;\n /** Injectable for tests. Never a network call. */\n now?: Date;\n /** For the error message when a key cannot be made safe. */\n context?: Partial<ConnectorErrorContext>;\n /**\n * The PROVIDER'S own limit, when it declares one — `idempotencyMaxLength` in\n * the connector index. Omitted uses {@link MAX_IDEMPOTENCY_KEY_LENGTH}, which\n * is the catalogue CEILING rather than any one provider's limit.\n *\n * Discord's `discord_message` declares 25, and for years only the ceiling was\n * applied — so a legitimate engine-derived key came back at 29 characters and\n * the connector's own validation refused it, failing the run at a node the\n * host had no way to fix.\n */\n maxLength?: number | null;\n};\n\n/**\n * The idempotency key for this execution of this node, or `null`.\n *\n * `null` means the host published no run identity at all — send no header\n * rather than inventing one, and keep the node `unsafe-to-replay`.\n *\n * **Throws** `ConnectorIdempotencyExpiredError` when this is a RETRY and the\n * provider's window has elapsed. That is not a defensive check; it is the only\n * correct answer, because both alternatives write twice.\n */\nexport function idempotencyKeyFor(\n ctx: unknown,\n nodeId: string,\n options: IdempotencyOptions = {},\n): string | null {\n const identity = runIdentityFrom(ctx);\n const windowSeconds =\n options.windowSeconds === undefined ? DEFAULT_IDEMPOTENCY_WINDOW_SECONDS : options.windowSeconds;\n\n if (!identity) {\n // The legacy seeded-key path. No attempt information exists, so there is\n // nothing to window-check; a host on this path is one that does not retry.\n const runKey = runKeyFrom(ctx);\n return runKey === null ? null : fit(`${runKey}:${nodeId}`, options.maxLength);\n }\n\n if (!identity.isReplaySafe(windowSeconds, options.now ?? new Date())) {\n throw new ConnectorIdempotencyExpiredError(\n `attempt ${identity.attempt} of this step began at ${identity.firstAttemptAt}, which is outside ` +\n `the provider's ${windowSeconds}s idempotency window. The provider has forgotten the original ` +\n `key, so resending it and sending a new one would BOTH write twice. Refusing, so a person can ` +\n `reconcile — check whether the first attempt landed before re-running this workflow.`,\n {\n service: options.context?.service ?? \"\",\n operation: options.context?.operation ?? \"\",\n ...options.context,\n },\n );\n }\n\n return fit(identity.stepKey(nodeId, options.occurrence), options.maxLength);\n}\n\n/**\n * The warning a node emits when it is about to write without a key.\n *\n * Said out loud, at the moment it matters, because the failure it precedes is\n * invisible: the run succeeds, the retry succeeds, and there are two charges.\n */\nexport const NO_IDEMPOTENCY_KEY_WARNING =\n \"no run identity available, so this write is being sent WITHOUT an idempotency key. \" +\n \"A retry would repeat the effect rather than recover from it — the node is marked \" +\n \"unsafe-to-replay, so a durable runner will give it one attempt. Durable runs supply an \" +\n \"identity automatically; for a synchronous run, pass `run` in RunOptions.\";\n\n/**\n * Shorten an over-long key deterministically.\n *\n * The hash has to be stable across attempts AND across runtimes, so it is a\n * plain FNV-1a over the key rather than anything host-provided. The prefix is\n * kept so a key remains greppable against a run.\n */\nfunction fit(key: string, maxLength?: number | null): string {\n // The SMALLER of the provider's limit and the catalogue ceiling. A descriptor\n // claiming more than any provider accepts is one to distrust, not obey.\n const limit =\n maxLength === undefined || maxLength === null\n ? MAX_IDEMPOTENCY_KEY_LENGTH\n : Math.max(1, Math.min(maxLength, MAX_IDEMPOTENCY_KEY_LENGTH));\n\n if (key.length <= limit) return key;\n\n const digest = fnv1a(key);\n\n // A limit too small to carry `<head>~<digest>` gets the digest alone. That\n // loses the greppable prefix, which is a real cost — but a key that is hard\n // to trace still dedupes, while a negative slice length produces nonsense.\n if (limit <= digest.length + 1) return digest.slice(0, limit);\n\n const head = key.slice(0, limit - digest.length - 1);\n\n return `${head}~${digest}`;\n}\n\nfunction fnv1a(value: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i);\n // 32-bit FNV prime, written as shifts so it stays exact in a double.\n hash = (hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24))) >>> 0;\n }\n\n return hash.toString(16).padStart(8, \"0\");\n}\n","/**\n * A subscription that EXPIRES, and when the host must act on it.\n *\n * `DeliveryMechanism` has said since 0.1.0 that a `subscription` trigger is a\n * webhook somebody has to renew, forever, and that if nobody does the workflow\n * stops firing with no error anywhere. This is the value for that duty — ONE\n * shape every expiring trigger declares, so a host runs one renewal scheduler\n * rather than learning a loop per connector, each with its own boundary bugs.\n *\n * Three facts and two verbs:\n *\n * - `expiresAt` — the provider's expiry, as an RFC 3339 instant. Google Calendar\n * hands back `expiration` as epoch MILLISECONDS in a string and Microsoft\n * Graph hands back `expirationDateTime` as ISO 8601; the CONNECTOR converts on\n * the way in, and this module refuses anything that is not an instant rather\n * than guessing units.\n * - `renewBeforeSeconds` — the connector's margin. Positive, because zero would\n * make `due` unreachable (the moment it applied, `expired` would win) — a\n * lease nobody ever renews, declared in a way nothing would report.\n * - `renewOperation` — the operation the host calls when the lease is due. For\n * Graph that is a renew; for a Google Calendar channel, which cannot be\n * renewed, it is the create again (the connector stops the old channel\n * itself). The lease says WHEN; the connector says WHAT.\n *\n * - `leaseState` — where the lease IS: `active`, `due` (inclusive at renewAt),\n * `expired` (inclusive at expiresAt, and it wins over `due`: a lease that has\n * just expired cannot be renewed).\n * - `leaseAction` — what the host DOES: `none`, `renew`, or `resync`. A missed\n * lease is never a quiet re-create: notifications during the gap are gone, so\n * the host must re-list (sync token or full) AND re-subscribe.\n *\n * The boundaries are DECIDED, not measured, and they are pinned in\n * `fixtures/subscription-lease/cases.json`, which the PHP twin reads too.\n */\nimport { ConnectorConfigError, type ConnectorErrorContext } from \"./errors\";\n\nexport type SubscriptionLease = {\n /** The provider's expiry, an RFC 3339 instant. */\n expiresAt: string;\n /** How early the host renews, in seconds. Positive. */\n renewBeforeSeconds: number;\n /** The operation the host calls when the lease is due. */\n renewOperation: string;\n};\n\nexport type LeaseState = \"active\" | \"due\" | \"expired\";\n\nexport type LeaseAction = \"none\" | \"renew\" | \"resync\";\n\n/** RFC 3339: date, `T`, time, optional fraction, `Z` or a numeric offset. */\nconst INSTANT = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$/;\n\nconst DEFAULT_CONTEXT: ConnectorErrorContext = { service: \"\", operation: \"subscription\" };\n\nfunction instantMs(value: unknown, field: string, at: ConnectorErrorContext): number {\n if (typeof value !== \"string\" || !INSTANT.test(value)) {\n throw new ConnectorConfigError(\n `${field} must be an RFC 3339 instant such as \"2026-09-22T00:00:00Z\", got ${JSON.stringify(value)}. ` +\n \"A provider's own shape — Google's epoch milliseconds, say — is converted by the connector, never guessed at here.\",\n at,\n );\n }\n const ms = Date.parse(value);\n if (Number.isNaN(ms)) {\n throw new ConnectorConfigError(`${field} is not a real instant: ${JSON.stringify(value)}`, at);\n }\n return ms;\n}\n\n/**\n * Validate a declaration and return the lease — a plain value a host stores\n * beside the trigger and hands back to `leaseState` / `leaseAction` later.\n */\nexport function subscriptionLease(\n input: { expiresAt: string; renewBeforeSeconds: number; renewOperation: string },\n at: ConnectorErrorContext = DEFAULT_CONTEXT,\n): SubscriptionLease {\n instantMs(input.expiresAt, \"expiresAt\", at);\n\n if (!Number.isInteger(input.renewBeforeSeconds) || input.renewBeforeSeconds <= 0) {\n throw new ConnectorConfigError(\n `renewBeforeSeconds must be a positive whole number of seconds, got ${JSON.stringify(input.renewBeforeSeconds)}. ` +\n \"Zero would make the lease due only at the instant it expires, which is a lease nobody renews.\",\n at,\n );\n }\n\n if (typeof input.renewOperation !== \"string\" || input.renewOperation.trim() === \"\") {\n throw new ConnectorConfigError(\n \"renewOperation must name the operation the host calls when the lease is due; a due lease with nothing to call is one nobody renews.\",\n at,\n );\n }\n\n return { expiresAt: input.expiresAt, renewBeforeSeconds: input.renewBeforeSeconds, renewOperation: input.renewOperation };\n}\n\n/** The instant the lease becomes due: `expiresAt - renewBeforeSeconds`. */\nexport function leaseRenewAt(lease: SubscriptionLease): string {\n return new Date(instantMs(lease.expiresAt, \"expiresAt\", DEFAULT_CONTEXT) - lease.renewBeforeSeconds * 1000).toISOString();\n}\n\n/** Where the lease is at `now`. Both boundaries are inclusive; `expired` wins. */\nexport function leaseState(lease: SubscriptionLease, now: string | Date): LeaseState {\n const at = now instanceof Date ? now.getTime() : instantMs(now, \"now\", DEFAULT_CONTEXT);\n const expires = instantMs(lease.expiresAt, \"expiresAt\", DEFAULT_CONTEXT);\n\n if (at >= expires) return \"expired\";\n if (at >= expires - lease.renewBeforeSeconds * 1000) return \"due\";\n return \"active\";\n}\n\n/** What the host does about the lease at `now`. */\nexport function leaseAction(lease: SubscriptionLease, now: string | Date): LeaseAction {\n switch (leaseState(lease, now)) {\n case \"active\":\n return \"none\";\n case \"due\":\n return \"renew\";\n case \"expired\":\n return \"resync\";\n }\n}\n","/**\n * Making a declared metric shape checkable against the code that produces it.\n *\n * ## The bug this exists to catch, which shipped for weeks\n *\n * Two connectors in the reference implementation declared\n * `capabilities.metrics: true` while their metric fetch returned `[]`. A pull\n * therefore did not skip them — it asked, got nothing, and reported nothing,\n * which on a dashboard is indistinguishable from *\"we asked and nobody\n * engaged\"*. A capability flag had quietly turned an unimplemented feature into\n * a reported zero, on the one surface whose entire job is not making claims it\n * cannot stand behind.\n *\n * A declared shape and the code that produces the numbers are the classic pair\n * that agrees on the day it is written and drifts silently after. So the mapping\n * is extracted as a **pure function per connector**, and these helpers feed it a\n * synthetic response and compare the keys it returns against the keys declared.\n * **No credentials, no network.**\n *\n * ## And the rule underneath all of it\n *\n * **Absent stays absent.** A count the provider did not send is omitted, never\n * reported as zero. A zero says \"nothing happened\"; an absence says \"we don't\n * know\". A measurement surface that confuses those is worthless, and the\n * confusion is one `?? 0` away.\n */\n\nimport { sandboxIsSelectable } from \"./mode\";\nimport type { Connector, MetricDescriptor, ProviderAdapter } from \"./seam\";\n\nexport type ShapeMismatch = {\n connector: string;\n /** Keys the mapping produced that nothing declared. */\n undeclared: string[];\n /** Keys declared that the mapping never produces. */\n unproduced: string[];\n};\n\n/**\n * Compare a declared shape against what a mapping actually returns.\n *\n * `produced` is the output of the connector's pure response→metrics function\n * given a synthetic response with EVERY field populated. Anything the mapping\n * can emit therefore appears, and anything it cannot is caught as a declaration\n * nobody can honour.\n */\nexport function compareShape(\n connectorId: string,\n declared: MetricDescriptor[] | undefined,\n produced: Record<string, number>,\n): ShapeMismatch | null {\n const declaredKeys = new Set((declared ?? []).map((metric) => metric.key));\n const producedKeys = new Set(Object.keys(produced));\n\n const undeclared = [...producedKeys].filter((key) => !declaredKeys.has(key)).sort();\n const unproduced = [...declaredKeys].filter((key) => !producedKeys.has(key)).sort();\n\n if (undeclared.length === 0 && unproduced.length === 0) return null;\n\n return { connector: connectorId, undeclared, unproduced };\n}\n\n/**\n * Every way a capability flag can outrun the code, as findings.\n *\n * Returns strings rather than throwing so a host can report all of them at once.\n * A check that stops at the first problem trains people to fix one thing and\n * re-run, which is how a list of six becomes six rounds.\n */\nexport function capabilityProblems(connector: Connector<never>): string[] {\n const problems: string[] = [];\n\n if (connector.capabilities.metrics) {\n if (!connector.metricShape || connector.metricShape.length === 0) {\n problems.push(\n `${connector.id} claims capabilities.metrics but declares no metricShape. A pull will therefore ask it, ` +\n \"get nothing, and report nothing — which reads as \\\"nobody engaged\\\" rather than \\\"not implemented\\\".\",\n );\n }\n if (!connector.fetchMetrics) {\n problems.push(`${connector.id} claims capabilities.metrics but has no fetchMetrics.`);\n }\n }\n\n if (!connector.capabilities.metrics && connector.metricShape) {\n problems.push(\n `${connector.id} declares a metricShape but capabilities.metrics is false. One of the two is wrong, and ` +\n \"the shape is the more believable half.\",\n );\n }\n\n if (connector.capabilities.feedback && !connector.fetchFeedback) {\n problems.push(`${connector.id} claims capabilities.feedback but has no fetchFeedback.`);\n }\n\n if (!connector.capabilities.feedback && connector.fetchFeedback) {\n problems.push(\n `${connector.id} implements fetchFeedback but does not claim the capability, so nothing will ever call it.`,\n );\n }\n\n for (const metric of connector.metricShape ?? []) {\n if (metric.means.trim().length < 10) {\n problems.push(`${connector.id}.${metric.key} does not say what it means.`);\n }\n }\n\n if (connector.delivery.idempotent && connector.delivery.why.trim().length < 10) {\n problems.push(\n `${connector.id} declares idempotent: true with no reason. That is the one claim whose failure is a public ` +\n \"duplicate, so it has to name the mechanism rather than restate the flag.\",\n );\n }\n\n if (connector.delivery.minIntervalMs > 0 && !connector.delivery.citation && connector.delivery.rateSource === \"documented\") {\n problems.push(\n `${connector.id} calls its rate limit \"documented\" but cites nothing. A number nobody can source gets quoted ` +\n \"as a platform fact; label it self-imposed or cite it.\",\n );\n }\n\n return problems;\n}\n\n/**\n * Drop keys whose value the provider did not actually send.\n *\n * The one function a connector's mapping should build on, so *absent stays\n * absent* is a property of the code rather than a habit. `0` survives; `null`,\n * `undefined` and non-numbers do not.\n */\nexport function reported(values: Record<string, unknown>): Record<string, number> {\n const out: Record<string, number> = {};\n\n for (const [key, value] of Object.entries(values)) {\n if (typeof value === \"number\" && Number.isFinite(value)) out[key] = value;\n }\n\n return out;\n}\n\n/**\n * Every way a PROVIDER's declaration can outrun what anyone actually checked.\n *\n * The sibling of `capabilityProblems`, one level up. `capabilities.metrics: true`\n * with no shape turns an unimplemented feature into a reported zero; the\n * equivalent here turns *nobody looked* into a statement about a test estate —\n * and that field is the one where being wrong sends a person to a live estate\n * believing it is a test one.\n *\n * Strings rather than a throw, so a host reports all of them at once. A check\n * that stops at the first problem trains people to fix one thing and re-run,\n * which is how a list of six becomes six rounds.\n */\nexport function providerProblems(provider: ProviderAdapter): string[] {\n const problems: string[] = [];\n\n if (provider.implemented && provider.sandbox === \"unverified\") {\n problems.push(\n `${provider.id} says it is implemented while its sandbox shape is still \"unverified\". Something a person ` +\n \"can run today must not be ambiguous about which estate it reaches — verify it, or set implemented: false.\",\n );\n }\n\n if (provider.implemented && provider.fields.length === 0) {\n problems.push(`${provider.id} says it is implemented but declares no credential fields.`);\n }\n\n if (provider.implemented && provider.setup.length === 0) {\n problems.push(\n `${provider.id} says it is implemented and names no setup steps. The code is never the expensive part; ` +\n \"the setup is, and a provider whose setup is undocumented is one nobody can stand up.\",\n );\n }\n\n for (const field of provider.fields) {\n if (field.help.trim().length < 10) {\n problems.push(`${provider.id}.${field.key} has no help text worth reading.`);\n }\n }\n\n // A verify that proves less than it appears to is worse than no verify, so a\n // provider that ships one has to say what it does NOT cover.\n if (provider.verify && !provider.proves) {\n problems.push(\n `${provider.id} ships a verify but does not say what it PROVES. A green tick that means more than it ` +\n \"should is worse than no tick — name the step it does not cover.\",\n );\n }\n\n if (provider.sandbox === \"restricted-reach\" && !provider.summary.toLowerCase().includes(\"reach\")) {\n problems.push(\n `${provider.id} is declared restricted-reach and its summary never mentions reach. That shape looks ` +\n \"exactly like a successful post nobody can see, so the surface has to say so before anyone runs it.\",\n );\n }\n\n return problems;\n}\n\n/** True when a provider's declared sandbox can actually be selected at run time. */\nexport function providerSandboxIsSelectable(provider: ProviderAdapter): boolean {\n return sandboxIsSelectable(provider.sandbox);\n}\n","/**\n * Raw MIME in, headers / parts / attachments out — the payload TRANSFORM an\n * inbound-email trigger declares.\n *\n * Written from scratch (the owner: no third-party code), as ONE implementation\n * in two runtimes — `php/src/Mime.php` is the twin — and held to the authored\n * corpus under `fixtures/mime/`, which both must reproduce byte for byte.\n * `fixtures/mime/README.md` lists every decision a case pins; the ones that\n * are easy to get differently in two languages are restated here:\n *\n * - Header lines are read as Latin-1 (one byte, one char), unfolded with a\n * single space, names lowercased, values RFC 2047-decoded (B and Q; adjacent\n * encoded-words join without the whitespace between them).\n * - The line break before a boundary delimiter belongs to the DELIMITER.\n * - `size` is the transfer-decoded byte length, before any charset conversion.\n * - A text part comes back as UTF-8 with the message's own line endings. A\n * charset this module does not decode (anything but utf-8, us-ascii,\n * iso-8859-1, iso-8859-15, windows-1252) leaves the part with no text.\n * - Every non-text leaf is an attachment, as is a text leaf declared\n * `attachment`; attachments carry their bytes as base64. `filename*` (RFC\n * 2231) wins over `filename`, which wins over Content-Type's `name`.\n * - `messageId` is the header verbatim; `contentId` has its angle brackets\n * stripped, because `cid:` references omit them.\n */\n\nexport type MimeHeader = { name: string; value: string };\n\nexport type MimePart = {\n index: number;\n contentType: string;\n charset: string | null;\n disposition: \"inline\" | \"attachment\" | null;\n filename: string | null;\n contentId: string | null;\n size: number;\n headers: MimeHeader[];\n};\n\nexport type MimeAttachment = MimePart & { content: string };\n\nexport type MimeMessage = {\n headers: MimeHeader[];\n subject: string | null;\n messageId: string | null;\n text: string | null;\n html: string | null;\n parts: MimePart[];\n attachments: MimeAttachment[];\n};\n\n/* ── Bytes ───────────────────────────────────────────────────────────────── */\n\nconst CR = 13;\nconst LF = 10;\n\nfunction latin1(bytes: Uint8Array): string {\n let s = \"\";\n for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]!);\n return s;\n}\n\nfunction bytesOfLatin1(s: string): Uint8Array {\n const out = new Uint8Array(s.length);\n for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i) & 0xff;\n return out;\n}\n\n/** Where the header block ends: the first blank line, CRLF or LF. Returns [headerEnd, bodyStart]. */\nfunction splitEntity(bytes: Uint8Array): [number, number] {\n for (let i = 0; i < bytes.length; i++) {\n if (bytes[i] === LF) {\n if (bytes[i + 1] === LF) return [i, i + 2];\n if (bytes[i + 1] === CR && bytes[i + 2] === LF) return [i, i + 3];\n }\n }\n return [bytes.length, bytes.length];\n}\n\n/* ── Headers ─────────────────────────────────────────────────────────────── */\n\nfunction parseHeaders(block: string): MimeHeader[] {\n const lines = block.split(/\\r?\\n/);\n const unfolded: string[] = [];\n for (const line of lines) {\n if (line === \"\") continue;\n if ((line.startsWith(\" \") || line.startsWith(\"\\t\")) && unfolded.length > 0) {\n unfolded[unfolded.length - 1] += \" \" + line.trim();\n } else {\n unfolded.push(line);\n }\n }\n const headers: MimeHeader[] = [];\n for (const line of unfolded) {\n const colon = line.indexOf(\":\");\n if (colon <= 0) continue;\n headers.push({ name: line.slice(0, colon).trim().toLowerCase(), value: decodeWords(line.slice(colon + 1).trim()) });\n }\n return headers;\n}\n\nfunction header(headers: MimeHeader[], name: string): string | null {\n return headers.find((h) => h.name === name)?.value ?? null;\n}\n\n/* ── RFC 2047 encoded-words ──────────────────────────────────────────────── */\n\nconst ENCODED_WORD = /=\\?([^?\\s]+)\\?([BbQq])\\?([^?\\s]*)\\?=/g;\n\nfunction decodeWords(value: string): string {\n // Adjacent encoded-words are one token: the whitespace between them is not\n // part of the text (RFC 2047 §6.2).\n const joined = value.replace(/(\\?=)[ \\t\\r\\n]+(=\\?)/g, \"$1$2\");\n return joined.replace(ENCODED_WORD, (whole, charset: string, encoding: string, text: string) => {\n const bytes =\n encoding.toUpperCase() === \"B\"\n ? base64Bytes(text)\n : bytesOfLatin1(text.replace(/_/g, \" \").replace(/=([0-9A-Fa-f]{2})/g, (_m, h: string) => String.fromCharCode(parseInt(h, 16))));\n const decoded = decodeCharset(bytes, charset);\n return decoded ?? whole;\n });\n}\n\n/* ── Parameters (Content-Type, Content-Disposition), with RFC 2231 ───────── */\n\nfunction parseParams(value: string): { main: string; params: Record<string, string> } {\n const [head, ...rest] = value.split(\";\");\n const params: Record<string, string> = {};\n const continued: Record<string, string[]> = {};\n for (const piece of rest) {\n const eq = piece.indexOf(\"=\");\n if (eq < 0) continue;\n let key = piece.slice(0, eq).trim().toLowerCase();\n let raw = piece.slice(eq + 1).trim();\n if (raw.startsWith('\"') && raw.endsWith('\"') && raw.length >= 2) raw = raw.slice(1, -1).replace(/\\\\(.)/g, \"$1\");\n\n // RFC 2231: `name*=charset'lang'percent-encoded`, and `name*0*=`, `name*1=`\n // continuations. A starred key carries an encoding; a numbered one is a\n // segment.\n const segment = /^(.+?)\\*(\\d+)(\\*)?$/.exec(key);\n if (segment) {\n const [, base, n, star] = segment;\n (continued[base!] ??= [])[Number(n)] = star ? decode2231(raw, Number(n) === 0) : raw;\n continue;\n }\n if (key.endsWith(\"*\")) {\n key = key.slice(0, -1);\n params[key] = decode2231(raw, true);\n continue;\n }\n params[key] ??= raw;\n }\n for (const [base, parts] of Object.entries(continued)) params[base] = parts.join(\"\");\n return { main: (head ?? \"\").trim().toLowerCase(), params };\n}\n\nlet pendingCharset2231 = \"utf-8\";\n\nfunction decode2231(raw: string, first: boolean): string {\n let text = raw;\n if (first) {\n const m = /^([^']*)'[^']*'(.*)$/s.exec(raw);\n if (m) {\n pendingCharset2231 = m[1] || \"utf-8\";\n text = m[2]!;\n }\n }\n const bytes = bytesOfLatin1(text.replace(/%([0-9A-Fa-f]{2})/g, (_m, h: string) => String.fromCharCode(parseInt(h, 16))));\n return decodeCharset(bytes, pendingCharset2231) ?? text;\n}\n\n/* ── Transfer encodings and charsets ─────────────────────────────────────── */\n\nfunction base64Bytes(text: string): Uint8Array {\n return new Uint8Array(Buffer.from(text.replace(/[^A-Za-z0-9+/=]/g, \"\"), \"base64\"));\n}\n\nfunction quotedPrintableBytes(bytes: Uint8Array): Uint8Array {\n const out: number[] = [];\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n if (b !== 0x3d /* = */) {\n out.push(b);\n continue;\n }\n const a = bytes[i + 1];\n const c = bytes[i + 2];\n // Soft line break: `=` then CRLF or LF.\n if (a === LF) {\n i += 1;\n continue;\n }\n if (a === CR && c === LF) {\n i += 2;\n continue;\n }\n if (a !== undefined && c !== undefined && isHex(a) && isHex(c)) {\n out.push(parseInt(String.fromCharCode(a, c), 16));\n i += 2;\n continue;\n }\n out.push(b);\n }\n return new Uint8Array(out);\n}\n\nfunction isHex(b: number): boolean {\n return (b >= 0x30 && b <= 0x39) || (b >= 0x41 && b <= 0x46) || (b >= 0x61 && b <= 0x66);\n}\n\nfunction transferDecode(body: Uint8Array, encoding: string | null): Uint8Array {\n switch ((encoding ?? \"7bit\").trim().toLowerCase()) {\n case \"base64\":\n return base64Bytes(latin1(body));\n case \"quoted-printable\":\n return quotedPrintableBytes(body);\n default:\n return body;\n }\n}\n\n/** UTF-8 text for a charset this module decodes; null for one it does not. */\nfunction decodeCharset(bytes: Uint8Array, charset: string): string | null {\n switch (charset.trim().toLowerCase()) {\n case \"utf-8\":\n case \"utf8\":\n return new TextDecoder(\"utf-8\").decode(bytes);\n case \"us-ascii\":\n case \"ascii\":\n case \"iso-8859-1\":\n case \"latin1\":\n // Node's `latin1` is true ISO-8859-1 (0x80–0x9F are C1 controls); the\n // WHATWG TextDecoder would silently give windows-1252 instead, which\n // the PHP twin does not.\n return Buffer.from(bytes).toString(\"latin1\");\n case \"windows-1252\":\n case \"cp1252\":\n return new TextDecoder(\"windows-1252\").decode(bytes);\n case \"iso-8859-15\":\n case \"latin9\":\n return new TextDecoder(\"iso-8859-15\").decode(bytes);\n default:\n return null;\n }\n}\n\n/* ── Entities ────────────────────────────────────────────────────────────── */\n\ntype Collected = { parts: MimePart[]; attachments: MimeAttachment[]; text: string | null; html: string | null };\n\nfunction walk(bytes: Uint8Array, entityHeaders: MimeHeader[] | null, acc: Collected): void {\n const [headerEnd, bodyStart] = splitEntity(bytes);\n const headers = entityHeaders ?? parseHeaders(latin1(bytes.subarray(0, headerEnd)));\n const body = bytes.subarray(entityHeaders ? 0 : bodyStart);\n\n const type = parseParams(header(headers, \"content-type\") ?? \"text/plain\");\n\n if (type.main.startsWith(\"multipart/\") && type.params.boundary) {\n for (const child of splitMultipart(body, type.params.boundary)) walk(child, null, acc);\n return;\n }\n\n const disposition = parseParams(header(headers, \"content-disposition\") ?? \"\");\n const decoded = transferDecode(body, header(headers, \"content-transfer-encoding\"));\n const declaredDisposition = disposition.main === \"attachment\" || disposition.main === \"inline\" ? disposition.main : null;\n const filename = disposition.params.filename ?? type.params.name ?? null;\n const contentId = header(headers, \"content-id\")?.replace(/^<|>$/g, \"\") ?? null;\n const isText = type.main.startsWith(\"text/\");\n const isAttachment = declaredDisposition === \"attachment\" || !isText;\n\n const part: MimePart = {\n index: acc.parts.length,\n contentType: type.main,\n charset: type.params.charset?.toLowerCase() ?? null,\n disposition: declaredDisposition,\n filename,\n contentId,\n size: decoded.length,\n headers,\n };\n acc.parts.push(part);\n\n if (isAttachment) {\n acc.attachments.push({ ...part, content: Buffer.from(decoded).toString(\"base64\") });\n return;\n }\n\n const text = decodeCharset(decoded, type.params.charset ?? \"utf-8\");\n if (text === null) return;\n if (type.main === \"text/plain\" && acc.text === null) acc.text = text;\n if (type.main === \"text/html\" && acc.html === null) acc.html = text;\n}\n\n/** The bodies between delimiters — the CRLF before each delimiter is the delimiter's. */\nfunction splitMultipart(body: Uint8Array, boundary: string): Uint8Array[] {\n const delimiter = bytesOfLatin1(`--${boundary}`);\n const children: Uint8Array[] = [];\n let partStart = -1;\n\n for (let i = 0; i <= body.length - delimiter.length; i++) {\n if (i !== 0 && body[i - 1] !== LF) continue;\n if (!startsWith(body, i, delimiter)) continue;\n\n // The preceding line break belongs to the delimiter.\n let partEnd = i;\n if (partEnd > 0 && body[partEnd - 1] === LF) partEnd -= 1;\n if (partEnd > 0 && body[partEnd - 1] === CR) partEnd -= 1;\n if (partStart >= 0) children.push(body.subarray(partStart, partEnd));\n\n let j = i + delimiter.length;\n const closing = body[j] === 0x2d && body[j + 1] === 0x2d;\n if (closing) return children;\n // Skip transport padding and the delimiter's own line ending.\n while (j < body.length && body[j] !== LF) j++;\n partStart = j + 1;\n i = j;\n }\n if (partStart >= 0 && partStart <= body.length) children.push(body.subarray(partStart));\n return children;\n}\n\nfunction startsWith(haystack: Uint8Array, at: number, needle: Uint8Array): boolean {\n for (let k = 0; k < needle.length; k++) if (haystack[at + k] !== needle[k]) return false;\n return true;\n}\n\n/* ── The entry point ─────────────────────────────────────────────────────── */\n\nexport function parseMime(raw: Uint8Array | string): MimeMessage {\n const bytes = typeof raw === \"string\" ? new Uint8Array(Buffer.from(raw, \"utf8\")) : raw;\n const [headerEnd, bodyStart] = splitEntity(bytes);\n const headers = parseHeaders(latin1(bytes.subarray(0, headerEnd)));\n const body = bytes.subarray(bodyStart);\n\n const acc: Collected = { parts: [], attachments: [], text: null, html: null };\n const type = parseParams(header(headers, \"content-type\") ?? \"text/plain\");\n if (type.main.startsWith(\"multipart/\") && type.params.boundary) {\n for (const child of splitMultipart(body, type.params.boundary)) walk(child, null, acc);\n } else {\n // A non-multipart message is one part whose headers are the message's\n // content-* headers.\n walk(body, headers.filter((h) => h.name.startsWith(\"content-\")), acc);\n }\n\n return {\n headers,\n subject: header(headers, \"subject\"),\n messageId: header(headers, \"message-id\"),\n text: acc.text,\n html: acc.html,\n parts: acc.parts,\n attachments: acc.attachments,\n };\n}\n","/**\n * Dry-verify — proving the transport with a credential that cannot work.\n *\n * ## The discipline, and why it beats a fake server\n *\n * A connector written from documentation can be wrong in three ways that look\n * identical until a real credential arrives:\n *\n * 1. the URL is wrong,\n * 2. the request shape is wrong,\n * 3. the error handling never runs.\n *\n * **All three are invisible to a test with a fake server**, because a fake\n * server agrees with whatever the code does. That is the single most important\n * thing this module encodes: a green unit suite against a mock proves the code\n * is consistent with itself.\n *\n * So a probe calls the REAL API with a deliberately invalid credential and\n * requires an **auth-shaped refusal**. That proves the host resolved, the path\n * exists, the method was accepted, and a failure was recognised as a failure.\n *\n * ## What counts as a pass, precisely\n *\n * An auth-shaped refusal is `401`, `403`, or a `404` on a provider that answers\n * 404 for an unknown credential-in-the-path (a Discord webhook does exactly\n * this). The provider declares which, because guessing here would let a genuine\n * \"this endpoint moved\" 404 read as success — the one outcome a probe exists to\n * catch.\n *\n * ## Offline is SKIPPED, never failed\n *\n * A check that goes red on a train gets ignored, and then it is worth nothing\n * when it goes red for real. Unreachable is reported as skipped with the reason,\n * and the assertions that need no network still run.\n *\n * ## And nothing is ever sent\n *\n * Every probe is a READ. A probe that wrote something would be a send, and sends\n * are the host's to gate.\n */\n\nimport { classifyError, classifyStatus, type FailureKind } from \"./delivery\";\n\nexport type ProbeOutcome = \"pass\" | \"fail\" | \"skip\";\n\nexport type ProbeResult = {\n connector: string;\n outcome: ProbeOutcome;\n /** What happened, in the provider's own words where possible. */\n detail: string;\n status?: number;\n kind?: FailureKind;\n};\n\nexport type ProbeSpec = {\n /** Which connector this probes. */\n connector: string;\n /**\n * The read-only request, fully formed, carrying a credential that cannot be\n * valid. Built by the connector so the probe exercises the connector's own URL\n * and auth placement rather than a second copy of them.\n */\n request: () => Promise<{ status: number; body: string }>;\n /**\n * Statuses that count as an auth-shaped refusal for THIS provider.\n *\n * Declared rather than assumed. Discord answers `404` for an unknown webhook\n * id, and on a provider that does not, a 404 means the endpoint moved — the\n * exact drift a probe is for.\n */\n authStatuses: number[];\n /** Why those statuses, for whoever reads a failure at 3am. */\n why: string;\n};\n\n/**\n * Run one probe.\n *\n * Never throws. A probe that threw would take the suite down with it, and the\n * suite's whole value is that it runs everywhere including on a laptop with no\n * network.\n */\nexport async function probe(spec: ProbeSpec): Promise<ProbeResult> {\n let response: { status: number; body: string };\n\n try {\n response = await spec.request();\n } catch (error) {\n const classified = classifyError(error);\n\n // Unreachable is a fact about the machine, not about the connector.\n if (classified.kind === \"unreachable\") {\n return { connector: spec.connector, outcome: \"skip\", detail: `offline — ${classified.detail}` };\n }\n\n return {\n connector: spec.connector,\n outcome: \"fail\",\n detail: `the request failed before any status arrived — ${classified.detail}`,\n kind: classified.kind,\n };\n }\n\n const { status, body } = response;\n\n if (spec.authStatuses.includes(status)) {\n return {\n connector: spec.connector,\n outcome: \"pass\",\n status,\n detail: `${status} — the provider refused an invalid credential, which proves host, path, method and error handling. ${spec.why}`,\n };\n }\n\n if (status < 400) {\n // A success with a credential that cannot be valid means the request is not\n // doing what it appears to. Worse than a failure, because it is green.\n return {\n connector: spec.connector,\n outcome: \"fail\",\n status,\n detail:\n `${status} — the provider ACCEPTED a deliberately invalid credential. Either the credential is reaching ` +\n \"nothing (wrong header, wrong field) or this endpoint does not authenticate. Neither is safe to ship.\",\n };\n }\n\n const classified = classifyStatus(status, body);\n\n return {\n connector: spec.connector,\n outcome: \"fail\",\n status,\n kind: classified.kind,\n detail:\n `${status} — expected one of ${spec.authStatuses.join(\", \")}. A ${status} here usually means the endpoint ` +\n `moved or the path is wrong, which no amount of local mocking would ever have said. ${body.slice(0, 200)}`,\n };\n}\n\nexport type ProbeReport = {\n results: ProbeResult[];\n passed: number;\n failed: number;\n skipped: number;\n /** True when nothing failed. Skips do not fail a report. */\n ok: boolean;\n};\n\n/** Run a set of probes and summarise. Sequential: probes hit real providers. */\nexport async function runProbes(specs: ProbeSpec[]): Promise<ProbeReport> {\n const results: ProbeResult[] = [];\n\n for (const spec of specs) results.push(await probe(spec));\n\n const passed = results.filter((result) => result.outcome === \"pass\").length;\n const failed = results.filter((result) => result.outcome === \"fail\").length;\n const skipped = results.filter((result) => result.outcome === \"skip\").length;\n\n return { results, passed, failed, skipped, ok: failed === 0 };\n}\n","/**\n * Measuring and slicing text the way providers actually measure and slice it.\n *\n * ## Why a package owns this at all\n *\n * Two of the nastiest bugs in a connector catalogue are one-liners, and both\n * come from reaching for the obvious JavaScript idiom:\n *\n * 1. **`text.length` is not a count of anything a provider limits.** It counts\n * UTF-16 code units. `\"👍\".length` is 2 and `\"👨👩👧\".length` is 8, so it\n * overcounts exactly the posts most likely to sit near a limit — and it fails\n * in the worse direction, because a post that passes our check and is then\n * refused by the server reads as the network being flaky rather than as our\n * bug.\n * 2. **`indexOf` returns a character index, and rich-text formats want BYTES.**\n * AT Protocol facets are UTF-8 byte ranges. An implementation using character\n * indices is correct for ASCII and silently corrupts every post containing an\n * emoji or an accent — and it corrupts the *link*, so the post looks fine and\n * the link goes somewhere wrong.\n *\n * Neither is discoverable by testing with ASCII, which is what everyone tests\n * with. So the package makes the wrong thing unreachable: a `ByteRange` is a\n * type, not a convention, and it can only be produced by a function that\n * encoded the string.\n *\n * ## The unit is declared, never assumed\n *\n * Providers count different things — Bluesky counts graphemes, Discord and\n * Telegram count characters, some count bytes. `TextUnit` makes the choice\n * explicit at the point a limit is declared, so nobody has to remember which\n * network is which.\n */\n\n/** What a provider actually counts. Declared per connector, never guessed. */\nexport type TextUnit = \"graphemes\" | \"characters\" | \"utf8-bytes\";\n\n/**\n * A half-open range of UTF-8 BYTES.\n *\n * Named for the unit because the whole failure mode is somebody passing\n * character offsets into a field that wants bytes. The two are equal for ASCII,\n * which is why this survives review and fails in production.\n */\nexport type ByteRange = {\n byteStart: number;\n byteEnd: number;\n};\n\nconst encoder = new TextEncoder();\n\n/**\n * Count graphemes — what a person means by \"characters\".\n *\n * `Intl.Segmenter` is the correct tool and has been in Node since 16. The\n * fallback is the spread operator, which counts code points rather than UTF-16\n * units and so is wrong by less. Both are better than `.length`.\n */\nexport function graphemes(text: string): number {\n const Segmenter = (\n Intl as unknown as {\n Segmenter?: new (locale?: string, options?: object) => { segment(input: string): Iterable<unknown> };\n }\n ).Segmenter;\n\n if (!Segmenter) return [...text].length;\n\n let count = 0;\n for (const _ of new Segmenter(undefined, { granularity: \"grapheme\" }).segment(text)) count++;\n\n return count;\n}\n\n/** Length of `text` in UTF-8 bytes. */\nexport function utf8Length(text: string): number {\n return encoder.encode(text).length;\n}\n\n/**\n * Count `text` in whichever unit a provider enforces.\n *\n * The one function every limit check goes through, so a connector never has to\n * decide — and so `text.length` never appears in connector source at all.\n */\nexport function measure(text: string, unit: TextUnit): number {\n if (unit === \"graphemes\") return graphemes(text);\n if (unit === \"utf8-bytes\") return utf8Length(text);\n\n return text.length;\n}\n\n/**\n * The UTF-8 byte range of a substring that starts at a CHARACTER index.\n *\n * The bridge between the index a regex gives you and the offsets a provider\n * wants. Takes the character index because that is what `matchAll` produces, and\n * returns bytes because that is what goes on the wire — doing the conversion in\n * one place is the entire point.\n */\nexport function byteRangeOf(text: string, charIndex: number, substring: string): ByteRange {\n const byteStart = utf8Length(text.slice(0, charIndex));\n\n return { byteStart, byteEnd: byteStart + utf8Length(substring) };\n}\n\n/**\n * Extract the substring a byte range names.\n *\n * Exists so a test can prove a range points where it claims to, which is the\n * only way to catch an off-by-one that ASCII hides. A range that lands\n * mid-character yields the replacement character rather than throwing, because\n * the useful failure here is a visibly wrong string in an assertion, not an\n * exception three frames away.\n */\nexport function sliceByteRange(text: string, range: ByteRange): string {\n const bytes = encoder.encode(text);\n\n return new TextDecoder().decode(bytes.slice(range.byteStart, range.byteEnd));\n}\n\n/**\n * Bare URLs in `text`, with their UTF-8 byte ranges.\n *\n * Trailing punctuation is excluded: a link at the end of a sentence would\n * otherwise swallow the full stop and 404. Closing brackets go too, for the same\n * reason and because a URL in parentheses is common in prose.\n *\n * This is the generic half of a \"link facet\" — a connector maps the ranges into\n * whatever its provider's rich-text shape is. The package owns the offsets\n * because the offsets are what people get wrong; it does not own the provider's\n * schema, because that is the provider's.\n */\nexport function linkRanges(text: string): Array<ByteRange & { url: string }> {\n const found: Array<ByteRange & { url: string }> = [];\n\n for (const match of text.matchAll(/https?:\\/\\/[^\\s]+/g)) {\n if (match.index === undefined) continue;\n const url = match[0].replace(/[.,;:!?)\\]}'\"]+$/, \"\");\n found.push({ ...byteRangeOf(text, match.index, url), url });\n }\n\n return found;\n}\n","/**\n * Rendering — what a provider would ACTUALLY receive.\n *\n * ## Why a connector package owns this, and why it is dangerous\n *\n * One piece of content often has to reach several providers whose limits are\n * incompatible. Without adaptation the same text is wasteful on one and refused\n * by another. With naive adaptation, the bytes that reach the public are not the\n * bytes anyone approved — and every guarantee a host makes about approval\n * becomes a lie the moment adaptation exists.\n *\n * So rendering happens **before** whatever gate the host applies: the person\n * approving sees exactly what each provider will receive and approves *that*.\n * Nothing is invented after the decision.\n *\n * ## Which forces three properties\n *\n * 1. **Pure.** Rules in, payload out. No clock, no randomness, no network. If\n * the preview and the send could disagree, the approval means nothing.\n * Anything variable — an instance's configured limit — is resolved by the\n * CALLER and passed in as a rule, so it becomes part of what was approved.\n * 2. **Versioned.** Any change to how text is split, counted or faceted bumps\n * `RENDERER_VERSION`, and the version is **inside** the payload hash rather\n * than beside it, so it cannot be checked separately and forgotten.\n * 3. **Honest about loss.** Anything that could not be fitted is reported as a\n * problem, never quietly applied. A truncated URL is worse than a refused\n * post, because it looks deliberate and goes somewhere wrong.\n *\n * ## And one rule about where length lives\n *\n * **No length rule outside this module.** A validator and a renderer that both\n * judge length will disagree, and then the validator refuses content the\n * renderer had already solved. `validate()` on a connector checks things the\n * renderer cannot — media, alt text, a required field — and leaves length here.\n */\n\nimport { linkRanges, measure, type ByteRange, type TextUnit } from \"./text\";\n\n/**\n * Bump on ANY change to how text is split, counted, or faceted.\n *\n * A payload approved under `1` refuses to send once this reads `2`, because the\n * payload the approver saw is no longer the payload that would go out. That is\n * the correct outcome and the reason the field exists.\n */\nexport const RENDERER_VERSION = \"render@1\";\n\nexport type Segment = {\n /** The text of this message, exactly as it would be sent. */\n text: string;\n /** How many of the provider's unit this segment uses. */\n count: number;\n /** Link ranges, in UTF-8 bytes, **relative to this segment**. */\n links?: Array<ByteRange & { url: string }>;\n};\n\nexport type RenderedPayload = {\n /** One entry per message. More than one means a thread. */\n segments: Segment[];\n /** What the provider counts, so a preview can label the number. */\n unit: TextUnit;\n /**\n * What the provider allows, or **null where it imposes no limit**.\n *\n * Null rather than a very large number: `Number.MAX_SAFE_INTEGER` is\n * arithmetically fine and reads, on a person's screen, as\n * `157 / 9007199254740991` — a number pretending to be a fact. \"No limit\" and\n * \"a limit so large it cannot be hit\" are different statements.\n */\n limit: number | null;\n /** Why this could not be rendered, when it could not. Never silently repaired. */\n problems: string[];\n rendererVersion: string;\n};\n\n/**\n * The rules a provider imposes on a message, as DATA.\n *\n * Declarative on purpose. Across the providers this was extracted from, the\n * differences that mattered were all values — the limit, the unit, whether a\n * thread mechanism exists, whether links carry byte offsets. A provider whose\n * rendering genuinely needs code supplies its own `render` on the connector and\n * ignores this; the point of the rule set is that most do not have to.\n */\nexport type RenderRules = {\n /**\n * The provider's limit per message, or `null` where it imposes none.\n *\n * **`null` means UNCOUNTED, and it is a real answer** — distinct from \"we do\n * not know\", which is not a renderable state at all. A caller that does not\n * know the limit must find out before rendering, because every alternative\n * silently produces the wrong bytes.\n *\n * ## This must stay RESOLVABLE PER CONNECTION, and never become static\n *\n * A Mastodon instance's character limit comes from the server it is running:\n * one instance allows 500, another 5000, and the same connector talks to\n * both. Baking the limit into the connector breaks in the quietest way\n * available — rendering to 500 on an instance that allows 5000 wastes most of\n * a post, and rendering to 5000 on one that allows 500 produces a refusal the\n * author cannot see coming from the preview.\n *\n * So the limit is resolved by the CALLER, from the connection, and passed IN\n * — which is also what makes it part of what was approved rather than\n * something decided later behind the approver's back. `withResolvedLimit()`\n * is the supported way to do that, and a test pins that per-call rules beat\n * declared ones.\n */\n limit: number | null;\n unit: TextUnit;\n /**\n * Whether this provider can carry a sequence of connected messages.\n *\n * False means over-length content is REFUSED rather than split — splitting\n * would invent a structure the provider does not have, and a numbered sequence\n * of unconnected messages is worse than a refusal because it looks deliberate.\n */\n thread: boolean;\n /** Include UTF-8 link ranges per segment, for providers with rich-text spans. */\n links?: boolean;\n /** Human name, used in refusal messages. */\n label: string;\n /**\n * Suffix added to each part of a thread, given the 1-based index and total.\n *\n * Defaults to ` (i/n)`. Its cost is measured and subtracted from the limit\n * BEFORE splitting, because a suffix discovered afterwards is a bug that only\n * appears on long threads.\n */\n numbering?: (index: number, total: number) => string;\n};\n\nconst defaultNumbering = (index: number, total: number): string => ` (${index}/${total})`;\n\n/**\n * Where a sentence ends: after `.`, `!` or `?` **only when whitespace follows**,\n * or after a newline.\n *\n * The whitespace condition is the whole point. A regex that treats every `.` as\n * a terminator splits inside `https://example.test/x`, and the URL can then land\n * across two messages — a link that goes nowhere, from copy that looked fine in\n * review. Abbreviations survive for the same reason.\n */\nconst SENTENCE_BOUNDARY = /(?<=[.!?])(?=\\s)|(?<=\\n)/;\n\n/**\n * Split text into parts that each fit the limit.\n *\n * **Sentence boundaries first, then words, and never mid-grapheme.** A thread\n * that splits mid-argument is worse than one that splits a little unevenly, so\n * this prefers a natural break even when it wastes room.\n *\n * A single token that cannot fit at all is reported as a problem rather than\n * hard-cut — see the module note about truncated URLs.\n */\nexport function splitToFit(\n text: string,\n limit: number,\n unit: TextUnit,\n): { parts: string[]; problems: string[] } {\n const count = (value: string): number => measure(value, unit);\n const trimmed = text.trim();\n\n if (count(trimmed) <= limit) return { parts: trimmed === \"\" ? [] : [trimmed], problems: [] };\n\n const problems: string[] = [];\n const parts: string[] = [];\n\n // Sentence-ish units, kept WITH their punctuation: a fragment that loses its\n // full stop reads as truncated even when it is complete.\n //\n // **A terminator only ends a sentence when whitespace or the end follows it.**\n // The obvious sentence regex splits inside `https://example.test/x`, and a\n // URL can then land across two messages — a link that goes nowhere, from copy\n // that looked fine in review. Abbreviations survive for the same reason.\n // Newlines still split: a deliberate break is a better seam than any sentence.\n const sentences = trimmed.split(SENTENCE_BOUNDARY).filter((part) => part !== \"\");\n\n let current = \"\";\n const flush = (): void => {\n if (current.trim() !== \"\") parts.push(current.trim());\n current = \"\";\n };\n\n for (const sentence of sentences) {\n const candidate = current + sentence;\n\n if (count(candidate.trim()) <= limit) {\n current = candidate;\n continue;\n }\n\n flush();\n\n if (count(sentence.trim()) <= limit) {\n current = sentence;\n continue;\n }\n\n let buffer = \"\";\n for (const word of sentence.split(/(\\s+)/)) {\n if (count((buffer + word).trim()) <= limit) {\n buffer += word;\n continue;\n }\n\n if (buffer.trim() !== \"\") parts.push(buffer.trim());\n buffer = \"\";\n\n if (count(word.trim()) > limit) {\n problems.push(\n `\"${word.trim().slice(0, 40)}…\" is ${count(word.trim())} ${unit} on its own, longer than one whole ` +\n `message (${limit}). It cannot be split without breaking it, so this needs rewriting rather than adapting.`,\n );\n continue;\n }\n\n buffer = word;\n }\n\n current = buffer;\n }\n\n flush();\n\n return { parts: parts.filter((part) => part !== \"\"), problems };\n}\n\n/**\n * Render text against a provider's rule set.\n *\n * The one entry point. A connector that needs nothing bespoke declares rules and\n * calls this; the shared behaviour — numbering cost, per-segment link offsets,\n * refusal where there is no thread mechanism — is then identical everywhere,\n * which is what makes a catalogue feel like one thing.\n */\nexport function render(text: string, rules: RenderRules): RenderedPayload {\n const { limit, unit, label } = rules;\n const count = (value: string): number => measure(value, unit);\n const trimmed = text.trim();\n\n if (limit === null || count(trimmed) <= limit) {\n return {\n segments: [{ text: trimmed, count: count(trimmed), ...(rules.links ? { links: linkRanges(trimmed) } : {}) }],\n unit,\n limit,\n problems: [],\n rendererVersion: RENDERER_VERSION,\n };\n }\n\n if (!rules.thread) {\n return {\n segments: [{ text: trimmed, count: count(trimmed), ...(rules.links ? { links: linkRanges(trimmed) } : {}) }],\n unit,\n limit,\n problems: [\n `${count(trimmed)} ${unit}; ${label}'s limit is ${limit}, and it has no thread mechanism to split into. ` +\n \"This needs shortening rather than adapting.\",\n ],\n rendererVersion: RENDERER_VERSION,\n };\n }\n\n const numbering = rules.numbering ?? defaultNumbering;\n const first = splitToFit(text, limit, unit);\n const willNumber = first.parts.length > 1;\n\n // Re-split against a reduced limit when numbering will be added, so the suffix\n // is accounted for rather than discovered afterwards. `99/99` is the widest\n // realistic suffix; using the real total would need the split that depends on\n // it, which is circular.\n const suffixCost = willNumber ? count(numbering(99, 99)) : 0;\n const { parts, problems } = willNumber ? splitToFit(text, limit - suffixCost, unit) : first;\n\n const segments: Segment[] = parts.map((part, index) => {\n const withNumber = parts.length > 1 ? `${part}${numbering(index + 1, parts.length)}` : part;\n\n return {\n text: withNumber,\n count: count(withNumber),\n // Computed per segment and never sliced from the original — an offset into\n // the whole text is meaningless once the text has been split.\n ...(rules.links ? { links: linkRanges(withNumber) } : {}),\n };\n });\n\n const over = segments.filter((segment) => segment.count > limit);\n if (over.length > 0) {\n problems.push(\n `${over.length} segment(s) still exceed ${limit} ${unit} after splitting. That is a renderer defect rather ` +\n \"than a content problem — the split should not produce an over-length message.\",\n );\n }\n\n return { segments, unit, limit, problems, rendererVersion: RENDERER_VERSION };\n}\n\n/**\n * A stable fingerprint of what would actually be sent.\n *\n * This is what an approval covers. A host recomputes it at dispatch and refuses\n * on mismatch: if the rendering changed for ANY reason — edited text, a changed\n * limit, a new renderer version — the hashes differ, because the approver\n * approved a payload rather than an intention.\n *\n * Async because it uses WebCrypto, which is the only hash available on every\n * runtime the suite supports. A host that wants it synchronously should hold the\n * value rather than recompute it.\n */\nexport async function payloadHash(payload: RenderedPayload): Promise<string> {\n const subtle = globalThis.crypto?.subtle;\n\n if (!subtle) {\n throw new Error(\n \"WebCrypto is not available in this runtime, so a payload hash cannot be computed. \" +\n \"Refusing rather than returning a weaker fingerprint that a host would trust equally.\",\n );\n }\n\n const canonical = JSON.stringify({\n // Inside the hash, deliberately — see RENDERER_VERSION.\n v: payload.rendererVersion,\n segments: payload.segments.map((segment) => segment.text),\n });\n\n const digest = await subtle.digest(\"SHA-256\", new TextEncoder().encode(canonical));\n\n return Array.from(new Uint8Array(digest))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Refuse empty content, centrally.\n *\n * The sha of the empty string is a valid sha, so an empty payload passes every\n * byte-verification a host can write. Refusing it in one place is what stops\n * every one of those checks from being hollow.\n */\nexport function isEmptyPayload(payload: RenderedPayload): boolean {\n return payload.segments.every((segment) => segment.text.trim() === \"\");\n}\n\n/**\n * Apply a per-connection limit over a connector's declared rules.\n *\n * The one supported way to do the thing `RenderRules.limit` insists on: a\n * Mastodon instance publishes its own `max_toot_chars`, a Discord webhook does\n * not, and the same connector serves both. Resolving happens at the call site,\n * with the connection in hand, and the resolved value travels INTO `render` so\n * it is part of the payload the approver saw.\n *\n * `undefined` keeps whatever the connector declared — \"I did not look\" is not\n * the same as \"there is no limit\", and only the second is `null`.\n */\nexport function withResolvedLimit(rules: RenderRules, limit: number | null | undefined): RenderRules {\n return limit === undefined ? rules : { ...rules, limit };\n}\n","/**\n * The seam — two objects, deliberately separate.\n *\n * They answer different questions and change on different clocks:\n *\n * - **`ProviderAdapter`** — *how does an operator stand this up, and does it\n * work?* Credential shapes, setup steps with the trap in each one named,\n * scopes, a read-only verify. It changes when the provider changes its\n * onboarding, which is rarely and disruptively.\n * - **`Connector`** — *what does calling this actually involve?* Capabilities,\n * delivery behaviour, validation, rendering, the call itself, measurement. It\n * changes when the API changes, which is often and quietly.\n *\n * Folding them together produces an object that has to be edited for both, and\n * a diff that cannot tell you which happened.\n *\n * ## What this package does NOT own, and never will\n *\n * Everything on this list belongs to the HOST, because each is enforced in one\n * place and every connector inherits it from the dispatch path rather than\n * implementing it:\n *\n * - **approval** — whether this may go out at all;\n * - **liveness** — `call(..., { dryRun })` takes the flag; a connector must\n * never resolve its own. A host may require two independent yeses, or ten;\n * that is its business and the package cannot help it get that wrong;\n * - **the approved-bytes comparison** — the host re-renders and refuses on\n * mismatch. The package makes that possible by keeping `render` pure and\n * versioned, and does not perform it;\n * - **consent** for anything reaching a list of people;\n * - **any journal** — decisions, dispatches, runs;\n * - **credential storage.** Credentials are ARGUMENTS. Nothing in this package\n * reads `process.env`, and a test asserts that it does not.\n *\n * A packager that owned any of those would be unusable by a host that takes them\n * seriously — which is the only kind of host worth building for.\n */\n\nimport type { DeliveryDeclaration } from \"./delivery\";\nimport type { RenderRules, RenderedPayload } from \"./render\";\nimport type { ConnectorMode, SandboxKind } from \"./mode\";\n\n/* ── Standing a provider up ───────────────────────────────────────────────── */\n\n/**\n * One credential a provider needs.\n *\n * **`secret` is chosen per field, never inferred from the type.** A Discord\n * webhook URL is a URL and is entirely a secret — it carries its token in the\n * path — and the reference implementation very nearly stored it as\n * configuration because it looked like a URL.\n */\n/**\n * Who a credential belongs to.\n *\n * Exported as a runtime constant as well as a type — see `CREDENTIAL_SCOPES`.\n */\nexport type CredentialScope = \"provider\" | \"account\";\n\n/**\n * The scopes, as DATA.\n *\n * Because this union crosses a JSON boundary and the compiler cannot follow it\n * there. When these values were renamed from `app`/`brand`, a consumer that\n * re-declared the field shape on its client kept compiling — their\n * `f.scope === \"brand\"` silently became never-true, and every credential field\n * would have rendered as shared. `tsc` gives a host mirroring these types no\n * help at all, so the values ship as data a host can validate against at the\n * boundary where the compiler stops.\n */\nexport const CREDENTIAL_SCOPES: readonly CredentialScope[] = [\"provider\", \"account\"];\n\n/** Severities, as data, for the same reason. */\nexport const PROBLEM_SEVERITIES = [\"block\", \"warn\"] as const;\n\n/** Canonical metric names, as data, for the same reason. */\nexport const CANONICAL_METRICS = [\"like\", \"share\", \"reply\", \"quote\", \"view\"] as const;\n\nexport type CredentialField = {\n /** Stable key. The host maps it to wherever it keeps values. */\n key: string;\n label: string;\n /** What it is, where it comes from, and what it does NOT prove. */\n help: string;\n /**\n * `provider` — one value for the whole installation (an OAuth app serves\n * every account). `account` — one value per connected account.\n *\n * The distinction exists because getting it wrong means either asking for the\n * same app secret five times or letting one account's token reach another's.\n */\n scope: CredentialScope;\n secret: boolean;\n required: boolean;\n /** A hint for an empty field. NEVER a real value. */\n placeholder?: string;\n};\n\nexport type SetupStep = {\n title: string;\n /** The step, and the trap in it. A step with no trap named is usually wrong. */\n detail: string;\n url?: string;\n};\n\n/**\n * The result of a read-only credential check.\n *\n * **`proves` is not decoration.** Telegram's `getMe` validates the token and\n * says nothing about whether the bot was added to the target chat — which is the\n * step everyone actually gets stuck on. A green tick that means more than it\n * should is worse than no tick, so a verify states its own scope.\n */\nexport type VerifyResult = {\n ok: boolean;\n /** What was learned — ideally the account's real name, so the id can be checked. */\n detail: string;\n /** Exactly what this proves, and what it does not. */\n proves?: string;\n};\n\n/**\n * How an operator stands a provider up.\n *\n * Every provider worth listing is declared, **including the ones that cannot\n * work yet**: a provider missing from a catalogue reads as unsupported, while a\n * provider listed as `implemented: false` tells you exactly what it would take.\n */\nexport type ProviderAdapter = {\n id: string;\n label: string;\n /** True when this can actually be used today. A real state, not a placeholder. */\n implemented: boolean;\n /** One line on what it is for, shown before anyone opens the setup. */\n summary: string;\n fields: CredentialField[];\n setup: SetupStep[];\n /** Exactly what to ask for, and no more. */\n scopes: string[];\n /**\n * How long credentials last, where the provider publishes a figure.\n *\n * Undefined means the provider states no lifetime — which a surface should\n * report as \"no published expiry\" rather than inventing one, and still note\n * that anything can be revoked. \"No known expiry\" is not \"cannot stop working\".\n */\n credentialLifetimeDays?: number;\n /**\n * How the provider exposes a test estate.\n *\n * **`\"unverified\"` is a real answer and is the right one until somebody has\n * actually checked.** Being wrong here points a workflow at a live estate\n * while a person believes it is a test one, so the type carries \"nobody\n * looked\" rather than forcing a guess — a comment saying so is not a type, and\n * `providerProblems()` reports the combination `implemented: true` +\n * `unverified`.\n */\n sandbox: SandboxKind;\n /**\n * What `verify` proves, and what it does NOT.\n *\n * Required whenever `verify` exists, and checked by `providerProblems()`.\n * Telegram's `getMe` validates the token and says nothing about whether the\n * bot was added to the target chat — which is the step everyone actually gets\n * stuck on. A green tick that means more than it should is worse than no tick.\n */\n proves?: string;\n /**\n * A read-only call proving the credential works AND reaches the right account.\n *\n * Deliberately a read. A verify that wrote something would be a send, and\n * sends are gated.\n */\n verify?(credentials: Record<string, string>): Promise<VerifyResult>;\n};\n\n/* ── Calling a provider ───────────────────────────────────────────────────── */\n\nexport type Problem = {\n severity: \"block\" | \"warn\";\n message: string;\n};\n\n/**\n * The result of one call.\n *\n * `ref` is how the provider identifies what was done — the handle every later\n * question is joined on. **A 2xx with no ref is a failure**, and the connector\n * says so rather than reporting a success nobody can point at.\n */\nexport type CallResult = {\n ok: boolean;\n ref: string | null;\n /** True when nothing actually left the building. */\n dryRun: boolean;\n detail: string;\n /** Which estate this ran against. Always reported, never inferred. */\n mode?: ConnectorMode;\n};\n\n/**\n * What a connector can report, in its own words — **declared, not inferred**.\n *\n * Inferring the shape from whatever a pull last returned gives an empty shape\n * when nothing has been pulled, and then *\"this reports nothing\"* and *\"nobody\n * has asked yet\"* are the same blank, needing opposite actions.\n *\n * `canonical` is what the metric IS across providers, where an honest equivalent\n * exists. A like and a favourite are the same act; a repost and a boost are the\n * same act; a quote often has no equivalent, and `null` says so — which is more\n * useful than a mapping somebody invented to make a table line up.\n */\nexport type MetricDescriptor = {\n /** The key the connector's own mapping actually returns. */\n key: string;\n /** What the provider itself calls it. */\n label: string;\n canonical: \"like\" | \"share\" | \"reply\" | \"quote\" | \"view\" | null;\n means: string;\n};\n\nexport type MetricSample = {\n ref: string;\n at: string;\n /** Only what the provider actually reported. **Absent stays absent.** */\n metrics: Record<string, number>;\n};\n\n/**\n * What a connector can do.\n *\n * **A capability flag must not outrun the code**, and this is checkable rather\n * than a rule people remember: `assertCapabilitiesHonest` in `metrics.ts` fails\n * when `metrics: true` has no declared shape. In the reference implementation\n * two connectors claimed metrics while returning `[]` for weeks — so a pull did\n * not skip them, it asked, got nothing, and reported nothing, which on a\n * dashboard is indistinguishable from *\"we asked and nobody engaged\"*.\n */\nexport type Capabilities = {\n call: boolean;\n metrics: boolean;\n feedback: boolean;\n};\n\n/**\n * One capability of one provider.\n *\n * `Target` is the host's own content shape — the package has no opinion about\n * it, and a field named for one host's content model has no business in a shared\n * contract.\n */\nexport type Connector<Target = unknown> = {\n id: string;\n label: string;\n /** Which `ProviderAdapter` stands this up. */\n provider: string;\n /**\n * The connector API version this was written against.\n *\n * Required, and checked at registration by `assertConnectorApi`. The core and\n * the catalogue release on separate clocks, and a connector is VENDORED — a\n * copy in someone else's repository, with no manifest of its own to carry a\n * version range. This number is the only thing that can say \"this copy was\n * written for a core you no longer have\", and it says it at build time rather\n * than in production. See `compat.ts`.\n */\n connectorApi: number;\n capabilities: Capabilities;\n /**\n * How this behaves when a call goes wrong, and how fast it may be used.\n * Cited, not asserted — see `DeliveryDeclaration`.\n */\n delivery: DeliveryDeclaration;\n /** Declared metric shape. **Absent, not empty**, where there are none. */\n metricShape?: MetricDescriptor[];\n /**\n * Rendering rules, where this connector renders text.\n *\n * Data rather than code for the common case. A provider whose rendering\n * genuinely needs code supplies `render` instead.\n */\n renderRules?: RenderRules;\n /**\n * Everything the renderer cannot judge — media, alt text, a required field.\n * **Never length**: see the note at the top of `render.ts`.\n */\n validate(target: Target): Problem[];\n /** Pure and versioned. Overrides `renderRules` when both are present. */\n render?(target: Target, rules?: Record<string, number>): RenderedPayload;\n /**\n * Do the thing.\n *\n * `credentials` are passed IN. `dryRun` is decided by the HOST. Neither is\n * negotiable, and both are asserted by tests in this package.\n */\n call(\n target: Target,\n options: { dryRun: boolean; credentials: Record<string, string>; mode?: ConnectorMode },\n ): Promise<CallResult>;\n fetchMetrics?(refs: string[], credentials: Record<string, string>): Promise<MetricSample[]>;\n /**\n * Feedback on things we did, for refs the host already holds.\n *\n * **Optional, and absent is a real answer** rather than a gap to be filled. A\n * connector with no feedback mechanism must not pretend by returning an empty\n * array — \"nobody replied\" and \"this cannot tell you\" are different, and only\n * one of them is news.\n */\n fetchFeedback?(\n refs: string[],\n credentials: Record<string, string>,\n ): Promise<\n Array<{\n id: string;\n inReplyTo: string;\n author: string;\n text: string;\n at: string;\n url: string | null;\n /** What answering this would need. Opaque to the host. */\n context: Record<string, string>;\n }>\n >;\n};\n\n/**\n * A catalogue of connectors and the providers that stand them up.\n *\n * A plain lookup rather than a registry with lifecycle: the host owns which\n * connectors it installed, and a package-level global would be one more thing\n * that can be half-initialised.\n */\nexport type Catalogue<Target = unknown> = {\n providers: Record<string, ProviderAdapter>;\n connectors: Record<string, Connector<Target>>;\n};\n","/**\n * Verifying an inbound provider webhook.\n *\n * An unverified webhook endpoint is a public, unauthenticated way to start a\n * workflow in someone's account — which is to say, a way for a stranger to make\n * your app refund an order or post to your Slack. So verification is part of the\n * connector pattern rather than an exercise for the host: a trigger node that\n * cannot verify its deliveries must say so, and a host must not be able to mount\n * one by accident.\n *\n * ## The three things every provider's scheme actually needs\n *\n * Providers differ in header names and in what exactly gets signed, and almost\n * nothing else:\n *\n * 1. **The RAW body.** Signatures are computed over bytes. Re-serialising parsed\n * JSON changes key order and whitespace and produces a signature mismatch\n * that looks like a wrong secret. The host must pass the body it received.\n * 2. **A constant-time comparison.** `===` on a signature leaks, through timing,\n * which prefix was right. That is a real forgery path, not a theoretical one.\n * 3. **A timestamp tolerance.** Without it a valid signature is valid forever,\n * so anyone who ever saw one delivery can replay it whenever they like.\n *\n * WebCrypto rather than `node:crypto`, so the same file runs on Node, Bun, Deno\n * and an edge runtime. Node 22 is the suite's floor and has it globally.\n */\n\nexport type WebhookVerification =\n | { ok: true }\n | { ok: false; reason: string };\n\nexport type HmacScheme = {\n /** Hash to use. */\n algorithm: \"SHA-256\" | \"SHA-1\" | \"SHA-512\";\n /**\n * Build the exact string that gets signed. Providers differ here more than\n * anywhere else — Stripe signs `${timestamp}.${body}`, Slack signs\n * `v0:${timestamp}:${body}`, GitHub signs the body alone.\n */\n payload: (raw: string, timestamp?: string) => string;\n /** Seconds a delivery stays acceptable. */\n tolerance?: number;\n /** Encoding of the signature the provider sends. */\n encoding?: \"hex\" | \"base64\";\n};\n\n/**\n * Verify an HMAC-signed delivery.\n *\n * Returns a RESULT rather than throwing, and the failure carries a reason: a\n * host wants to log which check failed (stale? wrong secret? no header?) while\n * still answering the provider with an opaque 400.\n */\nexport async function verifyHmac(options: {\n raw: string;\n signature: string | undefined;\n secret: string | undefined;\n scheme: HmacScheme;\n timestamp?: string;\n /** Seconds since the epoch. Injected so tests are not clock-dependent. */\n now?: number;\n}): Promise<WebhookVerification> {\n const { raw, signature, secret, scheme, timestamp } = options;\n\n if (!secret) {\n // Never \"accept when unconfigured\". An endpoint that verifies nothing\n // because nobody set a secret is strictly worse than one that is off: it\n // looks protected.\n return { ok: false, reason: \"no signing secret configured for this trigger\" };\n }\n if (!signature) return { ok: false, reason: \"delivery carried no signature header\" };\n\n if (scheme.tolerance !== undefined) {\n if (!timestamp) return { ok: false, reason: \"delivery carried no timestamp header\" };\n\n const sent = Number(timestamp);\n if (!Number.isFinite(sent)) return { ok: false, reason: \"timestamp header is not a number\" };\n\n const now = options.now ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - sent) > scheme.tolerance) {\n return { ok: false, reason: `delivery is outside the ${scheme.tolerance}s replay window` };\n }\n }\n\n const expected = await hmac(secret, scheme.payload(raw, timestamp), scheme.algorithm, scheme.encoding ?? \"hex\");\n\n return constantTimeEquals(expected, signature)\n ? { ok: true }\n : { ok: false, reason: \"signature did not match\" };\n}\n\n/** HMAC of `payload` under `secret`, hex or base64 encoded. */\nexport async function hmac(\n secret: string,\n payload: string,\n algorithm: HmacScheme[\"algorithm\"],\n encoding: \"hex\" | \"base64\" = \"hex\",\n): Promise<string> {\n const subtle = globalThis.crypto?.subtle;\n\n if (!subtle) {\n throw new Error(\n \"WebCrypto is not available in this runtime, so webhook signatures cannot be verified. \" +\n \"Refusing to accept deliveries unverified.\",\n );\n }\n\n const encoder = new TextEncoder();\n const key = await subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: algorithm },\n false,\n [\"sign\"],\n );\n const bytes = new Uint8Array(await subtle.sign(\"HMAC\", key, encoder.encode(payload)));\n\n if (encoding === \"base64\") {\n let binary = \"\";\n bytes.forEach((byte) => {\n binary += String.fromCharCode(byte);\n });\n\n return btoa(binary);\n }\n\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Compare two strings without leaking where they diverge.\n *\n * The length check first is deliberate and safe: a signature's length is fixed\n * by its algorithm and is not a secret, so revealing a mismatch there tells an\n * attacker nothing they could not compute.\n */\nexport function constantTimeEquals(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n\n return diff === 0;\n}\n\n/**\n * Pull one header case-insensitively.\n *\n * Header case is not preserved consistently across proxies, frameworks and\n * runtimes; a connector that read `req.headers[\"Stripe-Signature\"]` would work\n * behind one server and reject every delivery behind another.\n */\nexport function header(headers: Record<string, string | string[] | undefined>, name: string): string | undefined {\n const wanted = name.toLowerCase();\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() !== wanted) continue;\n\n return Array.isArray(value) ? value[0] : value;\n }\n\n return undefined;\n}\n","/**\n * How a connector trigger learns that something happened.\n *\n * **Not every provider pushes, and assuming webhooks is the single most common\n * way a connector catalogue ends up lying.** Google Drive and Microsoft Graph\n * want a *subscription* you must renew before it expires. Salesforce wants a\n * Pub/Sub or CometD stream. S3 wants an event notification routed to\n * infrastructure the consumer provisions. Reddit wants you to poll. Each of\n * those is a different obligation on the host, and a node that declared\n * \"webhook\" for all of them would install cleanly and then never fire.\n *\n * So a trigger declares its delivery mechanism, and the host can refuse to mount\n * one it has no machinery for — at author time, not at 3am.\n */\n\nimport type { ResolvedConnection } from \"./connection\";\nimport type { ConnectorFaker } from \"./faker\";\nimport { fakeRequest } from \"./faker\";\nimport type { HmacScheme, WebhookVerification } from \"./webhook\";\nimport { header, verifyHmac } from \"./webhook\";\n\n/**\n * The delivery mechanisms that actually exist in the wild.\n *\n * - `webhook` — the provider POSTs to a URL you own. Verify the signature.\n * - `subscription` — a webhook that EXPIRES. Someone has to renew it, forever,\n * and if nobody does the workflow stops firing without an error anywhere.\n * Distinct from `webhook` precisely because that ongoing duty is invisible.\n * - `poll` — you ask, on a schedule, and track a cursor.\n * - `pubsub` — the provider publishes to a broker (Google Pub/Sub, Salesforce\n * Pub/Sub API, SNS/SQS) that the consumer must provision.\n * - `stream` — a long-lived connection the host maintains (websocket, gRPC,\n * SSE, CometD).\n */\nexport type DeliveryMechanism = \"webhook\" | \"subscription\" | \"poll\" | \"pubsub\" | \"stream\";\n\nexport type TriggerDescriptor = {\n service: string;\n operation: string;\n delivery: DeliveryMechanism;\n /**\n * What the host has to do before this fires, in one sentence, aimed at the\n * person who has to do it. Surfaced by the editor and by the MCP, because a\n * trigger whose setup is undocumented is a trigger that silently never fires.\n */\n setup: string;\n /** For `subscription`: how long the provider keeps it alive, in seconds. */\n subscriptionTtl?: number;\n /** For `webhook` / `subscription`: how a delivery is authenticated. */\n verification?: WebhookVerificationSpec;\n /** For `poll`: the provider's smallest sane interval, in seconds. */\n minPollSeconds?: number;\n /** A faked sample event, so the node is runnable before any of the above. */\n faker: ConnectorFaker;\n};\n\nexport type WebhookVerificationSpec = {\n /** Header carrying the signature. */\n signatureHeader: string;\n /** Header carrying the timestamp, when the scheme signs one. */\n timestampHeader?: string;\n scheme: HmacScheme;\n /**\n * Some schemes pack the timestamp INTO the signature header\n * (Stripe: `t=…,v1=…`). Given the raw header value, return the parts.\n */\n parse?: (raw: string) => { signature?: string; timestamp?: string };\n};\n\nexport type InboundDelivery = {\n /** The body EXACTLY as received. Not re-serialised. See `webhook.ts`. */\n raw: string;\n headers: Record<string, string | string[] | undefined>;\n};\n\n/**\n * Verify an inbound delivery against a trigger's declared scheme.\n *\n * A trigger with no `verification` REFUSES rather than accepts. That asymmetry\n * is the whole safety property: an unverifiable endpoint is a stranger's button\n * for starting workflows in your account, and defaulting to \"allow\" would make\n * every misconfiguration into an open door that looks shut.\n */\nexport async function verifyDelivery(\n trigger: TriggerDescriptor,\n delivery: InboundDelivery,\n secret: string | undefined,\n now?: number,\n): Promise<WebhookVerification> {\n const spec = trigger.verification;\n\n if (!spec) {\n return {\n ok: false,\n reason: `${trigger.service}.${trigger.operation} declares no signature scheme, so a delivery cannot be trusted.`,\n };\n }\n\n const rawHeader = header(delivery.headers, spec.signatureHeader);\n const parsed = spec.parse && rawHeader ? spec.parse(rawHeader) : { signature: rawHeader };\n const timestamp = parsed.timestamp\n ?? (spec.timestampHeader ? header(delivery.headers, spec.timestampHeader) : undefined);\n\n return verifyHmac({\n raw: delivery.raw,\n signature: parsed.signature,\n secret,\n scheme: spec.scheme,\n timestamp,\n ...(now === undefined ? {} : { now }),\n });\n}\n\n/**\n * The event a trigger node publishes when it runs.\n *\n * Three sources, in order, and the third is why a connector trigger is usable\n * on day one:\n *\n * 1. A verified delivery the host injected on the `in` port.\n * 2. Nothing injected, but the connection is remote — that is a host wiring\n * error, and it fails loudly rather than emitting a plausible blank.\n * 3. `fake` mode — the trigger emits its own sample event, so an author can\n * press Run, see the real field names, and wire the downstream nodes against\n * them before the provider has ever been contacted.\n */\nexport function triggerEvent(\n trigger: TriggerDescriptor,\n connection: ResolvedConnection,\n injected: unknown,\n config: Record<string, unknown>,\n): unknown {\n if (hasDelivery(injected)) return injected;\n\n if (connection.mode === \"fake\") {\n return trigger.faker(\n trigger.operation,\n fakeRequest(trigger.service, trigger.operation, config),\n );\n }\n\n throw new Error(\n `${trigger.service}.${trigger.operation}: no event was delivered to this trigger. ` +\n `Its delivery mechanism is \"${trigger.delivery}\" — ${trigger.setup} ` +\n 'Set the node\\'s mode to \"fake\" to design against a sample event instead.',\n );\n}\n\n/**\n * Whether a value on the `in` port is an actual delivery.\n *\n * An EMPTY object or array counts as nothing delivered, and that is not\n * fussiness: a manually started run seeds `{}` on the trigger's input, so a\n * bare null check would hand the executor an empty envelope and call it an\n * event. Downstream every field would be `null`, the run would be green, and\n * nothing would say the trigger never fired.\n */\nfunction hasDelivery(value: unknown): boolean {\n if (value === undefined || value === null) return false;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === \"object\") return Object.keys(value as object).length > 0;\n\n return true;\n}\n"],"mappings":"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","names":["header","mode","missing","walk","classified","encoder","header","header"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/chain.ts","../src/compat.ts","../src/delivery.ts","../src/errors.ts","../src/faker.ts","../src/mode.ts","../src/connection.ts","../src/client.ts","../src/drift.ts","../src/idempotency.ts","../src/lease.ts","../src/metrics.ts","../src/mime.ts","../src/probe.ts","../src/text.ts","../src/render.ts","../src/seam.ts","../src/webhook.ts","../src/trigger.ts"],"sourcesContent":["/**\n * `@particle-academy/fancy-connector-core` — the runtime under every Fancy\n * connector.\n *\n * ## What this is\n *\n * The reusable guts of talking to a third-party API: modes and estates,\n * connections, one call path, a failure taxonomy that knows the difference\n * between *it never arrived* and *nobody can tell*, deterministic fakers,\n * webhook verification, pure versioned rendering, chain posting, declared metric\n * shapes, credential-free probes, and API-drift reporting.\n *\n * ## What this is NOT, and never will be\n *\n * It does not own a gate. Approval, liveness, the approved-bytes comparison,\n * consent, second review and every journal belong to the host — because each is\n * enforced in ONE place and every connector inherits it from the dispatch path\n * rather than implementing it. `seam.ts` states this in full.\n *\n * Three consequences that are asserted by tests rather than promised in prose:\n *\n * - **Nothing here reads `process.env`.** Credentials are arguments.\n * - **Nothing here retries an ambiguous failure** on a connector that has not\n * declared repeating a request harmless.\n * - **Nothing here phones home.** There is no telemetry, no central service, and\n * no URL this package contacts that a connector did not name.\n */\n\nexport * from \"./chain\";\nexport * from \"./compat\";\nexport * from \"./client\";\nexport * from \"./connection\";\nexport * from \"./delivery\";\nexport * from \"./drift\";\nexport * from \"./errors\";\nexport * from \"./faker\";\nexport * from \"./idempotency\";\nexport * from \"./lease\";\nexport * from \"./metrics\";\nexport * from \"./mime\";\nexport * from \"./mode\";\nexport * from \"./probe\";\nexport * from \"./render\";\nexport * from \"./seam\";\nexport * from \"./text\";\nexport * from \"./trigger\";\nexport * from \"./webhook\";\n","/**\n * Posting a sequence as a CHAIN — the bug that is silent in every direction.\n *\n * ## What went wrong in the reference implementation\n *\n * A renderer split long copy into `(1/3)`, `(2/3)`, `(3/3)` and the connector\n * posted each segment **top-level**, on two providers. Numbered like a thread,\n * connected to nothing. Nothing threw; the send reported success; the numbering\n * made it look deliberate, so a reader assumes the thread exists and they missed\n * it.\n *\n * It was unreachable by any test, because the chaining lived inside a loop that\n * needed a live session. So the chain builder here **takes the post function as\n * an argument** — which is the entire reason this is a module rather than four\n * lines inside a connector.\n *\n * ## The rule it encodes, which is one line and easy to reverse\n *\n * > **`root` is fixed at the top of the chain and never moves. `parent` advances\n * > to whatever was just posted.**\n *\n * Reverse them and every message attaches to the first: a fan, not a thread —\n * and the provider's response looks identical either way, which is why this\n * needs a test rather than care.\n *\n * ## What it deliberately does NOT do\n *\n * No retry. Retry wraps ONE request (see `delivery.ts`); wrapping the chain\n * would re-post every earlier segment when a later one failed, turning a partial\n * send into a duplicated one. A partial chain is reported with what it did post,\n * so a person can see exactly where it stopped.\n */\n\n/**\n * A reference to something already posted, as the provider identifies it.\n *\n * Opaque to this module: AT Protocol needs a uri **and** a cid, Mastodon needs\n * one status id, Telegram needs a message id and a chat. A shape naming one\n * provider's model would be wrong for the next one, so the chain carries\n * whatever the poster returned and never reads inside it.\n *\n * **`object`, not `Record<string, string | number>`** — and the difference is\n * not pedantry. An ordinary interface does not satisfy an index signature\n * unless it declares one, so `interface PostRef { uri: string; cid: string }`\n * did NOT satisfy the old constraint. The reference consumer worked around it\n * with `postChain<T & ChainRef>` plus two `as unknown as` casts, and could not\n * use `ChainOutcome<T>` in their own signatures at all — because the only other\n * fix was to add an index signature to a type used across their codebase in\n * order to satisfy a generic used in one place, which weakens the type\n * everywhere to please one call site.\n *\n * A constraint everyone casts past is not enforcing anything; it is just making\n * the casts. `object` still rejects the mistake worth rejecting — a primitive\n * as a reference — and accepts every real one.\n */\nexport type ChainRef = object;\n\nexport type ChainLinks<Ref extends ChainRef> = {\n /** The top of the conversation. Fixed for the whole chain. */\n root: Ref;\n /** The message immediately above this one. Advances every step. */\n parent: Ref;\n};\n\nexport type ChainOutcome<Ref extends ChainRef> = {\n /** Everything that was posted, in order. */\n posted: Ref[];\n /** The failure that stopped it, if one did. */\n failed?: { index: number; error: unknown };\n};\n\n/**\n * Post `segments` as a connected chain, optionally starting inside an existing\n * conversation.\n *\n * `post` is given the text and the links for this position — `undefined` for a\n * top-level message — and returns the provider's reference to what it created.\n *\n * **Stops at the first failure and reports what it posted.** Continuing would\n * produce a thread with a hole in it, and unwinding is not available: nothing\n * here can delete a public message, and pretending otherwise would be worse than\n * the hole.\n */\nexport async function postChain<Ref extends ChainRef>(\n segments: string[],\n answering: ChainLinks<Ref> | undefined,\n post: (text: string, links: ChainLinks<Ref> | undefined, index: number) => Promise<Ref>,\n): Promise<ChainOutcome<Ref>> {\n const posted: Ref[] = [];\n let root = answering?.root;\n let parent = answering?.parent;\n\n for (const [index, text] of segments.entries()) {\n let made: Ref;\n\n try {\n made = await post(text, root && parent ? { root, parent } : undefined, index);\n } catch (error) {\n return { posted, failed: { index, error } };\n }\n\n posted.push(made);\n // The first thing WE post becomes the root of our own chain — unless we are\n // already inside somebody else's, in which case theirs stays the root.\n root = root ?? made;\n parent = made;\n }\n\n return { posted };\n}\n","/**\n * The connector API version — what makes two release clocks safe.\n *\n * ## The problem this exists for\n *\n * The core and the connector catalogue are separate repositories, so they\n * release on separate clocks. That is deliberate: a provider changing its API is\n * a connector fix and must not wait on a core release. But it creates a hazard\n * that is worse here than in an ordinary package pair, because **connectors ship\n * as vendored source**:\n *\n * > A consumer copies a connector into their project. Six months later they\n * > upgrade the core. Their copy of the connector was written against a core two\n * > minors older, and pre-1.0 minors are allowed to break.\n *\n * Nothing in a version range catches that, because the vendored copy has no\n * manifest of its own — it is just files in someone else's repository. And the\n * failure is the quiet kind: the connector still compiles, still runs, and does\n * something subtly different.\n *\n * ## The fix: a second number that moves far more slowly\n *\n * The core's package version tracks everything — fixes, additions, internal\n * changes. `CONNECTOR_API_VERSION` tracks only one thing: **the surface a\n * connector is written against.** Adding a module, fixing a classifier, or\n * improving a message does not move it. Changing what a `Connector` must\n * implement, renaming a field a connector declares, or altering what `render`\n * returns does.\n *\n * So a core can go 0.3 → 0.9 without a single vendored connector caring, and the\n * one release that does matter announces itself in a number a connector can\n * check at import time rather than discover at run time.\n *\n * ## The support window, and why it is two rather than one\n *\n * The core accepts the current version **and the one before it**. One would mean\n * every consumer has to re-vendor every connector on the same day the core ships\n * — which nobody does, so in practice they would pin the core instead and stop\n * getting fixes. Two gives a real window: the catalogue re-issues its connectors\n * on the new version, consumers re-vendor when they next touch that connector,\n * and the old shape keeps working until the version after next.\n *\n * More than two would be a promise we cannot keep, because it means maintaining\n * a compatibility shim for a shape nobody has read in a year.\n */\n\n/**\n * The connector surface this core implements.\n *\n * **Bump this ONLY for a change a connector's own source can see**, and say in\n * the changelog what a connector author must DO. If nothing in `connectors/`\n * would have to change, it is not a connector API change.\n */\nexport const CONNECTOR_API_VERSION = 1;\n\n/**\n * Versions this core will run.\n *\n * Ordered newest first, purely so the error message reads naturally.\n */\nexport const SUPPORTED_CONNECTOR_API: readonly number[] = [1];\n\nexport class ConnectorApiMismatch extends Error {\n readonly connector: string;\n\n readonly declared: number;\n\n constructor(message: string, connector: string, declared: number) {\n super(message);\n this.name = \"ConnectorApiMismatch\";\n this.connector = connector;\n this.declared = declared;\n }\n}\n\n/**\n * Refuse a connector written against a surface this core does not implement.\n *\n * **Called at registration, not at call time.** A mismatch discovered on the\n * first real request is a mismatch discovered in production; discovered when the\n * catalogue is assembled, it is a build failure with a name attached.\n *\n * The message names the direction, because the two cases need opposite actions\n * and are trivially confused:\n *\n * - **connector NEWER than the core** → upgrade the core.\n * - **connector OLDER than the window** → re-vendor the connector.\n *\n * \"Incompatible versions\" sends someone to read source; naming which side is\n * behind sends them to the command.\n */\nexport function assertConnectorApi(connectorId: string, declared: number): void {\n if (SUPPORTED_CONNECTOR_API.includes(declared)) return;\n\n const supported = SUPPORTED_CONNECTOR_API.join(\", \");\n\n throw new ConnectorApiMismatch(\n declared > CONNECTOR_API_VERSION\n ? `\"${connectorId}\" was written against connector API ${declared}, and this core implements ${supported}. ` +\n \"The CONNECTOR is newer: upgrade @particle-academy/fancy-connector-core. Vendoring a connector ahead of \" +\n \"the core it needs is the one direction that cannot be made to work by trying.\"\n : `\"${connectorId}\" was written against connector API ${declared}, which is older than anything this core ` +\n `still runs (${supported}). The CONNECTOR is behind: move it to a release written for this core — ` +\n `upgrade its package (@particle-academy/${connectorId}-js, particle-academy/${connectorId}-php), or ` +\n \"regenerate a vendored copy from that release; its CHANGELOG names the connector API each release targets. \" +\n \"Nothing is adapted automatically, because guessing what a two-version-old connector meant is how a \" +\n \"connector quietly starts doing something else.\",\n connectorId,\n declared,\n );\n}\n\n/**\n * Whether a core version satisfies a connector's declared minimum.\n *\n * Exists so a registry or a CLI can answer *before* vendoring, which is the only\n * moment the answer is cheap. Compares the numeric prefix of each dotted\n * segment, so a prerelease suffix does not make `0.4.0-rc.1` sort below `0.3.9`\n * in a way nobody expected.\n */\nexport function satisfiesMinimum(coreVersion: string, minimum: string): boolean {\n const parts = (value: string): number[] =>\n value\n .split(\".\")\n .slice(0, 3)\n .map((segment) => Number.parseInt(segment, 10) || 0);\n\n const [a = 0, b = 0, c = 0] = parts(coreVersion);\n const [x = 0, y = 0, z = 0] = parts(minimum);\n\n if (a !== x) return a > x;\n if (b !== y) return b > y;\n\n return c >= z;\n}\n","/**\n * Failure classification — the one decision every retry gets wrong.\n *\n * ## The tension\n *\n * A network hiccup should not lose a call. And a retry after a request that\n * **succeeded and whose response was lost** is a second write — the exact\n * failure an idempotency gate exists to prevent, arriving through the door\n * marked *reliability*.\n *\n * Nearly every retry helper reconciles those the same way and loses: it catches\n * an error, waits, and tries again, because from the caller's side all failures\n * look alike. They are not alike.\n *\n * | what happened | did the provider receive it? | retry? |\n * |---|---|---|\n * | DNS failure, connection refused, socket reset before send | **no** | always safe |\n * | HTTP 429 / 5xx | **yes**, and it explicitly refused | safe — it says it did nothing |\n * | timeout, aborted request, unrecognised error | **unknown** | only where the provider is idempotent |\n * | HTTP 4xx that is not 429 | **yes**, and the answer was a real no | never |\n *\n * That third row is the whole design. A timeout looks exactly like a failure and\n * may have been a success.\n *\n * ## Why this is a separate axis from the error CLASS\n *\n * The first version of this runtime carried `retryable` as a fixed boolean on\n * each error class, and `ConnectorTransient` covered both a 5xx *and* a thrown\n * transport. Those are opposite cases: a 5xx is the provider telling you it did\n * nothing, and a thrown transport may be a socket that closed after the bytes\n * went out. Marking both `retryable = true` meant an ambiguous failure on a\n * connector with no idempotency key was retried — a silent double write, on the\n * path whose entire job is not producing one.\n *\n * So the primitive is the **kind**, and retryability is a function of the kind\n * AND what the connector declared about repeating a request. `error.retryable`\n * survives and now answers the narrower question — *is this safe to retry\n * whatever the connector is?* — which makes an old caller conservative rather\n * than wrong.\n */\n\n/** Why a call failed, in the only terms that decide whether to try again. */\nexport type FailureKind =\n /** Never reached the provider. A second attempt cannot duplicate anything. */\n | \"unreachable\"\n /** The provider answered, and its answer was \"not now\". It did nothing. */\n | \"refused-explicitly\"\n /** It may or may not have been acted on. Nobody can tell. */\n | \"ambiguous\"\n /** The provider answered and the answer was a real, permanent no. */\n | \"rejected\";\n\n/**\n * How a connector behaves when a call goes wrong, and how fast it may be used.\n *\n * **`idempotent` is the load-bearing field**, and it is the only thing that\n * makes an ambiguous failure safe to retry. A connector that has not thought\n * about it gets `false`, and an ambiguous failure is then reported for a person\n * instead of retried into a double write.\n */\nexport type DeliveryDeclaration = {\n idempotent: boolean;\n /**\n * Why it is or is not — **cited, not asserted**.\n *\n * `idempotent: true` with no reason is the one claim whose failure is a public\n * double write, so the reason names the mechanism (`Idempotency-Key`,\n * `com.atproto.repo.createRecord` having none) rather than restating the flag.\n */\n why: string;\n /** Smallest gap between two calls on this connector. */\n minIntervalMs: number;\n /**\n * Whose number `minIntervalMs` is.\n *\n * A confident figure nobody can cite is worse than an honest one that is too\n * slow, because the honest one gets revised when evidence turns up and the\n * confident one gets quoted as a platform fact.\n */\n rateSource: \"documented\" | \"self-imposed\";\n /** Where the numbers came from. See `citation.ts`. */\n citation?: Citation;\n};\n\n/**\n * Where a declared fact was read, and when.\n *\n * A citation with no date is an assertion wearing a URL. The date is what lets a\n * drift check say *this was true eight months ago and nobody has looked since*,\n * which is a different and more useful statement than *this is true*.\n */\nexport type Citation = {\n /** The provider's own documentation for this fact. */\n url: string;\n /** ISO date the URL was actually read by a person or a check. */\n readOn: string;\n /** The sentence the fact rests on, quoted. Optional but strongly wanted. */\n quote?: string;\n};\n\nexport type Classified = {\n kind: FailureKind;\n detail: string;\n /** Seconds the provider asked us to wait, when it said so. */\n retryAfter?: number;\n};\n\n/**\n * Codes that prove the request never left, or left and was refused at the door.\n *\n * Matched on codes rather than message text, because message text is not an API\n * and differs between Node, Bun, Deno and an edge runtime.\n */\nconst UNREACHABLE_CODES = new Set([\n // Every code here must PROVE the request never reached an application:\n // nothing listening, no route, or a name that never resolved. If a code can\n // also occur after the peer received and acted on the request, it belongs in\n // AMBIGUOUS_CODES -- `shouldRetry` retries `unreachable` without consulting\n // `idempotent`, so a wrong entry here is a duplicate send, not a slow one.\n \"ECONNREFUSED\",\n \"ENOTFOUND\",\n \"EAI_AGAIN\",\n \"EHOSTUNREACH\",\n \"ENETUNREACH\",\n \"ERR_SOCKET_CONNECTION_TIMEOUT\",\n]);\n\n/** Codes and names that mean *we stopped waiting*, which is not the same as *it did not happen*. */\nconst AMBIGUOUS_CODES = new Set([\n // A peer sends RST whenever it tears the connection down -- INCLUDING after\n // it has received and acted on the request but before the response comes\n // back. EPIPE is the same story from our side: we wrote to a socket the peer\n // had already closed, and whether an earlier complete request was processed\n // first is not knowable from here. Node surfaces the safe case and the unsafe\n // case as the same code, so the code cannot carry the claim.\n //\n // These two sat in UNREACHABLE_CODES until 0.3.0, which made them\n // unconditionally retryable and produced three sends of connectors that had\n // declared `idempotent: false`. Same defect as the 5xx / thrown-transport\n // split this module already fixed once, and a stated exception to a rule that\n // should have covered them.\n \"ECONNRESET\",\n \"EPIPE\",\n \"ETIMEDOUT\",\n \"UND_ERR_HEADERS_TIMEOUT\",\n \"UND_ERR_BODY_TIMEOUT\",\n \"UND_ERR_CONNECT_TIMEOUT\",\n]);\n\nconst AMBIGUOUS_NAMES = new Set([\"AbortError\", \"TimeoutError\"]);\n\n/**\n * Classify a thrown transport failure.\n *\n * **An unrecognised error falls to `ambiguous`, never to `unreachable`.** That\n * asymmetry is deliberate: an unknown failure treated as unreachable would be\n * retried, and the one thing worse than a lost call is two calls nobody asked\n * for. Guessing in the safe-looking direction is how this goes wrong.\n */\nexport function classifyError(error: unknown): Classified {\n // An error that already CARRIES a classification is authoritative, and this\n // check has to be first.\n //\n // `httpFailure()` attaches one so a connector can declare what counts as a\n // failure for its provider — Telegram's `200 OK` with `{\"ok\": false}` is a\n // real refusal, and only the connector knows that. Re-deriving from scratch\n // finds no Node error code on such an error and answers `ambiguous`: \"nobody\n // can tell\", about a `400` the provider was explicit about.\n //\n // `deliver()` has always read `error.classified` before falling back here, so\n // the retry path was never wrong. This was worse in a quieter way — a host\n // calling `classifyError` DIRECTLY, which is a reasonable thing to do with an\n // exported classifier, was told nobody could tell. Reported by the reference\n // consumer, who was careful to distinguish the two and nearly filed it as a\n // retry bug. The PHP twin already did this; the two runtimes disagreed.\n const attached = (error as { classified?: Classified } | null)?.classified;\n if (attached && typeof attached === \"object\" && typeof attached.kind === \"string\") return attached;\n\n const detail = error instanceof Error ? error.message : String(error);\n const code =\n (error as { code?: string })?.code ?? (error as { cause?: { code?: string } })?.cause?.code ?? \"\";\n const name = error instanceof Error ? error.name : \"\";\n\n if (UNREACHABLE_CODES.has(code)) return { kind: \"unreachable\", detail: `${code}: ${detail}` };\n if (AMBIGUOUS_NAMES.has(name) || AMBIGUOUS_CODES.has(code)) {\n return { kind: \"ambiguous\", detail: `${name || code}: ${detail}` };\n }\n\n return { kind: \"ambiguous\", detail };\n}\n\n/**\n * Classify an HTTP status.\n *\n * A status means the provider answered, so it knows what it did. 429 and 5xx are\n * \"not now\"; everything else in the 4xx range is a real no, and retrying a real\n * no just spends someone's rate limit on the same rejection.\n *\n * 429 is decided **before** the 4xx sweep, because it is a 4xx and is the one\n * 4xx worth retrying — the other ordering marks every throttle permanent and\n * turns a busy minute into a failed run.\n */\nexport function classifyStatus(status: number, body = \"\", retryAfterHeader?: string | null): Classified {\n const detail = `${status}${body ? `: ${body.slice(0, 200)}` : \"\"}`;\n const seconds = retryAfterHeader === undefined || retryAfterHeader === null ? NaN : Number(retryAfterHeader);\n\n if (status === 429 || status >= 500) {\n return {\n kind: \"refused-explicitly\",\n detail,\n ...(Number.isFinite(seconds) ? { retryAfter: seconds } : {}),\n };\n }\n\n return { kind: \"rejected\", detail };\n}\n\nexport type RetryPolicy = {\n /** Total attempts, including the first. */\n attempts: number;\n /** First backoff in ms. Doubles each attempt. */\n baseDelayMs: number;\n /** Never wait longer than this between attempts. */\n maxDelayMs: number;\n /**\n * Whether the provider makes a repeated request harmless.\n *\n * `false` is the correct default for any connector that has not proven\n * otherwise, and \"proven\" means a test that publishes twice and compares the\n * result — not a comment.\n */\n idempotent: boolean;\n};\n\nexport const DEFAULT_RETRY: RetryPolicy = {\n attempts: 3,\n baseDelayMs: 500,\n maxDelayMs: 8_000,\n idempotent: false,\n};\n\n/**\n * May this failure be retried?\n *\n * The whole rule, in four lines, in one place. A connector that classified its\n * own statuses would eventually decide some 4xx was worth retrying, and the\n * reason retries are safe at all is that this decision is made once.\n */\nexport function shouldRetry(kind: FailureKind, policy: Pick<RetryPolicy, \"idempotent\">): boolean {\n if (kind === \"rejected\") return false;\n if (kind === \"unreachable\" || kind === \"refused-explicitly\") return true;\n\n return policy.idempotent;\n}\n\n/**\n * True where a retry is safe **whatever the connector is** — i.e. without\n * needing to know whether repeating the request is harmless.\n *\n * This is what `ConnectorError.retryable` answers, and it is deliberately the\n * narrow question. A caller that has an idempotency declaration should ask\n * `shouldRetry` instead and get the extra case.\n */\nexport function isUnconditionallyRetryable(kind: FailureKind): boolean {\n return kind === \"unreachable\" || kind === \"refused-explicitly\";\n}\n\nexport type Attempt = {\n attempt: number;\n kind: FailureKind;\n detail: string;\n /** How long we waited before the NEXT attempt. Absent on the last. */\n waitedMs?: number;\n};\n\nexport type DeliveryOutcome<T> = {\n ok: boolean;\n value?: T;\n /** Every attempt that failed, in order. Empty when the first one worked. */\n attempts: Attempt[];\n /** Why it stopped, when it failed. Written for a person who has to act. */\n gaveUp?: string;\n /** The classification of the last failure, so a host can route on it. */\n kind?: FailureKind;\n /**\n * The last failure **as it was thrown** — absent when the call worked.\n *\n * `kind` and `gaveUp` are what `deliver()` concluded; this is what it\n * concluded it FROM. Until 0.5.0 an outcome carried only the conclusions, so\n * everything the error knew beyond them — the HTTP status, the provider's own\n * code, the class it was raised as — stopped here, and every failed\n * `callConnector` reached its host with none of it. See `failureFrom()` in\n * `client.ts`.\n *\n * `unknown`, because `send` may throw anything.\n */\n error?: unknown;\n};\n\n/**\n * The message a person reads when an ambiguous failure could not be retried.\n *\n * Exported because a host will want to recognise it, and because the wording is\n * load-bearing: it has to say *go and look*, not *it failed*. Those prompt\n * different actions and only one of them is correct.\n */\nexport const AMBIGUOUS_REFUSAL =\n \"The request may or may not have gone through, and this connector offers no way to repeat it \" +\n \"harmlessly. Refusing to try again — a duplicate is worse than a call that needs doing by hand. \" +\n \"Check the provider before re-running.\";\n\n/**\n * Run ONE request, retrying only where retrying is provably safe.\n *\n * ## Retry wraps ONE request, never a sequence\n *\n * Wrapping a multi-post publish would re-send every earlier segment of a thread\n * when a later one failed — turning a partial send into a duplicated one. So the\n * unit is a single request and a chain composes above it, not below.\n *\n * `send` must throw on failure. An error carrying a `classified` property (see\n * `httpFailure`) is trusted; anything else is classified here. That indirection\n * lets each connector decide what counts as a failure for it — a Telegram\n * `200 OK` with `{\"ok\": false}` is a failure and only the connector knows that —\n * while the decision about *retrying* stays in one place for all of them.\n *\n * `sleep` is injectable so a test proves the real backoff schedule without\n * waiting for it. A test that actually slept would be slow enough that somebody\n * would eventually shorten the delays to speed it up, and then the thing under\n * test would be the shortened version.\n */\nexport async function deliver<T>(\n send: (attempt: number) => Promise<T>,\n policy: RetryPolicy = DEFAULT_RETRY,\n sleep: (ms: number) => Promise<void> = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n): Promise<DeliveryOutcome<T>> {\n const attempts: Attempt[] = [];\n const budget = Math.max(1, policy.attempts);\n\n for (let attempt = 1; attempt <= budget; attempt++) {\n try {\n const value = await send(attempt);\n\n return { ok: true, value, attempts };\n } catch (error) {\n const classified = (error as { classified?: Classified }).classified ?? classifyError(error);\n\n if (!shouldRetry(classified.kind, policy)) {\n attempts.push({ attempt, kind: classified.kind, detail: classified.detail });\n\n return {\n ok: false,\n attempts,\n kind: classified.kind,\n gaveUp:\n classified.kind === \"ambiguous\"\n ? `${AMBIGUOUS_REFUSAL} (${classified.detail})`\n : classified.detail,\n error,\n };\n }\n\n if (attempt >= budget) {\n attempts.push({ attempt, kind: classified.kind, detail: classified.detail });\n\n return {\n ok: false,\n attempts,\n kind: classified.kind,\n gaveUp: `Gave up after ${attempt} attempts. ${classified.detail}`,\n error,\n };\n }\n\n // Exponential, but the provider's own number wins when it gave one. Ours\n // is a guess; theirs is an instruction, and ignoring it is how a rate\n // limit becomes a ban.\n const backoff = Math.min(policy.baseDelayMs * 2 ** (attempt - 1), policy.maxDelayMs);\n const waitedMs = classified.retryAfter ? Math.max(backoff, classified.retryAfter * 1000) : backoff;\n attempts.push({ attempt, kind: classified.kind, detail: classified.detail, waitedMs });\n await sleep(waitedMs);\n }\n }\n\n return { ok: false, attempts, gaveUp: \"Exhausted every attempt.\" };\n}\n\n/* ── Rate floors ──────────────────────────────────────────────────────────── */\n\nconst lastCallAt = new Map<string, number>();\n\n/**\n * Wait until this channel may be used again.\n *\n * In-process only, and that is the correct scope for a host running on one\n * machine. A host that fans out across processes owns the coordination, because\n * the package cannot see the other senders and pretending otherwise would be a\n * limiter that reports success while doing nothing.\n */\nexport async function respectRate(\n channel: string,\n minIntervalMs: number,\n now: () => number = () => Date.now(),\n sleep: (ms: number) => Promise<void> = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),\n): Promise<{ waitedMs: number }> {\n if (minIntervalMs <= 0) return { waitedMs: 0 };\n\n const previous = lastCallAt.get(channel);\n const at = now();\n const waitedMs = previous === undefined ? 0 : Math.max(0, previous + minIntervalMs - at);\n\n if (waitedMs > 0) await sleep(waitedMs);\n lastCallAt.set(channel, at + waitedMs);\n\n return { waitedMs };\n}\n\n/** Only for tests — the map is module-global and would leak between them. */\nexport function resetRateState(): void {\n lastCallAt.clear();\n}\n","/**\n * The connector error taxonomy.\n *\n * A host that retries needs to know WHICH KIND of failure it is looking at.\n * \"Something went wrong\" is not enough information to choose between retrying,\n * pausing for a person, and stopping — so every failure is classified at the\n * point it is known, which is inside the connector, not three frames up where\n * the type has already been lost.\n *\n * | class | kind | retry? |\n * |---|---|---|\n * | `ConnectorConfigError` | `rejected` | never — nothing about a second attempt changes an unset key |\n * | `ConnectorAuthError` | `rejected` | never — the credential is wrong; hammering it locks accounts |\n * | `ConnectorRateLimited` | `refused-explicitly` | yes, after `retryAfter` |\n * | `ConnectorTransient` | `refused-explicitly` | yes — a 5xx is the provider saying it did nothing |\n * | `ConnectorAmbiguous` | `ambiguous` | **only** where the connector is idempotent |\n * | `ConnectorUnreachable` | `unreachable` | yes — it never arrived |\n * | `ConnectorRequestError` | `rejected` | never — a 4xx we caused fails the same way twice |\n *\n * ## What changed, and why it is a fix rather than a refactor\n *\n * `ConnectorTransient` used to mean *5xx OR a thrown transport*, with\n * `retryable = true`. Those are opposite cases. A 5xx is the provider reporting\n * that it did nothing; a thrown transport may be a socket that closed after the\n * bytes went out. Retrying the second on a connector with no idempotency key is\n * a silent double write — the failure this whole layer exists to prevent.\n *\n * So the two are separate classes now, `kind` is the primitive, and `retryable`\n * answers the narrow question *is this safe whatever the connector is?* An older\n * caller reading `.retryable` therefore becomes conservative rather than wrong.\n */\n\nimport {\n classifyError,\n classifyStatus,\n isUnconditionallyRetryable,\n type Attempt,\n type Classified,\n type FailureKind,\n} from \"./delivery\";\n\nexport type ConnectorErrorContext = {\n service: string;\n operation: string;\n /** HTTP status when there was one. */\n status?: number;\n /** The provider's own error code, when it publishes one. */\n providerCode?: string;\n};\n\n/** Base for everything below, so a host can catch the family in one clause. */\nexport class ConnectorError extends Error {\n readonly service: string;\n\n readonly operation: string;\n\n readonly status?: number;\n\n readonly providerCode?: string;\n\n /**\n * Every failed attempt of the call, in order. Set on the error a call throws\n * (`callConnector`), absent on one built anywhere else.\n *\n * `declare`, not a field: it is attached with `Object.defineProperty` when the\n * call gives up, so an error that did not end a call carries no such property.\n */\n declare readonly attempts?: Attempt[];\n\n /**\n * What the call declared. With `attempts`, it separates \"never allowed to\n * retry\" from \"retries ran out\". Same presence rule as `attempts`.\n */\n declare readonly idempotent?: boolean;\n\n /**\n * What kind of failure this is. The primitive every retry decision reads.\n *\n * Defaults to `ambiguous` on the base class deliberately: an unclassified\n * failure must not be assumed safe.\n */\n readonly kind: FailureKind = \"ambiguous\";\n\n /**\n * `options.cause` is the standard `Error` cause — what this error was built\n * from. A failed call's error carries the classified error there, and a thrown\n * transport's carries the original exception, so nothing below is lost when\n * the message is rewritten for a person.\n */\n constructor(message: string, ctx: ConnectorErrorContext, options?: ErrorOptions) {\n // Only pass options that HAVE a cause: `{ cause: undefined }` still installs\n // an own `cause` property, which reads as \"caused by undefined\".\n super(message, options?.cause === undefined ? undefined : { cause: options.cause });\n this.name = new.target.name;\n this.service = ctx.service;\n this.operation = ctx.operation;\n this.status = ctx.status;\n this.providerCode = ctx.providerCode;\n }\n\n /**\n * Safe to retry **without knowing anything about the connector**.\n *\n * Ambiguous failures answer `false` here even though they may be retryable on\n * an idempotent connector — ask `shouldRetry(error.kind, { idempotent })` for\n * that. This getter is the conservative half on purpose.\n */\n get retryable(): boolean {\n return isUnconditionallyRetryable(this.kind);\n }\n\n /** The classification, in the shape `deliver()` reads off a thrown error. */\n get classified(): Classified {\n return { kind: this.kind, detail: this.message };\n }\n}\n\n/**\n * A required piece of configuration is missing or unusable.\n *\n * The message must name the exact key the consumer has to set. \"Stripe is not\n * configured\" sends someone reading source; \"no `secretKey` on the `stripe`\n * connection\" sends them to the line.\n */\nexport class ConnectorConfigError extends ConnectorError {\n override readonly kind: FailureKind = \"rejected\";\n}\n\n/** The provider rejected the credential. Retrying cannot help. */\nexport class ConnectorAuthError extends ConnectorError {\n override readonly kind: FailureKind = \"rejected\";\n}\n\n/** The provider asked us to slow down. It did nothing, and said so. */\nexport class ConnectorRateLimited extends ConnectorError {\n override readonly kind: FailureKind = \"refused-explicitly\";\n\n /** Seconds to wait, when the provider said. */\n readonly retryAfter?: number;\n\n constructor(message: string, ctx: ConnectorErrorContext & { retryAfter?: number }, options?: ErrorOptions) {\n super(message, ctx, options);\n this.retryAfter = ctx.retryAfter;\n }\n\n override get classified(): Classified {\n return {\n kind: this.kind,\n detail: this.message,\n ...(this.retryAfter === undefined ? {} : { retryAfter: this.retryAfter }),\n };\n }\n}\n\n/**\n * A 5xx. The provider answered, and its answer was \"not now\".\n *\n * Named for what it is rather than for how it feels: this is an *explicit\n * refusal*, which is why it is always safe to repeat. A timeout feels the same\n * to the caller and is not the same thing — see `ConnectorAmbiguous`.\n */\nexport class ConnectorTransient extends ConnectorError {\n override readonly kind: FailureKind = \"refused-explicitly\";\n}\n\n/**\n * The request never reached the provider — DNS, refused connection, a socket\n * closed before anything went out.\n *\n * Always safe to repeat, and the only transport-level failure of which that is\n * true.\n */\nexport class ConnectorUnreachable extends ConnectorError {\n override readonly kind: FailureKind = \"unreachable\";\n}\n\n/**\n * Nobody can tell whether the provider acted on it. A timeout, an abort, an\n * error nothing recognises.\n *\n * **Retryable only where the connector declared that repeating a request is\n * harmless.** On anything else this is reported for a person, because the\n * alternative is a duplicate that nobody will ever trace back to a bug.\n */\nexport class ConnectorAmbiguous extends ConnectorError {\n override readonly kind: FailureKind = \"ambiguous\";\n}\n\n/** A 4xx we caused. The same request will fail the same way. */\nexport class ConnectorRequestError extends ConnectorError {\n override readonly kind: FailureKind = \"rejected\";\n}\n\n/**\n * Classify an HTTP response into the taxonomy.\n *\n * 429 before the 4xx sweep, deliberately — see `classifyStatus`.\n */\nexport function classifyHttp(\n status: number,\n ctx: ConnectorErrorContext,\n body: string,\n retryAfter?: number,\n): ConnectorError {\n const detail = body.trim() === \"\" ? \"\" : ` — ${truncate(body, 400)}`;\n const where = `${ctx.service}.${ctx.operation}`;\n\n if (status === 429) {\n return new ConnectorRateLimited(`${where}: rate limited by the provider${detail}`, {\n ...ctx,\n status,\n ...(retryAfter === undefined ? {} : { retryAfter }),\n });\n }\n\n if (status === 401 || status === 403) {\n return new ConnectorAuthError(\n `${where}: the provider rejected the credential (${status})${detail}. ` +\n \"Check the credentials and that they match the mode you are running in — \" +\n \"a live key in sandbox, or the reverse, fails exactly like this.\",\n { ...ctx, status },\n );\n }\n\n if (status >= 500) {\n return new ConnectorTransient(`${where}: provider returned ${status}${detail}`, { ...ctx, status });\n }\n\n return new ConnectorRequestError(`${where}: request rejected with ${status}${detail}`, { ...ctx, status });\n}\n\n/**\n * Turn a thrown transport failure into the right class.\n *\n * The default is `ConnectorAmbiguous`, not `ConnectorTransient`. That single\n * default is the difference between \"a flaky network costs us a retry\" and \"a\n * flaky network costs someone a duplicate charge\".\n */\nexport function classifyThrown(cause: unknown, ctx: ConnectorErrorContext): ConnectorError {\n const classified = classifyError(cause);\n const message = `${ctx.service}.${ctx.operation}: ${classified.detail}`;\n\n // The original goes on as `cause`, as the PHP twin has always passed it as\n // `$previous`: a Node error code or an undici stack is exactly what someone\n // debugging an ambiguous failure needs, and the message keeps only its text.\n return classified.kind === \"unreachable\"\n ? new ConnectorUnreachable(message, ctx, { cause })\n : new ConnectorAmbiguous(message, ctx, { cause });\n}\n\n/**\n * An HTTP failure carrying its classification, for connectors that decide what\n * counts as a failure themselves.\n *\n * Telegram answers `200 OK` with `{\"ok\": false}` for a real refusal, so its\n * connector has to raise the failure itself — and when it does, it must be\n * classified the same way every other failure is. `httpFailure(400, …)` is how\n * it says \"this is a real no\" without inventing a second vocabulary.\n */\nexport function httpFailure(status: number, body: string, retryAfter?: string | null): Error {\n const classified = classifyStatus(status, body, retryAfter);\n const error = new Error(classified.detail) as Error & { classified: Classified };\n error.classified = classified;\n\n return error;\n}\n\nfunction truncate(value: string, max: number): string {\n return value.length <= max ? value : `${value.slice(0, max)}…`;\n}\n","/**\n * The faker layer — every connector ships one, always.\n *\n * ## Why every connector, and not just the ones without a sandbox\n *\n * A sandbox still needs an account, a key, a network, and a provider that is\n * up. That is four ways for someone evaluating the kit to get nothing working,\n * before they have learned anything about the kit. A faker removes all four:\n * vendor a node, drop it on a canvas, press run, see a shaped result.\n *\n * It is also what makes the golden fixtures honest. Fixtures run on both\n * runtimes and are the publish gate; if they needed a network they would either\n * be skipped in CI or be flaky, and a flaky gate is one people learn to\n * re-run rather than read.\n *\n * ## Deterministic, not random\n *\n * Same inputs, same output — always. A faker that returns a fresh uuid every\n * call cannot be asserted on, so its fixtures degrade to \"it did not throw\",\n * which is the assertion that catches nothing. Values here are derived from a\n * seed built out of the service, the operation and the request, so two runs\n * agree and two runtimes can be compared to each other.\n *\n * The values are obviously fake ON PURPOSE — `fake_`-prefixed ids, `example.test`\n * hosts, round numbers. Nobody should ever look at a faked result and wonder\n * whether it moved real money.\n */\n\n/** A faker: one function per service, switching on the operation. */\nexport type ConnectorFaker = (operation: string, request: FakeRequest) => unknown;\n\nexport type FakeRequest = {\n /** The node's resolved config. */\n config: Record<string, unknown>;\n /** The value on the incoming wire. */\n input?: unknown;\n /** Deterministic helpers, seeded from service + operation + config. */\n fake: FakeValues;\n};\n\n/**\n * Deterministic value helpers handed to a faker.\n *\n * Small on purpose. A faker's job is to return the SHAPE the provider returns —\n * the field names a downstream node will reference — not to simulate the\n * provider's business logic.\n */\nexport type FakeValues = {\n /** A stable id with the provider's usual prefix: `id(\"ch\")` → `ch_fake_1a2b3c`. */\n id: (prefix: string) => string;\n /** A stable integer in `[min, max]`. */\n int: (min: number, max: number) => number;\n /** Pick a stable element of a list. */\n pick: <T>(options: readonly T[]) => T;\n /** A fixed ISO-8601 instant, offset by whole seconds. Never `Date.now()`. */\n timestamp: (offsetSeconds?: number) => string;\n /** A stable lowercase hex string of `length` characters. */\n hex: (length: number) => string;\n};\n\n/**\n * The instant every faker counts from.\n *\n * A constant rather than the clock, because a fixture asserting on `createdAt`\n * must not start failing tomorrow. Chosen to be recognisably synthetic.\n */\nexport const FAKE_EPOCH = \"2026-01-01T00:00:00.000Z\";\n\n/** FNV-1a. Small, dependency-free, and stable across both runtimes. */\nexport function seedFrom(...parts: unknown[]): number {\n const text = parts.map((part) => (typeof part === \"string\" ? part : stableJson(part))).join(\"|\");\n let hash = 0x811c9dc5;\n\n for (let i = 0; i < text.length; i++) {\n hash ^= text.charCodeAt(i);\n // 32-bit FNV prime multiply, kept in range without BigInt.\n hash = Math.imul(hash, 0x01000193) >>> 0;\n }\n\n return hash >>> 0;\n}\n\n/**\n * Key order must not change a seed.\n *\n * `JSON.stringify` preserves insertion order, so `{a,b}` and `{b,a}` would hash\n * differently and the \"same inputs, same output\" promise would hold only for\n * objects that happened to be built in the same order — which is exactly the\n * kind of almost-true that survives review and fails in a fixture months later.\n */\nfunction stableJson(value: unknown): string {\n if (value === null || typeof value !== \"object\") return JSON.stringify(value) ?? \"null\";\n if (Array.isArray(value)) return `[${value.map(stableJson).join(\",\")}]`;\n\n const entries = Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== undefined)\n .sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))\n .map(([k, v]) => `${JSON.stringify(k)}:${stableJson(v)}`);\n\n return `{${entries.join(\",\")}}`;\n}\n\n/** Build the deterministic helpers for one faked call. */\nexport function fakeValues(seed: number): FakeValues {\n // A tiny xorshift, advanced per call so successive helpers differ while the\n // whole sequence stays a pure function of the seed.\n let state = seed || 0x9e3779b9;\n const next = (): number => {\n state ^= state << 13;\n state >>>= 0;\n state ^= state >>> 17;\n state ^= state << 5;\n state >>>= 0;\n\n return state;\n };\n\n const hex = (length: number): string => {\n let out = \"\";\n while (out.length < length) out += next().toString(16).padStart(8, \"0\");\n\n return out.slice(0, length);\n };\n\n return {\n hex,\n id: (prefix: string) => `${prefix}_fake_${hex(12)}`,\n int: (min: number, max: number) => min + (next() % Math.max(1, max - min + 1)),\n pick: <T>(options: readonly T[]): T => options[next() % options.length] as T,\n timestamp: (offsetSeconds = 0) =>\n new Date(Date.parse(FAKE_EPOCH) + offsetSeconds * 1000).toISOString(),\n };\n}\n\n/** Build the request handed to a faker, with helpers already seeded. */\nexport function fakeRequest(\n service: string,\n operation: string,\n config: Record<string, unknown>,\n input?: unknown,\n): FakeRequest {\n return { config, input, fake: fakeValues(seedFrom(service, operation, config)) };\n}\n","/**\n * Which copy of a provider a connector talks to — and who decides.\n *\n * Three modes, and the third is the one that makes this marketplace usable:\n *\n * | mode | talks to | needs credentials | needs network |\n * |-----------|-----------------------------|-------------------|---------------|\n * | `live` | the provider, for real | yes | yes |\n * | `sandbox` | the provider's test estate | yes (test ones) | yes |\n * | `fake` | the node's own faker | no | no |\n *\n * ## The environment is the DEFAULT, never the constraint\n *\n * A locally-hosted project defaults to sandbox. It is not *pinned* to sandbox:\n * an author who explicitly asks for `live` on their laptop gets `live`, because\n * the alternative — an environment that silently overrides a stated intention —\n * produces the worst outcome available here, which is a workflow that reports\n * success while having charged nobody. Equally, `sandbox` set explicitly stays\n * sandbox in production, which is how you stage a connector before cutting it\n * over.\n *\n * So: explicit beats the connection, the connection beats the environment.\n *\n * ## Why \"fake\" is a MODE and not a test double\n *\n * Every connector ships a faker (see `faker.ts`), and it is reachable the same\n * way in a test, in a demo and on a laptop with no credentials. Making it a\n * separate mechanism — a mock injected only under test — would mean the code\n * path a consumer develops against is not the one that runs, and the fixtures\n * would prove nothing about the executor.\n */\n\n/** The three environments a connector node can run against. */\nexport type ConnectorMode = \"fake\" | \"sandbox\" | \"live\";\n\n/** What a node's `mode` config field may say. `auto` means \"let the rules decide\". */\nexport type RequestedMode = ConnectorMode | \"auto\";\n\nexport const CONNECTOR_MODES: ConnectorMode[] = [\"fake\", \"sandbox\", \"live\"];\n\n/**\n * How a provider exposes its test estate — the four shapes that actually exist.\n *\n * This is data, not per-node code, because getting it wrong is the difference\n * between hitting a test ledger and hitting a real one. Verified per provider in\n * `.ai/plans/fancy-flow-connector-nodes.md`; a node declares which of the four\n * it is and the core does the rest.\n *\n * - `credential` — same base URL, a test-scoped key selects the estate\n * (Stripe's `sk_test_…`). The trap: pointing a live key at it works, so the\n * only guard is the key itself.\n * - `base-url` — a different host entirely (PayPal's `api-m.sandbox.paypal.com`).\n * - `separate-account` — a distinct tenant you must create, often with its own\n * login host (Salesforce sandbox orgs on `test.salesforce.com`).\n * - `restricted-reach` — there is no separate estate at all; what the provider\n * offers instead is an **account-level restriction on who can see the\n * result**. A Meta app in Development Mode; an unaudited TikTok app that can\n * only post `SELF_ONLY`. Same credentials, same endpoints, same estate — only\n * the audience changes, and nothing in a request selects it.\n *\n * **This is the dangerous one**, and it is why it is not folded into `none`.\n * It is the shape most likely to be mistaken for a sandbox, because it looks\n * exactly like a successful post that nobody can see: `ok: true`, a real id,\n * no audience. `none` says \"there is nothing\"; this says \"there is something,\n * it is not selectable from here, and success does not mean reach\".\n * - `none` — the provider has no test estate. `sandbox` is then not offered at\n * all, and the honest choices are `fake` and `live`.\n * - `unverified` — **nobody has checked yet.** A real state, and the type has to\n * carry it: this is the field where being wrong sends someone to a live\n * estate believing it is a test one, so \"researched but not verified\" must not\n * be forced to pick one of the other five. It belongs on a provider declared\n * `implemented: false`; on one declared `implemented: true` it is a finding,\n * and `providerProblems()` reports it.\n */\nexport type SandboxKind =\n | \"credential\"\n | \"base-url\"\n | \"separate-account\"\n | \"restricted-reach\"\n | \"none\"\n | \"unverified\";\n\n/** Every value, as data, for a host validating input that crossed a JSON boundary. */\nexport const SANDBOX_KINDS: readonly SandboxKind[] = [\n \"credential\",\n \"base-url\",\n \"separate-account\",\n \"restricted-reach\",\n \"none\",\n \"unverified\",\n];\n\n/**\n * Kinds a caller can actually SELECT with `mode: \"sandbox\"`.\n *\n * The other three are not a smaller version of the same thing - each is a\n * different answer to \"what test estate is there\", and none of them gives the\n * resolver anything to point at:\n *\n * - `none` - the provider has no test estate. Honest, and common.\n * - `restricted-reach` - there IS a safer way to run, but it is an\n * account-level setting on the provider's side, not something a request can\n * choose. Selecting it here would be a lie.\n * - `unverified` - nobody has checked. Refusing is the safe direction, because\n * the cost of guessing wrong is a workflow pointed at a live estate.\n */\nconst SELECTABLE_SANDBOX: ReadonlySet<SandboxKind> = new Set<SandboxKind>([\n \"credential\",\n \"base-url\",\n \"separate-account\",\n]);\n\nexport function sandboxIsSelectable(kind: SandboxKind): boolean {\n return SELECTABLE_SANDBOX.has(kind);\n}\n\n/**\n * Why `mode: \"sandbox\"` cannot be honoured for this kind, in the words the\n * person reading it needs.\n *\n * Three reasons, three messages. Collapsing them into \"no sandbox available\"\n * would hide the one that matters: `restricted-reach` is not an absence, it is\n * a different mechanism with a trap in it.\n */\nexport function sandboxRefusal(service: string, kind: SandboxKind): string {\n if (kind === \"restricted-reach\") {\n return (\n service +\n \" has no separate test estate. What it has instead is an ACCOUNT-LEVEL reach restriction - an unreviewed \" +\n \"app that posts only to its own developers, or only privately. Same credentials, same endpoints, same \" +\n \"estate: only the audience changes, and nothing in a request selects it. That makes it the sandbox shape \" +\n \"most likely to be mistaken for a real one, because a restricted call looks exactly like a successful \" +\n 'one - ok, a real id, and no audience. Use \"fake\" to develop without touching the provider, or \"live\" ' +\n \"and confirm reach on the provider's own surface, which is the only place it is visible.\"\n );\n }\n\n if (kind === \"unverified\") {\n return (\n \"Nobody has verified what test estate \" +\n service +\n ' offers, so \"sandbox\" cannot be honoured - the resolver would be guessing, and the cost of guessing ' +\n \"wrong here is a workflow pointed at a live estate while somebody believes it is a test one. Find out \" +\n 'and declare it, or use \"fake\".'\n );\n }\n\n return (\n service +\n ' has no sandbox estate, so \"sandbox\" cannot be honoured. ' +\n 'Use \"fake\" to develop without credentials, or \"live\" to talk to the provider.'\n );\n}\n\n/** The host's view of where it is running. Kept tiny so it is trivial to fake. */\nexport type HostEnvironment = {\n /** True only in a real production deployment. Everything else is \"local\". */\n production: boolean;\n};\n\nexport type ModeInputs = {\n /** What the node's config asked for. `undefined` / `\"auto\"` means \"decide\". */\n requested?: RequestedMode | null;\n /** A mode pinned on the CONNECTION, if the host pinned one. */\n connectionMode?: ConnectorMode | null;\n /** Whether the provider has a test estate at all. */\n sandbox: SandboxKind;\n /** Whether the connection actually carries credentials for the sandbox estate. */\n hasSandboxCredentials: boolean;\n environment: HostEnvironment;\n};\n\n/**\n * Resolve the mode a connector call runs in.\n *\n * Deliberately a pure function of stated facts, so both runtimes can be held to\n * the same table of cases and a host can unit-test its own wiring without a\n * network or a provider.\n */\nexport function resolveConnectorMode(inputs: ModeInputs): ConnectorMode {\n const { requested, connectionMode, sandbox, hasSandboxCredentials, environment } = inputs;\n\n // 1. An explicit ask wins everywhere. This is the rule that makes the\n // environment a default rather than a cage.\n if (requested && requested !== \"auto\") {\n if (requested === \"sandbox\" && !sandboxIsSelectable(sandbox)) {\n throw new ConnectorModeError(sandboxRefusal(\"This connector's provider\", sandbox));\n }\n\n return requested;\n }\n\n // 2. A mode pinned on the connection. One place per service, which is the\n // whole reason connections exist as a separate thing.\n if (connectionMode) return connectionMode;\n\n // 3. Otherwise the environment decides.\n if (environment.production) return \"live\";\n\n // Local: prefer the provider's own test estate when it exists AND is wired.\n // Falling through to `fake` when it is not is what makes a freshly vendored\n // node runnable with no setup at all — the difference between a marketplace\n // you can try and one you can only read about.\n return sandboxIsSelectable(sandbox) && hasSandboxCredentials ? \"sandbox\" : \"fake\";\n}\n\n/** Raised when a requested mode cannot be honoured. Never downgraded silently. */\nexport class ConnectorModeError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ConnectorModeError\";\n }\n}\n\n/** True when the mode reaches the provider over the network. */\nexport function isRemote(mode: ConnectorMode): boolean {\n return mode !== \"fake\";\n}\n","/**\n * A CONNECTION — credentials plus an environment choice — configured once per\n * service and referenced by every node that uses it.\n *\n * ## Why this is not per-node config\n *\n * The obvious design is to put an API key field on each node. It is also wrong,\n * for three reasons that only show up later:\n *\n * 1. **Rotation.** A key on twelve nodes is twelve edits and one you will miss.\n * 2. **The sandbox/live switch has to live somewhere singular.** If each node\n * carries its own, a graph can be half in the test estate and half in the\n * real one, and nothing about the canvas shows it.\n * 3. **Credentials would be in the graph.** A `WorkflowSchema` is plain JSON\n * that gets exported, committed, MCP'd to an agent and pasted into issues.\n * Nothing secret may ever be in it.\n *\n * So a node's config stores a **connection id** — an opaque, non-secret name\n * like `\"stripe\"` or `\"stripe-eu\"` — and the host resolves it to credentials at\n * run time. This is the same arrangement fancy-flow already uses for LLM clients\n * and `fancy-git` uses for providers: the node cannot reach for a credential,\n * the host has to hand one over.\n *\n * ```ts\n * registerConnectionHost({\n * environment: { production: process.env.NODE_ENV === \"production\" },\n * connections: {\n * stripe: {\n * service: \"stripe\",\n * live: { secretKey: process.env.STRIPE_SECRET_KEY },\n * sandbox: { secretKey: process.env.STRIPE_TEST_SECRET_KEY },\n * },\n * },\n * });\n * ```\n *\n * Note what is NOT here: no key, no token, no placeholder that looks like one.\n * Credentials are configuration, and configuration comes from the consumer's\n * environment.\n */\n\nimport { ConnectorConfigError } from \"./errors\";\nimport {\n ConnectorModeError,\n resolveConnectorMode,\n sandboxIsSelectable,\n sandboxRefusal,\n type ConnectorMode,\n type HostEnvironment,\n type RequestedMode,\n type SandboxKind,\n} from \"./mode\";\n\n/**\n * Credentials for one estate. Values are opaque strings the node's auth\n * strategy understands — `secretKey`, `clientId`/`clientSecret`, `token`.\n */\nexport type ConnectionCredentials = Record<string, string | undefined>;\n\n/** One configured connection to one service. */\nexport type ConnectionSpec = {\n /** The service this connection is for — must match the node's `connector.service`. */\n service: string;\n /** Human label, shown in the picker. Defaults to the id. */\n label?: string;\n /**\n * Pin this connection to a mode. Optional, and usually absent: leaving it\n * unset is what lets the environment supply the default.\n */\n mode?: ConnectorMode;\n live?: ConnectionCredentials;\n sandbox?: ConnectionCredentials;\n /**\n * Override the base URL per mode. Only needed for a `separate-account`\n * provider whose tenant has its own host (a Salesforce instance URL); the\n * `base-url` providers carry theirs in the node's service descriptor.\n */\n baseUrl?: Partial<Record<ConnectorMode, string>>;\n};\n\nexport type ConnectionHost = {\n environment: HostEnvironment;\n connections: Record<string, ConnectionSpec>;\n};\n\nlet host: ConnectionHost | null = null;\n\n/**\n * Install the host's connections. Returns an unregister function.\n *\n * One registration for the whole app, matching how a person thinks about it:\n * you authorize Stripe once, then every node that needs Stripe already has it.\n */\nexport function registerConnectionHost(next: ConnectionHost | null): () => void {\n host = next;\n\n return () => {\n if (host === next) host = null;\n };\n}\n\nexport function getConnectionHost(): ConnectionHost | null {\n return host;\n}\n\n/** Connection ids the host has configured for a service — feeds the picker. */\nexport function connectionsFor(service: string): Array<{ id: string; label: string }> {\n const current = getConnectionHost();\n if (!current) return [];\n\n return Object.entries(current.connections)\n .filter(([, spec]) => spec.service === service)\n .map(([id, spec]) => ({ id, label: spec.label ?? id }));\n}\n\n/** What a node's executor actually needs, once everything has been resolved. */\nexport type ResolvedConnection = {\n id: string;\n service: string;\n mode: ConnectorMode;\n /** Empty in `fake` mode — there is nothing to authenticate against. */\n credentials: ConnectionCredentials;\n /** Undefined in `fake` mode. */\n baseUrl?: string;\n};\n\nexport type ResolveOptions = {\n service: string;\n operation: string;\n /** The connection id from host config. Defaults to the service name. */\n connectionId?: string | null;\n /** The mode the caller asked for. */\n requested?: RequestedMode | null;\n /**\n * Credentials handed straight in, bypassing the registry entirely.\n *\n * The primary path for a host that already owns credential storage and has no\n * wish to hand a package a second copy: it passes them per call, this resolver\n * does not consult any global, and there is nothing to initialise. The\n * registry below remains for hosts that prefer to configure once.\n *\n * An EMPTY object is not the same as `null`: it means \"the caller supplied\n * credentials and there are none\", which fails the `requires` check loudly\n * rather than falling through to the registry and silently using someone\n * else's.\n */\n credentials?: ConnectionCredentials | null;\n sandbox: SandboxKind;\n /** Base URL per mode, from the node's service descriptor. */\n baseUrls?: Partial<Record<ConnectorMode, string>>;\n /** Credential keys that must be present for a remote call. */\n requires?: string[];\n};\n\n/**\n * Resolve the connection and mode for one call.\n *\n * ## `fake` needs no host at all\n *\n * A freshly vendored connector runs before anything is configured, and that is\n * deliberate: it is the difference between a marketplace someone can try and\n * one they can only read about. So the no-host case resolves to `fake` rather\n * than throwing — UNLESS the author explicitly asked for a remote mode, in\n * which case the ask is honoured by failing loudly instead of quietly doing\n * something else.\n */\nexport function resolveConnection(options: ResolveOptions): ResolvedConnection {\n const { service, operation, sandbox } = options;\n const requestedMode = options.requested ?? \"auto\";\n\n // Checked FIRST, before anything about the host, because it is a static fact\n // about the SERVICE. Asking for a sandbox that does not exist should say so\n // whether or not a connection happens to be configured — otherwise the same\n // mistake produces two different errors depending on unrelated state, and the\n // more useful one is the one you never see.\n if (requestedMode === \"sandbox\" && !sandboxIsSelectable(sandbox)) {\n throw new ConnectorModeError(sandboxRefusal(service, sandbox));\n }\n\n const id = options.connectionId?.trim() || service;\n\n // Explicit credentials win over everything. A host that passes them has\n // already made every decision this resolver would otherwise make, and reading\n // a global on top of that is how two sources of truth start disagreeing.\n if (options.credentials) {\n // Asking for the faker wins even here — an explicit `fake` is a statement,\n // and credentials sitting in the same call do not override it. Nothing\n // secret is carried into the fake path.\n if (requestedMode === \"fake\") return { id, service, mode: \"fake\", credentials: {} };\n\n // Otherwise: supplying credentials IS the statement that a real call is\n // intended, so `auto` resolves to `live` rather than consulting an\n // environment this resolver was told nothing about. `sandbox` is honoured\n // as asked.\n const mode: ConnectorMode = requestedMode === \"auto\" ? \"live\" : requestedMode;\n const missing = (options.requires ?? []).filter((key) => !options.credentials?.[key]);\n\n if (missing.length > 0) {\n throw new ConnectorConfigError(\n `${service}.${operation} was given credentials with no ${missing.join(\", \")}. ` +\n \"Nothing is inferred from an incomplete credential set — a partial one is a configuration mistake, \" +\n \"and quietly falling back to another source is how the wrong account gets written to.\",\n { service, operation },\n );\n }\n\n const explicitBase = options.baseUrls?.[mode];\n\n return {\n id,\n service,\n mode,\n credentials: options.credentials,\n ...(explicitBase ? { baseUrl: explicitBase } : {}),\n };\n }\n\n const current = getConnectionHost();\n const spec = current?.connections[id];\n\n if (spec && spec.service !== service) {\n throw new ConnectorConfigError(\n `connection \"${id}\" is configured for the \"${spec.service}\" service, but this node needs \"${service}\".`,\n { service, operation },\n );\n }\n\n if (!current || !spec) {\n if (requestedMode !== \"auto\" && requestedMode !== \"fake\") {\n throw new ConnectorConfigError(\n `no \"${id}\" connection is registered, so ${service}.${operation} cannot run in \"${requestedMode}\" mode. ` +\n \"Call registerConnectionHost({ connections: { \" +\n `${id}: { service: \"${service}\", live: { … } } } }) — the node has no credentials of its own ` +\n 'and must not invent any. Set the node\\'s mode to \"fake\" to develop without one.',\n { service, operation },\n );\n }\n\n return { id, service, mode: \"fake\", credentials: {} };\n }\n\n const sandboxCreds = spec.sandbox ?? {};\n const mode = resolveConnectorMode({\n requested: requestedMode,\n connectionMode: spec.mode ?? null,\n sandbox,\n hasSandboxCredentials: hasAll(sandboxCreds, options.requires ?? []),\n environment: current.environment,\n });\n\n if (mode === \"fake\") {\n return { id, service, mode, credentials: {} };\n }\n\n const credentials = (mode === \"sandbox\" ? spec.sandbox : spec.live) ?? {};\n const missing = (options.requires ?? []).filter((key) => !credentials[key]);\n\n if (missing.length > 0) {\n // Never silently degrade to the faker here. A run that \"succeeded\" because\n // it quietly stopped talking to the provider is the worst failure this\n // whole design exists to prevent — it is green, it is wrong, and nothing\n // reports it.\n throw new ConnectorConfigError(\n `connection \"${id}\" has no ${missing.join(\", \")} for ${mode} mode. ` +\n `Set connections.${id}.${mode}.${missing[0]}, or run this node in \"fake\" mode.`,\n { service, operation },\n );\n }\n\n const baseUrl = spec.baseUrl?.[mode] ?? options.baseUrls?.[mode];\n\n return { id, service, mode, credentials, ...(baseUrl ? { baseUrl } : {}) };\n}\n\nfunction hasAll(creds: ConnectionCredentials, keys: string[]): boolean {\n return keys.length > 0 && keys.every((key) => Boolean(creds[key]));\n}\n","/**\n * The one call path every connector uses.\n *\n * A connector never chooses between \"call the provider\" and \"call the faker\" —\n * it describes the request it wants and hands it here. That is the point: the\n * code path a consumer develops against with no credentials is the same code\n * path that runs in production, so fixtures prove something about the connector\n * rather than about a mock.\n *\n * ```ts\n * const charge = await callConnector(STRIPE, {\n * operation: \"charge_create\",\n * credentials,\n * request: { method: \"POST\", path: \"/v1/charges\", form: { amount, currency } },\n * idempotencyKey,\n * idempotent: true,\n * });\n * ```\n *\n * ## What changed from the first version of this runtime, and why\n *\n * The old loop caught a thrown transport, called it transient, and retried it.\n * A thrown transport includes a **timeout**, which may be a request the provider\n * received and acted on — so on a connector with no idempotency key that retry\n * is a silent double write. Retrying is now a function of the classification AND\n * the connector's declared idempotency, and `idempotent` defaults to `false`\n * because a connector that has not thought about it must not be assumed safe.\n *\n * See `delivery.ts` for the full table.\n */\n\nimport {\n DEFAULT_RETRY,\n deliver,\n type Attempt,\n type Classified,\n type DeliveryOutcome,\n type FailureKind,\n} from \"./delivery\";\nimport {\n classifyHttp,\n classifyThrown,\n ConnectorAmbiguous,\n ConnectorAuthError,\n ConnectorConfigError,\n ConnectorError,\n ConnectorRateLimited,\n ConnectorRequestError,\n ConnectorTransient,\n ConnectorUnreachable,\n type ConnectorErrorContext,\n} from \"./errors\";\nimport { fakeRequest, type ConnectorFaker } from \"./faker\";\nimport type { ConnectorMode, RequestedMode, SandboxKind } from \"./mode\";\nimport {\n resolveConnection,\n type ConnectionCredentials,\n type ResolvedConnection,\n} from \"./connection\";\n\n/**\n * Everything true of a SERVICE rather than of one operation.\n *\n * One per provider, declared once and shared by that provider's connectors. It\n * is data — base URLs, required credential keys, how the sandbox is selected —\n * so that facts verified against provider documentation live in exactly one\n * reviewable place instead of being retyped into each connector.\n */\nexport type ServiceDescriptor = {\n service: string;\n title: string;\n sandbox: SandboxKind;\n /**\n * Base URL per mode. A `credential`-sandbox provider gives the same URL for\n * both, which is itself worth writing down: it is why a live key pointed at\n * \"sandbox\" quietly reaches production.\n */\n baseUrls: Partial<Record<ConnectorMode, string>>;\n /** Credential keys a remote call cannot proceed without. */\n requires: string[];\n /**\n * Apply the provider's auth scheme to an outgoing request.\n *\n * A FUNCTION rather than a declarative header name, because there is no common\n * shape: the key can be a header (under any of a dozen names), a Basic\n * username with a blank password, a query parameter, a body field, or a URL\n * path segment — and several providers need more than one at once. It receives\n * the resolved mode too, because for some providers auth and estate are the\n * same decision expressed in the URL.\n */\n authorize: (\n credentials: ConnectionCredentials,\n request: PreparedRequest,\n mode: ConnectorMode,\n ) => void | Promise<void>;\n /** The service's faker. Required — see `faker.ts`. */\n faker: ConnectorFaker;\n /** Header the provider uses for idempotency, when it has one. */\n idempotencyHeader?: string;\n /**\n * Where this provider puts its OWN error code on a failed response, when it\n * publishes one — read only for a status of 400 or above, and carried as\n * `error.providerCode`.\n *\n * **Declared per service, never guessed.** The shapes do not agree, and the\n * near-misses are the dangerous part: Bluesky's `error` is a code\n * (`\"AuthenticationRequired\"`), Mastodon's `error` is a sentence\n * (`\"The access token is invalid\"`), Discord's code is a number under `code`,\n * and some providers use a header. A generic reader would publish Mastodon's\n * sentence as a code — the plausible answer that is wrong. With no\n * declaration the field is absent, which says *nobody said where to look*.\n *\n * Return a string or an integer (carried as its decimal string); anything\n * else, a blank string, or a throw, leaves the code absent. **A throw never\n * reaches the call**: a code is a detail on an answer the status already gave,\n * and a reader choking on an HTML error page must not turn an explicit `401`\n * into an unclassified failure.\n */\n providerCodeFrom?: (response: TransportResponse) => string | number | null | undefined;\n};\n\nexport type ConnectorRequest = {\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n query?: Record<string, string | number | boolean | undefined>;\n headers?: Record<string, string>;\n /** JSON body. Mutually exclusive with `form`. */\n json?: unknown;\n /** `application/x-www-form-urlencoded` body — what several payment APIs want. */\n form?: Record<string, string | number | boolean | undefined>;\n};\n\nexport type PreparedRequest = {\n method: string;\n url: string;\n headers: Record<string, string>;\n body?: string;\n};\n\n/** The HTTP seam. A host may swap it; nothing here assumes a browser or Node. */\nexport type Transport = (request: PreparedRequest) => Promise<TransportResponse>;\n\nexport type TransportResponse = {\n status: number;\n headers: Record<string, string>;\n body: string;\n};\n\nexport type CallOptions = {\n operation: string;\n /**\n * Credentials, passed IN.\n *\n * **Nothing in this package reads `process.env`.** A host stores values\n * wherever it stores values — a gitignored file behind a whitelist, a secret\n * manager, a Laravel config — and hands them over per call. A package that\n * reached for the environment itself would bypass that discipline entirely,\n * and a test in this repo asserts that it does not.\n *\n * Omit to resolve through a registered connection host instead; see\n * `connection.ts`.\n */\n credentials?: ConnectionCredentials;\n /** Which estate to use. Resolved from the connection host when omitted. */\n // `RequestedMode`, not `ConnectorMode`: this value is passed straight to\n // `resolveConnection`, which takes the wider type, and `resolveConnectorMode`\n // handles `\"auto\"` explicitly. Typed narrowly, the most common value a caller\n // has -- `\"auto\"` is the defaultConfig of every connector node -- was the one\n // it could not pass, while working perfectly at runtime.\n mode?: RequestedMode;\n /** Connection id, for the registered-host path. */\n connectionId?: string | null;\n /**\n * The caller's own config for this call.\n *\n * Passed to the faker so its output is deterministic, and read for\n * `connection` and `mode` when those were not given explicitly — which is what\n * a workflow node stores them in. Explicit arguments always win; this is a\n * convenience for a host whose configuration IS a config object, not a second\n * source of truth.\n */\n config?: Record<string, unknown>;\n input?: unknown;\n request: ConnectorRequest;\n /**\n * Value for the provider's idempotency header. Derive it from the run and the\n * step, never a fresh uuid: the entire point is that a RETRY carries the same\n * key. See `idempotency.ts`.\n */\n idempotencyKey?: string;\n /**\n * Whether repeating this exact request is harmless.\n *\n * **Defaults to `false`.** The only thing that makes an ambiguous failure\n * retryable, so it is opt-in and has to be true because the provider makes it\n * true — not because a retry would be convenient.\n */\n idempotent?: boolean;\n /** Retry budget for retryable failures. Total attempts, including the first. */\n attempts?: number;\n /** Override the transport for this call. Otherwise the registered one, then `fetch`. */\n transport?: Transport;\n /**\n * Called for each FAILED attempt once the call has finished, so a host can\n * journal what actually happened. A call that worked on the third try is a\n * different operational fact from one that worked immediately.\n *\n * A callback rather than a field on the result, because the result is a\n * published shape and this is a diagnostic — see `ConnectorResult`.\n */\n onAttempt?: (attempt: Attempt) => void;\n};\n\n/**\n * What a connector call returns, alongside the provider's own payload.\n *\n * The mode is REPORTED, never inferred by the caller. A call that emitted the\n * provider's data alone would leave every downstream reader — a human, an agent,\n * a log — unable to tell a faked result from a real one, which is the single\n * most important fact about a connector run.\n *\n * **Retry history is deliberately NOT here.** It was, briefly, and the flow\n * node marketplace's cross-runtime fixtures caught it immediately: this object\n * is published on a node's output port and referenced by templates and agents,\n * and PHP's `ConnectorResult::toArray()` does not carry it. Two runtimes\n * publishing different shapes is the exact drift those fixtures exist to\n * prevent, and a diagnostic field is a poor reason to break it. Pass\n * `onAttempt` to observe retries.\n */\nexport type ConnectorResult<T = unknown> = {\n data: T;\n /** Which estate this actually ran against. Always reported, never inferred. */\n mode: ConnectorMode;\n connection: string;\n};\n\nlet registeredTransport: Transport | null = null;\n\n/** Install a transport. Only needed if the runtime has no global `fetch`. */\nexport function registerTransport(next: Transport | null): void {\n registeredTransport = next;\n}\n\nexport async function callConnector<T = unknown>(\n service: ServiceDescriptor,\n options: CallOptions,\n): Promise<ConnectorResult<T>> {\n const config = options.config ?? {};\n const connection = resolveConnection({\n service: service.service,\n operation: options.operation,\n connectionId: options.connectionId ?? readString(config, \"connection\"),\n requested: options.mode ?? (readString(config, \"mode\") as RequestedMode | null),\n credentials: options.credentials ?? null,\n sandbox: service.sandbox,\n baseUrls: service.baseUrls,\n requires: service.requires,\n });\n\n if (connection.mode === \"fake\") {\n return {\n data: service.faker(\n options.operation,\n fakeRequest(service.service, options.operation, config, options.input),\n ) as T,\n mode: \"fake\",\n connection: connection.id,\n };\n }\n\n const ctx: ConnectorErrorContext = { service: service.service, operation: options.operation };\n const prepared = await prepare(service, connection, options);\n const send = options.transport ?? registeredTransport ?? fetchTransport;\n\n const outcome = await deliver<unknown>(\n async () => {\n let response: TransportResponse;\n\n try {\n response = await send(prepared);\n } catch (cause) {\n // classifyThrown decides unreachable vs ambiguous. It never returns a\n // 5xx-shaped \"transient\", because nothing here reached the provider.\n throw classifyThrown(cause, ctx);\n }\n\n if (response.status < 400) {\n return response.body.trim() === \"\" ? null : parseJson(response.body, ctx);\n }\n\n const providerCode = readProviderCode(service, response);\n\n throw classifyHttp(\n response.status,\n providerCode === undefined ? ctx : { ...ctx, providerCode },\n response.body,\n readRetryAfter(response.headers),\n );\n },\n {\n ...DEFAULT_RETRY,\n attempts: options.attempts ?? DEFAULT_RETRY.attempts,\n idempotent: options.idempotent ?? false,\n },\n );\n\n if (options.onAttempt) {\n for (const attempt of outcome.attempts) options.onAttempt(attempt);\n }\n\n if (!outcome.ok) {\n throw failureFrom(outcome, ctx, options.idempotent ?? false);\n }\n\n return { data: outcome.value as T, mode: connection.mode, connection: connection.id };\n}\n\n/**\n * Turn an exhausted delivery into the error a host sees.\n *\n * An ambiguous failure on a non-idempotent connector gets a message that says\n * *go and look*, because that is the action. \"Request failed\" would send someone\n * to re-run it, which is the one thing that must not happen.\n *\n * ## What the provider said survives the rewrite\n *\n * The message is rewritten for a person; nothing else is. The last failure's\n * HTTP `status` and `providerCode` are copied across, and the failure itself is\n * the standard `cause`.\n *\n * Until 0.5.0 this built a bare `ConnectorError` from `ctx` alone, because the\n * outcome never carried the error — so every failed call, on every host, arrived\n * with no status while its message quoted one. The nightly connector probes ask a\n * real provider to refuse an impossible credential and read `error.status` to\n * see the refusal; they reported \"failed before any status arrived\" for every\n * provider, every night, against providers that had answered exactly right.\n *\n * ## The class is the one the failure was classified as\n *\n * A `401` throws `ConnectorAuthError`, a `429` throws `ConnectorRateLimited`\n * with its `retryAfter`, and every other failure the class its `kind` names —\n * the table at the top of `errors.ts`, which a host catching by class was\n * already entitled to. `kind` alone cannot tell an auth failure from any other\n * `rejected`, or a throttle from a 5xx, so those two come from the classified\n * error; the rest are the same answer either way. This is exactly what the PHP\n * twin's `failureFrom()` does, which until 0.5.0 mapped by kind alone and threw\n * a `ConnectorRequestException` for a `401`.\n */\nfunction failureFrom(\n outcome: DeliveryOutcome<unknown>,\n ctx: ConnectorErrorContext,\n idempotent: boolean,\n): ConnectorError {\n const last = outcome.attempts.at(-1);\n const kind: FailureKind = outcome.kind ?? last?.kind ?? \"ambiguous\";\n const message = outcome.gaveUp ?? `${ctx.service}.${ctx.operation} failed.`;\n const cause = outcome.error;\n const said = cause instanceof ConnectorError ? cause : undefined;\n const context: ConnectorErrorContext = {\n ...ctx,\n ...(said?.status === undefined ? {} : { status: said.status }),\n ...(said?.providerCode === undefined ? {} : { providerCode: said.providerCode }),\n };\n const error = failureOfClass(kind, said, message, context, cause === undefined ? undefined : { cause });\n\n // `kind` is readonly on the class; this is the one place that knows the\n // aggregate answer across attempts, so it is set here rather than guessed.\n Object.defineProperty(error, \"kind\", { value: kind, enumerable: false });\n Object.defineProperty(error, \"attempts\", { value: outcome.attempts, enumerable: false });\n Object.defineProperty(error, \"idempotent\", { value: idempotent, enumerable: false });\n\n return error;\n}\n\n/**\n * The taxonomy class for a failure: the classified error's own class where\n * `kind` cannot express it (auth, rate limit), the class `kind` names otherwise.\n *\n * The subclass is trusted only when it AGREES with the aggregate kind. It always\n * does for anything `callConnector` raises; a disagreement would mean the class\n * describes a different failure from the one being reported.\n */\nfunction failureOfClass(\n kind: FailureKind,\n said: ConnectorError | undefined,\n message: string,\n context: ConnectorErrorContext,\n options: ErrorOptions | undefined,\n): ConnectorError {\n if (said?.kind === kind && said instanceof ConnectorAuthError) {\n return new ConnectorAuthError(message, context, options);\n }\n\n if (said?.kind === kind && said instanceof ConnectorRateLimited) {\n const retryAfter = said.retryAfter === undefined ? {} : { retryAfter: said.retryAfter };\n\n return new ConnectorRateLimited(message, { ...context, ...retryAfter }, options);\n }\n\n switch (kind) {\n case \"unreachable\":\n return new ConnectorUnreachable(message, context, options);\n case \"refused-explicitly\":\n return new ConnectorTransient(message, context, options);\n case \"rejected\":\n return new ConnectorRequestError(message, context, options);\n case \"ambiguous\":\n return new ConnectorAmbiguous(message, context, options);\n }\n}\n\nasync function prepare(\n service: ServiceDescriptor,\n connection: ResolvedConnection,\n options: CallOptions,\n): Promise<PreparedRequest> {\n const base = connection.baseUrl ?? service.baseUrls[connection.mode];\n\n if (!base) {\n throw new ConnectorConfigError(\n `${service.service}: no base URL for \"${connection.mode}\" mode. ` +\n \"The service descriptor must declare one per mode it supports.\",\n { service: service.service, operation: options.operation },\n );\n }\n\n const { method, path, query, headers, json, form } = options.request;\n const url = new URL(path.replace(/^\\/+/, \"\"), base.endsWith(\"/\") ? base : `${base}/`);\n\n for (const [key, value] of Object.entries(query ?? {})) {\n if (value !== undefined) url.searchParams.set(key, String(value));\n }\n\n const prepared: PreparedRequest = {\n method,\n url: url.toString(),\n headers: { Accept: \"application/json\", ...headers },\n };\n\n if (form) {\n prepared.headers[\"Content-Type\"] = \"application/x-www-form-urlencoded\";\n prepared.body = encodeForm(form);\n } else if (json !== undefined) {\n prepared.headers[\"Content-Type\"] = \"application/json\";\n prepared.body = JSON.stringify(json);\n }\n\n if (options.idempotencyKey && service.idempotencyHeader) {\n prepared.headers[service.idempotencyHeader] = options.idempotencyKey;\n }\n\n await service.authorize(connection.credentials, prepared, connection.mode);\n\n return prepared;\n}\n\n/**\n * Form encoding, including the bracketed nesting several payment APIs use\n * (`metadata[order_id]=7`). Flat maps pass through unchanged.\n */\nfunction encodeForm(form: Record<string, unknown>): string {\n const pairs: string[] = [];\n\n const walk = (prefix: string, value: unknown): void => {\n if (value === undefined || value === null) return;\n\n if (Array.isArray(value)) {\n value.forEach((item, index) => walk(`${prefix}[${index}]`, item));\n\n return;\n }\n\n if (typeof value === \"object\") {\n for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {\n walk(prefix === \"\" ? key : `${prefix}[${key}]`, nested);\n }\n\n return;\n }\n\n pairs.push(`${encodeURIComponent(prefix)}=${encodeURIComponent(String(value))}`);\n };\n\n walk(\"\", form);\n\n return pairs.join(\"&\");\n}\n\nconst fetchTransport: Transport = async (request) => {\n const globalFetch = (globalThis as { fetch?: typeof fetch }).fetch;\n\n if (!globalFetch) {\n throw new Error(\n \"no global fetch in this runtime — call registerTransport() with one before running a connector remotely.\",\n );\n }\n\n const response = await globalFetch(request.url, {\n method: request.method,\n headers: request.headers,\n ...(request.body === undefined ? {} : { body: request.body }),\n });\n\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key.toLowerCase()] = value;\n });\n\n return { status: response.status, headers, body: await response.text() };\n};\n\nfunction parseJson(body: string, ctx: ConnectorErrorContext): unknown {\n try {\n return JSON.parse(body);\n } catch {\n // A body that is not JSON on a 2xx is a REJECTION, not something to retry:\n // the same request will produce the same unparseable body.\n const error = new ConnectorError(\n `${ctx.service}.${ctx.operation}: the provider returned a body that is not JSON.`,\n ctx,\n );\n Object.defineProperty(error, \"kind\", { value: \"rejected\", enumerable: false });\n\n throw error;\n }\n}\n\n/**\n * The service's declared provider code, or absent. Never throws — see\n * `ServiceDescriptor.providerCodeFrom` for why a reader's failure must not\n * become the call's.\n */\nfunction readProviderCode(service: ServiceDescriptor, response: TransportResponse): string | undefined {\n if (!service.providerCodeFrom) return undefined;\n\n let code: unknown;\n\n try {\n code = service.providerCodeFrom(response);\n } catch {\n return undefined;\n }\n\n if (typeof code === \"number\") return Number.isSafeInteger(code) ? String(code) : undefined;\n\n return typeof code === \"string\" && code.trim() !== \"\" ? code : undefined;\n}\n\nfunction readRetryAfter(headers: Record<string, string>): number | undefined {\n const raw = headers[\"retry-after\"];\n if (!raw) return undefined;\n const seconds = Number(raw);\n\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n\nexport type { Classified };\n\nfunction readString(config: Record<string, unknown>, key: string): string | null {\n const value = config[key];\n\n return typeof value === \"string\" && value.trim() !== \"\" ? value.trim() : null;\n}\n","/**\n * API drift — being told the provider changed, rather than discovering it.\n *\n * ## The failure this addresses\n *\n * A connector's worst failure is silent. The provider changes its API and\n * nothing says so until something breaks in production — and by then the person\n * who wrote the connector has moved on, the reason it worked is gone, and the\n * first symptom is usually a 400 that reads like bad input.\n *\n * ## What the system DOES on drift: it REPORTS. It never adapts.\n *\n * This is the load-bearing decision and it is deliberate.\n *\n * A connector that reshapes itself around a changed API is worse than one that\n * fails loudly, for three reasons that compound:\n *\n * 1. **It cannot be reviewed.** The bytes that go out stop being a function of\n * the source someone read. A host that guarantees \"what was approved is what\n * was sent\" cannot make that guarantee about code that rewrote itself.\n * 2. **Its behaviour depends on WHEN it ran**, because it depends on what the\n * spec said at that moment. Two runs of the same workflow on the same input\n * can differ, and nothing in the run record explains why.\n * 3. **Adaptation is only ever a guess at intent.** A renamed field might be a\n * rename or might be a different field with a similar name; a removed one\n * might be gone or moved. Getting it wrong writes wrong data confidently.\n *\n * ## And it never changes RUNTIME behaviour either\n *\n * A drift check runs out of band — on a schedule, in CI, on demand. It does not\n * fail a call that is working. If the spec changed and the call still succeeds,\n * failing the run would be a self-inflicted outage caused by a document. The\n * only thing that changes runtime behaviour is the provider actually refusing,\n * which the error taxonomy already handles.\n *\n * So: **drift reports; the provider decides; a person acts.**\n *\n * ## Two detection strategies, because most providers publish nothing\n *\n * 1. **Specs**, where a provider publishes a machine-readable one at a stable\n * URL. Checked against the connector's declared slice — see `ApiContract`.\n * 2. **Recorded shapes**, where it does not. A fixture of the response fields a\n * connector reads, compared against a live response on a schedule. Field\n * NAMES only; values are never recorded, because a recorded response from a\n * real account is a data leak wearing a test fixture's clothes.\n *\n * The second is the fallback and is designed anyway, because it is the majority\n * case: of the providers surveyed for this package, the ones publishing a\n * maintained spec are a minority, and publishing one is not the same as\n * maintaining it — `slackapi/slack-api-specs` has not been touched since 2021.\n */\n\n/* ── What a connector declares it depends on ─────────────────────────────── */\n\n/**\n * One operation a connector calls.\n *\n * The `path` is the TEMPLATE, matching the provider's own documentation, so it\n * can be looked up in a spec: `/v1/charges/{charge}`, not the interpolated URL.\n */\nexport type ContractOperation = {\n /** Stable name, matching the connector's own operation id. */\n operation: string;\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n path: string;\n /** Request fields the connector SENDS. Drift here breaks writes. */\n sends?: string[];\n /** Response fields the connector READS. Drift here breaks reads, silently. */\n reads?: string[];\n};\n\n/**\n * Where a provider's machine-readable description lives.\n *\n * `kind` matters because \"machine-readable\" is not one format. AT Protocol\n * publishes **lexicons**, not OpenAPI, and a checker that assumed OpenAPI would\n * report every AT Protocol connector as unspecified — which is a different and\n * much worse answer than \"specified, in another format\".\n */\nexport type SpecSource =\n | { kind: \"openapi\"; url: string; note?: string }\n | { kind: \"lexicon\"; url: string; note?: string }\n | {\n kind: \"none\";\n /**\n * Why there is no spec, and what the fallback is. Required — \"none\" with\n * no reason is indistinguishable from \"nobody looked\".\n */\n note: string;\n };\n\n/** Everything a connector declares about the API it was written against. */\nexport type ApiContract = {\n connector: string;\n /** Base URL the operations hang off, for a human reading the report. */\n baseUrl?: string;\n spec: SpecSource;\n operations: ContractOperation[];\n /** When a person last read the provider's documentation for this contract. */\n reviewedOn: string;\n};\n\n/* ── What a check produces ───────────────────────────────────────────────── */\n\nexport type DriftFinding = {\n /**\n * `missing-operation` — the path or method is gone. A write will start failing.\n * `missing-request-field` — something we send is no longer accepted.\n * `missing-response-field` — something we read is gone. **Silent**: the code\n * keeps running and produces nothing, which is the dangerous one.\n * `unreadable-spec` — the spec URL moved or stopped parsing. Not drift in the\n * API, but drift in our ability to see it, and it must not read as \"clean\".\n * `stale-review` — nobody has looked at this contract in a long time.\n */\n kind:\n | \"missing-operation\"\n | \"missing-request-field\"\n | \"missing-response-field\"\n | \"unreadable-spec\"\n | \"stale-review\";\n operation?: string;\n detail: string;\n};\n\nexport type DriftReport = {\n connector: string;\n checkedAt: string;\n /** `unchecked` is a real outcome and never counts as clean. */\n outcome: \"clean\" | \"drifted\" | \"unchecked\";\n findings: DriftFinding[];\n /** How the check was done, so a report can be read without the code. */\n method: \"openapi\" | \"lexicon\" | \"recorded-shape\" | \"none\";\n};\n\n/* ── OpenAPI, read narrowly and on purpose ───────────────────────────────── */\n\n/**\n * The subset of OpenAPI this needs.\n *\n * Deliberately not a full parser and deliberately not a dependency. The general\n * problem — diff two OpenAPI documents — is large, and we do not have it. We\n * have a much smaller one: *for the operations this connector actually calls,\n * do the path, the method, and the fields we send and read still exist?*\n *\n * That projection is ours by definition, it is a few dozen lines, and it does\n * not drag a schema-validation library into every consumer's tree. A full diff\n * would also report thousands of changes to parts of the API nobody here calls,\n * which is the reliable way to make a drift report unread.\n */\ntype OpenApiDocument = {\n paths?: Record<string, Record<string, unknown>>;\n components?: { schemas?: Record<string, unknown> };\n};\n\n/** Resolve a local `$ref`. Remote refs are not followed — see `resolveRef`. */\nfunction resolveRef(document: OpenApiDocument, node: unknown, depth = 0): unknown {\n if (depth > 8 || node === null || typeof node !== \"object\") return node;\n\n const ref = (node as { $ref?: string }).$ref;\n if (typeof ref !== \"string\") return node;\n\n // Only same-document refs. A remote `$ref` would mean fetching arbitrary URLs\n // from a document we do not control, which is a request-forgery surface for a\n // check that runs on a schedule with nobody watching.\n if (!ref.startsWith(\"#/\")) return node;\n\n let current: unknown = document;\n for (const segment of ref.slice(2).split(\"/\")) {\n if (current === null || typeof current !== \"object\") return node;\n current = (current as Record<string, unknown>)[segment.replace(/~1/g, \"/\").replace(/~0/g, \"~\")];\n }\n\n return resolveRef(document, current, depth + 1);\n}\n\n/** Property names on a schema, following local refs and one level of composition. */\nfunction propertiesOf(document: OpenApiDocument, schema: unknown, depth = 0): Set<string> {\n const names = new Set<string>();\n const resolved = resolveRef(document, schema, depth) as Record<string, unknown> | null;\n\n if (!resolved || typeof resolved !== \"object\" || depth > 6) return names;\n\n const properties = resolveRef(document, resolved.properties, depth + 1);\n if (properties && typeof properties === \"object\") {\n for (const key of Object.keys(properties as Record<string, unknown>)) names.add(key);\n }\n\n // `data` / `items` wrappers are near-universal and a connector reads through\n // them, so an envelope must not read as \"the field is gone\".\n for (const key of [\"items\", \"additionalProperties\"]) {\n const nested = resolved[key];\n if (nested) for (const name of propertiesOf(document, nested, depth + 1)) names.add(name);\n }\n\n for (const key of [\"allOf\", \"oneOf\", \"anyOf\"]) {\n const list = resolved[key];\n if (Array.isArray(list)) {\n for (const entry of list) for (const name of propertiesOf(document, entry, depth + 1)) names.add(name);\n }\n }\n\n return names;\n}\n\nfunction requestSchemaOf(document: OpenApiDocument, operation: Record<string, unknown>): unknown {\n const body = resolveRef(document, operation.requestBody) as\n | { content?: Record<string, { schema?: unknown }> }\n | undefined;\n\n if (!body?.content) return undefined;\n\n for (const media of Object.values(body.content)) {\n if (media?.schema) return media.schema;\n }\n\n return undefined;\n}\n\nfunction responseSchemaOf(document: OpenApiDocument, operation: Record<string, unknown>): unknown {\n const responses = resolveRef(document, operation.responses) as Record<string, unknown> | undefined;\n if (!responses) return undefined;\n\n for (const status of Object.keys(responses)) {\n if (!/^2\\d\\d$/.test(status) && status !== \"default\") continue;\n const response = resolveRef(document, responses[status]) as\n | { content?: Record<string, { schema?: unknown }> }\n | undefined;\n if (!response?.content) continue;\n\n for (const media of Object.values(response.content)) {\n if (media?.schema) return media.schema;\n }\n }\n\n return undefined;\n}\n\n/**\n * Check a contract against an OpenAPI document already fetched by the caller.\n *\n * The FETCH is the caller's, not this function's — so the check is a pure\n * function of two documents and can be tested against a fixture with no network,\n * and so a host chooses its own HTTP client, cache and proxy. A checker that\n * fetched would also be a checker that phones out on a schedule, which is\n * exactly the thing several hosts of this package refuse to have.\n */\nexport function checkAgainstOpenApi(contract: ApiContract, document: unknown): DriftReport {\n const checkedAt = new Date().toISOString();\n const findings: DriftFinding[] = [];\n\n if (!document || typeof document !== \"object\" || !(document as OpenApiDocument).paths) {\n return {\n connector: contract.connector,\n checkedAt,\n outcome: \"unchecked\",\n method: \"openapi\",\n findings: [\n {\n kind: \"unreadable-spec\",\n detail:\n \"the document has no `paths`, so it is not an OpenAPI description we can read. Reported as UNCHECKED \" +\n \"rather than clean — a checker that cannot see is not a checker that saw nothing wrong.\",\n },\n ],\n };\n }\n\n const spec = document as OpenApiDocument;\n\n for (const operation of contract.operations) {\n const path = spec.paths?.[operation.path];\n\n if (!path) {\n findings.push({\n kind: \"missing-operation\",\n operation: operation.operation,\n detail: `${operation.method} ${operation.path} is not in the spec at all. The path moved or was removed.`,\n });\n continue;\n }\n\n const method = path[operation.method.toLowerCase()] as Record<string, unknown> | undefined;\n\n if (!method) {\n findings.push({\n kind: \"missing-operation\",\n operation: operation.operation,\n detail: `${operation.path} exists but no longer accepts ${operation.method}.`,\n });\n continue;\n }\n\n if (operation.sends?.length) {\n const accepted = propertiesOf(spec, requestSchemaOf(spec, method));\n // An empty schema means \"we could not read it\", not \"it accepts nothing\".\n if (accepted.size > 0) {\n for (const field of operation.sends) {\n if (!accepted.has(field)) {\n findings.push({\n kind: \"missing-request-field\",\n operation: operation.operation,\n detail: `sends \\`${field}\\`, which the spec no longer lists on ${operation.method} ${operation.path}.`,\n });\n }\n }\n }\n }\n\n if (operation.reads?.length) {\n const returned = propertiesOf(spec, responseSchemaOf(spec, method));\n if (returned.size > 0) {\n for (const field of operation.reads) {\n if (!returned.has(field)) {\n findings.push({\n kind: \"missing-response-field\",\n operation: operation.operation,\n detail:\n `reads \\`${field}\\`, which the spec no longer returns from ${operation.method} ${operation.path}. ` +\n \"This is the silent kind: the code keeps running and produces nothing.\",\n });\n }\n }\n }\n }\n }\n\n findings.push(...staleReviewFindings(contract, checkedAt));\n\n return {\n connector: contract.connector,\n checkedAt,\n outcome: findings.some((finding) => finding.kind !== \"stale-review\") ? \"drifted\" : \"clean\",\n method: \"openapi\",\n findings,\n };\n}\n\n/* ── The no-spec fallback: recorded shapes ───────────────────────────────── */\n\n/**\n * The field NAMES a connector saw in a real response, per operation.\n *\n * Names only. A recorded response body from a real account is a data leak\n * wearing a test fixture's clothes, and the thing being checked is the shape.\n */\nexport type RecordedShape = {\n connector: string;\n recordedOn: string;\n operations: Record<string, string[]>;\n};\n\n/**\n * The field names present in a response, flattened to dotted paths.\n *\n * Arrays collapse to their first element's shape: a response with three items\n * has one shape, and recording `items.0.id`, `items.1.id`, `items.2.id` would\n * make the fixture depend on how much data the account happened to have.\n */\nexport function shapeOf(value: unknown, prefix = \"\", depth = 0): string[] {\n if (depth > 6 || value === null || typeof value !== \"object\") return prefix ? [prefix] : [];\n\n if (Array.isArray(value)) {\n return value.length === 0 ? [`${prefix}[]`] : shapeOf(value[0], `${prefix}[]`, depth + 1);\n }\n\n const names: string[] = [];\n for (const [key, nested] of Object.entries(value as Record<string, unknown>)) {\n const path = prefix ? `${prefix}.${key}` : key;\n names.push(...shapeOf(nested, path, depth + 1));\n }\n\n return names.sort();\n}\n\n/**\n * Compare a live response's shape against what was recorded.\n *\n * **Only fields the connector READS are findings.** A provider adding fields is\n * normal and healthy; reporting it would bury the one line that matters under\n * every additive release, and a drift report nobody reads is worse than none\n * because it is evidence somebody is watching.\n */\nexport function checkAgainstRecordedShape(\n contract: ApiContract,\n operation: string,\n liveResponse: unknown,\n): DriftReport {\n const checkedAt = new Date().toISOString();\n const declared = contract.operations.find((entry) => entry.operation === operation);\n const present = new Set(shapeOf(liveResponse));\n const findings: DriftFinding[] = [];\n\n for (const field of declared?.reads ?? []) {\n // A dotted path matches its own name or any array-bearing variant of it, so\n // a connector reading `data.id` still matches `data[].id`.\n const found = [...present].some((name) => name === field || name.replace(/\\[\\]/g, \"\") === field);\n\n if (!found) {\n findings.push({\n kind: \"missing-response-field\",\n operation,\n detail:\n `reads \\`${field}\\`, which the live response no longer carries. Present instead: ` +\n `${[...present].slice(0, 20).join(\", \")}${present.size > 20 ? \", …\" : \"\"}`,\n });\n }\n }\n\n findings.push(...staleReviewFindings(contract, checkedAt));\n\n return {\n connector: contract.connector,\n checkedAt,\n outcome: findings.some((finding) => finding.kind !== \"stale-review\") ? \"drifted\" : \"clean\",\n method: \"recorded-shape\",\n findings,\n };\n}\n\n/** How long a contract may go unreviewed before that is itself a finding. */\nexport const REVIEW_STALE_DAYS = 180;\n\nfunction staleReviewFindings(contract: ApiContract, checkedAt: string): DriftFinding[] {\n const reviewed = Date.parse(contract.reviewedOn);\n if (!Number.isFinite(reviewed)) {\n return [{ kind: \"stale-review\", detail: `reviewedOn \"${contract.reviewedOn}\" is not a date.` }];\n }\n\n const days = Math.floor((Date.parse(checkedAt) - reviewed) / 86_400_000);\n\n return days > REVIEW_STALE_DAYS\n ? [\n {\n kind: \"stale-review\",\n detail:\n `nobody has read this provider's documentation in ${days} days. Not drift — an absence of looking, ` +\n \"which is the state a silent break grows in.\",\n },\n ]\n : [];\n}\n\n/**\n * A contract whose provider publishes nothing, checked as far as it can be.\n *\n * Reported as `unchecked` with the reason, never `clean`. Those are different\n * claims and the difference is the entire value of a report.\n */\nexport function unchecked(contract: ApiContract): DriftReport {\n const checkedAt = new Date().toISOString();\n\n return {\n connector: contract.connector,\n checkedAt,\n outcome: \"unchecked\",\n method: \"none\",\n findings: [\n {\n kind: \"unreadable-spec\",\n detail:\n contract.spec.kind === \"none\"\n ? contract.spec.note\n : \"no spec document was supplied to the checker, so nothing was compared.\",\n },\n ...staleReviewFindings(contract, checkedAt),\n ],\n };\n}\n","import { ConnectorConfigError, type ConnectorErrorContext } from \"./errors\";\n\n/**\n * A run, a position inside it, and how many times that position has been tried.\n *\n * Declared STRUCTURALLY rather than imported, and that is the whole point: this\n * package must be usable by a host that has never heard of a workflow engine.\n * `fancy-flow`'s `RunIdentity` class satisfies this shape exactly, so a flow\n * node passes `ctx.run` straight in with no adapter and no dependency in either\n * direction — and a host with its own notion of \"the same logical attempt\"\n * implements five members and gets the same guarantees.\n *\n * The identity is more than `(run, step)` for a reason: a step legitimately runs\n * many times in one run — once per loop iteration, once per nested invocation —\n * so the key carries the PATH that led to it. `attempt` is carried and\n * deliberately NOT part of the key, because a retry must produce the same key.\n */\nexport type RunIdentity = {\n /** Stable for the whole run: same across retries, resumes, workers and hosts. */\n readonly runKey: string;\n /** 1-based attempt of THIS logical step. Never part of the key. */\n readonly attempt: number;\n /** ISO-8601 instant of attempt 1 of this step. */\n readonly firstAttemptAt: string;\n /** The identity of one execution of one step, stable across its retries. */\n stepKey(stepId: string, occurrence?: number | null): string;\n /** May this attempt reuse the key and still be deduplicated? */\n isReplaySafe(windowSeconds: number | null | undefined, now?: Date | string): boolean;\n};\n\n/**\n * The idempotency key a writing connector sends, and when it must refuse to.\n *\n * ## What the engine now provides\n *\n * `fancy-flow` 0.46.0 carries a **run identity** on the execution context, so a\n * node can derive a key that is the same on every retry of one logical step and\n * different for every other execution of the same node.\n * `ctx.run.stepKey(ctx.node.id)` is that key. The equivalent landed in\n * `fancy-flow-php` 0.19.0 and the Python runtime, all three pinned by\n * `shared/flow-run-identity` in `@particle-academy/fancy-conformance`.\n *\n * Before that, neither engine could produce one, and both obvious substitutes\n * were worse than sending nothing:\n *\n * - **the node id alone** is stable across retries and also across RUNS, so two\n * legitimate payments share a key and the provider silently collapses the\n * second into the first — a payment that never happened, reported as success;\n * - **a fresh random value** is unique per run and also per ATTEMPT, so a retry\n * creates a second charge.\n *\n * So this used to return `null` and every writing node emitted a warning and\n * sent no key, staying pinned to one attempt. That is no longer the case where\n * a host supplies an identity — which durable runs do automatically.\n *\n * ## The part that is still a judgement call: the provider's window\n *\n * Providers forget idempotency keys. Stripe's window is **24 hours**. Past it,\n * resending the key creates a second charge and sending a fresh one creates a\n * second charge — there is no safe third option — so `idempotencyKeyFor`\n * **throws** rather than choosing. A loud stuck run that a person reconciles\n * beats a silent double write nobody ever sees.\n *\n * A first attempt is never refused, however long the run was parked: nothing\n * was sent on an earlier attempt, so there is nothing for the provider to have\n * forgotten. That is what lets an approval sit for a week and then charge.\n */\n\n/** Stripe's window, and the default for a provider that has not said otherwise. */\nexport const DEFAULT_IDEMPOTENCY_WINDOW_SECONDS = 24 * 60 * 60;\n\n/**\n * Longest key the strictest provider in the catalogue accepts.\n *\n * Stripe caps `Idempotency-Key` at 255 characters. A deep subflow path could\n * exceed that, and a provider rejecting the header is a 400 that looks like a\n * bug in the request body.\n */\nexport const MAX_IDEMPOTENCY_KEY_LENGTH = 255;\n\n/** Raised when a retry can no longer be made safe. Never retryable. */\nexport class ConnectorIdempotencyExpiredError extends ConnectorConfigError {}\n\n/** The run identity a host published, or `null`. Checked in order. */\nexport function runIdentityFrom(ctx: unknown): RunIdentity | null {\n const bag = ctx as { run?: RunIdentity } | null;\n\n return bag?.run ?? null;\n}\n\n/**\n * Where a host with no engine support may still publish a run key.\n *\n * Kept because a consumer on an older `fancy-flow` has no `ctx.run`, and\n * seeding `__runKey` into the run's initial inputs is the documented fallback.\n * It gives a per-run key with no attempt information, so the window check below\n * treats it as a first attempt — correct for a host that does not retry, and\n * the reason the engine-supplied identity is strictly better.\n */\nexport function runKeyFrom(ctx: unknown): string | null {\n const identity = runIdentityFrom(ctx);\n if (identity) return identity.runKey;\n\n const bag = ctx as Record<string, any> | null;\n const seeded = bag?.inputs?.__runKey;\n\n return typeof seeded === \"string\" && seeded.trim() !== \"\" ? seeded.trim() : null;\n}\n\nexport type IdempotencyOptions = {\n /**\n * How long the provider remembers a key. `null` means it never forgets; `0`\n * means it does not dedupe at all, so no retry may reuse one.\n */\n windowSeconds?: number | null;\n /** Distinguishes repeated executions of one node at the same level. */\n occurrence?: number | null;\n /** Injectable for tests. Never a network call. */\n now?: Date;\n /** For the error message when a key cannot be made safe. */\n context?: Partial<ConnectorErrorContext>;\n /**\n * The PROVIDER'S own limit, when it declares one — `idempotencyMaxLength` in\n * the connector index. Omitted uses {@link MAX_IDEMPOTENCY_KEY_LENGTH}, which\n * is the catalogue CEILING rather than any one provider's limit.\n *\n * Discord's `discord_message` declares 25, and for years only the ceiling was\n * applied — so a legitimate engine-derived key came back at 29 characters and\n * the connector's own validation refused it, failing the run at a node the\n * host had no way to fix.\n */\n maxLength?: number | null;\n};\n\n/**\n * The idempotency key for this execution of this node, or `null`.\n *\n * `null` means the host published no run identity at all — send no header\n * rather than inventing one, and keep the node `unsafe-to-replay`.\n *\n * **Throws** `ConnectorIdempotencyExpiredError` when this is a RETRY and the\n * provider's window has elapsed. That is not a defensive check; it is the only\n * correct answer, because both alternatives write twice.\n */\nexport function idempotencyKeyFor(\n ctx: unknown,\n nodeId: string,\n options: IdempotencyOptions = {},\n): string | null {\n const identity = runIdentityFrom(ctx);\n const windowSeconds =\n options.windowSeconds === undefined ? DEFAULT_IDEMPOTENCY_WINDOW_SECONDS : options.windowSeconds;\n\n if (!identity) {\n // The legacy seeded-key path. No attempt information exists, so there is\n // nothing to window-check; a host on this path is one that does not retry.\n const runKey = runKeyFrom(ctx);\n return runKey === null ? null : fit(`${runKey}:${nodeId}`, options.maxLength);\n }\n\n if (!identity.isReplaySafe(windowSeconds, options.now ?? new Date())) {\n throw new ConnectorIdempotencyExpiredError(\n `attempt ${identity.attempt} of this step began at ${identity.firstAttemptAt}, which is outside ` +\n `the provider's ${windowSeconds}s idempotency window. The provider has forgotten the original ` +\n `key, so resending it and sending a new one would BOTH write twice. Refusing, so a person can ` +\n `reconcile — check whether the first attempt landed before re-running this workflow.`,\n {\n service: options.context?.service ?? \"\",\n operation: options.context?.operation ?? \"\",\n ...options.context,\n },\n );\n }\n\n return fit(identity.stepKey(nodeId, options.occurrence), options.maxLength);\n}\n\n/**\n * The warning a node emits when it is about to write without a key.\n *\n * Said out loud, at the moment it matters, because the failure it precedes is\n * invisible: the run succeeds, the retry succeeds, and there are two charges.\n */\nexport const NO_IDEMPOTENCY_KEY_WARNING =\n \"no run identity available, so this write is being sent WITHOUT an idempotency key. \" +\n \"A retry would repeat the effect rather than recover from it — the node is marked \" +\n \"unsafe-to-replay, so a durable runner will give it one attempt. Durable runs supply an \" +\n \"identity automatically; for a synchronous run, pass `run` in RunOptions.\";\n\n/**\n * Shorten an over-long key deterministically.\n *\n * The hash has to be stable across attempts AND across runtimes, so it is a\n * plain FNV-1a over the key rather than anything host-provided. The prefix is\n * kept so a key remains greppable against a run.\n */\nfunction fit(key: string, maxLength?: number | null): string {\n // The SMALLER of the provider's limit and the catalogue ceiling. A descriptor\n // claiming more than any provider accepts is one to distrust, not obey.\n const limit =\n maxLength === undefined || maxLength === null\n ? MAX_IDEMPOTENCY_KEY_LENGTH\n : Math.max(1, Math.min(maxLength, MAX_IDEMPOTENCY_KEY_LENGTH));\n\n if (key.length <= limit) return key;\n\n const digest = fnv1a(key);\n\n // A limit too small to carry `<head>~<digest>` gets the digest alone. That\n // loses the greppable prefix, which is a real cost — but a key that is hard\n // to trace still dedupes, while a negative slice length produces nonsense.\n if (limit <= digest.length + 1) return digest.slice(0, limit);\n\n const head = key.slice(0, limit - digest.length - 1);\n\n return `${head}~${digest}`;\n}\n\nfunction fnv1a(value: string): string {\n let hash = 0x811c9dc5;\n for (let i = 0; i < value.length; i++) {\n hash ^= value.charCodeAt(i);\n // 32-bit FNV prime, written as shifts so it stays exact in a double.\n hash = (hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24))) >>> 0;\n }\n\n return hash.toString(16).padStart(8, \"0\");\n}\n","/**\n * A subscription that EXPIRES, and when the host must act on it.\n *\n * `DeliveryMechanism` has said since 0.1.0 that a `subscription` trigger is a\n * webhook somebody has to renew, forever, and that if nobody does the workflow\n * stops firing with no error anywhere. This is the value for that duty — ONE\n * shape every expiring trigger declares, so a host runs one renewal scheduler\n * rather than learning a loop per connector, each with its own boundary bugs.\n *\n * Three facts and two verbs:\n *\n * - `expiresAt` — the provider's expiry, as an RFC 3339 instant. Google Calendar\n * hands back `expiration` as epoch MILLISECONDS in a string and Microsoft\n * Graph hands back `expirationDateTime` as ISO 8601; the CONNECTOR converts on\n * the way in, and this module refuses anything that is not an instant rather\n * than guessing units.\n * - `renewBeforeSeconds` — the connector's margin. Positive, because zero would\n * make `due` unreachable (the moment it applied, `expired` would win) — a\n * lease nobody ever renews, declared in a way nothing would report.\n * - `renewOperation` — the operation the host calls when the lease is due. For\n * Graph that is a renew; for a Google Calendar channel, which cannot be\n * renewed, it is the create again (the connector stops the old channel\n * itself). The lease says WHEN; the connector says WHAT.\n *\n * - `leaseState` — where the lease IS: `active`, `due` (inclusive at renewAt),\n * `expired` (inclusive at expiresAt, and it wins over `due`: a lease that has\n * just expired cannot be renewed).\n * - `leaseAction` — what the host DOES: `none`, `renew`, or `resync`. A missed\n * lease is never a quiet re-create: notifications during the gap are gone, so\n * the host must re-list (sync token or full) AND re-subscribe.\n *\n * The boundaries are DECIDED, not measured, and they are pinned in\n * fancy-conformance's `shared/subscription-lease` suite, which drives the PHP\n * twin too (`tests/subscription-lease.test.ts` and its Pest mirror).\n */\nimport { ConnectorConfigError, type ConnectorErrorContext } from \"./errors\";\n\nexport type SubscriptionLease = {\n /** The provider's expiry, an RFC 3339 instant. */\n expiresAt: string;\n /** How early the host renews, in seconds. Positive. */\n renewBeforeSeconds: number;\n /** The operation the host calls when the lease is due. */\n renewOperation: string;\n};\n\nexport type LeaseState = \"active\" | \"due\" | \"expired\";\n\nexport type LeaseAction = \"none\" | \"renew\" | \"resync\";\n\n/**\n * How a provider spells its expiry. Two, because two providers: Google\n * Calendar's channel `expiration` is epoch MILLISECONDS (as a JSON string, and\n * a number is accepted too); Microsoft Graph's `expirationDateTime` is ISO\n * 8601 with a seven-digit fraction. A unit no shipped provider spells is not\n * here — a vocabulary with no provider is a claim that outruns the code.\n *\n * Data as well as a type: a host validates a definition at the JSON boundary,\n * where `tsc` cannot see.\n */\nexport const EXPIRES_AT_UNITS = [\"rfc3339\", \"epoch-ms\"] as const;\n\nexport type ExpiresAtUnit = (typeof EXPIRES_AT_UNITS)[number];\n\n/**\n * What a subscription trigger DECLARES about its lease, so a host can build\n * the value from the provider's create (or renew) response without knowing\n * the provider.\n */\nexport type LeaseDeclaration = {\n /** Dotted path to the expiry in the create/renew response, e.g. `expiration` or `channel.expiration`. */\n expiresAtFrom: string;\n expiresAtUnit: ExpiresAtUnit;\n /** How early the host renews, in seconds. Positive. */\n renewBeforeSeconds: number;\n /** The operation the host calls when the lease is due. */\n renewOperation: string;\n};\n\n/** RFC 3339: date, `T`, time, optional fraction, `Z` or a numeric offset. */\nconst INSTANT = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d{1,9})?(?:Z|[+-]\\d{2}:\\d{2})$/;\n\nconst DEFAULT_CONTEXT: ConnectorErrorContext = { service: \"\", operation: \"subscription\" };\n\nfunction instantMs(value: unknown, field: string, at: ConnectorErrorContext): number {\n if (typeof value !== \"string\" || !INSTANT.test(value)) {\n throw new ConnectorConfigError(\n `${field} must be an RFC 3339 instant such as \"2026-09-22T00:00:00Z\", got ${JSON.stringify(value)}. ` +\n \"A provider's own shape — Google's epoch milliseconds, say — is converted by the connector, never guessed at here.\",\n at,\n );\n }\n // Millisecond precision, TRUNCATED. Graph writes seven fractional digits;\n // the lease keeps three in both runtimes, so a boundary cannot fall between\n // them, and an expiry a fraction early is the safe direction.\n const ms = Date.parse(value.replace(/(\\.\\d{3})\\d+/, \"$1\"));\n if (Number.isNaN(ms)) {\n throw new ConnectorConfigError(`${field} is not a real instant: ${JSON.stringify(value)}`, at);\n }\n return ms;\n}\n\n/**\n * Build the lease from the provider's create (or renew) response, by\n * declaration. The declared unit is never guessed around: digits under\n * `rfc3339`, or an instant under `epoch-ms`, are refused — a lease built from\n * a misread expiry is one that expires unannounced.\n *\n * The expiry is stored as an RFC 3339 instant in UTC at millisecond\n * precision; the lease's own refusals (margin, operation) still apply.\n */\nexport function leaseFromResponse(\n declaration: LeaseDeclaration,\n response: unknown,\n at: ConnectorErrorContext = DEFAULT_CONTEXT,\n): SubscriptionLease {\n const { expiresAtFrom: path, expiresAtUnit: unit } = declaration;\n\n if (!(EXPIRES_AT_UNITS as readonly string[]).includes(unit)) {\n throw new ConnectorConfigError(\n `expiresAtUnit must be one of ${EXPIRES_AT_UNITS.join(\" | \")}, got ${JSON.stringify(unit)}.`,\n at,\n );\n }\n\n const value = readPath(response, path);\n if (value === undefined || value === null) {\n throw new ConnectorConfigError(\n `${path} is not present in the response, so no lease can be built — a subscription with no known expiry is one nobody renews.`,\n at,\n );\n }\n\n let expiresAt: string;\n if (unit === \"epoch-ms\") {\n const ms =\n typeof value === \"number\" ? value\n : typeof value === \"string\" && /^\\d+$/.test(value) ? Number(value)\n : Number.NaN;\n if (!Number.isSafeInteger(ms)) {\n throw new ConnectorConfigError(\n `${path} must be epoch milliseconds as digits under \"epoch-ms\", got ${JSON.stringify(value)}.`,\n at,\n );\n }\n expiresAt = new Date(ms).toISOString();\n } else {\n if (typeof value !== \"string\" || !INSTANT.test(value)) {\n throw new ConnectorConfigError(\n `${path} must be an RFC 3339 instant under \"rfc3339\", got ${JSON.stringify(value)}.`,\n at,\n );\n }\n expiresAt = new Date(instantMs(value, path, at)).toISOString();\n }\n\n return subscriptionLease(\n { expiresAt, renewBeforeSeconds: declaration.renewBeforeSeconds, renewOperation: declaration.renewOperation },\n at,\n );\n}\n\n/** Walk a dotted path into a parsed response. Absent anywhere along it is `undefined`. */\nfunction readPath(value: unknown, path: string): unknown {\n let current = value;\n for (const segment of path.split(\".\")) {\n if (current === null || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[segment];\n }\n return current;\n}\n\n/**\n * Validate a declaration and return the lease — a plain value a host stores\n * beside the trigger and hands back to `leaseState` / `leaseAction` later.\n */\nexport function subscriptionLease(\n input: { expiresAt: string; renewBeforeSeconds: number; renewOperation: string },\n at: ConnectorErrorContext = DEFAULT_CONTEXT,\n): SubscriptionLease {\n instantMs(input.expiresAt, \"expiresAt\", at);\n\n if (!Number.isInteger(input.renewBeforeSeconds) || input.renewBeforeSeconds <= 0) {\n throw new ConnectorConfigError(\n `renewBeforeSeconds must be a positive whole number of seconds, got ${JSON.stringify(input.renewBeforeSeconds)}. ` +\n \"Zero would make the lease due only at the instant it expires, which is a lease nobody renews.\",\n at,\n );\n }\n\n if (typeof input.renewOperation !== \"string\" || input.renewOperation.trim() === \"\") {\n throw new ConnectorConfigError(\n \"renewOperation must name the operation the host calls when the lease is due; a due lease with nothing to call is one nobody renews.\",\n at,\n );\n }\n\n return { expiresAt: input.expiresAt, renewBeforeSeconds: input.renewBeforeSeconds, renewOperation: input.renewOperation };\n}\n\n/** The instant the lease becomes due: `expiresAt - renewBeforeSeconds`. */\nexport function leaseRenewAt(lease: SubscriptionLease): string {\n return new Date(instantMs(lease.expiresAt, \"expiresAt\", DEFAULT_CONTEXT) - lease.renewBeforeSeconds * 1000).toISOString();\n}\n\n/** Where the lease is at `now`. Both boundaries are inclusive; `expired` wins. */\nexport function leaseState(lease: SubscriptionLease, now: string | Date): LeaseState {\n const at = now instanceof Date ? now.getTime() : instantMs(now, \"now\", DEFAULT_CONTEXT);\n const expires = instantMs(lease.expiresAt, \"expiresAt\", DEFAULT_CONTEXT);\n\n if (at >= expires) return \"expired\";\n if (at >= expires - lease.renewBeforeSeconds * 1000) return \"due\";\n return \"active\";\n}\n\n/** What the host does about the lease at `now`. */\nexport function leaseAction(lease: SubscriptionLease, now: string | Date): LeaseAction {\n switch (leaseState(lease, now)) {\n case \"active\":\n return \"none\";\n case \"due\":\n return \"renew\";\n case \"expired\":\n return \"resync\";\n }\n}\n","/**\n * Making a declared metric shape checkable against the code that produces it.\n *\n * ## The bug this exists to catch, which shipped for weeks\n *\n * Two connectors in the reference implementation declared\n * `capabilities.metrics: true` while their metric fetch returned `[]`. A pull\n * therefore did not skip them — it asked, got nothing, and reported nothing,\n * which on a dashboard is indistinguishable from *\"we asked and nobody\n * engaged\"*. A capability flag had quietly turned an unimplemented feature into\n * a reported zero, on the one surface whose entire job is not making claims it\n * cannot stand behind.\n *\n * A declared shape and the code that produces the numbers are the classic pair\n * that agrees on the day it is written and drifts silently after. So the mapping\n * is extracted as a **pure function per connector**, and these helpers feed it a\n * synthetic response and compare the keys it returns against the keys declared.\n * **No credentials, no network.**\n *\n * ## And the rule underneath all of it\n *\n * **Absent stays absent.** A count the provider did not send is omitted, never\n * reported as zero. A zero says \"nothing happened\"; an absence says \"we don't\n * know\". A measurement surface that confuses those is worthless, and the\n * confusion is one `?? 0` away.\n */\n\nimport { sandboxIsSelectable } from \"./mode\";\nimport type { Connector, MetricDescriptor, ProviderAdapter } from \"./seam\";\n\nexport type ShapeMismatch = {\n connector: string;\n /** Keys the mapping produced that nothing declared. */\n undeclared: string[];\n /** Keys declared that the mapping never produces. */\n unproduced: string[];\n};\n\n/**\n * Compare a declared shape against what a mapping actually returns.\n *\n * `produced` is the output of the connector's pure response→metrics function\n * given a synthetic response with EVERY field populated. Anything the mapping\n * can emit therefore appears, and anything it cannot is caught as a declaration\n * nobody can honour.\n */\nexport function compareShape(\n connectorId: string,\n declared: MetricDescriptor[] | undefined,\n produced: Record<string, number>,\n): ShapeMismatch | null {\n const declaredKeys = new Set((declared ?? []).map((metric) => metric.key));\n const producedKeys = new Set(Object.keys(produced));\n\n const undeclared = [...producedKeys].filter((key) => !declaredKeys.has(key)).sort();\n const unproduced = [...declaredKeys].filter((key) => !producedKeys.has(key)).sort();\n\n if (undeclared.length === 0 && unproduced.length === 0) return null;\n\n return { connector: connectorId, undeclared, unproduced };\n}\n\n/**\n * Every way a capability flag can outrun the code, as findings.\n *\n * Returns strings rather than throwing so a host can report all of them at once.\n * A check that stops at the first problem trains people to fix one thing and\n * re-run, which is how a list of six becomes six rounds.\n */\nexport function capabilityProblems(connector: Connector<never>): string[] {\n const problems: string[] = [];\n\n if (connector.capabilities.metrics) {\n if (!connector.metricShape || connector.metricShape.length === 0) {\n problems.push(\n `${connector.id} claims capabilities.metrics but declares no metricShape. A pull will therefore ask it, ` +\n \"get nothing, and report nothing — which reads as \\\"nobody engaged\\\" rather than \\\"not implemented\\\".\",\n );\n }\n if (!connector.fetchMetrics) {\n problems.push(`${connector.id} claims capabilities.metrics but has no fetchMetrics.`);\n }\n }\n\n if (!connector.capabilities.metrics && connector.metricShape) {\n problems.push(\n `${connector.id} declares a metricShape but capabilities.metrics is false. One of the two is wrong, and ` +\n \"the shape is the more believable half.\",\n );\n }\n\n if (connector.capabilities.feedback && !connector.fetchFeedback) {\n problems.push(`${connector.id} claims capabilities.feedback but has no fetchFeedback.`);\n }\n\n if (!connector.capabilities.feedback && connector.fetchFeedback) {\n problems.push(\n `${connector.id} implements fetchFeedback but does not claim the capability, so nothing will ever call it.`,\n );\n }\n\n for (const metric of connector.metricShape ?? []) {\n if (metric.means.trim().length < 10) {\n problems.push(`${connector.id}.${metric.key} does not say what it means.`);\n }\n }\n\n if (connector.delivery.idempotent && connector.delivery.why.trim().length < 10) {\n problems.push(\n `${connector.id} declares idempotent: true with no reason. That is the one claim whose failure is a public ` +\n \"duplicate, so it has to name the mechanism rather than restate the flag.\",\n );\n }\n\n if (connector.delivery.minIntervalMs > 0 && !connector.delivery.citation && connector.delivery.rateSource === \"documented\") {\n problems.push(\n `${connector.id} calls its rate limit \"documented\" but cites nothing. A number nobody can source gets quoted ` +\n \"as a platform fact; label it self-imposed or cite it.\",\n );\n }\n\n return problems;\n}\n\n/**\n * Drop keys whose value the provider did not actually send.\n *\n * The one function a connector's mapping should build on, so *absent stays\n * absent* is a property of the code rather than a habit. `0` survives; `null`,\n * `undefined` and non-numbers do not.\n */\nexport function reported(values: Record<string, unknown>): Record<string, number> {\n const out: Record<string, number> = {};\n\n for (const [key, value] of Object.entries(values)) {\n if (typeof value === \"number\" && Number.isFinite(value)) out[key] = value;\n }\n\n return out;\n}\n\n/**\n * Every way a PROVIDER's declaration can outrun what anyone actually checked.\n *\n * The sibling of `capabilityProblems`, one level up. `capabilities.metrics: true`\n * with no shape turns an unimplemented feature into a reported zero; the\n * equivalent here turns *nobody looked* into a statement about a test estate —\n * and that field is the one where being wrong sends a person to a live estate\n * believing it is a test one.\n *\n * Strings rather than a throw, so a host reports all of them at once. A check\n * that stops at the first problem trains people to fix one thing and re-run,\n * which is how a list of six becomes six rounds.\n */\nexport function providerProblems(provider: ProviderAdapter): string[] {\n const problems: string[] = [];\n\n if (provider.implemented && provider.sandbox === \"unverified\") {\n problems.push(\n `${provider.id} says it is implemented while its sandbox shape is still \"unverified\". Something a person ` +\n \"can run today must not be ambiguous about which estate it reaches — verify it, or set implemented: false.\",\n );\n }\n\n if (provider.implemented && provider.fields.length === 0) {\n problems.push(`${provider.id} says it is implemented but declares no credential fields.`);\n }\n\n if (provider.implemented && provider.setup.length === 0) {\n problems.push(\n `${provider.id} says it is implemented and names no setup steps. The code is never the expensive part; ` +\n \"the setup is, and a provider whose setup is undocumented is one nobody can stand up.\",\n );\n }\n\n for (const field of provider.fields) {\n if (field.help.trim().length < 10) {\n problems.push(`${provider.id}.${field.key} has no help text worth reading.`);\n }\n }\n\n // A verify that proves less than it appears to is worse than no verify, so a\n // provider that ships one has to say what it does NOT cover.\n if (provider.verify && !provider.proves) {\n problems.push(\n `${provider.id} ships a verify but does not say what it PROVES. A green tick that means more than it ` +\n \"should is worse than no tick — name the step it does not cover.\",\n );\n }\n\n if (provider.sandbox === \"restricted-reach\" && !provider.summary.toLowerCase().includes(\"reach\")) {\n problems.push(\n `${provider.id} is declared restricted-reach and its summary never mentions reach. That shape looks ` +\n \"exactly like a successful post nobody can see, so the surface has to say so before anyone runs it.\",\n );\n }\n\n return problems;\n}\n\n/** True when a provider's declared sandbox can actually be selected at run time. */\nexport function providerSandboxIsSelectable(provider: ProviderAdapter): boolean {\n return sandboxIsSelectable(provider.sandbox);\n}\n","/**\n * Raw MIME in, headers / parts / attachments out — the payload TRANSFORM an\n * inbound-email trigger declares.\n *\n * Written from scratch (the owner: no third-party code), as ONE implementation\n * in two runtimes — `php/src/Mime.php` is the twin — and held to the authored\n * corpus under `fixtures/mime/`, which both must reproduce byte for byte.\n * `fixtures/mime/README.md` lists every decision a case pins; the ones that\n * are easy to get differently in two languages are restated here:\n *\n * - Header lines are read as Latin-1 (one byte, one char), unfolded with a\n * single space, names lowercased, values RFC 2047-decoded (B and Q; adjacent\n * encoded-words join without the whitespace between them).\n * - The line break before a boundary delimiter belongs to the DELIMITER.\n * - `size` is the transfer-decoded byte length, before any charset conversion.\n * - A text part comes back as UTF-8 with the message's own line endings. A\n * charset this module does not decode (anything but utf-8, us-ascii,\n * iso-8859-1, iso-8859-15, windows-1252) leaves the part with no text.\n * - Every non-text leaf is an attachment, as is a text leaf declared\n * `attachment`; attachments carry their bytes as base64. `filename*` (RFC\n * 2231) wins over `filename`, which wins over Content-Type's `name`.\n * - `messageId` is the header verbatim; `contentId` has its angle brackets\n * stripped, because `cid:` references omit them.\n */\n\nexport type MimeHeader = { name: string; value: string };\n\nexport type MimePart = {\n index: number;\n contentType: string;\n charset: string | null;\n disposition: \"inline\" | \"attachment\" | null;\n filename: string | null;\n contentId: string | null;\n size: number;\n headers: MimeHeader[];\n};\n\nexport type MimeAttachment = MimePart & { content: string };\n\nexport type MimeMessage = {\n headers: MimeHeader[];\n subject: string | null;\n messageId: string | null;\n text: string | null;\n html: string | null;\n parts: MimePart[];\n attachments: MimeAttachment[];\n};\n\n/* ── Bytes ───────────────────────────────────────────────────────────────── */\n\nconst CR = 13;\nconst LF = 10;\n\nfunction latin1(bytes: Uint8Array): string {\n let s = \"\";\n for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]!);\n return s;\n}\n\nfunction bytesOfLatin1(s: string): Uint8Array {\n const out = new Uint8Array(s.length);\n for (let i = 0; i < s.length; i++) out[i] = s.charCodeAt(i) & 0xff;\n return out;\n}\n\n/** Where the header block ends: the first blank line, CRLF or LF. Returns [headerEnd, bodyStart]. */\nfunction splitEntity(bytes: Uint8Array): [number, number] {\n for (let i = 0; i < bytes.length; i++) {\n if (bytes[i] === LF) {\n if (bytes[i + 1] === LF) return [i, i + 2];\n if (bytes[i + 1] === CR && bytes[i + 2] === LF) return [i, i + 3];\n }\n }\n return [bytes.length, bytes.length];\n}\n\n/* ── Headers ─────────────────────────────────────────────────────────────── */\n\nfunction parseHeaders(block: string): MimeHeader[] {\n const lines = block.split(/\\r?\\n/);\n const unfolded: string[] = [];\n for (const line of lines) {\n if (line === \"\") continue;\n if ((line.startsWith(\" \") || line.startsWith(\"\\t\")) && unfolded.length > 0) {\n unfolded[unfolded.length - 1] += \" \" + line.trim();\n } else {\n unfolded.push(line);\n }\n }\n const headers: MimeHeader[] = [];\n for (const line of unfolded) {\n const colon = line.indexOf(\":\");\n if (colon <= 0) continue;\n headers.push({ name: line.slice(0, colon).trim().toLowerCase(), value: decodeWords(line.slice(colon + 1).trim()) });\n }\n return headers;\n}\n\nfunction header(headers: MimeHeader[], name: string): string | null {\n return headers.find((h) => h.name === name)?.value ?? null;\n}\n\n/* ── RFC 2047 encoded-words ──────────────────────────────────────────────── */\n\nconst ENCODED_WORD = /=\\?([^?\\s]+)\\?([BbQq])\\?([^?\\s]*)\\?=/g;\n\nfunction decodeWords(value: string): string {\n // Adjacent encoded-words are one token: the whitespace between them is not\n // part of the text (RFC 2047 §6.2).\n const joined = value.replace(/(\\?=)[ \\t\\r\\n]+(=\\?)/g, \"$1$2\");\n return joined.replace(ENCODED_WORD, (whole, charset: string, encoding: string, text: string) => {\n const bytes =\n encoding.toUpperCase() === \"B\"\n ? base64Bytes(text)\n : bytesOfLatin1(text.replace(/_/g, \" \").replace(/=([0-9A-Fa-f]{2})/g, (_m, h: string) => String.fromCharCode(parseInt(h, 16))));\n const decoded = decodeCharset(bytes, charset);\n return decoded ?? whole;\n });\n}\n\n/* ── Parameters (Content-Type, Content-Disposition), with RFC 2231 ───────── */\n\nfunction parseParams(value: string): { main: string; params: Record<string, string> } {\n const [head, ...rest] = value.split(\";\");\n const params: Record<string, string> = {};\n const continued: Record<string, string[]> = {};\n for (const piece of rest) {\n const eq = piece.indexOf(\"=\");\n if (eq < 0) continue;\n let key = piece.slice(0, eq).trim().toLowerCase();\n let raw = piece.slice(eq + 1).trim();\n if (raw.startsWith('\"') && raw.endsWith('\"') && raw.length >= 2) raw = raw.slice(1, -1).replace(/\\\\(.)/g, \"$1\");\n\n // RFC 2231: `name*=charset'lang'percent-encoded`, and `name*0*=`, `name*1=`\n // continuations. A starred key carries an encoding; a numbered one is a\n // segment.\n const segment = /^(.+?)\\*(\\d+)(\\*)?$/.exec(key);\n if (segment) {\n const [, base, n, star] = segment;\n (continued[base!] ??= [])[Number(n)] = star ? decode2231(raw, Number(n) === 0) : raw;\n continue;\n }\n if (key.endsWith(\"*\")) {\n key = key.slice(0, -1);\n params[key] = decode2231(raw, true);\n continue;\n }\n params[key] ??= raw;\n }\n for (const [base, parts] of Object.entries(continued)) params[base] = parts.join(\"\");\n return { main: (head ?? \"\").trim().toLowerCase(), params };\n}\n\nlet pendingCharset2231 = \"utf-8\";\n\nfunction decode2231(raw: string, first: boolean): string {\n let text = raw;\n if (first) {\n const m = /^([^']*)'[^']*'(.*)$/s.exec(raw);\n if (m) {\n pendingCharset2231 = m[1] || \"utf-8\";\n text = m[2]!;\n }\n }\n const bytes = bytesOfLatin1(text.replace(/%([0-9A-Fa-f]{2})/g, (_m, h: string) => String.fromCharCode(parseInt(h, 16))));\n return decodeCharset(bytes, pendingCharset2231) ?? text;\n}\n\n/* ── Transfer encodings and charsets ─────────────────────────────────────── */\n\nfunction base64Bytes(text: string): Uint8Array {\n return new Uint8Array(Buffer.from(text.replace(/[^A-Za-z0-9+/=]/g, \"\"), \"base64\"));\n}\n\nfunction quotedPrintableBytes(bytes: Uint8Array): Uint8Array {\n const out: number[] = [];\n for (let i = 0; i < bytes.length; i++) {\n const b = bytes[i]!;\n if (b !== 0x3d /* = */) {\n out.push(b);\n continue;\n }\n const a = bytes[i + 1];\n const c = bytes[i + 2];\n // Soft line break: `=` then CRLF or LF.\n if (a === LF) {\n i += 1;\n continue;\n }\n if (a === CR && c === LF) {\n i += 2;\n continue;\n }\n if (a !== undefined && c !== undefined && isHex(a) && isHex(c)) {\n out.push(parseInt(String.fromCharCode(a, c), 16));\n i += 2;\n continue;\n }\n out.push(b);\n }\n return new Uint8Array(out);\n}\n\nfunction isHex(b: number): boolean {\n return (b >= 0x30 && b <= 0x39) || (b >= 0x41 && b <= 0x46) || (b >= 0x61 && b <= 0x66);\n}\n\nfunction transferDecode(body: Uint8Array, encoding: string | null): Uint8Array {\n switch ((encoding ?? \"7bit\").trim().toLowerCase()) {\n case \"base64\":\n return base64Bytes(latin1(body));\n case \"quoted-printable\":\n return quotedPrintableBytes(body);\n default:\n return body;\n }\n}\n\n/** UTF-8 text for a charset this module decodes; null for one it does not. */\nfunction decodeCharset(bytes: Uint8Array, charset: string): string | null {\n switch (charset.trim().toLowerCase()) {\n case \"utf-8\":\n case \"utf8\":\n return new TextDecoder(\"utf-8\").decode(bytes);\n case \"us-ascii\":\n case \"ascii\":\n case \"iso-8859-1\":\n case \"latin1\":\n // Node's `latin1` is true ISO-8859-1 (0x80–0x9F are C1 controls); the\n // WHATWG TextDecoder would silently give windows-1252 instead, which\n // the PHP twin does not.\n return Buffer.from(bytes).toString(\"latin1\");\n case \"windows-1252\":\n case \"cp1252\":\n return new TextDecoder(\"windows-1252\").decode(bytes);\n case \"iso-8859-15\":\n case \"latin9\":\n return new TextDecoder(\"iso-8859-15\").decode(bytes);\n default:\n return null;\n }\n}\n\n/* ── Entities ────────────────────────────────────────────────────────────── */\n\ntype Collected = { parts: MimePart[]; attachments: MimeAttachment[]; text: string | null; html: string | null };\n\nfunction walk(bytes: Uint8Array, entityHeaders: MimeHeader[] | null, acc: Collected): void {\n const [headerEnd, bodyStart] = splitEntity(bytes);\n const headers = entityHeaders ?? parseHeaders(latin1(bytes.subarray(0, headerEnd)));\n const body = bytes.subarray(entityHeaders ? 0 : bodyStart);\n\n const type = parseParams(header(headers, \"content-type\") ?? \"text/plain\");\n\n if (type.main.startsWith(\"multipart/\") && type.params.boundary) {\n for (const child of splitMultipart(body, type.params.boundary)) walk(child, null, acc);\n return;\n }\n\n const disposition = parseParams(header(headers, \"content-disposition\") ?? \"\");\n const decoded = transferDecode(body, header(headers, \"content-transfer-encoding\"));\n const declaredDisposition = disposition.main === \"attachment\" || disposition.main === \"inline\" ? disposition.main : null;\n const filename = disposition.params.filename ?? type.params.name ?? null;\n const contentId = header(headers, \"content-id\")?.replace(/^<|>$/g, \"\") ?? null;\n const isText = type.main.startsWith(\"text/\");\n const isAttachment = declaredDisposition === \"attachment\" || !isText;\n\n const part: MimePart = {\n index: acc.parts.length,\n contentType: type.main,\n charset: type.params.charset?.toLowerCase() ?? null,\n disposition: declaredDisposition,\n filename,\n contentId,\n size: decoded.length,\n headers,\n };\n acc.parts.push(part);\n\n if (isAttachment) {\n acc.attachments.push({ ...part, content: Buffer.from(decoded).toString(\"base64\") });\n return;\n }\n\n const text = decodeCharset(decoded, type.params.charset ?? \"utf-8\");\n if (text === null) return;\n if (type.main === \"text/plain\" && acc.text === null) acc.text = text;\n if (type.main === \"text/html\" && acc.html === null) acc.html = text;\n}\n\n/** The bodies between delimiters — the CRLF before each delimiter is the delimiter's. */\nfunction splitMultipart(body: Uint8Array, boundary: string): Uint8Array[] {\n const delimiter = bytesOfLatin1(`--${boundary}`);\n const children: Uint8Array[] = [];\n let partStart = -1;\n\n for (let i = 0; i <= body.length - delimiter.length; i++) {\n if (i !== 0 && body[i - 1] !== LF) continue;\n if (!startsWith(body, i, delimiter)) continue;\n\n // The preceding line break belongs to the delimiter.\n let partEnd = i;\n if (partEnd > 0 && body[partEnd - 1] === LF) partEnd -= 1;\n if (partEnd > 0 && body[partEnd - 1] === CR) partEnd -= 1;\n if (partStart >= 0) children.push(body.subarray(partStart, partEnd));\n\n let j = i + delimiter.length;\n const closing = body[j] === 0x2d && body[j + 1] === 0x2d;\n if (closing) return children;\n // Skip transport padding and the delimiter's own line ending.\n while (j < body.length && body[j] !== LF) j++;\n partStart = j + 1;\n i = j;\n }\n if (partStart >= 0 && partStart <= body.length) children.push(body.subarray(partStart));\n return children;\n}\n\nfunction startsWith(haystack: Uint8Array, at: number, needle: Uint8Array): boolean {\n for (let k = 0; k < needle.length; k++) if (haystack[at + k] !== needle[k]) return false;\n return true;\n}\n\n/* ── The entry point ─────────────────────────────────────────────────────── */\n\nexport function parseMime(raw: Uint8Array | string): MimeMessage {\n const bytes = typeof raw === \"string\" ? new Uint8Array(Buffer.from(raw, \"utf8\")) : raw;\n const [headerEnd, bodyStart] = splitEntity(bytes);\n const headers = parseHeaders(latin1(bytes.subarray(0, headerEnd)));\n const body = bytes.subarray(bodyStart);\n\n const acc: Collected = { parts: [], attachments: [], text: null, html: null };\n const type = parseParams(header(headers, \"content-type\") ?? \"text/plain\");\n if (type.main.startsWith(\"multipart/\") && type.params.boundary) {\n for (const child of splitMultipart(body, type.params.boundary)) walk(child, null, acc);\n } else {\n // A non-multipart message is one part whose headers are the message's\n // content-* headers.\n walk(body, headers.filter((h) => h.name.startsWith(\"content-\")), acc);\n }\n\n return {\n headers,\n subject: header(headers, \"subject\"),\n messageId: header(headers, \"message-id\"),\n text: acc.text,\n html: acc.html,\n parts: acc.parts,\n attachments: acc.attachments,\n };\n}\n","/**\n * Dry-verify — proving the transport with a credential that cannot work.\n *\n * ## The discipline, and why it beats a fake server\n *\n * A connector written from documentation can be wrong in three ways that look\n * identical until a real credential arrives:\n *\n * 1. the URL is wrong,\n * 2. the request shape is wrong,\n * 3. the error handling never runs.\n *\n * **All three are invisible to a test with a fake server**, because a fake\n * server agrees with whatever the code does. That is the single most important\n * thing this module encodes: a green unit suite against a mock proves the code\n * is consistent with itself.\n *\n * So a probe calls the REAL API with a deliberately invalid credential and\n * requires an **auth-shaped refusal**. That proves the host resolved, the path\n * exists, the method was accepted, and a failure was recognised as a failure.\n *\n * ## What counts as a pass, precisely\n *\n * An auth-shaped refusal is `401`, `403`, or a `404` on a provider that answers\n * 404 for an unknown credential-in-the-path (a Discord webhook does exactly\n * this). The provider declares which, because guessing here would let a genuine\n * \"this endpoint moved\" 404 read as success — the one outcome a probe exists to\n * catch.\n *\n * ## Offline is SKIPPED, never failed\n *\n * A check that goes red on a train gets ignored, and then it is worth nothing\n * when it goes red for real. Unreachable is reported as skipped with the reason,\n * and the assertions that need no network still run.\n *\n * ## And nothing is ever sent\n *\n * Every probe is a READ. A probe that wrote something would be a send, and sends\n * are the host's to gate.\n */\n\nimport { classifyError, classifyStatus, type FailureKind } from \"./delivery\";\n\nexport type ProbeOutcome = \"pass\" | \"fail\" | \"skip\";\n\nexport type ProbeResult = {\n connector: string;\n outcome: ProbeOutcome;\n /** What happened, in the provider's own words where possible. */\n detail: string;\n status?: number;\n kind?: FailureKind;\n};\n\nexport type ProbeSpec = {\n /** Which connector this probes. */\n connector: string;\n /**\n * The read-only request, fully formed, carrying a credential that cannot be\n * valid. Built by the connector so the probe exercises the connector's own URL\n * and auth placement rather than a second copy of them.\n */\n request: () => Promise<{ status: number; body: string }>;\n /**\n * Statuses that count as an auth-shaped refusal for THIS provider.\n *\n * Declared rather than assumed. Discord answers `404` for an unknown webhook\n * id, and on a provider that does not, a 404 means the endpoint moved — the\n * exact drift a probe is for.\n */\n authStatuses: number[];\n /** Why those statuses, for whoever reads a failure at 3am. */\n why: string;\n};\n\n/**\n * Run one probe.\n *\n * Never throws. A probe that threw would take the suite down with it, and the\n * suite's whole value is that it runs everywhere including on a laptop with no\n * network.\n */\nexport async function probe(spec: ProbeSpec): Promise<ProbeResult> {\n let response: { status: number; body: string };\n\n try {\n response = await spec.request();\n } catch (error) {\n const classified = classifyError(error);\n\n // Unreachable is a fact about the machine, not about the connector.\n if (classified.kind === \"unreachable\") {\n return { connector: spec.connector, outcome: \"skip\", detail: `offline — ${classified.detail}` };\n }\n\n return {\n connector: spec.connector,\n outcome: \"fail\",\n detail: `the request failed before any status arrived — ${classified.detail}`,\n kind: classified.kind,\n };\n }\n\n const { status, body } = response;\n\n if (spec.authStatuses.includes(status)) {\n return {\n connector: spec.connector,\n outcome: \"pass\",\n status,\n detail: `${status} — the provider refused an invalid credential, which proves host, path, method and error handling. ${spec.why}`,\n };\n }\n\n if (status < 400) {\n // A success with a credential that cannot be valid means the request is not\n // doing what it appears to. Worse than a failure, because it is green.\n return {\n connector: spec.connector,\n outcome: \"fail\",\n status,\n detail:\n `${status} — the provider ACCEPTED a deliberately invalid credential. Either the credential is reaching ` +\n \"nothing (wrong header, wrong field) or this endpoint does not authenticate. Neither is safe to ship.\",\n };\n }\n\n const classified = classifyStatus(status, body);\n\n return {\n connector: spec.connector,\n outcome: \"fail\",\n status,\n kind: classified.kind,\n detail:\n `${status} — expected one of ${spec.authStatuses.join(\", \")}. A ${status} here usually means the endpoint ` +\n `moved or the path is wrong, which no amount of local mocking would ever have said. ${body.slice(0, 200)}`,\n };\n}\n\nexport type ProbeReport = {\n results: ProbeResult[];\n passed: number;\n failed: number;\n skipped: number;\n /** True when nothing failed. Skips do not fail a report. */\n ok: boolean;\n};\n\n/** Run a set of probes and summarise. Sequential: probes hit real providers. */\nexport async function runProbes(specs: ProbeSpec[]): Promise<ProbeReport> {\n const results: ProbeResult[] = [];\n\n for (const spec of specs) results.push(await probe(spec));\n\n const passed = results.filter((result) => result.outcome === \"pass\").length;\n const failed = results.filter((result) => result.outcome === \"fail\").length;\n const skipped = results.filter((result) => result.outcome === \"skip\").length;\n\n return { results, passed, failed, skipped, ok: failed === 0 };\n}\n","/**\n * Measuring and slicing text the way providers actually measure and slice it.\n *\n * ## Why a package owns this at all\n *\n * Two of the nastiest bugs in a connector catalogue are one-liners, and both\n * come from reaching for the obvious JavaScript idiom:\n *\n * 1. **`text.length` is not a count of anything a provider limits.** It counts\n * UTF-16 code units. `\"👍\".length` is 2 and `\"👨👩👧\".length` is 8, so it\n * overcounts exactly the posts most likely to sit near a limit — and it fails\n * in the worse direction, because a post that passes our check and is then\n * refused by the server reads as the network being flaky rather than as our\n * bug.\n * 2. **`indexOf` returns a character index, and rich-text formats want BYTES.**\n * AT Protocol facets are UTF-8 byte ranges. An implementation using character\n * indices is correct for ASCII and silently corrupts every post containing an\n * emoji or an accent — and it corrupts the *link*, so the post looks fine and\n * the link goes somewhere wrong.\n *\n * Neither is discoverable by testing with ASCII, which is what everyone tests\n * with. So the package makes the wrong thing unreachable: a `ByteRange` is a\n * type, not a convention, and it can only be produced by a function that\n * encoded the string.\n *\n * ## The unit is declared, never assumed\n *\n * Providers count different things — Bluesky counts graphemes, Discord and\n * Telegram count characters, some count bytes. `TextUnit` makes the choice\n * explicit at the point a limit is declared, so nobody has to remember which\n * network is which.\n */\n\n/** What a provider actually counts. Declared per connector, never guessed. */\nexport type TextUnit = \"graphemes\" | \"characters\" | \"utf8-bytes\";\n\n/**\n * A half-open range of UTF-8 BYTES.\n *\n * Named for the unit because the whole failure mode is somebody passing\n * character offsets into a field that wants bytes. The two are equal for ASCII,\n * which is why this survives review and fails in production.\n */\nexport type ByteRange = {\n byteStart: number;\n byteEnd: number;\n};\n\nconst encoder = new TextEncoder();\n\n/**\n * Count graphemes — what a person means by \"characters\".\n *\n * `Intl.Segmenter` is the correct tool and has been in Node since 16. The\n * fallback is the spread operator, which counts code points rather than UTF-16\n * units and so is wrong by less. Both are better than `.length`.\n */\nexport function graphemes(text: string): number {\n const Segmenter = (\n Intl as unknown as {\n Segmenter?: new (locale?: string, options?: object) => { segment(input: string): Iterable<unknown> };\n }\n ).Segmenter;\n\n if (!Segmenter) return [...text].length;\n\n let count = 0;\n for (const _ of new Segmenter(undefined, { granularity: \"grapheme\" }).segment(text)) count++;\n\n return count;\n}\n\n/** Length of `text` in UTF-8 bytes. */\nexport function utf8Length(text: string): number {\n return encoder.encode(text).length;\n}\n\n/**\n * Count `text` in whichever unit a provider enforces.\n *\n * The one function every limit check goes through, so a connector never has to\n * decide — and so `text.length` never appears in connector source at all.\n */\nexport function measure(text: string, unit: TextUnit): number {\n if (unit === \"graphemes\") return graphemes(text);\n if (unit === \"utf8-bytes\") return utf8Length(text);\n\n return text.length;\n}\n\n/**\n * The UTF-8 byte range of a substring that starts at a CHARACTER index.\n *\n * The bridge between the index a regex gives you and the offsets a provider\n * wants. Takes the character index because that is what `matchAll` produces, and\n * returns bytes because that is what goes on the wire — doing the conversion in\n * one place is the entire point.\n */\nexport function byteRangeOf(text: string, charIndex: number, substring: string): ByteRange {\n const byteStart = utf8Length(text.slice(0, charIndex));\n\n return { byteStart, byteEnd: byteStart + utf8Length(substring) };\n}\n\n/**\n * Extract the substring a byte range names.\n *\n * Exists so a test can prove a range points where it claims to, which is the\n * only way to catch an off-by-one that ASCII hides. A range that lands\n * mid-character yields the replacement character rather than throwing, because\n * the useful failure here is a visibly wrong string in an assertion, not an\n * exception three frames away.\n */\nexport function sliceByteRange(text: string, range: ByteRange): string {\n const bytes = encoder.encode(text);\n\n return new TextDecoder().decode(bytes.slice(range.byteStart, range.byteEnd));\n}\n\n/**\n * Bare URLs in `text`, with their UTF-8 byte ranges.\n *\n * Trailing punctuation is excluded: a link at the end of a sentence would\n * otherwise swallow the full stop and 404. Closing brackets go too, for the same\n * reason and because a URL in parentheses is common in prose.\n *\n * This is the generic half of a \"link facet\" — a connector maps the ranges into\n * whatever its provider's rich-text shape is. The package owns the offsets\n * because the offsets are what people get wrong; it does not own the provider's\n * schema, because that is the provider's.\n */\nexport function linkRanges(text: string): Array<ByteRange & { url: string }> {\n const found: Array<ByteRange & { url: string }> = [];\n\n for (const match of text.matchAll(/https?:\\/\\/[^\\s]+/g)) {\n if (match.index === undefined) continue;\n const url = match[0].replace(/[.,;:!?)\\]}'\"]+$/, \"\");\n found.push({ ...byteRangeOf(text, match.index, url), url });\n }\n\n return found;\n}\n","/**\n * Rendering — what a provider would ACTUALLY receive.\n *\n * ## Why a connector package owns this, and why it is dangerous\n *\n * One piece of content often has to reach several providers whose limits are\n * incompatible. Without adaptation the same text is wasteful on one and refused\n * by another. With naive adaptation, the bytes that reach the public are not the\n * bytes anyone approved — and every guarantee a host makes about approval\n * becomes a lie the moment adaptation exists.\n *\n * So rendering happens **before** whatever gate the host applies: the person\n * approving sees exactly what each provider will receive and approves *that*.\n * Nothing is invented after the decision.\n *\n * ## Which forces three properties\n *\n * 1. **Pure.** Rules in, payload out. No clock, no randomness, no network. If\n * the preview and the send could disagree, the approval means nothing.\n * Anything variable — an instance's configured limit — is resolved by the\n * CALLER and passed in as a rule, so it becomes part of what was approved.\n * 2. **Versioned.** Any change to how text is split, counted or faceted bumps\n * `RENDERER_VERSION`, and the version is **inside** the payload hash rather\n * than beside it, so it cannot be checked separately and forgotten.\n * 3. **Honest about loss.** Anything that could not be fitted is reported as a\n * problem, never quietly applied. A truncated URL is worse than a refused\n * post, because it looks deliberate and goes somewhere wrong.\n *\n * ## And one rule about where length lives\n *\n * **No length rule outside this module.** A validator and a renderer that both\n * judge length will disagree, and then the validator refuses content the\n * renderer had already solved. `validate()` on a connector checks things the\n * renderer cannot — media, alt text, a required field — and leaves length here.\n */\n\nimport { linkRanges, measure, type ByteRange, type TextUnit } from \"./text\";\n\n/**\n * Bump on ANY change to how text is split, counted, or faceted.\n *\n * A payload approved under `1` refuses to send once this reads `2`, because the\n * payload the approver saw is no longer the payload that would go out. That is\n * the correct outcome and the reason the field exists.\n */\nexport const RENDERER_VERSION = \"render@1\";\n\nexport type Segment = {\n /** The text of this message, exactly as it would be sent. */\n text: string;\n /** How many of the provider's unit this segment uses. */\n count: number;\n /** Link ranges, in UTF-8 bytes, **relative to this segment**. */\n links?: Array<ByteRange & { url: string }>;\n};\n\nexport type RenderedPayload = {\n /** One entry per message. More than one means a thread. */\n segments: Segment[];\n /** What the provider counts, so a preview can label the number. */\n unit: TextUnit;\n /**\n * What the provider allows, or **null where it imposes no limit**.\n *\n * Null rather than a very large number: `Number.MAX_SAFE_INTEGER` is\n * arithmetically fine and reads, on a person's screen, as\n * `157 / 9007199254740991` — a number pretending to be a fact. \"No limit\" and\n * \"a limit so large it cannot be hit\" are different statements.\n */\n limit: number | null;\n /** Why this could not be rendered, when it could not. Never silently repaired. */\n problems: string[];\n rendererVersion: string;\n};\n\n/**\n * The rules a provider imposes on a message, as DATA.\n *\n * Declarative on purpose. Across the providers this was extracted from, the\n * differences that mattered were all values — the limit, the unit, whether a\n * thread mechanism exists, whether links carry byte offsets. A provider whose\n * rendering genuinely needs code supplies its own `render` on the connector and\n * ignores this; the point of the rule set is that most do not have to.\n */\nexport type RenderRules = {\n /**\n * The provider's limit per message, or `null` where it imposes none.\n *\n * **`null` means UNCOUNTED, and it is a real answer** — distinct from \"we do\n * not know\", which is not a renderable state at all. A caller that does not\n * know the limit must find out before rendering, because every alternative\n * silently produces the wrong bytes.\n *\n * ## This must stay RESOLVABLE PER CONNECTION, and never become static\n *\n * A Mastodon instance's character limit comes from the server it is running:\n * one instance allows 500, another 5000, and the same connector talks to\n * both. Baking the limit into the connector breaks in the quietest way\n * available — rendering to 500 on an instance that allows 5000 wastes most of\n * a post, and rendering to 5000 on one that allows 500 produces a refusal the\n * author cannot see coming from the preview.\n *\n * So the limit is resolved by the CALLER, from the connection, and passed IN\n * — which is also what makes it part of what was approved rather than\n * something decided later behind the approver's back. `withResolvedLimit()`\n * is the supported way to do that, and a test pins that per-call rules beat\n * declared ones.\n */\n limit: number | null;\n unit: TextUnit;\n /**\n * Whether this provider can carry a sequence of connected messages.\n *\n * False means over-length content is REFUSED rather than split — splitting\n * would invent a structure the provider does not have, and a numbered sequence\n * of unconnected messages is worse than a refusal because it looks deliberate.\n */\n thread: boolean;\n /** Include UTF-8 link ranges per segment, for providers with rich-text spans. */\n links?: boolean;\n /** Human name, used in refusal messages. */\n label: string;\n /**\n * Suffix added to each part of a thread, given the 1-based index and total.\n *\n * Defaults to ` (i/n)`. Its cost is measured and subtracted from the limit\n * BEFORE splitting, because a suffix discovered afterwards is a bug that only\n * appears on long threads.\n */\n numbering?: (index: number, total: number) => string;\n};\n\nconst defaultNumbering = (index: number, total: number): string => ` (${index}/${total})`;\n\n/**\n * Where a sentence ends: after `.`, `!` or `?` **only when whitespace follows**,\n * or after a newline.\n *\n * The whitespace condition is the whole point. A regex that treats every `.` as\n * a terminator splits inside `https://example.test/x`, and the URL can then land\n * across two messages — a link that goes nowhere, from copy that looked fine in\n * review. Abbreviations survive for the same reason.\n */\nconst SENTENCE_BOUNDARY = /(?<=[.!?])(?=\\s)|(?<=\\n)/;\n\n/**\n * Split text into parts that each fit the limit.\n *\n * **Sentence boundaries first, then words, and never mid-grapheme.** A thread\n * that splits mid-argument is worse than one that splits a little unevenly, so\n * this prefers a natural break even when it wastes room.\n *\n * A single token that cannot fit at all is reported as a problem rather than\n * hard-cut — see the module note about truncated URLs.\n */\nexport function splitToFit(\n text: string,\n limit: number,\n unit: TextUnit,\n): { parts: string[]; problems: string[] } {\n const count = (value: string): number => measure(value, unit);\n const trimmed = text.trim();\n\n if (count(trimmed) <= limit) return { parts: trimmed === \"\" ? [] : [trimmed], problems: [] };\n\n const problems: string[] = [];\n const parts: string[] = [];\n\n // Sentence-ish units, kept WITH their punctuation: a fragment that loses its\n // full stop reads as truncated even when it is complete.\n //\n // **A terminator only ends a sentence when whitespace or the end follows it.**\n // The obvious sentence regex splits inside `https://example.test/x`, and a\n // URL can then land across two messages — a link that goes nowhere, from copy\n // that looked fine in review. Abbreviations survive for the same reason.\n // Newlines still split: a deliberate break is a better seam than any sentence.\n const sentences = trimmed.split(SENTENCE_BOUNDARY).filter((part) => part !== \"\");\n\n let current = \"\";\n const flush = (): void => {\n if (current.trim() !== \"\") parts.push(current.trim());\n current = \"\";\n };\n\n for (const sentence of sentences) {\n const candidate = current + sentence;\n\n if (count(candidate.trim()) <= limit) {\n current = candidate;\n continue;\n }\n\n flush();\n\n if (count(sentence.trim()) <= limit) {\n current = sentence;\n continue;\n }\n\n let buffer = \"\";\n for (const word of sentence.split(/(\\s+)/)) {\n if (count((buffer + word).trim()) <= limit) {\n buffer += word;\n continue;\n }\n\n if (buffer.trim() !== \"\") parts.push(buffer.trim());\n buffer = \"\";\n\n if (count(word.trim()) > limit) {\n problems.push(\n `\"${word.trim().slice(0, 40)}…\" is ${count(word.trim())} ${unit} on its own, longer than one whole ` +\n `message (${limit}). It cannot be split without breaking it, so this needs rewriting rather than adapting.`,\n );\n continue;\n }\n\n buffer = word;\n }\n\n current = buffer;\n }\n\n flush();\n\n return { parts: parts.filter((part) => part !== \"\"), problems };\n}\n\n/**\n * Render text against a provider's rule set.\n *\n * The one entry point. A connector that needs nothing bespoke declares rules and\n * calls this; the shared behaviour — numbering cost, per-segment link offsets,\n * refusal where there is no thread mechanism — is then identical everywhere,\n * which is what makes a catalogue feel like one thing.\n */\nexport function render(text: string, rules: RenderRules): RenderedPayload {\n const { limit, unit, label } = rules;\n const count = (value: string): number => measure(value, unit);\n const trimmed = text.trim();\n\n if (limit === null || count(trimmed) <= limit) {\n return {\n segments: [{ text: trimmed, count: count(trimmed), ...(rules.links ? { links: linkRanges(trimmed) } : {}) }],\n unit,\n limit,\n problems: [],\n rendererVersion: RENDERER_VERSION,\n };\n }\n\n if (!rules.thread) {\n return {\n segments: [{ text: trimmed, count: count(trimmed), ...(rules.links ? { links: linkRanges(trimmed) } : {}) }],\n unit,\n limit,\n problems: [\n `${count(trimmed)} ${unit}; ${label}'s limit is ${limit}, and it has no thread mechanism to split into. ` +\n \"This needs shortening rather than adapting.\",\n ],\n rendererVersion: RENDERER_VERSION,\n };\n }\n\n const numbering = rules.numbering ?? defaultNumbering;\n const first = splitToFit(text, limit, unit);\n const willNumber = first.parts.length > 1;\n\n // Re-split against a reduced limit when numbering will be added, so the suffix\n // is accounted for rather than discovered afterwards. `99/99` is the widest\n // realistic suffix; using the real total would need the split that depends on\n // it, which is circular.\n const suffixCost = willNumber ? count(numbering(99, 99)) : 0;\n const { parts, problems } = willNumber ? splitToFit(text, limit - suffixCost, unit) : first;\n\n const segments: Segment[] = parts.map((part, index) => {\n const withNumber = parts.length > 1 ? `${part}${numbering(index + 1, parts.length)}` : part;\n\n return {\n text: withNumber,\n count: count(withNumber),\n // Computed per segment and never sliced from the original — an offset into\n // the whole text is meaningless once the text has been split.\n ...(rules.links ? { links: linkRanges(withNumber) } : {}),\n };\n });\n\n const over = segments.filter((segment) => segment.count > limit);\n if (over.length > 0) {\n problems.push(\n `${over.length} segment(s) still exceed ${limit} ${unit} after splitting. That is a renderer defect rather ` +\n \"than a content problem — the split should not produce an over-length message.\",\n );\n }\n\n return { segments, unit, limit, problems, rendererVersion: RENDERER_VERSION };\n}\n\n/**\n * A stable fingerprint of what would actually be sent.\n *\n * This is what an approval covers. A host recomputes it at dispatch and refuses\n * on mismatch: if the rendering changed for ANY reason — edited text, a changed\n * limit, a new renderer version — the hashes differ, because the approver\n * approved a payload rather than an intention.\n *\n * Async because it uses WebCrypto, which is the only hash available on every\n * runtime the suite supports. A host that wants it synchronously should hold the\n * value rather than recompute it.\n */\nexport async function payloadHash(payload: RenderedPayload): Promise<string> {\n const subtle = globalThis.crypto?.subtle;\n\n if (!subtle) {\n throw new Error(\n \"WebCrypto is not available in this runtime, so a payload hash cannot be computed. \" +\n \"Refusing rather than returning a weaker fingerprint that a host would trust equally.\",\n );\n }\n\n const canonical = JSON.stringify({\n // Inside the hash, deliberately — see RENDERER_VERSION.\n v: payload.rendererVersion,\n segments: payload.segments.map((segment) => segment.text),\n });\n\n const digest = await subtle.digest(\"SHA-256\", new TextEncoder().encode(canonical));\n\n return Array.from(new Uint8Array(digest))\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Refuse empty content, centrally.\n *\n * The sha of the empty string is a valid sha, so an empty payload passes every\n * byte-verification a host can write. Refusing it in one place is what stops\n * every one of those checks from being hollow.\n */\nexport function isEmptyPayload(payload: RenderedPayload): boolean {\n return payload.segments.every((segment) => segment.text.trim() === \"\");\n}\n\n/**\n * Apply a per-connection limit over a connector's declared rules.\n *\n * The one supported way to do the thing `RenderRules.limit` insists on: a\n * Mastodon instance publishes its own `max_toot_chars`, a Discord webhook does\n * not, and the same connector serves both. Resolving happens at the call site,\n * with the connection in hand, and the resolved value travels INTO `render` so\n * it is part of the payload the approver saw.\n *\n * `undefined` keeps whatever the connector declared — \"I did not look\" is not\n * the same as \"there is no limit\", and only the second is `null`.\n */\nexport function withResolvedLimit(rules: RenderRules, limit: number | null | undefined): RenderRules {\n return limit === undefined ? rules : { ...rules, limit };\n}\n","/**\n * The seam — two objects, deliberately separate.\n *\n * They answer different questions and change on different clocks:\n *\n * - **`ProviderAdapter`** — *how does an operator stand this up, and does it\n * work?* Credential shapes, setup steps with the trap in each one named,\n * scopes, a read-only verify. It changes when the provider changes its\n * onboarding, which is rarely and disruptively.\n * - **`Connector`** — *what does calling this actually involve?* Capabilities,\n * delivery behaviour, validation, rendering, the call itself, measurement. It\n * changes when the API changes, which is often and quietly.\n *\n * Folding them together produces an object that has to be edited for both, and\n * a diff that cannot tell you which happened.\n *\n * ## What this package does NOT own, and never will\n *\n * Everything on this list belongs to the HOST, because each is enforced in one\n * place and every connector inherits it from the dispatch path rather than\n * implementing it:\n *\n * - **approval** — whether this may go out at all;\n * - **liveness** — `call(..., { dryRun })` takes the flag; a connector must\n * never resolve its own. A host may require two independent yeses, or ten;\n * that is its business and the package cannot help it get that wrong;\n * - **the approved-bytes comparison** — the host re-renders and refuses on\n * mismatch. The package makes that possible by keeping `render` pure and\n * versioned, and does not perform it;\n * - **consent** for anything reaching a list of people;\n * - **any journal** — decisions, dispatches, runs;\n * - **credential storage.** Credentials are ARGUMENTS. Nothing in this package\n * reads `process.env`, and a test asserts that it does not.\n *\n * A packager that owned any of those would be unusable by a host that takes them\n * seriously — which is the only kind of host worth building for.\n */\n\nimport type { DeliveryDeclaration } from \"./delivery\";\nimport type { RenderRules, RenderedPayload } from \"./render\";\nimport type { ConnectorMode, SandboxKind } from \"./mode\";\n\n/* ── Standing a provider up ───────────────────────────────────────────────── */\n\n/**\n * One credential a provider needs.\n *\n * **`secret` is chosen per field, never inferred from the type.** A Discord\n * webhook URL is a URL and is entirely a secret — it carries its token in the\n * path — and the reference implementation very nearly stored it as\n * configuration because it looked like a URL.\n */\n/**\n * Who a credential belongs to.\n *\n * Exported as a runtime constant as well as a type — see `CREDENTIAL_SCOPES`.\n */\nexport type CredentialScope = \"provider\" | \"account\";\n\n/**\n * The scopes, as DATA.\n *\n * Because this union crosses a JSON boundary and the compiler cannot follow it\n * there. When these values were renamed from `app`/`brand`, a consumer that\n * re-declared the field shape on its client kept compiling — their\n * `f.scope === \"brand\"` silently became never-true, and every credential field\n * would have rendered as shared. `tsc` gives a host mirroring these types no\n * help at all, so the values ship as data a host can validate against at the\n * boundary where the compiler stops.\n */\nexport const CREDENTIAL_SCOPES: readonly CredentialScope[] = [\"provider\", \"account\"];\n\n/** Severities, as data, for the same reason. */\nexport const PROBLEM_SEVERITIES = [\"block\", \"warn\"] as const;\n\n/** Canonical metric names, as data, for the same reason. */\nexport const CANONICAL_METRICS = [\"like\", \"share\", \"reply\", \"quote\", \"view\"] as const;\n\nexport type CredentialField = {\n /** Stable key. The host maps it to wherever it keeps values. */\n key: string;\n label: string;\n /** What it is, where it comes from, and what it does NOT prove. */\n help: string;\n /**\n * `provider` — one value for the whole installation (an OAuth app serves\n * every account). `account` — one value per connected account.\n *\n * The distinction exists because getting it wrong means either asking for the\n * same app secret five times or letting one account's token reach another's.\n */\n scope: CredentialScope;\n secret: boolean;\n required: boolean;\n /** A hint for an empty field. NEVER a real value. */\n placeholder?: string;\n};\n\nexport type SetupStep = {\n title: string;\n /** The step, and the trap in it. A step with no trap named is usually wrong. */\n detail: string;\n url?: string;\n};\n\n/**\n * The result of a read-only credential check.\n *\n * **`proves` is not decoration.** Telegram's `getMe` validates the token and\n * says nothing about whether the bot was added to the target chat — which is the\n * step everyone actually gets stuck on. A green tick that means more than it\n * should is worse than no tick, so a verify states its own scope.\n */\nexport type VerifyResult = {\n ok: boolean;\n /** What was learned — ideally the account's real name, so the id can be checked. */\n detail: string;\n /** Exactly what this proves, and what it does not. */\n proves?: string;\n};\n\n/**\n * How an operator stands a provider up.\n *\n * Every provider worth listing is declared, **including the ones that cannot\n * work yet**: a provider missing from a catalogue reads as unsupported, while a\n * provider listed as `implemented: false` tells you exactly what it would take.\n */\nexport type ProviderAdapter = {\n id: string;\n label: string;\n /** True when this can actually be used today. A real state, not a placeholder. */\n implemented: boolean;\n /** One line on what it is for, shown before anyone opens the setup. */\n summary: string;\n fields: CredentialField[];\n setup: SetupStep[];\n /** Exactly what to ask for, and no more. */\n scopes: string[];\n /**\n * How long credentials last, where the provider publishes a figure.\n *\n * Undefined means the provider states no lifetime — which a surface should\n * report as \"no published expiry\" rather than inventing one, and still note\n * that anything can be revoked. \"No known expiry\" is not \"cannot stop working\".\n */\n credentialLifetimeDays?: number;\n /**\n * How the provider exposes a test estate.\n *\n * **`\"unverified\"` is a real answer and is the right one until somebody has\n * actually checked.** Being wrong here points a workflow at a live estate\n * while a person believes it is a test one, so the type carries \"nobody\n * looked\" rather than forcing a guess — a comment saying so is not a type, and\n * `providerProblems()` reports the combination `implemented: true` +\n * `unverified`.\n */\n sandbox: SandboxKind;\n /**\n * What `verify` proves, and what it does NOT.\n *\n * Required whenever `verify` exists, and checked by `providerProblems()`.\n * Telegram's `getMe` validates the token and says nothing about whether the\n * bot was added to the target chat — which is the step everyone actually gets\n * stuck on. A green tick that means more than it should is worse than no tick.\n */\n proves?: string;\n /**\n * A read-only call proving the credential works AND reaches the right account.\n *\n * Deliberately a read. A verify that wrote something would be a send, and\n * sends are gated.\n */\n verify?(credentials: Record<string, string>): Promise<VerifyResult>;\n};\n\n/* ── Calling a provider ───────────────────────────────────────────────────── */\n\nexport type Problem = {\n severity: \"block\" | \"warn\";\n message: string;\n};\n\n/**\n * The result of one call.\n *\n * `ref` is how the provider identifies what was done — the handle every later\n * question is joined on. **A 2xx with no ref is a failure**, and the connector\n * says so rather than reporting a success nobody can point at.\n */\nexport type CallResult = {\n ok: boolean;\n ref: string | null;\n /** True when nothing actually left the building. */\n dryRun: boolean;\n detail: string;\n /** Which estate this ran against. Always reported, never inferred. */\n mode?: ConnectorMode;\n};\n\n/**\n * What a connector can report, in its own words — **declared, not inferred**.\n *\n * Inferring the shape from whatever a pull last returned gives an empty shape\n * when nothing has been pulled, and then *\"this reports nothing\"* and *\"nobody\n * has asked yet\"* are the same blank, needing opposite actions.\n *\n * `canonical` is what the metric IS across providers, where an honest equivalent\n * exists. A like and a favourite are the same act; a repost and a boost are the\n * same act; a quote often has no equivalent, and `null` says so — which is more\n * useful than a mapping somebody invented to make a table line up.\n */\nexport type MetricDescriptor = {\n /** The key the connector's own mapping actually returns. */\n key: string;\n /** What the provider itself calls it. */\n label: string;\n canonical: \"like\" | \"share\" | \"reply\" | \"quote\" | \"view\" | null;\n means: string;\n};\n\nexport type MetricSample = {\n ref: string;\n at: string;\n /** Only what the provider actually reported. **Absent stays absent.** */\n metrics: Record<string, number>;\n};\n\n/**\n * What a connector can do.\n *\n * **A capability flag must not outrun the code**, and this is checkable rather\n * than a rule people remember: `assertCapabilitiesHonest` in `metrics.ts` fails\n * when `metrics: true` has no declared shape. In the reference implementation\n * two connectors claimed metrics while returning `[]` for weeks — so a pull did\n * not skip them, it asked, got nothing, and reported nothing, which on a\n * dashboard is indistinguishable from *\"we asked and nobody engaged\"*.\n */\nexport type Capabilities = {\n call: boolean;\n metrics: boolean;\n feedback: boolean;\n};\n\n/**\n * One capability of one provider.\n *\n * `Target` is the host's own content shape — the package has no opinion about\n * it, and a field named for one host's content model has no business in a shared\n * contract.\n */\nexport type Connector<Target = unknown> = {\n id: string;\n label: string;\n /** Which `ProviderAdapter` stands this up. */\n provider: string;\n /**\n * The connector API version this was written against.\n *\n * Required, and checked at registration by `assertConnectorApi`. The core and\n * the catalogue release on separate clocks, and a connector is VENDORED — a\n * copy in someone else's repository, with no manifest of its own to carry a\n * version range. This number is the only thing that can say \"this copy was\n * written for a core you no longer have\", and it says it at build time rather\n * than in production. See `compat.ts`.\n */\n connectorApi: number;\n capabilities: Capabilities;\n /**\n * How this behaves when a call goes wrong, and how fast it may be used.\n * Cited, not asserted — see `DeliveryDeclaration`.\n */\n delivery: DeliveryDeclaration;\n /** Declared metric shape. **Absent, not empty**, where there are none. */\n metricShape?: MetricDescriptor[];\n /**\n * Rendering rules, where this connector renders text.\n *\n * Data rather than code for the common case. A provider whose rendering\n * genuinely needs code supplies `render` instead.\n */\n renderRules?: RenderRules;\n /**\n * Everything the renderer cannot judge — media, alt text, a required field.\n * **Never length**: see the note at the top of `render.ts`.\n */\n validate(target: Target): Problem[];\n /** Pure and versioned. Overrides `renderRules` when both are present. */\n render?(target: Target, rules?: Record<string, number>): RenderedPayload;\n /**\n * Do the thing.\n *\n * `credentials` are passed IN. `dryRun` is decided by the HOST. Neither is\n * negotiable, and both are asserted by tests in this package.\n */\n call(\n target: Target,\n options: { dryRun: boolean; credentials: Record<string, string>; mode?: ConnectorMode },\n ): Promise<CallResult>;\n fetchMetrics?(refs: string[], credentials: Record<string, string>): Promise<MetricSample[]>;\n /**\n * Feedback on things we did, for refs the host already holds.\n *\n * **Optional, and absent is a real answer** rather than a gap to be filled. A\n * connector with no feedback mechanism must not pretend by returning an empty\n * array — \"nobody replied\" and \"this cannot tell you\" are different, and only\n * one of them is news.\n */\n fetchFeedback?(\n refs: string[],\n credentials: Record<string, string>,\n ): Promise<\n Array<{\n id: string;\n inReplyTo: string;\n author: string;\n text: string;\n at: string;\n url: string | null;\n /** What answering this would need. Opaque to the host. */\n context: Record<string, string>;\n }>\n >;\n};\n\n/**\n * A catalogue of connectors and the providers that stand them up.\n *\n * A plain lookup rather than a registry with lifecycle: the host owns which\n * connectors it installed, and a package-level global would be one more thing\n * that can be half-initialised.\n */\nexport type Catalogue<Target = unknown> = {\n providers: Record<string, ProviderAdapter>;\n connectors: Record<string, Connector<Target>>;\n};\n","/**\n * Verifying an inbound provider webhook.\n *\n * An unverified webhook endpoint is a public, unauthenticated way to start a\n * workflow in someone's account — which is to say, a way for a stranger to make\n * your app refund an order or post to your Slack. So verification is part of the\n * connector pattern rather than an exercise for the host: a trigger node that\n * cannot verify its deliveries must say so, and a host must not be able to mount\n * one by accident.\n *\n * ## The three things every provider's scheme actually needs\n *\n * Providers differ in header names and in what exactly gets signed, and almost\n * nothing else:\n *\n * 1. **The RAW body.** Signatures are computed over bytes. Re-serialising parsed\n * JSON changes key order and whitespace and produces a signature mismatch\n * that looks like a wrong secret. The host must pass the body it received.\n * 2. **A constant-time comparison.** `===` on a signature leaks, through timing,\n * which prefix was right. That is a real forgery path, not a theoretical one.\n * 3. **A timestamp tolerance.** Without it a valid signature is valid forever,\n * so anyone who ever saw one delivery can replay it whenever they like.\n *\n * WebCrypto rather than `node:crypto`, so the same file runs on Node, Bun, Deno\n * and an edge runtime. Node 22 is the suite's floor and has it globally.\n */\n\nexport type WebhookVerification =\n | { ok: true }\n | { ok: false; reason: string };\n\nexport type HmacScheme = {\n /**\n * Optional and always `\"hmac\"`: every scheme written before 0.8.0 omits it\n * and still means HMAC. It exists so `verifyDelivery` dispatches on a NAMED\n * kind — `\"shared-token\"`, `\"hmac\"` or absent — and refuses any other value\n * by name, rather than treating \"has no kind\" as HMAC forever. (Fancy's\n * alignment review of 0.8.0: a third scheme that forgot its kind would\n * otherwise be verified as HMAC, silently.)\n */\n kind?: \"hmac\";\n /** Hash to use. */\n algorithm: \"SHA-256\" | \"SHA-1\" | \"SHA-512\";\n /**\n * Build the exact string that gets signed. Providers differ here more than\n * anywhere else — Stripe signs `${timestamp}.${body}`, Slack signs\n * `v0:${timestamp}:${body}`, GitHub signs the body alone.\n */\n payload: (raw: string, timestamp?: string) => string;\n /** Seconds a delivery stays acceptable. */\n tolerance?: number;\n /** Encoding of the signature the provider sends. */\n encoding?: \"hex\" | \"base64\";\n};\n\n/**\n * Verify an HMAC-signed delivery.\n *\n * Returns a RESULT rather than throwing, and the failure carries a reason: a\n * host wants to log which check failed (stale? wrong secret? no header?) while\n * still answering the provider with an opaque 400.\n */\nexport async function verifyHmac(options: {\n raw: string;\n signature: string | undefined;\n secret: string | undefined;\n scheme: HmacScheme;\n timestamp?: string;\n /** Seconds since the epoch. Injected so tests are not clock-dependent. */\n now?: number;\n}): Promise<WebhookVerification> {\n const { raw, signature, secret, scheme, timestamp } = options;\n\n if (!secret) {\n // Never \"accept when unconfigured\". An endpoint that verifies nothing\n // because nobody set a secret is strictly worse than one that is off: it\n // looks protected.\n return { ok: false, reason: \"no signing secret configured for this trigger\" };\n }\n if (!signature) return { ok: false, reason: \"delivery carried no signature header\" };\n\n if (scheme.tolerance !== undefined) {\n if (!timestamp) return { ok: false, reason: \"delivery carried no timestamp header\" };\n\n const sent = Number(timestamp);\n if (!Number.isFinite(sent)) return { ok: false, reason: \"timestamp header is not a number\" };\n\n const now = options.now ?? Math.floor(Date.now() / 1000);\n if (Math.abs(now - sent) > scheme.tolerance) {\n return { ok: false, reason: `delivery is outside the ${scheme.tolerance}s replay window` };\n }\n }\n\n const expected = await hmac(secret, scheme.payload(raw, timestamp), scheme.algorithm, scheme.encoding ?? \"hex\");\n\n return constantTimeEquals(expected, signature)\n ? { ok: true }\n : { ok: false, reason: \"signature did not match\" };\n}\n\n/** HMAC of `payload` under `secret`, hex or base64 encoded. */\nexport async function hmac(\n secret: string,\n payload: string,\n algorithm: HmacScheme[\"algorithm\"],\n encoding: \"hex\" | \"base64\" = \"hex\",\n): Promise<string> {\n const subtle = globalThis.crypto?.subtle;\n\n if (!subtle) {\n throw new Error(\n \"WebCrypto is not available in this runtime, so webhook signatures cannot be verified. \" +\n \"Refusing to accept deliveries unverified.\",\n );\n }\n\n const encoder = new TextEncoder();\n const key = await subtle.importKey(\n \"raw\",\n encoder.encode(secret),\n { name: \"HMAC\", hash: algorithm },\n false,\n [\"sign\"],\n );\n const bytes = new Uint8Array(await subtle.sign(\"HMAC\", key, encoder.encode(payload)));\n\n if (encoding === \"base64\") {\n let binary = \"\";\n bytes.forEach((byte) => {\n binary += String.fromCharCode(byte);\n });\n\n return btoa(binary);\n }\n\n return Array.from(bytes)\n .map((byte) => byte.toString(16).padStart(2, \"0\"))\n .join(\"\");\n}\n\n/**\n * Compare two strings without leaking where they diverge.\n *\n * The length check first is deliberate and safe: a signature's length is fixed\n * by its algorithm and is not a secret, so revealing a mismatch there tells an\n * attacker nothing they could not compute.\n */\nexport function constantTimeEquals(a: string, b: string): boolean {\n if (a.length !== b.length) return false;\n\n let diff = 0;\n for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);\n\n return diff === 0;\n}\n\n/**\n * A delivery verified by a token the provider ECHOES rather than a signature\n * it computes.\n *\n * Google Calendar sends the channel's `token` back in `X-Goog-Channel-Token`\n * on every notification — with an EMPTY body, so there is nothing to sign.\n * Microsoft Graph sends `clientState` inside every item of the notification's\n * `value` array. Same refusal-by-default as HMAC, same result shape, and a\n * constant-time comparison: a token is a secret, and `===` leaks which prefix\n * was right.\n *\n * A body `path` is dotted; a segment ending in `[]` means EVERY element of that\n * array, all of which must match — one wrong item refuses the whole delivery.\n */\nexport type SharedTokenScheme =\n | { kind: \"shared-token\"; in: \"header\"; name: string }\n | { kind: \"shared-token\"; in: \"body\"; path: string };\n\nexport function isSharedTokenScheme(scheme: HmacScheme | SharedTokenScheme): scheme is SharedTokenScheme {\n return \"kind\" in scheme && scheme.kind === \"shared-token\";\n}\n\nexport function verifySharedToken(options: {\n raw: string;\n headers: Record<string, string | string[] | undefined>;\n secret: string | undefined;\n scheme: SharedTokenScheme;\n}): WebhookVerification {\n const { raw, headers, secret, scheme } = options;\n\n if (!secret) {\n // Never \"accept when unconfigured\" — the same rule as verifyHmac, for the\n // same reason: an endpoint that verifies nothing LOOKS protected.\n return { ok: false, reason: \"no shared token configured for this trigger\" };\n }\n\n let tokens: unknown[];\n if (scheme.in === \"header\") {\n tokens = [header(headers, scheme.name)];\n } else {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n return { ok: false, reason: \"delivery body is not JSON\" };\n }\n tokens = readTokens(parsed, scheme.path.split(\".\"));\n }\n\n const present = tokens.filter((t) => t !== undefined && t !== null && t !== \"\");\n if (present.length === 0) return { ok: false, reason: \"delivery carried no token\" };\n\n // Every element is compared, none is skipped: a batch is accepted as a whole\n // or refused as a whole.\n let matched = present.length === tokens.length;\n for (const token of present) {\n matched = (typeof token === \"string\" && constantTimeEquals(secret, token)) && matched;\n }\n\n return matched ? { ok: true } : { ok: false, reason: \"token did not match\" };\n}\n\n/** Every value at a dotted path; a `[]` segment fans out over an array. Absent is `undefined`. */\nfunction readTokens(value: unknown, segments: string[]): unknown[] {\n if (segments.length === 0) return [value];\n\n const [head, ...rest] = segments as [string, ...string[]];\n const eachElement = head.endsWith(\"[]\");\n const key = eachElement ? head.slice(0, -2) : head;\n\n if (value === null || typeof value !== \"object\") return [undefined];\n const next = (value as Record<string, unknown>)[key];\n\n if (!eachElement) return readTokens(next, rest);\n if (!Array.isArray(next)) return [undefined];\n\n return next.flatMap((element) => readTokens(element, rest));\n}\n\n/**\n * A challenge the provider makes BEFORE it will deliver anything, answered by\n * echoing a query parameter as plain text. Microsoft Graph POSTs\n * `?validationToken=…` to the notification URL when a subscription is created\n * and refuses to create it unless the token comes back, decoded, as\n * `text/plain` within ten seconds.\n *\n * Declared as data on the trigger so a host can answer it in its own routing\n * layer; `handshakeResponse` is the pure half — what to send, or `undefined`\n * when the request is not a challenge at all.\n */\nexport type ChallengeHandshake = {\n kind: \"echo-query\";\n /** The query parameter carrying the challenge. */\n param: string;\n contentType?: \"text/plain\";\n};\n\nexport function handshakeResponse(\n handshake: ChallengeHandshake | undefined,\n query: Record<string, string | string[] | undefined>,\n): { status: 200; contentType: string; body: string } | undefined {\n if (!handshake) return undefined;\n\n const raw = query[handshake.param];\n const token = Array.isArray(raw) ? raw[0] : raw;\n if (!token) return undefined;\n\n return { status: 200, contentType: handshake.contentType ?? \"text/plain\", body: token };\n}\n\n/**\n * Pull one header case-insensitively.\n *\n * Header case is not preserved consistently across proxies, frameworks and\n * runtimes; a connector that read `req.headers[\"Stripe-Signature\"]` would work\n * behind one server and reject every delivery behind another.\n */\nexport function header(headers: Record<string, string | string[] | undefined>, name: string): string | undefined {\n const wanted = name.toLowerCase();\n\n for (const [key, value] of Object.entries(headers)) {\n if (key.toLowerCase() !== wanted) continue;\n\n return Array.isArray(value) ? value[0] : value;\n }\n\n return undefined;\n}\n","/**\n * How a connector trigger learns that something happened.\n *\n * **Not every provider pushes, and assuming webhooks is the single most common\n * way a connector catalogue ends up lying.** Google Drive and Microsoft Graph\n * want a *subscription* you must renew before it expires. Salesforce wants a\n * Pub/Sub or CometD stream. S3 wants an event notification routed to\n * infrastructure the consumer provisions. Reddit wants you to poll. Each of\n * those is a different obligation on the host, and a node that declared\n * \"webhook\" for all of them would install cleanly and then never fire.\n *\n * So a trigger declares its delivery mechanism, and the host can refuse to mount\n * one it has no machinery for — at author time, not at 3am.\n */\n\nimport type { ResolvedConnection } from \"./connection\";\nimport type { ConnectorFaker } from \"./faker\";\nimport { fakeRequest } from \"./faker\";\nimport type { LeaseDeclaration } from \"./lease\";\nimport type { ChallengeHandshake, HmacScheme, SharedTokenScheme, WebhookVerification } from \"./webhook\";\nimport { header, isSharedTokenScheme, verifyHmac, verifySharedToken } from \"./webhook\";\n\n/**\n * The delivery mechanisms that actually exist in the wild.\n *\n * - `webhook` — the provider POSTs to a URL you own. Verify the signature.\n * - `subscription` — a webhook that EXPIRES. Someone has to renew it, forever,\n * and if nobody does the workflow stops firing without an error anywhere.\n * Distinct from `webhook` precisely because that ongoing duty is invisible.\n * - `poll` — you ask, on a schedule, and track a cursor.\n * - `pubsub` — the provider publishes to a broker (Google Pub/Sub, Salesforce\n * Pub/Sub API, SNS/SQS) that the consumer must provision.\n * - `stream` — a long-lived connection the host maintains (websocket, gRPC,\n * SSE, CometD).\n */\nexport type DeliveryMechanism = \"webhook\" | \"subscription\" | \"poll\" | \"pubsub\" | \"stream\";\n\nexport type TriggerDescriptor = {\n service: string;\n operation: string;\n delivery: DeliveryMechanism;\n /**\n * What the host has to do before this fires, in one sentence, aimed at the\n * person who has to do it. Surfaced by the editor and by the MCP, because a\n * trigger whose setup is undocumented is a trigger that silently never fires.\n */\n setup: string;\n /** For `subscription`: how long the provider keeps it alive, in seconds. */\n subscriptionTtl?: number;\n /**\n * For `subscription`: where the provider's expiry sits in the create/renew\n * response and how it is spelled, so a host builds the `SubscriptionLease`\n * with `leaseFromResponse` and runs ONE renewal scheduler for every\n * expiring trigger.\n */\n lease?: LeaseDeclaration;\n /** For `webhook` / `subscription`: how a delivery is authenticated. */\n verification?: WebhookVerificationSpec;\n /** For `poll`: the provider's smallest sane interval, in seconds. */\n minPollSeconds?: number;\n /** A faked sample event, so the node is runnable before any of the above. */\n faker: ConnectorFaker;\n};\n\n/** The provider SIGNS each delivery. */\nexport type HmacVerificationSpec = {\n /** Header carrying the signature. */\n signatureHeader: string;\n /** Header carrying the timestamp, when the scheme signs one. */\n timestampHeader?: string;\n scheme: HmacScheme;\n /**\n * Some schemes pack the timestamp INTO the signature header\n * (Stripe: `t=…,v1=…`). Given the raw header value, return the parts.\n */\n parse?: (raw: string) => { signature?: string; timestamp?: string };\n /** A challenge the provider makes before it will deliver anything. */\n handshake?: ChallengeHandshake;\n};\n\n/**\n * The provider ECHOES a token the subscriber chose. Google Calendar's channel\n * token, Graph's `clientState`. See `SharedTokenScheme`.\n */\nexport type SharedTokenVerificationSpec = {\n scheme: SharedTokenScheme;\n /** A challenge the provider makes before it will deliver anything. */\n handshake?: ChallengeHandshake;\n};\n\nexport type WebhookVerificationSpec = HmacVerificationSpec | SharedTokenVerificationSpec;\n\nexport function isSharedTokenSpec(spec: WebhookVerificationSpec): spec is SharedTokenVerificationSpec {\n return isSharedTokenScheme(spec.scheme);\n}\n\nexport type InboundDelivery = {\n /** The body EXACTLY as received. Not re-serialised. See `webhook.ts`. */\n raw: string;\n headers: Record<string, string | string[] | undefined>;\n};\n\n/**\n * Verify an inbound delivery against a trigger's declared scheme.\n *\n * A trigger with no `verification` REFUSES rather than accepts. That asymmetry\n * is the whole safety property: an unverifiable endpoint is a stranger's button\n * for starting workflows in your account, and defaulting to \"allow\" would make\n * every misconfiguration into an open door that looks shut.\n */\nexport async function verifyDelivery(\n trigger: TriggerDescriptor,\n delivery: InboundDelivery,\n secret: string | undefined,\n now?: number,\n): Promise<WebhookVerification> {\n const spec = trigger.verification;\n\n if (!spec) {\n return {\n ok: false,\n reason: `${trigger.service}.${trigger.operation} declares no signature scheme, so a delivery cannot be trusted.`,\n };\n }\n\n if (isSharedTokenSpec(spec)) {\n return verifySharedToken({ raw: delivery.raw, headers: delivery.headers, secret, scheme: spec.scheme });\n }\n\n // Dispatch on a NAMED kind, and refuse a name nobody serves. \"Has no kind\"\n // means HMAC only because every pre-0.8.0 scheme was one; a scheme that\n // names anything else must not be verified as if it were.\n const kind = (spec.scheme as { kind?: string }).kind ?? \"hmac\";\n if (kind !== \"hmac\") {\n return {\n ok: false,\n reason: `${trigger.service}.${trigger.operation} declares a verification scheme \"${kind}\" this runtime does not know — refusing rather than guessing.`,\n };\n }\n\n const rawHeader = header(delivery.headers, spec.signatureHeader);\n const parsed = spec.parse && rawHeader ? spec.parse(rawHeader) : { signature: rawHeader };\n const timestamp = parsed.timestamp\n ?? (spec.timestampHeader ? header(delivery.headers, spec.timestampHeader) : undefined);\n\n return verifyHmac({\n raw: delivery.raw,\n signature: parsed.signature,\n secret,\n scheme: spec.scheme,\n timestamp,\n ...(now === undefined ? {} : { now }),\n });\n}\n\n/**\n * The event a trigger node publishes when it runs.\n *\n * Three sources, in order, and the third is why a connector trigger is usable\n * on day one:\n *\n * 1. A verified delivery the host injected on the `in` port.\n * 2. Nothing injected, but the connection is remote — that is a host wiring\n * error, and it fails loudly rather than emitting a plausible blank.\n * 3. `fake` mode — the trigger emits its own sample event, so an author can\n * press Run, see the real field names, and wire the downstream nodes against\n * them before the provider has ever been contacted.\n */\nexport function triggerEvent(\n trigger: TriggerDescriptor,\n connection: ResolvedConnection,\n injected: unknown,\n config: Record<string, unknown>,\n): unknown {\n if (hasDelivery(injected)) return injected;\n\n if (connection.mode === \"fake\") {\n return trigger.faker(\n trigger.operation,\n fakeRequest(trigger.service, trigger.operation, config),\n );\n }\n\n throw new Error(\n `${trigger.service}.${trigger.operation}: no event was delivered to this trigger. ` +\n `Its delivery mechanism is \"${trigger.delivery}\" — ${trigger.setup} ` +\n 'Set the node\\'s mode to \"fake\" to design against a sample event instead.',\n );\n}\n\n/**\n * Whether a value on the `in` port is an actual delivery.\n *\n * An EMPTY object or array counts as nothing delivered, and that is not\n * fussiness: a manually started run seeds `{}` on the trigger's input, so a\n * bare null check would hand the executor an empty envelope and call it an\n * event. Downstream every field would be `null`, the run would be green, and\n * nothing would say the trigger never fired.\n */\nfunction hasDelivery(value: unknown): boolean {\n if (value === undefined || value === null) return false;\n if (Array.isArray(value)) return value.length > 0;\n if (typeof value === \"object\") return Object.keys(value as object).length > 0;\n\n return true;\n}\n"],"mappings":"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","names":["header","mode","missing","walk","classified","encoder","header","header"]}
|