@vyckr/tachyon 0.2.0 → 1.0.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.
Files changed (49) hide show
  1. package/.env.example +3 -17
  2. package/README.md +87 -57
  3. package/bun.lock +127 -0
  4. package/components/counter.html +13 -0
  5. package/deno.lock +19 -0
  6. package/go.mod +3 -0
  7. package/lib/gson-2.3.jar +0 -0
  8. package/main.js +0 -0
  9. package/package.json +19 -20
  10. package/routes/DELETE +18 -0
  11. package/routes/GET +17 -0
  12. package/routes/HTML +28 -0
  13. package/routes/POST +32 -0
  14. package/routes/SOCKET +26 -0
  15. package/routes/api/:version/DELETE +10 -0
  16. package/routes/api/:version/GET +29 -0
  17. package/routes/api/:version/PATCH +24 -0
  18. package/routes/api/GET +29 -0
  19. package/routes/api/POST +16 -0
  20. package/routes/api/PUT +21 -0
  21. package/src/client/404.html +7 -0
  22. package/src/client/dev.html +14 -0
  23. package/src/client/dist.ts +20 -0
  24. package/src/client/hmr.js +12 -0
  25. package/src/client/prod.html +13 -0
  26. package/src/client/render.js +278 -0
  27. package/src/client/routes.json +1 -0
  28. package/src/client/yon.ts +341 -0
  29. package/src/router.ts +185 -0
  30. package/src/serve.ts +144 -0
  31. package/src/server/logger.ts +31 -0
  32. package/src/server/tach.ts +234 -0
  33. package/tests/index.test.ts +110 -0
  34. package/tests/stream.ts +24 -0
  35. package/tests/worker.ts +7 -0
  36. package/tsconfig.json +1 -1
  37. package/Dockerfile +0 -47
  38. package/bun.lockb +0 -0
  39. package/routes/byos/[primary]/doc/index.ts +0 -28
  40. package/routes/byos/[primary]/docs/index.ts +0 -28
  41. package/routes/byos/[primary]/join/[secondary]/docs/index.ts +0 -10
  42. package/routes/byos/[primary]/schema/index.ts +0 -17
  43. package/routes/byos/[primary]/stream/doc/index.ts +0 -28
  44. package/routes/byos/[primary]/stream/docs/index.ts +0 -28
  45. package/routes/utils/validation.ts +0 -36
  46. package/src/Tach.ts +0 -602
  47. package/src/Yon.ts +0 -25
  48. package/src/runtime.ts +0 -822
  49. package/types/index.d.ts +0 -13
