sleepy-serv 0.18.0 → 0.19.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 +113 -15
- package/dist/cli/build.d.ts +2 -0
- package/dist/cli/build.d.ts.map +1 -0
- package/dist/cli/dev.d.ts +2 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/init.d.ts +2 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/index.d.ts +17 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/middleware.d.ts.map +1 -0
- package/dist/core/socket.d.ts.map +1 -0
- package/dist/{utils.d.ts → core/utils.d.ts} +13 -0
- package/dist/core/utils.d.ts.map +1 -0
- package/dist/index.d.ts +3 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/plugin/codegen.d.ts +3 -0
- package/dist/plugin/codegen.d.ts.map +1 -0
- package/dist/plugin/config.d.ts +14 -0
- package/dist/plugin/config.d.ts.map +1 -0
- package/dist/plugin/index.d.ts +2 -0
- package/dist/plugin/index.d.ts.map +1 -0
- package/dist/plugin/scanner.d.ts +15 -0
- package/dist/plugin/scanner.d.ts.map +1 -0
- package/package.json +8 -1
- package/src/cli/build.ts +82 -0
- package/src/cli/dev.ts +27 -0
- package/src/cli/index.ts +53 -0
- package/src/cli/init.ts +108 -0
- package/src/core/index.ts +406 -0
- package/src/{utils.ts → core/utils.ts} +16 -0
- package/src/index.ts +5 -571
- package/src/plugin/codegen.ts +101 -0
- package/src/plugin/config.ts +34 -0
- package/src/plugin/index.ts +30 -0
- package/src/plugin/scanner.ts +148 -0
- package/dist/errors.d.ts.map +0 -1
- package/dist/messages.d.ts.map +0 -1
- package/dist/middleware.d.ts.map +0 -1
- package/dist/socket.d.ts.map +0 -1
- package/dist/utils.d.ts.map +0 -1
- /package/dist/{errors.d.ts → core/errors.d.ts} +0 -0
- /package/dist/{messages.d.ts → core/messages.d.ts} +0 -0
- /package/dist/{middleware.d.ts → core/middleware.d.ts} +0 -0
- /package/dist/{socket.d.ts → core/socket.d.ts} +0 -0
- /package/src/{errors.ts → core/errors.ts} +0 -0
- /package/src/{messages.ts → core/messages.ts} +0 -0
- /package/src/{middleware.ts → core/middleware.ts} +0 -0
- /package/src/{socket.ts → core/socket.ts} +0 -0
package/src/cli/init.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
|
|
4
|
+
const TEMPLATES: Record<string, string> = {
|
|
5
|
+
'sleepy.config.ts': 'export default {}',
|
|
6
|
+
|
|
7
|
+
'tsconfig.json': JSON.stringify({
|
|
8
|
+
compilerOptions: {
|
|
9
|
+
strict: true,
|
|
10
|
+
skipLibCheck: true,
|
|
11
|
+
target: 'ESNext',
|
|
12
|
+
module: 'ESNext',
|
|
13
|
+
moduleResolution: 'bundler',
|
|
14
|
+
types: ['bun-types'],
|
|
15
|
+
},
|
|
16
|
+
}, null, 2) + '\n',
|
|
17
|
+
|
|
18
|
+
'src/index.ts': `
|
|
19
|
+
import { createApp } from \'sleepy-serv\'
|
|
20
|
+
|
|
21
|
+
createApp(3000)
|
|
22
|
+
`.trim().concat('\n'),
|
|
23
|
+
|
|
24
|
+
'src/api/get.ts': `
|
|
25
|
+
export default function handler (): Response {
|
|
26
|
+
return Response.json({ ok: true })
|
|
27
|
+
}
|
|
28
|
+
`.trim().concat('\n'),
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function writeIfMissing (filePath: string, content: string): boolean {
|
|
32
|
+
if (fs.existsSync(filePath)) {
|
|
33
|
+
console.warn(` skip: ${filePath} (already exists)`)
|
|
34
|
+
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const dir = path.dirname(filePath)
|
|
39
|
+
|
|
40
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
41
|
+
fs.writeFileSync(filePath, content)
|
|
42
|
+
console.log(` create: ${filePath}`)
|
|
43
|
+
|
|
44
|
+
return true
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function writePackageJson (): void {
|
|
48
|
+
const filePath = 'package.json'
|
|
49
|
+
const name = path.basename(process.cwd())
|
|
50
|
+
|
|
51
|
+
const scripts = {
|
|
52
|
+
dev: 'sleepy dev',
|
|
53
|
+
build: 'sleepy build',
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (fs.existsSync(filePath)) {
|
|
57
|
+
const raw = fs.readFileSync(filePath, 'utf-8')
|
|
58
|
+
const existing = JSON.parse(raw)
|
|
59
|
+
|
|
60
|
+
existing.scripts = {
|
|
61
|
+
...existing.scripts,
|
|
62
|
+
...scripts,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
fs.writeFileSync(
|
|
66
|
+
filePath,
|
|
67
|
+
JSON.stringify(existing, null, 2) + '\n',
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
console.log(` update: ${filePath}`)
|
|
71
|
+
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const pkg = {
|
|
76
|
+
name,
|
|
77
|
+
version: '0.0.0',
|
|
78
|
+
type: 'module',
|
|
79
|
+
scripts,
|
|
80
|
+
dependencies: {
|
|
81
|
+
'sleepy-serv': 'latest',
|
|
82
|
+
},
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
fs.writeFileSync(
|
|
86
|
+
filePath,
|
|
87
|
+
JSON.stringify(pkg, null, 2) + '\n',
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
console.log(` create: ${filePath}`)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export async function init (): Promise<void> {
|
|
94
|
+
console.log('')
|
|
95
|
+
|
|
96
|
+
writePackageJson()
|
|
97
|
+
|
|
98
|
+
for (const [relPath, content] of Object.entries(TEMPLATES)) {
|
|
99
|
+
writeIfMissing(relPath, content)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log('')
|
|
103
|
+
console.log('Created sleepy-serv project in ./')
|
|
104
|
+
console.log('')
|
|
105
|
+
console.log(' bun install')
|
|
106
|
+
console.log(' sleepy dev')
|
|
107
|
+
console.log('')
|
|
108
|
+
}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import querystring from 'querystring'
|
|
2
|
+
import readline from 'node:readline'
|
|
3
|
+
|
|
4
|
+
import { stdin, stdout } from 'node:process'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
toSegments,
|
|
8
|
+
executeMiddlewareChain,
|
|
9
|
+
} from './utils'
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
buildSocketState,
|
|
13
|
+
buildSocketServer,
|
|
14
|
+
buildSocketHandlers,
|
|
15
|
+
buildSocketCommands,
|
|
16
|
+
} from './socket'
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
RequestError,
|
|
20
|
+
NotFoundError,
|
|
21
|
+
MethodNotAllowedError,
|
|
22
|
+
InternalServerError,
|
|
23
|
+
} from './errors'
|
|
24
|
+
|
|
25
|
+
import type { BunRequest } from 'bun'
|
|
26
|
+
|
|
27
|
+
import type {
|
|
28
|
+
AsyncHandlerResult,
|
|
29
|
+
HttpMethod,
|
|
30
|
+
Middleware,
|
|
31
|
+
MiddlewareChain,
|
|
32
|
+
EndpointRequest,
|
|
33
|
+
MetaEntry,
|
|
34
|
+
RouteConfig,
|
|
35
|
+
RouteDefinition,
|
|
36
|
+
SocketCommands,
|
|
37
|
+
AppOptions,
|
|
38
|
+
Server,
|
|
39
|
+
} from './utils'
|
|
40
|
+
|
|
41
|
+
import type {
|
|
42
|
+
SocketRoute,
|
|
43
|
+
SocketState,
|
|
44
|
+
} from './socket'
|
|
45
|
+
|
|
46
|
+
export * from './errors'
|
|
47
|
+
export { StatusCode, CloseCode, CloseReason, HttpMethod } from './utils'
|
|
48
|
+
|
|
49
|
+
export {
|
|
50
|
+
parseJsonBody,
|
|
51
|
+
setValidationFormats,
|
|
52
|
+
validateSchemas,
|
|
53
|
+
} from './middleware'
|
|
54
|
+
|
|
55
|
+
export type { FilterFn, SessionEntry, SocketCommands } from './utils'
|
|
56
|
+
|
|
57
|
+
export type {
|
|
58
|
+
AppOptions,
|
|
59
|
+
AsyncHandlerResult,
|
|
60
|
+
BaseRequest,
|
|
61
|
+
EndpointRequest,
|
|
62
|
+
FormattedError,
|
|
63
|
+
Handler,
|
|
64
|
+
MetaEntry,
|
|
65
|
+
Middleware,
|
|
66
|
+
MiddlewareChain,
|
|
67
|
+
NextFn,
|
|
68
|
+
HandlerResult,
|
|
69
|
+
Request,
|
|
70
|
+
RouteConfig,
|
|
71
|
+
RouteDefinition,
|
|
72
|
+
Server,
|
|
73
|
+
SocketConnection,
|
|
74
|
+
SocketOptions,
|
|
75
|
+
WebSocketRequest,
|
|
76
|
+
} from './utils'
|
|
77
|
+
|
|
78
|
+
export type {
|
|
79
|
+
FormatterField,
|
|
80
|
+
FormatterSchema,
|
|
81
|
+
ValidationSchemas,
|
|
82
|
+
} from './middleware'
|
|
83
|
+
|
|
84
|
+
type OutputRoutes = Record<string, string[]>
|
|
85
|
+
type ServerRoutes = Record<string, Record<string, EndpointHandler>>
|
|
86
|
+
|
|
87
|
+
type EndpointHandler = (
|
|
88
|
+
bunReq: BunRequest,
|
|
89
|
+
server: Server,
|
|
90
|
+
) => AsyncHandlerResult
|
|
91
|
+
|
|
92
|
+
type ChainRoute = {
|
|
93
|
+
method: HttpMethod
|
|
94
|
+
path: string
|
|
95
|
+
chain: MiddlewareChain
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
type ModuleRoute = {
|
|
99
|
+
method: HttpMethod
|
|
100
|
+
path: string
|
|
101
|
+
handler: EndpointHandler
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
type AppRoutes = {
|
|
105
|
+
server: ServerRoutes
|
|
106
|
+
output: OutputRoutes
|
|
107
|
+
socket: SocketRoute[]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
type CloseFn = (force?: boolean) => Promise<void>
|
|
111
|
+
|
|
112
|
+
export type App = {
|
|
113
|
+
server: Server
|
|
114
|
+
routes: OutputRoutes
|
|
115
|
+
ws: SocketCommands
|
|
116
|
+
close: CloseFn
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function methodNotAllowedHandler (_req: unknown): never {
|
|
120
|
+
throw new MethodNotAllowedError()
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function defaultMethodMap (): Record<string, EndpointHandler> {
|
|
124
|
+
return {
|
|
125
|
+
HEAD: methodNotAllowedHandler,
|
|
126
|
+
GET: methodNotAllowedHandler,
|
|
127
|
+
PUT: methodNotAllowedHandler,
|
|
128
|
+
POST: methodNotAllowedHandler,
|
|
129
|
+
PATCH: methodNotAllowedHandler,
|
|
130
|
+
DELETE: methodNotAllowedHandler,
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function buildEndpointRequest (
|
|
135
|
+
bunReq: BunRequest,
|
|
136
|
+
server: Server,
|
|
137
|
+
ws: SocketCommands,
|
|
138
|
+
): EndpointRequest {
|
|
139
|
+
const url = new URL(bunReq.url)
|
|
140
|
+
const qs = url.search.replace('?', '')
|
|
141
|
+
|
|
142
|
+
let bodyPromise: Promise<unknown> | null = null
|
|
143
|
+
|
|
144
|
+
const json = (): Promise<unknown> => {
|
|
145
|
+
if (!bodyPromise) {
|
|
146
|
+
bodyPromise = bunReq.json()
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return bodyPromise
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
method: bunReq.method as HttpMethod,
|
|
154
|
+
route: url.pathname,
|
|
155
|
+
headers: bunReq.headers,
|
|
156
|
+
params: bunReq.params ?? {},
|
|
157
|
+
query: querystring.parse(qs),
|
|
158
|
+
raw: bunReq,
|
|
159
|
+
server,
|
|
160
|
+
json,
|
|
161
|
+
ws,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function normalizeChain (
|
|
166
|
+
route: RouteDefinition,
|
|
167
|
+
): ChainRoute {
|
|
168
|
+
const chain = Array.isArray(route.chain)
|
|
169
|
+
? route.chain
|
|
170
|
+
: [route.chain]
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
method: route.method,
|
|
174
|
+
path: route.path,
|
|
175
|
+
chain,
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function buildMergedRoutes (
|
|
180
|
+
routePaths: ChainRoute[],
|
|
181
|
+
middleware: Middleware[],
|
|
182
|
+
meta: MetaEntry[],
|
|
183
|
+
state: SocketState,
|
|
184
|
+
mountPath: string,
|
|
185
|
+
): ChainRoute[] {
|
|
186
|
+
const socketRoutes = buildSocketHandlers(state)
|
|
187
|
+
|
|
188
|
+
for (const socketRoute of socketRoutes) {
|
|
189
|
+
const mountedPath = `${mountPath}${socketRoute.path}`
|
|
190
|
+
|
|
191
|
+
const targetItem = routePaths.find(item => (
|
|
192
|
+
item.method === socketRoute.method &&
|
|
193
|
+
item.path === mountedPath
|
|
194
|
+
))
|
|
195
|
+
|
|
196
|
+
if (targetItem) {
|
|
197
|
+
targetItem.chain.push(socketRoute.handler)
|
|
198
|
+
|
|
199
|
+
continue
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const metaMiddleware = meta
|
|
203
|
+
.filter(entry => socketRoute.path.startsWith(entry.path))
|
|
204
|
+
.sort((a, b) => a.path.length - b.path.length)
|
|
205
|
+
.flatMap(entry => entry.middleware)
|
|
206
|
+
|
|
207
|
+
routePaths.push({
|
|
208
|
+
method: socketRoute.method,
|
|
209
|
+
path: mountedPath,
|
|
210
|
+
chain: [
|
|
211
|
+
...middleware,
|
|
212
|
+
...metaMiddleware,
|
|
213
|
+
socketRoute.handler,
|
|
214
|
+
],
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return routePaths
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function buildSocketRoutes (mergedRoutes: ChainRoute[]): SocketRoute[] {
|
|
222
|
+
return mergedRoutes.map(route => ({
|
|
223
|
+
...route,
|
|
224
|
+
segments: toSegments(route.path),
|
|
225
|
+
}))
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function buildModuleRoutes (
|
|
229
|
+
socketRoutes: SocketRoute[],
|
|
230
|
+
ws: SocketCommands,
|
|
231
|
+
): ModuleRoute[] {
|
|
232
|
+
return socketRoutes.map(route => {
|
|
233
|
+
const handler: EndpointHandler = async (bunReq, server) => {
|
|
234
|
+
const req = buildEndpointRequest(bunReq, server, ws)
|
|
235
|
+
|
|
236
|
+
return executeMiddlewareChain(req, route.chain)
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
method: route.method,
|
|
241
|
+
path: route.path,
|
|
242
|
+
handler,
|
|
243
|
+
}
|
|
244
|
+
})
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function buildServerRoutes (moduleRoutes: ModuleRoute[]): ServerRoutes {
|
|
248
|
+
return moduleRoutes.reduce<ServerRoutes>(
|
|
249
|
+
(accum, curr) => {
|
|
250
|
+
if (!accum[curr.path]) {
|
|
251
|
+
accum[curr.path] = defaultMethodMap()
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
accum[curr.path][curr.method] = curr.handler
|
|
255
|
+
|
|
256
|
+
return accum
|
|
257
|
+
}, {})
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function buildOutputRoutes (moduleRoutes: ModuleRoute[]): OutputRoutes {
|
|
261
|
+
return moduleRoutes.reduce<OutputRoutes>((accum, curr) => {
|
|
262
|
+
accum[curr.path] = accum[curr.path] || []
|
|
263
|
+
accum[curr.path].push(curr.method)
|
|
264
|
+
|
|
265
|
+
return accum
|
|
266
|
+
}, {})
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function buildRoutes (
|
|
270
|
+
config: RouteConfig,
|
|
271
|
+
state: SocketState,
|
|
272
|
+
ws: SocketCommands,
|
|
273
|
+
opts: AppOptions,
|
|
274
|
+
): AppRoutes {
|
|
275
|
+
const mountPath = opts.mountPath || ''
|
|
276
|
+
const middleware = opts.middleware || []
|
|
277
|
+
const meta = config.meta || []
|
|
278
|
+
|
|
279
|
+
const normalRoutes = config.routes.map(route => {
|
|
280
|
+
const normalized = normalizeChain(route)
|
|
281
|
+
const routePath = normalized.path === '/' ? '' : normalized.path
|
|
282
|
+
|
|
283
|
+
const joinedPath = [mountPath, routePath]
|
|
284
|
+
.filter(item => item)
|
|
285
|
+
.join('') || '/'
|
|
286
|
+
|
|
287
|
+
return {
|
|
288
|
+
method: normalized.method,
|
|
289
|
+
path: joinedPath,
|
|
290
|
+
chain: [
|
|
291
|
+
...middleware,
|
|
292
|
+
...normalized.chain,
|
|
293
|
+
],
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
|
|
297
|
+
const mergedRoutes = buildMergedRoutes(
|
|
298
|
+
normalRoutes,
|
|
299
|
+
middleware,
|
|
300
|
+
meta,
|
|
301
|
+
state,
|
|
302
|
+
mountPath,
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
const socketRoutes = buildSocketRoutes(mergedRoutes)
|
|
306
|
+
const moduleRoutes = buildModuleRoutes(socketRoutes, ws)
|
|
307
|
+
const serverRoutes = buildServerRoutes(moduleRoutes)
|
|
308
|
+
const outputRoutes = buildOutputRoutes(moduleRoutes)
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
server: serverRoutes,
|
|
312
|
+
socket: socketRoutes,
|
|
313
|
+
output: outputRoutes,
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function buildServer (
|
|
318
|
+
port: number,
|
|
319
|
+
routes: AppRoutes,
|
|
320
|
+
state: SocketState,
|
|
321
|
+
ws: SocketCommands,
|
|
322
|
+
opts: AppOptions,
|
|
323
|
+
): Server {
|
|
324
|
+
const hostname = opts.hostname || '0.0.0.0'
|
|
325
|
+
|
|
326
|
+
const websocketServer = buildSocketServer(
|
|
327
|
+
routes.socket,
|
|
328
|
+
state,
|
|
329
|
+
ws,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
return Bun.serve({
|
|
333
|
+
port,
|
|
334
|
+
hostname,
|
|
335
|
+
routes: routes.server,
|
|
336
|
+
websocket: websocketServer,
|
|
337
|
+
async fetch (_req, _server) {
|
|
338
|
+
throw new NotFoundError()
|
|
339
|
+
},
|
|
340
|
+
error (err) {
|
|
341
|
+
console.error(err)
|
|
342
|
+
|
|
343
|
+
const isRequestError = !(err instanceof RequestError)
|
|
344
|
+
const httpError = isRequestError ? new InternalServerError() : err
|
|
345
|
+
const { status } = httpError.constructor as typeof RequestError
|
|
346
|
+
|
|
347
|
+
return Response.json(httpError.output, { status })
|
|
348
|
+
},
|
|
349
|
+
})
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function processIO (server: Server, opts: AppOptions): CloseFn {
|
|
353
|
+
const onClose = opts.onClose || (() => {})
|
|
354
|
+
|
|
355
|
+
console.info(`Running on port: ${server.port}`)
|
|
356
|
+
console.info('')
|
|
357
|
+
console.info('Press Ctrl+D to gracefully shutdown')
|
|
358
|
+
console.info('')
|
|
359
|
+
|
|
360
|
+
const shutdown = async (force = false): Promise<void> => {
|
|
361
|
+
await server.stop(force)
|
|
362
|
+
await onClose()
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (!stdin.isTTY) {
|
|
366
|
+
return shutdown
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const rl = readline.createInterface({
|
|
370
|
+
input: stdin,
|
|
371
|
+
output: stdout,
|
|
372
|
+
})
|
|
373
|
+
|
|
374
|
+
const handleClose = async (): Promise<void> => {
|
|
375
|
+
await shutdown()
|
|
376
|
+
process.exit(0)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
rl.on('close', handleClose)
|
|
380
|
+
|
|
381
|
+
return async (force = false) => {
|
|
382
|
+
rl.off('close', handleClose)
|
|
383
|
+
rl.close()
|
|
384
|
+
|
|
385
|
+
await shutdown(force)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export function createApp (
|
|
390
|
+
port: number,
|
|
391
|
+
config: RouteConfig,
|
|
392
|
+
opts: AppOptions = {},
|
|
393
|
+
): App {
|
|
394
|
+
const state = buildSocketState(opts)
|
|
395
|
+
const ws = buildSocketCommands(state)
|
|
396
|
+
const routes = buildRoutes(config, state, ws, opts)
|
|
397
|
+
const server = buildServer(port, routes, state, ws, opts)
|
|
398
|
+
const close = processIO(server, opts)
|
|
399
|
+
|
|
400
|
+
return {
|
|
401
|
+
routes: routes.output,
|
|
402
|
+
server,
|
|
403
|
+
ws,
|
|
404
|
+
close,
|
|
405
|
+
}
|
|
406
|
+
}
|
|
@@ -204,6 +204,22 @@ export type SocketConnection = {
|
|
|
204
204
|
|
|
205
205
|
export type Server = BunServer<SocketData>
|
|
206
206
|
|
|
207
|
+
export type RouteDefinition = {
|
|
208
|
+
method: HttpMethod
|
|
209
|
+
path: string
|
|
210
|
+
chain: Handler | MiddlewareChain
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export type MetaEntry = {
|
|
214
|
+
path: string
|
|
215
|
+
middleware: Middleware[]
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export type RouteConfig = {
|
|
219
|
+
routes: RouteDefinition[]
|
|
220
|
+
meta?: MetaEntry[]
|
|
221
|
+
}
|
|
222
|
+
|
|
207
223
|
export type AppOptions = {
|
|
208
224
|
hostname?: string
|
|
209
225
|
mountPath?: string
|