@simplysm/sd-claude 14.0.91 → 14.0.93

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 (93) hide show
  1. package/claude/references/sd-simplysm14/README.md +7 -6
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +59 -39
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +119 -186
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +70 -31
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +55 -57
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +86 -105
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +48 -57
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +37 -47
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +82 -74
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +61 -50
  11. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +74 -57
  12. package/claude/references/sd-simplysm14/apis/angular/sheet.md +63 -72
  13. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +23 -18
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -19
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +23 -18
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +72 -32
  17. package/claude/references/sd-simplysm14/apis/core-browser/README.md +18 -18
  18. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +29 -29
  19. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +41 -41
  20. package/claude/references/sd-simplysm14/apis/core-common/README.md +97 -90
  21. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +75 -51
  22. package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +81 -0
  23. package/claude/references/sd-simplysm14/apis/core-common/errors.md +27 -29
  24. package/claude/references/sd-simplysm14/apis/core-common/obj.md +44 -45
  25. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +34 -33
  26. package/claude/references/sd-simplysm14/apis/core-common/value-types.md +86 -0
  27. package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -6
  28. package/claude/references/sd-simplysm14/apis/core-node/consola.md +3 -0
  29. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +2 -2
  30. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +1 -1
  31. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +2 -2
  32. package/claude/references/sd-simplysm14/apis/core-node/worker.md +6 -3
  33. package/claude/references/sd-simplysm14/apis/excel/README.md +10 -10
  34. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +4 -2
  35. package/claude/references/sd-simplysm14/apis/excel/utils.md +1 -1
  36. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +6 -6
  37. package/claude/references/sd-simplysm14/apis/lint/README.md +6 -32
  38. package/claude/references/sd-simplysm14/apis/lint/recommended.md +60 -0
  39. package/claude/references/sd-simplysm14/apis/lint/rules.md +17 -17
  40. package/claude/references/sd-simplysm14/apis/orm-common/README.md +15 -6
  41. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +68 -102
  42. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +75 -89
  43. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +87 -99
  44. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +110 -147
  45. package/claude/references/sd-simplysm14/apis/orm-common/types.md +48 -51
  46. package/claude/references/sd-simplysm14/apis/orm-node/README.md +8 -13
  47. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +5 -5
  48. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +9 -6
  49. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +9 -8
  50. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +23 -19
  51. package/claude/references/sd-simplysm14/apis/service-client/README.md +20 -12
  52. package/claude/references/sd-simplysm14/apis/service-client/orm.md +6 -6
  53. package/claude/references/sd-simplysm14/apis/service-client/transport.md +1 -1
  54. package/claude/references/sd-simplysm14/apis/service-common/README.md +35 -32
  55. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +23 -22
  56. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +23 -23
  57. package/claude/references/sd-simplysm14/apis/service-server/README.md +51 -43
  58. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +6 -6
  59. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +31 -21
  60. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +8 -8
  61. package/claude/references/sd-simplysm14/apis/storage/README.md +55 -49
  62. package/claude/references/sd-simplysm14/manuals/client-component.md +843 -740
  63. package/claude/references/sd-simplysm14/manuals/client-crud.md +8 -0
  64. package/claude/references/sd-simplysm14/manuals/client-demo.md +6 -16
  65. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +26 -0
  66. package/claude/references/sd-simplysm14/manuals/logging.md +1 -1
  67. package/claude/references/sd-simplysm14/manuals/orm.md +15 -1
  68. package/claude/rules/sd-design-rules.md +7 -0
  69. package/claude/sd-system-prompt.md +5 -8
  70. package/claude/skills/sd-debug/SKILL.md +43 -0
  71. package/claude/skills/sd-debug/workflow.js +390 -0
  72. package/claude/skills/sd-demo/SKILL.md +18 -20
  73. package/claude/skills/sd-dev/SKILL.md +127 -24
  74. package/claude/skills/sd-docs/SKILL.md +5 -3
  75. package/claude/skills/sd-docs/references/subagent-prompt.md +2 -3
  76. package/claude/skills/sd-impl/SKILL.md +18 -18
  77. package/claude/skills/sd-manual/SKILL.md +1 -0
  78. package/claude/skills/sd-review/SKILL.md +24 -18
  79. package/claude/skills/sd-review/workflow.js +324 -0
  80. package/claude/skills/sd-spec/SKILL.md +96 -679
  81. package/claude/skills/sd-spec/references/example-spec.md +28 -50
  82. package/claude/skills/sd-spec/references/format-analyze.md +232 -0
  83. package/claude/skills/sd-spec/references/format-design.md +248 -0
  84. package/claude/skills/sd-spec/workflow-analyze.js +615 -0
  85. package/claude/skills/sd-spec/workflow-design.js +667 -0
  86. package/claude/skills/sd-unpack/scripts/handlers/office_com.py +5 -1
  87. package/package.json +1 -1
  88. package/scripts/postinstall.mjs +157 -18
  89. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +0 -68
  90. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +0 -77
  91. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +0 -86
  92. package/claude/skills/sd-skill/SKILL.md +0 -245
  93. package/claude/skills/sd-skill/scripts/run_eval.py +0 -380
@@ -1,113 +1,85 @@
1
1
  # @simplysm/orm-common — DbContext / 연결·트랜잭션·DDL·마이그레이션
2
2
 
3
- `DbContext` 추상 클래스를 상속해 테이블·뷰·프로시저를 클래스 프로퍼티로 등록하고, 연결·트랜잭션·DDL·마이그레이션을 실행하는 묶음. `DbContextExecutor` 구현체와 `{ database, schema? }` 옵션을 생성자로 주입한다. 각 프로퍼티가 독립 직렬화되어 40+ 테이블에서도 TS7056 이 발생하지 않는다. 트랜잭션 롤백 에러는 `DbTransactionError` 로 표준화된다.
4
-
5
- > 앱(Angular) 환경에서는 화면이 `DbContext` 를 직접 생성하지 않고 `AppOrmProvider.connectAsync(cb)` 로 감싼다(client-orm.md). `db` 인자가 곧 아래 `DbContext` 인스턴스이므로, 콜백 안의 쿼리 작성법은 동일하다.
3
+ `DbContext` 추상 클래스를 상속해 테이블·뷰·프로시저를 클래스 프로퍼티로 등록하고, 연결·트랜잭션·DDL·마이그레이션을 실행하는 묶음. 생성자에 `DbContextExecutor` 구현체와 `{ database, schema? }` 옵션을 주입한다. 각 프로퍼티가 독립적으로 직렬화되므로 40+ 테이블에서도 TS7056 이 발생하지 않는다. 롤백 중 발생하는 트랜잭션 에러는 `DbTransactionError` 로 표준화된다.
6
4
 
