@simplysm/sd-claude 14.0.88 → 14.0.90

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 (135) hide show
  1. package/claude/references/sd-simplysm14/README.md +16 -17
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +39 -43
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +174 -80
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +41 -50
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +60 -26
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +109 -37
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +61 -44
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +39 -31
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +73 -85
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +54 -39
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +55 -30
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +71 -67
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +82 -72
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +35 -36
  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 +38 -30
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +45 -50
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +42 -55
  19. package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +62 -0
  20. package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +39 -38
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +95 -103
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +59 -54
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +86 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +57 -66
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +86 -0
  26. package/claude/references/sd-simplysm14/apis/core-common/obj.md +60 -42
  27. package/claude/references/sd-simplysm14/apis/core-common/serialization.md +55 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +10 -8
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +29 -32
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +34 -22
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +29 -25
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +40 -53
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +22 -29
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +31 -31
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +26 -26
  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/lint/README.md +27 -21
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +89 -49
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +6 -62
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +149 -67
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +111 -99
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +115 -72
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +134 -92
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +67 -52
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +63 -26
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +51 -40
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +10 -12
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +92 -45
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +226 -108
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +90 -88
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +37 -29
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +45 -20
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +89 -40
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +126 -34
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +109 -54
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +70 -66
  62. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +47 -47
  63. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +71 -34
  64. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +31 -32
  65. package/claude/references/sd-simplysm14/apis/storage/README.md +34 -28
  66. package/claude/references/sd-simplysm14/manuals/client-app-structure.md +142 -140
  67. package/claude/references/sd-simplysm14/manuals/client-orm.md +1 -1
  68. package/claude/references/sd-simplysm14/manuals/client-service.md +19 -7
  69. package/claude/references/sd-simplysm14/manuals/client-shared-data.md +2 -2
  70. package/claude/references/sd-simplysm14/manuals/client-system-log.md +11 -3
  71. package/claude/references/sd-simplysm14/manuals/data-log.md +0 -1
  72. package/claude/references/sd-simplysm14/manuals/orm.md +16 -0
  73. package/claude/rules/sd-design-rules.md +10 -0
  74. package/claude/skills/sd-docs/SKILL.md +58 -46
  75. package/claude/skills/sd-docs/references/{doc-rules.md → subagent-prompt.md} +103 -103
  76. package/claude/skills/sd-impl/SKILL.md +1 -1
  77. package/claude/skills/sd-spec/SKILL.md +858 -858
  78. package/claude/skills/sd-spec/references/example-spec.md +26 -36
  79. package/package.json +1 -1
  80. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +0 -47
  81. package/claude/references/sd-simplysm14/apis/orm-common/query-builder.md +0 -29
  82. package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
  83. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
  84. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
  85. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
  86. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
  87. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
  88. package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
  89. package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
  90. package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
  91. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
  92. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
  93. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
  94. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
  95. package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
  96. package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
  97. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
  98. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
  99. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
  100. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
  101. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
  102. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
  103. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
  104. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
  105. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
  106. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
  107. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
  108. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
  109. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
  110. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
  111. package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
  112. package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
  113. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
  114. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
  115. package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
  116. package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
  117. package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
  118. package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
  119. package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
  120. package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
  121. package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
  122. package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
  123. package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
  124. package/claude/skills/sd-review/evals/golden.jsonl +0 -2
  125. package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
  126. package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
  127. package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
  128. package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
  129. package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
  130. package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
  131. package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
  132. package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
  133. package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
  134. package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
  135. package/claude/skills/sd-use/evals/golden.jsonl +0 -6
@@ -1,111 +1,154 @@
1
- # @simplysm/orm-common — Queryable (쿼리 작성)
1
+ # @simplysm/orm-common — Queryable (쿼리 작성·실행 · 프로시저 · 검색)
2
2
 
3
- `db.user()` 등록된 팩토리 호출로 받는 체이닝 쿼리 빌더. 옵션·필터·조인을 immutable 체이닝으로 쌓은 종결 메서드로 실행한다. 콜백 인자 `columns`/`u` 등은 `QueryableRecord`(column `ExprUnit` 프록시)이며, 표현식은 [expr.md](./expr.md) 로 만든다.
3
+ `db.user()` 처럼 컨텍스트 멤버를 호출하면 `Queryable<TData, TFrom>` 받는다. immutable 체이닝으로 옵션·필터·조인·그룹을 쌓고, 종단 메서드(`execute`/`single`/`count`/`insert`/...)로 실행한다. where/select/orderBy 콜백 안에서는 [expr.md](./expr.md) 의 `expr` 표현식을 만든다. 프로시저는 `Executable`, 텍스트 검색 구문은 `parseSearchQuery` 가 처리한다.
4
4
 
5
- `Queryable<TData, TFrom>` `TData` 결과 데이터 타입, `TFrom` 소스 TableBuilder(CUD 연산은 TableBuilder 기반에서만 가능, View/서브쿼리는 `never`).
5
+ 표준 조회 흐름(orm.md): `db.X()` `joinSingle` 로 도출 컬럼 부착 → `select` 모든 도출을 한 번에 projection → projected 컬럼 이름으로 `where`/`orderBy` `count` → `orderBy().limit().execute()`. SELECT 절에 `expr.subquery`/`expr.exists` 넣지 말고 `joinSingle` 로 1회 부착, `wrap()` 은 `distinct`/`groupBy` 뒤 `count` 등 프레임워크가 요구할 때만 사용.
6
6
 
