@simplysm/sd-claude 14.0.87 → 14.0.89

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 (127) hide show
  1. package/claude/references/sd-simplysm14/README.md +17 -18
  2. package/claude/references/sd-simplysm14/apis/angular/README.md +35 -0
  3. package/claude/references/sd-simplysm14/apis/angular/controls.md +51 -0
  4. package/claude/references/sd-simplysm14/apis/angular/crud.md +53 -0
  5. package/claude/references/sd-simplysm14/apis/angular/directives.md +34 -0
  6. package/claude/references/sd-simplysm14/apis/angular/features.md +40 -0
  7. package/claude/references/sd-simplysm14/apis/angular/infra.md +74 -0
  8. package/claude/references/sd-simplysm14/apis/angular/layout.md +27 -0
  9. package/claude/references/sd-simplysm14/apis/angular/overlay.md +103 -0
  10. package/claude/references/sd-simplysm14/apis/angular/routing-appstructure.md +69 -0
  11. package/claude/references/sd-simplysm14/apis/angular/selection-managers.md +28 -0
  12. package/claude/references/sd-simplysm14/apis/angular/shared-data.md +57 -0
  13. package/claude/references/sd-simplysm14/apis/angular/sheet.md +73 -0
  14. package/claude/references/sd-simplysm14/apis/capacitor-plugin-auto-update/README.md +78 -0
  15. package/claude/references/sd-simplysm14/apis/capacitor-plugin-file-system/README.md +66 -0
  16. package/claude/references/sd-simplysm14/apis/capacitor-plugin-intent/README.md +71 -0
  17. package/claude/references/sd-simplysm14/apis/capacitor-plugin-usb-storage/README.md +62 -0
  18. package/claude/references/sd-simplysm14/apis/core-browser/README.md +70 -0
  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 +80 -0
  21. package/claude/references/sd-simplysm14/apis/core-common/README.md +262 -0
  22. package/claude/references/sd-simplysm14/apis/core-common/array-ext.md +121 -0
  23. package/claude/references/sd-simplysm14/apis/core-common/async-runtime.md +128 -0
  24. package/claude/references/sd-simplysm14/apis/core-common/datetime.md +129 -0
  25. package/claude/references/sd-simplysm14/apis/core-common/errors.md +91 -0
  26. package/claude/references/sd-simplysm14/apis/core-common/json-transfer.md +53 -0
  27. package/claude/references/sd-simplysm14/apis/core-common/obj.md +117 -0
  28. package/claude/references/sd-simplysm14/apis/core-node/README.md +17 -0
  29. package/claude/references/sd-simplysm14/apis/core-node/consola.md +43 -0
  30. package/claude/references/sd-simplysm14/apis/core-node/cpx.md +50 -0
  31. package/claude/references/sd-simplysm14/apis/core-node/fs-watcher.md +41 -0
  32. package/claude/references/sd-simplysm14/apis/core-node/fsx.md +72 -0
  33. package/claude/references/sd-simplysm14/apis/core-node/pathx.md +39 -0
  34. package/claude/references/sd-simplysm14/apis/core-node/worker.md +52 -0
  35. package/claude/references/sd-simplysm14/apis/excel/README.md +43 -0
  36. package/claude/references/sd-simplysm14/apis/excel/cell.md +54 -0
  37. package/claude/references/sd-simplysm14/apis/excel/conditional-format.md +51 -0
  38. package/claude/references/sd-simplysm14/apis/excel/style.md +67 -0
  39. package/claude/references/sd-simplysm14/apis/excel/utils.md +35 -0
  40. package/claude/references/sd-simplysm14/apis/excel/workbook-worksheet.md +97 -0
  41. package/claude/references/sd-simplysm14/apis/excel/wrapper.md +83 -0
  42. package/claude/references/sd-simplysm14/apis/lint/README.md +49 -0
  43. package/claude/references/sd-simplysm14/apis/lint/rules.md +130 -0
  44. package/claude/references/sd-simplysm14/apis/orm-common/README.md +13 -0
  45. package/claude/references/sd-simplysm14/apis/orm-common/db-context.md +111 -0
  46. package/claude/references/sd-simplysm14/apis/orm-common/expr.md +128 -0
  47. package/claude/references/sd-simplysm14/apis/orm-common/queryable.md +145 -0
  48. package/claude/references/sd-simplysm14/apis/orm-common/schema.md +147 -0
  49. package/claude/references/sd-simplysm14/apis/orm-common/types.md +62 -0
  50. package/claude/references/sd-simplysm14/apis/orm-node/README.md +90 -0
  51. package/claude/references/sd-simplysm14/apis/orm-node/db-conn.md +94 -0
  52. package/claude/references/sd-simplysm14/apis/sd-cli/README.md +26 -0
  53. package/claude/references/sd-simplysm14/apis/sd-cli/SdTsCompiler.md +117 -0
  54. package/claude/references/sd-simplysm14/apis/sd-cli/sd-config-types.md +291 -0
  55. package/claude/references/sd-simplysm14/apis/service-client/README.md +150 -0
  56. package/claude/references/sd-simplysm14/apis/service-client/orm.md +48 -0
  57. package/claude/references/sd-simplysm14/apis/service-client/transport.md +59 -0
  58. package/claude/references/sd-simplysm14/apis/service-common/README.md +84 -0
  59. package/claude/references/sd-simplysm14/apis/service-common/app-structure.md +48 -0
  60. package/claude/references/sd-simplysm14/apis/service-common/protocol.md +72 -0
  61. package/claude/references/sd-simplysm14/apis/service-server/README.md +118 -0
  62. package/claude/references/sd-simplysm14/apis/service-server/service-authoring.md +71 -0
  63. package/claude/references/sd-simplysm14/apis/service-server/transport-internals.md +62 -0
  64. package/claude/references/sd-simplysm14/apis/service-server/v1-legacy.md +39 -0
  65. package/claude/references/sd-simplysm14/apis/storage/README.md +120 -0
  66. package/claude/skills/sd-demo/SKILL.md +6 -0
  67. package/claude/skills/sd-impl/SKILL.md +4 -7
  68. package/claude/skills/sd-spec/SKILL.md +31 -858
  69. package/claude/skills/sd-spec/references/spec-authoring.md +519 -0
  70. package/claude/workflows/sd-docs.js +84 -0
  71. package/claude/{skills/sd-docs/references/subagent-prompt.md → workflows/sd-docs.rules.md} +25 -40
  72. package/package.json +1 -1
  73. package/claude/skills/sd-demo/evals/fixtures/inventory-list/.specs/inventory/spec.md +0 -99
  74. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/package.json +0 -12
  75. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/index.ts +0 -3
  76. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inbound/inbound.list.ts +0 -150
  77. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/inventory/inventory-master.list.ts +0 -143
  78. package/claude/skills/sd-demo/evals/fixtures/inventory-list/packages/demo-client/src/screens/outbound/outbound.list.ts +0 -150
  79. package/claude/skills/sd-demo/evals/fixtures/inventory-list/pnpm-workspace.yaml +0 -2
  80. package/claude/skills/sd-demo/evals/fixtures/inventory-list/sd.config.ts +0 -12
  81. package/claude/skills/sd-demo/evals/golden.jsonl +0 -1
  82. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/package.json +0 -8
  83. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/src/.gitkeep +0 -0
  84. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tests/.gitkeep +0 -0
  85. package/claude/skills/sd-dev/evals/fixtures/minimal-ts-pkg/tsconfig.json +0 -10
  86. package/claude/skills/sd-dev/evals/golden.jsonl +0 -1
  87. package/claude/skills/sd-docs/SKILL.md +0 -58
  88. package/claude/skills/sd-docs/evals/fixtures/new-write/.claude/references/sd-simplysm14/README.md +0 -7
  89. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/package.json +0 -5
  90. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/bar/src/index.ts +0 -3
  91. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/package.json +0 -6
  92. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/baz/src/index.ts +0 -1
  93. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/package.json +0 -5
  94. package/claude/skills/sd-docs/evals/fixtures/new-write/packages/foo/src/index.ts +0 -8
  95. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/README.md +0 -7
  96. package/claude/skills/sd-docs/evals/fixtures/update-mixed/.claude/references/sd-simplysm14/apis/foo/README.md +0 -3
  97. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/package.json +0 -5
  98. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/bar/src/index.ts +0 -3
  99. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/package.json +0 -6
  100. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/baz/src/index.ts +0 -1
  101. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/package.json +0 -5
  102. package/claude/skills/sd-docs/evals/fixtures/update-mixed/packages/foo/src/index.ts +0 -8
  103. package/claude/skills/sd-docs/evals/golden.jsonl +0 -2
  104. package/claude/skills/sd-impl/evals/fixtures/case-a-new-screen/.specs/260513120000_warehouse/spec.md +0 -101
  105. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/.specs/260513120000_warehouse/spec.md +0 -101
  106. package/claude/skills/sd-impl/evals/fixtures/case-b-update-with-demo/packages/app/src/screens/box-register/box-register.view.ts +0 -46
  107. package/claude/skills/sd-impl/evals/fixtures/case-c-new-cross/.specs/260513120000_warehouse/spec.md +0 -89
  108. package/claude/skills/sd-impl/evals/fixtures/case-d-spec-modify/.specs/260513120000_warehouse/spec.md +0 -101
  109. package/claude/skills/sd-impl/evals/golden.jsonl +0 -4
  110. package/claude/skills/sd-manual/evals/fixtures/new-manual/src/notification.ts +0 -25
  111. package/claude/skills/sd-manual/evals/fixtures/update-manual/.claude/references/sd-simplysm14/manuals/notification.md +0 -14
  112. package/claude/skills/sd-manual/evals/fixtures/update-manual/src/notification.ts +0 -37
  113. package/claude/skills/sd-manual/evals/golden.jsonl +0 -2
  114. package/claude/skills/sd-review/evals/fixtures/code-review/src/foo.ts +0 -7
  115. package/claude/skills/sd-review/evals/fixtures/doc-review/docs/foo.md +0 -4
  116. package/claude/skills/sd-review/evals/golden.jsonl +0 -2
  117. package/claude/skills/sd-skill/evals/fixtures/existing-skill/.claude/skills/todo-format/SKILL.md +0 -14
  118. package/claude/skills/sd-skill/evals/fixtures/new-skill/.gitkeep +0 -0
  119. package/claude/skills/sd-skill/evals/golden.jsonl +0 -2
  120. package/claude/skills/sd-spec/evals/fixtures/case-a-split//355/232/214/354/235/230/353/241/235.md +0 -20
  121. package/claude/skills/sd-spec/evals/fixtures/case-b-detail/.specs/260513120000_warehouse/spec.md +0 -95
  122. package/claude/skills/sd-spec/evals/golden.jsonl +0 -2
  123. package/claude/skills/sd-unpack/evals/fixtures/eml-with-text-attachment/meeting.eml +0 -21
  124. package/claude/skills/sd-unpack/evals/fixtures/simple-eml/meeting.eml +0 -10
  125. package/claude/skills/sd-unpack/evals/golden.jsonl +0 -2
  126. package/claude/skills/sd-use/evals/fixtures/empty/.gitkeep +0 -0
  127. package/claude/skills/sd-use/evals/golden.jsonl +0 -6
