@simplysm/sd-claude 14.0.89 → 14.0.91

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 (79) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +52 -30
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +200 -38
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -53
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +66 -22
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +127 -40
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +60 -43
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +56 -20
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +74 -74
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +50 -40
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -15
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +59 -42
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +77 -62
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +8 -7
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +71 -43
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +22 -14
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +19 -19
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +17 -17
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +28 -28
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +37 -37
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +87 -219
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +54 -98
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +57 -99
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +60 -103
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +42 -47
  26. package/claude/references/sd-simplysm14/apis/core-common/obj.md +42 -88
  27. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -7
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +17 -12
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +14 -13
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +9 -8
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +14 -13
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +4 -8
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +14 -12
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +22 -22
  36. package/claude/references/sd-simplysm14/apis/excel/cell.md +37 -29
  37. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +29 -15
  38. package/claude/references/sd-simplysm14/apis/excel/style.md +33 -27
  39. package/claude/references/sd-simplysm14/apis/excel/utils.md +29 -19
  40. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +78 -55
  41. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +42 -45
  42. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -8
  43. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +118 -67
  44. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +83 -86
  45. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +102 -93
  46. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +138 -81
  47. package/claude/references/sd-simplysm14/apis/orm-common/types.md +49 -44
  48. package/claude/references/sd-simplysm14/apis/orm-node/README.md +42 -42
  49. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +44 -33
  50. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +11 -10
  51. package/claude/references/sd-simplysm14/apis/service-client/README.md +56 -52
  52. package/claude/references/sd-simplysm14/apis/service-client/orm.md +33 -28
  53. package/claude/references/sd-simplysm14/apis/service-client/transport.md +23 -21
  54. package/claude/references/sd-simplysm14/apis/service-common/README.md +83 -48
  55. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
  56. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
  57. package/claude/references/sd-simplysm14/apis/service-server/README.md +69 -81
  58. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +46 -43
  59. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +63 -37
  60. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +40 -30
  61. package/claude/references/sd-simplysm14/apis/storage/README.md +17 -17
  62. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +135 -140
  63. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
  64. package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
  65. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
  66. package/claude/references/sd-simplysm14/manuals/client-system-log.md +16 -4
  67. package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
  68. package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
  69. package/claude/rules/sd-design-rules.md +10 -0
  70. package/claude/skills/sd-demo/SKILL.md +0 -6
  71. package/claude/skills/sd-docs/SKILL.md +60 -0
  72. package/claude/{workflows/sd-docs.rules.md → skills/sd-docs/references/subagent-prompt.md} +118 -103
  73. package/claude/skills/sd-impl/SKILL.md +7 -4
  74. package/claude/skills/sd-spec/SKILL.md +842 -15
  75. package/claude/skills/sd-spec/references/example-spec.md +26 -36
  76. package/package.json +1 -1
  77. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -53
  78. package/claude/skills/sd-spec/references/spec-authoring.md +0 -519
  79. package/claude/workflows/sd-docs.js +0 -84
@@ -1,64 +1,105 @@
1
- # @simplysm/orm-common — 스키마 정의
1
+ # @simplysm/orm-common — 스키마 정의 (Table / View / Procedure / column / index / relation)
2
2
 
3
- DB 객체(Table/View/Procedure)와 그 구성요소(Column/Index/관계)를 fluent 빌더로 선언하는 묶음. 모든 빌더는 immutable — 각 메서드가 새 인스턴스를 반환한다. 정의한 빌더는 `DbContext` 안에서 `this.queryable()`/`this.executable()` 로 등록한다.
3
+ DB 객체(Table/View/Procedure)와 그 구성요소(column/index/관계)를 fluent 빌더로 선언하는 묶음. 모든 빌더는 immutable — 각 메서드가 새 인스턴스를 반환한다. 정의한 빌더는 `DbContext` 안에서 `this.queryable()`/`this.executable()` 로 등록한다. column 은 기본 `NOT NULL`; `.nullable()`/`.default(...)` 는 도메인 근거가 있을 때만 붙인다(orm.md).
4
4
 
5
5
  ## Table / TableBuilder
6
6
 
7
- `Table(name)` → `TableBuilder` 시작점. 체이닝으로 database·schema·columns·primaryKey·indexes·relations 를 붙인다.
8
-
9
- - `Table<TName>(name: TName): TableBuilder` — 이름만 가진 빈 빌더 생성. 이후 체이닝으로 정의를 쌓음.
10
- - `description(desc: string)` 테이블 설명. DDL 코멘트로 쓰임. 스키마 문서화·DB 코멘트가 필요할 때.
11
- - `database(db: string)` 소속 database 이름. dialect별 네임스페이스의 분절. 멀티 DB 환경에서 필수.
12
- - `schema(schema: string)` schema 이름. MSSQL(dbo)·PostgreSQL(public) 에서만 의미. MySQL 은 무시.
13
- - `columns(fn: (c) => Record<string, ColumnBuilder>)` — 컬럼 정의. `c` 는 `createColumnFactory()` 결과(int/varchar/... 타입 메서드). 타입 추론(`$inferSelect` 등)의 기준이 됨.
14
- - `primaryKey(...columns)` PK 컬럼 키. 여러 개 넘기면 복합 PK. CUD 의 output(pkColNames)·include 의 FK↔PK 매칭에 사용.
15
- - `indexes(fn: (i) => IndexBuilder[])` — 인덱스 배열. `i` 는 `createIndexFactory()` 결과(`i.index(...cols)`). DDL `addIndex` 생성에 사용.
16
- - `relations(fn: (r) => Record<string, 관계빌더>)` — FK/역참조 관계. `r` 는 Table 일 때 foreignKey/foreignKeyTarget/relationKey/relationKeyTarget 모두 제공. include() 자동 조인의 근거.
7
+ ```typescript
8
+ function Table<TName extends string>(name: TName): TableBuilder<TName, {}, {}>;
9
+
10
+ class TableBuilder<TName, TColumns, TRelations> {
11
+ readonly meta: { name; description?; database?; schema?; columns?; primaryKey?; relations?; indexes? };
12
+ readonly $inferSelect; // columns + 관계(optional)
13
+ readonly $inferColumns; // column 타입만
14
+ readonly $inferInsert; // INSERT 타입 (autoIncrement/nullable/default optional)
15
+ readonly $inferUpdate; // UPDATE 타입 (전부 optional)
16
+
17
+ description(desc: string): TableBuilder;
18
+ database(db: string): TableBuilder;
19
+ schema(schema: string): TableBuilder;
20
+ columns(fn: (c) => TNewColumns): TableBuilder;
21
+ primaryKey(...columns: (keyof TColumns)[]): TableBuilder;
22
+ indexes(fn: (i) => IndexBuilder[]): TableBuilder;
23
+ relations(fn: (r) => TRelations): TableBuilder;
24
+ }
25
+ ```
17
26
 
18
- 추론 전용 가상 프로퍼티(런타임 값 없음, 타입만): `$inferSelect`(컬럼+관계, 관계는 optional), `$inferColumns`(컬럼만), `$inferInsert`(autoIncrement/nullable/default 제외 필수), `$inferUpdate`(전부 optional).
27
+ - `Table(name)` `TableBuilder` 생성. 이후 fluent 메서드로 채운다.
28
+ - `description(desc)` — 테이블 설명. CREATE TABLE 시 DDL comment 로 들어감.
29
+ - `database(db)` — 데이터베이스명 고정. 미지정 시 `DbContext` 의 기본 database.
30
+ - `schema(schema)` — 스키마명(MSSQL/PostgreSQL). MySQL 무시.
31
+ - `columns(fn)` — column factory `c` 를 받아 `{ name: c.타입() }` 레코드를 반환. 아래 column factory 참조. 호출 후 `$inferColumns` 등 타입이 갱신.
32
+ - `primaryKey(...columns)` — PK column 이름(들). 여러 개 넘기면 복합 PK.
33
+ - `indexes(fn)` — index factory `i` 를 받아 `IndexBuilder[]` 반환.
34
+ - `relations(fn)` — relation factory `r` 를 받아 FK/역참조 관계 레코드 반환. Table 은 `foreignKey`/`foreignKeyTarget`/`relationKey`/`relationKeyTarget` 모두 사용 가능.
35
+ - `$inferSelect` — SELECT 결과 타입(컬럼 + include 가능한 관계는 optional). `queryable` 콜백 인자의 원천.
36
+ - `$inferInsert` / `$inferUpdate` — `insert`/`update` 입력 타입. INSERT 는 autoIncrement·nullable·default column 이 optional, UPDATE 는 전부 optional.
19
37
 
