blodemd 0.0.8 → 0.0.10
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.
- package/README.md +25 -9
- package/dev-server/app/[[...slug]]/page.tsx +1 -0
- package/dev-server/app/favicon.ico +0 -0
- package/dev-server/next.config.js +11 -13
- package/dev-server/package.json +1 -1
- package/dev-server/tsconfig.json +3 -0
- package/dist/cli.mjs +869 -184
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +1 -1
- package/docs/components/animate-ui/primitives/buttons/button.tsx +14 -0
- package/docs/components/api/api-playground.tsx +255 -80
- package/docs/components/api/api-reference.tsx +11 -1
- package/docs/components/docs/contextual-menu.tsx +227 -142
- package/docs/components/docs/copy-page-menu.tsx +148 -85
- package/docs/components/docs/doc-header.tsx +13 -3
- package/docs/components/docs/doc-shell.tsx +25 -14
- package/docs/components/docs/mobile-nav.tsx +0 -6
- package/docs/components/mdx/code-group.tsx +171 -62
- package/docs/components/mdx/steps.tsx +1 -1
- package/docs/components/mdx/tabs.tsx +131 -26
- package/docs/components/ui/copy-button.tsx +122 -0
- package/docs/components/ui/input.tsx +0 -1
- package/docs/components/ui/search.tsx +241 -132
- package/docs/components/ui/site-footer.tsx +39 -0
- package/docs/lib/config.ts +7 -0
- package/docs/lib/content-root.ts +33 -0
- package/docs/lib/content-source.ts +70 -0
- package/docs/lib/contextual-options.ts +20 -0
- package/docs/lib/docs-runtime.tsx +595 -0
- package/docs/lib/edge-config.ts +95 -0
- package/docs/lib/env.ts +22 -0
- package/docs/lib/openapi-proxy.ts +88 -0
- package/docs/lib/platform-config.ts +6 -0
- package/docs/lib/routes.ts +39 -0
- package/docs/lib/supabase.ts +13 -0
- package/docs/lib/tenancy.ts +350 -0
- package/docs/lib/tenant-headers.ts +14 -0
- package/docs/lib/tenant-static.ts +529 -0
- package/docs/lib/tenant-utility-context.ts +62 -0
- package/docs/lib/tenants.ts +68 -0
- package/docs/lib/use-mobile.ts +19 -0
- package/package.json +3 -2
- package/packages/@repo/common/dist/index.d.ts +7 -0
- package/packages/@repo/common/dist/index.d.ts.map +1 -1
- package/packages/@repo/common/dist/index.js +42 -0
- package/packages/@repo/common/src/index.ts +50 -0
- package/packages/@repo/contracts/dist/project.d.ts +1 -1
- package/packages/@repo/contracts/dist/project.js +1 -1
- package/packages/@repo/contracts/src/project.ts +1 -1
- package/packages/@repo/models/dist/docs-config.d.ts +194 -29
- package/packages/@repo/models/dist/docs-config.d.ts.map +1 -1
- package/packages/@repo/models/dist/docs-config.js +3 -28
- package/packages/@repo/models/src/docs-config.ts +5 -31
- package/packages/@repo/previewing/dist/blob-source.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/blob-source.js +7 -2
- package/packages/@repo/previewing/dist/fs-source.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/fs-source.js +2 -3
- package/packages/@repo/previewing/dist/index.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/index.js +20 -50
- package/packages/@repo/previewing/src/blob-source.ts +7 -4
- package/packages/@repo/previewing/src/fs-source.ts +2 -3
- package/packages/@repo/previewing/src/index.ts +29 -64
- package/packages/@repo/validation/dist/index.d.ts +2 -2
- package/packages/@repo/validation/dist/index.d.ts.map +1 -1
- package/packages/@repo/validation/dist/index.js +2 -2
- package/packages/@repo/validation/package.json +1 -0
- package/packages/@repo/validation/src/{mintlify-docs-schema.json → blodemd-docs-schema.json} +346 -1794
- package/packages/@repo/validation/src/index.ts +4 -4
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.mjs","names":["CONFIG_FILE","CONFIG_FILE","fileExists","normalizeRelativePath","parsePositiveInteger","delay","createServer"],"sources":["../src/constants.ts","../src/jwt.ts","../src/errors.ts","../src/oauth-token.ts","../src/storage.ts","../src/supabase.ts","../src/auth-session.ts","../src/site-config.ts","../src/dev/resolve-root.ts","../src/dev/watcher.ts","../src/dev/command.ts","../src/oauth-callback.ts","../src/pkce.ts","../src/runtime.ts","../src/cli.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const CLI_NAME = \"blodemd\";\n\nexport const BLODE_TOKEN_ENV = \"BLODEMD_API_KEY\";\nexport const BLODE_API_URL_ENV = \"BLODEMD_API_URL\";\nexport const BLODE_PROJECT_ENV = \"BLODEMD_PROJECT\";\nexport const BLODE_BRANCH_ENV = \"BLODEMD_BRANCH\";\nexport const BLODE_COMMIT_MESSAGE_ENV = \"BLODEMD_COMMIT_MESSAGE\";\n\nexport const DEFAULT_API_URL = \"https://api.blode.md\";\nexport const DEFAULT_SUPABASE_URL = \"https://bwnxwgkgyklzzmpbzuoz.supabase.co\";\n\nexport const OAUTH_CLIENT_ID = \"6b5f9860-fe96-4a83-b1ad-266260523c91\";\n\nexport const DEFAULT_OAUTH_CALLBACK_PORT = 8787;\nexport const DEFAULT_OAUTH_CALLBACK_PATH = \"/auth/callback\";\nexport const DEFAULT_OAUTH_TIMEOUT_SECONDS = 180;\n\nconst getDefaultConfigBaseDir = (): string => {\n if (process.platform === \"win32\") {\n return process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n }\n\n return process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\");\n};\n\nconst configBaseDir = getDefaultConfigBaseDir();\n\nexport const CONFIG_DIR = join(configBaseDir, CLI_NAME);\nexport const CREDENTIALS_FILE = join(CONFIG_DIR, \"credentials.json\");\n","export interface JwtClaims {\n exp?: number;\n email?: string;\n sub?: string;\n}\n\nconst parseJwtBase64Url = (input: string): string => {\n const normalized = input.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, \"=\");\n return Buffer.from(padded, \"base64\").toString(\"utf8\");\n};\n\nexport const parseJwtClaims = (token: string): JwtClaims | null => {\n const parts = token.split(\".\");\n const payloadPart = parts.at(1);\n\n if (!payloadPart) {\n return null;\n }\n\n try {\n const payload = parseJwtBase64Url(payloadPart);\n const parsed = JSON.parse(payload) as unknown;\n\n if (typeof parsed !== \"object\" || parsed === null) {\n return null;\n }\n\n const claims = parsed as Record<string, unknown>;\n\n return {\n email: typeof claims.email === \"string\" ? claims.email : undefined,\n exp: typeof claims.exp === \"number\" ? claims.exp : undefined,\n sub: typeof claims.sub === \"string\" ? claims.sub : undefined,\n };\n } catch {\n return null;\n }\n};\n","export const EXIT_CODES = {\n AUTH_REQUIRED: 4,\n CANCELLED: 2,\n ERROR: 1,\n NETWORK: 5,\n SUCCESS: 0,\n VALIDATION: 3,\n} as const;\n\ntype ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES];\n\nexport class CliError extends Error {\n readonly exitCode: ExitCode;\n readonly hint: string | null;\n\n constructor(\n message: string,\n exitCode: ExitCode = EXIT_CODES.ERROR,\n hint?: string\n ) {\n super(message);\n this.name = \"CliError\";\n this.exitCode = exitCode;\n this.hint = hint ?? null;\n }\n}\n\nexport const toCliError = (error: unknown): CliError => {\n if (error instanceof CliError) {\n return error;\n }\n\n if (error instanceof Error) {\n if (error instanceof TypeError && error.message.includes(\"fetch\")) {\n return new CliError(\n \"Cannot connect to Blode.md API.\",\n EXIT_CODES.NETWORK,\n \"Check your internet connection and API URL configuration.\"\n );\n }\n\n if (error.name === \"TimeoutError\" || error.name === \"AbortError\") {\n return new CliError(\n \"Request timed out.\",\n EXIT_CODES.NETWORK,\n \"The API may be unavailable. Try again later.\"\n );\n }\n\n return new CliError(error.message, EXIT_CODES.ERROR);\n }\n\n return new CliError(\"Unknown error\", EXIT_CODES.ERROR);\n};\n","import { CliError, EXIT_CODES } from \"./errors.js\";\n\nexport interface OAuthTokenConfig {\n tokenUrl: string;\n clientId: string;\n}\n\nexport interface OAuthTokenResponse {\n access_token: string;\n refresh_token?: string;\n token_type: string;\n expires_in: number;\n}\n\nconst postTokenRequest = async (\n url: string,\n body: URLSearchParams\n): Promise<OAuthTokenResponse> => {\n const response = await fetch(url, {\n body: body.toString(),\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new CliError(\n `OAuth token request failed (${response.status}): ${text}`,\n EXIT_CODES.AUTH_REQUIRED\n );\n }\n\n return (await response.json()) as OAuthTokenResponse;\n};\n\nexport const exchangeAuthorizationCode = (\n config: OAuthTokenConfig,\n code: string,\n codeVerifier: string,\n redirectUri: string\n): Promise<OAuthTokenResponse> => {\n const body = new URLSearchParams({\n client_id: config.clientId,\n code,\n code_verifier: codeVerifier,\n grant_type: \"authorization_code\",\n redirect_uri: redirectUri,\n });\n\n return postTokenRequest(config.tokenUrl, body);\n};\n\nexport const refreshAccessToken = (\n config: OAuthTokenConfig,\n refreshToken: string\n): Promise<OAuthTokenResponse> => {\n const body = new URLSearchParams({\n client_id: config.clientId,\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n });\n\n return postTokenRequest(config.tokenUrl, body);\n};\n","import { mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\n\nimport { CONFIG_DIR, CREDENTIALS_FILE } from \"./constants.js\";\nimport { CliError, EXIT_CODES } from \"./errors.js\";\nimport type {\n ApiKeyCredentials,\n AuthFileData,\n StoredAuthSession,\n} from \"./types.js\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst parseStoredAuthSession = (value: unknown): StoredAuthSession | null => {\n if (!isRecord(value)) {\n return null;\n }\n\n if (typeof value.accessToken !== \"string\") {\n return null;\n }\n\n if (value.refreshToken !== null && typeof value.refreshToken !== \"string\") {\n return null;\n }\n\n if (value.expiresAt !== null && typeof value.expiresAt !== \"string\") {\n return null;\n }\n\n const { user } = value;\n if (\n user !== null &&\n (!isRecord(user) ||\n typeof user.id !== \"string\" ||\n (user.email !== null && typeof user.email !== \"string\"))\n ) {\n return null;\n }\n\n if (typeof value.createdAt !== \"string\") {\n return null;\n }\n\n const parsedUser =\n user === null || !isRecord(user)\n ? null\n : {\n email: (user.email as string | null) ?? null,\n id: user.id as string,\n };\n\n return {\n accessToken: value.accessToken,\n createdAt: value.createdAt,\n expiresAt: (value.expiresAt as string | null) ?? null,\n refreshToken: (value.refreshToken as string | null) ?? null,\n user: parsedUser,\n };\n};\n\nconst parseApiKeyCredentials = (value: unknown): ApiKeyCredentials | null => {\n if (!isRecord(value)) {\n return null;\n }\n\n if (typeof value.apiKey !== \"string\") {\n return null;\n }\n\n return { apiKey: value.apiKey, type: \"api-key\" };\n};\n\nexport const readAuthFile = async (): Promise<AuthFileData | null> => {\n try {\n const raw = await readFile(CREDENTIALS_FILE, \"utf8\");\n const parsed = JSON.parse(raw) as unknown;\n\n if (!isRecord(parsed) || parsed.version !== 1) {\n throw new CliError(\n `Invalid credentials format in ${CREDENTIALS_FILE}`,\n EXIT_CODES.ERROR\n );\n }\n\n return {\n apiKey: parseApiKeyCredentials(parsed.apiKey) ?? undefined,\n session: parseStoredAuthSession(parsed.session) ?? undefined,\n version: 1,\n };\n } catch (error) {\n if (isRecord(error) && error.code === \"ENOENT\") {\n return null;\n }\n\n if (error instanceof CliError) {\n throw error;\n }\n\n return null;\n }\n};\n\nexport const readStoredAuthSession =\n async (): Promise<StoredAuthSession | null> => {\n const data = await readAuthFile();\n return data?.session ?? null;\n };\n\nexport const readStoredApiKey = async (): Promise<ApiKeyCredentials | null> => {\n const data = await readAuthFile();\n return data?.apiKey ?? null;\n};\n\nconst writeAuthFile = async (data: AuthFileData): Promise<void> => {\n await mkdir(CONFIG_DIR, { mode: 0o700, recursive: true });\n await writeFile(CREDENTIALS_FILE, `${JSON.stringify(data, null, 2)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n};\n\nexport const writeStoredAuthSession = async (\n session: StoredAuthSession\n): Promise<void> => {\n await writeAuthFile({\n session,\n version: 1,\n });\n};\n\nexport const writeStoredApiKey = async (\n apiKey: ApiKeyCredentials\n): Promise<void> => {\n await writeAuthFile({\n apiKey,\n version: 1,\n });\n};\n\nexport const clearStoredCredentials = async (): Promise<void> => {\n await rm(CREDENTIALS_FILE, { force: true });\n};\n","import { DEFAULT_SUPABASE_URL } from \"./constants.js\";\nimport { parseJwtClaims } from \"./jwt.js\";\nimport type { OAuthTokenResponse } from \"./oauth-token.js\";\nimport type { StoredAuthSession, SupabaseConfig } from \"./types.js\";\n\nexport const resolveSupabaseConfig = (): SupabaseConfig => {\n const url =\n process.env.SUPABASE_URL ??\n process.env.NEXT_PUBLIC_SUPABASE_URL ??\n DEFAULT_SUPABASE_URL;\n\n return { url };\n};\n\nexport const buildOAuthUrls = (\n config: SupabaseConfig\n): {\n authorizeUrl: string;\n tokenUrl: string;\n} => ({\n authorizeUrl: `${config.url}/auth/v1/oauth/authorize`,\n tokenUrl: `${config.url}/auth/v1/oauth/token`,\n});\n\nexport const tokenResponseToStoredSession = (\n response: OAuthTokenResponse\n): StoredAuthSession => {\n const claims = parseJwtClaims(response.access_token);\n\n let expiresAt: string | null = null;\n if (typeof claims?.exp === \"number\") {\n expiresAt = new Date(claims.exp * 1000).toISOString();\n } else if (response.expires_in > 0) {\n expiresAt = new Date(Date.now() + response.expires_in * 1000).toISOString();\n }\n\n return {\n accessToken: response.access_token,\n createdAt: new Date().toISOString(),\n expiresAt,\n refreshToken: response.refresh_token ?? null,\n user:\n claims?.sub || claims?.email\n ? {\n email: claims.email ?? null,\n id: claims.sub ?? \"unknown\",\n }\n : null,\n };\n};\n","import { BLODE_TOKEN_ENV, OAUTH_CLIENT_ID } from \"./constants.js\";\nimport { parseJwtClaims } from \"./jwt.js\";\nimport { refreshAccessToken } from \"./oauth-token.js\";\nimport {\n clearStoredCredentials,\n readAuthFile,\n writeStoredAuthSession,\n} from \"./storage.js\";\nimport {\n buildOAuthUrls,\n resolveSupabaseConfig,\n tokenResponseToStoredSession,\n} from \"./supabase.js\";\nimport type { ResolvedAuthToken, StoredAuthSession } from \"./types.js\";\n\nconst expiresInMs = (session: StoredAuthSession): number | null => {\n if (!session.expiresAt) {\n return null;\n }\n\n const expiresAtMs = Date.parse(session.expiresAt);\n\n if (Number.isNaN(expiresAtMs)) {\n return null;\n }\n\n return expiresAtMs - Date.now();\n};\n\nconst isExpired = (session: StoredAuthSession): boolean => {\n const ms = expiresInMs(session);\n return ms !== null && ms <= 0;\n};\n\nconst shouldRefresh = (session: StoredAuthSession): boolean => {\n const ms = expiresInMs(session);\n return ms !== null && ms <= 60_000;\n};\n\nconst tokenFromRaw = (\n token: string,\n source: ResolvedAuthToken[\"source\"]\n): ResolvedAuthToken => {\n const claims = parseJwtClaims(token);\n\n const expiresAt =\n typeof claims?.exp === \"number\"\n ? new Date(claims.exp * 1000).toISOString()\n : null;\n\n return {\n expiresAt,\n source,\n token,\n user:\n claims?.sub || claims?.email\n ? { email: claims.email ?? null, id: claims.sub ?? \"unknown\" }\n : null,\n };\n};\n\nconst sessionToResolvedToken = (\n session: StoredAuthSession\n): ResolvedAuthToken => ({\n expiresAt: session.expiresAt,\n source: \"stored\",\n token: session.accessToken,\n user: session.user,\n});\n\nexport const resolveAuthToken = async (\n optApiKey?: string\n): Promise<ResolvedAuthToken | null> => {\n const envToken = (optApiKey ?? process.env[BLODE_TOKEN_ENV])?.trim();\n\n if (envToken) {\n return tokenFromRaw(envToken, optApiKey ? \"flag\" : \"environment\");\n }\n\n const data = await readAuthFile();\n const session = data?.session;\n\n if (session) {\n if (!(shouldRefresh(session) || isExpired(session))) {\n return sessionToResolvedToken(session);\n }\n\n if (session.refreshToken) {\n try {\n const config = resolveSupabaseConfig();\n const { tokenUrl } = buildOAuthUrls(config);\n const tokenResponse = await refreshAccessToken(\n { clientId: OAUTH_CLIENT_ID, tokenUrl },\n session.refreshToken\n );\n const updatedSession = tokenResponseToStoredSession(tokenResponse);\n await writeStoredAuthSession(updatedSession);\n\n return sessionToResolvedToken(updatedSession);\n } catch {\n // Refresh failed — fall through to expiry check\n }\n }\n\n if (isExpired(session)) {\n await clearStoredCredentials();\n return null;\n }\n\n return sessionToResolvedToken(session);\n }\n\n if (data?.apiKey) {\n return {\n expiresAt: null,\n source: \"stored\",\n token: data.apiKey.apiKey,\n user: null,\n };\n }\n\n return null;\n};\n\nexport const resolveTokenStatus = (\n token: ResolvedAuthToken\n): {\n expiresInSeconds: number | null;\n expired: boolean;\n} => {\n if (!token.expiresAt) {\n return { expired: false, expiresInSeconds: null };\n }\n\n const expiresAtMs = Date.parse(token.expiresAt);\n\n if (Number.isNaN(expiresAtMs)) {\n return { expired: false, expiresInSeconds: null };\n }\n\n const expiresInSeconds = Math.floor((expiresAtMs - Date.now()) / 1000);\n\n return {\n expired: expiresInSeconds <= 0,\n expiresInSeconds,\n };\n};\n","import type { SiteConfig } from \"@repo/models\";\nimport { createFsSource, loadSiteConfig } from \"@repo/previewing\";\n\nimport { CliError, EXIT_CODES } from \"./errors.js\";\n\nconst CONFIG_FILE = \"docs.json\";\n\nexport interface ValidatedSiteConfigResult {\n config: SiteConfig;\n warnings: string[];\n}\n\nexport const loadValidatedSiteConfig = async (\n root: string\n): Promise<ValidatedSiteConfigResult> => {\n const result = await loadSiteConfig(createFsSource(root));\n\n if (!result.ok) {\n throw new CliError(\n result.errors.join(\"\\n\"),\n EXIT_CODES.VALIDATION,\n `Make sure ${CONFIG_FILE} exists and is valid JSON.`\n );\n }\n\n return {\n config: result.config,\n warnings: result.warnings,\n };\n};\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { loadValidatedSiteConfig } from \"../site-config.js\";\n\nconst CONFIG_FILE = \"docs.json\";\n\nconst fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const resolveDocsRoot = async (dir?: string): Promise<string> => {\n if (dir) {\n return path.resolve(process.cwd(), dir);\n }\n\n const candidates = [\n process.cwd(),\n path.join(process.cwd(), \"docs\"),\n path.join(process.cwd(), \"apps/docs\"),\n ];\n\n for (const candidate of candidates) {\n if (await fileExists(path.join(candidate, CONFIG_FILE))) {\n return candidate;\n }\n }\n\n return process.cwd();\n};\n\nexport const validateDocsRoot = async (root: string) =>\n await loadValidatedSiteConfig(root);\n","import path from \"node:path\";\n\nimport { log } from \"@clack/prompts\";\nimport { watch } from \"chokidar\";\n\nconst INVALIDATE_ENDPOINT = \"/blodemd-dev/invalidate\";\nconst WATCH_DEBOUNCE_MS = 100;\n\nconst normalizeRelativePath = (root: string, filePath: string) =>\n path.relative(root, filePath).split(path.sep).join(\"/\");\n\nconst isDirectoryEvent = (event: string) =>\n event === \"addDir\" || event === \"unlinkDir\";\n\nexport const createDevWatcher = ({\n port,\n root,\n}: {\n port: number;\n root: string;\n}) => {\n const watcher = watch(root, {\n ignoreInitial: true,\n ignored: [\"**/.git/**\", \"**/.next/**\", \"**/dist/**\", \"**/node_modules/**\"],\n });\n\n let flushTimer: NodeJS.Timeout | null = null;\n let pendingKind: \"config\" | \"content\" = \"content\";\n const pendingPaths = new Set<string>();\n\n const flush = async () => {\n flushTimer = null;\n\n const paths = [...pendingPaths];\n const kind = pendingKind;\n pendingPaths.clear();\n pendingKind = \"content\";\n\n if (!paths.length) {\n return;\n }\n\n try {\n const response = await fetch(\n `http://127.0.0.1:${port}${INVALIDATE_ENDPOINT}`,\n {\n body: JSON.stringify({ kind, paths }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n } catch (error) {\n log.error(\n `Failed to invalidate preview cache: ${\n error instanceof Error ? error.message : \"unknown error\"\n }`\n );\n }\n };\n\n watcher.on(\"all\", (event, changedPath) => {\n if (isDirectoryEvent(event)) {\n return;\n }\n\n const relativePath = normalizeRelativePath(root, changedPath);\n pendingPaths.add(relativePath);\n\n if (path.basename(changedPath) === \"docs.json\") {\n pendingKind = \"config\";\n }\n\n if (flushTimer) {\n clearTimeout(flushTimer);\n }\n\n flushTimer = setTimeout(() => {\n flush();\n }, WATCH_DEBOUNCE_MS);\n });\n\n return {\n async close() {\n if (flushTimer) {\n clearTimeout(flushTimer);\n await flush();\n }\n\n await watcher.close();\n },\n };\n};\n","import { spawn } from \"node:child_process\";\nimport { once } from \"node:events\";\nimport fs from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { createServer } from \"node:net\";\nimport path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { intro, log } from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport open from \"open\";\n\nimport { CONFIG_DIR } from \"../constants.js\";\nimport { CliError, EXIT_CODES, toCliError } from \"../errors.js\";\nimport { resolveDocsRoot, validateDocsRoot } from \"./resolve-root.js\";\nimport { createDevWatcher } from \"./watcher.js\";\n\nconst DEV_READY_ENDPOINT = \"/blodemd-dev/version\";\nconst DEV_READY_TIMEOUT_MS = 45_000;\nconst DEV_PORT_SCAN_LIMIT = 10;\nconst DEV_SHUTDOWN_TIMEOUT_MS = 5000;\nconst LOCALHOST = \"127.0.0.1\";\nconst RUNTIME_EXCLUDE_DIRS = new Set([\".next\", \".turbo\", \"node_modules\"]);\n\nconst parsePositiveInteger = (value: string, label: string): number => {\n const parsed = Number.parseInt(value, 10);\n\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new CliError(\n `${label} must be a positive integer.`,\n EXIT_CODES.VALIDATION\n );\n }\n\n return parsed;\n};\n\nconst fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n};\n\ntype PortAvailabilityProbe = (port: number) => Promise<boolean>;\n\nconst probePortAvailability: PortAvailabilityProbe = async (port) => {\n const server = createServer();\n\n const listening = (async () => {\n await once(server, \"listening\");\n return { kind: \"listening\" as const };\n })();\n const errored = (async () => {\n const [error] = await once(server, \"error\");\n return {\n error: error as NodeJS.ErrnoException,\n kind: \"error\" as const,\n };\n })();\n\n server.listen({ exclusive: true, host: LOCALHOST, port });\n\n const outcome = await Promise.race([listening, errored]);\n\n if (outcome.kind === \"error\") {\n if (\n outcome.error.code === \"EADDRINUSE\" ||\n outcome.error.code === \"EACCES\"\n ) {\n return false;\n }\n\n throw outcome.error;\n }\n\n server.close();\n await once(server, \"close\");\n return true;\n};\n\nexport const resolveDevPort = async (\n requestedPort: number,\n probePort: PortAvailabilityProbe = probePortAvailability\n): Promise<number> => {\n for (let offset = 0; offset < DEV_PORT_SCAN_LIMIT; offset += 1) {\n const candidate = requestedPort + offset;\n if (candidate > 65_535) {\n break;\n }\n\n if (await probePort(candidate)) {\n return candidate;\n }\n }\n\n throw new CliError(\n `No available port found within ${DEV_PORT_SCAN_LIMIT} attempts starting at ${requestedPort}.`,\n EXIT_CODES.ERROR,\n \"Close the process using the port or pass a different --port value.\"\n );\n};\n\nexport const shutdownChildProcess = async (\n child: ReturnType<typeof spawn>,\n timeoutMs: number = DEV_SHUTDOWN_TIMEOUT_MS\n): Promise<void> => {\n if (child.exitCode !== null) {\n return;\n }\n\n const timer = setTimeout(() => {\n if (child.exitCode === null) {\n child.kill(\"SIGKILL\");\n }\n }, timeoutMs);\n\n const exitPromise = once(child, \"exit\");\n\n try {\n child.kill(\"SIGTERM\");\n } catch (error) {\n clearTimeout(timer);\n\n const killError = error as NodeJS.ErrnoException;\n if (killError.code === \"ESRCH\") {\n return;\n }\n\n throw error;\n }\n\n await exitPromise.finally(() => {\n clearTimeout(timer);\n });\n};\n\n// --- Dev-server resolution ---\n\ninterface StandaloneServer {\n mode: \"standalone\";\n devServerDir: string;\n nextPackageRoot: string;\n packagesDir: string;\n}\n\ninterface MonorepoServer {\n mode: \"monorepo\";\n repoRoot: string;\n}\n\ntype DevServerResolution = StandaloneServer | MonorepoServer;\n\n/**\n * Derive the CLI npm package root from the running script path.\n * The CLI entry point is at `<pkg-root>/dist/cli.mjs`.\n */\nconst resolveCliPackageRoot = (cliFilePath: string): string =>\n path.dirname(path.dirname(cliFilePath));\n\nconst copyStandaloneTree = async (\n sourceDir: string,\n targetDir: string\n): Promise<void> => {\n await fs.cp(sourceDir, targetDir, {\n filter: (source) => {\n const relative = path.relative(sourceDir, source);\n if (!relative) {\n return true;\n }\n\n const topSegment = relative.split(path.sep)[0] ?? \"\";\n return !RUNTIME_EXCLUDE_DIRS.has(topSegment);\n },\n recursive: true,\n });\n};\n\nconst isStandaloneCliInstall = async (\n cliPackageRoot: string\n): Promise<boolean> => {\n try {\n const realRoot = await fs.realpath(cliPackageRoot);\n return realRoot.split(path.sep).includes(\"node_modules\");\n } catch {\n return cliPackageRoot.split(path.sep).includes(\"node_modules\");\n }\n};\n\nconst materializeStandaloneRuntime = async (\n cliPackageRoot: string\n): Promise<{ devServerDir: string; packagesDir: string }> => {\n const runtimeRoot = path.join(CONFIG_DIR, \"standalone-runtime\");\n await fs.rm(runtimeRoot, { force: true, recursive: true });\n await fs.mkdir(runtimeRoot, { recursive: true });\n\n for (const dir of [\"dev-server\", \"docs\", \"packages\"]) {\n await copyStandaloneTree(\n path.join(cliPackageRoot, dir),\n path.join(runtimeRoot, dir)\n );\n }\n\n await fs.symlink(\n path.join(cliPackageRoot, \"node_modules\"),\n path.join(runtimeRoot, \"node_modules\"),\n process.platform === \"win32\" ? \"junction\" : \"dir\"\n );\n\n await fs.writeFile(\n path.join(runtimeRoot, \"dev-server\", \"package.json\"),\n `${JSON.stringify(\n {\n dependencies: {\n next: \"16.2.1\",\n react: \"^19.2.0\",\n \"react-dom\": \"^19.2.0\",\n },\n devDependencies: {\n \"@types/node\": \"^22.19.15\",\n \"@types/react\": \"19.2.14\",\n \"@types/react-dom\": \"19.2.3\",\n typescript: \"6.0.2\",\n },\n name: \"blodemd-dev-server\",\n private: true,\n type: \"module\",\n },\n null,\n 2\n )}\\n`\n );\n\n return {\n devServerDir: path.join(runtimeRoot, \"dev-server\"),\n packagesDir: path.join(runtimeRoot, \"packages\"),\n };\n};\n\n/**\n * Check if a shipped dev-server exists alongside the CLI (npm-installed mode).\n * Verifies both the dev-server directory AND that `next` is resolvable\n * (it's a dependency when npm-installed, but not in the monorepo).\n */\nconst findStandaloneDevServer = async (\n cliPackageRoot: string\n): Promise<StandaloneServer | null> => {\n const devServerDir = path.join(cliPackageRoot, \"dev-server\");\n if (!(await fileExists(path.join(devServerDir, \"next.config.js\")))) {\n return null;\n }\n\n if (!(await isStandaloneCliInstall(cliPackageRoot))) {\n return null;\n }\n\n // Verify `next` is resolvable — this distinguishes npm-installed from\n // a monorepo checkout that happens to have dev-server/ from prepare-dist.\n try {\n createRequire(path.join(cliPackageRoot, \"package.json\")).resolve(\n \"next/package.json\"\n );\n } catch {\n return null;\n }\n\n const runtime = await materializeStandaloneRuntime(cliPackageRoot);\n\n return {\n devServerDir: runtime.devServerDir,\n mode: \"standalone\",\n nextPackageRoot: cliPackageRoot,\n packagesDir: runtime.packagesDir,\n };\n};\n\n/**\n * Resolve the `next` CLI binary from the blodemd package's own dependencies.\n */\nconst resolveNextBin = (cliPackageRoot: string): string => {\n const require = createRequire(path.join(cliPackageRoot, \"package.json\"));\n const nextPkgPath = require.resolve(\"next/package.json\");\n return path.join(path.dirname(nextPkgPath), \"dist\", \"bin\", \"next\");\n};\n\nconst findMonorepoRoot = async (start: string): Promise<string> => {\n let current = start;\n\n while (true) {\n const packageJsonPath = path.join(current, \"package.json\");\n if (await fileExists(packageJsonPath)) {\n const raw = await fs.readFile(packageJsonPath, \"utf8\");\n const parsed = JSON.parse(raw) as { workspaces?: string[] };\n const workspaces = parsed.workspaces ?? [];\n\n if (workspaces.includes(\"apps/*\") && workspaces.includes(\"packages/*\")) {\n return current;\n }\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n break;\n }\n current = parent;\n }\n\n throw new CliError(\n \"Could not locate the blodemd dev server.\",\n EXIT_CODES.ERROR,\n \"Make sure blodemd is installed correctly (npm i blodemd).\"\n );\n};\n\nconst resolveDevServer = async (\n cliFilePath: string\n): Promise<DevServerResolution> => {\n const cliPackageRoot = resolveCliPackageRoot(cliFilePath);\n\n // Try standalone mode first (npm-installed)\n const standalone = await findStandaloneDevServer(cliPackageRoot);\n if (standalone) {\n return standalone;\n }\n\n // Fall back to monorepo mode (development)\n const repoRoot = await findMonorepoRoot(path.dirname(cliFilePath));\n return { mode: \"monorepo\", repoRoot };\n};\n\nconst spawnDevServer = (\n server: DevServerResolution,\n { root, port }: { root: string; port: number }\n): ReturnType<typeof spawn> => {\n if (server.mode === \"standalone\") {\n const nextBin = resolveNextBin(server.nextPackageRoot);\n\n return spawn(process.execPath, [nextBin, \"dev\", \"--webpack\"], {\n cwd: server.devServerDir,\n env: {\n ...process.env,\n BLODEMD_PACKAGES_DIR: server.packagesDir,\n DOCS_ROOT: root,\n // NODE_PATH lets require.resolve (used by Next.js transpilePackages)\n // find @repo/* packages from our shipped packages/ directory.\n NODE_PATH: [server.packagesDir, process.env.NODE_PATH]\n .filter(Boolean)\n .join(path.delimiter),\n PORT: String(port),\n },\n stdio: \"inherit\",\n });\n }\n\n const npmCommand = process.platform === \"win32\" ? \"npm.cmd\" : \"npm\";\n return spawn(npmCommand, [\"run\", \"dev\", \"--workspace=dev-server\"], {\n cwd: server.repoRoot,\n env: {\n ...process.env,\n DOCS_ROOT: root,\n PORT: String(port),\n },\n stdio: \"inherit\",\n });\n};\n\n// --- Server readiness ---\n\nconst waitForServer = async ({\n child,\n port,\n}: {\n child: ReturnType<typeof spawn>;\n port: number;\n}) => {\n const url = `http://localhost:${port}${DEV_READY_ENDPOINT}`;\n const startedAt = Date.now();\n\n while (Date.now() - startedAt < DEV_READY_TIMEOUT_MS) {\n if (child.exitCode !== null) {\n throw new CliError(\n \"The local dev server exited before it became ready.\",\n EXIT_CODES.ERROR\n );\n }\n\n try {\n const response = await fetch(url, {\n cache: \"no-store\",\n headers: {\n accept: \"application/json\",\n },\n });\n\n if (response.ok) {\n return;\n }\n } catch {\n // Server is still starting.\n }\n\n await delay(500);\n }\n\n throw new CliError(\n \"Timed out waiting for the local dev server to start.\",\n EXIT_CODES.ERROR\n );\n};\n\n// --- Main command ---\n\nexport const devCommand = async ({\n dir,\n openBrowser,\n port: portValue,\n}: {\n dir?: string;\n openBrowser: boolean;\n port: string;\n}) => {\n intro(chalk.bold(\"blodemd dev\"));\n\n try {\n const port = parsePositiveInteger(portValue, \"Port\");\n const resolvedPort = await resolveDevPort(port);\n const root = await resolveDocsRoot(dir);\n await validateDocsRoot(root);\n\n const cliFilePath = fileURLToPath(import.meta.url);\n const server = await resolveDevServer(cliFilePath);\n const localUrl = `http://localhost:${resolvedPort}`;\n\n log.info(`Docs root: ${chalk.cyan(root)}`);\n\n const child = spawnDevServer(server, { port: resolvedPort, root });\n\n let watcher: Awaited<ReturnType<typeof createDevWatcher>> | null = null;\n let shuttingDown = false;\n\n const closeAll = async () => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n\n if (watcher) {\n await watcher.close();\n watcher = null;\n }\n\n await shutdownChildProcess(child);\n };\n\n process.once(\"SIGINT\", closeAll);\n process.once(\"SIGTERM\", closeAll);\n\n try {\n await waitForServer({ child, port: resolvedPort });\n\n watcher = await createDevWatcher({ port: resolvedPort, root });\n log.success(`Dev server running at ${chalk.cyan(localUrl)}`);\n\n if (openBrowser) {\n await open(localUrl);\n }\n\n const [code, signal] = (await once(child, \"exit\")) as [\n number | null,\n NodeJS.Signals | null,\n ];\n\n if (shuttingDown || signal === \"SIGINT\" || signal === \"SIGTERM\") {\n return;\n }\n\n if (code !== 0) {\n throw new CliError(\n `The local dev server exited with code ${code ?? \"unknown\"}.`,\n EXIT_CODES.ERROR\n );\n }\n } finally {\n await closeAll();\n process.removeListener(\"SIGINT\", closeAll);\n process.removeListener(\"SIGTERM\", closeAll);\n }\n } catch (error) {\n const cliError = toCliError(error);\n\n log.error(cliError.message);\n if (cliError.hint) {\n log.info(cliError.hint);\n }\n\n process.exitCode = cliError.exitCode;\n }\n};\n","// oxlint-disable no-use-before-define -- circular reference in callback pattern\nimport { createServer } from \"node:http\";\nimport type { Socket } from \"node:net\";\n\nimport { CliError, EXIT_CODES } from \"./errors.js\";\n\ninterface OAuthCallbackOptions {\n redirectUrl: URL;\n expectedState: string;\n timeoutMs: number;\n}\n\nconst SUCCESS_HTML =\n '<!doctype html><html><head><meta charset=\"utf-8\"/><title>Blode.md CLI</title></head><body><h2>Logged in! You can close this tab.</h2></body></html>';\n\nconst escapeHtml = (text: string): string =>\n text\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n\nconst errorHtml = (message: string): string =>\n `<!doctype html><html><head><meta charset=\"utf-8\"/><title>Blode.md CLI</title></head><body><h2>Login failed</h2><p>${escapeHtml(message)}</p></body></html>`;\n\nexport const waitForOAuthCode = (\n options: OAuthCallbackOptions\n): Promise<string> => {\n const host = options.redirectUrl.hostname;\n const port = Number(options.redirectUrl.port);\n const { pathname } = options.redirectUrl;\n\n if (!Number.isInteger(port) || port <= 0) {\n return Promise.reject(\n new CliError(\n \"OAuth redirect URL requires an explicit port\",\n EXIT_CODES.ERROR\n )\n );\n }\n\n // oxlint-disable-next-line eslint-plugin-promise/avoid-new -- wrapping callback-based HTTP server\n return new Promise<string>((resolve, reject) => {\n let settled = false;\n const sockets = new Set<Socket>();\n\n const settle = (ok: boolean, value: string | CliError): void => {\n if (settled) {\n return;\n }\n\n settled = true;\n clearTimeout(timer);\n\n httpServer.close(() => {\n if (ok) {\n resolve(value as string);\n } else {\n reject(value);\n }\n });\n\n // Destroy kept-alive connections so httpServer.close() can finish\n for (const socket of sockets) {\n socket.destroy();\n }\n };\n\n const httpServer = createServer((request, response) => {\n if (!request.url) {\n response.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(\"Missing request URL\"));\n settle(\n false,\n new CliError(\n \"OAuth callback is missing a request URL\",\n EXIT_CODES.ERROR\n )\n );\n return;\n }\n\n const url = new URL(request.url, options.redirectUrl.origin);\n\n if (url.pathname !== pathname) {\n response.writeHead(404, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(\"Invalid callback path\"));\n return;\n }\n\n const providerError = url.searchParams.get(\"error\");\n if (providerError) {\n const description =\n url.searchParams.get(\"error_description\") ?? providerError;\n\n response.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(description));\n\n settle(\n false,\n new CliError(\n `OAuth provider returned an error: ${description}`,\n EXIT_CODES.ERROR\n )\n );\n return;\n }\n\n const state = url.searchParams.get(\"state\");\n if (state !== options.expectedState) {\n response.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(\"State verification failed\"));\n\n settle(\n false,\n new CliError(\"OAuth state verification failed\", EXIT_CODES.ERROR)\n );\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n if (!code) {\n response.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(\"Authorization code was missing\"));\n\n settle(\n false,\n new CliError(\n \"OAuth callback is missing an authorization code\",\n EXIT_CODES.ERROR\n )\n );\n return;\n }\n\n response.writeHead(200, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(SUCCESS_HTML);\n settle(true, code);\n });\n\n httpServer.on(\"connection\", (socket) => {\n sockets.add(socket);\n socket.once(\"close\", () => sockets.delete(socket));\n });\n\n httpServer.on(\"error\", (error) => {\n settle(\n false,\n new CliError(\n `Failed to start callback server on ${host}:${port}: ${error.message}`,\n EXIT_CODES.ERROR\n )\n );\n });\n\n const timer = setTimeout(() => {\n settle(\n false,\n new CliError(\"Login timed out. Please try again.\", EXIT_CODES.CANCELLED)\n );\n }, options.timeoutMs);\n\n httpServer.listen(port, host);\n });\n};\n","import { createHash, randomBytes } from \"node:crypto\";\n\nexport const createOAuthState = (): string => randomBytes(24).toString(\"hex\");\n\nexport const createCodeVerifier = (): string =>\n randomBytes(64).toString(\"base64url\");\n\nexport const createCodeChallenge = (verifier: string): string =>\n createHash(\"sha256\").update(verifier).digest().toString(\"base64url\");\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { CliError, EXIT_CODES } from \"./errors.js\";\n\nconst MIN_SUPPORTED_NODE_VERSION = [20, 17, 0] as const;\nconst MAX_SUPPORTED_NODE_MAJOR = 25;\n\nexport const SUPPORTED_NODE_RANGE = \">=20.17.0 <25\";\n\nconst parseVersion = (input: string): [number, number, number] | null => {\n const match = /^v?(\\d+)\\.(\\d+)\\.(\\d+)/.exec(input.trim());\n if (!match) {\n return null;\n }\n\n const [, majorText = \"\", minorText = \"\", patchText = \"\"] = match;\n if (!majorText || !minorText || !patchText) {\n return null;\n }\n\n const major = Number.parseInt(majorText, 10);\n const minor = Number.parseInt(minorText, 10);\n const patch = Number.parseInt(patchText, 10);\n\n if ([major, minor, patch].some((value) => Number.isNaN(value))) {\n return null;\n }\n\n return [major, minor, patch];\n};\n\nexport const isSupportedNodeVersion = (version: string): boolean => {\n const parsed = parseVersion(version);\n if (!parsed) {\n return false;\n }\n\n const [major, minor, patch] = parsed;\n const [minMajor, minMinor, minPatch] = MIN_SUPPORTED_NODE_VERSION;\n\n if (major >= MAX_SUPPORTED_NODE_MAJOR) {\n return false;\n }\n\n if (major !== minMajor) {\n return major > minMajor;\n }\n\n if (minor !== minMinor) {\n return minor > minMinor;\n }\n\n return patch >= minPatch;\n};\n\nexport const assertSupportedNodeVersion = (\n version = process.versions.node\n): void => {\n if (isSupportedNodeVersion(version)) {\n return;\n }\n\n throw new CliError(\n `blodemd requires Node.js ${SUPPORTED_NODE_RANGE}. Current version: ${version}.`,\n EXIT_CODES.VALIDATION,\n \"Install a supported Node.js version and try again.\"\n );\n};\n\nexport const readCliVersion = (moduleUrl: string): string => {\n const moduleDir = path.dirname(fileURLToPath(moduleUrl));\n const packageJsonPath = path.resolve(moduleDir, \"..\", \"package.json\");\n const raw = readFileSync(packageJsonPath, \"utf8\");\n const parsed = JSON.parse(raw) as { version?: string };\n\n return parsed.version ?? \"0.0.0\";\n};\n","import { spawnSync } from \"node:child_process\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n confirm,\n intro,\n isCancel,\n log,\n password,\n spinner,\n} from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport { Command } from \"commander\";\nimport open from \"open\";\n\nimport { resolveAuthToken, resolveTokenStatus } from \"./auth-session.js\";\nimport {\n BLODE_API_URL_ENV,\n BLODE_BRANCH_ENV,\n BLODE_COMMIT_MESSAGE_ENV,\n BLODE_PROJECT_ENV,\n DEFAULT_API_URL,\n DEFAULT_OAUTH_CALLBACK_PATH,\n DEFAULT_OAUTH_CALLBACK_PORT,\n DEFAULT_OAUTH_TIMEOUT_SECONDS,\n OAUTH_CLIENT_ID,\n} from \"./constants.js\";\nimport { devCommand } from \"./dev/command.js\";\nimport { resolveDocsRoot } from \"./dev/resolve-root.js\";\nimport { CliError, EXIT_CODES, toCliError } from \"./errors.js\";\nimport { waitForOAuthCode } from \"./oauth-callback.js\";\nimport { exchangeAuthorizationCode } from \"./oauth-token.js\";\nimport {\n createCodeChallenge,\n createCodeVerifier,\n createOAuthState,\n} from \"./pkce.js\";\nimport { assertSupportedNodeVersion, readCliVersion } from \"./runtime.js\";\nimport { loadValidatedSiteConfig } from \"./site-config.js\";\nimport {\n clearStoredCredentials,\n readAuthFile,\n writeStoredApiKey,\n writeStoredAuthSession,\n} from \"./storage.js\";\nimport {\n buildOAuthUrls,\n resolveSupabaseConfig,\n tokenResponseToStoredSession,\n} from \"./supabase.js\";\nimport type { DeploymentResponse } from \"./types.js\";\n\nconst CONFIG_FILE = \"docs.json\";\n\nconst TEXT_CONTENT_TYPES: Record<string, string> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".md\": \"text/markdown; charset=utf-8\",\n \".mdx\": \"text/markdown; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".txt\": \"text/plain; charset=utf-8\",\n \".yaml\": \"application/yaml; charset=utf-8\",\n \".yml\": \"application/yaml; charset=utf-8\",\n};\n\n// --- File helpers ---\n\nconst ensureFile = async (filePath: string, content: string): Promise<void> => {\n try {\n await fs.writeFile(filePath, content, { flag: \"wx\" });\n } catch {\n // File already exists\n }\n};\n\nconst readGitValue = (gitArgs: string[]): string | undefined => {\n const result = spawnSync(\"git\", gitArgs, {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n\n if (result.status !== 0) {\n return;\n }\n\n const value = result.stdout.trim();\n return value || undefined;\n};\n\nconst normalizeRelativePath = (root: string, filePath: string): string =>\n path.relative(root, filePath).split(path.sep).join(\"/\");\n\nconst shouldSkipEntry = (name: string): boolean =>\n name.startsWith(\".\") || name === \"node_modules\";\n\nconst collectFiles = async (root: string): Promise<string[]> => {\n const entries = await fs.readdir(root, { withFileTypes: true });\n const files: string[] = [];\n\n for (const entry of entries) {\n if (shouldSkipEntry(entry.name)) {\n continue;\n }\n\n const absolutePath = path.join(root, entry.name);\n if (entry.isDirectory()) {\n files.push(...(await collectFiles(absolutePath)));\n continue;\n }\n\n if (entry.isFile()) {\n files.push(absolutePath);\n }\n }\n\n return files.toSorted((left, right) => left.localeCompare(right));\n};\n\nconst getContentType = (filePath: string): string =>\n TEXT_CONTENT_TYPES[path.extname(filePath).toLowerCase()] ??\n \"application/octet-stream\";\n\nconst readJson = async (response: Response): Promise<unknown> => {\n const text = await response.text();\n if (!text) {\n return null;\n }\n\n try {\n return JSON.parse(text) as unknown;\n } catch {\n return text;\n }\n};\n\nconst requestJson = async <T>(\n url: string,\n init: RequestInit,\n message: string\n): Promise<T> => {\n const response = await fetch(url, init);\n const data = await readJson(response);\n if (!response.ok) {\n const detail =\n typeof data === \"string\" ? data : JSON.stringify(data ?? {}, null, 2);\n throw new Error(`${message}: ${response.status} ${detail}`);\n }\n\n return data as T;\n};\n\nconst parsePositiveInteger = (value: string, label: string): number => {\n const parsed = Number.parseInt(value, 10);\n\n if (!Number.isInteger(parsed) || parsed <= 0) {\n throw new CliError(\n `${label} must be a positive integer.`,\n EXIT_CODES.VALIDATION\n );\n }\n\n return parsed;\n};\n\nconst reportCommandError = (prefix: string, error: unknown): void => {\n const cliError = toCliError(error);\n\n log.error(`${prefix}: ${cliError.message}`);\n if (cliError.hint) {\n log.info(cliError.hint);\n }\n log.info(\"Failed\");\n process.exitCode = cliError.exitCode;\n};\n\n// --- Auth helpers ---\n\nconst fetchUserEmail = async (\n apiUrl: string,\n token: string\n): Promise<string | null> => {\n try {\n const user = await requestJson<{ email: string }>(\n `${apiUrl}/auth/me`,\n { headers: { Authorization: `Bearer ${token}` } },\n \"Failed to fetch user info\"\n );\n return user.email;\n } catch {\n return null;\n }\n};\n\n// --- Push helpers ---\n\ninterface PushConfig {\n project: string;\n apiUrl: string;\n authToken: string;\n branch: string;\n commitMessage?: string;\n}\n\nconst resolvePushConfig = async (\n config: { name?: string },\n options: {\n apiKey?: string;\n apiUrl?: string;\n branch?: string;\n message?: string;\n project?: string;\n }\n): Promise<PushConfig> => {\n const project =\n options.project ?? process.env[BLODE_PROJECT_ENV] ?? config.name;\n const apiUrl =\n options.apiUrl ?? process.env[BLODE_API_URL_ENV] ?? DEFAULT_API_URL;\n\n const resolved = await resolveAuthToken(options.apiKey);\n const authToken = resolved?.token;\n\n const branch =\n options.branch ??\n process.env[BLODE_BRANCH_ENV] ??\n process.env.GITHUB_REF_NAME ??\n readGitValue([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]) ??\n \"main\";\n const commitMessage =\n options.message ??\n process.env[BLODE_COMMIT_MESSAGE_ENV] ??\n readGitValue([\"log\", \"-1\", \"--pretty=%s\"]);\n\n if (!project) {\n throw new Error(\n 'Missing project slug. Set \"name\" in docs.json, pass --project, or set BLODEMD_PROJECT.'\n );\n }\n if (!authToken) {\n throw new Error(\n 'Missing credentials. Run \"blodemd login\", pass --api-key, or set BLODEMD_API_KEY.'\n );\n }\n\n return { apiUrl, authToken, branch, commitMessage, project };\n};\n\nconst autoCreateProject = async (\n project: string,\n apiUrl: string,\n headers: Record<string, string>\n): Promise<boolean> => {\n const authData = await readAuthFile();\n if (!authData?.session) {\n throw new Error(\n `Project \"${project}\" not found. Create it at blode.md or login with \"blodemd login\" to auto-create.`\n );\n }\n\n const shouldCreate = await confirm({\n message: `Project \"${project}\" doesn't exist. Create it?`,\n });\n\n if (isCancel(shouldCreate) || !shouldCreate) {\n return false;\n }\n\n const createResult = await requestJson<{\n project: { id: string; slug: string };\n token: string;\n }>(\n new URL(\"/projects\", apiUrl).toString(),\n {\n body: JSON.stringify({ name: project, slug: project }),\n headers,\n method: \"POST\",\n },\n \"Failed to create project\"\n );\n\n log.success(`Project ${chalk.cyan(createResult.project.slug)} created`);\n log.info(`API key for CI: ${chalk.dim(createResult.token)}`);\n return true;\n};\n\n// 4 MB limit keeps each batch well under Vercel's 4.5 MB serverless body cap\nconst MAX_BATCH_BYTES = 4 * 1024 * 1024;\n\nconst uploadFiles = async (\n files: string[],\n root: string,\n apiPath: (suffix: string) => string,\n deploymentId: string,\n headers: Record<string, string>,\n s: ReturnType<typeof spinner>\n) => {\n s.start(`Uploading ${files.length} files`);\n\n const items = await Promise.all(\n files.map(async (filePath) => {\n const content = await fs.readFile(filePath);\n return {\n contentBase64: content.toString(\"base64\"),\n contentType: getContentType(filePath),\n path: normalizeRelativePath(root, filePath),\n };\n })\n );\n\n // Split into size-bounded batches\n const batches: (typeof items)[] = [];\n let current: typeof items = [];\n let currentBytes = 0;\n\n for (const item of items) {\n const itemBytes = item.contentBase64.length + item.path.length + 64;\n if (current.length > 0 && currentBytes + itemBytes > MAX_BATCH_BYTES) {\n batches.push(current);\n current = [];\n currentBytes = 0;\n }\n current.push(item);\n currentBytes += itemBytes;\n }\n if (current.length > 0) {\n batches.push(current);\n }\n\n let uploaded = 0;\n for (const batch of batches) {\n await requestJson(\n apiPath(`/${deploymentId}/files/batch`),\n {\n body: JSON.stringify({ files: batch }),\n headers,\n method: \"POST\",\n },\n \"Failed to upload files\"\n );\n uploaded += batch.length;\n s.message(`Uploading files (${uploaded}/${files.length})`);\n }\n\n s.stop(`Uploaded ${chalk.cyan(String(files.length))} files`);\n};\n\n// --- CLI ---\n\nconst program = new Command();\nconst cliVersion = readCliVersion(import.meta.url);\n\nprogram.name(\"blodemd\").description(\"Blode.md CLI\").version(cliVersion);\nprogram.hook(\"preAction\", () => {\n assertSupportedNodeVersion();\n});\n\n// login\n\nprogram\n .command(\"login\")\n .description(\"Authenticate with Blode.md\")\n .option(\"--token\", \"Paste an API key instead of using browser login\")\n .option(\n \"--port <port>\",\n \"Loopback callback port\",\n String(DEFAULT_OAUTH_CALLBACK_PORT)\n )\n .option(\n \"--timeout <seconds>\",\n \"OAuth timeout in seconds\",\n String(DEFAULT_OAUTH_TIMEOUT_SECONDS)\n )\n .option(\"--no-open\", \"Print URL instead of opening the browser\")\n .action(\n async (options: {\n token?: boolean;\n port: string;\n timeout: string;\n open: boolean;\n }) => {\n intro(chalk.bold(\"blodemd login\"));\n\n try {\n if (options.token) {\n const apiKey = await password({\n message: \"Enter your API key\",\n validate: (value) => {\n if (!value) {\n return \"API key is required.\";\n }\n },\n });\n\n if (isCancel(apiKey)) {\n log.warn(\"Cancelled\");\n return;\n }\n\n await writeStoredApiKey({ apiKey, type: \"api-key\" });\n\n const prefix = apiKey.split(\".\")[0] ?? apiKey.slice(0, 12);\n log.success(`Authenticated as ${chalk.cyan(prefix)}`);\n log.info(\"Done\");\n return;\n }\n\n // OAuth 2.1 authorization code flow with PKCE\n const config = resolveSupabaseConfig();\n const { authorizeUrl, tokenUrl } = buildOAuthUrls(config);\n const clientId = OAUTH_CLIENT_ID;\n\n const port = parsePositiveInteger(options.port, \"Port\");\n const timeoutSeconds = parsePositiveInteger(options.timeout, \"Timeout\");\n const redirectUrl = new URL(\n `http://127.0.0.1:${port}${DEFAULT_OAUTH_CALLBACK_PATH}`\n );\n\n const state = createOAuthState();\n const codeVerifier = createCodeVerifier();\n const codeChallenge = createCodeChallenge(codeVerifier);\n\n const authUrl = new URL(authorizeUrl);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"client_id\", clientId);\n authUrl.searchParams.set(\"redirect_uri\", redirectUrl.toString());\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authUrl.searchParams.set(\"state\", state);\n authUrl.searchParams.set(\"scope\", \"openid email profile\");\n\n const callbackPromise = waitForOAuthCode({\n expectedState: state,\n redirectUrl,\n timeoutMs: timeoutSeconds * 1000,\n });\n\n if (options.open) {\n log.info(\"Opening browser for authentication...\");\n log.info(\n `If the browser doesn't open, visit: ${chalk.cyan(authUrl.toString())}`\n );\n await open(authUrl.toString());\n } else {\n log.info(\"Open this URL to continue authentication:\");\n log.info(chalk.cyan(authUrl.toString()));\n }\n\n const code = await callbackPromise;\n\n const tokenResponse = await exchangeAuthorizationCode(\n { clientId, tokenUrl },\n code,\n codeVerifier,\n redirectUrl.toString()\n );\n\n const storedSession = tokenResponseToStoredSession(tokenResponse);\n await writeStoredAuthSession(storedSession);\n\n const email =\n storedSession.user?.email ??\n (await fetchUserEmail(\n process.env[BLODE_API_URL_ENV] ?? DEFAULT_API_URL,\n storedSession.accessToken\n ));\n\n if (email) {\n log.success(`Logged in as ${chalk.cyan(email)}`);\n } else {\n log.success(\"Logged in successfully.\");\n }\n\n log.info(\"Done\");\n } catch (error: unknown) {\n reportCommandError(\"Login failed\", error);\n }\n }\n );\n\n// logout\n\nprogram\n .command(\"logout\")\n .description(\"Remove stored credentials\")\n .action(async () => {\n intro(chalk.bold(\"blodemd logout\"));\n\n try {\n const existing = await readAuthFile();\n await clearStoredCredentials();\n\n if (existing?.session || existing?.apiKey) {\n log.success(\"Credentials removed.\");\n } else {\n log.info(\"No stored credentials found.\");\n }\n log.info(\"Done\");\n } catch (error: unknown) {\n reportCommandError(\"Logout failed\", error);\n }\n });\n\n// whoami\n\nprogram\n .command(\"whoami\")\n .description(\"Show current authentication\")\n .action(async () => {\n try {\n const resolved = await resolveAuthToken();\n\n if (!resolved) {\n log.warn('Not logged in. Run \"blodemd login\" to authenticate.');\n return;\n }\n\n if (resolved.source === \"environment\") {\n log.info(\"Authenticated via BLODEMD_API_KEY environment variable\");\n return;\n }\n\n // API keys have no expiry and no user info from JWT\n if (!resolved.expiresAt && !resolved.user) {\n const prefix =\n resolved.token.split(\".\")[0] ?? resolved.token.slice(0, 12);\n log.info(`Logged in with API key ${chalk.cyan(prefix)}`);\n return;\n }\n\n const status = resolveTokenStatus(resolved);\n\n const email =\n resolved.user?.email ??\n (await fetchUserEmail(\n process.env[BLODE_API_URL_ENV] ?? DEFAULT_API_URL,\n resolved.token\n ));\n\n if (email) {\n log.info(`Logged in as ${chalk.cyan(email)}`);\n } else {\n log.info(\"Logged in (could not fetch user details).\");\n }\n\n if (resolved.expiresAt && status.expired) {\n log.warn(\n 'Session has expired. Run \"blodemd login\" to re-authenticate.'\n );\n }\n } catch (error: unknown) {\n reportCommandError(\"Whoami failed\", error);\n }\n });\n\n// init\n\nprogram\n .command(\"init\")\n .description(\"Scaffold a docs folder\")\n .argument(\"[dir]\", \"target directory\", \"docs\")\n .action(async (dir: string) => {\n intro(chalk.bold(\"blodemd init\"));\n\n try {\n const root = path.resolve(process.cwd(), dir);\n await fs.mkdir(root, { recursive: true });\n\n const docsJson = {\n $schema: \"https://docs.blode.md/docs.json\",\n colors: { primary: \"#0D9373\" },\n name: \"my-project\",\n navigation: {\n groups: [{ group: \"Getting Started\", pages: [\"index\"] }],\n },\n theme: \"mint\",\n };\n\n await ensureFile(\n path.join(root, CONFIG_FILE),\n `${JSON.stringify(docsJson, null, 2)}\\n`\n );\n await ensureFile(\n path.join(root, \"index.mdx\"),\n \"---\\ntitle: Welcome\\n---\\n\\nStart writing your docs here.\\n\"\n );\n\n log.success(`Docs scaffolded in ${chalk.cyan(root)}`);\n log.info(`Set ${chalk.cyan(\"name\")} in docs.json to your project slug.`);\n log.info(\"Done\");\n } catch (error: unknown) {\n reportCommandError(\"Init failed\", error);\n }\n });\n\n// validate\n\nprogram\n .command(\"validate\")\n .description(\"Validate docs.json\")\n .argument(\"[dir]\", \"docs directory\")\n .action(async (dir?: string) => {\n intro(chalk.bold(\"blodemd validate\"));\n\n try {\n const root = await resolveDocsRoot(dir);\n const { warnings } = await loadValidatedSiteConfig(root);\n for (const warning of warnings) {\n log.warn(warning);\n }\n log.success(`${chalk.cyan(CONFIG_FILE)} is valid.`);\n log.info(\"Done\");\n } catch (error: unknown) {\n reportCommandError(\"Validation failed\", error);\n }\n });\n\n// push\n\nprogram\n .command(\"push\")\n .description(\"Deploy docs\")\n .argument(\"[dir]\", \"docs directory\")\n .option(\"--project <slug>\", \"project slug (env: BLODEMD_PROJECT)\")\n .option(\"--api-url <url>\", \"API URL (env: BLODEMD_API_URL)\")\n .option(\"--api-key <token>\", \"API key (env: BLODEMD_API_KEY)\")\n .option(\"--branch <name>\", \"git branch (env: BLODEMD_BRANCH)\")\n .option(\"--message <msg>\", \"deploy message (env: BLODEMD_COMMIT_MESSAGE)\")\n .action(\n async (\n dir: string | undefined,\n options: {\n apiKey?: string;\n apiUrl?: string;\n branch?: string;\n message?: string;\n project?: string;\n }\n ) => {\n intro(chalk.bold(\"blodemd push\"));\n const s = spinner();\n\n try {\n const root = await resolveDocsRoot(dir);\n\n s.start(\"Validating configuration\");\n const { config, warnings } = await loadValidatedSiteConfig(root);\n s.stop(\"Configuration valid\");\n for (const warning of warnings) {\n log.warn(warning);\n }\n\n const { project, apiUrl, authToken, branch, commitMessage } =\n await resolvePushConfig(config, options);\n\n s.start(\"Collecting files\");\n const files = await collectFiles(root);\n if (files.length === 0) {\n throw new Error(\"No files found to deploy.\");\n }\n s.stop(`Found ${chalk.cyan(String(files.length))} files`);\n\n const headers = {\n Authorization: `Bearer ${authToken}`,\n \"Content-Type\": \"application/json\",\n };\n\n const apiPath = (suffix: string): string =>\n new URL(\n `/projects/slug/${project}/deployments${suffix}`,\n apiUrl\n ).toString();\n\n const createDeploymentBody = JSON.stringify({ branch, commitMessage });\n\n // Try creating the deployment — if 404, offer to create the project\n s.start(\"Creating deployment\");\n let deployment: DeploymentResponse;\n try {\n deployment = await requestJson<DeploymentResponse>(\n apiPath(\"\"),\n { body: createDeploymentBody, headers, method: \"POST\" },\n \"Failed to create deployment\"\n );\n } catch (error: unknown) {\n const errorMessage = error instanceof Error ? error.message : \"\";\n if (!errorMessage.includes(\"404\")) {\n throw error;\n }\n\n s.stop(\"Project not found\");\n\n const created = await autoCreateProject(project, apiUrl, headers);\n if (!created) {\n log.info(\"Cancelled\");\n return;\n }\n\n s.start(\"Creating deployment\");\n deployment = await requestJson<DeploymentResponse>(\n apiPath(\"\"),\n { body: createDeploymentBody, headers, method: \"POST\" },\n \"Failed to create deployment\"\n );\n }\n s.stop(`Deployment ${chalk.cyan(deployment.id)} created`);\n\n await uploadFiles(files, root, apiPath, deployment.id, headers, s);\n\n s.start(\"Finalizing deployment\");\n const finalized = await requestJson<DeploymentResponse>(\n apiPath(`/${deployment.id}/finalize`),\n {\n body: JSON.stringify({ promote: true }),\n headers,\n method: \"POST\",\n },\n \"Failed to finalize deployment\"\n );\n s.stop(\"Deployment finalized\");\n\n log.success(`Published ${chalk.cyan(finalized.id)}`);\n if (finalized.manifestUrl) {\n log.info(`Manifest: ${finalized.manifestUrl}`);\n }\n if (typeof finalized.fileCount === \"number\") {\n log.info(`Files: ${finalized.fileCount}`);\n }\n\n log.info(\"Done\");\n } catch (error: unknown) {\n s.stop(\"Failed\");\n reportCommandError(\"Push failed\", error);\n }\n }\n );\n\n// dev\n\nprogram\n .command(\"dev\")\n .description(\"Start the local docs dev server\")\n .option(\"-p, --port <port>\", \"Port number\", \"3030\")\n .option(\"-d, --dir <dir>\", \"Docs directory\")\n .option(\"--no-open\", \"Don't open browser\")\n .action(\n async (options: { dir?: string; open?: boolean; port: string }) =>\n await devCommand({\n dir: options.dir,\n openBrowser: options.open ?? true,\n port: options.port,\n })\n );\n\nprogram.parse();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["CONFIG_FILE","CONFIG_FILE","fileExists","normalizeRelativePath","delay","createServer"],"sources":["../src/constants.ts","../src/jwt.ts","../src/errors.ts","../src/oauth-token.ts","../src/storage.ts","../src/supabase.ts","../src/auth-session.ts","../src/validation.ts","../src/site-config.ts","../src/dev/resolve-root.ts","../src/dev/watcher.ts","../src/dev/command.ts","../src/fs-utils.ts","../src/scaffold.ts","../src/new-flow.ts","../src/oauth-callback.ts","../src/pkce.ts","../src/runtime.ts","../src/upload.ts","../src/cli.ts"],"sourcesContent":["import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const CLI_NAME = \"blodemd\";\n\nexport const BLODE_TOKEN_ENV = \"BLODEMD_API_KEY\";\nexport const BLODE_API_URL_ENV = \"BLODEMD_API_URL\";\nexport const BLODE_PROJECT_ENV = \"BLODEMD_PROJECT\";\nexport const BLODE_BRANCH_ENV = \"BLODEMD_BRANCH\";\nexport const BLODE_COMMIT_MESSAGE_ENV = \"BLODEMD_COMMIT_MESSAGE\";\n\nexport const DEFAULT_API_URL = \"https://api.blode.md\";\nexport const DEFAULT_SUPABASE_URL = \"https://bwnxwgkgyklzzmpbzuoz.supabase.co\";\n\nexport const OAUTH_CLIENT_ID = \"6b5f9860-fe96-4a83-b1ad-266260523c91\";\n\nexport const DEFAULT_OAUTH_CALLBACK_PORT = 8787;\nexport const DEFAULT_OAUTH_CALLBACK_PATH = \"/auth/callback\";\nexport const DEFAULT_OAUTH_TIMEOUT_SECONDS = 180;\n\nconst getDefaultConfigBaseDir = (): string => {\n if (process.platform === \"win32\") {\n return process.env.APPDATA ?? join(homedir(), \"AppData\", \"Roaming\");\n }\n\n return process.env.XDG_CONFIG_HOME ?? join(homedir(), \".config\");\n};\n\nconst configBaseDir = getDefaultConfigBaseDir();\n\nexport const CONFIG_DIR = join(configBaseDir, CLI_NAME);\nexport const CREDENTIALS_FILE = join(CONFIG_DIR, \"credentials.json\");\n","export interface JwtClaims {\n exp?: number;\n email?: string;\n sub?: string;\n}\n\nconst parseJwtBase64Url = (input: string): string => {\n const normalized = input.replaceAll(\"-\", \"+\").replaceAll(\"_\", \"/\");\n const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, \"=\");\n return Buffer.from(padded, \"base64\").toString(\"utf8\");\n};\n\nexport const parseJwtClaims = (token: string): JwtClaims | null => {\n const parts = token.split(\".\");\n const payloadPart = parts.at(1);\n\n if (!payloadPart) {\n return null;\n }\n\n try {\n const payload = parseJwtBase64Url(payloadPart);\n const parsed = JSON.parse(payload) as unknown;\n\n if (typeof parsed !== \"object\" || parsed === null) {\n return null;\n }\n\n const claims = parsed as Record<string, unknown>;\n\n return {\n email: typeof claims.email === \"string\" ? claims.email : undefined,\n exp: typeof claims.exp === \"number\" ? claims.exp : undefined,\n sub: typeof claims.sub === \"string\" ? claims.sub : undefined,\n };\n } catch {\n return null;\n }\n};\n","export const EXIT_CODES = {\n AUTH_REQUIRED: 4,\n CANCELLED: 2,\n ERROR: 1,\n NETWORK: 5,\n SUCCESS: 0,\n VALIDATION: 3,\n} as const;\n\ntype ExitCode = (typeof EXIT_CODES)[keyof typeof EXIT_CODES];\n\nexport class CliError extends Error {\n readonly exitCode: ExitCode;\n readonly hint: string | null;\n\n constructor(\n message: string,\n exitCode: ExitCode = EXIT_CODES.ERROR,\n hint?: string\n ) {\n super(message);\n this.name = \"CliError\";\n this.exitCode = exitCode;\n this.hint = hint ?? null;\n }\n}\n\nexport const toCliError = (error: unknown): CliError => {\n if (error instanceof CliError) {\n return error;\n }\n\n if (error instanceof Error) {\n if (error instanceof TypeError && error.message.includes(\"fetch\")) {\n return new CliError(\n \"Cannot connect to Blode.md API.\",\n EXIT_CODES.NETWORK,\n \"Check your internet connection and API URL configuration.\"\n );\n }\n\n if (error.name === \"TimeoutError\" || error.name === \"AbortError\") {\n return new CliError(\n \"Request timed out.\",\n EXIT_CODES.NETWORK,\n \"The API may be unavailable. Try again later.\"\n );\n }\n\n return new CliError(error.message, EXIT_CODES.ERROR);\n }\n\n return new CliError(\"Unknown error\", EXIT_CODES.ERROR);\n};\n","import { CliError, EXIT_CODES } from \"./errors.js\";\n\nexport interface OAuthTokenConfig {\n tokenUrl: string;\n clientId: string;\n}\n\nexport interface OAuthTokenResponse {\n access_token: string;\n refresh_token?: string;\n token_type: string;\n expires_in: number;\n}\n\nconst postTokenRequest = async (\n url: string,\n body: URLSearchParams\n): Promise<OAuthTokenResponse> => {\n const response = await fetch(url, {\n body: body.toString(),\n headers: { \"content-type\": \"application/x-www-form-urlencoded\" },\n method: \"POST\",\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new CliError(\n `OAuth token request failed (${response.status}): ${text}`,\n EXIT_CODES.AUTH_REQUIRED\n );\n }\n\n return (await response.json()) as OAuthTokenResponse;\n};\n\nexport const exchangeAuthorizationCode = (\n config: OAuthTokenConfig,\n code: string,\n codeVerifier: string,\n redirectUri: string\n): Promise<OAuthTokenResponse> => {\n const body = new URLSearchParams({\n client_id: config.clientId,\n code,\n code_verifier: codeVerifier,\n grant_type: \"authorization_code\",\n redirect_uri: redirectUri,\n });\n\n return postTokenRequest(config.tokenUrl, body);\n};\n\nexport const refreshAccessToken = (\n config: OAuthTokenConfig,\n refreshToken: string\n): Promise<OAuthTokenResponse> => {\n const body = new URLSearchParams({\n client_id: config.clientId,\n grant_type: \"refresh_token\",\n refresh_token: refreshToken,\n });\n\n return postTokenRequest(config.tokenUrl, body);\n};\n","import { mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\n\nimport { CONFIG_DIR, CREDENTIALS_FILE } from \"./constants.js\";\nimport { CliError, EXIT_CODES } from \"./errors.js\";\nimport type {\n ApiKeyCredentials,\n AuthFileData,\n StoredAuthSession,\n StoredSessionUser,\n} from \"./types.js\";\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value !== null;\n\nconst readNullableString = (value: unknown): string | null | undefined => {\n if (value === undefined || value === null) {\n return null;\n }\n\n return typeof value === \"string\" ? value : undefined;\n};\n\nconst parseStoredSessionUser = (\n value: unknown\n): StoredSessionUser | null | undefined => {\n if (value === undefined || value === null) {\n return null;\n }\n\n if (!isRecord(value) || typeof value.id !== \"string\") {\n return undefined;\n }\n\n const email = readNullableString(value.email);\n if (email === undefined) {\n return undefined;\n }\n\n return {\n email,\n id: value.id,\n };\n};\n\nconst parseStoredAuthSession = (value: unknown): StoredAuthSession | null => {\n if (!isRecord(value)) {\n return null;\n }\n\n if (typeof value.accessToken !== \"string\") {\n return null;\n }\n\n const refreshToken = readNullableString(value.refreshToken);\n if (refreshToken === undefined) {\n return null;\n }\n\n const expiresAt = readNullableString(value.expiresAt);\n if (expiresAt === undefined) {\n return null;\n }\n\n const user = parseStoredSessionUser(value.user);\n if (user === undefined) {\n return null;\n }\n\n if (typeof value.createdAt !== \"string\") {\n return null;\n }\n\n return {\n accessToken: value.accessToken,\n createdAt: value.createdAt,\n expiresAt,\n refreshToken,\n user,\n };\n};\n\nconst parseApiKeyCredentials = (value: unknown): ApiKeyCredentials | null => {\n if (!isRecord(value)) {\n return null;\n }\n\n if (typeof value.apiKey !== \"string\") {\n return null;\n }\n\n return { apiKey: value.apiKey, type: \"api-key\" };\n};\n\nconst createInvalidCredentialsError = (detail?: string): CliError =>\n new CliError(\n detail\n ? `Invalid credentials format in ${CREDENTIALS_FILE}: ${detail}`\n : `Invalid credentials format in ${CREDENTIALS_FILE}`,\n EXIT_CODES.ERROR\n );\n\nconst parseAuthFile = (raw: string): AuthFileData => {\n let parsed: unknown;\n\n try {\n parsed = JSON.parse(raw) as unknown;\n } catch {\n throw new CliError(\n `Invalid credentials JSON in ${CREDENTIALS_FILE}`,\n EXIT_CODES.ERROR\n );\n }\n\n if (!isRecord(parsed) || parsed.version !== 1) {\n throw createInvalidCredentialsError();\n }\n\n const hasSession = Object.hasOwn(parsed, \"session\");\n const hasApiKey = Object.hasOwn(parsed, \"apiKey\");\n const session =\n hasSession && parsed.session !== undefined\n ? parseStoredAuthSession(parsed.session)\n : undefined;\n const apiKey =\n hasApiKey && parsed.apiKey !== undefined\n ? parseApiKeyCredentials(parsed.apiKey)\n : undefined;\n\n if (hasSession && parsed.session !== undefined && !session) {\n throw createInvalidCredentialsError(\"stored session is malformed.\");\n }\n\n if (hasApiKey && parsed.apiKey !== undefined && !apiKey) {\n throw createInvalidCredentialsError(\"stored API key is malformed.\");\n }\n\n return {\n apiKey: apiKey ?? undefined,\n session: session ?? undefined,\n version: 1,\n };\n};\n\nexport const readAuthFile = async (): Promise<AuthFileData | null> => {\n try {\n const raw = await readFile(CREDENTIALS_FILE, \"utf8\");\n return parseAuthFile(raw);\n } catch (error) {\n if (isRecord(error) && error.code === \"ENOENT\") {\n return null;\n }\n\n if (error instanceof CliError) {\n throw error;\n }\n\n throw new CliError(\n `Failed to read credentials file at ${CREDENTIALS_FILE}`,\n EXIT_CODES.ERROR\n );\n }\n};\n\nexport const readStoredAuthSession =\n async (): Promise<StoredAuthSession | null> => {\n const data = await readAuthFile();\n return data?.session ?? null;\n };\n\nexport const readStoredApiKey = async (): Promise<ApiKeyCredentials | null> => {\n const data = await readAuthFile();\n return data?.apiKey ?? null;\n};\n\nconst writeAuthFile = async (data: AuthFileData): Promise<void> => {\n await mkdir(CONFIG_DIR, { mode: 0o700, recursive: true });\n await writeFile(CREDENTIALS_FILE, `${JSON.stringify(data, null, 2)}\\n`, {\n encoding: \"utf8\",\n mode: 0o600,\n });\n};\n\nexport const writeStoredAuthSession = async (\n session: StoredAuthSession\n): Promise<void> => {\n await writeAuthFile({\n session,\n version: 1,\n });\n};\n\nexport const writeStoredApiKey = async (\n apiKey: ApiKeyCredentials\n): Promise<void> => {\n await writeAuthFile({\n apiKey,\n version: 1,\n });\n};\n\nexport const clearStoredCredentials = async (): Promise<void> => {\n await rm(CREDENTIALS_FILE, { force: true });\n};\n","import { DEFAULT_SUPABASE_URL } from \"./constants.js\";\nimport { parseJwtClaims } from \"./jwt.js\";\nimport type { OAuthTokenResponse } from \"./oauth-token.js\";\nimport type { StoredAuthSession, SupabaseConfig } from \"./types.js\";\n\nexport const resolveSupabaseConfig = (): SupabaseConfig => {\n const url =\n process.env.SUPABASE_URL ??\n process.env.NEXT_PUBLIC_SUPABASE_URL ??\n DEFAULT_SUPABASE_URL;\n\n return { url };\n};\n\nexport const buildOAuthUrls = (\n config: SupabaseConfig\n): {\n authorizeUrl: string;\n tokenUrl: string;\n} => ({\n authorizeUrl: `${config.url}/auth/v1/oauth/authorize`,\n tokenUrl: `${config.url}/auth/v1/oauth/token`,\n});\n\nexport const tokenResponseToStoredSession = (\n response: OAuthTokenResponse\n): StoredAuthSession => {\n const claims = parseJwtClaims(response.access_token);\n\n let expiresAt: string | null = null;\n if (typeof claims?.exp === \"number\") {\n expiresAt = new Date(claims.exp * 1000).toISOString();\n } else if (response.expires_in > 0) {\n expiresAt = new Date(Date.now() + response.expires_in * 1000).toISOString();\n }\n\n return {\n accessToken: response.access_token,\n createdAt: new Date().toISOString(),\n expiresAt,\n refreshToken: response.refresh_token ?? null,\n user:\n claims?.sub || claims?.email\n ? {\n email: claims.email ?? null,\n id: claims.sub ?? \"unknown\",\n }\n : null,\n };\n};\n","import { BLODE_TOKEN_ENV, OAUTH_CLIENT_ID } from \"./constants.js\";\nimport { parseJwtClaims } from \"./jwt.js\";\nimport { refreshAccessToken } from \"./oauth-token.js\";\nimport {\n clearStoredCredentials,\n readAuthFile,\n writeStoredAuthSession,\n} from \"./storage.js\";\nimport {\n buildOAuthUrls,\n resolveSupabaseConfig,\n tokenResponseToStoredSession,\n} from \"./supabase.js\";\nimport type { ResolvedAuthToken, StoredAuthSession } from \"./types.js\";\n\nconst expiresInMs = (session: StoredAuthSession): number | null => {\n if (!session.expiresAt) {\n return null;\n }\n\n const expiresAtMs = Date.parse(session.expiresAt);\n\n if (Number.isNaN(expiresAtMs)) {\n return null;\n }\n\n return expiresAtMs - Date.now();\n};\n\nconst isExpired = (session: StoredAuthSession): boolean => {\n const ms = expiresInMs(session);\n return ms !== null && ms <= 0;\n};\n\nconst shouldRefresh = (session: StoredAuthSession): boolean => {\n const ms = expiresInMs(session);\n return ms !== null && ms <= 60_000;\n};\n\nconst tokenFromRaw = (\n token: string,\n source: ResolvedAuthToken[\"source\"]\n): ResolvedAuthToken => {\n const claims = parseJwtClaims(token);\n\n const expiresAt =\n typeof claims?.exp === \"number\"\n ? new Date(claims.exp * 1000).toISOString()\n : null;\n\n return {\n expiresAt,\n source,\n token,\n user:\n claims?.sub || claims?.email\n ? { email: claims.email ?? null, id: claims.sub ?? \"unknown\" }\n : null,\n };\n};\n\nconst sessionToResolvedToken = (\n session: StoredAuthSession\n): ResolvedAuthToken => ({\n expiresAt: session.expiresAt,\n source: \"stored\",\n token: session.accessToken,\n user: session.user,\n});\n\nexport const resolveAuthToken = async (\n optApiKey?: string\n): Promise<ResolvedAuthToken | null> => {\n const envToken = (optApiKey ?? process.env[BLODE_TOKEN_ENV])?.trim();\n\n if (envToken) {\n return tokenFromRaw(envToken, optApiKey ? \"flag\" : \"environment\");\n }\n\n const data = await readAuthFile();\n const session = data?.session;\n\n if (session) {\n if (!(shouldRefresh(session) || isExpired(session))) {\n return sessionToResolvedToken(session);\n }\n\n if (session.refreshToken) {\n try {\n const config = resolveSupabaseConfig();\n const { tokenUrl } = buildOAuthUrls(config);\n const tokenResponse = await refreshAccessToken(\n { clientId: OAUTH_CLIENT_ID, tokenUrl },\n session.refreshToken\n );\n const updatedSession = tokenResponseToStoredSession(tokenResponse);\n await writeStoredAuthSession(updatedSession);\n\n return sessionToResolvedToken(updatedSession);\n } catch {\n // Refresh failed — fall through to expiry check\n }\n }\n\n if (isExpired(session)) {\n await clearStoredCredentials();\n return null;\n }\n\n return sessionToResolvedToken(session);\n }\n\n if (data?.apiKey) {\n return {\n expiresAt: null,\n source: \"stored\",\n token: data.apiKey.apiKey,\n user: null,\n };\n }\n\n return null;\n};\n\nexport const resolveTokenStatus = (\n token: ResolvedAuthToken\n): {\n expiresInSeconds: number | null;\n expired: boolean;\n} => {\n if (!token.expiresAt) {\n return { expired: false, expiresInSeconds: null };\n }\n\n const expiresAtMs = Date.parse(token.expiresAt);\n\n if (Number.isNaN(expiresAtMs)) {\n return { expired: false, expiresInSeconds: null };\n }\n\n const expiresInSeconds = Math.floor((expiresAtMs - Date.now()) / 1000);\n\n return {\n expired: expiresInSeconds <= 0,\n expiresInSeconds,\n };\n};\n","import { CliError, EXIT_CODES } from \"./errors.js\";\n\nconst MAX_PORT = 65_535;\n\nexport const parsePositiveInteger = (value: string, label: string): number => {\n const trimmed = value.trim();\n\n if (!/^\\d+$/u.test(trimmed)) {\n throw new CliError(\n `${label} must be a positive integer.`,\n EXIT_CODES.VALIDATION\n );\n }\n\n const parsed = Number(trimmed);\n\n if (!Number.isSafeInteger(parsed) || parsed <= 0) {\n throw new CliError(\n `${label} must be a positive integer.`,\n EXIT_CODES.VALIDATION\n );\n }\n\n return parsed;\n};\n\nexport const parsePort = (value: string, label = \"Port\"): number => {\n const parsed = parsePositiveInteger(value, label);\n\n if (parsed > MAX_PORT) {\n throw new CliError(\n `${label} must be between 1 and ${MAX_PORT}.`,\n EXIT_CODES.VALIDATION\n );\n }\n\n return parsed;\n};\n","import type { SiteConfig } from \"@repo/models\";\nimport { createFsSource, loadSiteConfig } from \"@repo/previewing\";\n\nimport { CliError, EXIT_CODES } from \"./errors.js\";\n\nconst CONFIG_FILE = \"docs.json\";\n\nexport interface ValidatedSiteConfigResult {\n config: SiteConfig;\n warnings: string[];\n}\n\nconst getSiteConfigHint = (errors: string[]): string => {\n if (errors.includes(`${CONFIG_FILE} not found.`)) {\n return `Make sure ${CONFIG_FILE} exists in the selected docs directory or pass the docs directory explicitly.`;\n }\n\n return `Fix the ${CONFIG_FILE} errors above and try again.`;\n};\n\nexport const loadValidatedSiteConfig = async (\n root: string\n): Promise<ValidatedSiteConfigResult> => {\n const result = await loadSiteConfig(createFsSource(root));\n\n if (!result.ok) {\n throw new CliError(\n result.errors.join(\"\\n\"),\n EXIT_CODES.VALIDATION,\n getSiteConfigHint(result.errors)\n );\n }\n\n return {\n config: result.config,\n warnings: result.warnings,\n };\n};\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { loadValidatedSiteConfig } from \"../site-config.js\";\n\nconst CONFIG_FILE = \"docs.json\";\nconst ROOT_CANDIDATES = [\"\", \"docs\", path.join(\"apps\", \"docs\")];\nconst NESTED_DOCS_ROOT_CONTAINERS = [\n path.join(\"content\"),\n path.join(\"apps\", \"docs\", \"content\"),\n];\n\nconst fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const resolveDocsRoot = async (\n dir?: string,\n cwd: string = process.cwd()\n): Promise<string> => {\n const currentWorkingDir = path.resolve(cwd);\n\n if (dir) {\n return path.resolve(currentWorkingDir, dir);\n }\n\n const candidates = ROOT_CANDIDATES.map((candidate) =>\n path.join(currentWorkingDir, candidate)\n );\n\n for (const candidate of candidates) {\n if (await fileExists(path.join(candidate, CONFIG_FILE))) {\n return candidate;\n }\n }\n\n for (const container of NESTED_DOCS_ROOT_CONTAINERS) {\n const containerPath = path.join(currentWorkingDir, container);\n if (!(await fileExists(containerPath))) {\n continue;\n }\n\n const entries = await fs.readdir(containerPath, { withFileTypes: true });\n const docsRoots = entries\n .filter((entry) => entry.isDirectory())\n .map((entry) => path.join(containerPath, entry.name))\n .toSorted((left, right) => {\n const preferredNames = [\"docs\", \"example\"];\n const leftRank = preferredNames.indexOf(path.basename(left));\n const rightRank = preferredNames.indexOf(path.basename(right));\n\n if (leftRank !== rightRank) {\n if (leftRank === -1) {\n return 1;\n }\n\n if (rightRank === -1) {\n return -1;\n }\n\n return leftRank - rightRank;\n }\n\n return left.localeCompare(right);\n });\n\n for (const candidate of docsRoots) {\n if (await fileExists(path.join(candidate, CONFIG_FILE))) {\n return candidate;\n }\n }\n }\n\n return currentWorkingDir;\n};\n\nexport const validateDocsRoot = loadValidatedSiteConfig;\n","import path from \"node:path\";\n\nimport { log } from \"@clack/prompts\";\nimport { watch } from \"chokidar\";\n\nconst INVALIDATE_ENDPOINT = \"/blodemd-dev/invalidate\";\nconst WATCH_DEBOUNCE_MS = 100;\n\nconst normalizeRelativePath = (root: string, filePath: string) =>\n path.relative(root, filePath).split(path.sep).join(\"/\");\n\nconst isDirectoryEvent = (event: string) =>\n event === \"addDir\" || event === \"unlinkDir\";\n\nexport const createDevWatcher = ({\n port,\n root,\n}: {\n port: number;\n root: string;\n}) => {\n const watcher = watch(root, {\n ignoreInitial: true,\n ignored: [\"**/.git/**\", \"**/.next/**\", \"**/dist/**\", \"**/node_modules/**\"],\n });\n\n let flushTimer: NodeJS.Timeout | null = null;\n let pendingKind: \"config\" | \"content\" = \"content\";\n const pendingPaths = new Set<string>();\n\n const flush = async () => {\n flushTimer = null;\n\n const paths = [...pendingPaths];\n const kind = pendingKind;\n pendingPaths.clear();\n pendingKind = \"content\";\n\n if (!paths.length) {\n return;\n }\n\n try {\n const response = await fetch(\n `http://127.0.0.1:${port}${INVALIDATE_ENDPOINT}`,\n {\n body: JSON.stringify({ kind, paths }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n method: \"POST\",\n }\n );\n\n if (!response.ok) {\n throw new Error(`HTTP ${response.status}`);\n }\n } catch (error) {\n log.error(\n `Failed to invalidate preview cache: ${\n error instanceof Error ? error.message : \"unknown error\"\n }`\n );\n }\n };\n\n watcher.on(\"all\", (event, changedPath) => {\n if (isDirectoryEvent(event)) {\n return;\n }\n\n const relativePath = normalizeRelativePath(root, changedPath);\n pendingPaths.add(relativePath);\n\n if (path.basename(changedPath) === \"docs.json\") {\n pendingKind = \"config\";\n }\n\n if (flushTimer) {\n clearTimeout(flushTimer);\n }\n\n flushTimer = setTimeout(() => {\n flush();\n }, WATCH_DEBOUNCE_MS);\n });\n\n return {\n async close() {\n if (flushTimer) {\n clearTimeout(flushTimer);\n await flush();\n }\n\n await watcher.close();\n },\n };\n};\n","import { spawn } from \"node:child_process\";\nimport { once } from \"node:events\";\nimport fs from \"node:fs/promises\";\nimport { createRequire } from \"node:module\";\nimport { createServer } from \"node:net\";\nimport path from \"node:path\";\nimport { setTimeout as delay } from \"node:timers/promises\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { intro, log } from \"@clack/prompts\";\nimport chalk from \"chalk\";\nimport open from \"open\";\n\nimport { CONFIG_DIR } from \"../constants.js\";\nimport { CliError, EXIT_CODES, toCliError } from \"../errors.js\";\nimport { parsePort } from \"../validation.js\";\nimport { resolveDocsRoot, validateDocsRoot } from \"./resolve-root.js\";\nimport { createDevWatcher } from \"./watcher.js\";\n\nconst DEV_READY_ENDPOINT = \"/blodemd-dev/version\";\nconst DEV_READY_TIMEOUT_MS = 45_000;\nconst DEV_PORT_SCAN_LIMIT = 10;\nconst DEV_SHUTDOWN_TIMEOUT_MS = 5000;\nconst LOCALHOST = \"127.0.0.1\";\nconst RUNTIME_EXCLUDE_DIRS = new Set([\".next\", \".turbo\", \"node_modules\"]);\nconst STANDALONE_RUNTIME_MAX_AGE_MS = 24 * 60 * 60 * 1000;\nconst STANDALONE_RUNTIME_PREFIX = \"standalone-runtime-\";\nconst TURBOPACK_ARGS = [\"dev\", \"--turbopack\"] as const;\n\nconst fileExists = async (filePath: string): Promise<boolean> => {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n};\n\nconst resolveCommonAncestor = (pathsToCompare: string[]): string => {\n const [firstPath, ...restPaths] = pathsToCompare;\n if (!firstPath) {\n return path.sep;\n }\n\n const first = path.resolve(firstPath);\n const { root } = path.parse(first);\n const firstSegments = first\n .slice(root.length)\n .split(path.sep)\n .filter(Boolean);\n const sharedSegments: string[] = [];\n\n for (const [index, segment] of firstSegments.entries()) {\n const isShared = restPaths.every((candidatePath) => {\n const candidate = path.resolve(candidatePath);\n if (path.parse(candidate).root !== root) {\n return false;\n }\n\n const candidateSegments = candidate\n .slice(root.length)\n .split(path.sep)\n .filter(Boolean);\n return candidateSegments[index] === segment;\n });\n\n if (!isShared) {\n break;\n }\n\n sharedSegments.push(segment);\n }\n\n return path.join(root, ...sharedSegments);\n};\n\nconst cleanupStandaloneRuntimeRoots = async (\n configDir: string,\n maxAgeMs: number = STANDALONE_RUNTIME_MAX_AGE_MS\n): Promise<void> => {\n const cutoff = Date.now() - maxAgeMs;\n const entries = await fs.readdir(configDir, { withFileTypes: true });\n\n await Promise.all(\n entries\n .filter(\n (entry) =>\n entry.isDirectory() &&\n entry.name.startsWith(STANDALONE_RUNTIME_PREFIX)\n )\n .map(async (entry) => {\n const entryPath = path.join(configDir, entry.name);\n const stats = await fs.stat(entryPath);\n\n if (stats.mtimeMs >= cutoff) {\n return;\n }\n\n await fs.rm(entryPath, { force: true, recursive: true });\n })\n );\n};\n\ntype PortAvailabilityProbe = (port: number) => Promise<boolean>;\n\nconst probePortAvailability: PortAvailabilityProbe = async (port) => {\n const server = createServer();\n\n const listening = (async () => {\n await once(server, \"listening\");\n return { kind: \"listening\" as const };\n })();\n const errored = (async () => {\n const [error] = await once(server, \"error\");\n return {\n error: error as NodeJS.ErrnoException,\n kind: \"error\" as const,\n };\n })();\n\n server.listen({ exclusive: true, host: LOCALHOST, port });\n\n const outcome = await Promise.race([listening, errored]);\n\n if (outcome.kind === \"error\") {\n if (\n outcome.error.code === \"EADDRINUSE\" ||\n outcome.error.code === \"EACCES\"\n ) {\n return false;\n }\n\n throw outcome.error;\n }\n\n server.close();\n await once(server, \"close\");\n return true;\n};\n\nexport const resolveDevPort = async (\n requestedPort: number,\n probePort: PortAvailabilityProbe = probePortAvailability\n): Promise<number> => {\n for (let offset = 0; offset < DEV_PORT_SCAN_LIMIT; offset += 1) {\n const candidate = requestedPort + offset;\n if (candidate > 65_535) {\n break;\n }\n\n if (await probePort(candidate)) {\n return candidate;\n }\n }\n\n throw new CliError(\n `No available port found within ${DEV_PORT_SCAN_LIMIT} attempts starting at ${requestedPort}.`,\n EXIT_CODES.ERROR,\n \"Close the process using the port or pass a different --port value.\"\n );\n};\n\nexport const shutdownChildProcess = async (\n child: ReturnType<typeof spawn>,\n timeoutMs: number = DEV_SHUTDOWN_TIMEOUT_MS\n): Promise<void> => {\n if (child.exitCode !== null) {\n return;\n }\n\n const timer = setTimeout(() => {\n if (child.exitCode === null) {\n child.kill(\"SIGKILL\");\n }\n }, timeoutMs);\n\n const exitPromise = once(child, \"exit\");\n\n try {\n child.kill(\"SIGTERM\");\n } catch (error) {\n clearTimeout(timer);\n\n const killError = error as NodeJS.ErrnoException;\n if (killError.code === \"ESRCH\") {\n return;\n }\n\n throw error;\n }\n\n await exitPromise.finally(() => {\n clearTimeout(timer);\n });\n};\n\n// --- Dev-server resolution ---\n\ninterface StandaloneServer {\n mode: \"standalone\";\n devServerDir: string;\n nextPackageRoot: string;\n runtimeRoot: string;\n}\n\ninterface MonorepoServer {\n mode: \"monorepo\";\n repoRoot: string;\n}\n\ntype DevServerResolution = StandaloneServer | MonorepoServer;\n\n/**\n * Derive the CLI npm package root from the running script path.\n * The CLI entry point is at `<pkg-root>/dist/cli.mjs`.\n */\nconst resolveCliPackageRoot = (cliFilePath: string): string =>\n path.dirname(path.dirname(cliFilePath));\n\nconst copyStandaloneTree = async (\n sourceDir: string,\n targetDir: string\n): Promise<void> => {\n await fs.cp(sourceDir, targetDir, {\n filter: (source) => {\n const relative = path.relative(sourceDir, source);\n if (!relative) {\n return true;\n }\n\n const topSegment = relative.split(path.sep)[0] ?? \"\";\n return !RUNTIME_EXCLUDE_DIRS.has(topSegment);\n },\n recursive: true,\n });\n};\n\nconst isStandaloneCliInstall = async (\n cliPackageRoot: string\n): Promise<boolean> => {\n try {\n const realRoot = await fs.realpath(cliPackageRoot);\n return realRoot.split(path.sep).includes(\"node_modules\");\n } catch {\n return cliPackageRoot.split(path.sep).includes(\"node_modules\");\n }\n};\n\nexport const createStandaloneRuntimeRoot = async (\n configDir: string = CONFIG_DIR\n): Promise<string> => {\n await fs.mkdir(configDir, { recursive: true });\n await cleanupStandaloneRuntimeRoots(configDir);\n return await fs.mkdtemp(path.join(configDir, STANDALONE_RUNTIME_PREFIX));\n};\n\nconst materializeStandaloneRuntime = async (\n cliPackageRoot: string\n): Promise<{\n devServerDir: string;\n runtimeRoot: string;\n}> => {\n const runtimeRoot = await createStandaloneRuntimeRoot();\n\n try {\n for (const dir of [\"dev-server\", \"docs\", \"packages\"]) {\n await copyStandaloneTree(\n path.join(cliPackageRoot, dir),\n path.join(runtimeRoot, dir)\n );\n }\n\n await fs.symlink(\n path.join(cliPackageRoot, \"node_modules\"),\n path.join(runtimeRoot, \"node_modules\"),\n process.platform === \"win32\" ? \"junction\" : \"dir\"\n );\n await fs.mkdir(path.join(runtimeRoot, \"dev-server\", \"node_modules\"), {\n recursive: true,\n });\n await fs.symlink(\n path.join(runtimeRoot, \"packages\", \"@repo\"),\n path.join(runtimeRoot, \"dev-server\", \"node_modules\", \"@repo\"),\n process.platform === \"win32\" ? \"junction\" : \"dir\"\n );\n\n await fs.writeFile(\n path.join(runtimeRoot, \"dev-server\", \"package.json\"),\n `${JSON.stringify(\n {\n dependencies: {\n next: \"16.2.1\",\n react: \"^19.2.0\",\n \"react-dom\": \"^19.2.0\",\n },\n devDependencies: {\n \"@types/node\": \"^22.19.15\",\n \"@types/react\": \"19.2.14\",\n \"@types/react-dom\": \"19.2.3\",\n typescript: \"6.0.2\",\n },\n name: \"blodemd-dev-server\",\n private: true,\n type: \"module\",\n },\n null,\n 2\n )}\\n`\n );\n\n return {\n devServerDir: path.join(runtimeRoot, \"dev-server\"),\n runtimeRoot,\n };\n } catch (error) {\n await fs.rm(runtimeRoot, { force: true, recursive: true });\n throw error;\n }\n};\n\n/**\n * Check if a shipped dev-server exists alongside an installed CLI package.\n * We only use standalone mode when the package root lives under `node_modules`.\n */\nconst findStandaloneDevServer = async (\n cliPackageRoot: string\n): Promise<StandaloneServer | null> => {\n const devServerDir = path.join(cliPackageRoot, \"dev-server\");\n if (!(await fileExists(path.join(devServerDir, \"next.config.js\")))) {\n return null;\n }\n\n if (!(await isStandaloneCliInstall(cliPackageRoot))) {\n return null;\n }\n\n // Verify `next` is resolvable — this distinguishes npm-installed from\n // a monorepo checkout that happens to have dev-server/ from prepare-dist.\n try {\n createRequire(path.join(cliPackageRoot, \"package.json\")).resolve(\n \"next/package.json\"\n );\n } catch {\n return null;\n }\n\n const runtime = await materializeStandaloneRuntime(cliPackageRoot);\n\n return {\n devServerDir: runtime.devServerDir,\n mode: \"standalone\",\n nextPackageRoot: cliPackageRoot,\n runtimeRoot: runtime.runtimeRoot,\n };\n};\n\n/**\n * Resolve the `next` CLI binary from the blodemd package's own dependencies.\n */\nconst resolveNextBin = (cliPackageRoot: string): string => {\n const require = createRequire(path.join(cliPackageRoot, \"package.json\"));\n const nextPkgPath = require.resolve(\"next/package.json\");\n return path.join(path.dirname(nextPkgPath), \"dist\", \"bin\", \"next\");\n};\n\nconst findMonorepoRoot = async (start: string): Promise<string> => {\n let current = start;\n\n while (true) {\n const packageJsonPath = path.join(current, \"package.json\");\n if (await fileExists(packageJsonPath)) {\n const raw = await fs.readFile(packageJsonPath, \"utf8\");\n const parsed = JSON.parse(raw) as { workspaces?: string[] };\n const workspaces = parsed.workspaces ?? [];\n\n if (workspaces.includes(\"apps/*\") && workspaces.includes(\"packages/*\")) {\n return current;\n }\n }\n\n const parent = path.dirname(current);\n if (parent === current) {\n break;\n }\n current = parent;\n }\n\n throw new CliError(\n \"Could not locate the blodemd dev server.\",\n EXIT_CODES.ERROR,\n \"Make sure blodemd is installed correctly (npm i blodemd).\"\n );\n};\n\nconst resolveDevServer = async (\n cliFilePath: string\n): Promise<DevServerResolution> => {\n const cliPackageRoot = resolveCliPackageRoot(cliFilePath);\n\n // Try standalone mode first (npm-installed)\n const standalone = await findStandaloneDevServer(cliPackageRoot);\n if (standalone) {\n return standalone;\n }\n\n // Fall back to monorepo mode (development)\n const repoRoot = await findMonorepoRoot(path.dirname(cliFilePath));\n return { mode: \"monorepo\", repoRoot };\n};\n\ninterface DevServerLaunch {\n args: string[];\n command: string;\n cwd: string;\n env: NodeJS.ProcessEnv;\n}\n\nexport const buildDevServerLaunch = (\n server: DevServerResolution,\n { root, port }: { root: string; port: number }\n): DevServerLaunch => {\n if (server.mode === \"standalone\") {\n const nextBin = resolveNextBin(server.nextPackageRoot);\n\n return {\n args: [nextBin, ...TURBOPACK_ARGS],\n command: process.execPath,\n cwd: server.devServerDir,\n env: {\n ...process.env,\n BLODEMD_PACKAGES_DIR: path.join(server.runtimeRoot, \"packages\"),\n BLODEMD_TURBOPACK_ROOT: resolveCommonAncestor([\n server.nextPackageRoot,\n server.runtimeRoot,\n ]),\n DOCS_ROOT: root,\n PORT: String(port),\n },\n };\n }\n\n const npmCommand = process.platform === \"win32\" ? \"npm.cmd\" : \"npm\";\n return {\n args: [\"run\", \"dev\", \"--workspace=dev-server\"],\n command: npmCommand,\n cwd: server.repoRoot,\n env: {\n ...process.env,\n DOCS_ROOT: root,\n PORT: String(port),\n },\n };\n};\n\nconst spawnDevServer = (\n server: DevServerResolution,\n options: { root: string; port: number }\n): ReturnType<typeof spawn> => {\n const launch = buildDevServerLaunch(server, options);\n\n return spawn(launch.command, launch.args, {\n cwd: launch.cwd,\n env: launch.env,\n stdio: \"inherit\",\n });\n};\n\ninterface DevCommandDependencies {\n createWatcher: typeof createDevWatcher;\n getCliFilePath: () => string;\n getIntro: typeof intro;\n getOpen: typeof open;\n getLog: typeof log;\n parsePortValue: typeof parsePort;\n removeDirectory: typeof fs.rm;\n resolveDevPortValue: typeof resolveDevPort;\n resolveDocsRootValue: typeof resolveDocsRoot;\n resolveServer: typeof resolveDevServer;\n shutdownChild: typeof shutdownChildProcess;\n spawnServer: typeof spawnDevServer;\n validateDocsRootValue: typeof validateDocsRoot;\n waitForServerReady: typeof waitForServer;\n}\n\n// --- Server readiness ---\n\nconst waitForServer = async ({\n child,\n port,\n}: {\n child: ReturnType<typeof spawn>;\n port: number;\n}) => {\n const url = `http://localhost:${port}${DEV_READY_ENDPOINT}`;\n const startedAt = Date.now();\n\n while (Date.now() - startedAt < DEV_READY_TIMEOUT_MS) {\n if (child.exitCode !== null) {\n throw new CliError(\n \"The local dev server exited before it became ready.\",\n EXIT_CODES.ERROR\n );\n }\n\n try {\n const response = await fetch(url, {\n cache: \"no-store\",\n headers: {\n accept: \"application/json\",\n },\n });\n\n if (response.ok) {\n return;\n }\n } catch {\n // Server is still starting.\n }\n\n await delay(500);\n }\n\n throw new CliError(\n \"Timed out waiting for the local dev server to start.\",\n EXIT_CODES.ERROR\n );\n};\n\nconst defaultDevCommandDependencies: DevCommandDependencies = {\n createWatcher: createDevWatcher,\n getCliFilePath: () => fileURLToPath(import.meta.url),\n getIntro: intro,\n getLog: log,\n getOpen: open,\n parsePortValue: parsePort,\n removeDirectory: fs.rm,\n resolveDevPortValue: resolveDevPort,\n resolveDocsRootValue: resolveDocsRoot,\n resolveServer: resolveDevServer,\n shutdownChild: shutdownChildProcess,\n spawnServer: spawnDevServer,\n validateDocsRootValue: validateDocsRoot,\n waitForServerReady: waitForServer,\n};\n\n// --- Main command ---\n\nexport const devCommand = async (\n {\n dir,\n openBrowser,\n port: portValue,\n }: {\n dir?: string;\n openBrowser: boolean;\n port: string;\n },\n dependencies: DevCommandDependencies = defaultDevCommandDependencies\n) => {\n const cliLog = dependencies.getLog;\n\n dependencies.getIntro(chalk.bold(\"blodemd dev\"));\n\n try {\n const port = dependencies.parsePortValue(portValue);\n const resolvedPort = await dependencies.resolveDevPortValue(port);\n const root = await dependencies.resolveDocsRootValue(dir);\n await dependencies.validateDocsRootValue(root);\n\n const cliFilePath = dependencies.getCliFilePath();\n const server = await dependencies.resolveServer(cliFilePath);\n const localUrl = `http://localhost:${resolvedPort}`;\n\n cliLog.info(`Docs root: ${chalk.cyan(root)}`);\n\n const child = dependencies.spawnServer(server, {\n port: resolvedPort,\n root,\n });\n\n let watcher: Awaited<ReturnType<typeof createDevWatcher>> | null = null;\n let shuttingDown = false;\n\n const closeAll = async () => {\n if (shuttingDown) {\n return;\n }\n shuttingDown = true;\n\n if (watcher) {\n await watcher.close();\n watcher = null;\n }\n\n await dependencies.shutdownChild(child);\n\n if (server.mode === \"standalone\") {\n await dependencies.removeDirectory(server.runtimeRoot, {\n force: true,\n recursive: true,\n });\n }\n };\n\n process.once(\"SIGINT\", closeAll);\n process.once(\"SIGTERM\", closeAll);\n\n try {\n await dependencies.waitForServerReady({ child, port: resolvedPort });\n\n watcher = await dependencies.createWatcher({ port: resolvedPort, root });\n cliLog.success(`Dev server running at ${chalk.cyan(localUrl)}`);\n\n if (openBrowser) {\n await dependencies.getOpen(localUrl);\n }\n\n const [code, signal] = (await once(child, \"exit\")) as [\n number | null,\n NodeJS.Signals | null,\n ];\n\n if (shuttingDown || signal === \"SIGINT\" || signal === \"SIGTERM\") {\n return;\n }\n\n if (code !== 0) {\n throw new CliError(\n `The local dev server exited with code ${code ?? \"unknown\"}.`,\n EXIT_CODES.ERROR\n );\n }\n } finally {\n await closeAll();\n process.removeListener(\"SIGINT\", closeAll);\n process.removeListener(\"SIGTERM\", closeAll);\n }\n } catch (error) {\n const cliError = toCliError(error);\n\n cliLog.error(cliError.message);\n if (cliError.hint) {\n cliLog.info(cliError.hint);\n }\n\n process.exitCode = cliError.exitCode;\n }\n};\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nexport const writeFileIfMissing = async (\n filePath: string,\n content: string\n): Promise<void> => {\n try {\n await fs.writeFile(filePath, content, { flag: \"wx\" });\n } catch (error) {\n const { code } = error as NodeJS.ErrnoException;\n\n if (code === \"EEXIST\") {\n const existing = await fs.stat(filePath).catch(() => null);\n\n if (existing?.isFile()) {\n return;\n }\n }\n\n throw error;\n }\n};\n\nexport const writeSymlinkIfMissing = async (\n filePath: string,\n target: string,\n options?: {\n fallbackContent?: string;\n lstat?: typeof fs.lstat;\n symlink?: typeof fs.symlink;\n }\n): Promise<void> => {\n const lstat = options?.lstat ?? fs.lstat;\n const symlink = options?.symlink ?? fs.symlink;\n\n try {\n await symlink(target, filePath);\n } catch (error) {\n const { code } = error as NodeJS.ErrnoException;\n\n if (code === \"EEXIST\") {\n const existing = await lstat(filePath).catch(() => null);\n\n if (existing?.isFile() || existing?.isSymbolicLink()) {\n return;\n }\n }\n\n if (\n options?.fallbackContent &&\n (code === \"EINVAL\" ||\n code === \"ENOTSUP\" ||\n code === \"EPERM\" ||\n code === \"UNKNOWN\")\n ) {\n await writeFileIfMissing(filePath, options.fallbackContent);\n return;\n }\n\n throw error;\n }\n};\n\nexport const findExistingPaths = async (\n root: string,\n relativePaths: string[]\n): Promise<string[]> => {\n const existingPaths = await Promise.all(\n relativePaths.map(async (relativePath) => {\n const stats = await fs\n .lstat(path.join(root, relativePath))\n .catch(() => null);\n\n return stats ? relativePath : null;\n })\n );\n\n return existingPaths\n .filter((relativePath): relativePath is string => relativePath !== null)\n .toSorted((left, right) => left.localeCompare(right));\n};\n","import path from \"node:path\";\n\nimport { slugify } from \"@repo/common\";\n\nexport const SCAFFOLD_TEMPLATES = [\"minimal\", \"starter\"] as const;\nexport type ScaffoldTemplate = (typeof SCAFFOLD_TEMPLATES)[number];\nexport const DEFAULT_SCAFFOLD_DIRECTORY = \"docs\";\nexport const DEFAULT_PROJECT_SLUG = \"my-project\";\n\ninterface BaseScaffoldFile {\n path: string;\n}\n\ninterface TextScaffoldFile extends BaseScaffoldFile {\n content: string;\n type?: \"file\";\n}\n\ninterface SymlinkScaffoldFile extends BaseScaffoldFile {\n fallbackContent: string;\n target: string;\n type: \"symlink\";\n}\n\nexport type ScaffoldFile = TextScaffoldFile | SymlinkScaffoldFile;\n\nconst stringifyJson = (value: unknown): string =>\n `${JSON.stringify(value, null, 2)}\\n`;\n\nexport const isScaffoldTemplate = (value: string): value is ScaffoldTemplate =>\n SCAFFOLD_TEMPLATES.includes(value as ScaffoldTemplate);\n\nconst normalizeProjectSlug = (value: string) =>\n slugify(value) || DEFAULT_PROJECT_SLUG;\n\nexport const resolveScaffoldDirectory = (directory?: string) =>\n directory?.trim() || DEFAULT_SCAFFOLD_DIRECTORY;\n\nexport const deriveDefaultProjectSlug = (\n directory: string | undefined,\n cwd: string\n) => {\n const resolvedDirectory = resolveScaffoldDirectory(directory);\n\n if (\n resolvedDirectory === \".\" ||\n resolvedDirectory === DEFAULT_SCAFFOLD_DIRECTORY\n ) {\n return normalizeProjectSlug(path.basename(cwd));\n }\n\n return normalizeProjectSlug(\n path.basename(path.resolve(cwd, resolvedDirectory))\n );\n};\n\nexport const validateProjectSlug = (value: string | undefined) => {\n const trimmed = value?.trim();\n if (!trimmed) {\n return \"Project slug is required.\";\n }\n\n const normalized = slugify(trimmed);\n if (!normalized) {\n return \"Use at least one letter or number.\";\n }\n\n if (normalized !== trimmed) {\n return `Use lowercase letters, numbers, and hyphens. Try \"${normalized}\".`;\n }\n};\n\nconst createMinimalDocsJson = (projectSlug: string) => ({\n $schema: \"https://blode.md/docs.json\",\n name: projectSlug,\n navigation: {\n groups: [{ group: \"Getting Started\", pages: [\"index\"] }],\n },\n});\n\nconst createStarterDocsJson = (projectSlug: string) => ({\n $schema: \"https://blode.md/docs.json\",\n appearance: { default: \"system\" },\n contextual: {\n options: [\"copy\", \"view\", \"chatgpt\", \"claude\"],\n },\n description: \"Ship documentation from your terminal.\",\n favicon: \"/favicon.svg\",\n logo: {\n alt: `${projectSlug} logo`,\n dark: \"/logo/dark.svg\",\n light: \"/logo/light.svg\",\n },\n metadata: { timestamp: true },\n name: projectSlug,\n navigation: {\n groups: [\n {\n group: \"Getting Started\",\n pages: [\"index\", \"quickstart\", \"development\"],\n },\n ],\n },\n});\n\nconst claudeInstructions = [\n \"> **First-time setup**: Customize this file for your project. Prompt the user to update terminology, style preferences, and content boundaries before drafting large amounts of docs.\",\n \"\",\n \"# Documentation project instructions\",\n \"\",\n \"## About this project\",\n \"\",\n \"- This is a documentation site built on [Blode.md](https://blode.md)\",\n \"- Pages are MDX files with YAML frontmatter\",\n \"- Configuration lives in `docs.json`\",\n \"- Run `blodemd dev` to preview locally\",\n \"- Run `blodemd validate` before publishing\",\n \"- Run `blodemd push` to deploy\",\n \"\",\n \"## Terminology\",\n \"\",\n \"{/* Add product-specific terms and preferred usage */}\",\n '{/* Example: Use \"workspace\" not \"project\", \"member\" not \"user\" */}',\n \"\",\n \"## Style preferences\",\n \"\",\n \"{/* Add any project-specific style rules below */}\",\n \"\",\n '- Use active voice and second person (\"you\")',\n \"- Keep sentences concise and task-oriented\",\n \"- Use sentence case for headings\",\n \"- Bold UI labels: Click **Settings**\",\n \"- Use code formatting for file names, commands, paths, JSON fields, and code references\",\n \"\",\n \"## Content boundaries\",\n \"\",\n \"{/* Define what should and shouldn't be documented */}\",\n \"{/* Example: Don't document internal admin features */}\",\n \"\",\n \"## Workflow reminders\",\n \"\",\n \"- Content lives in MDX files next to `docs.json`.\",\n \"- Update `docs.json` when navigation or branding changes.\",\n \"- Prefer concise, task-oriented documentation.\",\n \"- Run `blodemd validate` before publishing.\",\n \"\",\n].join(\"\\n\");\n\nconst createMinimalFiles = (projectSlug: string): ScaffoldFile[] => [\n {\n content: stringifyJson(createMinimalDocsJson(projectSlug)),\n path: \"docs.json\",\n },\n {\n content: \"---\\ntitle: Welcome\\n---\\n\\nStart writing your docs here.\\n\",\n path: \"index.mdx\",\n },\n];\n\nconst createStarterFiles = (projectSlug: string): ScaffoldFile[] => [\n {\n content: stringifyJson(createStarterDocsJson(projectSlug)),\n path: \"docs.json\",\n },\n {\n content: [\n \"---\",\n \"title: Welcome\",\n \"description: Start here.\",\n \"---\",\n \"\",\n \"# Welcome\",\n \"\",\n \"This starter gives you branded assets, repo helper files, and a small docs structure you can rewrite quickly.\",\n \"\",\n \"\",\n \"\",\n \"## What is included\",\n \"\",\n \"- A starter `docs.json` with branding, contextual actions, and navigation.\",\n \"- Placeholder brand assets in `/logo` and `/images`.\",\n \"- Repo helper files like `.gitignore`, `README.md`, `AGENTS.md`, and `CLAUDE.md`.\",\n \"\",\n \"## Next steps\",\n \"\",\n \"- Confirm the `name` field in `docs.json` matches your project slug.\",\n \"- Set `description` in `docs.json` to explain your product.\",\n \"- Replace the files in `/logo` and `/images` with your own brand assets.\",\n \"- Rewrite `CLAUDE.md` with your terminology and writing standards.\",\n \"- Update this page, then preview locally with `blodemd dev`.\",\n \"\",\n \"## Included pages\",\n \"\",\n \"- [Quickstart](quickstart)\",\n \"- [Development](development)\",\n \"\",\n ].join(\"\\n\"),\n path: \"index.mdx\",\n },\n {\n content: [\n \"---\",\n \"title: Quickstart\",\n \"description: Get your docs running fast.\",\n \"---\",\n \"\",\n \"# Quickstart\",\n \"\",\n \"\",\n \"\",\n \"1. Confirm the `name` field in `docs.json`.\",\n \"2. Update the `description` field to match your product.\",\n \"3. Replace the assets in `/logo` and `/images`.\",\n \"4. Run `blodemd dev` to preview locally.\",\n \"5. Run `blodemd push` when you are ready to publish.\",\n \"\",\n ].join(\"\\n\"),\n path: \"quickstart.mdx\",\n },\n {\n content: [\n \"---\",\n \"title: Development\",\n \"description: Work on your docs locally.\",\n \"---\",\n \"\",\n \"# Development\",\n \"\",\n \"\",\n \"\",\n \"Preview locally with:\",\n \"\",\n \"```bash\",\n \"blodemd dev\",\n \"```\",\n \"\",\n \"Validate your configuration with:\",\n \"\",\n \"```bash\",\n \"blodemd validate\",\n \"```\",\n \"\",\n \"Keep `CLAUDE.md` current as your product terminology and writing rules evolve.\",\n \"\",\n ].join(\"\\n\"),\n path: \"development.mdx\",\n },\n {\n content: [\n \"# Documentation starter\",\n \"\",\n \"This directory was scaffolded with `blodemd new --template starter`.\",\n \"\",\n \"## What is included\",\n \"\",\n \"- `docs.json` with branding, contextual actions, and starter navigation\",\n \"- `index.mdx`, `quickstart.mdx`, and `development.mdx`\",\n \"- Placeholder brand assets in `/logo` and `/images`\",\n \"- Repo helper files: `.gitignore`, `README.md`, `AGENTS.md`, and `CLAUDE.md`\",\n \"\",\n \"## Commands\",\n \"\",\n \"```bash\",\n \"blodemd dev\",\n \"blodemd validate\",\n \"blodemd push\",\n \"```\",\n \"\",\n \"## Customize\",\n \"\",\n \"- Confirm the project slug and set the description in `docs.json`.\",\n \"- Replace the assets in `/logo` and `/images`.\",\n \"- Rewrite `CLAUDE.md` with project-specific terminology and writing rules.\",\n \"- Rewrite the starter pages to match your product.\",\n \"- Add a `LICENSE` file deliberately if this repo will be public.\",\n \"\",\n ].join(\"\\n\"),\n path: \"README.md\",\n },\n {\n fallbackContent: claudeInstructions,\n path: \"AGENTS.md\",\n target: \"CLAUDE.md\",\n type: \"symlink\",\n },\n {\n content: claudeInstructions,\n path: \"CLAUDE.md\",\n },\n {\n content: [\n \"# dependencies\",\n \"node_modules/\",\n \"\",\n \"# local env files\",\n \".env*\",\n \"!.env.example\",\n \"\",\n \"# build and cache\",\n \".next/\",\n \".turbo/\",\n \"coverage/\",\n \"dist/\",\n \".vercel/\",\n \"*.tsbuildinfo\",\n \"\",\n \"# logs\",\n \"*.log\",\n \"\",\n \"# misc\",\n \".DS_Store\",\n \"\",\n ].join(\"\\n\"),\n path: \".gitignore\",\n },\n {\n content: [\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 64 64\" fill=\"none\">',\n ' <rect width=\"64\" height=\"64\" rx=\"16\" fill=\"#0D9373\"/>',\n ' <path d=\"M20 18h14c8.837 0 16 7.163 16 16s-7.163 16-16 16H20V18Z\" fill=\"#CFF6EE\"/>',\n ' <path d=\"M28 26h6c5.523 0 10 4.477 10 10s-4.477 10-10 10h-6V26Z\" fill=\"#0C3A33\"/>',\n \"</svg>\",\n \"\",\n ].join(\"\\n\"),\n path: \"favicon.svg\",\n },\n {\n content: [\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 240 64\" fill=\"none\">',\n ' <rect width=\"64\" height=\"64\" rx=\"16\" fill=\"#0C3A33\"/>',\n ' <path d=\"M20 18h14c8.837 0 16 7.163 16 16s-7.163 16-16 16H20V18Z\" fill=\"#CFF6EE\"/>',\n ` <text x=\"84\" y=\"41\" fill=\"#111827\" font-family=\"Arial, sans-serif\" font-size=\"28\" font-weight=\"700\">${projectSlug}</text>`,\n \"</svg>\",\n \"\",\n ].join(\"\\n\"),\n path: \"logo/light.svg\",\n },\n {\n content: [\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 240 64\" fill=\"none\">',\n ' <rect width=\"64\" height=\"64\" rx=\"16\" fill=\"#CFF6EE\"/>',\n ' <path d=\"M20 18h14c8.837 0 16 7.163 16 16s-7.163 16-16 16H20V18Z\" fill=\"#0C3A33\"/>',\n ` <text x=\"84\" y=\"41\" fill=\"#F9FAFB\" font-family=\"Arial, sans-serif\" font-size=\"28\" font-weight=\"700\">${projectSlug}</text>`,\n \"</svg>\",\n \"\",\n ].join(\"\\n\"),\n path: \"logo/dark.svg\",\n },\n {\n content: [\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 960 520\" fill=\"none\">',\n ' <rect width=\"960\" height=\"520\" rx=\"32\" fill=\"#F4FBF8\"/>',\n ' <rect x=\"48\" y=\"48\" width=\"260\" height=\"424\" rx=\"24\" fill=\"#E1F4EE\"/>',\n ' <rect x=\"96\" y=\"120\" width=\"164\" height=\"20\" rx=\"10\" fill=\"#0D9373\" opacity=\".25\"/>',\n ' <rect x=\"96\" y=\"164\" width=\"132\" height=\"16\" rx=\"8\" fill=\"#0D9373\" opacity=\".18\"/>',\n ' <rect x=\"96\" y=\"204\" width=\"152\" height=\"16\" rx=\"8\" fill=\"#0D9373\" opacity=\".18\"/>',\n ' <rect x=\"356\" y=\"80\" width=\"556\" height=\"104\" rx=\"24\" fill=\"#0D9373\"/>',\n ' <rect x=\"388\" y=\"116\" width=\"220\" height=\"18\" rx=\"9\" fill=\"#CFF6EE\"/>',\n ' <rect x=\"388\" y=\"148\" width=\"156\" height=\"14\" rx=\"7\" fill=\"#CFF6EE\" opacity=\".7\"/>',\n ' <rect x=\"356\" y=\"216\" width=\"268\" height=\"256\" rx=\"24\" fill=\"#FFFFFF\"/>',\n ' <rect x=\"388\" y=\"260\" width=\"168\" height=\"16\" rx=\"8\" fill=\"#0C3A33\" opacity=\".18\"/>',\n ' <rect x=\"388\" y=\"292\" width=\"196\" height=\"16\" rx=\"8\" fill=\"#0C3A33\" opacity=\".12\"/>',\n ' <rect x=\"656\" y=\"216\" width=\"256\" height=\"256\" rx=\"24\" fill=\"#0C3A33\"/>',\n ' <rect x=\"692\" y=\"260\" width=\"128\" height=\"16\" rx=\"8\" fill=\"#CFF6EE\" opacity=\".85\"/>',\n ' <rect x=\"692\" y=\"292\" width=\"152\" height=\"16\" rx=\"8\" fill=\"#CFF6EE\" opacity=\".45\"/>',\n ' <circle cx=\"804\" cy=\"380\" r=\"52\" fill=\"#0D9373\"/>',\n \"</svg>\",\n \"\",\n ].join(\"\\n\"),\n path: \"images/hero-light.svg\",\n },\n {\n content: [\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 960 520\" fill=\"none\">',\n ' <rect width=\"960\" height=\"520\" rx=\"32\" fill=\"#071715\"/>',\n ' <rect x=\"48\" y=\"48\" width=\"260\" height=\"424\" rx=\"24\" fill=\"#0F2E28\"/>',\n ' <rect x=\"96\" y=\"120\" width=\"164\" height=\"20\" rx=\"10\" fill=\"#CFF6EE\" opacity=\".18\"/>',\n ' <rect x=\"96\" y=\"164\" width=\"132\" height=\"16\" rx=\"8\" fill=\"#CFF6EE\" opacity=\".14\"/>',\n ' <rect x=\"96\" y=\"204\" width=\"152\" height=\"16\" rx=\"8\" fill=\"#CFF6EE\" opacity=\".14\"/>',\n ' <rect x=\"356\" y=\"80\" width=\"556\" height=\"104\" rx=\"24\" fill=\"#0D9373\"/>',\n ' <rect x=\"388\" y=\"116\" width=\"220\" height=\"18\" rx=\"9\" fill=\"#E8FFF9\"/>',\n ' <rect x=\"388\" y=\"148\" width=\"156\" height=\"14\" rx=\"7\" fill=\"#E8FFF9\" opacity=\".6\"/>',\n ' <rect x=\"356\" y=\"216\" width=\"268\" height=\"256\" rx=\"24\" fill=\"#0C3A33\"/>',\n ' <rect x=\"388\" y=\"260\" width=\"168\" height=\"16\" rx=\"8\" fill=\"#CFF6EE\" opacity=\".22\"/>',\n ' <rect x=\"388\" y=\"292\" width=\"196\" height=\"16\" rx=\"8\" fill=\"#CFF6EE\" opacity=\".16\"/>',\n ' <rect x=\"656\" y=\"216\" width=\"256\" height=\"256\" rx=\"24\" fill=\"#E9FFF8\"/>',\n ' <rect x=\"692\" y=\"260\" width=\"128\" height=\"16\" rx=\"8\" fill=\"#0C3A33\" opacity=\".24\"/>',\n ' <rect x=\"692\" y=\"292\" width=\"152\" height=\"16\" rx=\"8\" fill=\"#0C3A33\" opacity=\".12\"/>',\n ' <circle cx=\"804\" cy=\"380\" r=\"52\" fill=\"#0D9373\"/>',\n \"</svg>\",\n \"\",\n ].join(\"\\n\"),\n path: \"images/hero-dark.svg\",\n },\n {\n content: [\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 960 520\" fill=\"none\">',\n ' <rect width=\"960\" height=\"520\" rx=\"32\" fill=\"#F8FCFA\"/>',\n ' <rect x=\"60\" y=\"76\" width=\"840\" height=\"368\" rx=\"28\" fill=\"#FFFFFF\" stroke=\"#D7ECE6\" stroke-width=\"4\"/>',\n ' <rect x=\"108\" y=\"124\" width=\"96\" height=\"96\" rx=\"24\" fill=\"#0D9373\"/>',\n ' <path d=\"M136 172l18 18 38-48\" stroke=\"#CFF6EE\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"18\"/>',\n ' <rect x=\"244\" y=\"132\" width=\"280\" height=\"24\" rx=\"12\" fill=\"#0C3A33\"/>',\n ' <rect x=\"244\" y=\"176\" width=\"416\" height=\"18\" rx=\"9\" fill=\"#0C3A33\" opacity=\".16\"/>',\n ' <rect x=\"244\" y=\"214\" width=\"340\" height=\"18\" rx=\"9\" fill=\"#0C3A33\" opacity=\".12\"/>',\n ' <rect x=\"108\" y=\"280\" width=\"744\" height=\"22\" rx=\"11\" fill=\"#0D9373\" opacity=\".12\"/>',\n ' <rect x=\"108\" y=\"326\" width=\"520\" height=\"22\" rx=\"11\" fill=\"#0D9373\" opacity=\".12\"/>',\n ' <rect x=\"108\" y=\"372\" width=\"612\" height=\"22\" rx=\"11\" fill=\"#0D9373\" opacity=\".12\"/>',\n \"</svg>\",\n \"\",\n ].join(\"\\n\"),\n path: \"images/checks-passed.svg\",\n },\n];\n\nexport const getScaffoldFiles = (\n template: ScaffoldTemplate,\n options?: {\n projectSlug?: string;\n }\n): ScaffoldFile[] => {\n const projectSlug = options?.projectSlug ?? DEFAULT_PROJECT_SLUG;\n return template === \"starter\"\n ? createStarterFiles(projectSlug)\n : createMinimalFiles(projectSlug);\n};\n","import { DEFAULT_SCAFFOLD_DIRECTORY } from \"./scaffold.js\";\n\nexport const CREATE_IN_SUBDIRECTORY = \"create-in-subdirectory\";\nexport const SCAFFOLD_CURRENT_DIRECTORY = \"scaffold-current-directory\";\nexport const CANCEL_SCAFFOLD = \"cancel\";\n\nexport type NoArgInteractiveAction =\n | typeof CREATE_IN_SUBDIRECTORY\n | typeof SCAFFOLD_CURRENT_DIRECTORY\n | typeof CANCEL_SCAFFOLD;\n\nexport type InitialDirectoryResolution =\n | {\n directory: string;\n kind: \"target\";\n }\n | {\n kind: \"prompt\";\n };\n\nexport const resolveInitialDirectory = (options: {\n currentDirectoryEntries: readonly string[];\n directory?: string;\n interactive: boolean;\n}): InitialDirectoryResolution => {\n if (options.directory) {\n return { directory: options.directory, kind: \"target\" };\n }\n\n if (!options.interactive) {\n return { directory: DEFAULT_SCAFFOLD_DIRECTORY, kind: \"target\" };\n }\n\n if (options.currentDirectoryEntries.length === 0) {\n return { directory: \".\", kind: \"target\" };\n }\n\n return { kind: \"prompt\" };\n};\n\nexport const resolveDirectoryFromAction = (\n action: NoArgInteractiveAction,\n subdirectory?: string\n): string | undefined => {\n if (action === SCAFFOLD_CURRENT_DIRECTORY) {\n return \".\";\n }\n\n if (action === CREATE_IN_SUBDIRECTORY) {\n return subdirectory?.trim() || DEFAULT_SCAFFOLD_DIRECTORY;\n }\n};\n","// oxlint-disable no-use-before-define -- circular reference in callback pattern\nimport { createServer } from \"node:http\";\nimport type { Socket } from \"node:net\";\n\nimport { CliError, EXIT_CODES } from \"./errors.js\";\n\ninterface OAuthCallbackOptions {\n redirectUrl: URL;\n expectedState: string;\n timeoutMs: number;\n}\n\nconst SUCCESS_HTML =\n '<!doctype html><html><head><meta charset=\"utf-8\"/><title>Blode.md CLI</title></head><body><h2>Logged in! You can close this tab.</h2></body></html>';\n\nconst escapeHtml = (text: string): string =>\n text\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\");\n\nconst errorHtml = (message: string): string =>\n `<!doctype html><html><head><meta charset=\"utf-8\"/><title>Blode.md CLI</title></head><body><h2>Login failed</h2><p>${escapeHtml(message)}</p></body></html>`;\n\nexport const waitForOAuthCode = (\n options: OAuthCallbackOptions\n): Promise<string> => {\n const host = options.redirectUrl.hostname;\n const port = Number(options.redirectUrl.port);\n const { pathname } = options.redirectUrl;\n\n if (!Number.isInteger(port) || port <= 0) {\n return Promise.reject(\n new CliError(\n \"OAuth redirect URL requires an explicit port\",\n EXIT_CODES.ERROR\n )\n );\n }\n\n // oxlint-disable-next-line eslint-plugin-promise/avoid-new -- wrapping callback-based HTTP server\n return new Promise<string>((resolve, reject) => {\n let settled = false;\n const sockets = new Set<Socket>();\n\n const settle = (ok: boolean, value: string | CliError): void => {\n if (settled) {\n return;\n }\n\n settled = true;\n clearTimeout(timer);\n\n httpServer.close(() => {\n if (ok) {\n resolve(value as string);\n } else {\n reject(value);\n }\n });\n\n // Destroy kept-alive connections so httpServer.close() can finish\n for (const socket of sockets) {\n socket.destroy();\n }\n };\n\n const httpServer = createServer((request, response) => {\n if (!request.url) {\n response.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(\"Missing request URL\"));\n settle(\n false,\n new CliError(\n \"OAuth callback is missing a request URL\",\n EXIT_CODES.ERROR\n )\n );\n return;\n }\n\n const url = new URL(request.url, options.redirectUrl.origin);\n\n if (url.pathname !== pathname) {\n response.writeHead(404, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(\"Invalid callback path\"));\n return;\n }\n\n const providerError = url.searchParams.get(\"error\");\n if (providerError) {\n const description =\n url.searchParams.get(\"error_description\") ?? providerError;\n\n response.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(description));\n\n settle(\n false,\n new CliError(\n `OAuth provider returned an error: ${description}`,\n EXIT_CODES.ERROR\n )\n );\n return;\n }\n\n const state = url.searchParams.get(\"state\");\n if (state !== options.expectedState) {\n response.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(\"State verification failed\"));\n\n settle(\n false,\n new CliError(\"OAuth state verification failed\", EXIT_CODES.ERROR)\n );\n return;\n }\n\n const code = url.searchParams.get(\"code\");\n if (!code) {\n response.writeHead(400, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(errorHtml(\"Authorization code was missing\"));\n\n settle(\n false,\n new CliError(\n \"OAuth callback is missing an authorization code\",\n EXIT_CODES.ERROR\n )\n );\n return;\n }\n\n response.writeHead(200, { \"content-type\": \"text/html; charset=utf-8\" });\n response.end(SUCCESS_HTML);\n settle(true, code);\n });\n\n httpServer.on(\"connection\", (socket) => {\n sockets.add(socket);\n socket.once(\"close\", () => sockets.delete(socket));\n });\n\n httpServer.on(\"error\", (error) => {\n settle(\n false,\n new CliError(\n `Failed to start callback server on ${host}:${port}: ${error.message}`,\n EXIT_CODES.ERROR\n )\n );\n });\n\n const timer = setTimeout(() => {\n settle(\n false,\n new CliError(\"Login timed out. Please try again.\", EXIT_CODES.CANCELLED)\n );\n }, options.timeoutMs);\n\n httpServer.listen(port, host);\n });\n};\n","import { createHash, randomBytes } from \"node:crypto\";\n\nexport const createOAuthState = (): string => randomBytes(24).toString(\"hex\");\n\nexport const createCodeVerifier = (): string =>\n randomBytes(64).toString(\"base64url\");\n\nexport const createCodeChallenge = (verifier: string): string =>\n createHash(\"sha256\").update(verifier).digest().toString(\"base64url\");\n","import { readFileSync } from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\nimport { CliError, EXIT_CODES } from \"./errors.js\";\n\nconst MIN_SUPPORTED_NODE_VERSION = [20, 17, 0] as const;\n\nexport const SUPPORTED_NODE_RANGE = \">=20.17.0\";\n\nconst parseVersion = (input: string): [number, number, number] | null => {\n const match = /^v?(\\d+)\\.(\\d+)\\.(\\d+)/.exec(input.trim());\n if (!match) {\n return null;\n }\n\n const [, majorText = \"\", minorText = \"\", patchText = \"\"] = match;\n if (!majorText || !minorText || !patchText) {\n return null;\n }\n\n const major = Number.parseInt(majorText, 10);\n const minor = Number.parseInt(minorText, 10);\n const patch = Number.parseInt(patchText, 10);\n\n if ([major, minor, patch].some((value) => Number.isNaN(value))) {\n return null;\n }\n\n return [major, minor, patch];\n};\n\nexport const isSupportedNodeVersion = (version: string): boolean => {\n const parsed = parseVersion(version);\n if (!parsed) {\n return false;\n }\n\n const [major, minor, patch] = parsed;\n const [minMajor, minMinor, minPatch] = MIN_SUPPORTED_NODE_VERSION;\n\n if (major !== minMajor) {\n return major > minMajor;\n }\n\n if (minor !== minMinor) {\n return minor > minMinor;\n }\n\n return patch >= minPatch;\n};\n\nexport const assertSupportedNodeVersion = (\n version = process.versions.node\n): void => {\n if (isSupportedNodeVersion(version)) {\n return;\n }\n\n throw new CliError(\n `blodemd requires Node.js ${SUPPORTED_NODE_RANGE}. Current version: ${version}.`,\n EXIT_CODES.VALIDATION,\n \"Install a supported Node.js version and try again.\"\n );\n};\n\nexport const readCliVersion = (moduleUrl: string): string => {\n const moduleDir = path.dirname(fileURLToPath(moduleUrl));\n const packageJsonPath = path.resolve(moduleDir, \"..\", \"package.json\");\n const raw = readFileSync(packageJsonPath, \"utf8\");\n const parsed = JSON.parse(raw) as { version?: string };\n\n return parsed.version ?? \"0.0.0\";\n};\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { shouldIgnoreRootDocsFile } from \"@repo/common\";\n\nimport { CliError, EXIT_CODES } from \"./errors.js\";\n\nconst TEXT_CONTENT_TYPES: Record<string, string> = {\n \".css\": \"text/css; charset=utf-8\",\n \".html\": \"text/html; charset=utf-8\",\n \".js\": \"text/javascript; charset=utf-8\",\n \".json\": \"application/json; charset=utf-8\",\n \".md\": \"text/markdown; charset=utf-8\",\n \".mdx\": \"text/markdown; charset=utf-8\",\n \".svg\": \"image/svg+xml\",\n \".txt\": \"text/plain; charset=utf-8\",\n \".yaml\": \"application/yaml; charset=utf-8\",\n \".yml\": \"application/yaml; charset=utf-8\",\n};\n\nexport interface UploadBatchItem {\n contentBase64: string;\n contentType: string;\n path: string;\n}\n\ntype ReadFile = (filePath: string) => Promise<Buffer>;\n\nconst normalizeRelativePath = (root: string, filePath: string): string =>\n path.relative(root, filePath).split(path.sep).join(\"/\");\n\nconst getContentType = (filePath: string): string =>\n TEXT_CONTENT_TYPES[path.extname(filePath).toLowerCase()] ??\n \"application/octet-stream\";\n\nconst estimateUploadItemBytes = (item: UploadBatchItem): number =>\n item.contentBase64.length + item.path.length + 64;\n\nconst createUploadBatchItem = async (\n filePath: string,\n root: string,\n readFile: ReadFile\n): Promise<UploadBatchItem> => {\n const content = await readFile(filePath);\n\n return {\n contentBase64: content.toString(\"base64\"),\n contentType: getContentType(filePath),\n path: normalizeRelativePath(root, filePath),\n };\n};\n\nexport const createUploadBatches =\n async function* createUploadBatchesGenerator({\n files,\n maxBatchBytes,\n readFile = fs.readFile,\n root,\n }: {\n files: string[];\n maxBatchBytes: number;\n readFile?: ReadFile;\n root: string;\n }): AsyncGenerator<UploadBatchItem[]> {\n let currentBatch: UploadBatchItem[] = [];\n let currentBatchBytes = 0;\n\n for (const filePath of files) {\n const item = await createUploadBatchItem(filePath, root, readFile);\n if (shouldIgnoreRootDocsFile(item.path)) {\n continue;\n }\n const itemBytes = estimateUploadItemBytes(item);\n\n if (itemBytes > maxBatchBytes) {\n throw new CliError(\n `File \"${item.path}\" is too large to upload in a single request.`,\n EXIT_CODES.VALIDATION,\n \"Split the file into smaller pieces or raise the server request limit.\"\n );\n }\n\n if (\n currentBatch.length > 0 &&\n currentBatchBytes + itemBytes > maxBatchBytes\n ) {\n yield currentBatch;\n currentBatch = [];\n currentBatchBytes = 0;\n }\n\n currentBatch.push(item);\n currentBatchBytes += itemBytes;\n }\n\n if (currentBatch.length > 0) {\n yield currentBatch;\n }\n };\n","import { spawnSync } from \"node:child_process\";\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport {\n confirm,\n intro,\n isCancel,\n log,\n password,\n select,\n spinner,\n text,\n} from \"@clack/prompts\";\nimport { shouldIgnoreRootDocsFile } from \"@repo/common\";\nimport chalk from \"chalk\";\nimport { Command, InvalidArgumentError } from \"commander\";\nimport open from \"open\";\n\nimport { resolveAuthToken, resolveTokenStatus } from \"./auth-session.js\";\nimport {\n BLODE_API_URL_ENV,\n BLODE_BRANCH_ENV,\n BLODE_COMMIT_MESSAGE_ENV,\n CREDENTIALS_FILE,\n BLODE_PROJECT_ENV,\n DEFAULT_API_URL,\n DEFAULT_OAUTH_CALLBACK_PATH,\n DEFAULT_OAUTH_CALLBACK_PORT,\n DEFAULT_OAUTH_TIMEOUT_SECONDS,\n OAUTH_CLIENT_ID,\n} from \"./constants.js\";\nimport { devCommand } from \"./dev/command.js\";\nimport { resolveDocsRoot } from \"./dev/resolve-root.js\";\nimport { toCliError } from \"./errors.js\";\nimport {\n findExistingPaths,\n writeFileIfMissing,\n writeSymlinkIfMissing,\n} from \"./fs-utils.js\";\nimport {\n CANCEL_SCAFFOLD,\n CREATE_IN_SUBDIRECTORY,\n resolveDirectoryFromAction,\n resolveInitialDirectory,\n SCAFFOLD_CURRENT_DIRECTORY,\n} from \"./new-flow.js\";\nimport type { NoArgInteractiveAction } from \"./new-flow.js\";\nimport { waitForOAuthCode } from \"./oauth-callback.js\";\nimport { exchangeAuthorizationCode } from \"./oauth-token.js\";\nimport {\n createCodeChallenge,\n createCodeVerifier,\n createOAuthState,\n} from \"./pkce.js\";\nimport { assertSupportedNodeVersion, readCliVersion } from \"./runtime.js\";\nimport {\n DEFAULT_SCAFFOLD_DIRECTORY,\n deriveDefaultProjectSlug,\n getScaffoldFiles,\n isScaffoldTemplate,\n resolveScaffoldDirectory,\n SCAFFOLD_TEMPLATES,\n validateProjectSlug,\n} from \"./scaffold.js\";\nimport type { ScaffoldTemplate } from \"./scaffold.js\";\nimport { loadValidatedSiteConfig } from \"./site-config.js\";\nimport {\n clearStoredCredentials,\n readAuthFile,\n writeStoredApiKey,\n writeStoredAuthSession,\n} from \"./storage.js\";\nimport {\n buildOAuthUrls,\n resolveSupabaseConfig,\n tokenResponseToStoredSession,\n} from \"./supabase.js\";\nimport type { DeploymentResponse } from \"./types.js\";\nimport { createUploadBatches } from \"./upload.js\";\nimport { parsePort, parsePositiveInteger } from \"./validation.js\";\n\nconst CONFIG_FILE = \"docs.json\";\n\n// --- File helpers ---\n\nconst readGitValue = (gitArgs: string[]): string | undefined => {\n const result = spawnSync(\"git\", gitArgs, {\n encoding: \"utf8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n });\n\n if (result.status !== 0) {\n return;\n }\n\n const value = result.stdout.trim();\n return value || undefined;\n};\n\nconst shouldSkipEntry = (name: string): boolean =>\n name.startsWith(\".\") || name === \"node_modules\";\n\nconst collectFiles = async (\n root: string,\n directory = root\n): Promise<string[]> => {\n const entries = await fs.readdir(directory, { withFileTypes: true });\n const files: string[] = [];\n\n for (const entry of entries) {\n if (shouldSkipEntry(entry.name)) {\n continue;\n }\n\n const absolutePath = path.join(directory, entry.name);\n const relativePath = path\n .relative(root, absolutePath)\n .split(path.sep)\n .join(\"/\");\n if (entry.isDirectory()) {\n files.push(...(await collectFiles(root, absolutePath)));\n continue;\n }\n\n if (entry.isFile()) {\n if (shouldIgnoreRootDocsFile(relativePath)) {\n continue;\n }\n files.push(absolutePath);\n }\n }\n\n return files.toSorted((left, right) => left.localeCompare(right));\n};\n\nconst readJson = async (response: Response): Promise<unknown> => {\n const responseText = await response.text();\n if (!responseText) {\n return null;\n }\n\n try {\n return JSON.parse(responseText) as unknown;\n } catch {\n return responseText;\n }\n};\n\nconst requestJson = async <T>(\n url: string,\n init: RequestInit,\n message: string\n): Promise<T> => {\n const response = await fetch(url, init);\n const data = await readJson(response);\n if (!response.ok) {\n const detail =\n typeof data === \"string\" ? data : JSON.stringify(data ?? {}, null, 2);\n throw new Error(`${message}: ${response.status} ${detail}`);\n }\n\n return data as T;\n};\n\nconst reportCommandError = (prefix: string, error: unknown): void => {\n const cliError = toCliError(error);\n\n log.error(`${prefix}: ${cliError.message}`);\n if (cliError.hint) {\n log.info(cliError.hint);\n }\n log.info(\"Failed\");\n process.exitCode = cliError.exitCode;\n};\n\nconst parseScaffoldTemplate = (value: string): ScaffoldTemplate => {\n if (isScaffoldTemplate(value)) {\n return value;\n }\n\n throw new InvalidArgumentError(\n `Expected one of: ${SCAFFOLD_TEMPLATES.join(\", \")}.`\n );\n};\n\nconst parseProjectSlug = (value: string): string => {\n const validationError = validateProjectSlug(value);\n\n if (validationError) {\n throw new InvalidArgumentError(validationError);\n }\n\n return value.trim();\n};\n\nconst isInteractiveTerminal = () =>\n process.stdin.isTTY === true && process.stdout.isTTY === true;\n\nconst promptForNoArgDirectoryAction = async (): Promise<\n NoArgInteractiveAction | undefined\n> => {\n const directoryAction = await select<NoArgInteractiveAction>({\n initialValue: CREATE_IN_SUBDIRECTORY,\n message: \"Directory . is not empty. What would you like to do?\",\n options: [\n {\n hint: \"recommended\",\n label: \"Create in a subdirectory\",\n value: CREATE_IN_SUBDIRECTORY,\n },\n {\n hint: \"scaffold here\",\n label: \"Scaffold current directory\",\n value: SCAFFOLD_CURRENT_DIRECTORY,\n },\n {\n label: \"Cancel\",\n value: CANCEL_SCAFFOLD,\n },\n ],\n });\n\n if (isCancel(directoryAction)) {\n return;\n }\n\n return directoryAction;\n};\n\nconst promptForSubdirectoryName = async (): Promise<string | undefined> => {\n const subdirectory = await text({\n initialValue: DEFAULT_SCAFFOLD_DIRECTORY,\n message: \"Subdirectory name\",\n placeholder: DEFAULT_SCAFFOLD_DIRECTORY,\n validate: (value) => {\n if (!value?.trim()) {\n return \"Subdirectory name is required.\";\n }\n },\n });\n\n if (isCancel(subdirectory)) {\n return;\n }\n\n return subdirectory.trim();\n};\n\nconst promptForProjectSlug = async (\n initialValue: string\n): Promise<string | undefined> => {\n const projectSlug = await text({\n initialValue,\n message: \"Project slug\",\n placeholder: initialValue,\n validate: validateProjectSlug,\n });\n\n if (isCancel(projectSlug)) {\n return;\n }\n\n return projectSlug.trim();\n};\n\nconst resolveRequestedDirectory = async (\n directory: string | undefined,\n shouldPrompt: boolean\n): Promise<\n | {\n directory: string;\n skipNonEmptyConfirmation?: boolean;\n }\n | undefined\n> => {\n let currentDirectoryEntries: string[] = [];\n\n if (!directory && shouldPrompt) {\n currentDirectoryEntries = await fs.readdir(process.cwd());\n }\n\n const initialResolution = resolveInitialDirectory({\n currentDirectoryEntries,\n directory,\n interactive: shouldPrompt,\n });\n\n if (initialResolution.kind === \"target\") {\n return { directory: initialResolution.directory };\n }\n\n const directoryAction = await promptForNoArgDirectoryAction();\n\n if (!directoryAction || directoryAction === CANCEL_SCAFFOLD) {\n return;\n }\n\n const subdirectory =\n directoryAction === CREATE_IN_SUBDIRECTORY\n ? await promptForSubdirectoryName()\n : undefined;\n const resolvedDirectory = resolveDirectoryFromAction(\n directoryAction,\n subdirectory\n );\n\n if (!resolvedDirectory) {\n return;\n }\n\n return {\n directory: resolvedDirectory,\n skipNonEmptyConfirmation: directoryAction === SCAFFOLD_CURRENT_DIRECTORY,\n };\n};\n\nconst confirmScaffoldTarget = async (\n root: string,\n template: ScaffoldTemplate,\n shouldPrompt: boolean,\n options?: {\n skipNonEmptyConfirmation?: boolean;\n }\n): Promise<boolean> => {\n const existingTarget = await fs.lstat(root).catch(() => null);\n\n if (existingTarget && !existingTarget.isDirectory()) {\n throw new Error(\n `Target path already exists and is not a directory: ${root}`\n );\n }\n\n if (!existingTarget?.isDirectory()) {\n return true;\n }\n\n const existingScaffoldPaths = await findExistingPaths(\n root,\n getScaffoldFiles(template).map((file) => file.path)\n );\n\n if (existingScaffoldPaths.length > 0) {\n throw new Error(\n `Target directory already contains scaffold files: ${existingScaffoldPaths.join(\", \")}. Use a different directory or remove those files first.`\n );\n }\n\n const directoryEntries = await fs.readdir(root);\n const existingEntries = directoryEntries.toSorted((left, right) =>\n left.localeCompare(right)\n );\n\n if (existingEntries.length === 0) {\n return true;\n }\n\n if (options?.skipNonEmptyConfirmation) {\n return true;\n }\n\n if (!shouldPrompt) {\n throw new Error(\n `Target directory is not empty: ${root}. Choose an empty directory or run this command in an interactive terminal to confirm scaffolding there.`\n );\n }\n\n const shouldContinue = await confirm({\n message: `Scaffold into the non-empty directory ${root}? Existing files will be left untouched.`,\n });\n\n return !isCancel(shouldContinue) && shouldContinue;\n};\n\nconst resolveProjectSlug = async (\n providedName: string | undefined,\n directory: string,\n shouldPrompt: boolean\n): Promise<string | undefined> => {\n const defaultProjectSlug = deriveDefaultProjectSlug(directory, process.cwd());\n\n if (providedName) {\n return providedName;\n }\n\n if (!shouldPrompt) {\n return defaultProjectSlug;\n }\n\n return await promptForProjectSlug(defaultProjectSlug);\n};\n\nconst writeScaffoldFiles = async (\n root: string,\n template: ScaffoldTemplate,\n projectSlug: string\n) => {\n for (const file of getScaffoldFiles(template, { projectSlug })) {\n const filePath = path.join(root, file.path);\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n\n if (file.type === \"symlink\") {\n await writeSymlinkIfMissing(filePath, file.target, {\n fallbackContent: file.fallbackContent,\n });\n continue;\n }\n\n await writeFileIfMissing(filePath, file.content);\n }\n};\n\n// --- Auth helpers ---\n\nconst fetchUserEmail = async (\n apiUrl: string,\n token: string\n): Promise<string | null> => {\n try {\n const user = await requestJson<{ email: string }>(\n `${apiUrl}/auth/me`,\n { headers: { Authorization: `Bearer ${token}` } },\n \"Failed to fetch user info\"\n );\n return user.email;\n } catch {\n return null;\n }\n};\n\n// --- Push helpers ---\n\ninterface PushConfig {\n project: string;\n apiUrl: string;\n authToken: string;\n branch: string;\n commitMessage?: string;\n}\n\nconst resolvePushConfig = async (\n config: { name?: string },\n options: {\n apiKey?: string;\n apiUrl?: string;\n branch?: string;\n message?: string;\n project?: string;\n }\n): Promise<PushConfig> => {\n const project =\n options.project ?? process.env[BLODE_PROJECT_ENV] ?? config.name;\n const apiUrl =\n options.apiUrl ?? process.env[BLODE_API_URL_ENV] ?? DEFAULT_API_URL;\n\n const resolved = await resolveAuthToken(options.apiKey);\n const authToken = resolved?.token;\n\n const branch =\n options.branch ??\n process.env[BLODE_BRANCH_ENV] ??\n process.env.GITHUB_REF_NAME ??\n readGitValue([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"]) ??\n \"main\";\n const commitMessage =\n options.message ??\n process.env[BLODE_COMMIT_MESSAGE_ENV] ??\n readGitValue([\"log\", \"-1\", \"--pretty=%s\"]);\n\n if (!project) {\n throw new Error(\n 'Missing project slug. Set \"name\" in docs.json, pass --project, or set BLODEMD_PROJECT.'\n );\n }\n if (!authToken) {\n throw new Error(\n 'Missing credentials. Run \"blodemd login\", pass --api-key, or set BLODEMD_API_KEY.'\n );\n }\n\n return { apiUrl, authToken, branch, commitMessage, project };\n};\n\nconst autoCreateProject = async (\n project: string,\n apiUrl: string,\n headers: Record<string, string>\n): Promise<boolean> => {\n const authData = await readAuthFile();\n if (!authData?.session) {\n throw new Error(\n `Project \"${project}\" not found. Create it at blode.md or login with \"blodemd login\" to auto-create.`\n );\n }\n\n const shouldCreate = await confirm({\n message: `Project \"${project}\" doesn't exist. Create it?`,\n });\n\n if (isCancel(shouldCreate) || !shouldCreate) {\n return false;\n }\n\n const createResult = await requestJson<{\n project: { id: string; slug: string };\n token: string;\n }>(\n new URL(\"/projects\", apiUrl).toString(),\n {\n body: JSON.stringify({ name: project, slug: project }),\n headers,\n method: \"POST\",\n },\n \"Failed to create project\"\n );\n\n log.success(`Project ${chalk.cyan(createResult.project.slug)} created`);\n log.info(`API key for CI: ${chalk.dim(createResult.token)}`);\n return true;\n};\n\nconst scaffoldDocsSite = async (\n directory: string | undefined,\n options?: {\n deprecatedCommand?: string;\n name?: string;\n template?: ScaffoldTemplate;\n yes?: boolean;\n }\n) => {\n intro(chalk.bold(\"blodemd new\"));\n\n if (options?.deprecatedCommand) {\n log.warn(\n `\"${options.deprecatedCommand}\" is deprecated. Use ${chalk.cyan(\"blodemd new\")} instead.`\n );\n }\n\n try {\n const template = options?.template ?? \"minimal\";\n const shouldPrompt = isInteractiveTerminal() && !options?.yes;\n const selectedDirectory = await resolveRequestedDirectory(\n directory,\n shouldPrompt\n );\n\n if (!selectedDirectory) {\n log.warn(\"Cancelled\");\n return;\n }\n\n const resolvedDirectory = resolveScaffoldDirectory(\n selectedDirectory.directory\n );\n const root = path.resolve(process.cwd(), resolvedDirectory);\n\n if (\n !(await confirmScaffoldTarget(root, template, shouldPrompt, {\n skipNonEmptyConfirmation: selectedDirectory.skipNonEmptyConfirmation,\n }))\n ) {\n log.warn(\"Cancelled\");\n return;\n }\n\n const projectSlug = await resolveProjectSlug(\n options?.name,\n resolvedDirectory,\n shouldPrompt\n );\n\n if (!projectSlug) {\n log.warn(\"Cancelled\");\n return;\n }\n\n await fs.mkdir(root, { recursive: true });\n await writeScaffoldFiles(root, template, projectSlug);\n\n log.success(`Docs scaffolded in ${chalk.cyan(root)}`);\n if (template === \"starter\") {\n log.info(\"Starter template includes brand assets and helper files.\");\n }\n log.info(`Project slug: ${chalk.cyan(projectSlug)}`);\n log.info(\"Done\");\n } catch (error: unknown) {\n reportCommandError(\"New failed\", error);\n }\n};\n\n// 4 MB limit keeps each batch well under Vercel's 4.5 MB serverless body cap\nconst MAX_BATCH_BYTES = 4 * 1024 * 1024;\n\nconst uploadFiles = async (\n files: string[],\n root: string,\n apiPath: (suffix: string) => string,\n deploymentId: string,\n headers: Record<string, string>,\n s: ReturnType<typeof spinner>\n) => {\n s.start(`Uploading ${files.length} files`);\n\n let uploaded = 0;\n for await (const batch of createUploadBatches({\n files,\n maxBatchBytes: MAX_BATCH_BYTES,\n root,\n })) {\n await requestJson(\n apiPath(`/${deploymentId}/files/batch`),\n {\n body: JSON.stringify({ files: batch }),\n headers,\n method: \"POST\",\n },\n \"Failed to upload files\"\n );\n uploaded += batch.length;\n s.message(`Uploading files (${uploaded}/${files.length})`);\n }\n\n s.stop(`Uploaded ${chalk.cyan(String(files.length))} files`);\n};\n\n// --- CLI ---\n\nconst program = new Command();\nconst cliVersion = readCliVersion(import.meta.url);\n\nprogram.name(\"blodemd\").description(\"Blode.md CLI\").version(cliVersion);\nprogram.hook(\"preAction\", () => {\n assertSupportedNodeVersion();\n});\n\n// login\n\nprogram\n .command(\"login\")\n .description(\"Authenticate with Blode.md\")\n .option(\"--token\", \"Paste an API key instead of using browser login\")\n .option(\n \"--port <port>\",\n \"Loopback callback port\",\n String(DEFAULT_OAUTH_CALLBACK_PORT)\n )\n .option(\n \"--timeout <seconds>\",\n \"OAuth timeout in seconds\",\n String(DEFAULT_OAUTH_TIMEOUT_SECONDS)\n )\n .option(\"--no-open\", \"Print URL instead of opening the browser\")\n .action(\n async (options: {\n token?: boolean;\n port: string;\n timeout: string;\n open: boolean;\n }) => {\n intro(chalk.bold(\"blodemd login\"));\n\n try {\n if (options.token) {\n const apiKey = await password({\n message: \"Enter your API key\",\n validate: (value) => {\n if (!value) {\n return \"API key is required.\";\n }\n },\n });\n\n if (isCancel(apiKey)) {\n log.warn(\"Cancelled\");\n return;\n }\n\n await writeStoredApiKey({ apiKey, type: \"api-key\" });\n\n const prefix = apiKey.split(\".\")[0] ?? apiKey.slice(0, 12);\n log.success(`Authenticated as ${chalk.cyan(prefix)}`);\n log.info(\"Done\");\n return;\n }\n\n // OAuth 2.1 authorization code flow with PKCE\n const config = resolveSupabaseConfig();\n const { authorizeUrl, tokenUrl } = buildOAuthUrls(config);\n const clientId = OAUTH_CLIENT_ID;\n\n const port = parsePort(options.port);\n const timeoutSeconds = parsePositiveInteger(options.timeout, \"Timeout\");\n const redirectUrl = new URL(\n `http://127.0.0.1:${port}${DEFAULT_OAUTH_CALLBACK_PATH}`\n );\n\n const state = createOAuthState();\n const codeVerifier = createCodeVerifier();\n const codeChallenge = createCodeChallenge(codeVerifier);\n\n const authUrl = new URL(authorizeUrl);\n authUrl.searchParams.set(\"response_type\", \"code\");\n authUrl.searchParams.set(\"client_id\", clientId);\n authUrl.searchParams.set(\"redirect_uri\", redirectUrl.toString());\n authUrl.searchParams.set(\"code_challenge\", codeChallenge);\n authUrl.searchParams.set(\"code_challenge_method\", \"S256\");\n authUrl.searchParams.set(\"state\", state);\n authUrl.searchParams.set(\"scope\", \"openid email profile\");\n\n const callbackPromise = waitForOAuthCode({\n expectedState: state,\n redirectUrl,\n timeoutMs: timeoutSeconds * 1000,\n });\n\n if (options.open) {\n log.info(\"Opening browser for authentication...\");\n log.info(\n `If the browser doesn't open, visit: ${chalk.cyan(authUrl.toString())}`\n );\n await open(authUrl.toString());\n } else {\n log.info(\"Open this URL to continue authentication:\");\n log.info(chalk.cyan(authUrl.toString()));\n }\n\n const code = await callbackPromise;\n\n const tokenResponse = await exchangeAuthorizationCode(\n { clientId, tokenUrl },\n code,\n codeVerifier,\n redirectUrl.toString()\n );\n\n const storedSession = tokenResponseToStoredSession(tokenResponse);\n await writeStoredAuthSession(storedSession);\n\n const email =\n storedSession.user?.email ??\n (await fetchUserEmail(\n process.env[BLODE_API_URL_ENV] ?? DEFAULT_API_URL,\n storedSession.accessToken\n ));\n\n if (email) {\n log.success(`Logged in as ${chalk.cyan(email)}`);\n } else {\n log.success(\"Logged in successfully.\");\n }\n\n log.info(\"Done\");\n } catch (error: unknown) {\n reportCommandError(\"Login failed\", error);\n }\n }\n );\n\n// logout\n\nprogram\n .command(\"logout\")\n .description(\"Remove stored credentials\")\n .action(async () => {\n intro(chalk.bold(\"blodemd logout\"));\n\n try {\n let existing = false;\n try {\n await fs.access(CREDENTIALS_FILE);\n existing = true;\n } catch {\n existing = false;\n }\n\n await clearStoredCredentials();\n\n if (existing) {\n log.success(\"Credentials removed.\");\n } else {\n log.info(\"No stored credentials found.\");\n }\n log.info(\"Done\");\n } catch (error: unknown) {\n reportCommandError(\"Logout failed\", error);\n }\n });\n\n// whoami\n\nprogram\n .command(\"whoami\")\n .description(\"Show current authentication\")\n .action(async () => {\n try {\n const resolved = await resolveAuthToken();\n\n if (!resolved) {\n log.warn('Not logged in. Run \"blodemd login\" to authenticate.');\n return;\n }\n\n if (resolved.source === \"environment\") {\n log.info(\"Authenticated via BLODEMD_API_KEY environment variable\");\n return;\n }\n\n // API keys have no expiry and no user info from JWT\n if (!resolved.expiresAt && !resolved.user) {\n const prefix =\n resolved.token.split(\".\")[0] ?? resolved.token.slice(0, 12);\n log.info(`Logged in with API key ${chalk.cyan(prefix)}`);\n return;\n }\n\n const status = resolveTokenStatus(resolved);\n\n const email =\n resolved.user?.email ??\n (await fetchUserEmail(\n process.env[BLODE_API_URL_ENV] ?? DEFAULT_API_URL,\n resolved.token\n ));\n\n if (email) {\n log.info(`Logged in as ${chalk.cyan(email)}`);\n } else {\n log.info(\"Logged in (could not fetch user details).\");\n }\n\n if (resolved.expiresAt && status.expired) {\n log.warn(\n 'Session has expired. Run \"blodemd login\" to re-authenticate.'\n );\n }\n } catch (error: unknown) {\n reportCommandError(\"Whoami failed\", error);\n }\n });\n\n// new\n\nprogram\n .command(\"new\")\n .description(\"Create a new blode.md documentation site\")\n .argument(\"[directory]\", \"target directory\")\n .option(\"--name <slug>\", \"project slug for docs.json\", parseProjectSlug)\n .option(\n \"-t, --template <template>\",\n `scaffold template (${SCAFFOLD_TEMPLATES.join(\", \")})`,\n parseScaffoldTemplate,\n \"minimal\"\n )\n .option(\"-y, --yes\", \"accept defaults without prompting\")\n .action(\n async (\n directory: string | undefined,\n options: {\n name?: string;\n template: ScaffoldTemplate;\n yes?: boolean;\n }\n ) => {\n await scaffoldDocsSite(directory, {\n name: options.name,\n template: options.template,\n yes: options.yes,\n });\n }\n );\n\nprogram\n .command(\"init\", { hidden: true })\n .argument(\"[directory]\", \"target directory\")\n .option(\"--name <slug>\", \"project slug for docs.json\", parseProjectSlug)\n .option(\n \"-t, --template <template>\",\n `scaffold template (${SCAFFOLD_TEMPLATES.join(\", \")})`,\n parseScaffoldTemplate,\n \"minimal\"\n )\n .option(\"-y, --yes\", \"accept defaults without prompting\")\n .action(\n async (\n directory: string | undefined,\n options: {\n name?: string;\n template: ScaffoldTemplate;\n yes?: boolean;\n }\n ) => {\n await scaffoldDocsSite(directory, {\n deprecatedCommand: \"blodemd init\",\n name: options.name,\n template: options.template,\n yes: options.yes,\n });\n }\n );\n\n// validate\n\nprogram\n .command(\"validate\")\n .description(\"Validate docs.json\")\n .argument(\"[dir]\", \"docs directory\")\n .action(async (dir?: string) => {\n intro(chalk.bold(\"blodemd validate\"));\n\n try {\n const root = await resolveDocsRoot(dir);\n const { warnings } = await loadValidatedSiteConfig(root);\n for (const warning of warnings) {\n log.warn(warning);\n }\n log.success(`${chalk.cyan(CONFIG_FILE)} is valid.`);\n log.info(\"Done\");\n } catch (error: unknown) {\n reportCommandError(\"Validation failed\", error);\n }\n });\n\n// push\n\nprogram\n .command(\"push\")\n .description(\"Deploy docs\")\n .argument(\"[dir]\", \"docs directory\")\n .option(\"--project <slug>\", \"project slug (env: BLODEMD_PROJECT)\")\n .option(\"--api-url <url>\", \"API URL (env: BLODEMD_API_URL)\")\n .option(\"--api-key <token>\", \"API key (env: BLODEMD_API_KEY)\")\n .option(\"--branch <name>\", \"git branch (env: BLODEMD_BRANCH)\")\n .option(\"--message <msg>\", \"deploy message (env: BLODEMD_COMMIT_MESSAGE)\")\n .action(\n async (\n dir: string | undefined,\n options: {\n apiKey?: string;\n apiUrl?: string;\n branch?: string;\n message?: string;\n project?: string;\n }\n ) => {\n intro(chalk.bold(\"blodemd push\"));\n const s = spinner();\n\n try {\n const root = await resolveDocsRoot(dir);\n\n s.start(\"Validating configuration\");\n const { config, warnings } = await loadValidatedSiteConfig(root);\n s.stop(\"Configuration valid\");\n for (const warning of warnings) {\n log.warn(warning);\n }\n\n const { project, apiUrl, authToken, branch, commitMessage } =\n await resolvePushConfig(config, options);\n\n s.start(\"Collecting files\");\n const files = await collectFiles(root);\n if (files.length === 0) {\n throw new Error(\"No files found to deploy.\");\n }\n s.stop(`Found ${chalk.cyan(String(files.length))} files`);\n\n const headers = {\n Authorization: `Bearer ${authToken}`,\n \"Content-Type\": \"application/json\",\n };\n\n const apiPath = (suffix: string): string =>\n new URL(\n `/projects/slug/${project}/deployments${suffix}`,\n apiUrl\n ).toString();\n\n const createDeploymentBody = JSON.stringify({ branch, commitMessage });\n\n // Try creating the deployment — if 404, offer to create the project\n s.start(\"Creating deployment\");\n let deployment: DeploymentResponse;\n try {\n deployment = await requestJson<DeploymentResponse>(\n apiPath(\"\"),\n { body: createDeploymentBody, headers, method: \"POST\" },\n \"Failed to create deployment\"\n );\n } catch (error: unknown) {\n const errorMessage = error instanceof Error ? error.message : \"\";\n if (!errorMessage.includes(\"404\")) {\n throw error;\n }\n\n s.stop(\"Project not found\");\n\n const created = await autoCreateProject(project, apiUrl, headers);\n if (!created) {\n log.info(\"Cancelled\");\n return;\n }\n\n s.start(\"Creating deployment\");\n deployment = await requestJson<DeploymentResponse>(\n apiPath(\"\"),\n { body: createDeploymentBody, headers, method: \"POST\" },\n \"Failed to create deployment\"\n );\n }\n s.stop(`Deployment ${chalk.cyan(deployment.id)} created`);\n\n await uploadFiles(files, root, apiPath, deployment.id, headers, s);\n\n s.start(\"Finalizing deployment\");\n const finalized = await requestJson<DeploymentResponse>(\n apiPath(`/${deployment.id}/finalize`),\n {\n body: JSON.stringify({ promote: true }),\n headers,\n method: \"POST\",\n },\n \"Failed to finalize deployment\"\n );\n s.stop(\"Deployment finalized\");\n\n log.success(`Published ${chalk.cyan(finalized.id)}`);\n if (finalized.manifestUrl) {\n log.info(`Manifest: ${finalized.manifestUrl}`);\n }\n if (typeof finalized.fileCount === \"number\") {\n log.info(`Files: ${finalized.fileCount}`);\n }\n\n log.info(\"Done\");\n } catch (error: unknown) {\n s.stop(\"Failed\");\n reportCommandError(\"Push failed\", error);\n }\n }\n );\n\n// dev\n\nprogram\n .command(\"dev\")\n .description(\"Start the local docs dev server\")\n .option(\"-p, --port <port>\", \"Port number\", \"3030\")\n .option(\"-d, --dir <dir>\", \"Docs directory\")\n .option(\"--no-open\", \"Don't open browser\")\n .action(\n async (options: { dir?: string; open?: boolean; port: string }) =>\n await devCommand({\n dir: options.dir,\n openBrowser: options.open ?? true,\n port: options.port,\n })\n );\n\nprogram.parse();\n"],"mappings":"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AAyB,iBAAyB;CACtD,SAAS;CACT,YAAY,EAAE,SAAS,UAAU;CACjC,YAAY,EACV,SAAS;EAAC;EAAQ;EAAQ;EAAW;EAAS,EAC/C;CACD,aAAa;CACb,SAAS;CACT,MAAM;EACJ,KAAK,GAAG,YAAY;EACpB,MAAM;EACN,OAAO;EACR;CACD,UAAU,EAAE,WAAW,MAAM;CAC7B,MAAM;CACN,YAAY,EACV,QAAQ,CACN;EACE,OAAO;EACP,OAAO;GAAC;GAAS;GAAc;GAAc;EAC9C,CACF,EACF;CACF;AAED,MAAM,qBAAqB;CACzB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK;AAEZ,MAAM,sBAAsB,gBAAwC,CAClE;CACE,SAAS,cAAc,sBAAsB,YAAY,CAAC;CAC1D,MAAM;CACP,EACD;CACE,SAAS;CACT,MAAM;CACP,CACF;AAED,MAAM,sBAAsB,gBAAwC;CAClE;EACE,SAAS,cAAc,sBAAsB,YAAY,CAAC;EAC1D,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,iBAAiB;EACjB,MAAM;EACN,QAAQ;EACR,MAAM;EACP;CACD;EACE,SAAS;EACT,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA,yGAAyG,YAAY;GACrH;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA,yGAAyG,YAAY;GACrH;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACD;EACE,SAAS;GACP;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,KAAK,KAAK;EACZ,MAAM;EACP;CACF;AAED,MAAa,oBACX,UACA,YAGmB;CACnB,MAAM,cAAc,SAAS,eAAA;AAC7B,QAAO,aAAa,YAChB,mBAAmB,YAAY,GAC/B,m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"}
|
package/docs/app/globals.css
CHANGED
|
@@ -347,7 +347,7 @@
|
|
|
347
347
|
border-color: var(--border);
|
|
348
348
|
border-width: 0px;
|
|
349
349
|
border-radius: var(--radius-xl);
|
|
350
|
-
@apply
|
|
350
|
+
@apply md:-mx-1;
|
|
351
351
|
|
|
352
352
|
&:has([data-rehype-pretty-code-title]) [data-slot="copy-button"] {
|
|
353
353
|
top: calc(var(--spacing) * 1.5) !important;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import type * as React from "react";
|
|
4
|
+
|
|
5
|
+
type ButtonProps = React.ComponentProps<"button">;
|
|
6
|
+
|
|
7
|
+
const Button = ({ children, ...props }: ButtonProps) => (
|
|
8
|
+
<button type="button" {...props}>
|
|
9
|
+
{children}
|
|
10
|
+
</button>
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export { Button };
|
|
14
|
+
export type { ButtonProps };
|