@emeryld/create-rrroutes 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,39 +1,61 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
2
+ "use strict";
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") {
11
+ for (let key of __getOwnPropNames(from))
12
+ if (!__hasOwnProp.call(to, key) && key !== except)
13
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
14
+ }
15
+ return to;
16
+ };
17
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
18
+ // If the importer is in node compatibility mode or this is not an ESM
19
+ // file that has been converted to a CommonJS file using a Babel-
20
+ // compatible transform (i.e. "__esModule" has not been set), then set
21
+ // "default" to the CommonJS "module.exports" for node compatibility.
22
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
23
+ mod
24
+ ));
3
25
 
4
26
  // src/index.ts
5
- import path6 from "path";
27
+ var import_node_path6 = __toESM(require("path"), 1);
6
28
 
7
29
  // src/generators/client.ts
8
- import path2 from "path";
30
+ var import_node_path2 = __toESM(require("path"), 1);
9
31
 
10
32
  // src/utils/fs.ts
11
- import fs from "fs/promises";
12
- import path from "path";
33
+ var import_promises = __toESM(require("fs/promises"), 1);
34
+ var import_node_path = __toESM(require("path"), 1);
13
35
  async function ensureDir(dir) {
14
- await fs.mkdir(dir, { recursive: true });
36
+ await import_promises.default.mkdir(dir, { recursive: true });
15
37
  }
16
38
  async function pathExists(target) {
17
39
  try {
18
- await fs.access(target);
40
+ await import_promises.default.access(target);
19
41
  return true;
20
42
  } catch {
21
43
  return false;
22
44
  }
23
45
  }
24
46
  async function writeFileIfMissing(filePath, contents) {
25
- const dir = path.dirname(filePath);
47
+ const dir = import_node_path.default.dirname(filePath);
26
48
  await ensureDir(dir);
27
49
  if (await pathExists(filePath)) {
28
50
  return "skipped";
29
51
  }
30
- await fs.writeFile(filePath, contents, "utf8");
52
+ await import_promises.default.writeFile(filePath, contents, "utf8");
31
53
  return "created";
32
54
  }
33
55
  async function writeFileForce(filePath, contents) {
34
- const dir = path.dirname(filePath);
56
+ const dir = import_node_path.default.dirname(filePath);
35
57
  await ensureDir(dir);
36
- await fs.writeFile(filePath, contents, "utf8");
58
+ await import_promises.default.writeFile(filePath, contents, "utf8");
37
59
  }
38
60
 
39
61
  // src/utils/log.ts
@@ -794,9 +816,9 @@ async function scaffoldClient(ctx) {
794
816
  return ctx.clientKind === "react-native" ? scaffoldNativeClient(ctx) : scaffoldWebClient(ctx);
795
817
  }
