@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,18 +1,42 @@
1
1
  # @simplysm/excel — ExcelWrapper
2
2
 
3
- Zod 스키마 1개로 레코드 배열 ↔ Excel 파일을 타입 안전하게 매핑할읽는다. 스키마 각 필드의 `.describe()` Excel 헤더(표시명)가 되고, 필드 타입으로 읽기 변환·검증을 수행한다. 저수준 조작 없이 "정형 데이터의 import/export" 용도일 때 사용.
3
+ Zod 스키마로 엑셀 헤더 ↔ 필드 매핑, 셀 값 타입 변환, 행 단위 유효성 검사를 자동화해 "레코드 배열 ↔ 엑셀" 변환을 번에 처리할쓰는 고수준 래퍼. 헤더 텍스트는 각 필드의 `.describe()` 지정하며, 미지정 필드는 이름을 헤더로 쓴다. 표준 입력 양식 업로드/다운로드 같은 정형 변환에 적합.
4
4
 
5
- ## 시그니처
5
+ ## 생성자
6
6
 
7
7
  ```typescript
8
8
  new ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>>(schema: TSchema)
9
+ ```
10
+
11
+ - `schema: TSchema` — 레코드 구조를 정의하는 Zod 객체 스키마. 각 필드에 `.describe("헤더이름")` 으로 엑셀 헤더 표시명을 지정한다. optional/nullable/default/boolean 여부가 읽기 기본값·필수 강조·타입 변환 동작을 결정한다.
12
+
13
+ ## read
9
14
 
15
+ ```typescript
10
16
  read(
11
17
  file: Bytes | Blob,
12
18
  wsNameOrIndex: string | number = 0,
13
19
  options?: { excludes?: (keyof z.infer<TSchema>)[] },
14
20
  ): Promise<z.infer<TSchema>[]>
21
+ ```
22
+
23
+ - `file: Bytes | Blob` — 읽을 .xlsx 데이터.
24
+ - `wsNameOrIndex: string | number` — 읽을 시트 이름 또는 0 기반 인덱스. 기본 `0`(첫 시트).
25
+ - `options.excludes?: (keyof Schema)[]` — 매핑에서 제외할 필드 키 배열. 해당 헤더는 읽지 않음.
26
+
27
+ 동작: 스키마 표시명 집합에 해당하는 헤더만 골라 데이터 테이블을 읽고, 각 행을 필드 키로 역매핑한 뒤 값 변환 → Zod `safeParse` 검증한다. 빈/누락 값은 스키마 기본값 규칙(아래)으로 채우고, 한 행의 모든 매핑 값이 비면 그 행은 건너뛴다. 데이터가 0건이거나 검증 실패면 시트명을 포함해 throw(부분 반영 없이 전체 중단). 워크북은 내부에서 열고 finally 로 닫는다.
28
+
29
+ 값 변환 규칙:
30
+
31
+ - `ZodString` → 문자열(비문자열은 `String()`).
32
+ - `ZodNumber` → `num.parseFloat`.
33
+ - `ZodBoolean` → `"1"`/`"true"`→`true`, `"0"`/`"false"`→`false`, 그 외 `Boolean()`.
34
+ - `DateOnly`/`DateTime`/`Time` 인스턴스 → 그대로 보존.
35
+ - 빈/누락 값(`null`/`""`) → 스키마 기본값: `ZodDefault` 면 그 기본값, optional/nullable 면 `undefined`, 필수 boolean 이면 `false`, 그 외 `undefined`.
15
36
 
37
+ ## write
38
+
39
+ ```typescript
16
40
  write(
17
41
  wsName: string,
18
42
  records: Partial<z.infer<TSchema>>[],
@@ -20,56 +44,29 @@ write(
20
44
  ): Promise<ExcelWorkbook>
21
45
  ```
22
46
 
23
- - `schema: z.ZodObject` — 레코드 구조. 각 필드의 `.describe("표시명")` 이 Excel 헤더명. 미지정 시 필드 키를 헤더로 사용.
24
- - `read.file: Bytes | Blob`읽을 .xlsx 데이터.
25
- - `read.wsNameOrIndex: string | number` — 대상 시트(기본 0번). 이름 또는 0 기반 인덱스.
26
- - `read.options.excludes?: (keyof ...)[]` — 매핑에서 제외할 필드 키 목록.
27
- - `write.wsName: string` — 생성할 시트 이름.
28
- - `write.records: Partial<...>[]` — 기록할 부분 레코드 배열. 누락 필드는 빈 셀.
29
- - `write.options.excludes?: (keyof ...)[]` — 출력에서 제외할 필드 키 목록.
30
-
31
- ## read 동작
32
-
33
- - 헤더명↔필드 역매핑 후 `ws.getDataTable({ usableHeaderNameFn })` 로 표시명에 일치하는 컬럼만 추출.
34
- - 각 셀 값을 필드 타입별로 변환 후, 행마다 `schema.safeParse` 로 검증. 실패하면 그 행에서 throw(부분 반영 없음).
35
- - 모든 필드가 null/`""` 인 행은 skip.
36
- - 데이터가 한 건도 없으면 기대 헤더 목록을 담은 메시지로 throw.
37
- - 내부에서 워크북을 열고 `finally` 로 `close()` 하므로 호출자 정리 불필요.
47
+ - `wsName: string` — 만들 시트 이름.
48
+ - `records: Partial<Schema>[]` 출력할 레코드 배열(부분 객체 허용 누락 키는 빈 셀).
49
+ - `options.excludes?: (keyof Schema)[]` — 출력에서 제외할 필드 배열.
38
50
 
39
- ### 변환 규칙
40
-
41
- - 빈값(null/`""`) → 스키마 기본값: `ZodDefault` 면 그 기본값, optional/nullable 이면 `undefined`, 필수 boolean 이면 `false`, 그 외 `undefined`.
42
- - `ZodString` → 문자열(아니면 `String()` 캐스팅).
43
- - `ZodNumber` → number(문자열은 `num.parseFloat`).
44
- - `ZodBoolean` → `"1"`/`"true"` → `true`, `"0"`/`"false"` → `false`, 그 외 `Boolean()`.
45
- - `DateOnly`/`DateTime`/`Time` 인스턴스는 그대로 통과.
46
-
47
- ## write 동작
48
-
49
- - 0행에 헤더(제외 후 필드 순서), 1행부터 레코드 값 기록.
50
- - 전 셀에 4변 테두리 적용.
51
- - **필수**(optional/nullable/default 아님)이며 boolean 이 아닌 필드의 헤더 셀은 노란색(`00FFFF00`) 강조.
52
- - zoom 85%, 0행 틀고정 적용.
53
- - 반환된 `ExcelWorkbook` 의 리소스 관리는 **호출자 책임** — 사용 후 `close()` 필수. write 중 예외 발생 시 내부에서 close 후 rethrow.
51
+ 동작: 워크북에 시트 1개를 만들고, 0행에 표시명 헤더, 1행부터 각 레코드 값을 스키마 키 순서대로 쓴다. 전체 표에 4변 테두리, 필수(non-optional·non-nullable·non-default)이며 boolean 이 아닌 필드의 헤더 셀에 노란 배경(`"00FFFF00"`) 강조, zoom 85%, 0행 틀고정을 적용한다. **반환된 워크북의 close 는 호출자 책임** — 사용 후 반드시 `close()`.
54
52
 