20
38
  ```typescript
21
- const Post = Table("Post")
39
+ const User = Table("User")
22
40
  .database("mydb")
23
41
  .columns((c) => ({
24
42
  id: c.bigint().autoIncrement(),
25
- authorId: c.bigint(),
26
- title: c.varchar(200),
27
- content: c.text().nullable(),
43
+ name: c.varchar(100),
44
+ email: c.varchar(200).nullable(),
28
45
  }))
29
46
  .primaryKey("id")
30
- .indexes((i) => [i.index("authorId")])
31
- .relations((r) => ({ author: r.foreignKey(["authorId"], () => User) }));
47
+ .indexes((i) => [i.index("email").unique()]);
32
48
  ```
33
49
 
34
50
  ## View / ViewBuilder
35
51
 
36
- `View(name)` → `ViewBuilder`. `query()` 로 SELECT Queryable 을 데이터 소스로 지정. View 는 컬럼 추론을 query 결과 타입(`TData`)에서 얻고, FK 는 등록하지 않으므로 관계는 relationKey 계열만 쓴다.
52
+ ```typescript
53
+ function View(name: string): ViewBuilder;
54
+
55
+ class ViewBuilder<TDbContext, TData, TRelations> {
56
+ readonly meta: { name; description?; database?; schema?; viewFn?; relations? };
57
+ readonly $inferSelect; // TData
58
+
59
+ description(desc): ViewBuilder;
60
+ database(db): ViewBuilder;
61
+ schema(schema): ViewBuilder;
62
+ query(viewFn: (db) => Queryable<TViewData, any>): ViewBuilder;
63
+ relations(fn: (r) => T): ViewBuilder;
64
+ }
65
+ ```
37
66
 
38
- - `View(name: string): ViewBuilder` — 빈 View 빌더.
39
- - `description(desc)` / `database(db)` / `schema(schema)` Table 동일 의미.
40
- - `query<TViewData, TDb>(viewFn: (db: TDb) => Queryable<TViewData, any>)` — View 본문 SELECT. `db` DbContext, 반환 Queryable 컬럼 구조가 View 컬럼이 됨. `queryable()` 등록 이 viewFn 을 실행해 컬럼 alias 를 재배치함.
41
- - `relations(fn: (r) => ...)` — 관계 정의. View 의 `r` 은 relationKey/relationKeyTarget 만 제공(DB FK 미생성). 반환 타입에 관계가 합쳐짐(`TData & InferDeepRelations`).
67
+ - `View(name)` — 빈 `ViewBuilder` 생성.
68
+ - `query(viewFn)` 뷰 본문 SELECT 를 `db` 받는 함수로 정의. 반환 `Queryable` 의 select 결과가 뷰 데이터 타입이 됨. `queryable(this, View)` 등록 함수가 평가되어 column 이 구성된다.
69
+ - `relations(fn)` — 뷰는 `relationKey`/`relationKeyTarget` 사용 가능(DB FK 미생성). `foreignKey` 타입상 불가.
70
+ - `description`/`database`/`schema` — Table 동일 의미.
42
71
 
43
72
  ```typescript
44
73
  const ActiveUsers = View("ActiveUsers")
45
74
  .database("mydb")
46
- .query((db: MyDb) =>
47
- db.user()
48
- .where((u) => [expr.eq(u.isActive, true)])
49
- .select((u) => ({ id: u.id, name: u.name })),
75
+ .query((db: MainDb) =>
76
+ db.user().where((u) => [expr.eq(u.status, "active")]).select((u) => ({ id: u.id, name: u.name })),
50
77
  );
51
78
  ```
52
79
 
53
80
  ## Procedure / ProcedureBuilder
54
81
 
55
- `Procedure(name)` → `ProcedureBuilder`. 파라미터·반환 컬럼·본문 SQL 을 정의. `DbContext.executable()` 로 등록해 `Executable` 로 실행한다.
82
+ ```typescript
83
+ function Procedure(name: string): ProcedureBuilder<never, never>;
84
+
85
+ class ProcedureBuilder<TParams, TReturns> {
86
+ readonly meta: { name; description?; database?; schema?; params?; returns?; query? };
87
+ readonly $params; readonly $returns;
88
+
89
+ description(desc): ProcedureBuilder;
90
+ database(db): ProcedureBuilder;
91
+ schema(schema): ProcedureBuilder;
92
+ params(fn: (c) => T): ProcedureBuilder;
93
+ returns(fn: (c) => T): ProcedureBuilder;
94
+ body(sql: string): ProcedureBuilder;
95
+ }
96
+ ```
56
97
 
57
- - `Procedure(name: string): ProcedureBuilder<never, never>` — 빈 프로시저 빌더.
58
- - `description(desc)` / `database(db)` / `schema(schema)` Table 동일 의미.
59
- - `params(fn: (c) => Record<string, ColumnBuilder>)` — 입력 파라미터. `c` 컬럼 factory. dialect별 호출 구문 차이(MSSQL `@param`, MySQL/PG `param`) 주의.
60
- - `returns(fn: (c) => ...)` — 반환 결과 컬럼. 결과 타입 추론에 사용.
61
- - `body(sql: string)` — 본문 SQL(메타 키는 `query`). dialect별 구문 차이(MSSQL `@param`·`[User]`, PG `RETURN QUERY`) 주의.
98
+ - `Procedure(name)` — 빈 빌더 생성.
99
+ - `params(fn)` 입력 파라미터를 column factory 로 정의. `Executable.execute(params)` 입력 타입이 됨.
100
+ - `returns(fn)` — 결과 column 정의. `execute` 결과 타입이 됨.
101
+ - `body(sql)` — 프로시저 본문 SQL. DBMS별 구문 차이 주의(MySQL: `userId`, MSSQL: `@userId`, PostgreSQL: `RETURN QUERY` 필요).
102
+ - `description`/`database`/`schema` — Table 동일.
62
103
 
63
104
  ```typescript
64
105
  const GetUserById = Procedure("GetUserById")
@@ -68,53 +109,52 @@ const GetUserById = Procedure("GetUserById")
68
109
  .body("SELECT id, name FROM User WHERE id = userId");
69
110
  ```
70
111
 
71
- ## ColumnBuilder / createColumnFactory
112
+ ## column factory / ColumnBuilder
72
113
 
73
- `createColumnFactory()` 타입별 컬럼 생성 메서드를 반환하고,메서드의 결과 `ColumnBuilder` 수식자를 체이닝한다.
114
+ `columns`/`params`/`returns` 콜백 인자 `c` column factory.메서드는 `ColumnBuilder` 반환하고 `.autoIncrement()`/`.nullable()`/`.default()`/`.description()` 로 속성을 더한다(모두 immutable).
74
115
 
75
- 타입 메서드(전부 인자 없음, decimal/varchar/char 제외):
116
+ 타입 메서드:
76
117
 