7
5
  ## DbContext (abstract class)
8
6
 
9
7
  ```typescript
10
8
  abstract class DbContext implements DbContextBase {
11
9
  constructor(executor: DbContextExecutor, opt: { database: string; schema?: string });
12
-
13
- status: DbContextStatus; // "ready" | "connect" | "transact"
10
+ status: DbContextStatus; // "ready" | "connect" | "transact"
14
11
  get database(): string | undefined;
15
12
  get schema(): string | undefined;
16
- migrations: Migration[]; // 서브클래스에서 오버라이드
17
-
18
- // 등록 (protected — 서브클래스 프로퍼티 초기화에서 사용)
19
- protected queryable<T>(builder: T): () => Queryable<...>;
20
- protected executable<T>(builder: T): () => Executable<...>;
21
-
22
- // 연결
23
- connect<R>(fn: () => Promise<R>, isolationLevel?: IsolationLevel): Promise<R>;
24
- connectWithoutTransaction<R>(callback: () => Promise<R>): Promise<R>;
25
- transaction<R>(fn: () => Promise<R>, isolationLevel?: IsolationLevel): Promise<R>;
26
-
27
- // DDL 실행 / DDL QueryDef 생성기 / initialize ...
13
+ migrations: Migration[]; // 서브클래스에서 오버라이드
28
14
  }
29
15
  ```
30
16
 
31
- 식별자 풀이:
17
+ 서브클래스에서 `protected queryable(builder)` / `protected executable(builder)` 로 멤버를 등록한다.
32
18
 
33
- - constructor `executor: DbContextExecutor` — 실제 DB 연결·쿼리 실행을 위임할 어댑터(서버 node executor, 클라이언트 service-client executor). 이 패키지는 SQL/QueryDef 까지만 만들고 실행은 전부 executor 가 한다.
34
- - constructor `opt.database: string`기본 데이터베이스명. 빌더가 database 지정하지 않으면 이 값이 객체 네임스페이스에 쓰인다.
35
- - constructor `opt.schema?: string`기본 스키마명(MSSQL `dbo`, PostgreSQL `public`). MySQL 무시.
36
- - `status: "ready"|"connect"|"transact"` — 현재 상태. "ready"=미연결, "connect"=연결됨(트랜잭션 없음), "transact"=트랜잭션 중. `connect()` 중복 호출 방지·트랜잭션 중 DDL 차단 판정에 쓰인다.
37
- - `database` / `schema` (getter) 주입한 opt 값을 그대로 노출. 빌더의 `getQueryDefObjectName` 기본값으로 쓰임.
38
- - `migrations: Migration[]` — 마이그레이션 정의 배열. 기본 `[]`, 서브클래스에서 오버라이드해 채운다. `initialize()` 가 미실행 항목만 순서대로 실행.
39
- - `queryable(builder)` (protected) — `TableBuilder`/`ViewBuilder` 를 받아 호출할 때마다 새 alias 가 붙는 `() => Queryable` 팩토리를 반환. 서브클래스에서 `user = this.queryable(User)` 형태로 멤버를 만든다.
40
- - `executable(builder)` (protected) — `ProcedureBuilder` 를 받아 `() => Executable` 팩토리를 반환. 서브클래스에서 `getUserById = this.executable(GetUserById)` 형태로 만든다.
41
- - `connect(fn, isolationLevel?)` — 연결 + 트랜잭션으로 `fn` 을 감싼다. 정상 종료 시 commit, throw 시 rollback 후 재throw, 무조건 close. 첫 호출 시 관계 정합성을 1회 검증(`validateRelations`). 기본 진입점.
42
- - `connectWithoutTransaction(callback)` — 연결만 하고 트랜잭션은 열지 않음. 트랜잭션 안에서 동작하지 않는 작업(`initialize`/일부 DDL) 전용. 끝나면 close.
43
- - `transaction(fn, isolationLevel?)` — 이미 `connect` 상태일 때 그 안에서 트랜잭션 블록을 추가로 연다. "transact" 상태에서 재호출하면 throw.
44
- - `isolationLevel?` — 트랜잭션 격리 수준. 미지정 시 executor/DB 기본값. 값별 의미는 아래 `IsolationLevel` 참조.
19
+ - `executor`: `DbContextExecutor` — 실제 connect/close/begin/commit/rollback/executeDefs 수행하는 어댑터. 서버는 node 구현, 클라이언트는 service-client 구현을 넣는다.
20
+ - `opt.database`: string — 대상 데이터베이스 이름. `database` getter 노출.
21
+ - `opt.schema`: string — MSSQL/PostgreSQL 스키마(선택). 미지정 dialect 기본값.
22
+ - `status`: `"ready" | "connect" | "transact"` — 현재 연결 단계. "ready"=미연결, "connect"=연결됨(트랜잭션 ), "transact"=트랜잭션 중. `transact` 상태에서 DDL 실행 throw.
23
+ - `migrations`: `Migration[]` — 서브클래스에서 오버라이드하는 마이그레이션 정의 배열. `initialize()` 이미 적용된 것을 제외하고 순서대로 실행.
45
24
 
46
- 사용 (직접 API):
25
+ ### 멤버 등록 (protected)
26
+
27
+ - `queryable(builder)` — `TableBuilder`/`ViewBuilder` 를 받아 `() => Queryable<...>` 팩토리를 반환. 호출할 때마다 새 alias 가 부여된 Queryable 생성. CUD 는 `TableBuilder` 기반에서만 가능.
28
+ - `executable(builder)` — `ProcedureBuilder` 를 받아 `() => Executable<...>` 팩토리를 반환.
47
29
 
48
30
  ```typescript
49
- class MainDb extends DbContext {
31
+ class AppDb extends DbContext {
50
32
  user = this.queryable(User);
51
- post = this.queryable(Post);
52
- getUserById = this.executable(GetUserById);
53
- override migrations = [{ name: "001", up: async (db) => { await db.createTable(User); } }];
33
+ activeUsers = this.queryable(ActiveUsers); // View
34
+ getUserById = this.executable(GetUserById); // Procedure
35
+ override migrations = [{ name: "001_init", up: async (db) => { await db.createTable(User); } }];
54
36
  }
55
-
56
- const db = new MainDb(executor, { database: "mydb" });
57
- const users = await db.connect(async () => {
58
- return db.user().where((u) => [expr.eq(u.isActive, true)]).execute();
59
- });
37
+ const db = new AppDb(executor, { database: "mydb" });
60
38
  ```
