@simplysm/service-common 14.0.49 → 14.0.50

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
@@ -21,71 +21,50 @@ npm install @simplysm/service-common
21
21
 
22
22
  ### Protocol
23
23
 
24
- | API | Type | Description |
25
- |-----|------|-------------|
26
- | `PROTOCOL_CONFIG` | const | 프로토콜 설정 상수 (최대 크기, 청킹 임계값, 청크 크기, GC 주기, 만료 시간) |
27
- | `ServiceMessage` | type | 양방향 메시지의 유니언 타입 |
28
- | `ServiceClientMessage` | type | 클라이언트 서버 메시지 유니언 |
29
- | `ServiceServerMessage` | type | 서버 클라이언트 메시지 유니언 |
30
- | `ServiceServerRawMessage` | type | 진행 상태를 포함한 서버 메시지 유니언 |
31
- | `ServiceProgressMessage` | interface | 청크 수신 진행 상태 알림 |
32
- | `ServiceErrorMessage` | interface | 서버 에러 알림 |
33
- | `ServiceAuthMessage` | interface | 클라이언트 인증 메시지 (토큰) |
34
- | `ServiceRequestMessage` | interface | 서비스 메서드 요청 |
35
- | `ServiceResponseMessage` | interface | 서비스 메서드 응답 |
36
- | `ServiceAddEventListenerMessage` | interface | 이벤트 리스너 추가 요청 |
37
- | `ServiceRemoveEventListenerMessage` | interface | 이벤트 리스너 제거 요청 |
38
- | `ServiceGetEventListenerInfosMessage` | interface | 이벤트 리스너 정보 목록 요청 |
39
- | `ServiceEmitEventMessage` | interface | 이벤트 발생 요청 |
40
- | `ServiceEventMessage` | interface | 서버 이벤트 알림 |
41
- | `ServiceProtocol` | interface | 바이너리 프로토콜 인코더/디코더 |
42
- | `ServiceMessageDecodeResult` | type | 디코딩 결과 유니언 (complete \| progress) |
43
- | `createServiceProtocol` | function | ServiceProtocol 인스턴스 생성 팩토리 |
44
-
45
- → See [docs/protocol.md](./docs/protocol.md) for details.
24
+ | Entry | Kind | Description |
25
+ |-------|------|-------------|
26
+ | [`PROTOCOL_CONFIG`](./docs/protocol/protocol-config.md) | const | 프로토콜 설정 상수 (최대 크기, 청킹 임계값, 청크 크기, GC 주기, 만료 시간) |
27
+ | [`ServiceMessage`](./docs/protocol/service-message.md) | type | 양방향 메시지 유니언 (`ServiceClientMessage`, `ServiceServerMessage`, `ServiceServerRawMessage` 포함) |
28
+ | [`ServiceProgressMessage`](./docs/protocol/service-progress-message.md) | interface | 청크 수신 진행 상태 알림 |
29
+ | [`ServiceErrorMessage`](./docs/protocol/service-error-message.md) | interface | 서버 에러 알림 |
30
+ | [`ServiceAuthMessage`](./docs/protocol/service-auth-message.md) | interface | 클라이언트 인증 메시지 (토큰) |
31
+ | [`ServiceRequestMessage`](./docs/protocol/service-request-message.md) | interface | 서비스 메서드 요청 |
32
+ | [`ServiceResponseMessage`](./docs/protocol/service-response-message.md) | interface | 서비스 메서드 응답 |
33
+ | [`ServiceAddEventListenerMessage`](./docs/protocol/service-add-event-listener-message.md) | interface | 이벤트 리스너 추가 요청 |
34
+ | [`ServiceRemoveEventListenerMessage`](./docs/protocol/service-remove-event-listener-message.md) | interface | 이벤트 리스너 제거 요청 |
35
+ | [`ServiceGetEventListenerInfosMessage`](./docs/protocol/service-get-event-listener-infos-message.md) | interface | 이벤트 리스너 정보 목록 요청 |
36
+ | [`ServiceEmitEventMessage`](./docs/protocol/service-emit-event-message.md) | interface | 이벤트 발생 요청 |
37
+ | [`ServiceEventMessage`](./docs/protocol/service-event-message.md) | interface | 서버 이벤트 알림 |
38
+ | [`createServiceProtocol`](./docs/protocol/create-service-protocol.md) | function | ServiceProtocol 인스턴스 생성 팩토리 (`ServiceProtocol`, `ServiceMessageDecodeResult` 포함) |
46
39
 
