@timber-js/app 0.2.0-alpha.7 → 0.2.0-alpha.71
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 +8 -0
- package/dist/_chunks/{als-registry-B7DbZ2hS.js → als-registry-BJARkOcu.js} +1 -1
- package/dist/_chunks/als-registry-BJARkOcu.js.map +1 -0
- package/dist/_chunks/chunk-DYhsFzuS.js +33 -0
- package/dist/_chunks/{debug-gwlJkDuf.js → debug-ECi_61pb.js} +2 -2
- package/dist/_chunks/debug-ECi_61pb.js.map +1 -0
- package/dist/_chunks/define-CGuYoRHU.js +199 -0
- package/dist/_chunks/define-CGuYoRHU.js.map +1 -0
- package/dist/_chunks/define-Dz1bqwaS.js +106 -0
- package/dist/_chunks/define-Dz1bqwaS.js.map +1 -0
- package/dist/_chunks/define-cookie-B5mewxwM.js +93 -0
- package/dist/_chunks/define-cookie-B5mewxwM.js.map +1 -0
- package/dist/_chunks/error-boundary-D9hzsveV.js +216 -0
- package/dist/_chunks/error-boundary-D9hzsveV.js.map +1 -0
- package/dist/_chunks/{format-DviM89f0.js → format-Rn922VH2.js} +3 -20
- package/dist/_chunks/format-Rn922VH2.js.map +1 -0
- package/dist/_chunks/{tracing-Cwn7697K.js → handler-store-BVePM7hp.js} +68 -3
- package/dist/_chunks/handler-store-BVePM7hp.js.map +1 -0
- package/dist/_chunks/{interception-BOoWmLUA.js → interception-CEdHHviP.js} +171 -97
- package/dist/_chunks/interception-CEdHHviP.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → metadata-routes-DS3eKNmf.js} +1 -1
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js.map → metadata-routes-DS3eKNmf.js.map} +1 -1
- package/dist/_chunks/{request-context-DIkVh_jG.js → request-context-CywiO4jV.js} +181 -69
- package/dist/_chunks/request-context-CywiO4jV.js.map +1 -0
- package/dist/_chunks/schema-bridge-C4SwjCQD.js +86 -0
- package/dist/_chunks/schema-bridge-C4SwjCQD.js.map +1 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js +65 -0
- package/dist/_chunks/segment-classify-BDNn6EzD.js.map +1 -0
- package/dist/_chunks/segment-context-hzuJ048X.js +72 -0
- package/dist/_chunks/segment-context-hzuJ048X.js.map +1 -0
- package/dist/_chunks/stale-reload-BLUC_Pl_.js +64 -0
- package/dist/_chunks/stale-reload-BLUC_Pl_.js.map +1 -0
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-DAhgj8Gx.js} +1 -1
- package/dist/_chunks/use-query-states-DAhgj8Gx.js.map +1 -0
- package/dist/_chunks/wrappers-LZbghvn0.js +63 -0
- package/dist/_chunks/wrappers-LZbghvn0.js.map +1 -0
- package/dist/adapters/cloudflare-dev.d.ts +109 -0
- package/dist/adapters/cloudflare-dev.d.ts.map +1 -0
- package/dist/adapters/cloudflare-dev.js +73 -0
- package/dist/adapters/cloudflare-dev.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +148 -12
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +135 -11
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +56 -13
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.d.ts.map +1 -0
- package/dist/cache/fast-hash.d.ts +22 -0
- package/dist/cache/fast-hash.d.ts.map +1 -0
- package/dist/cache/handler-store.d.ts +31 -0
- package/dist/cache/handler-store.d.ts.map +1 -0
- package/dist/cache/index.d.ts +7 -5
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +111 -73
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/timber-cache.d.ts +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.d.ts +12 -5
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +1 -125
- package/dist/client/error-reconstituter.d.ts +54 -0
- package/dist/client/error-reconstituter.d.ts.map +1 -0
- package/dist/client/form.d.ts +2 -2
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/history.d.ts +19 -4
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +6 -5
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +537 -166
- package/dist/client/index.js.map +1 -1
- package/dist/client/link-pending-store.d.ts +78 -0
- package/dist/client/link-pending-store.d.ts.map +1 -0
- package/dist/client/link.d.ts +90 -32
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/nav-link-store.d.ts +36 -0
- package/dist/client/nav-link-store.d.ts.map +1 -0
- package/dist/client/navigation-api-types.d.ts +90 -0
- package/dist/client/navigation-api-types.d.ts.map +1 -0
- package/dist/client/navigation-api.d.ts +115 -0
- package/dist/client/navigation-api.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +13 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/{transition-root.d.ts → navigation-root.d.ts} +42 -8
- package/dist/client/navigation-root.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts.map +1 -1
- package/dist/client/router.d.ts +70 -4
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +38 -3
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +1 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-context.d.ts +1 -1
- package/dist/client/segment-context.d.ts.map +1 -1
- package/dist/client/segment-merger.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +63 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/ssr-data.d.ts +13 -4
- package/dist/client/ssr-data.d.ts.map +1 -1
- package/dist/client/stale-reload.d.ts +15 -0
- package/dist/client/stale-reload.d.ts.map +1 -1
- package/dist/client/top-loader.d.ts +3 -3
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/use-params.d.ts +6 -4
- package/dist/client/use-params.d.ts.map +1 -1
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/codec.d.ts +23 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +2 -0
- package/dist/cookies/define-cookie.d.ts +35 -14
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.d.ts +2 -0
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/cookies/index.js +3 -84
- package/dist/fonts/css.d.ts +1 -0
- package/dist/fonts/css.d.ts.map +1 -1
- package/dist/index.d.ts +154 -38
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12092 -11916
- package/dist/index.js.map +1 -1
- package/dist/plugins/adapter-build.d.ts +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +2 -2
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +3 -3
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/client-chunks.d.ts +32 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/content.d.ts +1 -1
- package/dist/plugins/content.d.ts.map +1 -1
- package/dist/plugins/dev-browser-logs.d.ts +84 -0
- package/dist/plugins/dev-browser-logs.d.ts.map +1 -0
- package/dist/plugins/dev-error-overlay.d.ts +26 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +1 -1
- package/dist/plugins/dev-logs.d.ts +1 -1
- package/dist/plugins/dev-logs.d.ts.map +1 -1
- package/dist/plugins/dev-server.d.ts +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/plugins/entries.d.ts +1 -1
- package/dist/plugins/entries.d.ts.map +1 -1
- package/dist/plugins/fonts.d.ts +19 -5
- package/dist/plugins/fonts.d.ts.map +1 -1
- package/dist/plugins/mdx.d.ts +1 -1
- package/dist/plugins/mdx.d.ts.map +1 -1
- package/dist/plugins/routing.d.ts +1 -1
- package/dist/plugins/routing.d.ts.map +1 -1
- package/dist/plugins/server-bundle.d.ts.map +1 -1
- package/dist/plugins/shims.d.ts +6 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +1 -1
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen.d.ts +2 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/index.d.ts +2 -0
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +3 -2
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/segment-classify.d.ts +46 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/status-file-lint.d.ts +2 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/routing/types.d.ts +16 -4
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/rsc-runtime/rsc.d.ts +1 -1
- package/dist/rsc-runtime/rsc.d.ts.map +1 -1
- package/dist/rsc-runtime/ssr.d.ts +12 -0
- package/dist/rsc-runtime/ssr.d.ts.map +1 -1
- package/dist/schema-bridge.d.ts +76 -0
- package/dist/schema-bridge.d.ts.map +1 -0
- package/dist/search-params/define.d.ts +139 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -6
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +4 -474
- package/dist/search-params/registry.d.ts +1 -1
- package/dist/search-params/wrappers.d.ts +53 -0
- package/dist/search-params/wrappers.d.ts.map +1 -0
- package/dist/segment-params/define.d.ts +78 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +7 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +4 -0
- package/dist/server/access-gate.d.ts +4 -0
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +12 -1
- package/dist/server/action-client.d.ts.map +1 -1
- package/dist/server/action-encryption.d.ts +76 -0
- package/dist/server/action-encryption.d.ts.map +1 -0
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +3 -6
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +32 -4
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +2 -2
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/debug.d.ts +1 -1
- package/dist/server/default-logger.d.ts +22 -0
- package/dist/server/default-logger.d.ts.map +1 -0
- package/dist/server/deny-page-resolver.d.ts +52 -0
- package/dist/server/deny-page-resolver.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-warnings.d.ts +0 -14
- package/dist/server/dev-warnings.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -5
- package/dist/server/early-hints.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +7 -1
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/fallback-error.d.ts +4 -3
- package/dist/server/fallback-error.d.ts.map +1 -1
- package/dist/server/flight-injection-state.d.ts +66 -0
- package/dist/server/flight-injection-state.d.ts.map +1 -0
- package/dist/server/flight-scripts.d.ts +42 -0
- package/dist/server/flight-scripts.d.ts.map +1 -0
- package/dist/server/flush.d.ts.map +1 -1
- package/dist/server/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injectors.d.ts +51 -11
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -3
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2176 -1663
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts +25 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +19 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +113 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/page-deny-boundary.d.ts +31 -0
- package/dist/server/page-deny-boundary.d.ts.map +1 -0
- package/dist/server/pipeline-interception.d.ts +1 -1
- package/dist/server/pipeline-interception.d.ts.map +1 -1
- package/dist/server/pipeline-metadata.d.ts +6 -0
- package/dist/server/pipeline-metadata.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +32 -10
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +30 -3
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/render-timeout.d.ts +51 -0
- package/dist/server/render-timeout.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +76 -37
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +27 -1
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-handler.d.ts.map +1 -1
- package/dist/server/route-matcher.d.ts +9 -2
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/api-handler.d.ts +2 -2
- package/dist/server/rsc-entry/api-handler.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts +26 -13
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/helpers.d.ts +48 -5
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +8 -3
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +3 -3
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +10 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +19 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/sitemap-generator.d.ts +129 -0
- package/dist/server/sitemap-generator.d.ts.map +1 -0
- package/dist/server/sitemap-handler.d.ts +22 -0
- package/dist/server/sitemap-handler.d.ts.map +1 -0
- package/dist/server/slot-resolver.d.ts +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-entry.d.ts +22 -0
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +39 -21
- package/dist/server/ssr-render.d.ts.map +1 -1
- package/dist/server/ssr-wrappers.d.ts +50 -0
- package/dist/server/ssr-wrappers.d.ts.map +1 -0
- package/dist/server/status-code-resolver.d.ts +1 -1
- package/dist/server/status-code-resolver.d.ts.map +1 -1
- package/dist/server/stream-utils.d.ts +36 -0
- package/dist/server/stream-utils.d.ts.map +1 -0
- package/dist/server/tracing.d.ts +10 -0
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +22 -19
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +1 -4
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/server/waituntil-bridge.d.ts.map +1 -1
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.d.ts.map +1 -1
- package/dist/shims/font-google.js +42 -0
- package/dist/shims/font-google.js.map +1 -0
- package/dist/shims/font-local.d.ts +26 -0
- package/dist/shims/font-local.d.ts.map +1 -0
- package/dist/shims/font-local.js +20 -0
- package/dist/shims/font-local.js.map +1 -0
- package/dist/shims/navigation-client.d.ts +1 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/utils/directive-parser.d.ts +5 -2
- package/dist/utils/directive-parser.d.ts.map +1 -1
- package/dist/utils/state-machine.d.ts +80 -0
- package/dist/utils/state-machine.d.ts.map +1 -0
- package/package.json +37 -17
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare.ts +342 -28
- package/src/adapters/compress-module.ts +24 -4
- package/src/adapters/nitro.ts +58 -9
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +38 -0
- package/src/cache/fast-hash.ts +34 -0
- package/src/cache/handler-store.ts +68 -0
- package/src/cache/index.ts +9 -5
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/timber-cache.ts +40 -29
- package/src/cli.ts +0 -0
- package/src/client/browser-entry.ts +314 -142
- package/src/client/error-boundary.tsx +48 -16
- package/src/client/error-reconstituter.tsx +65 -0
- package/src/client/form.tsx +2 -2
- package/src/client/history.ts +26 -4
- package/src/client/index.ts +13 -4
- package/src/client/link-pending-store.ts +136 -0
- package/src/client/link.tsx +346 -105
- package/src/client/nav-link-store.ts +47 -0
- package/src/client/navigation-api-types.ts +112 -0
- package/src/client/navigation-api.ts +332 -0
- package/src/client/navigation-context.ts +27 -6
- package/src/client/navigation-root.tsx +346 -0
- package/src/client/nuqs-adapter.tsx +16 -3
- package/src/client/router.ts +302 -77
- package/src/client/rsc-fetch.ts +93 -5
- package/src/client/segment-cache.ts +1 -1
- package/src/client/segment-context.ts +6 -1
- package/src/client/segment-merger.ts +2 -8
- package/src/client/segment-outlet.tsx +86 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/stale-reload.ts +73 -6
- package/src/client/top-loader.tsx +22 -13
- package/src/client/use-navigation-pending.ts +1 -1
- package/src/client/use-params.ts +7 -5
- package/src/client/use-query-states.ts +2 -2
- package/src/codec.ts +34 -0
- package/src/cookies/define-cookie.ts +72 -21
- package/src/cookies/index.ts +7 -0
- package/src/fonts/css.ts +2 -1
- package/src/index.ts +328 -92
- package/src/plugins/adapter-build.ts +8 -2
- package/src/plugins/build-manifest.ts +13 -2
- package/src/plugins/build-report.ts +3 -3
- package/src/plugins/client-chunks.ts +65 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-browser-logs.ts +288 -0
- package/src/plugins/dev-error-overlay.ts +70 -1
- package/src/plugins/dev-logs.ts +1 -1
- package/src/plugins/dev-server.ts +55 -9
- package/src/plugins/entries.ts +70 -9
- package/src/plugins/fonts.ts +167 -61
- package/src/plugins/mdx.ts +1 -1
- package/src/plugins/routing.ts +57 -17
- package/src/plugins/server-action-exports.ts +1 -1
- package/src/plugins/server-bundle.ts +32 -1
- package/src/plugins/shims.ts +76 -33
- package/src/plugins/static-build.ts +10 -6
- package/src/routing/codegen.ts +165 -105
- package/src/routing/index.ts +2 -0
- package/src/routing/scanner.ts +93 -23
- package/src/routing/segment-classify.ts +89 -0
- package/src/routing/status-file-lint.ts +3 -2
- package/src/routing/types.ts +17 -4
- package/src/rsc-runtime/rsc.ts +2 -0
- package/src/rsc-runtime/ssr.ts +50 -0
- package/src/rsc-runtime/vendor-types.d.ts +7 -0
- package/src/{search-params/codecs.ts → schema-bridge.ts} +57 -20
- package/src/search-params/define.ts +482 -0
- package/src/search-params/index.ts +13 -19
- package/src/search-params/registry.ts +1 -1
- package/src/search-params/wrappers.ts +85 -0
- package/src/segment-params/define.ts +279 -0
- package/src/segment-params/index.ts +28 -0
- package/src/server/access-gate.tsx +70 -29
- package/src/server/action-client.ts +28 -3
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +20 -3
- package/src/server/actions.ts +10 -9
- package/src/server/als-registry.ts +32 -4
- package/src/server/build-manifest.ts +10 -4
- package/src/server/compress.ts +25 -7
- package/src/server/debug.ts +1 -1
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-page-resolver.ts +154 -0
- package/src/server/deny-renderer.ts +24 -38
- package/src/server/dev-warnings.ts +2 -28
- package/src/server/early-hints.ts +36 -15
- package/src/server/error-boundary-wrapper.ts +74 -22
- package/src/server/fallback-error.ts +31 -15
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/flush.ts +2 -1
- package/src/server/form-data.ts +76 -0
- package/src/server/html-injectors.ts +277 -117
- package/src/server/index.ts +9 -5
- package/src/server/logger.ts +44 -36
- package/src/server/middleware-runner.ts +31 -4
- package/src/server/node-stream-transforms.ts +509 -0
- package/src/server/page-deny-boundary.tsx +56 -0
- package/src/server/pipeline-interception.ts +17 -16
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline.ts +195 -51
- package/src/server/primitives.ts +47 -5
- package/src/server/render-timeout.ts +108 -0
- package/src/server/request-context.ts +240 -117
- package/src/server/route-element-builder.ts +284 -197
- package/src/server/route-handler.ts +24 -4
- package/src/server/route-matcher.ts +24 -20
- package/src/server/rsc-entry/api-handler.ts +15 -16
- package/src/server/rsc-entry/error-renderer.ts +300 -89
- package/src/server/rsc-entry/helpers.ts +134 -5
- package/src/server/rsc-entry/index.ts +202 -113
- package/src/server/rsc-entry/rsc-payload.ts +100 -21
- package/src/server/rsc-entry/rsc-stream.ts +74 -18
- package/src/server/rsc-entry/ssr-bridge.ts +14 -5
- package/src/server/rsc-entry/ssr-renderer.ts +173 -40
- package/src/server/safe-load.ts +60 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +126 -0
- package/src/server/slot-resolver.ts +243 -228
- package/src/server/ssr-entry.ts +211 -32
- package/src/server/ssr-render.ts +289 -67
- package/src/server/ssr-wrappers.tsx +139 -0
- package/src/server/status-code-resolver.ts +1 -1
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +37 -3
- package/src/server/tree-builder.ts +92 -58
- package/src/server/types.ts +3 -6
- package/src/server/version-skew.ts +104 -0
- package/src/server/waituntil-bridge.ts +4 -1
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +34 -0
- package/src/shims/navigation-client.ts +1 -1
- package/src/shims/navigation.ts +2 -1
- package/src/utils/directive-parser.ts +5 -2
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-gwlJkDuf.js.map +0 -1
- package/dist/_chunks/format-DviM89f0.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/request-context-DIkVh_jG.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js +0 -88
- package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/tracing-Cwn7697K.js.map +0 -1
- package/dist/_chunks/use-cookie-DX-l1_5E.js +0 -91
- package/dist/_chunks/use-cookie-DX-l1_5E.js.map +0 -1
- package/dist/_chunks/use-query-states-D5KaffOK.js.map +0 -1
- package/dist/cache/register-cached-function.d.ts +0 -17
- package/dist/cache/register-cached-function.d.ts.map +0 -1
- package/dist/client/error-boundary.js.map +0 -1
- package/dist/client/link-status-provider.d.ts +0 -11
- package/dist/client/link-status-provider.d.ts.map +0 -1
- package/dist/client/transition-root.d.ts.map +0 -1
- package/dist/cookies/index.js.map +0 -1
- package/dist/plugins/cache-transform.d.ts +0 -36
- package/dist/plugins/cache-transform.d.ts.map +0 -1
- package/dist/plugins/dynamic-transform.d.ts +0 -72
- package/dist/plugins/dynamic-transform.d.ts.map +0 -1
- package/dist/search-params/analyze.d.ts +0 -54
- package/dist/search-params/analyze.d.ts.map +0 -1
- package/dist/search-params/builtin-codecs.d.ts +0 -105
- package/dist/search-params/builtin-codecs.d.ts.map +0 -1
- package/dist/search-params/codecs.d.ts +0 -53
- package/dist/search-params/codecs.d.ts.map +0 -1
- package/dist/search-params/create.d.ts +0 -106
- package/dist/search-params/create.d.ts.map +0 -1
- package/dist/search-params/index.js.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/server/response-cache.d.ts +0 -53
- package/dist/server/response-cache.d.ts.map +0 -1
- package/src/cache/register-cached-function.ts +0 -99
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/transition-root.tsx +0 -160
- package/src/plugins/cache-transform.ts +0 -199
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/create.ts +0 -321
- package/src/server/prerender.ts +0 -139
- package/src/server/response-cache.ts +0 -277
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createContext, createElement, useContext, useMemo } from "react";
|
|
2
|
+
//#region src/shared/merge-search-params.ts
|
|
3
|
+
/**
|
|
4
|
+
* Shared utility for merging preserved search params into a target URL.
|
|
5
|
+
*
|
|
6
|
+
* Used by both <Link> (client) and redirect() (server). Extracted to a shared
|
|
7
|
+
* module to avoid importing client code ('use client') from server modules.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Merge preserved search params from the current URL into a target href.
|
|
11
|
+
*
|
|
12
|
+
* When `preserve` is `true`, all current search params are merged.
|
|
13
|
+
* When `preserve` is a `string[]`, only the named params are merged.
|
|
14
|
+
*
|
|
15
|
+
* The target href's own search params take precedence — preserved params
|
|
16
|
+
* are only added if the target doesn't already define them.
|
|
17
|
+
*
|
|
18
|
+
* @param targetHref - The resolved target href (may already contain query string)
|
|
19
|
+
* @param currentSearch - The current URL's search string (e.g. "?private=access&page=2")
|
|
20
|
+
* @param preserve - `true` to preserve all, or `string[]` to preserve specific params
|
|
21
|
+
* @returns The target href with preserved search params merged in
|
|
22
|
+
*/
|
|
23
|
+
function mergePreservedSearchParams(targetHref, currentSearch, preserve) {
|
|
24
|
+
const currentParams = new URLSearchParams(currentSearch);
|
|
25
|
+
if (currentParams.size === 0) return targetHref;
|
|
26
|
+
const hashIdx = targetHref.indexOf("#");
|
|
27
|
+
const hrefWithoutHash = hashIdx >= 0 ? targetHref.slice(0, hashIdx) : targetHref;
|
|
28
|
+
const hash = hashIdx >= 0 ? targetHref.slice(hashIdx) : "";
|
|
29
|
+
const qIdx = hrefWithoutHash.indexOf("?");
|
|
30
|
+
const targetPath = qIdx >= 0 ? hrefWithoutHash.slice(0, qIdx) : hrefWithoutHash;
|
|
31
|
+
const targetParams = new URLSearchParams(qIdx >= 0 ? hrefWithoutHash.slice(qIdx + 1) : "");
|
|
32
|
+
const merged = new URLSearchParams(targetParams);
|
|
33
|
+
for (const [key, value] of currentParams) if (!targetParams.has(key)) {
|
|
34
|
+
if (preserve === true || preserve.includes(key)) merged.append(key, value);
|
|
35
|
+
}
|
|
36
|
+
const qs = merged.toString();
|
|
37
|
+
return (qs ? `${targetPath}?${qs}` : targetPath) + hash;
|
|
38
|
+
}
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/client/segment-context.ts
|
|
41
|
+
/**
|
|
42
|
+
* Segment Context — provides layout segment position for useSelectedLayoutSegment hooks.
|
|
43
|
+
*
|
|
44
|
+
* Each layout in the segment tree is wrapped with a SegmentProvider that stores
|
|
45
|
+
* the URL segments from root to the current layout level. The hooks read this
|
|
46
|
+
* context to determine which child segments are active below the calling layout.
|
|
47
|
+
*
|
|
48
|
+
* The context value is intentionally minimal: just the segment path array and
|
|
49
|
+
* parallel route keys. No internal cache details are exposed.
|
|
50
|
+
*
|
|
51
|
+
* Design docs: design/19-client-navigation.md, design/14-ecosystem.md
|
|
52
|
+
*/
|
|
53
|
+
var SegmentContext = createContext(null);
|
|
54
|
+
/** Read the segment context. Returns null if no provider is above this component. */
|
|
55
|
+
function useSegmentContext() {
|
|
56
|
+
return useContext(SegmentContext);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Wraps each layout to provide segment position context.
|
|
60
|
+
* Injected by rsc-entry.ts during element tree construction.
|
|
61
|
+
*/
|
|
62
|
+
function SegmentProvider({ segments, segmentId: _segmentId, parallelRouteKeys, children }) {
|
|
63
|
+
const value = useMemo(() => ({
|
|
64
|
+
segments,
|
|
65
|
+
parallelRouteKeys
|
|
66
|
+
}), [segments.join("/"), parallelRouteKeys.join(",")]);
|
|
67
|
+
return createElement(SegmentContext.Provider, { value }, children);
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { useSegmentContext as n, mergePreservedSearchParams as r, SegmentProvider as t };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=segment-context-hzuJ048X.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"segment-context-hzuJ048X.js","names":[],"sources":["../../src/shared/merge-search-params.ts","../../src/client/segment-context.ts"],"sourcesContent":["/**\n * Shared utility for merging preserved search params into a target URL.\n *\n * Used by both <Link> (client) and redirect() (server). Extracted to a shared\n * module to avoid importing client code ('use client') from server modules.\n */\n\n/**\n * Merge preserved search params from the current URL into a target href.\n *\n * When `preserve` is `true`, all current search params are merged.\n * When `preserve` is a `string[]`, only the named params are merged.\n *\n * The target href's own search params take precedence — preserved params\n * are only added if the target doesn't already define them.\n *\n * @param targetHref - The resolved target href (may already contain query string)\n * @param currentSearch - The current URL's search string (e.g. \"?private=access&page=2\")\n * @param preserve - `true` to preserve all, or `string[]` to preserve specific params\n * @returns The target href with preserved search params merged in\n */\nexport function mergePreservedSearchParams(\n targetHref: string,\n currentSearch: string,\n preserve: true | string[]\n): string {\n const currentParams = new URLSearchParams(currentSearch);\n if (currentParams.size === 0) return targetHref;\n\n // Split hash fragment from target before processing query params.\n // Hash must come after query string: /path?query=value#hash\n const hashIdx = targetHref.indexOf('#');\n const hrefWithoutHash = hashIdx >= 0 ? targetHref.slice(0, hashIdx) : targetHref;\n const hash = hashIdx >= 0 ? targetHref.slice(hashIdx) : '';\n\n // Split target into path and existing query\n const qIdx = hrefWithoutHash.indexOf('?');\n const targetPath = qIdx >= 0 ? hrefWithoutHash.slice(0, qIdx) : hrefWithoutHash;\n const targetParams = new URLSearchParams(qIdx >= 0 ? hrefWithoutHash.slice(qIdx + 1) : '');\n\n // Collect params to preserve (that aren't already in the target)\n const merged = new URLSearchParams(targetParams);\n for (const [key, value] of currentParams) {\n // Only preserve if: (a) not already in target, and (b) included in preserve list\n if (!targetParams.has(key)) {\n if (preserve === true || preserve.includes(key)) {\n merged.append(key, value);\n }\n }\n }\n\n const qs = merged.toString();\n const pathWithQuery = qs ? `${targetPath}?${qs}` : targetPath;\n return pathWithQuery + hash;\n}\n","/**\n * Segment Context — provides layout segment position for useSelectedLayoutSegment hooks.\n *\n * Each layout in the segment tree is wrapped with a SegmentProvider that stores\n * the URL segments from root to the current layout level. The hooks read this\n * context to determine which child segments are active below the calling layout.\n *\n * The context value is intentionally minimal: just the segment path array and\n * parallel route keys. No internal cache details are exposed.\n *\n * Design docs: design/19-client-navigation.md, design/14-ecosystem.md\n */\n\n'use client';\n\nimport { createContext, useContext, createElement, useMemo } from 'react';\n\n// ─── Types ───────────────────────────────────────────────────────\n\nexport interface SegmentContextValue {\n /** URL segments from root to this layout (e.g. ['', 'dashboard', 'settings']) */\n segments: string[];\n /** Parallel route slot keys available at this layout level (e.g. ['sidebar', 'modal']) */\n parallelRouteKeys: string[];\n}\n\n// ─── Context ─────────────────────────────────────────────────────\n\nconst SegmentContext = createContext<SegmentContextValue | null>(null);\n\n/** Read the segment context. Returns null if no provider is above this component. */\nexport function useSegmentContext(): SegmentContextValue | null {\n return useContext(SegmentContext);\n}\n\n// ─── Provider ────────────────────────────────────────────────────\n\ninterface SegmentProviderProps {\n segments: string[];\n /**\n * Unique identifier for this segment, used by the client-side segment\n * merger for element caching. For route groups this includes the group\n * name (e.g., \"/(marketing)\") since groups share their parent's urlPath.\n * Falls back to the reconstructed path from `segments` if not provided.\n */\n segmentId?: string;\n parallelRouteKeys: string[];\n children: React.ReactNode;\n}\n\n/**\n * Wraps each layout to provide segment position context.\n * Injected by rsc-entry.ts during element tree construction.\n */\nexport function SegmentProvider({\n segments,\n segmentId: _segmentId,\n parallelRouteKeys,\n children,\n}: SegmentProviderProps) {\n const value = useMemo(\n () => ({ segments, parallelRouteKeys }),\n // segments and parallelRouteKeys are static per layout — they don't change\n // across navigations. The layout's position in the tree is fixed.\n // Intentionally using derived keys — segments/parallelRouteKeys are static per layout\n [segments.join('/'), parallelRouteKeys.join(',')]\n );\n return createElement(SegmentContext.Provider, { value }, children);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAqBA,SAAgB,2BACd,YACA,eACA,UACQ;CACR,MAAM,gBAAgB,IAAI,gBAAgB,cAAc;AACxD,KAAI,cAAc,SAAS,EAAG,QAAO;CAIrC,MAAM,UAAU,WAAW,QAAQ,IAAI;CACvC,MAAM,kBAAkB,WAAW,IAAI,WAAW,MAAM,GAAG,QAAQ,GAAG;CACtE,MAAM,OAAO,WAAW,IAAI,WAAW,MAAM,QAAQ,GAAG;CAGxD,MAAM,OAAO,gBAAgB,QAAQ,IAAI;CACzC,MAAM,aAAa,QAAQ,IAAI,gBAAgB,MAAM,GAAG,KAAK,GAAG;CAChE,MAAM,eAAe,IAAI,gBAAgB,QAAQ,IAAI,gBAAgB,MAAM,OAAO,EAAE,GAAG,GAAG;CAG1F,MAAM,SAAS,IAAI,gBAAgB,aAAa;AAChD,MAAK,MAAM,CAAC,KAAK,UAAU,cAEzB,KAAI,CAAC,aAAa,IAAI,IAAI;MACpB,aAAa,QAAQ,SAAS,SAAS,IAAI,CAC7C,QAAO,OAAO,KAAK,MAAM;;CAK/B,MAAM,KAAK,OAAO,UAAU;AAE5B,SADsB,KAAK,GAAG,WAAW,GAAG,OAAO,cAC5B;;;;;;;;;;;;;;;;ACzBzB,IAAM,iBAAiB,cAA0C,KAAK;;AAGtE,SAAgB,oBAAgD;AAC9D,QAAO,WAAW,eAAe;;;;;;AAsBnC,SAAgB,gBAAgB,EAC9B,UACA,WAAW,YACX,mBACA,YACuB;CACvB,MAAM,QAAQ,eACL;EAAE;EAAU;EAAmB,GAItC,CAAC,SAAS,KAAK,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC,CAClD;AACD,QAAO,cAAc,eAAe,UAAU,EAAE,OAAO,EAAE,SAAS"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//#region src/client/stale-reload.ts
|
|
2
|
+
/**
|
|
3
|
+
* Stale Client Reference Reload
|
|
4
|
+
*
|
|
5
|
+
* When a new deployment ships updated bundles, clients running stale
|
|
6
|
+
* JavaScript may encounter "Could not find the module" errors during
|
|
7
|
+
* RSC Flight stream decoding. This happens because the RSC payload
|
|
8
|
+
* references module IDs from the new bundle that don't exist in the
|
|
9
|
+
* old client bundle.
|
|
10
|
+
*
|
|
11
|
+
* This module detects these specific errors and triggers a full page
|
|
12
|
+
* reload so the browser fetches the new bundle. A sessionStorage flag
|
|
13
|
+
* guards against infinite reload loops.
|
|
14
|
+
*
|
|
15
|
+
* See: LOCAL-332
|
|
16
|
+
*/
|
|
17
|
+
var RELOAD_FLAG_KEY = "__timber_stale_reload";
|
|
18
|
+
/**
|
|
19
|
+
* In-memory fallback counter for environments where sessionStorage is
|
|
20
|
+
* unavailable (private browsing, storage full, extension interference).
|
|
21
|
+
* Incremented each time triggerStaleReload() falls into the catch path.
|
|
22
|
+
* If the counter exceeds 0 on a subsequent call, the reload is suppressed
|
|
23
|
+
* to prevent an infinite loop. Resets naturally on page load (module
|
|
24
|
+
* re-evaluates) and can be manually reset via clearStaleReloadFlag().
|
|
25
|
+
*/
|
|
26
|
+
var memoryReloadCount = 0;
|
|
27
|
+
/**
|
|
28
|
+
* Trigger a full page reload to pick up new bundles.
|
|
29
|
+
*
|
|
30
|
+
* Sets a sessionStorage flag before reloading. If the flag is already
|
|
31
|
+
* set (meaning we already reloaded once for this reason), we don't
|
|
32
|
+
* reload again — this prevents infinite reload loops if the error
|
|
33
|
+
* persists after reload (e.g., a genuine bug rather than a stale bundle).
|
|
34
|
+
*
|
|
35
|
+
* @returns true if a reload was triggered, false if suppressed (loop guard)
|
|
36
|
+
*/
|
|
37
|
+
function triggerStaleReload() {
|
|
38
|
+
try {
|
|
39
|
+
if (sessionStorage.getItem(RELOAD_FLAG_KEY)) {
|
|
40
|
+
console.warn("[timber] Stale client reference detected again after reload. Not reloading to prevent infinite loop. This may indicate a deployment issue — try a hard refresh.");
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
sessionStorage.setItem(RELOAD_FLAG_KEY, "1");
|
|
44
|
+
console.warn("[timber] Stale client reference detected — the server has been redeployed with new bundles. Reloading to pick up the new version.");
|
|
45
|
+
window.location.reload();
|
|
46
|
+
return true;
|
|
47
|
+
} catch {
|
|
48
|
+
if (document.cookie.includes(RELOAD_FLAG_KEY + "=1") || memoryReloadCount > 0) {
|
|
49
|
+
console.warn("[timber] Stale client reference detected again after reload. Not reloading to prevent infinite loop. This may indicate a deployment issue — try a hard refresh.");
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
memoryReloadCount++;
|
|
53
|
+
try {
|
|
54
|
+
document.cookie = `${RELOAD_FLAG_KEY}=1; max-age=60; path=/; SameSite=Lax`;
|
|
55
|
+
} catch {}
|
|
56
|
+
console.warn("[timber] Stale client reference detected — the server has been redeployed with new bundles. Reloading to pick up the new version.");
|
|
57
|
+
window.location.reload();
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
//#endregion
|
|
62
|
+
export { triggerStaleReload };
|
|
63
|
+
|
|
64
|
+
//# sourceMappingURL=stale-reload-BLUC_Pl_.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stale-reload-BLUC_Pl_.js","names":[],"sources":["../../src/client/stale-reload.ts"],"sourcesContent":["/**\n * Stale Client Reference Reload\n *\n * When a new deployment ships updated bundles, clients running stale\n * JavaScript may encounter \"Could not find the module\" errors during\n * RSC Flight stream decoding. This happens because the RSC payload\n * references module IDs from the new bundle that don't exist in the\n * old client bundle.\n *\n * This module detects these specific errors and triggers a full page\n * reload so the browser fetches the new bundle. A sessionStorage flag\n * guards against infinite reload loops.\n *\n * See: LOCAL-332\n */\n\nconst RELOAD_FLAG_KEY = '__timber_stale_reload';\n\n/**\n * In-memory fallback counter for environments where sessionStorage is\n * unavailable (private browsing, storage full, extension interference).\n * Incremented each time triggerStaleReload() falls into the catch path.\n * If the counter exceeds 0 on a subsequent call, the reload is suppressed\n * to prevent an infinite loop. Resets naturally on page load (module\n * re-evaluates) and can be manually reset via clearStaleReloadFlag().\n */\nlet memoryReloadCount = 0;\n\n/**\n * Check if an error is a stale client reference error from React's\n * Flight client. These errors have the message pattern:\n * \"Could not find the module \\\"<id>\\\"\"\n *\n * This is thrown by react-server-dom-webpack's client when the RSC\n * payload references a module ID that doesn't exist in the client's\n * module map — typically because the server has been redeployed with\n * new bundle hashes while the client is still running old JavaScript.\n */\nexport function isStaleClientReference(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const msg = error.message;\n return msg.includes('Could not find the module') || msg.includes('client reference not found');\n}\n\n/**\n * Check if an error is a chunk load failure from a dynamic import.\n *\n * After a deployment, old chunk filenames no longer exist. When the client\n * tries to dynamically import a chunk that's been replaced, the browser\n * throws one of these errors:\n *\n * - Chromium: \"Failed to fetch dynamically imported module: <url>\"\n * - Firefox: \"error loading dynamically imported module: <url>\"\n * - Safari: \"Importing a module script failed.\"\n * - Vite/Rollup: \"Unable to preload CSS for <url>\"\n *\n * See TIM-446\n */\nexport function isChunkLoadError(error: unknown): boolean {\n if (!(error instanceof Error)) return false;\n const msg = error.message.toLowerCase();\n return (\n msg.includes('failed to fetch dynamically imported module') ||\n msg.includes('error loading dynamically imported module') ||\n msg.includes('importing a module script failed') ||\n msg.includes('unable to preload css') ||\n // Webpack-style chunk load errors (unlikely in Vite but defensive)\n msg.includes('loading chunk') ||\n msg.includes('loading css chunk')\n );\n}\n\n/**\n * Trigger a full page reload to pick up new bundles.\n *\n * Sets a sessionStorage flag before reloading. If the flag is already\n * set (meaning we already reloaded once for this reason), we don't\n * reload again — this prevents infinite reload loops if the error\n * persists after reload (e.g., a genuine bug rather than a stale bundle).\n *\n * @returns true if a reload was triggered, false if suppressed (loop guard)\n */\nexport function triggerStaleReload(): boolean {\n try {\n // Check if we already reloaded — prevent infinite loop\n if (sessionStorage.getItem(RELOAD_FLAG_KEY)) {\n console.warn(\n '[timber] Stale client reference detected again after reload. ' +\n 'Not reloading to prevent infinite loop. ' +\n 'This may indicate a deployment issue — try a hard refresh.'\n );\n return false;\n }\n\n // Set the flag before reloading\n sessionStorage.setItem(RELOAD_FLAG_KEY, '1');\n\n console.warn(\n '[timber] Stale client reference detected — the server has been ' +\n 'redeployed with new bundles. Reloading to pick up the new version.'\n );\n\n window.location.reload();\n return true;\n } catch {\n // sessionStorage unavailable (private browsing, storage full, etc.)\n // Use document.cookie as a reload-persistent fallback loop guard.\n // Module-level memoryReloadCount resets on every reload, so it can't\n // detect cross-reload loops. Cookies persist across reloads and are\n // available even when sessionStorage is blocked (TIM-576).\n const cookieFlag = document.cookie.includes(RELOAD_FLAG_KEY + '=1');\n if (cookieFlag || memoryReloadCount > 0) {\n console.warn(\n '[timber] Stale client reference detected again after reload. ' +\n 'Not reloading to prevent infinite loop. ' +\n 'This may indicate a deployment issue — try a hard refresh.'\n );\n return false;\n }\n\n memoryReloadCount++;\n // Set a short-lived cookie (60s) as the persistent loop guard.\n // It auto-expires so it won't block future legitimate reloads.\n try {\n document.cookie = `${RELOAD_FLAG_KEY}=1; max-age=60; path=/; SameSite=Lax`;\n } catch {\n // Cookie API unavailable — proceed anyway, memoryReloadCount guards same-page loops\n }\n console.warn(\n '[timber] Stale client reference detected — the server has been ' +\n 'redeployed with new bundles. Reloading to pick up the new version.'\n );\n window.location.reload();\n return true;\n }\n}\n\n/**\n * Clear the stale reload flag. Called on successful bootstrap to reset\n * the loop guard — if the page loaded successfully, the next stale\n * reference error should trigger a fresh reload attempt.\n */\nexport function clearStaleReloadFlag(): void {\n memoryReloadCount = 0;\n try {\n sessionStorage.removeItem(RELOAD_FLAG_KEY);\n } catch {\n // sessionStorage unavailable — nothing to clear\n }\n // Also clear the cookie fallback\n try {\n document.cookie = `${RELOAD_FLAG_KEY}=; max-age=0; path=/; SameSite=Lax`;\n } catch {\n // Cookie API unavailable\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAgBA,IAAM,kBAAkB;;;;;;;;;AAUxB,IAAI,oBAAoB;;;;;;;;;;;AAwDxB,SAAgB,qBAA8B;AAC5C,KAAI;AAEF,MAAI,eAAe,QAAQ,gBAAgB,EAAE;AAC3C,WAAQ,KACN,kKAGD;AACD,UAAO;;AAIT,iBAAe,QAAQ,iBAAiB,IAAI;AAE5C,UAAQ,KACN,oIAED;AAED,SAAO,SAAS,QAAQ;AACxB,SAAO;SACD;AAON,MADmB,SAAS,OAAO,SAAS,kBAAkB,KAAK,IACjD,oBAAoB,GAAG;AACvC,WAAQ,KACN,kKAGD;AACD,UAAO;;AAGT;AAGA,MAAI;AACF,YAAS,SAAS,GAAG,gBAAgB;UAC/B;AAGR,UAAQ,KACN,oIAED;AACD,SAAO,SAAS,QAAQ;AACxB,SAAO"}
|
|
@@ -106,4 +106,4 @@ function bindUseQueryStates(definition) {
|
|
|
106
106
|
//#endregion
|
|
107
107
|
export { registerSearchParams as i, useQueryStates$1 as n, getSearchParams as r, bindUseQueryStates as t };
|
|
108
108
|
|
|
109
|
-
//# sourceMappingURL=use-query-states-
|
|
109
|
+
//# sourceMappingURL=use-query-states-DAhgj8Gx.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-query-states-DAhgj8Gx.js","names":[],"sources":["../../src/search-params/registry.ts","../../src/client/use-query-states.ts"],"sourcesContent":["/**\n * Runtime registry for route-scoped search params definitions.\n *\n * When a route's modules load, the framework registers its search-params\n * definition here. useQueryStates('/route') resolves codecs from this map.\n *\n * Design doc: design/23-search-params.md §\"Runtime: Registration at Route Load\"\n */\n\nimport type { SearchParamsDefinition } from './define.js';\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst registry = new Map<string, SearchParamsDefinition<any>>();\n\n/**\n * Register a route's search params definition.\n * Called by the generated route manifest loader when a route's modules load.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function registerSearchParams(route: string, definition: SearchParamsDefinition<any>): void {\n registry.set(route, definition);\n}\n\n/**\n * Look up a route's search params definition.\n * Returns undefined if the route hasn't been loaded yet.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function getSearchParams(route: string): SearchParamsDefinition<any> | undefined {\n return registry.get(route);\n}\n","/**\n * useQueryStates — client-side hook for URL-synced search params.\n *\n * Delegates to nuqs for URL synchronization, batching, React 19 transitions,\n * and throttled URL writes. Bridges timber's SearchParamCodec protocol to\n * nuqs-compatible parsers.\n *\n * Design doc: design/23-search-params.md §\"Codec Bridge\"\n */\n\n'use client';\n\nimport { useQueryStates as nuqsUseQueryStates } from 'nuqs';\nimport type { SingleParser } from 'nuqs';\nimport type {\n SearchParamCodec,\n SearchParamsDefinition,\n SetParams,\n QueryStatesOptions,\n} from '../search-params/define.js';\nimport { getSearchParams } from '../search-params/registry.js';\n\n// ─── Codec Bridge ─────────────────────────────────────────────────\n\n/**\n * Bridge a timber SearchParamCodec to a nuqs-compatible SingleParser.\n *\n * nuqs parsers: { parse(string) → T|null, serialize?(T) → string, eq?, defaultValue? }\n * timber codecs: { parse(string|string[]|undefined) → T, serialize(T) → string|null }\n */\nfunction bridgeCodec<T>(codec: SearchParamCodec<T>): SingleParser<T> & { defaultValue: T } {\n return {\n parse: (v: string) => codec.parse(v),\n serialize: (v: T) => codec.serialize(v) ?? '',\n defaultValue: codec.parse(undefined) as T,\n eq: (a: T, b: T) => codec.serialize(a) === codec.serialize(b),\n };\n}\n\n/**\n * Bridge an entire codec map to nuqs-compatible parsers.\n */\nfunction bridgeCodecs<T extends Record<string, unknown>>(codecs: {\n [K in keyof T]: SearchParamCodec<T[K]>;\n}) {\n const result: Record<string, SingleParser<unknown> & { defaultValue: unknown }> = {};\n for (const key of Object.keys(codecs)) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n result[key] = bridgeCodec(codecs[key as keyof T]) as any;\n }\n return result as { [K in keyof T]: SingleParser<T[K]> & { defaultValue: T[K] } };\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────\n\n/**\n * Read and write typed search params from/to the URL.\n *\n * Delegates to nuqs internally. The timber nuqs adapter (auto-injected in\n * browser-entry.ts) handles RSC navigation on non-shallow updates.\n *\n * Usage:\n * ```ts\n * // Via a SearchParamsDefinition\n * const [params, setParams] = definition.useQueryStates()\n *\n * // Standalone with inline codecs\n * const [params, setParams] = useQueryStates({\n * page: fromSchema(z.coerce.number().int().min(1).default(1)),\n * })\n * ```\n */\nexport function useQueryStates<T extends Record<string, unknown>>(\n codecsOrRoute: { [K in keyof T]: SearchParamCodec<T[K]> } | string,\n _options?: QueryStatesOptions,\n urlKeys?: Readonly<Record<string, string>>\n): [T, SetParams<T>] {\n // Route-string overload: resolve codecs from the registry\n let codecs: { [K in keyof T]: SearchParamCodec<T[K]> };\n let resolvedUrlKeys = urlKeys;\n if (typeof codecsOrRoute === 'string') {\n const definition = getSearchParams(codecsOrRoute);\n if (!definition) {\n throw new Error(\n `useQueryStates('${codecsOrRoute}'): no search params registered for this route. ` +\n `Either the route has no search-params.ts file, or it hasn't been loaded yet. ` +\n `For cross-route usage, import the definition explicitly.`\n );\n }\n codecs = definition.codecs as { [K in keyof T]: SearchParamCodec<T[K]> };\n resolvedUrlKeys = definition.urlKeys;\n } else {\n codecs = codecsOrRoute;\n }\n\n const bridged = bridgeCodecs(codecs);\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const nuqsOptions: any = {};\n if (resolvedUrlKeys && Object.keys(resolvedUrlKeys).length > 0) {\n nuqsOptions.urlKeys = resolvedUrlKeys;\n }\n\n let values: Record<string, unknown>;\n let setValues: Function;\n try {\n [values, setValues] = nuqsUseQueryStates(bridged, nuqsOptions);\n } catch (err) {\n if (\n err instanceof Error &&\n /Invalid hook call|cannot be called|Cannot read properties of null/i.test(err.message)\n ) {\n throw new Error(\n 'useQueryStates is a client component hook and cannot be called outside a React component. ' +\n 'Use definition.parse(searchParams) in server components instead.'\n );\n }\n throw err;\n }\n\n // Wrap the nuqs setter to match timber's SetParams<T> signature.\n // nuqs's setter accepts Partial<Nullable<Values>> | UpdaterFn | null.\n // timber's setter accepts Partial<T> with optional SetParamsOptions.\n const setParams: SetParams<T> = (partial, setOptions?) => {\n const nuqsSetOptions: Record<string, unknown> = {};\n if (setOptions?.shallow !== undefined) nuqsSetOptions.shallow = setOptions.shallow;\n if (setOptions?.scroll !== undefined) nuqsSetOptions.scroll = setOptions.scroll;\n if (setOptions?.history !== undefined) nuqsSetOptions.history = setOptions.history;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n void setValues(partial as any, nuqsSetOptions);\n };\n\n return [values as T, setParams];\n}\n\n// ─── Definition binding ───────────────────────────────────────────\n\n/**\n * Create a useQueryStates binding for a SearchParamsDefinition.\n * This is used internally by SearchParamsDefinition.useQueryStates().\n */\nexport function bindUseQueryStates<T extends Record<string, unknown>>(\n definition: SearchParamsDefinition<T>\n): (options?: QueryStatesOptions) => [T, SetParams<T>] {\n return (options?: QueryStatesOptions) => {\n return useQueryStates<T>(definition.codecs, options, definition.urlKeys);\n };\n}\n"],"mappings":";;AAYA,IAAM,2BAAW,IAAI,KAA0C;;;;;AAO/D,SAAgB,qBAAqB,OAAe,YAA+C;AACjG,UAAS,IAAI,OAAO,WAAW;;;;;;AAQjC,SAAgB,gBAAgB,OAAwD;AACtF,QAAO,SAAS,IAAI,MAAM;;;;;;;;;;;;;;;;;;;ACC5B,SAAS,YAAe,OAAmE;AACzF,QAAO;EACL,QAAQ,MAAc,MAAM,MAAM,EAAE;EACpC,YAAY,MAAS,MAAM,UAAU,EAAE,IAAI;EAC3C,cAAc,MAAM,MAAM,KAAA,EAAU;EACpC,KAAK,GAAM,MAAS,MAAM,UAAU,EAAE,KAAK,MAAM,UAAU,EAAE;EAC9D;;;;;AAMH,SAAS,aAAgD,QAEtD;CACD,MAAM,SAA4E,EAAE;AACpF,MAAK,MAAM,OAAO,OAAO,KAAK,OAAO,CAEnC,QAAO,OAAO,YAAY,OAAO,KAAgB;AAEnD,QAAO;;;;;;;;;;;;;;;;;;;AAsBT,SAAgB,iBACd,eACA,UACA,SACmB;CAEnB,IAAI;CACJ,IAAI,kBAAkB;AACtB,KAAI,OAAO,kBAAkB,UAAU;EACrC,MAAM,aAAa,gBAAgB,cAAc;AACjD,MAAI,CAAC,WACH,OAAM,IAAI,MACR,mBAAmB,cAAc,uLAGlC;AAEH,WAAS,WAAW;AACpB,oBAAkB,WAAW;OAE7B,UAAS;CAGX,MAAM,UAAU,aAAa,OAAO;CAGpC,MAAM,cAAmB,EAAE;AAC3B,KAAI,mBAAmB,OAAO,KAAK,gBAAgB,CAAC,SAAS,EAC3D,aAAY,UAAU;CAGxB,IAAI;CACJ,IAAI;AACJ,KAAI;AACF,GAAC,QAAQ,aAAa,eAAmB,SAAS,YAAY;UACvD,KAAK;AACZ,MACE,eAAe,SACf,qEAAqE,KAAK,IAAI,QAAQ,CAEtF,OAAM,IAAI,MACR,6JAED;AAEH,QAAM;;CAMR,MAAM,aAA2B,SAAS,eAAgB;EACxD,MAAM,iBAA0C,EAAE;AAClD,MAAI,YAAY,YAAY,KAAA,EAAW,gBAAe,UAAU,WAAW;AAC3E,MAAI,YAAY,WAAW,KAAA,EAAW,gBAAe,SAAS,WAAW;AACzE,MAAI,YAAY,YAAY,KAAA,EAAW,gBAAe,UAAU,WAAW;AAEtE,YAAU,SAAgB,eAAe;;AAGhD,QAAO,CAAC,QAAa,UAAU;;;;;;AASjC,SAAgB,mBACd,YACqD;AACrD,SAAQ,YAAiC;AACvC,SAAO,iBAAkB,WAAW,QAAQ,SAAS,WAAW,QAAQ"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
//#region src/search-params/wrappers.ts
|
|
2
|
+
/**
|
|
3
|
+
* Wrap a nullable codec with a default value. When the inner codec returns
|
|
4
|
+
* null, the default is used instead. The output type becomes non-nullable.
|
|
5
|
+
*
|
|
6
|
+
* Works with any codec — nuqs parsers, custom codecs, fromSchema results.
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { parseAsInteger } from 'nuqs'
|
|
10
|
+
* import { withDefault } from '@timber-js/app/search-params'
|
|
11
|
+
*
|
|
12
|
+
* const page = withDefault(parseAsInteger, 1)
|
|
13
|
+
* // page.parse(undefined) → 1 (not null)
|
|
14
|
+
* // page.parse('5') → 5
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
function withDefault(codec, defaultValue) {
|
|
18
|
+
return {
|
|
19
|
+
parse(value) {
|
|
20
|
+
const result = codec.parse(value);
|
|
21
|
+
return result === null ? defaultValue : result;
|
|
22
|
+
},
|
|
23
|
+
serialize(value) {
|
|
24
|
+
return codec.serialize(value);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Attach a URL key alias to a codec. The alias determines what query
|
|
30
|
+
* parameter key is used in the URL, while the TypeScript property name
|
|
31
|
+
* stays descriptive.
|
|
32
|
+
*
|
|
33
|
+
* Aliases travel with codecs through object spread composition — when
|
|
34
|
+
* you spread a bundle containing aliased codecs into defineSearchParams,
|
|
35
|
+
* the aliases come along automatically.
|
|
36
|
+
*
|
|
37
|
+
* ```ts
|
|
38
|
+
* import { parseAsString } from 'nuqs'
|
|
39
|
+
* import { withUrlKey } from '@timber-js/app/search-params'
|
|
40
|
+
*
|
|
41
|
+
* export const searchable = {
|
|
42
|
+
* q: withUrlKey(parseAsString, 'search'),
|
|
43
|
+
* // ?search=shoes → { q: 'shoes' }
|
|
44
|
+
* }
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* Composes with withDefault:
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { parseAsInteger } from 'nuqs'
|
|
50
|
+
* withUrlKey(withDefault(parseAsInteger, 1), 'p')
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
function withUrlKey(codec, urlKey) {
|
|
54
|
+
return {
|
|
55
|
+
parse: codec.parse.bind(codec),
|
|
56
|
+
serialize: codec.serialize.bind(codec),
|
|
57
|
+
urlKey
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { withUrlKey as n, withDefault as t };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=wrappers-LZbghvn0.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrappers-LZbghvn0.js","names":[],"sources":["../../src/search-params/wrappers.ts"],"sourcesContent":["/**\n * Codec wrappers — withDefault and withUrlKey.\n *\n * These are timber-specific utilities that work with any SearchParamCodec.\n * For actual codecs (string, integer, boolean, etc.), use nuqs parsers\n * or Standard Schema objects (Zod, Valibot, ArkType) with auto-detection.\n *\n * Design doc: design/23-search-params.md\n */\n\nimport type { SearchParamCodec, SearchParamCodecWithUrlKey } from './define.js';\n\n// ---------------------------------------------------------------------------\n// withDefault\n// ---------------------------------------------------------------------------\n\n/**\n * Wrap a nullable codec with a default value. When the inner codec returns\n * null, the default is used instead. The output type becomes non-nullable.\n *\n * Works with any codec — nuqs parsers, custom codecs, fromSchema results.\n *\n * ```ts\n * import { parseAsInteger } from 'nuqs'\n * import { withDefault } from '@timber-js/app/search-params'\n *\n * const page = withDefault(parseAsInteger, 1)\n * // page.parse(undefined) → 1 (not null)\n * // page.parse('5') → 5\n * ```\n */\nexport function withDefault<T>(\n codec: SearchParamCodec<T | null>,\n defaultValue: T\n): SearchParamCodec<T> {\n return {\n parse(value: string | string[] | undefined): T {\n const result = codec.parse(value);\n return result === null ? defaultValue : result;\n },\n serialize(value: T): string | null {\n return codec.serialize(value);\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// withUrlKey\n// ---------------------------------------------------------------------------\n\n/**\n * Attach a URL key alias to a codec. The alias determines what query\n * parameter key is used in the URL, while the TypeScript property name\n * stays descriptive.\n *\n * Aliases travel with codecs through object spread composition — when\n * you spread a bundle containing aliased codecs into defineSearchParams,\n * the aliases come along automatically.\n *\n * ```ts\n * import { parseAsString } from 'nuqs'\n * import { withUrlKey } from '@timber-js/app/search-params'\n *\n * export const searchable = {\n * q: withUrlKey(parseAsString, 'search'),\n * // ?search=shoes → { q: 'shoes' }\n * }\n * ```\n *\n * Composes with withDefault:\n * ```ts\n * import { parseAsInteger } from 'nuqs'\n * withUrlKey(withDefault(parseAsInteger, 1), 'p')\n * ```\n */\nexport function withUrlKey<T>(\n codec: SearchParamCodec<T>,\n urlKey: string\n): SearchParamCodecWithUrlKey<T> {\n return {\n parse: codec.parse.bind(codec),\n serialize: codec.serialize.bind(codec),\n urlKey,\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;AA+BA,SAAgB,YACd,OACA,cACqB;AACrB,QAAO;EACL,MAAM,OAAyC;GAC7C,MAAM,SAAS,MAAM,MAAM,MAAM;AACjC,UAAO,WAAW,OAAO,eAAe;;EAE1C,UAAU,OAAyB;AACjC,UAAO,MAAM,UAAU,MAAM;;EAEhC;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCH,SAAgB,WACd,OACA,QAC+B;AAC/B,QAAO;EACL,OAAO,MAAM,MAAM,KAAK,MAAM;EAC9B,WAAW,MAAM,UAAU,KAAK,MAAM;EACtC;EACD"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloudflare dev-mode bindings via wrangler's getPlatformProxy().
|
|
3
|
+
*
|
|
4
|
+
* This Vite plugin starts a local workerd process (via Miniflare) that
|
|
5
|
+
* emulates Cloudflare bindings (KV, D1, R2, Queues, Durable Objects, etc.)
|
|
6
|
+
* during development. The proxy env is injected into the request ALS so
|
|
7
|
+
* `getCloudflareBindings()` works identically in dev and production.
|
|
8
|
+
*
|
|
9
|
+
* Design doc: design/35-cloudflare-primitives.md §"Dev Experience"
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* ```ts
|
|
13
|
+
* // vite.config.ts
|
|
14
|
+
* import { timber } from '@timber-js/app'
|
|
15
|
+
* import { cloudflare } from '@timber-js/app/adapters/cloudflare'
|
|
16
|
+
* import { cloudflareDevBindings } from '@timber-js/app/adapters/cloudflare/dev'
|
|
17
|
+
*
|
|
18
|
+
* export default defineConfig({
|
|
19
|
+
* plugins: [
|
|
20
|
+
* timber({ adapter: cloudflare() }),
|
|
21
|
+
* cloudflareDevBindings(),
|
|
22
|
+
* ],
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Requires `wrangler` as a dev dependency. Install it:
|
|
27
|
+
* ```bash
|
|
28
|
+
* pnpm add -D wrangler
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
import type { Plugin } from 'vite';
|
|
32
|
+
/**
|
|
33
|
+
* Dynamically import wrangler. Extracted as a named export so tests
|
|
34
|
+
* can mock it without needing wrangler installed.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
export declare function loadWrangler(): Promise<{
|
|
38
|
+
getPlatformProxy: (opts: any) => Promise<any>;
|
|
39
|
+
}>;
|
|
40
|
+
/**
|
|
41
|
+
* Symbol key used to store the dev request wrapper on ViteDevServer.
|
|
42
|
+
*
|
|
43
|
+
* @internal Exported for testing. The dev-server plugin uses
|
|
44
|
+
* `Symbol.for('timber:dev-request-wrapper')` directly to avoid
|
|
45
|
+
* importing from this module.
|
|
46
|
+
*/
|
|
47
|
+
export declare const DEV_REQUEST_WRAPPER_KEY: unique symbol;
|
|
48
|
+
/** Options for the Cloudflare dev bindings plugin. */
|
|
49
|
+
export interface CloudflareDevBindingsOptions {
|
|
50
|
+
/**
|
|
51
|
+
* Path to the wrangler configuration file.
|
|
52
|
+
* @default Auto-detected by wrangler (wrangler.jsonc, wrangler.json, wrangler.toml)
|
|
53
|
+
*/
|
|
54
|
+
configPath?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Whether to persist binding data (KV, D1, R2) between dev server restarts.
|
|
57
|
+
*
|
|
58
|
+
* - `true` — persist in wrangler's default location (`.wrangler/state/`)
|
|
59
|
+
* - `{ path: string }` — persist in a custom directory
|
|
60
|
+
* - `false` — start fresh on every restart
|
|
61
|
+
*
|
|
62
|
+
* @default true
|
|
63
|
+
*/
|
|
64
|
+
persist?: boolean | {
|
|
65
|
+
path: string;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Wrangler environment to use (for multi-environment configs).
|
|
69
|
+
* Maps to `[env.<name>]` sections in wrangler.toml / wrangler.jsonc.
|
|
70
|
+
*/
|
|
71
|
+
environment?: string;
|
|
72
|
+
/**
|
|
73
|
+
* @internal Override the wrangler loader for testing.
|
|
74
|
+
* Not part of the public API — may change without notice.
|
|
75
|
+
*/
|
|
76
|
+
_loadWrangler?: () => Promise<{
|
|
77
|
+
getPlatformProxy: (opts: any) => Promise<any>;
|
|
78
|
+
}>;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Vite plugin that provides Cloudflare bindings in dev mode via
|
|
82
|
+
* wrangler's `getPlatformProxy()`.
|
|
83
|
+
*
|
|
84
|
+
* Starts a local workerd process on dev server startup and injects
|
|
85
|
+
* the proxy env into the per-request ALS. This makes
|
|
86
|
+
* `getCloudflareBindings()` return real (emulated) bindings during
|
|
87
|
+
* development — KV, D1, R2, Queues, Durable Objects, and all other
|
|
88
|
+
* bindings declared in wrangler.jsonc.
|
|
89
|
+
*
|
|
90
|
+
* The proxy is automatically disposed when the dev server closes.
|
|
91
|
+
*
|
|
92
|
+
* **Requirements:**
|
|
93
|
+
* - `wrangler` installed as a dev dependency
|
|
94
|
+
* - A `wrangler.jsonc` (or `wrangler.toml`) with binding declarations
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* import { cloudflareDevBindings } from '@timber-js/app/adapters/cloudflare/dev'
|
|
99
|
+
*
|
|
100
|
+
* export default defineConfig({
|
|
101
|
+
* plugins: [
|
|
102
|
+
* timber({ adapter: cloudflare() }),
|
|
103
|
+
* cloudflareDevBindings(),
|
|
104
|
+
* ],
|
|
105
|
+
* })
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare function cloudflareDevBindings(options?: CloudflareDevBindingsOptions): Plugin;
|
|
109
|
+
//# sourceMappingURL=cloudflare-dev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-dev.d.ts","sourceRoot":"","sources":["../../src/adapters/cloudflare-dev.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAGlD;;;;GAIG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC;IAAE,gBAAgB,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;CAAE,CAAC,CAU/F;AAaD;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAA2C,CAAC;AAEhF,sDAAsD;AACtD,MAAM,WAAW,4BAA4B;IAC3C;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAErC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC;QAAE,gBAAgB,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;KAAE,CAAC,CAAC;CAClF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,4BAAiC,GAAG,MAAM,CA6CxF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { runWithBindings } from "./cloudflare.js";
|
|
2
|
+
//#region src/adapters/cloudflare-dev.ts
|
|
3
|
+
/**
|
|
4
|
+
* Dynamically import wrangler. Extracted as a named export so tests
|
|
5
|
+
* can mock it without needing wrangler installed.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
async function loadWrangler() {
|
|
9
|
+
try {
|
|
10
|
+
return await import("wrangler");
|
|
11
|
+
} catch {
|
|
12
|
+
throw new Error("[timber] Could not import wrangler. cloudflareDevBindings() requires wrangler as a dev dependency.\nInstall it: pnpm add -D wrangler");
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Symbol key used to store the dev request wrapper on ViteDevServer.
|
|
17
|
+
*
|
|
18
|
+
* @internal Exported for testing. The dev-server plugin uses
|
|
19
|
+
* `Symbol.for('timber:dev-request-wrapper')` directly to avoid
|
|
20
|
+
* importing from this module.
|
|
21
|
+
*/
|
|
22
|
+
var DEV_REQUEST_WRAPPER_KEY = Symbol.for("timber:dev-request-wrapper");
|
|
23
|
+
/**
|
|
24
|
+
* Vite plugin that provides Cloudflare bindings in dev mode via
|
|
25
|
+
* wrangler's `getPlatformProxy()`.
|
|
26
|
+
*
|
|
27
|
+
* Starts a local workerd process on dev server startup and injects
|
|
28
|
+
* the proxy env into the per-request ALS. This makes
|
|
29
|
+
* `getCloudflareBindings()` return real (emulated) bindings during
|
|
30
|
+
* development — KV, D1, R2, Queues, Durable Objects, and all other
|
|
31
|
+
* bindings declared in wrangler.jsonc.
|
|
32
|
+
*
|
|
33
|
+
* The proxy is automatically disposed when the dev server closes.
|
|
34
|
+
*
|
|
35
|
+
* **Requirements:**
|
|
36
|
+
* - `wrangler` installed as a dev dependency
|
|
37
|
+
* - A `wrangler.jsonc` (or `wrangler.toml`) with binding declarations
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* import { cloudflareDevBindings } from '@timber-js/app/adapters/cloudflare/dev'
|
|
42
|
+
*
|
|
43
|
+
* export default defineConfig({
|
|
44
|
+
* plugins: [
|
|
45
|
+
* timber({ adapter: cloudflare() }),
|
|
46
|
+
* cloudflareDevBindings(),
|
|
47
|
+
* ],
|
|
48
|
+
* })
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
function cloudflareDevBindings(options = {}) {
|
|
52
|
+
return {
|
|
53
|
+
name: "timber-cloudflare-dev-bindings",
|
|
54
|
+
apply: "serve",
|
|
55
|
+
async configureServer(server) {
|
|
56
|
+
const { getPlatformProxy } = await (options._loadWrangler ?? loadWrangler)();
|
|
57
|
+
const proxyOptions = { persist: options.persist ?? true };
|
|
58
|
+
if (options.configPath) proxyOptions.configPath = options.configPath;
|
|
59
|
+
if (options.environment) proxyOptions.environment = options.environment;
|
|
60
|
+
const proxy = await getPlatformProxy(proxyOptions);
|
|
61
|
+
server[DEV_REQUEST_WRAPPER_KEY] = (fn) => {
|
|
62
|
+
return runWithBindings(proxy.env, fn);
|
|
63
|
+
};
|
|
64
|
+
server.httpServer?.on("close", () => {
|
|
65
|
+
proxy.dispose().catch(() => {});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//#endregion
|
|
71
|
+
export { DEV_REQUEST_WRAPPER_KEY, cloudflareDevBindings, loadWrangler };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=cloudflare-dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloudflare-dev.js","names":[],"sources":["../../src/adapters/cloudflare-dev.ts"],"sourcesContent":["/**\n * Cloudflare dev-mode bindings via wrangler's getPlatformProxy().\n *\n * This Vite plugin starts a local workerd process (via Miniflare) that\n * emulates Cloudflare bindings (KV, D1, R2, Queues, Durable Objects, etc.)\n * during development. The proxy env is injected into the request ALS so\n * `getCloudflareBindings()` works identically in dev and production.\n *\n * Design doc: design/35-cloudflare-primitives.md §\"Dev Experience\"\n *\n * Usage:\n * ```ts\n * // vite.config.ts\n * import { timber } from '@timber-js/app'\n * import { cloudflare } from '@timber-js/app/adapters/cloudflare'\n * import { cloudflareDevBindings } from '@timber-js/app/adapters/cloudflare/dev'\n *\n * export default defineConfig({\n * plugins: [\n * timber({ adapter: cloudflare() }),\n * cloudflareDevBindings(),\n * ],\n * })\n * ```\n *\n * Requires `wrangler` as a dev dependency. Install it:\n * ```bash\n * pnpm add -D wrangler\n * ```\n */\n\nimport type { Plugin, ViteDevServer } from 'vite';\nimport { runWithBindings } from './cloudflare.js';\n\n/**\n * Dynamically import wrangler. Extracted as a named export so tests\n * can mock it without needing wrangler installed.\n * @internal\n */\nexport async function loadWrangler(): Promise<{ getPlatformProxy: (opts: any) => Promise<any> }> {\n try {\n return await import('wrangler');\n } catch {\n throw new Error(\n '[timber] Could not import wrangler. ' +\n 'cloudflareDevBindings() requires wrangler as a dev dependency.\\n' +\n 'Install it: pnpm add -D wrangler'\n );\n }\n}\n\n// ─── Contract ─────────────────────────────────────────────────────────────\n//\n// The dev request wrapper is stored on the ViteDevServer instance using a\n// well-known Symbol. This allows cross-plugin communication without import\n// dependencies — the dev-server plugin checks for this Symbol per-request\n// and wraps the handler call when present.\n//\n// The wrapper type is: <T>(fn: () => T) => T\n// It runs `fn` inside the Cloudflare bindings ALS so getCloudflareBindings()\n// works throughout the request lifecycle.\n\n/**\n * Symbol key used to store the dev request wrapper on ViteDevServer.\n *\n * @internal Exported for testing. The dev-server plugin uses\n * `Symbol.for('timber:dev-request-wrapper')` directly to avoid\n * importing from this module.\n */\nexport const DEV_REQUEST_WRAPPER_KEY = Symbol.for('timber:dev-request-wrapper');\n\n/** Options for the Cloudflare dev bindings plugin. */\nexport interface CloudflareDevBindingsOptions {\n /**\n * Path to the wrangler configuration file.\n * @default Auto-detected by wrangler (wrangler.jsonc, wrangler.json, wrangler.toml)\n */\n configPath?: string;\n\n /**\n * Whether to persist binding data (KV, D1, R2) between dev server restarts.\n *\n * - `true` — persist in wrangler's default location (`.wrangler/state/`)\n * - `{ path: string }` — persist in a custom directory\n * - `false` — start fresh on every restart\n *\n * @default true\n */\n persist?: boolean | { path: string };\n\n /**\n * Wrangler environment to use (for multi-environment configs).\n * Maps to `[env.<name>]` sections in wrangler.toml / wrangler.jsonc.\n */\n environment?: string;\n\n /**\n * @internal Override the wrangler loader for testing.\n * Not part of the public API — may change without notice.\n */\n _loadWrangler?: () => Promise<{ getPlatformProxy: (opts: any) => Promise<any> }>;\n}\n\n/**\n * Vite plugin that provides Cloudflare bindings in dev mode via\n * wrangler's `getPlatformProxy()`.\n *\n * Starts a local workerd process on dev server startup and injects\n * the proxy env into the per-request ALS. This makes\n * `getCloudflareBindings()` return real (emulated) bindings during\n * development — KV, D1, R2, Queues, Durable Objects, and all other\n * bindings declared in wrangler.jsonc.\n *\n * The proxy is automatically disposed when the dev server closes.\n *\n * **Requirements:**\n * - `wrangler` installed as a dev dependency\n * - A `wrangler.jsonc` (or `wrangler.toml`) with binding declarations\n *\n * @example\n * ```ts\n * import { cloudflareDevBindings } from '@timber-js/app/adapters/cloudflare/dev'\n *\n * export default defineConfig({\n * plugins: [\n * timber({ adapter: cloudflare() }),\n * cloudflareDevBindings(),\n * ],\n * })\n * ```\n */\nexport function cloudflareDevBindings(options: CloudflareDevBindingsOptions = {}): Plugin {\n return {\n name: 'timber-cloudflare-dev-bindings',\n\n // Dev mode only — never active during production builds.\n // See design/35-cloudflare-primitives.md §\"Security Considerations\":\n // \"Dev bindings are dev-only. apply: 'serve' ensures it never runs\n // in production builds.\"\n apply: 'serve',\n\n async configureServer(server: ViteDevServer) {\n // Dynamic import — wrangler is an optional peer dependency.\n // Users must install it themselves: `pnpm add -D wrangler`\n const { getPlatformProxy } = await (options._loadWrangler ?? loadWrangler)();\n\n // Build getPlatformProxy options from our public API.\n const proxyOptions: Record<string, unknown> = {\n persist: options.persist ?? true,\n };\n if (options.configPath) {\n proxyOptions.configPath = options.configPath;\n }\n if (options.environment) {\n proxyOptions.environment = options.environment;\n }\n\n const proxy = await getPlatformProxy(proxyOptions);\n\n // Store a request wrapper on the server instance. The dev-server\n // plugin checks for this Symbol per-request and wraps the RSC\n // handler call so getCloudflareBindings() works throughout the\n // request lifecycle (server components, middleware, server actions).\n (server as any)[DEV_REQUEST_WRAPPER_KEY] = <T>(fn: () => T): T => {\n return runWithBindings(proxy.env as Record<string, unknown>, fn);\n };\n\n // Dispose the proxy when the dev server closes. This shuts down\n // the local workerd process started by getPlatformProxy().\n server.httpServer?.on('close', () => {\n proxy.dispose().catch(() => {\n // Disposal errors are non-fatal — the process is shutting down.\n });\n });\n },\n };\n}\n"],"mappings":";;;;;;;AAuCA,eAAsB,eAA2E;AAC/F,KAAI;AACF,SAAO,MAAM,OAAO;SACd;AACN,QAAM,IAAI,MACR,uIAGD;;;;;;;;;;AAsBL,IAAa,0BAA0B,OAAO,IAAI,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8D/E,SAAgB,sBAAsB,UAAwC,EAAE,EAAU;AACxF,QAAO;EACL,MAAM;EAMN,OAAO;EAEP,MAAM,gBAAgB,QAAuB;GAG3C,MAAM,EAAE,qBAAqB,OAAO,QAAQ,iBAAiB,eAAe;GAG5E,MAAM,eAAwC,EAC5C,SAAS,QAAQ,WAAW,MAC7B;AACD,OAAI,QAAQ,WACV,cAAa,aAAa,QAAQ;AAEpC,OAAI,QAAQ,YACV,cAAa,cAAc,QAAQ;GAGrC,MAAM,QAAQ,MAAM,iBAAiB,aAAa;AAMjD,UAAe,4BAA+B,OAAmB;AAChE,WAAO,gBAAgB,MAAM,KAAgC,GAAG;;AAKlE,UAAO,YAAY,GAAG,eAAe;AACnC,UAAM,SAAS,CAAC,YAAY,GAE1B;KACF;;EAEL"}
|