@secondlayer/shared 11.21.0 → 11.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/src/archive/range-digest.d.ts +29 -0
  2. package/dist/src/archive/semantic-digest-builder.d.ts +29 -0
  3. package/dist/src/coverage/index.d.ts +29 -0
  4. package/dist/src/db/index.d.ts +29 -0
  5. package/dist/src/db/queries/chain-reorgs.d.ts +29 -0
  6. package/dist/src/db/queries/contracts.d.ts +29 -0
  7. package/dist/src/db/queries/instance.d.ts +29 -0
  8. package/dist/src/db/queries/integrity.d.ts +29 -0
  9. package/dist/src/db/queries/subgraph-gaps.d.ts +29 -0
  10. package/dist/src/db/queries/subgraph-operations.d.ts +29 -0
  11. package/dist/src/db/queries/subgraphs.d.ts +35 -0
  12. package/dist/src/db/queries/subgraphs.js +13 -2
  13. package/dist/src/db/queries/subgraphs.js.map +4 -4
  14. package/dist/src/db/queries/sync-scope.d.ts +29 -0
  15. package/dist/src/db/queries/webhooks.d.ts +29 -0
  16. package/dist/src/db/schema.d.ts +29 -0
  17. package/dist/src/index-http.d.ts +22 -1
  18. package/dist/src/index-http.js +11 -1
  19. package/dist/src/index-http.js.map +3 -3
  20. package/dist/src/index.d.ts +246 -208
  21. package/dist/src/index.js +4 -1
  22. package/dist/src/index.js.map +4 -4
  23. package/dist/src/node/local-client.d.ts +29 -0
  24. package/dist/src/node/tx-summary.d.ts +22 -0
  25. package/dist/src/node/tx-summary.js +194 -0
  26. package/dist/src/node/tx-summary.js.map +12 -0
  27. package/dist/src/schemas/index.d.ts +30 -1
  28. package/dist/src/schemas/index.js.map +2 -2
  29. package/dist/src/schemas/subgraphs.d.ts +30 -1
  30. package/dist/src/schemas/subgraphs.js.map +2 -2
  31. package/dist/src/subgraphs/spec.d.ts +27 -0
  32. package/dist/src/subgraphs/spec.js +4 -1
  33. package/dist/src/subgraphs/spec.js.map +3 -3
  34. package/migrations/0157_subgraph_pin_verification.ts +35 -0
  35. package/migrations/0158_subgraph_pin_preimage.ts +25 -0
  36. package/package.json +6 -2
