@mandujs/core 0.12.2 → 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.
- package/README.ko.md +304 -304
- package/README.md +653 -653
- package/package.json +1 -1
- package/src/brain/architecture/analyzer.ts +28 -26
- package/src/brain/doctor/analyzer.ts +1 -1
- package/src/bundler/build.ts +91 -91
- package/src/bundler/css.ts +302 -302
- package/src/bundler/dev.ts +0 -1
- package/src/change/history.ts +3 -3
- package/src/change/snapshot.ts +10 -9
- package/src/change/transaction.ts +2 -2
- package/src/client/Link.tsx +227 -227
- package/src/client/globals.ts +44 -44
- package/src/client/hooks.ts +267 -267
- package/src/client/index.ts +5 -5
- package/src/client/island.ts +8 -8
- package/src/client/router.ts +435 -435
- package/src/client/runtime.ts +23 -23
- package/src/client/serialize.ts +404 -404
- package/src/client/window-state.ts +101 -101
- package/src/config/mandu.ts +94 -96
- package/src/config/validate.ts +213 -215
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/content-layer.ts +314 -314
- package/src/content/content.test.ts +433 -433
- package/src/content/data-store.ts +245 -245
- package/src/content/digest.ts +133 -133
- package/src/content/index.ts +164 -164
- package/src/content/loader-context.ts +172 -172
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +169 -169
- package/src/content/loaders/glob.ts +252 -252
- package/src/content/loaders/index.ts +34 -34
- package/src/content/loaders/types.ts +137 -137
- package/src/content/meta-store.ts +209 -209
- package/src/content/types.ts +282 -282
- package/src/content/watcher.ts +135 -135
- package/src/contract/client-safe.test.ts +42 -42
- package/src/contract/client-safe.ts +114 -114
- package/src/contract/client.ts +16 -16
- package/src/contract/define.ts +459 -459
- package/src/contract/handler.ts +10 -10
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +404 -404
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/registry.ts +568 -568
- package/src/contract/schema.ts +48 -48
- package/src/contract/types.ts +58 -58
- package/src/contract/validator.ts +32 -32
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +465 -465
- package/src/devtools/client/catchers/error-catcher.ts +327 -327
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/catchers/network-proxy.ts +363 -363
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/kitchen-root.tsx +362 -362
- package/src/devtools/client/components/mandu-character.tsx +241 -241
- package/src/devtools/client/components/overlay.tsx +368 -368
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +244 -244
- package/src/devtools/client/components/panel/index.ts +32 -32
- package/src/devtools/client/components/panel/islands-panel.tsx +304 -304
- package/src/devtools/client/components/panel/network-panel.tsx +292 -292
- package/src/devtools/client/components/panel/panel-container.tsx +259 -259
- package/src/devtools/client/filters/context-filters.ts +282 -282
- package/src/devtools/client/filters/index.ts +16 -16
- package/src/devtools/client/index.ts +63 -63
- package/src/devtools/client/persistence.ts +335 -335
- package/src/devtools/client/state-manager.ts +478 -478
- package/src/devtools/design-tokens.ts +263 -263
- package/src/devtools/hook/create-hook.ts +207 -207
- package/src/devtools/hook/index.ts +13 -13
- package/src/devtools/index.ts +439 -439
- package/src/devtools/init.ts +266 -266
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/server/source-context.ts +444 -444
- package/src/devtools/types.ts +319 -319
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +222 -222
- package/src/devtools/worker/worker-manager.ts +409 -409
- package/src/error/classifier.ts +2 -2
- package/src/error/domains.ts +265 -265
- package/src/error/formatter.ts +32 -32
- package/src/error/result.ts +46 -46
- package/src/error/stack-analyzer.ts +5 -0
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/auth.ts +308 -308
- package/src/filling/context.ts +569 -569
- package/src/filling/deps.ts +238 -238
- package/src/generator/contract-glue.ts +2 -1
- package/src/generator/generate.ts +12 -10
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.ts +80 -79
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/ast-analyzer.ts +806 -806
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +128 -128
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/cqrs.test.ts +35 -14
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/presets/index.ts +291 -291
- package/src/guard/reporter.ts +445 -445
- package/src/guard/rules.ts +12 -12
- package/src/guard/statistics.ts +578 -578
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/types.ts +348 -348
- package/src/guard/validator.ts +834 -834
- package/src/guard/watcher.ts +404 -404
- package/src/index.ts +1 -0
- package/src/intent/index.ts +310 -310
- package/src/island/index.ts +304 -304
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/paths.test.ts +47 -0
- package/src/paths.ts +47 -0
- package/src/plugins/index.ts +38 -38
- package/src/plugins/registry.ts +377 -377
- package/src/plugins/types.ts +363 -363
- package/src/report/build.ts +1 -1
- package/src/report/index.ts +1 -1
- package/src/router/fs-patterns.ts +387 -387
- package/src/router/fs-routes.ts +344 -401
- package/src/router/fs-scanner.ts +497 -497
- package/src/router/fs-types.ts +270 -278
- package/src/router/index.ts +81 -81
- package/src/runtime/boundary.tsx +232 -232
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/logger.ts +677 -677
- package/src/runtime/router.test.ts +476 -476
- package/src/runtime/router.ts +105 -105
- package/src/runtime/security.ts +155 -155
- package/src/runtime/server.ts +24 -24
- package/src/runtime/session-key.ts +328 -328
- package/src/runtime/ssr.ts +367 -367
- package/src/runtime/streaming-ssr.ts +1245 -1245
- package/src/runtime/trace.ts +144 -144
- package/src/seo/index.ts +214 -214
- package/src/seo/integration/ssr.ts +307 -307
- package/src/seo/render/basic.ts +427 -427
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +191 -191
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +126 -126
- package/src/seo/resolve/index.ts +353 -353
- package/src/seo/resolve/opengraph.ts +143 -143
- package/src/seo/resolve/robots.ts +73 -73
- package/src/seo/resolve/title.ts +94 -94
- package/src/seo/resolve/twitter.ts +73 -73
- package/src/seo/resolve/url.ts +97 -97
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +575 -575
- package/src/slot/validator.ts +39 -39
- package/src/spec/index.ts +3 -3
- package/src/spec/load.ts +76 -76
- package/src/spec/lock.ts +56 -56
- package/src/utils/bun.ts +8 -8
- package/src/utils/lru-cache.ts +75 -75
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/rules.ts +5 -5
|
@@ -1,169 +1,169 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* File Loader - 단일 파일 로더
|
|
3
|
-
*
|
|
4
|
-
* JSON, YAML, TOML 파일을 로드하고 파싱
|
|
5
|
-
*
|
|
6
|
-
* @example
|
|
7
|
-
* ```ts
|
|
8
|
-
* file({ path: 'data/settings.json' })
|
|
9
|
-
* file({ path: 'config/app.yaml' })
|
|
10
|
-
* ```
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import type { Loader, FileLoaderOptions } from "./types";
|
|
14
|
-
import type { LoaderContext } from "../types";
|
|
15
|
-
import { LoaderError, ParseError } from "../types";
|
|
16
|
-
import { generateFileDigest } from "../digest";
|
|
17
|
-
import { inferParser } from "./types";
|
|
18
|
-
import * as fs from "fs";
|
|
19
|
-
import * as path from "path";
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* JSON 파싱
|
|
23
|
-
*/
|
|
24
|
-
function parseJson(content: string, filePath: string): unknown {
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(content);
|
|
27
|
-
} catch (error) {
|
|
28
|
-
throw new ParseError(
|
|
29
|
-
`Failed to parse JSON: ${error instanceof Error ? error.message : error}`,
|
|
30
|
-
undefined,
|
|
31
|
-
filePath
|
|
32
|
-
);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* YAML 파싱 (동적 임포트)
|
|
38
|
-
*/
|
|
39
|
-
async function parseYaml(content: string, filePath: string): Promise<unknown> {
|
|
40
|
-
try {
|
|
41
|
-
// yaml 패키지 동적 로드
|
|
42
|
-
const yaml = await import("yaml").catch(() => null);
|
|
43
|
-
|
|
44
|
-
if (!yaml) {
|
|
45
|
-
throw new Error("yaml package not installed. Run: pnpm add yaml");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return yaml.parse(content);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
if (error instanceof Error && error.message.includes("not installed")) {
|
|
51
|
-
throw error;
|
|
52
|
-
}
|
|
53
|
-
throw new ParseError(
|
|
54
|
-
`Failed to parse YAML: ${error instanceof Error ? error.message : error}`,
|
|
55
|
-
undefined,
|
|
56
|
-
filePath
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* TOML 파싱 (동적 임포트)
|
|
63
|
-
*/
|
|
64
|
-
async function parseToml(content: string, filePath: string): Promise<unknown> {
|
|
65
|
-
try {
|
|
66
|
-
// @iarna/toml 또는 toml 패키지 동적 로드
|
|
67
|
-
const toml = await import("@iarna/toml").catch(() =>
|
|
68
|
-
import("toml").catch(() => null)
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
if (!toml) {
|
|
72
|
-
throw new Error("toml package not installed. Run: pnpm add @iarna/toml");
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return toml.parse(content);
|
|
76
|
-
} catch (error) {
|
|
77
|
-
if (error instanceof Error && error.message.includes("not installed")) {
|
|
78
|
-
throw error;
|
|
79
|
-
}
|
|
80
|
-
throw new ParseError(
|
|
81
|
-
`Failed to parse TOML: ${error instanceof Error ? error.message : error}`,
|
|
82
|
-
undefined,
|
|
83
|
-
filePath
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* File Loader 팩토리
|
|
90
|
-
*/
|
|
91
|
-
export function file(options: FileLoaderOptions): Loader {
|
|
92
|
-
const { path: filePath, parser: explicitParser } = options;
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
name: "file",
|
|
96
|
-
|
|
97
|
-
async load(context: LoaderContext): Promise<void> {
|
|
98
|
-
const { store, config, logger, parseData, generateDigest } = context;
|
|
99
|
-
|
|
100
|
-
// 절대 경로 계산
|
|
101
|
-
const absolutePath = path.isAbsolute(filePath)
|
|
102
|
-
? filePath
|
|
103
|
-
: path.join(config.root, filePath);
|
|
104
|
-
|
|
105
|
-
// 파일 존재 확인
|
|
106
|
-
if (!fs.existsSync(absolutePath)) {
|
|
107
|
-
throw new LoaderError(
|
|
108
|
-
`File not found: ${absolutePath}`,
|
|
109
|
-
context.collection
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// 파일 읽기
|
|
114
|
-
const content = fs.readFileSync(absolutePath, "utf-8");
|
|
115
|
-
|
|
116
|
-
// 다이제스트 생성
|
|
117
|
-
const digest = generateFileDigest(content);
|
|
118
|
-
|
|
119
|
-
// 기존 엔트리와 비교
|
|
120
|
-
const existingEntry = store.get("_single");
|
|
121
|
-
if (existingEntry?.digest === digest) {
|
|
122
|
-
logger.debug("File unchanged, skipping parse");
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// 파서 결정
|
|
127
|
-
const parserType = explicitParser ?? inferParser(absolutePath);
|
|
128
|
-
|
|
129
|
-
if (!parserType || parserType === "markdown") {
|
|
130
|
-
throw new LoaderError(
|
|
131
|
-
`Cannot determine parser for file: ${absolutePath}. Use .json, .yaml, .yml, or .toml extension, or specify parser option.`,
|
|
132
|
-
context.collection
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// 파싱
|
|
137
|
-
let rawData: unknown;
|
|
138
|
-
|
|
139
|
-
switch (parserType) {
|
|
140
|
-
case "json":
|
|
141
|
-
rawData = parseJson(content, absolutePath);
|
|
142
|
-
break;
|
|
143
|
-
case "yaml":
|
|
144
|
-
rawData = await parseYaml(content, absolutePath);
|
|
145
|
-
break;
|
|
146
|
-
case "toml":
|
|
147
|
-
rawData = await parseToml(content, absolutePath);
|
|
148
|
-
break;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// 스키마 검증
|
|
152
|
-
const data = await parseData({
|
|
153
|
-
id: "_single",
|
|
154
|
-
data: rawData,
|
|
155
|
-
filePath: absolutePath,
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
// 저장
|
|
159
|
-
store.set({
|
|
160
|
-
id: "_single",
|
|
161
|
-
data: data as Record<string, unknown>,
|
|
162
|
-
filePath: absolutePath,
|
|
163
|
-
digest,
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
logger.info(`Loaded file: ${filePath}`);
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* File Loader - 단일 파일 로더
|
|
3
|
+
*
|
|
4
|
+
* JSON, YAML, TOML 파일을 로드하고 파싱
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* file({ path: 'data/settings.json' })
|
|
9
|
+
* file({ path: 'config/app.yaml' })
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { Loader, FileLoaderOptions } from "./types";
|
|
14
|
+
import type { LoaderContext } from "../types";
|
|
15
|
+
import { LoaderError, ParseError } from "../types";
|
|
16
|
+
import { generateFileDigest } from "../digest";
|
|
17
|
+
import { inferParser } from "./types";
|
|
18
|
+
import * as fs from "fs";
|
|
19
|
+
import * as path from "path";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* JSON 파싱
|
|
23
|
+
*/
|
|
24
|
+
function parseJson(content: string, filePath: string): unknown {
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(content);
|
|
27
|
+
} catch (error) {
|
|
28
|
+
throw new ParseError(
|
|
29
|
+
`Failed to parse JSON: ${error instanceof Error ? error.message : error}`,
|
|
30
|
+
undefined,
|
|
31
|
+
filePath
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* YAML 파싱 (동적 임포트)
|
|
38
|
+
*/
|
|
39
|
+
async function parseYaml(content: string, filePath: string): Promise<unknown> {
|
|
40
|
+
try {
|
|
41
|
+
// yaml 패키지 동적 로드
|
|
42
|
+
const yaml = await import("yaml").catch(() => null);
|
|
43
|
+
|
|
44
|
+
if (!yaml) {
|
|
45
|
+
throw new Error("yaml package not installed. Run: pnpm add yaml");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return yaml.parse(content);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
if (error instanceof Error && error.message.includes("not installed")) {
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
throw new ParseError(
|
|
54
|
+
`Failed to parse YAML: ${error instanceof Error ? error.message : error}`,
|
|
55
|
+
undefined,
|
|
56
|
+
filePath
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* TOML 파싱 (동적 임포트)
|
|
63
|
+
*/
|
|
64
|
+
async function parseToml(content: string, filePath: string): Promise<unknown> {
|
|
65
|
+
try {
|
|
66
|
+
// @iarna/toml 또는 toml 패키지 동적 로드
|
|
67
|
+
const toml = await import("@iarna/toml").catch(() =>
|
|
68
|
+
import("toml").catch(() => null)
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (!toml) {
|
|
72
|
+
throw new Error("toml package not installed. Run: pnpm add @iarna/toml");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return toml.parse(content);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
if (error instanceof Error && error.message.includes("not installed")) {
|
|
78
|
+
throw error;
|
|
79
|
+
}
|
|
80
|
+
throw new ParseError(
|
|
81
|
+
`Failed to parse TOML: ${error instanceof Error ? error.message : error}`,
|
|
82
|
+
undefined,
|
|
83
|
+
filePath
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* File Loader 팩토리
|
|
90
|
+
*/
|
|
91
|
+
export function file(options: FileLoaderOptions): Loader {
|
|
92
|
+
const { path: filePath, parser: explicitParser } = options;
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
name: "file",
|
|
96
|
+
|
|
97
|
+
async load(context: LoaderContext): Promise<void> {
|
|
98
|
+
const { store, config, logger, parseData, generateDigest } = context;
|
|
99
|
+
|
|
100
|
+
// 절대 경로 계산
|
|
101
|
+
const absolutePath = path.isAbsolute(filePath)
|
|
102
|
+
? filePath
|
|
103
|
+
: path.join(config.root, filePath);
|
|
104
|
+
|
|
105
|
+
// 파일 존재 확인
|
|
106
|
+
if (!fs.existsSync(absolutePath)) {
|
|
107
|
+
throw new LoaderError(
|
|
108
|
+
`File not found: ${absolutePath}`,
|
|
109
|
+
context.collection
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 파일 읽기
|
|
114
|
+
const content = fs.readFileSync(absolutePath, "utf-8");
|
|
115
|
+
|
|
116
|
+
// 다이제스트 생성
|
|
117
|
+
const digest = generateFileDigest(content);
|
|
118
|
+
|
|
119
|
+
// 기존 엔트리와 비교
|
|
120
|
+
const existingEntry = store.get("_single");
|
|
121
|
+
if (existingEntry?.digest === digest) {
|
|
122
|
+
logger.debug("File unchanged, skipping parse");
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// 파서 결정
|
|
127
|
+
const parserType = explicitParser ?? inferParser(absolutePath);
|
|
128
|
+
|
|
129
|
+
if (!parserType || parserType === "markdown") {
|
|
130
|
+
throw new LoaderError(
|
|
131
|
+
`Cannot determine parser for file: ${absolutePath}. Use .json, .yaml, .yml, or .toml extension, or specify parser option.`,
|
|
132
|
+
context.collection
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 파싱
|
|
137
|
+
let rawData: unknown;
|
|
138
|
+
|
|
139
|
+
switch (parserType) {
|
|
140
|
+
case "json":
|
|
141
|
+
rawData = parseJson(content, absolutePath);
|
|
142
|
+
break;
|
|
143
|
+
case "yaml":
|
|
144
|
+
rawData = await parseYaml(content, absolutePath);
|
|
145
|
+
break;
|
|
146
|
+
case "toml":
|
|
147
|
+
rawData = await parseToml(content, absolutePath);
|
|
148
|
+
break;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// 스키마 검증
|
|
152
|
+
const data = await parseData({
|
|
153
|
+
id: "_single",
|
|
154
|
+
data: rawData,
|
|
155
|
+
filePath: absolutePath,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// 저장
|
|
159
|
+
store.set({
|
|
160
|
+
id: "_single",
|
|
161
|
+
data: data as Record<string, unknown>,
|
|
162
|
+
filePath: absolutePath,
|
|
163
|
+
digest,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
logger.info(`Loaded file: ${filePath}`);
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|