77
- - `int()` — INT(4B, ±2^31). 일반 정수.
78
- - `bigint()` — BIGINT(8B, ±2^63). ID·대용량 정수.
79
- - `float()` — FLOAT(4B 단정밀도). 근사 실수.
80
- - `double()` — DOUBLE(8B 배정밀도). 근사 실수 고정밀.
81
- - `decimal(precision, scale?)` — DECIMAL 고정소수점. `precision`=전체 자릿수, `scale`=소수 자릿수. 금액 등 정확값.
118
+ - `int()` — INT(4바이트 정수).
119
+ - `bigint()` — BIGINT(8바이트 정수). autoIncrement PK 에 주로 사용.
120
+ - `float()` — FLOAT(단정밀도 실수).
121
+ - `double()` — DOUBLE(배정밀도 실수).
122
+ - `decimal(precision, scale?)` — 고정 소수점. `precision`=전체 자릿수, `scale`=소수 자릿수(선택). 금액 등 정밀도 필요 시.
82
123
  - `varchar(length)` — 가변 길이 문자열. `length`=최대 길이.
83
- - `char(length)` — 고정 길이 문자열. `length`=고정 길이. 코드값 등.
84
- - `text()` — 대용량 텍스트(TEXT/LONGTEXT).
85
- - `binary()` — 바이너리(MySQL LONGBLOB / MSSQL VARBINARY(MAX) / PG BYTEA). 값 타입 `Bytes`.
86
- - `boolean()` — 불리언(MySQL TINYINT(1) / MSSQL BIT / PG BOOLEAN).
87
- - `datetime()` — 날짜+시간(`DateTime`).
88
- - `date()` — 날짜만(`DateOnly`).
89
- - `time()` — 시간만(`Time`).
90
- - `uuid()` — UUID(MySQL BINARY(16) / MSSQL UNIQUEIDENTIFIER / PG UUID). 값 타입 `Uuid`.
91
-
92
- 수식자(`ColumnBuilder` 메서드):
93
-
94
- - `autoIncrement()` — 자동 증가. INSERT 추론에서 optional 처리, CUD output aiColName 으로 식별. PK 자동키에.
95
- - `nullable()` — NULL 허용. 값 타입에 `undefined` 추가, INSERT 추론에서 optional. "값 없음" 가능 컬럼에.
96
- - `default(value)` — DDL 기본값. INSERT 추론에서 optional 처리. `"CURRENT_TIMESTAMP"` 같은 문자열도 허용.
97
- - `description(desc)` — 컬럼 코멘트.
124
+ - `char(length)` — 고정 길이 문자열.
125
+ - `text()` — 대용량 텍스트.
126
+ - `binary()` — 바이너리(MySQL LONGBLOB / MSSQL VARBINARY(MAX) / PostgreSQL BYTEA). 값 타입 `Bytes`.
127
+ - `boolean()` — 불리언(MySQL TINYINT(1) / MSSQL BIT / PostgreSQL BOOLEAN).
128
+ - `datetime()` — 날짜+시간. 값 타입 `DateTime`.
129
+ - `date()` — 날짜만. 값 타입 `DateOnly`.
130
+ - `time()` — 시간만. 값 타입 `Time`.
131
+ - `uuid()` — UUID(MySQL BINARY(16) / MSSQL UNIQUEIDENTIFIER / PostgreSQL UUID). 값 타입 `Uuid`.
132
+
133
+ `ColumnBuilder` 속성 메서드:
134
+
135
+ - `autoIncrement()` — INSERT 시 자동 증가. INSERT 타입에서 optional 처리. PK 자동 증가 column 에.
136
+ - `nullable()` — NULL 허용. 값 타입에 `undefined` 추가, INSERT optional. 도메인상 값이 없을 수 있을 때만.
137
+ - `default(value)` — INSERT 시 미지정이면 사용할 기본값. INSERT optional 처리. 사용자가 명시 지시한 경우에만.
138
+ - `description(desc)` — column 설명(DDL comment).
98
139
 
99
140
  ```typescript
100
141
  .columns((c) => ({
101
142
  id: c.bigint().autoIncrement(),
143
+ price: c.decimal(10, 2),
102
144
  email: c.varchar(200).nullable(),
103
145
  status: c.varchar(20).default("active"),
104
146
  }))
105
147
  ```
106
148
 
107
- 추론 유틸 타입: `ColumnBuilderRecord`(컬럼 레코드), `InferColumns`(값 타입), `InferColumnExprs`(ExprInput 타입), `InferInsertColumns`/`InferUpdateColumns`/`RequiredInsertKeys`/`OptionalInsertKeys`(INSERT/UPDATE 추론), `DataToColumnBuilderRecord`(데이터→컬럼 변환, `insertInto` 대상 검증용).
108
-
109
- ## IndexBuilder / createIndexFactory
149
+ ## index factory / IndexBuilder
110
150
 
111
- `createIndexFactory<TColumnKey>()` `{ index(...columns) }`. 반환 `IndexBuilder` 수식자 체이닝.
151
+ `indexes` 콜백 인자 `i` 의 `index(...columns)` 시작해 fluent 옵션을 더한다.
112
152
 
113
- - `index(...columns): IndexBuilder` — 인덱스 컬럼 지정(복합 가능).
114
- - `name(name)` — 인덱스 이름. 미지정 시 DDL 생성기가 자동 작명.
115
- - `unique()` — 유니크 인덱스. 중복 방지 제약.
116
- - `orderBy(...orderBy)` — 컬럼별 정렬 방향. 각 값 `"ASC"|"DESC"`, 컬럼 수와 개수 일치 필요. 정렬 최적화 인덱스에.
117
- - `description(description)` — 인덱스 코멘트.
153
+ - `i.index(...columns)` — index 대상 column 이름(들). 여러 개면 복합 index.
154
+ - `.name(name)` — index 이름 지정. 미지정 시 자동 생성.
155
+ - `.unique()` — 유니크 index.
156
+ - `.orderBy(...orderBy)` — column별 정렬 방향 배열("ASC"|"DESC"). column 수와 길이 일치해야 함.
157
+ - `.description(description)` — index 설명(DDL comment).
118
158
 
119
159
  ```typescript
120
160
  .indexes((i) => [
@@ -123,25 +163,42 @@ const GetUserById = Procedure("GetUserById")
123
163
  ])
124
164
  ```
125
165
 
126
- ## 관계 빌더 (foreignKey / foreignKeyTarget / relationKey / relationKeyTarget)
166
+ ## relation factory
127
167
 
128
- `createRelationFactory(ownerFn)` owner 종류에 따라 다른 factory 를 반환: Table 이면 FK 4종 전부, View 이면 relationKey 계열 2종만. `relations()` 콜백의 `r` 가 이 factory. 대상 테이블은 순환 참조 방지를 위해 `() => Table` 지연 함수로 전달.
168
+ `relations` 콜백 인자 `r`. Table 4종 모두, View `relationKey`/`relationKeyTarget` 만. 대상 빌더는 순환 참조 방지를 위해 모두 `() => Target` 지연 함수로 넘긴다. `description`/`single` 은 메서드 체이닝이 아니라 마지막 `opts` 인자로 전달(체이닝은 TS7022 유발로 제거됨).
129
169
 
