@lemoncloud/chatic-sockets-lib 0.2.0 → 0.2.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.
Files changed (35) hide show
  1. package/dist/client-socket-v2/create-client-socket-v2.js +5 -0
  2. package/dist/client-socket-v2/gateways/auth-gateway.d.ts +6 -0
  3. package/dist/client-socket-v2/gateways/auth-gateway.js +11 -0
  4. package/dist/client-socket-v2/gateways/channel-gateway.d.ts +38 -0
  5. package/dist/client-socket-v2/gateways/channel-gateway.js +24 -0
  6. package/dist/client-socket-v2/gateways/chat-gateway.d.ts +22 -0
  7. package/dist/client-socket-v2/gateways/chat-gateway.js +16 -0
  8. package/dist/client-socket-v2/gateways/cloud-gateway.d.ts +12 -0
  9. package/dist/client-socket-v2/gateways/cloud-gateway.js +11 -0
  10. package/dist/client-socket-v2/gateways/user-gateway.d.ts +26 -0
  11. package/dist/client-socket-v2/gateways/user-gateway.js +18 -0
  12. package/dist/client-socket-v2/index.d.ts +8 -0
  13. package/dist/client-socket-v2/index.js +7 -0
  14. package/dist/client-socket-v2/socket-transport.d.ts +14 -4
  15. package/dist/client-socket-v2/socket-transport.js +58 -60
  16. package/dist/client-socket-v2/types.d.ts +16 -7
  17. package/dist/lib/channel/types.d.ts +175 -0
  18. package/dist/lib/channel/types.js +2 -0
  19. package/dist/lib/chat/types.d.ts +79 -0
  20. package/dist/lib/chat/types.js +2 -0
  21. package/dist/lib/cloud/types.d.ts +18 -0
  22. package/dist/lib/cloud/types.js +2 -0
  23. package/dist/lib/device/types.d.ts +1 -0
  24. package/dist/lib/socket-actions.d.ts +239 -0
  25. package/dist/lib/socket-actions.js +167 -0
  26. package/dist/lib/socket-inputs.d.ts +13 -0
  27. package/dist/lib/socket-inputs.js +8 -0
  28. package/dist/lib/sockets/types.d.ts +27 -0
  29. package/dist/lib/sockets/types.js +17 -0
  30. package/dist/lib/types.d.ts +2 -0
  31. package/dist/lib/user/types.d.ts +102 -0
  32. package/dist/lib/user/types.js +2 -0
  33. package/dist/modules/chat/types.d.ts +65 -0
  34. package/dist/modules/chat/types.js +55 -0
  35. package/package.json +1 -1
@@ -217,8 +217,13 @@ class ClientSocketV2Impl {
217
217
  return ((_a = this.aliases[source]) !== null && _a !== void 0 ? _a : source);
218
218
  };
