@simplysm/service-server 13.0.100 → 14.0.4

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 (88) hide show
  1. package/README.md +79 -133
  2. package/dist/auth/auth-token-payload.js +2 -1
  3. package/dist/auth/auth-token-payload.js.map +1 -6
  4. package/dist/auth/jwt-manager.js +21 -21
  5. package/dist/auth/jwt-manager.js.map +1 -6
  6. package/dist/core/define-service.d.ts +12 -12
  7. package/dist/core/define-service.d.ts.map +1 -1
  8. package/dist/core/define-service.js +77 -63
  9. package/dist/core/define-service.js.map +1 -6
  10. package/dist/core/service-executor.d.ts.map +1 -1
  11. package/dist/core/service-executor.js +42 -32
  12. package/dist/core/service-executor.js.map +1 -6
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +11 -2
  16. package/dist/index.js.map +1 -6
  17. package/dist/legacy/v1-auto-update-handler.d.ts +2 -2
  18. package/dist/legacy/v1-auto-update-handler.js +42 -35
  19. package/dist/legacy/v1-auto-update-handler.js.map +1 -6
  20. package/dist/protocol/protocol-wrapper.d.ts +9 -9
  21. package/dist/protocol/protocol-wrapper.js +64 -46
  22. package/dist/protocol/protocol-wrapper.js.map +1 -6
  23. package/dist/service-server.d.ts +2 -1
  24. package/dist/service-server.d.ts.map +1 -1
  25. package/dist/service-server.js +183 -165
  26. package/dist/service-server.js.map +1 -6
  27. package/dist/services/auto-update-service.js +35 -34
  28. package/dist/services/auto-update-service.js.map +1 -6
  29. package/dist/services/orm-service.js +114 -120
  30. package/dist/services/orm-service.js.map +1 -6
  31. package/dist/transport/http/http-request-handler.d.ts.map +1 -1
  32. package/dist/transport/http/http-request-handler.js +58 -46
  33. package/dist/transport/http/http-request-handler.js.map +1 -6
  34. package/dist/transport/http/static-file-handler.js +42 -39
  35. package/dist/transport/http/static-file-handler.js.map +1 -6
  36. package/dist/transport/http/upload-handler.d.ts.map +1 -1
  37. package/dist/transport/http/upload-handler.js +60 -55
  38. package/dist/transport/http/upload-handler.js.map +1 -6
  39. package/dist/transport/socket/service-socket.d.ts +13 -13
  40. package/dist/transport/socket/service-socket.js +132 -108
  41. package/dist/transport/socket/service-socket.js.map +1 -6
  42. package/dist/transport/socket/websocket-handler.d.ts +9 -13
  43. package/dist/transport/socket/websocket-handler.d.ts.map +1 -1
  44. package/dist/transport/socket/websocket-handler.js +148 -139
  45. package/dist/transport/socket/websocket-handler.js.map +1 -6
  46. package/dist/types/server-options.d.ts +1 -1
  47. package/dist/types/server-options.d.ts.map +1 -1
  48. package/dist/types/server-options.js +2 -1
  49. package/dist/types/server-options.js.map +1 -6
  50. package/dist/utils/config-manager.js +48 -46
  51. package/dist/utils/config-manager.js.map +1 -6
  52. package/dist/workers/service-protocol.worker.js +8 -11
  53. package/dist/workers/service-protocol.worker.js.map +1 -6
  54. package/docs/auth.md +28 -16
  55. package/docs/core.md +113 -54
  56. package/docs/legacy.md +21 -0
  57. package/docs/main.md +81 -0
  58. package/docs/protocol.md +31 -0
  59. package/docs/services.md +49 -44
  60. package/docs/transport.md +81 -76
  61. package/docs/types.md +31 -0
  62. package/docs/utilities.md +27 -0
  63. package/package.json +12 -13
  64. package/src/auth/jwt-manager.ts +2 -2
  65. package/src/core/define-service.ts +19 -19
  66. package/src/core/service-executor.ts +23 -17
  67. package/src/index.ts +10 -12
  68. package/src/legacy/v1-auto-update-handler.ts +10 -10
  69. package/src/protocol/protocol-wrapper.ts +16 -16
  70. package/src/service-server.ts +51 -43
  71. package/src/services/auto-update-service.ts +1 -1
  72. package/src/services/orm-service.ts +7 -7
  73. package/src/transport/http/http-request-handler.ts +16 -10
  74. package/src/transport/http/static-file-handler.ts +8 -8
  75. package/src/transport/http/upload-handler.ts +16 -9
  76. package/src/transport/socket/service-socket.ts +22 -22
  77. package/src/transport/socket/websocket-handler.ts +50 -70
  78. package/src/types/server-options.ts +1 -1
  79. package/src/utils/config-manager.ts +11 -11
  80. package/dist/services/smtp-client-service.d.ts +0 -8
  81. package/dist/services/smtp-client-service.d.ts.map +0 -1
  82. package/dist/services/smtp-client-service.js +0 -46
  83. package/dist/services/smtp-client-service.js.map +0 -6
  84. package/docs/server.md +0 -126
  85. package/src/services/smtp-client-service.ts +0 -59
  86. package/tests/define-service.spec.ts +0 -66
  87. package/tests/orm-service.spec.ts +0 -83
  88. package/tests/service-executor.spec.ts +0 -114