55
53
  ## 사용 예
56
54
 
57
55
  ```typescript
58
- import { z } from "zod";
59
-
60
56
  const schema = z.object({
61
- name: z.string().describe("이름"),
62
- age: z.number().optional().describe("나이"),
57
+ code: z.string().describe("코드"),
58
+ qty: z.number().describe("수량"),
59
+ note: z.string().optional().describe("비고"),
63
60
  });
64
61
  const wrapper = new ExcelWrapper(schema);
65
62
 
66
- // 읽기 ("이름"/"나이" 헤더를 0번 시트에서 매칭)
67
- const rows = await wrapper.read(bytes);
63
+ // 읽기
64
+ const records = await wrapper.read(bytes, "입력", { excludes: ["note"] });
68
65
 
69
- // 쓰기 (호출자가 close 책임)
70
- const wb = await wrapper.write("회원", [{ name: "홍길동", age: 30 }]);
66
+ // 쓰기 (워크북 close 는 호출자 책임)
67
+ const wb = await wrapper.write("결과", records);
71
68
  try {
72
- const out = await wb.toBytes();
69
+ const bytes = await wb.toBytes();
73
70
  } finally {
74
71
  await wb.close();
75
72
  }
@@ -77,7 +74,7 @@ try {
77
74
 
78
75
  ## 주의사항
79
76
 
80
- - `.describe()` 표시명이 실제 Excel 헤더와 일치해야 `read` 컬럼을 인식. 일치 헤더가 전혀 없으면 데이터 0건으로 throw.
81
- - 표시명 미지정 필드는 필드 그대로 헤더로 쓰이므로, 한글 헤더가 필요하면 반드시 `.describe()` 지정.
82
- - `read` 검증은 전부-성공 전제: 행이라도 `safeParse` 실패 전체 throw.
83
- - `write` 반환 워크북 미`close` 리소스 누수.
77
+ - 헤더 매핑은 `.describe()` 값(미지정 키)이 엑셀 헤더 텍스트와 정확히 일치해야 한다. 매핑되는 헤더가 중복이거나 데이터가 0건이면 throw.
78
+ - `read` 단위 Zod 검증을 거치므로, 행이라도 스키마 위반이면 전체가 throw(부분 결과 없음).
79
+ - `write` 필수 헤더 노랑 강조는 "필수 입력 칸" 안내 목적 — optional/nullable/default 또는 boolean 필드는 강조되지 않는다.
80
+ - 결측 보존: optional/nullable 필드의 빈 값은 `undefined` 유지된다(임의 치환 없음). 필수 boolean 만 `false` 채워진다.
@@ -1,43 +1,49 @@
1
1
  # @simplysm/lint
2
2
 
3
- 심플리즘 워크스페이스 전용 ESLint 플러그인. 커스텀 규칙 9 묶음(`eslint-plugin`)과, 규칙들을 포함해 JS/TS/HTML 파일별로 완성된 flat config 배열(`eslint-recommended`)을 서브패스 export 로 제공. `src/index.ts` 없음 — `package.json` 의 `exports`(`./eslint-plugin`, `./eslint-recommended`)가 진입점.
3
+ simplysm 전용 ESLint 9 flat-config 프리셋과 커스텀 규칙 9종을 제공하는 ESLint 플러그인 패키지. `src/index.ts` 없음 — `package.json` 의 `exports`(`./eslint-plugin`, `./eslint-recommended`) 두 subpath 가 진입점. 패키지 루트 import 는 없음.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **eslint-recommended** — 프로젝트 `eslint.config.{js,mjs,cjs}` 에서 simplysm 표준 lint 규칙 전체(JS/TS/HTML 파일별 + tseslint/angular-eslint 통합)를 번에 적용할 import 하는 default export(완성된 flat config 배열).
8
- - **eslint-plugin** `@simplysm/<rule>` 커스텀 규칙 9종을 직접 켜거나, 개별 규칙의 감지 범위·메시지·autofix·옵션을 파악할 때. 자세히: [rules.md](./rules.md)
7
+ - **eslint-recommended** (`@simplysm/lint/eslint-recommended`) — 프로젝트 `eslint.config.ts` 에서 통째로 spread 쓰는 완성형 flat-config 배열. JS/TS/HTML 파일별 규칙·ignore·플러그인·Angular 통합이 모두 포함됨. 프로젝트 lint 설정을 잡을 때.
8
+ - **eslint-plugin** (`@simplysm/lint/eslint-plugin`) 커스텀 규칙만 담은 ESLint Plugin 객체(`{ rules }`). recommended 를 쓰지 않고 규칙을 직접 골라 등록할 때, 또는 규칙 동작을 개별 검토할 때. 자세히: [rules.md](./rules.md)
9
9
 
10
10
  ## eslint-recommended
11
11
 
12
- `@simplysm/lint/eslint-recommended` 의 default export. `tseslint.config(...)` 결과(`FlatConfig` 배열)이므로 호출형이 아니라 완성된 상수. 그대로 export 하거나 spread 항목을 덧붙여 사용.
12
+ `@simplysm/lint/eslint-recommended` 의 default export. `typescript-eslint` 의 `tseslint.config(...)` 결과 = flat-config 객체 배열. 그대로 spread 해서 사용함.
13
13
 
14
- ```js
15
- // eslint.config.js
14
+ ```typescript
15
+ // eslint.config.ts
16
16
  import recommended from "@simplysm/lint/eslint-recommended";
17
- export default recommended;
18
- // 규칙 추가 시: export default [...recommended, { files: ["**/*.ts"], rules: { "no-debugger": "error" } }];
17
+
18
+ export default [...recommended];
19
19
  ```
20
20
 
21
- config 강제하는 핵심 내용(파일 glob 블록별):
21
+ 배열을 구성하는 config 블록(순서대로):
22
22
 
