vona-module-a-socket 5.0.0 → 5.1.2

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 (107) hide show
  1. package/LICENSE +21 -0
  2. package/cli/socketConnection/boilerplate/{{sceneName}}.{{beanName}}.ts_ +20 -0
  3. package/cli/socketNamespace/boilerplate/{{sceneName}}.{{beanName}}.ts_ +17 -0
  4. package/cli/socketPacket/boilerplate/{{sceneName}}.{{beanName}}.ts_ +15 -0
  5. package/dist/.metadata/index.d.ts +260 -0
  6. package/dist/.metadata/index.d.ts.map +1 -0
  7. package/dist/.metadata/this.d.ts +3 -0
  8. package/dist/.metadata/this.d.ts.map +1 -0
  9. package/dist/bean/bean.socket.d.ts +16 -0
  10. package/dist/bean/bean.socket.d.ts.map +1 -0
  11. package/dist/bean/broadcast.broadcast.d.ts +14 -0
  12. package/dist/bean/broadcast.broadcast.d.ts.map +1 -0
  13. package/dist/bean/broadcast.send.d.ts +13 -0
  14. package/dist/bean/broadcast.send.d.ts.map +1 -0
  15. package/dist/bean/hmr.socketConnection.d.ts +7 -0
  16. package/dist/bean/hmr.socketConnection.d.ts.map +1 -0
  17. package/dist/bean/hmr.socketPacket.d.ts +7 -0
  18. package/dist/bean/hmr.socketPacket.d.ts.map +1 -0
  19. package/dist/bean/socketConnection.alive.d.ts +14 -0
  20. package/dist/bean/socketConnection.alive.d.ts.map +1 -0
  21. package/dist/bean/socketConnection.app.d.ts +11 -0
  22. package/dist/bean/socketConnection.app.d.ts.map +1 -0
  23. package/dist/bean/socketConnection.cors.d.ts +11 -0
  24. package/dist/bean/socketConnection.cors.d.ts.map +1 -0
  25. package/dist/bean/socketConnection.event.d.ts +11 -0
  26. package/dist/bean/socketConnection.event.d.ts.map +1 -0
  27. package/dist/bean/socketConnection.instance.d.ts +11 -0
  28. package/dist/bean/socketConnection.instance.d.ts.map +1 -0
  29. package/dist/bean/socketConnection.passport.d.ts +12 -0
  30. package/dist/bean/socketConnection.passport.d.ts.map +1 -0
  31. package/dist/bean/socketConnection.ready.d.ts +11 -0
  32. package/dist/bean/socketConnection.ready.d.ts.map +1 -0
  33. package/dist/bean/socketPacket.event.d.ts +10 -0
  34. package/dist/bean/socketPacket.event.d.ts.map +1 -0
  35. package/dist/bean/socketPacket.performAction.d.ts +11 -0
  36. package/dist/bean/socketPacket.performAction.d.ts.map +1 -0
  37. package/dist/config/config.d.ts +15 -0
  38. package/dist/config/config.d.ts.map +1 -0
  39. package/dist/index.d.ts +4 -0
  40. package/dist/index.d.ts.map +1 -0
  41. package/dist/index.js +632 -0
  42. package/dist/index.js.map +1 -0
  43. package/dist/lib/beanSocketNamespaceBase.d.ts +7 -0
  44. package/dist/lib/beanSocketNamespaceBase.d.ts.map +1 -0
  45. package/dist/lib/const.d.ts +8 -0
  46. package/dist/lib/const.d.ts.map +1 -0
  47. package/dist/lib/index.d.ts +6 -0
  48. package/dist/lib/index.d.ts.map +1 -0
  49. package/dist/lib/socketConnection.d.ts +3 -0
  50. package/dist/lib/socketConnection.d.ts.map +1 -0
  51. package/dist/lib/socketNamespace.d.ts +4 -0
  52. package/dist/lib/socketNamespace.d.ts.map +1 -0
  53. package/dist/lib/socketPacket.d.ts +3 -0
  54. package/dist/lib/socketPacket.d.ts.map +1 -0
  55. package/dist/monkey.d.ts +7 -0
  56. package/dist/monkey.d.ts.map +1 -0
  57. package/dist/service/socket.d.ts +13 -0
  58. package/dist/service/socket.d.ts.map +1 -0
  59. package/dist/service/socketEvent.d.ts +10 -0
  60. package/dist/service/socketEvent.d.ts.map +1 -0
  61. package/dist/types/index.d.ts +6 -0
  62. package/dist/types/index.d.ts.map +1 -0
  63. package/dist/types/socket.d.ts +18 -0
  64. package/dist/types/socket.d.ts.map +1 -0
  65. package/dist/types/socketConnection.d.ts +34 -0
  66. package/dist/types/socketConnection.d.ts.map +1 -0
  67. package/dist/types/socketEvent.d.ts +46 -0
  68. package/dist/types/socketEvent.d.ts.map +1 -0
  69. package/dist/types/socketNamespace.d.ts +23 -0
  70. package/dist/types/socketNamespace.d.ts.map +1 -0
  71. package/dist/types/socketPacket.d.ts +25 -0
  72. package/dist/types/socketPacket.d.ts.map +1 -0
  73. package/package.json +58 -22
  74. package/src/.metadata/index.ts +359 -0
  75. package/src/.metadata/this.ts +2 -0
  76. package/src/bean/bean.socket.ts +60 -0
  77. package/src/bean/broadcast.broadcast.ts +23 -0
  78. package/src/bean/broadcast.send.ts +22 -0
  79. package/src/bean/hmr.socketConnection.ts +14 -0
  80. package/src/bean/hmr.socketPacket.ts +14 -0
  81. package/src/bean/socketConnection.alive.ts +48 -0
  82. package/src/bean/socketConnection.app.ts +26 -0
  83. package/src/bean/socketConnection.cors.ts +27 -0
  84. package/src/bean/socketConnection.event.ts +34 -0
  85. package/src/bean/socketConnection.instance.ts +23 -0
  86. package/src/bean/socketConnection.passport.ts +37 -0
  87. package/src/bean/socketConnection.ready.ts +21 -0
  88. package/src/bean/socketPacket.event.ts +29 -0
  89. package/src/bean/socketPacket.performAction.ts +37 -0
  90. package/src/config/config.ts +19 -0
  91. package/src/index.ts +3 -0
  92. package/src/lib/beanSocketNamespaceBase.ts +15 -0
  93. package/src/lib/const.ts +22 -0
  94. package/src/lib/index.ts +5 -0
  95. package/src/lib/socketConnection.ts +7 -0
  96. package/src/lib/socketNamespace.ts +9 -0
  97. package/src/lib/socketPacket.ts +7 -0
  98. package/src/monkey.ts +15 -0
  99. package/src/service/socket.ts +180 -0
  100. package/src/service/socketEvent.ts +30 -0
  101. package/src/types/index.ts +5 -0
  102. package/src/types/socket.ts +21 -0
  103. package/src/types/socketConnection.ts +41 -0
  104. package/src/types/socketEvent.ts +55 -0
  105. package/src/types/socketNamespace.ts +27 -0
  106. package/src/types/socketPacket.ts +30 -0
  107. package/README.md +0 -1