47
40
  ### Service Types
48
41
 
49
- | API | Type | Description |
50
- |-----|------|-------------|
51
- | `OrmService` | interface | DB 연결, 트랜잭션, 쿼리 실행 서비스 인터페이스 |
52
- | `DbConnOptions` | type | DB 연결 옵션 타입 |
53
- | `AutoUpdateService` | interface | 클라이언트 최신 버전 조회 서비스 인터페이스 |
54
- | `AppStructureService` | interface | 앱 구조 항목 조회 서비스 인터페이스 |
55
-
56
- → See [docs/service-types.md](./docs/service-types.md) for details.
42
+ | Entry | Kind | Description |
43
+ |-------|------|-------------|
44
+ | [`OrmService`](./docs/service-types/orm-service.md) | interface | DB 연결, 트랜잭션, 쿼리 실행 서비스 인터페이스 (`DbConnOptions` 포함) |
45
+ | [`AutoUpdateService`](./docs/service-types/auto-update-service.md) | interface | 클라이언트 최신 버전 조회 서비스 인터페이스 |
46
+ | [`AppStructureService`](./docs/service-types/app-structure-service.md) | interface | 구조 항목 조회 서비스 인터페이스 |
57
47
 
58
48
  ### Types
59
49
 
60
- | API | Type | Description |
61
- |-----|------|-------------|
62
- | `ServiceUploadResult` | interface | 파일 업로드 결과 |
63
-
64
- → See [docs/types.md](./docs/types.md) for details.
50
+ | Entry | Kind | Description |
51
+ |-------|------|-------------|
52
+ | [`ServiceUploadResult`](./docs/types/service-upload-result.md) | interface | 파일 업로드 결과 |
65
53
 
66
54
  ### App Structure
67
55
 
68
- | API | Type | Description |
69
- |-----|------|-------------|
70
- | `AppStructureItem` | type | 앱 구조 항목 (그룹 또는 리프) |
71
- | `AppStructureGroupItem` | interface | 자식을 가진 그룹 메뉴 항목 |
72
- | `AppStructureLeafItem` | interface | 말단 메뉴 항목 (권한, URL 포함) |
73
- | `AppStructureSubPermission` | interface | 리프 항목의 하위 권한 정의 |
74
- | `FlatPermission` | interface | 플래트닝된 권한 결과 |
75
- | `isUsableModules` | function | 개별 항목의 모듈 접근 가능 여부 판단 |
76
- | `isUsableModulesChain` | function | 모듈 체인 전체의 접근 가능 여부 판단 |
77
- | `getFlatPermissions` | function | 앱 구조 트리를 플래트닝하여 권한 배열 반환 |
78
-
79
- → See [docs/app-structure.md](./docs/app-structure.md) for details.
56
+ | Entry | Kind | Description |
57
+ |-------|------|-------------|
58
+ | [`AppStructureItem`](./docs/app-structure/app-structure-item.md) | type | 앱 구조 항목 유니언 (`AppStructureGroupItem`, `AppStructureLeafItem`, `AppStructureSubPermission`, `FlatPermission` 포함) |
59
+ | [`isUsableModules`](./docs/app-structure/is-usable-modules.md) | function | 단일 항목의 모듈 접근 가능 여부 판단 |
60
+ | [`isUsableModulesChain`](./docs/app-structure/is-usable-modules-chain.md) | function | 모듈 체인 전체의 접근 가능 여부 판단 |
61
+ | [`getFlatPermissions`](./docs/app-structure/get-flat-permissions.md) | function | 구조 트리를 플래트닝하여 권한 배열 반환 |
80
62
 
81
63
  ### Events
82
64
 
83
- | API | Type | Description |
84
- |-----|------|-------------|
85
- | `ServiceEventDef` | interface | 타입 안전 이벤트 정의 인터페이스 |
86
- | `defineEvent` | function | 타입 안전 이벤트를 정의하는 팩토리 함수 |
87
-
88
- → See [docs/events.md](./docs/events.md) for details.
65
+ | Entry | Kind | Description |
66
+ |-------|------|-------------|
67
+ | [`defineEvent`](./docs/events/define-event.md) | function | 타입 안전 이벤트를 정의하는 팩토리 함수 (`ServiceEventDef` 포함) |
89
68
 
90
69
  ## Usage Examples
91
70
 