23
- - **ignores** — `**/node_modules/**`, `**/dist/**`, `**/.*/**`(숨김 디렉터리 전체). 순회 자체를 건너뜀.
24
- - **`**/*.{js,mjs,cjs}`**node globals 적용. 켜는 규칙: `require-await`(async 함수에 await 없으면 오류), `no-shadow`(상위 스코프 변수 가림 금지), `no-duplicate-imports`(같은 모듈 중복 import 금지), `no-unused-expressions`(부수효과 없는 표현식 금지), `no-undef`(선언 안 된 식별자 사용 금지), `unused-imports/no-unused-imports`(미사용 import 제거, autofix), `unused-imports/no-unused-vars`(미사용 변수·인자 경고, `^_` 접두는 무시), `import/no-extraneous-dependencies`(미선언 의존성 import 금지, 단 `lib/**`·`eslint.config.*`·`simplysm.*`·`vitest.config.*` 는 devDependencies 허용), `@simplysm/no-subpath-imports-from-simplysm`, `@simplysm/no-hard-private` + 아래 공통 규칙군.
25
- - **`**/*.ts`** `angular.configs.tsRecommended` 펼친 뒤 추가. `processInlineTemplates` 인라인 템플릿 분리, `tseslint.parser`(`parserOptions.project: true`) 사용, `eslint-import-resolver-typescript` resolver 를 `import.meta.resolve` 로 자동 등록. 켜는 규칙: `no-console`(error), 그리고 타입 인식 `@typescript-eslint/*` 규칙 — `require-await`, `await-thenable`(thenable 만 await), `return-await`(`in-try-catch` = try/catch 안에서만 return await 요구), `no-floating-promises`(await/void 안 한 Promise 금지), `no-shadow`, `no-unnecessary-condition`(`allowConstantLoopConditions: true` = 상수 루프 조건 허용), `no-unnecessary-type-assertion`, `prefer-reduce-type-parameter`, `prefer-return-this-type`, `no-unused-expressions`, `strict-boolean-expressions`(`allowNullableBoolean`·`allowNullableObject` = nullable boolean/object 의 조건식 허용), `ban-ts-comment`(`ts-expect-error` 는 3자 이상 설명 필수), `prefer-readonly`, `naming-convention`(private 멤버는 `_` 접두 필수, 그 외 포맷 제약 없음), `no-misused-promises`(`checksVoidReturn.arguments:false`·`inheritedMethods:false` = 콜백 인자·상속 메서드의 void 반환 검사는 끔), `only-throw-error`(Error 외 throw 금지), `no-array-delete`(배열에 `delete` 금지). 커스텀 규칙 6종: `@simplysm/ng-no-async-effect`, `no-hard-private`, `no-subpath-imports-from-simplysm`, `ts-no-throw-not-implemented-error`(warn), `ts-no-unused-injects`, `ts-no-unused-protected-readonly`. + `@angular-eslint/no-output-native`(off).
26
- - **`**/*.html`** — `angular.configs.templateRecommended` + `templateAccessibility` 확장. 커스텀 템플릿 규칙: `@simplysm/ng-template-no-strict-null-check`, `ng-template-no-todo-comments`(warn), `ng-template-sd-require-binding-attrs` + `@angular-eslint/template/eqeqeq`(`allowNullOrUndefined: true` = null/undefined 비교는 느슨한 비교 허용), `label-has-associated-control`(off), `no-any`(error).
27
- - **`**/tests/**/*.ts`(override)**테스트 완화: `no-console`(off), `import/no-extraneous-dependencies`(off), `@simplysm/ts-no-throw-not-implemented-error`(off).
28
- - **`**/vitest.config.ts`(override)** — `no-restricted-properties`(off). 설정 파일에서 `process.env` 직접 접근 허용.
23
+ - **globalIgnores** — `**/node_modules/**`, `**/dist/**`, `**/.*/**` 순회 자체를 건너뜀. 점(.)으로 시작하는 디렉토리 전체가 제외 대상.
24
+ - **공통 languageOptions** — `ecmaVersion: "latest"`, `sourceType: "module"`.
25
+ - **JS 블록** (`**/*.js`, `**/*.mjs`, `**/*.cjs`) node 글로벌, `import`/`@simplysm`/`unused-imports` 플러그인 등록. 활성 규칙: `require-await`, `no-shadow`, `no-duplicate-imports`, `no-unused-expressions`, `no-undef`, unused-imports 2종, `import/no-extraneous-dependencies`(lib·`eslint.config`·`simplysm`·`vitest.config` 파일은 devDeps 허용), `@simplysm/no-subpath-imports-from-simplysm`, `@simplysm/no-hard-private`, node 빌트인 차단 규칙군, env 직접접근 차단 규칙군.
26
+ - **angular tsRecommended** — `angular.configs.tsRecommended` spread.
27
+ - **TS 블록** (`**/*.ts`) — `processor: angular.processInlineTemplates`(인라인 템플릿 추출 후 별도 lint), `parserOptions.project: true`(타입 정보 사용), `import/resolver` 로 typescript resolver(`alwaysTryTypes: true`) 지정. typescript-eslint 타입체크 규칙 다수 + `@simplysm` 커스텀 규칙 6종 활성. 커스텀 규칙 심각도: `ng-no-async-effect`/`no-hard-private`/`no-subpath-imports-from-simplysm`/`ts-no-unused-injects`/`ts-no-unused-protected-readonly` = `error`, `ts-no-throw-not-implemented-error` = `warn`. `@angular-eslint/no-output-native` 은 `off`.
28
+ - **HTML 블록** (`**/*.html`) — `angular.configs.templateRecommended` + `templateAccessibility` extends. `@simplysm` 템플릿 규칙 3종 활성: `ng-template-no-strict-null-check`=`error`, `ng-template-no-todo-comments`=`warn`, `ng-template-sd-require-binding-attrs`=`error`. `@angular-eslint/template/eqeqeq` `allowNullOrUndefined: true`, `label-has-associated-control`=`off`, `no-any`=`error`.
29
+ - **테스트 오버라이드** (`**/tests/**/*.ts`) — 테스트 코드 완화: `no-console`=`off`, `import/no-extraneous-dependencies`=`off`, `@simplysm/ts-no-throw-not-implemented-error`=`off`.
30
+ - **vitest.config 오버라이드** (`**/vitest.config.ts`) — `no-restricted-properties`=`off` (설정 파일에서 `process.env` 접근 허용).
29
31
 
30
- 공통 규칙군(JS/TS 양쪽 적용): `no-warning-comments`(warn, TODO/FIXME 등 경고), `eqeqeq`(`always`, 단 `null` 비교는 `==`/`!=` 허용), `no-self-compare`, `array-callback-return`(map/filter 등 콜백에 return 강제). 추가 공통 금지: `no-restricted-globals`(`Buffer` 전역 금지 → `Uint8Array`/`BytesUtils`), `no-restricted-imports`(`buffer`·`events`·`eventemitter3` 모듈 금지), `no-restricted-properties`(`process.env` 직접 접근 금지 → `env("...")`), `no-restricted-syntax`(`import.meta.env` 직접 접근·`env("NODE_ENV")`·`=== undefined`/`!== undefined` 류 strict 비교 금지 → `== null`/`!= null`).
32
+ 주의:
31
33
 