61
39
 
62
- 주의:
63
-
64
- - 트랜잭션("transact") 상태에서 DDL(`createTable` 등)을 `executeDefs` 로 보내면 "TRANSACTION 상태에서는 DDL을 실행할 수 없습니다" throw. DDL 은 `connectWithoutTransaction` 안에서 실행.
65
- - 롤백 자체가 실패해도 원래 에러를 우선 throw 하고, 롤백 실패 원인은 `err.cause` 로 부착(단, `NO_ACTIVE_TRANSACTION` 은 무시).
66
-
67
- ## DDL 실행 메서드
68
-
69
- `connectWithoutTransaction` 안에서 호출하며, 즉시 executor 로 실행한다. 모두 `Promise<void>`(예외: `schemaExists` → `Promise<boolean>`).
40
+ ### 연결·트랜잭션
70
41
 
71
- - `createTable(table: TableBuilder)` / `dropTable(table)` / `renameTable(table, newName)` 테이블 생성/삭제/이름변경. drop·rename `table` 인자는 `QueryDefObjectName`(`{ database?, schema?, name }`).
72
- - `createView(view: ViewBuilder)` / `dropView(view)` 생성/삭제.
73
- - `createProc(procedure: ProcedureBuilder)` / `dropProc(procedure)` 프로시저 생성/삭제.
74
- - `addColumn(table, columnName, column: ColumnBuilder)` / `dropColumn(table, column)` / `modifyColumn(table, columnName, column)` / `renameColumn(table, column, newName)` column 추가/삭제/타입·속성변경/이름변경.
75
- - `addPrimaryKey(table, columns: string[])` / `dropPrimaryKey(table)` — PK 추가/삭제. 복합 PK 는 `columns` 에 여러 이름.
76
- - `addForeignKey(table, relationName, relationDef: ForeignKeyBuilder)` / `dropForeignKey(table, relationName)` — FK 제약 추가/삭제.
77
- - `addIndex(table, indexBuilder: IndexBuilder<string[]>)` / `dropIndex(table, columns: string[])` — index 추가/삭제. drop 은 column 이름 배열로 식별.
78
- - `clearSchema(params: { database; schema? })` — 스키마의 모든 객체 삭제(초기화).
79
- - `schemaExists(database, schema?): Promise<boolean>` — 스키마 존재 여부.
80
- - `truncate(table)` — 테이블 데이터 전체 비우기(구조 유지).
81
- - `switchFk(table, enabled: boolean)` — FK 제약 일시 활성/비활성. `enabled` false=비활성(대량 적재 전), true=재활성. DDL 이 아니라 트랜잭션 안에서도 호출 가능.
82
-
83
- 각 `createX`/`dropX`/... 에는 동일 시그니처의 `getXQueryDef(...): QueryDef` 생성기 버전이 쌍으로 존재(실행하지 않고 QueryDef AST 만 반환). 추가로 `getCreateObjectQueryDef(builder)` 는 Table/View/Procedure 중 무엇이든 받아 알맞은 create QueryDef 를 반환한다. 마이그레이션에서 여러 DDL 을 모아 한 번에 보내거나 DDL 을 검사·로깅할 때 사용.
84
-
85
- ## initialize
42
+ - `connect(fn, isolationLevel?)` 연결 → 트랜잭션 시작 → `fn` 실행 → 성공 시 commit, 예외 rollback 재throw 최종 close. `ready` 아니면 throw. 최초 호출 시 관계 정의를 1회 검증. 업무 단위의 표준 진입점.
43
+ - `connectWithoutTransaction(callback)` 트랜잭션 없이 연결만 잡고 `callback` 실행 close. DDL·초기화처럼 트랜잭션 밖에서 실행해야 하는 작업용.
44
+ - `transaction(fn, isolationLevel?)` 이미 `connect` 상태에서 추가 트랜잭션 경계를 잡을 때. `transact` 상태면 throw. 성공 시 commit, 예외 시 rollback.
45
+ - `isolationLevel`: `"READ_UNCOMMITTED" | "READ_COMMITTED" | "REPEATABLE_READ" | "SERIALIZABLE"` 격리 수준(선택). 미지정 DB 기본값(보통 READ_COMMITTED). 더티 리드 허용~완전 직렬화 순으로 엄격해짐.
86
46
 
87
47
  ```typescript
88
- initialize(options?: { dbs?: string[]; force?: boolean }): Promise<boolean>
48
+ await db.connect(async () => {
49
+ const users = await db.user().where((u) => [expr.eq(u.isActive, true)]).execute();
50
+ await db.user().where((u) => [expr.eq(u.id, 1)]).update((u) => ({ name: "수정" }));
51
+ }); // 콜백 정상 종료 시 commit, throw 시 rollback
89
52
  ```
90
53
 
91
- - `dbs?: string[]` 초기화 대상 데이터베이스명 목록. 미지정 시 컨텍스트의 기본 database.
92
- - `force?: boolean` — true 면 기존 스키마를 비우고(`clearSchema`) 전체 재생성. false/미지정이면 미적용 마이그레이션만 증분 실행하고, 변경이 있었는지를 boolean 으로 반환.
93
- - 반환값 실제로 스키마를 만들거나 마이그레이션을 적용했으면 true. 트랜잭션 안에서 돌지 않으므로 `connectWithoutTransaction` 으로 호출.
54
+ ### DDL 실행 메서드 (트랜잭션 밖에서만)
55
+
56
+ 각각 `executeDefs` 즉시 실행한다. `transact` 상태에서 호출하면 throw.
94
57
 
