{"version":3,"file":"fastkv.mjs","names":[],"sources":["../src/fastkv.ts"],"sourcesContent":["export type NetworkId = \"mainnet\" | \"testnet\";\n\ninterface FastKvEntry {\n  value: unknown;\n}\n\ninterface FastKvListResponse {\n  entries?: Array<FastKvEntry | null>;\n}\n\nimport { fetchJsonOrNull } from \"./http-client\";\n\nfunction getNetworkIdForAccount(accountId: string): NetworkId {\n  return accountId.endsWith(\".testnet\") ? \"testnet\" : \"mainnet\";\n}\n\nconst FASTKV_TESTNET_URL = \"https://kv.test.fastnear.com\";\nconst FASTKV_MAINNET_URL = \"https://kv.main.fastnear.com\";\nconst REGISTRY_NAMESPACE_TESTNET = \"dev.allthethings.testnet\";\nconst REGISTRY_NAMESPACE_MAINNET = \"dev.everything.near\";\n\nexport function getFastKvBaseUrlForNetwork(network: NetworkId): string {\n  return network === \"testnet\" ? FASTKV_TESTNET_URL : FASTKV_MAINNET_URL;\n}\n\nfunction getFastKvBaseUrlForAccount(accountId: string): string {\n  return getNetworkIdForAccount(accountId) === \"testnet\"\n    ? getFastKvBaseUrlForNetwork(\"testnet\")\n    : getFastKvBaseUrlForNetwork(\"mainnet\");\n}\n\nexport function buildRegistryConfigUrl(\n  accountId: string,\n  gatewayId: string,\n  registry?: string,\n): string {\n  const baseUrl = getFastKvBaseUrlForAccount(accountId);\n  const namespace = getRegistryNamespaceForAccount(accountId, registry);\n  const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId));\n  return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`;\n}\n\nexport function buildRegistryConfigUrlForNetwork(\n  network: NetworkId,\n  accountId: string,\n  gatewayId: string,\n  registry?: string,\n): string {\n  const baseUrl = getFastKvBaseUrlForNetwork(network);\n  const namespace = getRegistryNamespaceForNetwork(network, registry);\n  const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId));\n  return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`;\n}\n\nexport function getRegistryNamespaceForAccount(accountId: string, registry?: string): string {\n  return registry ?? getRegistryNamespaceForNetwork(getNetworkIdForAccount(accountId));\n}\n\nexport function getRegistryNamespaceForNetwork(network: NetworkId, registry?: string): string {\n  if (registry) return registry;\n  return network === \"testnet\" ? REGISTRY_NAMESPACE_TESTNET : REGISTRY_NAMESPACE_MAINNET;\n}\n\nfunction getRegistryConfigKey(\n  accountId: string,\n  gatewayId: string,\n  pathSegments: string[] = [],\n): string {\n  const suffix =\n    pathSegments.length > 0\n      ? `/${pathSegments.map((segment) => encodeURIComponent(segment)).join(\"/\")}`\n      : \"\";\n  return `apps/${accountId}/${gatewayId}${suffix}/bos.config.json`;\n}\n\nexport function parseBosUrl(bosUrl: string): {\n  accountId: string;\n  gatewayId: string;\n  pathSegments: string[];\n} {\n  const strippedUrl = bosUrl.split(\"#\")[0]!;\n  const match = strippedUrl.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n  if (!match?.[1] || !match[2]) {\n    throw new Error(`Invalid BOS URL: ${bosUrl}`);\n  }\n\n  const pathSegments = match[2]\n    .split(\"/\")\n    .filter(Boolean)\n    .map((segment) => decodeURIComponent(segment));\n  if (pathSegments.length === 0) {\n    throw new Error(`Invalid BOS URL: ${bosUrl}`);\n  }\n\n  const [gatewayId, ...pathSegmentsTail] = pathSegments;\n  if (!gatewayId) {\n    throw new Error(`Invalid BOS URL: ${bosUrl}`);\n  }\n\n  return {\n    accountId: match[1],\n    gatewayId,\n    pathSegments: pathSegmentsTail,\n  };\n}\n\nexport async function fetchBosConfigFromFastKv<T>(bosUrl: string, registry?: string): Promise<T> {\n  const { accountId, gatewayId, pathSegments } = parseBosUrl(bosUrl);\n  const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId, pathSegments));\n  const payload = await fetchJson<FastKvListResponse>(\n    `${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId, registry))}/${encodeURIComponent(accountId)}/${key}`,\n  );\n  const value = payload?.entries?.find(Boolean)?.value;\n\n  if (!value) {\n    throw new Error(`No config found for ${bosUrl}`);\n  }\n\n  if (typeof value === \"string\") {\n    return JSON.parse(value) as T;\n  }\n\n  if (typeof value !== \"object\") {\n    throw new Error(`Invalid config value for ${bosUrl}`);\n  }\n\n  return value as T;\n}\n\nexport interface ConfigHistoryEntry {\n  blockHeight: number;\n  blockTimestampNs: string;\n  txHash?: string;\n  value: unknown;\n}\n\ninterface FastKvHistoryEntry {\n  block_height?: number;\n  block_timestamp?: string | number;\n  tx_hash?: string;\n  value?: unknown;\n}\n\ninterface FastKvHistoryResponse {\n  entries?: Array<FastKvHistoryEntry | null>;\n}\n\nconst FASTKV_HISTORY_MAX_LIMIT = 200;\n\n/**\n * Publish history for the registry config key (atomic-deploys 11): the\n * exact-key variant of FastData's history endpoint, newest-first. The API\n * caps a page at 200 rows; a config key's write history fits well inside\n * that, so a single request is the whole story (the limit param is clamped).\n */\nexport async function fetchConfigHistory(opts: {\n  accountId: string;\n  gatewayId: string;\n  registry?: string;\n  limit?: number;\n}): Promise<ConfigHistoryEntry[]> {\n  const key = encodeURIComponent(getRegistryConfigKey(opts.accountId, opts.gatewayId));\n  const limit = Math.max(1, Math.min(opts.limit ?? 20, FASTKV_HISTORY_MAX_LIMIT));\n  const url = `${getFastKvBaseUrlForAccount(opts.accountId)}/v0/history/${encodeURIComponent(getRegistryNamespaceForAccount(opts.accountId, opts.registry))}/${encodeURIComponent(opts.accountId)}/${key}?limit=${limit}`;\n  const payload = await fetchJson<FastKvHistoryResponse>(url);\n  const entries = (payload?.entries ?? []).filter(Boolean) as FastKvHistoryEntry[];\n\n  return entries\n    .map((entry) => ({\n      blockHeight: entry.block_height ?? 0,\n      blockTimestampNs: String(entry.block_timestamp ?? \"\"),\n      ...(entry.tx_hash ? { txHash: entry.tx_hash } : {}),\n      value:\n        typeof entry.value === \"string\" && entry.value.length > 0\n          ? (JSON.parse(entry.value) as unknown)\n          : entry.value,\n    }))\n    .sort((a, b) => b.blockHeight - a.blockHeight);\n}\n\nexport interface DeployManifestEntry {\n  key: string;\n  blockHeight: number;\n  blockTimestampNs: string;\n  value: unknown;\n}\n\n/**\n * The per-deploy audit trail (atomic-deploys 03/12): every publish writes\n * `apps/<account>/<gateway>/manifests/<ts>.json` atomically with the pointer\n * swap — this lists that key family newest-first via the history-by-prefix\n * endpoint.\n */\nexport async function fetchDeployManifests(opts: {\n  accountId: string;\n  gatewayId: string;\n  registry?: string;\n  limit?: number;\n}): Promise<DeployManifestEntry[]> {\n  const keyPrefix = encodeURIComponent(\n    `${getRegistryConfigKey(opts.accountId, opts.gatewayId).replace(/bos\\.config\\.json$/, \"\")}manifests/`,\n  );\n  const url = `${getFastKvBaseUrlForAccount(opts.accountId)}/v0/history/${encodeURIComponent(getRegistryNamespaceForAccount(opts.accountId, opts.registry))}/${encodeURIComponent(opts.accountId)}`;\n  const payload = await fetchJson<FastKvHistoryResponse>(url, {\n    method: \"POST\",\n    body: JSON.stringify({\n      key_prefix: decodeURIComponent(keyPrefix),\n      asc: false,\n      limit: Math.max(1, Math.min(opts.limit ?? 20, FASTKV_HISTORY_MAX_LIMIT)),\n    }),\n  });\n  const entries = (payload?.entries ?? []).filter(Boolean) as Array<\n    FastKvHistoryEntry & { key?: string }\n  >;\n\n  return entries\n    .map((entry) => ({\n      key: entry.key ?? \"\",\n      blockHeight: entry.block_height ?? 0,\n      blockTimestampNs: String(entry.block_timestamp ?? \"\"),\n      value:\n        typeof entry.value === \"string\" && entry.value.length > 0\n          ? (JSON.parse(entry.value) as unknown)\n          : entry.value,\n    }))\n    .sort((a, b) => b.blockHeight - a.blockHeight);\n}\n\nexport interface PluginManifest {\n  schemaVersion: number;\n  kind: string;\n  plugin: { name: string; version: string };\n  runtime: { remoteEntry: string };\n  contract: {\n    kind: string;\n    types: { path: string; exportName: string; typeName: string; sha256: string };\n  };\n  additionalExports?: Array<{ path: string; exports: string[]; sha256: string }>;\n}\n\nexport async function fetchRemotePluginManifest(cdnUrl: string): Promise<PluginManifest | null> {\n  const baseUrl = cdnUrl.replace(/\\/$/, \"\");\n  return fetchJsonOrNull<PluginManifest>(`${baseUrl}/plugin.manifest.json`, { retries: 2 });\n}\n\nasync function fetchJson<T>(url: string, init?: RequestInit): Promise<T | null> {\n  const headers: Record<string, string> = {\n    accept: \"application/json\",\n    \"content-type\": \"application/json\",\n    ...(init?.headers as Record<string, string> | undefined),\n  };\n  return fetchJsonOrNull<T>(url, {\n    method: init?.method,\n    headers,\n    body: init?.body ?? undefined,\n    retries: 3,\n  });\n}\n"],"mappings":"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"}