UNPKG

everything-dev

Version:

A consolidated product package for building Module Federation apps with oRPC APIs.

120 lines (118 loc) • 6.4 kB
import { fetchJsonOrNull } from "./http-client.mjs"; //#region src/fastkv.ts function getNetworkIdForAccount(accountId) { return accountId.endsWith(".testnet") ? "testnet" : "mainnet"; } const FASTKV_TESTNET_URL = "https://kv.test.fastnear.com"; const FASTKV_MAINNET_URL = "https://kv.main.fastnear.com"; const REGISTRY_NAMESPACE_TESTNET = "dev.allthethings.testnet"; const REGISTRY_NAMESPACE_MAINNET = "dev.everything.near"; function getFastKvBaseUrlForNetwork(network) { return network === "testnet" ? FASTKV_TESTNET_URL : FASTKV_MAINNET_URL; } function getFastKvBaseUrlForAccount(accountId) { return getNetworkIdForAccount(accountId) === "testnet" ? getFastKvBaseUrlForNetwork("testnet") : getFastKvBaseUrlForNetwork("mainnet"); } function buildRegistryConfigUrl(accountId, gatewayId, registry) { const baseUrl = getFastKvBaseUrlForAccount(accountId); const namespace = getRegistryNamespaceForAccount(accountId, registry); const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId)); return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`; } function buildRegistryConfigUrlForNetwork(network, accountId, gatewayId, registry) { const baseUrl = getFastKvBaseUrlForNetwork(network); const namespace = getRegistryNamespaceForNetwork(network, registry); const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId)); return `${baseUrl}/v0/latest/${encodeURIComponent(namespace)}/${encodeURIComponent(accountId)}/${key}`; } function getRegistryNamespaceForAccount(accountId, registry) { return registry ?? getRegistryNamespaceForNetwork(getNetworkIdForAccount(accountId)); } function getRegistryNamespaceForNetwork(network, registry) { if (registry) return registry; return network === "testnet" ? REGISTRY_NAMESPACE_TESTNET : REGISTRY_NAMESPACE_MAINNET; } function getRegistryConfigKey(accountId, gatewayId, pathSegments = []) { return `apps/${accountId}/${gatewayId}${pathSegments.length > 0 ? `/${pathSegments.map((segment) => encodeURIComponent(segment)).join("/")}` : ""}/bos.config.json`; } function parseBosUrl(bosUrl) { const match = bosUrl.split("#")[0].match(/^bos:\/\/([^/]+)\/(.+)$/); if (!match?.[1] || !match[2]) throw new Error(`Invalid BOS URL: ${bosUrl}`); const pathSegments = match[2].split("/").filter(Boolean).map((segment) => decodeURIComponent(segment)); if (pathSegments.length === 0) throw new Error(`Invalid BOS URL: ${bosUrl}`); const [gatewayId, ...pathSegmentsTail] = pathSegments; if (!gatewayId) throw new Error(`Invalid BOS URL: ${bosUrl}`); return { accountId: match[1], gatewayId, pathSegments: pathSegmentsTail }; } async function fetchBosConfigFromFastKv(bosUrl, registry) { const { accountId, gatewayId, pathSegments } = parseBosUrl(bosUrl); const key = encodeURIComponent(getRegistryConfigKey(accountId, gatewayId, pathSegments)); const value = (await fetchJson(`${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId, registry))}/${encodeURIComponent(accountId)}/${key}`))?.entries?.find(Boolean)?.value; if (!value) throw new Error(`No config found for ${bosUrl}`); if (typeof value === "string") return JSON.parse(value); if (typeof value !== "object") throw new Error(`Invalid config value for ${bosUrl}`); return value; } const FASTKV_HISTORY_MAX_LIMIT = 200; /** * Publish history for the registry config key (atomic-deploys 11): the * exact-key variant of FastData's history endpoint, newest-first. The API * caps a page at 200 rows; a config key's write history fits well inside * that, so a single request is the whole story (the limit param is clamped). */ async function fetchConfigHistory(opts) { const key = encodeURIComponent(getRegistryConfigKey(opts.accountId, opts.gatewayId)); const limit = Math.max(1, Math.min(opts.limit ?? 20, FASTKV_HISTORY_MAX_LIMIT)); return ((await fetchJson(`${getFastKvBaseUrlForAccount(opts.accountId)}/v0/history/${encodeURIComponent(getRegistryNamespaceForAccount(opts.accountId, opts.registry))}/${encodeURIComponent(opts.accountId)}/${key}?limit=${limit}`))?.entries ?? []).filter(Boolean).map((entry) => ({ blockHeight: entry.block_height ?? 0, blockTimestampNs: String(entry.block_timestamp ?? ""), ...entry.tx_hash ? { txHash: entry.tx_hash } : {}, value: typeof entry.value === "string" && entry.value.length > 0 ? JSON.parse(entry.value) : entry.value })).sort((a, b) => b.blockHeight - a.blockHeight); } /** * The per-deploy audit trail (atomic-deploys 03/12): every publish writes * `apps/<account>/<gateway>/manifests/<ts>.json` atomically with the pointer * swap — this lists that key family newest-first via the history-by-prefix * endpoint. */ async function fetchDeployManifests(opts) { const keyPrefix = encodeURIComponent(`${getRegistryConfigKey(opts.accountId, opts.gatewayId).replace(/bos\.config\.json$/, "")}manifests/`); return ((await fetchJson(`${getFastKvBaseUrlForAccount(opts.accountId)}/v0/history/${encodeURIComponent(getRegistryNamespaceForAccount(opts.accountId, opts.registry))}/${encodeURIComponent(opts.accountId)}`, { method: "POST", body: JSON.stringify({ key_prefix: decodeURIComponent(keyPrefix), asc: false, limit: Math.max(1, Math.min(opts.limit ?? 20, FASTKV_HISTORY_MAX_LIMIT)) }) }))?.entries ?? []).filter(Boolean).map((entry) => ({ key: entry.key ?? "", blockHeight: entry.block_height ?? 0, blockTimestampNs: String(entry.block_timestamp ?? ""), value: typeof entry.value === "string" && entry.value.length > 0 ? JSON.parse(entry.value) : entry.value })).sort((a, b) => b.blockHeight - a.blockHeight); } async function fetchRemotePluginManifest(cdnUrl) { const baseUrl = cdnUrl.replace(/\/$/, ""); return fetchJsonOrNull(`${baseUrl}/plugin.manifest.json`, { retries: 2 }); } async function fetchJson(url, init) { const headers = { accept: "application/json", "content-type": "application/json", ...init?.headers }; return fetchJsonOrNull(url, { method: init?.method, headers, body: init?.body ?? void 0, retries: 3 }); } //#endregion export { buildRegistryConfigUrl, buildRegistryConfigUrlForNetwork, fetchBosConfigFromFastKv, fetchConfigHistory, fetchDeployManifests, fetchRemotePluginManifest, getFastKvBaseUrlForNetwork, getRegistryNamespaceForAccount, getRegistryNamespaceForNetwork, parseBosUrl }; //# sourceMappingURL=fastkv.mjs.map