@secondlayer/stacks 6.2.1 → 6.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,10 +3,10 @@
3
3
  "sources": ["../src/filters/event-types.ts", "../src/filters/validate.ts", "../src/filters/factories.ts"],
4
4
  "sourcesContent": [
5
5
  "// The decoded event-type vocabulary — canonical home. Index (decoded layer)\n// and Streams (canonical firehose) expose the SAME set. This lives in\n// @secondlayer/stacks because it is the leaf of the dependency graph\n// (shared → stacks), so no package can drift a private copy upward:\n// @secondlayer/shared re-exports it for back-compat.\n/** The decoded event types. Index and Streams expose the same set. */\nexport const DECODED_EVENT_TYPES = [\n\t\"stx_transfer\",\n\t\"stx_mint\",\n\t\"stx_burn\",\n\t\"stx_lock\",\n\t\"ft_transfer\",\n\t\"ft_mint\",\n\t\"ft_burn\",\n\t\"nft_transfer\",\n\t\"nft_mint\",\n\t\"nft_burn\",\n\t\"print\",\n] as const;\n\nexport type DecodedEventType = (typeof DECODED_EVENT_TYPES)[number];\n\n/** Opt-in node vm_events, stored names. Parallel to DECODED_EVENT_TYPES — not\n * the decoded_events / Streams 1.0 vocab. Cursor second component is\n * ordinal. Inner calls are never `contract_call`. */\nexport const VM_EVENT_TYPES = [\n\t\"nested_contract_call\",\n\t\"var_set\",\n\t\"map_set\",\n\t\"map_insert\",\n\t\"map_delete\",\n] as const;\n\nexport type VmEventType = (typeof VM_EVENT_TYPES)[number];\n\n/** Node `/new_block.vm_events[].type` → stored/Index/Streams name. One map,\n * shared by the indexer parser and any consumer that reads raw observer\n * bodies, so the two dialects cannot drift. Inner calls are never\n * `contract_call` (that is the signed outer tx). */\nexport const VM_NODE_TO_STORED_TYPE = {\n\tcontract_call_event: \"nested_contract_call\",\n\tvar_set_event: \"var_set\",\n\tmap_set_event: \"map_set\",\n\tmap_insert_event: \"map_insert\",\n\tmap_delete_event: \"map_delete\",\n} as const satisfies Record<string, VmEventType>;\n\nexport type VmNodeEventType = keyof typeof VM_NODE_TO_STORED_TYPE;\n\n/** Every chain-event filter member across all four surfaces: the 10 decoded\n * token/STX types, the three contract-shaped types (spelled as subgraphs and\n * triggers spell them — Index/Streams project `print_event` → `print`), and\n * the five sBTC lifecycle types (Webhooks-only). */\nexport const CHAIN_EVENT_FILTER_TYPES = [\n\t\"stx_transfer\",\n\t\"stx_mint\",\n\t\"stx_burn\",\n\t\"stx_lock\",\n\t\"ft_transfer\",\n\t\"ft_mint\",\n\t\"ft_burn\",\n\t\"nft_transfer\",\n\t\"nft_mint\",\n\t\"nft_burn\",\n\t\"contract_call\",\n\t\"contract_deploy\",\n\t\"print_event\",\n\t\"nested_contract_call\",\n\t\"var_set\",\n\t\"map_set\",\n\t\"map_insert\",\n\t\"map_delete\",\n\t\"sbtc_deposit\",\n\t\"sbtc_withdrawal_create\",\n\t\"sbtc_withdrawal_accept\",\n\t\"sbtc_withdrawal_reject\",\n\t\"sbtc_withdrawal_swept_confirmed\",\n] as const;\n\nexport type ChainEventFilterType = (typeof CHAIN_EVENT_FILTER_TYPES)[number];\n",
6
- "import { parsePrincipal } from \"../utils/address.ts\";\n\n/** A validated Stacks principal (standard or contract). Branded so downstream\n * code can require \"already validated\" without re-checking. */\nexport type Principal = string & { readonly __principal: unique symbol };\n\n/**\n * `<contract>::<asset-name>` — the shape a fungible/non-fungible asset filter\n * takes, expressed so the compiler can check it.\n *\n * The most common mistake in this API is passing a CONTRACT ID\n * (`SP….sbtc-token`) where an asset identifier (`SP….sbtc-token::sbtc-token`)\n * belongs, and the failure mode is a query that quietly returns zero rows. The\n * two differ structurally by `::`, so a template-literal type catches it at the\n * call site — no brand, no cast, literals just work.\n *\n * A value that is only known at runtime (config, env) is not narrow enough on\n * purpose; run it through {@link assetId} once, which validates and narrows.\n *\n * Wildcard patterns (Webhooks/Subgraphs-only) are admitted by the second arm — they\n * are legitimately not full identifiers (`SPB.*`), and the runtime validator\n * short-circuits on them for the same reason.\n */\nexport type AssetIdentifier = `${string}::${string}` | `${string}*${string}`;\n\n/**\n * Narrow a runtime string to an {@link AssetIdentifier}, validating it.\n *\n * The escape hatch for config-driven values: `assetId(process.env.ASSET!)`\n * throws on a contract id instead of letting it through to a zero-row query.\n */\nexport function assetId(value: string): AssetIdentifier {\n\tassertAssetIdentifier(\"assetIdentifier\", value);\n\treturn value as AssetIdentifier;\n}\n\n/** `true` for a standard (`SP…`/`ST…`) or contract (`SP….name`) principal.\n * Shares {@link parsePrincipal} with the ABI guards so the two surfaces can't\n * disagree about what a principal is. */\nexport function isPrincipal(value: string): value is Principal {\n\treturn parsePrincipal(value) !== null;\n}\n\n/**\n * Tells whether a value contains a `*` wildcard.\n *\n * Webhooks and Subgraph sources match `*` wildcards in principal and\n * identifier patterns; every other surface treats the value literally.\n */\nexport function hasWildcard(value: string): boolean {\n\treturn value.includes(\"*\");\n}\n\n/** Throw unless `value` is a principal or a wildcard pattern. The factories\n * call this so a swapped argument (asset id where a sender belongs, typo'd\n * address) fails at construction — not as a silent zero-row query. */\nexport function assertPrincipalish(field: string, value: string): void {\n\tif (hasWildcard(value)) return;\n\tif (!isPrincipal(value)) {\n\t\tthrow new Error(\n\t\t\t`${field} \"${value}\" is not a valid Stacks principal (SP…/ST…, optionally .contract-name).`,\n\t\t);\n\t}\n}\n\n/** Throw unless `value` looks like `SP….contract::asset` (or a wildcard). */\nexport function assertAssetIdentifier(field: string, value: string): void {\n\tif (hasWildcard(value)) return;\n\tconst [contractId, assetName, ...rest] = value.split(\"::\");\n\tif (\n\t\t!contractId ||\n\t\t!assetName ||\n\t\trest.length > 0 ||\n\t\t!contractId.includes(\".\") ||\n\t\t!isPrincipal(contractId)\n\t) {\n\t\tthrow new Error(\n\t\t\t`${field} \"${value}\" is not a valid asset identifier (SP….contract-name::asset-name). Passing a bare contract id here matches zero rows — use contractId for that.`,\n\t\t);\n\t}\n}\n\n/** Throw unless `value` is a contract id `SP….name` (or a wildcard). */\nexport function assertContractId(field: string, value: string): void {\n\tif (hasWildcard(value)) return;\n\tif (value.includes(\"::\")) {\n\t\tthrow new Error(\n\t\t\t`${field} \"${value}\" is an asset identifier, not a contract id — use assetIdentifier for that.`,\n\t\t);\n\t}\n\tif (!value.includes(\".\") || !isPrincipal(value)) {\n\t\tthrow new Error(\n\t\t\t`${field} \"${value}\" is not a valid contract id (SP….contract-name).`,\n\t\t);\n\t}\n}\n",
7
- "import type { AbiContract } from \"../clarity/abi/contract.ts\";\nimport type { ChainEventFilterType } from \"./event-types.ts\";\nimport type { SubgraphMemberType, SubgraphSourceSpec } from \"./types.ts\";\nimport type {\n\tChainEventFilter,\n\tChainEventFilterSpec,\n\tChainTriggerShape,\n\tContractCallsParamsShape,\n\tIndexEventsParamsShape,\n\tPrintFieldType,\n\tSpecFor,\n\tStreamsParamsShape,\n\tStreamsVmParamsShape,\n} from \"./types.ts\";\nimport {\n\tassertAssetIdentifier,\n\tassertContractId,\n\tassertPrincipalish,\n\thasWildcard,\n} from \"./validate.ts\";\n\n// ── Field metadata driving validation and projections ────────────────────\n\nconst PRINCIPAL_FIELDS = new Set([\n\t\"sender\",\n\t\"recipient\",\n\t\"caller\",\n\t\"deployer\",\n\t\"lockedAddress\",\n]);\nconst AMOUNT_FIELDS = new Set([\"minAmount\", \"maxAmount\"]);\n/** Fields with no filtering semantics — safe to drop in a projection. */\nconst DECORATIVE_FIELDS = new Set([\"abi\", \"prints\"]);\n\nfunction specEntries(spec: ChainEventFilterSpec): Array<[string, unknown]> {\n\treturn Object.entries(spec).filter(\n\t\t([key, value]) => key !== \"type\" && value !== undefined,\n\t);\n}\n\nfunction validateSpec(spec: ChainEventFilterSpec): void {\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (PRINCIPAL_FIELDS.has(key)) {\n\t\t\tassertPrincipalish(key, value as string);\n\t\t} else if (key === \"assetIdentifier\") {\n\t\t\tassertAssetIdentifier(key, value as string);\n\t\t} else if (key === \"contractId\") {\n\t\t\t// A contract SET validates member-wise — one bad id in a router+pools\n\t\t\t// list must fail as loudly as a bad single id.\n\t\t\tfor (const id of Array.isArray(value) ? value : [value]) {\n\t\t\t\tassertContractId(key, id as string);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Throw for a field the target surface cannot express — dropping it would\n * silently widen the match, which is the exact bug this module exists to\n * kill. Names the surface that CAN express it. */\nfunction unsupported(surface: string, field: string, hint: string): never {\n\tthrow new Error(\n\t\t`${field} cannot be expressed on ${surface} — ${hint}. Drop the field from the filter or use the surface that supports it.`,\n\t);\n}\n\nfunction assertNoWildcards(surface: string, spec: ChainEventFilterSpec): void {\n\tfor (const [key, value] of specEntries(spec)) {\n\t\t// Arrays too: a wildcard inside a contractId set would otherwise reach\n\t\t// the wire as a literal `IN ('SP….pool-*')` — the silent zero-row match\n\t\t// this module exists to kill.\n\t\tconst candidates = Array.isArray(value) ? value : [value];\n\t\tfor (const candidate of candidates) {\n\t\t\tif (typeof candidate === \"string\" && hasWildcard(candidate)) {\n\t\t\t\tunsupported(\n\t\t\t\t\tsurface,\n\t\t\t\t\t`${key} wildcard \"${candidate}\"`,\n\t\t\t\t\t\"wildcard patterns are Webhooks/Subgraphs-only\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** The Index API treats `trait` and `contractId` as mutually exclusive (a\n * trait already resolves to a contract set). Throw here, naming the fix,\n * instead of letting the pair reach the server as a 400. Subgraphs AND them\n * — that surface accepts both. */\nfunction assertNotTraitAndContract(\n\tsurface: string,\n\tspec: ChainEventFilterSpec,\n): void {\n\tconst trait = \"trait\" in spec ? spec.trait : undefined;\n\tconst contract = \"contractId\" in spec ? spec.contractId : undefined;\n\tif (trait !== undefined && contract !== undefined) {\n\t\tunsupported(\n\t\t\tsurface,\n\t\t\t\"trait with contractId\",\n\t\t\t\"the Index treats them as mutually exclusive — drop one, or use a subgraph source, which ANDs the pair\",\n\t\t);\n\t}\n}\n\n// ── Projections ──────────────────────────────────────────────────────────\n\nfunction toChainTrigger(spec: ChainEventFilterSpec): ChainTriggerShape {\n\tconst out: Record<string, string | number> = {};\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (DECORATIVE_FIELDS.has(key)) continue;\n\t\tif (key === \"factory\") {\n\t\t\tunsupported(\n\t\t\t\t\"Webhooks\",\n\t\t\t\t\"factory\",\n\t\t\t\t\"dynamic factory discovery is a Subgraphs-only concept — a trigger targets addresses known when it is created\",\n\t\t\t);\n\t\t}\n\t\tif (Array.isArray(value)) {\n\t\t\t// One trigger, one contract: silently taking the first would watch\n\t\t\t// a fraction of what the filter says. Create one subscription per\n\t\t\t// contract instead.\n\t\t\tunsupported(\n\t\t\t\t\"Webhooks\",\n\t\t\t\t`${key} set`,\n\t\t\t\t\"a chain trigger targets ONE contract — create one subscription per contract, or use Index/Subgraphs which accept the set\",\n\t\t\t);\n\t\t}\n\t\t// The one sanctioned bigint→string boundary.\n\t\tout[key] =\n\t\t\ttypeof value === \"bigint\" ? value.toString() : (value as string | number);\n\t}\n\treturn { type: spec.type, ...out };\n}\n\nfunction toIndexParams(\n\tspec: ChainEventFilterSpec,\n\textra: Record<string, unknown> = {},\n): IndexEventsParamsShape {\n\tassertNoWildcards(\"Index events\", spec);\n\tassertNotTraitAndContract(\"Index events\", spec);\n\tconst out: Record<string, unknown> = {\n\t\teventType: spec.type === \"print_event\" ? \"print\" : spec.type,\n\t};\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (DECORATIVE_FIELDS.has(key)) continue;\n\t\tif (key === \"factory\") {\n\t\t\tunsupported(\n\t\t\t\t\"Index events\",\n\t\t\t\t\"factory\",\n\t\t\t\t\"dynamic factory discovery is a Subgraphs-only concept\",\n\t\t\t);\n\t\t}\n\t\tif (AMOUNT_FIELDS.has(key)) {\n\t\t\tunsupported(\n\t\t\t\t\"Index events\",\n\t\t\t\tkey,\n\t\t\t\t\"amount predicates are Webhooks/Subgraphs-only; filter client-side on the decoded rows\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"topic\") {\n\t\t\tunsupported(\n\t\t\t\t\"Index events\",\n\t\t\t\t\"topic\",\n\t\t\t\t\"per-topic reads are Subgraphs/Webhooks-only (or read the contract's print feed and switch on topic)\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"lockedAddress\") {\n\t\t\t// Index normalizes stx_lock's locked_address INTO the `sender` column\n\t\t\t// (the row's sender IS the locked address) — a rename, not a gap.\n\t\t\tout.sender = value;\n\t\t\tcontinue;\n\t\t}\n\t\tif (key === \"caller\" && spec.type !== \"nested_contract_call\") {\n\t\t\tunsupported(\n\t\t\t\t\"Index events\",\n\t\t\t\t\"caller\",\n\t\t\t\t\"outer contract-call fields live on index.contractCalls\",\n\t\t\t);\n\t\t}\n\t\tout[key] = value;\n\t}\n\treturn { ...out, ...extra } as IndexEventsParamsShape;\n}\n\nconst VM_MEMBERS = new Set<ChainEventFilterType>([\n\t\"nested_contract_call\",\n\t\"var_set\",\n\t\"map_set\",\n\t\"map_insert\",\n\t\"map_delete\",\n]);\n\nconst VM_INDEX_ONLY_FIELDS = new Set([\n\t\"functionName\",\n\t\"caller\",\n\t\"sender\",\n\t\"map\",\n\t\"varName\",\n]);\n\nfunction toStreamsParams(\n\tspec: ChainEventFilterSpec,\n\textra: Record<string, unknown> = {},\n): StreamsParamsShape | StreamsVmParamsShape {\n\tassertNoWildcards(\"Streams\", spec);\n\tconst out: Record<string, unknown> = {\n\t\ttypes: [spec.type === \"print_event\" ? \"print\" : spec.type],\n\t};\n\tconst isVm = VM_MEMBERS.has(spec.type);\n\tif (isVm) out.clock = \"vm\";\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (DECORATIVE_FIELDS.has(key)) continue;\n\t\t// Streams clock=vm has no payload predicates beyond contract_id (the\n\t\t// server rejects sender on clock=vm; function/map/var live on Index).\n\t\tif (isVm && VM_INDEX_ONLY_FIELDS.has(key)) {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\tkey,\n\t\t\t\t\"vm payload predicates are Index/Subgraphs/Subscriptions-only; Streams clock=vm narrows by types and contractId\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"factory\") {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\t\"factory\",\n\t\t\t\t\"dynamic factory discovery is a Subgraphs-only concept\",\n\t\t\t);\n\t\t}\n\t\tif (AMOUNT_FIELDS.has(key)) {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\tkey,\n\t\t\t\t\"amount predicates are Webhooks/Subgraphs-only\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"trait\") {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\t\"trait\",\n\t\t\t\t\"Streams has no trait resolution — Index and Subgraphs do\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"topic\") {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\t\"topic\",\n\t\t\t\t\"per-topic filtering is Subgraphs/Webhooks-only\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"lockedAddress\") {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\t\"lockedAddress\",\n\t\t\t\t\"stx_lock address filtering is Webhooks/Subgraphs-only\",\n\t\t\t);\n\t\t}\n\t\tout[key] = value;\n\t}\n\treturn { ...out, ...extra } as StreamsParamsShape | StreamsVmParamsShape;\n}\n\nfunction toContractCallsParams(\n\tspec: ChainEventFilterSpec,\n\textra: Record<string, unknown> = {},\n): ContractCallsParamsShape {\n\tassertNoWildcards(\"Index contract-calls\", spec);\n\tassertNotTraitAndContract(\"Index contract-calls\", spec);\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (DECORATIVE_FIELDS.has(key)) continue;\n\t\tif (key === \"factory\") {\n\t\t\tunsupported(\n\t\t\t\t\"Index contract-calls\",\n\t\t\t\t\"factory\",\n\t\t\t\t\"dynamic factory discovery is a Subgraphs-only concept\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"caller\") {\n\t\t\t// The endpoint filters by tx sender, which IS the caller — a rename,\n\t\t\t// not a gap.\n\t\t\tout.sender = value;\n\t\t\tcontinue;\n\t\t}\n\t\tout[key] = value;\n\t}\n\treturn { ...out, ...extra } as ContractCallsParamsShape;\n}\n\nfunction toSubgraphSource(spec: ChainEventFilterSpec): ChainEventFilterSpec {\n\t// The spec IS the subgraph source shape (camelCase, bigint amounts,\n\t// literal prints/abi preserved). Strip the projection methods by copying\n\t// data fields only.\n\treturn Object.fromEntries([\n\t\t[\"type\", spec.type],\n\t\t...specEntries(spec),\n\t]) as unknown as ChainEventFilterSpec;\n}\n\n// ── Factory ──────────────────────────────────────────────────────────────\n\nconst DECODED_MEMBERS = new Set<ChainEventFilterType>([\n\t\"stx_transfer\",\n\t\"stx_mint\",\n\t\"stx_burn\",\n\t\"stx_lock\",\n\t\"ft_transfer\",\n\t\"ft_mint\",\n\t\"ft_burn\",\n\t\"nft_transfer\",\n\t\"nft_mint\",\n\t\"nft_burn\",\n\t\"print_event\",\n]);\n\n/**\n * Builds a canonical filter from a member type and its fields.\n *\n * It validates the spec and attaches the projections its member supports.\n * Methods are attached per member at runtime, matching the type-level gating\n * exactly. `const F` preserves field literals, so a `prints` or `abi`\n * declaration keeps its exact type through `toSubgraphSource()`, which is what\n * feeds `defineSubgraph` narrowing.\n */\nexport function makeChainEventFilter<\n\tT extends ChainEventFilterType,\n\tconst F extends Omit<SpecFor<T>, \"type\">,\n>(type: T, fields: F): ChainEventFilter<T, { type: T } & F> {\n\tconst spec = { type, ...fields } as unknown as SpecFor<T>;\n\tvalidateSpec(spec);\n\n\tconst filter = { ...spec } as unknown as Record<string, unknown>;\n\tfilter.toChainTrigger = () => toChainTrigger(spec);\n\tif (DECODED_MEMBERS.has(type) || VM_MEMBERS.has(type)) {\n\t\tfilter.toIndexParams = (extra?: Record<string, unknown>) =>\n\t\t\ttoIndexParams(spec, extra);\n\t\tfilter.toStreamsParams = (extra?: Record<string, unknown>) =>\n\t\t\ttoStreamsParams(spec, extra);\n\t}\n\tif (type === \"contract_call\") {\n\t\tfilter.toContractCallsParams = (extra?: Record<string, unknown>) =>\n\t\t\ttoContractCallsParams(spec, extra);\n\t}\n\tif (!type.startsWith(\"sbtc_\")) {\n\t\tfilter.toSubgraphSource = () => toSubgraphSource(spec);\n\t}\n\treturn filter as unknown as ChainEventFilter<T, { type: T } & F>;\n}\n\n/**\n * Rebuilds a canonical filter from a subgraph source object.\n *\n * It is the inverse of `toSubgraphSource`. It powers migration and the\n * round-trip property gate: `toSubgraphSource(fromSubgraphSource(f))` must\n * deep-equal `f` for every production subgraph source.\n */\nexport function fromSubgraphSource(\n\tsource: SubgraphSourceSpec,\n): ChainEventFilter<SubgraphMemberType> {\n\tconst { type, ...fields } = source;\n\treturn makeChainEventFilter(\n\t\ttype,\n\t\tfields as Omit<SpecFor<typeof type>, \"type\">,\n\t) as unknown as ChainEventFilter<SubgraphMemberType>;\n}\n\ntype Fields<T extends ChainEventFilterType> = Omit<SpecFor<T>, \"type\">;\n\n/** The `on.*` namespace, annotated explicitly — bunup's dts emitter needs an\n * annotation on exported values (an inferred object of generic factories\n * collapses to `{}` in the emitted declarations). */\nexport interface OnNamespace {\n\t/**\n\t * Matches STX transfers.\n\t *\n\t * @param fields The members of {@link StxTransferSpec}, without `type`.\n\t */\n\tstxTransfer(\n\t\tfields?: Fields<\"stx_transfer\">,\n\t): ChainEventFilter<\"stx_transfer\">;\n\t/**\n\t * Matches STX mints.\n\t *\n\t * @param fields The members of {@link StxMintSpec}, without `type`.\n\t */\n\tstxMint(fields?: Fields<\"stx_mint\">): ChainEventFilter<\"stx_mint\">;\n\t/**\n\t * Matches STX burns.\n\t *\n\t * @param fields The members of {@link StxBurnSpec}, without `type`.\n\t */\n\tstxBurn(fields?: Fields<\"stx_burn\">): ChainEventFilter<\"stx_burn\">;\n\t/**\n\t * Matches STX lock events.\n\t *\n\t * @param fields The members of {@link StxLockSpec}, without `type`.\n\t */\n\tstxLock(fields?: Fields<\"stx_lock\">): ChainEventFilter<\"stx_lock\">;\n\t/**\n\t * Matches fungible token transfers.\n\t *\n\t * @param fields The members of {@link FtTransferSpec}, without `type`.\n\t */\n\tftTransfer(fields?: Fields<\"ft_transfer\">): ChainEventFilter<\"ft_transfer\">;\n\t/**\n\t * Matches fungible token mints.\n\t *\n\t * @param fields The members of {@link FtMintSpec}, without `type`.\n\t */\n\tftMint(fields?: Fields<\"ft_mint\">): ChainEventFilter<\"ft_mint\">;\n\t/**\n\t * Matches fungible token burns.\n\t *\n\t * @param fields The members of {@link FtBurnSpec}, without `type`.\n\t */\n\tftBurn(fields?: Fields<\"ft_burn\">): ChainEventFilter<\"ft_burn\">;\n\t/**\n\t * Matches NFT transfers.\n\t *\n\t * @param fields The members of {@link NftTransferSpec}, without `type`.\n\t */\n\tnftTransfer(\n\t\tfields?: Fields<\"nft_transfer\">,\n\t): ChainEventFilter<\"nft_transfer\">;\n\t/**\n\t * Matches NFT mints.\n\t *\n\t * @param fields The members of {@link NftMintSpec}, without `type`.\n\t */\n\tnftMint(fields?: Fields<\"nft_mint\">): ChainEventFilter<\"nft_mint\">;\n\t/**\n\t * Matches NFT burns.\n\t *\n\t * @param fields The members of {@link NftBurnSpec}, without `type`.\n\t */\n\tnftBurn(fields?: Fields<\"nft_burn\">): ChainEventFilter<\"nft_burn\">;\n\t/**\n\t * Matches contract calls.\n\t *\n\t * `abi` literals are preserved (`const A`), so `toSubgraphSource()` keeps\n\t * typing `event.input` inside `defineSubgraph`.\n\t *\n\t * @param fields The members of {@link ContractCallSpec}, without `type`.\n\t */\n\tcontractCall<const A extends AbiContract | undefined = undefined>(\n\t\tfields?: Omit<SpecFor<\"contract_call\">, \"type\" | \"abi\"> & { abi?: A },\n\t): ChainEventFilter<\n\t\t\"contract_call\",\n\t\t{ type: \"contract_call\" } & Omit<\n\t\t\tSpecFor<\"contract_call\">,\n\t\t\t\"type\" | \"abi\"\n\t\t> & {\n\t\t\t\tabi?: A;\n\t\t\t}\n\t>;\n\t/**\n\t * Matches contract deployments.\n\t *\n\t * @param fields The members of {@link ContractDeploySpec}, without `type`.\n\t */\n\tcontractDeploy(\n\t\tfields?: Fields<\"contract_deploy\">,\n\t): ChainEventFilter<\"contract_deploy\">;\n\t/**\n\t * Matches `print` events.\n\t *\n\t * The canonical member is `print_event`, as Subgraphs and Webhooks spell it;\n\t * `toIndexParams` and `toStreamsParams` project to `print`. `prints` literals\n\t * are preserved (`const P`), so `event.data` narrows per topic.\n\t *\n\t * @param fields The members of {@link PrintEventSpec}, without `type`.\n\t */\n\tprint<\n\t\tconst P extends\n\t\t\t| Record<string, Record<string, PrintFieldType>>\n\t\t\t| undefined = undefined,\n\t>(\n\t\tfields?: Omit<SpecFor<\"print_event\">, \"type\" | \"prints\"> & { prints?: P },\n\t): ChainEventFilter<\n\t\t\"print_event\",\n\t\t{ type: \"print_event\" } & Omit<\n\t\t\tSpecFor<\"print_event\">,\n\t\t\t\"type\" | \"prints\"\n\t\t> & {\n\t\t\t\tprints?: P;\n\t\t\t}\n\t>;\n\t/**\n\t * Matches contract calls made from inside another contract.\n\t *\n\t * @param fields The members of {@link NestedContractCallSpec}, without `type`.\n\t */\n\tnestedCall(\n\t\tfields?: Fields<\"nested_contract_call\">,\n\t): ChainEventFilter<\"nested_contract_call\">;\n\t/**\n\t * Matches data variable writes.\n\t *\n\t * @param fields The members of {@link VarSetSpec}, without `type`.\n\t */\n\tvarSet(fields?: Fields<\"var_set\">): ChainEventFilter<\"var_set\">;\n\t/**\n\t * Matches `map-set` writes.\n\t *\n\t * @param fields The members of {@link MapSetSpec}, without `type`.\n\t */\n\tmapSet(fields?: Fields<\"map_set\">): ChainEventFilter<\"map_set\">;\n\t/**\n\t * Matches `map-insert` writes.\n\t *\n\t * @param fields The members of {@link MapInsertSpec}, without `type`.\n\t */\n\tmapInsert(fields?: Fields<\"map_insert\">): ChainEventFilter<\"map_insert\">;\n\t/**\n\t * Matches `map-delete` writes.\n\t *\n\t * @param fields The members of {@link MapDeleteSpec}, without `type`.\n\t */\n\tmapDelete(fields?: Fields<\"map_delete\">): ChainEventFilter<\"map_delete\">;\n\t/**\n\t * Matches sBTC deposits.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcDepositSpec}, without `type`.\n\t */\n\tsbtcDeposit(\n\t\tfields?: Fields<\"sbtc_deposit\">,\n\t): ChainEventFilter<\"sbtc_deposit\">;\n\t/**\n\t * Matches sBTC withdrawal requests.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcWithdrawalCreateSpec}, without `type`.\n\t */\n\tsbtcWithdrawalCreate(\n\t\tfields?: Fields<\"sbtc_withdrawal_create\">,\n\t): ChainEventFilter<\"sbtc_withdrawal_create\">;\n\t/**\n\t * Matches accepted sBTC withdrawals.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcWithdrawalAcceptSpec}, without `type`.\n\t */\n\tsbtcWithdrawalAccept(\n\t\tfields?: Fields<\"sbtc_withdrawal_accept\">,\n\t): ChainEventFilter<\"sbtc_withdrawal_accept\">;\n\t/**\n\t * Matches rejected sBTC withdrawals.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcWithdrawalRejectSpec}, without `type`.\n\t */\n\tsbtcWithdrawalReject(\n\t\tfields?: Fields<\"sbtc_withdrawal_reject\">,\n\t): ChainEventFilter<\"sbtc_withdrawal_reject\">;\n\t/**\n\t * Matches sBTC withdrawals whose sweep has confirmed.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcWithdrawalSweptConfirmedSpec}, without `type`.\n\t */\n\tsbtcWithdrawalSweptConfirmed(\n\t\tfields?: Fields<\"sbtc_withdrawal_swept_confirmed\">,\n\t): ChainEventFilter<\"sbtc_withdrawal_swept_confirmed\">;\n}\n\n/**\n * `on.*` — one filter vocabulary for every surface.\n *\n * ```ts\n * import { on } from \"@secondlayer/stacks/filters\";\n *\n * const usdc = on.ftTransfer({ assetIdentifier: USDC, minAmount: 1_000_000n });\n *\n * sl.index.events.list(usdc.toIndexParams({ limit: 100 })); // pull\n * sl.streams.events.consume({ ...usdc.toStreamsParams(), onBatch });\n * sl.webhooks.create({ name, url, triggers: [usdc.toChainTrigger()] });\n * defineSubgraph({ sources: { usdc: usdc.toSubgraphSource() }, schema, handlers });\n * ```\n *\n * A surface a member can't reach is a missing method (compile error), and a\n * field a surface can't express throws at projection time — never a silent\n * zero-row or over-wide match.\n */\nexport const on: OnNamespace = {\n\tstxTransfer: (fields: Fields<\"stx_transfer\"> = {}) =>\n\t\tmakeChainEventFilter(\"stx_transfer\", fields),\n\tstxMint: (fields: Fields<\"stx_mint\"> = {}) =>\n\t\tmakeChainEventFilter(\"stx_mint\", fields),\n\tstxBurn: (fields: Fields<\"stx_burn\"> = {}) =>\n\t\tmakeChainEventFilter(\"stx_burn\", fields),\n\tstxLock: (fields: Fields<\"stx_lock\"> = {}) =>\n\t\tmakeChainEventFilter(\"stx_lock\", fields),\n\tftTransfer: (fields: Fields<\"ft_transfer\"> = {}) =>\n\t\tmakeChainEventFilter(\"ft_transfer\", fields),\n\tftMint: (fields: Fields<\"ft_mint\"> = {}) =>\n\t\tmakeChainEventFilter(\"ft_mint\", fields),\n\tftBurn: (fields: Fields<\"ft_burn\"> = {}) =>\n\t\tmakeChainEventFilter(\"ft_burn\", fields),\n\tnftTransfer: (fields: Fields<\"nft_transfer\"> = {}) =>\n\t\tmakeChainEventFilter(\"nft_transfer\", fields),\n\tnftMint: (fields: Fields<\"nft_mint\"> = {}) =>\n\t\tmakeChainEventFilter(\"nft_mint\", fields),\n\tnftBurn: (fields: Fields<\"nft_burn\"> = {}) =>\n\t\tmakeChainEventFilter(\"nft_burn\", fields),\n\tcontractCall: <const A extends AbiContract | undefined = undefined>(\n\t\tfields: Omit<SpecFor<\"contract_call\">, \"type\" | \"abi\"> & { abi?: A } = {},\n\t) => makeChainEventFilter(\"contract_call\", fields),\n\tcontractDeploy: (fields: Fields<\"contract_deploy\"> = {}) =>\n\t\tmakeChainEventFilter(\"contract_deploy\", fields),\n\t/** Canonical member is `print_event` (as Subgraphs and Webhooks spell\n\t * it); `toIndexParams`/`toStreamsParams` project to `print`. */\n\tprint: <\n\t\tconst P extends\n\t\t\t| Record<string, Record<string, PrintFieldType>>\n\t\t\t| undefined = undefined,\n\t>(\n\t\tfields: Omit<SpecFor<\"print_event\">, \"type\" | \"prints\"> & {\n\t\t\tprints?: P;\n\t\t} = {},\n\t) => makeChainEventFilter(\"print_event\", fields),\n\tnestedCall: (fields: Fields<\"nested_contract_call\"> = {}) =>\n\t\tmakeChainEventFilter(\"nested_contract_call\", fields),\n\tvarSet: (fields: Fields<\"var_set\"> = {}) =>\n\t\tmakeChainEventFilter(\"var_set\", fields),\n\tmapSet: (fields: Fields<\"map_set\"> = {}) =>\n\t\tmakeChainEventFilter(\"map_set\", fields),\n\tmapInsert: (fields: Fields<\"map_insert\"> = {}) =>\n\t\tmakeChainEventFilter(\"map_insert\", fields),\n\tmapDelete: (fields: Fields<\"map_delete\"> = {}) =>\n\t\tmakeChainEventFilter(\"map_delete\", fields),\n\tsbtcDeposit: (fields: Fields<\"sbtc_deposit\"> = {}) =>\n\t\tmakeChainEventFilter(\"sbtc_deposit\", fields),\n\tsbtcWithdrawalCreate: (fields: Fields<\"sbtc_withdrawal_create\"> = {}) =>\n\t\tmakeChainEventFilter(\"sbtc_withdrawal_create\", fields),\n\tsbtcWithdrawalAccept: (fields: Fields<\"sbtc_withdrawal_accept\"> = {}) =>\n\t\tmakeChainEventFilter(\"sbtc_withdrawal_accept\", fields),\n\tsbtcWithdrawalReject: (fields: Fields<\"sbtc_withdrawal_reject\"> = {}) =>\n\t\tmakeChainEventFilter(\"sbtc_withdrawal_reject\", fields),\n\tsbtcWithdrawalSweptConfirmed: (\n\t\tfields: Fields<\"sbtc_withdrawal_swept_confirmed\"> = {},\n\t) => makeChainEventFilter(\"sbtc_withdrawal_swept_confirmed\", fields),\n};\n"
6
+ "import { parsePrincipal } from \"../utils/address.ts\";\n\n/** A validated Stacks principal (standard or contract). Branded so downstream\n * code can require \"already validated\" without re-checking. */\nexport type Principal = string & { readonly __principal: unique symbol };\n\n/**\n * A fungible or non-fungible asset identifier: `<contract>::<asset-name>`.\n *\n * This is the shape an asset filter takes, expressed so the compiler can check it.\n *\n * The most common mistake in this API is passing a CONTRACT ID\n * (`SP….sbtc-token`) where an asset identifier (`SP….sbtc-token::sbtc-token`)\n * belongs, and the failure mode is a query that quietly returns zero rows. The\n * two differ structurally by `::`, so a template-literal type catches it at the\n * call site. No brand, no cast, literals just work.\n *\n * A value that is only known at runtime (config, env) is not narrow enough on\n * purpose; run it through {@link assetId} once, which validates and narrows.\n *\n * Wildcard patterns (Webhooks/Subgraphs-only) are admitted by the second arm.\n * They are legitimately not full identifiers (`SPB.*`), and the runtime\n * validator short-circuits on them for the same reason.\n */\nexport type AssetIdentifier = `${string}::${string}` | `${string}*${string}`;\n\n/**\n * Narrow a runtime string to an {@link AssetIdentifier}, validating it.\n *\n * The escape hatch for config-driven values: `assetId(process.env.ASSET!)`\n * throws on a contract id instead of letting it through to a zero-row query.\n */\nexport function assetId(value: string): AssetIdentifier {\n\tassertAssetIdentifier(\"assetIdentifier\", value);\n\treturn value as AssetIdentifier;\n}\n\n/** `true` for a standard (`SP…`/`ST…`) or contract (`SP….name`) principal.\n * Shares {@link parsePrincipal} with the ABI guards so the two surfaces can't\n * disagree about what a principal is. */\nexport function isPrincipal(value: string): value is Principal {\n\treturn parsePrincipal(value) !== null;\n}\n\n/**\n * Tells whether a value contains a `*` wildcard.\n *\n * Webhooks and Subgraph sources match `*` wildcards in principal and\n * identifier patterns; every other surface treats the value literally.\n */\nexport function hasWildcard(value: string): boolean {\n\treturn value.includes(\"*\");\n}\n\n/** Throw unless `value` is a principal or a wildcard pattern. The factories\n * call this so a swapped argument (asset id where a sender belongs, typo'd\n * address) fails at construction — not as a silent zero-row query. */\nexport function assertPrincipalish(field: string, value: string): void {\n\tif (hasWildcard(value)) return;\n\tif (!isPrincipal(value)) {\n\t\tthrow new Error(\n\t\t\t`${field} \"${value}\" is not a valid Stacks principal (SP…/ST…, optionally .contract-name).`,\n\t\t);\n\t}\n}\n\n/** Throw unless `value` looks like `SP….contract::asset` (or a wildcard). */\nexport function assertAssetIdentifier(field: string, value: string): void {\n\tif (hasWildcard(value)) return;\n\tconst [contractId, assetName, ...rest] = value.split(\"::\");\n\tif (\n\t\t!contractId ||\n\t\t!assetName ||\n\t\trest.length > 0 ||\n\t\t!contractId.includes(\".\") ||\n\t\t!isPrincipal(contractId)\n\t) {\n\t\tthrow new Error(\n\t\t\t`${field} \"${value}\" is not a valid asset identifier (SP….contract-name::asset-name). Passing a bare contract id here matches zero rows — use contractId for that.`,\n\t\t);\n\t}\n}\n\n/** Throw unless `value` is a contract id `SP….name` (or a wildcard). */\nexport function assertContractId(field: string, value: string): void {\n\tif (hasWildcard(value)) return;\n\tif (value.includes(\"::\")) {\n\t\tthrow new Error(\n\t\t\t`${field} \"${value}\" is an asset identifier, not a contract id — use assetIdentifier for that.`,\n\t\t);\n\t}\n\tif (!value.includes(\".\") || !isPrincipal(value)) {\n\t\tthrow new Error(\n\t\t\t`${field} \"${value}\" is not a valid contract id (SP….contract-name).`,\n\t\t);\n\t}\n}\n",
7
+ "import type { AbiContract } from \"../clarity/abi/contract.ts\";\nimport type { ChainEventFilterType } from \"./event-types.ts\";\nimport type { SubgraphMemberType, SubgraphSourceSpec } from \"./types.ts\";\nimport type {\n\tChainEventFilter,\n\tChainEventFilterSpec,\n\tChainTriggerShape,\n\tContractCallsParamsShape,\n\tIndexEventsParamsShape,\n\tPrintFieldType,\n\tSpecFor,\n\tStreamsParamsShape,\n\tStreamsVmParamsShape,\n} from \"./types.ts\";\nimport {\n\tassertAssetIdentifier,\n\tassertContractId,\n\tassertPrincipalish,\n\thasWildcard,\n} from \"./validate.ts\";\n\n// ── Field metadata driving validation and projections ────────────────────\n\nconst PRINCIPAL_FIELDS = new Set([\n\t\"sender\",\n\t\"recipient\",\n\t\"caller\",\n\t\"deployer\",\n\t\"lockedAddress\",\n]);\nconst AMOUNT_FIELDS = new Set([\"minAmount\", \"maxAmount\"]);\n/** Fields with no filtering semantics — safe to drop in a projection. */\nconst DECORATIVE_FIELDS = new Set([\"abi\", \"prints\"]);\n\nfunction specEntries(spec: ChainEventFilterSpec): Array<[string, unknown]> {\n\treturn Object.entries(spec).filter(\n\t\t([key, value]) => key !== \"type\" && value !== undefined,\n\t);\n}\n\nfunction validateSpec(spec: ChainEventFilterSpec): void {\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (PRINCIPAL_FIELDS.has(key)) {\n\t\t\tassertPrincipalish(key, value as string);\n\t\t} else if (key === \"assetIdentifier\") {\n\t\t\tassertAssetIdentifier(key, value as string);\n\t\t} else if (key === \"contractId\") {\n\t\t\t// A contract SET validates member-wise — one bad id in a router+pools\n\t\t\t// list must fail as loudly as a bad single id.\n\t\t\tfor (const id of Array.isArray(value) ? value : [value]) {\n\t\t\t\tassertContractId(key, id as string);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** Throw for a field the target surface cannot express — dropping it would\n * silently widen the match, which is the exact bug this module exists to\n * kill. Names the surface that CAN express it. */\nfunction unsupported(surface: string, field: string, hint: string): never {\n\tthrow new Error(\n\t\t`${field} cannot be expressed on ${surface} — ${hint}. Drop the field from the filter or use the surface that supports it.`,\n\t);\n}\n\nfunction assertNoWildcards(surface: string, spec: ChainEventFilterSpec): void {\n\tfor (const [key, value] of specEntries(spec)) {\n\t\t// Arrays too: a wildcard inside a contractId set would otherwise reach\n\t\t// the wire as a literal `IN ('SP….pool-*')` — the silent zero-row match\n\t\t// this module exists to kill.\n\t\tconst candidates = Array.isArray(value) ? value : [value];\n\t\tfor (const candidate of candidates) {\n\t\t\tif (typeof candidate === \"string\" && hasWildcard(candidate)) {\n\t\t\t\tunsupported(\n\t\t\t\t\tsurface,\n\t\t\t\t\t`${key} wildcard \"${candidate}\"`,\n\t\t\t\t\t\"wildcard patterns are Webhooks/Subgraphs-only\",\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t}\n}\n\n/** The Index API treats `trait` and `contractId` as mutually exclusive (a\n * trait already resolves to a contract set). Throw here, naming the fix,\n * instead of letting the pair reach the server as a 400. Subgraphs AND them\n * — that surface accepts both. */\nfunction assertNotTraitAndContract(\n\tsurface: string,\n\tspec: ChainEventFilterSpec,\n): void {\n\tconst trait = \"trait\" in spec ? spec.trait : undefined;\n\tconst contract = \"contractId\" in spec ? spec.contractId : undefined;\n\tif (trait !== undefined && contract !== undefined) {\n\t\tunsupported(\n\t\t\tsurface,\n\t\t\t\"trait with contractId\",\n\t\t\t\"the Index treats them as mutually exclusive — drop one, or use a subgraph source, which ANDs the pair\",\n\t\t);\n\t}\n}\n\n// ── Projections ──────────────────────────────────────────────────────────\n\nfunction toChainTrigger(spec: ChainEventFilterSpec): ChainTriggerShape {\n\tconst out: Record<string, string | number> = {};\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (DECORATIVE_FIELDS.has(key)) continue;\n\t\tif (key === \"factory\") {\n\t\t\tunsupported(\n\t\t\t\t\"Webhooks\",\n\t\t\t\t\"factory\",\n\t\t\t\t\"dynamic factory discovery is a Subgraphs-only concept — a trigger targets addresses known when it is created\",\n\t\t\t);\n\t\t}\n\t\tif (Array.isArray(value)) {\n\t\t\t// One trigger, one contract: silently taking the first would watch\n\t\t\t// a fraction of what the filter says. Create one subscription per\n\t\t\t// contract instead.\n\t\t\tunsupported(\n\t\t\t\t\"Webhooks\",\n\t\t\t\t`${key} set`,\n\t\t\t\t\"a chain trigger targets ONE contract — create one subscription per contract, or use Index/Subgraphs which accept the set\",\n\t\t\t);\n\t\t}\n\t\t// The one sanctioned bigint→string boundary.\n\t\tout[key] =\n\t\t\ttypeof value === \"bigint\" ? value.toString() : (value as string | number);\n\t}\n\treturn { type: spec.type, ...out };\n}\n\nfunction toIndexParams(\n\tspec: ChainEventFilterSpec,\n\textra: Record<string, unknown> = {},\n): IndexEventsParamsShape {\n\tassertNoWildcards(\"Index events\", spec);\n\tassertNotTraitAndContract(\"Index events\", spec);\n\tconst out: Record<string, unknown> = {\n\t\teventType: spec.type === \"print_event\" ? \"print\" : spec.type,\n\t};\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (DECORATIVE_FIELDS.has(key)) continue;\n\t\tif (key === \"factory\") {\n\t\t\tunsupported(\n\t\t\t\t\"Index events\",\n\t\t\t\t\"factory\",\n\t\t\t\t\"dynamic factory discovery is a Subgraphs-only concept\",\n\t\t\t);\n\t\t}\n\t\tif (AMOUNT_FIELDS.has(key)) {\n\t\t\tunsupported(\n\t\t\t\t\"Index events\",\n\t\t\t\tkey,\n\t\t\t\t\"amount predicates are Webhooks/Subgraphs-only; filter client-side on the decoded rows\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"topic\") {\n\t\t\tunsupported(\n\t\t\t\t\"Index events\",\n\t\t\t\t\"topic\",\n\t\t\t\t\"per-topic reads are Subgraphs/Webhooks-only (or read the contract's print feed and switch on topic)\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"lockedAddress\") {\n\t\t\t// Index normalizes stx_lock's locked_address INTO the `sender` column\n\t\t\t// (the row's sender IS the locked address) — a rename, not a gap.\n\t\t\tout.sender = value;\n\t\t\tcontinue;\n\t\t}\n\t\tif (key === \"caller\" && spec.type !== \"nested_contract_call\") {\n\t\t\tunsupported(\n\t\t\t\t\"Index events\",\n\t\t\t\t\"caller\",\n\t\t\t\t\"outer contract-call fields live on index.contractCalls\",\n\t\t\t);\n\t\t}\n\t\tout[key] = value;\n\t}\n\treturn { ...out, ...extra } as IndexEventsParamsShape;\n}\n\nconst VM_MEMBERS = new Set<ChainEventFilterType>([\n\t\"nested_contract_call\",\n\t\"var_set\",\n\t\"map_set\",\n\t\"map_insert\",\n\t\"map_delete\",\n]);\n\nconst VM_INDEX_ONLY_FIELDS = new Set([\n\t\"functionName\",\n\t\"caller\",\n\t\"sender\",\n\t\"map\",\n\t\"varName\",\n]);\n\nfunction toStreamsParams(\n\tspec: ChainEventFilterSpec,\n\textra: Record<string, unknown> = {},\n): StreamsParamsShape | StreamsVmParamsShape {\n\tassertNoWildcards(\"Streams\", spec);\n\tconst out: Record<string, unknown> = {\n\t\ttypes: [spec.type === \"print_event\" ? \"print\" : spec.type],\n\t};\n\tconst isVm = VM_MEMBERS.has(spec.type);\n\tif (isVm) out.clock = \"vm\";\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (DECORATIVE_FIELDS.has(key)) continue;\n\t\t// Streams clock=vm has no payload predicates beyond contract_id (the\n\t\t// server rejects sender on clock=vm; function/map/var live on Index).\n\t\tif (isVm && VM_INDEX_ONLY_FIELDS.has(key)) {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\tkey,\n\t\t\t\t\"vm payload predicates are Index/Subgraphs/Subscriptions-only; Streams clock=vm narrows by types and contractId\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"factory\") {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\t\"factory\",\n\t\t\t\t\"dynamic factory discovery is a Subgraphs-only concept\",\n\t\t\t);\n\t\t}\n\t\tif (AMOUNT_FIELDS.has(key)) {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\tkey,\n\t\t\t\t\"amount predicates are Webhooks/Subgraphs-only\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"trait\") {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\t\"trait\",\n\t\t\t\t\"Streams has no trait resolution — Index and Subgraphs do\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"topic\") {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\t\"topic\",\n\t\t\t\t\"per-topic filtering is Subgraphs/Webhooks-only\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"lockedAddress\") {\n\t\t\tunsupported(\n\t\t\t\t\"Streams\",\n\t\t\t\t\"lockedAddress\",\n\t\t\t\t\"stx_lock address filtering is Webhooks/Subgraphs-only\",\n\t\t\t);\n\t\t}\n\t\tout[key] = value;\n\t}\n\treturn { ...out, ...extra } as StreamsParamsShape | StreamsVmParamsShape;\n}\n\nfunction toContractCallsParams(\n\tspec: ChainEventFilterSpec,\n\textra: Record<string, unknown> = {},\n): ContractCallsParamsShape {\n\tassertNoWildcards(\"Index contract-calls\", spec);\n\tassertNotTraitAndContract(\"Index contract-calls\", spec);\n\tconst out: Record<string, unknown> = {};\n\tfor (const [key, value] of specEntries(spec)) {\n\t\tif (DECORATIVE_FIELDS.has(key)) continue;\n\t\tif (key === \"factory\") {\n\t\t\tunsupported(\n\t\t\t\t\"Index contract-calls\",\n\t\t\t\t\"factory\",\n\t\t\t\t\"dynamic factory discovery is a Subgraphs-only concept\",\n\t\t\t);\n\t\t}\n\t\tif (key === \"caller\") {\n\t\t\t// The endpoint filters by tx sender, which IS the caller — a rename,\n\t\t\t// not a gap.\n\t\t\tout.sender = value;\n\t\t\tcontinue;\n\t\t}\n\t\tout[key] = value;\n\t}\n\treturn { ...out, ...extra } as ContractCallsParamsShape;\n}\n\nfunction toSubgraphSource(spec: ChainEventFilterSpec): ChainEventFilterSpec {\n\t// The spec IS the subgraph source shape (camelCase, bigint amounts,\n\t// literal prints/abi preserved). Strip the projection methods by copying\n\t// data fields only.\n\treturn Object.fromEntries([\n\t\t[\"type\", spec.type],\n\t\t...specEntries(spec),\n\t]) as unknown as ChainEventFilterSpec;\n}\n\n// ── Factory ──────────────────────────────────────────────────────────────\n\nconst DECODED_MEMBERS = new Set<ChainEventFilterType>([\n\t\"stx_transfer\",\n\t\"stx_mint\",\n\t\"stx_burn\",\n\t\"stx_lock\",\n\t\"ft_transfer\",\n\t\"ft_mint\",\n\t\"ft_burn\",\n\t\"nft_transfer\",\n\t\"nft_mint\",\n\t\"nft_burn\",\n\t\"print_event\",\n]);\n\n/**\n * Builds a canonical filter from a member type and its fields.\n *\n * It validates the spec and attaches the projections its member supports.\n * Methods are attached per member at runtime, matching the type-level gating\n * exactly. `const F` preserves field literals, so a `prints` or `abi`\n * declaration keeps its exact type through `toSubgraphSource()`, which is what\n * feeds `defineSubgraph` narrowing.\n */\nexport function makeChainEventFilter<\n\tT extends ChainEventFilterType,\n\tconst F extends Omit<SpecFor<T>, \"type\">,\n>(type: T, fields: F): ChainEventFilter<T, { type: T } & F> {\n\tconst spec = { type, ...fields } as unknown as SpecFor<T>;\n\tvalidateSpec(spec);\n\n\tconst filter = { ...spec } as unknown as Record<string, unknown>;\n\tfilter.toChainTrigger = () => toChainTrigger(spec);\n\tif (DECODED_MEMBERS.has(type) || VM_MEMBERS.has(type)) {\n\t\tfilter.toIndexParams = (extra?: Record<string, unknown>) =>\n\t\t\ttoIndexParams(spec, extra);\n\t\tfilter.toStreamsParams = (extra?: Record<string, unknown>) =>\n\t\t\ttoStreamsParams(spec, extra);\n\t}\n\tif (type === \"contract_call\") {\n\t\tfilter.toContractCallsParams = (extra?: Record<string, unknown>) =>\n\t\t\ttoContractCallsParams(spec, extra);\n\t}\n\tif (!type.startsWith(\"sbtc_\")) {\n\t\tfilter.toSubgraphSource = () => toSubgraphSource(spec);\n\t}\n\treturn filter as unknown as ChainEventFilter<T, { type: T } & F>;\n}\n\n/**\n * Rebuilds a canonical filter from a subgraph source object.\n *\n * It is the inverse of `toSubgraphSource`. It powers migration and the\n * round-trip property gate: `toSubgraphSource(fromSubgraphSource(f))` must\n * deep-equal `f` for every production subgraph source.\n */\nexport function fromSubgraphSource(\n\tsource: SubgraphSourceSpec,\n): ChainEventFilter<SubgraphMemberType> {\n\tconst { type, ...fields } = source;\n\treturn makeChainEventFilter(\n\t\ttype,\n\t\tfields as Omit<SpecFor<typeof type>, \"type\">,\n\t) as unknown as ChainEventFilter<SubgraphMemberType>;\n}\n\n/** The fields a filter factory takes for one event type, without `type`. */\ntype Fields<T extends ChainEventFilterType> = Omit<SpecFor<T>, \"type\">;\n\n/** The `on.*` namespace, annotated explicitly — bunup's dts emitter needs an\n * annotation on exported values (an inferred object of generic factories\n * collapses to `{}` in the emitted declarations). */\nexport interface OnNamespace {\n\t/**\n\t * Matches STX transfers.\n\t *\n\t * @param fields The members of {@link StxTransferSpec}, without `type`.\n\t */\n\tstxTransfer(\n\t\tfields?: Fields<\"stx_transfer\">,\n\t): ChainEventFilter<\"stx_transfer\">;\n\t/**\n\t * Matches STX mints.\n\t *\n\t * @param fields The members of {@link StxMintSpec}, without `type`.\n\t */\n\tstxMint(fields?: Fields<\"stx_mint\">): ChainEventFilter<\"stx_mint\">;\n\t/**\n\t * Matches STX burns.\n\t *\n\t * @param fields The members of {@link StxBurnSpec}, without `type`.\n\t */\n\tstxBurn(fields?: Fields<\"stx_burn\">): ChainEventFilter<\"stx_burn\">;\n\t/**\n\t * Matches STX lock events.\n\t *\n\t * @param fields The members of {@link StxLockSpec}, without `type`.\n\t */\n\tstxLock(fields?: Fields<\"stx_lock\">): ChainEventFilter<\"stx_lock\">;\n\t/**\n\t * Matches fungible token transfers.\n\t *\n\t * @param fields The members of {@link FtTransferSpec}, without `type`.\n\t */\n\tftTransfer(fields?: Fields<\"ft_transfer\">): ChainEventFilter<\"ft_transfer\">;\n\t/**\n\t * Matches fungible token mints.\n\t *\n\t * @param fields The members of {@link FtMintSpec}, without `type`.\n\t */\n\tftMint(fields?: Fields<\"ft_mint\">): ChainEventFilter<\"ft_mint\">;\n\t/**\n\t * Matches fungible token burns.\n\t *\n\t * @param fields The members of {@link FtBurnSpec}, without `type`.\n\t */\n\tftBurn(fields?: Fields<\"ft_burn\">): ChainEventFilter<\"ft_burn\">;\n\t/**\n\t * Matches NFT transfers.\n\t *\n\t * @param fields The members of {@link NftTransferSpec}, without `type`.\n\t */\n\tnftTransfer(\n\t\tfields?: Fields<\"nft_transfer\">,\n\t): ChainEventFilter<\"nft_transfer\">;\n\t/**\n\t * Matches NFT mints.\n\t *\n\t * @param fields The members of {@link NftMintSpec}, without `type`.\n\t */\n\tnftMint(fields?: Fields<\"nft_mint\">): ChainEventFilter<\"nft_mint\">;\n\t/**\n\t * Matches NFT burns.\n\t *\n\t * @param fields The members of {@link NftBurnSpec}, without `type`.\n\t */\n\tnftBurn(fields?: Fields<\"nft_burn\">): ChainEventFilter<\"nft_burn\">;\n\t/**\n\t * Matches contract calls.\n\t *\n\t * `abi` literals are preserved (`const A`), so `toSubgraphSource()` keeps\n\t * typing `event.input` inside `defineSubgraph`.\n\t *\n\t * @param fields The members of {@link ContractCallSpec}, without `type`.\n\t */\n\tcontractCall<const A extends AbiContract | undefined = undefined>(\n\t\tfields?: Omit<SpecFor<\"contract_call\">, \"type\" | \"abi\"> & { abi?: A },\n\t): ChainEventFilter<\n\t\t\"contract_call\",\n\t\t{ type: \"contract_call\" } & Omit<\n\t\t\tSpecFor<\"contract_call\">,\n\t\t\t\"type\" | \"abi\"\n\t\t> & {\n\t\t\t\tabi?: A;\n\t\t\t}\n\t>;\n\t/**\n\t * Matches contract deployments.\n\t *\n\t * @param fields The members of {@link ContractDeploySpec}, without `type`.\n\t */\n\tcontractDeploy(\n\t\tfields?: Fields<\"contract_deploy\">,\n\t): ChainEventFilter<\"contract_deploy\">;\n\t/**\n\t * Matches `print` events.\n\t *\n\t * The canonical member is `print_event`, as Subgraphs and Webhooks spell it;\n\t * `toIndexParams` and `toStreamsParams` project to `print`. `prints` literals\n\t * are preserved (`const P`), so `event.data` narrows per topic.\n\t *\n\t * @param fields The members of {@link PrintEventSpec}, without `type`.\n\t */\n\tprint<\n\t\tconst P extends\n\t\t\t| Record<string, Record<string, PrintFieldType>>\n\t\t\t| undefined = undefined,\n\t>(\n\t\tfields?: Omit<SpecFor<\"print_event\">, \"type\" | \"prints\"> & { prints?: P },\n\t): ChainEventFilter<\n\t\t\"print_event\",\n\t\t{ type: \"print_event\" } & Omit<\n\t\t\tSpecFor<\"print_event\">,\n\t\t\t\"type\" | \"prints\"\n\t\t> & {\n\t\t\t\tprints?: P;\n\t\t\t}\n\t>;\n\t/**\n\t * Matches contract calls made from inside another contract.\n\t *\n\t * @param fields The members of {@link NestedContractCallSpec}, without `type`.\n\t */\n\tnestedCall(\n\t\tfields?: Fields<\"nested_contract_call\">,\n\t): ChainEventFilter<\"nested_contract_call\">;\n\t/**\n\t * Matches data variable writes.\n\t *\n\t * @param fields The members of {@link VarSetSpec}, without `type`.\n\t */\n\tvarSet(fields?: Fields<\"var_set\">): ChainEventFilter<\"var_set\">;\n\t/**\n\t * Matches `map-set` writes.\n\t *\n\t * @param fields The members of {@link MapSetSpec}, without `type`.\n\t */\n\tmapSet(fields?: Fields<\"map_set\">): ChainEventFilter<\"map_set\">;\n\t/**\n\t * Matches `map-insert` writes.\n\t *\n\t * @param fields The members of {@link MapInsertSpec}, without `type`.\n\t */\n\tmapInsert(fields?: Fields<\"map_insert\">): ChainEventFilter<\"map_insert\">;\n\t/**\n\t * Matches `map-delete` writes.\n\t *\n\t * @param fields The members of {@link MapDeleteSpec}, without `type`.\n\t */\n\tmapDelete(fields?: Fields<\"map_delete\">): ChainEventFilter<\"map_delete\">;\n\t/**\n\t * Matches sBTC deposits.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcDepositSpec}, without `type`.\n\t */\n\tsbtcDeposit(\n\t\tfields?: Fields<\"sbtc_deposit\">,\n\t): ChainEventFilter<\"sbtc_deposit\">;\n\t/**\n\t * Matches sBTC withdrawal requests.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcWithdrawalCreateSpec}, without `type`.\n\t */\n\tsbtcWithdrawalCreate(\n\t\tfields?: Fields<\"sbtc_withdrawal_create\">,\n\t): ChainEventFilter<\"sbtc_withdrawal_create\">;\n\t/**\n\t * Matches accepted sBTC withdrawals.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcWithdrawalAcceptSpec}, without `type`.\n\t */\n\tsbtcWithdrawalAccept(\n\t\tfields?: Fields<\"sbtc_withdrawal_accept\">,\n\t): ChainEventFilter<\"sbtc_withdrawal_accept\">;\n\t/**\n\t * Matches rejected sBTC withdrawals.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcWithdrawalRejectSpec}, without `type`.\n\t */\n\tsbtcWithdrawalReject(\n\t\tfields?: Fields<\"sbtc_withdrawal_reject\">,\n\t): ChainEventFilter<\"sbtc_withdrawal_reject\">;\n\t/**\n\t * Matches sBTC withdrawals whose sweep has confirmed.\n\t *\n\t * Webhooks only: it has no Index, Streams or subgraph projection.\n\t *\n\t * @param fields The members of {@link SbtcWithdrawalSweptConfirmedSpec}, without `type`.\n\t */\n\tsbtcWithdrawalSweptConfirmed(\n\t\tfields?: Fields<\"sbtc_withdrawal_swept_confirmed\">,\n\t): ChainEventFilter<\"sbtc_withdrawal_swept_confirmed\">;\n}\n\n/**\n * `on.*` — one filter vocabulary for every surface.\n *\n * ```ts\n * import { on } from \"@secondlayer/stacks/filters\";\n *\n * const usdc = on.ftTransfer({ assetIdentifier: USDC, minAmount: 1_000_000n });\n *\n * sl.index.events.list(usdc.toIndexParams({ limit: 100 })); // pull\n * sl.streams.events.consume({ ...usdc.toStreamsParams(), onBatch });\n * sl.webhooks.create({ name, url, triggers: [usdc.toChainTrigger()] });\n * defineSubgraph({ sources: { usdc: usdc.toSubgraphSource() }, schema, handlers });\n * ```\n *\n * A surface a member can't reach is a missing method (compile error), and a\n * field a surface can't express throws at projection time — never a silent\n * zero-row or over-wide match.\n */\nexport const on: OnNamespace = {\n\tstxTransfer: (fields: Fields<\"stx_transfer\"> = {}) =>\n\t\tmakeChainEventFilter(\"stx_transfer\", fields),\n\tstxMint: (fields: Fields<\"stx_mint\"> = {}) =>\n\t\tmakeChainEventFilter(\"stx_mint\", fields),\n\tstxBurn: (fields: Fields<\"stx_burn\"> = {}) =>\n\t\tmakeChainEventFilter(\"stx_burn\", fields),\n\tstxLock: (fields: Fields<\"stx_lock\"> = {}) =>\n\t\tmakeChainEventFilter(\"stx_lock\", fields),\n\tftTransfer: (fields: Fields<\"ft_transfer\"> = {}) =>\n\t\tmakeChainEventFilter(\"ft_transfer\", fields),\n\tftMint: (fields: Fields<\"ft_mint\"> = {}) =>\n\t\tmakeChainEventFilter(\"ft_mint\", fields),\n\tftBurn: (fields: Fields<\"ft_burn\"> = {}) =>\n\t\tmakeChainEventFilter(\"ft_burn\", fields),\n\tnftTransfer: (fields: Fields<\"nft_transfer\"> = {}) =>\n\t\tmakeChainEventFilter(\"nft_transfer\", fields),\n\tnftMint: (fields: Fields<\"nft_mint\"> = {}) =>\n\t\tmakeChainEventFilter(\"nft_mint\", fields),\n\tnftBurn: (fields: Fields<\"nft_burn\"> = {}) =>\n\t\tmakeChainEventFilter(\"nft_burn\", fields),\n\tcontractCall: <const A extends AbiContract | undefined = undefined>(\n\t\tfields: Omit<SpecFor<\"contract_call\">, \"type\" | \"abi\"> & { abi?: A } = {},\n\t) => makeChainEventFilter(\"contract_call\", fields),\n\tcontractDeploy: (fields: Fields<\"contract_deploy\"> = {}) =>\n\t\tmakeChainEventFilter(\"contract_deploy\", fields),\n\t/** Canonical member is `print_event` (as Subgraphs and Webhooks spell\n\t * it); `toIndexParams`/`toStreamsParams` project to `print`. */\n\tprint: <\n\t\tconst P extends\n\t\t\t| Record<string, Record<string, PrintFieldType>>\n\t\t\t| undefined = undefined,\n\t>(\n\t\tfields: Omit<SpecFor<\"print_event\">, \"type\" | \"prints\"> & {\n\t\t\tprints?: P;\n\t\t} = {},\n\t) => makeChainEventFilter(\"print_event\", fields),\n\tnestedCall: (fields: Fields<\"nested_contract_call\"> = {}) =>\n\t\tmakeChainEventFilter(\"nested_contract_call\", fields),\n\tvarSet: (fields: Fields<\"var_set\"> = {}) =>\n\t\tmakeChainEventFilter(\"var_set\", fields),\n\tmapSet: (fields: Fields<\"map_set\"> = {}) =>\n\t\tmakeChainEventFilter(\"map_set\", fields),\n\tmapInsert: (fields: Fields<\"map_insert\"> = {}) =>\n\t\tmakeChainEventFilter(\"map_insert\", fields),\n\tmapDelete: (fields: Fields<\"map_delete\"> = {}) =>\n\t\tmakeChainEventFilter(\"map_delete\", fields),\n\tsbtcDeposit: (fields: Fields<\"sbtc_deposit\"> = {}) =>\n\t\tmakeChainEventFilter(\"sbtc_deposit\", fields),\n\tsbtcWithdrawalCreate: (fields: Fields<\"sbtc_withdrawal_create\"> = {}) =>\n\t\tmakeChainEventFilter(\"sbtc_withdrawal_create\", fields),\n\tsbtcWithdrawalAccept: (fields: Fields<\"sbtc_withdrawal_accept\"> = {}) =>\n\t\tmakeChainEventFilter(\"sbtc_withdrawal_accept\", fields),\n\tsbtcWithdrawalReject: (fields: Fields<\"sbtc_withdrawal_reject\"> = {}) =>\n\t\tmakeChainEventFilter(\"sbtc_withdrawal_reject\", fields),\n\tsbtcWithdrawalSweptConfirmed: (\n\t\tfields: Fields<\"sbtc_withdrawal_swept_confirmed\"> = {},\n\t) => makeChainEventFilter(\"sbtc_withdrawal_swept_confirmed\", fields),\n};\n"
8
8
  ],
