@simplysm/service-client 13.0.100 → 14.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.
Files changed (55) hide show
  1. package/dist/features/event-client.d.ts.map +1 -1
  2. package/dist/features/event-client.js +75 -67
  3. package/dist/features/event-client.js.map +1 -6
  4. package/dist/features/file-client.js +41 -39
  5. package/dist/features/file-client.js.map +1 -6
  6. package/dist/features/orm/orm-client-connector.js +37 -38
  7. package/dist/features/orm/orm-client-connector.js.map +1 -6
  8. package/dist/features/orm/orm-client-db-context-executor.d.ts.map +1 -1
  9. package/dist/features/orm/orm-client-db-context-executor.js +60 -60
  10. package/dist/features/orm/orm-client-db-context-executor.js.map +1 -6
  11. package/dist/features/orm/orm-connect-options.js +2 -1
  12. package/dist/features/orm/orm-connect-options.js.map +1 -6
  13. package/dist/index.js +6 -1
  14. package/dist/index.js.map +1 -6
  15. package/dist/protocol/client-protocol-wrapper.d.ts +1 -0
  16. package/dist/protocol/client-protocol-wrapper.d.ts.map +1 -1
  17. package/dist/protocol/client-protocol-wrapper.js +92 -70
  18. package/dist/protocol/client-protocol-wrapper.js.map +1 -6
  19. package/dist/service-client.d.ts +2 -0
  20. package/dist/service-client.d.ts.map +1 -1
  21. package/dist/service-client.js +113 -111
  22. package/dist/service-client.js.map +1 -6
  23. package/dist/transport/service-transport.js +121 -104
  24. package/dist/transport/service-transport.js.map +1 -6
  25. package/dist/transport/socket-provider.js +180 -155
  26. package/dist/transport/socket-provider.js.map +1 -6
  27. package/dist/types/connection-options.d.ts +1 -1
  28. package/dist/types/connection-options.d.ts.map +1 -1
  29. package/dist/types/connection-options.js +2 -1
  30. package/dist/types/connection-options.js.map +1 -6
  31. package/dist/types/progress.types.d.ts +1 -0
  32. package/dist/types/progress.types.d.ts.map +1 -1
  33. package/dist/types/progress.types.js +2 -1
  34. package/dist/types/progress.types.js.map +1 -6
  35. package/dist/workers/client-protocol.worker.js +38 -24
  36. package/dist/workers/client-protocol.worker.js.map +1 -6
  37. package/package.json +16 -9
  38. package/src/features/event-client.ts +19 -17
  39. package/src/features/file-client.ts +5 -5
  40. package/src/features/orm/orm-client-connector.ts +2 -2
  41. package/src/features/orm/orm-client-db-context-executor.ts +8 -7
  42. package/src/index.ts +5 -5
  43. package/src/protocol/client-protocol-wrapper.ts +24 -19
  44. package/src/service-client.ts +22 -15
  45. package/src/transport/service-transport.ts +19 -19
  46. package/src/transport/socket-provider.ts +38 -38
  47. package/src/types/connection-options.ts +1 -1
  48. package/src/types/progress.types.ts +1 -0
  49. package/src/workers/client-protocol.worker.ts +9 -9
  50. package/README.md +0 -126
  51. package/docs/features.md +0 -143
  52. package/docs/protocol.md +0 -29
  53. package/docs/service-client.md +0 -93
  54. package/docs/transport.md +0 -96
  55. package/docs/types.md +0 -55
@@ -9,27 +9,29 @@ import { createServiceTransport, type ServiceTransport } from "./transport/servi
9
9
  import { createSocketProvider, type SocketProvider } from "./transport/socket-provider";
10
10
  import { createEventClient, type EventClient } from "./features/event-client";
11
11
  import { createFileClient, type FileClient } from "./features/file-client";
12
- import { createClientProtocolWrapper } from "./protocol/client-protocol-wrapper";
12
+ import { createClientProtocolWrapper, type ClientProtocolWrapper } from "./protocol/client-protocol-wrapper";
13
13
 
14
14
  const logger = consola.withTag("service-client:ServiceClient");
