@emeryld/rrroutes-server 2.2.16 → 2.3.0
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/README.md +104 -65
- package/dist/index.cjs +87 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +87 -21
- package/dist/index.js.map +1 -1
- package/dist/routesV3.server.d.ts +28 -34
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -22,18 +22,20 @@ This package peers with `@emeryld/rrroutes-contract` and bundles `zod`.
|
|
|
22
22
|
## Quick start: HTTP routes
|
|
23
23
|
|
|
24
24
|
```ts
|
|
25
|
-
import express from 'express'
|
|
26
|
-
import { finalize, resource } from '@emeryld/rrroutes-contract'
|
|
27
|
-
import { createRRRoute, defineControllers } from '@emeryld/rrroutes-server'
|
|
28
|
-
import multer from 'multer'
|
|
29
|
-
import { z } from 'zod'
|
|
25
|
+
import express from 'express'
|
|
26
|
+
import { finalize, resource } from '@emeryld/rrroutes-contract'
|
|
27
|
+
import { createRRRoute, defineControllers } from '@emeryld/rrroutes-server'
|
|
28
|
+
import multer from 'multer'
|
|
29
|
+
import { z } from 'zod'
|
|
30
30
|
|
|
31
31
|
// 1) Build & finalize contracts (usually elsewhere in your app)
|
|
32
32
|
const leaves = resource('/api')
|
|
33
33
|
.sub('profiles', (r) =>
|
|
34
34
|
r
|
|
35
35
|
.get({
|
|
36
|
-
outputSchema: z.array(
|
|
36
|
+
outputSchema: z.array(
|
|
37
|
+
z.object({ id: z.string().uuid(), name: z.string() }),
|
|
38
|
+
),
|
|
37
39
|
description: 'List profiles',
|
|
38
40
|
})
|
|
39
41
|
.routeParameter('profileId', z.string().uuid(), (p) =>
|
|
@@ -55,56 +57,73 @@ const leaves = resource('/api')
|
|
|
55
57
|
)
|
|
56
58
|
.done(),
|
|
57
59
|
)
|
|
58
|
-
.done()
|
|
60
|
+
.done()
|
|
59
61
|
|
|
60
|
-
const registry = finalize(leaves)
|
|
62
|
+
const registry = finalize(leaves)
|
|
61
63
|
|
|
62
64
|
// 2) Wire Express with ctx + derived upload middleware
|
|
63
|
-
const upload = multer({ storage: multer.memoryStorage() })
|
|
64
|
-
const app = express()
|
|
65
|
+
const upload = multer({ storage: multer.memoryStorage() })
|
|
66
|
+
const app = express()
|
|
65
67
|
const server = createRRRoute(app, {
|
|
66
|
-
buildCtx: async (req) => ({
|
|
68
|
+
buildCtx: async (req) => ({
|
|
69
|
+
user: await loadUser(req),
|
|
70
|
+
routesLogger: console,
|
|
71
|
+
}), // ctx lives on res.locals[CTX_SYMBOL]
|
|
67
72
|
globalMiddleware: {
|
|
68
73
|
before: [
|
|
69
74
|
({ ctx, next }) => {
|
|
70
|
-
if (!ctx.user) throw new Error('unauthorized')
|
|
71
|
-
next()
|
|
75
|
+
if (!ctx.user) throw new Error('unauthorized')
|
|
76
|
+
next()
|
|
72
77
|
},
|
|
73
78
|
],
|
|
74
79
|
},
|
|
75
80
|
fromCfg: {
|
|
76
|
-
upload: (files) =>
|
|
81
|
+
upload: (files) =>
|
|
82
|
+
files && files.length > 0 ? [upload.fields(files)] : [],
|
|
77
83
|
},
|
|
78
84
|
validateOutput: true, // parse handler returns with outputSchema (default true)
|
|
79
|
-
debug: {
|
|
80
|
-
|
|
85
|
+
debug: {
|
|
86
|
+
request: true,
|
|
87
|
+
handler: true,
|
|
88
|
+
verbose: true,
|
|
89
|
+
logger: (e) => console.debug(e),
|
|
90
|
+
},
|
|
91
|
+
})
|
|
81
92
|
|
|
82
93
|
// 3) Author controllers with enforced keys/types
|
|
83
|
-
const controllers = defineControllers<
|
|
94
|
+
const controllers = defineControllers<
|
|
95
|
+
typeof registry,
|
|
96
|
+
{ user: { id: string } }
|
|
97
|
+
>()({
|
|
84
98
|
'GET /api/profiles': {
|
|
85
99
|
handler: async ({ ctx }) => {
|
|
86
|
-
return fetchProfilesFor(ctx.user.id)
|
|
100
|
+
return fetchProfilesFor(ctx.user.id)
|
|
87
101
|
},
|
|
88
102
|
},
|
|
89
103
|
'PATCH /api/profiles/:profileId': {
|
|
90
|
-
before: [
|
|
104
|
+
before: [
|
|
105
|
+
({ ctx, params, next }) =>
|
|
106
|
+
params.profileId === ctx.user.id
|
|
107
|
+
? next()
|
|
108
|
+
: next(new Error('Forbidden')),
|
|
109
|
+
],
|
|
91
110
|
handler: async ({ params, body }) => {
|
|
92
|
-
return updateProfile(params.profileId, body)
|
|
111
|
+
return updateProfile(params.profileId, body)
|
|
93
112
|
},
|
|
94
113
|
},
|
|
95
114
|
'PUT /api/profiles/:profileId/avatar': {
|
|
96
115
|
handler: async ({ req, params }) => {
|
|
97
|
-
const avatar = (req.files as any)?.avatar?.[0]
|
|
98
|
-
await storeAvatar(params.profileId, avatar?.buffer)
|
|
99
|
-
return { ok: true }
|
|
116
|
+
const avatar = (req.files as any)?.avatar?.[0]
|
|
117
|
+
await storeAvatar(params.profileId, avatar?.buffer)
|
|
118
|
+
return { ok: true }
|
|
100
119
|
},
|
|
101
120
|
},
|
|
102
|
-
})
|
|
121
|
+
})
|
|
103
122
|
|
|
104
|
-
server.registerControllers(registry, controllers)
|
|
105
|
-
server.warnMissingControllers(registry, console)
|
|
123
|
+
server.registerControllers(registry, controllers)
|
|
124
|
+
server.warnMissingControllers(registry, console) // warns in dev about unhandled leaves
|
|
106
125
|
|
|
107
|
-
app.listen(3000)
|
|
126
|
+
app.listen(3000)
|
|
108
127
|
```
|
|
109
128
|
|
|
110
129
|
## Detailed usage (HTTP)
|
|
@@ -112,19 +131,26 @@ app.listen(3000);
|
|
|
112
131
|
### Controller maps and typing
|
|
113
132
|
|
|
114
133
|
```ts
|
|
115
|
-
import { defineControllers, bindExpressRoutes } from '@emeryld/rrroutes-server'
|
|
134
|
+
import { defineControllers, bindExpressRoutes } from '@emeryld/rrroutes-server'
|
|
116
135
|
|
|
117
136
|
const controllers = defineControllers<typeof registry, Ctx>()({
|
|
118
|
-
'POST /v1/articles': {
|
|
119
|
-
})
|
|
137
|
+
'POST /v1/articles': {
|
|
138
|
+
handler: async ({ body, ctx }) => createArticle(ctx.user.id, body),
|
|
139
|
+
},
|
|
140
|
+
})
|
|
120
141
|
|
|
121
142
|
// register only the controllers provided (missing keys are ignored)
|
|
122
143
|
bindExpressRoutes(app, registry, controllers, {
|
|
123
144
|
buildCtx: () => ({ user: { id: '123' } }),
|
|
124
|
-
})
|
|
145
|
+
})
|
|
125
146
|
|
|
126
147
|
// or enforce every key is present at compile time
|
|
127
|
-
bindExpressRoutes(
|
|
148
|
+
bindExpressRoutes(
|
|
149
|
+
app,
|
|
150
|
+
registry,
|
|
151
|
+
controllers as { [K in keyof typeof registry.byKey]: any },
|
|
152
|
+
{ buildCtx },
|
|
153
|
+
)
|
|
128
154
|
```
|
|
129
155
|
|
|
130
156
|
- `defineControllers<Registry, Ctx>()(map)` keeps literal `"METHOD /path"` keys accurate and infers params/query/body/output types per leaf.
|
|
@@ -136,24 +162,24 @@ bindExpressRoutes(app, registry, controllers as { [K in keyof typeof registry.by
|
|
|
136
162
|
Order: `buildCtx` → `global.before` → `fromCfg` (derived) → `route.before` → handler → `route.after` → `global.after`.
|
|
137
163
|
|
|
138
164
|
```ts
|
|
139
|
-
import { getCtx, CtxRequestHandler } from '@emeryld/rrroutes-server'
|
|
165
|
+
import { getCtx, CtxRequestHandler } from '@emeryld/rrroutes-server'
|
|
140
166
|
|
|
141
167
|
const audit: CtxRequestHandler<Ctx> = ({ ctx, req, next }) => {
|
|
142
|
-
ctx.routesLogger?.info?.('audit', { user: ctx.user?.id, path: req.path })
|
|
143
|
-
next()
|
|
144
|
-
}
|
|
168
|
+
ctx.routesLogger?.info?.('audit', { user: ctx.user?.id, path: req.path })
|
|
169
|
+
next()
|
|
170
|
+
}
|
|
145
171
|
|
|
146
172
|
const server = createRRRoute(app, {
|
|
147
173
|
buildCtx: (req, res) => ({ user: res.locals.user, routesLogger: console }),
|
|
148
174
|
globalMiddleware: { before: [audit] },
|
|
149
|
-
})
|
|
175
|
+
})
|
|
150
176
|
|
|
151
177
|
// Inside any Express middleware (even outside route.before/after), use getCtx to retrieve typed ctx:
|
|
152
178
|
app.use((req, res, next) => {
|
|
153
|
-
const ctx = getCtx<Ctx>(res)
|
|
154
|
-
ctx?.routesLogger?.debug?.('in arbitrary middleware')
|
|
155
|
-
next()
|
|
156
|
-
})
|
|
179
|
+
const ctx = getCtx<Ctx>(res)
|
|
180
|
+
ctx?.routesLogger?.debug?.('in arbitrary middleware')
|
|
181
|
+
next()
|
|
182
|
+
})
|
|
157
183
|
```
|
|
158
184
|
|
|
159
185
|
- `CtxRequestHandler` receives `{ req, res, next, ctx }` with your typed ctx.
|
|
@@ -164,17 +190,18 @@ app.use((req, res, next) => {
|
|
|
164
190
|
Use `fromCfg.upload` to attach middleware when a leaf declares `bodyFiles`.
|
|
165
191
|
|
|
166
192
|
```ts
|
|
167
|
-
import multer from 'multer'
|
|
168
|
-
import { FileField } from '@emeryld/rrroutes-contract'
|
|
193
|
+
import multer from 'multer'
|
|
194
|
+
import { FileField } from '@emeryld/rrroutes-contract'
|
|
169
195
|
|
|
170
|
-
const upload = multer({ storage: multer.memoryStorage() })
|
|
196
|
+
const upload = multer({ storage: multer.memoryStorage() })
|
|
171
197
|
|
|
172
198
|
const server = createRRRoute(app, {
|
|
173
199
|
buildCtx,
|
|
174
200
|
fromCfg: {
|
|
175
|
-
upload: (files: FileField[] | undefined) =>
|
|
201
|
+
upload: (files: FileField[] | undefined) =>
|
|
202
|
+
files?.length ? [upload.fields(files)] : [],
|
|
176
203
|
},
|
|
177
|
-
})
|
|
204
|
+
})
|
|
178
205
|
```
|
|
179
206
|
|
|
180
207
|
### Output validation and custom responders
|
|
@@ -186,7 +213,7 @@ const server = createRRRoute(app, {
|
|
|
186
213
|
const server = createRRRoute(app, {
|
|
187
214
|
buildCtx,
|
|
188
215
|
send: (res, data) => res.status(201).json({ data }),
|
|
189
|
-
})
|
|
216
|
+
})
|
|
190
217
|
```
|
|
191
218
|
|
|
192
219
|
### Debug logging
|
|
@@ -203,7 +230,7 @@ const server = createRRRoute(app, {
|
|
|
203
230
|
only: ['users:list'], // filter by RouteDef.debug?.debugName
|
|
204
231
|
logger: (event) => console.log('[route-debug]', event),
|
|
205
232
|
},
|
|
206
|
-
})
|
|
233
|
+
})
|
|
207
234
|
```
|
|
208
235
|
|
|
209
236
|
Per-route overrides:
|
|
@@ -212,7 +239,7 @@ Per-route overrides:
|
|
|
212
239
|
server.register(registry.byKey['GET /api/profiles'], {
|
|
213
240
|
debug: { handler: true, debugName: 'profiles:list' },
|
|
214
241
|
handler: async () => [],
|
|
215
|
-
})
|
|
242
|
+
})
|
|
216
243
|
```
|
|
217
244
|
|
|
218
245
|
Context logger passthrough: if `buildCtx` provides `routesLogger`, handler debug events also flow to that logger (useful for request-scoped loggers).
|
|
@@ -228,33 +255,45 @@ Context logger passthrough: if `buildCtx` provides `routesLogger`, handler debug
|
|
|
228
255
|
`@emeryld/rrroutes-server` also ships a typed Socket.IO wrapper that pairs with `defineSocketEvents` from the contract package.
|
|
229
256
|
|
|
230
257
|
```ts
|
|
231
|
-
import { Server } from 'socket.io'
|
|
232
|
-
import { defineSocketEvents } from '@emeryld/rrroutes-contract'
|
|
233
|
-
import {
|
|
234
|
-
|
|
258
|
+
import { Server } from 'socket.io'
|
|
259
|
+
import { defineSocketEvents } from '@emeryld/rrroutes-contract'
|
|
260
|
+
import {
|
|
261
|
+
createSocketConnections,
|
|
262
|
+
createConnectionLoggingMiddleware,
|
|
263
|
+
} from '@emeryld/rrroutes-server'
|
|
264
|
+
import { z } from 'zod'
|
|
235
265
|
|
|
236
266
|
const { config, events } = defineSocketEvents(
|
|
237
267
|
{
|
|
238
268
|
joinMetaMessage: z.object({ room: z.string() }),
|
|
239
269
|
leaveMetaMessage: z.object({ room: z.string() }),
|
|
240
270
|
pingPayload: z.object({ sentAt: z.string() }),
|
|
241
|
-
pongPayload: z.object({
|
|
271
|
+
pongPayload: z.object({
|
|
272
|
+
sentAt: z.string(),
|
|
273
|
+
sinceMs: z.number().optional(),
|
|
274
|
+
}),
|
|
242
275
|
},
|
|
243
276
|
{
|
|
244
|
-
'chat:message': {
|
|
277
|
+
'chat:message': {
|
|
278
|
+
message: z.object({
|
|
279
|
+
roomId: z.string(),
|
|
280
|
+
text: z.string(),
|
|
281
|
+
userId: z.string(),
|
|
282
|
+
}),
|
|
283
|
+
},
|
|
245
284
|
},
|
|
246
|
-
)
|
|
285
|
+
)
|
|
247
286
|
|
|
248
|
-
const io = new Server(3000, { cors: { origin: '*', credentials: true } })
|
|
249
|
-
io.use(createConnectionLoggingMiddleware({ includeHeaders: false }))
|
|
287
|
+
const io = new Server(3000, { cors: { origin: '*', credentials: true } })
|
|
288
|
+
io.use(createConnectionLoggingMiddleware({ includeHeaders: false }))
|
|
250
289
|
|
|
251
290
|
const sockets = createSocketConnections(io, events, {
|
|
252
291
|
config,
|
|
253
292
|
heartbeat: { enabled: true }, // enables sys:ping/sys:pong using config schemas
|
|
254
293
|
sys: {
|
|
255
294
|
'sys:connect': async ({ socket, complete }) => {
|
|
256
|
-
socket.data.user = await loadUserFromHandshake(socket.handshake)
|
|
257
|
-
await complete()
|
|
295
|
+
socket.data.user = await loadUserFromHandshake(socket.handshake)
|
|
296
|
+
await complete() // attach built-ins (ping/pong, join/leave)
|
|
258
297
|
},
|
|
259
298
|
'sys:ping': async ({ socket, ping }) => ({
|
|
260
299
|
sentAt: ping.sentAt,
|
|
@@ -268,17 +307,17 @@ const sockets = createSocketConnections(io, events, {
|
|
|
268
307
|
verbose: true,
|
|
269
308
|
logger: (e) => console.debug('[socket-debug]', e),
|
|
270
309
|
},
|
|
271
|
-
})
|
|
310
|
+
})
|
|
272
311
|
|
|
273
312
|
// Validate inbound payloads + emit envelopes
|
|
274
313
|
sockets.on('chat:message', async (payload, ctx) => {
|
|
275
|
-
await saveMessage(payload, ctx.user)
|
|
314
|
+
await saveMessage(payload, ctx.user)
|
|
276
315
|
// broadcast to room participants
|
|
277
|
-
sockets.emit('chat:message', payload, payload.roomId)
|
|
278
|
-
})
|
|
316
|
+
sockets.emit('chat:message', payload, payload.roomId)
|
|
317
|
+
})
|
|
279
318
|
|
|
280
319
|
// Graceful shutdown
|
|
281
|
-
process.on('SIGTERM', () => sockets.destroy())
|
|
320
|
+
process.on('SIGTERM', () => sockets.destroy())
|
|
282
321
|
```
|
|
283
322
|
|
|
284
323
|
- Payloads are validated on both emit and receive; invalid payloads trigger `<event>:error` with Zod issues.
|
package/dist/index.cjs
CHANGED
|
@@ -43,7 +43,10 @@ var serverDebugEventTypes = [
|
|
|
43
43
|
var noopServerEmit = () => {
|
|
44
44
|
};
|
|
45
45
|
function createServerDebugEmitter(option) {
|
|
46
|
-
const disabled = {
|
|
46
|
+
const disabled = {
|
|
47
|
+
emit: noopServerEmit,
|
|
48
|
+
mode: "minimal"
|
|
49
|
+
};
|
|
47
50
|
if (!option) return disabled;
|
|
48
51
|
if (typeof option === "object") {
|
|
49
52
|
const toggles = option;
|
|
@@ -66,8 +69,11 @@ function createServerDebugEmitter(option) {
|
|
|
66
69
|
}
|
|
67
70
|
var keyOf = (leaf) => `${leaf.method.toUpperCase()} ${leaf.path}`;
|
|
68
71
|
var CTX_SYMBOL = Symbol.for("typedLeaves.ctx");
|
|
69
|
-
var AFTER_HANDLER_NEXT_SYMBOL = Symbol.for(
|
|
72
|
+
var AFTER_HANDLER_NEXT_SYMBOL = Symbol.for(
|
|
73
|
+
"typedLeaves.afterHandlerNext"
|
|
74
|
+
);
|
|
70
75
|
function setAfterHandlerNext(res, value) {
|
|
76
|
+
;
|
|
71
77
|
res.locals[AFTER_HANDLER_NEXT_SYMBOL] = value;
|
|
72
78
|
}
|
|
73
79
|
function handlerInvokedNext(res) {
|
|
@@ -110,9 +116,11 @@ function logHandlerDebugWithRoutesLogger(logger, event) {
|
|
|
110
116
|
event
|
|
111
117
|
];
|
|
112
118
|
if (event.stage === "error") {
|
|
119
|
+
;
|
|
113
120
|
(logger.error ?? logger.warn ?? logger.debug ?? logger.info ?? logger.log ?? logger.system)?.call(logger, ...payload);
|
|
114
121
|
return;
|
|
115
122
|
}
|
|
123
|
+
;
|
|
116
124
|
(logger.debug ?? logger.verbose ?? logger.info ?? logger.log ?? logger.system)?.call(logger, ...payload);
|
|
117
125
|
}
|
|
118
126
|
var defaultSend = (res, data) => {
|
|
@@ -134,7 +142,9 @@ function collectRoutesFromStack(appOrRouter) {
|
|
|
134
142
|
const route = layer && layer.route;
|
|
135
143
|
if (!route) continue;
|
|
136
144
|
const paths = Array.isArray(route.path) ? route.path : [route.path];
|
|
137
|
-
const methodEntries = Object.entries(route.methods ?? {}).filter(
|
|
145
|
+
const methodEntries = Object.entries(route.methods ?? {}).filter(
|
|
146
|
+
([, enabled]) => enabled
|
|
147
|
+
);
|
|
138
148
|
for (const path of paths) {
|
|
139
149
|
for (const [method] of methodEntries) {
|
|
140
150
|
result.push(`${method.toUpperCase()} ${path}`);
|
|
@@ -175,11 +185,14 @@ function createRRRoute(router, config) {
|
|
|
175
185
|
let routeDebugEmitter;
|
|
176
186
|
if (defDebug) {
|
|
177
187
|
const { debugName: overrideName, ...rest } = defDebug;
|
|
178
|
-
const hasOverrides = Object.values(rest).some(
|
|
188
|
+
const hasOverrides = Object.values(rest).some(
|
|
189
|
+
(value) => value !== void 0
|
|
190
|
+
);
|
|
179
191
|
if (hasOverrides) {
|
|
180
|
-
routeDebugEmitter = createServerDebugEmitter(
|
|
181
|
-
|
|
182
|
-
|
|
192
|
+
routeDebugEmitter = createServerDebugEmitter({
|
|
193
|
+
...config.debug,
|
|
194
|
+
...rest
|
|
195
|
+
});
|
|
183
196
|
}
|
|
184
197
|
debugName = overrideName ?? debugName;
|
|
185
198
|
}
|
|
@@ -193,7 +206,13 @@ function createRRRoute(router, config) {
|
|
|
193
206
|
const ctxMw = async (req, res, next) => {
|
|
194
207
|
const requestUrl = req.originalUrl ?? path;
|
|
195
208
|
const startedAt = Date.now();
|
|
196
|
-
emit({
|
|
209
|
+
emit({
|
|
210
|
+
type: "buildCtx",
|
|
211
|
+
stage: "start",
|
|
212
|
+
method: methodUpper,
|
|
213
|
+
path,
|
|
214
|
+
url: requestUrl
|
|
215
|
+
});
|
|
197
216
|
try {
|
|
198
217
|
const ctx = await config.buildCtx(req, res);
|
|
199
218
|
res.locals[CTX_SYMBOL] = ctx;
|
|
@@ -220,11 +239,22 @@ function createRRRoute(router, config) {
|
|
|
220
239
|
next(err);
|
|
221
240
|
}
|
|
222
241
|
};
|
|
223
|
-
const before = [
|
|
242
|
+
const before = [
|
|
243
|
+
ctxMw,
|
|
244
|
+
...globalBeforeMws,
|
|
245
|
+
...derived,
|
|
246
|
+
...routeSpecific
|
|
247
|
+
];
|
|
224
248
|
const wrapped = async (req, res, next) => {
|
|
225
249
|
const requestUrl = req.originalUrl.split("?")[0] ?? path;
|
|
226
250
|
const startedAt = Date.now();
|
|
227
|
-
emit({
|
|
251
|
+
emit({
|
|
252
|
+
type: "request",
|
|
253
|
+
stage: "start",
|
|
254
|
+
method: methodUpper,
|
|
255
|
+
path,
|
|
256
|
+
url: requestUrl
|
|
257
|
+
});
|
|
228
258
|
let params;
|
|
229
259
|
let query;
|
|
230
260
|
let body;
|
|
@@ -371,10 +401,13 @@ function createRRRoute(router, config) {
|
|
|
371
401
|
const key = keyOf(leaf);
|
|
372
402
|
knownLeaves.set(key, leaf);
|
|
373
403
|
}
|
|
404
|
+
;
|
|
374
405
|
Object.keys(controllers).forEach((key) => {
|
|
375
406
|
const leaf = registry.byKey[key];
|
|
376
407
|
if (!leaf) {
|
|
377
|
-
logger?.warn?.(
|
|
408
|
+
logger?.warn?.(
|
|
409
|
+
`No leaf found for controller key: ${key}. Not registering route.`
|
|
410
|
+
);
|
|
378
411
|
return;
|
|
379
412
|
}
|
|
380
413
|
const def = controllers[key];
|
|
@@ -399,7 +432,9 @@ function createRRRoute(router, config) {
|
|
|
399
432
|
function warnMissing(registry, warnLogger) {
|
|
400
433
|
const registeredFromStore = new Set(Array.from(registered));
|
|
401
434
|
if (registeredFromStore.size === 0) {
|
|
402
|
-
collectRoutesFromStack(router).forEach(
|
|
435
|
+
collectRoutesFromStack(router).forEach(
|
|
436
|
+
(key) => registeredFromStore.add(key)
|
|
437
|
+
);
|
|
403
438
|
}
|
|
404
439
|
for (const leaf of registry.all) {
|
|
405
440
|
const key = keyOf(leaf);
|
|
@@ -543,7 +578,12 @@ function createBuiltInConnectionHandlers(opts) {
|
|
|
543
578
|
const list = toArray(parsed.data.rooms);
|
|
544
579
|
const join = async (room) => {
|
|
545
580
|
await socket.join(room);
|
|
546
|
-
dbg(null, {
|
|
581
|
+
dbg(null, {
|
|
582
|
+
type: "rooms",
|
|
583
|
+
action: "join",
|
|
584
|
+
rooms: room,
|
|
585
|
+
socketId: socket.id
|
|
586
|
+
});
|
|
547
587
|
};
|
|
548
588
|
const run = async () => {
|
|
549
589
|
await getSysEvent("sys:room_join")({
|
|
@@ -557,7 +597,9 @@ function createBuiltInConnectionHandlers(opts) {
|
|
|
557
597
|
try {
|
|
558
598
|
await run();
|
|
559
599
|
} catch (error) {
|
|
560
|
-
socket.emit(`${roomJoinEvent}:error`, {
|
|
600
|
+
socket.emit(`${roomJoinEvent}:error`, {
|
|
601
|
+
error: normalizeError(error)
|
|
602
|
+
});
|
|
561
603
|
dbg(roomJoinEvent, {
|
|
562
604
|
type: "rooms",
|
|
563
605
|
action: "join",
|
|
@@ -595,7 +637,12 @@ function createBuiltInConnectionHandlers(opts) {
|
|
|
595
637
|
const list = toArray(parsed.data.rooms);
|
|
596
638
|
const leave = async (room) => {
|
|
597
639
|
await socket.leave(room);
|
|
598
|
-
dbg(null, {
|
|
640
|
+
dbg(null, {
|
|
641
|
+
type: "rooms",
|
|
642
|
+
action: "leave",
|
|
643
|
+
rooms: room,
|
|
644
|
+
socketId: socket.id
|
|
645
|
+
});
|
|
599
646
|
};
|
|
600
647
|
const run = async () => {
|
|
601
648
|
await getSysEvent("sys:room_leave")({
|
|
@@ -609,7 +656,9 @@ function createBuiltInConnectionHandlers(opts) {
|
|
|
609
656
|
try {
|
|
610
657
|
await run();
|
|
611
658
|
} catch (error) {
|
|
612
|
-
socket.emit(`${roomLeaveEvent}:error`, {
|
|
659
|
+
socket.emit(`${roomLeaveEvent}:error`, {
|
|
660
|
+
error: normalizeError(error)
|
|
661
|
+
});
|
|
613
662
|
dbg(roomJoinEvent, {
|
|
614
663
|
type: "rooms",
|
|
615
664
|
phase: "handler_error",
|
|
@@ -658,7 +707,12 @@ function createBuiltInConnectionHandlers(opts) {
|
|
|
658
707
|
});
|
|
659
708
|
let pongPayload;
|
|
660
709
|
try {
|
|
661
|
-
pongPayload = await getSysEvent("sys:ping")({
|
|
710
|
+
pongPayload = await getSysEvent("sys:ping")({
|
|
711
|
+
ping: parsedPing.data,
|
|
712
|
+
ctx,
|
|
713
|
+
socket,
|
|
714
|
+
helper
|
|
715
|
+
});
|
|
662
716
|
} catch (error) {
|
|
663
717
|
socket.emit(`${pingEvent}:error`, { error: normalizeError(error) });
|
|
664
718
|
dbg(null, {
|
|
@@ -823,7 +877,9 @@ function createSocketConnections(io, events, opts) {
|
|
|
823
877
|
},
|
|
824
878
|
metadata
|
|
825
879
|
});
|
|
826
|
-
throw new Error(
|
|
880
|
+
throw new Error(
|
|
881
|
+
`Invalid payload for "${String(eventName)}": ${check.error.message}`
|
|
882
|
+
);
|
|
827
883
|
}
|
|
828
884
|
const envelope = {
|
|
829
885
|
eventName,
|
|
@@ -884,7 +940,12 @@ function createSocketConnections(io, events, opts) {
|
|
|
884
940
|
}
|
|
885
941
|
}
|
|
886
942
|
registrations.delete(eventName);
|
|
887
|
-
dbg(eventName, {
|
|
943
|
+
dbg(eventName, {
|
|
944
|
+
type: "register",
|
|
945
|
+
action: "unregister",
|
|
946
|
+
event: eventName,
|
|
947
|
+
msg: "unregistered"
|
|
948
|
+
});
|
|
888
949
|
};
|
|
889
950
|
const {
|
|
890
951
|
builtInConnectionListener,
|
|
@@ -970,7 +1031,10 @@ function createSocketConnections(io, events, opts) {
|
|
|
970
1031
|
});
|
|
971
1032
|
};
|
|
972
1033
|
io.on("connection", connectionListener);
|
|
973
|
-
addRegistration(String(eventName), {
|
|
1034
|
+
addRegistration(String(eventName), {
|
|
1035
|
+
connectionListener,
|
|
1036
|
+
socketListeners
|
|
1037
|
+
});
|
|
974
1038
|
dbg(String(eventName), {
|
|
975
1039
|
type: "register",
|
|
976
1040
|
action: "register",
|
|
@@ -1116,7 +1180,9 @@ var createConnectionLoggingMiddleware = (options = {}) => {
|
|
|
1116
1180
|
const logger = options.logger ?? defaultLogger;
|
|
1117
1181
|
const includeHeaders = options.includeHeaders ?? false;
|
|
1118
1182
|
const redactKeys = new Set(
|
|
1119
|
-
(options.redactAuthKeys ?? ["authorization", "token"]).map(
|
|
1183
|
+
(options.redactAuthKeys ?? ["authorization", "token"]).map(
|
|
1184
|
+
(key) => key.toLowerCase()
|
|
1185
|
+
)
|
|
1120
1186
|
);
|
|
1121
1187
|
return (socket, next) => {
|
|
1122
1188
|
const context = buildContext(socket, includeHeaders, redactKeys);
|