arcway 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/client/fetcher.js CHANGED
@@ -44,7 +44,7 @@ async function soloFetch(url, init) {
44
44
  throw new ApiError(res.status, 'UNKNOWN', json?.message || res.statusText);
45
45
  }
46
46
 
47
- return json.data;
47
+ return json;
48
48
  }
49
49
 
50
50
  export { ApiError, soloFetch };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arcway",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "A convention-based framework for building modular monoliths with strict domain boundaries.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -29,6 +29,7 @@
29
29
  "./ui/style-mira.css": "./client/ui/style-mira.css"
30
30
  },
31
31
  "scripts": {
32
+ "prepare": "esbuild client/router.jsx --outfile=client/router.js --format=esm --jsx=automatic",
32
33
  "test": "vitest run --project=unit",
33
34
  "test:storybook": "TMPDIR=~/tmp PLAYWRIGHT_BROWSERS_PATH=~/.cache/playwright vitest run --project=storybook",
34
35
  "test:all": "TMPDIR=~/tmp PLAYWRIGHT_BROWSERS_PATH=~/.cache/playwright vitest run",
@@ -37,7 +38,8 @@
37
38
  "lint": "eslint server/ client/ tests/",
38
39
  "lint:fix": "eslint server/ client/ tests/ --fix",
39
40
  "storybook": "storybook dev -p 6006",
40
- "build-storybook": "storybook build"
41
+ "build-storybook": "storybook build",
42
+ "deploy:docs": "cd ~/Projects/home-server/baremetal/ansible && ansible-playbook deploy-arcway-docs.yml"
41
43
  },
