@simplysm/sd-claude 14.0.87 → 14.0.89

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 (127) hide show
  1. package/claude/references/sd-simplysm14/README.md +17 -18
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +35 -0
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +51 -0
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +53 -0
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +34 -0
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +40 -0
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +74 -0
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +27 -0
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +103 -0
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +69 -0
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +28 -0
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +57 -0
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +73 -0
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +78 -0
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +66 -0
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +71 -0
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +62 -0
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +70 -0
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +80 -0
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +262 -0
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +121 -0
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +128 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +129 -0
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +91 -0
  26. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +53 -0
  27. package/claude/references/sd-simplysm14/apis/core-common/obj.md +117 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +17 -0
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +43 -0
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +50 -0
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +41 -0
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +72 -0
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +39 -0
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +52 -0
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +43 -0
  36. package/claude/references/sd-simplysm14/apis/excel/cell.md +54 -0
  37. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +51 -0
  38. package/claude/references/sd-simplysm14/apis/excel/style.md +67 -0
  39. package/claude/references/sd-simplysm14/apis/excel/utils.md +35 -0
  40. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +97 -0
  41. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +83 -0
  42. package/claude/references/sd-simplysm14/apis/lint/README.md +49 -0
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +130 -0
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +13 -0
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +111 -0
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +128 -0
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +145 -0
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +147 -0
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +62 -0
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +90 -0
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +94 -0
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +26 -0
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +117 -0
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +291 -0
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +150 -0
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +48 -0
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +59 -0
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +84 -0
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +48 -0
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +72 -0
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +118 -0
  62. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +71 -0
  63. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +62 -0
  64. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +39 -0
  65. package/claude/references/sd-simplysm14/apis/storage/README.md +120 -0
  66. package/claude/skills/sd-demo/SKILL.md +6 -0
  67. package/claude/skills/sd-impl/SKILL.md +4 -7
  68. package/claude/skills/sd-spec/SKILL.md +31 -858
  69. package/claude/skills/sd-spec/references/spec-authoring.md +519 -0
  70. package/claude/workflows/sd-docs.js +84 -0
  71. package/claude/{skills/sd-docs/references/subagent-prompt.md → workflows/sd-docs.rules.md} +25 -40
  72. package/package.json +1 -1
  73. package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
  74. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
  75. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
  76. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
  77. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
  78. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
  79. package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
  80. package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
  81. package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
  82. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
  83. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
  84. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
  85. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
  86. package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
  87. package/claude/skills/sd-docs/SKILL.md +0 -58
  88. package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
  89. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
  90. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
  91. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
  92. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
  93. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
  94. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
  95. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
  96. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
  97. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
  98. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
  99. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
  100. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
  101. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
  102. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
  103. package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
  104. package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
  105. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
  106. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
  107. package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
  108. package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
  109. package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
  110. package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
  111. package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
  112. package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
  113. package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
  114. package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
  115. package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
  116. package/claude/skills/sd-review/evals/golden.jsonl +0 -2
  117. package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
  118. package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
  119. package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
  120. package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
  121. package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
  122. package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
  123. package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
  124. package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
  125. package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
  126. package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
  127. package/claude/skills/sd-use/evals/golden.jsonl +0 -6
