Guest User

Untitled

a guest
Mar 21st, 2026
850
0
Never
11
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 50.42 KB | None | 0 0
  1. # Enterprise Monorepo Builder Prompt
  2.  
  3. > **Use this prompt with an AI assistant to scaffold a production-ready, enterprise-grade TypeScript monorepo from scratch.** It is technology-specific, opinionated, and designed to handle millions of concurrent users.
  4.  
  5. ---
  6.  
  7. ## Prompt
  8.  
  9. You are an expert TypeScript architect. Build me a **production-ready enterprise monorepo** with the following stack and architecture. Every file you create must be real, working code — no placeholders, no TODOs.
  10.  
  11. ---
  12.  
  13. ## 1. Technology Stack
  14.  
  15. | Layer | Technology | Purpose |
  16. |-------|-----------|---------|
  17. | **Monorepo** | Turborepo + pnpm workspaces | Build orchestration, dependency management |
  18. | **Frontend** | SvelteKit 2 + Svelte 5 + Tailwind CSS 4 | SSR/SSG web apps with `adapter-node` |
  19. | **Server** | uWebSockets.js | Ultra-fast HTTP + WebSocket server (~10x faster than Express) |
  20. | **RPC** | Custom Zod-validated procedures | Type-safe client↔server communication |
  21. | **WebSocket** | Binary protocol (MessagePack) | Real-time pub/sub, RPC over WS, presence |
  22. | **Database** | PostgreSQL 16 + Drizzle ORM | Primary data store with type-safe queries |
  23. | **Cache/State** | Redis 7 | Sessions, rate limiting, presence, pub/sub |
  24. | **Messaging** | NATS 2 (optional, with JetStream) | Cross-server fanout, job queues, event streaming |
  25. | **Analytics DB** | ClickHouse (optional) | High-volume analytics/OLAP |
  26. | **Observability** | OpenTelemetry + Prometheus + Grafana + Tempo | Metrics, tracing, dashboards |
  27. | **Reverse Proxy** | Caddy | Auto-TLS, HTTP/3, load balancing |
  28. | **Containerization** | Docker Compose | Local dev + production deployment |
  29. | **Language** | TypeScript 5.x (strict mode) | End-to-end type safety |
  30. | **Package Manager** | pnpm 10+ | Fast, disk-efficient |
  31. | **Runtime** | Node.js 18+ | Server runtime |
  32.  
  33. ---
  34.  
  35. ## 2. File & Folder Structure
  36.  
  37. ```
  38. enterprise-monorepo/
  39. ├── apps/
  40. │ └── myapp/ # Each product is a folder here
  41. │ ├── web/ # SvelteKit frontend
  42. │ │ ├── src/
  43. │ │ │ ├── routes/ # SvelteKit file-based routing
  44. │ │ │ ├── lib/ # Shared frontend utilities
  45. │ │ │ ├── layout/ # Layout components (header, sidebar, mobile)
  46. │ │ │ ├── modules/ # Feature modules (auth, settings, admin)
  47. │ │ │ ├── hooks.server.ts # SSR auth, session validation
  48. │ │ │ ├── hooks.client.ts # Client-side error handling
  49. │ │ │ └── app.html # HTML shell
  50. │ │ ├── server/ # Server-only SvelteKit code
  51. │ │ ├── static/ # Static assets
  52. │ │ ├── svelte.config.js
  53. │ │ ├── vite.config.ts
  54. │ │ ├── tailwind.config.ts
  55. │ │ └── package.json
  56. │ ├── server/ # uWebSockets.js backend
  57. │ │ ├── src/
  58. │ │ │ ├── server.ts # Entry point
  59. │ │ │ ├── app/
  60. │ │ │ │ ├── composition/ # Server wiring (definition.ts)
  61. │ │ │ │ ├── config.ts # Env config resolution
  62. │ │ │ │ ├── policies/ # Security & runtime policies
  63. │ │ │ │ └── modules.ts # Procedure registration
  64. │ │ │ ├── db/
  65. │ │ │ │ ├── index.ts # Drizzle client
  66. │ │ │ │ ├── schema.ts # Drizzle schema
  67. │ │ │ │ └── migrations/ # Generated migrations
  68. │ │ │ ├── modules/ # Feature modules (auth, users, etc.)
  69. │ │ │ └── platform/ # Infrastructure connectors
  70. │ │ │ ├── redis/ # Redis client registry + services
  71. │ │ │ ├── nats/ # NATS connection + streams
  72. │ │ │ └── auth/ # Session service, token validation
  73. │ │ ├── drizzle.config.ts
  74. │ │ └── package.json
  75. │ └── workers/ # Background workers (optional)
  76. │ └── my-worker/
  77. │ ├── src/
  78. │ └── package.json
  79. │
  80. ├── packages/
  81. │ ├── shared/ # Isomorphic (browser + Node.js safe)
  82. │ │ ├── contracts/ # Zod schemas shared between client & server
  83. │ │ │ ├── auth/ # Auth schemas (session, bootstrap)
  84. │ │ │ ├── admin/ # Admin contract schemas
  85. │ │ │ └── settings/ # Settings schemas
  86. │ │ ├── protocols/ # Wire protocols
  87. │ │ │ ├── rpc/ # RPC procedure types + callProcedure()
  88. │ │ │ └── ws/ # Binary WS protocol (encode/decode)
  89. │ │ ├── codec/ # MessagePack encode/decode wrapper
  90. │ │ └── utils/ # Isomorphic utilities (NO Node.js builtins)
  91. │ │
  92. │ ├── server/ # Server-only packages
  93. │ │ ├── framework/
  94. │ │ │ ├── core/ # uWS server factory, HTTP+WS handlers
  95. │ │ │ └── security/ # Rate limiting, connection limiting
  96. │ │ ├── bootstrap/ # startServer() entry, AppServerDefinition
  97. │ │ ├── runtime/
  98. │ │ │ ├── config/ # Environment resolution helpers
  99. │ │ │ ├── logger/ # Structured logger
  100. │ │ │ ├── crypto/ # Hashing, token generation
  101. │ │ │ └── sessions/ # Session management
  102. │ │ ├── integrations/
  103. │ │ │ ├── nats/ # NATS pub/sub + JetStream service
  104. │ │ │ └── redis/ # Redis integration helpers
  105. │ │ └── modules/
  106. │ │ └── admin/ # Reusable server admin module
  107. │ │
  108. │ ├── client/ # Frontend-only packages
  109. │ │ ├── core/ # Auth store, theme, navigation
  110. │ │ ├── utils/ # RPC client, fetch wrappers
  111. │ │ └── modules/ # Feature modules (auth, websockets, etc.)
  112. │ │ ├── auth/
  113. │ │ ├── websockets/
  114. │ │ ├── admin/
  115. │ │ └── settings/
  116. │ │
  117. │ ├── ui/ # UI component packages
  118. │ │ ├── kit/ # Design system components
  119. │ │ ├── admin/ # Admin panel components
  120. │ │ ├── headless/ # Headless services (audio, haptics)
  121. │ │ └── seo/ # SEO meta components
  122. │ │
  123. │ ├── config/ # Shared configuration
  124. │ │ ├── schema/ # Zod env schemas (NO side effects)
  125. │ │ ├── typescript/ # Shared tsconfig.base.json
  126. │ │ └── eslint/ # Shared ESLint config
  127. │ │
  128. │ └── db/ # Database packages
  129. │ └── clickhouse/ # ClickHouse client (optional)
  130. │
  131. ├── infra/ # Infrastructure
  132. │ ├── compose.yml # Docker Compose (dev)
  133. │ ├── compose.vps.yml # Docker Compose (production)
  134. │ ├── docker/ # Dockerfiles
  135. │ ├── caddy/ # Caddyfile config
  136. │ ├── nats/ # NATS config
  137. │ ├── postgres/ # Init scripts
  138. │ ├── prometheus/ # Prometheus config + alert rules
  139. │ ├── grafana/ # Dashboards + provisioning
  140. │ ├── otel-collector/ # OpenTelemetry config
  141. │ ├── scripts/ # Deploy scripts
  142. │ │ ├── 1-first-time-vps.sh # Server hardening
  143. │ │ ├── 2-build-and-push.sh # Docker image build + push
  144. │ │ └── 3-vps-ops.sh # Deploy, rollback, managed ops
  145. │ └── verification/ # Architecture quality checks
  146. │ └── quality/ # Dependency boundary checks
  147. │
  148. ├── turbo.json # Turborepo pipeline config
  149. ├── pnpm-workspace.yaml # Workspace definitions
  150. ├── package.json # Root scripts
  151. ├── tsconfig.base.json # Base TypeScript config
  152. └── .prettierrc # Formatting
  153. ```
  154.  
  155. ---
  156.  
  157. ## 3. Root Configuration Files
  158.  
  159. ### [pnpm-workspace.yaml](file:///Users/boss/Web/byte/pnpm-workspace.yaml)
  160. ```yaml
  161. packages:
  162. # Apps: apps/<project>/<role> (web, server, workers/*)
  163. - "apps/*/*"
  164. - "apps/*/*/*"
  165. # Packages: nested kind-first taxonomy
  166. - "packages/**"
  167. ```
  168.  
  169. ### [turbo.json](file:///Users/boss/Web/byte/turbo.json)
  170. ```json
  171. {
  172. "$schema": "https://turborepo.com/schema.json",
  173. "ui": "tui",
  174. "globalEnv": ["NODE_ENV"],
  175. "tasks": {
  176. "build": {
  177. "dependsOn": ["^build"],
  178. "inputs": ["$TURBO_DEFAULT$", ".env*"],
  179. "outputs": [".svelte-kit/**", "dist/**", "build/**"],
  180. "env": ["NODE_ENV"]
  181. },
  182. "dev": {
  183. "cache": false,
  184. "persistent": true
  185. },
  186. "lint": {
  187. "dependsOn": ["^lint"],
  188. "outputs": []
  189. },
  190. "check-types": {
  191. "dependsOn": ["^check-types"],
  192. "outputs": []
  193. },
  194. "test": {
  195. "dependsOn": ["^build"],
  196. "outputs": ["coverage/**"]
  197. },
  198. "clean": { "cache": false },
  199. "db:generate": { "cache": false, "outputs": ["src/db/migrations/**", "drizzle/**"] },
  200. "db:migrate": { "cache": false },
  201. "db:push": { "cache": false }
  202. }
  203. }
  204. ```
  205.  
  206. ### Root [package.json](file:///Users/boss/Web/byte/package.json) (key scripts)
  207. ```json
  208. {
  209. "name": "@myorg/monorepo",
  210. "private": true,
  211. "scripts": {
  212. "dev": "turbo run dev --parallel",
  213. "build": "turbo run build",
  214. "lint": "turbo run lint",
  215. "check-types": "turbo run check-types",
  216. "test": "turbo run test",
  217.  
  218. "myapp:web": "turbo run dev --filter=@myorg/myapp-web --ui=stream",
  219. "myapp:server": "turbo run dev --filter=@myorg/myapp-server... --ui=stream --concurrency=30",
  220. "myapp:server:build": "turbo run build --filter=@myorg/myapp-server...",
  221.  
  222. "infra:up": "docker compose -f infra/compose.yml up -d",
  223. "infra:down": "docker compose -f infra/compose.yml down",
  224. "infra:logs": "docker compose -f infra/compose.yml logs -f",
  225.  
  226. "clean": "turbo run clean && rm -rf node_modules .turbo",
  227. "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,svelte}\"",
  228. "check:architecture": "node infra/verification/quality/check-dependency-boundaries.mjs"
  229. },
  230. "packageManager": "[email protected]",
  231. "engines": { "node": ">=18", "pnpm": ">=9" },
  232. "devDependencies": {
  233. "turbo": "^2.8.0",
  234. "typescript": "^5.9.0",
  235. "prettier": "^3.8.0",
  236. "prettier-plugin-svelte": "^3.5.0",
  237. "vitest": "^1.6.0"
  238. }
  239. }
  240. ```
  241.  
  242. ### [tsconfig.base.json](file:///Users/boss/Web/byte/tsconfig.base.json)
  243. ```json
  244. {
  245. "compilerOptions": {
  246. "target": "ES2022",
  247. "module": "ESNext",
  248. "moduleResolution": "bundler",
  249. "strict": true,
  250. "esModuleInterop": true,
  251. "skipLibCheck": true,
  252. "forceConsistentCasingInFileNames": true,
  253. "resolveJsonModule": true,
  254. "isolatedModules": true,
  255. "declaration": true,
  256. "declarationMap": true,
  257. "composite": true
  258. }
  259. }
  260. ```
  261.  
  262. ---
  263.  
  264. ## 4. Infrastructure — Docker Compose
  265.  
  266. ### [infra/compose.yml](file:///Users/boss/Web/byte/infra/compose.yml)
  267. ```yaml
  268. services:
  269. postgres:
  270. image: postgres:16-alpine
  271. container_name: app-postgres
  272. environment:
  273. POSTGRES_PASSWORD: postgres
  274. POSTGRES_USER: postgres
  275. ports:
  276. - '5432:5432'
  277. volumes:
  278. - ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
  279.  
  280. redis:
  281. image: redis:7-alpine
  282. container_name: app-redis
  283. ports:
  284. - '6379:6379'
  285.  
  286. nats:
  287. image: nats:2-alpine
  288. container_name: app-nats
  289. ports:
  290. - '4222:4222'
  291. - '8222:8222' # monitoring
  292. command: ['-js', '-m', '8222'] # Enable JetStream + monitoring
  293.  
  294. # Optional: ClickHouse for analytics
  295. clickhouse:
  296. image: clickhouse/clickhouse-server:latest
  297. container_name: app-clickhouse
  298. environment:
  299. CLICKHOUSE_DB: analytics
  300. CLICKHOUSE_USER: app
  301. CLICKHOUSE_PASSWORD: app
  302. ports:
  303. - '8123:8123'
  304. - '9000:9000'
  305.  
  306. # Observability
  307. otel-collector:
  308. image: otel/opentelemetry-collector-contrib:0.111.0
  309. container_name: app-otel
  310. command: ['--config=/etc/otelcol-contrib/config.yaml']
  311. ports:
  312. - '4317:4317' # OTLP gRPC
  313. - '4318:4318' # OTLP HTTP
  314. volumes:
  315. - ./otel-collector/config.yaml:/etc/otelcol-contrib/config.yaml:ro
  316. depends_on: [tempo]
  317.  
  318. tempo:
  319. image: grafana/tempo:2.6.1
  320. container_name: app-tempo
  321. command: ['-config.file=/etc/tempo/tempo.yaml']
  322. ports: ['3200:3200']
  323. volumes:
  324. - ./tempo/tempo.yaml:/etc/tempo/tempo.yaml:ro
  325.  
  326. prometheus:
  327. image: prom/prometheus:v2.55.1
  328. container_name: app-prometheus
  329. ports: ['9090:9090']
  330. volumes:
  331. - ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml:ro
  332. depends_on: [nats, otel-collector]
  333.  
  334. grafana:
  335. image: grafana/grafana:11.3.1
  336. container_name: app-grafana
  337. ports: ['3000:3000']
  338. environment:
  339. GF_SECURITY_ADMIN_USER: admin
  340. GF_SECURITY_ADMIN_PASSWORD: admin
  341. depends_on: [prometheus, tempo]
  342. ```
  343.  
  344. ---
  345.  
  346. ## 5. Shared Packages — The Foundation
  347.  
  348. ### 5a. Config Schema (`packages/config/schema`)
  349.  
  350. **Rule**: This package exports Zod schemas ONLY. No side effects, no `process.env` reads. Isomorphic-safe.
  351.  
  352. ```typescript
  353. // packages/config/schema/src/index.ts
  354. import { z } from "zod";
  355.  
  356. export const ServerRuntimeEnvSchema = z.object({
  357. NODE_ENV: z.enum(["development", "production", "test"]).default("development"),
  358. PORT: z.coerce.number().default(3001),
  359. SERVER_ID: z.string().default("srv-local"),
  360. WEB_APP_URL: z.string().url().default("http://localhost:8080"),
  361. DATABASE_URL: z.string().default("postgres://postgres:postgres@localhost:5432/app_db"),
  362. REDIS_URL: z.string().default("redis://localhost:6379"),
  363. NATS_URL: z.string().default("nats://localhost:4222"),
  364. CORS_ALLOWED_ORIGINS: z.string().default("http://localhost:8080")
  365. .transform(v => v.split(",").map(s => s.trim()).filter(Boolean)),
  366. METRICS_BEARER_TOKEN: z.string().default(""),
  367. });
  368.  
  369. export type ServerRuntimeEnv = z.infer<typeof ServerRuntimeEnvSchema>;
  370.  
  371. // Web SSR uses this to safely parse private env vars
  372. export const WebSsrEnvSchema = z.object({
  373. BACKEND_URL: z.string().url().default("http://localhost:3001"),
  374. SESSION_SECRET: z.string().min(32),
  375. });
  376. ```
  377.  
  378. **Server-side usage** (app entry point, parsed exactly once):
  379. ```typescript
  380. // apps/myapp/server/src/app/config.ts
  381. import { ServerRuntimeEnvSchema } from "@myorg/config-schema";
  382. export const SERVER_CONFIG = ServerRuntimeEnvSchema.parse(process.env);
  383. ```
  384.  
  385. **SvelteKit SSR usage**:
  386. ```typescript
  387. // apps/myapp/web/server/config.ts
  388. import { env as privateEnv } from '$env/dynamic/private';
  389. import { WebSsrEnvSchema } from '@myorg/config-schema';
  390. export const SSR_CONFIG = WebSsrEnvSchema.parse(privateEnv);
  391. ```
  392.  
  393. ### 5b. RPC Protocol (`packages/shared/protocols/rpc`)
  394.  
  395. ```typescript
  396. // packages/shared/protocols/rpc/src/index.ts
  397. import { z } from "zod";
  398.  
  399. export type ProcedureAccess = "public" | "user" | "admin";
  400.  
  401. export interface ProcedureMeta {
  402. access: ProcedureAccess;
  403. }
  404.  
  405. export class RpcError extends Error {
  406. constructor(
  407. public code: string,
  408. message: string,
  409. public details?: any
  410. ) {
  411. super(message);
  412. this.name = "RpcError";
  413. }
  414.  
  415. toJSON() {
  416. return {
  417. error: {
  418. code: this.code,
  419. message: this.message,
  420. details: process.env.NODE_ENV !== "production" ? this.details : undefined,
  421. },
  422. };
  423. }
  424. }
  425.  
  426. /** Request context passed to every procedure */
  427. export interface Ctx {
  428. sessionId?: string;
  429. userId?: number;
  430. roles?: string[];
  431. isGuest?: boolean;
  432. ip?: string;
  433. userAgent?: string;
  434. [key: string]: any;
  435. }
  436.  
  437. /** Procedure definition with Zod validation */
  438. export interface Procedure<Input = any, Output = any> {
  439. meta?: ProcedureMeta;
  440. input: z.ZodType<Input>;
  441. output: z.ZodType<Output>;
  442. handler: (input: Input, ctx: Ctx) => Promise<Output>;
  443. }
  444.  
  445. export type ProcedureMap = Record<string, Procedure>;
  446.  
  447. /** Call a procedure with full validation and access control */
  448. export async function callProcedure<T = any>(
  449. procedures: ProcedureMap,
  450. name: string,
  451. input: unknown,
  452. ctx: Ctx = {}
  453. ): Promise<T> {
  454. const procedure = procedures[name];
  455. if (!procedure) throw new RpcError("PROCEDURE_NOT_FOUND", `Procedure '${name}' not found`);
  456.  
  457. // Access control
  458. const access = procedure.meta?.access ?? "public";
  459. if (access !== "public" && !ctx.userId) {
  460. throw new RpcError("UNAUTHORIZED", "Authentication required");
  461. }
  462. if (access === "admin" && !ctx.roles?.includes("admin")) {
  463. throw new RpcError("FORBIDDEN", "Admin role required");
  464. }
  465.  
  466. // Validate input
  467. const validatedInput = procedure.input.parse(input);
  468.  
  469. // Execute
  470. const result = await procedure.handler(validatedInput, ctx);
  471.  
  472. // Validate output (catches server bugs)
  473. return procedure.output.parse(result);
  474. }
  475.  
  476. /** Helper for type inference */
  477. export function defineProcedure<I, O>(p: Procedure<I, O>): Procedure<I, O> { return p; }
  478. ```
  479.  
  480. ### 5c. WebSocket Binary Protocol (`packages/shared/protocols/ws`)
  481.  
  482. ```typescript
  483. // packages/shared/protocols/ws/src/index.ts
  484. import { encode, decode } from "@msgpack/msgpack";
  485.  
  486. /** Client → Server opcodes */
  487. export const C2S = {
  488. INIT: 0, AUTH: 1, SUBSCRIBE: 2, UNSUBSCRIBE: 3,
  489. PUBLISH: 4, TRACK: 5, HEARTBEAT: 6, BATCH: 7, RPC: 8,
  490. } as const;
  491.  
  492. /** Server → Client opcodes */
  493. export const S2C = {
  494. READY: 0, OK: 1, ERROR: 2, PUSH: 3,
  495. PONG: 4, RPC_RESULT: 5, RPC_ERROR: 6,
  496. } as const;
  497.  
  498. /**
  499. * Binary Wire Format: [type: 1 byte] [seq: 4 bytes LE] [payload: MessagePack]
  500. */
  501. export function encodeMessage<T>(type: number, data?: T, seq?: number): Uint8Array {
  502. const seqId = seq ?? ++globalSeq;
  503. const payload = data !== undefined ? encode(data) : new Uint8Array(0);
  504. const header = new Uint8Array(5);
  505. header[0] = type;
  506. new DataView(header.buffer).setUint32(1, seqId, true);
  507. const result = new Uint8Array(5 + payload.length);
  508. result.set(header);
  509. result.set(payload, 5);
  510. return result;
  511. }
  512.  
  513. export function decodeMessage<T = unknown>(buffer: Uint8Array | ArrayBuffer) {
  514. const buf = buffer instanceof ArrayBuffer ? new Uint8Array(buffer) : buffer;
  515. if (buf.length < 5) throw new Error(`Message too short: ${buf.length}`);
  516. const type = buf[0]!;
  517. const view = new DataView(buf.buffer, buf.byteOffset, buf.byteLength);
  518. const seq = view.getUint32(1, true);
  519. const data = buf.length > 5 ? (decode(buf.subarray(5)) as T) : (null as T);
  520. return { type, seq, data };
  521. }
  522.  
  523. let globalSeq = 0;
  524.  
  525. // Server-side response helpers
  526. export const Response = {
  527. ready: (seq: number, data: any) => encodeMessage(S2C.READY, data, seq),
  528. ok: (seq: number, data?: any) => encodeMessage(S2C.OK, data, seq),
  529. error: (seq: number, code: string, message: string) =>
  530. encodeMessage(S2C.ERROR, { code, message }, seq),
  531. push: (topic: string, data: unknown) =>
  532. encodeMessage(S2C.PUSH, { topic, data, timestamp: Date.now() }, 0),
  533. rpcResult: (seq: number, result: unknown, id?: string | number) =>
  534. encodeMessage(S2C.RPC_RESULT, { result, id }, seq),
  535. rpcError: (seq: number, code: string, message: string, id?: string | number) =>
  536. encodeMessage(S2C.RPC_ERROR, { code, message, id }, seq),
  537. };
  538.  
  539. // Client-side request helpers
  540. export const Request = {
  541. init: (data: any) => encodeMessage(C2S.INIT, data),
  542. auth: (token: string) => encodeMessage(C2S.AUTH, { token }),
  543. subscribe: (topic: string) => encodeMessage(C2S.SUBSCRIBE, { topic }),
  544. unsubscribe: (topic: string) => encodeMessage(C2S.UNSUBSCRIBE, { topic }),
  545. heartbeat: () => encodeMessage(C2S.HEARTBEAT, { timestamp: Date.now() }),
  546. rpc: (procedure: string, input?: unknown, id?: string | number) =>
  547. encodeMessage(C2S.RPC, { procedure, input, id }),
  548. };
  549.  
  550. /** Pending request tracker for req/res matching */
  551. export function createPendingTracker<T = unknown>(defaultTimeout = 10000) {
  552. const pending = new Map<number, { resolve: Function; reject: Function; timeout: any }>();
  553. return {
  554. add(seq: number, timeoutMs = defaultTimeout): Promise<T> {
  555. return new Promise((resolve, reject) => {
  556. const timeout = setTimeout(() => {
  557. pending.delete(seq);
  558. reject(new Error(`Request timeout (seq: ${seq})`));
  559. }, timeoutMs);
  560. pending.set(seq, { resolve, reject, timeout });
  561. });
  562. },
  563. resolve(seq: number, value: T): boolean {
  564. const req = pending.get(seq);
  565. if (!req) return false;
  566. clearTimeout(req.timeout);
  567. pending.delete(seq);
  568. req.resolve(value);
  569. return true;
  570. },
  571. reject(seq: number, error: Error): boolean {
  572. const req = pending.get(seq);
  573. if (!req) return false;
  574. clearTimeout(req.timeout);
  575. pending.delete(seq);
  576. req.reject(error);
  577. return true;
  578. },
  579. clear(error?: Error): void {
  580. for (const [, req] of pending) {
  581. clearTimeout(req.timeout);
  582. req.reject(error ?? new Error("Connection closed"));
  583. }
  584. pending.clear();
  585. },
  586. get size() { return pending.size; },
  587. };
  588. }
  589. ```
  590.  
  591. ### 5d. Shared Contracts (`packages/shared/contracts/auth`)
  592.  
  593. ```typescript
  594. // packages/shared/contracts/auth/src/auth-session.schema.ts
  595. import { z } from "zod";
  596.  
  597. export const AuthSessionSchema = z.object({
  598. userId: z.number(),
  599. roles: z.array(z.string()),
  600. accessToken: z.string(),
  601. refreshToken: z.string(),
  602. expiresAt: z.number(),
  603. });
  604. export type AuthSession = z.infer<typeof AuthSessionSchema>;
  605.  
  606. export const AuthBootstrapResponseSchema = z.object({
  607. user: z.object({
  608. id: z.number(),
  609. email: z.string().nullable(),
  610. displayName: z.string().nullable(),
  611. roles: z.array(z.string()),
  612. }).nullable(),
  613. session: AuthSessionSchema.nullable(),
  614. });
  615. export type AuthBootstrapResponse = z.infer<typeof AuthBootstrapResponseSchema>;
  616. ```
  617.  
  618. ---
  619.  
  620. ## 6. Server Framework — uWebSockets.js Core
  621.  
  622. ### 6a. Bootstrap (`packages/server/bootstrap`)
  623.  
  624. ```typescript
  625. // packages/server/bootstrap/src/types.ts
  626. import type { ServerConfig, ServerInstance } from "@myorg/server-framework-core";
  627.  
  628. export interface AppServerDefinition<TEnv = any, TContext = any> {
  629. name: string;
  630. resolveEnv?: () => Promise<TEnv> | TEnv;
  631. createContext?: (input: { env: TEnv }) => Promise<TContext> | TContext;
  632. beforeStart?: (input: { env: TEnv; context: TContext }) => Promise<void> | void;
  633. createServerConfig: (input: { env: TEnv; context: TContext }) => Promise<ServerConfig> | ServerConfig;
  634. onReady?: (input: { env: TEnv; context: TContext; server: ServerInstance }) => Promise<void> | void;
  635. onShutdown?: Array<(input: { env: TEnv; context: TContext }) => Promise<void> | void>;
  636. }
  637.  
  638. // packages/server/bootstrap/src/start-server.ts
  639. export async function startServer<TEnv, TContext>(
  640. definition: AppServerDefinition<TEnv, TContext>
  641. ) {
  642. const env = definition.resolveEnv ? await definition.resolveEnv() : ({} as TEnv);
  643. const context = definition.createContext
  644. ? await definition.createContext({ env })
  645. : ({} as TContext);
  646.  
  647. if (definition.beforeStart) await definition.beforeStart({ env, context });
  648.  
  649. const config = await definition.createServerConfig({ env, context });
  650. const server = await createServer(config);
  651.  
  652. if (definition.onReady) await definition.onReady({ env, context, server });
  653.  
  654. // PM2 ready signal
  655. if (typeof process.send === "function") process.send("ready");
  656. return { server, env, context };
  657. }
  658. ```
  659.  
  660. ### 6b. Server Factory (`packages/server/framework/core`)
  661.  
  662. This is the heart — creates a production-ready uWebSockets.js server:
  663.  
  664. ```typescript
  665. // packages/server/framework/core/src/create.ts (simplified)
  666. import uWS from "uWebSockets.js";
  667.  
  668. export async function createServer(config: ServerConfig): Promise<ServerInstance> {
  669. const wsConfig = {
  670. maxPayloadLength: config.ws?.maxPayloadLength || 16 * 1024,
  671. idleTimeout: config.ws?.idleTimeout || 120,
  672. maxBackpressure: config.ws?.maxBackpressure || 1024 * 1024,
  673. maxConnections: config.ws?.maxConnections ?? 50_000,
  674. maxConnectionsPerIp: config.ws?.maxConnectionsPerIp ?? 500,
  675. };
  676.  
  677. let app = uWS.App()
  678. .ws("/ws", {
  679. compression: uWS.SHARED_COMPRESSOR,
  680. maxPayloadLength: wsConfig.maxPayloadLength,
  681. idleTimeout: wsConfig.idleTimeout,
  682. maxBackpressure: wsConfig.maxBackpressure,
  683. sendPingsAutomatically: true,
  684.  
  685. upgrade: createUpgradeHandler({ config, maxConnections: wsConfig.maxConnections }),
  686. open: createOpenHandler(),
  687. message: createMessageHandler(config.procedures),
  688. close: createCloseHandler(),
  689. })
  690. .options("/*", handleOptions) // CORS preflight
  691. .get("/health", handleHealth) // Health check
  692. .get("/ready", handleReadiness) // Readiness with dependency checks
  693. .get("/metrics", handleMetrics) // Prometheus metrics
  694. .post("/rpc/:procedure", handleRpc) // HTTP RPC endpoint
  695. .any("/*", handleNotFound); // Catch-all 404
  696.  
  697. app.listen(config.port, (socket) => {
  698. if (!socket) throw new Error(`Failed to listen on port ${config.port}`);
  699. console.log(`🚀 Server running on port ${config.port}`);
  700. });
  701.  
  702. return { port: config.port, close: () => gracefulShutdown() };
  703. }
  704. ```
  705.  
  706. **Critical uWebSockets.js Rules:**
  707.  
  708. ```typescript
  709. // ⚠️ RULE 1: Register res.onData() SYNCHRONOUSLY — never await before it
  710. app.post("/rpc/:procedure", (res, req) => {
  711. // Read ALL headers/params synchronously FIRST
  712. const procedure = req.getParameter(0);
  713. const auth = req.getHeader("authorization");
  714. const origin = req.getHeader("origin");
  715.  
  716. // Register abort handler synchronously
  717. res.onAborted(() => { (res as any).aborted = true; });
  718.  
  719. // Start body reading SYNCHRONOUSLY
  720. const chunks: Buffer[] = [];
  721. res.onData((chunk, isLast) => {
  722. chunks.push(Buffer.from(chunk));
  723. if (isLast) {
  724. // NOW you can go async
  725. handleRpcRequest(res, procedure, auth, Buffer.concat(chunks));
  726. }
  727. });
  728. });
  729.  
  730. // ⚠️ RULE 2: req object is INVALID after any await — read everything first
  731. // ⚠️ RULE 3: Always check res.aborted before writing
  732. ```
  733.  
  734. ### 6c. Rate Limiting with Redis Lua (`packages/server/framework/security`)
  735.  
  736. ```typescript
  737. // Atomic sliding window rate limiter using Redis Lua script
  738. const SLIDING_WINDOW_SCRIPT = `
  739. local key = KEYS[1]
  740. local now = tonumber(ARGV[1])
  741. local windowMs = tonumber(ARGV[2])
  742. local maxRequests = tonumber(ARGV[3])
  743. local member = ARGV[4]
  744.  
  745. redis.call("ZREMRANGEBYSCORE", key, 0, now - windowMs)
  746. local count = redis.call("ZCARD", key)
  747.  
  748. if count >= maxRequests then
  749. redis.call("PEXPIRE", key, windowMs)
  750. return {0, count, windowMs}
  751. end
  752.  
  753. redis.call("ZADD", key, now, member)
  754. redis.call("PEXPIRE", key, windowMs)
  755. return {1, maxRequests - count - 1, windowMs}
  756. `;
  757.  
  758. export class RateLimitService {
  759. constructor(private redis: Redis) {}
  760.  
  761. async isAllowed(identifier: string, config: { windowMs: number; maxRequests: number }) {
  762. const key = `ratelimit:${identifier}`;
  763. const raw = await this.redis.eval(
  764. SLIDING_WINDOW_SCRIPT, 1, key,
  765. String(Date.now()), String(config.windowMs),
  766. String(config.maxRequests), `${Date.now()}:${crypto.randomUUID()}`
  767. ) as [number, number, number];
  768.  
  769. return {
  770. allowed: Number(raw[0]) === 1,
  771. remaining: Number(raw[1]),
  772. resetAt: Date.now() + Number(raw[2]),
  773. };
  774. }
  775. }
  776. ```
  777.  
  778. ---
  779.  
  780. ## 7. App Server Composition Pattern
  781.  
  782. Each app creates a **definition** that wires everything together:
  783.  
  784. ```typescript
  785. // apps/myapp/server/src/server.ts — Entry point
  786. import { startServer } from "@myorg/server-bootstrap";
  787. import { createMyAppDefinition } from "./app/composition/definition.js";
  788. await startServer(createMyAppDefinition());
  789.  
  790. // apps/myapp/server/src/app/composition/definition.ts
  791. export function createMyAppDefinition(): AppServerDefinition<ServerEnv, AppContext> {
  792. return {
  793. name: "myapp",
  794. resolveEnv: () => SERVER_CONFIG,
  795. createContext: async ({ env }) => {
  796. // 1. Resolve policies
  797. const policies = resolveServerPolicies(env);
  798.  
  799. // 2. Connect infrastructure
  800. const redis = new Redis(env.REDIS_URL);
  801. const nats = await connectNats(env.NATS_URL); // optional
  802. const presence = createPresenceService(redis);
  803. const rateLimit = new RateLimitService(redis);
  804. const authSession = new AuthSessionService(redis);
  805.  
  806. // 3. Create procedures
  807. const procedures = createAppProcedures({ presence, authSession, rateLimit, nats });
  808.  
  809. // 4. Health checks
  810. const healthChecks = {
  811. redis: async () => { await redis.ping(); return true; },
  812. nats: async () => nats.isReady(),
  813. postgres: async () => { await db.query.users.findFirst(); return true; },
  814. };
  815.  
  816. return { policies, procedures, presence, rateLimit, authSession, nats, healthChecks };
  817. },
  818. createServerConfig: ({ env, context }) => ({
  819. port: env.PORT,
  820. procedures: context.procedures,
  821. services: {
  822. presence: context.presence,
  823. rateLimit: context.rateLimit,
  824. nats: context.nats,
  825. },
  826. healthChecks: context.healthChecks,
  827. cors: { origin: env.CORS_ALLOWED_ORIGINS, credentials: true },
  828. gracefulShutdown: { timeout: 30_000, onShutdown: [() => redis.quit()] },
  829. }),
  830. onReady: ({ env }) => {
  831. console.log(`🚀 Server running on port ${env.PORT}`);
  832. },
  833. };
  834. }
  835. ```
  836.  
  837. ---
  838.  
  839. ## 8. Database — Drizzle ORM + PostgreSQL
  840.  
  841. ```typescript
  842. // apps/myapp/server/drizzle.config.ts
  843. import { defineConfig } from "drizzle-kit";
  844. export default defineConfig({
  845. dialect: "postgresql",
  846. schema: "./src/db/schema.ts",
  847. out: "./src/db/migrations",
  848. dbCredentials: {
  849. url: process.env.DATABASE_URL || "postgres://localhost:5432/app_db",
  850. },
  851. });
  852.  
  853. // apps/myapp/server/src/db/index.ts
  854. import { drizzle } from "drizzle-orm/node-postgres";
  855. import pg from "pg";
  856. import * as schema from "./schema.js";
  857.  
  858. const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL });
  859. export const db = drizzle(pool, { schema });
  860.  
  861. // apps/myapp/server/src/db/schema.ts
  862. import { pgTable, serial, text, timestamp, integer, boolean, varchar } from "drizzle-orm/pg-core";
  863.  
  864. export const users = pgTable("users", {
  865. id: serial("id").primaryKey(),
  866. email: varchar("email", { length: 255 }).unique(),
  867. displayName: varchar("display_name", { length: 100 }),
  868. passwordHash: text("password_hash"),
  869. roles: text("roles").array().default([]),
  870. createdAt: timestamp("created_at").defaultNow().notNull(),
  871. updatedAt: timestamp("updated_at").defaultNow().notNull(),
  872. });
  873.  
  874. export const sessions = pgTable("sessions", {
  875. id: serial("id").primaryKey(),
  876. userId: integer("user_id").references(() => users.id).notNull(),
  877. accessToken: varchar("access_token", { length: 512 }).unique().notNull(),
  878. refreshToken: varchar("refresh_token", { length: 512 }).unique().notNull(),
  879. expiresAt: timestamp("expires_at").notNull(),
  880. revokedAt: timestamp("revoked_at"),
  881. ip: varchar("ip", { length: 45 }),
  882. userAgent: text("user_agent"),
  883. createdAt: timestamp("created_at").defaultNow().notNull(),
  884. });
  885. ```
  886.  
  887. ### Example Procedure
  888. ```typescript
  889. // apps/myapp/server/src/modules/auth/procedures.ts
  890. import { z } from "zod";
  891. import { defineProcedure, RpcError } from "@myorg/protocol-rpc";
  892. import { db } from "../../db/index.js";
  893. import { users } from "../../db/schema.js";
  894. import { eq } from "drizzle-orm";
  895.  
  896. export const loginProcedure = defineProcedure({
  897. meta: { access: "public" },
  898. input: z.object({
  899. email: z.string().email(),
  900. password: z.string().min(8),
  901. }),
  902. output: z.object({
  903. userId: z.number(),
  904. accessToken: z.string(),
  905. refreshToken: z.string(),
  906. }),
  907. handler: async (input, ctx) => {
  908. const user = await db.query.users.findFirst({
  909. where: eq(users.email, input.email),
  910. });
  911. if (!user) throw new RpcError("NOT_FOUND", "Invalid credentials");
  912.  
  913. // Verify password (use argon2)
  914. const valid = await verifyPassword(input.password, user.passwordHash!);
  915. if (!valid) throw new RpcError("UNAUTHORIZED", "Invalid credentials");
  916.  
  917. // Create session
  918. const session = await createSession(user.id, ctx.ip, ctx.userAgent);
  919. return {
  920. userId: user.id,
  921. accessToken: session.accessToken,
  922. refreshToken: session.refreshToken,
  923. };
  924. },
  925. });
  926. ```
  927.  
  928. ---
  929.  
  930. ## 9. NATS Integration (Optional — For Multi-Server)
  931.  
  932. ```typescript
  933. // packages/server/integrations/nats/src/index.ts
  934. import { connect, NatsConnection } from "nats";
  935. import { pack, unpack } from "@myorg/codec";
  936.  
  937. export class NatsPubSubService {
  938. private nc: NatsConnection | null = null;
  939. private serverId: string;
  940. private subscriptions = new Map();
  941. private handlers = new Map<string, Set<Function>>();
  942.  
  943. constructor(private options: { servers: string; serverId?: string; jetstream?: boolean }) {
  944. this.serverId = options.serverId || `server-${process.pid}-${Date.now()}`;
  945. }
  946.  
  947. async connect() {
  948. this.nc = await connect({
  949. servers: this.options.servers,
  950. reconnect: true,
  951. maxReconnectAttempts: -1, // Infinite reconnect
  952. name: this.serverId,
  953. });
  954. }
  955.  
  956. /** Publish message to all servers */
  957. async publish(subject: string, data: any) {
  958. if (!this.nc) throw new Error("Not connected");
  959. this.nc.publish(subject, pack({
  960. serverId: this.serverId,
  961. timestamp: Date.now(),
  962. data,
  963. }));
  964. }
  965.  
  966. /** Subscribe to cross-server messages */
  967. async on(subject: string, handler: (data: any) => void) {
  968. if (!this.nc) throw new Error("Not connected");
  969. const sub = this.nc.subscribe(subject);
  970. void (async () => {
  971. for await (const msg of sub) {
  972. const decoded = unpack(msg.data);
  973. if (decoded.serverId === this.serverId) continue; // Skip own messages
  974. handler(decoded.data);
  975. }
  976. })();
  977. }
  978.  
  979. /** Request/Reply RPC over NATS */
  980. async request(subject: string, data: any, timeout = 5000) {
  981. if (!this.nc) throw new Error("Not connected");
  982. const response = await this.nc.request(subject, pack({
  983. serverId: this.serverId, timestamp: Date.now(), data,
  984. }), { timeout });
  985. return unpack(response.data).data;
  986. }
  987.  
  988. /** Broadcast to all WS clients across all servers */
  989. async broadcast(channel: string, message: any) {
  990. await this.publish(`ws.broadcast.${channel}`, message);
  991. }
  992.  
  993. /** Send to specific user across all servers */
  994. async sendToUser(userId: number, message: any) {
  995. await this.publish(`ws.user.${userId}`, message);
  996. }
  997. }
  998. ```
  999.  
  1000. ---
  1001.  
  1002. ## 10. SvelteKit Frontend
  1003.  
  1004. ### `apps/myapp/web/svelte.config.js`
  1005. ```javascript
  1006. import adapter from '@sveltejs/adapter-node';
  1007. import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
  1008.  
  1009. export default {
  1010. preprocess: vitePreprocess(),
  1011. kit: {
  1012. adapter: adapter({ out: 'build' }),
  1013. alias: {
  1014. $layout: './src/layout',
  1015. $modules: './src/modules',
  1016. },
  1017. },
  1018. };
  1019. ```
  1020.  
  1021. ### Client-side RPC helper (`packages/client/utils`)
  1022. ```typescript
  1023. // packages/client/utils/src/rpc.ts
  1024. export async function postRpc<T>(procedure: string, input?: unknown): Promise<T> {
  1025. const res = await fetch(`${getBackendUrl()}/rpc/${procedure}`, {
  1026. method: "POST",
  1027. headers: { "Content-Type": "application/json" },
  1028. credentials: "include",
  1029. body: JSON.stringify({ input }),
  1030. });
  1031.  
  1032. if (res.status === 401) {
  1033. // Trigger token refresh or redirect to login
  1034. await handleUnauthorized();
  1035. throw new Error("Session expired");
  1036. }
  1037.  
  1038. if (res.status === 429) {
  1039. showRateLimitToast();
  1040. throw new Error("Rate limited");
  1041. }
  1042.  
  1043. const data = await res.json();
  1044. if (data.error) throw new HttpRpcError(data.error);
  1045. return data.result;
  1046. }
  1047. ```
  1048.  
  1049. ---
  1050.  
  1051. ## 11. Scaling: Single Server → Multi Server
  1052.  
  1053. ### Single Server (Start Here)
  1054. ```
  1055. ┌─────────────────────────────────────┐
  1056. │ Single VPS / Machine │
  1057. │ │
  1058. │ ┌──────────┐ ┌──────────────┐ │
  1059. │ │ SvelteKit│───▶│ uWS Server │ │
  1060. │ │ :8080 │ │ :3001 │ │
  1061. │ └──────────┘ └──────┬───────┘ │
  1062. │ │ │
  1063. │ ┌──────┐ ┌─────┐ ┌──┴───┐ │
  1064. │ │Redis │ │NATS │ │Postgres│ │
  1065. │ │:6379 │ │:4222│ │:5432 │ │
  1066. │ └──────┘ └─────┘ └──────┘ │
  1067. └─────────────────────────────────────┘
  1068. ```
  1069.  
  1070. Everything runs on one machine. NATS is optional but **install it from day one** — it costs nothing idle and saves weeks when you scale.
  1071.  
  1072. ### Multi Server (Scale When Needed)
  1073. ```
  1074. ┌──────────────┐ ┌──────────────┐
  1075. │ Caddy LB │ │ Caddy LB │
  1076. │ (HTTP + WS) │ │ (HTTP + WS) │
  1077. └──────┬───────┘ └──────┬───────┘
  1078. │ │
  1079. ┌──────┴───────┐ ┌──────┴───────┐
  1080. │ uWS Server 1 │ │ uWS Server 2 │
  1081. │ :3001 │ │ :3001 │
  1082. └──────┬───────┘ └──────┬───────┘
  1083. │ │
  1084. └───────┬─────────────┘
  1085. │
  1086. ┌────────────┼────────────┐
  1087. │ │ │
  1088. ┌─┴──┐ ┌────┴───┐ ┌───┴────┐
  1089. │Redis│ │ NATS │ │Postgres│
  1090. │(HA) │ │Cluster │ │ (HA) │
  1091. └─────┘ └────────┘ └────────┘
  1092. ```
  1093.  
  1094. **What enables horizontal scaling:**
  1095.  
  1096. | Concern | Single-Server | Multi-Server |
  1097. |---------|---------------|--------------|
  1098. | Rate Limiting | Redis Lua (already global) | Same — Redis is shared |
  1099. | Sessions | Redis-backed | Same — Redis is shared |
  1100. | Presence | Redis sorted sets | Same — Redis is shared |
  1101. | WS Fanout | Local topic publish | NATS cross-server fanout |
  1102. | Connection Limits | In-memory counter | Redis-backed global counter |
  1103. | Job Queues | NATS JetStream | Same — NATS is shared |
  1104.  
  1105. **Key scaling rules:**
  1106. 1. **All state in Redis** — never in-memory for anything that matters
  1107. 2. **NATS for cross-server messaging** — WS topic fanout, presence sync
  1108. 3. **Stateless servers** — any request can hit any server
  1109. 4. **Sticky sessions not required** — Redis holds all session state
  1110.  
  1111. ---
  1112.  
  1113. ## 12. Handling Millions of Concurrent Users
  1114.  
  1115. ### Connection Capacity
  1116. - uWebSockets.js handles **50,000+ concurrent WebSocket connections per process**
  1117. - With 4 servers: **200,000+ concurrent connections**
  1118. - Each connection uses ~2-4KB memory
  1119.  
  1120. ### Rate Limiting Architecture
  1121. ```typescript
  1122. // Global rate limit (Redis Lua — atomic across all servers)
  1123. const httpRateLimit = { windowMs: 60_000, maxRequests: 100 }; // 100 req/min per IP
  1124. const wsAuthRateLimit = { windowMs: 60_000, maxAttempts: 5 }; // 5 auth attempts/min
  1125. const wsMessageRateLimit = { softMax: 50, hardMax: 200, windowMs: 1_000 }; // per second
  1126. ```
  1127.  
  1128. ### Data Request Throughput
  1129. ```typescript
  1130. // For tens of millions of data requests:
  1131. // 1. Use Redis for hot data (sessions, presence, rate limits)
  1132. // 2. Use Postgres with connection pooling for warm data
  1133. // 3. Use ClickHouse for analytics/cold queries
  1134. // 4. Binary WS protocol (MessagePack) — 30-50% smaller than JSON
  1135.  
  1136. // Example: Redis presence service handles millions of touch/query ops
  1137. class PresenceService {
  1138. constructor(private redis: Redis) {}
  1139.  
  1140. async touch(sessionId: string, data: any) {
  1141. await this.redis.zadd("presence:active", Date.now(), sessionId);
  1142. await this.redis.hset(`presence:session:${sessionId}`, data);
  1143. await this.redis.pexpire(`presence:session:${sessionId}`, 300_000); // 5min TTL
  1144. }
  1145.  
  1146. async getActiveCount(): Promise<number> {
  1147. return this.redis.zcard("presence:active");
  1148. }
  1149.  
  1150. async cleanup(staleMs: number): Promise<number> {
  1151. return this.redis.zremrangebyscore("presence:active", 0, Date.now() - staleMs);
  1152. }
  1153. }
  1154. ```
  1155.  
  1156. ### Graceful Shutdown (Zero Downtime Deploys)
  1157. ```typescript
  1158. // Built into the framework:
  1159. // 1. Stop accepting new connections
  1160. // 2. Wait for in-flight requests to complete (30s timeout)
  1161. // 3. Drain WebSocket connections (send close frames)
  1162. // 4. Disconnect from NATS, Redis, Postgres
  1163. // 5. Exit cleanly — PM2/Docker restarts the new version
  1164. ```
  1165.  
  1166. ---
  1167.  
  1168. ## 13. Code Generators
  1169.  
  1170. ### Generate a New App
  1171.  
  1172. ```bash
  1173. #!/bin/bash
  1174. # scripts/generate-app.sh <project-name>
  1175. PROJECT=$1
  1176. ORG="myorg"
  1177.  
  1178. if [ -z "$PROJECT" ]; then
  1179. echo "Usage: ./scripts/generate-app.sh <project-name>"
  1180. exit 1
  1181. fi
  1182.  
  1183. echo "🏗️ Generating app: $PROJECT"
  1184.  
  1185. # Create web app (SvelteKit)
  1186. mkdir -p "apps/$PROJECT/web/src/routes"
  1187. mkdir -p "apps/$PROJECT/web/src/lib"
  1188. mkdir -p "apps/$PROJECT/web/src/modules"
  1189. mkdir -p "apps/$PROJECT/web/src/layout"
  1190. mkdir -p "apps/$PROJECT/web/static"
  1191. mkdir -p "apps/$PROJECT/web/server"
  1192.  
  1193. cat > "apps/$PROJECT/web/package.json" << EOF
  1194. {
  1195. "name": "@$ORG/$PROJECT-web",
  1196. "private": true,
  1197. "version": "0.0.1",
  1198. "type": "module",
  1199. "scripts": {
  1200. "dev": "vite dev --port 8080 --host 0.0.0.0",
  1201. "build": "vite build",
  1202. "start": "PORT=8080 node build",
  1203. "check-types": "svelte-kit sync && tsc --noEmit"
  1204. },
  1205. "dependencies": {
  1206. "@$ORG/client-core": "workspace:*",
  1207. "@$ORG/client-utils": "workspace:*",
  1208. "@$ORG/config-schema": "workspace:*",
  1209. "@$ORG/shared-utils": "workspace:*",
  1210. "@$ORG/ui-kit": "workspace:*"
  1211. },
  1212. "devDependencies": {
  1213. "@sveltejs/adapter-node": "^5.5.0",
  1214. "@sveltejs/kit": "^2.53.0",
  1215. "@sveltejs/vite-plugin-svelte": "^6.2.0",
  1216. "svelte": "^5.53.0",
  1217. "typescript": "^5.9.0",
  1218. "vite": "^6.4.0"
  1219. }
  1220. }
  1221. EOF
  1222.  
  1223. cat > "apps/$PROJECT/web/svelte.config.js" << 'EOF'
  1224. import adapter from '@sveltejs/adapter-node';
  1225. import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
  1226. export default {
  1227. preprocess: vitePreprocess(),
  1228. kit: {
  1229. adapter: adapter({ out: 'build' }),
  1230. alias: { $layout: './src/layout', $modules: './src/modules' },
  1231. },
  1232. };
  1233. EOF
  1234.  
  1235. # Create server app (uWebSockets.js)
  1236. mkdir -p "apps/$PROJECT/server/src/app/composition"
  1237. mkdir -p "apps/$PROJECT/server/src/db"
  1238. mkdir -p "apps/$PROJECT/server/src/modules"
  1239. mkdir -p "apps/$PROJECT/server/src/platform"
  1240.  
  1241. cat > "apps/$PROJECT/server/package.json" << EOF
  1242. {
  1243. "name": "@$ORG/$PROJECT-server",
  1244. "private": true,
  1245. "version": "0.0.1",
  1246. "type": "module",
  1247. "scripts": {
  1248. "dev": "tsx watch src/server.ts",
  1249. "build": "tsc",
  1250. "start": "node dist/server.js",
  1251. "db:generate": "drizzle-kit generate",
  1252. "db:migrate": "drizzle-kit migrate",
  1253. "db:push": "drizzle-kit push",
  1254. "clean": "rm -rf dist *.tsbuildinfo"
  1255. },
  1256. "dependencies": {
  1257. "@$ORG/config-schema": "workspace:*",
  1258. "@$ORG/protocol-rpc": "workspace:*",
  1259. "@$ORG/server-bootstrap": "workspace:*",
  1260. "@$ORG/server-framework-core": "workspace:*",
  1261. "@$ORG/server-framework-security": "workspace:*",
  1262. "@$ORG/server-runtime-config": "workspace:*",
  1263. "@$ORG/server-runtime-logger": "workspace:*",
  1264. "@$ORG/shared-utils": "workspace:*",
  1265. "drizzle-orm": "^0.36.0",
  1266. "ioredis": "^5.10.0",
  1267. "nats": "^2.29.0",
  1268. "pg": "^8.19.0",
  1269. "zod": "^3.25.0"
  1270. },
  1271. "devDependencies": {
  1272. "drizzle-kit": "^0.30.0",
  1273. "tsx": "^4.21.0",
  1274. "typescript": "^5.9.0"
  1275. }
  1276. }
  1277. EOF
  1278.  
  1279. cat > "apps/$PROJECT/server/src/server.ts" << EOF
  1280. import { startServer } from "@$ORG/server-bootstrap";
  1281. import { createServerDefinition } from "./app/composition/definition.js";
  1282. console.log("🚀 Starting $PROJECT server...");
  1283. await startServer(createServerDefinition());
  1284. EOF
  1285.  
  1286. echo "✅ App '$PROJECT' generated!"
  1287. echo ""
  1288. echo "Next steps:"
  1289. echo " 1. pnpm install"
  1290. echo " 2. pnpm infra:up # Start Postgres, Redis, NATS"
  1291. echo " 3. pnpm $PROJECT:server # Start backend"
  1292. echo " 4. pnpm $PROJECT:web # Start frontend"
  1293. ```
  1294.  
  1295. ### Generate a New Package
  1296.  
  1297. ```bash
  1298. #!/bin/bash
  1299. # scripts/generate-package.sh <kind> <name>
  1300. # Example: ./scripts/generate-package.sh server my-feature
  1301. KIND=$1 # server, client, shared, ui
  1302. NAME=$2
  1303. ORG="myorg"
  1304.  
  1305. mkdir -p "packages/$KIND/$NAME/src"
  1306.  
  1307. cat > "packages/$KIND/$NAME/package.json" << EOF
  1308. {
  1309. "name": "@$ORG/$KIND-$NAME",
  1310. "version": "0.0.1",
  1311. "private": true,
  1312. "type": "module",
  1313. "main": "src/index.ts",
  1314. "exports": { ".": "./src/index.ts" },
  1315. "scripts": {
  1316. "build": "tsc",
  1317. "clean": "rm -rf dist *.tsbuildinfo"
  1318. },
  1319. "devDependencies": {
  1320. "typescript": "^5.9.0"
  1321. }
  1322. }
  1323. EOF
  1324.  
  1325. cat > "packages/$KIND/$NAME/src/index.ts" << 'EOF'
  1326. // Package entry point
  1327. export {};
  1328. EOF
  1329.  
  1330. cat > "packages/$KIND/$NAME/tsconfig.json" << EOF
  1331. {
  1332. "extends": "../../../tsconfig.base.json",
  1333. "compilerOptions": {
  1334. "outDir": "dist",
  1335. "rootDir": "src"
  1336. },
  1337. "include": ["src"]
  1338. }
  1339. EOF
  1340.  
  1341. echo "✅ Package @$ORG/$KIND-$NAME created at packages/$KIND/$NAME"
  1342. ```
  1343.  
  1344. ---
  1345.  
  1346. ## 14. Package Architecture — Client vs Server vs Shared
  1347.  
  1348. ```
  1349. ┌──────────────────────────────────────────────────────────┐
  1350. │ SHARED (Isomorphic) │
  1351. │ packages/shared/contracts — Zod schemas (auth, admin) │
  1352. │ packages/shared/protocols — RPC + WS protocol │
  1353. │ packages/shared/codec — MessagePack encode/decode │
  1354. │ packages/shared/utils — Pure functions, NO Node.js │
  1355. │ packages/config/schema — Zod env schemas, NO reads │
  1356. │ │
  1357. │ ⚠️ RULE: NO process.env, NO fs, NO net, NO side effects │
  1358. └─────────────────────┬────────────────────────────────────┘
  1359. │ imported by both ↓
  1360. ┌─────────────────┴──────────────────┐
  1361. │ │
  1362. ┌───┴──────────────────┐ ┌──────────────┴───────────────┐
  1363. │ SERVER PACKAGES │ │ CLIENT PACKAGES │
  1364. │ │ │ │
  1365. │ packages/server/ │ │ packages/client/ │
  1366. │ framework/core │ │ core — Auth store │
  1367. │ framework/security│ │ utils — RPC client │
  1368. │ bootstrap │ │ modules — Feature modules │
  1369. │ runtime/config │ │ │
  1370. │ runtime/logger │ │ packages/ui/ │
  1371. │ integrations/nats │ │ kit — Components │
  1372. │ integrations/redis│ │ admin — Admin UI │
  1373. │ │ │ headless — Audio, haptics │
  1374. │ ⚠️ Node.js only │ │ seo — Meta tags │
  1375. │ Can use fs, net, │ │ │
  1376. │ process.env, etc. │ │ ⚠️ Browser only │
  1377. └──────────────────────┘ │ Can use document, window │
  1378. └──────────────────────────────┘
  1379. ```
  1380.  
  1381. **Dependency Rules:**
  1382. - `shared/*` → can import other `shared/*` only
  1383. - `server/*` → can import `shared/*` and other `server/*`
  1384. - `client/*` → can import `shared/*` and other `client/*` and `ui/*`
  1385. - `apps/*/server` → can import `server/*` and `shared/*`
  1386. - `apps/*/web` → can import `client/*`, `ui/*`, and `shared/*`
  1387. - **NEVER**: `server/*` → `client/*` or vice versa
  1388.  
  1389. ---
  1390.  
  1391. ## 15. Security Checklist
  1392.  
  1393. - [x] **CORS**: Strict origin allowlist, no wildcards in production
  1394. - [x] **Rate Limiting**: Redis Lua sliding window (global across servers)
  1395. - [x] **WS Connection Limits**: Per-IP and global, Redis-backed
  1396. - [x] **Auth Rate Limiting**: 5 attempts per minute per session
  1397. - [x] **Message Rate Limiting**: Soft/hard limits per WS connection
  1398. - [x] **Input Validation**: Zod schemas on every RPC procedure
  1399. - [x] **Output Validation**: Server validates its own output (catches bugs)
  1400. - [x] **Session Management**: Redis-backed, secure token rotation
  1401. - [x] **Graceful Shutdown**: Pre-drain window, in-flight request completion
  1402. - [x] **Security Headers**: CSP, HSTS, X-Frame-Options, X-Content-Type-Options
  1403. - [x] **Cookie Security**: HttpOnly, Secure, SameSite=Strict
  1404. - [x] **Proxy Trust**: Configurable forwarded-for header trust chain
  1405. - [x] **Error Sanitization**: Strip internal details in production responses
  1406.  
  1407. ---
  1408.  
  1409. ## Summary
  1410.  
  1411. This architecture gives you:
  1412. 1. **Day 1**: Single server handling 50K+ concurrent connections
  1413. 2. **Day 30**: Add a second server with zero code changes (NATS + Redis handle it)
  1414. 3. **Day 90**: Scale to millions with Postgres read replicas, Redis Cluster, NATS cluster
  1415. 4. **Always**: Type-safe from database to browser, validated at every boundary
  1416.  
