@mandujs/core 0.53.3 → 0.54.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.md +654 -654
- package/package.json +2 -2
- 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 +394 -394
- package/src/a11y/types.ts +125 -125
- package/src/auth/__tests__/login.test.ts +1 -1
- package/src/auth/__tests__/password.test.ts +122 -122
- 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 +120 -120
- 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/bundler/__tests__/build-runner.ts +113 -113
- 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 +10 -10
- package/src/bundler/__tests__/hdr.test.ts +353 -353
- package/src/bundler/__tests__/hmr-client.test.ts +532 -532
- package/src/bundler/__tests__/manifest-schema.test.ts +266 -266
- package/src/bundler/__tests__/prod-smoke.test.ts +138 -138
- package/src/bundler/__tests__/reverse-import-graph.test.ts +519 -519
- 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/budget.ts +404 -404
- package/src/bundler/build.test.ts +179 -179
- package/src/bundler/build.ts +55 -55
- package/src/bundler/dev.ts +42 -42
- package/src/bundler/manifest-schema.ts +301 -301
- package/src/bundler/plugins/__tests__/block-generated-imports.test.ts +263 -263
- 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 +155 -155
- 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/reverse-import-graph.ts +339 -339
- package/src/bundler/safe-build.test.ts +201 -201
- package/src/bundler/safe-build.ts +103 -103
- package/src/bundler/scenario-matrix.ts +229 -229
- package/src/bundler/types.ts +10 -10
- 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__/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 +340 -340
- package/src/client/prefetch-helper.ts +55 -55
- package/src/client/router.ts +11 -11
- package/src/client/runtime.ts +47 -47
- package/src/client/serialize.ts +404 -404
- 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.tsx +162 -162
- package/src/config/validate.ts +3 -3
- package/src/config/watcher.ts +311 -311
- package/src/constants.ts +40 -40
- package/src/content/collection.ts +8 -8
- 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/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/schema.ts +48 -48
- package/src/contract/types.ts +58 -58
- package/src/db/__tests__/db.test.ts +482 -482
- package/src/db/index.ts +138 -138
- 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 +182 -182
- 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__/webview-fallback.test.ts +254 -254
- package/src/desktop/__tests__/window.test.ts +248 -248
- 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/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/overlay.tsx +368 -368
- 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 +320 -320
- package/src/devtools/client/components/panel/network-panel.tsx +291 -291
- package/src/devtools/client/components/panel/panel-container.tsx +500 -500
- 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/client/persistence.ts +335 -335
- 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/server/source-context.ts +450 -450
- 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/devtools/worker/worker-manager.ts +410 -410
- package/src/diagnose/__tests__/checks.test.ts +451 -451
- package/src/diagnose/checks.ts +832 -832
- package/src/diagnose/index.ts +17 -17
- package/src/diagnose/run.ts +93 -93
- 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/result.ts +60 -60
- 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 +454 -454
- package/src/filling/auth.ts +308 -308
- package/src/filling/cookie-codec.ts +299 -299
- package/src/filling/deps.ts +265 -265
- 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/index.ts +3 -3
- 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/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/graph.ts +1 -1
- package/src/guard/healing.ts +36 -36
- 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/index.ts +105 -105
- package/src/index.ts +2 -2
- package/src/kitchen/api/agent-devtools-api.ts +779 -544
- package/src/kitchen/api/errors-grouping.ts +126 -0
- package/src/kitchen/kitchen-handler.ts +192 -63
- package/src/kitchen/kitchen-ui.ts +842 -464
- 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/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 +32 -32
- package/src/resource/ddl/__tests__/diff.test.ts +639 -639
- package/src/resource/ddl/__tests__/emit.test.ts +823 -823
- package/src/resource/ddl/__tests__/snapshot.test.ts +499 -499
- package/src/resource/ddl/emit.ts +559 -559
- 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/schema.ts +1 -1
- package/src/router/fs-patterns.test.ts +96 -96
- package/src/router/fs-routes.ts +6 -6
- 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__/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__/not-found.test.ts +152 -152
- 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/fast-refresh-runtime.ts +322 -322
- package/src/runtime/handler.ts +65 -65
- package/src/runtime/handlers.ts +300 -300
- 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/openapi-endpoint.ts +236 -236
- package/src/runtime/ppr.ts +74 -74
- package/src/runtime/registry.ts +171 -171
- package/src/runtime/router.ts +105 -105
- package/src/runtime/server.ts +67 -67
- package/src/runtime/shims.ts +48 -48
- package/src/runtime/trace.ts +144 -144
- 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/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/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/watcher.ts +18 -18
|
@@ -1,55 +1,55 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Issue #192 — Hover prefetch helper
|
|
3
|
-
*
|
|
4
|
-
* Self-contained IIFE that watches `mouseover` events bubbling from
|
|
5
|
-
* internal `<a href="/...">` anchors and issues a one-shot
|
|
6
|
-
* `<link rel="prefetch" as="document">` per unique anchor. The browser
|
|
7
|
-
* cache services the subsequent full-reload navigation, so most
|
|
8
|
-
* above-the-fold links feel instantaneous without requiring a SPA
|
|
9
|
-
* runtime.
|
|
10
|
-
*
|
|
11
|
-
* Design choices (locked to keep the payload tiny — target ≤500 bytes
|
|
12
|
-
* minified + gzipped):
|
|
13
|
-
*
|
|
14
|
-
* 1. **WeakSet-based dedup**: we never attach per-anchor listeners;
|
|
15
|
-
* we use a single `document`-level capture listener and stamp
|
|
16
|
-
* each anchor we've seen into a `WeakSet`. Anchors removed from
|
|
17
|
-
* the DOM collect automatically.
|
|
18
|
-
*
|
|
19
|
-
* 2. **Scope: same-origin `/...` paths only**: we deliberately skip
|
|
20
|
-
* absolute URLs, `mailto:`, `tel:`, `javascript:`, hash-only
|
|
21
|
-
* fragments, and `#`-on-same-page links. The `a[href^="/"]`
|
|
22
|
-
* selector implicitly covers this and avoids a surprise
|
|
23
|
-
* cross-origin DNS lookup.
|
|
24
|
-
*
|
|
25
|
-
* 3. **Opt-out per-link via `data-no-prefetch`**: mirrors Next.js'
|
|
26
|
-
* `prefetch={false}` ergonomics while being framework-agnostic —
|
|
27
|
-
* works with plain `<a>` and with Mandu's `<Link>` component.
|
|
28
|
-
*
|
|
29
|
-
* 4. **`as="document"` hint**: the correct token for HTML documents
|
|
30
|
-
* that will be navigated to via a subsequent click. Without it
|
|
31
|
-
* Chrome 121+ logs a `rel=prefetch as=missing` warning.
|
|
32
|
-
*
|
|
33
|
-
* 5. **Capture phase + `passive: true`**: listening in the capture
|
|
34
|
-
* phase catches the event before any app-level bubble handlers
|
|
35
|
-
* can cancel it (defensive against apps that `stopPropagation`
|
|
36
|
-
* on `<a>`). `passive` ensures we can never accidentally block
|
|
37
|
-
* scroll.
|
|
38
|
-
*
|
|
39
|
-
* 6. **Inline, not external bundle**: the helper is under 1KB and
|
|
40
|
-
* emitting it as a `<script>` child (rather than
|
|
41
|
-
* `<script src=".../_prefetch.js">`) avoids one HTTP round-trip
|
|
42
|
-
* on every SSR response and keeps the module graph unchanged.
|
|
43
|
-
*
|
|
44
|
-
* The exported `PREFETCH_HELPER_SCRIPT` wraps the IIFE in a
|
|
45
|
-
* `<script>` tag, ready to paste into `<head>`. Callers MUST keep it
|
|
46
|
-
* nonce-aware if they plan to enable CSP (future work — right now the
|
|
47
|
-
* Fast Refresh preamble is the only inline script covered by Mandu's
|
|
48
|
-
* CSP header).
|
|
49
|
-
*/
|
|
50
|
-
|
|
51
|
-
/** Inner IIFE — exposed for unit tests that want to parse the source. */
|
|
52
|
-
export const PREFETCH_HELPER_BODY = `(function(){var s=new WeakSet();document.addEventListener("mouseover",function(e){var t=e.target;if(!t||typeof t.closest!=="function")return;var a=t.closest("a[href^='/']");if(!a||s.has(a))return;if(a.dataset&&a.dataset.noPrefetch!==undefined)return;if(a.hasAttribute&&a.hasAttribute("download"))return;if(a.target&&a.target!=="_self")return;s.add(a);try{var l=document.createElement("link");l.rel="prefetch";l.href=a.href;l.as="document";document.head.appendChild(l);}catch(_){}},{passive:true,capture:true});})();`;
|
|
53
|
-
|
|
54
|
-
/** Ready-to-inject `<script>` tag for SSR `<head>` injection. */
|
|
55
|
-
export const PREFETCH_HELPER_SCRIPT = `<script>${PREFETCH_HELPER_BODY}</script>`;
|
|
1
|
+
/**
|
|
2
|
+
* Issue #192 — Hover prefetch helper
|
|
3
|
+
*
|
|
4
|
+
* Self-contained IIFE that watches `mouseover` events bubbling from
|
|
5
|
+
* internal `<a href="/...">` anchors and issues a one-shot
|
|
6
|
+
* `<link rel="prefetch" as="document">` per unique anchor. The browser
|
|
7
|
+
* cache services the subsequent full-reload navigation, so most
|
|
8
|
+
* above-the-fold links feel instantaneous without requiring a SPA
|
|
9
|
+
* runtime.
|
|
10
|
+
*
|
|
11
|
+
* Design choices (locked to keep the payload tiny — target ≤500 bytes
|
|
12
|
+
* minified + gzipped):
|
|
13
|
+
*
|
|
14
|
+
* 1. **WeakSet-based dedup**: we never attach per-anchor listeners;
|
|
15
|
+
* we use a single `document`-level capture listener and stamp
|
|
16
|
+
* each anchor we've seen into a `WeakSet`. Anchors removed from
|
|
17
|
+
* the DOM collect automatically.
|
|
18
|
+
*
|
|
19
|
+
* 2. **Scope: same-origin `/...` paths only**: we deliberately skip
|
|
20
|
+
* absolute URLs, `mailto:`, `tel:`, `javascript:`, hash-only
|
|
21
|
+
* fragments, and `#`-on-same-page links. The `a[href^="/"]`
|
|
22
|
+
* selector implicitly covers this and avoids a surprise
|
|
23
|
+
* cross-origin DNS lookup.
|
|
24
|
+
*
|
|
25
|
+
* 3. **Opt-out per-link via `data-no-prefetch`**: mirrors Next.js'
|
|
26
|
+
* `prefetch={false}` ergonomics while being framework-agnostic —
|
|
27
|
+
* works with plain `<a>` and with Mandu's `<Link>` component.
|
|
28
|
+
*
|
|
29
|
+
* 4. **`as="document"` hint**: the correct token for HTML documents
|
|
30
|
+
* that will be navigated to via a subsequent click. Without it
|
|
31
|
+
* Chrome 121+ logs a `rel=prefetch as=missing` warning.
|
|
32
|
+
*
|
|
33
|
+
* 5. **Capture phase + `passive: true`**: listening in the capture
|
|
34
|
+
* phase catches the event before any app-level bubble handlers
|
|
35
|
+
* can cancel it (defensive against apps that `stopPropagation`
|
|
36
|
+
* on `<a>`). `passive` ensures we can never accidentally block
|
|
37
|
+
* scroll.
|
|
38
|
+
*
|
|
39
|
+
* 6. **Inline, not external bundle**: the helper is under 1KB and
|
|
40
|
+
* emitting it as a `<script>` child (rather than
|
|
41
|
+
* `<script src=".../_prefetch.js">`) avoids one HTTP round-trip
|
|
42
|
+
* on every SSR response and keeps the module graph unchanged.
|
|
43
|
+
*
|
|
44
|
+
* The exported `PREFETCH_HELPER_SCRIPT` wraps the IIFE in a
|
|
45
|
+
* `<script>` tag, ready to paste into `<head>`. Callers MUST keep it
|
|
46
|
+
* nonce-aware if they plan to enable CSP (future work — right now the
|
|
47
|
+
* Fast Refresh preamble is the only inline script covered by Mandu's
|
|
48
|
+
* CSP header).
|
|
49
|
+
*/
|
|
50
|
+
|
|
51
|
+
/** Inner IIFE — exposed for unit tests that want to parse the source. */
|
|
52
|
+
export const PREFETCH_HELPER_BODY = `(function(){var s=new WeakSet();document.addEventListener("mouseover",function(e){var t=e.target;if(!t||typeof t.closest!=="function")return;var a=t.closest("a[href^='/']");if(!a||s.has(a))return;if(a.dataset&&a.dataset.noPrefetch!==undefined)return;if(a.hasAttribute&&a.hasAttribute("download"))return;if(a.target&&a.target!=="_self")return;s.add(a);try{var l=document.createElement("link");l.rel="prefetch";l.href=a.href;l.as="document";document.head.appendChild(l);}catch(_){}},{passive:true,capture:true});})();`;
|
|
53
|
+
|
|
54
|
+
/** Ready-to-inject `<script>` tag for SSR `<head>` injection. */
|
|
55
|
+
export const PREFETCH_HELPER_SCRIPT = `<script>${PREFETCH_HELPER_BODY}</script>`;
|
package/src/client/router.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* SPA 스타일 네비게이션을 위한 클라이언트 라우터
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
|
|
7
7
|
import { startTransition } from "react";
|
|
8
8
|
import {
|
|
9
9
|
getManduData,
|
|
@@ -404,12 +404,12 @@ export async function navigate(
|
|
|
404
404
|
function handlePopState(event: PopStateEvent): void {
|
|
405
405
|
const state = event.state;
|
|
406
406
|
|
|
407
|
-
if (state?.routeId) {
|
|
408
|
-
// Mandu로 방문한 페이지 - 데이터 다시 fetch
|
|
409
|
-
void navigate(window.location.pathname + window.location.search, {
|
|
410
|
-
replace: true,
|
|
411
|
-
scroll: false,
|
|
412
|
-
});
|
|
407
|
+
if (state?.routeId) {
|
|
408
|
+
// Mandu로 방문한 페이지 - 데이터 다시 fetch
|
|
409
|
+
void navigate(window.location.pathname + window.location.search, {
|
|
410
|
+
replace: true,
|
|
411
|
+
scroll: false,
|
|
412
|
+
});
|
|
413
413
|
} else {
|
|
414
414
|
// SPA 네비게이션 이력이 아닌 페이지 — 현재 라우터 상태 유지
|
|
415
415
|
// (getManduRoute()는 SSR 초기값이라 SPA 네비게이션 후에는 stale)
|
|
@@ -576,10 +576,10 @@ function handleLinkClick(event: MouseEvent): void {
|
|
|
576
576
|
// All clear — prevent the default full-page navigation and hand off
|
|
577
577
|
// to the client-side router. `href` preserves the user's original
|
|
578
578
|
// string (relative paths, fragments, query strings) so the router
|
|
579
|
-
// can normalize as needed.
|
|
580
|
-
event.preventDefault();
|
|
581
|
-
void navigate(href);
|
|
582
|
-
}
|
|
579
|
+
// can normalize as needed.
|
|
580
|
+
event.preventDefault();
|
|
581
|
+
void navigate(href);
|
|
582
|
+
}
|
|
583
583
|
|
|
584
584
|
// ========== Prefetch ==========
|
|
585
585
|
|
package/src/client/runtime.ts
CHANGED
|
@@ -6,18 +6,18 @@
|
|
|
6
6
|
* 실제 Hydration Runtime은 bundler/build.ts의 generateRuntimeSource()에서 생성됩니다.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { getHydratedRoots, getServerData as getGlobalServerData } from "./window-state";
|
|
9
|
+
import { getHydratedRoots, getServerData as getGlobalServerData } from "./window-state";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Hydration 상태 추적
|
|
13
13
|
*/
|
|
14
|
-
export interface HydrationState {
|
|
15
|
-
total: number;
|
|
16
|
-
hydrated: number;
|
|
17
|
-
failed: number;
|
|
18
|
-
recoverableErrors: number;
|
|
19
|
-
pending: Set<string>;
|
|
20
|
-
}
|
|
14
|
+
export interface HydrationState {
|
|
15
|
+
total: number;
|
|
16
|
+
hydrated: number;
|
|
17
|
+
failed: number;
|
|
18
|
+
recoverableErrors: number;
|
|
19
|
+
pending: Set<string>;
|
|
20
|
+
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
23
|
* Hydration 우선순위
|
|
@@ -27,23 +27,23 @@ export type HydrationPriority = "immediate" | "visible" | "idle" | "interaction"
|
|
|
27
27
|
/**
|
|
28
28
|
* 서버 데이터 가져오기
|
|
29
29
|
*/
|
|
30
|
-
export function getServerData<T = unknown>(islandId: string): T | undefined {
|
|
31
|
-
if (typeof window === "undefined") return undefined;
|
|
32
|
-
return getGlobalServerData<T>(islandId);
|
|
33
|
-
}
|
|
30
|
+
export function getServerData<T = unknown>(islandId: string): T | undefined {
|
|
31
|
+
if (typeof window === "undefined") return undefined;
|
|
32
|
+
return getGlobalServerData<T>(islandId);
|
|
33
|
+
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Hydration 상태 조회 (DOM 기반)
|
|
37
37
|
*/
|
|
38
|
-
export function getHydrationState(): Readonly<HydrationState> {
|
|
39
|
-
if (typeof document === "undefined") {
|
|
40
|
-
return { total: 0, hydrated: 0, failed: 0, recoverableErrors: 0, pending: new Set() };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const islands = document.querySelectorAll<HTMLElement>("[data-mandu-island]");
|
|
44
|
-
const hydrated = document.querySelectorAll<HTMLElement>("[data-mandu-hydrated]");
|
|
45
|
-
const failed = document.querySelectorAll<HTMLElement>("[data-mandu-error]");
|
|
46
|
-
const recoverableErrors = document.querySelectorAll<HTMLElement>("[data-mandu-recoverable-error]");
|
|
38
|
+
export function getHydrationState(): Readonly<HydrationState> {
|
|
39
|
+
if (typeof document === "undefined") {
|
|
40
|
+
return { total: 0, hydrated: 0, failed: 0, recoverableErrors: 0, pending: new Set() };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const islands = document.querySelectorAll<HTMLElement>("[data-mandu-island]");
|
|
44
|
+
const hydrated = document.querySelectorAll<HTMLElement>("[data-mandu-hydrated]");
|
|
45
|
+
const failed = document.querySelectorAll<HTMLElement>("[data-mandu-error]");
|
|
46
|
+
const recoverableErrors = document.querySelectorAll<HTMLElement>("[data-mandu-recoverable-error]");
|
|
47
47
|
|
|
48
48
|
const pending = new Set<string>();
|
|
49
49
|
islands.forEach((el) => {
|
|
@@ -53,37 +53,37 @@ export function getHydrationState(): Readonly<HydrationState> {
|
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
-
return {
|
|
57
|
-
total: islands.length,
|
|
58
|
-
hydrated: hydrated.length,
|
|
59
|
-
failed: failed.length,
|
|
60
|
-
recoverableErrors: recoverableErrors.length,
|
|
61
|
-
pending,
|
|
62
|
-
};
|
|
63
|
-
}
|
|
56
|
+
return {
|
|
57
|
+
total: islands.length,
|
|
58
|
+
hydrated: hydrated.length,
|
|
59
|
+
failed: failed.length,
|
|
60
|
+
recoverableErrors: recoverableErrors.length,
|
|
61
|
+
pending,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* 특정 Island unmount
|
|
67
67
|
*/
|
|
68
|
-
export function unmountIsland(id: string): boolean {
|
|
69
|
-
const roots = getHydratedRoots();
|
|
70
|
-
const root = roots.get(id);
|
|
71
|
-
if (!root) {
|
|
72
|
-
return false;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
root.unmount();
|
|
76
|
-
roots.delete(id);
|
|
77
|
-
return true;
|
|
78
|
-
}
|
|
68
|
+
export function unmountIsland(id: string): boolean {
|
|
69
|
+
const roots = getHydratedRoots();
|
|
70
|
+
const root = roots.get(id);
|
|
71
|
+
if (!root) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
root.unmount();
|
|
76
|
+
roots.delete(id);
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* 모든 Island unmount
|
|
82
82
|
*/
|
|
83
|
-
export function unmountAllIslands(): void {
|
|
84
|
-
const roots = getHydratedRoots();
|
|
85
|
-
for (const [id, root] of roots) {
|
|
86
|
-
root.unmount();
|
|
87
|
-
roots.delete(id);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
83
|
+
export function unmountAllIslands(): void {
|
|
84
|
+
const roots = getHydratedRoots();
|
|
85
|
+
for (const [id, root] of roots) {
|
|
86
|
+
root.unmount();
|
|
87
|
+
roots.delete(id);
|
|
88
|
+
}
|
|
89
|
+
}
|