7
- ## 옵션 (SELECT 컬럼 / DISTINCT / LOCK)
7
+ ## Queryable 조립 메서드 (체이닝)
8
8
 
9
- - `select(fn: (cols) => R): Queryable<...>` — SELECT column 재지정. `fn` 은 원본 column 프록시를 받아 `{별칭: ExprUnit | 리터럴 | 중첩객체}` 반환. CUD 소스성 상실(`TFrom=never`).
10
- - `distinct(): Queryable` — DISTINCT 적용. 이후 `count()` 직접 호출 불가(`wrap()` 필요).
11
- - `lock(): Queryable` — FOR UPDATE 행 잠금. 트랜잭션 내 배타 잠금이 필요할 때.
9
+ 옵션:
12
10
 
13
- ## 제한 (TOP / LIMIT)
11
+ - `select(fn)` SELECT column 재정의. `fn(columns) => R` 의 R 이 새 결과 형태. 리터럴 상수는 자동으로 ExprUnit 래핑. 호출 후 CUD 불가(`TFrom` 소실).
12
+ - `distinct()` — DISTINCT 적용. 이후 `count()` 하려면 `wrap()` 필요.
13
+ - `lock()` — 행 잠금(FOR UPDATE). 트랜잭션 안에서 선택 행 배타 잠금. CUD 가능 상태 유지.
14
14
 
15
- - `top(count: number): Queryable` — 상위 N행. ORDER BY 없이도 사용 가능.
16
- - `limit(skip: number, take: number): Queryable` — 페이지네이션(OFFSET `skip`, LIMIT `take`). **ORDER BY 선행 필수**(없으면 throw).
15
+ 제한:
17
16
 
18
- ## 정렬 (ORDER BY)
17
+ - `top(count)` — 상위 N행. ORDER BY 없이도 사용 가능.
18
+ - `limit(skip, take)` — 페이지네이션 OFFSET/LIMIT. `skip`=건너뛸 수, `take`=가져올 수. **먼저 `orderBy()` 호출 필수** — 없으면 throw.
19
19
 
20
- - `orderBy(fnOrKey, orderBy?: "ASC"|"DESC"): Queryable` — 정렬 조건 추가(여러 번 호출 시 순서대로 누적).
21
- - `fnOrKey` — 정렬 column 을 반환하는 함수 `(cols) => ExprUnit`, 또는 체인 경로 문자열(`"id"`, `"user.name"` — `obj.getChainValue` 로 해석. 동적 정렬 루프용).
22
- - `orderBy` — 방향. 기본 ASC.
20
+ 정렬:
23
21
 
24
- ## 검색 (WHERE / search)
22
+ - `orderBy(fnOrKey, orderBy?)` — 정렬 추가(여러 호출 시 순서대로). `fnOrKey`=정렬 column 반환 함수 또는 체인 경로 문자열(`"user.name"`, 동적 정렬용). `orderBy`="ASC"|"DESC"(기본 ASC).
25
23
 
26
- - `where(predicate: (cols) => WhereExprUnit[]): Queryable` — WHERE 조건 추가. 배열 내부는 AND, 여러 번 호출도 AND 누적. 조건은 `expr.eq` 등으로 생성.
27
- - `search(fn: (cols) => ExprUnit<string|undefined>[], searchText: string): Queryable` — 다중 column 텍스트 검색. `fn` 은 검색 대상 column 들, `searchText` 는 검색 문법 문자열(공백=OR, `+`=필수, `-`=제외, `"..."`=구문, `*`=와일드카드). 빈 문자열이면 변화 없이 self 반환. 내부적으로 `lower(col) LIKE pattern` 조합을 AND/OR 로 조립. (문법 상세: README 검색 파서)
24
+ 필터:
28
25
 
29
- ```typescript
30
- db.user().where((u) => [expr.eq(u.isActive, true)]).search((u) => [u.name, u.email], "John -withdrawn");
31
- ```
26
+ - `where(predicate)` — WHERE 조건 추가(여러 번 호출 시 AND 결합). `predicate(columns) => WhereExprUnit[]`. 배열 내 여러 조건도 AND.
27
+ - `search(fn, searchText)` — 텍스트 검색. `fn(columns) => ExprUnit<string|undefined>[]`(검색 대상 column 들), `searchText`=검색 구문(아래 `parseSearchQuery` 문법). 빈 문자열이면 무변경. 공백=OR, `+`=필수(AND), `-`=제외(NOT), `"구문"`=정확 일치(필수). 내부적으로 `expr.like(expr.lower(col), pattern)` 로 대소문자 무시 매칭.
32
28
 
33
- ## 그룹 (GROUP BY / HAVING)
29
+ 그룹:
34
30
 
35
- - `groupBy(fn: (cols) => ExprUnit[]): Queryable` — GROUP BY. 이후 `count()` 직접 호출 불가(`wrap()` 필요).
36
- - `having(predicate: (cols) => WhereExprUnit[]): Queryable` — 그룹 필터(GROUP BY 이후). AND 누적.
31
+ - `groupBy(fn)` — GROUP BY. `fn(columns) => ExprUnit<ColumnPrimitive>[]`. 호출 CUD 불가.
32
+ - `having(predicate)` — GROUP BY 이후 필터. `predicate(columns) => WhereExprUnit[]`.
37
33
 