@@ -1,40 +1,50 @@
1
1
  import { createServiceContext, getServiceAuthPermissions } from "./define-service.js";
2
- async function executeServiceMethod(server, def) {
3
- const serviceDef = server.options.services.find((item) => item.name === def.serviceName);
4
- if (serviceDef == null) {
5
- throw new Error(`Service [${def.serviceName}] not found.`);
6
- }
7
- const clientName = def.socket?.clientName ?? def.http?.clientName;
8
- if (clientName != null) {
9
- if (clientName.includes("..") || clientName.includes("/") || clientName.includes("\\")) {
10
- throw new Error(`[Security] Invalid client name: ${clientName}`);
2
+ export async function executeServiceMethod(server, def) {
3
+ // 서비스 정의 검색
4
+ const serviceDef = server.options.services.find((item) => item.name === def.serviceName);
5
+ if (serviceDef == null) {
6
+ throw new Error(`서비스 [${def.serviceName}]를 찾을 수 없습니다.`);
11
7
  }
12
- }
13
- const ctx = createServiceContext(server, def.socket, def.http);
14
- const methods = serviceDef.factory(ctx);
15
- const method = methods[def.methodName];
16
- if (typeof method !== "function") {
17
- throw new Error(`Method [${def.serviceName}.${def.methodName}] not found.`);
18
- }
19
- if (server.options.auth != null) {
8
+ // 요청 유효성 검증 (게이트키퍼)
9
+ const clientName = def.socket?.clientName ?? def.http?.clientName;
10
+ if (clientName != null) {
11
+ if (clientName.includes("..") || clientName.includes("/") || clientName.includes("\\")) {
12
+ throw new Error(`[보안] 유효하지 않은 클라이언트 이름: ${clientName}`);
13
+ }
14
+ }
15
+ // 컨텍스트 생성
16
+ const ctx = createServiceContext(server, def.socket, def.http);
17
+ // 팩토리를 호출하여 메서드 객체 생성
18
+ const methods = serviceDef.factory(ctx);
19
+ // 메서드 검색
20
+ const method = methods[def.methodName];
21
+ if (typeof method !== "function") {
22
+ throw new Error(`메서드 [${def.serviceName}.${def.methodName}]를 찾을 수 없습니다.`);
23
+ }
24
+ // 인증 확인
20
25
  const methodPerms = getServiceAuthPermissions(method);
21
26
  const requiredPerms = methodPerms ?? serviceDef.authPermissions;
22
27
  if (requiredPerms != null) {
23
- const authTokenPayload = def.socket?.authTokenPayload ?? def.http?.authTokenPayload;
24
- if (authTokenPayload == null) {
25
- throw new Error("Login is required.");
26
- }
27
- if (requiredPerms.length > 0) {
28
- const hasPerm = requiredPerms.some((perm) => authTokenPayload.roles.includes(perm));
29
- if (!hasPerm) {
30
- throw new Error("Insufficient permissions.");
28
+ if (server.options.auth === undefined) {
29
+ // auth 설정 누락 — 설정 오류
30
+ throw new Error("auth 설정이 필요합니다. auth 서비스를 사용하려면 서버 옵션에 auth를 설정하세요.");
31
+ }
32
+ if (server.options.auth !== false) {
33
+ // auth가 설정되어 있으면 인증 검사 수행
34
+ const authTokenPayload = def.socket?.authTokenPayload ?? def.http?.authTokenPayload;
35
+ if (authTokenPayload == null) {
36
+ throw new Error("로그인이 필요합니다.");
37
+ }
38
+ if (requiredPerms.length > 0) {
39
+ const hasPerm = requiredPerms.some((perm) => authTokenPayload.roles.includes(perm));
40
+ if (!hasPerm) {
41
+ throw new Error("권한이 부족합니다.");
42
+ }
43
+ }
31
44
  }
32
- }
45
+ // auth === false → 의도적 비활성화, 인증 스킵
33
46
  }
34
- }
35
- return await method(...def.params);
47
+ // 실행
48
+ return await method(...def.params);
36
49
  }
37
- export {
38
- executeServiceMethod
39
- };
40
- //# sourceMappingURL=service-executor.js.map
50
+ //# sourceMappingURL=service-executor.js.map
@@ -1,6 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/core/service-executor.ts"],
4
- "mappings": "AAGA,SAAS,sBAAsB,iCAAiC;AAEhE,eAAsB,qBACpB,QACA,KAOkB;AAElB,QAAM,aAAa,OAAO,QAAQ,SAAS,KAAK,CAAC,SAAS,KAAK,SAAS,IAAI,WAAW;AAEvF,MAAI,cAAc,MAAM;AACtB,UAAM,IAAI,MAAM,YAAY,IAAI,WAAW,cAAc;AAAA,EAC3D;AAGA,QAAM,aAAa,IAAI,QAAQ,cAAc,IAAI,MAAM;AACvD,MAAI,cAAc,MAAM;AACtB,QAAI,WAAW,SAAS,IAAI,KAAK,WAAW,SAAS,GAAG,KAAK,WAAW,SAAS,IAAI,GAAG;AACtF,YAAM,IAAI,MAAM,mCAAmC,UAAU,EAAE;AAAA,IACjE;AAAA,EACF;AAGA,QAAM,MAAM,qBAAqB,QAAQ,IAAI,QAAQ,IAAI,IAAI;AAG7D,QAAM,UAAU,WAAW,QAAQ,GAAG;AAGtC,QAAM,SAAU,QAAoC,IAAI,UAAU;AAClE,MAAI,OAAO,WAAW,YAAY;AAChC,UAAM,IAAI,MAAM,WAAW,IAAI,WAAW,IAAI,IAAI,UAAU,cAAc;AAAA,EAC5E;AAGA,MAAI,OAAO,QAAQ,QAAQ,MAAM;AAE/B,UAAM,cAAc,0BAA0B,MAAM;AACpD,UAAM,gBAAgB,eAAe,WAAW;AAEhD,QAAI,iBAAiB,MAAM;AACzB,YAAM,mBAAmB,IAAI,QAAQ,oBAAoB,IAAI,MAAM;AAEnE,UAAI,oBAAoB,MAAM;AAC5B,cAAM,IAAI,MAAM,oBAAoB;AAAA,MACtC;AAEA,UAAI,cAAc,SAAS,GAAG;AAC5B,cAAM,UAAU,cAAc,KAAK,CAAC,SAAS,iBAAiB,MAAM,SAAS,IAAI,CAAC;AAClF,YAAI,CAAC,SAAS;AACZ,gBAAM,IAAI,MAAM,2BAA2B;AAAA,QAC7C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO,MAAM,OAAO,GAAG,IAAI,MAAM;AACnC;",
5
- "names": []
6
- }
1
+ {"version":3,"file":"service-executor.js","sourceRoot":"","sources":["..\\..\\src\\core\\service-executor.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,kBAAkB,CAAC;AAEnF,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,MAAqB,EACrB,GAMC;IAED,YAAY;IACZ,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC,WAAW,CAAC,CAAC;IAEzF,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,WAAW,eAAe,CAAC,CAAC;IAC1D,CAAC;IAED,oBAAoB;IACpB,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,IAAI,GAAG,CAAC,IAAI,EAAE,UAAU,CAAC;IAClE,IAAI,UAAU,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACvF,MAAM,IAAI,KAAK,CAAC,0BAA0B,UAAU,EAAE,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,UAAU;IACV,MAAM,GAAG,GAAG,oBAAoB,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAE/D,sBAAsB;IACtB,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAExC,SAAS;IACT,MAAM,MAAM,GAAI,OAAmC,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACpE,IAAI,OAAO,MAAM,KAAK,UAAU,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,QAAQ,GAAG,CAAC,WAAW,IAAI,GAAG,CAAC,UAAU,eAAe,CAAC,CAAC;IAC5E,CAAC;IAED,QAAQ;IACR,MAAM,WAAW,GAAG,yBAAyB,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,aAAa,GAAG,WAAW,IAAI,UAAU,CAAC,eAAe,CAAC;IAEhE,IAAI,aAAa,IAAI,IAAI,EAAE,CAAC;QAC1B,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACtC,qBAAqB;YACrB,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACzE,CAAC;QAED,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;YAClC,0BAA0B;YAC1B,MAAM,gBAAgB,GAAG,GAAG,CAAC,MAAM,EAAE,gBAAgB,IAAI,GAAG,CAAC,IAAI,EAAE,gBAAgB,CAAC;YAEpF,IAAI,gBAAgB,IAAI,IAAI,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,aAAa,CAAC,CAAC;YACjC,CAAC;YAED,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,gBAAgB,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;gBACpF,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,MAAM,IAAI,KAAK,CAAC,YAAY,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;QACH,CAAC;QACD,mCAAmC;IACrC,CAAC;IAED,KAAK;IACL,OAAO,MAAM,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;AACrC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -11,7 +11,6 @@ export * from "./transport/http/static-file-handler";
11
11
  export * from "./protocol/protocol-wrapper";
12
12
  export * from "./services/orm-service";
13
13
  export * from "./services/auto-update-service";
14
- export * from "./services/smtp-client-service";
15
14
  export * from "./utils/config-manager";
16
15
  export * from "./legacy/v1-auto-update-handler";
17
16
  export * from "./service-server";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["..\\src\\index.ts"],"names":[],"mappings":"AACA,cAAc,wBAAwB,CAAC;AAGvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AAGnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAGlD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AAGrD,cAAc,6BAA6B,CAAC;AAG5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,cAAc,gCAAgC,CAAC;AAG/C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,iCAAiC,CAAC;AAGhD,cAAc,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["..\\src\\index.ts"],"names":[],"mappings":"AACA,cAAc,wBAAwB,CAAC;AAGvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AAGnC,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAGxC,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAGlD,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AAGrD,cAAc,6BAA6B,CAAC;AAG5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAE/C,cAAc,wBAAwB,CAAC;AAGvC,cAAc,iCAAiC,CAAC;AAGhD,cAAc,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -1,18 +1,27 @@
1
+ // 타입
1
2
  export * from "./types/server-options.js";
3
+ // 인증
2
4
  export * from "./auth/auth-token-payload.js";
3
5
  export * from "./auth/jwt-manager.js";
6
+ // 코어
4
7
  export * from "./core/define-service.js";
5
8
  export * from "./core/service-executor.js";
9
+ // 전송 계층 - Socket
6
10
  export * from "./transport/socket/websocket-handler.js";
7
11
  export * from "./transport/socket/service-socket.js";
12
+ // 전송 계층 - HTTP
8
13
  export * from "./transport/http/http-request-handler.js";
9
14
  export * from "./transport/http/upload-handler.js";
10
15
  export * from "./transport/http/static-file-handler.js";
16
+ // 프로토콜
11
17
  export * from "./protocol/protocol-wrapper.js";
18
+ // 서비스
12
19
  export * from "./services/orm-service.js";
13
20
  export * from "./services/auto-update-service.js";
14
- export * from "./services/smtp-client-service.js";
21
+ // 유틸리티
15
22
  export * from "./utils/config-manager.js";
23
+ // 레거시
16
24
  export * from "./legacy/v1-auto-update-handler.js";
25
+ // 메인
17
26
  export * from "./service-server.js";
18
- //# sourceMappingURL=index.js.map
27
+ //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,6 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["../src/index.ts"],
4
- "mappings": "AACA,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,cAAc;AAGd,cAAc;AAGd,cAAc;",
5
- "names": []
6
- }
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["..\\src\\index.ts"],"names":[],"mappings":"AAAA,KAAK;AACL,cAAc,wBAAwB,CAAC;AAEvC,KAAK;AACL,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oBAAoB,CAAC;AAEnC,KAAK;AACL,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAExC,iBAAiB;AACjB,cAAc,sCAAsC,CAAC;AACrD,cAAc,mCAAmC,CAAC;AAElD,eAAe;AACf,cAAc,uCAAuC,CAAC;AACtD,cAAc,iCAAiC,CAAC;AAChD,cAAc,sCAAsC,CAAC;AAErD,OAAO;AACP,cAAc,6BAA6B,CAAC;AAE5C,MAAM;AACN,cAAc,wBAAwB,CAAC;AACvC,cAAc,gCAAgC,CAAC;AAC/C,OAAO;AACP,cAAc,wBAAwB,CAAC;AAEvC,MAAM;AACN,cAAc,iCAAiC,CAAC;AAEhD,KAAK;AACL,cAAc,kBAAkB,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import type { WebSocket } from "ws";
2
2
  /**
3
- * V1 legacy client handler (only auto-update supported).
4
- * All other requests return an upgrade-required error.
3
+ * V1 레거시 클라이언트 핸들러 (자동 업데이트만 지원).
4
+ * 모든 요청은 업그레이드 필요 에러를 반환한다.
5
5
  */
6
6
  export declare function handleV1Connection(socket: WebSocket, autoUpdateMethods: {
7
7
  getLastVersion: (platform: string) => Promise<any>;
@@ -1,38 +1,45 @@
1
1
  import consola from "consola";
2
2
  const logger = consola.withTag("service-server:V1AutoUpdateHandler");
3
- function handleV1Connection(socket, autoUpdateMethods, clientNameSetter) {
4
- socket.send(JSON.stringify({ name: "connected" }));
5
- socket.on("message", (data) => {
6
- try {
7
- const msg = JSON.parse(data.toString());
8
- if (msg.command === "SdAutoUpdateService.getLastVersion") {
9
- clientNameSetter?.(msg.clientName);
10
- const result = autoUpdateMethods.getLastVersion(msg.params[0]);
11
- const response = {
12
- name: "response",
13
- reqUuid: msg.uuid,
14
- state: "success",
15
- body: result
16
- };
17
- socket.send(JSON.stringify(response));
18
- } else {
19
- const response = {
20
- name: "response",
21
- reqUuid: msg.uuid,
22
- state: "error",
23
- body: {
24
- message: "App upgrade is required.",
25
- code: "UPGRADE_REQUIRED"
26
- }
27
- };
28
- socket.send(JSON.stringify(response));
29
- }
30
- } catch (err) {
31
- logger.warn("V1 message processing error", err);
32
- }
33
- });
3
+ /**
4
+ * V1 레거시 클라이언트 핸들러 (자동 업데이트만 지원).
5
+ * 외 모든 요청은 업그레이드 필요 에러를 반환한다.
6
+ */
7
+ export function handleV1Connection(socket, autoUpdateMethods, clientNameSetter) {
8
+ // 연결 성립 알림
9
+ socket.send(JSON.stringify({ name: "connected" }));
10
+ socket.on("message", async (data) => {
11
+ try {
12
+ const msg = JSON.parse(data.toString());
13
+ // SdAutoUpdateService.getLastVersion만 허용
14
+ if (msg.command === "SdAutoUpdateService.getLastVersion") {
15
+ // 레거시 컨텍스트 설정
16
+ clientNameSetter?.(msg.clientName);
17
+ const result = await autoUpdateMethods.getLastVersion(msg.params[0]);
18
+ const response = {
19
+ name: "response",
20
+ reqUuid: msg.uuid,
21
+ state: "success",
22
+ body: result,
23
+ };
24
+ socket.send(JSON.stringify(response));
25
+ }
26
+ else {
27
+ // 그 외 모든 요청은 업그레이드 요구
28
+ const response = {
29
+ name: "response",
30
+ reqUuid: msg.uuid,
31
+ state: "error",
32
+ body: {
33
+ message: "앱 업그레이드가 필요합니다.",
34
+ code: "UPGRADE_REQUIRED",
35
+ },
36
+ };
37
+ socket.send(JSON.stringify(response));
38
+ }
39
+ }
40
+ catch (err) {
41
+ logger.warn("V1 메시지 처리 에러", err);
42
+ }
43
+ });
34
44
  }
35
- export {
36
- handleV1Connection
37
- };
38
- //# sourceMappingURL=v1-auto-update-handler.js.map
45
+ //# sourceMappingURL=v1-auto-update-handler.js.map
@@ -1,6 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/legacy/v1-auto-update-handler.ts"],
4
- "mappings": "AACA,OAAO,aAAa;AAEpB,MAAM,SAAS,QAAQ,QAAQ,oCAAoC;AAoB5D,SAAS,mBACd,QACA,mBACA,kBACA;AAEA,SAAO,KAAK,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC,CAAC;AAEjD,SAAO,GAAG,WAAW,CAAC,SAAS;AAC7B,QAAI;AACF,YAAM,MAAM,KAAK,MAAM,KAAK,SAAS,CAAC;AAGtC,UAAI,IAAI,YAAY,sCAAsC;AAExD,2BAAmB,IAAI,UAAU;AAEjC,cAAM,SAAS,kBAAkB,eAAe,IAAI,OAAO,CAAC,CAAW;AAEvE,cAAM,WAAwB;AAAA,UAC5B,MAAM;AAAA,UACN,SAAS,IAAI;AAAA,UACb,OAAO;AAAA,UACP,MAAM;AAAA,QACR;AACA,eAAO,KAAK,KAAK,UAAU,QAAQ,CAAC;AAAA,MACtC,OAAO;AAEL,cAAM,WAAwB;AAAA,UAC5B,MAAM;AAAA,UACN,SAAS,IAAI;AAAA,UACb,OAAO;AAAA,UACP,MAAM;AAAA,YACJ,SAAS;AAAA,YACT,MAAM;AAAA,UACR;AAAA,QACF;AACA,eAAO,KAAK,KAAK,UAAU,QAAQ,CAAC;AAAA,MACtC;AAAA,IACF,SAAS,KAAK;AACZ,aAAO,KAAK,+BAA+B,GAAG;AAAA,IAChD;AAAA,EACF,CAAC;AACH;",
5
- "names": []
6
- }
1
+ {"version":3,"file":"v1-auto-update-handler.js","sourceRoot":"","sources":["..\\..\\src\\legacy\\v1-auto-update-handler.ts"],"names":[],"mappings":"AACA,OAAO,OAAO,MAAM,SAAS,CAAC;AAE9B,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC;AAgBrE;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,MAAiB,EACjB,iBAAyE,EACzE,gBAA2D;IAE3D,WAAW;IACX,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC;IAEnD,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;QAClC,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAe,CAAC;YAEtD,yCAAyC;YACzC,IAAI,GAAG,CAAC,OAAO,KAAK,oCAAoC,EAAE,CAAC;gBACzD,cAAc;gBACd,gBAAgB,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;gBAEnC,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAW,CAAC,CAAC;gBAE/E,MAAM,QAAQ,GAAgB;oBAC5B,IAAI,EAAE,UAAU;oBAChB,OAAO,EAAE,GAAG,CAAC,IAAI;oBACjB,KAAK,EAAE,SAAS;oBAChB,IAAI,EAAE,MAAM;iBACb,CAAC;gBACF,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,sBAAsB;gBACtB,MAAM,QAAQ,GAAgB;oBAC5B,IAAI,EAAE,UAAU;oBAChB,OAAO,EAAE,GAAG,CAAC,IAAI;oBACjB,KAAK,EAAE,OAAO;oBACd,IAAI,EAAE;wBACJ,OAAO,EAAE,iBAAiB;wBAC1B,IAAI,EAAE,kBAAkB;qBACzB;iBACF,CAAC;gBACF,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,GAAG,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -1,33 +1,33 @@
1
1
  import type { Bytes } from "@simplysm/core-common";
2
2
  import type { ServiceMessageDecodeResult, ServiceMessage } from "@simplysm/service-common";
3
3
  /**
4
- * Protocol wrapper interface
4
+ * 프로토콜 래퍼 인터페이스
5
5
  *
6
- * Automatically offloads heavy message encoding/decoding to a worker thread
7
- * while using main thread for lightweight operations.
6
+ * 무거운 메시지 인코딩/디코딩을 worker 스레드에 자동으로 위임하고,
7
+ * 가벼운 작업은 메인 스레드에서 처리한다.
8
8
  */
9
9
  export interface ServerProtocolWrapper {
10
10
  /**
11
- * Encode message (auto worker delegation)
11
+ * 메시지를 인코딩한다 (worker 자동 위임)
12
12
  */
13
13
  encode(uuid: string, message: ServiceMessage): Promise<{
14
14
  chunks: Bytes[];
15
15
  totalSize: number;
16
16
  }>;
17
17
  /**
18
- * Decode message (auto worker delegation)
18
+ * 메시지를 디코딩한다 (worker 자동 위임)
19
19
  */
20
20
  decode(bytes: Bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>;
21
21
  /**
22
- * Dispose protocol resources
22
+ * 프로토콜 리소스를 해제한다
23
23
  */
24
24
  dispose(): void;
25
25
  }
26
26
  /**
27
- * Create a protocol wrapper instance
27
+ * 프로토콜 래퍼 인스턴스를 생성한다
28
28
  *
29
- * Automatically offloads heavy message encoding/decoding to a worker thread
30
- * while using main thread for lightweight operations.
29
+ * 무거운 메시지 인코딩/디코딩을 worker 스레드에 자동으로 위임하고,
30
+ * 가벼운 작업은 메인 스레드에서 처리한다.
31
31
  */
32
32
  export declare function createServerProtocolWrapper(): ServerProtocolWrapper;
33
33
  //# sourceMappingURL=protocol-wrapper.d.ts.map
@@ -1,53 +1,71 @@
1
1
  import { Worker } from "@simplysm/core-node";
2
2
  import { createServiceProtocol } from "@simplysm/service-common";
3
+ // 공유 worker 인스턴스 (지연 싱글턴)
3
4
  let sharedWorker;
4
5
  function getWorker() {
5
- if (sharedWorker == null) {
6
- sharedWorker = Worker.create(
7
- import.meta.resolve("../workers/service-protocol.worker"),
8
- {
9
- resourceLimits: { maxOldGenerationSizeMb: 4096 }
10
- }
11
- );
12
- }
13
- return sharedWorker;
14
- }
15
- function createServerProtocolWrapper() {
16
- const protocol = createServiceProtocol();
17
- const SIZE_THRESHOLD = 30 * 1024;
18
- function shouldUseWorkerForEncode(msg) {
19
- if (!("body" in msg)) return false;
20
- const body = msg.body;
21
- if (body instanceof Uint8Array) {
22
- return true;
23
- }
24
- if (Array.isArray(body)) {
25
- return body.length > 0 && body.some((item) => item instanceof Uint8Array);
6
+ if (sharedWorker == null) {
7
+ sharedWorker = Worker.create(import.meta.resolve("../workers/service-protocol.worker"), {
8
+ resourceLimits: { maxOldGenerationSizeMb: 4096 },
9
+ });
26
10
  }
27
- return false;
28
- }
29
- return {
30
- async encode(uuid, message) {
31
- if (shouldUseWorkerForEncode(message)) {
32
- return getWorker().encode(uuid, message);
33
- } else {
34
- return protocol.encode(uuid, message);
35
- }
36
- },
37
- async decode(bytes) {
38
- const totalSize = bytes.length;
39
- if (totalSize > SIZE_THRESHOLD) {
40
- return getWorker().decode(bytes);
41
- } else {
42
- return protocol.decode(bytes);
43
- }
44
- },
45
- dispose() {
46
- protocol.dispose();
11
+ return sharedWorker;
12
+ }
13
+ /**
14
+ * 프로토콜 래퍼 인스턴스를 생성한다
15
+ *
16
+ * 무거운 메시지 인코딩/디코딩을 worker 스레드에 자동으로 위임하고,
17
+ * 가벼운 작업은 메인 스레드에서 처리한다.
18
+ */
19
+ export function createServerProtocolWrapper() {
20
+ // -------------------------------------------------------------------
21
+ // 상태
22
+ // -------------------------------------------------------------------
23
+ const protocol = createServiceProtocol();
24
+ const SIZE_THRESHOLD = 30 * 1024; // 30KB
25
+ // -------------------------------------------------------------------
26
+ // 헬퍼
27
+ // -------------------------------------------------------------------
28
+ /**
29
+ * 메시지 인코딩에 worker를 사용해야 하는지 확인한다
30
+ */
31
+ function shouldUseWorkerForEncode(msg) {
32
+ if (!("body" in msg))
33
+ return false;
34
+ const body = msg.body;
35
+ // Uint8Array: 항상 worker 사용
36
+ if (body instanceof Uint8Array) {
37
+ return true;
38
+ }
39
+ // 배열: Uint8Array 요소 존재 여부 확인 (ORM 결과 등)
40
+ if (Array.isArray(body)) {
41
+ return body.length > 0 && body.some((item) => item instanceof Uint8Array);
42
+ }
43
+ return false;
47
44
  }
48
- };
45
+ // -------------------------------------------------------------------
46
+ // 공개 API
47
+ // -------------------------------------------------------------------
48
+ return {
49
+ async encode(uuid, message) {
50
+ if (shouldUseWorkerForEncode(message)) {
51
+ return getWorker().encode(uuid, message);
52
+ }
53
+ else {
54
+ return protocol.encode(uuid, message);
55
+ }
56
+ },
57
+ async decode(bytes) {
58
+ const totalSize = bytes.length;
59
+ if (totalSize > SIZE_THRESHOLD) {
60
+ return getWorker().decode(bytes);
61
+ }
62
+ else {
63
+ return protocol.decode(bytes);
64
+ }
65
+ },
66
+ dispose() {
67
+ protocol.dispose();
68
+ },
69
+ };
49
70
  }
50
- export {
51
- createServerProtocolWrapper
52
- };
53
- //# sourceMappingURL=protocol-wrapper.js.map
71
+ //# sourceMappingURL=protocol-wrapper.js.map
@@ -1,6 +1 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/protocol/protocol-wrapper.ts"],
4
- "mappings": "AACA,SAAS,cAAgC;AAEzC,SAAS,6BAA6B;AA2BtC,IAAI;AAEJ,SAAS,YAA6D;AACpE,MAAI,gBAAgB,MAAM;AACxB,mBAAe,OAAO;AAAA,MACpB,YAAY,QAAQ,oCAAoC;AAAA,MACxD;AAAA,QACE,gBAAgB,EAAE,wBAAwB,KAAK;AAAA,MACjD;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,8BAAqD;AAKnE,QAAM,WAAW,sBAAsB;AACvC,QAAM,iBAAiB,KAAK;AAS5B,WAAS,yBAAyB,KAA8B;AAC9D,QAAI,EAAE,UAAU,KAAM,QAAO;AAE7B,UAAM,OAAO,IAAI;AAGjB,QAAI,gBAAgB,YAAY;AAC9B,aAAO;AAAA,IACT;AAGA,QAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,aAAO,KAAK,SAAS,KAAK,KAAK,KAAK,CAAC,SAAS,gBAAgB,UAAU;AAAA,IAC1E;AAEA,WAAO;AAAA,EACT;AAMA,SAAO;AAAA,IACL,MAAM,OACJ,MACA,SACiD;AACjD,UAAI,yBAAyB,OAAO,GAAG;AACrC,eAAO,UAAU,EAAE,OAAO,MAAM,OAAO;AAAA,MACzC,OAAO;AACL,eAAO,SAAS,OAAO,MAAM,OAAO;AAAA,MACtC;AAAA,IACF;AAAA,IAEA,MAAM,OAAO,OAAmE;AAC9E,YAAM,YAAY,MAAM;AACxB,UAAI,YAAY,gBAAgB;AAC9B,eAAO,UAAU,EAAE,OAAO,KAAK;AAAA,MACjC,OAAO;AACL,eAAO,SAAS,OAAO,KAAK;AAAA,MAC9B;AAAA,IACF;AAAA,IAEA,UAAgB;AACd,eAAS,QAAQ;AAAA,IACnB;AAAA,EACF;AACF;",
5
- "names": []
6
- }
1
+ {"version":3,"file":"protocol-wrapper.js","sourceRoot":"","sources":["..\\..\\src\\protocol\\protocol-wrapper.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAoB,MAAM,qBAAqB,CAAC;AAE/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AA0BjE,0BAA0B;AAC1B,IAAI,YAAyE,CAAC;AAE9E,SAAS,SAAS;IAChB,IAAI,YAAY,IAAI,IAAI,EAAE,CAAC;QACzB,YAAY,GAAG,MAAM,CAAC,MAAM,CAC1B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,oCAAoC,CAAC,EACzD;YACE,cAAc,EAAE,EAAE,sBAAsB,EAAE,IAAI,EAAE;SACjD,CACF,CAAC;IACJ,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,2BAA2B;IACzC,sEAAsE;IACtE,KAAK;IACL,sEAAsE;IAEtE,MAAM,QAAQ,GAAG,qBAAqB,EAAE,CAAC;IACzC,MAAM,cAAc,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO;IAEzC,sEAAsE;IACtE,KAAK;IACL,sEAAsE;IAEtE;;OAEG;IACH,SAAS,wBAAwB,CAAC,GAAmB;QACnD,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;QAEnC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC;QAEtB,2BAA2B;QAC3B,IAAI,IAAI,YAAY,UAAU,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,wCAAwC;QACxC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACxB,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,YAAY,UAAU,CAAC,CAAC;QAC5E,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;IAED,sEAAsE;IACtE,SAAS;IACT,sEAAsE;IAEtE,OAAO;QACL,KAAK,CAAC,MAAM,CACV,IAAY,EACZ,OAAuB;YAEvB,IAAI,wBAAwB,CAAC,OAAO,CAAC,EAAE,CAAC;gBACtC,OAAO,SAAS,EAAE,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACN,OAAO,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED,KAAK,CAAC,MAAM,CAAC,KAAY;YACvB,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;YAC/B,IAAI,SAAS,GAAG,cAAc,EAAE,CAAC;gBAC/B,OAAO,SAAS,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC;iBAAM,CAAC;gBACN,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;QAED,OAAO;YACL,QAAQ,CAAC,OAAO,EAAE,CAAC;QACrB,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -10,11 +10,12 @@ export declare class ServiceServer<TAuthInfo = unknown> extends EventEmitter<{
10
10
  readonly options: ServiceServerOptions;
11
11
  isOpen: boolean;
12
12
  private readonly _wsHandler;
13
+ private readonly _jwtSecret;
14
+ private _shutdownRegistered;
13
15
  readonly fastify: FastifyInstance;
14
16
  constructor(options: ServiceServerOptions);
15
17
  listen(): Promise<void>;
16
18
  close(): Promise<void>;
17
- broadcastReload(clientName: string | undefined, changedFileSet: Set<string>): Promise<void>;
18
19
  emitEvent<TInfo, TData>(eventDef: ServiceEventDef<TInfo, TData>, infoSelector: (item: TInfo) => boolean, data: TData): Promise<void>;
19
20
  signAuthToken(payload: AuthTokenPayload<TAuthInfo>): Promise<string>;
20
21
  verifyAuthToken(token: string): Promise<AuthTokenPayload<TAuthInfo>>;
@@ -1 +1 @@
1
- {"version":3,"file":"service-server.d.ts","sourceRoot":"","sources":["..\\src\\service-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAIhE,OAAO,EAAQ,YAAY,EAAO,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAa/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAOnE,qBAAa,aAAa,CAAC,SAAS,GAAG,OAAO,CAAE,SAAQ,YAAY,CAAC;IACnE,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC;CACb,CAAC;IAOY,QAAQ,CAAC,OAAO,EAAE,oBAAoB;IANlD,MAAM,UAAS;IAEf,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4C;IAEvE,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;gBAEb,OAAO,EAAE,oBAAoB;IAiB5C,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IA4IvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,EAAE,cAAc,EAAE,GAAG,CAAC,MAAM,CAAC;IAK3E,SAAS,CAAC,KAAK,EAAE,KAAK,EAC1B,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EACvC,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,EACtC,IAAI,EAAE,KAAK;IAKP,aAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC;IAOlD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAO1E,OAAO,CAAC,yBAAyB;CAyBlC;AAED,wBAAgB,mBAAmB,CAAC,SAAS,GAAG,OAAO,EACrD,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,SAAS,CAAC,CAE1B"}
1
+ {"version":3,"file":"service-server.d.ts","sourceRoot":"","sources":["..\\src\\service-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAIhE,OAAO,EAAQ,YAAY,EAAO,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EAAE,eAAe,EAAkB,MAAM,SAAS,CAAC;AAa/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAOnE,qBAAa,aAAa,CAAC,SAAS,GAAG,OAAO,CAAE,SAAQ,YAAY,CAAC;IACnE,KAAK,EAAE,IAAI,CAAC;IACZ,KAAK,EAAE,IAAI,CAAC;CACb,CAAC;IASY,QAAQ,CAAC,OAAO,EAAE,oBAAoB;IARlD,MAAM,UAAS;IAEf,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA4C;IACvE,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAqB;IAChD,OAAO,CAAC,mBAAmB,CAAS;IAEpC,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;gBAEb,OAAO,EAAE,oBAAoB;IAmB5C,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAsJvB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,SAAS,CAAC,KAAK,EAAE,KAAK,EAC1B,QAAQ,EAAE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC,EACvC,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,EACtC,IAAI,EAAE,KAAK;IAKP,aAAa,CAAC,OAAO,EAAE,gBAAgB,CAAC,SAAS,CAAC;IAKlD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAK1E,OAAO,CAAC,yBAAyB;CA4BlC;AAED,wBAAgB,mBAAmB,CAAC,SAAS,GAAG,OAAO,EACrD,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,SAAS,CAAC,CAE1B"}