@mandujs/core 0.54.32 → 0.55.0-beta.1
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.md +654 -654
- package/package.json +106 -210
- package/scripts/postinstall-lock.ts +153 -153
- package/src/a11y/__tests__/run-audit.test.ts +333 -333
- package/src/a11y/fix-hints.ts +76 -76
- package/src/a11y/index.ts +18 -18
- package/src/a11y/run-audit.ts +15 -15
- package/src/a11y/types.ts +125 -125
- package/src/agent/__tests__/apply.test.ts +224 -0
- package/src/agent/__tests__/context.test.ts +97 -98
- package/src/agent/apply.ts +1031 -0
- package/src/agent/context.ts +552 -552
- package/src/agent/index.ts +4 -3
- package/src/agent/plan.ts +161 -243
- package/src/agent/repair.ts +239 -162
- package/src/agent/sync.ts +197 -198
- package/src/agent/types.ts +242 -94
- package/src/agent/verify.ts +55 -55
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +15 -5
- package/src/auth/__tests__/reset.test.ts +2 -2
- package/src/auth/__tests__/tokens.test.ts +274 -274
- package/src/auth/__tests__/verification.test.ts +274 -274
- package/src/auth/index.ts +76 -76
- package/src/auth/login.ts +225 -225
- package/src/auth/password.ts +14 -2
- package/src/auth/reset.ts +243 -243
- package/src/auth/tokens.ts +612 -612
- package/src/auth/verification.ts +253 -253
- package/src/brain/__tests__/redactor.test.ts +94 -94
- package/src/brain/adapters/__tests__/_helpers.ts +83 -83
- package/src/brain/adapters/__tests__/anthropic-oauth.test.ts +196 -196
- package/src/brain/adapters/__tests__/chatgpt-auth.test.ts +193 -193
- package/src/brain/adapters/__tests__/openai-oauth.test.ts +209 -209
- package/src/brain/adapters/__tests__/resolver.test.ts +143 -143
- package/src/brain/adapters/anthropic-oauth.ts +1 -1
- package/src/brain/adapters/chatgpt-auth.ts +300 -300
- package/src/brain/adapters/index.ts +319 -319
- package/src/brain/adapters/oauth-flow.ts +439 -439
- package/src/brain/consent.ts +240 -240
- package/src/brain/credentials.ts +396 -396
- package/src/brain/doctor/analyzer.ts +7 -7
- package/src/bundler/__snapshots__/build.test.ts.snap +5 -5
- package/src/bundler/__tests__/build-runner.ts +166 -166
- package/src/bundler/__tests__/client-boundary-transform.test.ts +524 -524
- package/src/bundler/__tests__/cold-start.test.ts +60 -60
- package/src/bundler/__tests__/css.test.ts +49 -20
- package/src/bundler/__tests__/dev-reliability.test.ts +619 -619
- package/src/bundler/__tests__/extended-watch.test.ts +711 -711
- package/src/bundler/__tests__/fast-refresh.test.ts +24 -24
- package/src/bundler/__tests__/generation.test.ts +447 -0
- package/src/bundler/__tests__/hdr.test.ts +24 -18
- package/src/bundler/__tests__/hmr-client.test.ts +62 -24
- package/src/bundler/__tests__/jsx-runtime-shim.test.ts +179 -140
- package/src/bundler/__tests__/manifest-schema.test.ts +305 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +42 -42
- package/src/bundler/__tests__/slot-dispatch.test.ts +573 -573
- package/src/bundler/__tests__/url-cap-and-slot-regex.test.ts +286 -286
- package/src/bundler/__tests__/vendor-cache.test.ts +455 -455
- package/src/bundler/analyzer.ts +15 -15
- package/src/bundler/budget.ts +404 -404
- package/src/bundler/build.test.ts +898 -915
- package/src/bundler/build.ts +113 -24
- package/src/bundler/client-boundary-transform.ts +977 -977
- package/src/bundler/css.ts +65 -44
- package/src/bundler/dev.ts +105 -73
- package/src/bundler/fast-refresh-preamble.ts +47 -47
- package/src/bundler/generation.ts +602 -0
- package/src/bundler/index.ts +3 -3
- package/src/bundler/manifest-schema.ts +55 -40
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +13 -13
- package/src/bundler/plugins/__tests__/react-compiler-config.test.ts +83 -83
- package/src/bundler/plugins/__tests__/react-compiler-lint.test.ts +110 -110
- package/src/bundler/plugins/block-generated-imports.ts +16 -15
- package/src/bundler/plugins/index.ts +83 -83
- package/src/bundler/plugins/react-compiler-config.ts +108 -108
- package/src/bundler/plugins/react-compiler-lint.ts +253 -253
- package/src/bundler/plugins/react-compiler.ts +162 -162
- package/src/bundler/prerender.ts +29 -9
- package/src/bundler/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +1 -1
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +58 -56
- package/src/bundler/vendor-cache-types.ts +130 -130
- package/src/bundler/vendor-cache.ts +526 -526
- package/src/change/snapshot.ts +18 -18
- package/src/client/Form.tsx +105 -105
- package/src/client/Link.tsx +9 -9
- package/src/client/__tests__/props-serialization.test.ts +37 -37
- package/src/client/__tests__/use-sse.test.ts +153 -153
- package/src/client/globals.ts +1 -1
- package/src/client/hooks.ts +362 -362
- package/src/client/hydrate.ts +2 -2
- package/src/client/index.ts +1 -1
- package/src/client/island.ts +79 -79
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/props-serialization.ts +233 -233
- package/src/client/runtime-entry.ts +598 -598
- package/src/client/runtime.ts +1 -1
- package/src/client/serialize.ts +50 -50
- package/src/client/use-fetch.ts +6 -6
- package/src/client/use-head.ts +197 -197
- package/src/client/use-sse.ts +378 -378
- package/src/client/window-state.ts +101 -101
- package/src/components/Image-compat.ts +3 -0
- package/src/components/Image.tsx +162 -162
- package/src/config/validate.ts +1 -1
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +9 -9
- package/src/content/content-layer.ts +7 -7
- package/src/content/data-store.ts +245 -245
- package/src/content/frontmatter.ts +189 -189
- package/src/content/generate-types.ts +1 -1
- package/src/content/index.ts +2 -2
- package/src/content/loader-context.ts +171 -171
- package/src/content/loaders/api.ts +216 -216
- package/src/content/loaders/file.ts +172 -172
- package/src/content/loaders/glob.ts +253 -253
- 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/prebuild.test.ts +571 -571
- package/src/content/prebuild.ts +636 -636
- package/src/content/schema.ts +20 -20
- package/src/content/sidebar.ts +630 -630
- package/src/content/slug.ts +110 -110
- 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/define.ts +11 -11
- package/src/contract/index.ts +1 -1
- package/src/contract/normalize.test.ts +276 -276
- package/src/contract/normalize.ts +410 -410
- package/src/contract/registry.test.ts +206 -206
- package/src/contract/route-helpers.ts +1 -1
- package/src/contract/rpc.ts +443 -443
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +9 -9
- package/src/db/index.ts +2 -2
- package/src/db/migrations/history-table.ts +345 -345
- package/src/db/migrations/index.ts +3 -3
- package/src/db/migrations/lock.ts +324 -324
- package/src/db/migrations/runner.ts +650 -650
- package/src/deploy/cache.ts +140 -140
- package/src/deploy/compile/vercel.ts +344 -344
- package/src/deploy/index.ts +87 -87
- package/src/deploy/inference/brain.ts +268 -268
- package/src/deploy/inference/context.ts +82 -82
- package/src/deploy/inference/filling-extract.ts +245 -245
- package/src/deploy/inference/heuristic.ts +182 -182
- package/src/deploy/intent.ts +173 -173
- package/src/deploy/plan.ts +178 -178
- package/src/design/__tests__/agents-link.test.ts +109 -109
- package/src/design/__tests__/extract-patch-diff.test.ts +265 -265
- package/src/design/__tests__/lint.test.ts +110 -110
- package/src/design/__tests__/parser.test.ts +195 -195
- package/src/design/__tests__/tailwind-theme.test.ts +229 -229
- package/src/design/agents-link.ts +165 -165
- package/src/design/diff.ts +138 -138
- package/src/design/extract.ts +285 -285
- package/src/design/index.ts +102 -102
- package/src/design/lint.ts +209 -209
- package/src/design/parser.ts +555 -555
- package/src/design/patch.ts +241 -241
- package/src/design/scaffold.ts +147 -147
- package/src/design/tailwind-theme.ts +441 -441
- package/src/design/types.ts +210 -210
- package/src/desktop/__tests__/smoke.test.ts +2 -2
- package/src/desktop/__tests__/webview-fallback.test.ts +12 -32
- package/src/desktop/__tests__/window.test.ts +20 -99
- package/src/desktop/__tests__/worker.test.ts +266 -266
- package/src/desktop/index.ts +43 -43
- package/src/desktop/types.ts +158 -158
- package/src/desktop/webview-fallback.ts +17 -4
- package/src/desktop/window.ts +17 -8
- package/src/desktop/worker.ts +180 -180
- package/src/dev-error-overlay/__tests__/overlay-injector.test.ts +241 -241
- package/src/dev-error-overlay/index.ts +30 -30
- package/src/dev-error-overlay/overlay-injector.ts +243 -243
- package/src/dev-error-overlay/overlay-styles.ts +52 -52
- package/src/dev-error-overlay/types.ts +66 -66
- package/src/devtools/ai/context-builder.ts +375 -375
- package/src/devtools/ai/index.ts +25 -25
- package/src/devtools/ai/mcp-connector.ts +25 -25
- package/src/devtools/client/catchers/error-catcher.ts +344 -344
- package/src/devtools/client/catchers/index.ts +18 -18
- package/src/devtools/client/components/index.ts +39 -39
- package/src/devtools/client/components/mandu-character.tsx +331 -331
- package/src/devtools/client/components/panel/errors-panel.tsx +259 -259
- package/src/devtools/client/components/panel/guard-panel.tsx +30 -30
- package/src/devtools/client/components/panel/islands-panel.tsx +16 -16
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +1 -1
- package/src/devtools/client/components/panel/preview-panel.tsx +46 -46
- 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/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 +265 -265
- package/src/devtools/protocol.ts +237 -237
- package/src/devtools/server/index.ts +17 -17
- package/src/devtools/types.ts +35 -35
- package/src/devtools/worker/index.ts +25 -25
- package/src/devtools/worker/redaction-worker.ts +233 -233
- package/src/diagnose/__tests__/checks.test.ts +136 -136
- package/src/diagnose/checks.ts +185 -185
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +10 -10
- package/src/diagnose/types.ts +53 -53
- package/src/email/__tests__/email.test.ts +355 -355
- package/src/email/index.ts +282 -282
- package/src/email/smtp.ts +64 -64
- package/src/error/domains.ts +265 -265
- package/src/error/types.ts +6 -6
- package/src/errors/extractor.ts +409 -409
- package/src/errors/index.ts +19 -19
- package/src/filling/__tests__/session-sqlite.test.ts +5 -1
- package/src/filling/auth.ts +308 -308
- package/src/filling/body-parse.test.ts +60 -60
- package/src/filling/cookie-codec.ts +5 -3
- package/src/filling/deps.ts +265 -265
- package/src/filling/head-method.test.ts +154 -154
- package/src/filling/session-sqlite.ts +617 -617
- package/src/filling/sse.ts +5 -5
- package/src/filling/ws.ts +78 -78
- package/src/generator/generate.ts +30 -30
- package/src/generator/index.ts +3 -3
- package/src/generator/templates.test.ts +48 -48
- package/src/generator/templates.ts +219 -219
- package/src/guard/__tests__/design-inline-class.test.ts +219 -219
- package/src/guard/__tests__/tsgolint-bridge.test.ts +347 -347
- package/src/guard/analyzer.ts +360 -360
- package/src/guard/auto-correct.ts +1 -1
- package/src/guard/check.ts +39 -24
- package/src/guard/config-guard.ts +13 -13
- package/src/guard/contract-guard.ts +9 -9
- package/src/guard/define-rule.ts +243 -243
- package/src/guard/design-inline-class.ts +393 -393
- package/src/guard/file-type.test.ts +24 -24
- package/src/guard/fs-routes-policy.ts +51 -51
- package/src/guard/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- package/src/guard/index.ts +11 -11
- package/src/guard/presets/atomic.ts +70 -70
- package/src/guard/presets/clean.ts +77 -77
- package/src/guard/presets/fsd.ts +79 -79
- package/src/guard/presets/hexagonal.ts +68 -68
- package/src/guard/reporter.ts +442 -442
- package/src/guard/rule-presets.ts +379 -379
- package/src/guard/semantic-slots.ts +1 -1
- package/src/guard/suggestions.ts +358 -358
- package/src/guard/tsgolint-bridge.ts +512 -512
- package/src/guard/types.ts +348 -348
- package/src/guard/watcher.ts +405 -405
- package/src/i18n/define.ts +126 -126
- package/src/i18n/index.ts +52 -52
- package/src/i18n/message-registry.ts +173 -173
- package/src/i18n/types.ts +112 -112
- package/src/id/__tests__/id.test.ts +3 -3
- package/src/id/index.ts +105 -105
- package/src/index.ts +9 -48
- package/src/internal/client-boundary.ts +266 -266
- package/src/internal/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -779
- package/src/kitchen/api/bundle-inspector.ts +179 -0
- package/src/kitchen/api/errors-grouping.ts +126 -126
- package/src/kitchen/api/file-api.ts +11 -11
- package/src/kitchen/kitchen-handler.ts +36 -0
- package/src/kitchen/kitchen-ui.ts +456 -0
- package/src/logging/index.ts +22 -22
- package/src/logging/transports.ts +365 -365
- package/src/middleware/bridge.ts +147 -147
- package/src/middleware/compose.ts +134 -134
- package/src/middleware/compress.ts +62 -62
- package/src/middleware/cors.ts +47 -47
- package/src/middleware/csrf.ts +328 -328
- package/src/middleware/define.ts +132 -132
- package/src/middleware/jwt.ts +134 -134
- package/src/middleware/logger.ts +58 -58
- package/src/middleware/oauth/__tests__/oauth.test.ts +1 -1
- package/src/middleware/oauth/index.ts +505 -505
- package/src/middleware/oauth/providers.ts +115 -115
- package/src/middleware/rate-limit/index.ts +522 -522
- package/src/middleware/rate-limit/sqlite-store.ts +382 -382
- package/src/middleware/scheduler-cron.ts +96 -96
- package/src/middleware/secure/__tests__/secure.test.ts +360 -360
- package/src/middleware/secure/csp.ts +193 -193
- package/src/middleware/session.ts +174 -174
- package/src/middleware/timeout.ts +55 -55
- package/src/observability/logger-adapter.ts +36 -36
- package/src/observability/sqlite-store.ts +254 -254
- package/src/openapi/generator.ts +1 -1
- package/src/openapi/openapi.test.ts +43 -43
- package/src/perf/__tests__/user-marks.test.ts +354 -354
- package/src/perf/index.ts +133 -133
- package/src/perf/user-marks.ts +1 -1
- package/src/plugins/__tests__/lifecycle-integration.test.ts +272 -272
- package/src/plugins/__tests__/runner.test.ts +409 -409
- package/src/plugins/define.ts +124 -124
- package/src/plugins/examples/dep-check-plugin.ts +80 -80
- package/src/plugins/examples/prerender-cache-plugin.ts +111 -111
- package/src/plugins/examples/sitemap-plugin.ts +65 -65
- package/src/plugins/runner.ts +361 -361
- package/src/plugins/types.ts +368 -368
- package/src/report/index.ts +1 -1
- package/src/resource/__tests__/generator.test.ts +7 -7
- package/src/resource/__tests__/schema.test.ts +14 -14
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +165 -165
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +146 -146
- package/src/resource/ddl/persistence-types.ts +218 -218
- package/src/resource/ddl/type-map.ts +223 -223
- package/src/resource/ddl/types.ts +232 -232
- package/src/resource/generator-repo.ts +630 -630
- package/src/resource/generator-schema.ts +11 -11
- package/src/resource/generators/slot.ts +72 -72
- package/src/resource/schema.ts +21 -21
- package/src/router/client-entry.test.ts +227 -227
- package/src/router/client-entry.ts +218 -218
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.test.ts +532 -532
- package/src/router/fs-routes.ts +53 -53
- package/src/router/fs-scanner.ts +216 -216
- package/src/router/fs-types.ts +19 -19
- package/src/router/route-source-analyzer.ts +521 -521
- package/src/routes/index.ts +74 -74
- package/src/routes/metadata-routes.ts +427 -427
- package/src/routes/types.ts +341 -341
- package/src/runtime/__tests__/devtools-adapter.test.ts +68 -68
- package/src/runtime/__tests__/error-boundary-redaction.test.ts +141 -141
- package/src/runtime/__tests__/hdr-client.test.ts +223 -223
- package/src/runtime/__tests__/http-errors.test.ts +117 -117
- package/src/runtime/__tests__/inline-client-hydration.test.ts +237 -237
- package/src/runtime/__tests__/not-found.test.ts +152 -152
- package/src/runtime/__tests__/observability-lifecycle.test.ts +103 -103
- package/src/runtime/__tests__/page-render-response.test.ts +517 -517
- package/src/runtime/__tests__/request-middleware.test.ts +70 -70
- package/src/runtime/__tests__/searchparams-page-props.test.ts +81 -81
- package/src/runtime/adapter.ts +47 -47
- package/src/runtime/boundary.tsx +252 -252
- package/src/runtime/cache.ts +494 -494
- package/src/runtime/compose.ts +222 -222
- package/src/runtime/devtools-adapter.ts +68 -68
- package/src/runtime/escape.ts +34 -34
- package/src/runtime/fast-refresh-runtime.ts +322 -322
- package/src/runtime/fast-refresh-types.ts +1 -1
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +30 -11
- package/src/runtime/http-errors.ts +113 -113
- package/src/runtime/image-handler.ts +1 -1
- package/src/runtime/lifecycle.ts +381 -381
- package/src/runtime/logger.test.ts +345 -345
- package/src/runtime/middleware.ts +264 -264
- package/src/runtime/not-found.ts +93 -93
- package/src/runtime/observability-lifecycle.ts +290 -290
- package/src/runtime/openapi-endpoint.ts +236 -236
- package/src/runtime/page-render-response.ts +287 -274
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/rate-limit.ts +1 -1
- package/src/runtime/redirect.ts +1 -1
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/request-middleware.ts +31 -31
- package/src/runtime/router.test.ts +4 -4
- package/src/runtime/router.ts +10 -10
- package/src/runtime/server.ts +127 -211
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/ssr.ts +43 -17
- package/src/runtime/static-files.ts +369 -289
- package/src/runtime/streaming-ssr.ts +219 -180
- package/src/runtime/trace.ts +144 -144
- package/src/scheduler/__tests__/scheduler.test.ts +12 -11
- package/src/scheduler/index.ts +11 -5
- package/src/scheduler/validate.ts +169 -169
- package/src/seo/index.ts +219 -219
- package/src/seo/integration/ssr.ts +306 -306
- package/src/seo/render/basic.ts +435 -435
- package/src/seo/render/index.ts +143 -143
- package/src/seo/render/jsonld.ts +539 -539
- package/src/seo/render/opengraph.ts +197 -197
- package/src/seo/render/robots.ts +116 -116
- package/src/seo/render/sitemap.ts +137 -137
- package/src/seo/render/twitter.ts +127 -127
- 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 +104 -104
- package/src/seo/routes/index.ts +290 -290
- package/src/seo/types.ts +588 -588
- package/src/slot/validator.ts +39 -39
- package/src/spec/schema.ts +35 -35
- package/src/storage/s3/__tests__/s3.test.ts +479 -479
- package/src/storage/s3/index.ts +412 -412
- package/src/testing/__tests__/assertions.test.ts +632 -632
- package/src/testing/__tests__/reporter.test.ts +454 -454
- package/src/testing/assertions.ts +986 -986
- package/src/testing/db.ts +157 -157
- package/src/testing/index.ts +9 -3
- package/src/testing/lcov.ts +192 -0
- package/src/testing/mocks.ts +203 -203
- package/src/testing/session.ts +190 -190
- package/src/types/branded.ts +56 -56
- package/src/types/index.ts +1 -1
- package/src/utils/safe-io.ts +188 -188
- package/src/utils/string-safe.ts +298 -298
- package/src/watcher/__tests__/watcher.test.ts +59 -59
- package/src/watcher/watcher.ts +61 -61
package/src/runtime/server.ts
CHANGED
|
@@ -117,12 +117,11 @@ import {
|
|
|
117
117
|
resolvePrerenderedFile,
|
|
118
118
|
type PrerenderIndex,
|
|
119
119
|
} from "../bundler/prerender";
|
|
120
|
-
import {
|
|
121
|
-
buildOverlayErrorHtml,
|
|
122
|
-
buildPayloadFromError,
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
} from "../dev-error-overlay";
|
|
120
|
+
import {
|
|
121
|
+
buildOverlayErrorHtml,
|
|
122
|
+
buildPayloadFromError,
|
|
123
|
+
shouldInjectOverlay,
|
|
124
|
+
} from "../dev-error-overlay";
|
|
126
125
|
// Phase 18.μ — i18n dispatch. `resolveLocale()` is pure (no side effects),
|
|
127
126
|
// `createTranslator()` binds a per-request `t()` to the registry.
|
|
128
127
|
import {
|
|
@@ -210,24 +209,15 @@ export interface ServerOptions {
|
|
|
210
209
|
* 자동으로 캐싱됨 (Next.js `export const revalidate` 등가).
|
|
211
210
|
* - `false`/undefined : 캐시 비활성화
|
|
212
211
|
*/
|
|
213
|
-
cache?: boolean | CacheStore | CacheConfig;
|
|
214
|
-
/**
|
|
215
|
-
* Internal management token for local CLI/runtime control endpoints.
|
|
216
|
-
* When set, token-protected endpoints such as `/_mandu/cache` become available.
|
|
217
|
-
*/
|
|
218
|
-
managementToken?: string;
|
|
219
|
-
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*/
|
|
223
|
-
clientBundleHealthy?: boolean;
|
|
224
|
-
/** Optional failure reason surfaced by dev overlay when unhealthy. */
|
|
225
|
-
clientBundleFailureReason?: string;
|
|
226
|
-
/** Optional route ID associated with the latest client-bundle failure. */
|
|
227
|
-
clientBundleFailureRouteId?: string;
|
|
228
|
-
/**
|
|
229
|
-
* Issue #192 — enable CSS View Transitions auto-inject (default `true`).
|
|
230
|
-
* When `true`, every SSR response gets
|
|
212
|
+
cache?: boolean | CacheStore | CacheConfig;
|
|
213
|
+
/**
|
|
214
|
+
* Internal management token for local CLI/runtime control endpoints.
|
|
215
|
+
* When set, token-protected endpoints such as `/_mandu/cache` become available.
|
|
216
|
+
*/
|
|
217
|
+
managementToken?: string;
|
|
218
|
+
/**
|
|
219
|
+
* Issue #192 — enable CSS View Transitions auto-inject (default `true`).
|
|
220
|
+
* When `true`, every SSR response gets
|
|
231
221
|
* `<style>@view-transition{navigation:auto}</style>` in its `<head>`,
|
|
232
222
|
* giving supported browsers a default crossfade on cross-document
|
|
233
223
|
* navigation. Pass `false` to suppress (typically wired from
|
|
@@ -446,9 +436,11 @@ export interface ManduServer {
|
|
|
446
436
|
router: Router;
|
|
447
437
|
/** 이 서버 인스턴스의 레지스트리 */
|
|
448
438
|
registry: ServerRegistry;
|
|
449
|
-
/** Replace the live dispatch table after FS routes change in dev mode. */
|
|
450
|
-
updateManifest: (nextManifest: RoutesManifest) => void;
|
|
451
|
-
|
|
439
|
+
/** Replace the live dispatch table after FS routes change in dev mode. */
|
|
440
|
+
updateManifest: (nextManifest: RoutesManifest) => void;
|
|
441
|
+
/** Replace the client artifact manifest after a dev-server restart. */
|
|
442
|
+
updateBundleManifest: (nextManifest: BundleManifest | undefined) => void;
|
|
443
|
+
stop: () => void;
|
|
452
444
|
}
|
|
453
445
|
|
|
454
446
|
export type ApiHandler = (req: Request, params: Record<string, string>) => Response | Promise<Response>;
|
|
@@ -543,29 +535,19 @@ type CreateAppFn = (context: AppContext) => React.ReactElement;
|
|
|
543
535
|
* 같은 프로세스에서 여러 서버를 띄울 때 핸들러가 섞이는 문제 방지
|
|
544
536
|
*/
|
|
545
537
|
export interface ServerRegistrySettings {
|
|
546
|
-
isDev: boolean;
|
|
547
|
-
hmrPort?: number;
|
|
548
|
-
bundleManifest?: BundleManifest;
|
|
549
|
-
rootDir: string;
|
|
550
|
-
publicDir: string;
|
|
551
|
-
cors?: CorsOptions | false;
|
|
552
|
-
streaming: boolean;
|
|
553
|
-
rateLimit?: NormalizedRateLimitOptions | false;
|
|
554
|
-
/**
|
|
555
|
-
*
|
|
556
|
-
*
|
|
557
|
-
*
|
|
558
|
-
|
|
559
|
-
clientBundleHealthy?: boolean;
|
|
560
|
-
/** Last client bundle failure message emitted by the dev bundler. */
|
|
561
|
-
clientBundleFailureReason?: string;
|
|
562
|
-
/** Route ID associated with the last client bundle failure. */
|
|
563
|
-
clientBundleFailureRouteId?: string;
|
|
564
|
-
/**
|
|
565
|
-
* CSS 파일 경로 (SSR 링크 주입용)
|
|
566
|
-
* - string: 해당 경로로 <link> 주입
|
|
567
|
-
* - false: CSS 링크 주입 비활성화
|
|
568
|
-
* - undefined: false로 처리 (404 방지)
|
|
538
|
+
isDev: boolean;
|
|
539
|
+
hmrPort?: number;
|
|
540
|
+
bundleManifest?: BundleManifest;
|
|
541
|
+
rootDir: string;
|
|
542
|
+
publicDir: string;
|
|
543
|
+
cors?: CorsOptions | false;
|
|
544
|
+
streaming: boolean;
|
|
545
|
+
rateLimit?: NormalizedRateLimitOptions | false;
|
|
546
|
+
/**
|
|
547
|
+
* CSS 파일 경로 (SSR 링크 주입용)
|
|
548
|
+
* - string: 해당 경로로 <link> 주입
|
|
549
|
+
* - false: CSS 링크 주입 비활성화
|
|
550
|
+
* - undefined: false로 처리 (404 방지)
|
|
569
551
|
*/
|
|
570
552
|
cssPath?: string | false;
|
|
571
553
|
/** ISR/SWR 캐시 스토어 */
|
|
@@ -720,17 +702,14 @@ export class ServerRegistry {
|
|
|
720
702
|
readonly pageGenerateMetadata: Map<string, GenerateMetadata> = new Map();
|
|
721
703
|
readonly layoutMetadata: Map<string, Metadata | null> = new Map();
|
|
722
704
|
readonly layoutGenerateMetadata: Map<string, GenerateMetadata> = new Map();
|
|
723
|
-
settings: ServerRegistrySettings = {
|
|
724
|
-
isDev: false,
|
|
725
|
-
rootDir: process.cwd(),
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
streaming: false,
|
|
732
|
-
rateLimit: false,
|
|
733
|
-
};
|
|
705
|
+
settings: ServerRegistrySettings = {
|
|
706
|
+
isDev: false,
|
|
707
|
+
rootDir: process.cwd(),
|
|
708
|
+
publicDir: "public",
|
|
709
|
+
cors: false,
|
|
710
|
+
streaming: false,
|
|
711
|
+
rateLimit: false,
|
|
712
|
+
};
|
|
734
713
|
|
|
735
714
|
registerApiHandler(routeId: string, handler: ApiHandler): void {
|
|
736
715
|
this.apiHandlers.set(routeId, handler);
|
|
@@ -3210,70 +3189,15 @@ function paramsInStaticSet(
|
|
|
3210
3189
|
if (allMatch) return true;
|
|
3211
3190
|
}
|
|
3212
3191
|
return false;
|
|
3213
|
-
}
|
|
3214
|
-
// ─── End Issue #214 ─────────────────────────────────────────────────────────
|
|
3215
|
-
|
|
3216
|
-
function
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
const payload = buildPayloadFromError(
|
|
3223
|
-
new Error(`${reason}${failureDetails}`),
|
|
3224
|
-
{
|
|
3225
|
-
kind: "manual",
|
|
3226
|
-
routeId,
|
|
3227
|
-
url: pathname,
|
|
3228
|
-
},
|
|
3229
|
-
);
|
|
3230
|
-
const payloadJson = JSON.stringify(payload).replace(/</g, "\\u003c");
|
|
3231
|
-
const reasonPayload = JSON.stringify({
|
|
3232
|
-
message: reason,
|
|
3233
|
-
routeId,
|
|
3234
|
-
pathname,
|
|
3235
|
-
}).replace(/</g, "\\u003c");
|
|
3236
|
-
|
|
3237
|
-
if (isCss) {
|
|
3238
|
-
const cssBody = `/* Mandu: client bundle unavailable in dev. ${reason}${failureDetails} */`;
|
|
3239
|
-
return new Response(cssBody, {
|
|
3240
|
-
status: 503,
|
|
3241
|
-
headers: {
|
|
3242
|
-
"Content-Type": "text/css; charset=utf-8",
|
|
3243
|
-
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
3244
|
-
},
|
|
3245
|
-
});
|
|
3246
|
-
}
|
|
3247
|
-
|
|
3248
|
-
const jsBody = [
|
|
3249
|
-
"(function(){",
|
|
3250
|
-
`var payload = ${payloadJson};`,
|
|
3251
|
-
"if (typeof console !== \"undefined\" && console && console.warn) {",
|
|
3252
|
-
`console.warn("Mandu: client bundle is unavailable in dev mode.", ${reasonPayload});`,
|
|
3253
|
-
"}",
|
|
3254
|
-
"if (typeof window !== \"undefined\") {",
|
|
3255
|
-
"try {",
|
|
3256
|
-
`window.dispatchEvent(new CustomEvent(${JSON.stringify(OVERLAY_CUSTOM_EVENT)}, { detail: payload }));`,
|
|
3257
|
-
"} catch (_) {}",
|
|
3258
|
-
"}",
|
|
3259
|
-
"})();",
|
|
3260
|
-
].join("");
|
|
3261
|
-
|
|
3262
|
-
return new Response(jsBody, {
|
|
3263
|
-
status: 503,
|
|
3264
|
-
headers: {
|
|
3265
|
-
"Content-Type": "application/javascript; charset=utf-8",
|
|
3266
|
-
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
3267
|
-
},
|
|
3268
|
-
});
|
|
3269
|
-
}
|
|
3270
|
-
|
|
3271
|
-
async function handleRequestInternal(
|
|
3272
|
-
req: Request,
|
|
3273
|
-
router: Router,
|
|
3274
|
-
registry: ServerRegistry,
|
|
3275
|
-
skipMiddleware: boolean = false
|
|
3276
|
-
): Promise<Result<Response>> {
|
|
3192
|
+
}
|
|
3193
|
+
// ─── End Issue #214 ─────────────────────────────────────────────────────────
|
|
3194
|
+
|
|
3195
|
+
async function handleRequestInternal(
|
|
3196
|
+
req: Request,
|
|
3197
|
+
router: Router,
|
|
3198
|
+
registry: ServerRegistry,
|
|
3199
|
+
skipMiddleware: boolean = false
|
|
3200
|
+
): Promise<Result<Response>> {
|
|
3277
3201
|
const url = new URL(req.url);
|
|
3278
3202
|
const pathname = url.pathname;
|
|
3279
3203
|
const settings = registry.settings;
|
|
@@ -3295,20 +3219,16 @@ async function handleRequestInternal(
|
|
|
3295
3219
|
// HMR signals "full reload" — user saves a file, reloads, and the old
|
|
3296
3220
|
// prerendered output wins. Production `mandu start` always runs with
|
|
3297
3221
|
// `isDev: false`, so prod behavior is unchanged.
|
|
3298
|
-
if (!settings.isDev) {
|
|
3299
|
-
const prerendered = await tryServePrerendered(pathname, settings, req.method, req);
|
|
3300
|
-
if (prerendered) {
|
|
3301
|
-
if (settings.cors && isCorsRequest(req)) {
|
|
3302
|
-
const corsOptions: CorsOptions = typeof settings.cors === 'object' ? settings.cors : {};
|
|
3303
|
-
return ok(applyCorsToResponse(prerendered, req, corsOptions));
|
|
3304
|
-
}
|
|
3305
|
-
return ok(prerendered);
|
|
3306
|
-
}
|
|
3307
|
-
}
|
|
3308
|
-
|
|
3309
|
-
if (settings.isDev && settings.clientBundleHealthy === false && pathname.startsWith("/.mandu/client/")) {
|
|
3310
|
-
return ok(buildClientBundleFailureResponse(pathname, settings));
|
|
3311
|
-
}
|
|
3222
|
+
if (!settings.isDev) {
|
|
3223
|
+
const prerendered = await tryServePrerendered(pathname, settings, req.method, req);
|
|
3224
|
+
if (prerendered) {
|
|
3225
|
+
if (settings.cors && isCorsRequest(req)) {
|
|
3226
|
+
const corsOptions: CorsOptions = typeof settings.cors === 'object' ? settings.cors : {};
|
|
3227
|
+
return ok(applyCorsToResponse(prerendered, req, corsOptions));
|
|
3228
|
+
}
|
|
3229
|
+
return ok(prerendered);
|
|
3230
|
+
}
|
|
3231
|
+
}
|
|
3312
3232
|
|
|
3313
3233
|
// ─── Phase 18.μ — i18n dispatch ─────────────────────────────────────────
|
|
3314
3234
|
// Runs AFTER γ's prerendered check (static HTML per-locale is already
|
|
@@ -3687,14 +3607,15 @@ function isPortInUseError(error: unknown): boolean {
|
|
|
3687
3607
|
return code === "EADDRINUSE" || message.includes("EADDRINUSE") || message.includes("address already in use");
|
|
3688
3608
|
}
|
|
3689
3609
|
|
|
3690
|
-
function startBunServerWithFallback(options: {
|
|
3610
|
+
function startBunServerWithFallback(options: {
|
|
3691
3611
|
port: number;
|
|
3692
3612
|
hostname?: string;
|
|
3693
3613
|
fetch: (req: Request, server: Server<undefined>) => Promise<Response | undefined>;
|
|
3694
3614
|
websocket?: Record<string, unknown>;
|
|
3695
3615
|
}): { server: Server<undefined>; port: number; attempts: number } {
|
|
3696
|
-
const { port: startPort, hostname, fetch, websocket } = options;
|
|
3697
|
-
let lastError: unknown = null;
|
|
3616
|
+
const { port: startPort, hostname, fetch, websocket } = options;
|
|
3617
|
+
let lastError: unknown = null;
|
|
3618
|
+
const attemptedPorts: number[] = [];
|
|
3698
3619
|
|
|
3699
3620
|
// `Parameters<typeof Bun.serve>[0]` captures the full `Serve.Options`
|
|
3700
3621
|
// XOR union. We only ever use the hostname/port branch, so cast through
|
|
@@ -3712,10 +3633,11 @@ function startBunServerWithFallback(options: {
|
|
|
3712
3633
|
|
|
3713
3634
|
for (let attempt = 0; attempt < MAX_PORT_ATTEMPTS; attempt++) {
|
|
3714
3635
|
const candidate = startPort + attempt;
|
|
3715
|
-
if (candidate < 1 || candidate > 65535) {
|
|
3716
|
-
continue;
|
|
3717
|
-
}
|
|
3718
|
-
|
|
3636
|
+
if (candidate < 1 || candidate > 65535) {
|
|
3637
|
+
continue;
|
|
3638
|
+
}
|
|
3639
|
+
attemptedPorts.push(candidate);
|
|
3640
|
+
try {
|
|
3719
3641
|
const server = Bun.serve({ port: candidate, ...serveOptions } as unknown as BunServeOptions);
|
|
3720
3642
|
return { server, port: server.port ?? candidate, attempts: attempt };
|
|
3721
3643
|
} catch (error) {
|
|
@@ -3724,10 +3646,22 @@ function startBunServerWithFallback(options: {
|
|
|
3724
3646
|
}
|
|
3725
3647
|
lastError = error;
|
|
3726
3648
|
}
|
|
3727
|
-
}
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3649
|
+
}
|
|
3650
|
+
|
|
3651
|
+
const attemptedRange = attemptedPorts.length === 0
|
|
3652
|
+
? String(startPort)
|
|
3653
|
+
: attemptedPorts.length === 1
|
|
3654
|
+
? String(attemptedPorts[0])
|
|
3655
|
+
: `${attemptedPorts[0]}-${attemptedPorts.at(-1)}`;
|
|
3656
|
+
const bindHost = hostname?.trim() || "::";
|
|
3657
|
+
const fatal = new Error(
|
|
3658
|
+
`[Mandu Port Bind Failed] Could not bind ${bindHost} on port(s) ${attemptedRange}. ` +
|
|
3659
|
+
"All candidates are already in use. Stop the conflicting process or choose another port with --port <number>.",
|
|
3660
|
+
);
|
|
3661
|
+
fatal.name = "ManduPortBindError";
|
|
3662
|
+
(fatal as Error & { cause?: unknown }).cause = lastError;
|
|
3663
|
+
throw fatal;
|
|
3664
|
+
}
|
|
3731
3665
|
|
|
3732
3666
|
// ========== Server Startup ==========
|
|
3733
3667
|
|
|
@@ -3832,15 +3766,12 @@ export function startServer(manifest: RoutesManifest, options: ServerOptions = {
|
|
|
3832
3766
|
cors = false,
|
|
3833
3767
|
streaming = false,
|
|
3834
3768
|
rateLimit = false,
|
|
3835
|
-
cssPath: cssPathOption,
|
|
3836
|
-
registry = defaultRegistry,
|
|
3837
|
-
guardConfig = null,
|
|
3838
|
-
cache: cacheOption,
|
|
3839
|
-
managementToken,
|
|
3840
|
-
|
|
3841
|
-
clientBundleFailureReason,
|
|
3842
|
-
clientBundleFailureRouteId,
|
|
3843
|
-
transitions,
|
|
3769
|
+
cssPath: cssPathOption,
|
|
3770
|
+
registry = defaultRegistry,
|
|
3771
|
+
guardConfig = null,
|
|
3772
|
+
cache: cacheOption,
|
|
3773
|
+
managementToken,
|
|
3774
|
+
transitions,
|
|
3844
3775
|
prefetch,
|
|
3845
3776
|
spa,
|
|
3846
3777
|
devtools,
|
|
@@ -3867,13 +3798,13 @@ export function startServer(manifest: RoutesManifest, options: ServerOptions = {
|
|
|
3867
3798
|
if (i18nOption !== undefined && !isI18nDefinition(i18nOption)) {
|
|
3868
3799
|
throw new Error(
|
|
3869
3800
|
"[mandu] ServerOptions.i18n must be the return value of defineI18n(). " +
|
|
3870
|
-
"Import { defineI18n } from '@mandujs/core/i18n'."
|
|
3801
|
+
"Import { defineI18n } from '@mandujs/core/compat/i18n/index'."
|
|
3871
3802
|
);
|
|
3872
3803
|
}
|
|
3873
3804
|
if (messagesOption !== undefined && !isMessageRegistry(messagesOption)) {
|
|
3874
3805
|
throw new Error(
|
|
3875
3806
|
"[mandu] ServerOptions.messages must be the return value of defineMessages(). " +
|
|
3876
|
-
"Import { defineMessages } from '@mandujs/core/i18n'."
|
|
3807
|
+
"Import { defineMessages } from '@mandujs/core/compat/i18n/index'."
|
|
3877
3808
|
);
|
|
3878
3809
|
}
|
|
3879
3810
|
|
|
@@ -3942,19 +3873,16 @@ export function startServer(manifest: RoutesManifest, options: ServerOptions = {
|
|
|
3942
3873
|
});
|
|
3943
3874
|
|
|
3944
3875
|
// Registry settings 저장 (초기값)
|
|
3945
|
-
registry.settings = {
|
|
3946
|
-
isDev,
|
|
3947
|
-
hmrPort,
|
|
3948
|
-
bundleManifest,
|
|
3949
|
-
rootDir,
|
|
3950
|
-
publicDir,
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
streaming,
|
|
3956
|
-
rateLimit: rateLimitOptions,
|
|
3957
|
-
cssPath,
|
|
3876
|
+
registry.settings = {
|
|
3877
|
+
isDev,
|
|
3878
|
+
hmrPort,
|
|
3879
|
+
bundleManifest,
|
|
3880
|
+
rootDir,
|
|
3881
|
+
publicDir,
|
|
3882
|
+
cors: corsOptions,
|
|
3883
|
+
streaming,
|
|
3884
|
+
rateLimit: rateLimitOptions,
|
|
3885
|
+
cssPath,
|
|
3958
3886
|
managementToken,
|
|
3959
3887
|
transitions,
|
|
3960
3888
|
prefetch,
|
|
@@ -4212,10 +4140,13 @@ export function startServer(manifest: RoutesManifest, options: ServerOptions = {
|
|
|
4212
4140
|
server,
|
|
4213
4141
|
router,
|
|
4214
4142
|
registry,
|
|
4215
|
-
updateManifest: (nextManifest: RoutesManifest) => {
|
|
4216
|
-
router.setRoutes(nextManifest.routes);
|
|
4217
|
-
registry.devtoolsAdapter?.updateManifest(nextManifest);
|
|
4218
|
-
},
|
|
4143
|
+
updateManifest: (nextManifest: RoutesManifest) => {
|
|
4144
|
+
router.setRoutes(nextManifest.routes);
|
|
4145
|
+
registry.devtoolsAdapter?.updateManifest(nextManifest);
|
|
4146
|
+
},
|
|
4147
|
+
updateBundleManifest: (nextManifest: BundleManifest | undefined) => {
|
|
4148
|
+
registry.settings.bundleManifest = nextManifest;
|
|
4149
|
+
},
|
|
4219
4150
|
stop: () => {
|
|
4220
4151
|
registry.devtoolsAdapter?.stop();
|
|
4221
4152
|
registry.devtoolsAdapter = null;
|
|
@@ -4246,19 +4177,10 @@ export const routeComponents = defaultRegistry.routeComponents;
|
|
|
4246
4177
|
export interface AppFetchHandlerOptions {
|
|
4247
4178
|
/** Project root (used for module path validation). Required. */
|
|
4248
4179
|
rootDir: string;
|
|
4249
|
-
/** Bundle manifest (Island hydration). Optional in pure-SSR apps. */
|
|
4250
|
-
bundleManifest?: BundleManifest;
|
|
4251
|
-
/**
|
|
4252
|
-
|
|
4253
|
-
* a runtime-neutral fetch handler.
|
|
4254
|
-
*/
|
|
4255
|
-
clientBundleHealthy?: boolean;
|
|
4256
|
-
/** Optional failure reason surfaced by dev overlay when unhealthy. */
|
|
4257
|
-
clientBundleFailureReason?: string;
|
|
4258
|
-
/** Optional route ID associated with the latest client-bundle failure. */
|
|
4259
|
-
clientBundleFailureRouteId?: string;
|
|
4260
|
-
/** CORS config — `true` allows all origins, object for fine-grained rules. */
|
|
4261
|
-
cors?: boolean | CorsOptions;
|
|
4180
|
+
/** Bundle manifest (Island hydration). Optional in pure-SSR apps. */
|
|
4181
|
+
bundleManifest?: BundleManifest;
|
|
4182
|
+
/** CORS config — `true` allows all origins, object for fine-grained rules. */
|
|
4183
|
+
cors?: boolean | CorsOptions;
|
|
4262
4184
|
/** Streaming SSR toggle. Default: `false`. */
|
|
4263
4185
|
streaming?: boolean;
|
|
4264
4186
|
/** Rate limit policy. Memory-backed; edge runtimes should prefer durable stores. */
|
|
@@ -4318,14 +4240,11 @@ export function createAppFetchHandler(
|
|
|
4318
4240
|
manifest: RoutesManifest,
|
|
4319
4241
|
options: AppFetchHandlerOptions
|
|
4320
4242
|
): (req: Request) => Promise<Response> {
|
|
4321
|
-
const {
|
|
4322
|
-
rootDir,
|
|
4323
|
-
bundleManifest,
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
clientBundleFailureRouteId,
|
|
4327
|
-
cors = false,
|
|
4328
|
-
streaming = false,
|
|
4243
|
+
const {
|
|
4244
|
+
rootDir,
|
|
4245
|
+
bundleManifest,
|
|
4246
|
+
cors = false,
|
|
4247
|
+
streaming = false,
|
|
4329
4248
|
rateLimit = false,
|
|
4330
4249
|
cssPath = false,
|
|
4331
4250
|
registry = defaultRegistry,
|
|
@@ -4336,18 +4255,15 @@ export function createAppFetchHandler(
|
|
|
4336
4255
|
const corsOptions: CorsOptions | false = cors === true ? {} : cors;
|
|
4337
4256
|
const rateLimitOptions = normalizeRateLimitOptions(rateLimit);
|
|
4338
4257
|
|
|
4339
|
-
registry.settings = {
|
|
4340
|
-
isDev: false,
|
|
4341
|
-
bundleManifest,
|
|
4342
|
-
rootDir,
|
|
4343
|
-
publicDir: "public",
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
streaming,
|
|
4349
|
-
rateLimit: rateLimitOptions,
|
|
4350
|
-
cssPath,
|
|
4258
|
+
registry.settings = {
|
|
4259
|
+
isDev: false,
|
|
4260
|
+
bundleManifest,
|
|
4261
|
+
rootDir,
|
|
4262
|
+
publicDir: "public",
|
|
4263
|
+
cors: corsOptions,
|
|
4264
|
+
streaming,
|
|
4265
|
+
rateLimit: rateLimitOptions,
|
|
4266
|
+
cssPath,
|
|
4351
4267
|
edge,
|
|
4352
4268
|
};
|
|
4353
4269
|
|
package/src/runtime/shims.ts
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime Shims for React Compatibility
|
|
3
|
-
*
|
|
4
|
-
* React 19+ 호환성을 위한 런타임 shim 스크립트들을 제공합니다.
|
|
5
|
-
*
|
|
6
|
-
* @module runtime/shims
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* React 19 Client Internals Shim Script
|
|
11
|
-
*
|
|
12
|
-
* React 19에서 react-dom/client가 실행되기 전에 ReactSharedInternals.S가
|
|
13
|
-
* 존재하는지 확인하고 필요시 초기화하는 inline 스크립트입니다.
|
|
14
|
-
*
|
|
15
|
-
* **사용 목적**:
|
|
16
|
-
* - Playwright headless 환경에서 hydration 실패 방지
|
|
17
|
-
* - React 19의 __CLIENT_INTERNALS.S가 null일 수 있는 문제 해결
|
|
18
|
-
* - SSR HTML에 삽입하여 번들 로드 전에 실행
|
|
19
|
-
*
|
|
20
|
-
* **안전성**:
|
|
21
|
-
* - try-catch로 감싸져 있어 오류 발생 시에도 안전
|
|
22
|
-
* - 기존 값이 있으면 덮어쓰지 않음
|
|
23
|
-
* - React가 없거나 internals가 없어도 실패하지 않음
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```typescript
|
|
27
|
-
* // SSR HTML에 삽입
|
|
28
|
-
* const html = `
|
|
29
|
-
* ${hydrationScripts}
|
|
30
|
-
* ${REACT_INTERNALS_SHIM_SCRIPT}
|
|
31
|
-
* ${routerScript}
|
|
32
|
-
* `;
|
|
33
|
-
* ```
|
|
34
|
-
*/
|
|
35
|
-
export const REACT_INTERNALS_SHIM_SCRIPT = `<script>
|
|
36
|
-
// React 19 internals shim: ensure ReactSharedInternals.S exists before react-dom/client runs.
|
|
37
|
-
// Some builds expect React.__CLIENT_INTERNALS... .S to be a function, but it may be null.
|
|
38
|
-
// This shim is safe: it only fills the slot if missing.
|
|
39
|
-
(function(){
|
|
40
|
-
try {
|
|
41
|
-
var React = window.React;
|
|
42
|
-
var i = React && React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
|
|
43
|
-
if (i && i.S == null) {
|
|
44
|
-
i.S = function(){};
|
|
45
|
-
}
|
|
46
|
-
} catch(e) {}
|
|
47
|
-
})();
|
|
48
|
-
</script>`;
|
|
1
|
+
/**
|
|
2
|
+
* Runtime Shims for React Compatibility
|
|
3
|
+
*
|
|
4
|
+
* React 19+ 호환성을 위한 런타임 shim 스크립트들을 제공합니다.
|
|
5
|
+
*
|
|
6
|
+
* @module runtime/shims
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* React 19 Client Internals Shim Script
|
|
11
|
+
*
|
|
12
|
+
* React 19에서 react-dom/client가 실행되기 전에 ReactSharedInternals.S가
|
|
13
|
+
* 존재하는지 확인하고 필요시 초기화하는 inline 스크립트입니다.
|
|
14
|
+
*
|
|
15
|
+
* **사용 목적**:
|
|
16
|
+
* - Playwright headless 환경에서 hydration 실패 방지
|
|
17
|
+
* - React 19의 __CLIENT_INTERNALS.S가 null일 수 있는 문제 해결
|
|
18
|
+
* - SSR HTML에 삽입하여 번들 로드 전에 실행
|
|
19
|
+
*
|
|
20
|
+
* **안전성**:
|
|
21
|
+
* - try-catch로 감싸져 있어 오류 발생 시에도 안전
|
|
22
|
+
* - 기존 값이 있으면 덮어쓰지 않음
|
|
23
|
+
* - React가 없거나 internals가 없어도 실패하지 않음
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* // SSR HTML에 삽입
|
|
28
|
+
* const html = `
|
|
29
|
+
* ${hydrationScripts}
|
|
30
|
+
* ${REACT_INTERNALS_SHIM_SCRIPT}
|
|
31
|
+
* ${routerScript}
|
|
32
|
+
* `;
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export const REACT_INTERNALS_SHIM_SCRIPT = `<script>
|
|
36
|
+
// React 19 internals shim: ensure ReactSharedInternals.S exists before react-dom/client runs.
|
|
37
|
+
// Some builds expect React.__CLIENT_INTERNALS... .S to be a function, but it may be null.
|
|
38
|
+
// This shim is safe: it only fills the slot if missing.
|
|
39
|
+
(function(){
|
|
40
|
+
try {
|
|
41
|
+
var React = window.React;
|
|
42
|
+
var i = React && React.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;
|
|
43
|
+
if (i && i.S == null) {
|
|
44
|
+
i.S = function(){};
|
|
45
|
+
}
|
|
46
|
+
} catch(e) {}
|
|
47
|
+
})();
|
|
48
|
+
</script>`;
|
package/src/runtime/ssr.ts
CHANGED
|
@@ -3,7 +3,11 @@ import { serializeProps } from "../client/serialize";
|
|
|
3
3
|
import { createRequire } from "module";
|
|
4
4
|
import React, { type ReactElement, type ReactNode } from "react";
|
|
5
5
|
import type { BundleManifest } from "../bundler/types";
|
|
6
|
-
import {
|
|
6
|
+
import { isSafePublishedManduUrl } from "../bundler/manifest-schema";
|
|
7
|
+
import {
|
|
8
|
+
scopeBundleManifestToGeneration,
|
|
9
|
+
scopeClientAssetUrl,
|
|
10
|
+
} from "../bundler/generation";
|
|
7
11
|
import type { HydrationConfig, HydrationPriority } from "../spec/schema";
|
|
8
12
|
import { PORTS, TIMEOUTS } from "../constants";
|
|
9
13
|
import { decodeHtmlText, escapeHtmlAttr, escapeHtmlText, escapeJsonForInlineScript } from "./escape";
|
|
@@ -319,7 +323,9 @@ export function wrapWithIsland(
|
|
|
319
323
|
bundleSrc?: string,
|
|
320
324
|
hydrate?: string
|
|
321
325
|
): string {
|
|
322
|
-
const cacheBustedSrc = bundleSrc
|
|
326
|
+
const cacheBustedSrc = bundleSrc
|
|
327
|
+
? `${bundleSrc}${bundleSrc.includes("?") ? "&" : "?"}t=${Date.now()}`
|
|
328
|
+
: undefined;
|
|
323
329
|
const srcAttr = cacheBustedSrc ? ` data-mandu-src="${escapeHtmlAttr(cacheBustedSrc)}"` : "";
|
|
324
330
|
const hydrateValue = hydrate ?? priorityToHydrateStrategy(priority);
|
|
325
331
|
const hydrateAttr = ` data-hydrate="${escapeHtmlAttr(hydrateValue)}"`;
|
|
@@ -392,12 +398,12 @@ function generateFastRefreshPreambleTag(
|
|
|
392
398
|
const fr = manifest?.shared?.fastRefresh;
|
|
393
399
|
if (!fr) return "";
|
|
394
400
|
if (!fr.glue || !fr.runtime) return "";
|
|
395
|
-
// Phase 7.2.R3 M-01 — manifest wire-up.
|
|
401
|
+
// Phase 7.2.R3 M-01 — manifest wire-up. The response-time validator
|
|
396
402
|
// tampered entries (protocol, traversal, non-/.mandu paths, >2KB).
|
|
397
403
|
// Fail closed: if either URL is suspect, skip the preamble entirely
|
|
398
404
|
// — dev refresh breaks loudly instead of injecting an attacker-
|
|
399
405
|
// controlled <script src> via a tampered manifest.
|
|
400
|
-
if (!
|
|
406
|
+
if (!isSafePublishedManduUrl(fr.glue) || !isSafePublishedManduUrl(fr.runtime)) return "";
|
|
401
407
|
const raw = generateFastRefreshPreamble(fr.glue, fr.runtime);
|
|
402
408
|
if (!nonce) return raw;
|
|
403
409
|
// Inject nonce attribute onto the first <script> tag ONLY. Matches
|
|
@@ -628,9 +634,13 @@ export async function resolveAsyncElement(node: ReactNode): Promise<ReactNode> {
|
|
|
628
634
|
return React.cloneElement(element, undefined, resolvedChildren);
|
|
629
635
|
}
|
|
630
636
|
|
|
631
|
-
export function renderToHTML(element: ReactElement, options: SSROptions = {}): string {
|
|
632
|
-
const hasExplicitTitle = options.title !== undefined;
|
|
633
|
-
const
|
|
637
|
+
export function renderToHTML(element: ReactElement, options: SSROptions = {}): string {
|
|
638
|
+
const hasExplicitTitle = options.title !== undefined;
|
|
639
|
+
const scopedBundleManifest = scopeBundleManifestToGeneration(options.bundleManifest);
|
|
640
|
+
const responseOptions = scopedBundleManifest === options.bundleManifest
|
|
641
|
+
? options
|
|
642
|
+
: { ...options, bundleManifest: scopedBundleManifest };
|
|
643
|
+
const {
|
|
634
644
|
title = "Mandu App",
|
|
635
645
|
lang = "ko",
|
|
636
646
|
serverData,
|
|
@@ -651,7 +661,7 @@ export function renderToHTML(element: ReactElement, options: SSROptions = {}): s
|
|
|
651
661
|
devtools,
|
|
652
662
|
devErrorOverlay,
|
|
653
663
|
layoutChain,
|
|
654
|
-
} =
|
|
664
|
+
} = responseOptions;
|
|
655
665
|
|
|
656
666
|
// Issue #233 — derive a stable short key for the layout chain so the
|
|
657
667
|
// SPA nav helper can detect cross-layout transitions. Deterministic
|
|
@@ -1083,9 +1093,19 @@ window.__MANDU_HMR_PORT__ = ${hmrPort};
|
|
|
1083
1093
|
}
|
|
1084
1094
|
}
|
|
1085
1095
|
if (!updated) location.reload();
|
|
1086
|
-
} else if (msg.type === 'error') {
|
|
1087
|
-
|
|
1088
|
-
|
|
1096
|
+
} else if (msg.type === 'error') {
|
|
1097
|
+
var errorData = msg.data || {};
|
|
1098
|
+
console.error('[Mandu HMR] Build error:', errorData.message);
|
|
1099
|
+
window.dispatchEvent(new CustomEvent('__MANDU_ERROR__', { detail: {
|
|
1100
|
+
name: errorData.name || 'BuildError',
|
|
1101
|
+
message: errorData.message || 'Build failed',
|
|
1102
|
+
stack: errorData.stack || '',
|
|
1103
|
+
kind: 'manual',
|
|
1104
|
+
timestamp: errorData.timestamp || Date.now(),
|
|
1105
|
+
routeId: errorData.routeId,
|
|
1106
|
+
url: window.location.pathname + window.location.search
|
|
1107
|
+
}}));
|
|
1108
|
+
}
|
|
1089
1109
|
} catch(err) {}
|
|
1090
1110
|
};
|
|
1091
1111
|
ws.onclose = function() { scheduleReconnect(); };
|
|
@@ -1148,12 +1168,18 @@ function shouldInjectDevtools(
|
|
|
1148
1168
|
* @internal Exported via `_testOnly_generateDevtoolsScript` below so tests
|
|
1149
1169
|
* can verify the URL shape without needing a full render.
|
|
1150
1170
|
*/
|
|
1151
|
-
function generateDevtoolsScript(manifest?: BundleManifest): string {
|
|
1152
|
-
const
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1171
|
+
function generateDevtoolsScript(manifest?: BundleManifest): string {
|
|
1172
|
+
const src = scopeClientAssetUrl(
|
|
1173
|
+
"/.mandu/client/_devtools.js",
|
|
1174
|
+
manifest?.generationId,
|
|
1175
|
+
);
|
|
1176
|
+
const key = manifest?.buildTime ? "v" : "t";
|
|
1177
|
+
const value = manifest?.buildTime
|
|
1178
|
+
? encodeURIComponent(manifest.buildTime)
|
|
1179
|
+
: Date.now();
|
|
1180
|
+
const cacheBust = `${src.includes("?") ? "&" : "?"}${key}=${value}`;
|
|
1181
|
+
return `<script type="module" src="${src}${cacheBust}"></script>`;
|
|
1182
|
+
}
|
|
1157
1183
|
|
|
1158
1184
|
/** @internal test helper — exposed only so unit tests can inspect the decision. */
|
|
1159
1185
|
export const _testOnly_shouldInjectDevtools = shouldInjectDevtools;
|