@@ -0,0 +1,291 @@
1
+ # @simplysm/sd-cli — sd.config.ts 설정 타입
2
+
3
+ 프로젝트 루트 `sd.config.ts` 작성·수정 시 함께 읽히는 타입 묶음. `sd.config.ts` 는 `SdConfigFn` 을 default export 해야 한다. 권위 소스는 `packages/sd-cli/src/sd-config.types.ts`. 모든 타입은 `import type { ... } from "@simplysm/sd-cli"` 로 가져온다.
4
+
5
+ ## SdConfigFn / SdConfigParams / SdConfig
6
+
7
+ ```typescript
8
+ type SdConfigFn = (params: SdConfigParams) => SdConfig | Promise<SdConfig>;
9
+
10
+ interface SdConfigParams {
11
+ cwd: string;
12
+ dev: boolean;
13
+ opt: string[];
14
+ }
15
+
16
+ interface SdConfig {
17
+ packages: Record<string, SdPackageConfig | undefined>;
18
+ replaceDeps?: Record<string, string>;
19
+ postPublish?: SdPostPublishScriptConfig[];
20
+ }
21
+ ```
22
+
23
+ `SdConfigParams` (sd-cli 가 설정 함수에 주입):
24
+
25
+ - cwd: string — 현재 작업 디렉토리(워크스페이스 루트). 설정에서 경로를 절대화할 때 쓴다.
26
+ - dev: boolean — 개발 모드 플래그. true 면 dev 실행(watch/dev). env·publish 를 모드별로 분기할 때 쓴다.
27
+ - opt: string[] — CLI 의 `-o` 플래그로 넘어온 추가 옵션 배열. 임의 빌드 변형(예: 특정 환경 타겟)을 분기할 때 쓴다.
28
+
29
+ `SdConfig`:
30
+
31
+ - packages: Record<string, SdPackageConfig | undefined> — 키는 `packages/` 하위 디렉토리명(예: `"core-common"`), 값은 해당 패키지 빌드 설정. `undefined` 면 그 패키지를 빌드 대상에서 제외. 워크스페이스의 어떤 패키지를 어떤 타겟으로 빌드할지 한 곳에 모은다.
32
+ - replaceDeps?: Record<string, string> — 의존성 교체(심링크). 키는 node_modules 에서 찾을 패키지 glob(예: `"@simplysm/*"`), 값은 로컬 소스 디렉토리 경로로 키의 `*` 캡처가 값의 `*` 에 치환됨(예: `"../simplysm/packages/*"`). 배포된 패키지 대신 로컬 소스를 곧바로 쓰고 싶을 때.
33
+ - postPublish?: SdPostPublishScriptConfig[] — 배포 완료 후 순차 실행할 스크립트 목록. 배포 후 알림·태깅 등 후처리에 쓴다.
34
+
35
+ 사용 예:
36
+
37
+ ```typescript
38
+ import type { SdConfigFn } from "@simplysm/sd-cli";
39
+
40
+ const config: SdConfigFn = ({ dev }) => ({
41
+ packages: {
42
+ "core-common": { target: "neutral" },
43
+ "core-node": { target: "node", publish: { type: "npm" } },
44
+ "demo-client": dev ? { target: "client", server: "demo-server" } : undefined,
45
+ },
46
+ });
47
+ export default config;
48
+ ```
49
+
50
+ ## SdPackageConfig (빌드 타겟 분기 유니온)
51
+
52
+ ```typescript
53
+ type SdPackageConfig =
54
+ | SdBuildPackageConfig // target: "node" | "browser" | "neutral"
55
+ | SdClientPackageConfig // target: "client"
56
+ | SdServerPackageConfig // target: "server"
57
+ | SdScriptsPackageConfig; // target: "scripts"
58
+ ```
59
+
60
+ 판별자는 `target`. enum literal 별 의미:
61
+
62
+ - "node" / "browser" / "neutral" (→ `SdBuildPackageConfig`) — esbuild 라이브러리 패키지. "node" = Node.js 전용, "browser" = 브라우저 전용, "neutral" = 공용. npm 배포 라이브러리에 쓴다.
63
+ - "client" (→ `SdClientPackageConfig`) — Frontend 앱(Angular + Capacitor/Electron/PWA 옵션). esbuild + define 으로 env 주입.
64
+ - "server" (→ `SdServerPackageConfig`) — Fastify 서버 앱. esbuild banner 로 env 주입, PM2 옵션.
65
+ - "scripts" (→ `SdScriptsPackageConfig`) — 유틸 패키지. watch 훅이 없으면 watch/typecheck 대상에서 제외됨.
66
+
67
+ ### SdBuildPackageConfig (node/browser/neutral)
68
+
69
+ ```typescript
70
+ interface SdBuildPackageConfig {
71
+ target: BuildTarget; // "node" | "browser" | "neutral"
72
+ publish?: SdPublishConfig;
73
+ copySrc?: string[];
74
+ watch?: SdWatchHookConfig;
75
+ }
76
+ ```
77
+
78
+ - target: "node"|"browser"|"neutral" — 빌드 런타임 타겟(위 풀이 참조). 라이브러리가 어느 환경에서 돌지에 맞춰 고른다.
79
+ - publish?: SdPublishConfig — 배포 대상 설정. 미지정 시 배포 안 함. npm 배포 라이브러리면 `{ type: "npm" }`.
80
+ - copySrc?: string[] — `src/` 에서 `dist/` 로 그대로 복사할 파일 glob(src 기준 상대). 컴파일 대상 아닌 정적 리소스를 산출물에 포함할 때.
81
+ - watch?: SdWatchHookConfig — watch 모드에서 빌드 엔진과 함께 실행할 훅. 빌드 외 부수 작업(코드 생성 등)을 watch 에 끼울 때.
82
+
83
+ ### SdClientPackageConfig (client)
84
+
85
+ ```typescript
86
+ interface SdClientPackageConfig {
87
+ target: "client";
88
+ server: string | number;
89
+ env?: Record<string, string>;
90
+ publish?: SdPublishConfig;
91
+ capacitor?: SdCapacitorConfig;
92
+ electron?: SdElectronConfig;
93
+ configs?: Record<string, unknown>;
94
+ exclude?: string[];
95
+ browserSupport?: SdBrowserSupportConfig;
96
+ pwa?: false | SdPwaConfig;
97
+ }
98
+ ```
99
+
100
+ - server: string | number — 연결할 dev 서버. string = 서버 패키지명(예: `"demo-server"`), number = 포트 직접 지정(하위 호환). 보통 같은 워크스페이스의 서버 패키지명을 준다.
101
+ - env?: Record<string, string> — 빌드 시 `process.env` 를 객체로 치환할 환경 변수. 프론트 코드에 빌드 타임 상수를 주입할 때.
102
+ - publish?: SdPublishConfig — 산출물 배포 설정.
103
+ - capacitor?: SdCapacitorConfig — Capacitor 모바일 앱 패키징 설정. 지정 시 Android 등으로 패키징.
104
+ - electron?: SdElectronConfig — Electron 데스크톱 앱 패키징 설정.
105
+ - configs?: Record<string, unknown> — 런타임 설정. 빌드 시 `dist/.config.json` 으로 기록되어 앱이 런타임에 읽음. 배포 환경별 가변 값.
106
+ - exclude?: string[] — Capacitor/Electron `package.json` 에 추가(번들 제외)할 패키지 목록.
107
+ - browserSupport?: SdBrowserSupportConfig — 브라우저 호환(browserslist/PostCSS/legacyModule) 설정.
108
+ - pwa?: false | SdPwaConfig — PWA 설정. `false` 면 비활성화, 미지정 시 기본값으로 활성화, 객체면 manifest 커스텀. PWA 가 필요 없으면 `false`.
109
+
110
+ ### SdServerPackageConfig (server)
111
+
112
+ ```typescript
113
+ interface SdServerPackageConfig {
114
+ target: "server";
115
+ env?: Record<string, string>;
116
+ publish?: SdPublishConfig;
117
+ configs?: Record<string, unknown>;
118
+ externals?: string[];
119
+ pm2?: { name?: string; ignoreWatchPaths?: string[] };
120
+ packageManager?: "volta" | "mise";
121
+ }
122
+ ```
123
+
124
+ - env?: Record<string, string> — 빌드 시 `process.env.KEY` 를 상수로 치환(esbuild banner). 서버 빌드 타임 상수 주입.
125
+ - configs?: Record<string, unknown> — 런타임 설정. `dist/.config.json` 으로 기록.
126
+ - externals?: string[] — esbuild 번들에 포함하지 않을 외부 모듈. 자동 `binding.gyp` 감지 항목에 더해짐. 네이티브 모듈을 번들에서 뺄 때.
127
+ - pm2?.name?: string — PM2 프로세스 이름. 미지정 시 `package.json` name 에서 생성. 지정 시 `dist/pm2.config.cjs` 생성.
128
+ - pm2?.ignoreWatchPaths?: string[] — PM2 watch 에서 제외할 경로.
129
+ - packageManager?: "volta" | "mise" — 산출물에 생성할 패키지 매니저 설정 종류. "volta" = volta 설정, "mise" = `mise.toml` 생성. 배포 서버의 매니저에 맞춘다.
130
+
131
+ ### SdScriptsPackageConfig (scripts)
132
+
133
+ ```typescript
134
+ interface SdScriptsPackageConfig {
135
+ target: "scripts";
136
+ publish?: SdPublishConfig;
137
+ watch?: SdWatchHookConfig;
138
+ }
139
+ ```
140
+
141
+ - watch?: SdWatchHookConfig — watch 훅. 지정해야만 이 패키지가 watch 모드에 포함됨(미지정 시 watch/typecheck 제외). 파일 변경 시 임의 명령 실행에 쓴다.
142
+
143
+ ## SdWatchHookConfig
144
+
145
+ ```typescript
146
+ interface SdWatchHookConfig {
147
+ target: string[];
148
+ cmd: string;
149
+ args?: string[];
150
+ }
151
+ ```
152
+
153
+ - target: string[] — 감시할 glob 패턴(패키지 디렉토리 기준 상대). 어떤 파일 변경에 반응할지.
154
+ - cmd: string — 변경 감지 시 실행할 명령어.
155
+ - args?: string[] — 명령어 인수.
156
+
157
+ ## 배포 설정 (SdPublishConfig 유니온 + SdPostPublishScriptConfig)
158
+
159
+ ```typescript
160
+ type SdPublishConfig = SdNpmPublishConfig | SdLocalDirectoryPublishConfig | SdStoragePublishConfig;
161
+ ```
162
+
163
+ 판별자는 `type`:
164
+
165
+ - `SdNpmPublishConfig` — `{ type: "npm" }`. npm 레지스트리 배포. 공개 라이브러리에 쓴다.
166
+ - `SdLocalDirectoryPublishConfig` — `{ type: "local-directory"; path: string }`. 로컬 디렉토리로 복사. `path` 는 `%VER%`/`%PROJECT%` 치환 지원. 사내 공유 폴더 배포에.
167
+ - `SdStoragePublishConfig` — `{ type: "ftp"|"ftps"|"sftp"; host; port?; path?; user?; password? }`. type 별 프로토콜 차이(ftp = 평문, ftps = TLS, sftp = SSH). 원격 서버 업로드 배포에. host 만 필수, 나머지는 선택.
168
+
169
+ ```typescript
170
+ interface SdPostPublishScriptConfig {
171
+ type: "script";
172
+ cmd: string;
173
+ args: string[]; // %VER%, %PROJECT% 치환 지원
174
+ }
175
+ ```
176
+
177
+ - cmd: string — 배포 후 실행할 명령어.
178
+ - args: string[] — 인수. `%VER%`(버전), `%PROJECT%`(프로젝트명) 치환됨.
179
+
180
+ ## Capacitor 설정 (client 의 capacitor)
181
+
182
+ ```typescript
183
+ interface SdCapacitorConfig {
184
+ appId: string;
185
+ appName: string;
186
+ plugins?: Record<string, Record<string, unknown> | true>;
187
+ icon?: string;
188
+ debug?: boolean;
189
+ platform?: { android?: SdCapacitorAndroidConfig };
190
+ }
191
+ ```
192
+
193
+ - appId: string — 앱 ID(역도메인, 예: `"com.example.app"`). 스토어 식별자.
194
+ - appName: string — 앱 표시 이름.
195
+ - plugins?: Record<string, Record<string, unknown> | true> — Capacitor 플러그인. 키 = 패키지명, 값 = `true`(옵션 없이 활성) 또는 옵션 객체. 옵션이 필요 없으면 `true`.
196
+ - icon?: string — 앱 아이콘 경로(패키지 기준 상대).
197
+ - debug?: boolean — 디버그 빌드 플래그.
198
+ - platform?.android?: SdCapacitorAndroidConfig — Android 플랫폼별 설정.
199
+
200
+ ```typescript
201
+ interface SdCapacitorAndroidConfig {
202
+ config?: Record<string, string>;
203
+ bundle?: boolean;
204
+ intentFilters?: SdCapacitorIntentFilter[];
205
+ sign?: SdCapacitorSignConfig;
206
+ sdkVersion?: number;
207
+ permissions?: SdCapacitorPermission[];
208
+ }
209
+ ```
210
+
211
+ - config?: Record<string, string> — `AndroidManifest.xml` 의 `<application>` 태그 속성(예: `{ requestLegacyExternalStorage: "true" }`).
212
+ - bundle?: boolean — true = AAB 번들, false = APK. 스토어 배포면 true.
213
+ - intentFilters?: SdCapacitorIntentFilter[] — 딥링크 등 Intent Filter 목록.
214
+ - sign?: SdCapacitorSignConfig — APK/AAB 서명 설정. 릴리스 빌드에 필요.
215
+ - sdkVersion?: number — Android SDK 버전(minSdk·targetSdk 공통).
216
+ - permissions?: SdCapacitorPermission[] — 추가 권한 목록.
217
+
218
+ ```typescript
219
+ interface SdCapacitorSignConfig { keystore: string; storePassword: string; alias: string; password: string; keystoreType?: string; }
220
+ interface SdCapacitorPermission { name: string; maxSdkVersion?: number; ignore?: string; }
221
+ interface SdCapacitorIntentFilter { action?: string; category?: string; }
222
+ ```
223
+
224
+ - SdCapacitorSignConfig.keystore — keystore 파일 경로(패키지 기준 상대). storePassword/alias/password = 서명 자격. keystoreType?: string — keystore 타입(기본값 `"jks"`).
225
+ - SdCapacitorPermission.name — 권한 이름(예: `"CAMERA"`). maxSdkVersion?: number — 권한 적용 최대 SDK. ignore?: string — `tools:ignore` 속성 값.
226
+ - SdCapacitorIntentFilter.action — intent 액션(예: `"android.intent.action.VIEW"`). category — intent 카테고리(예: `"android.intent.category.DEFAULT"`).
227
+
228
+ ## Electron 설정 (client 의 electron)
229
+
230
+ ```typescript
231
+ interface SdElectronConfig {
232
+ appId: string;
233
+ portable?: boolean;
234
+ installerIcon?: string;
235
+ reinstallDependencies?: string[];
236
+ postInstallScript?: string;
237
+ nsisOptions?: Record<string, unknown>;
238
+ env?: Record<string, string>;
239
+ }
240
+ ```
241
+
242
+ - appId: string — Electron 앱 ID(역도메인, 예: `"com.example.myapp"`).
243
+ - portable?: boolean — true = 포터블 `.exe`, false/미지정 = NSIS 설치 프로그램. 설치 없이 실행 배포면 true.
244
+ - installerIcon?: string — 설치 프로그램 아이콘(`.ico`, 패키지 기준 상대).
245
+ - reinstallDependencies?: string[] — Electron 에 포함할 npm 패키지(네이티브 모듈 등) 목록.
246
+ - postInstallScript?: string — npm postinstall 스크립트.
247
+ - nsisOptions?: Record<string, unknown> — NSIS 옵션(`portable` 이 false 일 때 적용).
248
+ - env?: Record<string, string> — 환경 변수. `electron-main.ts` 에서 `process.env` 로 접근 가능.
249
+
250
+ ## PWA 설정 (client 의 pwa)
251
+
252
+ ```typescript
253
+ interface SdPwaConfig { manifest?: SdPwaManifestConfig; }
254
+ interface SdPwaManifestConfig {
255
+ name?: string;
256
+ short_name?: string;
257
+ display?: "standalone" | "fullscreen" | "minimal-ui" | "browser";
258
+ theme_color?: string;
259
+ background_color?: string;
260
+ icons?: Array<{ src: string; sizes: string; type?: string }>;
261
+ }
262
+ ```
263
+
264
+ - manifest?: SdPwaManifestConfig — PWA manifest 커스터마이징. 미지정 시 기본 manifest.
265
+ - display?: "standalone"|"fullscreen"|"minimal-ui"|"browser" — 앱 표시 모드. "standalone" = 브라우저 UI 없는 앱 창, "fullscreen" = 전체 화면, "minimal-ui" = 최소 브라우저 UI, "browser" = 일반 탭. 네이티브 느낌이면 "standalone".
266
+ - name/short_name/theme_color/background_color — manifest 표준 필드(앱 이름·축약명·테마색·배경색).
267
+ - icons?: Array<{ src; sizes; type? }> — manifest 아이콘 목록(경로·크기·MIME).
268
+
269
+ ## SdBrowserSupportConfig (client 의 browserSupport)
270
+
271
+ ```typescript
272
+ interface SdBrowserSupportConfig {
273
+ browserslist?: string | string[];
274
+ postCss?: { plugins: [string, (object | string)?][] };
275
+ legacyModule?: boolean;
276
+ }
277
+ ```
278
+
279
+ - browserslist?: string | string[] — browserslist 쿼리(예: `"last 2 Chrome versions"` 또는 `["ie 11", "last 2 versions"]`). 트랜스파일·prefix 대상 브라우저 범위.
280
+ - postCss?.plugins: [string, (object|string)?][] — PostCSS 플러그인 `[이름, 옵션?]` 튜플 배열.
281
+ - legacyModule?: boolean — 레거시 모듈 지원. true 면 코드 분할 비활성화 + `import.meta` 치환. 구형 환경 대응이 필요할 때.
282
+
283
+ ## 보조 타입
284
+
285
+ ```typescript
286
+ type BuildTarget = "node" | "browser" | "neutral";
287
+ interface NpmConfig { name; version; description?; dependencies?; devDependencies?; peerDependencies?; volta?; }
288
+ ```
289
+
290
+ - BuildTarget — esbuild 라이브러리 빌드 런타임 타겟 enum(위 `SdPackageConfig` 풀이 참조).
291
+ - NpmConfig — `package.json` 구조 타입. name/version 필수, 나머지 선택. package.json 을 타입 안전하게 다룰 때.
@@ -0,0 +1,150 @@
1
+ # @simplysm/service-client
2
+
3
+ WebSocket 기반 서비스 서버(`@simplysm/service-server`)에 접속해 서비스 메서드 호출·서버 이벤트 구독·파일 업/다운로드·ORM 원격 실행을 수행하는 클라이언트. 브라우저(DOM Worker)와 Node.js(글로벌 `WebSocket` 없으면 `ws` polyfill, `worker_threads`) 양쪽에서 동작.
4
+
5
+ ## 사용 트리거 인덱스
6
+
7
+ - **createServiceClient / ServiceClient** — 서버 접속, 서비스 메서드 호출, 인증, 연결 상태 추적이 필요할 때. 이 패키지의 주 진입점. (아래 인라인 섹션)
8
+ - **ServiceConnectionOptions** — 클라이언트 생성 시 접속 대상(host/port/ssl)·재연결 정책을 정할 때. (아래 인라인 섹션)
9
+ - **getService / ServiceProxy** — 서버 서비스 인터페이스를 타입 안전 프록시로 호출할 때. (아래 인라인 섹션)
10
+ - **이벤트 구독 (addListener / getEvent / emitEvent / ClientEventProxy / EventClient)** — 서버 푸시 이벤트를 구독·발행할 때. (아래 인라인 섹션)
11
+ - **파일 업/다운로드 (uploadFile / downloadFileBuffer / FileClient)** — 인증된 파일 업로드 또는 서버 상대경로 파일 다운로드 시. (아래 인라인 섹션)
12
+ - **진행률 (ServiceProgress / ServiceProgressState)** — 대용량 요청·응답의 청크 전송 진행률을 추적할 때. (아래 인라인 섹션)
13
+ - **환경 호환 타입·헬퍼 (BlobInput / FileCollection / BrowserWorker / isWorkerSupported 등)** — Node/browser 공용 코드에서 DOM 전용 타입 회피, Worker 지원 여부 분기 시. (아래 인라인 섹션)
14
+ - **ORM 원격 실행 (createOrmClientConnector / OrmClientConnector / OrmConnectOptions / OrmClientDbContextExecutor)** — 서버측 ORM DbContext 를 클라이언트에서 트랜잭션 단위로 실행할 때. 자세히: [orm.md](./orm.md)
15
+ - **저수준 전송 계층 (SocketProvider / ServiceTransport / ClientProtocolWrapper 및 create\*)** — 일반적으로 직접 쓰지 않음. `ServiceClient` 가 내부에서 조립. 소켓·하트비트·프로토콜 동작을 이해해야 할 때. 자세히: [transport.md](./transport.md)
16
+
17
+ ## 메인 클라이언트 (createServiceClient / ServiceClient)
18
+
19
+ `createServiceClient(name, options): ServiceClient` — 클라이언트 인스턴스 생성. `new ServiceClient(name, options)` 와 동일.
20
+
21
+ - name: string — 클라이언트 식별 이름. WebSocket 접속 쿼리의 `clientName`·파일 업로드 헤더 `x-sd-client-name` 으로 서버에 전달.
22
+ - options: ServiceConnectionOptions — 접속 대상·재연결 정책(아래 섹션).
23
+
24
+ `ServiceClient` 는 `EventEmitter<ServiceClientEvents>` 를 상속하며 다음을 노출:
25
+
26
+ - `name: string` (readonly) — 생성 시 받은 클라이언트 이름.
27
+ - `options: ServiceConnectionOptions` (readonly) — 생성 시 받은 접속 옵션.
28
+ - `connected: boolean` (getter) — 현재 WebSocket 이 OPEN 상태인지. 재연결 중·종료 시 false. 이벤트 등록 가능 여부 판단에 사용.
29
+ - `hostUrl: string` (getter) — `http(s)://host:port` 형태의 HTTP 베이스 URL. `ssl` 이 true 면 https. 파일 클라이언트가 이 URL 을 사용.
30
+ - `connect(): Promise<void>` — 서버에 WebSocket 연결. 초기 연결 실패 시 throw.
31
+ - `close(): Promise<void>` — 연결 수동 종료(이후 재연결하지 않음) 및 프로토콜 워커 자원 해제. 종료 후 재사용 금지.
32
+ - `send(serviceName, methodName, params, progress?): Promise<unknown>` — 저수준 서비스 호출. `serviceName.methodName` 메시지를 전송하고 응답 반환. 보통 `getService` 프록시를 통해 간접 사용. `progress` 를 안 줘도 client 의 `request/response/server-progress` 이벤트는 항상 발생.
33
+ - `auth(token): Promise<void>` — 인증 토큰 전송 후 내부에 보관. 보관 토큰은 재연결 시 자동 재인증·파일 업로드 Bearer 인증에 재사용.
34
+
35
+ ```ts
36
+ const client = createServiceClient("my-app", { host: "localhost", port: 50080, ssl: false });
37
+ await client.connect();
38
+ await client.auth(jwtToken);
39
+ ```
40
+
41
+ **ServiceClientEvents** (EventEmitter 이벤트):
42
+
43
+ - `"request-progress": ServiceProgressState` — 요청(업로드) 청크 진행률. 요청 본문이 분할(청크 2개 이상) 전송될 때만 발생.
44
+ - `"response-progress": ServiceProgressState` — 응답(다운로드) 청크 진행률.
45
+ - `"server-progress": ServiceProgressState` — 서버가 처리 중 직접 보고하는 진행률(서버측 `progress` 메시지).
46
+ - `"state": "connected" | "closed" | "reconnecting"` — 연결 상태 변화. `"connected"` = 연결/재연결 성공, `"closed"` = 정상 종료 또는 재연결 한도 초과, `"reconnecting"` = 재연결 시도 중. `"connected"` 전이 시 보관 토큰으로 자동 재인증 및 이벤트 리스너 자동 복구 수행.
47
+
48
+ ```ts
49
+ client.on("state", (s) => { if (s === "reconnecting") showOfflineBanner(); });
50
+ ```
51
+
52
+ ## ServiceConnectionOptions
53
+
54
+ `createServiceClient` 의 두 번째 인자.
55
+
56
+ - port: number — 서버 포트. 필수.
57
+ - host: string — 서버 호스트. 필수.
58
+ - ssl?: boolean — TLS 사용 여부. true 면 `wss`/`https`, false·미지정이면 `ws`/`http`. TLS 서버에 붙을 때 true.
59
+ - maxReconnectCount?: number — 연결 끊김 시 최대 재연결 시도 횟수. 미지정 시 10. **0 이면 재연결 비활성화**(끊기면 즉시 포기). 테스트·단발성 연결이면 0.
60
+
61
+ ## getService / ServiceProxy
62
+
63
+ 서버 서비스 인터페이스의 각 메서드를 `Promise` 반환 함수로 노출하는 타입 안전 프록시.
64
+
65
+ `getService<TService>(serviceName): ServiceProxy<TService>` — `serviceName` 으로 등록된 서버 서비스의 프록시 반환. 프록시 메서드 호출 = `client.send(serviceName, 메서드명, 인자배열)`.
66
+
67
+ - TService — 서버 서비스 메서드 인터페이스 타입. 컴파일 타임 시그니처 검증용(런타임 검증 아님).
68
+ - `ServiceProxy<TService>` — TService 의 각 함수 멤버를 `(...args) => Promise<Awaited<R>>` 로 매핑. 함수 아닌 속성은 `never` 로 제외.
69
+
70
+ ```ts
71
+ const svc = client.getService<MyServiceMethods>("MyService");
72
+ const result = await svc.echo("hi"); // 서버의 MyService.echo("hi") 호출, Promise<string>
73
+ ```
74
+
75
+ ## 이벤트 구독 (addListener / getEvent / emitEvent)
76
+
77
+ 서버 푸시 이벤트는 `@simplysm/service-common` 의 `defineEvent` 산출물(`ServiceEventDef`. `$info` = 구독 필터 정보 타입, `$data` = 페이로드 타입) 단위로 구독. 등록한 리스너는 재연결 시 자동 복구됨.
78
+
79
+ `addListener<TEventDef>(eventDef, info, cb): Promise<string>` — 리스너 등록. **연결되지 않은 상태면 throw**. 반환 key 로 나중에 제거.
80
+
81
+ - eventDef: TEventDef — 이벤트 정의(`defineEvent` 결과). `$info`/`$data` 타입 출처.
82
+ - info: TEventDef["$info"] — 이 구독을 식별·필터링할 정보(서버가 emit 대상 선별에 사용).
83
+ - cb: (data) => PromiseLike<void> — 이벤트 수신 콜백. 콜백 내 예외는 로깅만 되고 전파되지 않음.
84
+
85
+ `removeListener(key): Promise<void>` — 등록 key 로 리스너 제거. 서버 전송 실패(연결 끊김 등) 시 무시(서버가 끊김 시 자동 정리).
86
+
87
+ `emitEvent<TEventDef>(eventDef, infoSelector, data): Promise<void>` — 이벤트 발행. 서버에서 동일 이벤트 구독자 목록을 조회한 뒤 `infoSelector(info)` 가 true 인 대상에게만 data 전송.
88
+
89
+ - infoSelector: (item: $info) => boolean — 발행 대상 구독자를 info 기준으로 필터.
90
+ - data: TEventDef["$data"] — 전송 페이로드.
91
+
92
+ `getEvent<TEventDef>(eventDef): ClientEventProxy<TEventDef>` — 특정 eventDef 에 바인딩된 프록시 반환(eventDef 반복 전달 생략용).
93
+
94
+ `ClientEventProxy<TEventDef>` 멤버: `addListener(info, cb)`, `removeListener(key)`, `emit(infoSelector, data)` — 위 client 메서드의 eventDef 고정판.
95
+
96
+ ```ts
97
+ const evt = defineEvent<{ channel: string }, string>("Chat");
98
+ const key = await client.addListener(evt, { channel: "room1" }, async (msg) => render(msg));
99
+ await client.emitEvent(evt, (info) => info.channel === "room1", "hello");
100
+ await client.removeListener(key);
101
+ ```
102
+
103
+ `EventClient` / `createEventClient(transport)` 는 `ServiceClient` 가 내부 조립에 쓰는 저수준 구현. 위 4개 메서드에 더해 `resubscribeAll(): Promise<void>`(보관된 모든 리스너를 서버에 재등록, 재연결 복구용)을 가짐. 일반 사용에선 직접 만들지 않음.
104
+
105
+ ## 파일 업/다운로드 (uploadFile / downloadFileBuffer)
106
+
107
+ `uploadFile(files): Promise<ServiceUploadResult[]>` — `POST <hostUrl>/upload` (multipart) 로 파일 업로드. 보관 토큰을 `Authorization: Bearer` 로 전송하므로 **사전 `auth()` 필수**(미인증 시 throw).
108
+
109
+ - files: `File[] | FileCollection | { name: string; data: BlobInput }[]` — 업로드 대상. 브라우저 `File` 배열, `FileCollection`(FileList 호환), 또는 `{ name, data }` 커스텀 객체 배열. 커스텀 객체의 data 가 `Blob` 이 아니면 `new Blob([data])` 로 감쌈.
110
+
111
+ `downloadFileBuffer(relPath): Promise<Bytes>` — `<hostUrl>/<relPath>` 를 GET 해 `Uint8Array` 반환. 응답 비정상(`!res.ok`) 시 throw.
112
+
113
+ - relPath: string — 서버 기준 상대경로. 선행 `/` 유무 모두 허용(없으면 자동 추가).
114
+
115
+ ```ts
116
+ await client.auth(token);
117
+ const results = await client.uploadFile([{ name: "a.txt", data: "hello" }]);
118
+ const bytes = await client.downloadFileBuffer("/files/a.txt");
119
+ ```
120
+
121
+ `FileClient` / `createFileClient(hostUrl, clientName)` 는 `ServiceClient` 내부 구현. `download(relPath)` / `upload(files, authToken)` 두 메서드를 가지며 직접 생성은 보통 불필요.
122
+
123
+ ## 진행률 (ServiceProgress / ServiceProgressState)
124
+
125
+ 대용량 요청/응답이 청크로 분할될 때 진행 상황을 보고하는 콜백·상태 타입.
126
+
127
+ `ServiceProgress` — `send(..., progress)` 에 넘기는 콜백 집합(메서드별 추적용, 전역 이벤트와 별개). 각 콜백 `(s: ServiceProgressState) => void`:
128
+
129
+ - request? — 요청 청크 업로드 진행 시 호출(요청 청크 2개 이상일 때만).
130
+ - response? — 응답 청크 수신 진행 시 호출. 분할 응답이었으면 완료 시 100%(`completedSize === totalSize`)를 한 번 더 보고.
131
+ - server? — 서버가 처리 중 보고한 진행률 수신 시 호출(`name: "progress"` 메시지).
132
+
133
+ `ServiceProgressState` — 진행률 스냅샷.
134
+
135
+ - uuid: string — 해당 요청/응답 식별자. 동시 요청 구분용.
136
+ - totalSize: number — 전체 바이트 수.
137
+ - completedSize: number — 완료 바이트 수. `completedSize === totalSize` 면 완료.
138
+
139
+ `send` 호출 시 위 콜백과 무관하게 ServiceClient 의 `request/response/server-progress` 이벤트도 항상 발생하므로, 전역 추적이면 콜백 대신 `client.on("response-progress", ...)` 를 써도 됨.
140
+
141
+ ## 환경 호환 타입·헬퍼 (browser-compat)
142
+
143
+ Node/browser 공용 코드에서 DOM 전용 타입을 피하고 Worker 지원 여부를 분기하기 위한 타입·함수. 프로토콜 인코딩/파싱 Worker 오프로딩 판단 시 내부에서 사용.
144
+
145
+ - `BlobInput` = `Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | string` — `Blob` 생성자가 받는 데이터 타입(DOM `BlobPart` 대체). `uploadFile` 의 커스텀 객체 data 타입.
146
+ - `FileCollection` (interface) — DOM `FileList` 대체. `length`, `item(index): File | null`, 인덱스 접근, `[Symbol.iterator]` 보유. 브라우저 `FileList` 와 구조적 호환.
147
+ - `BrowserWorker` (interface) — DOM `Worker` 최소 인터페이스(`onmessage`/`onerror` 핸들러, `postMessage(message, transfer?)`, `terminate()`). DOM lib 없이 타입체크 통과용.
148
+ - `isBrowserWorkerSupported(): boolean` — `globalThis` 에 `Worker` 존재 여부. 브라우저 DOM Worker 가용 판단.
149
+ - `isNodeWorkerSupported(): boolean` — `process.versions.node` 존재 여부. Node `worker_threads` 가용 판단.
150
+ - `isWorkerSupported(): boolean` — 위 둘 중 하나라도 true. 프로토콜 인코딩 오프로딩 가능 여부 판단(미지원 시 메인 스레드 폴백).
@@ -0,0 +1,48 @@
1
+ # @simplysm/service-client — ORM 원격 실행
2
+
3
+ 서버에 연결된 DB 를 클라이언트 측 `DbContext`(`@simplysm/orm-common`)로 트랜잭션 단위 실행하는 기능. 클라이언트가 직접 DB 에 붙지 않고, 모든 쿼리를 `ServiceClient` 의 `Orm` 서비스 RPC 로 서버에 위임한다. `connect` 콜백 내부에서만 쿼리 가능.
4
+
5
+ ## OrmConnectOptions<T extends DbContext>
6
+
7
+ `OrmClientConnector` 의 `connect` / `connectWithoutTransaction` 에 넘기는 설정.
8
+
9
+ - DbClass: `new (executor, opt) => T` — DbContext 클래스 생성자. `opt` = `{ database: string; schema?: string }`. 실제 인스턴스 생성에 사용.
10
+ - connOpt: `DbConnOptions & { configName: string }` — 서버측 DB 접속 설정. `configName` = 서버에 등록된 DB 설정 이름(서버가 이 이름으로 실제 접속 정보를 찾음).
11
+ - dbContextOpt?: `{ database: string; schema: string }` — DbContext 가 쓸 데이터베이스/스키마 명시. 미지정 시 서버 `getInfo()` 가 돌려주는 기본 database/schema 사용. `database` 가 dbContextOpt·서버 양쪽에서 모두 비면 `"database는 필수입니다."` throw(결측을 임의 보정하지 않음).
12
+
13
+ ## OrmClientConnector
14
+
15
+ DbContext 를 만들고 트랜잭션 경계를 잡아 콜백을 실행하는 커넥터. 사용 전 `ServiceClient.connect()` 로 소켓이 연결돼 있어야 함(RPC 의존).
16
+
17
+ `createOrmClientConnector(serviceClient): OrmClientConnector` — 커넥터 생성. 내부에서 `OrmClientDbContextExecutor` 로 RPC 위임.
18
+
19
+ - `connect<T, R>(config, callback): Promise<R>` — DbContext 를 만들고 **트랜잭션 안에서** `callback(db)` 실행. 콜백 정상 반환 시 커밋, throw 시 롤백(콜백 내 다건 작업이 원자 처리됨). 외래키 제약 위반 메시지(`a parent row: a foreign key constraint`, `conflicted with the REFERENCE`)는 사용자용 한국어 메시지로 감싸 원본을 `cause` 에 담아 재 throw.
20
+ - `connectWithoutTransaction<T, R>(config, callback): Promise<R>` — 트랜잭션 없이 `callback(db)` 실행. 조회 전용·트랜잭션 불필요 작업에 사용.
21
+ - config: `OrmConnectOptions<T>` — 위 옵션. callback: `(db: T) => Promise<R> | R` — DbContext 를 받아 쿼리하는 콜백.
22
+
23
+ ```ts
24
+ const connector = createOrmClientConnector(client);
25
+ await connector.connect(
26
+ { DbClass: MyDb, connOpt: { configName: "main" } },
27
+ async (db) => {
28
+ await db.foo.insertAsync({ /* ... */ });
29
+ return db.foo.where(/* ... */).resultAsync();
30
+ },
31
+ ); // 콜백 throw 시 자동 롤백
32
+ ```
33
+
34
+ ## OrmClientDbContextExecutor
35
+
36
+ `DbContextExecutor`(`@simplysm/orm-common`) 구현체. 모든 메서드를 `ServiceClient.getService<OrmService>("Orm")` RPC 로 위임. 보통 `OrmClientConnector` 가 내부에서 생성하므로 직접 다룰 일은 드묾.
37
+
38
+ `new OrmClientDbContextExecutor(client, opt)` — 생성. opt = `DbConnOptions & { configName: string }`. 생성 시 `Orm` 서비스 프록시 확보.
39
+
40
+ - `getInfo(): Promise<{ dialect; database?; schema? }>` — 서버 DB 의 dialect 및 기본 database/schema 조회.
41
+ - `connect(): Promise<void>` — 서버에 커넥션 생성 요청, 반환된 `connId` 보관. 이후 모든 실행 메서드는 `connId` 없으면(미연결) throw.
42
+ - `beginTransaction(isolationLevel?): Promise<void>` — 트랜잭션 시작. isolationLevel = 격리 수준(`IsolationLevel`), 미지정 시 서버 기본값.
43
+ - `commitTransaction(): Promise<void>` — 커밋.
44
+ - `rollbackTransaction(): Promise<void>` — 롤백.
45
+ - `close(): Promise<void>` — 서버 커넥션 종료 후 보관한 `connId` 해제.
46
+ - `executeDefs<T>(defs, options?): Promise<T[][]>` — 쿼리 정의 배열(`QueryDef[]`) 실행, 정의별 결과 배열 반환. options = 정의별 `ResultMeta`(결과 매핑 메타, 항목별 nullable).
47
+ - `executeParametrized(query, params?): Promise<unknown[][]>` — 파라미터 바인딩 raw SQL 실행. query = SQL 문자열, params = 바인딩 값 배열.
48
+ - `bulkInsert(tableName, columnDefs, records): Promise<void>` — 대량 삽입. columnDefs = `Record<string, ColumnMeta>`(컬럼별 메타), records = 삽입할 행 객체 배열.
@@ -0,0 +1,59 @@
1
+ # @simplysm/service-client — 저수준 전송 계층
2
+
3
+ `ServiceClient` 가 생성자에서 내부적으로 조립하는 저수준 모듈들. WebSocket 연결·하트비트·재연결(SocketProvider), 요청/응답 매칭과 메시지 디스패치(ServiceTransport), 인코딩/디코딩의 Worker 오프로딩(ClientProtocolWrapper). 일반 사용에서는 `ServiceClient` 만 쓰면 되고, 이 계층은 직접 다룰 일이 드물다.
4
+
5
+ ## SocketProvider / createSocketProvider
6
+
7
+ WebSocket 1개의 연결·하트비트·자동 재연결을 담당.
8
+
9
+ `createSocketProvider(url, clientName, maxReconnectCount): SocketProvider` — 프로바이더 생성.
10
+
11
+ - url: string — `ws(s)://host:port/ws`. 접속 시 `ver=2`, 생성된 `clientId`(UUID), `clientName` 쿼리를 붙임.
12
+ - clientName: string — 접속 쿼리에 실리는 식별명.
13
+ - maxReconnectCount: number — 최대 재연결 시도. 0 이면 재연결 안 함.
14
+
15
+ 내부 상수: 하트비트 ping 5초 간격, 30초 무수신 시 타임아웃, 재연결 3초 간격. 1바이트 `0x01` ping 전송, `0x02` pong 수신은 무시(타임스탬프만 갱신).
16
+
17
+ - `clientName: string` (readonly) — 생성 시 받은 식별명.
18
+ - `connected: boolean` (getter) — 소켓이 OPEN 인지.
19
+ - `connect(): Promise<void>` — 접속 시작. 실패 시 throw, 성공 시 재연결 카운트 리셋하고 `state: "connected"` emit.
20
+ - `close(): Promise<void>` — 수동 종료. 이후 자동 재연결 안 함. `state: "closed"` emit.
21
+ - `send(data: Bytes): Promise<void>` — 바이트 전송. 일정 시간 내 미연결이면 throw.
22
+ - `on(type, listener)` / `off(type, listener)` — 이벤트 구독/해제.
23
+
24
+ `SocketProviderEvents`:
25
+
26
+ - message: Bytes — 수신 바이트(ping/pong 1바이트 제어 프레임 제외).
27
+ - state: `"connected" | "closed" | "reconnecting"` — 연결 상태 변화. `"connected"` = 연결/재연결 성공, `"closed"` = 수동 종료 또는 재연결 한도 초과, `"reconnecting"` = 재연결 시도 중.
28
+
29
+ 하트비트 타임아웃 감지 시 소켓을 강제 정리하고(늦은 onclose 로 인한 중복 재연결 방지로 핸들러 해제) 수동 종료가 아니면 재연결을 시도. 최대 시도 초과 시 `state: "closed"` emit.
30
+
31
+ ## ServiceTransport / createServiceTransport
32
+
33
+ 요청별 uuid 매칭, 응답/에러/진행률/서버이벤트 디스패치를 담당.
34
+
35
+ `createServiceTransport(socket, protocol): ServiceTransport` — 트랜스포트 생성. 소켓 `message` 를 받아 decode 후 종류별 분기. 소켓이 `closed`/`reconnecting` 되면 대기 중인 모든 요청을 reject(메모리 해제).
36
+
37
+ - socket: SocketProvider — 하위 소켓.
38
+ - protocol: ClientProtocolWrapper — 인코드/디코드 래퍼.
39
+
40
+ 멤버:
41
+
42
+ - `send(message, progress?): Promise<unknown>` — 요청 1건 전송 후 응답 Promise 반환. uuid 생성 → 리스너 등록 → encode → 청크 순차 전송. 응답(`response`) 수신 시 resolve, 에러(`error`) 시 서버 에러 필드를 머지한 `Error` 로 reject. message = `ServiceClientMessage`, progress = `ServiceProgress`(선택).
43
+ - `on(type, listener)` / `off(type, listener)` — 이벤트 구독/해제.
44
+
45
+ `ServiceTransportEvents`:
46
+
47
+ - event: `{ keys: string[]; data: unknown }` — 서버가 푸시한 `evt:on` 메시지. `EventClient` 가 이걸 구독해 keys 에 매칭되는 로컬 리스너로 디스패치.
48
+
49
+ decode 실패 시에도 헤더 16바이트에서 uuid 를 선추출해 해당 요청만 reject. 분할 응답이면 완료 시 `progress.response` 로 100% 를 한 번 더 보고.
50
+
51
+ ## ClientProtocolWrapper / createClientProtocolWrapper
52
+
53
+ 인코드/디코드를 크기 기준으로 Worker 에 오프로딩하는 래퍼. `@simplysm/service-common` 의 `ServiceProtocol` 을 감쌈.
54
+
55
+ `createClientProtocolWrapper(protocol): ClientProtocolWrapper` — 래퍼 생성. 임계값 30KB. Worker 미가용·임계값 이하면 메인 스레드 처리로 폴백.
56
+
57
+ - `encode(uuid, message): Promise<{ chunks: Bytes[]; totalSize: number }>` — 메시지를 청크 배열로 인코드. body 가 Uint8Array, 30KB 초과 문자열, 길이 100 초과 배열, 또는 첫 항목이 Uint8Array 인 배열이면 Worker 사용. message = `ServiceMessage`.
58
+ - `decode(bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>` — 수신 바이트 디코드. 청크 재조립(stateful)은 한 메시지의 청크가 흩어지지 않도록 **항상 메인 스레드 단일 누적기**에서 수행하고(#35), 재조립 완료 후 30KB 초과 JSON 파싱(stateless)만 Worker 에 위임. 미완료(progress) 면 그대로 반환.
59
+ - `dispose(): void` — 프로토콜과 Worker 리졸버 정리. `ServiceClient.close()` 에서 호출.
@@ -0,0 +1,84 @@
1
+ # @simplysm/service-common
2
+
3
+ 서버·클라이언트가 공유하는 서비스 통신 계약. 바이너리 프로토콜(인코딩/청킹/재조립)과 메시지 타입, 서비스 인터페이스 타입(ORM·자동업데이트·업로드), 타입 안전 이벤트 정의, 앱 메뉴/권한 구조 모델을 한 패키지에 둔다. 구현체가 아니라 양쪽이 합의하는 타입·프로토콜만 제공한다.
4
+
5
+ ## 사용 트리거 인덱스
6
+
7
+ - **서비스 프로토콜** — 서버·클라이언트 간 메시지를 바이너리로 인코딩/디코딩하거나, 3MB 초과 메시지의 청킹·재조립을 다룰 때. 메시지 타입·`PROTOCOL_CONFIG` 상수 포함. 자세히: [protocol.md](./protocol.md)
8
+ - **앱 구조 / 권한** — 메뉴 트리(`AppStructureItem`)를 정의하거나, 사용자 활성 모듈 기준으로 권한을 평탄화·필터링할 때. 자세히: [app-structure.md](./app-structure.md)
9
+ - **defineEvent / ServiceEventDef** — 서버·클라 공통 패키지에서 타입 안전 서비스 이벤트를 정의해 emit/구독에 쓸 때. (아래 인라인)
10
+ - **OrmService / DbConnOptions** — DB 연결·트랜잭션·쿼리 실행 서비스 시그니처를 구현/호출할 때. (아래 인라인)
11
+ - **AutoUpdateService** — 클라이언트 자동 업데이트 최신 버전 조회 서비스를 구현/호출할 때. (아래 인라인)
12
+ - **ServiceUploadResult** — 파일 업로드 응답 결과를 다룰 때. (아래 인라인)
13
+
14
+ ## 이벤트 정의 (defineEvent / ServiceEventDef)
15
+
16
+ 서버·클라이언트가 공유하는 공통 패키지에서 이벤트를 1회 정의해, 양쪽이 동일 객체를 import 해 emit/구독한다. 정의 객체를 그대로 `emitEvent`/`addListener` 에 넘기면 이름·info·data 타입이 자동 추론된다.
17
+
18
+ ```ts
19
+ function defineEvent<TInfo = unknown, TData = unknown>(eventName: string): ServiceEventDef<TInfo, TData>;
20
+
21
+ interface ServiceEventDef<TInfo = unknown, TData = unknown> {
22
+ eventName: string;
23
+ readonly $info: TInfo;
24
+ readonly $data: TData;
25
+ }
26
+ ```
27
+
28
+ - `defineEvent` 의 `eventName: string` — 이벤트 식별 문자열. 서버/클라가 같은 정의를 import 하므로 충돌 없게 유일해야 함.
29
+ - `TInfo` — 구독자 필터링용 정보 타입(예: 특정 orderId 만 수신). 서버 emit 시 필터 함수가 받는 인자 타입.
30
+ - `TData` — 이벤트 페이로드 타입. 리스너 콜백이 받는 데이터 타입.
31
+ - `ServiceEventDef.eventName: string` — 런타임 식별자. emit/구독 매칭에 실제 사용되는 유일한 필드.
32
+ - `$info: TInfo` / `$data: TData` (readonly) — 타입 추출 전용 마커. 런타임 값은 `undefined`(미사용)이며 제네릭 추론용으로만 존재. 직접 읽지 말 것.
33
+
34
+ ```ts
35
+ // 공통 패키지: 정의 + export
36
+ export const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
37
+ // 서버: 정의 객체 전달 → 이름·타입 자동 추론
38
+ await server.emitEvent(OrderUpdated, (info) => info.orderId === 123, { status: "shipped" });
39
+ // 클라이언트: 구독 (data 타입 추론됨)
40
+ await client.addListener(OrderUpdated, { orderId: 123 }, async (data) => console.log(data.status));
41
+ ```
42
+
43
+ ## 서비스 인터페이스 타입
44
+
45
+ 서버가 구현하고 클라이언트가 프록시로 호출하는 서비스 계약. 본 패키지는 타입만 제공한다.
46
+
47
+ ### OrmService
48
+
49
+ DB 연결·트랜잭션·쿼리 실행. MySQL/MSSQL/PostgreSQL 지원. 인자는 `@simplysm/orm-common` 의 `Dialect`/`IsolationLevel`/`QueryDef`/`ColumnMeta`/`ResultMeta` 사용.
50
+
51
+ - `getInfo(opt: DbConnOptions & { configName: string }): Promise<{ dialect: Dialect; database?: string; schema?: string }>` — 연결 대상의 `dialect`/`database?`/`schema?` 메타 조회. `database`/`schema` 는 dialect 에 따라 없을 수 있어 optional(결측 그대로 전파). (`configName` 필수.)
52
+ - `connect(opt: DbConnOptions & { configName: string }): Promise<number>` — 연결 후 `connId`(이후 모든 호출에 쓸 연결 핸들) 반환. (`configName` 필수.)
53
+ - `close(connId: number): Promise<void>` — 해당 연결 해제.
54
+ - `beginTransaction(connId: number, isolationLevel?: IsolationLevel): Promise<void>` — 트랜잭션 시작. `isolationLevel` 생략 시 드라이버 기본 격리수준.
55
+ - `commitTransaction(connId: number): Promise<void>` — 트랜잭션 커밋.
56
+ - `rollbackTransaction(connId: number): Promise<void>` — 트랜잭션 롤백.
57
+ - `executeParametrized(connId: number, query: string, params?: unknown[]): Promise<unknown[][]>` — 파라미터 바인딩 raw SQL 직접 실행. 다중 결과셋이라 결과셋별 행 배열(`unknown[][]`) 반환. `params` 생략 시 바인딩 없는 평문 쿼리.
58
+ - `executeDefs(connId: number, defs: QueryDef[], options?: (ResultMeta | undefined)[]): Promise<unknown[][]>` — `QueryDef[]` 구조화 쿼리 일괄 실행. `options[i]` 는 `defs[i]` 결과의 `ResultMeta`(컬럼 타입 변환 지정); 메타 불필요한 def 자리엔 `undefined`(결측 보존, 빈 값 치환 금지).
59
+ - `bulkInsert(connId: number, tableName: string, columnDefs: Record<string, ColumnMeta>, records: Record<string, unknown>[]): Promise<void>` — 대량 INSERT. `columnDefs`=컬럼명→`ColumnMeta`(타입/변환), `records`=컬럼명→값 객체 배열.
60
+
61
+ ```ts
62
+ type DbConnOptions = { configName?: string; config?: Record<string, unknown> };
63
+ ```
64
+
65
+ - `configName?: string` — 서버에 사전 등록된 DB 설정 이름 참조. `getInfo`/`connect` 시그니처에서는 `& { configName: string }` 으로 교차되어 필수가 됨.
66
+ - `config?: Record<string, unknown>` — 인라인 연결 설정 객체. 등록 이름 대신 직접 접속 정보를 줄 때.
67
+
68
+ ### AutoUpdateService
69
+
70
+ 클라이언트 앱의 최신 배포 버전을 조회하는 원격 서비스 인터페이스.
71
+
72
+ - `getLastVersion(platform: string): Promise<{ version: string; downloadPath: string } | undefined>` — `platform`(대상 OS 식별자, 예: `"win32"`/`"darwin"`/`"linux"`) 별 최신 버전 정보. 등록된 버전이 없으면 `undefined`(결측 그대로 전파, 빈 객체로 치환하지 않음). `version`=최신 버전 문자열, `downloadPath`=설치 파일 다운로드 경로.
73
+
74
+ ## ServiceUploadResult
75
+
76
+ 서버에 업로드된 파일 1건의 응답 정보.
77
+
78
+ ```ts
79
+ interface ServiceUploadResult { path: string; filename: string; size: number; }
80
+ ```
81
+
82
+ - `path: string` — 서버 내 저장 경로.
83
+ - `filename: string` — 원본 파일명(클라이언트가 보낸 이름).
84
+ - `size: number` — 파일 크기(바이트).