95
- ## DbContextBase / DbContextStatus / DbContextDdlMethods
58
+ - `createTable(table)` / `dropTable(name)` / `renameTable(name, newName)` — 테이블 생성/삭제/이름변경.
59
+ - `truncate(name)` — 테이블 비우기(전 행 삭제, identity 리셋).
60
+ - `createView(view)` / `dropView(name)` — 뷰 생성/삭제.
61
+ - `createProc(procedure)` / `dropProc(name)` — 프로시저 생성/삭제.
62
+ - `addColumn(table, columnName, column)` / `dropColumn(table, column)` / `modifyColumn(table, columnName, column)` / `renameColumn(table, column, newName)` — 컬럼 변경. `column` 은 `ColumnBuilder`.
63
+ - `addPrimaryKey(table, columns)` / `dropPrimaryKey(table)` — PK 추가/삭제. `columns` 는 컬럼명 배열(복합 PK).
64
+ - `addForeignKey(table, relationName, relationDef)` / `dropForeignKey(table, relationName)` — FK 추가/삭제. `relationDef` 는 `ForeignKeyBuilder`.
65
+ - `addIndex(table, indexBuilder)` / `dropIndex(table, columns)` — 인덱스 추가/삭제.
66
+ - `clearSchema({ database, schema? })` — 스키마 내 모든 객체 삭제.
67
+ - `schemaExists(database, schema?)` — 스키마 존재 여부 `boolean` 반환.
68
+ - `switchFk(table, enabled)` — FK 제약 활성/비활성 토글. `enabled` true=활성, false=비활성. DDL 이 아니라 `transact` 상태에서도 호출 가능(대량 적재 시 FK 일시 해제 용도).
69
+ - `getQueryDefObjectName(tableOrView)` — 빌더에서 dialect 네임스페이스가 반영된 `QueryDefObjectName` 산출.
96
70
 
97
- - `DbContextBase` (interface) `Queryable`/`Executable`/`ViewBuilder` 가 의존하는 컨텍스트 최소 면(`status`, `database`, `schema`, `getNextAlias()`, `resetAliasCounter()`, `executeDefs()`, `getQueryDefObjectName()`, `switchFk()`). 직접 구현할 일은 드물고, 커스텀 컨텍스트 타입의 상한으로 쓰인다.
98
- - `DbContextStatus` — `"ready" | "connect" | "transact"`. 위 `status` 와 동일 의미.
99
- - `DbContextDdlMethods` (interface) — 위 DDL 실행 메서드 + QueryDef 생성기 전체를 모은 인터페이스. `Migration.up(db)` 의 `db` 타입이 `DbContextBase & DbContextDdlMethods` 라 마이그레이션 콜백에서 DDL 을 호출할 수 있다.
71
+ ### DDL QueryDef 생성기 (실행 없이 def )
100
72
 
101
- ## DbContextExecutor / ResultMeta
73
+ 실행 메서드와 1:1 대응하는 `get*QueryDef(...)` 가 모두 있다(`getCreateTableQueryDef`, `getDropTableQueryDef`, `getAddColumnQueryDef`, `getAddForeignKeyQueryDef`, `getTruncateQueryDef`, `getSwitchFkQueryDef`, `getClearSchemaQueryDef`, `getSchemaExistsQueryDef` 등). 실행하지 않고 `QueryDef` AST 만 얻어 배치 실행·검증·SQL 변환에 쓸 때 사용. 단일 `getCreateObjectQueryDef(builder)` 는 Table/View/Procedure 빌더 종류를 보고 알맞은 CREATE def 를 만든다.
102
74
 
103
- `DbContextExecutor` (interface) DbContext 가 연결·실행을 위임할 어댑터. 직접 구현은 서버/클라이언트 어댑터 패키지에서만.
75
+ ### 마이그레이션 / 초기화
104
76
 
105
- - `connect(): Promise<void>` / `close(): Promise<void>` 물리 연결 수립/종료.
106
- - `beginTransaction(isolationLevel?)` / `commitTransaction()` / `rollbackTransaction()` 트랜잭션 제어. rollback 은 활성 트랜잭션이 없으면 `DbTransactionError(NO_ACTIVE_TRANSACTION)` 를 던질 수 있음.
107
- - `executeDefs<T>(defs: QueryDef[], resultMetas?: (ResultMeta|undefined)[]): Promise<T[][]>` QueryDef 배열을 실행하고 def별 결과 배열을 반환. `resultMetas` 있으면 해당 def 결과를 그 메타로 타입 환원.
108
- - `ResultMeta` (interface) — `{ columns: Record<string, ColumnPrimitiveStr>; joins: Record<string, { isSingle: boolean }> }`. SELECT 결과를 TS 객체로 환원할 때의 column 타입·JOIN 중첩 구조 메타. `Queryable.getResultMeta()` 가 생성. (자세히는 [types.md](./types.md))
77
+ - `initialize(options?)` `migrations` 중 미적용분을 순서대로 실행하고, 적용 여부를 `_migration` 시스템 테이블에 기록. `boolean` 반환(변경 발생 여부 등).
78
+ - `options.dbs`: string[]대상 데이터베이스 목록 한정(선택).
79
+ - `options.force`: booleantrue 강제 재초기화. 스키마를 다시 깔아야 때만 사용.
80
+ - `executeDefs(defs, resultMetas?)` — `QueryDef[]` executor 실행해 `T[][]`(def 결과) 반환. `transact` 상태에서 DDL 타입이 섞이면 throw. 빌더가 만든 def 저수준으로 직접 실행할 사용.
109
81
 
110
- ## Migration
82
+ ## Migration (interface)
111
83
 
112
84
  ```typescript
113
85
  interface Migration {
@@ -116,47 +88,41 @@ interface Migration {
116
88
  }
117
89
  ```
118
90
 
119
- - `name: string` — 마이그레이션 고유 이름(타임스탬프 권장, `20260105_001_create_user`). 적용 여부 추적 키이므로 배포 변경 금지.
120
- - `up(db)`적용 실행할 함수. `db` 로 DDL 메서드를 호출. 미적용 항목만 `name` 순서대로 1회씩 실행된다.
121
-
122
- ## IsolationLevel
123
-
124
- `connect`/`transaction`/`beginTransaction` 의 격리 수준.
125
-
126
- - `"READ_UNCOMMITTED"` — 커밋 전 데이터까지 읽음(Dirty Read 허용). 가장 느슨, 정합성 낮음.
127
- - `"READ_COMMITTED"` — 커밋된 데이터만 읽음. 일반적 기본값.
128
- - `"REPEATABLE_READ"` — 트랜잭션 내 동일 쿼리가 동일 결과 보장.
129
- - `"SERIALIZABLE"` — 완전 직렬화. 가장 엄격, 경합 시 잠금/충돌 비용 큼.
91
+ - `name`: string — 고유 마이그레이션 식별자. 타임스탬프 접두 권장(`20260105_001_...`). 값으로 적용 여부를 추적하므로 한번 배포되면 변경 금지.
92
+ - `up`: `(db) => Promise<void>` 스키마 변경 함수. `db` 로 DDL 메서드를 호출. 실행은 `initialize()` 미적용분만 골라 호출.
130
93
 
131
94
  ## DbTransactionError / DbErrorCode
132
95
 