32
- 주의: 타입 인식 `@typescript-eslint` 규칙이 켜져 있어 TS 파일은 `parserOptions.project: true` 동작하는 환경(tsconfig 포함)이어야 함.
34
+ - TS 블록은 타입 정보(`parserOptions.project: true`)를 요구하므로, 대상 프로젝트에 유효한 `tsconfig` 있어야 함.
35
+ - node 빌트인 차단(`noNodeBuiltinsRules`): `Buffer` 글로벌 및 `buffer`/`events`/`eventemitter3` import 금지 — 각각 `Uint8Array`/`@simplysm/core-common`의 `BytesUtils`·`EventEmitter` 로 대체 유도. JS·TS 블록 모두에 적용.
36
+ - env 직접접근 차단(`noDirectEnvAccessRules`): `process.env`·`import.meta.env` 직접 접근, `env("NODE_ENV")` 호출, `=== undefined`/`!== undefined`(엄격 비교) 를 `no-restricted-properties`/`no-restricted-syntax` 로 막음(엄격 비교는 `== null`/`!= null` 로 유도). JS·TS 블록 모두에 적용.
33
37
 
34
38
  ## eslint-plugin
35
39
 
36
- `@simplysm/lint/eslint-plugin` 의 default export `{ rules: { ... } }` 형태의 ESLint 플러그인 객체. 9개 규칙을 규칙 ID → 규칙 정의로 매핑. 보통 `eslint-recommended` 가 내부에서 `@simplysm` 네임스페이스로 등록하므로, 직접 import 는 recommended 없이 커스텀 config 를 짜거나 특정 규칙만 개별 제어할 때만 필요. 규칙별 상세는 [rules.md](./rules.md).
40
+ `@simplysm/lint/eslint-plugin` 의 default export. ESLint Plugin 형태 객체.
37
41
 
38
- ```js
42
+ ```typescript
39
43
  import plugin from "@simplysm/lint/eslint-plugin";
40
- export default [{ plugins: { "@simplysm": plugin }, rules: { "@simplysm/no-hard-private": "error" } }];
44
+ // plugin === { rules: { "ng-no-async-effect": ..., "no-hard-private": ..., ... } }
41
45
  ```
42
46
 
43
- 규칙 ID 9종: `ng-no-async-effect`, `ng-template-no-strict-null-check`, `ng-template-no-todo-comments`, `ng-template-sd-require-binding-attrs`, `no-hard-private`, `no-subpath-imports-from-simplysm`, `ts-no-throw-not-implemented-error`, `ts-no-unused-injects`, `ts-no-unused-protected-readonly`.
47
+ - `rules` — 규칙 id → 규칙 객체 맵. 등록된 9 id: `ng-no-async-effect`, `ng-template-no-strict-null-check`, `ng-template-no-todo-comments`, `ng-template-sd-require-binding-attrs`, `no-hard-private`, `no-subpath-imports-from-simplysm`, `ts-no-throw-not-implemented-error`, `ts-no-unused-injects`, `ts-no-unused-protected-readonly`.
48
+
49
+ flat-config 에서 직접 등록할 때는 `plugins: { "@simplysm": plugin }` 로 매핑 후 `"@simplysm/<id>"` 로 켬. 각 규칙의 검사 대상·옵션·autofix·메시지는 [rules.md](./rules.md) 참조.
@@ -1,90 +1,130 @@
1
1
  # @simplysm/lint — rules
2
2
 
3
- `eslint-plugin` 이 노출하는 커스텀 ESLint 규칙 9종. 규칙 ID 는 등록 시 `@simplysm/<name>`. 개별 규칙을 켜거나 lint 위반 메시지·autofix·옵션 의미를 파악할 때 읽음. 옵션 없는 규칙은 `schema: []`. autofix 는 `fixable: "code"` 표시 규칙만 제공. 모든 규칙은 `createRule`(= `ESLintUtils.RuleCreator`)로 생성되어 문서 URL 이 자동 부여됨.
3
+ `eslint-plugin` 이 노출하는 커스텀 ESLint 규칙 9종. 규칙 id 는 등록 시 `@simplysm/<name>`. 개별 규칙을 켜거나 lint 위반 메시지·autofix·옵션 의미를 파악할 때 읽음. 모든 규칙은 `createRule`(= `ESLintUtils.RuleCreator`, `src/utils/create-rule.ts`)로 생성되어 문서 URL 이 자동 부여됨. 옵션 없는 규칙은 `schema: []`. autofix 는 아래 "autofix" 표기 규칙만 제공.
4
4
 
5
- ## ng-no-async-effect
5
+ ## no-hard-private
6
6
 
7
- `type: problem`, autofix 없음. `@angular/core` 의 `effect()` 인자로 async 함수(화살표/함수표현식이며 `async` 플래그)를 직접 전달하는 것을 금지. `await` 이후 읽은 signal 반응형 컨텍스트를 벗어나 의존성 추적에서 빠지는 버그, 그리고 반환값이 `Promise<void>` 가 되어 cleanup 등록이 막히는 문제를 방지.
7
+ ECMAScript hard private(`#field`) 사용을 금지하고 TypeScript `private _` 스타일을 강제. `type: "problem"`, autofix 있음, 옵션 없음. JS·TS 양쪽에서 동작.
8
8
 
9
- - 감지 범위: named import(`effect`), aliased import(`effect as ngEffect`), namespace import(`ng.effect(...)`). `@angular/core` 아닌 모듈·로컬 선언 `effect` 는 무시(스코프·import 정의를 추적해 판별).
10
- - `noAsyncEffect` — async 직접 전달 금지 메시지. 비동기 작업은 `void untracked(async () => { ... })` 안에서 수행하라고 안내.
9
+ 검사 대상: 클래스 필드 선언(`#field`), 메서드 선언(`#method()`), 접근자 선언(`accessor #field`), 멤버 접근 표현식(`this.#field`).
11
10
 
12
- ```ts
13
- // 위반
14
- effect(async () => { await load(); });
15
- // 권장
16
- effect(() => { sig(); void untracked(async () => { await load(); }); });
11
+ 메시지:
12
+ - `preferSoftPrivate` — hard private(#) 금지, `private _` 스타일 사용 안내. autofix 는 `#name` → `_name` 으로 치환하고, 선언부에 접근제어자가 없으면 `private ` 를 앞에 삽입(데코레이터·`static`·`async`·`readonly` 순서를 보존해 그 뒤 삽입). 토큰 계산 실패 시 이름만 바뀌는 불완전 수정을 막기 위해 수정 전체를 건너뜀.
13
+ - `nameConflict` `#{{name}}` `_{{name}}` 으로 바꿀 수 없음(동일 이름 멤버가 클래스에 이미 존재). 이 경우 보고만 하고 autofix 안 함. 중첩 클래스는 스택으로 각 클래스 멤버 집합을 관리.
14
+
15
+ ```typescript
16
+ class A { #count = 0; inc() { this.#count++; } }
17
+ // → private _count = 0; inc() { this._count++; }
17
18
  ```