@@ -0,0 +1,107 @@
1
+ # AppStructureItem
2
+
3
+ 앱 구조 항목 유니언 타입. `children` 필드 유무로 그룹(`AppStructureGroupItem`)과 리프(`AppStructureLeafItem`)를 구분한다.
4
+
5
+ ```typescript
6
+ export type AppStructureItem<TModule = unknown> =
7
+ | AppStructureGroupItem<TModule>
8
+ | AppStructureLeafItem<TModule>;
9
+ ```
10
+
11
+ `TModule` 제네릭은 모듈 식별자 타입이다 (일반적으로 `string`).
12
+
13
+ ## Related Types
14
+
15
+ ### `AppStructureGroupItem`
16
+
17
+ 자식을 가진 그룹 메뉴 항목. `children` 필드로 하위 항목을 재귀적으로 포함한다.
18
+
19
+ ```typescript
20
+ export interface AppStructureGroupItem<TModule> {
21
+ code: string;
22
+ title: string;
23
+ modules?: TModule[];
24
+ requiredModules?: TModule[];
25
+ icon?: string;
26
+ children: AppStructureItem<TModule>[];
27
+ }
28
+ ```
29
+
30
+ | Field | Type | Description |
31
+ |-------|------|-------------|
32
+ | `code` | `string` | 항목 코드 (권한 코드 체인에 사용) |
33
+ | `title` | `string` | 표시 이름 |
34
+ | `modules` | `TModule[]?` | 접근에 필요한 모듈 목록 (OR 조건: 하나라도 있으면 접근 가능) |
35
+ | `requiredModules` | `TModule[]?` | 접근에 필수인 모듈 목록 (AND 조건: 모두 있어야 접근 가능) |
36
+ | `icon` | `string?` | 아이콘 식별자 |
37
+ | `children` | `AppStructureItem<TModule>[]` | 하위 메뉴 항목 배열 |
38
+
39
+ ### `AppStructureLeafItem`
40
+
41
+ 말단 메뉴 항목. 실제 페이지 URL과 권한(`perms`)을 가진다.
42
+
43
+ ```typescript
44
+ export interface AppStructureLeafItem<TModule> {
45
+ code: string;
46
+ title: string;
47
+ modules?: TModule[];
48
+ requiredModules?: TModule[];
49
+ perms?: ("use" | "edit")[];
50
+ subPerms?: AppStructureSubPermission<TModule>[];
51
+ icon?: string;
52
+ url?: string;
53
+ isNotMenu?: boolean;
54
+ }
55
+ ```
56
+
57
+ | Field | Type | Description |
58
+ |-------|------|-------------|
59
+ | `code` | `string` | 항목 코드 |
60
+ | `title` | `string` | 표시 이름 |
61
+ | `modules` | `TModule[]?` | 접근에 필요한 모듈 목록 (OR 조건) |
62
+ | `requiredModules` | `TModule[]?` | 접근에 필수인 모듈 목록 (AND 조건) |
63
+ | `perms` | `("use" \| "edit")[]?` | 이 항목에 부여 가능한 권한 종류 |
64
+ | `subPerms` | `AppStructureSubPermission<TModule>[]?` | 하위 권한 정의 배열 |
65
+ | `icon` | `string?` | 아이콘 식별자 |
66
+ | `url` | `string?` | 페이지 URL |
67
+ | `isNotMenu` | `boolean?` | `true`이면 메뉴에 표시하지 않음 |
68
+
69
+ ### `AppStructureSubPermission`
70
+
71
+ 리프 항목의 하위 권한 정의. 각 하위 권한도 모듈 접근 제어를 가진다.
72
+
73
+ ```typescript
74
+ export interface AppStructureSubPermission<TModule> {
75
+ code: string;
76
+ title: string;
77
+ modules?: TModule[];
78
+ requiredModules?: TModule[];
79
+ perms: ("use" | "edit")[];
80
+ }
81
+ ```
82
+
83
+ | Field | Type | Description |
84
+ |-------|------|-------------|
85
+ | `code` | `string` | 하위 권한 코드 |
86
+ | `title` | `string` | 하위 권한 표시 이름 |
87
+ | `modules` | `TModule[]?` | 접근에 필요한 모듈 목록 (OR 조건) |
88
+ | `requiredModules` | `TModule[]?` | 접근에 필수인 모듈 목록 (AND 조건) |
89
+ | `perms` | `("use" \| "edit")[]` | 부여 가능한 권한 종류 |
90
+
91
+ ### `FlatPermission`
92
+
93
+ 트리를 플래트닝한 권한 결과. [`getFlatPermissions`](./get-flat-permissions.md)의 반환 타입이다.
94
+
95
+ ```typescript
96
+ export interface FlatPermission<TModule = unknown> {
97
+ titleChain: string[];
98
+ codeChain: string[];
99
+ modulesChain: TModule[][];
100
+ }
101
+ ```
102
+
103
+ | Field | Type | Description |
104
+ |-------|------|-------------|
105
+ | `titleChain` | `string[]` | 루트부터 현재 권한까지의 표시 이름 체인 (예: `["관리", "사용자"]`) |
106
+ | `codeChain` | `string[]` | 루트부터 현재 권한까지의 코드 체인 (예: `["admin", "user", "use"]`) |
107
+ | `modulesChain` | `TModule[][]` | 각 레벨에서 필요한 모듈 목록의 체인 |
@@ -0,0 +1,57 @@
1
+ # getFlatPermissions
2
+
3
+ 앱 구조 트리를 BFS로 순회하며 모듈 조건을 필터링하여 [`FlatPermission`](./app-structure-item.md)`[]`으로 플래트닝한다.
4
+
5
+ ```typescript
6
+ export function getFlatPermissions<TModule>(
7
+ items: AppStructureItem<TModule>[],
8
+ usableModules: TModule[] | undefined,
9
+ ): FlatPermission<TModule>[];
10
+ ```
11
+
12
+ ## Parameters
13
+
14
+ | Param | Type | Description |
15
+ |-------|------|-------------|
16
+ | `items` | `AppStructureItem<TModule>[]` | 앱 구조 트리의 최상위 항목 배열 |
17
+ | `usableModules` | `TModule[] \| undefined` | 사용자가 보유한 활성 모듈 목록. `undefined`이면 모듈 조건이 없는 항목만 포함 |
18
+
19
+ ## Returns
20
+
21
+ `FlatPermission<TModule>[]` — 모듈 조건을 만족하는 모든 권한의 플랫 목록.
22
+
23
+ 처리 로직:
24
+ 1. BFS로 트리를 순회하며 각 레벨의 `modules`(OR)와 `requiredModules`(AND) 조건을 체크
25
+ 2. 조건 미충족 항목은 하위 트리 전체를 건너뜀
26
+ 3. `AppStructureLeafItem`의 `perms`를 `codeChain`에 추가하여 `FlatPermission` 생성
27
+ 4. `subPerms`도 개별 모듈 조건을 체크하여 `FlatPermission`으로 변환
28
+
29
+ ## Usage
30
+
31
+ ```typescript
32
+ import { getFlatPermissions } from "@simplysm/service-common";
33
+ import type { AppStructureItem } from "@simplysm/service-common";
34
+
35
+ const items: AppStructureItem<string>[] = [
36
+ {
37
+ code: "admin",
38
+ title: "관리",
39
+ children: [
40
+ { code: "user", title: "사용자", perms: ["use", "edit"] },
41
+ ],
42
+ },
43
+ {
44
+ code: "report",
45
+ title: "리포트",
46
+ modules: ["moduleA"],
47
+ perms: ["use"],
48
+ },
49
+ ];
50
+
51
+ const perms = getFlatPermissions(items, ["moduleA"]);
52
+ // [
53
+ // { titleChain: ["관리", "사용자"], codeChain: ["admin", "user", "use"], modulesChain: [] },
54
+ // { titleChain: ["관리", "사용자"], codeChain: ["admin", "user", "edit"], modulesChain: [] },
55
+ // { titleChain: ["리포트"], codeChain: ["report", "use"], modulesChain: [["moduleA"]] },
56
+ // ]
57
+ ```
@@ -0,0 +1,23 @@
1
+ # isUsableModulesChain
2
+
3
+ 모듈 체인 전체의 접근 가능 여부를 판단한다. 트리의 각 레벨에서 모듈 조건을 모두 만족해야 한다.
4
+
5
+ ```typescript
6
+ export function isUsableModulesChain<TModule>(
7
+ modulesChain: TModule[][],
8
+ requiredModulesChain: TModule[][],
9
+ usableModules: TModule[] | undefined,
10
+ ): boolean;
11
+ ```
12
+
13
+ ## Parameters
14
+
15
+ | Param | Type | Description |
16
+ |-------|------|-------------|
17
+ | `modulesChain` | `TModule[][]` | 각 레벨의 OR 조건 모듈 배열 |
18
+ | `requiredModulesChain` | `TModule[][]` | 각 레벨의 AND 조건 모듈 배열 |
19
+ | `usableModules` | `TModule[] \| undefined` | 사용자가 보유한 활성 모듈 목록 |
20
+
21
+ ## Returns
22
+
23
+ `boolean` — 모든 레벨의 조건을 만족하면 `true`. 하나라도 실패하면 `false`.
@@ -0,0 +1,42 @@
1
+ # isUsableModules
2
+
3
+ 단일 항목의 모듈 접근 가능 여부를 판단한다.
4
+
5
+ ```typescript
6
+ export function isUsableModules<TModule>(
7
+ modules: TModule[] | undefined,
8
+ requiredModules: TModule[] | undefined,
9
+ usableModules: TModule[] | undefined,
10
+ ): boolean;
11
+ ```
12
+
13
+ ## Parameters
14
+
15
+ | Param | Type | Description |
16
+ |-------|------|-------------|
17
+ | `modules` | `TModule[] \| undefined` | OR 조건 모듈 목록. 하나라도 `usableModules`에 포함되면 통과 |
18
+ | `requiredModules` | `TModule[] \| undefined` | AND 조건 모듈 목록. 모두 `usableModules`에 포함되어야 통과 |
19
+ | `usableModules` | `TModule[] \| undefined` | 사용자가 보유한 활성 모듈 목록 |
20
+
21
+ ## Returns
22
+
23
+ `boolean` — `modules`와 `requiredModules` 조건을 모두 만족하면 `true`.
24
+
25
+ - `modules`가 `undefined`이거나 빈 배열이면 OR 조건은 자동 통과
26
+ - `requiredModules`가 `undefined`이거나 빈 배열이면 AND 조건은 자동 통과
27
+ - `usableModules`가 `undefined`이면 `modules`가 있을 때 `false`
28
+
29
+ ## Usage
30
+
31
+ ```typescript
32
+ import { isUsableModules } from "@simplysm/service-common";
33
+
34
+ // OR 조건: moduleA 또는 moduleB 중 하나라도 있으면 true
35
+ isUsableModules(["moduleA", "moduleB"], undefined, ["moduleA"]); // true
36
+
37
+ // AND 조건: 모두 있어야 true
38
+ isUsableModules(undefined, ["moduleA", "moduleB"], ["moduleA"]); // false
39
+
40
+ // 모듈 없음: 자동 통과
41
+ isUsableModules(undefined, undefined, undefined); // true
42
+ ```
@@ -1,6 +1,31 @@
1
- # Events
1
+ # defineEvent
2
2
 