38
- ## 조인 (JOIN / JOIN SINGLE / INCLUDE)
34
+ 조인:
39
35
 
40
- - `join<A, R>(as: A, fn: (qr, cols) => Queryable<R>): Queryable<TData & { [as]?: R[] }>` 1:N LEFT JOIN, 결과에 **배열**로 추가. `qr` `JoinQueryable`(`.from(table)`/`.select(cols)`/`.union(...)`), `cols` 는 바깥 column. 조인 조건은 반환 Queryable 의 `where` 로.
41
- - `joinSingle<A, R>(as, fn): Queryable<... & { [as]?: R }>` N:1/1:1 LEFT JOIN, 결과에 **단일 객체**로 추가.
42
- - `include(fn: (item) => PathProxy): Queryable` — TableBuilder FK/FKT 관계를 자동 조인. `fn` 타입 안전 경로 프록시(`item.user.company` 처럼 관계 필드만 접근 가능, ColumnPrimitive 필드는 접근 불가). 다단계·중복 include 지원. 관계 미정의 시 throw, View 기반 queryable 에선 사용 불가.
36
+ - `join(as, fn)` 1:N LEFT JOIN, 결과에 **배열**(`{ [as]?: R[] }`)로 부착. `fn(qr, parentCols) => Queryable` 안에서 `qr.from(Table).where(...)` 조인 본문 구성.
37
+ - `joinSingle(as, fn)` — N:1/1:1 LEFT JOIN, 결과에 **단일 객체**(`{ [as]?: R }`)로 부착. 도출 컬럼(집계·boolean)을 outer 행에 붙일 표준 수단.
38
+ - `include(fn)` — 빌더에 정의한 FK/FKT/RelationKey 관계 자동 JOIN. `fn(item) => item.user.company` 형태로 타입 안전 경로 지정(`PathProxy`, ColumnPrimitive 가 아닌 관계 필드만 접근 가능). 다단계·다중 호출 가능. 관계 미정의 시 throw.
43
39
 
44
- ```typescript
45
- db.post().include((p) => p.user.company);
46
- db.user().join("posts", (qr, u) => qr.from(Post).where((p) => [expr.eq(p.userId, u.id)]));
47
- ```
40
+ 서브쿼리/UNION:
48
41
 
49
- ## 서브쿼리 / UNION / 재귀
42
+ - `wrap()` — 현재 Queryable 을 서브쿼리(derived table)로 감쌈. `distinct()`/`groupBy()` 뒤 `count()` 처럼 프레임워크가 요구할 때만.
43
+ - `static Queryable.union(...queries)` — 2개 이상 Queryable 을 UNION(중복 제거)으로 결합. 결과 위 fluent 연산자는 외부 union 결과에 적용. select 컬럼 이름·타입·순서를 양쪽 동일하게 맞춰야 함(orm-union.md). 2개 미만이면 throw.
50
44
 
51
- - `wrap(): Queryable<TData, never>` — 현재 쿼리를 서브쿼리로 래핑. `distinct()`/`groupBy()` 후 `count()` 하려면 필수.
52
- - `static Queryable.union<TData>(...queries): Queryable<TData, never>` — 여러 Queryable UNION(중복 제거). 최소 2개, 미만 시 throw. (`JoinQueryable.union` 도 동일 의미)
53
- - `recursive(fn: (qr: RecursiveQueryable) => Queryable): Queryable<TData, never>` — 재귀 CTE(WITH RECURSIVE). 계층 데이터 조회. `qr.from(table)`/`qr.select(cols)`/`qr.union(...)` 로 재귀 본문 정의, 재귀 측은 `self` 프로퍼티로 base 를 자기 참조.
45
+ 재귀:
54
46
 
55
- ```typescript
56
- const count = await db.user().select((u) => ({ name: u.name })).distinct().wrap().count();
57
- ```
47
+ - `recursive(fn)` — WITH RECURSIVE CTE 생성(계층 데이터). `fn(cte) => cte.from(Table).where(...)` 안에서 `e.self[0]` 로 직전 단계 행을 참조.
48
+
49
+ `db.user().join(...)`/`recursive(...)`/`Queryable.union(...)` 안에서 쓰는 `JoinQueryable`·`RecursiveQueryable` 의 `from`/`select`/`union` 은 콜백 인자로만 노출되며 직접 import 하지 않는다.
58
50
 
59
- ## 종결SELECT 실행
51
+ ## Queryable종단 메서드
52
+
53
+ 조회:
60
54
 
61
55
  - `execute(): Promise<TData[]>` — SELECT 실행, 결과 배열.
62
56
  - `single(): Promise<TData | undefined>` — 단일 결과. 2건 이상이면 throw.