15
15
 
16
16
  interface ServiceClientEvents {
17
17
  "request-progress": ServiceProgressState;
18
18
  "response-progress": ServiceProgressState;
19
+ "server-progress": ServiceProgressState;
19
20
  "state": "connected" | "closed" | "reconnecting";
20
21
  "reload": Set<string>;
21
22
  }
22
23
 
23
24
  export class ServiceClient extends EventEmitter<ServiceClientEvents> {
24
- // Modules
25
+ // 모듈
25
26
  private readonly _socket: SocketProvider;
26
27
  private readonly _transport: ServiceTransport;
27
28
  private readonly _eventClient: EventClient;
28
29
  private readonly _fileClient: FileClient;
29
30
 
31
+ private readonly _protocolWrapper: ClientProtocolWrapper;
30
32
  private _authToken?: string;
31
33
 
32
- // State accessors
34
+ // 상태 접근자
33
35
  get connected() {
34
36
  return this._socket.connected;
35
37
  }
@@ -47,27 +49,27 @@ export class ServiceClient extends EventEmitter<ServiceClientEvents> {
47
49
  const wsProtocol = options.ssl ? "wss" : "ws";
48
50
  const wsUrl = `${wsProtocol}://${options.host}:${options.port}/ws`;
49
51
 
50
- // Initialize modules
52
+ // 모듈 초기화
51
53
  this._socket = createSocketProvider(wsUrl, this.name, this.options.maxReconnectCount ?? 10);
52
54
  const protocol = createServiceProtocol();
53
- const protocolWrapper = createClientProtocolWrapper(protocol);
54
- this._transport = createServiceTransport(this._socket, protocolWrapper);
55
+ this._protocolWrapper = createClientProtocolWrapper(protocol);
56
+ this._transport = createServiceTransport(this._socket, this._protocolWrapper);
55
57
  this._eventClient = createEventClient(this._transport);
56
58
  this._fileClient = createFileClient(this.hostUrl, this.name);
57
59
 
58
- // Event bindings
60
+ // 이벤트 바인딩
59
61
  this._socket.on("state", async (state) => {
60
62
  this.emit("state", state);
61
63
 
62
- // Auto-recover event listeners on reconnect
64
+ // 재연결 이벤트 리스너 자동 복구
63
65
  if (state === "connected") {
64
66
  try {
65
67
  if (this._authToken != null) {
66
- await this.auth(this._authToken); // Re-authenticate
68
+ await this.auth(this._authToken); // 재인증
67
69
  }
68
70
  await this._eventClient.resubscribeAll();
69
71
  } catch (err) {
70
- logger.error("Failed to recover event listeners", err);
72
+ logger.error("이벤트 리스너 복구 실패", err);
71
73
  }
72
74
  }
73
75
  });
@@ -77,7 +79,7 @@ export class ServiceClient extends EventEmitter<ServiceClientEvents> {
77
79
  });
78
80
  }
79
81
 
