@simplysm/service-client 13.0.69 → 13.0.70

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/README.md CHANGED
@@ -1,640 +1,48 @@
1
1
  # @simplysm/service-client
2
2
 
3
- A service client package for the Simplysm framework. Provides WebSocket communication with `@simplysm/service-server`, remote service invocation (RPC), event subscription, file upload/download, and ORM remote access.
4
-
5
- Works in both browser and Node.js environments, with built-in features like automatic message chunking/merging for large payloads, heartbeat-based connection monitoring, and automatic reconnection.
3
+ Simplysm package - Service module (client)
6
4
 
7
5
  ## Installation
8
6
 
9
- ```bash
10
- npm install @simplysm/service-client
11
- # or
12
7
  pnpm add @simplysm/service-client
13
- ```
14
-
15
- ## Main Modules
16
-
17
- ### Core Functions and Classes
18
-
19
- | Function/Class | Description |
20
- |--------|------|
21
- | `createServiceClient` | Factory function for creating a ServiceClient instance. **Recommended over using the class constructor directly.** |
22
- | `ServiceClient` | Main service client class. Provides integrated connection management, RPC calls, events, files, and authentication. |
23
- | `createServiceTransport` | Factory function for creating a ServiceTransport instance |
24
- | `ServiceTransport` | Message transport layer. Handles request/response matching, progress tracking, and protocol encoding/decoding. |
25
- | `createSocketProvider` | Factory function for creating a SocketProvider instance |
26
- | `SocketProvider` | WebSocket connection management. Handles heartbeat, auto-reconnection, and connection state events. |
27
- | `createClientProtocolWrapper` | Factory function for creating a ClientProtocolWrapper instance |
28
- | `ClientProtocolWrapper` | Protocol wrapper. Automatically selects main thread/Web Worker for encoding/decoding based on data size. |
29
-
30
- ### Feature Functions and Classes
31
-
32
- | Function/Class | Description |
33
- |--------|------|
34
- | `createEventClient` | Factory function for creating an EventClient instance |
35
- | `EventClient` | Server event subscription/publishing. Supports automatic listener recovery on reconnection. |
36
- | `createFileClient` | Factory function for creating a FileClient instance |
37
- | `FileClient` | Handles HTTP-based file upload/download. |
38
- | `createOrmClientConnector` | Factory function for creating an OrmClientConnector instance |
39
- | `OrmClientConnector` | ORM remote connection connector. Supports transaction/non-transaction connections. |
40
- | `OrmClientDbContextExecutor` | ORM DbContext remote executor. Calls server's `OrmService` via RPC. |
41
-
42
- ### Types/Interfaces
43
-
44
- | Type | Description |
45
- |------|------|
46
- | `ServiceConnectionConfig` | Server connection config (host, port, ssl, maxReconnectCount) |
47
- | `ServiceProgress` | Request/response progress callback |
48
- | `ServiceProgressState` | Progress state (uuid, totalSize, completedSize) |
49
- | `SocketProviderEvents` | Event map for SocketProvider (message, state) |
50
- | `ServiceTransportEvents` | Event map for ServiceTransport (reload, event) |
51
- | `OrmConnectConfig<T>` | ORM connection config (DbContext type, connection options, DB/schema override) |
52
- | `RemoteService<T>` | Utility type that wraps all method return types of a service interface with `Promise` |
53
-
54
- ## Usage
55
-
56
- ### Basic Connection and Service Call
57
-
58
- ```typescript
59
- import { createServiceClient } from "@simplysm/service-client";
60
-
61
- // Create client (recommended: use factory function)
62
- const client = createServiceClient("my-app", {
63
- host: "localhost",
64
- port: 8080,
65
- ssl: false,
66
- maxReconnectCount: 10, // Max reconnection attempts (default: 10, 0 means no reconnection)
67
- });
68
-
69
- // Connect to server
70
- await client.connect();
71
-
72
- // Check connection status
73
- console.log(client.connected); // true
74
- console.log(client.hostUrl); // "http://localhost:8080"
75
- console.log(client.name); // "my-app"
76
-
77
- // Direct RPC call
78
- const result = await client.send("MyService", "getUsers", [{ page: 1 }]);
79
-
80
- // Close connection
81
- await client.close();
82
- ```
83
-
84
- ### Type-Safe Service Call (getService)
85
-
86
- `getService<T>()` uses `Proxy` to provide type-safe remote calls to service interfaces.
87
-
88
- ```typescript
89
- // Server-side service definition
90
- import { defineService } from "@simplysm/service-server";
91
-
92
- export const UserService = defineService("UserService", (ctx) => ({
93
- getUsers: async (filter: { page: number }): Promise<User[]> => {
94
- // ...
95
- },
96
- createUser: async (data: CreateUserDto): Promise<number> => {
97
- // ...
98
- },
99
- deleteUser: async (id: number): Promise<void> => {
100
- // ...
101
- },
102
- }));
103
-
104
- // Export type for client-side usage
105
- export type UserServiceMethods = import("@simplysm/service-server").ServiceMethods<typeof UserService>;
106
-
107
- // Client-side usage
108
- import type { UserServiceMethods } from "./server/services/user-service";
109
-
110
- const userService = client.getService<UserServiceMethods>("UserService");
111
-
112
- // Parameter/return types are automatically inferred on method calls
113
- const users = await userService.getUsers({ page: 1 }); // users: User[]
114
- const newId = await userService.createUser({ name: "test" }); // newId: number
115
- ```
116
-
117
- `ServiceMethods<T>` extracts method types from a service definition. `RemoteService<T>` wraps all method return types with `Promise` (methods already returning `Promise` are not double-wrapped).
118
-
119
- ### Authentication
120
-
121
- ```typescript
122
- // Send auth token after server connection
123
- await client.connect();
124
- await client.auth("jwt-token-here");
125
-
126
- // Automatically re-authenticated on reconnection
127
- ```
128
-
129
- Tokens stored after `auth()` calls are automatically resent to the server on WebSocket reconnection.
130
-
131
- ### Connection State Monitoring
132
-
133
- `ServiceClient` extends `EventEmitter` and supports the following events.
134
-
135
- | Event | Type | Description |
136
- |--------|------|------|
137
- | `state` | `"connected" \| "closed" \| "reconnecting"` | Connection state change |
138
- | `request-progress` | `ServiceProgressState` | Request transmission progress |
139
- | `response-progress` | `ServiceProgressState` | Response reception progress |
140
- | `reload` | `Set<string>` | File change notification from server (dev mode HMR) |
141
-
142
- ```typescript
143
- // Monitor connection state changes
144
- client.on("state", (state) => {
145
- if (state === "connected") {
146
- console.log("Connected to server");
147
- } else if (state === "reconnecting") {
148
- console.log("Reconnection in progress...");
149
- } else if (state === "closed") {
150
- console.log("Connection closed");
151
- }
152
- });
153
-
154
- // Monitor request/response progress (large messages)
155
- client.on("request-progress", (state) => {
156
- const percent = Math.round((state.completedSize / state.totalSize) * 100);
157
- console.log(`Sending: ${percent}%`);
158
- });
159
-
160
- client.on("response-progress", (state) => {
161
- const percent = Math.round((state.completedSize / state.totalSize) * 100);
162
- console.log(`Receiving: ${percent}%`);
163
- });
164
- ```
165
-
166
- ### Individual Request Progress Tracking
167
-
168
- Track progress of individual requests with the `progress` parameter of the `send()` method.
169
-
170
- ```typescript
171
- const result = await client.send("DataService", "getLargeData", [query], {
172
- request: (state) => {
173
- console.log(`Request sending: ${state.completedSize}/${state.totalSize} bytes`);
174
- },
175
- response: (state) => {
176
- console.log(`Response receiving: ${state.completedSize}/${state.totalSize} bytes`);
177
- },
178
- });
179
- ```
180
-
181
- ### Event Subscription (Server -> Client)
182
-
183
- Subscribe to events from the server, and listeners are automatically recovered on reconnection.
184
-
185
- ```typescript
186
- import { defineEvent } from "@simplysm/service-common";
187
-
188
- // Event definition (shared between server/client)
189
- export const SharedDataChangeEvent = defineEvent<
190
- { name: string; filter: unknown },
191
- (string | number)[] | undefined
192
- >("SharedDataChangeEvent");
193
-
194
- // Subscribe to event
195
- const listenerKey = await client.addEventListener(
196
- SharedDataChangeEvent,
197
- { name: "users", filter: null },
198
- async (data) => {
199
- console.log("Data changed:", data);
200
- },
201
- );
202
-
203
- // Unsubscribe from event
204
- await client.removeEventListener(listenerKey);
205
- ```
206
-
207
- ### Event Publishing (Client -> Server -> Other Clients)
208
-
209
- ```typescript
210
- import { defineEvent } from "@simplysm/service-common";
211
-
212
- export const SharedDataChangeEvent = defineEvent<
213
- { name: string; filter: unknown },
214
- (string | number)[] | undefined
215
- >("SharedDataChangeEvent");
216
-
217
- // Publish event to listeners matching specific conditions
218
- await client.emitToServer(
219
- SharedDataChangeEvent,
220
- (info) => info.name === "users", // Target filter
221
- [1, 2, 3], // Data to send
222
- );
223
- ```
224
-
225
- The server finds listeners matching the `infoSelector` condition in the registered listener list and delivers the event.
226
-
227
- ### File Upload
228
-
229
- File upload is handled via HTTP POST requests and requires an authentication token.
230
-
231
- ```typescript
232
- // Authentication required
233
- await client.auth("jwt-token");
234
-
235
- // Upload with browser File object
236
- const fileInput = document.querySelector("input[type=file]") as HTMLInputElement;
237
- const results = await client.uploadFile(fileInput.files!);
238
-
239
- // Upload with custom data
240
- const results = await client.uploadFile([
241
- { name: "data.json", data: JSON.stringify({ key: "value" }) },
242
- { name: "image.png", data: imageBlob },
243
- ]);
244
-
245
- // Upload results
246
- for (const result of results) {
247
- console.log(result.path); // Server storage path
248
- console.log(result.filename); // Original filename
249
- console.log(result.size); // File size (bytes)
250
- }
251
- ```
252
-
253
- ### File Download
254
-
255
- ```typescript
256
- // Download file from server's relative path
257
- const buffer = await client.downloadFileBuffer("/uploads/2024/file.pdf");
258
- // buffer: Uint8Array
259
- ```
260
-
261
- ### ORM Remote Access
262
-
263
- Access the database through the server's ORM service. Transactions are automatically managed.
264
-
265
- ```typescript
266
- import { createOrmClientConnector } from "@simplysm/service-client";
267
- import type { OrmConnectConfig } from "@simplysm/service-client";
268
- import { DbContext } from "@simplysm/orm-common";
269
-
270
- const connector = createOrmClientConnector(client);
271
-
272
- // Connect with transaction (auto rollback on error)
273
- await connector.connect(
274
- {
275
- dbContextDef: MyDbContext,
276
- connOpt: { configName: "default" },
277
- dbContextOpt: { database: "mydb", schema: "dbo" }, // Optional
278
- },
279
- async (db) => {
280
- const users = await db.user().result();
281
- await db.user().insert([{ name: "test" }]);
282
- // Auto commit on callback success
283
- },
284
- );
285
-
286
- // Connect without transaction (suitable for read-only operations)
287
- await connector.connectWithoutTransaction(
288
- {
289
- dbContextDef: MyDbContext,
290
- connOpt: { configName: "default" },
291
- },
292
- async (db) => {
293
- const users = await db.user().result();
294
- return users;
295
- },
296
- );
297
- ```
298
-
299
- ## Detailed API
300
-
301
- ### ServiceConnectionConfig
302
-
303
- Server connection configuration interface.
304
-
305
- ```typescript
306
- import type { ServiceConnectionConfig } from "@simplysm/service-client";
307
- ```
308
-
309
- | Property | Type | Required | Description |
310
- |------|------|------|------|
311
- | `host` | `string` | Yes | Server host address |
312
- | `port` | `number` | Yes | Server port number |
313
- | `ssl` | `boolean` | No | SSL usage. If `true`, uses `wss://` / `https://` |
314
- | `maxReconnectCount` | `number` | No | Max reconnection attempts (default: 10). 0 means no reconnection |
315
-
316
- ### createServiceClient
317
-
318
- Factory function for creating a ServiceClient instance.
319
-
320
- ```typescript
321
- import { createServiceClient } from "@simplysm/service-client";
322
-
323
- function createServiceClient(name: string, options: ServiceConnectionConfig): ServiceClient
324
- ```
325
-
326
- **Parameters:**
327
- - `name` - Client identifier (used for server-side logging and connection management)
328
- - `options` - Server connection configuration
329
-
330
- **Returns:** ServiceClient instance
331
-
332
- **Example:**
333
- ```typescript
334
- const client = createServiceClient("my-app", {
335
- host: "localhost",
336
- port: 8080,
337
- ssl: false,
338
- });
339
- ```
340
-
341
- ### ServiceClient
342
-
343
- ```typescript
344
- import { ServiceClient } from "@simplysm/service-client";
345
- ```
346
-
347
- Extends `EventEmitter<ServiceClientEvents>`.
348
-
349
- | Method/Property | Type / Return Type | Description |
350
- |-------------|----------|------|
351
- | `constructor(name, options)` | - | Create client instance. `name` is the client identifier. **Note: Prefer using `createServiceClient()` factory function.** |
352
- | `name` | `string` | Client identifier (read-only) |
353
- | `options` | `ServiceConnectionConfig` | Connection configuration (read-only) |
354
- | `connected` | `boolean` | WebSocket connection status |
355
- | `hostUrl` | `string` | HTTP URL (e.g., `http://localhost:8080`) |
356
- | `connect()` | `Promise<void>` | Connect to server via WebSocket |
357
- | `close()` | `Promise<void>` | Close connection (Graceful Shutdown) |
358
- | `send(serviceName, methodName, params, progress?)` | `Promise<unknown>` | Remote call to service method |
359
- | `getService<TService>(serviceName)` | `RemoteService<TService>` | Create type-safe service proxy |
360
- | `auth(token)` | `Promise<void>` | Send auth token (auto re-auth on reconnection) |
361
- | `addEventListener(eventDef, info, cb)` | `Promise<string>` | Register event listener. Returns listener key |
362
- | `removeEventListener(key)` | `Promise<void>` | Remove event listener |
363
- | `emitToServer(eventDef, infoSelector, data)` | `Promise<void>` | Publish event to other clients through server |
364
- | `uploadFile(files)` | `Promise<ServiceUploadResult[]>` | File upload (auth required) |
365
- | `downloadFileBuffer(relPath)` | `Promise<Uint8Array>` | File download |
366
-
367
- ### ServiceProgress / ServiceProgressState
368
-
369
- Interfaces for tracking progress of large message transmissions.
370
-
371
- ```typescript
372
- import type { ServiceProgress, ServiceProgressState } from "@simplysm/service-client";
373
-
374
- interface ServiceProgress {
375
- request?: (s: ServiceProgressState) => void; // Request transmission progress
376
- response?: (s: ServiceProgressState) => void; // Response reception progress
377
- }
378
-
379
- interface ServiceProgressState {
380
- uuid: string; // Request unique identifier
381
- totalSize: number; // Total size (bytes)
382
- completedSize: number; // Completed size (bytes)
383
- }
384
- ```
385
-
386
- ### RemoteService\<TService\>
387
-
388
- Utility type that converts all methods of a service interface so their return types are wrapped with `Promise`. Methods already returning `Promise` are not double-wrapped. Non-function properties become `never`.
389
-
390
- ```typescript
391
- import type { RemoteService } from "@simplysm/service-client";
392
-
393
- type RemoteService<TService> = {
394
- [K in keyof TService]: TService[K] extends (...args: infer P) => infer R
395
- ? (...args: P) => Promise<Awaited<R>>
396
- : never;
397
- };
398
- ```
399
-
400
- ### SocketProvider / SocketProviderEvents
401
-
402
- Low-level WebSocket connection management interface. Not typically used directly — accessed indirectly through `ServiceClient`.
403
-
404
- ```typescript
405
- import { createSocketProvider } from "@simplysm/service-client";
406
- import type { SocketProvider, SocketProviderEvents } from "@simplysm/service-client";
407
-
408
- function createSocketProvider(
409
- url: string,
410
- clientName: string,
411
- maxReconnectCount: number,
412
- ): SocketProvider
413
- ```
414
-
415
- ```typescript
416
- interface SocketProviderEvents {
417
- message: Bytes;
418
- state: "connected" | "closed" | "reconnecting";
419
- }
420
-
421
- interface SocketProvider {
422
- readonly clientName: string;
423
- readonly connected: boolean;
424
- on<K extends keyof SocketProviderEvents & string>(type: K, listener: (data: SocketProviderEvents[K]) => void): void;
425
- off<K extends keyof SocketProviderEvents & string>(type: K, listener: (data: SocketProviderEvents[K]) => void): void;
426
- connect(): Promise<void>;
427
- close(): Promise<void>;
428
- send(data: Bytes): Promise<void>;
429
- }
430
- ```
431
-
432
- | Constant | Value | Description |
433
- |------|-----|------|
434
- | Heartbeat Timeout | 30s | Connection considered disconnected if no messages for this duration |
435
- | Heartbeat Interval | 5s | Ping transmission interval |
436
- | Reconnect Delay | 3s | Reconnection attempt interval |
437
-
438
- ### ServiceTransport / ServiceTransportEvents
439
-
440
- Message transport layer interface. Handles request/response matching, progress tracking, and protocol encoding/decoding. Not typically used directly — accessed indirectly through `ServiceClient`.
441
-
442
- ```typescript
443
- import { createServiceTransport } from "@simplysm/service-client";
444
- import type { ServiceTransport, ServiceTransportEvents } from "@simplysm/service-client";
445
-
446
- function createServiceTransport(
447
- socket: SocketProvider,
448
- protocol: ClientProtocolWrapper,
449
- ): ServiceTransport
450
- ```
451
-
452
- ```typescript
453
- interface ServiceTransportEvents {
454
- reload: Set<string>;
455
- event: { keys: string[]; data: unknown };
456
- }
457
-
458
- interface ServiceTransport {
459
- on<K extends keyof ServiceTransportEvents & string>(type: K, listener: (data: ServiceTransportEvents[K]) => void): void;
460
- off<K extends keyof ServiceTransportEvents & string>(type: K, listener: (data: ServiceTransportEvents[K]) => void): void;
461
- send(message: ServiceClientMessage, progress?: ServiceProgress): Promise<unknown>;
462
- }
463
- ```
464
-
465
- ### ClientProtocolWrapper
466
-
467
- Protocol wrapper interface. Automatically selects main thread/Web Worker for encoding/decoding based on data size. In browser environments, data exceeding 30KB is automatically processed in a Web Worker to prevent main thread blocking.
468
-
469
- ```typescript
470
- import { createClientProtocolWrapper } from "@simplysm/service-client";
471
- import type { ClientProtocolWrapper } from "@simplysm/service-client";
472
-
473
- function createClientProtocolWrapper(protocol: ServiceProtocol): ClientProtocolWrapper
474
- ```
475
-
476
- ```typescript
477
- interface ClientProtocolWrapper {
478
- encode(uuid: string, message: ServiceMessage): Promise<{ chunks: Bytes[]; totalSize: number }>;
479
- decode(bytes: Bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>;
480
- }
481
- ```
482
-
483
- | Threshold | Condition |
484
- |--------|------|
485
- | 30KB or less | Processed directly in main thread |
486
- | Over 30KB | Delegated to Web Worker (browser environments only) |
487
-
488
- Worker delegation conditions (during encoding):
489
- - `Uint8Array` data
490
- - Strings exceeding 30KB
491
- - Arrays exceeding 100 elements or arrays containing `Uint8Array`
492
-
493
- ### EventClient
494
-
495
- Server event subscription/publishing interface. Automatically recovers listeners on reconnection.
496
-
497
- ```typescript
498
- import { createEventClient } from "@simplysm/service-client";
499
- import type { EventClient } from "@simplysm/service-client";
500
-
501
- function createEventClient(transport: ServiceTransport): EventClient
502
- ```
503
-
504
- ```typescript
505
- interface EventClient {
506
- addListener<TInfo, TData>(eventDef: ServiceEventDef<TInfo, TData>, info: TInfo, cb: (data: TData) => PromiseLike<void>): Promise<string>;
507
- removeListener(key: string): Promise<void>;
508
- emitToServer<TInfo, TData>(eventDef: ServiceEventDef<TInfo, TData>, infoSelector: (item: TInfo) => boolean, data: TData): Promise<void>;
509
- reRegisterAll(): Promise<void>;
510
- }
511
- ```
512
-
513
- | Method | Description |
514
- |--------|------|
515
- | `addListener(eventDef, info, cb)` | Register event listener on the server. Returns a listener key for later removal. |
516
- | `removeListener(key)` | Unregister event listener from the server by key. |
517
- | `emitToServer(eventDef, infoSelector, data)` | Send event to all server-registered listeners matching `infoSelector`. |
518
- | `reRegisterAll()` | Re-register all listeners (called automatically on reconnection). |
519
-
520
- ### FileClient
521
-
522
- HTTP-based file upload/download interface.
523
-
524
- ```typescript
525
- import { createFileClient } from "@simplysm/service-client";
526
- import type { FileClient } from "@simplysm/service-client";
527
-
528
- function createFileClient(hostUrl: string, clientName: string): FileClient
529
- ```
530
-
531
- ```typescript
532
- interface FileClient {
533
- download(relPath: string): Promise<Bytes>;
534
- upload(
535
- files: File[] | FileList | { name: string; data: BlobPart }[],
536
- authToken: string,
537
- ): Promise<ServiceUploadResult[]>;
538
- }
539
- ```
540
-
541
- ### OrmConnectConfig\<TDef\>
542
-
543
- ORM remote connection configuration interface.
544
-
545
- ```typescript
546
- import type { OrmConnectConfig } from "@simplysm/service-client";
547
- ```
548
-
549
- | Property | Type | Required | Description |
550
- |------|------|------|------|
551
- | `dbContextDef` | `TDef` | Yes | DbContext class |
552
- | `connOpt` | `DbConnOptions & { configName: string }` | Yes | DB connection options. `configName` identifies the server-side DB config; `config` can pass additional connection settings |
553
- | `dbContextOpt` | `{ database: string; schema: string }` | No | Database/schema override |
554
-
555
- ### OrmClientConnector
556
-
557
- ORM remote connection connector interface. Manages transaction lifecycle over RPC.
558
-
559
- ```typescript
560
- import { createOrmClientConnector } from "@simplysm/service-client";
561
- import type { OrmClientConnector } from "@simplysm/service-client";
562
8
 
563
- function createOrmClientConnector(serviceClient: ServiceClient): OrmClientConnector
564
- ```
9
+ ## Source Index
565
10
 
