@ohos-ports/stacksjs-stx 0.2.287-beta.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/LICENSE.md +21 -0
- package/README.md +1042 -0
- package/dist/a11y.d.ts +256 -0
- package/dist/a11y.js +671 -0
- package/dist/analytics.d.ts +40 -0
- package/dist/analytics.js +159 -0
- package/dist/analyzer.d.ts +68 -0
- package/dist/analyzer.js +240 -0
- package/dist/animation.d.ts +123 -0
- package/dist/animation.js +688 -0
- package/dist/app-shell.d.ts +76 -0
- package/dist/app-shell.js +209 -0
- package/dist/app.d.ts +65 -0
- package/dist/app.js +79 -0
- package/dist/appearance-bootstrap.d.ts +54 -0
- package/dist/appearance-bootstrap.js +92 -0
- package/dist/asset-roots.d.ts +32 -0
- package/dist/asset-roots.js +7 -0
- package/dist/assets.d.ts +21 -0
- package/dist/assets.js +155 -0
- package/dist/ast.d.ts +265 -0
- package/dist/ast.js +630 -0
- package/dist/async-components.d.ts +123 -0
- package/dist/async-components.js +428 -0
- package/dist/auth.d.ts +13 -0
- package/dist/auth.js +12 -0
- package/dist/broadcasting.d.ts +77 -0
- package/dist/broadcasting.js +103 -0
- package/dist/browser-composables.d.ts +348 -0
- package/dist/browser-composables.js +606 -0
- package/dist/browser-core-imports.d.ts +12 -0
- package/dist/browser-core-imports.js +28 -0
- package/dist/build-assets.d.ts +32 -0
- package/dist/build-assets.js +29 -0
- package/dist/build-externalize-repeated.d.ts +34 -0
- package/dist/build-externalize-repeated.js +101 -0
- package/dist/build-externalize.d.ts +73 -0
- package/dist/build-externalize.js +80 -0
- package/dist/build-id.d.ts +57 -0
- package/dist/build-id.js +25 -0
- package/dist/build-message.d.ts +81 -0
- package/dist/build-message.js +65 -0
- package/dist/build-mode-detector.d.ts +26 -0
- package/dist/build-mode-detector.js +50 -0
- package/dist/build-optimizer.d.ts +101 -0
- package/dist/build-optimizer.js +433 -0
- package/dist/build-views.d.ts +56 -0
- package/dist/build-views.js +180 -0
- package/dist/build.d.ts +31 -0
- package/dist/build.js +34 -0
- package/dist/builtins/attrs.d.ts +49 -0
- package/dist/builtins/attrs.js +39 -0
- package/dist/builtins/drawer.d.ts +24 -0
- package/dist/builtins/drawer.js +37 -0
- package/dist/builtins/escape.d.ts +12 -0
- package/dist/builtins/escape.js +3 -0
- package/dist/builtins/icon.d.ts +45 -0
- package/dist/builtins/icon.js +100 -0
- package/dist/builtins/image-delivery.d.ts +16 -0
- package/dist/builtins/image-delivery.js +115 -0
- package/dist/builtins/image-placeholder.d.ts +78 -0
- package/dist/builtins/image-placeholder.js +137 -0
- package/dist/builtins/index.d.ts +26 -0
- package/dist/builtins/index.js +34 -0
- package/dist/builtins/modal.d.ts +26 -0
- package/dist/builtins/modal.js +38 -0
- package/dist/builtins/safe-image.d.ts +44 -0
- package/dist/builtins/safe-image.js +45 -0
- package/dist/builtins/stx-image.d.ts +29 -0
- package/dist/builtins/stx-image.js +222 -0
- package/dist/builtins/stx-link.d.ts +11 -0
- package/dist/builtins/stx-link.js +35 -0
- package/dist/builtins/stx-loading-indicator.d.ts +13 -0
- package/dist/builtins/stx-loading-indicator.js +35 -0
- package/dist/builtins/suspense.d.ts +30 -0
- package/dist/builtins/suspense.js +24 -0
- package/dist/builtins/toast.d.ts +19 -0
- package/dist/builtins/toast.js +42 -0
- package/dist/builtins/tooltip.d.ts +18 -0
- package/dist/builtins/tooltip.js +106 -0
- package/dist/builtins/transition-group.d.ts +27 -0
- package/dist/builtins/transition-group.js +22 -0
- package/dist/bun-shim.js +496 -0
- package/dist/bundle-analyzer/collector.d.ts +66 -0
- package/dist/bundle-analyzer/collector.js +233 -0
- package/dist/bundle-analyzer/index.d.ts +98 -0
- package/dist/bundle-analyzer/index.js +82 -0
- package/dist/bundle-analyzer/report.d.ts +39 -0
- package/dist/bundle-analyzer/report.js +318 -0
- package/dist/bundle-analyzer/treemap.d.ts +19 -0
- package/dist/bundle-analyzer/treemap.js +594 -0
- package/dist/bundle-analyzer.js +499 -0
- package/dist/caching.d.ts +69 -0
- package/dist/caching.js +135 -0
- package/dist/chunk-0y46fbkx.js +3 -0
- package/dist/chunk-122tyfre.js +2 -0
- package/dist/chunk-19nfvxfw.js +43 -0
- package/dist/chunk-1kah92n2.js +3 -0
- package/dist/chunk-1kms3b9d.js +2 -0
- package/dist/chunk-1q4y74mh.js +1194 -0
- package/dist/chunk-248e84r1.js +4 -0
- package/dist/chunk-24ps8kft.js +2 -0
- package/dist/chunk-2et5hxfv.js +2 -0
- package/dist/chunk-2r1rd8f0.js +2 -0
- package/dist/chunk-2tk1zkrz.js +2 -0
- package/dist/chunk-38ejahd9.js +3 -0
- package/dist/chunk-3zv4wz87.js +2 -0
- package/dist/chunk-4122k1bh.js +2 -0
- package/dist/chunk-45az9zht.js +2 -0
- package/dist/chunk-4qr85f07.js +2 -0
- package/dist/chunk-4ww3znrr.js +15 -0
- package/dist/chunk-54st8g2j.js +397 -0
- package/dist/chunk-58f0mthg.js +3 -0
- package/dist/chunk-5a19dhx5.js +2 -0
- package/dist/chunk-5kk8gsj2.js +3 -0
- package/dist/chunk-5w1e3yxs.js +499 -0
- package/dist/chunk-6gdycmxr.js +2 -0
- package/dist/chunk-6hmnaza7.js +4473 -0
- package/dist/chunk-71t0yan0.js +5 -0
- package/dist/chunk-74cv4ta1.js +3 -0
- package/dist/chunk-74v15e8z.js +2 -0
- package/dist/chunk-7b329e5v.js +3 -0
- package/dist/chunk-7q9mwh4v.js +84 -0
- package/dist/chunk-866y9d8b.js +8169 -0
- package/dist/chunk-8fwerspy.js +2 -0
- package/dist/chunk-8mjdpr94.js +147 -0
- package/dist/chunk-92mj1t26.js +60 -0
- package/dist/chunk-9b22663r.js +2 -0
- package/dist/chunk-9hjdgm4d.js +2 -0
- package/dist/chunk-9n0gvfrg.js +4 -0
- package/dist/chunk-9wr7zcad.js +18 -0
- package/dist/chunk-9y0g8kzt.js +358 -0
- package/dist/chunk-a0nfbmeh.js +3 -0
- package/dist/chunk-acn1b56m.js +1280 -0
- package/dist/chunk-awkq3xfx.js +2 -0
- package/dist/chunk-b6ej2fdf.js +1814 -0
- package/dist/chunk-b8rrnqry.js +2 -0
- package/dist/chunk-bfqfs07m.js +3022 -0
- package/dist/chunk-bs6kapy7.js +2 -0
- package/dist/chunk-btav940g.js +2 -0
- package/dist/chunk-bz820epz.js +3 -0
- package/dist/chunk-ccspd3mc.js +2 -0
- package/dist/chunk-csbgwaa5.js +2 -0
- package/dist/chunk-d8t4sjsp.js +2 -0
- package/dist/chunk-dap191q2.js +2 -0
- package/dist/chunk-de7fye10.js +4 -0
- package/dist/chunk-dgrt76d2.js +2 -0
- package/dist/chunk-dhh5q7qt.js +3 -0
- package/dist/chunk-dsffsqnt.js +2 -0
- package/dist/chunk-dtq7y5ym.js +2 -0
- package/dist/chunk-fed3xsp9.js +3 -0
- package/dist/chunk-ferav3bw.js +19 -0
- package/dist/chunk-fvhm8psm.js +3 -0
- package/dist/chunk-g4vpzv6b.js +2 -0
- package/dist/chunk-g9csmj0s.js +2 -0
- package/dist/chunk-gtcrpmz1.js +2 -0
- package/dist/chunk-h4xh8cms.js +4 -0
- package/dist/chunk-h8yqsz9m.js +2 -0
- package/dist/chunk-hcq5dw7a.js +2 -0
- package/dist/chunk-hm0xha6c.js +12 -0
- package/dist/chunk-hpybjwwf.js +8 -0
- package/dist/chunk-jegt35zh.js +2 -0
- package/dist/chunk-jr3jp3ev.js +8 -0
- package/dist/chunk-k56rjjc0.js +24 -0
- package/dist/chunk-kdh37tp8.js +194 -0
- package/dist/chunk-kxzb4et3.js +29 -0
- package/dist/chunk-mf4n3x1j.js +14 -0
- package/dist/chunk-mkd9kjgb.js +3 -0
- package/dist/chunk-mxfh1nxg.js +2 -0
- package/dist/chunk-n3ggrhb3.js +3 -0
- package/dist/chunk-nkb3scg6.js +8 -0
- package/dist/chunk-nnd4wq8j.js +231 -0
- package/dist/chunk-nsp1cw38.js +11 -0
- package/dist/chunk-nx5qkm8k.js +30 -0
- package/dist/chunk-p3242nck.js +2 -0
- package/dist/chunk-p98kfh94.js +2 -0
- package/dist/chunk-pkaabd8h.js +3 -0
- package/dist/chunk-pzm13sdc.js +3 -0
- package/dist/chunk-q4p5k2p3.js +2 -0
- package/dist/chunk-q9n5swx7.js +3 -0
- package/dist/chunk-qc1f74ek.js +3 -0
- package/dist/chunk-qpyjak7z.js +2 -0
- package/dist/chunk-qr82je1m.js +22 -0
- package/dist/chunk-qy73j6m4.js +288 -0
- package/dist/chunk-qzkhpsam.js +505 -0
- package/dist/chunk-r3ayvdz3.js +2 -0
- package/dist/chunk-r7r66978.js +2 -0
- package/dist/chunk-rbb7dvja.js +30 -0
- package/dist/chunk-refwkt87.js +71 -0
- package/dist/chunk-rj4kajww.js +2 -0
- package/dist/chunk-rp4rjjmf.js +2 -0
- package/dist/chunk-se0p17c2.js +898 -0
- package/dist/chunk-sj9pmene.js +2 -0
- package/dist/chunk-sscwkffs.js +2 -0
- package/dist/chunk-svpqxn2c.js +2 -0
- package/dist/chunk-t6nseeb9.js +351 -0
- package/dist/chunk-t8y1ggdy.js +2 -0
- package/dist/chunk-tndhnk9e.js +2 -0
- package/dist/chunk-v5e1gxwm.js +3 -0
- package/dist/chunk-vqxmxgaj.js +4 -0
- package/dist/chunk-vrrax298.js +2 -0
- package/dist/chunk-w3ycm4na.js +3 -0
- package/dist/chunk-wcm80anz.js +321 -0
- package/dist/chunk-wrzwgkbx.js +22 -0
- package/dist/chunk-x650vjcp.js +302 -0
- package/dist/chunk-xgnc1wy6.js +116 -0
- package/dist/chunk-xpp135ad.js +11 -0
- package/dist/chunk-xttsqmkm.js +216 -0
- package/dist/chunk-y11cxbxd.js +3757 -0
- package/dist/chunk-y7m2dpd3.js +341 -0
- package/dist/chunk-y98jsjqq.js +2 -0
- package/dist/chunk-ya386h70.js +2 -0
- package/dist/chunk-ydrz60kr.js +2 -0
- package/dist/chunk-ypmqf7tm.js +4 -0
- package/dist/chunk-z4jhj5e5.js +28 -0
- package/dist/chunk-zg75gjns.js +2 -0
- package/dist/chunk-zqqx1qaj.js +2 -0
- package/dist/cli-flags.d.ts +34 -0
- package/dist/cli-flags.js +20 -0
- package/dist/cli.js +174 -0
- package/dist/client/directive.d.ts +17 -0
- package/dist/client/directive.js +14 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.js +2 -0
- package/dist/client-helpers.d.ts +317 -0
- package/dist/client-helpers.js +312 -0
- package/dist/client-script-bundler.d.ts +103 -0
- package/dist/client-script-bundler.js +390 -0
- package/dist/client-script.d.ts +215 -0
- package/dist/client-script.js +865 -0
- package/dist/client.d.ts +21 -0
- package/dist/client.js +73 -0
- package/dist/codemod.d.ts +33 -0
- package/dist/codemod.js +244 -0
- package/dist/color-mode-boot.d.ts +92 -0
- package/dist/color-mode-boot.js +72 -0
- package/dist/component-client-factories.d.ts +33 -0
- package/dist/component-client-factories.js +77 -0
- package/dist/component-hmr.d.ts +107 -0
- package/dist/component-hmr.js +585 -0
- package/dist/component-library.d.ts +38 -0
- package/dist/component-library.js +994 -0
- package/dist/component-processing.d.ts +198 -0
- package/dist/component-processing.js +677 -0
- package/dist/component-registry.d.ts +127 -0
- package/dist/component-registry.js +90 -0
- package/dist/component-renderer.d.ts +52 -0
- package/dist/component-renderer.js +790 -0
- package/dist/components/StxImage.stx +12 -0
- package/dist/components/StxLink.stx +18 -0
- package/dist/components.d.ts +28 -0
- package/dist/components.js +213 -0
- package/dist/composable-loader.d.ts +62 -0
- package/dist/composable-loader.js +190 -0
- package/dist/composables/index.d.ts +417 -0
- package/dist/composables/index.js +222 -0
- package/dist/composables/use-battery.d.ts +46 -0
- package/dist/composables/use-battery.js +101 -0
- package/dist/composables/use-broadcast-channel.d.ts +59 -0
- package/dist/composables/use-broadcast-channel.js +80 -0
- package/dist/composables/use-channel.d.ts +67 -0
- package/dist/composables/use-channel.js +124 -0
- package/dist/composables/use-clipboard.d.ts +30 -0
- package/dist/composables/use-clipboard.js +90 -0
- package/dist/composables/use-color-mode.d.ts +67 -0
- package/dist/composables/use-color-mode.js +152 -0
- package/dist/composables/use-cookie.d.ts +59 -0
- package/dist/composables/use-cookie.js +86 -0
- package/dist/composables/use-device-orientation.d.ts +116 -0
- package/dist/composables/use-device-orientation.js +181 -0
- package/dist/composables/use-event-listener.d.ts +35 -0
- package/dist/composables/use-event-listener.js +27 -0
- package/dist/composables/use-event-source.d.ts +90 -0
- package/dist/composables/use-event-source.js +166 -0
- package/dist/composables/use-eye-dropper.d.ts +117 -0
- package/dist/composables/use-eye-dropper.js +142 -0
- package/dist/composables/use-fetch.d.ts +59 -0
- package/dist/composables/use-fetch.js +273 -0
- package/dist/composables/use-form.d.ts +91 -0
- package/dist/composables/use-form.js +112 -0
- package/dist/composables/use-fullscreen.d.ts +47 -0
- package/dist/composables/use-fullscreen.js +110 -0
- package/dist/composables/use-geolocation.d.ts +62 -0
- package/dist/composables/use-geolocation.js +101 -0
- package/dist/composables/use-idle.d.ts +102 -0
- package/dist/composables/use-idle.js +182 -0
- package/dist/composables/use-intersection-observer.d.ts +83 -0
- package/dist/composables/use-intersection-observer.js +168 -0
- package/dist/composables/use-keyboard.d.ts +100 -0
- package/dist/composables/use-keyboard.js +224 -0
- package/dist/composables/use-media-query.d.ts +56 -0
- package/dist/composables/use-media-query.js +62 -0
- package/dist/composables/use-meta.d.ts +35 -0
- package/dist/composables/use-meta.js +48 -0
- package/dist/composables/use-mouse.d.ts +65 -0
- package/dist/composables/use-mouse.js +286 -0
- package/dist/composables/use-mutation-observer.d.ts +101 -0
- package/dist/composables/use-mutation-observer.js +129 -0
- package/dist/composables/use-network.d.ts +40 -0
- package/dist/composables/use-network.js +85 -0
- package/dist/composables/use-notification.d.ts +98 -0
- package/dist/composables/use-notification.js +94 -0
- package/dist/composables/use-permissions.d.ts +119 -0
- package/dist/composables/use-permissions.js +132 -0
- package/dist/composables/use-query.d.ts +48 -0
- package/dist/composables/use-query.js +0 -0
- package/dist/composables/use-resize-observer.d.ts +60 -0
- package/dist/composables/use-resize-observer.js +157 -0
- package/dist/composables/use-router.d.ts +80 -0
- package/dist/composables/use-router.js +105 -0
- package/dist/composables/use-scroll-lock.d.ts +13 -0
- package/dist/composables/use-scroll-lock.js +55 -0
- package/dist/composables/use-share.d.ts +74 -0
- package/dist/composables/use-share.js +104 -0
- package/dist/composables/use-speech.d.ts +117 -0
- package/dist/composables/use-speech.js +294 -0
- package/dist/composables/use-storage.d.ts +69 -0
- package/dist/composables/use-storage.js +196 -0
- package/dist/composables/use-text-selection.d.ts +121 -0
- package/dist/composables/use-text-selection.js +205 -0
- package/dist/composables/use-timer.d.ts +92 -0
- package/dist/composables/use-timer.js +193 -0
- package/dist/composables/use-utilities.d.ts +103 -0
- package/dist/composables/use-utilities.js +176 -0
- package/dist/composables/use-wake-lock.d.ts +85 -0
- package/dist/composables/use-wake-lock.js +122 -0
- package/dist/composables/use-websocket.d.ts +70 -0
- package/dist/composables/use-websocket.js +165 -0
- package/dist/composables/use-window.d.ts +84 -0
- package/dist/composables/use-window.js +163 -0
- package/dist/composables.d.ts +287 -0
- package/dist/composables.js +562 -0
- package/dist/composition-api.d.ts +197 -0
- package/dist/composition-api.js +199 -0
- package/dist/compression.d.ts +30 -0
- package/dist/compression.js +107 -0
- package/dist/computed.d.ts +138 -0
- package/dist/computed.js +382 -0
- package/dist/conditionals.d.ts +48 -0
- package/dist/conditionals.js +626 -0
- package/dist/config.d.ts +77 -0
- package/dist/config.js +828 -0
- package/dist/cookie-serialize.d.ts +40 -0
- package/dist/cookie-serialize.js +18 -0
- package/dist/craft-bridge.d.ts +525 -0
- package/dist/craft-bridge.js +511 -0
- package/dist/craft-compiler.d.ts +245 -0
- package/dist/craft-compiler.js +810 -0
- package/dist/craft-components.d.ts +739 -0
- package/dist/craft-components.js +1074 -0
- package/dist/craft-entry.d.ts +6 -0
- package/dist/craft-entry.js +9 -0
- package/dist/craft-ssr.d.ts +147 -0
- package/dist/craft-ssr.js +559 -0
- package/dist/craft.js +9 -0
- package/dist/csp.d.ts +295 -0
- package/dist/csp.js +220 -0
- package/dist/csrf.d.ts +38 -0
- package/dist/csrf.js +38 -0
- package/dist/custom-directives.d.ts +5 -0
- package/dist/custom-directives.js +136 -0
- package/dist/database.d.ts +391 -0
- package/dist/database.js +5 -0
- package/dist/defer.d.ts +28 -0
- package/dist/defer.js +156 -0
- package/dist/deploy/config-generators.d.ts +75 -0
- package/dist/deploy/config-generators.js +214 -0
- package/dist/deploy/index.d.ts +88 -0
- package/dist/deploy/index.js +203 -0
- package/dist/deploy/netlify.d.ts +109 -0
- package/dist/deploy/netlify.js +167 -0
- package/dist/desktop.d.ts +16 -0
- package/dist/desktop.js +1 -0
- package/dist/dev-server/index.d.ts +22 -0
- package/dist/dev-server/index.js +7 -0
- package/dist/dev-server/keyboard-shortcuts.d.ts +34 -0
- package/dist/dev-server/keyboard-shortcuts.js +81 -0
- package/dist/dev-server/native-window.d.ts +59 -0
- package/dist/dev-server/native-window.js +91 -0
- package/dist/dev-server/port-utils.d.ts +27 -0
- package/dist/dev-server/port-utils.js +40 -0
- package/dist/dev-server/serve-app.d.ts +15 -0
- package/dist/dev-server/serve-app.js +727 -0
- package/dist/dev-server/serve-file.d.ts +3 -0
- package/dist/dev-server/serve-file.js +443 -0
- package/dist/dev-server/serve-markdown.d.ts +3 -0
- package/dist/dev-server/serve-markdown.js +284 -0
- package/dist/dev-server/serve-multi.d.ts +5 -0
- package/dist/dev-server/serve-multi.js +596 -0
- package/dist/dev-server/terminal-colors.d.ts +56 -0
- package/dist/dev-server/terminal-colors.js +45 -0
- package/dist/dev-server/theme-selector.d.ts +32 -0
- package/dist/dev-server/theme-selector.js +111 -0
- package/dist/dev-server/ts-css.d.ts +108 -0
- package/dist/dev-server/ts-css.js +438 -0
- package/dist/dev-server/types.d.ts +99 -0
- package/dist/dev-server/types.js +43 -0
- package/dist/dev-server.d.ts +6 -0
- package/dist/dev-server.js +4 -0
- package/dist/devtools.d.ts +182 -0
- package/dist/devtools.js +593 -0
- package/dist/directive-api.d.ts +154 -0
- package/dist/directive-api.js +355 -0
- package/dist/docs.d.ts +56 -0
- package/dist/docs.js +567 -0
- package/dist/doctor.d.ts +29 -0
- package/dist/doctor.js +123 -0
- package/dist/document-shell.d.ts +218 -0
- package/dist/document-shell.js +338 -0
- package/dist/dynamic-components.d.ts +23 -0
- package/dist/dynamic-components.js +197 -0
- package/dist/edge-runtime.d.ts +193 -0
- package/dist/edge-runtime.js +525 -0
- package/dist/env.d.ts +3 -0
- package/dist/env.js +9 -0
- package/dist/error-boundaries.d.ts +78 -0
- package/dist/error-boundaries.js +281 -0
- package/dist/error-handling.d.ts +14 -0
- package/dist/error-handling.js +1 -0
- package/dist/errors/codes.d.ts +92 -0
- package/dist/errors/codes.js +97 -0
- package/dist/errors/formatter.d.ts +84 -0
- package/dist/errors/formatter.js +196 -0
- package/dist/errors/index.d.ts +12 -0
- package/dist/errors/index.js +44 -0
- package/dist/errors/logger.d.ts +135 -0
- package/dist/errors/logger.js +250 -0
- package/dist/errors/sanitizer.d.ts +73 -0
- package/dist/errors/sanitizer.js +181 -0
- package/dist/errors/types.d.ts +92 -0
- package/dist/errors/types.js +86 -0
- package/dist/events.d.ts +145 -0
- package/dist/events.js +396 -0
- package/dist/expressions.d.ts +180 -0
- package/dist/expressions.js +719 -0
- package/dist/find-body-tag.d.ts +50 -0
- package/dist/find-body-tag.js +57 -0
- package/dist/formatter.d.ts +19 -0
- package/dist/formatter.js +265 -0
- package/dist/forms-validation.d.ts +189 -0
- package/dist/forms-validation.js +421 -0
- package/dist/forms.d.ts +224 -0
- package/dist/forms.js +729 -0
- package/dist/fragment-container.d.ts +57 -0
- package/dist/fragment-container.js +123 -0
- package/dist/framework-composables.d.ts +73 -0
- package/dist/framework-composables.js +126 -0
- package/dist/head-injection.d.ts +61 -0
- package/dist/head-injection.js +28 -0
- package/dist/head.d.ts +282 -0
- package/dist/head.js +231 -0
- package/dist/heatmap.d.ts +206 -0
- package/dist/heatmap.js +566 -0
- package/dist/hot-reload.d.ts +87 -0
- package/dist/hot-reload.js +373 -0
- package/dist/html-masking.d.ts +125 -0
- package/dist/html-masking.js +196 -0
- package/dist/hydration-runtime.d.ts +44 -0
- package/dist/hydration-runtime.js +195 -0
- package/dist/hydration.d.ts +135 -0
- package/dist/hydration.js +654 -0
- package/dist/i18n.d.ts +342 -0
- package/dist/i18n.js +678 -0
- package/dist/image-optimization/auto-sibling.d.ts +42 -0
- package/dist/image-optimization/auto-sibling.js +88 -0
- package/dist/image-optimization/build-plugin.d.ts +54 -0
- package/dist/image-optimization/build-plugin.js +241 -0
- package/dist/image-optimization/component.d.ts +46 -0
- package/dist/image-optimization/component.js +244 -0
- package/dist/image-optimization/directive.d.ts +30 -0
- package/dist/image-optimization/directive.js +203 -0
- package/dist/image-optimization/index.d.ts +156 -0
- package/dist/image-optimization/index.js +54 -0
- package/dist/image-optimization/processor.d.ts +140 -0
- package/dist/image-optimization/processor.js +262 -0
- package/dist/image-optimization/serve.d.ts +54 -0
- package/dist/image-optimization/serve.js +102 -0
- package/dist/import-rebase.d.ts +16 -0
- package/dist/import-rebase.js +16 -0
- package/dist/imported-signals.d.ts +43 -0
- package/dist/imported-signals.js +35 -0
- package/dist/includes.d.ts +171 -0
- package/dist/includes.js +764 -0
- package/dist/index.d.ts +365 -0
- package/dist/index.js +202 -0
- package/dist/init.d.ts +39 -0
- package/dist/init.js +505 -0
- package/dist/inline-assets.d.ts +28 -0
- package/dist/inline-assets.js +82 -0
- package/dist/interactive.d.ts +14 -0
- package/dist/interactive.js +383 -0
- package/dist/internal-markdown.d.ts +22 -0
- package/dist/internal-markdown.js +141 -0
- package/dist/island-chunking.d.ts +48 -0
- package/dist/island-chunking.js +52 -0
- package/dist/isolated-build.d.ts +12 -0
- package/dist/isolated-build.js +18 -0
- package/dist/js-ts.d.ts +10 -0
- package/dist/js-ts.js +60 -0
- package/dist/jsx-runtime.d.ts +92 -0
- package/dist/jsx-runtime.js +289 -0
- package/dist/keep-alive.d.ts +108 -0
- package/dist/keep-alive.js +421 -0
- package/dist/layout-types.d.ts +45 -0
- package/dist/layout-types.js +56 -0
- package/dist/lazy-loader.d.ts +90 -0
- package/dist/lazy-loader.js +245 -0
- package/dist/lazy-module.d.ts +11 -0
- package/dist/lazy-module.js +18 -0
- package/dist/loading-indicator.d.ts +50 -0
- package/dist/loading-indicator.js +322 -0
- package/dist/loops.d.ts +91 -0
- package/dist/loops.js +690 -0
- package/dist/manifest.d.ts +41 -0
- package/dist/manifest.js +31 -0
- package/dist/markdown-view.d.ts +25 -0
- package/dist/markdown-view.js +39 -0
- package/dist/markdown.d.ts +10 -0
- package/dist/markdown.js +68 -0
- package/dist/media/client/blur-up.d.ts +66 -0
- package/dist/media/client/blur-up.js +217 -0
- package/dist/media/client/index.d.ts +93 -0
- package/dist/media/client/index.js +53 -0
- package/dist/media/client/lazy-load.d.ts +74 -0
- package/dist/media/client/lazy-load.js +283 -0
- package/dist/media/client/upload-handler.d.ts +80 -0
- package/dist/media/client/upload-handler.js +354 -0
- package/dist/media/image/component.d.ts +60 -0
- package/dist/media/image/component.js +574 -0
- package/dist/media/image/directive.d.ts +23 -0
- package/dist/media/image/directive.js +160 -0
- package/dist/media/image/editing.d.ts +212 -0
- package/dist/media/image/editing.js +193 -0
- package/dist/media/image/index.d.ts +142 -0
- package/dist/media/image/index.js +80 -0
- package/dist/media/image/placeholder.d.ts +89 -0
- package/dist/media/image/placeholder.js +188 -0
- package/dist/media/image/processor/cache.d.ts +53 -0
- package/dist/media/image/processor/cache.js +51 -0
- package/dist/media/image/processor/index.d.ts +11 -0
- package/dist/media/image/processor/index.js +3 -0
- package/dist/media/image/processor/optimizer.d.ts +24 -0
- package/dist/media/image/processor/optimizer.js +202 -0
- package/dist/media/image/processor/responsive.d.ts +28 -0
- package/dist/media/image/processor/responsive.js +162 -0
- package/dist/media/image/srcset.d.ts +157 -0
- package/dist/media/image/srcset.js +194 -0
- package/dist/media/index.d.ts +331 -0
- package/dist/media/index.js +198 -0
- package/dist/media/manager/embed.d.ts +25 -0
- package/dist/media/manager/embed.js +253 -0
- package/dist/media/protected/component.d.ts +33 -0
- package/dist/media/protected/component.js +235 -0
- package/dist/media/protected/index.d.ts +47 -0
- package/dist/media/protected/index.js +20 -0
- package/dist/media/protected/signature.d.ts +72 -0
- package/dist/media/protected/signature.js +205 -0
- package/dist/media/shared/cache.d.ts +76 -0
- package/dist/media/shared/cache.js +132 -0
- package/dist/media/shared/hash.d.ts +31 -0
- package/dist/media/shared/hash.js +35 -0
- package/dist/media/shared/index.d.ts +7 -0
- package/dist/media/shared/index.js +2 -0
- package/dist/media/types.d.ts +1477 -0
- package/dist/media/types.js +0 -0
- package/dist/media/upload/component.d.ts +9 -0
- package/dist/media/upload/component.js +320 -0
- package/dist/media/upload/index.d.ts +8 -0
- package/dist/media/upload/index.js +1 -0
- package/dist/media/video/directive.d.ts +23 -0
- package/dist/media/video/directive.js +142 -0
- package/dist/media/video/index.d.ts +76 -0
- package/dist/media/video/index.js +583 -0
- package/dist/media/video/processor/cache.d.ts +55 -0
- package/dist/media/video/processor/cache.js +52 -0
- package/dist/media/video/processor/index.d.ts +12 -0
- package/dist/media/video/processor/index.js +4 -0
- package/dist/media/video/processor/streaming.d.ts +26 -0
- package/dist/media/video/processor/streaming.js +197 -0
- package/dist/media/video/processor/thumbnail.d.ts +35 -0
- package/dist/media/video/processor/thumbnail.js +134 -0
- package/dist/media/video/processor/transcoder.d.ts +19 -0
- package/dist/media/video/processor/transcoder.js +283 -0
- package/dist/menubar.d.ts +65 -0
- package/dist/menubar.js +99 -0
- package/dist/method-spoofing.d.ts +20 -0
- package/dist/method-spoofing.js +17 -0
- package/dist/middleware.d.ts +112 -0
- package/dist/middleware.js +40 -0
- package/dist/misc-directives.d.ts +55 -0
- package/dist/misc-directives.js +328 -0
- package/dist/native-build.d.ts +88 -0
- package/dist/native-build.js +575 -0
- package/dist/owned-routes.d.ts +42 -0
- package/dist/owned-routes.js +26 -0
- package/dist/page-action.d.ts +73 -0
- package/dist/page-action.js +91 -0
- package/dist/page-meta.d.ts +38 -0
- package/dist/page-meta.js +16 -0
- package/dist/page-response.d.ts +120 -0
- package/dist/page-response.js +117 -0
- package/dist/parser/directive-parser.d.ts +119 -0
- package/dist/parser/directive-parser.js +249 -0
- package/dist/parser/expression-parser.d.ts +58 -0
- package/dist/parser/expression-parser.js +358 -0
- package/dist/parser/index.d.ts +13 -0
- package/dist/parser/index.js +23 -0
- package/dist/parser/tokenizer.d.ts +77 -0
- package/dist/parser/tokenizer.js +534 -0
- package/dist/partial-hydration.d.ts +123 -0
- package/dist/partial-hydration.js +427 -0
- package/dist/performance-utils.d.ts +318 -0
- package/dist/performance-utils.js +466 -0
- package/dist/placeholder.d.ts +54 -0
- package/dist/placeholder.js +35 -0
- package/dist/plugin-system.d.ts +301 -0
- package/dist/plugin-system.js +214 -0
- package/dist/plugin.d.ts +3 -0
- package/dist/plugin.js +174 -0
- package/dist/precompiler.d.ts +43 -0
- package/dist/precompiler.js +670 -0
- package/dist/process.d.ts +16 -0
- package/dist/process.js +879 -0
- package/dist/production-build.d.ts +163 -0
- package/dist/production-build.js +596 -0
- package/dist/production-builder.d.ts +42 -0
- package/dist/production-builder.js +146 -0
- package/dist/production-server.d.ts +19 -0
- package/dist/production-server.js +208 -0
- package/dist/prop-sentinels.d.ts +31 -0
- package/dist/prop-sentinels.js +7 -0
- package/dist/props.d.ts +216 -0
- package/dist/props.js +224 -0
- package/dist/public-env.d.ts +29 -0
- package/dist/public-env.js +19 -0
- package/dist/pwa/audit.d.ts +42 -0
- package/dist/pwa/audit.js +255 -0
- package/dist/pwa/directives.d.ts +38 -0
- package/dist/pwa/directives.js +332 -0
- package/dist/pwa/icons.d.ts +42 -0
- package/dist/pwa/icons.js +109 -0
- package/dist/pwa/index.d.ts +104 -0
- package/dist/pwa/index.js +47 -0
- package/dist/pwa/inject.d.ts +32 -0
- package/dist/pwa/inject.js +102 -0
- package/dist/pwa/manifest.d.ts +104 -0
- package/dist/pwa/manifest.js +186 -0
- package/dist/pwa/offline.d.ts +8 -0
- package/dist/pwa/offline.js +259 -0
- package/dist/pwa/precache.d.ts +35 -0
- package/dist/pwa/precache.js +116 -0
- package/dist/pwa/service-worker.d.ts +33 -0
- package/dist/pwa/service-worker.js +860 -0
- package/dist/pwa/workbox-strategies.d.ts +100 -0
- package/dist/pwa/workbox-strategies.js +167 -0
- package/dist/pwa/workbox.d.ts +51 -0
- package/dist/pwa/workbox.js +382 -0
- package/dist/pwa.d.ts +52 -0
- package/dist/pwa.js +236 -0
- package/dist/reactive-bindings.d.ts +47 -0
- package/dist/reactive-bindings.js +268 -0
- package/dist/reactive-tracking.d.ts +81 -0
- package/dist/reactive-tracking.js +49 -0
- package/dist/reactive.d.ts +117 -0
- package/dist/reactive.js +593 -0
- package/dist/reactivity.d.ts +206 -0
- package/dist/reactivity.js +574 -0
- package/dist/release.d.ts +1 -0
- package/dist/release.js +1 -0
- package/dist/render-memo.d.ts +40 -0
- package/dist/render-memo.js +33 -0
- package/dist/render.d.ts +54 -0
- package/dist/render.js +153 -0
- package/dist/resolve-stx-targets.d.ts +49 -0
- package/dist/resolve-stx-targets.js +38 -0
- package/dist/route-middleware.d.ts +2 -0
- package/dist/route-middleware.js +17 -0
- package/dist/route-types.d.ts +134 -0
- package/dist/route-types.js +24 -0
- package/dist/router.d.ts +9 -0
- package/dist/router.js +9 -0
- package/dist/routes.d.ts +6 -0
- package/dist/routes.js +14 -0
- package/dist/runtime-globals.d.ts +236 -0
- package/dist/runtime-globals.js +230 -0
- package/dist/runtime-injection.d.ts +113 -0
- package/dist/runtime-injection.js +156 -0
- package/dist/runtime.d.ts +140 -0
- package/dist/runtime.js +94 -0
- package/dist/safe-evaluator.d.ts +165 -0
- package/dist/safe-evaluator.js +461 -0
- package/dist/scaffolding.d.ts +113 -0
- package/dist/scaffolding.js +897 -0
- package/dist/screenshot.d.ts +22 -0
- package/dist/screenshot.js +100 -0
- package/dist/script-classifier.d.ts +41 -0
- package/dist/script-classifier.js +51 -0
- package/dist/script-emit.d.ts +50 -0
- package/dist/script-emit.js +11 -0
- package/dist/script-json.d.ts +36 -0
- package/dist/script-json.js +6 -0
- package/dist/script-validation.d.ts +26 -0
- package/dist/script-validation.js +179 -0
- package/dist/seo.d.ts +225 -0
- package/dist/seo.js +486 -0
- package/dist/serve.d.ts +44 -0
- package/dist/serve.js +233 -0
- package/dist/server-bindings.d.ts +39 -0
- package/dist/server-bindings.js +171 -0
- package/dist/server-components.d.ts +138 -0
- package/dist/server-components.js +427 -0
- package/dist/sfc-template.d.ts +17 -0
- package/dist/sfc-template.js +32 -0
- package/dist/signal-processing.d.ts +222 -0
- package/dist/signal-processing.js +1081 -0
- package/dist/signals-api.d.ts +459 -0
- package/dist/signals-api.js +172 -0
- package/dist/signals.d.ts +41 -0
- package/dist/signals.js +8233 -0
- package/dist/site-builder/build.d.ts +18 -0
- package/dist/site-builder/build.js +252 -0
- package/dist/site-builder/i18n.d.ts +104 -0
- package/dist/site-builder/i18n.js +150 -0
- package/dist/site-builder/index.d.ts +27 -0
- package/dist/site-builder/index.js +16 -0
- package/dist/site-builder/robots.d.ts +2 -0
- package/dist/site-builder/robots.js +26 -0
- package/dist/site-builder/router.d.ts +10 -0
- package/dist/site-builder/router.js +27 -0
- package/dist/site-builder/seo.d.ts +8 -0
- package/dist/site-builder/seo.js +67 -0
- package/dist/site-builder/sitemap.d.ts +10 -0
- package/dist/site-builder/sitemap.js +26 -0
- package/dist/site-builder/theme.d.ts +12 -0
- package/dist/site-builder/theme.js +38 -0
- package/dist/site-builder/types.d.ts +74 -0
- package/dist/site-builder/types.js +0 -0
- package/dist/site-url.d.ts +46 -0
- package/dist/site-url.js +70 -0
- package/dist/slots.d.ts +129 -0
- package/dist/slots.js +286 -0
- package/dist/source-maps.d.ts +98 -0
- package/dist/source-maps.js +350 -0
- package/dist/source-signature.d.ts +45 -0
- package/dist/source-signature.js +20 -0
- package/dist/spa-nav.d.ts +49 -0
- package/dist/spa-nav.js +12 -0
- package/dist/spa-shell.d.ts +21 -0
- package/dist/spa-shell.js +34 -0
- package/dist/ssg.d.ts +357 -0
- package/dist/ssg.js +94 -0
- package/dist/ssr.d.ts +116 -0
- package/dist/ssr.js +339 -0
- package/dist/state-dir.d.ts +36 -0
- package/dist/state-dir.js +22 -0
- package/dist/state-management.d.ts +299 -0
- package/dist/state-management.js +519 -0
- package/dist/store-imports.d.ts +47 -0
- package/dist/store-imports.js +16 -0
- package/dist/store-loader.d.ts +15 -0
- package/dist/store-loader.js +111 -0
- package/dist/stores-client.d.ts +70 -0
- package/dist/stores-client.js +132 -0
- package/dist/story/addons.d.ts +123 -0
- package/dist/story/addons.js +261 -0
- package/dist/story/analytics.d.ts +163 -0
- package/dist/story/analytics.js +235 -0
- package/dist/story/auto-stories.d.ts +38 -0
- package/dist/story/auto-stories.js +158 -0
- package/dist/story/bookmarks.d.ts +102 -0
- package/dist/story/bookmarks.js +277 -0
- package/dist/story/bun-test.d.ts +45 -0
- package/dist/story/bun-test.js +102 -0
- package/dist/story/cli.d.ts +132 -0
- package/dist/story/cli.js +155 -0
- package/dist/story/collect/analyzer.d.ts +33 -0
- package/dist/story/collect/analyzer.js +305 -0
- package/dist/story/collect/index.d.ts +31 -0
- package/dist/story/collect/index.js +27 -0
- package/dist/story/collect/parser.d.ts +17 -0
- package/dist/story/collect/parser.js +129 -0
- package/dist/story/collect/scanner.d.ts +17 -0
- package/dist/story/collect/scanner.js +101 -0
- package/dist/story/collect/tree.d.ts +24 -0
- package/dist/story/collect/tree.js +110 -0
- package/dist/story/commands/build.d.ts +19 -0
- package/dist/story/commands/build.js +172 -0
- package/dist/story/commands/dev.d.ts +23 -0
- package/dist/story/commands/dev.js +78 -0
- package/dist/story/commands/index.d.ts +9 -0
- package/dist/story/commands/index.js +3 -0
- package/dist/story/commands/preview.d.ts +21 -0
- package/dist/story/commands/preview.js +56 -0
- package/dist/story/compiled-output.d.ts +27 -0
- package/dist/story/compiled-output.js +151 -0
- package/dist/story/composition.d.ts +47 -0
- package/dist/story/composition.js +261 -0
- package/dist/story/config-watcher.d.ts +33 -0
- package/dist/story/config-watcher.js +67 -0
- package/dist/story/config.d.ts +29 -0
- package/dist/story/config.js +64 -0
- package/dist/story/context.d.ts +27 -0
- package/dist/story/context.js +27 -0
- package/dist/story/controls/index.d.ts +54 -0
- package/dist/story/controls/index.js +320 -0
- package/dist/story/desktop-preview.d.ts +34 -0
- package/dist/story/desktop-preview.js +105 -0
- package/dist/story/docs-generator.d.ts +30 -0
- package/dist/story/docs-generator.js +187 -0
- package/dist/story/errors.d.ts +47 -0
- package/dist/story/errors.js +239 -0
- package/dist/story/figma-export.d.ts +169 -0
- package/dist/story/figma-export.js +224 -0
- package/dist/story/generator.d.ts +21 -0
- package/dist/story/generator.js +165 -0
- package/dist/story/hmr.d.ts +62 -0
- package/dist/story/hmr.js +131 -0
- package/dist/story/hot-swap.d.ts +46 -0
- package/dist/story/hot-swap.js +207 -0
- package/dist/story/index.d.ts +99 -0
- package/dist/story/index.js +49 -0
- package/dist/story/interactions.d.ts +53 -0
- package/dist/story/interactions.js +225 -0
- package/dist/story/keyboard-shortcuts.d.ts +39 -0
- package/dist/story/keyboard-shortcuts.js +345 -0
- package/dist/story/output.d.ts +115 -0
- package/dist/story/output.js +153 -0
- package/dist/story/performance.d.ts +129 -0
- package/dist/story/performance.js +252 -0
- package/dist/story/presets.d.ts +123 -0
- package/dist/story/presets.js +289 -0
- package/dist/story/props-validation.d.ts +45 -0
- package/dist/story/props-validation.js +215 -0
- package/dist/story/renderer.d.ts +69 -0
- package/dist/story/renderer.js +209 -0
- package/dist/story/search-index.d.ts +47 -0
- package/dist/story/search-index.js +106 -0
- package/dist/story/search.d.ts +56 -0
- package/dist/story/search.js +140 -0
- package/dist/story/server.d.ts +31 -0
- package/dist/story/server.js +965 -0
- package/dist/story/setup.d.ts +56 -0
- package/dist/story/setup.js +78 -0
- package/dist/story/snapshots.d.ts +65 -0
- package/dist/story/snapshots.js +171 -0
- package/dist/story/testing.d.ts +83 -0
- package/dist/story/testing.js +164 -0
- package/dist/story/theme.d.ts +68 -0
- package/dist/story/theme.js +170 -0
- package/dist/story/ts-css.d.ts +29 -0
- package/dist/story/ts-css.js +189 -0
- package/dist/story/types.d.ts +342 -0
- package/dist/story/types.js +0 -0
- package/dist/story/ui/code-panel.d.ts +43 -0
- package/dist/story/ui/code-panel.js +175 -0
- package/dist/story/ui/controls-panel.d.ts +25 -0
- package/dist/story/ui/controls-panel.js +168 -0
- package/dist/story/ui/index.d.ts +8 -0
- package/dist/story/ui/index.js +4 -0
- package/dist/story/ui/navigation.d.ts +55 -0
- package/dist/story/ui/navigation.js +250 -0
- package/dist/story/ui/preview.d.ts +46 -0
- package/dist/story/ui/preview.js +251 -0
- package/dist/story/visual-testing.d.ts +45 -0
- package/dist/story/visual-testing.js +248 -0
- package/dist/streaming.d.ts +238 -0
- package/dist/streaming.js +537 -0
- package/dist/strict-lint.d.ts +52 -0
- package/dist/strict-lint.js +269 -0
- package/dist/strip-literals.d.ts +40 -0
- package/dist/strip-literals.js +149 -0
- package/dist/stx-virtual-ts.d.ts +358 -0
- package/dist/stx-virtual-ts.js +670 -0
- package/dist/stx.d.ts +924 -0
- package/dist/style-scoping.d.ts +42 -0
- package/dist/style-scoping.js +82 -0
- package/dist/suspense.d.ts +98 -0
- package/dist/suspense.js +398 -0
- package/dist/teleport.d.ts +31 -0
- package/dist/teleport.js +109 -0
- package/dist/template-compiler.d.ts +33 -0
- package/dist/template-compiler.js +68 -0
- package/dist/template-hydrator.d.ts +28 -0
- package/dist/template-hydrator.js +95 -0
- package/dist/template-tag-balance.d.ts +32 -0
- package/dist/template-tag-balance.js +93 -0
- package/dist/testing.d.ts +330 -0
- package/dist/testing.js +588 -0
- package/dist/theme-tokens.d.ts +88 -0
- package/dist/theme-tokens.js +71 -0
- package/dist/transitions.d.ts +99 -0
- package/dist/transitions.js +443 -0
- package/dist/ts-css-config.d.ts +71 -0
- package/dist/ts-css-config.js +44 -0
- package/dist/type-checker.d.ts +85 -0
- package/dist/type-checker.js +581 -0
- package/dist/typecheck.d.ts +70 -0
- package/dist/typecheck.js +365 -0
- package/dist/types/component-types.d.ts +248 -0
- package/dist/types/component-types.js +0 -0
- package/dist/types/config-types.d.ts +938 -0
- package/dist/types/config-types.js +0 -0
- package/dist/types/context-types.d.ts +122 -0
- package/dist/types/context-types.js +0 -0
- package/dist/types/csp-types.d.ts +83 -0
- package/dist/types/csp-types.js +0 -0
- package/dist/types/directive-types.d.ts +315 -0
- package/dist/types/directive-types.js +3 -0
- package/dist/types/index.d.ts +20 -0
- package/dist/types/index.js +1 -0
- package/dist/types/pwa-types.d.ts +338 -0
- package/dist/types/pwa-types.js +0 -0
- package/dist/types.d.ts +27 -0
- package/dist/types.js +1 -0
- package/dist/typescript-templates.d.ts +156 -0
- package/dist/typescript-templates.js +386 -0
- package/dist/unresolved-identifiers.d.ts +52 -0
- package/dist/unresolved-identifiers.js +148 -0
- package/dist/utils.d.ts +112 -0
- package/dist/utils.js +1170 -0
- package/dist/validator.d.ts +80 -0
- package/dist/validator.js +171 -0
- package/dist/variable-extractor.d.ts +142 -0
- package/dist/variable-extractor.js +1306 -0
- package/dist/view-composers.d.ts +171 -0
- package/dist/view-composers.js +29 -0
- package/dist/virtual-scrolling.d.ts +127 -0
- package/dist/virtual-scrolling.js +454 -0
- package/dist/visual-editor.d.ts +185 -0
- package/dist/visual-editor.js +735 -0
- package/dist/visual-testing.d.ts +135 -0
- package/dist/visual-testing.js +126 -0
- package/dist/vue-template.d.ts +39 -0
- package/dist/vue-template.js +476 -0
- package/dist/web-components/css-scoping.d.ts +64 -0
- package/dist/web-components/css-scoping.js +210 -0
- package/dist/web-components/index.d.ts +29 -0
- package/dist/web-components/index.js +13 -0
- package/dist/web-components/reactive-generator.d.ts +72 -0
- package/dist/web-components/reactive-generator.js +484 -0
- package/dist/web-components.d.ts +253 -0
- package/dist/web-components.js +1466 -0
- package/dist/x-element.d.ts +42 -0
- package/dist/x-element.js +301 -0
- package/package.json +107 -0
- package/stx.d.ts +924 -0
|
@@ -0,0 +1,1814 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{ev as Ae,fv as u}from"./chunk-qc1f74ek.js";function _(e){return e.split(/[\\/]/).filter(Boolean).slice(0,-1).some((a)=>g.includes(a))}function m(e){return _(e.replace(/^\//,""))}var g;var b=u(()=>{g=["components","layouts","partials","emails"]});import h from"fs";import y from"path";function x(e,t){let a=e.filter((o)=>!m(o.pattern)),r=["// Auto-generated by STX - do not edit"];if(a.length===0)r.push("// No page routes found \u2014 route paths stay typed as string.","export {}","");else{r.push("declare module '@stacksjs/stx' {"," interface KnownRoutes {");for(let o of a){let i=[];for(let c of o.params){let f=o.pattern.includes(`:${c}?`)?"?":"";i.push(`${c}${f}: string`)}let l=i.join("; ");r.push(` '${o.pattern}': { ${l} }`)}r.push(" }","}","","export {}","")}let n=r.join(`
|
|
3
|
+
`),s=y.join(t,"route-types.d.ts");try{if(h.existsSync(s)&&h.readFileSync(s,"utf8")===n)return;h.mkdirSync(t,{recursive:!0}),h.writeFileSync(s,n)}catch{}}function S(e,t,a){let r=process.cwd(),n=y.relative(r,a),s=e.filter((i)=>!m(i.pattern)),o=["// Auto-generated by stx \u2014 do not edit",`// Source: ${n||"pages"}/`,"","export const routes = ["];for(let i of s){let l=y.relative(r,i.filePath).replace(/\\/g,"/"),c=JSON.stringify(i.params),f=i.layout?`, layout: '${y.relative(r,i.layout).replace(/\\/g,"/")}'`:"";o.push(` { pattern: '${i.pattern}', filePath: '${l}', isDynamic: ${i.isDynamic}, params: ${c}${f} },`)}o.push("] as const",""),o.push("export type RouteName = typeof routes[number]['pattern']",""),h.mkdirSync(t,{recursive:!0}),h.writeFileSync(y.join(t,"routes.ts"),o.join(`
|
|
4
|
+
`))}var F=u(()=>{b()});import V from"path";function k(e,t){let r=V.relative(t,e).replace(/\.(stx|md)$/,"");if(r=r.split(V.sep).join("/"),r==="index")return"/";if(r.endsWith("/index"))r=r.slice(0,-6);return r=r.replace(/\[\[([^\]]+)\]\]/g,":$1?"),r=r.replace(/\[([^\]]+)\]/g,":$1"),r=r.replace(/:\.\.\.([^/]+)/g,":$1*"),`/${r}`}function v(e){let t=[],a="",r=0;while(r<e.length){let n=e.slice(r),s=/^\/:([^/?*]+)\?/.exec(n);if(s){t.push(s[1]),a+="(?:/([^/]+))?",r+=s[0].length;continue}let o=/^:([^/?*]+)\*/.exec(n);if(o){t.push(o[1]),a+="(.+)",r+=o[0].length;continue}let i=/^:([^/?*]+)/.exec(n);if(i){t.push(i[1]),a+="([^/]+)",r+=i[0].length;continue}a+=e[r].replace(/[.+^${}()|[\]\\*?]/g,"\\$&"),r+=1}return{regex:new RegExp(`^${a}$`),params:t}}function D(e,t){let a=e===""?"/":e;for(let r of t){let n=a.match(r.regex);if(n){let s={};return r.params.forEach((o,i)=>{s[o]=n[i+1]}),{route:r,params:s}}}return null}function W(e){let t=e.replace(/\[\[([^\]]+)\]\]/g,":$1?").replace(/\[([^\]]+)\]/g,":$1").replace(/:\.\.\.([^/]+)/g,":$1*");return v(t)}var M=()=>{};import xe from"fs";import I from"path";function A(e,t){let a=[],r=I.dirname(e);while(r.startsWith(t)||r===t){let n=I.join(r,"_layout.stx");if(xe.existsSync(n))a.unshift(n);if(r===t)break;r=I.dirname(r)}return a}var N=()=>{};import P from"fs";import d from"path";function K(e,t){let a=[];if(!P.existsSync(e))return a;let r=P.readdirSync(e,{withFileTypes:!0});for(let n of r){let s=d.join(e,n.name);if(n.isDirectory()){if(n.name.startsWith("."))continue;if(g.includes(n.name))continue;a.push(...K(s,t))}else if(n.isFile()){let o=d.extname(n.name);if(t.includes(o)){if(n.name.startsWith("_")&&n.name!=="_layout.stx")continue;if(n.name==="_layout.stx")continue;a.push(s)}}}return a}class C{routes;pagesDir;pagesDirs;config;constructor(e,t={}){this.config=t;let a=t.pagesDirs&&t.pagesDirs.length>0?t.pagesDirs:[t.pagesDir||"pages"];this.pagesDirs=a.map((o)=>d.resolve(e,o)),this.pagesDir=this.pagesDirs[0];let r=t.extensions||[".stx"],n=new Set;this.routes=[];for(let o of this.pagesDirs){let i=K(o,r);for(let l of i){let c=k(l,o);if(n.has(c))continue;n.add(c);let{regex:f,params:H}=v(c),O=t.layouts!==!1?A(l,o):[];this.routes.push({pattern:c,regex:f,params:H,filePath:l,isDynamic:H.length>0,layout:O.length>0?O[O.length-1]:void 0})}}this.routes.sort((o,i)=>{if(!o.isDynamic&&i.isDynamic)return-1;if(o.isDynamic&&!i.isDynamic)return 1;let l=o.pattern.split("/").length,c=i.pattern.split("/").length;if(l!==c)return c-l;return o.pattern.localeCompare(i.pattern)});let s=d.resolve(e,t.stateDir||process.env.STX_DIR?.trim()||".stx");x(this.routes,s),S(this.routes,s,this.pagesDir)}match(e){let t=e===""?"/":e;for(let a of this.routes){let r=t.match(a.regex);if(r){let n={};return a.params.forEach((s,o)=>{n[s]=r[o+1]}),{route:a,params:n}}}return null}resolve(e,t){let a=this.routes.find((n)=>n.pattern===e||n.filePath.includes(e));if(!a)return"#not-found";let r=a.pattern;if(t)for(let[n,s]of Object.entries(t))r=r.replace(`:${n}`,encodeURIComponent(s));return r}addRoute(e){this.routes.push(e),this.routes.sort((t,a)=>{if(!t.isDynamic&&a.isDynamic)return-1;if(t.isDynamic&&!a.isDynamic)return 1;return t.pattern.localeCompare(a.pattern)})}getLayout(e){return e.layout??null}}function z(e,t){return new C(e,t)}function $(e,t){let a=[`${t}.stx`,"error.stx"];for(let r of a){let n=d.join(e,r);if(P.existsSync(n))return n}return null}function Y(e,t){return e.map((a)=>{let r=d.relative(t,a.filePath),n=a.isDynamic?" (dynamic)":"";return`${a.pattern} \u2192 ${r}${n}`})}var J=u(()=>{F();M();N();b()});function Z(e){E=e.replace(/\/$/,"")}function Q(e,t,a={}){p[e]={path:t,name:e,params:a}}function ee(e){for(let[t,a]of Object.entries(e))if(typeof a==="string")p[t]={path:a,name:t};else p[t]={...a,name:t}}function te(e,t={},a=!1){let r=p[e];if(!r)return console.warn(`Route [${e}] not defined.`),"#undefined-route";let n={...r.params,...t},s=r.path;s=s.replace(/:(\w+)/g,(i,l)=>{let c=n[l];if(c===void 0)return console.warn(`Missing parameter [${l}] for route [${e}].`),`:${l}`;return delete n[l],encodeURIComponent(String(c))});let o=Object.entries(n);if(o.length>0){let i=o.map(([l,c])=>`${encodeURIComponent(l)}=${encodeURIComponent(String(c))}`).join("&");s+=(s.includes("?")?"&":"?")+i}return a&&E?`${E}${s}`:s}function re(){for(let e of Object.keys(p))delete p[e];E=""}var p,E="";var ae=u(()=>{p={}});import ne from"fs";import oe from"path";function se(e,t={}){return{type:"redirect",path:e,options:{replace:t.replace??!1,external:t.external??!1,redirectCode:t.redirectCode??302}}}function ie(e){return{type:"abort",error:typeof e==="string"?{statusCode:500,message:e}:e}}function le(e,t={}){return{handler:e,mode:t.mode??"universal"}}function B(e){let t={};if(!e)return t;let a=e.split(";");for(let r of a){let[n,...s]=r.split("=");if(n){let o=n.trim(),i=s.join("=").trim();if(o)t[o]=decodeURIComponent(i)}}return t}function T(e,t,a={}){let r=`${encodeURIComponent(e)}=${encodeURIComponent(t)}`;if(a.expires){let n=typeof a.expires==="number"?new Date(Date.now()+a.expires*1000):a.expires;r+=`; Expires=${n.toUTCString()}`}if(a.path)r+=`; Path=${a.path}`;if(a.domain)r+=`; Domain=${a.domain}`;if(a.secure)r+="; Secure";if(a.httpOnly)r+="; HttpOnly";if(a.sameSite)r+=`; SameSite=${a.sameSite}`;return r}function U(e,t){let a=e.headers.get("cookie")||"",r=B(a);return{get(n){return r[n]},set(n,s,o={}){r[n]=s,t.append("Set-Cookie",T(n,s,o))},delete(n){delete r[n],t.append("Set-Cookie",T(n,"",{expires:new Date(0),path:"/"}))},getAll(){return{...r}}}}function G(){return{get(e){if(typeof document>"u")return;return B(document.cookie)[e]},set(e,t,a={}){if(typeof document>"u")return;document.cookie=T(e,t,a)},delete(e){if(typeof document>"u")return;document.cookie=T(e,"",{expires:new Date(0),path:"/"})},getAll(){if(typeof document>"u")return{};return B(document.cookie)}}}function X(){let e=typeof window<"u"&&typeof window.localStorage<"u";return{get(t){if(!e)return null;try{return window.localStorage.getItem(t)}catch{return null}},set(t,a){if(!e)return;try{window.localStorage.setItem(t,a)}catch{}},remove(t){if(!e)return;try{window.localStorage.removeItem(t)}catch{}},isAvailable(){return e}}}function R(e,t){w.set(e,t)}function j(e){return w.get(e)}function ce(e){return w.has(e)}function ue(){w.clear()}function he(){return Array.from(w.keys())}async function de(e,t="middleware"){let a=oe.resolve(e,t);if(!ne.existsSync(a))return;let r=ne.readdirSync(a);for(let n of r){if(!n.endsWith(".ts")&&!n.endsWith(".js"))continue;if(n.includes(".test.")||n.includes(".spec."))continue;let s=n.replace(/\.(ts|js)$/,""),o=oe.join(a,n);try{let l=(await import(o)).default;if(l&&typeof l.handler==="function")R(s,l);else if(typeof l==="function")R(s,{handler:l,mode:"universal"})}catch(i){console.warn(`Failed to load middleware '${s}' from ${o}:`,i)}}}function Se(e){let t={};if(!e)return t;let a=new URLSearchParams(e);for(let[r,n]of a)t[r]=n;return t}function pe(e,t,a,r=""){return{path:e,params:t,query:Se(r),fullPath:r?`${e}?${r}`:e,meta:a}}function fe(e,t,a,r){let n=r?.isServer??typeof window>"u",s=!n,o=new Headers;return{to:e,from:t,isClient:s,isServer:n,cookies:n&&a?U(a,o):G(),storage:X(),state:{},request:a,responseHeaders:o}}async function ge(e,t){let a=Array.isArray(e)?e:[e],r=t.isServer;for(let n of a){let s=j(n);if(!s)return console.error(`Route middleware '${n}' is not registered \u2014 failing the guard closed.`),{passed:!1,abort:{type:"abort",error:{statusCode:500,message:`Route middleware '${n}' is not registered`}},state:t.state,responseHeaders:t.responseHeaders};if(s.mode==="server"&&!r)continue;try{let o=await s.handler(t);if(o){if(o.type==="redirect")return{passed:!1,redirect:o,state:t.state,responseHeaders:t.responseHeaders};if(o.type==="abort")return{passed:!1,abort:o,state:t.state,responseHeaders:t.responseHeaders}}}catch(o){return console.error(`Error in middleware '${n}':`,o),{passed:!1,abort:{type:"abort",error:{statusCode:500,message:o instanceof Error?o.message:"Middleware error"}},state:t.state,responseHeaders:t.responseHeaders}}}return{passed:!0,state:t.state,responseHeaders:t.responseHeaders}}var w;var me=u(()=>{w=new Map});function q(e){let a=e.replace(/\\/g,"/").split("/").filter(Boolean),r=a.lastIndexOf("layouts"),n=r>=0?a[r+1]:a.at(-1);if(!n)return"app";return n.replace(/\.stx$/i,"")||"app"}function ye(e){let t=e.match(/<meta\b[^>]*name=["']stx-layout["'][^>]*content=["']([^"']+)["'][^>]*>/i),a=e.match(/<meta\b[^>]*name=["']stx-layout-group["'][^>]*content=["']([^"']+)["'][^>]*>/i),r=e.match(/<!--\s*stx-layout:\s*([^ ]+)\s*-->/i),n=t?.[1]||r?.[1]||"";return{layout:n,group:a?.[1]||q(n)}}var ve=()=>{};function ke(e){try{return new Bun.Transpiler({loader:"js",minifyWhitespace:!0}).transformSync(e).replace(/\}(var |let |const |function )/g,"};$1").trim()}catch{return e}}function we(){if(L)return L;return L=ke(`
|
|
5
|
+
;(function(){
|
|
6
|
+
'use strict';
|
|
7
|
+
var ROUTER_REV=7;
|
|
8
|
+
if(window.__stxRouter&&window.__stxRouter.__rev===ROUTER_REV)return;
|
|
9
|
+
|
|
10
|
+
// \u2500\u2500 Configuration \u2500\u2500
|
|
11
|
+
var defaults={container:'main',loadingClass:'stx-navigating',viewTransitions:true,cache:true,scrollToTop:true,prefetch:true,progress:true,progressColor:'#78dce8',progressHeight:'2px',interceptAllLinks:false,prefetchCacheMax:50,routeFocus:true,announceRoute:true,interceptForms:false};
|
|
12
|
+
var o=Object.assign({},defaults,window.__stxRouterConfig||{},window.STX_ROUTER_OPTIONS||{});
|
|
13
|
+
var containerSel=o.container;
|
|
14
|
+
var debug=!!o.debug;
|
|
15
|
+
function log(){if(debug&&typeof console!=='undefined'&&console.log)console.log.apply(console,arguments)}
|
|
16
|
+
|
|
17
|
+
// \u2500\u2500 Build skew (stacksjs/stx#1772) \u2500\u2500
|
|
18
|
+
// The build that rendered THIS document, and therefore the build the runtime
|
|
19
|
+
// executing right now came from. Under bun --watch a save restarts the server,
|
|
20
|
+
// so the next navigation can fetch a fragment produced by a newer build; when
|
|
21
|
+
// the scoped-script or binding format drifted between the two, the old runtime
|
|
22
|
+
// cannot hydrate the new fragment \u2014 literal moustaches, dead bindings, stale
|
|
23
|
+
// canvas. Sporadic, and never on a clean boot.
|
|
24
|
+
//
|
|
25
|
+
// Conservative on purpose: act only when BOTH ids are known. A missing id is
|
|
26
|
+
// "no information", never "mismatch", so statically hosted output (no headers)
|
|
27
|
+
// and older servers behave exactly as before.
|
|
28
|
+
var buildMeta=document.querySelector('meta[name="stx-build"]');
|
|
29
|
+
var loadedBuild=buildMeta?(buildMeta.getAttribute('content')||''):'';
|
|
30
|
+
window.__stxBuild=loadedBuild;
|
|
31
|
+
function isBuildSkew(incoming){
|
|
32
|
+
return !!(loadedBuild&&incoming&&incoming!==loadedBuild);
|
|
33
|
+
}
|
|
34
|
+
function reloadForSkew(url,incoming){
|
|
35
|
+
log('[router] build skew: page is',loadedBuild,'server is',incoming,'\u2014 full navigation');
|
|
36
|
+
location.href=url;
|
|
37
|
+
}
|
|
38
|
+
function runViewTransition(callback){
|
|
39
|
+
if(!o.viewTransitions||!document.startViewTransition)return false;
|
|
40
|
+
try{
|
|
41
|
+
var transition=document.startViewTransition(callback);
|
|
42
|
+
var onAbort=function(err){log('[router] view transition aborted:',err&&err.message?err.message:err)};
|
|
43
|
+
if(transition&&transition.ready&&transition.ready.catch)transition.ready.catch(onAbort);
|
|
44
|
+
if(transition&&transition.finished&&transition.finished.catch)transition.finished.catch(onAbort);
|
|
45
|
+
if(transition&&transition.updateCallbackDone&&transition.updateCallbackDone.catch)transition.updateCallbackDone.catch(onAbort);
|
|
46
|
+
return true;
|
|
47
|
+
}catch(err){
|
|
48
|
+
log('[router] view transition unavailable:',err&&err.message?err.message:err);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// \u2500\u2500 Progress bar (0\u2192100% at top of viewport) \u2500\u2500
|
|
54
|
+
// Native loading indicator baked into the router. Starts when navigation
|
|
55
|
+
// begins, trickles up to ~90% during fetch, snaps to 100% + fades on
|
|
56
|
+
// completion. Opt-out via window.__stxRouterConfig={progress:false}.
|
|
57
|
+
var progEl=null;
|
|
58
|
+
var progVal=0;
|
|
59
|
+
var progTimer=null;
|
|
60
|
+
function setProgress(v){
|
|
61
|
+
progVal=v<0?0:(v>100?100:v);
|
|
62
|
+
if(!progEl)return;
|
|
63
|
+
progEl.style.opacity=progVal>0?'1':'0';
|
|
64
|
+
progEl.style.transform='scaleX('+(progVal/100)+')';
|
|
65
|
+
}
|
|
66
|
+
function startProgress(){
|
|
67
|
+
if(!o.progress||!progEl)return;
|
|
68
|
+
if(progTimer){clearInterval(progTimer);progTimer=null}
|
|
69
|
+
setProgress(8);
|
|
70
|
+
progTimer=setInterval(function(){
|
|
71
|
+
if(progVal>=90)return;
|
|
72
|
+
// Asymptotic trickle \u2014 fast at first, slower near 90%
|
|
73
|
+
var inc=Math.max(0.4,(90-progVal)*0.08);
|
|
74
|
+
setProgress(progVal+inc);
|
|
75
|
+
},160);
|
|
76
|
+
}
|
|
77
|
+
function finishProgress(){
|
|
78
|
+
if(!o.progress||!progEl)return;
|
|
79
|
+
if(progTimer){clearInterval(progTimer);progTimer=null}
|
|
80
|
+
setProgress(100);
|
|
81
|
+
// Fade out, then reset. Delays chosen so the "full" state is briefly
|
|
82
|
+
// visible before the bar disappears.
|
|
83
|
+
setTimeout(function(){
|
|
84
|
+
if(!progEl)return;
|
|
85
|
+
progEl.style.opacity='0';
|
|
86
|
+
setTimeout(function(){progVal=0;if(progEl)progEl.style.transform='scaleX(0)'},260);
|
|
87
|
+
},180);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
var cache={};
|
|
91
|
+
var prefetching={};
|
|
92
|
+
var isNavigating=false;
|
|
93
|
+
|
|
94
|
+
// LRU bookkeeping for cache/layoutCache/layoutGroupCache. Pre-fix
|
|
95
|
+
// (stacksjs/stx#1719) these were plain objects with no eviction, so
|
|
96
|
+
// hover-prefetch on a sidebar of 200 internal links retained ~10MB
|
|
97
|
+
// of HTML strings for the page's lifetime. cacheOrder is a most-
|
|
98
|
+
// recently-used-last list of keys; setCache pushes new entries and
|
|
99
|
+
// evicts the oldest when length exceeds o.prefetchCacheMax (default
|
|
100
|
+
// 50). touchCache promotes a key on cache-hit. We use a plain array
|
|
101
|
+
// rather than a Map to keep the diff localized and the runtime
|
|
102
|
+
// hand-minified shape consistent.
|
|
103
|
+
var cacheOrder=[];
|
|
104
|
+
function setCache(key,html,layout,group,title,cattrs){
|
|
105
|
+
if(!(key in cache))cacheOrder.push(key);
|
|
106
|
+
cache[key]=html;
|
|
107
|
+
layoutCache[key]=layout;
|
|
108
|
+
layoutGroupCache[key]=group;
|
|
109
|
+
titleCache[key]=title||'';
|
|
110
|
+
attrsCache[key]=cattrs||'';
|
|
111
|
+
while(cacheOrder.length>o.prefetchCacheMax){
|
|
112
|
+
var oldest=cacheOrder.shift();
|
|
113
|
+
delete cache[oldest];
|
|
114
|
+
delete layoutCache[oldest];
|
|
115
|
+
delete layoutGroupCache[oldest];
|
|
116
|
+
delete titleCache[oldest];
|
|
117
|
+
delete attrsCache[oldest];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Read a prefetch response into the shape setCache stores, or null when this
|
|
121
|
+
// answer must not be cached at all.
|
|
122
|
+
//
|
|
123
|
+
// Both prefetch paths used to assemble this by hand, and both left out
|
|
124
|
+
// X-STX-Container-Attrs (#1947): setCache takes six arguments and they
|
|
125
|
+
// passed five, which JavaScript accepts without a word. The entry was
|
|
126
|
+
// cached with no container attributes, so a link that had been hovered
|
|
127
|
+
// before it was clicked applied '' on the cache hit, and setContainerAttrs
|
|
128
|
+
// stripped the class the previous navigation had put on the routed
|
|
129
|
+
// container. A dashboard lost its max-width, but only on links that
|
|
130
|
+
// happened to be hovered first, and a hard reload always fixed it \u2014 which
|
|
131
|
+
// made it read as a CSS problem.
|
|
132
|
+
//
|
|
133
|
+
// Hand-assembling it twice is what let them drift, so the two guards below
|
|
134
|
+
// live here rather than at the call sites \u2014 the hover path had one of them
|
|
135
|
+
// and the public router.prefetch() had neither:
|
|
136
|
+
//
|
|
137
|
+
// - a redirected body belongs to the URL it came FROM. A guarded route
|
|
138
|
+
// prefetched while logged out answers with /login's markup, and storing
|
|
139
|
+
// that under the guarded key poisons the cache: the later real
|
|
140
|
+
// navigation is a hit, so it swaps the login page in without ever
|
|
141
|
+
// reaching the network, where navigate's own redirect check would have
|
|
142
|
+
// caught it (#1849).
|
|
143
|
+
// - a fragment from a DIFFERENT build must not be handed to this page's
|
|
144
|
+
// runtime (#1772). navigate answers skew by reloading; a prefetch must
|
|
145
|
+
// not, because the user only hovered a link. Declining to cache is the
|
|
146
|
+
// whole fix \u2014 the click then goes to the network, where navigate does
|
|
147
|
+
// the reload properly.
|
|
148
|
+
function readPrefetchResponse(r,wantsFragment){
|
|
149
|
+
if(r.redirected)return Promise.resolve(null);
|
|
150
|
+
if(isBuildSkew(r.headers.get('X-STX-Build')||''))return Promise.resolve(null);
|
|
151
|
+
var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';
|
|
152
|
+
var layout=r.headers.get('X-STX-Layout')||'';
|
|
153
|
+
var group=r.headers.get('X-STX-Layout-Group')||'';
|
|
154
|
+
var title=r.headers.get('X-STX-Title')||'';
|
|
155
|
+
var cattrs=r.headers.get('X-STX-Container-Attrs')||'';
|
|
156
|
+
var runtime=r.headers.get('X-STX-Runtime')||'';
|
|
157
|
+
return r.text().then(function(html){return{html:isFrag?fragmentMarker(runtime)+html:html,layout:layout,layoutGroup:group,title:title,containerAttrs:cattrs}});
|
|
158
|
+
}
|
|
159
|
+
// Apply a page title captured from the fragment response's X-STX-Title
|
|
160
|
+
// header (URI-encoded) so SPA swaps keep document.title in sync with the
|
|
161
|
+
// page \u2014 full-document swaps set the title from the <title> tag directly.
|
|
162
|
+
function applyTitle(t){if(t){try{document.title=decodeURIComponent(t)}catch(e){document.title=t}}}
|
|
163
|
+
// \u2500\u2500 Container attributes \u2500\u2500
|
|
164
|
+
// A fragment carries only the container's INNER content, so any layout that
|
|
165
|
+
// lives on the destination's <main> itself (e.g. a centered auth page whose
|
|
166
|
+
// <main> carries flex + min-h-[100dvh] + items-center) would be
|
|
167
|
+
// dropped when injected into the persistent container. The server sends the
|
|
168
|
+
// destination container's attributes in X-STX-Container-Attrs (URI-encoded);
|
|
169
|
+
// we apply them here and track what we applied so navigating onward to a page
|
|
170
|
+
// with a bare <main> clears them again. Attributes the router never applied
|
|
171
|
+
// (the app's own id/data-stx-content hooks) are left untouched.
|
|
172
|
+
var pendingContainerAttrs='';
|
|
173
|
+
function parseContainerAttrs(attrStr){
|
|
174
|
+
var map={};
|
|
175
|
+
if(!attrStr)return map;
|
|
176
|
+
var probe=document.createElement('div');
|
|
177
|
+
probe.innerHTML='<i '+attrStr+'></i>';
|
|
178
|
+
var src=probe.firstChild;
|
|
179
|
+
// getAttributeNames first: it is the same standard DOM answer and it is
|
|
180
|
+
// what the test DOM implements. Reading .attributes alone made this
|
|
181
|
+
// silently return {} there \u2014 so the whole container-attrs path, header to
|
|
182
|
+
// element, had no test coverage at all while #1947 sat in it.
|
|
183
|
+
if(src&&src.getAttributeNames){
|
|
184
|
+
src.getAttributeNames().forEach(function(n){map[n]=src.getAttribute(n)});
|
|
185
|
+
}
|
|
186
|
+
else if(src&&src.attributes){
|
|
187
|
+
Array.prototype.forEach.call(src.attributes,function(a){map[a.name]=a.value});
|
|
188
|
+
}
|
|
189
|
+
return map;
|
|
190
|
+
}
|
|
191
|
+
function setContainerAttrs(el,map){
|
|
192
|
+
if(!el)return;
|
|
193
|
+
var prev=(el.getAttribute('data-stx-cattrs')||'').split(',');
|
|
194
|
+
prev.forEach(function(n){if(n&&!(n in map))el.removeAttribute(n)});
|
|
195
|
+
var names=[];
|
|
196
|
+
for(var k in map){
|
|
197
|
+
if(Object.prototype.hasOwnProperty.call(map,k)&&k!=='data-stx-cattrs'){
|
|
198
|
+
el.setAttribute(k,map[k]);names.push(k);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if(names.length)el.setAttribute('data-stx-cattrs',names.join(','));
|
|
202
|
+
else el.removeAttribute('data-stx-cattrs');
|
|
203
|
+
}
|
|
204
|
+
function applyContainerAttrs(el,encoded){
|
|
205
|
+
var attrStr='';
|
|
206
|
+
if(encoded){try{attrStr=decodeURIComponent(encoded)}catch(e){attrStr=encoded}}
|
|
207
|
+
setContainerAttrs(el,parseContainerAttrs(attrStr));
|
|
208
|
+
}
|
|
209
|
+
function touchCache(key){
|
|
210
|
+
var i=cacheOrder.indexOf(key);
|
|
211
|
+
if(i>=0){cacheOrder.splice(i,1);cacheOrder.push(key)}
|
|
212
|
+
}
|
|
213
|
+
function evictCache(key){
|
|
214
|
+
var i=cacheOrder.indexOf(key);
|
|
215
|
+
if(i>=0)cacheOrder.splice(i,1);
|
|
216
|
+
delete cache[key];
|
|
217
|
+
delete layoutCache[key];
|
|
218
|
+
delete layoutGroupCache[key];
|
|
219
|
+
delete titleCache[key];
|
|
220
|
+
delete attrsCache[key];
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Extract top-level CSS blocks (rules AND @media blocks with nested braces).
|
|
224
|
+
// The old regex ([^{}]+{[^{}]*}) silently dropped @media blocks because
|
|
225
|
+
// it couldn't handle nested braces \u2014 causing responsive classes (lg:, md:)
|
|
226
|
+
// to vanish after SPA navigation.
|
|
227
|
+
function extractCssBlocks(css){
|
|
228
|
+
var blocks=[];
|
|
229
|
+
var i=0;
|
|
230
|
+
var len=css.length;
|
|
231
|
+
while(i<len){
|
|
232
|
+
// Skip whitespace
|
|
233
|
+
while(i<len&&(css[i]===' '||css[i]==='\\n'||css[i]==='\\r'||css[i]==='\\t'))i++;
|
|
234
|
+
if(i>=len)break;
|
|
235
|
+
// Find the start of a block (first {)
|
|
236
|
+
var blockStart=i;
|
|
237
|
+
var bracePos=css.indexOf('{',i);
|
|
238
|
+
if(bracePos===-1)break;
|
|
239
|
+
// Walk forward tracking brace depth to find the matching close
|
|
240
|
+
var depth=0;
|
|
241
|
+
var j=bracePos;
|
|
242
|
+
while(j<len){
|
|
243
|
+
if(css[j]==='{')depth++;
|
|
244
|
+
else if(css[j]==='}'){depth--;if(depth===0){j++;break}}
|
|
245
|
+
j++;
|
|
246
|
+
}
|
|
247
|
+
if(depth!==0)break;
|
|
248
|
+
blocks.push(css.slice(blockStart,j).trim());
|
|
249
|
+
i=j;
|
|
250
|
+
}
|
|
251
|
+
return blocks;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function mergeCss(existing,incoming){
|
|
255
|
+
var blocks=extractCssBlocks(incoming);
|
|
256
|
+
var newBlocks=[];
|
|
257
|
+
for(var bi=0;bi<blocks.length;bi++){
|
|
258
|
+
if(existing.indexOf(blocks[bi])===-1)newBlocks.push(blocks[bi]);
|
|
259
|
+
}
|
|
260
|
+
if(newBlocks.length>0)return existing+'\\n'+newBlocks.join('\\n');
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function getContainer(){
|
|
265
|
+
return document.querySelector(containerSel)||document.querySelector('[data-stx-content]')||document.querySelector('main');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// \u2500\u2500 Navigation \u2500\u2500
|
|
269
|
+
// Layout group detection \u2014 layouts in the same group share a <main> container.
|
|
270
|
+
// Only truly different layout groups trigger a full page reload.
|
|
271
|
+
var layoutCache={};
|
|
272
|
+
var layoutGroupCache={};
|
|
273
|
+
var titleCache={};
|
|
274
|
+
var attrsCache={};
|
|
275
|
+
// Track executed script hashes to prevent redeclaration errors on navigation.
|
|
276
|
+
// Layout-level scripts (theme, nav setup) execute on initial page load and
|
|
277
|
+
// should NOT re-execute when navigating to another page with the same layout.
|
|
278
|
+
var executedScriptHashes={};
|
|
279
|
+
|
|
280
|
+
// \u2500\u2500 Re-execution contract (stacksjs/stx#1773) \u2500\u2500
|
|
281
|
+
// Whether a generated script may run again after an SPA swap used to be
|
|
282
|
+
// SNIFFED from its source: does it start with '(' or ';', or mention
|
|
283
|
+
// window.stx.mount. That is a property of the emitted shape, not of intent,
|
|
284
|
+
// so every change to how scripts are wrapped was one edit away from silently
|
|
285
|
+
// reclassifying them \u2014 and the symptom of getting it wrong is a component
|
|
286
|
+
// that renders completely dead (literal moustaches, stuck :show) only on a
|
|
287
|
+
// REVISIT, because nav-away already disposed its scope.
|
|
288
|
+
//
|
|
289
|
+
// Emitters now declare it: data-stx-run="always" on self-contained scope
|
|
290
|
+
// IIFEs and mount() wrappers, which own their scope and whose registration
|
|
291
|
+
// _cleanupContainer removed on the way out. Anything unstamped falls back to
|
|
292
|
+
// the old sniff, so a page rendered by an older server behaves exactly as
|
|
293
|
+
// before.
|
|
294
|
+
//
|
|
295
|
+
// The sniff itself now asks what the script REGISTERS rather than how it is
|
|
296
|
+
// spelled (#1828). cleanupContainer deletes window.stx._scopes entries,
|
|
297
|
+
// element disposers and destroy hooks on the way out, so the scripts that
|
|
298
|
+
// must run again are exactly the ones that put a scope back. That is
|
|
299
|
+
// detectable; a leading character is not.
|
|
300
|
+
//
|
|
301
|
+
// Deliberately NOT "strip leading comments, then test the first character",
|
|
302
|
+
// which is the obvious repair and is wrong in the expensive direction. Three
|
|
303
|
+
// emitters open with a comment and MUST NOT re-run: the animation scripts
|
|
304
|
+
// register matchMedia and DOMContentLoaded listeners plus an
|
|
305
|
+
// IntersectionObserver, and the STX lifecycle runtime owns a Map of live
|
|
306
|
+
// instances. Comment-stripping would flip all three to re-running on every
|
|
307
|
+
// navigation, leaking a listener and an observer each time and resetting the
|
|
308
|
+
// instance registry \u2014 trading a silent missing component for a silent leak.
|
|
309
|
+
var REGISTERS_SCOPE=/window\\.stx\\.mount|_scopes\\s*\\[|\\.initScope\\s*\\(|__stxComponentFactories\\s*\\[/;
|
|
310
|
+
function runsAlways(declared, code){
|
|
311
|
+
if(declared==='always')return true;
|
|
312
|
+
if(declared==='once')return false;
|
|
313
|
+
if(REGISTERS_SCOPE.test(code))return true;
|
|
314
|
+
var head=code.trimStart();
|
|
315
|
+
return head.charAt(0)==='('||head.charAt(0)===';';
|
|
316
|
+
}
|
|
317
|
+
function hashScript(code){
|
|
318
|
+
var h=0;for(var i=0;i<code.length;i++){h=((h<<5)-h)+code.charCodeAt(i);h|=0}
|
|
319
|
+
return h;
|
|
320
|
+
}
|
|
321
|
+
function hasStaticImport(code){
|
|
322
|
+
return /(?:^|\\n)[ \\t]*import(?:[ \\t]+|[{*'"])/.test(code);
|
|
323
|
+
}
|
|
324
|
+
function generatedSetupName(code){
|
|
325
|
+
var match=code.match(/function (__stx_setup_[A-Za-z0-9_]+)\\s*\\(/);
|
|
326
|
+
return match?match[1]:'';
|
|
327
|
+
}
|
|
328
|
+
// Does this fetched DOCUMENT carry the signals runtime?
|
|
329
|
+
//
|
|
330
|
+
// Unlike a fragment, a document holds the answer directly, so there is
|
|
331
|
+
// nothing to infer from markers or ask the server for: look for the script.
|
|
332
|
+
// Both emitted shapes carry data-stx-runtime \u2014 inline, and the serve-mode
|
|
333
|
+
// src="/_stx/runtime.js" \u2014 with the content sniff behind it for a document
|
|
334
|
+
// rendered by a server old enough not to stamp the attribute.
|
|
335
|
+
function documentShipsRuntime(doc,html){
|
|
336
|
+
if(doc&&doc.querySelector&&doc.querySelector('script[data-stx-runtime]'))return true;
|
|
337
|
+
return html.indexOf("'use strict';var cloakStyle")!==-1
|
|
338
|
+
||(html.indexOf('_cleanupContainer')!==-1&&html.indexOf('signals runtime loading')!==-1);
|
|
339
|
+
}
|
|
340
|
+
function isSignalsRuntimeScript(script,code){
|
|
341
|
+
return !!(script&&script.hasAttribute&&script.hasAttribute('data-stx-runtime'))
|
|
342
|
+
||(code.indexOf('_cleanupContainer')!==-1&&code.indexOf('signals runtime loading')!==-1)
|
|
343
|
+
||code.indexOf("'use strict';var cloakStyle")!==-1;
|
|
344
|
+
}
|
|
345
|
+
// A fragment never carries the signals runtime: the server strips the runtime
|
|
346
|
+
// IIFE out of it, and every re-execution path here filters it out again. So a
|
|
347
|
+
// page that never needed a runtime cannot hydrate a fragment that does \u2014 the
|
|
348
|
+
// setup function would be defined but never invoked, scope IIFEs throw while
|
|
349
|
+
// destructuring window.stx, and every x-cloak element stays display:none
|
|
350
|
+
// forever against the cloak style shipped on EVERY page. Hand those off to a
|
|
351
|
+
// real navigation, which loads the destination's own runtime (#1809).
|
|
352
|
+
//
|
|
353
|
+
// The SERVER decides this now, and says so in X-STX-Runtime, which travels
|
|
354
|
+
// into the fragment marker as rt=1 / rt=0 so it survives the prefetch cache.
|
|
355
|
+
// It is answering a question it knows the answer to exactly \u2014 did I put a
|
|
356
|
+
// runtime in this page \u2014 where the router could only guess from markup.
|
|
357
|
+
//
|
|
358
|
+
// Guessing missed a whole page shape (#1827). Every marker below originates in
|
|
359
|
+
// a client SCRIPT, so a page with reactive syntax and no script anywhere \u2014 a
|
|
360
|
+
// <script server>-only login form built from :if / x-model / :disabled \u2014 emits
|
|
361
|
+
// none of them, while the server ships it the runtime. Whatever root marker it
|
|
362
|
+
// does get lands on <body>, which a fragment excludes by construction. So
|
|
363
|
+
// <p :if="error" x-cloak>Invalid credentials</p>
|
|
364
|
+
// swapped into a runtime-less page keeps the x-cloak the server stamped on it,
|
|
365
|
+
// and only the runtime ever removes that: server-rendered text, in the DOM, at
|
|
366
|
+
// display:none forever.
|
|
367
|
+
//
|
|
368
|
+
// The same held for x-data on the routed container, whose scope attribute is
|
|
369
|
+
// hoisted out of the fragment body into X-STX-Container-Attrs. Asking the
|
|
370
|
+
// server covers both without either being enumerated.
|
|
371
|
+
//
|
|
372
|
+
// The sniff stays as the fallback for a server too old to declare it. Its
|
|
373
|
+
// discriminators matter. x-cloak and data-stx-scoped are NOT usable: each
|
|
374
|
+
// fragment inlines the head styles, cloak rule included, and the appearance
|
|
375
|
+
// bootstrap emits a self-contained data-stx-scoped script on pages with no
|
|
376
|
+
// reactivity at all. A bare window.stx is not usable either \u2014 the server
|
|
377
|
+
// prepends a _latestSetup=null clear script to every fragment. Any of those
|
|
378
|
+
// would make this always true and disable SPA navigation everywhere.
|
|
379
|
+
function fragmentNeedsRuntime(frag,declared){
|
|
380
|
+
if(declared==='1')return true;
|
|
381
|
+
if(declared==='0')return false;
|
|
382
|
+
if(frag.indexOf('__stx_setup_')!==-1
|
|
383
|
+
||frag.indexOf('data-stx-scope=')!==-1
|
|
384
|
+
||frag.indexOf('data-stx-reactive')!==-1
|
|
385
|
+
||/=\\s*window\\.stx\\s*;/.test(frag))return true;
|
|
386
|
+
var content=stripInertRegions(frag);
|
|
387
|
+
return BARE_DIRECTIVE.test(content)||content.indexOf('{{')!==-1;
|
|
388
|
+
}
|
|
389
|
+
// Documentation is both the largest population of runtime-less pages and the
|
|
390
|
+
// likeliest to contain directive syntax as literal CONTENT, so the fallback
|
|
391
|
+
// reads past the regions where that content lives. A page of stx examples must
|
|
392
|
+
// not full-reload on every hop through the docs \u2014 that is the regression
|
|
393
|
+
// #1809 existed to remove.
|
|
394
|
+
function stripInertRegions(frag){
|
|
395
|
+
return frag
|
|
396
|
+
.replace(new RegExp('<scr'+'ipt\\\\b[\\\\s\\\\S]*?<\\\\/scr'+'ipt>','gi'),'')
|
|
397
|
+
.replace(/<style\\b[\\s\\S]*?<\\/style>/gi,'')
|
|
398
|
+
.replace(/<pre\\b[\\s\\S]*?<\\/pre>/gi,'')
|
|
399
|
+
.replace(/<code\\b[\\s\\S]*?<\\/code>/gi,'')
|
|
400
|
+
.replace(/<!--[\\s\\S]*?-->/g,'');
|
|
401
|
+
}
|
|
402
|
+
// Anchored on the whitespace before an attribute name and the = that follows,
|
|
403
|
+
// so it reads attribute POSITIONS rather than characters. Bare ':' and '@' are
|
|
404
|
+
// everywhere in ordinary content \u2014 'xlink:href', 'background: red', '10:00',
|
|
405
|
+
// an email address \u2014 and none of those sit in that position.
|
|
406
|
+
//
|
|
407
|
+
// The x- arm is deliberately open rather than a list of known directives:
|
|
408
|
+
// x-attr is a GENERIC binding prefix, so x-href / x-src / x-value / x-alt bind
|
|
409
|
+
// exactly like x-text does. Measured over the 339 rendered docs pages, the
|
|
410
|
+
// open form costs nothing over an allowlist (3 matches either way, all three
|
|
411
|
+
// of them pages the CURRENT four-marker sniff already matches because they
|
|
412
|
+
// document its marker names) while covering directives an allowlist forgets.
|
|
413
|
+
var BARE_DIRECTIVE=/\\s(?::[a-z][\\w-]*|@[a-z][\\w.-]*|x-[a-z][\\w-]*)\\s*=\\s*["']/i;
|
|
414
|
+
// The fragment marker doubles as the carrier for the server's declaration.
|
|
415
|
+
// Encoding it in the HTML rather than threading a parameter keeps it inside
|
|
416
|
+
// the one value that already reaches swap() through every path, the prefetch
|
|
417
|
+
// cache included.
|
|
418
|
+
function fragmentMarker(declared){
|
|
419
|
+
return '<!--stx-fragment'+(declared==='true'?' rt=1':declared==='false'?' rt=0':'')+'-->';
|
|
420
|
+
}
|
|
421
|
+
// \u2500\u2500 External scripts inside the routed container \u2500\u2500
|
|
422
|
+
//
|
|
423
|
+
// A page whose x-data factory lives in its own file \u2014 a
|
|
424
|
+
// <script src="dashboard-xdata.js"> sitting inside [data-stx-content] \u2014 lost that
|
|
425
|
+
// file on every SPA navigation. Both swap paths dropped it: the fragment
|
|
426
|
+
// path's regex sees an empty body and returns '', and the full-document path
|
|
427
|
+
// removed it outright. The inline scripts beside it were re-executed with
|
|
428
|
+
// care; the external ones were deleted.
|
|
429
|
+
//
|
|
430
|
+
// The result was a screen that rendered on a cold load and came back empty
|
|
431
|
+
// after a click, reporting "dashboardXData is not defined" with every binding
|
|
432
|
+
// in the container left unevaluated \u2014 because nothing had defined it since
|
|
433
|
+
// the file was thrown away.
|
|
434
|
+
//
|
|
435
|
+
// Loaded once per URL, never re-run. What these files define is a global that
|
|
436
|
+
// outlives the navigation, so a second execution buys nothing and costs
|
|
437
|
+
// whatever side effects the file has: a mount helper firing again on every
|
|
438
|
+
// click through the same page is its own bug.
|
|
439
|
+
var loadedExternalScripts={};
|
|
440
|
+
// The base is resolved first because callers pass what they have: the
|
|
441
|
+
// fragment path knows only the path it navigated to ('/reports/disk'), and
|
|
442
|
+
// new URL(src, '/reports/disk') throws \u2014 a base must be absolute. Silently
|
|
443
|
+
// returning '' there meant the file was never requested, which is the bug
|
|
444
|
+
// this whole block exists to fix, reintroduced one level down.
|
|
445
|
+
function externalScriptKey(src,base){
|
|
446
|
+
try{
|
|
447
|
+
var absoluteBase=base?new URL(base,location.href).href:location.href;
|
|
448
|
+
return new URL(src,absoluteBase).href;
|
|
449
|
+
}catch(e){return ''}
|
|
450
|
+
}
|
|
451
|
+
function rememberExternalScript(src,base){
|
|
452
|
+
var key=externalScriptKey(src,base);
|
|
453
|
+
if(key)loadedExternalScripts[key]=1;
|
|
454
|
+
return key;
|
|
455
|
+
}
|
|
456
|
+
// Returns a promise, or null when there is nothing to wait for \u2014 an already
|
|
457
|
+
// loaded file, or an unparseable src. Resolves rather than rejects on error:
|
|
458
|
+
// one 404 must not stop the rest of the page hydrating, matching what the
|
|
459
|
+
// head-script path already does by sending failures to execScripts too.
|
|
460
|
+
function loadExternalScript(src,base){
|
|
461
|
+
var key=externalScriptKey(src,base);
|
|
462
|
+
if(!key||loadedExternalScripts[key])return null;
|
|
463
|
+
loadedExternalScripts[key]=1;
|
|
464
|
+
return new Promise(function(resolve){
|
|
465
|
+
var el=document.createElement('script');
|
|
466
|
+
el.src=key;
|
|
467
|
+
// Deliberately NOT data-stx-page: both swap paths clear those on every
|
|
468
|
+
// navigation, which would sweep this away and \u2014 since it is never loaded
|
|
469
|
+
// twice \u2014 leave the document without it. These are loaded once and stay,
|
|
470
|
+
// the same as the external head scripts beside them.
|
|
471
|
+
el.setAttribute('data-stx-external','');
|
|
472
|
+
el.onload=function(){resolve()};
|
|
473
|
+
el.onerror=function(){log('[router] container script failed:',key);resolve()};
|
|
474
|
+
document.head.appendChild(el);
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
// Record scripts from the initial page load
|
|
479
|
+
document.querySelectorAll('script').forEach(function(s){
|
|
480
|
+
var text=s.textContent||'';
|
|
481
|
+
if(s.hasAttribute('src'))rememberExternalScript(s.getAttribute('src'));
|
|
482
|
+
else if(text.trim())executedScriptHashes[hashScript(text)]=1;
|
|
483
|
+
});
|
|
484
|
+
function cacheKey(url){
|
|
485
|
+
// Base is the CURRENT document, not the origin root: a relative key like
|
|
486
|
+
// '?status=resolved' on /dashboard must cache as /dashboard?status=resolved,
|
|
487
|
+
// not /?status=resolved (#1777).
|
|
488
|
+
var u=new URL(url,location.href);
|
|
489
|
+
return u.pathname+u.search;
|
|
490
|
+
}
|
|
491
|
+
// Keep SPA fetches inside the active locale (/en/... when viewing English).
|
|
492
|
+
function withCurrentLocale(href){
|
|
493
|
+
var d=window.__stxI18n;
|
|
494
|
+
if(d&&typeof d.localizeHref==='function')return d.localizeHref(href);
|
|
495
|
+
return href;
|
|
496
|
+
}
|
|
497
|
+
function defaultLayoutGroup(layout){
|
|
498
|
+
if(!layout)return 'app';
|
|
499
|
+
var clean=String(layout).replace(/\\\\/g,'/');
|
|
500
|
+
var parts=clean.split('/').filter(Boolean);
|
|
501
|
+
var idx=parts.lastIndexOf('layouts');
|
|
502
|
+
var part=idx>=0?parts[idx+1]:parts[parts.length-1];
|
|
503
|
+
return part?part.replace(/\\.stx$/i,''):'app';
|
|
504
|
+
}
|
|
505
|
+
function getCurrentLayoutGroup(){
|
|
506
|
+
var meta=document.querySelector('meta[name="stx-layout-group"]');
|
|
507
|
+
if(meta&&meta.getAttribute('content'))return meta.getAttribute('content');
|
|
508
|
+
var layout=document.querySelector('meta[name="stx-layout"]');
|
|
509
|
+
return defaultLayoutGroup(layout?layout.getAttribute('content'):'');
|
|
510
|
+
}
|
|
511
|
+
function getDocLayoutGroup(doc,layout){
|
|
512
|
+
var meta=doc&&doc.querySelector?doc.querySelector('meta[name="stx-layout-group"]'):null;
|
|
513
|
+
return meta&&meta.getAttribute('content')?meta.getAttribute('content'):defaultLayoutGroup(layout||'');
|
|
514
|
+
}
|
|
515
|
+
function checkLayoutChange(newLayout,targetUrl,newGroup){
|
|
516
|
+
var currentLayout=document.querySelector('meta[name="stx-layout"]');
|
|
517
|
+
var curLayoutName=currentLayout?currentLayout.getAttribute('content'):'';
|
|
518
|
+
var curGroup=getCurrentLayoutGroup();
|
|
519
|
+
var nextGroup=newGroup||defaultLayoutGroup(newLayout);
|
|
520
|
+
log('[router] layout check: current='+curLayoutName+'('+curGroup+') new='+(newLayout||'')+'('+nextGroup+')');
|
|
521
|
+
// Different layout GROUP (e.g. app \u2192 auth): full reload
|
|
522
|
+
if(curGroup!==nextGroup){
|
|
523
|
+
log('[router] layout group change:',curGroup,'\u2192',nextGroup,'\u2014 full reload to:',targetUrl);
|
|
524
|
+
return true;
|
|
525
|
+
}
|
|
526
|
+
// Same group but different SPECIFIC layout (e.g. layouts/app \u2192 layouts/coach):
|
|
527
|
+
// full body swap so nav, sidebar, and other layout-level elements update
|
|
528
|
+
if(curLayoutName && newLayout && curLayoutName!==newLayout){
|
|
529
|
+
log('[router] layout name change:',curLayoutName,'\u2192',newLayout,'\u2014 full body swap');
|
|
530
|
+
return true;
|
|
531
|
+
}
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
function shouldUseFragmentResponse(){
|
|
536
|
+
return containerSel==='main'||containerSel==='[data-stx-content]';
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
// Write the address bar. The mode travels in the same slot as the legacy
|
|
540
|
+
// pushState boolean: false still means "do not touch history", true still
|
|
541
|
+
// pushes, and the string 'replace' replaces. Threading a fourth argument
|
|
542
|
+
// through swap() to each of the three history sites would have been the
|
|
543
|
+
// alternative (#1807).
|
|
544
|
+
function writeHistory(mode,href){
|
|
545
|
+
if(mode==='replace')history.replaceState({},'',href);
|
|
546
|
+
else history.pushState({},'',href);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
// Second arg accepts the legacy pushState boolean OR an options object
|
|
550
|
+
// { replace }. Callers inside this file still pass the boolean.
|
|
551
|
+
function navigate(url,pushState,force){
|
|
552
|
+
if(pushState&&typeof pushState==='object')pushState=pushState.replace?'replace':true;
|
|
553
|
+
// Lang-picker passes force=true with an already-localized path (/en/...).
|
|
554
|
+
// Re-localizing would map it back to the *current* locale and no-op.
|
|
555
|
+
if(!force) url=withCurrentLocale(url);
|
|
556
|
+
log('[router] navigate() called:',url,'isNavigating:',isNavigating);
|
|
557
|
+
if(isNavigating)return Promise.resolve(false);
|
|
558
|
+
// Resolve against the current document URL, matching native <a> semantics.
|
|
559
|
+
// With location.origin as the base, any relative href ('?status=resolved',
|
|
560
|
+
// '#anchor', './sibling') resolved to the SITE ROOT \u2014 so filter tabs,
|
|
561
|
+
// pagination and sort links silently bounced users to the landing page once
|
|
562
|
+
// the SPA interceptor was active (#1777).
|
|
563
|
+
var t=new URL(url,location.href);
|
|
564
|
+
|
|
565
|
+
if(t.origin!==location.origin){location.href=url;return Promise.resolve(false)}
|
|
566
|
+
|
|
567
|
+
if(t.pathname===location.pathname&&t.hash){
|
|
568
|
+
if(pushState!==false)writeHistory(pushState,t.href);
|
|
569
|
+
var el=document.querySelector(t.hash);
|
|
570
|
+
if(el)el.scrollIntoView({behavior:'smooth'});
|
|
571
|
+
return Promise.resolve(true);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Skip if user clicks a link to the page they're already on. We
|
|
575
|
+
// deliberately allow popstate (pushState===false) through because
|
|
576
|
+
// the browser has already updated location.href to the popped
|
|
577
|
+
// entry \u2014 without this allowance, hitting back would early-return
|
|
578
|
+
// and the visible page content would be left frozen on the
|
|
579
|
+
// forward-navigation page.
|
|
580
|
+
if(pushState!==false&&t.href===location.href&&!t.hash&&!force)return Promise.resolve(false);
|
|
581
|
+
|
|
582
|
+
isNavigating=true;
|
|
583
|
+
document.body.classList.add(o.loadingClass);
|
|
584
|
+
startProgress();
|
|
585
|
+
|
|
586
|
+
var targetHref=t.href;
|
|
587
|
+
var targetPath=cacheKey(url);
|
|
588
|
+
var targetHash=t.hash;
|
|
589
|
+
|
|
590
|
+
function done(){isNavigating=false;document.body.classList.remove(o.loadingClass);finishProgress()}
|
|
591
|
+
|
|
592
|
+
if(o.cache&&cache[targetPath]&&!force){
|
|
593
|
+
// Cache hit \u2192 promote in LRU order so this entry survives eviction
|
|
594
|
+
// longer than other entries that haven't been touched.
|
|
595
|
+
touchCache(targetPath);
|
|
596
|
+
if(checkLayoutChange(layoutCache[targetPath],url,layoutGroupCache[targetPath])){
|
|
597
|
+
// Layout changed \u2014 fetch full page and do full document swap
|
|
598
|
+
log('[router] cache hit but layout changed \u2014 fetching full page');
|
|
599
|
+
return fetch(url,{headers:{'Accept':'text/html'}}).then(function(r){
|
|
600
|
+
if(!r.ok)throw new Error(r.status);
|
|
601
|
+
return r.text();
|
|
602
|
+
}).then(function(html){
|
|
603
|
+
log('[router] full page fetched from cache path, len:',html.length);
|
|
604
|
+
pendingLayoutDecl={layout:layoutCache[targetPath]||'',group:layoutGroupCache[targetPath]||''};
|
|
605
|
+
return swap(html,targetPath,pushState,targetHash);
|
|
606
|
+
}).catch(function(err){
|
|
607
|
+
console.error('[router] full page fetch error:',err);
|
|
608
|
+
location.href=url;
|
|
609
|
+
}).finally(done);
|
|
610
|
+
}
|
|
611
|
+
pendingContainerAttrs=attrsCache[targetPath]||'';
|
|
612
|
+
pendingLayoutDecl=null;
|
|
613
|
+
return Promise.resolve(swap(cache[targetPath],targetPath,pushState,targetHash)).then(function(){
|
|
614
|
+
applyTitle(titleCache[targetPath]);
|
|
615
|
+
return true;
|
|
616
|
+
}).finally(done);
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
if(force&&cache[targetPath])evictCache(targetPath);
|
|
620
|
+
// First fetch as fragment (SPA mode) when the configured container is
|
|
621
|
+
// the page content. Custom app-shell containers need full documents so
|
|
622
|
+
// the router does not inject a <main> fragment into the wrong element.
|
|
623
|
+
var wantsFragment=shouldUseFragmentResponse();
|
|
624
|
+
return fetch(url,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){
|
|
625
|
+
if(!r.ok)throw new Error(r.status);
|
|
626
|
+
// A route guard answered with a redirect and fetch followed it
|
|
627
|
+
// transparently, so r.ok is the DESTINATION's 200 and this markup
|
|
628
|
+
// belongs to r.url \u2014 not to the path that was requested. Without
|
|
629
|
+
// retargeting, an auth guard's /login body is swapped in while the
|
|
630
|
+
// address bar, the history entry and the cache key all still read
|
|
631
|
+
// /dashboard: the user sees a login form at a URL that claims to be
|
|
632
|
+
// the page they asked for, and reloading bounces them again (#1849).
|
|
633
|
+
if(r.redirected&&r.url){
|
|
634
|
+
var rd=new URL(r.url,location.href);
|
|
635
|
+
// A guard that sends you off-origin (a hosted IdP) cannot be
|
|
636
|
+
// resolved by swapping a fragment \u2014 hand it to the browser.
|
|
637
|
+
if(rd.origin!==location.origin){location.href=r.url;return null}
|
|
638
|
+
log('[router] guard redirected:',url,'->',rd.pathname);
|
|
639
|
+
url=rd.pathname+rd.search+rd.hash;
|
|
640
|
+
targetPath=cacheKey(url);
|
|
641
|
+
targetHash=rd.hash;
|
|
642
|
+
}
|
|
643
|
+
// Before anything is parsed or swapped: a fragment from a different
|
|
644
|
+
// build must not be handed to this page's runtime (#1772).
|
|
645
|
+
var incomingBuild=r.headers.get('X-STX-Build')||'';
|
|
646
|
+
if(isBuildSkew(incomingBuild)){reloadForSkew(url,incomingBuild);return null}
|
|
647
|
+
var isFragment=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';
|
|
648
|
+
var newLayout=r.headers.get('X-STX-Layout')||'';
|
|
649
|
+
var newGroup=r.headers.get('X-STX-Layout-Group')||'';
|
|
650
|
+
var newTitle=r.headers.get('X-STX-Title')||'';
|
|
651
|
+
var newCAttrs=r.headers.get('X-STX-Container-Attrs')||'';
|
|
652
|
+
var newRuntime=r.headers.get('X-STX-Runtime')||'';
|
|
653
|
+
// Layout change? Fetch the FULL page (no X-STX-Router header) and do full document swap
|
|
654
|
+
if(isFragment&&checkLayoutChange(newLayout,url,newGroup)){
|
|
655
|
+
log('[router] layout change \u2014 fetching full page for document swap');
|
|
656
|
+
return fetch(url,{headers:{'Accept':'text/html'}}).then(function(fullRes){
|
|
657
|
+
log('[router] full page fetched:',fullRes.status,'ok:',fullRes.ok);
|
|
658
|
+
if(!fullRes.ok)throw new Error(fullRes.status);
|
|
659
|
+
return fullRes.text().then(function(html){
|
|
660
|
+
log('[router] full page html length:',html.length);
|
|
661
|
+
return{html:html,isFragment:false,layout:newLayout,layoutGroup:newGroup,title:newTitle};
|
|
662
|
+
});
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
return r.text().then(function(html){return{html:html,isFragment:isFragment,layout:newLayout,layoutGroup:newGroup,title:newTitle,containerAttrs:newCAttrs,runtime:newRuntime}});
|
|
666
|
+
}).then(function(result){
|
|
667
|
+
if(!result)return;
|
|
668
|
+
if(result.isFragment)result.html=fragmentMarker(result.runtime)+result.html;
|
|
669
|
+
if(o.cache)setCache(targetPath,result.html,result.layout,result.layoutGroup,result.title,result.containerAttrs);
|
|
670
|
+
pendingContainerAttrs=result.isFragment?(result.containerAttrs||''):'';
|
|
671
|
+
pendingLayoutDecl=result.isFragment?null:{layout:result.layout||'',group:result.layoutGroup||''};
|
|
672
|
+
return Promise.resolve(swap(result.html,targetPath,pushState,targetHash)).then(function(){
|
|
673
|
+
// Fragment swaps carry no <head>; apply the title from the header.
|
|
674
|
+
// Full-document swaps already set document.title from the <title> tag.
|
|
675
|
+
if(result.isFragment)applyTitle(result.title);
|
|
676
|
+
return true;
|
|
677
|
+
});
|
|
678
|
+
}).catch(function(err){
|
|
679
|
+
console.error('[router] fetch error:',err);
|
|
680
|
+
location.href=url;
|
|
681
|
+
}).finally(done);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// A full document is only safe to partial-swap into the stx shell if it is
|
|
686
|
+
// itself stx-rendered. Pages served by another engine mounted on the same
|
|
687
|
+
// origin (e.g. a docs/blog generator) carry none of these markers; swapping
|
|
688
|
+
// their <main> + styles into the stx shell corrupts the layout. Detect them
|
|
689
|
+
// and hand off to a native full navigation instead.
|
|
690
|
+
function isStxDocument(html){
|
|
691
|
+
return html.indexOf('name="stx-layout"')!==-1
|
|
692
|
+
||html.indexOf('__stxRouterConfig')!==-1
|
|
693
|
+
||html.indexOf('data-stx-content')!==-1;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// The layout and group the SERVER declared for the document about to be
|
|
697
|
+
// swapped in. Set immediately before each swap() call by whichever path
|
|
698
|
+
// fetched it, and consumed once.
|
|
699
|
+
//
|
|
700
|
+
// Threaded this way rather than as a swap() parameter, matching
|
|
701
|
+
// pendingContainerAttrs \u2014 see the note above writeHistory about the cost of
|
|
702
|
+
// adding an argument to swap() and its three call sites (#1807).
|
|
703
|
+
var pendingLayoutDecl=null;
|
|
704
|
+
// \u2500\u2500 Focus and route announcement \u2500\u2500
|
|
705
|
+
// A fragment swap replaces content without a document load, so the browser
|
|
706
|
+
// does none of what it normally does for a navigation. Focus stays on a link
|
|
707
|
+
// that no longer exists, which the browser resets to <body> \u2014 a keyboard user
|
|
708
|
+
// is silently returned to the top of the tab order \u2014 and a screen reader says
|
|
709
|
+
// nothing at all, so there is no signal the page changed. Neither is fixable
|
|
710
|
+
// from app code, because only the router knows a navigation happened (#1862).
|
|
711
|
+
function routeAnnouncer(){
|
|
712
|
+
var el=document.getElementById('stx-route-announcer');
|
|
713
|
+
if(el)return el;
|
|
714
|
+
el=document.createElement('div');
|
|
715
|
+
el.id='stx-route-announcer';
|
|
716
|
+
el.setAttribute('aria-live','polite');
|
|
717
|
+
el.setAttribute('role','status');
|
|
718
|
+
el.setAttribute('aria-atomic','true');
|
|
719
|
+
// Visually hidden but still ANNOUNCED. display:none and visibility:hidden
|
|
720
|
+
// are both skipped by screen readers, which would make this a silent no-op
|
|
721
|
+
// that looks correct in the DOM.
|
|
722
|
+
el.style.cssText='position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0';
|
|
723
|
+
document.body.appendChild(el);
|
|
724
|
+
return el;
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
function announceRoute(){
|
|
728
|
+
if(o.announceRoute===false)return;
|
|
729
|
+
var c=getContainer();
|
|
730
|
+
var h=c&&c.querySelector?c.querySelector('h1'):null;
|
|
731
|
+
// The heading names the destination better than the title, which often
|
|
732
|
+
// carries a site-name suffix. Falls back to the title, then gives up
|
|
733
|
+
// rather than announcing an empty string.
|
|
734
|
+
var label=(h&&(h.textContent||'').trim())||document.title||'';
|
|
735
|
+
if(!label)return;
|
|
736
|
+
var el=routeAnnouncer();
|
|
737
|
+
// Cleared first: several screen readers ignore a live-region update whose
|
|
738
|
+
// text is identical to what is already there, so navigating between two
|
|
739
|
+
// pages with the same heading would announce only the first.
|
|
740
|
+
el.textContent='';
|
|
741
|
+
el.textContent=label;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function focusAfterNavigation(hash){
|
|
745
|
+
if(o.routeFocus===false)return;
|
|
746
|
+
var target=null;
|
|
747
|
+
if(hash){try{target=document.querySelector(hash)}catch(e){target=null}}
|
|
748
|
+
if(!target)target=getContainer();
|
|
749
|
+
if(!target||!target.focus)return;
|
|
750
|
+
// The container is not naturally focusable. tabindex="-1" makes it
|
|
751
|
+
// programmatically focusable without inserting it into the tab order.
|
|
752
|
+
if(!target.hasAttribute('tabindex'))target.setAttribute('tabindex','-1');
|
|
753
|
+
// preventScroll because scroll position is already decided just above \u2014
|
|
754
|
+
// focusing would otherwise fight scrollToTop and the hash scrollIntoView.
|
|
755
|
+
try{target.focus({preventScroll:true})}catch(e){try{target.focus()}catch(e2){}}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
function swap(html,url,pushState,hash){
|
|
759
|
+
var fragMark=/^<!--stx-fragment(?: rt=([01]))?-->/.exec(html);
|
|
760
|
+
var isFragment=!!fragMark;
|
|
761
|
+
var declaredRuntime=fragMark&&fragMark[1]?fragMark[1]:'';
|
|
762
|
+
if(isFragment)html=html.slice(fragMark[0].length);
|
|
763
|
+
if(!isFragment&&!isStxDocument(html)){log('[router] non-stx document \u2014 full navigation to:',url);location.href=url;return Promise.resolve(false)}
|
|
764
|
+
var currentContent=getContainer();
|
|
765
|
+
log('[router] swap: isFragment='+isFragment+' container='+!!currentContent+' tag='+(currentContent&¤tContent.tagName)+' selector='+containerSel+' htmlLen='+html.length);
|
|
766
|
+
if(!currentContent){log('[router] no container \u2014 falling back');location.href=url;return Promise.resolve(false)}
|
|
767
|
+
|
|
768
|
+
// Fragment mode: server returned just the page content (no document wrapper)
|
|
769
|
+
if(isFragment){
|
|
770
|
+
// Before _cleanupContainer, not after: handing off later would dispose
|
|
771
|
+
// the OUTGOING page's scopes and then reload anyway. Inside doFragSwap
|
|
772
|
+
// would be later still \u2014 innerHTML is already replaced by then.
|
|
773
|
+
if(!window.stx&&fragmentNeedsRuntime(html,declaredRuntime)){
|
|
774
|
+
log('[router] fragment needs the signals runtime and this page has none \u2014 full navigation to:',url);
|
|
775
|
+
location.href=url;
|
|
776
|
+
return Promise.resolve(false);
|
|
777
|
+
}
|
|
778
|
+
if(window.stx&&window.stx._cleanupContainer)window.stx._cleanupContainer(currentContent);
|
|
779
|
+
function doFragSwap(){
|
|
780
|
+
// Extract scripts from fragment before injecting HTML
|
|
781
|
+
var fragScripts=[];
|
|
782
|
+
var fragScriptId=0;
|
|
783
|
+
var fragStyles=[];
|
|
784
|
+
var fragCssHrefs=[];
|
|
785
|
+
var fragCss=null;
|
|
786
|
+
var fragExternalScripts=[];
|
|
787
|
+
var cleanFrag=html.replace(new RegExp('<scr'+'ipt\\\\b([^>]*)>([\\\\s\\\\S]*?)<\\\\/scr'+'ipt>','gi'),function(m,attrs,code){
|
|
788
|
+
// A data block is not code, and everything below this line assumes it
|
|
789
|
+
// is: the body gets stashed in fragScripts, re-emitted as a pending
|
|
790
|
+
// placeholder, then wrapped in braces and executed. Wrapping a JSON
|
|
791
|
+
// object in braces reparses it as a labelled block, so its first ':'
|
|
792
|
+
// is a SyntaxError on every single fragment swap
|
|
793
|
+
// (stacksjs/stx#1801). @structuredData renders at its call site,
|
|
794
|
+
// which inside a layout is @section('content') \u2014 i.e. in the routed
|
|
795
|
+
// container \u2014 so the natural way to write it hit this.
|
|
796
|
+
//
|
|
797
|
+
// Left exactly as found rather than stripped: crawlers read
|
|
798
|
+
// structured data wherever it sits, and the browser will not execute
|
|
799
|
+
// a non-JS type anyway. Mirrors prepareRoutedBodyScripts' notion of
|
|
800
|
+
// executable, which this path never had.
|
|
801
|
+
var typeMatch=attrs.match(/\\btype\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))/i);
|
|
802
|
+
var scriptType=((typeMatch&&(typeMatch[1]||typeMatch[2]||typeMatch[3]))||'').trim().toLowerCase();
|
|
803
|
+
if(scriptType&&scriptType!=='text/javascript'&&scriptType!=='application/javascript'&&scriptType!=='module')return m;
|
|
804
|
+
// An external script has no body, so every check below reads it as
|
|
805
|
+
// nothing to run and the fragment loses it. Queue it instead; the
|
|
806
|
+
// file is where the page's factory usually lives.
|
|
807
|
+
var srcMatch=attrs.match(/\\bsrc\\s*=\\s*(?:"([^"]*)"|'([^']*)'|([^\\s>]+))/i);
|
|
808
|
+
var fragSrc=srcMatch&&(srcMatch[1]||srcMatch[2]||srcMatch[3]);
|
|
809
|
+
if(fragSrc){fragExternalScripts.push(fragSrc);return ''}
|
|
810
|
+
if(code&&code.trim()&&!isSignalsRuntimeScript({hasAttribute:function(name){return name==='data-stx-runtime'&&attrs.indexOf('data-stx-runtime')!==-1}},code)){
|
|
811
|
+
var slot='fragment-'+(++fragScriptId);
|
|
812
|
+
var scoped=/(?:^|\\s)data-stx-scoped(?:\\s|=|$)/i.test(attrs);
|
|
813
|
+
var runDecl=(attrs.match(/data-stx-run\\s*=\\s*["']?(always|once)["']?/i)||[])[1];
|
|
814
|
+
fragScripts.push({text:code,slot:slot,setupName:generatedSetupName(code),scoped:scoped,run:runDecl?runDecl.toLowerCase():''});
|
|
815
|
+
// Retain scoped setup code in its inert placeholder. A placeholder
|
|
816
|
+
// inside template.content is unreachable through document, so the
|
|
817
|
+
// repeated component runtime must execute it for each clone.
|
|
818
|
+
return '<scr'+'ipt type="application/stx-pending" data-stx-route-script="'+slot+'"'
|
|
819
|
+
+(scoped?' data-stx-scoped':'')
|
|
820
|
+
+(runDecl?' data-stx-run="'+runDecl.toLowerCase()+'"':'')
|
|
821
|
+
+'>'+code+'<\\/scr'+'ipt>';
|
|
822
|
+
}
|
|
823
|
+
return '';
|
|
824
|
+
});
|
|
825
|
+
cleanFrag=cleanFrag.replace(new RegExp('<sty'+'le\\\\b([^>]*)>([\\\\s\\\\S]*?)<\\\\/sty'+'le>','gi'),function(m,attrs,css){
|
|
826
|
+
if(attrs.indexOf('data-css')!==-1){
|
|
827
|
+
fragCss=css;
|
|
828
|
+
}else{
|
|
829
|
+
fragStyles.push({attrs:attrs,css:css});
|
|
830
|
+
}
|
|
831
|
+
return '';
|
|
832
|
+
});
|
|
833
|
+
cleanFrag=cleanFrag.replace(new RegExp('<link\\\\b([^>]*)>','gi'),function(m,attrs){
|
|
834
|
+
if(attrs.indexOf('data-css')===-1)return m;
|
|
835
|
+
var hrefMatch=attrs.match(/\\bhref=(["'])(.*?)\\1/i);
|
|
836
|
+
if(hrefMatch&&hrefMatch[2])fragCssHrefs.push(hrefMatch[2]);
|
|
837
|
+
return '';
|
|
838
|
+
});
|
|
839
|
+
// Remove old page styles (not css \u2014 that gets merged)
|
|
840
|
+
document.querySelectorAll('style[data-stx-page]').forEach(function(s){s.remove()});
|
|
841
|
+
// Merge css CSS from fragment into existing css style
|
|
842
|
+
if(fragCss){
|
|
843
|
+
var curCss=document.querySelector('head style[data-css]');
|
|
844
|
+
if(curCss){
|
|
845
|
+
var merged=mergeCss(curCss.textContent||'',fragCss);
|
|
846
|
+
if(merged)curCss.textContent=merged;
|
|
847
|
+
}else{
|
|
848
|
+
var cw=document.createElement('style');
|
|
849
|
+
cw.setAttribute('data-css','generated');
|
|
850
|
+
cw.textContent=fragCss;
|
|
851
|
+
document.head.appendChild(cw);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
fragCssHrefs.forEach(function(href){
|
|
855
|
+
var absolute=new URL(href,location.href).href;
|
|
856
|
+
var exists=false;
|
|
857
|
+
document.querySelectorAll('head link[data-css][href]').forEach(function(link){
|
|
858
|
+
if(new URL(link.getAttribute('href'),location.href).href===absolute)exists=true;
|
|
859
|
+
});
|
|
860
|
+
if(exists)return;
|
|
861
|
+
var link=document.createElement('link');
|
|
862
|
+
link.setAttribute('data-css','generated');
|
|
863
|
+
link.setAttribute('rel','stylesheet');
|
|
864
|
+
link.setAttribute('href',href);
|
|
865
|
+
document.head.appendChild(link);
|
|
866
|
+
});
|
|
867
|
+
// Add new page styles
|
|
868
|
+
fragStyles.forEach(function(s){
|
|
869
|
+
var el=document.createElement('style');
|
|
870
|
+
el.textContent=s.css;
|
|
871
|
+
el.setAttribute('data-stx-page','');
|
|
872
|
+
document.head.appendChild(el);
|
|
873
|
+
});
|
|
874
|
+
// Swap content \u2014 apply the destination container's own attributes first
|
|
875
|
+
// so the incoming markup lands in a correctly-laid-out container instead
|
|
876
|
+
// of flashing (or sticking) unstyled.
|
|
877
|
+
applyContainerAttrs(currentContent,pendingContainerAttrs);
|
|
878
|
+
currentContent.innerHTML=cleanFrag;
|
|
879
|
+
// Remove old page scripts
|
|
880
|
+
document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
|
|
881
|
+
if(pushState!==false)writeHistory(pushState,url+(hash||''));
|
|
882
|
+
updateNav(url);
|
|
883
|
+
updateActiveLinks();
|
|
884
|
+
if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});
|
|
885
|
+
else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
|
|
886
|
+
window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));
|
|
887
|
+
// Before page scripts run, so a page that focuses its own control on
|
|
888
|
+
// mount still wins \u2014 its script executes after this.
|
|
889
|
+
focusAfterNavigation(hash);
|
|
890
|
+
// Deferred a tick because the title is applied by the caller after this
|
|
891
|
+
// swap resolves, and the announcement should read the new one.
|
|
892
|
+
setTimeout(announceRoute,0);
|
|
893
|
+
// Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
|
|
894
|
+
log('[router] frag scripts:', fragScripts.length);
|
|
895
|
+
document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
|
|
896
|
+
fragScripts.sort(function(a,b){return (a.setupName?1:0)-(b.setupName?1:0)});
|
|
897
|
+
function runFragScripts(){
|
|
898
|
+
fragScripts.forEach(function(entry){
|
|
899
|
+
var code=entry.text;
|
|
900
|
+
log('[router] exec script len:', code.length, 'has __stx_setup:', code.indexOf('__stx_setup')>-1);
|
|
901
|
+
var placeholder=document.querySelector('script[data-stx-route-script="'+entry.slot+'"]');
|
|
902
|
+
var nestedScopedTemplate=false;
|
|
903
|
+
var scopedLoopSetup=false;
|
|
904
|
+
if(entry.scoped&&placeholder){
|
|
905
|
+
document.querySelectorAll('template').forEach(function(template){
|
|
906
|
+
if(template.contains(placeholder)||(template.content&&template.content.contains(placeholder)))
|
|
907
|
+
nestedScopedTemplate=true;
|
|
908
|
+
});
|
|
909
|
+
var loopRoot=placeholder.previousSibling;
|
|
910
|
+
while(loopRoot&&(
|
|
911
|
+
(loopRoot.nodeType===Node.TEXT_NODE&&!(loopRoot.textContent||'').trim())
|
|
912
|
+
||loopRoot.nodeType===Node.COMMENT_NODE
|
|
913
|
+
))loopRoot=loopRoot.previousSibling;
|
|
914
|
+
if(loopRoot&&loopRoot.nodeType===Node.ELEMENT_NODE&&loopRoot.hasAttribute('data-stx-scope')
|
|
915
|
+
&&(loopRoot.hasAttribute(':for')||loopRoot.hasAttribute('@for')||loopRoot.hasAttribute('x-for'))){
|
|
916
|
+
var loopScopeId=loopRoot.getAttribute('data-stx-scope');
|
|
917
|
+
scopedLoopSetup=!!loopScopeId&&code.indexOf(loopScopeId)!==-1;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if(entry.scoped&&(!placeholder||!placeholder.parentNode||nestedScopedTemplate||scopedLoopSetup)){
|
|
921
|
+
log('[router] leaving loop-scoped script for component hydration');
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
// Skip scripts that were already executed (layout-level partials
|
|
925
|
+
// like theme.stx, stores.stx, nav.stx). Their top-level const/let
|
|
926
|
+
// declarations would throw "Identifier has already been declared"
|
|
927
|
+
// on re-execution. Setup functions (__stx_setup_) must always
|
|
928
|
+
// re-execute because each page has its own setup.
|
|
929
|
+
var h=hashScript(code);
|
|
930
|
+
var isSetup=code.indexOf('__stx_setup_')!==-1;
|
|
931
|
+
// Self-contained scope scripts must re-execute on every navigation:
|
|
932
|
+
// they own their scope, won't collide with prior runs, and
|
|
933
|
+
// _cleanupContainer deleted their entry from window.stx._scopes on
|
|
934
|
+
// the way out. Skipping one leaves the page leaf with no scope
|
|
935
|
+
// registered \u2014 bindIf/@event never re-bind and every :if branch
|
|
936
|
+
// stays visible at once. Declared by the emitter via data-stx-run,
|
|
937
|
+
// sniffed only as a fallback (#1773).
|
|
938
|
+
var isAlreadyScoped=runsAlways(entry.run,code);
|
|
939
|
+
if(!isSetup&&!isAlreadyScoped&&executedScriptHashes[h]){
|
|
940
|
+
log('[router] skipping already-executed script (hash dedup)');
|
|
941
|
+
return;
|
|
942
|
+
}
|
|
943
|
+
executedScriptHashes[h]=1;
|
|
944
|
+
var ns=document.createElement('script');
|
|
945
|
+
var hasImport=hasStaticImport(code);
|
|
946
|
+
if(hasImport){
|
|
947
|
+
ns.type='module';
|
|
948
|
+
}
|
|
949
|
+
ns.textContent=(hasImport||isAlreadyScoped)?code:'{'+code+'}';
|
|
950
|
+
ns.setAttribute('data-stx-page','');
|
|
951
|
+
if(placeholder&&placeholder.parentNode){
|
|
952
|
+
ns.setAttribute('data-stx-positioned','');
|
|
953
|
+
placeholder.parentNode.replaceChild(ns,placeholder);
|
|
954
|
+
}else{
|
|
955
|
+
document.body.appendChild(ns);
|
|
956
|
+
}
|
|
957
|
+
});
|
|
958
|
+
// Guarded: the router ships on every page, the signals runtime only on
|
|
959
|
+
// pages that have a client script. log() is debug-gated but its
|
|
960
|
+
// ARGUMENTS are evaluated at the call site regardless, so an unguarded
|
|
961
|
+
// read here threw on every runtime-less page \u2014 aborting the swap and
|
|
962
|
+
// falling back to a full document load (#1809).
|
|
963
|
+
log('[router] scripts done. _latestSetup:', !!(window.stx&&window.stx._latestSetup));
|
|
964
|
+
// THEN fire stx:load \u2014 now _latestSetup is set and processElement has the right scope
|
|
965
|
+
window.dispatchEvent(new Event('stx:load'));
|
|
966
|
+
}
|
|
967
|
+
// The container's own files first, for the same reason as the
|
|
968
|
+
// full-document path: the inline scripts call into them. Only awaited
|
|
969
|
+
// when something actually has to load, so a fragment with no external
|
|
970
|
+
// scripts \u2014 every fragment before this change \u2014 still runs its inline
|
|
971
|
+
// scripts in the same synchronous turn and nothing about its timing
|
|
972
|
+
// moves.
|
|
973
|
+
var fragPending=[];
|
|
974
|
+
fragExternalScripts.forEach(function(src){
|
|
975
|
+
var pending=loadExternalScript(src,url);
|
|
976
|
+
if(pending)fragPending.push(pending);
|
|
977
|
+
});
|
|
978
|
+
if(fragPending.length)Promise.all(fragPending).then(runFragScripts).catch(runFragScripts);
|
|
979
|
+
else runFragScripts();
|
|
980
|
+
}
|
|
981
|
+
return new Promise(function(resolve,reject){
|
|
982
|
+
function completeFragSwap(){
|
|
983
|
+
try{
|
|
984
|
+
doFragSwap();
|
|
985
|
+
resolve(true);
|
|
986
|
+
}catch(err){reject(err)}
|
|
987
|
+
}
|
|
988
|
+
if(runViewTransition(completeFragSwap)){}
|
|
989
|
+
else{currentContent.style.transition='opacity 0.12s ease-out';currentContent.style.opacity='0';setTimeout(function(){completeFragSwap();currentContent.style.opacity='1';setTimeout(function(){currentContent.style.transition=''},150)},120)}
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// Full document mode: parse with DOMParser and extract container content
|
|
994
|
+
var parser=new DOMParser();
|
|
995
|
+
var doc=parser.parseFromString(html,'text/html');
|
|
996
|
+
// Full documents carry the build id as a meta rather than a header, and
|
|
997
|
+
// this path also serves cached HTML, so check here too (#1772).
|
|
998
|
+
var docBuildMeta=doc.querySelector('meta[name="stx-build"]');
|
|
999
|
+
var docBuild=docBuildMeta?(docBuildMeta.getAttribute('content')||''):'';
|
|
1000
|
+
if(isBuildSkew(docBuild)){reloadForSkew(url,docBuild);return Promise.resolve(false)}
|
|
1001
|
+
// Same hand-off as the fragment path, for the same reason (#1809, #1839).
|
|
1002
|
+
// A full document DOES carry the destination's runtime \u2014 and this path
|
|
1003
|
+
// throws it away: prepareRoutedBodyScripts drops every script[src] and
|
|
1004
|
+
// everything isSignalsRuntimeScript matches, on the assumption that the
|
|
1005
|
+
// current page already has one. When it does not, nothing puts a runtime
|
|
1006
|
+
// back, so the destination's client code dies on its first line with
|
|
1007
|
+
// "defineStore is not defined" (defineStore lives on window.stx) and the
|
|
1008
|
+
// page renders quietly inert \u2014 no error reaches the user.
|
|
1009
|
+
//
|
|
1010
|
+
// Before the swap and before _cleanupContainer, so we neither dispose the
|
|
1011
|
+
// outgoing page's scopes nor half-replace the document and then reload.
|
|
1012
|
+
if(!window.stx&&documentShipsRuntime(doc,html)){
|
|
1013
|
+
log('[router] document needs the signals runtime and this page has none \u2014 full navigation to:',url);
|
|
1014
|
+
location.href=url;
|
|
1015
|
+
return Promise.resolve(false);
|
|
1016
|
+
}
|
|
1017
|
+
var newContent=doc.querySelector(containerSel)||doc.querySelector('[data-stx-content]')||doc.querySelector('main');
|
|
1018
|
+
if(!newContent){location.href=url;return Promise.resolve(false)}
|
|
1019
|
+
|
|
1020
|
+
// Clean up existing signals/effects
|
|
1021
|
+
if(window.stx&&window.stx._cleanupContainer){
|
|
1022
|
+
window.stx._cleanupContainer(currentContent);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
function doSwap(){
|
|
1026
|
+
// \u2500\u2500 Swap <head> styles \u2500\u2500
|
|
1027
|
+
// Inject new styles FIRST, then remove old to prevent unstyled flash
|
|
1028
|
+
var keepIds={'stx-view-transitions':1,'stx-r-css':1};
|
|
1029
|
+
var curStyles=document.querySelectorAll('head style');
|
|
1030
|
+
var newStyles=doc.querySelectorAll('head style');
|
|
1031
|
+
|
|
1032
|
+
// Merge css styles instead of replacing \u2014 persistent elements
|
|
1033
|
+
// (nav, footer) outside <main> still need their utility classes
|
|
1034
|
+
var curCss=document.querySelector('head style[data-css]');
|
|
1035
|
+
var newCss=null;
|
|
1036
|
+
newStyles.forEach(function(s){if(s.getAttribute('data-css'))newCss=s});
|
|
1037
|
+
|
|
1038
|
+
if(curCss&&newCss){
|
|
1039
|
+
var merged=mergeCss(curCss.textContent||'',newCss.textContent||'');
|
|
1040
|
+
if(merged)curCss.textContent=merged;
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
var incoming=[];
|
|
1044
|
+
newStyles.forEach(function(s){
|
|
1045
|
+
if(!keepIds[s.id]&&!s.getAttribute('data-css')){
|
|
1046
|
+
var ns=document.createElement('style');
|
|
1047
|
+
ns.textContent=s.textContent;
|
|
1048
|
+
ns.setAttribute('data-stx-incoming','');
|
|
1049
|
+
document.head.appendChild(ns);
|
|
1050
|
+
incoming.push(ns);
|
|
1051
|
+
}
|
|
1052
|
+
});
|
|
1053
|
+
|
|
1054
|
+
// If no existing css but new page has one, add it
|
|
1055
|
+
if(!curCss&&newCss){
|
|
1056
|
+
var ns=document.createElement('style');
|
|
1057
|
+
ns.textContent=newCss.textContent;
|
|
1058
|
+
ns.setAttribute('data-css',newCss.getAttribute('data-css'));
|
|
1059
|
+
document.head.appendChild(ns);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// Remove old styles (except persistent ones, css, and incoming)
|
|
1063
|
+
curStyles.forEach(function(s){
|
|
1064
|
+
if(!keepIds[s.id]&&!s.hasAttribute('data-stx-incoming')&&!s.hasAttribute('data-css'))s.remove();
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
incoming.forEach(function(s){s.removeAttribute('data-stx-incoming')});
|
|
1068
|
+
|
|
1069
|
+
// \u2500\u2500 Swap <head> stylesheet / font <link>s \u2500\u2500
|
|
1070
|
+
// Without this, an SPA navigation to a page whose <link rel="stylesheet">
|
|
1071
|
+
// (or font/preconnect <link>) the current page had not already loaded left
|
|
1072
|
+
// the swapped-in content completely unstyled (e.g. a page that pulls in its
|
|
1073
|
+
// own marketing.css, or Google Fonts). Additive and href-deduped, mirroring
|
|
1074
|
+
// the external <head script[src]> reconcile below \u2014 never removes existing
|
|
1075
|
+
// links, so stylesheets shared across pages simply persist.
|
|
1076
|
+
// Relative asset hrefs resolve against the document that declared them:
|
|
1077
|
+
// the CURRENT page for links already in <head>, and the page being
|
|
1078
|
+
// navigated TO for links from the fetched document. Against location.origin
|
|
1079
|
+
// both collapsed to the site root, so 'assets/app.css' on /docs/intro
|
|
1080
|
+
// deduped as /assets/app.css \u2014 the wrong file, and a false cache hit that
|
|
1081
|
+
// skipped the real stylesheet (#1777).
|
|
1082
|
+
var docBase;
|
|
1083
|
+
try{docBase=new URL(url,location.href).href}catch(e){docBase=location.href}
|
|
1084
|
+
var linkSel='link[rel="stylesheet"],link[rel="preconnect"]';
|
|
1085
|
+
var curLinks={};
|
|
1086
|
+
document.querySelectorAll('head '+linkSel).forEach(function(l){var h=l.getAttribute('href');if(h)curLinks[new URL(h,location.href).href]=1});
|
|
1087
|
+
doc.querySelectorAll('head '+linkSel).forEach(function(l){
|
|
1088
|
+
var href=l.getAttribute('href');
|
|
1089
|
+
if(!href)return;
|
|
1090
|
+
var abs=new URL(href,docBase).href;
|
|
1091
|
+
if(curLinks[abs])return;
|
|
1092
|
+
curLinks[abs]=1;
|
|
1093
|
+
var nl=document.createElement('link');
|
|
1094
|
+
Array.from(l.attributes).forEach(function(a){nl.setAttribute(a.name,a.value)});
|
|
1095
|
+
document.head.appendChild(nl);
|
|
1096
|
+
});
|
|
1097
|
+
|
|
1098
|
+
// \u2500\u2500 Swap content \u2500\u2500
|
|
1099
|
+
// For layout changes: swap the entire <body> to replace layout chrome (nav, footer, etc.)
|
|
1100
|
+
// For same-layout: swap only the container (<main>)
|
|
1101
|
+
var newBody=doc.querySelector('body');
|
|
1102
|
+
var isLayoutChange=false;
|
|
1103
|
+
var declaredLayout=pendingLayoutDecl;
|
|
1104
|
+
pendingLayoutDecl=null;
|
|
1105
|
+
if(newBody){
|
|
1106
|
+
var newMeta=doc.querySelector('meta[name="stx-layout"]');
|
|
1107
|
+
var curMeta=document.querySelector('meta[name="stx-layout"]');
|
|
1108
|
+
var curLayout=curMeta?curMeta.getAttribute('content'):'';
|
|
1109
|
+
// The server's answer wins over the document's metas. We are only here
|
|
1110
|
+
// because checkLayoutChange already said the group changed, based on
|
|
1111
|
+
// exactly these headers \u2014 re-deriving from metas and disagreeing meant
|
|
1112
|
+
// paying for the full-document fetch and then discarding the reason we
|
|
1113
|
+
// made it, so the destination's <main> was swapped into the previous
|
|
1114
|
+
// page's chrome (#1833).
|
|
1115
|
+
//
|
|
1116
|
+
// They disagree more easily than they look. The bun-plugin serve path
|
|
1117
|
+
// calls a layout-less page 'default' while the client's own fallback
|
|
1118
|
+
// calls it 'app'; a <head> with attributes silently skips the meta
|
|
1119
|
+
// injection entirely; and the i18n path rewrites every page's group to
|
|
1120
|
+
// i18n:<locale>. Any one of those leaves the metas saying "unchanged"
|
|
1121
|
+
// about a document the headers said had changed.
|
|
1122
|
+
var newLayout=declaredLayout&&declaredLayout.layout
|
|
1123
|
+
? declaredLayout.layout
|
|
1124
|
+
: (newMeta?newMeta.getAttribute('content'):'');
|
|
1125
|
+
var curGroup=getCurrentLayoutGroup();
|
|
1126
|
+
var newGroup=declaredLayout&&declaredLayout.group
|
|
1127
|
+
? declaredLayout.group
|
|
1128
|
+
: getDocLayoutGroup(doc,newLayout);
|
|
1129
|
+
isLayoutChange=curGroup!==newGroup||!!(curLayout&&newLayout&&curLayout!==newLayout);
|
|
1130
|
+
}
|
|
1131
|
+
var incomingSetupName=newBody?(newBody.getAttribute('data-stx')||''):'';
|
|
1132
|
+
// Scripts assigned through innerHTML are inert. Preserve executable
|
|
1133
|
+
// scripts as placeholders so re-execution keeps document.currentScript
|
|
1134
|
+
// beside the component template it owns.
|
|
1135
|
+
var routedBodyScripts=[];
|
|
1136
|
+
var routedExternalScripts=[];
|
|
1137
|
+
var routedBodyScriptId=0;
|
|
1138
|
+
function prepareRoutedBodyScripts(root){
|
|
1139
|
+
if(!root)return;
|
|
1140
|
+
root.querySelectorAll('script').forEach(function(s){
|
|
1141
|
+
var text=s.textContent||'';
|
|
1142
|
+
// Not discarded: queued against docBase, so a relative src resolves
|
|
1143
|
+
// against the page being navigated to rather than the current URL.
|
|
1144
|
+
if(s.hasAttribute('src')){routedExternalScripts.push(s.getAttribute('src'));s.remove();return}
|
|
1145
|
+
var type=(s.getAttribute('type')||'').trim().toLowerCase();
|
|
1146
|
+
var executable=!type||type==='text/javascript'||type==='application/javascript'||type==='module';
|
|
1147
|
+
if(!executable||!text.trim())return;
|
|
1148
|
+
if(isSignalsRuntimeScript(s,text)||text.indexOf('__stxRouter')!==-1){s.remove();return}
|
|
1149
|
+
var slot='document-'+(++routedBodyScriptId);
|
|
1150
|
+
var setupName=generatedSetupName(text);
|
|
1151
|
+
if(setupName)incomingSetupName=setupName;
|
|
1152
|
+
routedBodyScripts.push({text:text,runAlways:true,slot:slot,setupName:setupName,run:s.getAttribute('data-stx-run')||''});
|
|
1153
|
+
s.textContent='';
|
|
1154
|
+
s.setAttribute('type','application/stx-pending');
|
|
1155
|
+
s.setAttribute('data-stx-route-script',slot);
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
prepareRoutedBodyScripts(isLayoutChange?newBody:newContent);
|
|
1159
|
+
if(isLayoutChange&&newBody){
|
|
1160
|
+
log('[router] full body swap for layout change');
|
|
1161
|
+
// Replace entire body content \u2014 layout chrome and all
|
|
1162
|
+
var bodyHTML=newBody.innerHTML;
|
|
1163
|
+
document.body.innerHTML=bodyHTML;
|
|
1164
|
+
// Copy body attributes (class, data-stx, etc.)
|
|
1165
|
+
Array.from(newBody.attributes).forEach(function(attr){document.body.setAttribute(attr.name,attr.value)});
|
|
1166
|
+
// Update layout meta tag
|
|
1167
|
+
var oldMeta=document.querySelector('meta[name="stx-layout"]');
|
|
1168
|
+
var freshMeta=doc.querySelector('meta[name="stx-layout"]');
|
|
1169
|
+
if(oldMeta&&freshMeta)oldMeta.setAttribute('content',freshMeta.getAttribute('content')||'');
|
|
1170
|
+
else if(freshMeta){var m=document.createElement('meta');m.name='stx-layout';m.content=freshMeta.getAttribute('content')||'';document.head.appendChild(m)}
|
|
1171
|
+
var oldGroupMeta=document.querySelector('meta[name="stx-layout-group"]');
|
|
1172
|
+
var freshGroupMeta=doc.querySelector('meta[name="stx-layout-group"]');
|
|
1173
|
+
if(oldGroupMeta&&freshGroupMeta)oldGroupMeta.setAttribute('content',freshGroupMeta.getAttribute('content')||'');
|
|
1174
|
+
else if(freshGroupMeta){var gm=document.createElement('meta');gm.name='stx-layout-group';gm.content=freshGroupMeta.getAttribute('content')||'';document.head.appendChild(gm)}
|
|
1175
|
+
// Update container reference for script execution below
|
|
1176
|
+
currentContent=document.querySelector(containerSel)||document.querySelector('main')||document.body;
|
|
1177
|
+
} else {
|
|
1178
|
+
// Same layout \u2014 swap only container content. Carry the destination
|
|
1179
|
+
// container's attributes across too (same reason as the fragment path:
|
|
1180
|
+
// pages whose layout lives on <main> itself would otherwise lose it).
|
|
1181
|
+
var attrMap={};
|
|
1182
|
+
Array.prototype.forEach.call(newContent.attributes,function(a){attrMap[a.name]=a.value});
|
|
1183
|
+
setContainerAttrs(currentContent,attrMap);
|
|
1184
|
+
var cleanHTML=newContent.innerHTML;
|
|
1185
|
+
currentContent.innerHTML=cleanHTML;
|
|
1186
|
+
if(incomingSetupName)document.body.setAttribute('data-stx',incomingSetupName);
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
// \u2500\u2500 Load new external <head> scripts \u2500\u2500
|
|
1190
|
+
var loadedSrcs={};
|
|
1191
|
+
document.querySelectorAll('head script[src]').forEach(function(s){loadedSrcs[s.src]=1});
|
|
1192
|
+
var extPromises=[];
|
|
1193
|
+
doc.querySelectorAll('head script[src]').forEach(function(s){
|
|
1194
|
+
// Fetched-document script: resolve against the page being navigated to
|
|
1195
|
+
// (docBase), not the origin root \u2014 same rationale as the <link> reconcile (#1777).
|
|
1196
|
+
var src=new URL(s.getAttribute('src'),docBase).href;
|
|
1197
|
+
if(loadedSrcs[src])return;
|
|
1198
|
+
loadedSrcs[src]=1;
|
|
1199
|
+
extPromises.push(new Promise(function(resolve,reject){
|
|
1200
|
+
var ns=document.createElement('script');
|
|
1201
|
+
ns.src=src;
|
|
1202
|
+
ns.onload=resolve;
|
|
1203
|
+
ns.onerror=reject;
|
|
1204
|
+
document.head.appendChild(ns);
|
|
1205
|
+
}));
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
// Before the inline scripts, and through the same barrier: an inline
|
|
1209
|
+
// script's whole reason for existing is often to call into one of these,
|
|
1210
|
+
// so running it first would fail exactly the way this bug did.
|
|
1211
|
+
routedExternalScripts.forEach(function(src){
|
|
1212
|
+
var pending=loadExternalScript(src,docBase);
|
|
1213
|
+
if(pending)extPromises.push(pending);
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
// \u2500\u2500 Script re-execution \u2500\u2500
|
|
1217
|
+
// Remove previously injected page scripts
|
|
1218
|
+
document.querySelectorAll('script[data-stx-page]').forEach(function(s){s.remove()});
|
|
1219
|
+
|
|
1220
|
+
var scripts=routedBodyScripts.slice();
|
|
1221
|
+
function addScript(text, runAlways, slot){
|
|
1222
|
+
scripts.push({text:text,runAlways:!!runAlways,slot:slot||''});
|
|
1223
|
+
}
|
|
1224
|
+
if(isLayoutChange){
|
|
1225
|
+
// Also collect setup functions from <head>
|
|
1226
|
+
doc.querySelectorAll('head script').forEach(function(s){
|
|
1227
|
+
var text=s.textContent||'';
|
|
1228
|
+
if(s.hasAttribute('src'))return;
|
|
1229
|
+
if(!text.trim())return;
|
|
1230
|
+
if(isSignalsRuntimeScript(s,text))return;
|
|
1231
|
+
if(text.indexOf('__stx_setup_')!==-1)addScript(text,true);
|
|
1232
|
+
});
|
|
1233
|
+
} else {
|
|
1234
|
+
// Collect page scripts from <head> AND <body> (outside container).
|
|
1235
|
+
// SSG builds place the setup function in <body> before <main>, not
|
|
1236
|
+
// in <head>. Without this, SPA navigation on static sites never
|
|
1237
|
+
// runs the setup and reactive data stays empty.
|
|
1238
|
+
var seenSetups={};
|
|
1239
|
+
scripts.forEach(function(entry){var t=entry.text;if(t.indexOf('__stx_setup_')!==-1)seenSetups[t.substring(0,80)]=1});
|
|
1240
|
+
doc.querySelectorAll('script').forEach(function(s){
|
|
1241
|
+
var text=s.textContent||'';
|
|
1242
|
+
if(s.hasAttribute('src'))return;
|
|
1243
|
+
if(!text.trim())return;
|
|
1244
|
+
// Skip signals runtime and router (they contain __stx_setup references but aren't setup functions)
|
|
1245
|
+
if(isSignalsRuntimeScript(s,text))return;
|
|
1246
|
+
if(text.indexOf('__stxRouter')!==-1)return;
|
|
1247
|
+
if(newContent.contains(s))return;
|
|
1248
|
+
if(text.indexOf('__stx_setup_')===-1&&!s.hasAttribute('data-stx-scoped')&&!s.hasAttribute('data-stx-page')&&!s.hasAttribute('data-stx-route-params'))return;
|
|
1249
|
+
var key=text.substring(0,80);
|
|
1250
|
+
if(seenSetups[key])return;
|
|
1251
|
+
seenSetups[key]=1;
|
|
1252
|
+
addScript(text,true);
|
|
1253
|
+
});
|
|
1254
|
+
}
|
|
1255
|
+
// Component setup scripts also assign window.stx._latestSetup. Run the
|
|
1256
|
+
// destination page setup last so stx:load hydrates with the page scope,
|
|
1257
|
+
// not whichever nested component happened to render last.
|
|
1258
|
+
if(incomingSetupName){
|
|
1259
|
+
scripts.sort(function(a,b){
|
|
1260
|
+
var aPage=(a.setupName||generatedSetupName(a.text))===incomingSetupName?1:0;
|
|
1261
|
+
var bPage=(b.setupName||generatedSetupName(b.text))===incomingSetupName?1:0;
|
|
1262
|
+
return aPage-bPage;
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
// Push history state (before active link updates so location.pathname is current)
|
|
1267
|
+
if(pushState!==false)writeHistory(pushState,url+(hash||''));
|
|
1268
|
+
|
|
1269
|
+
// Update active nav links
|
|
1270
|
+
updateNav(url);
|
|
1271
|
+
updateActiveLinks();
|
|
1272
|
+
|
|
1273
|
+
// Scroll
|
|
1274
|
+
if(o.scrollToTop&&!hash)window.scrollTo({top:0,behavior:'instant'});
|
|
1275
|
+
else if(hash){var el=document.querySelector(hash);if(el)el.scrollIntoView({behavior:'smooth'})}
|
|
1276
|
+
|
|
1277
|
+
// Update title
|
|
1278
|
+
var newTitle=doc.querySelector('title');
|
|
1279
|
+
if(newTitle)document.title=newTitle.textContent;
|
|
1280
|
+
|
|
1281
|
+
// Update <html lang> from the destination doc so screen readers,
|
|
1282
|
+
// CSS :lang() selectors, and any i18n picker that mirrors
|
|
1283
|
+
// document.documentElement.lang stay accurate after SPA hops.
|
|
1284
|
+
if(doc.documentElement&&doc.documentElement.lang){
|
|
1285
|
+
document.documentElement.lang=doc.documentElement.lang;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
// \u2500\u2500 Reconcile <html> attributes (stacksjs/stx#1798) \u2500\u2500
|
|
1289
|
+
// A layout that scopes its design tokens to the root element
|
|
1290
|
+
// (html.marketing { --bg: \u2026 }) needs that class to LEAVE when you
|
|
1291
|
+
// navigate to a layout that doesn't want it \u2014 otherwise both token sets
|
|
1292
|
+
// match at once and the second layout paints with the first one's
|
|
1293
|
+
// palette. Head stylesheets are additive across a swap, so the class is
|
|
1294
|
+
// the only thing disambiguating them.
|
|
1295
|
+
//
|
|
1296
|
+
// Only what stx wrote is touched, per the markers emitted by
|
|
1297
|
+
// document-shell.ts. Diffing the whole element against the incoming
|
|
1298
|
+
// document would strip the color-mode boot's dark class and
|
|
1299
|
+
// data-reduced-motion, which exist only on the live page.
|
|
1300
|
+
if(doc.documentElement){
|
|
1301
|
+
var curRoot=document.documentElement,incRoot=doc.documentElement;
|
|
1302
|
+
var tokens=function(el,attr){var v=el.getAttribute(attr);return v?v.split(/\\s+/).filter(Boolean):[]};
|
|
1303
|
+
var prevCls=tokens(curRoot,'data-stx-html-class'),nextCls=tokens(incRoot,'data-stx-html-class');
|
|
1304
|
+
prevCls.forEach(function(c){if(nextCls.indexOf(c)===-1)curRoot.classList.remove(c)});
|
|
1305
|
+
nextCls.forEach(function(c){curRoot.classList.add(c)});
|
|
1306
|
+
if(nextCls.length)curRoot.setAttribute('data-stx-html-class',nextCls.join(' '));
|
|
1307
|
+
else curRoot.removeAttribute('data-stx-html-class');
|
|
1308
|
+
|
|
1309
|
+
var prevNames=tokens(curRoot,'data-stx-html-attrs'),nextNames=tokens(incRoot,'data-stx-html-attrs');
|
|
1310
|
+
prevNames.forEach(function(n){if(nextNames.indexOf(n)===-1)curRoot.removeAttribute(n)});
|
|
1311
|
+
nextNames.forEach(function(n){var v=incRoot.getAttribute(n);if(v!==null)curRoot.setAttribute(n,v)});
|
|
1312
|
+
if(nextNames.length)curRoot.setAttribute('data-stx-html-attrs',nextNames.join(' '));
|
|
1313
|
+
else curRoot.removeAttribute('data-stx-html-attrs');
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
window.dispatchEvent(new CustomEvent('stx:navigate',{detail:{url:url}}));
|
|
1317
|
+
|
|
1318
|
+
// Execute page scripts FIRST \u2014 they define setup functions and set _latestSetup
|
|
1319
|
+
function execScripts(){
|
|
1320
|
+
scripts.forEach(function(entry){
|
|
1321
|
+
var text=typeof entry==='string'?entry:entry.text;
|
|
1322
|
+
var runAlways=typeof entry==='string'?false:entry.runAlways;
|
|
1323
|
+
// Skip scripts that were already executed (layout-level partials
|
|
1324
|
+
// like theme.stx). Their top-level const/function declarations
|
|
1325
|
+
// would throw "Identifier has already been declared" on re-execution.
|
|
1326
|
+
// Exception: setup functions (__stx_setup_) must always re-execute
|
|
1327
|
+
// because each page has its own setup, and import statements
|
|
1328
|
+
// need special handling.
|
|
1329
|
+
var h=hashScript(text);
|
|
1330
|
+
var isSetup=text.indexOf('__stx_setup_')!==-1;
|
|
1331
|
+
var hasImport=hasStaticImport(text);
|
|
1332
|
+
if(!runAlways&&!isSetup&&executedScriptHashes[h])return;
|
|
1333
|
+
executedScriptHashes[h]=1;
|
|
1334
|
+
// Wrap scripts with import statements as modules (Bug 3 fix)
|
|
1335
|
+
var ns=document.createElement('script');
|
|
1336
|
+
if(hasImport){
|
|
1337
|
+
ns.type='module';
|
|
1338
|
+
}
|
|
1339
|
+
// Wrap in block scope to prevent const/let collisions on re-navigation.
|
|
1340
|
+
// Top-level const/let in <script> tags are global-scoped in browsers
|
|
1341
|
+
// and throw "Identifier has already been declared" on re-execution.
|
|
1342
|
+
// Skip the wrap for module scripts \u2014 they get their own scope from
|
|
1343
|
+
// ESM, and top-level 'import' is illegal inside a block, which
|
|
1344
|
+
// would throw SyntaxError before the script ever runs.
|
|
1345
|
+
var alreadyScoped=runsAlways(typeof entry==='string'?'':entry.run,text);
|
|
1346
|
+
ns.textContent=(hasImport||alreadyScoped)?text:'{'+text+'}';
|
|
1347
|
+
ns.setAttribute('data-stx-page','');
|
|
1348
|
+
var placeholder=entry.slot?document.querySelector('script[data-stx-route-script="'+entry.slot+'"]'):null;
|
|
1349
|
+
if(placeholder&&placeholder.parentNode){
|
|
1350
|
+
ns.setAttribute('data-stx-positioned','');
|
|
1351
|
+
placeholder.parentNode.replaceChild(ns,placeholder);
|
|
1352
|
+
}else{
|
|
1353
|
+
document.body.appendChild(ns);
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
// THEN fire stx:load \u2014 now _latestSetup is set and processElement has the right scope
|
|
1357
|
+
window.dispatchEvent(new Event('stx:load'));
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
if(extPromises.length>0){
|
|
1361
|
+
Promise.all(extPromises).then(execScripts).catch(execScripts);
|
|
1362
|
+
}
|
|
1363
|
+
else {
|
|
1364
|
+
execScripts();
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
return new Promise(function(resolve,reject){
|
|
1369
|
+
function completeSwap(){
|
|
1370
|
+
try{
|
|
1371
|
+
doSwap();
|
|
1372
|
+
resolve(true);
|
|
1373
|
+
}catch(err){reject(err)}
|
|
1374
|
+
}
|
|
1375
|
+
if(runViewTransition(completeSwap)){
|
|
1376
|
+
}
|
|
1377
|
+
else {
|
|
1378
|
+
// Fallback fade for browsers without View Transitions API
|
|
1379
|
+
currentContent.style.transition='opacity 0.12s ease-out';
|
|
1380
|
+
currentContent.style.opacity='0';
|
|
1381
|
+
setTimeout(function(){
|
|
1382
|
+
completeSwap();
|
|
1383
|
+
currentContent.style.opacity='1';
|
|
1384
|
+
setTimeout(function(){currentContent.style.transition=''},150);
|
|
1385
|
+
},120);
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
// \u2500\u2500 Link interception \u2500\u2500
|
|
1391
|
+
// Links the router must never touch, however it found them. This is the
|
|
1392
|
+
// opt-out set, and it is deliberately separate from shouldIntercept's
|
|
1393
|
+
// eligibility rules below: [data-stx-link] is an author saying "this is a
|
|
1394
|
+
// router link", which answers eligibility but must NOT override an explicit
|
|
1395
|
+
// opt-out or a non-navigational scheme.
|
|
1396
|
+
function isRouterExcluded(link){
|
|
1397
|
+
if(!link)return true;
|
|
1398
|
+
var href=link.getAttribute('href');
|
|
1399
|
+
if(!href)return true;
|
|
1400
|
+
if(href.startsWith('http')||href.startsWith('#')||href.startsWith('mailto:')||href.startsWith('tel:')||href.startsWith('javascript:'))return true;
|
|
1401
|
+
if(link.target==='_blank')return true;
|
|
1402
|
+
if(link.hasAttribute('data-stx-no-router')||link.hasAttribute('data-no-router')||link.hasAttribute('download'))return true;
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// Paths the router can actually render, compiled from the server's own route
|
|
1407
|
+
// table and shipped as regex sources (#1864). Absent when discovery found
|
|
1408
|
+
// nothing, which means UNKNOWN, not "owns nothing" \u2014 treating it as the
|
|
1409
|
+
// latter would disable SPA navigation for a whole site.
|
|
1410
|
+
var ownedRoutes=null;
|
|
1411
|
+
(function(){
|
|
1412
|
+
var raw=o.ownedRoutes;
|
|
1413
|
+
if(!raw||!raw.length)return;
|
|
1414
|
+
var compiled=[];
|
|
1415
|
+
for(var i=0;i<raw.length;i++){
|
|
1416
|
+
try{compiled.push(new RegExp(raw[i]))}
|
|
1417
|
+
catch(e){}
|
|
1418
|
+
}
|
|
1419
|
+
if(compiled.length)ownedRoutes=compiled;
|
|
1420
|
+
})();
|
|
1421
|
+
|
|
1422
|
+
// Does the router own this path? Only meaningful when the route table made it
|
|
1423
|
+
// to the client; otherwise every path is treated as owned, preserving the old
|
|
1424
|
+
// behaviour.
|
|
1425
|
+
function routerOwns(pathname){
|
|
1426
|
+
if(!ownedRoutes)return true;
|
|
1427
|
+
for(var i=0;i<ownedRoutes.length;i++){
|
|
1428
|
+
if(ownedRoutes[i].test(pathname))return true;
|
|
1429
|
+
}
|
|
1430
|
+
return false;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
// Eligibility for auto-claiming an arbitrary same-origin anchor under
|
|
1434
|
+
// interceptAllLinks. Only reached for anchors the author did not mark.
|
|
1435
|
+
function shouldIntercept(link){
|
|
1436
|
+
if(isRouterExcluded(link))return false;
|
|
1437
|
+
var href=link.getAttribute('href');
|
|
1438
|
+
if(href.startsWith('?'))return true;
|
|
1439
|
+
if(href===location.pathname)return false;
|
|
1440
|
+
if(!getContainer())return false;
|
|
1441
|
+
// An endpoint the router cannot render must go to the browser. Claiming it
|
|
1442
|
+
// meant a failed fragment fetch, a fallback navigation, and the endpoint
|
|
1443
|
+
// hit TWICE \u2014 on OAuth redirects, that mints state twice.
|
|
1444
|
+
var path=href;
|
|
1445
|
+
var q=path.indexOf('?');if(q!==-1)path=path.slice(0,q);
|
|
1446
|
+
var h=path.indexOf('#');if(h!==-1)path=path.slice(0,h);
|
|
1447
|
+
if(path.charAt(0)==='/'&&!routerOwns(path)){
|
|
1448
|
+
log('[router] not a known route, leaving to the browser:',href);
|
|
1449
|
+
return false;
|
|
1450
|
+
}
|
|
1451
|
+
return true;
|
|
1452
|
+
}
|
|
1453
|
+
|
|
1454
|
+
// SPA click handling. By default only [data-stx-link] elements get
|
|
1455
|
+
// SPA navigation; regular <a href> does native full page reload. Set
|
|
1456
|
+
// interceptAllLinks:true (window.__stxRouterConfig or build-time
|
|
1457
|
+
// config) to intercept any same-origin anchor \u2014 used by static-site
|
|
1458
|
+
// mode where every page is part of the same SPA shell.
|
|
1459
|
+
document.addEventListener('click',function(e){
|
|
1460
|
+
if(e.metaKey||e.ctrlKey||e.shiftKey||e.altKey||e.button!==0)return;
|
|
1461
|
+
// A handler on the page has already decided this click is not a
|
|
1462
|
+
// navigation, so the router must not perform one - the same contract the
|
|
1463
|
+
// browser honours for a plain anchor.
|
|
1464
|
+
//
|
|
1465
|
+
// This listener used to run in the CAPTURE phase, which broke that twice
|
|
1466
|
+
// over: it saw the click before the anchor did, so defaultPrevented was
|
|
1467
|
+
// necessarily false by the time it was read here, and the stopPropagation
|
|
1468
|
+
// below then stopped the event ever reaching the element - so a page's own
|
|
1469
|
+
// click handler on an intercepted link never ran AT ALL. Progressive
|
|
1470
|
+
// enhancement on an anchor was impossible; the only way out was to swap it
|
|
1471
|
+
// for a button in script (stacksjs/stacks#2393).
|
|
1472
|
+
//
|
|
1473
|
+
// Bubbling instead puts the page's handlers first, which is where a
|
|
1474
|
+
// decision about the page's own markup belongs.
|
|
1475
|
+
if(e.defaultPrevented)return;
|
|
1476
|
+
if(!e.target||!e.target.closest)return;
|
|
1477
|
+
var link=e.target.closest('[data-stx-link]');
|
|
1478
|
+
// A marked link still has to clear the opt-out set. It used to skip it
|
|
1479
|
+
// entirely, so data-no-router on a [data-stx-link] anchor was silently
|
|
1480
|
+
// ignored and an OAuth redirect got claimed by the router.
|
|
1481
|
+
if(link&&isRouterExcluded(link)){log('[router] excluded:',link.getAttribute('href'));return}
|
|
1482
|
+
if(!link&&o.interceptAllLinks){
|
|
1483
|
+
var anchor=e.target.closest('a[href]');
|
|
1484
|
+
if(anchor&&shouldIntercept(anchor))link=anchor;
|
|
1485
|
+
}
|
|
1486
|
+
if(!link){return}
|
|
1487
|
+
var href=link.getAttribute('href');
|
|
1488
|
+
log('[router] click intercepted:',href,'container:',!!getContainer(),'defaultPrevented:',e.defaultPrevented);
|
|
1489
|
+
e.preventDefault();
|
|
1490
|
+
e.stopPropagation();
|
|
1491
|
+
log('[router] navigating to:',href);
|
|
1492
|
+
navigate(withCurrentLocale(href));
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
// \u2500\u2500 Form submission \u2500\u2500
|
|
1496
|
+
// A submit IS a navigation, and the router only ever watched clicks. So any
|
|
1497
|
+
// in-app form tore down the SPA with a full document load and discarded
|
|
1498
|
+
// every signal on the page, and the only way out was a hand-written fetch in
|
|
1499
|
+
// a @submit handler with its own loading and error state.
|
|
1500
|
+
//
|
|
1501
|
+
// Gated exactly like links: a form opts in with data-stx-form, or
|
|
1502
|
+
// interceptForms claims them all \u2014 the same shape as [data-stx-link] /
|
|
1503
|
+
// interceptAllLinks. Deliberately not on by default: a POST has side
|
|
1504
|
+
// effects, so claiming one has to be a decision rather than something that
|
|
1505
|
+
// starts happening on upgrade (#1863).
|
|
1506
|
+
function formAttrOf(form,submitter,submitterAttr,formAttr){
|
|
1507
|
+
// A submit button overrides the form: formaction / formmethod / formenctype.
|
|
1508
|
+
var v=submitter&&submitter.getAttribute?submitter.getAttribute(submitterAttr):null;
|
|
1509
|
+
return (v!==null&&v!==undefined)?v:form.getAttribute(formAttr);
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
function formTargetUrl(form,submitter){
|
|
1513
|
+
return formAttrOf(form,submitter,'formaction','action')||(location.pathname+location.search);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
function formMethodOf(form,submitter){
|
|
1517
|
+
return String(formAttrOf(form,submitter,'formmethod','method')||'get').toLowerCase();
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
function formExcluded(form,submitter){
|
|
1521
|
+
if(!form||form.tagName!=='FORM')return true;
|
|
1522
|
+
if(form.hasAttribute('data-stx-no-router')||form.hasAttribute('data-no-router'))return true;
|
|
1523
|
+
// A form aimed at another browsing context is not this page's navigation.
|
|
1524
|
+
if(form.target&&form.target!=='_self')return true;
|
|
1525
|
+
try{
|
|
1526
|
+
// Resolving against location handles relative, absolute and foreign
|
|
1527
|
+
// schemes in one step: mailto: parses to a null origin and is excluded.
|
|
1528
|
+
if(new URL(formTargetUrl(form,submitter),location.href).origin!==location.origin)return true;
|
|
1529
|
+
}
|
|
1530
|
+
catch(e){return true}
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
|
|
1534
|
+
function setFormPending(form,on){
|
|
1535
|
+
if(on){form.classList.add('stx-submitting');form.setAttribute('aria-busy','true')}
|
|
1536
|
+
else{form.classList.remove('stx-submitting');form.removeAttribute('aria-busy')}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
function submitForm(form,submitter){
|
|
1540
|
+
var method=formMethodOf(form,submitter);
|
|
1541
|
+
var action=formTargetUrl(form,submitter);
|
|
1542
|
+
var fd=new FormData(form);
|
|
1543
|
+
// A named submit button contributes its value, exactly as a native submit
|
|
1544
|
+
// would. FormData does not include it, so multi-button forms (Save vs
|
|
1545
|
+
// Delete) would otherwise lose the one piece of state that distinguishes them.
|
|
1546
|
+
if(submitter&&submitter.name)fd.append(submitter.name,submitter.value||'');
|
|
1547
|
+
|
|
1548
|
+
if(method==='get'){
|
|
1549
|
+
// A GET form is a link whose href the user filled in. Serialise and hand
|
|
1550
|
+
// it to the ordinary navigation path \u2014 no special swap handling, and it
|
|
1551
|
+
// inherits caching, prefetch and the guard-redirect behaviour for free.
|
|
1552
|
+
var qs=new URLSearchParams();
|
|
1553
|
+
fd.forEach(function(v,k){if(typeof v==='string')qs.append(k,v)});
|
|
1554
|
+
var gu=new URL(action,location.href);
|
|
1555
|
+
gu.search=qs.toString();
|
|
1556
|
+
return navigate(gu.pathname+gu.search+gu.hash);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
setFormPending(form,true);
|
|
1560
|
+
form.dispatchEvent(new CustomEvent('stx:form-submit',{bubbles:true,detail:{action:action,method:method}}));
|
|
1561
|
+
|
|
1562
|
+
var enctype=String(formAttrOf(form,submitter,'formenctype','enctype')||'').toLowerCase();
|
|
1563
|
+
// Respect the declared encoding. Handing FormData to fetch always produces
|
|
1564
|
+
// multipart, which a server expecting urlencoded will read as empty.
|
|
1565
|
+
var body=enctype.indexOf('multipart')===0?fd:new URLSearchParams(fd);
|
|
1566
|
+
var wantsFragment=shouldUseFragmentResponse();
|
|
1567
|
+
|
|
1568
|
+
return fetch(action,{
|
|
1569
|
+
method:method.toUpperCase(),
|
|
1570
|
+
body:body,
|
|
1571
|
+
headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'},
|
|
1572
|
+
credentials:'same-origin',
|
|
1573
|
+
}).then(function(r){
|
|
1574
|
+
// POST/redirect/GET. The server said where the result lives, so go there
|
|
1575
|
+
// through the normal path rather than swapping the POST's body under the
|
|
1576
|
+
// form's own URL \u2014 otherwise reloading re-submits.
|
|
1577
|
+
if(r.redirected&&r.url){
|
|
1578
|
+
var rd=new URL(r.url,location.href);
|
|
1579
|
+
if(rd.origin!==location.origin){location.href=r.url;return}
|
|
1580
|
+
return navigate(rd.pathname+rd.search+rd.hash);
|
|
1581
|
+
}
|
|
1582
|
+
return r.text().then(function(html){
|
|
1583
|
+
var isFrag=wantsFragment&&r.headers.get('X-STX-Fragment')==='true';
|
|
1584
|
+
pendingContainerAttrs=isFrag?(r.headers.get('X-STX-Container-Attrs')||''):'';
|
|
1585
|
+
pendingLayoutDecl=isFrag?null:{layout:r.headers.get('X-STX-Layout')||'',group:r.headers.get('X-STX-Layout-Group')||''};
|
|
1586
|
+
var marked=isFrag?fragmentMarker(r.headers.get('X-STX-Runtime')||'')+html:html;
|
|
1587
|
+
// Deliberately NOT cached: this body is the answer to one POST, and
|
|
1588
|
+
// serving it later for a GET of the same path would be a lie.
|
|
1589
|
+
// 'replace' so re-rendered validation errors do not stack one history
|
|
1590
|
+
// entry per attempt.
|
|
1591
|
+
return Promise.resolve(swap(marked,cacheKey(action),'replace','')).then(function(){
|
|
1592
|
+
applyTitle(r.headers.get('X-STX-Title')||'');
|
|
1593
|
+
});
|
|
1594
|
+
});
|
|
1595
|
+
}).catch(function(err){
|
|
1596
|
+
// No native re-submit on failure: that would send the POST a second time.
|
|
1597
|
+
// The app is told instead, and the form is left intact for a retry.
|
|
1598
|
+
console.error('[router] form submit failed:',err);
|
|
1599
|
+
form.dispatchEvent(new CustomEvent('stx:form-error',{bubbles:true,detail:{error:err,action:action}}));
|
|
1600
|
+
}).finally(function(){setFormPending(form,false)});
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
document.addEventListener('submit',function(e){
|
|
1604
|
+
// Bubble phase, and defaultPrevented is honoured, so a page's own @submit
|
|
1605
|
+
// handler always wins. This is a fallback for forms nobody else claimed,
|
|
1606
|
+
// never an override of one that is already handled.
|
|
1607
|
+
if(e.defaultPrevented)return;
|
|
1608
|
+
var form=e.target;
|
|
1609
|
+
if(!form||form.tagName!=='FORM')return;
|
|
1610
|
+
if(!form.hasAttribute('data-stx-form')&&!form.hasAttribute('data-stx-link')&&!o.interceptForms)return;
|
|
1611
|
+
var submitter=e.submitter||null;
|
|
1612
|
+
if(formExcluded(form,submitter))return;
|
|
1613
|
+
// Without a container there is nothing to swap into, so a native submit is
|
|
1614
|
+
// the only thing that can work.
|
|
1615
|
+
if(!getContainer())return;
|
|
1616
|
+
// Constraint validation has already run by the time submit fires, so an
|
|
1617
|
+
// invalid form never reaches here.
|
|
1618
|
+
e.preventDefault();
|
|
1619
|
+
// A synchronous throw in here would be swallowed by dispatchEvent, and
|
|
1620
|
+
// preventDefault has already run \u2014 so the form would be silently dead with
|
|
1621
|
+
// nothing in the console. Report it and clear the pending state instead.
|
|
1622
|
+
try{submitForm(form,submitter)}
|
|
1623
|
+
catch(err){
|
|
1624
|
+
console.error('[router] form submit failed:',err);
|
|
1625
|
+
setFormPending(form,false);
|
|
1626
|
+
form.dispatchEvent(new CustomEvent('stx:form-error',{bubbles:true,detail:{error:err}}));
|
|
1627
|
+
}
|
|
1628
|
+
});
|
|
1629
|
+
|
|
1630
|
+
// \u2500\u2500 Back/forward \u2500\u2500
|
|
1631
|
+
window.addEventListener('popstate',function(){
|
|
1632
|
+
navigate(location.pathname+location.search+location.hash,false);
|
|
1633
|
+
});
|
|
1634
|
+
|
|
1635
|
+
// \u2500\u2500 Prefetch on hover \u2500\u2500
|
|
1636
|
+
if(o.prefetch){
|
|
1637
|
+
document.addEventListener('mouseover',function(e){
|
|
1638
|
+
if(!e.target||!e.target.closest)return;
|
|
1639
|
+
var link=e.target.closest('[data-stx-link]');
|
|
1640
|
+
// Same opt-out set as the click path. Without it, hovering a link the
|
|
1641
|
+
// router is not allowed to claim still fired a real GET at it \u2014 a
|
|
1642
|
+
// logout or OAuth URL was requested on hover alone.
|
|
1643
|
+
if(isRouterExcluded(link))return;
|
|
1644
|
+
var href=withCurrentLocale(link.getAttribute('href'));
|
|
1645
|
+
var key=cacheKey(href);
|
|
1646
|
+
if(cache[key]||prefetching[key])return;
|
|
1647
|
+
prefetching[key]=true;
|
|
1648
|
+
var wantsFragment=shouldUseFragmentResponse();
|
|
1649
|
+
fetch(href,{headers:wantsFragment?{'X-STX-Router':'true','Accept':'text/html'}:{'Accept':'text/html'}}).then(function(r){
|
|
1650
|
+
return readPrefetchResponse(r,wantsFragment);
|
|
1651
|
+
}).then(function(result){
|
|
1652
|
+
if(result&&o.cache)setCache(key,result.html,result.layout,result.layoutGroup,result.title,result.containerAttrs);
|
|
1653
|
+
}).catch(function(){}).finally(function(){delete prefetching[key]});
|
|
1654
|
+
},true);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
// \u2500\u2500 Active link management \u2500\u2500
|
|
1658
|
+
function updateNav(url){
|
|
1659
|
+
document.querySelectorAll('nav a[href], #mobileNav a[href], [data-stx-nav] a[href]').forEach(function(a){
|
|
1660
|
+
var href=a.getAttribute('href');
|
|
1661
|
+
if(!href||href.startsWith('#')||href.startsWith('http'))return;
|
|
1662
|
+
var isActive=(href===url)||(href==='/'&&url==='/');
|
|
1663
|
+
if(a.hasAttribute('data-stx-link')){
|
|
1664
|
+
var ac=a.getAttribute('data-stx-active-class')||'active';
|
|
1665
|
+
if(isActive)ac.split(' ').forEach(function(cls){if(cls)a.classList.add(cls)});else ac.split(' ').forEach(function(cls){if(cls)a.classList.remove(cls)});
|
|
1666
|
+
}
|
|
1667
|
+
});
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
function updateActiveLinks(){
|
|
1671
|
+
// Update active classes on <stx-link> elements (and legacy data-stx-link)
|
|
1672
|
+
var links=document.querySelectorAll('[data-stx-link]');
|
|
1673
|
+
var cur=location.pathname;
|
|
1674
|
+
links.forEach(function(link){
|
|
1675
|
+
var href=link.getAttribute('to')||link.getAttribute('href')||'';
|
|
1676
|
+
var ac=link.getAttribute('active-class')||link.getAttribute('data-stx-active-class')||'active';
|
|
1677
|
+
var eac=link.getAttribute('exact-active-class')||link.getAttribute('data-stx-exact-active-class')||'exact-active';
|
|
1678
|
+
ac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});
|
|
1679
|
+
eac.split(' ').forEach(function(cls){if(cls)link.classList.remove(cls)});
|
|
1680
|
+
var isExact=cur===href;
|
|
1681
|
+
var isActive=href!=='/'?cur.startsWith(href):cur==='/';
|
|
1682
|
+
if(isExact)eac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});
|
|
1683
|
+
if(isActive)ac.split(' ').forEach(function(cls){if(cls)link.classList.add(cls)});
|
|
1684
|
+
markCurrent(link,isExact);
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// aria-current="page" names the page the user is ON, so it has to move when
|
|
1689
|
+
// the page does. The server stamps it on the link for the page it rendered;
|
|
1690
|
+
// after that, only a full load would ever correct it, and a fragment swap is
|
|
1691
|
+
// not a full load. Left alone, a screen reader announces the entry the user
|
|
1692
|
+
// arrived through as current for the rest of the session, however far they
|
|
1693
|
+
// navigate \u2014 silently wrong, and only to the people relying on it.
|
|
1694
|
+
//
|
|
1695
|
+
// Only ever touched on links that already carry the attribute or are the
|
|
1696
|
+
// current one, so a page using aria-current for something else \u2014 a step in a
|
|
1697
|
+
// wizard, a sort direction \u2014 is not rewritten out from under itself.
|
|
1698
|
+
function markCurrent(link,isExact){
|
|
1699
|
+
if(isExact){
|
|
1700
|
+
if(link.getAttribute('aria-current')!=='page')link.setAttribute('aria-current','page');
|
|
1701
|
+
return;
|
|
1702
|
+
}
|
|
1703
|
+
if(link.getAttribute('aria-current')==='page')link.removeAttribute('aria-current');
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
// \u2500\u2500 Progress bar DOM + style \u2500\u2500
|
|
1707
|
+
// Injects a fixed-position element at the top of the viewport plus the
|
|
1708
|
+
// minimal CSS for its transform/opacity transitions. Kept idempotent so
|
|
1709
|
+
// repeated init() calls (e.g. after full-body swaps) don't duplicate.
|
|
1710
|
+
// Also ships the default View Transitions fade+slide CSS when
|
|
1711
|
+
// viewTransitions is enabled and the browser supports it. Apps can
|
|
1712
|
+
// override by defining more specific ::view-transition-* rules later in
|
|
1713
|
+
// the cascade, or opt out entirely with viewTransitions:false.
|
|
1714
|
+
function injectStyles(){
|
|
1715
|
+
if(!document.getElementById('stx-r-css')){
|
|
1716
|
+
var s=document.createElement('style');s.id='stx-r-css';
|
|
1717
|
+
// Strip characters that could escape our CSS block and inject new
|
|
1718
|
+
// declarations: ; { } ( ) " ' \\ < > plus whitespace control chars.
|
|
1719
|
+
// This keeps the value safe to concat into a stylesheet even if a
|
|
1720
|
+
// caller wires the config from an untrusted source.
|
|
1721
|
+
var sanitize=function(v){return String(v).replace(/[;{}()"'\\\\<>\\n\\r\\t]/g,'')};
|
|
1722
|
+
var pc=sanitize(o.progressColor);
|
|
1723
|
+
var ph=sanitize(o.progressHeight);
|
|
1724
|
+
var css='.stx-navigating{cursor:wait}.stx-navigating a,.stx-navigating button{pointer-events:none}#stx-router-progress{position:fixed;top:0;left:0;right:0;height:'+ph+';background:'+pc+';box-shadow:0 0 8px '+pc+',0 0 4px '+pc+';transform:scaleX(0);transform-origin:left;transition:transform .18s ease-out,opacity .26s ease;opacity:0;pointer-events:none;z-index:999999}';
|
|
1725
|
+
if(o.viewTransitions&&'startViewTransition' in document){
|
|
1726
|
+
var dur=(o.viewTransitionDuration||220)+'ms';
|
|
1727
|
+
var ease=o.viewTransitionEasing||'cubic-bezier(0.16, 1, 0.3, 1)';
|
|
1728
|
+
css+='::view-transition-old(root),::view-transition-new(root){animation-duration:'+dur+';animation-timing-function:'+ease+'}';
|
|
1729
|
+
css+='::view-transition-old(root){animation-name:stx-r-fade-out}::view-transition-new(root){animation-name:stx-r-fade-in}';
|
|
1730
|
+
css+='@keyframes stx-r-fade-out{from{opacity:1;transform:translateY(0)}to{opacity:0;transform:translateY(-4px)}}';
|
|
1731
|
+
css+='@keyframes stx-r-fade-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}';
|
|
1732
|
+
css+='@media (prefers-reduced-motion: reduce){::view-transition-old(root),::view-transition-new(root){animation-duration:0s;animation-name:none}}';
|
|
1733
|
+
}
|
|
1734
|
+
s.textContent=css;
|
|
1735
|
+
document.head.appendChild(s);
|
|
1736
|
+
}
|
|
1737
|
+
if(o.progress&&!document.getElementById('stx-router-progress')){
|
|
1738
|
+
var el=document.createElement('div');
|
|
1739
|
+
el.id='stx-router-progress';
|
|
1740
|
+
el.setAttribute('role','progressbar');
|
|
1741
|
+
el.setAttribute('aria-hidden','true');
|
|
1742
|
+
// Append to <html>, not <body>: when the router swaps the body
|
|
1743
|
+
// (container:'body' for full-page static-site SPAs) the progress
|
|
1744
|
+
// element gets wiped along with it. <html> stays put across
|
|
1745
|
+
// swaps, so the bar survives multi-hop navigation.
|
|
1746
|
+
document.documentElement.appendChild(el);
|
|
1747
|
+
progEl=el;
|
|
1748
|
+
} else if(o.progress){
|
|
1749
|
+
progEl=document.getElementById('stx-router-progress');
|
|
1750
|
+
}
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
function injectViewTransitionCSS(){
|
|
1754
|
+
if(document.getElementById('stx-view-transitions'))return;
|
|
1755
|
+
var s=document.createElement('style');s.id='stx-view-transitions';
|
|
1756
|
+
s.textContent='::view-transition-group(root){animation:none}::view-transition-old(root){animation:none}::view-transition-new(root){animation:none}main,#app-content,[data-stx-content]{view-transition-name:stx-content}::view-transition-old(stx-content){animation:stx-fade-out .15s ease-out both}::view-transition-new(stx-content){animation:stx-fade-in .15s ease-in .1s both}@keyframes stx-fade-out{from{opacity:1}to{opacity:0}}@keyframes stx-fade-in{from{opacity:0}to{opacity:1}}::view-transition{background:transparent}::view-transition-group(stx-content){background:inherit;overflow:hidden}';
|
|
1757
|
+
(document.head||document.documentElement).appendChild(s);
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// \u2500\u2500 Public API \u2500\u2500
|
|
1761
|
+
var router={
|
|
1762
|
+
navigate:navigate,
|
|
1763
|
+
navigateTo:navigate,
|
|
1764
|
+
prefetch:function(url){
|
|
1765
|
+
var key=cacheKey(url);
|
|
1766
|
+
if(!cache[key]){
|
|
1767
|
+
var wantsFragment=shouldUseFragmentResponse();
|
|
1768
|
+
fetch(url,{headers:wantsFragment?{'X-STX-Router':'true'}:{'Accept':'text/html'}}).then(function(r){return readPrefetchResponse(r,wantsFragment)}).then(function(result){if(result)setCache(key,result.html,result.layout,result.layoutGroup,result.title,result.containerAttrs)}).catch(function(){});
|
|
1769
|
+
}
|
|
1770
|
+
},
|
|
1771
|
+
// Re-run the CURRENT route against the server and swap the result.
|
|
1772
|
+
//
|
|
1773
|
+
// There was no way to do this. invalidate() on a query only re-runs client
|
|
1774
|
+
// fetches, so anything the server rendered \u2014 a list the mutation just
|
|
1775
|
+
// changed, a count in the layout \u2014 could only be refreshed with a full
|
|
1776
|
+
// document load, which discards every signal on the page. That is the
|
|
1777
|
+
// exact thing the SPA exists to avoid, so apps reached for
|
|
1778
|
+
// location.reload() and lost their state (#1850, #1858).
|
|
1779
|
+
//
|
|
1780
|
+
// force:true both evicts the cache entry and defeats navigate's
|
|
1781
|
+
// same-URL early return, so this is exposing behaviour the router already
|
|
1782
|
+
// had rather than adding a second code path. 'replace' because a refresh
|
|
1783
|
+
// is not a new place \u2014 it must not add a history entry you can go Back to.
|
|
1784
|
+
refresh:function(){
|
|
1785
|
+
return navigate(location.pathname+location.search+location.hash,'replace',true);
|
|
1786
|
+
},
|
|
1787
|
+
// Expire ONE entry, so a mutation can invalidate just the page it affected
|
|
1788
|
+
// and let the next visit re-fetch, instead of throwing the whole cache away.
|
|
1789
|
+
invalidate:function(url){
|
|
1790
|
+
evictCache(cacheKey(url||(location.pathname+location.search)));
|
|
1791
|
+
},
|
|
1792
|
+
clearCache:function(){for(var k in cache)delete cache[k];for(var lk in layoutCache)delete layoutCache[lk];for(var gk in layoutGroupCache)delete layoutGroupCache[gk];cacheOrder.length=0},
|
|
1793
|
+
cache:cache,
|
|
1794
|
+
swap:swap,
|
|
1795
|
+
updateNav:updateNav
|
|
1796
|
+
};
|
|
1797
|
+
|
|
1798
|
+
router.__rev=ROUTER_REV;
|
|
1799
|
+
window.__stxRouter=router;
|
|
1800
|
+
window.stxRouter=router;
|
|
1801
|
+
if(window.stx)window.stx.router=router;
|
|
1802
|
+
|
|
1803
|
+
// \u2500\u2500 Initialize \u2500\u2500
|
|
1804
|
+
function init(){
|
|
1805
|
+
injectStyles();
|
|
1806
|
+
injectViewTransitionCSS();
|
|
1807
|
+
updateActiveLinks();
|
|
1808
|
+
updateNav(location.pathname);
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
if(document.readyState==='loading')document.addEventListener('DOMContentLoaded',init);else init();
|
|
1812
|
+
})();
|
|
1813
|
+
`),L}var L;var be=()=>{};var Ce={};Ae(Ce,{setAppUrl:()=>Z,runMiddleware:()=>ge,route:()=>te,resolveLayoutChain:()=>A,resetRoutes:()=>re,registerMiddleware:()=>R,patternToRegex:()=>v,navigateTo:()=>se,matchRoute:()=>D,loadMiddlewareFromDirectory:()=>de,isNonPageRoutePattern:()=>m,isNonPageRoutePath:()=>_,hasMiddleware:()=>ce,getRouterScript:()=>we,getMiddlewareNames:()=>he,getMiddleware:()=>j,generateRouteTypes:()=>x,generateRouteManifest:()=>S,formatRoutes:()=>Y,findErrorPage:()=>$,filePathToPattern:()=>k,extractLayoutMetadata:()=>ye,deriveLayoutGroup:()=>q,defineRoutes:()=>ee,defineRoute:()=>Q,defineMiddleware:()=>le,createStorageManager:()=>X,createServerCookieManager:()=>U,createRouter:()=>z,createRouteLocation:()=>pe,createMiddlewareContext:()=>fe,createClientCookieManager:()=>G,clearMiddleware:()=>ue,bracketPathToRegex:()=>W,abortNavigation:()=>ie,Router:()=>C,NON_PAGE_DIRS:()=>g});var Ee=u(()=>{J();M();ae();me();N();ve();F();b();be()});
|
|
1814
|
+
export{g as lu,_ as mu,m as nu,x as ou,S as pu,k as qu,v as ru,D as su,W as tu,A as uu,C as vu,z as wu,$ as xu,Y as yu,Z as zu,Q as Au,ee as Bu,te as Cu,re as Du,se as Eu,ie as Fu,le as Gu,U as Hu,G as Iu,X as Ju,R as Ku,j as Lu,ce as Mu,ue as Nu,he as Ou,de as Pu,pe as Qu,fe as Ru,ge as Su,q as Tu,ye as Uu,ve as Vu,we as Wu,Ce as Xu,Ee as Yu};
|