topsyde-utils 1.3.2 → 2.0.1
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/dist/index.d.ts +2 -43
- package/dist/index.js +1 -38
- package/dist/index.js.map +1 -1
- package/dist/utils/Lib.d.ts +0 -12
- package/dist/utils/Lib.js +0 -65
- package/dist/utils/Lib.js.map +1 -1
- package/dist/utils/index.d.ts +0 -3
- package/dist/utils/index.js +0 -3
- package/dist/utils/index.js.map +1 -1
- package/dist/websocket.shared.types.d.ts +25 -0
- package/dist/websocket.shared.types.js +4 -0
- package/dist/websocket.shared.types.js.map +1 -0
- package/package.json +12 -66
- package/src/__tests__/singleton.test.ts +0 -143
- package/src/index.ts +2 -83
- package/src/utils/Lib.ts +0 -77
- package/src/utils/index.ts +0 -3
- package/src/websocket.shared.types.ts +27 -0
- package/dist/application.d.ts +0 -18
- package/dist/application.js +0 -60
- package/dist/application.js.map +0 -1
- package/dist/client/api/base.api.d.ts +0 -63
- package/dist/client/api/base.api.js +0 -61
- package/dist/client/api/base.api.js.map +0 -1
- package/dist/client/api/index.d.ts +0 -2
- package/dist/client/api/index.js +0 -5
- package/dist/client/api/index.js.map +0 -1
- package/dist/client/rxjs/index.d.ts +0 -1
- package/dist/client/rxjs/index.js +0 -4
- package/dist/client/rxjs/index.js.map +0 -1
- package/dist/client/rxjs/useRxjs.d.ts +0 -17
- package/dist/client/rxjs/useRxjs.js +0 -87
- package/dist/client/rxjs/useRxjs.js.map +0 -1
- package/dist/client/vite/plugins/index.d.ts +0 -2
- package/dist/client/vite/plugins/index.js +0 -5
- package/dist/client/vite/plugins/index.js.map +0 -1
- package/dist/client/vite/plugins/topsydeUtilsVitePlugin.d.ts +0 -9
- package/dist/client/vite/plugins/topsydeUtilsVitePlugin.js +0 -74
- package/dist/client/vite/plugins/topsydeUtilsVitePlugin.js.map +0 -1
- package/dist/external/index.d.ts +0 -1
- package/dist/external/index.js +0 -4
- package/dist/external/index.js.map +0 -1
- package/dist/external/re-exports.d.ts +0 -16
- package/dist/external/re-exports.js +0 -24
- package/dist/external/re-exports.js.map +0 -1
- package/dist/server/base/base.database.d.ts +0 -10
- package/dist/server/base/base.database.js +0 -23
- package/dist/server/base/base.database.js.map +0 -1
- package/dist/server/base/index.d.ts +0 -2
- package/dist/server/base/index.js +0 -5
- package/dist/server/base/index.js.map +0 -1
- package/dist/server/bun/index.d.ts +0 -3
- package/dist/server/bun/index.js +0 -6
- package/dist/server/bun/index.js.map +0 -1
- package/dist/server/bun/router/controller-discovery.d.ts +0 -13
- package/dist/server/bun/router/controller-discovery.js +0 -83
- package/dist/server/bun/router/controller-discovery.js.map +0 -1
- package/dist/server/bun/router/index.d.ts +0 -6
- package/dist/server/bun/router/index.js +0 -9
- package/dist/server/bun/router/index.js.map +0 -1
- package/dist/server/bun/router/router.d.ts +0 -12
- package/dist/server/bun/router/router.internal.d.ts +0 -15
- package/dist/server/bun/router/router.internal.js +0 -51
- package/dist/server/bun/router/router.internal.js.map +0 -1
- package/dist/server/bun/router/router.js +0 -38
- package/dist/server/bun/router/router.js.map +0 -1
- package/dist/server/bun/router/routes.d.ts +0 -5
- package/dist/server/bun/router/routes.js +0 -2
- package/dist/server/bun/router/routes.js.map +0 -1
- package/dist/server/bun/websocket/Channel.d.ts +0 -68
- package/dist/server/bun/websocket/Channel.js +0 -263
- package/dist/server/bun/websocket/Channel.js.map +0 -1
- package/dist/server/bun/websocket/Client.d.ts +0 -87
- package/dist/server/bun/websocket/Client.js +0 -193
- package/dist/server/bun/websocket/Client.js.map +0 -1
- package/dist/server/bun/websocket/Message.d.ts +0 -10
- package/dist/server/bun/websocket/Message.js +0 -103
- package/dist/server/bun/websocket/Message.js.map +0 -1
- package/dist/server/bun/websocket/Websocket.d.ts +0 -171
- package/dist/server/bun/websocket/Websocket.js +0 -336
- package/dist/server/bun/websocket/Websocket.js.map +0 -1
- package/dist/server/bun/websocket/index.d.ts +0 -11
- package/dist/server/bun/websocket/index.js +0 -14
- package/dist/server/bun/websocket/index.js.map +0 -1
- package/dist/server/bun/websocket/websocket.enums.d.ts +0 -27
- package/dist/server/bun/websocket/websocket.enums.js +0 -31
- package/dist/server/bun/websocket/websocket.enums.js.map +0 -1
- package/dist/server/bun/websocket/websocket.guards.d.ts +0 -3
- package/dist/server/bun/websocket/websocket.guards.js +0 -17
- package/dist/server/bun/websocket/websocket.guards.js.map +0 -1
- package/dist/server/bun/websocket/websocket.types.d.ts +0 -235
- package/dist/server/bun/websocket/websocket.types.js +0 -2
- package/dist/server/bun/websocket/websocket.types.js.map +0 -1
- package/dist/server/controller.d.ts +0 -62
- package/dist/server/controller.js +0 -55
- package/dist/server/controller.js.map +0 -1
- package/dist/server/index.d.ts +0 -4
- package/dist/server/index.js +0 -7
- package/dist/server/index.js.map +0 -1
- package/dist/server/service.d.ts +0 -5
- package/dist/server/service.js +0 -38
- package/dist/server/service.js.map +0 -1
- package/dist/utils/BaseDto.d.ts +0 -33
- package/dist/utils/BaseDto.js +0 -69
- package/dist/utils/BaseDto.js.map +0 -1
- package/dist/utils/BaseEntity.d.ts +0 -31
- package/dist/utils/BaseEntity.js +0 -37
- package/dist/utils/BaseEntity.js.map +0 -1
- package/dist/utils/dto_validators/IsNumberOrRangeConstraint.d.ts +0 -9
- package/dist/utils/dto_validators/IsNumberOrRangeConstraint.js +0 -85
- package/dist/utils/dto_validators/IsNumberOrRangeConstraint.js.map +0 -1
- package/dist/utils/dto_validators/index.d.ts +0 -1
- package/dist/utils/dto_validators/index.js +0 -4
- package/dist/utils/dto_validators/index.js.map +0 -1
- package/src/__tests__/app.test.ts +0 -206
- package/src/application.ts +0 -73
- package/src/client/api/base.api.ts +0 -111
- package/src/client/api/index.ts +0 -5
- package/src/client/rxjs/index.ts +0 -4
- package/src/client/rxjs/useRxjs.ts +0 -113
- package/src/client/vite/plugins/index.ts +0 -5
- package/src/client/vite/plugins/topsydeUtilsVitePlugin.ts +0 -80
- package/src/external/index.ts +0 -4
- package/src/external/re-exports.ts +0 -54
- package/src/server/base/base.database.ts +0 -31
- package/src/server/base/index.ts +0 -5
- package/src/server/bun/index.ts +0 -6
- package/src/server/bun/router/controller-discovery.ts +0 -94
- package/src/server/bun/router/index.ts +0 -9
- package/src/server/bun/router/router.internal.ts +0 -64
- package/src/server/bun/router/router.ts +0 -51
- package/src/server/bun/router/routes.ts +0 -7
- package/src/server/bun/websocket/Channel.ts +0 -310
- package/src/server/bun/websocket/Client.ts +0 -243
- package/src/server/bun/websocket/ISSUES.md +0 -1175
- package/src/server/bun/websocket/Message.ts +0 -120
- package/src/server/bun/websocket/Websocket.ts +0 -402
- package/src/server/bun/websocket/index.ts +0 -14
- package/src/server/bun/websocket/websocket.enums.ts +0 -29
- package/src/server/bun/websocket/websocket.guards.ts +0 -22
- package/src/server/bun/websocket/websocket.types.ts +0 -252
- package/src/server/controller.ts +0 -121
- package/src/server/index.ts +0 -7
- package/src/server/service.ts +0 -36
- package/src/utils/BaseDto.ts +0 -77
- package/src/utils/BaseEntity.ts +0 -49
- package/src/utils/dto_validators/IsNumberOrRangeConstraint.ts +0 -32
- package/src/utils/dto_validators/index.ts +0 -4
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { Routes } from "./routes";
|
|
2
|
-
declare class Router_Internal {
|
|
3
|
-
private registry;
|
|
4
|
-
private routes;
|
|
5
|
-
constructor(routes?: Routes);
|
|
6
|
-
post<T>(req: Request): Promise<T>;
|
|
7
|
-
get<T>(req: Request): Promise<T>;
|
|
8
|
-
private handleRequest;
|
|
9
|
-
private getPath;
|
|
10
|
-
private resolve;
|
|
11
|
-
private register;
|
|
12
|
-
setRoutes(routes: Routes): void;
|
|
13
|
-
private controllerFactory;
|
|
14
|
-
}
|
|
15
|
-
export default Router_Internal;
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { Throwable } from "../../../index.js";
|
|
2
|
-
import { ERROR_CODE } from "../../../errors.js";
|
|
3
|
-
import { Debug } from "../../../utils/Lib.js";
|
|
4
|
-
class Router_Internal {
|
|
5
|
-
constructor(routes) {
|
|
6
|
-
this.registry = new Map();
|
|
7
|
-
this.routes = routes ?? {};
|
|
8
|
-
}
|
|
9
|
-
async post(req) {
|
|
10
|
-
return await this.handleRequest(req);
|
|
11
|
-
}
|
|
12
|
-
async get(req) {
|
|
13
|
-
return await this.handleRequest(req);
|
|
14
|
-
}
|
|
15
|
-
async handleRequest(request) {
|
|
16
|
-
const path = this.getPath(request);
|
|
17
|
-
const output = await this.resolve(path).call(request);
|
|
18
|
-
return output;
|
|
19
|
-
}
|
|
20
|
-
getPath(request) {
|
|
21
|
-
return new URL(request.url).pathname;
|
|
22
|
-
}
|
|
23
|
-
resolve(path) {
|
|
24
|
-
const controllerKey = path.split("/")[1];
|
|
25
|
-
return this.register(controllerKey, () => this.controllerFactory(controllerKey));
|
|
26
|
-
}
|
|
27
|
-
register(controllerKey, factory) {
|
|
28
|
-
if (!this.registry.has(controllerKey)) {
|
|
29
|
-
this.registry.set(controllerKey, factory());
|
|
30
|
-
Debug.Log(`Caching controller: /${controllerKey}`);
|
|
31
|
-
}
|
|
32
|
-
return this.registry.get(controllerKey);
|
|
33
|
-
}
|
|
34
|
-
setRoutes(routes) {
|
|
35
|
-
this.routes = routes;
|
|
36
|
-
}
|
|
37
|
-
controllerFactory(controllerKey) {
|
|
38
|
-
try {
|
|
39
|
-
if (!(controllerKey in this.routes))
|
|
40
|
-
throw new Throwable(`${ERROR_CODE.INVALID_CONTROLLER}: ${controllerKey}`, { logError: false });
|
|
41
|
-
const ControllerClass = this.routes[controllerKey];
|
|
42
|
-
return new ControllerClass();
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
console.error("controllerFactory", err);
|
|
46
|
-
throw err;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
export default Router_Internal;
|
|
51
|
-
//# sourceMappingURL=router.internal.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"router.internal.js","sourceRoot":"","sources":["../../../../src/server/bun/router/router.internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG3C,MAAM,eAAe;IAIpB,YAAY,MAAe;QAHnB,aAAQ,GAAG,IAAI,GAAG,EAAsB,CAAC;QAIhD,IAAI,CAAC,MAAM,GAAG,MAAM,IAAI,EAAE,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,IAAI,CAAI,GAAY;QACzB,OAAO,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAED,KAAK,CAAC,GAAG,CAAI,GAAY;QACxB,OAAO,MAAM,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACtC,CAAC;IAEO,KAAK,CAAC,aAAa,CAAI,OAAgB;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAI,OAAO,CAAC,CAAC;QACzD,OAAO,MAAM,CAAC;IACf,CAAC;IAEO,OAAO,CAAC,OAAgB;QAC/B,OAAO,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IACtC,CAAC;IAEO,OAAO,CAAC,IAAY;QAC3B,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC,QAAQ,CAAC,aAAa,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC;IAClF,CAAC;IAEO,QAAQ,CAAC,aAAqB,EAAE,OAAyB;QAChE,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;YAC5C,KAAK,CAAC,GAAG,CAAC,wBAAwB,aAAa,EAAE,CAAC,CAAC;QACpD,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,aAAa,CAAe,CAAC;IACvD,CAAC;IAEM,SAAS,CAAC,MAAc;QAC9B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAEO,iBAAiB,CAAC,aAAqB;QAC9C,IAAI,CAAC;YACJ,IAAI,CAAC,CAAC,aAAa,IAAI,IAAI,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,SAAS,CAAC,GAAG,UAAU,CAAC,kBAAkB,KAAK,aAAa,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;YAEpI,MAAM,eAAe,GAAG,IAAI,CAAC,MAAM,CAAC,aAAyC,CAAC,CAAC;YAE/E,OAAO,IAAI,eAAe,EAAE,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,GAAG,CAAC,CAAC;YACxC,MAAM,GAAG,CAAC;QACX,CAAC;IACF,CAAC;CACD;AAED,eAAe,eAAe,CAAC","sourcesContent":["import { Throwable } from \"../../..\";\nimport { ERROR_CODE } from \"../../../errors\";\nimport Controller from \"../../controller\";\nimport { Debug } from \"../../../utils/Lib\";\nimport { Routes } from \"./routes\";\n\nclass Router_Internal {\n\tprivate registry = new Map<string, Controller>();\n\tprivate routes: Routes;\n\n\tconstructor(routes?: Routes) {\n\t\tthis.routes = routes ?? {};\n\t}\n\n\tasync post<T>(req: Request): Promise<T> {\n\t\treturn await this.handleRequest(req);\n\t}\n\n\tasync get<T>(req: Request): Promise<T> {\n\t\treturn await this.handleRequest(req);\n\t}\n\n\tprivate async handleRequest<T>(request: Request): Promise<T> {\n\t\tconst path = this.getPath(request);\n\t\tconst output = await this.resolve(path).call<T>(request);\n\t\treturn output;\n\t}\n\n\tprivate getPath(request: Request): string {\n\t\treturn new URL(request.url).pathname;\n\t}\n\n\tprivate resolve(path: string): Controller {\n\t\tconst controllerKey = path.split(\"/\")[1];\n\t\treturn this.register(controllerKey, () => this.controllerFactory(controllerKey));\n\t}\n\n\tprivate register(controllerKey: string, factory: () => Controller): Controller {\n\t\tif (!this.registry.has(controllerKey)) {\n\t\t\tthis.registry.set(controllerKey, factory());\n\t\t\tDebug.Log(`Caching controller: /${controllerKey}`);\n\t\t}\n\t\treturn this.registry.get(controllerKey) as Controller;\n\t}\n\n\tpublic setRoutes(routes: Routes): void {\n\t\tthis.routes = routes;\n\t}\n\n\tprivate controllerFactory(controllerKey: string): Controller {\n\t\ttry {\n\t\t\tif (!(controllerKey in this.routes)) throw new Throwable(`${ERROR_CODE.INVALID_CONTROLLER}: ${controllerKey}`, { logError: false });\n\n\t\t\tconst ControllerClass = this.routes[controllerKey as keyof typeof this.routes];\n\n\t\t\treturn new ControllerClass();\n\t\t} catch (err) {\n\t\t\tconsole.error(\"controllerFactory\", err);\n\t\t\tthrow err;\n\t\t}\n\t}\n}\n\nexport default Router_Internal;\n"]}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { ERROR_CODE } from "../../../errors.js";
|
|
2
|
-
import Singleton from "../../../singleton.js";
|
|
3
|
-
import Guards from "../../../utils/Guards.js";
|
|
4
|
-
import Router_Internal from "./router.internal.js";
|
|
5
|
-
class Router extends Singleton {
|
|
6
|
-
constructor(routes) {
|
|
7
|
-
super();
|
|
8
|
-
this.internal = new Router_Internal(routes);
|
|
9
|
-
}
|
|
10
|
-
setRoutes(routes) {
|
|
11
|
-
this.internal.setRoutes(routes);
|
|
12
|
-
}
|
|
13
|
-
static async Call(request) {
|
|
14
|
-
if (Guards.IsNil(request))
|
|
15
|
-
throw ERROR_CODE.NO_REQUEST;
|
|
16
|
-
const methods = this.getMethodMap();
|
|
17
|
-
const method = methods[request.method];
|
|
18
|
-
if (Guards.IsNil(method))
|
|
19
|
-
throw ERROR_CODE.INVALID_METHOD;
|
|
20
|
-
return await method(request);
|
|
21
|
-
}
|
|
22
|
-
static SetRoutes(routes) {
|
|
23
|
-
this.GetInstance().setRoutes(routes);
|
|
24
|
-
}
|
|
25
|
-
static getMethodMap() {
|
|
26
|
-
const router = this.GetInstance();
|
|
27
|
-
return {
|
|
28
|
-
GET: async (req) => await router.internal.get(req),
|
|
29
|
-
POST: async (req) => await router.internal.post(req),
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
static GetQueryParams(request) {
|
|
33
|
-
const url = new URL(request.url);
|
|
34
|
-
return url.searchParams;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
export default Router;
|
|
38
|
-
//# sourceMappingURL=router.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"router.js","sourceRoot":"","sources":["../../../../src/server/bun/router/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAC3C,OAAO,MAAM,MAAM,uBAAuB,CAAC;AAC3C,OAAO,eAAe,MAAM,mBAAmB,CAAC;AAOhD,MAAM,MAAO,SAAQ,SAAS;IAG7B,YAAmB,MAAe;QACjC,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,QAAQ,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,CAAC;IAEO,SAAS,CAAC,MAAc;QAC/B,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACjC,CAAC;IAEM,MAAM,CAAC,KAAK,CAAC,IAAI,CAAI,OAAgB;QAC3C,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,MAAM,UAAU,CAAC,UAAU,CAAC;QACvD,MAAM,OAAO,GAAiB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEvC,IAAI,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;YAAE,MAAM,UAAU,CAAC,cAAc,CAAC;QAE1D,OAAO,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAEM,MAAM,CAAC,SAAS,CAAC,MAAc;QACrC,IAAI,CAAC,WAAW,EAAU,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAC9C,CAAC;IAEO,MAAM,CAAC,YAAY;QAC1B,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,EAAU,CAAC;QAC1C,OAAO;YACN,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC;YAClD,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAAC,MAAM,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC;SACpD,CAAC;IACH,CAAC;IAEM,MAAM,CAAC,cAAc,CAAC,OAAgB;QAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,OAAO,GAAG,CAAC,YAAY,CAAC;IACzB,CAAC;CACD;AAED,eAAe,MAAM,CAAC","sourcesContent":["import { ERROR_CODE } from \"../../../errors\";\nimport Singleton from \"../../../singleton\";\nimport Guards from \"../../../utils/Guards\";\nimport Router_Internal from \"./router.internal\";\nimport { Routes } from \"./routes\";\n\ntype MethodMap<T> = {\n\t[method: string]: (req: Request) => Promise<T>;\n};\n\nclass Router extends Singleton {\n\tprivate internal: Router_Internal;\n\n\tpublic constructor(routes?: Routes) {\n\t\tsuper();\n\t\tthis.internal = new Router_Internal(routes);\n\t}\n\n\tprivate setRoutes(routes: Routes): void {\n\t\tthis.internal.setRoutes(routes);\n\t}\n\n\tpublic static async Call<T>(request: Request): Promise<T> {\n\t\tif (Guards.IsNil(request)) throw ERROR_CODE.NO_REQUEST;\n\t\tconst methods: MethodMap<T> = this.getMethodMap();\n\t\tconst method = methods[request.method];\n\n\t\tif (Guards.IsNil(method)) throw ERROR_CODE.INVALID_METHOD;\n\n\t\treturn await method(request);\n\t}\n\n\tpublic static SetRoutes(routes: Routes) {\n\t\tthis.GetInstance<Router>().setRoutes(routes);\n\t}\n\n\tprivate static getMethodMap<T>(): MethodMap<T> {\n\t\tconst router = this.GetInstance<Router>();\n\t\treturn {\n\t\t\tGET: async (req) => await router.internal.get(req),\n\t\t\tPOST: async (req) => await router.internal.post(req),\n\t\t};\n\t}\n\n\tpublic static GetQueryParams(request: Request): URLSearchParams {\n\t\tconst url = new URL(request.url);\n\t\treturn url.searchParams;\n\t}\n}\n\nexport default Router;\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../../src/server/bun/router/routes.ts"],"names":[],"mappings":"AAMA,MAAM,CAAC,MAAM,MAAM,GAAW,EAAE,CAAC","sourcesContent":["import Controller from \"../../controller\";\n\nexport type Routes = {\n\t[key: string]: new () => Controller;\n};\n\nexport const routes: Routes = {};\n"]}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import Websocket from "./Websocket";
|
|
2
|
-
import type { BroadcastOptions, I_WebsocketChannel, I_WebsocketClient, I_WebsocketEntity, WebsocketChannel, WebsocketMessage, AddMemberResult, AddMemberOptions, RemoveMemberOptions } from "./websocket.types";
|
|
3
|
-
/**
|
|
4
|
-
* Channel - Pub/sub topic for WebSocket clients
|
|
5
|
-
*
|
|
6
|
-
* ## Membership Contract
|
|
7
|
-
* - `addMember()` validates capacity and adds to `members` map
|
|
8
|
-
* - Client drives join via `joinChannel()` which subscribes and handles rollback
|
|
9
|
-
* - If subscription fails, membership is automatically rolled back
|
|
10
|
-
* - Member count never exceeds `limit`
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* const channel = new Channel("game-1", "Game Room", ws, 10);
|
|
14
|
-
* const result = channel.addMember(client);
|
|
15
|
-
* if (result.success) {
|
|
16
|
-
* channel.broadcast({ type: "player.joined", content: { player: client.whoami() } });
|
|
17
|
-
* }
|
|
18
|
-
*/
|
|
19
|
-
export default class Channel<T extends Websocket = Websocket> implements I_WebsocketChannel<T> {
|
|
20
|
-
createdAt: Date;
|
|
21
|
-
id: string;
|
|
22
|
-
name: string;
|
|
23
|
-
limit: number;
|
|
24
|
-
members: Map<string, I_WebsocketClient>;
|
|
25
|
-
metadata: Record<string, string>;
|
|
26
|
-
ws: T;
|
|
27
|
-
constructor(id: string, name: string, ws: T, limit?: number, members?: Map<string, I_WebsocketClient>, metadata?: Record<string, string>);
|
|
28
|
-
broadcast(message: WebsocketMessage | string, options?: BroadcastOptions): void;
|
|
29
|
-
private getFilteredMetadata;
|
|
30
|
-
hasMember(client: I_WebsocketEntity | string): boolean;
|
|
31
|
-
/**
|
|
32
|
-
* ATOMIC: Add member to channel (membership only, no side effects)
|
|
33
|
-
* Internal method used for rollback-safe operations
|
|
34
|
-
* @internal
|
|
35
|
-
*/
|
|
36
|
-
private addToMembersMap;
|
|
37
|
-
/**
|
|
38
|
-
* Add a client to this channel with full coordination
|
|
39
|
-
* Handles: membership + WebSocket subscription + client-side tracking + optional notification
|
|
40
|
-
* This ensures two-way coordination between channel and client
|
|
41
|
-
*/
|
|
42
|
-
addMember(client: I_WebsocketClient, options?: AddMemberOptions): AddMemberResult;
|
|
43
|
-
addMembers(clients: I_WebsocketClient[], options?: AddMemberOptions): AddMemberResult[];
|
|
44
|
-
private notifyChannelFull;
|
|
45
|
-
/**
|
|
46
|
-
* Internal method to remove a member without triggering client-side cleanup.
|
|
47
|
-
* Used for rollback operations when joinChannel fails.
|
|
48
|
-
* @internal
|
|
49
|
-
*/
|
|
50
|
-
removeFromMembersMap(client: I_WebsocketClient): void;
|
|
51
|
-
/**
|
|
52
|
-
* Remove a client from this channel with full coordination
|
|
53
|
-
* Handles: membership removal + WebSocket unsubscription + client-side tracking removal + optional notification
|
|
54
|
-
* This ensures two-way coordination between channel and client
|
|
55
|
-
*/
|
|
56
|
-
removeMember(entity: I_WebsocketEntity, options?: RemoveMemberOptions): false | I_WebsocketClient;
|
|
57
|
-
getMember(client: I_WebsocketEntity | string): I_WebsocketClient | undefined;
|
|
58
|
-
getMembers(clients?: string[] | I_WebsocketEntity[]): I_WebsocketClient[];
|
|
59
|
-
getMetadata(): Record<string, string>;
|
|
60
|
-
getCreatedAt(): Date;
|
|
61
|
-
getId(): string;
|
|
62
|
-
getName(): string;
|
|
63
|
-
getLimit(): number;
|
|
64
|
-
getSize(): number;
|
|
65
|
-
canAddMember(): boolean;
|
|
66
|
-
delete(): void;
|
|
67
|
-
static GetChannelType(channels: WebsocketChannel<I_WebsocketChannel> | undefined): typeof Channel;
|
|
68
|
-
}
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
import { Guards, Lib } from "../../../utils/index.js";
|
|
2
|
-
import Message from "./Message.js";
|
|
3
|
-
import { E_WebsocketMessageType } from "./websocket.enums.js";
|
|
4
|
-
/**
|
|
5
|
-
* Channel - Pub/sub topic for WebSocket clients
|
|
6
|
-
*
|
|
7
|
-
* ## Membership Contract
|
|
8
|
-
* - `addMember()` validates capacity and adds to `members` map
|
|
9
|
-
* - Client drives join via `joinChannel()` which subscribes and handles rollback
|
|
10
|
-
* - If subscription fails, membership is automatically rolled back
|
|
11
|
-
* - Member count never exceeds `limit`
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* const channel = new Channel("game-1", "Game Room", ws, 10);
|
|
15
|
-
* const result = channel.addMember(client);
|
|
16
|
-
* if (result.success) {
|
|
17
|
-
* channel.broadcast({ type: "player.joined", content: { player: client.whoami() } });
|
|
18
|
-
* }
|
|
19
|
-
*/
|
|
20
|
-
export default class Channel {
|
|
21
|
-
constructor(id, name, ws, limit, members, metadata) {
|
|
22
|
-
this.createdAt = new Date();
|
|
23
|
-
this.id = id;
|
|
24
|
-
this.name = name;
|
|
25
|
-
this.limit = limit ?? 5;
|
|
26
|
-
this.members = members ?? new Map();
|
|
27
|
-
this.metadata = metadata ?? {};
|
|
28
|
-
this.ws = ws;
|
|
29
|
-
}
|
|
30
|
-
broadcast(message, options) {
|
|
31
|
-
if (Guards.IsString(message)) {
|
|
32
|
-
const msg = {
|
|
33
|
-
type: "message",
|
|
34
|
-
content: { message },
|
|
35
|
-
};
|
|
36
|
-
message = msg;
|
|
37
|
-
}
|
|
38
|
-
const output = Message.Create(message, { ...options, channel: this.id });
|
|
39
|
-
// Include channel metadata if requested
|
|
40
|
-
if (options?.includeMetadata) {
|
|
41
|
-
output.metadata = options.includeMetadata === true ? this.getMetadata() : this.getFilteredMetadata(options.includeMetadata);
|
|
42
|
-
}
|
|
43
|
-
const serializedMessage = Message.Serialize(output);
|
|
44
|
-
// If we need to exclude clients, send individually to prevent excluded clients from receiving
|
|
45
|
-
if (options?.excludeClients && options.excludeClients.length > 0) {
|
|
46
|
-
const excludeSet = new Set(options.excludeClients); // O(1) lookup
|
|
47
|
-
for (const [clientId, client] of this.members) {
|
|
48
|
-
if (!excludeSet.has(clientId)) {
|
|
49
|
-
try {
|
|
50
|
-
client.ws.send(serializedMessage);
|
|
51
|
-
}
|
|
52
|
-
catch (error) {
|
|
53
|
-
Lib.Warn(`Failed to send to client ${clientId}:`, error);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
// Otherwise use pub/sub for everyone
|
|
60
|
-
this.ws.server.publish(this.id, serializedMessage);
|
|
61
|
-
}
|
|
62
|
-
// Helper method for filtered metadata
|
|
63
|
-
getFilteredMetadata(keys) {
|
|
64
|
-
const metadata = this.getMetadata();
|
|
65
|
-
const filtered = {};
|
|
66
|
-
for (const key of keys) {
|
|
67
|
-
if (metadata[key] !== undefined) {
|
|
68
|
-
filtered[key] = metadata[key];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return filtered;
|
|
72
|
-
}
|
|
73
|
-
hasMember(client) {
|
|
74
|
-
if (typeof client === "string")
|
|
75
|
-
return this.members.has(client);
|
|
76
|
-
return this.members.has(client.id);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* ATOMIC: Add member to channel (membership only, no side effects)
|
|
80
|
-
* Internal method used for rollback-safe operations
|
|
81
|
-
* @internal
|
|
82
|
-
*/
|
|
83
|
-
addToMembersMap(client, options) {
|
|
84
|
-
// Check if already a member
|
|
85
|
-
if (this.members.has(client.id)) {
|
|
86
|
-
return { success: false, reason: "already_member" };
|
|
87
|
-
}
|
|
88
|
-
// Check capacity
|
|
89
|
-
if (!this.canAddMember()) {
|
|
90
|
-
// Optionally notify client why they can't join
|
|
91
|
-
if (options?.notify_when_full) {
|
|
92
|
-
this.notifyChannelFull(client);
|
|
93
|
-
}
|
|
94
|
-
return { success: false, reason: "full" };
|
|
95
|
-
}
|
|
96
|
-
try {
|
|
97
|
-
this.members.set(client.id, client);
|
|
98
|
-
return { success: true, client };
|
|
99
|
-
}
|
|
100
|
-
catch (error) {
|
|
101
|
-
// Rollback
|
|
102
|
-
this.members.delete(client.id);
|
|
103
|
-
return {
|
|
104
|
-
success: false,
|
|
105
|
-
reason: "error",
|
|
106
|
-
error: error instanceof Error ? error : new Error(String(error)),
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Add a client to this channel with full coordination
|
|
112
|
-
* Handles: membership + WebSocket subscription + client-side tracking + optional notification
|
|
113
|
-
* This ensures two-way coordination between channel and client
|
|
114
|
-
*/
|
|
115
|
-
addMember(client, options) {
|
|
116
|
-
// 1. Atomic membership add
|
|
117
|
-
const result = this.addToMembersMap(client, options);
|
|
118
|
-
if (!result.success) {
|
|
119
|
-
return result;
|
|
120
|
-
}
|
|
121
|
-
try {
|
|
122
|
-
// 2. Subscribe client's WebSocket to channel pub/sub topic
|
|
123
|
-
// CRITICAL: Without this, client won't receive channel.broadcast() messages
|
|
124
|
-
client.subscribe(this.id);
|
|
125
|
-
// 3. Track channel on client side (client's channels map)
|
|
126
|
-
client.trackChannel(this);
|
|
127
|
-
// 4. Optional welcome notification
|
|
128
|
-
if (options?.notify) {
|
|
129
|
-
client.send({
|
|
130
|
-
type: E_WebsocketMessageType.CLIENT_JOIN_CHANNEL,
|
|
131
|
-
content: { message: "Welcome to the channel" },
|
|
132
|
-
channel: this.id,
|
|
133
|
-
client: client.whoami(),
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
return result;
|
|
137
|
-
}
|
|
138
|
-
catch (error) {
|
|
139
|
-
// Rollback on failure: remove membership + unsubscribe + untrack
|
|
140
|
-
this.removeFromMembersMap(client);
|
|
141
|
-
client.unsubscribe(this.id);
|
|
142
|
-
client.untrackChannel(this);
|
|
143
|
-
throw error;
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
addMembers(clients, options) {
|
|
147
|
-
const results = [];
|
|
148
|
-
for (const client of clients) {
|
|
149
|
-
const result = this.addMember(client, options);
|
|
150
|
-
results.push(result);
|
|
151
|
-
if (!result.success) {
|
|
152
|
-
// Stop adding further members on failure
|
|
153
|
-
break;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return results;
|
|
157
|
-
}
|
|
158
|
-
notifyChannelFull(client) {
|
|
159
|
-
client.send({
|
|
160
|
-
type: E_WebsocketMessageType.ERROR,
|
|
161
|
-
content: {
|
|
162
|
-
message: `Channel "${this.name}" is full (${this.limit} members)`,
|
|
163
|
-
code: "CHANNEL_FULL",
|
|
164
|
-
channel: this.id,
|
|
165
|
-
},
|
|
166
|
-
});
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Internal method to remove a member without triggering client-side cleanup.
|
|
170
|
-
* Used for rollback operations when joinChannel fails.
|
|
171
|
-
* @internal
|
|
172
|
-
*/
|
|
173
|
-
removeFromMembersMap(client) {
|
|
174
|
-
this.members.delete(client.id);
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Remove a client from this channel with full coordination
|
|
178
|
-
* Handles: membership removal + WebSocket unsubscription + client-side tracking removal + optional notification
|
|
179
|
-
* This ensures two-way coordination between channel and client
|
|
180
|
-
*/
|
|
181
|
-
removeMember(entity, options) {
|
|
182
|
-
// 1. Check if member exists
|
|
183
|
-
if (!this.members.has(entity.id))
|
|
184
|
-
return false;
|
|
185
|
-
const client = this.members.get(entity.id);
|
|
186
|
-
if (!client)
|
|
187
|
-
return false;
|
|
188
|
-
// 2. Remove from channel members (atomic operation)
|
|
189
|
-
this.removeFromMembersMap(client);
|
|
190
|
-
// 3. Unsubscribe client's WebSocket from channel pub/sub topic
|
|
191
|
-
client.unsubscribe(this.id);
|
|
192
|
-
// 4. Untrack channel on client side (remove from client's channels map)
|
|
193
|
-
client.untrackChannel(this);
|
|
194
|
-
// 5. Optional goodbye notification
|
|
195
|
-
if (options?.notify) {
|
|
196
|
-
client.send({
|
|
197
|
-
type: E_WebsocketMessageType.CLIENT_LEAVE_CHANNEL,
|
|
198
|
-
content: { message: "You left the channel" },
|
|
199
|
-
channel: this.id,
|
|
200
|
-
client: client.whoami(),
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
return client;
|
|
204
|
-
}
|
|
205
|
-
getMember(client) {
|
|
206
|
-
if (typeof client === "string")
|
|
207
|
-
return this.members.get(client);
|
|
208
|
-
return this.members.get(client.id);
|
|
209
|
-
}
|
|
210
|
-
getMembers(clients) {
|
|
211
|
-
if (!clients)
|
|
212
|
-
return Array.from(this.members.values());
|
|
213
|
-
return clients.map((client) => this.getMember(client)).filter((client) => client !== undefined);
|
|
214
|
-
}
|
|
215
|
-
getMetadata() {
|
|
216
|
-
return this.metadata;
|
|
217
|
-
}
|
|
218
|
-
getCreatedAt() {
|
|
219
|
-
return this.createdAt;
|
|
220
|
-
}
|
|
221
|
-
getId() {
|
|
222
|
-
return this.id;
|
|
223
|
-
}
|
|
224
|
-
getName() {
|
|
225
|
-
return this.name;
|
|
226
|
-
}
|
|
227
|
-
getLimit() {
|
|
228
|
-
return this.limit;
|
|
229
|
-
}
|
|
230
|
-
getSize() {
|
|
231
|
-
return this.members.size;
|
|
232
|
-
}
|
|
233
|
-
canAddMember() {
|
|
234
|
-
const size = this.getSize();
|
|
235
|
-
return size < this.limit;
|
|
236
|
-
}
|
|
237
|
-
delete() {
|
|
238
|
-
//first remove all members
|
|
239
|
-
this.members.forEach((member) => {
|
|
240
|
-
this.removeMember(member, { notify: true });
|
|
241
|
-
});
|
|
242
|
-
//then clear members map
|
|
243
|
-
this.members.clear();
|
|
244
|
-
}
|
|
245
|
-
static GetChannelType(channels) {
|
|
246
|
-
if (!channels)
|
|
247
|
-
return Channel;
|
|
248
|
-
if (channels.size > 0) {
|
|
249
|
-
const firstChannel = channels.values().next().value;
|
|
250
|
-
if (firstChannel) {
|
|
251
|
-
return firstChannel.constructor;
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
return Channel;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
Lib.Warn("Channels are empty, using default channel class");
|
|
259
|
-
return Channel;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
//# sourceMappingURL=Channel.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"Channel.js","sourceRoot":"","sources":["../../../../src/server/bun/websocket/Channel.ts"],"names":[],"mappings":"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","sourcesContent":["import { Guards, Lib } from \"../../../utils\";\nimport Message from \"./Message\";\nimport Websocket from \"./Websocket\";\nimport type {\n\tBroadcastOptions,\n\tI_WebsocketChannel,\n\tI_WebsocketClient,\n\tI_WebsocketEntity,\n\tWebsocketChannel,\n\tWebsocketMessage,\n\tAddMemberResult,\n\tAddMemberOptions,\n\tRemoveMemberOptions,\n} from \"./websocket.types\";\nimport { E_WebsocketMessageType } from \"./websocket.enums\";\n\n/**\n * Channel - Pub/sub topic for WebSocket clients\n *\n * ## Membership Contract\n * - `addMember()` validates capacity and adds to `members` map\n * - Client drives join via `joinChannel()` which subscribes and handles rollback\n * - If subscription fails, membership is automatically rolled back\n * - Member count never exceeds `limit`\n *\n * @example\n * const channel = new Channel(\"game-1\", \"Game Room\", ws, 10);\n * const result = channel.addMember(client);\n * if (result.success) {\n * channel.broadcast({ type: \"player.joined\", content: { player: client.whoami() } });\n * }\n */\nexport default class Channel<T extends Websocket = Websocket> implements I_WebsocketChannel<T> {\n\tpublic createdAt: Date = new Date();\n\tpublic id: string;\n\tpublic name: string;\n\tpublic limit: number;\n\tpublic members: Map<string, I_WebsocketClient>;\n\tpublic metadata: Record<string, string>;\n\tpublic ws: T;\n\n\tconstructor(id: string, name: string, ws: T, limit?: number, members?: Map<string, I_WebsocketClient>, metadata?: Record<string, string>) {\n\t\tthis.id = id;\n\t\tthis.name = name;\n\t\tthis.limit = limit ?? 5;\n\t\tthis.members = members ?? new Map();\n\t\tthis.metadata = metadata ?? {};\n\t\tthis.ws = ws;\n\t}\n\n\tpublic broadcast(message: WebsocketMessage | string, options?: BroadcastOptions) {\n\t\tif (Guards.IsString(message)) {\n\t\t\tconst msg: WebsocketMessage = {\n\t\t\t\ttype: \"message\",\n\t\t\t\tcontent: { message },\n\t\t\t};\n\t\t\tmessage = msg;\n\t\t}\n\n\t\tconst output = Message.Create(message, { ...options, channel: this.id });\n\n\t\t// Include channel metadata if requested\n\t\tif (options?.includeMetadata) {\n\t\t\toutput.metadata = options.includeMetadata === true ? this.getMetadata() : this.getFilteredMetadata(options.includeMetadata);\n\t\t}\n\n\t\tconst serializedMessage = Message.Serialize(output);\n\n\t\t// If we need to exclude clients, send individually to prevent excluded clients from receiving\n\t\tif (options?.excludeClients && options.excludeClients.length > 0) {\n\t\t\tconst excludeSet = new Set(options.excludeClients); // O(1) lookup\n\n\t\t\tfor (const [clientId, client] of this.members) {\n\t\t\t\tif (!excludeSet.has(clientId)) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tclient.ws.send(serializedMessage);\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\tLib.Warn(`Failed to send to client ${clientId}:`, error);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// Otherwise use pub/sub for everyone\n\t\tthis.ws.server.publish(this.id, serializedMessage);\n\t}\n\n\t// Helper method for filtered metadata\n\tprivate getFilteredMetadata(keys: string[]) {\n\t\tconst metadata = this.getMetadata();\n\t\tconst filtered: Record<string, string> = {};\n\n\t\tfor (const key of keys) {\n\t\t\tif (metadata[key] !== undefined) {\n\t\t\t\tfiltered[key] = metadata[key];\n\t\t\t}\n\t\t}\n\n\t\treturn filtered;\n\t}\n\n\tpublic hasMember(client: I_WebsocketEntity | string) {\n\t\tif (typeof client === \"string\") return this.members.has(client);\n\t\treturn this.members.has(client.id);\n\t}\n\n\t/**\n\t * ATOMIC: Add member to channel (membership only, no side effects)\n\t * Internal method used for rollback-safe operations\n\t * @internal\n\t */\n\tprivate addToMembersMap(client: I_WebsocketClient, options?: AddMemberOptions): AddMemberResult {\n\t\t// Check if already a member\n\t\tif (this.members.has(client.id)) {\n\t\t\treturn { success: false, reason: \"already_member\" };\n\t\t}\n\n\t\t// Check capacity\n\t\tif (!this.canAddMember()) {\n\t\t\t// Optionally notify client why they can't join\n\t\t\tif (options?.notify_when_full) {\n\t\t\t\tthis.notifyChannelFull(client);\n\t\t\t}\n\t\t\treturn { success: false, reason: \"full\" };\n\t\t}\n\n\t\ttry {\n\t\t\tthis.members.set(client.id, client);\n\t\t\treturn { success: true, client };\n\t\t} catch (error) {\n\t\t\t// Rollback\n\t\t\tthis.members.delete(client.id);\n\t\t\treturn {\n\t\t\t\tsuccess: false,\n\t\t\t\treason: \"error\",\n\t\t\t\terror: error instanceof Error ? error : new Error(String(error)),\n\t\t\t};\n\t\t}\n\t}\n\n\t/**\n\t * Add a client to this channel with full coordination\n\t * Handles: membership + WebSocket subscription + client-side tracking + optional notification\n\t * This ensures two-way coordination between channel and client\n\t */\n\tpublic addMember(client: I_WebsocketClient, options?: AddMemberOptions): AddMemberResult {\n\t\t// 1. Atomic membership add\n\t\tconst result = this.addToMembersMap(client, options);\n\t\tif (!result.success) {\n\t\t\treturn result;\n\t\t}\n\n\t\ttry {\n\t\t\t// 2. Subscribe client's WebSocket to channel pub/sub topic\n\t\t\t// CRITICAL: Without this, client won't receive channel.broadcast() messages\n\t\t\tclient.subscribe(this.id);\n\n\t\t\t// 3. Track channel on client side (client's channels map)\n\t\t\tclient.trackChannel(this);\n\n\t\t\t// 4. Optional welcome notification\n\t\t\tif (options?.notify) {\n\t\t\t\tclient.send({\n\t\t\t\t\ttype: E_WebsocketMessageType.CLIENT_JOIN_CHANNEL,\n\t\t\t\t\tcontent: { message: \"Welcome to the channel\" },\n\t\t\t\t\tchannel: this.id,\n\t\t\t\t\tclient: client.whoami(),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\t// Rollback on failure: remove membership + unsubscribe + untrack\n\t\t\tthis.removeFromMembersMap(client);\n\t\t\tclient.unsubscribe(this.id);\n\t\t\tclient.untrackChannel(this);\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\tpublic addMembers(clients: I_WebsocketClient[], options?: AddMemberOptions): AddMemberResult[] {\n\t\tconst results: AddMemberResult[] = [];\n\t\tfor (const client of clients) {\n\t\t\tconst result = this.addMember(client, options);\n\t\t\tresults.push(result);\n\t\t\tif (!result.success) {\n\t\t\t\t// Stop adding further members on failure\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\treturn results;\n\t}\n\n\tprivate notifyChannelFull(client: I_WebsocketClient): void {\n\t\tclient.send({\n\t\t\ttype: E_WebsocketMessageType.ERROR,\n\t\t\tcontent: {\n\t\t\t\tmessage: `Channel \"${this.name}\" is full (${this.limit} members)`,\n\t\t\t\tcode: \"CHANNEL_FULL\",\n\t\t\t\tchannel: this.id,\n\t\t\t},\n\t\t});\n\t}\n\n\t/**\n\t * Internal method to remove a member without triggering client-side cleanup.\n\t * Used for rollback operations when joinChannel fails.\n\t * @internal\n\t */\n\tpublic removeFromMembersMap(client: I_WebsocketClient): void {\n\t\tthis.members.delete(client.id);\n\t}\n\n\t/**\n\t * Remove a client from this channel with full coordination\n\t * Handles: membership removal + WebSocket unsubscription + client-side tracking removal + optional notification\n\t * This ensures two-way coordination between channel and client\n\t */\n\tpublic removeMember(entity: I_WebsocketEntity, options?: RemoveMemberOptions) {\n\t\t// 1. Check if member exists\n\t\tif (!this.members.has(entity.id)) return false;\n\t\tconst client = this.members.get(entity.id);\n\t\tif (!client) return false;\n\n\t\t// 2. Remove from channel members (atomic operation)\n\t\tthis.removeFromMembersMap(client);\n\n\t\t// 3. Unsubscribe client's WebSocket from channel pub/sub topic\n\t\tclient.unsubscribe(this.id);\n\n\t\t// 4. Untrack channel on client side (remove from client's channels map)\n\t\tclient.untrackChannel(this);\n\n\t\t// 5. Optional goodbye notification\n\t\tif (options?.notify) {\n\t\t\tclient.send({\n\t\t\t\ttype: E_WebsocketMessageType.CLIENT_LEAVE_CHANNEL,\n\t\t\t\tcontent: { message: \"You left the channel\" },\n\t\t\t\tchannel: this.id,\n\t\t\t\tclient: client.whoami(),\n\t\t\t});\n\t\t}\n\n\t\treturn client;\n\t}\n\n\tpublic getMember(client: I_WebsocketEntity | string) {\n\t\tif (typeof client === \"string\") return this.members.get(client);\n\t\treturn this.members.get(client.id);\n\t}\n\n\tpublic getMembers(clients?: string[] | I_WebsocketEntity[]): I_WebsocketClient[] {\n\t\tif (!clients) return Array.from(this.members.values());\n\t\treturn clients.map((client) => this.getMember(client)).filter((client) => client !== undefined) as I_WebsocketClient[];\n\t}\n\n\tpublic getMetadata() {\n\t\treturn this.metadata;\n\t}\n\n\tpublic getCreatedAt() {\n\t\treturn this.createdAt;\n\t}\n\n\tpublic getId() {\n\t\treturn this.id;\n\t}\n\n\tpublic getName() {\n\t\treturn this.name;\n\t}\n\n\tpublic getLimit() {\n\t\treturn this.limit;\n\t}\n\n\tpublic getSize() {\n\t\treturn this.members.size;\n\t}\n\n\tpublic canAddMember() {\n\t\tconst size = this.getSize();\n\t\treturn size < this.limit;\n\t}\n\n\tpublic delete() {\n\t\t//first remove all members\n\t\tthis.members.forEach((member) => {\n\t\t\tthis.removeMember(member, { notify: true });\n\t\t});\n\t\t//then clear members map\n\t\tthis.members.clear();\n\t}\n\n\tpublic static GetChannelType(channels: WebsocketChannel<I_WebsocketChannel> | undefined) {\n\t\tif (!channels) return Channel;\n\t\tif (channels.size > 0) {\n\t\t\tconst firstChannel = channels.values().next().value;\n\t\t\tif (firstChannel) {\n\t\t\t\treturn firstChannel.constructor as typeof Channel;\n\t\t\t} else {\n\t\t\t\treturn Channel;\n\t\t\t}\n\t\t} else {\n\t\t\tLib.Warn(\"Channels are empty, using default channel class\");\n\t\t\treturn Channel;\n\t\t}\n\t}\n}\n"]}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { ServerWebSocket } from "bun";
|
|
2
|
-
import type { I_WebsocketClient, WebsocketEntityData, WebsocketChannel, WebsocketStructuredMessage, I_WebsocketEntity, I_WebsocketChannel, WebsocketMessageOptions } from "./websocket.types";
|
|
3
|
-
import { E_ClientState } from "./websocket.enums";
|
|
4
|
-
/**
|
|
5
|
-
* Client - Connected WebSocket client with channel membership
|
|
6
|
-
*
|
|
7
|
-
* ## Channel Membership
|
|
8
|
-
* - Maintains own channel list and handles Bun pub/sub subscriptions
|
|
9
|
-
* - `joinChannel()` adds to channel, subscribes, and handles rollback on failure
|
|
10
|
-
* - Always use `channel.addMember(client)` in application code, not `client.joinChannel()` directly
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* // ✅ Correct
|
|
14
|
-
* channel.addMember(client);
|
|
15
|
-
*
|
|
16
|
-
* // ❌ Incorrect - internal use only
|
|
17
|
-
* client.joinChannel(channel);
|
|
18
|
-
*/
|
|
19
|
-
export default class Client implements I_WebsocketClient {
|
|
20
|
-
private _id;
|
|
21
|
-
private _name;
|
|
22
|
-
private _ws;
|
|
23
|
-
private _channels;
|
|
24
|
-
private _state;
|
|
25
|
-
private _connectedAt?;
|
|
26
|
-
private _disconnectedAt?;
|
|
27
|
-
private set ws(value);
|
|
28
|
-
get ws(): ServerWebSocket<WebsocketEntityData>;
|
|
29
|
-
private set id(value);
|
|
30
|
-
get id(): string;
|
|
31
|
-
get name(): string;
|
|
32
|
-
private set name(value);
|
|
33
|
-
private set channels(value);
|
|
34
|
-
get channels(): WebsocketChannel<I_WebsocketChannel>;
|
|
35
|
-
get state(): E_ClientState;
|
|
36
|
-
constructor(entity: I_WebsocketEntity);
|
|
37
|
-
canReceiveMessages(): boolean;
|
|
38
|
-
markConnected(): void;
|
|
39
|
-
markDisconnecting(): void;
|
|
40
|
-
markDisconnected(): void;
|
|
41
|
-
getConnectionInfo(): {
|
|
42
|
-
id: string;
|
|
43
|
-
name: string;
|
|
44
|
-
state: E_ClientState;
|
|
45
|
-
connectedAt: Date | undefined;
|
|
46
|
-
disconnectedAt: Date | undefined;
|
|
47
|
-
uptime: number;
|
|
48
|
-
channelCount: number;
|
|
49
|
-
};
|
|
50
|
-
/**
|
|
51
|
-
* HELPER: Track channel on client side (for channel.addMember coordination)
|
|
52
|
-
* Allows channel to update client's internal channel map
|
|
53
|
-
* @internal Used by channel.addMember()
|
|
54
|
-
*/
|
|
55
|
-
trackChannel(channel: I_WebsocketChannel): void;
|
|
56
|
-
/**
|
|
57
|
-
* HELPER: Untrack channel on client side (for channel.addMember rollback)
|
|
58
|
-
* Allows channel to remove from client's internal channel map during rollback
|
|
59
|
-
* @internal Used by channel.addMember() error handling
|
|
60
|
-
*/
|
|
61
|
-
untrackChannel(channel: I_WebsocketChannel): void;
|
|
62
|
-
/**
|
|
63
|
-
* Join a channel (thin wrapper that delegates to channel.addMember)
|
|
64
|
-
* channel.addMember() handles all coordination: membership + subscription + tracking + notification
|
|
65
|
-
*/
|
|
66
|
-
joinChannel(channel: I_WebsocketChannel, send?: boolean): {
|
|
67
|
-
success: boolean;
|
|
68
|
-
reason: string;
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* Leave a channel (thin wrapper that delegates to channel.removeMember)
|
|
72
|
-
* channel.removeMember() handles all coordination: membership removal + unsubscription + tracking removal + notification
|
|
73
|
-
*/
|
|
74
|
-
leaveChannel(channel: I_WebsocketChannel, send?: boolean): void;
|
|
75
|
-
joinChannels(channels: I_WebsocketChannel[], send?: boolean): void;
|
|
76
|
-
leaveChannels(channels?: I_WebsocketChannel[], send?: boolean): void;
|
|
77
|
-
whoami(): {
|
|
78
|
-
id: string;
|
|
79
|
-
name: string;
|
|
80
|
-
};
|
|
81
|
-
send(message: string, options?: WebsocketMessageOptions): void;
|
|
82
|
-
send(message: WebsocketStructuredMessage): void;
|
|
83
|
-
subscribe(channel: string): void;
|
|
84
|
-
unsubscribe(channel: string): void;
|
|
85
|
-
static GetClientType(clients: Map<string, I_WebsocketClient> | undefined): typeof Client;
|
|
86
|
-
static System(): WebsocketEntityData;
|
|
87
|
-
}
|