package/src/Tach.ts DELETED
@@ -1,602 +0,0 @@
1
- import { AsyncLocalStorage } from "node:async_hooks";
2
- import { watch } from "node:fs";
3
- import { exists } from "node:fs/promises";
4
- import Silo from "@vyckr/byos";
5
- import { Glob, Server } from "bun";
6
-
7
- const Tach = {
8
-
9
- indexedRoutes: new Map<string, Map<string, Function>>(),
10
-
11
- routeSlugs: new Map<string, Map<string, number>>(),
12
-
13
- allMethods: process.env.ALLOW_METHODS ? process.env.ALLOW_METHODS.split(',') : ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
14
-
15
- inDevelopment: process.env.PRODUCTION ? false : true,
16
-
17
- headers: {
18
- "Access-Control-Allow-Headers": process.env.ALLOW_HEADERS || "",
19
- "Access-Control-Allow-Origin": process.env.ALLLOW_ORGINS || "",
20
- "Access-Control-Allow-Credential": process.env.ALLOW_CREDENTIALS || "false",
21
- "Access-Control-Expose-Headers": process.env.ALLOW_EXPOSE_HEADERS || "",
22
- "Access-Control-Max-Age": process.env.ALLOW_MAX_AGE || "",
23
- "Access-Control-Allow-Methods": process.env.ALLOW_METHODS || ""
24
- },
25
-
26
- dbPath: process.env.DB_DIR,
27
-
28
- saveStats: process.env.SAVE_STATS === 'true',
29
- saveRequests: process.env.SAVE_REQUESTS === 'true',
30
- saveErrors: process.env.SAVE_ERRORS === 'true',
31
- saveLogs: process.env.SAVE_LOGS === 'true',
32
-
33
- logsTableName: "_logs",
34
- errorsTableName: "_errors",
35
- requestTableName: "_requests",
36
- statsTableName: "_stats",
37
-
38
- context: new AsyncLocalStorage<_log[]>(),
39
-
40
- routesPath: process.env.LAMBDA_TASK_ROOT ? `${process.env.LAMBDA_TASK_ROOT}/routes` : `${process.cwd()}/routes`,
41
-
42
- hasMiddleware: await exists(`${process.env.LAMBDA_TASK_ROOT || process.cwd()}/routes/middleware.ts`) || await exists(`${process.env.LAMBDA_TASK_ROOT || process.cwd()}/routes/middleware.js`) ,
43
-
44
- pathsMatch(routeSegs: string[], pathSegs: string[]) {
45
-
46
- if (routeSegs.length !== pathSegs.length) {
47
- return false;
48
- }
49
-
50
- const slugs = Tach.routeSlugs.get(`${routeSegs.join('/')}/index.ts`) || Tach.routeSlugs.get(`${routeSegs.join('/')}/index.js`) || new Map<string, number>()
51
-
52
- for (let i = 0; i < routeSegs.length; i++) {
53
- if (!slugs.has(routeSegs[i]) && routeSegs[i] !== pathSegs[i]) {
54
- return false;
55
- }
56
- }
57
-
58
- return true;
59
- },
60
-
61
- getHandler(request: Request) {
62
-
63
- const url = new URL(request.url);
64
-
65
- let handler;
66
- let params: string[] = [];
67
- const paths = url.pathname.split('/').slice(1);
68
- const allowedMethods: string[] = [];
69
-
70
- let slugs = new Map<string, string>()
71
-
72
- let bestMatchKey = '';
73
- let bestMatchLength = -1;
74
-
75
- for (const [routeKey] of Tach.indexedRoutes) {
76
-
77
- const routeSegs = routeKey.split('/')
78
-
79
- routeSegs.pop()
80
-
81
- const isMatch = Tach.pathsMatch(routeSegs, paths.slice(0, routeSegs.length));
82
-
83
- if (isMatch && routeSegs.length > bestMatchLength) {
84
- bestMatchKey = routeKey;
85
- bestMatchLength = routeSegs.length;
86
- }
87
- }
88
-
89
- if (bestMatchKey) {
90
- const routeMap = Tach.indexedRoutes.get(bestMatchKey)!
91
- handler = routeMap.get(request.method);
92
-
93
- for (const [key] of routeMap) {
94
- if (Tach.allMethods.includes(key)) allowedMethods.push(key);
95
- }
96
-
97
- params = paths.slice(bestMatchLength);
98
-
99
- const slugMap = Tach.routeSlugs.get(bestMatchKey) ?? new Map<string, number>()
100
-
101
- slugMap.forEach((idx, key) => slugs.set(key, paths[idx]))
102
- }
103
-
104
- Tach.headers = { ...Tach.headers, "Access-Control-Allow-Methods": allowedMethods.join(',') };
105
-
106
- if (!handler) throw new Error(`Route ${request.method} ${url.pathname} not found`, { cause: 404 });
107
-
108
- return { handler, params: Tach.parseParams(params), slugs }
109
- },
110
-
111
- formatDate() {
112
- return new Date().toISOString().replace('T', ' ').replace('Z', '')
113
- },
114
-
115
- formatMsg(...msg: any[]) {
116
-
117
- if(msg instanceof Set) return "\n" + JSON.stringify(Array.from(msg), null, 2)
118
-
119
- else if(msg instanceof Map) return "\n" + JSON.stringify(Object.fromEntries(msg), null, 2)
120
-
121
- else if(msg instanceof FormData) {
122
- const formEntries: Record<string, any> = {}
123
- msg.forEach((val, key) => formEntries[key] = val)
124
- return "\n" + JSON.stringify(formEntries, null, 2)
125
- }
126
-
127
- else if(Array.isArray(msg)
128
- || (typeof msg === 'object' && !Array.isArray(msg))
129
- || (typeof msg === 'object' && msg !== null)) return "\n" + JSON.stringify(msg, null, 2)
130
-
131
- return msg
132
- },
133
-
134
- configLogger() {
135
-
136
- const logger = console.log
137
-
138
- function log(...args: any[]): void {
139
-
140
- if (!args.length) {
141
- return;
142
- }
143
-
144
- const messages = args.map(arg => Bun.inspect(arg).replace(/\n/g, "\r"));
145
-
146
- logger(...messages);
147
- }
148
-
149
- const reset = '\x1b[0m'
150
-
151
- console.info = (...args: any[]) => {
152
- const info = `[${Tach.formatDate()}]\x1b[32m INFO${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
153
- log(info)
154
- if(Tach.context.getStore()) {
155
- const logWriter = Tach.context.getStore()
156
- if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${info.replace(reset, '').replace('\x1b[32m', '')}\n`, type: "info" })
157
- }
158
- }
159
-
160
- console.error = (...args: any[]) => {
161
- const err = `[${Tach.formatDate()}]\x1b[31m ERROR${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
162
- log(err)
163
- if(Tach.context.getStore()) {
164
- const logWriter = Tach.context.getStore()
165
- if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${err.replace(reset, '').replace('\x1b[31m', '')}\n`, type: "error" })
166
- }
167
- }
168
-
169
- console.debug = (...args: any[]) => {
170
- const bug = `[${Tach.formatDate()}]\x1b[36m DEBUG${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
171
- log(bug)
172
- if(Tach.context.getStore()) {
173
- const logWriter = Tach.context.getStore()
174
- if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${bug.replace(reset, '').replace('\x1b[36m', '')}\n`, type: "debug" })
175
- }
176
- }
177
-
178
- console.warn = (...args: any[]) => {
179
- const warn = `[${Tach.formatDate()}]\x1b[33m WARN${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
180
- log(warn)
181
- if(Tach.context.getStore()) {
182
- const logWriter = Tach.context.getStore()
183
- if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${warn.replace(reset, '').replace('\x1b[33m', '')}\n`, type: "warn" })
184
- }
185
- }
186
-
187
- console.trace = (...args: any[]) => {
188
- const trace = `[${Tach.formatDate()}]\x1b[35m TRACE${reset} (${process.pid}) ${Tach.formatMsg(...args)}`
189
- log(trace)
190
- if(Tach.context.getStore()) {
191
- const logWriter = Tach.context.getStore()
192
- if(logWriter && Tach.dbPath && Tach.saveLogs) logWriter.push({ date: Date.now(), msg: `${trace.replace(reset, '').replace('\x1b[35m', '')}\n`, type: "trace" })
193
- }
194
- }
195
- },
196
-
197
- async logRequest(request: Request, status: number, context: _HTTPContext, data: any = null) {
198
-
199
- if(Tach.dbPath && Tach.saveRequests) {
200
-
201
- const url = new URL(request.url)
202
- const date = Date.now()
203
- const duration = date - (context.requestTime ?? 0)
204
-
205
- await Silo.putData(Tach.requestTableName, { ipAddress: context.ipAddress, url: `${url.pathname}${url.search}`, method: request.method, status, duration, date, size: data ? String(data).length : 0, data })
206
- }
207
- },
208
-
209
- async processRequest(request: Request, context: _HTTPContext) {
210
-
211
- const { handler, params, slugs } = Tach.getHandler(request)
212
-
213
- if(slugs.size > 0) context.slugs = slugs
214
-
215
- const body = await request.blob()
216
-
217
- let data: Blob | Record<string, any> | undefined
218
-
219
- if(body.size > 0) {
220
-
221
- if(body.type.includes('form')) data = Tach.parseKVParams(await body.formData())
222
- else {
223
- try {
224
- data = await body.json()
225
- } catch {
226
- data = body
227
- }
228
- }
229
- }
230
-
231
- const searchParams = new URL(request.url).searchParams
232
-
233
- let queryParams: Record<string, any> | undefined;
234
-
235
- if(searchParams.size > 0) queryParams = Tach.parseKVParams(searchParams)
236
-
237
- const middlewarePath = await exists(`${Tach.routesPath}/middleware.ts`) ? `${Tach.routesPath}/middleware.ts` : `${Tach.routesPath}/middleware.js`
238
-
239
- if(params.length > 0 && !queryParams && !data) {
240
-
241
- let res = undefined
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)
250
-
251
- await Tach.logRequest(request, 200, context)
252
-
253
- return res
254
-
255
- } else if(params.length === 0 && queryParams && !data) {
256
-
257
- let res = undefined
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)
266
-
267
- await Tach.logRequest(request, 200, context)
268
-
269
- return res
270
-
271
- } else if(params.length === 0 && !queryParams && data) {
272
-
273
- let res = undefined
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)
282
-
283
- await Tach.logRequest(request, 200, context, await body.text())
284
-
285
- return res
286
-
287
- } else if(params.length > 0 && queryParams && !data) {
288
-
289
- let res = undefined
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)
298
-
299
- await Tach.logRequest(request, 200, context)
300
-
301
- return res
302
-
303
- } else if(params.length > 0 && !queryParams && data) {
304
-
305
- let res = undefined
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)
314
-
315
- await Tach.logRequest(request, 200, context, await body.text())
316
-
317
- return res
318
-
319
- } else if(params.length === 0 && data && queryParams) {
320
-
321
- let res = undefined
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)
330
-
331
- await Tach.logRequest(request, 200, context, await body.text())
332
-
333
- return res
334
-
335
- } else if(params.length > 0 && data && queryParams) {
336
-
337
- let res = undefined
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)
346
-
347
- await Tach.logRequest(request, 200, context, await body.text())
348
-
349
- return res
350
-
351
- } else {
352
-
353
- let res = undefined
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)
362
-
363
- await Tach.logRequest(request, 200, context)
364
-
365
- return res
366
- }
367
- },
368
-
369
- isAsyncIterator(data: any) {
370
- return typeof data === "object" && Object.hasOwn(data, Symbol.asyncIterator)
371
- },
372
-
373
- hasFunctions(data: any) {
374
- return typeof data === "object" && (Object.keys(data).some((elem) => typeof elem === "function") || Object.values(data).some((elem) => typeof elem === "function"))
375
- },
376
-
377
- processResponse(status: number, data?: any) {
378
-
379
- const headers = Tach.headers
380
-
381
- if(data instanceof Set) return Response.json(Array.from(data), { status, headers })
382
-
383
- if(data instanceof Map) return Response.json(Object.fromEntries(data), { status, headers })
384
-
385
- if(data instanceof FormData || data instanceof Blob) return new Response(data, { status, headers })
386
-
387
- if(typeof data === "object" && !Array.isArray(data) && !Tach.isAsyncIterator(data) && !Tach.hasFunctions(data)) return Response.json(data, { status, headers })
388
-
389
- if((typeof data === "object" && Array.isArray(data)) || data instanceof Array) return Response.json(data, { status, headers })
390
-
391
- if(typeof data === "number" || typeof data === "boolean") return Response.json(data, { status, headers })
392
-
393
- return new Response(data, { status, headers })
394
- },
395
-
396
- async logError(e: Error, ipAddress: string, url: URL, method: string, logs: _log[], startTime?: number) {
397
-
398
- const path = url.pathname
399
-
400
- if(logs.length > 0 && Tach.saveLogs && Tach.dbPath) await Promise.all(logs.map(log => {
401
- return Silo.putData(Tach.logsTableName, { ipAddress, path, method, ...log })
402
- }))
403
-
404
- if(Tach.dbPath && Tach.saveErrors) await Silo.putData(Tach.errorsTableName, { ipAddress, date: Date.now(),path, method, error: e.message })
405
-
406
- console.error(`"${method} ${path}" ${e.cause as number ?? 500} ${startTime ? `- ${Date.now() - startTime}ms` : ''} - ${e.message.length} byte(s)`)
407
- },
408
-
409
- watchFiles() {
410
-
411
- if(Tach.inDevelopment) {
412
-
413
- watch(Tach.routesPath, { recursive: true }, async (ev, filename) => {
414
- delete import.meta.require.cache[`${Tach.routesPath}/${filename}`]
415
- if(!filename?.split('/').some((path) => path.startsWith('_'))) await Tach.validateRoutes(filename!)
416
- })
417
- }
418
- },
419
-
420
- async fetch(req: Request, server: Server) {
421
-
422
- const request = req.clone()
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'
431
-
432
- return await Tach.context.run(logs, async () => {
433
-
434
- let res: Response
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() })
467
-
468
- res = Response.json({ detail: (e as Error).message }, { status: (e as Error).cause as number ?? 500, headers: Tach.headers })
469
- }
470
-
471
- return res
472
- })
473
- },
474
-
475
- async validateRoutes(route?: string) {
476
-
477
- const staticPaths: string[] = []
478
-
479
- const validateRoute = async (route: string) => {
480
-
481
- const paths = route.split('/')
482
-
483
- const pattern = /[<>|\[\]]/
484
-
485
- const slugs = new Map<string, number>()
486
-
487
- if(pattern.test(paths[0]) || pattern.test(paths[paths.length - 1])) throw new Error(`Invalid route ${route}`)
488
-
489
- paths.forEach((path, idx) => {
490
-
491
- if(pattern.test(path) && (pattern.test(paths[idx - 1]) || pattern.test(paths[idx + 1]))) {
492
- throw new Error(`Invalid route ${route}`)
493
- }
494
-
495
- if(pattern.test(path)) slugs.set(path, idx)
496
- })
497
-
498
- const staticPath = paths.filter((path) => !pattern.test(path)).join(',')
499
-
500
- if(staticPaths.includes(staticPath)) throw new Error(`Duplicate route ${route}`)
501
-
502
- staticPaths.push(staticPath)
503
-
504
- const module = await import(`${Tach.routesPath}/${route}`)
505
-
506
- const controller = (new module.default() as any).constructor
507
-
508
- const methodFuncs = new Map<string, Function>()
509
-
510
- for(const method of Tach.allMethods) {
511
-
512
- if(controller[method]) {
513
-
514
- methodFuncs.set(method, controller[method])
515
- }
516
- }
517
-
518
- Tach.indexedRoutes.set(route, methodFuncs)
519
-
520
- if(slugs.size > 0) Tach.routeSlugs.set(route, slugs)
521
- }
522
-
523
- if(route) return await validateRoute(route)
524
-
525
- const routes = Array.from(new Glob(`**/*/index.{ts,js}`).scanSync({ cwd: Tach.routesPath }))
526
-
527
- for(const route of routes) await validateRoute(route)
528
- },
529
-
530
- parseParams(input: string[]) {
531
-
532
- const params: (string | boolean | number | null | undefined)[] = []
533
-
534
- for(const param of input) {
535
-
536
- const num = Number(param)
537
-
538
- if(!Number.isNaN(num)) params.push(num)
539
-
540
- else if(param === 'true') params.push(true)
541
-
542
- else if(param === 'false') params.push(false)
543
-
544
- else if(param === 'null') params.push(null)
545
-
546
- else if(param === 'undefined') params.push(undefined)
547
-
548
- else params.push(param)
549
- }
550
-
551
- return params
552
- },
553
-
554
- parseKVParams(input: URLSearchParams | FormData) {
555
-
556
- const params: Record<string, any> = {}
557
-
558
- for(const [key, val] of input) {
559
-
560
- if(typeof val === "string") {
561
-
562
- try {
563
-
564
- params[key] = JSON.parse(val)
565
-
566
- } catch {
567
-
568
- const num = Number(val)
569
-
570
- if(!Number.isNaN(num)) params[key] = num
571
-
572
- else if(val === 'true') params[key] = true
573
-
574
- else if(val === 'false') params[key] = false
575
-
576
- else if(typeof val === "string" && val.includes(',')) params[key] = Tach.parseParams(val.split(','))
577
-
578
- else if(val === 'null') params[key] = null
579
-
580
- if(params[key] === undefined) params[key] = val
581
- }
582
-
583
- } else params[key] = val
584
- }
585
-
586
- return params
587
- }
588
- }
589
-
590
- try {
591
-
592
- await Tach.validateRoutes()
593
-
594
- Tach.watchFiles()
595
-
596
- Tach.configLogger()
597
-
598
- } catch(e) {
599
- console.log(`Tach.ts --> ${e}`)
600
- }
601
-
602
- export default Tach
package/src/Yon.ts DELETED
@@ -1,25 +0,0 @@
1
- #!/usr/bin/env bun
2
- import Tach from "./Tach.js"
3
- import Silo from "@vyckr/byos";
4
-
5
- try {
6
-
7
- const start = Date.now()
8
-
9
- const server = Bun.serve({ fetch: Tach.fetch, async error(req) {
10
-
11
- if(Tach.dbPath && Tach.saveStats) await Silo.putData(Tach.statsTableName, { cpu: process.cpuUsage(), memory: process.memoryUsage(), date: Date.now() })
12
-
13
- return Response.json({ detail: req.message }, { status: req.cause as number ?? 500, headers: Tach.headers })
14
- },
15
- development: Tach.inDevelopment,
16
- port: process.env.PORT || 8000
17
- })
18
-
19
- process.on('SIGINT', () => process.exit(0))
20
-
21
- console.info(`Live Server is running on http://${server.hostname}:${server.port} (Press CTRL+C to quit) - StartUp Time: ${Date.now() - start}ms`)
22
-
23
- } catch(e) {
24
- if(e instanceof Error) console.error(e.message)
25
- }