@ogygia/core 0.0.0 → 0.11.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 +21 -0
- package/README.md +7 -1
- package/ambient.d.ts +194 -0
- package/dist/ClientBindingStub.svelte +6 -0
- package/dist/ClientBindingStub.svelte.d.ts +26 -0
- package/dist/KeepHost.svelte +19 -0
- package/dist/KeepHost.svelte.d.ts +10 -0
- package/dist/LakeBoundary.svelte +16 -0
- package/dist/LakeBoundary.svelte.d.ts +11 -0
- package/dist/LateIsland.svelte +22 -0
- package/dist/LateIsland.svelte.d.ts +15 -0
- package/dist/LateIslandAwait.svelte +17 -0
- package/dist/LateIslandAwait.svelte.d.ts +15 -0
- package/dist/LiveHost.svelte +19 -0
- package/dist/LiveHost.svelte.d.ts +10 -0
- package/dist/NestedProvider.svelte +13 -0
- package/dist/NestedProvider.svelte.d.ts +8 -0
- package/dist/OgygiaBoundary.svelte +5 -0
- package/dist/OgygiaBoundary.svelte.d.ts +17 -0
- package/dist/PromiseRegion.svelte +40 -0
- package/dist/PromiseRegion.svelte.d.ts +15 -0
- package/dist/PromiseRegionAwait.svelte +27 -0
- package/dist/PromiseRegionAwait.svelte.d.ts +15 -0
- package/dist/Provide.svelte +27 -0
- package/dist/Provide.svelte.d.ts +8 -0
- package/dist/RawHtml.svelte +9 -0
- package/dist/RawHtml.svelte.d.ts +6 -0
- package/dist/Region.svelte +1173 -0
- package/dist/Region.svelte.d.ts +68 -0
- package/dist/SlotBoundary.svelte +13 -0
- package/dist/SlotBoundary.svelte.d.ts +7 -0
- package/dist/app.d.ts +4 -0
- package/dist/app.js +3 -0
- package/dist/boundary.d.ts +35 -0
- package/dist/boundary.js +127 -0
- package/dist/build-cache.d.ts +13 -0
- package/dist/build-cache.js +77 -0
- package/dist/compiler/blank-styles.d.ts +1 -0
- package/dist/compiler/blank-styles.js +63 -0
- package/dist/compiler/bom.d.ts +2 -0
- package/dist/compiler/bom.js +15 -0
- package/dist/compiler/browser-full.d.ts +8 -0
- package/dist/compiler/browser-full.js +8 -0
- package/dist/compiler/browser.d.ts +11 -0
- package/dist/compiler/browser.js +11 -0
- package/dist/compiler/content/transportables.d.ts +26 -0
- package/dist/compiler/content/transportables.js +219 -0
- package/dist/compiler/ctx.d.ts +180 -0
- package/dist/compiler/ctx.js +183 -0
- package/dist/compiler/debarrel/barrel.d.ts +55 -0
- package/dist/compiler/debarrel/barrel.js +248 -0
- package/dist/compiler/debarrel/options.d.ts +67 -0
- package/dist/compiler/debarrel/options.js +31 -0
- package/dist/compiler/debarrel/parse.d.ts +65 -0
- package/dist/compiler/debarrel/parse.js +256 -0
- package/dist/compiler/debarrel/plugin.d.ts +31 -0
- package/dist/compiler/debarrel/plugin.js +190 -0
- package/dist/compiler/debarrel/rewrite.d.ts +27 -0
- package/dist/compiler/debarrel/rewrite.js +118 -0
- package/dist/compiler/debarrel/svelte.d.ts +13 -0
- package/dist/compiler/debarrel/svelte.js +56 -0
- package/dist/compiler/dev/css-scope.d.ts +16 -0
- package/dist/compiler/dev/css-scope.js +38 -0
- package/dist/compiler/dev/dev-hmr.d.ts +25 -0
- package/dist/compiler/dev/dev-hmr.js +29 -0
- package/dist/compiler/dev/hmr.d.ts +34 -0
- package/dist/compiler/dev/hmr.js +57 -0
- package/dist/compiler/dev/region-bytes.d.ts +34 -0
- package/dist/compiler/dev/region-bytes.js +126 -0
- package/dist/compiler/driver.d.ts +280 -0
- package/dist/compiler/driver.js +1352 -0
- package/dist/compiler/flags.d.ts +16 -0
- package/dist/compiler/flags.js +101 -0
- package/dist/compiler/fouc-css.d.ts +97 -0
- package/dist/compiler/fouc-css.js +335 -0
- package/dist/compiler/free-vars.d.ts +20 -0
- package/dist/compiler/free-vars.js +327 -0
- package/dist/compiler/host.d.ts +65 -0
- package/dist/compiler/host.js +51 -0
- package/dist/compiler/ids.d.ts +84 -0
- package/dist/compiler/ids.js +86 -0
- package/dist/compiler/index.d.ts +11 -0
- package/dist/compiler/index.js +11 -0
- package/dist/compiler/kit.d.ts +129 -0
- package/dist/compiler/kit.js +563 -0
- package/dist/compiler/link/build-output.d.ts +44 -0
- package/dist/compiler/link/build-output.js +147 -0
- package/dist/compiler/link/caps.d.ts +48 -0
- package/dist/compiler/link/caps.js +64 -0
- package/dist/compiler/link/context-detect.d.ts +23 -0
- package/dist/compiler/link/context-detect.js +137 -0
- package/dist/compiler/link/emit-gate.d.ts +11 -0
- package/dist/compiler/link/emit-gate.js +40 -0
- package/dist/compiler/link/entry-shim.d.ts +4 -0
- package/dist/compiler/link/entry-shim.js +29 -0
- package/dist/compiler/link/island-deps.d.ts +142 -0
- package/dist/compiler/link/island-deps.js +525 -0
- package/dist/compiler/link/manifest.d.ts +5 -0
- package/dist/compiler/link/manifest.js +9 -0
- package/dist/compiler/link/page-keys.d.ts +64 -0
- package/dist/compiler/link/page-keys.js +683 -0
- package/dist/compiler/link/registry-stub.d.ts +41 -0
- package/dist/compiler/link/registry-stub.js +212 -0
- package/dist/compiler/link/router-config.d.ts +11 -0
- package/dist/compiler/link/router-config.js +15 -0
- package/dist/compiler/link/router-css.d.ts +22 -0
- package/dist/compiler/link/router-css.js +142 -0
- package/dist/compiler/link/runtime-entry.d.ts +71 -0
- package/dist/compiler/link/runtime-entry.js +132 -0
- package/dist/compiler/link/server-manifest.d.ts +12 -0
- package/dist/compiler/link/server-manifest.js +45 -0
- package/dist/compiler/link/speculation.d.ts +17 -0
- package/dist/compiler/link/speculation.js +36 -0
- package/dist/compiler/link/transport.d.ts +31 -0
- package/dist/compiler/link/transport.js +67 -0
- package/dist/compiler/macros/bake.d.ts +20 -0
- package/dist/compiler/macros/bake.js +317 -0
- package/dist/compiler/macros/browser.d.ts +11 -0
- package/dist/compiler/macros/browser.js +69 -0
- package/dist/compiler/macros/dedent.d.ts +12 -0
- package/dist/compiler/macros/dedent.js +36 -0
- package/dist/compiler/macros/dollar.d.ts +15 -0
- package/dist/compiler/macros/dollar.js +387 -0
- package/dist/compiler/macros/pipeline.d.ts +31 -0
- package/dist/compiler/macros/pipeline.js +106 -0
- package/dist/compiler/macros/source.d.ts +6 -0
- package/dist/compiler/macros/source.js +127 -0
- package/dist/compiler/macros/store.d.ts +17 -0
- package/dist/compiler/macros/store.js +244 -0
- package/dist/compiler/macros/wire.d.ts +14 -0
- package/dist/compiler/macros/wire.js +160 -0
- package/dist/compiler/ownership-stamps.d.ts +6 -0
- package/dist/compiler/ownership-stamps.js +144 -0
- package/dist/compiler/parse/oxc.d.ts +34 -0
- package/dist/compiler/parse/oxc.js +59 -0
- package/dist/compiler/parse/scan.d.ts +43 -0
- package/dist/compiler/parse/scan.js +232 -0
- package/dist/compiler/program.d.ts +140 -0
- package/dist/compiler/program.js +242 -0
- package/dist/compiler/region/emit.d.ts +169 -0
- package/dist/compiler/region/emit.js +286 -0
- package/dist/compiler/region/identity.d.ts +28 -0
- package/dist/compiler/region/identity.js +75 -0
- package/dist/compiler/region/ir.d.ts +65 -0
- package/dist/compiler/region/ir.js +0 -0
- package/dist/compiler/region/transform.d.ts +178 -0
- package/dist/compiler/region/transform.js +1933 -0
- package/dist/compiler/script-request.d.ts +1 -0
- package/dist/compiler/script-request.js +43 -0
- package/dist/context-bridge.d.ts +65 -0
- package/dist/context-bridge.js +217 -0
- package/dist/context-registry.d.ts +40 -0
- package/dist/context-registry.js +15 -0
- package/dist/context.d.ts +72 -0
- package/dist/context.js +158 -0
- package/dist/current-region.d.ts +14 -0
- package/dist/current-region.js +36 -0
- package/dist/document.d.ts +41 -0
- package/dist/document.js +125 -0
- package/dist/entry-hydrate.d.ts +5 -0
- package/dist/entry-hydrate.js +42 -0
- package/dist/escape.d.ts +18 -0
- package/dist/escape.js +37 -0
- package/dist/events/bus.d.ts +33 -0
- package/dist/events/bus.js +153 -0
- package/dist/events/index.d.ts +4 -0
- package/dist/events/index.js +4 -0
- package/dist/events/schema.d.ts +389 -0
- package/dist/events/schema.js +23 -0
- package/dist/events/server-registry.d.ts +7 -0
- package/dist/events/server-registry.js +13 -0
- package/dist/events/sinks.d.ts +64 -0
- package/dist/events/sinks.js +85 -0
- package/dist/fn-transport.d.ts +33 -0
- package/dist/fn-transport.js +140 -0
- package/dist/frame.d.ts +20 -0
- package/dist/frame.js +19 -0
- package/dist/holes-record.d.ts +6 -0
- package/dist/holes-record.js +8 -0
- package/dist/hooks.d.ts +43 -0
- package/dist/hooks.js +1191 -0
- package/dist/hydration-info.d.ts +1 -0
- package/dist/hydration-info.js +31 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +17 -0
- package/dist/insights/attr-rules.d.ts +15 -0
- package/dist/insights/attr-rules.js +27 -0
- package/dist/insights/cpu.d.ts +82 -0
- package/dist/insights/cpu.js +177 -0
- package/dist/insights/fp-drift.d.ts +28 -0
- package/dist/insights/fp-drift.js +148 -0
- package/dist/insights/hole-segments.d.ts +26 -0
- package/dist/insights/hole-segments.js +50 -0
- package/dist/insights/html-diff.d.ts +25 -0
- package/dist/insights/html-diff.js +157 -0
- package/dist/insights/hydration-hazards.d.ts +36 -0
- package/dist/insights/hydration-hazards.js +136 -0
- package/dist/insights/hydration-status.d.ts +2 -0
- package/dist/insights/hydration-status.js +0 -0
- package/dist/insights/index.d.ts +14 -0
- package/dist/insights/index.js +12 -0
- package/dist/insights/page-assets.d.ts +211 -0
- package/dist/insights/page-assets.js +608 -0
- package/dist/insights/page-insights.d.ts +749 -0
- package/dist/insights/page-insights.js +2157 -0
- package/dist/insights/session-data.d.ts +108 -0
- package/dist/insights/session-data.js +0 -0
- package/dist/insights/session-insights.d.ts +121 -0
- package/dist/insights/session-insights.js +548 -0
- package/dist/insights/switch.d.ts +53 -0
- package/dist/insights/switch.js +91 -0
- package/dist/insights/testing-version.d.ts +19 -0
- package/dist/insights/testing-version.js +21 -0
- package/dist/insights/third-party.d.ts +75 -0
- package/dist/insights/third-party.js +160 -0
- package/dist/internal-instruments.d.ts +13 -0
- package/dist/internal-instruments.js +12 -0
- package/dist/internal-reconcile.d.ts +3 -0
- package/dist/internal-reconcile.js +3 -0
- package/dist/internal-register.d.ts +7 -0
- package/dist/internal-register.js +7 -0
- package/dist/internal-server.d.ts +7 -0
- package/dist/internal-server.js +7 -0
- package/dist/internal.d.ts +13 -0
- package/dist/internal.js +13 -0
- package/dist/island-graph.d.ts +36 -0
- package/dist/island-graph.js +89 -0
- package/dist/keep-fallback-marker.d.ts +7 -0
- package/dist/keep-fallback-marker.js +9 -0
- package/dist/keep-fallback.d.ts +13 -0
- package/dist/keep-fallback.js +39 -0
- package/dist/kit-paths.d.ts +2 -0
- package/dist/kit-paths.js +39 -0
- package/dist/late-region-registry.d.ts +40 -0
- package/dist/late-region-registry.js +36 -0
- package/dist/live-transport.d.ts +57 -0
- package/dist/live-transport.js +145 -0
- package/dist/markup.d.ts +4 -0
- package/dist/markup.js +4 -0
- package/dist/own-package.d.ts +15 -0
- package/dist/own-package.js +20 -0
- package/dist/page-defer.d.ts +33 -0
- package/dist/page-defer.js +35 -0
- package/dist/page-facts-script.d.ts +7 -0
- package/dist/page-facts-script.js +9 -0
- package/dist/page-seed-registry.d.ts +72 -0
- package/dist/page-seed-registry.js +31 -0
- package/dist/plugin/capability.d.ts +14 -0
- package/dist/plugin/capability.js +44 -0
- package/dist/plugin/client.d.ts +39 -0
- package/dist/plugin/client.js +6 -0
- package/dist/plugin/css.d.ts +3 -0
- package/dist/plugin/css.js +54 -0
- package/dist/plugin/facts.d.ts +42 -0
- package/dist/plugin/facts.js +37 -0
- package/dist/plugin/hooks.d.ts +33 -0
- package/dist/plugin/hooks.js +38 -0
- package/dist/plugin/index.d.ts +127 -0
- package/dist/plugin/index.js +26 -0
- package/dist/plugin/routes.d.ts +32 -0
- package/dist/plugin/routes.js +35 -0
- package/dist/plugin/server.d.ts +27 -0
- package/dist/plugin/server.js +92 -0
- package/dist/plugin/standard-schema.d.ts +27 -0
- package/dist/plugin/standard-schema.js +0 -0
- package/dist/plugin/testing.d.ts +13 -0
- package/dist/plugin/testing.js +54 -0
- package/dist/plugin/utils.d.ts +13 -0
- package/dist/plugin/utils.js +19 -0
- package/dist/portable-form.d.ts +22 -0
- package/dist/portable-form.js +52 -0
- package/dist/preference.d.ts +35 -0
- package/dist/preference.js +84 -0
- package/dist/preload.d.ts +1 -0
- package/dist/preload.js +21 -0
- package/dist/ref-watch.d.ts +31 -0
- package/dist/ref-watch.js +66 -0
- package/dist/ref.d.ts +124 -0
- package/dist/ref.js +272 -0
- package/dist/region-brand.d.ts +8 -0
- package/dist/region-brand.js +10 -0
- package/dist/region-core.d.ts +2 -0
- package/dist/region-core.js +1 -0
- package/dist/region-snippet.d.ts +73 -0
- package/dist/region-snippet.js +351 -0
- package/dist/region.d.ts +127 -0
- package/dist/region.js +162 -0
- package/dist/request-event.d.ts +1 -0
- package/dist/request-event.js +37 -0
- package/dist/router-css.d.ts +39 -0
- package/dist/router-css.js +66 -0
- package/dist/runtime/app-transport.d.ts +2 -0
- package/dist/runtime/app-transport.js +13 -0
- package/dist/runtime/beacon.d.ts +271 -0
- package/dist/runtime/beacon.js +2190 -0
- package/dist/runtime/boot-link.d.ts +1 -0
- package/dist/runtime/boot-link.js +44 -0
- package/dist/runtime/client-hooks.d.ts +5 -0
- package/dist/runtime/client-hooks.js +22 -0
- package/dist/runtime/client-plugins.d.ts +8 -0
- package/dist/runtime/client-plugins.js +34 -0
- package/dist/runtime/concurrency.d.ts +12 -0
- package/dist/runtime/concurrency.js +29 -0
- package/dist/runtime/connected.d.ts +12 -0
- package/dist/runtime/connected.js +16 -0
- package/dist/runtime/context.d.ts +1 -0
- package/dist/runtime/context.js +19 -0
- package/dist/runtime/core.d.ts +15 -0
- package/dist/runtime/core.js +1173 -0
- package/dist/runtime/drift-watch.d.ts +9 -0
- package/dist/runtime/drift-watch.js +60 -0
- package/dist/runtime/entry-locations.d.ts +33 -0
- package/dist/runtime/entry-locations.js +119 -0
- package/dist/runtime/foreign-props.d.ts +8 -0
- package/dist/runtime/foreign-props.js +14 -0
- package/dist/runtime/form-continuity.d.ts +10 -0
- package/dist/runtime/form-continuity.js +107 -0
- package/dist/runtime/frame-nav.d.ts +36 -0
- package/dist/runtime/frame-nav.js +225 -0
- package/dist/runtime/frame-store.d.ts +62 -0
- package/dist/runtime/frame-store.js +214 -0
- package/dist/runtime/frames.d.ts +1 -0
- package/dist/runtime/frames.js +25 -0
- package/dist/runtime/full.d.ts +4 -0
- package/dist/runtime/full.js +32 -0
- package/dist/runtime/hash.d.ts +41 -0
- package/dist/runtime/hash.js +79 -0
- package/dist/runtime/hole-facts.d.ts +8 -0
- package/dist/runtime/hole-facts.js +57 -0
- package/dist/runtime/host-classes.d.ts +24 -0
- package/dist/runtime/host-classes.js +43 -0
- package/dist/runtime/hydrate-core.d.ts +101 -0
- package/dist/runtime/hydrate-core.js +605 -0
- package/dist/runtime/index.d.ts +2 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/interaction-replay.d.ts +11 -0
- package/dist/runtime/interaction-replay.js +70 -0
- package/dist/runtime/interaction.d.ts +33 -0
- package/dist/runtime/interaction.js +217 -0
- package/dist/runtime/island-graph-preload.d.ts +13 -0
- package/dist/runtime/island-graph-preload.js +99 -0
- package/dist/runtime/kit-boot.d.ts +29 -0
- package/dist/runtime/kit-boot.js +60 -0
- package/dist/runtime/kit-page-thread.svelte.d.ts +1 -0
- package/dist/runtime/kit-page-thread.svelte.js +116 -0
- package/dist/runtime/lake-anchors.d.ts +13 -0
- package/dist/runtime/lake-anchors.js +19 -0
- package/dist/runtime/lakes.d.ts +15 -0
- package/dist/runtime/lakes.js +212 -0
- package/dist/runtime/live.d.ts +3 -0
- package/dist/runtime/live.js +17 -0
- package/dist/runtime/load-scheduler.d.ts +53 -0
- package/dist/runtime/load-scheduler.js +317 -0
- package/dist/runtime/morph.d.ts +42 -0
- package/dist/runtime/morph.js +671 -0
- package/dist/runtime/nav-handle.d.ts +25 -0
- package/dist/runtime/nav-handle.js +71 -0
- package/dist/runtime/nav-hooks.d.ts +2 -0
- package/dist/runtime/nav-hooks.js +23 -0
- package/dist/runtime/nearest-attr.d.ts +4 -0
- package/dist/runtime/nearest-attr.js +16 -0
- package/dist/runtime/observe.d.ts +27 -0
- package/dist/runtime/observe.js +63 -0
- package/dist/runtime/own-cost.d.ts +9 -0
- package/dist/runtime/own-cost.js +35 -0
- package/dist/runtime/ownership.d.ts +101 -0
- package/dist/runtime/ownership.js +115 -0
- package/dist/runtime/page-cache.d.ts +23 -0
- package/dist/runtime/page-cache.js +74 -0
- package/dist/runtime/page-defer.d.ts +14 -0
- package/dist/runtime/page-defer.js +103 -0
- package/dist/runtime/parse-html.d.ts +40 -0
- package/dist/runtime/parse-html.js +81 -0
- package/dist/runtime/reconcile.d.ts +41 -0
- package/dist/runtime/reconcile.js +194 -0
- package/dist/runtime/region-attrs.d.ts +95 -0
- package/dist/runtime/region-attrs.js +153 -0
- package/dist/runtime/region-endpoint-url.d.ts +76 -0
- package/dist/runtime/region-endpoint-url.js +169 -0
- package/dist/runtime/region-ids.d.ts +8 -0
- package/dist/runtime/region-ids.js +33 -0
- package/dist/runtime/remote-seeds.d.ts +2 -0
- package/dist/runtime/remote-seeds.js +21 -0
- package/dist/runtime/render-blocking.d.ts +2 -0
- package/dist/runtime/render-blocking.js +30 -0
- package/dist/runtime/restore.d.ts +34 -0
- package/dist/runtime/restore.js +390 -0
- package/dist/runtime/router-nav.d.ts +63 -0
- package/dist/runtime/router-nav.js +740 -0
- package/dist/runtime/router.d.ts +201 -0
- package/dist/runtime/router.js +593 -0
- package/dist/runtime/schedule.d.ts +49 -0
- package/dist/runtime/schedule.js +256 -0
- package/dist/runtime/seeds.d.ts +50 -0
- package/dist/runtime/seeds.js +208 -0
- package/dist/runtime/session.d.ts +52 -0
- package/dist/runtime/session.js +130 -0
- package/dist/runtime/sidecar.d.ts +3 -0
- package/dist/runtime/sidecar.js +56 -0
- package/dist/runtime/slots.d.ts +183 -0
- package/dist/runtime/slots.js +44 -0
- package/dist/runtime/spa-cacheable.d.ts +7 -0
- package/dist/runtime/spa-cacheable.js +18 -0
- package/dist/runtime/speculate-hint.d.ts +1 -0
- package/dist/runtime/speculate-hint.js +35 -0
- package/dist/runtime/svelte-warnings.d.ts +28 -0
- package/dist/runtime/svelte-warnings.js +71 -0
- package/dist/runtime/wire-classes.d.ts +3 -0
- package/dist/runtime/wire-classes.js +69 -0
- package/dist/runtime/wire-format.d.ts +7 -0
- package/dist/runtime/wire-format.js +22 -0
- package/dist/script.d.ts +134 -0
- package/dist/script.js +184 -0
- package/dist/seed-refs.d.ts +105 -0
- package/dist/seed-refs.js +487 -0
- package/dist/server/capability-lifetime.d.ts +28 -0
- package/dist/server/capability-lifetime.js +31 -0
- package/dist/server/dead-kit-resolves.d.ts +1 -0
- package/dist/server/dead-kit-resolves.js +39 -0
- package/dist/server/document-assembly.d.ts +30 -0
- package/dist/server/document-assembly.js +26 -0
- package/dist/server/document-tail.d.ts +113 -0
- package/dist/server/document-tail.js +208 -0
- package/dist/server/endpoint.d.ts +51 -0
- package/dist/server/endpoint.js +90 -0
- package/dist/server/entry-location.d.ts +8 -0
- package/dist/server/entry-location.js +29 -0
- package/dist/server/fingerprint.d.ts +1 -0
- package/dist/server/fingerprint.js +25 -0
- package/dist/server/head-presence.d.ts +45 -0
- package/dist/server/head-presence.js +156 -0
- package/dist/server/hmac.d.ts +38 -0
- package/dist/server/hmac.js +89 -0
- package/dist/server/hole-graph.d.ts +7 -0
- package/dist/server/hole-graph.js +71 -0
- package/dist/server/hole-urls.d.ts +3 -0
- package/dist/server/hole-urls.js +76 -0
- package/dist/server/html-scan.d.ts +19 -0
- package/dist/server/html-scan.js +60 -0
- package/dist/server/kit-context.d.ts +78 -0
- package/dist/server/kit-context.js +110 -0
- package/dist/server/late-wire.d.ts +22 -0
- package/dist/server/late-wire.js +28 -0
- package/dist/server/page-facts.d.ts +25 -0
- package/dist/server/page-facts.js +138 -0
- package/dist/server/page-seed.d.ts +33 -0
- package/dist/server/page-seed.js +60 -0
- package/dist/server/page-stream.d.ts +55 -0
- package/dist/server/page-stream.js +121 -0
- package/dist/server/payload.d.ts +6 -0
- package/dist/server/payload.js +22 -0
- package/dist/server/personal-response.d.ts +6 -0
- package/dist/server/personal-response.js +13 -0
- package/dist/server/props-wire.d.ts +56 -0
- package/dist/server/props-wire.js +178 -0
- package/dist/server/rate-limit.d.ts +22 -0
- package/dist/server/rate-limit.js +63 -0
- package/dist/server/region-css.d.ts +5 -0
- package/dist/server/region-css.js +30 -0
- package/dist/server/region-endpoint.d.ts +35 -0
- package/dist/server/region-endpoint.js +142 -0
- package/dist/server/region-props.d.ts +6 -0
- package/dist/server/region-props.js +44 -0
- package/dist/server/remote-seed-gate.d.ts +50 -0
- package/dist/server/remote-seed-gate.js +66 -0
- package/dist/server/render-cache.d.ts +26 -0
- package/dist/server/render-cache.js +51 -0
- package/dist/server/render-page.d.ts +51 -0
- package/dist/server/render-page.js +176 -0
- package/dist/server/render-region-html.d.ts +2 -0
- package/dist/server/render-region-html.js +19 -0
- package/dist/server/request-stats.d.ts +174 -0
- package/dist/server/request-stats.js +54 -0
- package/dist/server/reversible.d.ts +83 -0
- package/dist/server/reversible.js +994 -0
- package/dist/server/seed-shape.d.ts +22 -0
- package/dist/server/seed-shape.js +24 -0
- package/dist/server/shared-cache.d.ts +35 -0
- package/dist/server/shared-cache.js +104 -0
- package/dist/server/signing-key.d.ts +11 -0
- package/dist/server/signing-key.js +47 -0
- package/dist/server/sized-lru.d.ts +20 -0
- package/dist/server/sized-lru.js +68 -0
- package/dist/server/split-regions.d.ts +81 -0
- package/dist/server/split-regions.js +368 -0
- package/dist/server/stream-regions.d.ts +5 -0
- package/dist/server/stream-regions.js +36 -0
- package/dist/shared-state.d.ts +7 -0
- package/dist/shared-state.js +152 -0
- package/dist/shims/app-environment.d.ts +4 -0
- package/dist/shims/app-environment.js +6 -0
- package/dist/shims/app-navigation.d.ts +33 -0
- package/dist/shims/app-navigation.js +119 -0
- package/dist/shims/app-paths.d.ts +6 -0
- package/dist/shims/app-paths.js +18 -0
- package/dist/shims/app-state.svelte.d.ts +23 -0
- package/dist/shims/app-state.svelte.js +50 -0
- package/dist/shims/app-stores.d.ts +21 -0
- package/dist/shims/app-stores.js +54 -0
- package/dist/shims/kit-bridge.d.ts +29 -0
- package/dist/shims/kit-bridge.js +7 -0
- package/dist/shims/kit-remote/client-stub.d.ts +27 -0
- package/dist/shims/kit-remote/client-stub.js +43 -0
- package/dist/shims/kit-remote/paths-internal-stub.d.ts +3 -0
- package/dist/shims/kit-remote/paths-internal-stub.js +5 -0
- package/dist/shims/kit-remote/remote-cache.d.ts +49 -0
- package/dist/shims/kit-remote/remote-cache.js +73 -0
- package/dist/shims/kit-remote/state-stub.d.ts +8 -0
- package/dist/shims/kit-remote/state-stub.js +8 -0
- package/dist/shims/page-store.svelte.d.ts +39 -0
- package/dist/shims/page-store.svelte.js +77 -0
- package/dist/source.d.ts +29 -0
- package/dist/source.js +87 -0
- package/dist/store-transport.d.ts +86 -0
- package/dist/store-transport.js +229 -0
- package/dist/tool-fetches.d.ts +5 -0
- package/dist/tool-fetches.js +41 -0
- package/dist/transport.d.ts +48 -0
- package/dist/transport.js +181 -0
- package/dist/unscoped-css.d.ts +26 -0
- package/dist/unscoped-css.js +53 -0
- package/dist/vite/dev-maps.d.ts +33 -0
- package/dist/vite/dev-maps.js +140 -0
- package/dist/vite/index.d.ts +19 -0
- package/dist/vite/index.js +820 -0
- package/dist/vite/kit-dirs.d.ts +7 -0
- package/dist/vite/kit-dirs.js +67 -0
- package/dist/vite/late-island.d.ts +25 -0
- package/dist/vite/late-island.js +33 -0
- package/dist/vite/module-map.d.ts +8 -0
- package/dist/vite/module-map.js +144 -0
- package/dist/vite/options.d.ts +208 -0
- package/dist/vite/options.js +74 -0
- package/dist/vite/package-files.d.ts +3 -0
- package/dist/vite/package-files.js +84 -0
- package/dist/vite/paths.d.ts +45 -0
- package/dist/vite/paths.js +112 -0
- package/dist/vite/plugin-api.d.ts +166 -0
- package/dist/vite/plugin-api.js +212 -0
- package/dist/vite/profiler-maps.d.ts +23 -0
- package/dist/vite/profiler-maps.js +178 -0
- package/dist/vite/server-sourcemaps.d.ts +8 -0
- package/dist/vite/server-sourcemaps.js +80 -0
- package/dist/vite/sourcemaps.d.ts +18 -0
- package/dist/vite/sourcemaps.js +80 -0
- package/dist/vite/style-preprocess.d.ts +2 -0
- package/dist/vite/style-preprocess.js +96 -0
- package/package.json +180 -6
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,1191 @@
|
|
|
1
|
+
import { REGION_BRAND } from "./region-brand.js";
|
|
2
|
+
import { signing_key } from "./server/signing-key.js";
|
|
3
|
+
import { mpaSpeculationRules } from "./compiler/link/speculation.js";
|
|
4
|
+
import { set_page_route_config } from "./plugin/routes.js";
|
|
5
|
+
import { kit_page_facts_tail, kit_page_ready, kit_render_context, set_kit_event_reader, set_kit_page_reader, set_render_page_port } from "./server/kit-context.js";
|
|
6
|
+
import { PAGE_FACTS_ROUTE_HEADER } from "./server/page-facts.js";
|
|
7
|
+
import { configure_render_page, note_render_page_ask, page_url_of, prepare_render_page, render_page, render_page_facts_script, render_page_version } from "./server/render-page.js";
|
|
8
|
+
import { absolutize_hole_html } from "./server/hole-urls.js";
|
|
9
|
+
import { is_keep_fallback } from "./keep-fallback.js";
|
|
10
|
+
import { set_source_recorder } from "./source.js";
|
|
11
|
+
import { kit_asset } from "./kit-paths.js";
|
|
12
|
+
import { region_mac_message, sign, verify } from "./server/hmac.js";
|
|
13
|
+
import { expiry_refusal, note_cache_control } from "./server/shared-cache.js";
|
|
14
|
+
import { app_lifetime } from "./server/region-endpoint.js";
|
|
15
|
+
import { cached_render, render_cache_key } from "./server/render-cache.js";
|
|
16
|
+
import { B64Url } from "./server/payload.js";
|
|
17
|
+
import { REF_WIRE_KEY, ref_reviver } from "./ref.js";
|
|
18
|
+
import { current_facts, facts_of, mark_per_visitor, set_current_request } from "./plugin/facts.js";
|
|
19
|
+
import { around, request_contexts, start_plugins } from "./plugin/server.js";
|
|
20
|
+
import { register_wire_kind } from "./live-transport.js";
|
|
21
|
+
import { register_derived_kind, register_store_kind } from "./store-transport.js";
|
|
22
|
+
import { register_snippet_kind } from "./region-snippet.js";
|
|
23
|
+
import { register_fn_kind } from "./fn-transport.js";
|
|
24
|
+
import { configure_islands_endpoint, is_region_id, is_region_ttl, islands_endpoint } from "./server/endpoint.js";
|
|
25
|
+
import { build_parcel, done_parcel } from "./server/stream-regions.js";
|
|
26
|
+
import { dedupe_head_links, page_declares_dev_hmr_script, page_declares_router_meta, page_declares_runtime_script, page_declares_speculation_rules, runtime_first } from "./server/head-presence.js";
|
|
27
|
+
import { assemble, locate } from "./server/document-assembly.js";
|
|
28
|
+
import { error_route_is_csr_true, route_is_csr_true } from "./context.js";
|
|
29
|
+
import { merge_seed_ask, shape_page_data } from "./server/seed-shape.js";
|
|
30
|
+
import { html_has_kit_bootstrap } from "./runtime/kit-boot.js";
|
|
31
|
+
import { STAND_IN_CSS, check_script, mark, settle } from "./server/reversible.js";
|
|
32
|
+
import { hole_head } from "./server/hole-graph.js";
|
|
33
|
+
import { restore, restore_adopt } from "./runtime/restore.js";
|
|
34
|
+
import { RateLimiter } from "./server/rate-limit.js";
|
|
35
|
+
import { KEEP_FALLBACK_HTML } from "./keep-fallback-marker.js";
|
|
36
|
+
import { PageSeed } from "./server/page-seed.js";
|
|
37
|
+
import { analyze, index_seed, json_culprit, set_measure_memo_reader } from "./seed-refs.js";
|
|
38
|
+
import { WIRE_FORMAT_ATTR, WIRE_FORMAT_JSON } from "./server/props-wire.js";
|
|
39
|
+
import { page_seed_reducers, resolve_script, settle_deferred, stage_deferred } from "./server/page-stream.js";
|
|
40
|
+
import { PAGE_DEFER_BOOTSTRAP, PAGE_DEFER_GLOBAL } from "./page-defer.js";
|
|
41
|
+
import { ConcurrencyGate } from "./runtime/concurrency.js";
|
|
42
|
+
import { serialize_provided_context } from "./context-bridge.js";
|
|
43
|
+
import { escape_script_text } from "./escape.js";
|
|
44
|
+
import { hole_copy_key } from "./runtime/hash.js";
|
|
45
|
+
import { PAGE_CTX_MARKER, set_ctx_recorder } from "./context-registry.js";
|
|
46
|
+
import { set_ask_scope_opener, set_page_recorder, set_seed_ask_reader } from "./page-seed-registry.js";
|
|
47
|
+
import { collect_remote_seed } from "./server/remote-seed-gate.js";
|
|
48
|
+
import { DocumentTail, set_tail_reader } from "./server/document-tail.js";
|
|
49
|
+
import { runtime_bootstrap } from "./server/entry-location.js";
|
|
50
|
+
import { region_css_tag } from "./server/region-css.js";
|
|
51
|
+
import { set_late_recorder, set_late_scope_opener, set_late_taker } from "./late-region-registry.js";
|
|
52
|
+
import { has_batch_hole_listener, record_batch_hole_stats, record_hole_stats, record_request_stats, request_stats_detailed, request_stats_wanted } from "./server/request-stats.js";
|
|
53
|
+
import { set_server_devtools_recorder } from "./events/server-registry.js";
|
|
54
|
+
import { drop_dead_kit_resolves } from "./server/dead-kit-resolves.js";
|
|
55
|
+
import "./events/schema.js";
|
|
56
|
+
import { DEVTOOLS_COOKIE, SWITCH_COOKIE_OPTIONS, switch_in_url } from "./insights/switch.js";
|
|
57
|
+
import { mark_personal } from "./server/personal-response.js";
|
|
58
|
+
import { ogygiaTransport } from "./transport.js";
|
|
59
|
+
import { document } from "./document.js";
|
|
60
|
+
import { liftRegions, restoreRegions, scanRegions } from "./server/split-regions.js";
|
|
61
|
+
import { render } from "svelte/server";
|
|
62
|
+
import * as devalue from "devalue";
|
|
63
|
+
import { stringify } from "devalue";
|
|
64
|
+
import runtime_url from "virtual:ogygia/runtime-url";
|
|
65
|
+
import { fnManifest, islandCss } from "virtual:ogygia/island-deps";
|
|
66
|
+
import { try_get_request_store } from "@sveltejs/kit/internal/server";
|
|
67
|
+
import dev_hmr_url from "virtual:ogygia/dev-hmr-url";
|
|
68
|
+
import { enabled, viewTransitions } from "virtual:ogygia/router-config";
|
|
69
|
+
import { island_name, island_reads_page_data, island_url, load_islands, render_island } from "virtual:ogygia/server-manifest";
|
|
70
|
+
import { create_remote_key } from "virtual:ogygia/kit-wire";
|
|
71
|
+
import { building, dev } from "$app/env";
|
|
72
|
+
import { rateLimit } from "virtual:ogygia/rate-limit";
|
|
73
|
+
import { sessionCookie } from "virtual:ogygia/session-cookie";
|
|
74
|
+
import { route_config } from "virtual:ogygia/route-config";
|
|
75
|
+
import devtools_boot_url from "virtual:ogygia/devtools-boot-url";
|
|
76
|
+
import { plugins } from "virtual:ogygia/server-plugins";
|
|
77
|
+
// src/hooks.ts
|
|
78
|
+
/**
|
|
79
|
+
* SvelteKit server `handle` for signed region holes (`defer` / remount:`swr`).
|
|
80
|
+
*
|
|
81
|
+
* Serves `GET <base>/__ogygia__?id=…&props=…&exp=…&sig=…` by verifying the region MAC,
|
|
82
|
+
* rendering the region component server-side (cookies, remote functions, and `await` work),
|
|
83
|
+
* and returning HTML for the client runtime to swap in.
|
|
84
|
+
*
|
|
85
|
+
* Composable with `sequence()` — intercepts only the region endpoint path; otherwise
|
|
86
|
+
* calls `resolve(event)`. Also injects the document-level page seed used by islands.
|
|
87
|
+
*
|
|
88
|
+
* @example
|
|
89
|
+
* ```ts
|
|
90
|
+
* // src/hooks.server.ts
|
|
91
|
+
* import { ogygiaHandle } from 'ogygia/hooks';
|
|
92
|
+
* import { sequence } from '@sveltejs/kit/hooks';
|
|
93
|
+
* export const handle = sequence(ogygiaHandle(), myOtherHandle);
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @packageDocumentation
|
|
97
|
+
*/
|
|
98
|
+
/** Is devtools on for this request's browser (a devtools BUILD): the URL's `?og-devtools=on|off`
|
|
99
|
+
* wins and is written to the response's cookie, else the cookie (insights/switch.ts). */
|
|
100
|
+
function devtools_switch(event) {
|
|
101
|
+
if (building) return false;
|
|
102
|
+
const want = switch_in_url(event.url);
|
|
103
|
+
if (want === null) return event.cookies.get(DEVTOOLS_COOKIE) === "on";
|
|
104
|
+
if (want) event.cookies.set(DEVTOOLS_COOKIE, "on", SWITCH_COOKIE_OPTIONS);
|
|
105
|
+
else event.cookies.delete(DEVTOOLS_COOKIE, { path: "/" });
|
|
106
|
+
return want;
|
|
107
|
+
}
|
|
108
|
+
/** Hard cap on rendered region HTML (bytes). */
|
|
109
|
+
const MAX_REGION_BODY = 2e6;
|
|
110
|
+
const DEVTOOLS = typeof __OGYGIA_DEVTOOLS__ !== "undefined" ? __OGYGIA_DEVTOOLS__ : false;
|
|
111
|
+
const DEVTOOLS_LAZY = typeof __OGYGIA_DEVTOOLS_LAZY__ !== "undefined" ? __OGYGIA_DEVTOOLS_LAZY__ : false;
|
|
112
|
+
/** High-res clock for server-realm devtools timestamps. */
|
|
113
|
+
const dt_now = () => typeof performance !== "undefined" && performance.now ? performance.now() : Date.now();
|
|
114
|
+
const bags = /* @__PURE__ */ new WeakMap();
|
|
115
|
+
/** This request's bag, or `undefined` outside a Kit page request (a hole endpoint, a remote-function
|
|
116
|
+
* call, a render with no Kit store around it). */
|
|
117
|
+
function bag_of() {
|
|
118
|
+
const request = try_get_request_store()?.event?.request;
|
|
119
|
+
return request ? bags.get(request) : void 0;
|
|
120
|
+
}
|
|
121
|
+
set_ctx_recorder((key, value) => {
|
|
122
|
+
const bag = bag_of();
|
|
123
|
+
if (bag) bag.ctx.set(key, value);
|
|
124
|
+
});
|
|
125
|
+
const doc_asks = /* @__PURE__ */ new WeakMap();
|
|
126
|
+
const request_of = () => try_get_request_store()?.event?.request;
|
|
127
|
+
set_ask_scope_opener(() => {
|
|
128
|
+
const request = request_of();
|
|
129
|
+
if (request && !bags.has(request) && !doc_asks.has(request)) doc_asks.set(request, {
|
|
130
|
+
wanted: false,
|
|
131
|
+
keys: null
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
set_seed_ask_reader(() => {
|
|
135
|
+
const bag = bag_of();
|
|
136
|
+
if (bag) return {
|
|
137
|
+
wanted: bag.seed_wanted,
|
|
138
|
+
keys: bag.seed_keys
|
|
139
|
+
};
|
|
140
|
+
const request = request_of();
|
|
141
|
+
const d = request ? doc_asks.get(request) : void 0;
|
|
142
|
+
return d ? {
|
|
143
|
+
wanted: d.wanted,
|
|
144
|
+
keys: d.keys
|
|
145
|
+
} : void 0;
|
|
146
|
+
});
|
|
147
|
+
set_page_recorder((snapshot, seed, remotes, entry) => {
|
|
148
|
+
const event = try_get_request_store()?.event;
|
|
149
|
+
if (event && note_render_page_ask(event, seed)) return;
|
|
150
|
+
const bag = bag_of();
|
|
151
|
+
if (!bag) {
|
|
152
|
+
const request = request_of();
|
|
153
|
+
const d = request ? doc_asks.get(request) : void 0;
|
|
154
|
+
if (d && seed !== false) {
|
|
155
|
+
d.wanted = true;
|
|
156
|
+
d.keys = merge_seed_ask(d.keys, seed);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
bag.page = {
|
|
161
|
+
...bag.page,
|
|
162
|
+
...snapshot
|
|
163
|
+
};
|
|
164
|
+
if (seed !== false) {
|
|
165
|
+
bag.seed_wanted = true;
|
|
166
|
+
bag.seed_keys = merge_seed_ask(bag.seed_keys, seed);
|
|
167
|
+
}
|
|
168
|
+
if (bag.seed_asks && entry && seed !== false) bag.seed_asks.set(entry, seed);
|
|
169
|
+
if (remotes === null) bag.remotes_wanted = null;
|
|
170
|
+
else if (bag.remotes_wanted) for (const h of remotes) bag.remotes_wanted.add(h);
|
|
171
|
+
});
|
|
172
|
+
set_tail_reader(() => bag_of()?.tail ?? null);
|
|
173
|
+
set_measure_memo_reader(() => bag_of()?.measure_memo ?? null);
|
|
174
|
+
configure_render_page({ warn: dev ? (m) => console.warn(m) : null });
|
|
175
|
+
set_kit_event_reader(() => try_get_request_store()?.event ?? null);
|
|
176
|
+
/** Does this hole's server tree read `page.data` (the build's answer; unknown in dev → yes). */
|
|
177
|
+
const hole_reads_page_data = (id) => island_reads_page_data === null || island_reads_page_data[id] !== false;
|
|
178
|
+
set_render_page_port({
|
|
179
|
+
ready(reads_data) {
|
|
180
|
+
const store = try_get_request_store();
|
|
181
|
+
return prepare_render_page(store?.event, store?.state?.transport, reads_data);
|
|
182
|
+
},
|
|
183
|
+
tail() {
|
|
184
|
+
const store = try_get_request_store();
|
|
185
|
+
return render_page_facts_script(store?.event, store?.state?.transport);
|
|
186
|
+
},
|
|
187
|
+
version(hole_id) {
|
|
188
|
+
if (!hole_reads_page_data(hole_id)) return "";
|
|
189
|
+
const v = render_page_version(try_get_request_store()?.event, bag_of()?.page ?? void 0);
|
|
190
|
+
return v ? "&pv=" + v : "";
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
set_kit_page_reader(() => {
|
|
194
|
+
const bag = bag_of();
|
|
195
|
+
return render_page(bag ? bag.page ?? {} : void 0, try_get_request_store()?.event);
|
|
196
|
+
});
|
|
197
|
+
set_late_scope_opener(() => {
|
|
198
|
+
const bag = bag_of();
|
|
199
|
+
if (bag) bag.late_open = true;
|
|
200
|
+
});
|
|
201
|
+
set_late_recorder((promise) => {
|
|
202
|
+
const bag = bag_of();
|
|
203
|
+
if (!bag?.late_open) return null;
|
|
204
|
+
const id = `r${bag.late_next++}`;
|
|
205
|
+
(bag.late ??= []).push({
|
|
206
|
+
id,
|
|
207
|
+
promise
|
|
208
|
+
});
|
|
209
|
+
return id;
|
|
210
|
+
});
|
|
211
|
+
set_late_taker(() => {
|
|
212
|
+
const bag = bag_of();
|
|
213
|
+
if (!bag?.late?.length) return null;
|
|
214
|
+
const list = bag.late;
|
|
215
|
+
bag.late = null;
|
|
216
|
+
return list;
|
|
217
|
+
});
|
|
218
|
+
set_current_request(() => try_get_request_store()?.event?.request);
|
|
219
|
+
set_source_recorder((tag) => {
|
|
220
|
+
current_facts()?.sources?.push(tag);
|
|
221
|
+
});
|
|
222
|
+
set_page_route_config(route_config);
|
|
223
|
+
const plugins$1 = start_plugins(plugins, {
|
|
224
|
+
sign: (message) => sign(signing_key().secret, message),
|
|
225
|
+
verify: (message, signature) => verify(signing_key().secret, message, signature),
|
|
226
|
+
lifetime: app_lifetime,
|
|
227
|
+
deny: region_denied
|
|
228
|
+
});
|
|
229
|
+
const dt_buffers = /* @__PURE__ */ new WeakMap();
|
|
230
|
+
function dt_record(bag, input) {
|
|
231
|
+
const buf = bag ? dt_buffers.get(bag) : void 0;
|
|
232
|
+
if (!buf) return;
|
|
233
|
+
buf.events.push({
|
|
234
|
+
...input,
|
|
235
|
+
v: 1,
|
|
236
|
+
seq: buf.seq++,
|
|
237
|
+
t: dt_now(),
|
|
238
|
+
realm: "server"
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
if (DEVTOOLS) set_server_devtools_recorder((input) => dt_record(bag_of(), input));
|
|
242
|
+
/** Cap on a batch POST body before `request.json()` buffers it. 32 endpoints × ~8.5kB (props cap
|
|
243
|
+
* 8192 + URL overhead) ≈ 270kB; 512kB leaves margin. Rejected up front via `content-length`. */
|
|
244
|
+
const MAX_BATCH_BODY = 524288;
|
|
245
|
+
/** Abort waiting on slow region SSR (work may continue — see INVARIANTS · RENDER-TIMEOUT). */
|
|
246
|
+
const RENDER_TIMEOUT_MS = 1e4;
|
|
247
|
+
/** Process-local cap on concurrent region SSR (M1 CPU amp under valid MAC). */
|
|
248
|
+
const render_gate = new ConcurrencyGate(4);
|
|
249
|
+
/** The per-plugin contexts of an app with no plugins (shared: never written to). */
|
|
250
|
+
const NO_CTXS = [];
|
|
251
|
+
/**
|
|
252
|
+
* Anti-framing + MIME + Referrer on every region response.
|
|
253
|
+
* Referrer-Policy strips capability query strings from third-party asset requests (H6).
|
|
254
|
+
*/
|
|
255
|
+
const REGION_FRAME_HEADERS = {
|
|
256
|
+
"X-Frame-Options": "DENY",
|
|
257
|
+
"Content-Security-Policy": "frame-ancestors 'none'",
|
|
258
|
+
"X-Content-Type-Options": "nosniff",
|
|
259
|
+
"Referrer-Policy": "no-referrer"
|
|
260
|
+
};
|
|
261
|
+
function region_response(body, init) {
|
|
262
|
+
return new Response(body, {
|
|
263
|
+
status: init.status,
|
|
264
|
+
headers: {
|
|
265
|
+
...REGION_FRAME_HEADERS,
|
|
266
|
+
...init.headers
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* Every refused region request: a 403 that says why (`x-ogygia-deny`) and which key answered
|
|
272
|
+
* (`x-ogygia-key`, the fingerprint a capability carries as `&k=`). Neither is an oracle: every
|
|
273
|
+
* reason past `sig` is reached only with a capability this key signed (an unknown id still never
|
|
274
|
+
* tells from a bad MAC, SEC-01), and the fingerprint is an HMAC of a constant.
|
|
275
|
+
*/
|
|
276
|
+
function region_denied(why) {
|
|
277
|
+
return region_response("Forbidden", {
|
|
278
|
+
status: 403,
|
|
279
|
+
headers: {
|
|
280
|
+
"x-ogygia-deny": why,
|
|
281
|
+
"x-ogygia-key": signing_key().fp
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
/** A capability minted under another key than this deployment's (its `&k=` fingerprint). One
|
|
286
|
+
* without `k` (minted before capabilities named their key) is left to the MAC. */
|
|
287
|
+
function foreign_key(k) {
|
|
288
|
+
return k !== null && k !== signing_key().fp;
|
|
289
|
+
}
|
|
290
|
+
function capability_of(params) {
|
|
291
|
+
return {
|
|
292
|
+
id: params.get("id") ?? "",
|
|
293
|
+
render: params.get("r") ?? "",
|
|
294
|
+
payload: params.get("props") ?? "",
|
|
295
|
+
exp: params.get("exp") ?? "",
|
|
296
|
+
ttl: params.get("ttl") ?? "",
|
|
297
|
+
sig: params.get("sig") ?? "",
|
|
298
|
+
k: params.get("k")
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* The server entry that answers a verified capability: its own id when this build has it, else an
|
|
303
|
+
* entry of this build rendering the same component (its signed render identity) — the capability
|
|
304
|
+
* came from another build: an older cached page, another deployment, a changed schedule or salt.
|
|
305
|
+
* Null when this build renders neither: the component is gone, moved or renamed.
|
|
306
|
+
*/
|
|
307
|
+
function capability_target(cap) {
|
|
308
|
+
if (Object.hasOwn(island_url, cap.id)) return cap.id;
|
|
309
|
+
if (cap.render !== "" && Object.hasOwn(render_island, cap.render)) return render_island[cap.render];
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Decode a request pathname without throwing on malformed percent-encoding (SEC-05).
|
|
314
|
+
* @returns decoded path, or null if the encoding is invalid
|
|
315
|
+
*/
|
|
316
|
+
function decode_pathname(pathname) {
|
|
317
|
+
try {
|
|
318
|
+
return decodeURIComponent(pathname);
|
|
319
|
+
} catch {
|
|
320
|
+
return null;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/** Resolve a rate-limit key; fail closed (null) when the adapter cannot identify the client.
|
|
324
|
+
* Never trust X-Forwarded-For / spoofable proxy headers — adapters must provide getClientAddress(). */
|
|
325
|
+
function client_ip(event) {
|
|
326
|
+
try {
|
|
327
|
+
return event.getClientAddress();
|
|
328
|
+
} catch {
|
|
329
|
+
return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Stylesheet `<link>`s a server-picked region needs so a page that never imported the component
|
|
334
|
+
* still styles the fetched HTML (the whole point of a held region — the page can't know which
|
|
335
|
+
* component the server will choose). Tagged `data-ogygia-region-css` so the client runtime HOISTS
|
|
336
|
+
* them into `<head>`: a body `<link>` wouldn't load inside a `<template>` batch parcel, and a head
|
|
337
|
+
* link loads once and is deduped across regions. Empty for a plain island (its CSS is already in the
|
|
338
|
+
* page's own stylesheet) or before a build (`islandCss` reads the build-time handoff).
|
|
339
|
+
*/
|
|
340
|
+
function region_css_links(id) {
|
|
341
|
+
const url = island_url[id];
|
|
342
|
+
if (!url) return "";
|
|
343
|
+
let out = "";
|
|
344
|
+
for (const href of islandCss(url)) out += region_css_tag(href, kit_asset(href));
|
|
345
|
+
return out;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Wrap a payload as an `application/ogygia-*` side-channel `<script>` the runtime reads. The
|
|
349
|
+
* `payload` MUST be `<`-safe already: devalue output is by construction (it writes `<`), a
|
|
350
|
+
* JSON payload goes through `escape_script_text` first — so this only builds the tag. One spot for
|
|
351
|
+
* the side-channel shape (page / remote / ctx / devtools).
|
|
352
|
+
*/
|
|
353
|
+
function emit_ogygia_script(subtype, escaped_payload, marker = "") {
|
|
354
|
+
return `<script type="application/ogygia-${subtype}"${marker ? " " + marker : ""}>${escaped_payload}<\/script>`;
|
|
355
|
+
}
|
|
356
|
+
const DOUBLE_QUOTE_G = /"/g;
|
|
357
|
+
/** Stamped into the head of a csr=true document: the runtime reads it (`kit_hydrates_page`)
|
|
358
|
+
* instead of scanning the page's inline scripts for Kit's bootstrap. */
|
|
359
|
+
const CSR_META = "<meta name=\"ogygia-csr\" content=\"true\">";
|
|
360
|
+
const CSR_FALSE_META = "<meta name=\"ogygia-csr\" content=\"false\">";
|
|
361
|
+
/** the og.$ factory script, per manifest object (the build's, the same every request) */
|
|
362
|
+
const fnm_script_cache = /* @__PURE__ */ new WeakMap();
|
|
363
|
+
/** A document fetched by ogygia's router (runtime/router-nav.ts `nav_headers`: a navigation or a
|
|
364
|
+
* prefetch) — morphed into the live page rather than loaded as one. */
|
|
365
|
+
function router_fetched(event) {
|
|
366
|
+
return event.request.headers.get("x-ogygia-spa") !== null;
|
|
367
|
+
}
|
|
368
|
+
const NO_TRANSPORT = {};
|
|
369
|
+
const seed_codec_cache = /* @__PURE__ */ new WeakMap();
|
|
370
|
+
/** The seed's devalue reducers — the app's transport encoders merged with the defer markers — and a
|
|
371
|
+
* stringify bound to them, made once per transport object (Kit hands the same one every request). */
|
|
372
|
+
function seed_codecs(transport) {
|
|
373
|
+
const key = transport ?? NO_TRANSPORT;
|
|
374
|
+
let hit = seed_codec_cache.get(key);
|
|
375
|
+
if (!hit) {
|
|
376
|
+
const seed_reducers = {
|
|
377
|
+
...Object.fromEntries(Object.entries(transport ?? {}).map(([name, codec]) => [name, codec.encode])),
|
|
378
|
+
...page_seed_reducers
|
|
379
|
+
};
|
|
380
|
+
hit = {
|
|
381
|
+
seed_reducers,
|
|
382
|
+
seed_stringify: ((v) => stringify(v, seed_reducers))
|
|
383
|
+
};
|
|
384
|
+
seed_codec_cache.set(key, hit);
|
|
385
|
+
}
|
|
386
|
+
return hit;
|
|
387
|
+
}
|
|
388
|
+
/** `</body>` as bytes (ASCII: the same in every UTF-8 document). */
|
|
389
|
+
const BODY_CLOSE_BYTES = [
|
|
390
|
+
60,
|
|
391
|
+
47,
|
|
392
|
+
98,
|
|
393
|
+
111,
|
|
394
|
+
100,
|
|
395
|
+
121,
|
|
396
|
+
62
|
|
397
|
+
];
|
|
398
|
+
/** Does `</body>` end in `chunk` — searched backwards from its end, where it sits — or straddle the
|
|
399
|
+
* boundary with the previous chunk's last bytes (`carry`, at most 6)? No decode. */
|
|
400
|
+
function has_body_close(carry, chunk) {
|
|
401
|
+
const at_bytes = (buf, i) => {
|
|
402
|
+
for (let k = 0; k < 7; k++) if (buf[i + k] !== BODY_CLOSE_BYTES[k]) return false;
|
|
403
|
+
return true;
|
|
404
|
+
};
|
|
405
|
+
for (let i = chunk.lastIndexOf(60); i !== -1; i = i === 0 ? -1 : chunk.lastIndexOf(60, i - 1)) if (at_bytes(chunk, i)) return true;
|
|
406
|
+
if (!carry.length) return false;
|
|
407
|
+
const seam = new Uint8Array(carry.length + Math.min(6, chunk.length));
|
|
408
|
+
seam.set(carry);
|
|
409
|
+
seam.set(chunk.subarray(0, seam.length - carry.length), carry.length);
|
|
410
|
+
for (let i = 0; i + 7 <= seam.length; i++) if (at_bytes(seam, i)) return true;
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
function csr_page_of(html, body_end, event, bag) {
|
|
414
|
+
return event?.route?.id != null ? bag?.page?.error != null ? error_route_is_csr_true(event.route.id) : route_is_csr_true(event.route.id) : body_end !== -1 && html_has_kit_bootstrap(html, body_end);
|
|
415
|
+
}
|
|
416
|
+
/** The restorer, inline (runtime/restore.ts — self-contained, so its source runs as is): restores the
|
|
417
|
+
* document now, and stays as `__og_restore` / `__og_restore_adopt` for hole answers and router swaps. */
|
|
418
|
+
const RESTORER_SCRIPT = `<script data-og-restore>(function(){var r=${restore.toString()};window.__og_restore=r;window.__og_restore_adopt=${restore_adopt.toString()};r(document)})()<\/script>`;
|
|
419
|
+
var OgygiaHandle = class {
|
|
420
|
+
#endpoint;
|
|
421
|
+
#speculation_rules;
|
|
422
|
+
/** The app's server transform (documents and region answers) — see OgygiaHandleOptions. */
|
|
423
|
+
#transform;
|
|
424
|
+
render_rate;
|
|
425
|
+
probe_rate;
|
|
426
|
+
constructor(options = {}) {
|
|
427
|
+
this.#transform = options.transform;
|
|
428
|
+
configure_islands_endpoint(options.endpoint);
|
|
429
|
+
this.#endpoint = islands_endpoint();
|
|
430
|
+
this.#speculation_rules = enabled ? "" : mpaSpeculationRules(this.#endpoint);
|
|
431
|
+
this.render_rate = new RateLimiter({
|
|
432
|
+
max: rateLimit.max,
|
|
433
|
+
windowMs: rateLimit.windowMs
|
|
434
|
+
});
|
|
435
|
+
/** Cheap pre-HMAC probe budget — forged traffic pays this, not full render quota (HMAC-CPU-DOS). */
|
|
436
|
+
this.probe_rate = new RateLimiter({
|
|
437
|
+
max: rateLimit.max <= 0 ? 0 : Math.max(rateLimit.max * 5, 120),
|
|
438
|
+
windowMs: rateLimit.windowMs
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
handle = around(plugins$1, (input) => this.#core(input));
|
|
442
|
+
#core = async ({ event, resolve }) => {
|
|
443
|
+
const path = decode_pathname(event.url.pathname);
|
|
444
|
+
if (path === null) return new Response("Bad Request", { status: 400 });
|
|
445
|
+
const facts = facts_of(event.request);
|
|
446
|
+
if (plugins$1.length === 0) return this.#route(event, resolve, path, NO_CTXS, facts);
|
|
447
|
+
const ctxs = request_contexts(plugins$1, event, path, facts, (endpoint) => this.#render_hole(endpoint, event));
|
|
448
|
+
try {
|
|
449
|
+
for (let i = 0; i < plugins$1.length; i++) {
|
|
450
|
+
const answer = await plugins$1[i].def.request?.(ctxs[i]);
|
|
451
|
+
if (answer) return answer;
|
|
452
|
+
}
|
|
453
|
+
return await this.#route(event, resolve, path, ctxs, facts);
|
|
454
|
+
} finally {
|
|
455
|
+
for (let i = 0; i < plugins$1.length; i++) try {
|
|
456
|
+
plugins$1[i].def.done?.(ctxs[i]);
|
|
457
|
+
} catch {}
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
/** One signed hole rendered in place with THIS request's event (a plugin's `ctx.renderHole`:
|
|
461
|
+
* the stored-page stitch). Null keeps the fallback: a refused capability, a failed render, or
|
|
462
|
+
* an answer that asks to keep it. */
|
|
463
|
+
async #render_hole(endpoint, event) {
|
|
464
|
+
const out = await this.#render_capability(endpoint, event);
|
|
465
|
+
return out && out.html !== "<!--ogygia:keep-fallback-->" ? out.html : null;
|
|
466
|
+
}
|
|
467
|
+
async #route(event, resolve, path, ctxs, facts) {
|
|
468
|
+
if (!path.endsWith(this.#endpoint)) {
|
|
469
|
+
if (event.isDataRequest && event.request.headers.has("x-ogygia-page-facts")) {
|
|
470
|
+
const res = await resolve(event);
|
|
471
|
+
try {
|
|
472
|
+
res.headers.set(PAGE_FACTS_ROUTE_HEADER, encodeURIComponent(JSON.stringify({
|
|
473
|
+
id: event.route.id,
|
|
474
|
+
params: event.params
|
|
475
|
+
})));
|
|
476
|
+
} catch {}
|
|
477
|
+
return res;
|
|
478
|
+
}
|
|
479
|
+
const devtools_on = DEVTOOLS && (!DEVTOOLS_LAZY || devtools_switch(event));
|
|
480
|
+
const devtools_personal = DEVTOOLS_LAZY && !building && (devtools_on || switch_in_url(event.url) !== null);
|
|
481
|
+
facts.private = devtools_personal;
|
|
482
|
+
facts.answerForm = !!this.#transform && router_fetched(event);
|
|
483
|
+
const store = try_get_request_store();
|
|
484
|
+
const bag = {
|
|
485
|
+
ctx: /* @__PURE__ */ new Map(),
|
|
486
|
+
page: null,
|
|
487
|
+
seed_wanted: false,
|
|
488
|
+
seed_keys: null,
|
|
489
|
+
seed_asks: request_stats_detailed() ? /* @__PURE__ */ new Map() : null,
|
|
490
|
+
remotes_wanted: /* @__PURE__ */ new Set(),
|
|
491
|
+
tail: new DocumentTail(),
|
|
492
|
+
measure_memo: /* @__PURE__ */ new Map(),
|
|
493
|
+
deferred: null,
|
|
494
|
+
defer_next_id: 0,
|
|
495
|
+
late: null,
|
|
496
|
+
late_next: 0,
|
|
497
|
+
late_open: false,
|
|
498
|
+
seed_reducers: null,
|
|
499
|
+
facts
|
|
500
|
+
};
|
|
501
|
+
const lean = devtools_on && !building && (event.cookies.get("og_testing") === "lean" || event.url.searchParams.get("og-testing") === "lean");
|
|
502
|
+
if (devtools_on && !lean) dt_buffers.set(bag, {
|
|
503
|
+
events: [],
|
|
504
|
+
seq: 0
|
|
505
|
+
});
|
|
506
|
+
bags.set(event.request, bag);
|
|
507
|
+
for (let i = 0; i < ctxs.length; i++) {
|
|
508
|
+
const answer = await plugins$1[i].def.page?.(ctxs[i]);
|
|
509
|
+
if (answer) return answer;
|
|
510
|
+
}
|
|
511
|
+
const set_headers = event.setHeaders;
|
|
512
|
+
event.setHeaders = (headers) => {
|
|
513
|
+
set_headers(headers);
|
|
514
|
+
for (const key in headers) if (key.toLowerCase() === "cache-control") note_cache_control(event.request, headers[key]);
|
|
515
|
+
};
|
|
516
|
+
const response = await resolve(event, { transformPageChunk: async ({ html }) => this.inject_client_seeds(this.#transform ? await this.#transform_document(html, event, bag) : html, store?.state, event, bag) });
|
|
517
|
+
if (devtools_personal) mark_personal(response);
|
|
518
|
+
if (this.#transform && (response.headers.get("content-type") ?? "").includes("text/html")) try {
|
|
519
|
+
response.headers.append("vary", "x-ogygia-spa");
|
|
520
|
+
} catch {}
|
|
521
|
+
if (devtools_on && DEVTOOLS_LAZY && (response.headers.get("content-type") ?? "").includes("text/html")) try {
|
|
522
|
+
if (!response.headers.has("document-policy")) response.headers.set("document-policy", "js-profiling");
|
|
523
|
+
if (event.route?.id) response.headers.append("server-timing", `og-route;desc=${JSON.stringify(event.route.id)}`);
|
|
524
|
+
} catch {}
|
|
525
|
+
if (bag.deferred && bag.deferred.length) facts.streamed = true;
|
|
526
|
+
let out = response;
|
|
527
|
+
for (let i = 0; i < ctxs.length; i++) out = await plugins$1[i].def.response?.(ctxs[i], out) ?? out;
|
|
528
|
+
if (out !== response) return out;
|
|
529
|
+
if (bag.deferred && bag.deferred.length) return this.stream_page_deferred(response, bag.deferred, bag.defer_next_id, bag.seed_reducers ?? void 0);
|
|
530
|
+
if (!bag.kit_client && !response.headers.has("content-length") && (response.headers.get("content-type") ?? "").includes("text/html")) return drop_dead_kit_resolves(response);
|
|
531
|
+
return response;
|
|
532
|
+
}
|
|
533
|
+
if (event.request.method.toUpperCase() === "POST") return this.render_batch(event);
|
|
534
|
+
return this.render_region(event);
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Batch frame stream (client navigation, single-flight). POST body: a JSON `string[]` of signed
|
|
538
|
+
* region endpoints — the calls the client needs. Renders them in parallel and flushes each as a
|
|
539
|
+
* `<template data-ogygia-slot="sig">…</template>` frame the moment IT settles (out of order); a
|
|
540
|
+
* done sentinel ends it. One response, many frames. Every call carries its own MAC so there's no
|
|
541
|
+
* extra auth; same-origin + per-IP budget mirror the single-region path.
|
|
542
|
+
*/
|
|
543
|
+
async render_batch(event) {
|
|
544
|
+
const MAX_BATCH = 32;
|
|
545
|
+
if (event.request.headers.get("sec-fetch-site") === "cross-site") return region_denied("cross-site");
|
|
546
|
+
const ip = client_ip(event);
|
|
547
|
+
if (!ip) return region_response("Too Many Requests", { status: 429 });
|
|
548
|
+
if (this.probe_rate.limited(ip) || this.render_rate.limited(ip)) return region_response("Too Many Requests", { status: 429 });
|
|
549
|
+
const content_length = Number(event.request.headers.get("content-length"));
|
|
550
|
+
if (Number.isFinite(content_length) && content_length > MAX_BATCH_BODY) return region_response("Payload Too Large", { status: 413 });
|
|
551
|
+
let parsed;
|
|
552
|
+
try {
|
|
553
|
+
parsed = await event.request.json();
|
|
554
|
+
} catch {
|
|
555
|
+
return region_response("Bad Request", { status: 400 });
|
|
556
|
+
}
|
|
557
|
+
if (!Array.isArray(parsed) || parsed.length === 0) return region_response("Bad Request", { status: 400 });
|
|
558
|
+
const calls = parsed.filter((e) => typeof e === "string").slice(0, MAX_BATCH);
|
|
559
|
+
const render = (endpoint) => this.#render_capability(endpoint, event, true);
|
|
560
|
+
const encoder = new TextEncoder();
|
|
561
|
+
return region_response(new ReadableStream({ async start(controller) {
|
|
562
|
+
await Promise.all(calls.map(async (endpoint) => {
|
|
563
|
+
const out = await render(endpoint).catch(() => null);
|
|
564
|
+
const parcel = out ? build_parcel(out.slot, out.html) : null;
|
|
565
|
+
if (parcel) controller.enqueue(encoder.encode(parcel));
|
|
566
|
+
}));
|
|
567
|
+
controller.enqueue(encoder.encode(done_parcel()));
|
|
568
|
+
controller.close();
|
|
569
|
+
} }), {
|
|
570
|
+
status: 200,
|
|
571
|
+
headers: {
|
|
572
|
+
"content-type": "text/html; charset=utf-8",
|
|
573
|
+
"cache-control": "no-store",
|
|
574
|
+
"x-accel-buffering": "no"
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
/**
|
|
579
|
+
* THE APP'S TRANSFORM ON A DOCUMENT (server/reversible.ts): the chunk carrying `</head>` (the
|
|
580
|
+
* document; a streamed tail chunk passes untouched) is marked, handed to the transform, settled.
|
|
581
|
+
* Then the head gets the transform's `data-og-head` assets (deduped) and, when the parser would have
|
|
582
|
+
* restructured a tag, the stand-ins' sheet; the body gets the restorer — inline, before `</body>`,
|
|
583
|
+
* so it runs before any island wakes, before Kit starts, and before a deferred module upgrades a
|
|
584
|
+
* host. It also stays on the page as a global for the runtime's hole answers and router swaps.
|
|
585
|
+
*/
|
|
586
|
+
async #transform_document(html, event, bag) {
|
|
587
|
+
if (html.indexOf("</head>") === -1) return html;
|
|
588
|
+
const csr = csr_page_of(html, html.lastIndexOf("</body>"), event, bag);
|
|
589
|
+
const answer = router_fetched(event);
|
|
590
|
+
const marked = mark(html, "document", csr, answer);
|
|
591
|
+
const out = await this.#transform(marked ? marked.html : html, {
|
|
592
|
+
kind: "document",
|
|
593
|
+
csr,
|
|
594
|
+
event
|
|
595
|
+
});
|
|
596
|
+
const s = marked ? settle(out, marked.record, { dev }) : null;
|
|
597
|
+
let first_load = false;
|
|
598
|
+
if (marked && !answer) {
|
|
599
|
+
for (const h of marked.record.hosts.values()) if (!h.planned) first_load = true;
|
|
600
|
+
}
|
|
601
|
+
bag.transform_head = (s?.head.join("") ?? "") + (s?.stand_ins ? `<style data-og-stand-ins>${STAND_IN_CSS}</style>` : "") + (first_load ? "<meta name=\"ogygia-first-load\">" : "");
|
|
602
|
+
bag.transform_body = (s?.check ? check_script(s.check) : "") + RESTORER_SCRIPT;
|
|
603
|
+
return s ? s.html : out;
|
|
604
|
+
}
|
|
605
|
+
/** THE APP'S TRANSFORM ON A REGION ANSWER: marked, transformed, settled. The runtime restores it
|
|
606
|
+
* before insertion (and hoists its `data-og-head` assets), so no stand-in sheet is needed. */
|
|
607
|
+
async #transform_region(html, event) {
|
|
608
|
+
const marked = mark(html, "region", false);
|
|
609
|
+
const out = await this.#transform(marked ? marked.html : html, {
|
|
610
|
+
kind: "region",
|
|
611
|
+
csr: false,
|
|
612
|
+
event
|
|
613
|
+
});
|
|
614
|
+
if (!marked) return out;
|
|
615
|
+
const s = settle(out, marked.record, { dev });
|
|
616
|
+
return s.check ? s.html + check_script(s.check) : s.html;
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* Document-level side-channels: one page snapshot + optional remote query seed.
|
|
620
|
+
* Skips Kit-booted (csr=true) pages which serialize remotes themselves.
|
|
621
|
+
*/
|
|
622
|
+
async inject_client_seeds(html, state, event, bag) {
|
|
623
|
+
const t_start = performance.now();
|
|
624
|
+
const spans = locate(html);
|
|
625
|
+
const head = spans.head_end === -1 ? null : html.slice(0, spans.head_end);
|
|
626
|
+
const csr_page = csr_page_of(html, spans.body_end, event, bag);
|
|
627
|
+
if (csr_page && bag) bag.kit_client = true;
|
|
628
|
+
if (csr_page) {
|
|
629
|
+
let head_inject = "";
|
|
630
|
+
if (head !== null) {
|
|
631
|
+
head_inject = CSR_META;
|
|
632
|
+
if (devtools_boot_url && !head.includes("data-ogygia-devtools-boot")) head_inject += `<script type="module" data-ogygia-devtools-boot src="${devtools_boot_url}"><\/script>`;
|
|
633
|
+
}
|
|
634
|
+
if (head !== null && bag?.transform_head) head_inject = bag.transform_head + head_inject;
|
|
635
|
+
const tail = spans.body_end !== -1 && bag ? (bag.transform_body ?? "") + bag.tail.render(null) : "";
|
|
636
|
+
if (head === null && !tail) return html;
|
|
637
|
+
const ordered = head === null ? null : runtime_first(head, null);
|
|
638
|
+
return assemble(html, spans, ordered === head ? null : ordered, head_inject, tail);
|
|
639
|
+
}
|
|
640
|
+
let head_out = null;
|
|
641
|
+
let head_inject = head !== null ? CSR_FALSE_META : "";
|
|
642
|
+
if (head !== null) {
|
|
643
|
+
const deduped = dedupe_head_links(head);
|
|
644
|
+
if (deduped !== head) head_out = deduped;
|
|
645
|
+
const probe = head_out ?? head;
|
|
646
|
+
if (!enabled && !page_declares_speculation_rules(probe)) head_inject += `<script type="speculationrules" data-ogygia-speculate>${this.#speculation_rules}<\/script>`;
|
|
647
|
+
if (enabled && !page_declares_router_meta(probe)) head_inject += `<meta name="ogygia-router" content="${viewTransitions ? "vt" : "plain"}">`;
|
|
648
|
+
const runtime_tag = enabled && runtime_url && !page_declares_runtime_script(probe) ? runtime_bootstrap(kit_asset) : null;
|
|
649
|
+
const ordered = runtime_first(probe, runtime_tag);
|
|
650
|
+
if (ordered !== probe) head_out = ordered;
|
|
651
|
+
if (dev_hmr_url && !page_declares_dev_hmr_script(probe)) {
|
|
652
|
+
head_inject += `<script type="module" data-ogygia-dev-hmr src="${kit_asset(dev_hmr_url)}"><\/script>`;
|
|
653
|
+
const scope = (event?.route.id ?? "").split("/").filter(Boolean)[0] ?? "";
|
|
654
|
+
head_inject += `<meta name="ogygia-dev-scope" content="${scope.replace(DOUBLE_QUOTE_G, "")}">`;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
if (head !== null && bag?.transform_head) head_inject = bag.transform_head + head_inject;
|
|
658
|
+
if (spans.body_end === -1) return assemble(html, spans, head_out, head_inject, "");
|
|
659
|
+
const rendered = !!bag && bag.page !== null;
|
|
660
|
+
const scripts = bag?.transform_body ? [bag.transform_body] : [];
|
|
661
|
+
if (!rendered) {
|
|
662
|
+
this.append_devtools_seed(scripts, bag);
|
|
663
|
+
return assemble(html, spans, head_out, head_inject, scripts.join(""));
|
|
664
|
+
}
|
|
665
|
+
const page_snap = bag.page;
|
|
666
|
+
const seed_wanted = bag.seed_wanted;
|
|
667
|
+
const detail = request_stats_detailed();
|
|
668
|
+
const tail_html = bag.tail.render(seed_wanted ? index_seed(page_snap.data) : null, detail);
|
|
669
|
+
let seed_keys = bag.seed_keys;
|
|
670
|
+
if (seed_wanted && seed_keys !== "all" && seed_keys !== null) for (const k of index_seed(page_snap.data).touched) seed_keys.add(k);
|
|
671
|
+
const shaped_data = shape_page_data(page_snap.data, seed_keys);
|
|
672
|
+
let seed_data = shaped_data;
|
|
673
|
+
let seed_form = page_snap.form;
|
|
674
|
+
const { seed_reducers, seed_stringify } = seed_codecs(state?.transport);
|
|
675
|
+
const data_shape = analyze(shaped_data);
|
|
676
|
+
const form_shape = analyze(page_snap.form ?? null);
|
|
677
|
+
const has_pending = data_shape.thenable || form_shape.thenable;
|
|
678
|
+
if (has_pending) mark_per_visitor(bag.facts, "streamed load (promise in page data)");
|
|
679
|
+
const can_stream = event?.request.headers.get("sec-fetch-mode") === "navigate";
|
|
680
|
+
if (has_pending && can_stream && seed_wanted) {
|
|
681
|
+
const staged_data = stage_deferred(shaped_data, 0);
|
|
682
|
+
const staged_form = stage_deferred(page_snap.form, staged_data.next_id);
|
|
683
|
+
seed_data = staged_data.staged;
|
|
684
|
+
seed_form = staged_form.staged;
|
|
685
|
+
bag.deferred = [...staged_data.deferred, ...staged_form.deferred];
|
|
686
|
+
bag.defer_next_id = staged_form.next_id;
|
|
687
|
+
bag.seed_reducers = seed_reducers;
|
|
688
|
+
scripts.push(`<script>${PAGE_DEFER_BOOTSTRAP}<\/script>`);
|
|
689
|
+
} else if (has_pending && seed_wanted) {
|
|
690
|
+
seed_data = await settle_deferred(shaped_data);
|
|
691
|
+
seed_form = await settle_deferred(page_snap.form);
|
|
692
|
+
}
|
|
693
|
+
if (tail_html) scripts.push(tail_html);
|
|
694
|
+
let remote_seed_bytes = 0;
|
|
695
|
+
let fnm_bytes = 0;
|
|
696
|
+
let ctx_bytes = 0;
|
|
697
|
+
const page_payload = event && seed_wanted ? PageSeed.serialize({
|
|
698
|
+
url: event.url,
|
|
699
|
+
params: event.params,
|
|
700
|
+
route: event.route,
|
|
701
|
+
status: page_snap.status ?? 200,
|
|
702
|
+
data: seed_data,
|
|
703
|
+
form: seed_form,
|
|
704
|
+
error: page_snap.error
|
|
705
|
+
}, seed_stringify, !has_pending && data_shape.json && form_shape.json && analyze(page_snap.error ?? null).json) : null;
|
|
706
|
+
if (page_payload) {
|
|
707
|
+
scripts.push(emit_ogygia_script("page", page_payload.text, "data-ogygia-page" + (page_payload.json ? ` ${WIRE_FORMAT_ATTR}="${WIRE_FORMAT_JSON}"` : "")));
|
|
708
|
+
if (DEVTOOLS) dt_record(bag, {
|
|
709
|
+
domain: "server",
|
|
710
|
+
name: "server.seed.injected",
|
|
711
|
+
kind: "page",
|
|
712
|
+
bytes: page_payload.text.length
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
if (state?.remote?.implicit) {
|
|
716
|
+
const remote_script = await this.build_remote_seed_script(state, bag.remotes_wanted);
|
|
717
|
+
if (remote_script) {
|
|
718
|
+
scripts.push(remote_script);
|
|
719
|
+
remote_seed_bytes = remote_script.length;
|
|
720
|
+
if (DEVTOOLS) dt_record(bag, {
|
|
721
|
+
domain: "server",
|
|
722
|
+
name: "server.seed.injected",
|
|
723
|
+
kind: "remote",
|
|
724
|
+
bytes: remote_script.length
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
const fnm = fnManifest();
|
|
729
|
+
if (fnm) {
|
|
730
|
+
let fnm_script = fnm_script_cache.get(fnm);
|
|
731
|
+
if (fnm_script === void 0) {
|
|
732
|
+
fnm_script = `<script data-ogygia-fnm>globalThis.__OG_FNM=Object.assign(globalThis.__OG_FNM||{},{${Object.entries(fnm).map(([tag, src]) => `${JSON.stringify(tag)}:(${src})`).join(",")}});<\/script>`;
|
|
733
|
+
fnm_script_cache.set(fnm, fnm_script);
|
|
734
|
+
}
|
|
735
|
+
scripts.push(fnm_script);
|
|
736
|
+
fnm_bytes = fnm_script.length;
|
|
737
|
+
}
|
|
738
|
+
const provided = bag?.ctx;
|
|
739
|
+
if (provided?.size) {
|
|
740
|
+
const payload = serialize_provided_context(provided);
|
|
741
|
+
if (payload) {
|
|
742
|
+
scripts.push(emit_ogygia_script("ctx", payload, PAGE_CTX_MARKER));
|
|
743
|
+
ctx_bytes = payload.length;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
this.append_devtools_seed(scripts, bag);
|
|
747
|
+
const out = assemble(html, spans, head_out, head_inject, scripts.join(""));
|
|
748
|
+
if (event && request_stats_wanted()) {
|
|
749
|
+
const size = bag.tail.size;
|
|
750
|
+
record_request_stats(event.request, {
|
|
751
|
+
transform_ms: Math.round((performance.now() - t_start) * 100) / 100,
|
|
752
|
+
islands: size.props,
|
|
753
|
+
hints: size.hints,
|
|
754
|
+
holes: size.holes,
|
|
755
|
+
seed_bytes: page_payload ? page_payload.text.length : 0,
|
|
756
|
+
remote_seed_bytes,
|
|
757
|
+
tail_bytes: tail_html.length,
|
|
758
|
+
fnm_bytes,
|
|
759
|
+
ctx_bytes,
|
|
760
|
+
seed_json: page_payload?.json ?? false,
|
|
761
|
+
...detail ? {
|
|
762
|
+
seed_culprit: page_payload && !page_payload.json ? json_culprit(seed_data) : null,
|
|
763
|
+
island_rows: bag.tail.island_rows() ?? [],
|
|
764
|
+
seed: this.explain_seed(page_snap.data, seed_keys, bag),
|
|
765
|
+
hole_rows: bag.tail.hole_rows()
|
|
766
|
+
} : {}
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
return out;
|
|
770
|
+
}
|
|
771
|
+
/**
|
|
772
|
+
* THE SEED EXPLAINER: per top-level `page.data` key, its size and why it ships — an island's
|
|
773
|
+
* client code reads it (the build's per-entry keys), a sidecar points into it (seed refs), or
|
|
774
|
+
* some island reads the page whole and everything ships. Profiler detail only.
|
|
775
|
+
*/
|
|
776
|
+
explain_seed(data, seed_keys, bag) {
|
|
777
|
+
const rows = bag.tail.island_rows() ?? [];
|
|
778
|
+
const names = /* @__PURE__ */ new Map();
|
|
779
|
+
for (const row of rows) if (row.name && !names.has(row.entry)) names.set(row.entry, row.name);
|
|
780
|
+
const name_of = (entry) => names.get(entry) ?? entry;
|
|
781
|
+
const add = (m, k, v) => (m.get(k) ?? m.set(k, /* @__PURE__ */ new Set()).get(k)).add(v);
|
|
782
|
+
const whole = /* @__PURE__ */ new Set();
|
|
783
|
+
const readers = /* @__PURE__ */ new Map();
|
|
784
|
+
for (const [entry, ask] of bag.seed_asks ?? []) if (ask === "all") whole.add(name_of(entry));
|
|
785
|
+
else if (ask !== false) for (const k of ask) add(readers, k, name_of(entry));
|
|
786
|
+
const referenced = /* @__PURE__ */ new Map();
|
|
787
|
+
for (const row of rows) for (const k of row.ref_keys) add(referenced, k, name_of(row.entry));
|
|
788
|
+
const whole_by = [...whole];
|
|
789
|
+
const keys = [];
|
|
790
|
+
if (data && typeof data === "object" && !Array.isArray(data)) for (const key of Object.keys(data)) {
|
|
791
|
+
const shipped = seed_keys === "all" || seed_keys !== null && seed_keys.has(key);
|
|
792
|
+
const read = [...readers.get(key) ?? []];
|
|
793
|
+
const refd = [...referenced.get(key) ?? []];
|
|
794
|
+
keys.push({
|
|
795
|
+
key,
|
|
796
|
+
bytes: analyze(data[key]).bytes,
|
|
797
|
+
readers: read,
|
|
798
|
+
referenced_by: refd,
|
|
799
|
+
shipped,
|
|
800
|
+
reason: !shipped ? null : read.length ? "read" : refd.length ? "referenced" : whole_by.length ? "whole" : "read"
|
|
801
|
+
});
|
|
802
|
+
}
|
|
803
|
+
keys.sort((a, b) => b.bytes - a.bytes);
|
|
804
|
+
return {
|
|
805
|
+
keys,
|
|
806
|
+
whole_by
|
|
807
|
+
};
|
|
808
|
+
}
|
|
809
|
+
/** DEVTOOLS: drain this request's server-realm events (region renders, capability mints, seeds)
|
|
810
|
+
* into an `application/ogygia-devtools` side-channel the client bus ingests — one stream, both
|
|
811
|
+
* realms, correlated by fingerprint. Appended LAST so the seed.injected events are included. */
|
|
812
|
+
append_devtools_seed(scripts, bag) {
|
|
813
|
+
const dt_buf = DEVTOOLS && bag ? dt_buffers.get(bag) : void 0;
|
|
814
|
+
if (DEVTOOLS && dt_buf && dt_buf.events.length) scripts.push(emit_ogygia_script("devtools", escape_script_text(JSON.stringify(dt_buf.events))));
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Stream the captured `$page.data` promises into islands (csr=false, real browser load). The
|
|
818
|
+
* document — carrying the pending seed + resolve-global bootstrap — is already built by `resolve`;
|
|
819
|
+
* here we (1) forward it, (2) DRAIN Kit's own dead csr=false resolve tail so its
|
|
820
|
+
* `__sveltekit_<hash> is not defined` never reaches the browser, and (3) emit one
|
|
821
|
+
* `<script>__ogygia_page_resolve(id, ok, value)<\/script>` per promise AS IT SETTLES — completion
|
|
822
|
+
* order, non-blocking. Each island's `{#await page.data.x}` flips pending → resolved live.
|
|
823
|
+
*/
|
|
824
|
+
stream_page_deferred(response, deferred, initial_next_id, reducers) {
|
|
825
|
+
const source = response.body;
|
|
826
|
+
if (!source) return response;
|
|
827
|
+
const reader = source.getReader();
|
|
828
|
+
const encoder = new TextEncoder();
|
|
829
|
+
const stream = new ReadableStream({
|
|
830
|
+
async start(controller) {
|
|
831
|
+
let carry = /* @__PURE__ */ new Uint8Array(0);
|
|
832
|
+
try {
|
|
833
|
+
for (;;) {
|
|
834
|
+
const { value, done } = await reader.read();
|
|
835
|
+
if (done) break;
|
|
836
|
+
controller.enqueue(value);
|
|
837
|
+
if (has_body_close(carry, value)) break;
|
|
838
|
+
carry = value.length >= 6 ? value.subarray(value.length - 6) : value;
|
|
839
|
+
}
|
|
840
|
+
} catch {}
|
|
841
|
+
(async () => {
|
|
842
|
+
try {
|
|
843
|
+
for (;;) {
|
|
844
|
+
const { done } = await reader.read();
|
|
845
|
+
if (done) break;
|
|
846
|
+
}
|
|
847
|
+
} catch {}
|
|
848
|
+
})();
|
|
849
|
+
let pending = deferred.length;
|
|
850
|
+
let next_id = initial_next_id;
|
|
851
|
+
let closed = false;
|
|
852
|
+
const maybe_close = () => {
|
|
853
|
+
if (pending === 0 && !closed) {
|
|
854
|
+
closed = true;
|
|
855
|
+
try {
|
|
856
|
+
controller.close();
|
|
857
|
+
} catch {}
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
const push = (id, ok, value) => {
|
|
861
|
+
try {
|
|
862
|
+
controller.enqueue(encoder.encode(resolve_script(PAGE_DEFER_GLOBAL, id, {
|
|
863
|
+
ok,
|
|
864
|
+
value
|
|
865
|
+
}, reducers)));
|
|
866
|
+
} catch {}
|
|
867
|
+
};
|
|
868
|
+
const stream_one = (id, promise) => {
|
|
869
|
+
Promise.resolve(promise).then((value) => {
|
|
870
|
+
const { staged, deferred: nested, next_id: after } = stage_deferred(value, next_id);
|
|
871
|
+
next_id = after;
|
|
872
|
+
push(id, true, staged);
|
|
873
|
+
pending += nested.length;
|
|
874
|
+
for (const n of nested) stream_one(n.id, n.promise);
|
|
875
|
+
pending -= 1;
|
|
876
|
+
maybe_close();
|
|
877
|
+
}, (error) => {
|
|
878
|
+
push(id, false, error);
|
|
879
|
+
pending -= 1;
|
|
880
|
+
maybe_close();
|
|
881
|
+
});
|
|
882
|
+
};
|
|
883
|
+
for (const { id, promise } of deferred) stream_one(id, promise);
|
|
884
|
+
maybe_close();
|
|
885
|
+
},
|
|
886
|
+
cancel() {
|
|
887
|
+
reader.cancel().catch(() => {});
|
|
888
|
+
}
|
|
889
|
+
});
|
|
890
|
+
const headers = new Headers(response.headers);
|
|
891
|
+
headers.delete("content-length");
|
|
892
|
+
return new Response(stream, {
|
|
893
|
+
status: response.status,
|
|
894
|
+
statusText: response.statusText,
|
|
895
|
+
headers
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* True when a remote's resolved value (deep) contains a region carrying baked SSR HTML — a
|
|
900
|
+
* page-sized render, not seed data (see the skip in {@link build_remote_seed_script}). Depth-capped:
|
|
901
|
+
* a region ticket sits shallow in any sane payload (a DocView's `entry.body` is 2 levels deep).
|
|
902
|
+
*/
|
|
903
|
+
has_baked_region(value, depth = 0) {
|
|
904
|
+
if (depth > 6 || value === null || typeof value !== "object") return false;
|
|
905
|
+
const r = value;
|
|
906
|
+
if (r[REGION_BRAND] === true && typeof r.html === "string") return true;
|
|
907
|
+
if (Array.isArray(value)) return value.some((x) => this.has_baked_region(x, depth + 1));
|
|
908
|
+
for (const k in r) if (this.has_baked_region(r[k], depth + 1)) return true;
|
|
909
|
+
return false;
|
|
910
|
+
}
|
|
911
|
+
async build_remote_seed_script(state, wanted = null) {
|
|
912
|
+
const implicit = state.remote?.implicit;
|
|
913
|
+
if (!implicit) return null;
|
|
914
|
+
const data = await collect_remote_seed(implicit, wanted, {
|
|
915
|
+
memo: (internals) => state.remote.data?.get(internals),
|
|
916
|
+
skip_value: (v) => this.has_baked_region(v),
|
|
917
|
+
key: create_remote_key
|
|
918
|
+
});
|
|
919
|
+
if (!Object.values(data).some((b) => Object.keys(b).length > 0)) return null;
|
|
920
|
+
const transport = state.transport || {};
|
|
921
|
+
const reducers = Object.fromEntries(Object.entries(transport).map(([name, codec]) => [name, codec.encode]));
|
|
922
|
+
return emit_ogygia_script("remote", devalue.stringify(data, reducers));
|
|
923
|
+
}
|
|
924
|
+
/**
|
|
925
|
+
* `render()` the island under the concurrency gate + timeout. Returns the HTML body, or `null`
|
|
926
|
+
* if the render threw or timed out. Shared by the endpoint ({@link render_region}) and the
|
|
927
|
+
* batch path ({@link #render_capability}).
|
|
928
|
+
*/
|
|
929
|
+
async #render_component(load, props, cache, report, timing, base, reads_page_data = false) {
|
|
930
|
+
const render_body = async () => {
|
|
931
|
+
const mod = await load();
|
|
932
|
+
const rendered = render(mod.default, {
|
|
933
|
+
props,
|
|
934
|
+
context: kit_render_context()
|
|
935
|
+
});
|
|
936
|
+
return await Promise.race([Promise.resolve(rendered).then((out) => {
|
|
937
|
+
const body = out.body;
|
|
938
|
+
if (body === "<!--ogygia:keep-fallback-->") return body;
|
|
939
|
+
return hole_head(out.head, base) + body + kit_page_facts_tail();
|
|
940
|
+
}), new Promise((_, rej) => setTimeout(() => rej(/* @__PURE__ */ new Error("region render timeout")), RENDER_TIMEOUT_MS))]);
|
|
941
|
+
};
|
|
942
|
+
try {
|
|
943
|
+
return await cached_render(async () => {
|
|
944
|
+
await kit_page_ready(reads_page_data);
|
|
945
|
+
if (!timing) return render_gate.run(render_body);
|
|
946
|
+
const asked = performance.now();
|
|
947
|
+
return render_gate.run(async () => {
|
|
948
|
+
const entered = performance.now();
|
|
949
|
+
timing.queue_ms = entered - asked;
|
|
950
|
+
try {
|
|
951
|
+
return await render_body();
|
|
952
|
+
} finally {
|
|
953
|
+
timing.render_ms = performance.now() - entered;
|
|
954
|
+
}
|
|
955
|
+
});
|
|
956
|
+
}, cache, Date.now(), report);
|
|
957
|
+
} catch (e) {
|
|
958
|
+
if (is_keep_fallback(e)) return KEEP_FALLBACK_HTML;
|
|
959
|
+
if (import.meta.env.DEV) console.warn("[ogygia] region render failed:", e);
|
|
960
|
+
return null;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
/** A cached hole's memo key: the call and the visitor's session, and — when its tree reads
|
|
964
|
+
* `page.data` — the page it renders for (one page's facts never answer another page's hole). */
|
|
965
|
+
#hole_cache_key(id, payload, event, reads_page_data) {
|
|
966
|
+
const session = this.#region_session(event);
|
|
967
|
+
return render_cache_key(id, payload, reads_page_data ? session + "\n" + page_url_of(event).href : session);
|
|
968
|
+
}
|
|
969
|
+
/** The session cookie value sealed into a region capability (empty when no `sessionCookie` is
|
|
970
|
+
* configured) — the same read the MAC verify uses, and the per-user part of the render-cache key. */
|
|
971
|
+
#region_session(event) {
|
|
972
|
+
return sessionCookie ? event.cookies.get(sessionCookie) ?? "" : "";
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* Every check a capability passes BEFORE any HMAC (P5-HMAC-CPU), in order: charset/length (ids
|
|
976
|
+
* are 12-hex from the transform; `ttl` is signed, but charset-gated here so a forged value never
|
|
977
|
+
* reaches a response header), expiry (a renewal instead needs one inside the renew window: a URL
|
|
978
|
+
* that has not expired never renews), and the signing key it names. Null when it passes, else why
|
|
979
|
+
* it is refused.
|
|
980
|
+
*/
|
|
981
|
+
#capability_precheck(cap, renewal = false) {
|
|
982
|
+
if (!is_region_id(cap.id) || cap.render !== "" && !is_region_id(cap.render) || cap.payload.length > 8192 || !is_region_ttl(cap.ttl)) return "shape";
|
|
983
|
+
const expiry = expiry_refusal(cap.exp, Math.floor(Date.now() / 1e3), renewal, app_lifetime.renew_window);
|
|
984
|
+
if (expiry) return expiry;
|
|
985
|
+
if (foreign_key(cap.k)) return "foreign-key";
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
/**
|
|
989
|
+
* RENEWAL (server/shared-cache.ts): re-sign an EXPIRED capability this app minted for an
|
|
990
|
+
* anonymous hole — the rescue for a hole on a document a cache kept longer than the hole's
|
|
991
|
+
* capability. Refused (null) unless the visitor carries no session and the MAC over the ORIGINAL
|
|
992
|
+
* tuple verifies with an empty session: nothing session-bound is ever renewed, and only a URL the
|
|
993
|
+
* app itself put in public HTML can be. The caller has already gated shape + the renew window
|
|
994
|
+
* and charged the probe budget. Returns the fresh `exp` + `sig`.
|
|
995
|
+
*/
|
|
996
|
+
#renew_capability(cap, event) {
|
|
997
|
+
if (this.#region_session(event) !== "") return null;
|
|
998
|
+
const { secret } = signing_key();
|
|
999
|
+
if (!verify(secret, region_mac_message(cap.id, cap.exp, cap.payload, "", cap.ttl, cap.render), cap.sig)) return null;
|
|
1000
|
+
const exp = String(app_lifetime.renewed_expiry());
|
|
1001
|
+
return {
|
|
1002
|
+
...cap,
|
|
1003
|
+
exp,
|
|
1004
|
+
sig: sign(secret, region_mac_message(cap.id, exp, cap.payload, "", cap.ttl, cap.render)),
|
|
1005
|
+
k: signing_key().fp
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
/** THE auth check: session-bound region MAC verify (its render identity inside, when it has one). */
|
|
1009
|
+
#verify_region_mac(cap, event) {
|
|
1010
|
+
const session = sessionCookie ? event.cookies.get(sessionCookie) ?? "" : "";
|
|
1011
|
+
return verify(signing_key().secret, region_mac_message(cap.id, cap.exp, cap.payload, session, cap.ttl, cap.render), cap.sig);
|
|
1012
|
+
}
|
|
1013
|
+
/** Eval the island module (registers transportable codecs the reviver needs on a cold-start defer),
|
|
1014
|
+
* then decode + revive the props payload. Null on parse failure or a non-object/array result. */
|
|
1015
|
+
async #decode_region_props(payload, load) {
|
|
1016
|
+
let props;
|
|
1017
|
+
try {
|
|
1018
|
+
await load();
|
|
1019
|
+
register_wire_kind();
|
|
1020
|
+
register_store_kind();
|
|
1021
|
+
register_snippet_kind();
|
|
1022
|
+
register_fn_kind();
|
|
1023
|
+
register_derived_kind();
|
|
1024
|
+
props = devalue.parse(B64Url.decode(payload), { [REF_WIRE_KEY]: ref_reviver(false) });
|
|
1025
|
+
} catch (e) {
|
|
1026
|
+
if (import.meta.env.DEV) console.warn("[ogygia] region endpoint 403: island module load / props decode failed", e);
|
|
1027
|
+
return null;
|
|
1028
|
+
}
|
|
1029
|
+
if (props === null || typeof props !== "object" || Array.isArray(props)) return null;
|
|
1030
|
+
return props;
|
|
1031
|
+
}
|
|
1032
|
+
/**
|
|
1033
|
+
* Batch (single-flight navigation): verify a hole's OWN signed capability URL and render it in-process. Same
|
|
1034
|
+
* trust boundary as the endpoint (verify the MAC before touching the manifest), but every failure —
|
|
1035
|
+
* bad/expired MAC, unknown id, non-serializable props, render error, oversize — resolves to
|
|
1036
|
+
* `null` so the hole silently falls back to a client fetch. Never throws.
|
|
1037
|
+
*/
|
|
1038
|
+
async #render_capability(endpoint, event, batch = false) {
|
|
1039
|
+
const q = endpoint.indexOf("?");
|
|
1040
|
+
if (q === -1) return null;
|
|
1041
|
+
const cap = capability_of(new URLSearchParams(endpoint.slice(q + 1)));
|
|
1042
|
+
const { id, payload, sig } = cap;
|
|
1043
|
+
if (this.#capability_precheck(cap)) return null;
|
|
1044
|
+
if (!this.#verify_region_mac(cap, event)) return null;
|
|
1045
|
+
const target = capability_target(cap);
|
|
1046
|
+
if (target === null) return null;
|
|
1047
|
+
const load = (await load_islands()).islands[target];
|
|
1048
|
+
if (typeof load !== "function") return null;
|
|
1049
|
+
const props = await this.#decode_region_props(payload, load);
|
|
1050
|
+
if (!props) return null;
|
|
1051
|
+
const ttl = Number(cap.ttl) || 0;
|
|
1052
|
+
const reads_page_data = hole_reads_page_data(target);
|
|
1053
|
+
const cache = ttl > 0 ? {
|
|
1054
|
+
key: this.#hole_cache_key(target, payload, event, reads_page_data),
|
|
1055
|
+
ttl
|
|
1056
|
+
} : void 0;
|
|
1057
|
+
const stats = batch && has_batch_hole_listener();
|
|
1058
|
+
const t0 = stats ? performance.now() : 0;
|
|
1059
|
+
const timing = {};
|
|
1060
|
+
let outcome = "none";
|
|
1061
|
+
const body = await this.#render_component(load, props, cache, stats ? (o) => outcome = o : void 0, stats ? timing : void 0, event.url, reads_page_data);
|
|
1062
|
+
if (stats) record_batch_hole_stats(event.request, {
|
|
1063
|
+
kind: "hole",
|
|
1064
|
+
id,
|
|
1065
|
+
cache: outcome,
|
|
1066
|
+
ttl,
|
|
1067
|
+
...timing.queue_ms !== void 0 ? { queue_ms: Math.round(timing.queue_ms * 10) / 10 } : {},
|
|
1068
|
+
...island_name?.[target] ? { name: island_name[target] } : {},
|
|
1069
|
+
p: hole_copy_key(payload),
|
|
1070
|
+
ms: performance.now() - t0,
|
|
1071
|
+
status: body === null ? 500 : 200
|
|
1072
|
+
});
|
|
1073
|
+
if (body === null || body.length > MAX_REGION_BODY) return null;
|
|
1074
|
+
if (body === "<!--ogygia:keep-fallback-->") return {
|
|
1075
|
+
slot: sig,
|
|
1076
|
+
html: KEEP_FALLBACK_HTML
|
|
1077
|
+
};
|
|
1078
|
+
let html = absolutize_hole_html(region_css_links(target) + body, event.url);
|
|
1079
|
+
if (batch && this.#transform) html = await this.#transform_region(html, event);
|
|
1080
|
+
return {
|
|
1081
|
+
slot: sig,
|
|
1082
|
+
html
|
|
1083
|
+
};
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Verify the region MAC, then render. Unknown ids and bad MACs both return 403 so the
|
|
1087
|
+
* existence of a region id is not an oracle (SEC-01). Expiry is part of the MAC message.
|
|
1088
|
+
*
|
|
1089
|
+
* Ordering: length/exp → probe rate (pre-HMAC) → verify → render rate → render.
|
|
1090
|
+
* Probe stops forged CPU amplification; render budget is only charged after a valid MAC.
|
|
1091
|
+
* A renewal (`&renew=1`) swaps the first three for its own: shape + renew window → probe →
|
|
1092
|
+
* anonymous-only re-verify of the original tuple + re-sign; then the same render rate + render.
|
|
1093
|
+
*/
|
|
1094
|
+
async render_region(event) {
|
|
1095
|
+
const method = event.request.method.toUpperCase();
|
|
1096
|
+
if (method !== "GET" && method !== "HEAD") return region_response("Method Not Allowed", {
|
|
1097
|
+
status: 405,
|
|
1098
|
+
headers: { allow: "GET, HEAD" }
|
|
1099
|
+
});
|
|
1100
|
+
if (event.request.headers.get("sec-fetch-site") === "cross-site") return region_denied("cross-site");
|
|
1101
|
+
const url = event.url;
|
|
1102
|
+
const ip = client_ip(event);
|
|
1103
|
+
if (!ip) return region_response("Too Many Requests", { status: 429 });
|
|
1104
|
+
let cap = capability_of(url.searchParams);
|
|
1105
|
+
const { id, payload } = cap;
|
|
1106
|
+
const ttl_raw = cap.ttl;
|
|
1107
|
+
let renewed = null;
|
|
1108
|
+
const renewal = url.searchParams.get("renew") === "1";
|
|
1109
|
+
const refused = this.#capability_precheck(cap, renewal);
|
|
1110
|
+
if (refused) {
|
|
1111
|
+
if (import.meta.env.DEV && refused === "foreign-key") console.warn(`[ogygia] region endpoint 403: id "${id}" was signed by key ${cap.k}, this server signs with ${signing_key().fp}`);
|
|
1112
|
+
return region_denied(refused);
|
|
1113
|
+
}
|
|
1114
|
+
if (this.probe_rate.limited(ip)) return region_response("Too Many Requests", { status: 429 });
|
|
1115
|
+
if (renewal) {
|
|
1116
|
+
const fresh = this.#renew_capability(cap, event);
|
|
1117
|
+
if (!fresh) return region_denied(this.#region_session(event) !== "" ? "renew-session" : "sig");
|
|
1118
|
+
cap = fresh;
|
|
1119
|
+
renewed = `${url.pathname}?id=${encodeURIComponent(id)}${cap.render ? `&r=${cap.render}` : ""}&props=${payload}&exp=${cap.exp}${ttl_raw ? `&ttl=${ttl_raw}` : ""}&sig=${cap.sig}&k=${cap.k}`;
|
|
1120
|
+
} else if (!this.#verify_region_mac(cap, event)) {
|
|
1121
|
+
if (import.meta.env.DEV) console.warn(`[ogygia] region endpoint 403: bad signature for id "${id}"`);
|
|
1122
|
+
return region_denied("sig");
|
|
1123
|
+
}
|
|
1124
|
+
const cache_control = ttl_raw && !renewed ? `private, max-age=${Number(ttl_raw)}` : "no-store";
|
|
1125
|
+
/** The fresh capability rides every successful answer to a renewal (the 204 too). */
|
|
1126
|
+
const renew_headers = renewed ? { "x-ogygia-capability": renewed } : {};
|
|
1127
|
+
if (this.render_rate.limited(ip)) return region_response("Too Many Requests", { status: 429 });
|
|
1128
|
+
const target = capability_target(cap);
|
|
1129
|
+
if (target === null) {
|
|
1130
|
+
if (import.meta.env.DEV) console.warn(`[ogygia] region endpoint 403: id "${id}"${cap.render ? ` (component ${cap.render})` : ""} is not in the server manifest (${Object.keys(island_url).length} known)`);
|
|
1131
|
+
return region_denied("unknown-id");
|
|
1132
|
+
}
|
|
1133
|
+
const load = (await load_islands()).islands[target];
|
|
1134
|
+
if (typeof load !== "function") return region_denied("unknown-id");
|
|
1135
|
+
const props = await this.#decode_region_props(payload, load);
|
|
1136
|
+
if (!props) return region_denied("props");
|
|
1137
|
+
const ttl = Number(ttl_raw) || 0;
|
|
1138
|
+
const reads_page_data = hole_reads_page_data(target);
|
|
1139
|
+
const cache = ttl > 0 ? {
|
|
1140
|
+
key: this.#hole_cache_key(target, payload, event, reads_page_data),
|
|
1141
|
+
ttl
|
|
1142
|
+
} : void 0;
|
|
1143
|
+
const timing = {};
|
|
1144
|
+
let outcome = "none";
|
|
1145
|
+
const body = await this.#render_component(load, props, cache, (o) => outcome = o, timing, event.url, reads_page_data);
|
|
1146
|
+
record_hole_stats(event.request, {
|
|
1147
|
+
kind: "hole",
|
|
1148
|
+
id,
|
|
1149
|
+
cache: outcome,
|
|
1150
|
+
ttl,
|
|
1151
|
+
...timing.queue_ms !== void 0 ? { queue_ms: Math.round(timing.queue_ms * 10) / 10 } : {},
|
|
1152
|
+
...island_name?.[target] ? { name: island_name[target] } : {},
|
|
1153
|
+
p: hole_copy_key(payload)
|
|
1154
|
+
});
|
|
1155
|
+
const server_timing = timing.queue_ms !== void 0 && (import.meta.env.DEV || DEVTOOLS && event.cookies.get("og_devtools") === "on" || event.cookies.get("og_profiler_beacon") === "1") ? { "server-timing": `og-queue;dur=${timing.queue_ms.toFixed(1)}, og-render;dur=${(timing.render_ms ?? 0).toFixed(1)}` } : {};
|
|
1156
|
+
if (body === null) return region_response("Region render failed", { status: 500 });
|
|
1157
|
+
if (body === "<!--ogygia:keep-fallback-->") return region_response(null, {
|
|
1158
|
+
status: 204,
|
|
1159
|
+
headers: renewed ? {
|
|
1160
|
+
"cache-control": "no-store",
|
|
1161
|
+
...renew_headers
|
|
1162
|
+
} : void 0
|
|
1163
|
+
});
|
|
1164
|
+
if (body.length > MAX_REGION_BODY) return region_denied("oversize");
|
|
1165
|
+
let html = absolutize_hole_html(region_css_links(target) + body, event.url);
|
|
1166
|
+
if (this.#transform) html = await this.#transform_region(html, event);
|
|
1167
|
+
if (method === "HEAD") return region_response(null, {
|
|
1168
|
+
status: 200,
|
|
1169
|
+
headers: {
|
|
1170
|
+
"content-type": "text/html; charset=utf-8",
|
|
1171
|
+
"content-length": String(new TextEncoder().encode(html).byteLength),
|
|
1172
|
+
"cache-control": cache_control,
|
|
1173
|
+
...renew_headers
|
|
1174
|
+
}
|
|
1175
|
+
});
|
|
1176
|
+
return region_response(html, {
|
|
1177
|
+
status: 200,
|
|
1178
|
+
headers: {
|
|
1179
|
+
"content-type": "text/html; charset=utf-8",
|
|
1180
|
+
"cache-control": cache_control,
|
|
1181
|
+
...renew_headers,
|
|
1182
|
+
...server_timing
|
|
1183
|
+
}
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
function handle(options = {}) {
|
|
1188
|
+
const instance = new OgygiaHandle(options);
|
|
1189
|
+
return (args) => instance.handle(args);
|
|
1190
|
+
}
|
|
1191
|
+
export { document, handle, liftRegions, restoreRegions, scanRegions, ogygiaTransport as transport };
|