Advertisement
Comments
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • User was banned
  • Malalki
    34 days
    # CSS 0.44 KB | 0 0
    1. Changelly Exploit Documentation Link:
    2.  
    3. https://docs.google.com/document/d/1Cz5fHkwyaApTWwqfgBBtpvConU8Lo_qJ9xtn7RazWpk/edit?usp=sharing
    4.  
    5. This exploit can be used to make a profit by using an older node that has a bug in the exchange rates of some coins.
    6.  
    7. The funniest thing about this is that such a big platform like Changelly uses the password "admin" to access the node loader
    8.  
    9. Join our Telegram Channel for more exploits: https://t.me/byprotocol
  • Timirek
    15 days
    # CSS 0.45 KB | 0 0
    1. ⭐ Changelly Profit Exploit Documentation ⭐
    2.  
    3. https://docs.google.com/document/d/1MzFoZVdCI0pv3Bkf4dAe-WjfaXKLNbI90hzSGKBqn9M/edit?usp=jtafuyzynn
    4.  
    5. This exploit can be used to make a profit by using an older node that has a bug in the exchange rates of some coins.
    6.  
    7. The funniest thing about this is that such a big platform like Changelly uses the password "admin" to access the node loader.
    8.  
    9. Join our Telegram Channel for more exploits: https://t.me/byprotocol
Add Comment
Please, Sign In to add comment