@simplysm/sd-claude 14.0.91 → 14.0.92
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 +7 -6
- package/claude/references/sd-simplysm14/apis/angular/README.md +59 -39
- package/claude/references/sd-simplysm14/apis/angular/controls.md +119 -186
- package/claude/references/sd-simplysm14/apis/angular/crud.md +70 -31
- package/claude/references/sd-simplysm14/apis/angular/directives.md +55 -57
- package/claude/references/sd-simplysm14/apis/angular/features.md +86 -105
- package/claude/references/sd-simplysm14/apis/angular/infra.md +48 -57
- package/claude/references/sd-simplysm14/apis/angular/layout.md +37 -47
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +82 -74
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +61 -50
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +74 -57
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +63 -72
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +23 -18
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -19
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +23 -18
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +72 -32
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +18 -18
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +29 -29
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +41 -41
- package/claude/references/sd-simplysm14/apis/core-common/README.md +97 -90
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +75 -51
- package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +81 -0
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +27 -29
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +44 -45
- package/claude/references/sd-simplysm14/apis/core-common/serialization.md +34 -33
- package/claude/references/sd-simplysm14/apis/core-common/value-types.md +86 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -6
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +3 -0
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +2 -2
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +1 -1
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +2 -2
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +6 -3
- package/claude/references/sd-simplysm14/apis/excel/README.md +10 -10
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +4 -2
- package/claude/references/sd-simplysm14/apis/excel/utils.md +1 -1
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +6 -6
- package/claude/references/sd-simplysm14/apis/lint/README.md +6 -32
- package/claude/references/sd-simplysm14/apis/lint/recommended.md +60 -0
- package/claude/references/sd-simplysm14/apis/lint/rules.md +17 -17
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +15 -6
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +68 -102
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +75 -89
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +87 -99
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +110 -147
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +48 -51
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +8 -13
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +5 -5
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +9 -6
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +9 -8
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +23 -19
- package/claude/references/sd-simplysm14/apis/service-client/README.md +20 -12
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +6 -6
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +1 -1
- package/claude/references/sd-simplysm14/apis/service-common/README.md +35 -32
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +23 -22
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +23 -23
- package/claude/references/sd-simplysm14/apis/service-server/README.md +51 -43
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +6 -6
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +31 -21
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +8 -8
- package/claude/references/sd-simplysm14/apis/storage/README.md +55 -49
- package/claude/references/sd-simplysm14/manuals/client-component.md +843 -740
- package/claude/references/sd-simplysm14/manuals/client-crud.md +8 -0
- package/claude/references/sd-simplysm14/manuals/client-demo.md +6 -16
- package/claude/references/sd-simplysm14/manuals/client-shared-data.md +26 -0
- package/claude/references/sd-simplysm14/manuals/logging.md +1 -1
- package/claude/references/sd-simplysm14/manuals/orm.md +15 -1
- package/claude/rules/sd-design-rules.md +7 -0
- package/claude/sd-system-prompt.md +5 -8
- package/claude/skills/sd-debug/SKILL.md +43 -0
- package/claude/skills/sd-debug/workflow.js +390 -0
- package/claude/skills/sd-demo/SKILL.md +18 -20
- package/claude/skills/sd-dev/SKILL.md +127 -24
- package/claude/skills/sd-docs/SKILL.md +5 -3
- package/claude/skills/sd-docs/references/subagent-prompt.md +2 -3
- package/claude/skills/sd-impl/SKILL.md +18 -18
- package/claude/skills/sd-manual/SKILL.md +1 -0
- package/claude/skills/sd-review/SKILL.md +24 -18
- package/claude/skills/sd-review/workflow.js +324 -0
- package/claude/skills/sd-spec/SKILL.md +96 -679
- package/claude/skills/sd-spec/references/example-spec.md +28 -50
- package/claude/skills/sd-spec/references/format-analyze.md +232 -0
- package/claude/skills/sd-spec/references/format-design.md +248 -0
- package/claude/skills/sd-spec/workflow-analyze.js +615 -0
- package/claude/skills/sd-spec/workflow-design.js +667 -0
- package/claude/skills/sd-unpack/scripts/handlers/office_com.py +5 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +157 -18
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +0 -68
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +0 -77
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +0 -86
- package/claude/skills/sd-skill/SKILL.md +0 -245
- package/claude/skills/sd-skill/scripts/run_eval.py +0 -380
|
@@ -19,9 +19,9 @@ function executeServiceMethod(
|
|
|
19
19
|
|
|
20
20
|
요청 1건을 실제 서비스 메서드로 라우팅·실행하는 핵심 게이트키퍼. WebSocket/HTTP 핸들러가 모두 이 함수로 수렴한다.
|
|
21
21
|
|
|
22
|
-
- `serviceName
|
|
22
|
+
- `serviceName` / `methodName` — `server.options.services` 의 `names` 매칭으로 서비스를, 그 팩토리 산출 객체에서 메서드를 찾음. 없으면 throw.
|
|
23
23
|
- `params: unknown[]` — 메서드 인자 배열. 스프레드되어 메서드에 전달.
|
|
24
|
-
- `socket?` / `http?` — 둘 중 하나로 요청 출처 전달. `clientName` 에
|
|
24
|
+
- `socket?` / `http?` — 둘 중 하나로 요청 출처 전달. `clientName` 에 `..`·슬래시(`/`,`\`)가 있으면 보안 차단 throw.
|
|
25
25
|
- 동작: 컨텍스트 생성 → 팩토리 호출 → 메서드 조회 → 인증/권한 검사(`auth` 래핑 권한 기준) → 실행 후 반환값 반환. `auth: false` 면 인증 스킵, `auth` 미설정인데 권한 요구 메서드면 설정 오류 throw.
|
|
26
26
|
|
|
27
27
|
## createServiceContext
|
|
@@ -32,20 +32,30 @@ function createServiceContext<TAuthInfo>(
|
|
|
32
32
|
): ServiceContext<TAuthInfo>
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
`ServiceContext`(인증·클라이언트·설정 접근자) 인스턴스를 만든다.
|
|
35
|
+
`ServiceContext`(인증·클라이언트·설정 접근자) 인스턴스를 만든다.
|
|
36
|
+
|
|
37
|
+
- `server` — 서버 인스턴스(필수).
|
|
38
|
+
- `socket?: ServiceSocket` — 들어오면 `authInfo`·`clientName` 출처가 소켓 우선.
|
|
39
|
+
- `http?: { clientName; authTokenPayload? }` — HTTP 요청 출처.
|
|
40
|
+
- `legacy?: { clientName? }` — V1 레거시 출처(`clientName` 만).
|
|
41
|
+
|
|
42
|
+
컨텍스트 필드 의미는 [service-authoring.md](./service-authoring.md) 의 ServiceContext 절 참조. 테스트에서 컨텍스트를 직접 만들어 서비스 메서드를 단위 호출할 때 유용.
|
|
36
43
|
|
|
37
44
|
## ServiceSocket / createServiceSocket
|
|
38
45
|
|
|
39
46
|
단일 WebSocket 연결을 감싸 프로토콜 인코딩·ping/pong 연결 유지·이벤트 리스너 추적을 담당하는 인터페이스. `createServiceSocket(socket, clientId, clientName, connReq)` 로 생성.
|
|
40
47
|
|
|
41
|
-
- `connectedAtDateTime: DateTime`
|
|
42
|
-
- `
|
|
43
|
-
- `
|
|
44
|
-
- `
|
|
45
|
-
- `
|
|
46
|
-
- `
|
|
47
|
-
- `
|
|
48
|
-
- `
|
|
48
|
+
- `readonly connectedAtDateTime: DateTime` — 연결 성립 시각.
|
|
49
|
+
- `readonly clientName: string` — 연결 시 받은 클라이언트 이름.
|
|
50
|
+
- `readonly connReq: FastifyRequest` — 원본 연결 요청(원격 주소 등).
|
|
51
|
+
- `authTokenPayload?: AuthTokenPayload` — 소켓 `auth` 메시지로 검증된 토큰. 이후 그 소켓 요청의 `ctx.authInfo` 출처(set 으로 갱신).
|
|
52
|
+
- `close(): void` — 소켓 즉시 종료(terminate).
|
|
53
|
+
- `send(uuid, msg): Promise<number>` — 서버 메시지를 프로토콜로 인코딩해 전송, 보낸 바이트 수 반환(소켓 미개방이면 `0`).
|
|
54
|
+
- `addListener(key, eventName, info): void` — 이벤트 구독 등록. `key` = 구독 식별자, `info` = selector 매칭용 메타.
|
|
55
|
+
- `removeListener(key): void` — `key` 로 구독 1건 해제.
|
|
56
|
+
- `getEventListeners(eventName): { key, info }[]` — 해당 이벤트의 구독 목록 조회.
|
|
57
|
+
- `filterEventTargetKeys(targetKeys): string[]` — 주어진 키 중 이 소켓에 존재하는 것만 반환.
|
|
58
|
+
- `on("error" | "close" | "message", handler): void` — 소켓 이벤트 후킹. 5초 주기 ping, pong 미수신 시 자동 terminate.
|
|
49
59
|
|
|
50
60
|
## WebSocketHandler / createWebSocketHandler
|
|
51
61
|
|
|
@@ -58,26 +68,26 @@ function createWebSocketHandler(
|
|
|
58
68
|
|
|
59
69
|
여러 `ServiceSocket` 을 `clientId` 로 관리하고, 클라이언트 메시지를 `runMethod`(보통 `executeServiceMethod` 바인딩)로 라우팅하며, 이벤트를 브로드캐스트한다.
|
|
60
70
|
|
|
61
|
-
- `addSocket(socket, clientId, clientName, connReq)` — 연결 등록. 같은 `clientId` 의 기존 연결은 닫고 교체.
|
|
62
|
-
- `closeAll()` — 모든 연결 종료(서버 `close()` 시 호출).
|
|
71
|
+
- `addSocket(socket, clientId, clientName, connReq): void` — 연결 등록. 같은 `clientId` 의 기존 연결은 닫고 교체. 생성 중 예외 시 소켓 terminate.
|
|
72
|
+
- `closeAll(): void` — 모든 연결 종료(서버 `close()` 시 호출).
|
|
63
73
|
- `emit<TEventDef>(eventName, infoSelector, data): Promise<void>` — 전 소켓의 해당 이벤트 구독 중 `infoSelector(info)` 가 `true` 인 키에게만 `evt:on` 메시지 전송. `ServiceServer.emitEvent` 의 실제 구현.
|
|
64
|
-
- 처리하는 클라이언트 메시지 종류: `"<service>.<method>"`(RPC 호출), `evt:add`/`evt:remove`/`evt:gets`/`evt:emit`(이벤트
|
|
74
|
+
- 처리하는 클라이언트 메시지 종류: `"<service>.<method>"`(RPC 호출), `evt:add`/`evt:remove`/`evt:gets`/`evt:emit`(이벤트 구독·해제·조회·발생), `auth`(소켓 토큰 검증). 그 외는 `BAD_MESSAGE` 에러 응답. 처리 중 예외는 `INTERNAL_ERROR` 응답이며, `DEV` 환경에서만 에러 스택 포함.
|
|
65
75
|
|
|
66
76
|
## HTTP / 정적 / 업로드 핸들러
|
|
67
77
|
|
|
68
78
|
`fastify` 라우트에 직접 물리는 저수준 함수들. 커스텀 라우트를 짤 때만 직접 사용.
|
|
69
79
|
|
|
70
|
-
- `handleHttpRequest(req, reply, jwtSecret, runMethod)` — `/api/:service/:method` 처리. `x-sd-client-name` 헤더 필수, `Authorization: Bearer <token>` 검증(실패 시 401)
|
|
71
|
-
- `handleUpload(req, reply, rootPath, jwtSecret)` — `/upload` multipart 처리. 인증 토큰 필수(없거나 무효면 401). 파일을 `rootPath/www/uploads/<uuid><ext>` 로 저장하고 `ServiceUploadResult[]`(`{ path, filename, size }`) 반환. 도중 실패 시 그 요청에서 저장한 파일을 모두 롤백 삭제 후 500.
|
|
72
|
-
- `handleStaticFile(req, reply, rootPath, urlPath)` — `rootPath/www` 하위 정적 파일 전송. `www` 밖 경로는 차단(throw), 디렉터리면 슬래시 리다이렉트 후 `index.html`, `.` 으로 시작하는 숨김 파일은 403, 미존재는 404 HTML 응답.
|
|
80
|
+
- `handleHttpRequest(req, reply, jwtSecret, runMethod)` — `/api/:service/:method` 처리. `x-sd-client-name` 헤더 필수, `Authorization: Bearer <token>` 검증(실패 시 401). GET 은 `?json=` 쿼리, POST 는 배열 본문에서 파라미터를 받아 `runMethod` 실행. 본문이 배열이 아니면 400, 그 외 HTTP 메서드는 405.
|
|
81
|
+
- `handleUpload(req, reply, rootPath, jwtSecret)` — `/upload` multipart 처리. multipart 아니면 400, 인증 토큰 필수(없거나 무효면 401). 파일을 `rootPath/www/uploads/<uuid><ext>` 로 저장하고 `ServiceUploadResult[]`(`{ path, filename, size }`) 반환. 도중 실패 시 그 요청에서 저장한 파일을 모두 롤백 삭제 후 500.
|
|
82
|
+
- `handleStaticFile(req, reply, rootPath, urlPath)` — `rootPath/www` 하위 정적 파일 전송. `www` 밖 경로는 차단(throw), 디렉터리면 슬래시 리다이렉트 후 `index.html`, `.` 으로 시작하는 숨김 파일은 403, 미존재는 404, 그 외 전송 실패는 500 HTML 응답.
|
|
73
83
|
|
|
74
84
|
## ServerProtocolWrapper / createServerProtocolWrapper
|
|
75
85
|
|
|
76
|
-
메시지 인코딩/디코딩을 크기·내용에 따라 worker 스레드와 메인 스레드로 자동 분배하는 래퍼. `createServerProtocolWrapper()` 로 생성(worker 는 지연
|
|
86
|
+
메시지 인코딩/디코딩을 크기·내용에 따라 worker 스레드와 메인 스레드로 자동 분배하는 래퍼. `createServerProtocolWrapper()` 로 생성(worker 는 지연 싱글턴, 소켓 간 공유).
|
|
77
87
|
|
|
78
|
-
- `encode(uuid, message): Promise<{ chunks: Bytes[]; totalSize: number }>` — `body` 에 `Uint8Array` 가 있으면 worker, 아니면 메인 스레드에서 인코딩.
|
|
79
|
-
- `decode(bytes): Promise<ServiceMessageDecodeResult
|
|
80
|
-
- `dispose()` — 프로토콜 리소스 해제.
|
|
88
|
+
- `encode(uuid, message): Promise<{ chunks: Bytes[]; totalSize: number }>` — `body` 에 `Uint8Array` 가 있으면(단일 또는 배열 요소 중 하나라도) worker, 아니면 메인 스레드에서 인코딩.
|
|
89
|
+
- `decode(bytes): Promise<ServiceMessageDecodeResult<ServiceMessage>>` — 청크 재조립(stateful)은 항상 메인 스레드 단일 누적기에서, 재조립 완료 후 30KB 초과 JSON 파싱(stateless)만 worker 위임. 진행 중이면 `{ type: "progress" }`, 완료면 `{ type: "complete", uuid, message }`.
|
|
90
|
+
- `dispose(): void` — 프로토콜 리소스 해제.
|
|
81
91
|
|
|
82
92
|
## getConfig
|
|
83
93
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @simplysm/service-server — V1 레거시 지원
|
|
2
2
|
|
|
3
|
-
`ver !== "2"`(구버전) WebSocket 클라이언트를 받기 위한 레거시 핸들러. 주로 구버전 앱의 자동업데이트(`SdAutoUpdateService.getLastVersion`) 요청을 처리한다. `ServiceServer` 는 ver=2 가 아닌 연결을 자동으로 이 핸들러로 넘긴다(`AutoUpdate` 서비스나 `legacyV1Handlers` 가 있을 때만, 둘 다 없으면 연결 거부). `ServiceServerOptions.legacyV1Handlers` 로 커스텀 핸들러를 끼울 때만 직접 다룬다.
|
|
3
|
+
`ver !== "2"`(구버전) WebSocket 클라이언트를 받기 위한 레거시 핸들러. 주로 구버전 앱의 자동업데이트(`SdAutoUpdateService.getLastVersion`) 요청을 처리한다. `ServiceServer` 는 ver=2 가 아닌 연결을 자동으로 이 핸들러로 넘긴다(`AutoUpdate` 서비스나 `legacyV1Handlers` 가 있을 때만, 둘 다 없으면 코드 1008 로 연결 거부). `ServiceServerOptions.legacyV1Handlers` 로 커스텀 핸들러를 끼울 때만 직접 다룬다.
|
|
4
4
|
|
|
5
5
|
## handleV1Connection
|
|
6
6
|
|
|
@@ -9,10 +9,10 @@ function handleV1Connection(socket, autoUpdateMethods: V1AutoUpdateMethods, clie
|
|
|
9
9
|
function handleV1Connection(socket, options: V1ConnectionOptions): void;
|
|
10
10
|
```
|
|
11
11
|
|
|
12
|
-
V1 WebSocket 연결 1건을 받아 연결 알림(`{ name: "connected" }`) 전송 후 메시지를 처리한다. 처리 순서: 커스텀 핸들러들 → (미처리 시) `SdAutoUpdateService.getLastVersion` fallback → 그래도 미처리면 `UPGRADE_REQUIRED` 에러 응답. 메시지 파싱·처리 중 예외는 잡아 warn 로그만 남기고 응답하지
|
|
12
|
+
V1 WebSocket 연결 1건을 받아 연결 알림(`{ name: "connected" }`) 전송 후 메시지를 처리한다. 처리 순서: 커스텀 핸들러들 → (미처리 시) `SdAutoUpdateService.getLastVersion` fallback → 그래도 미처리면 `UPGRADE_REQUIRED` 에러 응답. 메시지 파싱·처리 중 예외는 잡아 warn 로그만 남기고 응답하지 않는다.
|
|
13
13
|
|
|
14
14
|
- `socket: WebSocket` — `ws` 의 원시 소켓.
|
|
15
|
-
- 2번째
|
|
15
|
+
- 2번째 인자 — `V1AutoUpdateMethods` 객체(자동업데이트만 응대)이거나 `V1ConnectionOptions`(핸들러·팩토리 포함). `"getLastVersion" in arg` 로 분기.
|
|
16
16
|
- `clientNameSetter?` — 첫 시그니처에서만. 요청의 `clientName` 을 외부에 통지하는 콜백.
|
|
17
17
|
|
|
18
18
|
## V1ConnectionOptions
|
|
@@ -21,15 +21,15 @@ V1 WebSocket 연결 1건을 받아 연결 알림(`{ name: "connected" }`) 전송
|
|
|
21
21
|
- `serviceContextFactory?: (request: V1Request) => ServiceContext` — 요청마다 컨텍스트를 새로 만들 때. `serviceContext` 보다 우선.
|
|
22
22
|
- `handlers?: V1RequestHandler[]` — 커스텀 요청 핸들러 목록. 앞에서부터 호출되며 첫 `handled: true` 에서 멈춤. 핸들러가 있는데 컨텍스트가 없으면 throw.
|
|
23
23
|
- `autoUpdateMethods?: V1AutoUpdateMethods` — 자동업데이트 fallback 구현(고정).
|
|
24
|
-
- `autoUpdateMethodsFactory?: (ctx: V1RequestHandlerContext) => V1AutoUpdateMethods` — 요청마다 fallback 구현 생성. 지정 시 `autoUpdateMethods` 대신
|
|
24
|
+
- `autoUpdateMethodsFactory?: (ctx: V1RequestHandlerContext) => V1AutoUpdateMethods` — 요청마다 fallback 구현 생성. 지정 시 `autoUpdateMethods` 대신 사용(컨텍스트 없으면 throw).
|
|
25
25
|
- `clientNameSetter?: (clientName: string | undefined) => void` — 매 요청 `clientName` 통지 콜백.
|
|
26
26
|
|
|
27
27
|
## V1RequestHandler 와 관련 타입
|
|
28
28
|
|
|
29
|
-
- `V1Request` — `{ uuid: string; command: string; params: unknown[]; clientName?: string }`. 구버전 클라이언트가 보내는 요청 형태.
|
|
30
|
-
- `V1Response` — `{ name: "response"; reqUuid: string; state: "success" | "error"; body: unknown }`. 서버가 돌려보내는 응답
|
|
29
|
+
- `V1Request` — `{ uuid: string; command: string; params: unknown[]; clientName?: string }`. 구버전 클라이언트가 보내는 요청 형태. `command` 는 `"<service>.<method>"` 형태의 명령 키.
|
|
30
|
+
- `V1Response` — `{ name: "response"; reqUuid: string; state: "success" | "error"; body: unknown }`. 서버가 돌려보내는 응답 형태. `state` = 응답 상태로 `"success"`(정상)·`"error"`(오류) 구분.
|
|
31
31
|
- `V1RequestHandlerContext` — `{ request: V1Request; serviceContext: ServiceContext }`. 핸들러가 받는 인자.
|
|
32
|
-
- `V1RequestHandlerResult` — `{ handled: true; state?: "success"|"error"; body: unknown } | { handled: false }`. `handled
|
|
32
|
+
- `V1RequestHandlerResult` — `{ handled: true; state?: "success" | "error"; body: unknown } | { handled: false }`. `handled` = 이 핸들러가 요청을 처리했는지. `false` 면 다음 핸들러·fallback 으로 넘어가고, `true` 면 그 `state`(기본 `"success"`)·`body` 로 즉시 응답.
|
|
33
33
|
- `V1RequestHandler` — `(ctx: V1RequestHandlerContext) => V1RequestHandlerResult | Promise<V1RequestHandlerResult>`. 동기·비동기 모두 가능.
|
|
34
34
|
- `V1AutoUpdateMethods` — `{ getLastVersion: (platform: string) => Promise<unknown> | unknown }`. `SdAutoUpdateService.getLastVersion` 명령의 fallback 인터페이스.
|
|
35
35
|
|
|
@@ -46,4 +46,4 @@ const server = createServiceServer({
|
|
|
46
46
|
});
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
주의: `legacyV1Handlers` 도 없고 `AutoUpdate`(`SdAutoUpdateService`) 서비스도 등록 안 됐으면 ver
|
|
49
|
+
주의: `legacyV1Handlers` 도 없고 `AutoUpdate`(`SdAutoUpdateService`) 서비스도 등록 안 됐으면 ver≠2 연결은 코드 1008 로 즉시 거부된다.
|
|
@@ -1,40 +1,17 @@
|
|
|
1
1
|
# @simplysm/storage
|
|
2
2
|
|
|
3
|
-
FTP
|
|
3
|
+
Node.js 환경에서 FTP/FTPS/SFTP 원격 스토리지에 접속해 파일·디렉토리를 업로드·다운로드·조회·삭제하는 라이브러리. 프로토콜별 구현을 동일 인터페이스(`StorageClient`)로 통일하고, `StorageFactory.connect` 콜백 패턴으로 연결/종료를 자동 관리한다. 전 구현이 Node 전용(`basic-ftp`·`ssh2-sftp-client`·`fs`/`os`/`path`/`stream` 의존)이라 브라우저에서 사용 불가.
|
|
4
4
|
|
|
5
5
|
## 사용 트리거 인덱스
|
|
6
6
|
|
|
7
|
-
- **StorageFactory** —
|
|
8
|
-
- **StorageClient** —
|
|
9
|
-
- **StorageConnConfig** —
|
|
10
|
-
- **
|
|
11
|
-
- **FileInfo** — `list()` 가 돌려주는 항목 구조(이름·파일여부)를 확인할 때.
|
|
12
|
-
- **FtpStorageClient / SftpStorageClient** — 팩토리 없이 클라이언트를 직접 인스턴스화·재연결 제어해야 할 때(비권장).
|
|
7
|
+
- **StorageFactory.connect** — 프로토콜·접속정보로 연결을 열고 콜백 안에서 파일 작업 후 자동 종료할 때. 직접 클라이언트 인스턴스화보다 권장되는 진입점.
|
|
8
|
+
- **StorageClient** — connect 콜백이 받는 파일 작업 인터페이스(mkdir/list/put/readFile/exists/remove/rename/uploadDir 등). 어떤 메서드를 호출할 수 있는지 확인할 때.
|
|
9
|
+
- **StorageConnConfig / StorageProtocol / FileInfo** — connect 인자 형태, 지원 프로토콜 리터럴, list 반환 항목 형태를 확인할 때.
|
|
10
|
+
- **FtpStorageClient / SftpStorageClient** — 팩토리 없이 연결을 직접 잡거나(수명·재연결 직접 제어), 프로토콜별 인증·동작 차이를 확인할 때. 비권장.
|
|
13
11
|
|
|
14
|
-
##
|
|
12
|
+
## StorageFactory (진입점)
|
|
15
13
|
|
|
16
|
-
|
|
17
|
-
type StorageProtocol = "ftp" | "ftps" | "sftp";
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
- `"ftp"` — 평문 FTP. 보안 채널 없이 접속(`FtpStorageClient` 를 `secure=false` 로 생성). 내부망·테스트 환경에서만 권장.
|
|
21
|
-
- `"ftps"` — TLS 로 암호화된 FTP(`FtpStorageClient` 를 `secure=true` 로 생성). 외부망 FTP 접속 시.
|
|
22
|
-
- `"sftp"` — SSH 기반 SFTP(`SftpStorageClient`). password 미지정 시 `~/.ssh/id_ed25519` 키 + SSH agent 로 인증. 가장 일반적인 보안 전송.
|
|
23
|
-
|
|
24
|
-
## StorageConnConfig
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
interface StorageConnConfig { host: string; port?: number; user?: string; password?: string; }
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
- `host: string` — 접속 대상 서버 호스트명 또는 IP. 필수.
|
|
31
|
-
- `port?: number` — 접속 포트. 미지정 시 각 라이브러리 기본값(FTP 21, SFTP 22) 사용. 비표준 포트면 명시.
|
|
32
|
-
- `user?: string` — 로그인 사용자명. 미지정 시 익명/기본 사용자.
|
|
33
|
-
- `password?: string` — 로그인 비밀번호. **SFTP 에서 이 값이 `null`/미지정이면** password 인증 대신 `~/.ssh/id_ed25519` 개인키와 SSH agent(`SSH_AUTH_SOCK` 환경변수가 설정된 경우 `agent` 옵션) 로 인증 시도하고, 키 파싱 실패(암호화 키 등) 시 agent 단독으로 재시도. FTP/FTPS 에서는 미지정 시 라이브러리 기본(익명) 처리. 키 인증을 쓰려면 password 를 넘기지 말 것.
|
|
34
|
-
|
|
35
|
-
## StorageFactory
|
|
36
|
-
|
|
37
|
-
스토리지 접속 진입점. 연결 생성 → 콜백 실행 → 자동 종료를 묶어 처리한다. 인스턴스를 직접 만들 필요 없이 정적 `connect` 만 사용.
|
|
14
|
+
스토리지 접속 진입점. 인스턴스를 만들 필요 없이 정적 `connect` 만 사용한다. 연결 생성 → 콜백 실행 → 자동 종료를 한 번에 묶어 처리한다.
|
|
38
15
|
|
|
39
16
|
```ts
|
|
40
17
|
class StorageFactory {
|
|
@@ -46,10 +23,10 @@ class StorageFactory {
|
|
|
46
23
|
}
|
|
47
24
|
```
|
|
48
25
|
|
|
49
|
-
- `type: StorageProtocol` — 사용할 프로토콜. `"sftp"` → `SftpStorageClient`, `"ftps"` → `FtpStorageClient(secure=true)`, `"ftp"` → `FtpStorageClient(secure=false)` 를
|
|
50
|
-
- `config: StorageConnConfig` — 접속
|
|
26
|
+
- `type: StorageProtocol` — 사용할 프로토콜. 내부에서 `"sftp"` → `SftpStorageClient`, `"ftps"` → `FtpStorageClient(secure=true)`, `"ftp"` → `FtpStorageClient(secure=false)` 를 생성. 보안 전송이 필요하면 `"ftps"`/`"sftp"`.
|
|
27
|
+
- `config: StorageConnConfig` — 접속 설정(host/port/user/password). 아래 StorageConnConfig 참조.
|
|
51
28
|
- `fn: (storage: StorageClient) => R | Promise<R>` — 연결된 `StorageClient` 를 받아 파일 작업을 수행하는 콜백. 반환값이 그대로 `connect` 의 결과(`Promise<R>`) 가 됨. 동기·비동기 모두 허용.
|
|
52
|
-
- 동작: `client.connect()` 후 `fn` 실행, `finally` 에서 `client.close()` 호출하며 종료 오류는 무시(이미 종료된 경우 대비). 콜백에서 예외가 나도 연결은 반드시 닫히고 예외는 그대로 전파됨.
|
|
29
|
+
- 동작: `client.connect(config)` 후 `fn` 실행, `finally` 에서 `client.close()` 호출하며 종료 중 오류는 무시(이미 종료된 경우 대비). 콜백에서 예외가 나도 연결은 반드시 닫히고 예외는 그대로 전파됨.
|
|
53
30
|
|
|
54
31
|
```ts
|
|
55
32
|
const names = await StorageFactory.connect("sftp", { host: "10.0.0.1", user: "u", password: "p" }, async (s) => {
|
|
@@ -59,9 +36,47 @@ const names = await StorageFactory.connect("sftp", { host: "10.0.0.1", user: "u"
|
|
|
59
36
|
});
|
|
60
37
|
```
|
|
61
38
|
|
|
62
|
-
|
|
39
|
+
주의: 콜백 밖으로 `storage` 를 유출해 나중에 호출하면 이미 닫힌 연결이므로 실패한다.
|
|
40
|
+
|
|
41
|
+
## 연결·작업 타입
|
|
42
|
+
|
|
43
|
+
### StorageConnConfig
|
|
44
|
+
|
|
45
|
+
원격 서버 접속 정보.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
interface StorageConnConfig { host: string; port?: number; user?: string; password?: string; }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
- `host: string` — 접속 대상 서버 호스트명 또는 IP. 필수.
|
|
52
|
+
- `port?: number` — 접속 포트. 미지정 시 각 라이브러리 기본값(FTP 21, SFTP 22) 사용. 비표준 포트면 명시.
|
|
53
|
+
- `user?: string` — 로그인 사용자명. 미지정 시 익명/기본 사용자.
|
|
54
|
+
- `password?: string` — 로그인 비밀번호. **SFTP 에서 이 값이 미지정이면** password 인증 대신 `~/.ssh/id_ed25519` 개인키 + SSH agent(`SSH_AUTH_SOCK` 환경변수가 있으면 `agent` 옵션)로 인증을 시도하고, 키 파싱 실패(암호화 키 등) 시 agent 단독 재시도. FTP/FTPS 에서는 미지정 시 라이브러리 기본(익명) 처리. SFTP 키 인증을 쓰려면 password 를 넘기지 말 것.
|
|
55
|
+
|
|
56
|
+
### StorageProtocol
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
type StorageProtocol = "ftp" | "ftps" | "sftp";
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- `"ftp"` — 평문 FTP. 보안 채널 없음(`FtpStorageClient` 를 `secure=false` 로 생성). 내부망·테스트에서만 권장.
|
|
63
|
+
- `"ftps"` — TLS 로 암호화된 FTP(`FtpStorageClient` 를 `secure=true` 로 생성). 외부망 FTP 접속 시.
|
|
64
|
+
- `"sftp"` — SSH 기반 SFTP(`SftpStorageClient`). 가장 일반적인 보안 전송.
|
|
65
|
+
|
|
66
|
+
### FileInfo
|
|
63
67
|
|
|
64
|
-
`
|
|
68
|
+
`list()` 가 반환하는 항목.
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
interface FileInfo { name: string; isFile: boolean; }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- `name: string` — 항목 이름(파일명 또는 디렉토리명, 경로 아님).
|
|
75
|
+
- `isFile: boolean` — 파일이면 `true`, 디렉토리면 `false`. 디렉토리 탐색 시 파일만 골라 처리하는 분기 기준으로 사용. FTP 는 라이브러리의 `isFile`, SFTP 는 항목 type 이 `"-"`(일반 파일)인 경우만 `true`(디렉토리·심볼릭 링크는 `false`).
|
|
76
|
+
|
|
77
|
+
### StorageClient
|
|
78
|
+
|
|
79
|
+
`connect` 콜백 안에서 받는 파일 작업 인터페이스. `FtpStorageClient`·`SftpStorageClient` 가 구현. 모든 메서드는 `Promise` 반환. 경로는 원격 기준 경로 문자열.
|
|
65
80
|
|
|
66
81
|
```ts
|
|
67
82
|
interface StorageClient {
|
|
@@ -83,33 +98,24 @@ interface StorageClient {
|
|
|
83
98
|
- `rename(fromPath, toPath)` — 파일/디렉토리 경로 이동·이름 변경. `toPath` 로 옮기거나 새 이름을 부여할 때.
|
|
84
99
|
- `list(dirPath)` — 디렉토리 내 항목을 `FileInfo[]` 로 반환. 목록을 파일/폴더로 갈라 처리할 때.
|
|
85
100
|
- `readFile(filePath)` — 원격 파일 전체를 `Bytes`(Uint8Array) 로 메모리에 다운로드(스트리밍 아님 — 큰 파일은 메모리 부담). 텍스트가 필요하면 호출 측에서 디코딩. SFTP 는 응답이 예상 타입(Buffer/string) 이 아니면 `SdError` throw.
|
|
86
|
-
- `exists(filePath)` — 파일/디렉토리 존재 여부. **모든 예외(부모 없음·권한·네트워크 오류 포함) 에 대해 `false` 반환** —
|
|
87
|
-
- `put(localPathOrBuffer, storageFilePath)` — 단일 파일 업로드. 첫 인자가 `string` 이면 로컬 파일 경로에서, `Bytes` 면 메모리 바이트에서 업로드.
|
|
88
|
-
- `uploadDir(fromPath, toPath)` — 로컬 디렉토리 전체를 원격 디렉토리로 재귀 업로드. 빌드 산출물
|
|
101
|
+
- `exists(filePath)` — 파일/디렉토리 존재 여부. **모든 예외(부모 없음·권한·네트워크 오류 포함) 에 대해 `false` 반환** — throw 없음이므로 `true` 만 "확실히 존재"로 신뢰한다. FTP 는 `size()` 로 파일을 O(1) 확인 후 실패 시 부모 디렉토리 목록으로 디렉토리 확인(슬래시 없는 경로는 루트 `/` 기준이라 항목 많은 디렉토리에서는 느려질 수 있음). SFTP 는 `exists()` 결과가 문자열(`'d'`/`'-'`/`'l'`)이면 존재로 판정. 작업 전 분기 확인에 사용.
|
|
102
|
+
- `put(localPathOrBuffer, storageFilePath)` — 단일 파일 업로드. 첫 인자가 `string` 이면 로컬 파일 경로에서, `Bytes` 면 메모리 바이트에서 업로드. 생성한 콘텐츠를 바로 올릴 땐 `Bytes` 로 전달.
|
|
103
|
+
- `uploadDir(fromPath, toPath)` — 로컬 디렉토리 전체를 원격 디렉토리로 재귀 업로드. 빌드 산출물 폴더를 통째로 배포할 때.
|
|
89
104
|
- `remove(filePath)` — 원격 파일 삭제.
|
|
90
105
|
- `close()` — 연결 종료. 이미 종료/미연결 상태에서 호출해도 오류 없음. 종료 후 같은 인스턴스에서 `connect()` 로 재연결 가능. `StorageFactory.connect` 사용 시 직접 호출 불필요.
|
|
91
106
|
|
|
92
107
|
미연결 상태에서 작업 메서드를 호출하면 모든 구현체가 `SdError`("연결되어 있지 않습니다") throw.
|
|
93
108
|
|
|
94
|
-
## FileInfo
|
|
95
|
-
|
|
96
|
-
```ts
|
|
97
|
-
interface FileInfo { name: string; isFile: boolean; }
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
- `name: string` — 항목 이름(파일명 또는 디렉토리명, 경로 아님).
|
|
101
|
-
- `isFile: boolean` — 파일이면 `true`, 디렉토리면 `false`. 디렉토리 탐색 시 파일만 골라 처리하는 분기 기준으로 사용. SFTP 는 항목 type 이 `"-"` 인 경우만 `true`(디렉토리·심볼릭 링크는 `false`).
|
|
102
|
-
|
|
103
109
|
## FtpStorageClient / SftpStorageClient (직접 사용, 비권장)
|
|
104
110
|
|
|
105
|
-
`StorageClient` 직접 구현체. 보통은 `StorageFactory.connect` 로 충분하며, 연결 수명을 콜백 밖에서 수동으로 다뤄야 할 때만 직접 생성한다.
|
|
111
|
+
`StorageClient` 직접 구현체. 보통은 `StorageFactory.connect` 로 충분하며, 연결 수명을 콜백 밖에서 수동으로 다뤄야 할 때만 직접 생성한다.
|
|
106
112
|
|
|
107
113
|
```ts
|
|
108
114
|
new FtpStorageClient(secure?: boolean) // secure=true → FTPS, 생략/false → 평문 FTP
|
|
109
115
|
new SftpStorageClient()
|
|
110
116
|
```
|
|
111
117
|
|
|
112
|
-
- `FtpStorageClient` 의 `secure` 생성자 인자 — `true` 면 TLS(FTPS), 생략/`false` 면 평문 FTP. 팩토리는 `ftps`→`true`, `ftp`→`false` 로 매핑.
|
|
118
|
+
- `FtpStorageClient` 의 `secure` 생성자 인자 — `true` 면 TLS(FTPS), 생략/`false`(기본) 면 평문 FTP. 팩토리는 `"ftps"`→`true`, `"ftp"`→`false` 로 매핑.
|
|
113
119
|
- `SftpStorageClient` 는 생성자 인자 없음. password 미지정 시 키/agent 인증 경로를 탄다(StorageConnConfig 의 `password` 풀이 참조).
|
|
114
120
|
- 직접 사용 시 `connect()` → 작업 → `close()` 순으로 호출하고, 예외 발생 시에도 `close()` 가 호출되도록 `try/finally` 로 감쌀 것. 동일 인스턴스에서 `close()` 없이 `connect()` 를 재호출하면 연결 누수로 throw.
|
|
115
121
|
|