@vyckr/tachyon 0.2.0 → 0.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/package.json +1 -1
- package/routes/proxy.ts +8 -0
- package/src/Tach.ts +93 -152
package/package.json
CHANGED
package/routes/proxy.ts
ADDED
package/src/Tach.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
2
|
import { watch } from "node:fs";
|
|
3
|
-
import { exists } from "node:fs/promises";
|
|
4
3
|
import Silo from "@vyckr/byos";
|
|
5
4
|
import { Glob, Server } from "bun";
|
|
6
5
|
|
|
@@ -39,8 +38,63 @@ const Tach = {
|
|
|
39
38
|
|
|
40
39
|
routesPath: process.env.LAMBDA_TASK_ROOT ? `${process.env.LAMBDA_TASK_ROOT}/routes` : `${process.cwd()}/routes`,
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
proxyMod: null,
|
|
43
42
|
|
|
43
|
+
async proxy(req: Request, server?: Server) {
|
|
44
|
+
|
|
45
|
+
const request = req.clone()
|
|
46
|
+
|
|
47
|
+
const logs: _log[] = []
|
|
48
|
+
|
|
49
|
+
const url = new URL(req.url)
|
|
50
|
+
|
|
51
|
+
const startTime = Date.now()
|
|
52
|
+
|
|
53
|
+
const ipAddress = server && server.requestIP ? server.requestIP(req)!.address : '0.0.0.0'
|
|
54
|
+
|
|
55
|
+
return await Tach.context.run(logs, async () => {
|
|
56
|
+
|
|
57
|
+
let res: Response
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
|
|
61
|
+
const data = await Tach.processRequest(req, { ipAddress, request: req, requestTime: startTime, logs, slugs: new Map<string, any>() })
|
|
62
|
+
|
|
63
|
+
res = Tach.processResponse(200, data)
|
|
64
|
+
|
|
65
|
+
if(logs.length > 0 && Tach.saveLogs && Tach.dbPath) await Promise.all(logs.map(log => {
|
|
66
|
+
return Silo.putData(Tach.logsTableName, { ipAddress, path: url.pathname, method: req.method, ...log })
|
|
67
|
+
}))
|
|
68
|
+
|
|
69
|
+
if(!Tach.isAsyncIterator(data)) {
|
|
70
|
+
|
|
71
|
+
const status = res.status
|
|
72
|
+
const response_size = typeof data !== "undefined" ? String(data).length : 0
|
|
73
|
+
const url = new URL(req.url)
|
|
74
|
+
const method = req.method
|
|
75
|
+
const date = Date.now()
|
|
76
|
+
const duration = date - startTime
|
|
77
|
+
|
|
78
|
+
console.info(`"${method} ${url.pathname}" ${status} - ${duration}ms - ${response_size} byte(s)`)
|
|
79
|
+
|
|
80
|
+
if(Tach.dbPath && Tach.saveStats) await Silo.putData(Tach.statsTableName, { ipAddress, cpu: process.cpuUsage(), memory: process.memoryUsage(), date: Date.now() })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
} catch(e) {
|
|
84
|
+
|
|
85
|
+
const method = request.method
|
|
86
|
+
|
|
87
|
+
await Tach.logError(e as Error, ipAddress, url, method, logs, startTime)
|
|
88
|
+
|
|
89
|
+
if(Tach.dbPath && Tach.saveStats) await Silo.putData(Tach.statsTableName, { ipAddress, cpu: process.cpuUsage(), memory: process.memoryUsage(), date: Date.now() })
|
|
90
|
+
|
|
91
|
+
res = Response.json({ detail: (e as Error).message }, { status: (e as Error).cause as number ?? 500, headers: Tach.headers })
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return res
|
|
95
|
+
})
|
|
96
|
+
},
|
|
97
|
+
|
|
44
98
|
pathsMatch(routeSegs: string[], pathSegs: string[]) {
|
|
45
99
|
|
|
46
100
|
if (routeSegs.length !== pathSegs.length) {
|
|
@@ -114,43 +168,35 @@ const Tach = {
|
|
|
114
168
|
|
|
115
169
|
formatMsg(...msg: any[]) {
|
|
116
170
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
else if(msg instanceof Map) return "\n" + JSON.stringify(Object.fromEntries(msg), null, 2)
|
|
171
|
+
const formatted: string[] = []
|
|
120
172
|
|
|
121
|
-
|
|
122
|
-
const formEntries: Record<string, any> = {}
|
|
123
|
-
msg.forEach((val, key) => formEntries[key] = val)
|
|
124
|
-
return "\n" + JSON.stringify(formEntries, null, 2)
|
|
125
|
-
}
|
|
173
|
+
for(const arg of msg) {
|
|
126
174
|
|
|
127
|
-
|
|
128
|
-
|| (typeof msg === 'object' && !Array.isArray(msg))
|
|
129
|
-
|| (typeof msg === 'object' && msg !== null)) return "\n" + JSON.stringify(msg, null, 2)
|
|
175
|
+
if(arg instanceof Set) formatted.push(JSON.stringify(Array.from(arg), null, 2))
|
|
130
176
|
|
|
131
|
-
|
|
132
|
-
},
|
|
177
|
+
else if(arg instanceof Map) formatted.push(JSON.stringify(Object.fromEntries(arg), null, 2))
|
|
133
178
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
function log(...args: any[]): void {
|
|
139
|
-
|
|
140
|
-
if (!args.length) {
|
|
141
|
-
return;
|
|
179
|
+
else if(arg instanceof FormData) {
|
|
180
|
+
const formEntries: Record<string, any> = {}
|
|
181
|
+
arg.forEach((val, key) => formEntries[key] = val)
|
|
182
|
+
formatted.push(JSON.stringify(formEntries, null, 2))
|
|
142
183
|
}
|
|
143
184
|
|
|
144
|
-
|
|
185
|
+
else if(Array.isArray(arg) || (typeof arg === 'object' && !Array.isArray(arg)) || (typeof arg === 'object' && arg !== null)) formatted.push(JSON.stringify(arg, null, 2))
|
|
145
186
|
|
|
146
|
-
|
|
187
|
+
else formatted.push(arg)
|
|
147
188
|
}
|
|
148
189
|
|
|
190
|
+
return formatted.join('\n\n')
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
configLogger() {
|
|
194
|
+
|
|
149
195
|
const reset = '\x1b[0m'
|
|
150
196
|
|
|
151
197
|
console.info = (...args: any[]) => {
|
|
152
198
|
const info = `[${Tach.formatDate()}]\x1b[32m INFO${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
|
|
153
|
-
log(info)
|
|
199
|
+
console.log(info)
|
|
154
200
|
if(Tach.context.getStore()) {
|
|
155
201
|
const logWriter = Tach.context.getStore()
|
|
156
202
|
if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${info.replace(reset, '').replace('\x1b[32m', '')}\n`, type: "info" })
|
|
@@ -159,7 +205,7 @@ const Tach = {
|
|
|
159
205
|
|
|
160
206
|
console.error = (...args: any[]) => {
|
|
161
207
|
const err = `[${Tach.formatDate()}]\x1b[31m ERROR${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
|
|
162
|
-
log(err)
|
|
208
|
+
console.log(err)
|
|
163
209
|
if(Tach.context.getStore()) {
|
|
164
210
|
const logWriter = Tach.context.getStore()
|
|
165
211
|
if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${err.replace(reset, '').replace('\x1b[31m', '')}\n`, type: "error" })
|
|
@@ -168,7 +214,7 @@ const Tach = {
|
|
|
168
214
|
|
|
169
215
|
console.debug = (...args: any[]) => {
|
|
170
216
|
const bug = `[${Tach.formatDate()}]\x1b[36m DEBUG${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
|
|
171
|
-
log(bug)
|
|
217
|
+
console.log(bug)
|
|
172
218
|
if(Tach.context.getStore()) {
|
|
173
219
|
const logWriter = Tach.context.getStore()
|
|
174
220
|
if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${bug.replace(reset, '').replace('\x1b[36m', '')}\n`, type: "debug" })
|
|
@@ -177,7 +223,7 @@ const Tach = {
|
|
|
177
223
|
|
|
178
224
|
console.warn = (...args: any[]) => {
|
|
179
225
|
const warn = `[${Tach.formatDate()}]\x1b[33m WARN${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
|
|
180
|
-
log(warn)
|
|
226
|
+
console.log(warn)
|
|
181
227
|
if(Tach.context.getStore()) {
|
|
182
228
|
const logWriter = Tach.context.getStore()
|
|
183
229
|
if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${warn.replace(reset, '').replace('\x1b[33m', '')}\n`, type: "warn" })
|
|
@@ -186,7 +232,7 @@ const Tach = {
|
|
|
186
232
|
|
|
187
233
|
console.trace = (...args: any[]) => {
|
|
188
234
|
const trace = `[${Tach.formatDate()}]\x1b[35m TRACE${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
|
|
189
|
-
log(trace)
|
|
235
|
+
console.log(trace)
|
|
190
236
|
if(Tach.context.getStore()) {
|
|
191
237
|
const logWriter = Tach.context.getStore()
|
|
192
238
|
if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${trace.replace(reset, '').replace('\x1b[35m', '')}\n`, type: "trace" })
|
|
@@ -234,19 +280,9 @@ const Tach = {
|
|
|
234
280
|
|
|
235
281
|
if(searchParams.size > 0) queryParams = Tach.parseKVParams(searchParams)
|
|
236
282
|
|
|
237
|
-
const middlewarePath = await exists(`${Tach.routesPath}/middleware.ts`) ? `${Tach.routesPath}/middleware.ts` : `${Tach.routesPath}/middleware.js`
|
|
238
|
-
|
|
239
283
|
if(params.length > 0 && !queryParams && !data) {
|
|
240
284
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
if(Tach.hasMiddleware) {
|
|
244
|
-
|
|
245
|
-
const middleware = (await import(middlewarePath)).default
|
|
246
|
-
|
|
247
|
-
res = await middleware(async () => handler(...params, context))
|
|
248
|
-
|
|
249
|
-
} else res = await handler(...params, context)
|
|
285
|
+
const res = await handler(...params, context)
|
|
250
286
|
|
|
251
287
|
await Tach.logRequest(request, 200, context)
|
|
252
288
|
|
|
@@ -254,15 +290,7 @@ const Tach = {
|
|
|
254
290
|
|
|
255
291
|
} else if(params.length === 0 && queryParams && !data) {
|
|
256
292
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if(Tach.hasMiddleware) {
|
|
260
|
-
|
|
261
|
-
const middleware = (await import(middlewarePath)).default
|
|
262
|
-
|
|
263
|
-
res = await middleware(async () => handler(queryParams, context))
|
|
264
|
-
|
|
265
|
-
} else res = await handler(queryParams, context)
|
|
293
|
+
const res = await handler(queryParams, context)
|
|
266
294
|
|
|
267
295
|
await Tach.logRequest(request, 200, context)
|
|
268
296
|
|
|
@@ -270,15 +298,7 @@ const Tach = {
|
|
|
270
298
|
|
|
271
299
|
} else if(params.length === 0 && !queryParams && data) {
|
|
272
300
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
if(Tach.hasMiddleware) {
|
|
276
|
-
|
|
277
|
-
const middleware = (await import(middlewarePath)).default
|
|
278
|
-
|
|
279
|
-
res = await middleware(async () => handler(data, context))
|
|
280
|
-
|
|
281
|
-
} else res = await handler(data, context)
|
|
301
|
+
const res = await handler(data, context)
|
|
282
302
|
|
|
283
303
|
await Tach.logRequest(request, 200, context, await body.text())
|
|
284
304
|
|
|
@@ -286,15 +306,7 @@ const Tach = {
|
|
|
286
306
|
|
|
287
307
|
} else if(params.length > 0 && queryParams && !data) {
|
|
288
308
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
if(Tach.hasMiddleware) {
|
|
292
|
-
|
|
293
|
-
const middleware = (await import(middlewarePath)).default
|
|
294
|
-
|
|
295
|
-
res = await middleware(async () => handler(...params, queryParams, context))
|
|
296
|
-
|
|
297
|
-
} else res = await handler(...params, queryParams, context)
|
|
309
|
+
const res = await handler(...params, queryParams, context)
|
|
298
310
|
|
|
299
311
|
await Tach.logRequest(request, 200, context)
|
|
300
312
|
|
|
@@ -302,15 +314,7 @@ const Tach = {
|
|
|
302
314
|
|
|
303
315
|
} else if(params.length > 0 && !queryParams && data) {
|
|
304
316
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if(Tach.hasMiddleware) {
|
|
308
|
-
|
|
309
|
-
const middleware = (await import(middlewarePath)).default
|
|
310
|
-
|
|
311
|
-
res = await middleware(async () => handler(...params, data, context))
|
|
312
|
-
|
|
313
|
-
} else res = await handler(...params, data, context)
|
|
317
|
+
const res = await handler(...params, data, context)
|
|
314
318
|
|
|
315
319
|
await Tach.logRequest(request, 200, context, await body.text())
|
|
316
320
|
|
|
@@ -318,15 +322,7 @@ const Tach = {
|
|
|
318
322
|
|
|
319
323
|
} else if(params.length === 0 && data && queryParams) {
|
|
320
324
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if(Tach.hasMiddleware) {
|
|
324
|
-
|
|
325
|
-
const middleware = (await import(middlewarePath)).default
|
|
326
|
-
|
|
327
|
-
res = await middleware(async () => handler(queryParams, data, context))
|
|
328
|
-
|
|
329
|
-
} else res = await handler(queryParams, data, context)
|
|
325
|
+
const res = await handler(queryParams, data, context)
|
|
330
326
|
|
|
331
327
|
await Tach.logRequest(request, 200, context, await body.text())
|
|
332
328
|
|
|
@@ -334,15 +330,7 @@ const Tach = {
|
|
|
334
330
|
|
|
335
331
|
} else if(params.length > 0 && data && queryParams) {
|
|
336
332
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
if(Tach.hasMiddleware) {
|
|
340
|
-
|
|
341
|
-
const middleware = (await import(middlewarePath)).default
|
|
342
|
-
|
|
343
|
-
res = await middleware(async () => handler(...params, queryParams, data, context))
|
|
344
|
-
|
|
345
|
-
} else res = await handler(...params, queryParams, data, context)
|
|
333
|
+
const res = await handler(...params, queryParams, data, context)
|
|
346
334
|
|
|
347
335
|
await Tach.logRequest(request, 200, context, await body.text())
|
|
348
336
|
|
|
@@ -350,15 +338,7 @@ const Tach = {
|
|
|
350
338
|
|
|
351
339
|
} else {
|
|
352
340
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
if(Tach.hasMiddleware) {
|
|
356
|
-
|
|
357
|
-
const middleware = (await import(middlewarePath)).default
|
|
358
|
-
|
|
359
|
-
res = await middleware(async () => handler(context))
|
|
360
|
-
|
|
361
|
-
} else res = await handler(context)
|
|
341
|
+
const res = await handler(context)
|
|
362
342
|
|
|
363
343
|
await Tach.logRequest(request, 200, context)
|
|
364
344
|
|
|
@@ -419,57 +399,14 @@ const Tach = {
|
|
|
419
399
|
|
|
420
400
|
async fetch(req: Request, server: Server) {
|
|
421
401
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
const logs: _log[] = []
|
|
425
|
-
|
|
426
|
-
const url = new URL(req.url)
|
|
427
|
-
|
|
428
|
-
const startTime = Date.now()
|
|
429
|
-
|
|
430
|
-
const ipAddress = server.requestIP ? server.requestIP(req)!.address : '0.0.0.0'
|
|
402
|
+
if(Tach.proxyMod) {
|
|
431
403
|
|
|
432
|
-
|
|
404
|
+
const middleware = (Tach.proxyMod as any).default
|
|
433
405
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
try {
|
|
437
|
-
|
|
438
|
-
const data = await Tach.processRequest(req, { ipAddress, request: req, requestTime: startTime, logs, slugs: new Map<string, any>() })
|
|
439
|
-
|
|
440
|
-
res = Tach.processResponse(200, data)
|
|
441
|
-
|
|
442
|
-
if(logs.length > 0 && Tach.saveLogs && Tach.dbPath) await Promise.all(logs.map(log => {
|
|
443
|
-
return Silo.putData(Tach.logsTableName, { ipAddress, path: url.pathname, method: req.method, ...log })
|
|
444
|
-
}))
|
|
445
|
-
|
|
446
|
-
if(!Tach.isAsyncIterator(data)) {
|
|
447
|
-
|
|
448
|
-
const status = res.status
|
|
449
|
-
const response_size = typeof data !== "undefined" ? String(data).length : 0
|
|
450
|
-
const url = new URL(req.url)
|
|
451
|
-
const method = req.method
|
|
452
|
-
const date = Date.now()
|
|
453
|
-
const duration = date - startTime
|
|
454
|
-
|
|
455
|
-
console.info(`"${method} ${url.pathname}" ${status} - ${duration}ms - ${response_size} byte(s)`)
|
|
456
|
-
|
|
457
|
-
if(Tach.dbPath && Tach.saveStats) await Silo.putData(Tach.statsTableName, { ipAddress, cpu: process.cpuUsage(), memory: process.memoryUsage(), date: Date.now() })
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
} catch(e) {
|
|
461
|
-
|
|
462
|
-
const method = request.method
|
|
463
|
-
|
|
464
|
-
await Tach.logError(e as Error, ipAddress, url, method, logs, startTime)
|
|
465
|
-
|
|
466
|
-
if(Tach.dbPath && Tach.saveStats) await Silo.putData(Tach.statsTableName, { ipAddress, cpu: process.cpuUsage(), memory: process.memoryUsage(), date: Date.now() })
|
|
406
|
+
if(middleware) return await middleware(req, Tach.proxy)
|
|
407
|
+
}
|
|
467
408
|
|
|
468
|
-
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
return res
|
|
472
|
-
})
|
|
409
|
+
return await Tach.proxy(req, server)
|
|
473
410
|
},
|
|
474
411
|
|
|
475
412
|
async validateRoutes(route?: string) {
|
|
@@ -525,6 +462,10 @@ const Tach = {
|
|
|
525
462
|
const routes = Array.from(new Glob(`**/*/index.{ts,js}`).scanSync({ cwd: Tach.routesPath }))
|
|
526
463
|
|
|
527
464
|
for(const route of routes) await validateRoute(route)
|
|
465
|
+
|
|
466
|
+
const proxy = Array.from(new Glob(`**/proxy.{ts,js}`).scanSync({ cwd: Tach.routesPath }))
|
|
467
|
+
|
|
468
|
+
if(proxy[0]) Tach.proxyMod = await import(`${Tach.routesPath}/${proxy[0]}`)
|
|
528
469
|
},
|
|
529
470
|
|
|
530
471
|
parseParams(input: string[]) {
|