@@ -0,0 +1,37 @@
1
+ import type { Next } from 'vona';
2
+ import type { IDecoratorSocketConnectionOptions, ISocketConnectionExecute } from 'vona-module-a-socket';
3
+ import type { WebSocket } from 'ws';
4
+
5
+ import { BeanBase } from 'vona';
6
+ import { SocketConnection } from 'vona-module-a-socket';
7
+
8
+ export interface ISocketConnectionOptionsPassport extends IDecoratorSocketConnectionOptions {}
9
+
10
+ @SocketConnection<ISocketConnectionOptionsPassport>({ dependencies: 'a-socket:event' })
11
+ export class SocketConnectionPassport extends BeanBase implements ISocketConnectionExecute {
12
+ async enter(ws: WebSocket, _options: ISocketConnectionOptionsPassport, next: Next): Promise<void> {
13
+ // checkOauthCode
14
+ await this._checkOauthCode(ws);
15
+ // next
16
+ return next();
17
+ }
18
+
19
+ async exit(_ws: WebSocket, _options: ISocketConnectionOptionsPassport, next: Next): Promise<void> {
20
+ // next
21
+ return next();
22
+ }
23
+
24
+ private async _checkOauthCode(_ws: WebSocket) {
25
+ // auth token
26
+ if (!this.bean.passport.current) {
27
+ const code = this.ctx.query[this.scope.config.queryKey.passportCode] as string | undefined;
28
+ if (code) {
29
+ await this.bean.passport.checkAuthToken(code, 'access', { path: this.ctx.path });
30
+ }
31
+ }
32
+ // check current
33
+ if (!this.bean.passport.current) {
34
+ await this.bean.passport.signinWithAnonymous();
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,21 @@
1
+ import type { Next } from 'vona';
2
+ import type { IDecoratorSocketConnectionOptions, ISocketConnectionExecute } from 'vona-module-a-socket';
3
+ import type { WebSocket } from 'ws';
4
+
5
+ import { BeanBase } from 'vona';
6
+ import { SocketConnection } from 'vona-module-a-socket';
7
+
8
+ export interface ISocketConnectionOptionsReady extends IDecoratorSocketConnectionOptions {}
9
+
10
+ @SocketConnection<ISocketConnectionOptionsReady>({ dependencies: 'a-socket:passport' })
11
+ export class SocketConnectionReady extends BeanBase implements ISocketConnectionExecute {
12
+ async enter(_ws: WebSocket, _options: ISocketConnectionOptionsReady, next: Next): Promise<void> {
13
+ // next
14
+ return next();
15
+ }
16
+
17
+ async exit(_ws: WebSocket, _options: ISocketConnectionOptionsReady, next: Next): Promise<void> {
18
+ // next
19
+ return next();
20
+ }
21
+ }
@@ -0,0 +1,29 @@
1
+ import type { Next } from 'vona';
2
+ import type { WebSocket } from 'ws';
3
+
4
+ import { BeanBase } from 'vona';
5
+
6
+ import type { TypeSocketPacketEvent } from '../types/socketEvent.ts';
7
+ import type { IDecoratorSocketPacketOptions, ISocketPacketExecute } from '../types/socketPacket.ts';
8
+
9
+ import { SocketPacket } from '../lib/socketPacket.ts';
10
+ import { socketEventRecordReverse } from '../types/socketEvent.ts';
11
+
12
+ export interface ISocketPacketOptionsEvent extends IDecoratorSocketPacketOptions {}
13
+
14
+ @SocketPacket<ISocketPacketOptionsEvent>()
15
+ export class SocketPacketEvent extends BeanBase implements ISocketPacketExecute {
16
+ async execute(data: any, _ws: WebSocket, _options: ISocketPacketOptionsEvent, next: Next): Promise<void> {
17
+ let packet: TypeSocketPacketEvent;
18
+ const eventPrefix = this.scope.config.eventPrefix;
19
+ if (typeof data === 'string' && data.startsWith(eventPrefix)) {
20
+ const packetInner = JSON.parse(data.substring(eventPrefix.length));
21
+ const eventName = socketEventRecordReverse[packetInner[0]] ?? packetInner[0];
22
+ packet = [eventName, packetInner[1]];
23
+ } else {
24
+ packet = [undefined, data];
25
+ }
26
+ // next
27
+ return next(packet);
28
+ }
29
+ }
@@ -0,0 +1,37 @@
1
+ import type { Next } from 'vona';
2
+ import type { WebSocket } from 'ws';
3
+
4
+ import { BeanBase } from 'vona';
5
+
6
+ import type {
7
+ ISocketEventPerformActionOptionsInner,
8
+ ISocketEventRecord,
9
+ ISocketEventRecordSystem,
10
+ TypeSocketPacketEvent,
11
+ } from '../types/socketEvent.ts';
12
+ import type { IDecoratorSocketPacketOptions, ISocketPacketExecute } from '../types/socketPacket.ts';
13
+
14
+ import { SocketPacket } from '../lib/socketPacket.ts';
15
+
16
+ export interface ISocketPacketOptionsPerformAction extends IDecoratorSocketPacketOptions {}
17
+
18
+ @SocketPacket<ISocketPacketOptionsPerformAction>({ dependencies: 'a-socket:event' })
19
+ export class SocketPacketPerformAction extends BeanBase implements ISocketPacketExecute {
20
+ async execute(packet: TypeSocketPacketEvent, ws: WebSocket, _options: ISocketPacketOptionsPerformAction, next: Next): Promise<void> {
21
+ const eventName: keyof ISocketEventRecord | undefined = packet[0];
22
+ const data: ISocketEventPerformActionOptionsInner = packet[1];
23
+ if (eventName !== ('sysPerformAction' satisfies keyof ISocketEventRecordSystem)) return next();
24
+ try {
25
+ // performAction
26
+ const res = await this.$scope.executor.service.executor.performActionInner(data.m, data.p as never, {
27
+ innerAccess: false,
28
+ query: data.q,
29
+ body: data.b,
30
+ headers: data.h,
31
+ });
32
+ ws.sendEvent('sysPerformActionBack' satisfies keyof ISocketEventRecordSystem as never, { i: data.i, c: 0, d: res } as never);
33
+ } catch (err: any) {
34
+ ws.sendEvent('sysPerformActionBack' satisfies keyof ISocketEventRecordSystem as never, { i: data.i, c: err.code, m: err.message } as never);
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,19 @@
1
+ import type { VonaApplication } from 'vona';
2
+
3
+ import { $customKey } from 'vona';
4
+
5
+ export function config(_app: VonaApplication) {
6
+ return {
7
+ eventPrefix: '_:',
8
+ globalPrefix: '/ws',
9
+ queryKey: {
10
+ passportCode: $customKey('x-vona-passport-code'),
11
+ instanceName: $customKey('x-vona-instance-name'),
12
+ locale: $customKey('x-vona-locale'),
13
+ tz: $customKey('x-vona-tz'),
14
+ },
15
+ timeout: {
16
+ ping: 20000,
17
+ },
18
+ };
19
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './.metadata/index.ts';
2
+ export * from './lib/index.ts';
3
+ export * from './types/index.ts';
@@ -0,0 +1,15 @@
1
+ import { BeanBase, cast } from 'vona';
2
+
3
+ import type { ISendEventOptions } from '../types/socketEvent.ts';
4
+ import type { IDecoratorSocketNamespaceOptions } from '../types/socketNamespace.ts';
5
+
6
+ export class BeanSocketNamespaceBase<EVENTS extends {}> extends BeanBase {
7
+ send<K extends keyof EVENTS>(id: string, eventName: K, data?: EVENTS[K], options?: ISendEventOptions) {
8
+ this.$scope.socket.service.socketEvent.send(id, eventName, data, options);
9
+ }
10
+
11
+ broadcast<K extends keyof EVENTS>(eventName: K, data?: EVENTS[K], options?: ISendEventOptions) {
12
+ const namespace = cast<IDecoratorSocketNamespaceOptions>(this.$onionOptions).namespace;
13
+ this.$scope.socket.service.socketEvent.broadcast(namespace, eventName, data, options);
14
+ }
15
+ }
@@ -0,0 +1,22 @@
1
+ import type { VonaApplication } from 'vona';
2
+
3
+ export const SymbolCacheSocketConnections = Symbol('SymbolCacheSocketConnections');
4
+ export const SymbolCacheSocketPackets = Symbol('SymbolCacheSocketPackets');
5
+
6
+ export function getCacheSocketConnections(app: VonaApplication) {
7
+ if (!app.meta[SymbolCacheSocketConnections]) app.meta[SymbolCacheSocketConnections] = {};
8
+ return app.meta[SymbolCacheSocketConnections];
9
+ }
10
+
11
+ export function clearAllCacheSocketConnections(app: VonaApplication) {
12
+ delete app.meta[SymbolCacheSocketConnections];
13
+ }
14
+
15
+ export function getCacheSocketPackets(app: VonaApplication) {
16
+ if (!app.meta[SymbolCacheSocketPackets]) app.meta[SymbolCacheSocketPackets] = {};
17
+ return app.meta[SymbolCacheSocketPackets];
18
+ }
19
+
20
+ export function clearAllCacheSocketPackets(app: VonaApplication) {
21
+ delete app.meta[SymbolCacheSocketPackets];
22
+ }
@@ -0,0 +1,5 @@
1
+ export * from './beanSocketNamespaceBase.ts';
2
+ export * from './const.ts';
3
+ export * from './socketConnection.ts';
4
+ export * from './socketNamespace.ts';
5
+ export * from './socketPacket.ts';
@@ -0,0 +1,7 @@
1
+ import { createBeanDecorator } from 'vona';
2
+
3
+ import type { IDecoratorSocketConnectionOptions } from '../types/socketConnection.ts';
4
+
5
+ export function SocketConnection<T extends IDecoratorSocketConnectionOptions>(options?: T): ClassDecorator {
6
+ return createBeanDecorator('socketConnection', options);
7
+ }
@@ -0,0 +1,9 @@
1
+ import type { PowerPartial } from 'vona';
2
+
3
+ import { createBeanDecorator } from 'vona';
4
+
5
+ import type { IDecoratorSocketNamespaceOptions } from '../types/socketNamespace.ts';
6
+
7
+ export function SocketNamespace<T extends IDecoratorSocketNamespaceOptions>(options?: PowerPartial<T>): ClassDecorator {
8
+ return createBeanDecorator('socketNamespace', options);
9
+ }
@@ -0,0 +1,7 @@
1
+ import { createBeanDecorator } from 'vona';
2
+
3
+ import type { IDecoratorSocketPacketOptions } from '../types/socketPacket.ts';
4
+
5
+ export function SocketPacket<T extends IDecoratorSocketPacketOptions>(options?: T): ClassDecorator {
6
+ return createBeanDecorator('socketPacket', options);
7
+ }
package/src/monkey.ts ADDED
@@ -0,0 +1,15 @@
1
+ import type { IMonkeyAppClose, IMonkeyAppReady } from 'vona';
2
+
3
+ import { BeanSimple } from 'vona';
4
+
5
+ import { __ThisModule__ } from './.metadata/this.ts';
6
+
7
+ export class Monkey extends BeanSimple implements IMonkeyAppReady, IMonkeyAppClose {
8
+ async appReady() {
9
+ await this.app.scope(__ThisModule__).service.socket.appReady();
10
+ }
11
+
12
+ async appClose() {
13
+ await this.app.scope(__ThisModule__).service.socket.appClose();
14
+ }
15
+ }
@@ -0,0 +1,180 @@
1
+ import type { IncomingMessage } from 'node:http';
2
+ import type { IInstanceRecord, ILocaleRecord, Next } from 'vona';
3
+ import type { IOnionSlice } from 'vona-module-a-onion';
4
+
5
+ import { compose } from '@cabloy/compose';
6
+ import { AsyncResource } from 'node:async_hooks';
7
+ import { URL } from 'node:url';
8
+ import { BeanBase, uuidv4 } from 'vona';
9
+ import { Service } from 'vona-module-a-bean';
10
+ import { WebSocket, WebSocketServer } from 'ws';
11
+
12
+ import type {
13
+ ISocketConnectionComposeData,
14
+ ISocketConnectionExecute,
15
+ ISocketConnectionRecord,
16
+ ISocketPacketComposeData,
17
+ } from '../types/socketConnection.ts';
18
+ import type { ISocketEventRecordSystem } from '../types/socketEvent.ts';
19
+ import type { ISocketNamespaceRecord } from '../types/socketNamespace.ts';
20
+ import type { ISocketPacketExecute, ISocketPacketRecord } from '../types/socketPacket.ts';
21
+
22
+ import { getCacheSocketConnections, getCacheSocketPackets } from '../lib/const.ts';
23
+
24
+ @Service()
25
+ export class ServiceSocket extends BeanBase {
26
+ async appReady() {
27
+ // maybe running in play
28
+ if (!this.app.server) return;
29
+ // wss
30
+ this.app.wss = new WebSocketServer({ server: this.app.server });
31
+ this.app.wss.on('connection', (ws, req) => {
32
+ this._onConnection(ws, req);
33
+ });
34
+ }
35
+
36
+ async appClose() {
37
+ this.app.wss.close();
38
+ this.bean.socket.close();
39
+ }
40
+
41
+ private async _onConnection(ws: WebSocket, req: IncomingMessage) {
42
+ if (this.app.meta.appClose) {
43
+ // terminate
44
+ ws.terminate();
45
+ return;
46
+ }
47
+ const url = URL.parse(req.url ?? '', `https://${req.headers.host}`); // req.headers.host 'http://localhost'
48
+ const instanceName = (url?.searchParams.get(this.scope.config.queryKey.instanceName) || undefined) as keyof IInstanceRecord | undefined;
49
+ const locale = (url?.searchParams.get(this.scope.config.queryKey.locale) || undefined) as keyof ILocaleRecord | undefined;
50
+ const tz = (url?.searchParams.get(this.scope.config.queryKey.tz) || undefined) as string | undefined;
51
+ // enter
52
+ return await this.app.bean.executor.newCtx(
53
+ async () => {
54
+ const ctx = this.app.ctx;
55
+ // ws
56
+ Object.defineProperty(ctx, 'ws', {
57
+ get() {
58
+ return ws;
59
+ },
60
+ });
61
+ // enter
62
+ try {
63
+ // namespace
64
+ ws.namespace = this.getNamespace();
65
+ // id
66
+ ws.id = uuidv4();
67
+ // add
68
+ this.bean.socket.addClient(ws);
69
+ // compose
70
+ await this._getComposeSocketConnections(ws.namespace)({ method: 'enter', ws });
71
+ } catch (err: any) {
72
+ this.app.handleError(err);
73
+ // terminate
74
+ ws.terminate();
75
+ return;
76
+ }
77
+ // promise
78
+ return new Promise(resolve => {
79
+ // exit
80
+ ws.onclose = AsyncResource.bind(async () => {
81
+ try {
82
+ // compose
83
+ await this._getComposeSocketConnections(ws.namespace)({ method: 'exit', ws });
84
+ } catch (err: any) {
85
+ this.app.handleError(err);
86
+ } finally {
87
+ // remove
88
+ this.bean.socket.removeClient(ws);
89
+ resolve(undefined);
90
+ }
91
+ });
92
+ // message
93
+ ws.onmessage = AsyncResource.bind(async event => {
94
+ try {
95
+ await this._getComposeSocketPackets(ws.namespace)({ data: event.data, ws });
96
+ } catch (err: any) {
97
+ this.app.handleError(err);
98
+ }
99
+ });
100
+ // error
101
+ ws.onerror = AsyncResource.bind(event => {
102
+ this.$logger.error(event.error);
103
+ });
104
+ // onReady
105
+ ws.sendEvent('sysReady' satisfies keyof ISocketEventRecordSystem as never);
106
+ });
107
+ },
108
+ { dbInfo: { level: 0 }, innerAccess: false, req, instanceName, locale, tz },
109
+ ); // not set instance: true
110
+ }
111
+
112
+ public getNamespace(): keyof ISocketNamespaceRecord {
113
+ const globalPrefix = this.scope.config.globalPrefix;
114
+ let path = this.ctx.path;
115
+ if (path.startsWith(globalPrefix)) {
116
+ path = path.substring(globalPrefix.length);
117
+ }
118
+ if (!path) path = '/';
119
+ return path as keyof ISocketNamespaceRecord;
120
+ }
121
+
122
+ private _getComposeSocketConnections(namespace: keyof ISocketNamespaceRecord) {
123
+ const cacheSocketConnections = getCacheSocketConnections(this.app);
124
+ if (!cacheSocketConnections[namespace]) {
125
+ const connections = this.bean.onion.socketConnection.getOnionsEnabledWrapped(item => {
126
+ return this._wrapOnionConnection(item);
127
+ });
128
+ cacheSocketConnections[namespace] = compose(connections);
129
+ }
130
+ return cacheSocketConnections[namespace];
131
+ }
132
+
133
+ private _getComposeSocketPackets(namespace: keyof ISocketNamespaceRecord) {
134
+ const cacheSocketPackets = getCacheSocketPackets(this.app);
135
+ if (!cacheSocketPackets[namespace]) {
136
+ const packets = this.bean.onion.socketPacket.getOnionsEnabledWrapped(item => {
137
+ return this._wrapOnionPacket(item);
138
+ });
139
+ cacheSocketPackets[namespace] = compose(packets);
140
+ }
141
+ return cacheSocketPackets[namespace];
142
+ }
143
+
144
+ private _wrapOnionConnection<T extends keyof ISocketConnectionRecord>(item: IOnionSlice<ISocketConnectionRecord, T>) {
145
+ const fn = (data: ISocketConnectionComposeData, next: Next) => {
146
+ const options = item.beanOptions.options!;
147
+ // execute
148
+ const beanFullName = item.beanOptions.beanFullName;
149
+ const beanInstance = this.app.bean._getBean<ISocketConnectionExecute>(beanFullName as any);
150
+ if (!beanInstance) {
151
+ throw new Error(`socketConnection bean not found: ${beanFullName}`);
152
+ }
153
+ return beanInstance[data.method](data.ws, options, next);
154
+ };
155
+ fn._name = item.name;
156
+ return fn;
157
+ }
158
+
159
+ private _wrapOnionPacket<T extends keyof ISocketPacketRecord>(item: IOnionSlice<ISocketPacketRecord, T>) {
160
+ const fn = (data: ISocketPacketComposeData, next: Next) => {
161
+ const options = item.beanOptions.options!;
162
+ // execute
163
+ const beanFullName = item.beanOptions.beanFullName;
164
+ const beanInstance = this.app.bean._getBean<ISocketPacketExecute>(beanFullName as any);
165
+ if (!beanInstance) {
166
+ throw new Error(`socketPacket bean not found: ${beanFullName}`);
167
+ }
168
+ return beanInstance.execute(data.data, data.ws, options, _patchPacketNext(data, next));
169
+ };
170
+ fn._name = item.name;
171
+ return fn;
172
+ }
173
+ }
174
+
175
+ function _patchPacketNext(data: ISocketPacketComposeData, next) {
176
+ return (...args) => {
177
+ const context = args.length === 0 ? data.data : args[0];
178
+ return next({ data: context, ws: data.ws });
179
+ };
180
+ }
@@ -0,0 +1,30 @@
1
+ import { BeanBase } from 'vona';
2
+ import { Service } from 'vona-module-a-bean';
3
+
4
+ import type { ISendEventOptions } from '../types/socketEvent.ts';
5
+ import type { ISocketNamespaceRecord } from '../types/socketNamespace.ts';
6
+
7
+ @Service()
8
+ export class ServiceSocketEvent extends BeanBase {
9
+ send(id: string, eventName: PropertyKey, data?: unknown, options?: ISendEventOptions) {
10
+ this.sendWorker(id, eventName, data, options);
11
+ this.scope.broadcast.send.emit({ id, eventName, data, options });
12
+ }
13
+
14
+ sendWorker(id: string, eventName: PropertyKey, data?: unknown, options?: ISendEventOptions) {
15
+ this.bean.socket.clients[id]?.sendEvent(eventName, data, options);
16
+ }
17
+
18
+ broadcast(namespace: keyof ISocketNamespaceRecord, eventName: PropertyKey, data?: unknown, options?: ISendEventOptions) {
19
+ this.broadcastWorker(namespace, eventName, data, options);
20
+ this.scope.broadcast.broadcast.emit({ namespace, eventName, data, options });
21
+ }
22
+
23
+ broadcastWorker(namespace: keyof ISocketNamespaceRecord, eventName: PropertyKey, data?: unknown, options?: ISendEventOptions) {
24
+ const ids = this.bean.socket.clientsNamespace[namespace];
25
+ if (!ids) return;
26
+ for (const id of ids) {
27
+ this.bean.socket.clients[id]?.sendEvent(eventName, data, options);
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,5 @@
1
+ export * from './socket.ts';
2
+ export * from './socketConnection.ts';
3
+ export * from './socketEvent.ts';
4
+ export * from './socketNamespace.ts';
5
+ export * from './socketPacket.ts';
@@ -0,0 +1,21 @@
1
+ import type { WebSocket, WebSocketServer } from 'ws';
2
+
3
+ import type { ISocketNamespaceRecord } from './socketNamespace.ts';
4
+
5
+ declare module 'vona' {
6
+ export interface VonaApplication {
7
+ wss: WebSocketServer;
8
+ }
9
+
10
+ export interface VonaContext {
11
+ get ws(): WebSocket;
12
+ }
13
+ }
14
+
15
+ declare module 'ws' {
16
+ export interface WebSocket {
17
+ namespace: keyof ISocketNamespaceRecord;
18
+ id: string;
19
+ isAlive: boolean;
20
+ }
21
+ }
@@ -0,0 +1,41 @@
1
+ import type { Next, OmitNever } from 'vona';
2
+ import type { IOnionOptionsDeps, ServiceOnion, TypeOnionOptionsBaseSimple } from 'vona-module-a-onion';
3
+ import type { WebSocket } from 'ws';
4
+
5
+ import type { ISocketNamespaceRecord } from './socketNamespace.ts';
6
+
7
+ export interface ISocketConnectionRecord {}
8
+
9
+ export interface ISocketConnectionExecute {
10
+ enter: (ws: WebSocket, options: IDecoratorSocketConnectionOptions, next: Next) => Promise<void>;
11
+ exit: (ws: WebSocket, options: IDecoratorSocketConnectionOptions, next: Next) => Promise<void>;
12
+ }
13
+
14
+ export interface IDecoratorSocketConnectionOptions
15
+ extends TypeOnionOptionsBaseSimple<keyof ISocketNamespaceRecord>, IOnionOptionsDeps<keyof ISocketConnectionRecord> {}
16
+
17
+ export interface ISocketConnectionComposeData {
18
+ method: 'enter' | 'exit';
19
+ ws: WebSocket;
20
+ }
21
+
22
+ export interface ISocketPacketComposeData {
23
+ data: any;
24
+ ws: WebSocket;
25
+ }
26
+
27
+ declare module 'vona-module-a-onion' {
28
+ export interface BeanOnion {
29
+ socketConnection: ServiceOnion<ISocketConnectionRecord>;
30
+ }
31
+ }
32
+
33
+ declare module 'vona' {
34
+ export interface ConfigOnions {
35
+ socketConnection: OmitNever<ISocketConnectionRecord>;
36
+ }
37
+
38
+ export interface IBeanSceneRecord {
39
+ socketConnection: never;
40
+ }
41
+ }
@@ -0,0 +1,55 @@
1
+ export interface ISocketEventRecordSystem {
2
+ sysReady: never;
3
+ sysPerformAction: never;
4
+ sysPerformActionBack: never;
5
+ }
6
+
7
+ export interface ISocketEventRecord {}
8
+
9
+ export const socketEventRecord = {
10
+ sysReady: '_a',
11
+ sysPerformAction: '_b',
12
+ sysPerformActionBack: '_c',
13
+ };
14
+ export const socketEventRecordReverse = {
15
+ _a: 'sysReady',
16
+ _b: 'sysPerformAction',
17
+ _c: 'sysPerformActionBack',
18
+ };
19
+
20
+ export type TypeSocketEventPerformActionMethod = 'get' | 'post' | 'delete' | 'put' | 'patch';
21
+ export interface ISocketEventPerformActionOptions {
22
+ method: TypeSocketEventPerformActionMethod;
23
+ path: string;
24
+ body?: any;
25
+ headers?: object;
26
+ }
27
+ export interface ISocketEventPerformActionOptionsInner {
28
+ i: number;
29
+ m: TypeSocketEventPerformActionMethod;
30
+ q?: object;
31
+ p: string;
32
+ b?: any;
33
+ h?: object;
34
+ }
35
+
36
+ export type TypeSocketPacketEvent<K extends keyof ISocketEventRecord = never> = [K | undefined, ISocketEventRecord[K] | any];
37
+
38
+ export interface ISendEventOptions {
39
+ mask?: boolean | undefined;
40
+ binary?: boolean | undefined;
41
+ compress?: boolean | undefined;
42
+ fin?: boolean | undefined;
43
+ }
44
+
45
+ declare module 'ws' {
46
+ export interface WebSocket {
47
+ sendEvent<K extends keyof ISocketEventRecord>(
48
+ eventName: K | PropertyKey,
49
+ data?: ISocketEventRecord[K] | any,
50
+ options?: ISendEventOptions,
51
+ cb?: (err?: Error) => void,
52
+ ): void;
53
+ sendEvent<K extends keyof ISocketEventRecord>(eventName: K | PropertyKey, data?: ISocketEventRecord[K] | any, cb?: (err?: Error) => void): void;
54
+ }
55
+ }
@@ -0,0 +1,27 @@
1
+ import type { OmitNever } from 'vona';
2
+ import type { ServiceOnion } from 'vona-module-a-onion';
3
+
4
+ export interface ISocketNamespaceRecord {
5
+ '/': never;
6
+ }
7
+
8
+ export interface IDecoratorSocketNamespaceOptions<Events extends {} = {}> {
9
+ namespace: keyof ISocketNamespaceRecord;
10
+ events: Events;
11
+ }
12
+
13
+ declare module 'vona-module-a-onion' {
14
+ export interface BeanOnion {
15
+ socketNamespace: ServiceOnion<ISocketNamespaceRecord>;
16
+ }
17
+ }
18
+
19
+ declare module 'vona' {
20
+ export interface ConfigOnions {
21
+ socketNamespace: OmitNever<ISocketNamespaceRecord>;
22
+ }
23
+
24
+ export interface IBeanSceneRecord {
25
+ socketNamespace: never;
26
+ }
27
+ }
@@ -0,0 +1,30 @@
1
+ import type { Next, OmitNever } from 'vona';
2
+ import type { IOnionOptionsDeps, ServiceOnion, TypeOnionOptionsBaseSimple } from 'vona-module-a-onion';
3
+ import type { WebSocket } from 'ws';
4
+
5
+ import type { ISocketNamespaceRecord } from './socketNamespace.ts';
6
+
7
+ export interface ISocketPacketRecord {}
8
+
9
+ export interface ISocketPacketExecute {
10
+ execute: (data: any, ws: WebSocket, options: IDecoratorSocketPacketOptions, next: Next) => Promise<void>;
11
+ }
12
+
13
+ export interface IDecoratorSocketPacketOptions
14
+ extends TypeOnionOptionsBaseSimple<keyof ISocketNamespaceRecord>, IOnionOptionsDeps<keyof ISocketPacketRecord> {}
15
+
16
+ declare module 'vona-module-a-onion' {
17
+ export interface BeanOnion {
18
+ socketPacket: ServiceOnion<ISocketPacketRecord>;
19
+ }
20
+ }
21
+
22
+ declare module 'vona' {
23
+ export interface ConfigOnions {
24
+ socketPacket: OmitNever<ISocketPacketRecord>;
25
+ }
26
+
27
+ export interface IBeanSceneRecord {
28
+ socketPacket: never;
29
+ }
30
+ }
package/README.md DELETED
@@ -1 +0,0 @@
1
- # @cabloy/set