63
- - `first(): Promise<TData | undefined>` — 첫 결과(내부적으로 `top(1)`).
64
- - `count(fn?: (cols) => ExprUnit): Promise<number>` — 행 수. `fn` 지정 시 해당 column 카운트. `distinct()`/`groupBy()` 직후 호출 throw(`wrap()` 먼저).
65
- - `exists(): Promise<boolean>` — 조건 충족 행 존재 여부(`top(1)` 후 길이 검사).
66
- - `getSelectQueryDef(): SelectQueryDef` / `getResultMeta(outputColumns?): ResultMeta` — 실행용 def·결과 메타 생성(executor·서브쿼리 합성에서 사용).
57
+ - `first(): Promise<TData | undefined>` — 첫 행만(내부적으로 `top(1)`). 복수여도 에러 없음.
58
+ - `count(fn?): Promise<number>` — 행 수. `fn?`=셀 column 지정(미지정전체). `distinct()`/`groupBy()` 직후 호출하면 throw(`wrap()` 먼저).
59
+ - `exists(): Promise<boolean>` — 조건 매칭 행 존재 여부(내부 `top(1)`).
60
+
61
+ CUD(모두 `TFrom` 이 TableBuilder 일 때만 — `select`/`groupBy` 후엔 불가):
62
+
63
+ - `insert(records)` / `insert(records, outputColumns)` — 다건 INSERT. `records: $inferInsert[]`. MSSQL 1000행 제한 때문에 1000개씩 청크 분할. `outputColumns: K[]` 지정 시 삽입된 행에서 그 column 만 배열로 반환(AI/PK 값 회수). 빈 배열이면 no-op.
64
+ - `insertIfNotExists(record)` / `(record, outputColumns)` — 현재 WHERE 조건 매칭 행이 없을 때만 1건 INSERT. output 지정 시 삽입 행 1개 반환.
65
+ - `insertInto(targetTable)` / `(targetTable, outputColumns)` — 현재 SELECT 결과를 다른 테이블에 INSERT INTO ... SELECT. `targetTable` 의 column 구조가 현재 결과와 호환해야 함.
66
+ - `update(recordFwd)` / `(recordFwd, outputColumns)` — UPDATE. `recordFwd(cols) => QueryableWriteRecord<$inferUpdate>`(갱신할 column→값). 값은 `ExprInput`(리터럴 직접 가능, `expr.val` 불필요). output 지정 시 갱신 행 반환.
67
+ - `delete()` / `delete(outputColumns)` — DELETE. output 지정 시 삭제 행 반환.
68
+ - `upsert(updateFn)` / `upsert(insertFn, outputColumns?)` / `upsert(updateFn, insertFn)` / `upsert(updateFn, insertFn, outputColumns)` — WHERE 매칭 행 있으면 UPDATE, 없으면 INSERT(MERGE). `updateFn(cols) => 갱신값`, `insertFn(updateRecord) => 삽입값`(생략 시 update 값 재사용). output 지정 시 영향 행 반환.
69
+
70
+ QueryDef 생성기(실행 없이 AST 만): `getSelectQueryDef()`, `getInsertQueryDef(records, outputColumns?)`, `getInsertIfNotExistsQueryDef(...)`, `getInsertIntoQueryDef(...)`, `getUpdateQueryDef(...)`, `getDeleteQueryDef(...)`, `getUpsertQueryDef(...)`, `getResultMeta(outputColumns?)`. executor 우회·디버깅·배치 조립용.
67
71
 
68
- ## 종결 — INSERT
72
+ 기타:
69
73
 
70
- INSERT 는 `outputColumns` 미전달 `void`, 전달 해당 column 뽑은 레코드를 반환(반환 자동증가 PK 수신 등).
71
- - `insert(records: TFrom["$inferInsert"][], outputColumns?): Promise<void | Pick<...>[]>` — 다건 INSERT. MSSQL 1000행 제한 때문에 1000건씩 청크 분할. AI column 에 명시값 있으면 자동 overrideIdentity.
72
- - `insertIfNotExists(record, outputColumns?): Promise<void | Pick<...>>` — 현재 WHERE 조건 매칭이 없을 때만 INSERT.
73
- - `insertInto(targetTable, outputColumns?): Promise<void | Pick<...>[]>` — 현재 SELECT 결과를 다른 테이블에 INSERT INTO ... SELECT. `targetTable` column 구조가 현재 데이터와 호환되어야 함(타입 매칭).
74
- - `getInsertQueryDef` / `getInsertIfNotExistsQueryDef` / `getInsertIntoQueryDef` — 각 def 생성기.
74
+ - `switchFk(enabled)` Queryable 소스 테이블 FK 제약을 활성/비활성. 트랜잭션 안에서 가능.
75
+ - `readonly meta` 내부 조립 상태(from/where/joins/columns 등). 직접 수정하지 않음.
75
76
 
76
77
  ```typescript
77
- const [inserted] = await db.user().insert([{ name: "Hong" }], ["id"]);
78
+ // 표준 조회 (orm.md 흐름)
79
+ const items = await db.order()
80
+ .joinSingle("state", (q, p) =>
81
+ q.from(OrderLine).where((l) => [expr.eq(l.orderId, p.id)])
82
+ .select((l) => ({ sumQty: expr.sum(l.qty), doneCnt: expr.count(l.doneAt) })),
83
+ )
84
+ .select((p) => ({
85
+ id: p.id,
86
+ sumQty: expr.coalesce(p.state!.sumQty, 0),
87
+ isDone: expr.gt(p.state!.doneCnt, 0),
88
+ }))
89
+ .where((r) => [expr.eq(r.isDone, true)])
90
+ .orderBy((r) => r.id, "DESC")
91
+ .limit(0, 50)
92
+ .execute();
93
+
94
+ // CUD — 리터럴 직접 전달
95
+ await db.user().where((u) => [expr.eq(u.id, 1)]).update((u) => ({ name: "새이름" }));
96
+ const [inserted] = await db.user().insert([{ name: "홍길동" }], ["id"]);
78
97
  ```