133
- DBMS 네이티브 트랜잭션 에러를 표준 코드로 래핑한다. 롤백·재시도 분기에서 `instanceof DbTransactionError` + `err.code` 판별.
96
+ DBMS 네이티브 에러를 dialect 독립 코드로 래핑한다. `connect`/`transaction` 롤백 단계에서 "이미 롤백되어 활성 트랜잭션이 없음" 같은 상황을 코드로 식별해 무시 여부를 판단할 때 쓴다.
134
97
 
135
98
  ```typescript
136
99
  class DbTransactionError extends Error {
137
- readonly name = "DbTransactionError";
138
100
  constructor(code: DbErrorCode, message: string, originalError?: unknown);
139
101
  readonly code: DbErrorCode;
140
102
  readonly originalError?: unknown;
141
103
  }
104
+ enum DbErrorCode { NO_ACTIVE_TRANSACTION, TRANSACTION_ALREADY_STARTED, DEADLOCK, LOCK_TIMEOUT }
142
105
  ```
143
106
 
144
- - `code: DbErrorCode` — 표준화된 에러 코드(아래). 분기 기준.
145
- - `message: string`사람용 메시지.
146
- - `originalError?: unknown` — 원본 DBMS 에러(디버깅용 원형 보존).
147
-
148
- `DbErrorCode` (enum, 값은 동명 문자열):
149
-
150
- - `NO_ACTIVE_TRANSACTION` — 롤백 대상 활성 트랜잭션 없음. 이미 롤백/커밋된 경우. `connect` 내부에서 이 코드는 무시된다.
151
- - `TRANSACTION_ALREADY_STARTED` — 트랜잭션이 이미 시작됨(중복 begin).
152
- - `DEADLOCK` — 데드락 발생. 재시도 정책 트리거로 사용.
153
- - `LOCK_TIMEOUT` — 잠금 대기 타임아웃.
107
+ - `code`: `DbErrorCode` — 표준화된 에러 종류. 아래 enum literal 로 분기.
108
+ - `originalError`: unknown래핑 전 원본 DBMS 에러(디버깅용). dialect 별 원인 추적 시 참조.
109
+ - `NO_ACTIVE_TRANSACTION` — 롤백/커밋할 활성 트랜잭션이 없음. 이미 롤백된 경우 무시 분기에 사용.
110
+ - `TRANSACTION_ALREADY_STARTED` — 트랜잭션이 이미 시작됨(중첩 시작 시도).
111
+ - `DEADLOCK` 교착 상태로 트랜잭션이 강제 중단됨. 재시도 정책 분기에 사용.
112
+ - `LOCK_TIMEOUT` — 잠금 대기 시간 초과. 재시도/백오프 분기에 사용.
154
113
 
155
114
  ```typescript
156
115
  try {
157
- await executor.rollbackTransaction();
116
+ await db.rollbackTransaction();
158
117
  } catch (err) {
159
118
  if (err instanceof DbTransactionError && err.code === DbErrorCode.NO_ACTIVE_TRANSACTION) return;
160
119
  throw err;
161
120
  }
162
121
  ```
122
+
123
+ ## DbContextBase / DbContextStatus / DbContextDdlMethods / SD_BUILDER
124
+
125
+ - `DbContextBase` (interface) — `Queryable`/`Executable`/`ViewBuilder` 가 의존하는 컨텍스트 최소 인터페이스(`status`, `database`, `schema`, `getNextAlias`, `resetAliasCounter`, `executeDefs`, `getQueryDefObjectName`, `switchFk`). `DbContext` 가 구현한다. executor·뷰 정의처럼 컨텍스트 전체가 아니라 일부 능력만 요구하는 시그니처에 쓴다.
126
+ - `DbContextStatus` (type) — `"ready" | "connect" | "transact"`. 위 `status` 와 동일.
127
+ - `DbContextDdlMethods` (interface) — DDL 실행 메서드 + `get*QueryDef` 생성기를 모은 인터페이스. `Migration.up` 의 `db` 파라미터 타입(`DbContextBase & DbContextDdlMethods`)에 쓰여, 마이그레이션 함수에서 DDL 만 노출되게 한다.
128
+ - `SD_BUILDER` (symbol) — `queryable()`/`executable()` 가 반환하는 팩토리 함수에 원본 빌더를 매다는 심볼 키. 등록된 멤버에서 원본 `TableBuilder`/`ViewBuilder`/`ProcedureBuilder` 를 역참조해야 할 때(스키마 수집·DDL 자동화 등) 사용.
@@ -1,125 +1,111 @@
1
1
  # @simplysm/orm-common — expr (SQL 표현식 빌더)
2
2
 
3
- `expr` 객체로 dialect 독립 SQL 표현식을 JSON AST(`Expr`)로 조립한다. dialect별 QueryBuilder 가 MySQL/MSSQL/PostgreSQL 로 변환. where/having 콜백은 `WhereExprUnit[]`, select/orderBy/groupBy 콜백은 `ExprUnit<T>`, update/upsert/where 비교값은 `ExprInput<T>`(= `ExprUnit<T> | T`)를 다룬다.
3
+ `expr` 객체로 dialect 독립 SQL 표현식을 JSON AST(`Expr`)로 조립한다. dialect 별 QueryBuilder 가 MySQL/MSSQL/PostgreSQL 로 변환. where/having 콜백은 `WhereExprUnit[]`, select/orderBy/groupBy 콜백은 `ExprUnit<T>`, update/upsert/insert/where 비교값은 `ExprInput<T>`(= `ExprUnit<T> | T`)를 다룬다. 비교·쓰기 값은 리터럴을 그대로 넘긴다 — `expr.val` 로 감싸지 말 것(orm.md). `expr.subquery`/`expr.exists` 를 SELECT 절에 넣지 말고 `joinSingle` 로 집계를 부착한다(orm.md).
4
4
 
5
- **리터럴 래핑 규칙(orm.md)**: where 비교·update/upsert/insert 값은 `ExprInput` 자리라 **리터럴을 그대로** 넘긴다 — `expr.val` 로 감싸지 말 것. `expr.val` 은 `select` 콜백에서 상수 column 을 만들 때처럼 `ExprUnit` 이 요구되는 자리에서만.
5
+ ## 래퍼 타입
6
6
 
