everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 14.2 kB
Source Map (JSON)
{"version":3,"file":"publish.mjs","names":[],"sources":["../src/publish.ts"],"sourcesContent":["import { readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport process from \"node:process\";\nimport { Effect } from \"effect\";\nimport { buildWorkspaceTargets, selectWorkspaceTargets } from \"./build\";\nimport { generateCodeArtifacts } from \"./code-artifacts\";\nimport { loadResolvedConfig } from \"./config\";\nimport type { WorkspaceDeployResult } from \"./contract\";\nimport {\n buildRegistryConfigUrlForNetwork,\n fetchBosConfigFromFastKv,\n getRegistryNamespaceForNetwork,\n} from \"./fastkv\";\nimport { ensureNearCli, executeTransaction, resolveNearSigningMode } from \"./near-cli\";\nimport { getNetworkIdForAccount } from \"./network\";\nimport type { BosConfig, BosConfigInput, RuntimeConfig } from \"./types\";\nimport { padRight } from \"./utils/string\";\nimport { colors, icons } from \"./utils/theme\";\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nexport function extractPublishedUrl(output: string): string | null {\n const deployMatch = output.match(/🚀.*Deployed:\\s*(https?:\\S+)/);\n if (deployMatch) return deployMatch[1];\n const match = output.match(/https?:\\/\\/[^\\s\"'<>]+/g);\n if (!match || match.length === 0) return null;\n return match[match.length - 1] ?? null;\n}\n\nexport async function waitForPublishedConfig(opts: {\n account: string;\n gateway: string;\n publishConfig: BosConfigInput;\n timeoutMs?: number;\n intervalMs?: number;\n}): Promise<void> {\n const envTimeoutMs = Number(process.env.BOS_PUBLISH_CONFIRMATION_TIMEOUT_MS);\n const envIntervalMs = Number(process.env.BOS_PUBLISH_CONFIRMATION_INTERVAL_MS);\n const timeoutMs =\n opts.timeoutMs ?? (Number.isFinite(envTimeoutMs) ? envTimeoutMs : undefined) ?? 120_000;\n const intervalMs =\n opts.intervalMs ?? (Number.isFinite(envIntervalMs) ? envIntervalMs : undefined) ?? 3_000;\n const startedAt = Date.now();\n let lastError: unknown;\n\n while (Date.now() - startedAt < timeoutMs) {\n try {\n const verifiedConfig = await fetchBosConfigFromFastKv<BosConfigInput>(\n `bos://${opts.account}/${opts.gateway}`,\n );\n\n if (JSON.stringify(verifiedConfig) === JSON.stringify(opts.publishConfig)) {\n return;\n }\n } catch (error) {\n lastError = error;\n }\n\n await sleep(intervalMs);\n }\n\n const reason = lastError instanceof Error ? ` Last error: ${lastError.message}` : \"\";\n throw new Error(\n `Timed out waiting for publish confirmation at bos://${opts.account}/${opts.gateway}.${reason}`,\n );\n}\n\ninterface PublishToFastKvInput {\n bosConfig: BosConfig;\n runtimeConfig: RuntimeConfig | null;\n configDir: string;\n env: \"production\" | \"staging\";\n build: boolean;\n dryRun: boolean;\n verbose: boolean;\n packages: string;\n network?: \"mainnet\" | \"testnet\";\n privateKey?: string;\n}\n\ninterface PublishToFastKvResult {\n status: \"published\" | \"error\" | \"dry-run\";\n registryUrl: string;\n txHash?: string;\n built?: string[];\n skipped?: string[];\n error?: string;\n publishConfig?: BosConfigInput;\n deployResults?: WorkspaceDeployResult[];\n}\n\nexport async function publishToFastKv(input: PublishToFastKvInput): Promise<PublishToFastKvResult> {\n const { env, dryRun, configDir } = input;\n let bosConfig = input.bosConfig;\n const runtimeConfig = input.runtimeConfig;\n\n const isStaging = env === \"staging\";\n const account = bosConfig.account;\n const gateway = isStaging ? (bosConfig.staging?.domain ?? bosConfig.domain) : bosConfig.domain;\n if (!gateway) {\n return {\n status: \"error\",\n registryUrl: \"\",\n error: \"bos.config.json must define domain to publish\",\n };\n }\n\n const network = input.network ?? getNetworkIdForAccount(account);\n const registryUrl = buildRegistryConfigUrlForNetwork(network, account, gateway);\n const targets = selectWorkspaceTargets(input.packages, bosConfig);\n\n let built: string[] | undefined;\n let skipped: string[] | undefined;\n let deployResults: WorkspaceDeployResult[] | undefined;\n\n if (dryRun) {\n return { status: \"dry-run\", registryUrl, built, skipped };\n }\n\n const privateKey =\n input.privateKey || process.env.NEAR_PRIVATE_KEY || process.env.BOS_NEAR_PRIVATE_KEY;\n let signingMode: ReturnType<typeof resolveNearSigningMode>;\n try {\n signingMode = resolveNearSigningMode(privateKey);\n } catch (error) {\n return {\n status: \"error\" as const,\n registryUrl,\n error: error instanceof Error ? error.message : \"Unknown error\",\n };\n }\n\n if (input.build) {\n console.log(\" Ensuring NEAR CLI...\");\n await Effect.runPromise(ensureNearCli);\n console.log(\" NEAR CLI ready\");\n\n await generateCodeArtifacts(configDir, bosConfig, {\n env: \"production\",\n runtimeConfig: runtimeConfig ?? undefined,\n });\n\n const result = await buildWorkspaceTargets({\n configDir,\n bosConfig,\n runtimeConfig,\n targets,\n deploy: true,\n verbose: input.verbose,\n });\n built = result.built;\n skipped = result.skipped;\n deployResults = result.deployResults;\n\n if (deployResults) {\n const failures = deployResults.filter((r) => !r.success);\n if (failures.length > 0) {\n const total = deployResults.length;\n console.log();\n console.log(\n colors.error(\n ` ${icons.err} Deploy failed — ${failures.length} of ${total} workspace${total > 1 ? \"s\" : \"\"} failed`,\n ),\n );\n console.log();\n for (const f of failures) {\n const errorLine = (f.error ?? \"Failed\").split(\"\\n\")[0];\n console.log(` ${colors.error(icons.err)} ${padRight(f.key, 28)} ${errorLine}`);\n }\n console.log();\n if (!input.verbose) {\n console.log(colors.dim(\" Run with --verbose for full build output.\"));\n console.log();\n }\n return {\n status: \"error\" as const,\n registryUrl,\n built,\n skipped,\n deployResults,\n error: `${failures.length} of ${total} workspaces failed to deploy`,\n };\n }\n }\n\n const refreshed = await loadResolvedConfig({ cwd: configDir });\n if (!refreshed?.config) {\n return {\n status: \"error\",\n registryUrl,\n built,\n skipped,\n deployResults,\n error: \"Failed to reload bos.config.json after build\",\n };\n }\n\n bosConfig = refreshed.config;\n }\n\n const rawConfigPath = join(configDir, \"bos.config.json\");\n const rawConfig = JSON.parse(readFileSync(rawConfigPath, \"utf-8\")) as BosConfigInput;\n const publishPayload: BosConfigInput = isStaging ? { ...rawConfig, domain: gateway } : rawConfig;\n\n const registryEntries: Record<string, string> = {\n [`apps/${account}/${gateway}/bos.config.json`]: JSON.stringify(publishPayload),\n };\n\n const payload = JSON.stringify(registryEntries);\n const argsBase64 = Buffer.from(payload).toString(\"base64\");\n\n console.log();\n console.log(\" Publishing to:\");\n console.log(` ${colors.cyan(registryUrl)}`);\n\n try {\n let txHash: string | undefined;\n\n console.log(` Submitting transaction on ${network}...`);\n\n try {\n const tx = await Effect.runPromise(\n executeTransaction(\n {\n account,\n contract: getRegistryNamespaceForNetwork(network),\n method: \"__fastdata_kv\",\n argsBase64,\n network,\n privateKey: signingMode._tag === \"privateKey\" ? signingMode.privateKey : undefined,\n gas: \"300Tgas\",\n deposit: \"0NEAR\",\n verbose: input.verbose,\n },\n signingMode,\n ),\n );\n txHash = tx.txHash;\n if (txHash && !tx.output?.includes(\"CodeDoesNotExist\")) {\n console.log(` Transaction submitted: ${colors.dim(txHash)}`);\n }\n } catch (error) {\n console.log(colors.dim(\" Transaction reported an error — verifying publish...\"));\n try {\n await waitForPublishedConfig({\n account,\n gateway,\n publishConfig: publishPayload,\n timeoutMs: 30_000,\n intervalMs: 2_000,\n });\n txHash = extractTransactionHash(error);\n } catch {\n throw error;\n }\n }\n\n console.log(\" Waiting for publish confirmation...\");\n await waitForPublishedConfig({\n account,\n gateway,\n publishConfig: publishPayload,\n });\n\n return {\n status: \"published\",\n registryUrl,\n txHash,\n built,\n skipped,\n deployResults,\n publishConfig: publishPayload,\n };\n } catch (error) {\n return {\n status: \"error\",\n registryUrl,\n error: formatNearError(error),\n built,\n skipped,\n deployResults,\n };\n }\n}\n\nfunction formatNearError(error: unknown): string {\n const message = error instanceof Error ? error.message : String(error);\n\n if (message.includes(\"does not have enough allowance\")) {\n return (\n \"The publish access key has insufficient allowance to cover this transaction.\\n\" +\n \" Regenerate your key with a higher allowance:\\n\" +\n \" bos key generate\\n\" +\n ` Original: ${message}`\n );\n }\n\n if (message.includes(\"exceeded gas\") || message.includes(\"GasLimitExceeded\")) {\n return `Transaction exceeded gas limit.\\n Original: ${message}`;\n }\n\n if (message.includes(\"timeout\") || message.includes(\"Timeout\")) {\n return `Transaction timed out. Check NEAR network status.\\n Original: ${message}`;\n }\n\n return message;\n}\n\nfunction extractTransactionHash(error: unknown) {\n const message = error instanceof Error ? error.message : String(error);\n const match = message.match(/Transaction ID:\\s*([A-Za-z0-9]+)/i);\n return match?.[1];\n}\n"],"mappings":"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"}