566
- ```typescript
567
- interface OrmClientConnector {
568
- connect<TDef extends DbContextDef<any, any, any>, R>(
569
- config: OrmConnectConfig<TDef>,
570
- callback: (db: DbContextInstance<TDef>) => Promise<R> | R,
571
- ): Promise<R>;
572
- connectWithoutTransaction<TDef extends DbContextDef<any, any, any>, R>(
573
- config: OrmConnectConfig<TDef>,
574
- callback: (db: DbContextInstance<TDef>) => Promise<R> | R,
575
- ): Promise<R>;
576
- }
577
- ```
11
+ ### Types
578
12
 
579
- | Method | Description |
580
- |--------|------|
581
- | `connect(config, callback)` | Open a transactional DB connection. Commits on success, rolls back on error. Foreign key errors are converted to user-friendly messages. |
582
- | `connectWithoutTransaction(config, callback)` | Open a non-transactional DB connection. Suitable for read-only operations. |
13
+ | Source | Exports | Description | Test |
14
+ |--------|---------|-------------|------|
15
+ | `src/types/connection-config.ts` | `ServiceConnectionConfig` | Host, port, SSL, and reconnect options for a service connection | - |
16
+ | `src/types/progress.types.ts` | `ServiceProgress`, `ServiceProgressState` | Callbacks and state shape for tracking request/response progress | - |
583
17
 