7
- ```typescript
8
- // 좋음 // 나쁨 (불필요한 래핑)
9
- .where((u) => [expr.eq(u.status, "active")]) // expr.eq(u.status, expr.val("string","active"))
10
- .update((u) => ({ name: "새이름" })) // ({ name: expr.val("string","새이름") })
11
- ```
12
-
13
- 연산 함수 인자는 대부분 `ExprInput<T>` — column 프록시(`ExprUnit`)·중첩 식·리터럴을 섞어 넘길 수 있다. `undefined` 컬럼 타입은 `.n` getter 로 non-null 단언 가능.
14
-
15
- ## ExprUnit / WhereExprUnit / ExprInput
16
-
17
- - `ExprUnit<TPrimitive>` — 타입 안전 값 표현식 래퍼. `.dataType`(ColumnPrimitiveStr), `.expr`(AST), `.$infer`(타입 추론 마커). `.n` getter — 동일 식을 `NonNullable<T>` 로 좁힌 새 ExprUnit(`p.state!.sumQty` 처럼 nullable join 컬럼을 non-null 로 다룰 때 `.n` 대신 `!` 도 가능).
18
- - `WhereExprUnit` — WHERE/HAVING 절 boolean 표현식 래퍼(`.expr: WhereExpr`). 비교·논리 함수가 반환.
19
- - `ExprInput<T>` — `ExprUnit<T> | T`. 연산 인자·쓰기 값이 받는 타입(리터럴 직접 허용).
7
+ - `ExprUnit<T>` — 타입 안전 표현식 래퍼. `dataType`(`ColumnPrimitiveStr`)·`expr`(`Expr` AST) 보유. getter `n` 은 동일 표현식을 non-nullable 타입(`NonNullable<T>`)으로 다시 래핑(coalesce 후 null 아님이 보장될 때 타입만 좁힐 용도).
8
+ - `WhereExprUnit` WHERE 절용 래퍼(`WhereExpr` AST 보유). `where`/`having` 콜백 반환 원소.
9
+ - `ExprInput<T>` = `ExprUnit<T> | T` — 표현식 또는 리터럴 둘 다 받는 입력 타입. 비교 target·쓰기 값 자리.
10
+ - `SwitchExprBuilder<T>` — `expr.switch()` 반환하는 CASE 빌더(`case`/`default`).
20
11
 
21
12
  ## 값 생성
22
13
 
23
- - `val(dataType, value)` — 리터럴을 ExprUnit 으로 래핑. `dataType`="string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Bytes". `value` undefined 허용(결과 타입에 undefined 포함). `select` 상수 컬럼 등 ExprUnit 강제 자리에서만.
24
- - `col(dataType, ...path)` — column 참조 생성(내부용). 보통 콜백 프록시로 충분.
25
- - `raw(dataType)\`SQL\`` — 이스케이프 해치. 태그드 템플릿, 보간값은 자동 파라미터화. ORM 미지원 DB 함수 직접 사용 시. 보간값은 `ExprInput`. union 의 NULL 자리채움(`` expr.raw("number")`NULL` ``)에도 사용(orm-union.md).
26
- - `toExpr(value)` — `ExprInput` `Expr` AST 변환(내부 헬퍼).
14
+ - `val(dataType, value)` — 리터럴을 `ExprUnit` 으로 래핑. `dataType`=`"string"|"number"|"boolean"|"DateTime"|"DateOnly"|"Time"|"Uuid"|"Buffer"` 등 원시 타입 문자열. `value`=값(undefined 허용 NULL). `ExprUnit` 이 요구되는 자리(select 의 리터럴 상수 컬럼 등)에서만 사용.
15
+ - `col(dataType, ...path)` — 컬럼 참조 `ExprUnit` 생성. `path`=별칭·컬럼 경로. 보통 콜백의 컬럼 프록시가 대신하므로 내부용.
16
+ - `raw(dataType)\`...\``Raw SQL 이스케이프 해치. 태그드 템플릿. 보간 값은 자동 파라미터화. ORM 미지원 DB 함수·UNION 타입 명시 NULL(`` expr.raw("number")`NULL` ``) 사용.
17
+ - `toExpr(value)` — `ExprInput` `Expr` AST 변환(내부 헬퍼).
27
18
 
28
- ## WHERE — 비교 (반환 `WhereExprUnit`)
19
+ ## WHERE — 비교 (`WhereExprUnit` 반환)
29
20
 
30
- - `eq(source, target)` — 동등(NULL 안전: MySQL `<=>`, 그 외 `IS NULL OR =`).
21
+ - `eq(source, target)` — `=` 비교(NULL 안전: MySQL `<=>`, 그 외 `IS NULL OR =`). NULL 끼리도 일치 판정해야 할 때.
31
22
  - `gt(source, target)` / `lt(source, target)` / `gte(source, target)` / `lte(source, target)` — `>` / `<` / `>=` / `<=`.
