@timber-js/app 0.2.0-alpha.15 → 0.2.0-alpha.150
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/actions-a0RczDnB.js +1106 -0
- package/dist/_chunks/actions-a0RczDnB.js.map +1 -0
- package/dist/_chunks/build-output-helper-B20ZuTSA.js +102 -0
- package/dist/_chunks/build-output-helper-B20ZuTSA.js.map +1 -0
- package/dist/_chunks/cache-api-Co-yI-gP.js +424 -0
- package/dist/_chunks/cache-api-Co-yI-gP.js.map +1 -0
- package/dist/_chunks/{metadata-routes-Cjmvi3rQ.js → canonicalize-P41GR6tY.js} +94 -18
- package/dist/_chunks/canonicalize-P41GR6tY.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-DJDdyEae.js +629 -0
- package/dist/_chunks/cli-schema-sync-DJDdyEae.js.map +1 -0
- package/dist/_chunks/cli-schema-sync-D_IhHEUW.js +2 -0
- package/dist/_chunks/codec-registry-oOUxugz3.js +21 -0
- package/dist/_chunks/codec-registry-oOUxugz3.js.map +1 -0
- package/dist/_chunks/define-Bssfp6ot.js +82 -0
- package/dist/_chunks/define-Bssfp6ot.js.map +1 -0
- package/dist/_chunks/define-CFmvb4Bt.js +203 -0
- package/dist/_chunks/define-CFmvb4Bt.js.map +1 -0
- package/dist/_chunks/fast-hash-D6hIVt1Y.js +36 -0
- package/dist/_chunks/fast-hash-D6hIVt1Y.js.map +1 -0
- package/dist/_chunks/format-CfwjgPz9.js +14 -0
- package/dist/_chunks/format-CfwjgPz9.js.map +1 -0
- package/dist/_chunks/href-validation-FiA_rToW.js +141 -0
- package/dist/_chunks/href-validation-FiA_rToW.js.map +1 -0
- package/dist/_chunks/logger-CM0clugc.js +645 -0
- package/dist/_chunks/logger-CM0clugc.js.map +1 -0
- package/dist/_chunks/navigation-root-bUDZLw74.js +124 -0
- package/dist/_chunks/navigation-root-bUDZLw74.js.map +1 -0
- package/dist/_chunks/plugin-context-DurqFBuY.js +2 -0
- package/dist/_chunks/plugin-context-GfDWsdd2.js +268 -0
- package/dist/_chunks/plugin-context-GfDWsdd2.js.map +1 -0
- package/dist/_chunks/registry-DbJPKoBp.js +20 -0
- package/dist/_chunks/registry-DbJPKoBp.js.map +1 -0
- package/dist/_chunks/resolve-schema-3iUvBV5T.js +50 -0
- package/dist/_chunks/resolve-schema-3iUvBV5T.js.map +1 -0
- package/dist/_chunks/router-ref-CtmF-aPv.js +28 -0
- package/dist/_chunks/router-ref-CtmF-aPv.js.map +1 -0
- package/dist/_chunks/schema-bridge-BY3QLBL7.js +119 -0
- package/dist/_chunks/schema-bridge-BY3QLBL7.js.map +1 -0
- package/dist/_chunks/segment-classify-CDDRVKs7.js +137 -0
- package/dist/_chunks/segment-classify-CDDRVKs7.js.map +1 -0
- package/dist/_chunks/segment-context-ZDnXDkbz.js +34 -0
- package/dist/_chunks/segment-context-ZDnXDkbz.js.map +1 -0
- package/dist/_chunks/{ssr-data-MjmprTmO.js → ssr-data-BOWsq18U.js} +1 -1
- package/dist/_chunks/ssr-data-BOWsq18U.js.map +1 -0
- package/dist/_chunks/tree-match-D2l830j2.js +102 -0
- package/dist/_chunks/tree-match-D2l830j2.js.map +1 -0
- package/dist/_chunks/{use-query-states-D5KaffOK.js → use-query-states-CbeQmext.js} +7 -20
- package/dist/_chunks/use-query-states-CbeQmext.js.map +1 -0
- package/dist/_chunks/use-segment-params-D1CzlgBo.js +151 -0
- package/dist/_chunks/use-segment-params-D1CzlgBo.js.map +1 -0
- package/dist/_chunks/validation-taUkvA4Q.js +113 -0
- package/dist/_chunks/validation-taUkvA4Q.js.map +1 -0
- package/dist/_chunks/walkers-DvWDj453.js +790 -0
- package/dist/_chunks/walkers-DvWDj453.js.map +1 -0
- package/dist/adapters/build-output-helper.d.ts +28 -0
- package/dist/adapters/build-output-helper.d.ts.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-kv-cache.d.ts +64 -0
- package/dist/adapters/cloudflare-kv-cache.d.ts.map +1 -0
- package/dist/adapters/cloudflare-kv-cache.js +117 -0
- package/dist/adapters/cloudflare-kv-cache.js.map +1 -0
- package/dist/adapters/cloudflare.d.ts +142 -20
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +150 -64
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/compress-module.d.ts +5 -4
- package/dist/adapters/compress-module.d.ts.map +1 -1
- package/dist/adapters/nitro.d.ts +17 -3
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +146 -147
- package/dist/adapters/nitro.js.map +1 -1
- package/dist/adapters/shared.d.ts +16 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/cache/cache-api.d.ts +24 -0
- package/dist/cache/cache-api.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 +25 -7
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +100 -240
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/invalidation-epoch.d.ts +43 -0
- package/dist/cache/invalidation-epoch.d.ts.map +1 -0
- package/dist/cache/redis-handler.d.ts +85 -8
- package/dist/cache/redis-handler.d.ts.map +1 -1
- package/dist/cache/singleflight.d.ts +18 -1
- package/dist/cache/singleflight.d.ts.map +1 -1
- package/dist/cache/sizeof.d.ts +22 -0
- package/dist/cache/sizeof.d.ts.map +1 -0
- package/dist/cache/stable-stringify.d.ts +9 -0
- package/dist/cache/stable-stringify.d.ts.map +1 -1
- package/dist/cache/timber-cache.d.ts +20 -10
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/cli-schema-sync.d.ts +59 -0
- package/dist/cli-schema-sync.d.ts.map +1 -0
- package/dist/cli.d.ts +26 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +70 -33
- package/dist/cli.js.map +1 -1
- package/dist/client/browser-dev.d.ts +34 -1
- package/dist/client/browser-dev.d.ts.map +1 -1
- package/dist/client/browser-entry/action-dispatch.d.ts +17 -0
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -0
- package/dist/client/browser-entry/hmr.d.ts +21 -0
- package/dist/client/browser-entry/hmr.d.ts.map +1 -0
- package/dist/client/browser-entry/hydrate.d.ts +46 -0
- package/dist/client/browser-entry/hydrate.d.ts.map +1 -0
- package/dist/client/browser-entry/index.d.ts +29 -0
- package/dist/client/browser-entry/index.d.ts.map +1 -0
- package/dist/client/browser-entry/post-hydration.d.ts +26 -0
- package/dist/client/browser-entry/post-hydration.d.ts.map +1 -0
- package/dist/client/browser-entry/router-init.d.ts +26 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -0
- package/dist/client/browser-entry/rsc-stream.d.ts +24 -0
- package/dist/client/browser-entry/rsc-stream.d.ts.map +1 -0
- package/dist/client/browser-entry/scroll.d.ts +28 -0
- package/dist/client/browser-entry/scroll.d.ts.map +1 -0
- 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 +47 -10
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/error-reconstituter.d.ts +61 -0
- package/dist/client/error-reconstituter.d.ts.map +1 -0
- package/dist/client/form.d.ts +16 -27
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/history.d.ts +11 -3
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.d.ts +10 -24
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +183 -1134
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +19 -0
- package/dist/client/internal.d.ts.map +1 -0
- package/dist/client/internal.js +900 -0
- package/dist/client/internal.js.map +1 -0
- package/dist/client/link.d.ts +69 -60
- package/dist/client/link.d.ts.map +1 -1
- 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 +122 -0
- package/dist/client/navigation-api.d.ts.map +1 -0
- package/dist/client/navigation-context.d.ts +3 -2
- package/dist/client/navigation-context.d.ts.map +1 -1
- package/dist/client/navigation-root.d.ts +119 -0
- package/dist/client/navigation-root.d.ts.map +1 -0
- package/dist/client/nuqs-adapter.d.ts +1 -1
- package/dist/client/nuqs-adapter.d.ts.map +1 -1
- package/dist/client/router-ref.d.ts +1 -1
- package/dist/client/router.d.ts +86 -10
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +46 -3
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +3 -1
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/segment-outlet.d.ts +49 -0
- package/dist/client/segment-outlet.d.ts.map +1 -0
- package/dist/client/segment-update-context.d.ts +21 -0
- package/dist/client/segment-update-context.d.ts.map +1 -0
- package/dist/client/shallow-url.d.ts +10 -0
- package/dist/client/shallow-url.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/state.d.ts +1 -1
- package/dist/client/top-loader.d.ts +5 -5
- package/dist/client/top-loader.d.ts.map +1 -1
- package/dist/client/use-cookie.d.ts +12 -0
- package/dist/client/use-cookie.d.ts.map +1 -1
- package/dist/client/use-link-status.d.ts +5 -5
- package/dist/client/use-link-status.d.ts.map +1 -1
- package/dist/client/use-pathname.d.ts.map +1 -1
- package/dist/client/use-pending-navigation.d.ts +23 -0
- package/dist/client/use-pending-navigation.d.ts.map +1 -0
- package/dist/client/use-query-states.d.ts +1 -1
- package/dist/client/use-query-states.d.ts.map +1 -1
- package/dist/client/use-router.d.ts +1 -1
- package/dist/client/use-router.d.ts.map +1 -1
- package/dist/client/use-search-params.d.ts +12 -5
- package/dist/client/use-search-params.d.ts.map +1 -1
- package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +8 -6
- package/dist/client/use-segment-params.d.ts.map +1 -0
- package/dist/codec.d.ts +69 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +75 -0
- package/dist/codec.js.map +1 -0
- package/dist/config-types.d.ts +294 -0
- package/dist/config-types.d.ts.map +1 -0
- package/dist/config-validation.d.ts +51 -0
- package/dist/config-validation.d.ts.map +1 -0
- package/dist/content/index.d.ts +1 -10
- package/dist/content/index.d.ts.map +1 -1
- package/dist/content/index.js +0 -2
- package/dist/cookies/define-cookie.d.ts +58 -25
- package/dist/cookies/define-cookie.d.ts.map +1 -1
- package/dist/cookies/index.d.ts +2 -1
- package/dist/cookies/index.d.ts.map +1 -1
- package/dist/cookies/index.js +249 -36
- package/dist/cookies/index.js.map +1 -1
- package/dist/cookies/json-cookie.d.ts +64 -0
- package/dist/cookies/json-cookie.d.ts.map +1 -0
- package/dist/cookies/validation.d.ts +34 -0
- package/dist/cookies/validation.d.ts.map +1 -0
- package/dist/dev-tools/404-page.d.ts +50 -0
- package/dist/dev-tools/404-page.d.ts.map +1 -0
- package/dist/dev-tools/browser-logs.d.ts +84 -0
- package/dist/dev-tools/browser-logs.d.ts.map +1 -0
- package/dist/dev-tools/dev-page-shell.d.ts +78 -0
- package/dist/dev-tools/dev-page-shell.d.ts.map +1 -0
- package/dist/dev-tools/error-page.d.ts +32 -0
- package/dist/dev-tools/error-page.d.ts.map +1 -0
- package/dist/dev-tools/holding-server.d.ts +54 -0
- package/dist/dev-tools/holding-server.d.ts.map +1 -0
- package/dist/dev-tools/hydration-overlay.d.ts +11 -0
- package/dist/dev-tools/hydration-overlay.d.ts.map +1 -0
- package/dist/dev-tools/index.d.ts +31 -0
- package/dist/dev-tools/index.d.ts.map +1 -0
- package/dist/dev-tools/instrumentation.d.ts +59 -0
- package/dist/dev-tools/instrumentation.d.ts.map +1 -0
- package/dist/{server/dev-logger.d.ts → dev-tools/logger.d.ts} +1 -1
- package/dist/dev-tools/logger.d.ts.map +1 -0
- package/dist/{plugins/dev-logs.d.ts → dev-tools/logs.d.ts} +2 -2
- package/dist/dev-tools/logs.d.ts.map +1 -0
- package/dist/dev-tools/overlay.d.ts +91 -0
- package/dist/dev-tools/overlay.d.ts.map +1 -0
- package/dist/dev-tools/stack-classifier.d.ts +34 -0
- package/dist/dev-tools/stack-classifier.d.ts.map +1 -0
- package/dist/dev-tools/terminal.d.ts +28 -0
- package/dist/dev-tools/terminal.d.ts.map +1 -0
- package/dist/fonts/ast-walk.d.ts +68 -0
- package/dist/fonts/ast-walk.d.ts.map +1 -0
- package/dist/fonts/ast.d.ts +110 -6
- package/dist/fonts/ast.d.ts.map +1 -1
- package/dist/fonts/bundle.d.ts +50 -0
- package/dist/fonts/bundle.d.ts.map +1 -0
- package/dist/fonts/dev-middleware.d.ts +22 -0
- package/dist/fonts/dev-middleware.d.ts.map +1 -0
- package/dist/fonts/fallbacks.d.ts +0 -7
- package/dist/fonts/fallbacks.d.ts.map +1 -1
- package/dist/fonts/google.d.ts +4 -2
- package/dist/fonts/google.d.ts.map +1 -1
- package/dist/fonts/local.d.ts +4 -4
- package/dist/fonts/local.d.ts.map +1 -1
- package/dist/fonts/paths.d.ts +43 -0
- package/dist/fonts/paths.d.ts.map +1 -0
- package/dist/fonts/pipeline.d.ts +158 -0
- package/dist/fonts/pipeline.d.ts.map +1 -0
- package/dist/fonts/transform.d.ts +78 -0
- package/dist/fonts/transform.d.ts.map +1 -0
- package/dist/fonts/types.d.ts +47 -1
- package/dist/fonts/types.d.ts.map +1 -1
- package/dist/fonts/virtual-modules.d.ts +77 -0
- package/dist/fonts/virtual-modules.d.ts.map +1 -0
- package/dist/index.d.ts +46 -180
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6432 -13423
- package/dist/index.js.map +1 -1
- package/dist/params/codec-registry.d.ts +26 -0
- package/dist/params/codec-registry.d.ts.map +1 -0
- package/dist/params/define-schema.d.ts +81 -0
- package/dist/params/define-schema.d.ts.map +1 -0
- package/dist/params/index.d.ts +5 -0
- package/dist/params/index.d.ts.map +1 -0
- package/dist/params/index.js +34 -0
- package/dist/params/index.js.map +1 -0
- package/dist/params/resolve-schema.d.ts +34 -0
- package/dist/params/resolve-schema.d.ts.map +1 -0
- package/dist/plugin-context.d.ts +137 -0
- package/dist/plugin-context.d.ts.map +1 -0
- package/dist/plugins/adapter-build.d.ts +20 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/plugins/build-manifest.d.ts +7 -3
- package/dist/plugins/build-manifest.d.ts.map +1 -1
- package/dist/plugins/build-report.d.ts +9 -7
- package/dist/plugins/build-report.d.ts.map +1 -1
- package/dist/plugins/cache.d.ts +73 -0
- package/dist/plugins/cache.d.ts.map +1 -0
- package/dist/plugins/client-chunks.d.ts +39 -0
- package/dist/plugins/client-chunks.d.ts.map +1 -0
- package/dist/plugins/cloudflare-wasm.d.ts +21 -0
- package/dist/plugins/cloudflare-wasm.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-server.d.ts +17 -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 +16 -73
- 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 +29 -5
- package/dist/plugins/shims.d.ts.map +1 -1
- package/dist/plugins/static-build.d.ts +5 -26
- package/dist/plugins/static-build.d.ts.map +1 -1
- package/dist/routing/codegen-shared.d.ts +67 -0
- package/dist/routing/codegen-shared.d.ts.map +1 -0
- package/dist/routing/codegen-types.d.ts +64 -0
- package/dist/routing/codegen-types.d.ts.map +1 -0
- package/dist/routing/codegen.d.ts +7 -2
- package/dist/routing/codegen.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts +41 -0
- package/dist/routing/convention-lint.d.ts.map +1 -0
- package/dist/routing/file-cache.d.ts +8 -0
- package/dist/routing/file-cache.d.ts.map +1 -0
- package/dist/routing/index.d.ts +7 -1
- package/dist/routing/index.d.ts.map +1 -1
- package/dist/routing/index.js +4 -2
- package/dist/routing/interception.d.ts +0 -5
- package/dist/routing/interception.d.ts.map +1 -1
- package/dist/routing/link-codegen.d.ts +90 -0
- package/dist/routing/link-codegen.d.ts.map +1 -0
- package/dist/routing/manifest-codegen.d.ts +32 -0
- package/dist/routing/manifest-codegen.d.ts.map +1 -0
- package/dist/routing/scanner.d.ts +1 -10
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/schema-validation.d.ts +35 -0
- package/dist/routing/schema-validation.d.ts.map +1 -0
- package/dist/routing/segment-classify.d.ts +75 -0
- package/dist/routing/segment-classify.d.ts.map +1 -0
- package/dist/routing/segment-codegen.d.ts +38 -0
- package/dist/routing/segment-codegen.d.ts.map +1 -0
- package/dist/routing/types.d.ts +77 -25
- package/dist/routing/types.d.ts.map +1 -1
- package/dist/routing/walkers.d.ts +51 -0
- package/dist/routing/walkers.d.ts.map +1 -0
- 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 +91 -0
- package/dist/schema-bridge.d.ts.map +1 -0
- package/dist/search-params/define.d.ts +157 -0
- package/dist/search-params/define.d.ts.map +1 -0
- package/dist/search-params/index.d.ts +4 -7
- package/dist/search-params/index.d.ts.map +1 -1
- package/dist/search-params/index.js +37 -440
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/registry.d.ts +2 -2
- package/dist/search-params/registry.d.ts.map +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 +89 -0
- package/dist/segment-params/define.d.ts.map +1 -0
- package/dist/segment-params/index.d.ts +3 -0
- package/dist/segment-params/index.d.ts.map +1 -0
- package/dist/segment-params/index.js +2 -0
- package/dist/server/access-gate.d.ts +8 -3
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-client.d.ts +41 -6
- 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 +35 -1
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +9 -30
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +70 -11
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/asset-headers.d.ts +1 -15
- package/dist/server/asset-headers.d.ts.map +1 -1
- package/dist/server/body-limits.d.ts.map +1 -1
- package/dist/server/build-manifest.d.ts +6 -29
- package/dist/server/build-manifest.d.ts.map +1 -1
- package/dist/server/canonicalize.d.ts +10 -0
- package/dist/server/canonicalize.d.ts.map +1 -1
- package/dist/server/coalesce-tick.d.ts +40 -0
- package/dist/server/coalesce-tick.d.ts.map +1 -0
- package/dist/server/cookie-context.d.ts +170 -0
- package/dist/server/cookie-context.d.ts.map +1 -0
- package/dist/server/cookie-parsing.d.ts +51 -0
- package/dist/server/cookie-parsing.d.ts.map +1 -0
- package/dist/server/csrf.d.ts +3 -2
- package/dist/server/csrf.d.ts.map +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-boundary.d.ts +115 -0
- package/dist/server/deny-boundary.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts +2 -2
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/dev-source-map.d.ts +22 -0
- package/dist/server/dev-source-map.d.ts.map +1 -0
- package/dist/server/early-hints-sender.d.ts.map +1 -1
- package/dist/server/early-hints.d.ts +13 -11
- 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 +12 -7
- 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/form-data.d.ts +29 -0
- package/dist/server/form-data.d.ts.map +1 -1
- package/dist/server/html-injector-core.d.ts +221 -0
- package/dist/server/html-injector-core.d.ts.map +1 -0
- package/dist/server/html-injectors.d.ts +81 -41
- package/dist/server/html-injectors.d.ts.map +1 -1
- package/dist/server/index.d.ts +10 -46
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +170 -2911
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +44 -0
- package/dist/server/internal.d.ts.map +1 -0
- package/dist/server/internal.js +3015 -0
- package/dist/server/internal.js.map +1 -0
- package/dist/server/logger.d.ts +39 -7
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/metadata-routes.d.ts +23 -7
- package/dist/server/metadata-routes.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +36 -4
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/node-stream-transforms.d.ts +119 -0
- package/dist/server/node-stream-transforms.d.ts.map +1 -0
- package/dist/server/param-coercion.d.ts +54 -0
- package/dist/server/param-coercion.d.ts.map +1 -0
- package/dist/server/pipeline-helpers.d.ts +95 -0
- package/dist/server/pipeline-helpers.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-outcome.d.ts +49 -0
- package/dist/server/pipeline-outcome.d.ts.map +1 -0
- package/dist/server/pipeline-phases.d.ts +56 -0
- package/dist/server/pipeline-phases.d.ts.map +1 -0
- package/dist/server/pipeline.d.ts +122 -19
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/port-resolution.d.ts +117 -0
- package/dist/server/port-resolution.d.ts.map +1 -0
- package/dist/server/primitives.d.ts +75 -31
- 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/render-utils.d.ts +25 -0
- package/dist/server/render-utils.d.ts.map +1 -0
- package/dist/server/request-context.d.ts +62 -107
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.d.ts +30 -18
- 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 +25 -36
- package/dist/server/route-matcher.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts +66 -0
- package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -0
- package/dist/server/rsc-entry/api-handler.d.ts +12 -3
- 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 +51 -23
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- package/dist/server/rsc-entry/index.d.ts +19 -8
- package/dist/server/rsc-entry/index.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts +50 -0
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -0
- package/dist/server/rsc-entry/revalidate-renderer.d.ts +108 -0
- package/dist/server/rsc-entry/revalidate-renderer.d.ts.map +1 -0
- 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 +17 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts +5 -1
- package/dist/server/rsc-entry/ssr-bridge.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts +34 -4
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts +119 -0
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -0
- package/dist/server/safe-load.d.ts +46 -0
- package/dist/server/safe-load.d.ts.map +1 -0
- package/dist/server/sensitive-fields.d.ts +74 -0
- package/dist/server/sensitive-fields.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 +7 -5
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/ssr-bridge-types.d.ts +121 -0
- package/dist/server/ssr-bridge-types.d.ts.map +1 -0
- package/dist/server/ssr-entry.d.ts +2 -43
- package/dist/server/ssr-entry.d.ts.map +1 -1
- package/dist/server/ssr-render.d.ts +64 -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/state-tree-diff.d.ts +12 -5
- package/dist/server/state-tree-diff.d.ts.map +1 -1
- package/dist/server/status-code-resolver.d.ts +22 -11
- 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 +7 -6
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +62 -30
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/tree-match.d.ts +49 -0
- package/dist/server/tree-match.d.ts.map +1 -0
- package/dist/server/types.d.ts +71 -6
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/utils/escape-html.d.ts +14 -0
- package/dist/server/utils/escape-html.d.ts.map +1 -0
- package/dist/server/utils/mdx-file.d.ts +15 -0
- package/dist/server/utils/mdx-file.d.ts.map +1 -0
- package/dist/server/version-skew.d.ts +61 -0
- package/dist/server/version-skew.d.ts.map +1 -0
- package/dist/shared/head-header.d.ts +54 -0
- package/dist/shared/head-header.d.ts.map +1 -0
- package/dist/shared/href-validation.d.ts +49 -0
- package/dist/shared/href-validation.d.ts.map +1 -0
- package/dist/shared/merge-search-params.d.ts +22 -0
- package/dist/shared/merge-search-params.d.ts.map +1 -0
- package/dist/shared/redirect-type.d.ts +17 -0
- package/dist/shared/redirect-type.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/headers.d.ts +53 -1
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/image.d.ts +16 -16
- package/dist/shims/navigation-client.d.ts +35 -11
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +11 -5
- package/dist/shims/navigation.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 +63 -24
- package/src/adapters/build-output-helper.ts +139 -0
- package/src/adapters/cloudflare-dev.ts +177 -0
- package/src/adapters/cloudflare-kv-cache.ts +179 -0
- package/src/adapters/cloudflare.ts +356 -116
- package/src/adapters/compress-module.ts +27 -92
- package/src/adapters/nitro.ts +155 -99
- package/src/adapters/raw-imports.d.ts +5 -0
- package/src/adapters/shared.ts +40 -0
- package/src/adapters/wrangler.d.ts +7 -0
- package/src/cache/cache-api.ts +47 -0
- package/src/cache/handler-store.ts +68 -0
- package/src/cache/index.ts +84 -18
- package/src/cache/invalidation-epoch.ts +93 -0
- package/src/cache/redis-handler.ts +110 -15
- package/src/cache/singleflight.ts +62 -4
- package/src/cache/sizeof.ts +33 -0
- package/src/cache/stable-stringify.ts +65 -2
- package/src/cache/timber-cache.ts +132 -36
- package/src/cli-schema-sync.ts +388 -0
- package/src/cli.ts +98 -34
- package/src/client/browser-dev.ts +153 -1
- package/src/client/browser-entry/action-dispatch.ts +119 -0
- package/src/client/browser-entry/hmr.ts +70 -0
- package/src/client/browser-entry/hydrate.ts +187 -0
- package/src/client/browser-entry/index.ts +111 -0
- package/src/client/browser-entry/post-hydration.ts +104 -0
- package/src/client/browser-entry/router-init.ts +292 -0
- package/src/client/browser-entry/rsc-stream.ts +157 -0
- package/src/client/browser-entry/scroll.ts +50 -0
- package/src/client/error-boundary.tsx +127 -22
- package/src/client/error-reconstituter.tsx +79 -0
- package/src/client/form.tsx +31 -32
- package/src/client/history.ts +11 -3
- package/src/client/index.ts +67 -45
- package/src/client/internal.ts +58 -0
- package/src/client/link.tsx +346 -150
- package/src/client/navigation-api-types.ts +112 -0
- package/src/client/navigation-api.ts +347 -0
- package/src/client/navigation-context.ts +15 -7
- package/src/client/navigation-root.tsx +355 -0
- package/src/client/nuqs-adapter.tsx +16 -3
- package/src/client/router-ref.ts +1 -1
- package/src/client/router.ts +490 -140
- package/src/client/rsc-fetch.ts +220 -17
- package/src/client/segment-cache.ts +6 -3
- package/src/client/segment-outlet.tsx +77 -0
- package/src/client/segment-update-context.ts +42 -0
- package/src/client/shallow-url.ts +22 -0
- package/src/client/ssr-data.ts +13 -5
- package/src/client/state.ts +2 -2
- package/src/client/top-loader.tsx +11 -9
- package/src/client/use-cookie.ts +31 -2
- package/src/client/use-link-status.ts +7 -7
- package/src/client/use-pathname.ts +1 -0
- package/src/client/use-pending-navigation.ts +40 -0
- package/src/client/use-query-states.ts +9 -3
- package/src/client/use-router.ts +4 -1
- package/src/client/use-search-params.ts +31 -44
- package/src/client/{use-params.ts → use-segment-params.ts} +9 -6
- package/src/codec.ts +147 -0
- package/src/config-types.ts +292 -0
- package/src/config-validation.ts +311 -0
- package/src/content/index.ts +5 -13
- package/src/cookies/define-cookie.ts +217 -53
- package/src/cookies/index.ts +12 -1
- package/src/cookies/json-cookie.ts +105 -0
- package/src/cookies/validation.ts +192 -0
- package/src/dev-tools/404-page.ts +286 -0
- package/src/dev-tools/browser-logs.ts +291 -0
- package/src/dev-tools/dev-page-shell.ts +260 -0
- package/src/dev-tools/error-page.ts +331 -0
- package/src/dev-tools/holding-server.ts +187 -0
- package/src/dev-tools/hydration-overlay.ts +158 -0
- package/src/dev-tools/index.ts +83 -0
- package/src/dev-tools/instrumentation.ts +213 -0
- package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +9 -5
- package/src/dev-tools/overlay.ts +469 -0
- package/src/dev-tools/stack-classifier.ts +75 -0
- package/src/dev-tools/terminal.ts +183 -0
- package/src/fonts/ast-walk.ts +235 -0
- package/src/fonts/ast.ts +477 -126
- package/src/fonts/bundle.ts +138 -0
- package/src/fonts/dev-middleware.ts +85 -0
- package/src/fonts/fallbacks.ts +0 -23
- package/src/fonts/google.ts +7 -3
- package/src/fonts/local.ts +9 -8
- package/src/fonts/paths.ts +58 -0
- package/src/fonts/pipeline.ts +316 -0
- package/src/fonts/transform.ts +344 -0
- package/src/fonts/types.ts +52 -1
- package/src/fonts/virtual-modules.ts +179 -0
- package/src/index.ts +417 -346
- package/src/params/codec-registry.ts +34 -0
- package/src/params/define-schema.ts +115 -0
- package/src/params/index.ts +17 -0
- package/src/params/resolve-schema.ts +84 -0
- package/src/plugin-context.ts +325 -0
- package/src/plugins/adapter-build.ts +98 -4
- package/src/plugins/build-manifest.ts +168 -23
- package/src/plugins/build-report.ts +16 -25
- package/src/plugins/cache.ts +285 -0
- package/src/plugins/client-chunks.ts +113 -0
- package/src/plugins/cloudflare-wasm.ts +141 -0
- package/src/plugins/content.ts +1 -1
- package/src/plugins/dev-server.ts +264 -46
- package/src/plugins/entries.ts +110 -8
- package/src/plugins/fonts.ts +170 -663
- package/src/plugins/mdx.ts +47 -10
- package/src/plugins/routing.ts +237 -241
- package/src/plugins/server-bundle.ts +85 -12
- package/src/plugins/shims.ts +207 -64
- package/src/plugins/static-build.ts +15 -89
- package/src/routing/codegen-shared.ts +115 -0
- package/src/routing/codegen-types.ts +65 -0
- package/src/routing/codegen.ts +380 -178
- package/src/routing/convention-lint.ts +358 -0
- package/src/routing/file-cache.ts +23 -0
- package/src/routing/index.ts +10 -1
- package/src/routing/interception.ts +3 -22
- package/src/routing/link-codegen.ts +310 -0
- package/src/routing/manifest-codegen.ts +290 -0
- package/src/routing/scanner.ts +248 -188
- package/src/routing/schema-validation.ts +149 -0
- package/src/routing/segment-classify.ts +188 -0
- package/src/routing/segment-codegen.ts +98 -0
- package/src/routing/types.ts +78 -25
- package/src/routing/walkers.ts +90 -0
- 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/schema-bridge.ts +252 -0
- package/src/search-params/define.ts +570 -0
- package/src/search-params/index.ts +14 -20
- package/src/search-params/registry.ts +2 -2
- package/src/search-params/wrappers.ts +110 -0
- package/src/segment-params/define.ts +249 -0
- package/src/segment-params/index.ts +9 -0
- package/src/server/access-gate.tsx +84 -34
- package/src/server/action-client.ts +119 -28
- package/src/server/action-encryption.ts +144 -0
- package/src/server/action-handler.ts +122 -16
- package/src/server/actions.ts +39 -53
- package/src/server/als-registry.ts +108 -20
- package/src/server/asset-headers.ts +8 -34
- package/src/server/body-limits.ts +10 -2
- package/src/server/build-manifest.ts +13 -56
- package/src/server/canonicalize.ts +6 -3
- package/src/server/coalesce-tick.ts +75 -0
- package/src/server/compress.ts +34 -7
- package/src/server/cookie-context.ts +484 -0
- package/src/server/cookie-parsing.ts +147 -0
- package/src/server/csrf.ts +39 -6
- package/src/server/default-logger.ts +99 -0
- package/src/server/deny-boundary.ts +311 -0
- package/src/server/deny-renderer.ts +80 -64
- package/src/server/dev-source-map.ts +31 -0
- package/src/server/early-hints-sender.ts +3 -2
- package/src/server/early-hints.ts +37 -27
- package/src/server/error-boundary-wrapper.ts +61 -22
- package/src/server/fallback-error.ts +79 -102
- package/src/server/flight-injection-state.ts +113 -0
- package/src/server/flight-scripts.ts +62 -0
- package/src/server/form-data.ts +101 -5
- package/src/server/html-injector-core.ts +437 -0
- package/src/server/html-injectors.ts +256 -251
- package/src/server/index.ts +29 -183
- package/src/server/internal.ts +165 -0
- package/src/server/logger.ts +67 -36
- package/src/server/metadata-routes.ts +44 -19
- package/src/server/middleware-runner.ts +75 -4
- package/src/server/node-stream-transforms.ts +331 -0
- package/src/server/param-coercion.ts +206 -0
- package/src/server/pipeline-helpers.ts +204 -0
- package/src/server/pipeline-interception.ts +32 -18
- package/src/server/pipeline-metadata.ts +13 -0
- package/src/server/pipeline-outcome.ts +197 -0
- package/src/server/pipeline-phases.ts +527 -0
- package/src/server/pipeline.ts +213 -382
- package/src/server/port-resolution.ts +215 -0
- package/src/server/primitives.ts +126 -63
- package/src/server/render-timeout.ts +108 -0
- package/src/server/render-utils.ts +31 -0
- package/src/server/request-context.ts +168 -337
- package/src/server/route-element-builder.ts +481 -222
- package/src/server/route-handler.ts +31 -4
- package/src/server/route-matcher.ts +144 -277
- package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
- package/src/server/rsc-entry/api-handler.ts +69 -32
- package/src/server/rsc-entry/error-renderer.ts +306 -89
- package/src/server/rsc-entry/helpers.ts +160 -59
- package/src/server/rsc-entry/index.ts +299 -329
- package/src/server/rsc-entry/render-route.ts +311 -0
- package/src/server/rsc-entry/revalidate-renderer.ts +344 -0
- package/src/server/rsc-entry/rsc-payload.ts +71 -26
- package/src/server/rsc-entry/rsc-stream.ts +85 -13
- package/src/server/rsc-entry/ssr-bridge.ts +18 -5
- package/src/server/rsc-entry/ssr-renderer.ts +273 -43
- package/src/server/rsc-entry/wrap-action-dispatch.ts +449 -0
- package/src/server/safe-load.ts +60 -0
- package/src/server/sensitive-fields.ts +230 -0
- package/src/server/sitemap-generator.ts +338 -0
- package/src/server/sitemap-handler.ts +148 -0
- package/src/server/slot-resolver.ts +258 -257
- package/src/server/ssr-bridge-types.ts +124 -0
- package/src/server/ssr-entry.ts +206 -78
- package/src/server/ssr-render.ts +345 -82
- package/src/server/ssr-wrappers.tsx +141 -0
- package/src/server/state-tree-diff.ts +30 -23
- package/src/server/status-code-resolver.ts +115 -130
- package/src/server/stream-utils.ts +213 -0
- package/src/server/tracing.ts +38 -11
- package/src/server/tree-builder.ts +204 -90
- package/src/server/tree-match.ts +220 -0
- package/src/server/types.ts +78 -9
- package/src/server/utils/escape-html.ts +20 -0
- package/src/server/utils/mdx-file.ts +22 -0
- package/src/server/version-skew.ts +104 -0
- package/src/shared/head-header.ts +69 -0
- package/src/shared/href-validation.ts +159 -0
- package/src/shared/merge-search-params.ts +55 -0
- package/src/shared/redirect-type.ts +17 -0
- package/src/shims/font-google.ts +1 -1
- package/src/shims/font-local.ts +34 -0
- package/src/shims/headers.ts +122 -1
- package/src/shims/navigation-client.ts +51 -29
- package/src/shims/navigation.ts +14 -7
- package/src/utils/state-machine.ts +111 -0
- package/dist/_chunks/als-registry-B7DbZ2hS.js +0 -41
- package/dist/_chunks/als-registry-B7DbZ2hS.js.map +0 -1
- package/dist/_chunks/debug-B3Gypr3D.js +0 -108
- package/dist/_chunks/debug-B3Gypr3D.js.map +0 -1
- package/dist/_chunks/format-RyoGQL74.js +0 -164
- package/dist/_chunks/format-RyoGQL74.js.map +0 -1
- package/dist/_chunks/interception-BOoWmLUA.js +0 -676
- package/dist/_chunks/interception-BOoWmLUA.js.map +0 -1
- package/dist/_chunks/metadata-routes-Cjmvi3rQ.js.map +0 -1
- package/dist/_chunks/request-context-BQUC8PHn.js +0 -330
- package/dist/_chunks/request-context-BQUC8PHn.js.map +0 -1
- package/dist/_chunks/ssr-data-MjmprTmO.js.map +0 -1
- package/dist/_chunks/tracing-CemImE6h.js +0 -187
- package/dist/_chunks/tracing-CemImE6h.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/browser-entry.d.ts +0 -21
- package/dist/client/browser-entry.d.ts.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/segment-merger.d.ts +0 -119
- package/dist/client/segment-merger.d.ts.map +0 -1
- package/dist/client/stale-reload.d.ts +0 -44
- package/dist/client/stale-reload.d.ts.map +0 -1
- package/dist/client/transition-root.d.ts +0 -74
- package/dist/client/transition-root.d.ts.map +0 -1
- package/dist/client/use-navigation-pending.d.ts +0 -26
- package/dist/client/use-navigation-pending.d.ts.map +0 -1
- package/dist/client/use-params.d.ts.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/dev-error-overlay.d.ts +0 -60
- package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
- package/dist/plugins/dev-logs.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/plugins/server-action-exports.d.ts +0 -26
- package/dist/plugins/server-action-exports.d.ts.map +0 -1
- package/dist/routing/status-file-lint.d.ts +0 -33
- package/dist/routing/status-file-lint.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/server/dev-fetch-instrumentation.d.ts +0 -22
- package/dist/server/dev-fetch-instrumentation.d.ts.map +0 -1
- package/dist/server/dev-logger.d.ts.map +0 -1
- package/dist/server/dev-span-processor.d.ts +0 -29
- package/dist/server/dev-span-processor.d.ts.map +0 -1
- package/dist/server/dev-warnings.d.ts +0 -129
- package/dist/server/dev-warnings.d.ts.map +0 -1
- package/dist/server/flush.d.ts +0 -74
- package/dist/server/flush.d.ts.map +0 -1
- package/dist/server/manifest-status-resolver.d.ts +0 -58
- package/dist/server/manifest-status-resolver.d.ts.map +0 -1
- package/dist/server/prerender.d.ts +0 -77
- package/dist/server/prerender.d.ts.map +0 -1
- package/dist/utils/directive-parser.d.ts +0 -70
- package/dist/utils/directive-parser.d.ts.map +0 -1
- package/src/cache/register-cached-function.ts +0 -103
- package/src/client/browser-entry.ts +0 -678
- package/src/client/link-status-provider.tsx +0 -30
- package/src/client/segment-merger.ts +0 -339
- package/src/client/stale-reload.ts +0 -87
- package/src/client/transition-root.tsx +0 -166
- package/src/client/use-navigation-pending.ts +0 -38
- package/src/plugins/cache-transform.ts +0 -199
- package/src/plugins/dev-error-overlay.ts +0 -230
- package/src/plugins/dynamic-transform.ts +0 -161
- package/src/plugins/server-action-exports.ts +0 -220
- package/src/routing/status-file-lint.ts +0 -112
- package/src/search-params/analyze.ts +0 -192
- package/src/search-params/builtin-codecs.ts +0 -228
- package/src/search-params/codecs.ts +0 -153
- package/src/search-params/create.ts +0 -321
- package/src/server/dev-fetch-instrumentation.ts +0 -96
- package/src/server/dev-span-processor.ts +0 -78
- package/src/server/dev-warnings.ts +0 -283
- package/src/server/flush.ts +0 -182
- package/src/server/manifest-status-resolver.ts +0 -215
- package/src/server/prerender.ts +0 -139
- package/src/utils/directive-parser.ts +0 -529
- /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CacheOptions } from './index';
|
|
2
|
+
/**
|
|
3
|
+
* Public caching API: `cache(fn, opts)`.
|
|
4
|
+
*
|
|
5
|
+
* Wraps an async function with cross-request caching. Uses the configured
|
|
6
|
+
* cache handler (defaults to MemoryCacheHandler, overridable via timber.config.ts).
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* import { cache } from '@timber-js/app/cache';
|
|
10
|
+
*
|
|
11
|
+
* const getUser = cache(
|
|
12
|
+
* async (id: string) => db.users.findUnique({ where: { id } }),
|
|
13
|
+
* { ttl: 60, tags: (id) => [`user:${id}`] }
|
|
14
|
+
* );
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function cache<Fn extends (...args: any[]) => Promise<any>>(fn: Fn, opts: CacheOptions<Fn>, stableId?: string): Fn;
|
|
18
|
+
export declare namespace cache {
|
|
19
|
+
var invalidate: (opts: {
|
|
20
|
+
key?: string;
|
|
21
|
+
tag?: string;
|
|
22
|
+
}) => Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=cache-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache-api.d.ts","sourceRoot":"","sources":["../../src/cache/cache-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAK5C;;;;;;;;;;;;;;GAcG;AAEH,wBAAgB,KAAK,CAAC,EAAE,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,EAC/D,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC,EACtB,QAAQ,CAAC,EAAE,MAAM,GAChB,EAAE,CAMJ;yBAVe,KAAK;2BAoB8B;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,KAAG,OAAO,CAAC,IAAI,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level cache handler singleton.
|
|
3
|
+
*
|
|
4
|
+
* Lazily initialized to MemoryCacheHandler on first access. The framework
|
|
5
|
+
* replaces this at boot from timber.config.ts via setCacheHandler().
|
|
6
|
+
*
|
|
7
|
+
* This module avoids importing from ./index to prevent circular dependencies.
|
|
8
|
+
*/
|
|
9
|
+
interface CacheHandlerLike {
|
|
10
|
+
get(key: string): Promise<{
|
|
11
|
+
value: unknown;
|
|
12
|
+
stale: boolean;
|
|
13
|
+
} | null>;
|
|
14
|
+
set(key: string, value: unknown, opts: {
|
|
15
|
+
ttl: number;
|
|
16
|
+
tags: string[];
|
|
17
|
+
}): Promise<void>;
|
|
18
|
+
invalidate(opts: {
|
|
19
|
+
key?: string;
|
|
20
|
+
tag?: string;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
/** Replace the active cache handler. Called by the framework at boot. */
|
|
24
|
+
export declare function setCacheHandler(h: CacheHandlerLike): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get the active cache handler. Creates a default MemoryCacheHandler on
|
|
27
|
+
* first access if none has been set via setCacheHandler().
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCacheHandler(): CacheHandlerLike;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=handler-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler-store.d.ts","sourceRoot":"","sources":["../../src/cache/handler-store.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,UAAU,gBAAgB;IACxB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACrE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,UAAU,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAID,yEAAyE;AACzE,wBAAgB,eAAe,CAAC,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAEzD;AAED;;;GAGG;AACH,wBAAgB,eAAe,IAAI,gBAAgB,CAOlD"}
|
package/dist/cache/index.d.ts
CHANGED
|
@@ -17,14 +17,30 @@ export interface CacheOptions<Fn extends (...args: any[]) => any> {
|
|
|
17
17
|
key?: (...args: Parameters<Fn>) => string;
|
|
18
18
|
staleWhileRevalidate?: boolean;
|
|
19
19
|
tags?: string[] | ((...args: Parameters<Fn>) => string[]);
|
|
20
|
+
/** Timeout (ms) for singleflight-coalesced calls. Prevents hung fn() from
|
|
21
|
+
* permanently blocking all future callers for the same cache key. See TIM-518. */
|
|
22
|
+
timeoutMs?: number;
|
|
20
23
|
}
|
|
21
24
|
export interface MemoryCacheHandlerOptions {
|
|
22
25
|
/** Maximum number of entries. Oldest accessed entries are evicted first. Default: 1000. */
|
|
26
|
+
maxEntries?: number;
|
|
27
|
+
/**
|
|
28
|
+
* @deprecated Use `maxEntries` instead. Will be removed in a future release.
|
|
29
|
+
* Alias for `maxEntries` — maximum number of entries (not bytes).
|
|
30
|
+
*/
|
|
23
31
|
maxSize?: number;
|
|
32
|
+
/** Maximum total byte budget for all cached values. Oldest entries are evicted when exceeded. Default: no limit. */
|
|
33
|
+
maxBytes?: number;
|
|
34
|
+
/** Maximum byte size for a single cache entry. Entries exceeding this are silently dropped. Default: no limit. */
|
|
35
|
+
maxEntryBytes?: number;
|
|
24
36
|
}
|
|
25
37
|
export declare class MemoryCacheHandler implements CacheHandler {
|
|
26
38
|
private store;
|
|
27
|
-
private
|
|
39
|
+
private maxEntries;
|
|
40
|
+
private maxBytes;
|
|
41
|
+
private maxEntryBytes;
|
|
42
|
+
private currentBytes;
|
|
43
|
+
private readonly _trackBytes;
|
|
28
44
|
constructor(opts?: MemoryCacheHandlerOptions);
|
|
29
45
|
get(key: string): Promise<{
|
|
30
46
|
value: unknown;
|
|
@@ -40,13 +56,15 @@ export declare class MemoryCacheHandler implements CacheHandler {
|
|
|
40
56
|
}): Promise<void>;
|
|
41
57
|
/** Number of entries currently in the cache. */
|
|
42
58
|
get size(): number;
|
|
59
|
+
/** Estimated total byte size of all cached values. Only tracked when maxBytes or maxEntryBytes is configured; returns 0 otherwise. */
|
|
60
|
+
get bytes(): number;
|
|
61
|
+
/** Evict the oldest entry (front of Map). */
|
|
62
|
+
private evictOldest;
|
|
43
63
|
}
|
|
44
64
|
export { RedisCacheHandler } from './redis-handler';
|
|
45
65
|
export type { RedisClient } from './redis-handler';
|
|
46
|
-
export {
|
|
47
|
-
export {
|
|
48
|
-
export
|
|
49
|
-
export {
|
|
50
|
-
export { createSingleflight } from './singleflight';
|
|
51
|
-
export type { Singleflight } from './singleflight';
|
|
66
|
+
export { cache } from './cache-api';
|
|
67
|
+
export { setCacheHandler, getCacheHandler } from './handler-store';
|
|
68
|
+
export { setDefaultSingleflightTimeout } from './timber-cache';
|
|
69
|
+
export { estimateByteSize } from './sizeof';
|
|
52
70
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IACrE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvF,UAAU,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjE;AAGD,MAAM,WAAW,YAAY,CAAC,EAAE,SAAS,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG;IAC9D,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,KAAK,MAAM,CAAC;IAC1C,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,EAAE,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC;IAC1D;uFACmF;IACnF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,2FAA2F;IAC3F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oHAAoH;IACpH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kHAAkH;IAClH,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,kBAAmB,YAAW,YAAY;IACrD,OAAO,CAAC,KAAK,CAGT;IACJ,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;gBAE1B,IAAI,CAAC,EAAE,yBAAyB;IAQtC,GAAG,CAAC,GAAG,EAAE,MAAM;;;;IAYf,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAA;KAAE;IAwCtE,UAAU,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE;IAkBrD,gDAAgD;IAChD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,sIAAsI;IACtI,IAAI,KAAK,IAAI,MAAM,CAElB;IAED,6CAA6C;IAC7C,OAAO,CAAC,WAAW;CAQpB;AAED,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,6BAA6B,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC"}
|
package/dist/cache/index.js
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
|
-
import "../_chunks/
|
|
2
|
-
|
|
1
|
+
import { i as setDefaultSingleflightTimeout, n as getCacheHandler, r as setCacheHandler, t as cache } from "../_chunks/cache-api-Co-yI-gP.js";
|
|
2
|
+
//#region src/cache/sizeof.ts
|
|
3
|
+
/**
|
|
4
|
+
* Lightweight byte-size estimation for cache entries.
|
|
5
|
+
*
|
|
6
|
+
* Estimates the in-memory byte cost of a JavaScript value using
|
|
7
|
+
* JSON.stringify().length * 2 (UTF-16 char width). This is a rough
|
|
8
|
+
* approximation — it doesn't account for V8 object overhead, Map
|
|
9
|
+
* metadata, or non-serializable values — but it's fast and good
|
|
10
|
+
* enough for cache budget enforcement.
|
|
11
|
+
*
|
|
12
|
+
* Values that fail JSON serialization (circular references, BigInt,
|
|
13
|
+
* etc.) return 0, allowing the entry to be cached without counting
|
|
14
|
+
* toward the byte budget. This is a conservative choice: it's better
|
|
15
|
+
* to cache and undercount than to reject the entry.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Estimate the byte size of a value.
|
|
19
|
+
*
|
|
20
|
+
* Uses `JSON.stringify(value).length * 2` to approximate UTF-16
|
|
21
|
+
* in-memory size. Returns 0 if the value is not serializable.
|
|
22
|
+
*/
|
|
23
|
+
function estimateByteSize(value) {
|
|
24
|
+
try {
|
|
25
|
+
const json = JSON.stringify(value);
|
|
26
|
+
if (json === void 0) return 0;
|
|
27
|
+
return json.length * 2;
|
|
28
|
+
} catch (e) {
|
|
29
|
+
console.debug("[timber] sizeof estimation failed:", e instanceof Error ? e.message : e);
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
3
34
|
//#region src/cache/redis-handler.ts
|
|
4
35
|
var KEY_PREFIX = "timber:cache:";
|
|
5
36
|
var TAG_PREFIX = "timber:tag:";
|
|
@@ -10,8 +41,9 @@ var TAG_PREFIX = "timber:tag:";
|
|
|
10
41
|
* invalidations. Tag-based invalidation uses Redis Sets to track which keys
|
|
11
42
|
* belong to which tags.
|
|
12
43
|
*
|
|
13
|
-
* Bring your own Redis client
|
|
14
|
-
* interface
|
|
44
|
+
* Bring your own Redis client wrapped to the {@link RedisClient} contract —
|
|
45
|
+
* see the adapter examples on the interface for ioredis, node-redis v4, and
|
|
46
|
+
* @upstash/redis.
|
|
15
47
|
*/
|
|
16
48
|
var RedisCacheHandler = class {
|
|
17
49
|
client;
|
|
@@ -41,249 +73,47 @@ var RedisCacheHandler = class {
|
|
|
41
73
|
const expiresAt = Date.now() + opts.ttl * 1e3;
|
|
42
74
|
const payload = JSON.stringify({
|
|
43
75
|
value,
|
|
44
|
-
expiresAt
|
|
76
|
+
expiresAt,
|
|
77
|
+
tags: opts.tags
|
|
45
78
|
});
|
|
46
79
|
const redisTtlSeconds = Math.max(opts.ttl * 2 + 60, 120);
|
|
47
|
-
await this.client.set(ck, payload,
|
|
48
|
-
for (const tag of opts.tags)
|
|
80
|
+
await this.client.set(ck, payload, redisTtlSeconds);
|
|
81
|
+
for (const tag of opts.tags) {
|
|
82
|
+
await this.client.sadd(this.tagKey(tag), key);
|
|
83
|
+
if (await this.client.ttl(this.tagKey(tag)) < redisTtlSeconds) await this.client.expire(this.tagKey(tag), redisTtlSeconds);
|
|
84
|
+
}
|
|
49
85
|
}
|
|
50
86
|
async invalidate(opts) {
|
|
51
|
-
if (opts.key)
|
|
87
|
+
if (opts.key) {
|
|
88
|
+
const raw = await this.client.get(this.cacheKey(opts.key));
|
|
89
|
+
if (raw !== null) {
|
|
90
|
+
const entry = JSON.parse(raw);
|
|
91
|
+
if (entry.tags) await Promise.all(entry.tags.map((tag) => this.client.srem(this.tagKey(tag), opts.key)));
|
|
92
|
+
}
|
|
93
|
+
await this.client.del(this.cacheKey(opts.key));
|
|
94
|
+
}
|
|
52
95
|
if (opts.tag) {
|
|
53
96
|
const tk = this.tagKey(opts.tag);
|
|
54
97
|
const keys = await this.client.smembers(tk);
|
|
55
|
-
|
|
56
|
-
const cacheKeys = keys.map((k) => this.cacheKey(k));
|
|
57
|
-
await this.client.del(cacheKeys);
|
|
58
|
-
}
|
|
98
|
+
await Promise.all(keys.map((k) => this.client.del(this.cacheKey(k))));
|
|
59
99
|
await this.client.del(tk);
|
|
60
100
|
}
|
|
61
101
|
}
|
|
62
102
|
};
|
|
63
103
|
//#endregion
|
|
64
|
-
//#region src/cache/stable-stringify.ts
|
|
65
|
-
/**
|
|
66
|
-
* Deterministic JSON serialization with sorted object keys.
|
|
67
|
-
* Used for cache key generation — ensures { a: 1, b: 2 } and { b: 2, a: 1 }
|
|
68
|
-
* produce the same string.
|
|
69
|
-
*/
|
|
70
|
-
function stableStringify(value) {
|
|
71
|
-
if (value === null || value === void 0) return JSON.stringify(value);
|
|
72
|
-
if (typeof value !== "object") return JSON.stringify(value);
|
|
73
|
-
if (Array.isArray(value)) return "[" + value.map((item) => stableStringify(item)).join(",") + "]";
|
|
74
|
-
const obj = value;
|
|
75
|
-
const keys = Object.keys(obj).sort();
|
|
76
|
-
const pairs = [];
|
|
77
|
-
for (const key of keys) {
|
|
78
|
-
if (obj[key] === void 0) continue;
|
|
79
|
-
pairs.push(JSON.stringify(key) + ":" + stableStringify(obj[key]));
|
|
80
|
-
}
|
|
81
|
-
return "{" + pairs.join(",") + "}";
|
|
82
|
-
}
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region src/cache/singleflight.ts
|
|
85
|
-
function createSingleflight() {
|
|
86
|
-
const inflight = /* @__PURE__ */ new Map();
|
|
87
|
-
return { do(key, fn) {
|
|
88
|
-
const existing = inflight.get(key);
|
|
89
|
-
if (existing) return existing;
|
|
90
|
-
const promise = fn().finally(() => {
|
|
91
|
-
inflight.delete(key);
|
|
92
|
-
});
|
|
93
|
-
inflight.set(key, promise);
|
|
94
|
-
return promise;
|
|
95
|
-
} };
|
|
96
|
-
}
|
|
97
|
-
//#endregion
|
|
98
|
-
//#region src/cache/fast-hash.ts
|
|
99
|
-
/**
|
|
100
|
-
* Fast non-cryptographic hash for cache keys.
|
|
101
|
-
*
|
|
102
|
-
* FNV-1a 64-bit produces a well-distributed hash with a collision
|
|
103
|
-
* probability of ~1 in 5 billion at 77k keys (birthday paradox).
|
|
104
|
-
* Not suitable for security, but ideal for cache key generation
|
|
105
|
-
* where we need speed over crypto strength.
|
|
106
|
-
*
|
|
107
|
-
* Uses BigInt for 64-bit arithmetic — supported in all modern runtimes
|
|
108
|
-
* including Cloudflare Workers. No node:crypto dependency.
|
|
109
|
-
*
|
|
110
|
-
* See TIM-370.
|
|
111
|
-
*/
|
|
112
|
-
var FNV_OFFSET_BASIS = 14695981039346656037n;
|
|
113
|
-
var FNV_PRIME = 1099511628211n;
|
|
114
|
-
var MASK_64 = 18446744073709551615n;
|
|
115
|
-
/**
|
|
116
|
-
* Compute a 64-bit FNV-1a hash of a string, returned as a 16-char hex string.
|
|
117
|
-
*
|
|
118
|
-
* 64 bits gives ~5 billion keys before a 50% collision probability
|
|
119
|
-
* (birthday paradox), making accidental collisions effectively impossible
|
|
120
|
-
* for cache key use cases.
|
|
121
|
-
*/
|
|
122
|
-
function fnv1aHash(input) {
|
|
123
|
-
let hash = FNV_OFFSET_BASIS;
|
|
124
|
-
for (let i = 0; i < input.length; i++) {
|
|
125
|
-
hash ^= BigInt(input.charCodeAt(i));
|
|
126
|
-
hash = hash * FNV_PRIME & MASK_64;
|
|
127
|
-
}
|
|
128
|
-
return hash.toString(16).padStart(16, "0");
|
|
129
|
-
}
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region src/cache/timber-cache.ts
|
|
132
|
-
var singleflight$1 = createSingleflight();
|
|
133
|
-
/**
|
|
134
|
-
* Generate a cache key from function identity and serialized args.
|
|
135
|
-
*
|
|
136
|
-
* Uses FNV-1a (fast non-crypto hash) instead of SHA-256. Cache keys don't
|
|
137
|
-
* need collision resistance — they need speed. The fnId prefix provides
|
|
138
|
-
* namespace isolation; the hash covers the args.
|
|
139
|
-
*
|
|
140
|
-
* See TIM-370 for perf motivation.
|
|
141
|
-
*/
|
|
142
|
-
function defaultKeyGenerator(fnId, args) {
|
|
143
|
-
const raw = fnId + ":" + stableStringify(args);
|
|
144
|
-
return fnId + ":" + fnv1aHash(raw);
|
|
145
|
-
}
|
|
146
|
-
/**
|
|
147
|
-
* Resolve tags from the options — supports static array or function form.
|
|
148
|
-
*/
|
|
149
|
-
function resolveTags$1(opts, args) {
|
|
150
|
-
if (!opts.tags) return [];
|
|
151
|
-
if (Array.isArray(opts.tags)) return opts.tags;
|
|
152
|
-
return opts.tags(...args);
|
|
153
|
-
}
|
|
154
|
-
var fnIdCounter = 0;
|
|
155
|
-
/**
|
|
156
|
-
* Creates a cached wrapper around an async function.
|
|
157
|
-
*
|
|
158
|
-
* - SHA-256 default keys with normalized JSON args
|
|
159
|
-
* - Singleflight: concurrent misses → single execution
|
|
160
|
-
* - SWR: serve stale immediately, background refetch
|
|
161
|
-
* - Tags as string[] or function of args
|
|
162
|
-
* - No ALS dependency
|
|
163
|
-
*
|
|
164
|
-
* Cache hits/misses are recorded as OTEL span events on the enclosing
|
|
165
|
-
* span (not child spans). The DevSpanProcessor reads these for dev log output.
|
|
166
|
-
*/
|
|
167
|
-
function createCache(fn, opts, handler) {
|
|
168
|
-
const fnId = `timber-cache:${fnIdCounter++}`;
|
|
169
|
-
return async (...args) => {
|
|
170
|
-
const key = opts.key ? opts.key(...args) : defaultKeyGenerator(fnId, args);
|
|
171
|
-
const cacheStart = performance.now();
|
|
172
|
-
const cached = await handler.get(key);
|
|
173
|
-
if (cached && !cached.stale) {
|
|
174
|
-
addSpanEventSync("timber.cache.hit", {
|
|
175
|
-
key,
|
|
176
|
-
duration_ms: Math.round(performance.now() - cacheStart)
|
|
177
|
-
});
|
|
178
|
-
return cached.value;
|
|
179
|
-
}
|
|
180
|
-
if (cached && cached.stale && opts.staleWhileRevalidate) {
|
|
181
|
-
addSpanEventSync("timber.cache.hit", {
|
|
182
|
-
key,
|
|
183
|
-
duration_ms: Math.round(performance.now() - cacheStart),
|
|
184
|
-
stale: true
|
|
185
|
-
});
|
|
186
|
-
singleflight$1.do(`swr:${key}`, async () => {
|
|
187
|
-
try {
|
|
188
|
-
const fresh = await fn(...args);
|
|
189
|
-
const tags = resolveTags$1(opts, args);
|
|
190
|
-
await handler.set(key, fresh, {
|
|
191
|
-
ttl: opts.ttl,
|
|
192
|
-
tags
|
|
193
|
-
});
|
|
194
|
-
} catch {}
|
|
195
|
-
}).catch(() => {});
|
|
196
|
-
return cached.value;
|
|
197
|
-
}
|
|
198
|
-
const result = await singleflight$1.do(key, () => fn(...args));
|
|
199
|
-
const tags = resolveTags$1(opts, args);
|
|
200
|
-
await handler.set(key, result, {
|
|
201
|
-
ttl: opts.ttl,
|
|
202
|
-
tags
|
|
203
|
-
});
|
|
204
|
-
addSpanEventSync("timber.cache.miss", {
|
|
205
|
-
key,
|
|
206
|
-
duration_ms: Math.round(performance.now() - cacheStart)
|
|
207
|
-
});
|
|
208
|
-
return result;
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
/**
|
|
212
|
-
* Invalidate cache entries by tag or key.
|
|
213
|
-
*/
|
|
214
|
-
createCache.invalidate = async function invalidate(handler, opts) {
|
|
215
|
-
await handler.invalidate(opts);
|
|
216
|
-
};
|
|
217
|
-
//#endregion
|
|
218
|
-
//#region src/cache/register-cached-function.ts
|
|
219
|
-
var singleflight = createSingleflight();
|
|
220
|
-
var REQUEST_SPECIFIC_PROPS = new Set([
|
|
221
|
-
"cookies",
|
|
222
|
-
"cookie",
|
|
223
|
-
"session",
|
|
224
|
-
"sessionId",
|
|
225
|
-
"token",
|
|
226
|
-
"authorization",
|
|
227
|
-
"auth",
|
|
228
|
-
"headers"
|
|
229
|
-
]);
|
|
230
|
-
/**
|
|
231
|
-
* Generate a cache key from a stable function ID and serialized args.
|
|
232
|
-
*
|
|
233
|
-
* Uses FNV-1a (fast non-crypto hash) instead of SHA-256. The id prefix
|
|
234
|
-
* provides namespace isolation; the hash covers the args.
|
|
235
|
-
* See TIM-370.
|
|
236
|
-
*/
|
|
237
|
-
function generateKey(id, args) {
|
|
238
|
-
const raw = id + ":" + stableStringify(args);
|
|
239
|
-
return id + ":" + fnv1aHash(raw);
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Resolve tags from options — supports static array or function form.
|
|
243
|
-
*/
|
|
244
|
-
function resolveTags(opts, args) {
|
|
245
|
-
if (!opts.tags) return [];
|
|
246
|
-
if (Array.isArray(opts.tags)) return opts.tags;
|
|
247
|
-
return opts.tags(...args);
|
|
248
|
-
}
|
|
249
|
-
/**
|
|
250
|
-
* Checks if component props contain request-specific keys and emits a dev warning.
|
|
251
|
-
* Only runs when process.env.NODE_ENV !== 'production'.
|
|
252
|
-
*/
|
|
253
|
-
function warnRequestSpecificProps(id, props) {
|
|
254
|
-
if (typeof props !== "object" || props === null) return;
|
|
255
|
-
const suspicious = Object.keys(props).filter((k) => REQUEST_SPECIFIC_PROPS.has(k.toLowerCase()));
|
|
256
|
-
if (suspicious.length > 0) console.warn(`[timber] "use cache" component ${id} received request-specific props: ${suspicious.join(", ")}. This may serve one user's cached render to another user. Remove request-specific data from props or remove "use cache".`);
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Runtime for the "use cache" directive transform. Wraps an async function
|
|
260
|
-
* with caching using the same cache handler as timber.cache.
|
|
261
|
-
*
|
|
262
|
-
* The stable `id` (file path + function name) ensures cache keys are consistent
|
|
263
|
-
* across builds. Args/props are hashed with SHA-256 for the per-call key.
|
|
264
|
-
*/
|
|
265
|
-
function registerCachedFunction(fn, opts, handler) {
|
|
266
|
-
return async (...args) => {
|
|
267
|
-
if (opts.isComponent && process.env.NODE_ENV !== "production" && args.length > 0) warnRequestSpecificProps(opts.id, args[0]);
|
|
268
|
-
const key = generateKey(opts.id, args);
|
|
269
|
-
const cached = await handler.get(key);
|
|
270
|
-
if (cached && !cached.stale) return cached.value;
|
|
271
|
-
const result = await singleflight.do(key, () => fn(...args));
|
|
272
|
-
const tags = resolveTags(opts, args);
|
|
273
|
-
await handler.set(key, result, {
|
|
274
|
-
ttl: opts.ttl,
|
|
275
|
-
tags
|
|
276
|
-
});
|
|
277
|
-
return result;
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
//#endregion
|
|
281
104
|
//#region src/cache/index.ts
|
|
282
105
|
var MemoryCacheHandler = class {
|
|
283
106
|
store = /* @__PURE__ */ new Map();
|
|
284
|
-
|
|
107
|
+
maxEntries;
|
|
108
|
+
maxBytes;
|
|
109
|
+
maxEntryBytes;
|
|
110
|
+
currentBytes = 0;
|
|
111
|
+
_trackBytes;
|
|
285
112
|
constructor(opts) {
|
|
286
|
-
this.
|
|
113
|
+
this.maxEntries = opts?.maxEntries ?? opts?.maxSize ?? 1e3;
|
|
114
|
+
this.maxBytes = opts?.maxBytes;
|
|
115
|
+
this.maxEntryBytes = opts?.maxEntryBytes;
|
|
116
|
+
this._trackBytes = this.maxBytes !== void 0 || this.maxEntryBytes !== void 0;
|
|
287
117
|
}
|
|
288
118
|
async get(key) {
|
|
289
119
|
const entry = this.store.get(key);
|
|
@@ -297,30 +127,60 @@ var MemoryCacheHandler = class {
|
|
|
297
127
|
};
|
|
298
128
|
}
|
|
299
129
|
async set(key, value, opts) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
130
|
+
const byteSize = this._trackBytes ? estimateByteSize(value) : 0;
|
|
131
|
+
if (this.maxEntryBytes !== void 0 && byteSize > this.maxEntryBytes) return;
|
|
132
|
+
if (this.store.has(key)) {
|
|
133
|
+
const existing = this.store.get(key);
|
|
134
|
+
this.currentBytes -= existing.byteSize;
|
|
135
|
+
this.store.delete(key);
|
|
136
|
+
}
|
|
137
|
+
while (this.store.size >= this.maxEntries) this.evictOldest();
|
|
138
|
+
if (this.maxBytes !== void 0) {
|
|
139
|
+
while (this.currentBytes + byteSize > this.maxBytes && this.store.size > 0) this.evictOldest();
|
|
140
|
+
if (this.currentBytes + byteSize > this.maxBytes) return;
|
|
305
141
|
}
|
|
306
142
|
this.store.set(key, {
|
|
307
143
|
value,
|
|
308
144
|
expiresAt: Date.now() + opts.ttl * 1e3,
|
|
309
|
-
tags: opts.tags
|
|
145
|
+
tags: opts.tags,
|
|
146
|
+
byteSize
|
|
310
147
|
});
|
|
148
|
+
this.currentBytes += byteSize;
|
|
311
149
|
}
|
|
312
150
|
async invalidate(opts) {
|
|
313
|
-
if (opts.key)
|
|
151
|
+
if (opts.key) {
|
|
152
|
+
const entry = this.store.get(opts.key);
|
|
153
|
+
if (entry) {
|
|
154
|
+
this.currentBytes -= entry.byteSize;
|
|
155
|
+
this.store.delete(opts.key);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
314
158
|
if (opts.tag) {
|
|
315
|
-
for (const [key, entry] of this.store) if (entry.tags.includes(opts.tag))
|
|
159
|
+
for (const [key, entry] of this.store) if (entry.tags.includes(opts.tag)) {
|
|
160
|
+
this.currentBytes -= entry.byteSize;
|
|
161
|
+
this.store.delete(key);
|
|
162
|
+
}
|
|
316
163
|
}
|
|
317
164
|
}
|
|
318
165
|
/** Number of entries currently in the cache. */
|
|
319
166
|
get size() {
|
|
320
167
|
return this.store.size;
|
|
321
168
|
}
|
|
169
|
+
/** Estimated total byte size of all cached values. Only tracked when maxBytes or maxEntryBytes is configured; returns 0 otherwise. */
|
|
170
|
+
get bytes() {
|
|
171
|
+
return this.currentBytes;
|
|
172
|
+
}
|
|
173
|
+
/** Evict the oldest entry (front of Map). */
|
|
174
|
+
evictOldest() {
|
|
175
|
+
const oldest = this.store.keys().next().value;
|
|
176
|
+
if (oldest !== void 0) {
|
|
177
|
+
const entry = this.store.get(oldest);
|
|
178
|
+
this.currentBytes -= entry.byteSize;
|
|
179
|
+
this.store.delete(oldest);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
322
182
|
};
|
|
323
183
|
//#endregion
|
|
324
|
-
export { MemoryCacheHandler, RedisCacheHandler,
|
|
184
|
+
export { MemoryCacheHandler, RedisCacheHandler, cache, estimateByteSize, getCacheHandler, setCacheHandler, setDefaultSingleflightTimeout };
|
|
325
185
|
|
|
326
186
|
//# sourceMappingURL=index.js.map
|
package/dist/cache/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/cache/redis-handler.ts","../../src/cache/stable-stringify.ts","../../src/cache/singleflight.ts","../../src/cache/fast-hash.ts","../../src/cache/timber-cache.ts","../../src/cache/register-cached-function.ts","../../src/cache/index.ts"],"sourcesContent":["import type { CacheHandler } from './index';\n\n/**\n * Minimal Redis client interface — compatible with ioredis, node-redis, and\n * Cloudflare Workers Redis bindings. We depend on the interface, not the\n * implementation, so users bring their own Redis client.\n */\nexport interface RedisClient {\n get(key: string): Promise<string | null>;\n set(key: string, value: string, ...args: unknown[]): Promise<unknown>;\n del(key: string | string[]): Promise<number>;\n sadd(key: string, ...members: string[]): Promise<number>;\n smembers(key: string): Promise<string[]>;\n}\n\nconst KEY_PREFIX = 'timber:cache:';\nconst TAG_PREFIX = 'timber:tag:';\n\n/**\n * Redis-backed CacheHandler for distributed caching.\n *\n * All instances sharing the same Redis see each other's cache entries and\n * invalidations. Tag-based invalidation uses Redis Sets to track which keys\n * belong to which tags.\n *\n * Bring your own Redis client — any client implementing the RedisClient\n * interface works (ioredis, node-redis, @upstash/redis, etc.).\n */\nexport class RedisCacheHandler implements CacheHandler {\n private client: RedisClient;\n private prefix: string;\n\n constructor(client: RedisClient, opts?: { prefix?: string }) {\n this.client = client;\n this.prefix = opts?.prefix ?? '';\n }\n\n private cacheKey(key: string): string {\n return `${this.prefix}${KEY_PREFIX}${key}`;\n }\n\n private tagKey(tag: string): string {\n return `${this.prefix}${TAG_PREFIX}${tag}`;\n }\n\n async get(key: string): Promise<{ value: unknown; stale: boolean } | null> {\n const raw = await this.client.get(this.cacheKey(key));\n if (raw === null) return null;\n\n const entry = JSON.parse(raw) as { value: unknown; expiresAt: number };\n const stale = Date.now() > entry.expiresAt;\n return { value: entry.value, stale };\n }\n\n async set(key: string, value: unknown, opts: { ttl: number; tags: string[] }): Promise<void> {\n const ck = this.cacheKey(key);\n const expiresAt = Date.now() + opts.ttl * 1000;\n const payload = JSON.stringify({ value, expiresAt });\n\n // Redis TTL with generous margin beyond the logical TTL to allow SWR reads\n // on stale entries. The logical staleness is determined by expiresAt.\n // We use 2x TTL + 60s as the Redis expiry so stale entries remain\n // available for SWR background refetches.\n const redisTtlSeconds = Math.max(opts.ttl * 2 + 60, 120);\n await this.client.set(ck, payload, 'EX', redisTtlSeconds);\n\n // Track key membership in each tag set\n for (const tag of opts.tags) {\n await this.client.sadd(this.tagKey(tag), key);\n }\n }\n\n async invalidate(opts: { key?: string; tag?: string }): Promise<void> {\n if (opts.key) {\n await this.client.del(this.cacheKey(opts.key));\n }\n\n if (opts.tag) {\n const tk = this.tagKey(opts.tag);\n const keys = await this.client.smembers(tk);\n\n if (keys.length > 0) {\n const cacheKeys = keys.map((k) => this.cacheKey(k));\n await this.client.del(cacheKeys);\n }\n\n // Clean up the tag set itself\n await this.client.del(tk);\n }\n }\n}\n","/**\n * Deterministic JSON serialization with sorted object keys.\n * Used for cache key generation — ensures { a: 1, b: 2 } and { b: 2, a: 1 }\n * produce the same string.\n */\nexport function stableStringify(value: unknown): string {\n if (value === null || value === undefined) return JSON.stringify(value);\n if (typeof value !== 'object') return JSON.stringify(value);\n if (Array.isArray(value)) {\n return '[' + value.map((item) => stableStringify(item)).join(',') + ']';\n }\n\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj).sort();\n const pairs: string[] = [];\n for (const key of keys) {\n if (obj[key] === undefined) continue;\n pairs.push(JSON.stringify(key) + ':' + stableStringify(obj[key]));\n }\n return '{' + pairs.join(',') + '}';\n}\n","/**\n * Singleflight coalesces concurrent calls with the same key into a single\n * execution. All callers receive the same result (or error).\n *\n * Per-process, in-memory. Each process coalesces independently.\n */\nexport interface Singleflight {\n do<T>(key: string, fn: () => Promise<T>): Promise<T>;\n}\n\nexport function createSingleflight(): Singleflight {\n const inflight = new Map<string, Promise<unknown>>();\n\n return {\n do<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const existing = inflight.get(key);\n if (existing) return existing as Promise<T>;\n\n const promise = fn().finally(() => {\n inflight.delete(key);\n });\n inflight.set(key, promise);\n return promise;\n },\n };\n}\n","/**\n * Fast non-cryptographic hash for cache keys.\n *\n * FNV-1a 64-bit produces a well-distributed hash with a collision\n * probability of ~1 in 5 billion at 77k keys (birthday paradox).\n * Not suitable for security, but ideal for cache key generation\n * where we need speed over crypto strength.\n *\n * Uses BigInt for 64-bit arithmetic — supported in all modern runtimes\n * including Cloudflare Workers. No node:crypto dependency.\n *\n * See TIM-370.\n */\n\n// FNV-1a constants for 64-bit hash\nconst FNV_OFFSET_BASIS = 0xcbf29ce484222325n;\nconst FNV_PRIME = 0x100000001b3n;\nconst MASK_64 = 0xffffffffffffffffn;\n\n/**\n * Compute a 64-bit FNV-1a hash of a string, returned as a 16-char hex string.\n *\n * 64 bits gives ~5 billion keys before a 50% collision probability\n * (birthday paradox), making accidental collisions effectively impossible\n * for cache key use cases.\n */\nexport function fnv1aHash(input: string): string {\n let hash = FNV_OFFSET_BASIS;\n for (let i = 0; i < input.length; i++) {\n hash ^= BigInt(input.charCodeAt(i));\n hash = (hash * FNV_PRIME) & MASK_64;\n }\n return hash.toString(16).padStart(16, '0');\n}\n","import type { CacheHandler, CacheOptions } from './index';\nimport { stableStringify } from './stable-stringify';\nimport { createSingleflight } from './singleflight';\nimport { addSpanEventSync } from '#/server/tracing.js';\nimport { fnv1aHash } from './fast-hash.js';\n\nconst singleflight = createSingleflight();\n\n/**\n * Generate a cache key from function identity and serialized args.\n *\n * Uses FNV-1a (fast non-crypto hash) instead of SHA-256. Cache keys don't\n * need collision resistance — they need speed. The fnId prefix provides\n * namespace isolation; the hash covers the args.\n *\n * See TIM-370 for perf motivation.\n */\nfunction defaultKeyGenerator(fnId: string, args: unknown[]): string {\n const raw = fnId + ':' + stableStringify(args);\n return fnId + ':' + fnv1aHash(raw);\n}\n\n/**\n * Resolve tags from the options — supports static array or function form.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction resolveTags<Fn extends (...args: any[]) => any>(\n opts: CacheOptions<Fn>,\n args: Parameters<Fn>\n): string[] {\n if (!opts.tags) return [];\n if (Array.isArray(opts.tags)) return opts.tags;\n return opts.tags(...args);\n}\n\n// Counter for generating unique function IDs when no explicit key is provided.\nlet fnIdCounter = 0;\n\n/**\n * Creates a cached wrapper around an async function.\n *\n * - SHA-256 default keys with normalized JSON args\n * - Singleflight: concurrent misses → single execution\n * - SWR: serve stale immediately, background refetch\n * - Tags as string[] or function of args\n * - No ALS dependency\n *\n * Cache hits/misses are recorded as OTEL span events on the enclosing\n * span (not child spans). The DevSpanProcessor reads these for dev log output.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function createCache<Fn extends (...args: any[]) => Promise<any>>(\n fn: Fn,\n opts: CacheOptions<Fn>,\n handler: CacheHandler\n): (...args: Parameters<Fn>) => Promise<Awaited<ReturnType<Fn>>> {\n const fnId = `timber-cache:${fnIdCounter++}`;\n\n return async (...args: Parameters<Fn>): Promise<Awaited<ReturnType<Fn>>> => {\n const key = opts.key ? opts.key(...args) : defaultKeyGenerator(fnId, args);\n\n const cacheStart = performance.now();\n const cached = await handler.get(key);\n\n if (cached && !cached.stale) {\n // Record as OTEL span event on enclosing span (not a child span).\n // Fire-and-forget — no microtask overhead on the cache hot path.\n addSpanEventSync('timber.cache.hit', {\n key,\n duration_ms: Math.round(performance.now() - cacheStart),\n });\n return cached.value as Awaited<ReturnType<Fn>>;\n }\n\n if (cached && cached.stale && opts.staleWhileRevalidate) {\n // Record stale cache hit as OTEL span event (fire-and-forget).\n addSpanEventSync('timber.cache.hit', {\n key,\n duration_ms: Math.round(performance.now() - cacheStart),\n stale: true,\n });\n // Serve stale immediately, trigger background refetch\n singleflight\n .do(`swr:${key}`, async () => {\n try {\n const fresh = await fn(...args);\n const tags = resolveTags(opts, args);\n await handler.set(key, fresh, { ttl: opts.ttl, tags });\n } catch {\n // Failed refetch — stale entry continues to be served.\n // Error is swallowed per design doc: \"Error is logged.\"\n }\n })\n .catch(() => {\n // Singleflight promise rejection handled — stale continues.\n });\n return cached.value as Awaited<ReturnType<Fn>>;\n }\n\n // Cache miss (or stale without SWR) — execute with singleflight\n const result = await singleflight.do(key, () => fn(...args));\n const tags = resolveTags(opts, args);\n await handler.set(key, result, { ttl: opts.ttl, tags });\n\n // Record cache miss as OTEL span event (fire-and-forget).\n addSpanEventSync('timber.cache.miss', {\n key,\n duration_ms: Math.round(performance.now() - cacheStart),\n });\n\n return result as Awaited<ReturnType<Fn>>;\n };\n}\n\n/**\n * Invalidate cache entries by tag or key.\n */\ncreateCache.invalidate = async function invalidate(\n handler: CacheHandler,\n opts: { key?: string; tag?: string }\n): Promise<void> {\n await handler.invalidate(opts);\n};\n","import type { CacheHandler } from './index';\nimport { stableStringify } from './stable-stringify';\nimport { createSingleflight } from './singleflight';\nimport { fnv1aHash } from './fast-hash.js';\n\nconst singleflight = createSingleflight();\n\n// Prop names that suggest request-specific data — triggers dev warning for \"use cache\" components.\nconst REQUEST_SPECIFIC_PROPS = new Set([\n 'cookies',\n 'cookie',\n 'session',\n 'sessionId',\n 'token',\n 'authorization',\n 'auth',\n 'headers',\n]);\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface RegisterCachedFunctionOptions<Fn extends (...args: any[]) => any> {\n ttl: number;\n id: string;\n tags?: string[] | ((...args: Parameters<Fn>) => string[]);\n /** True when the cached function is a React component (PascalCase name). */\n isComponent?: boolean;\n}\n\n/**\n * Generate a cache key from a stable function ID and serialized args.\n *\n * Uses FNV-1a (fast non-crypto hash) instead of SHA-256. The id prefix\n * provides namespace isolation; the hash covers the args.\n * See TIM-370.\n */\nfunction generateKey(id: string, args: unknown[]): string {\n const raw = id + ':' + stableStringify(args);\n return id + ':' + fnv1aHash(raw);\n}\n\n/**\n * Resolve tags from options — supports static array or function form.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction resolveTags<Fn extends (...args: any[]) => any>(\n opts: RegisterCachedFunctionOptions<Fn>,\n args: Parameters<Fn>\n): string[] {\n if (!opts.tags) return [];\n if (Array.isArray(opts.tags)) return opts.tags;\n return opts.tags(...args);\n}\n\n/**\n * Checks if component props contain request-specific keys and emits a dev warning.\n * Only runs when process.env.NODE_ENV !== 'production'.\n */\nfunction warnRequestSpecificProps(id: string, props: unknown): void {\n if (typeof props !== 'object' || props === null) return;\n const keys = Object.keys(props);\n const suspicious = keys.filter((k) => REQUEST_SPECIFIC_PROPS.has(k.toLowerCase()));\n if (suspicious.length > 0) {\n console.warn(\n `[timber] \"use cache\" component ${id} received request-specific props: ${suspicious.join(', ')}. ` +\n `This may serve one user's cached render to another user. ` +\n `Remove request-specific data from props or remove \"use cache\".`\n );\n }\n}\n\n/**\n * Runtime for the \"use cache\" directive transform. Wraps an async function\n * with caching using the same cache handler as timber.cache.\n *\n * The stable `id` (file path + function name) ensures cache keys are consistent\n * across builds. Args/props are hashed with SHA-256 for the per-call key.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function registerCachedFunction<Fn extends (...args: any[]) => Promise<any>>(\n fn: Fn,\n opts: RegisterCachedFunctionOptions<Fn>,\n handler: CacheHandler\n): (...args: Parameters<Fn>) => Promise<Awaited<ReturnType<Fn>>> {\n return async (...args: Parameters<Fn>): Promise<Awaited<ReturnType<Fn>>> => {\n // Dev-mode warning for components with request-specific props\n if (opts.isComponent && process.env.NODE_ENV !== 'production' && args.length > 0) {\n warnRequestSpecificProps(opts.id, args[0]);\n }\n\n const key = generateKey(opts.id, args);\n const cached = await handler.get(key);\n\n if (cached && !cached.stale) {\n return cached.value as Awaited<ReturnType<Fn>>;\n }\n\n // Cache miss or stale — execute with singleflight\n const result = await singleflight.do(key, () => fn(...args));\n const tags = resolveTags(opts, args);\n await handler.set(key, result, { ttl: opts.ttl, tags });\n return result as Awaited<ReturnType<Fn>>;\n };\n}\n","// @timber-js/app/cache — Caching primitives\n\nexport interface CacheHandler {\n get(key: string): Promise<{ value: unknown; stale: boolean } | null>;\n set(key: string, value: unknown, opts: { ttl: number; tags: string[] }): Promise<void>;\n invalidate(opts: { key?: string; tag?: string }): Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface CacheOptions<Fn extends (...args: any[]) => any> {\n ttl: number;\n key?: (...args: Parameters<Fn>) => string;\n staleWhileRevalidate?: boolean;\n tags?: string[] | ((...args: Parameters<Fn>) => string[]);\n}\n\nexport interface MemoryCacheHandlerOptions {\n /** Maximum number of entries. Oldest accessed entries are evicted first. Default: 1000. */\n maxSize?: number;\n}\n\nexport class MemoryCacheHandler implements CacheHandler {\n private store = new Map<string, { value: unknown; expiresAt: number; tags: string[] }>();\n private maxSize: number;\n\n constructor(opts?: MemoryCacheHandlerOptions) {\n this.maxSize = opts?.maxSize ?? 1000;\n }\n\n async get(key: string) {\n const entry = this.store.get(key);\n if (!entry) return null;\n\n // Move to end of Map (most recently used) for LRU ordering\n this.store.delete(key);\n this.store.set(key, entry);\n\n const stale = Date.now() > entry.expiresAt;\n return { value: entry.value, stale };\n }\n\n async set(key: string, value: unknown, opts: { ttl: number; tags: string[] }) {\n // If key already exists, delete first to refresh insertion order\n if (this.store.has(key)) {\n this.store.delete(key);\n }\n\n // Evict oldest entries (front of Map) if at capacity\n while (this.store.size >= this.maxSize) {\n const oldest = this.store.keys().next().value;\n if (oldest !== undefined) {\n this.store.delete(oldest);\n } else {\n break;\n }\n }\n\n this.store.set(key, {\n value,\n expiresAt: Date.now() + opts.ttl * 1000,\n tags: opts.tags,\n });\n }\n\n async invalidate(opts: { key?: string; tag?: string }) {\n if (opts.key) {\n this.store.delete(opts.key);\n }\n if (opts.tag) {\n for (const [key, entry] of this.store) {\n if (entry.tags.includes(opts.tag)) {\n this.store.delete(key);\n }\n }\n }\n }\n\n /** Number of entries currently in the cache. */\n get size(): number {\n return this.store.size;\n }\n}\n\nexport { RedisCacheHandler } from './redis-handler';\nexport type { RedisClient } from './redis-handler';\nexport { createCache } from './timber-cache';\nexport { registerCachedFunction } from './register-cached-function';\nexport type { RegisterCachedFunctionOptions } from './register-cached-function';\nexport { stableStringify } from './stable-stringify';\nexport { createSingleflight } from './singleflight';\nexport type { Singleflight } from './singleflight';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/cache/sizeof.ts","../../src/cache/redis-handler.ts","../../src/cache/index.ts"],"sourcesContent":["/**\n * Lightweight byte-size estimation for cache entries.\n *\n * Estimates the in-memory byte cost of a JavaScript value using\n * JSON.stringify().length * 2 (UTF-16 char width). This is a rough\n * approximation — it doesn't account for V8 object overhead, Map\n * metadata, or non-serializable values — but it's fast and good\n * enough for cache budget enforcement.\n *\n * Values that fail JSON serialization (circular references, BigInt,\n * etc.) return 0, allowing the entry to be cached without counting\n * toward the byte budget. This is a conservative choice: it's better\n * to cache and undercount than to reject the entry.\n */\n\n/**\n * Estimate the byte size of a value.\n *\n * Uses `JSON.stringify(value).length * 2` to approximate UTF-16\n * in-memory size. Returns 0 if the value is not serializable.\n */\nexport function estimateByteSize(value: unknown): number {\n try {\n const json = JSON.stringify(value);\n if (json === undefined) return 0;\n // Each JS string character is 2 bytes (UTF-16)\n return json.length * 2;\n } catch (e) {\n // Circular reference or non-serializable value\n console.debug('[timber] sizeof estimation failed:', e instanceof Error ? e.message : e);\n return 0;\n }\n}\n","import type { CacheHandler } from './index';\n\n/**\n * Minimal Redis client contract for `RedisCacheHandler`.\n *\n * This is deliberately NOT the native shape of any client library. ioredis,\n * node-redis v4, and @upstash/redis all disagree on TTL argument shapes\n * (positional `'EX'` vs `{ EX }` vs `{ ex }`), method casing (`sadd` vs\n * `sAdd`), and array handling in `del` — a contract that pattern-matches one\n * client lets another silently drop the expiry (TIM-1030: node-redis ignored\n * the positional `'EX'` and wrote keys with no TTL). Instead, TTL semantics\n * are explicit in the signature and you adapt your client with a thin\n * wrapper, so a mismatch is a type error rather than silent unbounded\n * keyspace growth.\n *\n * @example ioredis\n * ```ts\n * import Redis from 'ioredis';\n * const redis = new Redis(process.env.REDIS_URL);\n * const client: RedisClient = {\n * get: (k) => redis.get(k),\n * set: (k, v, ttl) => redis.set(k, v, 'EX', ttl),\n * expire: (k, s) => redis.expire(k, s),\n * del: (k) => redis.del(k),\n * sadd: (k, ...m) => redis.sadd(k, ...m),\n * srem: (k, ...m) => redis.srem(k, ...m),\n * smembers: (k) => redis.smembers(k),\n * ttl: (k) => redis.ttl(k),\n * };\n * ```\n *\n * @example node-redis v4\n * ```ts\n * import { createClient } from 'redis';\n * const redis = createClient({ url: process.env.REDIS_URL });\n * await redis.connect();\n * const client: RedisClient = {\n * get: (k) => redis.get(k),\n * set: (k, v, ttl) => redis.set(k, v, { EX: ttl }),\n * expire: (k, s) => redis.expire(k, s),\n * del: (k) => redis.del(k),\n * sadd: (k, ...m) => redis.sAdd(k, m),\n * srem: (k, ...m) => redis.sRem(k, m),\n * smembers: (k) => redis.sMembers(k),\n * ttl: (k) => redis.ttl(k),\n * };\n * ```\n *\n * @example @upstash/redis\n * ```ts\n * import { Redis } from '@upstash/redis';\n * // automaticDeserialization must be off: the handler stores JSON strings\n * // and parses them itself — auto-parsing would hand back objects.\n * const redis = new Redis({ url, token, automaticDeserialization: false });\n * const client: RedisClient = {\n * get: (k) => redis.get(k),\n * set: (k, v, ttl) => redis.set(k, v, { ex: ttl }),\n * expire: (k, s) => redis.expire(k, s),\n * del: (k) => redis.del(k),\n * sadd: (k, ...m) => redis.sadd(k, m[0], ...m.slice(1)),\n * srem: (k, ...m) => redis.srem(k, m[0], ...m.slice(1)),\n * smembers: (k) => redis.smembers(k),\n * ttl: (k) => redis.ttl(k),\n * };\n * ```\n */\nexport interface RedisClient {\n /** `GET key` — the stored string, or null when absent. */\n get(key: string): Promise<string | null>;\n /**\n * `SET key value EX ttlSeconds` — store with an expiry. The TTL is a\n * required positional argument so no adapter can forget it.\n */\n set(key: string, value: string, ttlSeconds: number): Promise<unknown>;\n /** `EXPIRE key seconds` — set/refresh a key's TTL (used for tag sets). */\n expire(key: string, seconds: number): Promise<unknown>;\n /** `DEL key` — delete a single key. Multi-key deletes loop per key. */\n del(key: string): Promise<unknown>;\n /** `SADD key member [member ...]` — add members to a set. */\n sadd(key: string, ...members: string[]): Promise<unknown>;\n /** `SREM key member [member ...]` — remove members from a set. */\n srem(key: string, ...members: string[]): Promise<unknown>;\n /** `SMEMBERS key` — all members of a set. */\n smembers(key: string): Promise<string[]>;\n /**\n * `TTL key` — remaining time-to-live in seconds. Returns -2 if the key does\n * not exist, -1 if the key exists but has no expiry.\n */\n ttl(key: string): Promise<number>;\n}\n\nconst KEY_PREFIX = 'timber:cache:';\nconst TAG_PREFIX = 'timber:tag:';\n\n/**\n * Redis-backed CacheHandler for distributed caching.\n *\n * All instances sharing the same Redis see each other's cache entries and\n * invalidations. Tag-based invalidation uses Redis Sets to track which keys\n * belong to which tags.\n *\n * Bring your own Redis client wrapped to the {@link RedisClient} contract —\n * see the adapter examples on the interface for ioredis, node-redis v4, and\n * @upstash/redis.\n */\nexport class RedisCacheHandler implements CacheHandler {\n private client: RedisClient;\n private prefix: string;\n\n constructor(client: RedisClient, opts?: { prefix?: string }) {\n this.client = client;\n this.prefix = opts?.prefix ?? '';\n }\n\n private cacheKey(key: string): string {\n return `${this.prefix}${KEY_PREFIX}${key}`;\n }\n\n private tagKey(tag: string): string {\n return `${this.prefix}${TAG_PREFIX}${tag}`;\n }\n\n async get(key: string): Promise<{ value: unknown; stale: boolean } | null> {\n const raw = await this.client.get(this.cacheKey(key));\n if (raw === null) return null;\n\n const entry = JSON.parse(raw) as { value: unknown; expiresAt: number };\n const stale = Date.now() > entry.expiresAt;\n return { value: entry.value, stale };\n }\n\n async set(key: string, value: unknown, opts: { ttl: number; tags: string[] }): Promise<void> {\n const ck = this.cacheKey(key);\n const expiresAt = Date.now() + opts.ttl * 1000;\n const payload = JSON.stringify({ value, expiresAt, tags: opts.tags });\n\n // Redis TTL with generous margin beyond the logical TTL to allow SWR reads\n // on stale entries. The logical staleness is determined by expiresAt.\n // We use 2x TTL + 60s as the Redis expiry so stale entries remain\n // available for SWR background refetches.\n const redisTtlSeconds = Math.max(opts.ttl * 2 + 60, 120);\n await this.client.set(ck, payload, redisTtlSeconds);\n\n // Track key membership in each tag set, and expire the set so it doesn't\n // outlive all its members. Only extend the TTL, never shorten — a short-TTL\n // entry must not shrink a tag set that already contains long-TTL entries,\n // otherwise invalidate({tag}) would miss the long-lived entries.\n for (const tag of opts.tags) {\n await this.client.sadd(this.tagKey(tag), key);\n const currentTtl = await this.client.ttl(this.tagKey(tag));\n // Only extend, never shorten. -2 = key doesn't exist (rare race after\n // sadd), -1 = no expiry (new set or legacy — set initial TTL), positive\n // but shorter = extend. A longer current TTL means a longer-lived entry\n // set it first — don't shorten.\n if (currentTtl < redisTtlSeconds) {\n await this.client.expire(this.tagKey(tag), redisTtlSeconds);\n }\n }\n }\n\n async invalidate(opts: { key?: string; tag?: string }): Promise<void> {\n if (opts.key) {\n const raw = await this.client.get(this.cacheKey(opts.key));\n if (raw !== null) {\n const entry = JSON.parse(raw) as { tags?: string[] };\n if (entry.tags) {\n await Promise.all(entry.tags.map((tag) => this.client.srem(this.tagKey(tag), opts.key!)));\n }\n }\n await this.client.del(this.cacheKey(opts.key));\n }\n\n if (opts.tag) {\n const tk = this.tagKey(opts.tag);\n const keys = await this.client.smembers(tk);\n\n // One del per key: the only multi-key shape every client accepts.\n // ioredis takes del(...keys), node-redis takes del(key | key[]), and\n // @upstash/redis takes del(...keys) but breaks on an array argument.\n await Promise.all(keys.map((k) => this.client.del(this.cacheKey(k))));\n\n // Clean up the tag set itself\n await this.client.del(tk);\n }\n }\n}\n","// @timber-js/app/cache — Caching primitives\n\nimport { estimateByteSize } from './sizeof.js';\n\nexport interface CacheHandler {\n get(key: string): Promise<{ value: unknown; stale: boolean } | null>;\n set(key: string, value: unknown, opts: { ttl: number; tags: string[] }): Promise<void>;\n invalidate(opts: { key?: string; tag?: string }): Promise<void>;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport interface CacheOptions<Fn extends (...args: any[]) => any> {\n ttl: number;\n key?: (...args: Parameters<Fn>) => string;\n staleWhileRevalidate?: boolean;\n tags?: string[] | ((...args: Parameters<Fn>) => string[]);\n /** Timeout (ms) for singleflight-coalesced calls. Prevents hung fn() from\n * permanently blocking all future callers for the same cache key. See TIM-518. */\n timeoutMs?: number;\n}\n\nexport interface MemoryCacheHandlerOptions {\n /** Maximum number of entries. Oldest accessed entries are evicted first. Default: 1000. */\n maxEntries?: number;\n /**\n * @deprecated Use `maxEntries` instead. Will be removed in a future release.\n * Alias for `maxEntries` — maximum number of entries (not bytes).\n */\n maxSize?: number;\n /** Maximum total byte budget for all cached values. Oldest entries are evicted when exceeded. Default: no limit. */\n maxBytes?: number;\n /** Maximum byte size for a single cache entry. Entries exceeding this are silently dropped. Default: no limit. */\n maxEntryBytes?: number;\n}\n\nexport class MemoryCacheHandler implements CacheHandler {\n private store = new Map<\n string,\n { value: unknown; expiresAt: number; tags: string[]; byteSize: number }\n >();\n private maxEntries: number;\n private maxBytes: number | undefined;\n private maxEntryBytes: number | undefined;\n private currentBytes = 0;\n private readonly _trackBytes: boolean;\n\n constructor(opts?: MemoryCacheHandlerOptions) {\n // maxEntries takes precedence over deprecated maxSize\n this.maxEntries = opts?.maxEntries ?? opts?.maxSize ?? 1000;\n this.maxBytes = opts?.maxBytes;\n this.maxEntryBytes = opts?.maxEntryBytes;\n this._trackBytes = this.maxBytes !== undefined || this.maxEntryBytes !== undefined;\n }\n\n async get(key: string) {\n const entry = this.store.get(key);\n if (!entry) return null;\n\n // Move to end of Map (most recently used) for LRU ordering\n this.store.delete(key);\n this.store.set(key, entry);\n\n const stale = Date.now() > entry.expiresAt;\n return { value: entry.value, stale };\n }\n\n async set(key: string, value: unknown, opts: { ttl: number; tags: string[] }) {\n const byteSize = this._trackBytes ? estimateByteSize(value) : 0;\n\n // Reject entries exceeding per-entry byte limit\n if (this.maxEntryBytes !== undefined && byteSize > this.maxEntryBytes) {\n return;\n }\n\n // If key already exists, delete first to refresh insertion order and reclaim bytes\n if (this.store.has(key)) {\n const existing = this.store.get(key)!;\n this.currentBytes -= existing.byteSize;\n this.store.delete(key);\n }\n\n // Evict oldest entries (front of Map) if at entry count capacity\n while (this.store.size >= this.maxEntries) {\n this.evictOldest();\n }\n\n // Evict oldest entries if byte budget would be exceeded\n if (this.maxBytes !== undefined) {\n while (this.currentBytes + byteSize > this.maxBytes && this.store.size > 0) {\n this.evictOldest();\n }\n // If the single entry exceeds the total byte budget, don't store it\n if (this.currentBytes + byteSize > this.maxBytes) {\n return;\n }\n }\n\n this.store.set(key, {\n value,\n expiresAt: Date.now() + opts.ttl * 1000,\n tags: opts.tags,\n byteSize,\n });\n this.currentBytes += byteSize;\n }\n\n async invalidate(opts: { key?: string; tag?: string }) {\n if (opts.key) {\n const entry = this.store.get(opts.key);\n if (entry) {\n this.currentBytes -= entry.byteSize;\n this.store.delete(opts.key);\n }\n }\n if (opts.tag) {\n for (const [key, entry] of this.store) {\n if (entry.tags.includes(opts.tag)) {\n this.currentBytes -= entry.byteSize;\n this.store.delete(key);\n }\n }\n }\n }\n\n /** Number of entries currently in the cache. */\n get size(): number {\n return this.store.size;\n }\n\n /** Estimated total byte size of all cached values. Only tracked when maxBytes or maxEntryBytes is configured; returns 0 otherwise. */\n get bytes(): number {\n return this.currentBytes;\n }\n\n /** Evict the oldest entry (front of Map). */\n private evictOldest(): void {\n const oldest = this.store.keys().next().value;\n if (oldest !== undefined) {\n const entry = this.store.get(oldest)!;\n this.currentBytes -= entry.byteSize;\n this.store.delete(oldest);\n }\n }\n}\n\nexport { RedisCacheHandler } from './redis-handler';\nexport type { RedisClient } from './redis-handler';\nexport { cache } from './cache-api';\nexport { setCacheHandler, getCacheHandler } from './handler-store';\nexport { setDefaultSingleflightTimeout } from './timber-cache';\n// NOTE: registerCachedFunction (runtime for 'use cache' directive) removed.\n// Future feature pending design doc. See design/06-caching.md.\nexport { estimateByteSize } from './sizeof';\n// stableStringify, createSingleflight, and SingleflightTimeoutError are\n// internal utilities used by the cache implementation. They are not\n// re-exported here — import directly from the source files if needed\n// within the package. See TIM-720.\n"],"mappings":"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"}
|