79
98
 
80
- ## 종결 UPDATE / DELETE
99
+ ## queryable / executable (팩토리 함수)
81
100
 
82
- - `update(recordFwd: (cols) => QueryableWriteRecord, outputColumns?): Promise<void | Pick<...>[]>` — UPDATE. `recordFwd` 는 column → 갱신값(`ExprInput`) 매핑 반환. 기존 참조 가능(`expr.mul(p.price, ...)`). WHERE 는 미리 체이닝.
83
- - `delete(outputColumns?): Promise<void | Pick<...>[]>` — DELETE. WHERE 미리 체이닝.
84
- - `getUpdateQueryDef` / `getDeleteQueryDef` — def 생성기.
101
+ `DbContext` 내부에서 `this.queryable()`/`this.executable()` 쓰는 일반적이지만, 모듈 함수 형태도 export 됨.
85
102
 
86
- ## 종결UPSERT
103
+ - `queryable(db, tableOrView, as?)` `() => Queryable` 팩토리. `as?` 미지정 시 호출마다 새 alias(`db.getNextAlias()`), 지정 시 고정. 반환 Queryable 의 column 은 빌더 정의로부터 구성(View 는 `viewFn` 평가).
104
+ - `executable(db, builder)` — `() => Executable` 팩토리.
87
105
 
88
- - `upsert(updateFn, insertFn?, outputColumns?): Promise<void | Pick<...>[]>` — WHERE 매칭 있으면 UPDATE, 없으면 INSERT.
89
- - `updateFn: (cols) => QueryableWriteRecord` — 갱신/삽입 공통값(insertFn 생략 시 INSERT 도 이 값 사용).
90
- - `insertFn?: (updateRecord) => QueryableWriteRecord` — INSERT 전용값(update 결과를 받아 변형). UPDATE/INSERT 데이터가 다를 때.
91
- - `outputColumns?` — 반환 column.
92
- - `getUpsertQueryDef(...)` — def 생성기.
106
+ ## Executable (프로시저 실행)
93
107
 
94
108
  ```typescript
95
- await db.user()
96
- .where((u) => [expr.eq(u.email, "t@t.com")])
97
- .upsert(() => ({ name: expr.val("string", "x"), email: expr.val("string", "t@t.com") }));
109
+ class Executable<TParams, TReturns> {
110
+ getExecProcQueryDef(params?): ExecProcQueryDef;
111
+ execute(params): Promise<InferColumnExprs<TReturns>[][]>;
112
+ }
98
113
  ```
99
114
 
100
- ## DDL Helper / 기타
115
+ - `execute(params)` 프로시저 실행. `params`=`returns`/`params` 정의에 맞는 `InferColumnExprs<TParams>`(리터럴 또는 ExprUnit). 결과는 **결과셋 배열의 배열**(다중 SELECT 가능) — 단일 결과셋이면 `result[0]`.
116
+ - `getExecProcQueryDef(params?)` — 실행 없이 QueryDef 반환. params 가 있는데 프로시저에 파라미터 정의가 없으면 throw.
117
+
118
+ ```typescript
119
+ const [rows] = await db.getUserById().execute({ userId: 1n });
120
+ ```
121
+
122
+ ## parseSearchQuery / ParsedSearchQuery
123
+
124
+ `search()` 가 내부로 쓰지만 직접 호출도 가능. 검색 구문 문자열을 SQL LIKE 패턴으로 분해.
125
+
126
+ ```typescript
127
+ function parseSearchQuery(searchText: string): ParsedSearchQuery;
128
+ interface ParsedSearchQuery { or: string[]; must: string[]; not: string[]; }
129
+ ```
130
+
131
+ - `or: string[]` — 일반 검색어(공백 구분, OR). 와일드카드 없으면 `%term%`(부분 일치).
132
+ - `must: string[]` — `+term` 또는 `"정확 구문"`(AND 필수).
133
+ - `not: string[]` — `-term`(NOT 제외).
134
+ - 와일드카드 `*` → `%`(`app*`→`app%` 시작 일치). 이스케이프: `\\` `\*` `\%` `\"` `\+` `\-` 는 리터럴. 닫히지 않은 `"` 는 일반 텍스트로 처리.
135
+
136
+ ```typescript
137
+ parseSearchQuery('apple "delicious fruit" -banana +strawberry');
138
+ // { or: ["%apple%"], must: ["%delicious fruit%", "%strawberry%"], not: ["%banana%"] }
139
+ ```
140
+
141
+ ## getMatchedPrimaryKeys
142
+
143
+ ```typescript
144
+ function getMatchedPrimaryKeys(fkCols: string[], targetTable: TableBuilder): string[];
145
+ ```
101
146
 
102
- - `switchFk(enabled: boolean): Promise<void>` 테이블 FK 제약 활성/비활성(트랜잭션 가능). TableBuilder/ViewBuilder 기반에서만.
103
- - `queryable(db, tableOrView, as?): () => Queryable` — Table/View 용 Queryable 팩토리 함수(보통 `DbContext.queryable()` 가 호출). `as` 미지정 시 자동 alias.
104
- - `getMatchedPrimaryKeys(fkCols, targetTable): string[]` — FK column 배열과 대상 PK 매칭(개수 불일치 시 throw). include 내부 헬퍼.
147
+ - FK column 배열을 대상 테이블 PK 와 매칭해 PK column 이름 배열 반환. 개수 불일치 시 throw. `include()` 관계 조건을 만들 내부 사용 직접 호출은 드묾.
105
148
 
