@simplysm/sd-claude 14.0.86 → 14.0.88

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 (68) hide show
  1. package/claude/references/sd-simplysm14/README.md +17 -18
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +61 -0
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +119 -0
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +50 -0
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +44 -0
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +55 -0
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +74 -0
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +55 -0
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +115 -0
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +64 -0
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +43 -0
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +70 -0
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +78 -0
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +80 -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 +67 -0
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +83 -0
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +79 -0
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +138 -0
  21. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +72 -0
  22. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +95 -0
  23. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +47 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +53 -0
  25. package/claude/references/sd-simplysm14/apis/core-node/README.md +14 -0
  26. package/claude/references/sd-simplysm14/apis/core-node/consola.md +51 -0
  27. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +39 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +38 -0
  29. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +86 -0
  30. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +42 -0
  31. package/claude/references/sd-simplysm14/apis/core-node/worker.md +54 -0
  32. package/claude/references/sd-simplysm14/apis/excel/README.md +43 -0
  33. package/claude/references/sd-simplysm14/apis/excel/cell.md +54 -0
  34. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +51 -0
  35. package/claude/references/sd-simplysm14/apis/excel/style.md +67 -0
  36. package/claude/references/sd-simplysm14/apis/excel/utils.md +35 -0
  37. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +97 -0
  38. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +83 -0
  39. package/claude/references/sd-simplysm14/apis/lint/README.md +43 -0
  40. package/claude/references/sd-simplysm14/apis/lint/rules.md +90 -0
  41. package/claude/references/sd-simplysm14/apis/orm-common/README.md +67 -0
  42. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +80 -0
  43. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +113 -0
  44. package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +29 -0
  45. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +111 -0
  46. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +162 -0
  47. package/claude/references/sd-simplysm14/apis/orm-common/types.md +52 -0
  48. package/claude/references/sd-simplysm14/apis/orm-node/README.md +53 -0
  49. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +94 -0
  50. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +29 -0
  51. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +70 -0
  52. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +173 -0
  53. package/claude/references/sd-simplysm14/apis/service-client/README.md +152 -0
  54. package/claude/references/sd-simplysm14/apis/service-client/orm.md +45 -0
  55. package/claude/references/sd-simplysm14/apis/service-client/transport.md +36 -0
  56. package/claude/references/sd-simplysm14/apis/service-common/README.md +70 -0
  57. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +48 -0
  58. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +72 -0
  59. package/claude/references/sd-simplysm14/apis/service-server/README.md +102 -0
  60. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +74 -0
  61. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +51 -0
  62. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +50 -0
  63. package/claude/references/sd-simplysm14/apis/storage/README.md +114 -0
  64. package/claude/rules/sd-design-rules.md +11 -0
  65. package/claude/skills/sd-docs/SKILL.md +17 -29
  66. package/claude/skills/sd-docs/references/{subagent-prompt.md → doc-rules.md} +25 -40
  67. package/claude/skills/sd-spec/SKILL.md +2 -2
  68. package/package.json +1 -1