584
- ### OrmClientDbContextExecutor
18
+ ### Transport
585
19
 
586
- Implements the `DbContextExecutor` interface from `@simplysm/orm-common`. Delegates all DB operations to the server's `OrmService` via RPC. Not typically used directly — used internally by `OrmClientConnector`.
20
+ | Source | Exports | Description | Test |
21
+ |--------|---------|-------------|------|
22
+ | `src/transport/socket-provider.ts` | `SocketProviderEvents`, `SocketProvider`, `createSocketProvider` | WebSocket wrapper with heartbeat, auto-reconnect, and ping/pong | - |
23
+ | `src/transport/service-transport.ts` | `ServiceTransportEvents`, `ServiceTransport`, `createServiceTransport` | Sends protocol messages over a socket and matches responses by UUID | - |
587
24
 
588
- ```typescript
589
- import { OrmClientDbContextExecutor } from "@simplysm/service-client";
25
+ ### Protocol
590
26
 
591
- class OrmClientDbContextExecutor implements DbContextExecutor {
592
- constructor(client: ServiceClient, opt: DbConnOptions & { configName: string })
593
- getInfo(): Promise<{ dialect: Dialect; database?: string; schema?: string }>
594
- connect(): Promise<void>
595
- beginTransaction(isolationLevel?: IsolationLevel): Promise<void>
596
- commitTransaction(): Promise<void>
597
- rollbackTransaction(): Promise<void>
598
- close(): Promise<void>
599
- executeDefs<T = Record<string, unknown>>(defs: QueryDef[], options?: (ResultMeta | undefined)[]): Promise<T[][]>
600
- executeParametrized(query: string, params?: unknown[]): Promise<unknown[][]>
601
- bulkInsert(tableName: string, columnDefs: Record<string, ColumnMeta>, records: Record<string, unknown>[]): Promise<void>
602
- }
603
- ```
27
+ | Source | Exports | Description | Test |
28
+ |--------|---------|-------------|------|
29
+ | `src/protocol/client-protocol-wrapper.ts` | `ClientProtocolWrapper`, `createClientProtocolWrapper` | Encodes/decodes protocol messages, offloading large payloads to a Web Worker | - |
604
30
 