130
- - `r.foreignKey(columns, () => Target, opts?)` — N:1 FK 관계(DB FK 제약 생성). `columns`=현재 테이블의 FK 컬럼들. `opts.description`=관계 설명. include 단일 객체로 로드. → `ForeignKeyBuilder`.
131
- - `r.foreignKeyTarget(() => Target, relationName, opts?)` — 1:N 역참조(DB FK 미생성, 상대 FK 를 역으로 봄). `relationName`=상대 테이블의 FK 관계 이름. `opts.single:true`=1:1 단일 객체로 로드(미지정/false 배열). `opts.description`=설명. → `ForeignKeyTargetBuilder`.
132
- - `r.relationKey(columns, () => Target, opts?)` — N:1 논리 관계(DB FK 미생성). foreignKey 와 동일하나 제약 없음. View 에서도 사용 가능. → `RelationKeyBuilder`.
133
- - `r.relationKeyTarget(() => Target, relationName, opts?)` — 1:N 논리 역참조(DB FK 미생성). foreignKeyTarget 과 동일. `opts.single`/`opts.description` 동일. View 에서도 사용 가능. → `RelationKeyTargetBuilder`.
170
+ - `r.foreignKey(columns, () => Target, opts?)` — N:1 FK 관계(DB FK 제약 **생성**). `columns`=현재 테이블 FK column 배열, 대상은 테이블의 PK 매칭. `opts.description?`. → `ForeignKeyBuilder`.
171
+ - `r.foreignKeyTarget(() => Target, relationName, opts?)` — FK 역참조(1:N, DB FK 생성 측의 역방향). `relationName`=대상 테이블에서 이쪽을 가리키는 FK 관계 이름. `opts.single: true` 면 1:1 단일 객체, 아니면 배열. `opts.description?`. → `ForeignKeyTargetBuilder`.
172
+ - `r.relationKey(columns, () => Target, opts?)` — N:1 논리 관계(DB FK **미생성**). FK 와 동일하나 제약 없음. View 에서도 사용. → `RelationKeyBuilder`.
173
+ - `r.relationKeyTarget(() => Target, relationName, opts?)` — 1:N/1:1 논리 역참조(DB FK 미생성). `opts.single`/`opts.description` 동일. → `RelationKeyTargetBuilder`.
174
+
175
+ 각 `opts`:
176
+
177
+ - `description?: string` — 관계 설명.
178
+ - `single?: boolean` (target 계열만) — true=결과를 단일 객체(1:1), false/미지정=배열(1:N). `include()`/`$inferSelect` 의 해당 키 타입이 단일/배열로 갈린다.
134
179
 
135
180
  ```typescript
136
- // 자식 테이블: N:1
137
- .relations((r) => ({ author: r.foreignKey(["authorId"], () => User, { description: "작성자" }) }))
138
- // 부모 테이블: 1:N 역참조 + 1:1 단일
139
- .relations((r) => ({
140
- posts: r.foreignKeyTarget(() => Post, "author"),
141
- profile: r.foreignKeyTarget(() => Profile, "user", { single: true }),
142
- }))
181
+ const Post = Table("Post")
182
+ .columns((c) => ({ id: c.bigint().autoIncrement(), authorId: c.bigint() }))
183
+ .primaryKey("id")
184
+ .relations((r) => ({ author: r.foreignKey(["authorId"], () => User, { description: "작성자" }) }));
185
+
186
+ const User = Table("User")
187
+ .columns((c) => ({ id: c.bigint().autoIncrement(), name: c.varchar(100) }))
188
+ .primaryKey("id")
189
+ .relations((r) => ({
190
+ posts: r.foreignKeyTarget(() => Post, "author"),
191
+ profile: r.foreignKeyTarget(() => Profile, "user", { single: true }),
192
+ }));
143
193
  ```
144
194
 
145
- 빌더 클래스(메타만 보관, 메서드 없음): `ForeignKeyBuilder`/`ForeignKeyTargetBuilder`/`RelationKeyBuilder`/`RelationKeyTargetBuilder`. 추론 타입: `RelationBuilderRecord`, `InferDeepRelations`(관계를 optional 로, 같은 테이블 재방문 순환 절단), `ExtractRelationTarget`/`ExtractRelationTargetResult`.
195
+ 주의: FK column 개수와 대상 테이블 PK 개수가 다르면 `include()`/join "FK/PK column count mismatch" throw. PK 복합이면 FK column 도 같은 순서·개수로.
196
+
197
+ ## 빌더/추론 타입 (직접 참조 드묾)
146
198
 
147
- > 주의: description/single 은 반드시 factory `opts` 인자로 전달한다. `.description()`·`.single()` 메서드 체이닝은 TS 순환 참조(TS7022) 때문에 제거되어 존재하지 않는다.
199
+ - `ColumnBuilderRecord` `Record<string, ColumnBuilder<...>>`. `columns`/`params`/`returns` 반환 타입.
200
+ - `RelationBuilderRecord` — 4종 relation 빌더의 union 레코드. `relations` 반환 타입.
201
+ - `InferColumns<T>` / `InferInsertColumns<T>` / `InferUpdateColumns<T>` / `InferColumnExprs<T>` — column 레코드에서 각각 값 타입 / INSERT 입력 / UPDATE 입력 / `ExprInput` 입력 타입을 추론. `$inferColumns` 등 내부 사용.
202
+ - `RequiredInsertKeys<T>` / `OptionalInsertKeys<T>` — INSERT 시 필수/선택 column key 분리(autoIncrement·nullable·default 가 선택).
203
+ - `DataToColumnBuilderRecord<TData>` — 데이터 레코드 → column 빌더 레코드 역변환. `insertInto` 의 대상 테이블 제약에 사용.
204
+ - `InferDeepRelations<T>` / `ExtractRelationTarget<T>` / `ExtractRelationTargetResult<T>` — 관계 정의에서 심층 결과 타입을 optional 로 추론(같은 테이블 재방문 시 순환 차단). `$inferSelect` 의 관계 부분.
@@ -1,62 +1,67 @@
1
- # @simplysm/orm-common — 타입·실행 엔진 내부
1
+ # @simplysm/orm-common — 하위 타입 / QueryDef AST / QueryBuilder / 결과 파싱
2
2
 
3
- executor·어댑터를 구현하거나 QueryDef/Expr AST·결과 메타를 직접 다루고, QueryDef dialect SQL 렌더링하거나 raw 결과를 TS 객체로 환원할 때 참조하는 묶음. 일반 쿼리 작성에서는 expr/Queryable 가 이 타입들을 가려주므로 직접 쓸 일이 적다.
3
+ executor·QueryBuilder 직접 구현하거나, `QueryDef`/`Expr` AST·column 타입을 다루거나, 원시 결과를 TS 객체로 환원할 때 참조하는 묶음. 일반 쿼리 작성에서는 expr/Queryable 가 이 타입들을 가려주므로 직접 쓸 일이 적다.
4
4
 
5
- ## Column 타입
5
+ ## column 타입
6
6
 