796
818
  async function scaffoldWebClient(ctx) {
797
- const baseDir = path2.join(ctx.rootDir, "packages", "client");
798
- await ensureDir(path2.join(baseDir, "src", "lib"));
799
- await ensureDir(path2.join(baseDir, "src", "pages"));
819
+ const baseDir = import_node_path2.default.join(ctx.rootDir, "packages", "client");
820
+ await ensureDir(import_node_path2.default.join(baseDir, "src", "lib"));
821
+ await ensureDir(import_node_path2.default.join(baseDir, "src", "pages"));
800
822
  const pkgJson = {
801
823
  name: ctx.packageNames.client,
802
824
  version: "0.1.0",
@@ -843,17 +865,17 @@ async function scaffoldWebClient(ctx) {
843
865
  ".env": CLIENT_ENV
844
866
  };
845
867
  for (const [name, contents] of Object.entries(files)) {
846
- const fullPath = path2.join(baseDir, name);
868
+ const fullPath = import_node_path2.default.join(baseDir, name);
847
869
  const writer = name === ".env" ? writeFileForce : writeFileIfMissing;
848
870
  const result = await writer(fullPath, contents);
849
- const rel = path2.relative(ctx.rootDir, fullPath);
871
+ const rel = import_node_path2.default.relative(ctx.rootDir, fullPath);
850
872
  if (result === "created" || result === void 0) log.created(rel);
851
873
  else log.skipped(rel);
852
874
  }
853
875
  }
854
876
  async function scaffoldNativeClient(ctx) {
855
- const baseDir = path2.join(ctx.rootDir, "packages", "client");
856
- await ensureDir(path2.join(baseDir, "src", "screens"));
877
+ const baseDir = import_node_path2.default.join(ctx.rootDir, "packages", "client");
878
+ await ensureDir(import_node_path2.default.join(baseDir, "src", "screens"));
857
879
  const pkgJson = nativePackageJson(
858
880
  ctx.packageNames.client,
859
881
  ctx.packageNames.contract
@@ -875,17 +897,17 @@ async function scaffoldNativeClient(ctx) {
875
897
  ".env": NATIVE_ENV
876
898
  };
877
899
  for (const [name, contents] of Object.entries(files)) {
878
- const fullPath = path2.join(baseDir, name);
900
+ const fullPath = import_node_path2.default.join(baseDir, name);
879
901
  const writer = name === ".env" ? writeFileForce : writeFileIfMissing;
880
902
  const result = await writer(fullPath, contents);
881
- const rel = path2.relative(ctx.rootDir, fullPath);
903
+ const rel = import_node_path2.default.relative(ctx.rootDir, fullPath);
882
904
  if (result === "created" || result === void 0) log.created(rel);
883
905
  else log.skipped(rel);
884
906
  }
885
907
  }
886
908
 
887
909
  // src/generators/contract.ts
888
- import path3 from "path";
910
+ var import_node_path3 = __toESM(require("path"), 1);
889
911
  var CONTRACT_TS = `import { defineSocketEvents, finalize, resource } from '@emeryld/rrroutes-contract'
890
912
  import { z } from 'zod'
891
913
 
@@ -957,8 +979,8 @@ export const socketEvents = sockets.events
957
979
  export type AppRegistry = typeof registry
958
980
  `;
959
981
  async function scaffoldContract(ctx) {
960
- const baseDir = path3.join(ctx.rootDir, "packages", "contract");
961
- await ensureDir(path3.join(baseDir, "src"));
982
+ const baseDir = import_node_path3.default.join(ctx.rootDir, "packages", "contract");
983
+ await ensureDir(import_node_path3.default.join(baseDir, "src"));
962
984
  const pkgJson = {
963
985
  name: ctx.packageNames.contract,
964
986
  version: "0.1.0",
@@ -997,15 +1019,15 @@ async function scaffoldContract(ctx) {
997
1019
  "src/index.ts": CONTRACT_TS
998
1020
  };
999
1021
  for (const [name, contents] of Object.entries(files)) {
1000
- const fullPath = path3.join(baseDir, name);
1022
+ const fullPath = import_node_path3.default.join(baseDir, name);
1001
1023
  const result = await writeFileIfMissing(fullPath, contents);
1002
- if (result === "created") log.created(path3.relative(ctx.rootDir, fullPath));
1003
- else log.skipped(path3.relative(ctx.rootDir, fullPath));
1024
+ if (result === "created") log.created(import_node_path3.default.relative(ctx.rootDir, fullPath));
1025
+ else log.skipped(import_node_path3.default.relative(ctx.rootDir, fullPath));
1004
1026
  }
1005
1027
  }
1006
1028
 
1007
1029
  // src/generators/root.ts
1008
- import path4 from "path";
1030
+ var import_node_path4 = __toESM(require("path"), 1);
1009
1031
  function formatJson(obj) {
1010
1032
  return `${JSON.stringify(obj, null, 2)}
1011
1033
  `;
@@ -1106,15 +1128,15 @@ async function scaffoldRoot(ctx) {
1106
1128
  ].filter(Boolean).join("\n")
1107
1129
  };
1108
1130
  for (const [name, contents] of Object.entries(files)) {
1109
- const fullPath = path4.join(ctx.rootDir, name);
1131
+ const fullPath = import_node_path4.default.join(ctx.rootDir, name);
1110
1132
  const result = await writeFileIfMissing(fullPath, contents);
1111
- if (result === "created") log.created(path4.relative(ctx.rootDir, fullPath));
1112
- else log.skipped(path4.relative(ctx.rootDir, fullPath));
1133
+ if (result === "created") log.created(import_node_path4.default.relative(ctx.rootDir, fullPath));
1134
+ else log.skipped(import_node_path4.default.relative(ctx.rootDir, fullPath));
1113
1135
  }
1114
1136
  }
1115
1137
 
1116
1138
  // src/generators/server.ts
1117
- import path5 from "path";
1139
+ var import_node_path5 = __toESM(require("path"), 1);
1118
1140
  function serverIndexTs() {
1119
1141
  return `import 'dotenv/config'
1120
1142
  import http from 'node:http'
@@ -1339,9 +1361,9 @@ CMD ["node", "packages/server/dist/index.js"]
1339
1361
  `;
1340
1362
  }
1341
1363
  async function scaffoldServer(ctx) {
1342
- const baseDir = path5.join(ctx.rootDir, "packages", "server");
1343
- await ensureDir(path5.join(baseDir, "src"));
1344
- await ensureDir(path5.join(baseDir, "prisma"));
1364
+ const baseDir = import_node_path5.default.join(ctx.rootDir, "packages", "server");
1365
+ await ensureDir(import_node_path5.default.join(baseDir, "src"));
1366
+ await ensureDir(import_node_path5.default.join(baseDir, "prisma"));
1345
1367
  const pkgJson = {
1346
1368
  name: ctx.packageNames.server,
1347
1369
  version: "0.1.0",
@@ -1402,20 +1424,20 @@ async function scaffoldServer(ctx) {
1402
1424
  Dockerfile: dockerfile(ctx.packageNames.server, ctx.packageNames.contract)
1403
1425
  };
1404
1426
  for (const [name, contents] of Object.entries(files)) {
1405
- const fullPath = path5.join(baseDir, name);
1427
+ const fullPath = import_node_path5.default.join(baseDir, name);
1406
1428
  const writer = name === ".env" ? writeFileForce : writeFileIfMissing;
1407
1429
  const result = await writer(fullPath, contents);
1408
- const rel = path5.relative(ctx.rootDir, fullPath);
1430
+ const rel = import_node_path5.default.relative(ctx.rootDir, fullPath);
1409
1431
  if (result === "created" || result === void 0) log.created(rel);
1410
1432
  else log.skipped(rel);
1411
1433
  }
1412
1434
  }
1413
1435
 
1414
1436
  // src/utils/prompt.ts
1415
- import { createInterface } from "readline/promises";
1416
- import { stdin as input, stdout as output } from "process";
1437
+ var import_promises2 = require("readline/promises");
1438
+ var import_node_process = require("process");
1417
1439
  function createPrompt() {
1418
- const rl = createInterface({ input, output });
1440
+ const rl = (0, import_promises2.createInterface)({ input: import_node_process.stdin, output: import_node_process.stdout });
1419
1441
  return {
1420
1442
  async text(message, fallback) {
1421
1443
  const suffix = fallback ? ` (${fallback})` : "";
@@ -1466,7 +1488,7 @@ async function gatherContext() {
1466
1488
  const projectName = await prompt.text("Project name", "rrroutes-starter") ?? "rrroutes-starter";
1467
1489
  const appSlug = slugify(projectName);
1468
1490
  const targetDirInput = await prompt.text("Target directory", `./${appSlug}`) ?? `./${appSlug}`;
1469
- const rootDir = path6.resolve(process.cwd(), targetDirInput);
1491
+ const rootDir = import_node_path6.default.resolve(process.cwd(), targetDirInput);
1470
1492
  const scopeInput = await prompt.text("NPM scope (without @)", appSlug) ?? appSlug;
1471
1493
  const packageScope = slugify(scopeInput);
1472
1494
  const selection = await prompt.select("What should we create?", [
@@ -1521,4 +1543,4 @@ main().catch((err) => {
1521
1543
  log.error(err instanceof Error ? err.message : String(err));
1522
1544
  process.exit(1);
1523
1545
  });
1524
- //# sourceMappingURL=index.js.map
1546
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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/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 { 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 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 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 </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 </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 </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 },\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\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.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: 'ts-node --esm 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 },\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 },\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","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"]}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@emeryld/create-rrroutes",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "bin": {
7
- "create-rrroutes": "./dist/index.js"
7
+ "create-rrroutes": "./dist/index.cjs"
8
8
  },
9
9
  "files": [
10
10
  "dist"
package/dist/index.d.ts DELETED
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
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/strings.ts"],"sourcesContent":["#!/usr/bin/env node\nimport 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 { createPrompt } from './utils/prompt'\nimport { slugify } from './utils/strings'\nimport { log } from './utils/log'\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 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 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 </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 </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 </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 },\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\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.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: 'ts-node --esm 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 },\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 },\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","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":["path","path","path","path","path","path","path","path","path","idx","input","path"]}