@mandujs/core 0.54.11 → 0.54.12

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/package.json CHANGED
@@ -1,200 +1,200 @@
1
- {
2
- "name": "@mandujs/core",
3
- "version": "0.54.11",
4
- "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
- "type": "module",
6
- "main": "./src/index.ts",
7
- "types": "./src/index.ts",
8
- "exports": {
9
- ".": "./src/index.ts",
10
- "./a11y": "./src/a11y/index.ts",
11
- "./agent": "./src/agent/index.ts",
12
- "./auth": "./src/auth/index.ts",
13
- "./auth/login": "./src/auth/login.ts",
14
- "./auth/password": "./src/auth/password.ts",
15
- "./auth/reset": "./src/auth/reset.ts",
16
- "./auth/verification": "./src/auth/verification.ts",
17
- "./client": "./src/client/index.ts",
18
- "./client/rpc": "./src/client/rpc.ts",
19
- "./contract": "./src/contract/index.ts",
20
- "./contract/rpc": "./src/contract/rpc.ts",
21
- "./content": "./src/content/index.ts",
22
- "./content/prebuild": "./src/content/prebuild.ts",
23
- "./content/collection": "./src/content/collection.ts",
24
- "./content/sidebar": "./src/content/sidebar.ts",
25
- "./content/slug": "./src/content/slug.ts",
26
- "./content/llms-txt": "./src/content/llms-txt.ts",
27
- "./content/schema": "./src/content/schema.ts",
28
- "./config": "./src/config/index.ts",
29
- "./config/mandu": "./src/config/mandu.ts",
30
- "./config/validate": "./src/config/validate.ts",
31
- "./db": "./src/db/index.ts",
32
- "./db/migrations": "./src/db/migrations/index.ts",
33
- "./db/migrations/history-table": "./src/db/migrations/history-table.ts",
34
- "./db/migrations/runner": "./src/db/migrations/runner.ts",
35
- "./deploy": "./src/deploy/index.ts",
36
- "./design": "./src/design/index.ts",
37
- "./desktop": "./src/desktop/index.ts",
38
- "./desktop/worker": "./src/desktop/worker.ts",
39
- "./email": "./src/email/index.ts",
40
- "./filling": "./src/filling/index.ts",
41
- "./filling/cookie-codec": "./src/filling/cookie-codec.ts",
42
- "./filling/session-sqlite": "./src/filling/session-sqlite.ts",
43
- "./kitchen": "./src/kitchen/index.ts",
44
- "./logging": "./src/logging/index.ts",
45
- "./middleware": "./src/middleware/index.ts",
46
- "./middleware/csrf": "./src/middleware/csrf.ts",
47
- "./middleware/oauth": "./src/middleware/oauth/index.ts",
48
- "./middleware/secure": "./src/middleware/secure/index.ts",
49
- "./middleware/rate-limit": "./src/middleware/rate-limit/index.ts",
50
- "./middleware/session": "./src/middleware/session.ts",
51
- "./testing": "./src/testing/index.ts",
52
- "./testing/db": "./src/testing/db.ts",
53
- "./testing/mocks": "./src/testing/mocks.ts",
54
- "./testing/reporter": "./src/testing/reporter.ts",
55
- "./testing/server": "./src/testing/server.ts",
56
- "./testing/session": "./src/testing/session.ts",
57
- "./testing/snapshot": "./src/testing/snapshot.ts",
58
- "./plugins": "./src/plugins/index.ts",
59
- "./plugins/define": "./src/plugins/define.ts",
60
- "./plugins/runner": "./src/plugins/runner.ts",
61
- "./error": "./src/error/index.ts",
62
- "./i18n": "./src/i18n/index.ts",
63
- "./id": "./src/id/index.ts",
64
- "./observability": "./src/observability/index.ts",
65
- "./openapi/generator": "./src/openapi/generator.ts",
66
- "./perf": "./src/perf/index.ts",
67
- "./perf/hmr-markers": "./src/perf/hmr-markers.ts",
68
- "./perf/user-marks": "./src/perf/user-marks.ts",
69
- "./resource": "./src/resource/index.ts",
70
- "./resource/ddl/diff": "./src/resource/ddl/diff.ts",
71
- "./resource/ddl/emit": "./src/resource/ddl/emit.ts",
72
- "./resource/ddl/snapshot": "./src/resource/ddl/snapshot.ts",
73
- "./resource/ddl/type-map": "./src/resource/ddl/type-map.ts",
74
- "./resource/ddl/types": "./src/resource/ddl/types.ts",
75
- "./resource/generator-repo": "./src/resource/generator-repo.ts",
76
- "./resource/parser": "./src/resource/parser.ts",
77
- "./resource/schema": "./src/resource/schema.ts",
78
- "./router": "./src/router/index.ts",
79
- "./routes": "./src/routes/index.ts",
80
- "./runtime": "./src/runtime/index.ts",
81
- "./runtime/cache": "./src/runtime/cache.ts",
82
- "./runtime/router": "./src/runtime/router.ts",
83
- "./runtime/server": "./src/runtime/server.ts",
84
- "./scheduler": "./src/scheduler/index.ts",
85
- "./storage/s3": "./src/storage/s3/index.ts",
86
- "./guard": "./src/guard/index.ts",
87
- "./guard/define-rule": "./src/guard/define-rule.ts",
88
- "./guard/design-inline-class": "./src/guard/design-inline-class.ts",
89
- "./guard/rule-presets": "./src/guard/rule-presets.ts",
90
- "./guard/tsgolint-bridge": "./src/guard/tsgolint-bridge.ts",
91
- "./bundler": "./src/bundler/index.ts",
92
- "./bundler/prerender": "./src/bundler/prerender.ts",
93
- "./bundler/safe-build": "./src/bundler/safe-build.ts",
94
- "./bundler/types": "./src/bundler/types.ts",
95
- "./bundler/hmr-types": "./src/bundler/hmr-types.ts",
96
- "./bundler/scenario-matrix": "./src/bundler/scenario-matrix.ts",
97
- "./bundler/vendor-cache-types": "./src/bundler/vendor-cache-types.ts",
98
- "./bundler/manifest-schema": "./src/bundler/manifest-schema.ts",
99
- "./bundler/analyzer": "./src/bundler/analyzer.ts",
100
- "./bundler/budget": "./src/bundler/budget.ts",
101
- "./bundler/plugins": "./src/bundler/plugins/index.ts",
102
- "./bundler/plugins/block-generated-imports": "./src/bundler/plugins/block-generated-imports.ts",
103
- "./bundler/generate-static-params": "./src/bundler/generate-static-params.ts",
104
- "./diagnose": "./src/diagnose/index.ts",
105
- "./dev-error-overlay": "./src/dev-error-overlay/index.ts",
106
- "./middleware/compose": "./src/middleware/compose.ts",
107
- "./middleware/define": "./src/middleware/define.ts",
108
- "./middleware/bridge": "./src/middleware/bridge.ts",
109
- "./client/hydrate": "./src/client/hydrate.ts",
110
- "./runtime/fast-refresh-types": "./src/runtime/fast-refresh-types.ts",
111
- "./components/Image": "./src/components/Image.tsx"
112
- },
113
- "files": [
114
- "src/**/*",
115
- "scripts/postinstall-lock.ts"
116
- ],
117
- "scripts": {
118
- "postinstall": "bun ./scripts/postinstall-lock.ts",
119
- "test": "bun test tests/streaming-ssr && bun test tests/hydration tests/typing src",
120
- "test:hydration": "bun test tests/hydration",
121
- "test:streaming": "bun test tests/streaming-ssr",
122
- "test:watch": "bun test --watch"
123
- },
124
- "devDependencies": {
125
- "@happy-dom/global-registrator": "^15.0.0"
126
- },
127
- "keywords": [
128
- "mandu",
129
- "framework",
130
- "agent",
131
- "ai",
132
- "code-generation"
133
- ],
134
- "repository": {
135
- "type": "git",
136
- "url": "git+https://github.com/konamgil/mandu.git",
137
- "directory": "packages/core"
138
- },
139
- "author": "konamgil",
140
- "license": "MPL-2.0",
141
- "publishConfig": {
142
- "access": "public"
143
- },
144
- "engines": {
145
- "bun": ">=1.3.12"
146
- },
147
- "peerDependencies": {
148
- "react": "^19.2.0",
149
- "react-dom": "^19.2.0",
150
- "react-refresh": ">=0.18.0",
151
- "@tailwindcss/cli": ">=4.0.0",
152
- "webview-bun": "^2.4.0",
153
- "axe-core": ">=4.8.0",
154
- "jsdom": ">=24.0.0",
155
- "happy-dom": ">=15.0.0",
156
- "@babel/core": "^7.29.0",
157
- "babel-plugin-react-compiler": "^1.0.0",
158
- "eslint": "^9.0.0",
159
- "eslint-plugin-react-compiler": "^19.0.0 || ^1.0.0"
160
- },
161
- "peerDependenciesMeta": {
162
- "@tailwindcss/cli": {
163
- "optional": true
164
- },
165
- "react-refresh": {
166
- "optional": true
167
- },
168
- "webview-bun": {
169
- "optional": true
170
- },
171
- "axe-core": {
172
- "optional": true
173
- },
174
- "jsdom": {
175
- "optional": true
176
- },
177
- "happy-dom": {
178
- "optional": true
179
- },
180
- "@babel/core": {
181
- "optional": true
182
- },
183
- "babel-plugin-react-compiler": {
184
- "optional": true
185
- },
186
- "eslint": {
187
- "optional": true
188
- },
189
- "eslint-plugin-react-compiler": {
190
- "optional": true
191
- }
192
- },
193
- "dependencies": {
194
- "chokidar": "^5.0.0",
195
- "fast-glob": "^3.3.2",
196
- "glob": "^13.0.0",
197
- "minimatch": "^10.1.1",
198
- "zod": "^3.23.8"
199
- }
200
- }
1
+ {
2
+ "name": "@mandujs/core",
3
+ "version": "0.54.12",
4
+ "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
+ "type": "module",
6
+ "main": "./src/index.ts",
7
+ "types": "./src/index.ts",
8
+ "exports": {
9
+ ".": "./src/index.ts",
10
+ "./a11y": "./src/a11y/index.ts",
11
+ "./agent": "./src/agent/index.ts",
12
+ "./auth": "./src/auth/index.ts",
13
+ "./auth/login": "./src/auth/login.ts",
14
+ "./auth/password": "./src/auth/password.ts",
15
+ "./auth/reset": "./src/auth/reset.ts",
16
+ "./auth/verification": "./src/auth/verification.ts",
17
+ "./client": "./src/client/index.ts",
18
+ "./client/rpc": "./src/client/rpc.ts",
19
+ "./contract": "./src/contract/index.ts",
20
+ "./contract/rpc": "./src/contract/rpc.ts",
21
+ "./content": "./src/content/index.ts",
22
+ "./content/prebuild": "./src/content/prebuild.ts",
23
+ "./content/collection": "./src/content/collection.ts",
24
+ "./content/sidebar": "./src/content/sidebar.ts",
25
+ "./content/slug": "./src/content/slug.ts",
26
+ "./content/llms-txt": "./src/content/llms-txt.ts",
27
+ "./content/schema": "./src/content/schema.ts",
28
+ "./config": "./src/config/index.ts",
29
+ "./config/mandu": "./src/config/mandu.ts",
30
+ "./config/validate": "./src/config/validate.ts",
31
+ "./db": "./src/db/index.ts",
32
+ "./db/migrations": "./src/db/migrations/index.ts",
33
+ "./db/migrations/history-table": "./src/db/migrations/history-table.ts",
34
+ "./db/migrations/runner": "./src/db/migrations/runner.ts",
35
+ "./deploy": "./src/deploy/index.ts",
36
+ "./design": "./src/design/index.ts",
37
+ "./desktop": "./src/desktop/index.ts",
38
+ "./desktop/worker": "./src/desktop/worker.ts",
39
+ "./email": "./src/email/index.ts",
40
+ "./filling": "./src/filling/index.ts",
41
+ "./filling/cookie-codec": "./src/filling/cookie-codec.ts",
42
+ "./filling/session-sqlite": "./src/filling/session-sqlite.ts",
43
+ "./kitchen": "./src/kitchen/index.ts",
44
+ "./logging": "./src/logging/index.ts",
45
+ "./middleware": "./src/middleware/index.ts",
46
+ "./middleware/csrf": "./src/middleware/csrf.ts",
47
+ "./middleware/oauth": "./src/middleware/oauth/index.ts",
48
+ "./middleware/secure": "./src/middleware/secure/index.ts",
49
+ "./middleware/rate-limit": "./src/middleware/rate-limit/index.ts",
50
+ "./middleware/session": "./src/middleware/session.ts",
51
+ "./testing": "./src/testing/index.ts",
52
+ "./testing/db": "./src/testing/db.ts",
53
+ "./testing/mocks": "./src/testing/mocks.ts",
54
+ "./testing/reporter": "./src/testing/reporter.ts",
55
+ "./testing/server": "./src/testing/server.ts",
56
+ "./testing/session": "./src/testing/session.ts",
57
+ "./testing/snapshot": "./src/testing/snapshot.ts",
58
+ "./plugins": "./src/plugins/index.ts",
59
+ "./plugins/define": "./src/plugins/define.ts",
60
+ "./plugins/runner": "./src/plugins/runner.ts",
61
+ "./error": "./src/error/index.ts",
62
+ "./i18n": "./src/i18n/index.ts",
63
+ "./id": "./src/id/index.ts",
64
+ "./observability": "./src/observability/index.ts",
65
+ "./openapi/generator": "./src/openapi/generator.ts",
66
+ "./perf": "./src/perf/index.ts",
67
+ "./perf/hmr-markers": "./src/perf/hmr-markers.ts",
68
+ "./perf/user-marks": "./src/perf/user-marks.ts",
69
+ "./resource": "./src/resource/index.ts",
70
+ "./resource/ddl/diff": "./src/resource/ddl/diff.ts",
71
+ "./resource/ddl/emit": "./src/resource/ddl/emit.ts",
72
+ "./resource/ddl/snapshot": "./src/resource/ddl/snapshot.ts",
73
+ "./resource/ddl/type-map": "./src/resource/ddl/type-map.ts",
74
+ "./resource/ddl/types": "./src/resource/ddl/types.ts",
75
+ "./resource/generator-repo": "./src/resource/generator-repo.ts",
76
+ "./resource/parser": "./src/resource/parser.ts",
77
+ "./resource/schema": "./src/resource/schema.ts",
78
+ "./router": "./src/router/index.ts",
79
+ "./routes": "./src/routes/index.ts",
80
+ "./runtime": "./src/runtime/index.ts",
81
+ "./runtime/cache": "./src/runtime/cache.ts",
82
+ "./runtime/router": "./src/runtime/router.ts",
83
+ "./runtime/server": "./src/runtime/server.ts",
84
+ "./scheduler": "./src/scheduler/index.ts",
85
+ "./storage/s3": "./src/storage/s3/index.ts",
86
+ "./guard": "./src/guard/index.ts",
87
+ "./guard/define-rule": "./src/guard/define-rule.ts",
88
+ "./guard/design-inline-class": "./src/guard/design-inline-class.ts",
89
+ "./guard/rule-presets": "./src/guard/rule-presets.ts",
90
+ "./guard/tsgolint-bridge": "./src/guard/tsgolint-bridge.ts",
91
+ "./bundler": "./src/bundler/index.ts",
92
+ "./bundler/prerender": "./src/bundler/prerender.ts",
93
+ "./bundler/safe-build": "./src/bundler/safe-build.ts",
94
+ "./bundler/types": "./src/bundler/types.ts",
95
+ "./bundler/hmr-types": "./src/bundler/hmr-types.ts",
96
+ "./bundler/scenario-matrix": "./src/bundler/scenario-matrix.ts",
97
+ "./bundler/vendor-cache-types": "./src/bundler/vendor-cache-types.ts",
98
+ "./bundler/manifest-schema": "./src/bundler/manifest-schema.ts",
99
+ "./bundler/analyzer": "./src/bundler/analyzer.ts",
100
+ "./bundler/budget": "./src/bundler/budget.ts",
101
+ "./bundler/plugins": "./src/bundler/plugins/index.ts",
102
+ "./bundler/plugins/block-generated-imports": "./src/bundler/plugins/block-generated-imports.ts",
103
+ "./bundler/generate-static-params": "./src/bundler/generate-static-params.ts",
104
+ "./diagnose": "./src/diagnose/index.ts",
105
+ "./dev-error-overlay": "./src/dev-error-overlay/index.ts",
106
+ "./middleware/compose": "./src/middleware/compose.ts",
107
+ "./middleware/define": "./src/middleware/define.ts",
108
+ "./middleware/bridge": "./src/middleware/bridge.ts",
109
+ "./client/hydrate": "./src/client/hydrate.ts",
110
+ "./runtime/fast-refresh-types": "./src/runtime/fast-refresh-types.ts",
111
+ "./components/Image": "./src/components/Image.tsx"
112
+ },
113
+ "files": [
114
+ "src/**/*",
115
+ "scripts/postinstall-lock.ts"
116
+ ],
117
+ "scripts": {
118
+ "postinstall": "bun ./scripts/postinstall-lock.ts",
119
+ "test": "bun test tests/streaming-ssr && bun test tests/hydration tests/typing src",
120
+ "test:hydration": "bun test tests/hydration",
121
+ "test:streaming": "bun test tests/streaming-ssr",
122
+ "test:watch": "bun test --watch"
123
+ },
124
+ "devDependencies": {
125
+ "@happy-dom/global-registrator": "^15.0.0"
126
+ },
127
+ "keywords": [
128
+ "mandu",
129
+ "framework",
130
+ "agent",
131
+ "ai",
132
+ "code-generation"
133
+ ],
134
+ "repository": {
135
+ "type": "git",
136
+ "url": "git+https://github.com/konamgil/mandu.git",
137
+ "directory": "packages/core"
138
+ },
139
+ "author": "konamgil",
140
+ "license": "MPL-2.0",
141
+ "publishConfig": {
142
+ "access": "public"
143
+ },
144
+ "engines": {
145
+ "bun": ">=1.3.12"
146
+ },
147
+ "peerDependencies": {
148
+ "react": "^19.2.0",
149
+ "react-dom": "^19.2.0",
150
+ "react-refresh": ">=0.18.0",
151
+ "@tailwindcss/cli": ">=4.0.0",
152
+ "webview-bun": "^2.4.0",
153
+ "axe-core": ">=4.8.0",
154
+ "jsdom": ">=24.0.0",
155
+ "happy-dom": ">=15.0.0",
156
+ "@babel/core": "^7.29.0",
157
+ "babel-plugin-react-compiler": "^1.0.0",
158
+ "eslint": "^9.0.0",
159
+ "eslint-plugin-react-compiler": "^19.0.0 || ^1.0.0"
160
+ },
161
+ "peerDependenciesMeta": {
162
+ "@tailwindcss/cli": {
163
+ "optional": true
164
+ },
165
+ "react-refresh": {
166
+ "optional": true
167
+ },
168
+ "webview-bun": {
169
+ "optional": true
170
+ },
171
+ "axe-core": {
172
+ "optional": true
173
+ },
174
+ "jsdom": {
175
+ "optional": true
176
+ },
177
+ "happy-dom": {
178
+ "optional": true
179
+ },
180
+ "@babel/core": {
181
+ "optional": true
182
+ },
183
+ "babel-plugin-react-compiler": {
184
+ "optional": true
185
+ },
186
+ "eslint": {
187
+ "optional": true
188
+ },
189
+ "eslint-plugin-react-compiler": {
190
+ "optional": true
191
+ }
192
+ },
193
+ "dependencies": {
194
+ "chokidar": "^5.0.0",
195
+ "fast-glob": "^3.3.2",
196
+ "glob": "^13.0.0",
197
+ "minimatch": "^10.1.1",
198
+ "zod": "^3.23.8"
199
+ }
200
+ }
@@ -237,15 +237,30 @@ describe("buildClientBundles vendor shims", () => {
237
237
  "}\n",
238
238
  "utf-8",
239
239
  );
240
- await writeFile(
241
- path.join(routeClientRoot, "src", "client", "pages", "login", "LoginPage.client.tsx"),
242
- '"use client";\nexport default function LoginPage() { return <form />; }\n',
243
- "utf-8",
244
- );
240
+ await writeFile(
241
+ path.join(routeClientRoot, "src", "client", "pages", "login", "LoginPage.client.tsx"),
242
+ '"use client";\n' +
243
+ 'import { useState } from "react";\n' +
244
+ "export default function LoginPage() {\n" +
245
+ ' const [email, setEmail] = useState("");\n' +
246
+ ' return <form><input value={email} onChange={(event) => setEmail(event.currentTarget.value)} /></form>;\n' +
247
+ "}\n",
248
+ "utf-8",
249
+ );
245
250
 
246
251
  const routeClientResult = await runBuildInSubprocess(routeClientRoot, "server-page-route-client-import");
247
252
  expect(routeClientResult.success).toBe(true);
248
- expect(await Bun.file(path.join(routeClientRoot, ".mandu", "client", "login.island.js")).exists()).toBe(true);
253
+ const bundlePath = path.join(routeClientRoot, ".mandu", "client", "login.island.js");
254
+ expect(await Bun.file(bundlePath).exists()).toBe(true);
255
+
256
+ const bundleSource = await readFile(bundlePath, "utf-8");
257
+ expect(bundleSource).not.toContain("var LoginPage = LoginPage;");
258
+ const parseResult = await Bun.build({
259
+ entrypoints: [bundlePath],
260
+ target: "browser",
261
+ external: ["react", "react-dom", "react-dom/client", "react/jsx-dev-runtime"],
262
+ });
263
+ expect(parseResult.success).toBe(true);
249
264
  } finally {
250
265
  await rm(routeClientRoot, { recursive: true, force: true });
251
266
  }