@@ -2,12 +2,12 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/db/queries/subgraphs.ts", "../src/mode.ts", "../src/schemas/subgraphs.ts", "../src/db/jsonb.ts"],
4
4
  "sourcesContent": [
5
- "import { type Kysely, sql } from \"kysely\";\nimport { isPlatformMode } from \"../../mode.ts\";\nimport { BILLING_PAUSED_PREFIX } from \"../../schemas/subgraphs.ts\";\nimport { jsonb } from \"../jsonb.ts\";\nimport type { Database, Subgraph } from \"../types.ts\";\n\n/** Convert a subgraph name to its PostgreSQL schema name. */\nexport function pgSchemaName(subgraphName: string): string {\n\tconst safeName = subgraphName.replace(/-/g, \"_\");\n\treturn `subgraph_${safeName}`;\n}\n\n/** OSS writes empty account_id — name is the local unique key. */\nexport function localSubgraphAccountId(accountId?: string): string {\n\tif (!isPlatformMode()) return \"\";\n\treturn accountId ?? \"\";\n}\n\nexport async function registerSubgraph(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tname: string;\n\t\tversion: string;\n\t\tdefinition: Record<string, unknown>;\n\t\tschemaHash: string;\n\t\thandlerPath: string;\n\t\tapiKeyId?: string;\n\t\taccountId?: string;\n\t\tschemaName?: string;\n\t\tstartBlock?: number;\n\t\thandlerCode?: string;\n\t\tsourceCode?: string;\n\t},\n): Promise<Subgraph> {\n\tconst accountId = localSubgraphAccountId(data.accountId);\n\tconst values = {\n\t\tname: data.name,\n\t\tversion: data.version,\n\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\tschema_hash: data.schemaHash,\n\t\thandler_path: data.handlerPath,\n\t\taccount_id: accountId,\n\t\thandler_code: data.handlerCode ?? null,\n\t\tsource_code: data.sourceCode ?? null,\n\t\tschema_name: data.schemaName ?? null,\n\t\tstart_block: data.startBlock ?? 0,\n\t};\n\tconst updateSet = {\n\t\tversion: data.version,\n\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\tschema_hash: data.schemaHash,\n\t\thandler_path: data.handlerPath,\n\t\thandler_code: data.handlerCode ?? null,\n\t\tsource_code: data.sourceCode ?? null,\n\t\tschema_name: data.schemaName ?? null,\n\t\tstart_block: data.startBlock ?? 0,\n\t\tupdated_at: new Date(),\n\t};\n\n\t// OSS: name is unique. Upsert by name so leftover rows with a non-empty\n\t// account_id (pre-local-namespace deploys) don't insert a second row.\n\tif (!isPlatformMode()) {\n\t\tconst existing = await db\n\t\t\t.selectFrom(\"subgraphs\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"name\", \"=\", data.name)\n\t\t\t.executeTakeFirst();\n\t\tif (existing) {\n\t\t\treturn await db\n\t\t\t\t.updateTable(\"subgraphs\")\n\t\t\t\t.set({ ...updateSet, account_id: \"\" })\n\t\t\t\t.where(\"id\", \"=\", existing.id)\n\t\t\t\t.returningAll()\n\t\t\t\t.executeTakeFirstOrThrow();\n\t\t}\n\t\treturn await db\n\t\t\t.insertInto(\"subgraphs\")\n\t\t\t.values(values)\n\t\t\t.returningAll()\n\t\t\t.executeTakeFirstOrThrow();\n\t}\n\n\treturn await db\n\t\t.insertInto(\"subgraphs\")\n\t\t.values(values)\n\t\t.onConflict((oc) =>\n\t\t\toc.columns([\"name\", \"account_id\"]).doUpdateSet(updateSet),\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll().where(\"name\", \"=\", name);\n\n\tif (isPlatformMode() && accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\n\treturn (await query.executeTakeFirst()) ?? null;\n}\n\nexport async function listSubgraphs(\n\tdb: Kysely<Database>,\n\taccountId?: string,\n): Promise<Subgraph[]> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll();\n\tif (isPlatformMode() && accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\treturn query.execute();\n}\n\nexport async function updateSubgraphStatus(\n\tdb: Kysely<Database>,\n\tname: string,\n\tstatus: string,\n\tlastProcessedBlock?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tstatus,\n\t\t\t...(lastProcessedBlock !== undefined\n\t\t\t\t? { last_processed_block: lastProcessedBlock }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\n/** Live-walk ERROR write (f069): a block whose handlers ALL failed\n * (`processed === 0 && errors > 0`, `block-processor.ts`'s `applyProgress`)\n * is stamped 'error' and its height is recorded as processed, same as\n * `updateSubgraphStatus(db, name, \"error\", blockHeight)` — except the\n * cursor half of that write now goes through the same conditional `<`\n * guard as {@link recordLiveProgress}. Marking a subgraph 'error' is not\n * itself replay-sensitive, but it shares the `last_processed_block` column\n * with the replay guard: an unconditional write here could regress the\n * cursor a racing (successful) writer already advanced past, which is\n * exactly the regress-then-re-walk loop this guard exists to close. In\n * practice `ctx.rollbackTo` (`runner.ts`) means an all-failed block never\n * flushes writes, so a lost race here is always a silent no-op, never a\n * case that needs `CursorRaceLostError`-style tx abort. Returns whether\n * the write applied. Live path only — reindex/backfill's error stamping\n * goes through {@link updateSubgraphStatus} unconditionally, as before. */\nexport async function recordLiveError(\n\tdb: Kysely<Database>,\n\tname: string,\n\tblockHeight: number,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tstatus: \"error\",\n\t\t\tlast_processed_block: blockHeight,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.where(\"last_processed_block\", \"<\", blockHeight)\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0n) > 0;\n}\n\nexport async function recordSubgraphProcessed(\n\tdb: Kysely<Database>,\n\tname: string,\n\tprocessed: number,\n\terrors: number,\n\tlastError?: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\ttotal_processed: sql`total_processed + ${processed}`,\n\t\t\ttotal_errors: sql`total_errors + ${errors}`,\n\t\t\t...(lastError\n\t\t\t\t? { last_error: lastError, last_error_at: new Date() }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\n/** Record a billing pause on the existing health fields. Status and counters\n * are untouched: a paused subgraph is healthy, just waiting on its account. */\nexport async function markSubgraphBillingPaused(\n\tdb: Kysely<Database>,\n\tname: string,\n\tcode: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tlast_error: `${BILLING_PAUSED_PREFIX}${code}`,\n\t\t\tlast_error_at: new Date(),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\n/** Clear a billing pause once reads succeed again. Leaves any other\n * `last_error` alone. */\nexport async function clearSubgraphBillingPaused(\n\tdb: Kysely<Database>,\n\tname: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({ last_error: null, last_error_at: null, updated_at: new Date() })\n\t\t.where(\"name\", \"=\", name)\n\t\t.where(\"last_error\", \"like\", `${BILLING_PAUSED_PREFIX}%`)\n\t\t.execute();\n}\n\nexport async function updateSubgraphHandlerPath(\n\tdb: Kysely<Database>,\n\tname: string,\n\thandlerPath: string,\n\topts?: { handlerCode?: string; sourceCode?: string },\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\thandler_path: handlerPath,\n\t\t\t...(opts?.handlerCode != null ? { handler_code: opts.handlerCode } : {}),\n\t\t\t...(opts?.sourceCode != null ? { source_code: opts.sourceCode } : {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function deleteSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tconst subgraph = await getSubgraph(db, name, accountId);\n\tif (!subgraph) return null;\n\n\t// Use stored schema_name if available, otherwise compute\n\tconst schemaName = subgraph.schema_name ?? pgSchemaName(name);\n\n\t// Cascade to webhooks: a webhook pointing at a deleted\n\t// subgraph + table will throw `relation does not exist` on every\n\t// subsequent emission. Pause active subs and purge any pending outbox\n\t// rows so receivers don't get phantom replays. We don't delete the\n\t// webhooks themselves — operators may want to repoint them at a\n\t// resurrected subgraph; we just stop them firing.\n\tawait db\n\t\t.updateTable(\"webhooks\")\n\t\t.set({\n\t\t\tstatus: \"paused\",\n\t\t\tlast_error: `Subgraph \"${name}\" deleted; webhook auto-paused.`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"subgraph_name\", \"=\", name)\n\t\t.execute();\n\tawait db\n\t\t.deleteFrom(\"webhook_outbox\")\n\t\t.where(\"status\", \"=\", \"pending\")\n\t\t.where(\"webhook_id\", \"in\", (qb) =>\n\t\t\tqb.selectFrom(\"webhooks\").select(\"id\").where(\"subgraph_name\", \"=\", name),\n\t\t)\n\t\t.execute();\n\n\t// Drop the subgraph's schema (CASCADE drops all tables within).\n\tawait sql`DROP SCHEMA IF EXISTS ${sql.raw(`\"${schemaName}\"`)} CASCADE`.execute(\n\t\tdb,\n\t);\n\n\t// Remove from registry\n\tawait db.deleteFrom(\"subgraphs\").where(\"id\", \"=\", subgraph.id).execute();\n\n\treturn subgraph;\n}\n\n/** Set or clear a paid subgraph's expiry (NULL = no expiry, e.g. on claim). */\nexport async function updateSubgraphExpiry(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId: string,\n\texpiresAt: Date | null,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({ expires_at: expiresAt })\n\t\t.where(\"name\", \"=\", name)\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.execute();\n}\n\n/** Live-walk progress write: advances the cursor and PROMOTES toward\n * 'active' (deploying/error → active keeps reorg eligibility, reorg.ts gates\n * on it) but never overwrites an explicit 'reindexing' park — unconditional\n * status stamping let catch-up flap a parked subgraph back into its own path\n * per block, fighting the queued reindex op.\n *\n * `last_processed_block` is now written CONDITIONALLY — `WHERE\n * last_processed_block < lastProcessedBlock`, mirroring\n * `advanceOperationCursor`'s (`subgraph-operations.ts`) comparison shape —\n * and returns whether THIS call actually advanced it. This used to be\n * unconditional by design, on the premise that the only two writers able to\n * advance/rewind a given subgraph's cursor concurrently (the catch-up walk\n * and the reorg handler) serialize on an in-process per-subgraph lock +\n * reorg epoch guard (`packages/subgraphs/src/runtime/catchup.ts`,\n * `subgraphBlockLockHeld` / `reorgEpoch`; f057). That guard is in-process\n * only. The f068 investigation\n * (`docs/internal/audits/asset-holdings-unreproducible-balances-f068.md`)\n * found a cross-process sequence it does not cover: a catch-up leader that\n * loses its advisory-lock connection mid-walk keeps writing (leadership is\n * checked only at walk entry, not per-iteration) while a new leader takes\n * over and walks the same range — both commit increments for overlapping\n * heights, and every regressed cursor write from the laggard re-triggers a\n * full re-walk, growing per-height application without bound. The\n * conditional advance here (paired with the fast-path replay guard armed on\n * the live path in `block-processor.ts`) is now the cross-process\n * guarantee: a block at/below the current cursor can never re-advance it,\n * so a laggard's commits can no longer regress-then-retrigger.\n *\n * This conditionality is why the reorg rewind can no longer go through this\n * function — a rewind's whole point is to move the cursor *backward*, which\n * the `<` guard would silently refuse. Reorg mode calls\n * {@link rewindLiveProgress} instead, which keeps the old unconditional\n * semantics and is safe specifically because the reorg path takes a\n * transaction-scoped advisory lock (`reorg.ts`) other writers can't cross. */\nexport async function recordLiveProgress(\n\tdb: Kysely<Database>,\n\tname: string,\n\tlastProcessedBlock: number,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tlast_processed_block: lastProcessedBlock,\n\t\t\tstatus: sql`CASE WHEN status = 'reindexing' THEN status ELSE 'active' END`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.where(\"last_processed_block\", \"<\", lastProcessedBlock)\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0n) > 0;\n}\n\n/** Reorg-rewind progress write: the OLD unconditional `recordLiveProgress`\n * semantics, verbatim — moves `last_processed_block` to the fork height\n * regardless of its current value, because that is by definition a\n * backward move the conditional `<` guard in `recordLiveProgress` would\n * refuse. Same status-promotion CASE (never unparks an explicit\n * 'reindexing').\n *\n * Callers: exclusively the subgraph-reorg path\n * (`packages/subgraphs/src/runtime/reorg.ts`, via `block-processor.ts`'s\n * `reorgRewind` mode), which is safe from the cross-process race this\n * function reintroduces because it holds a transaction-scoped Postgres\n * advisory lock (`pg_advisory_xact_lock`) for the whole\n * delete+reprocess+rewind sequence — no other writer can interleave a\n * forward write between the rewind and the fork block's reprocess. Do not\n * call this from the live catch-up path. */\nexport async function rewindLiveProgress(\n\tdb: Kysely<Database>,\n\tname: string,\n\tlastProcessedBlock: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tlast_processed_block: lastProcessedBlock,\n\t\t\tstatus: sql`CASE WHEN status = 'reindexing' THEN status ELSE 'active' END`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n",
5
+ "import { type Kysely, sql } from \"kysely\";\nimport { isPlatformMode } from \"../../mode.ts\";\nimport {\n\tBILLING_PAUSED_PREFIX,\n\ttype SubgraphVerification,\n} from \"../../schemas/subgraphs.ts\";\nimport { jsonb } from \"../jsonb.ts\";\nimport type { Database, Subgraph } from \"../types.ts\";\n\n/** Convert a subgraph name to its PostgreSQL schema name. */\nexport function pgSchemaName(subgraphName: string): string {\n\tconst safeName = subgraphName.replace(/-/g, \"_\");\n\treturn `subgraph_${safeName}`;\n}\n\n/** OSS writes empty account_id — name is the local unique key. */\nexport function localSubgraphAccountId(accountId?: string): string {\n\tif (!isPlatformMode()) return \"\";\n\treturn accountId ?? \"\";\n}\n\nexport async function registerSubgraph(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tname: string;\n\t\tversion: string;\n\t\tdefinition: Record<string, unknown>;\n\t\tschemaHash: string;\n\t\thandlerPath: string;\n\t\tapiKeyId?: string;\n\t\taccountId?: string;\n\t\tschemaName?: string;\n\t\tstartBlock?: number;\n\t\thandlerCode?: string;\n\t\tsourceCode?: string;\n\t\tpin?: string | null;\n\t\tpinPreimage?: string | null;\n\t\tverification?: SubgraphVerification | null;\n\t},\n): Promise<Subgraph> {\n\tconst accountId = localSubgraphAccountId(data.accountId);\n\tconst values = {\n\t\tname: data.name,\n\t\tversion: data.version,\n\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\tschema_hash: data.schemaHash,\n\t\thandler_path: data.handlerPath,\n\t\taccount_id: accountId,\n\t\thandler_code: data.handlerCode ?? null,\n\t\tsource_code: data.sourceCode ?? null,\n\t\tschema_name: data.schemaName ?? null,\n\t\tstart_block: data.startBlock ?? 0,\n\t\tpin: data.pin ?? null,\n\t\tpin_preimage: data.pinPreimage ?? null,\n\t\tverification: data.verification\n\t\t\t? jsonb<SubgraphVerification>(data.verification)\n\t\t\t: null,\n\t};\n\tconst updateSet = {\n\t\tversion: data.version,\n\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\tschema_hash: data.schemaHash,\n\t\thandler_path: data.handlerPath,\n\t\thandler_code: data.handlerCode ?? null,\n\t\tsource_code: data.sourceCode ?? null,\n\t\tschema_name: data.schemaName ?? null,\n\t\tstart_block: data.startBlock ?? 0,\n\t\tpin: values.pin,\n\t\tpin_preimage: values.pin_preimage,\n\t\tverification: values.verification,\n\t\tupdated_at: new Date(),\n\t};\n\n\t// OSS: name is unique. Upsert by name so leftover rows with a non-empty\n\t// account_id (pre-local-namespace deploys) don't insert a second row.\n\tif (!isPlatformMode()) {\n\t\tconst existing = await db\n\t\t\t.selectFrom(\"subgraphs\")\n\t\t\t.select(\"id\")\n\t\t\t.where(\"name\", \"=\", data.name)\n\t\t\t.executeTakeFirst();\n\t\tif (existing) {\n\t\t\treturn await db\n\t\t\t\t.updateTable(\"subgraphs\")\n\t\t\t\t.set({ ...updateSet, account_id: \"\" })\n\t\t\t\t.where(\"id\", \"=\", existing.id)\n\t\t\t\t.returningAll()\n\t\t\t\t.executeTakeFirstOrThrow();\n\t\t}\n\t\treturn await db\n\t\t\t.insertInto(\"subgraphs\")\n\t\t\t.values(values)\n\t\t\t.returningAll()\n\t\t\t.executeTakeFirstOrThrow();\n\t}\n\n\treturn await db\n\t\t.insertInto(\"subgraphs\")\n\t\t.values(values)\n\t\t.onConflict((oc) =>\n\t\t\toc.columns([\"name\", \"account_id\"]).doUpdateSet(updateSet),\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll().where(\"name\", \"=\", name);\n\n\tif (isPlatformMode() && accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\n\treturn (await query.executeTakeFirst()) ?? null;\n}\n\nexport async function listSubgraphs(\n\tdb: Kysely<Database>,\n\taccountId?: string,\n): Promise<Subgraph[]> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll();\n\tif (isPlatformMode() && accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\treturn query.execute();\n}\n\nexport async function updateSubgraphStatus(\n\tdb: Kysely<Database>,\n\tname: string,\n\tstatus: string,\n\tlastProcessedBlock?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tstatus,\n\t\t\t...(lastProcessedBlock !== undefined\n\t\t\t\t? { last_processed_block: lastProcessedBlock }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\n/** Live-walk ERROR write (f069): a block whose handlers ALL failed\n * (`processed === 0 && errors > 0`, `block-processor.ts`'s `applyProgress`)\n * is stamped 'error' and its height is recorded as processed, same as\n * `updateSubgraphStatus(db, name, \"error\", blockHeight)` — except the\n * cursor half of that write now goes through the same conditional `<`\n * guard as {@link recordLiveProgress}. Marking a subgraph 'error' is not\n * itself replay-sensitive, but it shares the `last_processed_block` column\n * with the replay guard: an unconditional write here could regress the\n * cursor a racing (successful) writer already advanced past, which is\n * exactly the regress-then-re-walk loop this guard exists to close. In\n * practice `ctx.rollbackTo` (`runner.ts`) means an all-failed block never\n * flushes writes, so a lost race here is always a silent no-op, never a\n * case that needs `CursorRaceLostError`-style tx abort. Returns whether\n * the write applied. Live path only — reindex/backfill's error stamping\n * goes through {@link updateSubgraphStatus} unconditionally, as before. */\nexport async function recordLiveError(\n\tdb: Kysely<Database>,\n\tname: string,\n\tblockHeight: number,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tstatus: \"error\",\n\t\t\tlast_processed_block: blockHeight,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.where(\"last_processed_block\", \"<\", blockHeight)\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0n) > 0;\n}\n\nexport async function recordSubgraphProcessed(\n\tdb: Kysely<Database>,\n\tname: string,\n\tprocessed: number,\n\terrors: number,\n\tlastError?: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\ttotal_processed: sql`total_processed + ${processed}`,\n\t\t\ttotal_errors: sql`total_errors + ${errors}`,\n\t\t\t...(lastError\n\t\t\t\t? { last_error: lastError, last_error_at: new Date() }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\n/** Record a billing pause on the existing health fields. Status and counters\n * are untouched: a paused subgraph is healthy, just waiting on its account. */\nexport async function markSubgraphBillingPaused(\n\tdb: Kysely<Database>,\n\tname: string,\n\tcode: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tlast_error: `${BILLING_PAUSED_PREFIX}${code}`,\n\t\t\tlast_error_at: new Date(),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\n/** Clear a billing pause once reads succeed again. Leaves any other\n * `last_error` alone. */\nexport async function clearSubgraphBillingPaused(\n\tdb: Kysely<Database>,\n\tname: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({ last_error: null, last_error_at: null, updated_at: new Date() })\n\t\t.where(\"name\", \"=\", name)\n\t\t.where(\"last_error\", \"like\", `${BILLING_PAUSED_PREFIX}%`)\n\t\t.execute();\n}\n\nexport async function updateSubgraphHandlerPath(\n\tdb: Kysely<Database>,\n\tname: string,\n\thandlerPath: string,\n\topts?: {\n\t\thandlerCode?: string;\n\t\tsourceCode?: string;\n\t\tpin?: string | null;\n\t\tpinPreimage?: string | null;\n\t\tverification?: SubgraphVerification | null;\n\t},\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\thandler_path: handlerPath,\n\t\t\t...(opts?.handlerCode != null ? { handler_code: opts.handlerCode } : {}),\n\t\t\t...(opts?.sourceCode != null ? { source_code: opts.sourceCode } : {}),\n\t\t\t...(opts?.pin !== undefined ? { pin: opts.pin } : {}),\n\t\t\t...(opts?.pinPreimage !== undefined\n\t\t\t\t? { pin_preimage: opts.pinPreimage }\n\t\t\t\t: {}),\n\t\t\t...(opts?.verification !== undefined\n\t\t\t\t? {\n\t\t\t\t\t\tverification: opts.verification\n\t\t\t\t\t\t\t? jsonb<SubgraphVerification>(opts.verification)\n\t\t\t\t\t\t\t: null,\n\t\t\t\t\t}\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function deleteSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tconst subgraph = await getSubgraph(db, name, accountId);\n\tif (!subgraph) return null;\n\n\t// Use stored schema_name if available, otherwise compute\n\tconst schemaName = subgraph.schema_name ?? pgSchemaName(name);\n\n\t// Cascade to webhooks: a webhook pointing at a deleted\n\t// subgraph + table will throw `relation does not exist` on every\n\t// subsequent emission. Pause active subs and purge any pending outbox\n\t// rows so receivers don't get phantom replays. We don't delete the\n\t// webhooks themselves — operators may want to repoint them at a\n\t// resurrected subgraph; we just stop them firing.\n\tawait db\n\t\t.updateTable(\"webhooks\")\n\t\t.set({\n\t\t\tstatus: \"paused\",\n\t\t\tlast_error: `Subgraph \"${name}\" deleted; webhook auto-paused.`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"subgraph_name\", \"=\", name)\n\t\t.execute();\n\tawait db\n\t\t.deleteFrom(\"webhook_outbox\")\n\t\t.where(\"status\", \"=\", \"pending\")\n\t\t.where(\"webhook_id\", \"in\", (qb) =>\n\t\t\tqb.selectFrom(\"webhooks\").select(\"id\").where(\"subgraph_name\", \"=\", name),\n\t\t)\n\t\t.execute();\n\n\t// Drop the subgraph's schema (CASCADE drops all tables within).\n\tawait sql`DROP SCHEMA IF EXISTS ${sql.raw(`\"${schemaName}\"`)} CASCADE`.execute(\n\t\tdb,\n\t);\n\n\t// Remove from registry\n\tawait db.deleteFrom(\"subgraphs\").where(\"id\", \"=\", subgraph.id).execute();\n\n\treturn subgraph;\n}\n\n/** Set or clear a paid subgraph's expiry (NULL = no expiry, e.g. on claim). */\nexport async function updateSubgraphExpiry(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId: string,\n\texpiresAt: Date | null,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({ expires_at: expiresAt })\n\t\t.where(\"name\", \"=\", name)\n\t\t.where(\"account_id\", \"=\", accountId)\n\t\t.execute();\n}\n\n/** Live-walk progress write: advances the cursor and PROMOTES toward\n * 'active' (deploying/error → active keeps reorg eligibility, reorg.ts gates\n * on it) but never overwrites an explicit 'reindexing' park — unconditional\n * status stamping let catch-up flap a parked subgraph back into its own path\n * per block, fighting the queued reindex op.\n *\n * `last_processed_block` is now written CONDITIONALLY — `WHERE\n * last_processed_block < lastProcessedBlock`, mirroring\n * `advanceOperationCursor`'s (`subgraph-operations.ts`) comparison shape —\n * and returns whether THIS call actually advanced it. This used to be\n * unconditional by design, on the premise that the only two writers able to\n * advance/rewind a given subgraph's cursor concurrently (the catch-up walk\n * and the reorg handler) serialize on an in-process per-subgraph lock +\n * reorg epoch guard (`packages/subgraphs/src/runtime/catchup.ts`,\n * `subgraphBlockLockHeld` / `reorgEpoch`; f057). That guard is in-process\n * only. The f068 investigation\n * (`docs/internal/audits/asset-holdings-unreproducible-balances-f068.md`)\n * found a cross-process sequence it does not cover: a catch-up leader that\n * loses its advisory-lock connection mid-walk keeps writing (leadership is\n * checked only at walk entry, not per-iteration) while a new leader takes\n * over and walks the same range — both commit increments for overlapping\n * heights, and every regressed cursor write from the laggard re-triggers a\n * full re-walk, growing per-height application without bound. The\n * conditional advance here (paired with the fast-path replay guard armed on\n * the live path in `block-processor.ts`) is now the cross-process\n * guarantee: a block at/below the current cursor can never re-advance it,\n * so a laggard's commits can no longer regress-then-retrigger.\n *\n * This conditionality is why the reorg rewind can no longer go through this\n * function — a rewind's whole point is to move the cursor *backward*, which\n * the `<` guard would silently refuse. Reorg mode calls\n * {@link rewindLiveProgress} instead, which keeps the old unconditional\n * semantics and is safe specifically because the reorg path takes a\n * transaction-scoped advisory lock (`reorg.ts`) other writers can't cross. */\nexport async function recordLiveProgress(\n\tdb: Kysely<Database>,\n\tname: string,\n\tlastProcessedBlock: number,\n): Promise<boolean> {\n\tconst result = await db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tlast_processed_block: lastProcessedBlock,\n\t\t\tstatus: sql`CASE WHEN status = 'reindexing' THEN status ELSE 'active' END`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.where(\"last_processed_block\", \"<\", lastProcessedBlock)\n\t\t.executeTakeFirst();\n\treturn Number(result.numUpdatedRows ?? 0n) > 0;\n}\n\n/** Reorg-rewind progress write: the OLD unconditional `recordLiveProgress`\n * semantics, verbatim — moves `last_processed_block` to the fork height\n * regardless of its current value, because that is by definition a\n * backward move the conditional `<` guard in `recordLiveProgress` would\n * refuse. Same status-promotion CASE (never unparks an explicit\n * 'reindexing').\n *\n * Callers: exclusively the subgraph-reorg path\n * (`packages/subgraphs/src/runtime/reorg.ts`, via `block-processor.ts`'s\n * `reorgRewind` mode), which is safe from the cross-process race this\n * function reintroduces because it holds a transaction-scoped Postgres\n * advisory lock (`pg_advisory_xact_lock`) for the whole\n * delete+reprocess+rewind sequence — no other writer can interleave a\n * forward write between the rewind and the fork block's reprocess. Do not\n * call this from the live catch-up path. */\nexport async function rewindLiveProgress(\n\tdb: Kysely<Database>,\n\tname: string,\n\tlastProcessedBlock: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tlast_processed_block: lastProcessedBlock,\n\t\t\tstatus: sql`CASE WHEN status = 'reindexing' THEN status ELSE 'active' END`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n",
6
6
  "/**\n * Instance modes for the Secondlayer platform.\n *\n * - `oss`: self-hosted, single-tenant. No auth middleware, no platform routes\n * (projects, admin). Everything runs against a single `DATABASE_URL`.\n * Intended for `docker compose up`.\n *\n * - `platform`: the metered archive deployment. Magic-link auth, API keys,\n * accounts, credits, and metered `/v1` reads (Index + Streams + subgraphs)\n * against a single shared DB. No paid tier ladder — one metered `free`\n * tier plus first-party internal service credentials.\n */\n\nexport type InstanceMode = \"oss\" | \"platform\";\n\n/** What the operator wrote in the env — `archive` is the honest name for the\n * metered public-archive deployment and aliases `platform` behaviorally. */\nexport type DeclaredInstanceMode = InstanceMode | \"archive\";\n\nconst VALID_MODES: readonly DeclaredInstanceMode[] = [\n\t\"oss\",\n\t\"platform\",\n\t\"archive\",\n];\n\nfunction declaredMode(): DeclaredInstanceMode {\n\tconst raw = process.env.INSTANCE_MODE?.trim().toLowerCase();\n\tif (raw && (VALID_MODES as readonly string[]).includes(raw)) {\n\t\treturn raw as DeclaredInstanceMode;\n\t}\n\treturn \"oss\";\n}\n\n/**\n * Resolve the active instance mode from `process.env.INSTANCE_MODE`.\n * Defaults to `\"oss\"` — the safest default for self-hosters who deploy\n * without setting the variable. `archive` normalizes to `platform` here so\n * every behavioral branch stays a two-way choice; use\n * `getDeclaredInstanceMode()` for display surfaces.\n */\nexport function getInstanceMode(): InstanceMode {\n\tconst declared = declaredMode();\n\treturn declared === \"archive\" ? \"platform\" : declared;\n}\n\n/** The mode as declared in the env, for status/identity surfaces. */\nexport function getDeclaredInstanceMode(): DeclaredInstanceMode {\n\treturn declaredMode();\n}\n\n/**\n * True when public reads are credit-metered (the free window, retention, and\n * credits gates arm). Platform/archive deployments meter by default;\n * self-host never does unless the operator explicitly sets\n * `METERED_READS=true`. `METERED_READS=false` disarms a metered deployment.\n */\nexport function isMeteredReads(): boolean {\n\tconst override = process.env.METERED_READS?.trim().toLowerCase();\n\tif (override === \"true\") return true;\n\tif (override === \"false\") return false;\n\treturn getInstanceMode() === \"platform\";\n}\n\n/** True when the active mode is `\"platform\"` (shared multi-tenant). */\nexport function isPlatformMode(): boolean {\n\treturn getInstanceMode() === \"platform\";\n}\n\n/** True when the active mode is `\"oss\"` (self-hosted). */\nexport function isOssMode(): boolean {\n\treturn getInstanceMode() === \"oss\";\n}\n",
7
- "import { z } from \"zod\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n\t/** Validate the definition + print the DDL plan without deploying. */\n\tdryRun?: boolean;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t\tdryRun: z.boolean().optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"handler_updated\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\t/** Effective indexing start height. */\n\tstart_block?: number;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\t/** Bounded candidate-event denominator for the reindex just started, when the\n\t * op was classifiable as sparse/light. Absent for heavy ops (no upfront estimate\n\t * possible) — check `sl subgraphs status` after ~30s for a rate-based ETA instead. */\n\testimatedEvents?: number;\n\t/** Non-blocking deploy lints (e.g. handler reads a print field never observed on-chain). */\n\twarnings?: string[];\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\n/** Prefix of the `last_error` a billing-paused subgraph carries, e.g.\n * `billing_paused: spend_cap_reached`. Stable: the processor writes it and\n * the watchdog and CLI key off it. */\nexport const BILLING_PAUSED_PREFIX = \"billing_paused: \";\n\n/** The billing code in a subgraph's `last_error`, or `null` when it isn't a\n * billing pause. */\nexport function billingPausedCode(\n\tlastError: string | null | undefined,\n): string | null {\n\treturn lastError?.startsWith(BILLING_PAUSED_PREFIX)\n\t\t? lastError.slice(BILLING_PAUSED_PREFIX.length)\n\t\t: null;\n}\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalRows?: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tgapCount: number;\n\t/** history_filling = expected gaps while a tip-first backfill op runs. */\n\tintegrity: \"complete\" | \"gaps_detected\" | \"history_filling\";\n\t/** Most recent indexing error reason + when it occurred, if any. */\n\tlastError?: string | null;\n\tlastErrorAt?: string | null;\n\t/** Last row mutation timestamp; powers per-card freshness. */\n\tupdatedAt?: string | null;\n\t/** Number of webhooks attached to this subgraph. */\n\twebhookCount?: number;\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\t/** Present while the populating operation is queued: approximate claim\n\t * position + honest event denominator + naive start estimate. */\n\tqueue?: {\n\t\tposition: number | null;\n\t\testimatedEvents: number | null;\n\t\testimatedStartSeconds: number | null;\n\t};\n\t/** Event-based progress for sparse syncs (block pct is meaningless when\n\t * most heights are skipped). */\n\testimatedEvents?: number;\n\tprocessedEvents?: number;\n\tetaSeconds?: number | null;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\t/** history_filling = expected gaps while a tip-first backfill op runs. */\n\tintegrity: \"complete\" | \"gaps_detected\" | \"history_filling\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t\t/** True after the processor matched work but user tables still have 0 rows. */\n\t\temptyMapping: boolean;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: readonly (readonly string[])[];\n\t\t\tuniqueKeys?: readonly (readonly string[])[];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n\n/**\n * Request shape for `GET /v1/subgraphs/:subgraphName/:tableName/aggregate`.\n * `filters` reuses the list/count where-surface; the rest name the columns to\n * aggregate. SUM/MIN/MAX columns must be numeric (uint/int, plus `_block_height`).\n */\nexport interface SubgraphAggregateParams {\n\tfilters?: Record<string, string>;\n\tcount?: boolean;\n\tcountDistinct?: string[];\n\tsum?: string[];\n\tmin?: string[];\n\tmax?: string[];\n}\n\n/**\n * Aggregate response. Keys are present only for requested aggregates.\n * `count`/`countDistinct` are JSON numbers (counts << 2^53); `sum`/`min`/`max`\n * are lossless strings (NUMERIC `::text`). `sum` of an empty set is `\"0\"`;\n * `min`/`max` are `null` when the filtered set is empty or all-null.\n */\nexport interface SubgraphAggregateResponse {\n\tcount?: number;\n\tcountDistinct?: Record<string, number>;\n\tsum?: Record<string, string>;\n\tmin?: Record<string, string | null>;\n\tmax?: Record<string, string | null>;\n}\n",
7
+ "import { z } from \"zod\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n\t/** Validate the definition + print the DDL plan without deploying. */\n\tdryRun?: boolean;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t\tdryRun: z.boolean().optional(),\n\t});\n\n/**\n * How far a subgraph's rows can be checked without trusting the server,\n * derived from its sources and a scan of its handler. Never configured.\n *\n * - `state`: every input is a named state write, provable from block headers.\n * Handlers run in the deterministic realm. A deployed `state` subgraph is\n * verifiable: one with any reason is refused at deploy.\n * - `events`: an input (events, prints, tx results, `ctx.client`) needs\n * re-execution proofs, which are not served yet.\n * - `none`: a source can never be proven as written (trait scope, tip-first\n * backfill).\n */\nexport interface SubgraphVerification {\n\tlevel: \"state\" | \"events\" | \"none\";\n\t/** What stands between the subgraph and checkable rows, e.g.\n\t * `print_event source \"swaps\" needs event proofs` or\n\t * `subgraph.ts:41:12 Date: wall-clock time differs per run`. */\n\treasons: string[];\n\t/** What stays unproven even at this level, e.g. tx attribution of writes. */\n\tunproven: string[];\n}\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"handler_updated\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\t/** Effective indexing start height. */\n\tstart_block?: number;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\t/** Bounded candidate-event denominator for the reindex just started, when the\n\t * op was classifiable as sparse/light. Absent for heavy ops (no upfront estimate\n\t * possible) — check `sl subgraphs status` after ~30s for a rate-based ETA instead. */\n\testimatedEvents?: number;\n\t/** Non-blocking deploy lints (e.g. handler reads a print field never observed on-chain). */\n\twarnings?: string[];\n\t/** sha256 identity of what was deployed; absent on unbundled deploys. */\n\tpin?: string;\n\tverification?: SubgraphVerification;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\n/** Prefix of the `last_error` a billing-paused subgraph carries, e.g.\n * `billing_paused: spend_cap_reached`. Stable: the processor writes it and\n * the watchdog and CLI key off it. */\nexport const BILLING_PAUSED_PREFIX = \"billing_paused: \";\n\n/** The billing code in a subgraph's `last_error`, or `null` when it isn't a\n * billing pause. */\nexport function billingPausedCode(\n\tlastError: string | null | undefined,\n): string | null {\n\treturn lastError?.startsWith(BILLING_PAUSED_PREFIX)\n\t\t? lastError.slice(BILLING_PAUSED_PREFIX.length)\n\t\t: null;\n}\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalRows?: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tgapCount: number;\n\t/** history_filling = expected gaps while a tip-first backfill op runs. */\n\tintegrity: \"complete\" | \"gaps_detected\" | \"history_filling\";\n\t/** Most recent indexing error reason + when it occurred, if any. */\n\tlastError?: string | null;\n\tlastErrorAt?: string | null;\n\t/** Last row mutation timestamp; powers per-card freshness. */\n\tupdatedAt?: string | null;\n\t/** Number of webhooks attached to this subgraph. */\n\twebhookCount?: number;\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\t/** Present while the populating operation is queued: approximate claim\n\t * position + honest event denominator + naive start estimate. */\n\tqueue?: {\n\t\tposition: number | null;\n\t\testimatedEvents: number | null;\n\t\testimatedStartSeconds: number | null;\n\t};\n\t/** Event-based progress for sparse syncs (block pct is meaningless when\n\t * most heights are skipped). */\n\testimatedEvents?: number;\n\tprocessedEvents?: number;\n\tetaSeconds?: number | null;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\t/** history_filling = expected gaps while a tip-first backfill op runs. */\n\tintegrity: \"complete\" | \"gaps_detected\" | \"history_filling\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\t/** sha256 over schema, handler bundle, startBlock, network and runtime.\n\t * Null for subgraphs deployed before pins existed. */\n\tpin?: string | null;\n\t/** Null for subgraphs deployed before levels were derived. */\n\tverification?: SubgraphVerification | null;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t\t/** True after the processor matched work but user tables still have 0 rows. */\n\t\temptyMapping: boolean;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: readonly (readonly string[])[];\n\t\t\tuniqueKeys?: readonly (readonly string[])[];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n\n/**\n * Request shape for `GET /v1/subgraphs/:subgraphName/:tableName/aggregate`.\n * `filters` reuses the list/count where-surface; the rest name the columns to\n * aggregate. SUM/MIN/MAX columns must be numeric (uint/int, plus `_block_height`).\n */\nexport interface SubgraphAggregateParams {\n\tfilters?: Record<string, string>;\n\tcount?: boolean;\n\tcountDistinct?: string[];\n\tsum?: string[];\n\tmin?: string[];\n\tmax?: string[];\n}\n\n/**\n * Aggregate response. Keys are present only for requested aggregates.\n * `count`/`countDistinct` are JSON numbers (counts << 2^53); `sum`/`min`/`max`\n * are lossless strings (NUMERIC `::text`). `sum` of an empty set is `\"0\"`;\n * `min`/`max` are `null` when the filtered set is empty or all-null.\n */\nexport interface SubgraphAggregateResponse {\n\tcount?: number;\n\tcountDistinct?: Record<string, number>;\n\tsum?: Record<string, string>;\n\tmin?: Record<string, string | null>;\n\tmax?: Record<string, string | null>;\n}\n",
8
8
  "import { type RawBuilder, sql } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n *\n * Generic parameter lets callers set the RawBuilder's output type so they\n * don't need to cast at the insert site. Default is `unknown` — widen at\n * the call site when the column type is narrower, e.g. `jsonb<MyShape>(...)`.\n */\nexport function jsonb<T = unknown>(value: T): RawBuilder<T> {\n\tconst escaped = JSON.stringify(value, (_k, v) =>\n\t\ttypeof v === \"bigint\" ? v.toString() : v,\n\t).replace(/'/g, \"''\");\n\treturn sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n"
9
9
  ],