106
- ## 관련 타입
149
+ ## 결과/입력 변환 타입
107
150
 
108
- - `QueryableRecord<TData>` — column → `ExprUnit` 프록시 레코드(콜백 인자 타입).
109
- - `QueryableWriteRecord<TData>` — column `ExprInput`(쓰기 콜백 반환 타입).
110
- - `UnwrapQueryableRecord<R>` — `select` 결과 `ExprUnit` 언래핑 데이터 타입.
111
- - `PathProxy<TObject>` — `include` 경로 수집용 타입 안전 프록시(관계 필드만 노출).
151
+ - `QueryableRecord<TData>` — 결과 데이터 column 프록시 타입. 각 ColumnPrimitive 가 `ExprUnit<T>`, 중첩 관계는 재귀. where/select 등 콜백 인자 타입.
152
+ - `QueryableWriteRecord<TData>` — 쓰기용. 필드가 `ExprInput<T>`. `update`/`upsert` 콜백 반환 타입.
153
+ - `UnwrapQueryableRecord<R>` — `select` 결과를 다시 데이터 타입으로 역추론(ExprUnit→값).
154
+ - `PathProxy<TObject>` — `include` 경로 지정용 프록시 타입(관계 필드만 접근).
@@ -1,21 +1,39 @@
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` `queryable()`/`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)` 으로 시작해 메서드 체이닝.
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
+ ```
8
26
 
9
- - `Table<TName>(name: TName): TableBuilder` — 빈 테이블 빌더 생성.
10
- - `TableBuilder.description(desc: string)` — 테이블 설명. DDL Comment사용.
11
- - `TableBuilder.database(db: string)` — 소속 database 이름.
12
- - `TableBuilder.schema(schema: string)` — schema 이름(MSSQL=dbo, PostgreSQL=public). MySQL 무시.
13
- - `TableBuilder.columns(fn: (c) => Record<string, ColumnBuilder>)` — column 정의. `c` column factory(아래 ColumnBuilder factory). 타입 추론의 핵심.
14
- - `TableBuilder.primaryKey(...columns: string[])` — PK column 지정. 여러 개 전달 복합 PK.
15
- - `TableBuilder.indexes(fn: (i) => IndexBuilder[])` — index 정의. `i` index factory.
16
- - `TableBuilder.relations(fn: (r) => Record<string, RelationBuilder>)` FK/역참조 관계 정의. `r`relation factory(FK + RelationKey 모두 사용 가능).
17
- - `TableBuilder.meta` 설정이 담긴 메타(`name`/`description`/`database`/`schema`/`columns`/`primaryKey`/`relations`/`indexes`). 런타임에서 읽음.
18
- - 타입 추론 프로퍼티(값은 없고 타입만): `$inferSelect`(column+관계 전체), `$inferColumns`(column 만), `$inferInsert`(autoIncrement/nullable/default optional), `$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
39
  const User = Table("User")
@@ -26,42 +44,62 @@ const User = Table("User")
26
44
  email: c.varchar(200).nullable(),
27
45
  }))
28
46
  .primaryKey("id")
29
- .indexes((i) => [i.index("email").unique()])
30
- .relations((r) => ({ posts: r.foreignKeyTarget(() => Post, "author") }));
47
+ .indexes((i) => [i.index("email").unique()]);
31
48
  ```
32
49
 
33
50
  ## View / ViewBuilder
34
51
 
35
- 뷰 정의 빌더. SELECT Queryable 로 데이터 소스를 정의. 관계는 RelationKey 만 가능(DB FK 미생성).
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
+ ```
36
66
 
37
- - `View(name: string): ViewBuilder` — 빈 빌더 생성.
38
- - `ViewBuilder.description(desc: string)` — 뷰 설명(DDL Comment).
39
- - `ViewBuilder.database(db: string)` — 소속 database 이름.
40
- - `ViewBuilder.schema(schema: string)` — schema 이름(MSSQL/PostgreSQL).
41
- - `ViewBuilder.query(viewFn: (db) => Queryable)` — 뷰 본문 SELECT 정의. `db` 는 DbContext, 반환은 Queryable.
42
- - `ViewBuilder.relations(fn: (r) => ...)` — 관계 정의(`relationKey`/`relationKeyTarget` 만).
43
- - `ViewBuilder.meta` — `name`/`description`/`database`/`schema`/`viewFn`/`relations`.
44
- - `$inferSelect` — 뷰 데이터 타입(타입 추론용).
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 과 동일 의미.
45
71
 
46
72
  ```typescript
47
73
  const ActiveUsers = View("ActiveUsers")
48
74
  .database("mydb")