605
- ## Architecture
31
+ ### Features
606
32
 
607
- ```
608
- ServiceClient (integrated entry point)
609
- |
610
- +-- SocketProvider (WebSocket connection management)
611
- | +-- Heartbeat (Ping/Pong)
612
- | +-- Auto reconnection
613
- |
614
- +-- ServiceTransport (message send/receive)
615
- | +-- ClientProtocolWrapper (encoding/decoding)
616
- | | +-- ServiceProtocol (main thread)
617
- | | +-- Web Worker (large data)
618
- | +-- Request/response matching (UUID-based)
619
- | +-- Progress tracking
620
- |
621
- +-- EventClient (event subscription/publishing)
622
- | +-- Listener management (registration/removal)
623
- | +-- Auto recovery on reconnection
624
- |
625
- +-- FileClient (HTTP file transfer)
626
- +-- Upload (FormData, POST)
627
- +-- Download (GET)
628
- ```
33
+ | Source | Exports | Description | Test |
34
+ |--------|---------|-------------|------|
35
+ | `src/features/event-client.ts` | `EventClient`, `createEventClient` | Registers, removes, and re-registers server-side event listeners | - |
36
+ | `src/features/file-client.ts` | `FileClient`, `createFileClient` | Downloads files via fetch and uploads files via multipart form-data | - |
37
+ | `src/features/orm/orm-connect-config.ts` | `OrmConnectConfig` | Config type pairing a DbContextDef with connection options and DB name | - |
38
+ | `src/features/orm/orm-client-connector.ts` | `OrmClientConnector`, `createOrmClientConnector` | Opens a remote ORM DB context with or without a transaction | - |
39
+ | `src/features/orm/orm-client-db-context-executor.ts` | `OrmClientDbContextExecutor` | DbContextExecutor that delegates all DB operations to the server OrmService | - |
629
40
 