18
19
 
19
- ## no-hard-private
20
+ ## no-subpath-imports-from-simplysm
20
21
 
21
- `type: problem`, autofix 있음. ECMAScript hard private(`#field`) 금지, TypeScript `private _` 스타일 강제.
22
+ `@simplysm/*` 패키지의 `src` 하위 경로 import 를 금지(빌드 산출물 export 경유를 강제). `type: "problem"`, autofix 있음, 옵션 없음. JS·TS 양쪽에서 동작.
22
23
 
23
- - 감지: 필드 선언 `#field`, 메서드 선언 `#method()`, 접근자 선언 `accessor #field`, 사용처 `this.#field`. 중첩 클래스도 스택으로 추적.
24
- - autofix: `#a` → `_a` 이름 변경 + 선언부 앞에 `private ` 삽입(데코레이터·`static`·`async`·`readonly` 순서 보존; 사용처 `this.#a` → `this._a`). 단, 같은 클래스에 `_a` 멤버가 이미 있으면 충돌로 변환 불가 → 보고만 하고 autofix 안 함.
25
- - `preferSoftPrivate` — hard private 금지(이름 변경 가능 시 적용되는 일반 메시지).
26
- - `nameConflict` — `_{{name}}` 멤버가 이미 존재해 변환 불가. `{{name}}` 치환.
24
+ 검사 대상: 정적 import(`import ... from`), 동적 import(`import("...")`), 재내보내기(`export { x } from`), 전체 재내보내기(`export * from`). import 경로를 `/` 로 분리해 3번째 세그먼트가 `src` 인 경우(`@simplysm/pkg/src`, `@simplysm/pkg/src/xxx`)만 위반. `@simplysm/pkg`·`@simplysm/pkg/xxx`(src 아닌 subpath)는 허용.
27
25
 
28
- ## no-subpath-imports-from-simplysm
26
+ 메시지:
27
+ - `noSubpathImport` — `'@simplysm/{{pkg}}'` 에서 `src` 하위 경로 import 불가. autofix 는 경로를 `@simplysm/<pkg>`(원래 따옴표 유지)로 치환.
28
+
29
+ ```typescript
30
+ import { x } from "@simplysm/core-common/src/x"; // → "@simplysm/core-common"
31
+ ```
32
+
33
+ ## ng-no-async-effect
34
+
35
+ Angular `@angular/core` 의 `effect()` 에 async 함수를 직접 전달하는 것을 금지. `type: "problem"`, autofix 없음, 옵션 없음. `await` 이후 signal read 가 reactive 의존성으로 추적되지 않고 반환값이 `Promise<void>` 가 되어 cleanup 등록이 막히는 문제 방지.
29
36
 
30
- `type: problem`, autofix 있음. `@simplysm/<pkg>/src/...` 형태의 `src` 하위 경로 import 금지(`@simplysm/<pkg>` `src` 아닌 하위 경로는 허용).
37
+ 검사 대상: `effect(...)` 호출의 인자가 async ArrowFunction/FunctionExpression 인 경우. `effect` 식별자가 `@angular/core` 에서 import 되었는지 스코프로 검증 — named(`import { effect }`)·aliased(`effect as ngEffect`)·namespace(`import * as ng` → `ng.effect(...)`) import 만 인정. 다른 모듈 또는 로컬 선언 `effect` 는 무시.
31
38
 
32
- - 감지: 정적 `import ... from '...'`, 동적 `import('...')`, 재내보내기 `export { ... } from '...'`, 전체 재내보내기 `export * from '...'`. 경로를 `/` 로 분해해 3번째 세그먼트가 `src` 인 경우만 위반.
33
- - autofix: `@simplysm/pkg/src/x` `@simplysm/pkg` 치환(원본 따옴표 종류 유지).
34
- - `noSubpathImport` — `{{pkg}}`(패키지명)·`{{importPath}}`(원본 경로) 치환.
39
+ 메시지:
40
+ - `noAsyncEffect` async 함수 직접 전달 금지. 비동기는 `void untracked(async () => { ... })` 내부에서 수행하라고 안내. 보고 위치는 첫 인자(콜백) 노드.
41
+
42
+ ```typescript
43
+ effect(() => { this.sig(); void untracked(async () => { await this.load(); }); });
44
+ ```
35
45
 
36
46
  ## ts-no-throw-not-implemented-error
37
47
 
38
- `type: suggestion`, autofix 없음, recommended 에서 `warn`(테스트에서는 off). `@simplysm/core-common` 의 `NotImplementedError` 를 `new` 로 생성하는 코드를 감지해 미구현 코드의 프로덕션 유입을 경고.
48
+ `@simplysm/core-common` 의 `NotImplementedError` 를 `new` 로 인스턴스화하는 코드를 감지(미구현 코드의 프로덕션 유입 방지). `type: "suggestion"`, autofix 없음, 옵션 없음. recommended 에서 `warn`(테스트 파일은 `off`).
49
+
50
+ 검사 대상: `new NotImplementedError(...)`(named/aliased import), `new CC.NotImplementedError(...)`(namespace import). 식별자가 `@simplysm/core-common` 에서 import 되었는지 스코프로 검증. 동적 import(`await import(...)`)는 감지 안 함.
39
51
 
40
- - 감지: named/aliased import(`NotImplementedError`, `NotImplementedError as NIE`), namespace import(`new CC.NotImplementedError()`). import 소스가 `@simplysm/core-common` 인지 스코프로 검증. 동적 import 는 무시.
41
- - `noThrowNotImplementedError` — `{{text}}` 치환. `new` 첫 인자가 비어있지 않은 문자열 리터럴이면 그 문자열을, 아니면 `"미구현"` 을 그대로 출력.
52
+ 메시지:
53
+ - `noThrowNotImplementedError` — 메시지 본문은 `{{text}}` 치환. 첫 인자가 비어있지 않은 문자열 리터럴이면 그 값을, 아니면 `"미구현"` 을 출력.
42
54
 
