@407dev/cli 0.1.0
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 +79 -0
- package/dist/index.d.ts +313 -0
- package/dist/index.js +4289 -0
- package/dist/index.js.map +1 -0
- package/package.json +54 -0
- package/skills/407dev-cms-fields/SKILL.md +145 -0
- package/skills/407dev-cms-fields/references/helpers.md +78 -0
- package/skills/407dev-cms-keying/SKILL.md +80 -0
- package/skills/407dev-cms-setup/SKILL.md +98 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/extract/check.ts","../src/extract/collect.ts","../src/extract/collection-schema.ts","../src/extract/resolve.ts","../src/extract/field-registry.ts","../src/extract/parse.ts","../src/extract/seed-eval.ts","../src/extract/manifest.ts","../src/extract/merge.ts","../src/extract/routes.ts","../src/commands/check.ts","../src/commands/extract.ts","../src/commands/link.ts","../../config/src/index.ts","../../events/src/schema.ts","../../events/src/normalize.ts","../../events/src/wire.ts","../../events/src/queries.ts","../../events/src/journey.ts","../../events/src/ndjson.ts","../../events/src/vitalsBundle.generated.ts","../../events/src/snippet.ts","../../events/src/ua.ts","../../events/src/vitals.ts","../../events/src/privacy.ts","../../events/src/deep.ts","../../api-client/src/index.ts","../src/credentials.ts","../src/jwt.ts","../src/auth.ts","../src/env.ts","../src/commands/login.ts","../src/commands/logout.ts","../src/commands/push.ts","../src/manifest-loader.ts","../src/migrations.ts","../src/state-store.ts","../src/commands/skills.ts","../src/commands/whoami.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { fileURLToPath } from 'node:url';\nimport { parseArgs } from 'node:util';\nimport { executeCheck } from './commands/check';\nimport { executeExtract } from './commands/extract';\nimport { executeLink } from './commands/link';\nimport { executeLogin } from './commands/login';\nimport { executeLogout } from './commands/logout';\nimport { executePush } from './commands/push';\nimport { executeSkillsInstall, executeSkillsList } from './commands/skills';\nimport { executeWhoami } from './commands/whoami';\nimport { loadEnvFiles } from './env';\nimport { loadManifest } from './manifest-loader';\nimport { runEntryMigrations } from './migrations';\nimport { StateStore } from './state-store';\nimport type { PushCommandResult } from './types';\n\nexport * from './auth';\nexport * from './credentials';\nexport * from './commands/check';\nexport * from './commands/extract';\nexport * from './commands/link';\nexport * from './commands/login';\nexport * from './commands/logout';\nexport * from './commands/push';\nexport * from './commands/skills';\nexport * from './commands/whoami';\nexport * from './extract';\nexport * from './manifest-loader';\nexport * from './migrations';\nexport * from './state-store';\nexport * from './types';\n\nexport const VERSION = '0.0.1';\n\nexport function printHelp(): string {\n return `\n407dev CMS CLI - v${VERSION}\n\nUsage:\n cms login [options] Authenticate CLI via browser session\n cms logout [options] Log out and remove local credentials\n cms whoami [options] Show current authentication status\n cms link [options] Link local repository to a CMS site\n cms push [options] Push code-first site manifest to the CMS API\n cms check [options] Validate templates, scopes, groups, collections, and routes\n cms extract [options] Extract code-first site manifest from AST\n cms skills install [dir] Install CMS agent skills into <dir>/.claude/skills\n cms skills list List bundled CMS agent skills\n\nCommands & Options:\n login:\n --api-url <url> API URL (default: CMS_API_URL or local dev)\n\n logout:\n --api-url <url> API URL (default: CMS_API_URL or local dev)\n\n whoami:\n --api-url <url> API URL (default: CMS_API_URL or local dev)\n\n link:\n -s, --site <id> Site ID to link (prompted interactively if omitted)\n --api-url <url> API URL (default: CMS_API_URL or local dev)\n\n push:\n -s, --site <id> Site ID (default: env CMS_SITE_ID)\n -c, --config <path> Path to cms.manifest.ts (default: static AST extraction)\n --check Run static AST validation checks before pushing\n -y, --yes Automatically accept orphaned fields and changes\n --no-sync Skip running 'astro sync' when deriving routes\n --force Force push even if local manifest hash matches state\n --dry-run Validate manifest and compute diff without modifying database\n\n check & extract:\n --json Output raw manifest as JSON (extract only)\n --no-sync Skip running 'astro sync' when deriving routes\n\n skills install:\n --force Overwrite skill files that were edited locally\n\nGlobal Options:\n -v, --version Show CLI version\n -h, --help Show help information\n`;\n}\n\nexport function formatPushReport(result: PushCommandResult): string {\n if (result.skipped) {\n return '⨠Manifest is unchanged. Nothing to push.';\n }\n\n if (result.aborted) {\n return `đ ${result.errors.join('\\n')}`;\n }\n\n const lines: string[] = [];\n lines.push('đŚ Manifest Push Summary:');\n lines.push(` Hash: ${result.hash}`);\n\n if (result.report) {\n const r = result.report;\n lines.push(` Created items: ${r.created}`);\n lines.push(` Updated items: ${r.updated}`);\n lines.push(` Unchanged items: ${r.unchanged}`);\n lines.push(` Renamed items: ${r.renamed}`);\n\n if (r.orphaned_fields.length > 0) {\n lines.push(` Orphaned fields: ${r.orphaned_fields.map((f) => f.key).join(', ')}`);\n }\n if (r.orphaned_collections.length > 0) {\n lines.push(\n ` Orphaned collections: ${r.orphaned_collections.map((c) => `${c.key} (${c.entry_count} entries)`).join(', ')}`,\n );\n }\n if (r.seeded && r.seeded.length > 0) {\n const seededList = r.seeded\n .map((s) => `${s.key} (${s.count} ${s.count === 1 ? 'entry' : 'entries'})`)\n .join(', ');\n lines.push(` Seeded ${seededList} â publish to make them live.`);\n }\n if (r.unpublished_collections && r.unpublished_collections.length > 0) {\n const unpublishedList = r.unpublished_collections\n .map((c) => `${c.key} (${c.entry_count} entries)`)\n .join(', ');\n lines.push(\n ` â ď¸ Unpublished: ${unpublishedList} â publish in the dashboard to make them live (rebuilding or redeploying won't).`,\n );\n }\n\n lines.push(` Routes: +${r.routes.added} / -${r.routes.removed}`);\n }\n\n if (result.migrationsRun) {\n lines.push(` Entry migrations applied: ${result.migrationsRun}`);\n }\n\n if (result.warnings.length > 0) {\n lines.push('\\nâ ď¸ Warnings:');\n for (const w of result.warnings) {\n lines.push(` - ${w}`);\n }\n }\n\n if (result.errors.length > 0) {\n lines.push('\\nâ Errors:');\n for (const e of result.errors) {\n lines.push(` - ${e}`);\n }\n }\n\n return lines.join('\\n');\n}\n\nexport async function runCli(args: string[] = []): Promise<string> {\n const targetDir = args[1] && !args[1].startsWith('-') ? args[1] : process.cwd();\n loadEnvFiles(targetDir);\n\n if (args.includes('--version') || args.includes('-v')) {\n return VERSION;\n }\n\n if (args.includes('--help') || args.includes('-h') || args.length === 0) {\n return printHelp();\n }\n\n const command = args[0];\n\n if (command === 'login') {\n const rawOptions = parseArgs({\n args: args.slice(1),\n options: {\n 'api-url': { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n allowPositionals: true,\n strict: false,\n });\n\n if (rawOptions.values.help) {\n return printHelp();\n }\n\n return await executeLogin({\n apiUrl: (rawOptions.values['api-url'] as string) || undefined,\n });\n }\n\n if (command === 'logout') {\n const rawOptions = parseArgs({\n args: args.slice(1),\n options: {\n 'api-url': { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n allowPositionals: true,\n strict: false,\n });\n\n if (rawOptions.values.help) {\n return printHelp();\n }\n\n return executeLogout({\n apiUrl: (rawOptions.values['api-url'] as string) || undefined,\n });\n }\n\n if (command === 'whoami') {\n const rawOptions = parseArgs({\n args: args.slice(1),\n options: {\n 'api-url': { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n allowPositionals: true,\n strict: false,\n });\n\n if (rawOptions.values.help) {\n return printHelp();\n }\n\n return await executeWhoami({\n apiUrl: (rawOptions.values['api-url'] as string) || undefined,\n });\n }\n\n if (command === 'link') {\n const rawOptions = parseArgs({\n args: args.slice(1),\n options: {\n site: { type: 'string', short: 's' },\n 'site-id': { type: 'string' },\n 'api-url': { type: 'string' },\n help: { type: 'boolean', short: 'h' },\n },\n allowPositionals: true,\n strict: false,\n });\n\n if (rawOptions.values.help) {\n return printHelp();\n }\n\n const siteId =\n (rawOptions.values.site as string) || (rawOptions.values['site-id'] as string) || undefined;\n\n return await executeLink({\n siteId,\n apiUrl: (rawOptions.values['api-url'] as string) || undefined,\n siteDir: rawOptions.positionals[0] || undefined,\n });\n }\n\n if (command === 'extract') {\n const rawOptions = parseArgs({\n args: args.slice(1),\n options: {\n json: { type: 'boolean' },\n 'no-sync': { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n allowPositionals: true,\n strict: false,\n });\n\n if (rawOptions.values.help) {\n return printHelp();\n }\n\n return await executeExtract({\n siteDir: rawOptions.positionals[0] || undefined,\n json: Boolean(rawOptions.values.json),\n noSync: Boolean(rawOptions.values['no-sync']),\n });\n }\n\n if (command === 'check') {\n const rawOptions = parseArgs({\n args: args.slice(1),\n options: {\n 'no-sync': { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n allowPositionals: true,\n strict: false,\n });\n\n if (rawOptions.values.help) {\n return printHelp();\n }\n\n const checkRes = await executeCheck({\n siteDir: rawOptions.positionals[0] || undefined,\n noSync: Boolean(rawOptions.values['no-sync']),\n });\n\n if (!checkRes.success) {\n throw new Error(checkRes.report);\n }\n\n return checkRes.report;\n }\n\n if (command === 'push') {\n const rawOptions = parseArgs({\n args: args.slice(1),\n options: {\n site: { type: 'string', short: 's' },\n 'site-id': { type: 'string' },\n config: { type: 'string', short: 'c' },\n check: { type: 'boolean' },\n yes: { type: 'boolean', short: 'y' },\n 'no-sync': { type: 'boolean' },\n force: { type: 'boolean' },\n 'dry-run': { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n allowPositionals: true,\n strict: false,\n });\n\n if (rawOptions.values.help) {\n return printHelp();\n }\n\n const siteId =\n (rawOptions.values.site as string) ||\n (rawOptions.values['site-id'] as string) ||\n process.env.CMS_SITE_ID;\n\n if (!siteId) {\n throw new Error('site ID is required. Pass --site <id> or set CMS_SITE_ID.');\n }\n\n const result = await executePush({\n siteId,\n siteDir: rawOptions.positionals[0] || undefined,\n configPath: rawOptions.values.config as string | undefined,\n check: Boolean(rawOptions.values.check),\n yes: Boolean(rawOptions.values.yes),\n noSync: Boolean(rawOptions.values['no-sync']),\n force: Boolean(rawOptions.values.force),\n dryRun: Boolean(rawOptions.values['dry-run']),\n });\n\n const report = formatPushReport(result);\n if (!result.success) {\n throw new Error(report);\n }\n return report;\n }\n\n if (command === 'skills') {\n const rawOptions = parseArgs({\n args: args.slice(1),\n options: {\n force: { type: 'boolean' },\n help: { type: 'boolean', short: 'h' },\n },\n allowPositionals: true,\n strict: false,\n });\n\n if (rawOptions.values.help) {\n return printHelp();\n }\n\n const [subcommand, targetDir] = rawOptions.positionals;\n if (subcommand === 'install') {\n return executeSkillsInstall({ targetDir, force: Boolean(rawOptions.values.force) });\n }\n if (subcommand === 'list') {\n return executeSkillsList();\n }\n throw new Error(`Unknown skills subcommand: \"${subcommand ?? ''}\". Use \"install\" or \"list\".`);\n }\n\n throw new Error(`Unknown command: \"${command}\". Run \"cms --help\" for available commands.`);\n}\n\n// Entrypoint when executed directly via bin/node\nfunction isDirectExecution(): boolean {\n if (!process.argv[1]) return false;\n try {\n const scriptPath = fileURLToPath(import.meta.url);\n const invokedPath = fs.realpathSync(process.argv[1]);\n return scriptPath === invokedPath;\n } catch {\n return false;\n }\n}\n\nif (isDirectExecution()) {\n runCli(process.argv.slice(2))\n .then((output) => {\n console.log(output);\n })\n .catch((err) => {\n console.error(`\\nâ Error: ${err.message}\\n`);\n process.exit(1);\n });\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport type { SiteManifest } from '@407dev/api-client';\nimport { collectFromFile } from './collect';\nimport { buildSiteManifest } from './manifest';\nimport { mergeExtractionResults } from './merge';\nimport { globSourceFiles, parseFile } from './parse';\nimport { resolveFileContext } from './resolve';\nimport { loadDerivedRoutes } from './routes';\nimport type { ExtractionDiagnostic, FileExtractionResult, MergedExtractionResult } from './types';\n\nexport interface ExtractOptions {\n siteDir?: string;\n noSync?: boolean;\n}\n\nexport interface ExtractResult {\n manifest: SiteManifest;\n diagnostics: ExtractionDiagnostic[];\n errors: ExtractionDiagnostic[];\n warnings: ExtractionDiagnostic[];\n merged: MergedExtractionResult;\n}\n\nexport async function extractProject(\n siteDir = process.cwd(),\n options: ExtractOptions = {},\n): Promise<ExtractResult> {\n const srcDir = path.join(siteDir, 'src');\n const targetDir = fs.existsSync(srcDir) ? srcDir : siteDir;\n const files = globSourceFiles(targetDir);\n\n const fileResults: FileExtractionResult[] = [];\n const cachedParsedFiles = new Map();\n\n for (const file of files) {\n const parsed = await parseFile(file, siteDir);\n cachedParsedFiles.set(file, parsed);\n const context = await resolveFileContext(parsed, siteDir, cachedParsedFiles);\n const collected = collectFromFile(parsed, context);\n fileResults.push(collected);\n }\n\n // Merge per-file extractions\n const merged = mergeExtractionResults(fileResults);\n\n // Load derived routes\n const { routes, diagnostics: routeDiagnostics } = loadDerivedRoutes(siteDir, merged.collections, {\n noSync: options.noSync,\n });\n\n // Build manifest\n const { manifest, diagnostics } = buildSiteManifest(merged, routes);\n diagnostics.push(...routeDiagnostics);\n\n const errors = diagnostics.filter((d) => d.type === 'error');\n const warnings = diagnostics.filter((d) => d.type === 'warning');\n\n return {\n manifest,\n diagnostics,\n errors,\n warnings,\n merged,\n };\n}\n\nexport function formatDiagnostics(diagnostics: ExtractionDiagnostic[]): string {\n if (diagnostics.length === 0) {\n return '⨠All checks passed cleanly with 0 errors.';\n }\n\n const lines: string[] = [];\n for (const d of diagnostics) {\n const prefix = d.type === 'error' ? 'â' : 'â ď¸';\n const loc = `${d.file}:${d.line}:${d.column}`;\n lines.push(`${prefix} ${loc}: ${d.message}`);\n }\n\n const errors = diagnostics.filter((d) => d.type === 'error').length;\n const warnings = diagnostics.filter((d) => d.type === 'warning').length;\n lines.push(`\\nFound ${errors} error(s) and ${warnings} warning(s).`);\n\n return lines.join('\\n');\n}\n","import ts from 'typescript';\nimport { parseZodObjectSchema } from './collection-schema';\nimport { knownFieldMethodNames, normalizeFieldType } from './field-registry';\nimport type { ParsedFile } from './parse';\nimport { type ResolvedContext, getStringLiteralValue } from './resolve';\nimport { extractCollectionSeed } from './seed-eval';\nimport type {\n ExtractedCollection,\n ExtractedField,\n ExtractionDiagnostic,\n FileExtractionResult,\n} from './types';\n\nexport function collectFromFile(\n parsed: ParsedFile,\n context: ResolvedContext,\n): FileExtractionResult {\n const knownFieldTypes = knownFieldMethodNames();\n const { sourceFile, getLocation, relativePath } = parsed;\n const fields: ExtractedField[] = [];\n const collectionsMap = new Map<string, ExtractedCollection>();\n for (const coll of context.collections.values()) {\n collectionsMap.set(coll.key, coll);\n }\n const diagnostics: ExtractionDiagnostic[] = [...context.diagnostics];\n\n // Helper to extract options like defaultValue from 2nd argument\n function extractOptions(node?: ts.Node): {\n defaultValue?: unknown;\n constraints?: Record<string, unknown>;\n } {\n if (!node || !ts.isObjectLiteralExpression(node)) return {};\n const result: { defaultValue?: unknown; constraints?: Record<string, unknown> } = {};\n\n for (const prop of node.properties) {\n if (ts.isPropertyAssignment(prop)) {\n const propName = prop.name.getText(sourceFile);\n if (propName === 'defaultValue') {\n const strVal = getStringLiteralValue(prop.initializer);\n if (strVal !== null) {\n result.defaultValue = strVal;\n } else if (ts.isNumericLiteral(prop.initializer)) {\n result.defaultValue = Number(prop.initializer.text);\n } else if (\n prop.initializer.kind === ts.SyntaxKind.TrueKeyword ||\n prop.initializer.kind === ts.SyntaxKind.FalseKeyword\n ) {\n result.defaultValue = prop.initializer.kind === ts.SyntaxKind.TrueKeyword;\n }\n }\n }\n }\n return result;\n }\n\n // 1. Multiply group instances into extracted fields\n for (const [varName, instance] of context.groupInstances) {\n for (const [shapeKey, shapeType] of Object.entries(instance.shape)) {\n const fullKey = `${instance.prefix}.${shapeKey}`;\n fields.push({\n key: fullKey,\n type: normalizeFieldType(shapeType),\n group: instance.groupName,\n location: instance.location,\n });\n }\n }\n\n function checkKeyArgument(keyNode: ts.Node | undefined, calleeText: string): string | null {\n if (!keyNode) {\n diagnostics.push({\n type: 'error',\n message: `Field helper '${calleeText}' called without a key`,\n file: relativePath,\n line: getLocation(keyNode || sourceFile).line,\n column: getLocation(keyNode || sourceFile).column,\n });\n return null;\n }\n\n const literalVal = getStringLiteralValue(keyNode);\n if (literalVal === null) {\n diagnostics.push({\n type: 'error',\n message: `Field key in '${calleeText}' must be a static string literal, got non-literal expression`,\n file: relativePath,\n line: getLocation(keyNode).line,\n column: getLocation(keyNode).column,\n });\n return null;\n }\n\n return literalVal;\n }\n\n // 2. Walk AST for calls and JSX elements\n function walk(node: ts.Node) {\n // Check call expressions: f.text('...'), c.text('...'), defineCollection({ ... })\n if (ts.isCallExpression(node)) {\n const expr = node.expression;\n const exprText = expr.getText(sourceFile);\n\n // defineCollection({ ... })\n if (exprText === 'defineCollection' || exprText === 'f.defineCollection') {\n const arg0 = node.arguments[0];\n if (arg0 && ts.isObjectLiteralExpression(arg0)) {\n let collKey = '';\n let route: string | undefined;\n let presentation: 'visual' | 'form' | undefined;\n let titleField: string | undefined;\n let bodyField: string | undefined;\n let listColumns: string[] | undefined;\n let orderable: boolean | undefined;\n let group: string | undefined;\n let schemaNode: ts.Node | undefined;\n let seed: Array<{ slug: string; content: Record<string, unknown> }> | undefined;\n\n for (const prop of arg0.properties) {\n if (ts.isPropertyAssignment(prop)) {\n const pName = prop.name.getText(sourceFile);\n if (pName === 'key') {\n collKey = getStringLiteralValue(prop.initializer) || '';\n } else if (pName === 'route') {\n route = getStringLiteralValue(prop.initializer) || undefined;\n } else if (pName === 'presentation') {\n const val = getStringLiteralValue(prop.initializer);\n if (val === 'visual' || val === 'form') presentation = val;\n } else if (pName === 'titleField') {\n titleField = getStringLiteralValue(prop.initializer) || undefined;\n } else if (pName === 'bodyField') {\n bodyField = getStringLiteralValue(prop.initializer) || undefined;\n } else if (pName === 'group') {\n group = getStringLiteralValue(prop.initializer) || undefined;\n } else if (pName === 'orderable') {\n orderable = prop.initializer.kind === ts.SyntaxKind.TrueKeyword;\n } else if (pName === 'schema') {\n schemaNode = prop.initializer;\n } else if (pName === 'seed') {\n seed = extractCollectionSeed(\n prop.initializer,\n sourceFile,\n getLocation,\n relativePath,\n diagnostics,\n );\n } else if (pName === 'listColumns') {\n if (ts.isArrayLiteralExpression(prop.initializer)) {\n listColumns = prop.initializer.elements\n .map((el: ts.Expression) => getStringLiteralValue(el))\n .filter((s: string | null): s is string => s !== null);\n }\n }\n }\n }\n\n if (!collKey) {\n diagnostics.push({\n type: 'error',\n message: 'Collection defined without a static string key',\n file: relativePath,\n line: getLocation(node).line,\n column: getLocation(node).column,\n });\n } else {\n const { jsonSchema } = schemaNode\n ? parseZodObjectSchema(schemaNode, sourceFile, bodyField)\n : { jsonSchema: { type: 'object', properties: {} } };\n\n collectionsMap.set(collKey, {\n key: collKey,\n jsonSchema,\n schemaVersion: 1,\n orderable,\n routePattern: route,\n presentation,\n titleField,\n listColumns,\n bodyField,\n group,\n seed,\n location: getLocation(node),\n });\n }\n }\n }\n\n // f.<type>('key') or c.<type>('key')\n if (ts.isPropertyAccessExpression(expr)) {\n const objText = expr.expression.getText(sourceFile);\n const methodName = expr.name.getText(sourceFile);\n\n let fieldType: string | undefined;\n let prefix = '';\n let groupName: string | undefined;\n\n if (objText === 'f') {\n if (knownFieldTypes.has(methodName)) {\n fieldType = methodName;\n }\n } else if (context.scopes.has(objText)) {\n if (knownFieldTypes.has(methodName)) {\n fieldType = methodName;\n prefix = context.scopes.get(objText)?.prefix || '';\n }\n } else if (context.groupInstances.has(objText)) {\n const inst = context.groupInstances.get(objText);\n if (inst?.shape[methodName]) {\n fieldType = inst.shape[methodName];\n prefix = inst.prefix;\n groupName = inst.groupName;\n }\n }\n\n if (fieldType) {\n const keyArg = node.arguments[0];\n // For group helper e.g. hero.heading(), key is methodName if no arg passed!\n let key: string | null = null;\n if (groupName && !keyArg) {\n key = methodName;\n } else {\n key = checkKeyArgument(keyArg, exprText);\n }\n\n if (key !== null) {\n const fullKey = prefix ? `${prefix}.${key}` : key;\n const normType = normalizeFieldType(fieldType);\n\n const opts = extractOptions(node.arguments[1]);\n fields.push({\n key: fullKey,\n type: normType,\n defaultValue: opts.defaultValue,\n constraints: opts.constraints,\n group: groupName,\n location: getLocation(node),\n });\n }\n }\n }\n }\n\n // Check JSX elements: <f.text k=\"key\" />, <c.img k=\"key\" />, <hero.heading />\n if (ts.isJsxSelfClosingElement(node) || ts.isJsxOpeningElement(node)) {\n const tagText = node.tagName.getText(sourceFile);\n const parts = tagText.split('.');\n\n if (parts.length === 2) {\n const [objName, memberName] = parts;\n let fieldType: string | undefined;\n let prefix = '';\n let groupName: string | undefined;\n\n if (objName === 'f') {\n if (knownFieldTypes.has(memberName)) {\n fieldType = memberName;\n }\n } else if (context.scopes.has(objName)) {\n if (knownFieldTypes.has(memberName)) {\n fieldType = memberName;\n prefix = context.scopes.get(objName)?.prefix || '';\n }\n } else if (context.groupInstances.has(objName)) {\n const inst = context.groupInstances.get(objName);\n if (inst?.shape[memberName]) {\n fieldType = inst.shape[memberName];\n prefix = inst.prefix;\n groupName = inst.groupName;\n }\n }\n\n if (fieldType) {\n // Look for k or key attribute\n let keyAttrVal: string | null = null;\n let defaultValue: unknown;\n\n for (const attr of node.attributes.properties) {\n if (ts.isJsxAttribute(attr)) {\n const aName = attr.name.getText(sourceFile);\n if (aName === 'k' || aName === 'key') {\n if (attr.initializer) {\n if (ts.isStringLiteral(attr.initializer)) {\n keyAttrVal = attr.initializer.text;\n } else if (ts.isJsxExpression(attr.initializer) && attr.initializer.expression) {\n keyAttrVal = getStringLiteralValue(attr.initializer.expression);\n if (keyAttrVal === null) {\n diagnostics.push({\n type: 'error',\n message: `JSX attribute '${aName}' on '<${tagText}>' must be a static string literal`,\n file: relativePath,\n line: getLocation(attr.initializer).line,\n column: getLocation(attr.initializer).column,\n });\n }\n }\n }\n } else if (aName === 'defaultValue' && attr.initializer) {\n if (ts.isStringLiteral(attr.initializer)) {\n defaultValue = attr.initializer.text;\n }\n }\n }\n }\n\n if (groupName && !keyAttrVal) {\n keyAttrVal = memberName;\n }\n\n if (keyAttrVal !== null) {\n const fullKey = prefix ? `${prefix}.${keyAttrVal}` : keyAttrVal;\n const normType = normalizeFieldType(fieldType);\n\n fields.push({\n key: fullKey,\n type: normType,\n defaultValue,\n group: groupName,\n location: getLocation(node),\n });\n }\n }\n }\n }\n\n ts.forEachChild(node, walk);\n }\n\n walk(sourceFile);\n\n return {\n file: relativePath,\n fields,\n collections: Array.from(collectionsMap.values()),\n groups: Array.from(context.groups.values()).map((g) => ({\n groupName: g.groupName,\n shape: g.shape,\n location: g.definedLocation,\n })),\n scopes: Array.from(context.scopes.entries()).map(([varName, s]) => ({\n varName,\n prefix: s.prefix,\n location: s.location,\n })),\n diagnostics,\n };\n}\n","import ts from 'typescript';\nimport { getStringLiteralValue } from './resolve';\nimport { extractCollectionSeed } from './seed-eval';\nimport type { ExtractedCollection, ExtractionDiagnostic, SourceLocation } from './types';\n\nexport function parseZodObjectSchema(\n schemaNode: ts.Node,\n sourceFile: ts.SourceFile,\n bodyField?: string,\n): {\n jsonSchema: Record<string, unknown>;\n properties: Record<string, unknown>;\n} {\n const properties: Record<string, unknown> = {};\n const required: string[] = [];\n\n function processPropAssignment(name: string, initNode: ts.Node) {\n const initText = initNode.getText(sourceFile);\n let propSchema: Record<string, unknown> = { type: 'string' };\n\n if (name === bodyField) {\n // A collection's bodyField is always richtext content rendered via <RichText>,\n // regardless of how loosely its zod schema types the field (e.g. z.any()).\n propSchema = { type: 'object', 'x-cms-type': 'richtext' };\n } else if (initText.includes('number()')) {\n propSchema = { type: 'number' };\n } else if (initText.includes('boolean()')) {\n propSchema = { type: 'boolean' };\n } else if (initText.includes('array(')) {\n propSchema = { type: 'array', items: { type: 'string' } };\n } else if (initText.includes('richtext') || initText.includes('richText')) {\n propSchema = { type: 'object', 'x-cms-type': 'richtext' };\n } else if (initText.includes('streamVideo(')) {\n propSchema = { type: 'object', 'x-cms-type': 'media.streamVideo' };\n } else if (\n initText.includes('image(') ||\n initText.includes('video(') ||\n initText.includes('asset(')\n ) {\n propSchema = { type: 'string', format: 'cms-asset-ref' };\n }\n\n properties[name] = propSchema;\n if (!initText.includes('.optional()')) {\n required.push(name);\n }\n }\n\n if (ts.isCallExpression(schemaNode)) {\n const callText = schemaNode.expression.getText(sourceFile);\n if (callText === 'z.object' && schemaNode.arguments[0]) {\n const objArg = schemaNode.arguments[0];\n if (ts.isObjectLiteralExpression(objArg)) {\n for (const prop of objArg.properties) {\n if (ts.isPropertyAssignment(prop)) {\n processPropAssignment(prop.name.getText(sourceFile), prop.initializer);\n }\n }\n }\n }\n }\n\n return {\n jsonSchema: {\n $schema: 'https://json-schema.org/draft/2020-12/schema',\n type: 'object',\n properties,\n required,\n additionalProperties: false,\n },\n properties,\n };\n}\n\nexport function extractCollectionFromCall(\n callExpr: ts.CallExpression,\n sf: ts.SourceFile,\n getLocation: (node: ts.Node) => SourceLocation,\n diagnostics: ExtractionDiagnostic[] = [],\n file = '',\n): ExtractedCollection | null {\n const arg0 = callExpr.arguments[0];\n if (!arg0 || !ts.isObjectLiteralExpression(arg0)) return null;\n\n let collKey = '';\n let route: string | undefined;\n let presentation: 'visual' | 'form' | undefined;\n let titleField: string | undefined;\n let bodyField: string | undefined;\n let listColumns: string[] | undefined;\n let orderable: boolean | undefined;\n let group: string | undefined;\n let schemaNode: ts.Node | undefined;\n let seed: Array<{ slug: string; content: Record<string, unknown> }> | undefined;\n\n for (const prop of arg0.properties) {\n if (ts.isPropertyAssignment(prop)) {\n const pName = prop.name.getText(sf);\n if (pName === 'key') {\n collKey = getStringLiteralValue(prop.initializer) || '';\n } else if (pName === 'route') {\n route = getStringLiteralValue(prop.initializer) || undefined;\n } else if (pName === 'presentation') {\n const val = getStringLiteralValue(prop.initializer);\n if (val === 'visual' || val === 'form') presentation = val;\n } else if (pName === 'titleField') {\n titleField = getStringLiteralValue(prop.initializer) || undefined;\n } else if (pName === 'bodyField') {\n bodyField = getStringLiteralValue(prop.initializer) || undefined;\n } else if (pName === 'group') {\n group = getStringLiteralValue(prop.initializer) || undefined;\n } else if (pName === 'orderable') {\n orderable = prop.initializer.kind === ts.SyntaxKind.TrueKeyword;\n } else if (pName === 'schema') {\n schemaNode = prop.initializer;\n } else if (pName === 'seed') {\n seed = extractCollectionSeed(prop.initializer, sf, getLocation, file, diagnostics);\n } else if (pName === 'listColumns') {\n if (ts.isArrayLiteralExpression(prop.initializer)) {\n listColumns = prop.initializer.elements\n .map((el: ts.Expression) => getStringLiteralValue(el))\n .filter((s: string | null): s is string => s !== null);\n }\n }\n }\n }\n\n if (!collKey) {\n diagnostics.push({\n type: 'error',\n message: 'Collection defined without a static string key',\n file,\n line: getLocation(callExpr).line,\n column: getLocation(callExpr).column,\n });\n return null;\n }\n\n const { jsonSchema } = schemaNode\n ? parseZodObjectSchema(schemaNode, sf, bodyField)\n : { jsonSchema: { type: 'object', properties: {} } };\n\n return {\n key: collKey,\n jsonSchema,\n schemaVersion: 1,\n orderable,\n routePattern: route,\n presentation,\n titleField,\n listColumns,\n bodyField,\n group,\n seed,\n location: getLocation(callExpr),\n };\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport ts from 'typescript';\nimport { extractCollectionFromCall } from './collection-schema';\nimport { knownFieldMethodNames, normalizeFieldType } from './field-registry';\nimport type { ParsedFile } from './parse';\nimport { parseFile } from './parse';\nimport type {\n ExtractedCollection,\n ExtractedGroup,\n ExtractedScope,\n ExtractionDiagnostic,\n SourceLocation,\n} from './types';\n\nexport interface ResolvedGroup {\n groupName: string;\n shape: Record<string, string>; // fieldName -> fieldType\n definedLocation: SourceLocation;\n hopCount: number; // 0 for in-file, 1 for 1-hop import\n}\n\nexport interface ResolvedContext {\n scopes: Map<string, { prefix: string; location: SourceLocation }>;\n groups: Map<string, ResolvedGroup>;\n groupInstances: Map<\n string,\n { groupName: string; prefix: string; shape: Record<string, string>; location: SourceLocation }\n >;\n collections: Map<string, ExtractedCollection>;\n diagnostics: ExtractionDiagnostic[];\n}\n\nexport function parseBareSpecifier(specifier: string): { packageName: string; subpath: string } {\n if (specifier.startsWith('@')) {\n const parts = specifier.split('/');\n const packageName = parts.slice(0, 2).join('/');\n const subpathParts = parts.slice(2);\n const subpath = subpathParts.length > 0 ? `./${subpathParts.join('/')}` : '.';\n return { packageName, subpath };\n }\n const parts = specifier.split('/');\n const packageName = parts[0];\n const subpathParts = parts.slice(1);\n const subpath = subpathParts.length > 0 ? `./${subpathParts.join('/')}` : '.';\n return { packageName, subpath };\n}\n\nexport function findPackageRoot(fromFile: string, packageName: string): string | null {\n let currentDir = path.dirname(path.resolve(fromFile));\n const root = path.parse(currentDir).root;\n while (true) {\n const candidate = path.join(currentDir, 'node_modules', ...packageName.split('/'));\n if (fs.existsSync(candidate) && fs.existsSync(path.join(candidate, 'package.json'))) {\n return candidate;\n }\n if (currentDir === root) {\n break;\n }\n currentDir = path.dirname(currentDir);\n }\n return null;\n}\n\nexport interface ExportResolution {\n resolvedPath: string | null;\n hasSource: boolean;\n exportFound: boolean;\n}\n\nexport function resolvePackageExport(pkgRoot: string, subpath: string): ExportResolution {\n try {\n const pkgJsonPath = path.join(pkgRoot, 'package.json');\n if (!fs.existsSync(pkgJsonPath)) {\n return { resolvedPath: null, hasSource: false, exportFound: false };\n }\n const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8'));\n\n if (pkgJson.exports) {\n const exportsObj = pkgJson.exports;\n let target = exportsObj[subpath];\n let matchedSub: string | null = null;\n\n if (!target && typeof exportsObj === 'object' && exportsObj !== null) {\n let bestPrefix = '';\n let bestKey: string | null = null;\n\n for (const key of Object.keys(exportsObj)) {\n if (key.includes('*')) {\n const starIdx = key.indexOf('*');\n const prefix = key.slice(0, starIdx);\n const suffix = key.slice(starIdx + 1);\n\n if (\n subpath.startsWith(prefix) &&\n subpath.endsWith(suffix) &&\n subpath.length >= prefix.length + suffix.length\n ) {\n if (prefix.length >= bestPrefix.length) {\n bestPrefix = prefix;\n bestKey = key;\n }\n }\n }\n }\n\n if (bestKey !== null) {\n const starIdx = bestKey.indexOf('*');\n const prefix = bestKey.slice(0, starIdx);\n const suffix = bestKey.slice(starIdx + 1);\n matchedSub = subpath.slice(prefix.length, subpath.length - suffix.length);\n target = exportsObj[bestKey];\n }\n }\n\n if (!target && subpath === '.') {\n if (\n typeof exportsObj === 'string' ||\n exportsObj.source ||\n exportsObj.import ||\n exportsObj.default\n ) {\n target = exportsObj;\n }\n }\n if (!target) {\n return { resolvedPath: null, hasSource: false, exportFound: false };\n }\n\n const substitute = (val: string) =>\n matchedSub !== null ? val.replaceAll('*', matchedSub) : val;\n\n if (typeof target === 'string') {\n return {\n resolvedPath: path.resolve(pkgRoot, substitute(target)),\n hasSource: true,\n exportFound: true,\n };\n }\n if (typeof target === 'object' && target !== null) {\n if (typeof target.source === 'string') {\n return {\n resolvedPath: path.resolve(pkgRoot, substitute(target.source)),\n hasSource: true,\n exportFound: true,\n };\n }\n return { resolvedPath: null, hasSource: false, exportFound: true };\n }\n }\n\n if (subpath === '.' && typeof pkgJson.source === 'string') {\n return {\n resolvedPath: path.resolve(pkgRoot, pkgJson.source),\n hasSource: true,\n exportFound: true,\n };\n }\n } catch {\n // Ignore JSON parse errors\n }\n return { resolvedPath: null, hasSource: false, exportFound: false };\n}\n\nexport function resolveModulePath(fromFile: string, importSpecifier: string): string | null {\n if (importSpecifier.startsWith('.')) {\n const dir = path.dirname(fromFile);\n const resolvedBase = path.resolve(dir, importSpecifier);\n\n const extensions = ['.ts', '.tsx', '.js', '.jsx', '.astro', '/index.ts', '/index.js'];\n for (const ext of extensions) {\n const candidate = resolvedBase + ext;\n if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {\n return candidate;\n }\n }\n\n if (fs.existsSync(resolvedBase) && fs.statSync(resolvedBase).isFile()) {\n return resolvedBase;\n }\n\n return null;\n }\n\n const { packageName, subpath } = parseBareSpecifier(importSpecifier);\n const pkgRoot = findPackageRoot(fromFile, packageName);\n if (!pkgRoot) {\n return null;\n }\n const result = resolvePackageExport(pkgRoot, subpath);\n if (result.hasSource && result.resolvedPath && fs.existsSync(result.resolvedPath)) {\n return result.resolvedPath;\n }\n return null;\n}\n\nexport function getStringLiteralValue(node: ts.Node): string | null {\n if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {\n return node.text;\n }\n return null;\n}\n\n/** Resolves a group shape value (e.g. `f.text`, `f.text('key')`, `hero.text`) to its member method name. */\nfunction shapeValueMethodName(node: ts.Node): string | null {\n const target = ts.isCallExpression(node) ? node.expression : node;\n if (ts.isPropertyAccessExpression(target)) {\n return target.name.text;\n }\n return null;\n}\n\nexport function extractGroupFromCall(\n callExpr: ts.CallExpression,\n fallbackName: string,\n sf: ts.SourceFile,\n diagnostics: ExtractionDiagnostic[] = [],\n getLocation?: (node: ts.Node) => SourceLocation,\n): { groupName: string; shape: Record<string, string> } | null {\n const args = callExpr.arguments;\n let groupName = fallbackName;\n let shapeNode: ts.Node | null = null;\n\n if (args.length === 1) {\n shapeNode = args[0];\n } else if (args.length >= 2) {\n const literalName = getStringLiteralValue(args[0]);\n if (literalName) {\n groupName = literalName;\n }\n shapeNode = args[1];\n }\n\n if (shapeNode && ts.isObjectLiteralExpression(shapeNode)) {\n const known = knownFieldMethodNames();\n const shape: Record<string, string> = {};\n for (const prop of shapeNode.properties) {\n if (ts.isPropertyAssignment(prop)) {\n const propName = prop.name.getText(sf);\n const methodName = shapeValueMethodName(prop.initializer);\n\n if (methodName && known.has(methodName)) {\n shape[propName] = normalizeFieldType(methodName);\n } else {\n shape[propName] = 'text';\n if (getLocation) {\n diagnostics.push({\n type: 'warning',\n message: `Group '${fallbackName}' field '${propName}' does not reference a known field helper (got '${prop.initializer.getText(sf)}'); defaulting to 'text'`,\n file: getLocation(prop).file,\n line: getLocation(prop).line,\n column: getLocation(prop).column,\n });\n }\n }\n } else if (ts.isShorthandPropertyAssignment(prop)) {\n const propName = prop.name.getText(sf);\n shape[propName] = 'text';\n }\n }\n return { groupName, shape };\n }\n\n return null;\n}\n\nasync function checkIsGroupOrCollection(\n filePath: string,\n symbolName: string,\n rootPath: string,\n cachedParsedFiles: Map<string, ParsedFile>,\n visited = new Set<string>(),\n): Promise<boolean> {\n const visitKey = `${filePath}::${symbolName}`;\n if (visited.has(visitKey)) return false;\n visited.add(visitKey);\n\n let parsed = cachedParsedFiles.get(filePath);\n if (!parsed) {\n parsed = await parseFile(filePath, rootPath);\n cachedParsedFiles.set(filePath, parsed);\n }\n\n for (const stmt of parsed.sourceFile.statements) {\n if (ts.isVariableStatement(stmt)) {\n for (const decl of stmt.declarationList.declarations) {\n if (decl.name.getText(parsed.sourceFile) === symbolName && decl.initializer) {\n if (ts.isCallExpression(decl.initializer)) {\n const callText = decl.initializer.expression.getText(parsed.sourceFile);\n if (\n callText === 'f.group' ||\n callText === 'defineGroup' ||\n callText.endsWith('.group') ||\n callText === 'defineCollection' ||\n callText === 'f.defineCollection'\n ) {\n return true;\n }\n } else if (\n ts.isArrowFunction(decl.initializer) ||\n ts.isFunctionExpression(decl.initializer)\n ) {\n let found = false;\n ts.forEachChild(decl.initializer.body, (child) => {\n if (ts.isCallExpression(child)) {\n const t = child.expression.getText(parsed.sourceFile);\n if (t === 'defineCollection' || t === 'f.defineCollection') found = true;\n }\n });\n if (found) return true;\n }\n }\n }\n }\n\n if (ts.isFunctionDeclaration(stmt) && stmt.name?.text === symbolName) {\n if (stmt.body) {\n let found = false;\n ts.forEachChild(stmt.body, (child) => {\n if (ts.isCallExpression(child)) {\n const t = child.expression.getText(parsed.sourceFile);\n if (t === 'defineCollection' || t === 'f.defineCollection') found = true;\n }\n });\n if (found) return true;\n }\n }\n\n // Follow re-export\n if (ts.isExportDeclaration(stmt) && stmt.moduleSpecifier) {\n const specifier = getStringLiteralValue(stmt.moduleSpecifier);\n if (specifier && stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {\n for (const el of stmt.exportClause.elements) {\n if (el.name.text === symbolName) {\n const targetSymbol = el.propertyName ? el.propertyName.text : el.name.text;\n const targetModulePath = resolveModulePath(parsed.filePath, specifier);\n if (targetModulePath) {\n const res = await checkIsGroupOrCollection(\n targetModulePath,\n targetSymbol,\n rootPath,\n cachedParsedFiles,\n visited,\n );\n if (res) return true;\n }\n }\n }\n }\n }\n\n // Follow import\n if (ts.isImportDeclaration(stmt) && stmt.moduleSpecifier) {\n const specifier = getStringLiteralValue(stmt.moduleSpecifier);\n if (specifier && stmt.importClause?.namedBindings) {\n if (ts.isNamedImports(stmt.importClause.namedBindings)) {\n for (const el of stmt.importClause.namedBindings.elements) {\n if (el.name.text === symbolName) {\n const targetSymbol = el.propertyName ? el.propertyName.text : el.name.text;\n const targetModulePath = resolveModulePath(parsed.filePath, specifier);\n if (targetModulePath) {\n const res = await checkIsGroupOrCollection(\n targetModulePath,\n targetSymbol,\n rootPath,\n cachedParsedFiles,\n visited,\n );\n if (res) return true;\n }\n }\n }\n }\n }\n }\n }\n\n return false;\n}\n\nexport async function resolveFileContext(\n parsed: ParsedFile,\n rootPath = process.cwd(),\n cachedParsedFiles: Map<string, ParsedFile> = new Map(),\n): Promise<ResolvedContext> {\n const scopes = new Map<string, { prefix: string; location: SourceLocation }>();\n const groups = new Map<string, ResolvedGroup>();\n const groupInstances = new Map<\n string,\n { groupName: string; prefix: string; shape: Record<string, string>; location: SourceLocation }\n >();\n const collections = new Map<string, ExtractedCollection>();\n const diagnostics: ExtractionDiagnostic[] = [];\n\n const { sourceFile, getLocation } = parsed;\n\n // 1. Scan top-level imports to track potential group/collection imports\n const importedSymbols = new Map<\n string,\n { importName: string; moduleSpecifier: string; node: ts.ImportDeclaration }\n >();\n\n for (const statement of sourceFile.statements) {\n if (ts.isImportDeclaration(statement)) {\n const moduleSpecifier = getStringLiteralValue(statement.moduleSpecifier);\n if (moduleSpecifier && statement.importClause?.namedBindings) {\n if (ts.isNamedImports(statement.importClause.namedBindings)) {\n for (const element of statement.importClause.namedBindings.elements) {\n const importName = element.propertyName ? element.propertyName.text : element.name.text;\n const localName = element.name.text;\n importedSymbols.set(localName, {\n importName,\n moduleSpecifier,\n node: statement,\n });\n }\n }\n }\n }\n }\n\n // Check loud failure on top-level export const X = <importedIdent>(...)\n for (const statement of sourceFile.statements) {\n if (ts.isVariableStatement(statement)) {\n const isExported = statement.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword);\n if (isExported) {\n for (const decl of statement.declarationList.declarations) {\n if (decl.initializer && ts.isCallExpression(decl.initializer)) {\n const calleeText = decl.initializer.expression.getText(sourceFile);\n const importInfo = importedSymbols.get(calleeText);\n if (importInfo && !importInfo.moduleSpecifier.startsWith('.')) {\n const { packageName, subpath } = parseBareSpecifier(importInfo.moduleSpecifier);\n const pkgRoot = findPackageRoot(parsed.filePath, packageName);\n if (pkgRoot) {\n const res = resolvePackageExport(pkgRoot, subpath);\n if (res.exportFound && !res.hasSource) {\n diagnostics.push({\n type: 'error',\n message: `Package '${packageName}' export '${subpath}' lacks a 'source' condition required for static extraction`,\n file: parsed.relativePath,\n line: getLocation(decl).line,\n column: getLocation(decl).column,\n });\n }\n }\n }\n }\n }\n }\n }\n }\n\n // 2. Scan AST for in-file scopes, groups, and collections\n function walk(node: ts.Node) {\n if (ts.isVariableDeclaration(node) && node.initializer) {\n const varName = node.name.getText(sourceFile);\n\n if (ts.isCallExpression(node.initializer)) {\n const callText = node.initializer.expression.getText(sourceFile);\n\n // f.scope(...)\n if (callText === 'f.scope' || callText.endsWith('.scope')) {\n const firstArg = node.initializer.arguments[0];\n if (!firstArg) {\n diagnostics.push({\n type: 'error',\n message: `Scope '${varName}' declared without a prefix`,\n file: parsed.relativePath,\n line: getLocation(node).line,\n column: getLocation(node).column,\n });\n } else {\n const prefix = getStringLiteralValue(firstArg);\n if (prefix === null) {\n diagnostics.push({\n type: 'error',\n message: `Scope prefix for '${varName}' must be a static string literal, got non-literal expression`,\n file: parsed.relativePath,\n line: getLocation(firstArg).line,\n column: getLocation(firstArg).column,\n });\n } else {\n if (callText.endsWith('.scope') && callText !== 'f.scope') {\n const parentVar = callText.replace(/\\.scope$/, '');\n const parentScope = scopes.get(parentVar);\n const fullPrefix = parentScope ? `${parentScope.prefix}.${prefix}` : prefix;\n scopes.set(varName, {\n prefix: fullPrefix,\n location: getLocation(node),\n });\n } else {\n scopes.set(varName, {\n prefix,\n location: getLocation(node),\n });\n }\n }\n }\n }\n\n // f.group(...) or defineGroup(...)\n if (callText === 'f.group' || callText === 'defineGroup' || callText.endsWith('.group')) {\n const groupInfo = extractGroupFromCall(\n node.initializer,\n varName,\n sourceFile,\n diagnostics,\n getLocation,\n );\n if (groupInfo) {\n groups.set(varName, {\n groupName: groupInfo.groupName,\n shape: groupInfo.shape,\n definedLocation: getLocation(node),\n hopCount: 0,\n });\n }\n }\n }\n }\n\n ts.forEachChild(node, walk);\n }\n\n walk(sourceFile);\n\n // 3. Resolve 1-hop imported groups and collections\n for (const [localName, importInfo] of importedSymbols) {\n const resolvedPath = resolveModulePath(parsed.filePath, importInfo.moduleSpecifier);\n if (!resolvedPath) {\n continue;\n }\n\n let targetParsed = cachedParsedFiles.get(resolvedPath);\n if (!targetParsed) {\n targetParsed = await parseFile(resolvedPath, rootPath);\n cachedParsedFiles.set(resolvedPath, targetParsed);\n }\n\n for (const stmt of targetParsed.sourceFile.statements) {\n // Check 2-hop re-export\n if (ts.isExportDeclaration(stmt) && stmt.moduleSpecifier) {\n const specifier = getStringLiteralValue(stmt.moduleSpecifier);\n if (specifier && stmt.exportClause && ts.isNamedExports(stmt.exportClause)) {\n for (const el of stmt.exportClause.elements) {\n if (el.name.text === importInfo.importName) {\n const targetSymbol = el.propertyName ? el.propertyName.text : el.name.text;\n const nextPath = resolveModulePath(targetParsed.filePath, specifier);\n const isGroupOrColl = nextPath\n ? await checkIsGroupOrCollection(\n nextPath,\n targetSymbol,\n rootPath,\n cachedParsedFiles,\n )\n : false;\n if (isGroupOrColl) {\n diagnostics.push({\n type: 'error',\n message: `Group or collection '${importInfo.importName}' is imported across multiple hops (> 1 hop) via '${importInfo.moduleSpecifier}'. It must be defined in the directly imported module.`,\n file: parsed.relativePath,\n line: getLocation(importInfo.node).line,\n column: getLocation(importInfo.node).column,\n });\n }\n }\n }\n }\n }\n\n // Check 2-hop import\n if (ts.isImportDeclaration(stmt) && stmt.moduleSpecifier) {\n const specifier = getStringLiteralValue(stmt.moduleSpecifier);\n if (specifier && stmt.importClause?.namedBindings) {\n if (ts.isNamedImports(stmt.importClause.namedBindings)) {\n for (const el of stmt.importClause.namedBindings.elements) {\n if (el.name.text === importInfo.importName) {\n const targetSymbol = el.propertyName ? el.propertyName.text : el.name.text;\n const nextPath = resolveModulePath(targetParsed.filePath, specifier);\n const isGroupOrColl = nextPath\n ? await checkIsGroupOrCollection(\n nextPath,\n targetSymbol,\n rootPath,\n cachedParsedFiles,\n )\n : false;\n if (isGroupOrColl) {\n diagnostics.push({\n type: 'error',\n message: `Group or collection '${importInfo.importName}' is imported across multiple hops (> 1 hop) via '${importInfo.moduleSpecifier}'. It must be defined in the directly imported module.`,\n file: parsed.relativePath,\n line: getLocation(importInfo.node).line,\n column: getLocation(importInfo.node).column,\n });\n }\n }\n }\n }\n }\n }\n\n // Check export const Hero = f.group(...) or export const blog = defineCollection(...)\n if (ts.isVariableStatement(stmt)) {\n for (const decl of stmt.declarationList.declarations) {\n if (\n decl.name.getText(targetParsed.sourceFile) === importInfo.importName &&\n decl.initializer\n ) {\n if (ts.isCallExpression(decl.initializer)) {\n const callText = decl.initializer.expression.getText(targetParsed.sourceFile);\n if (\n callText === 'f.group' ||\n callText === 'defineGroup' ||\n callText.endsWith('.group')\n ) {\n const groupInfo = extractGroupFromCall(\n decl.initializer,\n importInfo.importName,\n targetParsed.sourceFile,\n diagnostics,\n targetParsed.getLocation,\n );\n if (groupInfo) {\n groups.set(localName, {\n groupName: groupInfo.groupName,\n shape: groupInfo.shape,\n definedLocation: targetParsed.getLocation(decl),\n hopCount: 1,\n });\n }\n } else if (callText === 'defineCollection' || callText === 'f.defineCollection') {\n const coll = extractCollectionFromCall(\n decl.initializer,\n targetParsed.sourceFile,\n targetParsed.getLocation,\n diagnostics,\n targetParsed.relativePath,\n );\n if (coll) {\n collections.set(localName, coll);\n }\n }\n } else if (\n ts.isArrowFunction(decl.initializer) ||\n ts.isFunctionExpression(decl.initializer)\n ) {\n const findCall = (n: ts.Node): ts.CallExpression | null => {\n if (ts.isCallExpression(n)) {\n const t = n.expression.getText(targetParsed.sourceFile);\n if (t === 'defineCollection' || t === 'f.defineCollection') return n;\n }\n let res: ts.CallExpression | null = null;\n ts.forEachChild(n, (child) => {\n if (!res) res = findCall(child);\n });\n return res;\n };\n const call = findCall(decl.initializer.body);\n if (call) {\n const coll = extractCollectionFromCall(\n call,\n targetParsed.sourceFile,\n targetParsed.getLocation,\n diagnostics,\n targetParsed.relativePath,\n );\n if (coll) {\n collections.set(localName, coll);\n }\n }\n }\n }\n }\n }\n\n // Check export function X() { return defineCollection(...) }\n if (ts.isFunctionDeclaration(stmt) && stmt.name?.text === importInfo.importName) {\n const findCall = (n: ts.Node): ts.CallExpression | null => {\n if (ts.isCallExpression(n)) {\n const t = n.expression.getText(targetParsed.sourceFile);\n if (t === 'defineCollection' || t === 'f.defineCollection') return n;\n }\n let res: ts.CallExpression | null = null;\n ts.forEachChild(n, (child) => {\n if (!res) res = findCall(child);\n });\n return res;\n };\n if (stmt.body) {\n const call = findCall(stmt.body);\n if (call) {\n const coll = extractCollectionFromCall(\n call,\n targetParsed.sourceFile,\n targetParsed.getLocation,\n diagnostics,\n targetParsed.relativePath,\n );\n if (coll) {\n collections.set(localName, coll);\n }\n }\n }\n }\n }\n }\n\n // 4. Second pass to record resolved group instances and check for dynamic scopes\n function walkInstances(node: ts.Node) {\n if (ts.isVariableDeclaration(node) && node.initializer) {\n const varName = node.name.getText(sourceFile);\n if (ts.isCallExpression(node.initializer)) {\n const calleeName = node.initializer.expression.getText(sourceFile);\n const resolvedGroup = groups.get(calleeName);\n\n if (resolvedGroup) {\n const firstArg = node.initializer.arguments[0];\n if (!firstArg) {\n diagnostics.push({\n type: 'error',\n message: `Group '${calleeName}' instantiated without a scope prefix`,\n file: parsed.relativePath,\n line: getLocation(node).line,\n column: getLocation(node).column,\n });\n } else {\n const prefix = getStringLiteralValue(firstArg);\n if (prefix === null) {\n diagnostics.push({\n type: 'error',\n message: `Group '${calleeName}' instantiated with dynamic non-literal scope`,\n file: parsed.relativePath,\n line: getLocation(firstArg).line,\n column: getLocation(firstArg).column,\n });\n } else {\n groupInstances.set(varName, {\n groupName: resolvedGroup.groupName,\n prefix,\n shape: resolvedGroup.shape,\n location: getLocation(node),\n });\n }\n }\n }\n }\n }\n ts.forEachChild(node, walkInstances);\n }\n\n walkInstances(sourceFile);\n\n return {\n scopes,\n groups,\n groupInstances,\n collections,\n diagnostics,\n };\n}\n","import { listFieldTypes, resolveTypeAlias } from '@407dev/field-types';\n\n/** Field helper method names (`f.<name>`, `<group>.<name>`), derived from the field-types registry. */\nexport function knownFieldMethodNames(): Set<string> {\n const types = listFieldTypes();\n const names = new Set<string>();\n for (const def of types) {\n names.add(def.id);\n if (def.aliases) {\n for (const alias of def.aliases) {\n names.add(alias);\n }\n }\n }\n return names;\n}\n\n/** Maps a helper method name (e.g. 'img', 'videoList') to its canonical registry type id (e.g. 'image', 'media.videoList'). */\nexport function normalizeFieldType(methodName: string): string {\n return resolveTypeAlias(methodName);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { convertToTSX } from '@astrojs/compiler';\nimport { type AnyMap, TraceMap, originalPositionFor } from '@jridgewell/trace-mapping';\nimport ts from 'typescript';\nimport type { SourceLocation } from './types';\n\nexport interface ParsedFile {\n filePath: string;\n relativePath: string;\n isAstro: boolean;\n sourceFile: ts.SourceFile;\n traceMap?: TraceMap;\n getLocation: (node: ts.Node) => SourceLocation;\n}\n\nexport async function parseFile(filePath: string, rootPath = process.cwd()): Promise<ParsedFile> {\n const content = fs.readFileSync(filePath, 'utf-8');\n const relativePath = path.relative(rootPath, filePath).replace(/\\\\/g, '/');\n const isAstro = filePath.endsWith('.astro');\n\n if (isAstro) {\n const { code, map } = await convertToTSX(content, {\n sourcemap: 'both',\n });\n\n // biome-ignore lint/suspicious/noExplicitAny: TraceMap accepts various map formats\n const traceMap = map ? new TraceMap(map as any) : undefined;\n const sourceFile = ts.createSourceFile(\n filePath,\n code,\n ts.ScriptTarget.Latest,\n true,\n ts.ScriptKind.TSX,\n );\n\n const getLocation = (node: ts.Node): SourceLocation => {\n const pos = node.getStart(sourceFile);\n const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);\n // TS line is 0-indexed, column is 0-indexed\n if (traceMap) {\n const orig = originalPositionFor(traceMap, {\n line: line + 1,\n column: character,\n });\n if (orig && orig.line !== null && orig.column !== null) {\n return {\n file: relativePath,\n line: orig.line,\n column: orig.column + 1,\n };\n }\n }\n return {\n file: relativePath,\n line: line + 1,\n column: character + 1,\n };\n };\n\n return {\n filePath,\n relativePath,\n isAstro: true,\n sourceFile,\n traceMap,\n getLocation,\n };\n }\n\n const isTsx = filePath.endsWith('.tsx') || filePath.endsWith('.jsx');\n const scriptKind = isTsx\n ? ts.ScriptKind.TSX\n : filePath.endsWith('.ts')\n ? ts.ScriptKind.TS\n : ts.ScriptKind.JS;\n\n const sourceFile = ts.createSourceFile(\n filePath,\n content,\n ts.ScriptTarget.Latest,\n true,\n scriptKind,\n );\n\n const getLocation = (node: ts.Node): SourceLocation => {\n const pos = node.getStart(sourceFile);\n const { line, character } = sourceFile.getLineAndCharacterOfPosition(pos);\n return {\n file: relativePath,\n line: line + 1,\n column: character + 1,\n };\n };\n\n return {\n filePath,\n relativePath,\n isAstro: false,\n sourceFile,\n getLocation,\n };\n}\n\nexport function globSourceFiles(dir: string): string[] {\n const files: string[] = [];\n\n function walk(currentDir: string) {\n if (!fs.existsSync(currentDir)) return;\n const entries = fs.readdirSync(currentDir, { withFileTypes: true });\n for (const entry of entries) {\n if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist') {\n continue;\n }\n const full = path.join(currentDir, entry.name);\n if (entry.isDirectory()) {\n walk(full);\n } else if (\n /\\.(astro|ts|tsx|js|jsx)$/.test(entry.name) &&\n !entry.name.endsWith('.d.ts') &&\n !entry.name.includes('.test.') &&\n !entry.name.includes('.spec.')\n ) {\n files.push(full);\n }\n }\n }\n\n walk(dir);\n return files.sort();\n}\n","import ts from 'typescript';\nimport type { ExtractionDiagnostic, SourceLocation } from './types';\n\nexport interface EvalLiteralResult {\n ok: boolean;\n value?: unknown;\n errorNode?: ts.Node;\n}\n\nexport function unwrapTypeAssertions(node: ts.Node): ts.Node {\n let current = node;\n while (\n ts.isAsExpression(current) ||\n ts.isTypeAssertionExpression(current) ||\n (ts.isSatisfiesExpression?.(current) ?? false) ||\n ts.isParenthesizedExpression(current)\n ) {\n current = (current as ts.AsExpression | ts.TypeAssertion | ts.ParenthesizedExpression)\n .expression;\n }\n return current;\n}\n\nexport function evalStaticJson(rawNode: ts.Node): EvalLiteralResult {\n const node = unwrapTypeAssertions(rawNode);\n\n if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node)) {\n return { ok: true, value: node.text };\n }\n if (ts.isNumericLiteral(node)) {\n return { ok: true, value: Number(node.text) };\n }\n if (node.kind === ts.SyntaxKind.TrueKeyword) {\n return { ok: true, value: true };\n }\n if (node.kind === ts.SyntaxKind.FalseKeyword) {\n return { ok: true, value: false };\n }\n if (node.kind === ts.SyntaxKind.NullKeyword) {\n return { ok: true, value: null };\n }\n if (ts.isPrefixUnaryExpression(node)) {\n const operand = unwrapTypeAssertions(node.operand);\n if (node.operator === ts.SyntaxKind.MinusToken && ts.isNumericLiteral(operand)) {\n return { ok: true, value: -Number(operand.text) };\n }\n if (node.operator === ts.SyntaxKind.PlusToken && ts.isNumericLiteral(operand)) {\n return { ok: true, value: Number(operand.text) };\n }\n return { ok: false, errorNode: node };\n }\n if (ts.isArrayLiteralExpression(node)) {\n const arr: unknown[] = [];\n for (const el of node.elements) {\n if (ts.isSpreadElement(el)) {\n return { ok: false, errorNode: el };\n }\n const res = evalStaticJson(el);\n if (!res.ok) return res;\n arr.push(res.value);\n }\n return { ok: true, value: arr };\n }\n if (ts.isObjectLiteralExpression(node)) {\n const obj: Record<string, unknown> = {};\n for (const prop of node.properties) {\n if (!ts.isPropertyAssignment(prop)) {\n return { ok: false, errorNode: prop };\n }\n let propName: string;\n if (\n ts.isIdentifier(prop.name) ||\n ts.isStringLiteral(prop.name) ||\n ts.isNoSubstitutionTemplateLiteral(prop.name)\n ) {\n propName = prop.name.text;\n } else if (ts.isNumericLiteral(prop.name)) {\n propName = prop.name.text;\n } else {\n return { ok: false, errorNode: prop.name };\n }\n const valRes = evalStaticJson(prop.initializer);\n if (!valRes.ok) return valRes;\n obj[propName] = valRes.value;\n }\n return { ok: true, value: obj };\n }\n\n return { ok: false, errorNode: node };\n}\n\nexport function extractCollectionSeed(\n rawSeedNode: ts.Node,\n sourceFile: ts.SourceFile,\n getLocation: (node: ts.Node) => SourceLocation,\n relativePath: string,\n diagnostics: ExtractionDiagnostic[],\n): Array<{ slug: string; content: Record<string, unknown> }> | undefined {\n const seedNode = unwrapTypeAssertions(rawSeedNode);\n let targetArrayNode: ts.Node | undefined;\n\n if (ts.isIdentifier(seedNode)) {\n const idName = seedNode.text;\n let found = false;\n let isConst = false;\n let declInit: ts.Expression | undefined;\n\n function walk(node: ts.Node) {\n if (found) return;\n if (ts.isVariableStatement(node)) {\n const hasConst = Boolean(node.declarationList.flags & ts.NodeFlags.Const);\n for (const decl of node.declarationList.declarations) {\n if (ts.isIdentifier(decl.name) && decl.name.text === idName) {\n found = true;\n isConst = hasConst;\n declInit = decl.initializer;\n return;\n }\n }\n }\n ts.forEachChild(node, walk);\n }\n\n walk(sourceFile);\n\n const unwrappedDeclInit = declInit ? unwrapTypeAssertions(declInit) : undefined;\n if (\n !found ||\n !isConst ||\n !unwrappedDeclInit ||\n !ts.isArrayLiteralExpression(unwrappedDeclInit)\n ) {\n diagnostics.push({\n type: 'error',\n message: 'seed must be a literal',\n file: relativePath,\n line: getLocation(\n declInit && !ts.isArrayLiteralExpression(unwrappedDeclInit ?? declInit)\n ? declInit\n : seedNode,\n ).line,\n column: getLocation(\n declInit && !ts.isArrayLiteralExpression(unwrappedDeclInit ?? declInit)\n ? declInit\n : seedNode,\n ).column,\n });\n return undefined;\n }\n\n targetArrayNode = unwrappedDeclInit;\n } else if (ts.isArrayLiteralExpression(seedNode)) {\n targetArrayNode = seedNode;\n } else {\n diagnostics.push({\n type: 'error',\n message: 'seed must be a literal',\n file: relativePath,\n line: getLocation(seedNode).line,\n column: getLocation(seedNode).column,\n });\n return undefined;\n }\n\n const evalResult = evalStaticJson(targetArrayNode);\n if (!evalResult.ok || !Array.isArray(evalResult.value)) {\n const errNode = evalResult.errorNode || targetArrayNode;\n diagnostics.push({\n type: 'error',\n message: 'seed must be a literal',\n file: relativePath,\n line: getLocation(errNode).line,\n column: getLocation(errNode).column,\n });\n return undefined;\n }\n\n return evalResult.value as Array<{ slug: string; content: Record<string, unknown> }>;\n}\n","import type {\n SiteManifest,\n SiteManifestCollection,\n SiteManifestField,\n SiteManifestRoute,\n} from '@407dev/api-client';\nimport { validateContent } from '@407dev/blocks';\nimport type { ExtractionDiagnostic, MergedExtractionResult } from './types';\n\nexport function buildSiteManifest(\n merged: MergedExtractionResult,\n extraRoutes: SiteManifestRoute[] = [],\n): { manifest: SiteManifest; diagnostics: ExtractionDiagnostic[] } {\n const diagnostics: ExtractionDiagnostic[] = [...merged.diagnostics];\n const fields: Record<string, SiteManifestField> = {};\n const collections: Record<string, SiteManifestCollection> = {};\n\n // 1. Convert fields\n for (const [key, field] of Object.entries(merged.fields)) {\n const uniqueFiles = Array.from(new Set(field.sources.map((s) => s.file))).sort();\n const sourcePath = uniqueFiles.join(',');\n\n const manifestField: SiteManifestField = {\n type: field.type,\n defaultValue: field.defaultValue,\n constraints: field.constraints,\n group: field.group,\n sourcePath: sourcePath || undefined,\n };\n\n fields[key] = manifestField;\n }\n\n // 2. Convert and validate collections\n for (const [key, coll] of Object.entries(merged.collections)) {\n const schemaProps = (coll.jsonSchema.properties || {}) as Record<string, unknown>;\n\n // Check titleField\n if (coll.titleField && !(coll.titleField in schemaProps)) {\n diagnostics.push({\n type: 'error',\n message: `Collection '${key}' specifies titleField '${coll.titleField}' which does not exist in its schema properties`,\n file: coll.location.file,\n line: coll.location.line,\n column: coll.location.column,\n });\n }\n\n // Check bodyField\n if (coll.bodyField && !(coll.bodyField in schemaProps)) {\n diagnostics.push({\n type: 'error',\n message: `Collection '${key}' specifies bodyField '${coll.bodyField}' which does not exist in its schema properties`,\n file: coll.location.file,\n line: coll.location.line,\n column: coll.location.column,\n });\n }\n\n // Check listColumns\n if (coll.listColumns) {\n for (const col of coll.listColumns) {\n if (!(col in schemaProps)) {\n diagnostics.push({\n type: 'error',\n message: `Collection '${key}' specifies listColumn '${col}' which does not exist in its schema properties`,\n file: coll.location.file,\n line: coll.location.line,\n column: coll.location.column,\n });\n }\n }\n }\n\n // Validate seed if present\n if (coll.seed) {\n const seenSlugs = new Set<string>();\n for (const item of coll.seed) {\n if (!item || typeof item !== 'object' || !item.slug || typeof item.slug !== 'string') {\n diagnostics.push({\n type: 'error',\n message: `Collection '${key}' seed item must be an object with a 'slug' string`,\n file: coll.location.file,\n line: coll.location.line,\n column: coll.location.column,\n });\n continue;\n }\n\n if (seenSlugs.has(item.slug)) {\n diagnostics.push({\n type: 'error',\n message: `Collection '${key}' seed has duplicate slug '${item.slug}'`,\n file: coll.location.file,\n line: coll.location.line,\n column: coll.location.column,\n });\n }\n seenSlugs.add(item.slug);\n\n const valResult = validateContent(coll.jsonSchema, item.content || {});\n if (!valResult.valid) {\n for (const err of valResult.errors ?? []) {\n diagnostics.push({\n type: 'error',\n message: `Collection '${key}' seed item '${item.slug}' validation failed: ${err.message}`,\n file: coll.location.file,\n line: coll.location.line,\n column: coll.location.column,\n });\n }\n }\n }\n }\n\n collections[key] = {\n jsonSchema: coll.jsonSchema,\n schemaVersion: coll.schemaVersion || 1,\n orderable: coll.orderable,\n routePattern: coll.routePattern,\n presentation: coll.presentation,\n titleField: coll.titleField,\n listColumns: coll.listColumns,\n bodyField: coll.bodyField,\n group: coll.group,\n seed: coll.seed,\n };\n }\n\n // 3. Assemble routes\n const routes: SiteManifestRoute[] = [...extraRoutes];\n\n for (const [key, coll] of Object.entries(merged.collections)) {\n if (coll.routePattern) {\n routes.push({\n pathOrPattern: coll.routePattern,\n kind: 'collection',\n collectionKey: key,\n sourcePath: coll.location.file,\n });\n }\n }\n\n const manifest: SiteManifest = {\n manifestVersion: 1,\n fields,\n collections: Object.keys(collections).length > 0 ? collections : undefined,\n routes: routes.length > 0 ? routes : undefined,\n };\n\n return { manifest, diagnostics };\n}\n","import type {\n ExtractedCollection,\n ExtractedField,\n ExtractionDiagnostic,\n FileExtractionResult,\n MergedExtractionResult,\n MergedField,\n SourceLocation,\n} from './types';\n\nfunction areObjectsEqual(a?: Record<string, unknown>, b?: Record<string, unknown>): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n const keysA = Object.keys(a).sort();\n const keysB = Object.keys(b).sort();\n if (keysA.length !== keysB.length) return false;\n return JSON.stringify(a) === JSON.stringify(b);\n}\n\nexport function mergeExtractionResults(\n fileResults: FileExtractionResult[],\n): MergedExtractionResult {\n const mergedFields: Record<string, MergedField> = {};\n const collections: Record<string, ExtractedCollection> = {};\n const diagnostics: ExtractionDiagnostic[] = [];\n\n for (const res of fileResults) {\n // 1. Collect diagnostics\n diagnostics.push(...res.diagnostics);\n\n // 2. Merge collections\n for (const coll of res.collections) {\n if (!collections[coll.key]) {\n collections[coll.key] = coll;\n }\n }\n\n // 3. Merge fields\n for (const field of res.fields) {\n const existing = mergedFields[field.key];\n if (!existing) {\n mergedFields[field.key] = {\n type: field.type,\n constraints: field.constraints,\n defaultValue: field.defaultValue,\n group: field.group,\n sources: [field.location],\n };\n } else {\n // Check for conflicts: type, defaultValue, constraints\n const typeConflict = existing.type !== field.type;\n const defaultConflict =\n existing.defaultValue !== undefined &&\n field.defaultValue !== undefined &&\n existing.defaultValue !== field.defaultValue;\n const constraintsConflict = !areObjectsEqual(existing.constraints, field.constraints);\n\n if (typeConflict || defaultConflict || constraintsConflict) {\n const allSources = [...existing.sources, field.location];\n const locStr = allSources.map((s) => `${s.file}:${s.line}:${s.column}`).join(' vs ');\n\n diagnostics.push({\n type: 'error',\n message: `Conflicting field definition for key '${field.key}' (${locStr})`,\n file: field.location.file,\n line: field.location.line,\n column: field.location.column,\n });\n }\n\n // Merge sources and group\n existing.sources.push(field.location);\n if (!existing.group && field.group) {\n existing.group = field.group;\n }\n }\n }\n }\n\n return {\n fields: mergedFields,\n collections,\n routes: [],\n diagnostics,\n };\n}\n","import { execSync } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport type { SiteManifestRoute } from '@407dev/api-client';\nimport type { ExtractedCollection, ExtractionDiagnostic } from './types';\n\nexport interface AstroResolvedRoute {\n route?: string;\n component?: string;\n type?: string;\n params?: string[];\n}\n\nexport function shouldSyncRoutes(siteDir: string, cmsRoutesFile: string): boolean {\n if (!fs.existsSync(cmsRoutesFile)) {\n return true;\n }\n\n const routesStat = fs.statSync(cmsRoutesFile);\n const srcDir = path.join(siteDir, 'src');\n if (!fs.existsSync(srcDir)) {\n return false;\n }\n\n // Check if any file in src/ is newer than routes.json\n let newestMtime = 0;\n function checkMtime(dir: string) {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory() && !entry.name.startsWith('.') && entry.name !== 'node_modules') {\n checkMtime(full);\n } else if (entry.isFile()) {\n const stat = fs.statSync(full);\n if (stat.mtimeMs > newestMtime) {\n newestMtime = stat.mtimeMs;\n }\n }\n }\n }\n\n checkMtime(srcDir);\n return newestMtime > routesStat.mtimeMs;\n}\n\nexport function syncAstroRoutes(siteDir: string): boolean {\n try {\n execSync('npx astro sync', {\n cwd: siteDir,\n stdio: 'ignore',\n });\n return true;\n } catch {\n return false;\n }\n}\n\nexport function deriveRouteFromComponent(componentPath: string): string {\n let route = componentPath\n .replace(/\\\\/g, '/')\n .replace(/^.*src\\/pages\\/?/, '/')\n .replace(/\\.(astro|mdx|md|ts|js|jsx|tsx)$/, '')\n .replace(/\\/index$/, '');\n if (!route.startsWith('/')) {\n route = `/${route}`;\n }\n return route === '' ? '/' : route;\n}\n\nexport function scanPagesDirectory(siteDir: string): AstroResolvedRoute[] {\n const pagesDir = path.join(siteDir, 'src', 'pages');\n if (!fs.existsSync(pagesDir)) return [];\n\n const found: AstroResolvedRoute[] = [];\n function walk(dir: string) {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {\n walk(fullPath);\n }\n } else if (entry.isFile()) {\n const ext = path.extname(entry.name).toLowerCase();\n if (['.astro', '.md', '.mdx', '.html', '.ts', '.js', '.jsx', '.tsx'].includes(ext)) {\n if (entry.name.startsWith('_')) continue;\n const route = deriveRouteFromComponent(fullPath);\n const params: string[] = [];\n const matches = route.matchAll(/\\[([^\\]]+)\\]/g);\n for (const m of matches) {\n params.push(m[1]);\n }\n found.push({\n route,\n component: fullPath,\n type: 'page',\n params,\n });\n }\n }\n }\n }\n walk(pagesDir);\n return found;\n}\n\nexport function loadDerivedRoutes(\n siteDir: string,\n collections: Record<string, ExtractedCollection> = {},\n options: { noSync?: boolean } = {},\n): { routes: SiteManifestRoute[]; diagnostics: ExtractionDiagnostic[] } {\n const diagnostics: ExtractionDiagnostic[] = [];\n const cmsRoutesFile = path.join(siteDir, '.cms', 'routes.json');\n\n if (!options.noSync && shouldSyncRoutes(siteDir, cmsRoutesFile)) {\n syncAstroRoutes(siteDir);\n }\n\n const staticRoutes: SiteManifestRoute[] = [];\n const seenPatterns = new Set<string>();\n let astroRoutes: AstroResolvedRoute[] = [];\n\n if (fs.existsSync(cmsRoutesFile)) {\n try {\n const raw = fs.readFileSync(cmsRoutesFile, 'utf-8');\n astroRoutes = JSON.parse(raw);\n } catch {\n // Ignore parse error\n }\n }\n\n // Combine routes.json with filesystem scan of src/pages to guarantee detection\n const scannedRoutes = scanPagesDirectory(siteDir);\n const combinedRoutes = [...astroRoutes, ...scannedRoutes];\n\n const knownAstroPatterns = new Set<string>();\n\n for (const r of combinedRoutes) {\n if (r.type === 'endpoint') continue;\n let pattern = r.route || '';\n if (!pattern && r.component) {\n pattern = deriveRouteFromComponent(r.component);\n }\n if (!pattern || pattern === '/404' || pattern.startsWith('/_')) continue;\n\n knownAstroPatterns.add(pattern);\n\n // If route is static (no params)\n if (!r.params || r.params.length === 0) {\n if (!seenPatterns.has(pattern)) {\n seenPatterns.add(pattern);\n let relComponent: string | undefined;\n if (r.component) {\n const absComp = path.isAbsolute(r.component)\n ? r.component\n : path.resolve(siteDir, r.component);\n relComponent = path.relative(siteDir, absComp).replace(/\\\\/g, '/');\n }\n staticRoutes.push({\n pathOrPattern: pattern,\n kind: 'static',\n sourcePath: relComponent || undefined,\n });\n }\n }\n }\n\n // Cross-check collection routes\n for (const [key, coll] of Object.entries(collections)) {\n if (coll.routePattern) {\n const normalizedRoute = coll.routePattern.startsWith('/')\n ? coll.routePattern\n : `/${coll.routePattern}`;\n\n // Check against known dynamic patterns if we have routes.json\n if (knownAstroPatterns.size > 0 && !knownAstroPatterns.has(normalizedRoute)) {\n diagnostics.push({\n type: 'error',\n message: `Collection '${key}' specifies route '${coll.routePattern}' with no matching dynamic route found in Astro pages`,\n file: coll.location.file,\n line: coll.location.line,\n column: coll.location.column,\n });\n }\n }\n }\n\n return { routes: staticRoutes, diagnostics };\n}\n","import { extractProject, formatDiagnostics } from '../extract/check';\n\nexport interface CheckCommandOptions {\n siteDir?: string;\n noSync?: boolean;\n}\n\nexport interface CheckCommandResult {\n success: boolean;\n errorsCount: number;\n warningsCount: number;\n report: string;\n}\n\nexport async function executeCheck(options: CheckCommandOptions = {}): Promise<CheckCommandResult> {\n const result = await extractProject(options.siteDir, { noSync: options.noSync });\n const report = formatDiagnostics(result.diagnostics);\n\n return {\n success: result.errors.length === 0,\n errorsCount: result.errors.length,\n warningsCount: result.warnings.length,\n report,\n };\n}\n","import { extractProject } from '../extract/check';\n\nexport interface ExtractCommandOptions {\n siteDir?: string;\n noSync?: boolean;\n json?: boolean;\n}\n\nexport async function executeExtract(options: ExtractCommandOptions = {}): Promise<string> {\n const result = await extractProject(options.siteDir, { noSync: options.noSync });\n if (options.json) {\n return JSON.stringify(result.manifest, null, 2);\n }\n\n const lines: string[] = [];\n lines.push('đŚ Extracted Site Manifest:');\n lines.push(` Fields: ${Object.keys(result.manifest.fields).length}`);\n lines.push(` Collections: ${Object.keys(result.manifest.collections || {}).length}`);\n lines.push(` Routes: ${(result.manifest.routes || []).length}`);\n\n if (result.errors.length > 0) {\n lines.push(`\\nâ Found ${result.errors.length} error(s):`);\n for (const e of result.errors) {\n lines.push(` - ${e.file}:${e.line}:${e.column}: ${e.message}`);\n }\n }\n\n if (result.warnings.length > 0) {\n lines.push(`\\nâ ď¸ Found ${result.warnings.length} warning(s):`);\n for (const w of result.warnings) {\n lines.push(` - ${w.file}:${w.line}:${w.column}: ${w.message}`);\n }\n }\n\n return lines.join('\\n');\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport readline from 'node:readline/promises';\nimport { type SiteRecord, createApiClient } from '@407dev/api-client';\nimport { DEFAULT_DEV_API_URL, DEFAULT_DEV_CONTENT_URL } from '@407dev/config';\nimport { resolveAuthToken } from '../auth';\nimport { normalizeApiUrl } from '../credentials';\nimport { loadEnvFiles } from '../env';\nimport type { ApiClient } from '../types';\n\nexport interface LinkCommandOptions {\n siteId?: string;\n apiUrl?: string;\n siteDir?: string;\n apiClient?: ApiClient;\n selectPrompt?: (sites: SiteRecord[]) => Promise<SiteRecord>;\n}\n\nexport function mergeEnvContent(content: string, vars: Record<string, string>): string {\n const lines = content ? content.split('\\n') : [];\n const remainingKeys = new Set(Object.keys(vars));\n\n const updatedLines = lines.map((line) => {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) return line;\n const eqIdx = line.indexOf('=');\n if (eqIdx > 0) {\n const key = line.slice(0, eqIdx).trim();\n if (remainingKeys.has(key)) {\n remainingKeys.delete(key);\n return `${key}=${vars[key]}`;\n }\n }\n return line;\n });\n\n if (\n updatedLines.length > 0 &&\n updatedLines[updatedLines.length - 1].trim() !== '' &&\n remainingKeys.size > 0\n ) {\n updatedLines.push('');\n }\n\n for (const key of remainingKeys) {\n updatedLines.push(`${key}=${vars[key]}`);\n }\n\n let result = updatedLines.join('\\n');\n if (!result.endsWith('\\n')) {\n result += '\\n';\n }\n return result;\n}\n\nexport function mergeEnvFile(filePath: string, vars: Record<string, string>): void {\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf-8') : '';\n const merged = mergeEnvContent(existing, vars);\n fs.writeFileSync(filePath, merged, 'utf-8');\n}\n\nasync function defaultSelectPrompt(sites: SiteRecord[]): Promise<SiteRecord> {\n if (!process.stdin.isTTY) {\n if (sites.length === 1) {\n return sites[0];\n }\n throw new Error('Multiple sites available. Non-interactive shell must specify --site <id>.');\n }\n\n console.log('\\nAvailable sites:');\n sites.forEach((s, idx) => {\n console.log(` [${idx + 1}] ${s.name} (${s.id})`);\n });\n\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n try {\n while (true) {\n const ans = await rl.question(`\\nSelect site [1-${sites.length}]: `);\n const num = Number.parseInt(ans.trim(), 10);\n if (!Number.isNaN(num) && num >= 1 && num <= sites.length) {\n return sites[num - 1];\n }\n console.log(`Invalid selection. Please enter a number between 1 and ${sites.length}.`);\n }\n } finally {\n rl.close();\n }\n}\n\nexport async function executeLink(options: LinkCommandOptions = {}): Promise<string> {\n const siteDir = options.siteDir || process.cwd();\n loadEnvFiles(siteDir);\n\n const apiUrl = normalizeApiUrl(options.apiUrl || process.env.CMS_API_URL || DEFAULT_DEV_API_URL);\n\n // 1. Resolve contentUrl from /cli/config\n let contentUrl = DEFAULT_DEV_CONTENT_URL;\n try {\n const configRes = await fetch(`${apiUrl}/cli/config`);\n if (configRes.ok) {\n const configJson = (await configRes.json()) as {\n contentUrl?: string;\n data?: { contentUrl?: string };\n content_url?: string;\n };\n contentUrl =\n configJson.contentUrl ||\n configJson.data?.contentUrl ||\n configJson.content_url ||\n DEFAULT_DEV_CONTENT_URL;\n }\n } catch {\n // Fall back to default\n }\n\n // 2. Initialize API client\n let apiClient = options.apiClient;\n if (!apiClient) {\n const authToken = await resolveAuthToken(apiUrl);\n apiClient = createApiClient({\n baseUrl: apiUrl,\n authToken,\n });\n }\n\n // 3. Resolve site\n let selectedSiteId = options.siteId;\n let selectedSiteName = '';\n\n if (selectedSiteId) {\n const siteRes = await apiClient.getSite(selectedSiteId);\n if (siteRes.ok && siteRes.data) {\n selectedSiteName = siteRes.data.name;\n }\n } else {\n const sitesRes = await apiClient.getSites();\n if (!sitesRes.ok || !sitesRes.data || sitesRes.data.length === 0) {\n throw new Error('No sites found in workspace. Create a site in the web dashboard first.');\n }\n\n const sites = sitesRes.data;\n const promptFn = options.selectPrompt || defaultSelectPrompt;\n const chosen = await promptFn(sites);\n selectedSiteId = chosen.id;\n selectedSiteName = chosen.name;\n }\n\n if (!selectedSiteId) {\n throw new Error('Failed to resolve site ID to link');\n }\n\n // 4. Update .env\n const envPath = path.join(siteDir, '.env');\n mergeEnvFile(envPath, {\n CMS_SITE_ID: selectedSiteId,\n CMS_API_URL: apiUrl,\n CMS_CONTENT_URL: contentUrl,\n });\n\n // 5. Check astro.config.*\n let astroConfigCheck = '';\n const possibleConfigs = [\n 'astro.config.mjs',\n 'astro.config.ts',\n 'astro.config.js',\n 'astro.config.cjs',\n ];\n let configFound = false;\n let configConfigured = false;\n\n for (const cfg of possibleConfigs) {\n const fullPath = path.join(siteDir, cfg);\n if (fs.existsSync(fullPath)) {\n configFound = true;\n try {\n const content = fs.readFileSync(fullPath, 'utf-8');\n if (content.includes('cmsAstro') || content.includes('@407dev/cms-astro')) {\n configConfigured = true;\n }\n } catch {\n // Ignore read error\n }\n break;\n }\n }\n\n if (!configConfigured) {\n astroConfigCheck = `\nNext steps:\nAdd @407dev/cms-astro to your astro.config.mjs:\n import { cmsAstro } from '@407dev/cms-astro';\n\n export default defineConfig({\n integrations: [cmsAstro()],\n });\n`;\n }\n\n const siteDisplay = selectedSiteName\n ? `\"${selectedSiteName}\" (${selectedSiteId})`\n : selectedSiteId;\n\n return `⨠Linked site ${siteDisplay}\nUpdated .env:\n CMS_SITE_ID=${selectedSiteId}\n CMS_API_URL=${apiUrl}\n CMS_CONTENT_URL=${contentUrl}\n${astroConfigCheck ? `${astroConfigCheck}\\n` : ''}Ready to push schema: run \"cms push\"`;\n}\n","export const configVersion = '1.0.0';\n\nexport * from './preview-token.js';\n\nexport interface JwtSecretEnv {\n JWT_SECRET?: string;\n}\n\nexport interface PreviewTokenSecretEnv {\n PREVIEW_TOKEN_SECRET?: string;\n}\n\n// Local dev JWT secret used consistently across dev environments (.dev.vars, demo-site)\nexport const DEFAULT_DEV_JWT_SECRET =\n 'f44b0b9bf64e28c3b12e3c7ec5cec7b79f139d50f43655138caafe6b890565ce';\n\nexport const DEFAULT_DEV_ORCHESTRATOR_SECRET =\n 'dev-orchestrator-secret-f44b0b9bf64e28c3b12e3c7ec5cec7b79';\n\nexport const DEFAULT_DEV_ANALYTICS_QUERY_INTERNAL_SECRET =\n 'dev-analytics-query-internal-secret-b12e3c7ec5cec7b79f139d5';\n\nexport function getJwtSecret(env?: JwtSecretEnv): string {\n const secret = env?.JWT_SECRET || (typeof process !== 'undefined' && process.env?.JWT_SECRET);\n if (!secret) {\n throw new Error('JWT_SECRET is required');\n }\n return secret;\n}\n\nexport function getPreviewTokenSecret(env?: PreviewTokenSecretEnv): string {\n const secret =\n env?.PREVIEW_TOKEN_SECRET ||\n (typeof process !== 'undefined' && process.env?.PREVIEW_TOKEN_SECRET);\n if (!secret) {\n throw new Error('PREVIEW_TOKEN_SECRET is required');\n }\n return secret;\n}\n\n// Leading slash, no trailing slash (except root '/'), lowercase, duplicate slashes collapsed\nexport function normalizePath(rawPath?: string): string {\n if (!rawPath || rawPath.trim() === '') {\n return '/';\n }\n let p = rawPath.trim().toLowerCase();\n if (!p.startsWith('/')) {\n p = `/${p}`;\n }\n p = p.replace(/\\/{2,}/g, '/');\n if (p.length > 1 && p.endsWith('/')) {\n p = p.replace(/\\/+$/, '');\n }\n return p;\n}\n\n// Local Supabase URL fallback\nexport const DEFAULT_SUPABASE_URL = 'http://127.0.0.1:54321';\n\nexport const DEFAULT_DEV_API_URL = 'http://127.0.0.1:8787';\nexport const DEFAULT_DEV_CONTENT_URL = 'http://127.0.0.1:8788';\nexport const DEFAULT_DEV_ANALYTICS_QUERY_URL = 'http://127.0.0.1:8791';\nexport const DEFAULT_DEV_WEB_URL = 'https://web.localhost';\nexport const DEFAULT_DEV_FALLBACK_WEB_URL = 'http://localhost:5173';\nexport const DEFAULT_DEV_DEMO_SITE_URL = 'https://demo.localhost';\nexport const DEFAULT_DEV_FALLBACK_DEMO_SITE_URL = 'http://localhost:4321';\n\n// Canonical local development origins allowlist across both portless names and raw loopback ports\nexport const LOCAL_DEV_ORIGINS: string[] = [\n 'http://localhost:4321',\n 'http://localhost:3000',\n 'http://localhost:5173',\n 'http://localhost:8787',\n 'http://localhost:8788',\n 'http://localhost:8789',\n 'http://localhost:8790',\n 'http://localhost:8791',\n 'http://127.0.0.1:4321',\n 'http://127.0.0.1:3000',\n 'http://127.0.0.1:5173',\n 'http://127.0.0.1:8787',\n 'http://127.0.0.1:8788',\n 'http://127.0.0.1:8789',\n 'http://127.0.0.1:8790',\n 'http://127.0.0.1:8791',\n 'https://web.localhost',\n 'https://api.localhost',\n 'https://content.localhost',\n 'https://builds.localhost',\n 'https://analytics.localhost',\n 'https://analytics-query.localhost',\n 'https://demo.localhost',\n];\n\n// Local Supabase demo anon key, used only when SUPABASE_ANON_KEY is unset\nexport const DEFAULT_SUPABASE_ANON_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6ImFub24iLCJleHAiOjE5ODM4MTI5OTZ9.CRXP1A7WOeoJeXxjNni43kdQwgnWNReilDMblYTn_I0';\n\nexport const DEFAULT_SUPABASE_SERVICE_ROLE_KEY =\n 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU';\n\nexport function normalizeOrigin(raw?: string | null): string | null {\n if (!raw || typeof raw !== 'string') return null;\n const trimmed = raw.trim();\n if (!trimmed) return null;\n\n try {\n const withScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//.test(trimmed)\n ? trimmed\n : `https://${trimmed}`;\n const url = new URL(withScheme);\n if (!url.protocol || !url.host) return null;\n return `${url.protocol}//${url.host}`.toLowerCase();\n } catch {\n return null;\n }\n}\n\nexport function deriveSiteOrigins(\n domain?: string | null,\n customDomain?: string | null,\n opts?: { includeLocalhost?: boolean },\n): string[] {\n const origins = new Set<string>();\n\n const normDomain = normalizeOrigin(domain);\n if (normDomain) origins.add(normDomain);\n\n const normCustom = normalizeOrigin(customDomain);\n if (normCustom) origins.add(normCustom);\n\n if (opts?.includeLocalhost) {\n for (const origin of LOCAL_DEV_ORIGINS) {\n origins.add(origin);\n }\n }\n\n return Array.from(origins);\n}\n\nexport const PAGES_PROJECT_NAME_REGEX = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;\n// 58 = Cloudflare's project-name cap; reserve 8 for the `-preview` twin name.\nexport const PAGES_PROJECT_NAME_MAX = 50;\n\nexport function validatePagesProjectName(name: string): string | null {\n if (!name || typeof name !== 'string' || name.trim() === '') {\n return 'Project name cannot be empty';\n }\n if (name.length > PAGES_PROJECT_NAME_MAX) {\n return `Project name must be ${PAGES_PROJECT_NAME_MAX} characters or fewer`;\n }\n if (!PAGES_PROJECT_NAME_REGEX.test(name)) {\n return 'Project name can only contain lowercase letters, numbers, and hyphens, and cannot start or end with a hyphen';\n }\n return null;\n}\n","export const EVENT_SCHEMA_VERSION = 2;\nexport const MAX_PROPS_KEYS = 8;\nexport const MAX_PROPS_BYTES = 512;\n\nexport interface AnalyticsEvent {\n site: string;\n timestamp: number;\n event: string;\n path: string;\n referrer_host?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_content?: string;\n utm_term?: string;\n country?: string;\n region?: string;\n city?: string;\n browser?: string;\n os?: string;\n device?: string;\n lcp?: number;\n inp?: number;\n cls?: number;\n fcp?: number;\n ttfb?: number;\n vitals_attribution?: string;\n visitor?: string;\n session?: string;\n props?: Record<string, unknown> | string;\n flagged?: 'bot' | string;\n aggregate_only?: boolean;\n v?: number;\n}\n\nexport interface IngestPayload {\n u: string;\n r?: string;\n event?: string;\n props?: Record<string, unknown>;\n lcp?: number;\n inp?: number;\n cls?: number;\n fcp?: number;\n ttfb?: number;\n vitals_attribution?: string;\n}\n\nexport type ParseIngestResult = { ok: true; payload: IngestPayload } | { ok: false; error: string };\n\nexport function sanitizeProps(raw: unknown): Record<string, unknown> | undefined {\n if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {\n return undefined;\n }\n\n const entries = Object.entries(raw as Record<string, unknown>);\n if (entries.length === 0) return undefined;\n\n // Cap keys at MAX_PROPS_KEYS\n const cappedEntries = entries.slice(0, MAX_PROPS_KEYS);\n const result: Record<string, unknown> = {};\n\n const encoder = new TextEncoder();\n\n for (const [key, val] of cappedEntries) {\n // Only accept primitive or simple values\n if (typeof val === 'function' || typeof val === 'symbol') continue;\n result[key] = val;\n }\n\n // Ensure JSON byte size is <= MAX_PROPS_BYTES\n let json = JSON.stringify(result);\n if (encoder.encode(json).length <= MAX_PROPS_BYTES) {\n return result;\n }\n\n // If over byte budget, drop keys from the end until it fits\n const keys = Object.keys(result);\n while (keys.length > 0) {\n const lastKey = keys.pop();\n if (lastKey) delete result[lastKey];\n json = JSON.stringify(result);\n if (encoder.encode(json).length <= MAX_PROPS_BYTES) {\n break;\n }\n }\n\n return Object.keys(result).length > 0 ? result : undefined;\n}\n\nexport function parseIngestPayload(raw: unknown): ParseIngestResult {\n if (!raw) {\n return { ok: false, error: 'empty_payload' };\n }\n\n let obj: Record<string, unknown>;\n if (typeof raw === 'string') {\n try {\n obj = JSON.parse(raw);\n } catch {\n return { ok: false, error: 'invalid_json' };\n }\n } else if (typeof raw === 'object') {\n obj = raw as Record<string, unknown>;\n } else {\n return { ok: false, error: 'invalid_payload_type' };\n }\n\n // Support 'u' or 'path' / 'url'\n const u =\n typeof obj.u === 'string'\n ? obj.u\n : typeof obj.url === 'string'\n ? obj.url\n : typeof obj.path === 'string'\n ? obj.path\n : null;\n if (!u) {\n return { ok: false, error: 'missing_url' };\n }\n\n const r =\n typeof obj.r === 'string' ? obj.r : typeof obj.referrer === 'string' ? obj.referrer : undefined;\n const event =\n typeof obj.e === 'string' ? obj.e : typeof obj.event === 'string' ? obj.event : 'pageview';\n const rawProps = (obj.p ?? obj.props) as Record<string, unknown> | undefined;\n const props = sanitizeProps(rawProps);\n\n const parseNum = (v: unknown): number | undefined =>\n typeof v === 'number' && !Number.isNaN(v) && v >= 0 ? v : undefined;\n\n const lcp = parseNum(obj.lcp ?? rawProps?.lcp);\n const inp = parseNum(obj.inp ?? rawProps?.inp);\n const cls = parseNum(obj.cls ?? rawProps?.cls);\n const fcp = parseNum(obj.fcp ?? rawProps?.fcp);\n const ttfb = parseNum(obj.ttfb ?? rawProps?.ttfb);\n\n const rawAttr = obj.vitals_attribution ?? rawProps?.attribution;\n const vitals_attribution =\n typeof rawAttr === 'string'\n ? rawAttr\n : rawAttr && typeof rawAttr === 'object'\n ? JSON.stringify(rawAttr)\n : undefined;\n\n return {\n ok: true,\n payload: {\n u,\n r,\n event,\n props,\n lcp,\n inp,\n cls,\n fcp,\n ttfb,\n vitals_attribution,\n },\n };\n}\n","export interface NormalizePathOptions {\n collapse?: Array<{ pattern: string; as: string }>;\n}\n\nexport interface NormalizedEventPath {\n path: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_content?: string;\n utm_term?: string;\n}\n\nconst UUID_REGEX = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/gi;\nconst LONG_NUMERIC_SEGMENT_REGEX = /(^|\\/)\\d{4,}($|\\/)/g;\n\nexport function normalizeEventPath(\n rawUrl: string,\n opts?: NormalizePathOptions,\n): NormalizedEventPath {\n if (!rawUrl || typeof rawUrl !== 'string') {\n return { path: '/' };\n }\n\n // 1. Drop fragment\n const withoutFragment = rawUrl.split('#')[0];\n\n // 2. Split query\n const [pathnameRaw, queryString] = withoutFragment.split('?');\n\n // Parse UTM parameters from query\n let utm_source: string | undefined;\n let utm_medium: string | undefined;\n let utm_campaign: string | undefined;\n let utm_content: string | undefined;\n let utm_term: string | undefined;\n\n if (queryString) {\n const params = new URLSearchParams(queryString);\n utm_source = params.get('utm_source') || undefined;\n utm_medium = params.get('utm_medium') || undefined;\n utm_campaign = params.get('utm_campaign') || undefined;\n utm_content = params.get('utm_content') || undefined;\n utm_term = params.get('utm_term') || undefined;\n }\n\n // If rawUrl was a full URL, extract pathname\n let path = pathnameRaw;\n if (path.startsWith('http://') || path.startsWith('https://')) {\n try {\n const parsed = new URL(path);\n path = parsed.pathname;\n if (!utm_source) utm_source = parsed.searchParams.get('utm_source') || undefined;\n if (!utm_medium) utm_medium = parsed.searchParams.get('utm_medium') || undefined;\n if (!utm_campaign) utm_campaign = parsed.searchParams.get('utm_campaign') || undefined;\n if (!utm_content) utm_content = parsed.searchParams.get('utm_content') || undefined;\n if (!utm_term) utm_term = parsed.searchParams.get('utm_term') || undefined;\n } catch {\n // fallback to pathnameRaw\n }\n }\n\n // Ensure leading slash\n if (!path.startsWith('/')) {\n path = `/${path}`;\n }\n\n // Strip trailing slash (except root '/')\n if (path.length > 1 && path.endsWith('/')) {\n path = path.replace(/\\/+$/, '');\n }\n\n // Cap at 128 chars before collapse\n if (path.length > 128) {\n path = path.slice(0, 128);\n }\n\n // 3. Apply built-in default collapse rules\n // UUIDs -> :id\n path = path.replace(UUID_REGEX, ':id');\n\n // Long-numeric segments (4+ digits) -> :id\n path = path.replace(LONG_NUMERIC_SEGMENT_REGEX, '$1:id$2');\n // Second pass in case of consecutive segments\n path = path.replace(LONG_NUMERIC_SEGMENT_REGEX, '$1:id$2');\n\n // 4. Apply site-configured collapse patterns\n if (opts?.collapse) {\n for (const rule of opts.collapse) {\n try {\n const regex = new RegExp(rule.pattern);\n if (regex.test(path)) {\n path = path.replace(regex, rule.as);\n }\n } catch {\n // Ignore invalid regex patterns\n }\n }\n }\n\n return {\n path,\n utm_source,\n utm_medium,\n utm_campaign,\n utm_content,\n utm_term,\n };\n}\n\nexport function referrerHost(raw?: string, selfHost?: string): string | undefined {\n if (!raw || typeof raw !== 'string') return undefined;\n\n let host: string;\n try {\n const url = new URL(\n raw.startsWith('http://') || raw.startsWith('https://') ? raw : `https://${raw}`,\n );\n host = url.hostname.toLowerCase();\n } catch {\n return undefined;\n }\n\n if (!host) return undefined;\n\n if (selfHost) {\n let cleanSelfHost = selfHost.toLowerCase();\n try {\n if (cleanSelfHost.startsWith('http://') || cleanSelfHost.startsWith('https://')) {\n cleanSelfHost = new URL(cleanSelfHost).hostname.toLowerCase();\n } else {\n cleanSelfHost = cleanSelfHost.split(':')[0];\n }\n } catch {\n // keep cleanSelfHost\n }\n\n if (host === cleanSelfHost) {\n return undefined;\n }\n }\n\n return host;\n}\n","import { type AnalyticsEvent, EVENT_SCHEMA_VERSION } from './schema.js';\n\nexport interface AnalyticsEngineDataPoint {\n indexes?: string[];\n blobs?: (string | null | undefined)[];\n doubles?: number[];\n}\n\nexport function toDataPoint(event: AnalyticsEvent): AnalyticsEngineDataPoint {\n const propsStr =\n typeof event.props === 'string' ? event.props : event.props ? JSON.stringify(event.props) : '';\n const vitalsAttrStr =\n typeof event.vitals_attribution === 'string'\n ? event.vitals_attribution\n : event.vitals_attribution\n ? JSON.stringify(event.vitals_attribution)\n : '';\n\n return {\n indexes: [event.site],\n blobs: [\n event.event || 'pageview', // blob1: event\n event.path || '/', // blob2: path\n event.referrer_host || '', // blob3: referrer_host\n event.utm_source || '', // blob4: utm_source\n event.utm_medium || '', // blob5: utm_medium\n event.utm_campaign || '', // blob6: utm_campaign\n event.country || '', // blob7: country\n event.region || '', // blob8: region\n event.visitor || '', // blob9: visitor\n event.session || '', // blob10: session\n propsStr, // blob11: props\n event.city || '', // blob12: city\n event.browser || '', // blob13: browser\n event.os || '', // blob14: os\n event.device || '', // blob15: device\n event.utm_content || '', // blob16: utm_content\n event.utm_term || '', // blob17: utm_term\n vitalsAttrStr, // blob18: vitals attribution JSON\n ],\n doubles: [\n 1, // double1: count\n EVENT_SCHEMA_VERSION, // double2: version (2)\n typeof event.lcp === 'number' && event.lcp >= 0 ? event.lcp : -1, // double3: lcp\n typeof event.inp === 'number' && event.inp >= 0 ? event.inp : -1, // double4: inp\n typeof event.cls === 'number' && event.cls >= 0 ? event.cls : -1, // double5: cls\n typeof event.fcp === 'number' && event.fcp >= 0 ? event.fcp : -1, // double6: fcp\n typeof event.ttfb === 'number' && event.ttfb >= 0 ? event.ttfb : -1, // double7: ttfb\n ],\n };\n}\n\nexport function fromDataPoint(row: Record<string, unknown>): AnalyticsEvent {\n // If row has WAE format (blob1..blob18, index1, double1..double7, timestamp)\n const site = String(row.index1 || row.site || '');\n const timestamp = typeof row.timestamp === 'number' ? row.timestamp : Date.now();\n const event = String(row.blob1 || row.event || 'pageview');\n const path = String(row.blob2 || row.path || '/');\n const referrer_host = (row.blob3 || row.referrer_host || undefined) as string | undefined;\n const utm_source = (row.blob4 || row.utm_source || undefined) as string | undefined;\n const utm_medium = (row.blob5 || row.utm_medium || undefined) as string | undefined;\n const utm_campaign = (row.blob6 || row.utm_campaign || undefined) as string | undefined;\n const country = (row.blob7 || row.country || undefined) as string | undefined;\n const region = (row.blob8 || row.region || undefined) as string | undefined;\n const visitor = (row.blob9 || row.visitor || undefined) as string | undefined;\n const session = (row.blob10 || row.session || undefined) as string | undefined;\n const props = (row.blob11 || row.props || undefined) as string | undefined;\n const city = (row.blob12 || row.city || undefined) as string | undefined;\n const browser = (row.blob13 || row.browser || undefined) as string | undefined;\n const os = (row.blob14 || row.os || undefined) as string | undefined;\n const device = (row.blob15 || row.device || undefined) as string | undefined;\n const utm_content = (row.blob16 || row.utm_content || undefined) as string | undefined;\n const utm_term = (row.blob17 || row.utm_term || undefined) as string | undefined;\n const vitals_attribution = (row.blob18 || row.vitals_attribution || undefined) as\n | string\n | undefined;\n\n const parseDouble = (d: unknown): number | undefined => {\n if (typeof d === 'number' && d >= 0) return d;\n return undefined;\n };\n\n const lcp = parseDouble(row.double3 ?? row.lcp);\n const inp = parseDouble(row.double4 ?? row.inp);\n const cls = parseDouble(row.double5 ?? row.cls);\n const fcp = parseDouble(row.double6 ?? row.fcp);\n const ttfb = parseDouble(row.double7 ?? row.ttfb);\n\n return {\n site,\n timestamp,\n event,\n path,\n referrer_host: referrer_host || undefined,\n utm_source: utm_source || undefined,\n utm_medium: utm_medium || undefined,\n utm_campaign: utm_campaign || undefined,\n utm_content: utm_content || undefined,\n utm_term: utm_term || undefined,\n country: country || undefined,\n region: region || undefined,\n city: city || undefined,\n browser: browser || undefined,\n os: os || undefined,\n device: device || undefined,\n lcp,\n inp,\n cls,\n fcp,\n ttfb,\n vitals_attribution: vitals_attribution || undefined,\n visitor: visitor || undefined,\n session: session || undefined,\n props: props || undefined,\n v: typeof row.double2 === 'number' ? row.double2 : (row.v as number | undefined),\n };\n}\n","export type FilterDimension =\n | 'path'\n | 'entry_page'\n | 'exit_page'\n | 'referrer'\n | 'channel'\n | 'country'\n | 'region'\n | 'city'\n | 'browser'\n | 'os'\n | 'device'\n | 'event'\n | 'utm_campaign'\n | 'utm_source'\n | 'utm_medium'\n | 'utm_content'\n | 'utm_term'\n | 'first_seen'\n | 'last_activity'\n | 'last_seen'\n | 'prop';\n\nexport interface InsightsFilter {\n dim: FilterDimension;\n op: 'is' | 'is_not';\n value: string;\n key?: string;\n}\n\nexport function isVisitorHash(v: unknown): v is string {\n return typeof v === 'string' && /^[0-9a-f]{64}$/.test(v);\n}\n\nexport type InsightsQueryName =\n | 'timeseries'\n | 'topPages'\n | 'referrers'\n | 'countries'\n | 'customEvents'\n | 'summary'\n | 'dimensionValues'\n | 'channels'\n | 'realtime'\n | 'vitalsDistribution'\n | 'vitalsTimeseries'\n | 'visitorList'\n | 'visitorEvents'\n | 'liveEvents';\n\nexport interface TimeseriesRow {\n bucket: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface TopPagesRow {\n path: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface ReferrersRow {\n referrer: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface CountriesRow {\n country: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface CustomEventsRow {\n event: string;\n count: number;\n}\n\nexport interface SummaryRow {\n pageviews: number;\n visitors: number;\n sessions: number;\n}\n\nexport interface DimensionValuesRow {\n value: string;\n count: number;\n}\n\nexport interface ChannelsRow {\n channel: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface RealtimeRow {\n bucket: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface VitalsDistributionRow {\n metric: 'lcp' | 'inp' | 'cls' | 'fcp' | 'ttfb';\n good: number;\n needsImprovement: number;\n poor: number;\n samples: number;\n}\n\nexport interface VitalsTimeseriesRow {\n bucket: string;\n lcp_good: number;\n lcp_ni: number;\n lcp_poor: number;\n lcp_samples: number;\n inp_good: number;\n inp_ni: number;\n inp_poor: number;\n inp_samples: number;\n cls_good: number;\n cls_ni: number;\n cls_poor: number;\n cls_samples: number;\n fcp_good: number;\n fcp_ni: number;\n fcp_poor: number;\n fcp_samples: number;\n ttfb_good: number;\n ttfb_ni: number;\n ttfb_poor: number;\n ttfb_samples: number;\n}\n\nexport interface VisitorListRow {\n visitor: string;\n first_seen: number | string;\n last_seen: number | string;\n events: number;\n sessions: number;\n country?: string;\n region?: string;\n city?: string;\n browser?: string;\n os?: string;\n device?: string;\n referrer_host?: string;\n excluded_events?: number;\n}\n\nexport interface VisitorEventRow {\n event: string;\n path: string;\n referrer_host?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_content?: string;\n utm_term?: string;\n country?: string;\n region?: string;\n city?: string;\n browser?: string;\n os?: string;\n device?: string;\n props?: Record<string, unknown> | string;\n session?: string;\n visitor?: string;\n timestamp: number | string;\n}\n\nexport interface LiveEventRow {\n event: string;\n path: string;\n referrer_host?: string;\n country?: string;\n region?: string;\n city?: string;\n browser?: string;\n os?: string;\n device?: string;\n visitor?: string;\n timestamp: number | string;\n}\n\nexport const LIVE_EVENTS_WINDOW_MS = 5 * 60_000;\nexport const LIVE_EVENTS_MAX_LIMIT = 500;\n\nexport interface InsightsQueryTypes {\n timeseries: TimeseriesRow;\n topPages: TopPagesRow;\n referrers: ReferrersRow;\n countries: CountriesRow;\n customEvents: CustomEventsRow;\n summary: SummaryRow;\n dimensionValues: DimensionValuesRow;\n channels: ChannelsRow;\n realtime: RealtimeRow;\n vitalsDistribution: VitalsDistributionRow;\n vitalsTimeseries: VitalsTimeseriesRow;\n visitorList: VisitorListRow;\n visitorEvents: VisitorEventRow;\n liveEvents: LiveEventRow;\n}\n\nexport interface InsightsQueryParams {\n siteId: string;\n from?: number | string;\n to?: number | string;\n interval?: 'day' | 'hour' | 'week' | 'month';\n limit?: number;\n dataset?: string;\n filters?: InsightsFilter[];\n dimension?: string;\n visitor?: string;\n before?: number;\n}\n\nexport const INSIGHTS_QUERIES: Record<\n InsightsQueryName,\n {\n name: InsightsQueryName;\n defaultLimit: number;\n }\n> = {\n timeseries: { name: 'timeseries', defaultLimit: 1000 },\n topPages: { name: 'topPages', defaultLimit: 10 },\n referrers: { name: 'referrers', defaultLimit: 10 },\n countries: { name: 'countries', defaultLimit: 10 },\n customEvents: { name: 'customEvents', defaultLimit: 10 },\n summary: { name: 'summary', defaultLimit: 1 },\n dimensionValues: { name: 'dimensionValues', defaultLimit: 50 },\n channels: { name: 'channels', defaultLimit: 10 },\n realtime: { name: 'realtime', defaultLimit: 60 },\n vitalsDistribution: { name: 'vitalsDistribution', defaultLimit: 1 },\n vitalsTimeseries: { name: 'vitalsTimeseries', defaultLimit: 1000 },\n visitorList: { name: 'visitorList', defaultLimit: 50 },\n visitorEvents: { name: 'visitorEvents', defaultLimit: 1000 },\n liveEvents: { name: 'liveEvents', defaultLimit: 200 },\n};\n\nexport const SOCIAL_HOSTS = [\n 't.co',\n 'twitter.com',\n 'www.twitter.com',\n 'x.com',\n 'www.x.com',\n 'facebook.com',\n 'www.facebook.com',\n 'm.facebook.com',\n 'l.facebook.com',\n 'lm.facebook.com',\n 'instagram.com',\n 'www.instagram.com',\n 'l.instagram.com',\n 'linkedin.com',\n 'www.linkedin.com',\n 'lnkd.in',\n 'reddit.com',\n 'www.reddit.com',\n 'old.reddit.com',\n 'pinterest.com',\n 'www.pinterest.com',\n 'tiktok.com',\n 'www.tiktok.com',\n 'youtube.com',\n 'www.youtube.com',\n 'threads.net',\n 'www.threads.net',\n 'news.ycombinator.com',\n 'bsky.app',\n] as const;\n\nexport const SEARCH_HOSTS = [\n 'google.com',\n 'www.google.com',\n 'bing.com',\n 'www.bing.com',\n 'duckduckgo.com',\n 'www.duckduckgo.com',\n 'search.yahoo.com',\n 'yahoo.com',\n 'www.yahoo.com',\n 'ecosia.org',\n 'www.ecosia.org',\n 'baidu.com',\n 'www.baidu.com',\n 'yandex.com',\n 'yandex.ru',\n 'www.yandex.com',\n 'qwant.com',\n 'www.qwant.com',\n] as const;\n\nexport const PAID_MEDIUMS = ['cpc', 'ppc', 'paid', 'paid_social'] as const;\nexport const EMAIL_MEDIUMS = ['email', 'newsletter'] as const;\n\nexport function isValidFilterValue(val: string): boolean {\n if (typeof val !== 'string') return false;\n if (val.length === 0 || val.length > 256) return false;\n if (/['\\\\;]/.test(val)) return false;\n for (let i = 0; i < val.length; i++) {\n const code = val.charCodeAt(i);\n if ((code >= 0x00 && code <= 0x1f) || (code >= 0x7f && code <= 0x9f)) {\n return false;\n }\n }\n return true;\n}\n\nexport function getTimeRangeForFilterValue(\n value: string,\n nowMs: number = Date.now(),\n): { startMs: number; endMs: number } | null {\n const v = value.toLowerCase().trim();\n if (v === '24h' || v === 'last 24 hours') {\n return { startMs: nowMs - 24 * 60 * 60 * 1000, endMs: nowMs };\n }\n if (v === '7d' || v === 'last 7 days') {\n return { startMs: nowMs - 7 * 24 * 60 * 60 * 1000, endMs: nowMs };\n }\n if (v === '30d' || v === 'last 30 days') {\n return { startMs: nowMs - 30 * 24 * 60 * 60 * 1000, endMs: nowMs };\n }\n if (v === 'today') {\n const todayStr = new Date(nowMs).toISOString().slice(0, 10);\n const startMs = Date.parse(`${todayStr}T00:00:00.000Z`);\n const endMs = Date.parse(`${todayStr}T23:59:59.999Z`);\n return { startMs, endMs };\n }\n if (v === 'yesterday') {\n const d = new Date(nowMs);\n d.setUTCDate(d.getUTCDate() - 1);\n const yestStr = d.toISOString().slice(0, 10);\n const startMs = Date.parse(`${yestStr}T00:00:00.000Z`);\n const endMs = Date.parse(`${yestStr}T23:59:59.999Z`);\n return { startMs, endMs };\n }\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(v)) {\n const startMs = Date.parse(`${v}T00:00:00.000Z`);\n const endMs = Date.parse(`${v}T23:59:59.999Z`);\n if (!Number.isNaN(startMs) && !Number.isNaN(endMs)) {\n return { startMs, endMs };\n }\n }\n return null;\n}\n\nexport function parseDateToYmd(val: string | number): { ymd: string; ms: number } | null {\n if (typeof val === 'number') {\n if (Number.isNaN(val) || val <= 0) return null;\n const d = new Date(val);\n if (Number.isNaN(d.getTime())) return null;\n return { ymd: d.toISOString().slice(0, 10), ms: val };\n }\n if (typeof val === 'string') {\n const trimmed = val.trim();\n if (!trimmed) return null;\n // Check YYYY-MM-DD format\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {\n const d = new Date(`${trimmed}T00:00:00.000Z`);\n if (Number.isNaN(d.getTime())) return null;\n return { ymd: trimmed, ms: d.getTime() };\n }\n const parsed = Date.parse(trimmed);\n if (!Number.isNaN(parsed)) {\n return { ymd: new Date(parsed).toISOString().slice(0, 10), ms: parsed };\n }\n const num = Number(trimmed);\n if (!Number.isNaN(num) && num > 0) {\n const d = new Date(num);\n if (!Number.isNaN(d.getTime())) {\n return { ymd: d.toISOString().slice(0, 10), ms: num };\n }\n }\n }\n return null;\n}\n\nexport function validateVisitorRange(\n from?: number | string,\n to?: number | string,\n): { ok: true; fromMs: number; toMs: number; ymd: string } | { ok: false; error: string } {\n if (from === undefined && to === undefined) {\n const today = new Date().toISOString().slice(0, 10);\n const startMs = Date.parse(`${today}T00:00:00.000Z`);\n const endMs = Date.parse(`${today}T23:59:59.999Z`);\n return { ok: true, fromMs: startMs, toMs: endMs, ymd: today };\n }\n\n const parsedFrom = from !== undefined ? parseDateToYmd(from) : undefined;\n const parsedTo = to !== undefined ? parseDateToYmd(to) : undefined;\n\n const resolvedFrom = parsedFrom ?? parsedTo;\n const resolvedTo = parsedTo ?? parsedFrom;\n\n if (!resolvedFrom || !resolvedTo) {\n return { ok: false, error: 'invalid_params' };\n }\n\n if (resolvedFrom.ymd !== resolvedTo.ymd) {\n return { ok: false, error: 'visitor_range_must_be_single_day' };\n }\n\n return {\n ok: true,\n fromMs: parsedFrom ? parsedFrom.ms : resolvedTo.ms,\n toMs: parsedTo ? parsedTo.ms : resolvedFrom.ms,\n ymd: resolvedFrom.ymd,\n };\n}\n\n/** Own-property allowlist check; `in` also matches inherited Object.prototype keys (constructor, toString, ...). */\nfunction hasOwn(map: Record<string, string>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(map, key);\n}\n\n/** Escapes LIKE wildcards so prop values match literally; `\\` is safe since isValidFilterValue rejects it in input. */\nfunction escapeLikeWildcards(val: string): string {\n return val.replace(/[%_]/g, (ch) => `\\\\${ch}`);\n}\n\nexport const MAX_FILTERS_PER_QUERY = 10;\n\nconst D1_DIMENSION_COLUMNS: Record<string, string> = {\n path: 'path',\n referrer: 'referrer_host',\n country: 'country',\n region: 'region',\n city: 'city',\n browser: 'browser',\n os: 'os',\n device: 'device',\n event: 'event',\n utm_campaign: 'utm_campaign',\n utm_source: 'utm_source',\n utm_medium: 'utm_medium',\n utm_content: 'utm_content',\n utm_term: 'utm_term',\n};\n\nconst WAE_DIMENSION_COLUMNS: Record<string, string> = {\n path: 'blob2',\n referrer: 'blob3',\n country: 'blob7',\n region: 'blob8',\n city: 'blob12',\n browser: 'blob13',\n os: 'blob14',\n device: 'blob15',\n event: 'blob1',\n utm_campaign: 'blob6',\n utm_source: 'blob4',\n utm_medium: 'blob5',\n utm_content: 'blob16',\n utm_term: 'blob17',\n};\n\nexport function renderChannelCaseExpr(flavor: 'wae' | 'd1'): string {\n const socialList = SOCIAL_HOSTS.map((h) => `'${h}'`).join(', ');\n const searchList = SEARCH_HOSTS.map((h) => `'${h}'`).join(', ');\n const paidList = PAID_MEDIUMS.map((m) => `'${m}'`).join(', ');\n const emailList = EMAIL_MEDIUMS.map((m) => `'${m}'`).join(', ');\n\n if (flavor === 'wae') {\n return `CASE\n WHEN blob5 IN (${paidList}) THEN 'Paid'\n WHEN blob5 IN (${emailList}) THEN 'Email'\n WHEN blob3 IN (${socialList}) THEN 'Social'\n WHEN blob3 IN (${searchList}) THEN 'Organic Search'\n WHEN blob3 != '' THEN 'Referral'\n ELSE 'Direct'\n END`;\n }\n return `CASE\n WHEN utm_medium IN (${paidList}) THEN 'Paid'\n WHEN utm_medium IN (${emailList}) THEN 'Email'\n WHEN referrer_host IN (${socialList}) THEN 'Social'\n WHEN referrer_host IN (${searchList}) THEN 'Organic Search'\n WHEN referrer_host IS NOT NULL AND referrer_host != '' THEN 'Referral'\n ELSE 'Direct'\n END`;\n}\n\nfunction getD1ChannelPredicate(channel: string): string | null {\n const socialList = SOCIAL_HOSTS.map((h) => `'${h}'`).join(', ');\n const searchList = SEARCH_HOSTS.map((h) => `'${h}'`).join(', ');\n const nonReferralMediums = [...PAID_MEDIUMS, ...EMAIL_MEDIUMS].map((m) => `'${m}'`).join(', ');\n const allSocialAndSearch = [...SOCIAL_HOSTS, ...SEARCH_HOSTS].map((h) => `'${h}'`).join(', ');\n\n switch (channel) {\n case 'direct':\n return `((referrer_host IS NULL OR referrer_host = '') AND (utm_source IS NULL OR utm_source = ''))`;\n case 'paid':\n return `utm_medium IN (${PAID_MEDIUMS.map((m) => `'${m}'`).join(', ')})`;\n case 'email':\n return `utm_medium IN (${EMAIL_MEDIUMS.map((m) => `'${m}'`).join(', ')})`;\n case 'social':\n return `referrer_host IN (${socialList})`;\n case 'organic':\n return `referrer_host IN (${searchList})`;\n case 'referral':\n return `(referrer_host IS NOT NULL AND referrer_host != '' AND (utm_medium IS NULL OR utm_medium NOT IN (${nonReferralMediums})) AND referrer_host NOT IN (${allSocialAndSearch}))`;\n default:\n return null;\n }\n}\n\nfunction getWaeChannelPredicate(channel: string): string | null {\n const socialList = SOCIAL_HOSTS.map((h) => `'${h}'`).join(', ');\n const searchList = SEARCH_HOSTS.map((h) => `'${h}'`).join(', ');\n const nonReferralMediums = [...PAID_MEDIUMS, ...EMAIL_MEDIUMS].map((m) => `'${m}'`).join(', ');\n const allSocialAndSearch = [...SOCIAL_HOSTS, ...SEARCH_HOSTS].map((h) => `'${h}'`).join(', ');\n\n switch (channel) {\n case 'direct':\n return `(blob3 = '' AND blob4 = '')`;\n case 'paid':\n return `blob5 IN (${PAID_MEDIUMS.map((m) => `'${m}'`).join(', ')})`;\n case 'email':\n return `blob5 IN (${EMAIL_MEDIUMS.map((m) => `'${m}'`).join(', ')})`;\n case 'social':\n return `blob3 IN (${socialList})`;\n case 'organic':\n return `blob3 IN (${searchList})`;\n case 'referral':\n return `(blob3 != '' AND blob5 NOT IN (${nonReferralMediums}) AND blob3 NOT IN (${allSocialAndSearch}))`;\n default:\n return null;\n }\n}\n\nexport function buildD1FilterPredicates(filters?: InsightsFilter[]): {\n clauses: string[];\n binds: unknown[];\n} {\n const clauses: string[] = [];\n const binds: unknown[] = [];\n if (!filters || !Array.isArray(filters)) return { clauses, binds };\n\n for (const filter of filters.slice(0, MAX_FILTERS_PER_QUERY)) {\n if (!filter || typeof filter !== 'object') continue;\n const { dim, op, value, key } = filter;\n if (op !== 'is' && op !== 'is_not') continue;\n if (!isValidFilterValue(value)) continue;\n\n if (dim === 'channel') {\n const channelPred = getD1ChannelPredicate(value);\n if (!channelPred) continue;\n if (op === 'is') {\n clauses.push(channelPred);\n } else {\n clauses.push(`NOT (${channelPred})`);\n }\n } else if (dim === 'prop') {\n if (!key || !isValidFilterValue(key)) continue;\n if (op === 'is') {\n clauses.push(`json_extract(props, '$.' || ?) = ?`);\n binds.push(key, value);\n } else {\n clauses.push(\n `(json_extract(props, '$.' || ?) != ? OR json_extract(props, '$.' || ?) IS NULL)`,\n );\n binds.push(key, value, key);\n }\n } else if (hasOwn(D1_DIMENSION_COLUMNS, dim)) {\n const col = D1_DIMENSION_COLUMNS[dim];\n if (op === 'is') {\n clauses.push(`${col} = ?`);\n binds.push(value);\n } else {\n clauses.push(`(${col} != ? OR ${col} IS NULL)`);\n binds.push(value);\n }\n }\n }\n\n return { clauses, binds };\n}\n\nexport function buildWaeFilterPredicates(filters?: InsightsFilter[]): string[] {\n const clauses: string[] = [];\n if (!filters || !Array.isArray(filters)) return clauses;\n\n for (const filter of filters.slice(0, MAX_FILTERS_PER_QUERY)) {\n if (!filter || typeof filter !== 'object') continue;\n const { dim, op, value, key } = filter;\n if (op !== 'is' && op !== 'is_not') continue;\n if (!isValidFilterValue(value)) continue;\n\n if (dim === 'channel') {\n const channelPred = getWaeChannelPredicate(value);\n if (!channelPred) continue;\n if (op === 'is') {\n clauses.push(channelPred);\n } else {\n clauses.push(`NOT (${channelPred})`);\n }\n } else if (dim === 'prop') {\n if (!key || !isValidFilterValue(key)) continue;\n // ClickHouse's LIKE treats `\\` as its escape character by default (no ESCAPE clause needed).\n const pattern = `%\"${escapeLikeWildcards(key)}\":\"${escapeLikeWildcards(value)}\"%`;\n if (op === 'is') {\n clauses.push(`blob11 LIKE '${pattern}'`);\n } else {\n clauses.push(`blob11 NOT LIKE '${pattern}'`);\n }\n } else if (hasOwn(WAE_DIMENSION_COLUMNS, dim)) {\n const col = WAE_DIMENSION_COLUMNS[dim];\n if (op === 'is') {\n clauses.push(`${col} = '${value}'`);\n } else {\n clauses.push(`${col} != '${value}'`);\n }\n }\n }\n\n return clauses;\n}\n\nfunction parseTimeRange(\n from?: number | string,\n to?: number | string,\n): { fromMs: number; toMs: number; fromSec: number; toSec: number } {\n const now = Date.now();\n const thirtyDaysAgo = now - 30 * 24 * 60 * 60 * 1000;\n\n let fromMs = thirtyDaysAgo;\n if (typeof from === 'number') fromMs = from;\n else if (typeof from === 'string') {\n const parsed = Date.parse(from);\n if (!Number.isNaN(parsed)) fromMs = parsed;\n else if (!Number.isNaN(Number(from))) fromMs = Number(from);\n }\n\n let toMs = now;\n if (typeof to === 'number') toMs = to;\n else if (typeof to === 'string') {\n const parsed = Date.parse(to);\n if (!Number.isNaN(parsed)) toMs = parsed;\n else if (!Number.isNaN(Number(to))) toMs = Number(to);\n }\n\n return {\n fromMs,\n toMs,\n fromSec: Math.floor(fromMs / 1000),\n toSec: Math.floor(toMs / 1000),\n };\n}\n\nfunction escapeSql(val: string): string {\n return val.replace(/'/g, \"''\");\n}\n\nexport function renderWaeSql(name: InsightsQueryName, params: InsightsQueryParams): string {\n const {\n siteId,\n interval = 'day',\n limit = INSIGHTS_QUERIES[name]?.defaultLimit ?? 10,\n dataset = 'cms_analytics',\n filters,\n dimension,\n } = params;\n const { fromSec, toSec } = parseTimeRange(params.from, params.to);\n const site = escapeSql(siteId);\n const filterClauses = buildWaeFilterPredicates(filters);\n const filterSql = filterClauses.length > 0 ? ` AND ${filterClauses.join(' AND ')}` : '';\n\n switch (name) {\n case 'timeseries': {\n let bucketFunc = 'toStartOfDay(timestamp)';\n if (interval === 'hour') bucketFunc = 'toStartOfHour(timestamp)';\n // mode 1 = Monday-first week, matching the client's ISO week bucketing.\n else if (interval === 'week') bucketFunc = 'toStartOfWeek(timestamp, 1)';\n else if (interval === 'month') bucketFunc = 'toStartOfMonth(timestamp)';\n\n return `SELECT ${bucketFunc} AS bucket, sum(_sample_interval) AS pageviews, count(DISTINCT blob9) AS visitors FROM ${dataset} WHERE index1 = '${site}' AND blob1 = 'pageview' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY bucket ORDER BY bucket ASC LIMIT ${limit}`;\n }\n case 'topPages': {\n return `SELECT blob2 AS path, sum(_sample_interval) AS pageviews, count(DISTINCT blob9) AS visitors FROM ${dataset} WHERE index1 = '${site}' AND blob1 = 'pageview' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY path ORDER BY pageviews DESC LIMIT ${limit}`;\n }\n case 'referrers': {\n return `SELECT blob3 AS referrer, sum(_sample_interval) AS pageviews, count(DISTINCT blob9) AS visitors FROM ${dataset} WHERE index1 = '${site}' AND blob3 != '' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY referrer ORDER BY pageviews DESC LIMIT ${limit}`;\n }\n case 'countries': {\n return `SELECT blob7 AS country, sum(_sample_interval) AS pageviews, count(DISTINCT blob9) AS visitors FROM ${dataset} WHERE index1 = '${site}' AND blob7 != '' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY country ORDER BY pageviews DESC LIMIT ${limit}`;\n }\n case 'customEvents': {\n return `SELECT blob1 AS event, sum(_sample_interval) AS count FROM ${dataset} WHERE index1 = '${site}' AND blob1 != 'pageview' AND blob1 != 'vitals' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY event ORDER BY count DESC LIMIT ${limit}`;\n }\n case 'summary': {\n return `SELECT sum(CASE WHEN blob1 = 'pageview' THEN _sample_interval ELSE 0 END) AS pageviews, count(DISTINCT blob9) AS visitors, count(DISTINCT blob10) AS sessions FROM ${dataset} WHERE index1 = '${site}' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql}`;\n }\n case 'dimensionValues': {\n const col =\n dimension && hasOwn(WAE_DIMENSION_COLUMNS, dimension)\n ? WAE_DIMENSION_COLUMNS[dimension]\n : null;\n if (!col) {\n throw new Error(`Invalid dimension for dimensionValues query: ${dimension}`);\n }\n const eventFilter = dimension === 'event' ? '' : \" AND blob1 = 'pageview'\";\n const havingClause = dimension === 'city' ? ' HAVING count >= 3' : '';\n return `SELECT ${col} AS value, sum(_sample_interval) AS count FROM ${dataset} WHERE index1 = '${site}' AND ${col} != ''${eventFilter} AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY value${havingClause} ORDER BY count DESC LIMIT ${limit}`;\n }\n case 'channels': {\n const caseExpr = renderChannelCaseExpr('wae');\n return `SELECT ${caseExpr} AS channel, sum(_sample_interval) AS pageviews, count(DISTINCT blob9) AS visitors FROM ${dataset} WHERE index1 = '${site}' AND blob1 = 'pageview' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY channel ORDER BY pageviews DESC LIMIT ${limit}`;\n }\n case 'realtime': {\n const rtFromSec = Math.floor((Date.now() - 30 * 60 * 1000) / 1000);\n return `SELECT toStartOfMinute(timestamp) AS bucket, sum(_sample_interval) AS pageviews, count(DISTINCT blob9) AS visitors FROM ${dataset} WHERE index1 = '${site}' AND blob1 = 'pageview' AND timestamp >= toDateTime(${rtFromSec})${filterSql} GROUP BY bucket ORDER BY bucket ASC LIMIT ${limit}`;\n }\n case 'vitalsDistribution': {\n return `SELECT countIf(double3 >= 0 AND double3 <= 2500) AS lcp_good, countIf(double3 > 2500 AND double3 <= 4000) AS lcp_needs_improvement, countIf(double3 > 4000) AS lcp_poor, countIf(double3 >= 0) AS lcp_samples, countIf(double4 >= 0 AND double4 <= 200) AS inp_good, countIf(double4 > 200 AND double4 <= 500) AS inp_needs_improvement, countIf(double4 > 500) AS inp_poor, countIf(double4 >= 0) AS inp_samples, countIf(double5 >= 0 AND double5 <= 0.1) AS cls_good, countIf(double5 > 0.1 AND double5 <= 0.25) AS cls_needs_improvement, countIf(double5 > 0.25) AS cls_poor, countIf(double5 >= 0) AS cls_samples, countIf(double6 >= 0 AND double6 <= 1800) AS fcp_good, countIf(double6 > 1800 AND double6 <= 3000) AS fcp_needs_improvement, countIf(double6 > 3000) AS fcp_poor, countIf(double6 >= 0) AS fcp_samples, countIf(double7 >= 0 AND double7 <= 800) AS ttfb_good, countIf(double7 > 800 AND double7 <= 1800) AS ttfb_needs_improvement, countIf(double7 > 1800) AS ttfb_poor, countIf(double7 >= 0) AS ttfb_samples FROM ${dataset} WHERE index1 = '${site}' AND blob1 = 'vitals' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql}`;\n }\n case 'vitalsTimeseries': {\n let bucketFunc = 'toStartOfDay(timestamp)';\n if (interval === 'hour') bucketFunc = 'toStartOfHour(timestamp)';\n else if (interval === 'week') bucketFunc = 'toStartOfWeek(timestamp, 1)';\n else if (interval === 'month') bucketFunc = 'toStartOfMonth(timestamp)';\n return `SELECT ${bucketFunc} AS bucket, countIf(double3 >= 0 AND double3 <= 2500) AS lcp_good, countIf(double3 > 2500 AND double3 <= 4000) AS lcp_ni, countIf(double3 > 4000) AS lcp_poor, countIf(double3 >= 0) AS lcp_samples, countIf(double4 >= 0 AND double4 <= 200) AS inp_good, countIf(double4 > 200 AND double4 <= 500) AS inp_ni, countIf(double4 > 500) AS inp_poor, countIf(double4 >= 0) AS inp_samples, countIf(double5 >= 0 AND double5 <= 0.1) AS cls_good, countIf(double5 > 0.1 AND double5 <= 0.25) AS cls_ni, countIf(double5 > 0.25) AS cls_poor, countIf(double5 >= 0) AS cls_samples, countIf(double6 >= 0 AND double6 <= 1800) AS fcp_good, countIf(double6 > 1800 AND double6 <= 3000) AS fcp_ni, countIf(double6 > 3000) AS fcp_poor, countIf(double6 >= 0) AS fcp_samples, countIf(double7 >= 0 AND double7 <= 800) AS ttfb_good, countIf(double7 > 800 AND double7 <= 1800) AS ttfb_ni, countIf(double7 > 1800) AS ttfb_poor, countIf(double7 >= 0) AS ttfb_samples FROM ${dataset} WHERE index1 = '${site}' AND blob1 = 'vitals' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY bucket ORDER BY bucket ASC LIMIT ${limit}`;\n }\n case 'visitorList': {\n const rangeRes = validateVisitorRange(params.from, params.to);\n if (!rangeRes.ok) {\n throw new Error(rangeRes.error);\n }\n const havingClauses: string[] = [];\n if (typeof params.before === 'number' && !Number.isNaN(params.before)) {\n havingClauses.push(`last_seen < toDateTime(${Math.floor(params.before / 1000)})`);\n }\n if (params.filters) {\n for (const f of params.filters) {\n if (f.dim === 'first_seen' || f.dim === 'last_activity' || f.dim === 'last_seen') {\n const tr = getTimeRangeForFilterValue(f.value);\n if (tr) {\n const startSec = Math.floor(tr.startMs / 1000);\n const endSec = Math.floor(tr.endMs / 1000);\n const col = f.dim === 'first_seen' ? 'first_seen' : 'last_seen';\n if (f.op === 'is') {\n havingClauses.push(\n `${col} >= toDateTime(${startSec}) AND ${col} <= toDateTime(${endSec})`,\n );\n } else {\n havingClauses.push(\n `(${col} < toDateTime(${startSec}) OR ${col} > toDateTime(${endSec}))`,\n );\n }\n }\n }\n }\n }\n const havingClause = havingClauses.length > 0 ? ` HAVING ${havingClauses.join(' AND ')}` : '';\n return `SELECT blob9 AS visitor, min(timestamp) AS first_seen, max(timestamp) AS last_seen, sum(_sample_interval) AS events, count(DISTINCT blob10) AS sessions, max(blob7) AS country, max(blob8) AS region, max(blob12) AS city, max(blob13) AS browser, max(blob14) AS os, max(blob15) AS device, max(blob3) AS referrer_host FROM ${dataset} WHERE index1 = '${site}' AND blob9 != '' AND blob1 != 'vitals' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} GROUP BY visitor${havingClause} ORDER BY last_seen DESC LIMIT ${limit}`;\n }\n case 'visitorEvents': {\n const rangeRes = validateVisitorRange(params.from, params.to);\n if (!rangeRes.ok) {\n throw new Error(rangeRes.error);\n }\n if (!params.visitor || !isVisitorHash(params.visitor)) {\n throw new Error('visitorEvents requires a valid 64-character hex visitor hash');\n }\n const visitor = escapeSql(params.visitor);\n return `SELECT blob1 AS event, blob2 AS path, blob3 AS referrer_host, blob4 AS utm_source, blob5 AS utm_medium, blob6 AS utm_campaign, blob16 AS utm_content, blob17 AS utm_term, blob7 AS country, blob8 AS region, blob12 AS city, blob13 AS browser, blob14 AS os, blob15 AS device, blob11 AS props, blob10 AS session, blob9 AS visitor, timestamp FROM ${dataset} WHERE index1 = '${site}' AND blob9 = '${visitor}' AND blob1 != 'vitals' AND timestamp >= toDateTime(${fromSec}) AND timestamp <= toDateTime(${toSec})${filterSql} ORDER BY timestamp ASC LIMIT ${limit}`;\n }\n case 'liveEvents': {\n const minFromSec = Math.floor((Date.now() - LIVE_EVENTS_WINDOW_MS) / 1000);\n const effectiveFromSec =\n params.from !== undefined ? Math.max(fromSec, minFromSec) : minFromSec;\n const effectiveLimit = Math.min(Math.max(1, limit), LIVE_EVENTS_MAX_LIMIT);\n return `SELECT blob1 AS event, blob2 AS path, blob3 AS referrer_host, blob4 AS utm_source, blob5 AS utm_medium, blob6 AS utm_campaign, blob16 AS utm_content, blob17 AS utm_term, blob7 AS country, blob8 AS region, blob12 AS city, blob13 AS browser, blob14 AS os, blob15 AS device, blob11 AS props, blob10 AS session, blob9 AS visitor, timestamp FROM ${dataset} WHERE index1 = '${site}' AND blob1 = 'pageview' AND timestamp > toDateTime(${effectiveFromSec})${filterSql} ORDER BY timestamp DESC LIMIT ${effectiveLimit}`;\n }\n default:\n throw new Error(`Unknown insights query: ${name}`);\n }\n}\n\nexport function renderD1Sql(\n name: InsightsQueryName,\n params: InsightsQueryParams,\n): { sql: string; binds: unknown[] } {\n const {\n siteId,\n interval = 'day',\n limit = INSIGHTS_QUERIES[name]?.defaultLimit ?? 10,\n filters,\n dimension,\n } = params;\n const { fromMs, toMs } = parseTimeRange(params.from, params.to);\n const { clauses, binds: filterBinds } = buildD1FilterPredicates(filters);\n const filterSql = clauses.length > 0 ? ` AND ${clauses.join(' AND ')}` : '';\n\n switch (name) {\n case 'timeseries': {\n // 'weekday 0' (next/self Sunday) then '-6 days' yields that week's Monday, matching WAE's toStartOfWeek(timestamp, 1).\n let bucketExpr = `strftime('%Y-%m-%d', timestamp / 1000, 'unixepoch')`;\n if (interval === 'hour')\n bucketExpr = `strftime('%Y-%m-%dT%H:00:00Z', timestamp / 1000, 'unixepoch')`;\n else if (interval === 'week')\n bucketExpr = `strftime('%Y-%m-%d', timestamp / 1000, 'unixepoch', 'weekday 0', '-6 days')`;\n else if (interval === 'month')\n bucketExpr = `strftime('%Y-%m', timestamp / 1000, 'unixepoch')`;\n\n const sql = `SELECT ${bucketExpr} AS bucket, count(*) AS pageviews, count(DISTINCT CASE WHEN visitor != '' THEN visitor END) AS visitors FROM analytics_hot WHERE site = ? AND event = 'pageview' AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY bucket ORDER BY bucket ASC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'topPages': {\n const sql = `SELECT path, count(*) AS pageviews, count(DISTINCT CASE WHEN visitor != '' THEN visitor END) AS visitors FROM analytics_hot WHERE site = ? AND event = 'pageview' AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY path ORDER BY pageviews DESC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'referrers': {\n const sql = `SELECT referrer_host AS referrer, count(*) AS pageviews, count(DISTINCT CASE WHEN visitor != '' THEN visitor END) AS visitors FROM analytics_hot WHERE site = ? AND referrer_host IS NOT NULL AND referrer_host != '' AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY referrer ORDER BY pageviews DESC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'countries': {\n const sql = `SELECT country, count(*) AS pageviews, count(DISTINCT CASE WHEN visitor != '' THEN visitor END) AS visitors FROM analytics_hot WHERE site = ? AND country IS NOT NULL AND country != '' AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY country ORDER BY pageviews DESC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'customEvents': {\n const sql = `SELECT event, count(*) AS count FROM analytics_hot WHERE site = ? AND event != 'pageview' AND event != 'vitals' AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY event ORDER BY count DESC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'summary': {\n const sql = `SELECT count(CASE WHEN event = 'pageview' THEN 1 END) AS pageviews, count(DISTINCT CASE WHEN visitor != '' THEN visitor END) AS visitors, count(DISTINCT CASE WHEN session != '' THEN session END) AS sessions FROM analytics_hot WHERE site = ? AND timestamp >= ? AND timestamp <= ?${filterSql}`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds],\n };\n }\n case 'dimensionValues': {\n const col =\n dimension && hasOwn(D1_DIMENSION_COLUMNS, dimension)\n ? D1_DIMENSION_COLUMNS[dimension]\n : null;\n if (!col) {\n throw new Error(`Invalid dimension for dimensionValues query: ${dimension}`);\n }\n const eventFilter = dimension === 'event' ? '' : \" AND event = 'pageview'\";\n const havingClause = dimension === 'city' ? ' HAVING count >= 3' : '';\n const sql = `SELECT ${col} AS value, count(*) AS count FROM analytics_hot WHERE site = ? AND ${col} IS NOT NULL AND ${col} != ''${eventFilter} AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY value${havingClause} ORDER BY count DESC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'channels': {\n const caseExpr = renderChannelCaseExpr('d1');\n const sql = `SELECT ${caseExpr} AS channel, count(*) AS pageviews, count(DISTINCT CASE WHEN visitor != '' THEN visitor END) AS visitors FROM analytics_hot WHERE site = ? AND event = 'pageview' AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY channel ORDER BY pageviews DESC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'realtime': {\n const rtFromMs = Date.now() - 30 * 60 * 1000;\n const sql = `SELECT strftime('%Y-%m-%dT%H:%M:00Z', timestamp / 1000, 'unixepoch') AS bucket, count(*) AS pageviews, count(DISTINCT CASE WHEN visitor != '' THEN visitor END) AS visitors FROM analytics_hot WHERE site = ? AND event = 'pageview' AND timestamp >= ?${filterSql} GROUP BY bucket ORDER BY bucket ASC LIMIT ?`;\n return {\n sql,\n binds: [siteId, rtFromMs, ...filterBinds, limit],\n };\n }\n case 'vitalsDistribution': {\n const sql = `SELECT count(CASE WHEN lcp IS NOT NULL AND lcp >= 0 AND lcp <= 2500 THEN 1 END) AS lcp_good, count(CASE WHEN lcp > 2500 AND lcp <= 4000 THEN 1 END) AS lcp_needs_improvement, count(CASE WHEN lcp > 4000 THEN 1 END) AS lcp_poor, count(CASE WHEN lcp IS NOT NULL AND lcp >= 0 THEN 1 END) AS lcp_samples, count(CASE WHEN inp IS NOT NULL AND inp >= 0 AND inp <= 200 THEN 1 END) AS inp_good, count(CASE WHEN inp > 200 AND inp <= 500 THEN 1 END) AS inp_needs_improvement, count(CASE WHEN inp > 500 THEN 1 END) AS inp_poor, count(CASE WHEN inp IS NOT NULL AND inp >= 0 THEN 1 END) AS inp_samples, count(CASE WHEN cls IS NOT NULL AND cls >= 0 AND cls <= 0.1 THEN 1 END) AS cls_good, count(CASE WHEN cls > 0.1 AND cls <= 0.25 THEN 1 END) AS cls_needs_improvement, count(CASE WHEN cls > 0.25 THEN 1 END) AS cls_poor, count(CASE WHEN cls IS NOT NULL AND cls >= 0 THEN 1 END) AS cls_samples, count(CASE WHEN fcp IS NOT NULL AND fcp >= 0 AND fcp <= 1800 THEN 1 END) AS fcp_good, count(CASE WHEN fcp > 1800 AND fcp <= 3000 THEN 1 END) AS fcp_needs_improvement, count(CASE WHEN fcp > 3000 THEN 1 END) AS fcp_poor, count(CASE WHEN fcp IS NOT NULL AND fcp >= 0 THEN 1 END) AS fcp_samples, count(CASE WHEN ttfb IS NOT NULL AND ttfb >= 0 AND ttfb <= 800 THEN 1 END) AS ttfb_good, count(CASE WHEN ttfb > 800 AND ttfb <= 1800 THEN 1 END) AS ttfb_needs_improvement, count(CASE WHEN ttfb > 1800 THEN 1 END) AS ttfb_poor, count(CASE WHEN ttfb IS NOT NULL AND ttfb >= 0 THEN 1 END) AS ttfb_samples FROM analytics_hot WHERE site = ? AND event = 'vitals' AND timestamp >= ? AND timestamp <= ?${filterSql}`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds],\n };\n }\n case 'vitalsTimeseries': {\n let bucketExpr = `strftime('%Y-%m-%d', timestamp / 1000, 'unixepoch')`;\n if (interval === 'hour')\n bucketExpr = `strftime('%Y-%m-%dT%H:00:00Z', timestamp / 1000, 'unixepoch')`;\n else if (interval === 'week')\n bucketExpr = `strftime('%Y-%m-%d', timestamp / 1000, 'unixepoch', 'weekday 0', '-6 days')`;\n else if (interval === 'month')\n bucketExpr = `strftime('%Y-%m', timestamp / 1000, 'unixepoch')`;\n const sql = `SELECT ${bucketExpr} AS bucket, count(CASE WHEN lcp IS NOT NULL AND lcp >= 0 AND lcp <= 2500 THEN 1 END) AS lcp_good, count(CASE WHEN lcp > 2500 AND lcp <= 4000 THEN 1 END) AS lcp_ni, count(CASE WHEN lcp > 4000 THEN 1 END) AS lcp_poor, count(CASE WHEN lcp IS NOT NULL AND lcp >= 0 THEN 1 END) AS lcp_samples, count(CASE WHEN inp IS NOT NULL AND inp >= 0 AND inp <= 200 THEN 1 END) AS inp_good, count(CASE WHEN inp > 200 AND inp <= 500 THEN 1 END) AS inp_ni, count(CASE WHEN inp > 500 THEN 1 END) AS inp_poor, count(CASE WHEN inp IS NOT NULL AND inp >= 0 THEN 1 END) AS inp_samples, count(CASE WHEN cls IS NOT NULL AND cls >= 0 AND cls <= 0.1 THEN 1 END) AS cls_good, count(CASE WHEN cls > 0.1 AND cls <= 0.25 THEN 1 END) AS cls_ni, count(CASE WHEN cls > 0.25 THEN 1 END) AS cls_poor, count(CASE WHEN cls IS NOT NULL AND cls >= 0 THEN 1 END) AS cls_samples, count(CASE WHEN fcp IS NOT NULL AND fcp >= 0 AND fcp <= 1800 THEN 1 END) AS fcp_good, count(CASE WHEN fcp > 1800 AND fcp <= 3000 THEN 1 END) AS fcp_ni, count(CASE WHEN fcp > 3000 THEN 1 END) AS fcp_poor, count(CASE WHEN fcp IS NOT NULL AND fcp >= 0 THEN 1 END) AS fcp_samples, count(CASE WHEN ttfb IS NOT NULL AND ttfb >= 0 AND ttfb <= 800 THEN 1 END) AS ttfb_good, count(CASE WHEN ttfb > 800 AND ttfb <= 1800 THEN 1 END) AS ttfb_ni, count(CASE WHEN ttfb > 1800 THEN 1 END) AS ttfb_poor, count(CASE WHEN ttfb IS NOT NULL AND ttfb >= 0 THEN 1 END) AS ttfb_samples FROM analytics_hot WHERE site = ? AND event = 'vitals' AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY bucket ORDER BY bucket ASC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'visitorList': {\n const rangeRes = validateVisitorRange(params.from, params.to);\n if (!rangeRes.ok) {\n throw new Error(rangeRes.error);\n }\n const hasBefore = typeof params.before === 'number' && !Number.isNaN(params.before);\n const havingClauses: string[] = [];\n const havingBinds: unknown[] = [];\n if (hasBefore) {\n havingClauses.push('last_seen < ?');\n havingBinds.push(params.before);\n }\n if (params.filters) {\n for (const f of params.filters) {\n if (f.dim === 'first_seen' || f.dim === 'last_activity' || f.dim === 'last_seen') {\n const tr = getTimeRangeForFilterValue(f.value);\n if (tr) {\n const col = f.dim === 'first_seen' ? 'first_seen' : 'last_seen';\n if (f.op === 'is') {\n havingClauses.push(`${col} >= ? AND ${col} <= ?`);\n havingBinds.push(tr.startMs, tr.endMs);\n } else {\n havingClauses.push(`(${col} < ? OR ${col} > ?)`);\n havingBinds.push(tr.startMs, tr.endMs);\n }\n }\n }\n }\n }\n const havingClause = havingClauses.length > 0 ? ` HAVING ${havingClauses.join(' AND ')}` : '';\n const sql = `SELECT visitor, min(timestamp) AS first_seen, max(timestamp) AS last_seen, count(*) AS events, count(DISTINCT CASE WHEN session != '' THEN session END) AS sessions, max(country) AS country, max(region) AS region, max(city) AS city, max(browser) AS browser, max(os) AS os, max(device) AS device, max(referrer_host) AS referrer_host, (SELECT count(*) FROM analytics_hot WHERE site = ? AND visitor = '' AND timestamp >= ? AND timestamp <= ?) AS excluded_events FROM analytics_hot WHERE site = ? AND visitor != '' AND event != 'vitals' AND timestamp >= ? AND timestamp <= ?${filterSql} GROUP BY visitor${havingClause} ORDER BY last_seen DESC LIMIT ?`;\n return {\n sql,\n binds: [siteId, fromMs, toMs, siteId, fromMs, toMs, ...filterBinds, ...havingBinds, limit],\n };\n }\n case 'visitorEvents': {\n const rangeRes = validateVisitorRange(params.from, params.to);\n if (!rangeRes.ok) {\n throw new Error(rangeRes.error);\n }\n if (!params.visitor || !isVisitorHash(params.visitor)) {\n throw new Error('visitorEvents requires a valid 64-character hex visitor hash');\n }\n const sql = `SELECT event, path, referrer_host, utm_source, utm_medium, utm_campaign, utm_content, utm_term, country, region, city, browser, os, device, props, session, visitor, timestamp FROM analytics_hot WHERE site = ? AND visitor = ? AND event != 'vitals' AND timestamp >= ? AND timestamp <= ?${filterSql} ORDER BY timestamp ASC LIMIT ?`;\n return {\n sql,\n binds: [siteId, params.visitor, fromMs, toMs, ...filterBinds, limit],\n };\n }\n case 'liveEvents': {\n const minFromMs = Date.now() - LIVE_EVENTS_WINDOW_MS;\n const effectiveFromMs = params.from !== undefined ? Math.max(fromMs, minFromMs) : minFromMs;\n const effectiveLimit = Math.min(Math.max(1, limit), LIVE_EVENTS_MAX_LIMIT);\n const sql = `SELECT event, path, referrer_host, utm_source, utm_medium, utm_campaign, utm_content, utm_term, country, region, city, browser, os, device, props, session, visitor, timestamp FROM analytics_hot WHERE site = ? AND event = 'pageview' AND timestamp >= ?${filterSql} ORDER BY timestamp DESC LIMIT ?`;\n return {\n sql,\n binds: [siteId, effectiveFromMs, ...filterBinds, effectiveLimit],\n };\n }\n default:\n throw new Error(`Unknown insights query: ${name}`);\n }\n}\n","import type { VisitorEventRow } from './queries.js';\n\nexport interface JourneyEvent {\n event: string;\n path: string;\n timestamp: number;\n dwellMs: number | null;\n referrer_host?: string;\n utm_source?: string;\n utm_medium?: string;\n utm_campaign?: string;\n utm_content?: string;\n utm_term?: string;\n country?: string;\n region?: string;\n city?: string;\n browser?: string;\n os?: string;\n device?: string;\n props?: Record<string, unknown>;\n session?: string;\n visitor?: string;\n}\n\nexport interface JourneySession {\n sessionId: string;\n startedAt: number;\n endedAt: number;\n durationMs: number;\n entryPath: string;\n exitPath: string;\n referrerHost?: string;\n events: JourneyEvent[];\n pageviews: number;\n customEvents: number;\n}\n\nexport interface VisitorJourney {\n visitor: string;\n firstSeen: number;\n lastSeen: number;\n totalEvents: number;\n totalPageviews: number;\n totalSessions: number;\n country?: string;\n region?: string;\n city?: string;\n browser?: string;\n os?: string;\n device?: string;\n firstReferrer?: string;\n eventCounts: Record<string, number>;\n hourlyActivity: number[];\n sessions: JourneySession[];\n}\n\nfunction parseTimestamp(ts: number | string): number {\n if (typeof ts === 'number') {\n return Number.isNaN(ts) ? 0 : ts;\n }\n if (typeof ts === 'string') {\n const num = Number(ts);\n if (!Number.isNaN(num) && num > 0) return num;\n const parsed = Date.parse(ts.includes('T') ? ts : `${ts.replace(' ', 'T')}Z`);\n if (!Number.isNaN(parsed)) return parsed;\n }\n return 0;\n}\n\nfunction parseProps(raw: unknown): Record<string, unknown> | undefined {\n if (!raw) return undefined;\n if (typeof raw === 'object' && !Array.isArray(raw)) {\n return raw as Record<string, unknown>;\n }\n if (typeof raw === 'string') {\n try {\n const parsed = JSON.parse(raw);\n if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {\n return parsed as Record<string, unknown>;\n }\n } catch {\n return undefined;\n }\n }\n return undefined;\n}\n\nexport function buildVisitorJourney(rows: VisitorEventRow[]): VisitorJourney {\n const hourlyActivity = new Array<number>(24).fill(0);\n const eventCounts: Record<string, number> = {};\n\n if (!rows || !Array.isArray(rows) || rows.length === 0) {\n return {\n visitor: '',\n firstSeen: 0,\n lastSeen: 0,\n totalEvents: 0,\n totalPageviews: 0,\n totalSessions: 0,\n eventCounts,\n hourlyActivity,\n sessions: [],\n };\n }\n\n // Filter out empty visitor rows (e.g. aggregate-only or invalid rows) and vitals beacons\n const validRows = rows.filter(\n (r) => r && typeof r.visitor === 'string' && r.visitor.trim() !== '' && r.event !== 'vitals',\n );\n if (validRows.length === 0) {\n return {\n visitor: '',\n firstSeen: 0,\n lastSeen: 0,\n totalEvents: 0,\n totalPageviews: 0,\n totalSessions: 0,\n eventCounts,\n hourlyActivity,\n sessions: [],\n };\n }\n\n // Normalize row items with numeric timestamps\n interface NormalizedRow {\n raw: VisitorEventRow;\n timestamp: number;\n sessionKey: string;\n }\n\n const normalized: NormalizedRow[] = validRows.map((r) => {\n const ts = parseTimestamp(r.timestamp);\n const sessionKey =\n r.session && typeof r.session === 'string' && r.session.trim() !== ''\n ? r.session\n : 'unassigned_session';\n return {\n raw: r,\n timestamp: ts,\n sessionKey,\n };\n });\n\n // Sort ascending by timestamp\n normalized.sort((a, b) => a.timestamp - b.timestamp);\n\n const visitor = normalized[0].raw.visitor || '';\n let country: string | undefined;\n let region: string | undefined;\n let city: string | undefined;\n let browser: string | undefined;\n let os: string | undefined;\n let device: string | undefined;\n let firstReferrer: string | undefined;\n\n let totalPageviews = 0;\n\n // Group by session, preserving order of first appearance\n const sessionOrder: string[] = [];\n const sessionMap = new Map<string, NormalizedRow[]>();\n\n for (const item of normalized) {\n const { raw, timestamp, sessionKey } = item;\n\n // Track metadata (latest non-empty wins, except firstReferrer which preserves earliest)\n if (raw.country) country = raw.country;\n if (raw.region) region = raw.region;\n if (raw.city) city = raw.city;\n if (raw.browser) browser = raw.browser;\n if (raw.os) os = raw.os;\n if (raw.device) device = raw.device;\n\n if (!firstReferrer && raw.referrer_host) {\n firstReferrer = raw.referrer_host;\n }\n\n // Tally event counts\n const eventName = raw.event || 'pageview';\n eventCounts[eventName] = (eventCounts[eventName] || 0) + 1;\n if (eventName === 'pageview') {\n totalPageviews++;\n }\n\n // Tally hourly activity (UTC)\n if (timestamp > 0) {\n const utcHour = new Date(timestamp).getUTCHours();\n if (utcHour >= 0 && utcHour < 24) {\n hourlyActivity[utcHour]++;\n }\n }\n\n // Group sessions\n let list = sessionMap.get(sessionKey);\n if (!list) {\n list = [];\n sessionMap.set(sessionKey, list);\n sessionOrder.push(sessionKey);\n }\n list.push(item);\n }\n\n const sessions: JourneySession[] = [];\n\n for (const sessionKey of sessionOrder) {\n const sRows = sessionMap.get(sessionKey);\n if (!sRows || sRows.length === 0) continue;\n // Inside each session, sort by timestamp\n sRows.sort((a, b) => a.timestamp - b.timestamp);\n\n const startedAt = sRows[0].timestamp;\n const endedAt = sRows[sRows.length - 1].timestamp;\n const durationMs = Math.max(0, endedAt - startedAt);\n const entryPath = sRows[0].raw.path || '/';\n const exitPath = sRows[sRows.length - 1].raw.path || '/';\n const referrerHost = sRows[0].raw.referrer_host || undefined;\n\n let sPageviews = 0;\n let sCustomEvents = 0;\n\n const events: JourneyEvent[] = [];\n\n for (let i = 0; i < sRows.length; i++) {\n const current = sRows[i];\n const eventName = current.raw.event || 'pageview';\n if (eventName === 'pageview') {\n sPageviews++;\n } else {\n sCustomEvents++;\n }\n\n // Dwell per event = next event's timestamp â this one's, within the same session.\n // The last event of a session has dwellMs: null, not 0.\n let dwellMs: number | null = null;\n if (i < sRows.length - 1) {\n const next = sRows[i + 1];\n dwellMs = Math.max(0, next.timestamp - current.timestamp);\n }\n\n events.push({\n event: eventName,\n path: current.raw.path || '/',\n timestamp: current.timestamp,\n dwellMs,\n referrer_host: current.raw.referrer_host || undefined,\n utm_source: current.raw.utm_source || undefined,\n utm_medium: current.raw.utm_medium || undefined,\n utm_campaign: current.raw.utm_campaign || undefined,\n utm_content: current.raw.utm_content || undefined,\n utm_term: current.raw.utm_term || undefined,\n country: current.raw.country || undefined,\n region: current.raw.region || undefined,\n city: current.raw.city || undefined,\n browser: current.raw.browser || undefined,\n os: current.raw.os || undefined,\n device: current.raw.device || undefined,\n props: parseProps(current.raw.props),\n session: current.raw.session || undefined,\n visitor: current.raw.visitor || undefined,\n });\n }\n\n sessions.push({\n sessionId: sessionKey,\n startedAt,\n endedAt,\n durationMs,\n entryPath,\n exitPath,\n referrerHost,\n events,\n pageviews: sPageviews,\n customEvents: sCustomEvents,\n });\n }\n\n const firstSeen = normalized[0].timestamp;\n const lastSeen = normalized[normalized.length - 1].timestamp;\n\n return {\n visitor,\n firstSeen,\n lastSeen,\n totalEvents: normalized.length,\n totalPageviews,\n totalSessions: sessions.length,\n country,\n region,\n city,\n browser,\n os,\n device,\n firstReferrer,\n eventCounts,\n hourlyActivity,\n sessions,\n };\n}\n","import type { AnalyticsEvent } from './schema.js';\n\nexport async function encodeArchiveBatch(events: AnalyticsEvent[]): Promise<Uint8Array> {\n const ndjson = events.map((e) => JSON.stringify(e)).join('\\n') + (events.length > 0 ? '\\n' : '');\n const bytes = new TextEncoder().encode(ndjson);\n\n // Use web-standard CompressionStream\n const stream = new ReadableStream({\n start(controller) {\n controller.enqueue(bytes);\n controller.close();\n },\n }).pipeThrough(new CompressionStream('gzip'));\n\n const response = new Response(stream);\n const arrayBuffer = await response.arrayBuffer();\n return new Uint8Array(arrayBuffer);\n}\n\nexport function archiveObjectKey(siteId: string, date: string, seq?: string | number): string {\n const identifier = seq !== undefined ? String(seq) : `${Date.now()}-${crypto.randomUUID()}`;\n return `site=${siteId}/date=${date}/${identifier}.ndjson.gz`;\n}\n\nexport function manifestObjectKey(siteId: string, date: string, seq?: string | number): string {\n const identifier = seq !== undefined ? String(seq) : `${Date.now()}-${crypto.randomUUID()}`;\n return `site=${siteId}/date=${date}/_manifests/${identifier}.json`;\n}\n","// Automatically generated by scripts/bundle-vitals.mjs. Do not edit directly.\nexport const VITALS_PACKAGE_VERSION = \"6.2.0\";\nexport const VITALS_BUNDLE_HASH = \"dfe95eea0b10\";\nexport const VITALS_BUNDLE_PATH = \"/_ev/vitals.dfe95eea0b10.js\";\nexport const VITALS_BUNDLE_CODE = \"var J=class{t;o=0;i=[];l(t){if(t.hadRecentInput)return;let s=this.i[0],r=this.i.at(-1);this.o&&s&&r&&t.startTime-r.startTime<1e3&&t.startTime-s.startTime<5e3?(this.o+=t.value,this.i.push(t)):(this.o=t.value,this.i=[t]),this.t?.(t)}},R=()=>{let e=performance.getEntriesByType(\\\"navigation\\\")[0];if(e&&e.responseStart>0&&e.responseStart<performance.now())return e},q=e=>{if(document.readyState===\\\"loading\\\")return\\\"loading\\\";let t=R();if(t){if(e<t.domInteractive)return\\\"loading\\\";if(t.domContentLoadedEventStart===0||e<t.domContentLoadedEventStart)return\\\"dom-interactive\\\";if(t.domComplete===0||e<t.domComplete)return\\\"dom-content-loaded\\\"}return\\\"complete\\\"},Pt=e=>{let t=e.nodeName;return e.nodeType===1?t.toLowerCase():t.toUpperCase().replace(/^#/,\\\"\\\")},ut=e=>{let t=\\\"\\\";try{for(;e?.nodeType!==9;){let s=e,r=s.id?\\\"#\\\"+s.id:[Pt(s),...Array.from(s.classList??[]).sort()].join(\\\".\\\");if(t.length+r.length>99)return t||r;if(t=t?r+\\\">\\\"+t:r,s.id)break;e=s.parentNode}}catch{}return t},ht=new WeakMap;function A(e,t){let s=ht.get(t);return s||(s=new WeakMap,ht.set(t,s)),s.get(e)||s.set(e,new t),s.get(e)}var bt=-1,O=()=>bt,j=e=>{addEventListener(\\\"pageshow\\\",t=>{t.persisted&&(bt=t.timeStamp,e(t))},!0)},C=(e,t,s,r)=>{let a,n;return i=>{t.value>=0&&(i||r)&&(n=t.value-(a??0),(n||a===void 0)&&(a=t.value,t.delta=n,t.rating=((c,o)=>c>o[1]?\\\"poor\\\":c>o[0]?\\\"needs-improvement\\\":\\\"good\\\")(t.value,s),e(t)))}},gt=e=>{requestAnimationFrame(()=>requestAnimationFrame(()=>e()))},z=()=>R()?.activationStart??0,x=-1,Et=new Set,vt=()=>document.visibilityState!==\\\"hidden\\\"||document.prerendering?1/0:0,st=e=>{if(document.visibilityState===\\\"hidden\\\"){if(e.type===\\\"visibilitychange\\\")for(let t of Et)t();isFinite(x)||(x=e.type===\\\"visibilitychange\\\"?e.timeStamp:0,removeEventListener(\\\"prerenderingchange\\\",st,!0))}},H=(e=!1)=>{if(e&&(x=1/0),x<0){let t=z();x=(document.prerendering?void 0:globalThis.performance.getEntriesByType(\\\"visibility-state\\\").find(r=>r.name===\\\"hidden\\\"&&r.startTime>=t)?.startTime)??vt(),addEventListener(\\\"visibilitychange\\\",st,!0),addEventListener(\\\"prerenderingchange\\\",st,!0),j(()=>{setTimeout(()=>{x=vt()})})}return{get firstHiddenTime(){return x},onHidden(t){Et.add(t)}}},L=(e,t=-1,s,r=0,a,n,i)=>{let c=R(),o=c?.navigationId||0,l=\\\"navigate\\\";return s?l=s:O()>=0?l=\\\"back-forward-cache\\\":c&&(document.prerendering||z()>0?l=\\\"prerender\\\":document.wasDiscarded?l=\\\"restore\\\":c.type&&(l=c.type.replace(/_/g,\\\"-\\\"))),{name:e,value:t,rating:\\\"good\\\",delta:0,entries:[],id:`v6-${Date.now()}-${Math.floor(8999999999999*Math.random())+1e12}`,navigationType:l,navigationId:r||o,navigationInteractionId:a,navigationURL:n||c?.name,navigationStartTime:i||0}},P=(e,t,s={})=>{try{let r=e.filter(a=>PerformanceObserver.supportedEntryTypes.includes(a));if(r.length>0){let a=new PerformanceObserver(n=>{queueMicrotask(()=>{let i=n.getEntries();r.length>1&&i.sort((c,o)=>c.startTime+c.duration-(o.startTime+o.duration)),t(i)})});for(let n of r)a.observe({type:n,buffered:!0,...s});return a}}catch{}},B=e=>globalThis.PerformanceObserver?.supportedEntryTypes?.includes(\\\"soft-navigation\\\")&&typeof globalThis.PerformanceSoftNavigation?.prototype?.getLargestInteractionContentfulPaint==\\\"function\\\"&&e&&e.reportSoftNavs,Ct=(e,t)=>{if(e.set(t.navigationId,t),e.size>2){let s=e.keys().next().value;s!==void 0&&e.delete(s)}},Mt=e=>{let t=!1;return()=>{t||(e(),t=!0)}},K=class{u},Y=e=>{document.prerendering?addEventListener(\\\"prerenderingchange\\\",e,!0):e()},it=[1800,3e3],Lt=(e,t={})=>{let s=B(t);Y(()=>{let r=A(t,K),a=H(),n,i=L(\\\"FCP\\\"),c=P([\\\"paint\\\"],o=>{for(let l of o)l.name===\\\"first-contentful-paint\\\"&&(c.disconnect(),l.startTime<a.firstHiddenTime&&(i.value=Math.max(l.startTime-z(),0),i.entries.push(l),i.navigationId=l.navigationId||i.navigationId,n(!0)))});c&&(n=C(e,i,it,t.reportAllChanges),j(o=>{i=L(\\\"FCP\\\",-1,\\\"back-forward-cache\\\",i.navigationId,i.navigationInteractionId,i.navigationURL,O()),n=C(e,i,it,t.reportAllChanges),gt(()=>{i.value=performance.now()-o.timeStamp,n(!0)})})),s&&P([\\\"soft-navigation\\\"],o=>{o.forEach(l=>{r.u&&l.navigationId&&Ct(r.u,l);let m=Math.max((l.presentationTime||l.paintTime||0)-l.startTime,0);i=L(\\\"FCP\\\",m,\\\"soft-navigation\\\",l.navigationId,l.interactionId,l.name,l.startTime),n=C(e,i,it,t.reportAllChanges),n(!0)})},t)})},Tt=[.1,.25],yt=e=>e.find(t=>t.node?.nodeType===1)||e[0],Ft=(e,t={})=>{let s=A(t=Object.assign({},t),J),r=new WeakMap;s.t=a=>{if(a?.sources?.length){let n=yt(a.sources),i=n?.node;if(i){let c=t.generateTarget?.(i)??ut(i);r.set(n,c)}}},((a,n={})=>{let i=H();Lt(Mt(()=>{let c,o=L(\\\"CLS\\\",0),l=A(n,J),m=(g,u,h,T,I)=>{o=L(\\\"CLS\\\",0,g,u,h,T,I),l.o=0,c=C(a,o,Tt,n.reportAllChanges)},E=(g=!1)=>{l.o>o.value&&(o.value=l.o,o.entries=l.i),c(g)},S=g=>{E(!0),m(\\\"soft-navigation\\\",g.navigationId,g.interactionId,g.name,g.startTime)},d=g=>{for(let u of g)u.entryType!==\\\"soft-navigation\\\"?l.l(u):S(u);E()},p=[\\\"layout-shift\\\"];B(n)&&p.push(\\\"soft-navigation\\\");let f=P(p,d);f&&(c=C(a,o,Tt,n.reportAllChanges),i.onHidden(()=>{d(f.takeRecords()),c(!0)}),j(()=>{m(\\\"back-forward-cache\\\",o.navigationId,o.navigationInteractionId,o.navigationURL,O()),gt(c)}),setTimeout(c))}))})(a=>{e((n=>{let i={};if(n.entries.length){let c=n.entries.reduce((o,l)=>o.value>l.value?o:l);if(c?.sources?.length){let o=yt(c.sources);o&&(i={largestShiftTarget:r.get(o),largestShiftTime:c.startTime,largestShiftValue:c.value,largestShiftSource:o,largestShiftEntry:c,loadState:q(c.startTime)})}}return Object.assign(n,{attribution:i})})(a))},t)},xt=(e,t={})=>{let s=A(t=Object.assign({},t),K);B(t)&&(s.u=new Map),Lt(r=>{e((a=>{let n={timeToFirstByte:0,firstByteToFCP:a.value,loadState:q(O())};if(a.navigationType!==\\\"soft-navigation\\\"){if(a.entries.length){let i=R(),c=a.entries.at(-1);if(i){let o=i.responseStart,l=i.activationStart||0,m=Math.max(0,o-l);n={timeToFirstByte:m,firstByteToFCP:a.value-m,loadState:q(a.entries[0].startTime),navigationEntry:i,fcpEntry:c}}}}else{let i=s.u?.get(a.navigationId);i&&(n={timeToFirstByte:0,firstByteToFCP:a.value,loadState:\\\"complete\\\",navigationEntry:i})}return Object.assign(a,{attribution:n})})(r))},t)},kt=0,at=1/0,_=0,wt=e=>{for(let t of e)t.interactionId&&(at=Math.min(at,t.interactionId),_=Math.max(_,t.interactionId),kt=_?(_-at)/7+1:0)},ct,St=()=>ct?kt:performance.interactionCount??0,At=()=>{\\\"interactionCount\\\"in performance||ct||(ct=P([\\\"event\\\"],wt,{durationThreshold:0}))},Q=class{h=0;v=[];p=new Map;m;M;T(){return St()-this.h}D(){this.h=St(),this.v.length=0,this.p.clear()}S(t){let s=this.T(),r=Math.min(this.v.length-1,Math.floor(s/50));return!s||r!==-1||t!==\\\"soft-navigation\\\"&&t!==\\\"back-forward-cache\\\"?this.v[r]:{k:8,id:-1,entries:[]}}l(t){if(this.m?.(t),!t.interactionId&&t.entryType!==\\\"first-input\\\")return;let s=this.v.at(-1),r=this.p.get(t.interactionId);if(r||this.v.length<10||t.duration>s.k){if(r?t.duration>r.k?(r.entries=[t],r.k=t.duration):t.duration===r.k&&t.startTime===r.entries[0].startTime&&r.entries.push(t):(r={id:t.interactionId,entries:[t],k:t.duration},this.p.set(r.id,r),this.v.push(r)),this.v.sort((a,n)=>n.k-a.k),this.v.length>10){let a=this.v.splice(10);for(let n of a)this.p.delete(n.id)}this.M?.(r)}}},lt=e=>{let t=\\\"requestIdleCallback\\\"in globalThis?1e3:0,s=globalThis.requestIdleCallback||setTimeout,r=globalThis.cancelIdleCallback||clearTimeout;if(document.visibilityState===\\\"hidden\\\")e();else{let a=Mt(e),n=-1,i=()=>{r(n),a()};addEventListener(\\\"visibilitychange\\\",i,{once:!0,capture:!0}),n=s(()=>{removeEventListener(\\\"visibilitychange\\\",i,{capture:!0}),a()},{timeout:t})}},It=[200,500],Bt=(e,t={})=>{let s=A(t=Object.assign({},t),Q),r=[],a=[],n=0,i=new WeakMap,c=new WeakMap,o=!1,l=()=>{o||(lt(m),o=!0)},m=()=>{let d=new Set(s.v.map(g=>i.get(g.entries[0]))),p=a.length-10;a=a.filter((g,u)=>u>=p||d.has(g));let f=new Set;for(let g of a){let u=E(g.startTime,g.processingEnd);for(let h of u)f.add(h)}r=r.filter(g=>g.startTime>n||f.has(g)),o=!1};s.m=d=>{let p=d.startTime+d.duration,f;n=Math.max(n,d.processingEnd);for(let g=a.length-1;g>=0;g--){let u=a[g];if(Math.abs(p-u.renderTime)<=8){f=u,f.startTime=Math.min(d.startTime,f.startTime),f.processingStart=Math.min(d.processingStart,f.processingStart),f.processingEnd=Math.max(d.processingEnd,f.processingEnd),t.includeProcessedEventEntries&&f.entries.push(d);break}}f||(f={startTime:d.startTime,processingStart:d.processingStart,processingEnd:d.processingEnd,renderTime:p,entries:t.includeProcessedEventEntries?[d]:[]},a.push(f)),(d.interactionId||d.entryType===\\\"first-input\\\")&&i.set(d,f),l()},s.M=d=>{if(!c.get(d)){let p=d.entries.find(f=>f.target)?.target;if(p){let f=t.generateTarget?.(p)??ut(p);c.set(d,f)}else{let f=d.entries.find(g=>g.targetSelector)?.targetSelector;f&&c.set(d,f)}}};let E=(d,p)=>{let f=[];for(let g of r)if(!(g.startTime+g.duration<d)){if(g.startTime>p)break;f.push(g)}return f},S=d=>{if(d.entries.length===0){let v=d.navigationStartTime||0,b={processedEventEntries:[],longAnimationFrameEntries:[],inputDelay:0,processingDuration:0,presentationDelay:d.value,loadState:q(v)};return Object.assign(d,{attribution:b})}let p=d.entries[0],f=i.get(p),g=f.processingStart,u=Math.max(p.startTime+p.duration,g),h=Math.min(f.processingEnd,u),T=f.entries.sort((v,b)=>v.processingStart-b.processingStart),I=E(p.startTime,h),k=s.p.get(p.interactionId),M={interactionTarget:c.get(k),interactionType:p.name.startsWith(\\\"key\\\")?\\\"keyboard\\\":\\\"pointer\\\",interactionTime:p.startTime,nextPaintTime:u,processedEventEntries:T,longAnimationFrameEntries:I,inputDelay:g-p.startTime,processingDuration:h-g,presentationDelay:u-h,loadState:q(p.startTime),longestScript:void 0,totalScriptDuration:void 0,totalStyleAndLayoutDuration:void 0,totalPaintDuration:void 0,totalUnattributedDuration:void 0};return(v=>{let b=v.interactionTime,y=v.nextPaintTime;if(!v.longAnimationFrameEntries?.length||!b||!y)return;let F=v.inputDelay,w=v.processingDuration,N,U,Z=0,W=0,tt=0,et=0;for(let $ of v.longAnimationFrameEntries){W=W+$.startTime+$.duration-$.styleAndLayoutStart;for(let D of $.scripts){let ft=D.startTime+D.duration;if(ft<b)continue;let V=ft-Math.max(b,D.startTime),pt=D.duration?V/D.duration*D.forcedStyleAndLayoutDuration:0;Z+=V-pt,W+=pt,V>et&&(U=D.startTime<b+F?\\\"input-delay\\\":D.startTime>=b+F+w?\\\"presentation-delay\\\":\\\"processing-duration\\\",N=D,et=V)}}let nt=v.longAnimationFrameEntries.at(-1),mt=nt?nt.startTime+nt.duration:0;mt>=b+F+w&&(tt=y-mt),N&&U&&(v.longestScript={entry:N,subpart:U,intersectingDuration:et}),v.totalScriptDuration=Z,v.totalStyleAndLayoutDuration=W,v.totalPaintDuration=tt,v.totalUnattributedDuration=y-b-Z-W-tt})(M),Object.assign(d,{attribution:M})};P([\\\"long-animation-frame\\\"],d=>{r=r.concat(d),l()},t),((d,p={})=>{if(!globalThis.PerformanceEventTiming||!(\\\"interactionId\\\"in PerformanceEventTiming.prototype))return;let f=H();Y(()=>{At();let g,u=L(\\\"INP\\\"),h=A(p,Q),T=(y,F,w,N,U)=>{h.D(),u=L(\\\"INP\\\",-1,y,F,w,N,U),g=C(d,u,It,p.reportAllChanges)},I=()=>{let y=h.S(u.navigationType);y&&y.k!==u.value&&(u.value=y.k,u.entries=y.entries,g())},k=y=>{I(),g(!0),T(\\\"soft-navigation\\\",y.navigationId,y.interactionId,y.name,y.startTime)},M=(y,F=!1)=>{lt(()=>{for(let w of y)w.entryType!==\\\"soft-navigation\\\"?h.l(w):k(w);I(),F&&g(!0)})},v=[\\\"event\\\",\\\"first-input\\\"];B(p)&&v.push(\\\"soft-navigation\\\");let b=P(v,M,{...p,durationThreshold:p.durationThreshold??40});g=C(d,u,It,p.reportAllChanges),b&&(f.onHidden(()=>{M(b.takeRecords(),!0)}),j(()=>{T(\\\"back-forward-cache\\\",u.navigationId,u.navigationInteractionId,u.navigationURL,O())}))})})(d=>{e(S(d))},t)},X=class{m;u;l(t){this.m?.(t)}},rt=[2500,4e3],Dt=50,G=[];P([\\\"resource\\\"],e=>{for(let t of e)G.push(t),G.length>Dt&&G.shift()});var Ot=(e,t={})=>{(t=Object.assign({},t)).resourceBufferSize!=null&&(Dt=t.resourceBufferSize);let s=A(t,X),r=new WeakMap;B(t)&&(s.u=new Map),s.m=a=>{let n=a.element;if(n){let i=t.generateTarget?.(n)??ut(n);r.set(a,i)}else a.id&&r.set(a,`#${a.id}`)},((a,n={})=>{let i=!1,c=B(n);Y(()=>{let o,l=H(),m=L(\\\"LCP\\\"),E=A(n,X),S=(u,h,T,I,k)=>{m=L(\\\"LCP\\\",-1,u,h,T,I,k),o=C(a,m,rt,n.reportAllChanges),i=!1,u===\\\"soft-navigation\\\"&&(l=H(!0))},d=u=>{E.u&&u.navigationId&&Ct(E.u,u),i||o(!0),S(\\\"soft-navigation\\\",u.navigationId,u.interactionId,u.name,u.startTime);let h=u.getLargestInteractionContentfulPaint?.();h&&p([h])},p=u=>{n.reportAllChanges||c||(u=u.slice(-1));for(let h of u){if(!h)continue;if(h.entryType===\\\"soft-navigation\\\"){d(h);continue}let T=0,I=[],k=h.startTime;if(h.entryType===\\\"largest-contentful-paint\\\")T=Math.max(h.startTime-z(),0),E.l(h),I=[h];else if(h.entryType===\\\"interaction-contentful-paint\\\"){let M=h;if(!m.navigationId||\\\"interactionId\\\"in M&&M.interactionId!=m.navigationInteractionId)continue;k=M.largestContentfulPaint?.renderTime||0,T=Math.max(k-h.startTime,0),M.largestContentfulPaint&&(E.l(M.largestContentfulPaint),I=[M.largestContentfulPaint])}k<l.firstHiddenTime&&(m.value=T,m.entries=I,o())}},f=[\\\"largest-contentful-paint\\\"];c&&f.push(\\\"interaction-contentful-paint\\\",\\\"soft-navigation\\\");let g=P(f,p);if(g){o=C(a,m,rt,n.reportAllChanges);let u=[\\\"keydown\\\",\\\"click\\\",\\\"visibilitychange\\\"],h=T=>{if(T.isTrusted&&!i){let I=m.id;lt(()=>{if(!i){if(!c){g.disconnect();for(let k of u)removeEventListener(k,h,{capture:!0})}I===m.id&&(i=!0,o(!0))}})}};for(let T of u)addEventListener(T,h,{capture:!0});j(T=>{S(\\\"back-forward-cache\\\",m.navigationId,m.navigationInteractionId,m.navigationURL,O()),o=C(a,m,rt,n.reportAllChanges),gt(()=>{m.value=performance.now()-T.timeStamp,i=!0,o(!0)})})}})})(a=>{e((n=>{let i={timeToFirstByte:0,resourceLoadDelay:0,resourceLoadDuration:0,elementRenderDelay:n.value};if(n.entries.length){let c=n.entries.at(-1),o=c.url&&(G.findLast(S=>S.name===c.url)||performance.getEntriesByType(\\\"resource\\\").findLast(S=>S.name===c.url)),l;i.target=r.get(c),i.lcpEntry=c,c.url&&(i.url=c.url),o&&(i.lcpResourceEntry=o);let m=0,E=0;if(n.navigationType!==\\\"soft-navigation\\\"?(l=R(),m=l?.activationStart??0,E=l?.responseStart??0):(m=n.navigationStartTime||0,l=s.u?.get(n.navigationId)),l){let S=Math.max(0,E-m),d=Math.max(S,o?(o.requestStart||o.startTime)-m:0),p=Math.min(n.value,Math.max(d,o?o.responseEnd-m:0));i={...i,timeToFirstByte:S,resourceLoadDelay:d-S,resourceLoadDuration:p-d,elementRenderDelay:n.value-p,navigationEntry:l}}}return Object.assign(n,{attribution:i})})(a))},t)},ot=[800,1800],dt=e=>{document.prerendering?Y(()=>dt(e)):document.readyState!==\\\"complete\\\"?addEventListener(\\\"load\\\",()=>dt(e),!0):setTimeout(e)},Rt=(e,t={})=>{((s,r={})=>{let a=B(r),n=L(\\\"TTFB\\\"),i=C(s,n,ot,r.reportAllChanges);dt(()=>{let c=R();if(c){let o=c.responseStart;n.value=Math.max(o-z(),0),n.entries=[c],i(!0),j(()=>{n=L(\\\"TTFB\\\",0,\\\"back-forward-cache\\\",n.navigationId,n.navigationInteractionId,n.navigationURL,O()),i=C(s,n,ot,r.reportAllChanges),i(!0)}),a&&P([\\\"soft-navigation\\\"],l=>{l.forEach(m=>{m.navigationId&&(n=L(\\\"TTFB\\\",0,\\\"soft-navigation\\\",m.navigationId,m.interactionId,m.name,m.startTime),n.entries=[m],i=C(s,n,ot,r.reportAllChanges),i(!0))})},r)}})})(s=>{e((r=>{let a=r.entries[0],n={waitingDuration:0,cacheDuration:0,dnsDuration:0,connectionDuration:0,requestDuration:0,navigationEntry:a};if(r.entries.length&&a instanceof PerformanceNavigationTiming){let i=a.activationStart||0,c=Math.max((a.workerStart||a.fetchStart||0)-i,0),o=Math.max(a.domainLookupStart-i,0),l=Math.max(a.connectStart-i,0),m=Math.max(a.connectEnd-i,0);n={waitingDuration:c,cacheDuration:o-c,dnsDuration:l-o,connectionDuration:m-l,requestDuration:r.value-m,navigationEntry:a}}return Object.assign(r,{attribution:n})})(s))},t)};export{Tt as CLSThresholds,it as FCPThresholds,It as INPThresholds,rt as LCPThresholds,ot as TTFBThresholds,Ft as onCLS,xt as onFCP,Bt as onINP,Ot as onLCP,Rt as onTTFB};\";\n","import { VITALS_BUNDLE_PATH } from './vitalsBundle.generated.js';\n\nexport interface BeaconSnippetOptions {\n siteId?: string;\n customEvents?: string[];\n endpoint?: string;\n vitalsUrl?: string;\n}\n\nexport function buildBeaconSnippet(opts?: BeaconSnippetOptions): string {\n const endpoint = opts?.endpoint ?? '/_ev';\n\n return `(function(){\n var ep=${JSON.stringify(endpoint)};\n function s(p){\n var b=JSON.stringify(p);\n if(navigator.sendBeacon){\n navigator.sendBeacon(ep,b);\n } else {\n fetch(ep,{method:\"POST\",body:b,keepalive:true,headers:{\"Content-Type\":\"application/json\"}}).catch(function(){});\n }\n }\n function t(n,props){\n s({u:location.pathname+location.search,r:document.referrer,e:n||\"pageview\",p:props});\n }\n window.cms=window.cms||{};\n window.cms.track=t;\n t(\"pageview\");\n document.addEventListener(\"astro:page-load\",function(){\n t(\"pageview\");\n });\n})();`;\n}\n\nexport function buildVitalsSnippet(opts?: BeaconSnippetOptions): string {\n const endpoint = opts?.endpoint ?? '/_ev';\n const vitalsUrl = opts?.vitalsUrl ?? VITALS_BUNDLE_PATH;\n\n return `(function(){\n var ep=${JSON.stringify(endpoint)};\n var vu=${JSON.stringify(vitalsUrl)};\n var v={};\n var sent=false;\n function s(p){\n var b=JSON.stringify(p);\n if(navigator.sendBeacon){\n navigator.sendBeacon(ep,b);\n } else {\n fetch(ep,{method:\"POST\",body:b,keepalive:true,headers:{\"Content-Type\":\"application/json\"}}).catch(function(){});\n }\n }\n function flush(){\n if(sent)return;\n sent=true;\n s({u:location.pathname+location.search,r:document.referrer,e:\"vitals\",p:v});\n }\n function init(){\n import(/* @vite-ignore */ vu).then(function(m){\n function set(name,metric){\n v[name]=metric.value;\n if(metric.attribution){\n v.attribution=v.attribution||{};\n if(name===\"lcp\"&&metric.attribution.element)v.attribution.lcp=metric.attribution.element;\n if(name===\"cls\"&&metric.attribution.largestShiftTarget)v.attribution.cls=metric.attribution.largestShiftTarget;\n }\n }\n m.onLCP(function(metric){set(\"lcp\",metric);});\n m.onINP(function(metric){set(\"inp\",metric);});\n m.onCLS(function(metric){set(\"cls\",metric);});\n m.onFCP(function(metric){set(\"fcp\",metric);});\n m.onTTFB(function(metric){set(\"ttfb\",metric);});\n }).catch(function(){});\n }\n if(document.readyState===\"complete\"){\n init();\n }else{\n window.addEventListener(\"load\",init);\n }\n document.addEventListener(\"visibilitychange\",function(){\n if(document.visibilityState===\"hidden\")flush();\n });\n window.addEventListener(\"pagehide\",flush);\n document.addEventListener(\"astro:before-swap\",flush);\n document.addEventListener(\"astro:page-load\",function(){\n sent=false;\n v={};\n });\n})();`;\n}\n","export interface UserAgentClassification {\n browser: 'Chrome' | 'Safari' | 'Firefox' | 'Edge' | 'Opera' | 'Samsung Internet' | 'Other';\n os: 'Windows' | 'macOS' | 'iOS' | 'Android' | 'Linux' | 'ChromeOS' | 'Other';\n device: 'Desktop' | 'Mobile' | 'Tablet';\n}\n\n/**\n * Lightweight, zero-dependency User-Agent classifier.\n * Maps raw User-Agent strings to coarse categories for privacy and cardinality hygiene.\n */\nexport function classifyUserAgent(ua?: string): UserAgentClassification {\n if (!ua || typeof ua !== 'string') {\n return {\n browser: 'Other',\n os: 'Other',\n device: 'Desktop',\n };\n }\n\n // 1. Browser (order matters due to shared tokens)\n let browser: UserAgentClassification['browser'] = 'Other';\n if (/SamsungBrowser\\//i.test(ua)) {\n browser = 'Samsung Internet';\n } else if (/OPR\\/|Opera\\//i.test(ua)) {\n browser = 'Opera';\n } else if (/Edg\\/|Edge\\//i.test(ua)) {\n browser = 'Edge';\n } else if (/Firefox\\/|FxiOS\\//i.test(ua)) {\n browser = 'Firefox';\n } else if (/Chrome\\/|CriOS\\/|HeadlessChrome\\//i.test(ua)) {\n browser = 'Chrome';\n } else if (/Safari\\//i.test(ua) && !/Android/i.test(ua)) {\n browser = 'Safari';\n }\n\n // 2. OS (order matters)\n let os: UserAgentClassification['os'] = 'Other';\n if (/Android/i.test(ua)) {\n os = 'Android';\n } else if (/iPhone|iPad|iPod/i.test(ua)) {\n os = 'iOS';\n } else if (/Macintosh|Mac OS X/i.test(ua)) {\n os = 'macOS';\n } else if (/Windows|Win32|Win64|Windows NT/i.test(ua)) {\n os = 'Windows';\n } else if (/CrOS/i.test(ua)) {\n os = 'ChromeOS';\n } else if (/Linux|X11/i.test(ua)) {\n os = 'Linux';\n }\n\n // 3. Device\n let device: UserAgentClassification['device'] = 'Desktop';\n if (/iPad|Tablet|Silk\\/|Kindle/i.test(ua) || (/Android/i.test(ua) && !/Mobile/i.test(ua))) {\n device = 'Tablet';\n } else if (/Mobile|iPhone|iPod|Android|webOS|BlackBerry|Windows Phone/i.test(ua)) {\n device = 'Mobile';\n }\n\n return { browser, os, device };\n}\n","export type VitalsMetricName = 'lcp' | 'inp' | 'cls' | 'fcp' | 'ttfb';\nexport type VitalsRating = 'good' | 'needs-improvement' | 'poor';\n\nexport const WEB_VITALS_THRESHOLDS: Record<VitalsMetricName, readonly [number, number]> = {\n lcp: [2500, 4000], // ms\n inp: [200, 500], // ms\n cls: [0.1, 0.25], // unitless\n fcp: [1800, 3000], // ms\n ttfb: [800, 1800], // ms\n} as const; // [good ceiling, poor floor]\n\nexport function getVitalsRating(metric: VitalsMetricName, value: number): VitalsRating {\n const [goodCeil, poorFloor] = WEB_VITALS_THRESHOLDS[metric];\n if (value <= goodCeil) return 'good';\n if (value <= poorFloor) return 'needs-improvement';\n return 'poor';\n}\n\nexport interface MetricSampleDistribution {\n good: number;\n needsImprovement?: number;\n poor?: number;\n samples: number;\n}\n\n/**\n * Calculates the aggregate Core Web Vitals Experience Score (0-100).\n * Weighted equally across Core Web Vitals metrics (LCP, INP, CLS) where samples exist.\n * Diagnostic metrics (FCP, TTFB) are excluded from the score.\n */\nexport function computeExperienceScore(\n distributionMap: Partial<Record<VitalsMetricName, MetricSampleDistribution>>,\n): number {\n const cwvMetrics: Array<VitalsMetricName> = ['lcp', 'inp', 'cls'];\n let sumRatios = 0;\n let activeMetricCount = 0;\n\n for (const metric of cwvMetrics) {\n const dist = distributionMap[metric];\n if (dist && dist.samples > 0) {\n const goodRatio = dist.good / dist.samples;\n sumRatios += goodRatio;\n activeMetricCount++;\n }\n }\n\n if (activeMetricCount === 0) {\n return 100;\n }\n\n return Math.round((sumRatios / activeMetricCount) * 100);\n}\n","export interface PrivacyModelEntry {\n title: string;\n description: string;\n}\n\nexport const PRIVACY_COLLECTION_MODEL: Record<string, PrivacyModelEntry> = {\n cookies: {\n title: 'No Cookies or Local Storage',\n description:\n 'Analytics runs entirely cookieless without persistent client-side storage or tracking IDs.',\n },\n ipHandling: {\n title: 'Zero Raw IP Storage',\n description:\n 'Client IP addresses are hashed in-memory alongside a daily rotating salt and are never persisted anywhere.',\n },\n dailyRotation: {\n title: 'Cryptographic Salt Destruction',\n description:\n \"Salts rotate at 00:00 UTC and past salts are destroyed, making yesterday's visitors cryptographically unlinkable today.\",\n },\n sessionWindows: {\n title: 'Fixed 30-Minute Tumbling Buckets',\n description:\n 'Sessions are computed into 30-minute tumbling time windows derived from daily visitor hashes without sliding state.',\n },\n privacySignals: {\n title: 'Honors GPC & DNT',\n description:\n 'Requests with Sec-GPC: 1 or DNT: 1 omit visitor and session identifiers entirely and record aggregate-only metrics.',\n },\n ownership: {\n title: 'First-Party Infrastructure',\n description:\n 'Raw analytics data never leaves the platform or site infrastructure; hot queries and cold archives are platform-owned.',\n },\n};\n","import {\n EMAIL_MEDIUMS,\n type FilterDimension,\n type InsightsFilter,\n MAX_FILTERS_PER_QUERY,\n PAID_MEDIUMS,\n SEARCH_HOSTS,\n SOCIAL_HOSTS,\n type VisitorEventRow,\n type VisitorListRow,\n getTimeRangeForFilterValue,\n isValidFilterValue,\n isVisitorHash,\n} from './queries.js';\n\nexport const PARQUET_SCHEMA_VERSION = 2;\nexport const ROLLUP_SCHEMA_VERSION = 1;\nexport const MAX_RANGE_DAYS = 400;\nexport const HOT_TIER_RETENTION_DAYS = 90;\n\nexport const PARQUET_COLUMNS = [\n 'site',\n 'timestamp',\n 'event',\n 'path',\n 'referrer_host',\n 'utm_source',\n 'utm_medium',\n 'utm_campaign',\n 'utm_content',\n 'utm_term',\n 'country',\n 'region',\n 'city',\n 'browser',\n 'os',\n 'device',\n 'lcp',\n 'inp',\n 'cls',\n 'fcp',\n 'ttfb',\n 'vitals_attribution',\n 'visitor',\n 'session',\n 'props',\n 'flagged',\n 'aggregate_only',\n 'v',\n 'date',\n 'seq',\n] as const;\n\nexport const ROLLUP_QUERIES = [\n 'allTimeSummary',\n 'allTimeTimeseries',\n 'allTimeTopPages',\n 'allTimeReferrers',\n 'allTimeCountries',\n 'allTimeCustomEvents',\n 'allTimeVitalsPercentiles',\n] as const;\n\nexport type RollupQueryName = (typeof ROLLUP_QUERIES)[number];\n\nexport const DEEP_QUERIES = [\n 'funnel',\n 'funnelBreakdown',\n 'entryExitPaths',\n 'pathTransitions',\n 'retentionCohorts',\n 'longRangeTimeseries',\n 'longRangeTopPages',\n 'longRangeReferrers',\n 'longRangeCountries',\n 'longRangeCustomEvents',\n 'longRangeSummary',\n 'vitalsPercentiles',\n 'visitorList',\n 'visitorEvents',\n ...ROLLUP_QUERIES,\n] as const;\n\nexport type DeepQueryName = (typeof DEEP_QUERIES)[number];\n\nfunction generateLogEdges(min: number, max: number, count: number, decimals = 0): number[] {\n const edges: number[] = [];\n const factor = (max / min) ** (1 / count);\n let cur = min;\n for (let i = 1; i <= count; i++) {\n cur *= factor;\n const factorDec = 10 ** decimals;\n edges.push(Math.round(cur * factorDec) / factorDec);\n }\n return edges;\n}\n\nexport const VITALS_HISTOGRAM_EDGES: Record<'lcp' | 'inp' | 'cls' | 'fcp' | 'ttfb', number[]> = {\n lcp: generateLogEdges(100, 10000, 48, 1),\n inp: generateLogEdges(10, 2000, 48, 1),\n cls: generateLogEdges(0.005, 1.0, 48, 4),\n fcp: generateLogEdges(100, 10000, 48, 1),\n ttfb: generateLogEdges(10, 5000, 48, 1),\n};\n\nexport function vitalsBucketIndexExpr(metric: string, column: string): string {\n const edges = VITALS_HISTOGRAM_EDGES[metric as keyof typeof VITALS_HISTOGRAM_EDGES];\n if (!edges) {\n throw new Error(`Unknown vitals metric: ${metric}`);\n }\n const whens = edges.map((e, idx) => `WHEN ${column} <= ${e} THEN ${idx}`);\n return `CASE WHEN ${column} IS NULL THEN NULL ${whens.join(' ')} ELSE ${edges.length} END`;\n}\n\nexport type SessionizationMode = 'strict' | 'stitched';\n\nexport interface FunnelStep {\n name?: string;\n path?: string;\n event?: string;\n}\n\nexport interface FunnelRow {\n step: number;\n name: string;\n count: number;\n dropOff: number;\n conversionRate: number;\n}\n\nexport interface FunnelBreakdownRow {\n value: string;\n people: number;\n converted: number;\n conversionRate: number;\n}\n\nexport interface EntryExitPathsRow {\n path: string;\n entries: number;\n exits: number;\n bounces: number;\n}\n\nexport interface PathTransitionsRow {\n fromPath: string;\n toPath: string;\n count: number;\n}\n\nexport interface RetentionCohortsRow {\n cohort: string;\n period: number;\n visitors: number;\n retained: number;\n percentage: number;\n}\n\nexport interface LongRangeTimeseriesRow {\n bucket: string;\n pageviews: number;\n visitors: number;\n sessions: number;\n}\n\nexport interface LongRangeTopPagesRow {\n path: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface LongRangeReferrersRow {\n referrer: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface LongRangeCountriesRow {\n country: string;\n pageviews: number;\n visitors: number;\n}\n\nexport interface LongRangeCustomEventsRow {\n event: string;\n count: number;\n}\n\nexport interface LongRangeSummaryRow {\n pageviews: number;\n visitors: number;\n sessions: number;\n bounceRate?: number;\n}\n\nexport interface VitalsPercentilesRow {\n metric: 'lcp' | 'inp' | 'cls' | 'fcp' | 'ttfb';\n p75: number;\n samples: number;\n}\n\nexport interface DeepQueryTypes {\n funnel: FunnelRow;\n funnelBreakdown: FunnelBreakdownRow;\n entryExitPaths: EntryExitPathsRow;\n pathTransitions: PathTransitionsRow;\n retentionCohorts: RetentionCohortsRow;\n longRangeTimeseries: LongRangeTimeseriesRow;\n longRangeTopPages: LongRangeTopPagesRow;\n longRangeReferrers: LongRangeReferrersRow;\n longRangeCountries: LongRangeCountriesRow;\n longRangeCustomEvents: LongRangeCustomEventsRow;\n longRangeSummary: LongRangeSummaryRow;\n vitalsPercentiles: VitalsPercentilesRow;\n visitorList: VisitorListRow;\n visitorEvents: VisitorEventRow;\n}\n\nexport interface DeepQueryParams {\n siteId: string;\n from: string | number;\n to: string | number;\n sessionization?: SessionizationMode;\n steps?: FunnelStep[] | string;\n windowMinutes?: number | string;\n limit?: number | string;\n interval?: 'hour' | 'day' | 'week' | 'month';\n granularity?: 'day' | 'week';\n filters?: InsightsFilter[] | string;\n dimension?: 'referrer' | 'country' | string;\n visitor?: string;\n before?: number | string;\n}\n\nexport interface ValidatedDeepParams {\n siteId: string;\n from: string;\n to: string;\n fromDate: string;\n toDate: string;\n fromMs: number;\n toMs: number;\n sessionization: SessionizationMode;\n steps: FunnelStep[];\n windowMinutes: number;\n limit: number;\n interval: 'hour' | 'day' | 'week' | 'month';\n granularity: 'day' | 'week';\n filters: InsightsFilter[];\n dimension: 'referrer' | 'country';\n visitor?: string;\n before?: number;\n}\n\nexport type ValidateDeepParamsResult =\n | { ok: true; params: ValidatedDeepParams }\n | { ok: false; error: string; code?: string };\n\nfunction parseDateToYmd(val: string | number): { ymd: string; ms: number } | null {\n if (typeof val === 'number') {\n if (Number.isNaN(val) || val <= 0) return null;\n const d = new Date(val);\n if (Number.isNaN(d.getTime())) return null;\n return { ymd: d.toISOString().slice(0, 10), ms: val };\n }\n if (typeof val === 'string') {\n const trimmed = val.trim();\n if (!trimmed) return null;\n // Check YYYY-MM-DD format\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(trimmed)) {\n const d = new Date(`${trimmed}T00:00:00.000Z`);\n if (Number.isNaN(d.getTime())) return null;\n return { ymd: trimmed, ms: d.getTime() };\n }\n const parsed = Date.parse(trimmed);\n if (!Number.isNaN(parsed)) {\n return { ymd: new Date(parsed).toISOString().slice(0, 10), ms: parsed };\n }\n const num = Number(trimmed);\n if (!Number.isNaN(num) && num > 0) {\n const d = new Date(num);\n if (!Number.isNaN(d.getTime())) {\n return { ymd: d.toISOString().slice(0, 10), ms: num };\n }\n }\n }\n return null;\n}\n\nexport function validateDeepParams(\n name: string,\n raw: Record<string, unknown> | DeepQueryParams,\n): ValidateDeepParamsResult {\n if (!DEEP_QUERIES.includes(name as DeepQueryName)) {\n return { ok: false, error: 'invalid_query' };\n }\n\n const siteId = typeof raw.siteId === 'string' ? raw.siteId.trim() : '';\n if (!siteId) {\n return { ok: false, error: 'missing_site_bounds' };\n }\n\n if (raw.from === undefined || raw.from === null || raw.to === undefined || raw.to === null) {\n return { ok: false, error: 'missing_site_bounds' };\n }\n\n const parsedFrom = parseDateToYmd(raw.from as string | number);\n const parsedTo = parseDateToYmd(raw.to as string | number);\n\n if (!parsedFrom || !parsedTo) {\n return { ok: false, error: 'invalid_params' };\n }\n\n if (parsedFrom.ymd > parsedTo.ymd || parsedFrom.ms > parsedTo.ms) {\n return { ok: false, error: 'invalid_date_range' };\n }\n\n if (name === 'visitorList' || name === 'visitorEvents') {\n if (parsedFrom.ymd !== parsedTo.ymd) {\n return { ok: false, error: 'visitor_range_must_be_single_day' };\n }\n }\n\n if (name === 'visitorEvents') {\n if (!raw.visitor || typeof raw.visitor !== 'string' || !isVisitorHash(raw.visitor)) {\n return { ok: false, error: 'invalid_visitor' };\n }\n }\n\n const daysDiff = Math.round((parsedTo.ms - parsedFrom.ms) / (24 * 60 * 60 * 1000)) + 1;\n const isRollupQuery = (ROLLUP_QUERIES as readonly string[]).includes(name);\n if (!isRollupQuery && daysDiff > MAX_RANGE_DAYS) {\n return { ok: false, error: 'range_too_large' };\n }\n\n const sessionization: SessionizationMode =\n name === 'visitorList' || name === 'visitorEvents' || isRollupQuery\n ? 'strict'\n : raw.sessionization === 'stitched'\n ? 'stitched'\n : 'strict';\n\n let steps: FunnelStep[] = [];\n if (name === 'funnel' || name === 'funnelBreakdown') {\n let parsedSteps: unknown = raw.steps;\n if (typeof raw.steps === 'string') {\n try {\n parsedSteps = JSON.parse(raw.steps);\n } catch {\n // Fallback: comma-separated\n parsedSteps = raw.steps.split(',').map((s: string) => ({ path: s.trim() }));\n }\n }\n if (Array.isArray(parsedSteps)) {\n steps = parsedSteps.map((s: unknown) => {\n if (typeof s === 'string') {\n return s.startsWith('/') ? { path: s } : { event: s };\n }\n if (s && typeof s === 'object') {\n const obj = s as Record<string, unknown>;\n return {\n name: typeof obj.name === 'string' ? obj.name : undefined,\n path: typeof obj.path === 'string' ? obj.path : undefined,\n event: typeof obj.event === 'string' ? obj.event : undefined,\n };\n }\n return {};\n });\n }\n if (steps.length < 2 || steps.length > 8) {\n return { ok: false, error: 'invalid_steps' };\n }\n }\n\n const filters: InsightsFilter[] = [];\n let parsedFilters: unknown = raw.filters;\n if (typeof raw.filters === 'string') {\n try {\n parsedFilters = JSON.parse(raw.filters);\n } catch {\n parsedFilters = [];\n }\n }\n if (Array.isArray(parsedFilters)) {\n for (const f of parsedFilters.slice(0, MAX_FILTERS_PER_QUERY)) {\n if (!f || typeof f !== 'object') continue;\n const { dim, op, value, key } = f as Record<string, unknown>;\n if (typeof dim !== 'string' || typeof op !== 'string' || typeof value !== 'string') continue;\n if (op !== 'is' && op !== 'is_not') continue;\n if (!isValidFilterValue(value)) continue;\n if (dim === 'prop') {\n if (typeof key !== 'string' || !isValidFilterValue(key)) continue;\n filters.push({ dim: 'prop', op, value, key });\n } else {\n filters.push({ dim: dim as FilterDimension, op, value });\n }\n }\n }\n if (isRollupQuery && filters.length > 0) {\n return { ok: false, error: 'filters_unsupported_at_rollup_tier' };\n }\n\n const dimension: 'referrer' | 'country' = raw.dimension === 'country' ? 'country' : 'referrer';\n\n const defaultLimit = name === 'visitorEvents' ? 1000 : name === 'visitorList' ? 50 : 10;\n const maxLimit = name === 'visitorEvents' ? 1000 : 100;\n\n let limit = defaultLimit;\n if (raw.limit !== undefined && raw.limit !== null) {\n const numLimit = Number(raw.limit);\n if (Number.isNaN(numLimit) || numLimit < 1) {\n return { ok: false, error: 'invalid_limit' };\n }\n if (numLimit > maxLimit) {\n return { ok: false, error: 'limit_too_large' };\n }\n limit = numLimit;\n }\n\n let before: number | undefined;\n if (raw.before !== undefined && raw.before !== null) {\n const numBefore = Number(raw.before);\n if (!Number.isNaN(numBefore) && numBefore > 0) {\n before = numBefore;\n }\n }\n\n let windowMinutes = 60;\n if (raw.windowMinutes !== undefined && raw.windowMinutes !== null) {\n const numWindow = Number(raw.windowMinutes);\n if (!Number.isNaN(numWindow) && numWindow > 0) {\n windowMinutes = numWindow;\n }\n }\n\n const interval = (\n ['hour', 'day', 'week', 'month'].includes(raw.interval as string) ? raw.interval : 'day'\n ) as 'hour' | 'day' | 'week' | 'month';\n\n const granularity = (raw.granularity === 'week' ? 'week' : 'day') as 'day' | 'week';\n\n return {\n ok: true,\n params: {\n siteId,\n from: String(raw.from),\n to: String(raw.to),\n fromDate: parsedFrom.ymd,\n toDate: parsedTo.ymd,\n fromMs: parsedFrom.ms,\n toMs: parsedTo.ms,\n sessionization,\n steps,\n windowMinutes,\n limit,\n interval,\n granularity,\n filters,\n dimension,\n visitor: typeof raw.visitor === 'string' ? raw.visitor : undefined,\n before,\n },\n };\n}\n\nfunction escapeSql(val: string): string {\n return val.replace(/'/g, \"''\");\n}\n\nconst DUCKDB_DIMENSION_COLUMNS: Record<string, string> = {\n path: 'path',\n referrer: 'referrer_host',\n country: 'country',\n region: 'region',\n city: 'city',\n browser: 'browser',\n os: 'os',\n device: 'device',\n event: 'event',\n utm_campaign: 'utm_campaign',\n utm_source: 'utm_source',\n utm_medium: 'utm_medium',\n utm_content: 'utm_content',\n utm_term: 'utm_term',\n};\n\nfunction hasOwn(map: Record<string, string>, key: string): boolean {\n return Object.prototype.hasOwnProperty.call(map, key);\n}\n\nfunction getDuckDbChannelPredicate(channel: string): string | null {\n const socialList = SOCIAL_HOSTS.map((h) => `'${h}'`).join(', ');\n const searchList = SEARCH_HOSTS.map((h) => `'${h}'`).join(', ');\n const nonReferralMediums = [...PAID_MEDIUMS, ...EMAIL_MEDIUMS].map((m) => `'${m}'`).join(', ');\n const allSocialAndSearch = [...SOCIAL_HOSTS, ...SEARCH_HOSTS].map((h) => `'${h}'`).join(', ');\n\n switch (channel) {\n case 'direct':\n return `((referrer_host IS NULL OR referrer_host = '') AND (utm_source IS NULL OR utm_source = ''))`;\n case 'paid':\n return `utm_medium IN (${PAID_MEDIUMS.map((m) => `'${m}'`).join(', ')})`;\n case 'email':\n return `utm_medium IN (${EMAIL_MEDIUMS.map((m) => `'${m}'`).join(', ')})`;\n case 'social':\n return `referrer_host IN (${socialList})`;\n case 'organic':\n return `referrer_host IN (${searchList})`;\n case 'referral':\n return `(referrer_host IS NOT NULL AND referrer_host != '' AND (utm_medium IS NULL OR utm_medium NOT IN (${nonReferralMediums})) AND referrer_host NOT IN (${allSocialAndSearch}))`;\n default:\n return null;\n }\n}\n\nexport function buildDuckDbFilterPredicates(filters?: InsightsFilter[]): string[] {\n const clauses: string[] = [];\n if (!filters || !Array.isArray(filters)) return clauses;\n\n for (const filter of filters.slice(0, MAX_FILTERS_PER_QUERY)) {\n if (!filter || typeof filter !== 'object') continue;\n const { dim, op, value, key } = filter;\n if (op !== 'is' && op !== 'is_not') continue;\n if (!isValidFilterValue(value)) continue;\n\n if (dim === 'channel') {\n const channelPred = getDuckDbChannelPredicate(value);\n if (!channelPred) continue;\n if (op === 'is') {\n clauses.push(channelPred);\n } else {\n clauses.push(`NOT (${channelPred})`);\n }\n } else if (dim === 'prop') {\n if (!key || !isValidFilterValue(key)) continue;\n const escapedKey = escapeSql(key);\n const escapedVal = escapeSql(value);\n if (op === 'is') {\n clauses.push(`json_extract_string(props, '$.${escapedKey}') = '${escapedVal}'`);\n } else {\n clauses.push(\n `(json_extract_string(props, '$.${escapedKey}') != '${escapedVal}' OR json_extract_string(props, '$.${escapedKey}') IS NULL)`,\n );\n }\n } else if (hasOwn(DUCKDB_DIMENSION_COLUMNS, dim)) {\n const col = DUCKDB_DIMENSION_COLUMNS[dim];\n const escapedVal = escapeSql(value);\n if (op === 'is') {\n clauses.push(`${col} = '${escapedVal}'`);\n } else {\n clauses.push(`(${col} != '${escapedVal}' OR ${col} IS NULL)`);\n }\n }\n // entry_page / exit_page are session-scoped, not row-scoped â skipped at deep tier.\n }\n\n return clauses;\n}\n\nfunction renderStepCondition(step: FunnelStep, defaultName: string): string {\n const parts: string[] = [];\n if (step.path) {\n parts.push(`path = '${escapeSql(step.path)}'`);\n }\n if (step.event) {\n parts.push(`event = '${escapeSql(step.event)}'`);\n }\n if (parts.length === 0) {\n const name = step.name || defaultName;\n if (name.startsWith('/')) {\n parts.push(`path = '${escapeSql(name)}'`);\n } else {\n parts.push(`event = '${escapeSql(name)}'`);\n }\n }\n return parts.join(' AND ');\n}\n\nfunction renderBaseCte(filesList: string, site: string, filterSql = ''): string {\n return `\nWITH raw_events AS (\n SELECT\n site,\n timestamp,\n event,\n path,\n referrer_host,\n utm_source,\n utm_medium,\n utm_campaign,\n utm_content,\n utm_term,\n country,\n region,\n city,\n browser,\n os,\n device,\n lcp,\n inp,\n cls,\n fcp,\n ttfb,\n vitals_attribution,\n visitor,\n session,\n props,\n flagged,\n aggregate_only,\n coalesce(v, 1) AS v,\n date,\n seq\n FROM read_parquet([${filesList}])\n WHERE site = '${site}'\n AND (flagged IS NULL OR flagged != 'bot')${filterSql}\n)\n`;\n}\n\nfunction renderSessionCte(\n sessionization: SessionizationMode,\n fromDate: string,\n toDate: string,\n excludedMonthsClause = '',\n): string {\n return sessionization === 'stitched'\n ? `\n, valid_events AS (\n SELECT * FROM raw_events\n WHERE (aggregate_only IS NULL OR aggregate_only = false)\n AND ((visitor IS NOT NULL AND visitor != '') OR (session IS NOT NULL AND session != ''))\n)\n, event_lags AS (\n SELECT\n *,\n coalesce(nullif(visitor, ''), session) AS entity_id,\n lag(timestamp) OVER (\n PARTITION BY coalesce(nullif(visitor, ''), session)\n ORDER BY timestamp ASC, date ASC, seq ASC\n ) AS prev_ts\n FROM valid_events\n)\n, session_boundaries AS (\n SELECT\n *,\n CASE\n WHEN prev_ts IS NULL OR timestamp - prev_ts > 1800000 THEN 1\n ELSE 0\n END AS is_new_session\n FROM event_lags\n)\n, sessionized_events AS (\n SELECT\n *,\n entity_id || '_' || sum(is_new_session) OVER (\n PARTITION BY entity_id\n ORDER BY timestamp ASC, date ASC, seq ASC\n ) AS session_id\n FROM session_boundaries\n)\n, session_starts AS (\n SELECT\n session_id,\n entity_id,\n min(timestamp) AS session_min_ts,\n strftime(epoch_ms(min(timestamp)), '%Y-%m-%d') AS owning_date\n FROM sessionized_events\n GROUP BY session_id, entity_id\n)\n, bounded_sessions AS (\n SELECT\n e.*,\n s.owning_date,\n s.session_min_ts\n FROM sessionized_events e\n JOIN session_starts s ON e.session_id = s.session_id\n WHERE s.owning_date >= '${fromDate}' AND s.owning_date <= '${toDate}'${excludedMonthsClause}\n)\n`\n : `\n, valid_events AS (\n SELECT * FROM raw_events\n WHERE (aggregate_only IS NULL OR aggregate_only = false)\n AND session IS NOT NULL AND session != ''\n)\n, session_starts AS (\n SELECT\n session AS session_id,\n min(timestamp) AS session_min_ts,\n strftime(epoch_ms(min(timestamp)), '%Y-%m-%d') AS owning_date\n FROM valid_events\n GROUP BY session\n)\n, bounded_sessions AS (\n SELECT\n e.*,\n s.owning_date,\n s.session_min_ts,\n e.session AS session_id,\n coalesce(nullif(e.visitor, ''), e.session) AS entity_id\n FROM valid_events e\n JOIN session_starts s ON e.session = s.session_id\n WHERE s.owning_date >= '${fromDate}' AND s.owning_date <= '${toDate}'${excludedMonthsClause}\n)\n`;\n}\n\nexport function renderDailyMetricsAggregate(opts: {\n filesList: string;\n site: string;\n fromDate: string;\n toDate: string;\n sessionization: SessionizationMode;\n excludedMonths?: string[];\n}): string {\n const site = escapeSql(opts.site);\n const fromDate = escapeSql(opts.fromDate);\n const toDate = escapeSql(opts.toDate);\n const baseCte = renderBaseCte(opts.filesList, site);\n\n let excludedMonthsOwning = '';\n let excludedMonthsPv = '';\n if (opts.excludedMonths && opts.excludedMonths.length > 0) {\n const list = opts.excludedMonths.map((m) => `'${escapeSql(m)}'`).join(', ');\n excludedMonthsOwning = ` AND substr(s.owning_date, 1, 7) NOT IN (${list})`;\n excludedMonthsPv = ` AND strftime(epoch_ms(timestamp), '%Y-%m') NOT IN (${list})`;\n }\n\n const sessionCte = renderSessionCte(opts.sessionization, fromDate, toDate, excludedMonthsOwning);\n\n return `\n${baseCte}\n${sessionCte}\n, daily_pv AS (\n SELECT\n strftime(epoch_ms(timestamp), '%Y-%m-%d') AS date,\n count(*)::BIGINT AS pageviews\n FROM raw_events\n WHERE event = 'pageview'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'${excludedMonthsPv}\n GROUP BY 1\n)\n, session_summary AS (\n SELECT\n session_id,\n owning_date,\n count(CASE WHEN event = 'pageview' THEN 1 END) AS pv_count\n FROM bounded_sessions\n GROUP BY session_id, owning_date\n)\n, daily_visitors AS (\n SELECT\n owning_date AS date,\n count(DISTINCT entity_id)::BIGINT AS visitors\n FROM bounded_sessions\n GROUP BY owning_date\n)\n, daily_sessions AS (\n SELECT\n s.owning_date AS date,\n v.visitors,\n count(DISTINCT s.session_id)::BIGINT AS sessions,\n sum(CASE WHEN s.pv_count = 1 THEN 1 ELSE 0 END)::BIGINT AS bounce_sessions,\n sum(CASE WHEN s.pv_count > 0 THEN 1 ELSE 0 END)::BIGINT AS pv_sessions\n FROM session_summary s\n JOIN daily_visitors v ON s.owning_date = v.date\n GROUP BY s.owning_date, v.visitors\n)\nSELECT\n coalesce(p.date, s.date) AS date,\n coalesce(p.pageviews, 0)::BIGINT AS pageviews,\n coalesce(s.visitors, 0)::BIGINT AS visitors,\n coalesce(s.sessions, 0)::BIGINT AS sessions,\n coalesce(s.bounce_sessions, 0)::BIGINT AS bounce_sessions,\n coalesce(s.pv_sessions, 0)::BIGINT AS pv_sessions\nFROM daily_pv p\nFULL OUTER JOIN daily_sessions s ON p.date = s.date\n`;\n}\n\nexport function renderDailyDimsAggregate(opts: {\n filesList: string;\n site: string;\n fromDate: string;\n toDate: string;\n month?: string;\n excludedMonths?: string[];\n}): string {\n const site = escapeSql(opts.site);\n const fromDate = escapeSql(opts.fromDate);\n const toDate = escapeSql(opts.toDate);\n const month = escapeSql(opts.month || opts.fromDate.slice(0, 7));\n const baseCte = renderBaseCte(opts.filesList, site);\n\n let excludedMonthsSql = '';\n if (opts.excludedMonths && opts.excludedMonths.length > 0) {\n const list = opts.excludedMonths.map((m) => `'${escapeSql(m)}'`).join(', ');\n excludedMonthsSql = ` AND strftime(epoch_ms(timestamp), '%Y-%m') NOT IN (${list})`;\n }\n\n return `\n${baseCte}\n, unranked_dims AS (\n SELECT\n '${month}' AS month,\n 'path' AS kind,\n path AS value,\n count(*)::BIGINT AS pageviews,\n count(DISTINCT CASE WHEN visitor IS NOT NULL AND visitor != '' THEN visitor END)::BIGINT AS visitors\n FROM raw_events\n WHERE event = 'pageview'\n AND path IS NOT NULL AND path != ''\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'${excludedMonthsSql}\n GROUP BY path\n\n UNION ALL\n\n SELECT\n '${month}' AS month,\n 'referrer' AS kind,\n referrer_host AS value,\n count(*)::BIGINT AS pageviews,\n count(DISTINCT CASE WHEN visitor IS NOT NULL AND visitor != '' THEN visitor END)::BIGINT AS visitors\n FROM raw_events\n WHERE event = 'pageview'\n AND referrer_host IS NOT NULL AND referrer_host != ''\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'${excludedMonthsSql}\n GROUP BY referrer_host\n\n UNION ALL\n\n SELECT\n '${month}' AS month,\n 'country' AS kind,\n country AS value,\n count(*)::BIGINT AS pageviews,\n count(DISTINCT CASE WHEN visitor IS NOT NULL AND visitor != '' THEN visitor END)::BIGINT AS visitors\n FROM raw_events\n WHERE event = 'pageview'\n AND country IS NOT NULL AND country != ''\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'${excludedMonthsSql}\n GROUP BY country\n\n UNION ALL\n\n SELECT\n '${month}' AS month,\n 'event' AS kind,\n event AS value,\n count(*)::BIGINT AS pageviews,\n count(DISTINCT CASE WHEN visitor IS NOT NULL AND visitor != '' THEN visitor END)::BIGINT AS visitors\n FROM raw_events\n WHERE event != 'pageview'\n AND event != 'vitals'\n AND event IS NOT NULL AND event != ''\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'${excludedMonthsSql}\n GROUP BY event\n)\n, ranked_dims AS (\n SELECT\n month,\n kind,\n value,\n pageviews,\n visitors,\n row_number() OVER (PARTITION BY kind ORDER BY pageviews DESC, value ASC) AS rn\n FROM unranked_dims\n)\nSELECT\n month,\n kind,\n CASE WHEN rn <= 1000 THEN value ELSE '(other)' END AS value,\n sum(pageviews)::BIGINT AS pageviews,\n sum(visitors)::BIGINT AS visitors\nFROM ranked_dims\nGROUP BY month, kind, CASE WHEN rn <= 1000 THEN value ELSE '(other)' END\n`;\n}\n\nexport function renderDailyVitalsHistogram(opts: {\n filesList: string;\n site: string;\n fromDate: string;\n toDate: string;\n month?: string;\n excludedMonths?: string[];\n}): string {\n const site = escapeSql(opts.site);\n const fromDate = escapeSql(opts.fromDate);\n const toDate = escapeSql(opts.toDate);\n const month = escapeSql(opts.month || opts.fromDate.slice(0, 7));\n const baseCte = renderBaseCte(opts.filesList, site);\n\n let excludedMonthsSql = '';\n if (opts.excludedMonths && opts.excludedMonths.length > 0) {\n const list = opts.excludedMonths.map((m) => `'${escapeSql(m)}'`).join(', ');\n excludedMonthsSql = ` AND strftime(epoch_ms(timestamp), '%Y-%m') NOT IN (${list})`;\n }\n\n const metrics = ['lcp', 'inp', 'cls', 'fcp', 'ttfb'] as const;\n const unions = metrics\n .map(\n (m) => `\n SELECT\n '${month}' AS month,\n '${m}' AS metric,\n ${vitalsBucketIndexExpr(m, m)}::INTEGER AS bucket_idx,\n count(*)::BIGINT AS samples\n FROM raw_events\n WHERE event = 'vitals'\n AND ${m} IS NOT NULL\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'${excludedMonthsSql}\n GROUP BY 1, 2, 3\n`,\n )\n .join('\\nUNION ALL\\n');\n\n return `\n${baseCte}\n${unions}\n`;\n}\n\nfunction renderVitalsBoundsTable(): string {\n const rows: string[] = [];\n const metrics = ['lcp', 'inp', 'cls', 'fcp', 'ttfb'] as const;\n for (const m of metrics) {\n const edges = VITALS_HISTOGRAM_EDGES[m];\n rows.push(`('${m}', 0, 0.0, ${edges[0]})`);\n for (let i = 1; i < edges.length; i++) {\n rows.push(`('${m}', ${i}, ${edges[i - 1]}, ${edges[i]})`);\n }\n const lastEdge = edges[edges.length - 1];\n rows.push(`('${m}', ${edges.length}, ${lastEdge}, ${lastEdge * 1.5})`);\n }\n return rows.join(',\\n ');\n}\n\nexport function renderDuckDbSql(\n name: DeepQueryName,\n params: ValidatedDeepParams,\n files: string[],\n rollupFiles: string[] = [],\n): string {\n const site = escapeSql(params.siteId);\n const fromDate = escapeSql(params.fromDate);\n const toDate = escapeSql(params.toDate);\n\n if (files.length === 0 && rollupFiles.length === 0) {\n switch (name) {\n case 'allTimeSummary':\n case 'longRangeSummary':\n return 'SELECT 0::BIGINT AS pageviews, 0::BIGINT AS visitors, 0::BIGINT AS sessions, 0.0::DOUBLE AS bounceRate;';\n case 'allTimeTimeseries':\n case 'longRangeTimeseries':\n return \"SELECT '' AS bucket, 0::BIGINT AS pageviews, 0::BIGINT AS visitors, 0::BIGINT AS sessions WHERE 1=0;\";\n case 'allTimeTopPages':\n case 'longRangeTopPages':\n return \"SELECT '' AS path, 0::BIGINT AS pageviews, 0::BIGINT AS visitors WHERE 1=0;\";\n case 'allTimeReferrers':\n case 'longRangeReferrers':\n return \"SELECT '' AS referrer, 0::BIGINT AS pageviews, 0::BIGINT AS visitors WHERE 1=0;\";\n case 'allTimeCountries':\n case 'longRangeCountries':\n return \"SELECT '' AS country, 0::BIGINT AS pageviews, 0::BIGINT AS visitors WHERE 1=0;\";\n case 'allTimeCustomEvents':\n case 'longRangeCustomEvents':\n return \"SELECT '' AS event, 0::BIGINT AS count WHERE 1=0;\";\n case 'allTimeVitalsPercentiles':\n case 'vitalsPercentiles':\n return \"SELECT '' AS metric, 0.0::DOUBLE AS p75, 0::BIGINT AS samples WHERE 1=0;\";\n case 'funnel':\n return \"SELECT 0::INTEGER AS step, '' AS name, 0::BIGINT AS count, 0::BIGINT AS dropOff, 0.0::DOUBLE AS conversionRate WHERE 1=0;\";\n case 'funnelBreakdown':\n return \"SELECT '' AS value, 0::BIGINT AS people, 0::BIGINT AS converted, 0.0::DOUBLE AS conversionRate WHERE 1=0;\";\n case 'entryExitPaths':\n return \"SELECT '' AS path, 0::BIGINT AS entries, 0::BIGINT AS exits, 0::BIGINT AS bounces WHERE 1=0;\";\n case 'pathTransitions':\n return \"SELECT '' AS fromPath, '' AS toPath, 0::BIGINT AS count WHERE 1=0;\";\n case 'retentionCohorts':\n return \"SELECT '' AS cohort, 0::BIGINT AS period, 0::BIGINT AS visitors, 0::BIGINT AS retained, 0.0::DOUBLE AS percentage WHERE 1=0;\";\n case 'visitorList':\n return \"SELECT '' AS visitor, 0::BIGINT AS first_seen, 0::BIGINT AS last_seen, 0::BIGINT AS events, 0::BIGINT AS sessions, '' AS country, '' AS region, '' AS city, '' AS browser, '' AS os, '' AS device, '' AS referrer_host, 0::BIGINT AS excluded_events WHERE 1=0;\";\n case 'visitorEvents':\n return \"SELECT '' AS event, '' AS path, '' AS referrer_host, '' AS utm_source, '' AS utm_medium, '' AS utm_campaign, '' AS utm_content, '' AS utm_term, '' AS country, '' AS region, '' AS city, '' AS browser, '' AS os, '' AS device, '{}' AS props, '' AS session, '' AS visitor, 0::BIGINT AS timestamp WHERE 1=0;\";\n default:\n throw new Error(`Unknown query: ${name}`);\n }\n }\n\n const filesList = files.map((f) => `'${escapeSql(f)}'`).join(', ');\n const rolledUpMonths = Array.from(\n new Set(\n rollupFiles.map((f) => f.match(/month=([^/]+)/)?.[1]).filter((m): m is string => Boolean(m)),\n ),\n );\n\n const filterClauses = buildDuckDbFilterPredicates(params.filters);\n const filterSql = filterClauses.length > 0 ? `\\n AND ${filterClauses.join('\\n AND ')}` : '';\n\n const baseCte = renderBaseCte(filesList, site, filterSql);\n const sessionCte = renderSessionCte(params.sessionization, fromDate, toDate);\n\n switch (name) {\n case 'funnel': {\n const windowMs = params.windowMinutes * 60 * 1000;\n const steps = params.steps;\n const stepCtes: string[] = [];\n\n stepCtes.push(`\n, step_1_hits AS (\n SELECT\n session_id,\n min(timestamp) AS t1\n FROM bounded_sessions\n WHERE ${renderStepCondition(steps[0], 'Step 1')}\n GROUP BY session_id\n)\n`);\n\n for (let i = 1; i < steps.length; i++) {\n const prevIdx = i;\n const currIdx = i + 1;\n stepCtes.push(`\n, step_${currIdx}_hits AS (\n SELECT\n b.session_id,\n h${prevIdx}.t1,\n min(b.timestamp) AS t${currIdx}\n FROM bounded_sessions b\n JOIN step_${prevIdx}_hits h${prevIdx} ON b.session_id = h${prevIdx}.session_id\n WHERE ${renderStepCondition(steps[i], `Step ${currIdx}`)}\n AND b.timestamp >= h${prevIdx}.t${prevIdx}\n AND (b.timestamp - h${prevIdx}.t1) <= ${windowMs}\n GROUP BY b.session_id, h${prevIdx}.t1\n)\n`);\n }\n\n const countCols = steps\n .map((_, i) => `(SELECT count(DISTINCT session_id) FROM step_${i + 1}_hits) AS c${i + 1}`)\n .join(', ');\n\n const stepUnions = steps.map((s, i) => {\n const idx = i + 1;\n const stepName = escapeSql(s.name || s.path || s.event || `Step ${idx}`);\n if (idx === 1) {\n return `SELECT 1 AS step, '${stepName}' AS name, c1::BIGINT AS count, 0::BIGINT AS dropOff, CASE WHEN c1 > 0 THEN 100.0::DOUBLE ELSE 0.0::DOUBLE END AS conversionRate FROM counts`;\n }\n return `SELECT ${idx} AS step, '${stepName}' AS name, c${idx}::BIGINT AS count, (c${idx - 1} - c${idx})::BIGINT AS dropOff, CASE WHEN c1 > 0 THEN round(c${idx} * 100.0 / c1, 2)::DOUBLE ELSE 0.0::DOUBLE END AS conversionRate FROM counts`;\n });\n\n return `\n${baseCte}\n${sessionCte}\n${stepCtes.join('')}\n, counts AS (\n SELECT ${countCols}\n)\n${stepUnions.join('\\nUNION ALL\\n')}\nORDER BY step ASC;\n`;\n }\n\n case 'funnelBreakdown': {\n const windowMs = params.windowMinutes * 60 * 1000;\n const steps = params.steps;\n const stepCtes: string[] = [];\n\n stepCtes.push(`\n, step_1_hits AS (\n SELECT\n session_id,\n min(timestamp) AS t1\n FROM bounded_sessions\n WHERE ${renderStepCondition(steps[0], 'Step 1')}\n GROUP BY session_id\n)\n`);\n\n for (let i = 1; i < steps.length; i++) {\n const prevIdx = i;\n const currIdx = i + 1;\n stepCtes.push(`\n, step_${currIdx}_hits AS (\n SELECT\n b.session_id,\n h${prevIdx}.t1,\n min(b.timestamp) AS t${currIdx}\n FROM bounded_sessions b\n JOIN step_${prevIdx}_hits h${prevIdx} ON b.session_id = h${prevIdx}.session_id\n WHERE ${renderStepCondition(steps[i], `Step ${currIdx}`)}\n AND b.timestamp >= h${prevIdx}.t${prevIdx}\n AND (b.timestamp - h${prevIdx}.t1) <= ${windowMs}\n GROUP BY b.session_id, h${prevIdx}.t1\n)\n`);\n }\n\n const dimCol = params.dimension === 'country' ? 'country' : 'referrer_host';\n const defaultVal = params.dimension === 'country' ? 'Unknown' : 'Direct';\n const lastIdx = steps.length;\n\n return `\n${baseCte}\n${sessionCte}\n${stepCtes.join('')}\n, first_touch AS (\n SELECT\n session_id,\n coalesce(nullif(${dimCol}, ''), '${defaultVal}') AS value,\n row_number() OVER (PARTITION BY session_id ORDER BY timestamp ASC, date ASC, seq ASC) AS rn\n FROM bounded_sessions\n)\n, session_values AS (\n SELECT session_id, value\n FROM first_touch\n WHERE rn = 1\n)\nSELECT\n v.value AS value,\n count(DISTINCT s1.session_id)::BIGINT AS people,\n count(DISTINCT s${lastIdx}.session_id)::BIGINT AS converted,\n CASE\n WHEN count(DISTINCT s1.session_id) > 0 THEN\n round(count(DISTINCT s${lastIdx}.session_id) * 100.0 / count(DISTINCT s1.session_id), 2)::DOUBLE\n ELSE 0.0::DOUBLE\n END AS conversionRate\nFROM step_1_hits s1\nJOIN session_values v ON s1.session_id = v.session_id\nLEFT JOIN step_${lastIdx}_hits s${lastIdx} ON s1.session_id = s${lastIdx}.session_id\nGROUP BY v.value\nORDER BY people DESC, value ASC\nLIMIT ${params.limit};\n`;\n }\n\n case 'entryExitPaths': {\n return `\n${baseCte}\n${sessionCte}\n, session_pageviews AS (\n SELECT\n session_id,\n path,\n timestamp,\n date,\n seq,\n row_number() OVER (PARTITION BY session_id ORDER BY timestamp ASC, date ASC, seq ASC) AS rn_asc,\n row_number() OVER (PARTITION BY session_id ORDER BY timestamp DESC, date DESC, seq DESC) AS rn_desc,\n count(*) OVER (PARTITION BY session_id) AS total_pv\n FROM bounded_sessions\n WHERE event = 'pageview'\n)\n, session_entry_exit AS (\n SELECT\n session_id,\n max(CASE WHEN rn_asc = 1 THEN path END) AS entry_path,\n max(CASE WHEN rn_desc = 1 THEN path END) AS exit_path,\n max(CASE WHEN total_pv = 1 THEN 1 ELSE 0 END) AS is_bounce\n FROM session_pageviews\n GROUP BY session_id\n)\n, entry_counts AS (\n SELECT entry_path AS path, count(*)::BIGINT AS entries, sum(is_bounce)::BIGINT AS bounces\n FROM session_entry_exit\n GROUP BY entry_path\n)\n, exit_counts AS (\n SELECT exit_path AS path, count(*)::BIGINT AS exits\n FROM session_entry_exit\n GROUP BY exit_path\n)\nSELECT\n coalesce(en.path, ex.path) AS path,\n coalesce(en.entries, 0)::BIGINT AS entries,\n coalesce(ex.exits, 0)::BIGINT AS exits,\n coalesce(en.bounces, 0)::BIGINT AS bounces\nFROM entry_counts en\nFULL OUTER JOIN exit_counts ex ON en.path = ex.path\nORDER BY entries DESC, path ASC\nLIMIT ${params.limit};\n`;\n }\n\n case 'pathTransitions': {\n return `\n${baseCte}\n${sessionCte}\n, session_pageviews AS (\n SELECT\n session_id,\n path,\n lead(path) OVER (\n PARTITION BY session_id\n ORDER BY timestamp ASC, date ASC, seq ASC\n ) AS next_path\n FROM bounded_sessions\n WHERE event = 'pageview'\n)\nSELECT\n path AS fromPath,\n next_path AS toPath,\n count(*)::BIGINT AS count\nFROM session_pageviews\nWHERE next_path IS NOT NULL\nGROUP BY fromPath, toPath\nORDER BY count DESC, fromPath ASC, toPath ASC\nLIMIT ${params.limit};\n`;\n }\n\n case 'retentionCohorts': {\n return `\n${baseCte}\n${sessionCte}\n, visitor_first_seen AS (\n SELECT\n entity_id,\n min(session_min_ts) AS first_ts,\n strftime(epoch_ms(min(session_min_ts)), '%Y-%m-%d') AS cohort_day\n FROM bounded_sessions\n GROUP BY entity_id\n)\n, visitor_activities AS (\n SELECT DISTINCT\n b.entity_id,\n v.cohort_day,\n datediff('day', epoch_ms(v.first_ts), epoch_ms(b.timestamp)) AS period\n FROM bounded_sessions b\n JOIN visitor_first_seen v ON b.entity_id = v.entity_id\n)\n, cohort_sizes AS (\n SELECT\n cohort_day AS cohort,\n count(DISTINCT entity_id)::BIGINT AS visitors\n FROM visitor_first_seen\n GROUP BY cohort_day\n)\nSELECT\n a.cohort_day AS cohort,\n a.period::BIGINT AS period,\n s.visitors::BIGINT AS visitors,\n count(DISTINCT a.entity_id)::BIGINT AS retained,\n round(count(DISTINCT a.entity_id) * 100.0 / s.visitors, 2)::DOUBLE AS percentage\nFROM visitor_activities a\nJOIN cohort_sizes s ON a.cohort_day = s.cohort\nGROUP BY a.cohort_day, a.period, s.visitors\nORDER BY cohort ASC, period ASC;\n`;\n }\n\n case 'longRangeTimeseries': {\n let bucketExpr = `strftime(epoch_ms(timestamp), '%Y-%m-%d')`;\n let sessionBucketExpr = `strftime(epoch_ms(session_min_ts), '%Y-%m-%d')`;\n if (params.interval === 'hour') {\n bucketExpr = `strftime(epoch_ms(timestamp), '%Y-%m-%dT%H:00:00Z')`;\n sessionBucketExpr = `strftime(epoch_ms(session_min_ts), '%Y-%m-%dT%H:00:00Z')`;\n } else if (params.interval === 'week') {\n bucketExpr = `strftime(date_trunc('week', epoch_ms(timestamp)), '%Y-%m-%d')`;\n sessionBucketExpr = `strftime(date_trunc('week', epoch_ms(session_min_ts)), '%Y-%m-%d')`;\n } else if (params.interval === 'month') {\n bucketExpr = `strftime(epoch_ms(timestamp), '%Y-%m')`;\n sessionBucketExpr = `strftime(epoch_ms(session_min_ts), '%Y-%m')`;\n }\n\n // pageviews comes from raw_events (includes aggregate_only, matching longRangeSummary); visitors/sessions need an identity, so those come from bounded_sessions.\n return `\n${baseCte}\n${sessionCte}\n, pv_buckets AS (\n SELECT\n ${bucketExpr} AS bucket,\n count(*)::BIGINT AS pageviews\n FROM raw_events\n WHERE event = 'pageview'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'\n GROUP BY bucket\n)\n, activity_buckets AS (\n SELECT\n ${sessionBucketExpr} AS bucket,\n count(DISTINCT CASE WHEN visitor IS NOT NULL AND visitor != '' THEN visitor END)::BIGINT AS visitors,\n count(DISTINCT session_id)::BIGINT AS sessions\n FROM bounded_sessions\n GROUP BY bucket\n)\nSELECT\n coalesce(p.bucket, a.bucket) AS bucket,\n coalesce(p.pageviews, 0)::BIGINT AS pageviews,\n coalesce(a.visitors, 0)::BIGINT AS visitors,\n coalesce(a.sessions, 0)::BIGINT AS sessions\nFROM pv_buckets p\nFULL OUTER JOIN activity_buckets a ON p.bucket = a.bucket\nORDER BY bucket ASC;\n`;\n }\n\n case 'longRangeTopPages': {\n return `\n${baseCte}\nSELECT\n path,\n count(*)::BIGINT AS pageviews,\n count(DISTINCT CASE WHEN visitor IS NOT NULL AND visitor != '' THEN visitor END)::BIGINT AS visitors\nFROM raw_events\nWHERE event = 'pageview'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'\nGROUP BY path\nORDER BY pageviews DESC, path ASC\nLIMIT ${params.limit};\n`;\n }\n\n case 'longRangeReferrers': {\n return `\n${baseCte}\nSELECT\n referrer_host AS referrer,\n count(*)::BIGINT AS pageviews,\n count(DISTINCT CASE WHEN visitor IS NOT NULL AND visitor != '' THEN visitor END)::BIGINT AS visitors\nFROM raw_events\nWHERE event = 'pageview'\n AND referrer_host IS NOT NULL AND referrer_host != ''\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'\nGROUP BY referrer\nORDER BY pageviews DESC, referrer ASC\nLIMIT ${params.limit};\n`;\n }\n\n case 'longRangeCountries': {\n return `\n${baseCte}\nSELECT\n country,\n count(*)::BIGINT AS pageviews,\n count(DISTINCT CASE WHEN visitor IS NOT NULL AND visitor != '' THEN visitor END)::BIGINT AS visitors\nFROM raw_events\nWHERE event = 'pageview'\n AND country IS NOT NULL AND country != ''\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'\nGROUP BY country\nORDER BY pageviews DESC, country ASC\nLIMIT ${params.limit};\n`;\n }\n\n case 'longRangeCustomEvents': {\n return `\n${baseCte}\nSELECT\n event,\n count(*)::BIGINT AS count\nFROM raw_events\nWHERE event != 'pageview'\n AND event != 'vitals'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'\nGROUP BY event\nORDER BY count DESC, event ASC\nLIMIT ${params.limit};\n`;\n }\n\n case 'longRangeSummary': {\n return `\n${baseCte}\n${sessionCte}\n, pv_counts AS (\n SELECT count(*)::BIGINT AS pageviews\n FROM raw_events\n WHERE event = 'pageview'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'\n)\n, session_stats AS (\n SELECT\n count(DISTINCT entity_id)::BIGINT AS visitors,\n count(DISTINCT session_id)::BIGINT AS sessions\n FROM bounded_sessions\n)\n, bounce_stats AS (\n SELECT\n count(*)::BIGINT AS total_sessions,\n sum(CASE WHEN pv_per_session = 1 THEN 1 ELSE 0 END)::BIGINT AS bounce_sessions\n FROM (\n SELECT session_id, count(*) AS pv_per_session\n FROM bounded_sessions\n WHERE event = 'pageview'\n GROUP BY session_id\n )\n)\nSELECT\n coalesce(p.pageviews, 0)::BIGINT AS pageviews,\n coalesce(s.visitors, 0)::BIGINT AS visitors,\n coalesce(s.sessions, 0)::BIGINT AS sessions,\n CASE\n WHEN coalesce(b.total_sessions, 0) > 0 THEN round(coalesce(b.bounce_sessions, 0) * 100.0 / b.total_sessions, 2)::DOUBLE\n ELSE 0.0::DOUBLE\n END AS bounceRate\nFROM pv_counts p\nCROSS JOIN session_stats s\nCROSS JOIN bounce_stats b;\n`;\n }\n\n case 'vitalsPercentiles': {\n return `\n${baseCte}\n, vitals_data AS (\n SELECT\n quantile_cont(lcp, 0.75) AS lcp_p75,\n count(lcp) AS lcp_samples,\n quantile_cont(inp, 0.75) AS inp_p75,\n count(inp) AS inp_samples,\n quantile_cont(cls, 0.75) AS cls_p75,\n count(cls) AS cls_samples,\n quantile_cont(fcp, 0.75) AS fcp_p75,\n count(fcp) AS fcp_samples,\n quantile_cont(ttfb, 0.75) AS ttfb_p75,\n count(ttfb) AS ttfb_samples\n FROM raw_events\n WHERE event = 'vitals'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') >= '${fromDate}'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') <= '${toDate}'\n)\nSELECT 'lcp' AS metric, coalesce(lcp_p75, 0.0)::DOUBLE AS p75, coalesce(lcp_samples, 0)::BIGINT AS samples FROM vitals_data\nUNION ALL\nSELECT 'inp' AS metric, coalesce(inp_p75, 0.0)::DOUBLE AS p75, coalesce(inp_samples, 0)::BIGINT AS samples FROM vitals_data\nUNION ALL\nSELECT 'cls' AS metric, coalesce(cls_p75, 0.0)::DOUBLE AS p75, coalesce(cls_samples, 0)::BIGINT AS samples FROM vitals_data\nUNION ALL\nSELECT 'fcp' AS metric, coalesce(fcp_p75, 0.0)::DOUBLE AS p75, coalesce(fcp_samples, 0)::BIGINT AS samples FROM vitals_data\nUNION ALL\nSELECT 'ttfb' AS metric, coalesce(ttfb_p75, 0.0)::DOUBLE AS p75, coalesce(ttfb_samples, 0)::BIGINT AS samples FROM vitals_data;\n`;\n }\n\n case 'visitorList': {\n const havingClauses: string[] = [];\n if (params.before !== undefined && !Number.isNaN(params.before)) {\n havingClauses.push(`max(timestamp) < ${params.before}`);\n }\n if (params.filters) {\n for (const f of params.filters) {\n if (f.dim === 'first_seen' || f.dim === 'last_activity' || f.dim === 'last_seen') {\n const tr = getTimeRangeForFilterValue(f.value);\n if (tr) {\n const expr = f.dim === 'first_seen' ? 'min(timestamp)' : 'max(timestamp)';\n if (f.op === 'is') {\n havingClauses.push(`${expr} >= ${tr.startMs} AND ${expr} <= ${tr.endMs}`);\n } else {\n havingClauses.push(`(${expr} < ${tr.startMs} OR ${expr} > ${tr.endMs})`);\n }\n }\n }\n }\n }\n const havingClause =\n havingClauses.length > 0 ? `\\nHAVING ${havingClauses.join(' AND ')}` : '';\n return `\n${baseCte}${sessionCte}\nSELECT\n visitor,\n min(timestamp)::BIGINT AS first_seen,\n max(timestamp)::BIGINT AS last_seen,\n count(*)::BIGINT AS events,\n count(DISTINCT session_id)::BIGINT AS sessions,\n max(country) AS country,\n max(region) AS region,\n max(city) AS city,\n max(browser) AS browser,\n max(os) AS os,\n max(device) AS device,\n max(referrer_host) AS referrer_host,\n (SELECT count(*)::BIGINT FROM raw_events WHERE (aggregate_only = true OR visitor IS NULL OR visitor = '')) AS excluded_events\nFROM bounded_sessions\nWHERE visitor IS NOT NULL AND visitor != '' AND event != 'vitals'\nGROUP BY visitor${havingClause}\nORDER BY last_seen DESC\nLIMIT ${params.limit};\n`;\n }\n\n case 'visitorEvents': {\n if (!params.visitor || !isVisitorHash(params.visitor)) {\n throw new Error('invalid_visitor');\n }\n const visitor = escapeSql(params.visitor);\n return `\n${baseCte}\nSELECT\n event,\n path,\n referrer_host,\n utm_source,\n utm_medium,\n utm_campaign,\n utm_content,\n utm_term,\n country,\n region,\n city,\n browser,\n os,\n device,\n props,\n session,\n visitor,\n timestamp::BIGINT AS timestamp\nFROM raw_events\nWHERE (aggregate_only IS NULL OR aggregate_only = false)\n AND visitor = '${visitor}'\n AND event != 'vitals'\n AND strftime(epoch_ms(timestamp), '%Y-%m-%d') = '${fromDate}'\nORDER BY timestamp ASC\nLIMIT ${params.limit};\n`;\n }\n\n case 'allTimeSummary': {\n const metricsFiles = rollupFiles.filter((f) => f.endsWith('metrics.parquet'));\n const rollupFilesList = metricsFiles.map((f) => `'${escapeSql(f)}'`).join(', ');\n\n const rollupSql =\n metricsFiles.length > 0\n ? `SELECT date, pageviews, visitors, sessions, bounce_sessions, pv_sessions FROM read_parquet([${rollupFilesList}])`\n : `SELECT '' AS date, 0::BIGINT AS pageviews, 0::BIGINT AS visitors, 0::BIGINT AS sessions, 0::BIGINT AS bounce_sessions, 0::BIGINT AS pv_sessions WHERE 1=0`;\n\n const tailSql =\n files.length > 0\n ? renderDailyMetricsAggregate({\n filesList,\n site,\n fromDate,\n toDate,\n sessionization: 'strict',\n excludedMonths: rolledUpMonths,\n })\n : `SELECT '' AS date, 0::BIGINT AS pageviews, 0::BIGINT AS visitors, 0::BIGINT AS sessions, 0::BIGINT AS bounce_sessions, 0::BIGINT AS pv_sessions WHERE 1=0`;\n\n return `\nWITH rollup_part AS (\n ${rollupSql}\n)\n, tail_part AS (\n ${tailSql}\n)\n, unified AS (\n SELECT * FROM rollup_part\n UNION ALL\n SELECT * FROM tail_part\n)\nSELECT\n coalesce(sum(pageviews), 0)::BIGINT AS pageviews,\n coalesce(sum(visitors), 0)::BIGINT AS visitors,\n coalesce(sum(sessions), 0)::BIGINT AS sessions,\n CASE\n WHEN coalesce(sum(pv_sessions), 0) > 0 THEN round(coalesce(sum(bounce_sessions), 0) * 100.0 / sum(pv_sessions), 2)::DOUBLE\n ELSE 0.0::DOUBLE\n END AS bounceRate\nFROM unified;\n`;\n }\n\n case 'allTimeTimeseries': {\n const metricsFiles = rollupFiles.filter((f) => f.endsWith('metrics.parquet'));\n const rollupFilesList = metricsFiles.map((f) => `'${escapeSql(f)}'`).join(', ');\n\n const rollupSql =\n metricsFiles.length > 0\n ? `SELECT date, pageviews, visitors, sessions, bounce_sessions, pv_sessions FROM read_parquet([${rollupFilesList}])`\n : `SELECT '' AS date, 0::BIGINT AS pageviews, 0::BIGINT AS visitors, 0::BIGINT AS sessions, 0::BIGINT AS bounce_sessions, 0::BIGINT AS pv_sessions WHERE 1=0`;\n\n const tailSql =\n files.length > 0\n ? renderDailyMetricsAggregate({\n filesList,\n site,\n fromDate,\n toDate,\n sessionization: 'strict',\n excludedMonths: rolledUpMonths,\n })\n : `SELECT '' AS date, 0::BIGINT AS pageviews, 0::BIGINT AS visitors, 0::BIGINT AS sessions, 0::BIGINT AS bounce_sessions, 0::BIGINT AS pv_sessions WHERE 1=0`;\n\n let bucketExpr = 'date';\n if (params.interval === 'week') {\n bucketExpr = \"strftime(date_trunc('week', cast(date as DATE)), '%Y-%m-%d')\";\n } else if (params.interval === 'month') {\n bucketExpr = 'substr(date, 1, 7)';\n }\n\n return `\nWITH rollup_part AS (\n ${rollupSql}\n)\n, tail_part AS (\n ${tailSql}\n)\n, unified AS (\n SELECT * FROM rollup_part\n UNION ALL\n SELECT * FROM tail_part\n)\nSELECT\n ${bucketExpr} AS bucket,\n coalesce(sum(pageviews), 0)::BIGINT AS pageviews,\n coalesce(sum(visitors), 0)::BIGINT AS visitors,\n coalesce(sum(sessions), 0)::BIGINT AS sessions\nFROM unified\nGROUP BY bucket\nORDER BY bucket ASC;\n`;\n }\n\n case 'allTimeTopPages':\n case 'allTimeReferrers':\n case 'allTimeCountries':\n case 'allTimeCustomEvents': {\n const dimsFiles = rollupFiles.filter((f) => f.endsWith('dims.parquet'));\n const rollupFilesList = dimsFiles.map((f) => `'${escapeSql(f)}'`).join(', ');\n\n const rollupSql =\n dimsFiles.length > 0\n ? `SELECT month, kind, value, pageviews, visitors FROM read_parquet([${rollupFilesList}])`\n : `SELECT '' AS month, '' AS kind, '' AS value, 0::BIGINT AS pageviews, 0::BIGINT AS visitors WHERE 1=0`;\n\n const tailSql =\n files.length > 0\n ? renderDailyDimsAggregate({\n filesList,\n site,\n fromDate,\n toDate,\n excludedMonths: rolledUpMonths,\n })\n : `SELECT '' AS month, '' AS kind, '' AS value, 0::BIGINT AS pageviews, 0::BIGINT AS visitors WHERE 1=0`;\n\n const kind =\n name === 'allTimeTopPages'\n ? 'path'\n : name === 'allTimeReferrers'\n ? 'referrer'\n : name === 'allTimeCountries'\n ? 'country'\n : 'event';\n\n const selectCols =\n name === 'allTimeCustomEvents'\n ? 'value AS event, coalesce(sum(pageviews), 0)::BIGINT AS count'\n : `${name === 'allTimeTopPages' ? 'value AS path' : name === 'allTimeReferrers' ? 'value AS referrer' : 'value AS country'}, coalesce(sum(pageviews), 0)::BIGINT AS pageviews, coalesce(sum(visitors), 0)::BIGINT AS visitors`;\n\n const orderBy =\n name === 'allTimeCustomEvents'\n ? 'count DESC, event ASC'\n : `pageviews DESC, ${name === 'allTimeTopPages' ? 'path' : name === 'allTimeReferrers' ? 'referrer' : 'country'} ASC`;\n\n return `\nWITH rollup_part AS (\n ${rollupSql}\n)\n, tail_part AS (\n ${tailSql}\n)\n, unified AS (\n SELECT * FROM rollup_part\n UNION ALL\n SELECT * FROM tail_part\n)\nSELECT\n ${selectCols}\nFROM unified\nWHERE kind = '${kind}' AND value != '(other)'\nGROUP BY value\nORDER BY ${orderBy}\nLIMIT ${params.limit};\n`;\n }\n\n case 'allTimeVitalsPercentiles': {\n const vitalsFiles = rollupFiles.filter((f) => f.endsWith('vitals.parquet'));\n const rollupFilesList = vitalsFiles.map((f) => `'${escapeSql(f)}'`).join(', ');\n\n const rollupSql =\n vitalsFiles.length > 0\n ? `SELECT metric, bucket_idx, samples FROM read_parquet([${rollupFilesList}])`\n : `SELECT '' AS metric, 0::INTEGER AS bucket_idx, 0::BIGINT AS samples WHERE 1=0`;\n\n const tailSql =\n files.length > 0\n ? renderDailyVitalsHistogram({\n filesList,\n site,\n fromDate,\n toDate,\n excludedMonths: rolledUpMonths,\n })\n : `SELECT '' AS month, '' AS metric, 0::INTEGER AS bucket_idx, 0::BIGINT AS samples WHERE 1=0`;\n\n return `\nWITH rollup_part AS (\n ${rollupSql}\n)\n, tail_part AS (\n SELECT metric, bucket_idx, samples FROM (\n ${tailSql}\n )\n)\n, unified AS (\n SELECT metric, bucket_idx, samples FROM rollup_part\n UNION ALL\n SELECT metric, bucket_idx, samples FROM tail_part\n)\n, bucket_totals AS (\n SELECT\n metric,\n bucket_idx,\n sum(samples)::BIGINT AS samples\n FROM unified\n WHERE metric IS NOT NULL AND metric != '' AND bucket_idx IS NOT NULL AND samples > 0\n GROUP BY metric, bucket_idx\n)\n, metric_totals AS (\n SELECT\n metric,\n sum(samples)::BIGINT AS total_samples\n FROM bucket_totals\n GROUP BY metric\n)\n, cum_buckets AS (\n SELECT\n b.metric,\n b.bucket_idx,\n b.samples,\n m.total_samples,\n sum(b.samples) OVER (\n PARTITION BY b.metric\n ORDER BY b.bucket_idx\n ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW\n ) AS cum_samples,\n coalesce(\n sum(b.samples) OVER (\n PARTITION BY b.metric\n ORDER BY b.bucket_idx\n ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING\n ),\n 0\n ) AS prev_cum\n FROM bucket_totals b\n JOIN metric_totals m ON b.metric = m.metric\n)\n, p75_buckets AS (\n SELECT\n metric,\n bucket_idx,\n samples,\n total_samples,\n prev_cum,\n cum_samples\n FROM cum_buckets\n WHERE prev_cum < 0.75 * total_samples AND cum_samples >= 0.75 * total_samples\n)\n, bounds_table(metric, bucket_idx, lower_bound, upper_bound) AS (\n VALUES\n ${renderVitalsBoundsTable()}\n)\n, calculated_p75 AS (\n SELECT\n p.metric,\n p.total_samples AS samples,\n CASE\n WHEN p.samples > 0 THEN\n bb.lower_bound + ((0.75 * p.total_samples - p.prev_cum)::DOUBLE / p.samples) * (bb.upper_bound - bb.lower_bound)\n ELSE 0.0\n END AS p75\n FROM p75_buckets p\n JOIN bounds_table bb ON p.metric = bb.metric AND p.bucket_idx = bb.bucket_idx\n)\n, all_metrics(metric) AS (\n VALUES ('lcp'), ('inp'), ('cls'), ('fcp'), ('ttfb')\n)\nSELECT\n m.metric,\n coalesce(round(c.p75, CASE WHEN m.metric = 'cls' THEN 4 ELSE 2 END), 0.0)::DOUBLE AS p75,\n coalesce(c.samples, 0)::BIGINT AS samples\nFROM all_metrics m\nLEFT JOIN calculated_p75 c ON m.metric = c.metric\nORDER BY CASE m.metric\n WHEN 'lcp' THEN 1\n WHEN 'inp' THEN 2\n WHEN 'cls' THEN 3\n WHEN 'fcp' THEN 4\n WHEN 'ttfb' THEN 5\n ELSE 6\nEND;\n`;\n }\n\n default:\n throw new Error(`Unknown query: ${name}`);\n }\n}\n","import type {\n DeepQueryParams,\n InsightsFilter,\n SessionizationMode,\n VisitorEventRow,\n VisitorJourney,\n VisitorListRow,\n} from '@407dev/events';\nimport type { SiteManifest } from './manifest.js';\nimport type { CommentAnchor, CommentFallback } from './preview-bridge.js';\n\nexport * from './manifest.js';\nexport * from './preview-bridge.js';\nexport * from './routes.js';\nexport type {\n FilterDimension,\n InsightsFilter,\n CountriesRow,\n CustomEventsRow,\n DimensionValuesRow,\n ChannelsRow,\n RealtimeRow,\n VitalsDistributionRow,\n InsightsQueryParams,\n InsightsQueryName,\n InsightsQueryTypes,\n ReferrersRow,\n SummaryRow,\n TimeseriesRow,\n TopPagesRow,\n DeepQueryName,\n SessionizationMode,\n FunnelStep,\n FunnelRow,\n FunnelBreakdownRow,\n EntryExitPathsRow,\n PathTransitionsRow,\n RetentionCohortsRow,\n LongRangeTimeseriesRow,\n LongRangeTopPagesRow,\n LongRangeReferrersRow,\n LongRangeCountriesRow,\n LongRangeCustomEventsRow,\n LongRangeSummaryRow,\n VitalsPercentilesRow,\n DeepQueryTypes,\n DeepQueryParams,\n ValidatedDeepParams,\n VitalsMetricName,\n VitalsRating,\n VitalsTimeseriesRow,\n VisitorListRow,\n VisitorEventRow,\n LiveEventRow,\n JourneyEvent,\n JourneySession,\n VisitorJourney,\n} from '@407dev/events';\nexport {\n PRIVACY_COLLECTION_MODEL,\n DEEP_QUERIES,\n ROLLUP_QUERIES,\n PARQUET_SCHEMA_VERSION,\n ROLLUP_SCHEMA_VERSION,\n MAX_RANGE_DAYS,\n HOT_TIER_RETENTION_DAYS,\n WEB_VITALS_THRESHOLDS,\n getVitalsRating,\n computeExperienceScore,\n isVisitorHash,\n buildVisitorJourney,\n LIVE_EVENTS_WINDOW_MS,\n LIVE_EVENTS_MAX_LIMIT,\n} from '@407dev/events';\nexport type { RollupQueryName } from '@407dev/events';\n\nexport interface InsightsCoverageInfo {\n earliest: string | null;\n latest: string | null;\n totalDays: number;\n totalBytes: number;\n completePartitions?: string[];\n rollupEarliestMonth?: string | null;\n rollupLatestMonth?: string | null;\n rollupToken?: string | null;\n}\n\nexport interface InsightsResult<T = unknown> {\n query: string;\n results: T[];\n backend: 'wae' | 'dev-mirror' | 'deep' | 'hybrid';\n sampled: boolean;\n coverage?: InsightsCoverageInfo;\n filtersApplied?: boolean;\n filterScope?: 'all' | 'hot-only' | 'partial';\n filterBoundaryDate?: string;\n excludedEvents?: number;\n excludedCount?: number;\n}\n\nexport interface InsightsDriftItem {\n day: string;\n hot_accepted: number;\n archive_accepted: number;\n archive_failed: number;\n bot_archived: number;\n}\n\nexport interface InsightsDriftResult {\n drift: number;\n hotAccepted: number;\n archiveAccepted: number;\n archiveFailed: number;\n botArchived: number;\n items: InsightsDriftItem[];\n}\n\nexport interface SearchConsoleConnection {\n id: string;\n workspace_id: string;\n site_id: string;\n property_url: string | null;\n property_type: 'site' | 'domain' | null;\n google_account_email: string | null;\n status: 'pending_property' | 'connected' | 'error' | 'revoked';\n last_error: string | null;\n last_synced_at: string | null;\n backfill_start_date: string | null;\n backfill_cursor_date: string | null;\n backfill_completed_at: string | null;\n created_by: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface SearchConsoleProperty {\n siteUrl: string;\n permissionLevel: string;\n propertyType: 'site' | 'domain';\n}\n\nexport interface SearchConsoleQueryRow {\n query: string;\n clicks: number;\n impressions: number;\n ctr: number;\n position: number;\n}\n\nexport interface SearchConsolePageRow {\n page: string;\n clicks: number;\n impressions: number;\n ctr: number;\n position: number;\n}\n\nexport interface MarketingConfigSummary {\n enabled: boolean;\n fromAddress?: string;\n replyTo?: string;\n physicalAddress?: string;\n publicUrl?: string;\n doubleOptIn: boolean;\n confirmedUrl?: string;\n unsubscribedUrl?: string;\n}\n\nexport interface SubscriberStats {\n active: number;\n pending: number;\n unsubscribed: number;\n suppressed: number;\n total: number;\n}\n\nexport interface MemberItem {\n id: string;\n email: string;\n name?: string | null;\n emailVerified: boolean;\n createdAt: number;\n lastSessionAt?: number | null;\n}\n\nexport interface MemberStats {\n total: number;\n verified: number;\n unverified: number;\n}\n\nexport interface SubscriberItem {\n id: string;\n email: string;\n status: 'pending' | 'active' | 'unsubscribed' | 'suppressed';\n source?: string | null;\n createdAt: number;\n consentedAt?: number | null;\n unsubscribedAt?: number | null;\n suppressedAt?: number | null;\n suppressionReason?: 'bounce' | 'complaint' | 'manual' | null;\n}\n\nexport interface DeployTokenRecord {\n id: string;\n site_id: string;\n name: string;\n token_prefix: string;\n created_by: string | null;\n created_at: string;\n last_used_at: string | null;\n revoked_at: string | null;\n}\n\nexport interface CreatedDeployToken extends DeployTokenRecord {\n token: string;\n}\n\nexport interface CampaignPreviewResult {\n html: string;\n text: string;\n warnings: string[];\n}\n\nexport interface SendRow {\n id: string;\n entryId?: string | null;\n entrySlug?: string | null;\n subject: string;\n preheader?: string | null;\n html: string;\n text: string;\n fromAddress: string;\n replyTo?: string | null;\n physicalAddress: string;\n status: 'queued' | 'sending' | 'complete' | 'failed';\n createdAt: number;\n createdBy?: string | null;\n startedAt?: number | null;\n completedAt?: number | null;\n total: number;\n sent: number;\n failed: number;\n skipped: number;\n unknown: number;\n}\n\nexport interface DispatchResult {\n dispatched: number;\n sent: number;\n failed: number;\n skipped: number;\n unknown: number;\n remaining: number;\n}\n\nexport interface SmsSubscriberItem {\n id: string;\n phoneE164: string;\n npa: string;\n tzId: string;\n status: 'pending' | 'active' | 'unsubscribed' | 'suppressed';\n source?: string | null;\n createdAt: number;\n consentedAt?: number | null;\n consentDisclosure?: string | null;\n unsubscribedAt?: number | null;\n suppressedAt?: number | null;\n suppressionReason?: 'bounce' | 'complaint' | 'manual' | null;\n stateOverride?: string | null;\n welcomeSentAt?: number | null;\n}\n\nexport interface SmsSubscriberStats {\n active: number;\n pending: number;\n unsubscribed: number;\n suppressed: number;\n total: number;\n}\n\nexport interface SmsPreviewResult {\n body: string;\n encoding: 'GSM-7' | 'UCS-2';\n chars: number;\n segments: number;\n warnings: string[];\n}\n\nexport interface SmsSendRow {\n id: string;\n entryId?: string | null;\n entrySlug?: string | null;\n body: string;\n brandName: string;\n status: 'queued' | 'sending' | 'complete' | 'failed';\n createdAt: number;\n createdBy?: string | null;\n startedAt?: number | null;\n completedAt?: number | null;\n total: number;\n sent: number;\n failed: number;\n skipped: number;\n unknown: number;\n held: number;\n}\n\nexport interface SmsConfigSummary {\n enabled: boolean;\n brandName?: string;\n programDescription?: string;\n messageFrequency?: string;\n publicUrl?: string;\n configured?: boolean;\n messagingServiceSid?: string | null;\n provisioningState?: string;\n provisioningError?: string | null;\n}\n\nexport interface SmsDispatchResult extends DispatchResult {\n held?: number;\n}\n\nexport interface ApiClientOptions {\n baseUrl: string;\n authToken?: string;\n workspaceId?: string;\n}\n\nexport interface ApiResponse<T = unknown> {\n data: T | null;\n ok: boolean;\n error?: string;\n errors?: unknown[];\n}\n\nexport type PreviewHostState = 'none' | 'provisioning' | 'ready' | 'failed';\n\nexport type PagesProjectStatus = 'found' | 'not_found' | 'unknown';\n\nexport interface PagesProjectStatusResponse {\n status: PagesProjectStatus;\n twin: PagesProjectStatus;\n error?: string;\n}\n\nexport interface SiteRecord {\n id: string;\n workspace_id: string;\n org_id?: string | null;\n project_id?: string | null;\n name: string;\n slug: string;\n domain?: string | null;\n custom_domain?: string | null;\n cf_pages_project?: string | null;\n cf_build_branch?: string;\n preview_url?: string | null;\n preview_url_override?: string | null;\n preview_pages_project?: string | null;\n preview_host_state?: PreviewHostState;\n preview_host_error?: string | null;\n published_site_version_id?: string | null;\n manifest_hash?: string | null;\n favicon_url?: string | null;\n screenshot_url?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface CliSessionResult {\n access_token: string;\n refresh_token: string;\n expires_at?: number;\n user?: {\n email?: string;\n };\n session?: {\n access_token: string;\n refresh_token: string;\n expires_at?: number;\n user?: {\n email?: string;\n };\n };\n}\n\nexport type SiteSupportRequestType = 'change_request' | 'bug_report';\n\nexport type ChangeRequestStatus =\n | 'draft'\n | 'sent'\n | 'in_progress'\n | 'needs_clarification'\n | 'done'\n | 'cancelled';\n\nexport type NotificationEventType =\n | 'change_request.sent'\n | 'change_request.replied'\n | 'proposal.approved'\n | 'proposal.signed'\n | 'invoice.paid'\n | 'invoice.payment_failed'\n | 'reminder.contract_pending'\n | 'reminder.proposal_pending'\n | 'reminder.invoice_due'\n | 'digest.monthly_analytics'\n | 'reminder.escalated';\n\nexport interface NotificationEmailPrefs {\n reminders: boolean;\n monthly_digest: boolean;\n}\n\nexport interface NotificationRecord {\n id: string;\n workspace_id: string;\n user_id: string;\n event_type: NotificationEventType;\n dedupe_key: string;\n site_id?: string | null;\n project_id?: string | null;\n subject_type: 'change_request' | 'proposal' | 'invoice' | 'site';\n subject_id: string;\n title: string;\n body?: string | null;\n href: string;\n read_at?: string | null;\n created_at: string;\n}\n\nexport interface NotificationChannel {\n id: string;\n workspace_id: string;\n kind: 'slack' | 'discord';\n name: string;\n webhook_url_preview?: string | null;\n enabled: boolean;\n event_types: NotificationEventType[];\n site_ids?: string[] | null;\n created_by?: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface ChangeRequestRecord {\n id: string;\n workspace_id: string;\n site_id: string;\n project_id?: string | null;\n type: SiteSupportRequestType;\n title?: string | null;\n description?: string | null;\n status: ChangeRequestStatus;\n created_by?: string | null;\n sent_at?: string | null;\n created_at: string;\n updated_at: string;\n linear_issue_id?: string | null;\n linear_identifier?: string | null;\n linear_url?: string | null;\n linear_sync_error?: string | null;\n comment_count?: number;\n sites?: {\n name?: string | null;\n slug?: string | null;\n domain?: string | null;\n } | null;\n}\n\nexport interface ChangeRequestReply {\n id: string;\n workspace_id: string;\n site_id: string;\n comment_id: string;\n body: string;\n created_by?: string | null;\n created_at: string;\n}\n\nexport interface ChangeRequestComment {\n id: string;\n workspace_id: string;\n site_id: string;\n change_request_id: string;\n seq: number;\n route_path: string;\n anchor?: CommentAnchor | null;\n fallback: CommentFallback;\n body: string;\n resolved_at?: string | null;\n created_by?: string | null;\n created_at: string;\n updated_at: string;\n replies?: ChangeRequestReply[];\n}\n\nexport type BuildStatus = 'queued' | 'building' | 'live' | 'failed';\n\nexport interface BuildRecord {\n id: string;\n workspace_id: string;\n site_id: string;\n status: BuildStatus;\n trigger_publish_event_ids: string[];\n cf_deployment_id?: string | null;\n started_at?: string | null;\n finished_at?: string | null;\n error?: string | null;\n log_url?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface FieldDefinitionRecord {\n id: string;\n workspace_id: string;\n site_id: string;\n key: string;\n type: string;\n constraints: Record<string, unknown>;\n default_value?: unknown;\n group_name?: string | null;\n source_path?: string | null;\n pushed_hash: string;\n orphaned_at?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface FieldValueRecord {\n id?: string;\n workspace_id: string;\n site_id: string;\n key: string;\n value?: unknown;\n updated_by?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface FieldWithDefinition extends FieldDefinitionRecord {\n value?: unknown;\n value_updated_at?: string;\n value_updated_by?: string | null;\n}\n\nexport interface CollectionRecord {\n id: string;\n workspace_id: string;\n site_id: string;\n key: string;\n json_schema: Record<string, unknown>;\n schema_version: number;\n orderable: boolean;\n route_pattern?: string | null;\n presentation: 'visual' | 'form';\n title_field?: string | null;\n list_columns?: string[] | null;\n body_field?: string | null;\n /** Manifest key is `group`; the column is `group_name`, matching field_definitions. */\n group_name?: string | null;\n pushed_hash: string;\n seeded_at?: string | null;\n orphaned_at?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface CollectionEntryStats {\n draft: number;\n published: number;\n scheduled: number;\n}\n\nexport interface StreamVideoRecord {\n uid: string;\n site_id: string;\n title?: string | null;\n state: 'queued' | 'inprogress' | 'ready' | 'error';\n pct_complete: number;\n duration_ms?: number | null;\n thumbnail?: string | null;\n ready_at?: string | null;\n error?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface ContentPerformanceRow {\n collectionKey: string;\n slug: string;\n title: string;\n path: string;\n status: string;\n /** null when no analytics row matched this path â not the same as zero views. */\n pageviews: number | null;\n visitors: number | null;\n}\n\nexport interface ContentPerformanceResult {\n results: ContentPerformanceRow[];\n backend: string;\n sampled: boolean;\n}\n\nexport interface CollectionSchemaVersionRecord {\n workspace_id: string;\n site_id: string;\n collection_key: string;\n schema_version: number;\n json_schema: Record<string, unknown>;\n created_at?: string;\n}\n\nexport interface EntryRecord {\n id: string;\n workspace_id: string;\n site_id: string;\n collection_key: string;\n slug: string;\n content: Record<string, unknown>;\n schema_version: number;\n position?: string | null;\n status: 'draft' | 'published' | 'scheduled';\n scheduled_at?: string | null;\n published_at?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface RouteRecord {\n id: string;\n workspace_id: string;\n site_id: string;\n path_or_pattern: string;\n kind: 'static' | 'collection';\n collection_key?: string | null;\n source_path?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface RouteSeo {\n title?: string;\n description?: string;\n canonical?: string;\n og_image?: string;\n og_title?: string;\n og_description?: string;\n twitter_card?: string;\n noindex?: boolean;\n sitemap_changefreq?: string;\n sitemap_priority?: number;\n [key: string]: unknown;\n}\n\nexport interface AssetRecord {\n id: string;\n workspace_id: string;\n site_id: string;\n filename: string;\n mime_type: string;\n size: number;\n metadata: Record<string, unknown>;\n url?: string | null;\n alt: string;\n focal_x: number;\n focal_y: number;\n width?: number | null;\n height?: number | null;\n storage_key?: string | null;\n kind?: 'image' | 'video' | 'audio' | 'file' | string;\n duration_ms?: number | null;\n renditions?: Record<string, unknown>;\n status?: 'uploading' | 'processing' | 'ready' | 'failed' | string;\n error?: string | null;\n raw_key?: string | null;\n preset_version?: number | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface AssetUsageItem {\n type: 'field' | 'entry' | 'block_instance' | string;\n name: string;\n key?: string;\n slug?: string;\n collection?: string;\n block_key?: string;\n route_path?: string;\n}\n\nexport interface AssetUsageResult {\n usage: AssetUsageItem[];\n referenced: boolean;\n}\n\nexport interface OrgRecord {\n id: string;\n workspace_id: string;\n name: string;\n slug: string;\n status: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface OrgMemberRecord {\n id: string;\n org_id: string;\n workspace_id: string;\n user_id: string;\n role: 'owner' | 'viewer';\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface WorkspaceRecord {\n id: string;\n name: string;\n slug: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface WorkspaceMemberRecord {\n id: string;\n workspace_id: string;\n user_id: string;\n role: 'owner' | 'editor' | 'viewer';\n email: string;\n name?: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface AccountAuthMethodResult {\n method: 'password' | 'magic_link';\n}\n\nexport interface ProjectRecord {\n id: string;\n org_id: string;\n workspace_id: string;\n name: string;\n slug: string;\n status: 'proposal' | 'contract' | 'billing' | 'in_progress' | 'done' | 'maintenance';\n created_at?: string;\n updated_at?: string;\n}\n\n// No r2_key field â the raw storage key must never reach the browser.\nexport interface DeliverableRecord {\n id: string;\n project_id: string;\n org_id: string;\n workspace_id: string;\n folder_id?: string | null;\n title: string;\n kind: string;\n mime_type: string;\n size: number;\n uploaded_by?: string | null;\n visible_at?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface DeliverableFolderRecord {\n id: string;\n project_id: string;\n org_id: string;\n workspace_id: string;\n parent_id?: string | null;\n name: string;\n created_by?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface DeliverableShareLink {\n id: string;\n deliverable_id: string;\n workspace_id: string;\n token?: string;\n created_by?: string | null;\n expires_at?: string | null;\n revoked_at?: string | null;\n view_count: number;\n last_accessed_at?: string | null;\n created_at?: string;\n}\n\nexport interface PublicShareRecord {\n title: string;\n mime_type: string;\n size: number;\n kind: string;\n project_name: string;\n org_name: string;\n expires_at?: string | null;\n}\n\nexport interface OrgDeliverableRecord extends DeliverableRecord {\n projects: { id: string; name: string };\n}\n\nexport interface DeliverableDownloadResult {\n url: string;\n expires_at: number;\n}\n\nexport interface ProposalRecipient {\n id?: string;\n name: string;\n email: string;\n order?: number;\n role?: string;\n}\n\nexport interface ProposalVersionRecord {\n id: string;\n proposal_id: string;\n org_id: string;\n workspace_id: string;\n version_number: number;\n byte_size: number;\n checksum_sha256: string;\n created_by?: string | null;\n created_at?: string;\n sent_at?: string | null;\n has_thumbnail?: boolean;\n}\n\nexport interface ProposalEnvelopeRecord {\n id: string;\n proposal_id: string;\n version_id: string;\n org_id: string;\n workspace_id: string;\n provider: string;\n provider_envelope_id: string;\n state:\n | 'created'\n | 'sent'\n | 'viewed'\n | 'in_progress'\n | 'completed'\n | 'declined'\n | 'expired'\n | 'canceled'\n | 'error'\n | 'voided';\n recipients: ProposalRecipient[];\n archive_state: 'not_ready' | 'pending' | 'archived' | 'failed';\n archive_attempts: number;\n archive_last_error?: string | null;\n last_provider_check_at?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface EnvelopeStatusResult {\n envelopeId: string;\n provider: string;\n state: ProposalEnvelopeRecord['state'];\n recipients: Array<{ id?: string; name?: string; email: string; status: string; order?: number }>;\n}\n\nexport type DocumentType = 'proposal' | 'contract';\nexport type DocumentStatus =\n | 'draft'\n | 'pending_approval'\n | 'approved'\n | 'sent'\n | 'viewed'\n | 'signed'\n | 'declined'\n | 'void';\n\nexport interface ProposalRecord {\n id: string;\n project_id: string;\n org_id: string;\n workspace_id: string;\n title: string;\n type?: DocumentType;\n status: DocumentStatus;\n current_version_id?: string | null;\n approved_at?: string | null;\n approved_by?: string | null;\n created_by?: string | null;\n created_at?: string;\n updated_at?: string;\n current_version?: ProposalVersionRecord | null;\n active_envelope?: ProposalEnvelopeRecord | null;\n versions?: ProposalVersionRecord[];\n envelopes?: ProposalEnvelopeRecord[];\n}\n\nexport type DocumentRecord = ProposalRecord;\n\nexport interface ProposalEventRecord {\n id: string;\n provider: string;\n provider_event_key: string;\n envelope_id?: string | null;\n proposal_id?: string | null;\n event_type: string;\n event_time: string;\n verified: boolean;\n reject_reason?: string | null;\n source: 'webhook' | 'poll' | 'local';\n projection?: Record<string, unknown> | null;\n received_at: string;\n}\n\nexport interface ProposalSigningLink {\n recipientId: string;\n email: string;\n url: string;\n}\n\nexport interface ProposalUrlResult {\n url: string;\n expires_at: number;\n}\n\nexport interface ProposalRecipientOption {\n user_id: string;\n name: string;\n email: string;\n role: string;\n}\n\nexport interface BillingAccountRecord {\n org_id: string;\n workspace_id: string;\n stripe_customer_id: string;\n currency?: string | null;\n livemode: boolean;\n delinquent?: boolean | null;\n linked_by?: string | null;\n linked_at?: string | null;\n last_reconciled_at?: string | null;\n last_reconcile_error?: string | null;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface BillingCustomerCandidate {\n id: string;\n name?: string | null;\n email?: string | null;\n /** Stripe creation time, seconds since epoch. */\n created?: number;\n livemode?: boolean;\n}\n\nexport interface BillingCustomerDefaults {\n name: string;\n email: string;\n members: Array<{ user_id: string; name: string; email: string; role: string }>;\n}\n\nexport type CreateBillingCustomerResult =\n | { status: 'created'; account: BillingAccountRecord; customer: BillingCustomerCandidate }\n | { status: 'existing_customers_found'; candidates: BillingCustomerCandidate[] };\n\nexport interface BillingInvoiceRecord {\n stripe_invoice_id: string;\n org_id: string;\n workspace_id: string;\n project_id?: string | null;\n stripe_customer_id: string;\n stripe_subscription_id?: string | null;\n number?: string | null;\n status: 'draft' | 'open' | 'paid' | 'uncollectible' | 'void' | string;\n status_rank: number;\n currency?: string | null;\n amount_due: number;\n amount_paid: number;\n amount_remaining: number;\n subtotal: number;\n total: number;\n due_date?: string | null;\n period_start?: string | null;\n period_end?: string | null;\n finalized_at?: string | null;\n paid_at?: string | null;\n voided_at?: string | null;\n hosted_invoice_url?: string | null;\n invoice_pdf?: string | null;\n billing_phase?: 'deposit' | 'final' | null;\n stripe_created_at: string;\n source_event_id?: string | null;\n source_event_created_at?: string;\n mirrored_at?: string;\n}\n\nexport interface BillingSubscriptionItem {\n price_id?: string | null;\n nickname?: string | null;\n product_name?: string | null;\n unit_amount?: number | null;\n currency?: string | null;\n interval?: string | null;\n quantity?: number;\n}\n\nexport interface BillingSubscriptionRecord {\n stripe_subscription_id: string;\n org_id: string;\n workspace_id: string;\n project_id?: string | null;\n stripe_customer_id: string;\n status:\n | 'incomplete'\n | 'incomplete_expired'\n | 'trialing'\n | 'active'\n | 'past_due'\n | 'paused'\n | 'unpaid'\n | 'canceled'\n | string;\n status_rank: number;\n collection_method?: string | null;\n currency?: string | null;\n current_period_start?: string | null;\n current_period_end?: string | null;\n cancel_at_period_end?: boolean;\n canceled_at?: string | null;\n trial_end?: string | null;\n start_date?: string | null;\n items: BillingSubscriptionItem[];\n latest_invoice_id?: string | null;\n stripe_created_at: string;\n source_event_id?: string | null;\n source_event_created_at?: string;\n mirrored_at?: string;\n}\n\nexport interface BillingInvoiceItemRecord {\n stripe_line_item_id: string;\n stripe_invoice_id: string;\n org_id: string;\n workspace_id: string;\n project_id?: string | null;\n description?: string | null;\n quantity: number;\n unit_amount?: number | null;\n amount: number;\n currency?: string | null;\n period_start?: string | null;\n period_end?: string | null;\n proration: boolean;\n kind?: string | null;\n hours?: number | null;\n change_request?: string | null;\n metadata?: Record<string, unknown>;\n stripe_created_at?: string | null;\n mirrored_at?: string;\n}\n\nexport type EngagementType = 'recurring' | 'fixed' | 'none';\n\nexport interface ProjectBillingTotals {\n contract_total: number;\n paid: number;\n outstanding: number;\n hourly_total: number;\n hourly_hours: number;\n next_due_date: string | null;\n currency: string;\n}\n\nexport interface ProjectBillingFixedSchedule {\n deposit: BillingInvoiceRecord | null;\n final: BillingInvoiceRecord | null;\n}\n\nexport interface ProjectBillingSummary {\n engagement_type: EngagementType;\n totals: ProjectBillingTotals;\n fixed_schedule: ProjectBillingFixedSchedule;\n subscriptions: BillingSubscriptionRecord[];\n invoices: BillingInvoiceRecord[];\n line_items: BillingInvoiceItemRecord[];\n account: BillingAccountRecord | null;\n}\n\nexport interface BillingSummaryResult {\n account: BillingAccountRecord | null;\n subscriptions: BillingSubscriptionRecord[];\n invoices: BillingInvoiceRecord[];\n}\n\nexport interface BillingOverviewRow {\n org_id: string;\n org_name: string;\n org_slug: string;\n account: BillingAccountRecord | null;\n open_invoices_count: number;\n open_invoices_amount: number;\n active_subscriptions_count: number;\n subscription_status: string;\n last_invoice_date?: string | null;\n currency: string;\n}\n\nexport interface BillingPortalSession {\n url: string;\n}\n\nexport type AgencyHomeActionKind =\n | 'contract_unsigned'\n | 'proposal_pending'\n | 'invoice_open'\n | 'change_request_untriaged';\n\nexport interface AgencyHomeActionItem {\n kind: AgencyHomeActionKind;\n /** Proposal id, stripe_invoice_id, or change request id depending on `kind`. */\n id: string;\n title: string;\n org_id: string | null;\n /** Org name, or the site name for change requests, which hang off sites not orgs. */\n org_name: string | null;\n project_id: string | null;\n site_id: string | null;\n /** Whole UTC days since `anchor_at`, computed the same way cron/reminders.ts does. */\n days_elapsed: number;\n anchor_at: string | null;\n /** Minor currency units; invoices only. */\n amount_due: number | null;\n currency: string | null;\n href: string;\n}\n\nexport interface AgencyHomeSummary {\n /** Sorted by days_elapsed DESC and capped; `action_counts` carries the true totals. */\n actions: AgencyHomeActionItem[];\n action_counts: Record<AgencyHomeActionKind, number>;\n stages: Record<ProjectRecord['status'], number>;\n money: {\n /** Bucketed by currency â Stripe mirrors currency per invoice, so totals never cross. */\n open_invoices: Array<{ currency: string; amount: number; count: number }>;\n overdue_invoice_count: number;\n active_subscription_count: number;\n usage: {\n period_month: string;\n billed_usd: number;\n cost_usd: number;\n margin_usd: number;\n margin_pct: number;\n } | null;\n close_exception_count: number;\n };\n delivery: {\n builds_in_flight: number;\n builds_failed_24h: number;\n last_failed_build: {\n site_id: string;\n site_name: string | null;\n error: string | null;\n created_at: string;\n } | null;\n scheduled_entries_7d: number;\n next_scheduled_at: string | null;\n search_console_errors: number;\n };\n generated_at: string;\n}\n\nexport interface PriceBookEntry {\n id: string;\n workspace_id: string;\n org_id?: string | null;\n meter_id: string;\n treatment: 'bundled' | 'at_cost' | 'markup' | 'unbilled';\n rate?: number | null;\n markup_pct?: number | null;\n included_allowance?: number | null;\n display_rate: boolean;\n effective_from: string;\n note?: string | null;\n created_at?: string;\n created_by?: string | null;\n}\n\nexport interface CloseRun {\n id: string;\n workspace_id: string;\n org_id: string;\n period_month: string;\n status: 'pending' | 'previewed' | 'committing' | 'committed' | 'failed' | 'refused';\n refusal_reason?: string | null;\n partial_period: boolean;\n period_start: string;\n period_end: string;\n subtotal_usd: number;\n cost_basis_usd?: number;\n computed_at?: string | null;\n committed_at?: string | null;\n committed_by?: string | null;\n error?: string | null;\n created_at?: string;\n close_line_items?: CloseLineItem[];\n}\n\nexport interface CloseLineItem {\n id: string;\n close_run_id: string;\n workspace_id: string;\n org_id: string;\n meter_id: string;\n treatment: string;\n price_book_entry_id?: string | null;\n quantity: number;\n allowance_applied: number;\n billable_quantity: number;\n included_allowance?: number | null;\n unit_rate_usd?: number | null;\n amount_usd?: number | null;\n amount_cents?: number | null;\n description: string;\n display_rate?: boolean;\n rollup_hash?: string;\n adjusts_period?: string | null;\n stripe_invoiceitem_id?: string | null;\n committed_at?: string | null;\n created_at?: string;\n}\n\nexport interface UsageDashboardData {\n mode: 'committed' | 'preview';\n periodMonth: string;\n run?: CloseRun;\n lines?: CloseLineItem[];\n preview?: {\n status: 'ok' | 'refused';\n refusalReason?: string;\n lines: CloseLineItem[];\n adjustments: CloseLineItem[];\n unmappedMeters: string[];\n totals: { subtotalUsd: number; costBasisUsd: number };\n partialPeriod: boolean;\n periodStart: string;\n periodEnd: string;\n };\n totals: {\n subtotalUsd: number;\n costBasisUsd?: number;\n };\n}\n\nexport interface CloseExceptionRow {\n kind: 'unmapped_meter' | 'unattributed_usage' | 'failed_close' | 'no_subscription';\n workspace_id?: string | null;\n org_id?: string | null;\n meter_id?: string | null;\n period_date?: string | null;\n quantity?: number;\n close_run_id?: string | null;\n external_id?: string | null;\n details?: string | null;\n created_at?: string;\n}\n\nexport interface OrgMarginRow {\n workspace_id: string;\n org_id: string;\n period_month: string;\n billed_usd: number;\n cost_usd: number;\n margin_usd: number;\n margin_pct: number;\n line_count: number;\n}\n\nexport type LinearBoardColumn = 'backlog' | 'todo' | 'in_progress' | 'done';\n\nexport const LINEAR_BOARD_COLUMNS: Array<{ id: LinearBoardColumn; label: string }> = [\n { id: 'backlog', label: 'Backlog' },\n { id: 'todo', label: 'Todo' },\n { id: 'in_progress', label: 'In Progress' },\n { id: 'done', label: 'Done' },\n];\n\nexport function mapLinearStateToColumn(stateType: string): LinearBoardColumn {\n const normalized = (stateType || '').toLowerCase();\n switch (normalized) {\n case 'backlog':\n case 'triage':\n return 'backlog';\n case 'unstarted':\n return 'todo';\n case 'started':\n return 'in_progress';\n case 'completed':\n case 'canceled':\n case 'cancelled':\n return 'done';\n default:\n return 'backlog';\n }\n}\n\nexport interface LinearLabel {\n id: string;\n name: string;\n color: string;\n}\n\nexport function projectLinearLabels(\n labels?: Array<{ id: string; name?: string | null; color?: string | null }> | null,\n labelIds?: string[] | null,\n): LinearLabel[] {\n if (Array.isArray(labels)) {\n return labels.map((l) => ({\n id: l.id,\n name: l.name || '',\n color: l.color || '',\n }));\n }\n if (Array.isArray(labelIds)) {\n return labelIds.map((id) => ({\n id,\n name: '',\n color: '',\n }));\n }\n return [];\n}\n\nexport interface LinearIssueRecord {\n linear_issue_id: string;\n project_id: string;\n org_id: string;\n workspace_id: string;\n linear_project_id: string;\n identifier: string;\n title: string;\n state_name: string;\n state_type: string;\n state_position: number;\n priority: number;\n labels: LinearLabel[];\n linear_created_at: string;\n linear_updated_at: string;\n linear_completed_at?: string | null;\n mirrored_at: string;\n}\n\nexport interface LinearActivityRecord {\n id: string;\n project_id: string;\n org_id: string;\n workspace_id: string;\n linear_issue_id: string;\n identifier: string;\n from_state_name: string;\n to_state_name: string;\n changed_at: string;\n}\n\nexport interface LinearLinkRecord {\n project_id: string;\n org_id: string;\n workspace_id: string;\n linear_project_id: string;\n linear_project_name?: string | null;\n last_synced_at?: string | null;\n last_sync_error?: string | null;\n created_at: string;\n updated_at: string;\n}\n\nexport interface LinearProjectRecord {\n linear_project_id: string;\n name: string;\n team_key: string;\n state: string;\n updated_at: string;\n synced_at: string;\n}\n\nexport interface ProjectBoardResult {\n columns: {\n backlog: LinearIssueRecord[];\n todo: LinearIssueRecord[];\n in_progress: LinearIssueRecord[];\n done: LinearIssueRecord[];\n };\n last_synced_at: string | null;\n link: LinearLinkRecord | null;\n}\n\nexport interface RedirectRecord {\n id: string;\n workspace_id: string;\n site_id: string;\n from_path: string;\n to_path: string;\n status_code: number;\n enabled: boolean;\n order_key: string;\n created_at?: string;\n updated_at?: string;\n}\n\nexport interface SiteSettingsRecord {\n site_id: string;\n workspace_id: string;\n robots: Record<string, unknown>;\n default_seo: RouteSeo;\n sitemap: Record<string, unknown>;\n not_found_path?: string | null;\n preview_origins?: string[];\n updated_at?: string;\n}\n\nexport interface ContentBundle {\n bundleVersion: 1;\n site_id: string;\n fields: Record<string, unknown>;\n collections: Record<\n string,\n {\n presentation: 'visual' | 'form';\n orderable: boolean;\n route_pattern?: string | null;\n schema_version: number;\n title_field?: string | null;\n list_columns?: string[] | null;\n body_field?: string | null;\n }\n >;\n entries: Record<\n string,\n Array<{\n slug: string;\n content: Record<string, unknown>;\n position?: string | null;\n status: 'draft' | 'published';\n published_at?: string | null;\n }>\n >;\n routes: Array<{\n path?: string;\n pattern?: string;\n kind: 'static' | 'collection';\n collection?: string | null;\n }>;\n settings: {\n robots?: Record<string, unknown>;\n default_seo?: RouteSeo;\n sitemap?: Record<string, unknown>;\n not_found_path?: string | null;\n };\n redirects: Array<{\n id: string;\n from_path: string;\n to_path: string;\n status_code: number;\n enabled: boolean;\n order_key: string;\n }>;\n}\n\nexport interface SiteVersionRecord {\n id: string;\n workspace_id?: string;\n site_id?: string;\n version_number: number;\n bundle?: ContentBundle;\n bundle_hash: string;\n manifest_hash?: string | null;\n published_by?: string | null;\n published_at: string;\n restored_from_version_id?: string | null;\n restorable: boolean;\n}\n\nexport interface ManifestPushReport {\n created: number;\n updated: number;\n unchanged: number;\n renamed: number;\n seeded?: Array<{ key: string; count: number }>;\n orphaned_fields: Array<{ key: string; has_value: boolean }>;\n orphaned_collections: Array<{ key: string; entry_count: number }>;\n unpublished_collections?: Array<{ key: string; entry_count: number }>;\n routes: { added: number; removed: number };\n warnings: string[];\n}\n\nexport interface SiteStatusResult {\n dirty: boolean;\n draft_bundle_hash: string;\n published_bundle_hash: string | null;\n published_version_id: string | null;\n version_number: number;\n}\n\nexport interface PublishSiteResult {\n version_id: string;\n version_number: number;\n bundle_hash: string;\n published_at: string;\n}\n\nexport interface VersionScope {\n field_keys?: string[];\n entries?: Array<{ collection: string; slug: string }>;\n}\n\nexport interface VersionDiffItem {\n key: string;\n label: string;\n group?: string;\n entryRef?: { collection: string; slug: string };\n kind: 'field' | 'entry' | 'setting' | 'redirect';\n changed: boolean;\n current?: unknown;\n target?: unknown;\n}\n\nexport interface VersionRestoreReport {\n fields_restored: number;\n fields_cleared: number;\n entries_restored: number;\n skipped: string[];\n added_since: Array<{ collection: string; slug: string }>;\n warnings: string[];\n}\n\nexport interface PreviewTokenResult {\n token: string;\n site_id: string;\n expires_at: number;\n allowed_origins?: string[];\n ok: boolean;\n}\n\nexport interface SubmissionRecord {\n id: string;\n form_key: string;\n payload: Record<string, string | number | boolean | null>;\n submitted_at: number;\n verification: 'turnstile' | 'none';\n status: 'new' | 'handled' | 'spam';\n handled_at: number | null;\n handled_by: string | null;\n notified_at: number | null;\n}\n\nexport interface SubmissionsPage {\n items: SubmissionRecord[];\n nextCursor: string | null;\n}\n\nexport interface SiteServicesHealth {\n reachable: boolean;\n ok?: boolean;\n schema?: { expected: number; actual: number; ok: boolean };\n}\n\nexport interface SearchDocumentRecord {\n id: string;\n project_id: string;\n org_id: string;\n title: string;\n type?: DocumentType;\n status: DocumentStatus;\n}\n\nexport interface SearchInvoiceRecord {\n stripe_invoice_id: string;\n org_id: string;\n project_id?: string | null;\n number?: string | null;\n status: string;\n total: number;\n currency?: string | null;\n hosted_invoice_url?: string | null;\n}\n\nexport interface SearchResults {\n sites: SiteRecord[];\n projects: ProjectRecord[];\n deliverables: DeliverableRecord[];\n documents: SearchDocumentRecord[];\n invoices: SearchInvoiceRecord[];\n routes: RouteRecord[];\n collections: CollectionRecord[];\n assets: AssetRecord[];\n}\n\ninterface JsonPayload {\n data?: unknown;\n error?: string;\n errors?: unknown[];\n [key: string]: unknown;\n}\n\nexport function createApiClient(options: ApiClientOptions) {\n const getHeaders = () => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n if (options.authToken) {\n headers.Authorization = `Bearer ${options.authToken}`;\n }\n if (options.workspaceId) {\n headers['x-workspace-id'] = options.workspaceId;\n }\n return headers;\n };\n\n const request = async <T = unknown>(\n path: string,\n init?: RequestInit,\n ): Promise<ApiResponse<T>> => {\n try {\n const url = path.startsWith('http')\n ? path\n : `${options.baseUrl}${path.startsWith('/') ? '' : '/'}${path}`;\n const res = await fetch(url, {\n ...init,\n headers: {\n ...getHeaders(),\n ...(init?.headers || {}),\n },\n });\n const json = (await res.json().catch(() => ({}))) as JsonPayload;\n if (!res.ok) {\n return {\n data: null,\n ok: false,\n error: json.error || res.statusText,\n errors: json.errors,\n };\n }\n return {\n data: (json.data !== undefined ? json.data : json) as T,\n ok: true,\n };\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n return { data: null, ok: false, error: message };\n }\n };\n\n return {\n baseUrl: options.baseUrl,\n\n async get<T = unknown>(path: string): Promise<ApiResponse<T>> {\n return request<T>(path, { method: 'GET' });\n },\n\n async post<T = unknown>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>(path, {\n method: 'POST',\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n },\n\n async patch<T = unknown>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>(path, {\n method: 'PATCH',\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n },\n\n async put<T = unknown>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>(path, {\n method: 'PUT',\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n },\n\n async delete<T = unknown>(path: string, body?: unknown): Promise<ApiResponse<T>> {\n return request<T>(path, {\n method: 'DELETE',\n body: body !== undefined ? JSON.stringify(body) : undefined,\n });\n },\n\n // Sites\n async getSites(workspaceId?: string): Promise<ApiResponse<SiteRecord[]>> {\n const ws = workspaceId || options.workspaceId;\n const query = ws ? `?workspace_id=${ws}` : '';\n return this.get<SiteRecord[]>(`/sites${query}`);\n },\n\n async getSite(id: string): Promise<ApiResponse<SiteRecord>> {\n return this.get<SiteRecord>(`/sites/${id}`);\n },\n\n async createSite(input: {\n workspace_id?: string;\n org_id?: string | null;\n project_id?: string | null;\n name: string;\n slug: string;\n domain?: string | null;\n cf_pages_project?: string | null;\n cf_build_branch?: string;\n preview_url_override?: string | null;\n }): Promise<ApiResponse<SiteRecord>> {\n return this.post<SiteRecord>('/sites', input);\n },\n\n async updateSite(\n siteId: string,\n updates: {\n name?: string;\n slug?: string;\n domain?: string | null;\n custom_domain?: string | null;\n cf_pages_project?: string | null;\n cf_build_branch?: string;\n preview_url_override?: string | null;\n },\n ): Promise<ApiResponse<SiteRecord>> {\n return this.patch<SiteRecord>(`/sites/${siteId}`, updates);\n },\n\n async deleteSite(siteId: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/sites/${siteId}`);\n },\n\n async reprovisionPreviewHost(siteId: string): Promise<ApiResponse<{ site: SiteRecord }>> {\n return this.post<{ site: SiteRecord }>(`/sites/${siteId}/preview-host`);\n },\n\n async checkPagesProject(\n name: string,\n workspaceId?: string,\n ): Promise<ApiResponse<PagesProjectStatusResponse>> {\n const ws = workspaceId || options.workspaceId || '';\n const query = `?name=${encodeURIComponent(name)}&workspace_id=${encodeURIComponent(ws)}`;\n return this.get<PagesProjectStatusResponse>(`/sites/pages-project-status${query}`);\n },\n\n // Change Requests & Comments\n async getChangeRequests(\n siteId: string,\n status?: ChangeRequestStatus,\n ): Promise<ApiResponse<ChangeRequestRecord[]>> {\n const query = status ? `?status=${encodeURIComponent(status)}` : '';\n return this.get<ChangeRequestRecord[]>(`/sites/${siteId}/change-requests${query}`);\n },\n\n async getOrCreateDraftChangeRequest(\n siteId: string,\n input?: { type?: SiteSupportRequestType; title?: string; description?: string },\n ): Promise<ApiResponse<ChangeRequestRecord>> {\n return this.post<ChangeRequestRecord>(`/sites/${siteId}/change-requests`, input || {});\n },\n\n async updateChangeRequest(\n id: string,\n input: Partial<Pick<ChangeRequestRecord, 'type' | 'title' | 'description' | 'status'>>,\n ): Promise<ApiResponse<ChangeRequestRecord>> {\n return this.patch<ChangeRequestRecord>(`/change-requests/${id}`, input);\n },\n\n async sendChangeRequest(id: string): Promise<ApiResponse<ChangeRequestRecord>> {\n return this.post<ChangeRequestRecord>(`/change-requests/${id}/send`, {});\n },\n\n async getChangeRequestComments(\n changeRequestId: string,\n ): Promise<ApiResponse<ChangeRequestComment[]>> {\n return this.get<ChangeRequestComment[]>(`/change-requests/${changeRequestId}/comments`);\n },\n\n async createChangeRequestComment(\n changeRequestId: string,\n input: {\n route_path: string;\n anchor?: CommentAnchor | null;\n fallback: CommentFallback;\n body: string;\n },\n ): Promise<ApiResponse<ChangeRequestComment>> {\n return this.post<ChangeRequestComment>(`/change-requests/${changeRequestId}/comments`, input);\n },\n\n async updateChangeRequestComment(\n commentId: string,\n input: {\n body?: string;\n anchor?: CommentAnchor | null;\n fallback?: CommentFallback;\n route_path?: string;\n resolved_at?: string | null;\n },\n ): Promise<ApiResponse<ChangeRequestComment>> {\n return this.patch<ChangeRequestComment>(`/change-request-comments/${commentId}`, input);\n },\n\n async deleteChangeRequestComment(commentId: string): Promise<ApiResponse<{ id: string }>> {\n return this.delete<{ id: string }>(`/change-request-comments/${commentId}`);\n },\n\n async createCommentReply(\n commentId: string,\n body: string,\n ): Promise<ApiResponse<ChangeRequestReply>> {\n return this.post<ChangeRequestReply>(`/change-request-comments/${commentId}/replies`, {\n body,\n });\n },\n\n async listChangeRequests(options?: {\n siteId?: string;\n status?: ChangeRequestStatus;\n }): Promise<ApiResponse<ChangeRequestRecord[]>> {\n const params = new URLSearchParams();\n if (options?.siteId) params.set('siteId', options.siteId);\n if (options?.status) params.set('status', options.status);\n const query = params.toString() ? `?${params.toString()}` : '';\n return this.get<ChangeRequestRecord[]>(`/change-requests${query}`);\n },\n\n // Notifications & Channels\n async getNotifications(options?: {\n unread?: boolean;\n limit?: number;\n }): Promise<ApiResponse<NotificationRecord[]>> {\n const params = new URLSearchParams();\n if (options?.unread) params.set('unread', '1');\n if (options?.limit) params.set('limit', String(options.limit));\n const query = params.toString() ? `?${params.toString()}` : '';\n return this.get<NotificationRecord[]>(`/notifications${query}`);\n },\n\n async markNotificationsRead(input: {\n ids?: string[];\n all?: boolean;\n }): Promise<ApiResponse<{ ok: boolean }>> {\n return this.post<{ ok: boolean }>('/notifications/read', input);\n },\n\n async getEmailPrefs(): Promise<ApiResponse<NotificationEmailPrefs>> {\n return this.get<NotificationEmailPrefs>('/notifications/email-prefs');\n },\n\n async updateEmailPrefs(input: {\n reminders?: boolean;\n monthly_digest?: boolean;\n workspace_id?: string;\n }): Promise<ApiResponse<NotificationEmailPrefs>> {\n return this.patch<NotificationEmailPrefs>('/notifications/email-prefs', input);\n },\n\n async listNotificationChannels(\n workspaceId: string,\n ): Promise<ApiResponse<NotificationChannel[]>> {\n return this.get<NotificationChannel[]>(`/workspaces/${workspaceId}/notification-channels`);\n },\n\n async createNotificationChannel(\n workspaceId: string,\n input: {\n kind: 'slack' | 'discord';\n name: string;\n webhook_url: string;\n enabled?: boolean;\n event_types?: NotificationEventType[];\n site_ids?: string[] | null;\n },\n ): Promise<ApiResponse<NotificationChannel>> {\n return this.post<NotificationChannel>(\n `/workspaces/${workspaceId}/notification-channels`,\n input,\n );\n },\n\n async updateNotificationChannel(\n id: string,\n input: Partial<{\n kind: 'slack' | 'discord';\n name: string;\n webhook_url: string;\n enabled: boolean;\n event_types: NotificationEventType[];\n site_ids: string[] | null;\n }>,\n ): Promise<ApiResponse<NotificationChannel>> {\n return this.patch<NotificationChannel>(`/notification-channels/${id}`, input);\n },\n\n async deleteNotificationChannel(id: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/notification-channels/${id}`);\n },\n\n async testNotificationChannel(\n id: string,\n ): Promise<ApiResponse<{ ok: boolean; outcome: string; status?: number; error?: string }>> {\n return this.post<{ ok: boolean; outcome: string; status?: number; error?: string }>(\n `/notification-channels/${id}/test`,\n {},\n );\n },\n\n // Manifest Push\n async pushManifest(\n siteId: string,\n manifest: SiteManifest,\n renames?: Array<{ from: string; to: string }>,\n ): Promise<ApiResponse<ManifestPushReport>> {\n return this.put<ManifestPushReport>(`/sites/${siteId}/manifest`, {\n manifest,\n renames,\n });\n },\n\n // Fields\n async getFields(\n siteId: string,\n opts?: { include_orphaned?: boolean },\n ): Promise<ApiResponse<FieldWithDefinition[]>> {\n const query = opts?.include_orphaned ? '?include_orphaned=1' : '';\n return this.get<FieldWithDefinition[]>(`/sites/${siteId}/fields${query}`);\n },\n\n async setFieldValue(\n siteId: string,\n key: string,\n value: unknown,\n type?: string,\n ): Promise<ApiResponse<{ key: string; value: unknown }>> {\n return this.patch<{ key: string; value: unknown }>(\n `/sites/${siteId}/fields/${encodeURIComponent(key)}`,\n { value, type },\n );\n },\n\n async setFieldValues(\n siteId: string,\n values: Record<string, unknown>,\n types?: Record<string, string>,\n ): Promise<ApiResponse<{ updated: number }>> {\n return this.patch<{ updated: number }>(`/sites/${siteId}/fields`, { values, types });\n },\n\n async deleteOrphanedFields(\n siteId: string,\n keys: string[],\n ): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/sites/${siteId}/fields/orphaned`, { keys });\n },\n\n // Collections\n async getCollections(\n siteId: string,\n opts?: { include_orphaned?: boolean },\n ): Promise<ApiResponse<CollectionRecord[]>> {\n const query = opts?.include_orphaned ? '?include_orphaned=1' : '';\n return this.get<CollectionRecord[]>(`/sites/${siteId}/collections${query}`);\n },\n\n async getCollectionStats(\n siteId: string,\n ): Promise<ApiResponse<Record<string, CollectionEntryStats>>> {\n return this.get<Record<string, CollectionEntryStats>>(`/sites/${siteId}/collections/stats`);\n },\n\n // Stream videos (Cloudflare Stream) â distinct from `assets`, which is the R2 pipeline.\n async createStreamDirectUpload(\n siteId: string,\n body?: { maxDurationSeconds?: number; title?: string },\n ): Promise<ApiResponse<{ uploadURL: string; uid: string }>> {\n return this.post<{ uploadURL: string; uid: string }>(\n `/sites/${siteId}/stream/direct-upload`,\n body ?? {},\n );\n },\n\n async getStreamVideos(siteId: string): Promise<ApiResponse<StreamVideoRecord[]>> {\n return this.get<StreamVideoRecord[]>(`/sites/${siteId}/stream/videos`);\n },\n\n async getStreamVideo(\n siteId: string,\n uid: string,\n ): Promise<\n ApiResponse<{\n uid: string;\n state: string;\n pctComplete: number;\n durationMs?: number | null;\n thumbnail?: string | null;\n ready: boolean;\n error?: string | null;\n }>\n > {\n return this.get(`/sites/${siteId}/stream/videos/${uid}`);\n },\n\n async updateStreamVideo(\n siteId: string,\n uid: string,\n updates: { title: string | null },\n ): Promise<ApiResponse<StreamVideoRecord>> {\n return this.patch<StreamVideoRecord>(`/sites/${siteId}/stream/videos/${uid}`, updates);\n },\n\n async getStreamVideoUsage(\n siteId: string,\n uid: string,\n ): Promise<ApiResponse<{ usage: AssetUsageItem[]; referenced: boolean }>> {\n return this.get(`/sites/${siteId}/stream/videos/${uid}/usage`);\n },\n\n async deleteStreamVideo(\n siteId: string,\n uid: string,\n ): Promise<ApiResponse<{ usage?: Array<{ type: string; name: string }> }>> {\n return this.delete(`/sites/${siteId}/stream/videos/${uid}`);\n },\n\n async listDeployTokens(siteId: string): Promise<ApiResponse<DeployTokenRecord[]>> {\n return this.get<DeployTokenRecord[]>(`/sites/${siteId}/deploy-tokens`);\n },\n\n async createDeployToken(\n siteId: string,\n name?: string,\n ): Promise<ApiResponse<CreatedDeployToken>> {\n return this.post<CreatedDeployToken>(`/sites/${siteId}/deploy-tokens`, { name });\n },\n\n async revokeDeployToken(siteId: string, tokenId: string): Promise<ApiResponse<void>> {\n return this.delete<void>(`/sites/${siteId}/deploy-tokens/${tokenId}`);\n },\n\n async createCliSession(): Promise<ApiResponse<CliSessionResult>> {\n return this.post<CliSessionResult>('/cli/sessions');\n },\n\n async listEntries(\n siteId: string,\n collectionKey: string,\n opts?: { status?: 'draft' | 'published' },\n ): Promise<ApiResponse<EntryRecord[]>> {\n const query = opts?.status ? `?status=${opts.status}` : '';\n return this.get<EntryRecord[]>(\n `/sites/${siteId}/collections/${encodeURIComponent(collectionKey)}/entries${query}`,\n );\n },\n\n async createEntry(\n siteId: string,\n collectionKey: string,\n input: {\n slug: string;\n content?: Record<string, unknown>;\n position?: string | null;\n status?: 'draft' | 'published' | 'scheduled';\n scheduled_at?: string | null;\n },\n ): Promise<ApiResponse<EntryRecord>> {\n return this.post<EntryRecord>(\n `/sites/${siteId}/collections/${encodeURIComponent(collectionKey)}/entries`,\n input,\n );\n },\n\n async getEntry(id: string): Promise<ApiResponse<EntryRecord>> {\n return this.get<EntryRecord>(`/entries/${id}`);\n },\n\n async updateEntry(\n id: string,\n input: {\n slug?: string;\n content?: Record<string, unknown>;\n position?: string | null;\n status?: 'draft' | 'published' | 'scheduled';\n scheduled_at?: string | null;\n expected_updated_at?: string;\n },\n ): Promise<ApiResponse<EntryRecord>> {\n return this.patch<EntryRecord>(`/entries/${id}`, input);\n },\n\n async setEntryStatus(\n id: string,\n status: 'draft' | 'published' | 'scheduled',\n scheduled_at?: string | null,\n ): Promise<ApiResponse<EntryRecord>> {\n return this.patch<EntryRecord>(`/entries/${id}`, { status, scheduled_at });\n },\n\n async reorderEntry(\n id: string,\n input: {\n prev_order_key?: string | null;\n next_order_key?: string | null;\n prev_position?: string | null;\n next_position?: string | null;\n },\n ): Promise<ApiResponse<EntryRecord>> {\n return this.post<EntryRecord>(`/entries/${id}/reorder`, input);\n },\n\n async deleteEntry(id: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/entries/${id}`);\n },\n\n // Routes (read-only list derived from manifest)\n async getRoutes(siteId: string): Promise<ApiResponse<RouteRecord[]>> {\n return this.get<RouteRecord[]>(`/sites/${siteId}/routes`);\n },\n\n // Site Status & Publishing\n async getSiteStatus(siteId: string): Promise<ApiResponse<SiteStatusResult>> {\n return this.get<SiteStatusResult>(`/sites/${siteId}/status`);\n },\n\n async publishSite(siteId: string): Promise<ApiResponse<PublishSiteResult>> {\n return this.post<PublishSiteResult>(`/sites/${siteId}/publish`);\n },\n\n async getSiteVersions(siteId: string, limit = 50): Promise<ApiResponse<SiteVersionRecord[]>> {\n return this.get<SiteVersionRecord[]>(`/sites/${siteId}/versions?limit=${limit}`);\n },\n\n async diffSiteVersion(\n siteId: string,\n versionId: string,\n scope?: VersionScope,\n ): Promise<ApiResponse<VersionDiffItem[]>> {\n return this.post<VersionDiffItem[]>(\n `/sites/${siteId}/versions/${versionId}/diff`,\n scope || {},\n );\n },\n\n async restoreSiteVersion(\n siteId: string,\n versionId: string,\n scope?: VersionScope,\n ): Promise<ApiResponse<VersionRestoreReport>> {\n return this.post<VersionRestoreReport>(\n `/sites/${siteId}/versions/${versionId}/restore`,\n scope || {},\n );\n },\n\n // Assets\n async getAssets(siteId: string): Promise<ApiResponse<AssetRecord[]>> {\n return this.get<AssetRecord[]>(`/assets?site_id=${siteId}`);\n },\n\n async getAsset(id: string): Promise<ApiResponse<AssetRecord>> {\n return this.get<AssetRecord>(`/assets/${id}`);\n },\n\n async createAsset(input: {\n workspace_id?: string;\n site_id: string;\n filename: string;\n mime_type: string;\n size?: number;\n metadata?: Record<string, unknown>;\n url?: string | null;\n alt?: string;\n focal_x?: number;\n focal_y?: number;\n width?: number | null;\n height?: number | null;\n storage_key?: string | null;\n }): Promise<ApiResponse<AssetRecord>> {\n const workspace_id = input.workspace_id || options.workspaceId;\n return this.post<AssetRecord>('/assets', { ...input, workspace_id });\n },\n\n async uploadAsset(\n siteId: string,\n file: File | Blob,\n filename: string,\n opts?: { width?: number; height?: number; contentType?: string },\n ): Promise<ApiResponse<AssetRecord>> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const query = new URLSearchParams({\n site_id: siteId,\n filename,\n });\n if (opts?.width) query.set('width', String(opts.width));\n if (opts?.height) query.set('height', String(opts.height));\n\n const headers: Record<string, string> = {\n 'Content-Type': opts?.contentType || (file as File).type || 'application/octet-stream',\n };\n if (options.authToken) {\n headers.Authorization = `Bearer ${options.authToken}`;\n }\n if (options.workspaceId) {\n headers['x-workspace-id'] = options.workspaceId;\n }\n\n try {\n const res = await fetch(`${baseUrl}/assets/upload?${query.toString()}`, {\n method: 'POST',\n headers,\n body: file,\n });\n const json = (await res.json().catch(() => ({}))) as ApiResponse<AssetRecord>;\n if (!res.ok) {\n return {\n data: null,\n ok: false,\n error: json.error || `HTTP ${res.status}: ${res.statusText}`,\n };\n }\n return json;\n } catch (err) {\n return {\n data: null,\n ok: false,\n error: err instanceof Error ? err.message : 'Network request failed',\n };\n }\n },\n\n async updateAsset(\n id: string,\n updates: Partial<AssetRecord>,\n ): Promise<ApiResponse<AssetRecord>> {\n return this.patch<AssetRecord>(`/assets/${id}`, updates);\n },\n\n async deleteAsset(id: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/assets/${id}`);\n },\n\n async getAssetUsage(id: string): Promise<ApiResponse<AssetUsageResult>> {\n return this.get<AssetUsageResult>(`/assets/${id}/usage`);\n },\n\n async startMultipartUpload(input: {\n site_id: string;\n filename: string;\n mime: string;\n size: number;\n preset_set?: string;\n }): Promise<ApiResponse<{ assetId: string; uploadId: string; key: string; partSize: number }>> {\n return this.post('/assets/multipart/start', input);\n },\n\n async uploadMultipartPart(params: {\n assetId: string;\n uploadId: string;\n partNumber: number;\n data: Blob | ArrayBuffer;\n }): Promise<ApiResponse<{ etag: string; partNumber: number }>> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const query = new URLSearchParams({\n assetId: params.assetId,\n uploadId: params.uploadId,\n n: String(params.partNumber),\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/octet-stream',\n };\n if (options.authToken) {\n headers.Authorization = `Bearer ${options.authToken}`;\n }\n if (options.workspaceId) {\n headers['x-workspace-id'] = options.workspaceId;\n }\n\n try {\n const res = await fetch(`${baseUrl}/assets/multipart/part?${query.toString()}`, {\n method: 'PUT',\n headers,\n body: params.data,\n });\n const json = (await res.json().catch(() => ({}))) as ApiResponse<{\n etag: string;\n partNumber: number;\n }>;\n if (!res.ok) {\n return {\n data: null,\n ok: false,\n error: json.error || `HTTP ${res.status}: ${res.statusText}`,\n };\n }\n return json;\n } catch (err) {\n return {\n data: null,\n ok: false,\n error: err instanceof Error ? err.message : 'Network request failed',\n };\n }\n },\n\n async completeMultipartUpload(input: {\n assetId: string;\n uploadId: string;\n parts: Array<{ partNumber: number; etag: string }>;\n }): Promise<ApiResponse<AssetRecord>> {\n return this.post<AssetRecord>('/assets/multipart/complete', input);\n },\n\n async abortMultipartUpload(input: {\n assetId: string;\n uploadId: string;\n }): Promise<ApiResponse<{ ok: boolean }>> {\n return this.post<{ ok: boolean }>('/assets/multipart/abort', input);\n },\n\n async uploadAssetMultipart(\n siteId: string,\n file: File,\n opts?: {\n presetSet?: string;\n onProgress?: (progress: number) => void;\n resume?: {\n assetId: string;\n uploadId: string;\n parts?: Array<{ partNumber: number; etag: string }>;\n };\n abortOnFailure?: boolean;\n },\n ): Promise<\n ApiResponse<AssetRecord> & {\n resumeState?: {\n assetId: string;\n uploadId: string;\n parts: Array<{ partNumber: number; etag: string }>;\n };\n }\n > {\n let assetId = opts?.resume?.assetId;\n let uploadId = opts?.resume?.uploadId;\n const partSize = 8 * 1024 * 1024;\n const uploadedParts: Array<{ partNumber: number; etag: string }> = [\n ...(opts?.resume?.parts || []),\n ];\n\n if (!assetId || !uploadId) {\n const startRes = await this.startMultipartUpload({\n site_id: siteId,\n filename: file.name,\n mime: file.type || 'application/octet-stream',\n size: file.size,\n preset_set: opts?.presetSet,\n });\n\n if (!startRes.ok || !startRes.data) {\n return {\n data: null,\n ok: false,\n error: startRes.error || 'Failed to start multipart upload',\n };\n }\n\n assetId = startRes.data.assetId;\n uploadId = startRes.data.uploadId;\n }\n\n const totalParts = Math.ceil(file.size / partSize);\n\n try {\n for (let i = 0; i < totalParts; i++) {\n const partNumber = i + 1;\n\n // Skip already uploaded parts when resuming\n if (uploadedParts.some((p) => p.partNumber === partNumber)) {\n if (opts?.onProgress) {\n opts.onProgress(Math.round(((i + 1) / totalParts) * 100));\n }\n continue;\n }\n\n const start = i * partSize;\n const end = Math.min(start + partSize, file.size);\n const chunk = file.slice(start, end);\n\n let partSuccess = false;\n let lastErr = '';\n\n for (let attempt = 1; attempt <= 3; attempt++) {\n const partRes = await this.uploadMultipartPart({\n assetId,\n uploadId,\n partNumber,\n data: chunk,\n });\n\n if (partRes.ok && partRes.data?.etag) {\n uploadedParts.push({ partNumber, etag: partRes.data.etag });\n partSuccess = true;\n break;\n }\n lastErr = partRes.error || 'Part upload failed';\n await new Promise((r) => setTimeout(r, attempt * 500));\n }\n\n if (!partSuccess) {\n if (opts?.abortOnFailure) {\n await this.abortMultipartUpload({ assetId, uploadId }).catch(() => {});\n }\n return {\n data: null,\n ok: false,\n error: `Failed to upload part ${partNumber}/${totalParts}: ${lastErr}`,\n resumeState: { assetId, uploadId, parts: uploadedParts },\n };\n }\n\n if (opts?.onProgress) {\n opts.onProgress(Math.round(((i + 1) / totalParts) * 100));\n }\n }\n\n return await this.completeMultipartUpload({\n assetId,\n uploadId,\n parts: uploadedParts.sort((a, b) => a.partNumber - b.partNumber),\n });\n } catch (err) {\n if (opts?.abortOnFailure) {\n await this.abortMultipartUpload({ assetId, uploadId }).catch(() => {});\n }\n return {\n data: null,\n ok: false,\n error: err instanceof Error ? err.message : 'Multipart upload failed',\n resumeState: { assetId, uploadId, parts: uploadedParts },\n };\n }\n },\n\n // Redirects\n async getRedirects(siteId: string): Promise<ApiResponse<RedirectRecord[]>> {\n return this.get<RedirectRecord[]>(`/redirects?site_id=${siteId}`);\n },\n\n async getRedirect(id: string): Promise<ApiResponse<RedirectRecord>> {\n return this.get<RedirectRecord>(`/redirects/${id}`);\n },\n\n async createRedirect(input: {\n workspace_id?: string;\n site_id: string;\n from_path: string;\n to_path: string;\n status_code?: number;\n enabled?: boolean;\n order_key?: string;\n prev_order_key?: string | null;\n next_order_key?: string | null;\n }): Promise<ApiResponse<RedirectRecord>> {\n const workspace_id = input.workspace_id || options.workspaceId;\n return this.post<RedirectRecord>('/redirects', { ...input, workspace_id });\n },\n\n async updateRedirect(\n id: string,\n updates: Partial<RedirectRecord>,\n ): Promise<ApiResponse<RedirectRecord>> {\n return this.patch<RedirectRecord>(`/redirects/${id}`, updates);\n },\n\n async deleteRedirect(id: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/redirects/${id}`);\n },\n\n async reorderRedirect(\n id: string,\n prevOrderKey?: string | null,\n nextOrderKey?: string | null,\n ): Promise<ApiResponse<RedirectRecord>> {\n return this.post<RedirectRecord>(`/redirects/${id}/reorder`, {\n prev_order_key: prevOrderKey,\n next_order_key: nextOrderKey,\n });\n },\n\n // Site Settings\n async getSiteSettings(siteId: string): Promise<ApiResponse<SiteSettingsRecord>> {\n return this.get<SiteSettingsRecord>(`/sites/${siteId}/settings`);\n },\n\n async updateSiteSettings(\n siteId: string,\n settings: Partial<SiteSettingsRecord> & { workspace_id?: string },\n ): Promise<ApiResponse<SiteSettingsRecord>> {\n return this.put<SiteSettingsRecord>(`/sites/${siteId}/settings`, settings);\n },\n\n // Preview Tokens\n async createPreviewToken(siteId: string): Promise<ApiResponse<PreviewTokenResult>> {\n return this.post<PreviewTokenResult>(`/sites/${siteId}/preview-token`);\n },\n\n // Builds\n async retryBuild(buildId: string): Promise<ApiResponse<{ ok: boolean; site_id?: string }>> {\n return this.post<{ ok: boolean; site_id?: string }>(`/builds/${buildId}/retry`);\n },\n\n // Auth\n async getAccountAuthMethod(email: string): Promise<ApiResponse<AccountAuthMethodResult>> {\n return this.post<AccountAuthMethodResult>('/auth/account-type', { email });\n },\n\n // Portal: Orgs\n async getOrgs(workspaceId?: string): Promise<ApiResponse<OrgRecord[]>> {\n const ws = workspaceId || options.workspaceId;\n const query = ws ? `?workspace_id=${ws}` : '';\n return this.get<OrgRecord[]>(`/orgs${query}`);\n },\n\n async getOrg(id: string): Promise<ApiResponse<OrgRecord>> {\n return this.get<OrgRecord>(`/orgs/${id}`);\n },\n\n async createOrg(input: {\n workspace_id?: string;\n name: string;\n slug: string;\n status?: string;\n }): Promise<ApiResponse<OrgRecord>> {\n const workspace_id = input.workspace_id || options.workspaceId;\n return this.post<OrgRecord>('/orgs', { ...input, workspace_id });\n },\n\n async updateOrg(\n id: string,\n updates: Partial<Pick<OrgRecord, 'name' | 'slug' | 'status'>>,\n ): Promise<ApiResponse<OrgRecord>> {\n return this.patch<OrgRecord>(`/orgs/${id}`, updates);\n },\n\n async deleteOrg(id: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/orgs/${id}`);\n },\n\n async inviteOrgMember(\n orgId: string,\n input: { email: string; role?: 'owner' | 'viewer' },\n ): Promise<ApiResponse<OrgMemberRecord>> {\n return this.post<OrgMemberRecord>(`/orgs/${orgId}/members`, input);\n },\n\n // Workspaces\n async listWorkspaces(): Promise<ApiResponse<WorkspaceRecord[]>> {\n return this.get<WorkspaceRecord[]>('/workspaces');\n },\n\n async listWorkspaceMembers(\n workspaceId?: string,\n ): Promise<ApiResponse<WorkspaceMemberRecord[]>> {\n const ws = workspaceId || options.workspaceId;\n return this.get<WorkspaceMemberRecord[]>(`/workspaces/${ws}/members`);\n },\n\n async inviteWorkspaceMember(\n workspaceId: string,\n input: { email: string; role?: 'owner' | 'editor' | 'viewer' },\n ): Promise<ApiResponse<WorkspaceMemberRecord>> {\n return this.post<WorkspaceMemberRecord>(`/workspaces/${workspaceId}/members`, input);\n },\n\n async updateWorkspaceMemberRole(\n workspaceId: string,\n userId: string,\n role: 'owner' | 'editor' | 'viewer',\n ): Promise<ApiResponse<WorkspaceMemberRecord>> {\n return this.patch<WorkspaceMemberRecord>(`/workspaces/${workspaceId}/members/${userId}`, {\n role,\n });\n },\n\n async removeWorkspaceMember(\n workspaceId: string,\n userId: string,\n ): Promise<ApiResponse<{ success: true }>> {\n return this.delete<{ success: true }>(`/workspaces/${workspaceId}/members/${userId}`);\n },\n\n // Portal: Projects\n async getProjects(workspaceId?: string): Promise<ApiResponse<ProjectRecord[]>> {\n const ws = workspaceId || options.workspaceId;\n const query = ws ? `?workspace_id=${ws}` : '';\n return this.get<ProjectRecord[]>(`/projects${query}`);\n },\n\n async getProject(id: string): Promise<ApiResponse<ProjectRecord>> {\n return this.get<ProjectRecord>(`/projects/${id}`);\n },\n\n async getOrgProjects(orgId: string): Promise<ApiResponse<ProjectRecord[]>> {\n return this.get<ProjectRecord[]>(`/orgs/${orgId}/projects`);\n },\n\n async createProject(\n orgId: string,\n input: { name: string; slug: string; status?: ProjectRecord['status'] },\n ): Promise<ApiResponse<ProjectRecord>> {\n return this.post<ProjectRecord>(`/orgs/${orgId}/projects`, input);\n },\n\n async updateProject(\n id: string,\n updates: Partial<Pick<ProjectRecord, 'name' | 'slug' | 'status'>>,\n ): Promise<ApiResponse<ProjectRecord>> {\n return this.patch<ProjectRecord>(`/projects/${id}`, updates);\n },\n\n async deleteProject(id: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/projects/${id}`);\n },\n\n async advanceProjectStage(\n projectId: string,\n to: ProjectRecord['status'],\n ): Promise<ApiResponse<{ ok: boolean; project_id: string; status: string }>> {\n return this.post<{ ok: boolean; project_id: string; status: string }>(\n `/projects/${projectId}/advance-stage`,\n { to },\n );\n },\n\n // Portal: Deliverables\n async getProjectDeliverables(projectId: string): Promise<ApiResponse<DeliverableRecord[]>> {\n return this.get<DeliverableRecord[]>(`/projects/${projectId}/deliverables`);\n },\n\n async getProjectDeliverableFolders(\n projectId: string,\n ): Promise<ApiResponse<DeliverableFolderRecord[]>> {\n return this.get<DeliverableFolderRecord[]>(`/projects/${projectId}/deliverable-folders`);\n },\n\n async createDeliverableFolder(\n projectId: string,\n payload: { name: string; parent_id?: string | null },\n ): Promise<ApiResponse<DeliverableFolderRecord>> {\n return this.post<DeliverableFolderRecord>(\n `/projects/${projectId}/deliverable-folders`,\n payload,\n );\n },\n\n async updateDeliverableFolder(\n id: string,\n updates: { name?: string; parent_id?: string | null },\n ): Promise<ApiResponse<DeliverableFolderRecord>> {\n return this.patch<DeliverableFolderRecord>(`/deliverable-folders/${id}`, updates);\n },\n\n async deleteDeliverableFolder(id: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/deliverable-folders/${id}`);\n },\n\n async getOrgDeliverables(orgId: string): Promise<ApiResponse<OrgDeliverableRecord[]>> {\n return this.get<OrgDeliverableRecord[]>(`/orgs/${orgId}/deliverables`);\n },\n\n async uploadDeliverable(\n projectId: string,\n file: File | Blob,\n opts: {\n title: string;\n filename: string;\n kind?: string;\n contentType?: string;\n folderId?: string | null;\n },\n ): Promise<ApiResponse<DeliverableRecord>> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const query = new URLSearchParams({\n title: opts.title,\n filename: opts.filename,\n });\n if (opts.kind) query.set('kind', opts.kind);\n if (opts.folderId) query.set('folder_id', opts.folderId);\n\n const headers: Record<string, string> = {\n 'Content-Type': opts.contentType || (file as File).type || 'application/octet-stream',\n };\n if (options.authToken) {\n headers.Authorization = `Bearer ${options.authToken}`;\n }\n if (options.workspaceId) {\n headers['x-workspace-id'] = options.workspaceId;\n }\n\n try {\n const res = await fetch(\n `${baseUrl}/projects/${projectId}/deliverables/upload?${query.toString()}`,\n {\n method: 'POST',\n headers,\n body: file,\n },\n );\n const json = (await res.json().catch(() => ({}))) as ApiResponse<DeliverableRecord>;\n if (!res.ok) {\n return {\n data: null,\n ok: false,\n error: json.error || `HTTP ${res.status}: ${res.statusText}`,\n };\n }\n return json;\n } catch (err) {\n return {\n data: null,\n ok: false,\n error: err instanceof Error ? err.message : 'Network request failed',\n };\n }\n },\n\n async updateDeliverable(\n id: string,\n updates: Partial<Pick<DeliverableRecord, 'title' | 'kind' | 'visible_at' | 'folder_id'>>,\n ): Promise<ApiResponse<DeliverableRecord>> {\n return this.patch<DeliverableRecord>(`/deliverables/${id}`, updates);\n },\n\n async deleteDeliverable(id: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/deliverables/${id}`);\n },\n\n async getDeliverableDownloadUrl(\n id: string,\n opts?: { disposition?: 'inline' | 'attachment' },\n ): Promise<ApiResponse<DeliverableDownloadResult>> {\n return this.post<DeliverableDownloadResult>(`/deliverables/${id}/download-url`, opts);\n },\n\n async getDeliverableShare(\n deliverableId: string,\n ): Promise<ApiResponse<DeliverableShareLink | null>> {\n return this.get<DeliverableShareLink | null>(`/deliverables/${deliverableId}/share`);\n },\n\n async createDeliverableShare(\n deliverableId: string,\n payload?: { expires_at?: string | null },\n ): Promise<ApiResponse<DeliverableShareLink>> {\n return this.post<DeliverableShareLink>(`/deliverables/${deliverableId}/share`, payload || {});\n },\n\n async revokeDeliverableShare(deliverableId: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/deliverables/${deliverableId}/share`);\n },\n\n async getPublicShare(token: string): Promise<ApiResponse<PublicShareRecord>> {\n return this.get<PublicShareRecord>(`/share/${token}`);\n },\n\n // Portal: Documents (Proposals & Contracts) & E-Signature\n async getProjectDocuments(projectId: string): Promise<ApiResponse<DocumentRecord[]>> {\n return this.get<DocumentRecord[]>(`/projects/${projectId}/documents`);\n },\n\n async getDocument(documentId: string): Promise<ApiResponse<DocumentRecord>> {\n return this.get<DocumentRecord>(`/documents/${documentId}`);\n },\n\n async getDocumentEvents(documentId: string): Promise<ApiResponse<ProposalEventRecord[]>> {\n return this.get<ProposalEventRecord[]>(`/documents/${documentId}/events`);\n },\n\n async createDocument(\n projectId: string,\n file: File | Blob,\n opts: { title: string; type?: DocumentType; filename?: string },\n ): Promise<ApiResponse<DocumentRecord>> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const query = new URLSearchParams({\n title: opts.title,\n type: opts.type || 'proposal',\n filename: opts.filename || 'document.pdf',\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': (file as File).type || 'application/pdf',\n };\n if (options.authToken) {\n headers.Authorization = `Bearer ${options.authToken}`;\n }\n if (options.workspaceId) {\n headers['x-workspace-id'] = options.workspaceId;\n }\n\n try {\n const res = await fetch(`${baseUrl}/projects/${projectId}/documents?${query.toString()}`, {\n method: 'POST',\n headers,\n body: file,\n });\n const json = (await res.json().catch(() => ({}))) as ApiResponse<DocumentRecord>;\n if (!res.ok) {\n return {\n data: null,\n ok: false,\n error: json.error || `HTTP ${res.status}: ${res.statusText}`,\n };\n }\n return json;\n } catch (err) {\n return {\n data: null,\n ok: false,\n error: err instanceof Error ? err.message : 'Network request failed',\n };\n }\n },\n\n async approveDocument(documentId: string): Promise<ApiResponse<DocumentRecord>> {\n return this.post<DocumentRecord>(`/documents/${documentId}/approve`);\n },\n\n async uploadDocumentVersion(\n documentId: string,\n file: File | Blob,\n opts?: { filename?: string },\n ): Promise<ApiResponse<ProposalVersionRecord>> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n const query = new URLSearchParams({\n filename: opts?.filename || 'document.pdf',\n });\n\n const headers: Record<string, string> = {\n 'Content-Type': (file as File).type || 'application/pdf',\n };\n if (options.authToken) {\n headers.Authorization = `Bearer ${options.authToken}`;\n }\n if (options.workspaceId) {\n headers['x-workspace-id'] = options.workspaceId;\n }\n\n try {\n const res = await fetch(`${baseUrl}/documents/${documentId}/versions?${query.toString()}`, {\n method: 'POST',\n headers,\n body: file,\n });\n const json = (await res.json().catch(() => ({}))) as ApiResponse<ProposalVersionRecord>;\n if (!res.ok) {\n return {\n data: null,\n ok: false,\n error: json.error || `HTTP ${res.status}: ${res.statusText}`,\n };\n }\n return json;\n } catch (err) {\n return {\n data: null,\n ok: false,\n error: err instanceof Error ? err.message : 'Network request failed',\n };\n }\n },\n\n async uploadDocumentThumbnail(\n versionId: string,\n blob: Blob,\n ): Promise<ApiResponse<{ ok: boolean }>> {\n const baseUrl = options.baseUrl.replace(/\\/+$/, '');\n\n const headers: Record<string, string> = { 'Content-Type': 'image/png' };\n if (options.authToken) {\n headers.Authorization = `Bearer ${options.authToken}`;\n }\n if (options.workspaceId) {\n headers['x-workspace-id'] = options.workspaceId;\n }\n\n try {\n const res = await fetch(`${baseUrl}/document-versions/${versionId}/thumbnail`, {\n method: 'POST',\n headers,\n body: blob,\n });\n const json = (await res.json().catch(() => ({}))) as ApiResponse<{ ok: boolean }>;\n if (!res.ok) {\n return {\n data: null,\n ok: false,\n error: json.error || `HTTP ${res.status}: ${res.statusText}`,\n };\n }\n return json;\n } catch (err) {\n return {\n data: null,\n ok: false,\n error: err instanceof Error ? err.message : 'Network request failed',\n };\n }\n },\n\n async sendDocument(\n documentId: string,\n input: { recipients: ProposalRecipient[] },\n ): Promise<ApiResponse<DocumentRecord>> {\n return this.post<DocumentRecord>(`/documents/${documentId}/send`, input);\n },\n\n async voidDocument(documentId: string, reason?: string): Promise<ApiResponse<DocumentRecord>> {\n return this.post<DocumentRecord>(`/documents/${documentId}/void`, { reason });\n },\n\n async getDocumentSigningLinks(documentId: string): Promise<ApiResponse<ProposalSigningLink[]>> {\n return this.get<ProposalSigningLink[]>(`/documents/${documentId}/signing-links`);\n },\n\n async syncDocumentStatus(documentId: string): Promise<ApiResponse<EnvelopeStatusResult>> {\n return this.post<EnvelopeStatusResult>(`/documents/${documentId}/sync-status`);\n },\n\n async getDocumentVersionViewUrl(\n versionId: string,\n kind: 'original' | 'thumbnail' = 'original',\n ): Promise<ApiResponse<ProposalUrlResult>> {\n return this.post<ProposalUrlResult>(`/document-versions/${versionId}/view-url?kind=${kind}`);\n },\n\n async getDocumentSignedUrl(\n envelopeId: string,\n kind: 'signed' | 'audit' = 'signed',\n ): Promise<ApiResponse<ProposalUrlResult>> {\n return this.post<ProposalUrlResult>(\n `/document-envelopes/${envelopeId}/signed-url?kind=${kind}`,\n );\n },\n\n async getProjectDocumentRecipients(\n projectId: string,\n ): Promise<ApiResponse<ProposalRecipientOption[]>> {\n return this.get<ProposalRecipientOption[]>(`/projects/${projectId}/document-recipients`);\n },\n\n // Backwards-compatible aliases for proposals\n async getProjectProposals(projectId: string): Promise<ApiResponse<ProposalRecord[]>> {\n return this.getProjectDocuments(projectId);\n },\n\n async getProposal(proposalId: string): Promise<ApiResponse<ProposalRecord>> {\n return this.getDocument(proposalId);\n },\n\n async getProposalEvents(proposalId: string): Promise<ApiResponse<ProposalEventRecord[]>> {\n return this.getDocumentEvents(proposalId);\n },\n\n async createProposal(\n projectId: string,\n file: File | Blob,\n opts: { title: string; filename?: string },\n ): Promise<ApiResponse<ProposalRecord>> {\n return this.createDocument(projectId, file, { ...opts, type: 'proposal' });\n },\n\n async uploadProposalVersion(\n proposalId: string,\n file: File | Blob,\n opts?: { filename?: string },\n ): Promise<ApiResponse<ProposalVersionRecord>> {\n return this.uploadDocumentVersion(proposalId, file, opts);\n },\n\n async sendProposal(\n proposalId: string,\n input: { recipients: ProposalRecipient[] },\n ): Promise<ApiResponse<ProposalRecord>> {\n return this.sendDocument(proposalId, input);\n },\n\n async voidProposal(proposalId: string, reason?: string): Promise<ApiResponse<ProposalRecord>> {\n return this.voidDocument(proposalId, reason);\n },\n\n async getProposalSigningLinks(proposalId: string): Promise<ApiResponse<ProposalSigningLink[]>> {\n return this.getDocumentSigningLinks(proposalId);\n },\n\n async getProposalVersionViewUrl(\n versionId: string,\n kind: 'original' | 'thumbnail' = 'original',\n ): Promise<ApiResponse<ProposalUrlResult>> {\n return this.getDocumentVersionViewUrl(versionId, kind);\n },\n\n async getProposalSignedUrl(\n envelopeId: string,\n kind: 'signed' | 'audit' = 'signed',\n ): Promise<ApiResponse<ProposalUrlResult>> {\n return this.getDocumentSignedUrl(envelopeId, kind);\n },\n\n async getProjectProposalRecipients(\n projectId: string,\n ): Promise<ApiResponse<ProposalRecipientOption[]>> {\n return this.getProjectDocumentRecipients(projectId);\n },\n\n // Site Services (form submissions inbox)\n async getSiteSubmissions(\n siteId: string,\n opts?: { status?: 'new' | 'handled' | 'spam'; cursor?: string; limit?: number },\n ): Promise<ApiResponse<SubmissionsPage>> {\n const params = new URLSearchParams();\n if (opts?.status) params.set('status', opts.status);\n if (opts?.cursor) params.set('cursor', opts.cursor);\n if (opts?.limit) params.set('limit', String(opts.limit));\n const query = params.toString();\n return this.get<SubmissionsPage>(`/sites/${siteId}/submissions${query ? `?${query}` : ''}`);\n },\n\n async getSiteSubmission(\n siteId: string,\n submissionId: string,\n ): Promise<ApiResponse<SubmissionRecord>> {\n return this.get<SubmissionRecord>(`/sites/${siteId}/submissions/${submissionId}`);\n },\n\n async updateSiteSubmission(\n siteId: string,\n submissionId: string,\n updates: { status: 'new' | 'handled' | 'spam'; handledBy?: string },\n ): Promise<ApiResponse<SubmissionRecord>> {\n return this.patch<SubmissionRecord>(`/sites/${siteId}/submissions/${submissionId}`, updates);\n },\n\n async getSiteServicesHealth(siteId: string): Promise<ApiResponse<SiteServicesHealth>> {\n return this.get<SiteServicesHealth>(`/sites/${siteId}/services/health`);\n },\n\n // Site Insights / Analytics\n async getSiteInsights<T = unknown>(\n siteId: string,\n query: string,\n opts?: {\n from?: string | number;\n to?: string | number;\n interval?: 'day' | 'hour' | 'week' | 'month';\n limit?: number;\n tier?: 'auto' | 'hot' | 'deep';\n sessionization?: SessionizationMode;\n filters?: InsightsFilter[];\n dimension?: string;\n },\n ): Promise<ApiResponse<InsightsResult<T>>> {\n const params = new URLSearchParams();\n if (opts?.from !== undefined) params.set('from', String(opts.from));\n if (opts?.to !== undefined) params.set('to', String(opts.to));\n if (opts?.interval) params.set('interval', opts.interval);\n if (opts?.limit !== undefined) params.set('limit', String(opts.limit));\n if (opts?.tier) params.set('tier', opts.tier);\n if (opts?.sessionization) params.set('sessionization', opts.sessionization);\n if (opts?.dimension) params.set('dimension', opts.dimension);\n if (opts?.filters && opts.filters.length > 0) {\n params.set('filters', JSON.stringify(opts.filters));\n }\n const q = params.toString();\n return this.get<InsightsResult<T>>(`/sites/${siteId}/insights/${query}${q ? `?${q}` : ''}`);\n },\n\n async getSiteInsightsDeep<T = unknown>(\n siteId: string,\n query: string,\n params?: Record<string, unknown> | DeepQueryParams,\n ): Promise<ApiResponse<InsightsResult<T>>> {\n const sp = new URLSearchParams();\n if (params) {\n for (const [k, v] of Object.entries(params)) {\n if (v !== undefined && v !== null) {\n if (typeof v === 'object') {\n sp.set(k, JSON.stringify(v));\n } else {\n sp.set(k, String(v));\n }\n }\n }\n }\n const q = sp.toString();\n return this.get<InsightsResult<T>>(\n `/sites/${siteId}/insights/deep/${query}${q ? `?${q}` : ''}`,\n );\n },\n\n async getSiteInsightsCoverage(siteId: string): Promise<ApiResponse<InsightsCoverageInfo>> {\n return this.get<InsightsCoverageInfo>(`/sites/${siteId}/insights/coverage`);\n },\n\n async getSiteContentPerformance(\n siteId: string,\n opts?: {\n from?: string | number;\n to?: string | number;\n limit?: number;\n collection?: string;\n },\n ): Promise<ApiResponse<ContentPerformanceResult>> {\n const params = new URLSearchParams();\n if (opts?.from !== undefined) params.set('from', String(opts.from));\n if (opts?.to !== undefined) params.set('to', String(opts.to));\n if (opts?.limit !== undefined) params.set('limit', String(opts.limit));\n if (opts?.collection) params.set('collection', opts.collection);\n const q = params.toString();\n return this.get<ContentPerformanceResult>(\n `/sites/${siteId}/insights/content${q ? `?${q}` : ''}`,\n );\n },\n\n async getSiteInsightsDrift(siteId: string): Promise<ApiResponse<InsightsDriftResult>> {\n return this.get<InsightsDriftResult>(`/sites/${siteId}/insights/drift`);\n },\n\n async getSiteVisitors(\n siteId: string,\n opts?: {\n day?: string;\n from?: number | string;\n to?: number | string;\n limit?: number;\n before?: number;\n filters?: InsightsFilter[];\n },\n ): Promise<ApiResponse<InsightsResult<VisitorListRow>>> {\n const params = new URLSearchParams();\n if (opts?.day) params.set('day', opts.day);\n if (opts?.from !== undefined) params.set('from', String(opts.from));\n if (opts?.to !== undefined) params.set('to', String(opts.to));\n if (opts?.limit !== undefined) params.set('limit', String(opts.limit));\n if (opts?.before !== undefined) params.set('before', String(opts.before));\n if (opts?.filters && opts.filters.length > 0) {\n params.set('filters', JSON.stringify(opts.filters));\n }\n const q = params.toString();\n return this.get<InsightsResult<VisitorListRow>>(\n `/sites/${siteId}/insights/visitors${q ? `?${q}` : ''}`,\n );\n },\n\n async getSiteVisitorJourney(\n siteId: string,\n visitor: string,\n opts?: { day?: string; from?: number | string; to?: number | string },\n ): Promise<ApiResponse<InsightsResult<VisitorEventRow> & { journey?: VisitorJourney }>> {\n const params = new URLSearchParams();\n if (opts?.day) params.set('day', opts.day);\n if (opts?.from !== undefined) params.set('from', String(opts.from));\n if (opts?.to !== undefined) params.set('to', String(opts.to));\n const q = params.toString();\n return this.get<InsightsResult<VisitorEventRow> & { journey?: VisitorJourney }>(\n `/sites/${siteId}/insights/visitors/${encodeURIComponent(visitor)}${q ? `?${q}` : ''}`,\n );\n },\n\n // Google Search Console\n async getSearchConsoleConnection(\n siteId: string,\n ): Promise<ApiResponse<SearchConsoleConnection | null>> {\n return this.get<SearchConsoleConnection | null>(`/sites/${siteId}/search-console`);\n },\n\n async startSearchConsoleOAuth(siteId: string): Promise<ApiResponse<{ authUrl: string }>> {\n return this.post<{ authUrl: string }>(`/sites/${siteId}/search-console/oauth/start`, {});\n },\n\n async listSearchConsoleProperties(\n siteId: string,\n ): Promise<ApiResponse<{ properties: SearchConsoleProperty[] }>> {\n return this.get<{ properties: SearchConsoleProperty[] }>(\n `/sites/${siteId}/search-console/properties`,\n );\n },\n\n async bindSearchConsoleProperty(\n siteId: string,\n payload: { propertyUrl: string; propertyType: 'site' | 'domain' },\n ): Promise<ApiResponse<SearchConsoleConnection>> {\n return this.post<SearchConsoleConnection>(\n `/sites/${siteId}/search-console/property`,\n payload,\n );\n },\n\n async syncSearchConsole(\n siteId: string,\n ): Promise<ApiResponse<{ ok: boolean; synced_at: string }>> {\n return this.post<{ ok: boolean; synced_at: string }>(\n `/sites/${siteId}/search-console/sync`,\n {},\n );\n },\n\n async disconnectSearchConsole(siteId: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/sites/${siteId}/search-console`);\n },\n\n async getSearchConsoleQueries(\n siteId: string,\n opts?: { from?: string | number; to?: string | number; limit?: number },\n ): Promise<ApiResponse<{ results: SearchConsoleQueryRow[] }>> {\n const params = new URLSearchParams();\n if (opts?.from !== undefined) params.set('from', String(opts.from));\n if (opts?.to !== undefined) params.set('to', String(opts.to));\n if (opts?.limit !== undefined) params.set('limit', String(opts.limit));\n const q = params.toString();\n return this.get<{ results: SearchConsoleQueryRow[] }>(\n `/sites/${siteId}/search-console/queries${q ? `?${q}` : ''}`,\n );\n },\n\n async getSearchConsolePages(\n siteId: string,\n opts?: { from?: string | number; to?: string | number; limit?: number },\n ): Promise<ApiResponse<{ results: SearchConsolePageRow[] }>> {\n const params = new URLSearchParams();\n if (opts?.from !== undefined) params.set('from', String(opts.from));\n if (opts?.to !== undefined) params.set('to', String(opts.to));\n if (opts?.limit !== undefined) params.set('limit', String(opts.limit));\n const q = params.toString();\n return this.get<{ results: SearchConsolePageRow[] }>(\n `/sites/${siteId}/search-console/pages${q ? `?${q}` : ''}`,\n );\n },\n\n // Marketing & Campaigns\n async listCampaignEntries(siteId: string): Promise<ApiResponse<EntryRecord[]>> {\n return this.get<EntryRecord[]>(`/sites/${siteId}/campaigns/entries`);\n },\n\n async getCampaignPreview(\n siteId: string,\n entryId: string,\n ): Promise<ApiResponse<CampaignPreviewResult>> {\n return this.get<CampaignPreviewResult>(\n `/sites/${siteId}/campaigns/preview?entryId=${encodeURIComponent(entryId)}`,\n );\n },\n\n async createCampaignSend(siteId: string, entryId: string): Promise<ApiResponse<SendRow>> {\n return this.post<SendRow>(`/sites/${siteId}/campaigns/sends`, { entryId });\n },\n\n async dispatchCampaignSend(\n siteId: string,\n sendId: string,\n ): Promise<ApiResponse<DispatchResult>> {\n return this.post<DispatchResult>(`/sites/${siteId}/campaigns/sends/${sendId}/dispatch`);\n },\n\n async testSendCampaign(\n siteId: string,\n entryId: string,\n to: string,\n ): Promise<ApiResponse<{ ok: boolean; id?: string }>> {\n return this.post<{ ok: boolean; id?: string }>(`/sites/${siteId}/campaigns/test-send`, {\n entryId,\n to,\n });\n },\n\n async listCampaignSends(\n siteId: string,\n opts?: { limit?: number; cursor?: string },\n ): Promise<ApiResponse<{ items: SendRow[]; nextCursor: string | null }>> {\n const params = new URLSearchParams();\n if (opts?.limit) params.set('limit', String(opts.limit));\n if (opts?.cursor) params.set('cursor', opts.cursor);\n const q = params.toString();\n return this.get<{ items: SendRow[]; nextCursor: string | null }>(\n `/sites/${siteId}/campaigns/sends${q ? `?${q}` : ''}`,\n );\n },\n\n async getCampaignSend(siteId: string, sendId: string): Promise<ApiResponse<SendRow>> {\n return this.get<SendRow>(`/sites/${siteId}/campaigns/sends/${sendId}`);\n },\n\n async listSubscribers(\n siteId: string,\n opts?: { status?: string; limit?: number; cursor?: string },\n ): Promise<ApiResponse<{ items: SubscriberItem[]; nextCursor: string | null }>> {\n const params = new URLSearchParams();\n if (opts?.status) params.set('status', opts.status);\n if (opts?.limit) params.set('limit', String(opts.limit));\n if (opts?.cursor) params.set('cursor', opts.cursor);\n const q = params.toString();\n return this.get<{ items: SubscriberItem[]; nextCursor: string | null }>(\n `/sites/${siteId}/subscribers${q ? `?${q}` : ''}`,\n );\n },\n\n async getSubscriberStats(siteId: string): Promise<ApiResponse<SubscriberStats>> {\n return this.get<SubscriberStats>(`/sites/${siteId}/subscribers/stats`);\n },\n\n async getMarketingConfig(siteId: string): Promise<ApiResponse<MarketingConfigSummary>> {\n return this.get<MarketingConfigSummary>(`/sites/${siteId}/marketing/config`);\n },\n\n // SMS Marketing\n async listSmsCampaignEntries(siteId: string): Promise<ApiResponse<EntryRecord[]>> {\n return this.get<EntryRecord[]>(`/sites/${siteId}/sms/entries`);\n },\n\n async getSmsPreview(siteId: string, entryId: string): Promise<ApiResponse<SmsPreviewResult>> {\n return this.get<SmsPreviewResult>(\n `/sites/${siteId}/sms/preview?entryId=${encodeURIComponent(entryId)}`,\n );\n },\n\n async createSmsSend(siteId: string, entryId: string): Promise<ApiResponse<SmsSendRow>> {\n return this.post<SmsSendRow>(`/sites/${siteId}/sms/sends`, { entryId });\n },\n\n async dispatchSmsSend(siteId: string, sendId: string): Promise<ApiResponse<SmsDispatchResult>> {\n return this.post<SmsDispatchResult>(`/sites/${siteId}/sms/sends/${sendId}/dispatch`);\n },\n\n async testSendSms(\n siteId: string,\n entryId: string,\n to: string,\n ): Promise<ApiResponse<{ ok: boolean; sid?: string }>> {\n return this.post<{ ok: boolean; sid?: string }>(`/sites/${siteId}/sms/test-send`, {\n entryId,\n to,\n });\n },\n\n async listSmsSends(\n siteId: string,\n opts?: { limit?: number; cursor?: string },\n ): Promise<ApiResponse<{ items: SmsSendRow[]; nextCursor: string | null }>> {\n const params = new URLSearchParams();\n if (opts?.limit) params.set('limit', String(opts.limit));\n if (opts?.cursor) params.set('cursor', opts.cursor);\n const q = params.toString();\n return this.get<{ items: SmsSendRow[]; nextCursor: string | null }>(\n `/sites/${siteId}/sms/sends${q ? `?${q}` : ''}`,\n );\n },\n\n async getSmsSend(siteId: string, sendId: string): Promise<ApiResponse<SmsSendRow>> {\n return this.get<SmsSendRow>(`/sites/${siteId}/sms/sends/${sendId}`);\n },\n\n async listSmsSubscribers(\n siteId: string,\n opts?: { status?: string; limit?: number; cursor?: string },\n ): Promise<ApiResponse<{ items: SmsSubscriberItem[]; nextCursor: string | null }>> {\n const params = new URLSearchParams();\n if (opts?.status) params.set('status', opts.status);\n if (opts?.limit) params.set('limit', String(opts.limit));\n if (opts?.cursor) params.set('cursor', opts.cursor);\n const q = params.toString();\n return this.get<{ items: SmsSubscriberItem[]; nextCursor: string | null }>(\n `/sites/${siteId}/sms/subscribers${q ? `?${q}` : ''}`,\n );\n },\n\n async getSmsSubscriberStats(siteId: string): Promise<ApiResponse<SmsSubscriberStats>> {\n return this.get<SmsSubscriberStats>(`/sites/${siteId}/sms/subscribers/stats`);\n },\n\n async getSmsConfig(siteId: string): Promise<ApiResponse<SmsConfigSummary>> {\n return this.get<SmsConfigSummary>(`/sites/${siteId}/sms/config`);\n },\n\n async provisionSms(\n siteId: string,\n ): Promise<ApiResponse<{ ok: boolean; state: string; error?: string; serviceSid?: string }>> {\n return this.post<{ ok: boolean; state: string; error?: string; serviceSid?: string }>(\n `/sites/${siteId}/sms/provision`,\n );\n },\n\n async setSmsMessagingServiceSid(\n siteId: string,\n sid: string,\n ): Promise<ApiResponse<{ ok: boolean; data: { sid: string } }>> {\n return this.put<{ ok: boolean; data: { sid: string } }>(\n `/sites/${siteId}/sms/messaging-service-sid`,\n { sid },\n );\n },\n\n async listSiteMembers(\n siteId: string,\n opts?: { limit?: number; cursor?: string },\n ): Promise<ApiResponse<{ items: MemberItem[]; nextCursor: string | null }>> {\n const params = new URLSearchParams();\n if (opts?.limit) params.set('limit', String(opts.limit));\n if (opts?.cursor) params.set('cursor', opts.cursor);\n const q = params.toString();\n return this.get<{ items: MemberItem[]; nextCursor: string | null }>(\n `/sites/${siteId}/members${q ? `?${q}` : ''}`,\n );\n },\n\n async getSiteMemberStats(siteId: string): Promise<ApiResponse<MemberStats>> {\n return this.get<MemberStats>(`/sites/${siteId}/members/stats`);\n },\n\n async deleteSiteMember(\n siteId: string,\n memberId: string,\n ): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/sites/${siteId}/members/${memberId}`);\n },\n\n // Linear Mirror & Status Board\n async getProjectBoard(projectId: string): Promise<ApiResponse<ProjectBoardResult>> {\n return this.get<ProjectBoardResult>(`/projects/${projectId}/board`);\n },\n\n async getProjectActivity(\n projectId: string,\n days = 30,\n ): Promise<ApiResponse<LinearActivityRecord[]>> {\n return this.get<LinearActivityRecord[]>(`/projects/${projectId}/activity?days=${days}`);\n },\n\n async getProjectLinearLink(projectId: string): Promise<ApiResponse<LinearLinkRecord | null>> {\n return this.get<LinearLinkRecord | null>(`/projects/${projectId}/linear-link`);\n },\n\n async setProjectLinearLink(\n projectId: string,\n linearProjectId: string,\n ): Promise<ApiResponse<LinearLinkRecord>> {\n return this.put<LinearLinkRecord>(`/projects/${projectId}/linear-link`, {\n linear_project_id: linearProjectId,\n });\n },\n\n async deleteProjectLinearLink(projectId: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/projects/${projectId}/linear-link`);\n },\n\n async listLinearProjects(): Promise<ApiResponse<LinearProjectRecord[]>> {\n return this.get<LinearProjectRecord[]>('/linear/projects');\n },\n\n async refreshLinearProjects(): Promise<ApiResponse<LinearProjectRecord[]>> {\n return this.post<LinearProjectRecord[]>('/linear/projects/refresh');\n },\n\n async resyncProjectLinear(\n projectId: string,\n ): Promise<ApiResponse<{ ok: boolean; synced_at?: string }>> {\n return this.post<{ ok: boolean; synced_at?: string }>(`/projects/${projectId}/linear-resync`);\n },\n\n // Billing & Stripe Mirror\n async getProjectBilling(projectId: string): Promise<ApiResponse<ProjectBillingSummary>> {\n return this.get<ProjectBillingSummary>(`/projects/${projectId}/billing`);\n },\n\n async getOrgBilling(\n orgId: string,\n projectId?: string,\n ): Promise<ApiResponse<BillingSummaryResult>> {\n const q = projectId ? `?project_id=${encodeURIComponent(projectId)}` : '';\n return this.get<BillingSummaryResult>(`/orgs/${orgId}/billing${q}`);\n },\n\n async getBillingOverview(): Promise<ApiResponse<BillingOverviewRow[]>> {\n return this.get<BillingOverviewRow[]>('/billing/overview');\n },\n\n async getAgencyHome(): Promise<ApiResponse<AgencyHomeSummary>> {\n return this.get<AgencyHomeSummary>('/home/agency');\n },\n\n async createBillingPortalSession(orgId: string): Promise<ApiResponse<BillingPortalSession>> {\n return this.post<BillingPortalSession>(`/orgs/${orgId}/billing/portal-session`);\n },\n\n async linkBillingAccount(\n orgId: string,\n stripeCustomerId: string,\n ): Promise<ApiResponse<BillingAccountRecord>> {\n return this.put<BillingAccountRecord>(`/orgs/${orgId}/billing/account`, {\n stripe_customer_id: stripeCustomerId,\n });\n },\n\n async unlinkBillingAccount(orgId: string): Promise<ApiResponse<{ ok: boolean }>> {\n return this.delete<{ ok: boolean }>(`/orgs/${orgId}/billing/account`);\n },\n\n async getBillingCustomerDefaults(orgId: string): Promise<ApiResponse<BillingCustomerDefaults>> {\n return this.get<BillingCustomerDefaults>(`/orgs/${orgId}/billing/customer-defaults`);\n },\n\n async createBillingCustomer(\n orgId: string,\n input: {\n name: string;\n email?: string;\n description?: string;\n allowDuplicate?: boolean;\n },\n ): Promise<ApiResponse<CreateBillingCustomerResult>> {\n return this.post<CreateBillingCustomerResult>(`/orgs/${orgId}/billing/customer`, {\n name: input.name,\n email: input.email,\n description: input.description,\n allow_duplicate: input.allowDuplicate,\n });\n },\n\n // Price Book, Monthly Close, and Usage\n async getBillingUsage(\n orgId: string,\n period?: string,\n ): Promise<ApiResponse<UsageDashboardData>> {\n const q = period ? `&period=${encodeURIComponent(period)}` : '';\n return this.get<UsageDashboardData>(`/billing/usage?orgId=${encodeURIComponent(orgId)}${q}`);\n },\n\n async getPriceBook(\n orgId?: string,\n ): Promise<ApiResponse<{ entries: PriceBookEntry[]; effective: PriceBookEntry[] }>> {\n const q = orgId ? `?orgId=${encodeURIComponent(orgId)}` : '';\n return this.get<{ entries: PriceBookEntry[]; effective: PriceBookEntry[] }>(\n `/billing/price-book${q}`,\n );\n },\n\n async updatePriceBook(entry: Partial<PriceBookEntry>): Promise<ApiResponse<PriceBookEntry>> {\n return this.put<PriceBookEntry>('/billing/price-book', entry);\n },\n\n async previewClose(\n orgId: string,\n period: string,\n ): Promise<ApiResponse<UsageDashboardData['preview']>> {\n return this.post<UsageDashboardData['preview']>('/billing/closes/preview', { orgId, period });\n },\n\n async commitClose(closeRunId: string): Promise<\n ApiResponse<{\n status: string;\n closeRunId: string;\n itemsCommitted: number;\n stripeItemsCreated: number;\n }>\n > {\n return this.post<{\n status: string;\n closeRunId: string;\n itemsCommitted: number;\n stripeItemsCreated: number;\n }>(`/billing/closes/${closeRunId}/commit`);\n },\n\n async getCloseRuns(params?: {\n org?: string;\n period?: string;\n }): Promise<ApiResponse<{ items: CloseRun[] }>> {\n const q = new URLSearchParams();\n if (params?.org) q.set('org', params.org);\n if (params?.period) q.set('period', params.period);\n const queryStr = q.toString() ? `?${q.toString()}` : '';\n return this.get<{ items: CloseRun[] }>(`/billing/closes${queryStr}`);\n },\n\n async getBillingWorklist(): Promise<ApiResponse<{ items: CloseExceptionRow[] }>> {\n return this.get<{ items: CloseExceptionRow[] }>('/billing/worklist');\n },\n\n async getBillingMargin(period?: string): Promise<ApiResponse<{ items: OrgMarginRow[] }>> {\n const q = period ? `?period=${encodeURIComponent(period)}` : '';\n return this.get<{ items: OrgMarginRow[] }>(`/billing/margin${q}`);\n },\n\n // Global search across sites, projects, deliverables, documents, invoices, routes, collections, assets\n async search(\n q: string,\n opts?: { siteId?: string; limit?: number },\n ): Promise<ApiResponse<SearchResults>> {\n const params = new URLSearchParams();\n params.set('q', q);\n if (opts?.siteId) params.set('site_id', opts.siteId);\n if (opts?.limit !== undefined) params.set('limit', String(opts.limit));\n return this.get<SearchResults>(`/search?${params.toString()}`);\n },\n };\n}\n","import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { createClient } from '@supabase/supabase-js';\n\nexport interface StoredCredentials {\n access_token: string;\n refresh_token: string;\n expires_at: number; // Unix timestamp in seconds\n email?: string;\n}\n\nexport function normalizeApiUrl(url: string): string {\n return url.trim().replace(/\\/+$/, '');\n}\n\nexport function getDefaultCredentialsPath(): string {\n if (process.env.CMS_CREDENTIALS_PATH) {\n return path.resolve(process.env.CMS_CREDENTIALS_PATH);\n }\n return path.join(os.homedir(), '.config', '407dev', 'credentials.json');\n}\n\nexport function loadAllCredentials(customPath?: string): Record<string, StoredCredentials> {\n const filePath = customPath || getDefaultCredentialsPath();\n if (!fs.existsSync(filePath)) {\n return {};\n }\n try {\n const raw = fs.readFileSync(filePath, 'utf-8');\n return JSON.parse(raw);\n } catch {\n return {};\n }\n}\n\nexport function getStoredCredentials(\n apiUrl: string,\n customPath?: string,\n): StoredCredentials | null {\n const all = loadAllCredentials(customPath);\n const normalized = normalizeApiUrl(apiUrl);\n return all[normalized] || null;\n}\n\nexport function saveStoredCredentials(\n apiUrl: string,\n creds: StoredCredentials,\n customPath?: string,\n): void {\n const filePath = customPath || getDefaultCredentialsPath();\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true, mode: 0o700 });\n } else {\n try {\n fs.chmodSync(dir, 0o700);\n } catch {\n // Ignore if chmod fails\n }\n }\n\n const all = loadAllCredentials(customPath);\n const normalized = normalizeApiUrl(apiUrl);\n all[normalized] = creds;\n\n fs.writeFileSync(filePath, JSON.stringify(all, null, 2), {\n encoding: 'utf-8',\n mode: 0o600,\n });\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n // Ignore if chmod fails\n }\n}\n\nexport function removeStoredCredentials(apiUrl: string, customPath?: string): boolean {\n const filePath = customPath || getDefaultCredentialsPath();\n if (!fs.existsSync(filePath)) {\n return false;\n }\n const all = loadAllCredentials(customPath);\n const normalized = normalizeApiUrl(apiUrl);\n if (!(normalized in all)) {\n return false;\n }\n delete all[normalized];\n fs.writeFileSync(filePath, JSON.stringify(all, null, 2), {\n encoding: 'utf-8',\n mode: 0o600,\n });\n try {\n fs.chmodSync(filePath, 0o600);\n } catch {\n // Ignore\n }\n return true;\n}\n\nexport async function refreshCredentialsIfNeeded(\n apiUrl: string,\n creds: StoredCredentials,\n customPath?: string,\n fetchFn: typeof fetch = fetch,\n): Promise<StoredCredentials> {\n const now = Math.floor(Date.now() / 1000);\n // If expires_at is more than 60s in the future, token is still valid\n if (creds.expires_at - now >= 60) {\n return creds;\n }\n\n // Token is expired or expiring soon; refresh via Supabase\n const normalized = normalizeApiUrl(apiUrl);\n const configRes = await fetchFn(`${normalized}/cli/config`);\n if (!configRes.ok) {\n throw new Error(\n `Failed to fetch CLI config from ${normalized} (status ${configRes.status}): ${configRes.statusText}`,\n );\n }\n const configData = (await configRes.json()) as {\n supabaseUrl?: string;\n supabaseAnonKey?: string;\n data?: { supabaseUrl?: string; supabaseAnonKey?: string };\n supabase_url?: string;\n supabase_anon_key?: string;\n };\n const supabaseUrl =\n configData.supabaseUrl || configData.data?.supabaseUrl || configData.supabase_url;\n const supabaseAnonKey =\n configData.supabaseAnonKey || configData.data?.supabaseAnonKey || configData.supabase_anon_key;\n\n if (!supabaseUrl || !supabaseAnonKey) {\n throw new Error(`Invalid CLI config returned from ${normalized}: missing Supabase credentials`);\n }\n\n const supabase = createClient(supabaseUrl, supabaseAnonKey, {\n auth: {\n persistSession: false,\n autoRefreshToken: false,\n },\n });\n\n const { data, error } = await supabase.auth.refreshSession({\n refresh_token: creds.refresh_token,\n });\n\n if (error || !data.session) {\n throw new Error(`Failed to refresh session: ${error?.message || 'unknown error'}`);\n }\n\n const updated: StoredCredentials = {\n access_token: data.session.access_token,\n refresh_token: data.session.refresh_token,\n expires_at: data.session.expires_at || Math.floor(Date.now() / 1000) + 3600,\n email: data.session.user?.email || creds.email,\n };\n\n saveStoredCredentials(apiUrl, updated, customPath);\n return updated;\n}\n","import crypto from 'node:crypto';\n\nexport function signDevJwt(\n secret: string,\n workspaceId = '00000000-0000-0000-0000-000000000001',\n): string {\n const header = { alg: 'HS256', typ: 'JWT' };\n const payload = {\n aud: 'authenticated',\n role: 'authenticated',\n sub: '00000000-0000-0000-0000-000000000003',\n email: 'dev@example.com',\n app_metadata: {\n workspace_id: workspaceId,\n role: 'owner',\n workspaces: {\n [workspaceId]: { role: 'owner' },\n },\n },\n user_metadata: { role: 'owner' },\n iat: Math.floor(Date.now() / 1000),\n exp: Math.floor(Date.now() / 1000) + 3600 * 24 * 365,\n };\n\n const encodedHeader = Buffer.from(JSON.stringify(header)).toString('base64url');\n const encodedPayload = Buffer.from(JSON.stringify(payload)).toString('base64url');\n const data = `${encodedHeader}.${encodedPayload}`;\n const signature = crypto.createHmac('sha256', secret).update(data).digest('base64url');\n\n return `${data}.${signature}`;\n}\n","import { DEFAULT_DEV_API_URL } from '@407dev/config';\nimport { getStoredCredentials, normalizeApiUrl, refreshCredentialsIfNeeded } from './credentials';\nimport { signDevJwt } from './jwt';\n\nexport async function resolveAuthToken(\n apiUrl: string = process.env.CMS_API_URL || DEFAULT_DEV_API_URL,\n customCredentialsPath?: string,\n): Promise<string> {\n // 1. Explicit env token\n const envToken = process.env.CMS_AUTH_TOKEN || process.env.SUPABASE_ACCESS_TOKEN;\n if (envToken?.trim()) {\n return envToken.trim();\n }\n\n // 2. Stored credentials\n const normalized = normalizeApiUrl(apiUrl);\n const creds = getStoredCredentials(normalized, customCredentialsPath);\n if (creds) {\n try {\n const refreshed = await refreshCredentialsIfNeeded(normalized, creds, customCredentialsPath);\n return refreshed.access_token;\n } catch {\n // If refresh fails, fall through to dev JWT or login prompt\n }\n }\n\n // 3. Localhost dev JWT\n if (normalized.includes('127.0.0.1') || normalized.includes('localhost')) {\n const secret =\n process.env.SUPABASE_JWT_SECRET || 'super-secret-jwt-token-with-at-least-32-characters-long';\n try {\n return await signDevJwt(secret);\n } catch {\n // Fall through to error\n }\n }\n\n // 4. Not logged in\n throw new Error(`Not logged in to ${normalized}. Run: cms login`);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nexport function loadEnvFiles(siteDir: string = process.cwd()): void {\n const envFiles = [\n path.join(siteDir, '.env'),\n path.join(siteDir, '.env.local'),\n path.join(siteDir, '.env.development'),\n path.join(process.cwd(), '.env'),\n path.join(process.cwd(), '.env.local'),\n ];\n\n const seen = new Set<string>();\n\n for (const file of envFiles) {\n const resolved = path.resolve(file);\n if (seen.has(resolved)) continue;\n seen.add(resolved);\n\n if (fs.existsSync(resolved)) {\n try {\n const content = fs.readFileSync(resolved, 'utf-8');\n for (const line of content.split('\\n')) {\n const trimmed = line.trim();\n if (!trimmed || trimmed.startsWith('#')) continue;\n const eqIdx = trimmed.indexOf('=');\n if (eqIdx > 0) {\n const key = trimmed.slice(0, eqIdx).trim();\n let val = trimmed.slice(eqIdx + 1).trim();\n if (\n (val.startsWith('\"') && val.endsWith('\"')) ||\n (val.startsWith(\"'\") && val.endsWith(\"'\"))\n ) {\n val = val.slice(1, -1);\n }\n if (process.env[key] === undefined) {\n process.env[key] = val;\n }\n }\n }\n } catch {\n // Ignore file read error\n }\n }\n }\n}\n","import { spawn } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport http from 'node:http';\nimport type { AddressInfo } from 'node:net';\nimport { DEFAULT_DEV_API_URL, DEFAULT_DEV_WEB_URL } from '@407dev/config';\nimport { normalizeApiUrl, saveStoredCredentials } from '../credentials';\n\nexport interface LoginCommandOptions {\n apiUrl?: string;\n openBrowser?: boolean; // Default true, false for testing\n timeoutMs?: number; // Default 300_000 (5 mins)\n}\n\nexport async function executeLogin(options: LoginCommandOptions = {}): Promise<string> {\n const apiUrl = normalizeApiUrl(options.apiUrl || process.env.CMS_API_URL || DEFAULT_DEV_API_URL);\n const openBrowser = options.openBrowser ?? true;\n const timeoutMs = options.timeoutMs ?? 300_000;\n\n // 1. Fetch public config to determine webUrl\n let webUrl = DEFAULT_DEV_WEB_URL;\n try {\n const configRes = await fetch(`${apiUrl}/cli/config`);\n if (configRes.ok) {\n const configJson = (await configRes.json()) as {\n webUrl?: string;\n data?: { webUrl?: string };\n web_url?: string;\n };\n webUrl =\n configJson.webUrl || configJson.data?.webUrl || configJson.web_url || DEFAULT_DEV_WEB_URL;\n }\n } catch {\n // Fall back to default\n }\n\n const state = crypto.randomBytes(16).toString('hex');\n\n return new Promise<string>((resolve, reject) => {\n let timeoutId: NodeJS.Timeout | null = null;\n\n const server = http.createServer((req, res) => {\n // Allow CORS for preflight\n res.setHeader('Access-Control-Allow-Origin', '*');\n res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type');\n\n if (req.method === 'OPTIONS') {\n res.writeHead(204);\n res.end();\n return;\n }\n\n if (req.method === 'POST' && req.url === '/callback') {\n let body = '';\n req.on('data', (chunk) => {\n body += chunk;\n });\n\n req.on('end', () => {\n let payload: Record<string, unknown> = {};\n const contentType = req.headers['content-type'] || '';\n if (contentType.includes('application/json')) {\n try {\n payload = JSON.parse(body);\n } catch {\n payload = {};\n }\n } else {\n const params = new URLSearchParams(body);\n payload = Object.fromEntries(params.entries());\n }\n\n if (!payload.state || payload.state !== state) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Invalid state parameter');\n return;\n }\n\n const accessToken =\n typeof payload.access_token === 'string' ? payload.access_token : undefined;\n const refreshToken =\n typeof payload.refresh_token === 'string' ? payload.refresh_token : undefined;\n const expiresAt = Number(payload.expires_at) || Math.floor(Date.now() / 1000) + 3600;\n const email = typeof payload.email === 'string' ? payload.email : undefined;\n\n if (!accessToken || !refreshToken) {\n res.writeHead(400, { 'Content-Type': 'text/plain' });\n res.end('Missing access_token or refresh_token in callback payload');\n return;\n }\n\n saveStoredCredentials(apiUrl, {\n access_token: accessToken,\n refresh_token: refreshToken,\n expires_at: expiresAt,\n email,\n });\n\n res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });\n res.end(`<!DOCTYPE html>\n<html>\n<head><title>Authenticated</title></head>\n<body style=\"font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #fafafa;\">\n <div style=\"background: #ffffff; padding: 2rem 3rem; border-radius: 8px; border: 1px solid #eaeaea; text-align: center; max-width: 420px; box-shadow: 0 4px 12px rgba(0,0,0,0.05);\">\n <h2 style=\"margin-top: 0; color: #111;\">Authenticated Successfully</h2>\n <p style=\"color: #666; font-size: 14px;\">You can now close this tab and return to your terminal.</p>\n </div>\n</body>\n</html>`);\n\n if (timeoutId) clearTimeout(timeoutId);\n server.close();\n\n const emailMsg = email ? ` as ${email}` : '';\n resolve(`⨠Logged in successfully${emailMsg} (${apiUrl})`);\n });\n return;\n }\n\n res.writeHead(404, { 'Content-Type': 'text/plain' });\n res.end('Not Found');\n });\n\n server.listen(0, '127.0.0.1', () => {\n const address = server.address() as AddressInfo;\n const port = address.port;\n const authorizeUrl = `${webUrl.replace(/\\/+$/, '')}/cli/authorize?port=${port}&state=${state}`;\n\n console.log(`\\nOpening browser to authenticate:\\n ${authorizeUrl}\\n`);\n\n if (openBrowser) {\n const cmd =\n process.platform === 'darwin'\n ? 'open'\n : process.platform === 'win32'\n ? 'start'\n : 'xdg-open';\n try {\n spawn(cmd, [authorizeUrl], { detached: true, stdio: 'ignore' }).unref();\n } catch {\n // Fall back to manual browser open\n }\n }\n\n timeoutId = setTimeout(() => {\n server.close();\n reject(new Error('Login timed out after 5 minutes. Please try again.'));\n }, timeoutMs);\n });\n\n server.on('error', (err) => {\n if (timeoutId) clearTimeout(timeoutId);\n reject(err);\n });\n });\n}\n","import { DEFAULT_DEV_API_URL } from '@407dev/config';\nimport { normalizeApiUrl, removeStoredCredentials } from '../credentials';\n\nexport interface LogoutCommandOptions {\n apiUrl?: string;\n customCredentialsPath?: string;\n}\n\nexport function executeLogout(options: LogoutCommandOptions = {}): string {\n const apiUrl = normalizeApiUrl(options.apiUrl || process.env.CMS_API_URL || DEFAULT_DEV_API_URL);\n const removed = removeStoredCredentials(apiUrl, options.customCredentialsPath);\n if (removed) {\n return `Logged out from ${apiUrl}.`;\n }\n return `Not currently logged in to ${apiUrl}.`;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { createApiClient } from '@407dev/api-client';\nimport { hashSchema } from '@407dev/blocks';\nimport { DEFAULT_DEV_API_URL } from '@407dev/config';\nimport { resolveAuthToken } from '../auth';\nimport { loadEnvFiles } from '../env';\nimport { extractProject } from '../extract/check';\nimport { loadManifest } from '../manifest-loader';\nimport { runEntryMigrations } from '../migrations';\nimport { StateStore } from '../state-store';\nimport type { ApiClient, PushCommandOptions, PushCommandResult } from '../types';\n\n/** Non-interactive shells can't confirm, so they must pass --yes explicitly rather than silently orphaning data. */\nasync function defaultConfirm(message: string): Promise<boolean> {\n if (!process.stdin.isTTY) {\n return false;\n }\n const readline = await import('node:readline/promises');\n const rl = readline.createInterface({ input: process.stdin, output: process.stdout });\n try {\n const answer = await rl.question(`${message} [y/N] `);\n return /^y(es)?$/i.test(answer.trim());\n } finally {\n rl.close();\n }\n}\n\n/** Previews which currently-synced fields/collections are absent from the new manifest; skips (rather than throws) if the client lacks getFields/getCollections. */\nasync function previewOrphans(\n apiClient: ApiClient,\n siteId: string,\n manifest: PushCommandOptions['manifest'],\n): Promise<{ fieldKeys: string[]; collectionKeys: string[] }> {\n if (typeof apiClient.getFields !== 'function' || !manifest) {\n return { fieldKeys: [], collectionKeys: [] };\n }\n\n const [fieldsRes, collectionsRes] = await Promise.all([\n apiClient.getFields(siteId),\n typeof apiClient.getCollections === 'function'\n ? apiClient.getCollections(siteId)\n : Promise.resolve({ ok: true as const, data: [] }),\n ]);\n\n const existingFields = fieldsRes.ok ? fieldsRes.data || [] : [];\n const existingCollections = collectionsRes.ok ? collectionsRes.data || [] : [];\n\n return {\n fieldKeys: existingFields\n .filter((f) => !f.orphaned_at && !(f.key in manifest.fields))\n .map((f) => f.key),\n collectionKeys: existingCollections\n .filter((c) => !manifest.collections?.[c.key])\n .map((c) => c.key),\n };\n}\n\nexport async function executePush(options: PushCommandOptions): Promise<PushCommandResult> {\n const siteDir = options.siteDir || process.cwd();\n loadEnvFiles(siteDir);\n\n const siteId = options.siteId || process.env.CMS_SITE_ID;\n if (!siteId) {\n return {\n success: false,\n hash: '',\n warnings: [],\n errors: ['site ID is required. Pass --site <id> or set CMS_SITE_ID in .env.'],\n };\n }\n\n const { configPath, force, dryRun, check, yes, noSync, statePath, renames, migrations } = options;\n const warnings: string[] = [];\n const errors: string[] = [];\n\n // 1. Manifest resolution (extractor by default, loadManifest fallback)\n let manifest = options.manifest;\n\n if (!manifest) {\n const srcDir = path.join(siteDir, 'src');\n const hasSourceDir = fs.existsSync(srcDir) && fs.statSync(srcDir).isDirectory();\n\n if (hasSourceDir && !configPath) {\n // Use static AST extraction\n try {\n const extractResult = await extractProject(siteDir, { noSync });\n\n for (const w of extractResult.warnings) {\n warnings.push(`${w.file}:${w.line}:${w.column}: ${w.message}`);\n }\n\n if (extractResult.errors.length > 0) {\n const formattedErrors = extractResult.errors.map(\n (e) => `${e.file}:${e.line}:${e.column}: ${e.message}`,\n );\n if (check) {\n return {\n success: false,\n hash: '',\n warnings,\n errors: formattedErrors,\n };\n }\n errors.push(...formattedErrors);\n }\n\n manifest = extractResult.manifest;\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n success: false,\n hash: '',\n warnings,\n errors: [`Extraction failed: ${msg}`],\n };\n }\n } else {\n // Fallback for hand-written cms.manifest.ts\n try {\n manifest = await loadManifest(configPath);\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n return {\n success: false,\n hash: '',\n warnings,\n errors: [msg],\n };\n }\n }\n }\n\n // 2. Compute canonical hash\n const hash = hashSchema(manifest);\n\n // 3. State store check\n const stateStore = new StateStore(statePath);\n const lastHash = stateStore.getSiteManifestHash(siteId);\n\n if (lastHash === hash && !force) {\n return {\n success: true,\n skipped: true,\n hash,\n warnings: ['Manifest is unchanged. Use --force to push anyway.'],\n errors: [],\n };\n }\n\n const baseUrl = process.env.CMS_API_URL || DEFAULT_DEV_API_URL;\n let apiClient = options.apiClient;\n if (!apiClient) {\n try {\n const authToken = await resolveAuthToken(baseUrl);\n apiClient = createApiClient({\n baseUrl,\n authToken,\n });\n } catch (err) {\n if (!dryRun) {\n const message =\n err instanceof Error ? err.message : 'Authentication required. Run: cms login';\n return {\n success: false,\n hash,\n warnings,\n errors: [message],\n };\n }\n }\n }\n\n if (dryRun) {\n const dryRunWarnings = ['Dry run complete. No changes were applied.'];\n\n if (apiClient && manifest?.collections) {\n try {\n const [collectionsRes, statsRes] = await Promise.all([\n typeof apiClient.getCollections === 'function'\n ? apiClient.getCollections(siteId)\n : Promise.resolve({ ok: true as const, data: [] }),\n typeof apiClient.getCollectionStats === 'function'\n ? apiClient.getCollectionStats(siteId)\n : Promise.resolve({ ok: true as const, data: {} }),\n ]);\n\n const existingCollections = collectionsRes.ok ? collectionsRes.data || [] : [];\n const existingStats: Record<\n string,\n { draft: number; published: number; scheduled: number }\n > =\n statsRes.ok && statsRes.data\n ? (statsRes.data as Record<\n string,\n { draft: number; published: number; scheduled: number }\n >)\n : {};\n\n const wouldSeed: Array<{ key: string; count: number }> = [];\n\n for (const [key, coll] of Object.entries(manifest.collections)) {\n if (coll.seed && coll.seed.length > 0) {\n const existing = existingCollections.find((c) => c.key === key);\n const stats = existingStats[key];\n const entryCount = stats ? stats.draft + stats.published + stats.scheduled : 0;\n\n if (!existing || (!existing.seeded_at && entryCount === 0)) {\n wouldSeed.push({ key, count: coll.seed.length });\n }\n }\n }\n\n if (wouldSeed.length > 0) {\n const list = wouldSeed\n .map((s) => `${s.key} (${s.count} ${s.count === 1 ? 'entry' : 'entries'})`)\n .join(', ');\n dryRunWarnings.push(`Would seed ${list}.`);\n }\n } catch {\n // Ignore API query failures during dry-run\n }\n }\n\n return {\n success: true,\n hash,\n warnings: dryRunWarnings,\n errors: [],\n };\n }\n\n if (!apiClient) {\n return {\n success: false,\n hash,\n warnings,\n errors: ['Authentication required. Run: cms login'],\n };\n }\n\n // 4.5 Preview orphans and confirm before applying, unless --yes was passed\n if (!yes) {\n const { fieldKeys, collectionKeys } = await previewOrphans(apiClient, siteId, manifest);\n if (fieldKeys.length > 0 || collectionKeys.length > 0) {\n const parts: string[] = [];\n if (fieldKeys.length > 0) parts.push(`${fieldKeys.length} field(s): ${fieldKeys.join(', ')}`);\n if (collectionKeys.length > 0) {\n parts.push(`${collectionKeys.length} collection(s): ${collectionKeys.join(', ')}`);\n }\n\n const confirmFn = options.confirm || defaultConfirm;\n const proceed = await confirmFn(\n `This push will orphan ${parts.join('; ')}. Stored values are preserved but will stop syncing from code. Continue?`,\n );\n\n if (!proceed) {\n return {\n success: false,\n aborted: true,\n hash,\n warnings,\n errors: [\n 'Push aborted: orphaned fields/collections were not confirmed. Re-run with --yes to skip this prompt.',\n ],\n };\n }\n }\n }\n\n // 5. Push manifest to API\n const pushRes = await apiClient.pushManifest(siteId, manifest, renames);\n if (!pushRes.ok || !pushRes.data) {\n return {\n success: false,\n hash,\n warnings,\n errors: [pushRes.error || 'Failed to push manifest to CMS API'],\n };\n }\n\n const report = pushRes.data;\n if (report.warnings && report.warnings.length > 0) {\n warnings.push(...report.warnings);\n }\n\n // 6. Run entry migrations if provided\n let migrationsRun = 0;\n if (migrations && migrations.length > 0) {\n const migrationResult = await runEntryMigrations(apiClient, siteId, migrations);\n migrationsRun = migrationResult.migratedCount;\n if (migrationResult.errors.length > 0) {\n errors.push(...migrationResult.errors);\n }\n }\n\n // 7. Save state\n stateStore.saveSiteManifestHash(siteId, hash);\n\n return {\n success: errors.length === 0,\n hash,\n report,\n migrationsRun,\n warnings,\n errors,\n };\n}\n","import { existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport type { SiteManifest } from '@407dev/api-client';\n\nexport async function loadManifest(pathOrObject?: string | SiteManifest): Promise<SiteManifest> {\n if (typeof pathOrObject === 'object' && pathOrObject !== null) {\n if (pathOrObject.manifestVersion !== 1) {\n throw new Error(`Unsupported manifestVersion: ${pathOrObject.manifestVersion}`);\n }\n return pathOrObject;\n }\n\n const manifestPath = pathOrObject\n ? resolve(pathOrObject)\n : [\n resolve(process.cwd(), 'cms.manifest.ts'),\n resolve(process.cwd(), 'cms.manifest.js'),\n resolve(process.cwd(), 'cms.config.ts'),\n resolve(process.cwd(), 'cms.config.js'),\n ].find((p) => existsSync(p));\n\n if (!manifestPath || !existsSync(manifestPath)) {\n throw new Error(`Manifest file not found. Expected cms.manifest.ts in ${process.cwd()}`);\n }\n\n const fileUrl = pathToFileURL(manifestPath).href;\n const mod = (await import(fileUrl)) as {\n default?: SiteManifest | { manifest?: SiteManifest };\n manifest?: SiteManifest;\n };\n\n const manifest =\n mod.manifest ||\n (mod.default && 'manifestVersion' in mod.default\n ? (mod.default as SiteManifest)\n : mod.default?.manifest);\n\n if (!manifest) {\n throw new Error(`No manifest export found in ${manifestPath}`);\n }\n\n if (manifest.manifestVersion !== 1) {\n throw new Error(`Unsupported manifestVersion: ${manifest.manifestVersion}`);\n }\n\n return manifest;\n}\n","import type { ApiClient, EntryMigration } from './types';\n\nexport async function runEntryMigrations(\n apiClient: ApiClient,\n siteId: string,\n migrations: EntryMigration[],\n): Promise<{ migratedCount: number; errors: string[] }> {\n let migratedCount = 0;\n const errors: string[] = [];\n\n for (const m of migrations) {\n const listRes = await apiClient.listEntries(siteId, m.collection);\n if (!listRes.ok || !listRes.data) {\n errors.push(`Failed to list entries for collection '${m.collection}': ${listRes.error}`);\n continue;\n }\n\n const entriesToMigrate = listRes.data.filter((e) => e.schema_version === m.fromVersion);\n\n for (const entry of entriesToMigrate) {\n try {\n const migratedContent = m.migrate(entry.content || {});\n const updateRes = await apiClient.updateEntry(entry.id, {\n content: migratedContent,\n expected_updated_at: entry.updated_at,\n });\n\n if (updateRes.ok) {\n migratedCount++;\n } else {\n errors.push(\n `Failed to migrate entry '${entry.slug}' in '${m.collection}': ${updateRes.error}`,\n );\n }\n } catch (err) {\n errors.push(\n `Exception in migration for entry '${entry.slug}' in '${m.collection}': ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n return { migratedCount, errors };\n}\n","import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport type { CmsCliState } from './types';\n\nexport class StateStore {\n private filePath: string;\n\n constructor(customPath?: string) {\n this.filePath = customPath ? resolve(customPath) : resolve(process.cwd(), '.cms', 'state.json');\n }\n\n load(): CmsCliState {\n if (!existsSync(this.filePath)) {\n return { sites: {} };\n }\n\n try {\n const content = readFileSync(this.filePath, 'utf-8');\n return JSON.parse(content) as CmsCliState;\n } catch {\n return { sites: {} };\n }\n }\n\n getSiteManifestHash(siteId: string): string | null {\n const data = this.load();\n return data.sites[siteId]?.manifestHash || null;\n }\n\n saveSiteManifestHash(siteId: string, hash: string): void {\n const data = this.load();\n if (!data.sites) {\n data.sites = {};\n }\n\n data.sites[siteId] = {\n manifestHash: hash,\n lastPushedAt: new Date().toISOString(),\n };\n\n try {\n const dir = dirname(this.filePath);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n writeFileSync(this.filePath, JSON.stringify(data, null, 2), 'utf-8');\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n console.warn(`[CMS Warning] Could not save CLI state: ${message}`);\n }\n }\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nexport interface SkillsInstallOptions {\n targetDir?: string;\n force?: boolean;\n sourceDir?: string;\n}\n\nexport interface SkillInstallResult {\n name: string;\n status: 'installed' | 'updated' | 'unchanged' | 'conflict';\n conflicts: string[];\n}\n\n// Walks up rather than using a fixed relative path: this file runs from src/commands in tests\n// but from the single bundled dist/index.js when published.\nexport function resolveBundledSkillsDir(): string {\n let dir = path.dirname(fileURLToPath(import.meta.url));\n while (true) {\n const pkgPath = path.join(dir, 'package.json');\n if (fs.existsSync(pkgPath)) {\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')) as { name?: string };\n if (pkg.name === '@407dev/cli') return path.join(dir, 'skills');\n }\n const parent = path.dirname(dir);\n if (parent === dir) throw new Error('Could not locate bundled skills for @407dev/cli');\n dir = parent;\n }\n}\n\nexport function listBundledSkills(sourceDir = resolveBundledSkillsDir()): string[] {\n return fs\n .readdirSync(sourceDir, { withFileTypes: true })\n .filter((d) => d.isDirectory() && fs.existsSync(path.join(sourceDir, d.name, 'SKILL.md')))\n .map((d) => d.name)\n .sort();\n}\n\nfunction listFilesRecursive(root: string, rel = ''): string[] {\n const files: string[] = [];\n for (const entry of fs.readdirSync(path.join(root, rel), { withFileTypes: true })) {\n const entryRel = path.join(rel, entry.name);\n if (entry.isDirectory()) files.push(...listFilesRecursive(root, entryRel));\n else if (entry.isFile()) files.push(entryRel);\n }\n return files.sort();\n}\n\nexport function installSkills(options: SkillsInstallOptions = {}): SkillInstallResult[] {\n const sourceDir = options.sourceDir || resolveBundledSkillsDir();\n const destRoot = path.join(options.targetDir || process.cwd(), '.claude', 'skills');\n const results: SkillInstallResult[] = [];\n\n for (const name of listBundledSkills(sourceDir)) {\n const srcSkillDir = path.join(sourceDir, name);\n const destSkillDir = path.join(destRoot, name);\n const existedBefore = fs.existsSync(destSkillDir);\n const conflicts: string[] = [];\n let wroteAny = false;\n\n for (const rel of listFilesRecursive(srcSkillDir)) {\n const srcContent = fs.readFileSync(path.join(srcSkillDir, rel));\n const destPath = path.join(destSkillDir, rel);\n\n if (fs.existsSync(destPath)) {\n if (fs.readFileSync(destPath).equals(srcContent)) continue;\n if (!options.force) {\n conflicts.push(rel);\n continue;\n }\n }\n\n fs.mkdirSync(path.dirname(destPath), { recursive: true });\n fs.writeFileSync(destPath, srcContent);\n wroteAny = true;\n }\n\n let status: SkillInstallResult['status'] = 'unchanged';\n if (conflicts.length > 0) status = 'conflict';\n else if (!existedBefore) status = 'installed';\n else if (wroteAny) status = 'updated';\n\n results.push({ name, status, conflicts });\n }\n\n return results;\n}\n\nexport function executeSkillsInstall(options: SkillsInstallOptions = {}): string {\n const targetDir = path.resolve(options.targetDir || process.cwd());\n const results = installSkills({ ...options, targetDir });\n\n const lines = [`Agent skills in ${path.join(targetDir, '.claude', 'skills')}:`];\n for (const r of results) {\n if (r.status === 'conflict') {\n lines.push(\n ` ! ${r.name}: kept local edits to ${r.conflicts.join(', ')} (re-run with --force to overwrite)`,\n );\n } else {\n lines.push(` â ${r.name} (${r.status})`);\n }\n }\n return lines.join('\\n');\n}\n\nexport function executeSkillsList(): string {\n return ['Bundled agent skills:', ...listBundledSkills().map((n) => ` - ${n}`)].join('\\n');\n}\n","import { DEFAULT_DEV_API_URL } from '@407dev/config';\nimport { getStoredCredentials, normalizeApiUrl, refreshCredentialsIfNeeded } from '../credentials';\n\nexport interface WhoamiCommandOptions {\n apiUrl?: string;\n customCredentialsPath?: string;\n}\n\nexport async function executeWhoami(options: WhoamiCommandOptions = {}): Promise<string> {\n const apiUrl = normalizeApiUrl(options.apiUrl || process.env.CMS_API_URL || DEFAULT_DEV_API_URL);\n const creds = getStoredCredentials(apiUrl, options.customCredentialsPath);\n\n if (creds) {\n try {\n const refreshed = await refreshCredentialsIfNeeded(\n apiUrl,\n creds,\n options.customCredentialsPath,\n );\n const email = refreshed.email || 'authenticated user';\n return `Logged in to ${apiUrl} as ${email}`;\n } catch {\n return `Logged in to ${apiUrl} (stored credentials expired or refresh failed)`;\n }\n }\n\n const envToken = process.env.CMS_AUTH_TOKEN || process.env.SUPABASE_ACCESS_TOKEN;\n if (envToken?.trim()) {\n return `Logged in to ${apiUrl} via environment token (CMS_AUTH_TOKEN)`;\n }\n\n if (apiUrl.includes('127.0.0.1') || apiUrl.includes('localhost')) {\n return `Using local development credentials for ${apiUrl}`;\n }\n\n return `Not logged in to ${apiUrl}. Run \"cms login\" to authenticate.`;\n}\n"],"mappings":"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","names":["fs","fileURLToPath","fs","path","ts","ts","fs","path","ts","sourceFile","getLocation","parts","packageName","subpathParts","subpath","path","fs","ts","ts","walk","ts","ts","fs","path","path","fs","fs","path","path","options","fs","path","crypto","fs","path","fs","path","crypto","crypto","resolve","fs","path","existsSync","resolve","readline","path","fs","fs","path","targetDir","fileURLToPath","fs"]}
|