630
- ## Caveats
41
+ ### Main
631
42
 
632
- - **Auth Required**: You must authenticate with `auth()` before calling `uploadFile()`. An error occurs if not authenticated.
633
- - **Connection State Check**: `addEventListener()` can only be called when connected to the server. An error occurs if not connected.
634
- - **Auto Reconnection**: If the connection is lost, automatic reconnection is attempted up to `maxReconnectCount` times at 3-second intervals. On successful reconnection, auth tokens and event listeners are automatically recovered.
635
- - **Large Messages**: Large messages are automatically split/merged by `ServiceProtocol` from `@simplysm/service-common`. Progress can be tracked via `ServiceProgress` callbacks or `ServiceClient` events.
636
- - **Web Worker**: In browser environments, encoding/decoding of data exceeding 30KB is automatically handled in a Web Worker. In Node.js environments, it's always processed in the main thread.
637
- - **Foreign Key Error Conversion**: ORM connection errors due to foreign key constraint violations are automatically converted to user-friendly messages.
43
+ | Source | Exports | Description | Test |
44
+ |--------|---------|-------------|------|
45
+ | `src/service-client.ts` | `ServiceClient`, `RemoteService`, `createServiceClient` | Top-level client that composes socket, transport, events, and file features | - |
638
46
 
639
47
  ## License
640
48
 
@@ -46,7 +46,7 @@ function createEventClient(transport) {
46
46
  body: { key, name: value.eventName, info: value.info }
47
47
  });
48
48
  } catch (err) {
49
- logger.error("\uC774\uBCA4\uD2B8 \uB9AC\uC2A4\uB108 \uBCF5\uAD6C \uC2E4\uD328", { err, eventName: value.eventName });
49
+ logger.error("Failed to recover event listener", { err, eventName: value.eventName });
50
50
  }
51
51
  }
52
52
  }
@@ -57,7 +57,7 @@ function createEventClient(transport) {
57
57
  try {
58
58
  await entry.cb(data);
59
59
  } catch (err) {
60
- logger.error("\uC774\uBCA4\uD2B8 \uD578\uB4E4\uB7EC \uC624\uB958", { err, eventName: entry.eventName });
60
+ logger.error("Event handler error", { err, eventName: entry.eventName });
61
61
  }
62
62
  }
63
63
  }