3
- ## `ServiceEventDef`
3
+ 타입 안전한 서비스 이벤트를 정의하는 팩토리 함수.
4
+
5
+ ```typescript
6
+ export function defineEvent<TInfo = unknown, TData = unknown>(
7
+ eventName: string,
8
+ ): ServiceEventDef<TInfo, TData>;
9
+ ```
10
+
11
+ ## Parameters
12
+
13
+ | Param | Type | Description |
14
+ |-------|------|-------------|
15
+ | `eventName` | `string` | 이벤트 이름 (고유해야 함) |
16
+
17
+ | Type Parameter | Default | Description |
18
+ |---------------|---------|-------------|
19
+ | `TInfo` | `unknown` | 이벤트 필터링 조건의 타입. 구독 시 필터로 사용 |
20
+ | `TData` | `unknown` | 이벤트 페이로드의 타입. 이벤트 발생/수신 시 데이터 타입 |
21
+
22
+ ## Returns
23
+
24
+ `ServiceEventDef<TInfo, TData>` — 이벤트 정의 인스턴스.
25
+
26
+ ## Related Types
27
+
28
+ ### `ServiceEventDef`
4
29
 
5
30
  `defineEvent()`로 생성된 이벤트 정의. `$info`와 `$data`는 런타임에서 사용되지 않는 타입 전용 마커다.