49
- .query((db: MyDb) => db.user().where((u) => [expr.eq(u.status, "active")]));
75
+ .query((db: MainDb) =>
76
+ db.user().where((u) => [expr.eq(u.status, "active")]).select((u) => ({ id: u.id, name: u.name })),
77
+ );
50
78
  ```
51
79
 
52
80
  ## Procedure / ProcedureBuilder
53
81
 
54
- 저장 프로시저 정의 빌더. `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
+ ```
55
97
 
56
- - `Procedure(name: string): ProcedureBuilder` — 빈 프로시저 빌더 생성.
57
- - `ProcedureBuilder.description(desc: string)` — 설명(DDL Comment).
58
- - `ProcedureBuilder.database(db: string)` — 소속 database.
59
- - `ProcedureBuilder.schema(schema: string)` schema 이름.
60
- - `ProcedureBuilder.params(fn: (c) => Record<string, ColumnBuilder>)` — 입력 파라미터 정의(column factory 사용).
61
- - `ProcedureBuilder.returns(fn: (c) => Record<string, ColumnBuilder>)` — 반환 결과 column 정의.
62
- - `ProcedureBuilder.body(sql: string)` — 본문 SQL. DBMS별 구문 차이 주의(MySQL: `param`, MSSQL: `@param`, PostgreSQL: `RETURN QUERY` 필요).
63
- - `ProcedureBuilder.meta` — `name`/`description`/`database`/`schema`/`params`/`returns`/`query`.
64
- - `$params` / `$returns` — 타입 추론용.
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 동일.
65
103
 
66
104
  ```typescript
67
105
  const GetUserById = Procedure("GetUserById")
@@ -71,51 +109,52 @@ const GetUserById = Procedure("GetUserById")
71
109
  .body("SELECT id, name FROM User WHERE id = userId");
72
110
  ```
73
111
 
74
- ## ColumnBuilder / createColumnFactory
75
-
76
- `columns()`/`params()`/`returns()` 콜백의 `c` 받는 factory. 타입별 메서드로 column 만든 modifier 체이닝.
77
-
78
- factory 타입 메서드:
79
- - `c.int()` — INT(4바이트). 값 타입 `number`.
80
- - `c.bigint()` — BIGINT(8바이트). 값 타입 `number`(bigint 아님).
81
- - `c.float()` — FLOAT(4바이트 단정밀도). `number`.
82
- - `c.double()` — DOUBLE(8바이트 배정밀도). `number`.
83
- - `c.decimal(precision, scale?)` — DECIMAL 고정소수점. `precision` 전체 자릿수, `scale` 소수 자릿수(선택). `number`.
84
- - `c.varchar(length)` — VARCHAR(가변). `length` 최대 길이. `string`.
85
- - `c.char(length)` — CHAR(고정). `string`.
86
- - `c.text()` — TEXT 대용량. `string`.
87
- - `c.binary()` — 바이너리(MySQL=LONGBLOB, MSSQL=VARBINARY(MAX), PostgreSQL=BYTEA). `Bytes`.
88
- - `c.boolean()` — BOOLEAN(MySQL=TINYINT(1), MSSQL=BIT, PostgreSQL=BOOLEAN). `boolean`.
89
- - `c.datetime()` — DATETIME(날짜+시간). `DateTime`.
90
- - `c.date()` — DATE(날짜만). `DateOnly`.
91
- - `c.time()` — TIME(시간만). `Time`.
92
- - `c.uuid()` — UUID(MySQL=BINARY(16), MSSQL=UNIQUEIDENTIFIER, PostgreSQL=UUID). `Uuid`.
93
-
94
- modifier(생성된 ColumnBuilder 에 체이닝):
95
- - `.autoIncrement()` 자동 증가. INSERT 타입에서 optional 처리. (PK 식별 시 OUTPUT 의 aiColName 으로도 사용)
96
- - `.nullable()` — NULL 허용. 값 타입에 `undefined` 추가, INSERT 타입에서 optional.
97
- - `.default(value)` — INSERT 시 미지정 기본값. INSERT 타입에서 optional. (예: `"CURRENT_TIMESTAMP"`)
98
- - `.description(desc)` — column 설명(DDL Comment).
99
- - `ColumnBuilder.meta` — `ColumnMeta`(`type`/`dataType`/`autoIncrement?`/`nullable?`/`default?`/`description?`).
112
+ ## column factory / ColumnBuilder
113
+
114
+ `columns`/`params`/`returns` 콜백 인자 `c` column factory. 메서드는 `ColumnBuilder` 반환하고 `.autoIncrement()`/`.nullable()`/`.default()`/`.description()` 속성을 더한다(모두 immutable).
115
+
116
+ 타입 메서드:
117
+
118
+ - `int()` — INT(4바이트 정수).
119
+ - `bigint()` — BIGINT(8바이트 정수). autoIncrement PK 에 주로 사용.
120
+ - `float()` — FLOAT(단정밀도 실수).
121
+ - `double()` — DOUBLE(배정밀도 실수).
122
+ - `decimal(precision, scale?)` — 고정 소수점. `precision`=전체 자릿수, `scale`=소수 자릿수(선택). 금액 정밀도 필요 시.
123
+ - `varchar(length)` — 가변 길이 문자열. `length`=최대 길이.
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).
100
139
 
101
140
  ```typescript
102
141
  .columns((c) => ({
103
142
  id: c.bigint().autoIncrement(),
104
- status: c.varchar(20).default("active"),
143
+ price: c.decimal(10, 2),
105
144
  email: c.varchar(200).nullable(),
145
+ status: c.varchar(20).default("active"),
106
146
  }))