32
- - `between(source, from?, to?)` — 범위. `from`/`to` 하나가 undefined 면 방향 제한 없음(한쪽만 주면 `>=`/`<=` 로 동작).
33
- - `null(source)` — IS NULL.
34
- - `like(source, pattern)` — LIKE. `%`=0+ 문자, `_`=1 문자, 특수문자 `\` 이스케이프.
35
- - `regexp(source, pattern)` — 정규식 매칭(구문은 DBMS 의존).
36
- - `in(source, values)` — IN(값 목록).
37
- - `inQuery(source, query)` — IN (SELECT ...). `query` 는 단일 column SELECT Queryable — 아니면 throw.
38
- - `exists(query)` — EXISTS (서브쿼리 행 존재). SELECT 절은 제거되어 패킷 절약. (orm.md: SELECT 절 내부 `exists` 는 행당 N회 실행되므로 금지 — `joinSingle` 로 부착)
23
+ - `between(source, from?, to?)` — 범위. `from` undefined 하한 없음, `to` undefined 면 상한 없음(한쪽만 주면 단방향 부등호).
24
+ - `null(source)` — `IS NULL`. nullable 컬럼의 결측 판정.
39
25
 
40
- ## WHERE — 논리 (반환 `WhereExprUnit`)
26
+ ## WHERE — 문자열/IN/논리
41
27
 
42
- - `not(arg)` — NOT.
43
- - `and(conditions)` — AND 결합. 배열이면 throw. `where` 에 배열 넘기면 자동 AND 라 보통 불필요.
44
- - `or(conditions)` — OR 결합. 빈 배열이면 throw.
28
+ - `like(source, pattern)` — `LIKE`(% 다수, _ 단일, `\` 이스케이프). 부분/접두/접미 검색.
29
+ - `regexp(source, pattern)` — 정규식 매칭(구문은 DBMS 의존).
30
+ - `in(source, values)` — `IN (값목록)`. `values`=`ExprInput[]`.
31
+ - `inQuery(source, query)` — `IN (SELECT 단일컬럼)`. 서브쿼리가 단일 컬럼 select 가 아니면 throw.
32
+ - `exists(query)` — `EXISTS (...)`. 서브쿼리 SELECT 절은 패킷 절약 위해 제거됨. WHERE 절 존재 검사용(SELECT 절에는 쓰지 말 것).
33
+ - `not(arg)` — 조건 부정.
34
+ - `and(conditions)` / `or(conditions)` — 조건 배열 AND/OR 결합. 빈 배열이면 `ArgumentError`. (`where` 에 배열을 넘기면 자동 AND 이므로 `and` 는 OR 안에서 묶을 때 등에 사용.)
45
35
 
46
- ## SELECT — 문자열 (반환 `ExprUnit`)
36
+ ```typescript
37
+ db.user().where((u) => [
38
+ expr.eq(u.status, "active"),
39
+ expr.between(u.age, 18, undefined),
40
+ expr.or([expr.like(u.name, "김%"), expr.like(u.name, "이%")]),
41
+ ])
42
+ ```
47
43
 
48
- - `concat(...args)`CONCAT(NULL→빈문자열).
49
- - `left(source, length)` / `right(source, length)` — 왼쪽/오른쪽 N자.
44
+ ## SELECT문자열 (`ExprUnit` 반환)
45
+
46
+ - `concat(...args)` — `CONCAT`(NULL 은 빈 문자열 처리).
47
+ - `left(source, length)` / `right(source, length)` — 왼쪽/오른쪽 N자 추출.
50
48
  - `trim(source)` — 양쪽 공백 제거.
51
- - `padStart(source, length, fillString)` — LPAD(목표 길이까지 왼쪽 채움).
49
+ - `padStart(source, length, fillString)` — `LPAD`. `length` 도달까지 `fillString` 으로 왼쪽 패딩(주문번호 zero-pad 등).
52
50
  - `replace(source, from, to)` — 문자열 치환.
53
51
  - `upper(source)` / `lower(source)` — 대/소문자 변환.
54
52
  - `length(source)` — 문자 수. `byteLength(source)` — 바이트 수(UTF-8 CJK 3바이트).
55
- - `substring(source, start, length?)` — 부분 문자열(1-기반 인덱스, length 생략 시 끝까지).
56
- - `indexOf(source, search)` — 위치(1-기반, 없으면 0).
57
-
58
- ## SELECT — 숫자 (반환 `ExprUnit`)
53
+ - `substring(source, start, length?)` — 부분 문자열(1부터 시작, `length` 생략 시 끝까지).
54
+ - `indexOf(source, search)` — 위치 찾기(1부터, 없으면 0).
59
55
 
60
- - `abs(source)`절대값. `round(source, digits)` — 반올림(소수 자릿수). `ceil(source)` — 올림. `floor(source)` — 내림.
56
+ ## SELECT숫자 / 날짜
61
57
 
62
- ## SELECT 날짜 (반환 `ExprUnit`)
58
+ - `abs(source)` / `round(source, digits)` / `ceil(source)` / `floor(source)` — 절대값/반올림(`digits`=소수자릿수)/올림/내림.
59
+ - `year`/`month`/`day`/`hour`/`minute`/`second`(source) — 날짜·시간 구성요소 추출(number).
60
+ - `isoWeek(source)` — ISO 8601 주 번호(월요일 시작, 1~53). `isoWeekStartDate(source)` — 해당 주 월요일(DateOnly). `isoYearMonth(source)` — 해당 월 1일(DateOnly).
61
+ - `dateDiff(unit, from, to)` — 날짜 차이(`to - from`). `unit`=`"year"|"month"|"day"|"hour"|"minute"|"second"`.
62
+ - `dateAdd(unit, source, value)` — 날짜 가감(`value` 음수 허용). 결과 타입은 `source` 와 동일.
63
+ - `formatDate(source, format)` — 날짜 포맷 문자열(포맷 구문 DBMS 의존, 예 `"%Y-%m-%d"`).
63
64
 
64
- - `year(source)` / `month(source)` / `day(source)` 연/월/일(source: DateTime|DateOnly).
65
- - `hour(source)` / `minute(source)` / `second(source)` — 시/분/초(source: DateTime|Time).
66
- - `isoWeek(source)` — ISO 주 번호(1~53). `isoWeekStartDate(source)` — 그 주 월요일. `isoYearMonth(source)` — 해당 월 1일.
67
- - `dateDiff(unit, from, to)` — 날짜 차(to - from). `unit`="year"|"month"|"day"|"hour"|"minute"|"second".
68
- - `dateAdd(unit, source, value)` — 날짜 가감(value 음수 허용). 결과 타입은 source 와 동일.
69
- - `formatDate(source, format)` — 포맷 문자열로 변환(`"%Y-%m-%d"` 등, 규칙 DBMS 의존).
65
+ ## SELECT조건
70
66
 
71
- ## SELECT조건 (반환 `ExprUnit`)
67
+ - `coalesce(...args)` non-null 값(`COALESCE`). 마지막 인자가 non-nullable 이면 결과도 non-nullable 로 추론.
68
+ - `nullIf(source, value)` — `source === value` 면 NULL, 아니면 source(빈 문자열 → NULL 변환 등).
69
+ - `is(condition)` — `WhereExprUnit` 을 boolean `ExprUnit` 으로 변환(조건 결과를 컬럼으로). select 절에서 도메인 boolean 컬럼 만들 때.
70
+ - `switch<T>()` — CASE WHEN 빌더(`SwitchExprBuilder`). `case(condition, then)` 체이닝 후 `default(value)` 로 종료. then/default 의 타입에서 결과 타입 추론(모두 리터럴이면 non-null 하나에서 추론, 전부 null 이면 throw).
71
+ - `if(condition, then, else_)` — 삼항(IIF/IF). then/else 중 ExprUnit 또는 non-null 리터럴에서 타입 추론.
72
72
 
73
- - `coalesce(...args)` — 첫 non-null. 마지막 인수가 non-nullable 이면 결과도 non-nullable. join 도출 컬럼 기본값(`coalesce(p.state!.sum, 0)`)에 자주.
74
- - `nullIf(source, value)` source===value 이면 NULL(빈 문자열→NULL 변환 등).
75
- - `is(condition)` — `WhereExprUnit` → boolean column 으로 변환(SELECT 절에서 조건 결과를 컬럼화).
76
- - `switch<T>()` — CASE WHEN 빌더. `.case(condition, then).case(...).default(value)` 체이닝으로 `ExprUnit<T>` 마무리.
77
- - `if(condition, then, else_)` — 삼항(IF/IIF). then/else 중 최소 하나 non-null 아니면 throw(타입 추론용).
78
-
79
- ## SELECT — 집계 (반환 `ExprUnit`)
80
-
81
- NULL 값 행은 무시, 전부 NULL/무행이면 NULL.
73
+ ```typescript
74
+ db.user().select((u) => ({
75
+ isActive: expr.is(expr.eq(u.status, "active")),
76
+ grade: expr.switch<string>().case(expr.gte(u.score, 90), "A").case(expr.gte(u.score, 80), "B").default("F"),
77
+ }))
78
+ ```
82
79
 
83
- - `count(arg?, distinct?)` 수. `arg` 미지정=전체, `distinct: true`=중복 제거.
84
- - `sum(arg)` / `avg(arg)` — 합/평균(number, 결과 nullable).
85
- - `max(arg)` / `min(arg)` — 최대/최소(결과 nullable).
80
+ ## SELECT집계 / 기타
86
81
 
87
- ## SELECT 기타 (반환 `ExprUnit`)
82
+ 집계는 NULL 행을 무시하고, 모든 값이 NULL 이거나 행이 없을 때만 NULL 반환.
88
83
 
89
- - `greatest(...args)` / `least(...args)` 여러 최대/최소(행 비교). 인자 ExprUnit 하나는 있어야 타입 추론(없으면 throw).
90
- - `rowNum()` 전체 행 순번(1-기반, 단순 버전). `random()` — 0~1 난수(무작위 정렬에).
91
- - `cast(source, targetType)` 타입 변환. `targetType`=`DataType`(`{ type:"varchar", length:20 }` ).
92
- - `subquery(dataType, queryable)` 스칼라 서브쿼리(1행 1열). `queryable`=`getSelectQueryDef()` 보유 객체. (orm.md: SELECT 내부 subquery 는 행당 N회 실행 → `joinSingle` 권장)
84
+ - `count(arg?, distinct?)` `COUNT`. `arg` 생략 전체 행, 지정 컬럼의 non-null 행. `distinct` true 중복 제거 카운트.
85
+ - `sum(arg)` / `avg(arg)` — 합계/평균(number, NULL 가능).
86
+ - `max(arg)` / `min(arg)` 최대/최소(타입 유지, NULL 가능).
87
+ - `greatest(...args)` / `least(...args)` 인자들 중 최대/최소값( 단위, 집계 아님).
88
+ - `rowNum()` — 단순 행 순번(1부터). `random()` — 0~1 난수(무작위 정렬 `orderBy(() => expr.random())`).
89
+ - `cast(source, targetType)` — 타입 변환. `targetType`=`DataType`(예 `{ type: "varchar", length: 20 }`).
90
+ - `subquery(dataType, queryable)` — 스칼라 서브쿼리(단일 행·단일 컬럼). SELECT 절에서 단일 값 반환. (행마다 N회 실행되므로 집계는 `joinSingle` 권장, orm.md.)
93
91
 
94
- ## SELECT — Window 함수 (반환 `ExprUnit`)
92
+ ## SELECT — 윈도우 함수
95
93
 
96
- 모두 `spec: { partitionBy?: ExprInput[]; orderBy?: [ExprInput, ("ASC"|"DESC")?][] }`(OVER 절) 인자를 받음.
94
+ 모두 `spec: { partitionBy?: ExprInput[]; orderBy?: [ExprInput, ("ASC"|"DESC")?][] }` 를 받는다(`partitionBy`=구간 분할 컬럼, `orderBy`=구간 내 정렬).
97
95
 
98
- - `rowNumber(spec)` — 파티션 내 순번(1-기반).
99
- - `rank(spec)` — 순위(동순위 후 건너뜀: 1,1,3). `denseRank(spec)` — 순위(동순위 후 연속: 1,1,2).
100
- - `ntile(n, spec)` — 파티션을 n 그룹으로 분할(그룹 번호 1~n).
101
- - `lag(column, spec, options?)` / `lead(column, spec, options?)` — 이전/다음 행 값. `options.offset?`(기본 1), `options.default?`(없을 때 기본값).
96
+ - `rowNumber(spec)` — `ROW_NUMBER()`(파티션 내 1부터 순번).
97
+ - `rank(spec)` — `RANK()`(동순위 후 건너뜀: 1,1,3). `denseRank(spec)` — `DENSE_RANK()`(연속: 1,1,2).
98
+ - `ntile(n, spec)` — `NTILE(n)`(파티션을 `n` 그룹으로 분할, 1~n).
99
+ - `lag(column, spec, options?)` / `lead(column, spec, options?)` — 이전/다음 행 값. `options.offset`(기본 1)·`options.default`(이전/다음 행 없을 때 기본값).
102
100
  - `firstValue(column, spec)` / `lastValue(column, spec)` — 프레임 내 첫/마지막 값.
103
- - `sumOver(column, spec)` / `avgOver(column, spec)` / `minOver(column, spec)` / `maxOver(column, spec)` window 합/평균/최소/최대(누적합·이동평균).
104
- - `countOver(spec, column?)` — window 행 수. `column` 미지정=전체.
101
+ - `sumOver`/`avgOver`(column, spec) — 윈도우 합계/평균(누적합·이동평균).
102
+ - `countOver(spec, column?)` — 윈도우 카운트(`column` 생략 시 전체 행).
103
+ - `minOver`/`maxOver`(column, spec) — 윈도우 최소/최대.
105
104
 
106
105
  ```typescript
107
106
  db.order().select((o) => ({
108
107
  ...o,
108
+ rowNum: expr.rowNumber({ partitionBy: [o.userId], orderBy: [[o.createdAt, "DESC"]] }),
109
109
  runningTotal: expr.sumOver(o.amount, { partitionBy: [o.userId], orderBy: [[o.createdAt, "ASC"]] }),
110
- }));
111
- ```
112
-
113
- ## SwitchExprBuilder
114
-
115
- `expr.switch<T>()` 반환 객체.
116
-
117
- - `case(condition: WhereExprUnit, then: ExprInput<T>)` — 분기 추가(체이닝).
118
- - `default(value: ExprInput<T>)` — ELSE 값 + 마무리, `ExprUnit<T>` 반환. case/default 중 최소 하나 non-null 아니면 throw.
119
-
120
- ```typescript
121
- grade: expr.switch<string>()
122
- .case(expr.gte(u.score, 90), "A")
123
- .case(expr.gte(u.score, 80), "B")
124
- .default("F"),
110
+ }))
125
111
  ```