@pyreon/create-zero 0.4.1 → 0.11.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.
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import('../lib/index.js')
2
+ import("../lib/index.js")
package/lib/index.js CHANGED
@@ -246,8 +246,8 @@ function pyreonVersion(pkg) {
246
246
  "compiler",
247
247
  "cli",
248
248
  "mcp"
249
- ].some((c) => pkg === `@pyreon/${c}`)) return "^0.7.5";
250
- if (pkg === "@pyreon/zero" || pkg === "@pyreon/meta" || pkg === "@pyreon/zero-cli" || pkg === "@pyreon/create-zero") return "^0.3.0";
249
+ ].some((c) => pkg === `@pyreon/${c}`)) return "^0.7.11";
250
+ if (pkg === "@pyreon/zero" || pkg === "@pyreon/meta" || pkg === "@pyreon/zero-cli" || pkg === "@pyreon/create-zero") return "^0.4.1";
251
251
  if ([
252
252
  "store",
253
253
  "form",
@@ -261,8 +261,8 @@ function pyreonVersion(pkg) {
261
261
  "permissions",
262
262
  "flow",
263
263
  "code"
264
- ].some((f) => pkg === `@pyreon/${f}`)) return "^0.9.0";
265
- return "^0.3.0";
264
+ ].some((f) => pkg === `@pyreon/${f}`)) return "^0.10.0";
265
+ return "^0.4.1";
266
266
  }