9
- "mappings": ";;;;;AAMO,IAAM,uBAAsB;AAAA,EAClC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAOO,IAAM,kBAAiB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAQO,IAAM,0BAAyB;AAAA,EACrC,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,eAAe;AAAA,EACf,kBAAkB;AAAA,EAClB,kBAAkB;AACnB;AAQO,IAAM,4BAA2B;AAAA,EACvC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;;AC9CO,SAAS,QAAO,CAAC,OAAgC;AAAA,EACvD,uBAAsB,mBAAmB,KAAK;AAAA,EAC9C,OAAO;AAAA;AAMD,SAAS,YAAW,CAAC,OAAmC;AAAA,EAC9D,OAAO,gBAAe,KAAK,MAAM;AAAA;AAS3B,SAAS,YAAW,CAAC,OAAwB;AAAA,EACnD,OAAO,MAAM,SAAS,GAAG;AAAA;AAMnB,SAAS,mBAAkB,CAAC,OAAe,OAAqB;AAAA,EACtE,IAAI,aAAY,KAAK;AAAA,IAAG;AAAA,EACxB,IAAI,CAAC,aAAY,KAAK,GAAG;AAAA,IACxB,MAAM,IAAI,MACT,GAAG,UAAU,8EACd;AAAA,EACD;AAAA;AAIM,SAAS,sBAAqB,CAAC,OAAe,OAAqB;AAAA,EACzE,IAAI,aAAY,KAAK;AAAA,IAAG;AAAA,EACxB,OAAO,YAAY,cAAc,QAAQ,MAAM,MAAM,IAAI;AAAA,EACzD,IACC,CAAC,cACD,CAAC,aACD,KAAK,SAAS,KACd,CAAC,WAAW,SAAS,GAAG,KACxB,CAAC,aAAY,UAAU,GACtB;AAAA,IACD,MAAM,IAAI,MACT,GAAG,UAAU,sJACd;AAAA,EACD;AAAA;AAIM,SAAS,iBAAgB,CAAC,OAAe,OAAqB;AAAA,EACpE,IAAI,aAAY,KAAK;AAAA,IAAG;AAAA,EACxB,IAAI,MAAM,SAAS,IAAI,GAAG;AAAA,IACzB,MAAM,IAAI,MACT,GAAG,UAAU,kFACd;AAAA,EACD;AAAA,EACA,IAAI,CAAC,MAAM,SAAS,GAAG,KAAK,CAAC,aAAY,KAAK,GAAG;AAAA,IAChD,MAAM,IAAI,MACT,GAAG,UAAU,wDACd;AAAA,EACD;AAAA;;;ACvED,IAAM,mBAAmB,IAAI,IAAI;AAAA,EAChC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AACD,IAAM,gBAAgB,IAAI,IAAI,CAAC,aAAa,WAAW,CAAC;AAExD,IAAM,oBAAoB,IAAI,IAAI,CAAC,OAAO,QAAQ,CAAC;AAEnD,SAAS,WAAW,CAAC,MAAsD;AAAA,EAC1E,OAAO,OAAO,QAAQ,IAAI,EAAE,OAC3B,EAAE,KAAK,WAAW,QAAQ,UAAU,UAAU,SAC/C;AAAA;AAGD,SAAS,YAAY,CAAC,MAAkC;AAAA,EACvD,YAAY,KAAK,UAAU,YAAY,IAAI,GAAG;AAAA,IAC7C,IAAI,iBAAiB,IAAI,GAAG,GAAG;AAAA,MAC9B,oBAAmB,KAAK,KAAe;AAAA,IACxC,EAAO,SAAI,QAAQ,mBAAmB;AAAA,MACrC,uBAAsB,KAAK,KAAe;AAAA,IAC3C,EAAO,SAAI,QAAQ,cAAc;AAAA,MAGhC,WAAW,MAAM,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK,GAAG;AAAA,QACxD,kBAAiB,KAAK,EAAY;AAAA,MACnC;AAAA,IACD;AAAA,EACD;AAAA;AAMD,SAAS,WAAW,CAAC,SAAiB,OAAe,MAAqB;AAAA,EACzE,MAAM,IAAI,MACT,GAAG,gCAAgC,aAAa,2EACjD;AAAA;AAGD,SAAS,iBAAiB,CAAC,SAAiB,MAAkC;AAAA,EAC7E,YAAY,KAAK,UAAU,YAAY,IAAI,GAAG;AAAA,IAI7C,MAAM,aAAa,MAAM,QAAQ,KAAK,IAAI,QAAQ,CAAC,KAAK;AAAA,IACxD,WAAW,aAAa,YAAY;AAAA,MACnC,IAAI,OAAO,cAAc,YAAY,aAAY,SAAS,GAAG;AAAA,QAC5D,YACC,SACA,GAAG,iBAAiB,cACpB,+CACD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAAA;AAOD,SAAS,yBAAyB,CACjC,SACA,MACO;AAAA,EACP,MAAM,QAAQ,WAAW,OAAO,KAAK,QAAQ;AAAA,EAC7C,MAAM,WAAW,gBAAgB,OAAO,KAAK,aAAa;AAAA,EAC1D,IAAI,UAAU,aAAa,aAAa,WAAW;AAAA,IAClD,YACC,SACA,yBACA,uGACD;AAAA,EACD;AAAA;AAKD,SAAS,cAAc,CAAC,MAA+C;AAAA,EACtE,MAAM,MAAuC,CAAC;AAAA,EAC9C,YAAY,KAAK,UAAU,YAAY,IAAI,GAAG;AAAA,IAC7C,IAAI,kBAAkB,IAAI,GAAG;AAAA,MAAG;AAAA,IAChC,IAAI,QAAQ,WAAW;AAAA,MACtB,YACC,YACA,WACA,8GACD;AAAA,IACD;AAAA,IACA,IAAI,MAAM,QAAQ,KAAK,GAAG;AAAA,MAIzB,YACC,YACA,GAAG,WACH,0HACD;AAAA,IACD;AAAA,IAEA,IAAI,OACH,OAAO,UAAU,WAAW,MAAM,SAAS,IAAK;AAAA,EAClD;AAAA,EACA,OAAO,EAAE,MAAM,KAAK,SAAS,IAAI;AAAA;AAGlC,SAAS,aAAa,CACrB,MACA,QAAiC,CAAC,GACT;AAAA,EACzB,kBAAkB,gBAAgB,IAAI;AAAA,EACtC,0BAA0B,gBAAgB,IAAI;AAAA,EAC9C,MAAM,MAA+B;AAAA,IACpC,WAAW,KAAK,SAAS,gBAAgB,UAAU,KAAK;AAAA,EACzD;AAAA,EACA,YAAY,KAAK,UAAU,YAAY,IAAI,GAAG;AAAA,IAC7C,IAAI,kBAAkB,IAAI,GAAG;AAAA,MAAG;AAAA,IAChC,IAAI,QAAQ,WAAW;AAAA,MACtB,YACC,gBACA,WACA,uDACD;AAAA,IACD;AAAA,IACA,IAAI,cAAc,IAAI,GAAG,GAAG;AAAA,MAC3B,YACC,gBACA,KACA,uFACD;AAAA,IACD;AAAA,IACA,IAAI,QAAQ,SAAS;AAAA,MACpB,YACC,gBACA,SACA,qGACD;AAAA,IACD;AAAA,IACA,IAAI,QAAQ,iBAAiB;AAAA,MAG5B,IAAI,SAAS;AAAA,MACb;AAAA,IACD;AAAA,IACA,IAAI,QAAQ,YAAY,KAAK,SAAS,wBAAwB;AAAA,MAC7D,YACC,gBACA,UACA,wDACD;AAAA,IACD;AAAA,IACA,IAAI,OAAO;AAAA,EACZ;AAAA,EACA,OAAO,KAAK,QAAQ,MAAM;AAAA;AAG3B,IAAM,aAAa,IAAI,IAA0B;AAAA,EAChD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAED,IAAM,uBAAuB,IAAI,IAAI;AAAA,EACpC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAED,SAAS,eAAe,CACvB,MACA,QAAiC,CAAC,GACU;AAAA,EAC5C,kBAAkB,WAAW,IAAI;AAAA,EACjC,MAAM,MAA+B;AAAA,IACpC,OAAO,CAAC,KAAK,SAAS,gBAAgB,UAAU,KAAK,IAAI;AAAA,EAC1D;AAAA,EACA,MAAM,OAAO,WAAW,IAAI,KAAK,IAAI;AAAA,EACrC,IAAI;AAAA,IAAM,IAAI,QAAQ;AAAA,EACtB,YAAY,KAAK,UAAU,YAAY,IAAI,GAAG;AAAA,IAC7C,IAAI,kBAAkB,IAAI,GAAG;AAAA,MAAG;AAAA,IAGhC,IAAI,QAAQ,qBAAqB,IAAI,GAAG,GAAG;AAAA,MAC1C,YACC,WACA,KACA,gHACD;AAAA,IACD;AAAA,IACA,IAAI,QAAQ,WAAW;AAAA,MACtB,YACC,WACA,WACA,uDACD;AAAA,IACD;AAAA,IACA,IAAI,cAAc,IAAI,GAAG,GAAG;AAAA,MAC3B,YACC,WACA,KACA,+CACD;AAAA,IACD;AAAA,IACA,IAAI,QAAQ,SAAS;AAAA,MACpB,YACC,WACA,SACA,0DACD;AAAA,IACD;AAAA,IACA,IAAI,QAAQ,SAAS;AAAA,MACpB,YACC,WACA,SACA,gDACD;AAAA,IACD;AAAA,IACA,IAAI,QAAQ,iBAAiB;AAAA,MAC5B,YACC,WACA,iBACA,uDACD;AAAA,IACD;AAAA,IACA,IAAI,OAAO;AAAA,EACZ;AAAA,EACA,OAAO,KAAK,QAAQ,MAAM;AAAA;AAG3B,SAAS,qBAAqB,CAC7B,MACA,QAAiC,CAAC,GACP;AAAA,EAC3B,kBAAkB,wBAAwB,IAAI;AAAA,EAC9C,0BAA0B,wBAAwB,IAAI;AAAA,EACtD,MAAM,MAA+B,CAAC;AAAA,EACtC,YAAY,KAAK,UAAU,YAAY,IAAI,GAAG;AAAA,IAC7C,IAAI,kBAAkB,IAAI,GAAG;AAAA,MAAG;AAAA,IAChC,IAAI,QAAQ,WAAW;AAAA,MACtB,YACC,wBACA,WACA,uDACD;AAAA,IACD;AAAA,IACA,IAAI,QAAQ,UAAU;AAAA,MAGrB,IAAI,SAAS;AAAA,MACb;AAAA,IACD;AAAA,IACA,IAAI,OAAO;AAAA,EACZ;AAAA,EACA,OAAO,KAAK,QAAQ,MAAM;AAAA;AAG3B,SAAS,gBAAgB,CAAC,MAAkD;AAAA,EAI3E,OAAO,OAAO,YAAY;AAAA,IACzB,CAAC,QAAQ,KAAK,IAAI;AAAA,IAClB,GAAG,YAAY,IAAI;AAAA,EACpB,CAAC;AAAA;AAKF,IAAM,kBAAkB,IAAI,IAA0B;AAAA,EACrD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD,CAAC;AAWM,SAAS,qBAGf,CAAC,MAAS,QAAiD;AAAA,EAC3D,MAAM,OAAO,EAAE,SAAS,OAAO;AAAA,EAC/B,aAAa,IAAI;AAAA,EAEjB,MAAM,SAAS,KAAK,KAAK;AAAA,EACzB,OAAO,iBAAiB,MAAM,eAAe,IAAI;AAAA,EACjD,IAAI,gBAAgB,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,GAAG;AAAA,IACtD,OAAO,gBAAgB,CAAC,UACvB,cAAc,MAAM,KAAK;AAAA,IAC1B,OAAO,kBAAkB,CAAC,UACzB,gBAAgB,MAAM,KAAK;AAAA,EAC7B;AAAA,EACA,IAAI,SAAS,iBAAiB;AAAA,IAC7B,OAAO,wBAAwB,CAAC,UAC/B,sBAAsB,MAAM,KAAK;AAAA,EACnC;AAAA,EACA,IAAI,CAAC,KAAK,WAAW,OAAO,GAAG;AAAA,IAC9B,OAAO,mBAAmB,MAAM,iBAAiB,IAAI;AAAA,EACtD;AAAA,EACA,OAAO;AAAA;AAUD,SAAS,mBAAkB,CACjC,QACuC;AAAA,EACvC,QAAQ,SAAS,WAAW;AAAA,EAC5B,OAAO,sBACN,MACA,MACD;AAAA;AAkOM,IAAM,MAAkB;AAAA,EAC9B,aAAa,CAAC,SAAiC,CAAC,MAC/C,sBAAqB,gBAAgB,MAAM;AAAA,EAC5C,SAAS,CAAC,SAA6B,CAAC,MACvC,sBAAqB,YAAY,MAAM;AAAA,EACxC,SAAS,CAAC,SAA6B,CAAC,MACvC,sBAAqB,YAAY,MAAM;AAAA,EACxC,SAAS,CAAC,SAA6B,CAAC,MACvC,sBAAqB,YAAY,MAAM;AAAA,EACxC,YAAY,CAAC,SAAgC,CAAC,MAC7C,sBAAqB,eAAe,MAAM;AAAA,EAC3C,QAAQ,CAAC,SAA4B,CAAC,MACrC,sBAAqB,WAAW,MAAM;AAAA,EACvC,QAAQ,CAAC,SAA4B,CAAC,MACrC,sBAAqB,WAAW,MAAM;AAAA,EACvC,aAAa,CAAC,SAAiC,CAAC,MAC/C,sBAAqB,gBAAgB,MAAM;AAAA,EAC5C,SAAS,CAAC,SAA6B,CAAC,MACvC,sBAAqB,YAAY,MAAM;AAAA,EACxC,SAAS,CAAC,SAA6B,CAAC,MACvC,sBAAqB,YAAY,MAAM;AAAA,EACxC,cAAc,CACb,SAAuE,CAAC,MACpE,sBAAqB,iBAAiB,MAAM;AAAA,EACjD,gBAAgB,CAAC,SAAoC,CAAC,MACrD,sBAAqB,mBAAmB,MAAM;AAAA,EAG/C,OAAO,CAKN,SAEI,CAAC,MACD,sBAAqB,eAAe,MAAM;AAAA,EAC/C,YAAY,CAAC,SAAyC,CAAC,MACtD,sBAAqB,wBAAwB,MAAM;AAAA,EACpD,QAAQ,CAAC,SAA4B,CAAC,MACrC,sBAAqB,WAAW,MAAM;AAAA,EACvC,QAAQ,CAAC,SAA4B,CAAC,MACrC,sBAAqB,WAAW,MAAM;AAAA,EACvC,WAAW,CAAC,SAA+B,CAAC,MAC3C,sBAAqB,cAAc,MAAM;AAAA,EAC1C,WAAW,CAAC,SAA+B,CAAC,MAC3C,sBAAqB,cAAc,MAAM;AAAA,EAC1C,aAAa,CAAC,SAAiC,CAAC,MAC/C,sBAAqB,gBAAgB,MAAM;AAAA,EAC5C,sBAAsB,CAAC,SAA2C,CAAC,MAClE,sBAAqB,0BAA0B,MAAM;AAAA,EACtD,sBAAsB,CAAC,SAA2C,CAAC,MAClE,sBAAqB,0BAA0B,MAAM;AAAA,EACtD,sBAAsB,CAAC,SAA2C,CAAC,MAClE,sBAAqB,0BAA0B,MAAM;AAAA,EACtD,8BAA8B,CAC7B,SAAoD,CAAC,MACjD,sBAAqB,mCAAmC,MAAM;AACpE;",
9
+ "mappings": "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",
10
10
  "debugId": "B5D7ACE96FE5280F64756E2164756E21",
