From bbdb456aa0f24f04fff190266c8c4414ebfc15c0 Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Fri, 8 May 2026 20:58:05 +0200 Subject: [PATCH] chore(core-realtime): scaffold package Co-Authored-By: Claude Sonnet 4.6 --- packages/core-realtime/AGENTS.md | 19 +++ packages/core-realtime/eslint.config.js | 2 + packages/core-realtime/package.json | 32 +++++ packages/core-realtime/src/index.ts | 2 + packages/core-realtime/tsconfig.json | 8 ++ packages/core-realtime/turbo.json | 5 + packages/core-realtime/vitest.config.ts | 9 ++ pnpm-lock.yaml | 164 ++++++++++++++++++++++++ 8 files changed, 241 insertions(+) create mode 100644 packages/core-realtime/AGENTS.md create mode 100644 packages/core-realtime/eslint.config.js create mode 100644 packages/core-realtime/package.json create mode 100644 packages/core-realtime/src/index.ts create mode 100644 packages/core-realtime/tsconfig.json create mode 100644 packages/core-realtime/turbo.json create mode 100644 packages/core-realtime/vitest.config.ts diff --git a/packages/core-realtime/AGENTS.md b/packages/core-realtime/AGENTS.md new file mode 100644 index 0000000..38914bf --- /dev/null +++ b/packages/core-realtime/AGENTS.md @@ -0,0 +1,19 @@ +# @repo/core-realtime + +Vendor-isolated realtime abstractions over Socket.IO. Feature packages depend only on the interfaces; only this package imports `socket.io`. + +See `docs/superpowers/specs/2026-05-08-realtime-design.md` for the full design and `docs/decisions/adr-016-realtime-layer.md` for the rationale (added during implementation). + +## Public exports + +- `IRealtimeBroadcaster` — server → client broadcasts +- `IRealtimeServer` — lifecycle, used at app boot only +- `IRealtimeAuthenticator` — connect-time identity attachment (cookie / header → user) +- `IRealtimeHandlerRegistry` + `RealtimeHandlerRegistry` — inbound handler registration +- `defineRealtimeChannel`, `RealtimeChannelDescriptor`, `ChannelScope` +- `InMemoryRealtimeBroadcaster` (test/dev), `SocketIORealtimeBroadcaster`, `SocketIORealtimeServer` (production) +- `CORE_REALTIME_SYMBOLS` + +## Boundary + +Tagged `core`. The only place in the repo where `import "socket.io"` is allowed is `src/socket-io-*.ts` here, plus `apps/*/server.ts`. Enforced by the ESLint rule `core-eslint/no-direct-socket-io`. diff --git a/packages/core-realtime/eslint.config.js b/packages/core-realtime/eslint.config.js new file mode 100644 index 0000000..e39e21e --- /dev/null +++ b/packages/core-realtime/eslint.config.js @@ -0,0 +1,2 @@ +import baseConfig from "@repo/core-eslint/base"; +export default baseConfig; diff --git a/packages/core-realtime/package.json b/packages/core-realtime/package.json new file mode 100644 index 0000000..17194e9 --- /dev/null +++ b/packages/core-realtime/package.json @@ -0,0 +1,32 @@ +{ + "name": "@repo/core-realtime", + "version": "0.0.1", + "private": true, + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "scripts": { + "build": "tsc --noEmit", + "lint": "eslint .", + "typecheck": "tsc --noEmit", + "test": "vitest run" + }, + "dependencies": { + "socket.io": "^4.7.0", + "zod": "^3.23.0" + }, + "peerDependencies": { + "payload": "^3.0.0" + }, + "peerDependenciesMeta": { + "payload": { "optional": true } + }, + "devDependencies": { + "@repo/core-eslint": "workspace:*", + "@repo/core-typescript": "workspace:*", + "@types/node": "^22.0.0", + "typescript": "^5.8.0", + "vitest": "^3.0.0" + } +} diff --git a/packages/core-realtime/src/index.ts b/packages/core-realtime/src/index.ts new file mode 100644 index 0000000..adfedd6 --- /dev/null +++ b/packages/core-realtime/src/index.ts @@ -0,0 +1,2 @@ +// Public barrel — populated as components land in Phase 2. +export {}; diff --git a/packages/core-realtime/tsconfig.json b/packages/core-realtime/tsconfig.json new file mode 100644 index 0000000..e7df3a2 --- /dev/null +++ b/packages/core-realtime/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "@repo/core-typescript/base.json", + "compilerOptions": { + "rootDir": ".", + "paths": { "@/*": ["./src/*"] } + }, + "include": ["src/**/*", "*.config.ts"] +} diff --git a/packages/core-realtime/turbo.json b/packages/core-realtime/turbo.json new file mode 100644 index 0000000..3b1522d --- /dev/null +++ b/packages/core-realtime/turbo.json @@ -0,0 +1,5 @@ +{ + "$schema": "https://turborepo.dev/schema.json", + "extends": ["//"], + "tags": ["core"] +} diff --git a/packages/core-realtime/vitest.config.ts b/packages/core-realtime/vitest.config.ts new file mode 100644 index 0000000..2ee07c1 --- /dev/null +++ b/packages/core-realtime/vitest.config.ts @@ -0,0 +1,9 @@ +import path from "node:path"; +import { mergeConfig } from "vitest/config"; +import { nodeVitestConfig } from "@repo/core-typescript/vitest.base.node"; + +export default mergeConfig(nodeVitestConfig, { + resolve: { + alias: { "@": path.resolve(__dirname, "./src") }, + }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0e1d031..ca10382 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -556,6 +556,34 @@ importers: specifier: ^3.0.0 version: 3.2.4(@types/debug@4.1.13)(@types/node@25.5.2)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0) + packages/core-realtime: + dependencies: + payload: + specifier: ^3.0.0 + version: 3.81.0(graphql@16.13.2)(typescript@5.9.3) + socket.io: + specifier: ^4.7.0 + version: 4.8.3 + zod: + specifier: ^3.23.0 + version: 3.25.76 + devDependencies: + '@repo/core-eslint': + specifier: workspace:* + version: link:../core-eslint + '@repo/core-typescript': + specifier: workspace:* + version: link:../core-typescript + '@types/node': + specifier: ^22.0.0 + version: 22.19.17 + typescript: + specifier: ^5.8.0 + version: 5.9.3 + vitest: + specifier: ^3.0.0 + version: 3.2.4(@types/debug@4.1.13)(@types/node@22.19.17)(happy-dom@20.8.9)(jiti@2.6.1)(jsdom@25.0.1)(lightningcss@1.32.0)(sass@1.99.0)(terser@5.46.2)(tsx@4.21.0) + packages/core-shared: dependencies: '@sentry/nextjs': @@ -3206,6 +3234,9 @@ packages: '@sinonjs/fake-timers@10.3.0': resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@socket.io/component-emitter@3.1.2': + resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -3733,6 +3764,9 @@ packages: '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + '@types/cors@2.8.19': + resolution: {integrity: sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==} + '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} @@ -4005,6 +4039,10 @@ packages: '@xtuc/long@4.2.2': resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + acorn-import-attributes@1.9.5: resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==} peerDependencies: @@ -4177,6 +4215,10 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} + base64id@2.0.0: + resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} + engines: {node: ^4.5.0 || >= 5.9} + baseline-browser-mapping@2.10.15: resolution: {integrity: sha512-1nfKCq9wuAZFTkA2ey/3OXXx7GzFjLdkTiFVNwlJ9WqdI706CZRIhEqjuwanjMIja+84jDLa9rcyZDPDiVkASQ==} engines: {node: '>=6.0.0'} @@ -4431,10 +4473,18 @@ packages: cookie-es@2.0.1: resolution: {integrity: sha512-aVf4A4hI2w70LnF7GG+7xDQUkliwiXWXFvTjkip4+b64ygDQ2sJPRSKFDHbxn8o0xu9QzPkMuuiWIXyFSE2slA==} + cookie@0.7.2: + resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} + engines: {node: '>= 0.6'} + copy-anything@4.0.5: resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} engines: {node: '>=18'} + cors@2.8.6: + resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==} + engines: {node: '>= 0.10'} + corser@2.0.1: resolution: {integrity: sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==} engines: {node: '>= 0.4.0'} @@ -4737,6 +4787,14 @@ packages: end-of-stream@1.4.5: resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} + engine.io-parser@5.2.3: + resolution: {integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==} + engines: {node: '>=10.0.0'} + + engine.io@6.6.7: + resolution: {integrity: sha512-DgOngfDKM2EviOH3Mr9m7ks1q8roetLy/IMmYthAYzbpInMbYc/GS+fWFA3rl1gvwKVsQrVV61fo5emD1y3OJQ==} + engines: {node: '>=10.2.0'} + enhanced-resolve@5.20.1: resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==} engines: {node: '>=10.13.0'} @@ -6111,6 +6169,10 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} @@ -6842,6 +6904,17 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + socket.io-adapter@2.5.6: + resolution: {integrity: sha512-DkkO/dz7MGln0dHn5bmN3pPy+JmywNICWrJqVWiVOyvXjWQFIv9c2h24JrQLLFJ2aQVQf/Cvl1vblnd4r2apLQ==} + + socket.io-parser@4.2.6: + resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} + engines: {node: '>=10.0.0'} + + socket.io@4.8.3: + resolution: {integrity: sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A==} + engines: {node: '>=10.2.0'} + sonic-boom@4.2.1: resolution: {integrity: sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==} @@ -7302,6 +7375,10 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vfile-message@4.0.3: resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} @@ -7500,6 +7577,18 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.20.0: resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} engines: {node: '>=10.0.0'} @@ -10151,6 +10240,8 @@ snapshots: dependencies: '@sinonjs/commons': 3.0.1 + '@socket.io/component-emitter@3.1.2': {} + '@standard-schema/spec@1.1.0': {} '@storybook/addon-actions@8.6.14(storybook@8.6.18(prettier@3.8.1))': @@ -10700,6 +10791,10 @@ snapshots: dependencies: '@types/node': 22.19.17 + '@types/cors@2.8.19': + dependencies: + '@types/node': 22.19.17 + '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 @@ -11091,6 +11186,11 @@ snapshots: '@xtuc/long@4.2.2': {} + accepts@1.3.8: + dependencies: + mime-types: 2.1.35 + negotiator: 0.6.3 + acorn-import-attributes@1.9.5(acorn@8.16.0): dependencies: acorn: 8.16.0 @@ -11281,6 +11381,8 @@ snapshots: balanced-match@4.0.4: {} + base64id@2.0.0: {} + baseline-browser-mapping@2.10.15: {} basic-auth@2.0.1: @@ -11516,10 +11618,17 @@ snapshots: cookie-es@2.0.1: {} + cookie@0.7.2: {} + copy-anything@4.0.5: dependencies: is-what: 5.5.0 + cors@2.8.6: + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + corser@2.0.1: {} cosmiconfig@7.1.0: @@ -11716,6 +11825,25 @@ snapshots: dependencies: once: 1.4.0 + engine.io-parser@5.2.3: {} + + engine.io@6.6.7: + dependencies: + '@types/cors': 2.8.19 + '@types/node': 22.19.17 + '@types/ws': 8.18.1 + accepts: 1.3.8 + base64id: 2.0.0 + cookie: 0.7.2 + cors: 2.8.6 + debug: 4.4.3 + engine.io-parser: 5.2.3 + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + enhanced-resolve@5.20.1: dependencies: graceful-fs: 4.2.11 @@ -13495,6 +13623,8 @@ snapshots: natural-compare@1.4.0: {} + negotiator@0.6.3: {} + neo-async@2.6.2: {} next@15.5.14(@opentelemetry/api@1.9.1)(@playwright/test@1.59.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(sass@1.99.0): @@ -14357,6 +14487,36 @@ snapshots: slash@5.1.0: {} + socket.io-adapter@2.5.6: + dependencies: + debug: 4.4.3 + ws: 8.18.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.6: + dependencies: + '@socket.io/component-emitter': 3.1.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + socket.io@4.8.3: + dependencies: + accepts: 1.3.8 + base64id: 2.0.0 + cors: 2.8.6 + debug: 4.4.3 + engine.io: 6.6.7 + socket.io-adapter: 2.5.6 + socket.io-parser: 4.2.6 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + sonic-boom@4.2.1: dependencies: atomic-sleep: 1.0.0 @@ -14777,6 +14937,8 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + vary@1.1.2: {} + vfile-message@4.0.3: dependencies: '@types/unist': 3.0.3 @@ -15107,6 +15269,8 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 + ws@8.18.3: {} + ws@8.20.0: {} xml-name-validator@5.0.0: {}