@mandujs/core 0.18.22 → 0.19.2
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 +0 -14
- package/package.json +4 -1
- package/src/brain/architecture/analyzer.ts +4 -4
- package/src/brain/doctor/analyzer.ts +18 -14
- package/src/bundler/build.test.ts +127 -0
- package/src/bundler/build.ts +291 -113
- package/src/bundler/css.ts +20 -5
- package/src/bundler/dev.ts +55 -2
- package/src/bundler/prerender.ts +195 -0
- package/src/change/snapshot.ts +4 -23
- package/src/change/types.ts +2 -3
- package/src/client/Form.tsx +105 -0
- package/src/client/__tests__/use-sse.test.ts +153 -0
- package/src/client/hooks.ts +105 -6
- package/src/client/index.ts +35 -6
- package/src/client/router.ts +670 -433
- package/src/client/rpc.ts +140 -0
- package/src/client/runtime.ts +24 -21
- package/src/client/use-fetch.ts +239 -0
- package/src/client/use-head.ts +197 -0
- package/src/client/use-sse.ts +378 -0
- package/src/components/Image.tsx +162 -0
- package/src/config/mandu.ts +5 -0
- package/src/config/validate.ts +34 -0
- package/src/content/index.ts +5 -1
- package/src/devtools/client/catchers/error-catcher.ts +17 -0
- package/src/devtools/client/catchers/network-proxy.ts +390 -367
- package/src/devtools/client/components/kitchen-root.tsx +479 -467
- package/src/devtools/client/components/panel/diff-viewer.tsx +219 -0
- package/src/devtools/client/components/panel/guard-panel.tsx +374 -244
- package/src/devtools/client/components/panel/index.ts +45 -32
- package/src/devtools/client/components/panel/panel-container.tsx +332 -312
- package/src/devtools/client/components/panel/preview-panel.tsx +188 -0
- package/src/devtools/client/state-manager.ts +535 -478
- package/src/devtools/design-tokens.ts +265 -264
- package/src/devtools/types.ts +345 -319
- package/src/filling/context.ts +65 -0
- package/src/filling/filling.ts +336 -14
- package/src/filling/index.ts +5 -1
- package/src/filling/session.ts +216 -0
- package/src/filling/ws.ts +78 -0
- package/src/generator/generate.ts +2 -2
- package/src/guard/auto-correct.ts +0 -29
- package/src/guard/check.ts +14 -31
- package/src/guard/presets/index.ts +296 -294
- package/src/guard/rules.ts +15 -19
- package/src/guard/validator.ts +834 -834
- package/src/index.ts +5 -1
- package/src/island/index.ts +373 -304
- package/src/kitchen/api/contract-api.ts +225 -0
- package/src/kitchen/api/diff-parser.ts +108 -0
- package/src/kitchen/api/file-api.ts +273 -0
- package/src/kitchen/api/guard-api.ts +83 -0
- package/src/kitchen/api/guard-decisions.ts +100 -0
- package/src/kitchen/api/routes-api.ts +50 -0
- package/src/kitchen/index.ts +21 -0
- package/src/kitchen/kitchen-handler.ts +256 -0
- package/src/kitchen/kitchen-ui.ts +1732 -0
- package/src/kitchen/stream/activity-sse.ts +145 -0
- package/src/kitchen/stream/file-tailer.ts +99 -0
- package/src/middleware/compress.ts +62 -0
- package/src/middleware/cors.ts +47 -0
- package/src/middleware/index.ts +10 -0
- package/src/middleware/jwt.ts +134 -0
- package/src/middleware/logger.ts +58 -0
- package/src/middleware/timeout.ts +55 -0
- package/src/paths.ts +0 -4
- package/src/plugins/hooks.ts +64 -0
- package/src/plugins/index.ts +3 -0
- package/src/plugins/types.ts +5 -0
- package/src/report/build.ts +0 -6
- package/src/resource/__tests__/backward-compat.test.ts +0 -1
- package/src/router/fs-patterns.ts +11 -1
- package/src/router/fs-routes.ts +78 -14
- package/src/router/fs-scanner.ts +2 -2
- package/src/router/fs-types.ts +2 -1
- package/src/runtime/adapter-bun.ts +62 -0
- package/src/runtime/adapter.ts +47 -0
- package/src/runtime/cache.ts +310 -0
- package/src/runtime/handler.ts +65 -0
- package/src/runtime/image-handler.ts +195 -0
- package/src/runtime/index.ts +12 -0
- package/src/runtime/middleware.ts +263 -0
- package/src/runtime/server.ts +686 -92
- package/src/runtime/ssr.ts +55 -29
- package/src/runtime/streaming-ssr.ts +106 -82
- package/src/spec/index.ts +0 -1
- package/src/spec/schema.ts +1 -0
- package/src/testing/index.ts +144 -0
- package/src/watcher/watcher.ts +27 -1
- package/src/spec/lock.ts +0 -56
package/README.ko.md
CHANGED
|
@@ -55,19 +55,6 @@ if (result.success && result.data) {
|
|
|
55
55
|
}
|
|
56
56
|
```
|
|
57
57
|
|
|
58
|
-
### Lock 파일
|
|
59
|
-
|
|
60
|
-
```typescript
|
|
61
|
-
import { writeLock, readLock } from "@mandujs/core";
|
|
62
|
-
|
|
63
|
-
// lock 파일 쓰기
|
|
64
|
-
const lock = await writeLock(".mandu/spec.lock.json", manifest);
|
|
65
|
-
console.log(lock.routesHash);
|
|
66
|
-
|
|
67
|
-
// lock 파일 읽기
|
|
68
|
-
const existing = await readLock(".mandu/spec.lock.json");
|
|
69
|
-
```
|
|
70
|
-
|
|
71
58
|
## Generator 모듈
|
|
72
59
|
|
|
73
60
|
Spec 기반 코드 생성.
|
|
@@ -132,7 +119,6 @@ if (!result.passed) {
|
|
|
132
119
|
|
|
133
120
|
| 규칙 ID | 설명 | 자동 수정 |
|
|
134
121
|
|---------|------|----------|
|
|
135
|
-
| `SPEC_HASH_MISMATCH` | spec과 lock 해시 불일치 | ✅ |
|
|
136
122
|
| `GENERATED_MANUAL_EDIT` | generated 파일 수동 수정 | ✅ |
|
|
137
123
|
| `HANDLER_NOT_FOUND` | 핸들러 파일 없음 | ❌ |
|
|
138
124
|
| `COMPONENT_NOT_FOUND` | 컴포넌트 파일 없음 | ❌ |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mandujs/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.2",
|
|
4
4
|
"description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./src/index.ts",
|
|
10
10
|
"./client": "./src/client/index.ts",
|
|
11
|
+
"./middleware": "./src/middleware/index.ts",
|
|
12
|
+
"./testing": "./src/testing/index.ts",
|
|
11
13
|
"./plugins": "./src/plugins/index.ts",
|
|
12
14
|
"./error": "./src/error/index.ts",
|
|
15
|
+
"./bundler/prerender": "./src/bundler/prerender.ts",
|
|
13
16
|
"./*": "./src/*"
|
|
14
17
|
},
|
|
15
18
|
"files": [
|
|
@@ -34,7 +34,7 @@ export const DEFAULT_ARCHITECTURE_CONFIG: ArchitectureConfig = {
|
|
|
34
34
|
"spec/slots/": {
|
|
35
35
|
pattern: "spec/slots/**",
|
|
36
36
|
description: "Slot 파일 전용",
|
|
37
|
-
allowedFiles: ["*.slot.ts", "*.client.ts"],
|
|
37
|
+
allowedFiles: ["*.slot.ts", "*.slot.tsx", "*.client.ts", "*.client.tsx"],
|
|
38
38
|
},
|
|
39
39
|
"spec/contracts/": {
|
|
40
40
|
pattern: "spec/contracts/**",
|
|
@@ -96,9 +96,9 @@ export const DEFAULT_ARCHITECTURE_CONFIG: ArchitectureConfig = {
|
|
|
96
96
|
naming: [
|
|
97
97
|
{
|
|
98
98
|
folder: "spec/slots/",
|
|
99
|
-
filePattern: "^[a-z][a-z0-9-]*\\.(slot|client)\\.
|
|
100
|
-
description: "Slot 파일은 kebab-case.slot.ts 또는 kebab-case.client.ts",
|
|
101
|
-
examples: ["users-list.slot.ts", "counter.client.
|
|
99
|
+
filePattern: "^[a-z][a-z0-9-]*\\.(slot|client)\\.tsx?$",
|
|
100
|
+
description: "Slot 파일은 kebab-case.slot.ts(x) 또는 kebab-case.client.ts(x)",
|
|
101
|
+
examples: ["users-list.slot.ts", "counter.client.tsx"],
|
|
102
102
|
},
|
|
103
103
|
{
|
|
104
104
|
folder: "spec/contracts/",
|
|
@@ -25,7 +25,7 @@ export type ViolationCategory =
|
|
|
25
25
|
* Categorize a violation by its rule ID
|
|
26
26
|
*/
|
|
27
27
|
export function categorizeViolation(ruleId: string): ViolationCategory {
|
|
28
|
-
if (ruleId.includes("SPEC")
|
|
28
|
+
if (ruleId.includes("SPEC")) {
|
|
29
29
|
return "spec";
|
|
30
30
|
}
|
|
31
31
|
if (ruleId.includes("GENERATED") || ruleId.includes("FORBIDDEN_IMPORT")) {
|
|
@@ -138,16 +138,6 @@ export function generateTemplatePatches(
|
|
|
138
138
|
|
|
139
139
|
for (const violation of violations) {
|
|
140
140
|
switch (violation.ruleId) {
|
|
141
|
-
case GUARD_RULES.SPEC_HASH_MISMATCH?.id:
|
|
142
|
-
patches.push({
|
|
143
|
-
file: ".mandu/spec.lock.json",
|
|
144
|
-
description: "Spec lock 파일 갱신",
|
|
145
|
-
type: "command",
|
|
146
|
-
command: "bunx mandu spec-upsert",
|
|
147
|
-
confidence: 0.9,
|
|
148
|
-
});
|
|
149
|
-
break;
|
|
150
|
-
|
|
151
141
|
case GUARD_RULES.GENERATED_MANUAL_EDIT?.id:
|
|
152
142
|
patches.push({
|
|
153
143
|
file: violation.file,
|
|
@@ -208,6 +198,23 @@ export function generateTemplatePatches(
|
|
|
208
198
|
});
|
|
209
199
|
break;
|
|
210
200
|
|
|
201
|
+
case GUARD_RULES.ISLAND_FIRST_INTEGRITY?.id:
|
|
202
|
+
patches.push({
|
|
203
|
+
file: violation.file,
|
|
204
|
+
description:
|
|
205
|
+
"Create a .island.tsx file in the same app/ directory as page.tsx. " +
|
|
206
|
+
"Do NOT import or re-export the island in page.tsx — island() returns " +
|
|
207
|
+
"a config object, not a React component. Use data-island attributes instead.",
|
|
208
|
+
type: "modify",
|
|
209
|
+
content:
|
|
210
|
+
`// Example: app/my-feature.island.tsx\n` +
|
|
211
|
+
`import { island } from "@mandujs/core/client";\n\n` +
|
|
212
|
+
`export default island("visible", MyComponent);\n\n` +
|
|
213
|
+
`// In page.tsx, reference via: <div data-island="my-feature">...</div>`,
|
|
214
|
+
confidence: 0.9,
|
|
215
|
+
});
|
|
216
|
+
break;
|
|
217
|
+
|
|
211
218
|
default:
|
|
212
219
|
// Generic suggestion based on violation.suggestion
|
|
213
220
|
if (violation.suggestion) {
|
|
@@ -307,9 +314,6 @@ export async function analyzeViolations(
|
|
|
307
314
|
|
|
308
315
|
// Determine recommended next command
|
|
309
316
|
let nextCommand = "bunx mandu generate";
|
|
310
|
-
if (violations.some((v) => v.ruleId === GUARD_RULES.SPEC_HASH_MISMATCH?.id)) {
|
|
311
|
-
nextCommand = "bunx mandu spec-upsert";
|
|
312
|
-
}
|
|
313
317
|
|
|
314
318
|
// If LLM is not requested or not available, return template analysis
|
|
315
319
|
if (!useLLM) {
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
|
|
2
|
+
import { mkdtemp, mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { pathToFileURL } from "url";
|
|
5
|
+
import type { RoutesManifest } from "../spec/schema";
|
|
6
|
+
import type { BundleResult } from "./types";
|
|
7
|
+
import { buildClientBundles } from "./build";
|
|
8
|
+
|
|
9
|
+
// 모든 테스트가 하나의 빌드 결과를 공유 — 병렬 Bun.build 충돌 방지
|
|
10
|
+
let rootDir: string;
|
|
11
|
+
let result: BundleResult;
|
|
12
|
+
|
|
13
|
+
async function importBuiltModule(relativePath: string): Promise<Record<string, unknown>> {
|
|
14
|
+
const fileUrl = pathToFileURL(path.join(rootDir, relativePath)).href;
|
|
15
|
+
return import(`${fileUrl}?t=${Date.now()}`);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
rootDir = await mkdtemp(path.join(import.meta.dir, ".tmp-bundler-"));
|
|
20
|
+
|
|
21
|
+
await mkdir(path.join(rootDir, "app"), { recursive: true });
|
|
22
|
+
await writeFile(
|
|
23
|
+
path.join(rootDir, "package.json"),
|
|
24
|
+
JSON.stringify({ name: "mandu-build-test", type: "module" }, null, 2),
|
|
25
|
+
"utf-8",
|
|
26
|
+
);
|
|
27
|
+
await writeFile(
|
|
28
|
+
path.join(rootDir, "app", "demo.client.tsx"),
|
|
29
|
+
"export default function DemoIsland() { return null; }\n",
|
|
30
|
+
"utf-8",
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const manifest: RoutesManifest = {
|
|
34
|
+
version: 1,
|
|
35
|
+
routes: [
|
|
36
|
+
{
|
|
37
|
+
id: "demo",
|
|
38
|
+
kind: "page",
|
|
39
|
+
pattern: "/",
|
|
40
|
+
module: "app/page.tsx",
|
|
41
|
+
componentModule: "app/page.tsx",
|
|
42
|
+
clientModule: "app/demo.client.tsx",
|
|
43
|
+
hydration: {
|
|
44
|
+
strategy: "island",
|
|
45
|
+
priority: "visible",
|
|
46
|
+
preload: false,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
result = await buildClientBundles(manifest, rootDir, {
|
|
53
|
+
minify: false,
|
|
54
|
+
sourcemap: false,
|
|
55
|
+
splitting: false,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
afterAll(async () => {
|
|
60
|
+
if (rootDir) {
|
|
61
|
+
await rm(rootDir, { recursive: true, force: true });
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("buildClientBundles vendor shims", () => {
|
|
66
|
+
test("build succeeds", () => {
|
|
67
|
+
if (!result.success) {
|
|
68
|
+
console.error("[build.test] errors:", result.errors);
|
|
69
|
+
}
|
|
70
|
+
expect(result.success).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("re-exports modern React 19 APIs used by islands", async () => {
|
|
74
|
+
const reactShim = await importBuiltModule(".mandu/client/_react.js");
|
|
75
|
+
const requiredExports = [
|
|
76
|
+
"Activity",
|
|
77
|
+
"__COMPILER_RUNTIME",
|
|
78
|
+
"cache",
|
|
79
|
+
"cacheSignal",
|
|
80
|
+
"startTransition",
|
|
81
|
+
"use",
|
|
82
|
+
"useActionState",
|
|
83
|
+
"useEffectEvent",
|
|
84
|
+
"useOptimistic",
|
|
85
|
+
"unstable_useCacheRefresh",
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
for (const exportName of requiredExports) {
|
|
89
|
+
expect(exportName in reactShim).toBe(true);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("re-exports modern react-dom and react-dom/client APIs", async () => {
|
|
94
|
+
const reactDomShim = await importBuiltModule(".mandu/client/_react-dom.js");
|
|
95
|
+
for (const exportName of [
|
|
96
|
+
"preconnect",
|
|
97
|
+
"prefetchDNS",
|
|
98
|
+
"preinit",
|
|
99
|
+
"preinitModule",
|
|
100
|
+
"preload",
|
|
101
|
+
"preloadModule",
|
|
102
|
+
"requestFormReset",
|
|
103
|
+
"unstable_batchedUpdates",
|
|
104
|
+
"useFormState",
|
|
105
|
+
"useFormStatus",
|
|
106
|
+
]) {
|
|
107
|
+
expect(exportName in reactDomShim).toBe(true);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const reactDomClientShim = await importBuiltModule(".mandu/client/_react-dom-client.js");
|
|
111
|
+
for (const exportName of ["createRoot", "hydrateRoot", "version"]) {
|
|
112
|
+
expect(exportName in reactDomClientShim).toBe(true);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("embeds hydration guards for deferred trigger strategies", async () => {
|
|
117
|
+
const runtimeSource = await readFile(path.join(rootDir, ".mandu", "client", "_runtime.js"), "utf-8");
|
|
118
|
+
expect(runtimeSource).toContain("function resolveHydrationTarget");
|
|
119
|
+
expect(runtimeSource).toContain("function hasHydratableMarkup");
|
|
120
|
+
expect(runtimeSource).toContain("function shouldHydrateCompiledIsland");
|
|
121
|
+
expect(runtimeSource).toContain("onRecoverableError");
|
|
122
|
+
expect(runtimeSource).toContain("data-mandu-hydrating");
|
|
123
|
+
expect(runtimeSource).toContain("data-mandu-render-mode");
|
|
124
|
+
expect(runtimeSource).toContain("data-mandu-recoverable-error");
|
|
125
|
+
expect(runtimeSource).toContain("pointerdown");
|
|
126
|
+
});
|
|
127
|
+
});
|