blodemd 0.0.11 → 0.0.13
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 +11 -47
- package/dev-server/app/layout.tsx +1 -1
- package/dev-server/next.config.js +19 -9
- package/dev-server/tsconfig.json +0 -3
- package/dist/cli.mjs +732 -123
- package/dist/cli.mjs.map +1 -1
- package/docs/app/globals.css +15 -1
- package/docs/components/api/api-playground.tsx +2 -2
- package/docs/components/docs/copy-page-menu.tsx +55 -27
- package/docs/components/docs/doc-header.tsx +1 -1
- package/docs/components/docs/doc-shell.tsx +89 -88
- package/docs/components/docs/doc-sidebar.tsx +6 -3
- package/docs/components/docs/doc-toc.tsx +1 -1
- package/docs/components/docs/mobile-nav.tsx +8 -16
- package/docs/components/docs/sidebar-scroll-area.tsx +58 -0
- package/docs/components/git/repo-picker.tsx +526 -0
- package/docs/components/mdx/agent-instructions.tsx +17 -0
- package/docs/components/mdx/code-block.tsx +6 -1
- package/docs/components/mdx/code-group.tsx +1 -1
- package/docs/components/mdx/iframe.tsx +62 -0
- package/docs/components/mdx/index.tsx +4 -0
- package/docs/components/mdx/tabs.tsx +5 -5
- package/docs/components/mdx/video.tsx +45 -12
- package/docs/components/third-parties.tsx +29 -0
- package/docs/components/ui/badge.tsx +61 -0
- package/docs/components/ui/breadcrumb.tsx +61 -41
- package/docs/components/ui/button-group.tsx +83 -0
- package/docs/components/ui/button.tsx +30 -55
- package/docs/components/ui/command.tsx +32 -4
- package/docs/components/ui/copy-button.tsx +12 -19
- package/docs/components/ui/dialog.tsx +50 -1
- package/docs/components/ui/input.tsx +16 -97
- package/docs/components/ui/kbd.tsx +98 -0
- package/docs/components/ui/morph-icon.tsx +79 -0
- package/docs/components/ui/popover.tsx +225 -30
- package/docs/components/ui/search.tsx +0 -9
- package/docs/components/ui/sheet.tsx +30 -1
- package/docs/components/ui/sidebar.tsx +332 -7
- package/docs/components/ui/site-footer.tsx +6 -4
- package/docs/components/ui/skeleton.tsx +11 -0
- package/docs/components/ui/switch.tsx +32 -0
- package/docs/components/ui/tabs.tsx +138 -0
- package/docs/lib/api-client.ts +72 -0
- package/docs/lib/contextual-options.ts +9 -0
- package/docs/lib/dashboard-session.ts +167 -0
- package/docs/lib/db.ts +13 -0
- package/docs/lib/env.ts +4 -3
- package/docs/lib/etag.ts +22 -0
- package/docs/lib/github-install.ts +33 -0
- package/docs/lib/project-authz.ts +46 -0
- package/docs/lib/routes.ts +5 -1
- package/docs/lib/supabase.ts +30 -6
- package/docs/lib/tenancy.ts +1 -0
- package/docs/lib/tenant-static.ts +206 -4
- package/docs/lib/tenants.ts +5 -1
- package/docs/lib/time-ago.ts +24 -0
- package/docs/lib/use-tab-observer.ts +71 -0
- package/package.json +3 -1
- package/packages/@repo/common/package.json +2 -2
- package/packages/@repo/contracts/dist/git.d.ts +28 -0
- package/packages/@repo/contracts/dist/git.d.ts.map +1 -0
- package/packages/@repo/contracts/dist/git.js +24 -0
- package/packages/@repo/contracts/dist/index.d.ts +1 -1
- package/packages/@repo/contracts/dist/index.d.ts.map +1 -1
- package/packages/@repo/contracts/dist/index.js +1 -1
- package/packages/@repo/contracts/package.json +2 -2
- package/packages/@repo/contracts/src/git.ts +31 -0
- package/packages/@repo/contracts/src/index.ts +1 -1
- package/packages/@repo/models/dist/docs-config.d.ts +6 -0
- package/packages/@repo/models/dist/docs-config.d.ts.map +1 -1
- package/packages/@repo/models/dist/docs-config.js +1 -0
- package/packages/@repo/models/package.json +2 -2
- package/packages/@repo/models/src/docs-config.ts +1 -0
- package/packages/@repo/prebuild/package.json +2 -2
- package/packages/@repo/previewing/dist/index.d.ts +3 -0
- package/packages/@repo/previewing/dist/index.d.ts.map +1 -1
- package/packages/@repo/previewing/dist/index.js +48 -0
- package/packages/@repo/previewing/package.json +2 -2
- package/packages/@repo/previewing/src/index.ts +56 -0
- package/packages/@repo/validation/package.json +2 -2
- package/packages/@repo/validation/src/blodemd-docs-schema.json +1 -0
- package/scripts/prepare-package.mjs +14 -0
- package/packages/@repo/contracts/dist/api-key.d.ts +0 -30
- package/packages/@repo/contracts/dist/api-key.d.ts.map +0 -1
- package/packages/@repo/contracts/dist/api-key.js +0 -20
- package/packages/@repo/contracts/src/api-key.ts +0 -27
package/dist/cli.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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/project-config.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 { slugify } from \"@repo/common\";\n\nexport interface DocsProjectConfig {\n name?: string;\n slug?: string;\n}\n\nexport interface ResolvedProjectTarget {\n project?: string;\n usedLegacyNameFallback: boolean;\n}\n\nexport const LEGACY_PROJECT_NAME_FALLBACK_WARNING =\n \"docs.json.slug is recommended. Falling back to docs.json.name as the deployment slug is deprecated.\";\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\nexport const deriveDisplayNameFromProjectSlug = (projectSlug: string) =>\n projectSlug\n .split(\"-\")\n .filter(Boolean)\n .map((segment) => segment[0]?.toUpperCase() + segment.slice(1))\n .join(\" \");\n\nexport const resolveProjectTarget = (options: {\n cliProject?: string;\n config: DocsProjectConfig;\n envProject?: string;\n}): ResolvedProjectTarget => {\n if (options.cliProject) {\n return {\n project: options.cliProject,\n usedLegacyNameFallback: false,\n };\n }\n\n if (options.envProject) {\n return {\n project: options.envProject,\n usedLegacyNameFallback: false,\n };\n }\n\n if (options.config.slug) {\n return {\n project: options.config.slug,\n usedLegacyNameFallback: false,\n };\n }\n\n if (options.config.name) {\n return {\n project: options.config.name,\n usedLegacyNameFallback: true,\n };\n }\n\n return {\n project: undefined,\n usedLegacyNameFallback: false,\n };\n};\n\nexport const getProjectSlugError = (project: string | undefined) => {\n if (!project) {\n return;\n }\n\n return validateProjectSlug(project);\n};\n","import path from \"node:path\";\n\nimport { slugify } from \"@repo/common\";\n\nimport { deriveDisplayNameFromProjectSlug } from \"./project-config.js\";\n\nexport { validateProjectSlug } from \"./project-config.js\";\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\nconst escapeXmlText = (value: string) =>\n value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\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\nconst createMinimalDocsJson = (projectSlug: string, displayName: string) => ({\n $schema: \"https://blode.md/docs.json\",\n name: displayName,\n navigation: {\n groups: [{ group: \"Getting Started\", pages: [\"index\"] }],\n },\n slug: projectSlug,\n});\n\nconst createStarterDocsJson = (projectSlug: string, displayName: 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: `${displayName} logo`,\n dark: \"/logo/dark.svg\",\n light: \"/logo/light.svg\",\n },\n metadata: { timestamp: true },\n name: displayName,\n navigation: {\n groups: [\n {\n group: \"Getting Started\",\n pages: [\"index\", \"quickstart\", \"development\"],\n },\n ],\n },\n slug: projectSlug,\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 = (\n projectSlug: string,\n displayName: string\n): ScaffoldFile[] => [\n {\n content: stringifyJson(createMinimalDocsJson(projectSlug, displayName)),\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 = (\n projectSlug: string,\n displayName: string\n): ScaffoldFile[] => {\n const escapedDisplayName = escapeXmlText(displayName);\n\n return [\n {\n content: stringifyJson(createStarterDocsJson(projectSlug, displayName)),\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 `slug` in `docs.json` matches your deployment target.\",\n \"- Update `name` in `docs.json` to match the visible product or docs brand.\",\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 `slug` in `docs.json` matches your deployment target.\",\n \"2. Update `name` in `docs.json` to match your visible docs brand.\",\n \"3. Update the `description` field to match your product.\",\n \"4. Replace the assets in `/logo` and `/images`.\",\n \"5. Run `blodemd dev` to preview locally.\",\n \"6. 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 `slug` in `docs.json` and set the display `name` and description.\",\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\">${escapedDisplayName}</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\">${escapedDisplayName}</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};\n\nexport const getScaffoldFiles = (\n template: ScaffoldTemplate,\n options?: {\n displayName?: string;\n projectSlug?: string;\n }\n): ScaffoldFile[] => {\n const projectSlug = options?.projectSlug ?? DEFAULT_PROJECT_SLUG;\n const displayName =\n options?.displayName ?? deriveDisplayNameFromProjectSlug(projectSlug);\n return template === \"starter\"\n ? createStarterFiles(projectSlug, displayName)\n : createMinimalFiles(projectSlug, displayName);\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 {\n deriveDisplayNameFromProjectSlug,\n getProjectSlugError,\n LEGACY_PROJECT_NAME_FALLBACK_WARNING,\n resolveProjectTarget,\n validateProjectSlug,\n} from \"./project-config.js\";\nimport { assertSupportedNodeVersion, readCliVersion } from \"./runtime.js\";\nimport {\n DEFAULT_SCAFFOLD_DIRECTORY,\n deriveDefaultProjectSlug,\n getScaffoldFiles,\n isScaffoldTemplate,\n resolveScaffoldDirectory,\n SCAFFOLD_TEMPLATES,\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 promptForDisplayName = async (\n initialValue: string\n): Promise<string | undefined> => {\n const displayName = await text({\n initialValue,\n message: \"Display name\",\n placeholder: initialValue,\n validate: (value) => {\n if (!value?.trim()) {\n return \"Display name is required.\";\n }\n },\n });\n\n if (isCancel(displayName)) {\n return;\n }\n\n return displayName.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 providedSlug: string | undefined,\n directory: string,\n shouldPrompt: boolean\n): Promise<string | undefined> => {\n const defaultProjectSlug = deriveDefaultProjectSlug(directory, process.cwd());\n\n if (providedSlug) {\n return providedSlug;\n }\n\n if (!shouldPrompt) {\n return defaultProjectSlug;\n }\n\n return await promptForProjectSlug(defaultProjectSlug);\n};\n\nconst resolveDisplayName = async (\n providedDisplayName: string | undefined,\n projectSlug: string,\n shouldPrompt: boolean\n): Promise<string | undefined> => {\n const defaultDisplayName = deriveDisplayNameFromProjectSlug(projectSlug);\n\n if (providedDisplayName?.trim()) {\n return providedDisplayName.trim();\n }\n\n if (!shouldPrompt) {\n return defaultDisplayName;\n }\n\n return await promptForDisplayName(defaultDisplayName);\n};\n\nconst writeScaffoldFiles = async (\n root: string,\n template: ScaffoldTemplate,\n options: {\n displayName: string;\n projectSlug: string;\n }\n) => {\n for (const file of getScaffoldFiles(template, options)) {\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 projectDisplayName: string;\n apiUrl: string;\n authToken: string;\n branch: string;\n commitMessage?: string;\n usedLegacyNameFallback: boolean;\n}\n\nconst resolvePushConfig = async (\n config: { name?: string; slug?: 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, usedLegacyNameFallback } = resolveProjectTarget({\n cliProject: options.project,\n config,\n envProject: process.env[BLODE_PROJECT_ENV],\n });\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 \"slug\" in docs.json, pass --project, or set BLODEMD_PROJECT.'\n );\n }\n\n const projectSlugError = getProjectSlugError(project);\n if (projectSlugError) {\n if (usedLegacyNameFallback) {\n throw new Error(\n `docs.json.name is not a valid deployment slug. Add \"slug\" to docs.json, pass --project, or set BLODEMD_PROJECT. ${projectSlugError}`\n );\n }\n\n throw new Error(`Invalid project slug \"${project}\". ${projectSlugError}`);\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 {\n apiUrl,\n authToken,\n branch,\n commitMessage,\n project,\n projectDisplayName: config.name?.trim() || project,\n usedLegacyNameFallback,\n };\n};\n\nconst autoCreateProject = async (\n project: string,\n projectDisplayName: 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: projectDisplayName, 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 displayName?: string;\n name?: string;\n slug?: 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 if (options?.name && !options.slug) {\n log.warn(\n `\"${chalk.cyan(\"--name\")}\" is deprecated. Use ${chalk.cyan(\"--slug\")} 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?.slug ?? options?.name,\n resolvedDirectory,\n shouldPrompt\n );\n\n if (!projectSlug) {\n log.warn(\"Cancelled\");\n return;\n }\n\n const displayName = await resolveDisplayName(\n options?.displayName,\n projectSlug,\n shouldPrompt\n );\n\n if (!displayName) {\n log.warn(\"Cancelled\");\n return;\n }\n\n await fs.mkdir(root, { recursive: true });\n await writeScaffoldFiles(root, template, { displayName, 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(`Display name: ${chalk.cyan(displayName)}`);\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(\"--slug <slug>\", \"project slug for docs.json\", parseProjectSlug)\n .option(\"--name <slug>\", \"deprecated alias for --slug\", parseProjectSlug)\n .option(\"--display-name <name>\", \"display name for docs.json\")\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 displayName?: string;\n name?: string;\n slug?: string;\n template: ScaffoldTemplate;\n yes?: boolean;\n }\n ) => {\n await scaffoldDocsSite(directory, {\n displayName: options.displayName,\n name: options.name,\n slug: options.slug ?? 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(\"--slug <slug>\", \"project slug for docs.json\", parseProjectSlug)\n .option(\"--name <slug>\", \"deprecated alias for --slug\", parseProjectSlug)\n .option(\"--display-name <name>\", \"display name for docs.json\")\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 displayName?: string;\n name?: string;\n slug?: string;\n template: ScaffoldTemplate;\n yes?: boolean;\n }\n ) => {\n await scaffoldDocsSite(directory, {\n deprecatedCommand: \"blodemd init\",\n displayName: options.displayName,\n name: options.name,\n slug: options.slug ?? 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 {\n project,\n projectDisplayName,\n apiUrl,\n authToken,\n branch,\n commitMessage,\n usedLegacyNameFallback,\n } = await resolvePushConfig(config, options);\n\n if (usedLegacyNameFallback) {\n log.warn(LEGACY_PROJECT_NAME_FALLBACK_WARNING);\n }\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(\n project,\n projectDisplayName,\n apiUrl,\n headers\n );\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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;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,KAAK,KAAK;AAEZ,MAAM,sBACJ,aACA,gBACmB,CACnB;CACE,SAAS,cAAc,sBAAsB,aAAa,YAAY,CAAC;CACvE,MAAM;CACP,EACD;CACE,SAAS;CACT,MAAM;CACP,CACF;AAED,MAAM,sBACJ,aACA,gBACmB;CACnB,MAAM,qBAAqB,cAAc,YAAY;AAErD,QAAO;EACL;GACE,SAAS,cAAc,sBAAsB,aAAa,YAAY,CAAC;GACvE,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,iBAAiB;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACP;EACD;GACE,SAAS;GACT,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK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"}
|
|
1
|
+
{"version":3,"file":"cli.mjs","names":["LEGACY_PROJECT_NAME_FALLBACK_WARNING","CONFIG_FILE","CONFIG_FILE","fileExists","normalizeRelativePath","delay","createServer"],"sources":["../../../packages/common/dist/index.js","../src/constants.ts","../src/errors.ts","../src/oauth-token.ts","../src/storage.ts","../src/jwt.ts","../src/supabase.ts","../src/auth-session.ts","../src/validation.ts","../../../packages/models/dist/docs-config.js","../../../packages/validation/dist/index.js","../../../packages/previewing/dist/fs-source.js","../../../packages/previewing/dist/index.js","../src/site-config.ts","../src/dev/resolve-root.ts","../src/dev/watcher.ts","../src/dev/command.ts","../src/fs-utils.ts","../src/project-config.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":["const BACKSLASH_TO_SLASH_REGEX = /\\\\/g;\nconst TRAILING_SLASHES_REGEX = /\\/+$/g;\nconst LEADING_SLASHES_REGEX = /^\\/+/;\nconst PORT_REGEX = /:\\d+$/;\nexport const LOCAL_ROOT_HOSTS = [\n \"localhost\",\n \"127.0.0.1\",\n \"docs.localhost\",\n];\nexport const normalizePath = (value) => {\n const trimmed = value\n .replace(BACKSLASH_TO_SLASH_REGEX, \"/\")\n .replace(TRAILING_SLASHES_REGEX, \"\");\n return trimmed.replace(LEADING_SLASHES_REGEX, \"\");\n};\nexport const normalizeHost = (value) => value.trim().replace(PORT_REGEX, \"\").toLowerCase();\nexport const getPortlessHostFromEnv = (env) => {\n const portlessUrl = env.PORTLESS_URL?.trim();\n if (!portlessUrl) {\n return null;\n }\n try {\n return normalizeHost(new URL(portlessUrl).host);\n }\n catch {\n return normalizeHost(portlessUrl);\n }\n};\nexport const getLocalRootHostsFromEnv = (env) => {\n const hosts = new Set(LOCAL_ROOT_HOSTS);\n const portlessHost = getPortlessHostFromEnv(env);\n if (portlessHost) {\n hosts.add(portlessHost);\n }\n return hosts;\n};\nexport const withLeadingSlash = (value) => {\n if (!value) {\n return \"/\";\n }\n return value.startsWith(\"/\") ? value : `/${value}`;\n};\nexport const withoutLeadingSlash = (value) => {\n if (!value) {\n return \"\";\n }\n return value.startsWith(\"/\") ? value.slice(1) : value;\n};\nexport const slugify = (value) => value\n .toLowerCase()\n .trim()\n .replaceAll(/[^a-z0-9]+/g, \"-\")\n .replaceAll(/(^-|-$)+/g, \"\");\nconst IGNORED_ROOT_DOCS_FILES = new Set([\n \".gitignore\",\n \"AGENTS.md\",\n \"CLAUDE.md\",\n \"LICENSE\",\n \"LICENSE.md\",\n \"README.md\",\n]);\nexport const shouldIgnoreRootDocsFile = (value) => {\n const normalized = normalizePath(value);\n if (!normalized || normalized.includes(\"/\")) {\n return false;\n }\n return IGNORED_ROOT_DOCS_FILES.has(normalized);\n};\nexport const ensureArray = (value) => {\n if (value === undefined) {\n return [];\n }\n return Array.isArray(value) ? value : [value];\n};\nexport const uniq = (values) => [...new Set(values)];\nexport const safeJsonParse = (value) => {\n try {\n return JSON.parse(value);\n }\n catch {\n return null;\n }\n};\nexport const clamp = (value, min, max) => Math.min(Math.max(value, min), max);\n","import { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\nexport const CLI_NAME = \"blodemd\";\n\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 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 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 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 session =\n hasSession && parsed.session !== undefined\n ? parseStoredAuthSession(parsed.session)\n : undefined;\n\n if (hasSession && parsed.session !== undefined && !session) {\n throw createInvalidCredentialsError(\"stored session is malformed.\");\n }\n\n return {\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\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 clearStoredCredentials = async (): Promise<void> => {\n await rm(CREDENTIALS_FILE, { force: true });\n};\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","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 { OAUTH_CLIENT_ID } from \"./constants.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 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 (): Promise<ResolvedAuthToken | null> => {\n const data = await readAuthFile();\n const session = data?.session;\n\n if (!session) {\n return null;\n }\n\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\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 { z } from \"zod\";\nconst UrlOrPathSchema = z.string().min(1);\nconst SlugSchema = z\n .string()\n .min(1)\n .regex(/^[a-z0-9-]+$/);\nexport const DocsColorsSchema = z\n .object({\n background: z.string().optional(),\n border: z.string().optional(),\n dark: z.string().optional(),\n light: z.string().optional(),\n muted: z.string().optional(),\n primary: z.string().min(1),\n surface: z.string().optional(),\n})\n .strict();\nexport const DocsFontsSchema = z\n .object({\n body: z.string().optional(),\n cssUrl: z.string().optional(),\n heading: z.string().optional(),\n mono: z.string().optional(),\n provider: z.enum([\"google\", \"local\", \"custom\"]).optional(),\n})\n .strict();\nexport const DocsLogoSchema = z\n .object({\n alt: z.string().optional(),\n dark: UrlOrPathSchema.optional(),\n href: z.string().min(1).optional(),\n light: UrlOrPathSchema.optional(),\n})\n .strict();\nexport const DocsNavLinkSchema = z\n .object({\n href: z.string().min(1),\n label: z.string().min(1),\n})\n .strict();\nexport const DocsNavAnchorSchema = z\n .object({\n href: z.string().min(1),\n label: z.string().min(1),\n})\n .strict();\nexport const DocsNavLocaleSchema = z\n .object({\n label: z.string().min(1),\n locale: z.string().optional(),\n url: z.string().min(1),\n})\n .strict();\nexport const DocsNavVersionSchema = z\n .object({\n label: z.string().min(1),\n url: z.string().min(1),\n})\n .strict();\nexport const DocsOpenApiSourceSchema = z\n .object({\n basePath: z.string().optional(),\n directory: z.string().optional(),\n include: z.array(z.string()).optional(),\n source: z.string().min(1),\n})\n .strict();\nexport const DocsNavGroupSchema = z\n .object({\n expanded: z.boolean().optional(),\n group: z.string().optional(),\n hidden: z.boolean().optional(),\n openapi: z.union([z.string().min(1), DocsOpenApiSourceSchema]).optional(),\n pages: z.array(z.string()).optional(),\n})\n .strict();\nexport const DocsNavTabSchema = z\n .object({\n groups: z.array(DocsNavGroupSchema).optional(),\n href: z.string().min(1).optional(),\n icon: z.string().optional(),\n label: z.string().min(1),\n pages: z.array(z.string()).optional(),\n})\n .strict()\n .refine((value) => Boolean(value.groups?.length || value.pages?.length || value.href), { message: \"tab must define groups, pages, or href\", path: [] });\nexport const DocsNavigationSchema = z\n .object({\n global: z\n .object({\n anchors: z.array(DocsNavAnchorSchema).optional(),\n links: z.array(DocsNavLinkSchema).optional(),\n })\n .strict()\n .optional(),\n groups: z.array(DocsNavGroupSchema).optional(),\n hidden: z.array(z.string()).optional(),\n languages: z.array(DocsNavLocaleSchema).optional(),\n pages: z.array(z.string()).optional(),\n tabs: z.array(DocsNavTabSchema).optional(),\n versions: z.array(DocsNavVersionSchema).optional(),\n})\n .strict();\nexport const DocsScriptsSchema = z\n .object({\n body: z.array(z.string()).optional(),\n head: z.array(z.string()).optional(),\n})\n .strict();\nexport const DocsSeoSchema = z\n .object({\n indexing: z.enum([\"all\", \"default\"]).optional(),\n})\n .strict();\nexport const DocsFeatureFlagsSchema = z\n .object({\n rightToc: z.boolean().optional(),\n search: z.boolean().optional(),\n themeToggle: z.boolean().optional(),\n toc: z.boolean().optional(),\n})\n .strict();\nexport const DocsOpenApiProxySchema = z\n .object({\n allowedHosts: z.array(z.string()).optional(),\n enabled: z.boolean().optional(),\n})\n .strict();\nconst MintlifyLogoSchema = z.union([\n UrlOrPathSchema,\n z\n .object({\n dark: UrlOrPathSchema,\n href: z.string().min(1).optional(),\n light: UrlOrPathSchema,\n })\n .strict(),\n]);\nconst MintlifyFaviconSchema = z.union([\n UrlOrPathSchema,\n z\n .object({\n dark: UrlOrPathSchema,\n light: UrlOrPathSchema,\n })\n .strict(),\n]);\nconst MintlifyNavbarLinkSchema = z\n .object({\n href: z.string().min(1),\n icon: z.string().optional(),\n iconType: z.string().optional(),\n label: z.string().optional(),\n type: z.enum([\"discord\", \"github\"]).optional(),\n})\n .strict();\nconst MintlifyNavbarPrimarySchema = z\n .object({\n href: z.string().min(1),\n label: z.string().optional(),\n type: z.enum([\"button\", \"discord\", \"github\"]),\n})\n .strict();\nconst MintlifyNavbarSchema = z\n .object({\n links: z.array(MintlifyNavbarLinkSchema).optional(),\n primary: MintlifyNavbarPrimarySchema.optional(),\n})\n .strict();\nconst MintlifyNavigationGlobalSchema = z\n .object({\n anchors: z\n .array(z\n .object({\n anchor: z.string().min(1),\n color: z\n .object({\n dark: z.string().optional(),\n light: z.string().optional(),\n })\n .strict()\n .optional(),\n hidden: z.boolean().optional(),\n href: z.string().min(1),\n icon: z.string().optional(),\n iconType: z.string().optional(),\n })\n .strict())\n .optional(),\n})\n .strict();\nconst MintlifyNavigationGroupSchema = z\n .object({\n expanded: z.boolean().optional(),\n group: z.string().min(1),\n hidden: z.boolean().optional(),\n icon: z.string().optional(),\n pages: z.array(z.string()).optional(),\n root: z.string().optional(),\n tag: z.string().optional(),\n})\n .strict();\nconst MintlifyNavTabSchema = z\n .object({\n groups: z.array(MintlifyNavigationGroupSchema).optional(),\n hidden: z.boolean().optional(),\n href: z.string().min(1).optional(),\n icon: z.string().optional(),\n pages: z.array(z.string()).optional(),\n tab: z.string().min(1),\n})\n .strict();\nconst MintlifyNavigationSchema = z\n .object({\n global: MintlifyNavigationGlobalSchema.optional(),\n groups: z.array(MintlifyNavigationGroupSchema).optional(),\n languages: z\n .array(z\n .object({\n default: z.boolean().optional(),\n hidden: z.boolean().optional(),\n href: z.string().min(1),\n language: z.string().min(1),\n })\n .strict())\n .optional(),\n pages: z.array(z.string()).optional(),\n tabs: z.array(MintlifyNavTabSchema).optional(),\n versions: z\n .array(z\n .object({\n default: z.boolean().optional(),\n hidden: z.boolean().optional(),\n href: z.string().min(1),\n version: z.string().min(1),\n })\n .strict())\n .optional(),\n})\n .strict()\n .refine((value) => Boolean(value.groups?.length ||\n value.pages?.length ||\n value.tabs?.length ||\n value.languages?.length ||\n value.versions?.length), {\n message: \"navigation must define at least one of groups, pages, tabs, languages, or versions\",\n path: [],\n});\nconst MintlifyApiSchema = z\n .object({\n asyncapi: z\n .union([z.string(), z.array(z.string()), DocsOpenApiSourceSchema])\n .optional(),\n examples: z\n .object({\n autogenerate: z.boolean().optional(),\n defaults: z.enum([\"all\", \"required\"]).optional(),\n languages: z.array(z.string()).optional(),\n prefill: z.boolean().optional(),\n })\n .strict()\n .optional(),\n mdx: z\n .object({\n auth: z\n .object({\n method: z.enum([\"basic\", \"bearer\", \"cobo\", \"key\"]).optional(),\n name: z.string().optional(),\n })\n .strict()\n .optional(),\n server: z\n .union([z.string().min(1), z.array(z.string().min(1))])\n .optional(),\n })\n .strict()\n .optional(),\n openapi: z\n .union([z.string(), z.array(z.string()), DocsOpenApiSourceSchema])\n .optional(),\n params: z\n .object({\n expanded: z.enum([\"all\", \"closed\"]).optional(),\n })\n .strict()\n .optional(),\n playground: z\n .object({\n credentials: z.boolean().optional(),\n display: z.enum([\"auth\", \"interactive\", \"none\", \"simple\"]).optional(),\n proxy: z.boolean().optional(),\n })\n .strict()\n .optional(),\n url: z.literal(\"full\").optional(),\n})\n .strict();\nconst MintlifyAppearanceSchema = z\n .object({\n default: z.enum([\"dark\", \"light\", \"system\"]).optional(),\n strict: z.boolean().optional(),\n})\n .strict();\nconst MintlifyMetadataSchema = z\n .object({\n timestamp: z.boolean().optional(),\n})\n .strict();\nconst MintlifySearchSchema = z\n .object({\n prompt: z.string().optional(),\n})\n .strict();\nexport const ContextualBuiltinOptionSchema = z.enum([\n \"add-mcp\",\n \"aistudio\",\n \"assistant\",\n \"chatgpt\",\n \"claude\",\n \"copy\",\n \"cursor\",\n \"devin\",\n \"devin-mcp\",\n \"gemini\",\n \"grok\",\n \"mcp\",\n \"perplexity\",\n \"view\",\n \"vscode\",\n \"windsurf\",\n]);\nconst ContextualCustomHrefQuerySchema = z\n .object({\n key: z.string().min(1),\n value: z.string().min(1),\n})\n .strict();\nconst ContextualCustomHrefObjectSchema = z\n .object({\n base: z.string().min(1),\n query: z.array(ContextualCustomHrefQuerySchema),\n})\n .strict();\nexport const ContextualCustomOptionSchema = z\n .object({\n description: z.string().min(1),\n href: z.union([z.string().min(1), ContextualCustomHrefObjectSchema]),\n icon: z.string().min(1),\n iconType: z.string().optional(),\n title: z.string().min(1),\n})\n .strict();\nexport const ContextualOptionSchema = z.union([\n ContextualBuiltinOptionSchema,\n ContextualCustomOptionSchema,\n]);\nexport const DocsContextualSchema = z\n .object({\n display: z.enum([\"header\", \"toc\"]).optional(),\n options: z.array(ContextualOptionSchema),\n})\n .strict();\nexport const DocsConfigSchema = z\n .object({\n $schema: z.string().optional(),\n api: MintlifyApiSchema.optional(),\n appearance: MintlifyAppearanceSchema.optional(),\n contextual: DocsContextualSchema.optional(),\n description: z.string().optional(),\n favicon: MintlifyFaviconSchema.optional(),\n logo: MintlifyLogoSchema.optional(),\n metadata: MintlifyMetadataSchema.optional(),\n name: z.string().min(1),\n navbar: MintlifyNavbarSchema.optional(),\n navigation: MintlifyNavigationSchema,\n search: MintlifySearchSchema.optional(),\n seo: DocsSeoSchema.optional(),\n slug: SlugSchema.optional(),\n})\n .strict();\nexport const MintlifyDocsConfigSchema = DocsConfigSchema;\nexport const ContentTypeSchema = z.enum([\n \"site\",\n \"blog\",\n \"docs\",\n \"courses\",\n \"products\",\n \"notes\",\n \"forms\",\n \"sheets\",\n \"slides\",\n \"todos\",\n]);\nconst FrontmatterBaseSchema = z\n .object({\n description: z.string().optional(),\n hidden: z.boolean().optional(),\n title: z.string().min(1),\n})\n .passthrough();\nconst FrontmatterBlogSchema = FrontmatterBaseSchema.extend({\n date: z.string().min(1),\n tags: z.array(z.string()).optional(),\n}).passthrough();\nconst FrontmatterCoursesSchema = FrontmatterBaseSchema.extend({\n order: z.number(),\n}).passthrough();\nconst FrontmatterProductsSchema = FrontmatterBaseSchema.extend({\n currency: z.string().min(1),\n price: z.number(),\n sku: z.string().min(1),\n}).passthrough();\nconst FrontmatterNotesSchema = FrontmatterBaseSchema.extend({\n date: z.string().min(1),\n}).passthrough();\nconst FormFieldSchema = z\n .object({\n id: z.string().min(1),\n label: z.string().min(1),\n options: z.array(z.string()).optional(),\n required: z.boolean().optional(),\n type: z.string().min(1),\n})\n .passthrough();\nconst FrontmatterFormsSchema = FrontmatterBaseSchema.extend({\n fields: z.array(FormFieldSchema).min(1),\n}).passthrough();\nconst FrontmatterSheetsSchema = FrontmatterBaseSchema.extend({\n columns: z.array(z.string()).min(1),\n}).passthrough();\nconst FrontmatterTodosSchema = FrontmatterBaseSchema.extend({\n date: z.string().min(1),\n}).passthrough();\nexport const PageModeSchema = z.enum([\n \"default\",\n \"wide\",\n \"custom\",\n \"frame\",\n \"center\",\n]);\nconst FrontmatterDocsSchema = FrontmatterBaseSchema.extend({\n deprecated: z.boolean().optional(),\n hideApiMarker: z.boolean().optional(),\n hideFooterPagination: z.boolean().optional(),\n icon: z.string().optional(),\n iconType: z\n .enum([\n \"regular\",\n \"solid\",\n \"light\",\n \"thin\",\n \"sharp-solid\",\n \"duotone\",\n \"brands\",\n ])\n .optional(),\n keywords: z.array(z.string()).optional(),\n mode: PageModeSchema.optional(),\n noindex: z.boolean().optional(),\n sidebarTitle: z.string().optional(),\n tag: z.string().optional(),\n url: z.string().url().optional(),\n}).passthrough();\nexport const FrontmatterSchemaByType = {\n blog: FrontmatterBlogSchema,\n courses: FrontmatterCoursesSchema,\n docs: FrontmatterDocsSchema,\n forms: FrontmatterFormsSchema,\n notes: FrontmatterNotesSchema,\n products: FrontmatterProductsSchema,\n sheets: FrontmatterSheetsSchema,\n site: FrontmatterBaseSchema,\n slides: FrontmatterBaseSchema,\n todos: FrontmatterTodosSchema,\n};\nexport const CollectionIndexSchema = z\n .object({\n description: z.string().optional(),\n hidden: z.boolean().optional(),\n slug: z.string().min(1),\n title: z.string().optional(),\n})\n .strict();\nexport const CollectionSortSchema = z\n .object({\n direction: z.enum([\"asc\", \"desc\"]).optional(),\n field: z.enum([\"date\", \"order\", \"title\", \"price\"]).optional(),\n})\n .strict();\nexport const CollectionConfigSchema = z\n .object({\n id: z.string().min(1),\n index: CollectionIndexSchema.optional(),\n navigation: DocsNavigationSchema.optional(),\n openapi: z\n .union([z.string(), z.array(z.string()), DocsOpenApiSourceSchema])\n .optional(),\n root: z.string().optional(),\n slugPrefix: z.string().optional(),\n sort: CollectionSortSchema.optional(),\n type: ContentTypeSchema,\n})\n .strict();\nexport const SiteConfigSchema = z\n .object({\n collections: z.array(CollectionConfigSchema).min(1),\n colors: DocsColorsSchema.optional(),\n contextual: DocsContextualSchema.optional(),\n description: z.string().optional(),\n favicon: UrlOrPathSchema.optional(),\n features: DocsFeatureFlagsSchema.optional(),\n fonts: DocsFontsSchema.optional(),\n logo: DocsLogoSchema.optional(),\n metadata: z\n .object({\n defaultTitle: z.string().optional(),\n ogImage: UrlOrPathSchema.optional(),\n titleTemplate: z.string().optional(),\n })\n .strict()\n .optional(),\n name: z.string().min(1),\n navigation: DocsNavigationSchema.optional(),\n openapiProxy: DocsOpenApiProxySchema.optional(),\n scripts: DocsScriptsSchema.optional(),\n seo: DocsSeoSchema.optional(),\n slug: SlugSchema.optional(),\n theme: z.string().optional(),\n})\n .strict();\n","import { DocsConfigSchema, FrontmatterSchemaByType, SiteConfigSchema, } from \"@repo/models\";\nconst formatIssues = (issues) => issues.map((issue) => {\n const path = issue.path.length ? issue.path.map(String).join(\".\") : \"root\";\n return `${path}: ${issue.message}`;\n});\nexport const validateSiteConfig = (input) => {\n const result = SiteConfigSchema.safeParse(input);\n if (result.success) {\n return { data: result.data, success: true };\n }\n const issues = formatIssues(result.error.issues);\n return { errors: issues, success: false };\n};\nexport const validateDocsConfig = (input) => {\n const result = DocsConfigSchema.safeParse(input);\n if (result.success) {\n return { data: result.data, success: true };\n }\n const issues = formatIssues(result.error.issues);\n return { errors: issues, success: false };\n};\nexport const validateFrontmatter = (type, input) => {\n const schema = FrontmatterSchemaByType[type];\n const result = schema.safeParse(input);\n if (result.success) {\n return { data: result.data, success: true };\n }\n const issues = formatIssues(result.error.issues);\n return { errors: issues, success: false };\n};\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { normalizePath, shouldIgnoreRootDocsFile } from \"@repo/common\";\nconst IGNORED_DIRECTORIES = new Set([\"app\", \"lib\", \"node_modules\", \"public\"]);\nconst isNotFoundError = (error) => Boolean(error &&\n typeof error === \"object\" &&\n \"code\" in error &&\n error.code === \"ENOENT\");\nconst isWithinRoot = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path.sep}`);\nconst resolveWithinRoot = (root, relativePath) => {\n const normalized = normalizePath(relativePath);\n const absolutePath = path.resolve(root, normalized);\n if (!isWithinRoot(root, absolutePath)) {\n throw new Error(`Path \"${relativePath}\" escapes the content source root.`);\n }\n return absolutePath;\n};\nconst walkFiles = async (directory, prefix) => {\n const entries = await fs.readdir(directory, { withFileTypes: true });\n const files = [];\n for (const entry of entries) {\n if (entry.name.startsWith(\".\")) {\n continue;\n }\n const absolutePath = path.join(directory, entry.name);\n const relativePath = prefix ? path.join(prefix, entry.name) : entry.name;\n if (entry.isDirectory()) {\n if (IGNORED_DIRECTORIES.has(entry.name)) {\n continue;\n }\n files.push(...(await walkFiles(absolutePath, relativePath)));\n continue;\n }\n if (entry.isFile()) {\n if (!prefix && shouldIgnoreRootDocsFile(entry.name)) {\n continue;\n }\n files.push(normalizePath(relativePath));\n }\n }\n return files;\n};\nexport class FsContentSource {\n root;\n constructor(root) {\n this.root = path.resolve(root);\n }\n async readFile(relativePath) {\n return await fs.readFile(resolveWithinRoot(this.root, relativePath), \"utf8\");\n }\n async listFiles(directory) {\n return await walkFiles(resolveWithinRoot(this.root, directory), \"\");\n }\n async exists(relativePath) {\n try {\n await fs.access(resolveWithinRoot(this.root, relativePath));\n return true;\n }\n catch (error) {\n if (isNotFoundError(error)) {\n return false;\n }\n throw error;\n }\n }\n // oxlint-disable-next-line eslint/class-methods-use-this\n resolveUrl() {\n return null;\n }\n}\nexport const createFsSource = (root) => new FsContentSource(root);\n","import path from \"node:path\";\nimport { ensureArray, normalizePath, slugify } from \"@repo/common\";\nimport { PageModeSchema } from \"@repo/models\";\nimport { extractOpenApiOperations, openApiIdentifier, openApiSlug, parseOpenApiSpec, } from \"@repo/prebuild\";\nimport { validateDocsConfig, validateFrontmatter, validateSiteConfig, } from \"@repo/validation\";\nimport YAML from \"yaml\";\nexport { BlobContentSource, createBlobSource } from \"./blob-source.js\";\nexport { createFsSource, FsContentSource } from \"./fs-source.js\";\nexport const PREBUILT_INDEX_PATH = \"_content-index.json\";\nexport const PREBUILT_OPENAPI_INDEX_PATH = \"_openapi-index.json\";\nexport const PREBUILT_SEARCH_INDEX_PATH = \"_search-index.json\";\nexport const PREBUILT_TOC_INDEX_PATH = \"_toc-index.json\";\nexport const PREBUILT_UTILITY_INDEX_PATH = \"_utility-index.json\";\nexport const PREBUILT_UTILITY_SITEMAP_PATH = \"_utility/sitemap.xml\";\nexport const PREBUILT_UTILITY_LLMS_PATH = \"_utility/llms.txt\";\nexport const PREBUILT_UTILITY_LLMS_FULL_PATH = \"_utility/llms-full.txt\";\nexport const PREBUILT_UTILITY_SKILLS_INDEX_PATH = \"_utility/skills-index.json\";\nexport const PREBUILT_UTILITY_SKILLS_MD_PREFIX = \"_utility/skills/\";\nexport const UTILITY_DOCS_ROOT_TOKEN = \"__BLODEMD_DOCS_ROOT__\";\nexport const LEGACY_PROJECT_NAME_FALLBACK_WARNING = \"docs.json.slug is recommended. Falling back to docs.json.name as the deployment slug is deprecated.\";\nconst validModes = new Set(PageModeSchema.options);\nexport const buildPageMetadataMap = (index) => {\n const map = new Map();\n for (const entry of index.entries) {\n if (entry.kind !== \"entry\") {\n continue;\n }\n const fm = entry.frontmatter;\n const meta = {\n title: entry.title,\n };\n const hasFields = true;\n if (typeof fm.sidebarTitle === \"string\") {\n meta.sidebarTitle = fm.sidebarTitle;\n }\n if (typeof fm.icon === \"string\") {\n meta.icon = fm.icon;\n }\n if (typeof fm.iconType === \"string\") {\n meta.iconType = fm.iconType;\n }\n if (typeof fm.tag === \"string\") {\n meta.tag = fm.tag;\n }\n if (typeof fm.hidden === \"boolean\") {\n meta.hidden = fm.hidden;\n }\n if (typeof fm.deprecated === \"boolean\") {\n meta.deprecated = fm.deprecated;\n }\n if (typeof fm.url === \"string\") {\n meta.url = fm.url;\n }\n if (typeof fm.mode === \"string\" && validModes.has(fm.mode)) {\n meta.mode = fm.mode;\n }\n if (typeof fm.noindex === \"boolean\") {\n meta.noindex = fm.noindex;\n }\n if (typeof fm.hideFooterPagination === \"boolean\") {\n meta.hideFooterPagination = fm.hideFooterPagination;\n }\n if (typeof fm.hideApiMarker === \"boolean\") {\n meta.hideApiMarker = fm.hideApiMarker;\n }\n if (Array.isArray(fm.keywords)) {\n meta.keywords = fm.keywords;\n }\n if (hasFields) {\n map.set(entry.slug, meta);\n }\n }\n return map;\n};\nconst DOCS_CONFIG_FILE = \"docs.json\";\nconst DOC_FILE_EXTENSION_REGEX = /\\.(mdx|md)$/;\nconst FRONTMATTER_REGEX = /^---\\s*\\n([\\s\\S]*?)\\n---\\s*\\n?/;\nconst INDEX_SUFFIX = \"/index\";\nconst titleFromSlug = (slug) => {\n const clean = slug.replaceAll(\"-\", \" \").split(\"/\").pop() ?? slug;\n if (clean === \"index\") {\n return \"Overview\";\n }\n return clean.replaceAll(/\\b\\w/g, (char) => char.toUpperCase());\n};\nconst parseFrontmatter = (source) => {\n const match = FRONTMATTER_REGEX.exec(source);\n if (!match) {\n return { body: source, frontmatter: {} };\n }\n const raw = match[1] ?? \"\";\n const data = YAML.parse(raw) ?? {};\n const body = source.slice(match[0].length);\n return { body, frontmatter: data };\n};\nconst slugFromFile = (relativePath) => {\n const clean = normalizePath(relativePath);\n const withoutExt = clean.replace(DOC_FILE_EXTENSION_REGEX, \"\");\n if (withoutExt.endsWith(INDEX_SUFFIX)) {\n const trimmed = withoutExt.slice(0, -INDEX_SUFFIX.length);\n return trimmed.length ? trimmed : \"index\";\n }\n return withoutExt.length ? withoutExt : \"index\";\n};\nconst defaultLinkLabel = (input) => {\n if (input.label) {\n return input.label;\n }\n if (input.type === \"github\") {\n return \"GitHub\";\n }\n if (input.type === \"discord\") {\n return \"Discord\";\n }\n try {\n return new URL(input.href).hostname;\n }\n catch {\n return input.href;\n }\n};\n// oxlint-disable-next-line eslint/complexity\nconst mapDocsConfig = (docs) => {\n const navigation = {\n global: docs.navbar?.links?.length || docs.navigation.global?.anchors?.length\n ? {\n anchors: docs.navigation.global?.anchors?.map((anchor) => ({\n href: anchor.href,\n label: anchor.anchor,\n })),\n links: docs.navbar?.links?.map((link) => ({\n href: link.href,\n label: defaultLinkLabel(link),\n })),\n }\n : undefined,\n groups: docs.navigation.groups?.map((group) => ({\n expanded: group.expanded,\n group: group.group,\n hidden: group.hidden,\n pages: group.root\n ? [\n group.root,\n ...(group.pages ?? []).filter((page) => page !== group.root),\n ]\n : group.pages,\n })),\n languages: docs.navigation.languages?.map((language) => ({\n label: language.language,\n locale: language.language,\n url: language.href,\n })),\n pages: docs.navigation.pages,\n tabs: docs.navigation.tabs?.map((tab) => ({\n groups: tab.groups?.map((group) => ({\n expanded: group.expanded,\n group: group.group,\n hidden: group.hidden,\n pages: group.root\n ? [\n group.root,\n ...(group.pages ?? []).filter((page) => page !== group.root),\n ]\n : group.pages,\n })),\n href: tab.href,\n icon: tab.icon,\n label: tab.tab,\n pages: tab.pages,\n })),\n versions: docs.navigation.versions?.map((version) => ({\n label: version.version,\n url: version.href,\n })),\n };\n return {\n collections: [\n {\n id: \"docs\",\n navigation,\n openapi: docs.api?.openapi,\n root: \"\",\n type: \"docs\",\n },\n ],\n contextual: docs.contextual,\n description: docs.description,\n favicon: typeof docs.favicon === \"string\" ? docs.favicon : docs.favicon?.light,\n features: {\n rightToc: true,\n search: true,\n themeToggle: docs.appearance?.strict !== true,\n toc: true,\n },\n logo: docs.logo\n ? {\n dark: typeof docs.logo === \"string\" ? docs.logo : docs.logo.dark,\n href: typeof docs.logo === \"string\" ? undefined : docs.logo.href,\n light: typeof docs.logo === \"string\" ? docs.logo : docs.logo.light,\n }\n : undefined,\n name: docs.name,\n navigation,\n openapiProxy: {\n enabled: docs.api?.playground?.proxy !== false &&\n Boolean(docs.api?.openapi || docs.api?.asyncapi),\n },\n seo: docs.seo,\n slug: docs.slug,\n };\n};\nconst getProjectWarnings = (config) => config.slug ? [] : [LEGACY_PROJECT_NAME_FALLBACK_WARNING];\nconst readJsonConfig = async (source, relativePath) => JSON.parse(await source.readFile(relativePath));\nconst normalizeRefPath = (baseDirectory, reference) => {\n if (reference.startsWith(\"/\") ||\n reference.startsWith(\"\\\\\") ||\n reference.startsWith(\"http://\") ||\n reference.startsWith(\"https://\")) {\n throw new Error(`Invalid $ref \"${reference}\". Only relative JSON files are supported.`);\n }\n const normalized = normalizePath(path.posix.join(baseDirectory, reference));\n if (!normalized ||\n normalized === \".\" ||\n normalized.startsWith(\"../\") ||\n normalized.includes(\"/../\")) {\n throw new Error(`Invalid $ref \"${reference}\".`);\n }\n return normalized;\n};\nconst resolveJsonRefs = async (source, value, baseDirectory, seen) => {\n if (Array.isArray(value)) {\n return await Promise.all(value.map((item) => resolveJsonRefs(source, item, baseDirectory, seen)));\n }\n if (!value || typeof value !== \"object\") {\n return value;\n }\n const record = value;\n const reference = record.$ref;\n if (typeof reference === \"string\") {\n const resolvedPath = normalizeRefPath(baseDirectory, reference);\n if (seen.has(resolvedPath)) {\n throw new Error(`Circular $ref detected for \"${resolvedPath}\".`);\n }\n const nextSeen = new Set(seen);\n // oxlint-disable-next-line eslint-plugin-unicorn/no-immediate-mutation\n nextSeen.add(resolvedPath);\n const referenced = await readJsonConfig(source, resolvedPath);\n const referencedValue = await resolveJsonRefs(source, referenced, path.posix.dirname(resolvedPath) === \".\"\n ? \"\"\n : normalizePath(path.posix.dirname(resolvedPath)), nextSeen);\n const siblingEntries = Object.entries(record).filter(([key]) => key !== \"$ref\");\n if (!siblingEntries.length ||\n !referencedValue ||\n typeof referencedValue !== \"object\" ||\n Array.isArray(referencedValue)) {\n return referencedValue;\n }\n const siblingValue = await resolveJsonRefs(source, Object.fromEntries(siblingEntries), baseDirectory, seen);\n return {\n ...referencedValue,\n ...siblingValue,\n };\n }\n const resolvedEntries = await Promise.all(Object.entries(record).map(async ([key, entryValue]) => [\n key,\n await resolveJsonRefs(source, entryValue, baseDirectory, seen),\n ]));\n return Object.fromEntries(resolvedEntries);\n};\nconst readResolvedJsonConfig = async (source, relativePath) => await resolveJsonRefs(source, await readJsonConfig(source, relativePath), path.posix.dirname(relativePath) === \".\"\n ? \"\"\n : normalizePath(path.posix.dirname(relativePath)), new Set([relativePath]));\nconst loadDocsConfig = async (source) => {\n if (!(await source.exists(DOCS_CONFIG_FILE))) {\n return null;\n }\n try {\n const parsed = await readResolvedJsonConfig(source, DOCS_CONFIG_FILE);\n // Try SiteConfig format first (has collections, theme, colors, etc.)\n const siteResult = validateSiteConfig(parsed);\n if (siteResult.success) {\n return {\n config: siteResult.data,\n ok: true,\n warnings: getProjectWarnings(siteResult.data),\n };\n }\n // Fall back to DocsConfig format (Mintlify-compatible) and map to SiteConfig\n const docsResult = validateDocsConfig(parsed);\n if (docsResult.success) {\n return {\n config: mapDocsConfig(docsResult.data),\n ok: true,\n warnings: getProjectWarnings(docsResult.data),\n };\n }\n return { errors: docsResult.errors, ok: false };\n }\n catch (error) {\n return {\n errors: [\n error instanceof Error\n ? error.message\n : `Failed to load ${DOCS_CONFIG_FILE}`,\n ],\n ok: false,\n };\n }\n};\nexport const loadSiteConfig = async (source) => {\n const docsConfig = await loadDocsConfig(source);\n if (docsConfig) {\n return docsConfig;\n }\n return {\n errors: [`${DOCS_CONFIG_FILE} not found.`],\n ok: false,\n };\n};\nexport const loadContentSource = async (source, relativePath) => await source.readFile(relativePath);\nconst listContentFiles = async (source, directory) => {\n const files = await source.listFiles(directory);\n return files.filter((file) => DOC_FILE_EXTENSION_REGEX.test(file));\n};\nconst sortDefaults = {\n blog: { direction: \"desc\", field: \"date\" },\n courses: { direction: \"asc\", field: \"order\" },\n docs: { direction: \"asc\", field: \"title\" },\n forms: { direction: \"asc\", field: \"title\" },\n notes: { direction: \"desc\", field: \"date\" },\n products: { direction: \"asc\", field: \"title\" },\n sheets: { direction: \"asc\", field: \"title\" },\n site: { direction: \"asc\", field: \"title\" },\n slides: { direction: \"asc\", field: \"title\" },\n todos: { direction: \"desc\", field: \"date\" },\n};\nconst normalizeSortValue = (value) => {\n if (typeof value === \"number\") {\n return value;\n }\n if (typeof value === \"string\") {\n const timestamp = Date.parse(value);\n if (!Number.isNaN(timestamp)) {\n return timestamp;\n }\n return value.toLowerCase();\n }\n return null;\n};\nconst compareValues = (a, b, direction) => {\n const left = normalizeSortValue(a);\n const right = normalizeSortValue(b);\n if (left === null && right === null) {\n return 0;\n }\n if (left === null) {\n return 1;\n }\n if (right === null) {\n return -1;\n }\n const multiplier = direction === \"desc\" ? -1 : 1;\n if (typeof left === \"number\" && typeof right === \"number\") {\n return (left - right) * multiplier;\n }\n if (left < right) {\n return -1 * multiplier;\n }\n if (left > right) {\n return 1 * multiplier;\n }\n return 0;\n};\nconst autoIndexTypes = new Set([\n \"blog\",\n \"courses\",\n \"products\",\n \"notes\",\n \"forms\",\n \"sheets\",\n \"slides\",\n \"todos\",\n]);\nconst getCollectionIndex = (collection, slugPrefix) => {\n if (collection.index) {\n return collection.index;\n }\n if (autoIndexTypes.has(collection.type)) {\n const slug = slugPrefix || collection.id;\n return {\n slug,\n title: titleFromSlug(slug),\n };\n }\n return null;\n};\nconst addEntry = (entry, index, errors) => {\n if (index.bySlug.has(entry.slug)) {\n errors.push(`slug \"${entry.slug}\" is defined more than once`);\n return;\n }\n index.entries.push(entry);\n index.bySlug.set(entry.slug, entry);\n};\nconst resolveEntrySlug = (relativeSlug, slugPrefix) => {\n if (!slugPrefix) {\n return relativeSlug;\n }\n if (relativeSlug === \"index\") {\n return slugPrefix;\n }\n return normalizePath(`${slugPrefix}/${relativeSlug}`);\n};\nconst buildEntryFromFile = async ({ collection, errors, file, root, slugPrefix, source, }) => {\n const sourcePath = root\n ? normalizePath(path.join(root, file))\n : normalizePath(file);\n let entrySource = \"\";\n try {\n entrySource = await source.readFile(sourcePath);\n }\n catch (error) {\n errors.push(error instanceof Error ? error.message : `Failed to read ${sourcePath}`);\n return null;\n }\n const { frontmatter } = parseFrontmatter(entrySource);\n const frontmatterResult = validateFrontmatter(collection.type, frontmatter);\n if (!frontmatterResult.success) {\n for (const issue of frontmatterResult.errors) {\n errors.push(`${sourcePath}: ${issue}`);\n }\n }\n const resolvedFrontmatter = frontmatterResult.success\n ? frontmatterResult.data\n : frontmatter;\n const relativeSlug = slugFromFile(file);\n const slug = resolveEntrySlug(relativeSlug, slugPrefix);\n const title = typeof resolvedFrontmatter?.title === \"string\"\n ? resolvedFrontmatter.title\n : titleFromSlug(slug);\n const description = typeof resolvedFrontmatter?.description === \"string\"\n ? resolvedFrontmatter.description\n : undefined;\n const hidden = typeof resolvedFrontmatter?.hidden === \"boolean\"\n ? resolvedFrontmatter.hidden\n : undefined;\n return {\n collectionId: collection.id,\n description,\n frontmatter: resolvedFrontmatter,\n hidden: hidden || undefined,\n kind: \"entry\",\n relativePath: sourcePath,\n slug,\n sourcePath,\n title,\n type: collection.type,\n };\n};\nexport const buildContentIndex = async (source, config) => {\n const errors = [];\n const index = {\n byCollection: new Map(),\n bySlug: new Map(),\n entries: [],\n errors,\n };\n for (const collection of config.collections) {\n const root = normalizePath(collection.root ?? \"\");\n const slugPrefix = normalizePath(collection.slugPrefix ?? \"\");\n let files = [];\n try {\n files = await listContentFiles(source, root);\n }\n catch (error) {\n errors.push(error instanceof Error ? error.message : `Failed to read ${root || \".\"}`);\n continue;\n }\n const collectionEntries = [];\n const resolvedEntries = await Promise.all(files.map(async (file) => await buildEntryFromFile({\n collection,\n errors,\n file,\n root,\n slugPrefix,\n source,\n })));\n for (const entry of resolvedEntries) {\n if (!entry) {\n continue;\n }\n collectionEntries.push(entry);\n addEntry(entry, index, errors);\n }\n const sortConfig = {\n ...sortDefaults[collection.type],\n ...collection.sort,\n };\n const sortField = sortConfig.field ?? \"title\";\n const sortDirection = sortConfig.direction ?? \"asc\";\n collectionEntries.sort((left, right) => {\n const leftValue = left.kind === \"entry\"\n ? left.frontmatter[sortField]\n : undefined;\n const rightValue = right.kind === \"entry\"\n ? right.frontmatter[sortField]\n : undefined;\n return compareValues(leftValue, rightValue, sortDirection);\n });\n index.byCollection.set(collection.id, collectionEntries);\n const collectionIndex = getCollectionIndex(collection, slugPrefix);\n if (collectionIndex) {\n const indexEntry = {\n collectionId: collection.id,\n description: collectionIndex.description,\n kind: \"index\",\n slug: collectionIndex.slug,\n title: collectionIndex.title ?? titleFromSlug(collectionIndex.slug),\n type: collection.type,\n };\n addEntry(indexEntry, index, errors);\n }\n }\n return index;\n};\nconst NEWLINE_REGEX = /\\r?\\n/;\nconst HEADING_REGEX = /^(#{2,4})\\s+(.*)$/;\nconst LEADING_H1_REGEX = /^#\\s+([^\\r\\n]+)(?:\\r?\\n(?:\\r?\\n)?)?/;\nexport const extractToc = (source) => {\n const withoutCode = source.replaceAll(/```[\\s\\S]*?```/g, \"\");\n const lines = withoutCode.split(NEWLINE_REGEX);\n const toc = [];\n for (const line of lines) {\n const match = HEADING_REGEX.exec(line.trim());\n if (!match) {\n continue;\n }\n const [, hashes = \"\", heading = \"\"] = match;\n if (!(hashes && heading)) {\n continue;\n }\n toc.push({\n id: slugify(heading.trim()),\n level: hashes.length,\n title: heading.trim(),\n });\n }\n return toc;\n};\nconst stripMatchingLeadingH1 = (source, title) => {\n const trimmed = source.trimStart();\n const match = LEADING_H1_REGEX.exec(trimmed);\n if (!match) {\n return trimmed.trim();\n }\n const [headingLine = \"\", headingTitle = \"\"] = match;\n if (slugify(headingTitle) !== slugify(title)) {\n return trimmed.trim();\n }\n return trimmed.slice(headingLine.length).trim();\n};\nexport const formatMarkdownPage = (title, source) => {\n const content = stripMatchingLeadingH1(source, title);\n if (!content) {\n return `# ${title}`;\n }\n return `# ${title}\\n\\n${content}`;\n};\nexport const formatMarkdownPageSection = (title, url, source) => {\n const content = stripMatchingLeadingH1(source, title);\n if (!content) {\n return `# ${title} (${url})`;\n }\n return `# ${title} (${url})\\n\\n${content}`;\n};\nconst shouldIncludeSearchEntry = (entry, pageMetadataMap, config) => {\n const pageMeta = pageMetadataMap.get(entry.slug);\n if (pageMeta?.hidden || pageMeta?.noindex) {\n return false;\n }\n if (config.seo?.indexing !== \"all\" &&\n entry.kind === \"entry\" &&\n entry.hidden === true) {\n return false;\n }\n return true;\n};\nconst stripFrontmatter = (source) => parseFrontmatter(source).body.trim();\nconst getDocsCollection = (config) => config.collections.find((collection) => collection.type === \"docs\");\nconst getDocsNavigation = (config) => getDocsCollection(config)?.navigation ?? config.navigation;\nconst getDocsCollectionWithNavigation = (config) => {\n const docsCollection = getDocsCollection(config);\n const docsNavigation = getDocsNavigation(config);\n return docsCollection &&\n docsNavigation &&\n docsCollection.navigation !== docsNavigation\n ? { ...docsCollection, navigation: docsNavigation }\n : docsCollection;\n};\nconst getOpenApiSourceKey = (source) => `${source.source}::${source.directory ?? \"\"}::${(source.include ?? []).join(\"|\")}`;\nconst toOpenApiSourceObject = (value) => {\n if (typeof value === \"string\") {\n return { source: value };\n }\n return value;\n};\nconst collectOpenApiSources = (collection) => {\n const sources = [];\n for (const item of ensureArray(collection?.openapi)) {\n if (!item) {\n continue;\n }\n sources.push(toOpenApiSourceObject(item));\n }\n const groups = collection?.navigation?.groups ?? [];\n for (const group of groups) {\n if (!group.openapi) {\n continue;\n }\n sources.push(toOpenApiSourceObject(group.openapi));\n }\n const seen = new Set();\n return sources.filter((source) => {\n const key = getOpenApiSourceKey(source);\n if (seen.has(key)) {\n return false;\n }\n seen.add(key);\n return true;\n });\n};\nconst formatOpenApiPageContent = (operation) => {\n const parts = [`Method: ${operation.method}`, `Path: ${operation.path}`];\n if (operation.description) {\n parts.push(operation.description);\n }\n if (operation.tags.length) {\n parts.push(`Tags: ${operation.tags.join(\", \")}`);\n }\n if (operation.parameters.length) {\n parts.push(`Parameters:\\n${JSON.stringify(operation.parameters, null, 2)}`);\n }\n if (operation.requestBody) {\n parts.push(`Request Body:\\n${JSON.stringify(operation.requestBody, null, 2)}`);\n }\n if (operation.responses) {\n parts.push(`Responses:\\n${JSON.stringify(operation.responses, null, 2)}`);\n }\n return parts.join(\"\\n\\n\");\n};\nconst getGroupedOpenApiSourceKey = (source) => getOpenApiSourceKey(toOpenApiSourceObject(source));\nconst collectUtilityOpenApiPages = (pagesByIdentifier, pagesBySource, operations, directory, openApiSource, slugPrefix) => {\n const sourceKey = getOpenApiSourceKey(openApiSource);\n const includeIdentifiers = openApiSource.include?.length\n ? new Set(openApiSource.include)\n : null;\n for (const operation of operations) {\n const identifier = openApiIdentifier(operation.method, operation.path);\n if (includeIdentifiers && !includeIdentifiers.has(identifier)) {\n continue;\n }\n const baseSlug = normalizePath(openApiSlug(operation.method, operation.path, directory));\n const slug = slugPrefix\n ? normalizePath(`${slugPrefix}/${baseSlug}`)\n : baseSlug;\n const page = {\n content: formatOpenApiPageContent(operation),\n description: operation.description,\n identifier,\n slug,\n sourceKey,\n title: operation.summary ?? identifier,\n };\n pagesByIdentifier.set(identifier, page);\n if (!pagesBySource.has(sourceKey)) {\n pagesBySource.set(sourceKey, []);\n }\n pagesBySource.get(sourceKey)?.push(page);\n }\n};\nconst addUtilityPagesFromSourceKey = (pages, pagesBySource, sourceKey) => {\n for (const page of pagesBySource.get(sourceKey) ?? []) {\n pages.set(page.slug, page);\n }\n};\nconst addReferencedUtilityPages = (pages, pagesByIdentifier, pageReferences, hiddenPages, groupHidden = false) => {\n for (const pageReference of pageReferences ?? []) {\n if (groupHidden || hiddenPages.has(pageReference)) {\n continue;\n }\n const page = pagesByIdentifier.get(pageReference);\n if (page) {\n pages.set(page.slug, page);\n }\n }\n};\nconst buildUtilityOpenApiPages = async (config, collection, source) => {\n if (!collection || collection.type !== \"docs\") {\n return [];\n }\n const docsNavigation = getDocsNavigation(config);\n const hiddenPages = new Set(docsNavigation?.hidden);\n const slugPrefix = normalizePath(collection.slugPrefix ?? \"\");\n const byIdentifier = new Map();\n const bySource = new Map();\n const pages = new Map();\n const sources = collectOpenApiSources(collection);\n const resolved = await Promise.all(sources.map(async (item) => {\n const rawSpec = await source.readFile(item.source);\n const spec = parseOpenApiSpec(rawSpec, item.source);\n const directory = item.directory ?? \"api\";\n const { operations } = extractOpenApiOperations(spec, directory);\n return { directory, operations, source: item };\n }));\n for (const { directory, operations, source: openApiSource } of resolved) {\n collectUtilityOpenApiPages(byIdentifier, bySource, operations, directory, openApiSource, slugPrefix);\n }\n for (const openApiSource of ensureArray(collection.openapi)) {\n if (!openApiSource) {\n continue;\n }\n addUtilityPagesFromSourceKey(pages, bySource, getGroupedOpenApiSourceKey(openApiSource));\n }\n for (const group of docsNavigation?.groups ?? []) {\n const groupHidden = group.hidden === true;\n addReferencedUtilityPages(pages, byIdentifier, group.pages, hiddenPages, groupHidden);\n if (groupHidden || !group.openapi) {\n continue;\n }\n addUtilityPagesFromSourceKey(pages, bySource, getGroupedOpenApiSourceKey(group.openapi));\n }\n addReferencedUtilityPages(pages, byIdentifier, docsNavigation?.pages, hiddenPages);\n return [...pages.values()];\n};\nexport const buildSearchIndex = (index, config, utilityIndex) => {\n const pageMetadataMap = buildPageMetadataMap(index);\n const items = new Map();\n for (const page of utilityIndex?.pages ?? []) {\n items.set(page.slug, {\n path: page.slug,\n title: page.title,\n });\n }\n for (const entry of index.entries) {\n if (!shouldIncludeSearchEntry(entry, pageMetadataMap, config)) {\n continue;\n }\n const pageMeta = pageMetadataMap.get(entry.slug);\n items.set(entry.slug, {\n href: pageMeta?.url,\n path: entry.slug,\n title: pageMeta?.sidebarTitle ?? entry.title,\n });\n }\n return [...items.values()];\n};\nexport const buildUtilityIndex = async (index, source, config) => {\n const pageMetadataMap = buildPageMetadataMap(index);\n const pages = new Map();\n for (const entry of index.entries) {\n if (entry.kind !== \"entry\") {\n continue;\n }\n if (!shouldIncludeSearchEntry(entry, pageMetadataMap, config)) {\n continue;\n }\n const rawContent = await source.readFile(entry.relativePath);\n pages.set(entry.slug, {\n content: stripFrontmatter(rawContent),\n description: entry.description,\n slug: entry.slug,\n title: entry.title,\n });\n }\n for (const page of await buildUtilityOpenApiPages(config, getDocsCollectionWithNavigation(config), source)) {\n pages.set(page.slug, page);\n }\n const sortedPages = [...pages.values()];\n // oxlint-disable-next-line eslint-plugin-unicorn/no-array-sort\n sortedPages.sort((left, right) => left.slug.localeCompare(right.slug));\n return {\n description: config.description,\n name: config.name,\n pages: sortedPages,\n slug: config.slug,\n };\n};\nconst toUtilityDocPath = (value) => {\n const clean = normalizePath(value);\n if (!clean || clean === \"index\") {\n return \"/\";\n }\n return `/${clean}`;\n};\nconst toUtilityTemplatedDocUrl = (value) => `${UTILITY_DOCS_ROOT_TOKEN}${toUtilityDocPath(value)}`;\nexport const getPrebuiltUtilityLlmPagePath = (slug) => {\n const normalized = normalizePath(slug);\n return `_utility/llms-pages/${normalized || \"index\"}.mdx`;\n};\nexport const buildUtilityArtifacts = (index) => {\n const llmsLines = [\n `# ${index.name}`,\n index.description ? `> ${index.description}` : null,\n \"\",\n `Sitemap: ${toUtilityTemplatedDocUrl(\"sitemap.xml\")}`,\n `Skills: ${UTILITY_DOCS_ROOT_TOKEN}/.well-known/skills/index.json`,\n \"\",\n \"## Docs\",\n ...index.pages.map((page) => {\n const description = page.description ? `: ${page.description}` : \"\";\n return `- [${page.title}](${toUtilityTemplatedDocUrl(page.slug)})${description}`;\n }),\n ];\n const sitemap = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n${index.pages\n .map((page) => ` <url><loc>${toUtilityTemplatedDocUrl(page.slug)}</loc></url>`)\n .join(\"\\n\")}\n</urlset>`;\n const llmsFull = index.pages\n .map((page) => formatMarkdownPageSection(page.title, toUtilityTemplatedDocUrl(page.slug), page.content))\n .join(\"\\n\\n\");\n const skillSlug = index.slug ?? slugify(index.name);\n const skillDescription = `${index.name} documentation. ${index.description ?? \"\"}`.trim();\n const skillsIndex = JSON.stringify({\n skills: [\n {\n description: skillDescription,\n files: [\"SKILL.md\"],\n name: skillSlug,\n },\n ],\n }, null, 2);\n const topPages = index.pages.slice(0, 20);\n const skillMdLines = [\n \"---\",\n `name: ${skillSlug}`,\n `description: ${skillDescription} Use when working with ${index.name}, answering questions about its features, or helping users follow its guides.`,\n \"---\",\n \"\",\n `# ${index.name}`,\n \"\",\n index.description ? `${index.description}\\n` : \"\",\n \"## Documentation\",\n \"\",\n `- Full docs index: ${UTILITY_DOCS_ROOT_TOKEN}/llms.txt`,\n `- Complete docs content: ${UTILITY_DOCS_ROOT_TOKEN}/llms-full.txt`,\n \"- Append `.md` to any page URL for raw markdown\",\n \"\",\n \"## Key Pages\",\n \"\",\n ...topPages.map((page) => {\n const desc = page.description ? ` - ${page.description}` : \"\";\n return `- [${page.title}](${toUtilityTemplatedDocUrl(page.slug)})${desc}`;\n }),\n ];\n return [\n {\n content: sitemap,\n contentType: \"application/xml; charset=utf-8\",\n path: PREBUILT_UTILITY_SITEMAP_PATH,\n },\n {\n content: llmsLines.filter((line) => line !== null).join(\"\\n\"),\n contentType: \"text/plain; charset=utf-8\",\n path: PREBUILT_UTILITY_LLMS_PATH,\n },\n {\n content: llmsFull,\n contentType: \"text/plain; charset=utf-8\",\n path: PREBUILT_UTILITY_LLMS_FULL_PATH,\n },\n {\n content: skillsIndex,\n contentType: \"application/json; charset=utf-8\",\n path: PREBUILT_UTILITY_SKILLS_INDEX_PATH,\n },\n {\n content: skillMdLines.filter((line) => line !== null).join(\"\\n\"),\n contentType: \"text/markdown; charset=utf-8\",\n path: `${PREBUILT_UTILITY_SKILLS_MD_PREFIX}${skillSlug}/SKILL.md`,\n },\n ...index.pages.map((page) => ({\n content: formatMarkdownPage(page.title, page.content),\n contentType: \"text/markdown; charset=utf-8\",\n path: getPrebuiltUtilityLlmPagePath(page.slug),\n })),\n ];\n};\nexport const buildTocIndex = async (index, source) => {\n const itemsBySlug = new Map();\n for (const entry of index.entries) {\n if (entry.kind !== \"entry\") {\n continue;\n }\n const rawContent = await source.readFile(entry.relativePath);\n itemsBySlug.set(entry.slug, extractToc(rawContent));\n }\n return itemsBySlug;\n};\nexport const serializeContentIndex = (index) => JSON.stringify({\n collections: Object.fromEntries(index.byCollection),\n entries: index.entries,\n version: 1,\n});\nexport const serializeOpenApiIndex = (entries) => JSON.stringify({\n entries,\n version: 1,\n});\nexport const loadPrebuiltContentIndex = async (source) => {\n try {\n const raw = await source.readFile(PREBUILT_INDEX_PATH);\n const data = JSON.parse(raw);\n if (data.version !== 1 || !Array.isArray(data.entries)) {\n return null;\n }\n const bySlug = new Map();\n const byCollection = new Map();\n for (const entry of data.entries) {\n bySlug.set(entry.slug, entry);\n }\n for (const [collectionId, entries] of Object.entries(data.collections ?? {})) {\n byCollection.set(collectionId, entries);\n }\n return {\n byCollection,\n bySlug,\n entries: data.entries,\n errors: [],\n };\n }\n catch {\n return null;\n }\n};\nexport const loadPrebuiltOpenApiIndex = async (source) => {\n try {\n const raw = await source.readFile(PREBUILT_OPENAPI_INDEX_PATH);\n const data = JSON.parse(raw);\n if (data.version !== 1 || !Array.isArray(data.entries)) {\n return null;\n }\n return data.entries;\n }\n catch {\n return null;\n }\n};\nexport const serializeSearchIndex = (items) => JSON.stringify({\n items,\n version: 1,\n});\nexport const loadPrebuiltSearchIndex = async (source) => {\n try {\n const raw = await source.readFile(PREBUILT_SEARCH_INDEX_PATH);\n const data = JSON.parse(raw);\n if (data.version !== 1 || !Array.isArray(data.items)) {\n return null;\n }\n return data.items;\n }\n catch {\n return null;\n }\n};\nexport const serializeTocIndex = (itemsBySlug) => JSON.stringify({\n itemsBySlug: Object.fromEntries(itemsBySlug),\n version: 1,\n});\nexport const loadPrebuiltTocIndex = async (source) => {\n try {\n const raw = await source.readFile(PREBUILT_TOC_INDEX_PATH);\n const data = JSON.parse(raw);\n if (data.version !== 1 || typeof data.itemsBySlug !== \"object\") {\n return null;\n }\n return new Map(Object.entries(data.itemsBySlug ?? {}));\n }\n catch {\n return null;\n }\n};\nexport const serializeUtilityIndex = (index) => JSON.stringify({\n ...index,\n version: 1,\n});\nexport const loadPrebuiltUtilityIndex = async (source) => {\n try {\n const raw = await source.readFile(PREBUILT_UTILITY_INDEX_PATH);\n const data = JSON.parse(raw);\n if (data.version !== 1 ||\n typeof data.name !== \"string\" ||\n !Array.isArray(data.pages)) {\n return null;\n }\n return {\n description: data.description,\n name: data.name,\n pages: data.pages,\n };\n }\n catch {\n return null;\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\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\n/**\n * Locate the `node_modules` directory that actually contains the CLI's\n * transitive dependencies. Package managers like npm/yarn-classic (and\n * `npx` caches) hoist shared deps above the package directory, so\n * `<cliPackageRoot>/node_modules` may not exist or may not contain `next`.\n * Resolve `next/package.json` and use the directory that owns it.\n */\nconst resolveRuntimeNodeModules = async (\n cliPackageRoot: string\n): Promise<string> => {\n const localNodeModules = path.join(cliPackageRoot, \"node_modules\");\n if (await fileExists(path.join(localNodeModules, \"next\", \"package.json\"))) {\n return localNodeModules;\n }\n\n const nextPkgPath = createRequire(\n path.join(cliPackageRoot, \"package.json\")\n ).resolve(\"next/package.json\");\n return path.dirname(path.dirname(nextPkgPath));\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 const runtimeNodeModules = await resolveRuntimeNodeModules(cliPackageRoot);\n await fs.symlink(\n runtimeNodeModules,\n path.join(runtimeRoot, \"node_modules\"),\n process.platform === \"win32\" ? \"junction\" : \"dir\"\n );\n // Both dev-server/ and docs/ import from `@repo/*`, and the\n // `@repo/*` packages import each other. Module resolution walks up\n // from each consuming file, so every consumption root needs a\n // `node_modules/@repo` symlink pointing at the shared packages/\n // copy. `packages/node_modules/@repo` also satisfies lookups from\n // inside `packages/@repo/<pkg>/dist/*.js` imports.\n const linkTarget = path.join(runtimeRoot, \"packages\", \"@repo\");\n for (const consumer of [\"dev-server\", \"docs\", \"packages\"]) {\n await fs.mkdir(path.join(runtimeRoot, consumer, \"node_modules\"), {\n recursive: true,\n });\n await fs.symlink(\n linkTarget,\n path.join(runtimeRoot, consumer, \"node_modules\", \"@repo\"),\n process.platform === \"win32\" ? \"junction\" : \"dir\"\n );\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 { slugify } from \"@repo/common\";\n\nexport interface DocsProjectConfig {\n name?: string;\n slug?: string;\n}\n\nexport interface ResolvedProjectTarget {\n project?: string;\n usedLegacyNameFallback: boolean;\n}\n\nexport const LEGACY_PROJECT_NAME_FALLBACK_WARNING =\n \"docs.json.slug is recommended. Falling back to docs.json.name as the deployment slug is deprecated.\";\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\nexport const deriveDisplayNameFromProjectSlug = (projectSlug: string) =>\n projectSlug\n .split(\"-\")\n .filter(Boolean)\n .map((segment) => segment[0]?.toUpperCase() + segment.slice(1))\n .join(\" \");\n\nexport const resolveProjectTarget = (options: {\n cliProject?: string;\n config: DocsProjectConfig;\n envProject?: string;\n}): ResolvedProjectTarget => {\n if (options.cliProject) {\n return {\n project: options.cliProject,\n usedLegacyNameFallback: false,\n };\n }\n\n if (options.envProject) {\n return {\n project: options.envProject,\n usedLegacyNameFallback: false,\n };\n }\n\n if (options.config.slug) {\n return {\n project: options.config.slug,\n usedLegacyNameFallback: false,\n };\n }\n\n if (options.config.name) {\n return {\n project: options.config.name,\n usedLegacyNameFallback: true,\n };\n }\n\n return {\n project: undefined,\n usedLegacyNameFallback: false,\n };\n};\n\nexport const getProjectSlugError = (project: string | undefined) => {\n if (!project) {\n return;\n }\n\n return validateProjectSlug(project);\n};\n","import path from \"node:path\";\n\nimport { slugify } from \"@repo/common\";\n\nimport { deriveDisplayNameFromProjectSlug } from \"./project-config.js\";\n\nexport { validateProjectSlug } from \"./project-config.js\";\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\nconst escapeXmlText = (value: string) =>\n value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\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\nconst createMinimalDocsJson = (projectSlug: string, displayName: string) => ({\n $schema: \"https://blode.md/docs.json\",\n name: displayName,\n navigation: {\n groups: [{ group: \"Getting Started\", pages: [\"index\"] }],\n },\n slug: projectSlug,\n});\n\nconst createStarterDocsJson = (projectSlug: string, displayName: 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: `${displayName} logo`,\n dark: \"/logo/dark.svg\",\n light: \"/logo/light.svg\",\n },\n metadata: { timestamp: true },\n name: displayName,\n navigation: {\n groups: [\n {\n group: \"Getting Started\",\n pages: [\"index\", \"quickstart\", \"development\"],\n },\n ],\n },\n slug: projectSlug,\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 = (\n projectSlug: string,\n displayName: string\n): ScaffoldFile[] => [\n {\n content: stringifyJson(createMinimalDocsJson(projectSlug, displayName)),\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 = (\n projectSlug: string,\n displayName: string\n): ScaffoldFile[] => {\n const escapedDisplayName = escapeXmlText(displayName);\n\n return [\n {\n content: stringifyJson(createStarterDocsJson(projectSlug, displayName)),\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 `slug` in `docs.json` matches your deployment target.\",\n \"- Update `name` in `docs.json` to match the visible product or docs brand.\",\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 `slug` in `docs.json` matches your deployment target.\",\n \"2. Update `name` in `docs.json` to match your visible docs brand.\",\n \"3. Update the `description` field to match your product.\",\n \"4. Replace the assets in `/logo` and `/images`.\",\n \"5. Run `blodemd dev` to preview locally.\",\n \"6. 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 `slug` in `docs.json` and set the display `name` and description.\",\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\">${escapedDisplayName}</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\">${escapedDisplayName}</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};\n\nexport const getScaffoldFiles = (\n template: ScaffoldTemplate,\n options?: {\n displayName?: string;\n projectSlug?: string;\n }\n): ScaffoldFile[] => {\n const projectSlug = options?.projectSlug ?? DEFAULT_PROJECT_SLUG;\n const displayName =\n options?.displayName ?? deriveDisplayNameFromProjectSlug(projectSlug);\n return template === \"starter\"\n ? createStarterFiles(projectSlug, displayName)\n : createMinimalFiles(projectSlug, displayName);\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 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 BLODE_PROJECT_ENV,\n CREDENTIALS_FILE,\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 {\n deriveDisplayNameFromProjectSlug,\n getProjectSlugError,\n LEGACY_PROJECT_NAME_FALLBACK_WARNING,\n resolveProjectTarget,\n validateProjectSlug,\n} from \"./project-config.js\";\nimport { assertSupportedNodeVersion, readCliVersion } from \"./runtime.js\";\nimport {\n DEFAULT_SCAFFOLD_DIRECTORY,\n deriveDefaultProjectSlug,\n getScaffoldFiles,\n isScaffoldTemplate,\n resolveScaffoldDirectory,\n SCAFFOLD_TEMPLATES,\n} from \"./scaffold.js\";\nimport type { ScaffoldTemplate } from \"./scaffold.js\";\nimport { loadValidatedSiteConfig } from \"./site-config.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 { 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 promptForDisplayName = async (\n initialValue: string\n): Promise<string | undefined> => {\n const displayName = await text({\n initialValue,\n message: \"Display name\",\n placeholder: initialValue,\n validate: (value) => {\n if (!value?.trim()) {\n return \"Display name is required.\";\n }\n },\n });\n\n if (isCancel(displayName)) {\n return;\n }\n\n return displayName.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 providedSlug: string | undefined,\n directory: string,\n shouldPrompt: boolean\n): Promise<string | undefined> => {\n const defaultProjectSlug = deriveDefaultProjectSlug(directory, process.cwd());\n\n if (providedSlug) {\n return providedSlug;\n }\n\n if (!shouldPrompt) {\n return defaultProjectSlug;\n }\n\n return await promptForProjectSlug(defaultProjectSlug);\n};\n\nconst resolveDisplayName = async (\n providedDisplayName: string | undefined,\n projectSlug: string,\n shouldPrompt: boolean\n): Promise<string | undefined> => {\n const defaultDisplayName = deriveDisplayNameFromProjectSlug(projectSlug);\n\n if (providedDisplayName?.trim()) {\n return providedDisplayName.trim();\n }\n\n if (!shouldPrompt) {\n return defaultDisplayName;\n }\n\n return await promptForDisplayName(defaultDisplayName);\n};\n\nconst writeScaffoldFiles = async (\n root: string,\n template: ScaffoldTemplate,\n options: {\n displayName: string;\n projectSlug: string;\n }\n) => {\n for (const file of getScaffoldFiles(template, options)) {\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 projectDisplayName: string;\n apiUrl: string;\n authToken: string;\n branch: string;\n commitMessage?: string;\n usedLegacyNameFallback: boolean;\n}\n\nconst resolvePushConfig = async (\n config: { name?: string; slug?: string },\n options: {\n apiUrl?: string;\n branch?: string;\n message?: string;\n project?: string;\n }\n): Promise<PushConfig> => {\n const { project, usedLegacyNameFallback } = resolveProjectTarget({\n cliProject: options.project,\n config,\n envProject: process.env[BLODE_PROJECT_ENV],\n });\n const apiUrl =\n options.apiUrl ?? process.env[BLODE_API_URL_ENV] ?? DEFAULT_API_URL;\n\n const resolved = await resolveAuthToken();\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 \"slug\" in docs.json, pass --project, or set BLODEMD_PROJECT.'\n );\n }\n\n const projectSlugError = getProjectSlugError(project);\n if (projectSlugError) {\n if (usedLegacyNameFallback) {\n throw new Error(\n `docs.json.name is not a valid deployment slug. Add \"slug\" to docs.json, pass --project, or set BLODEMD_PROJECT. ${projectSlugError}`\n );\n }\n\n throw new Error(`Invalid project slug \"${project}\". ${projectSlugError}`);\n }\n if (!authToken) {\n throw new Error('Not logged in. Run \"blodemd login\" to authenticate.');\n }\n\n return {\n apiUrl,\n authToken,\n branch,\n commitMessage,\n project,\n projectDisplayName: config.name?.trim() || project,\n usedLegacyNameFallback,\n };\n};\n\nconst autoCreateProject = async (\n project: string,\n projectDisplayName: 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<{ id: string; slug: string }>(\n new URL(\"/projects\", apiUrl).toString(),\n {\n body: JSON.stringify({ name: projectDisplayName, slug: project }),\n headers,\n method: \"POST\",\n },\n \"Failed to create project\"\n );\n\n log.success(`Project ${chalk.cyan(createResult.slug)} created`);\n return true;\n};\n\nconst scaffoldDocsSite = async (\n directory: string | undefined,\n options?: {\n deprecatedCommand?: string;\n displayName?: string;\n name?: string;\n slug?: 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 if (options?.name && !options.slug) {\n log.warn(\n `\"${chalk.cyan(\"--name\")}\" is deprecated. Use ${chalk.cyan(\"--slug\")} 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?.slug ?? options?.name,\n resolvedDirectory,\n shouldPrompt\n );\n\n if (!projectSlug) {\n log.warn(\"Cancelled\");\n return;\n }\n\n const displayName = await resolveDisplayName(\n options?.displayName,\n projectSlug,\n shouldPrompt\n );\n\n if (!displayName) {\n log.warn(\"Cancelled\");\n return;\n }\n\n await fs.mkdir(root, { recursive: true });\n await writeScaffoldFiles(root, template, { displayName, 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(`Display name: ${chalk.cyan(displayName)}`);\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 via GitHub in your browser\")\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(async (options: { port: string; timeout: string; open: boolean }) => {\n intro(chalk.bold(\"blodemd login\"));\n\n try {\n // OAuth 2.1 authorization code flow with PKCE (GitHub via Supabase)\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 // Pin provider=github so users go straight to GitHub OAuth without a picker\n authUrl.searchParams.set(\"provider\", \"github\");\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// 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 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(\"--slug <slug>\", \"project slug for docs.json\", parseProjectSlug)\n .option(\"--name <slug>\", \"deprecated alias for --slug\", parseProjectSlug)\n .option(\"--display-name <name>\", \"display name for docs.json\")\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 displayName?: string;\n name?: string;\n slug?: string;\n template: ScaffoldTemplate;\n yes?: boolean;\n }\n ) => {\n await scaffoldDocsSite(directory, {\n displayName: options.displayName,\n name: options.name,\n slug: options.slug ?? 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(\"--slug <slug>\", \"project slug for docs.json\", parseProjectSlug)\n .option(\"--name <slug>\", \"deprecated alias for --slug\", parseProjectSlug)\n .option(\"--display-name <name>\", \"display name for docs.json\")\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 displayName?: string;\n name?: string;\n slug?: string;\n template: ScaffoldTemplate;\n yes?: boolean;\n }\n ) => {\n await scaffoldDocsSite(directory, {\n deprecatedCommand: \"blodemd init\",\n displayName: options.displayName,\n name: options.name,\n slug: options.slug ?? 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(\"--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 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 {\n project,\n projectDisplayName,\n apiUrl,\n authToken,\n branch,\n commitMessage,\n usedLegacyNameFallback,\n } = await resolvePushConfig(config, options);\n\n if (usedLegacyNameFallback) {\n log.warn(LEGACY_PROJECT_NAME_FALLBACK_WARNING);\n }\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(\n project,\n projectDisplayName,\n apiUrl,\n headers\n );\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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ACvE,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,iBAAiB;GACjB,MAAM;GACN,QAAQ;GACR,MAAM;GACP;EACD;GACE,SAAS;GACT,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA;IACA;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA,yGAAyG,mBAAmB;IAC5H;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MAAM;GACP;EACD;GACE,SAAS;IACP;IACA;IACA;IACA,yGAAyG,mBAAmB;IAC5H;IACA;IACD,CAAC,KAAK,KAAK;GACZ,MA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"}
|