7154a0a49c
Stand up the WebSocket live-broadcast server inside the Processor process: - src/live/server.ts: createLiveServer factory with start/stop lifecycle, per-connection LiveConnection type, sendOutbound helper with back-pressure guard, 30s frame-level heartbeat via ws ping/pong, pluggable onMessage handler (stub returns error/not-implemented until 1.5.2/1.5.3). - src/live/protocol.ts: zod schemas for inbound subscribe/unsubscribe messages, all outbound types (subscribed/unsubscribed/position/error), WsCloseCodes. - src/shared/types.ts: extracted Metrics interface so src/live/ can import it without crossing the enforced src/live/ ↔ src/core/ ESLint boundary. - src/core/types.ts: re-exports Metrics from shared/types to keep Phase 1 call sites unchanged. - src/config/load.ts: LIVE_WS_PORT, LIVE_WS_HOST, LIVE_WS_PING_INTERVAL_MS, LIVE_WS_DRAIN_TIMEOUT_MS, LIVE_WS_BACKPRESSURE_THRESHOLD_BYTES, DIRECTUS_BASE_URL, DIRECTUS_AUTH_TIMEOUT_MS, DIRECTUS_AUTHZ_TIMEOUT_MS, LIVE_BROADCAST_GROUP_PREFIX, LIVE_BROADCAST_BATCH_SIZE, LIVE_BROADCAST_BATCH_BLOCK_MS, LIVE_DEVICE_EVENT_REFRESH_MS. - src/observability/metrics.ts: Phase 1.5 metrics inventory (connections, inbound/outbound counters, auth/authz histograms, subscription gauge, broadcast counters + lag histogram, snapshot histograms, device-event map). - src/main.ts: wires the live server alongside the durable-write consumer; shutdown order: live server → consumer → metrics → Redis → Postgres. - eslint.config.js: import/no-restricted-paths zones for src/live/ ↔ src/core/. - test/live-server.test.ts: 7 unit tests covering connect, ping, protocol violation, valid message dispatch, connections gauge, and stop() drain.
90 lines
2.8 KiB
JavaScript
90 lines
2.8 KiB
JavaScript
// @ts-check
|
|
import tseslint from '@typescript-eslint/eslint-plugin';
|
|
import tsParser from '@typescript-eslint/parser';
|
|
import importPlugin from 'eslint-plugin-import';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = dirname(__filename);
|
|
|
|
/** @type {import('eslint').Linter.Config[]} */
|
|
export default [
|
|
{
|
|
ignores: ['dist/**', 'node_modules/**', 'coverage/**'],
|
|
},
|
|
{
|
|
files: ['src/**/*.ts', 'test/**/*.ts'],
|
|
plugins: {
|
|
'@typescript-eslint': tseslint,
|
|
import: importPlugin,
|
|
},
|
|
languageOptions: {
|
|
parser: tsParser,
|
|
parserOptions: {
|
|
project: './tsconfig.test.json',
|
|
tsconfigRootDir: __dirname,
|
|
ecmaVersion: 2022,
|
|
sourceType: 'module',
|
|
},
|
|
},
|
|
settings: {
|
|
'import/resolver': {
|
|
typescript: {
|
|
project: join(__dirname, 'tsconfig.test.json'),
|
|
},
|
|
},
|
|
},
|
|
rules: {
|
|
// TypeScript strict promise rules — critical in a stream consumer where
|
|
// unhandled rejection silently loses work.
|
|
'@typescript-eslint/no-floating-promises': 'error',
|
|
'@typescript-eslint/no-misused-promises': 'error',
|
|
|
|
// General quality
|
|
'@typescript-eslint/no-explicit-any': 'error',
|
|
'@typescript-eslint/no-unused-vars': [
|
|
'error',
|
|
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
],
|
|
'@typescript-eslint/consistent-type-imports': [
|
|
'error',
|
|
{ prefer: 'type-imports' },
|
|
],
|
|
|
|
// Domain isolation: core/ must NEVER import from domain/.
|
|
// src/domain/ does not exist yet — this rule is preemptive so Phase 2
|
|
// cannot violate the boundary by accident.
|
|
//
|
|
// Live isolation: src/live/ and src/core/ must not import from each
|
|
// other; src/db/pool.ts is the only shared module between them.
|
|
'import/no-restricted-paths': [
|
|
'error',
|
|
{
|
|
basePath: __dirname,
|
|
zones: [
|
|
{
|
|
target: 'src/core',
|
|
from: 'src/domain',
|
|
message:
|
|
'src/core must not import from src/domain — domain logic depends on core, not the reverse.',
|
|
},
|
|
{
|
|
target: 'src/core',
|
|
from: 'src/live',
|
|
message:
|
|
'src/core must not import from src/live — Phase 1 throughput pipeline is independent of the live broadcast layer.',
|
|
},
|
|
{
|
|
target: 'src/live',
|
|
from: 'src/core',
|
|
message:
|
|
'src/live must not import from src/core — use src/db/pool.ts for the shared Postgres pool. If you need a Phase 1 type, move it to a shared types file.',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
];
|