10
- "mappings": "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",
11
- "debugId": "23059D593B71468164756E2164756E21",
10
+ "mappings": "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",
11
+ "debugId": "3407FD67D654BD8464756E2164756E21",
12
12
  "names": []
13
13
  }
@@ -29,6 +29,27 @@ type SyncScope = {
29
29
  }
30
30
  };
31
31
  import { ColumnType, Generated } from "kysely";
32
+ /**
33
+ * How far a subgraph's rows can be checked without trusting the server,
34
+ * derived from its sources and a scan of its handler. Never configured.
35
+ *
36
+ * - `state`: every input is a named state write, provable from block headers.
37
+ * Handlers run in the deterministic realm. A deployed `state` subgraph is
38
+ * verifiable: one with any reason is refused at deploy.
39
+ * - `events`: an input (events, prints, tx results, `ctx.client`) needs
40
+ * re-execution proofs, which are not served yet.
41
+ * - `none`: a source can never be proven as written (trait scope, tip-first
42
+ * backfill).
43
+ */
44
+ interface SubgraphVerification {
45
+ level: "state" | "events" | "none";
46
+ /** What stands between the subgraph and checkable rows, e.g.
47
+ * `print_event source "swaps" needs event proofs` or
48
+ * `subgraph.ts:41:12 Date: wall-clock time differs per run`. */
49
+ reasons: string[];
50
+ /** What stays unproven even at this level, e.g. tx attribution of writes. */
51
+ unproven: string[];
52
+ }
32
53
  interface BlocksTable {
33
54
  height: number;
34
55
  hash: string;
@@ -189,6 +210,14 @@ interface SubgraphsTable {
189
210
  expires_at: Date | null;
190
211
  /** (event type, contract) probe pairs persisted at deploy for weight classification. */
191
212
  sparse_probe_targets: unknown | null;
213
+ /** sha256 over everything that shapes rows (schema, handler bundle,
214
+ * startBlock, network, runtime). NULL for unbundled or older deploys. */
215
+ pin: string | null;
216
+ /** The canonical JSON `pin` hashes, so a client can recompute it. NULL
217
+ * wherever `pin` is, and for deploys before migration 0158. */
218
+ pin_preimage: string | null;
219
+ /** Level derived at deploy. NULL (older deploys) = not verifiable. */
220
+ verification: SubgraphVerification | null;
192
221
  created_at: Generated<Date>;
193
222
  updated_at: Generated<Date>;
194
223
  }
@@ -19,6 +19,27 @@ type FailureClass = (typeof FAILURE_CLASSES)[number];
19
19
  declare const RETRY_STATES: readonly ["open", "retrying", "halted", "resolved"];
20
20
  type RetryState = (typeof RETRY_STATES)[number];
21
21
  type BootstrapSource = "archive" | "genesis" | "import";
22
+ /**
23
+ * How far a subgraph's rows can be checked without trusting the server,
24
+ * derived from its sources and a scan of its handler. Never configured.
25
+ *
26
+ * - `state`: every input is a named state write, provable from block headers.
27
+ * Handlers run in the deterministic realm. A deployed `state` subgraph is
28
+ * verifiable: one with any reason is refused at deploy.
29
+ * - `events`: an input (events, prints, tx results, `ctx.client`) needs
30
+ * re-execution proofs, which are not served yet.
31
+ * - `none`: a source can never be proven as written (trait scope, tip-first
32
+ * backfill).
33
+ */
34
+ interface SubgraphVerification {
35
+ level: "state" | "events" | "none";
36
+ /** What stands between the subgraph and checkable rows, e.g.
37
+ * `print_event source "swaps" needs event proofs` or
38
+ * `subgraph.ts:41:12 Date: wall-clock time differs per run`. */
39
+ reasons: string[];
40
+ /** What stays unproven even at this level, e.g. tx attribution of writes. */
41
+ unproven: string[];
42
+ }
22
43
  interface BlocksTable {
23
44
  height: number;
24
45
  hash: string;
@@ -179,6 +200,14 @@ interface SubgraphsTable {
179
200
  expires_at: Date | null;
180
201
  /** (event type, contract) probe pairs persisted at deploy for weight classification. */
181
202
  sparse_probe_targets: unknown | null;
203
+ /** sha256 over everything that shapes rows (schema, handler bundle,
204
+ * startBlock, network, runtime). NULL for unbundled or older deploys. */
205
+ pin: string | null;
206
+ /** The canonical JSON `pin` hashes, so a client can recompute it. NULL
207
+ * wherever `pin` is, and for deploys before migration 0158. */
208
+ pin_preimage: string | null;
209
+ /** Level derived at deploy. NULL (older deploys) = not verifiable. */
210
+ verification: SubgraphVerification | null;
182
211
  created_at: Generated<Date>;
183
212
  updated_at: Generated<Date>;
184
213
  }
@@ -18,6 +18,27 @@ type FailureClass = (typeof FAILURE_CLASSES)[number];
18
18
  declare const RETRY_STATES: readonly ["open", "retrying", "halted", "resolved"];
19
19
  type RetryState = (typeof RETRY_STATES)[number];
20
20
  type BootstrapSource = "archive" | "genesis" | "import";
21
+ /**
22
+ * How far a subgraph's rows can be checked without trusting the server,
23
+ * derived from its sources and a scan of its handler. Never configured.
24
+ *
25
+ * - `state`: every input is a named state write, provable from block headers.
26
+ * Handlers run in the deterministic realm. A deployed `state` subgraph is
27
+ * verifiable: one with any reason is refused at deploy.
28
+ * - `events`: an input (events, prints, tx results, `ctx.client`) needs
29
+ * re-execution proofs, which are not served yet.
30
+ * - `none`: a source can never be proven as written (trait scope, tip-first
31
+ * backfill).
32
+ */
33
+ interface SubgraphVerification {
34
+ level: "state" | "events" | "none";
35
+ /** What stands between the subgraph and checkable rows, e.g.
36
+ * `print_event source "swaps" needs event proofs` or
37
+ * `subgraph.ts:41:12 Date: wall-clock time differs per run`. */
38
+ reasons: string[];
39
+ /** What stays unproven even at this level, e.g. tx attribution of writes. */
40
+ unproven: string[];
41
+ }
21
42
  interface BlocksTable {
22
43
  height: number;
23
44
  hash: string;
@@ -178,6 +199,14 @@ interface SubgraphsTable {
178
199
  expires_at: Date | null;
179
200
  /** (event type, contract) probe pairs persisted at deploy for weight classification. */
180
201
  sparse_probe_targets: unknown | null;
202
+ /** sha256 over everything that shapes rows (schema, handler bundle,
203
+ * startBlock, network, runtime). NULL for unbundled or older deploys. */
204
+ pin: string | null;
205
+ /** The canonical JSON `pin` hashes, so a client can recompute it. NULL
206
+ * wherever `pin` is, and for deploys before migration 0158. */
207
+ pin_preimage: string | null;
208
+ /** Level derived at deploy. NULL (older deploys) = not verifiable. */
209
+ verification: SubgraphVerification | null;
181
210
  created_at: Generated<Date>;
182
211
  updated_at: Generated<Date>;
183
212
  }
@@ -186,6 +186,21 @@ type IndexTransactionRow = {
186
186
  contract_id: string | null
187
187
  } | null
188
188
  };
189
+ /** A `/v1/index/state-writes` row read with `tx_context=true`. */
190
+ type IndexStateWriteRow = {
191
+ block_height: number
192
+ ordinal: number
193
+ /** Null for block-level writes. */
194
+ tx_index: number | null
195
+ key: string
196
+ value_hex: string
197
+ tx_id?: string | null
198
+ tx_sender?: string | null
199
+ tx_type?: string | null
200
+ tx_status?: string | null
201
+ tx_contract_id?: string | null
202
+ tx_function_name?: string | null
203
+ };
189
204
  type StreamsReorgRow = {
190
205
  id: string
191
206
  detected_at: string
@@ -263,6 +278,12 @@ declare class IndexHttpClient {
263
278
  * null. One page, limit 1 — built for sparse-scan probes. */
264
279
  firstEventHeight(eventType: string, fromHeight: number, toHeight: number, contractId?: string): Promise<number | null>;
265
280
  walkEvents(eventType: string, fromHeight: number, toHeight: number, withTx?: boolean, contractId?: string): Promise<IndexEventRow[]>;
281
+ /** Every node storage write over the range, each with its writing tx
282
+ * (`tx_context=true`); scoped to one contract's keys when given. */
283
+ walkStateWrites(fromHeight: number, toHeight: number, contractId?: string): Promise<IndexStateWriteRow[]>;
284
+ /** Lowest height the Index serves `state_writes` for, or null when it has
285
+ * none (its node does not deliver them). One row. */
286
+ firstStateWriteHeight(): Promise<number | null>;
266
287
  walkTransactions(fromHeight: number, toHeight: number): Promise<IndexTransactionRow[]>;
267
288
  /** Canonical tip height from the Streams clock. */
268
289
  getStreamsTip(): Promise<number>;
@@ -347,4 +368,4 @@ declare class IndexHttpClient {
347
368
  }
348
369
  /** Decoder + subgraph processor client. One env contract; omits bearer when no key. */
349
370
  declare function createInternalIndexHttpClient(): IndexHttpClient;
350
- export { BillingPausedCode, BillingPausedError, IndexBlockRow, IndexEventRow, IndexHttpClient, IndexHttpOptions, IndexHttpStatusError, IndexTipEnvelope, IndexTransactionRow, MAX_INDEX_WAIT_SECONDS, StreamsReorgRow, createInternalIndexHttpClient };
371
+ export { BillingPausedCode, BillingPausedError, IndexBlockRow, IndexEventRow, IndexHttpClient, IndexHttpOptions, IndexHttpStatusError, IndexStateWriteRow, IndexTipEnvelope, IndexTransactionRow, MAX_INDEX_WAIT_SECONDS, StreamsReorgRow, createInternalIndexHttpClient };
@@ -179,6 +179,16 @@ class IndexHttpClient {
179
179
  ...contractId ? { contract_id: contractId } : {}
180
180
  });
181
181
  }
182
+ walkStateWrites(fromHeight, toHeight, contractId) {
183
+ return this.walk("/v1/index/state-writes", "state_writes", fromHeight, toHeight, {
184
+ tx_context: "true",
185
+ ...contractId ? { contract_id: contractId } : {}
186
+ });
187
+ }
188
+ async firstStateWriteHeight() {
189
+ const { items } = await this.getPage("/v1/index/state-writes", "state_writes", new URLSearchParams({ from_height: "0", limit: "1" }));
190
+ return items[0]?.block_height ?? null;
191
+ }
182
192
  walkTransactions(fromHeight, toHeight) {
183
193
  return this.walk("/v1/index/transactions", "transactions", fromHeight, toHeight);
184
194
  }
@@ -273,5 +283,5 @@ export {
273
283
  createInternalIndexHttpClient
274
284
  };
275
285
 
276
- //# debugId=6A36B9F72886706564756E2164756E21
286
+ //# debugId=3E907135EB430D7E64756E2164756E21
277
287
  //# sourceMappingURL=index-http.js.map
@@ -3,9 +3,9 @@
3
3
  "sources": ["../src/index-internal-auth.ts", "../src/index-http.ts"],
4
4
  "sourcesContent": [
5
5
  "// Internal service credentials for first-party consumers of /v1/index and\n// /v1/streams over HTTP (decoder, subgraph processor PublicApiBlockSource).\n// Seeded as internal tenants with NO account_id, so these reads are\n// unmetered (metering gates on account_id). Lives in shared so the API (seed)\n// and the consumers import it without a cycle.\n//\n// Resolution: STREAMS_INTERNAL_API_KEY / INDEX_INTERNAL_API_KEY, then\n// INSTANCE_TOKEN (OSS). No committed fallback. Empty-string env values fall\n// through (`trim()`, then empty): compose interpolates\n// `${STREAMS_INTERNAL_API_KEY:-}` which is \"\" when unset.\n//\n// Archive keys must not use the `sk-sl_` prefix; generate with `sl-int_`.\n// OSS compose should omit the internal env and send INSTANCE_TOKEN.\nexport const INDEX_INTERNAL_TENANT_ID = \"tenant_index_internal\";\nexport const INTERNAL_STREAMS_TENANT_ID = \"tenant_streams_decode_internal\";\n\nconst DEFAULT_INTERNAL_INDEX_BASE_URL = \"http://api:3800\";\n\nconst MISSING_INTERNAL_STREAMS_API_KEY =\n\t\"Set STREAMS_INTERNAL_API_KEY (archive; prefix sl-int_) or INSTANCE_TOKEN (OSS). Empty env no longer falls back to a committed secret.\";\n\nconst MISSING_INTERNAL_INDEX_API_KEY =\n\t\"Set INDEX_INTERNAL_API_KEY (archive; prefix sl-int_) or INSTANCE_TOKEN (OSS). Without one, Index reads carry no credential and platform mode 401s every one of them.\";\n\nexport function defaultInternalIndexApiKey(\n\tenv: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n\tconst internal = env.INDEX_INTERNAL_API_KEY?.trim();\n\tif (internal) return internal;\n\tconst instance = env.INSTANCE_TOKEN?.trim();\n\tif (instance) return instance;\n\treturn undefined;\n}\n\nexport function defaultInternalStreamsApiKey(\n\tenv: NodeJS.ProcessEnv = process.env,\n): string | undefined {\n\tconst internal = env.STREAMS_INTERNAL_API_KEY?.trim();\n\tif (internal) return internal;\n\tconst instance = env.INSTANCE_TOKEN?.trim();\n\tif (instance) return instance;\n\treturn undefined;\n}\n\n/**\n * Platform-mode Index consumer: refuse to run with neither credential.\n *\n * Platform mode requires a credential on every Index read (a keyless read\n * 401s, `api/src/index/auth.ts`) — a first-party consumer with no internal\n * key would fail every request at call time. Fail here at startup instead.\n *\n * Self-hosted (oss/dedicated) instances are not gated, so they must NOT call\n * this — `defaultInternalIndexApiKey()` returning undefined is correct there.\n */\nexport function requireInternalIndexApiKey(\n\tenv: NodeJS.ProcessEnv = process.env,\n): string {\n\tconst key = defaultInternalIndexApiKey(env);\n\tif (!key) throw new Error(MISSING_INTERNAL_INDEX_API_KEY);\n\treturn key;\n}\n\n/** Decoder startup: refuse to run with neither credential. */\nexport function requireInternalStreamsApiKey(\n\tenv: NodeJS.ProcessEnv = process.env,\n): string {\n\tconst key = defaultInternalStreamsApiKey(env);\n\tif (!key) throw new Error(MISSING_INTERNAL_STREAMS_API_KEY);\n\treturn key;\n}\n\n/** Shared env contract for decoder + subgraph HTTP dogfood. */\nexport function defaultInternalIndexBaseUrl(): string {\n\treturn (\n\t\tprocess.env.SUBGRAPH_INDEX_API_URL ||\n\t\tprocess.env.STREAMS_API_URL ||\n\t\tDEFAULT_INTERNAL_INDEX_BASE_URL\n\t);\n}\n",
6
- "import {\n\tdefaultInternalIndexApiKey,\n\tdefaultInternalIndexBaseUrl,\n\tdefaultInternalStreamsApiKey,\n} from \"./index-internal-auth.ts\";\nimport type { RuneStreamsEvent } from \"./streams-rows/events.ts\";\n\n/**\n * Low-level transport for the public Index (`/v1/index`) + Streams clock\n * (`/v1/streams`) HTTP APIs: cursor-paginated reads, tip, reorgs. Lives in\n * `shared` (a leaf both the SDK and the subgraph runtime depend on) so the wire\n * format has one home and no package cycle. The SDK's ergonomic client should\n * eventually consume these row types too (see the plan's convergence task).\n *\n * This is intentionally minimal — just the GETs the subgraph runtime's\n * PublicApiBlockSource needs. It is NOT the SDK's full client (walk/consume/\n * retries/auth resolution).\n */\n\nconst PAGE_LIMIT = 1000;\n\n/** Ceiling on the Index long-poll `wait` param (seconds), shared by client and\n * server so neither drifts from the other. Kept comfortably under app-server\n * Caddy's `reverse_proxy` (no read/write timeout override, so this is a\n * self-imposed ceiling, not one it's protecting against) and Bun's own\n * 90s idleTimeout on the api listener. */\nexport const MAX_INDEX_WAIT_SECONDS = 25;\n\n// Transport resilience for the streams-index data plane. The api runs N>1\n// replicas behind Caddy; during a rolling deploy one replica is briefly\n// unreachable, surfacing as a thrown fetch (connection refused/reset) or a\n// Caddy 502/503/504 while it fails over. Retrying a few times with backoff\n// makes a single-replica recreate transparent to the subgraph-processor /\n// decoder — closing the processors-depend-on-api coupling.\nconst MAX_ATTEMPTS = 4;\nconst RETRY_BASE_MS = 150;\nconst RETRYABLE_STATUS = new Set([502, 503, 504]);\n\nconst delay = (ms: number): Promise<void> =>\n\tnew Promise((resolve) => setTimeout(resolve, ms));\n\n/** A non-2xx Index/Streams response, carrying the status so a caller can\n * distinguish \"the server rejected this request\" (e.g. a `wait`/`from_height`\n * param an older server's `validateQueryParams` doesn't recognize, HTTP 400)\n * from a transient outage — see `getIndexTipEnvelope`'s wait fallback. */\nexport class IndexHttpStatusError extends Error {\n\tconstructor(\n\t\treadonly status: number,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"IndexHttpStatusError\";\n\t}\n}\n\n/** The two refusals a metered read can get once an account is past its free\n * allowance (`checkRowsAllowance`). Both are deliberate billing states, not\n * outages: retrying them in a storm only burns the request budget. */\nexport type BillingPausedCode = \"spend_cap_reached\" | \"insufficient_credits\";\n\nconst BILLING_PAUSED_CODES: ReadonlySet<string> = new Set<BillingPausedCode>([\n\t\"spend_cap_reached\",\n\t\"insufficient_credits\",\n]);\n\n/** A read refused with `402` and `{ error: \"spend_cap_reached\" |\n * \"insufficient_credits\" }`. Raised only on an exact body match, so an\n * unmetered (self-host) server never produces it. */\nexport class BillingPausedError extends IndexHttpStatusError {\n\tconstructor(\n\t\treadonly code: BillingPausedCode,\n\t\tmessage: string,\n\t) {\n\t\tsuper(402, message);\n\t\tthis.name = \"BillingPausedError\";\n\t}\n}\n\n/** `BillingPausedError` when `status`/`body` is exactly a billing refusal. */\nfunction billingPausedFrom(\n\tstatus: number,\n\tbody: string,\n\tmessage: string,\n): BillingPausedError | null {\n\tif (status !== 402) return null;\n\ttry {\n\t\tconst code = (JSON.parse(body) as { error?: unknown } | null)?.error;\n\t\tif (typeof code === \"string\" && BILLING_PAUSED_CODES.has(code)) {\n\t\t\treturn new BillingPausedError(code as BillingPausedCode, message);\n\t\t}\n\t} catch {\n\t\t// Not JSON: not a billing refusal.\n\t}\n\treturn null;\n}\n\ntype Envelope<K extends string, T> = {\n\t[P in K]: T[];\n} & { next_cursor: string | null };\n\n// ── Index API wire shapes (single source of truth) ─────────────────────────\nexport type IndexBlockRow = {\n\tblock_height: number;\n\tblock_hash: string;\n\tparent_hash: string;\n\tburn_block_height: number;\n\tburn_block_hash: string | null;\n\t/** Nakamoto StacksBlockId. Absent from pre-2026-07 API deployments. */\n\tindex_block_hash?: string | null;\n\tblock_time: string | null;\n};\n\ntype IndexEventCommon = {\n\tblock_height: number;\n\ttx_id: string;\n\ttx_index?: number;\n\tevent_index: number;\n\tcontract_id: string | null;\n\t/** Submitting-tx context — present only when the read passed `tx_context=true`.\n\t * Lets a consumer build the tx for `ctx.tx` without a separate\n\t * walkTransactions (the ~37x reindex over-fetch; see indexing-speed plan). */\n\ttx_sender?: string | null;\n\ttx_type?: string | null;\n\ttx_status?: string | null;\n\ttx_contract_id?: string | null;\n\ttx_function_name?: string | null;\n};\n\nexport type IndexEventRow = IndexEventCommon &\n\t(\n\t\t| {\n\t\t\t\tevent_type: \"ft_transfer\" | \"ft_mint\" | \"ft_burn\";\n\t\t\t\tasset_identifier: string;\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tamount: string;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"nft_transfer\" | \"nft_mint\" | \"nft_burn\";\n\t\t\t\tasset_identifier: string;\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tvalue: string;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"stx_transfer\" | \"stx_mint\" | \"stx_burn\";\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tamount: string;\n\t\t\t\tmemo?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"stx_lock\";\n\t\t\t\tsender: string;\n\t\t\t\tamount: string;\n\t\t\t\tpayload: { unlock_height: string | null };\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"print\";\n\t\t\t\tpayload: {\n\t\t\t\t\ttopic: string | null;\n\t\t\t\t\tvalue: unknown;\n\t\t\t\t\traw_value: string | null;\n\t\t\t\t};\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"nested_contract_call\";\n\t\t\t\tsender?: string | null;\n\t\t\t\tcaller: string;\n\t\t\t\tfunction_name: string;\n\t\t\t\tfunction_args?: unknown;\n\t\t\t\traw_result?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"var_set\";\n\t\t\t\tvar_name: string;\n\t\t\t\traw_value?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"map_set\" | \"map_insert\";\n\t\t\t\tmap: string;\n\t\t\t\traw_key?: string | null;\n\t\t\t\traw_value?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"map_delete\";\n\t\t\t\tmap: string;\n\t\t\t\traw_key?: string | null;\n\t\t }\n\t);\n\nexport type IndexTransactionRow = {\n\ttx_id: string;\n\tblock_height: number;\n\tblock_time?: string | null;\n\tburn_block_height?: number | null;\n\ttx_index: number;\n\ttx_type: string;\n\tsender: string;\n\tstatus: string;\n\tcontract_call?: {\n\t\tcontract_id: string;\n\t\tfunction_name: string;\n\t\tfunction_args_hex?: string[] | null;\n\t\tresult_hex?: string | null;\n\t} | null;\n\tsmart_contract?: { contract_id: string | null } | null;\n};\n\nexport type StreamsReorgRow = {\n\tid: string;\n\tdetected_at: string;\n\tfork_point_height: number;\n\torphaned_range: { from: string; to: string };\n\tnew_canonical_tip: string;\n};\n\nexport type IndexHttpOptions = {\n\t/** Base URL for /v1/index (the decoded data plane). */\n\tindexBaseUrl: string;\n\t/** Bearer for /v1/index. Empty/unset omits the header. */\n\tindexApiKey?: string;\n\t/** Base URL for /v1/streams (the canonical clock). */\n\tstreamsBaseUrl: string;\n\t/** Bearer for /v1/streams. Empty/unset omits the header. */\n\tstreamsApiKey?: string;\n};\n\n/** `/v1/index/blocks?limit=1`'s envelope — read for its `tip`, not its rows. */\nexport type IndexTipEnvelope = {\n\ttip: {\n\t\tblock_height: number;\n\t\tsource_block_height?: number;\n\t\t/**\n\t\t * Committed height per classic decoded event_type, e.g.\n\t\t * `{ ft_transfer: 950, stx_transfer: 940 }`. `null` means that decoder\n\t\t * has no checkpoint yet (fail-closed for a caller that references it,\n\t\t * not \"committed to height 0\"); a missing key means an older server\n\t\t * that doesn't send this field at all. Absent on an instance built\n\t\t * before this field was added.\n\t\t */\n\t\tdecoded_heights?: Record<string, number | null>;\n\t};\n};\n\nexport class IndexHttpClient {\n\tprivate readonly indexBaseUrl: string;\n\tprivate readonly indexApiKey: string | undefined;\n\tprivate readonly streamsBaseUrl: string;\n\tprivate readonly streamsApiKey: string | undefined;\n\t/** Populated by the last `getIndexTipEnvelope()` call (via `getIndexTip`/\n\t * `getIndexSourceTip`) so `getDecodedHeights()` can read the SAME response\n\t * synchronously instead of making a second request. */\n\tprivate lastTipEnvelope: IndexTipEnvelope | undefined;\n\t/** Flips false the first time a `wait` request 400s — see\n\t * `getIndexTipEnvelope`'s catch. Per-instance, not global: a fresh client\n\t * (e.g. against a different/upgraded server) always tries wait again. */\n\tprivate waitSupported = true;\n\n\tconstructor(opts: IndexHttpOptions) {\n\t\tthis.indexBaseUrl = opts.indexBaseUrl.replace(/\\/+$/, \"\");\n\t\tthis.indexApiKey = opts.indexApiKey ?? defaultInternalIndexApiKey();\n\t\tthis.streamsBaseUrl = opts.streamsBaseUrl.replace(/\\/+$/, \"\");\n\t\tthis.streamsApiKey = opts.streamsApiKey;\n\t}\n\n\tprivate async get<T>(url: string, apiKey: string | undefined): Promise<T> {\n\t\t// Index reads are anon — omit the header entirely when no key is set, so\n\t\t// an empty key reads anonymously rather than 401-ing as an invalid bearer.\n\t\tconst headers: Record<string, string> = apiKey\n\t\t\t? { authorization: `Bearer ${apiKey}` }\n\t\t\t: {};\n\t\tlet lastErr: unknown;\n\t\tfor (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n\t\t\tlet res: Response;\n\t\t\ttry {\n\t\t\t\tres = await fetch(url, { headers });\n\t\t\t} catch (err) {\n\t\t\t\t// An explicit abort/cancel is intentional — surface it immediately\n\t\t\t\t// rather than burning the retry budget masking it as transient.\n\t\t\t\tif (err instanceof Error && err.name === \"AbortError\") throw err;\n\t\t\t\t// Otherwise a transport-level failure (connection refused/reset) —\n\t\t\t\t// e.g. an api replica mid-recreate. Retry; the next attempt\n\t\t\t\t// round-robins to a healthy replica.\n\t\t\t\tlastErr = err;\n\t\t\t\tif (attempt >= MAX_ATTEMPTS) break;\n\t\t\t\tawait delay(RETRY_BASE_MS * 2 ** (attempt - 1));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!res.ok) {\n\t\t\t\tif (RETRYABLE_STATUS.has(res.status) && attempt < MAX_ATTEMPTS) {\n\t\t\t\t\t// Drain the body so the connection can be reused, then back off.\n\t\t\t\t\tawait res.text().catch(() => {});\n\t\t\t\t\tawait delay(RETRY_BASE_MS * 2 ** (attempt - 1));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst body = await res.text();\n\t\t\t\tconst message = `GET ${url} → ${res.status} ${body}`;\n\t\t\t\tthrow (\n\t\t\t\t\tbillingPausedFrom(res.status, body, message) ??\n\t\t\t\t\tnew IndexHttpStatusError(res.status, message)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn (await res.json()) as T;\n\t\t}\n\t\tthrow (\n\t\t\tlastErr ?? new Error(`GET ${url} failed after ${MAX_ATTEMPTS} attempts`)\n\t\t);\n\t}\n\n\t/** Fetch a single cursor page of an Index collection. */\n\tprivate async getPage<K extends string, T>(\n\t\tpath: string,\n\t\tkey: K,\n\t\tparams: URLSearchParams,\n\t): Promise<{ items: T[]; next_cursor: string | null }> {\n\t\tconst env: Envelope<K, T> = await this.get(\n\t\t\t`${this.indexBaseUrl}${path}?${params}`,\n\t\t\tthis.indexApiKey,\n\t\t);\n\t\treturn { items: env[key], next_cursor: env.next_cursor };\n\t}\n\n\t/** Drain a cursor-paginated Index collection over [fromHeight, toHeight]. */\n\tprivate async walk<K extends string, T>(\n\t\tpath: string,\n\t\tkey: K,\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t\textraParams: Record<string, string> = {},\n\t): Promise<T[]> {\n\t\tconst out: T[] = [];\n\t\tlet cursor: string | null = null;\n\t\tdo {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\tto_height: String(toHeight),\n\t\t\t\tlimit: String(PAGE_LIMIT),\n\t\t\t\t...extraParams,\n\t\t\t});\n\t\t\t// from_height and cursor are mutually exclusive — anchor the first page\n\t\t\t// by height, then page forward by cursor only.\n\t\t\tif (cursor) params.set(\"cursor\", cursor);\n\t\t\telse params.set(\"from_height\", String(fromHeight));\n\t\t\tconst { items, next_cursor } = await this.getPage<K, T>(\n\t\t\t\tpath,\n\t\t\t\tkey,\n\t\t\t\tparams,\n\t\t\t);\n\t\t\tout.push(...items);\n\t\t\tcursor = next_cursor;\n\t\t} while (cursor);\n\t\treturn out;\n\t}\n\n\t/**\n\t * Fetch ONE page of contract-call transactions filtered to `contractId`.\n\t * Unlike walk(), this does NOT drain — the caller pages by feeding back\n\t * next_cursor — so a sparse high-volume filter (e.g. a single contract over\n\t * all history) costs one request per batch, not O(all-history) per tick.\n\t * `cursor` is exclusive (rows strictly after it); on the first call pass\n\t * `fromHeight` to anchor the backfill start instead.\n\t */\n\tasync fetchContractCalls(\n\t\tcontractId: string,\n\t\topts: {\n\t\t\ttoHeight: number;\n\t\t\tcursor?: string | null;\n\t\t\tfromHeight?: number;\n\t\t\tlimit?: number;\n\t\t},\n\t): Promise<{\n\t\ttransactions: IndexTransactionRow[];\n\t\tnext_cursor: string | null;\n\t}> {\n\t\tconst params = new URLSearchParams({\n\t\t\tto_height: String(opts.toHeight),\n\t\t\tlimit: String(opts.limit ?? PAGE_LIMIT),\n\t\t\tcontract_id: contractId,\n\t\t});\n\t\tif (opts.cursor) params.set(\"cursor\", opts.cursor);\n\t\telse params.set(\"from_height\", String(opts.fromHeight ?? 0));\n\t\tconst { items, next_cursor } = await this.getPage<\n\t\t\t\"transactions\",\n\t\t\tIndexTransactionRow\n\t\t>(\"/v1/index/transactions\", \"transactions\", params);\n\t\treturn { transactions: items, next_cursor };\n\t}\n\n\twalkBlocks(fromHeight: number, toHeight: number): Promise<IndexBlockRow[]> {\n\t\treturn this.walk<\"blocks\", IndexBlockRow>(\n\t\t\t\"/v1/index/blocks\",\n\t\t\t\"blocks\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t);\n\t}\n\n\t/** Lowest block height in [fromHeight, toHeight] with a matching event, or\n\t * null. One page, limit 1 — built for sparse-scan probes. */\n\tasync firstEventHeight(\n\t\teventType: string,\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t\tcontractId?: string,\n\t): Promise<number | null> {\n\t\tconst params = new URLSearchParams({\n\t\t\tevent_type: eventType,\n\t\t\tfrom_height: String(fromHeight),\n\t\t\tto_height: String(toHeight),\n\t\t\tlimit: \"1\",\n\t\t\t...(contractId ? { contract_id: contractId } : {}),\n\t\t});\n\t\tconst { items } = await this.getPage<\"events\", IndexEventRow>(\n\t\t\t\"/v1/index/events\",\n\t\t\t\"events\",\n\t\t\tparams,\n\t\t);\n\t\treturn items[0]?.block_height ?? null;\n\t}\n\n\twalkEvents(\n\t\teventType: string,\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t\t/** Join the submitting tx so each row carries `tx_*` — lets an event-only\n\t\t * subgraph skip walkTransactions and build `ctx.tx` from the event. */\n\t\twithTx = false,\n\t\t/** Scope the walk to one contract (what a filter pins), so the Index\n\t\t * returns, and meters, only that contract's rows. */\n\t\tcontractId?: string,\n\t): Promise<IndexEventRow[]> {\n\t\treturn this.walk<\"events\", IndexEventRow>(\n\t\t\t\"/v1/index/events\",\n\t\t\t\"events\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t\t{\n\t\t\t\tevent_type: eventType,\n\t\t\t\t...(withTx ? { tx_context: \"true\" } : {}),\n\t\t\t\t...(contractId ? { contract_id: contractId } : {}),\n\t\t\t},\n\t\t);\n\t}\n\n\twalkTransactions(\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t): Promise<IndexTransactionRow[]> {\n\t\treturn this.walk<\"transactions\", IndexTransactionRow>(\n\t\t\t\"/v1/index/transactions\",\n\t\t\t\"transactions\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t);\n\t}\n\n\t/** Canonical tip height from the Streams clock. */\n\tasync getStreamsTip(): Promise<number> {\n\t\tconst tip = await this.get<{ block_height: number }>(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/tip`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t\treturn Number(tip.block_height) || 0;\n\t}\n\n\t/**\n\t * Highest block height the Index data plane can serve (tip is inline in every\n\t * envelope). This is the data-availability bound — a consumer must not\n\t * process past it, even if the Streams clock is ahead.\n\t *\n\t * `opts.wait` (seconds, clamped to `MAX_INDEX_WAIT_SECONDS`) long-polls:\n\t * the server holds the request until a fresher tip than `opts.knownHeight`\n\t * is available or `wait` elapses. Pass `knownHeight` —\n\t * the last tip THIS caller observed — or `wait` is a no-op (nothing to\n\t * compare against). A caller that only wants the plain tip omits both.\n\t */\n\tasync getIndexTip(opts?: {\n\t\twait?: number;\n\t\tknownHeight?: number;\n\t\t/**\n\t\t * Event types this caller actually reads (e.g. a chain evaluator's\n\t\t * `referencedEventTypes`). Narrows the returned tip — and, together with\n\t\t * `wait`, what counts as \"nothing new\" — to the MIN committed height\n\t\t * over just these types, instead of the global floor over EVERY classic\n\t\t * decoder. Without it, an unrelated decoder committing (any of ~15,\n\t\t * several times a block) moves the global floor and makes an unrelated\n\t\t * `wait` return early — the exact busy-idle pattern this fixes. Omit\n\t\t * when there's no meaningful subset (falls back to the global floor,\n\t\t * unchanged from before this option existed).\n\t\t */\n\t\teventTypes?: readonly string[];\n\t}): Promise<number> {\n\t\t// `tip_only`: this method never reads `blocks[]`, only `tip`, so the\n\t\t// server can skip the row query. It ALSO fixes what `tip_only` exists\n\t\t// for: `blocks.length` is governed by the SOURCE tip (`/blocks` windows\n\t\t// to it), but `knownHeight` here is the DECODED tip this method itself\n\t\t// returns — comparing against raw rows made an idle `wait` return\n\t\t// almost instantly whenever ingest ran even slightly ahead of decode\n\t\t// (routes/index.ts's /blocks handler judges emptiness against\n\t\t// `tip.block_height` directly when `tip_only` is set, sidestepping the\n\t\t// mismatch). Do NOT set this on `getIndexSourceTip` below — its\n\t\t// `knownHeight` baseline is the source tip already, so the row-based\n\t\t// check is the CORRECT one there, not a bug to route around.\n\t\tconst env = await this.getIndexTipEnvelope({\n\t\t\t...opts,\n\t\t\ttipOnly: true,\n\t\t\teventTypes: opts?.eventTypes,\n\t\t});\n\t\treturn Number(env.tip?.block_height) || 0;\n\t}\n\n\t/** Ingest tip. VM rows land with the block; decoded `block_height` can lag.\n\t * Same `wait`/`knownHeight` long-poll contract as {@link getIndexTip}. */\n\tasync getIndexSourceTip(opts?: {\n\t\twait?: number;\n\t\tknownHeight?: number;\n\t}): Promise<number> {\n\t\tconst env = await this.getIndexTipEnvelope(opts);\n\t\treturn Number(env.tip?.source_block_height ?? env.tip?.block_height) || 0;\n\t}\n\n\t/**\n\t * Per-decoder committed heights from the last tip envelope fetched by\n\t * `getIndexTip()`/`getIndexSourceTip()` — reads the cached response, no\n\t * extra request. `undefined` before either has been called, or against an\n\t * older server that doesn't send the field. A caller (the chain evaluator)\n\t * uses this to bound itself by the decoders it actually reads instead of\n\t * the conservative cross-decoder floor `block_height` itself carries.\n\t */\n\tgetDecodedHeights(): Record<string, number | null> | undefined {\n\t\treturn this.lastTipEnvelope?.tip.decoded_heights;\n\t}\n\n\t/** False once a `wait`/`tip_only` request has 400'd on THIS client instance\n\t * (see the catch in `getIndexTipEnvelope`) — an older server that doesn't\n\t * recognize these params. A caller that reuses one client across many\n\t * calls (the chain evaluator) uses this to stop scheduling\n\t * itself as if `wait` actually blocked, instead of re-discovering the 400\n\t * on every single call. */\n\twaitIsSupported(): boolean {\n\t\treturn this.waitSupported;\n\t}\n\n\tprivate async getIndexTipEnvelope(opts?: {\n\t\twait?: number;\n\t\tknownHeight?: number;\n\t\ttipOnly?: boolean;\n\t\teventTypes?: readonly string[];\n\t}): Promise<IndexTipEnvelope> {\n\t\tconst wantsWait = this.waitSupported && (opts?.wait ?? 0) > 0;\n\t\tconst wantsTipOnly = this.waitSupported && Boolean(opts?.tipOnly);\n\t\tconst wantsEventTypes =\n\t\t\tthis.waitSupported && wantsTipOnly && Boolean(opts?.eventTypes?.length);\n\t\tconst params = new URLSearchParams({ limit: \"1\" });\n\t\tif (wantsTipOnly) params.set(\"tip_only\", \"true\");\n\t\t// Only meaningful alongside tip_only — it narrows what the returned\n\t\t// (and wait-compared) tip means, and tip_only is what makes the server\n\t\t// return a tip at all instead of a page of rows.\n\t\tif (wantsEventTypes && opts?.eventTypes) {\n\t\t\tparams.set(\"event_types\", opts.eventTypes.join(\",\"));\n\t\t}\n\t\tif (wantsWait) {\n\t\t\tparams.set(\n\t\t\t\t\"wait\",\n\t\t\t\tString(Math.min(MAX_INDEX_WAIT_SECONDS, Math.max(0, opts?.wait ?? 0))),\n\t\t\t);\n\t\t\t// `from_height` is what makes the wait meaningful here: with no cursor,\n\t\t\t// `/v1/index/blocks?limit=1` always returns a row (the default window's\n\t\t\t// oldest block), so the server would never see \"nothing new\" to wait\n\t\t\t// on. Anchoring one past the last height THIS caller saw makes the page\n\t\t\t// empty until a fresher block lands — the same signal the endpoint\n\t\t\t// already uses for a normal forward walk.\n\t\t\tif (opts?.knownHeight !== undefined) {\n\t\t\t\tparams.set(\"from_height\", String(opts.knownHeight + 1));\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst env = await this.get<IndexTipEnvelope>(\n\t\t\t\t`${this.indexBaseUrl}/v1/index/blocks?${params}`,\n\t\t\t\tthis.indexApiKey,\n\t\t\t);\n\t\t\tthis.lastTipEnvelope = env;\n\t\t\treturn env;\n\t\t} catch (err) {\n\t\t\t// An older server's `validateQueryParams` 400s on `wait`/`from_height`/\n\t\t\t// `tip_only`/`event_types` it doesn't recognize. Treat that ONE status\n\t\t\t// as \"this server predates long-poll\", not an outage: disable every\n\t\t\t// extension for the rest of this client's life (event_types is only\n\t\t\t// ever sent alongside tip_only, so gating on `wantsTipOnly` already\n\t\t\t// covers a server that knows `wait`/`tip_only` but not `event_types`\n\t\t\t// yet) and retry plainly, so `FallbackBlockSource` never mistakes a\n\t\t\t// version-skew 400 for the api being down and routes to the DB tap.\n\t\t\t// Gating on `wantsWait || wantsTipOnly` (not `wantsWait` alone)\n\t\t\t// matters mid-rollout: a server already upgraded for `wait` but not\n\t\t\t// yet for `tip_only` still 400s on the one it doesn't know, even on a\n\t\t\t// plain, non-waiting `getIndexTip()` call.\n\t\t\tif (\n\t\t\t\t(wantsWait || wantsTipOnly) &&\n\t\t\t\terr instanceof IndexHttpStatusError &&\n\t\t\t\terr.status === 400\n\t\t\t) {\n\t\t\t\tthis.waitSupported = false;\n\t\t\t\treturn this.getIndexTipEnvelope(opts);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Reorgs since a resume token (wall-clock `detected_at`-keyed). `chain`\n\t * defaults to `\"stacks\"` (unchanged); `\"bitcoin\"` reads Runes reorgs\n\t * (`btc_reorgs`, plan 060) instead — a separate feed, own resume tokens. */\n\tasync listReorgs(\n\t\tsince: string,\n\t\tchain: \"stacks\" | \"bitcoin\" = \"stacks\",\n\t): Promise<{ reorgs: StreamsReorgRow[]; next_since: string | null }> {\n\t\tconst params = new URLSearchParams({ since });\n\t\tif (chain === \"bitcoin\") params.set(\"chain\", \"bitcoin\");\n\t\treturn this.get(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/reorgs?${params}`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t}\n\n\t/** Bitcoin (Runes) Streams tip height — `chain=bitcoin`'s own clock,\n\t * unrelated to the Stacks tip (plan 060). */\n\tasync getBitcoinStreamsTip(): Promise<number> {\n\t\tconst tip = await this.get<{ block_height: number }>(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/tip?chain=bitcoin`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t\treturn Number(tip.block_height) || 0;\n\t}\n\n\t/**\n\t * One page of `chain=bitcoin` Streams events (Runes activity, plan 060's\n\t * evaluator source). Unlike `walkEvents`, this does NOT drain — the caller\n\t * pages by feeding back `next_cursor` — so a tick bounds its own work\n\t * instead of draining an unbounded backlog in one call. `afterCursor`\n\t * (exclusive) takes precedence over `fromHeight` when both are given,\n\t * mirroring the REST endpoint's own `cursor`/`from_height` exclusivity.\n\t */\n\tasync getBitcoinStreamsEventsPage(opts: {\n\t\ttypes: readonly string[];\n\t\ttoHeight: number;\n\t\tafterCursor?: string;\n\t\tfromHeight?: number;\n\t\tlimit?: number;\n\t}): Promise<{ events: RuneStreamsEvent[]; next_cursor: string | null }> {\n\t\tconst params = new URLSearchParams({\n\t\t\tchain: \"bitcoin\",\n\t\t\tto_height: String(opts.toHeight),\n\t\t\tlimit: String(opts.limit ?? PAGE_LIMIT),\n\t\t\ttypes: opts.types.join(\",\"),\n\t\t});\n\t\tif (opts.afterCursor) params.set(\"from_cursor\", opts.afterCursor);\n\t\telse params.set(\"from_height\", String(opts.fromHeight ?? 0));\n\t\treturn this.get(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/events?${params}`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t}\n}\n\n/** Decoder + subgraph processor client. One env contract; omits bearer when no key. */\nexport function createInternalIndexHttpClient(): IndexHttpClient {\n\tconst baseUrl = defaultInternalIndexBaseUrl();\n\treturn new IndexHttpClient({\n\t\tindexBaseUrl: baseUrl,\n\t\tstreamsBaseUrl: baseUrl,\n\t\tindexApiKey: defaultInternalIndexApiKey(),\n\t\tstreamsApiKey: defaultInternalStreamsApiKey(),\n\t});\n}\n"
6
+ "import {\n\tdefaultInternalIndexApiKey,\n\tdefaultInternalIndexBaseUrl,\n\tdefaultInternalStreamsApiKey,\n} from \"./index-internal-auth.ts\";\nimport type { RuneStreamsEvent } from \"./streams-rows/events.ts\";\n\n/**\n * Low-level transport for the public Index (`/v1/index`) + Streams clock\n * (`/v1/streams`) HTTP APIs: cursor-paginated reads, tip, reorgs. Lives in\n * `shared` (a leaf both the SDK and the subgraph runtime depend on) so the wire\n * format has one home and no package cycle. The SDK's ergonomic client should\n * eventually consume these row types too (see the plan's convergence task).\n *\n * This is intentionally minimal — just the GETs the subgraph runtime's\n * PublicApiBlockSource needs. It is NOT the SDK's full client (walk/consume/\n * retries/auth resolution).\n */\n\nconst PAGE_LIMIT = 1000;\n\n/** Ceiling on the Index long-poll `wait` param (seconds), shared by client and\n * server so neither drifts from the other. Kept comfortably under app-server\n * Caddy's `reverse_proxy` (no read/write timeout override, so this is a\n * self-imposed ceiling, not one it's protecting against) and Bun's own\n * 90s idleTimeout on the api listener. */\nexport const MAX_INDEX_WAIT_SECONDS = 25;\n\n// Transport resilience for the streams-index data plane. The api runs N>1\n// replicas behind Caddy; during a rolling deploy one replica is briefly\n// unreachable, surfacing as a thrown fetch (connection refused/reset) or a\n// Caddy 502/503/504 while it fails over. Retrying a few times with backoff\n// makes a single-replica recreate transparent to the subgraph-processor /\n// decoder — closing the processors-depend-on-api coupling.\nconst MAX_ATTEMPTS = 4;\nconst RETRY_BASE_MS = 150;\nconst RETRYABLE_STATUS = new Set([502, 503, 504]);\n\nconst delay = (ms: number): Promise<void> =>\n\tnew Promise((resolve) => setTimeout(resolve, ms));\n\n/** A non-2xx Index/Streams response, carrying the status so a caller can\n * distinguish \"the server rejected this request\" (e.g. a `wait`/`from_height`\n * param an older server's `validateQueryParams` doesn't recognize, HTTP 400)\n * from a transient outage — see `getIndexTipEnvelope`'s wait fallback. */\nexport class IndexHttpStatusError extends Error {\n\tconstructor(\n\t\treadonly status: number,\n\t\tmessage: string,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"IndexHttpStatusError\";\n\t}\n}\n\n/** The two refusals a metered read can get once an account is past its free\n * allowance (`checkRowsAllowance`). Both are deliberate billing states, not\n * outages: retrying them in a storm only burns the request budget. */\nexport type BillingPausedCode = \"spend_cap_reached\" | \"insufficient_credits\";\n\nconst BILLING_PAUSED_CODES: ReadonlySet<string> = new Set<BillingPausedCode>([\n\t\"spend_cap_reached\",\n\t\"insufficient_credits\",\n]);\n\n/** A read refused with `402` and `{ error: \"spend_cap_reached\" |\n * \"insufficient_credits\" }`. Raised only on an exact body match, so an\n * unmetered (self-host) server never produces it. */\nexport class BillingPausedError extends IndexHttpStatusError {\n\tconstructor(\n\t\treadonly code: BillingPausedCode,\n\t\tmessage: string,\n\t) {\n\t\tsuper(402, message);\n\t\tthis.name = \"BillingPausedError\";\n\t}\n}\n\n/** `BillingPausedError` when `status`/`body` is exactly a billing refusal. */\nfunction billingPausedFrom(\n\tstatus: number,\n\tbody: string,\n\tmessage: string,\n): BillingPausedError | null {\n\tif (status !== 402) return null;\n\ttry {\n\t\tconst code = (JSON.parse(body) as { error?: unknown } | null)?.error;\n\t\tif (typeof code === \"string\" && BILLING_PAUSED_CODES.has(code)) {\n\t\t\treturn new BillingPausedError(code as BillingPausedCode, message);\n\t\t}\n\t} catch {\n\t\t// Not JSON: not a billing refusal.\n\t}\n\treturn null;\n}\n\ntype Envelope<K extends string, T> = {\n\t[P in K]: T[];\n} & { next_cursor: string | null };\n\n// ── Index API wire shapes (single source of truth) ─────────────────────────\nexport type IndexBlockRow = {\n\tblock_height: number;\n\tblock_hash: string;\n\tparent_hash: string;\n\tburn_block_height: number;\n\tburn_block_hash: string | null;\n\t/** Nakamoto StacksBlockId. Absent from pre-2026-07 API deployments. */\n\tindex_block_hash?: string | null;\n\tblock_time: string | null;\n};\n\ntype IndexEventCommon = {\n\tblock_height: number;\n\ttx_id: string;\n\ttx_index?: number;\n\tevent_index: number;\n\tcontract_id: string | null;\n\t/** Submitting-tx context — present only when the read passed `tx_context=true`.\n\t * Lets a consumer build the tx for `ctx.tx` without a separate\n\t * walkTransactions (the ~37x reindex over-fetch; see indexing-speed plan). */\n\ttx_sender?: string | null;\n\ttx_type?: string | null;\n\ttx_status?: string | null;\n\ttx_contract_id?: string | null;\n\ttx_function_name?: string | null;\n};\n\nexport type IndexEventRow = IndexEventCommon &\n\t(\n\t\t| {\n\t\t\t\tevent_type: \"ft_transfer\" | \"ft_mint\" | \"ft_burn\";\n\t\t\t\tasset_identifier: string;\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tamount: string;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"nft_transfer\" | \"nft_mint\" | \"nft_burn\";\n\t\t\t\tasset_identifier: string;\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tvalue: string;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"stx_transfer\" | \"stx_mint\" | \"stx_burn\";\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tamount: string;\n\t\t\t\tmemo?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"stx_lock\";\n\t\t\t\tsender: string;\n\t\t\t\tamount: string;\n\t\t\t\tpayload: { unlock_height: string | null };\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"print\";\n\t\t\t\tpayload: {\n\t\t\t\t\ttopic: string | null;\n\t\t\t\t\tvalue: unknown;\n\t\t\t\t\traw_value: string | null;\n\t\t\t\t};\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"nested_contract_call\";\n\t\t\t\tsender?: string | null;\n\t\t\t\tcaller: string;\n\t\t\t\tfunction_name: string;\n\t\t\t\tfunction_args?: unknown;\n\t\t\t\traw_result?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"var_set\";\n\t\t\t\tvar_name: string;\n\t\t\t\traw_value?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"map_set\" | \"map_insert\";\n\t\t\t\tmap: string;\n\t\t\t\traw_key?: string | null;\n\t\t\t\traw_value?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"map_delete\";\n\t\t\t\tmap: string;\n\t\t\t\traw_key?: string | null;\n\t\t }\n\t);\n\nexport type IndexTransactionRow = {\n\ttx_id: string;\n\tblock_height: number;\n\tblock_time?: string | null;\n\tburn_block_height?: number | null;\n\ttx_index: number;\n\ttx_type: string;\n\tsender: string;\n\tstatus: string;\n\tcontract_call?: {\n\t\tcontract_id: string;\n\t\tfunction_name: string;\n\t\tfunction_args_hex?: string[] | null;\n\t\tresult_hex?: string | null;\n\t} | null;\n\tsmart_contract?: { contract_id: string | null } | null;\n};\n\n/** A `/v1/index/state-writes` row read with `tx_context=true`. */\nexport type IndexStateWriteRow = {\n\tblock_height: number;\n\tordinal: number;\n\t/** Null for block-level writes. */\n\ttx_index: number | null;\n\tkey: string;\n\tvalue_hex: string;\n\ttx_id?: string | null;\n\ttx_sender?: string | null;\n\ttx_type?: string | null;\n\ttx_status?: string | null;\n\ttx_contract_id?: string | null;\n\ttx_function_name?: string | null;\n};\n\nexport type StreamsReorgRow = {\n\tid: string;\n\tdetected_at: string;\n\tfork_point_height: number;\n\torphaned_range: { from: string; to: string };\n\tnew_canonical_tip: string;\n};\n\nexport type IndexHttpOptions = {\n\t/** Base URL for /v1/index (the decoded data plane). */\n\tindexBaseUrl: string;\n\t/** Bearer for /v1/index. Empty/unset omits the header. */\n\tindexApiKey?: string;\n\t/** Base URL for /v1/streams (the canonical clock). */\n\tstreamsBaseUrl: string;\n\t/** Bearer for /v1/streams. Empty/unset omits the header. */\n\tstreamsApiKey?: string;\n};\n\n/** `/v1/index/blocks?limit=1`'s envelope — read for its `tip`, not its rows. */\nexport type IndexTipEnvelope = {\n\ttip: {\n\t\tblock_height: number;\n\t\tsource_block_height?: number;\n\t\t/**\n\t\t * Committed height per classic decoded event_type, e.g.\n\t\t * `{ ft_transfer: 950, stx_transfer: 940 }`. `null` means that decoder\n\t\t * has no checkpoint yet (fail-closed for a caller that references it,\n\t\t * not \"committed to height 0\"); a missing key means an older server\n\t\t * that doesn't send this field at all. Absent on an instance built\n\t\t * before this field was added.\n\t\t */\n\t\tdecoded_heights?: Record<string, number | null>;\n\t};\n};\n\nexport class IndexHttpClient {\n\tprivate readonly indexBaseUrl: string;\n\tprivate readonly indexApiKey: string | undefined;\n\tprivate readonly streamsBaseUrl: string;\n\tprivate readonly streamsApiKey: string | undefined;\n\t/** Populated by the last `getIndexTipEnvelope()` call (via `getIndexTip`/\n\t * `getIndexSourceTip`) so `getDecodedHeights()` can read the SAME response\n\t * synchronously instead of making a second request. */\n\tprivate lastTipEnvelope: IndexTipEnvelope | undefined;\n\t/** Flips false the first time a `wait` request 400s — see\n\t * `getIndexTipEnvelope`'s catch. Per-instance, not global: a fresh client\n\t * (e.g. against a different/upgraded server) always tries wait again. */\n\tprivate waitSupported = true;\n\n\tconstructor(opts: IndexHttpOptions) {\n\t\tthis.indexBaseUrl = opts.indexBaseUrl.replace(/\\/+$/, \"\");\n\t\tthis.indexApiKey = opts.indexApiKey ?? defaultInternalIndexApiKey();\n\t\tthis.streamsBaseUrl = opts.streamsBaseUrl.replace(/\\/+$/, \"\");\n\t\tthis.streamsApiKey = opts.streamsApiKey;\n\t}\n\n\tprivate async get<T>(url: string, apiKey: string | undefined): Promise<T> {\n\t\t// Index reads are anon — omit the header entirely when no key is set, so\n\t\t// an empty key reads anonymously rather than 401-ing as an invalid bearer.\n\t\tconst headers: Record<string, string> = apiKey\n\t\t\t? { authorization: `Bearer ${apiKey}` }\n\t\t\t: {};\n\t\tlet lastErr: unknown;\n\t\tfor (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {\n\t\t\tlet res: Response;\n\t\t\ttry {\n\t\t\t\tres = await fetch(url, { headers });\n\t\t\t} catch (err) {\n\t\t\t\t// An explicit abort/cancel is intentional — surface it immediately\n\t\t\t\t// rather than burning the retry budget masking it as transient.\n\t\t\t\tif (err instanceof Error && err.name === \"AbortError\") throw err;\n\t\t\t\t// Otherwise a transport-level failure (connection refused/reset) —\n\t\t\t\t// e.g. an api replica mid-recreate. Retry; the next attempt\n\t\t\t\t// round-robins to a healthy replica.\n\t\t\t\tlastErr = err;\n\t\t\t\tif (attempt >= MAX_ATTEMPTS) break;\n\t\t\t\tawait delay(RETRY_BASE_MS * 2 ** (attempt - 1));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!res.ok) {\n\t\t\t\tif (RETRYABLE_STATUS.has(res.status) && attempt < MAX_ATTEMPTS) {\n\t\t\t\t\t// Drain the body so the connection can be reused, then back off.\n\t\t\t\t\tawait res.text().catch(() => {});\n\t\t\t\t\tawait delay(RETRY_BASE_MS * 2 ** (attempt - 1));\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst body = await res.text();\n\t\t\t\tconst message = `GET ${url} → ${res.status} ${body}`;\n\t\t\t\tthrow (\n\t\t\t\t\tbillingPausedFrom(res.status, body, message) ??\n\t\t\t\t\tnew IndexHttpStatusError(res.status, message)\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn (await res.json()) as T;\n\t\t}\n\t\tthrow (\n\t\t\tlastErr ?? new Error(`GET ${url} failed after ${MAX_ATTEMPTS} attempts`)\n\t\t);\n\t}\n\n\t/** Fetch a single cursor page of an Index collection. */\n\tprivate async getPage<K extends string, T>(\n\t\tpath: string,\n\t\tkey: K,\n\t\tparams: URLSearchParams,\n\t): Promise<{ items: T[]; next_cursor: string | null }> {\n\t\tconst env: Envelope<K, T> = await this.get(\n\t\t\t`${this.indexBaseUrl}${path}?${params}`,\n\t\t\tthis.indexApiKey,\n\t\t);\n\t\treturn { items: env[key], next_cursor: env.next_cursor };\n\t}\n\n\t/** Drain a cursor-paginated Index collection over [fromHeight, toHeight]. */\n\tprivate async walk<K extends string, T>(\n\t\tpath: string,\n\t\tkey: K,\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t\textraParams: Record<string, string> = {},\n\t): Promise<T[]> {\n\t\tconst out: T[] = [];\n\t\tlet cursor: string | null = null;\n\t\tdo {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\tto_height: String(toHeight),\n\t\t\t\tlimit: String(PAGE_LIMIT),\n\t\t\t\t...extraParams,\n\t\t\t});\n\t\t\t// from_height and cursor are mutually exclusive — anchor the first page\n\t\t\t// by height, then page forward by cursor only.\n\t\t\tif (cursor) params.set(\"cursor\", cursor);\n\t\t\telse params.set(\"from_height\", String(fromHeight));\n\t\t\tconst { items, next_cursor } = await this.getPage<K, T>(\n\t\t\t\tpath,\n\t\t\t\tkey,\n\t\t\t\tparams,\n\t\t\t);\n\t\t\tout.push(...items);\n\t\t\tcursor = next_cursor;\n\t\t} while (cursor);\n\t\treturn out;\n\t}\n\n\t/**\n\t * Fetch ONE page of contract-call transactions filtered to `contractId`.\n\t * Unlike walk(), this does NOT drain — the caller pages by feeding back\n\t * next_cursor — so a sparse high-volume filter (e.g. a single contract over\n\t * all history) costs one request per batch, not O(all-history) per tick.\n\t * `cursor` is exclusive (rows strictly after it); on the first call pass\n\t * `fromHeight` to anchor the backfill start instead.\n\t */\n\tasync fetchContractCalls(\n\t\tcontractId: string,\n\t\topts: {\n\t\t\ttoHeight: number;\n\t\t\tcursor?: string | null;\n\t\t\tfromHeight?: number;\n\t\t\tlimit?: number;\n\t\t},\n\t): Promise<{\n\t\ttransactions: IndexTransactionRow[];\n\t\tnext_cursor: string | null;\n\t}> {\n\t\tconst params = new URLSearchParams({\n\t\t\tto_height: String(opts.toHeight),\n\t\t\tlimit: String(opts.limit ?? PAGE_LIMIT),\n\t\t\tcontract_id: contractId,\n\t\t});\n\t\tif (opts.cursor) params.set(\"cursor\", opts.cursor);\n\t\telse params.set(\"from_height\", String(opts.fromHeight ?? 0));\n\t\tconst { items, next_cursor } = await this.getPage<\n\t\t\t\"transactions\",\n\t\t\tIndexTransactionRow\n\t\t>(\"/v1/index/transactions\", \"transactions\", params);\n\t\treturn { transactions: items, next_cursor };\n\t}\n\n\twalkBlocks(fromHeight: number, toHeight: number): Promise<IndexBlockRow[]> {\n\t\treturn this.walk<\"blocks\", IndexBlockRow>(\n\t\t\t\"/v1/index/blocks\",\n\t\t\t\"blocks\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t);\n\t}\n\n\t/** Lowest block height in [fromHeight, toHeight] with a matching event, or\n\t * null. One page, limit 1 — built for sparse-scan probes. */\n\tasync firstEventHeight(\n\t\teventType: string,\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t\tcontractId?: string,\n\t): Promise<number | null> {\n\t\tconst params = new URLSearchParams({\n\t\t\tevent_type: eventType,\n\t\t\tfrom_height: String(fromHeight),\n\t\t\tto_height: String(toHeight),\n\t\t\tlimit: \"1\",\n\t\t\t...(contractId ? { contract_id: contractId } : {}),\n\t\t});\n\t\tconst { items } = await this.getPage<\"events\", IndexEventRow>(\n\t\t\t\"/v1/index/events\",\n\t\t\t\"events\",\n\t\t\tparams,\n\t\t);\n\t\treturn items[0]?.block_height ?? null;\n\t}\n\n\twalkEvents(\n\t\teventType: string,\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t\t/** Join the submitting tx so each row carries `tx_*` — lets an event-only\n\t\t * subgraph skip walkTransactions and build `ctx.tx` from the event. */\n\t\twithTx = false,\n\t\t/** Scope the walk to one contract (what a filter pins), so the Index\n\t\t * returns, and meters, only that contract's rows. */\n\t\tcontractId?: string,\n\t): Promise<IndexEventRow[]> {\n\t\treturn this.walk<\"events\", IndexEventRow>(\n\t\t\t\"/v1/index/events\",\n\t\t\t\"events\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t\t{\n\t\t\t\tevent_type: eventType,\n\t\t\t\t...(withTx ? { tx_context: \"true\" } : {}),\n\t\t\t\t...(contractId ? { contract_id: contractId } : {}),\n\t\t\t},\n\t\t);\n\t}\n\n\t/** Every node storage write over the range, each with its writing tx\n\t * (`tx_context=true`); scoped to one contract's keys when given. */\n\twalkStateWrites(\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t\tcontractId?: string,\n\t): Promise<IndexStateWriteRow[]> {\n\t\treturn this.walk<\"state_writes\", IndexStateWriteRow>(\n\t\t\t\"/v1/index/state-writes\",\n\t\t\t\"state_writes\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t\t{\n\t\t\t\ttx_context: \"true\",\n\t\t\t\t...(contractId ? { contract_id: contractId } : {}),\n\t\t\t},\n\t\t);\n\t}\n\n\t/** Lowest height the Index serves `state_writes` for, or null when it has\n\t * none (its node does not deliver them). One row. */\n\tasync firstStateWriteHeight(): Promise<number | null> {\n\t\tconst { items } = await this.getPage<\"state_writes\", IndexStateWriteRow>(\n\t\t\t\"/v1/index/state-writes\",\n\t\t\t\"state_writes\",\n\t\t\tnew URLSearchParams({ from_height: \"0\", limit: \"1\" }),\n\t\t);\n\t\treturn items[0]?.block_height ?? null;\n\t}\n\n\twalkTransactions(\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t): Promise<IndexTransactionRow[]> {\n\t\treturn this.walk<\"transactions\", IndexTransactionRow>(\n\t\t\t\"/v1/index/transactions\",\n\t\t\t\"transactions\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t);\n\t}\n\n\t/** Canonical tip height from the Streams clock. */\n\tasync getStreamsTip(): Promise<number> {\n\t\tconst tip = await this.get<{ block_height: number }>(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/tip`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t\treturn Number(tip.block_height) || 0;\n\t}\n\n\t/**\n\t * Highest block height the Index data plane can serve (tip is inline in every\n\t * envelope). This is the data-availability bound — a consumer must not\n\t * process past it, even if the Streams clock is ahead.\n\t *\n\t * `opts.wait` (seconds, clamped to `MAX_INDEX_WAIT_SECONDS`) long-polls:\n\t * the server holds the request until a fresher tip than `opts.knownHeight`\n\t * is available or `wait` elapses. Pass `knownHeight` —\n\t * the last tip THIS caller observed — or `wait` is a no-op (nothing to\n\t * compare against). A caller that only wants the plain tip omits both.\n\t */\n\tasync getIndexTip(opts?: {\n\t\twait?: number;\n\t\tknownHeight?: number;\n\t\t/**\n\t\t * Event types this caller actually reads (e.g. a chain evaluator's\n\t\t * `referencedEventTypes`). Narrows the returned tip — and, together with\n\t\t * `wait`, what counts as \"nothing new\" — to the MIN committed height\n\t\t * over just these types, instead of the global floor over EVERY classic\n\t\t * decoder. Without it, an unrelated decoder committing (any of ~15,\n\t\t * several times a block) moves the global floor and makes an unrelated\n\t\t * `wait` return early — the exact busy-idle pattern this fixes. Omit\n\t\t * when there's no meaningful subset (falls back to the global floor,\n\t\t * unchanged from before this option existed).\n\t\t */\n\t\teventTypes?: readonly string[];\n\t}): Promise<number> {\n\t\t// `tip_only`: this method never reads `blocks[]`, only `tip`, so the\n\t\t// server can skip the row query. It ALSO fixes what `tip_only` exists\n\t\t// for: `blocks.length` is governed by the SOURCE tip (`/blocks` windows\n\t\t// to it), but `knownHeight` here is the DECODED tip this method itself\n\t\t// returns — comparing against raw rows made an idle `wait` return\n\t\t// almost instantly whenever ingest ran even slightly ahead of decode\n\t\t// (routes/index.ts's /blocks handler judges emptiness against\n\t\t// `tip.block_height` directly when `tip_only` is set, sidestepping the\n\t\t// mismatch). Do NOT set this on `getIndexSourceTip` below — its\n\t\t// `knownHeight` baseline is the source tip already, so the row-based\n\t\t// check is the CORRECT one there, not a bug to route around.\n\t\tconst env = await this.getIndexTipEnvelope({\n\t\t\t...opts,\n\t\t\ttipOnly: true,\n\t\t\teventTypes: opts?.eventTypes,\n\t\t});\n\t\treturn Number(env.tip?.block_height) || 0;\n\t}\n\n\t/** Ingest tip. VM rows land with the block; decoded `block_height` can lag.\n\t * Same `wait`/`knownHeight` long-poll contract as {@link getIndexTip}. */\n\tasync getIndexSourceTip(opts?: {\n\t\twait?: number;\n\t\tknownHeight?: number;\n\t}): Promise<number> {\n\t\tconst env = await this.getIndexTipEnvelope(opts);\n\t\treturn Number(env.tip?.source_block_height ?? env.tip?.block_height) || 0;\n\t}\n\n\t/**\n\t * Per-decoder committed heights from the last tip envelope fetched by\n\t * `getIndexTip()`/`getIndexSourceTip()` — reads the cached response, no\n\t * extra request. `undefined` before either has been called, or against an\n\t * older server that doesn't send the field. A caller (the chain evaluator)\n\t * uses this to bound itself by the decoders it actually reads instead of\n\t * the conservative cross-decoder floor `block_height` itself carries.\n\t */\n\tgetDecodedHeights(): Record<string, number | null> | undefined {\n\t\treturn this.lastTipEnvelope?.tip.decoded_heights;\n\t}\n\n\t/** False once a `wait`/`tip_only` request has 400'd on THIS client instance\n\t * (see the catch in `getIndexTipEnvelope`) — an older server that doesn't\n\t * recognize these params. A caller that reuses one client across many\n\t * calls (the chain evaluator) uses this to stop scheduling\n\t * itself as if `wait` actually blocked, instead of re-discovering the 400\n\t * on every single call. */\n\twaitIsSupported(): boolean {\n\t\treturn this.waitSupported;\n\t}\n\n\tprivate async getIndexTipEnvelope(opts?: {\n\t\twait?: number;\n\t\tknownHeight?: number;\n\t\ttipOnly?: boolean;\n\t\teventTypes?: readonly string[];\n\t}): Promise<IndexTipEnvelope> {\n\t\tconst wantsWait = this.waitSupported && (opts?.wait ?? 0) > 0;\n\t\tconst wantsTipOnly = this.waitSupported && Boolean(opts?.tipOnly);\n\t\tconst wantsEventTypes =\n\t\t\tthis.waitSupported && wantsTipOnly && Boolean(opts?.eventTypes?.length);\n\t\tconst params = new URLSearchParams({ limit: \"1\" });\n\t\tif (wantsTipOnly) params.set(\"tip_only\", \"true\");\n\t\t// Only meaningful alongside tip_only — it narrows what the returned\n\t\t// (and wait-compared) tip means, and tip_only is what makes the server\n\t\t// return a tip at all instead of a page of rows.\n\t\tif (wantsEventTypes && opts?.eventTypes) {\n\t\t\tparams.set(\"event_types\", opts.eventTypes.join(\",\"));\n\t\t}\n\t\tif (wantsWait) {\n\t\t\tparams.set(\n\t\t\t\t\"wait\",\n\t\t\t\tString(Math.min(MAX_INDEX_WAIT_SECONDS, Math.max(0, opts?.wait ?? 0))),\n\t\t\t);\n\t\t\t// `from_height` is what makes the wait meaningful here: with no cursor,\n\t\t\t// `/v1/index/blocks?limit=1` always returns a row (the default window's\n\t\t\t// oldest block), so the server would never see \"nothing new\" to wait\n\t\t\t// on. Anchoring one past the last height THIS caller saw makes the page\n\t\t\t// empty until a fresher block lands — the same signal the endpoint\n\t\t\t// already uses for a normal forward walk.\n\t\t\tif (opts?.knownHeight !== undefined) {\n\t\t\t\tparams.set(\"from_height\", String(opts.knownHeight + 1));\n\t\t\t}\n\t\t}\n\t\ttry {\n\t\t\tconst env = await this.get<IndexTipEnvelope>(\n\t\t\t\t`${this.indexBaseUrl}/v1/index/blocks?${params}`,\n\t\t\t\tthis.indexApiKey,\n\t\t\t);\n\t\t\tthis.lastTipEnvelope = env;\n\t\t\treturn env;\n\t\t} catch (err) {\n\t\t\t// An older server's `validateQueryParams` 400s on `wait`/`from_height`/\n\t\t\t// `tip_only`/`event_types` it doesn't recognize. Treat that ONE status\n\t\t\t// as \"this server predates long-poll\", not an outage: disable every\n\t\t\t// extension for the rest of this client's life (event_types is only\n\t\t\t// ever sent alongside tip_only, so gating on `wantsTipOnly` already\n\t\t\t// covers a server that knows `wait`/`tip_only` but not `event_types`\n\t\t\t// yet) and retry plainly, so `FallbackBlockSource` never mistakes a\n\t\t\t// version-skew 400 for the api being down and routes to the DB tap.\n\t\t\t// Gating on `wantsWait || wantsTipOnly` (not `wantsWait` alone)\n\t\t\t// matters mid-rollout: a server already upgraded for `wait` but not\n\t\t\t// yet for `tip_only` still 400s on the one it doesn't know, even on a\n\t\t\t// plain, non-waiting `getIndexTip()` call.\n\t\t\tif (\n\t\t\t\t(wantsWait || wantsTipOnly) &&\n\t\t\t\terr instanceof IndexHttpStatusError &&\n\t\t\t\terr.status === 400\n\t\t\t) {\n\t\t\t\tthis.waitSupported = false;\n\t\t\t\treturn this.getIndexTipEnvelope(opts);\n\t\t\t}\n\t\t\tthrow err;\n\t\t}\n\t}\n\n\t/** Reorgs since a resume token (wall-clock `detected_at`-keyed). `chain`\n\t * defaults to `\"stacks\"` (unchanged); `\"bitcoin\"` reads Runes reorgs\n\t * (`btc_reorgs`, plan 060) instead — a separate feed, own resume tokens. */\n\tasync listReorgs(\n\t\tsince: string,\n\t\tchain: \"stacks\" | \"bitcoin\" = \"stacks\",\n\t): Promise<{ reorgs: StreamsReorgRow[]; next_since: string | null }> {\n\t\tconst params = new URLSearchParams({ since });\n\t\tif (chain === \"bitcoin\") params.set(\"chain\", \"bitcoin\");\n\t\treturn this.get(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/reorgs?${params}`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t}\n\n\t/** Bitcoin (Runes) Streams tip height — `chain=bitcoin`'s own clock,\n\t * unrelated to the Stacks tip (plan 060). */\n\tasync getBitcoinStreamsTip(): Promise<number> {\n\t\tconst tip = await this.get<{ block_height: number }>(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/tip?chain=bitcoin`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t\treturn Number(tip.block_height) || 0;\n\t}\n\n\t/**\n\t * One page of `chain=bitcoin` Streams events (Runes activity, plan 060's\n\t * evaluator source). Unlike `walkEvents`, this does NOT drain — the caller\n\t * pages by feeding back `next_cursor` — so a tick bounds its own work\n\t * instead of draining an unbounded backlog in one call. `afterCursor`\n\t * (exclusive) takes precedence over `fromHeight` when both are given,\n\t * mirroring the REST endpoint's own `cursor`/`from_height` exclusivity.\n\t */\n\tasync getBitcoinStreamsEventsPage(opts: {\n\t\ttypes: readonly string[];\n\t\ttoHeight: number;\n\t\tafterCursor?: string;\n\t\tfromHeight?: number;\n\t\tlimit?: number;\n\t}): Promise<{ events: RuneStreamsEvent[]; next_cursor: string | null }> {\n\t\tconst params = new URLSearchParams({\n\t\t\tchain: \"bitcoin\",\n\t\t\tto_height: String(opts.toHeight),\n\t\t\tlimit: String(opts.limit ?? PAGE_LIMIT),\n\t\t\ttypes: opts.types.join(\",\"),\n\t\t});\n\t\tif (opts.afterCursor) params.set(\"from_cursor\", opts.afterCursor);\n\t\telse params.set(\"from_height\", String(opts.fromHeight ?? 0));\n\t\treturn this.get(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/events?${params}`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t}\n}\n\n/** Decoder + subgraph processor client. One env contract; omits bearer when no key. */\nexport function createInternalIndexHttpClient(): IndexHttpClient {\n\tconst baseUrl = defaultInternalIndexBaseUrl();\n\treturn new IndexHttpClient({\n\t\tindexBaseUrl: baseUrl,\n\t\tstreamsBaseUrl: baseUrl,\n\t\tindexApiKey: defaultInternalIndexApiKey(),\n\t\tstreamsApiKey: defaultInternalStreamsApiKey(),\n\t});\n}\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "6A36B9F72886706564756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "3E907135EB430D7E64756E2164756E21",
10
10
  "names": []
11
11
  }