267
267
  function generatePackageJson(config) {
268
268
  const deps = {
@@ -299,8 +299,8 @@ function generatePackageJson(config) {
299
299
  const devDeps = {
300
300
  "@pyreon/vite-plugin": pyreonVersion("@pyreon/vite-plugin"),
301
301
  "@pyreon/zero-cli": pyreonVersion("@pyreon/zero-cli"),
302
- "typescript": "^5.9.3",
303
- "vite": "^7.0.0"
302
+ typescript: "^5.9.3",
303
+ vite: "^7.0.0"
304
304
  };
305
305
  if (config.aiToolchain) devDeps["@pyreon/mcp"] = pyreonVersion("@pyreon/mcp");
306
306
  const pkg = {
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync } from 'node:fs'\nimport { cp, readFile, writeFile } from 'node:fs/promises'\nimport { basename, join, resolve } from 'node:path'\nimport * as p from '@clack/prompts'\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\ninterface ProjectConfig {\n name: string\n targetDir: string\n renderMode: 'ssr-stream' | 'ssr-string' | 'ssg' | 'spa'\n features: string[]\n packageStrategy: 'meta' | 'individual'\n aiToolchain: boolean\n}\n\n// ─── Feature definitions ────────────────────────────────────────────────────\n\nconst FEATURES = {\n store: {\n label: 'State Management (@pyreon/store)',\n deps: ['@pyreon/store'],\n },\n query: {\n label: 'Data Fetching (@pyreon/query)',\n deps: ['@pyreon/query', '@tanstack/query-core'],\n },\n forms: {\n label: 'Forms + Validation (@pyreon/form, @pyreon/validation)',\n deps: ['@pyreon/form', '@pyreon/validation', 'zod'],\n },\n feature: {\n label: 'Feature CRUD (@pyreon/feature) — includes store, query, forms',\n deps: [\n '@pyreon/feature',\n '@pyreon/store',\n '@pyreon/query',\n '@pyreon/form',\n '@pyreon/validation',\n '@tanstack/query-core',\n 'zod',\n ],\n },\n i18n: {\n label: 'Internationalization (@pyreon/i18n)',\n deps: ['@pyreon/i18n'],\n },\n table: {\n label: 'Tables (@pyreon/table)',\n deps: ['@pyreon/table', '@tanstack/table-core'],\n },\n virtual: {\n label: 'Virtual Lists (@pyreon/virtual)',\n deps: ['@pyreon/virtual', '@tanstack/virtual-core'],\n },\n styler: {\n label: 'CSS-in-JS (@pyreon/styler)',\n deps: ['@pyreon/styler', '@pyreon/ui-core'],\n },\n elements: {\n label: 'UI Elements (@pyreon/elements, @pyreon/coolgrid)',\n deps: ['@pyreon/elements', '@pyreon/coolgrid', '@pyreon/unistyle', '@pyreon/ui-core'],\n },\n animations: {\n label: 'Animations (@pyreon/kinetic + 120 presets)',\n deps: ['@pyreon/kinetic', '@pyreon/kinetic-presets'],\n },\n hooks: {\n label: 'Hooks (@pyreon/hooks — 25+ signal-based utilities)',\n deps: ['@pyreon/hooks'],\n },\n charts: {\n label: 'Charts (@pyreon/charts — reactive ECharts)',\n deps: ['@pyreon/charts'],\n },\n hotkeys: {\n label: 'Hotkeys (@pyreon/hotkeys — keyboard shortcuts)',\n deps: ['@pyreon/hotkeys'],\n },\n storage: {\n label: 'Storage (@pyreon/storage — localStorage, cookies, IndexedDB)',\n deps: ['@pyreon/storage'],\n },\n flow: {\n label: 'Flow Diagrams (@pyreon/flow — reactive node graphs)',\n deps: ['@pyreon/flow'],\n },\n code: {\n label: 'Code Editor (@pyreon/code — CodeMirror 6)',\n deps: ['@pyreon/code'],\n },\n} as const\n\ntype FeatureKey = keyof typeof FEATURES\n\n// ─── Template directory ─────────────────────────────────────────────────────\n\nconst TEMPLATE_DIR = resolve(import.meta.dirname, '../templates/default')\n\n// ─── Main ───────────────────────────────────────────────────────────────────\n\nasync function main() {\n const args = process.argv.slice(2)\n const argName = args[0]\n\n if (argName === '--help' || argName === '-h') {\n console.log('Usage: create-zero [project-name]')\n process.exit(0)\n }\n\n p.intro('Create a new Pyreon Zero project')\n\n // Project name\n const name = argName ?? (await p.text({\n message: 'Project name',\n placeholder: 'my-zero-app',\n validate: (v) => {\n if (!v?.trim()) return 'Project name is required'\n if (existsSync(resolve(process.cwd(), v))) return `Directory \"${v}\" already exists`\n },\n }))\n\n if (p.isCancel(name)) {\n p.cancel('Cancelled.')\n process.exit(0)\n }\n\n const targetDir = resolve(process.cwd(), name as string)\n if (existsSync(targetDir)) {\n p.cancel(`Directory \"${name}\" already exists.`)\n process.exit(1)\n }\n\n // Rendering mode\n const renderMode = await p.select({\n message: 'Rendering mode',\n options: [\n { value: 'ssr-stream', label: 'SSR Streaming', hint: 'recommended — progressive HTML with Suspense' },\n { value: 'ssr-string', label: 'SSR String', hint: 'buffered HTML, simpler but slower TTFB' },\n { value: 'ssg', label: 'Static (SSG)', hint: 'pre-rendered at build time' },\n { value: 'spa', label: 'SPA', hint: 'client-only, no server rendering' },\n ],\n })\n\n if (p.isCancel(renderMode)) {\n p.cancel('Cancelled.')\n process.exit(0)\n }\n\n // Features\n const features = await p.multiselect({\n message: 'Select features (space to toggle, enter to confirm)',\n options: Object.entries(FEATURES).map(([key, { label }]) => ({\n value: key,\n label,\n })),\n initialValues: ['store', 'query', 'forms'],\n required: false,\n })\n\n if (p.isCancel(features)) {\n p.cancel('Cancelled.')\n process.exit(0)\n }\n\n // Package strategy\n const packageStrategy = await p.select({\n message: 'Package imports',\n options: [\n { value: 'meta', label: '@pyreon/meta (single barrel)', hint: 'one import for everything — simpler, tree-shaken at build' },\n { value: 'individual', label: 'Individual packages', hint: 'only install what you selected — smaller node_modules' },\n ],\n })\n\n if (p.isCancel(packageStrategy)) {\n p.cancel('Cancelled.')\n process.exit(0)\n }\n\n // AI toolchain\n const aiToolchain = await p.confirm({\n message: 'Include AI toolchain? (MCP server, CLAUDE.md, doctor)',\n initialValue: true,\n })\n\n if (p.isCancel(aiToolchain)) {\n p.cancel('Cancelled.')\n process.exit(0)\n }\n\n const config: ProjectConfig = {\n name: name as string,\n targetDir,\n renderMode: renderMode as ProjectConfig['renderMode'],\n features: features as string[],\n packageStrategy: packageStrategy as ProjectConfig['packageStrategy'],\n aiToolchain: aiToolchain as boolean,\n }\n\n const s = p.spinner()\n s.start('Scaffolding project...')\n\n await scaffold(config)\n\n s.stop('Project created!')\n\n // Next steps\n p.note(\n [\n `cd ${config.name}`,\n 'bun install',\n 'bun run dev',\n ].join('\\n'),\n 'Next steps',\n )\n\n p.outro('Happy building!')\n}\n\n// ─── Scaffolding ────────────────────────────────────────────────────────────\n\nasync function scaffold(config: ProjectConfig) {\n // Copy full template as base\n await cp(TEMPLATE_DIR, config.targetDir, { recursive: true })\n\n // Generate customized files\n await writeFile(\n join(config.targetDir, 'package.json'),\n generatePackageJson(config),\n )\n\n await writeFile(\n join(config.targetDir, 'vite.config.ts'),\n generateViteConfig(config),\n )\n\n await writeFile(\n join(config.targetDir, 'src/entry-server.ts'),\n generateEntryServer(config),\n )\n\n await writeFile(\n join(config.targetDir, 'env.d.ts'),\n generateEnvDts(config),\n )\n\n // Create .gitignore (npm strips it from packages)\n await writeFile(\n join(config.targetDir, '.gitignore'),\n 'node_modules\\ndist\\n.DS_Store\\n*.local\\n.pyreon\\n',\n )\n\n // AI toolchain files\n if (config.aiToolchain) {\n await writeFile(\n join(config.targetDir, '.mcp.json'),\n JSON.stringify(\n {\n mcpServers: {\n pyreon: { command: 'bunx', args: ['@pyreon/mcp'] },\n },\n },\n null,\n 2,\n ),\n )\n } else {\n // Remove AI files from copied template\n const aiFiles = ['.mcp.json', 'CLAUDE.md']\n for (const f of aiFiles) {\n const path = join(config.targetDir, f)\n if (existsSync(path)) {\n const { unlink } = await import('node:fs/promises')\n await unlink(path)\n }\n }\n }\n\n // Remove feature-specific files if features not selected\n if (!config.features.includes('feature') && !config.features.includes('forms')) {\n await removeIfExists(join(config.targetDir, 'src/routes/posts/new.tsx'))\n await removeIfExists(join(config.targetDir, 'src/features'))\n }\n\n if (!config.features.includes('store')) {\n await removeIfExists(join(config.targetDir, 'src/stores'))\n }\n\n // Remove store import from layout if store not selected\n if (!config.features.includes('store')) {\n const layoutPath = join(config.targetDir, 'src/routes/_layout.tsx')\n if (existsSync(layoutPath)) {\n let layout = await readFile(layoutPath, 'utf-8')\n layout = layout\n .replace(/import .* from '\\.\\.\\/stores\\/app'\\n/g, '')\n .replace(/.*useAppStore.*\\n/g, '')\n .replace(/\\s*<button[\\s\\S]*?sidebar-toggle[\\s\\S]*?<\\/button>\\n/g, '')\n await writeFile(layoutPath, layout)\n }\n }\n}\n\n// ─── File generators ────────────────────────────────────────────────────────\n\n// Resolve the correct version range for a @pyreon/* package\nfunction pyreonVersion(pkg: string): string {\n // Core packages\n const core = ['core', 'reactivity', 'runtime-dom', 'runtime-server', 'server', 'head', 'router', 'vite-plugin', 'compiler', 'cli', 'mcp']\n if (core.some((c) => pkg === `@pyreon/${c}`)) return '^0.7.5'\n // Zero framework packages\n if (pkg === '@pyreon/zero' || pkg === '@pyreon/meta' || pkg === '@pyreon/zero-cli' || pkg === '@pyreon/create-zero') return '^0.3.0'\n // Fundamentals\n const fundamentals = ['store', 'form', 'validation', 'query', 'table', 'virtual', 'i18n', 'feature', 'machine', 'permissions', 'flow', 'code']\n if (fundamentals.some((f) => pkg === `@pyreon/${f}`)) return '^0.9.0'\n // UI system\n return '^0.3.0'\n}\n\nfunction generatePackageJson(config: ProjectConfig): string {\n const deps: Record<string, string> = {\n '@pyreon/core': pyreonVersion('@pyreon/core'),\n '@pyreon/head': pyreonVersion('@pyreon/head'),\n '@pyreon/reactivity': pyreonVersion('@pyreon/reactivity'),\n '@pyreon/router': pyreonVersion('@pyreon/router'),\n '@pyreon/runtime-dom': pyreonVersion('@pyreon/runtime-dom'),\n '@pyreon/runtime-server': pyreonVersion('@pyreon/runtime-server'),\n '@pyreon/server': pyreonVersion('@pyreon/server'),\n '@pyreon/zero': pyreonVersion('@pyreon/zero'),\n }\n\n if (config.packageStrategy === 'meta') {\n // Single barrel — includes all fundamentals + UI system\n deps['@pyreon/meta'] = pyreonVersion('@pyreon/meta')\n // Still need non-pyreon deps for selected features\n for (const key of config.features) {\n const feature = FEATURES[key as FeatureKey]\n if (feature) {\n for (const dep of feature.deps) {\n if (!dep.startsWith('@pyreon/')) {\n if (dep.startsWith('@tanstack/')) {\n deps[dep] = dep.includes('query') ? '^5.90.0' : dep.includes('table') ? '^8.21.0' : '^3.13.0'\n } else if (dep === 'zod') {\n deps[dep] = '^4.0.0'\n }\n }\n }\n }\n }\n } else {\n // Individual packages — only install what's selected\n const allDeps = new Set<string>()\n for (const key of config.features) {\n const feature = FEATURES[key as FeatureKey]\n if (feature) {\n for (const dep of feature.deps) allDeps.add(dep)\n }\n }\n for (const dep of allDeps) {\n if (dep.startsWith('@pyreon/')) {\n deps[dep] = pyreonVersion(dep)\n } else if (dep.startsWith('@tanstack/')) {\n deps[dep] = dep.includes('query') ? '^5.90.0' : dep.includes('table') ? '^8.21.0' : '^3.13.0'\n } else if (dep === 'zod') {\n deps[dep] = '^4.0.0'\n }\n }\n }\n\n const devDeps: Record<string, string> = {\n '@pyreon/vite-plugin': pyreonVersion('@pyreon/vite-plugin'),\n '@pyreon/zero-cli': pyreonVersion('@pyreon/zero-cli'),\n 'typescript': '^5.9.3',\n 'vite': '^7.0.0',\n }\n\n if (config.aiToolchain) {\n devDeps['@pyreon/mcp'] = pyreonVersion('@pyreon/mcp')\n }\n\n const scripts: Record<string, string> = {\n dev: 'zero dev',\n build: 'zero build',\n preview: 'zero preview',\n doctor: 'zero doctor',\n 'doctor:fix': 'zero doctor --fix',\n 'doctor:ci': 'zero doctor --ci',\n }\n\n const pkg = {\n name: basename(config.name),\n version: '0.0.1',\n private: true,\n type: 'module',\n scripts,\n dependencies: Object.fromEntries(Object.entries(deps).sort(([a], [b]) => a.localeCompare(b))),\n devDependencies: Object.fromEntries(Object.entries(devDeps).sort(([a], [b]) => a.localeCompare(b))),\n }\n\n return `${JSON.stringify(pkg, null, 2)}\\n`\n}\n\nfunction generateViteConfig(config: ProjectConfig): string {\n const modeMap = {\n 'ssr-stream': `mode: 'ssr', ssr: { mode: 'stream' }`,\n 'ssr-string': `mode: 'ssr'`,\n ssg: `mode: 'ssg'`,\n spa: `mode: 'spa'`,\n }\n\n return `import pyreon from '@pyreon/vite-plugin'\nimport zero from '@pyreon/zero'\nimport { fontPlugin } from '@pyreon/zero/font'\nimport { seoPlugin } from '@pyreon/zero/seo'\n\nexport default {\n plugins: [\n pyreon(),\n zero({ ${modeMap[config.renderMode]} }),\n\n // Google Fonts — self-hosted at build time, CDN in dev\n fontPlugin({\n google: ['Inter:wght@400;500;600;700;800', 'JetBrains Mono:wght@400'],\n fallbacks: {\n Inter: { fallback: 'Arial', sizeAdjust: 1.07, ascentOverride: 90 },\n },\n }),\n\n // Generate sitemap.xml and robots.txt at build time\n seoPlugin({\n sitemap: { origin: 'https://example.com' },\n robots: {\n rules: [{ userAgent: '*', allow: ['/'] }],\n sitemap: 'https://example.com/sitemap.xml',\n },\n }),\n ],\n}\n`\n}\n\nfunction generateEntryServer(config: ProjectConfig): string {\n const imports = [\n `import { routes } from 'virtual:zero/routes'`,\n `import { routeMiddleware } from 'virtual:zero/route-middleware'`,\n `import { createServer } from '@pyreon/zero'`,\n `import {\\n cacheMiddleware,\\n securityHeaders,\\n varyEncoding,\\n} from '@pyreon/zero/cache'`,\n ]\n\n const modeMap = {\n 'ssr-stream': `stream`,\n 'ssr-string': `string`,\n ssg: `string`,\n spa: `string`,\n }\n\n return `${imports.join('\\n')}\n\nexport default createServer({\n routes,\n routeMiddleware,\n config: {\n ssr: { mode: '${modeMap[config.renderMode]}' },\n },\n middleware: [\n securityHeaders(),\n cacheMiddleware({ staleWhileRevalidate: 120 }),\n varyEncoding(),\n ],\n})\n`\n}\n\nfunction generateEnvDts(config: ProjectConfig): string {\n let content = `/// <reference types=\"vite/client\" />\n\ndeclare module 'virtual:zero/routes' {\n import type { RouteRecord } from '@pyreon/router'\n export const routes: RouteRecord[]\n}\n\ndeclare module 'virtual:zero/route-middleware' {\n import type { RouteMiddlewareEntry } from '@pyreon/zero'\n export const routeMiddleware: RouteMiddlewareEntry[]\n}\n`\n\n if (config.features.includes('query')) {\n content += `\ndeclare module 'virtual:zero/actions' {\n export {}\n}\n`\n }\n\n return content\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nasync function removeIfExists(path: string) {\n if (!existsSync(path)) return\n const { rm } = await import('node:fs/promises')\n await rm(path, { recursive: true })\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { existsSync } from \"node:fs\"\nimport { cp, readFile, writeFile } from \"node:fs/promises\"\nimport { basename, join, resolve } from \"node:path\"\nimport * as p from \"@clack/prompts\"\n\n// ─── Types ──────────────────────────────────────────────────────────────────\n\ninterface ProjectConfig {\n name: string\n targetDir: string\n renderMode: \"ssr-stream\" | \"ssr-string\" | \"ssg\" | \"spa\"\n features: string[]\n packageStrategy: \"meta\" | \"individual\"\n aiToolchain: boolean\n}\n\n// ─── Feature definitions ────────────────────────────────────────────────────\n\nconst FEATURES = {\n store: {\n label: \"State Management (@pyreon/store)\",\n deps: [\"@pyreon/store\"],\n },\n query: {\n label: \"Data Fetching (@pyreon/query)\",\n deps: [\"@pyreon/query\", \"@tanstack/query-core\"],\n },\n forms: {\n label: \"Forms + Validation (@pyreon/form, @pyreon/validation)\",\n deps: [\"@pyreon/form\", \"@pyreon/validation\", \"zod\"],\n },\n feature: {\n label: \"Feature CRUD (@pyreon/feature) — includes store, query, forms\",\n deps: [\n \"@pyreon/feature\",\n \"@pyreon/store\",\n \"@pyreon/query\",\n \"@pyreon/form\",\n \"@pyreon/validation\",\n \"@tanstack/query-core\",\n \"zod\",\n ],\n },\n i18n: {\n label: \"Internationalization (@pyreon/i18n)\",\n deps: [\"@pyreon/i18n\"],\n },\n table: {\n label: \"Tables (@pyreon/table)\",\n deps: [\"@pyreon/table\", \"@tanstack/table-core\"],\n },\n virtual: {\n label: \"Virtual Lists (@pyreon/virtual)\",\n deps: [\"@pyreon/virtual\", \"@tanstack/virtual-core\"],\n },\n styler: {\n label: \"CSS-in-JS (@pyreon/styler)\",\n deps: [\"@pyreon/styler\", \"@pyreon/ui-core\"],\n },\n elements: {\n label: \"UI Elements (@pyreon/elements, @pyreon/coolgrid)\",\n deps: [\"@pyreon/elements\", \"@pyreon/coolgrid\", \"@pyreon/unistyle\", \"@pyreon/ui-core\"],\n },\n animations: {\n label: \"Animations (@pyreon/kinetic + 120 presets)\",\n deps: [\"@pyreon/kinetic\", \"@pyreon/kinetic-presets\"],\n },\n hooks: {\n label: \"Hooks (@pyreon/hooks — 25+ signal-based utilities)\",\n deps: [\"@pyreon/hooks\"],\n },\n charts: {\n label: \"Charts (@pyreon/charts — reactive ECharts)\",\n deps: [\"@pyreon/charts\"],\n },\n hotkeys: {\n label: \"Hotkeys (@pyreon/hotkeys — keyboard shortcuts)\",\n deps: [\"@pyreon/hotkeys\"],\n },\n storage: {\n label: \"Storage (@pyreon/storage — localStorage, cookies, IndexedDB)\",\n deps: [\"@pyreon/storage\"],\n },\n flow: {\n label: \"Flow Diagrams (@pyreon/flow — reactive node graphs)\",\n deps: [\"@pyreon/flow\"],\n },\n code: {\n label: \"Code Editor (@pyreon/code — CodeMirror 6)\",\n deps: [\"@pyreon/code\"],\n },\n} as const\n\ntype FeatureKey = keyof typeof FEATURES\n\n// ─── Template directory ─────────────────────────────────────────────────────\n\nconst TEMPLATE_DIR = resolve(import.meta.dirname, \"../templates/default\")\n\n// ─── Main ───────────────────────────────────────────────────────────────────\n\nasync function main() {\n const args = process.argv.slice(2)\n const argName = args[0]\n\n if (argName === \"--help\" || argName === \"-h\") {\n console.log(\"Usage: create-zero [project-name]\")\n process.exit(0)\n }\n\n p.intro(\"Create a new Pyreon Zero project\")\n\n // Project name\n const name =\n argName ??\n (await p.text({\n message: \"Project name\",\n placeholder: \"my-zero-app\",\n validate: (v) => {\n if (!v?.trim()) return \"Project name is required\"\n if (existsSync(resolve(process.cwd(), v))) return `Directory \"${v}\" already exists`\n },\n }))\n\n if (p.isCancel(name)) {\n p.cancel(\"Cancelled.\")\n process.exit(0)\n }\n\n const targetDir = resolve(process.cwd(), name as string)\n if (existsSync(targetDir)) {\n p.cancel(`Directory \"${name}\" already exists.`)\n process.exit(1)\n }\n\n // Rendering mode\n const renderMode = await p.select({\n message: \"Rendering mode\",\n options: [\n {\n value: \"ssr-stream\",\n label: \"SSR Streaming\",\n hint: \"recommended — progressive HTML with Suspense\",\n },\n { value: \"ssr-string\", label: \"SSR String\", hint: \"buffered HTML, simpler but slower TTFB\" },\n { value: \"ssg\", label: \"Static (SSG)\", hint: \"pre-rendered at build time\" },\n { value: \"spa\", label: \"SPA\", hint: \"client-only, no server rendering\" },\n ],\n })\n\n if (p.isCancel(renderMode)) {\n p.cancel(\"Cancelled.\")\n process.exit(0)\n }\n\n // Features\n const features = await p.multiselect({\n message: \"Select features (space to toggle, enter to confirm)\",\n options: Object.entries(FEATURES).map(([key, { label }]) => ({\n value: key,\n label,\n })),\n initialValues: [\"store\", \"query\", \"forms\"],\n required: false,\n })\n\n if (p.isCancel(features)) {\n p.cancel(\"Cancelled.\")\n process.exit(0)\n }\n\n // Package strategy\n const packageStrategy = await p.select({\n message: \"Package imports\",\n options: [\n {\n value: \"meta\",\n label: \"@pyreon/meta (single barrel)\",\n hint: \"one import for everything — simpler, tree-shaken at build\",\n },\n {\n value: \"individual\",\n label: \"Individual packages\",\n hint: \"only install what you selected — smaller node_modules\",\n },\n ],\n })\n\n if (p.isCancel(packageStrategy)) {\n p.cancel(\"Cancelled.\")\n process.exit(0)\n }\n\n // AI toolchain\n const aiToolchain = await p.confirm({\n message: \"Include AI toolchain? (MCP server, CLAUDE.md, doctor)\",\n initialValue: true,\n })\n\n if (p.isCancel(aiToolchain)) {\n p.cancel(\"Cancelled.\")\n process.exit(0)\n }\n\n const config: ProjectConfig = {\n name: name as string,\n targetDir,\n renderMode: renderMode as ProjectConfig[\"renderMode\"],\n features: features as string[],\n packageStrategy: packageStrategy as ProjectConfig[\"packageStrategy\"],\n aiToolchain: aiToolchain as boolean,\n }\n\n const s = p.spinner()\n s.start(\"Scaffolding project...\")\n\n await scaffold(config)\n\n s.stop(\"Project created!\")\n\n // Next steps\n p.note([`cd ${config.name}`, \"bun install\", \"bun run dev\"].join(\"\\n\"), \"Next steps\")\n\n p.outro(\"Happy building!\")\n}\n\n// ─── Scaffolding ────────────────────────────────────────────────────────────\n\nasync function scaffold(config: ProjectConfig) {\n // Copy full template as base\n await cp(TEMPLATE_DIR, config.targetDir, { recursive: true })\n\n // Generate customized files\n await writeFile(join(config.targetDir, \"package.json\"), generatePackageJson(config))\n\n await writeFile(join(config.targetDir, \"vite.config.ts\"), generateViteConfig(config))\n\n await writeFile(join(config.targetDir, \"src/entry-server.ts\"), generateEntryServer(config))\n\n await writeFile(join(config.targetDir, \"env.d.ts\"), generateEnvDts(config))\n\n // Create .gitignore (npm strips it from packages)\n await writeFile(\n join(config.targetDir, \".gitignore\"),\n \"node_modules\\ndist\\n.DS_Store\\n*.local\\n.pyreon\\n\",\n )\n\n // AI toolchain files\n if (config.aiToolchain) {\n await writeFile(\n join(config.targetDir, \".mcp.json\"),\n JSON.stringify(\n {\n mcpServers: {\n pyreon: { command: \"bunx\", args: [\"@pyreon/mcp\"] },\n },\n },\n null,\n 2,\n ),\n )\n } else {\n // Remove AI files from copied template\n const aiFiles = [\".mcp.json\", \"CLAUDE.md\"]\n for (const f of aiFiles) {\n const path = join(config.targetDir, f)\n if (existsSync(path)) {\n const { unlink } = await import(\"node:fs/promises\")\n await unlink(path)\n }\n }\n }\n\n // Remove feature-specific files if features not selected\n if (!config.features.includes(\"feature\") && !config.features.includes(\"forms\")) {\n await removeIfExists(join(config.targetDir, \"src/routes/posts/new.tsx\"))\n await removeIfExists(join(config.targetDir, \"src/features\"))\n }\n\n if (!config.features.includes(\"store\")) {\n await removeIfExists(join(config.targetDir, \"src/stores\"))\n }\n\n // Remove store import from layout if store not selected\n if (!config.features.includes(\"store\")) {\n const layoutPath = join(config.targetDir, \"src/routes/_layout.tsx\")\n if (existsSync(layoutPath)) {\n let layout = await readFile(layoutPath, \"utf-8\")\n layout = layout\n .replace(/import .* from '\\.\\.\\/stores\\/app'\\n/g, \"\")\n .replace(/.*useAppStore.*\\n/g, \"\")\n .replace(/\\s*<button[\\s\\S]*?sidebar-toggle[\\s\\S]*?<\\/button>\\n/g, \"\")\n await writeFile(layoutPath, layout)\n }\n }\n}\n\n// ─── File generators ────────────────────────────────────────────────────────\n\n// Resolve the correct version range for a @pyreon/* package\nfunction pyreonVersion(pkg: string): string {\n // Core packages\n const core = [\n \"core\",\n \"reactivity\",\n \"runtime-dom\",\n \"runtime-server\",\n \"server\",\n \"head\",\n \"router\",\n \"vite-plugin\",\n \"compiler\",\n \"cli\",\n \"mcp\",\n ]\n if (core.some((c) => pkg === `@pyreon/${c}`)) return \"^0.7.11\"\n // Zero framework packages\n if (\n pkg === \"@pyreon/zero\" ||\n pkg === \"@pyreon/meta\" ||\n pkg === \"@pyreon/zero-cli\" ||\n pkg === \"@pyreon/create-zero\"\n )\n return \"^0.4.1\"\n // Fundamentals\n const fundamentals = [\n \"store\",\n \"form\",\n \"validation\",\n \"query\",\n \"table\",\n \"virtual\",\n \"i18n\",\n \"feature\",\n \"machine\",\n \"permissions\",\n \"flow\",\n \"code\",\n ]\n if (fundamentals.some((f) => pkg === `@pyreon/${f}`)) return \"^0.10.0\"\n // UI system\n return \"^0.4.1\"\n}\n\nfunction generatePackageJson(config: ProjectConfig): string {\n const deps: Record<string, string> = {\n \"@pyreon/core\": pyreonVersion(\"@pyreon/core\"),\n \"@pyreon/head\": pyreonVersion(\"@pyreon/head\"),\n \"@pyreon/reactivity\": pyreonVersion(\"@pyreon/reactivity\"),\n \"@pyreon/router\": pyreonVersion(\"@pyreon/router\"),\n \"@pyreon/runtime-dom\": pyreonVersion(\"@pyreon/runtime-dom\"),\n \"@pyreon/runtime-server\": pyreonVersion(\"@pyreon/runtime-server\"),\n \"@pyreon/server\": pyreonVersion(\"@pyreon/server\"),\n \"@pyreon/zero\": pyreonVersion(\"@pyreon/zero\"),\n }\n\n if (config.packageStrategy === \"meta\") {\n // Single barrel — includes all fundamentals + UI system\n deps[\"@pyreon/meta\"] = pyreonVersion(\"@pyreon/meta\")\n // Still need non-pyreon deps for selected features\n for (const key of config.features) {\n const feature = FEATURES[key as FeatureKey]\n if (feature) {\n for (const dep of feature.deps) {\n if (!dep.startsWith(\"@pyreon/\")) {\n if (dep.startsWith(\"@tanstack/\")) {\n deps[dep] = dep.includes(\"query\")\n ? \"^5.90.0\"\n : dep.includes(\"table\")\n ? \"^8.21.0\"\n : \"^3.13.0\"\n } else if (dep === \"zod\") {\n deps[dep] = \"^4.0.0\"\n }\n }\n }\n }\n }\n } else {\n // Individual packages — only install what's selected\n const allDeps = new Set<string>()\n for (const key of config.features) {\n const feature = FEATURES[key as FeatureKey]\n if (feature) {\n for (const dep of feature.deps) allDeps.add(dep)\n }\n }\n for (const dep of allDeps) {\n if (dep.startsWith(\"@pyreon/\")) {\n deps[dep] = pyreonVersion(dep)\n } else if (dep.startsWith(\"@tanstack/\")) {\n deps[dep] = dep.includes(\"query\")\n ? \"^5.90.0\"\n : dep.includes(\"table\")\n ? \"^8.21.0\"\n : \"^3.13.0\"\n } else if (dep === \"zod\") {\n deps[dep] = \"^4.0.0\"\n }\n }\n }\n\n const devDeps: Record<string, string> = {\n \"@pyreon/vite-plugin\": pyreonVersion(\"@pyreon/vite-plugin\"),\n \"@pyreon/zero-cli\": pyreonVersion(\"@pyreon/zero-cli\"),\n typescript: \"^5.9.3\",\n vite: \"^7.0.0\",\n }\n\n if (config.aiToolchain) {\n devDeps[\"@pyreon/mcp\"] = pyreonVersion(\"@pyreon/mcp\")\n }\n\n const scripts: Record<string, string> = {\n dev: \"zero dev\",\n build: \"zero build\",\n preview: \"zero preview\",\n doctor: \"zero doctor\",\n \"doctor:fix\": \"zero doctor --fix\",\n \"doctor:ci\": \"zero doctor --ci\",\n }\n\n const pkg = {\n name: basename(config.name),\n version: \"0.0.1\",\n private: true,\n type: \"module\",\n scripts,\n dependencies: Object.fromEntries(Object.entries(deps).sort(([a], [b]) => a.localeCompare(b))),\n devDependencies: Object.fromEntries(\n Object.entries(devDeps).sort(([a], [b]) => a.localeCompare(b)),\n ),\n }\n\n return `${JSON.stringify(pkg, null, 2)}\\n`\n}\n\nfunction generateViteConfig(config: ProjectConfig): string {\n const modeMap = {\n \"ssr-stream\": `mode: 'ssr', ssr: { mode: 'stream' }`,\n \"ssr-string\": `mode: 'ssr'`,\n ssg: `mode: 'ssg'`,\n spa: `mode: 'spa'`,\n }\n\n return `import pyreon from '@pyreon/vite-plugin'\nimport zero from '@pyreon/zero'\nimport { fontPlugin } from '@pyreon/zero/font'\nimport { seoPlugin } from '@pyreon/zero/seo'\n\nexport default {\n plugins: [\n pyreon(),\n zero({ ${modeMap[config.renderMode]} }),\n\n // Google Fonts — self-hosted at build time, CDN in dev\n fontPlugin({\n google: ['Inter:wght@400;500;600;700;800', 'JetBrains Mono:wght@400'],\n fallbacks: {\n Inter: { fallback: 'Arial', sizeAdjust: 1.07, ascentOverride: 90 },\n },\n }),\n\n // Generate sitemap.xml and robots.txt at build time\n seoPlugin({\n sitemap: { origin: 'https://example.com' },\n robots: {\n rules: [{ userAgent: '*', allow: ['/'] }],\n sitemap: 'https://example.com/sitemap.xml',\n },\n }),\n ],\n}\n`\n}\n\nfunction generateEntryServer(config: ProjectConfig): string {\n const imports = [\n `import { routes } from 'virtual:zero/routes'`,\n `import { routeMiddleware } from 'virtual:zero/route-middleware'`,\n `import { createServer } from '@pyreon/zero'`,\n `import {\\n cacheMiddleware,\\n securityHeaders,\\n varyEncoding,\\n} from '@pyreon/zero/cache'`,\n ]\n\n const modeMap = {\n \"ssr-stream\": `stream`,\n \"ssr-string\": `string`,\n ssg: `string`,\n spa: `string`,\n }\n\n return `${imports.join(\"\\n\")}\n\nexport default createServer({\n routes,\n routeMiddleware,\n config: {\n ssr: { mode: '${modeMap[config.renderMode]}' },\n },\n middleware: [\n securityHeaders(),\n cacheMiddleware({ staleWhileRevalidate: 120 }),\n varyEncoding(),\n ],\n})\n`\n}\n\nfunction generateEnvDts(config: ProjectConfig): string {\n let content = `/// <reference types=\"vite/client\" />\n\ndeclare module 'virtual:zero/routes' {\n import type { RouteRecord } from '@pyreon/router'\n export const routes: RouteRecord[]\n}\n\ndeclare module 'virtual:zero/route-middleware' {\n import type { RouteMiddlewareEntry } from '@pyreon/zero'\n export const routeMiddleware: RouteMiddlewareEntry[]\n}\n`\n\n if (config.features.includes(\"query\")) {\n content += `\ndeclare module 'virtual:zero/actions' {\n export {}\n}\n`\n }\n\n return content\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nasync function removeIfExists(path: string) {\n if (!existsSync(path)) return\n const { rm } = await import(\"node:fs/promises\")\n await rm(path, { recursive: true })\n}\n\nmain().catch((err) => {\n console.error(err)\n process.exit(1)\n})\n"],"mappings":";;;;;;AAkBA,MAAM,WAAW;CACf,OAAO;EACL,OAAO;EACP,MAAM,CAAC,gBAAgB;EACxB;CACD,OAAO;EACL,OAAO;EACP,MAAM,CAAC,iBAAiB,uBAAuB;EAChD;CACD,OAAO;EACL,OAAO;EACP,MAAM;GAAC;GAAgB;GAAsB;GAAM;EACpD;CACD,SAAS;EACP,OAAO;EACP,MAAM;GACJ;GACA;GACA;GACA;GACA;GACA;GACA;GACD;EACF;CACD,MAAM;EACJ,OAAO;EACP,MAAM,CAAC,eAAe;EACvB;CACD,OAAO;EACL,OAAO;EACP,MAAM,CAAC,iBAAiB,uBAAuB;EAChD;CACD,SAAS;EACP,OAAO;EACP,MAAM,CAAC,mBAAmB,yBAAyB;EACpD;CACD,QAAQ;EACN,OAAO;EACP,MAAM,CAAC,kBAAkB,kBAAkB;EAC5C;CACD,UAAU;EACR,OAAO;EACP,MAAM;GAAC;GAAoB;GAAoB;GAAoB;GAAkB;EACtF;CACD,YAAY;EACV,OAAO;EACP,MAAM,CAAC,mBAAmB,0BAA0B;EACrD;CACD,OAAO;EACL,OAAO;EACP,MAAM,CAAC,gBAAgB;EACxB;CACD,QAAQ;EACN,OAAO;EACP,MAAM,CAAC,iBAAiB;EACzB;CACD,SAAS;EACP,OAAO;EACP,MAAM,CAAC,kBAAkB;EAC1B;CACD,SAAS;EACP,OAAO;EACP,MAAM,CAAC,kBAAkB;EAC1B;CACD,MAAM;EACJ,OAAO;EACP,MAAM,CAAC,eAAe;EACvB;CACD,MAAM;EACJ,OAAO;EACP,MAAM,CAAC,eAAe;EACvB;CACF;AAMD,MAAM,eAAe,QAAQ,OAAO,KAAK,SAAS,uBAAuB;AAIzE,eAAe,OAAO;CAEpB,MAAM,UADO,QAAQ,KAAK,MAAM,EAAE,CACb;AAErB,KAAI,YAAY,YAAY,YAAY,MAAM;AAC5C,UAAQ,IAAI,oCAAoC;AAChD,UAAQ,KAAK,EAAE;;AAGjB,GAAE,MAAM,mCAAmC;CAG3C,MAAM,OACJ,WACC,MAAM,EAAE,KAAK;EACZ,SAAS;EACT,aAAa;EACb,WAAW,MAAM;AACf,OAAI,CAAC,GAAG,MAAM,CAAE,QAAO;AACvB,OAAI,WAAW,QAAQ,QAAQ,KAAK,EAAE,EAAE,CAAC,CAAE,QAAO,cAAc,EAAE;;EAErE,CAAC;AAEJ,KAAI,EAAE,SAAS,KAAK,EAAE;AACpB,IAAE,OAAO,aAAa;AACtB,UAAQ,KAAK,EAAE;;CAGjB,MAAM,YAAY,QAAQ,QAAQ,KAAK,EAAE,KAAe;AACxD,KAAI,WAAW,UAAU,EAAE;AACzB,IAAE,OAAO,cAAc,KAAK,mBAAmB;AAC/C,UAAQ,KAAK,EAAE;;CAIjB,MAAM,aAAa,MAAM,EAAE,OAAO;EAChC,SAAS;EACT,SAAS;GACP;IACE,OAAO;IACP,OAAO;IACP,MAAM;IACP;GACD;IAAE,OAAO;IAAc,OAAO;IAAc,MAAM;IAA0C;GAC5F;IAAE,OAAO;IAAO,OAAO;IAAgB,MAAM;IAA8B;GAC3E;IAAE,OAAO;IAAO,OAAO;IAAO,MAAM;IAAoC;GACzE;EACF,CAAC;AAEF,KAAI,EAAE,SAAS,WAAW,EAAE;AAC1B,IAAE,OAAO,aAAa;AACtB,UAAQ,KAAK,EAAE;;CAIjB,MAAM,WAAW,MAAM,EAAE,YAAY;EACnC,SAAS;EACT,SAAS,OAAO,QAAQ,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,cAAc;GAC3D,OAAO;GACP;GACD,EAAE;EACH,eAAe;GAAC;GAAS;GAAS;GAAQ;EAC1C,UAAU;EACX,CAAC;AAEF,KAAI,EAAE,SAAS,SAAS,EAAE;AACxB,IAAE,OAAO,aAAa;AACtB,UAAQ,KAAK,EAAE;;CAIjB,MAAM,kBAAkB,MAAM,EAAE,OAAO;EACrC,SAAS;EACT,SAAS,CACP;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP,EACD;GACE,OAAO;GACP,OAAO;GACP,MAAM;GACP,CACF;EACF,CAAC;AAEF,KAAI,EAAE,SAAS,gBAAgB,EAAE;AAC/B,IAAE,OAAO,aAAa;AACtB,UAAQ,KAAK,EAAE;;CAIjB,MAAM,cAAc,MAAM,EAAE,QAAQ;EAClC,SAAS;EACT,cAAc;EACf,CAAC;AAEF,KAAI,EAAE,SAAS,YAAY,EAAE;AAC3B,IAAE,OAAO,aAAa;AACtB,UAAQ,KAAK,EAAE;;CAGjB,MAAM,SAAwB;EACtB;EACN;EACY;EACF;EACO;EACJ;EACd;CAED,MAAM,IAAI,EAAE,SAAS;AACrB,GAAE,MAAM,yBAAyB;AAEjC,OAAM,SAAS,OAAO;AAEtB,GAAE,KAAK,mBAAmB;AAG1B,GAAE,KAAK;EAAC,MAAM,OAAO;EAAQ;EAAe;EAAc,CAAC,KAAK,KAAK,EAAE,aAAa;AAEpF,GAAE,MAAM,kBAAkB;;AAK5B,eAAe,SAAS,QAAuB;AAE7C,OAAM,GAAG,cAAc,OAAO,WAAW,EAAE,WAAW,MAAM,CAAC;AAG7D,OAAM,UAAU,KAAK,OAAO,WAAW,eAAe,EAAE,oBAAoB,OAAO,CAAC;AAEpF,OAAM,UAAU,KAAK,OAAO,WAAW,iBAAiB,EAAE,mBAAmB,OAAO,CAAC;AAErF,OAAM,UAAU,KAAK,OAAO,WAAW,sBAAsB,EAAE,oBAAoB,OAAO,CAAC;AAE3F,OAAM,UAAU,KAAK,OAAO,WAAW,WAAW,EAAE,eAAe,OAAO,CAAC;AAG3E,OAAM,UACJ,KAAK,OAAO,WAAW,aAAa,EACpC,oDACD;AAGD,KAAI,OAAO,YACT,OAAM,UACJ,KAAK,OAAO,WAAW,YAAY,EACnC,KAAK,UACH,EACE,YAAY,EACV,QAAQ;EAAE,SAAS;EAAQ,MAAM,CAAC,cAAc;EAAE,EACnD,EACF,EACD,MACA,EACD,CACF;KAID,MAAK,MAAM,KADK,CAAC,aAAa,YAAY,EACjB;EACvB,MAAM,OAAO,KAAK,OAAO,WAAW,EAAE;AACtC,MAAI,WAAW,KAAK,EAAE;GACpB,MAAM,EAAE,WAAW,MAAM,OAAO;AAChC,SAAM,OAAO,KAAK;;;AAMxB,KAAI,CAAC,OAAO,SAAS,SAAS,UAAU,IAAI,CAAC,OAAO,SAAS,SAAS,QAAQ,EAAE;AAC9E,QAAM,eAAe,KAAK,OAAO,WAAW,2BAA2B,CAAC;AACxE,QAAM,eAAe,KAAK,OAAO,WAAW,eAAe,CAAC;;AAG9D,KAAI,CAAC,OAAO,SAAS,SAAS,QAAQ,CACpC,OAAM,eAAe,KAAK,OAAO,WAAW,aAAa,CAAC;AAI5D,KAAI,CAAC,OAAO,SAAS,SAAS,QAAQ,EAAE;EACtC,MAAM,aAAa,KAAK,OAAO,WAAW,yBAAyB;AACnE,MAAI,WAAW,WAAW,EAAE;GAC1B,IAAI,SAAS,MAAM,SAAS,YAAY,QAAQ;AAChD,YAAS,OACN,QAAQ,yCAAyC,GAAG,CACpD,QAAQ,sBAAsB,GAAG,CACjC,QAAQ,yDAAyD,GAAG;AACvE,SAAM,UAAU,YAAY,OAAO;;;;AAQzC,SAAS,cAAc,KAAqB;AAe1C,KAba;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACQ,MAAM,MAAM,QAAQ,WAAW,IAAI,CAAE,QAAO;AAErD,KACE,QAAQ,kBACR,QAAQ,kBACR,QAAQ,sBACR,QAAQ,sBAER,QAAO;AAgBT,KAdqB;EACnB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CACgB,MAAM,MAAM,QAAQ,WAAW,IAAI,CAAE,QAAO;AAE7D,QAAO;;AAGT,SAAS,oBAAoB,QAA+B;CAC1D,MAAM,OAA+B;EACnC,gBAAgB,cAAc,eAAe;EAC7C,gBAAgB,cAAc,eAAe;EAC7C,sBAAsB,cAAc,qBAAqB;EACzD,kBAAkB,cAAc,iBAAiB;EACjD,uBAAuB,cAAc,sBAAsB;EAC3D,0BAA0B,cAAc,yBAAyB;EACjE,kBAAkB,cAAc,iBAAiB;EACjD,gBAAgB,cAAc,eAAe;EAC9C;AAED,KAAI,OAAO,oBAAoB,QAAQ;AAErC,OAAK,kBAAkB,cAAc,eAAe;AAEpD,OAAK,MAAM,OAAO,OAAO,UAAU;GACjC,MAAM,UAAU,SAAS;AACzB,OAAI,SACF;SAAK,MAAM,OAAO,QAAQ,KACxB,KAAI,CAAC,IAAI,WAAW,WAAW,EAC7B;SAAI,IAAI,WAAW,aAAa,CAC9B,MAAK,OAAO,IAAI,SAAS,QAAQ,GAC7B,YACA,IAAI,SAAS,QAAQ,GACnB,YACA;cACG,QAAQ,MACjB,MAAK,OAAO;;;;QAMjB;EAEL,MAAM,0BAAU,IAAI,KAAa;AACjC,OAAK,MAAM,OAAO,OAAO,UAAU;GACjC,MAAM,UAAU,SAAS;AACzB,OAAI,QACF,MAAK,MAAM,OAAO,QAAQ,KAAM,SAAQ,IAAI,IAAI;;AAGpD,OAAK,MAAM,OAAO,QAChB,KAAI,IAAI,WAAW,WAAW,CAC5B,MAAK,OAAO,cAAc,IAAI;WACrB,IAAI,WAAW,aAAa,CACrC,MAAK,OAAO,IAAI,SAAS,QAAQ,GAC7B,YACA,IAAI,SAAS,QAAQ,GACnB,YACA;WACG,QAAQ,MACjB,MAAK,OAAO;;CAKlB,MAAM,UAAkC;EACtC,uBAAuB,cAAc,sBAAsB;EAC3D,oBAAoB,cAAc,mBAAmB;EACrD,YAAY;EACZ,MAAM;EACP;AAED,KAAI,OAAO,YACT,SAAQ,iBAAiB,cAAc,cAAc;CAYvD,MAAM,MAAM;EACV,MAAM,SAAS,OAAO,KAAK;EAC3B,SAAS;EACT,SAAS;EACT,MAAM;EACN,SAdsC;GACtC,KAAK;GACL,OAAO;GACP,SAAS;GACT,QAAQ;GACR,cAAc;GACd,aAAa;GACd;EAQC,cAAc,OAAO,YAAY,OAAO,QAAQ,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CAAC;EAC7F,iBAAiB,OAAO,YACtB,OAAO,QAAQ,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,cAAc,EAAE,CAAC,CAC/D;EACF;AAED,QAAO,GAAG,KAAK,UAAU,KAAK,MAAM,EAAE,CAAC;;AAGzC,SAAS,mBAAmB,QAA+B;AAQzD,QAAO;;;;;;;;aAPS;EACd,cAAc;EACd,cAAc;EACd,KAAK;EACL,KAAK;EACN,CAUkB,OAAO,YAAY;;;;;;;;;;;;;;;;;;;;;;AAuBxC,SAAS,oBAAoB,QAA+B;AAe1D,QAAO,GAdS;EACd;EACA;EACA;EACA;EACD,CASiB,KAAK,KAAK,CAAC;;;;;;oBAPb;EACd,cAAc;EACd,cAAc;EACd,KAAK;EACL,KAAK;EACN,CAQyB,OAAO,YAAY;;;;;;;;;;AAW/C,SAAS,eAAe,QAA+B;CACrD,IAAI,UAAU;;;;;;;;;;;;AAad,KAAI,OAAO,SAAS,SAAS,QAAQ,CACnC,YAAW;;;;;AAOb,QAAO;;AAKT,eAAe,eAAe,MAAc;AAC1C,KAAI,CAAC,WAAW,KAAK,CAAE;CACvB,MAAM,EAAE,OAAO,MAAM,OAAO;AAC5B,OAAM,GAAG,MAAM,EAAE,WAAW,MAAM,CAAC;;AAGrC,MAAM,CAAC,OAAO,QAAQ;AACpB,SAAQ,MAAM,IAAI;AAClB,SAAQ,KAAK,EAAE;EACf"}
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@pyreon/create-zero",
3
- "version": "0.4.1",
3
+ "version": "0.11.0",
4
4
  "description": "Create a new Pyreon Zero project",
5
5
  "license": "MIT",
6
6
  "author": "Vit Bokisch",
7
7
  "repository": {
8
8
  "type": "git",
9
- "url": "https://github.com/pyreon/zero",
10
- "directory": "packages/create-zero"
9
+ "url": "https://github.com/pyreon/pyreon.git",
10
+ "directory": "packages/zero/create-zero"
11
11
  },
12
12
  "type": "module",
13
13
  "main": "./lib/index.js",
@@ -24,7 +24,8 @@
24
24
  ],
25
25
  "scripts": {
26
26
  "build": "vl_rolldown_build",
27
- "typecheck": "tsc --noEmit"
27
+ "typecheck": "tsc --noEmit",
28
+ "lint": "biome check ."
28
29
  },
29
30
  "dependencies": {
30
31
  "@clack/prompts": "^1.0.0"
@@ -15,10 +15,12 @@ This project uses Pyreon Zero, a signal-based full-stack meta-framework. Do NOT
15
15
 
16
16
  - Use `class=` not `className`
17
17
  - Use `for=` not `htmlFor`
18
+ - Use camelCase events: `onClick`, `onMouseEnter`, `onLoad` (not `onclick`, `onmouseenter`)
19
+ - Use `srcSet` not `srcset`, `fetchPriority` not `fetchpriority`
18
20
  - Reactive text: `{() => count()}`
19
21
  - Conditional: `{() => show() ? <A /> : null}`
20
22
  - Lists: `{() => items().map(item => <Item />)}`
21
- - Events: `onClick={() => ...}` (standard DOM events)
23
+ - Events: `onClick={() => ...}`
22
24
  - JSX import source is `@pyreon/core` (auto-configured, no manual import needed)
23
25
 
24
26
  ## File-Based Routing
@@ -1,16 +1,16 @@
1
1
  /// <reference types="vite/client" />
2
2
 
3
- declare module 'virtual:zero/routes' {
4
- import type { RouteRecord } from '@pyreon/router'
3
+ declare module "virtual:zero/routes" {
4
+ import type { RouteRecord } from "@pyreon/router"
5
5
  export const routes: RouteRecord[]
6
6
  }
7
7
 
8
- declare module 'virtual:zero/route-middleware' {
9
- import type { RouteMiddlewareEntry } from '@pyreon/zero'
8
+ declare module "virtual:zero/route-middleware" {
9
+ import type { RouteMiddlewareEntry } from "@pyreon/zero"
10
10
  export const routeMiddleware: RouteMiddlewareEntry[]
11
11
  }
12
12
 
13
- declare module 'virtual:zero/api-routes' {
14
- import type { ApiRouteEntry } from '@pyreon/zero'
13
+ declare module "virtual:zero/api-routes" {
14
+ import type { ApiRouteEntry } from "@pyreon/zero"
15
15
  export const apiRoutes: ApiRouteEntry[]
16
16
  }
@@ -6,7 +6,7 @@
6
6
  <meta name="theme-color" content="#0a0a0b" />
7
7
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
8
8
  <!-- Prevent flash of wrong theme -->
9
- <script>(function(){try{var t=localStorage.getItem("zero-theme");var r=t==="light"?"light":t==="dark"?"dark":window.matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light";document.documentElement.dataset.theme=r}catch(e){}})()</script>
9
+ <script>(()=> {try{var t=localStorage.getItem("zero-theme");var r=t==="light"?"light":t==="dark"?"dark":window.matchMedia("(prefers-color-scheme:dark)").matches?"dark":"light";document.documentElement.dataset.theme=r}catch(e){}})()</script>
10
10
  <!--pyreon-head-->
11
11
  </head>
12
12
  <body>
@@ -12,17 +12,17 @@
12
12
  "doctor:ci": "zero doctor --ci"
13
13
  },
14
14
  "dependencies": {
15
- "@pyreon/meta": "^0.3.0",
16
- "@pyreon/zero": "^0.3.0",
15
+ "@pyreon/meta": "latest",
16
+ "@pyreon/zero": "latest",
17
17
  "@tanstack/query-core": "^5.90.0",
18
18
  "@tanstack/table-core": "^8.21.0",
19
19
  "@tanstack/virtual-core": "^3.13.0",
20
20
  "zod": "^4.0.0"
21
21
  },
22
22
  "devDependencies": {
23
- "@pyreon/mcp": "^0.7.5",
24
- "@pyreon/vite-plugin": "^0.7.5",
25
- "@pyreon/zero-cli": "^0.3.0",
23
+ "@pyreon/mcp": "latest",
24
+ "@pyreon/vite-plugin": "latest",
25
+ "@pyreon/zero-cli": "latest",
26
26
  "typescript": "^6.0.2",
27
27
  "vite": "^8.0.0"
28
28
  }
@@ -1,5 +1,5 @@
1
- import './global.css'
2
- import { routes } from 'virtual:zero/routes'
3
- import { startClient } from '@pyreon/zero/client'
1
+ import "./global.css"
2
+ import { routes } from "virtual:zero/routes"
3
+ import { startClient } from "@pyreon/zero/client"
4
4
 
5
5
  startClient({ routes })
@@ -1,25 +1,21 @@
1
- import { apiRoutes } from 'virtual:zero/api-routes'
2
- import { routeMiddleware } from 'virtual:zero/route-middleware'
3
- import { routes } from 'virtual:zero/routes'
4
- import { createServer } from '@pyreon/zero'
5
- import {
6
- cacheMiddleware,
7
- securityHeaders,
8
- varyEncoding,
9
- } from '@pyreon/zero/cache'
10
- import { corsMiddleware } from '@pyreon/zero/cors'
11
- import { rateLimitMiddleware } from '@pyreon/zero/rate-limit'
1
+ import { apiRoutes } from "virtual:zero/api-routes"
2
+ import { routeMiddleware } from "virtual:zero/route-middleware"
3
+ import { routes } from "virtual:zero/routes"
4
+ import { createServer } from "@pyreon/zero"
5
+ import { cacheMiddleware, securityHeaders, varyEncoding } from "@pyreon/zero/cache"
6
+ import { corsMiddleware } from "@pyreon/zero/cors"
7
+ import { rateLimitMiddleware } from "@pyreon/zero/rate-limit"
12
8
 
13
9
  export default createServer({
14
10
  routes,
15
11
  routeMiddleware,
16
12
  apiRoutes,
17
13
  config: {
18
- ssr: { mode: 'stream' },
14
+ ssr: { mode: "stream" },
19
15
  },
20
16
  middleware: [
21
17
  corsMiddleware(),
22
- rateLimitMiddleware({ max: 100, window: 60, include: ['/api/*'] }),
18
+ rateLimitMiddleware({ max: 100, window: 60, include: ["/api/*"] }),
23
19
  securityHeaders(),
24
20
  cacheMiddleware({ staleWhileRevalidate: 120 }),
25
21
  varyEncoding(),
@@ -1,12 +1,12 @@
1
- import { defineFeature } from '@pyreon/feature'
2
- import { z } from 'zod'
1
+ import { defineFeature } from "@pyreon/feature"
2
+ import { z } from "zod"
3
3
 
4
4
  export const posts = defineFeature({
5
- name: 'posts',
5
+ name: "posts",
6
6
  schema: z.object({
7
- title: z.string().min(3, 'Title must be at least 3 characters'),
8
- body: z.string().min(10, 'Body must be at least 10 characters'),
7
+ title: z.string().min(3, "Title must be at least 3 characters"),
8
+ body: z.string().min(10, "Body must be at least 10 characters"),
9
9
  published: z.boolean(),
10
10
  }),
11
- api: '/api/posts',
11
+ api: "/api/posts",
12
12
  })
@@ -1,9 +1,9 @@
1
- import { useHead } from '@pyreon/head'
2
- import type { MiddlewareContext } from '@pyreon/server'
3
- import type { LoaderContext } from '@pyreon/zero'
1
+ import { useHead } from "@pyreon/head"
2
+ import type { MiddlewareContext } from "@pyreon/server"
3
+ import type { LoaderContext } from "@pyreon/zero"
4
4
 
5
5
  export const meta = {
6
- title: 'Dashboard — Pyreon Zero',
6
+ title: "Dashboard — Pyreon Zero",
7
7
  }
8
8
 
9
9
  /**
@@ -16,11 +16,10 @@ export const meta = {
16
16
  export function guard() {
17
17
  // Simulate auth check — in a real app, check session/token
18
18
  const isAuthenticated =
19
- typeof window !== 'undefined' &&
20
- localStorage.getItem('zero-demo-auth') === 'true'
19
+ typeof window !== "undefined" && localStorage.getItem("zero-demo-auth") === "true"
21
20
 
22
21
  if (!isAuthenticated) {
23
- return '/about' // Redirect unauthenticated users
22
+ return "/about" // Redirect unauthenticated users
24
23
  }
25
24
  return true // Allow navigation
26
25
  }
@@ -34,16 +33,16 @@ export function guard() {
34
33
  */
35
34
  export const middleware = (ctx: MiddlewareContext) => {
36
35
  // Add server timing header to track route performance
37
- ctx.headers.set('Server-Timing', `route;desc="Dashboard"`)
36
+ ctx.headers.set("Server-Timing", `route;desc="Dashboard"`)
38
37
  }
39
38
 
40
39
  export async function loader(_ctx: LoaderContext) {
41
40
  return {
42
- user: 'Demo User',
41
+ user: "Demo User",
43
42
  stats: {
44
43
  views: 12_847,
45
44
  routes: 6,
46
- buildTime: '1.2s',
45
+ buildTime: "1.2s",
47
46
  },
48
47
  }
49
48
  }
@@ -57,8 +56,8 @@ export default function Dashboard() {
57
56
  <span class="badge">Protected Route</span>
58
57
  <h1 style="margin-top: var(--space-md);">Dashboard</h1>
59
58
  <p>
60
- This route is protected by a <code>guard</code> function and uses
61
- per-route <code>middleware</code> for server-side logging.
59
+ This route is protected by a <code>guard</code> function and uses per-route{" "}
60
+ <code>middleware</code> for server-side logging.
62
61
  </p>
63
62
  </div>
64
63
 
@@ -77,31 +76,27 @@ export default function Dashboard() {
77
76
  </div>
78
77
  </div>
79
78
 
80
- <div
81
- class="code-block"
82
- style="max-width: 520px; margin: var(--space-2xl) auto 0;"
83
- >
79
+ <div class="code-block" style="max-width: 520px; margin: var(--space-2xl) auto 0;">
84
80
  <div class="code-block-header">
85
81
  <span>dashboard.tsx</span>
86
82
  </div>
87
83
  <pre>
88
84
  <code>
89
- <span class="cm">{'// Navigation guard — protect routes'}</span>
90
- <span class="kw">export function</span>{' '}
91
- <span class="fn">guard</span>() {'{'}
92
- <span class="kw">if</span> (!isAuthenticated) {'{'}
93
- <span class="kw">return</span> <span class="str">"/login"</span>{' '}
94
- <span class="cm">{'// redirect'}</span>
95
- {'}'}
96
- <span class="kw">return</span> <span class="str">true</span>{' '}
97
- <span class="cm">{'// allow'}</span>
98
- {'}'}
99
- <span class="cm">{'// Per-route server middleware'}</span>
100
- <span class="kw">export const</span>{' '}
101
- <span class="fn">middleware</span> = (req, next) =&gt; {'{'}
102
- <span class="cm">{'// logging, auth, rate limiting...'}</span>
85
+ <span class="cm">{"// Navigation guard — protect routes"}</span>
86
+ <span class="kw">export function</span> <span class="fn">guard</span>() {"{"}
87
+ <span class="kw">if</span> (!isAuthenticated) {"{"}
88
+ <span class="kw">return</span> <span class="str">"/login"</span>{" "}
89
+ <span class="cm">{"// redirect"}</span>
90
+ {"}"}
91
+ <span class="kw">return</span> <span class="str">true</span>{" "}
92
+ <span class="cm">{"// allow"}</span>
93
+ {"}"}
94
+ <span class="cm">{"// Per-route server middleware"}</span>
95
+ <span class="kw">export const</span> <span class="fn">middleware</span> = (req, next)
96
+ =&gt; {"{"}
97
+ <span class="cm">{"// logging, auth, rate limiting..."}</span>
103
98
  <span class="kw">return</span> <span class="fn">next</span>(req)
104
- {'}'}
99
+ {"}"}
105
100
  </code>
106
101
  </pre>
107
102
  </div>
@@ -110,9 +105,9 @@ export default function Dashboard() {
110
105
  <button
111
106
  type="button"
112
107
  class="btn btn-secondary"
113
- onclick={() => {
114
- localStorage.removeItem('zero-demo-auth')
115
- window.location.href = '/about'
108
+ onClick={() => {
109
+ localStorage.removeItem("zero-demo-auth")
110
+ window.location.href = "/about"
116
111
  }}
117
112
  >
118
113
  Log out (clear demo auth)
@@ -1,22 +1,17 @@
1
- import { useHead } from '@pyreon/head'
2
- import { Link } from '@pyreon/zero/link'
1
+ import { useHead } from "@pyreon/head"
2
+ import { Link } from "@pyreon/zero/link"
3
3
 
4
4
  export default function ErrorPage() {
5
- useHead({ title: 'Something went wrong — Zero' })
5
+ useHead({ title: "Something went wrong — Zero" })
6
6
 
7
7
  return (
8
8
  <div class="error-page">
9
9
  <div class="error-code">500</div>
10
10
  <h1>Something went wrong</h1>
11
11
  <p style="color: var(--c-text-secondary); max-width: 400px;">
12
- An unexpected error occurred. Try refreshing the page or navigating back
13
- home.
12
+ An unexpected error occurred. Try refreshing the page or navigating back home.
14
13
  </p>
15
- <Link
16
- href="/"
17
- class="btn btn-primary"
18
- style="margin-top: var(--space-md);"
19
- >
14
+ <Link href="/" class="btn btn-primary" style="margin-top: var(--space-md);">
20
15
  Back to Home
21
16
  </Link>
22
17
  </div>
@@ -1,14 +1,15 @@
1
- import { QueryClient, QueryClientProvider } from '@pyreon/query'
2
- import { Link } from '@pyreon/zero/link'
3
- import { ThemeToggle } from '@pyreon/zero/theme'
4
- import { useAppStore } from '../stores/app'
1
+ import { QueryClient, QueryClientProvider } from "@pyreon/query"
2
+ import { Link } from "@pyreon/zero/link"
3
+ import { ThemeToggle } from "@pyreon/zero/theme"
4
+ import { useAppStore } from "../stores/app"
5
5
 
6
6
  const queryClient = new QueryClient({
7
7
  defaultOptions: { queries: { staleTime: 30000 } },
8
8
  })
9
9
 
10
10
  export function layout(props: { children: any }) {
11
- const { sidebarOpen, toggleSidebar } = useAppStore()
11
+ const { store } = useAppStore()
12
+ const { sidebarOpen, toggleSidebar } = store
12
13
 
13
14
  return (
14
15
  <QueryClientProvider client={queryClient}>
@@ -22,11 +23,7 @@ export function layout(props: { children: any }) {
22
23
  <Link href="/" prefetch="viewport" exactActiveClass="nav-active">
23
24
  Home
24
25
  </Link>
25
- <Link
26
- href="/counter"
27
- prefetch="hover"
28
- exactActiveClass="nav-active"
29
- >
26
+ <Link href="/counter" prefetch="hover" exactActiveClass="nav-active">
30
27
  Counter
31
28
  </Link>
32
29
  <Link href="/posts" prefetch="hover" activeClass="nav-active">
@@ -35,11 +32,7 @@ export function layout(props: { children: any }) {
35
32
  <Link href="/about" prefetch="hover" exactActiveClass="nav-active">
36
33
  About
37
34
  </Link>
38
- <Link
39
- href="/dashboard"
40
- prefetch="hover"
41
- exactActiveClass="nav-active"
42
- >
35
+ <Link href="/dashboard" prefetch="hover" exactActiveClass="nav-active">
43
36
  Dashboard
44
37
  </Link>
45
38
  <button
@@ -48,7 +41,7 @@ export function layout(props: { children: any }) {
48
41
  onClick={toggleSidebar}
49
42
  title="Toggle sidebar"
50
43
  >
51
- {() => (sidebarOpen() ? '' : '')}
44
+ {() => (sidebarOpen() ? "" : "")}
52
45
  </button>
53
46
  <ThemeToggle class="theme-toggle" />
54
47
  </nav>
@@ -57,9 +50,7 @@ export function layout(props: { children: any }) {
57
50
 
58
51
  <main class="app-main">{props.children}</main>
59
52
 
60
- <footer class="app-footer">
61
- Built with Pyreon Zero — signal-based, blazing fast.
62
- </footer>
53
+ <footer class="app-footer">Built with Pyreon Zero — signal-based, blazing fast.</footer>
63
54
  </QueryClientProvider>
64
55
  )
65
56
  }