7
- - `type DataType` — SQL 타입 union. `{type:"int"}`/`{type:"bigint"}`/`{type:"float"}`/`{type:"double"}`/`{type:"decimal";precision;scale?}`/`{type:"varchar";length}`/`{type:"char";length}`/`{type:"text"}`/`{type:"binary"}`/`{type:"boolean"}`/`{type:"datetime"}`/`{type:"date"}`/`{type:"time"}`/`{type:"uuid"}`. cast/DDL 에서 사용.
8
- - `type ColumnPrimitiveMap` — TS 타입 이름→실제 타입(`string`/`number`/`boolean`/`DateTime`/`DateOnly`/`Time`/`Uuid`/`Bytes`).
9
- - `type ColumnPrimitiveStr` — 맵의 키(`"string"` ). ExprUnit.dataType 의 타입.
10
- - `type ColumnPrimitive` — 저장 가능한 모든 원시값 `| undefined`(undefined=NULL).
11
- - `const dataTypeStrToColumnPrimitiveStr` — SQL 타입명→TS 타입명 매핑 객체(`int→"number"`, `datetime→"DateTime"` 등).
12
- - `type InferColumnPrimitiveFromDataType<T>` — DataType 에서 TS 타입 추론(cast 결과 타입).
13
- - `function inferColumnPrimitiveStr(value): ColumnPrimitiveStr` — 런타임 값에서 타입명 추론. NULL 이면 throw(추론 불가).
14
- - `interface ColumnMeta` — 컬럼 메타: `type`(ColumnPrimitiveStr), `dataType`, `autoIncrement?`, `nullable?`, `default?`, `description?`.
7
+ - `DataType` — SQL 데이터 타입 union: `{type:"int"}`, `{type:"bigint"}`, `{type:"float"}`, `{type:"double"}`, `{type:"decimal", precision, scale?}`, `{type:"varchar", length}`, `{type:"char", length}`, `{type:"text"}`, `{type:"binary"}`, `{type:"boolean"}`, `{type:"datetime"}`, `{type:"date"}`, `{type:"time"}`, `{type:"uuid"}`. `expr.cast` 의 targetType·DDL column 정의에 사용.
8
+ - `ColumnPrimitiveMap` — TS 타입 이름 → 실제 타입 매핑: `string→string`, `number→number`, `boolean→boolean`, `DateTime→DateTime`, `DateOnly→DateOnly`, `Time→Time`, `Uuid→Uuid`, `Bytes→Bytes`.
9
+ - `ColumnPrimitiveStr` — `keyof ColumnPrimitiveMap`(타입 이름 문자열). `ExprUnit.dataType`·`val`dataType.
10
+ - `ColumnPrimitive` — 저장 가능한 모든 원시 union + `undefined`(=NULL).
11
+ - `dataTypeStrToColumnPrimitiveStr` (const) — SQL DataType 이름 → TS 타입 이름 매핑 객체(`int→"number"`, `varchar→"string"`, `datetime→"DateTime"` 등). `cast` 가 결과 타입 결정에 사용.
12
+ - `InferColumnPrimitiveFromDataType<T>` — `DataType` 에서 TS 타입 추론.
13
+ - `inferColumnPrimitiveStr(value)` — 런타임 값에서 `ColumnPrimitiveStr` 추론. NULL 이면 추론 불가 throw.
14
+ - `ColumnMeta` — column 메타: `{ type: ColumnPrimitiveStr; dataType: DataType; autoIncrement?; nullable?; default?; description? }`. `ColumnBuilder.meta` 타입.
15
15
 
16
- ## Database 타입
16
+ ## db 타입
17
17
 
18
- - `type Dialect = "mysql" | "mssql" | "postgresql"` 지원 DBMS.
19
- - `const dialects: Dialect[]` dialect 목록(테스트 `it.each` 등에).
20
- - `type IsolationLevel` — `"READ_UNCOMMITTED"|"READ_COMMITTED"|"REPEATABLE_READ"|"SERIALIZABLE"`. 트랜잭션 격리(엄격도 ).
21
- - `type DataRecord` — 재귀 결과 행 타입(`{ [k]: ColumnPrimitive | DataRecord | DataRecord[] }`). include 중첩 표현.
22
- - `interface QueryBuildResult` — QueryBuilder.build() 반환: `sql`(렌더된 SQL), `resultSetIndex?`(가져올 결과셋 인덱스, 기본 0), `resultSetStride?`(N번째마다 추출, MySQL 배치 INSERT 의 SELECT 만 모을 때).
23
- - `interface ResultMeta` — 결과 변환 메타: `columns`(컬럼키→ColumnPrimitiveStr), `joins`(조인키→`{isSingle}`). `parseQueryResult` 입력.
18
+ - `Dialect` `"mysql" | "mssql" | "postgresql"`. 지원 DBMS(MySQL 8.0.14+, MSSQL 2012+, PostgreSQL 9.0+).
19
+ - `dialects` (const) `Dialect[]`(전체 목록). dialect 테스트 매트릭스에 사용.
20
+ - `DataRecord` — 재귀 결과 레코드 타입: `{ [key: string]: ColumnPrimitive | DataRecord | DataRecord[] }`. include 중첩(단일 객체/배열)을 표현.
21
+ - `QueryBuildResult` — `build()` 반환: `{ sql: string; resultSetIndex?: number; resultSetStride?: number }`. `resultSetIndex`=결과를 가져올 셋 index(기본 0, 예 MySQL INSERT+OUTPUT 는 1). `resultSetStride`=다중 결과에서 N번째마다 추출(MySQL 배치 INSERT 의 `INSERT;SELECT;...` 에서 SELECT 만 모을 때).
22
+ - `IsolationLevel` — 트랜잭션 격리 수준(자세히는 [db-context.md](./db-context.md)).
23
+ - `DbContextExecutor` / `ResultMeta` / `Migration` executor·결과 메타·마이그레이션 정의. [db-context.md](./db-context.md) 참조.
24
24
 
25
- ## Executor / Migration 인터페이스
25
+ ## QueryDef AST (query-def.ts)
26
26
 
27
- - `interface DbContextExecutor` DB 연결·실행 계약(orm-node 등이 구현). `connect()`/`close()`/`beginTransaction(isolationLevel?)`/`commitTransaction()`/`rollbackTransaction()`/`executeDefs<T>(defs, resultMetas?): Promise<T[][]>`. 새 DB 어댑터 작성 시 이 면을 채움.
28
- - `interface Migration` — `{ name; up(db) }` ([db-context.md](./db-context.md) 참조).
27
+ `executeDefs`/`build` 다루는 쿼리 정의 AST. `Queryable.getXQueryDef()`·`DbContext.getXQueryDef()` 생성.
29
28
 
30
- ## QueryDef AST
29
+ - `QueryDefObjectName` — `{ database?; schema?; name }`. DB 객체 네임스페이스(MySQL `database.name`, MSSQL `database.schema.name`, PostgreSQL `schema.name`).
30
+ - DML: `SelectQueryDef`(from/as/select/distinct/top/lock/where/joins/orderBy/limit/groupBy/having/with), `SelectQueryDefJoin`(SelectQueryDef + `isSingle?`), `InsertQueryDef`(records/overrideIdentity?/output?), `InsertIfNotExistsQueryDef`, `InsertIntoQueryDef`, `UpdateQueryDef`, `DeleteQueryDef`, `UpsertQueryDef`.
31
+ - `CudOutputDef` — CUD OUTPUT 절: `{ columns: string[]; pkColNames: string[]; aiColName? }`. 삽입/갱신/삭제 행 회수 정의.
32
+ - DDL: `ClearSchemaQueryDef`, `CreateTableQueryDef`/`DropTableQueryDef`/`RenameTableQueryDef`/`TruncateQueryDef`, `AddColumnQueryDef`/`DropColumnQueryDef`/`ModifyColumnQueryDef`/`RenameColumnQueryDef`, `AddPrimaryKeyQueryDef`/`DropPrimaryKeyQueryDef`/`AddForeignKeyQueryDef`/`DropForeignKeyQueryDef`/`AddIndexQueryDef`/`DropIndexQueryDef`, `CreateViewQueryDef`/`DropViewQueryDef`/`CreateProcQueryDef`/`DropProcQueryDef`/`ExecProcQueryDef`.
33
+ - Utils/Meta: `SwitchFkQueryDef`(`{ table; enabled }`, DDL 아님 — 트랜잭션 가능), `SchemaExistsQueryDef`.
34
+ - `DDL_TYPES` (const) — DDL QueryDef type 문자열 배열. 트랜잭션 중 DDL 차단 판정(`switchFk` 는 제외)에 사용.
35
+ - `DdlType` — `(typeof DDL_TYPES)[number]`.
36
+ - `QueryDef` — 전체 union(DML + DDL + SwitchFk + SchemaExists). `executeDefs(defs: QueryDef[])` 의 원소 타입.
31
37
 
32
- `type QueryDef` DML+DDL+Utils+Meta 의 union. executor 의 `executeDefs` 가 받고 QueryBuilder 가 SQL 로 변환.
38
+ ## Expr AST (expr.ts)
33
39
 