6
31
 
@@ -18,32 +43,11 @@ export interface ServiceEventDef<TInfo = unknown, TData = unknown> {
18
43
  | `$info` | `TInfo` | 타입 추출 전용 마커 (런타임에서 사용하지 않음). 이벤트 필터링 조건 타입 |
19
44
  | `$data` | `TData` | 타입 추출 전용 마커 (런타임에서 사용하지 않음). 이벤트 페이로드 타입 |
20
45
 
21
- `TInfo`와 `TData`는 제네릭 파라미터로, `defineEvent<TInfo, TData>()` 호출 시 지정된 타입이 할당된다. 기본값은 `unknown`.
22
-
23
- ## `defineEvent`
24
-
25
- 타입 안전한 서비스 이벤트를 정의하는 팩토리 함수.
46
+ ## Usage
26
47
 
27
48
  ```typescript
28
- export function defineEvent<TInfo = unknown, TData = unknown>(
29
- eventName: string,
30
- ): ServiceEventDef<TInfo, TData>;
31
- ```
32
-
33
- | Parameter | Type | Description |
34
- |-----------|------|-------------|
35
- | `eventName` | `string` | 이벤트 이름 (고유해야 함) |
49
+ import { defineEvent } from "@simplysm/service-common";
36
50
 
37
- | Type Parameter | Default | Description |
38
- |---------------|---------|-------------|
39
- | `TInfo` | `unknown` | 이벤트 필터링 조건의 타입. 구독 시 필터로 사용 |
40
- | `TData` | `unknown` | 이벤트 페이로드의 타입. 이벤트 발생/수신 시 데이터 타입 |
41
-
42
- 반환: `ServiceEventDef<TInfo, TData>` 인스턴스.
43
-
44
- 사용 예:
45
-
46
- ```typescript
47
51
  // 서버에서 이벤트 정의 + 타입 export
48
52
  export const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
49
53
 
@@ -60,5 +64,5 @@ const key = await orderEvt.addListener({ orderId: 123 }, async (data) => {
60
64
 
61
65
  // 직접 호출 방식 (하위 호환)
62
66
  await server.emitEvent<typeof OrderUpdated>("OrderUpdated", (info) => info.orderId === 123, { status: "shipped" });
63
- await client.addListener<typeof OrderUpdated>("OrderUpdated", { orderId: 123 }, async (data) => { ... });
67
+ await client.addListener<typeof OrderUpdated>("OrderUpdated", { orderId: 123 }, async (data) => { /* ... */ });
64
68
  ```
@@ -0,0 +1,93 @@
1
+ # createServiceProtocol
2
+
3
+ ServiceProtocol 인스턴스를 생성하는 팩토리 함수.
4
+
5
+ ```typescript
6
+ export function createServiceProtocol(): ServiceProtocol;
7
+ ```
8
+
9
+ 내부에 `LazyGcMap` 기반 청크 누적기를 캡슐화한다. 미완성 메시지는 60초 후 GC로 자동 정리된다. 사용 후 반드시 `dispose()`를 호출하여 GC 타이머를 해제해야 한다.
10
+
11
+ ## Returns
12
+
13
+ `ServiceProtocol` — 인코딩/디코딩/해제 메서드를 포함한 프로토콜 인스턴스.
14
+
15
+ ## Related Types
16
+
17
+ ### `ServiceProtocol`
18
+
19
+ 바이너리 프로토콜 V2 인코더/디코더 인터페이스.
20
+
21
+ ```typescript
22
+ export interface ServiceProtocol {
23
+ encode(uuid: string, message: ServiceMessage): { chunks: Bytes[]; totalSize: number };
24
+ decode<T extends ServiceMessage>(bytes: Bytes): ServiceMessageDecodeResult<T>;
25
+ dispose(): void;
26
+ }
27
+ ```
28
+
29
+ | Method | Parameters | Return | Description |
30
+ |--------|-----------|--------|-------------|
31
+ | `encode` | `uuid: string, message: ServiceMessage` | `{ chunks: Bytes[]; totalSize: number }` | 메시지를 인코딩한다. 3MB 초과 시 300KB 청크로 자동 분할. `totalSize` > 100MB이면 `ArgumentError` 발생 |
32
+ | `decode` | `bytes: Bytes` | `ServiceMessageDecodeResult<T>` | 청크를 디코딩한다. 청크가 모두 도착하면 `complete`, 아니면 `progress` 반환 |
33
+ | `dispose` | 없음 | `void` | 내부 GC 타이머와 청크 누적기를 해제한다. 사용 후 반드시 호출 |
34
+
35
+ 헤더 구조 (28바이트, Big Endian):
36
+
37
+ | Offset | Size | Field |
38
+ |--------|------|-------|
39
+ | 0 | 16 | UUID (바이너리) |
40
+ | 16 | 8 | TotalSize (uint64, 상위 4바이트 = 0) |
41
+ | 24 | 4 | Index (uint32) |
42
+
43
+ ### `ServiceMessageDecodeResult`
44
+
45
+ 메시지 디코딩 결과 유니언 타입. `type` 필드로 분기한다.
46
+
47
+ ```typescript
48
+ export type ServiceMessageDecodeResult<TMessage extends ServiceMessage> =
49
+ | { type: "complete"; uuid: string; message: TMessage }
50
+ | { type: "progress"; uuid: string; totalSize: number; completedSize: number };
51
+ ```
52
+
53
+ **Variant: `complete`** — 모든 청크가 도착하여 메시지 재조립이 완료된 상태.
54
+
55
+ | Field | Type | Description |
56
+ |-------|------|-------------|
57
+ | `type` | `"complete"` | discriminant |
58
+ | `uuid` | `string` | 메시지 UUID |
59
+ | `message` | `TMessage` | 재조립된 메시지 |
60
+
61
+ **Variant: `progress`** — 청크 메시지 수신 진행 중.
62
+
63
+ | Field | Type | Description |
64
+ |-------|------|-------------|
65
+ | `type` | `"progress"` | discriminant |
66
+ | `uuid` | `string` | 메시지 UUID |
67
+ | `totalSize` | `number` | 전체 크기 (바이트) |
68
+ | `completedSize` | `number` | 수신 완료된 크기 (바이트) |
69
+
70
+ ## Usage
71
+
72
+ ```typescript
73
+ import { createServiceProtocol } from "@simplysm/service-common";
74
+
75
+ const protocol = createServiceProtocol();
76
+
77
+ // 메시지 인코딩 (3MB 초과 시 자동 청킹)
78
+ const { chunks, totalSize } = protocol.encode(uuid, {
79
+ name: "OrmService.connect",
80
+ body: [{ configName: "default" }],
81
+ });
82
+
83
+ // 메시지 디코딩 (청크 자동 재조립)
84
+ for (const chunk of chunks) {
85
+ const result = protocol.decode(chunk);
86
+ if (result.type === "complete") {
87
+ // result.message: 재조립된 메시지
88
+ }
89
+ }
90
+
91
+ // 사용 후 반드시 해제
92
+ protocol.dispose();
93
+ ```
@@ -0,0 +1,21 @@
1
+ # PROTOCOL_CONFIG
2
+
3
+ 서비스 프로토콜 설정 상수.
4
+
5
+ ```typescript
6
+ export const PROTOCOL_CONFIG = {
7
+ MAX_TOTAL_SIZE: 100 * 1024 * 1024,
8
+ SPLIT_MESSAGE_SIZE: 3 * 1024 * 1024,
9
+ CHUNK_SIZE: 300 * 1024,
10
+ GC_INTERVAL: 10 * 1000,
11
+ EXPIRE_TIME: 60 * 1000,
12
+ } as const;
13
+ ```
14
+
15
+ | Field | Type | Description |
16
+ |-------|------|-------------|
17
+ | `MAX_TOTAL_SIZE` | `number` | 단일 메시지의 최대 허용 크기 (100MB). 초과 시 `ArgumentError` 발생 |
18
+ | `SPLIT_MESSAGE_SIZE` | `number` | 이 크기를 초과하면 자동으로 청크 분할 (3MB) |
19
+ | `CHUNK_SIZE` | `number` | 분할된 각 청크의 크기 (300KB) |
20
+ | `GC_INTERVAL` | `number` | 내부 청크 누적기의 가비지 컬렉션 주기 (10초, 밀리초 단위) |
21
+ | `EXPIRE_TIME` | `number` | 미완성 청크 메시지의 만료 시간 (60초). 이 시간 내에 모든 청크가 도착하지 않으면 제거 |
@@ -0,0 +1,23 @@
1
+ # ServiceAddEventListenerMessage
2
+
3
+ 클라이언트가 보내는 이벤트 리스너 추가 메시지.
4
+
5
+ ```typescript
6
+ export interface ServiceAddEventListenerMessage {
7
+ name: "evt:add";
8
+ body: {
9
+ key: string;
10
+ name: string;
11
+ info: unknown;
12
+ };
13
+ }
14
+ ```
15
+
16
+ ## Members
17
+
18
+ | Field | Type | Description |
19
+ |-------|------|-------------|
20
+ | `name` | `"evt:add"` | 고정 문자열 discriminant |
21
+ | `body.key` | `string` | 리스너 키 (UUID). `ServiceRemoveEventListenerMessage`에서 사용 |
22
+ | `body.name` | `string` | 이벤트 이름 (`ServiceEventDef.eventName`) |
23
+ | `body.info` | `unknown` | 이벤트 발생 시 필터링을 위한 추가 리스너 정보 |
@@ -0,0 +1,17 @@
1
+ # ServiceAuthMessage
2
+
3
+ 클라이언트가 보내는 인증 메시지.
4
+
5
+ ```typescript
6
+ export interface ServiceAuthMessage {
7
+ name: "auth";
8
+ body: string;
9
+ }
10
+ ```
11
+
12
+ ## Members
13
+
14
+ | Field | Type | Description |
15
+ |-------|------|-------------|
16
+ | `name` | `"auth"` | 고정 문자열 discriminant |
17
+ | `body` | `string` | 인증 토큰 |
@@ -0,0 +1,21 @@
1
+ # ServiceEmitEventMessage
2
+
3
+ 클라이언트가 보내는 이벤트 발생 메시지.
4
+
5
+ ```typescript
6
+ export interface ServiceEmitEventMessage {
7
+ name: "evt:emit";
8
+ body: {
9
+ keys: string[];
10
+ data: unknown;
11
+ };
12
+ }
13
+ ```
14
+
15
+ ## Members
16
+
17
+ | Field | Type | Description |
18
+ |-------|------|-------------|
19
+ | `name` | `"evt:emit"` | 고정 문자열 discriminant |
20
+ | `body.keys` | `string[]` | 대상 리스너 키 목록 |
21
+ | `body.data` | `unknown` | 이벤트 데이터 |
@@ -0,0 +1,29 @@
1
+ # ServiceErrorMessage
2
+
3
+ 서버가 보내는 에러 알림 메시지.
4
+
5
+ ```typescript
6
+ export interface ServiceErrorMessage {
7
+ name: "error";
8
+ body: {
9
+ name: string;
10
+ message: string;
11
+ code: string;
12
+ stack?: string;
13
+ detail?: unknown;
14
+ cause?: unknown;
15
+ };
16
+ }
17
+ ```
18
+
19
+ ## Members
20
+
21
+ | Field | Type | Description |
22
+ |-------|------|-------------|
23
+ | `name` | `"error"` | 고정 문자열 discriminant |
24
+ | `body.name` | `string` | 에러 이름 (클래스명) |
25
+ | `body.message` | `string` | 에러 메시지 |
26
+ | `body.code` | `string` | 에러 코드 |
27
+ | `body.stack` | `string?` | 스택 트레이스 (선택) |
28
+ | `body.detail` | `unknown?` | 추가 상세 정보 (선택) |
29
+ | `body.cause` | `unknown?` | 원인 에러 (선택) |
@@ -0,0 +1,21 @@
1
+ # ServiceEventMessage
2
+
3
+ 서버가 보내는 이벤트 알림 메시지.
4
+
5
+ ```typescript
6
+ export interface ServiceEventMessage {
7
+ name: "evt:on";
8
+ body: {
9
+ keys: string[];
10
+ data: unknown;
11
+ };
12
+ }
13
+ ```
14
+
15
+ ## Members
16
+
17
+ | Field | Type | Description |
18
+ |-------|------|-------------|
19
+ | `name` | `"evt:on"` | 고정 문자열 discriminant |
20
+ | `body.keys` | `string[]` | 대상 리스너 키 목록 |
21
+ | `body.data` | `unknown` | 이벤트 데이터 |
@@ -0,0 +1,19 @@
1
+ # ServiceGetEventListenerInfosMessage
2
+
3
+ 클라이언트가 보내는 이벤트 리스너 정보 목록 요청 메시지.
4
+
5
+ ```typescript
6
+ export interface ServiceGetEventListenerInfosMessage {
7
+ name: "evt:gets";
8
+ body: {
9
+ name: string;
10
+ };
11
+ }
12
+ ```
13
+
14
+ ## Members
15
+
16
+ | Field | Type | Description |
17
+ |-------|------|-------------|
18
+ | `name` | `"evt:gets"` | 고정 문자열 discriminant |
19
+ | `body.name` | `string` | 조회할 이벤트 이름 |