80
- // Proxy creation method for type safety
82
+ // 타입 안전성을 위한 프록시 생성 메서드
81
83
  getService<TService>(serviceName: string): ServiceProxy<TService> {
82
84
  return new Proxy({} as ServiceProxy<TService>, {
83
85
  get: (_target, prop) => {
@@ -95,6 +97,7 @@ export class ServiceClient extends EventEmitter<ServiceClientEvents> {
95
97
 
96
98
  async close(): Promise<void> {
97
99
  await this._socket.close();
100
+ this._protocolWrapper.dispose();
98
101
  }
99
102
 
100
103
  async send(
@@ -117,6 +120,10 @@ export class ServiceClient extends EventEmitter<ServiceClientEvents> {
117
120
  this.emit("response-progress", state);
118
121
  progress?.response?.(state);
119
122
  },
123
+ server: (state) => {
124
+ this.emit("server-progress", state);
125
+ progress?.server?.(state);
126
+ },
120
127
  },
121
128
  );
122
129
  }
@@ -131,7 +138,7 @@ export class ServiceClient extends EventEmitter<ServiceClientEvents> {
131
138
  info: TInfo,
132
139
  cb: (data: TData) => PromiseLike<void>,
133
140
  ): Promise<string> {
134
- if (!this.connected) throw new Error("Not connected to the server.");
141
+ if (!this.connected) throw new Error("서버에 연결되지 않았습니다.");
135
142
  return this._eventClient.addListener(eventDef, info, cb);
136
143
  }
137
144
 
@@ -150,7 +157,7 @@ export class ServiceClient extends EventEmitter<ServiceClientEvents> {
150
157
  async uploadFile(files: File[] | FileList | { name: string; data: BlobPart }[]) {
151
158
  if (this._authToken == null) {
152
159
  throw new Error(
153
- "No authentication token found. Call auth() to authenticate before uploading files.",
160
+ "인증 토큰이 없습니다. 파일 업로드 전에 auth() 호출하여 인증해 주세요.",
154
161
  );
155
162
  }
156
163
  return this._fileClient.upload(files, this._authToken);
@@ -161,11 +168,11 @@ export class ServiceClient extends EventEmitter<ServiceClientEvents> {
161
168
  }
162
169
  }
163
170
 
164
- // Type transformer that wraps all method return types of TService with Promise
171
+ // TService의 모든 메서드 반환 타입을 Promise로 래핑하는 타입 변환기
165
172
  export type ServiceProxy<TService> = {
166
173
  [K in keyof TService]: TService[K] extends (...args: infer P) => infer R
167
174
  ? (...args: P) => Promise<Awaited<R>>
168
- : never; // Non-function properties are excluded
175
+ : never; // 함수가 아닌 속성은 제외
169
176
  };
170
177
 
171
178
  export function createServiceClient(name: string, options: ServiceConnectionOptions): ServiceClient {
@@ -41,34 +41,34 @@ export function createServiceTransport(
41
41
  }
42
42
  >();
43
43
 
44
- // Store response progress totalSize (for emitting 100% on complete)
44
+ // 응답 progress totalSize 저장 (완료 100% 전송용)
45
45
  const responseProgressTotalSize = new Map<string, number>();
46
46
 
47
47
  socket.on("message", onMessage);
48
48
 
49
- // When socket disconnects, reject all pending requests to free memory
49
+ // 소켓 연결 끊김 메모리 해제를 위해 모든 대기 중인 요청을 reject
50
50
  socket.on("state", (state) => {
51
51
  if (state === "closed" || state === "reconnecting") {
52
- cancelAllRequests("Socket connection lost");
52
+ cancelAllRequests("소켓 연결이 끊어졌습니다");
53
53
  }
54
54
  });
55
55
 
56
56
  async function send(message: ServiceClientMessage, progress?: ServiceProgress): Promise<unknown> {
57
57
  const uuid = Uuid.generate().toString();
58
58
 
59
- // Start awaiting response (register listener before sending request for safety)
59
+ // 응답 대기 시작 (안전을 위해 요청 전송 전에 리스너 등록)
60
60
  const responsePromise = new Promise((resolve, reject) => {
61
61
  pendingRequests.set(uuid, { resolve, reject, progress });
62
62
  });
63
63
 
64
- // Prevent unhandled rejection when the promise is orphaned (e.g., during HMR cleanup)
64
+ // Promise가 고아가 되었을 unhandled rejection 방지 (예: HMR 정리 중)
65
65
  responsePromise.catch(() => {});
66
66
 
67
- // Send request
67
+ // 요청 전송
68
68
  try {
69
69
  const { chunks, totalSize } = await protocol.encode(uuid, message);
70
70
 
71
- // Initialize progress
71
+ // progress 초기화
72
72
  if (chunks.length > 1) {
73
73
  progress?.request?.({
74
74
  uuid,
@@ -77,18 +77,18 @@ export function createServiceTransport(
77
77
  });
78
78
  }
79
79
 
80
- // Send
80
+ // 전송
81
81
  for (const chunk of chunks) {
82
82
  await socket.send(chunk);
83
83
  }
84
84
  } catch (err) {
85
- // Clean up immediately on send failure
85
+ // 전송 실패 즉시 정리
86
86
  pendingRequests.get(uuid)?.reject(err as Error);
87
87
  pendingRequests.delete(uuid);
88
88
  throw err;
89
89
  }
90
90
 
91
- // Return response result
91
+ // 응답 결과 반환
92
92
  return responsePromise;
93
93
  }
94
94
 
@@ -99,7 +99,7 @@ export function createServiceTransport(
99
99
 
100
100
  try {
101
101
  if (decoded.type === "progress") {
102
- // Remember totalSize (for emitting 100% on complete)
102
+ // totalSize 기억 (완료 100% 전송용)
103
103
  responseProgressTotalSize.set(decoded.uuid, decoded.totalSize);
104
104
 
105
105
  listenerInfo?.progress?.response?.({
@@ -110,13 +110,13 @@ export function createServiceTransport(
110
110
  } else {
111
111
  if (decoded.message.name === "progress") {
112
112
  const body = decoded.message.body as { totalSize: number; completedSize: number };
113
- listenerInfo?.progress?.request?.({
113
+ listenerInfo?.progress?.server?.({
114
114
  uuid: decoded.uuid,
115
115
  totalSize: body.totalSize,
116
116
  completedSize: body.completedSize,
117
117
  });
118
118
  } else if (decoded.message.name === "response") {
119
- // Emit 100% progress if it was a split message
119
+ // 분할 메시지였을 경우 100% progress 전송
120
120
  const totalSize = responseProgressTotalSize.get(decoded.uuid);
121
121
  if (totalSize != null) {
122
122
  responseProgressTotalSize.delete(decoded.uuid);
@@ -127,15 +127,15 @@ export function createServiceTransport(
127
127
  });
128
128
  }
129
129
 
130
- // Remove from Map since response received
130
+ // 응답 수신으로 Map에서 제거
131
131
  pendingRequests.delete(decoded.uuid);
132
132
 
133
133
  listenerInfo?.resolve(decoded.message.body as ServiceResponseMessage);
134
134
  } else if (decoded.message.name === "error") {
135
- // Clean up progress totalSize
135
+ // progress totalSize 정리
136
136
  responseProgressTotalSize.delete(decoded.uuid);
137
137
 
138
- // Remove from Map since error received
138
+ // 에러 수신으로 Map에서 제거
139
139
  pendingRequests.delete(decoded.uuid);
140
140
 
141
141
  listenerInfo?.reject(toError(decoded.message.body));
@@ -148,7 +148,7 @@ export function createServiceTransport(
148
148
  const body = decoded.message.body as { keys: string[]; data: unknown };
149
149
  emitter.emit("event", { keys: body.keys, data: body.data });
150
150
  } else {
151
- throw new Error("Invalid message received from server.");
151
+ throw new Error("서버로부터 잘못된 메시지를 수신했습니다.");
152
152
  }
153
153
  }
154
154
  } catch (err) {
@@ -156,10 +156,10 @@ export function createServiceTransport(
156
156
  }
157
157
  }
158
158
 
159
- // Cancel all pending requests
159
+ // 모든 대기 중인 요청 취소
160
160
  function cancelAllRequests(reason: string): void {
161
161
  for (const listenerInfo of pendingRequests.values()) {
162
- listenerInfo.reject(new Error(`Request canceled: ${reason}`));
162
+ listenerInfo.reject(new Error(`요청 취소됨: ${reason}`));
163
163
  }
164
164
  pendingRequests.clear();
165
165
  responseProgressTotalSize.clear();
@@ -30,15 +30,15 @@ export function createSocketProvider(
30
30
  clientName: string,
31
31
  maxReconnectCount: number,
32
32
  ): SocketProvider {
33
- // Configuration constants
34
- const HEARTBEAT_TIMEOUT = 30000; // Consider disconnected if no message for 30s
35
- const HEARTBEAT_INTERVAL = 5000; // Send ping every 5s
36
- const RECONNECT_DELAY = 3000; // Retry reconnect every 3s
33
+ // 설정 상수
34
+ const HEARTBEAT_TIMEOUT = 30000; // 30초 동안 메시지가 없으면 연결 끊김으로 간주
35
+ const HEARTBEAT_INTERVAL = 5000; // 5초마다 ping 전송
36
+ const RECONNECT_DELAY = 3000; // 3초마다 재연결 시도
37
37
 
38
- // Pre-allocate 1-byte buffer (saves memory)
38
+ // 1바이트 버퍼 사전 할당 (메모리 절약)
39
39
  const PING_PACKET = new Uint8Array([0x01]);
40
40
 
41
- // State
41
+ // 상태
42
42
  let ws: WebSocket | undefined;
43
43
  let isManualClose = false;
44
44
  let reconnectCount = 0;
@@ -58,10 +58,10 @@ export function createSocketProvider(
58
58
  try {
59
59
  await createSocket();
60
60
  startHeartbeat();
61
- reconnectCount = 0; // Reset count on successful connection
61
+ reconnectCount = 0; // 연결 성공 카운트 초기화
62
62
  emitter.emit("state", "connected");
63
63
  } catch (err) {
64
- // Throw on initial connection failure (so the caller can handle it)
64
+ // 초기 연결 실패 예외를 던짐 (호출자가 처리할 있도록)
65
65
  throw err;
66
66
  }
67
67
  }
@@ -72,7 +72,7 @@ export function createSocketProvider(
72
72
  const currentWs = ws;
73
73
  if (currentWs != null) {
74
74
  currentWs.close();
75
- // Wait until fully closed (graceful shutdown)
75
+ // 완전히 닫힐 때까지 대기 (정상 종료)
76
76
  await wait.until(() => currentWs.readyState === WebSocket.CLOSED, 100, 30).catch(() => {});
77
77
  }
78
78
  emitter.emit("state", "closed");
@@ -82,11 +82,11 @@ export function createSocketProvider(
82
82
  try {
83
83
  await wait.until(() => isConnected(), undefined, 50);
84
84
  } catch {
85
- throw new Error("Not connected to the server. Please check your internet connection.");
85
+ throw new Error("서버에 연결되지 않았습니다. 인터넷 연결을 확인해 주세요.");
86
86
  }
87
87
  const currentWs = ws;
88
88
  if (currentWs == null) {
89
- throw new Error("WebSocket is not connected.");
89
+ throw new Error("WebSocket 연결되지 않았습니다.");
90
90
  }
91
91
  currentWs.send(data);
92
92
  }
@@ -109,7 +109,7 @@ export function createSocketProvider(
109
109
  };
110
110
 
111
111
  newWs.onerror = (event: Event) => {
112
- // Reject on error during connection
112
+ // 연결 에러 발생 시 reject
113
113
  if (!isConnected()) {
114
114
  const errorEvent = event as ErrorEvent;
115
115
  const msg = errorEvent.message;
@@ -118,21 +118,21 @@ export function createSocketProvider(
118
118
  };
119
119
  });
120
120
 
121
- // At this point ws is always assigned (assigned in ws.onopen)
121
+ // 시점에서 ws 항상 할당됨 (ws.onopen에서 할당됨)
122
122
  const currentWs = ws;
123
123
  if (currentWs == null) {
124
- throw new Error("WebSocket initialization failed");
124
+ throw new Error("WebSocket 초기화 실패");
125
125
  }
126
126
 
127
127
  currentWs.onmessage = (event) => {
128
- lastHeartbeatTime = Date.now(); // Update heartbeat
128
+ lastHeartbeatTime = Date.now(); // 하트비트 갱신
129
129
 
130
130
  const data = event.data as ArrayBuffer;
131
131
  const bytes = new Uint8Array(data);
132
132
 
133
- // Raw Ping/Pong handling (checked first)
134
- // If 1 byte and first byte is 0x02 (Pong), ignore
135
- // (only heartbeat timestamp was updated, nothing else to do)
133
+ // Raw Ping/Pong 처리 (먼저 확인)
134
+ // 1바이트이고 번째 바이트가 0x02 (Pong)이면 무시
135
+ // (하트비트 타임스탬프만 갱신되었으므로 추가 작업 불필요)
136
136
  if (bytes.length === 1 && bytes[0] === 0x02) return;
137
137
 
138
138
  emitter.emit("message", bytes);
@@ -147,11 +147,11 @@ export function createSocketProvider(
147
147
  }
148
148
 
149
149
  async function tryReconnect(): Promise<void> {
150
- // Loop-based reconnect (used instead of recursion for stack safety)
150
+ // 루프 기반 재연결 (스택 안전성을 위해 재귀 대신 사용)
151
151
  while (reconnectCount < maxReconnectCount) {
152
152
  reconnectCount++;
153
153
  emitter.emit("state", "reconnecting");
154
- logger.warn("WebSocket disconnected. Attempting reconnect...", {
154
+ logger.warn("WebSocket 연결 끊김. 재연결 시도 중...", {
155
155
  reconnectCount,
156
156
  maxReconnectCount,
157
157
  });
@@ -162,16 +162,16 @@ export function createSocketProvider(
162
162
  await createSocket();
163
163
  startHeartbeat();
164
164
  reconnectCount = 0;
165
- emitter.emit("state", "connected"); // Notify reconnect success
166
- logger.info("WebSocket reconnected successfully");
167
- return; // Exit on successful reconnect
165
+ emitter.emit("state", "connected"); // 재연결 성공 알림
166
+ logger.info("WebSocket 재연결 성공");
167
+ return; // 재연결 성공 루프 종료
168
168
  } catch {
169
- // Continue loop on failure
169
+ // 실패 루프 계속
170
170
  }
171
171
  }
172
172
 
173
- // Max retry count exceeded
174
- logger.error("Reconnect retry limit exceeded. Giving up.");
173
+ // 최대 재시도 횟수 초과
174
+ logger.error("재연결 재시도 한도 초과. 연결을 포기합니다.");
175
175
  emitter.emit("state", "closed");
176
176
  }
177
177
 
@@ -180,32 +180,32 @@ export function createSocketProvider(
180
180
  lastHeartbeatTime = Date.now();
181
181
 
182
182
  heartbeatTimer = setInterval(() => {
183
- // Timeout check
183
+ // 타임아웃 확인
184
184
  if (Date.now() - lastHeartbeatTime > HEARTBEAT_TIMEOUT) {
185
- logger.warn("Heartbeat Timeout. Connection lost.");
185
+ logger.warn("하트비트 타임아웃. 연결이 끊어졌습니다.");
186
186
 
187
- // Stop the timer immediately on timeout to prevent repeated execution.
187
+ // 반복 실행 방지를 위해 타임아웃 즉시 타이머 중지
188
188
  stopHeartbeat();
189
189
 
190
- // Don't wait for socket close (onclose may not fire); force cleanup and reconnect.
190
+ // 소켓 종료를 기다리지 않음 (onclose 발생하지 않을 수 있음); 강제 정리 재연결
191
191
  if (ws != null) {
192
192
  const tempWs = ws;
193
- ws = undefined; // Consider connection as disconnected
193
+ ws = undefined; // 연결 끊김으로 간주
194
194
 
195
- // Remove event handlers from old socket
196
- // Prevent duplicate reconnect from late onclose events
195
+ // 이전 소켓에서 이벤트 핸들러 제거
196
+ // 늦게 발생하는 onclose 이벤트로 인한 중복 재연결 방지
197
197
  tempWs.onclose = null;
198
198
  tempWs.onerror = null;
199
199
  tempWs.onmessage = null;
200
200
 
201
- // Attempt to close socket (ignore errors)
201
+ // 소켓 닫기 시도 (에러 무시)
202
202
  try {
203
203
  tempWs.close();
204
204
  } catch {
205
- // ignore
205
+ // 무시
206
206
  }
207
207
 
208
- // Force reconnect logic if not a manual close
208
+ // 수동 종료가 아닌 경우 강제 재연결 로직 실행
209
209
  if (!isManualClose) {
210
210
  void tryReconnect();
211
211
  }
@@ -213,13 +213,13 @@ export function createSocketProvider(
213
213
  return;
214
214
  }
215
215
 
216
- // Send ping
216
+ // ping 전송
217
217
  const currentWs = ws;
218
218
  if (isConnected() && currentWs != null) {
219
219
  try {
220
220
  currentWs.send(PING_PACKET);
221
221
  } catch (err) {
222
- logger.warn("Ping send failed", err);
222
+ logger.warn("ping 전송 실패", err);
223
223
  }
224
224
  }
225
225
  }, HEARTBEAT_INTERVAL);
@@ -2,6 +2,6 @@ export interface ServiceConnectionOptions {
2
2
  port: number;
3
3
  host: string;
4
4
  ssl?: boolean;
5
- /** Set to 0 to disable reconnect; disconnects immediately */
5
+ /** 0으로 설정하면 재연결을 비활성화하고 즉시 연결을 끊음 */
6
6
  maxReconnectCount?: number;
7
7
  }
@@ -1,6 +1,7 @@
1
1
  export interface ServiceProgress {
2
2
  request?: (s: ServiceProgressState) => void;
3
3
  response?: (s: ServiceProgressState) => void;
4
+ server?: (s: ServiceProgressState) => void;
4
5
  }
5
6
 
6
7
  export interface ServiceProgressState {
@@ -17,34 +17,34 @@ self.onmessage = (event: MessageEvent) => {
17
17
  let transferList: Transferable[] = [];
18
18
 
19
19
  if (type === "encode") {
20
- // [Main -> Worker] Encode request (data: { uuid, message })
21
- // message is already a Plain Object (via Structured Clone)
20
+ // [Main -> Worker] 인코딩 요청 (data: { uuid, message })
21
+ // message 이미 Plain Object (Structured Clone을 통해 전달)
22
22
  const { uuid, message } = data as {
23
23
  uuid: string;
24
24
  message: Parameters<typeof protocol.encode>[1];
25
25
  };
26
26
  const { chunks } = protocol.encode(uuid, message);
27
27
 
28
- // Buffer[] is transferable, return as result
28
+ // Buffer[] 전송 가능하므로 결과로 반환
29
29
  result = chunks;
30
- // Add internal ArrayBuffers of result chunks to transfer list
30
+ // 결과 chunk의 내부 ArrayBuffer를 전송 목록에 추가
31
31
  transferList = chunks.map((chunk) => chunk.buffer as ArrayBuffer);
32
32
  } else {
33
- // [Main -> Worker] Decode request (data: Uint8Array)
34
- // data arrives as Uint8Array
33
+ // [Main -> Worker] 디코딩 요청 (data: Uint8Array)
34
+ // data Uint8Array로 전달됨
35
35
  const bytes = new Uint8Array(data as ArrayBuffer);
36
36
  const decodeResult = protocol.decode(bytes);
37
37
 
38
- // Convert result object to transferable form (prepare for zero-copy)
38
+ // 결과 객체를 전송 가능한 형태로 변환 (zero-copy 준비)
39
39
  const encoded = transfer.encode(decodeResult);
40
40
  result = encoded.result;
41
41
  transferList = encoded.transferList;
42
42
  }
43
43
 
44
- // [Worker -> Main] Success response
44
+ // [Worker -> Main] 성공 응답
45
45
  self.postMessage({ id, type: "success", result }, { transfer: transferList });
46
46
  } catch (err) {
47
- // [Worker -> Main] Error response
47
+ // [Worker -> Main] 에러 응답
48
48
  self.postMessage({
49
49
  id,
50
50
  type: "error",
package/README.md DELETED
@@ -1,126 +0,0 @@
1
- # @simplysm/service-client
2
-
3
- Simplysm package - Service module (client)
4
-
5
- WebSocket-based service client for communicating with `@simplysm/service-server`. Provides type-safe service method calls, event pub/sub, file upload/download, and client-side ORM connectivity.
6
-
7
- ## Installation
8
-
9
- ```bash
10
- npm install @simplysm/service-client
11
- ```
12
-
13
- ## API Overview
14
-
15
- ### Types
16
- | API | Type | Description |
17
- |-----|------|-------------|
18
- | `ServiceConnectionOptions` | interface | Connection options (host, port, ssl, reconnect) |
19
- | `ServiceProgress` | interface | Progress callback config for requests |
20
- | `ServiceProgressState` | interface | Progress state (uuid, totalSize, completedSize) |
21
-
22
- -> See [docs/types.md](./docs/types.md) for details.
23
-
24
- ### Transport
25
- | API | Type | Description |
26
- |-----|------|-------------|
27
- | `SocketProvider` | interface | WebSocket connection provider |
28
- | `SocketProviderEvents` | interface | Socket events (message, state) |
29
- | `createSocketProvider` | function | Create socket with heartbeat and auto-reconnect |
30
- | `ServiceTransport` | interface | Message routing and request/response correlation |
31
- | `ServiceTransportEvents` | interface | Transport events (reload, event) |
32
- | `createServiceTransport` | function | Create transport instance |
33
-
34
- -> See [docs/transport.md](./docs/transport.md) for details.
35
-
36
- ### Protocol
37
- | API | Type | Description |
38
- |-----|------|-------------|
39
- | `ClientProtocolWrapper` | interface | Protocol wrapper with Web Worker offloading |
40
- | `createClientProtocolWrapper` | function | Create protocol wrapper |
41
-
42
- -> See [docs/protocol.md](./docs/protocol.md) for details.
43
-
44
- ### Features (Event, File, ORM)
45
- | API | Type | Description |
46
- |-----|------|-------------|
47
- | `EventClient` | interface | Event listener management |
48
- | `createEventClient` | function | Create event client |
49
- | `FileClient` | interface | File upload/download |
50
- | `createFileClient` | function | Create file client |
51
- | `OrmConnectOptions` | interface | ORM connection options |
52
- | `OrmClientConnector` | interface | Client-side ORM connector |
53
- | `createOrmClientConnector` | function | Create ORM connector |
54
- | `OrmClientDbContextExecutor` | class | DbContext executor via service protocol |
55
-
56
- -> See [docs/features.md](./docs/features.md) for details.
57
-
58
- ### Main
59
- | API | Type | Description |
60
- |-----|------|-------------|
61
- | `ServiceClient` | class | Main client (connect, auth, service calls, events, files) |
62
- | `ServiceClientEvents` | interface | Client events (progress, state, reload) |
63
- | `ServiceProxy` | type | Type wrapper for remote service methods |
64
- | `createServiceClient` | function | Factory to create ServiceClient |
65
-
66
- -> See [docs/service-client.md](./docs/service-client.md) for details.
67
-
68
- ## Usage Examples
69
-
70
- ### Connect and Call Service Methods
71
-
72
- ```typescript
73
- import { createServiceClient } from "@simplysm/service-client";
74
-
75
- const client = createServiceClient("my-app", {
76
- host: "localhost",
77
- port: 3000,
78
- });
79
-
80
- await client.connect();
81
- await client.auth(jwtToken);
82
-
83
- // Type-safe service proxy
84
- interface UserService {
85
- getProfile(): Promise<{ name: string; email: string }>;
86
- updateName(name: string): Promise<void>;
87
- }
88
-
89
- const userService = client.getService<UserService>("User");
90
- const profile = await userService.getProfile();
91
- await userService.updateName("New Name");
92
- ```
93
-
94
- ### Client-Side ORM
95
-
96
- ```typescript
97
- import { createOrmClientConnector } from "@simplysm/service-client";
98
-
99
- const orm = createOrmClientConnector(client);
100
-
101
- const users = await orm.connect(
102
- { dbContextDef: MyDb, connOpt: { configName: "main" } },
103
- async (db) => {
104
- return db.user().where((u) => [expr.eq(u.status, "active")]).execute();
105
- },
106
- );
107
- ```
108
-
109
- ### Event Subscription
110
-
111
- ```typescript
112
- import { defineEvent } from "@simplysm/service-common";
113
-
114
- const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
115
-
116
- const key = await client.addListener(
117
- OrderUpdated,
118
- { orderId: 123 },
119
- async (data) => {
120
- console.log("Order status:", data.status);
121
- },
122
- );
123
-
124
- // Later: unsubscribe
125
- await client.removeListener(key);
126
- ```