219
219
  this.handleRawMessage = (raw) => {
220
+ var _a, _b, _c, _d;
220
221
  try {
222
+ if (((_b = (_a = this.options).shouldHandleRaw) === null || _b === void 0 ? void 0 : _b.call(_a, { raw })) === false)
223
+ return;
221
224
  const message = parseMessage(raw);
225
+ if (((_d = (_c = this.options).shouldHandleMessage) === null || _d === void 0 ? void 0 : _d.call(_c, { raw, message })) === false)
226
+ return;
222
227
  const handled = this.pending.settle(message);
223
228
  const event = { message, raw };
224
229
  this.messageListeners.forEach(listener => listener(event));
@@ -0,0 +1,6 @@
1
+ import type { AuthUpdateInput, AuthUpdateResponse } from '../../lib/auth/types';
2
+ import type { ClientSocketV2 } from '../types';
3
+ export interface AuthGateway {
4
+ update(data: AuthUpdateInput): Promise<AuthUpdateResponse>;
5
+ }
6
+ export declare const createAuthGateway: (client: ClientSocketV2) => AuthGateway;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAuthGateway = void 0;
4
+ const create_domain_gateway_1 = require("./create-domain-gateway");
5
+ const createAuthGateway = (client) => {
6
+ const gateway = (0, create_domain_gateway_1.createDomainGateway)('auth', client);
7
+ return {
8
+ update: data => gateway.request('update', data),
9
+ };
10
+ };
11
+ exports.createAuthGateway = createAuthGateway;
@@ -0,0 +1,38 @@
1
+ import type { ChannelCreateInput, ChannelDeleteInput, ChannelGetSelfInput, ChannelInviteInput, ChannelJoinInput, ChannelLeaveInput, ChannelListUserInput, ChannelMineInput, ChannelSyncInput, ChannelSyncProfileInput, ChannelSyncUsersInput, ChannelUnreadsInput, ChannelUpdateInput, ChannelUpdateJoinInput } from '../../lib/channel/types';
2
+ import type { ClientSocketV2 } from '../types';
3
+ /**
4
+ * channel 도메인 게이트웨이.
5
+ * - 모든 응답은 @lemoncloud/chatic-socials-api 소유(pass-through)이므로 제네릭 T로 호출부에서 주입한다.
6
+ * - 미주입 시 unknown(any 아님 — 사용 전 narrowing/캐스팅 강제). 각 메서드 주석의 $socials.<View> 가 실제 응답 타입.
7
+ */
8
+ export interface ChannelGateway {
9
+ /** $socials.ChannelView */
10
+ create<T = unknown>(data: ChannelCreateInput): Promise<T>;
11
+ /** $socials.ChannelView */
12
+ update<T = unknown>(data: ChannelUpdateInput): Promise<T>;
13
+ /** $socials.ChannelView */
14
+ delete<T = unknown>(data: ChannelDeleteInput): Promise<T>;
15
+ /** $socials.JoinView */
16
+ join<T = unknown>(data: ChannelJoinInput): Promise<T>;
17
+ /** $socials.ChannelView */
18
+ leave<T = unknown>(data: ChannelLeaveInput): Promise<T>;
19
+ /** $socials.ChannelView */
20
+ getSelf<T = unknown>(data?: ChannelGetSelfInput | null): Promise<T>;
21
+ /** ListResult<$socials.ChannelView> */
22
+ mine<T = unknown>(data?: ChannelMineInput | null): Promise<T>;
23
+ /** ListResult<$socials.UserView> */
24
+ listUser<T = unknown>(data: ChannelListUserInput): Promise<T>;
25
+ /** $socials.ChannelView */
26
+ invite<T = unknown>(data: ChannelInviteInput): Promise<T>;
27
+ /** $socials.JoinView */
28
+ updateJoin<T = unknown>(data: ChannelUpdateJoinInput): Promise<T>;
29
+ /** $socials.UnreadsSummaryView */
30
+ unreads<T = unknown>(data?: ChannelUnreadsInput | null): Promise<T>;
31
+ /** $socials.ChannelSyncView */
32
+ sync<T = unknown>(data?: ChannelSyncInput | null): Promise<T>;
33
+ /** $socials.ChannelUsersSyncView */
34
+ syncUsers<T = unknown>(data?: ChannelSyncUsersInput | null): Promise<T>;
35
+ /** $socials.SiteProfileSyncView */
36
+ syncProfile<T = unknown>(data?: ChannelSyncProfileInput | null): Promise<T>;
37
+ }
38
+ export declare const createChannelGateway: (client: ClientSocketV2) => ChannelGateway;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createChannelGateway = void 0;
4
+ const create_domain_gateway_1 = require("./create-domain-gateway");
5
+ const createChannelGateway = (client) => {
6
+ const gateway = (0, create_domain_gateway_1.createDomainGateway)('channel', client);
7
+ return {
8
+ create: (data) => gateway.request('create', data),
9
+ update: (data) => gateway.request('update', data),
10
+ delete: (data) => gateway.request('delete', data),
11
+ join: (data) => gateway.request('join', data),
12
+ leave: (data) => gateway.request('leave', data),
13
+ getSelf: (data) => gateway.request('get-self', data !== null && data !== void 0 ? data : null),
14
+ mine: (data) => gateway.request('mine', data !== null && data !== void 0 ? data : null),
15
+ listUser: (data) => gateway.request('list-user', data),
16
+ invite: (data) => gateway.request('invite', data),
17
+ updateJoin: (data) => gateway.request('update-join', data),
18
+ unreads: (data) => gateway.request('unreads', data !== null && data !== void 0 ? data : null),
19
+ sync: (data) => gateway.request('sync', data !== null && data !== void 0 ? data : null),
20
+ syncUsers: (data) => gateway.request('sync-users', data !== null && data !== void 0 ? data : null),
21
+ syncProfile: (data) => gateway.request('sync-site-profile', data !== null && data !== void 0 ? data : null),
22
+ };
23
+ };
24
+ exports.createChannelGateway = createChannelGateway;
@@ -0,0 +1,22 @@
1
+ import type { ChatFeedInput, ChatReadInput, ChatSendInput, ChatUpdateInput, ChatDeleteInput, ChatGetInput } from '../../lib/chat/types';
2
+ import type { ClientSocketV2 } from '../types';
3
+ /**
4
+ * chat 도메인 게이트웨이.
5
+ * - 모든 응답은 @lemoncloud/chatic-socials-api 소유(pass-through)이므로 제네릭 T로 호출부에서 주입한다.
6
+ * - 미주입 시 unknown. 각 메서드 주석의 $socials.<View> 가 실제 응답 타입.
7
+ */
8
+ export interface ChatGateway {
9
+ /** $socials.ChatView */
10
+ send<T = unknown>(data: ChatSendInput): Promise<T>;
11
+ /** $socials.ChatView */
12
+ get<T = unknown>(data: ChatGetInput): Promise<T>;
13
+ /** $socials.JoinView */
14
+ read<T = unknown>(data: ChatReadInput): Promise<T>;
15
+ /** $socials.ChatFeedResult */
16
+ feed<T = unknown>(data: ChatFeedInput): Promise<T>;
17
+ /** $socials.ChatView */
18
+ update<T = unknown>(data: ChatUpdateInput): Promise<T>;
19
+ /** $socials.ChatView */
20
+ delete<T = unknown>(data: ChatDeleteInput): Promise<T>;
21
+ }
22
+ export declare const createChatGateway: (client: ClientSocketV2) => ChatGateway;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createChatGateway = void 0;
4
+ const create_domain_gateway_1 = require("./create-domain-gateway");
5
+ const createChatGateway = (client) => {
6
+ const gateway = (0, create_domain_gateway_1.createDomainGateway)('chat', client);
7
+ return {
8
+ send: (data) => gateway.request('send', data),
9
+ get: (data) => gateway.request('get', data),
10
+ read: (data) => gateway.request('read', data),
11
+ feed: (data) => gateway.request('feed', data),
12
+ update: (data) => gateway.request('update', data),
13
+ delete: (data) => gateway.request('delete', data),
14
+ };
15
+ };
16
+ exports.createChatGateway = createChatGateway;
@@ -0,0 +1,12 @@
1
+ import type { CloudUpdateInput } from '../../lib/cloud/types';
2
+ import type { ClientSocketV2 } from '../types';
3
+ /**
4
+ * cloud 도메인 게이트웨이.
5
+ * - 응답은 @lemoncloud/chatic-backend-api 소유(pass-through)이므로 제네릭 T로 호출부에서 주입한다.
6
+ * - 미주입 시 unknown. 주석의 $backend.<View> 가 실제 응답 타입.
7
+ */
8
+ export interface CloudGateway {
9
+ /** $backend.CloudView */
10
+ update<T = unknown>(data: CloudUpdateInput): Promise<T>;
11
+ }
12
+ export declare const createCloudGateway: (client: ClientSocketV2) => CloudGateway;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCloudGateway = void 0;
4
+ const create_domain_gateway_1 = require("./create-domain-gateway");
5
+ const createCloudGateway = (client) => {
6
+ const gateway = (0, create_domain_gateway_1.createDomainGateway)('cloud', client);
7
+ return {
8
+ update: (data) => gateway.request('update', data),
9
+ };
10
+ };
11
+ exports.createCloudGateway = createCloudGateway;
@@ -0,0 +1,26 @@
1
+ import type { UserGetSiteProfileInput, UserInviteBatchInput, UserInviteInput, UserMakeSiteInput, UserMySiteInput, UserSetSiteProfileInput, UserUpdateProfileInput, UserUpdateSiteInput } from '../../lib/user/types';
2
+ import type { ClientSocketV2 } from '../types';
3
+ /**
4
+ * user 도메인 게이트웨이.
5
+ * - 응답은 @lemoncloud/chatic-socials-api / chatic-backend-api 소유(pass-through)이므로 제네릭 T로 호출부에서 주입한다.
6
+ * - 미주입 시 unknown. 각 메서드 주석의 $socials/$backend.<View> 가 실제 응답 타입(서버가 unknown으로 둔 것은 표기 생략).
7
+ */
8
+ export interface UserGateway {
9
+ /** $socials.ProfileView */
10
+ getSiteProfile<T = unknown>(data?: UserGetSiteProfileInput | null): Promise<T>;
11
+ /** ListResult<$backend.MySiteView> */
12
+ mySite<T = unknown>(data?: UserMySiteInput | null): Promise<T>;
13
+ /** $backend.MySiteView */
14
+ makeSite<T = unknown>(data: UserMakeSiteInput): Promise<T>;
15
+ /** $backend.MyInviteView */
16
+ invite<T = unknown>(data: UserInviteInput): Promise<T>;
17
+ /** ListResult<$backend.MyInviteView> */
18
+ inviteBatch<T = unknown>(data: UserInviteBatchInput): Promise<T>;
19
+ /** $socials.UserView */
20
+ updateProfile<T = unknown>(data: UserUpdateProfileInput): Promise<T>;
21
+ /** $backend.MySiteView */
22
+ updateSite<T = unknown>(data: UserUpdateSiteInput): Promise<T>;
23
+ /** $socials.ProfileView */
24
+ setSiteProfile<T = unknown>(data: UserSetSiteProfileInput): Promise<T>;
25
+ }
26
+ export declare const createUserGateway: (client: ClientSocketV2) => UserGateway;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createUserGateway = void 0;
4
+ const create_domain_gateway_1 = require("./create-domain-gateway");
5
+ const createUserGateway = (client) => {
6
+ const gateway = (0, create_domain_gateway_1.createDomainGateway)('user', client);
7
+ return {
8
+ getSiteProfile: (data) => gateway.request('get-site-profile', data !== null && data !== void 0 ? data : null),
9
+ mySite: (data) => gateway.request('my-site', data !== null && data !== void 0 ? data : null),
10
+ makeSite: (data) => gateway.request('make-site', data),
11
+ invite: (data) => gateway.request('invite', data),
12
+ inviteBatch: (data) => gateway.request('invite-batch', data),
13
+ updateProfile: (data) => gateway.request('update-profile', data),
14
+ updateSite: (data) => gateway.request('update-site', data),
15
+ setSiteProfile: (data) => gateway.request('set-site-profile', data),
16
+ };
17
+ };
18
+ exports.createUserGateway = createUserGateway;
@@ -19,7 +19,15 @@ export * from './create-device-runtime';
19
19
  export * from './plans/device-sync-plan';
20
20
  export * from './gateways/create-domain-gateway';
21
21
  export * from './gateways/device-gateway';
22
+ export * from './gateways/channel-gateway';
23
+ export * from './gateways/auth-gateway';
24
+ export * from './gateways/chat-gateway';
25
+ export * from './gateways/cloud-gateway';
26
+ export * from './gateways/user-gateway';
22
27
  export * from '../lib/types';
23
28
  export * from '../lib/device/types';
24
29
  export * from '../lib/device/contracts';
25
30
  export * from '../lib/auth/types';
31
+ export * from '../lib/socket-actions';
32
+ export * from '../lib/socket-inputs';
33
+ export type { AuthUpdateInput, DeviceGetInput, DeviceSaveInput, DeviceSyncInput } from '../lib/socket-inputs';
@@ -37,7 +37,14 @@ __exportStar(require("./create-device-runtime"), exports);
37
37
  __exportStar(require("./plans/device-sync-plan"), exports);
38
38
  __exportStar(require("./gateways/create-domain-gateway"), exports);
39
39
  __exportStar(require("./gateways/device-gateway"), exports);
40
+ __exportStar(require("./gateways/channel-gateway"), exports);
41
+ __exportStar(require("./gateways/auth-gateway"), exports);
42
+ __exportStar(require("./gateways/chat-gateway"), exports);
43
+ __exportStar(require("./gateways/cloud-gateway"), exports);
44
+ __exportStar(require("./gateways/user-gateway"), exports);
40
45
  __exportStar(require("../lib/types"), exports);
41
46
  __exportStar(require("../lib/device/types"), exports);
42
47
  __exportStar(require("../lib/device/contracts"), exports);
43
48
  __exportStar(require("../lib/auth/types"), exports);
49
+ __exportStar(require("../lib/socket-actions"), exports);
50
+ __exportStar(require("../lib/socket-inputs"), exports);
@@ -3,13 +3,21 @@ export interface WebSocketTransportOptions {
3
3
  /** connect() open 이벤트 대기 timeout. 만료 시 socket close + Promise reject. 0이면 무한 대기. 기본 10000 */
4
4
  connectTimeoutMs?: number;
5
5
  }
6
+ /**
7
+ * `SocketTransport` adapter over lemon-model's owned WebSocket network.
8
+ *
9
+ * Raw network concerns (socket creation, OPEN send guard, actual close, raw event mapping) are owned by
10
+ * `OwnedWebSocketNetwork`. This adapter only translates that single-shot network into chatic's reconnectable,
11
+ * code-carrying, 4-event / 5-state lifecycle. The connect timeout stays here because it belongs to the
12
+ * reconnect/backoff policy, not the raw transport.
13
+ */
6
14
  export declare class WebSocketTransport implements SocketTransport {
7
15
  readonly url: string;
8
16
  readonly protocols?: string | string[];
9
- private readonly socketFactory;
17
+ private readonly socketFactory?;
10
18
  private _state;
11
- private socket?;
12
- private unbinders;
19
+ private network?;
20
+ private networkUnsubs;
13
21
  private readonly listeners;
14
22
  private readonly connectTimeoutMs;
15
23
  private connecting?;
@@ -21,5 +29,7 @@ export declare class WebSocketTransport implements SocketTransport {
21
29
  disconnect: (code?: number, reason?: string) => Promise<void>;
22
30
  send: (raw: string) => void;
23
31
  private emit;
24
- private cleanupSocket;
32
+ /** bridge chatic's `SocketLike` factory to the owned network's `WebSocketClosable` factory */
33
+ private buildSocketFactory;
34
+ private cleanupNetwork;
25
35
  }
@@ -10,30 +10,24 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.WebSocketTransport = void 0;
13
- const common_1 = require("./common");
14
- const OPEN_STATE = 1;
15
- const bindSocketListener = (socket, type, listener) => {
16
- if (socket.addEventListener) {
17
- socket.addEventListener(type, listener);
18
- return () => { var _a; return (_a = socket.removeEventListener) === null || _a === void 0 ? void 0 : _a.call(socket, type, listener); };
19
- }
20
- const key = `on${type}`;
21
- const prev = socket[key];
22
- socket[key] = listener;
23
- return () => {
24
- if (socket[key] === listener)
25
- socket[key] = prev !== null && prev !== void 0 ? prev : null;
26
- };
27
- };
13
+ const lemon_model_1 = require("lemon-model");
28
14
  const DEFAULT_CONNECT_TIMEOUT_MS = 10000;
15
+ /**
16
+ * `SocketTransport` adapter over lemon-model's owned WebSocket network.
17
+ *
18
+ * Raw network concerns (socket creation, OPEN send guard, actual close, raw event mapping) are owned by
19
+ * `OwnedWebSocketNetwork`. This adapter only translates that single-shot network into chatic's reconnectable,
20
+ * code-carrying, 4-event / 5-state lifecycle. The connect timeout stays here because it belongs to the
21
+ * reconnect/backoff policy, not the raw transport.
22
+ */
29
23
  class WebSocketTransport {
30
- constructor(url, protocols, socketFactory = context => new globalThis.WebSocket(context.url, context.protocols), options) {
24
+ constructor(url, protocols, socketFactory, options) {
31
25
  var _a;
32
26
  this.url = url;
33
27
  this.protocols = protocols;
34
28
  this.socketFactory = socketFactory;
35
29
  this._state = 'idle';
36
- this.unbinders = [];
30
+ this.networkUnsubs = [];
37
31
  this.listeners = new Map();
38
32
  this.on = (type, listener) => {
39
33
  var _a;
@@ -51,17 +45,23 @@ class WebSocketTransport {
51
45
  return;
52
46
  if (this.connecting)
53
47
  return this.connecting;
54
- this.cleanupSocket();
48
+ this.cleanupNetwork();
55
49
  this._state = 'connecting';
56
- const socket = this.socketFactory({ url: this.url, protocols: this.protocols });
57
- this.socket = socket;
50
+ const network = (0, lemon_model_1.createOwnedWebSocketNetwork)({
51
+ url: this.url,
52
+ protocols: this.protocols,
53
+ socketFactory: this.buildSocketFactory(),
54
+ connectTimeoutMs: 0, // connect timeout is owned here (reconnect policy), not by the network
55
+ });
56
+ this.network = network;
58
57
  this.connecting = new Promise((resolve, reject) => {
59
- let connectSettled = false;
58
+ var _a;
59
+ let settled = false;
60
60
  let timeoutTimer;
61
61
  const settleConnect = (handler) => {
62
- if (connectSettled)
62
+ if (settled)
63
63
  return;
64
- connectSettled = true;
64
+ settled = true;
65
65
  if (timeoutTimer)
66
66
  clearTimeout(timeoutTimer);
67
67
  this.connecting = undefined;
@@ -71,75 +71,73 @@ class WebSocketTransport {
71
71
  this.cancelConnecting = error => settleConnect(() => reject(error));
72
72
  if (this.connectTimeoutMs > 0) {
73
73
  timeoutTimer = setTimeout(() => {
74
- if (connectSettled)
75
- return;
76
74
  const error = new Error(`408 CONNECT TIMEOUT - WebSocketTransport.connect() after ${this.connectTimeoutMs}ms`);
77
75
  settleConnect(() => reject(error));
78
- try {
79
- socket.close(1000, 'connect-timeout');
80
- }
81
- catch (_a) {
82
- /** ignore — best-effort cleanup */
83
- }
76
+ network.close(1000, 'connect-timeout');
84
77
  }, this.connectTimeoutMs);
85
78
  }
86
- this.unbinders = [
87
- bindSocketListener(socket, 'open', () => {
88
- this._state = 'connected';
89
- this.emit('open', undefined);
90
- settleConnect(() => resolve());
91
- }),
92
- bindSocketListener(socket, 'close', event => {
93
- this._state = 'closed';
94
- this.emit('close', {
95
- code: event === null || event === void 0 ? void 0 : event.code,
96
- reason: event === null || event === void 0 ? void 0 : event.reason,
97
- wasClean: event === null || event === void 0 ? void 0 : event.wasClean,
98
- });
99
- settleConnect(() => resolve());
100
- }),
101
- bindSocketListener(socket, 'error', event => {
79
+ const unsubOpen = (_a = network.onOpen) === null || _a === void 0 ? void 0 : _a.call(network, () => {
80
+ this._state = 'connected';
81
+ this.emit('open', undefined);
82
+ settleConnect(() => resolve());
83
+ });
84
+ this.networkUnsubs = [
85
+ network.onMessage(raw => this.emit('message', { data: raw })),
86
+ network.onError((event, context) => {
102
87
  var _a;
103
- const error = (_a = event === null || event === void 0 ? void 0 : event.error) !== null && _a !== void 0 ? _a : new Error(`socket transport error`);
88
+ if (context.scope === 'ownedWebSocket.close') {
89
+ const close = event;
90
+ this._state = 'closed';
91
+ this.emit('close', { code: close === null || close === void 0 ? void 0 : close.code, reason: close === null || close === void 0 ? void 0 : close.reason, wasClean: close === null || close === void 0 ? void 0 : close.wasClean });
92
+ settleConnect(() => resolve()); // close-before-open resolves connect (reconnect drives off state)
93
+ return;
94
+ }
95
+ const error = (_a = event === null || event === void 0 ? void 0 : event.error) !== null && _a !== void 0 ? _a : (event instanceof Error ? event : new Error(`socket transport error`));
104
96
  this.emit('error', { error });
105
97
  if (this._state !== 'connected')
106
98
  this._state = 'closed';
107
99
  settleConnect(() => reject(error));
108
100
  }),
109
- bindSocketListener(socket, 'message', event => {
110
- this.emit('message', { data: (0, common_1.asString)(event === null || event === void 0 ? void 0 : event.data, '') });
111
- }),
112
101
  ];
102
+ if (unsubOpen)
103
+ this.networkUnsubs.push(unsubOpen);
113
104
  });
114
105
  return this.connecting;
115
106
  });
116
107
  this.disconnect = (code, reason) => __awaiter(this, void 0, void 0, function* () {
117
108
  var _b;
118
- if (!this.socket) {
109
+ if (!this.network) {
119
110
  this._state = 'closed';
120
111
  return;
121
112
  }
122
113
  this._state = 'closing';
123
- const socket = this.socket;
124
- socket.close(code, reason);
114
+ const network = this.network;
115
+ network.close(code, reason);
125
116
  (_b = this.cancelConnecting) === null || _b === void 0 ? void 0 : _b.call(this, new Error(`499 CLIENT CLOSED REQUEST - disconnect during connecting`));
126
- this.cleanupSocket();
117
+ this.cleanupNetwork();
127
118
  this._state = 'closed';
128
119
  this.emit('close', { code, reason, wasClean: true });
129
120
  });
130
121
  this.send = (raw) => {
131
- if (!this.socket || this.socket.readyState !== undefined && this.socket.readyState !== OPEN_STATE) {
122
+ if (!this.network || this.network.readyState !== 'open') {
132
123
  throw new Error(`503 SOCKET NOT CONNECTED - WebSocketTransport.send()`);
133
124
  }
134
- this.socket.send(raw);
125
+ this.network.send(raw);
135
126
  };
136
127
  this.emit = (type, event) => {
137
128
  var _a;
138
129
  (_a = this.listeners.get(type)) === null || _a === void 0 ? void 0 : _a.forEach(listener => listener(event));
139
130
  };
140
- this.cleanupSocket = () => {
141
- this.unbinders.splice(0).forEach(unbind => unbind());
142
- this.socket = undefined;
131
+ /** bridge chatic's `SocketLike` factory to the owned network's `WebSocketClosable` factory */
132
+ this.buildSocketFactory = () => {
133
+ const factory = this.socketFactory;
134
+ if (!factory)
135
+ return undefined;
136
+ return context => factory({ url: context.url, protocols: context.protocols });
137
+ };
138
+ this.cleanupNetwork = () => {
139
+ this.networkUnsubs.splice(0).forEach(unsub => unsub());
140
+ this.network = undefined;
143
141
  };
144
142
  this.connectTimeoutMs = (_a = options === null || options === void 0 ? void 0 : options.connectTimeoutMs) !== null && _a !== void 0 ? _a : DEFAULT_CONNECT_TIMEOUT_MS;
145
143
  }
@@ -2,20 +2,27 @@ import type { ResolveSocketPacketType, SocketMessage, SocketPacketInputType, Soc
2
2
  import type { DeviceBootstrapInput } from '../lib/device/types';
3
3
  export declare type ClientSocketState = 'idle' | 'connecting' | 'connected' | 'closing' | 'closed';
4
4
  export interface SocketLike {
5
- readonly readyState?: number;
5
+ readonly readyState: number;
6
6
  send(data: string): void;
7
7
  close(code?: number, reason?: string): void;
8
- addEventListener?(type: 'open' | 'close' | 'error' | 'message', listener: (event?: any) => void): void;
9
- removeEventListener?(type: 'open' | 'close' | 'error' | 'message', listener: (event?: any) => void): void;
10
- onopen?: ((event?: any) => void) | null;
11
- onclose?: ((event?: any) => void) | null;
12
- onerror?: ((event?: any) => void) | null;
13
- onmessage?: ((event?: any) => void) | null;
8
+ addEventListener(type: 'open' | 'close' | 'error' | 'message', listener: (event?: any) => void, options?: {
9
+ once?: boolean;
10
+ }): void;
11
+ removeEventListener(type: 'open' | 'close' | 'error' | 'message', listener: (event?: any) => void): void;
14
12
  }
15
13
  export interface SocketFactoryContext {
16
14
  url: string;
17
15
  protocols?: string | string[];
18
16
  }
17
+ /** context for the pre-parse raw ownership filter */
18
+ export interface ClientSocketInboundFilterContext {
19
+ raw: string;
20
+ }
21
+ /** context for the post-parse message ownership filter */
22
+ export interface ClientSocketMessageFilterContext<T = any> {
23
+ raw: string;
24
+ message: SocketMessage<T>;
25
+ }
19
26
  export interface SocketTransportEventMap {
20
27
  open: void;
21
28
  close: {
@@ -64,6 +71,8 @@ export interface ClientSocketOptions {
64
71
  protocols?: string | string[];
65
72
  device?: DeviceBootstrapInput | null;
66
73
  socketFactory?: (context: SocketFactoryContext) => SocketLike;
74
+ shouldHandleRaw?: (context: ClientSocketInboundFilterContext) => boolean;
75
+ shouldHandleMessage?: (context: ClientSocketMessageFilterContext) => boolean;
67
76
  aliases?: Partial<Record<SocketPacketInputType | string, SocketPacketType>>;
68
77
  timerScheduler?: SharedTimerScheduler;
69
78
  requestTimeoutMs?: number;