@@ -0,0 +1,147 @@
1
+ # @simplysm/orm-common — 스키마 정의
2
+
3
+ DB 객체(Table/View/Procedure)와 그 구성요소(Column/Index/관계)를 fluent 빌더로 선언하는 묶음. 모든 빌더는 immutable — 각 메서드가 새 인스턴스를 반환한다. 정의한 빌더는 `DbContext` 안에서 `this.queryable()`/`this.executable()` 로 등록한다.
4
+
5
+ ## Table / TableBuilder
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() 자동 조인의 근거.
17
+
18
+ 추론 전용 가상 프로퍼티(런타임 값 없음, 타입만): `$inferSelect`(컬럼+관계, 관계는 optional), `$inferColumns`(컬럼만), `$inferInsert`(autoIncrement/nullable/default 제외 시 필수), `$inferUpdate`(전부 optional).
19
+
20
+ ```typescript
21
+ const Post = Table("Post")
22
+ .database("mydb")
23
+ .columns((c) => ({
24
+ id: c.bigint().autoIncrement(),
25
+ authorId: c.bigint(),
26
+ title: c.varchar(200),
27
+ content: c.text().nullable(),
28
+ }))
29
+ .primaryKey("id")
30
+ .indexes((i) => [i.index("authorId")])
31
+ .relations((r) => ({ author: r.foreignKey(["authorId"], () => User) }));
32
+ ```
33
+
34
+ ## View / ViewBuilder
35
+
36
+ `View(name)` → `ViewBuilder`. `query()` 로 SELECT Queryable 을 데이터 소스로 지정. View 는 컬럼 추론을 query 결과 타입(`TData`)에서 얻고, FK 는 등록하지 않으므로 관계는 relationKey 계열만 쓴다.
37
+
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`).
42
+
43
+ ```typescript
44
+ const ActiveUsers = View("ActiveUsers")
45
+ .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 })),
50
+ );
51
+ ```
52
+
53
+ ## Procedure / ProcedureBuilder
54
+
55
+ `Procedure(name)` → `ProcedureBuilder`. 파라미터·반환 컬럼·본문 SQL 을 정의. `DbContext.executable()` 로 등록해 `Executable` 로 실행한다.
56
+
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`) 주의.
62
+
63
+ ```typescript
64
+ const GetUserById = Procedure("GetUserById")
65
+ .database("mydb")
66
+ .params((c) => ({ userId: c.bigint() }))
67
+ .returns((c) => ({ id: c.bigint(), name: c.varchar(100) }))
68
+ .body("SELECT id, name FROM User WHERE id = userId");
69
+ ```
70
+
71
+ ## ColumnBuilder / createColumnFactory
72
+
73
+ `createColumnFactory()` 가 타입별 컬럼 생성 메서드를 반환하고, 각 메서드의 결과 `ColumnBuilder` 에 수식자를 체이닝한다.
74
+
75
+ 타입 메서드(전부 인자 없음, decimal/varchar/char 제외):
76
+
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`=소수 자릿수. 금액 등 정확값.
82
+ - `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)` — 컬럼 코멘트.
98
+
99
+ ```typescript
100
+ .columns((c) => ({
101
+ id: c.bigint().autoIncrement(),
102
+ email: c.varchar(200).nullable(),
103
+ status: c.varchar(20).default("active"),
104
+ }))
105
+ ```
106
+
107
+ 추론 유틸 타입: `ColumnBuilderRecord`(컬럼 레코드), `InferColumns`(값 타입), `InferColumnExprs`(ExprInput 타입), `InferInsertColumns`/`InferUpdateColumns`/`RequiredInsertKeys`/`OptionalInsertKeys`(INSERT/UPDATE 추론), `DataToColumnBuilderRecord`(데이터→컬럼 변환, `insertInto` 대상 검증용).
108
+
109
+ ## IndexBuilder / createIndexFactory
110
+
111
+ `createIndexFactory<TColumnKey>()` → `{ index(...columns) }`. 반환 `IndexBuilder` 에 수식자 체이닝.
112
+
113
+ - `index(...columns): IndexBuilder` — 인덱스 컬럼 지정(복합 가능).
114
+ - `name(name)` — 인덱스 이름. 미지정 시 DDL 생성기가 자동 작명.
115
+ - `unique()` — 유니크 인덱스. 중복 방지 제약.
116
+ - `orderBy(...orderBy)` — 컬럼별 정렬 방향. 각 값 `"ASC"|"DESC"`, 컬럼 수와 개수 일치 필요. 정렬 최적화 인덱스에.
117
+ - `description(description)` — 인덱스 코멘트.
118
+
119
+ ```typescript
120
+ .indexes((i) => [
121
+ i.index("email").unique(),
122
+ i.index("status", "createdAt").orderBy("ASC", "DESC"),
123
+ ])
124
+ ```
125
+
126
+ ## 관계 빌더 (foreignKey / foreignKeyTarget / relationKey / relationKeyTarget)
127
+
128
+ `createRelationFactory(ownerFn)` 가 owner 종류에 따라 다른 factory 를 반환: Table 이면 FK 4종 전부, View 이면 relationKey 계열 2종만. `relations()` 콜백의 `r` 가 이 factory. 대상 테이블은 순환 참조 방지를 위해 `() => Table` 지연 함수로 전달.
129
+
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`.
134
+
135
+ ```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
+ }))
143
+ ```
144
+
145
+ 빌더 클래스(메타만 보관, 메서드 없음): `ForeignKeyBuilder`/`ForeignKeyTargetBuilder`/`RelationKeyBuilder`/`RelationKeyTargetBuilder`. 추론 타입: `RelationBuilderRecord`, `InferDeepRelations`(관계를 optional 로, 같은 테이블 재방문 시 순환 절단), `ExtractRelationTarget`/`ExtractRelationTargetResult`.
146
+
147
+ > 주의: description/single 은 반드시 factory `opts` 인자로 전달한다. `.description()`·`.single()` 메서드 체이닝은 TS 순환 참조(TS7022) 때문에 제거되어 존재하지 않는다.
@@ -0,0 +1,62 @@
1
+ # @simplysm/orm-common — 타입·실행 엔진 내부
2
+
3
+ executor·어댑터를 구현하거나 QueryDef/Expr AST·결과 메타를 직접 다루고, QueryDef 를 dialect SQL 로 렌더링하거나 raw 결과를 TS 객체로 환원할 때 참조하는 묶음. 일반 쿼리 작성에서는 expr/Queryable 가 이 타입들을 가려주므로 직접 쓸 일이 적다.
4
+
5
+ ## Column 타입
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?`.
15
+
16
+ ## Database 타입
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` 입력.
24
+
25
+ ## Executor / Migration 인터페이스
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) 참조).
29
+
30
+ ## QueryDef AST
31
+
32
+ `type QueryDef` 는 DML+DDL+Utils+Meta 의 union. executor 의 `executeDefs` 가 받고 QueryBuilder 가 SQL 로 변환.
33
+
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.
39
+
40
+ ## Expr AST
41
+
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` 디스크리미네이터 + 피연산자 필드를 가진 평범한 데이터 객체.
47
+
48
+ ## dialect QueryBuilder (SQL 렌더러)
49
+
50
+ QueryDef → SQL 문자열 변환. executor 구현체가 사용.
51
+
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·표현식 변환에 사용.
56
+
57
+ ## 결과 파싱 유틸
58
+
59
+ executor 가 DB raw 결과를 TS 객체로 환원할 때 사용. 일반 사용자는 직접 호출하지 않음.
60
+
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 결과만 모을 때).
@@ -0,0 +1,90 @@
1
+ # @simplysm/orm-node
2
+
3
+ Node.js 환경에서 `@simplysm/orm-common` 의 `DbContext` 를 실제 DB(MySQL/MSSQL/PostgreSQL)에 연결·실행하는 ORM 런타임. 고수준 진입점(`createOrm`)과 저수준 연결(`createDbConn`/`DbConn`)을 함께 제공.
4
+
5
+ ## 사용 트리거 인덱스
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)
10
+
11
+ ## ORM 진입
12
+
13
+ `DbContext` 서브클래스와 연결 설정을 받아 트랜잭션 경계를 관리하는 고수준 진입. query DSL 자체는 `@simplysm/orm-common` 의 `DbContext` 가 제공하고, 이 군은 그 컨텍스트를 실제 연결에 묶는 역할.
14
+
15
+ ### createOrm
16
+
17
+ ```typescript
18
+ function createOrm<T extends DbContext>(
19
+ DbClass: new (executor: DbContextExecutor, opt: { database: string; schema?: string }) => T,
20
+ config: DbConnConfig,
21
+ options?: OrmOptions,
22
+ ): Orm<T>
23
+ ```
24
+
25
+ `DbContext` 서브클래스를 받아 `Orm<T>` 를 반환. 인스턴스는 메서드 호출마다 새로 만들어지므로 `Orm` 객체는 재사용 가능.
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·스키마만 바꿔 쓸 때.
30
+
31
+ database 해석: `options.database` → `config.database` 순으로 찾고, 둘 다 없거나 빈 문자열이면 `"database는 필수입니다"` throw. schema 도 `options.schema` → `config.schema` 순.
32
+
33
+ ```typescript
34
+ class MyDb extends DbContext {
35
+ user = this.queryable(User);
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()); // 트랜잭션 안
39
+ ```
40
+
41
+ ### Orm
42
+
43
+ ```typescript
44
+ interface Orm<T extends DbContext> {
45
+ readonly DbClass; readonly config: DbConnConfig; readonly options?: OrmOptions;
46
+ connect<R>(callback: (conn: T) => Promise<R>, isolationLevel?: IsolationLevel): Promise<R>;
47
+ connectWithoutTransaction<R>(callback: (conn: T) => Promise<R>): Promise<R>;
48
+ }
49
+ ```
50
+
51
+ `createOrm` 반환 타입. 매 호출마다 DB 인스턴스를 새로 만들어 연결→콜백→정리.
52
+
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` 의 결과가 됨.
57
+
58
+ ### OrmOptions
59
+
60
+ ```typescript
61
+ interface OrmOptions { database?: string; schema?: string; }
62
+ ```
63
+
64
+ `createOrm` 3번째 인자. config 보다 우선 적용.
65
+
66
+ - database?: 사용할 DB 이름. config 의 `database` 대신 쓸 때. 다중 테넌트처럼 접속 정보는 같고 DB 만 다를 때.
67
+ - schema?: 스키마 이름(예: MSSQL `dbo`, PostgreSQL `public`). MySQL 은 스키마 개념이 없어 보통 미지정.
68
+
69
+ ### NodeDbContextExecutor
70
+
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 별 개별 실행.
@@ -0,0 +1,94 @@
1
+ # @simplysm/orm-node — 저수준 DB 연결
2
+
3
+ `createOrm` 추상화를 거치지 않고 raw SQL·파라미터 쿼리·bulk insert·수동 트랜잭션을 직접 다루거나, dialect별 접속 설정 타입을 작성하거나, `DbContext` 의 executor 를 직접 구성할 때 같이 읽히는 묶음. 연결 인스턴스 생성·연결 인터페이스·접속 설정·executor·dialect 헬퍼·상수로 구성.
4
+
5
+ ## createDbConn
6
+
7
+ ```typescript
8
+ function createDbConn(config: DbConnConfig): Promise<DbConn>
9
+ ```
10
+
11
+ `config.dialect` 에 맞는 드라이버를 **지연 import** 해 연결 인스턴스를 만든다(mysql → `mysql2/promise`, postgresql → `pg` + `pg-copy-streams`, mssql/mssql-azure → `tedious`). 한 번 로드한 드라이버는 모듈 캐시에 보관해 재사용. 반환 객체는 아직 **미연결** 상태이므로 `connect()` 를 별도 호출해야 함.
12
+
13
+ ```typescript
14
+ const conn = await createDbConn({ dialect: "postgresql", host: "localhost", username: "u", password: "p", database: "db" });
15
+ await conn.connect();
16
+ ```
17
+
18
+ ## DbConn
19
+
20
+ 저수준 연결 인터페이스. `EventEmitter<{ close: void }>` 를 상속하며 연결 종료 시 `close` 이벤트를 발생시킨다. 구현체 `MssqlDbConn`/`MysqlDbConn`/`PostgresqlDbConn` 는 직접 export 되지 않고 `createDbConn` 으로만 획득(타입 import 만 가능).
21
+
22
+ - `config: DbConnConfig` — 이 연결의 접속 설정(읽기 전용).
23
+ - `isConnected: boolean` — 현재 연결 여부. `connect` 성공 시 `true`, `close`/종료(`end`) 이벤트 시 `false`. 상태 확인·재연결 판단에 사용.
24
+ - `isInTransaction: boolean` — 트랜잭션 진행 여부. `beginTransaction` 후 `true`, 커밋·롤백 후 `false`.
25
+ - `connect(): Promise<void>` — 연결 수립. 이미 연결돼 있으면 `DB_CONN_ERRORS.ALREADY_CONNECTED` throw.
26
+ - `close(): Promise<void>` — 연결 종료. 미연결 상태면 아무 동작 없이 반환(throw 안 함).
27
+ - `beginTransaction(isolationLevel?: IsolationLevel): Promise<void>` — 트랜잭션 시작. `isolationLevel` 미지정 시 `config.defaultIsolationLevel`, 그것도 없으면 `READ_UNCOMMITTED` 로 시작. 테스트로 확인된 허용 값: `"READ_UNCOMMITTED"|"READ_COMMITTED"|"REPEATABLE_READ"|"SERIALIZABLE"`.
28
+ - `commitTransaction(): Promise<void>` — 커밋.
29
+ - `rollbackTransaction(): Promise<void>` — 롤백.
30
+ - `execute(queries: string[]): Promise<Record<string, unknown>[][]>` — 원시 SQL 문자열 배열을 순차 실행. 빈/공백 문자열은 건너뜀. 각 쿼리의 결과셋들을 평탄화해 하나의 배열로 묶어 반환. DDL·다건 SQL 일괄 실행에 사용.
31
+ - `executeParametrized(query: string, params?: unknown[]): Promise<Record<string, unknown>[][]>` — 파라미터 바인딩 쿼리 실행. SQL 인젝션 회피·값 재바인딩 시. 반환은 결과셋 배열(MySQL 멀티스테이트먼트는 statement별로 분리되며 INSERT/UPDATE/DELETE 자리는 빈 배열, PostgreSQL 은 단일 결과셋). `params` 미지정 시 MSSQL 은 배치(`execSqlBatch`) 경로로 실행.
32
+ - `bulkInsert(tableName: string, columnMetas: Record<string, ColumnMeta>, records: Record<string, unknown>[]): Promise<void>` — 네이티브 bulk API 로 대량 삽입. `tableName` 은 `database.table` 또는 `database.schema.table`(dialect별 인용 부호 포함된 완전 수식명). `columnMetas` 는 컬럼명 → `ColumnMeta`(`dataType`·`nullable`) 매핑이며 **키 순서가 컬럼 순서를 결정**, `records` 의 각 객체는 이 키들로 값 추출. `records` 가 빈 배열이면 아무 동작 없이 반환. dialect별 경로: MSSQL tedious `BulkLoad`, MySQL `LOAD DATA LOCAL INFILE`(임시 CSV 파일 생성·삭제, UUID/binary 는 `UNHEX`), PostgreSQL `COPY FROM STDIN`(CSV 스트림). `DateTime`/`DateOnly`/`Time`/`Uuid`/`Uint8Array` 값은 dialect별로 적절히 변환됨.
33
+
34
+ ```typescript
35
+ await conn.beginTransaction("READ_COMMITTED");
36
+ const [rows] = await conn.executeParametrized('SELECT * FROM "User" WHERE id = $1', [1]);
37
+ await conn.commitTransaction();
38
+ ```
39
+
40
+ ## DbConnConfig (dialect별 분기)
41
+
42
+ `MysqlDbConnConfig | MssqlDbConnConfig | PostgresqlDbConnConfig` 유니온. `dialect` 리터럴로 분기.
43
+
44
+ 공통 필드:
45
+
46
+ - `dialect` — `"mysql"` | `"mssql"` | `"mssql-azure"` | `"postgresql"`. DBMS 선택. `"mssql-azure"` 는 Azure SQL 용으로 드라이버는 mssql 과 동일하되 연결 시 `encrypt` 활성화. 쿼리 빌더용 dialect 로는 `getDialectFromConfig` 가 `"mssql"` 로 정규화.
47
+ - `host: string` — 접속 호스트.
48
+ - `port?: number` — 포트. 미지정 시 드라이버 기본값(PostgreSQL 은 미지정 시 5432 로 보정).
49
+ - `username: string` — 인증 사용자.
50
+ - `password: string` — 인증 비밀번호.
51
+ - `database?: string` — 접속 기본 DB 이름.
52
+ - `defaultIsolationLevel?: IsolationLevel` — `beginTransaction` 의 `isolationLevel` 미지정 시 적용할 기본 격리 수준.
53
+
54
+ dialect별 추가 필드:
55
+
56
+ - `MysqlDbConnConfig` — `dialect: "mysql"`. 공통 필드만(스키마 개념 없음).
57
+ - `MssqlDbConnConfig` — `dialect: "mssql" | "mssql-azure"`. `schema?: string`(예 `dbo`) 추가.
58
+ - `PostgresqlDbConnConfig` — `dialect: "postgresql"`. `schema?: string`(예 `public`) 추가.
59
+
60
+ ## getDialectFromConfig
61
+
62
+ ```typescript
63
+ function getDialectFromConfig(config: DbConnConfig): Dialect
64
+ ```
65
+
66
+ `config.dialect` 를 `@simplysm/orm-common` 의 `Dialect` 로 변환. `"mssql-azure"` → `"mssql"` 로 정규화하고 나머지는 그대로 반환. 쿼리 빌더의 dialect 결정에 사용. Azure 여부와 무관하게 같은 SQL 방언을 써야 할 때.
67
+
68
+ ## NodeDbContextExecutor
69
+
70
+ ```typescript
71
+ new NodeDbContextExecutor(config: DbConnConfig)
72
+ ```
73
+
74
+ `@simplysm/orm-common` 의 `DbContextExecutor` 를 Node 환경에서 구현한 클래스. `createOrm` 이 내부에서 자동 주입하므로 보통 직접 쓸 일은 없고, `DbContext` 를 `createOrm` 없이 수동 인스턴스화할 때만 사용. 생성자는 `DbConnConfig` 만 받고, 실제 연결은 `connect()` 시점에 `createDbConn` 으로 지연 생성한다.
75
+
76
+ - `connect(): Promise<void>` — `createDbConn(config)` 로 연결 생성 후 수립.
77
+ - `close(): Promise<void>` — 연결 종료 후 내부 참조 해제.
78
+ - `beginTransaction(isolationLevel?: IsolationLevel)` / `commitTransaction()` / `rollbackTransaction()` — 내부 `DbConn` 에 트랜잭션 위임.
79
+ - `executeParametrized(query: string, params?: unknown[]): Promise<Record<string, unknown>[][]>` — 파라미터 쿼리 위임.
80
+ - `bulkInsert(tableName: string, columnMetas: Record<string, ColumnMeta>, records: DataRecord[]): Promise<void>` — bulk insert 위임.
81
+ - `executeDefs<T>(defs: QueryDef[], resultMetas?: (ResultMeta | undefined)[]): Promise<T[][]>` — `QueryDef` 배열을 dialect 쿼리 빌더로 SQL 변환해 실행. `resultMetas` 가 전부 `null`/미지정이면(결과 불필요) 모든 def 를 하나의 SQL 로 합쳐 단일 요청으로 보내고 def 수만큼 빈 배열 반환(쓰기 전용 최적화). 그 외엔 def 마다 개별 실행 후, 해당 위치에 `resultMeta` 가 있으면 `parseQueryResult` 로 타입 변환해 반환, 없으면 raw 결과셋 그대로 반환.
82
+ - 모든 실행 메서드는 미연결 상태에서 호출 시 `SdError(DB_CONN_ERRORS.NOT_CONNECTED)` throw.
83
+
84
+ ## 상수
85
+
86
+ - `DB_CONN_CONNECT_TIMEOUT` — 연결 수립 타임아웃. `10 * 1000`(10초). 연결 드라이버의 connect 타임아웃으로 전달.
87
+ - `DB_CONN_DEFAULT_TIMEOUT` — 쿼리 기본 타임아웃. `10 * 60 * 1000`(10분). 마지막 활동 후 이 값의 2배가 지나면 연결을 자동 종료(idle 타임아웃)하는 데도 사용.
88
+ - `DB_CONN_ERRORS` — 오류 메시지 상수 객체(`as const`). `NOT_CONNECTED`(미연결 상태에서 실행 시), `ALREADY_CONNECTED`(이미 연결된 상태에서 재연결 시). `expect(...).rejects.toThrow(DB_CONN_ERRORS.NOT_CONNECTED)` 처럼 throw 비교·메시지 매칭에 사용.
89
+
90
+ ## 주의사항
91
+
92
+ - `createDbConn` 반환 객체는 미연결 상태 — 반드시 `connect()` 호출 후 사용.
93
+ - bulk insert 는 dialect별 네이티브 경로가 달라(MySQL 임시파일 + `LOCAL INFILE`, PostgreSQL `COPY`, MSSQL `BulkLoad`) 서버 측 권한·설정에 의존할 수 있음.
94
+ - 대부분의 작업은 `createOrm`(README)으로 충분. 이 계층은 raw SQL·executor 커스터마이징이 필요한 경우에만.
@@ -0,0 +1,26 @@
1
+ # @simplysm/sd-cli
2
+
3
+ simplysm 빌드/배포 오케스트레이터 CLI. entry 에서 공개되는 것은 CLI 실행 코드가 아니라 ① `sd.config.ts` 작성용 설정 타입, ② 내부 TypeScript/Angular 컴파일러(`SdTsCompiler`), ③ Vitest 전용 Angular Vite 플러그인(`sdAngularPlugin`) 세 가지뿐이다.
4
+
5
+ ## 사용 트리거 인덱스
6
+
7
+ - **sd.config.ts 설정 타입** — 프로젝트 루트 `sd.config.ts` 를 작성·수정하며 패키지 빌드 타겟·배포·Capacitor/Electron/PWA·서버 옵션을 지정할 때. 자세히: [sd-config-types.md](./sd-config-types.md)
8
+ - **SdTsCompiler / ISdTsCompilerOptions / ISdTsCompilerResult** — sd-cli 외부에서 패키지 단위 TS(또는 Angular AOT) 증분 컴파일을 직접 구동하거나 그 결과(emit·진단·lint·SCSS)를 다룰 때. 자세히: [SdTsCompiler.md](./SdTsCompiler.md)
9
+ - **sdAngularPlugin / SdAngularPluginOptions** — Vitest 의 Angular project 에서 `.ts` 를 AOT 컴파일해 주입하는 Vite 플러그인을 설정할 때. (인라인, 아래 참조)
10
+
11
+ ## sdAngularPlugin
12
+
13
+ ```ts
14
+ function sdAngularPlugin(options: SdAngularPluginOptions): Plugin // vite Plugin 반환
15
+ interface SdAngularPluginOptions { pkg: string }
16
+ ```
17
+
18
+ Angular AOT 컴파일을 수행하는 Vite 플러그인. **Vitest 전용**(`vitest.config.ts` 의 `angular` project plugins 에 등록). 내부적으로 `SdTsCompiler` 로 대상 패키지의 `.ts`(tests 포함, `includeTests: true`)를 AOT 컴파일하고, Vite `transform` 훅에서 컴파일된 JS 를 반환한다. `enforce: "pre"` 로 다른 transform 보다 먼저 동작하며, 컴파일러가 만든 인라인 base64 소스맵을 분리해 Vite 호환 형태(`{ code, map }`)로 넘긴다. `buildEnd` 마다 내부 컴파일러를 폐기해 다음 watch 재빌드 때 재생성하고, watch 변경 파일은 `buildStart` 의 캐시 무효화에 사용한다.
19
+
20
+ - pkg: string — 컴파일 대상 패키지 디렉토리명. `sd.config.ts` 의 `packages` 키와 동일(`@simplysm/` 접두사 제외한 짧은 이름, 예: `"angular"`). 플러그인은 `process.cwd()/packages/<pkg>` 를 컴파일 루트로 잡으므로, 테스트하려는 Angular 패키지명을 그대로 넣는다.
21
+
22
+ ```ts
23
+ // vitest.config.ts
24
+ import { sdAngularPlugin } from "@simplysm/sd-cli";
25
+ plugins: [sdAngularPlugin({ pkg: "angular" })]
26
+ ```
@@ -0,0 +1,117 @@
1
+ # @simplysm/sd-cli — SdTsCompiler
2
+
3
+ 패키지 디렉토리의 `.ts` 를 TypeScript 또는 Angular AOT 로 **증분** 컴파일하는 클래스. 한 번의 `compileAsync` 호출로 직렬화된 진단 + emit 결과 + lint + SCSS 결과를 한 묶음(`ISdTsCompilerResult`)으로 반환한다. tsconfig 의 `angularCompilerOptions` 존재 여부로 Angular/일반 모드를 자동 판별. 빌드 엔진과 `sdAngularPlugin` 내부에서 사용. 진단은 worker 경계를 통과하도록 `SerializedDiagnostic` 으로 직렬화되며, 내부 크래시는 단계별로 잡아 진단으로 보고(부분 복구)한다.
4
+
5
+ ## ISdTsCompilerOptions (생성자 인자)
6
+
7
+ ```typescript
8
+ interface ISdTsCompilerOptions {
9
+ pkgDir: string;
10
+ cwd: string;
11
+ output: { js: boolean; dts: boolean };
12
+ includeTests?: boolean;
13
+ env?: TypecheckEnv;
14
+ // Angular 전용 (isForAngular 시 활성)
15
+ sourceFileCache?: AngularSourceFileCache;
16
+ transformStylesheet?: (data: string, containingFile: string, stylesheetFile?: string) => Promise<string | null>;
17
+ externalStylesheets?: Map<string, string>;
18
+ compilerOptionsTransformer?: (options: ts.CompilerOptions) => ts.CompilerOptions;
19
+ // SCSS/lint 통합
20
+ lint?: boolean;
21
+ globalScss?: boolean;
22
+ }
23
+ ```
24
+
25
+ - pkgDir: string — 컴파일 대상 패키지 디렉토리. `<pkgDir>/tsconfig.json` 을 파싱하고 `<pkgDir>/dist` 로 emit, `<pkgDir>/.cache` 에 tsbuildinfo 를 둔다.
26
+ - cwd: string — 워크스페이스 루트. 진단 필터링(`isWorkspaceDiagnostic`)·경로 상대화·에러 포맷 기준점.
27
+ - output.js: boolean — JS emit 여부. true 면 `.js` 산출(non-Angular 은 import 에 `.js` 확장자 부착 + 경로 재작성, Angular 은 `emitResults` 로 반환).
28
+ - output.dts: boolean — `.d.ts` emit 여부. `js`/`dts` 조합에 따라 emit-only·declaration-only·noEmit(둘 다 false = 타입체크만) 으로 분기.
29
+ - includeTests?: boolean — `tests/` 파일을 rootNames 에 포함할지. 기본 false. 테스트까지 컴파일해야 하면 true(예: `sdAngularPlugin`).
30
+ - env?: TypecheckEnv — 타입체크 환경. 지정 시 `getCompilerOptionsForEnv()` 로 환경별 compilerOptions 를 적용하고 tsbuildinfo 파일명에 접미사를 붙임. 환경 분리 타입체크에 쓴다.
31
+ - sourceFileCache?: AngularSourceFileCache — Angular 증분용 SourceFile 캐시. 미제공 시 내부 생성. 여러 `compileAsync` 라운드 간 캐시를 공유하려면 외부에서 주입.
32
+ - transformStylesheet?: (data, containingFile, stylesheetFile?) => Promise<string|null> — 컴포넌트 스타일 변환 콜백(Angular only). `null` 반환 시 변환 안 함. 미제공이고 Angular 면 라이브러리용 SCSS 변환 콜백을 자동 생성. 클라이언트 빌드처럼 커스텀 스타일 파이프라인이 필요할 때 직접 제공.
33
+ - externalStylesheets?: Map<string, string> — 외부 스타일시트 맵(클라이언트 빌드용). 지정 시 비-템플릿 스타일 리소스를 해시 기반 `.css` 외부 파일명으로 매핑(`resourceNameToFileName`). 스타일을 별도 청크로 뽑을 때.
34
+ - compilerOptionsTransformer?: (options) => ts.CompilerOptions — 최종 compilerOptions 후처리 훅. 클라이언트의 `target`/`module`/`rootDir` 강제 등에 쓴다.
35
+ - lint?: boolean — true 면 `compileAsync` 가 program 기반 lint 를 함께 돌려 결과를 `result.lint` 에 담는다(affected 파일만 대상). 컴파일과 동시에 lint 하고 싶을 때.
36
+ - globalScss?: boolean — true 면 `scss/styles.scss` → `dist/styles.css` 글로벌 SCSS 를 컴파일하고 에러를 `result.scssErrors` 에 더한다.
37
+
38
+ ## ISdTsCompilerEmitOptions (compileAsync 2번째 인자)
39
+
40
+ ```typescript
41
+ interface ISdTsCompilerEmitOptions {
42
+ sourceFilter?: (fileName: string) => boolean;
43
+ additionalTransformers?: {
44
+ before?: ts.TransformerFactory<ts.SourceFile>[];
45
+ after?: ts.TransformerFactory<ts.SourceFile>[];
46
+ };
47
+ }
48
+ ```
49
+
50
+ - sourceFilter?: (fileName) => boolean — emit 결과 필터(Angular only). true 인 소스만 `emitResults` 에 남긴다. 특정 파일의 emit 만 필요할 때(예: HMR 단일 파일).
51
+ - additionalTransformers?.before / .after — Angular 기본 transformers 앞/뒤에 끼울 추가 TS transformer 배열(Angular only). 커스텀 코드 변환을 주입할 때.
52
+
53
+ ## SdTsCompiler — 메서드
54
+
55
+ ```typescript
56
+ class SdTsCompiler {
57
+ constructor(options: ISdTsCompilerOptions);
58
+ compileAsync(modifiedFiles?: ReadonlySet<string>, emitOptions?: ISdTsCompilerEmitOptions): Promise<ISdTsCompilerResult>;
59
+ get sideEffectScssRegistry(): Map<string, SideEffectScssEntry>;
60
+ compileSideEffectScss(): void;
61
+ findAffectedByScss(scssPath: string): string[];
62
+ }
63
+ ```
64
+
65
+ - compileAsync(modifiedFiles?, emitOptions?) — 1회 증분 컴파일 실행. `modifiedFiles` = 직전 변경된 파일 절대경로 집합(증분 무효화용, 미지정·빈 집합이면 캐시 그대로 사용; node_modules 포함 변경 시 packageJsonCache 클리어). `emitOptions` 는 위 emit 필터/transformer. 같은 인스턴스로 반복 호출해 증분 빌드를 이어간다.
66
+ - sideEffectScssRegistry (getter) — side-effect SCSS 등록부(`Map<소스, SideEffectScssEntry>`) 참조. emit 코드가 항목을 등록하는 통로.
67
+ - compileSideEffectScss() — 위 레지스트리의 모든 항목을 CSS 로 컴파일하고 에러/의존성을 내부 상태에 반영.
68
+ - findAffectedByScss(scssPath) — 주어진 SCSS 경로에 의존하는 소유자 파일 경로 배열을 반환(역방향 탐색). watch 에서 SCSS 변경 시 재컴파일 대상 산출에 쓴다.
69
+
70
+ 사용 예:
71
+
72
+ ```typescript
73
+ import { SdTsCompiler } from "@simplysm/sd-cli";
74
+
75
+ const compiler = new SdTsCompiler({
76
+ pkgDir: "/repo/packages/core-common",
77
+ cwd: "/repo",
78
+ output: { js: true, dts: true },
79
+ });
80
+ const result = await compiler.compileAsync();
81
+ if (result.errorCount > 0) console.error(result.errors);
82
+ // watch 라운드: 변경 파일만 넘겨 증분 컴파일
83
+ const next = await compiler.compileAsync(new Set(["/repo/packages/core-common/src/foo.ts"]));
84
+ ```
85
+
86
+ ## ISdTsCompilerResult (compileAsync 반환)
87
+
88
+ ```typescript
89
+ interface ISdTsCompilerResult {
90
+ program: ts.Program;
91
+ builderProgram: ts.EmitAndSemanticDiagnosticsBuilderProgram;
92
+ isForAngular: boolean;
93
+ affectedFiles: ReadonlySet<string> | undefined;
94
+ diagnostics: SerializedDiagnostic[];
95
+ errorCount: number;
96
+ warningCount: number;
97
+ errors?: string[];
98
+ ngtscProgram?: NgtscProgram;
99
+ emitResults?: EmitResult[]; // EmitResult: { filename; contents; sourceFileName }
100
+ lint?: LintWithProgramResult;
101
+ scssErrors: string[];
102
+ scssDependencies: ReadonlyMap<string, ReadonlySet<string>>;
103
+ }
104
+ ```
105
+
106
+ - program: ts.Program — TS Program 참조. lint·외부 도구에 넘길 때.
107
+ - builderProgram — 증분 BuilderProgram 참조.
108
+ - isForAngular: boolean — Angular 모드로 컴파일됐는지(tsconfig 의 `angularCompilerOptions` 유무로 결정). 후속 처리 분기에 쓴다.
109
+ - affectedFiles: ReadonlySet<string> | undefined — 이번 빌드에서 영향받은 파일(posix 경로). `undefined` = 전역 변경(전체 리빌드). 부분 재처리 범위 판단에 쓴다.
110
+ - diagnostics: SerializedDiagnostic[] — 직렬화된 진단 전체(worker 경계 통과용). 내부 크래시 진단도 합산됨.
111
+ - errorCount / warningCount: number — Error / Warning 카테고리 진단 수. 크래시는 errorCount 에 가산.
112
+ - errors?: string[] — Error 진단을 `"파일:줄:열: TS코드: 메시지"` 형식으로 포맷한 배열(없으면 undefined). 로그 출력에 바로 쓴다.
113
+ - ngtscProgram?: NgtscProgram — NgtscProgram 참조(Angular only, HMR 용). non-Angular 이면 undefined.
114
+ - emitResults?: EmitResult[] — Angular emit 결과 배열. 각 항목 `{ filename; contents; sourceFileName }`(sourceFileName = 원본 소스 경로). non-Angular 은 writeFile 훅으로 디스크에 직접 쓰므로 undefined. 메모리상 컴파일 결과가 필요한 플러그인이 소비.
115
+ - lint?: LintWithProgramResult — lint 결과(`lint: true` 일 때만).
116
+ - scssErrors: string[] — SCSS 컴파일 에러 목록.
117
+ - scssDependencies: ReadonlyMap<string, ReadonlySet<string>> — SCSS 의존성 맵(소유자 파일 → 의존 SCSS 경로 집합). watch 역방향 탐색용.