34
- - 공통: `interface QueryDefObjectName` `{ database?; schema?; name }`(dialect별 네임스페이스: MySQL `db.name`, MSSQL `db.schema.name`, PG `schema.name`). `interface CudOutputDef` — `{ columns; pkColNames; aiColName? }`(CUD OUTPUT).
35
- - DML: `SelectQueryDef`(from/select/where/joins/orderBy/limit/groupBy/having/with/distinct/top/lock), `SelectQueryDefJoin`(+`isSingle?`), `InsertQueryDef`(records/overrideIdentity?/output?), `InsertIfNotExistsQueryDef`(record/existsSelectQuery), `InsertIntoQueryDef`(recordsSelectQuery), `UpdateQueryDef`(record/where/joins), `DeleteQueryDef`, `UpsertQueryDef`(existsSelectQuery/insertRecord/updateRecord).
36
- - DDL: `ClearSchemaQueryDef`, `CreateTableQueryDef`/`DropTableQueryDef`/`RenameTableQueryDef`/`TruncateQueryDef`, `AddColumnQueryDef`/`DropColumnQueryDef`/`ModifyColumnQueryDef`/`RenameColumnQueryDef`, `AddPrimaryKeyQueryDef`/`DropPrimaryKeyQueryDef`/`AddForeignKeyQueryDef`/`DropForeignKeyQueryDef`/`AddIndexQueryDef`/`DropIndexQueryDef`, `CreateViewQueryDef`/`DropViewQueryDef`/`CreateProcQueryDef`/`DropProcQueryDef`/`ExecProcQueryDef`.
37
- - Utils/Meta: `SwitchFkQueryDef`(`table`/`enabled`), `SchemaExistsQueryDef`.
38
- - `const DDL_TYPES` — DDL QueryDef 의 `type` 문자열 배열(`switchFk` 제외 — 트랜잭션 내 허용). `transact` 중 DDL 차단 검사·검증에. `type DdlType` — 그 union.
40
+ `ExprUnit.expr`/`WhereExprUnit.expr` 담는 JSON AST. QueryBuilder ExprRenderer SQL 변환.
39
41
 
40
- ## Expr AST
42
+ - `Expr` — 전체 표현식 union(값 `ExprColumn`/`ExprValue`/`ExprRaw`, 문자열/숫자/날짜/조건/집계/기타/window `ExprWindow`/시스템 `ExprSubquery`). select/orderBy 등.
43
+ - `WhereExpr` — WHERE 전용 union(비교 `ExprEq`/`ExprGt`/.../`ExprIn`/`ExprInQuery`/`ExprExists` + 논리 `ExprNot`/`ExprAnd`/`ExprOr`). where/having.
44
+ - 개별 인터페이스(`ExprEq`, `ExprConcat`, `ExprCount`, `ExprCast`, `ExprWindow` 등) — 각 `expr.*` 함수가 만드는 노드. `type` discriminant + 피연산자 필드.
45
+ - `DateUnit` — `"year"|"month"|"day"|"hour"|"minute"|"second"`. dateDiff/dateAdd.
46
+ - `WinFn` — window 함수 노드 union(`WinFnRowNumber`/`WinFnRank`/`WinFnLag`/`WinFnSum`/...). `WinSpec` — `{ partitionBy?: Expr[]; orderBy?: [Expr,("ASC"|"DESC")?][] }`(OVER 절).
41
47
 
42
- - `type Expr` 값/문자열/숫자/날짜/조건/집계/window 표현식의 전체 union.
43
- - `type WhereExpr` — 비교·논리·NULL·LIKE·IN·EXISTS 등 WHERE 전용 union.
44
- - `type DateUnit = "year"|"month"|"day"|"hour"|"minute"|"second"` — dateDiff/dateAdd 단위.
45
- - `interface WinSpec` — `{ partitionBy?: Expr[]; orderBy?: [Expr, ("ASC"|"DESC")?][] }`(window OVER 절 AST).
46
- - 노드별 인터페이스: `ExprColumn`(`path`), `ExprValue`(`value`), `ExprRaw`(`sql`/`params`), 비교 `ExprEq`/`ExprGt`/`ExprLt`/`ExprGte`/`ExprLte`/`ExprBetween`/`ExprIsNull`/`ExprLike`/`ExprRegexp`/`ExprIn`/`ExprInQuery`/`ExprExists`, 논리 `ExprNot`/`ExprAnd`/`ExprOr`, 문자열·숫자·날짜·조건·집계·기타(`ExprConcat`...`ExprSubquery`), window `ExprWindow` 등. 각 노드는 `type` 디스크리미네이터 + 피연산자 필드를 가진 평범한 데이터 객체.
48
+ ## QueryBuilder (QueryDef SQL)
47
49
 
48
- ## dialect QueryBuilder (SQL 렌더러)
50
+ 직접 SQL 문자열이 필요하거나 dialect 동작을 검증할 때.
49
51
 
50
- QueryDef SQL 문자열 변환. executor 구현체가 사용.
52
+ - `createQueryBuilder(dialect: Dialect): QueryBuilderBase` dialect 맞는 QueryBuilder 생성(`mysql`→Mysql, `mssql`→Mssql, `postgresql`→Postgresql).
53
+ - `QueryBuilderBase` (abstract) — `build(def: QueryDef): QueryBuildResult` 가 공개 진입점. `def.type` 으로 동적 dispatch. 나머지는 protected/abstract(dialect 구현).
54
+ - `MysqlQueryBuilder` / `MssqlQueryBuilder` / `PostgresqlQueryBuilder` — dialect별 구현. 보통 `createQueryBuilder` 로 얻음.
55
+ - `ExprRendererBase` (abstract) — Expr→SQL 렌더러. `render(expr): string`, `renderWhere(exprs): string` 공개. `wrap(name)`/`escapeString`/`escapeValue` 는 dialect abstract.
56
+ - `MysqlExprRenderer` / `MssqlExprRenderer` / `PostgresqlExprRenderer` — dialect별 렌더러.
51
57
 
52
- - `createQueryBuilder(dialect: Dialect): QueryBuilderBase` — dialect 에 맞는 빌더 인스턴스 생성(mysql→Mysql, mssql→Mssql, postgresql→Postgresql).
53
- - `abstract class QueryBuilderBase` — 렌더링 기반. `build(def): QueryBuildResult` 가 `def.type` 이름의 메서드로 동적 dispatch(미지원 타입이면 throw). 공통 절 렌더링(WHERE/ORDER BY/GROUP BY/HAVING/JOIN/FROM)은 구현, dialect 차이는 abstract(select/insert/... DML, 각 DDL, tableName/renderJoin). LATERAL/CROSS APPLY 필요 감지(`needsLateral`), 재귀 self 조인 감지(`isRecursiveSelfJoin`) 보조.
54
- - `class MysqlQueryBuilder` / `class MssqlQueryBuilder` / `class PostgresqlQueryBuilder` — `QueryBuilderBase` 의 dialect 구현체.
55
- - `abstract class ExprRendererBase` — `Expr`/`WhereExpr` 노드를 SQL 조각으로 렌더링하는 추상 기반. `class MysqlExprRenderer`/`class MssqlExprRenderer`/`class PostgresqlExprRenderer` 가 dialect 구현. QueryBuilder 내부에서 식별자 wrap·표현식 변환에 사용.
58
+ ```typescript
59
+ const sql = createQueryBuilder("mysql").build(queryable.getSelectQueryDef()).sql;
60
+ ```
56
61
 
57
- ## 결과 파싱 유틸
62
+ ## 결과 파싱
58
63
 
59
- executor 가 DB raw 결과를 TS 객체로 환원할 때 사용. 일반 사용자는 직접 호출하지 않음.
64
+ executor 가 원시 행을 받은 뒤 `ResultMeta` 로 TS 객체로 환원할 때.
60
65
 
