@simplysm/sd-claude 14.0.91 → 14.0.92
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/claude/references/sd-simplysm14/README.md +7 -6
- package/claude/references/sd-simplysm14/apis/angular/README.md +59 -39
- package/claude/references/sd-simplysm14/apis/angular/controls.md +119 -186
- package/claude/references/sd-simplysm14/apis/angular/crud.md +70 -31
- package/claude/references/sd-simplysm14/apis/angular/directives.md +55 -57
- package/claude/references/sd-simplysm14/apis/angular/features.md +86 -105
- package/claude/references/sd-simplysm14/apis/angular/infra.md +48 -57
- package/claude/references/sd-simplysm14/apis/angular/layout.md +37 -47
- package/claude/references/sd-simplysm14/apis/angular/overlay.md +82 -74
- package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +61 -50
- package/claude/references/sd-simplysm14/apis/angular/shared-data.md +74 -57
- package/claude/references/sd-simplysm14/apis/angular/sheet.md +63 -72
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +23 -18
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +21 -19
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +23 -18
- package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +72 -32
- package/claude/references/sd-simplysm14/apis/core-browser/README.md +18 -18
- package/claude/references/sd-simplysm14/apis/core-browser/dom-element.md +29 -29
- package/claude/references/sd-simplysm14/apis/core-browser/indexed-db.md +41 -41
- package/claude/references/sd-simplysm14/apis/core-common/README.md +97 -90
- package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +75 -51
- package/claude/references/sd-simplysm14/apis/core-common/collection-ext.md +81 -0
- package/claude/references/sd-simplysm14/apis/core-common/errors.md +27 -29
- package/claude/references/sd-simplysm14/apis/core-common/obj.md +44 -45
- package/claude/references/sd-simplysm14/apis/core-common/serialization.md +34 -33
- package/claude/references/sd-simplysm14/apis/core-common/value-types.md +86 -0
- package/claude/references/sd-simplysm14/apis/core-node/README.md +6 -6
- package/claude/references/sd-simplysm14/apis/core-node/consola.md +3 -0
- package/claude/references/sd-simplysm14/apis/core-node/cpx.md +2 -2
- package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +1 -1
- package/claude/references/sd-simplysm14/apis/core-node/fsx.md +2 -2
- package/claude/references/sd-simplysm14/apis/core-node/worker.md +6 -3
- package/claude/references/sd-simplysm14/apis/excel/README.md +10 -10
- package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +4 -2
- package/claude/references/sd-simplysm14/apis/excel/utils.md +1 -1
- package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +6 -6
- package/claude/references/sd-simplysm14/apis/lint/README.md +6 -32
- package/claude/references/sd-simplysm14/apis/lint/recommended.md +60 -0
- package/claude/references/sd-simplysm14/apis/lint/rules.md +17 -17
- package/claude/references/sd-simplysm14/apis/orm-common/README.md +15 -6
- package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +68 -102
- package/claude/references/sd-simplysm14/apis/orm-common/expr.md +75 -89
- package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +87 -99
- package/claude/references/sd-simplysm14/apis/orm-common/schema.md +110 -147
- package/claude/references/sd-simplysm14/apis/orm-common/types.md +48 -51
- package/claude/references/sd-simplysm14/apis/orm-node/README.md +8 -13
- package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +5 -5
- package/claude/references/sd-simplysm14/apis/sd-cli/README.md +9 -6
- package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +9 -8
- package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +23 -19
- package/claude/references/sd-simplysm14/apis/service-client/README.md +20 -12
- package/claude/references/sd-simplysm14/apis/service-client/orm.md +6 -6
- package/claude/references/sd-simplysm14/apis/service-client/transport.md +1 -1
- package/claude/references/sd-simplysm14/apis/service-common/README.md +35 -32
- package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +23 -22
- package/claude/references/sd-simplysm14/apis/service-common/protocol.md +23 -23
- package/claude/references/sd-simplysm14/apis/service-server/README.md +51 -43
- package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +6 -6
- package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +31 -21
- package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +8 -8
- package/claude/references/sd-simplysm14/apis/storage/README.md +55 -49
- package/claude/references/sd-simplysm14/manuals/client-component.md +843 -740
- package/claude/references/sd-simplysm14/manuals/client-crud.md +8 -0
- package/claude/references/sd-simplysm14/manuals/client-demo.md +6 -16
- package/claude/references/sd-simplysm14/manuals/client-shared-data.md +26 -0
- package/claude/references/sd-simplysm14/manuals/logging.md +1 -1
- package/claude/references/sd-simplysm14/manuals/orm.md +15 -1
- package/claude/rules/sd-design-rules.md +7 -0
- package/claude/sd-system-prompt.md +5 -8
- package/claude/skills/sd-debug/SKILL.md +43 -0
- package/claude/skills/sd-debug/workflow.js +390 -0
- package/claude/skills/sd-demo/SKILL.md +18 -20
- package/claude/skills/sd-dev/SKILL.md +127 -24
- package/claude/skills/sd-docs/SKILL.md +5 -3
- package/claude/skills/sd-docs/references/subagent-prompt.md +2 -3
- package/claude/skills/sd-impl/SKILL.md +18 -18
- package/claude/skills/sd-manual/SKILL.md +1 -0
- package/claude/skills/sd-review/SKILL.md +24 -18
- package/claude/skills/sd-review/workflow.js +324 -0
- package/claude/skills/sd-spec/SKILL.md +96 -679
- package/claude/skills/sd-spec/references/example-spec.md +28 -50
- package/claude/skills/sd-spec/references/format-analyze.md +232 -0
- package/claude/skills/sd-spec/references/format-design.md +248 -0
- package/claude/skills/sd-spec/workflow-analyze.js +615 -0
- package/claude/skills/sd-spec/workflow-design.js +667 -0
- package/claude/skills/sd-unpack/scripts/handlers/office_com.py +5 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +157 -18
- package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +0 -68
- package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +0 -77
- package/claude/references/sd-simplysm14/apis/core-common/datetime.md +0 -86
- package/claude/skills/sd-skill/SKILL.md +0 -245
- package/claude/skills/sd-skill/scripts/run_eval.py +0 -380
|
@@ -1,119 +1,121 @@
|
|
|
1
1
|
# @simplysm/orm-common — Queryable (쿼리 작성·실행 · 프로시저 · 검색)
|
|
2
2
|
|
|
3
|
-
`db.user()` 처럼 컨텍스트 멤버를 호출하면 `Queryable<TData, TFrom>` 를 받는다. immutable 체이닝으로 옵션·필터·조인·그룹을 쌓고, 종단 메서드(`execute`/`single`/`count`/`insert`/...)로 실행한다. where/select/orderBy 콜백 안에서는 [expr.md](./expr.md) 의 `expr` 로 표현식을 만든다. 프로시저는 `Executable`, 텍스트 검색 구문은 `parseSearchQuery` 가 처리한다.
|
|
3
|
+
`db.user()` 처럼 컨텍스트 멤버를 호출하면 `Queryable<TData, TFrom>` 를 받는다. immutable 체이닝으로 옵션·필터·조인·그룹을 쌓고, 종단 메서드(`execute`/`single`/`count`/`insert`/...)로 실행한다. where/select/orderBy 콜백 안에서는 [expr.md](./expr.md) 의 `expr` 로 표현식을 만든다. 프로시저는 `Executable`, 텍스트 검색 구문은 `parseSearchQuery` 가 처리한다. 표준 조회 흐름·안티패턴은 orm.md/orm-union.md 참조.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Queryable<TData, TFrom>
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`TData` = 결과 데이터 타입, `TFrom` = 소스 `TableBuilder`(CUD 가능 여부). `select`/`join`/`groupBy`/`distinct`/`wrap`/`union` 등을 거치면 `TFrom` 이 `never` 가 되어 CUD 가 막힌다(파생 컬럼 위에서는 INSERT/UPDATE 불가).
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
### 옵션 — select / distinct / lock
|
|
10
10
|
|
|
11
|
-
- `select(fn)` —
|
|
12
|
-
- `distinct()` — DISTINCT 적용. 이후 `count()` 하려면 `wrap()`
|
|
13
|
-
- `lock()` —
|
|
11
|
+
- `select(fn)` — projection. `fn` 은 원본 컬럼 프록시를 받아 새 컬럼 객체 반환. 반환 객체 안에서 `expr.*` 도출(coalesce/CASE/산식)을 한 번에 작성. 결과 `TData` 가 새 shape 로 바뀜.
|
|
12
|
+
- `distinct()` — `DISTINCT` 적용. 이후 `count()` 하려면 `wrap()` 먼저(아니면 throw).
|
|
13
|
+
- `lock()` — `FOR UPDATE` 행 잠금. 트랜잭션 내에서 선택 행 배타 잠금이 필요할 때.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
```typescript
|
|
16
|
+
db.user().select((u) => ({ userName: u.name, userEmail: u.email }))
|
|
17
|
+
```
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
- `limit(skip, take)` — 페이지네이션 OFFSET/LIMIT. `skip`=건너뛸 수, `take`=가져올 수. **먼저 `orderBy()` 호출 필수** — 없으면 throw.
|
|
19
|
+
### 제한 — top / limit
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
- `top(count)` — 상위 `count` 행만. ORDER BY 없이도 사용 가능.
|
|
22
|
+
- `limit(skip, take)` — 페이지네이션. `skip`=OFFSET, `take`=LIMIT. **호출 전 `orderBy()` 필수**(없으면 throw).
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
```typescript
|
|
25
|
+
db.user().orderBy((u) => u.createdAt, "DESC").limit(page * 50, 50)
|
|
26
|
+
```
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
### 정렬 — orderBy
|
|
25
29
|
|
|
26
|
-
- `
|
|
27
|
-
- `search(fn, searchText)` — 텍스트 검색. `fn(columns) => ExprUnit<string|undefined>[]`(검색 대상 column 들), `searchText`=검색 구문(아래 `parseSearchQuery` 문법). 빈 문자열이면 무변경. 공백=OR, `+`=필수(AND), `-`=제외(NOT), `"구문"`=정확 일치(필수). 내부적으로 `expr.like(expr.lower(col), pattern)` 로 대소문자 무시 매칭.
|
|
30
|
+
- `orderBy(fnOrKey, orderBy?)` — 정렬 조건 추가(여러 번 호출 시 순서대로). `fnOrKey`=컬럼 반환 함수 또는 체인 경로 문자열(`"user.name"`, 동적 정렬용). `orderBy`=`"ASC" | "DESC"`(기본 ASC).
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
### 필터 — where / search
|
|
30
33
|
|
|
31
|
-
- `
|
|
32
|
-
- `
|
|
34
|
+
- `where(predicate)` — WHERE 조건 추가(여러 번 호출 시 AND 결합). `predicate` 는 컬럼 프록시를 받아 `WhereExprUnit[]` 반환. select 로 만든 파생 컬럼 이름도 직접 참조 가능(framework 가 AST inline).
|
|
35
|
+
- `search(fn, searchText)` — 텍스트 검색. `fn`=검색 대상 문자열 컬럼 배열 반환, `searchText`=검색 구문(`parseSearchQuery` 규칙: 공백=OR, `+`=필수, `-`=제외, `"..."`=구문, `*`=와일드카드). 빈 문자열이면 조건 미추가. 내부적으로 `LOWER(col) LIKE pattern` 조합으로 변환.
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
```typescript
|
|
38
|
+
db.user()
|
|
39
|
+
.where((u) => [expr.eq(u.isActive, true)])
|
|
40
|
+
.search((u) => [u.name, u.email], "John -withdrawn")
|
|
41
|
+
```
|
|
35
42
|
|
|
36
|
-
|
|
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
|
+
### 그룹 — groupBy / having
|
|
39
44
|
|
|
40
|
-
|
|
45
|
+
- `groupBy(fn)` — GROUP BY. `fn` 은 그룹 컬럼 배열 반환. 이후 `count()` 하려면 `wrap()` 먼저(아니면 throw).
|
|
46
|
+
- `having(predicate)` — 그룹 필터(여러 번 호출 시 AND). 집계 컬럼 기준 필터링.
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
- `static Queryable.union(...queries)` — 2개 이상 Queryable 을 UNION(중복 제거)으로 결합. 결과 위 fluent 연산자는 외부 union 결과에 적용. select 컬럼 이름·타입·순서를 양쪽 동일하게 맞춰야 함(orm-union.md). 2개 미만이면 throw.
|
|
48
|
+
### 조인 — join / joinSingle / include
|
|
44
49
|
|
|
45
|
-
|
|
50
|
+
- `join(as, fn)` — 1:N LEFT JOIN. 결과에 `as` 키로 배열 추가. `fn(qr, cols)` 의 `qr.from(Table).where(...)` 로 조인 조건·서브쿼리 정의.
|
|
51
|
+
- `joinSingle(as, fn)` — N:1/1:1 LEFT JOIN. 결과에 `as` 키로 단일 객체(또는 undefined) 추가. 도메인 boolean·집계는 SELECT subquery 대신 `joinSingle` 안 `from+where+select(aggregate)` 로 부착(orm.md).
|
|
52
|
+
- `include(fn)` — `TableBuilder` 의 FK/FKT 관계를 자동 조인. `fn(item)` 은 PathProxy 로 관계 경로만 접근(컬럼은 컴파일 에러). 다단계(`p.user.company`)·다중 호출 지원. 관계 미정의면 throw.
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
```typescript
|
|
55
|
+
db.post()
|
|
56
|
+
.joinSingle("user", (qr, p) => qr.from(User).where((u) => [expr.eq(u.id, p.userId)]))
|
|
57
|
+
.include((p) => p.user.company)
|
|
58
|
+
```
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
### 서브쿼리 — wrap / union / recursive
|
|
50
61
|
|
|
51
|
-
|
|
62
|
+
- `wrap()` — 현재 Queryable 을 서브쿼리(derived table)로 감쌈. `distinct()`/`groupBy()` 후 `count()` 호출에 필요한 자리에서만 사용(불필요한 wrap 금지, orm.md).
|
|
63
|
+
- `static Queryable.union(...queries)` — 2개 이상 Queryable 을 UNION 결합(중복 제거). 결과는 derived table 로 취급되어 이후 `orderBy`/`limit`/`where` 등은 union 결과 위에 적용. 각 소스에 predicate pushdown 하려면 union 전에 미리 호출. 2개 미만이면 `ArgumentError`. 이종 엔티티 합치기는 orm-union.md.
|
|
64
|
+
- `recursive(fn)` — 재귀 CTE(WITH RECURSIVE). 계층 데이터(조직도·카테고리 트리)용. `fn(cte)` 의 `cte.from(Table)` 안에서 `self` 로 상위 결과를 자기참조.
|
|
52
65
|
|
|
53
|
-
|
|
66
|
+
```typescript
|
|
67
|
+
const items = await Queryable.union(inQr, outQr).orderBy((r) => r.date, "DESC").limit(0, 50).execute();
|
|
68
|
+
```
|
|
54
69
|
|
|
55
|
-
|
|
56
|
-
- `single(): Promise<TData | undefined>` — 단일 결과. 2건 이상이면 throw.
|
|
57
|
-
- `first(): Promise<TData | undefined>` — 첫 행만(내부적으로 `top(1)`). 복수여도 에러 없음.
|
|
58
|
-
- `count(fn?): Promise<number>` — 행 수. `fn?`=셀 column 지정(미지정 시 전체). `distinct()`/`groupBy()` 직후 호출하면 throw(→ `wrap()` 먼저).
|
|
59
|
-
- `exists(): Promise<boolean>` — 조건 매칭 행 존재 여부(내부 `top(1)`).
|
|
70
|
+
### 종단 — SELECT 실행
|
|
60
71
|
|
|
61
|
-
|
|
72
|
+
- `execute()` — SELECT 실행, 결과 배열 반환.
|
|
73
|
+
- `single()` — 단일 결과 또는 undefined. 2건 이상이면 `ArgumentError`.
|
|
74
|
+
- `first()` — 첫 결과 또는 undefined(`top(1)`).
|
|
75
|
+
- `exists()` — 조건 일치 행 존재 여부 `boolean`(`top(1)`).
|
|
76
|
+
- `count(fn?)` — 행 수. `fn`=셀 컬럼 지정(생략 시 전체). `distinct()`/`groupBy()` 직후 호출 시 throw(→ `wrap()` 먼저).
|
|
77
|
+
- `getSelectQueryDef()` / `getResultMeta(outputColumns?)` — 실행 없이 `SelectQueryDef` AST / 결과 변환 메타(`ResultMeta`) 산출. 서브쿼리 합성·저수준 실행용.
|
|
62
78
|
|
|
63
|
-
|
|
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 지정 시 영향 행 반환.
|
|
79
|
+
### 종단 — INSERT
|
|
69
80
|
|
|
70
|
-
|
|
81
|
+
- `insert(records, outputColumns?)` — 배열 삽입(MSSQL 1000행 제한 대비 1000개씩 청크 분할). `records`=`$inferInsert[]`. `outputColumns` 지정 시 삽입된 행의 해당 컬럼 배열 반환(예: 생성된 `id`). 빈 배열이면 즉시 반환.
|
|
82
|
+
- `insertIfNotExists(record, outputColumns?)` — 현재 WHERE 조건에 맞는 행이 없을 때만 1건 삽입. `outputColumns` 지정 시 삽입 행 반환.
|
|
83
|
+
- `insertInto(targetTable, outputColumns?)` — 현재 SELECT 결과를 다른 테이블에 INSERT INTO ... SELECT. `targetTable` 컬럼이 현재 데이터 shape 와 맞아야 함.
|
|
71
84
|
|
|
72
|
-
|
|
85
|
+
```typescript
|
|
86
|
+
const [created] = await db.user().insert([{ name: "홍길동" }], ["id"]);
|
|
87
|
+
```
|
|
73
88
|
|
|
74
|
-
|
|
75
|
-
|
|
89
|
+
### 종단 — UPDATE / DELETE / UPSERT
|
|
90
|
+
|
|
91
|
+
- `update(recordFwd, outputColumns?)` — UPDATE. `recordFwd(cols)` 가 갱신 컬럼/값 객체 반환(`ExprInput`, 리터럴 직접 전달 — `expr.val` 불필요). `outputColumns` 지정 시 갱신 행 반환.
|
|
92
|
+
- `delete(outputColumns?)` — DELETE. `outputColumns` 지정 시 삭제 행 반환.
|
|
93
|
+
- `upsert(updateFn, insertFn?, outputColumns?)` — WHERE 조건 일치 시 UPDATE, 없으면 INSERT. `insertFn` 생략 시 update 값으로 insert. `insertFn(updateRecord)` 는 update 결과를 받아 insert 레코드 산출. `outputColumns` 지정 시 영향 행 반환.
|
|
94
|
+
- 각 종단마다 `getUpdateQueryDef`/`getDeleteQueryDef`/`getUpsertQueryDef`/`getInsertQueryDef`/`getInsertIfNotExistsQueryDef`/`getInsertIntoQueryDef` 로 실행 없이 def 만 얻을 수 있음.
|
|
76
95
|
|
|
77
96
|
```typescript
|
|
78
|
-
|
|
79
|
-
|
|
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"]);
|
|
97
|
+
await db.user().where((u) => [expr.eq(u.id, 1)]).update(() => ({ name: "수정" }));
|
|
98
|
+
await db.user().where((u) => [expr.eq(u.email, "a@b.com")]).upsert(() => ({ name: "A", email: "a@b.com" }));
|
|
97
99
|
```
|
|
98
100
|
|
|
99
|
-
|
|
101
|
+
### DDL 헬퍼
|
|
100
102
|
|
|
101
|
-
`
|
|
103
|
+
- `switchFk(enabled)` — 이 테이블의 FK 제약 활성/비활성 토글. `enabled` true=활성, false=비활성. 대량 적재 전 FK 일시 해제용. Table/View 기반에서만.
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
- `executable(db, builder)` — `() => Executable` 팩토리.
|
|
105
|
+
## queryable / getMatchedPrimaryKeys (factory)
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
- `queryable(db, tableOrView, as?)` — Table/View 별 Queryable 팩토리 함수 생성. `DbContext` 내부에서 멤버 등록에 쓰임(보통 `this.queryable(...)` 로 호출). `as` 미지정 시 호출마다 새 alias.
|
|
108
|
+
- `getMatchedPrimaryKeys(fkCols, targetTable)` — FK 컬럼 배열과 대상 테이블 PK 를 매칭해 PK 컬럼명 배열 반환. 개수 불일치 시 throw. include/조인 조건 생성 내부에서 사용.
|
|
107
109
|
|
|
108
|
-
|
|
109
|
-
class Executable<TParams, TReturns> {
|
|
110
|
-
getExecProcQueryDef(params?): ExecProcQueryDef;
|
|
111
|
-
execute(params): Promise<InferColumnExprs<TReturns>[][]>;
|
|
112
|
-
}
|
|
113
|
-
```
|
|
110
|
+
타입 export: `QueryableRecord<TData>`(컬럼 프록시 타입), `QueryableWriteRecord<TData>`(쓰기용 `ExprInput` 레코드), `UnwrapQueryableRecord<R>`(select 결과 역추론), `PathProxy<T>`(include 경로 프록시).
|
|
114
111
|
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
## Executable<TParams, TReturns> / executable
|
|
113
|
+
|
|
114
|
+
저장 프로시저 실행 래퍼. `db.getUserById()` 로 인스턴스를 얻어 실행한다.
|
|
115
|
+
|
|
116
|
+
- `execute(params)` — 프로시저 실행. `params`=`InferColumnExprs<TParams>`(`ExprInput` 또는 리터럴). 결과는 `TReturns[][]`(다중 결과셋). 정의에 파라미터가 없는데 전달하면 throw.
|
|
117
|
+
- `getExecProcQueryDef(params?)` — 실행 없이 `ExecProcQueryDef` 만 산출.
|
|
118
|
+
- `executable(db, builder)` — `Executable` 팩토리 생성(`DbContext` 멤버 등록용, 보통 `this.executable(...)`).
|
|
117
119
|
|
|
118
120
|
```typescript
|
|
119
121
|
const [rows] = await db.getUserById().execute({ userId: 1n });
|
|
@@ -121,34 +123,20 @@ const [rows] = await db.getUserById().execute({ userId: 1n });
|
|
|
121
123
|
|
|
122
124
|
## parseSearchQuery / ParsedSearchQuery
|
|
123
125
|
|
|
124
|
-
`search()` 가
|
|
126
|
+
`search()` 가 내부적으로 쓰는 검색 구문 파서. 검색 문자열을 SQL LIKE 패턴으로 변환한다. 커스텀 검색 UI 를 직접 만들 때 외부에서도 호출 가능.
|
|
125
127
|
|
|
126
128
|
```typescript
|
|
127
129
|
function parseSearchQuery(searchText: string): ParsedSearchQuery;
|
|
128
130
|
interface ParsedSearchQuery { or: string[]; must: string[]; not: string[]; }
|
|
129
131
|
```
|
|
130
132
|
|
|
131
|
-
- `or
|
|
132
|
-
- `must
|
|
133
|
-
- `not
|
|
134
|
-
- 와일드카드 `*` → `%`(`app*`→`app%` 시작 일치). 이스케이프: `\\` `\*` `\%` `\"` `\+` `\-` 는 리터럴. 닫히지 않은 `"` 는 일반 텍스트로 처리.
|
|
133
|
+
- `or`: string[] — 일반 검색어(OR 조건) LIKE 패턴. 공백으로 구분된 토큰.
|
|
134
|
+
- `must`: string[] — 필수 포함(AND 조건) LIKE 패턴. `+term` 또는 `"구문"`(따옴표 = 정확 일치 = 필수).
|
|
135
|
+
- `not`: string[] — 제외(NOT 조건) LIKE 패턴. `-term`.
|
|
135
136
|
|
|
136
|
-
|
|
137
|
-
parseSearchQuery('apple "delicious fruit" -banana +strawberry');
|
|
138
|
-
// { or: ["%apple%"], must: ["%delicious fruit%", "%strawberry%"], not: ["%banana%"] }
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
## getMatchedPrimaryKeys
|
|
137
|
+
구문 규칙: 와일드카드 없는 단어 `apple` → `%apple%`(부분 일치), `app*` → `app%`(시작 일치), `*apple` → `%apple`(끝 일치). 이스케이프 `\\` `\*` `\%` `\"` `\+` `\-` 는 리터럴 문자로 처리. 닫히지 않은 따옴표는 일반 텍스트로 취급.
|
|
142
138
|
|
|
143
139
|
```typescript
|
|
144
|
-
|
|
140
|
+
parseSearchQuery('apple "맛있는 과일" -banana +strawberry');
|
|
141
|
+
// { or: ["%apple%"], must: ["%맛있는 과일%", "%strawberry%"], not: ["%banana%"] }
|
|
145
142
|
```
|
|
146
|
-
|
|
147
|
-
- FK column 배열을 대상 테이블 PK 와 매칭해 PK column 이름 배열 반환. 개수 불일치 시 throw. `include()` 가 관계 조건을 만들 때 내부 사용 — 직접 호출은 드묾.
|
|
148
|
-
|
|
149
|
-
## 결과/입력 변환 타입
|
|
150
|
-
|
|
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,160 +1,80 @@
|
|
|
1
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
7
|
```typescript
|
|
8
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
9
|
```
|
|
26
10
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
- `database(db)` —
|
|
30
|
-
- `schema(schema)` —
|
|
31
|
-
- `
|
|
32
|
-
- `
|
|
33
|
-
- `
|
|
34
|
-
- `
|
|
35
|
-
-
|
|
36
|
-
|
|
11
|
+
`Table(name)` 으로 시작해 fluent 체이닝으로 정의한다. 각 메서드는 새 `TableBuilder` 를 반환.
|
|
12
|
+
|
|
13
|
+
- `database(db)` — 데이터베이스 이름 설정. dialect 네임스페이스 산출에 사용.
|
|
14
|
+
- `schema(schema)` — 스키마 이름 설정(MSSQL: dbo, PostgreSQL: public). MySQL 은 무시.
|
|
15
|
+
- `description(desc)` — 테이블 코멘트(DDL COMMENT). 문서화 목적.
|
|
16
|
+
- `columns((c) => ({...}))` — column 정의. `c` 는 column 팩토리(아래). 반환 객체의 키가 컬럼명.
|
|
17
|
+
- `primaryKey(...columns)` — PK 컬럼 지정(가변 인자). 둘 이상이면 복합 PK.
|
|
18
|
+
- `indexes((i) => [...])` — 인덱스 정의. `i` 는 index 팩토리.
|
|
19
|
+
- `relations((r) => ({...}))` — 관계(FK/역참조/논리관계) 정의. `r` 은 relation 팩토리.
|
|
20
|
+
|
|
21
|
+
타입 추론 필드(런타임 값 아님): `$inferSelect`(컬럼+관계), `$inferColumns`(컬럼만), `$inferInsert`(autoIncrement/nullable/default 는 optional), `$inferUpdate`(전부 optional). `Queryable` 의 결과/입력 타입이 여기서 파생된다.
|
|
37
22
|
|
|
38
23
|
```typescript
|
|
39
24
|
const User = Table("User")
|
|
40
25
|
.database("mydb")
|
|
41
26
|
.columns((c) => ({
|
|
42
|
-
id: c.
|
|
27
|
+
id: c.int().autoIncrement(),
|
|
43
28
|
name: c.varchar(100),
|
|
44
29
|
email: c.varchar(200).nullable(),
|
|
30
|
+
isActive: c.boolean().default(true),
|
|
31
|
+
companyId: c.int().nullable(),
|
|
45
32
|
}))
|
|
46
33
|
.primaryKey("id")
|
|
47
|
-
.indexes((i) => [i.index("email").unique()])
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
```
|
|
66
|
-
|
|
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 과 동일 의미.
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
const ActiveUsers = View("ActiveUsers")
|
|
74
|
-
.database("mydb")
|
|
75
|
-
.query((db: MainDb) =>
|
|
76
|
-
db.user().where((u) => [expr.eq(u.status, "active")]).select((u) => ({ id: u.id, name: u.name })),
|
|
77
|
-
);
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
## Procedure / ProcedureBuilder
|
|
81
|
-
|
|
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
|
-
```
|
|
97
|
-
|
|
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 과 동일.
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
const GetUserById = Procedure("GetUserById")
|
|
106
|
-
.database("mydb")
|
|
107
|
-
.params((c) => ({ userId: c.bigint() }))
|
|
108
|
-
.returns((c) => ({ id: c.bigint(), name: c.varchar(100) }))
|
|
109
|
-
.body("SELECT id, name FROM User WHERE id = userId");
|
|
34
|
+
.indexes((i) => [i.index("email").unique()])
|
|
35
|
+
.relations((r) => ({
|
|
36
|
+
company: r.foreignKey(["companyId"], () => Company),
|
|
37
|
+
posts: r.foreignKeyTarget(() => Post, "user"),
|
|
38
|
+
}));
|
|
110
39
|
```
|
|
111
40
|
|
|
112
|
-
## column
|
|
41
|
+
## column 팩토리 / ColumnBuilder
|
|
113
42
|
|
|
114
|
-
`columns
|
|
43
|
+
`columns((c) => ...)` 의 `c` 가 노출하는 타입 생성 메서드. 각자 `ColumnBuilder` 를 반환하고, 그 위에 수식 메서드를 체이닝한다.
|
|
115
44
|
|
|
116
45
|
타입 메서드:
|
|
117
46
|
|
|
118
|
-
- `int()` — INT(4바이트 정수).
|
|
119
|
-
- `bigint()` — BIGINT(8바이트 정수).
|
|
120
|
-
- `float()` —
|
|
121
|
-
- `double()` —
|
|
122
|
-
- `decimal(precision, scale?)` — 고정 소수점. `precision`=전체 자릿수, `scale`=소수 자릿수(선택).
|
|
47
|
+
- `int()` — INT(4바이트 정수). 일반 정수 PK/카운트.
|
|
48
|
+
- `bigint()` — BIGINT(8바이트 정수). 큰 범위 ID.
|
|
49
|
+
- `float()` — 단정밀도 부동소수점. 정밀도 덜 중요한 실수.
|
|
50
|
+
- `double()` — 배정밀도 부동소수점. 일반 실수 연산.
|
|
51
|
+
- `decimal(precision, scale?)` — 고정 소수점. `precision`=전체 자릿수, `scale`=소수 자릿수(선택). 금액처럼 반올림 오차가 곤란한 값.
|
|
123
52
|
- `varchar(length)` — 가변 길이 문자열. `length`=최대 길이.
|
|
124
|
-
- `char(length)` — 고정 길이 문자열.
|
|
125
|
-
- `text()` — 대용량
|
|
126
|
-
- `binary()` — 바이너리(MySQL LONGBLOB / MSSQL VARBINARY(MAX) / PostgreSQL BYTEA). 값
|
|
53
|
+
- `char(length)` — 고정 길이 문자열. 코드값처럼 길이가 일정한 값.
|
|
54
|
+
- `text()` — 대용량 텍스트(본문 등).
|
|
55
|
+
- `binary()` — 바이너리(MySQL LONGBLOB / MSSQL VARBINARY(MAX) / PostgreSQL BYTEA). 값 타입은 `Bytes`.
|
|
127
56
|
- `boolean()` — 불리언(MySQL TINYINT(1) / MSSQL BIT / PostgreSQL BOOLEAN).
|
|
128
57
|
- `datetime()` — 날짜+시간. 값 타입 `DateTime`.
|
|
129
58
|
- `date()` — 날짜만. 값 타입 `DateOnly`.
|
|
130
59
|
- `time()` — 시간만. 값 타입 `Time`.
|
|
131
60
|
- `uuid()` — UUID(MySQL BINARY(16) / MSSQL UNIQUEIDENTIFIER / PostgreSQL UUID). 값 타입 `Uuid`.
|
|
132
61
|
|
|
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).
|
|
62
|
+
수식 메서드(`ColumnBuilder`):
|
|
139
63
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
email: c.varchar(200).nullable(),
|
|
145
|
-
status: c.varchar(20).default("active"),
|
|
146
|
-
}))
|
|
147
|
-
```
|
|
64
|
+
- `autoIncrement()` — 자동 증가. INSERT 타입에서 optional 처리. 보통 정수 PK 에만.
|
|
65
|
+
- `nullable()` — NULL 허용. 값 타입에 `undefined` 추가, INSERT 타입에서 optional. 도메인상 값이 없을 수 있을 때만.
|
|
66
|
+
- `default(value)` — INSERT 시 미지정이면 사용할 기본값. INSERT 타입에서 optional. 사용자가 명시적으로 지시한 경우에만.
|
|
67
|
+
- `description(desc)` — 컬럼 코멘트(DDL COMMENT).
|
|
148
68
|
|
|
149
|
-
## index
|
|
69
|
+
## index 팩토리 / IndexBuilder
|
|
150
70
|
|
|
151
|
-
`indexes
|
|
71
|
+
`indexes((i) => [...])` 의 `i.index(...columns)` 로 시작. immutable 체이닝.
|
|
152
72
|
|
|
153
|
-
- `
|
|
154
|
-
-
|
|
155
|
-
-
|
|
156
|
-
-
|
|
157
|
-
-
|
|
73
|
+
- `index(...columns)` — 인덱스 대상 컬럼(가변 인자, 복합 인덱스 가능). `IndexBuilder` 반환.
|
|
74
|
+
- `name(name)` — 인덱스 이름 지정. 미지정 시 자동 명명.
|
|
75
|
+
- `unique()` — 유니크 인덱스로 설정. 중복 방지 제약이 필요할 때.
|
|
76
|
+
- `orderBy(...orderBy)` — 컬럼별 정렬 방향(`"ASC" | "DESC"`). 인자 수가 컬럼 수와 일치해야 함. 범위/정렬 조회 최적화용.
|
|
77
|
+
- `description(desc)` — 인덱스 코멘트.
|
|
158
78
|
|
|
159
79
|
```typescript
|
|
160
80
|
.indexes((i) => [
|
|
@@ -163,42 +83,85 @@ const GetUserById = Procedure("GetUserById")
|
|
|
163
83
|
])
|
|
164
84
|
```
|
|
165
85
|
|
|
166
|
-
## relation
|
|
86
|
+
## relation 팩토리 / 관계 빌더
|
|
167
87
|
|
|
168
|
-
`relations`
|
|
88
|
+
`relations((r) => ({...}))` 의 `r` 가 노출하는 관계 정의 메서드. Table 은 FK 계열 + RelationKey 계열 모두, View 는 RelationKey 계열만 사용 가능. 관계는 `include()`(queryable.md)로 자동 조인되며, 정의만 한다고 DB 쿼리가 나가지는 않는다. `description`/`single` 등 옵션은 메서드 체이닝이 아니라 마지막 `opts` 인자로 전달한다(순환 참조로 인한 TS7022 회피 목적).
|
|
169
89
|
|
|
170
|
-
- `
|
|
171
|
-
- `
|
|
172
|
-
- `
|
|
173
|
-
- `
|
|
90
|
+
- `foreignKey(columns, targetFn, opts?)` — N:1 FK. DB 에 실제 FK 제약 생성. `columns`=현재 테이블의 FK 컬럼 배열, `targetFn`=대상 테이블 지연 팩토리(`() => User`). `opts.description` 선택.
|
|
91
|
+
- `foreignKeyTarget(targetTableFn, relationName, opts?)` — 1:N 역참조. DB 객체는 안 만들고 `include` 시 배열로 로드. `relationName`=대상 테이블에 정의된 FK 관계 이름. `opts.single: true` 면 단일 객체(1:1)로 로드, `opts.description` 선택.
|
|
92
|
+
- `relationKey(columns, targetFn, opts?)` — N:1 논리 관계. `foreignKey` 와 동일하나 DB FK 제약을 생성하지 않음. View 에서도 사용 가능. 물리 FK 를 걸 수 없는 관계(뷰↔테이블 등)에 사용.
|
|
93
|
+
- `relationKeyTarget(targetTableFn, relationName, opts?)` — 1:N 논리 역참조. `foreignKeyTarget` 의 FK 미생성 버전. `opts.single`/`opts.description` 동일.
|
|
174
94
|
|
|
175
|
-
|
|
95
|
+
opts 공통 필드:
|
|
176
96
|
|
|
177
|
-
- `description
|
|
178
|
-
- `single
|
|
97
|
+
- `description`: string — 관계 코멘트.
|
|
98
|
+
- `single`: true — (target 계열만) 역참조를 배열이 아닌 단일 객체로 로드. 1:1 관계일 때.
|
|
179
99
|
|
|
180
100
|
```typescript
|
|
181
101
|
const Post = Table("Post")
|
|
182
|
-
.columns((c) => ({ id: c.
|
|
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) }))
|
|
102
|
+
.columns((c) => ({ id: c.int().autoIncrement(), userId: c.int(), title: c.varchar(300) }))
|
|
188
103
|
.primaryKey("id")
|
|
189
104
|
.relations((r) => ({
|
|
190
|
-
|
|
191
|
-
profile: r.foreignKeyTarget(() => Profile, "user", { single: true }),
|
|
105
|
+
user: r.foreignKey(["userId"], () => User, { description: "작성자" }),
|
|
192
106
|
}));
|
|
193
107
|
```
|
|
194
108
|
|
|
195
|
-
|
|
109
|
+
빌더 클래스: `ForeignKeyBuilder`, `ForeignKeyTargetBuilder`, `RelationKeyBuilder`, `RelationKeyTargetBuilder` 가 export 되며, `meta` 프로퍼티로 정의 내용을 노출한다(DDL 자동화·검증용). 보통 직접 `new` 하지 않고 위 팩토리로 생성한다.
|
|
110
|
+
|
|
111
|
+
## View / ViewBuilder
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
function View(name: string): ViewBuilder<...>;
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
쿼리 결과를 가상 테이블로 정의한다. `query` 콜백 안에서 `DbContext` 를 받아 `Queryable` 을 반환하면 그것이 뷰 본문이 된다.
|
|
118
|
+
|
|
119
|
+
- `database(db)` / `schema(schema)` / `description(desc)` — Table 과 동일.
|
|
120
|
+
- `query((db) => db.x().select(...))` — 뷰 본문 SELECT 정의. `db` 는 `DbContext`, 반환은 `Queryable`. select 결과 컬럼이 뷰 컬럼이 됨.
|
|
121
|
+
- `relations((r) => ({...}))` — 논리 관계(RelationKey 계열)만 정의 가능.
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
const ActiveUsers = View("ActiveUsers")
|
|
125
|
+
.database("mydb")
|
|
126
|
+
.query((db: AppDb) =>
|
|
127
|
+
db.user().where((u) => [expr.eq(u.isActive, true)]).select((u) => ({ id: u.id, name: u.name })),
|
|
128
|
+
);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Procedure / ProcedureBuilder
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
function Procedure(name: string): ProcedureBuilder<never, never>;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
저장 프로시저를 정의한다. `executable()` 로 등록 후 `Executable.execute(params)`(queryable.md)로 호출.
|
|
138
|
+
|
|
139
|
+
- `database(db)` / `schema(schema)` / `description(desc)` — 동일.
|
|
140
|
+
- `params((c) => ({...}))` — 입력 파라미터 정의. `c` 는 column 팩토리. 키가 파라미터명.
|
|
141
|
+
- `returns((c) => ({...}))` — 반환 결과 컬럼 정의.
|
|
142
|
+
- `body(sql)` — 프로시저 본문 SQL. dialect 별 파라미터 구문 차이 주의(MySQL/PostgreSQL: `userId`, MSSQL: `@userId`).
|
|
143
|
+
|
|
144
|
+
타입 추론 필드: `$params`, `$returns`(`Executable` 의 입력/출력 타입 파생).
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const GetUserById = Procedure("GetUserById")
|
|
148
|
+
.database("mydb")
|
|
149
|
+
.params((c) => ({ userId: c.bigint() }))
|
|
150
|
+
.returns((c) => ({ id: c.bigint(), name: c.varchar(100) }))
|
|
151
|
+
.body("SELECT id, name FROM User WHERE id = userId");
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 타입 추론 유틸 / 기타 export
|
|
155
|
+
|
|
156
|
+
column-builder 가 함께 export 하는 타입(주로 빌더 내부·executor·고급 타입 작업용):
|
|
196
157
|
|
|
197
|
-
|
|
158
|
+
- `ColumnBuilderRecord` — `Record<string, ColumnBuilder<...>>`. `columns()` 반환 타입.
|
|
159
|
+
- `InferColumns<T>` — column 빌더 레코드에서 실제 값 타입 추론.
|
|
160
|
+
- `InferColumnExprs<T>` — 각 컬럼을 `ExprInput<V>` 로 추론(프로시저 파라미터 타입 등).
|
|
161
|
+
- `InferInsertColumns<T>` / `InferUpdateColumns<T>` — INSERT(필수/optional 분리)·UPDATE(전부 optional) 타입.
|
|
162
|
+
- `RequiredInsertKeys<T>` / `OptionalInsertKeys<T>` — INSERT 필수/optional 키 집합.
|
|
163
|
+
- `DataToColumnBuilderRecord<TData>` — 데이터 레코드를 column 빌더 레코드로 역변환(`insertInto` 대상 테이블 제약에 사용).
|
|
164
|
+
- `RelationBuilderRecord` — 관계 빌더 레코드 타입.
|
|
165
|
+
- `InferDeepRelations<TRelations>` / `ExtractRelationTarget<T>` / `ExtractRelationTargetResult<T>` — 관계를 통한 심층 타입 추론(관계는 `include` 전이라 모두 optional 로 추론).
|
|
198
166
|
|
|
199
|
-
|
|
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` 의 관계 부분.
|
|
167
|
+
`Table`/`View`/`Procedure` 빌더와 `_Migration`(시스템 마이그레이션 테이블 정의)도 함께 노출된다.
|