11
11
  "names": []
12
12
  }
@@ -73,6 +73,14 @@ function rootFromProof2(leafInternal, proof) {
73
73
  }
74
74
 
75
75
  // src/bitcoin/proof.ts
76
+ class BitcoinSourceError2 extends Error {
77
+ code;
78
+ name = "BitcoinSourceError";
79
+ constructor(code, message) {
80
+ super(message);
81
+ this.code = code;
82
+ }
83
+ }
76
84
  function normalizeTxid(txid) {
77
85
  return without0x2(txid).toLowerCase();
78
86
  }
@@ -149,22 +157,32 @@ function bitcoinRpcSource2(config) {
149
157
  const basic = typeof config.auth === "string" ? config.auth : btoa(`${config.auth.username}:${config.auth.password}`);
150
158
  headers.authorization = `Basic ${basic}`;
151
159
  }
152
- const res = await doFetch(config.url, {
153
- method: "POST",
154
- headers,
155
- body: JSON.stringify({
156
- jsonrpc: "1.0",
157
- id: "secondlayer",
158
- method,
159
- params: rpcParams
160
- })
161
- });
162
- if (!res.ok) {
163
- throw new Error(`bitcoin rpc ${method} failed: HTTP ${res.status}`);
160
+ let res;
161
+ try {
162
+ res = await doFetch(config.url, {
163
+ method: "POST",
164
+ headers,
165
+ body: JSON.stringify({
166
+ jsonrpc: "1.0",
167
+ id: "secondlayer",
168
+ method,
169
+ params: rpcParams
170
+ })
171
+ });
172
+ } catch (error) {
173
+ throw new BitcoinSourceError2("unavailable", `bitcoin rpc ${method} failed: ${error instanceof Error ? error.message : "network error"}`);
174
+ }
175
+ let json;
176
+ try {
177
+ json = await res.json();
178
+ } catch {
179
+ throw new BitcoinSourceError2("unavailable", `bitcoin rpc ${method} failed: HTTP ${res.status}`);
164
180
  }
165
- const json = await res.json();
166
181
  if (json.error) {
167
- throw new Error(`bitcoin rpc ${method} error: ${json.error.message}`);
182
+ throw new BitcoinSourceError2(json.error.code === -5 ? "not-found" : "unavailable", `bitcoin rpc ${method} error: ${json.error.message}`);
183
+ }
184
+ if (!res.ok) {
185
+ throw new BitcoinSourceError2("unavailable", `bitcoin rpc ${method} failed: HTTP ${res.status}`);
168
186
  }
169
187
  return json.result;
170
188
  }