43
- ```ts
44
- throw new NotImplementedError("결제 모듈 미구현"); // → "결제 모듈 미구현" 경고
55
+ ```typescript
56
+ import { NotImplementedError } from "@simplysm/core-common";
57
+ new NotImplementedError("결제 연동"); // → "결제 연동" 경고
45
58
  ```
46
59
 
47
60
  ## ts-no-unused-injects
48
61
 
49
- `type: problem`, autofix 있음. 클래스 내 `inject()` 호출로 초기화된 프로퍼티 같은 클래스 어디에서도 참조되지 않는 필드를 보고.
62
+ 미사용 Angular `inject()` 필드를 감지. `type: "problem"`, autofix 있음, 옵션 없음.
50
63
 
51
- - 감지: `PropertyDefinition` 값이 `inject(...)` 호출이고 key 식별자인 필드. 클래스 본문 전체를 재귀 순회해 필드명과 동일한 식별자(자기 key 제외)가 하나도 없으면 미사용으로 판정.
52
- - autofix: 해당 필드 제거(앞 토큰 끝 ~ 뒤 토큰 사이 range 삭제).
53
- - `unusedInject` — `inject() field "{{name}}" is never used.`
64
+ 검사 대상: 클래스 본문에서 `inject(...)` 호출로 초기화된 PropertyDefinition(키가 Identifier). 같은 클래스 본문 전체를 순회해 필드명과 동일한 Identifier 참조(필드 자신 제외)가 0개면 미사용으로 판정. 클래스 내부 참조만 검사 — 템플릿 사용 여부는 보지 않음.
65
+
66
+ 메시지:
67
+ - `unusedInject` — `inject() field "{{name}}" is never used.` autofix 는 해당 필드 선언을 앞 토큰 끝부터 제거(뒤 토큰이 있으면 필드 끝까지).
68
+
69
+ ```typescript
70
+ class C { private _svc = inject(MyService); } // _svc 미참조 시 제거
71
+ ```
54
72
 
55
73
  ## ts-no-unused-protected-readonly
56
74
 
57
- `type: problem`, autofix 있음. `@Component` 데코레이터가 달린 클래스에서 `protected readonly`(non-static) 필드가 인라인 `template` 클래스 본문 어디에서도 참조되지 않으면 보고.
75
+ Angular `@Component` 인라인 템플릿·클래스 본문 어디에서도 쓰이는 `protected readonly` 필드를 감지. `type: "problem"`, autofix 있음, 옵션 없음.
76
+
77
+ 검사 대상: `@Component` 데코레이터가 있고 그 첫 인자 객체에 `template` 속성(문자열 리터럴 또는 템플릿 리터럴)이 있는 클래스. 그 클래스의 `protected readonly` 비-static 필드(키가 Identifier)가 ① 인라인 템플릿에서 미참조 ② 클래스 본문 다른 멤버에서 미참조 둘 다일 때 보고. 템플릿 식별자는 `@angular/compiler` 의 `parseTemplate` 으로 AST 파싱 후 `ImplicitReceiver`/`ThisReceiver` 위 `PropertyRead`(클래스 필드 참조)만 수집하며, `*ngFor` 로컬·`@let`·`@if ... as`·`@for` item/별칭 등 스코프 로컬 변수는 제외. `@if`/`@switch`/`@for`/`@defer` 블록, 입력/이벤트/구조 디렉티브 바인딩까지 순회. `templateUrl`(외부 템플릿)은 대상 아님(`template` 문자열만).
58
78
 
59
- - 동작: `@Component({ template: ... })` 의 인라인 템플릿 문자열(템플릿 리터럴/문자열 리터럴)을 `@angular/compiler` 의 `parseTemplate` 으로 파싱해 바인딩 식별자를 수집. `@for` item·context 변수, `@if (...; as alias)`, `@let` 선언, 구조 디렉티브(`*ngFor` 등) 로컬 변수는 스코프에서 제외(오탐 방지). 템플릿·클래스 양쪽 모두 미사용일 때만 보고. `template` prop 이 없거나 빈 문자열이면 검사하지 않음(외부 `templateUrl` 미지원).
60
- - autofix: 필드 선언 라인 제거(앞 들여쓰기·뒤 세미콜론/개행 포함).
61
- - `unusedField` — `Protected readonly field "{{name}}" is not used in class or template.`
79
+ 메시지:
80
+ - `unusedField` — `Protected readonly field "{{name}}" is not used in class or template.` autofix 필드 선언과 앞 들여쓰기·뒤 `;`·개행을 함께 제거.
81
+
82
+ ```typescript
83
+ @Component({ template: `<div>{{title}}</div>` })
84
+ class C { protected readonly title = "x"; protected readonly unused = 1; } // unused 제거
85
+ ```
62
86
 
63
87
  ## ng-template-no-strict-null-check
64
88
 
65
- `type: problem`, autofix 없음(인라인 템플릿 offset 매핑 문제로 미제공). Angular 템플릿 바인딩에서 `=== null`/`undefined`, `!== null`/`undefined` 금지, `== null`/`!= null` 강제.
89
+ Angular HTML 템플릿에서 엄격 비교(`=== null`, `!== null`, `=== undefined`, `!== undefined`)를 금지하고 `== null`/`!= null` 로 통일하도록 강제. `type: "problem"`, autofix 없음(인라인 템플릿 offset 매핑 문제로 미제공), 옵션 없음. HTML 파일 대상.
90
+
91
+ 검사 대상: 템플릿 표현식의 `Binary` 노드 중 연산자가 `===`/`!==` 이고 양변 중 하나가 nil 리터럴(value 가 null/undefined)인 경우.
66
92
 
67
- - 감지: `===`/`!==` 이항 연산의 한쪽 피연산자가 `null`/`undefined` 리터럴(value 가 nil)인 경우.
68
- - `noStrictNullCheck` — `{{actual}}`(원본 표현식)·`{{replacement}}`(권장 표현식, `x == null`/`x != null`) 치환.
93
+ 메시지:
94
+ - `noStrictNullCheck` — `{{actual}}`(예: `x === null`) 사용 금지, `{{replacement}}`(예: `x == null`) 사용 안내. `===`→`==`, `!==`→`!=` 로 변환한 권장 표현을 제시.
95
+
96
+ ```html
97
+ @if (user !== null) {} <!-- user != null 사용하라고 보고 -->
98
+ ```
69
99
 
70
100
  ## ng-template-no-todo-comments
71
101
 
72
- `type: problem`, autofix 없음, recommended 에서 `warn`. HTML 템플릿의 `<!-- TODO: ... -->` 주석을 감지. AST visitor 아니라 raw 텍스트 정규식으로 처리(visitor 객체 반환).
102
+ Angular HTML 템플릿 `<!-- TODO: ... -->` 주석을 경고. `type: "problem"`, autofix 없음, 옵션 없음. recommended 에서 `warn`. HTML 파일 대상.
73
103
 
74
- - 감지: HTML 주석(`<!-- ... -->`) 내부에 `TODO:` 가 포함된 경우. 메시지에는 `TODO:` 이후를 trim 한 본문을 그대로 출력.
75
- - `noTodo` — `{{content}}` 치환(주석에 적힌 TODO 내용).
104
+ 동작: raw 텍스트를 `<!--...-->` 정규식으로 훑어 주석 내용에 `TODO:` 가 있으면 보고(AST 방문자 없이 빈 객체 반환). 메시지 본문은 `TODO:` trim 한 내용.
105
+
106
+ 메시지:
107
+ - `noTodo` — 본문은 `{{content}}`(TODO 뒤 텍스트) 그대로 출력.
108
+
109
+ ```html
110
+ <!-- TODO: 페이징 추가 --> <!-- "페이징 추가" 경고 -->
111
+ ```
76
112
 
77
113
  ## ng-template-sd-require-binding-attrs
78
114
 
79
- `type: problem`, autofix 있음, 옵션 있음. `sd-` 접두사 컴포넌트에서 허용 목록 밖 plain attribute 사용을 금지하고 Angular property binding(`[attr]="..."`)을 강제.
115
+ `sd-*` 접두사 커스텀 컴포넌트에서 허용목록 밖 plain attribute 사용을 금지하고 Angular property binding(`[attr]="..."`)을 강제. `type: "problem"`, autofix 있음. HTML 파일 대상. **유일하게 옵션 있는 규칙**.
116
+
117
+ 옵션(`RuleOptions`, 모두 선택):
118
+ - `selectorPrefixes: string[]` — 검사 대상 요소 태그 접두사. 미지정 시 `["sd-"]`. 태그명을 소문자로 비교. 다른 디자인 시스템 접두사를 검사하려면 지정.
119
+ - `allowAttributes: string[]` — plain attribute 로 허용할 정확한 이름 목록. 미지정 시 `["id","class","style","title","tabindex","role"]`. 소문자 비교. 추가로 허용할 표준 속성을 늘릴 때.
120
+ - `allowAttributePrefixes: string[]` — plain attribute 로 허용할 접두사 목록. 미지정 시 `["aria-","data-","sd-"]`. 소문자 비교. 접두사 기반(aria/data 등) 속성군을 통째 허용할 때.
121
+
122
+ 동작: 대상 태그(접두사 매칭) 요소의 attribute 중 `allowAttributes`(정확 일치)·`allowAttributePrefixes`(접두사 일치) 어디에도 안 드는 것을 보고.
80
123
 
81
- - 옵션(객체 1개, 모든 키 선택):
82
- - `selectorPrefixes: string[]` — 검사 대상 엘리먼트 태그 접두사. 기본 `["sd-"]`. 접두사로 시작하는 태그만 검사. 다른 디자인 시스템 접두사를 쓰면 값으로 교체.
83
- - `allowAttributes: string[]` — plain 으로 허용할 attribute 이름. 기본 `["id","class","style","title","tabindex","role"]`. 대소문자 무시. 표준 전역 속성을 추가 허용할 때 확장.
84
- - `allowAttributePrefixes: string[]` — plain 으로 허용할 attribute 접두사. 기본 `["aria-","data-","sd-"]`. 접근성·데이터 속성군을 통째로 허용.
85
- - autofix: 값 없는 attr → `[attr]="true"`, 값 있는 attr → `[attr]="'<escaped>'"`(`\`·`'` 이스케이프). 빈 span 이면 fix 생략.
86
- - `requireBindingForAttribute` — `{{attrName}}`·`{{elementName}}` 치환.
124
+ 메시지:
125
+ - `requireBindingForAttribute` — `"{{attrName}}"` `"{{elementName}}"` plain attribute 로 불가, property binding 사용 안내. autofix 는 값이 빈 문자열이면 `[attr]="true"`, 값이 있으면 `\` `'` 이스케이프해 `[attr]="'값'"` 치환(span start≥end 면 수정 안 함).
87
126
 