107
147
  ```
108
148
 
109
- ## IndexBuilder / createIndexFactory
149
+ ## index factory / IndexBuilder
110
150
 
111
- `indexes()` 콜백의 `i` factory.
151
+ `indexes` 콜백 인자 `i` 의 `index(...columns)` 로 시작해 fluent 로 옵션을 더한다.
112
152
 
113
- - `i.index(...columns: string[]): IndexBuilder` — index 생성. 여러 column 전달 복합 index.
114
- - `IndexBuilder.name(name)` — index 이름 지정(미지정 시 자동).
115
- - `IndexBuilder.unique()` — 유니크 index.
116
- - `IndexBuilder.orderBy(...orderBy: ("ASC"|"DESC")[])` column 별 정렬 방향. column 수와 인자 일치 필요.
117
- - `IndexBuilder.description(description)` — index 설명(DDL Comment).
118
- - `IndexBuilder.meta` — `columns`/`name?`/`unique?`/`orderBy?`/`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).
119
158
 
120
159
  ```typescript
121
160
  .indexes((i) => [
@@ -124,39 +163,42 @@ modifier(생성된 ColumnBuilder 에 체이닝):
124
163
  ])
125
164
  ```
126
165
 
127
- ## 관계 빌더 (relations 콜백의 r factory)
166
+ ## relation factory
128
167
 
129
- Table `relations()` FK+RelationKey 다, View `relations()` RelationKey 사용 가능.
168
+ `relations` 콜백 인자 `r`. Table 4종 모두, View 는 `relationKey`/`relationKeyTarget` 만. 대상 빌더는 순환 참조 방지를 위해 모두 `() => Target` 지연 함수로 넘긴다. `description`/`single` 은 메서드 체이닝이 아니라 마지막 `opts` 인자로 전달(체이닝은 TS7022 유발로 제거됨).
130
169
 
131
- - `r.foreignKey(columns: string[], targetFn: () => Table, opts?: { description? })` — N:1 FK(DB FK 제약 생성). `columns` owner 의 FK column, `targetFn` 지연 평가 대상 테이블. → `ForeignKeyBuilder`.
132
- - `r.foreignKeyTarget(targetTableFn: () => Table, relationName: string, opts?: { single?: boolean; description? })` — 1:N 역참조(DB FK 생성 측의 반대편). `relationName` 대상 테이블에 정의된 FK 관계 이름. `single:true` 면 단일 객체(1:1), 기본은 배열. → `ForeignKeyTargetBuilder`.
133
- - `r.relationKey(columns, targetFn, opts?)` — N:1 논리 관계(DB FK **미생성**). View 에서도 사용. → `RelationKeyBuilder`.
134
- - `r.relationKeyTarget(targetTableFn, relationName, opts?: { single?; description? })` — 1:N 논리 역참조(DB FK 미생성). `single:true` 단일. → `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`.
135
174
 
136
- 관계 빌더의 `meta`:
137
- - `ForeignKeyBuilder.meta` / `RelationKeyBuilder.meta` — `ownerFn`/`columns`/`targetFn`/`description?`.
138
- - `ForeignKeyTargetBuilder.meta` / `RelationKeyTargetBuilder.meta` — `targetTableFn`/`relationName`/`description?`/`isSingle?`.
175
+ 각 `opts`:
139
176
 
140
- > 주의: `description`/`single` 은 메서드 체이닝(`.description()`)이 아니라 factory 의 `opts` 인자로 전달한다(순환 참조 TS7022 회피).
177
+ - `description?: string` 관계 설명.
178
+ - `single?: boolean` (target 계열만) — true=결과를 단일 객체(1:1), false/미지정=배열(1:N). `include()`/`$inferSelect` 의 해당 키 타입이 단일/배열로 갈린다.
141
179
 
142
180
  ```typescript
143
181
  const Post = Table("Post")
144
182
  .columns((c) => ({ id: c.bigint().autoIncrement(), authorId: c.bigint() }))
145
183
  .primaryKey("id")
146
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
+ }));
147
193
  ```
148
194
 
149
- ## 타입 추론 유틸 / 레코드 타입
195
+ 주의: FK column 개수와 대상 테이블 PK 개수가 다르면 `include()`/join 시 "FK/PK column count mismatch" throw. PK 가 복합이면 FK column 도 같은 순서·개수로.
150
196
 
151
- 빌더에서 export 되는 추론 헬퍼·레코드 타입(직접 타입 작성 시):
197
+ ## 빌더/추론 타입 (직접 참조 드묾)
152
198
 
153
- - `ColumnBuilderRecord` — `Record<string, ColumnBuilder>`. `columns()` 반환 타입.
154
- - `InferColumns<TBuilders>`column 레코드 타입 객체.
155
- - `InferColumnExprs<TBuilders>` — column 레코드 `ExprInput` 입력 타입 객체(프로시저 params).
156
- - `InferInsertColumns<TBuilders>` — INSERT 타입(필수/optional 분리).
157
- - `InferUpdateColumns<TBuilders>` — UPDATE 타입(전부 Partial).
158
- - `RequiredInsertKeys` / `OptionalInsertKeys`INSERT 필수/선택 key 추출.
159
- - `DataToColumnBuilderRecord<TData>` — 데이터 레코드 → column 빌더 레코드(insertInto 타입 매칭용).
160
- - `RelationBuilderRecord` — 4개 관계 빌더 union 의 레코드.
161
- - `InferDeepRelations<TRelations, TVisited?>` — 관계 정의 → 심층 관계 타입(전부 optional, 동일 테이블 재방문 시 순환 차단).
162
- - `ExtractRelationTarget` / `ExtractRelationTargetResult` — 단일(N:1)/배열·단일(1:N) 대상 타입 추출.
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` 의 관계 부분.