@simplysm/sd-claude 14.0.88 → 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.
- package/claude/references/sd-simplysm14/README.md +17 -17
- package/claude/references/sd-simplysm14/apis/angular/README.md +27 -53
- package/claude/references/sd-simplysm14/apis/angular/controls.md +37 -105
- package/claude/references/sd-simplysm14/apis/angular/crud.md +46 -43
- package/claude/references/sd-simplysm14/apis/angular/directives.md +22 -32
- package/claude/references/sd-simplysm14/apis/angular/features.md +40 -55
- package/claude/references/sd-simplysm14/apis/angular/infra.md +40 -40
- package/claude/references/sd-simplysm14/apis/angular/layout.md +25 -53
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +70 -82
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +44 -39
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +21 -36
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +52 -65
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +65 -70
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +33 -35
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +7 -7
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +29 -29
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +13 -12
- package/claude/references/sd-simplysm14/apis/core-common/README.md +222 -98
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +102 -53
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +128 -0
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +98 -64
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +91 -0
- package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +34 -28
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +104 -40
- package/claude/references/sd-simplysm14/apis/core-node/README.md +11 -8
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +23 -31
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +33 -22
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +28 -25
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +39 -53
- package/claude/references/sd-simplysm14/apis/core-node/pathx.md +26 -29
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +27 -29
- package/claude/references/sd-simplysm14/apis/excel/README.md +14 -14
- package/claude/references/sd-simplysm14/apis/lint/README.md +27 -21
- package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +5 -59
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +98 -67
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +107 -92
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +99 -65
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +83 -98
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +62 -52
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +62 -25
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +27 -27
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +12 -15
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
- package/claude/references/sd-simplysm14/apis/service-client/README.md +84 -86
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +14 -11
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +33 -10
- package/claude/references/sd-simplysm14/apis/service-common/README.md +37 -23
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +9 -9
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +13 -13
- package/claude/references/sd-simplysm14/apis/service-server/README.md +81 -65
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +32 -35
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +44 -33
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +34 -45
- package/claude/references/sd-simplysm14/apis/storage/README.md +24 -18
- package/claude/skills/sd-demo/SKILL.md +6 -0
- package/claude/skills/sd-impl/SKILL.md +4 -7
- package/claude/skills/sd-spec/SKILL.md +31 -858
- package/claude/skills/sd-spec/references/spec-authoring.md +519 -0
- package/claude/workflows/sd-docs.js +84 -0
- package/package.json +1 -1
- package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
- package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
- package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
- package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
- package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
- package/claude/skills/sd-docs/SKILL.md +0 -46
- package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
- package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
- package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
- package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
- package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
- package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
- package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
- package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
- package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
- package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
- package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
- package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
- package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
- package/claude/skills/sd-review/evals/golden.jsonl +0 -2
- package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
- package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
- package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
- package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
- package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
- package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
- package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
- package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
- package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
- package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
- package/claude/skills/sd-use/evals/golden.jsonl +0 -6
- /package/claude/{skills/sd-docs/references/doc-rules.md → workflows/sd-docs.rules.md} +0 -0
|
@@ -1,122 +1,116 @@
|
|
|
1
1
|
# @simplysm/service-client
|
|
2
2
|
|
|
3
|
-
WebSocket 기반
|
|
3
|
+
WebSocket 기반 서비스 서버(`@simplysm/service-server`)에 접속해 서비스 메서드 호출·서버 이벤트 구독·파일 업/다운로드·ORM 원격 실행을 수행하는 클라이언트. 브라우저(DOM Worker)와 Node.js(글로벌 `WebSocket` 없으면 `ws` polyfill, `worker_threads`) 양쪽에서 동작.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **createServiceClient / ServiceClient** — 서버
|
|
8
|
-
- **ServiceConnectionOptions** — 클라이언트 생성 시
|
|
9
|
-
- **getService / ServiceProxy** — 서버 서비스 인터페이스를 타입 안전 프록시로 호출할 때.
|
|
10
|
-
- **이벤트
|
|
11
|
-
- **파일
|
|
12
|
-
- **진행률
|
|
13
|
-
- **환경 호환
|
|
14
|
-
- **ORM 원격
|
|
15
|
-
- **저수준 전송
|
|
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
16
|
|
|
17
|
-
## ServiceClient
|
|
17
|
+
## 메인 클라이언트 (createServiceClient / ServiceClient)
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
`createServiceClient(name, options): ServiceClient` — 클라이언트 인스턴스 생성. `new ServiceClient(name, options)` 와 동일.
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
- `createServiceClient(name: string, options: ServiceConnectionOptions): ServiceClient` — 클라이언트 인스턴스 생성. `new ServiceClient(...)` 와 동일.
|
|
26
|
-
- `name: string` (생성자, readonly) — 클라이언트 식별 이름. WebSocket 접속 쿼리의 `clientName`, 파일 업로드 헤더 `x-sd-client-name` 으로 전송.
|
|
27
|
-
- `options: ServiceConnectionOptions` (생성자, readonly) — 접속 옵션. 아래 ServiceConnectionOptions 참조.
|
|
28
|
-
|
|
29
|
-
상태 접근자:
|
|
21
|
+
- name: string — 클라이언트 식별 이름. WebSocket 접속 쿼리의 `clientName`·파일 업로드 헤더 `x-sd-client-name` 으로 서버에 전달.
|
|
22
|
+
- options: ServiceConnectionOptions — 접속 대상·재연결 정책(아래 섹션).
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
- `hostUrl: string` (getter) — `http(s)://<host>:<port>` 형태 HTTP 베이스 URL. ssl 이면 `https`. 파일 전송이 이 URL 을 사용.
|
|
24
|
+
`ServiceClient` 는 `EventEmitter<ServiceClientEvents>` 를 상속하며 다음을 노출:
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- `
|
|
37
|
-
- `
|
|
38
|
-
- `
|
|
39
|
-
- `
|
|
40
|
-
- `
|
|
41
|
-
-
|
|
42
|
-
- 파일 관련(`uploadFile`, `downloadFileBuffer`) — 아래 "파일 전송" 참조.
|
|
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 인증에 재사용.
|
|
43
34
|
|
|
44
35
|
```ts
|
|
45
36
|
const client = createServiceClient("my-app", { host: "localhost", port: 50080, ssl: false });
|
|
46
37
|
await client.connect();
|
|
47
38
|
await client.auth(jwtToken);
|
|
48
|
-
client.on("state", (state) => console.log(state)); // "connected" | "closed" | "reconnecting"
|
|
49
39
|
```
|
|
50
40
|
|
|
51
|
-
ServiceClientEvents (EventEmitter 이벤트):
|
|
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"` 전이 시 보관 토큰으로 자동 재인증 및 이벤트 리스너 자동 복구 수행.
|
|
52
47
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
- `server-progress: ServiceProgressState` — 서버가 처리 중 보고하는 진행률.
|
|
48
|
+
```ts
|
|
49
|
+
client.on("state", (s) => { if (s === "reconnecting") showOfflineBanner(); });
|
|
50
|
+
```
|
|
57
51
|
|
|
58
52
|
## ServiceConnectionOptions
|
|
59
53
|
|
|
60
54
|
`createServiceClient` 의 두 번째 인자.
|
|
61
55
|
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
-
|
|
56
|
+
- port: number — 서버 포트. 필수.
|
|
57
|
+
- host: string — 서버 호스트. 필수.
|
|
58
|
+
- ssl?: boolean — TLS 사용 여부. true 면 `wss`/`https`, false·미지정이면 `ws`/`http`. TLS 서버에 붙을 때 true.
|
|
59
|
+
- maxReconnectCount?: number — 연결 끊김 시 최대 재연결 시도 횟수. 미지정 시 10. **0 이면 재연결 비활성화**(끊기면 즉시 포기). 테스트·단발성 연결이면 0.
|
|
66
60
|
|
|
67
61
|
## getService / ServiceProxy
|
|
68
62
|
|
|
69
|
-
서버 서비스 인터페이스의 각 메서드를 `Promise` 반환 함수로 노출하는 프록시.
|
|
63
|
+
서버 서비스 인터페이스의 각 메서드를 `Promise` 반환 함수로 노출하는 타입 안전 프록시.
|
|
70
64
|
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
`getService<TService>(serviceName): ServiceProxy<TService>` — `serviceName` 으로 등록된 서버 서비스의 프록시 반환. 프록시 메서드 호출 = `client.send(serviceName, 메서드명, 인자배열)`.
|
|
66
|
+
|
|
67
|
+
- TService — 서버 서비스 메서드 인터페이스 타입. 컴파일 타임 시그니처 검증용(런타임 검증 아님).
|
|
68
|
+
- `ServiceProxy<TService>` — TService 의 각 함수 멤버를 `(...args) => Promise<Awaited<R>>` 로 매핑. 함수 아닌 속성은 `never` 로 제외.
|
|
73
69
|
|
|
74
70
|
```ts
|
|
75
|
-
const svc = client.getService<
|
|
71
|
+
const svc = client.getService<MyServiceMethods>("MyService");
|
|
76
72
|
const result = await svc.echo("hi"); // 서버의 MyService.echo("hi") 호출, Promise<string>
|
|
77
73
|
```
|
|
78
74
|
|
|
79
|
-
## 이벤트 구독
|
|
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> — 이벤트 수신 콜백. 콜백 내 예외는 로깅만 되고 전파되지 않음.
|
|
80
84
|
|
|
81
|
-
|
|
85
|
+
`removeListener(key): Promise<void>` — 등록 key 로 리스너 제거. 서버 전송 실패(연결 끊김 등) 시 무시(서버가 끊김 시 자동 정리).
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
`emitEvent<TEventDef>(eventDef, infoSelector, data): Promise<void>` — 이벤트 발행. 서버에서 동일 이벤트 구독자 목록을 조회한 뒤 `infoSelector(info)` 가 true 인 대상에게만 data 전송.
|
|
84
88
|
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
- `emitEvent<TEventDef>(eventDef, infoSelector, data): Promise<void>` — 이벤트 발행. `infoSelector: (info) => boolean` 로 서버에 등록된 리스너 중 대상을 골라 `data: TEventDef["$data"]` 전달.
|
|
88
|
-
- `getEvent<TEventDef>(eventDef): ClientEventProxy<TEventDef>` — 특정 이벤트 정의에 바인딩된 프록시 반환(eventDef 반복 전달 생략용).
|
|
89
|
+
- infoSelector: (item: $info) => boolean — 발행 대상 구독자를 info 기준으로 필터.
|
|
90
|
+
- data: TEventDef["$data"] — 전송 페이로드.
|
|
89
91
|
|
|
90
|
-
ClientEventProxy<TEventDef
|
|
92
|
+
`getEvent<TEventDef>(eventDef): ClientEventProxy<TEventDef>` — 특정 eventDef 에 바인딩된 프록시 반환(eventDef 반복 전달 생략용).
|
|
91
93
|
|
|
92
|
-
|
|
93
|
-
- `removeListener(key): Promise<void>` — 리스너 해제.
|
|
94
|
-
- `emit(infoSelector, data): Promise<void>` — 위 `emitEvent` 의 eventDef 고정판.
|
|
94
|
+
`ClientEventProxy<TEventDef>` 멤버: `addListener(info, cb)`, `removeListener(key)`, `emit(infoSelector, data)` — 위 client 메서드의 eventDef 고정판.
|
|
95
95
|
|
|
96
96
|
```ts
|
|
97
|
-
const
|
|
98
|
-
const key = await client.addListener(
|
|
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");
|
|
99
100
|
await client.removeListener(key);
|
|
100
101
|
```
|
|
101
102
|
|
|
102
103
|
`EventClient` / `createEventClient(transport)` 는 `ServiceClient` 가 내부 조립에 쓰는 저수준 구현. 위 4개 메서드에 더해 `resubscribeAll(): Promise<void>`(보관된 모든 리스너를 서버에 재등록, 재연결 복구용)을 가짐. 일반 사용에선 직접 만들지 않음.
|
|
103
104
|
|
|
104
|
-
## 파일
|
|
105
|
+
## 파일 업/다운로드 (uploadFile / downloadFileBuffer)
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
`uploadFile(files): Promise<ServiceUploadResult[]>` — `POST <hostUrl>/upload` (multipart) 로 파일 업로드. 보관 토큰을 `Authorization: Bearer` 로 전송하므로 **사전 `auth()` 필수**(미인증 시 throw).
|
|
107
108
|
|
|
108
|
-
-
|
|
109
|
-
- `downloadFileBuffer(relPath: string): Promise<Bytes>` — `<hostUrl>/<relPath>` 를 GET 해 바이트(`Uint8Array`)로 반환. 응답 비정상(`!res.ok`) 시 throw.
|
|
109
|
+
- files: `File[] | FileCollection | { name: string; data: BlobInput }[]` — 업로드 대상. 브라우저 `File` 배열, `FileCollection`(FileList 호환), 또는 `{ name, data }` 커스텀 객체 배열. 커스텀 객체의 data 가 `Blob` 이 아니면 `new Blob([data])` 로 감쌈.
|
|
110
110
|
|
|
111
|
-
`
|
|
111
|
+
`downloadFileBuffer(relPath): Promise<Bytes>` — `<hostUrl>/<relPath>` 를 GET 해 `Uint8Array` 반환. 응답 비정상(`!res.ok`) 시 throw.
|
|
112
112
|
|
|
113
|
-
-
|
|
114
|
-
- `FileCollection` — DOM `FileList` 와 구조 호환 인터페이스(`length`, `item(i)`, 인덱스 접근, iterable). DOM lib 없이도 타입체크 통과용 대체 타입.
|
|
115
|
-
- `{ name: string; data: BlobInput }[]` — 커스텀 객체. `name` = 파일명, `data` = 본문. `data` 가 `Blob` 이 아니면 `new Blob([data])` 로 감쌈.
|
|
116
|
-
|
|
117
|
-
`BlobInput` — Blob 생성 입력 타입(DOM `BlobPart` 대체): `Blob | Uint8Array<ArrayBuffer> | ArrayBuffer | string` 중 하나.
|
|
118
|
-
|
|
119
|
-
`FileClient` / `createFileClient(hostUrl, clientName)` 는 `ServiceClient` 내부 구현. `download(relPath)`/`upload(files, authToken)` 두 메서드를 가지며 직접 생성은 보통 불필요.
|
|
113
|
+
- relPath: string — 서버 기준 상대경로. 선행 `/` 유무 모두 허용(없으면 자동 추가).
|
|
120
114
|
|
|
121
115
|
```ts
|
|
122
116
|
await client.auth(token);
|
|
@@ -124,29 +118,33 @@ const results = await client.uploadFile([{ name: "a.txt", data: "hello" }]);
|
|
|
124
118
|
const bytes = await client.downloadFileBuffer("/files/a.txt");
|
|
125
119
|
```
|
|
126
120
|
|
|
127
|
-
|
|
121
|
+
`FileClient` / `createFileClient(hostUrl, clientName)` 는 `ServiceClient` 내부 구현. `download(relPath)` / `upload(files, authToken)` 두 메서드를 가지며 직접 생성은 보통 불필요.
|
|
122
|
+
|
|
123
|
+
## 진행률 (ServiceProgress / ServiceProgressState)
|
|
128
124
|
|
|
129
125
|
대용량 요청/응답이 청크로 분할될 때 진행 상황을 보고하는 콜백·상태 타입.
|
|
130
126
|
|
|
131
|
-
ServiceProgress — `send`
|
|
127
|
+
`ServiceProgress` — `send(..., progress)` 에 넘기는 콜백 집합(메서드별 추적용, 전역 이벤트와 별개). 각 콜백 `(s: ServiceProgressState) => void`:
|
|
132
128
|
|
|
133
|
-
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
129
|
+
- request? — 요청 청크 업로드 진행 시 호출(요청 청크 2개 이상일 때만).
|
|
130
|
+
- response? — 응답 청크 수신 진행 시 호출. 분할 응답이었으면 완료 시 100%(`completedSize === totalSize`)를 한 번 더 보고.
|
|
131
|
+
- server? — 서버가 처리 중 보고한 진행률 수신 시 호출(`name: "progress"` 메시지).
|
|
136
132
|
|
|
137
|
-
ServiceProgressState
|
|
133
|
+
`ServiceProgressState` — 진행률 스냅샷.
|
|
138
134
|
|
|
139
|
-
-
|
|
140
|
-
-
|
|
141
|
-
-
|
|
135
|
+
- uuid: string — 해당 요청/응답 식별자. 동시 요청 구분용.
|
|
136
|
+
- totalSize: number — 전체 바이트 수.
|
|
137
|
+
- completedSize: number — 완료 바이트 수. `completedSize === totalSize` 면 완료.
|
|
142
138
|
|
|
143
139
|
`send` 호출 시 위 콜백과 무관하게 ServiceClient 의 `request/response/server-progress` 이벤트도 항상 발생하므로, 전역 추적이면 콜백 대신 `client.on("response-progress", ...)` 를 써도 됨.
|
|
144
140
|
|
|
145
|
-
## 환경 호환
|
|
141
|
+
## 환경 호환 타입·헬퍼 (browser-compat)
|
|
146
142
|
|
|
147
|
-
Node
|
|
143
|
+
Node/browser 공용 코드에서 DOM 전용 타입을 피하고 Worker 지원 여부를 분기하기 위한 타입·함수. 프로토콜 인코딩/파싱 Worker 오프로딩 판단 시 내부에서 사용.
|
|
148
144
|
|
|
149
|
-
- `
|
|
150
|
-
- `
|
|
151
|
-
- `
|
|
152
|
-
- `
|
|
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. 프로토콜 인코딩 오프로딩 가능 여부 판단(미지원 시 메인 스레드 폴백).
|
|
@@ -4,19 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
## OrmConnectOptions<T extends DbContext>
|
|
6
6
|
|
|
7
|
-
`OrmClientConnector` 의 `connect
|
|
7
|
+
`OrmClientConnector` 의 `connect` / `connectWithoutTransaction` 에 넘기는 설정.
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
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
12
|
|
|
13
13
|
## OrmClientConnector
|
|
14
14
|
|
|
15
15
|
DbContext 를 만들고 트랜잭션 경계를 잡아 콜백을 실행하는 커넥터. 사용 전 `ServiceClient.connect()` 로 소켓이 연결돼 있어야 함(RPC 의존).
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
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.
|
|
19
20
|
- `connectWithoutTransaction<T, R>(config, callback): Promise<R>` — 트랜잭션 없이 `callback(db)` 실행. 조회 전용·트랜잭션 불필요 작업에 사용.
|
|
21
|
+
- config: `OrmConnectOptions<T>` — 위 옵션. callback: `(db: T) => Promise<R> | R` — DbContext 를 받아 쿼리하는 콜백.
|
|
20
22
|
|
|
21
23
|
```ts
|
|
22
24
|
const connector = createOrmClientConnector(client);
|
|
@@ -33,13 +35,14 @@ await connector.connect(
|
|
|
33
35
|
|
|
34
36
|
`DbContextExecutor`(`@simplysm/orm-common`) 구현체. 모든 메서드를 `ServiceClient.getService<OrmService>("Orm")` RPC 로 위임. 보통 `OrmClientConnector` 가 내부에서 생성하므로 직접 다룰 일은 드묾.
|
|
35
37
|
|
|
36
|
-
|
|
38
|
+
`new OrmClientDbContextExecutor(client, opt)` — 생성. opt = `DbConnOptions & { configName: string }`. 생성 시 `Orm` 서비스 프록시 확보.
|
|
39
|
+
|
|
37
40
|
- `getInfo(): Promise<{ dialect; database?; schema? }>` — 서버 DB 의 dialect 및 기본 database/schema 조회.
|
|
38
41
|
- `connect(): Promise<void>` — 서버에 커넥션 생성 요청, 반환된 `connId` 보관. 이후 모든 실행 메서드는 `connId` 없으면(미연결) throw.
|
|
39
|
-
- `beginTransaction(isolationLevel?): Promise<void>` — 트랜잭션 시작.
|
|
42
|
+
- `beginTransaction(isolationLevel?): Promise<void>` — 트랜잭션 시작. isolationLevel = 격리 수준(`IsolationLevel`), 미지정 시 서버 기본값.
|
|
40
43
|
- `commitTransaction(): Promise<void>` — 커밋.
|
|
41
44
|
- `rollbackTransaction(): Promise<void>` — 롤백.
|
|
42
45
|
- `close(): Promise<void>` — 서버 커넥션 종료 후 보관한 `connId` 해제.
|
|
43
|
-
- `executeDefs<T>(defs
|
|
44
|
-
- `executeParametrized(query
|
|
45
|
-
- `bulkInsert(tableName, columnDefs, records): Promise<void>` — 대량 삽입.
|
|
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 = 삽입할 행 객체 배열.
|
|
@@ -6,31 +6,54 @@
|
|
|
6
6
|
|
|
7
7
|
WebSocket 1개의 연결·하트비트·자동 재연결을 담당.
|
|
8
8
|
|
|
9
|
-
|
|
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
|
+
|
|
10
17
|
- `clientName: string` (readonly) — 생성 시 받은 식별명.
|
|
11
18
|
- `connected: boolean` (getter) — 소켓이 OPEN 인지.
|
|
12
19
|
- `connect(): Promise<void>` — 접속 시작. 실패 시 throw, 성공 시 재연결 카운트 리셋하고 `state: "connected"` emit.
|
|
13
20
|
- `close(): Promise<void>` — 수동 종료. 이후 자동 재연결 안 함. `state: "closed"` emit.
|
|
14
21
|
- `send(data: Bytes): Promise<void>` — 바이트 전송. 일정 시간 내 미연결이면 throw.
|
|
15
|
-
- `on
|
|
22
|
+
- `on(type, listener)` / `off(type, listener)` — 이벤트 구독/해제.
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
`SocketProviderEvents`:
|
|
25
|
+
|
|
26
|
+
- message: Bytes — 수신 바이트(ping/pong 1바이트 제어 프레임 제외).
|
|
27
|
+
- state: `"connected" | "closed" | "reconnecting"` — 연결 상태 변화. `"connected"` = 연결/재연결 성공, `"closed"` = 수동 종료 또는 재연결 한도 초과, `"reconnecting"` = 재연결 시도 중.
|
|
28
|
+
|
|
29
|
+
하트비트 타임아웃 감지 시 소켓을 강제 정리하고(늦은 onclose 로 인한 중복 재연결 방지로 핸들러 해제) 수동 종료가 아니면 재연결을 시도. 최대 시도 초과 시 `state: "closed"` emit.
|
|
18
30
|
|
|
19
31
|
## ServiceTransport / createServiceTransport
|
|
20
32
|
|
|
21
33
|
요청별 uuid 매칭, 응답/에러/진행률/서버이벤트 디스패치를 담당.
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
-
|
|
35
|
+
`createServiceTransport(socket, protocol): ServiceTransport` — 트랜스포트 생성. 소켓 `message` 를 받아 decode 후 종류별 분기. 소켓이 `closed`/`reconnecting` 되면 대기 중인 모든 요청을 reject(메모리 해제).
|
|
36
|
+
|
|
37
|
+
- socket: SocketProvider — 하위 소켓.
|
|
38
|
+
- protocol: ClientProtocolWrapper — 인코드/디코드 래퍼.
|
|
26
39
|
|
|
27
|
-
|
|
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% 를 한 번 더 보고.
|
|
28
50
|
|
|
29
51
|
## ClientProtocolWrapper / createClientProtocolWrapper
|
|
30
52
|
|
|
31
53
|
인코드/디코드를 크기 기준으로 Worker 에 오프로딩하는 래퍼. `@simplysm/service-common` 의 `ServiceProtocol` 을 감쌈.
|
|
32
54
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- `
|
|
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) 면 그대로 반환.
|
|
36
59
|
- `dispose(): void` — 프로토콜과 Worker 리졸버 정리. `ServiceClient.close()` 에서 호출.
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
# @simplysm/service-common
|
|
2
2
|
|
|
3
|
-
서버·클라이언트가 공유하는 서비스 통신 계약. 바이너리 프로토콜(인코딩/청킹/재조립)
|
|
3
|
+
서버·클라이언트가 공유하는 서비스 통신 계약. 바이너리 프로토콜(인코딩/청킹/재조립)과 메시지 타입, 서비스 인터페이스 타입(ORM·자동업데이트·업로드), 타입 안전 이벤트 정의, 앱 메뉴/권한 구조 모델을 한 패키지에 둔다. 구현체가 아니라 양쪽이 합의하는 타입·프로토콜만 제공한다.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **서비스 프로토콜** — 서버·클라이언트 간 메시지를 바이너리로 인코딩/디코딩하거나, 3MB 초과 메시지의 청킹·재조립을 다룰 때. 메시지 타입·`PROTOCOL_CONFIG` 상수 포함.
|
|
8
|
-
- **앱 구조 / 권한** — 메뉴 트리(`AppStructureItem`)를 정의하거나, 사용자 활성 모듈 기준으로 권한을 평탄화·필터링할 때.
|
|
9
|
-
- **defineEvent / ServiceEventDef** — 서버·클라 공통 패키지에서 타입
|
|
7
|
+
- **서비스 프로토콜** — 서버·클라이언트 간 메시지를 바이너리로 인코딩/디코딩하거나, 3MB 초과 메시지의 청킹·재조립을 다룰 때. 메시지 타입·`PROTOCOL_CONFIG` 상수 포함. 자세히: [protocol.md](./protocol.md)
|
|
8
|
+
- **앱 구조 / 권한** — 메뉴 트리(`AppStructureItem`)를 정의하거나, 사용자 활성 모듈 기준으로 권한을 평탄화·필터링할 때. 자세히: [app-structure.md](./app-structure.md)
|
|
9
|
+
- **defineEvent / ServiceEventDef** — 서버·클라 공통 패키지에서 타입 안전 서비스 이벤트를 정의해 emit/구독에 쓸 때. (아래 인라인)
|
|
10
10
|
- **OrmService / DbConnOptions** — DB 연결·트랜잭션·쿼리 실행 서비스 시그니처를 구현/호출할 때. (아래 인라인)
|
|
11
11
|
- **AutoUpdateService** — 클라이언트 자동 업데이트 최신 버전 조회 서비스를 구현/호출할 때. (아래 인라인)
|
|
12
12
|
- **ServiceUploadResult** — 파일 업로드 응답 결과를 다룰 때. (아래 인라인)
|
|
13
13
|
|
|
14
14
|
## 이벤트 정의 (defineEvent / ServiceEventDef)
|
|
15
15
|
|
|
16
|
-
서버·클라이언트가 공유하는 공통 패키지에서 이벤트를 1회
|
|
16
|
+
서버·클라이언트가 공유하는 공통 패키지에서 이벤트를 1회 정의해, 양쪽이 동일 객체를 import 해 emit/구독한다. 정의 객체를 그대로 `emitEvent`/`addListener` 에 넘기면 이름·info·data 타입이 자동 추론된다.
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
19
|
function defineEvent<TInfo = unknown, TData = unknown>(eventName: string): ServiceEventDef<TInfo, TData>;
|
|
@@ -25,45 +25,59 @@ interface ServiceEventDef<TInfo = unknown, TData = unknown> {
|
|
|
25
25
|
}
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
- `defineEvent
|
|
29
|
-
- `TInfo` — 구독자 필터링용 정보 타입(예: 특정 orderId 만 수신). 서버 emit 시 필터
|
|
28
|
+
- `defineEvent` 의 `eventName: string` — 이벤트 식별 문자열. 서버/클라가 같은 정의를 import 하므로 충돌 없게 유일해야 함.
|
|
29
|
+
- `TInfo` — 구독자 필터링용 정보 타입(예: 특정 orderId 만 수신). 서버 emit 시 필터 함수가 받는 인자 타입.
|
|
30
30
|
- `TData` — 이벤트 페이로드 타입. 리스너 콜백이 받는 데이터 타입.
|
|
31
|
-
- `eventName: string`
|
|
32
|
-
- `$info: TInfo` / `$data: TData` — 타입 추출 전용 마커. 런타임 값은 `undefined`
|
|
31
|
+
- `ServiceEventDef.eventName: string` — 런타임 식별자. emit/구독 매칭에 실제 사용되는 유일한 필드.
|
|
32
|
+
- `$info: TInfo` / `$data: TData` (readonly) — 타입 추출 전용 마커. 런타임 값은 `undefined`(미사용)이며 제네릭 추론용으로만 존재. 직접 읽지 말 것.
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
|
+
// 공통 패키지: 정의 + export
|
|
35
36
|
export const OrderUpdated = defineEvent<{ orderId: number }, { status: string }>("OrderUpdated");
|
|
37
|
+
// 서버: 정의 객체 전달 → 이름·타입 자동 추론
|
|
36
38
|
await server.emitEvent(OrderUpdated, (info) => info.orderId === 123, { status: "shipped" });
|
|
39
|
+
// 클라이언트: 구독 (data 타입 추론됨)
|
|
37
40
|
await client.addListener(OrderUpdated, { orderId: 123 }, async (data) => console.log(data.status));
|
|
38
41
|
```
|
|
39
42
|
|
|
40
43
|
## 서비스 인터페이스 타입
|
|
41
44
|
|
|
42
|
-
서버가 구현하고 클라이언트가 프록시로 호출하는 서비스 계약. 본 패키지는
|
|
45
|
+
서버가 구현하고 클라이언트가 프록시로 호출하는 서비스 계약. 본 패키지는 타입만 제공한다.
|
|
43
46
|
|
|
44
47
|
### OrmService
|
|
45
48
|
|
|
46
|
-
DB 연결·트랜잭션·쿼리 실행. MySQL/MSSQL/PostgreSQL 지원.
|
|
49
|
+
DB 연결·트랜잭션·쿼리 실행. MySQL/MSSQL/PostgreSQL 지원. 인자는 `@simplysm/orm-common` 의 `Dialect`/`IsolationLevel`/`QueryDef`/`ColumnMeta`/`ResultMeta` 사용.
|
|
47
50
|
|
|
48
|
-
- `getInfo(opt: DbConnOptions & { configName: string })
|
|
49
|
-
- `connect(opt: DbConnOptions & { configName: string }): Promise<number>` — 연결 후 `connId`(이후 호출에 쓸 핸들) 반환.
|
|
50
|
-
- `close(connId)
|
|
51
|
-
- `beginTransaction(connId, isolationLevel
|
|
52
|
-
- `commitTransaction(connId)
|
|
53
|
-
- `
|
|
54
|
-
- `
|
|
55
|
-
- `
|
|
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`=컬럼명→값 객체 배열.
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
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>` — 인라인 연결 설정 객체. 등록 이름 대신 직접 접속 정보를 줄 때.
|
|
59
67
|
|
|
60
68
|
### AutoUpdateService
|
|
61
69
|
|
|
62
|
-
|
|
70
|
+
클라이언트 앱의 최신 배포 버전을 조회하는 원격 서비스 인터페이스.
|
|
71
|
+
|
|
72
|
+
- `getLastVersion(platform: string): Promise<{ version: string; downloadPath: string } | undefined>` — `platform`(대상 OS 식별자, 예: `"win32"`/`"darwin"`/`"linux"`) 별 최신 버전 정보. 등록된 버전이 없으면 `undefined`(결측 그대로 전파, 빈 객체로 치환하지 않음). `version`=최신 버전 문자열, `downloadPath`=설치 파일 다운로드 경로.
|
|
63
73
|
|
|
64
74
|
## ServiceUploadResult
|
|
65
75
|
|
|
66
|
-
서버에 업로드된
|
|
76
|
+
서버에 업로드된 파일 1건의 응답 정보.
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
interface ServiceUploadResult { path: string; filename: string; size: number; }
|
|
80
|
+
```
|
|
67
81
|
|
|
68
82
|
- `path: string` — 서버 내 저장 경로.
|
|
69
83
|
- `filename: string` — 원본 파일명(클라이언트가 보낸 이름).
|
|
@@ -4,37 +4,37 @@
|
|
|
4
4
|
|
|
5
5
|
## 트리 타입
|
|
6
6
|
|
|
7
|
-
`AppStructureItem<TModule>` = `AppStructureGroupItem
|
|
7
|
+
`AppStructureItem<TModule>` = `AppStructureGroupItem<TModule>` | `AppStructureLeafItem<TModule>`. 메뉴 트리의 노드(`children` 유무로 그룹/리프 판별).
|
|
8
8
|
|
|
9
9
|
`AppStructureGroupItem<TModule>` — 하위 노드를 갖는 그룹 노드.
|
|
10
10
|
- `code: string` — 노드 식별 코드. 권한 codeChain 에 누적됨.
|
|
11
11
|
- `title: string` — 표시 제목. titleChain 에 누적됨.
|
|
12
|
-
- `modules?: TModule[]` — 이 중 하나라도 활성이면
|
|
13
|
-
- `requiredModules?: TModule[]` — 전부 활성이어야
|
|
12
|
+
- `modules?: TModule[]` — 이 중 하나라도 활성이면 통과(OR). 빈 배열/`undefined` 면 제약 없음.
|
|
13
|
+
- `requiredModules?: TModule[]` — 전부 활성이어야 통과(AND).
|
|
14
14
|
- `icon?: string` — 메뉴 아이콘.
|
|
15
15
|
- `children: AppStructureItem<TModule>[]` — 하위 노드 배열(필수, 그룹 판별 키).
|
|
16
16
|
|
|
17
17
|
`AppStructureLeafItem<TModule>` — 실제 화면 노드.
|
|
18
|
-
- `code` / `title` / `modules?` / `requiredModules?` / `icon?` — 그룹과 동일 의미.
|
|
18
|
+
- `code: string` / `title: string` / `modules?` / `requiredModules?` / `icon?` — 그룹과 동일 의미.
|
|
19
19
|
- `perms?: ("use" | "edit")[]` — 이 화면 직접 권한. `"use"`=조회 권한 / `"edit"`=편집 권한. 각 항목이 평탄 권한 1건이 됨.
|
|
20
20
|
- `subPerms?: AppStructureSubPermission<TModule>[]` — 화면 내 세부 권한 묶음.
|
|
21
21
|
- `url?: string` — 라우팅 경로.
|
|
22
22
|
- `isNotMenu?: boolean` — true 면 메뉴에 노출 안 함(권한만 존재하는 화면), false/미지정이면 메뉴 노출.
|
|
23
23
|
|
|
24
24
|
`AppStructureSubPermission<TModule>` — 화면 하위 세부 권한 묶음.
|
|
25
|
-
- `code` / `title` / `modules?` / `requiredModules?` — 동일 의미.
|
|
25
|
+
- `code: string` / `title: string` / `modules?` / `requiredModules?` — 동일 의미. subPerm 자체의 modules/requiredModules 도 별도 검사됨.
|
|
26
26
|
- `perms: ("use" | "edit")[]` — 이 세부 묶음의 권한 종류(필수). `"use"`=조회 / `"edit"`=편집.
|
|
27
27
|
|
|
28
28
|
`FlatPermission<TModule>` — 평탄화 결과 1건.
|
|
29
29
|
- `titleChain: string[]` — 루트→해당 권한까지 제목 경로.
|
|
30
|
-
- `codeChain: string[]` —
|
|
30
|
+
- `codeChain: string[]` — code + perm/subPerm 코드 누적 경로(권한 식별자).
|
|
31
31
|
- `modulesChain: TModule[][]` — 경로상 각 레벨 modules 누적.
|
|
32
32
|
|
|
33
33
|
## 유틸 함수
|
|
34
34
|
|
|
35
35
|
- `isUsableModules(modules, requiredModules, usableModules): boolean` — 단일 노드 가시성 판정. `requiredModules` 전부 포함(AND) **그리고** `modules` 중 하나 포함(또는 빈 배열/`undefined` 면 통과, OR). 둘 중 하나만 검사하려면 나머지 인자에 `undefined` 전달.
|
|
36
|
-
- `isUsableModulesChain(modulesChain, requiredModulesChain, usableModules): boolean` —
|
|
37
|
-
- `getFlatPermissions(items, usableModules): FlatPermission[]` — 트리를 BFS 순회하며 `usableModules` 로 필터된 모든 권한을 평탄 목록으로 산출. 모듈 체인을 통과한 노드의 `perms`·`subPerms.perms` 각각을 `FlatPermission` 1건으로 변환. subPerm 은 자체 modules/requiredModules 도 추가 검사.
|
|
36
|
+
- `isUsableModulesChain(modulesChain, requiredModulesChain, usableModules): boolean` — 루트부터 누적된 체인 전체 통과 여부. 각 레벨 modules 는 OR, 각 레벨 requiredModules 는 AND 로 모두 만족해야 true.
|
|
37
|
+
- `getFlatPermissions(items, usableModules): FlatPermission<TModule>[]` — 트리를 BFS 순회하며 `usableModules` 로 필터된 모든 권한을 평탄 목록으로 산출. 모듈 체인을 통과한 노드의 `perms`·`subPerms.perms` 각각을 `FlatPermission` 1건으로 변환. subPerm 은 자체 modules/requiredModules 도 추가 검사.
|
|
38
38
|
|
|
39
39
|
```ts
|
|
40
40
|
const flats = getFlatPermissions(appStructure, currentUser.usableModules);
|
|
@@ -45,4 +45,4 @@ if (isUsableModules(item.modules, item.requiredModules, usableModules)) showMenu
|
|
|
45
45
|
주의:
|
|
46
46
|
- `usableModules` 가 `undefined` 이면 modules/requiredModules 가 지정된 노드는 통과 못 함(`includes` 가 false).
|
|
47
47
|
- `modules` 가 비었거나 `undefined` 인 노드는 모듈 제약 없이 항상 통과(OR 기본).
|
|
48
|
-
- `codeChain` 마지막 요소는 perm(`"use"`/`"edit"`) 또는 subPerm.code
|
|
48
|
+
- `codeChain` 마지막 요소는 perm(`"use"`/`"edit"`), 또는 subPerm.code 뒤의 perm 으로 끝남.
|