88
- ```js
89
- { "@simplysm/ng-template-sd-require-binding-attrs": ["error", { allowAttributes: ["id", "for"] }] }
127
+ ```html
128
+ <sd-button myattr="hello"></sd-button> <!-- <sd-button [myattr]="'hello'"></sd-button> -->
129
+ <sd-button disabled></sd-button> <!-- → <sd-button [disabled]="true"></sd-button> -->
90
130
  ```
@@ -1,67 +1,11 @@
1
1
  # @simplysm/orm-common
2
2
 
3
- Dialect 독립 ORM 코어. 테이블/뷰/프로시저를 빌더로 정의하고, `DbContext` 클래스에 등록한 뒤, 체이닝 `Queryable` 로 타입 안전한 SELECT/CUD 쿼리를 JSON AST(`QueryDef`/`Expr`)조립한다. 실제 SQL 변환·DB 연결은 dialect QueryBuilder(이 패키지) + `@simplysm/orm-node` executor 담당.
3
+ Dialect 독립적 ORM 코어. `DbContext` 를 상속해 테이블/뷰/프로시저를 등록하고, fluent `Queryable` 체이닝 + JSON AST `expr` 로 쿼리를 구성하면 dialect별 QueryBuilder 가 MySQL/MSSQL/PostgreSQL SQL변환한다. 실제 DB 연결·실행은 `DbContextExecutor` 구현체(서버/클라이언트)가 담당하므로 이 패키지 자체는 SQL 문자열·QueryDef AST 까지만 생성한다.
4
4
 
5
5
  ## 사용 트리거 인덱스
6
6
 
7
- - **스키마 정의 (Table/View/Procedure/Column/Index/Relation 빌더)** — DB 객체를 fluent 빌더로 선언하고 column·PK·index·FK 관계를 잡을 때. 자세히: [schema.md](./schema.md)
8
- - **DbContext (연결·트랜잭션·DDL·마이그레이션·초기화)** 빌더들을 컨텍스트에 등록하고 `connect`/`transaction` 으로 실행, DDL·migration·`initialize` 돌릴 때. 자세히: [db-context.md](./db-context.md)
9
- - **Queryable (SELECT/JOIN/INSERT/UPDATE/DELETE/UPSERT 체이닝)** — `db.user()` 로 받은 쿼리 빌더에 where/orderBy/join/include/group/recursive/union 걸고 execute/single/count/insert/update/delete/upsert 호출할 때. 자세히: [queryable.md](./queryable.md)
10
- - **expr (SQL 표현식 빌더)** — where/select/orderBy 콜백 안에서 비교·문자열·숫자·날짜·집계·조건·window·서브쿼리 표현식을 만들 때. 자세히: [expr.md](./expr.md)
11
- - **QueryDef / Expr / Column 타입**executor·QueryBuilder 를 직접 구현하거나 AST·결과 메타를 다룰 참조하는 타입군. 자세히: [types.md](./types.md)
12
- - **QueryBuilder (dialect SQL 렌더러)** — QueryDef 를 mysql/mssql/postgresql SQL 로 변환하는 클래스. executor 구현체가 사용. 자세히: [query-builder.md](./query-builder.md)
13
- - **결과 파싱 유틸 / 검색 파서 / 프로시저 실행 / 에러** — 아래 인라인 섹션 참조.
14
-
15
- ## Executable / executable (프로시저 실행)
16
-
17
- 저장 프로시저를 `DbContext.executable(Procedure)` 로 등록하면 `() => Executable` 팩토리가 반환된다. `Executable` 은 프로시저 실행 래퍼.
18
-
19
- - `executable(db: DbContextBase, builder: ProcedureBuilder): () => Executable` — DbContext 내부에서 프로시저 getter 를 만드는 팩토리. 보통 `DbContext.executable()` 보호 메서드가 대신 호출.
20
- - `Executable.execute(params): Promise<TReturns[][]>` — 프로시저 실행. `params` 는 `ProcedureBuilder.params()` 로 정의한 키별 값(리터럴 또는 ExprUnit). 결과는 결과셋 배열의 배열.
21
- - `Executable.getExecProcQueryDef(params?)` — 실행용 `ExecProcQueryDef` 생성. 파라미터 미정의 프로시저에 값 전달 시 throw.
22
-
23
- ```typescript
24
- const result = await db.getUserById().execute({ userId: 1 });
25
- ```
26
-
27
- ## 결과 파싱 유틸
28
-
29
- executor 가 DB raw 결과를 TS 객체로 환원할 때 쓰는 유틸. 일반 사용자는 직접 호출하지 않음.
30
-
31
- - `parseQueryResult<T>(rawResults, meta: ResultMeta): Promise<T[] | undefined>` — flat raw 행 배열을 `meta.columns`(키→타입) 로 타입 변환하고 `meta.joins`(키→`{isSingle}`) 로 중첩 그룹핑. 입력이 비었거나 파싱 후 전부 빈 객체면 undefined. async 전용(100행마다 이벤트 루프 양보). `isSingle:true` 관계에 서로 다른 다건이 매칭되면 throw.
32
- - `pickResultSets<T>(rawResults: T[][], buildResult): T[]` — 다중 결과셋에서 필요분 추출. `resultSetIndex` 없으면 첫 셋, `resultSetStride` 없으면 해당 인덱스 셋, 있으면 인덱스부터 stride 간격으로 concat(MySQL 배치 INSERT 의 SELECT 결과만 모을 때).
33
-
34
- ## 검색 파서
35
-
36
- `Queryable.search()` 내부에서 쓰이며, 검색 문법 문자열을 SQL LIKE 패턴으로 변환.
37
-
38
- - `parseSearchQuery(searchText: string): ParsedSearchQuery` — 검색 문자열 파싱. 반환 `{ or, must, not }` 각각 LIKE 패턴 배열.
39
- - `or: string[]` — 공백 구분 일반 토큰(OR, 하나 이상 일치). 와일드카드 없는 토큰은 `%토큰%`(부분 일치).
40
- - `must: string[]` — `+토큰` 또는 `"정확한 구문"`(AND, 필수 포함).
41
- - `not: string[]` — `-토큰`(NOT, 제외).
42
- - 문법: `term1 term2`(OR), `+term`(필수), `-term`(제외), `"구문"`(정확·필수), `*`(와일드카드→`%`). 이스케이프 `\\ \* \% \" \+ \-`. 닫히지 않은 따옴표는 일반 텍스트.
43
- - `interface ParsedSearchQuery` — 위 `or`/`must`/`not` 세 LIKE 패턴 배열을 담는 타입.
44
-
45
- ## 에러 (DbTransactionError / DbErrorCode)
46
-
47
- DBMS 네이티브 트랜잭션 에러를 표준 코드로 래핑. `connect`/`transaction` 의 롤백 경로에서 발생.
48
-
49
- - `class DbTransactionError extends Error` — 표준화된 트랜잭션 에러.
50
- - `code: DbErrorCode` — 표준 에러 코드.
51
- - `message: string` — 에러 메시지(생성자 2번째 인자).
52
- - `originalError?: unknown` — 원본 DBMS 에러(디버깅용).
53
- - `name` — 항상 `"DbTransactionError"`.
54
- - `enum DbErrorCode` — 표준 트랜잭션 에러 코드.
55
- - `NO_ACTIVE_TRANSACTION` — 활성 트랜잭션 없는데 ROLLBACK 시도. 롤백 중 이 코드면 `connect`/`transaction` 이 무시(원 에러 보존).
56
- - `TRANSACTION_ALREADY_STARTED` — 이미 트랜잭션 시작됨.
57
- - `DEADLOCK` — 데드락 발생.
58
- - `LOCK_TIMEOUT` — 잠금 타임아웃.
59
-
60
- ```typescript
61
- try {
62
- await db.rollbackTransaction();
63
- } catch (err) {
64
- if (err instanceof DbTransactionError && err.code === DbErrorCode.NO_ACTIVE_TRANSACTION) return;
65
- throw err;
66
- }
67
- ```
7
+ - **DbContext / 연결 / 트랜잭션 / DDL / 마이그레이션** — DB 컨텍스트 클래스를 정의하거나, `connect`/`transaction` 으로 쿼리를 감싸거나, 스키마를 생성·변경하거나, `initialize`/`migrations` 로 마이그레이션을 돌릴 때. `DbTransactionError`·`Migration`·`IsolationLevel`·`DbContextExecutor` 포함. 자세히: [db-context.md](./db-context.md)
8
+ - **스키마 정의 (Table / View / Procedure / column / index / relation)** — `Table(...)`/`View(...)`/`Procedure(...)` 빌더로 테이블·뷰·프로시저 스키마와 column·PK·index·FK 관계를 선언하고 타입을 추론할 때. 자세히: [schema.md](./schema.md)
9
+ - **Queryable 체이닝 / CRUD 실행 / 검색** — `db.X()` 로 얻은 `Queryable` `select`/`where`/`join`/`include`/`groupBy`/`orderBy` 조립하고 `execute`/`single`/`count`/`insert`/`update`/`delete`/`upsert` 실행할 때. `Queryable.union`·`search`·`Executable`·`parseSearchQuery` 포함. 자세히: [queryable.md](./queryable.md)
10
+ - **expr 표현식 빌더**`where`/`select`/`orderBy`/`having` 콜백 안에서 비교·논리·문자열·숫자·날짜·집계·window·조건·서브쿼리 표현식을 만들 때. `ExprUnit`/`WhereExprUnit`/`ExprInput` 포함. 자세히: [expr.md](./expr.md)
11
+ - **하위 타입 / QueryDef AST / QueryBuilder / 결과 파싱**Executor·QueryBuilder 를 직접 구현하거나, `QueryDef` AST·`Expr` AST·column 타입을 다루거나, 원시 결과를 `parseQueryResult`/`pickResultSets` 로 변환할 때. 자세히: [types.md](./types.md)