@@ -100,13 +100,16 @@ function shouldSplitChunks(options: BundlerOptions): boolean {
100
100
  return options.splitting ?? (resolveBundlerMode(options) === "production");
101
101
  }
102
102
 
103
- function nodeEnvDefine(options: BundlerOptions): string {
104
- return JSON.stringify(resolveBundlerMode(options));
105
- }
106
-
107
- function resolveClientOutDir(rootDir: string, outDir?: string): string {
108
- const defaultOutDir = path.join(rootDir, ".mandu/client");
109
- if (!outDir) return defaultOutDir;
103
+ function nodeEnvDefine(options: BundlerOptions): string {
104
+ return JSON.stringify(resolveBundlerMode(options));
105
+ }
106
+
107
+ const FAST_REFRESH_SELF_ALIAS_PATTERN =
108
+ /^var\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*\1;\s*\r?\n(?=\$RefreshReg\$\(\1,\s*["'][^"']*:default["']\);)/gm;
109
+
110
+ function resolveClientOutDir(rootDir: string, outDir?: string): string {
111
+ const defaultOutDir = path.join(rootDir, ".mandu/client");
112
+ if (!outDir) return defaultOutDir;
110
113
 
111
114
  const resolvedOutDir = path.isAbsolute(outDir) ? outDir : path.resolve(rootDir, outDir);
112
115
  if (path.normalize(resolvedOutDir) === path.normalize(path.join(rootDir, ".mandu"))) {
@@ -293,9 +296,9 @@ export async function collectCompilerLintTargets(
293
296
  }
294
297
 
295
298
  /** Build a single per-island bundle. */
296
- async function buildPerIslandBundle(
297
- entry: IslandFileEntry, outDir: string, options: BundlerOptions
298
- ): Promise<{ name: string; js: string; route: string; priority: IslandFileEntry["priority"] }> {
299
+ async function buildPerIslandBundle(
300
+ entry: IslandFileEntry, outDir: string, options: BundlerOptions
301
+ ): Promise<{ name: string; js: string; route: string; priority: IslandFileEntry["priority"] }> {
299
302
  const entryPath = path.join(outDir, `_entry_island_${entry.name}.js`);
300
303
  const outputName = `${entry.name}.island.js`;
301
304
  // Phase 7.1 B-1/B-4: wire Bun's native React Fast Refresh transform +
@@ -317,12 +320,13 @@ async function buildPerIslandBundle(
317
320
  define: { "process.env.NODE_ENV": nodeEnvDefine(options), ...options.define },
318
321
  });
319
322
  await fs.unlink(entryPath).catch(() => {});
320
- if (!result.success) {
321
- const grouped = result.logs.map((l) => ` - ${l.message}`).join("\n");
322
- throw new Error(`Island build failed for '${entry.name}' (source: ${entry.filePath}):\n${grouped}\n Hint: Check the import paths and TypeScript types in this island file.`);
323
- }
324
- return { name: entry.name, js: `/.mandu/client/${outputName}`, route: entry.routeId, priority: entry.priority };
325
- } catch (error) {
323
+ if (!result.success) {
324
+ const grouped = result.logs.map((l) => ` - ${l.message}`).join("\n");
325
+ throw new Error(`Island build failed for '${entry.name}' (source: ${entry.filePath}):\n${grouped}\n Hint: Check the import paths and TypeScript types in this island file.`);
326
+ }
327
+ await sanitizeGeneratedClientBundle(path.join(outDir, outputName), isDev);
328
+ return { name: entry.name, js: `/.mandu/client/${outputName}`, route: entry.routeId, priority: entry.priority };
329
+ } catch (error) {
326
330
  await fs.unlink(entryPath).catch(() => {});
327
331
  throw error;
328
332
  }
@@ -366,11 +370,11 @@ async function buildPartialBundle(
366
370
  const grouped = result.logs.map((l) => ` - ${l.message}`).join("\n");
367
371
  throw new Error(`Partial build failed for '${entry.name}' (source: ${entry.filePath}):\n${grouped}\n Hint: Export a Mandu partial from this file with \`partial({ component })\`.`);
368
372
  }
369
-
370
- const outputPath = path.join(outDir, outputName);
371
- const outputFile = Bun.file(outputPath);
372
- const content = await outputFile.text();
373
- const gzipped = Bun.gzipSync(Buffer.from(content));
373
+
374
+ const outputPath = path.join(outDir, outputName);
375
+ const sanitizedContent = await sanitizeGeneratedClientBundle(outputPath, isDev);
376
+ const outputFile = Bun.file(outputPath);
377
+ const gzipped = Bun.gzipSync(Buffer.from(sanitizedContent));
374
378
 
375
379
  return {
376
380
  name: entry.name,
@@ -1959,9 +1963,9 @@ async function buildIsland(
1959
1963
  actualOutputName = outputName;
1960
1964
  }
1961
1965
 
1962
- const outputFile = Bun.file(actualOutputPath);
1963
- const content = await outputFile.text();
1964
- const gzipped = Bun.gzipSync(Buffer.from(content));
1966
+ const outputFile = Bun.file(actualOutputPath);
1967
+ const content = await sanitizeGeneratedClientBundle(actualOutputPath, isDev);
1968
+ const gzipped = Bun.gzipSync(Buffer.from(content));
1965
1969
 
1966
1970
  return {
1967
1971
  routeId: route.id,
@@ -1973,11 +1977,53 @@ async function buildIsland(
1973
1977
  } catch (error) {
1974
1978
  await fs.unlink(entryPath).catch(() => {});
1975
1979
  throw error;
1976
- }
1977
- }
1978
-
1979
- /**
1980
- * 번들 매니페스트 생성
1980
+ }
1981
+ }
1982
+
1983
+ async function sanitizeGeneratedClientBundle(outputPath: string, isDev: boolean): Promise<string> {
1984
+ const source = await Bun.file(outputPath).text();
1985
+ if (!isDev) return source;
1986
+
1987
+ const sanitized = removeFastRefreshSelfAliases(source);
1988
+ if (sanitized === source) return source;
1989
+
1990
+ await Bun.write(outputPath, sanitized);
1991
+ await validateGeneratedClientBundle(outputPath);
1992
+ return sanitized;
1993
+ }
1994
+
1995
+ function removeFastRefreshSelfAliases(source: string): string {
1996
+ return source.replace(FAST_REFRESH_SELF_ALIAS_PATTERN, (line, localName: string, offset: number) => {
1997
+ const priorSource = source.slice(0, offset);
1998
+ const declarationPattern = new RegExp(`(?:const|let|class|function)\\s+${escapeRegExp(localName)}\\b`);
1999
+ return declarationPattern.test(priorSource) ? "" : line;
2000
+ });
2001
+ }
2002
+
2003
+ async function validateGeneratedClientBundle(outputPath: string): Promise<void> {
2004
+ const tempOutDir = await fs.mkdtemp(path.join(path.dirname(outputPath), ".validate-"));
2005
+ try {
2006
+ const result = await safeBuild({
2007
+ entrypoints: [outputPath],
2008
+ outdir: tempOutDir,
2009
+ target: "browser",
2010
+ external: ["react", "react-dom", "react-dom/client", "react/jsx-runtime", "react/jsx-dev-runtime"],
2011
+ });
2012
+ if (result.success) return;
2013
+
2014
+ const grouped = result.logs.map((log) => ` - ${log.message}`).join("\n");
2015
+ throw new Error(`Generated client bundle failed syntax validation (source: ${outputPath}):\n${grouped}`);
2016
+ } finally {
2017
+ await fs.rm(tempOutDir, { recursive: true, force: true }).catch(() => {});
2018
+ }
2019
+ }
2020
+
2021
+ function escapeRegExp(value: string): string {
2022
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2023
+ }
2024
+
2025
+ /**
2026
+ * 번들 매니페스트 생성
1981
2027
  */
1982
2028
  function createBundleManifest(
1983
2029
  outputs: BundleOutput[],