@@ -0,0 +1,35 @@
1
+ # @simplysm/excel — ExcelUtils
2
+
3
+ 셀 주소(A1 표기) ↔ 좌표 변환, Excel 날짜 시리얼 ↔ 타임스탬프 변환, 숫자형식 코드/ID/이름 상호 변환이 필요할 때 읽는다. 모든 메서드는 `static` 이라 인스턴스 없이 `ExcelUtils.xxx()` 로 호출.
4
+
5
+ ## 주소 변환
6
+
7
+ - `stringifyAddr(point: ExcelAddressPoint): string` — 0 기반 좌표 `{r,c}` → `"A1"`. 로그·범위 ref 조립에 사용.
8
+ - `stringifyRowAddr(r: number): string` — 행 인덱스 → 행 문자열(0 → `"1"`).
9
+ - `stringifyColAddr(c: number): string` — 열 인덱스 → 열 문자(0 → `"A"`, 26 → `"AA"`). 0~16383 범위 밖이면 throw.
10
+ - `parseRowAddr(addr: string): number` — 주소 문자열에서 0 기반 행 인덱스(`"A3"` → 2). 파싱 실패 시 throw.
11
+ - `parseColAddr(addr: string): number` — 주소 문자열에서 0 기반 열 인덱스(`"B3"` → 1).
12
+ - `parseCellAddr(addr: string): ExcelAddressPoint` — 셀 주소 → 좌표(`"B3"` → `{r:2,c:1}`).
13
+ - `parseRangeAddr(rangeAddr: string): ExcelAddressRangePoint` — 범위 주소 → 좌표 범위(`"A1:C3"` → `{s:{r:0,c:0}, e:{r:2,c:2}}`). `:` 없으면 단일 셀을 `s===e` 로.
14
+ - `stringifyRangeAddr(point: ExcelAddressRangePoint): string` — 좌표 범위 → 문자열(`"A1:C3"`). `s===e` 면 단일 셀 문자열만 반환.
15
+
16
+ ## 날짜 변환
17
+
18
+ Excel 은 1900-01-01 을 1 로 계산(1899-12-30 이 날짜 0). 변환 시 로컬 타임존을 보정한다.
19
+
20
+ - `convertTimeTickToNumber(tick: number): number` — JS 타임스탬프(ms) → Excel 날짜 시리얼 숫자.
21
+ - `convertNumberToTimeTick(value: number): number` — Excel 날짜 시리얼 숫자 → JS 타임스탬프(ms).
22
+
23
+ ## 숫자형식 변환
24
+
25
+ - `convertNumFmtCodeToName(numFmtCode: string): ExcelNumberFormat` — formatCode 문자열을 분석해 `"number"`/`"DateOnly"`/`"DateTime"`/`"Time"`/`"string"` 판별. `"General"` 은 `"number"`. 시간 문맥의 `mm`(분)은 날짜 판별에서 제외. 어디에도 해당 안 되면 throw.
26
+ - `convertNumFmtIdToName(numFmtId: number): ExcelNumberFormat` — Excel 내장 numFmtId 를 형식명으로. 범위: 0~13·37~40·48 → `"number"`, 14~17·27~31·34~36·50~58 → `"DateOnly"`, 22 → `"DateTime"`, 18~21·32~33·45~47 → `"Time"`, 49 → `"string"`. 범위 밖이면 throw.
27
+ - `convertNumFmtNameToId(numFmtName: ExcelNumberFormat): number` — 역방향. `"number"` → 0, `"DateOnly"` → 14, `"DateTime"` → 22, `"Time"` → 18, `"string"` → 49.
28
+
29
+ ## 사용 예
30
+
31
+ ```typescript
32
+ const { r, c } = ExcelUtils.parseCellAddr("C5"); // { r: 4, c: 2 }
33
+ const ref = ExcelUtils.stringifyRangeAddr({ s: { r: 0, c: 0 }, e: { r: 9, c: 1 } }); // "A1:B10"
34
+ const serial = ExcelUtils.convertTimeTickToNumber(Date.now()); // Excel 날짜 숫자
35
+ ```
@@ -0,0 +1,97 @@
1
+ # @simplysm/excel — ExcelWorkbook / ExcelWorksheet
2
+
3
+ .xlsx 파일을 열거나 새로 만들고, 시트를 추가·조회하고, 시트 단위로 데이터 테이블/매트릭스/이미지/뷰를 다루고, 바이트/Blob 로 내보낼 때 함께 읽는 묶음. 워크북은 내부 ZIP 리소스를 lazy-load 하므로 사용 후 반드시 `close()` 해야 한다.
4
+
5
+ ## ExcelWorkbook
6
+
7
+ ```typescript
8
+ new ExcelWorkbook(arg?: Blob | Bytes)
9
+ ```
10
+
11
+ - 생성자 `arg` — 기존 .xlsx 데이터(`Blob` 또는 `Uint8Array`). 생략하면 빈 워크북(ContentTypes/rels/workbook 골격)을 새로 만든다. 기존 파일 편집이면 전달, 새 파일 생성이면 생략.
12
+
13
+ 메서드:
14
+
15
+ - `getWorksheetNames(): Promise<string[]>` — 워크북의 모든 시트 이름을 정의 순서로 반환.
16
+ - `addWorksheet(name: string): Promise<ExcelWorksheet>` — 새 시트를 만들어 반환. ContentTypes·workbook rels 도 함께 갱신. `name` = 추가할 시트 이름.
17
+ - `getWorksheet(nameOrIndex: string | number): Promise<ExcelWorksheet>` — 이름(string) 또는 0 기반 인덱스(number)로 시트 조회. 같은 시트는 캐시돼 동일 인스턴스 반환. 없으면 throw.
18
+ - `setDefaultStyle(opts: ExcelStyleOptions): Promise<void>` — 워크북 전역 default 셀 스타일. fontId/fillId/borderId 를 명시하지 않은 모든 셀에 적용. 자세히: [style.md](./style.md).
19
+ - `toBytes(): Promise<Bytes>` — 워크북을 ZIP 직렬화해 바이트로 반환.
20
+ - `toBlob(): Promise<Blob>` — `toBytes()` 결과를 xlsx MIME 의 `Blob` 으로 래핑. 브라우저 다운로드용.
21
+ - `close(): Promise<void>` — ZIP 리더·내부 캐시 해제. 호출 후 워크북 사용 불가. 이미 닫혔으면 no-op(안전). 미호출 시 리소스 누수.
22
+ - `readonly zipCache: ZipCache` — 내부 ZIP 캐시. 일반 사용에서는 직접 다루지 않음.
23
+
24
+ `close()` 외 모든 메서드는 닫힌 워크북에서 호출 시 throw.
25
+
26
+ ### 사용 예
27
+
28
+ ```typescript
29
+ const wb = new ExcelWorkbook(bytes); // 기존 파일 열기
30
+ try {
31
+ const ws = await wb.getWorksheet(0);
32
+ const table = await ws.getDataTable({ checkEndColIndex: 0 });
33
+ } finally {
34
+ await wb.close();
35
+ }
36
+ ```
37
+
38
+ ## ExcelWorksheet
39
+
40
+ `wb.getWorksheet` / `wb.addWorksheet` 로 얻는다. 행/열/셀 접근, 복사, 데이터 변환, 뷰, 조건부 서식, 이미지를 제공.
41
+
42
+ ### 이름
43
+
44
+ - `getName(): Promise<string>` — 시트 이름 반환(못 찾으면 throw).
45
+ - `setName(newName: string): Promise<void>` — 시트 이름 변경.
46
+
47
+ ### 셀/행/열 접근 (모두 0 기반)
48
+
49
+ - `row(r): ExcelRow` — `r` 행 객체(캐시). 행 단위 순회·셀 접근. 자세히: [cell.md](./cell.md).
50
+ - `col(c): ExcelCol` — `c` 열 객체(캐시). 열 단위 순회·너비 설정.
51
+ - `cell(r, c): ExcelCell` — 단일 셀 객체(캐시). 값/수식/스타일/병합.
52
+ - `getRange(): Promise<ExcelAddressRangePoint>` — 시트의 데이터 범위(`{s, e}`).
53
+ - `getCells(): Promise<ExcelCell[][]>` — 데이터 범위 전체를 행 우선 2차원 셀 배열로.
54
+
55
+ ### 복사
56
+
57
+ - `copyCellStyle(srcAddr, targetAddr): Promise<void>` — 셀 스타일 ID 만 복사(값 미복사). `srcAddr`/`targetAddr` = `ExcelAddressPoint`.
58
+ - `copyRowStyle(srcR, targetR): Promise<void>` — 데이터 범위 폭만큼 한 행의 셀 스타일을 다른 행에 복사.
59
+ - `copyCell(srcAddr, targetAddr): Promise<void>` — 셀 전체(값·수식·스타일) 복사.
60
+ - `copyRow(srcR, targetR): Promise<void>` — 한 행을 다른 행으로 복사(대상 덮어쓰기).
61
+ - `insertCopyRow(srcR, targetR): Promise<void>` — `srcR` 행을 `targetR` 위치에 삽입 복사. `targetR` 이하 기존 행은 한 칸 아래로 밀리고, 삽입 지점을 관통하는 다중행 병합은 1행 확장된다. 행 추가 삽입(기존 보존)이 필요할 때 `copyRow`(덮어쓰기) 대신 사용.
62
+
63
+ ### 데이터 변환
64
+
65
+ - `getDataTable(opt?): Promise<Record<string, ExcelValueType>[]>` — 헤더 행을 키로, 이후 행을 레코드로 변환.
66
+ - `opt.headerRowIndex?: number` — 헤더로 쓸 행 인덱스. 미지정 시 데이터 범위 첫 행.
67
+ - `opt.checkEndColIndex?: number` — 데이터 끝 판정 열. 이 열이 비면 그 행에서 중단. 빈 행 뒤 잡음 데이터를 끊을 때 지정.
68
+ - `opt.usableHeaderNameFn?: (headerName: string) => boolean` — `true` 반환한 헤더만 컬럼으로 채택. 일부 컬럼만 읽을 때.
69
+ - 헤더 문자열이 중복되면 throw.
70
+ - `setDataMatrix(matrix: ExcelValueType[][]): Promise<void>` — 0행 0열부터 행 우선으로 2차원 배열을 그대로 기록.
71
+ - `setRecords(records: Record<string, ExcelValueType>[]): Promise<void>` — 0행에 헤더(전 레코드 키의 distinct, 빈 키 제외) 자동 생성 후 1행부터 값 기록.
72
+
73
+ ### 뷰
74
+
75
+ - `setTabColor(color): Promise<void>` — 시트 탭 색. `color` = ARGB 8자리(예: `"00FF0000"`).
76
+ - `setZoom(percent): Promise<void>` — 확대/축소 비율(퍼센트).
77
+ - `freezeAt(point: { r?: number; c?: number }): Promise<void>` — 틀 고정. `r` = 이 행 위에서 고정, `c` = 이 열 왼쪽에서 고정. 둘 다/하나만 지정 가능.
78
+
79
+ ### 조건부 서식 / 이미지
80
+
81
+ - `addConditionalFormat(opts): Promise<void>` — 셀/범위에 native CF 규칙 추가. 자세히: [conditional-format.md](./conditional-format.md).
82
+ - `addImage(opts): Promise<void>` — 이미지 삽입.
83
+ - `opts.bytes: Bytes` — 이미지 바이너리.
84
+ - `opts.ext: string` — 확장자(png/jpg 등). MIME 결정 불가 시 throw.
85
+ - `opts.from: { r, c, rOff?, cOff? }` — 시작 앵커(0 기반 행/열, `rOff`/`cOff` 는 EMU 오프셋).
86
+ - `opts.to?: { r, c, rOff?, cOff? }` — 끝 앵커. 생략 시 `from` 기준 1행·1열 크기로 배치.
87
+ - 같은 시트의 기존 drawing 이 있으면 재사용하고 없으면 새로 만든다.
88
+
89
+ ### 사용 예
90
+
91
+ ```typescript
92
+ const ws = await wb.addWorksheet("매출");
93
+ await ws.setRecords([{ 품목: "사과", 수량: 10 }]);
94
+ await ws.setZoom(85);
95
+ await ws.freezeAt({ r: 0 });
96
+ await ws.addImage({ bytes, ext: "png", from: { r: 0, c: 3 } });
97
+ ```
@@ -0,0 +1,83 @@
1
+ # @simplysm/excel — ExcelWrapper
2
+
3
+ Zod 스키마 1개로 레코드 배열 ↔ Excel 파일을 타입 안전하게 매핑할 때 읽는다. 스키마 각 필드의 `.describe()` 가 Excel 헤더(표시명)가 되고, 필드 타입으로 읽기 시 값 변환·검증을 수행한다. 저수준 셀 조작 없이 "정형 데이터의 import/export" 용도일 때 사용.
4
+
5
+ ## 시그니처
6
+
7
+ ```typescript
8
+ new ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>>(schema: TSchema)
9
+
10
+ read(
11
+ file: Bytes | Blob,
12
+ wsNameOrIndex: string | number = 0,
13
+ options?: { excludes?: (keyof z.infer<TSchema>)[] },
14
+ ): Promise<z.infer<TSchema>[]>
15
+
16
+ write(
17
+ wsName: string,
18
+ records: Partial<z.infer<TSchema>>[],
19
+ options?: { excludes?: (keyof z.infer<TSchema>)[] },
20
+ ): Promise<ExcelWorkbook>
21
+ ```
22
+
23
+ - `schema: z.ZodObject` — 레코드 구조. 각 필드의 `.describe("표시명")` 이 Excel 헤더명. 미지정 시 필드 키를 헤더로 사용.
24
+ - `read.file: Bytes | Blob` — 읽을 .xlsx 데이터.
25
+ - `read.wsNameOrIndex: string | number` — 대상 시트(기본 0번). 이름 또는 0 기반 인덱스.
26
+ - `read.options.excludes?: (keyof ...)[]` — 매핑에서 제외할 필드 키 목록.
27
+ - `write.wsName: string` — 생성할 시트 이름.
28
+ - `write.records: Partial<...>[]` — 기록할 부분 레코드 배열. 누락 필드는 빈 셀.
29
+ - `write.options.excludes?: (keyof ...)[]` — 출력에서 제외할 필드 키 목록.
30
+
31
+ ## read 동작
32
+
33
+ - 헤더명↔필드 역매핑 후 `ws.getDataTable({ usableHeaderNameFn })` 로 표시명에 일치하는 컬럼만 추출.
34
+ - 각 셀 값을 필드 타입별로 변환 후, 행마다 `schema.safeParse` 로 검증. 실패하면 그 행에서 throw(부분 반영 없음).
35
+ - 모든 필드가 null/`""` 인 행은 skip.
36
+ - 데이터가 한 건도 없으면 기대 헤더 목록을 담은 메시지로 throw.
37
+ - 내부에서 워크북을 열고 `finally` 로 `close()` 하므로 호출자 정리 불필요.
38
+
39
+ ### 값 변환 규칙
40
+
41
+ - 빈값(null/`""`) → 스키마 기본값: `ZodDefault` 면 그 기본값, optional/nullable 이면 `undefined`, 필수 boolean 이면 `false`, 그 외 `undefined`.
42
+ - `ZodString` → 문자열(아니면 `String()` 캐스팅).
43
+ - `ZodNumber` → number(문자열은 `num.parseFloat`).
44
+ - `ZodBoolean` → `"1"`/`"true"` → `true`, `"0"`/`"false"` → `false`, 그 외 `Boolean()`.
45
+ - `DateOnly`/`DateTime`/`Time` 인스턴스는 그대로 통과.
46
+
47
+ ## write 동작
48
+
49
+ - 0행에 헤더(제외 후 필드 순서), 1행부터 레코드 값 기록.
50
+ - 전 셀에 4변 테두리 적용.
51
+ - **필수**(optional/nullable/default 아님)이며 boolean 이 아닌 필드의 헤더 셀은 노란색(`00FFFF00`) 강조.
52
+ - zoom 85%, 0행 틀고정 적용.
53
+ - 반환된 `ExcelWorkbook` 의 리소스 관리는 **호출자 책임** — 사용 후 `close()` 필수. write 중 예외 발생 시 내부에서 close 후 rethrow.
54
+
55
+ ## 사용 예
56
+
57
+ ```typescript
58
+ import { z } from "zod";
59
+
60
+ const schema = z.object({
61
+ name: z.string().describe("이름"),
62
+ age: z.number().optional().describe("나이"),
63
+ });
64
+ const wrapper = new ExcelWrapper(schema);
65
+
66
+ // 읽기 ("이름"/"나이" 헤더를 0번 시트에서 매칭)
67
+ const rows = await wrapper.read(bytes);
68
+
69
+ // 쓰기 (호출자가 close 책임)
70
+ const wb = await wrapper.write("회원", [{ name: "홍길동", age: 30 }]);
71
+ try {
72
+ const out = await wb.toBytes();
73
+ } finally {
74
+ await wb.close();
75
+ }
76
+ ```
77
+
78
+ ## 주의사항
79
+
80
+ - `.describe()` 표시명이 실제 Excel 헤더와 일치해야 `read` 가 컬럼을 인식. 일치 헤더가 전혀 없으면 데이터 0건으로 throw.
81
+ - 표시명 미지정 필드는 필드 키 그대로 헤더로 쓰이므로, 한글 헤더가 필요하면 반드시 `.describe()` 지정.
82
+ - `read` 행 검증은 전부-성공 전제: 한 행이라도 `safeParse` 실패 시 전체 throw.
83
+ - `write` 반환 워크북 미`close` 시 리소스 누수.
@@ -0,0 +1,43 @@
1
+ # @simplysm/lint
2
+
3
+ 심플리즘 워크스페이스 전용 ESLint 플러그인. 커스텀 규칙 9종 묶음(`eslint-plugin`)과, 그 규칙들을 포함해 JS/TS/HTML 파일별로 완성된 flat config 배열(`eslint-recommended`)을 서브패스 export 로 제공. `src/index.ts` 없음 — `package.json` 의 `exports`(`./eslint-plugin`, `./eslint-recommended`)가 진입점.
4
+
5
+ ## 사용 트리거 인덱스
6
+
7
+ - **eslint-recommended** — 프로젝트 `eslint.config.{js,mjs,cjs}` 에서 simplysm 표준 lint 규칙 전체(JS/TS/HTML 파일별 + tseslint/angular-eslint 통합)를 한 번에 적용할 때 import 하는 default export(완성된 flat config 배열).
8
+ - **eslint-plugin** — `@simplysm/<rule>` 커스텀 규칙 9종을 직접 켜거나, 개별 규칙의 감지 범위·메시지·autofix·옵션을 파악할 때. 자세히: [rules.md](./rules.md)
9
+
10
+ ## eslint-recommended
11
+
12
+ `@simplysm/lint/eslint-recommended` 의 default export. `tseslint.config(...)` 결과(`FlatConfig` 배열)이므로 호출형이 아니라 완성된 상수. 그대로 export 하거나 spread 후 항목을 덧붙여 사용.
13
+
14
+ ```js
15
+ // eslint.config.js
16
+ import recommended from "@simplysm/lint/eslint-recommended";
17
+ export default recommended;
18
+ // 규칙 추가 시: export default [...recommended, { files: ["**/*.ts"], rules: { "no-debugger": "error" } }];
19
+ ```
20
+
21
+ config 가 강제하는 핵심 내용(파일 glob 블록별):
22
+
23
+ - **ignores** — `**/node_modules/**`, `**/dist/**`, `**/.*/**`(숨김 디렉터리 전체). 순회 자체를 건너뜀.
24
+ - **`**/*.{js,mjs,cjs}`** — node globals 적용. 켜는 규칙: `require-await`(async 함수에 await 없으면 오류), `no-shadow`(상위 스코프 변수 가림 금지), `no-duplicate-imports`(같은 모듈 중복 import 금지), `no-unused-expressions`(부수효과 없는 표현식 금지), `no-undef`(선언 안 된 식별자 사용 금지), `unused-imports/no-unused-imports`(미사용 import 제거, autofix), `unused-imports/no-unused-vars`(미사용 변수·인자 경고, `^_` 접두는 무시), `import/no-extraneous-dependencies`(미선언 의존성 import 금지, 단 `lib/**`·`eslint.config.*`·`simplysm.*`·`vitest.config.*` 는 devDependencies 허용), `@simplysm/no-subpath-imports-from-simplysm`, `@simplysm/no-hard-private` + 아래 공통 규칙군.
25
+ - **`**/*.ts`** — `angular.configs.tsRecommended` 를 펼친 뒤 추가. `processInlineTemplates` 로 인라인 템플릿 분리, `tseslint.parser`(`parserOptions.project: true`) 사용, `eslint-import-resolver-typescript` resolver 를 `import.meta.resolve` 로 자동 등록. 켜는 규칙: `no-console`(error), 그리고 타입 인식 `@typescript-eslint/*` 규칙 — `require-await`, `await-thenable`(thenable 만 await), `return-await`(`in-try-catch` = try/catch 안에서만 return await 요구), `no-floating-promises`(await/void 안 한 Promise 금지), `no-shadow`, `no-unnecessary-condition`(`allowConstantLoopConditions: true` = 상수 루프 조건 허용), `no-unnecessary-type-assertion`, `prefer-reduce-type-parameter`, `prefer-return-this-type`, `no-unused-expressions`, `strict-boolean-expressions`(`allowNullableBoolean`·`allowNullableObject` = nullable boolean/object 의 조건식 허용), `ban-ts-comment`(`ts-expect-error` 는 3자 이상 설명 필수), `prefer-readonly`, `naming-convention`(private 멤버는 `_` 접두 필수, 그 외 포맷 제약 없음), `no-misused-promises`(`checksVoidReturn.arguments:false`·`inheritedMethods:false` = 콜백 인자·상속 메서드의 void 반환 검사는 끔), `only-throw-error`(Error 외 throw 금지), `no-array-delete`(배열에 `delete` 금지). 커스텀 규칙 6종: `@simplysm/ng-no-async-effect`, `no-hard-private`, `no-subpath-imports-from-simplysm`, `ts-no-throw-not-implemented-error`(warn), `ts-no-unused-injects`, `ts-no-unused-protected-readonly`. + `@angular-eslint/no-output-native`(off).
26
+ - **`**/*.html`** — `angular.configs.templateRecommended` + `templateAccessibility` 확장. 커스텀 템플릿 규칙: `@simplysm/ng-template-no-strict-null-check`, `ng-template-no-todo-comments`(warn), `ng-template-sd-require-binding-attrs` + `@angular-eslint/template/eqeqeq`(`allowNullOrUndefined: true` = null/undefined 비교는 느슨한 비교 허용), `label-has-associated-control`(off), `no-any`(error).
27
+ - **`**/tests/**/*.ts`(override)** — 테스트 완화: `no-console`(off), `import/no-extraneous-dependencies`(off), `@simplysm/ts-no-throw-not-implemented-error`(off).
28
+ - **`**/vitest.config.ts`(override)** — `no-restricted-properties`(off). 설정 파일에서 `process.env` 직접 접근 허용.
29
+
30
+ 공통 규칙군(JS/TS 양쪽 적용): `no-warning-comments`(warn, TODO/FIXME 등 경고), `eqeqeq`(`always`, 단 `null` 비교는 `==`/`!=` 허용), `no-self-compare`, `array-callback-return`(map/filter 등 콜백에 return 강제). 추가 공통 금지: `no-restricted-globals`(`Buffer` 전역 금지 → `Uint8Array`/`BytesUtils`), `no-restricted-imports`(`buffer`·`events`·`eventemitter3` 모듈 금지), `no-restricted-properties`(`process.env` 직접 접근 금지 → `env("...")`), `no-restricted-syntax`(`import.meta.env` 직접 접근·`env("NODE_ENV")`·`=== undefined`/`!== undefined` 류 strict 비교 금지 → `== null`/`!= null`).
31
+
32
+ 주의: 타입 인식 `@typescript-eslint` 규칙이 켜져 있어 TS 파일은 `parserOptions.project: true` 가 동작하는 환경(tsconfig 포함)이어야 함.
33
+
34
+ ## eslint-plugin
35
+
36
+ `@simplysm/lint/eslint-plugin` 의 default export 는 `{ rules: { ... } }` 형태의 ESLint 플러그인 객체. 9개 규칙을 규칙 ID → 규칙 정의로 매핑. 보통 `eslint-recommended` 가 내부에서 `@simplysm` 네임스페이스로 등록하므로, 직접 import 는 recommended 없이 커스텀 config 를 짜거나 특정 규칙만 개별 제어할 때만 필요. 규칙별 상세는 [rules.md](./rules.md).
37
+
38
+ ```js
39
+ import plugin from "@simplysm/lint/eslint-plugin";
40
+ export default [{ plugins: { "@simplysm": plugin }, rules: { "@simplysm/no-hard-private": "error" } }];
41
+ ```
42
+
43
+ 규칙 ID 9종: `ng-no-async-effect`, `ng-template-no-strict-null-check`, `ng-template-no-todo-comments`, `ng-template-sd-require-binding-attrs`, `no-hard-private`, `no-subpath-imports-from-simplysm`, `ts-no-throw-not-implemented-error`, `ts-no-unused-injects`, `ts-no-unused-protected-readonly`.
@@ -0,0 +1,90 @@
1
+ # @simplysm/lint — rules
2
+
3
+ `eslint-plugin` 이 노출하는 커스텀 ESLint 규칙 9종. 규칙 ID 는 등록 시 `@simplysm/<name>`. 개별 규칙을 켜거나 lint 위반 메시지·autofix·옵션 의미를 파악할 때 읽음. 옵션 없는 규칙은 `schema: []`. autofix 는 `fixable: "code"` 표시 규칙만 제공. 모든 규칙은 `createRule`(= `ESLintUtils.RuleCreator`)로 생성되어 문서 URL 이 자동 부여됨.
4
+
5
+ ## ng-no-async-effect
6
+
7
+ `type: problem`, autofix 없음. `@angular/core` 의 `effect()` 첫 인자로 async 함수(화살표/함수표현식이며 `async` 플래그)를 직접 전달하는 것을 금지. `await` 이후 읽은 signal 이 반응형 컨텍스트를 벗어나 의존성 추적에서 빠지는 버그, 그리고 반환값이 `Promise<void>` 가 되어 cleanup 등록이 막히는 문제를 방지.
8
+
9
+ - 감지 범위: named import(`effect`), aliased import(`effect as ngEffect`), namespace import(`ng.effect(...)`). `@angular/core` 가 아닌 모듈·로컬 선언 `effect` 는 무시(스코프·import 정의를 추적해 판별).
10
+ - `noAsyncEffect` — async 직접 전달 금지 메시지. 비동기 작업은 `void untracked(async () => { ... })` 안에서 수행하라고 안내.
11
+
12
+ ```ts
13
+ // 위반
14
+ effect(async () => { await load(); });
15
+ // 권장
16
+ effect(() => { sig(); void untracked(async () => { await load(); }); });
17
+ ```
18
+
19
+ ## no-hard-private
20
+
21
+ `type: problem`, autofix 있음. ECMAScript hard private(`#field`) 금지, TypeScript `private _` 스타일 강제.
22
+
23
+ - 감지: 필드 선언 `#field`, 메서드 선언 `#method()`, 접근자 선언 `accessor #field`, 사용처 `this.#field`. 중첩 클래스도 스택으로 추적.
24
+ - autofix: `#a` → `_a` 이름 변경 + 선언부 앞에 `private ` 삽입(데코레이터·`static`·`async`·`readonly` 순서 보존; 사용처 `this.#a` → `this._a`). 단, 같은 클래스에 `_a` 멤버가 이미 있으면 충돌로 변환 불가 → 보고만 하고 autofix 안 함.
25
+ - `preferSoftPrivate` — hard private 금지(이름 변경 가능 시 적용되는 일반 메시지).
26
+ - `nameConflict` — `_{{name}}` 멤버가 이미 존재해 변환 불가. `{{name}}` 치환.
27
+
28
+ ## no-subpath-imports-from-simplysm
29
+
30
+ `type: problem`, autofix 있음. `@simplysm/<pkg>/src/...` 형태의 `src` 하위 경로 import 금지(`@simplysm/<pkg>` 및 `src` 가 아닌 하위 경로는 허용).
31
+
32
+ - 감지: 정적 `import ... from '...'`, 동적 `import('...')`, 재내보내기 `export { ... } from '...'`, 전체 재내보내기 `export * from '...'`. 경로를 `/` 로 분해해 3번째 세그먼트가 `src` 인 경우만 위반.
33
+ - autofix: `@simplysm/pkg/src/x` → `@simplysm/pkg` 로 치환(원본 따옴표 종류 유지).
34
+ - `noSubpathImport` — `{{pkg}}`(패키지명)·`{{importPath}}`(원본 경로) 치환.
35
+
36
+ ## ts-no-throw-not-implemented-error
37
+
38
+ `type: suggestion`, autofix 없음, recommended 에서 `warn`(테스트에서는 off). `@simplysm/core-common` 의 `NotImplementedError` 를 `new` 로 생성하는 코드를 감지해 미구현 코드의 프로덕션 유입을 경고.
39
+
40
+ - 감지: named/aliased import(`NotImplementedError`, `NotImplementedError as NIE`), namespace import(`new CC.NotImplementedError()`). import 소스가 `@simplysm/core-common` 인지 스코프로 검증. 동적 import 는 무시.
41
+ - `noThrowNotImplementedError` — `{{text}}` 치환. `new` 첫 인자가 비어있지 않은 문자열 리터럴이면 그 문자열을, 아니면 `"미구현"` 을 그대로 출력.
42
+
43
+ ```ts
44
+ throw new NotImplementedError("결제 모듈 미구현"); // → "결제 모듈 미구현" 경고
45
+ ```
46
+
47
+ ## ts-no-unused-injects
48
+
49
+ `type: problem`, autofix 있음. 클래스 내 `inject()` 호출로 초기화된 프로퍼티 중 같은 클래스 어디에서도 참조되지 않는 필드를 보고.
50
+
51
+ - 감지: `PropertyDefinition` 의 값이 `inject(...)` 호출이고 key 가 식별자인 필드. 클래스 본문 전체를 재귀 순회해 필드명과 동일한 식별자(자기 key 제외)가 하나도 없으면 미사용으로 판정.
52
+ - autofix: 해당 필드 제거(앞 토큰 끝 ~ 뒤 토큰 사이 range 삭제).
53
+ - `unusedInject` — `inject() field "{{name}}" is never used.`
54
+
55
+ ## ts-no-unused-protected-readonly
56
+
57
+ `type: problem`, autofix 있음. `@Component` 데코레이터가 달린 클래스에서 `protected readonly`(non-static) 필드가 인라인 `template` 과 클래스 본문 어디에서도 참조되지 않으면 보고.
58
+
59
+ - 동작: `@Component({ template: ... })` 의 인라인 템플릿 문자열(템플릿 리터럴/문자열 리터럴)을 `@angular/compiler` 의 `parseTemplate` 으로 파싱해 바인딩 식별자를 수집. `@for` item·context 변수, `@if (...; as alias)`, `@let` 선언, 구조 디렉티브(`*ngFor` 등) 로컬 변수는 스코프에서 제외(오탐 방지). 템플릿·클래스 양쪽 모두 미사용일 때만 보고. `template` prop 이 없거나 빈 문자열이면 검사하지 않음(외부 `templateUrl` 미지원).
60
+ - autofix: 필드 선언 라인 제거(앞 들여쓰기·뒤 세미콜론/개행 포함).
61
+ - `unusedField` — `Protected readonly field "{{name}}" is not used in class or template.`
62
+
63
+ ## ng-template-no-strict-null-check
64
+
65
+ `type: problem`, autofix 없음(인라인 템플릿 offset 매핑 문제로 미제공). Angular 템플릿 바인딩에서 `=== null`/`undefined`, `!== null`/`undefined` 금지, `== null`/`!= null` 강제.
66
+
67
+ - 감지: `===`/`!==` 이항 연산의 한쪽 피연산자가 `null`/`undefined` 리터럴(value 가 nil)인 경우.
68
+ - `noStrictNullCheck` — `{{actual}}`(원본 표현식)·`{{replacement}}`(권장 표현식, `x == null`/`x != null`) 치환.
69
+
70
+ ## ng-template-no-todo-comments
71
+
72
+ `type: problem`, autofix 없음, recommended 에서 `warn`. HTML 템플릿의 `<!-- TODO: ... -->` 주석을 감지. AST visitor 가 아니라 raw 텍스트 정규식으로 처리(visitor 는 빈 객체 반환).
73
+
74
+ - 감지: HTML 주석(`<!-- ... -->`) 내부에 `TODO:` 가 포함된 경우. 메시지에는 `TODO:` 이후를 trim 한 본문을 그대로 출력.
75
+ - `noTodo` — `{{content}}` 치환(주석에 적힌 TODO 내용).
76
+
77
+ ## ng-template-sd-require-binding-attrs
78
+
79
+ `type: problem`, autofix 있음, 옵션 있음. `sd-` 접두사 컴포넌트에서 허용 목록 밖 plain attribute 사용을 금지하고 Angular property binding(`[attr]="..."`)을 강제.
80
+
81
+ - 옵션(객체 1개, 모든 키 선택):
82
+ - `selectorPrefixes: string[]` — 검사 대상 엘리먼트 태그 접두사. 기본 `["sd-"]`. 이 접두사로 시작하는 태그만 검사. 다른 디자인 시스템 접두사를 쓰면 그 값으로 교체.
83
+ - `allowAttributes: string[]` — plain 으로 허용할 attribute 이름. 기본 `["id","class","style","title","tabindex","role"]`. 대소문자 무시. 표준 전역 속성을 추가 허용할 때 확장.
84
+ - `allowAttributePrefixes: string[]` — plain 으로 허용할 attribute 접두사. 기본 `["aria-","data-","sd-"]`. 접근성·데이터 속성군을 통째로 허용.
85
+ - autofix: 값 없는 attr → `[attr]="true"`, 값 있는 attr → `[attr]="'<escaped>'"`(`\`·`'` 이스케이프). 빈 span 이면 fix 생략.
86
+ - `requireBindingForAttribute` — `{{attrName}}`·`{{elementName}}` 치환.
87
+
88
+ ```js
89
+ { "@simplysm/ng-template-sd-require-binding-attrs": ["error", { allowAttributes: ["id", "for"] }] }
90
+ ```
@@ -0,0 +1,67 @@
1
+ # @simplysm/orm-common
2
+
3
+ Dialect 독립 ORM 코어. 테이블/뷰/프로시저를 빌더로 정의하고, `DbContext` 클래스에 등록한 뒤, 체이닝 `Queryable` 로 타입 안전한 SELECT/CUD 쿼리를 JSON AST(`QueryDef`/`Expr`)로 조립한다. 실제 SQL 변환·DB 연결은 dialect QueryBuilder(이 패키지) + `@simplysm/orm-node` 등 executor 가 담당.
4
+
5
+ ## 사용 트리거 인덱스
6
+
7
+ - **스키마 정의 (Table/View/Procedure/Column/Index/Relation 빌더)** — DB 객체를 fluent 빌더로 선언하고 column·PK·index·FK 관계를 잡을 때. 자세히: [schema.md](./schema.md)
8
+ - **DbContext (연결·트랜잭션·DDL·마이그레이션·초기화)** — 빌더들을 한 컨텍스트에 등록하고 `connect`/`transaction` 으로 실행, DDL·migration·`initialize` 를 돌릴 때. 자세히: [db-context.md](./db-context.md)
9
+ - **Queryable (SELECT/JOIN/INSERT/UPDATE/DELETE/UPSERT 체이닝)** — `db.user()` 로 받은 쿼리 빌더에 where/orderBy/join/include/group/recursive/union 을 걸고 execute/single/count/insert/update/delete/upsert 를 호출할 때. 자세히: [queryable.md](./queryable.md)
10
+ - **expr (SQL 표현식 빌더)** — where/select/orderBy 콜백 안에서 비교·문자열·숫자·날짜·집계·조건·window·서브쿼리 표현식을 만들 때. 자세히: [expr.md](./expr.md)
11
+ - **QueryDef / Expr / Column 타입** — executor·QueryBuilder 를 직접 구현하거나 AST·결과 메타를 다룰 때 참조하는 타입군. 자세히: [types.md](./types.md)
12
+ - **QueryBuilder (dialect SQL 렌더러)** — QueryDef 를 mysql/mssql/postgresql SQL 로 변환하는 클래스. executor 구현체가 사용. 자세히: [query-builder.md](./query-builder.md)
13
+ - **결과 파싱 유틸 / 검색 파서 / 프로시저 실행 / 에러** — 아래 인라인 섹션 참조.
14
+
15
+ ## Executable / executable (프로시저 실행)
16
+
17
+ 저장 프로시저를 `DbContext.executable(Procedure)` 로 등록하면 `() => Executable` 팩토리가 반환된다. `Executable` 은 프로시저 실행 래퍼.
18
+
19
+ - `executable(db: DbContextBase, builder: ProcedureBuilder): () => Executable` — DbContext 내부에서 프로시저 getter 를 만드는 팩토리. 보통 `DbContext.executable()` 보호 메서드가 대신 호출.
20
+ - `Executable.execute(params): Promise<TReturns[][]>` — 프로시저 실행. `params` 는 `ProcedureBuilder.params()` 로 정의한 키별 값(리터럴 또는 ExprUnit). 결과는 결과셋 배열의 배열.
21
+ - `Executable.getExecProcQueryDef(params?)` — 실행용 `ExecProcQueryDef` 생성. 파라미터 미정의 프로시저에 값 전달 시 throw.
22
+
23
+ ```typescript
24
+ const result = await db.getUserById().execute({ userId: 1 });
25
+ ```
26
+
27
+ ## 결과 파싱 유틸
28
+
29
+ executor 가 DB raw 결과를 TS 객체로 환원할 때 쓰는 유틸. 일반 사용자는 직접 호출하지 않음.
30
+
31
+ - `parseQueryResult<T>(rawResults, meta: ResultMeta): Promise<T[] | undefined>` — flat raw 행 배열을 `meta.columns`(키→타입) 로 타입 변환하고 `meta.joins`(키→`{isSingle}`) 로 중첩 그룹핑. 입력이 비었거나 파싱 후 전부 빈 객체면 undefined. async 전용(100행마다 이벤트 루프 양보). `isSingle:true` 관계에 서로 다른 다건이 매칭되면 throw.
32
+ - `pickResultSets<T>(rawResults: T[][], buildResult): T[]` — 다중 결과셋에서 필요분 추출. `resultSetIndex` 없으면 첫 셋, `resultSetStride` 없으면 해당 인덱스 셋, 있으면 인덱스부터 stride 간격으로 concat(MySQL 배치 INSERT 의 SELECT 결과만 모을 때).
33
+
34
+ ## 검색 파서
35
+
36
+ `Queryable.search()` 내부에서 쓰이며, 검색 문법 문자열을 SQL LIKE 패턴으로 변환.
37
+
38
+ - `parseSearchQuery(searchText: string): ParsedSearchQuery` — 검색 문자열 파싱. 반환 `{ or, must, not }` 각각 LIKE 패턴 배열.
39
+ - `or: string[]` — 공백 구분 일반 토큰(OR, 하나 이상 일치). 와일드카드 없는 토큰은 `%토큰%`(부분 일치).
40
+ - `must: string[]` — `+토큰` 또는 `"정확한 구문"`(AND, 필수 포함).
41
+ - `not: string[]` — `-토큰`(NOT, 제외).
42
+ - 문법: `term1 term2`(OR), `+term`(필수), `-term`(제외), `"구문"`(정확·필수), `*`(와일드카드→`%`). 이스케이프 `\\ \* \% \" \+ \-`. 닫히지 않은 따옴표는 일반 텍스트.
43
+ - `interface ParsedSearchQuery` — 위 `or`/`must`/`not` 세 LIKE 패턴 배열을 담는 타입.
44
+
45
+ ## 에러 (DbTransactionError / DbErrorCode)
46
+
47
+ DBMS 네이티브 트랜잭션 에러를 표준 코드로 래핑. `connect`/`transaction` 의 롤백 경로에서 발생.
48
+
49
+ - `class DbTransactionError extends Error` — 표준화된 트랜잭션 에러.
50
+ - `code: DbErrorCode` — 표준 에러 코드.
51
+ - `message: string` — 에러 메시지(생성자 2번째 인자).
52
+ - `originalError?: unknown` — 원본 DBMS 에러(디버깅용).
53
+ - `name` — 항상 `"DbTransactionError"`.
54
+ - `enum DbErrorCode` — 표준 트랜잭션 에러 코드.
55
+ - `NO_ACTIVE_TRANSACTION` — 활성 트랜잭션 없는데 ROLLBACK 시도. 롤백 중 이 코드면 `connect`/`transaction` 이 무시(원 에러 보존).
56
+ - `TRANSACTION_ALREADY_STARTED` — 이미 트랜잭션 시작됨.
57
+ - `DEADLOCK` — 데드락 발생.
58
+ - `LOCK_TIMEOUT` — 잠금 타임아웃.
59
+
60
+ ```typescript
61
+ try {
62
+ await db.rollbackTransaction();
63
+ } catch (err) {
64
+ if (err instanceof DbTransactionError && err.code === DbErrorCode.NO_ACTIVE_TRANSACTION) return;
65
+ throw err;
66
+ }
67
+ ```
@@ -0,0 +1,80 @@
1
+ # @simplysm/orm-common — DbContext / 연결·DDL·마이그레이션
2
+
3
+ 테이블/뷰/프로시저 빌더를 class 프로퍼티로 등록하고, 연결·트랜잭션·DDL·초기화·마이그레이션을 제공하는 추상 클래스. 서브클래스로 확장해 사용. 실제 DB I/O 는 생성자에 주입한 `DbContextExecutor` 가 담당.
4
+
5
+ ## DbContext (추상 클래스)
6
+
7
+ - `new DbContext(executor: DbContextExecutor, opt: { database: string; schema? })` — 생성자(서브클래스가 호출). `executor` 는 DB I/O 구현, `opt.database`/`opt.schema` 는 기본 네임스페이스.
8
+ - `status: DbContextStatus` — 현재 상태(`"ready"`|`"connect"`|`"transact"`). 연결/트랜잭션 가드에 사용.
9
+ - `database` / `schema` — 주입한 기본 database/schema.
10
+ - 등록 메서드(protected, 서브클래스 프로퍼티 초기화에서 호출):
11
+ - `queryable(builder): () => Queryable` — Table/View 빌더를 Queryable 팩토리로 등록. 호출할 때마다 새 alias 부여.
12
+ - `executable(builder): () => Executable` — Procedure 빌더를 Executable 팩토리로 등록.
13
+ - `migrations: Migration[]` — 마이그레이션 정의(서브클래스에서 오버라이드). 기본 빈 배열.
14
+
15
+ ```typescript
16
+ class MainDb extends DbContext {
17
+ user = this.queryable(User);
18
+ post = this.queryable(Post);
19
+ getUserById = this.executable(GetUserById);
20
+ migrations = [{ name: "001", up: async (db) => { await db.createTable(User); } }];
21
+ }
22
+ const db = new MainDb(executor, { database: "mydb" });
23
+ await db.connect(async () => { await db.user().execute(); });
24
+ ```
25
+
26
+ ## 연결 / 트랜잭션
27
+
28
+ - `connect<T>(fn, isolationLevel?): Promise<T>` — 연결 + 트랜잭션 1개를 감싸 `fn` 실행. 성공 시 commit, 예외 시 rollback 후 throw, 종료 시 항상 close. `ready` 가 아니면 throw. 최초 호출 시 관계 정합성 검증. `isolationLevel` 은 격리 수준(선택).
29
+ - `connectWithoutTransaction<T>(callback): Promise<T>` — 트랜잭션 없이 연결만 잡고 `callback` 실행, 종료 시 close. 내부에서 별도 `transaction()` 을 직접 호출할 때 사용.
30
+ - `transaction<T>(fn, isolationLevel?): Promise<T>` — 이미 연결된 상태에서 트랜잭션만 시작/commit/rollback. `transact` 상태면 throw. (DDL 은 transact 상태에서 실행 불가 — `executeDefs` 가 throw)
31
+
32
+ ## DbContextBase 내부 인터페이스 메서드
33
+
34
+ executor·Queryable 이 의존하는 저수준 메서드(직접 호출은 드묾):
35
+ - `getNextAlias(): string` — 다음 테이블 alias(`T1`, `T2`...) 발급.
36
+ - `resetAliasCounter(): void` — alias 카운터 초기화(연결 시작 시 호출).
37
+ - `executeDefs<T>(defs: QueryDef[], resultMetas?): Promise<T[][]>` — QueryDef 배열을 executor 로 실행. transact 상태에서 DDL 포함 시 throw.
38
+ - `getQueryDefObjectName(tableOrView): QueryDefObjectName` — 빌더 → DB 객체 이름(database/schema/name) 해석.
39
+ - `switchFk(table, enabled): Promise<void>` — FK 제약 활성/비활성(트랜잭션 내 가능).
40
+
41
+ ## DDL 실행 메서드 (await 후 즉시 실행)
42
+
43
+ 각각 해당 DDL 을 `executeDefs` 로 즉시 실행. `table`/`view`/`procedure` 인자는 `QueryDefObjectName` 또는 빌더.
44
+ - `createTable(table)` / `dropTable(table)` / `renameTable(table, newName)` — 테이블 생성/삭제/이름변경.
45
+ - `createView(view)` / `dropView(view)` — 뷰 생성/삭제.
46
+ - `createProc(procedure)` / `dropProc(procedure)` — 프로시저 생성/삭제.
47
+ - `addColumn(table, columnName, column)` — column 추가(`column` 은 `ColumnBuilder`).
48
+ - `dropColumn(table, column)` — column 삭제.
49
+ - `modifyColumn(table, columnName, column)` — column 타입/속성 변경.
50
+ - `renameColumn(table, column, newName)` — column 이름 변경.
51
+ - `addPrimaryKey(table, columns)` / `dropPrimaryKey(table)` — PK 추가/삭제.
52
+ - `addForeignKey(table, relationName, relationDef)` — FK 추가(`relationDef` 는 `ForeignKeyBuilder`).
53
+ - `dropForeignKey(table, relationName)` — FK 삭제.
54
+ - `addIndex(table, indexBuilder)` / `dropIndex(table, columns)` — index 추가/삭제.
55
+ - `clearSchema(params: { database; schema? })` — 스키마 내 모든 객체 삭제.
56
+ - `schemaExists(database, schema?): Promise<boolean>` — 스키마 존재 확인.
57
+ - `truncate(table)` — 테이블 비우기.
58
+
59
+ ## DDL QueryDef 생성기 (실행 없이 def 만 반환)
60
+
61
+ 위 DDL 들의 `get*QueryDef(...)` 버전. 즉시 실행하지 않고 `QueryDef` 만 반환해 배치·검사용으로 모을 때 사용. 예: `getCreateTableQueryDef(table)`, `getDropTableQueryDef(table)`, `getAddColumnQueryDef(table, columnName, column)`, `getAddForeignKeyQueryDef(...)`, `getTruncateQueryDef(table)`, `getSwitchFkQueryDef(table, enabled)` 등. `getCreateObjectQueryDef(builder)` 는 Table/View/Procedure 중 무엇이든 받아 알맞은 CREATE def 반환.
62
+
63
+ ## 초기화 / 마이그레이션
64
+
65
+ - `initialize(options?: { dbs?: string[]; force?: boolean }): Promise<boolean>` — 스키마 동기화/마이그레이션 실행.
66
+ - `dbs?: string[]` — 대상 database 한정(미지정 시 전체).
67
+ - `force?: boolean` — true 면 기존 스키마를 강제 재생성. 운영 데이터 손실 위험이 있어 개발/초기 셋업에서만.
68
+ - 반환 boolean — 초기화 수행 여부.
69
+ - `interface Migration` — 마이그레이션 1건.
70
+ - `name: string` — 고유 이름(타임스탬프 권장, 적용 여부 추적 키).
71
+ - `up: (db) => Promise<void>` — 적용 함수. `db` 는 DDL 메서드를 갖춘 컨텍스트.
72
+
73
+ ## 관련 타입
74
+
75
+ - `type DbContextStatus = "ready" | "connect" | "transact"`.
76
+ - `interface DbContextBase` — Queryable/Executable/ViewBuilder 가 의존하는 핵심 인터페이스(위 저수준 메서드 + database/schema/status).
77
+ - `interface DbContextDdlMethods` — DDL 실행+생성기 메서드 시그니처 모음(Migration.up 의 db 타입).
78
+ - `interface DbContextExecutor` — 주입할 실행기 인터페이스. `connect()`/`close()`/`beginTransaction(isolationLevel?)`/`commitTransaction()`/`rollbackTransaction()`/`executeDefs(defs, resultMetas?)`.
79
+ - `const SD_BUILDER: symbol` — 등록된 팩토리 함수에 원본 빌더를 매달아두는 심볼 키(내부 메타 조회용).
80
+ - `_Migration` — 시스템 마이그레이션 테이블 빌더(`_migration`, PK `code: varchar(255)`). initialize 가 적용 이력 저장에 사용.
@@ -0,0 +1,113 @@
1
+ # @simplysm/orm-common — expr (SQL 표현식 빌더)
2
+
3
+ dialect 독립 SQL 표현식을 JSON AST(`Expr`)로 만드는 `expr` 객체. `where`/`having`/`search` 콜백에선 비교·논리 함수(`WhereExprUnit` 반환), `select`/`orderBy`/`groupBy` 콜백에선 값·함수·집계(`ExprUnit<T>` 반환)를 쓴다. `ExprInput<T>` = `ExprUnit<T> | T`(리터럴 직접 전달 가능). `ExprUnit.n` 게터는 nullable 을 non-null 타입으로 좁힌다.
4
+
5
+ ## 값 생성
6
+
7
+ - `expr.val(dataType, value): ExprUnit` — 리터럴 래핑. `dataType` 은 `"string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes"`, `value` 는 그 값(undefined=NULL 허용). UPDATE/UPSERT 쓰기값에 주로 사용.
8
+ - `expr.col(dataType, ...path): ExprUnit` — column 참조 직접 생성(보통은 프록시 사용, 내부용).
9
+ - `expr.raw(dataType)\`SQL ${expr}\`: ExprUnit` — Raw SQL 이스케이프 해치. 태그드 템플릿, 보간값은 자동 파라미터화. ORM 미지원 DB 함수가 필요할 때만.
10
+ - `expr.toExpr(value): Expr` — `ExprInput` → `Expr` AST 변환(내부 헬퍼).
11
+
12
+ ## WHERE — 비교 (WhereExprUnit 반환)
13
+
14
+ - `expr.eq(source, target)` — 동등(=). **NULL 안전**(MySQL `<=>`, MSSQL/PG `IS NULL OR =`).
15
+ - `expr.gt(source, target)` — 초과(>).
16
+ - `expr.lt(source, target)` — 미만(<).
17
+ - `expr.gte(source, target)` — 이상(>=).
18
+ - `expr.lte(source, target)` — 이하(<=).
19
+ - `expr.between(source, from?, to?)` — 범위(BETWEEN). `from`/`to` undefined 면 해당 방향 무제한(한쪽만 주면 `>=`/`<=`).
20
+ - `expr.null(source)` — IS NULL 검사.
21
+ - `expr.like(source, pattern)` — LIKE 패턴(`%`=0+, `_`=1자, 특수문자 `\` 이스케이프).
22
+ - `expr.regexp(source, pattern)` — 정규식 매칭(구문은 DBMS 의존).
23
+ - `expr.in(source, values)` — IN 값 목록.
24
+ - `expr.inQuery(source, query)` — IN (서브쿼리). `query` 는 단일 column SELECT Queryable(아니면 throw).
25
+ - `expr.exists(query)` — EXISTS 서브쿼리(행 1개 이상이면 true). SELECT 절은 패킷 절약 위해 제거됨.
26
+
27
+ ## WHERE — 논리
28
+
29
+ - `expr.not(arg: WhereExprUnit)` — 부정(NOT).
30
+ - `expr.and(conditions: WhereExprUnit[])` — AND 결합. 빈 배열 throw.
31
+ - `expr.or(conditions: WhereExprUnit[])` — OR 결합. 빈 배열 throw.
32
+
33
+ ## SELECT — 문자열 (ExprUnit 반환)
34
+
35
+ - `expr.concat(...args)` — CONCAT(NULL 은 빈 문자열 취급).
36
+ - `expr.left(source, length)` / `expr.right(source, length)` — 좌/우 N자 추출.
37
+ - `expr.trim(source)` — 양쪽 공백 제거.
38
+ - `expr.padStart(source, length, fillString)` — 좌측 패딩(LPAD), 목표 길이까지 `fillString` 반복.
39
+ - `expr.replace(source, from, to)` — 치환.
40
+ - `expr.upper(source)` / `expr.lower(source)` — 대/소문자 변환.
41
+ - `expr.length(source)` — 문자 수(CHAR_LENGTH).
42
+ - `expr.byteLength(source)` — 바이트 길이(UTF-8 CJK 3바이트).
43
+ - `expr.substring(source, start, length?)` — 부분 문자열(1-base 인덱스, length 생략 시 끝까지).
44
+ - `expr.indexOf(source, search)` — 위치(1-base, 없으면 0).
45
+
46
+ ## SELECT — 숫자
47
+
48
+ - `expr.abs(source)` — 절대값.
49
+ - `expr.round(source, digits: number)` — 반올림(소수 `digits` 자리).
50
+ - `expr.ceil(source)` / `expr.floor(source)` — 올림/내림.
51
+
52
+ ## SELECT — 날짜
53
+
54
+ - `expr.year/month/day(source)` — 연/월/일 추출(`DateTime|DateOnly`).
55
+ - `expr.hour/minute/second(source)` — 시/분/초 추출(`DateTime|Time`).
56
+ - `expr.isoWeek(source)` — ISO 주 번호(월요일 시작, 1~53). `DateOnly`.
57
+ - `expr.isoWeekStartDate(source)` — 해당 주 월요일 날짜. `DateOnly`.
58
+ - `expr.isoYearMonth(source)` — 해당 월 1일. `DateOnly`.
59
+ - `expr.dateDiff(unit: DateUnit, from, to)` — 날짜 차(`to - from`). `unit` = `"year"|"month"|"day"|"hour"|"minute"|"second"`.
60
+ - `expr.dateAdd(unit, source, value)` — 날짜 가감(`value` 음수 허용). 반환 타입은 source 와 동일.
61
+ - `expr.formatDate(source, format: string)` — 포맷 문자열로 변환(규칙 DBMS 의존).
62
+
63
+ ## SELECT — 조건
64
+
65
+ - `expr.coalesce(...args)` — 첫 non-null 반환(COALESCE). 마지막 인자가 non-nullable 이면 결과도 non-nullable.
66
+ - `expr.nullIf(source, value)` — `source===value` 면 NULL, 아니면 source.
67
+ - `expr.is(condition: WhereExprUnit): ExprUnit<boolean>` — WHERE 조건을 boolean 값으로 SELECT.
68
+ - `expr.switch<T>()` — CASE WHEN 빌더. `.case(condition, then).case(...).default(value)` 체이닝으로 분기 구성. 타입은 then/default 의 ExprUnit 또는 non-null 리터럴에서 추론(전부 null 이면 throw).
69
+ - `expr.if(condition, then, else_)` — 삼항(IF/IIF). then/else 중 최소 하나 non-null(전부 null throw).
70
+
71
+ ## SELECT — 집계
72
+
73
+ 집계는 모든 값 NULL/행 없음일 때만 NULL(NULL 행은 무시).
74
+ - `expr.count(arg?, distinct?: boolean)` — COUNT. `arg` 생략 시 전체 행, `distinct:true` 면 중복 제거.
75
+ - `expr.sum(arg)` — 합계(nullable number).
76
+ - `expr.avg(arg)` — 평균(nullable number).
77
+ - `expr.max(arg)` / `expr.min(arg)` — 최대/최소(nullable, 입력 타입 유지).
78
+
79
+ ## SELECT — 기타
80
+
81
+ - `expr.greatest(...args)` / `expr.least(...args)` — 인자 중 최대/최소값(GREATEST/LEAST). 인자 중 ExprUnit 없으면 throw.
82
+ - `expr.rowNum(): ExprUnit<number>` — 단순 행 순번(1-base).
83
+ - `expr.random(): ExprUnit<number>` — 0~1 난수(무작위 정렬 등).
84
+ - `expr.cast(source, targetType: DataType): ExprUnit` — 타입 변환(CAST). `targetType` 예: `{ type: "varchar", length: 20 }`.
85
+ - `expr.subquery(dataType, queryable): ExprUnit` — 스칼라 서브쿼리(정확히 1행 1열). `queryable` 은 `getSelectQueryDef()` 를 가진 객체.
86
+
87
+ ## SELECT — Window 함수
88
+
89
+ `spec: WinSpecInput` = `{ partitionBy?: ExprInput[]; orderBy?: [ExprInput, ("ASC"|"DESC")?][] }`.
90
+ - `expr.rowNumber(spec)` — ROW_NUMBER()(파티션 내 1-base 순번).
91
+ - `expr.rank(spec)` — RANK()(동순위 후 건너뜀: 1,1,3).
92
+ - `expr.denseRank(spec)` — DENSE_RANK()(동순위 후 연속: 1,1,2).
93
+ - `expr.ntile(n: number, spec)` — NTILE(n)(파티션을 n 그룹으로, 1~n).
94
+ - `expr.lag(column, spec, options?)` / `expr.lead(column, spec, options?)` — 이전/다음 행 값. `options` = `{ offset?: number; default?: ExprInput }`(offset 기본 1, default 는 경계값).
95
+ - `expr.firstValue(column, spec)` / `expr.lastValue(column, spec)` — 프레임 첫/마지막 값.
96
+ - `expr.sumOver/avgOver(column, spec)` — window 합/평균(누적합·이동평균).
97
+ - `expr.countOver(spec, column?)` — window 카운트(column 생략 시 전체).
98
+ - `expr.minOver/maxOver(column, spec)` — window 최소/최대.
99
+
100
+ ```typescript
101
+ db.order().select((o) => ({
102
+ ...o,
103
+ rowNum: expr.rowNumber({ partitionBy: [o.userId], orderBy: [[o.createdAt, "DESC"]] }),
104
+ runningTotal: expr.sumOver(o.amount, { partitionBy: [o.userId], orderBy: [[o.createdAt, "ASC"]] }),
105
+ }));
106
+ ```
107
+
108
+ ## 관련 타입 / 클래스
109
+
110
+ - `class ExprUnit<T>` — 값 표현식 래퍼. `dataType`/`expr`(AST)/`$infer`(타입). `.n` 게터로 non-null 좁히기.
111
+ - `class WhereExprUnit` — WHERE 절 표현식 래퍼(`expr` AST 만).
112
+ - `type ExprInput<T> = ExprUnit<T> | T`.
113
+ - `interface SwitchExprBuilder<T>` — `expr.switch()` 빌더 인터페이스(`case`/`default`).