@josui/token-studio 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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cli.ts","../src/shared/constants.ts","../src/shared/path-utils.ts","../src/shared/config.ts","../src/server/index.ts","../src/server/api.ts","../src/shared/schemas.ts","../src/shared/validators.ts","../src/server/repository.ts","../src/shared/document.ts","../src/server/fs-utils.ts","../src/server/terrazzo.ts"],"sourcesContent":["import path from 'node:path';\nimport process from 'node:process';\nimport { Command } from 'commander';\nimport open from 'open';\nimport { DEFAULT_TOKENS_RELATIVE_DIR, DEFAULT_TERRAZZO_RELATIVE_PATH } from './shared/constants';\nimport { inferTerrazzoPathFromTokensDir } from './shared/path-utils';\nimport { findConfigFile, findNearestTokensRoot, readConfig } from './shared/config';\nimport { startServer } from './server/index';\n\nasync function run(): Promise<void> {\n const program = new Command();\n\n program\n .name('josui-token-studio')\n .description('Launch local token CRUD editor')\n .option('--cwd <path>', 'Working directory', process.cwd())\n .option('--config <path>', 'Path to token-studio config JSON')\n .option('--tokens-dir <path>', 'Tokens directory path')\n .option('--port <number>', 'Port to run local server', '4598')\n .option('--no-open', 'Do not open browser automatically');\n\n program.parse(process.argv);\n const options = program.opts<{\n cwd: string;\n config?: string;\n tokensDir?: string;\n port: string;\n open: boolean;\n }>();\n\n const cwd = path.resolve(options.cwd);\n const explicitConfigPath = options.config ? path.resolve(cwd, options.config) : null;\n const discoveredConfigPath = explicitConfigPath ?? (await findConfigFile(cwd));\n const config = discoveredConfigPath ? await readConfig(discoveredConfigPath) : {};\n const configBaseDir = discoveredConfigPath ? path.dirname(discoveredConfigPath) : cwd;\n\n const discoveredTokensDir = await findNearestTokensRoot(cwd);\n const configTokensDir = config.tokensDir\n ? path.resolve(configBaseDir, config.tokensDir)\n : undefined;\n const tokensDir = path.resolve(\n options.tokensDir ??\n configTokensDir ??\n discoveredTokensDir ??\n path.join(cwd, DEFAULT_TOKENS_RELATIVE_DIR)\n );\n\n const defaultTerrazzoPath = path.resolve(\n config.terrazzoPath\n ? path.resolve(configBaseDir, config.terrazzoPath)\n : path.join(cwd, DEFAULT_TERRAZZO_RELATIVE_PATH)\n );\n const terrazzoPath = inferTerrazzoPathFromTokensDir(tokensDir, defaultTerrazzoPath);\n\n const server = startServer({\n tokensDir,\n terrazzoPath,\n port: Number(options.port),\n webDir: path.resolve(import.meta.dirname, '..', 'dist', 'web'),\n });\n\n process.stdout.write(`Token Studio running at ${server.url}\\n`);\n if (discoveredConfigPath) {\n process.stdout.write(`config: ${discoveredConfigPath}\\n`);\n }\n process.stdout.write(`tokensDir: ${tokensDir}\\n`);\n process.stdout.write(`terrazzo: ${terrazzoPath}\\n`);\n\n if (options.open) {\n await open(server.url);\n }\n\n const shutdown = async (): Promise<void> => {\n await server.stop();\n process.exit(0);\n };\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n}\n\nvoid run();\n","import type { SupportedTokenType } from './types';\n\nexport const SUPPORTED_TYPES: SupportedTokenType[] = [\n 'color',\n 'dimension',\n 'duration',\n 'cubicBezier',\n 'fontFamily',\n 'number',\n 'string',\n];\n\nexport const DEFAULT_TOKENS_RELATIVE_DIR = 'packages/tokens/src/tokens';\nexport const DEFAULT_TERRAZZO_RELATIVE_PATH = 'packages/tokens/terrazzo.config.mjs';\n","import path from 'node:path';\n\nexport function normalizeCategoryName(name: string): string {\n return name.trim().toLowerCase();\n}\n\nexport function categoryFileName(name: string): string {\n return `${name}.json`;\n}\n\nexport function validateCategoryName(name: string): string | null {\n if (!name) {\n return 'Category name is required';\n }\n\n if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) {\n return 'Category name must be kebab-case';\n }\n\n return null;\n}\n\nexport function resolveDefaultPaths(cwd: string): {\n tokensDir: string;\n terrazzoPath: string;\n} {\n return {\n tokensDir: path.join(cwd, 'packages/tokens/src/tokens'),\n terrazzoPath: path.join(cwd, 'packages/tokens/terrazzo.config.mjs'),\n };\n}\n\nexport function inferTerrazzoPathFromTokensDir(tokensDir: string, fallback: string): string {\n const normalized = path.normalize(tokensDir);\n const suffix = path.normalize(path.join('src', 'tokens'));\n\n if (normalized.endsWith(suffix)) {\n return path.join(normalized, '..', '..', 'terrazzo.config.mjs');\n }\n\n return fallback;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nconst CONFIG_FILE_NAMES = ['token-studio.config.json', '.token-studio.json'] as const;\n\nexport interface TokenStudioConfig {\n tokensDir?: string;\n terrazzoPath?: string;\n}\n\nasync function exists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function findConfigFile(startDir: string): Promise<string | null> {\n let currentDir = path.resolve(startDir);\n\n while (true) {\n for (const fileName of CONFIG_FILE_NAMES) {\n const candidate = path.join(currentDir, fileName);\n if (await exists(candidate)) {\n return candidate;\n }\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n break;\n }\n\n currentDir = parentDir;\n }\n\n return null;\n}\n\nexport async function readConfig(configPath: string): Promise<TokenStudioConfig> {\n const content = await fs.readFile(configPath, 'utf8');\n const parsed = JSON.parse(content) as TokenStudioConfig;\n return parsed;\n}\n\nexport async function findNearestTokensRoot(startDir: string): Promise<string | null> {\n let currentDir = path.resolve(startDir);\n\n while (true) {\n const candidate = path.join(currentDir, 'packages/tokens/src/tokens');\n if (await exists(candidate)) {\n return candidate;\n }\n\n const parentDir = path.dirname(currentDir);\n if (parentDir === currentDir) {\n break;\n }\n\n currentDir = parentDir;\n }\n\n return null;\n}\n","import { serve } from '@hono/node-server';\nimport path from 'node:path';\nimport { createApi } from './api';\nimport { TokenRepository } from './repository';\n\nexport interface StartServerOptions {\n tokensDir: string;\n terrazzoPath: string;\n port: number;\n webDir: string;\n}\n\nexport function startServer(options: StartServerOptions): {\n stop: () => Promise<void>;\n url: string;\n} {\n const repository = new TokenRepository({\n tokensDir: options.tokensDir,\n terrazzoPath: options.terrazzoPath,\n });\n const app = createApi(repository, path.resolve(options.webDir));\n\n const server = serve({\n fetch: app.fetch,\n port: options.port,\n });\n\n return {\n url: `http://127.0.0.1:${options.port}`,\n stop: async () => {\n await new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error) {\n reject(error);\n return;\n }\n resolve();\n });\n });\n },\n };\n}\n","import path from 'node:path';\nimport fs from 'node:fs/promises';\nimport { Hono } from 'hono';\nimport { serveStatic } from '@hono/node-server/serve-static';\nimport { createCategorySchema, saveCategorySchema, validateSchema } from '../shared/schemas';\nimport { validateCategoryDocument } from '../shared/validators';\nimport type { TokenRepository } from './repository';\n\nexport function createApi(repository: TokenRepository, webDir: string): Hono {\n const app = new Hono();\n\n app.get('/api/health', (context) => {\n return context.json({\n status: 'ok',\n tokensDir: repository.tokensDir,\n terrazzoPath: repository.terrazzoPath,\n });\n });\n\n app.get('/api/categories', async (context) => {\n const categories = await repository.listCategories();\n return context.json({ categories });\n });\n\n app.get('/api/categories/:name', async (context) => {\n const name = context.req.param('name');\n try {\n const category = await repository.getCategory(name);\n return context.json(category);\n } catch (error) {\n return context.json({ error: (error as Error).message }, 404);\n }\n });\n\n app.post('/api/categories', async (context) => {\n const body = await context.req.json();\n const parsed = createCategorySchema.safeParse(body);\n\n if (!parsed.success) {\n return context.json({ error: parsed.error.issues[0]?.message ?? 'Invalid request' }, 400);\n }\n\n try {\n const created = await repository.createCategory(parsed.data.name, parsed.data.type);\n return context.json(created, 201);\n } catch (error) {\n return context.json({ error: (error as Error).message }, 400);\n }\n });\n\n app.put('/api/categories/:name', async (context) => {\n const name = context.req.param('name');\n const body = await context.req.json();\n const parsed = saveCategorySchema.safeParse(body);\n\n if (!parsed.success) {\n return context.json({ error: parsed.error.issues[0]?.message ?? 'Invalid request' }, 400);\n }\n\n try {\n const saved = await repository.saveCategory(name, parsed.data.document);\n return context.json(saved);\n } catch (error) {\n return context.json({ error: (error as Error).message }, 400);\n }\n });\n\n app.delete('/api/categories/:name', async (context) => {\n const name = context.req.param('name');\n await repository.deleteCategory(name);\n return context.body(null, 204);\n });\n\n app.post('/api/validate', async (context) => {\n const body = await context.req.json();\n const parsed = validateSchema.safeParse(body);\n if (!parsed.success) {\n return context.json({ error: parsed.error.issues[0]?.message ?? 'Invalid request' }, 400);\n }\n\n const result = validateCategoryDocument(parsed.data.name, parsed.data.document);\n return context.json(result);\n });\n\n app.use('*', serveStatic({ root: webDir }));\n app.get('*', async (context) => {\n const requestPath = context.req.path;\n if (requestPath.startsWith('/api/')) {\n return context.notFound();\n }\n if (path.extname(requestPath)) {\n return context.notFound();\n }\n\n const indexPath = path.join(webDir, 'index.html');\n try {\n const html = await fs.readFile(indexPath, 'utf8');\n return context.html(html);\n } catch {\n return context.text(\n 'Token Studio web assets are missing. Run `pnpm --filter @josui/token-studio build` first.',\n 503\n );\n }\n });\n\n return app;\n}\n","import { z } from 'zod';\nimport { SUPPORTED_TYPES } from './constants';\n\nconst supportedTypes = z.enum(SUPPORTED_TYPES);\n\nexport const createCategorySchema = z.object({\n name: z.string().trim().min(1),\n type: supportedTypes,\n});\n\nexport const saveCategorySchema = z.object({\n document: z.record(z.string(), z.unknown()),\n});\n\nexport const validateSchema = z.object({\n name: z.string().trim().min(1),\n document: z.record(z.string(), z.unknown()),\n});\n","import type { SupportedTokenType, ValidationResult } from './types';\nimport { SUPPORTED_TYPES } from './constants';\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isReferenceValue(value: unknown): boolean {\n return typeof value === 'string' && /^\\{[a-z0-9.-]+(?:\\.[a-z0-9.-]+)*\\}$/i.test(value);\n}\n\nfunction isSupportedType(type: unknown): type is SupportedTokenType {\n return typeof type === 'string' && SUPPORTED_TYPES.includes(type as SupportedTokenType);\n}\n\nfunction validateTypedLiteral(type: SupportedTokenType, value: unknown): string | null {\n if (isReferenceValue(value)) {\n return null;\n }\n\n switch (type) {\n case 'color': {\n if (!isObject(value)) {\n return 'Color value must be an object';\n }\n if (typeof value.colorSpace !== 'string') {\n return 'Color value needs colorSpace';\n }\n if (!Array.isArray(value.components) || value.components.some((c) => typeof c !== 'number')) {\n return 'Color value needs numeric components array';\n }\n return null;\n }\n case 'dimension':\n case 'duration': {\n if (!isObject(value)) {\n return `${type} value must be an object`;\n }\n if (typeof value.value !== 'number') {\n return `${type} value needs numeric value`;\n }\n if (typeof value.unit !== 'string') {\n return `${type} value needs unit`;\n }\n return null;\n }\n case 'cubicBezier': {\n if (!Array.isArray(value) || value.length !== 4 || value.some((n) => typeof n !== 'number')) {\n return 'cubicBezier value must be an array of four numbers';\n }\n return null;\n }\n case 'fontFamily': {\n if (typeof value === 'string') {\n return null;\n }\n if (Array.isArray(value) && value.every((part) => typeof part === 'string')) {\n return null;\n }\n return 'fontFamily value must be string or string[]';\n }\n case 'number': {\n if (typeof value !== 'number') {\n return 'number token must have numeric value';\n }\n return null;\n }\n case 'string': {\n if (typeof value !== 'string') {\n return 'string token must have string value';\n }\n return null;\n }\n default:\n return 'Unsupported token type';\n }\n}\n\ninterface TraverseContext {\n inheritedType?: SupportedTokenType;\n}\n\nexport function validateCategoryDocument(\n name: string,\n document: Record<string, unknown>\n): ValidationResult {\n const issues: ValidationResult['issues'] = [];\n\n const rootKeys = Object.keys(document);\n if (rootKeys.length !== 1) {\n issues.push({ path: '$', message: 'Document must contain exactly one root key' });\n return { valid: false, issues };\n }\n\n const [rootKey] = rootKeys;\n if (rootKey !== name) {\n issues.push({ path: '$', message: `Root key must match category name \"${name}\"` });\n }\n\n const rootValue = document[rootKey];\n if (!isObject(rootValue)) {\n issues.push({ path: rootKey, message: 'Root value must be an object' });\n return { valid: false, issues };\n }\n\n function traverse(node: Record<string, unknown>, path: string, context: TraverseContext): void {\n const localType = isSupportedType(node.$type) ? node.$type : context.inheritedType;\n const hasValue = Object.prototype.hasOwnProperty.call(node, '$value');\n\n if (hasValue) {\n if (!localType) {\n issues.push({\n path,\n message: 'Token must define $type directly or inherit one from parent',\n });\n } else {\n const typedIssue = validateTypedLiteral(localType, node.$value);\n if (typedIssue) {\n issues.push({ path: `${path}.$value`, message: typedIssue });\n }\n }\n\n if (Object.prototype.hasOwnProperty.call(node, '$extensions')) {\n const extensions = node.$extensions;\n if (!isObject(extensions)) {\n issues.push({ path: `${path}.$extensions`, message: '$extensions must be an object' });\n } else if (Object.prototype.hasOwnProperty.call(extensions, 'mode')) {\n const mode = extensions.mode;\n if (!isObject(mode)) {\n issues.push({ path: `${path}.$extensions.mode`, message: 'mode must be an object' });\n } else {\n for (const modeKey of ['light', 'dark'] as const) {\n if (Object.prototype.hasOwnProperty.call(mode, modeKey) && localType) {\n const typedIssue = validateTypedLiteral(localType, mode[modeKey]);\n if (typedIssue) {\n issues.push({\n path: `${path}.$extensions.mode.${modeKey}`,\n message: typedIssue,\n });\n }\n }\n }\n }\n }\n }\n }\n\n for (const [key, value] of Object.entries(node)) {\n if (key.startsWith('$')) {\n continue;\n }\n\n if (!isObject(value)) {\n issues.push({ path: `${path}.${key}`, message: 'Group/token nodes must be objects' });\n continue;\n }\n\n traverse(value, `${path}.${key}`, {\n inheritedType: localType,\n });\n }\n }\n\n traverse(rootValue, rootKey, {\n inheritedType: isSupportedType(rootValue.$type) ? rootValue.$type : undefined,\n });\n\n return {\n valid: issues.length === 0,\n issues,\n };\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport {\n categoryFileName,\n normalizeCategoryName,\n validateCategoryName,\n} from '../shared/path-utils';\nimport type { CategoryDocument, CategorySummary, SupportedTokenType } from '../shared/types';\nimport { validateCategoryDocument } from '../shared/validators';\nimport { flattenTokens } from '../shared/document';\nimport { ensureDirectory, readJsonFile, removeFileIfExists, writeJsonAtomic } from './fs-utils';\nimport { syncTerrazzoTokens } from './terrazzo';\n\nexport interface RepositoryOptions {\n tokensDir: string;\n terrazzoPath: string;\n}\n\nexport class TokenRepository {\n constructor(private readonly options: RepositoryOptions) {}\n\n get tokensDir(): string {\n return this.options.tokensDir;\n }\n\n get terrazzoPath(): string {\n return this.options.terrazzoPath;\n }\n\n async listCategories(): Promise<CategorySummary[]> {\n await ensureDirectory(this.tokensDir);\n\n const entries = await fs.readdir(this.tokensDir, { withFileTypes: true });\n const categories: CategorySummary[] = [];\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith('.json')) {\n continue;\n }\n\n const name = entry.name.replace(/\\.json$/, '');\n categories.push({\n name,\n fileName: entry.name,\n path: path.join(this.tokensDir, entry.name),\n });\n }\n\n return categories.sort((a, b) => a.name.localeCompare(b.name));\n }\n\n private categoryPath(name: string): string {\n return path.join(this.tokensDir, categoryFileName(name));\n }\n\n async getCategory(name: string): Promise<CategoryDocument> {\n const normalized = normalizeCategoryName(name);\n const filePath = this.categoryPath(normalized);\n const document = await readJsonFile(filePath);\n\n return {\n name: normalized,\n document,\n tokens: flattenTokens(document),\n };\n }\n\n async createCategory(name: string, type: SupportedTokenType): Promise<CategoryDocument> {\n const normalized = normalizeCategoryName(name);\n const nameError = validateCategoryName(normalized);\n if (nameError) {\n throw new Error(nameError);\n }\n\n const filePath = this.categoryPath(normalized);\n\n try {\n await fs.access(filePath);\n throw new Error(`Category ${normalized} already exists`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n\n const document: Record<string, unknown> = {\n [normalized]: {\n $type: type,\n },\n };\n\n const validation = validateCategoryDocument(normalized, document);\n if (!validation.valid) {\n throw new Error(validation.issues[0]?.message ?? 'Validation failed');\n }\n\n await writeJsonAtomic(filePath, document);\n await syncTerrazzoTokens(\n this.terrazzoPath,\n `./src/tokens/${categoryFileName(normalized)}`,\n 'add'\n );\n\n return {\n name: normalized,\n document,\n tokens: [],\n };\n }\n\n async saveCategory(name: string, document: Record<string, unknown>): Promise<CategoryDocument> {\n const normalized = normalizeCategoryName(name);\n const validation = validateCategoryDocument(normalized, document);\n if (!validation.valid) {\n throw new Error(\n validation.issues.map((issue) => `${issue.path}: ${issue.message}`).join('\\n')\n );\n }\n\n const filePath = this.categoryPath(normalized);\n await writeJsonAtomic(filePath, document);\n\n return {\n name: normalized,\n document,\n tokens: flattenTokens(document),\n };\n }\n\n async deleteCategory(name: string): Promise<void> {\n const normalized = normalizeCategoryName(name);\n await removeFileIfExists(this.categoryPath(normalized));\n await syncTerrazzoTokens(\n this.terrazzoPath,\n `./src/tokens/${categoryFileName(normalized)}`,\n 'remove'\n );\n }\n}\n","import type { SupportedTokenType, TokenItem } from './types';\n\nfunction isObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction asSupportedType(value: unknown): SupportedTokenType | undefined {\n if (\n value === 'color' ||\n value === 'dimension' ||\n value === 'duration' ||\n value === 'cubicBezier' ||\n value === 'fontFamily' ||\n value === 'number' ||\n value === 'string'\n ) {\n return value;\n }\n\n return undefined;\n}\n\nexport function flattenTokens(document: Record<string, unknown>): TokenItem[] {\n const rootKeys = Object.keys(document);\n if (rootKeys.length !== 1) {\n return [];\n }\n\n const [rootKey] = rootKeys;\n const root = document[rootKey];\n if (!isObject(root)) {\n return [];\n }\n\n const tokens: TokenItem[] = [];\n\n function walk(\n node: Record<string, unknown>,\n currentPath: string,\n inheritedType?: SupportedTokenType\n ): void {\n const localType = asSupportedType(node.$type) ?? inheritedType;\n\n if (Object.prototype.hasOwnProperty.call(node, '$value') && localType) {\n const entry: TokenItem = {\n path: currentPath,\n type: localType,\n description: typeof node.$description === 'string' ? node.$description : undefined,\n value: node.$value,\n hasMode: false,\n };\n\n if (isObject(node.$extensions) && isObject(node.$extensions.mode)) {\n entry.hasMode = true;\n entry.mode = {\n light: node.$extensions.mode.light,\n dark: node.$extensions.mode.dark,\n };\n }\n\n tokens.push(entry);\n }\n\n for (const [key, value] of Object.entries(node)) {\n if (key.startsWith('$')) {\n continue;\n }\n if (!isObject(value)) {\n continue;\n }\n walk(value, `${currentPath}.${key}`, localType);\n }\n }\n\n walk(root, rootKey, asSupportedType(root.$type));\n\n return tokens.sort((a, b) => a.path.localeCompare(b.path));\n}\n\nfunction ensureObject(parent: Record<string, unknown>, key: string): Record<string, unknown> {\n const existing = parent[key];\n if (isObject(existing)) {\n return existing;\n }\n\n const created: Record<string, unknown> = {};\n parent[key] = created;\n return created;\n}\n\nexport interface UpsertTokenInput {\n path: string;\n type: SupportedTokenType;\n description?: string;\n value: unknown;\n mode?: {\n light?: unknown;\n dark?: unknown;\n };\n}\n\nexport function upsertToken(\n document: Record<string, unknown>,\n input: UpsertTokenInput\n): Record<string, unknown> {\n const cloned = structuredClone(document);\n const segments = input.path.split('.').filter(Boolean);\n if (segments.length < 2) {\n return cloned;\n }\n\n let cursor = cloned as Record<string, unknown>;\n for (let index = 0; index < segments.length; index += 1) {\n const segment = segments[index];\n const isLeaf = index === segments.length - 1;\n if (!isLeaf) {\n cursor = ensureObject(cursor, segment);\n continue;\n }\n\n const leaf = ensureObject(cursor, segment);\n leaf.$type = input.type;\n leaf.$value = input.value;\n\n if (input.description) {\n leaf.$description = input.description;\n } else {\n delete leaf.$description;\n }\n\n if (input.mode && (input.mode.light !== undefined || input.mode.dark !== undefined)) {\n leaf.$extensions = {\n mode: {\n ...(input.mode.light !== undefined ? { light: input.mode.light } : {}),\n ...(input.mode.dark !== undefined ? { dark: input.mode.dark } : {}),\n },\n };\n } else {\n delete leaf.$extensions;\n }\n }\n\n return cloned;\n}\n\nexport function deleteToken(\n document: Record<string, unknown>,\n tokenPath: string\n): Record<string, unknown> {\n const cloned = structuredClone(document);\n const segments = tokenPath.split('.').filter(Boolean);\n if (segments.length < 2) {\n return cloned;\n }\n\n const [rootKey] = segments;\n const rootNode = cloned[rootKey];\n if (!isObject(rootNode)) {\n return cloned;\n }\n\n function removePath(node: Record<string, unknown>, index: number): boolean {\n const key = segments[index];\n const value = node[key];\n\n if (!isObject(value)) {\n return false;\n }\n\n if (index === segments.length - 1) {\n delete node[key];\n return Object.keys(node).filter((candidate) => !candidate.startsWith('$')).length === 0;\n }\n\n const shouldPrune = removePath(value, index + 1);\n if (shouldPrune) {\n delete node[key];\n }\n\n return Object.keys(node).filter((candidate) => !candidate.startsWith('$')).length === 0;\n }\n\n removePath(rootNode, 1);\n\n return cloned;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { randomUUID } from 'node:crypto';\n\nexport async function readJsonFile(filePath: string): Promise<Record<string, unknown>> {\n const content = await fs.readFile(filePath, 'utf8');\n return JSON.parse(content) as Record<string, unknown>;\n}\n\nexport async function writeJsonAtomic(\n filePath: string,\n document: Record<string, unknown>\n): Promise<void> {\n const directory = path.dirname(filePath);\n const tempPath = path.join(directory, `.${path.basename(filePath)}.${randomUUID()}.tmp`);\n const payload = `${JSON.stringify(document, null, 2)}\\n`;\n\n await fs.writeFile(tempPath, payload, 'utf8');\n await fs.rename(tempPath, filePath);\n}\n\nexport async function removeFileIfExists(filePath: string): Promise<void> {\n try {\n await fs.unlink(filePath);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n}\n\nexport async function ensureDirectory(directory: string): Promise<void> {\n await fs.mkdir(directory, { recursive: true });\n}\n","import fs from 'node:fs/promises';\n\nfunction parseTokenArray(\n configContent: string\n): { entries: string[]; start: number; end: number } | null {\n const match = /tokens\\s*:\\s*\\[(?<entries>[\\s\\S]*?)\\],/m.exec(configContent);\n if (!match || !match.groups) {\n return null;\n }\n\n const fullMatch = match[0];\n const start = match.index;\n const end = start + fullMatch.length;\n const entriesBlock = match.groups.entries;\n\n const entries = Array.from(entriesBlock.matchAll(/'([^']+)'/g), (entry) => entry[1]);\n\n return {\n entries,\n start,\n end,\n };\n}\n\nfunction formatTokenArray(entries: string[]): string {\n const formattedEntries = entries.map((entry) => ` '${entry}',`).join('\\n');\n return `tokens: [\\n${formattedEntries}\\n ],`;\n}\n\nexport async function syncTerrazzoTokens(\n terrazzoPath: string,\n tokenRelativePath: string,\n operation: 'add' | 'remove'\n): Promise<void> {\n let configContent: string;\n try {\n configContent = await fs.readFile(terrazzoPath, 'utf8');\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return;\n }\n throw error;\n }\n\n const parsed = parseTokenArray(configContent);\n if (!parsed) {\n return;\n }\n\n let nextEntries = parsed.entries;\n\n if (operation === 'add') {\n if (!nextEntries.includes(tokenRelativePath)) {\n nextEntries = [...nextEntries, tokenRelativePath];\n }\n } else {\n nextEntries = nextEntries.filter((entry) => entry !== tokenRelativePath);\n }\n\n const replacement = formatTokenArray(nextEntries);\n const nextConfig = `${configContent.slice(0, parsed.start)}${replacement}${configContent.slice(parsed.end)}`;\n\n if (nextConfig !== configContent) {\n await fs.writeFile(terrazzoPath, nextConfig, 'utf8');\n }\n}\n"],"mappings":"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","names":["path","path","path","path","fs","path","path","fs","fs","path","isObject","fs","path","fs","fs","path","path","path"]}
@@ -0,0 +1 @@
1
+ /*! tailwindcss v4.1.18 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(97.1% .013 17.38);--color-red-300:oklch(80.8% .114 19.571);--color-red-700:oklch(50.5% .213 27.518);--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1/.75);--text-sm:.875rem;--text-sm--line-height:calc(1.25/.875);--text-xl:1.25rem;--text-xl--line-height:calc(1.75/1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2/1.5);--font-weight-medium:500;--font-weight-semibold:600;--radius-xl:.75rem;--radius-2xl:1rem;--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.static{position:static}.mx-auto{margin-inline:auto}.mb-4{margin-bottom:calc(var(--spacing)*4)}.block{display:block}.flex{display:flex}.grid{display:grid}.h-24{height:calc(var(--spacing)*24)}.min-h-screen{min-height:100vh}.w-full{width:100%}.max-w-\[1280px\]{max-width:1280px}.min-w-full{min-width:100%}.border-collapse{border-collapse:collapse}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.justify-between{justify-content:space-between}.gap-1{gap:calc(var(--spacing)*1)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}.gap-4{gap:calc(var(--spacing)*4)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*3)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*3)*calc(1 - var(--tw-space-y-reverse)))}:where(.space-y-4>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*4)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*4)*calc(1 - var(--tw-space-y-reverse)))}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:var(--radius-2xl)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-\[var\(--line\)\]{border-color:var(--line)}.border-red-300{border-color:var(--color-red-300)}.bg-\[var\(--accent\)\]{background-color:var(--accent)}.bg-\[var\(--panel\)\]{background-color:var(--panel)}.bg-\[var\(--paper\)\]{background-color:var(--paper)}.bg-red-50{background-color:var(--color-red-50)}.bg-white{background-color:var(--color-white)}.p-2{padding:calc(var(--spacing)*2)}.p-3{padding:calc(var(--spacing)*3)}.p-4{padding:calc(var(--spacing)*4)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-3{padding-inline:calc(var(--spacing)*3)}.py-1{padding-block:calc(var(--spacing)*1)}.py-2{padding-block:calc(var(--spacing)*2)}.text-left{text-align:left}.align-top{vertical-align:top}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.whitespace-pre-wrap{white-space:pre-wrap}.text-red-700{color:var(--color-red-700)}.text-white{color:var(--color-white)}.opacity-70{opacity:.7}.opacity-75{opacity:.75}.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a),0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}@media(hover:hover){.hover\:border-\[var\(--accent\)\]:hover{border-color:var(--accent)}.hover\:bg-\[var\(--paper\)\]:hover{background-color:var(--paper)}}.disabled\:opacity-60:disabled{opacity:.6}@media(min-width:40rem){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media(min-width:48rem){.md\:col-span-2{grid-column:span 2/span 2}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-\[1fr_auto_auto\]{grid-template-columns:1fr auto auto}}@media(min-width:64rem){.lg\:grid-cols-\[240px_1fr\]{grid-template-columns:240px 1fr}}}:root{--paper:#f4f1ea;--ink:#1f1a17;--accent:#1f6b5b;--accent-2:#d86a28;--line:#d9d2c4;--panel:#fffdf8}body{min-height:100vh;color:var(--ink);background:radial-gradient(circle at 20% 10%,var(--accent),transparent 42%),radial-gradient(circle at 80% 90%,var(--accent-2),transparent 48%),var(--paper);margin:0}@supports (color:color-mix(in lab,red,red)){body{background:radial-gradient(circle at 20% 10%,color-mix(in oklab,var(--accent)16%,transparent),transparent 42%),radial-gradient(circle at 80% 90%,color-mix(in oklab,var(--accent-2)14%,transparent),transparent 48%),var(--paper)}}body{font-family:IBM Plex Sans,Avenir Next,Segoe UI,sans-serif}h1,h2,h3{letter-spacing:.01em;font-family:Fraunces,Iowan Old Style,serif}input,select,textarea,button{font:inherit}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}