@mandujs/core 0.12.1 → 0.13.0

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 (177) hide show
  1. package/README.ko.md +304 -304
  2. package/README.md +653 -653
  3. package/package.json +8 -8
  4. package/src/brain/architecture/analyzer.ts +28 -26
  5. package/src/brain/doctor/analyzer.ts +1 -1
  6. package/src/bundler/build.ts +91 -91
  7. package/src/bundler/css.ts +302 -302
  8. package/src/bundler/dev.ts +0 -1
  9. package/src/change/history.ts +3 -3
  10. package/src/change/snapshot.ts +10 -9
  11. package/src/change/transaction.ts +2 -2
  12. package/src/client/Link.tsx +227 -227
  13. package/src/client/globals.ts +44 -44
  14. package/src/client/hooks.ts +267 -267
  15. package/src/client/index.ts +5 -5
  16. package/src/client/island.ts +8 -8
  17. package/src/client/router.ts +435 -435
  18. package/src/client/runtime.ts +23 -23
  19. package/src/client/serialize.ts +404 -404
  20. package/src/client/window-state.ts +101 -101
  21. package/src/config/mandu.ts +94 -96
  22. package/src/config/validate.ts +213 -215
  23. package/src/config/watcher.ts +311 -311
  24. package/src/constants.ts +40 -40
  25. package/src/content/content-layer.ts +314 -314
  26. package/src/content/content.test.ts +433 -433
  27. package/src/content/data-store.ts +245 -245
  28. package/src/content/digest.ts +133 -133
  29. package/src/content/index.ts +164 -164
  30. package/src/content/loader-context.ts +172 -172
  31. package/src/content/loaders/api.ts +216 -216
  32. package/src/content/loaders/file.ts +169 -169
  33. package/src/content/loaders/glob.ts +252 -252
  34. package/src/content/loaders/index.ts +34 -34
  35. package/src/content/loaders/types.ts +137 -137
  36. package/src/content/meta-store.ts +209 -209
  37. package/src/content/types.ts +282 -282
  38. package/src/content/watcher.ts +135 -135
  39. package/src/contract/client-safe.test.ts +42 -42
  40. package/src/contract/client-safe.ts +114 -114
  41. package/src/contract/client.ts +16 -16
  42. package/src/contract/define.ts +459 -459
  43. package/src/contract/handler.ts +10 -10
  44. package/src/contract/normalize.test.ts +276 -276
  45. package/src/contract/normalize.ts +404 -404
  46. package/src/contract/registry.test.ts +206 -206
  47. package/src/contract/registry.ts +568 -568
  48. package/src/contract/schema.ts +48 -48
  49. package/src/contract/types.ts +58 -58
  50. package/src/contract/validator.ts +32 -32
  51. package/src/devtools/ai/context-builder.ts +375 -375
  52. package/src/devtools/ai/index.ts +25 -25
  53. package/src/devtools/ai/mcp-connector.ts +465 -465
  54. package/src/devtools/client/catchers/error-catcher.ts +327 -327
  55. package/src/devtools/client/catchers/index.ts +18 -18
  56. package/src/devtools/client/catchers/network-proxy.ts +363 -363
  57. package/src/devtools/client/components/index.ts +39 -39
  58. package/src/devtools/client/components/kitchen-root.tsx +362 -362
  59. package/src/devtools/client/components/mandu-character.tsx +241 -241
  60. package/src/devtools/client/components/overlay.tsx +368 -368
  61. package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
  62. package/src/devtools/client/components/panel/guard-panel.tsx +244 -244
  63. package/src/devtools/client/components/panel/index.ts +32 -32
  64. package/src/devtools/client/components/panel/islands-panel.tsx +304 -304
  65. package/src/devtools/client/components/panel/network-panel.tsx +292 -292
  66. package/src/devtools/client/components/panel/panel-container.tsx +259 -259
  67. package/src/devtools/client/filters/context-filters.ts +282 -282
  68. package/src/devtools/client/filters/index.ts +16 -16
  69. package/src/devtools/client/index.ts +63 -63
  70. package/src/devtools/client/persistence.ts +335 -335
  71. package/src/devtools/client/state-manager.ts +478 -478
  72. package/src/devtools/design-tokens.ts +263 -263
  73. package/src/devtools/hook/create-hook.ts +207 -207
  74. package/src/devtools/hook/index.ts +13 -13
  75. package/src/devtools/index.ts +439 -439
  76. package/src/devtools/init.ts +266 -266
  77. package/src/devtools/protocol.ts +237 -237
  78. package/src/devtools/server/index.ts +17 -17
  79. package/src/devtools/server/source-context.ts +444 -444
  80. package/src/devtools/types.ts +319 -319
  81. package/src/devtools/worker/index.ts +25 -25
  82. package/src/devtools/worker/redaction-worker.ts +222 -222
  83. package/src/devtools/worker/worker-manager.ts +409 -409
  84. package/src/error/classifier.ts +2 -2
  85. package/src/error/domains.ts +265 -265
  86. package/src/error/formatter.ts +32 -32
  87. package/src/error/result.ts +46 -46
  88. package/src/error/stack-analyzer.ts +5 -0
  89. package/src/error/types.ts +6 -6
  90. package/src/errors/extractor.ts +409 -409
  91. package/src/errors/index.ts +19 -19
  92. package/src/filling/auth.ts +308 -308
  93. package/src/filling/context.ts +569 -569
  94. package/src/filling/deps.ts +238 -238
  95. package/src/generator/contract-glue.ts +2 -1
  96. package/src/generator/generate.ts +12 -10
  97. package/src/generator/index.ts +3 -3
  98. package/src/generator/templates.ts +80 -79
  99. package/src/guard/analyzer.ts +360 -360
  100. package/src/guard/ast-analyzer.ts +806 -806
  101. package/src/guard/auto-correct.ts +1 -1
  102. package/src/guard/check.ts +128 -128
  103. package/src/guard/contract-guard.ts +9 -9
  104. package/src/guard/file-type.test.ts +24 -24
  105. package/src/guard/healing.ts +2 -0
  106. package/src/guard/index.ts +2 -0
  107. package/src/guard/negotiation.ts +430 -4
  108. package/src/guard/presets/atomic.ts +70 -70
  109. package/src/guard/presets/clean.ts +77 -77
  110. package/src/guard/presets/cqrs.test.ts +175 -0
  111. package/src/guard/presets/cqrs.ts +107 -0
  112. package/src/guard/presets/fsd.ts +79 -79
  113. package/src/guard/presets/hexagonal.ts +68 -68
  114. package/src/guard/presets/index.ts +291 -288
  115. package/src/guard/reporter.ts +445 -445
  116. package/src/guard/rules.ts +12 -12
  117. package/src/guard/statistics.ts +578 -578
  118. package/src/guard/suggestions.ts +358 -352
  119. package/src/guard/types.ts +348 -347
  120. package/src/guard/validator.ts +834 -834
  121. package/src/guard/watcher.ts +404 -404
  122. package/src/index.ts +1 -0
  123. package/src/intent/index.ts +310 -310
  124. package/src/island/index.ts +304 -304
  125. package/src/logging/index.ts +22 -22
  126. package/src/logging/transports.ts +365 -365
  127. package/src/paths.test.ts +47 -0
  128. package/src/paths.ts +47 -0
  129. package/src/plugins/index.ts +38 -38
  130. package/src/plugins/registry.ts +377 -377
  131. package/src/plugins/types.ts +363 -363
  132. package/src/report/build.ts +1 -1
  133. package/src/report/index.ts +1 -1
  134. package/src/router/fs-patterns.ts +387 -387
  135. package/src/router/fs-routes.ts +344 -401
  136. package/src/router/fs-scanner.ts +497 -497
  137. package/src/router/fs-types.ts +270 -278
  138. package/src/router/index.ts +81 -81
  139. package/src/runtime/boundary.tsx +232 -232
  140. package/src/runtime/compose.ts +222 -222
  141. package/src/runtime/lifecycle.ts +381 -381
  142. package/src/runtime/logger.test.ts +345 -345
  143. package/src/runtime/logger.ts +677 -677
  144. package/src/runtime/router.test.ts +476 -476
  145. package/src/runtime/router.ts +105 -105
  146. package/src/runtime/security.ts +155 -155
  147. package/src/runtime/server.ts +24 -24
  148. package/src/runtime/session-key.ts +328 -328
  149. package/src/runtime/ssr.ts +367 -367
  150. package/src/runtime/streaming-ssr.ts +1245 -1245
  151. package/src/runtime/trace.ts +144 -144
  152. package/src/seo/index.ts +214 -214
  153. package/src/seo/integration/ssr.ts +307 -307
  154. package/src/seo/render/basic.ts +427 -427
  155. package/src/seo/render/index.ts +143 -143
  156. package/src/seo/render/jsonld.ts +539 -539
  157. package/src/seo/render/opengraph.ts +191 -191
  158. package/src/seo/render/robots.ts +116 -116
  159. package/src/seo/render/sitemap.ts +137 -137
  160. package/src/seo/render/twitter.ts +126 -126
  161. package/src/seo/resolve/index.ts +353 -353
  162. package/src/seo/resolve/opengraph.ts +143 -143
  163. package/src/seo/resolve/robots.ts +73 -73
  164. package/src/seo/resolve/title.ts +94 -94
  165. package/src/seo/resolve/twitter.ts +73 -73
  166. package/src/seo/resolve/url.ts +97 -97
  167. package/src/seo/routes/index.ts +290 -290
  168. package/src/seo/types.ts +575 -575
  169. package/src/slot/validator.ts +39 -39
  170. package/src/spec/index.ts +3 -3
  171. package/src/spec/load.ts +76 -76
  172. package/src/spec/lock.ts +56 -56
  173. package/src/utils/bun.ts +8 -8
  174. package/src/utils/lru-cache.ts +75 -75
  175. package/src/utils/safe-io.ts +188 -188
  176. package/src/utils/string-safe.ts +298 -298
  177. package/src/watcher/rules.ts +5 -5
