@simplysm/service-common 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.
- package/dist/define-event.d.ts +7 -7
- package/dist/define-event.d.ts.map +1 -1
- package/dist/define-event.js +21 -10
- package/dist/define-event.js.map +1 -6
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -2
- package/dist/index.js.map +1 -6
- package/dist/protocol/create-service-protocol.d.ts +20 -20
- package/dist/protocol/create-service-protocol.d.ts.map +1 -1
- package/dist/protocol/create-service-protocol.js +149 -112
- package/dist/protocol/create-service-protocol.js.map +1 -6
- package/dist/protocol/protocol.types.d.ts +17 -17
- package/dist/protocol/protocol.types.d.ts.map +1 -1
- package/dist/protocol/protocol.types.js +16 -15
- package/dist/protocol/protocol.types.js.map +1 -6
- package/dist/service-types/auto-update-service.types.d.ts +5 -5
- package/dist/service-types/auto-update-service.types.js +2 -1
- package/dist/service-types/auto-update-service.types.js.map +1 -6
- package/dist/service-types/orm-service.types.d.ts +7 -5
- package/dist/service-types/orm-service.types.d.ts.map +1 -1
- package/dist/service-types/orm-service.types.js +2 -1
- package/dist/service-types/orm-service.types.js.map +1 -6
- package/dist/types.d.ts +5 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -1
- package/dist/types.js.map +1 -6
- package/package.json +6 -8
- package/src/define-event.ts +7 -7
- package/src/index.ts +4 -6
- package/src/protocol/create-service-protocol.ts +45 -38
- package/src/protocol/protocol.types.ts +39 -39
- package/src/service-types/auto-update-service.types.ts +5 -5
- package/src/service-types/orm-service.types.ts +5 -5
- package/src/types.ts +5 -5
- package/README.md +0 -111
- package/dist/service-types/smtp-client-service.types.d.ts +0 -38
- package/dist/service-types/smtp-client-service.types.d.ts.map +0 -1
- package/dist/service-types/smtp-client-service.types.js +0 -1
- package/dist/service-types/smtp-client-service.types.js.map +0 -6
- package/docs/events.md +0 -51
- package/docs/protocol.md +0 -252
- package/docs/service-types.md +0 -162
- package/src/service-types/smtp-client-service.types.ts +0 -41
- package/tests/define-event.spec.ts +0 -11
- package/tests/protocol/service-protocol.spec.ts +0 -251
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
// ----------------------------------------------------------------------
|
|
2
|
+
// 프로토콜 상수
|
|
3
|
+
// ----------------------------------------------------------------------
|
|
4
|
+
/** 서비스 프로토콜 설정 */
|
|
5
|
+
export const PROTOCOL_CONFIG = {
|
|
6
|
+
/** 최대 메시지 크기 (100MB) */
|
|
7
|
+
MAX_TOTAL_SIZE: 100 * 1024 * 1024,
|
|
8
|
+
/** 청킹 임계값 (3MB) */
|
|
9
|
+
SPLIT_MESSAGE_SIZE: 3 * 1024 * 1024,
|
|
10
|
+
/** 청크 크기 (300KB) */
|
|
11
|
+
CHUNK_SIZE: 300 * 1024,
|
|
12
|
+
/** GC 주기 (10초) */
|
|
13
|
+
GC_INTERVAL: 10 * 1000,
|
|
14
|
+
/** 미완성 메시지 만료 시간 (60초) */
|
|
15
|
+
EXPIRE_TIME: 60 * 1000,
|
|
12
16
|
};
|
|
13
|
-
|
|
14
|
-
PROTOCOL_CONFIG
|
|
15
|
-
};
|
|
16
|
-
//# sourceMappingURL=protocol.types.js.map
|
|
17
|
+
//# sourceMappingURL=protocol.types.js.map
|
|
@@ -1,6 +1 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/protocol/protocol.types.ts"],
|
|
4
|
-
"mappings": "AAKO,MAAM,kBAAkB;AAAA;AAAA,EAE7B,gBAAgB,MAAM,OAAO;AAAA;AAAA,EAE7B,oBAAoB,IAAI,OAAO;AAAA;AAAA,EAE/B,YAAY,MAAM;AAAA;AAAA,EAElB,aAAa,KAAK;AAAA;AAAA,EAElB,aAAa,KAAK;AACpB;",
|
|
5
|
-
"names": []
|
|
6
|
-
}
|
|
1
|
+
{"version":3,"file":"protocol.types.js","sourceRoot":"","sources":["..\\..\\src\\protocol\\protocol.types.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,UAAU;AACV,yEAAyE;AAEzE,kBAAkB;AAClB,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,wBAAwB;IACxB,cAAc,EAAE,GAAG,GAAG,IAAI,GAAG,IAAI;IACjC,mBAAmB;IACnB,kBAAkB,EAAE,CAAC,GAAG,IAAI,GAAG,IAAI;IACnC,oBAAoB;IACpB,UAAU,EAAE,GAAG,GAAG,IAAI;IACtB,kBAAkB;IAClB,WAAW,EAAE,EAAE,GAAG,IAAI;IACtB,0BAA0B;IAC1B,WAAW,EAAE,EAAE,GAAG,IAAI;CACd,CAAC"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 자동 업데이트 서비스 인터페이스
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 클라이언트 애플리케이션의 최신 버전 정보를 조회한다.
|
|
5
5
|
*/
|
|
6
6
|
export interface AutoUpdateService {
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* @param platform
|
|
10
|
-
* @returns
|
|
8
|
+
* 지정된 플랫폼의 최신 버전 정보를 조회한다.
|
|
9
|
+
* @param platform 대상 플랫폼 (예: "win32", "darwin", "linux")
|
|
10
|
+
* @returns 최신 버전 정보, 버전이 없으면 undefined
|
|
11
11
|
*/
|
|
12
12
|
getLastVersion(platform: string): Promise<{
|
|
13
13
|
version: string;
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
export {};
|
|
2
|
+
//# sourceMappingURL=auto-update-service.types.js.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { Dialect, IsolationLevel, QueryDef, ColumnMeta, ResultMeta } from "@simplysm/orm-common";
|
|
2
2
|
/**
|
|
3
|
-
* ORM
|
|
3
|
+
* ORM 서비스 인터페이스
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* 데이터베이스 연결, 트랜잭션 관리, 쿼리 실행을 제공한다.
|
|
6
|
+
* MySQL, MSSQL, PostgreSQL을 지원한다.
|
|
7
7
|
*/
|
|
8
8
|
export interface OrmService {
|
|
9
9
|
getInfo(opt: DbConnOptions & {
|
|
@@ -13,7 +13,9 @@ export interface OrmService {
|
|
|
13
13
|
database?: string;
|
|
14
14
|
schema?: string;
|
|
15
15
|
}>;
|
|
16
|
-
connect(opt:
|
|
16
|
+
connect(opt: DbConnOptions & {
|
|
17
|
+
configName: string;
|
|
18
|
+
}): Promise<number>;
|
|
17
19
|
close(connId: number): Promise<void>;
|
|
18
20
|
beginTransaction(connId: number, isolationLevel?: IsolationLevel): Promise<void>;
|
|
19
21
|
commitTransaction(connId: number): Promise<void>;
|
|
@@ -22,7 +24,7 @@ export interface OrmService {
|
|
|
22
24
|
executeDefs(connId: number, defs: QueryDef[], options?: (ResultMeta | undefined)[]): Promise<unknown[][]>;
|
|
23
25
|
bulkInsert(connId: number, tableName: string, columnDefs: Record<string, ColumnMeta>, records: Record<string, unknown>[]): Promise<void>;
|
|
24
26
|
}
|
|
25
|
-
/**
|
|
27
|
+
/** 데이터베이스 연결 옵션 */
|
|
26
28
|
export type DbConnOptions = {
|
|
27
29
|
configName?: string;
|
|
28
30
|
config?: Record<string, unknown>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orm-service.types.d.ts","sourceRoot":"","sources":["..\\..\\src\\service-types\\orm-service.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,QAAQ,EACR,UAAU,EACV,UAAU,EACX,MAAM,sBAAsB,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,GAAG,EAAE,aAAa,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAC5D,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"orm-service.types.d.ts","sourceRoot":"","sources":["..\\..\\src\\service-types\\orm-service.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,OAAO,EACP,cAAc,EACd,QAAQ,EACR,UAAU,EACV,UAAU,EACX,MAAM,sBAAsB,CAAC;AAE9B;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,GAAG,EAAE,aAAa,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;QAC5D,OAAO,EAAE,OAAO,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IAEH,OAAO,CAAC,GAAG,EAAE,aAAa,GAAG;QAAE,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEtE,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAErC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjF,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjD,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnD,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAE7F,WAAW,CACT,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,QAAQ,EAAE,EAChB,OAAO,CAAC,EAAE,CAAC,UAAU,GAAG,SAAS,CAAC,EAAE,GACnC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAExB,UAAU,CACR,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,EACtC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB;AAED,mBAAmB;AACnB,MAAM,MAAM,aAAa,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC"}
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
export {};
|
|
2
|
+
//# sourceMappingURL=orm-service.types.js.map
|
package/dist/types.d.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 파일 업로드 결과
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 서버에 업로드된 파일의 정보를 포함한다.
|
|
5
5
|
*/
|
|
6
6
|
export interface ServiceUploadResult {
|
|
7
|
-
/**
|
|
7
|
+
/** 서버 내 저장 경로 */
|
|
8
8
|
path: string;
|
|
9
|
-
/**
|
|
9
|
+
/** 원본 파일명 */
|
|
10
10
|
filename: string;
|
|
11
|
-
/**
|
|
11
|
+
/** 파일 크기 (바이트) */
|
|
12
12
|
size: number;
|
|
13
13
|
}
|
|
14
14
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["..\\src\\types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["..\\src\\types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IAClC,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,kBAAkB;IAClB,IAAI,EAAE,MAAM,CAAC;CACd"}
|
package/dist/types.js
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
export {};
|
|
2
|
+
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/service-common",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
5
|
-
"author": "
|
|
3
|
+
"version": "14.0.1",
|
|
4
|
+
"description": "심플리즘 패키지 - 서비스 (common)",
|
|
5
|
+
"author": "심플리즘",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -14,13 +14,11 @@
|
|
|
14
14
|
"types": "./dist/index.d.ts",
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
-
"
|
|
18
|
-
"src",
|
|
19
|
-
"tests"
|
|
17
|
+
"src"
|
|
20
18
|
],
|
|
21
19
|
"sideEffects": false,
|
|
22
20
|
"dependencies": {
|
|
23
|
-
"@simplysm/core-common": "
|
|
24
|
-
"@simplysm/orm-common": "
|
|
21
|
+
"@simplysm/core-common": "14.0.1",
|
|
22
|
+
"@simplysm/orm-common": "14.0.1"
|
|
25
23
|
}
|
|
26
24
|
}
|
package/src/define-event.ts
CHANGED
|
@@ -1,25 +1,25 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* $info
|
|
2
|
+
* defineEvent()로 생성된 이벤트 정의.
|
|
3
|
+
* $info와 $data는 타입 전용 마커임 (런타임에서는 사용되지 않음).
|
|
4
4
|
*/
|
|
5
5
|
export interface ServiceEventDef<TInfo = unknown, TData = unknown> {
|
|
6
6
|
eventName: string;
|
|
7
|
-
/**
|
|
7
|
+
/** 타입 추출 전용 (런타임에서는 사용되지 않음) */
|
|
8
8
|
readonly $info: TInfo;
|
|
9
|
-
/**
|
|
9
|
+
/** 타입 추출 전용 (런타임에서는 사용되지 않음) */
|
|
10
10
|
readonly $data: TData;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
*
|
|
14
|
+
* 타입 안전한 info와 data를 가진 서비스 이벤트를 정의한다.
|
|
15
15
|
*
|
|
16
16
|
* @example
|
|
17
17
|
* const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
|
|
18
18
|
*
|
|
19
|
-
* //
|
|
19
|
+
* // 서버에서 이벤트 발생
|
|
20
20
|
* ctx.socket?.emitEvent(OrderUpdated, { orderId: 123 }, { status: "shipped" });
|
|
21
21
|
*
|
|
22
|
-
* //
|
|
22
|
+
* // 클라이언트에서 구독
|
|
23
23
|
* await client.addEventListener(OrderUpdated, { orderId: 123 }, (data) => {
|
|
24
24
|
* console.log(data.status); // typed
|
|
25
25
|
* });
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
|
-
//
|
|
1
|
+
// 프로토콜
|
|
2
2
|
export * from "./protocol/protocol.types";
|
|
3
3
|
export * from "./protocol/create-service-protocol";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// 서비스 타입
|
|
6
6
|
export * from "./service-types/orm-service.types";
|
|
7
7
|
export * from "./service-types/auto-update-service.types";
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
// Types
|
|
8
|
+
// 타입
|
|
11
9
|
export * from "./types";
|
|
12
10
|
|
|
13
|
-
//
|
|
11
|
+
// 정의
|
|
14
12
|
export * from "./define-event";
|
|
@@ -10,56 +10,56 @@ import {
|
|
|
10
10
|
import { PROTOCOL_CONFIG, type ServiceMessage } from "./protocol.types";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* 서비스 프로토콜 인터페이스
|
|
14
14
|
*
|
|
15
|
-
*
|
|
16
|
-
* -
|
|
17
|
-
* -
|
|
18
|
-
* -
|
|
19
|
-
* -
|
|
15
|
+
* 바이너리 프로토콜 V2:
|
|
16
|
+
* - 헤더: 28바이트 (UUID 16 + TotalSize 8 + Index 4)
|
|
17
|
+
* - 본문: JSON
|
|
18
|
+
* - 자동 청킹: 3MB 초과 시 300KB 청크로 분할
|
|
19
|
+
* - 최대 메시지 크기: 100MB
|
|
20
20
|
*/
|
|
21
21
|
export interface ServiceProtocol {
|
|
22
22
|
/**
|
|
23
|
-
*
|
|
23
|
+
* 메시지를 인코딩한다 (필요 시 자동 분할)
|
|
24
24
|
*/
|
|
25
25
|
encode(uuid: string, message: ServiceMessage): { chunks: Bytes[]; totalSize: number };
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
|
-
*
|
|
28
|
+
* 메시지를 디코딩한다 (청크 패킷 자동 재조립)
|
|
29
29
|
*/
|
|
30
30
|
decode<T extends ServiceMessage>(bytes: Bytes): ServiceMessageDecodeResult<T>;
|
|
31
31
|
|
|
32
32
|
/**
|
|
33
|
-
*
|
|
33
|
+
* 프로토콜 인스턴스를 해제한다.
|
|
34
34
|
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
35
|
+
* 내부 청크 누적기의 GC 타이머를 해제하고 메모리를 반환한다.
|
|
36
|
+
* 프로토콜 인스턴스가 더 이상 필요하지 않을 때 반드시 호출해야 한다.
|
|
37
37
|
*/
|
|
38
38
|
dispose(): void;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
42
|
+
* 메시지 디코딩 결과 타입 (유니언)
|
|
43
43
|
*
|
|
44
|
-
* - `type: "complete"`:
|
|
45
|
-
* - `type: "progress"`:
|
|
44
|
+
* - `type: "complete"`: 모든 청크를 수신하여 메시지 재조립이 완료됨
|
|
45
|
+
* - `type: "progress"`: 청크 메시지 진행 중 (일부 청크만 도착)
|
|
46
46
|
*/
|
|
47
47
|
export type ServiceMessageDecodeResult<TMessage extends ServiceMessage> =
|
|
48
48
|
| { type: "complete"; uuid: string; message: TMessage }
|
|
49
49
|
| { type: "progress"; uuid: string; totalSize: number; completedSize: number };
|
|
50
50
|
|
|
51
51
|
/**
|
|
52
|
-
*
|
|
52
|
+
* 서비스 프로토콜 인코더/디코더를 생성한다
|
|
53
53
|
*
|
|
54
|
-
*
|
|
55
|
-
* -
|
|
56
|
-
* -
|
|
57
|
-
* -
|
|
58
|
-
* -
|
|
54
|
+
* 바이너리 프로토콜 V2:
|
|
55
|
+
* - 헤더: 28바이트 (UUID 16 + TotalSize 8 + Index 4)
|
|
56
|
+
* - 본문: JSON
|
|
57
|
+
* - 자동 청킹: 3MB 초과 시 300KB 청크로 분할
|
|
58
|
+
* - 최대 메시지 크기: 100MB
|
|
59
59
|
*/
|
|
60
60
|
export function createServiceProtocol(): ServiceProtocol {
|
|
61
61
|
// -------------------------------------------------------------------
|
|
62
|
-
//
|
|
62
|
+
// 상태
|
|
63
63
|
// -------------------------------------------------------------------
|
|
64
64
|
|
|
65
65
|
const accumulator = new LazyGcMap<
|
|
@@ -75,17 +75,17 @@ export function createServiceProtocol(): ServiceProtocol {
|
|
|
75
75
|
});
|
|
76
76
|
|
|
77
77
|
// -------------------------------------------------------------------
|
|
78
|
-
//
|
|
78
|
+
// 인코딩 헬퍼
|
|
79
79
|
// -------------------------------------------------------------------
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
|
-
*
|
|
82
|
+
* 메시지 청크를 인코딩한다 (헤더 + 본문)
|
|
83
83
|
*
|
|
84
|
-
*
|
|
84
|
+
* 헤더 구조 (28바이트, Big Endian):
|
|
85
85
|
* ```
|
|
86
86
|
* Offset Size Field
|
|
87
87
|
* ------ ---- -----
|
|
88
|
-
* 0 16 UUID (
|
|
88
|
+
* 0 16 UUID (바이너리)
|
|
89
89
|
* 16 8 TotalSize (uint64)
|
|
90
90
|
* 24 4 Index (uint32)
|
|
91
91
|
* ```
|
|
@@ -117,7 +117,7 @@ export function createServiceProtocol(): ServiceProtocol {
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
// -------------------------------------------------------------------
|
|
120
|
-
//
|
|
120
|
+
// 공개 API
|
|
121
121
|
// -------------------------------------------------------------------
|
|
122
122
|
|
|
123
123
|
return {
|
|
@@ -127,20 +127,20 @@ export function createServiceProtocol(): ServiceProtocol {
|
|
|
127
127
|
|
|
128
128
|
const totalSize = msgBytes.length;
|
|
129
129
|
|
|
130
|
-
//
|
|
130
|
+
// 전체 크기 제한 확인 (우선 수행)
|
|
131
131
|
if (totalSize > PROTOCOL_CONFIG.MAX_TOTAL_SIZE) {
|
|
132
|
-
throw new ArgumentError("
|
|
132
|
+
throw new ArgumentError("메시지 크기가 제한을 초과했습니다.", {
|
|
133
133
|
totalSize,
|
|
134
134
|
maxSize: PROTOCOL_CONFIG.MAX_TOTAL_SIZE,
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
//
|
|
138
|
+
// 충분히 작으면 그대로 반환
|
|
139
139
|
if (totalSize <= PROTOCOL_CONFIG.SPLIT_MESSAGE_SIZE) {
|
|
140
140
|
return { chunks: [encodeChunk({ uuid, totalSize, index: 0 }, msgBytes)], totalSize };
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
//
|
|
143
|
+
// 청크로 분할
|
|
144
144
|
const chunks: Bytes[] = [];
|
|
145
145
|
let offset = 0;
|
|
146
146
|
let index = 0;
|
|
@@ -160,13 +160,13 @@ export function createServiceProtocol(): ServiceProtocol {
|
|
|
160
160
|
|
|
161
161
|
decode<T extends ServiceMessage>(bytes: Bytes): ServiceMessageDecodeResult<T> {
|
|
162
162
|
if (bytes.length < 28) {
|
|
163
|
-
throw new ArgumentError("
|
|
163
|
+
throw new ArgumentError("버퍼 크기가 헤더 크기보다 작습니다.", {
|
|
164
164
|
bufferSize: bytes.length,
|
|
165
165
|
minimumSize: 28,
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
-
// 1.
|
|
169
|
+
// 1. 헤더 읽기
|
|
170
170
|
|
|
171
171
|
// UUID
|
|
172
172
|
const uuidBytes = bytes.subarray(0, 16);
|
|
@@ -177,9 +177,9 @@ export function createServiceProtocol(): ServiceProtocol {
|
|
|
177
177
|
const totalSize = Number(headerView.getBigUint64(16, false));
|
|
178
178
|
const index = headerView.getUint32(24, false);
|
|
179
179
|
|
|
180
|
-
//
|
|
180
|
+
// 전체 크기 제한 확인 (우선 수행)
|
|
181
181
|
if (totalSize > PROTOCOL_CONFIG.MAX_TOTAL_SIZE) {
|
|
182
|
-
throw new ArgumentError("
|
|
182
|
+
throw new ArgumentError("메시지 크기가 제한을 초과했습니다.", {
|
|
183
183
|
totalSize,
|
|
184
184
|
maxSize: PROTOCOL_CONFIG.MAX_TOTAL_SIZE,
|
|
185
185
|
});
|
|
@@ -193,7 +193,7 @@ export function createServiceProtocol(): ServiceProtocol {
|
|
|
193
193
|
chunks: [],
|
|
194
194
|
}));
|
|
195
195
|
if (accItem.chunks[index] == null) {
|
|
196
|
-
//
|
|
196
|
+
// 중복 패킷 방어
|
|
197
197
|
accItem.chunks[index] = bodyBytes;
|
|
198
198
|
accItem.completedSize += bodyBytes.length;
|
|
199
199
|
}
|
|
@@ -205,15 +205,15 @@ export function createServiceProtocol(): ServiceProtocol {
|
|
|
205
205
|
totalSize: totalSize,
|
|
206
206
|
completedSize: accItem.completedSize,
|
|
207
207
|
};
|
|
208
|
-
} else {
|
|
209
|
-
accumulator.delete(uuid); //
|
|
208
|
+
} else if (accItem.completedSize === accItem.totalSize) {
|
|
209
|
+
accumulator.delete(uuid); // 메모리 해제
|
|
210
210
|
|
|
211
211
|
const resultBytes = bytesU.concat(accItem.chunks.filterExists());
|
|
212
212
|
let messageArr: [string, unknown];
|
|
213
213
|
try {
|
|
214
214
|
messageArr = json.parse<[string, unknown]>(new TextDecoder().decode(resultBytes));
|
|
215
215
|
} catch (err) {
|
|
216
|
-
throw new ArgumentError("
|
|
216
|
+
throw new ArgumentError("메시지 디코딩에 실패했습니다.", { uuid, cause: err });
|
|
217
217
|
}
|
|
218
218
|
return {
|
|
219
219
|
type: "complete",
|
|
@@ -223,6 +223,13 @@ export function createServiceProtocol(): ServiceProtocol {
|
|
|
223
223
|
body: messageArr[1],
|
|
224
224
|
} as T,
|
|
225
225
|
};
|
|
226
|
+
} else {
|
|
227
|
+
accumulator.delete(uuid);
|
|
228
|
+
throw new ArgumentError("프로토콜 무결성 위반: completedSize가 totalSize를 초과했습니다.", {
|
|
229
|
+
uuid,
|
|
230
|
+
completedSize: accItem.completedSize,
|
|
231
|
+
totalSize: accItem.totalSize,
|
|
232
|
+
});
|
|
226
233
|
}
|
|
227
234
|
},
|
|
228
235
|
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
// ----------------------------------------------------------------------
|
|
2
|
-
//
|
|
2
|
+
// 프로토콜 상수
|
|
3
3
|
// ----------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
/**
|
|
5
|
+
/** 서비스 프로토콜 설정 */
|
|
6
6
|
export const PROTOCOL_CONFIG = {
|
|
7
|
-
/**
|
|
7
|
+
/** 최대 메시지 크기 (100MB) */
|
|
8
8
|
MAX_TOTAL_SIZE: 100 * 1024 * 1024,
|
|
9
|
-
/**
|
|
9
|
+
/** 청킹 임계값 (3MB) */
|
|
10
10
|
SPLIT_MESSAGE_SIZE: 3 * 1024 * 1024,
|
|
11
|
-
/**
|
|
11
|
+
/** 청크 크기 (300KB) */
|
|
12
12
|
CHUNK_SIZE: 300 * 1024,
|
|
13
|
-
/** GC
|
|
13
|
+
/** GC 주기 (10초) */
|
|
14
14
|
GC_INTERVAL: 10 * 1000,
|
|
15
|
-
/**
|
|
15
|
+
/** 미완성 메시지 만료 시간 (60초) */
|
|
16
16
|
EXPIRE_TIME: 60 * 1000,
|
|
17
17
|
} as const;
|
|
18
18
|
|
|
19
19
|
// ----------------------------------------------------------------------
|
|
20
|
-
//
|
|
20
|
+
// 메시지 타입
|
|
21
21
|
// ----------------------------------------------------------------------
|
|
22
22
|
|
|
23
23
|
export type ServiceMessage =
|
|
@@ -34,10 +34,10 @@ export type ServiceMessage =
|
|
|
34
34
|
| ServiceEventMessage;
|
|
35
35
|
|
|
36
36
|
export type ServiceServerMessage =
|
|
37
|
-
| ServiceReloadMessage //
|
|
37
|
+
| ServiceReloadMessage // 알림
|
|
38
38
|
| ServiceResponseMessage
|
|
39
39
|
| ServiceErrorMessage
|
|
40
|
-
| ServiceEventMessage; //
|
|
40
|
+
| ServiceEventMessage; // 알림
|
|
41
41
|
|
|
42
42
|
export type ServiceServerRawMessage = ServiceProgressMessage | ServiceServerMessage;
|
|
43
43
|
|
|
@@ -50,28 +50,28 @@ export type ServiceClientMessage =
|
|
|
50
50
|
| ServiceEmitEventMessage;
|
|
51
51
|
|
|
52
52
|
// ----------------------------------------------------------------------
|
|
53
|
-
//
|
|
53
|
+
// 시스템 (공통)
|
|
54
54
|
// ----------------------------------------------------------------------
|
|
55
55
|
|
|
56
|
-
/**
|
|
56
|
+
/** 서버: 클라이언트에 리로드 명령 */
|
|
57
57
|
export interface ServiceReloadMessage {
|
|
58
58
|
name: "reload";
|
|
59
59
|
body: {
|
|
60
|
-
clientName: string | undefined; //
|
|
61
|
-
changedFileSet: Set<string>; //
|
|
60
|
+
clientName: string | undefined; // 클라이언트 이름
|
|
61
|
+
changedFileSet: Set<string>; // 변경된 파일 목록
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
/**
|
|
65
|
+
/** 서버: 수신된 청크 메시지의 진행 상태 알림 */
|
|
66
66
|
export interface ServiceProgressMessage {
|
|
67
67
|
name: "progress";
|
|
68
68
|
body: {
|
|
69
|
-
totalSize: number; //
|
|
70
|
-
completedSize: number; //
|
|
69
|
+
totalSize: number; // 전체 크기 (바이트)
|
|
70
|
+
completedSize: number; // 완료된 크기 (바이트)
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
/**
|
|
74
|
+
/** 서버: 에러 알림 */
|
|
75
75
|
export interface ServiceErrorMessage {
|
|
76
76
|
name: "error";
|
|
77
77
|
body: {
|
|
@@ -84,72 +84,72 @@ export interface ServiceErrorMessage {
|
|
|
84
84
|
};
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
-
/**
|
|
87
|
+
/** 클라이언트: 인증 메시지 */
|
|
88
88
|
export interface ServiceAuthMessage {
|
|
89
89
|
name: "auth";
|
|
90
|
-
body: string; //
|
|
90
|
+
body: string; // 토큰
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
// ----------------------------------------------------------------------
|
|
94
94
|
// Service.Method
|
|
95
95
|
// ----------------------------------------------------------------------
|
|
96
96
|
|
|
97
|
-
/**
|
|
97
|
+
/** 클라이언트: 서비스 메서드 요청 */
|
|
98
98
|
export interface ServiceRequestMessage {
|
|
99
99
|
name: `${string}.${string}`; // ${service}.${method}
|
|
100
|
-
body: unknown[]; //
|
|
100
|
+
body: unknown[]; // 매개변수
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/**
|
|
103
|
+
/** 서버: 서비스 메서드 응답 */
|
|
104
104
|
export interface ServiceResponseMessage {
|
|
105
105
|
name: "response";
|
|
106
|
-
body?: unknown; //
|
|
106
|
+
body?: unknown; // 결과
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// ----------------------------------------------------------------------
|
|
110
|
-
//
|
|
110
|
+
// 이벤트
|
|
111
111
|
// ----------------------------------------------------------------------
|
|
112
112
|
|
|
113
|
-
/**
|
|
113
|
+
/** 클라이언트: 이벤트 리스너 추가 */
|
|
114
114
|
export interface ServiceAddEventListenerMessage {
|
|
115
115
|
name: "evt:add";
|
|
116
116
|
body: {
|
|
117
|
-
key: string; //
|
|
118
|
-
name: string; //
|
|
119
|
-
info: unknown; //
|
|
117
|
+
key: string; // 리스너 키 (uuid) - removeEventListener에 필요
|
|
118
|
+
name: string; // 이벤트 이름 (Type.name)
|
|
119
|
+
info: unknown; // 이벤트 발생 시 필터링을 위한 추가 리스너 정보
|
|
120
120
|
};
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
/**
|
|
123
|
+
/** 클라이언트: 이벤트 리스너 제거 */
|
|
124
124
|
export interface ServiceRemoveEventListenerMessage {
|
|
125
125
|
name: "evt:remove";
|
|
126
126
|
body: {
|
|
127
|
-
key: string; //
|
|
127
|
+
key: string; // 리스너 키 (uuid)
|
|
128
128
|
};
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
-
/**
|
|
131
|
+
/** 클라이언트: 이벤트 리스너 정보 목록 요청 */
|
|
132
132
|
export interface ServiceGetEventListenerInfosMessage {
|
|
133
133
|
name: "evt:gets";
|
|
134
134
|
body: {
|
|
135
|
-
name: string; //
|
|
135
|
+
name: string; // 이벤트 이름
|
|
136
136
|
};
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
/**
|
|
139
|
+
/** 클라이언트: 이벤트 발생 */
|
|
140
140
|
export interface ServiceEmitEventMessage {
|
|
141
141
|
name: "evt:emit";
|
|
142
142
|
body: {
|
|
143
|
-
keys: string[]; //
|
|
144
|
-
data: unknown; //
|
|
143
|
+
keys: string[]; // 리스너 키 목록
|
|
144
|
+
data: unknown; // 데이터
|
|
145
145
|
};
|
|
146
146
|
}
|
|
147
147
|
|
|
148
|
-
/**
|
|
148
|
+
/** 서버: 이벤트 알림 */
|
|
149
149
|
export interface ServiceEventMessage {
|
|
150
150
|
name: "evt:on";
|
|
151
151
|
body: {
|
|
152
|
-
keys: string[]; //
|
|
153
|
-
data: unknown; //
|
|
152
|
+
keys: string[]; // 리스너 키 목록
|
|
153
|
+
data: unknown; // 데이터
|
|
154
154
|
};
|
|
155
155
|
}
|