61
- - `parseQueryResult<T>(rawResults, meta: ResultMeta): Promise<T[] | undefined>` — flat raw 배열을 `meta.columns`(키→타입)로 타입 변환하고 `meta.joins`(키→`{isSingle}`) 중첩 그룹핑. 입력이 비었거나 파싱 후 전부 빈 객체면 undefined. async 전용(100행마다 이벤트 루프 양보). `isSingle:true` 관계에 서로 다른 다건이 매칭되면 throw.
62
- - `pickResultSets<T>(rawResults: T[][], buildResult): T[]` — 다중 결과셋 추출. `resultSetIndex` 없으면 첫 셋, `resultSetStride` 없으면 해당 인덱스 셋, 있으면 인덱스부터 stride 간격으로 concat(MySQL 배치 INSERT 의 SELECT 결과만 모을 때).
66
+ - `parseQueryResult<T>(rawResults, meta): Promise<T[] | undefined>` — 원시 결과 배열 + `ResultMeta` 로 타입 변환·JOIN 중첩. JOIN 없으면 단순 파싱, 있으면 그룹키 기준 재귀 그룹핑(O(n) Map). 입력 비었거나 파싱 후 전부 빈 객체면 `undefined`. 100건마다 이벤트 루프 양보(async 전용). isSingle 관계에 서로 다른 결과가 섞이면 throw.
67
+ - `pickResultSets<T>(rawResults, buildResult): T[]` — 다중 결과셋에서 `QueryBuildResult` `resultSetIndex`/`resultSetStride` 에 따라 필요한 셋만 추출. index 없으면 첫 셋, stride 없으면 index 단일, stride 있으면 index 부터 stride 간격으로 concat(MySQL 배치 INSERT 의 SELECT 모을 때).
@@ -1,16 +1,16 @@
1
1
  # @simplysm/orm-node
2
2
 
3
- Node.js 환경에서 `@simplysm/orm-common` 의 `DbContext` 를 실제 DB(MySQL/MSSQL/PostgreSQL)에 연결·실행하는 ORM 런타임. 고수준 진입점(`createOrm`)과 저수준 연결(`createDbConn`/`DbConn`)을 함께 제공.
3
+ Node.js 환경에서 `@simplysm/orm-common` 의 `DbContext` 를 실제 DB(MySQL/MSSQL/PostgreSQL)에 연결·실행하는 ORM 런타임. 고수준 진입점(`createOrm`)과 저수준 연결(`createDbConn`/`DbConn`)을 함께 노출.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **createOrm / Orm / OrmOptions** — `DbContext` 서브클래스로 ORM 인스턴스를 만들고 트랜잭션 단위로 query 를 돌릴 때. (이 README 아래 "ORM 진입" 군)
8
- - **NodeDbContextExecutor** — `DbContext` 에 직접 주입할 executor 를 손수 만들 때(보통 `createOrm` 이 내부에서 처리하므로 직접 쓸 일은 드묾). (이 README 아래 "ORM 진입" 군)
9
- - **createDbConn / DbConn / 설정 타입 / DB_CONN_\* / getDialectFromConfig** — ORM 없이 raw SQL·bulk insert·트랜잭션을 직접 다루거나 dialect 연결을 손수 제어할 때. 자세히: [db-conn.md](./db-conn.md)
7
+ - **createOrm / Orm / OrmOptions** — `DbContext` 서브클래스로 ORM 인스턴스를 만들고 `connect`/`connectWithoutTransaction` 으로 트랜잭션 경계를 잡아 query 를 돌릴 때. (아래 "ORM 진입" 군)
8
+ - **NodeDbContextExecutor** — `DbContext` 에 직접 주입할 executor 를 손수 만들 때(`createOrm` 이 내부에서 자동 생성하므로 직접 쓸 일은 드묾). (아래 "ORM 진입" 군 및 db-conn.md)
9
+ - **createDbConn / DbConn / DbConnConfig 계열 / getDialectFromConfig / DB_CONN_\* 상수** — ORM 없이 raw SQL·파라미터 쿼리·bulk insert·수동 트랜잭션을 직접 다루거나 dialect별 접속 설정을 작성할 때. 자세히: [db-conn.md](./db-conn.md)
10
10
 
11
11
  ## ORM 진입
12
12
 
13
- `DbContext` 서브클래스와 연결 설정을 받아 트랜잭션 경계를 관리하는 고수준 진입. query DSL 자체는 `@simplysm/orm-common` 의 `DbContext` 가 제공하고, 이 군은 그 컨텍스트를 실제 연결에 묶는 역할.
13
+ `DbContext` 서브클래스와 접속 설정을 받아 연결·트랜잭션 경계를 관리하는 고수준 진입. query DSL 자체는 `@simplysm/orm-common` 의 `DbContext` 가 제공하고, 이 군은 그 컨텍스트를 실제 DB 연결에 묶는 역할.
14
14
 
15
15
  ### createOrm
16
16
 
@@ -22,69 +22,69 @@ function createOrm<T extends DbContext>(
22
22
  ): Orm<T>
23
23
  ```
24
24
 
25
- `DbContext` 서브클래스를 받아 `Orm<T>` 를 반환. 인스턴스는 메서드 호출마다 새로 만들어지므로 `Orm` 객체는 재사용 가능.
25
+ `DbContext` 서브클래스를 받아 `Orm<T>` 를 반환. DB 인스턴스는 `connect`/`connectWithoutTransaction` 호출마다 새로 생성되므로 반환된 `Orm` 객체 자체는 재사용 가능.
26
26
 
27
- - DbClass: `DbContext` 를 상속한 생성자. `executor`(연결 실행자)와 `{ database, schema? }` 를 받는 시그니처 고정. query 진입점(`this.queryable(Entity)`)을 정의한 사용자 DB 클래스를 넘김.
28
- - config: `DbConnConfig`(dialect 별 분기 유니온, [db-conn.md](./db-conn.md) 참조). 접속 대상·인증 정보.
29
- - options?: `OrmOptions`. config 의 `database`/`schema` 를 덮어쓰는 우선 옵션. 같은 접속 정보로 DB·스키마만 바꿔 쓸 때.
27
+ - DbClass `DbContext` 를 상속한 생성자. `(executor, { database, schema? })` 시그니처 고정. query 진입점(`this.queryable(Entity)`)을 정의한 사용자 DB 클래스를 넘김. 어떤 엔티티 집합을 다룰지 결정하는 자리.
28
+ - config `DbConnConfig`(dialect별 분기 유니온, [db-conn.md](./db-conn.md) 참조). 접속 대상·인증 정보. DBMS 종류·호스트·계정이 여기서 정해짐.
29
+ - options? `OrmOptions`. config 의 `database`/`schema` 를 덮어쓰는 우선 옵션. 같은 접속 정보로 DB·스키마만 바꿔 쓸 때(다중 테넌트 등) 지정.
30
30
 
31
- database 해석: `options.database` → `config.database` 순으로 찾고, 둘 다 없거나 빈 문자열이면 `"database는 필수입니다"` throw. schema `options.schema` → `config.schema` 순.
31
+ database 해석: `options.database` → `config.database` 순으로 찾고, 둘 다 없거나 빈 문자열이면 `"database는 필수입니다"` throw. schema 해석도 `options.schema` → `config.schema` 순(없으면 `undefined` 유지).
32
32
 
33
33
  ```typescript