@@ -1,77 +1,77 @@
1
- /**
2
- * Clean Architecture Preset
3
- *
4
- * 백엔드 권장 아키텍처
5
- */
6
-
7
- import type { PresetDefinition } from "../types";
8
-
9
- /**
10
- * Clean Architecture 레이어 계층 구조
11
- *
12
- * 의존성: 외부 → 내부 (domain이 핵심)
13
- */
14
- export const CLEAN_HIERARCHY = [
15
- "api",
16
- "infra",
17
- "application",
18
- "domain",
19
- "core",
20
- "shared",
21
- ] as const;
22
-
23
- /**
24
- * Clean Architecture 프리셋 정의
25
- */
26
- export const cleanPreset: PresetDefinition = {
27
- name: "clean",
28
- description: "Clean Architecture - 백엔드 권장 아키텍처",
29
-
30
- hierarchy: [...CLEAN_HIERARCHY],
31
-
32
- layers: [
33
- {
34
- name: "api",
35
- pattern: "src/**/api/**",
36
- canImport: ["application", "core", "shared"],
37
- description: "Controllers, Routes, DTOs",
38
- },
39
- {
40
- name: "infra",
41
- pattern: "src/**/infra/**",
42
- canImport: ["application", "domain", "core", "shared"],
43
- description: "Repositories 구현, 외부 API 클라이언트",
44
- },
45
- {
46
- name: "application",
47
- pattern: "src/**/application/**",
48
- canImport: ["domain", "core", "shared"],
49
- description: "Use Cases, Application Services",
50
- },
51
- {
52
- name: "domain",
53
- pattern: "src/**/domain/**",
54
- canImport: ["shared"],
55
- description: "Entities, Value Objects, Domain Services",
56
- },
57
- {
58
- name: "core",
59
- pattern: "src/core/**",
60
- canImport: ["shared"],
61
- description: "공통 핵심 (auth, config, errors)",
62
- },
63
- {
64
- name: "shared",
65
- pattern: "src/shared/**",
66
- canImport: [],
67
- description: "공유 유틸리티, 타입",
68
- },
69
- ],
70
-
71
- defaultSeverity: {
72
- layerViolation: "error",
73
- circularDependency: "error",
74
- crossSliceDependency: "warn",
75
- deepNesting: "info",
76
- },
77
- };
1
+ /**
2
+ * Clean Architecture Preset
3
+ *
4
+ * 백엔드 권장 아키텍처
5
+ */
6
+
7
+ import type { PresetDefinition } from "../types";
8
+
9
+ /**
10
+ * Clean Architecture 레이어 계층 구조
11
+ *
12
+ * 의존성: 외부 → 내부 (domain이 핵심)
13
+ */
14
+ export const CLEAN_HIERARCHY = [
15
+ "api",
16
+ "infra",
17
+ "application",
18
+ "domain",
19
+ "core",
20
+ "shared",
21
+ ] as const;
22
+
23
+ /**
24
+ * Clean Architecture 프리셋 정의
25
+ */
26
+ export const cleanPreset: PresetDefinition = {
27
+ name: "clean",
28
+ description: "Clean Architecture - 백엔드 권장 아키텍처",
29
+
30
+ hierarchy: [...CLEAN_HIERARCHY],
31
+
32
+ layers: [
33
+ {
34
+ name: "api",
35
+ pattern: "src/**/api/**",
36
+ canImport: ["application", "core", "shared"],
37
+ description: "Controllers, Routes, DTOs",
38
+ },
39
+ {
40
+ name: "infra",
41
+ pattern: "src/**/infra/**",
42
+ canImport: ["application", "domain", "core", "shared"],
43
+ description: "Repositories 구현, 외부 API 클라이언트",
44
+ },
45
+ {
46
+ name: "application",
47
+ pattern: "src/**/application/**",
48
+ canImport: ["domain", "core", "shared"],
49
+ description: "Use Cases, Application Services",
50
+ },
51
+ {
52
+ name: "domain",
53
+ pattern: "src/**/domain/**",
54
+ canImport: ["shared"],
55
+ description: "Entities, Value Objects, Domain Services",
56
+ },
57
+ {
58
+ name: "core",
59
+ pattern: "src/core/**",
60
+ canImport: ["shared"],
61
+ description: "공통 핵심 (auth, config, errors)",
62
+ },
63
+ {
64
+ name: "shared",
65
+ pattern: "src/shared/**",
66
+ canImport: [],
67
+ description: "공유 유틸리티, 타입",
68
+ },
69
+ ],
70
+
71
+ defaultSeverity: {
72
+ layerViolation: "error",
73
+ circularDependency: "error",
74
+ crossSliceDependency: "warn",
75
+ deepNesting: "info",
76
+ },
77
+ };
@@ -0,0 +1,175 @@
1
+ /**
2
+ * CQRS Preset Tests
3
+ *
4
+ * CQRS 프리셋의 구조 검증 및 Command/Query 분리 규칙 테스트
5
+ */
6
+
7
+ import { describe, it, expect, beforeAll, afterAll } from "bun:test";
8
+ import { mkdtemp, rm, mkdir } from "fs/promises";
9
+ import { join } from "path";
10
+ import { tmpdir } from "os";
11
+ import { cqrsPreset, CQRS_HIERARCHY } from "./cqrs";
12
+ import { presets, getPreset } from "./index";
13
+ import { negotiate, generateScaffold } from "../negotiation";
14
+
15
+ // ═══════════════════════════════════════════════════════════════════════════
16
+ // Preset Structure Tests
17
+ // ═══════════════════════════════════════════════════════════════════════════
18
+
19
+ describe("CQRS Preset - Structure", () => {
20
+ it("should be registered in presets map", () => {
21
+ expect(presets.cqrs).toBeDefined();
22
+ expect(presets.cqrs).toBe(cqrsPreset);
23
+ });
24
+
25
+ it("should be retrievable via getPreset", () => {
26
+ expect(getPreset("cqrs")).toBe(cqrsPreset);
27
+ });
28
+
29
+ it("should have correct name and description", () => {
30
+ expect(cqrsPreset.name).toBe("cqrs");
31
+ expect(cqrsPreset.description).toContain("CQRS");
32
+ });
33
+
34
+ it("should define all 10 layers", () => {
35
+ expect(cqrsPreset.layers).toHaveLength(10);
36
+ });
37
+
38
+ it("should have hierarchy matching layer names", () => {
39
+ const layerNames = cqrsPreset.layers.map((l) => l.name);
40
+ for (const h of CQRS_HIERARCHY) {
41
+ expect(layerNames).toContain(h);
42
+ }
43
+ });
44
+
45
+ it("should set layerViolation severity to error", () => {
46
+ expect(cqrsPreset.defaultSeverity?.layerViolation).toBe("error");
47
+ });
48
+ });
49
+
50
+ // ═══════════════════════════════════════════════════════════════════════════
51
+ // CQRS Separation Rules - TODO(human)
52
+ // ═══════════════════════════════════════════════════════════════════════════
53
+
54
+ describe("CQRS Preset - Command/Query Separation", () => {
55
+ const findLayer = (name: string) => cqrsPreset.layers.find((l) => l.name === name)!;
56
+
57
+ it("commands cannot import queries", () => {
58
+ const commands = findLayer("application/commands");
59
+ expect(commands.canImport).not.toContain("application/queries");
60
+ });
61
+
62
+ it("queries cannot import commands or events", () => {
63
+ const queries = findLayer("application/queries");
64
+ expect(queries.canImport).not.toContain("application/commands");
65
+ expect(queries.canImport).not.toContain("application/events");
66
+ });
67
+
68
+ it("commands can import domain, dto, events", () => {
69
+ const commands = findLayer("application/commands");
70
+ expect(commands.canImport).toContain("domain");
71
+ expect(commands.canImport).toContain("application/dto");
72
+ expect(commands.canImport).toContain("application/events");
73
+ });
74
+
75
+ it("queries can import domain, dto", () => {
76
+ const queries = findLayer("application/queries");
77
+ expect(queries.canImport).toContain("domain");
78
+ expect(queries.canImport).toContain("application/dto");
79
+ });
80
+
81
+ it("domain can only import shared", () => {
82
+ const domain = findLayer("domain");
83
+ expect(domain.canImport).toEqual(["shared"]);
84
+ });
85
+
86
+ it("shared cannot import anything", () => {
87
+ const shared = findLayer("shared");
88
+ expect(shared.canImport).toEqual([]);
89
+ });
90
+ });
91
+
92
+ // ═══════════════════════════════════════════════════════════════════════════
93
+ // CQRS Scaffolding Tests
94
+ // ═══════════════════════════════════════════════════════════════════════════
95
+
96
+ let SCAFFOLD_DIR: string;
97
+
98
+ beforeAll(async () => {
99
+ SCAFFOLD_DIR = await mkdtemp(join(tmpdir(), "test-cqrs-scaffold-"));
100
+ await mkdir(join(SCAFFOLD_DIR, "spec", "decisions"), { recursive: true });
101
+ });
102
+
103
+ afterAll(async () => {
104
+ await rm(SCAFFOLD_DIR, { recursive: true, force: true });
105
+ });
106
+
107
+ describe("CQRS Preset - Scaffold Templates", () => {
108
+ it("should generate commands/queries separation for auth", async () => {
109
+ const result = await negotiate(
110
+ { intent: "사용자 인증 추가", category: "auth", preset: "cqrs" },
111
+ SCAFFOLD_DIR,
112
+ );
113
+
114
+ expect(result.approved).toBe(true);
115
+ expect(result.preset).toBe("cqrs");
116
+
117
+ const paths = result.structure.map((s) => s.path);
118
+ expect(paths.some((p) => p.includes("application/commands"))).toBe(true);
119
+ expect(paths.some((p) => p.includes("application/queries"))).toBe(true);
120
+ expect(paths.some((p) => p.includes("application/dto"))).toBe(true);
121
+ expect(paths.some((p) => p.includes("application/events"))).toBe(true);
122
+ });
123
+
124
+ it("should generate CRUD with separate commands and queries", async () => {
125
+ const result = await negotiate(
126
+ { intent: "상품 관리 CRUD", category: "crud", preset: "cqrs" },
127
+ SCAFFOLD_DIR,
128
+ );
129
+
130
+ const commandsDir = result.structure.find((s) => s.path.includes("application/commands"));
131
+ const queriesDir = result.structure.find((s) => s.path.includes("application/queries"));
132
+
133
+ expect(commandsDir).toBeDefined();
134
+ expect(queriesDir).toBeDefined();
135
+
136
+ // commands에 create/update/delete가 있어야 함
137
+ const cmdFiles = commandsDir!.files.map((f) => f.name);
138
+ expect(cmdFiles.some((f) => f.includes("create"))).toBe(true);
139
+ expect(cmdFiles.some((f) => f.includes("update"))).toBe(true);
140
+ expect(cmdFiles.some((f) => f.includes("delete"))).toBe(true);
141
+
142
+ // queries에 get/list가 있어야 함
143
+ const qryFiles = queriesDir!.files.map((f) => f.name);
144
+ expect(qryFiles.some((f) => f.includes("get"))).toBe(true);
145
+ expect(qryFiles.some((f) => f.includes("list"))).toBe(true);
146
+ });
147
+
148
+ it("should create actual scaffold files", async () => {
149
+ const testDir = await mkdtemp(join(SCAFFOLD_DIR, "actual-"));
150
+ await mkdir(join(testDir, "spec", "decisions"), { recursive: true });
151
+
152
+ const plan = await negotiate(
153
+ { intent: "order feature", category: "crud", preset: "cqrs" },
154
+ testDir,
155
+ );
156
+ const result = await generateScaffold(plan.structure, testDir);
157
+
158
+ expect(result.success).toBe(true);
159
+ expect(result.createdFiles.some((f) => f.includes("command"))).toBe(true);
160
+ expect(result.createdFiles.some((f) => f.includes("query"))).toBe(true);
161
+ });
162
+
163
+ it("should use src/ prefixed paths (no adjustStructureForPreset)", async () => {
164
+ const result = await negotiate(
165
+ { intent: "payment api", category: "api", preset: "cqrs" },
166
+ SCAFFOLD_DIR,
167
+ );
168
+
169
+ // CQRS 전용 템플릿은 이미 src/ 기준 경로를 사용
170
+ const paths = result.structure.map((s) => s.path);
171
+ for (const p of paths) {
172
+ expect(p.startsWith("src/")).toBe(true);
173
+ }
174
+ });
175
+ });
@@ -0,0 +1,107 @@
1
+ /**
2
+ * CQRS Preset
3
+ *
4
+ * Command Query Responsibility Segregation 아키텍처
5
+ * 쓰기 경로(Commands)와 읽기 경로(Queries)의 격리를 프레임워크 레벨에서 강제
6
+ */
7
+
8
+ import type { PresetDefinition } from "../types";
9
+
10
+ /**
11
+ * CQRS 레이어 계층 구조
12
+ *
13
+ * 의존성: 외부 → 내부 (domain이 핵심)
14
+ * commands와 queries는 서로 격리됨
15
+ */
16
+ export const CQRS_HIERARCHY = [
17
+ "api",
18
+ "infra",
19
+ "application/commands",
20
+ "application/queries",
21
+ "application/dto",
22
+ "application/mappers",
23
+ "application/events",
24
+ "domain",
25
+ "core",
26
+ "shared",
27
+ ] as const;
28
+
29
+ /**
30
+ * CQRS 프리셋 정의
31
+ */
32
+ export const cqrsPreset: PresetDefinition = {
33
+ name: "cqrs",
34
+ description: "CQRS - Command/Query 분리 아키텍처",
35
+
36
+ hierarchy: [...CQRS_HIERARCHY],
37
+
38
+ layers: [
39
+ {
40
+ name: "api",
41
+ pattern: "src/**/api/**",
42
+ canImport: ["application/commands", "application/queries", "application/dto", "core", "shared"],
43
+ description: "Controllers, Routes - Command/Query 디스패치",
44
+ },
45
+ {
46
+ name: "infra",
47
+ pattern: "src/**/infra/**",
48
+ canImport: ["application/commands", "application/queries", "domain", "core", "shared"],
49
+ description: "Repository 구현, 외부 서비스 어댑터",
50
+ },
51
+ {
52
+ name: "application/commands",
53
+ pattern: "src/**/application/commands/**",
54
+ canImport: ["domain", "application/dto", "application/events", "core", "shared"],
55
+ description: "Command Handlers - 쓰기 경로 (queries 접근 불가)",
56
+ },
57
+ {
58
+ name: "application/queries",
59
+ pattern: "src/**/application/queries/**",
60
+ canImport: ["domain", "application/dto", "core", "shared"],
61
+ description: "Query Handlers - 읽기 경로 (commands, events 접근 불가)",
62
+ },
63
+ {
64
+ name: "application/dto",
65
+ pattern: "src/**/application/dto/**",
66
+ canImport: ["domain", "shared"],
67
+ description: "Data Transfer Objects",
68
+ },
69
+ {
70
+ name: "application/mappers",
71
+ pattern: "src/**/application/mappers/**",
72
+ canImport: ["domain", "application/dto", "shared"],
73
+ description: "Domain ↔ DTO 변환기",
74
+ },
75
+ {
76
+ name: "application/events",
77
+ pattern: "src/**/application/events/**",
78
+ canImport: ["domain", "shared"],
79
+ description: "Domain Events, Integration Events",
80
+ },
81
+ {
82
+ name: "domain",
83
+ pattern: "src/**/domain/**",
84
+ canImport: ["shared"],
85
+ description: "Entities, Value Objects, Domain Services, Aggregates",
86
+ },
87
+ {
88
+ name: "core",
89
+ pattern: "src/core/**",
90
+ canImport: ["shared"],
91
+ description: "공통 핵심 (auth, config, errors, CQRS bus)",
92
+ },
93
+ {
94
+ name: "shared",
95
+ pattern: "src/shared/**",
96
+ canImport: [],
97
+ description: "공유 유틸리티, 타입, 인터페이스",
98
+ },
99
+ ],
100
+
101
+ defaultSeverity: {
102
+ layerViolation: "error",
103
+ circularDependency: "error",
104
+ crossSliceDependency: "warn",
105
+ deepNesting: "info",
106
+ },
107
+ };
@@ -1,79 +1,79 @@
1
- /**
2
- * FSD (Feature-Sliced Design) Preset
3
- *
4
- * 프론트엔드 권장 아키텍처
5
- *
6
- * @see https://feature-sliced.design/
7
- */
8
-
9
- import type { PresetDefinition } from "../types";
10
-
11
- /**
12
- * FSD 레이어 계층 구조 (상위 → 하위)
13
- *
14
- * app → pages → widgets → features → entities → shared
15
- */
16
- export const FSD_HIERARCHY = [
17
- "app",
18
- "pages",
19
- "widgets",
20
- "features",
21
- "entities",
22
- "shared",
23
- ] as const;
24
-
25
- /**
26
- * FSD 프리셋 정의
27
- */
28
- export const fsdPreset: PresetDefinition = {
29
- name: "fsd",
30
- description: "Feature-Sliced Design - 프론트엔드 권장 아키텍처",
31
-
32
- hierarchy: [...FSD_HIERARCHY],
33
-
34
- layers: [
35
- {
36
- name: "app",
37
- pattern: "src/app/**",
38
- canImport: ["pages", "widgets", "features", "entities", "shared"],
39
- description: "앱 진입점, 프로바이더, 글로벌 스타일",
40
- },
41
- {
42
- name: "pages",
43
- pattern: "src/pages/**",
44
- canImport: ["widgets", "features", "entities", "shared"],
45
- description: "페이지 컴포넌트, 라우팅",
46
- },
47
- {
48
- name: "widgets",
49
- pattern: "src/widgets/**",
50
- canImport: ["features", "entities", "shared"],
51
- description: "독립적인 UI 블록 (Header, Sidebar 등)",
52
- },
53
- {
54
- name: "features",
55
- pattern: "src/features/**",
56
- canImport: ["entities", "shared"],
57
- description: "비즈니스 기능 (로그인, 결제 등)",
58
- },
59
- {
60
- name: "entities",
61
- pattern: "src/entities/**",
62
- canImport: ["shared"],
63
- description: "비즈니스 엔티티 (User, Product 등)",
64
- },
65
- {
66
- name: "shared",
67
- pattern: "src/shared/**",
68
- canImport: [],
69
- description: "공유 유틸, UI 컴포넌트, 라이브러리",
70
- },
71
- ],
72
-
73
- defaultSeverity: {
74
- layerViolation: "error",
75
- circularDependency: "warn",
76
- crossSliceDependency: "warn",
77
- deepNesting: "info",
78
- },
79
- };
1
+ /**
2
+ * FSD (Feature-Sliced Design) Preset
3
+ *
4
+ * 프론트엔드 권장 아키텍처
5
+ *
6
+ * @see https://feature-sliced.design/
7
+ */
8
+
9
+ import type { PresetDefinition } from "../types";
10
+
11
+ /**
12
+ * FSD 레이어 계층 구조 (상위 → 하위)
13
+ *
14
+ * app → pages → widgets → features → entities → shared
15
+ */
16
+ export const FSD_HIERARCHY = [
17
+ "app",
18
+ "pages",
19
+ "widgets",
20
+ "features",
21
+ "entities",
22
+ "shared",
23
+ ] as const;
24
+
25
+ /**
26
+ * FSD 프리셋 정의
27
+ */
28
+ export const fsdPreset: PresetDefinition = {
29
+ name: "fsd",
30
+ description: "Feature-Sliced Design - 프론트엔드 권장 아키텍처",
31
+
32
+ hierarchy: [...FSD_HIERARCHY],
33
+
34
+ layers: [
35
+ {
36
+ name: "app",
37
+ pattern: "src/app/**",
38
+ canImport: ["pages", "widgets", "features", "entities", "shared"],
39
+ description: "앱 진입점, 프로바이더, 글로벌 스타일",
40
+ },
41
+ {
42
+ name: "pages",
43
+ pattern: "src/pages/**",
44
+ canImport: ["widgets", "features", "entities", "shared"],
45
+ description: "페이지 컴포넌트, 라우팅",
46
+ },
47
+ {
48
+ name: "widgets",
49
+ pattern: "src/widgets/**",
50
+ canImport: ["features", "entities", "shared"],
51
+ description: "독립적인 UI 블록 (Header, Sidebar 등)",
52
+ },
53
+ {
54
+ name: "features",
55
+ pattern: "src/features/**",
56
+ canImport: ["entities", "shared"],
57
+ description: "비즈니스 기능 (로그인, 결제 등)",
58
+ },
59
+ {
60
+ name: "entities",
61
+ pattern: "src/entities/**",
62
+ canImport: ["shared"],
63
+ description: "비즈니스 엔티티 (User, Product 등)",
64
+ },
65
+ {
66
+ name: "shared",
67
+ pattern: "src/shared/**",
68
+ canImport: [],
69
+ description: "공유 유틸, UI 컴포넌트, 라이브러리",
70
+ },
71
+ ],
72
+
73
+ defaultSeverity: {
74
+ layerViolation: "error",
75
+ circularDependency: "warn",
76
+ crossSliceDependency: "warn",
77
+ deepNesting: "info",
78
+ },
79
+ };