@@ -178,7 +196,7 @@ function bitcoinRpcSource2(config) {
178
196
  true
179
197
  ]);
180
198
  if (!tx.blockhash) {
181
- throw new Error(`tx ${txid} is not in a block (node needs -txindex, or the tx is unconfirmed)`);
199
+ throw new BitcoinSourceError2("unconfirmed", `tx ${txid} is not in a block (node needs -txindex, or the tx is unconfirmed)`);
182
200
  }
183
201
  const block = await rpc("getblock", [
184
202
  tx.blockhash,
@@ -199,9 +217,14 @@ function esploraSource2(config) {
199
217
  const doFetch = config.fetch ?? fetch;
200
218
  const base = config.url.replace(/\/+$/, "");
201
219
  async function get(path) {
202
- const res = await doFetch(`${base}${path}`);
220
+ let res;
221
+ try {
222
+ res = await doFetch(`${base}${path}`);
223
+ } catch (error) {
224
+ throw new BitcoinSourceError2("unavailable", `esplora GET ${path} failed: ${error instanceof Error ? error.message : "network error"}`);
225
+ }
203
226
  if (!res.ok) {
204
- throw new Error(`esplora GET ${path} failed: HTTP ${res.status}`);
227
+ throw new BitcoinSourceError2(res.status === 404 ? "not-found" : "unavailable", `esplora GET ${path} failed: HTTP ${res.status}`);
205
228
  }
206
229
  return res;
207
230
  }
@@ -212,7 +235,7 @@ function esploraSource2(config) {
212
235
  async getBlockForTx(txid) {
213
236
  const status = await (await get(`/tx/${txid}`)).json();
214
237
  if (!status.status.confirmed) {
215
- throw new Error(`tx ${txid} is unconfirmed`);
238
+ throw new BitcoinSourceError2("unconfirmed", `tx ${txid} is unconfirmed`);
216
239
  }
217
240
  const blockHashHex = status.status.block_hash;
218
241
  const header = (await (await get(`/block/${blockHashHex}/header`)).text()).trim();
@@ -231,7 +254,7 @@ function esploraSource2(config) {
231
254
  // src/epochs.ts
232
255
  var EPOCH_4_ACTIVATION_BURN_HEIGHT_MAINNET2 = 960230;
233
256
 
234
- export { merkleRoot2, buildMerkleProof2, rootFromProof2, buildTxProof2, fallbackProofSource2, bitcoinRpcSource2, esploraSource2, EPOCH_4_ACTIVATION_BURN_HEIGHT_MAINNET2 };
257
+ export { merkleRoot2, buildMerkleProof2, rootFromProof2, BitcoinSourceError2, buildTxProof2, fallbackProofSource2, bitcoinRpcSource2, esploraSource2, EPOCH_4_ACTIVATION_BURN_HEIGHT_MAINNET2 };
235
258
 
236
- //# debugId=058A5CB7C5A2CAEE64756E2164756E21
237
- //# sourceMappingURL=stacks-esm-nt5hf8ht.js.map
259
+ //# debugId=4CD23767D82ECDDE64756E2164756E21
260
+ //# sourceMappingURL=stacks-esm-z0rq3ejt.js.map
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/bitcoin/merkle.ts", "../src/bitcoin/proof.ts", "../src/epochs.ts"],
4
+ "sourcesContent": [
5
+ "import { concatBytes } from \"../utils/encoding.ts\";\nimport { doubleSha256 } from \"./serialize.ts\";\n\n/**\n * A Bitcoin merkle inclusion proof, shaped for the SIP-044 `verify-merkle-proof`\n * built-in: `(leaf-hash, root-hash, tx-index, tx-count, sibling-hashes)`.\n *\n * - `siblings` are the sibling node hashes from the leaf up to (excluding) the\n * root, in *internal* byte order — never reversed.\n * - `txCount` (not tree-depth) pins the canonical tree shape; the built-in\n * rejects any proof whose length differs from `ceil(log2(tx-count))`.\n */\nexport interface MerkleProof {\n\tsiblings: Uint8Array[];\n\ttxIndex: number;\n\ttxCount: number;\n}\n\n/** Combine an ordered pair of nodes into their parent hash. */\nfunction hashPair(left: Uint8Array, right: Uint8Array): Uint8Array {\n\treturn doubleSha256(concatBytes(left, right));\n}\n\n/** Collapse one merkle level into the next, duplicating the last node if odd. */\nfunction nextLevel(level: Uint8Array[]): Uint8Array[] {\n\tconst next: Uint8Array[] = [];\n\tfor (let i = 0; i < level.length; i += 2) {\n\t\tconst left = level[i];\n\t\tif (left === undefined) throw new Error(\"merkle: missing left node\");\n\t\t// Bitcoin duplicates the final node when a level has an odd count.\n\t\tconst right = level[i + 1] ?? left;\n\t\tnext.push(hashPair(left, right));\n\t}\n\treturn next;\n}\n\n/**\n * Compute the merkle root over txids in *internal* byte order. The result is\n * also internal order — it matches the `merkle-root` field read straight out of\n * an 80-byte block header, so it can be cross-checked against the header before\n * a proof is trusted.\n */\nexport function merkleRoot(txidsInternal: Uint8Array[]): Uint8Array {\n\tif (txidsInternal.length === 0) {\n\t\tthrow new Error(\"merkleRoot: empty tx list\");\n\t}\n\tlet level = txidsInternal.slice();\n\twhile (level.length > 1) {\n\t\tlevel = nextLevel(level);\n\t}\n\tconst root = level[0];\n\tif (root === undefined) throw new Error(\"merkleRoot: no root\");\n\treturn root;\n}\n\n/**\n * Build the merkle inclusion proof for the tx at `txIndex`. Sibling count is\n * exactly `ceil(log2(txCount))`, as the SIP-044 built-in requires.\n */\nexport function buildMerkleProof(\n\ttxidsInternal: Uint8Array[],\n\ttxIndex: number,\n): MerkleProof {\n\tconst txCount = txidsInternal.length;\n\tif (txCount === 0) {\n\t\tthrow new Error(\"buildMerkleProof: empty tx list\");\n\t}\n\tif (txIndex < 0 || txIndex >= txCount) {\n\t\tthrow new Error(\n\t\t\t`buildMerkleProof: txIndex ${txIndex} out of range for ${txCount} txs`,\n\t\t);\n\t}\n\n\tconst siblings: Uint8Array[] = [];\n\tlet index = txIndex;\n\tlet level = txidsInternal.slice();\n\twhile (level.length > 1) {\n\t\tconst isRight = index % 2 === 1;\n\t\tconst siblingIndex = isRight ? index - 1 : index + 1;\n\t\tconst self = level[index];\n\t\tif (self === undefined) throw new Error(\"buildMerkleProof: missing node\");\n\t\t// When the node is the last of an odd level, it is paired with itself.\n\t\tsiblings.push(level[siblingIndex] ?? self);\n\t\tlevel = nextLevel(level);\n\t\tindex = Math.floor(index / 2);\n\t}\n\n\treturn { siblings, txIndex, txCount };\n}\n\n/**\n * Recompute the merkle root from a leaf + its proof — the same fold the on-chain\n * `verify-merkle-proof` performs. Use it off-chain to self-check a constructed\n * proof against the header's merkle root before submitting a contract call.\n */\nexport function rootFromProof(\n\tleafInternal: Uint8Array,\n\tproof: MerkleProof,\n): Uint8Array {\n\tlet hash = leafInternal;\n\tlet index = proof.txIndex;\n\tfor (const sibling of proof.siblings) {\n\t\thash = index % 2 === 1 ? hashPair(sibling, hash) : hashPair(hash, sibling);\n\t\tindex = Math.floor(index / 2);\n\t}\n\treturn hash;\n}\n",
6
+ "import { bytesToHex, hexToBytes, without0x } from \"../utils/encoding.ts\";\nimport { type MerkleProof, buildMerkleProof, rootFromProof } from \"./merkle.ts\";\nimport { parseBitcoinTx, parseBlockHeader, reverseBytes } from \"./serialize.ts\";\n\n/**\n * A self-contained Bitcoin SPV proof: everything the SIP-044 built-ins need to\n * prove a tx (and one of its outputs) is committed in a confirmed block. Hashes\n * are internal byte order throughout.\n */\nexport interface SpvProof {\n\trawTx: Uint8Array;\n\t/** The tx's txid, internal order — the merkle leaf. */\n\ttxidInternal: Uint8Array;\n\t/** Output index of interest, if the proof targets a specific output. */\n\tvout?: number;\n\tmerkle: MerkleProof;\n\t/** The 80-byte block header that commits the tx. */\n\theader: Uint8Array;\n\t/** Bitcoin block height. */\n\theight: number;\n}\n\n/** The block context a `ProofSource` resolves for a confirmed tx. */\nexport interface BlockForTx {\n\t/** 80-byte block header. */\n\theader: Uint8Array;\n\theight: number;\n\t/** All of the block's txids, internal order, in block order. */\n\ttxidsInternal: Uint8Array[];\n\t/** Index of the target tx within the block. */\n\ttxIndex: number;\n}\n\n/**\n * Where proof inputs come from. The default is the integrator's own Bitcoin node\n * (`bitcoinRpcSource`) — trustless; a hosted Esplora-compatible endpoint\n * (`esploraSource`) is the fallback. `buildTxProof` independently re-checks\n * whatever a source returns, so a wrong or hostile source fails loudly rather\n * than producing a bad proof.\n */\nexport interface ProofSource {\n\t/** Raw (serialized) tx bytes for a txid (display-order hex). */\n\tgetRawTx(txid: string): Promise<Uint8Array>;\n\t/** The confirming block's header, height, and txid set for a txid. */\n\tgetBlockForTx(txid: string): Promise<BlockForTx>;\n}\n\n/** Why a source could not produce a proof input. */\nexport type BitcoinSourceErrorCode =\n\t| \"not-found\"\n\t| \"unconfirmed\"\n\t| \"unavailable\";\n\n/**\n * Thrown by `bitcoinRpcSource` / `esploraSource` when the source itself cannot\n * answer: the tx is unknown, not yet in a block, or the source is unreachable.\n * Inconsistent data from a source is a plain `Error` (the source lied).\n */\nexport class BitcoinSourceError extends Error {\n\toverride readonly name = \"BitcoinSourceError\";\n\tconstructor(\n\t\treadonly code: BitcoinSourceErrorCode,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t}\n}\n\nfunction normalizeTxid(txid: string): string {\n\treturn without0x(txid).toLowerCase();\n}\n\n/**\n * Locate `txid` in a block's txid set and assemble its `BlockForTx`. Owns the\n * display→internal byte-order reversal (`reverseBytes`) so the ProofSources\n * can't drift on the one operation a silent merkle-root mismatch would hide.\n * `txidsDisplay` are display-order hex txids as returned by Core / Esplora.\n */\nfunction assembleBlockForTx(args: {\n\ttxid: string;\n\tblockId: string;\n\theaderHex: string;\n\theight: number;\n\ttxidsDisplay: string[];\n}): BlockForTx {\n\tconst txIndex = args.txidsDisplay.indexOf(normalizeTxid(args.txid));\n\tif (txIndex < 0) {\n\t\tthrow new Error(`tx ${args.txid} not found in block ${args.blockId}`);\n\t}\n\treturn {\n\t\theader: hexToBytes(args.headerHex),\n\t\theight: args.height,\n\t\ttxidsInternal: args.txidsDisplay.map((t) => reverseBytes(hexToBytes(t))),\n\t\ttxIndex,\n\t};\n}\n\n/**\n * Assemble an `SpvProof` for a txid from a `ProofSource`, validating every claim\n * the source makes:\n * - the returned raw tx actually hashes to the requested txid,\n * - the claimed `txIndex` points at that txid in the block, and\n * - the resulting merkle proof folds back to the block header's merkle root.\n * Any mismatch throws — the proof is never returned half-trusted.\n */\nexport async function buildTxProof(\n\tsource: ProofSource,\n\tparams: { txid: string; vout?: number },\n): Promise<SpvProof> {\n\tconst { txid, vout } = params;\n\tconst want = normalizeTxid(txid);\n\n\tconst [rawTx, block] = await Promise.all([\n\t\tsource.getRawTx(txid),\n\t\tsource.getBlockForTx(txid),\n\t]);\n\n\tconst parsed = parseBitcoinTx(rawTx);\n\tconst gotDisplay = bytesToHex(reverseBytes(parsed.txidInternal));\n\tif (gotDisplay !== want) {\n\t\tthrow new Error(\n\t\t\t`source returned a tx whose id ${gotDisplay} does not match requested ${want}`,\n\t\t);\n\t}\n\n\tconst atIndex = block.txidsInternal[block.txIndex];\n\tif (!atIndex || bytesToHex(atIndex) !== bytesToHex(parsed.txidInternal)) {\n\t\tthrow new Error(\n\t\t\t`source txIndex ${block.txIndex} does not point at tx ${want}`,\n\t\t);\n\t}\n\n\tconst merkle = buildMerkleProof(block.txidsInternal, block.txIndex);\n\n\tconst headerRoot = parseBlockHeader(block.header).merkleRoot;\n\tconst computed = rootFromProof(parsed.txidInternal, merkle);\n\tif (bytesToHex(computed) !== bytesToHex(headerRoot)) {\n\t\tthrow new Error(\n\t\t\t\"constructed merkle proof does not reconcile with the block header merkle root\",\n\t\t);\n\t}\n\n\treturn {\n\t\trawTx,\n\t\ttxidInternal: parsed.txidInternal,\n\t\tvout,\n\t\tmerkle,\n\t\theader: block.header,\n\t\theight: block.height,\n\t};\n}\n\n/**\n * Compose sources into an ordered fallback chain: each call tries them in turn\n * and returns the first success, throwing the last error if all fail. Put the\n * trustless integrator node first and a hosted endpoint last.\n */\nexport function fallbackProofSource(sources: ProofSource[]): ProofSource {\n\tif (sources.length === 0) {\n\t\tthrow new Error(\"fallbackProofSource: at least one source is required\");\n\t}\n\tasync function firstSuccess<T>(\n\t\tfn: (s: ProofSource) => Promise<T>,\n\t): Promise<T> {\n\t\tlet lastError: unknown;\n\t\tfor (const source of sources) {\n\t\t\ttry {\n\t\t\t\treturn await fn(source);\n\t\t\t} catch (error) {\n\t\t\t\tlastError = error;\n\t\t\t}\n\t\t}\n\t\tthrow lastError;\n\t}\n\treturn {\n\t\tgetRawTx: (txid) => firstSuccess((s) => s.getRawTx(txid)),\n\t\tgetBlockForTx: (txid) => firstSuccess((s) => s.getBlockForTx(txid)),\n\t};\n}\n\nexport interface BitcoinRpcConfig {\n\t/** Bitcoin Core JSON-RPC endpoint URL. */\n\turl: string;\n\t/** Basic auth — `{ username, password }` or a pre-encoded base64 string. */\n\tauth?: { username: string; password: string } | string;\n\t/** Override the fetch implementation (testing / custom agents). */\n\tfetch?: typeof fetch;\n}\n\n/**\n * A `ProofSource` backed by the integrator's own Bitcoin Core node over\n * JSON-RPC. This is the trustless default. The node must run with `-txindex`\n * (or the tx must be in the mempool's block view) so `getrawtransaction` can\n * resolve the confirming block.\n */\nexport function bitcoinRpcSource(config: BitcoinRpcConfig): ProofSource {\n\tconst doFetch = config.fetch ?? fetch;\n\n\tasync function rpc<T>(method: string, rpcParams: unknown[]): Promise<T> {\n\t\tconst headers: Record<string, string> = {\n\t\t\t\"content-type\": \"application/json\",\n\t\t};\n\t\tif (config.auth) {\n\t\t\tconst basic =\n\t\t\t\ttypeof config.auth === \"string\"\n\t\t\t\t\t? config.auth\n\t\t\t\t\t: btoa(`${config.auth.username}:${config.auth.password}`);\n\t\t\theaders.authorization = `Basic ${basic}`;\n\t\t}\n\t\tlet res: Response;\n\t\ttry {\n\t\t\tres = await doFetch(config.url, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders,\n\t\t\t\tbody: JSON.stringify({\n\t\t\t\t\tjsonrpc: \"1.0\",\n\t\t\t\t\tid: \"secondlayer\",\n\t\t\t\t\tmethod,\n\t\t\t\t\tparams: rpcParams,\n\t\t\t\t}),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tthrow new BitcoinSourceError(\n\t\t\t\t\"unavailable\",\n\t\t\t\t`bitcoin rpc ${method} failed: ${error instanceof Error ? error.message : \"network error\"}`,\n\t\t\t);\n\t\t}\n\t\t// Core answers RPC errors with HTTP 500 and the error in the JSON body.\n\t\tlet json: { result: T; error: { code?: number; message: string } | null };\n\t\ttry {\n\t\t\tjson = (await res.json()) as typeof json;\n\t\t} catch {\n\t\t\tthrow new BitcoinSourceError(\n\t\t\t\t\"unavailable\",\n\t\t\t\t`bitcoin rpc ${method} failed: HTTP ${res.status}`,\n\t\t\t);\n\t\t}\n\t\tif (json.error) {\n\t\t\tthrow new BitcoinSourceError(\n\t\t\t\tjson.error.code === -5 ? \"not-found\" : \"unavailable\",\n\t\t\t\t`bitcoin rpc ${method} error: ${json.error.message}`,\n\t\t\t);\n\t\t}\n\t\tif (!res.ok) {\n\t\t\tthrow new BitcoinSourceError(\n\t\t\t\t\"unavailable\",\n\t\t\t\t`bitcoin rpc ${method} failed: HTTP ${res.status}`,\n\t\t\t);\n\t\t}\n\t\treturn json.result;\n\t}\n\n\treturn {\n\t\tasync getRawTx(txid) {\n\t\t\treturn hexToBytes(await rpc<string>(\"getrawtransaction\", [txid, false]));\n\t\t},\n\t\tasync getBlockForTx(txid) {\n\t\t\tconst tx = await rpc<{ blockhash?: string }>(\"getrawtransaction\", [\n\t\t\t\ttxid,\n\t\t\t\ttrue,\n\t\t\t]);\n\t\t\tif (!tx.blockhash) {\n\t\t\t\tthrow new BitcoinSourceError(\n\t\t\t\t\t\"unconfirmed\",\n\t\t\t\t\t`tx ${txid} is not in a block (node needs -txindex, or the tx is unconfirmed)`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst block = await rpc<{ tx: string[]; height: number }>(\"getblock\", [\n\t\t\t\ttx.blockhash,\n\t\t\t\t1,\n\t\t\t]);\n\t\t\tconst header = await rpc<string>(\"getblockheader\", [tx.blockhash, false]);\n\t\t\treturn assembleBlockForTx({\n\t\t\t\ttxid,\n\t\t\t\tblockId: tx.blockhash,\n\t\t\t\theaderHex: header,\n\t\t\t\theight: block.height,\n\t\t\t\ttxidsDisplay: block.tx,\n\t\t\t});\n\t\t},\n\t};\n}\n\nexport interface EsploraConfig {\n\t/** Esplora REST base URL, e.g. `https://blockstream.info/api` or a self-hosted instance. */\n\turl: string;\n\t/** Override the fetch implementation. */\n\tfetch?: typeof fetch;\n}\n\n/**\n * A `ProofSource` backed by an Esplora REST API (self-hosted, or a hosted\n * provider as a fallback). Provider-agnostic: any Esplora-compatible endpoint\n * works. Use as the hosted fallback behind `bitcoinRpcSource`.\n */\nexport function esploraSource(config: EsploraConfig): ProofSource {\n\tconst doFetch = config.fetch ?? fetch;\n\tconst base = config.url.replace(/\\/+$/, \"\");\n\n\tasync function get(path: string): Promise<Response> {\n\t\tlet res: Response;\n\t\ttry {\n\t\t\tres = await doFetch(`${base}${path}`);\n\t\t} catch (error) {\n\t\t\tthrow new BitcoinSourceError(\n\t\t\t\t\"unavailable\",\n\t\t\t\t`esplora GET ${path} failed: ${error instanceof Error ? error.message : \"network error\"}`,\n\t\t\t);\n\t\t}\n\t\tif (!res.ok) {\n\t\t\tthrow new BitcoinSourceError(\n\t\t\t\tres.status === 404 ? \"not-found\" : \"unavailable\",\n\t\t\t\t`esplora GET ${path} failed: HTTP ${res.status}`,\n\t\t\t);\n\t\t}\n\t\treturn res;\n\t}\n\n\treturn {\n\t\tasync getRawTx(txid) {\n\t\t\treturn hexToBytes((await (await get(`/tx/${txid}/hex`)).text()).trim());\n\t\t},\n\t\tasync getBlockForTx(txid) {\n\t\t\tconst status = (await (await get(`/tx/${txid}`)).json()) as {\n\t\t\t\tstatus: {\n\t\t\t\t\tconfirmed: boolean;\n\t\t\t\t\tblock_height: number;\n\t\t\t\t\tblock_hash: string;\n\t\t\t\t};\n\t\t\t};\n\t\t\tif (!status.status.confirmed) {\n\t\t\t\tthrow new BitcoinSourceError(\n\t\t\t\t\t\"unconfirmed\",\n\t\t\t\t\t`tx ${txid} is unconfirmed`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst blockHashHex = status.status.block_hash;\n\t\t\tconst header = (\n\t\t\t\tawait (await get(`/block/${blockHashHex}/header`)).text()\n\t\t\t).trim();\n\t\t\tconst txids = (await (\n\t\t\t\tawait get(`/block/${blockHashHex}/txids`)\n\t\t\t).json()) as string[];\n\t\t\treturn assembleBlockForTx({\n\t\t\t\ttxid,\n\t\t\t\tblockId: blockHashHex,\n\t\t\t\theaderHex: header,\n\t\t\t\theight: status.status.block_height,\n\t\t\t\ttxidsDisplay: txids,\n\t\t\t});\n\t\t},\n\t};\n}\n",
7
+ "/**\n * Stacks hard-fork epoch activation heights, as Bitcoin burn block heights.\n *\n * Epoch 4.0 carries both SIP-044 (the native Bitcoin SPV built-ins / Clarity 6)\n * and SIP-045 (`pox-5` Bitcoin Staking) — one fork, one height. Keep it here so\n * the two modules can never disagree.\n */\n\n/**\n * Epoch 4.0 activation height on mainnet — Bitcoin block 960,230 (~2026-07-30\n * AM UTC, per the stacks-core 4.0.1 release notes).\n *\n * Only mainnet has a fixed height. On other networks, read it from the node\n * (`getPox5Activation` for pox-5) or pass it explicitly.\n */\nexport const EPOCH_4_ACTIVATION_BURN_HEIGHT_MAINNET = 960_230;\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "4CD23767D82ECDDE64756E2164756E21",
11
+ "names": []
12
+ }
@@ -66,6 +66,7 @@ var __export = (target, all) => {
66
66
  // src/bitcoin/index.ts
67
67
  var exports_bitcoin = {};
68
68
  __export(exports_bitcoin, {
69
+ BitcoinSourceError: () => BitcoinSourceError,
69
70
  BtcReader: () => BtcReader,
70
71
  EPOCH_4_ACTIVATION_BURN_HEIGHT_MAINNET: () => EPOCH_4_ACTIVATION_BURN_HEIGHT_MAINNET,
71
72
  SPV_ADAPTER_ABI: () => SPV_ADAPTER_ABI,
@@ -452,6 +453,14 @@ function rootFromProof(leafInternal, proof) {
452
453
  }
453
454
 
454
455
  // src/bitcoin/proof.ts
456
+ class BitcoinSourceError extends Error {
457
+ code;
458
+ name = "BitcoinSourceError";
459
+ constructor(code, message) {
460
+ super(message);
461
+ this.code = code;
462
+ }
463
+ }
455
464
  function normalizeTxid(txid) {
456
465
  return without0x(txid).toLowerCase();
457
466
  }
@@ -528,22 +537,32 @@ function bitcoinRpcSource(config) {
528
537
  const basic = typeof config.auth === "string" ? config.auth : btoa(`${config.auth.username}:${config.auth.password}`);
529
538
  headers.authorization = `Basic ${basic}`;
530
539
  }
531
- const res = await doFetch(config.url, {
532
- method: "POST",
533
- headers,
534
- body: JSON.stringify({
535
- jsonrpc: "1.0",
536
- id: "secondlayer",
537
- method,
538
- params: rpcParams
539
- })
540
- });
541
- if (!res.ok) {
542
- throw new Error(`bitcoin rpc ${method} failed: HTTP ${res.status}`);
540
+ let res;
541
+ try {
542
+ res = await doFetch(config.url, {
543
+ method: "POST",
544
+ headers,
545
+ body: JSON.stringify({
546
+ jsonrpc: "1.0",
547
+ id: "secondlayer",
548
+ method,
549
+ params: rpcParams
550
+ })
551
+ });
552
+ } catch (error) {
553
+ throw new BitcoinSourceError("unavailable", `bitcoin rpc ${method} failed: ${error instanceof Error ? error.message : "network error"}`);
554
+ }
555
+ let json;
556
+ try {
557
+ json = await res.json();
558
+ } catch {
559
+ throw new BitcoinSourceError("unavailable", `bitcoin rpc ${method} failed: HTTP ${res.status}`);
543
560
  }
544
- const json = await res.json();
545
561
  if (json.error) {
546
- throw new Error(`bitcoin rpc ${method} error: ${json.error.message}`);
562
+ throw new BitcoinSourceError(json.error.code === -5 ? "not-found" : "unavailable", `bitcoin rpc ${method} error: ${json.error.message}`);
563
+ }
564
+ if (!res.ok) {
565
+ throw new BitcoinSourceError("unavailable", `bitcoin rpc ${method} failed: HTTP ${res.status}`);
547
566
  }
548
567
  return json.result;
549
568
  }
@@ -557,7 +576,7 @@ function bitcoinRpcSource(config) {
557
576
  true
558
577
  ]);
559
578
  if (!tx.blockhash) {
560
- throw new Error(`tx ${txid} is not in a block (node needs -txindex, or the tx is unconfirmed)`);
579
+ throw new BitcoinSourceError("unconfirmed", `tx ${txid} is not in a block (node needs -txindex, or the tx is unconfirmed)`);
561
580
  }
562
581
  const block = await rpc("getblock", [
563
582
  tx.blockhash,
@@ -578,9 +597,14 @@ function esploraSource(config) {
578
597
  const doFetch = config.fetch ?? fetch;
579
598
  const base = config.url.replace(/\/+$/, "");
580
599
  async function get(path) {
581
- const res = await doFetch(`${base}${path}`);
600
+ let res;
601
+ try {
602
+ res = await doFetch(`${base}${path}`);
603
+ } catch (error) {
604
+ throw new BitcoinSourceError("unavailable", `esplora GET ${path} failed: ${error instanceof Error ? error.message : "network error"}`);
605
+ }
582
606
  if (!res.ok) {
583
- throw new Error(`esplora GET ${path} failed: HTTP ${res.status}`);
607
+ throw new BitcoinSourceError(res.status === 404 ? "not-found" : "unavailable", `esplora GET ${path} failed: HTTP ${res.status}`);
584
608
  }
585
609
  return res;
586
610
  }
@@ -591,7 +615,7 @@ function esploraSource(config) {
591
615
  async getBlockForTx(txid) {
592
616
  const status = await (await get(`/tx/${txid}`)).json();
593
617
  if (!status.status.confirmed) {
594
- throw new Error(`tx ${txid} is unconfirmed`);
618
+ throw new BitcoinSourceError("unconfirmed", `tx ${txid} is unconfirmed`);
595
619
  }
596
620
  const blockHashHex = status.status.block_hash;
597
621
  const header = (await (await get(`/block/${blockHashHex}/header`)).text()).trim();
@@ -2798,5 +2822,5 @@ async function verifyBitcoinPayment(client, params) {
2798
2822
  return { verified, mined, output, proof };
2799
2823
  }
2800
2824
 
2801
- //# debugId=A75A612053B8AE0464756E2164756E21
2825
+ //# debugId=0A88BBD4FA62DB1264756E2164756E21
2802
2826
  //# sourceMappingURL=index.cjs.map