34
- class MyDb extends DbContext {
34
+ class TestDb extends DbContext {
35
35
  user = this.queryable(User);
36
36
  }
37
- const orm = createOrm(MyDb, { dialect: "mysql", host: "localhost", port: 3306, username: "root", password: "pw", database: "mydb" });
38
- await orm.connect(async (db) => db.user().execute()); // 트랜잭션 안
37
+ const orm = createOrm(TestDb, mysqlConfig, { database: "TestDb" });
38
+ await orm.connect(async (db) => {
39
+ await db.user().insert([{ id: 100, name: "orm-test" }]);
40
+ return db.user().execute();
41
+ }); // 트랜잭션 안에서 실행 후 자동 커밋
39
42
  ```
40
43
 
41
44
  ### Orm
42
45
 
43
46
  ```typescript
44
47
  interface Orm<T extends DbContext> {
45
- readonly DbClass; readonly config: DbConnConfig; readonly options?: OrmOptions;
48
+ readonly DbClass: new (executor: DbContextExecutor, opt: { database: string; schema?: string }) => T;
49
+ readonly config: DbConnConfig;
50
+ readonly options?: OrmOptions;
46
51
  connect<R>(callback: (conn: T) => Promise<R>, isolationLevel?: IsolationLevel): Promise<R>;
47
52
  connectWithoutTransaction<R>(callback: (conn: T) => Promise<R>): Promise<R>;
48
53
  }
49
54
  ```
50
55
 
51
- `createOrm` 반환 타입. 호출마다 DB 인스턴스를 새로 만들어 연결→콜백→정리.
56
+ `createOrm` 반환 타입. 메서드 호출마다 DB 인스턴스를 새로 만들어 연결→콜백→정리.
52
57
 
53
- - connect: 콜백을 **트랜잭션 안에서** 실행. 콜백 정상 종료 커밋, throw 롤백.
54
- - isolationLevel?: `"READ_UNCOMMITTED" | "READ_COMMITTED" | "REPEATABLE_READ" | "SERIALIZABLE"`(`@simplysm/orm-common`). 트랜잭션 격리 수준. 미지정 연결의 `defaultIsolationLevel`, 그것도 없으면 `READ_UNCOMMITTED`. 더티 리드를 막아야 하면 `READ_COMMITTED` 이상.
55
- - connectWithoutTransaction: 콜백을 **트랜잭션 없이** 실행. 콜백 내부에서 `db.transaction(...)` 으로 부분 트랜잭션을 직접 사용(examples 테스트 패턴).
56
- - callback: 연결된 DbContext 인스턴스(`T`)를 받아 query 수행하고 임의 `R` 반환. 반환값이 `connect`/`connectWithoutTransaction` 결과가 됨.
58
+ - DbClass / config / options `createOrm` 넘긴 값을 그대로 읽기 전용으로 보관. 같은 설정으로 재연결·진단할 때 참조.
59
+ - connect 콜백을 **트랜잭션 안에서** 실행. 콜백이 정상 종료하면 커밋, throw 하면 롤백 오류를 다시 throw. 여러 쓰기를 원자적으로 묶어야 때.
60
+ - isolationLevel? `"READ_UNCOMMITTED" | "READ_COMMITTED" | "REPEATABLE_READ" | "SERIALIZABLE"`(`@simplysm/orm-common`). 트랜잭션 격리 수준. 미지정 시 연결의 `defaultIsolationLevel`, 그것도 없으면 `READ_UNCOMMITTED`. 더티 리드를 막아야 하면 `READ_COMMITTED` 이상으로 올림.
61
+ - connectWithoutTransaction 콜백을 **트랜잭션 없이** 실행. 읽기 전용이거나, 콜백 내부에서 `db.transaction(...)` 으로 부분 트랜잭션을 직접 열어 일부 구간만 원자화할 때.
62
+ - callback — 연결된 DbContext 인스턴스(`T`)를 받아 query 를 수행하고 임의 값 `R` 을 반환. 그 반환값이 `connect`/`connectWithoutTransaction` 의 결과가 됨.
63
+
64
+ ```typescript
65
+ // 읽기는 트랜잭션 없이, 그 안에서 일부 쓰기만 부분 트랜잭션으로
66
+ await orm.connectWithoutTransaction(async (db) => {
67
+ await db.transaction(async () => {
68
+ await db.user().insert([{ id: 300, name: "partial-tx" }]);
69
+ });
70
+ return db.user().execute();
71
+ });
72
+ ```
57
73
 
58
74
  ### OrmOptions
59
75
 
60
76
  ```typescript
61
- interface OrmOptions { database?: string; schema?: string; }
77
+ interface OrmOptions {
78
+ database?: string;
79
+ schema?: string;
80
+ }
62
81
  ```
63
82
 
64
83
  `createOrm` 3번째 인자. config 보다 우선 적용.
65
84
 
66
- - database?: 사용할 DB 이름. config 의 `database` 대신 쓸 때. 다중 테넌트처럼 접속 정보는 같고 DB 만 다를 때.
67
- - schema?: 스키마 이름(예: MSSQL `dbo`, PostgreSQL `public`). MySQL 은 스키마 개념이 없어 보통 미지정.
85
+ - database? 사용할 DB 이름. config 의 `database` 대신 쓸 때. 접속 정보는 같고 DB 만 다른 다중 테넌트 상황에서 인스턴스별로 지정.
86
+ - schema? 스키마 이름(예: MSSQL `dbo`, PostgreSQL `public`). MySQL 은 스키마 개념이 없어 보통 미지정.
68
87
 
69
88
  ### NodeDbContextExecutor
70
89
 
71
- `DbContextExecutor`(`@simplysm/orm-common`) 의 Node 구현체. `createOrm` 이 내부에서 생성·주입하므로 직접 다룰 일은 드묾. `DbContext` 를 `createOrm` 없이 손수 조립할 때만 사용.
72
-
73
- ```typescript
74
- class NodeDbContextExecutor implements DbContextExecutor {
75
- constructor(config: DbConnConfig);
76
- connect(): Promise<void>; close(): Promise<void>;
77
- beginTransaction(isolationLevel?: IsolationLevel): Promise<void>;
78
- commitTransaction(): Promise<void>; rollbackTransaction(): Promise<void>;
79
- executeParametrized(query: string, params?: unknown[]): Promise<Record<string, unknown>[][]>;
80
- bulkInsert(tableName: string, columnMetas: Record<string, ColumnMeta>, records: DataRecord[]): Promise<void>;
81
- executeDefs<T>(defs: QueryDef[], resultMetas?: (ResultMeta | undefined)[]): Promise<T[][]>;
82
- }
83
- ```
84
-
85
- - constructor(config): `DbConnConfig` 로 dialect 를 결정(`getDialectFromConfig`). `connect()` 호출 전까지 실제 연결은 열지 않음.
86
- - connect/close: 내부적으로 `createDbConn` 으로 연결 생성 후 open/close. `connect` 미호출 상태에서 다른 메서드 호출 시 `DB_CONN_ERRORS.NOT_CONNECTED` 로 throw.
87
- - begin/commit/rollbackTransaction: 연결의 트랜잭션 제어를 그대로 위임. `isolationLevel?` 은 위 `Orm.connect` 와 동일 의미.
88
- - executeParametrized: 파라미터 바인딩 query 1건 실행. dialect 별 placeholder 차이는 [db-conn.md](./db-conn.md) 참조.
89
- - bulkInsert: 네이티브 bulk API 로 대량 삽입. tableName, 컬럼명→`ColumnMeta` 매핑, 레코드 배열을 넘김.
90
- - executeDefs: `QueryDef[]` 를 dialect 별 query builder 로 SQL 변환 후 실행하고 `resultMetas` 로 결과 파싱. `resultMetas` 가 전부 null 이면 결과가 불필요한 것으로 보고 모든 def 를 하나의 SQL 로 합쳐 1회 실행, `defs.length` 만큼 빈 배열을 반환(인터페이스 계약 유지). 그 외엔 def 별 개별 실행.
90
+ `@simplysm/orm-common` 의 `DbContextExecutor` 를 Node 환경에서 구현한 클래스. `createOrm` 이 내부에서 생성·주입하므로 직접 다룰 일은 드묾. `DbContext` 를 `createOrm` 없이 손수 조립할 때만 사용. 생성자·메서드 전체 시그니처와 동작은 [db-conn.md](./db-conn.md) 의 동명 섹션 참조.