@timber-js/app 0.2.0-alpha.98 → 0.2.0-alpha.99
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-CQ8Z8VGL.js +1061 -0
- package/dist/_chunks/actions-CQ8Z8VGL.js.map +1 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js +61 -0
- package/dist/_chunks/build-output-helper-DXnW0qjz.js.map +1 -0
- package/dist/_chunks/{define-Itxvcd7F.js → define-B-Q_UMOD.js} +19 -23
- package/dist/_chunks/define-B-Q_UMOD.js.map +1 -0
- package/dist/_chunks/{define-C77ScO0m.js → define-CfBPoJb0.js} +24 -7
- package/dist/_chunks/define-CfBPoJb0.js.map +1 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js +194 -0
- package/dist/_chunks/define-cookie-BjpIt4UC.js.map +1 -0
- package/dist/_chunks/{format-CYBGxKtc.js → format-Bcn-Iv1x.js} +1 -1
- package/dist/_chunks/{format-CYBGxKtc.js.map → format-Bcn-Iv1x.js.map} +1 -1
- package/dist/_chunks/handler-store-B-lqaGyh.js +54 -0
- package/dist/_chunks/handler-store-B-lqaGyh.js.map +1 -0
- package/dist/_chunks/logger-0m8MsKdc.js +291 -0
- package/dist/_chunks/logger-0m8MsKdc.js.map +1 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js +122 -0
- package/dist/_chunks/merge-search-params-BphMdht_.js.map +1 -0
- package/dist/_chunks/navigation-root-BCYczjml.js +96 -0
- package/dist/_chunks/navigation-root-BCYczjml.js.map +1 -0
- package/dist/_chunks/registry-I2ss-lvy.js +20 -0
- package/dist/_chunks/registry-I2ss-lvy.js.map +1 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js +28 -0
- package/dist/_chunks/router-ref-h3-UaCQv.js.map +1 -0
- package/dist/_chunks/{schema-bridge-C3xl_vfb.js → schema-bridge-Cxu4l-7p.js} +1 -1
- package/dist/_chunks/{schema-bridge-C3xl_vfb.js.map → schema-bridge-Cxu4l-7p.js.map} +1 -1
- package/dist/_chunks/{segment-context-fHFLF1PE.js → segment-context-Dx_OizxD.js} +1 -1
- package/dist/_chunks/{segment-context-fHFLF1PE.js.map → segment-context-Dx_OizxD.js.map} +1 -1
- package/dist/_chunks/{router-ref-C8OCm7g7.js → ssr-data-B4CdH7rE.js} +2 -26
- package/dist/_chunks/ssr-data-B4CdH7rE.js.map +1 -0
- package/dist/_chunks/{stale-reload-BX5gL1r-.js → stale-reload-Bab885FO.js} +1 -1
- package/dist/_chunks/{stale-reload-BX5gL1r-.js.map → stale-reload-Bab885FO.js.map} +1 -1
- package/dist/_chunks/tracing-C8V-YGsP.js +329 -0
- package/dist/_chunks/tracing-C8V-YGsP.js.map +1 -0
- package/dist/_chunks/{use-query-states-BiV5GJgm.js → use-query-states-B2XTqxDR.js} +3 -19
- package/dist/_chunks/use-query-states-B2XTqxDR.js.map +1 -0
- package/dist/_chunks/{use-params-IOPu7E8t.js → use-segment-params-BkpKAQ7D.js} +9 -95
- package/dist/_chunks/use-segment-params-BkpKAQ7D.js.map +1 -0
- package/dist/_chunks/{walkers-VOXgavMF.js → walkers-Tg0Alwcg.js} +6 -3
- package/dist/_chunks/walkers-Tg0Alwcg.js.map +1 -0
- package/dist/_chunks/{dev-warnings-DpGRGoDi.js → warnings-Cg47l5sk.js} +3 -3
- package/dist/_chunks/warnings-Cg47l5sk.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.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +8 -28
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/adapters/nitro.d.ts.map +1 -1
- package/dist/adapters/nitro.js +8 -26
- 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/index.js +9 -2
- package/dist/cache/index.js.map +1 -1
- package/dist/cache/timber-cache.d.ts.map +1 -1
- package/dist/client/error-boundary.js +2 -1
- package/dist/client/error-boundary.js.map +1 -1
- package/dist/client/form.d.ts +10 -24
- package/dist/client/form.d.ts.map +1 -1
- package/dist/client/index.d.ts +1 -5
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +40 -90
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +2 -1
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +81 -7
- package/dist/client/internal.js.map +1 -1
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/state.d.ts +1 -1
- package/dist/client/use-cookie.d.ts +8 -0
- package/dist/client/use-cookie.d.ts.map +1 -1
- package/dist/client/{use-params.d.ts → use-segment-params.d.ts} +1 -1
- package/dist/client/use-segment-params.d.ts.map +1 -0
- package/dist/codec.d.ts +1 -1
- package/dist/codec.d.ts.map +1 -1
- package/dist/codec.js +2 -2
- package/dist/config-types.d.ts +28 -0
- package/dist/config-types.d.ts.map +1 -1
- package/dist/cookies/define-cookie.d.ts +87 -35
- 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 +48 -2
- package/dist/cookies/index.js.map +1 -0
- 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 +46 -0
- package/dist/cookies/validation.d.ts.map +1 -0
- package/dist/{plugins/dev-404-page.d.ts → dev-tools/404-page.d.ts} +1 -1
- package/dist/dev-tools/404-page.d.ts.map +1 -0
- package/dist/{plugins/dev-browser-logs.d.ts → dev-tools/browser-logs.d.ts} +1 -1
- package/dist/dev-tools/browser-logs.d.ts.map +1 -0
- package/dist/{plugins/dev-error-page.d.ts → dev-tools/error-page.d.ts} +2 -2
- package/dist/dev-tools/error-page.d.ts.map +1 -0
- package/dist/{server/dev-holding-server.d.ts → dev-tools/holding-server.d.ts} +1 -1
- package/dist/dev-tools/holding-server.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/{server/dev-span-processor.d.ts → dev-tools/instrumentation.d.ts} +26 -6
- 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} +1 -1
- package/dist/dev-tools/logs.d.ts.map +1 -0
- package/dist/{plugins/dev-error-overlay.d.ts → dev-tools/overlay.d.ts} +3 -12
- 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/{plugins/dev-terminal-error.d.ts → dev-tools/terminal.d.ts} +2 -2
- package/dist/dev-tools/terminal.d.ts.map +1 -0
- package/dist/{server/dev-warnings.d.ts → dev-tools/warnings.d.ts} +1 -1
- package/dist/dev-tools/warnings.d.ts.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +97 -72
- package/dist/index.js.map +1 -1
- package/dist/plugin-context.d.ts +1 -1
- package/dist/plugin-context.d.ts.map +1 -1
- package/dist/plugins/adapter-build.d.ts.map +1 -1
- package/dist/routing/convention-lint.d.ts.map +1 -1
- package/dist/routing/index.js +1 -1
- package/dist/routing/scanner.d.ts.map +1 -1
- package/dist/routing/status-file-lint.d.ts.map +1 -1
- package/dist/search-params/define.d.ts +25 -7
- package/dist/search-params/define.d.ts.map +1 -1
- package/dist/search-params/index.js +5 -3
- package/dist/search-params/index.js.map +1 -1
- package/dist/search-params/wrappers.d.ts +2 -2
- package/dist/search-params/wrappers.d.ts.map +1 -1
- package/dist/segment-params/define.d.ts +23 -6
- package/dist/segment-params/define.d.ts.map +1 -1
- package/dist/segment-params/index.js +1 -1
- package/dist/server/access-gate.d.ts +4 -3
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts +15 -6
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +5 -5
- 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/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/deny-boundary.d.ts +90 -0
- package/dist/server/deny-boundary.d.ts.map +1 -0
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/early-hints-sender.d.ts.map +1 -1
- package/dist/server/index.d.ts +5 -4
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -149
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +6 -4
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +261 -408
- package/dist/server/internal.js.map +1 -1
- package/dist/server/logger.d.ts +14 -0
- package/dist/server/logger.d.ts.map +1 -1
- package/dist/server/middleware-runner.d.ts +17 -0
- package/dist/server/middleware-runner.d.ts.map +1 -1
- package/dist/server/param-coercion.d.ts +26 -0
- package/dist/server/param-coercion.d.ts.map +1 -0
- package/dist/server/pipeline-helpers.d.ts +14 -7
- package/dist/server/pipeline-helpers.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 +4 -49
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/pipeline.d.ts +0 -2
- package/dist/server/pipeline.d.ts.map +1 -1
- package/dist/server/request-context.d.ts +22 -159
- package/dist/server/request-context.d.ts.map +1 -1
- package/dist/server/route-element-builder.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/helpers.d.ts +1 -1
- package/dist/server/rsc-entry/helpers.d.ts.map +1 -1
- 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/wrap-action-dispatch.d.ts +59 -14
- package/dist/server/rsc-entry/wrap-action-dispatch.d.ts.map +1 -1
- package/dist/server/state-tree-diff.d.ts.map +1 -1
- package/dist/server/tracing.d.ts +1 -1
- package/dist/server/tracing.d.ts.map +1 -1
- package/dist/server/tree-builder.d.ts +45 -16
- package/dist/server/tree-builder.d.ts.map +1 -1
- package/dist/server/types.d.ts +48 -0
- 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/shims/headers.d.ts +2 -2
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/navigation-client.d.ts +3 -1
- package/dist/shims/navigation-client.d.ts.map +1 -1
- package/dist/shims/navigation.d.ts +9 -4
- package/dist/shims/navigation.d.ts.map +1 -1
- package/package.json +6 -7
- package/src/adapters/build-output-helper.ts +77 -0
- package/src/adapters/cloudflare.ts +10 -50
- package/src/adapters/nitro.ts +11 -45
- package/src/adapters/shared.ts +40 -0
- package/src/cache/timber-cache.ts +3 -2
- package/src/cli.ts +0 -0
- package/src/client/form.tsx +17 -25
- package/src/client/index.ts +16 -9
- package/src/client/internal.ts +3 -2
- package/src/client/router.ts +1 -1
- package/src/client/rsc-fetch.ts +15 -0
- package/src/client/state.ts +2 -2
- package/src/client/use-cookie.ts +29 -0
- package/src/codec.ts +3 -7
- package/src/config-types.ts +28 -0
- package/src/cookies/define-cookie.ts +271 -78
- package/src/cookies/index.ts +11 -8
- package/src/cookies/json-cookie.ts +105 -0
- package/src/cookies/validation.ts +134 -0
- package/src/{plugins/dev-404-page.ts → dev-tools/404-page.ts} +2 -7
- package/src/{plugins/dev-error-page.ts → dev-tools/error-page.ts} +5 -32
- package/src/dev-tools/index.ts +90 -0
- package/src/dev-tools/instrumentation.ts +176 -0
- package/src/{plugins/dev-logs.ts → dev-tools/logs.ts} +2 -2
- package/src/{plugins/dev-error-overlay.ts → dev-tools/overlay.ts} +5 -23
- package/src/dev-tools/stack-classifier.ts +75 -0
- package/src/{plugins/dev-terminal-error.ts → dev-tools/terminal.ts} +4 -38
- package/src/{server/dev-warnings.ts → dev-tools/warnings.ts} +1 -1
- package/src/index.ts +11 -3
- package/src/plugin-context.ts +1 -1
- package/src/plugins/adapter-build.ts +3 -1
- package/src/plugins/dev-server.ts +3 -3
- package/src/plugins/shims.ts +1 -1
- package/src/plugins/static-build.ts +1 -1
- package/src/routing/convention-lint.ts +5 -4
- package/src/routing/scanner.ts +5 -2
- package/src/routing/status-file-lint.ts +4 -2
- package/src/search-params/define.ts +71 -15
- package/src/search-params/wrappers.ts +9 -2
- package/src/segment-params/define.ts +66 -13
- package/src/server/access-gate.tsx +9 -8
- package/src/server/action-handler.ts +28 -38
- package/src/server/als-registry.ts +5 -5
- package/src/server/asset-headers.ts +8 -34
- package/src/server/cookie-context.ts +468 -0
- package/src/server/cookie-parsing.ts +135 -0
- package/src/server/{deny-page-resolver.ts → deny-boundary.ts} +78 -14
- package/src/server/deny-renderer.ts +2 -7
- package/src/server/early-hints-sender.ts +3 -2
- package/src/server/fallback-error.ts +1 -1
- package/src/server/index.ts +13 -14
- package/src/server/internal.ts +10 -3
- package/src/server/logger.ts +23 -0
- package/src/server/middleware-runner.ts +44 -0
- package/src/server/param-coercion.ts +76 -0
- package/src/server/pipeline-helpers.ts +37 -13
- package/src/server/pipeline-outcome.ts +167 -0
- package/src/server/pipeline-phases.ts +27 -209
- package/src/server/pipeline.ts +2 -9
- package/src/server/request-context.ts +46 -451
- package/src/server/route-element-builder.ts +7 -3
- package/src/server/rsc-entry/action-middleware-runner.ts +167 -0
- package/src/server/rsc-entry/error-renderer.ts +1 -1
- package/src/server/rsc-entry/helpers.ts +2 -7
- package/src/server/rsc-entry/index.ts +34 -273
- package/src/server/rsc-entry/render-route.ts +304 -0
- package/src/server/rsc-entry/rsc-payload.ts +1 -1
- package/src/server/rsc-entry/ssr-renderer.ts +2 -2
- package/src/server/rsc-entry/wrap-action-dispatch.ts +316 -23
- package/src/server/ssr-entry.ts +1 -1
- package/src/server/state-tree-diff.ts +4 -1
- package/src/server/tracing.ts +3 -3
- package/src/server/tree-builder.ts +128 -52
- package/src/server/types.ts +52 -0
- package/src/server/utils/escape-html.ts +20 -0
- package/src/shims/headers.ts +3 -3
- package/src/shims/navigation-client.ts +4 -3
- package/src/shims/navigation.ts +9 -7
- package/dist/_chunks/actions-DLnUaR65.js +0 -421
- package/dist/_chunks/actions-DLnUaR65.js.map +0 -1
- package/dist/_chunks/als-registry-HS0LGUl2.js +0 -41
- package/dist/_chunks/als-registry-HS0LGUl2.js.map +0 -1
- package/dist/_chunks/debug-ECi_61pb.js +0 -108
- package/dist/_chunks/debug-ECi_61pb.js.map +0 -1
- package/dist/_chunks/define-C77ScO0m.js.map +0 -1
- package/dist/_chunks/define-Itxvcd7F.js.map +0 -1
- package/dist/_chunks/define-cookie-BowvzoP0.js +0 -94
- package/dist/_chunks/define-cookie-BowvzoP0.js.map +0 -1
- package/dist/_chunks/dev-warnings-DpGRGoDi.js.map +0 -1
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js +0 -41
- package/dist/_chunks/merge-search-params-Cm_KIWDX.js.map +0 -1
- package/dist/_chunks/request-context-CK5tZqIP.js +0 -478
- package/dist/_chunks/request-context-CK5tZqIP.js.map +0 -1
- package/dist/_chunks/router-ref-C8OCm7g7.js.map +0 -1
- package/dist/_chunks/tracing-CCYbKn5n.js +0 -238
- package/dist/_chunks/tracing-CCYbKn5n.js.map +0 -1
- package/dist/_chunks/use-params-IOPu7E8t.js.map +0 -1
- package/dist/_chunks/use-query-states-BiV5GJgm.js.map +0 -1
- package/dist/_chunks/walkers-VOXgavMF.js.map +0 -1
- package/dist/client/use-params.d.ts.map +0 -1
- package/dist/plugins/dev-404-page.d.ts.map +0 -1
- package/dist/plugins/dev-browser-logs.d.ts.map +0 -1
- package/dist/plugins/dev-error-overlay.d.ts.map +0 -1
- package/dist/plugins/dev-error-page.d.ts.map +0 -1
- package/dist/plugins/dev-logs.d.ts.map +0 -1
- package/dist/plugins/dev-terminal-error.d.ts.map +0 -1
- package/dist/server/deny-page-resolver.d.ts +0 -52
- package/dist/server/deny-page-resolver.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-holding-server.d.ts.map +0 -1
- package/dist/server/dev-logger.d.ts.map +0 -1
- package/dist/server/dev-span-processor.d.ts.map +0 -1
- package/dist/server/dev-warnings.d.ts.map +0 -1
- package/dist/server/page-deny-boundary.d.ts +0 -31
- package/dist/server/page-deny-boundary.d.ts.map +0 -1
- package/src/server/dev-fetch-instrumentation.ts +0 -96
- package/src/server/dev-span-processor.ts +0 -78
- package/src/server/page-deny-boundary.tsx +0 -56
- /package/src/client/{use-params.ts → use-segment-params.ts} +0 -0
- /package/src/{plugins/dev-browser-logs.ts → dev-tools/browser-logs.ts} +0 -0
- /package/src/{server/dev-holding-server.ts → dev-tools/holding-server.ts} +0 -0
- /package/src/{server/dev-logger.ts → dev-tools/logger.ts} +0 -0
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* See design/02-rendering-pipeline.md §"Element Tree Construction"
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import type {
|
|
13
|
+
import type { ReactNode } from 'react';
|
|
14
|
+
import type { RouteFile, SegmentNode } from '../routing/types.js';
|
|
14
15
|
|
|
15
16
|
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
16
17
|
|
|
@@ -25,22 +26,87 @@ export interface LoadedModule {
|
|
|
25
26
|
/** Function that loads a route file's module. */
|
|
26
27
|
export type ModuleLoader = (file: RouteFile) => LoadedModule | Promise<LoadedModule>;
|
|
27
28
|
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
/**
|
|
30
|
+
* A React component reference loaded from a route module's default export.
|
|
31
|
+
*
|
|
32
|
+
* Loaded modules' `default` is typed as `unknown` (modules are dynamic), so
|
|
33
|
+
* call sites narrow it through `isValidElementType` (below) before treating
|
|
34
|
+
* it as a component. The signature is a callable returning `ReactNode` —
|
|
35
|
+
* TypeScript's view of every valid React component shape, including exotic
|
|
36
|
+
* components (`memo`, `forwardRef`, `lazy`) which the type system treats as
|
|
37
|
+
* callable even though their runtime values are objects with `$$typeof`
|
|
38
|
+
* markers rather than functions.
|
|
39
|
+
*/
|
|
40
|
+
export type LoadedComponent = (...args: unknown[]) => ReactNode;
|
|
41
|
+
|
|
42
|
+
// Marker symbols React stamps onto exotic component types. Mirrors the
|
|
43
|
+
// internal `isValidElementType` check in `react.development.js` — React
|
|
44
|
+
// doesn't export it, and we don't want to add `react-is` just for this one
|
|
45
|
+
// validation. Inlined the same way `isClientReference` in
|
|
46
|
+
// `route-element-builder.ts` inlines the client-reference marker.
|
|
47
|
+
const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');
|
|
48
|
+
const REACT_MEMO_TYPE = Symbol.for('react.memo');
|
|
49
|
+
const REACT_LAZY_TYPE = Symbol.for('react.lazy');
|
|
50
|
+
const REACT_PROVIDER_TYPE = Symbol.for('react.provider');
|
|
51
|
+
const REACT_CONTEXT_TYPE = Symbol.for('react.context');
|
|
52
|
+
const REACT_SUSPENSE_TYPE = Symbol.for('react.suspense');
|
|
53
|
+
const REACT_SUSPENSE_LIST_TYPE = Symbol.for('react.suspense_list');
|
|
54
|
+
const REACT_CLIENT_REFERENCE_TYPE = Symbol.for('react.client.reference');
|
|
55
|
+
|
|
56
|
+
const REACT_COMPONENT_TYPE_MARKERS: ReadonlySet<symbol> = new Set([
|
|
57
|
+
REACT_FORWARD_REF_TYPE,
|
|
58
|
+
REACT_MEMO_TYPE,
|
|
59
|
+
REACT_LAZY_TYPE,
|
|
60
|
+
REACT_PROVIDER_TYPE,
|
|
61
|
+
REACT_CONTEXT_TYPE,
|
|
62
|
+
REACT_SUSPENSE_TYPE,
|
|
63
|
+
REACT_SUSPENSE_LIST_TYPE,
|
|
64
|
+
REACT_CLIENT_REFERENCE_TYPE,
|
|
65
|
+
]);
|
|
31
66
|
|
|
32
|
-
/**
|
|
67
|
+
/**
|
|
68
|
+
* Validate that a loaded module's `default` export is something React
|
|
69
|
+
* accepts as the first argument to `createElement` — i.e. a valid component
|
|
70
|
+
* type. React doesn't export `isValidElementType` (only `isValidElement`,
|
|
71
|
+
* which checks for *elements*, not *component types*), so this mirrors
|
|
72
|
+
* React's internal check:
|
|
73
|
+
*
|
|
74
|
+
* - functions → function or class components
|
|
75
|
+
* - objects with a `$$typeof` matching one of React's known component
|
|
76
|
+
* markers → exotic components (`memo`, `forwardRef`, `lazy`, context,
|
|
77
|
+
* suspense, client references via `@vitejs/plugin-rsc`)
|
|
78
|
+
*
|
|
79
|
+
* Strings (HTML tag names) are valid for `createElement` but never appear
|
|
80
|
+
* as a route module's default export, so they're not recognized here.
|
|
81
|
+
*
|
|
82
|
+
* Anything else (numbers, plain config objects, JSON, etc.) is rejected so
|
|
83
|
+
* the boundary wrapper is skipped rather than crashing inside React.
|
|
84
|
+
*/
|
|
85
|
+
function isValidElementType(value: unknown): value is LoadedComponent {
|
|
86
|
+
if (typeof value === 'function') return true;
|
|
87
|
+
if (typeof value !== 'object' || value === null) return false;
|
|
88
|
+
const marker = (value as { $$typeof?: unknown }).$$typeof;
|
|
89
|
+
return typeof marker === 'symbol' && REACT_COMPONENT_TYPE_MARKERS.has(marker);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Function that creates a React element. Matches React.createElement signature.
|
|
94
|
+
*
|
|
95
|
+
* `props` is typed as `object | null` rather than `Record<string, unknown>` so
|
|
96
|
+
* that interface types with known keys (e.g. `AccessGateProps`,
|
|
97
|
+
* `ErrorBoundaryProps`) flow through without an explicit index-signature cast.
|
|
98
|
+
*/
|
|
33
99
|
export type CreateElement = (
|
|
34
100
|
type: unknown,
|
|
35
|
-
props:
|
|
101
|
+
props: object | null,
|
|
36
102
|
...children: unknown[]
|
|
37
|
-
) =>
|
|
103
|
+
) => ReactNode;
|
|
38
104
|
|
|
39
105
|
/**
|
|
40
106
|
* Resolved slot content for a layout.
|
|
41
107
|
* Key is slot name (without @), value is the element tree for that slot.
|
|
42
108
|
*/
|
|
43
|
-
export type SlotElements = Map<string,
|
|
109
|
+
export type SlotElements = Map<string, ReactNode>;
|
|
44
110
|
|
|
45
111
|
/** Configuration for the tree builder. */
|
|
46
112
|
export interface TreeBuilderConfig {
|
|
@@ -92,8 +158,8 @@ export interface AccessGateProps {
|
|
|
92
158
|
* This prevents the error from reaching React Flight, eliminating the
|
|
93
159
|
* second render pass. See TIM-666.
|
|
94
160
|
*/
|
|
95
|
-
denyPages?: import('./deny-
|
|
96
|
-
children:
|
|
161
|
+
denyPages?: import('./deny-boundary.js').DenyPageEntry[];
|
|
162
|
+
children: ReactNode;
|
|
97
163
|
}
|
|
98
164
|
|
|
99
165
|
/**
|
|
@@ -107,24 +173,33 @@ export interface AccessGateProps {
|
|
|
107
173
|
export interface SlotAccessGateProps {
|
|
108
174
|
accessFn: () => unknown;
|
|
109
175
|
/** The denied.tsx component (not a pre-built element). null if no denied.tsx exists. */
|
|
110
|
-
DeniedComponent:
|
|
176
|
+
DeniedComponent: LoadedComponent | null;
|
|
111
177
|
/** Slot directory name without @ prefix (e.g. "admin", "sidebar"). */
|
|
112
178
|
slotName: string;
|
|
113
179
|
/** createElement function for building elements dynamically. */
|
|
114
180
|
createElement: CreateElement;
|
|
115
|
-
defaultFallback:
|
|
116
|
-
children:
|
|
181
|
+
defaultFallback: ReactNode;
|
|
182
|
+
children: ReactNode;
|
|
117
183
|
}
|
|
118
184
|
|
|
119
185
|
/**
|
|
120
186
|
* Framework-injected error boundary wrapper.
|
|
121
187
|
* Wraps content with status-code error boundary handling.
|
|
188
|
+
*
|
|
189
|
+
* Field types must agree with `TimberErrorBoundaryProps` in
|
|
190
|
+
* `client/error-boundary.tsx`. The two are kept structurally compatible by
|
|
191
|
+
* convention rather than by direct type import — tree-builder.ts is the
|
|
192
|
+
* server-side construction site and may not import types from a 'use client'
|
|
193
|
+
* module to keep the server barrel free of client coupling.
|
|
122
194
|
*/
|
|
123
195
|
export interface ErrorBoundaryProps {
|
|
124
|
-
|
|
125
|
-
|
|
196
|
+
/** The component to render when an error is caught (TSX status files). */
|
|
197
|
+
fallbackComponent?: LoadedComponent;
|
|
198
|
+
/** Pre-rendered fallback element (MDX status files — see TIM-503). */
|
|
199
|
+
fallbackElement?: ReactNode;
|
|
200
|
+
/** Status code filter: 400 = any 4xx, 500 = any 5xx, specific number = exact match. */
|
|
126
201
|
status?: number;
|
|
127
|
-
children:
|
|
202
|
+
children: ReactNode;
|
|
128
203
|
}
|
|
129
204
|
|
|
130
205
|
// ─── Tree Builder ────────────────────────────────────────────────────────────
|
|
@@ -133,8 +208,11 @@ export interface ErrorBoundaryProps {
|
|
|
133
208
|
* Result of building the element tree.
|
|
134
209
|
*/
|
|
135
210
|
export interface TreeBuildResult {
|
|
136
|
-
/**
|
|
137
|
-
|
|
211
|
+
/**
|
|
212
|
+
* The root React element tree ready for renderToReadableStream.
|
|
213
|
+
* `null` for API routes (route.ts), which don't render a React tree.
|
|
214
|
+
*/
|
|
215
|
+
tree: ReactNode;
|
|
138
216
|
/** Whether the leaf segment is a route.ts (API endpoint) rather than a page. */
|
|
139
217
|
isApiRoute: boolean;
|
|
140
218
|
}
|
|
@@ -167,7 +245,7 @@ export async function buildElementTree(config: TreeBuilderConfig): Promise<TreeB
|
|
|
167
245
|
|
|
168
246
|
// Start with the page component
|
|
169
247
|
const pageModule = leaf.page ? await loadModule(leaf.page) : null;
|
|
170
|
-
const PageComponent = pageModule?.default as
|
|
248
|
+
const PageComponent = pageModule?.default as LoadedComponent | undefined;
|
|
171
249
|
|
|
172
250
|
if (!PageComponent) {
|
|
173
251
|
throw new Error(
|
|
@@ -177,7 +255,7 @@ export async function buildElementTree(config: TreeBuilderConfig): Promise<TreeB
|
|
|
177
255
|
}
|
|
178
256
|
|
|
179
257
|
// Build the page element — params are accessed via getSegmentParams() from ALS
|
|
180
|
-
let element:
|
|
258
|
+
let element: ReactNode = createElement(PageComponent, {});
|
|
181
259
|
|
|
182
260
|
// Build tree bottom-up: wrap page, then walk segments from leaf to root
|
|
183
261
|
for (let i = segments.length - 1; i >= 0; i--) {
|
|
@@ -206,13 +284,11 @@ export async function buildElementTree(config: TreeBuilderConfig): Promise<TreeB
|
|
|
206
284
|
// Wrap in layout (if exists and not the leaf's page-level wrapping)
|
|
207
285
|
if (segment.layout) {
|
|
208
286
|
const layoutModule = await loadModule(segment.layout);
|
|
209
|
-
const LayoutComponent = layoutModule.default as
|
|
210
|
-
| ((...args: unknown[]) => ReactElement)
|
|
211
|
-
| undefined;
|
|
287
|
+
const LayoutComponent = layoutModule.default as LoadedComponent | undefined;
|
|
212
288
|
|
|
213
289
|
if (LayoutComponent) {
|
|
214
290
|
// Resolve parallel slots for this layout
|
|
215
|
-
const slotProps: Record<string,
|
|
291
|
+
const slotProps: Record<string, ReactNode> = {};
|
|
216
292
|
const slotNames = Object.keys(segment.slots);
|
|
217
293
|
if (slotNames.length > 0) {
|
|
218
294
|
for (const slotName of slotNames) {
|
|
@@ -250,23 +326,21 @@ async function buildSlotElement(
|
|
|
250
326
|
loadModule: ModuleLoader,
|
|
251
327
|
createElement: CreateElement,
|
|
252
328
|
errorBoundaryComponent: unknown
|
|
253
|
-
): Promise<
|
|
329
|
+
): Promise<ReactNode> {
|
|
254
330
|
// Load slot page
|
|
255
331
|
const pageModule = slotNode.page ? await loadModule(slotNode.page) : null;
|
|
256
|
-
const PageComponent = pageModule?.default as
|
|
332
|
+
const PageComponent = pageModule?.default as LoadedComponent | undefined;
|
|
257
333
|
|
|
258
334
|
// Load default.tsx fallback
|
|
259
335
|
const defaultModule = slotNode.default ? await loadModule(slotNode.default) : null;
|
|
260
|
-
const DefaultComponent = defaultModule?.default as
|
|
261
|
-
| ((...args: unknown[]) => ReactElement)
|
|
262
|
-
| undefined;
|
|
336
|
+
const DefaultComponent = defaultModule?.default as LoadedComponent | undefined;
|
|
263
337
|
|
|
264
338
|
// If no page, render default.tsx or null
|
|
265
339
|
if (!PageComponent) {
|
|
266
340
|
return DefaultComponent ? createElement(DefaultComponent, {}) : null;
|
|
267
341
|
}
|
|
268
342
|
|
|
269
|
-
let element:
|
|
343
|
+
let element: ReactNode = createElement(PageComponent, {});
|
|
270
344
|
|
|
271
345
|
// Wrap in error boundaries
|
|
272
346
|
element = await wrapWithErrorBoundaries(
|
|
@@ -285,8 +359,7 @@ async function buildSlotElement(
|
|
|
285
359
|
// Load denied.tsx — pass component (not pre-built element) so
|
|
286
360
|
// SlotAccessGate can forward DenySignal.data dynamically. See TIM-488.
|
|
287
361
|
const deniedModule = slotNode.denied ? await loadModule(slotNode.denied) : null;
|
|
288
|
-
const DeniedComponent =
|
|
289
|
-
(deniedModule?.default as ((...args: unknown[]) => ReactElement) | undefined) ?? null;
|
|
362
|
+
const DeniedComponent = (deniedModule?.default as LoadedComponent | undefined) ?? null;
|
|
290
363
|
|
|
291
364
|
const defaultFallback = DefaultComponent ? createElement(DefaultComponent, {}) : null;
|
|
292
365
|
|
|
@@ -336,11 +409,11 @@ function isMdxFile(file: RouteFile): boolean {
|
|
|
336
409
|
*/
|
|
337
410
|
async function wrapWithErrorBoundaries(
|
|
338
411
|
segment: SegmentNode,
|
|
339
|
-
element:
|
|
412
|
+
element: ReactNode,
|
|
340
413
|
loadModule: ModuleLoader,
|
|
341
414
|
createElement: CreateElement,
|
|
342
415
|
errorBoundaryComponent: unknown
|
|
343
|
-
): Promise<
|
|
416
|
+
): Promise<ReactNode> {
|
|
344
417
|
// Wrapping is applied inside-out. The last wrap call produces the outermost boundary.
|
|
345
418
|
// Order: specific status → category → error.tsx (outermost)
|
|
346
419
|
|
|
@@ -351,19 +424,22 @@ async function wrapWithErrorBoundaries(
|
|
|
351
424
|
const status = parseInt(key, 10);
|
|
352
425
|
if (!isNaN(status)) {
|
|
353
426
|
const mod = await loadModule(file);
|
|
354
|
-
|
|
427
|
+
// mod.default is `unknown` — narrow to a component reference.
|
|
428
|
+
// `isValidElementType` accepts memo/forwardRef objects in addition to
|
|
429
|
+
// bare functions; non-component values fall through.
|
|
430
|
+
const Component = isValidElementType(mod.default) ? mod.default : null;
|
|
355
431
|
if (Component) {
|
|
356
|
-
const boundaryProps = isMdxFile(file)
|
|
357
|
-
?
|
|
432
|
+
const boundaryProps: ErrorBoundaryProps = isMdxFile(file)
|
|
433
|
+
? {
|
|
358
434
|
fallbackElement: createElement(Component, { status }),
|
|
359
435
|
status,
|
|
360
436
|
children: element,
|
|
361
|
-
}
|
|
362
|
-
:
|
|
437
|
+
}
|
|
438
|
+
: {
|
|
363
439
|
fallbackComponent: Component,
|
|
364
440
|
status,
|
|
365
441
|
children: element,
|
|
366
|
-
}
|
|
442
|
+
};
|
|
367
443
|
element = createElement(errorBoundaryComponent, boundaryProps);
|
|
368
444
|
}
|
|
369
445
|
}
|
|
@@ -374,20 +450,20 @@ async function wrapWithErrorBoundaries(
|
|
|
374
450
|
for (const [key, file] of Object.entries(segment.statusFiles)) {
|
|
375
451
|
if (key === '4xx' || key === '5xx') {
|
|
376
452
|
const mod = await loadModule(file);
|
|
377
|
-
const Component = mod.default;
|
|
453
|
+
const Component = isValidElementType(mod.default) ? mod.default : null;
|
|
378
454
|
if (Component) {
|
|
379
455
|
const categoryStatus = key === '4xx' ? 400 : 500;
|
|
380
|
-
const boundaryProps = isMdxFile(file)
|
|
381
|
-
?
|
|
456
|
+
const boundaryProps: ErrorBoundaryProps = isMdxFile(file)
|
|
457
|
+
? {
|
|
382
458
|
fallbackElement: createElement(Component, {}),
|
|
383
459
|
status: categoryStatus,
|
|
384
460
|
children: element,
|
|
385
|
-
}
|
|
386
|
-
:
|
|
461
|
+
}
|
|
462
|
+
: {
|
|
387
463
|
fallbackComponent: Component,
|
|
388
464
|
status: categoryStatus,
|
|
389
465
|
children: element,
|
|
390
|
-
}
|
|
466
|
+
};
|
|
391
467
|
element = createElement(errorBoundaryComponent, boundaryProps);
|
|
392
468
|
}
|
|
393
469
|
}
|
|
@@ -399,17 +475,17 @@ async function wrapWithErrorBoundaries(
|
|
|
399
475
|
// MDX error files are pre-rendered without those props (they're static content).
|
|
400
476
|
if (segment.error) {
|
|
401
477
|
const errorModule = await loadModule(segment.error);
|
|
402
|
-
const ErrorComponent = errorModule.default;
|
|
478
|
+
const ErrorComponent = isValidElementType(errorModule.default) ? errorModule.default : null;
|
|
403
479
|
if (ErrorComponent) {
|
|
404
|
-
const boundaryProps = isMdxFile(segment.error)
|
|
405
|
-
?
|
|
480
|
+
const boundaryProps: ErrorBoundaryProps = isMdxFile(segment.error)
|
|
481
|
+
? {
|
|
406
482
|
fallbackElement: createElement(ErrorComponent, {}),
|
|
407
483
|
children: element,
|
|
408
|
-
}
|
|
409
|
-
:
|
|
484
|
+
}
|
|
485
|
+
: {
|
|
410
486
|
fallbackComponent: ErrorComponent,
|
|
411
487
|
children: element,
|
|
412
|
-
}
|
|
488
|
+
};
|
|
413
489
|
element = createElement(errorBoundaryComponent, boundaryProps);
|
|
414
490
|
}
|
|
415
491
|
}
|
package/src/server/types.ts
CHANGED
|
@@ -160,4 +160,56 @@ export namespace MetadataRoute {
|
|
|
160
160
|
sitemap?: string | string[];
|
|
161
161
|
host?: string;
|
|
162
162
|
}
|
|
163
|
+
/**
|
|
164
|
+
* PWA Web App Manifest — serialized as `application/manifest+json`.
|
|
165
|
+
*
|
|
166
|
+
* Intentionally permissive: the manifest spec is large and evolving
|
|
167
|
+
* (W3C + browser-specific extensions). Users that want stricter typing
|
|
168
|
+
* can augment the return type with a library like `web-app-manifest`.
|
|
169
|
+
*/
|
|
170
|
+
export type Manifest = Record<string, unknown>;
|
|
163
171
|
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The value a dynamic metadata route handler may return.
|
|
175
|
+
*
|
|
176
|
+
* This is a structural union — the pipeline dispatches on the shape at
|
|
177
|
+
* request time (see `pipeline-phases.ts`):
|
|
178
|
+
* - `Response` → returned as-is (e.g. `ImageResponse`, `Response.redirect()`).
|
|
179
|
+
* Used by `icon.tsx`, `opengraph-image.tsx`, `twitter-image.tsx`,
|
|
180
|
+
* `apple-icon.tsx`.
|
|
181
|
+
* - `string` → written as the response body verbatim. Used by `robots.ts`
|
|
182
|
+
* and by any handler that wants to emit a pre-serialized payload.
|
|
183
|
+
* - `MetadataRoute.Sitemap` → serialized via the sitemap XML writer. Used
|
|
184
|
+
* by `sitemap.ts`.
|
|
185
|
+
* - `MetadataRoute.Manifest` → serialized via `JSON.stringify`. Used by
|
|
186
|
+
* `manifest.ts`.
|
|
187
|
+
*
|
|
188
|
+
* See design/16-metadata.md §"Metadata Routes".
|
|
189
|
+
*/
|
|
190
|
+
export type MetadataResult = Response | string | MetadataRoute.Sitemap | MetadataRoute.Manifest;
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Default export signature for a dynamic metadata route file
|
|
194
|
+
* (`sitemap.ts`, `robots.ts`, `manifest.ts`, `icon.tsx`,
|
|
195
|
+
* `opengraph-image.tsx`, `twitter-image.tsx`, `apple-icon.tsx`).
|
|
196
|
+
*
|
|
197
|
+
* Parameterize `TResult` to narrow the return type for a specific route:
|
|
198
|
+
*
|
|
199
|
+
* ```ts
|
|
200
|
+
* // app/sitemap.ts
|
|
201
|
+
* import type { MetadataHandler, MetadataRoute } from '@timber-js/app/server';
|
|
202
|
+
*
|
|
203
|
+
* const sitemap: MetadataHandler<MetadataRoute.Sitemap> = async () => [
|
|
204
|
+
* { url: 'https://example.com/', changeFrequency: 'weekly', priority: 1 },
|
|
205
|
+
* ];
|
|
206
|
+
* export default sitemap;
|
|
207
|
+
* ```
|
|
208
|
+
*
|
|
209
|
+
* The handler is currently invoked with no arguments. Per-request context
|
|
210
|
+
* (`params`, `searchParams`, `Request`) is not yet wired through to metadata
|
|
211
|
+
* route handlers; when it is, this signature will grow a context parameter.
|
|
212
|
+
*/
|
|
213
|
+
export type MetadataHandler<TResult extends MetadataResult = MetadataResult> = () =>
|
|
214
|
+
| TResult
|
|
215
|
+
| Promise<TResult>;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared HTML escaping utility.
|
|
3
|
+
*
|
|
4
|
+
* Used by both dev-only paths (error pages, 404 page) and production
|
|
5
|
+
* paths (deny-renderer, RSC helpers). Lives in server/utils/ because
|
|
6
|
+
* it's not dev-only.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Escape a string for safe embedding in HTML content or attributes.
|
|
11
|
+
*
|
|
12
|
+
* Replaces `&`, `<`, `>`, and `"` with their HTML entity equivalents.
|
|
13
|
+
*/
|
|
14
|
+
export function escapeHtml(str: string): string {
|
|
15
|
+
return str
|
|
16
|
+
.replace(/&/g, '&')
|
|
17
|
+
.replace(/</g, '<')
|
|
18
|
+
.replace(/>/g, '>')
|
|
19
|
+
.replace(/"/g, '"');
|
|
20
|
+
}
|
package/src/shims/headers.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* pipeline (both import from the same shared request-context chunk in dist/).
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
// Re-export timber's
|
|
10
|
-
export { getHeaders,
|
|
9
|
+
// Re-export timber's primary API
|
|
10
|
+
export { getHeaders, getCookieJar } from '@timber-js/app/server';
|
|
11
11
|
|
|
12
12
|
// Next.js compat aliases — libraries importing from 'next/headers' expect these names.
|
|
13
|
-
export { getHeaders as headers,
|
|
13
|
+
export { getHeaders as headers, getCookieJar as cookies } from '@timber-js/app/server';
|
|
@@ -16,11 +16,12 @@
|
|
|
16
16
|
* See design/14-ecosystem.md §"next/navigation" for the full shim audit.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
// Hooks —
|
|
19
|
+
// Hooks — useSegmentParams and useSearchParams import from direct modules
|
|
20
|
+
// since they're no longer in @timber-js/app/client public exports.
|
|
21
|
+
export { useSegmentParams } from '../client/use-segment-params.js';
|
|
22
|
+
export { useSearchParams } from '../client/use-search-params.js';
|
|
20
23
|
export {
|
|
21
|
-
useSegmentParams,
|
|
22
24
|
usePathname,
|
|
23
|
-
useSearchParams,
|
|
24
25
|
useRouter,
|
|
25
26
|
useSelectedLayoutSegment,
|
|
26
27
|
useSelectedLayoutSegments,
|
package/src/shims/navigation.ts
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shim: next/navigation → timber navigation primitives
|
|
3
3
|
*
|
|
4
|
-
* Client hooks import from
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Client hooks import from their direct modules since useSegmentParams,
|
|
5
|
+
* useSearchParams, and useQueryStates are no longer part of
|
|
6
|
+
* @timber-js/app/client public exports (they live on define* factories).
|
|
7
|
+
* The shim re-exports them for next/navigation library compat (nuqs etc).
|
|
8
|
+
*
|
|
9
|
+
* Server functions import from @timber-js/app/server for ALS singleton consistency.
|
|
7
10
|
*/
|
|
8
11
|
|
|
9
|
-
// Hooks
|
|
12
|
+
// Hooks re-exported for next/navigation compat
|
|
13
|
+
export { useSegmentParams, useSegmentParams as useParams } from '../client/use-segment-params.js';
|
|
14
|
+
export { useSearchParams } from '../client/use-search-params.js';
|
|
10
15
|
export {
|
|
11
|
-
useSegmentParams,
|
|
12
|
-
useSegmentParams as useParams,
|
|
13
16
|
usePathname,
|
|
14
|
-
useSearchParams,
|
|
15
17
|
useRouter,
|
|
16
18
|
useSelectedLayoutSegment,
|
|
17
19
|
useSelectedLayoutSegments,
|