everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
1 lines • 10.8 kB
Source Map (JSON)
{"version":3,"file":"cdn.mjs","names":[],"sources":["../../../src/services/deploy/cdn.ts"],"sourcesContent":["interface SessionCredentialLike {\n apiKey?: string;\n siteUrl?: string;\n accountId?: string | null;\n}\n\nexport interface CdnDeployInputs {\n cdnOrigin: string | undefined;\n storageOrigin: string;\n apiKey: string | undefined;\n warning?: string;\n error?: string;\n}\n\nconst LOCAL_HOSTNAMES = new Set([\"localhost\", \"127.0.0.1\", \"::1\", \"[::1]\", \"0.0.0.0\", \"local\"]);\n\n/** Local/private destinations can never serve production bundles. */\nexport function isLocalOrigin(url: string): boolean {\n try {\n const { hostname, protocol } = new URL(url);\n if (protocol !== \"http:\" && protocol !== \"https:\") return true;\n const host = hostname.toLowerCase().replace(/^\\[|\\]$/g, \"\");\n return (\n LOCAL_HOSTNAMES.has(host) ||\n host.endsWith(\".localhost\") ||\n host.endsWith(\".local\") ||\n /^127\\.\\d+\\.\\d+\\.\\d+$/.test(host) ||\n /^10\\.\\d+\\.\\d+\\.\\d+$/.test(host) ||\n /^192\\.168\\.\\d+\\.\\d+$/.test(host) ||\n /^172\\.(1[6-9]|2\\d|3[01])\\.\\d+\\.\\d+$/.test(host) ||\n host.startsWith(\"169.254.\") ||\n (host.includes(\":\") &&\n (host.startsWith(\"fe80:\") || host.startsWith(\"fc\") || host.startsWith(\"fd\")))\n );\n } catch {\n return true;\n }\n}\n\n/**\n * Deploy origin resolution (ADR 0020, as amended): the CDN origin comes from\n * the env or the authored `cdn.origin` (inherited via extends) — a hard\n * requirement when uploads are planned, because the host never serves\n * `/bundles` itself. The upload origin is the env, the `bos login` session's\n * siteUrl, or the runtime's own gateway. A session pinned to a local site is\n * a hard error (the origin can only ever be a squatter's port); an env-pinned\n * local origin is deliberate local testing and only warns. Config-only\n * publishes (`uploadsPlanned: false`) ship the committed config verbatim —\n * no origins are resolved and nothing is required.\n */\nexport function resolveCdnDeployInputs(input: {\n env: Record<string, string | undefined>;\n bosConfig: { cdn?: { origin?: string } } | null;\n session: SessionCredentialLike | null;\n custodyAccount?: string;\n account: string;\n gateway: string;\n uploadsPlanned: boolean;\n}): CdnDeployInputs {\n const envCdnOrigin = input.env.BOS_BUNDLE_CDN_ORIGIN?.replace(/\\/$/, \"\");\n const configCdnOrigin = input.bosConfig?.cdn?.origin?.replace(/\\/$/, \"\");\n const storageEnvOrigin = input.env.BOS_STORAGE_ORIGIN?.replace(/\\/$/, \"\");\n const sessionSite = input.session?.siteUrl?.replace(/\\/$/, \"\");\n const apiKey = input.env.BOS_STORAGE_API_KEY ?? input.session?.apiKey;\n const uploadsPlanned = input.uploadsPlanned;\n\n const storageOriginDefault = storageEnvOrigin ?? sessionSite ?? `https://${input.gateway}`;\n\n if (!uploadsPlanned) {\n return { cdnOrigin: undefined, storageOrigin: storageOriginDefault, apiKey };\n }\n\n let cdnOrigin: string | undefined;\n let warning: string | undefined;\n if (envCdnOrigin) {\n cdnOrigin = envCdnOrigin;\n if (isLocalOrigin(envCdnOrigin)) {\n warning = `BOS_BUNDLE_CDN_ORIGIN is a local URL (${envCdnOrigin}) — deploying to a local stack, published bundle URLs will not resolve publicly.`;\n }\n } else if (configCdnOrigin) {\n if (isLocalOrigin(configCdnOrigin)) {\n return {\n cdnOrigin,\n storageOrigin: storageOriginDefault,\n apiKey,\n error:\n `the authored config sets cdn.origin to the local URL ${configCdnOrigin} — deploy bundle ` +\n \"origins must be publicly reachable. Set BOS_BUNDLE_CDN_ORIGIN for a local deploy, \" +\n \"or fix cdn.origin (inherited via extends).\",\n };\n }\n cdnOrigin = configCdnOrigin;\n } else {\n return {\n cdnOrigin,\n storageOrigin: storageOriginDefault,\n apiKey,\n error:\n \"CDN deploy requires a bundle origin — set `cdn.origin` in bos.config.json \" +\n \"(children inherit it from the base runtime via extends) or BOS_BUNDLE_CDN_ORIGIN \" +\n \"in the environment.\",\n };\n }\n\n let storageOrigin: string;\n if (storageEnvOrigin) {\n storageOrigin = storageEnvOrigin;\n if (isLocalOrigin(storageEnvOrigin) && !warning) {\n warning = `BOS_STORAGE_ORIGIN is a local URL (${storageEnvOrigin}) — uploading to a local stack.`;\n }\n } else if (sessionSite) {\n if (isLocalOrigin(sessionSite)) {\n return {\n cdnOrigin,\n storageOrigin: sessionSite,\n apiKey,\n error:\n `The CLI session pins uploads to ${sessionSite} — a local site can never receive ` +\n \"production bundles. Re-run `bos login` against the production site, or set \" +\n \"BOS_STORAGE_ORIGIN explicitly.\",\n };\n }\n storageOrigin = sessionSite;\n } else {\n return {\n cdnOrigin,\n storageOrigin: storageOriginDefault,\n apiKey,\n warning,\n error:\n `Bundle uploads require an upload-service origin — the gateway (${input.gateway}) no longer serves ` +\n \"uploads (ADR 0027: the upload service is standalone at upload.everything.dev). Set BOS_STORAGE_ORIGIN \" +\n \"in the environment (CI) or re-run `bos login` against the production site so the session pins it.\",\n };\n }\n\n if (!apiKey) {\n return {\n cdnOrigin,\n storageOrigin,\n apiKey,\n warning,\n error:\n \"CDN deploy requires bundle-upload credentials — run `bos login --key` and add the printed key \" +\n \"to GitHub secrets as BOS_STORAGE_API_KEY (the deploy key also serves interactive deploys)\",\n };\n }\n const uploadIdentities = new Set(\n [input.session?.accountId, input.custodyAccount].filter((id): id is string => !!id),\n );\n if (uploadIdentities.size > 0 && !uploadIdentities.has(input.account)) {\n const sessionAccount = input.session?.accountId;\n return {\n cdnOrigin,\n storageOrigin,\n apiKey,\n warning,\n error: sessionAccount\n ? `The CLI session was created for ${sessionAccount}, but the configured account is ${input.account}. Re-run bos login under the matching account.`\n : `The configured account is ${input.account}, but the custody publish identity is ${input.custodyAccount}. Set the authored config's account to the custody wallet account.`,\n };\n }\n return { cdnOrigin, storageOrigin, apiKey, warning };\n}\n\n/**\n * Pre-upload probe: the storage origin must actually be the bundle-upload\n * service.\n * `/health` is the primary readiness check, with MCP kept as a compatibility\n * fallback for older fixtures and legacy hosts.\n */\nexport async function probeStorageOrigin(\n storageOrigin: string,\n fetchImpl: typeof fetch = fetch,\n): Promise<string | undefined> {\n const base = storageOrigin.replace(/\\/$/, \"\");\n const healthUrl = `${base}/health`;\n const mcpUrl = `${base}/.well-known/mcp.json`;\n let response: Response;\n try {\n response = await fetchImpl(healthUrl, { headers: { accept: \"text/plain\" } });\n } catch (error) {\n return `unreachable (${String(error instanceof Error ? error.message : error)})`;\n }\n if (response.ok) {\n const body = await response.text().catch(() => \"\");\n if (body.trim() === \"OK\" || body.trim().length === 0) return undefined;\n }\n let mcpResponse: Response;\n try {\n mcpResponse = await fetchImpl(mcpUrl, { headers: { accept: \"application/json\" } });\n } catch {\n return `GET /health responded ${response.status}; fallback /.well-known/mcp.json unreachable`;\n }\n if (!mcpResponse.ok) {\n return `GET /health responded ${response.status}; GET /.well-known/mcp.json responded ${mcpResponse.status}`;\n }\n try {\n const body = await mcpResponse.json();\n if (!body || typeof body !== \"object\") {\n return \"did not return the platform MCP descriptor\";\n }\n return undefined;\n } catch {\n return \"did not return the platform MCP descriptor (non-JSON response)\";\n }\n}\n"],"mappings":"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"}