42
44
  "dependencies": {
43
45
  "@aws-sdk/client-s3": "^3.987.0",
package/server/bin/cli.js CHANGED
@@ -11,6 +11,7 @@ import registerLint from './commands/lint.js';
11
11
  import registerGraphQLSchema from './commands/graphql-schema.js';
12
12
  import registerSchema from './commands/schema.js';
13
13
  import registerMigrate from './commands/migrate.js';
14
+ import registerBootstrap from './commands/bootstrap.js';
14
15
 
15
16
  function getPackageVersion() {
16
17
  try {
@@ -36,6 +37,7 @@ function createProgram() {
36
37
  registerGraphQLSchema(program);
37
38
  registerSchema(program);
38
39
  registerMigrate(program);
40
+ registerBootstrap(program);
39
41
  return program;
40
42
  }
41
43
 
@@ -0,0 +1,147 @@
1
+ import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+
4
+ const FILES = {
5
+ 'arcway.config.js': `export default {
6
+ database: {
7
+ client: 'better-sqlite3',
8
+ connection: { filename: './data.db' },
9
+ },
10
+ };
11
+ `,
12
+
13
+ 'api/index.js': `export const GET = {
14
+ handler: async (ctx) => {
15
+ return { data: { message: 'Hello from Arcway!' } };
16
+ },
17
+ };
18
+ `,
19
+
20
+ 'api/users/index.js': `export const GET = {
21
+ handler: async (ctx) => {
22
+ const { db } = ctx;
23
+ const users = await db('users').select('*');
24
+ return { data: users };
25
+ },
26
+ };
27
+
28
+ export const POST = {
29
+ schema: {
30
+ body: { name: 'string', email: 'string.email' },
31
+ },
32
+ handler: async (ctx) => {
33
+ const { req, db, events } = ctx;
34
+ const [id] = await db('users').insert(req.body);
35
+ await events.emit('users/created', { id, ...req.body });
36
+ return { status: 201, data: { id } };
37
+ },
38
+ };
39
+ `,
40
+
41
+ 'api/users/[id].js': `export const GET = {
42
+ handler: async (ctx) => {
43
+ const { req, db } = ctx;
44
+ const user = await db('users').where('id', req.query.id).first();
45
+ if (!user) return { status: 404, error: 'User not found' };
46
+ return { data: user };
47
+ },
48
+ };
49
+ `,
50
+
51
+ 'listeners/users/created.js': `export default async (ctx) => {
52
+ const { event, log } = ctx;
53
+ log.info('New user created', { userId: event.payload.id });
54
+ };
55
+ `,
56
+
57
+ 'jobs/cleanup-sessions.js': `export default {
58
+ schedule: '0 3 * * *', // Every day at 3 AM
59
+ handler: async (ctx) => {
60
+ const { db, log } = ctx;
61
+ const deleted = await db('sessions')
62
+ .where('expires_at', '<', new Date().toISOString())
63
+ .delete();
64
+ log.info('Cleaned up expired sessions', { deleted });
65
+ },
66
+ };
67
+ `,
68
+
69
+ 'migrations/202601010000-create-users.js': `export async function up(knex) {
70
+ await knex.schema.createTable('users', (table) => {
71
+ table.increments('id').primary();
72
+ table.string('name').notNullable();
73
+ table.string('email').notNullable().unique();
74
+ table.timestamps(true, true);
75
+ });
76
+ }
77
+
78
+ export async function down(knex) {
79
+ await knex.schema.dropTableIfExists('users');
80
+ }
81
+ `,
82
+
83
+ 'pages/index.jsx': `export default function Home() {
84
+ return (
85
+ <div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
86
+ <h1>Welcome to Arcway</h1>
87
+ <p>Edit <code>pages/index.jsx</code> to get started.</p>
88
+ </div>
89
+ );
90
+ }
91
+ `,
92
+
93
+ '.env': `# NODE_ENV=development
94
+ # SESSION_PASSWORD=change-me-to-a-random-32-char-string
95
+ `,
96
+
97
+ '.gitignore': `node_modules/
98
+ .build/
99
+ data.db
100
+ .env.local
101
+ .env.*.local
102
+ `,
103
+ };
104
+
105
+ function bootstrap(rootDir) {
106
+ let created = 0;
107
+ let skipped = 0;
108
+
109
+ for (const [relativePath, content] of Object.entries(FILES)) {
110
+ const fullPath = join(rootDir, relativePath);
111
+ if (existsSync(fullPath)) {
112
+ console.log(` skip ${relativePath} (already exists)`);
113
+ skipped++;
114
+ continue;
115
+ }
116
+ const dir = join(fullPath, '..');
117
+ mkdirSync(dir, { recursive: true });
118
+ writeFileSync(fullPath, content);
119
+ console.log(` create ${relativePath}`);
120
+ created++;
121
+ }
122
+
123
+ return { created, skipped };
124
+ }
125
+
126
+ function register(program) {
127
+ program
128
+ .command('bootstrap')
129
+ .description('Scaffold a new Arcway project in the current directory')
130
+ .action(() => {
131
+ const rootDir = process.cwd();
132
+ console.log(`\nBootstrapping Arcway project in ${rootDir}\n`);
133
+
134
+ const { created, skipped } = bootstrap(rootDir);
135
+
136
+ console.log(`\n ${created} file(s) created, ${skipped} skipped\n`);
137
+
138
+ if (created > 0) {
139
+ console.log('Next steps:');
140
+ console.log(' npm install arcway better-sqlite3');
141
+ console.log(' npx arcway dev\n');
142
+ }
143
+ });
144
+ }
145
+
146
+ export default register;
147
+ export { bootstrap };
@@ -6,7 +6,10 @@ import { createDB } from '#server/db/index.js';
6
6
  async function runMigrateMake(name) {
7
7
  const rootDir = process.cwd();
8
8
  try {
9
- const migrationsDir = path.resolve(rootDir, 'migrations');
9
+ const mode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
10
+ loadEnvFiles(rootDir, mode);
11
+ const config = await makeConfig(rootDir);
12
+ const migrationsDir = config.database.dir;
10
13
  await fs.mkdir(migrationsDir, { recursive: true });
11
14
  const now = new Date();
12
15
  const timestamp = [
@@ -1,4 +1,3 @@
1
- import path from 'node:path';
2
1
  import { makeConfig } from '#server/config/loader.js';
3
2
  import { loadEnvFiles } from '#server/env.js';
4
3
  import { createDB } from '#server/db/index.js';
@@ -11,7 +10,7 @@ async function runSeed() {
11
10
  const config = await makeConfig(rootDir);
12
11
  const db = await createDB(config.database);
13
12
  await db.runMigrations();
14
- const seedsDir = path.join(rootDir, 'seeds');
13
+ const seedsDir = config.seeds.dir;
15
14
  const results = await runSeeds(db, seedsDir);
16
15
  if (results.length === 0) {
17
16
  console.log('No seed files found.');
@@ -17,6 +17,7 @@ import resolvePages from './modules/pages.js';
17
17
  import resolveBuild from './modules/build.js';
18
18
  import resolveMcp from './modules/mcp.js';
19
19
  import resolveWebsocket from './modules/websocket.js';
20
+ import resolveSeeds from './modules/seeds.js';
20
21
 
21
22
  function deepMerge(target, source) {
22
23
  const result = { ...target };
@@ -53,6 +54,7 @@ const modules = [
53
54
  resolveBuild,
54
55
  resolveMcp,
55
56
  resolveWebsocket,
57
+ resolveSeeds,
56
58
  ];
57
59
 
58
60
  async function makeConfig(rootDir, { overrides, mode } = {}) {
@@ -2,7 +2,7 @@ import path from 'node:path';
2
2
 
3
3
  const DEFAULTS = {
4
4
  sqliteFilename: '.build/db/arcway.db',
5
- migrationsDir: 'migrations',
5
+ dir: 'migrations',
6
6
  };
7
7
 
8
8
  const CLIENT_MAP = {
@@ -15,8 +15,8 @@ function resolve(config, { rootDir } = {}) {
15
15
  const db = { ...DEFAULTS, ...config.database };
16
16
  // Resolve friendly client names to actual knex driver names
17
17
  db.client = CLIENT_MAP[db.client] ?? db.client;
18
- if (db.migrationsDir && !path.isAbsolute(db.migrationsDir)) {
19
- db.migrationsDir = path.resolve(rootDir, db.migrationsDir);
18
+ if (db.dir && !path.isAbsolute(db.dir)) {
19
+ db.dir = path.resolve(rootDir, db.dir);
20
20
  }
21
21
  const isSqlite = db.client === 'better-sqlite3' || db.client === 'sqlite3';
22
22
  if (isSqlite && !db.connection) {
@@ -1,13 +1,13 @@
1
1
  import path from 'node:path';
2
2
 
3
3
  const DEFAULTS = {
4
- listenersDir: 'listeners',
4
+ dir: 'listeners',
5
5
  };
6
6
 
7
7
  function resolve(config, { rootDir } = {}) {
8
8
  const events = { ...DEFAULTS, ...config.events };
9
- if (events.listenersDir && !path.isAbsolute(events.listenersDir)) {
10
- events.listenersDir = path.resolve(rootDir, events.listenersDir);
9
+ if (events.dir && !path.isAbsolute(events.dir)) {
10
+ events.dir = path.resolve(rootDir, events.dir);
11
11
  }
12
12
  return { ...config, events };
13
13
  }
@@ -6,13 +6,13 @@ const DEFAULTS = {
6
6
  pollIntervalMs: 60000,
7
7
  tableName: 'arcway_jobs',
8
8
  leaseTable: 'arcway_job_leases',
9
- jobsDir: 'jobs',
9
+ dir: 'jobs',
10
10
  };
11
11
 
12
12
  function resolve(config, { rootDir } = {}) {
13
13
  const jobs = { ...DEFAULTS, ...config.jobs };
14
- if (jobs.jobsDir && !path.isAbsolute(jobs.jobsDir)) {
15
- jobs.jobsDir = path.resolve(rootDir, jobs.jobsDir);
14
+ if (jobs.dir && !path.isAbsolute(jobs.dir)) {
15
+ jobs.dir = path.resolve(rootDir, jobs.dir);
16
16
  }
17
17
  return { ...config, jobs };
18
18
  }
@@ -0,0 +1,15 @@
1
+ import path from 'node:path';
2
+
3
+ const DEFAULTS = {
4
+ dir: 'seeds',
5
+ };
6
+
7
+ function resolve(config, { rootDir } = {}) {
8
+ const seeds = { ...DEFAULTS, ...config.seeds };
9
+ if (seeds.dir && !path.isAbsolute(seeds.dir)) {
10
+ seeds.dir = path.resolve(rootDir, seeds.dir);
11
+ }
12
+ return { ...config, seeds };
13
+ }
14
+
15
+ export default resolve;
@@ -3,6 +3,7 @@ const DEFAULTS = {
3
3
  port: 3000,
4
4
  maxBodySize: 1024 * 1024,
5
5
  shutdownTimeoutMs: 10000,
6
+ trustProxy: false,
6
7
  };
7
8
 
8
9
  const DEFAULT_CORS = {
@@ -45,6 +45,12 @@ async function createDB(config, { log } = {}) {
45
45
  throw new Error(`Database connection failed: ${err}`);
46
46
  }
47
47
 
48
+ if (isSqlite) {
49
+ await db.raw('PRAGMA journal_mode = WAL');
50
+ await db.raw('PRAGMA busy_timeout = 5000');
51
+ await db.raw('PRAGMA synchronous = NORMAL');
52
+ }
53
+
48
54
  const origDestroy = db.destroy.bind(db);
49
55
  Object.defineProperty(db, 'destroy', {
50
56
  value: async () => {
@@ -56,7 +62,7 @@ async function createDB(config, { log } = {}) {
56
62
  });
57
63
 
58
64
  db.runMigrations = async () => {
59
- const migrationsDir = config.migrationsDir;
65
+ const migrationsDir = config.dir;
60
66
  if (!migrationsDir) return;
61
67
  const [batch, migrations] = await db.migrate.latest({
62
68
  migrationSource: new MigrationSource(migrationsDir),
@@ -69,8 +75,8 @@ async function createDB(config, { log } = {}) {
69
75
  };
70
76
 
71
77
  db.runRollback = async () => {
72
- const migrationsDir = config.migrationsDir;
73
- if (!migrationsDir) throw new Error('No migrationsDir configured');
78
+ const migrationsDir = config.dir;
79
+ if (!migrationsDir) throw new Error('No migrations dir configured');
74
80
  const [batch, entries] = await db.migrate.rollback({
75
81
  migrationSource: new MigrationSource(migrationsDir),
76
82
  });
@@ -6,34 +6,22 @@ class MemoryTransport {
6
6
  const regex = patternToRegex(pattern);
7
7
  this.subscriptions.push({ pattern, regex, handler });
8
8
  }
9
- /**
10
- * Emit an event. All matching subscribers are called concurrently.
11
- * Errors in individual listeners are caught and collected — one failing
12
- * listener does not prevent others from running.
13
- */
9
+ /** Emit an event. Handlers run asynchronously — fire-and-forget. */
14
10
  async emit(eventName, payload) {
15
11
  const matching = this.subscriptions.filter((sub) => sub.regex.test(eventName));
16
- if (matching.length === 0) {
17
- return;
18
- }
19
- const errors = [];
20
- const results = await Promise.allSettled(
12
+ if (matching.length === 0) return;
13
+ Promise.allSettled(
21
14
  matching.map((sub) => sub.handler(eventName, payload)),
22
- );
23
- for (let i = 0; i < results.length; i++) {
24
- const result = results[i];
25
- if (result.status === 'rejected') {
26
- const sub = matching[i];
27
- errors.push({ pattern: sub.pattern, error: result.reason });
28
- console.error(
29
- `Event listener error [${sub.pattern}] for event "${eventName}":`,
30
- result.reason,
31
- );
15
+ ).then((results) => {
16
+ for (let i = 0; i < results.length; i++) {
17
+ if (results[i].status === 'rejected') {
18
+ console.error(
19
+ `Event listener error [${matching[i].pattern}] for event "${eventName}":`,
20
+ results[i].reason,
21
+ );
22
+ }
32
23
  }
33
- }
34
- if (errors.length > 0 && errors.length === matching.length) {
35
- throw new Error(`All ${errors.length} listener(s) for event "${eventName}" failed`);
36
- }
24
+ });
37
25
  }
38
26
  async disconnect() {}
39
27
 
@@ -20,13 +20,13 @@ function validateHandler(item, filePath, index) {
20
20
 
21
21
  class EventHandler {
22
22
  _events;
23
- _listenersDir;
23
+ _dir;
24
24
  _log;
25
25
  _appContext;
26
26
  _listeners = [];
27
27
 
28
28
  constructor(config, { events, log, appContext } = {}) {
29
- this._listenersDir = config?.listenersDir;
29
+ this._dir = config?.dir;
30
30
  this._events = events;
31
31
  this._log = log;
32
32
  this._appContext = appContext ?? {
@@ -41,8 +41,8 @@ class EventHandler {
41
41
  }
42
42
 
43
43
  async init() {
44
- if (!this._listenersDir) return;
45
- const entries = await discoverModules(this._listenersDir, {
44
+ if (!this._dir) return;
45
+ const entries = await discoverModules(this._dir, {
46
46
  recursive: true,
47
47
  label: 'listener file',
48
48
  });
@@ -17,14 +17,14 @@ function createGraphQLHandler(options) {
17
17
  cors: false,
18
18
  context: async ({ request }) => {
19
19
  const loaders = makeLoaders();
20
- const requestId = request.headers.get('x-request-id') ?? randomUUID();
20
+ const id = request.headers.get('x-request-id') ?? randomUUID();
21
21
  const headers = {};
22
22
  request.headers.forEach((value, key) => {
23
23
  headers[key] = value;
24
24
  });
25
25
  const cookies = parseCookiesFromHeader(request.headers.get('cookie') ?? '');
26
26
  const session = await resolveSession(cookies, sessionConfig);
27
- return { loaders, session, requestId, headers, cookies };
27
+ return { loaders, session, id, headers, cookies };
28
28
  },
29
29
  logging: log
30
30
  ? {
@@ -59,7 +59,7 @@ class JobRunner {
59
59
  }
60
60
 
61
61
  async init() {
62
- const jobsDir = this._config?.jobsDir;
62
+ const jobsDir = this._config?.dir;
63
63
  if (!jobsDir) return;
64
64
 
65
65
  // Discover and register user jobs
@@ -46,7 +46,7 @@ const validateRequest = validateRequestSchema;
46
46
  async function serializeResponse(res, response, responseHeaders, statusCode) {
47
47
  const customContentType = responseHeaders['Content-Type'] || responseHeaders['content-type'];
48
48
  if (!customContentType || customContentType.includes('application/json')) {
49
- const responseBody = response.error ? { error: response.error } : { data: response.data };
49
+ const responseBody = response.error ? { error: response.error } : (response.data ?? null);
50
50
  sendJson(res, statusCode, responseBody, responseHeaders);
51
51
  return;
52
52
  }
@@ -140,7 +140,7 @@ class ApiRouter {
140
140
  return buildContext(
141
141
  {
142
142
  ...this._appContext,
143
- log: this._log.extend({ requestId: reqInfo.requestId }),
143
+ log: this._log.extend({ requestId: reqInfo.id }),
144
144
  },
145
145
  { req: reqInfo },
146
146
  );
@@ -197,7 +197,8 @@ class ApiRouter {
197
197
  async handle(req, res) {
198
198
  const method = req.method ?? 'GET';
199
199
  const pathname = (req.url ?? '/').split('?')[0];
200
- const requestId = req.requestId;
200
+ const id = req.id;
201
+ const ip = req.ip;
201
202
  const startTime = Date.now();
202
203
 
203
204
  const matched = matchRoute(this._routes, method, pathname);
@@ -205,20 +206,25 @@ class ApiRouter {
205
206
 
206
207
  const { route, params } = matched;
207
208
 
209
+ // ── Body parsing control ──
210
+ const body = route.config.parseBody === false ? req.rawBody : req.body;
211
+
208
212
  // ── Validation ──
209
213
  const mergedQuery = { ...(req.query ?? {}), ...params };
210
- const validated = validateRequest(route.config.schema, mergedQuery, req.body);
214
+ const validated = validateRequest(route.config.schema, mergedQuery, body);
211
215
  if (validated.error) {
212
216
  sendJson(res, 400, { error: validated.error });
213
217
  return true;
214
218
  }
215
219
 
216
220
  const reqInfo = {
217
- requestId,
221
+ id,
222
+ ip,
218
223
  method,
219
224
  path: pathname,
220
225
  query: validated.query,
221
226
  body: validated.body,
227
+ rawBody: req.rawBody,
222
228
  headers: req.flatHeaders ?? flattenHeaders(req.headers),
223
229
  cookies: req.cookies ?? {},
224
230
  session: req.session,
@@ -128,9 +128,9 @@ async function createTestContext(domainName, options) {
128
128
  if (options?.rootDir) {
129
129
  const migrationsDir = path.join(options.rootDir, 'migrations');
130
130
  await db.migrate.latest({ migrationSource: new MigrationSource(migrationsDir) });
131
- } else if (options?.migrationsDir) {
131
+ } else if (options?.dir || options?.migrationsDir) {
132
132
  await db.migrate.latest({
133
- migrationSource: new MigrationSource(options.migrationsDir),
133
+ migrationSource: new MigrationSource(options.dir ?? options.migrationsDir),
134
134
  });
135
135
  }
136
136
  const scopedDb = db;
@@ -83,12 +83,23 @@ class WebServer {
83
83
  }
84
84
  }
85
85
 
86
+ const trustProxy = config.server?.trustProxy ?? false;
87
+
86
88
  const requestHandler = (req, res) => {
87
89
  // ── Request ID ──
88
90
  const incomingId = req.headers['x-request-id'];
89
- const requestId = (Array.isArray(incomingId) ? incomingId[0] : incomingId) ?? randomUUID();
90
- res.setHeader('X-Request-Id', requestId);
91
- req.requestId = requestId;
91
+ req.id = (Array.isArray(incomingId) ? incomingId[0] : incomingId) ?? randomUUID();
92
+ res.setHeader('X-Request-Id', req.id);
93
+
94
+ // ── Client IP ──
95
+ if (trustProxy) {
96
+ const forwarded = req.headers['x-forwarded-for'];
97
+ req.ip = forwarded
98
+ ? String(forwarded).split(',')[0].trim()
99
+ : req.headers['x-real-ip'] ?? req.socket?.remoteAddress ?? '127.0.0.1';
100
+ } else {
101
+ req.ip = req.socket?.remoteAddress ?? '127.0.0.1';
102
+ }
92
103
 
93
104
  // ── CORS ──
94
105
  if (corsConfig) {
@@ -124,7 +135,7 @@ class WebServer {
124
135
  res.end(JSON.stringify({ error: { code: ErrorCodes.INVALID_JSON, message: 'Request body is not valid JSON' } }));
125
136
  return;
126
137
  }
127
- log.error('Unhandled error in request handler', { error: msg, requestId });
138
+ log.error('Unhandled error in request handler', { error: msg, requestId: req.id });
128
139
  res.writeHead(500, { 'Content-Type': 'application/json' });
129
140
  res.end(JSON.stringify({ error: { code: 'INTERNAL_ERROR', message: 'An unexpected error occurred' } }));
130
141
  });
@@ -177,6 +188,7 @@ class WebServer {
177
188
  // Body (POST/PUT/PATCH only)
178
189
  if (method === 'POST' || method === 'PUT' || method === 'PATCH') {
179
190
  const rawBody = await readBody(req, maxBodySize);
191
+ req.rawBody = rawBody;
180
192
  if (rawBody.length > 0) {
181
193
  const contentType = req.headers['content-type'] ?? '';
182
194
  if (contentType.includes('application/json')) {
@@ -22,16 +22,16 @@ function createRealtimeServer(options) {
22
22
  function buildCtx(reqInfo) {
23
23
  const requestLog = {
24
24
  debug(message, data) {
25
- appContext.log.debug(message, { ...data, requestId: reqInfo.requestId });
25
+ appContext.log.debug(message, { ...data, requestId: reqInfo.id });
26
26
  },
27
27
  info(message, data) {
28
- appContext.log.info(message, { ...data, requestId: reqInfo.requestId });
28
+ appContext.log.info(message, { ...data, requestId: reqInfo.id });
29
29
  },
30
30
  warn(message, data) {
31
- appContext.log.warn(message, { ...data, requestId: reqInfo.requestId });
31
+ appContext.log.warn(message, { ...data, requestId: reqInfo.id });
32
32
  },
33
33
  error(message, data) {
34
- appContext.log.error(message, { ...data, requestId: reqInfo.requestId });
34
+ appContext.log.error(message, { ...data, requestId: reqInfo.id });
35
35
  },
36
36
  };
37
37
  return buildContext({ ...appContext, log: requestLog }, { req: reqInfo });
@@ -70,7 +70,7 @@ function createRealtimeServer(options) {
70
70
  ...params,
71
71
  };
72
72
  return {
73
- requestId: randomUUID(),
73
+ id: randomUUID(),
74
74
  method: wsMsg.method,
75
75
  path: wsMsg.path,
76
76
  query: mergedQuery,
@@ -270,7 +270,7 @@ class WsRouter {
270
270
 
271
271
  _buildRequestInfo(client, wsMsg, params) {
272
272
  return {
273
- requestId: randomUUID(),
273
+ id: randomUUID(),
274
274
  method: wsMsg.method,
275
275
  path: wsMsg.path,
276
276
  query: { ...(wsMsg.query ?? {}), ...params },