@halooj/framework 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/interface.ts ADDED
@@ -0,0 +1,44 @@
1
+ import type { ApiHandler } from './api';
2
+ import type { ConnectionHandler, Handler, NotFoundHandler } from './server';
3
+
4
+ export interface KnownHandlers {
5
+ NotFound: NotFoundHandler;
6
+ }
7
+
8
+ type MapHandlerEvents<N extends string, H extends Handler> =
9
+ Record<`handler/${HookType}/${N}`, (thisArg: H) => VoidReturn>
10
+ & Record<`handler/${HookWithMethod}/${N}/${Methods}`, (thisArg: H) => VoidReturn>;
11
+
12
+ type KnownHandlerEvents = {
13
+ [key in keyof KnownHandlers]: MapHandlerEvents<key, KnownHandlers[key]>
14
+ }[keyof KnownHandlers];
15
+
16
+ type HandlerEvents =
17
+ Record<`handler/${HookType}`, (thisArg: Handler) => VoidReturn>
18
+ & Record<`connection/${'create' | 'active' | 'close'}`, (thisArg: any) => VoidReturn>;
19
+
20
+ export type VoidReturn = Promise<any> | any;
21
+ export type HookType = 'before-prepare' | 'before' | 'before-operation' | 'after' | 'finish';
22
+ export type Methods = 'get' | 'post' | 'put' | 'delete' | 'patch';
23
+ export type HookWithMethod = Exclude<HookType, 'before-operation'>;
24
+
25
+ export interface ServerEvents extends KnownHandlerEvents, HandlerEvents {
26
+ 'handler/create': (thisArg: Handler | ConnectionHandler, type: 'ws' | 'http') => VoidReturn;
27
+ 'handler/create/http': (thisArg: Handler) => VoidReturn;
28
+ 'handler/create/ws': (thisArg: ConnectionHandler) => VoidReturn;
29
+ 'handler/init': (thisArg: Handler) => VoidReturn;
30
+ 'handler/error': (thisArg: Handler | ConnectionHandler, e: Error) => VoidReturn;
31
+ 'handler/api/before': (thisArg: ApiHandler) => VoidReturn;
32
+ 'api/before': (args: any) => VoidReturn;
33
+ [k: `handler/${HookType}/${string}`]: (thisArg: any) => VoidReturn;
34
+ [k: `handler/${HookWithMethod}/${string}/${Methods}`]: (thisArg: any) => VoidReturn;
35
+ [k: `handler/register/${string}`]: (HandlerClass: new (...args: any[]) => any) => VoidReturn;
36
+ [k: `handler/error/${string}`]: (thisArg: Handler, e: Error) => VoidReturn;
37
+ [k: `handler/api/before/${string}`]: (thisArg: ApiHandler) => VoidReturn;
38
+ [k: `api/before/${string}`]: (args: any) => VoidReturn;
39
+ }
40
+
41
+ declare module 'cordis' {
42
+ interface Events extends ServerEvents {
43
+ }
44
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@halooj/framework",
3
+ "version": "0.3.0",
4
+ "main": "index.ts",
5
+ "license": "MIT",
6
+ "repository": "https://github.com/hydro-dev/Hydro",
7
+ "peerDependencies": {
8
+ "cordis": "*"
9
+ },
10
+ "dependencies": {
11
+ "@koa/router": "^15.6.0",
12
+ "@mongodb-js/saslprep": "^1.4.11",
13
+ "emoji-regex": "^10.6.0",
14
+ "fs-extra": "^11.3.5",
15
+ "koa": "^3.2.1",
16
+ "koa-body": "^8.0.0",
17
+ "koa-compress": "5.2.1",
18
+ "path-to-regexp": "^8.4.2",
19
+ "sanitize-filename": "^1.6.4",
20
+ "schemastery": "^3.18.0",
21
+ "shorty.js": "^1.0.1",
22
+ "ws": "^8.21.0",
23
+ "@halooj/utils": "^1.5.6"
24
+ },
25
+ "devDependencies": {
26
+ "@types/fs-extra": "^11.0.4",
27
+ "@types/koa": "^3.0.3",
28
+ "@types/koa-compress": "^4.0.7",
29
+ "@types/koa__router": "^12.0.5",
30
+ "@types/ws": "^8.18.1"
31
+ },
32
+ "publishConfig": {
33
+ "access": "public"
34
+ }
35
+ }
package/router.ts ADDED
@@ -0,0 +1,69 @@
1
+ import { IncomingMessage } from 'http';
2
+ import KoaRouter, { type RouterInstance } from '@koa/router';
3
+ import { Keys, pathToRegexp } from 'path-to-regexp';
4
+ import type WebSocket from 'ws';
5
+ import type { KoaContext } from './server';
6
+
7
+ type WebSocketCallback = (socket: WebSocket, request: IncomingMessage, ctx: KoaContext) => void;
8
+ function remove<T>(list: T[], item: T) {
9
+ const index = list.indexOf(item);
10
+ if (index >= 0) list.splice(index, 1);
11
+ }
12
+
13
+ export class WebSocketLayer {
14
+ clients = new Set<WebSocket>();
15
+ regexp: RegExp;
16
+ keys: Keys;
17
+
18
+ constructor(path: Parameters<typeof pathToRegexp>[0], public callback?: WebSocketCallback) {
19
+ const r = pathToRegexp(path);
20
+ this.regexp = r.regexp;
21
+ this.keys = r.keys;
22
+ }
23
+
24
+ accept(socket: WebSocket, request: IncomingMessage, ctx: KoaContext) {
25
+ const match = this.regexp.exec(new URL(request.url, `http://${request.headers.host}`).pathname);
26
+ if (!match) return false;
27
+ ctx.params ||= {};
28
+ for (let i = 0; i < this.keys.length; i++) {
29
+ ctx.params[this.keys[i].name] = match[i + 1];
30
+ ctx.HydroContext.args[this.keys[i].name] = match[i + 1];
31
+ }
32
+ this.clients.add(socket);
33
+ socket.on('close', () => {
34
+ this.clients.delete(socket);
35
+ });
36
+ this.callback?.(socket, request, ctx);
37
+ return true;
38
+ }
39
+
40
+ close() {
41
+ for (const socket of this.clients) {
42
+ socket.close();
43
+ }
44
+ }
45
+ }
46
+
47
+ export class Router extends KoaRouter {
48
+ wsStack: WebSocketLayer[] = [];
49
+ disposeLastOp = () => null;
50
+
51
+ /**
52
+ * hack into router methods to make sure that koa middlewares are disposable
53
+ */
54
+ register(...args: Parameters<RouterInstance['register']>) {
55
+ const layer = super.register(...args);
56
+ this.disposeLastOp = () => remove(this.stack, layer);
57
+ return layer;
58
+ }
59
+
60
+ ws(path: Parameters<typeof pathToRegexp>[0], callback?: WebSocketCallback) {
61
+ const layer = new WebSocketLayer(path, callback);
62
+ this.wsStack.push(layer);
63
+ this.disposeLastOp = () => {
64
+ layer.close();
65
+ remove(this.wsStack, layer);
66
+ };
67
+ return layer;
68
+ }
69
+ }
package/serializer.ts ADDED
@@ -0,0 +1,11 @@
1
+ import type { HandlerCommon } from './server';
2
+
3
+ export default function serializer(ignoreSerializeFunction = false, h?: HandlerCommon) {
4
+ return (k: string, v: any) => {
5
+ if (k.startsWith('_') && k !== '_id') return undefined;
6
+ if (typeof v === 'bigint') return `BigInt::${v.toString()}`;
7
+ if (!ignoreSerializeFunction && v && typeof v === 'object'
8
+ && 'serialize' in v && typeof v.serialize === 'function') return v.serialize(h);
9
+ return v;
10
+ };
11
+ }