@emeryld/create-rrroutes 0.1.6 → 0.1.8

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/dist/index.cjs CHANGED
@@ -331,7 +331,7 @@ export function HealthPage() {
331
331
  const [echo, setEcho] = React.useState('hello rrroute')
332
332
  const httpGet = healthGet.useEndpoint()
333
333
  const httpPost = healthPost.useEndpoint()
334
- const socket = useSocketClient<typeof import('${contractImport}').socketEvents>()
334
+ const socket = useSocketClient()
335
335
  const { logs, push, clear } = useLogs()
336
336
 
337
337
  useSocketConnection({
@@ -706,7 +706,7 @@ export function HealthScreen() {
706
706
  const [echo, setEcho] = React.useState('hello rrroute')
707
707
  const httpGet = healthGet.useEndpoint()
708
708
  const httpPost = healthPost.useEndpoint()
709
- const socket = useSocketClient<typeof import('${contractImport}').socketEvents>()
709
+ const socket = useSocketClient()
710
710
  const { logs, push, clear } = useLogs()
711
711
 
712
712
  useSocketConnection({
@@ -961,7 +961,7 @@ const sockets = defineSocketEvents(
961
961
  sentAt: z.string(),
962
962
  }),
963
963
  pongPayload: z.object({
964
- ok: z.literal(true),
964
+ ok: z.boolean(),
965
965
  receivedAt: z.string(),
966
966
  echo: z.string().optional(),
967
967
  }),
@@ -1050,6 +1050,15 @@ function formatJson(obj) {
1050
1050
  }
1051
1051
  async function scaffoldRoot(ctx) {
1052
1052
  await ensureDir(ctx.rootDir);
1053
+ const paths = {};
1054
+ const addPaths = (pkgName, dir) => {
1055
+ const srcRoot = import_node_path4.default.posix.join("packages", dir, "src");
1056
+ paths[pkgName] = [import_node_path4.default.posix.join(srcRoot, "index.ts")];
1057
+ paths[`${pkgName}/*`] = [import_node_path4.default.posix.join(srcRoot, "*")];
1058
+ };
1059
+ if (ctx.targets.contract) addPaths(ctx.packageNames.contract, "contract");
1060
+ if (ctx.targets.server) addPaths(ctx.packageNames.server, "server");
1061
+ if (ctx.targets.client) addPaths(ctx.packageNames.client, "client");
1053
1062
  const scripts = {
1054
1063
  build: "npm run build --workspaces",
1055
1064
  typecheck: "npm run typecheck --workspaces --if-present",
@@ -1124,7 +1133,8 @@ async function scaffoldRoot(ctx) {
1124
1133
  resolveJsonModule: true,
1125
1134
  forceConsistentCasingInFileNames: true,
1126
1135
  sourceMap: true,
1127
- baseUrl: "."
1136
+ baseUrl: ".",
1137
+ paths
1128
1138
  }
1129
1139
  }),
1130
1140
  ".gitignore": [
@@ -1203,7 +1213,7 @@ main().catch(async (err) => {
1203
1213
  `;
1204
1214
  }
1205
1215
  function httpTs(contractImport) {
1206
- return `import express from 'express'
1216
+ return `import express, { Router } from 'express'
1207
1217
  import cors from 'cors'
1208
1218
  import { createRRRoute } from '@emeryld/rrroutes-server'
1209
1219
  import { registry } from '${contractImport}'
@@ -1212,7 +1222,7 @@ import { prisma } from './prisma'
1212
1222
 
1213
1223
  const CORS_ORIGIN = process.env.CORS_ORIGIN ?? 'http://localhost:5173'
1214
1224
 
1215
- export const app = express()
1225
+ export const app: Router = express()
1216
1226
  app.use(cors({ origin: CORS_ORIGIN, credentials: true }))
1217
1227
  app.use(express.json())
1218
1228
 
@@ -1254,8 +1264,10 @@ export const controllers = defineControllers<typeof registry, RequestCtx>()({
1254
1264
  data: { note: 'GET health' },
1255
1265
  })
1256
1266
  return {
1267
+ out:{
1257
1268
  status: 'ok',
1258
1269
  html: '<!doctype html><html><body><h1>healthy</h1></body></html>',
1270
+ }
1259
1271
  }
1260
1272
  },
1261
1273
  },
@@ -1265,8 +1277,10 @@ export const controllers = defineControllers<typeof registry, RequestCtx>()({
1265
1277
  data: { note: body?.echo ?? 'POST health' },
1266
1278
  })
1267
1279
  return {
1280
+ out:{
1268
1281
  status: 'ok',
1269
1282
  received: body?.echo ?? 'pong',
1283
+ }
1270
1284
  }
1271
1285
  },
1272
1286
  },
@@ -1361,6 +1375,21 @@ function serverEnv() {
1361
1375
  "DATABASE_URL=postgresql://postgres:postgres@localhost:5432/rrroutes"
1362
1376
  ].join("\n");
1363
1377
  }
1378
+ var tsNodeLoaderRegistrationImport = 'data:text/javascript,import { register } from "node:module"; import { pathToFileURL } from "node:url"; register("ts-node/esm", pathToFileURL("./"));';
1379
+ var nodemonExecCommand = `node --import '${tsNodeLoaderRegistrationImport}' ./src/index.ts`;
1380
+ function nodemonJson() {
1381
+ return `${JSON.stringify(
1382
+ {
1383
+ watch: ["src"],
1384
+ ext: "ts",
1385
+ ignore: ["src/**/*.test.ts"],
1386
+ exec: nodemonExecCommand
1387
+ },
1388
+ null,
1389
+ 2
1390
+ )}
1391
+ `;
1392
+ }
1364
1393
  function dockerfile(serverName, contractName) {
1365
1394
  return `FROM node:20-alpine
1366
1395
  WORKDIR /app
@@ -1397,7 +1426,7 @@ async function scaffoldServer(ctx) {
1397
1426
  types: "dist/index.d.ts",
1398
1427
  files: ["dist"],
1399
1428
  scripts: {
1400
- dev: 'nodemon --watch src --ext ts --exec "node --loader ts-node/esm --experimental-specifier-resolution=node src/index.ts"',
1429
+ dev: "nodemon --config nodemon.json",
1401
1430
  build: "tsc -p tsconfig.json",
1402
1431
  typecheck: "tsc -p tsconfig.json --noEmit",
1403
1432
  start: "node dist/index.js",
@@ -1448,6 +1477,7 @@ async function scaffoldServer(ctx) {
1448
1477
  "prisma/schema.prisma": prismaSchema(),
1449
1478
  ".env": `${serverEnv()}
1450
1479
  `,
1480
+ "nodemon.json": nodemonJson(),
1451
1481
  Dockerfile: dockerfile(ctx.packageNames.server, ctx.packageNames.contract)
1452
1482
  };
1453
1483
  for (const [name, contents] of Object.entries(files)) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/generators/client.ts","../src/utils/fs.ts","../src/utils/log.ts","../src/generators/contract.ts","../src/generators/root.ts","../src/generators/server.ts","../src/utils/prompt.ts","../src/utils/run.ts","../src/utils/strings.ts"],"sourcesContent":["import path from 'node:path'\nimport { scaffoldClient } from './generators/client'\nimport { scaffoldContract } from './generators/contract'\nimport { scaffoldRoot } from './generators/root'\nimport { scaffoldServer } from './generators/server'\nimport { GeneratorContext, Targets, type ClientKind } from './types'\nimport { log } from './utils/log'\nimport { createPrompt } from './utils/prompt'\nimport { runCommand } from './utils/run'\nimport { slugify } from './utils/strings'\n\nfunction resolveTargets(choice: string): Targets {\n if (choice === 'all')\n return { client: true, server: true, contract: true } as const\n if (choice === 'client')\n return { client: true, server: false, contract: true } as const\n if (choice === 'server')\n return { client: false, server: true, contract: true } as const\n return { client: false, server: false, contract: true } as const\n}\n\nasync function gatherContext(): Promise<GeneratorContext> {\n const prompt = createPrompt()\n try {\n const projectName =\n (await prompt.text('Project name', 'rrroutes-starter')) ??\n 'rrroutes-starter'\n const appSlug = slugify(projectName)\n const targetDirInput =\n (await prompt.text('Target directory', `./${appSlug}`)) ?? `./${appSlug}`\n const rootDir = path.resolve(process.cwd(), targetDirInput)\n const scopeInput =\n (await prompt.text('NPM scope (without @)', appSlug)) ?? appSlug\n const packageScope = slugify(scopeInput)\n\n const selection = await prompt.select('What should we create?', [\n {\n label: 'Everything (contract + server + client)',\n value: 'all',\n },\n { label: 'Contract only', value: 'contract' },\n { label: 'Backend only', value: 'server' },\n { label: 'Frontend only', value: 'client' },\n ])\n const targets = resolveTargets(selection)\n\n let clientKind: ClientKind = 'react'\n if (targets.client) {\n clientKind = await prompt.select<ClientKind>(\n 'Frontend runtime',\n [\n { label: 'React (web, Vite)', value: 'react' },\n { label: 'React Native (Expo)', value: 'react-native' },\n ],\n 0,\n )\n }\n\n const packageNames = {\n contract: `@${packageScope}/contract`,\n server: `@${packageScope}/server`,\n client: `@${packageScope}/client`,\n }\n\n return {\n projectName,\n appSlug,\n packageScope,\n rootDir,\n clientKind,\n targets,\n packageNames,\n }\n } finally {\n prompt.close()\n }\n}\n\nasync function runPostScaffold(ctx: GeneratorContext) {\n if (process.env.CREATERRROUTES_SKIP_POSTINSTALL === '1') {\n log.warn('Skipping install/build steps (CREATERRROUTES_SKIP_POSTINSTALL=1).')\n return\n }\n\n const steps: { label: string; args: string[]; enabled: boolean }[] = [\n {\n label: 'Installing workspace dependencies (pnpm install)',\n args: ['install'],\n enabled: true,\n },\n {\n label: `Building shared contract (${ctx.packageNames.contract})`,\n args: ['--filter', ctx.packageNames.contract, 'build'],\n enabled: ctx.targets.contract,\n },\n {\n label: `Generating Prisma client for ${ctx.packageNames.server}`,\n args: ['--filter', ctx.packageNames.server, 'prisma:generate'],\n enabled: ctx.targets.server,\n },\n ]\n\n for (const step of steps) {\n if (!step.enabled) continue\n try {\n log.step(step.label)\n await runCommand('pnpm', step.args, ctx.rootDir)\n } catch (err) {\n log.warn(\n `Step failed (${step.label}). Run manually: pnpm ${step.args.join(' ')}\\n${String(err)}`,\n )\n break\n }\n }\n}\n\nasync function main() {\n const ctx = await gatherContext()\n log.info(`Scaffolding RRRoutes starter in ${ctx.rootDir}`)\n\n await scaffoldRoot(ctx)\n if (ctx.targets.contract) await scaffoldContract(ctx)\n if (ctx.targets.server) await scaffoldServer(ctx)\n if (ctx.targets.client) await scaffoldClient(ctx)\n\n await runPostScaffold(ctx)\n\n log.info('Done. Install dependencies and start hacking!')\n}\n\nmain().catch((err) => {\n log.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n})\n","import path from 'node:path'\nimport { GeneratorContext } from '../types'\nimport { ensureDir, writeFileForce, writeFileIfMissing } from '../utils/fs'\nimport { log } from '../utils/log'\n\nfunction baseTsConfig() {\n return {\n extends: '../../tsconfig.base.json',\n compilerOptions: {\n outDir: 'dist',\n rootDir: 'src',\n types: ['vite/client'],\n },\n include: ['src/**/*', 'vite.config.ts'],\n }\n}\n\nfunction viteConfig() {\n return `import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n plugins: [react()],\n})\n`\n}\n\nfunction queryClient(contractImport: string) {\n return `import { QueryClient } from '@tanstack/react-query'\nimport { createRouteClient } from '@emeryld/rrroutes-client'\nimport { registry } from '${contractImport}'\n\nconst baseUrl = import.meta.env.VITE_API_URL ?? 'http://localhost:4000'\n\nexport const queryClient = new QueryClient()\n\nexport const routeClient = createRouteClient({\n baseUrl,\n queryClient,\n environment: import.meta.env.MODE === 'production' ? 'production' : 'development',\n})\n\nexport const healthGet = routeClient.build(registry.byKey['GET /api/health'])\nexport const healthPost = routeClient.build(registry.byKey['POST /api/health'])\n`\n}\n\nfunction socketProvider(contractImport: string) {\n return `import React from 'react'\nimport { io, type Socket } from 'socket.io-client'\nimport {\n buildSocketProvider,\n type SocketClientOptions,\n} from '@emeryld/rrroutes-client'\nimport { socketConfig, socketEvents } from '${contractImport}'\n\nconst socketUrl = import.meta.env.VITE_SOCKET_URL ?? 'http://localhost:4000'\nconst socketPath = import.meta.env.VITE_SOCKET_PATH ?? '/socket.io'\n\nconst sysEvents: SocketClientOptions<\n typeof socketEvents,\n typeof socketConfig\n>['sys'] = {\n 'sys:connect': async ({ socket }) => {\n console.info('socket connected', socket.id)\n },\n 'sys:disconnect': async ({ reason }) => {\n console.info('socket disconnected', reason)\n },\n 'sys:reconnect': async ({ attempt, socket }) => {\n console.info('socket reconnect', attempt, socket?.id)\n },\n 'sys:connect_error': async ({ error }) => {\n console.warn('socket connect error', error)\n },\n 'sys:ping': () => ({\n note: 'client-heartbeat',\n sentAt: new Date().toISOString(),\n }),\n 'sys:pong': async ({ payload }) => {\n console.info('socket pong', payload)\n },\n 'sys:room_join': async ({ rooms }) => {\n console.info('joining rooms', rooms)\n return true\n },\n 'sys:room_leave': async ({ rooms }) => {\n console.info('leaving rooms', rooms)\n return true\n },\n}\n\nconst baseOptions: Omit<\n SocketClientOptions<typeof socketEvents, typeof socketConfig>,\n 'socket'\n> = {\n config: socketConfig,\n sys: sysEvents,\n environment: import.meta.env.MODE === 'production' ? 'production' : 'development',\n heartbeat: { intervalMs: 15_000, timeoutMs: 7_500 },\n debug: {\n connection: true,\n heartbeat: true,\n },\n}\n\nconst { SocketProvider, useSocketClient, useSocketConnection } =\n buildSocketProvider({\n events: socketEvents,\n options: baseOptions,\n })\n\nfunction getSocket(): Promise<Socket> {\n return Promise.resolve(\n io(socketUrl, {\n path: socketPath,\n transports: ['websocket'],\n }),\n )\n}\n\nexport const roomMeta = { room: 'health' }\n\nexport function AppSocketProvider(props: React.PropsWithChildren) {\n return (\n <SocketProvider\n getSocket={getSocket}\n destroyLeaveMeta={roomMeta}\n fallback={<p>Connecting socket…</p>}\n >\n {props.children}\n </SocketProvider>\n )\n}\n\nexport { useSocketClient, useSocketConnection }\n`\n}\n\nconst MAIN_TSX = `import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './styles.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n)\n`\n\nfunction appTsx() {\n return `import React from 'react'\nimport { QueryClientProvider } from '@tanstack/react-query'\nimport { queryClient } from './lib/queryClient'\nimport { AppSocketProvider } from './lib/socket'\nimport { HealthPage } from './pages/HealthPage'\n\nexport default function App() {\n return (\n <QueryClientProvider client={queryClient}>\n <AppSocketProvider>\n <HealthPage />\n </AppSocketProvider>\n </QueryClientProvider>\n )\n}\n`\n}\n\nconst STYLES = `:root {\n font-family: 'Inter', system-ui, -apple-system, sans-serif;\n color: #0b1021;\n background: #f7f8fb;\n}\n\nbody {\n margin: 0;\n background: radial-gradient(circle at 20% 20%, #eef2ff, #f7f8fb 45%);\n min-height: 100vh;\n}\n\n.health {\n max-width: 960px;\n margin: 0 auto;\n padding: 32px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n}\n\n.card {\n background: #fff;\n border: 1px solid #e5e7eb;\n border-radius: 12px;\n padding: 16px;\n box-shadow: 0 8px 24px rgba(12, 18, 32, 0.05);\n}\n\nbutton {\n background: #0f172a;\n color: #fff;\n border: none;\n border-radius: 8px;\n padding: 10px 12px;\n cursor: pointer;\n font-weight: 600;\n}\n\nbutton.secondary {\n background: #e2e8f0;\n color: #0f172a;\n}\n\nbutton + button {\n margin-left: 8px;\n}\n\ntextarea {\n width: 100%;\n min-height: 80px;\n}\n\n.logs {\n font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo,\n monospace;\n background: #0f172a;\n color: #e2e8f0;\n padding: 12px;\n border-radius: 8px;\n min-height: 160px;\n white-space: pre-line;\n}\n`\n\nfunction healthPage(contractImport: string) {\n return `import React from 'react'\nimport { healthGet, healthPost } from '../lib/queryClient'\nimport { roomMeta, useSocketClient, useSocketConnection } from '../lib/socket'\n\nconst now = () => new Date().toLocaleTimeString()\n\nfunction useLogs() {\n const [logs, setLogs] = React.useState<string[]>([])\n return {\n logs,\n push: (msg: string) =>\n setLogs((prev) => [\\`[\\${now()}] \\${msg}\\`, ...prev].slice(0, 60)),\n clear: () => setLogs([]),\n }\n}\n\nexport function HealthPage() {\n const [echo, setEcho] = React.useState('hello rrroute')\n const httpGet = healthGet.useEndpoint()\n const httpPost = healthPost.useEndpoint()\n const socket = useSocketClient<typeof import('${contractImport}').socketEvents>()\n const { logs, push, clear } = useLogs()\n\n useSocketConnection({\n event: 'health:connected',\n rooms: ['health'],\n joinMeta: roomMeta,\n leaveMeta: roomMeta,\n onMessage: (payload) => {\n push(\\`socket connected (\\${payload.socketId})\\`)\n },\n })\n\n useSocketConnection({\n event: 'health:pong',\n rooms: ['health'],\n joinMeta: roomMeta,\n leaveMeta: roomMeta,\n onMessage: (payload) => {\n push(\n \\`pong at \\${payload.at}\\${payload.echo ? \\` (echo: \\${payload.echo})\\` : ''}\\`,\n )\n },\n })\n\n return (\n <div className=\"health\">\n <h1>RRRoutes health sandbox</h1>\n <div className=\"grid\">\n <div className=\"card\">\n <h2>HTTP endpoints</h2>\n <p>GET and POST against the shared contract.</p>\n <div>\n <button onClick={() => httpGet.refetch()}>GET /health</button>\n {httpGet.error ? (\n <p style={{ color: 'crimson', marginTop: 6 }}>\n GET error: {String(httpGet.error)}\n </p>\n ) : null}\n </div>\n <div style={{ marginTop: 12 }}>\n <input\n value={echo}\n onChange={(e) => setEcho(e.target.value)}\n placeholder=\"echo payload\"\n style={{ width: '100%', padding: '8px' }}\n />\n <div style={{ marginTop: 8 }}>\n <button\n onClick={() =>\n httpPost.mutateAsync({ echo }).then(() => {\n push('POST /health ok')\n })\n }\n >\n POST /health\n </button>\n </div>\n {httpPost.error ? (\n <p style={{ color: 'crimson', marginTop: 6 }}>\n POST error: {String(httpPost.error)}\n </p>\n ) : null}\n </div>\n <div style={{ marginTop: 12 }}>\n <strong>GET data</strong>\n <pre>{JSON.stringify(httpGet.data, null, 2) ?? 'none'}</pre>\n <strong>POST data</strong>\n <pre>{JSON.stringify(httpPost.data, null, 2) ?? 'none'}</pre>\n </div>\n </div>\n\n <div className=\"card\">\n <h2>Socket</h2>\n <p>Connect, ping, and watch lifecycle events.</p>\n <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>\n <button onClick={() => socket.connect()}>Connect</button>\n <button className=\"secondary\" onClick={() => socket.disconnect()}>\n Disconnect\n </button>\n <button\n onClick={() =>\n socket.emit('health:ping', { note: 'ping from client' })\n }\n >\n Emit ping\n </button>\n <button\n className=\"secondary\"\n onClick={() => socket.joinRooms(['health'], roomMeta)}\n >\n Join room\n </button>\n <button\n className=\"secondary\"\n onClick={() => socket.leaveRooms(['health'], roomMeta)}\n >\n Leave room\n </button>\n </div>\n {httpGet.error || httpPost.error ? (\n <p style={{ color: 'crimson', marginTop: 8 }}>\n Check server logs; errors will also show above.\n </p>\n ) : null}\n </div>\n\n <div className=\"card\">\n <h2>Socket logs</h2>\n <button className=\"secondary\" onClick={clear}>\n Clear\n </button>\n <div className=\"logs\">\n {logs.length === 0 ? 'No messages yet' : logs.join('\\\\n')}\n </div>\n </div>\n </div>\n </div>\n )\n}\n`\n}\n\nfunction indexHtml() {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>RRRoutes starter</title>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/src/main.tsx\"></script>\n </body>\n</html>\n`\n}\n\nconst CLIENT_ENV = `VITE_API_URL=http://localhost:4000\nVITE_SOCKET_URL=http://localhost:4000\nVITE_SOCKET_PATH=/socket.io\n`\n\n// React Native / Expo template ------------------------------------------------\n\nfunction nativePackageJson(clientName: string, contractName: string) {\n return {\n name: clientName,\n version: '0.1.0',\n private: true,\n main: 'expo/AppEntry',\n scripts: {\n start: 'expo start',\n android: 'expo start --android',\n ios: 'expo start --ios',\n web: 'expo start --web',\n test: 'echo \"add tests\"',\n },\n dependencies: {\n [contractName]: 'workspace:*',\n '@emeryld/rrroutes-client': '^2.5.3',\n '@tanstack/react-query': '^5.90.12',\n expo: '~52.0.8',\n 'expo-constants': '~16.0.2',\n 'expo-status-bar': '~2.0.1',\n react: '18.3.1',\n 'react-native': '0.76.6',\n 'react-native-safe-area-context': '4.12.0',\n 'react-native-screens': '4.4.0',\n 'socket.io-client': '^4.8.3',\n zod: '^4.2.1',\n },\n devDependencies: {\n '@babel/core': '^7.25.2',\n '@types/react': '~18.2.79',\n '@types/react-native': '~0.73.0',\n typescript: '^5.9.3',\n },\n }\n}\n\nfunction nativeAppJson(appSlug: string) {\n return {\n expo: {\n name: `${appSlug}-client`,\n slug: `${appSlug}-client`,\n version: '0.1.0',\n orientation: 'portrait',\n scheme: appSlug,\n platforms: ['ios', 'android', 'web'],\n userInterfaceStyle: 'light',\n extra: {\n apiUrl: 'http://localhost:4000',\n socketUrl: 'http://localhost:4000',\n socketPath: '/socket.io',\n },\n experiments: {\n typedRoutes: false,\n },\n },\n }\n}\n\nconst NATIVE_BABEL = `module.exports = function (api) {\n api.cache(true)\n return {\n presets: ['babel-preset-expo'],\n }\n}\n`\n\nconst NATIVE_TS_CONFIG = {\n extends: '../../tsconfig.base.json',\n compilerOptions: {\n jsx: 'react-native',\n types: ['react-native', 'react'],\n },\n include: ['**/*.ts', '**/*.tsx'],\n exclude: ['node_modules', 'babel.config.js', 'metro.config.js'],\n}\n\nfunction nativeQueryClient(contractImport: string) {\n return `import Constants from 'expo-constants'\nimport { QueryClient } from '@tanstack/react-query'\nimport { createRouteClient } from '@emeryld/rrroutes-client'\nimport { registry } from '${contractImport}'\n\nconst { apiUrl } = (Constants.expoConfig?.extra ?? {}) as Record<string, string>\n\nexport const queryClient = new QueryClient()\n\nexport const routeClient = createRouteClient({\n baseUrl: apiUrl ?? 'http://localhost:4000',\n queryClient,\n})\n\nexport const healthGet = routeClient.build(registry.byKey['GET /api/health'])\nexport const healthPost = routeClient.build(registry.byKey['POST /api/health'])\n`\n}\n\nfunction nativeSocket(contractImport: string) {\n return `import React from 'react'\nimport Constants from 'expo-constants'\nimport { io, type Socket } from 'socket.io-client'\nimport {\n buildSocketProvider,\n type SocketClientOptions,\n} from '@emeryld/rrroutes-client'\nimport { socketConfig, socketEvents } from '${contractImport}'\n\nconst extras = (Constants.expoConfig?.extra ?? {}) as Record<string, string>\nconst socketUrl = extras.socketUrl ?? 'http://localhost:4000'\nconst socketPath = extras.socketPath ?? '/socket.io'\n\nconst sysEvents: SocketClientOptions<\n typeof socketEvents,\n typeof socketConfig\n>['sys'] = {\n 'sys:connect': async ({ socket }) => {\n console.log('socket connected', socket.id)\n },\n 'sys:disconnect': async ({ reason }) => console.log('disconnected', reason),\n 'sys:reconnect': async ({ attempt, socket }) =>\n console.log('reconnect', attempt, socket?.id),\n 'sys:connect_error': async ({ error }) =>\n console.warn('socket connect error', error),\n 'sys:ping': () => ({\n note: 'client-heartbeat',\n sentAt: new Date().toISOString(),\n }),\n 'sys:pong': async ({ payload }) => console.log('pong', payload),\n 'sys:room_join': async ({ rooms }) => {\n console.log('join rooms', rooms)\n return true\n },\n 'sys:room_leave': async ({ rooms }) => {\n console.log('leave rooms', rooms)\n return true\n },\n}\n\nconst baseOptions: Omit<\n SocketClientOptions<typeof socketEvents, typeof socketConfig>,\n 'socket'\n> = {\n config: socketConfig,\n sys: sysEvents,\n heartbeat: { intervalMs: 15_000, timeoutMs: 7_500 },\n debug: { connection: true },\n}\n\nconst { SocketProvider, useSocketClient, useSocketConnection } =\n buildSocketProvider({\n events: socketEvents,\n options: baseOptions,\n })\n\nfunction getSocket(): Promise<Socket> {\n return Promise.resolve(\n io(socketUrl, {\n path: socketPath,\n transports: ['websocket'],\n }),\n )\n}\n\nexport const roomMeta = { room: 'health' }\n\nexport function AppSocketProvider(props: React.PropsWithChildren) {\n return (\n <SocketProvider\n getSocket={getSocket}\n destroyLeaveMeta={roomMeta}\n fallback={<></>}\n >\n {props.children}\n </SocketProvider>\n )\n}\n\nexport { useSocketClient, useSocketConnection }\n`\n}\n\nfunction nativeAppTsx() {\n return `import React from 'react'\nimport { SafeAreaView, ScrollView, StatusBar, StyleSheet } from 'react-native'\nimport { QueryClientProvider } from '@tanstack/react-query'\nimport { queryClient } from './src/queryClient'\nimport { AppSocketProvider } from './src/socket'\nimport { HealthScreen } from './src/screens/HealthScreen'\n\nexport default function App() {\n return (\n <QueryClientProvider client={queryClient}>\n <AppSocketProvider>\n <StatusBar barStyle=\"dark-content\" />\n <SafeAreaView style={styles.container}>\n <ScrollView contentInsetAdjustmentBehavior=\"automatic\">\n <HealthScreen />\n </ScrollView>\n </SafeAreaView>\n </AppSocketProvider>\n </QueryClientProvider>\n )\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n padding: 16,\n backgroundColor: '#f4f5fb',\n },\n})\n`\n}\n\nfunction nativeHealthScreen(contractImport: string) {\n return `import React from 'react'\nimport {\n Button,\n StyleSheet,\n Text,\n TextInput,\n View,\n} from 'react-native'\nimport { healthGet, healthPost } from '../queryClient'\nimport { roomMeta, useSocketClient, useSocketConnection } from '../socket'\n\nconst now = () => new Date().toLocaleTimeString()\n\nfunction useLogs() {\n const [logs, setLogs] = React.useState<string[]>([])\n return {\n logs,\n push: (msg: string) =>\n setLogs((prev) => [\\`[\\${now()}] \\${msg}\\`, ...prev].slice(0, 60)),\n clear: () => setLogs([]),\n }\n}\n\nexport function HealthScreen() {\n const [echo, setEcho] = React.useState('hello rrroute')\n const httpGet = healthGet.useEndpoint()\n const httpPost = healthPost.useEndpoint()\n const socket = useSocketClient<typeof import('${contractImport}').socketEvents>()\n const { logs, push, clear } = useLogs()\n\n useSocketConnection({\n event: 'health:connected',\n rooms: ['health'],\n joinMeta: roomMeta,\n leaveMeta: roomMeta,\n onMessage: (payload) => push(\\`connected \\${payload.socketId}\\`),\n })\n\n useSocketConnection({\n event: 'health:pong',\n rooms: ['health'],\n joinMeta: roomMeta,\n leaveMeta: roomMeta,\n onMessage: (payload) =>\n push(\\`pong at \\${payload.at}\\${payload.echo ? \\` (echo: \\${payload.echo})\\` : ''}\\`),\n })\n\n return (\n <View style={styles.card}>\n <Text style={styles.title}>RRRoutes health sandbox</Text>\n <View style={styles.section}>\n <Text style={styles.heading}>HTTP</Text>\n <Button title=\"GET /health\" onPress={() => httpGet.refetch()} />\n <View style={{ height: 12 }} />\n <TextInput\n style={styles.input}\n value={echo}\n onChangeText={setEcho}\n placeholder=\"echo payload\"\n />\n <Button\n title=\"POST /health\"\n onPress={() =>\n httpPost\n .mutateAsync({ echo })\n .then(() => push('POST /health ok'))\n }\n />\n <Text style={styles.label}>GET data</Text>\n <Text style={styles.code}>\n {JSON.stringify(httpGet.data ?? {}, null, 2)}\n </Text>\n <Text style={styles.label}>POST data</Text>\n <Text style={styles.code}>\n {JSON.stringify(httpPost.data ?? {}, null, 2)}\n </Text>\n </View>\n\n <View style={styles.section}>\n <Text style={styles.heading}>Socket</Text>\n <View style={styles.row}>\n <Button title=\"Connect\" onPress={() => socket.connect()} />\n <Button title=\"Disconnect\" onPress={() => socket.disconnect()} />\n </View>\n <View style={{ height: 8 }} />\n <Button\n title=\"Emit ping\"\n onPress={() => socket.emit('health:ping', { note: 'ping from app' })}\n />\n <View style={{ height: 8 }} />\n <View style={styles.row}>\n <Button\n title=\"Join room\"\n onPress={() => socket.joinRooms(['health'], roomMeta)}\n />\n <Button\n title=\"Leave room\"\n onPress={() => socket.leaveRooms(['health'], roomMeta)}\n />\n </View>\n </View>\n\n <View style={styles.section}>\n <Text style={styles.heading}>Socket logs</Text>\n <Button title=\"Clear logs\" onPress={clear} />\n <Text style={styles.code}>\n {logs.length === 0 ? 'No messages yet' : logs.join('\\\\n')}\n </Text>\n </View>\n </View>\n )\n}\n\nconst styles = StyleSheet.create({\n card: {\n backgroundColor: '#fff',\n borderRadius: 12,\n padding: 16,\n gap: 12,\n shadowColor: '#000',\n shadowOpacity: 0.05,\n shadowRadius: 8,\n },\n title: { fontSize: 20, fontWeight: '700' },\n section: { gap: 8 },\n heading: { fontWeight: '600', fontSize: 16 },\n row: { flexDirection: 'row', gap: 8, justifyContent: 'space-between' },\n input: {\n borderWidth: 1,\n borderColor: '#d0d4de',\n borderRadius: 8,\n padding: 8,\n },\n label: { marginTop: 6, fontWeight: '600' },\n code: {\n backgroundColor: '#0f172a',\n color: '#e2e8f0',\n padding: 8,\n borderRadius: 8,\n fontFamily: 'Courier',\n },\n})\n`\n}\n\nconst NATIVE_ENV = `API_URL=http://localhost:4000\nSOCKET_URL=http://localhost:4000\nSOCKET_PATH=/socket.io\n`\n\n// Scaffold orchestrator -------------------------------------------------------\n\nexport async function scaffoldClient(ctx: GeneratorContext) {\n return ctx.clientKind === 'react-native'\n ? scaffoldNativeClient(ctx)\n : scaffoldWebClient(ctx)\n}\n\nasync function scaffoldWebClient(ctx: GeneratorContext) {\n const baseDir = path.join(ctx.rootDir, 'packages', 'client')\n await ensureDir(path.join(baseDir, 'src', 'lib'))\n await ensureDir(path.join(baseDir, 'src', 'pages'))\n\n const pkgJson = {\n name: ctx.packageNames.client,\n version: '0.1.0',\n private: true,\n type: 'module',\n main: 'dist/index.js',\n scripts: {\n dev: 'vite',\n build: 'vite build',\n preview: 'vite preview',\n typecheck: 'tsc -p tsconfig.json --noEmit',\n },\n dependencies: {\n [ctx.packageNames.contract]: 'workspace:*',\n '@emeryld/rrroutes-client': '^2.5.3',\n '@tanstack/react-query': '^5.90.12',\n react: '^18.3.1',\n 'react-dom': '^18.3.1',\n 'socket.io-client': '^4.8.3',\n zod: '^4.2.1',\n },\n devDependencies: {\n '@types/react': '^18.3.27',\n '@types/react-dom': '^18.3.7',\n '@vitejs/plugin-react': '^4.3.4',\n typescript: '^5.9.3',\n vite: '^6.4.1',\n },\n }\n\n const files: Record<string, string> = {\n 'package.json': `${JSON.stringify(pkgJson, null, 2)}\\n`,\n 'tsconfig.json': `${JSON.stringify(baseTsConfig(), null, 2)}\\n`,\n 'vite.config.ts': viteConfig(),\n 'src/main.tsx': MAIN_TSX,\n 'src/App.tsx': appTsx(),\n 'src/lib/queryClient.ts': queryClient(ctx.packageNames.contract),\n 'src/lib/socket.tsx': socketProvider(ctx.packageNames.contract),\n 'src/pages/HealthPage.tsx': healthPage(ctx.packageNames.contract),\n 'src/styles.css': STYLES,\n 'src/env.d.ts': '/// <reference types=\"vite/client\" />\\n',\n 'index.html': indexHtml(),\n '.env': CLIENT_ENV,\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(baseDir, name)\n const writer = name === '.env' ? writeFileForce : writeFileIfMissing\n const result = await writer(fullPath, contents)\n const rel = path.relative(ctx.rootDir, fullPath)\n if (result === 'created' || result === undefined) log.created(rel)\n else log.skipped(rel)\n }\n}\n\nasync function scaffoldNativeClient(ctx: GeneratorContext) {\n const baseDir = path.join(ctx.rootDir, 'packages', 'client')\n await ensureDir(path.join(baseDir, 'src', 'screens'))\n\n const pkgJson = nativePackageJson(\n ctx.packageNames.client,\n ctx.packageNames.contract,\n )\n\n const files: Record<string, string> = {\n 'package.json': `${JSON.stringify(pkgJson, null, 2)}\\n`,\n 'tsconfig.json': `${JSON.stringify(NATIVE_TS_CONFIG, null, 2)}\\n`,\n 'app.json': `${JSON.stringify(nativeAppJson(ctx.appSlug), null, 2)}\\n`,\n 'babel.config.js': NATIVE_BABEL,\n 'App.tsx': nativeAppTsx(),\n 'src/queryClient.ts': nativeQueryClient(ctx.packageNames.contract),\n 'src/socket.tsx': nativeSocket(ctx.packageNames.contract),\n 'src/screens/HealthScreen.tsx': nativeHealthScreen(\n ctx.packageNames.contract,\n ),\n '.env': NATIVE_ENV,\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(baseDir, name)\n const writer = name === '.env' ? writeFileForce : writeFileIfMissing\n const result = await writer(fullPath, contents)\n const rel = path.relative(ctx.rootDir, fullPath)\n if (result === 'created' || result === undefined) log.created(rel)\n else log.skipped(rel)\n }\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nexport async function ensureDir(dir: string): Promise<void> {\n await fs.mkdir(dir, { recursive: true })\n}\n\nexport async function pathExists(target: string): Promise<boolean> {\n try {\n await fs.access(target)\n return true\n } catch {\n return false\n }\n}\n\nexport async function writeFileIfMissing(\n filePath: string,\n contents: string,\n): Promise<'created' | 'skipped'> {\n const dir = path.dirname(filePath)\n await ensureDir(dir)\n if (await pathExists(filePath)) {\n return 'skipped'\n }\n await fs.writeFile(filePath, contents, 'utf8')\n return 'created'\n}\n\nexport async function writeFileForce(\n filePath: string,\n contents: string,\n): Promise<void> {\n const dir = path.dirname(filePath)\n await ensureDir(dir)\n await fs.writeFile(filePath, contents, 'utf8')\n}\n","export const log = {\n info(msg: string) {\n console.log(msg)\n },\n step(msg: string) {\n console.log(`• ${msg}`)\n },\n created(target: string) {\n console.log(` created ${target}`)\n },\n skipped(target: string) {\n console.log(` skipped ${target} (already exists)`)\n },\n warn(msg: string) {\n console.warn(` warning: ${msg}`)\n },\n error(msg: string) {\n console.error(` error: ${msg}`)\n },\n}\n","import path from 'node:path'\nimport { GeneratorContext } from '../types'\nimport { ensureDir, writeFileIfMissing } from '../utils/fs'\nimport { log } from '../utils/log'\n\nconst CONTRACT_TS = `import { defineSocketEvents, finalize, resource } from '@emeryld/rrroutes-contract'\nimport { z } from 'zod'\n\nconst routes = resource('/api')\n .sub(\n resource('health')\n .get({\n outputSchema: z.object({\n status: z.literal('ok'),\n html: z.string().describe('Tiny HTML heartbeat'),\n }),\n description: 'Basic GET health probe for uptime + docs.',\n })\n .post({\n bodySchema: z.object({\n echo: z.string().optional(),\n }),\n outputSchema: z.object({\n status: z.literal('ok'),\n received: z.string().optional(),\n }),\n description: 'POST health probe that echoes a payload.',\n })\n .done(),\n )\n .done()\n\nexport const registry = finalize(routes)\n\nconst sockets = defineSocketEvents(\n {\n joinMetaMessage: z.object({ room: z.string().optional() }),\n leaveMetaMessage: z.object({ room: z.string().optional() }),\n pingPayload: z.object({\n note: z.string().default('ping'),\n sentAt: z.string(),\n }),\n pongPayload: z.object({\n ok: z.literal(true),\n receivedAt: z.string(),\n echo: z.string().optional(),\n }),\n },\n {\n 'health:connected': {\n message: z.object({\n socketId: z.string(),\n at: z.string(),\n message: z.string(),\n }),\n },\n 'health:ping': {\n message: z.object({\n note: z.string().default('ping'),\n }),\n },\n 'health:pong': {\n message: z.object({\n ok: z.literal(true),\n at: z.string(),\n echo: z.string().optional(),\n }),\n },\n },\n)\n\nexport const socketConfig = sockets.config\nexport const socketEvents = sockets.events\nexport type AppRegistry = typeof registry\n`\n\nexport async function scaffoldContract(ctx: GeneratorContext) {\n const baseDir = path.join(ctx.rootDir, 'packages', 'contract')\n await ensureDir(path.join(baseDir, 'src'))\n\n const pkgJson = {\n name: ctx.packageNames.contract,\n version: '0.1.0',\n private: false,\n type: 'module',\n main: 'dist/index.js',\n types: 'dist/index.d.ts',\n files: ['dist'],\n scripts: {\n build: 'tsc -p tsconfig.json',\n typecheck: 'tsc -p tsconfig.json --noEmit',\n watch: 'tsc -p tsconfig.json --watch --preserveWatchOutput',\n },\n dependencies: {\n '@emeryld/rrroutes-contract': '^2.5.2',\n zod: '^4.2.1',\n },\n devDependencies: {\n typescript: '^5.9.3',\n },\n }\n\n const tsconfig = {\n extends: '../../tsconfig.base.json',\n compilerOptions: {\n outDir: 'dist',\n rootDir: 'src',\n declaration: true,\n sourceMap: true,\n },\n include: ['src/**/*'],\n }\n\n const files: Record<string, string> = {\n 'package.json': `${JSON.stringify(pkgJson, null, 2)}\\n`,\n 'tsconfig.json': `${JSON.stringify(tsconfig, null, 2)}\\n`,\n 'src/index.ts': CONTRACT_TS,\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(baseDir, name)\n const result = await writeFileIfMissing(fullPath, contents)\n if (result === 'created') log.created(path.relative(ctx.rootDir, fullPath))\n else log.skipped(path.relative(ctx.rootDir, fullPath))\n }\n}\n","import path from 'node:path'\nimport { GeneratorContext } from '../types'\nimport { ensureDir, writeFileIfMissing } from '../utils/fs'\nimport { log } from '../utils/log'\n\nfunction formatJson(obj: unknown): string {\n return `${JSON.stringify(obj, null, 2)}\\n`\n}\n\nexport async function scaffoldRoot(ctx: GeneratorContext) {\n await ensureDir(ctx.rootDir)\n\n const scripts: Record<string, string> = {\n build: 'npm run build --workspaces',\n typecheck: 'npm run typecheck --workspaces --if-present',\n lint: 'npm run lint --workspaces --if-present',\n }\n if (ctx.targets.server) {\n scripts['dev:server'] = `npm run dev --workspace ${ctx.packageNames.server}`\n }\n if (ctx.targets.client) {\n scripts['dev:client'] = `npm run dev --workspace ${ctx.packageNames.client}`\n }\n if (ctx.targets.contract) {\n scripts['dev:contract'] = `npm run watch --workspace ${ctx.packageNames.contract}`\n }\n\n const highlights = ['- Shared contract package (RRRoutes registry + sockets)']\n if (ctx.targets.server) {\n highlights.unshift(\n '- Backend (Express + Socket.IO + RRRoutes server bindings)',\n )\n }\n if (ctx.targets.client) {\n highlights.splice(\n ctx.targets.server ? 1 : 0,\n 0,\n '- Frontend client (React or React Native) with React Query + sockets',\n )\n }\n\n const quickstart: string[] = ['1) Install deps: `npm install` (workspaces)']\n if (ctx.targets.server) {\n quickstart.push(\n `${quickstart.length + 1}) Backend env: edit \\`packages/server/.env\\``,\n )\n }\n if (ctx.targets.contract) {\n quickstart.push(\n `${quickstart.length + 1}) Optional: keep shared contract rebuilding via \\`npm run dev:contract\\``,\n )\n }\n if (ctx.targets.client) {\n quickstart.push(\n `${quickstart.length + 1}) Client env: edit \\`packages/client/.env\\``,\n )\n }\n if (ctx.targets.server || ctx.targets.client) {\n quickstart.push(\n `${quickstart.length + 1}) Run dev servers:${\n ctx.targets.server ? `\\n - API: \\`npm run dev:server\\`` : ''\n }${ctx.targets.client ? `\\n - Client: \\`npm run dev:client\\`` : ''}`,\n )\n }\n\n const pkgJson = {\n name: `${ctx.appSlug}-stack`,\n private: true,\n workspaces: ['packages/*'],\n scripts,\n devDependencies: {\n typescript: '^5.9.3',\n },\n }\n\n const files: Record<string, string> = {\n 'package.json': formatJson(pkgJson),\n 'pnpm-workspace.yaml': \"packages:\\n - 'packages/*'\\n\",\n 'tsconfig.base.json': formatJson({\n $schema: 'https://json.schemastore.org/tsconfig',\n compilerOptions: {\n target: 'ES2020',\n module: 'ESNext',\n moduleResolution: 'Bundler',\n jsx: 'react-jsx',\n strict: true,\n skipLibCheck: true,\n resolveJsonModule: true,\n forceConsistentCasingInFileNames: true,\n sourceMap: true,\n baseUrl: '.',\n },\n }),\n '.gitignore': [\n 'node_modules',\n 'dist',\n '.turbo',\n '.expo',\n '.DS_Store',\n '.env',\n '.env.local',\n 'npm-debug.log*',\n 'yarn-error.log',\n ].join('\\n'),\n README: [\n `# ${ctx.projectName} (RRRoutes starter)`,\n '',\n 'Generated via `create-rrroutes`. Includes:',\n ...highlights,\n '',\n '## Quickstart',\n ...quickstart,\n '',\n ctx.targets.server\n ? 'Dockerfile for the API lives in `packages/server/Dockerfile`.'\n : '',\n '',\n ]\n .filter(Boolean)\n .join('\\n'),\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(ctx.rootDir, name)\n const result = await writeFileIfMissing(fullPath, contents)\n if (result === 'created') log.created(path.relative(ctx.rootDir, fullPath))\n else log.skipped(path.relative(ctx.rootDir, fullPath))\n }\n}\n","import path from 'node:path'\nimport { GeneratorContext } from '../types'\nimport { ensureDir, writeFileIfMissing, writeFileForce } from '../utils/fs'\nimport { log } from '../utils/log'\n\nfunction serverIndexTs() {\n return `import 'dotenv/config'\nimport http from 'node:http'\nimport { app } from './http'\nimport { createSockets } from './socket'\nimport { prisma } from './prisma'\n\nasync function main() {\n const PORT = Number.parseInt(process.env.PORT ?? '4000', 10)\n const CORS_ORIGIN = process.env.CORS_ORIGIN ?? 'http://localhost:5173'\n const SOCKET_PATH = process.env.SOCKET_PATH ?? '/socket.io'\n\n await prisma.$connect()\n\n const httpServer = http.createServer(app)\n const sockets = createSockets(httpServer, {\n corsOrigin: CORS_ORIGIN,\n socketPath: SOCKET_PATH,\n })\n\n httpServer.listen(PORT, () => {\n console.log(\n \\`API listening on http://localhost:\\${PORT} (CORS origin: \\${CORS_ORIGIN})\\`,\n )\n })\n\n const shutdown = async () => {\n await sockets.destroy()\n await prisma.$disconnect()\n }\n process.on('SIGTERM', shutdown)\n process.on('SIGINT', shutdown)\n}\n\nmain().catch(async (err) => {\n console.error(err)\n await prisma.$disconnect().catch(() => {})\n process.exit(1)\n})\n`\n}\n\nfunction httpTs(contractImport: string) {\n return `import express from 'express'\nimport cors from 'cors'\nimport { createRRRoute } from '@emeryld/rrroutes-server'\nimport { registry } from '${contractImport}'\nimport { controllers, type RequestCtx } from './controllers'\nimport { prisma } from './prisma'\n\nconst CORS_ORIGIN = process.env.CORS_ORIGIN ?? 'http://localhost:5173'\n\nexport const app = express()\napp.use(cors({ origin: CORS_ORIGIN, credentials: true }))\napp.use(express.json())\n\napp.get('/', (_req, res) => {\n res.send('<h1>RRRoutes starter API</h1>')\n})\n\nconst routes = createRRRoute<RequestCtx>(app, {\n buildCtx: async () => ({\n requestId: Math.random().toString(36).slice(2),\n prisma,\n routesLogger: console,\n }),\n debug:\n process.env.NODE_ENV === 'development'\n ? { request: true, handler: true }\n : undefined,\n})\n\nroutes.registerControllers(registry, controllers)\nroutes.warnMissingControllers(registry, console)\nexport { routes }\n`\n}\n\nfunction controllersTs(contractImport: string) {\n return `import { defineControllers } from '@emeryld/rrroutes-server'\nimport type { PrismaClient } from '@prisma/client'\nimport { registry } from '${contractImport}'\n\nexport type RequestCtx = {\n prisma: PrismaClient\n requestId: string\n}\n\nexport const controllers = defineControllers<typeof registry, RequestCtx>()({\n 'GET /api/health': {\n handler: async ({ ctx }) => {\n await ctx.prisma.healthCheck.create({\n data: { note: 'GET health' },\n })\n return {\n status: 'ok',\n html: '<!doctype html><html><body><h1>healthy</h1></body></html>',\n }\n },\n },\n 'POST /api/health': {\n handler: async ({ ctx, body }) => {\n await ctx.prisma.healthCheck.create({\n data: { note: body?.echo ?? 'POST health' },\n })\n return {\n status: 'ok',\n received: body?.echo ?? 'pong',\n }\n },\n },\n})\n`\n}\n\nfunction socketTs(contractImport: string) {\n return `import { Server as SocketIOServer } from 'socket.io'\nimport type { Server } from 'node:http'\nimport { createSocketConnections } from '@emeryld/rrroutes-server'\nimport { socketConfig, socketEvents } from '${contractImport}'\n\nexport function createSockets(\n httpServer: Server,\n opts: { corsOrigin: string; socketPath: string },\n) {\n const io = new SocketIOServer(httpServer, {\n cors: { origin: opts.corsOrigin, credentials: true },\n path: opts.socketPath,\n })\n\n const sockets = createSocketConnections(io, socketEvents, {\n config: socketConfig,\n heartbeat: { enabled: true },\n sys: {\n 'sys:connect': async ({ socket, helper, complete }) => {\n helper.emit('health:connected', {\n socketId: socket.id,\n at: new Date().toISOString(),\n message: 'connected',\n })\n complete()\n },\n 'sys:disconnect': async ({ cleanup }) => cleanup(),\n 'sys:ping': async ({ ping }) => ({\n ok: true,\n receivedAt: new Date().toISOString(),\n echo: ping.note,\n }),\n 'sys:room_join': async ({ rooms, join }) => {\n await Promise.all(rooms.map((room) => join(room)))\n },\n 'sys:room_leave': async ({ rooms, leave }) => {\n await Promise.all(rooms.map((room) => leave(room)))\n },\n },\n })\n\n sockets.on('health:ping', async (payload, ctx) => {\n sockets.emit(\n 'health:pong',\n { ok: true, at: new Date().toISOString(), echo: payload.note },\n ctx.socketId,\n )\n })\n\n return sockets\n}\n`\n}\n\nfunction prismaTs() {\n return `import { PrismaClient } from '@prisma/client'\n\nexport const prisma = new PrismaClient({\n log: ['warn', 'error'],\n})\n`\n}\n\nfunction prismaSchema() {\n return `generator client {\n provider = \"prisma-client-js\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel HealthCheck {\n id String @id @default(uuid())\n note String?\n createdAt DateTime @default(now())\n}\n`\n}\n\nfunction serverEnv() {\n return [\n 'PORT=4000',\n 'NODE_ENV=development',\n 'CORS_ORIGIN=http://localhost:5173',\n 'SOCKET_PATH=/socket.io',\n 'DATABASE_URL=postgresql://postgres:postgres@localhost:5432/rrroutes',\n ].join('\\n')\n}\n\nfunction dockerfile(serverName: string, contractName: string) {\n return `FROM node:20-alpine\nWORKDIR /app\n\n# Install workspace deps (hoists to /app/node_modules)\nCOPY package.json pnpm-workspace.yaml tsconfig.base.json ./\nCOPY packages/contract/package.json packages/contract/package.json\nCOPY packages/server/package.json packages/server/package.json\nCOPY packages/server/prisma/schema.prisma packages/server/prisma/schema.prisma\nRUN npm install\n\n# Copy source and build server + contract\nCOPY packages ./packages\n\n# Generate Prisma client\nRUN npx prisma generate --schema packages/server/prisma/schema.prisma\n\nRUN npm run build --workspace ${contractName} && npm run build --workspace ${serverName}\n\nEXPOSE 4000\nCMD [\"node\", \"packages/server/dist/index.js\"]\n`\n}\n\nexport async function scaffoldServer(ctx: GeneratorContext) {\n const baseDir = path.join(ctx.rootDir, 'packages', 'server')\n await ensureDir(path.join(baseDir, 'src'))\n await ensureDir(path.join(baseDir, 'prisma'))\n\n const pkgJson = {\n name: ctx.packageNames.server,\n version: '0.1.0',\n private: false,\n type: 'module',\n main: 'dist/index.js',\n types: 'dist/index.d.ts',\n files: ['dist'],\n scripts: {\n dev: 'nodemon --watch src --ext ts --exec \"node --loader ts-node/esm --experimental-specifier-resolution=node src/index.ts\"',\n build: 'tsc -p tsconfig.json',\n typecheck: 'tsc -p tsconfig.json --noEmit',\n start: 'node dist/index.js',\n 'prisma:generate': 'prisma generate --schema prisma/schema.prisma',\n 'prisma:migrate': 'prisma migrate dev --schema prisma/schema.prisma --name init',\n 'db:start': 'docker run --name rrroutes-db -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=rrroutes -p 5432:5432 -d postgres:15 || docker start rrroutes-db',\n 'db:stop': 'docker stop rrroutes-db || true',\n },\n dependencies: {\n [ctx.packageNames.contract]: 'workspace:*',\n '@emeryld/rrroutes-server': '^2.4.1',\n '@prisma/client': '^5.22.0',\n 'socket.io': '^4.8.1',\n express: '^5.1.0',\n cors: '^2.8.5',\n dotenv: '^16.4.5',\n zod: '^4.2.1',\n },\n devDependencies: {\n '@types/express': '^5.0.6',\n '@types/node': '^24.10.2',\n prisma: '^5.22.0',\n typescript: '^5.9.3',\n 'ts-node': '^10.9.2',\n nodemon: '^3.1.9',\n },\n }\n\n const tsconfig = {\n extends: '../../tsconfig.base.json',\n compilerOptions: {\n outDir: 'dist',\n rootDir: 'src',\n declaration: true,\n sourceMap: true,\n },\n include: ['src/**/*'],\n }\n\n const files: Record<string, string> = {\n 'package.json': `${JSON.stringify(pkgJson, null, 2)}\\n`,\n 'tsconfig.json': `${JSON.stringify(tsconfig, null, 2)}\\n`,\n 'src/index.ts': serverIndexTs(),\n 'src/http.ts': httpTs(ctx.packageNames.contract),\n 'src/controllers.ts': controllersTs(ctx.packageNames.contract),\n 'src/socket.ts': socketTs(ctx.packageNames.contract),\n 'src/prisma.ts': prismaTs(),\n 'prisma/schema.prisma': prismaSchema(),\n '.env': `${serverEnv()}\\n`,\n Dockerfile: dockerfile(ctx.packageNames.server, ctx.packageNames.contract),\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(baseDir, name)\n const writer =\n name === '.env'\n ? writeFileForce // allow updating env defaults\n : writeFileIfMissing\n const result = await writer(fullPath, contents)\n const rel = path.relative(ctx.rootDir, fullPath)\n if (result === 'created' || result === undefined) log.created(rel)\n else log.skipped(rel)\n }\n}\n","import { createInterface } from 'node:readline/promises'\nimport { stdin as input, stdout as output } from 'node:process'\n\ntype Option<T> = {\n label: string\n value: T\n}\n\nexport function createPrompt() {\n const rl = createInterface({ input, output })\n\n return {\n async text(message: string, fallback?: string) {\n const suffix = fallback ? ` (${fallback})` : ''\n const answer = (await rl.question(`${message}${suffix}: `)).trim()\n return answer.length > 0 ? answer : fallback\n },\n async confirm(message: string, fallback = true) {\n const suffix = fallback ? ' (Y/n)' : ' (y/N)'\n const answer = (await rl.question(`${message}${suffix}: `)).trim()\n if (!answer) return fallback\n return ['y', 'yes'].includes(answer.toLowerCase())\n },\n async select<T>(\n message: string,\n options: Option<T>[],\n fallbackIndex = 0,\n ) {\n console.log(message)\n options.forEach((opt, idx) =>\n console.log(` [${idx + 1}] ${opt.label}`),\n )\n const answer = (await rl.question(`Choose 1-${options.length}: `)).trim()\n const idx = Number.parseInt(answer, 10)\n const normalized =\n Number.isNaN(idx) || idx < 1 || idx > options.length\n ? fallbackIndex\n : idx - 1\n return options[normalized]!.value\n },\n close() {\n rl.close()\n },\n }\n}\n","import { spawn } from 'node:child_process'\n\nexport async function runCommand(\n cmd: string,\n args: string[],\n cwd: string,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(cmd, args, {\n cwd,\n stdio: 'inherit',\n shell: process.platform === 'win32',\n })\n child.on('exit', (code) => {\n if (code === 0) resolve()\n else reject(new Error(`${cmd} ${args.join(' ')} exited with ${code}`))\n })\n child.on('error', (err) => reject(err))\n })\n}\n","export function slugify(input: string, fallback = 'rrroutes-app'): string {\n const slug = input\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n return slug.length > 0 ? slug : fallback\n}\n"],"mappings":"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","names":["import_node_path","import_node_path","fs","path","path","import_node_path","path","import_node_path","path","import_node_path","path","import_promises","input","output","idx","input","path"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/generators/client.ts","../src/utils/fs.ts","../src/utils/log.ts","../src/generators/contract.ts","../src/generators/root.ts","../src/generators/server.ts","../src/utils/prompt.ts","../src/utils/run.ts","../src/utils/strings.ts"],"sourcesContent":["import path from 'node:path'\nimport { scaffoldClient } from './generators/client'\nimport { scaffoldContract } from './generators/contract'\nimport { scaffoldRoot } from './generators/root'\nimport { scaffoldServer } from './generators/server'\nimport { GeneratorContext, Targets, type ClientKind } from './types'\nimport { log } from './utils/log'\nimport { createPrompt } from './utils/prompt'\nimport { runCommand } from './utils/run'\nimport { slugify } from './utils/strings'\n\nfunction resolveTargets(choice: string): Targets {\n if (choice === 'all')\n return { client: true, server: true, contract: true } as const\n if (choice === 'client')\n return { client: true, server: false, contract: true } as const\n if (choice === 'server')\n return { client: false, server: true, contract: true } as const\n return { client: false, server: false, contract: true } as const\n}\n\nasync function gatherContext(): Promise<GeneratorContext> {\n const prompt = createPrompt()\n try {\n const projectName =\n (await prompt.text('Project name', 'rrroutes-starter')) ??\n 'rrroutes-starter'\n const appSlug = slugify(projectName)\n const targetDirInput =\n (await prompt.text('Target directory', `./${appSlug}`)) ?? `./${appSlug}`\n const rootDir = path.resolve(process.cwd(), targetDirInput)\n const scopeInput =\n (await prompt.text('NPM scope (without @)', appSlug)) ?? appSlug\n const packageScope = slugify(scopeInput)\n\n const selection = await prompt.select('What should we create?', [\n {\n label: 'Everything (contract + server + client)',\n value: 'all',\n },\n { label: 'Contract only', value: 'contract' },\n { label: 'Backend only', value: 'server' },\n { label: 'Frontend only', value: 'client' },\n ])\n const targets = resolveTargets(selection)\n\n let clientKind: ClientKind = 'react'\n if (targets.client) {\n clientKind = await prompt.select<ClientKind>(\n 'Frontend runtime',\n [\n { label: 'React (web, Vite)', value: 'react' },\n { label: 'React Native (Expo)', value: 'react-native' },\n ],\n 0,\n )\n }\n\n const packageNames = {\n contract: `@${packageScope}/contract`,\n server: `@${packageScope}/server`,\n client: `@${packageScope}/client`,\n }\n\n return {\n projectName,\n appSlug,\n packageScope,\n rootDir,\n clientKind,\n targets,\n packageNames,\n }\n } finally {\n prompt.close()\n }\n}\n\nasync function runPostScaffold(ctx: GeneratorContext) {\n if (process.env.CREATERRROUTES_SKIP_POSTINSTALL === '1') {\n log.warn('Skipping install/build steps (CREATERRROUTES_SKIP_POSTINSTALL=1).')\n return\n }\n\n const steps: { label: string; args: string[]; enabled: boolean }[] = [\n {\n label: 'Installing workspace dependencies (pnpm install)',\n args: ['install'],\n enabled: true,\n },\n {\n label: `Building shared contract (${ctx.packageNames.contract})`,\n args: ['--filter', ctx.packageNames.contract, 'build'],\n enabled: ctx.targets.contract,\n },\n {\n label: `Generating Prisma client for ${ctx.packageNames.server}`,\n args: ['--filter', ctx.packageNames.server, 'prisma:generate'],\n enabled: ctx.targets.server,\n },\n ]\n\n for (const step of steps) {\n if (!step.enabled) continue\n try {\n log.step(step.label)\n await runCommand('pnpm', step.args, ctx.rootDir)\n } catch (err) {\n log.warn(\n `Step failed (${step.label}). Run manually: pnpm ${step.args.join(' ')}\\n${String(err)}`,\n )\n break\n }\n }\n}\n\nasync function main() {\n const ctx = await gatherContext()\n log.info(`Scaffolding RRRoutes starter in ${ctx.rootDir}`)\n\n await scaffoldRoot(ctx)\n if (ctx.targets.contract) await scaffoldContract(ctx)\n if (ctx.targets.server) await scaffoldServer(ctx)\n if (ctx.targets.client) await scaffoldClient(ctx)\n\n await runPostScaffold(ctx)\n\n log.info('Done. Install dependencies and start hacking!')\n}\n\nmain().catch((err) => {\n log.error(err instanceof Error ? err.message : String(err))\n process.exit(1)\n})\n","import path from 'node:path'\nimport { GeneratorContext } from '../types'\nimport { ensureDir, writeFileForce, writeFileIfMissing } from '../utils/fs'\nimport { log } from '../utils/log'\n\nfunction baseTsConfig() {\n return {\n extends: '../../tsconfig.base.json',\n compilerOptions: {\n outDir: 'dist',\n rootDir: 'src',\n types: ['vite/client'],\n },\n include: ['src/**/*', 'vite.config.ts'],\n }\n}\n\nfunction viteConfig() {\n return `import { defineConfig } from 'vite'\nimport react from '@vitejs/plugin-react'\n\nexport default defineConfig({\n plugins: [react()],\n})\n`\n}\n\nfunction queryClient(contractImport: string) {\n return `import { QueryClient } from '@tanstack/react-query'\nimport { createRouteClient } from '@emeryld/rrroutes-client'\nimport { registry } from '${contractImport}'\n\nconst baseUrl = import.meta.env.VITE_API_URL ?? 'http://localhost:4000'\n\nexport const queryClient = new QueryClient()\n\nexport const routeClient = createRouteClient({\n baseUrl,\n queryClient,\n environment: import.meta.env.MODE === 'production' ? 'production' : 'development',\n})\n\nexport const healthGet = routeClient.build(registry.byKey['GET /api/health'])\nexport const healthPost = routeClient.build(registry.byKey['POST /api/health'])\n`\n}\n\nfunction socketProvider(contractImport: string) {\n return `import React from 'react'\nimport { io, type Socket } from 'socket.io-client'\nimport {\n buildSocketProvider,\n type SocketClientOptions,\n} from '@emeryld/rrroutes-client'\nimport { socketConfig, socketEvents } from '${contractImport}'\n\nconst socketUrl = import.meta.env.VITE_SOCKET_URL ?? 'http://localhost:4000'\nconst socketPath = import.meta.env.VITE_SOCKET_PATH ?? '/socket.io'\n\nconst sysEvents: SocketClientOptions<\n typeof socketEvents,\n typeof socketConfig\n>['sys'] = {\n 'sys:connect': async ({ socket }) => {\n console.info('socket connected', socket.id)\n },\n 'sys:disconnect': async ({ reason }) => {\n console.info('socket disconnected', reason)\n },\n 'sys:reconnect': async ({ attempt, socket }) => {\n console.info('socket reconnect', attempt, socket?.id)\n },\n 'sys:connect_error': async ({ error }) => {\n console.warn('socket connect error', error)\n },\n 'sys:ping': () => ({\n note: 'client-heartbeat',\n sentAt: new Date().toISOString(),\n }),\n 'sys:pong': async ({ payload }) => {\n console.info('socket pong', payload)\n },\n 'sys:room_join': async ({ rooms }) => {\n console.info('joining rooms', rooms)\n return true\n },\n 'sys:room_leave': async ({ rooms }) => {\n console.info('leaving rooms', rooms)\n return true\n },\n}\n\nconst baseOptions: Omit<\n SocketClientOptions<typeof socketEvents, typeof socketConfig>,\n 'socket'\n> = {\n config: socketConfig,\n sys: sysEvents,\n environment: import.meta.env.MODE === 'production' ? 'production' : 'development',\n heartbeat: { intervalMs: 15_000, timeoutMs: 7_500 },\n debug: {\n connection: true,\n heartbeat: true,\n },\n}\n\nconst { SocketProvider, useSocketClient, useSocketConnection } =\n buildSocketProvider({\n events: socketEvents,\n options: baseOptions,\n })\n\nfunction getSocket(): Promise<Socket> {\n return Promise.resolve(\n io(socketUrl, {\n path: socketPath,\n transports: ['websocket'],\n }),\n )\n}\n\nexport const roomMeta = { room: 'health' }\n\nexport function AppSocketProvider(props: React.PropsWithChildren) {\n return (\n <SocketProvider\n getSocket={getSocket}\n destroyLeaveMeta={roomMeta}\n fallback={<p>Connecting socket…</p>}\n >\n {props.children}\n </SocketProvider>\n )\n}\n\nexport { useSocketClient, useSocketConnection }\n`\n}\n\nconst MAIN_TSX = `import React from 'react'\nimport ReactDOM from 'react-dom/client'\nimport App from './App'\nimport './styles.css'\n\nReactDOM.createRoot(document.getElementById('root')!).render(\n <React.StrictMode>\n <App />\n </React.StrictMode>,\n)\n`\n\nfunction appTsx() {\n return `import React from 'react'\nimport { QueryClientProvider } from '@tanstack/react-query'\nimport { queryClient } from './lib/queryClient'\nimport { AppSocketProvider } from './lib/socket'\nimport { HealthPage } from './pages/HealthPage'\n\nexport default function App() {\n return (\n <QueryClientProvider client={queryClient}>\n <AppSocketProvider>\n <HealthPage />\n </AppSocketProvider>\n </QueryClientProvider>\n )\n}\n`\n}\n\nconst STYLES = `:root {\n font-family: 'Inter', system-ui, -apple-system, sans-serif;\n color: #0b1021;\n background: #f7f8fb;\n}\n\nbody {\n margin: 0;\n background: radial-gradient(circle at 20% 20%, #eef2ff, #f7f8fb 45%);\n min-height: 100vh;\n}\n\n.health {\n max-width: 960px;\n margin: 0 auto;\n padding: 32px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));\n gap: 16px;\n}\n\n.card {\n background: #fff;\n border: 1px solid #e5e7eb;\n border-radius: 12px;\n padding: 16px;\n box-shadow: 0 8px 24px rgba(12, 18, 32, 0.05);\n}\n\nbutton {\n background: #0f172a;\n color: #fff;\n border: none;\n border-radius: 8px;\n padding: 10px 12px;\n cursor: pointer;\n font-weight: 600;\n}\n\nbutton.secondary {\n background: #e2e8f0;\n color: #0f172a;\n}\n\nbutton + button {\n margin-left: 8px;\n}\n\ntextarea {\n width: 100%;\n min-height: 80px;\n}\n\n.logs {\n font-family: ui-monospace, SFMono-Regular, Consolas, 'Liberation Mono', Menlo,\n monospace;\n background: #0f172a;\n color: #e2e8f0;\n padding: 12px;\n border-radius: 8px;\n min-height: 160px;\n white-space: pre-line;\n}\n`\n\nfunction healthPage(contractImport: string) {\n return `import React from 'react'\nimport { healthGet, healthPost } from '../lib/queryClient'\nimport { roomMeta, useSocketClient, useSocketConnection } from '../lib/socket'\n\nconst now = () => new Date().toLocaleTimeString()\n\nfunction useLogs() {\n const [logs, setLogs] = React.useState<string[]>([])\n return {\n logs,\n push: (msg: string) =>\n setLogs((prev) => [\\`[\\${now()}] \\${msg}\\`, ...prev].slice(0, 60)),\n clear: () => setLogs([]),\n }\n}\n\nexport function HealthPage() {\n const [echo, setEcho] = React.useState('hello rrroute')\n const httpGet = healthGet.useEndpoint()\n const httpPost = healthPost.useEndpoint()\n const socket = useSocketClient()\n const { logs, push, clear } = useLogs()\n\n useSocketConnection({\n event: 'health:connected',\n rooms: ['health'],\n joinMeta: roomMeta,\n leaveMeta: roomMeta,\n onMessage: (payload) => {\n push(\\`socket connected (\\${payload.socketId})\\`)\n },\n })\n\n useSocketConnection({\n event: 'health:pong',\n rooms: ['health'],\n joinMeta: roomMeta,\n leaveMeta: roomMeta,\n onMessage: (payload) => {\n push(\n \\`pong at \\${payload.at}\\${payload.echo ? \\` (echo: \\${payload.echo})\\` : ''}\\`,\n )\n },\n })\n\n return (\n <div className=\"health\">\n <h1>RRRoutes health sandbox</h1>\n <div className=\"grid\">\n <div className=\"card\">\n <h2>HTTP endpoints</h2>\n <p>GET and POST against the shared contract.</p>\n <div>\n <button onClick={() => httpGet.refetch()}>GET /health</button>\n {httpGet.error ? (\n <p style={{ color: 'crimson', marginTop: 6 }}>\n GET error: {String(httpGet.error)}\n </p>\n ) : null}\n </div>\n <div style={{ marginTop: 12 }}>\n <input\n value={echo}\n onChange={(e) => setEcho(e.target.value)}\n placeholder=\"echo payload\"\n style={{ width: '100%', padding: '8px' }}\n />\n <div style={{ marginTop: 8 }}>\n <button\n onClick={() =>\n httpPost.mutateAsync({ echo }).then(() => {\n push('POST /health ok')\n })\n }\n >\n POST /health\n </button>\n </div>\n {httpPost.error ? (\n <p style={{ color: 'crimson', marginTop: 6 }}>\n POST error: {String(httpPost.error)}\n </p>\n ) : null}\n </div>\n <div style={{ marginTop: 12 }}>\n <strong>GET data</strong>\n <pre>{JSON.stringify(httpGet.data, null, 2) ?? 'none'}</pre>\n <strong>POST data</strong>\n <pre>{JSON.stringify(httpPost.data, null, 2) ?? 'none'}</pre>\n </div>\n </div>\n\n <div className=\"card\">\n <h2>Socket</h2>\n <p>Connect, ping, and watch lifecycle events.</p>\n <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>\n <button onClick={() => socket.connect()}>Connect</button>\n <button className=\"secondary\" onClick={() => socket.disconnect()}>\n Disconnect\n </button>\n <button\n onClick={() =>\n socket.emit('health:ping', { note: 'ping from client' })\n }\n >\n Emit ping\n </button>\n <button\n className=\"secondary\"\n onClick={() => socket.joinRooms(['health'], roomMeta)}\n >\n Join room\n </button>\n <button\n className=\"secondary\"\n onClick={() => socket.leaveRooms(['health'], roomMeta)}\n >\n Leave room\n </button>\n </div>\n {httpGet.error || httpPost.error ? (\n <p style={{ color: 'crimson', marginTop: 8 }}>\n Check server logs; errors will also show above.\n </p>\n ) : null}\n </div>\n\n <div className=\"card\">\n <h2>Socket logs</h2>\n <button className=\"secondary\" onClick={clear}>\n Clear\n </button>\n <div className=\"logs\">\n {logs.length === 0 ? 'No messages yet' : logs.join('\\\\n')}\n </div>\n </div>\n </div>\n </div>\n )\n}\n`\n}\n\nfunction indexHtml() {\n return `<!doctype html>\n<html lang=\"en\">\n <head>\n <meta charset=\"UTF-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <title>RRRoutes starter</title>\n </head>\n <body>\n <div id=\"root\"></div>\n <script type=\"module\" src=\"/src/main.tsx\"></script>\n </body>\n</html>\n`\n}\n\nconst CLIENT_ENV = `VITE_API_URL=http://localhost:4000\nVITE_SOCKET_URL=http://localhost:4000\nVITE_SOCKET_PATH=/socket.io\n`\n\n// React Native / Expo template ------------------------------------------------\n\nfunction nativePackageJson(clientName: string, contractName: string) {\n return {\n name: clientName,\n version: '0.1.0',\n private: true,\n main: 'expo/AppEntry',\n scripts: {\n start: 'expo start',\n android: 'expo start --android',\n ios: 'expo start --ios',\n web: 'expo start --web',\n test: 'echo \"add tests\"',\n },\n dependencies: {\n [contractName]: 'workspace:*',\n '@emeryld/rrroutes-client': '^2.5.3',\n '@tanstack/react-query': '^5.90.12',\n expo: '~52.0.8',\n 'expo-constants': '~16.0.2',\n 'expo-status-bar': '~2.0.1',\n react: '18.3.1',\n 'react-native': '0.76.6',\n 'react-native-safe-area-context': '4.12.0',\n 'react-native-screens': '4.4.0',\n 'socket.io-client': '^4.8.3',\n zod: '^4.2.1',\n },\n devDependencies: {\n '@babel/core': '^7.25.2',\n '@types/react': '~18.2.79',\n '@types/react-native': '~0.73.0',\n typescript: '^5.9.3',\n },\n }\n}\n\nfunction nativeAppJson(appSlug: string) {\n return {\n expo: {\n name: `${appSlug}-client`,\n slug: `${appSlug}-client`,\n version: '0.1.0',\n orientation: 'portrait',\n scheme: appSlug,\n platforms: ['ios', 'android', 'web'],\n userInterfaceStyle: 'light',\n extra: {\n apiUrl: 'http://localhost:4000',\n socketUrl: 'http://localhost:4000',\n socketPath: '/socket.io',\n },\n experiments: {\n typedRoutes: false,\n },\n },\n }\n}\n\nconst NATIVE_BABEL = `module.exports = function (api) {\n api.cache(true)\n return {\n presets: ['babel-preset-expo'],\n }\n}\n`\n\nconst NATIVE_TS_CONFIG = {\n extends: '../../tsconfig.base.json',\n compilerOptions: {\n jsx: 'react-native',\n types: ['react-native', 'react'],\n },\n include: ['**/*.ts', '**/*.tsx'],\n exclude: ['node_modules', 'babel.config.js', 'metro.config.js'],\n}\n\nfunction nativeQueryClient(contractImport: string) {\n return `import Constants from 'expo-constants'\nimport { QueryClient } from '@tanstack/react-query'\nimport { createRouteClient } from '@emeryld/rrroutes-client'\nimport { registry } from '${contractImport}'\n\nconst { apiUrl } = (Constants.expoConfig?.extra ?? {}) as Record<string, string>\n\nexport const queryClient = new QueryClient()\n\nexport const routeClient = createRouteClient({\n baseUrl: apiUrl ?? 'http://localhost:4000',\n queryClient,\n})\n\nexport const healthGet = routeClient.build(registry.byKey['GET /api/health'])\nexport const healthPost = routeClient.build(registry.byKey['POST /api/health'])\n`\n}\n\nfunction nativeSocket(contractImport: string) {\n return `import React from 'react'\nimport Constants from 'expo-constants'\nimport { io, type Socket } from 'socket.io-client'\nimport {\n buildSocketProvider,\n type SocketClientOptions,\n} from '@emeryld/rrroutes-client'\nimport { socketConfig, socketEvents } from '${contractImport}'\n\nconst extras = (Constants.expoConfig?.extra ?? {}) as Record<string, string>\nconst socketUrl = extras.socketUrl ?? 'http://localhost:4000'\nconst socketPath = extras.socketPath ?? '/socket.io'\n\nconst sysEvents: SocketClientOptions<\n typeof socketEvents,\n typeof socketConfig\n>['sys'] = {\n 'sys:connect': async ({ socket }) => {\n console.log('socket connected', socket.id)\n },\n 'sys:disconnect': async ({ reason }) => console.log('disconnected', reason),\n 'sys:reconnect': async ({ attempt, socket }) =>\n console.log('reconnect', attempt, socket?.id),\n 'sys:connect_error': async ({ error }) =>\n console.warn('socket connect error', error),\n 'sys:ping': () => ({\n note: 'client-heartbeat',\n sentAt: new Date().toISOString(),\n }),\n 'sys:pong': async ({ payload }) => console.log('pong', payload),\n 'sys:room_join': async ({ rooms }) => {\n console.log('join rooms', rooms)\n return true\n },\n 'sys:room_leave': async ({ rooms }) => {\n console.log('leave rooms', rooms)\n return true\n },\n}\n\nconst baseOptions: Omit<\n SocketClientOptions<typeof socketEvents, typeof socketConfig>,\n 'socket'\n> = {\n config: socketConfig,\n sys: sysEvents,\n heartbeat: { intervalMs: 15_000, timeoutMs: 7_500 },\n debug: { connection: true },\n}\n\nconst { SocketProvider, useSocketClient, useSocketConnection } =\n buildSocketProvider({\n events: socketEvents,\n options: baseOptions,\n })\n\nfunction getSocket(): Promise<Socket> {\n return Promise.resolve(\n io(socketUrl, {\n path: socketPath,\n transports: ['websocket'],\n }),\n )\n}\n\nexport const roomMeta = { room: 'health' }\n\nexport function AppSocketProvider(props: React.PropsWithChildren) {\n return (\n <SocketProvider\n getSocket={getSocket}\n destroyLeaveMeta={roomMeta}\n fallback={<></>}\n >\n {props.children}\n </SocketProvider>\n )\n}\n\nexport { useSocketClient, useSocketConnection }\n`\n}\n\nfunction nativeAppTsx() {\n return `import React from 'react'\nimport { SafeAreaView, ScrollView, StatusBar, StyleSheet } from 'react-native'\nimport { QueryClientProvider } from '@tanstack/react-query'\nimport { queryClient } from './src/queryClient'\nimport { AppSocketProvider } from './src/socket'\nimport { HealthScreen } from './src/screens/HealthScreen'\n\nexport default function App() {\n return (\n <QueryClientProvider client={queryClient}>\n <AppSocketProvider>\n <StatusBar barStyle=\"dark-content\" />\n <SafeAreaView style={styles.container}>\n <ScrollView contentInsetAdjustmentBehavior=\"automatic\">\n <HealthScreen />\n </ScrollView>\n </SafeAreaView>\n </AppSocketProvider>\n </QueryClientProvider>\n )\n}\n\nconst styles = StyleSheet.create({\n container: {\n flex: 1,\n padding: 16,\n backgroundColor: '#f4f5fb',\n },\n})\n`\n}\n\nfunction nativeHealthScreen(contractImport: string) {\n return `import React from 'react'\nimport {\n Button,\n StyleSheet,\n Text,\n TextInput,\n View,\n} from 'react-native'\nimport { healthGet, healthPost } from '../queryClient'\nimport { roomMeta, useSocketClient, useSocketConnection } from '../socket'\n\nconst now = () => new Date().toLocaleTimeString()\n\nfunction useLogs() {\n const [logs, setLogs] = React.useState<string[]>([])\n return {\n logs,\n push: (msg: string) =>\n setLogs((prev) => [\\`[\\${now()}] \\${msg}\\`, ...prev].slice(0, 60)),\n clear: () => setLogs([]),\n }\n}\n\nexport function HealthScreen() {\n const [echo, setEcho] = React.useState('hello rrroute')\n const httpGet = healthGet.useEndpoint()\n const httpPost = healthPost.useEndpoint()\n const socket = useSocketClient()\n const { logs, push, clear } = useLogs()\n\n useSocketConnection({\n event: 'health:connected',\n rooms: ['health'],\n joinMeta: roomMeta,\n leaveMeta: roomMeta,\n onMessage: (payload) => push(\\`connected \\${payload.socketId}\\`),\n })\n\n useSocketConnection({\n event: 'health:pong',\n rooms: ['health'],\n joinMeta: roomMeta,\n leaveMeta: roomMeta,\n onMessage: (payload) =>\n push(\\`pong at \\${payload.at}\\${payload.echo ? \\` (echo: \\${payload.echo})\\` : ''}\\`),\n })\n\n return (\n <View style={styles.card}>\n <Text style={styles.title}>RRRoutes health sandbox</Text>\n <View style={styles.section}>\n <Text style={styles.heading}>HTTP</Text>\n <Button title=\"GET /health\" onPress={() => httpGet.refetch()} />\n <View style={{ height: 12 }} />\n <TextInput\n style={styles.input}\n value={echo}\n onChangeText={setEcho}\n placeholder=\"echo payload\"\n />\n <Button\n title=\"POST /health\"\n onPress={() =>\n httpPost\n .mutateAsync({ echo })\n .then(() => push('POST /health ok'))\n }\n />\n <Text style={styles.label}>GET data</Text>\n <Text style={styles.code}>\n {JSON.stringify(httpGet.data ?? {}, null, 2)}\n </Text>\n <Text style={styles.label}>POST data</Text>\n <Text style={styles.code}>\n {JSON.stringify(httpPost.data ?? {}, null, 2)}\n </Text>\n </View>\n\n <View style={styles.section}>\n <Text style={styles.heading}>Socket</Text>\n <View style={styles.row}>\n <Button title=\"Connect\" onPress={() => socket.connect()} />\n <Button title=\"Disconnect\" onPress={() => socket.disconnect()} />\n </View>\n <View style={{ height: 8 }} />\n <Button\n title=\"Emit ping\"\n onPress={() => socket.emit('health:ping', { note: 'ping from app' })}\n />\n <View style={{ height: 8 }} />\n <View style={styles.row}>\n <Button\n title=\"Join room\"\n onPress={() => socket.joinRooms(['health'], roomMeta)}\n />\n <Button\n title=\"Leave room\"\n onPress={() => socket.leaveRooms(['health'], roomMeta)}\n />\n </View>\n </View>\n\n <View style={styles.section}>\n <Text style={styles.heading}>Socket logs</Text>\n <Button title=\"Clear logs\" onPress={clear} />\n <Text style={styles.code}>\n {logs.length === 0 ? 'No messages yet' : logs.join('\\\\n')}\n </Text>\n </View>\n </View>\n )\n}\n\nconst styles = StyleSheet.create({\n card: {\n backgroundColor: '#fff',\n borderRadius: 12,\n padding: 16,\n gap: 12,\n shadowColor: '#000',\n shadowOpacity: 0.05,\n shadowRadius: 8,\n },\n title: { fontSize: 20, fontWeight: '700' },\n section: { gap: 8 },\n heading: { fontWeight: '600', fontSize: 16 },\n row: { flexDirection: 'row', gap: 8, justifyContent: 'space-between' },\n input: {\n borderWidth: 1,\n borderColor: '#d0d4de',\n borderRadius: 8,\n padding: 8,\n },\n label: { marginTop: 6, fontWeight: '600' },\n code: {\n backgroundColor: '#0f172a',\n color: '#e2e8f0',\n padding: 8,\n borderRadius: 8,\n fontFamily: 'Courier',\n },\n})\n`\n}\n\nconst NATIVE_ENV = `API_URL=http://localhost:4000\nSOCKET_URL=http://localhost:4000\nSOCKET_PATH=/socket.io\n`\n\n// Scaffold orchestrator -------------------------------------------------------\n\nexport async function scaffoldClient(ctx: GeneratorContext) {\n return ctx.clientKind === 'react-native'\n ? scaffoldNativeClient(ctx)\n : scaffoldWebClient(ctx)\n}\n\nasync function scaffoldWebClient(ctx: GeneratorContext) {\n const baseDir = path.join(ctx.rootDir, 'packages', 'client')\n await ensureDir(path.join(baseDir, 'src', 'lib'))\n await ensureDir(path.join(baseDir, 'src', 'pages'))\n\n const pkgJson = {\n name: ctx.packageNames.client,\n version: '0.1.0',\n private: true,\n type: 'module',\n main: 'dist/index.js',\n scripts: {\n dev: 'vite',\n build: 'vite build',\n preview: 'vite preview',\n typecheck: 'tsc -p tsconfig.json --noEmit',\n },\n dependencies: {\n [ctx.packageNames.contract]: 'workspace:*',\n '@emeryld/rrroutes-client': '^2.5.3',\n '@tanstack/react-query': '^5.90.12',\n react: '^18.3.1',\n 'react-dom': '^18.3.1',\n 'socket.io-client': '^4.8.3',\n zod: '^4.2.1',\n },\n devDependencies: {\n '@types/react': '^18.3.27',\n '@types/react-dom': '^18.3.7',\n '@vitejs/plugin-react': '^4.3.4',\n typescript: '^5.9.3',\n vite: '^6.4.1',\n },\n }\n\n const files: Record<string, string> = {\n 'package.json': `${JSON.stringify(pkgJson, null, 2)}\\n`,\n 'tsconfig.json': `${JSON.stringify(baseTsConfig(), null, 2)}\\n`,\n 'vite.config.ts': viteConfig(),\n 'src/main.tsx': MAIN_TSX,\n 'src/App.tsx': appTsx(),\n 'src/lib/queryClient.ts': queryClient(ctx.packageNames.contract),\n 'src/lib/socket.tsx': socketProvider(ctx.packageNames.contract),\n 'src/pages/HealthPage.tsx': healthPage(ctx.packageNames.contract),\n 'src/styles.css': STYLES,\n 'src/env.d.ts': '/// <reference types=\"vite/client\" />\\n',\n 'index.html': indexHtml(),\n '.env': CLIENT_ENV,\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(baseDir, name)\n const writer = name === '.env' ? writeFileForce : writeFileIfMissing\n const result = await writer(fullPath, contents)\n const rel = path.relative(ctx.rootDir, fullPath)\n if (result === 'created' || result === undefined) log.created(rel)\n else log.skipped(rel)\n }\n}\n\nasync function scaffoldNativeClient(ctx: GeneratorContext) {\n const baseDir = path.join(ctx.rootDir, 'packages', 'client')\n await ensureDir(path.join(baseDir, 'src', 'screens'))\n\n const pkgJson = nativePackageJson(\n ctx.packageNames.client,\n ctx.packageNames.contract,\n )\n\n const files: Record<string, string> = {\n 'package.json': `${JSON.stringify(pkgJson, null, 2)}\\n`,\n 'tsconfig.json': `${JSON.stringify(NATIVE_TS_CONFIG, null, 2)}\\n`,\n 'app.json': `${JSON.stringify(nativeAppJson(ctx.appSlug), null, 2)}\\n`,\n 'babel.config.js': NATIVE_BABEL,\n 'App.tsx': nativeAppTsx(),\n 'src/queryClient.ts': nativeQueryClient(ctx.packageNames.contract),\n 'src/socket.tsx': nativeSocket(ctx.packageNames.contract),\n 'src/screens/HealthScreen.tsx': nativeHealthScreen(\n ctx.packageNames.contract,\n ),\n '.env': NATIVE_ENV,\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(baseDir, name)\n const writer = name === '.env' ? writeFileForce : writeFileIfMissing\n const result = await writer(fullPath, contents)\n const rel = path.relative(ctx.rootDir, fullPath)\n if (result === 'created' || result === undefined) log.created(rel)\n else log.skipped(rel)\n }\n}\n","import fs from 'node:fs/promises'\nimport path from 'node:path'\n\nexport async function ensureDir(dir: string): Promise<void> {\n await fs.mkdir(dir, { recursive: true })\n}\n\nexport async function pathExists(target: string): Promise<boolean> {\n try {\n await fs.access(target)\n return true\n } catch {\n return false\n }\n}\n\nexport async function writeFileIfMissing(\n filePath: string,\n contents: string,\n): Promise<'created' | 'skipped'> {\n const dir = path.dirname(filePath)\n await ensureDir(dir)\n if (await pathExists(filePath)) {\n return 'skipped'\n }\n await fs.writeFile(filePath, contents, 'utf8')\n return 'created'\n}\n\nexport async function writeFileForce(\n filePath: string,\n contents: string,\n): Promise<void> {\n const dir = path.dirname(filePath)\n await ensureDir(dir)\n await fs.writeFile(filePath, contents, 'utf8')\n}\n","export const log = {\n info(msg: string) {\n console.log(msg)\n },\n step(msg: string) {\n console.log(`• ${msg}`)\n },\n created(target: string) {\n console.log(` created ${target}`)\n },\n skipped(target: string) {\n console.log(` skipped ${target} (already exists)`)\n },\n warn(msg: string) {\n console.warn(` warning: ${msg}`)\n },\n error(msg: string) {\n console.error(` error: ${msg}`)\n },\n}\n","import path from 'node:path'\nimport { GeneratorContext } from '../types'\nimport { ensureDir, writeFileIfMissing } from '../utils/fs'\nimport { log } from '../utils/log'\n\nconst CONTRACT_TS = `import { defineSocketEvents, finalize, resource } from '@emeryld/rrroutes-contract'\nimport { z } from 'zod'\n\nconst routes = resource('/api')\n .sub(\n resource('health')\n .get({\n outputSchema: z.object({\n status: z.literal('ok'),\n html: z.string().describe('Tiny HTML heartbeat'),\n }),\n description: 'Basic GET health probe for uptime + docs.',\n })\n .post({\n bodySchema: z.object({\n echo: z.string().optional(),\n }),\n outputSchema: z.object({\n status: z.literal('ok'),\n received: z.string().optional(),\n }),\n description: 'POST health probe that echoes a payload.',\n })\n .done(),\n )\n .done()\n\nexport const registry = finalize(routes)\n\nconst sockets = defineSocketEvents(\n {\n joinMetaMessage: z.object({ room: z.string().optional() }),\n leaveMetaMessage: z.object({ room: z.string().optional() }),\n pingPayload: z.object({\n note: z.string().default('ping'),\n sentAt: z.string(),\n }),\n pongPayload: z.object({\n ok: z.boolean(),\n receivedAt: z.string(),\n echo: z.string().optional(),\n }),\n },\n {\n 'health:connected': {\n message: z.object({\n socketId: z.string(),\n at: z.string(),\n message: z.string(),\n }),\n },\n 'health:ping': {\n message: z.object({\n note: z.string().default('ping'),\n }),\n },\n 'health:pong': {\n message: z.object({\n ok: z.literal(true),\n at: z.string(),\n echo: z.string().optional(),\n }),\n },\n },\n)\n\nexport const socketConfig = sockets.config\nexport const socketEvents = sockets.events\nexport type AppRegistry = typeof registry\n`\n\nexport async function scaffoldContract(ctx: GeneratorContext) {\n const baseDir = path.join(ctx.rootDir, 'packages', 'contract')\n await ensureDir(path.join(baseDir, 'src'))\n\n const pkgJson = {\n name: ctx.packageNames.contract,\n version: '0.1.0',\n private: false,\n type: 'module',\n main: 'dist/index.js',\n types: 'dist/index.d.ts',\n files: ['dist'],\n scripts: {\n build: 'tsc -p tsconfig.json',\n typecheck: 'tsc -p tsconfig.json --noEmit',\n watch: 'tsc -p tsconfig.json --watch --preserveWatchOutput',\n },\n dependencies: {\n '@emeryld/rrroutes-contract': '^2.5.2',\n zod: '^4.2.1',\n },\n devDependencies: {\n typescript: '^5.9.3',\n },\n }\n\n const tsconfig = {\n extends: '../../tsconfig.base.json',\n compilerOptions: {\n outDir: 'dist',\n rootDir: 'src',\n declaration: true,\n sourceMap: true,\n },\n include: ['src/**/*'],\n }\n\n const files: Record<string, string> = {\n 'package.json': `${JSON.stringify(pkgJson, null, 2)}\\n`,\n 'tsconfig.json': `${JSON.stringify(tsconfig, null, 2)}\\n`,\n 'src/index.ts': CONTRACT_TS,\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(baseDir, name)\n const result = await writeFileIfMissing(fullPath, contents)\n if (result === 'created') log.created(path.relative(ctx.rootDir, fullPath))\n else log.skipped(path.relative(ctx.rootDir, fullPath))\n }\n}\n","import path from 'node:path'\nimport { GeneratorContext } from '../types'\nimport { ensureDir, writeFileIfMissing } from '../utils/fs'\nimport { log } from '../utils/log'\n\nfunction formatJson(obj: unknown): string {\n return `${JSON.stringify(obj, null, 2)}\\n`\n}\n\nexport async function scaffoldRoot(ctx: GeneratorContext) {\n await ensureDir(ctx.rootDir)\n\n const paths: Record<string, string[]> = {}\n const addPaths = (pkgName: string, dir: string) => {\n const srcRoot = path.posix.join('packages', dir, 'src')\n paths[pkgName] = [path.posix.join(srcRoot, 'index.ts')]\n paths[`${pkgName}/*`] = [path.posix.join(srcRoot, '*')]\n }\n if (ctx.targets.contract) addPaths(ctx.packageNames.contract, 'contract')\n if (ctx.targets.server) addPaths(ctx.packageNames.server, 'server')\n if (ctx.targets.client) addPaths(ctx.packageNames.client, 'client')\n\n const scripts: Record<string, string> = {\n build: 'npm run build --workspaces',\n typecheck: 'npm run typecheck --workspaces --if-present',\n lint: 'npm run lint --workspaces --if-present',\n }\n if (ctx.targets.server) {\n scripts['dev:server'] = `npm run dev --workspace ${ctx.packageNames.server}`\n }\n if (ctx.targets.client) {\n scripts['dev:client'] = `npm run dev --workspace ${ctx.packageNames.client}`\n }\n if (ctx.targets.contract) {\n scripts['dev:contract'] = `npm run watch --workspace ${ctx.packageNames.contract}`\n }\n\n const highlights = ['- Shared contract package (RRRoutes registry + sockets)']\n if (ctx.targets.server) {\n highlights.unshift(\n '- Backend (Express + Socket.IO + RRRoutes server bindings)',\n )\n }\n if (ctx.targets.client) {\n highlights.splice(\n ctx.targets.server ? 1 : 0,\n 0,\n '- Frontend client (React or React Native) with React Query + sockets',\n )\n }\n\n const quickstart: string[] = ['1) Install deps: `npm install` (workspaces)']\n if (ctx.targets.server) {\n quickstart.push(\n `${quickstart.length + 1}) Backend env: edit \\`packages/server/.env\\``,\n )\n }\n if (ctx.targets.contract) {\n quickstart.push(\n `${quickstart.length + 1}) Optional: keep shared contract rebuilding via \\`npm run dev:contract\\``,\n )\n }\n if (ctx.targets.client) {\n quickstart.push(\n `${quickstart.length + 1}) Client env: edit \\`packages/client/.env\\``,\n )\n }\n if (ctx.targets.server || ctx.targets.client) {\n quickstart.push(\n `${quickstart.length + 1}) Run dev servers:${\n ctx.targets.server ? `\\n - API: \\`npm run dev:server\\`` : ''\n }${ctx.targets.client ? `\\n - Client: \\`npm run dev:client\\`` : ''}`,\n )\n }\n\n const pkgJson = {\n name: `${ctx.appSlug}-stack`,\n private: true,\n workspaces: ['packages/*'],\n scripts,\n devDependencies: {\n typescript: '^5.9.3',\n },\n }\n\n const files: Record<string, string> = {\n 'package.json': formatJson(pkgJson),\n 'pnpm-workspace.yaml': \"packages:\\n - 'packages/*'\\n\",\n 'tsconfig.base.json': formatJson({\n $schema: 'https://json.schemastore.org/tsconfig',\n compilerOptions: {\n target: 'ES2020',\n module: 'ESNext',\n moduleResolution: 'Bundler',\n jsx: 'react-jsx',\n strict: true,\n skipLibCheck: true,\n resolveJsonModule: true,\n forceConsistentCasingInFileNames: true,\n sourceMap: true,\n baseUrl: '.',\n paths,\n },\n }),\n '.gitignore': [\n 'node_modules',\n 'dist',\n '.turbo',\n '.expo',\n '.DS_Store',\n '.env',\n '.env.local',\n 'npm-debug.log*',\n 'yarn-error.log',\n ].join('\\n'),\n README: [\n `# ${ctx.projectName} (RRRoutes starter)`,\n '',\n 'Generated via `create-rrroutes`. Includes:',\n ...highlights,\n '',\n '## Quickstart',\n ...quickstart,\n '',\n ctx.targets.server\n ? 'Dockerfile for the API lives in `packages/server/Dockerfile`.'\n : '',\n '',\n ]\n .filter(Boolean)\n .join('\\n'),\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(ctx.rootDir, name)\n const result = await writeFileIfMissing(fullPath, contents)\n if (result === 'created') log.created(path.relative(ctx.rootDir, fullPath))\n else log.skipped(path.relative(ctx.rootDir, fullPath))\n }\n}\n","import path from 'node:path'\nimport { GeneratorContext } from '../types'\nimport { ensureDir, writeFileForce, writeFileIfMissing } from '../utils/fs'\nimport { log } from '../utils/log'\n\nfunction serverIndexTs() {\n return `import 'dotenv/config'\nimport http from 'node:http'\nimport { app } from './http'\nimport { createSockets } from './socket'\nimport { prisma } from './prisma'\n\nasync function main() {\n const PORT = Number.parseInt(process.env.PORT ?? '4000', 10)\n const CORS_ORIGIN = process.env.CORS_ORIGIN ?? 'http://localhost:5173'\n const SOCKET_PATH = process.env.SOCKET_PATH ?? '/socket.io'\n\n await prisma.$connect()\n\n const httpServer = http.createServer(app)\n const sockets = createSockets(httpServer, {\n corsOrigin: CORS_ORIGIN,\n socketPath: SOCKET_PATH,\n })\n\n httpServer.listen(PORT, () => {\n console.log(\n \\`API listening on http://localhost:\\${PORT} (CORS origin: \\${CORS_ORIGIN})\\`,\n )\n })\n\n const shutdown = async () => {\n await sockets.destroy()\n await prisma.$disconnect()\n }\n process.on('SIGTERM', shutdown)\n process.on('SIGINT', shutdown)\n}\n\nmain().catch(async (err) => {\n console.error(err)\n await prisma.$disconnect().catch(() => {})\n process.exit(1)\n})\n`\n}\n\nfunction httpTs(contractImport: string) {\n return `import express, { Router } from 'express'\nimport cors from 'cors'\nimport { createRRRoute } from '@emeryld/rrroutes-server'\nimport { registry } from '${contractImport}'\nimport { controllers, type RequestCtx } from './controllers'\nimport { prisma } from './prisma'\n\nconst CORS_ORIGIN = process.env.CORS_ORIGIN ?? 'http://localhost:5173'\n\nexport const app: Router = express()\napp.use(cors({ origin: CORS_ORIGIN, credentials: true }))\napp.use(express.json())\n\napp.get('/', (_req, res) => {\n res.send('<h1>RRRoutes starter API</h1>')\n})\n\nconst routes = createRRRoute<RequestCtx>(app, {\n buildCtx: async () => ({\n requestId: Math.random().toString(36).slice(2),\n prisma,\n routesLogger: console,\n }),\n debug:\n process.env.NODE_ENV === 'development'\n ? { request: true, handler: true }\n : undefined,\n})\n\nroutes.registerControllers(registry, controllers)\nroutes.warnMissingControllers(registry, console)\nexport { routes }\n`\n}\n\nfunction controllersTs(contractImport: string) {\n return `import { defineControllers } from '@emeryld/rrroutes-server'\nimport type { PrismaClient } from '@prisma/client'\nimport { registry } from '${contractImport}'\n\nexport type RequestCtx = {\n prisma: PrismaClient\n requestId: string\n}\n\nexport const controllers = defineControllers<typeof registry, RequestCtx>()({\n 'GET /api/health': {\n handler: async ({ ctx }) => {\n await ctx.prisma.healthCheck.create({\n data: { note: 'GET health' },\n })\n return {\n out:{\n status: 'ok',\n html: '<!doctype html><html><body><h1>healthy</h1></body></html>',\n}\n }\n },\n },\n 'POST /api/health': {\n handler: async ({ ctx, body }) => {\n await ctx.prisma.healthCheck.create({\n data: { note: body?.echo ?? 'POST health' },\n })\n return {\n out:{\n status: 'ok',\n received: body?.echo ?? 'pong',\n}\n }\n },\n },\n})\n`\n}\n\nfunction socketTs(contractImport: string) {\n return `import { Server as SocketIOServer } from 'socket.io'\nimport type { Server } from 'node:http'\nimport { createSocketConnections } from '@emeryld/rrroutes-server'\nimport { socketConfig, socketEvents } from '${contractImport}'\n\nexport function createSockets(\n httpServer: Server,\n opts: { corsOrigin: string; socketPath: string },\n) {\n const io = new SocketIOServer(httpServer, {\n cors: { origin: opts.corsOrigin, credentials: true },\n path: opts.socketPath,\n })\n\n const sockets = createSocketConnections(io, socketEvents, {\n config: socketConfig,\n heartbeat: { enabled: true },\n sys: {\n 'sys:connect': async ({ socket, helper, complete }) => {\n helper.emit('health:connected', {\n socketId: socket.id,\n at: new Date().toISOString(),\n message: 'connected',\n })\n complete()\n },\n 'sys:disconnect': async ({ cleanup }) => cleanup(),\n 'sys:ping': async ({ ping }) => ({\n ok: true,\n receivedAt: new Date().toISOString(),\n echo: ping.note,\n }),\n 'sys:room_join': async ({ rooms, join }) => {\n await Promise.all(rooms.map((room) => join(room)))\n },\n 'sys:room_leave': async ({ rooms, leave }) => {\n await Promise.all(rooms.map((room) => leave(room)))\n },\n },\n })\n\n sockets.on('health:ping', async (payload, ctx) => {\n sockets.emit(\n 'health:pong',\n { ok: true, at: new Date().toISOString(), echo: payload.note },\n ctx.socketId,\n )\n })\n\n return sockets\n}\n`\n}\n\nfunction prismaTs() {\n return `import { PrismaClient } from '@prisma/client'\n\nexport const prisma = new PrismaClient({\n log: ['warn', 'error'],\n})\n`\n}\n\nfunction prismaSchema() {\n return `generator client {\n provider = \"prisma-client-js\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\nmodel HealthCheck {\n id String @id @default(uuid())\n note String?\n createdAt DateTime @default(now())\n}\n`\n}\n\nfunction serverEnv() {\n return [\n 'PORT=4000',\n 'NODE_ENV=development',\n 'CORS_ORIGIN=http://localhost:5173',\n 'SOCKET_PATH=/socket.io',\n 'DATABASE_URL=postgresql://postgres:postgres@localhost:5432/rrroutes',\n ].join('\\n')\n}\n\nconst tsNodeLoaderRegistrationImport =\n 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));'\n\nexport const nodemonExecCommand = `node --import '${tsNodeLoaderRegistrationImport}' ./src/index.ts`\n\nfunction nodemonJson() {\n return `${JSON.stringify(\n {\n watch: ['src'],\n ext: 'ts',\n ignore: ['src/**/*.test.ts'],\n exec: nodemonExecCommand,\n },\n null,\n 2,\n )}\\n`\n}\n\nfunction dockerfile(serverName: string, contractName: string) {\n return `FROM node:20-alpine\nWORKDIR /app\n\n# Install workspace deps (hoists to /app/node_modules)\nCOPY package.json pnpm-workspace.yaml tsconfig.base.json ./\nCOPY packages/contract/package.json packages/contract/package.json\nCOPY packages/server/package.json packages/server/package.json\nCOPY packages/server/prisma/schema.prisma packages/server/prisma/schema.prisma\nRUN npm install\n\n# Copy source and build server + contract\nCOPY packages ./packages\n\n# Generate Prisma client\nRUN npx prisma generate --schema packages/server/prisma/schema.prisma\n\nRUN npm run build --workspace ${contractName} && npm run build --workspace ${serverName}\n\nEXPOSE 4000\nCMD [\"node\", \"packages/server/dist/index.js\"]\n`\n}\n\nexport async function scaffoldServer(ctx: GeneratorContext) {\n const baseDir = path.join(ctx.rootDir, 'packages', 'server')\n await ensureDir(path.join(baseDir, 'src'))\n await ensureDir(path.join(baseDir, 'prisma'))\n\n const pkgJson = {\n name: ctx.packageNames.server,\n version: '0.1.0',\n private: false,\n type: 'module',\n main: 'dist/index.js',\n types: 'dist/index.d.ts',\n files: ['dist'],\n scripts: {\n dev: 'nodemon --config nodemon.json',\n build: 'tsc -p tsconfig.json',\n typecheck: 'tsc -p tsconfig.json --noEmit',\n start: 'node dist/index.js',\n 'prisma:generate': 'prisma generate --schema prisma/schema.prisma',\n 'prisma:migrate':\n 'prisma migrate dev --schema prisma/schema.prisma --name init',\n 'db:start':\n 'docker run --name rrroutes-db -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=rrroutes -p 5432:5432 -d postgres:15 || docker start rrroutes-db',\n 'db:stop': 'docker stop rrroutes-db || true',\n },\n dependencies: {\n [ctx.packageNames.contract]: 'workspace:*',\n '@emeryld/rrroutes-server': '^2.4.1',\n '@prisma/client': '^5.22.0',\n 'socket.io': '^4.8.1',\n express: '^5.1.0',\n cors: '^2.8.5',\n dotenv: '^16.4.5',\n zod: '^4.2.1',\n },\n devDependencies: {\n '@types/express': '^5.0.6',\n '@types/node': '^24.10.2',\n prisma: '^5.22.0',\n typescript: '^5.9.3',\n 'ts-node': '^10.9.2',\n nodemon: '^3.1.9',\n },\n }\n\n const tsconfig = {\n extends: '../../tsconfig.base.json',\n compilerOptions: {\n outDir: 'dist',\n rootDir: 'src',\n declaration: true,\n sourceMap: true,\n },\n include: ['src/**/*'],\n }\n\n const files: Record<string, string> = {\n 'package.json': `${JSON.stringify(pkgJson, null, 2)}\\n`,\n 'tsconfig.json': `${JSON.stringify(tsconfig, null, 2)}\\n`,\n 'src/index.ts': serverIndexTs(),\n 'src/http.ts': httpTs(ctx.packageNames.contract),\n 'src/controllers.ts': controllersTs(ctx.packageNames.contract),\n 'src/socket.ts': socketTs(ctx.packageNames.contract),\n 'src/prisma.ts': prismaTs(),\n 'prisma/schema.prisma': prismaSchema(),\n '.env': `${serverEnv()}\\n`,\n 'nodemon.json': nodemonJson(),\n Dockerfile: dockerfile(ctx.packageNames.server, ctx.packageNames.contract),\n }\n\n for (const [name, contents] of Object.entries(files)) {\n const fullPath = path.join(baseDir, name)\n const writer =\n name === '.env'\n ? writeFileForce // allow updating env defaults\n : writeFileIfMissing\n const result = await writer(fullPath, contents)\n const rel = path.relative(ctx.rootDir, fullPath)\n if (result === 'created' || result === undefined) log.created(rel)\n else log.skipped(rel)\n }\n}\n","import { createInterface } from 'node:readline/promises'\nimport { stdin as input, stdout as output } from 'node:process'\n\ntype Option<T> = {\n label: string\n value: T\n}\n\nexport function createPrompt() {\n const rl = createInterface({ input, output })\n\n return {\n async text(message: string, fallback?: string) {\n const suffix = fallback ? ` (${fallback})` : ''\n const answer = (await rl.question(`${message}${suffix}: `)).trim()\n return answer.length > 0 ? answer : fallback\n },\n async confirm(message: string, fallback = true) {\n const suffix = fallback ? ' (Y/n)' : ' (y/N)'\n const answer = (await rl.question(`${message}${suffix}: `)).trim()\n if (!answer) return fallback\n return ['y', 'yes'].includes(answer.toLowerCase())\n },\n async select<T>(\n message: string,\n options: Option<T>[],\n fallbackIndex = 0,\n ) {\n console.log(message)\n options.forEach((opt, idx) =>\n console.log(` [${idx + 1}] ${opt.label}`),\n )\n const answer = (await rl.question(`Choose 1-${options.length}: `)).trim()\n const idx = Number.parseInt(answer, 10)\n const normalized =\n Number.isNaN(idx) || idx < 1 || idx > options.length\n ? fallbackIndex\n : idx - 1\n return options[normalized]!.value\n },\n close() {\n rl.close()\n },\n }\n}\n","import { spawn } from 'node:child_process'\n\nexport async function runCommand(\n cmd: string,\n args: string[],\n cwd: string,\n): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const child = spawn(cmd, args, {\n cwd,\n stdio: 'inherit',\n shell: process.platform === 'win32',\n })\n child.on('exit', (code) => {\n if (code === 0) resolve()\n else reject(new Error(`${cmd} ${args.join(' ')} exited with ${code}`))\n })\n child.on('error', (err) => reject(err))\n })\n}\n","export function slugify(input: string, fallback = 'rrroutes-app'): string {\n const slug = input\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n return slug.length > 0 ? slug : fallback\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,IAAAA,oBAAiB;;;ACAjB,IAAAC,oBAAiB;;;ACAjB,sBAAe;AACf,uBAAiB;AAEjB,eAAsB,UAAU,KAA4B;AAC1D,QAAM,gBAAAC,QAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACzC;AAEA,eAAsB,WAAW,QAAkC;AACjE,MAAI;AACF,UAAM,gBAAAA,QAAG,OAAO,MAAM;AACtB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAsB,mBACpB,UACA,UACgC;AAChC,QAAM,MAAM,iBAAAC,QAAK,QAAQ,QAAQ;AACjC,QAAM,UAAU,GAAG;AACnB,MAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,WAAO;AAAA,EACT;AACA,QAAM,gBAAAD,QAAG,UAAU,UAAU,UAAU,MAAM;AAC7C,SAAO;AACT;AAEA,eAAsB,eACpB,UACA,UACe;AACf,QAAM,MAAM,iBAAAC,QAAK,QAAQ,QAAQ;AACjC,QAAM,UAAU,GAAG;AACnB,QAAM,gBAAAD,QAAG,UAAU,UAAU,UAAU,MAAM;AAC/C;;;ACpCO,IAAM,MAAM;AAAA,EACjB,KAAK,KAAa;AAChB,YAAQ,IAAI,GAAG;AAAA,EACjB;AAAA,EACA,KAAK,KAAa;AAChB,YAAQ,IAAI,UAAK,GAAG,EAAE;AAAA,EACxB;AAAA,EACA,QAAQ,QAAgB;AACtB,YAAQ,IAAI,aAAa,MAAM,EAAE;AAAA,EACnC;AAAA,EACA,QAAQ,QAAgB;AACtB,YAAQ,IAAI,aAAa,MAAM,mBAAmB;AAAA,EACpD;AAAA,EACA,KAAK,KAAa;AAChB,YAAQ,KAAK,cAAc,GAAG,EAAE;AAAA,EAClC;AAAA,EACA,MAAM,KAAa;AACjB,YAAQ,MAAM,YAAY,GAAG,EAAE;AAAA,EACjC;AACF;;;AFdA,SAAS,eAAe;AACtB,SAAO;AAAA,IACL,SAAS;AAAA,IACT,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,OAAO,CAAC,aAAa;AAAA,IACvB;AAAA,IACA,SAAS,CAAC,YAAY,gBAAgB;AAAA,EACxC;AACF;AAEA,SAAS,aAAa;AACpB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAOT;AAEA,SAAS,YAAY,gBAAwB;AAC3C,SAAO;AAAA;AAAA,4BAEmB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAe1C;AAEA,SAAS,eAAe,gBAAwB;AAC9C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CAMqC,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAmF5D;AAEA,IAAM,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAYjB,SAAS,SAAS;AAChB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBT;AAEA,IAAM,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuEf,SAAS,WAAW,gBAAwB;AAC1C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6IT;AAEA,SAAS,YAAY;AACnB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAaT;AAEA,IAAM,aAAa;AAAA;AAAA;AAAA;AAOnB,SAAS,kBAAkB,YAAoB,cAAsB;AACnE,SAAO;AAAA,IACL,MAAM;AAAA,IACN,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,SAAS;AAAA,MACP,OAAO;AAAA,MACP,SAAS;AAAA,MACT,KAAK;AAAA,MACL,KAAK;AAAA,MACL,MAAM;AAAA,IACR;AAAA,IACA,cAAc;AAAA,MACZ,CAAC,YAAY,GAAG;AAAA,MAChB,4BAA4B;AAAA,MAC5B,yBAAyB;AAAA,MACzB,MAAM;AAAA,MACN,kBAAkB;AAAA,MAClB,mBAAmB;AAAA,MACnB,OAAO;AAAA,MACP,gBAAgB;AAAA,MAChB,kCAAkC;AAAA,MAClC,wBAAwB;AAAA,MACxB,oBAAoB;AAAA,MACpB,KAAK;AAAA,IACP;AAAA,IACA,iBAAiB;AAAA,MACf,eAAe;AAAA,MACf,gBAAgB;AAAA,MAChB,uBAAuB;AAAA,MACvB,YAAY;AAAA,IACd;AAAA,EACF;AACF;AAEA,SAAS,cAAc,SAAiB;AACtC,SAAO;AAAA,IACL,MAAM;AAAA,MACJ,MAAM,GAAG,OAAO;AAAA,MAChB,MAAM,GAAG,OAAO;AAAA,MAChB,SAAS;AAAA,MACT,aAAa;AAAA,MACb,QAAQ;AAAA,MACR,WAAW,CAAC,OAAO,WAAW,KAAK;AAAA,MACnC,oBAAoB;AAAA,MACpB,OAAO;AAAA,QACL,QAAQ;AAAA,QACR,WAAW;AAAA,QACX,YAAY;AAAA,MACd;AAAA,MACA,aAAa;AAAA,QACX,aAAa;AAAA,MACf;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAM,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQrB,IAAM,mBAAmB;AAAA,EACvB,SAAS;AAAA,EACT,iBAAiB;AAAA,IACf,KAAK;AAAA,IACL,OAAO,CAAC,gBAAgB,OAAO;AAAA,EACjC;AAAA,EACA,SAAS,CAAC,WAAW,UAAU;AAAA,EAC/B,SAAS,CAAC,gBAAgB,mBAAmB,iBAAiB;AAChE;AAEA,SAAS,kBAAkB,gBAAwB;AACjD,SAAO;AAAA;AAAA;AAAA,4BAGmB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAc1C;AAEA,SAAS,aAAa,gBAAwB;AAC5C,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,8CAOqC,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0E5D;AAEA,SAAS,eAAe;AACtB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BT;AAEA,SAAS,mBAAmB,gBAAwB;AAClD,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA+IT;AAEA,IAAM,aAAa;AAAA;AAAA;AAAA;AAOnB,eAAsB,eAAe,KAAuB;AAC1D,SAAO,IAAI,eAAe,iBACtB,qBAAqB,GAAG,IACxB,kBAAkB,GAAG;AAC3B;AAEA,eAAe,kBAAkB,KAAuB;AACtD,QAAM,UAAU,kBAAAE,QAAK,KAAK,IAAI,SAAS,YAAY,QAAQ;AAC3D,QAAM,UAAU,kBAAAA,QAAK,KAAK,SAAS,OAAO,KAAK,CAAC;AAChD,QAAM,UAAU,kBAAAA,QAAK,KAAK,SAAS,OAAO,OAAO,CAAC;AAElD,QAAM,UAAU;AAAA,IACd,MAAM,IAAI,aAAa;AAAA,IACvB,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,MAAM;AAAA,IACN,SAAS;AAAA,MACP,KAAK;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,MACT,WAAW;AAAA,IACb;AAAA,IACA,cAAc;AAAA,MACZ,CAAC,IAAI,aAAa,QAAQ,GAAG;AAAA,MAC7B,4BAA4B;AAAA,MAC5B,yBAAyB;AAAA,MACzB,OAAO;AAAA,MACP,aAAa;AAAA,MACb,oBAAoB;AAAA,MACpB,KAAK;AAAA,IACP;AAAA,IACA,iBAAiB;AAAA,MACf,gBAAgB;AAAA,MAChB,oBAAoB;AAAA,MACpB,wBAAwB;AAAA,MACxB,YAAY;AAAA,MACZ,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,QAAgC;AAAA,IACpC,gBAAgB,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA;AAAA,IACnD,iBAAiB,GAAG,KAAK,UAAU,aAAa,GAAG,MAAM,CAAC,CAAC;AAAA;AAAA,IAC3D,kBAAkB,WAAW;AAAA,IAC7B,gBAAgB;AAAA,IAChB,eAAe,OAAO;AAAA,IACtB,0BAA0B,YAAY,IAAI,aAAa,QAAQ;AAAA,IAC/D,sBAAsB,eAAe,IAAI,aAAa,QAAQ;AAAA,IAC9D,4BAA4B,WAAW,IAAI,aAAa,QAAQ;AAAA,IAChE,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,cAAc,UAAU;AAAA,IACxB,QAAQ;AAAA,EACV;AAEA,aAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACpD,UAAM,WAAW,kBAAAA,QAAK,KAAK,SAAS,IAAI;AACxC,UAAM,SAAS,SAAS,SAAS,iBAAiB;AAClD,UAAM,SAAS,MAAM,OAAO,UAAU,QAAQ;AAC9C,UAAM,MAAM,kBAAAA,QAAK,SAAS,IAAI,SAAS,QAAQ;AAC/C,QAAI,WAAW,aAAa,WAAW,OAAW,KAAI,QAAQ,GAAG;AAAA,QAC5D,KAAI,QAAQ,GAAG;AAAA,EACtB;AACF;AAEA,eAAe,qBAAqB,KAAuB;AACzD,QAAM,UAAU,kBAAAA,QAAK,KAAK,IAAI,SAAS,YAAY,QAAQ;AAC3D,QAAM,UAAU,kBAAAA,QAAK,KAAK,SAAS,OAAO,SAAS,CAAC;AAEpD,QAAM,UAAU;AAAA,IACd,IAAI,aAAa;AAAA,IACjB,IAAI,aAAa;AAAA,EACnB;AAEA,QAAM,QAAgC;AAAA,IACpC,gBAAgB,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA;AAAA,IACnD,iBAAiB,GAAG,KAAK,UAAU,kBAAkB,MAAM,CAAC,CAAC;AAAA;AAAA,IAC7D,YAAY,GAAG,KAAK,UAAU,cAAc,IAAI,OAAO,GAAG,MAAM,CAAC,CAAC;AAAA;AAAA,IAClE,mBAAmB;AAAA,IACnB,WAAW,aAAa;AAAA,IACxB,sBAAsB,kBAAkB,IAAI,aAAa,QAAQ;AAAA,IACjE,kBAAkB,aAAa,IAAI,aAAa,QAAQ;AAAA,IACxD,gCAAgC;AAAA,MAC9B,IAAI,aAAa;AAAA,IACnB;AAAA,IACA,QAAQ;AAAA,EACV;AAEA,aAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACpD,UAAM,WAAW,kBAAAA,QAAK,KAAK,SAAS,IAAI;AACxC,UAAM,SAAS,SAAS,SAAS,iBAAiB;AAClD,UAAM,SAAS,MAAM,OAAO,UAAU,QAAQ;AAC9C,UAAM,MAAM,kBAAAA,QAAK,SAAS,IAAI,SAAS,QAAQ;AAC/C,QAAI,WAAW,aAAa,WAAW,OAAW,KAAI,QAAQ,GAAG;AAAA,QAC5D,KAAI,QAAQ,GAAG;AAAA,EACtB;AACF;;;AGv2BA,IAAAC,oBAAiB;AAKjB,IAAM,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuEpB,eAAsB,iBAAiB,KAAuB;AAC5D,QAAM,UAAU,kBAAAC,QAAK,KAAK,IAAI,SAAS,YAAY,UAAU;AAC7D,QAAM,UAAU,kBAAAA,QAAK,KAAK,SAAS,KAAK,CAAC;AAEzC,QAAM,UAAU;AAAA,IACd,MAAM,IAAI,aAAa;AAAA,IACvB,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,MAAM;AAAA,IACd,SAAS;AAAA,MACP,OAAO;AAAA,MACP,WAAW;AAAA,MACX,OAAO;AAAA,IACT;AAAA,IACA,cAAc;AAAA,MACZ,8BAA8B;AAAA,MAC9B,KAAK;AAAA,IACP;AAAA,IACA,iBAAiB;AAAA,MACf,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,WAAW;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,aAAa;AAAA,MACb,WAAW;AAAA,IACb;AAAA,IACA,SAAS,CAAC,UAAU;AAAA,EACtB;AAEA,QAAM,QAAgC;AAAA,IACpC,gBAAgB,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA;AAAA,IACnD,iBAAiB,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA;AAAA,IACrD,gBAAgB;AAAA,EAClB;AAEA,aAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACpD,UAAM,WAAW,kBAAAA,QAAK,KAAK,SAAS,IAAI;AACxC,UAAM,SAAS,MAAM,mBAAmB,UAAU,QAAQ;AAC1D,QAAI,WAAW,UAAW,KAAI,QAAQ,kBAAAA,QAAK,SAAS,IAAI,SAAS,QAAQ,CAAC;AAAA,QACrE,KAAI,QAAQ,kBAAAA,QAAK,SAAS,IAAI,SAAS,QAAQ,CAAC;AAAA,EACvD;AACF;;;AC7HA,IAAAC,oBAAiB;AAKjB,SAAS,WAAW,KAAsB;AACxC,SAAO,GAAG,KAAK,UAAU,KAAK,MAAM,CAAC,CAAC;AAAA;AACxC;AAEA,eAAsB,aAAa,KAAuB;AACxD,QAAM,UAAU,IAAI,OAAO;AAE3B,QAAM,QAAkC,CAAC;AACzC,QAAM,WAAW,CAAC,SAAiB,QAAgB;AACjD,UAAM,UAAU,kBAAAC,QAAK,MAAM,KAAK,YAAY,KAAK,KAAK;AACtD,UAAM,OAAO,IAAI,CAAC,kBAAAA,QAAK,MAAM,KAAK,SAAS,UAAU,CAAC;AACtD,UAAM,GAAG,OAAO,IAAI,IAAI,CAAC,kBAAAA,QAAK,MAAM,KAAK,SAAS,GAAG,CAAC;AAAA,EACxD;AACA,MAAI,IAAI,QAAQ,SAAU,UAAS,IAAI,aAAa,UAAU,UAAU;AACxE,MAAI,IAAI,QAAQ,OAAQ,UAAS,IAAI,aAAa,QAAQ,QAAQ;AAClE,MAAI,IAAI,QAAQ,OAAQ,UAAS,IAAI,aAAa,QAAQ,QAAQ;AAElE,QAAM,UAAkC;AAAA,IACtC,OAAO;AAAA,IACP,WAAW;AAAA,IACX,MAAM;AAAA,EACR;AACA,MAAI,IAAI,QAAQ,QAAQ;AACtB,YAAQ,YAAY,IAAI,2BAA2B,IAAI,aAAa,MAAM;AAAA,EAC5E;AACA,MAAI,IAAI,QAAQ,QAAQ;AACtB,YAAQ,YAAY,IAAI,2BAA2B,IAAI,aAAa,MAAM;AAAA,EAC5E;AACA,MAAI,IAAI,QAAQ,UAAU;AACxB,YAAQ,cAAc,IAAI,6BAA6B,IAAI,aAAa,QAAQ;AAAA,EAClF;AAEA,QAAM,aAAa,CAAC,yDAAyD;AAC7E,MAAI,IAAI,QAAQ,QAAQ;AACtB,eAAW;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,QAAQ;AACtB,eAAW;AAAA,MACT,IAAI,QAAQ,SAAS,IAAI;AAAA,MACzB;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,aAAuB,CAAC,6CAA6C;AAC3E,MAAI,IAAI,QAAQ,QAAQ;AACtB,eAAW;AAAA,MACT,GAAG,WAAW,SAAS,CAAC;AAAA,IAC1B;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,UAAU;AACxB,eAAW;AAAA,MACT,GAAG,WAAW,SAAS,CAAC;AAAA,IAC1B;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,QAAQ;AACtB,eAAW;AAAA,MACT,GAAG,WAAW,SAAS,CAAC;AAAA,IAC1B;AAAA,EACF;AACA,MAAI,IAAI,QAAQ,UAAU,IAAI,QAAQ,QAAQ;AAC5C,eAAW;AAAA,MACT,GAAG,WAAW,SAAS,CAAC,qBACtB,IAAI,QAAQ,SAAS;AAAA,oCAAuC,EAC9D,GAAG,IAAI,QAAQ,SAAS;AAAA,uCAA0C,EAAE;AAAA,IACtE;AAAA,EACF;AAEA,QAAM,UAAU;AAAA,IACd,MAAM,GAAG,IAAI,OAAO;AAAA,IACpB,SAAS;AAAA,IACT,YAAY,CAAC,YAAY;AAAA,IACzB;AAAA,IACA,iBAAiB;AAAA,MACf,YAAY;AAAA,IACd;AAAA,EACF;AAEA,QAAM,QAAgC;AAAA,IACpC,gBAAgB,WAAW,OAAO;AAAA,IAClC,uBAAuB;AAAA,IACvB,sBAAsB,WAAW;AAAA,MAC/B,SAAS;AAAA,MACT,iBAAiB;AAAA,QACf,QAAQ;AAAA,QACR,QAAQ;AAAA,QACR,kBAAkB;AAAA,QAClB,KAAK;AAAA,QACL,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,mBAAmB;AAAA,QACnB,kCAAkC;AAAA,QAClC,WAAW;AAAA,QACX,SAAS;AAAA,QACT;AAAA,MACF;AAAA,IACF,CAAC;AAAA,IACD,cAAc;AAAA,MACZ;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,EAAE,KAAK,IAAI;AAAA,IACX,QAAQ;AAAA,MACN,KAAK,IAAI,WAAW;AAAA,MACpB;AAAA,MACA;AAAA,MACA,GAAG;AAAA,MACH;AAAA,MACA;AAAA,MACA,GAAG;AAAA,MACH;AAAA,MACA,IAAI,QAAQ,SACR,kEACA;AAAA,MACJ;AAAA,IACF,EACG,OAAO,OAAO,EACd,KAAK,IAAI;AAAA,EACd;AAEA,aAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACpD,UAAM,WAAW,kBAAAA,QAAK,KAAK,IAAI,SAAS,IAAI;AAC5C,UAAM,SAAS,MAAM,mBAAmB,UAAU,QAAQ;AAC1D,QAAI,WAAW,UAAW,KAAI,QAAQ,kBAAAA,QAAK,SAAS,IAAI,SAAS,QAAQ,CAAC;AAAA,QACrE,KAAI,QAAQ,kBAAAA,QAAK,SAAS,IAAI,SAAS,QAAQ,CAAC;AAAA,EACvD;AACF;;;AC3IA,IAAAC,oBAAiB;AAKjB,SAAS,gBAAgB;AACvB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuCT;AAEA,SAAS,OAAO,gBAAwB;AACtC,SAAO;AAAA;AAAA;AAAA,4BAGmB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8B1C;AAEA,SAAS,cAAc,gBAAwB;AAC7C,SAAO;AAAA;AAAA,4BAEmB,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAoC1C;AAEA,SAAS,SAAS,gBAAwB;AACxC,SAAO;AAAA;AAAA;AAAA,8CAGqC,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiD5D;AAEA,SAAS,WAAW;AAClB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAMT;AAEA,SAAS,eAAe;AACtB,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeT;AAEA,SAAS,YAAY;AACnB,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,EAAE,KAAK,IAAI;AACb;AAEA,IAAM,iCACJ;AAEK,IAAM,qBAAqB,kBAAkB,8BAA8B;AAElF,SAAS,cAAc;AACrB,SAAO,GAAG,KAAK;AAAA,IACb;AAAA,MACE,OAAO,CAAC,KAAK;AAAA,MACb,KAAK;AAAA,MACL,QAAQ,CAAC,kBAAkB;AAAA,MAC3B,MAAM;AAAA,IACR;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAAA;AACH;AAEA,SAAS,WAAW,YAAoB,cAAsB;AAC5D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,gCAgBuB,YAAY,iCAAiC,UAAU;AAAA;AAAA;AAAA;AAAA;AAKvF;AAEA,eAAsB,eAAe,KAAuB;AAC1D,QAAM,UAAU,kBAAAC,QAAK,KAAK,IAAI,SAAS,YAAY,QAAQ;AAC3D,QAAM,UAAU,kBAAAA,QAAK,KAAK,SAAS,KAAK,CAAC;AACzC,QAAM,UAAU,kBAAAA,QAAK,KAAK,SAAS,QAAQ,CAAC;AAE5C,QAAM,UAAU;AAAA,IACd,MAAM,IAAI,aAAa;AAAA,IACvB,SAAS;AAAA,IACT,SAAS;AAAA,IACT,MAAM;AAAA,IACN,MAAM;AAAA,IACN,OAAO;AAAA,IACP,OAAO,CAAC,MAAM;AAAA,IACd,SAAS;AAAA,MACP,KAAK;AAAA,MACL,OAAO;AAAA,MACP,WAAW;AAAA,MACX,OAAO;AAAA,MACP,mBAAmB;AAAA,MACnB,kBACE;AAAA,MACF,YACE;AAAA,MACF,WAAW;AAAA,IACb;AAAA,IACA,cAAc;AAAA,MACZ,CAAC,IAAI,aAAa,QAAQ,GAAG;AAAA,MAC7B,4BAA4B;AAAA,MAC5B,kBAAkB;AAAA,MAClB,aAAa;AAAA,MACb,SAAS;AAAA,MACT,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,KAAK;AAAA,IACP;AAAA,IACA,iBAAiB;AAAA,MACf,kBAAkB;AAAA,MAClB,eAAe;AAAA,MACf,QAAQ;AAAA,MACR,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AAAA,EACF;AAEA,QAAM,WAAW;AAAA,IACf,SAAS;AAAA,IACT,iBAAiB;AAAA,MACf,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,aAAa;AAAA,MACb,WAAW;AAAA,IACb;AAAA,IACA,SAAS,CAAC,UAAU;AAAA,EACtB;AAEA,QAAM,QAAgC;AAAA,IACpC,gBAAgB,GAAG,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AAAA;AAAA,IACnD,iBAAiB,GAAG,KAAK,UAAU,UAAU,MAAM,CAAC,CAAC;AAAA;AAAA,IACrD,gBAAgB,cAAc;AAAA,IAC9B,eAAe,OAAO,IAAI,aAAa,QAAQ;AAAA,IAC/C,sBAAsB,cAAc,IAAI,aAAa,QAAQ;AAAA,IAC7D,iBAAiB,SAAS,IAAI,aAAa,QAAQ;AAAA,IACnD,iBAAiB,SAAS;AAAA,IAC1B,wBAAwB,aAAa;AAAA,IACrC,QAAQ,GAAG,UAAU,CAAC;AAAA;AAAA,IACtB,gBAAgB,YAAY;AAAA,IAC5B,YAAY,WAAW,IAAI,aAAa,QAAQ,IAAI,aAAa,QAAQ;AAAA,EAC3E;AAEA,aAAW,CAAC,MAAM,QAAQ,KAAK,OAAO,QAAQ,KAAK,GAAG;AACpD,UAAM,WAAW,kBAAAA,QAAK,KAAK,SAAS,IAAI;AACxC,UAAM,SACJ,SAAS,SACL,iBACA;AACN,UAAM,SAAS,MAAM,OAAO,UAAU,QAAQ;AAC9C,UAAM,MAAM,kBAAAA,QAAK,SAAS,IAAI,SAAS,QAAQ;AAC/C,QAAI,WAAW,aAAa,WAAW,OAAW,KAAI,QAAQ,GAAG;AAAA,QAC5D,KAAI,QAAQ,GAAG;AAAA,EACtB;AACF;;;ACnVA,IAAAC,mBAAgC;AAChC,0BAAiD;AAO1C,SAAS,eAAe;AAC7B,QAAM,SAAK,kCAAgB,EAAE,2BAAAC,OAAO,4BAAAC,OAAO,CAAC;AAE5C,SAAO;AAAA,IACL,MAAM,KAAK,SAAiB,UAAmB;AAC7C,YAAM,SAAS,WAAW,KAAK,QAAQ,MAAM;AAC7C,YAAM,UAAU,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,IAAI,GAAG,KAAK;AACjE,aAAO,OAAO,SAAS,IAAI,SAAS;AAAA,IACtC;AAAA,IACA,MAAM,QAAQ,SAAiB,WAAW,MAAM;AAC9C,YAAM,SAAS,WAAW,WAAW;AACrC,YAAM,UAAU,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,MAAM,IAAI,GAAG,KAAK;AACjE,UAAI,CAAC,OAAQ,QAAO;AACpB,aAAO,CAAC,KAAK,KAAK,EAAE,SAAS,OAAO,YAAY,CAAC;AAAA,IACnD;AAAA,IACA,MAAM,OACJ,SACA,SACA,gBAAgB,GAChB;AACA,cAAQ,IAAI,OAAO;AACnB,cAAQ;AAAA,QAAQ,CAAC,KAAKC,SACpB,QAAQ,IAAI,MAAMA,OAAM,CAAC,KAAK,IAAI,KAAK,EAAE;AAAA,MAC3C;AACA,YAAM,UAAU,MAAM,GAAG,SAAS,YAAY,QAAQ,MAAM,IAAI,GAAG,KAAK;AACxE,YAAM,MAAM,OAAO,SAAS,QAAQ,EAAE;AACtC,YAAM,aACJ,OAAO,MAAM,GAAG,KAAK,MAAM,KAAK,MAAM,QAAQ,SAC1C,gBACA,MAAM;AACZ,aAAO,QAAQ,UAAU,EAAG;AAAA,IAC9B;AAAA,IACA,QAAQ;AACN,SAAG,MAAM;AAAA,IACX;AAAA,EACF;AACF;;;AC5CA,gCAAsB;AAEtB,eAAsB,WACpB,KACA,MACA,KACe;AACf,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,YAAQ,iCAAM,KAAK,MAAM;AAAA,MAC7B;AAAA,MACA,OAAO;AAAA,MACP,OAAO,QAAQ,aAAa;AAAA,IAC9B,CAAC;AACD,UAAM,GAAG,QAAQ,CAAC,SAAS;AACzB,UAAI,SAAS,EAAG,SAAQ;AAAA,UACnB,QAAO,IAAI,MAAM,GAAG,GAAG,IAAI,KAAK,KAAK,GAAG,CAAC,gBAAgB,IAAI,EAAE,CAAC;AAAA,IACvE,CAAC;AACD,UAAM,GAAG,SAAS,CAAC,QAAQ,OAAO,GAAG,CAAC;AAAA,EACxC,CAAC;AACH;;;ACnBO,SAAS,QAAQC,QAAe,WAAW,gBAAwB;AACxE,QAAM,OAAOA,OACV,KAAK,EACL,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE;AACzB,SAAO,KAAK,SAAS,IAAI,OAAO;AAClC;;;ATIA,SAAS,eAAe,QAAyB;AAC/C,MAAI,WAAW;AACb,WAAO,EAAE,QAAQ,MAAM,QAAQ,MAAM,UAAU,KAAK;AACtD,MAAI,WAAW;AACb,WAAO,EAAE,QAAQ,MAAM,QAAQ,OAAO,UAAU,KAAK;AACvD,MAAI,WAAW;AACb,WAAO,EAAE,QAAQ,OAAO,QAAQ,MAAM,UAAU,KAAK;AACvD,SAAO,EAAE,QAAQ,OAAO,QAAQ,OAAO,UAAU,KAAK;AACxD;AAEA,eAAe,gBAA2C;AACxD,QAAM,SAAS,aAAa;AAC5B,MAAI;AACF,UAAM,cACH,MAAM,OAAO,KAAK,gBAAgB,kBAAkB,KACrD;AACF,UAAM,UAAU,QAAQ,WAAW;AACnC,UAAM,iBACH,MAAM,OAAO,KAAK,oBAAoB,KAAK,OAAO,EAAE,KAAM,KAAK,OAAO;AACzE,UAAM,UAAU,kBAAAC,QAAK,QAAQ,QAAQ,IAAI,GAAG,cAAc;AAC1D,UAAM,aACH,MAAM,OAAO,KAAK,yBAAyB,OAAO,KAAM;AAC3D,UAAM,eAAe,QAAQ,UAAU;AAEvC,UAAM,YAAY,MAAM,OAAO,OAAO,0BAA0B;AAAA,MAC9D;AAAA,QACE,OAAO;AAAA,QACP,OAAO;AAAA,MACT;AAAA,MACA,EAAE,OAAO,iBAAiB,OAAO,WAAW;AAAA,MAC5C,EAAE,OAAO,gBAAgB,OAAO,SAAS;AAAA,MACzC,EAAE,OAAO,iBAAiB,OAAO,SAAS;AAAA,IAC5C,CAAC;AACD,UAAM,UAAU,eAAe,SAAS;AAExC,QAAI,aAAyB;AAC7B,QAAI,QAAQ,QAAQ;AAClB,mBAAa,MAAM,OAAO;AAAA,QACxB;AAAA,QACA;AAAA,UACE,EAAE,OAAO,qBAAqB,OAAO,QAAQ;AAAA,UAC7C,EAAE,OAAO,uBAAuB,OAAO,eAAe;AAAA,QACxD;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,UAAM,eAAe;AAAA,MACnB,UAAU,IAAI,YAAY;AAAA,MAC1B,QAAQ,IAAI,YAAY;AAAA,MACxB,QAAQ,IAAI,YAAY;AAAA,IAC1B;AAEA,WAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF,UAAE;AACA,WAAO,MAAM;AAAA,EACf;AACF;AAEA,eAAe,gBAAgB,KAAuB;AACpD,MAAI,QAAQ,IAAI,oCAAoC,KAAK;AACvD,QAAI,KAAK,mEAAmE;AAC5E;AAAA,EACF;AAEA,QAAM,QAA+D;AAAA,IACnE;AAAA,MACE,OAAO;AAAA,MACP,MAAM,CAAC,SAAS;AAAA,MAChB,SAAS;AAAA,IACX;AAAA,IACA;AAAA,MACE,OAAO,6BAA6B,IAAI,aAAa,QAAQ;AAAA,MAC7D,MAAM,CAAC,YAAY,IAAI,aAAa,UAAU,OAAO;AAAA,MACrD,SAAS,IAAI,QAAQ;AAAA,IACvB;AAAA,IACA;AAAA,MACE,OAAO,gCAAgC,IAAI,aAAa,MAAM;AAAA,MAC9D,MAAM,CAAC,YAAY,IAAI,aAAa,QAAQ,iBAAiB;AAAA,MAC7D,SAAS,IAAI,QAAQ;AAAA,IACvB;AAAA,EACF;AAEA,aAAW,QAAQ,OAAO;AACxB,QAAI,CAAC,KAAK,QAAS;AACnB,QAAI;AACF,UAAI,KAAK,KAAK,KAAK;AACnB,YAAM,WAAW,QAAQ,KAAK,MAAM,IAAI,OAAO;AAAA,IACjD,SAAS,KAAK;AACZ,UAAI;AAAA,QACF,gBAAgB,KAAK,KAAK,yBAAyB,KAAK,KAAK,KAAK,GAAG,CAAC;AAAA,EAAK,OAAO,GAAG,CAAC;AAAA,MACxF;AACA;AAAA,IACF;AAAA,EACF;AACF;AAEA,eAAe,OAAO;AACpB,QAAM,MAAM,MAAM,cAAc;AAChC,MAAI,KAAK,mCAAmC,IAAI,OAAO,EAAE;AAEzD,QAAM,aAAa,GAAG;AACtB,MAAI,IAAI,QAAQ,SAAU,OAAM,iBAAiB,GAAG;AACpD,MAAI,IAAI,QAAQ,OAAQ,OAAM,eAAe,GAAG;AAChD,MAAI,IAAI,QAAQ,OAAQ,OAAM,eAAe,GAAG;AAEhD,QAAM,gBAAgB,GAAG;AAEzB,MAAI,KAAK,+CAA+C;AAC1D;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,MAAI,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AAC1D,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["import_node_path","import_node_path","fs","path","path","import_node_path","path","import_node_path","path","import_node_path","path","import_promises","input","output","idx","input","path"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@emeryld/create-rrroutes",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {