@timber-js/app 0.2.0-alpha.198 → 0.2.0-alpha.199
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/dist/_chunks/{actions-BS-m5SLv.js → actions-d1hCqnU3.js} +35 -8
- package/dist/_chunks/actions-d1hCqnU3.js.map +1 -0
- package/dist/_chunks/als-registry-C6kcfprT.js.map +1 -1
- package/dist/_chunks/{cache-api-DqzgTEqk.js → cache-api-ByagcC-J.js} +2 -2
- package/dist/_chunks/{cache-api-DqzgTEqk.js.map → cache-api-ByagcC-J.js.map} +1 -1
- package/dist/_chunks/canonicalize-CgHoscYO.js +66 -0
- package/dist/_chunks/canonicalize-CgHoscYO.js.map +1 -0
- package/dist/_chunks/{chains-CZG7E5zg.js → chains-Bpb0W4ax.js} +3 -3
- package/dist/_chunks/{chains-CZG7E5zg.js.map → chains-Bpb0W4ax.js.map} +1 -1
- package/dist/_chunks/{cli-check-dVDi1GQz.js → cli-check-D6VolrDV.js} +3 -3
- package/dist/_chunks/{cli-check-dVDi1GQz.js.map → cli-check-D6VolrDV.js.map} +1 -1
- package/dist/_chunks/{cli-schema-sync-DTy_-Msq.js → cli-schema-sync-D6rO-VcS.js} +2 -2
- package/dist/_chunks/{cli-schema-sync-DTy_-Msq.js.map → cli-schema-sync-D6rO-VcS.js.map} +1 -1
- package/dist/_chunks/{convention-lint-Ph6luW4c.js → convention-lint-fRkwVwEH.js} +25 -4
- package/dist/_chunks/convention-lint-fRkwVwEH.js.map +1 -0
- package/dist/_chunks/error-boundary-BfPHZjm0.js +1050 -0
- package/dist/_chunks/error-boundary-BfPHZjm0.js.map +1 -0
- package/dist/_chunks/{live-graph-BXDsdzBv.js → live-graph-D_2D32Ad.js} +3 -3
- package/dist/_chunks/{live-graph-BXDsdzBv.js.map → live-graph-D_2D32Ad.js.map} +1 -1
- package/dist/_chunks/{logger-DDirEsn7.js → logger-uLBuGKDI.js} +471 -440
- package/dist/_chunks/logger-uLBuGKDI.js.map +1 -0
- package/dist/_chunks/{navigation-root-B00jjGd5.js → navigation-context-D0TU0Jog.js} +3 -101
- package/dist/_chunks/navigation-context-D0TU0Jog.js.map +1 -0
- package/dist/_chunks/navigation-root-mHSK9psY.js +126 -0
- package/dist/_chunks/{navigation-root-B00jjGd5.js.map → navigation-root-mHSK9psY.js.map} +1 -1
- package/dist/_chunks/{poison-scan-BoDLgbix.js → poison-scan-Bm9Yyqk9.js} +2 -2
- package/dist/_chunks/{poison-scan-BoDLgbix.js.map → poison-scan-Bm9Yyqk9.js.map} +1 -1
- package/dist/_chunks/{scanner-tdFPvDYi.js → scanner-AiazgH_f.js} +6 -5
- package/dist/_chunks/scanner-AiazgH_f.js.map +1 -0
- package/dist/_chunks/{segment-keys-BhqoHiLc.js → segment-keys-lqtdookO.js} +2 -65
- package/dist/_chunks/segment-keys-lqtdookO.js.map +1 -0
- package/dist/_chunks/{ssr-data-BQGhTPAK.js → ssr-data-D6T6Y3ef.js} +4 -26
- package/dist/_chunks/ssr-data-D6T6Y3ef.js.map +1 -0
- package/dist/_chunks/state-FippDgxN.js +52 -0
- package/dist/_chunks/state-FippDgxN.js.map +1 -0
- package/dist/_chunks/status-page-marker-DwQBrLBz.js +496 -0
- package/dist/_chunks/status-page-marker-DwQBrLBz.js.map +1 -0
- package/dist/_chunks/{walkers-DNX05dC0.js → walkers-B6XUtmqK.js} +2 -2
- package/dist/_chunks/{walkers-DNX05dC0.js.map → walkers-B6XUtmqK.js.map} +1 -1
- package/dist/analyze/crawl-entry.js +2 -2
- package/dist/analyze/graph-command.js +2 -2
- package/dist/cache/index.js +1 -1
- package/dist/cli.js +2 -2
- package/dist/client/browser-entry/action-dispatch.d.ts +6 -4
- package/dist/client/browser-entry/action-dispatch.d.ts.map +1 -1
- package/dist/client/browser-entry/action-queue.d.ts +44 -0
- package/dist/client/browser-entry/action-queue.d.ts.map +1 -0
- package/dist/client/browser-entry/router-init.d.ts.map +1 -1
- package/dist/client/deny-last-resort.d.ts +29 -0
- package/dist/client/deny-last-resort.d.ts.map +1 -0
- package/dist/client/error-boundary.d.ts +48 -2
- package/dist/client/error-boundary.d.ts.map +1 -1
- package/dist/client/error-boundary.js +2 -2
- package/dist/client/history.d.ts +21 -2
- package/dist/client/history.d.ts.map +1 -1
- package/dist/client/index.js +34 -14
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal.d.ts +1 -0
- package/dist/client/internal.d.ts.map +1 -1
- package/dist/client/internal.js +272 -1225
- package/dist/client/internal.js.map +1 -1
- package/dist/client/link.d.ts.map +1 -1
- package/dist/client/navigation-commit.d.ts +12 -19
- package/dist/client/navigation-commit.d.ts.map +1 -1
- package/dist/client/navigation-transition.d.ts +62 -11
- package/dist/client/navigation-transition.d.ts.map +1 -1
- package/dist/client/router-effects.d.ts +9 -8
- package/dist/client/router-effects.d.ts.map +1 -1
- package/dist/client/router-lifecycle.d.ts +60 -17
- package/dist/client/router-lifecycle.d.ts.map +1 -1
- package/dist/client/router-pipeline.d.ts +7 -4
- package/dist/client/router-pipeline.d.ts.map +1 -1
- package/dist/client/router-types.d.ts +63 -8
- package/dist/client/router-types.d.ts.map +1 -1
- package/dist/client/router.d.ts.map +1 -1
- package/dist/client/rsc-fetch.d.ts +0 -9
- package/dist/client/rsc-fetch.d.ts.map +1 -1
- package/dist/client/segment-cache.d.ts +23 -8
- package/dist/client/segment-cache.d.ts.map +1 -1
- package/dist/client/state.d.ts +16 -0
- package/dist/client/state.d.ts.map +1 -1
- package/dist/client/status-page-marker.d.ts +25 -0
- package/dist/client/status-page-marker.d.ts.map +1 -0
- package/dist/cookies/index.js +1 -1
- package/dist/dev-tools/holding-server.d.ts +4 -17
- package/dist/dev-tools/holding-server.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +74 -175
- package/dist/index.js.map +1 -1
- package/dist/plugins/dev-server.d.ts.map +1 -1
- package/dist/routing/index.js +2 -2
- package/dist/routing/interception.d.ts +2 -2
- package/dist/routing/slot-placement.d.ts +2 -2
- package/dist/server/access-gate.d.ts +73 -1
- package/dist/server/access-gate.d.ts.map +1 -1
- package/dist/server/action-handler.d.ts.map +1 -1
- package/dist/server/actions.d.ts +16 -1
- package/dist/server/actions.d.ts.map +1 -1
- package/dist/server/als-registry.d.ts +3 -9
- package/dist/server/als-registry.d.ts.map +1 -1
- package/dist/server/children-interception.d.ts +1 -1
- package/dist/server/default-status-page.d.ts +2 -2
- package/dist/server/default-status-page.d.ts.map +1 -1
- package/dist/server/deny-boundary.d.ts +15 -9
- package/dist/server/deny-boundary.d.ts.map +1 -1
- package/dist/server/deny-renderer.d.ts.map +1 -1
- package/dist/server/error-boundary-wrapper.d.ts +21 -4
- package/dist/server/error-boundary-wrapper.d.ts.map +1 -1
- package/dist/server/error-response-headers.d.ts +3 -0
- package/dist/server/error-response-headers.d.ts.map +1 -0
- package/dist/server/index.js +3 -3
- package/dist/server/index.js.map +1 -1
- package/dist/server/internal.d.ts +1 -2
- package/dist/server/internal.d.ts.map +1 -1
- package/dist/server/internal.js +2339 -2506
- package/dist/server/internal.js.map +1 -1
- package/dist/server/metadata-collector.d.ts +2 -5
- package/dist/server/metadata-collector.d.ts.map +1 -1
- package/dist/server/param-coercion.d.ts +10 -3
- package/dist/server/param-coercion.d.ts.map +1 -1
- package/dist/server/pipeline-outcome.d.ts.map +1 -1
- package/dist/server/pipeline-phases.d.ts +11 -0
- package/dist/server/pipeline-phases.d.ts.map +1 -1
- package/dist/server/port-resolution.d.ts +3 -89
- package/dist/server/port-resolution.d.ts.map +1 -1
- package/dist/server/primitives.d.ts +38 -10
- package/dist/server/primitives.d.ts.map +1 -1
- package/dist/server/response-cache-policy.d.ts +3 -0
- package/dist/server/response-cache-policy.d.ts.map +1 -0
- package/dist/server/route-element-builder.d.ts +11 -41
- package/dist/server/route-element-builder.d.ts.map +1 -1
- package/dist/server/route-element-helpers.d.ts +12 -0
- package/dist/server/route-element-helpers.d.ts.map +1 -0
- package/dist/server/route-module-loader.d.ts +37 -0
- package/dist/server/route-module-loader.d.ts.map +1 -0
- package/dist/server/rsc-cache-key-guard.d.ts.map +1 -1
- package/dist/server/rsc-entry/action-middleware-runner.d.ts.map +1 -1
- package/dist/server/rsc-entry/error-renderer.d.ts.map +1 -1
- package/dist/server/rsc-entry/render-route.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts +22 -1
- package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
- package/dist/server/rsc-entry/rsc-stream.d.ts +4 -11
- package/dist/server/rsc-entry/rsc-stream.d.ts.map +1 -1
- package/dist/server/rsc-entry/ssr-renderer.d.ts.map +1 -1
- package/dist/server/rsc-error-envelope.d.ts +11 -0
- package/dist/server/rsc-error-envelope.d.ts.map +1 -0
- package/dist/server/skippable-prefix.d.ts +18 -15
- package/dist/server/skippable-prefix.d.ts.map +1 -1
- package/dist/server/slot-resolver.d.ts.map +1 -1
- package/dist/server/slot-subtree-contain.d.ts +54 -0
- package/dist/server/slot-subtree-contain.d.ts.map +1 -0
- package/dist/server/stream-utils.d.ts.map +1 -1
- package/dist/server/utils/element-type.d.ts +10 -0
- package/dist/server/utils/element-type.d.ts.map +1 -1
- package/dist/shared/rsc-error-envelope.d.ts +0 -9
- package/dist/shared/rsc-error-envelope.d.ts.map +1 -1
- package/dist/shared/status-reason-phrase.d.ts +26 -0
- package/dist/shared/status-reason-phrase.d.ts.map +1 -0
- package/docs/api/30-api-server.mdx +4 -2
- package/docs/api/31-api-client.mdx +5 -1
- package/docs/api/36-cli.mdx +5 -3
- package/docs/learn/12-error-handling.mdx +5 -1
- package/package.json +10 -10
- package/src/client/browser-entry/action-dispatch.ts +166 -99
- package/src/client/browser-entry/action-queue.ts +90 -0
- package/src/client/browser-entry/router-init.ts +60 -35
- package/src/client/deny-last-resort.tsx +54 -0
- package/src/client/error-boundary.tsx +144 -42
- package/src/client/history.ts +52 -3
- package/src/client/internal.ts +1 -0
- package/src/client/link.tsx +70 -35
- package/src/client/navigation-commit.ts +79 -27
- package/src/client/navigation-transition.ts +176 -127
- package/src/client/router-effects.ts +14 -17
- package/src/client/router-lifecycle.ts +181 -115
- package/src/client/router-pipeline.ts +94 -71
- package/src/client/router-types.ts +61 -7
- package/src/client/router.ts +147 -74
- package/src/client/rsc-fetch.ts +0 -13
- package/src/client/segment-cache.ts +43 -10
- package/src/client/state.ts +26 -0
- package/src/client/status-page-marker.tsx +32 -0
- package/src/dev-tools/holding-server.ts +4 -17
- package/src/index.ts +18 -34
- package/src/plugins/dev-server.ts +2 -1
- package/src/routing/interception.ts +2 -2
- package/src/routing/slot-placement.ts +2 -2
- package/src/server/access-gate.tsx +89 -21
- package/src/server/action-client.ts +2 -2
- package/src/server/action-handler.ts +23 -10
- package/src/server/actions.ts +81 -34
- package/src/server/als-registry.ts +3 -9
- package/src/server/children-interception.ts +1 -1
- package/src/server/default-status-page.ts +7 -47
- package/src/server/deny-boundary.ts +45 -28
- package/src/server/deny-renderer.ts +6 -2
- package/src/server/error-boundary-wrapper.ts +23 -4
- package/src/server/error-response-headers.ts +18 -0
- package/src/server/internal.ts +2 -10
- package/src/server/metadata-collector.ts +3 -18
- package/src/server/param-coercion.ts +13 -4
- package/src/server/pipeline-outcome.ts +35 -13
- package/src/server/pipeline-phases.ts +22 -16
- package/src/server/port-resolution.ts +3 -165
- package/src/server/prebuilt-builder.ts +4 -4
- package/src/server/primitives.ts +75 -11
- package/src/server/response-cache-policy.ts +45 -0
- package/src/server/route-element-builder.ts +149 -412
- package/src/server/route-element-helpers.ts +37 -0
- package/src/server/route-handler.ts +2 -2
- package/src/server/route-module-loader.ts +161 -0
- package/src/server/rsc-cache-key-guard.ts +2 -42
- package/src/server/rsc-entry/action-middleware-runner.ts +4 -4
- package/src/server/rsc-entry/api-handler.ts +5 -5
- package/src/server/rsc-entry/error-renderer.ts +3 -4
- package/src/server/rsc-entry/helpers.ts +1 -1
- package/src/server/rsc-entry/index.ts +3 -3
- package/src/server/rsc-entry/render-route.ts +4 -8
- package/src/server/rsc-entry/rsc-payload.ts +59 -42
- package/src/server/rsc-entry/rsc-stream.ts +48 -27
- package/src/server/rsc-entry/ssr-renderer.ts +6 -10
- package/src/server/rsc-error-envelope.ts +18 -0
- package/src/server/skippable-prefix.ts +105 -7
- package/src/server/slot-resolver.ts +43 -12
- package/src/server/slot-subtree-contain.ts +255 -0
- package/src/server/stream-utils.ts +12 -8
- package/src/server/utils/element-type.ts +18 -2
- package/src/shared/rsc-error-envelope.ts +0 -15
- package/src/shared/status-reason-phrase.ts +61 -0
- package/dist/_chunks/actions-BS-m5SLv.js.map +0 -1
- package/dist/_chunks/convention-lint-Ph6luW4c.js.map +0 -1
- package/dist/_chunks/error-boundary-BvRCCmbN.js +0 -353
- package/dist/_chunks/error-boundary-BvRCCmbN.js.map +0 -1
- package/dist/_chunks/logger-DDirEsn7.js.map +0 -1
- package/dist/_chunks/mdx-file-CXyHGUpS.js +0 -25
- package/dist/_chunks/mdx-file-CXyHGUpS.js.map +0 -1
- package/dist/_chunks/router-ref-8gr8qsxN.js +0 -28
- package/dist/_chunks/router-ref-8gr8qsxN.js.map +0 -1
- package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js +0 -40
- package/dist/_chunks/rsc-error-envelope-tT5PJs4q.js.map +0 -1
- package/dist/_chunks/scanner-tdFPvDYi.js.map +0 -1
- package/dist/_chunks/segment-keys-BhqoHiLc.js.map +0 -1
- package/dist/_chunks/ssr-data-BQGhTPAK.js.map +0 -1
- package/dist/server/tree-builder.d.ts +0 -150
- package/dist/server/tree-builder.d.ts.map +0 -1
- package/src/server/tree-builder.ts +0 -313
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { n as classifyUrlSegment } from "./segment-classify-C539Pa2O.js";
|
|
2
2
|
import { t as readFileCached } from "./file-cache-Dw6BJPG7.js";
|
|
3
|
-
import { n as findSchemaFile, r as parseExistingSchemaKeys } from "./cli-schema-sync-
|
|
3
|
+
import { n as findSchemaFile, r as parseExistingSchemaKeys } from "./cli-schema-sync-D6rO-VcS.js";
|
|
4
4
|
//#region src/routing/schema-validation.ts
|
|
5
5
|
/**
|
|
6
6
|
* Schema key/value validation against the filesystem route tree.
|
|
@@ -141,4 +141,4 @@ function walk(node, chain, result, includeSlots) {
|
|
|
141
141
|
//#endregion
|
|
142
142
|
export { collectDynamicSegmentsFromTree as n, validateSchemaAgainstRoutes as r, collectLeafRoutes as t };
|
|
143
143
|
|
|
144
|
-
//# sourceMappingURL=walkers-
|
|
144
|
+
//# sourceMappingURL=walkers-B6XUtmqK.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"walkers-DNX05dC0.js","names":[],"sources":["../../src/routing/schema-validation.ts","../../src/routing/walkers.ts"],"sourcesContent":["/**\n * Schema key/value validation against the filesystem route tree.\n *\n * Runs at codegen time (build + dev) to catch mismatches between\n * app/schema.ts keys and the actual dynamic segments on disk.\n *\n * Design doc: design/41-global-params.md\n */\n\nimport { readFileCached } from './file-cache.ts';\nimport type { RouteTree, SegmentNode } from './types.ts';\nimport { findSchemaFile, parseExistingSchemaKeys } from '../cli-schema-sync.ts';\nimport { classifyUrlSegment } from './segment-classify.ts';\n\nexport interface SchemaWarning {\n type: 'stale-key' | 'missing-key' | 'bracket-mismatch';\n key: string;\n message: string;\n filesystemKey?: string;\n}\n\n/**\n * Collect all unique bracket-keyed dynamic segment names from a route tree.\n * Returns entries like '[artistSlug]', '[...slug]', '[[...topic]]'.\n */\nexport function collectDynamicSegmentsFromTree(tree: RouteTree): Set<string> {\n const segments = new Set<string>();\n walkTree(tree.root, segments);\n return segments;\n}\n\nfunction walkTree(node: SegmentNode, segments: Set<string>): void {\n if (node.paramName && node.segmentName) {\n const classified = classifyUrlSegment(node.segmentName);\n if (classified.kind !== 'static') {\n // Use bare bracket form for schema keys — affixed segments like\n // `img-[id].png` register as `[id]`, matching runtime codec lookup.\n if (classified.kind === 'dynamic' && (classified.prefix || classified.suffix)) {\n segments.add(`[${classified.name}]`);\n } else {\n segments.add(node.segmentName);\n }\n }\n }\n for (const child of node.children) {\n walkTree(child, segments);\n }\n for (const slot of Object.values(node.slots)) {\n walkTree(slot, segments);\n }\n}\n\n/**\n * Extract the param name from a bracket key.\n * '[id]' → 'id', '[...slug]' → 'slug', '[[...topic]]' → 'topic'\n */\nfunction extractParamName(bracketKey: string): string | null {\n const seg = classifyUrlSegment(bracketKey);\n if (seg.kind === 'static') return null;\n return seg.name;\n}\n\n/**\n * Validate schema keys against the filesystem route tree.\n *\n * Returns warnings for:\n * - Stale keys: schema keys that don't match any filesystem segment\n * - Bracket form mismatches: same param name, different bracket form\n * - Missing keys: filesystem segments not registered in schema (informational)\n */\nexport function validateSchemaAgainstRoutes(tree: RouteTree, appDir: string): SchemaWarning[] {\n const schemaPath = findSchemaFile(appDir);\n if (!schemaPath) return [];\n\n const content = readFileCached(schemaPath);\n const schemaKeys = parseExistingSchemaKeys(content);\n\n if (schemaKeys.length === 0) return [];\n\n const filesystemSegments = collectDynamicSegmentsFromTree(tree);\n\n return validateKeys(schemaKeys, filesystemSegments);\n}\n\n/**\n * Core validation: compare schema keys against filesystem segments.\n * Exported separately for testing without filesystem dependencies.\n */\nexport function validateKeys(\n schemaKeys: string[],\n filesystemSegments: Set<string>\n): SchemaWarning[] {\n const warnings: SchemaWarning[] = [];\n\n // Build a map of param name → bracket form for filesystem segments\n const fsParamToKey = new Map<string, string>();\n for (const fsKey of filesystemSegments) {\n const name = extractParamName(fsKey);\n if (name) {\n fsParamToKey.set(name, fsKey);\n }\n }\n\n // Build a set of schema param names for the missing-key check\n const schemaParamNames = new Set<string>();\n\n for (const schemaKey of schemaKeys) {\n const schemaParamName = extractParamName(schemaKey);\n if (!schemaParamName) continue;\n\n schemaParamNames.add(schemaParamName);\n\n if (filesystemSegments.has(schemaKey)) {\n // Exact match — no issue\n continue;\n }\n\n // Check if the param name exists but with a different bracket form\n const fsKey = fsParamToKey.get(schemaParamName);\n if (fsKey) {\n warnings.push({\n type: 'bracket-mismatch',\n key: schemaKey,\n filesystemKey: fsKey,\n message:\n `Schema key '${schemaKey}' does not match filesystem bracket form '${fsKey}'. ` +\n `Update the schema key to '${fsKey}'.`,\n });\n } else {\n warnings.push({\n type: 'stale-key',\n key: schemaKey,\n message:\n `Schema key '${schemaKey}' does not match any dynamic segment in the filesystem. ` +\n `Remove it from app/schema.ts or create the corresponding route segment.`,\n });\n }\n }\n\n // Check for filesystem segments not in schema (informational)\n for (const fsKey of filesystemSegments) {\n const name = extractParamName(fsKey);\n if (name && !schemaParamNames.has(name)) {\n warnings.push({\n type: 'missing-key',\n key: fsKey,\n message:\n `Dynamic segment '${fsKey}' has no codec in app/schema.ts (defaults to string). ` +\n `Run: timber schema sync`,\n });\n }\n }\n\n return warnings;\n}\n","/**\n * Shared route-tree walkers (TIM-848).\n *\n * Tiny helpers that walk a `SegmentNode<TFile>` tree generically. Both the\n * build-time tree (`SegmentNode<RouteFile>`) and the runtime manifest\n * tree (`SegmentNode<ManifestFile>`) flow through these helpers because\n * the walker only reads the structural fields shared by both shapes\n * (`children`, `slots`, `page`, `route`, `urlPath`).\n *\n * Before this module, three near-identical `collectRoutes` functions\n * lived in `plugins/dev-404-page.ts`, `plugins/build-report.ts`, and\n * `routing/codegen.ts`. The codegen one is special-purpose (it\n * accumulates `ParamEntry[]` and resolves codec chains) and stays\n * local; the other two now share `collectLeafRoutes` from this file.\n */\n\nimport type { SegmentNode } from './types.ts';\n\n/** A leaf route discovered while walking the segment tree. */\nexport interface LeafRoute<TFile> {\n /** URL path of the leaf (root is \"/\"). */\n urlPath: string;\n /** Segment chain from root to this leaf, inclusive. */\n segments: SegmentNode<TFile>[];\n /** The page file at this leaf, if any. */\n page?: TFile;\n /** The route handler file at this leaf, if any. */\n route?: TFile;\n}\n\n/** Options for `collectLeafRoutes`. */\nexport interface CollectLeafRoutesOptions {\n /**\n * If true, recurse into parallel slots and emit slot leaves alongside\n * the main route tree. Defaults to `false` because slots render\n * alongside their parent at the same URL and are not separately\n * URL-addressable. The build report excludes slots; route-listing\n * UIs that want to show \"all leaves with a page handler\" can opt in.\n */\n includeSlots?: boolean;\n}\n\n/**\n * Walk a segment tree and collect every leaf with a `page` or `route`\n * handler. Generic over `TFile` so it works on both the build-time\n * scanner output and the runtime manifest tree.\n *\n * - Pages and route handlers at the same URL produce two distinct\n * entries (the build report deduplicates by URL afterward).\n * - Parallel slots are skipped unless `includeSlots: true` (slots\n * share their parent's URL and are not addressable on their own).\n * - Intercepting subtrees are skipped unconditionally. They render only\n * on soft navigation and add no URL depth, so their computed `urlPath`s\n * are not addresses — listing them would advertise routes (`/[year]`)\n * that no request can reach. This matches every other consumer of the\n * tree that produces URLs: the sitemap generator, the static generator,\n * prerendering and the prebuilt-payload builder all skip them too.\n * - Result is sorted by `urlPath` for deterministic output.\n */\nexport function collectLeafRoutes<TFile>(\n root: SegmentNode<TFile>,\n options: CollectLeafRoutesOptions = {}\n): LeafRoute<TFile>[] {\n const { includeSlots = false } = options;\n const result: LeafRoute<TFile>[] = [];\n walk(root, [], result, includeSlots);\n result.sort((a, b) => a.urlPath.localeCompare(b.urlPath));\n return result;\n}\n\nfunction walk<TFile>(\n node: SegmentNode<TFile>,\n chain: SegmentNode<TFile>[],\n result: LeafRoute<TFile>[],\n includeSlots: boolean\n): void {\n if (node.segmentType === 'intercepting') return;\n\n const currentChain = [...chain, node];\n const path = node.urlPath || '/';\n\n if (node.page) {\n result.push({ urlPath: path, segments: currentChain, page: node.page });\n }\n if (node.route) {\n result.push({ urlPath: path, segments: currentChain, route: node.route });\n }\n\n for (const child of node.children) {\n walk(child, currentChain, result, includeSlots);\n }\n\n if (includeSlots) {\n for (const slotNode of Object.values(node.slots)) {\n walk(slotNode, currentChain, result, includeSlots);\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"walkers-B6XUtmqK.js","names":[],"sources":["../../src/routing/schema-validation.ts","../../src/routing/walkers.ts"],"sourcesContent":["/**\n * Schema key/value validation against the filesystem route tree.\n *\n * Runs at codegen time (build + dev) to catch mismatches between\n * app/schema.ts keys and the actual dynamic segments on disk.\n *\n * Design doc: design/41-global-params.md\n */\n\nimport { readFileCached } from './file-cache.ts';\nimport type { RouteTree, SegmentNode } from './types.ts';\nimport { findSchemaFile, parseExistingSchemaKeys } from '../cli-schema-sync.ts';\nimport { classifyUrlSegment } from './segment-classify.ts';\n\nexport interface SchemaWarning {\n type: 'stale-key' | 'missing-key' | 'bracket-mismatch';\n key: string;\n message: string;\n filesystemKey?: string;\n}\n\n/**\n * Collect all unique bracket-keyed dynamic segment names from a route tree.\n * Returns entries like '[artistSlug]', '[...slug]', '[[...topic]]'.\n */\nexport function collectDynamicSegmentsFromTree(tree: RouteTree): Set<string> {\n const segments = new Set<string>();\n walkTree(tree.root, segments);\n return segments;\n}\n\nfunction walkTree(node: SegmentNode, segments: Set<string>): void {\n if (node.paramName && node.segmentName) {\n const classified = classifyUrlSegment(node.segmentName);\n if (classified.kind !== 'static') {\n // Use bare bracket form for schema keys — affixed segments like\n // `img-[id].png` register as `[id]`, matching runtime codec lookup.\n if (classified.kind === 'dynamic' && (classified.prefix || classified.suffix)) {\n segments.add(`[${classified.name}]`);\n } else {\n segments.add(node.segmentName);\n }\n }\n }\n for (const child of node.children) {\n walkTree(child, segments);\n }\n for (const slot of Object.values(node.slots)) {\n walkTree(slot, segments);\n }\n}\n\n/**\n * Extract the param name from a bracket key.\n * '[id]' → 'id', '[...slug]' → 'slug', '[[...topic]]' → 'topic'\n */\nfunction extractParamName(bracketKey: string): string | null {\n const seg = classifyUrlSegment(bracketKey);\n if (seg.kind === 'static') return null;\n return seg.name;\n}\n\n/**\n * Validate schema keys against the filesystem route tree.\n *\n * Returns warnings for:\n * - Stale keys: schema keys that don't match any filesystem segment\n * - Bracket form mismatches: same param name, different bracket form\n * - Missing keys: filesystem segments not registered in schema (informational)\n */\nexport function validateSchemaAgainstRoutes(tree: RouteTree, appDir: string): SchemaWarning[] {\n const schemaPath = findSchemaFile(appDir);\n if (!schemaPath) return [];\n\n const content = readFileCached(schemaPath);\n const schemaKeys = parseExistingSchemaKeys(content);\n\n if (schemaKeys.length === 0) return [];\n\n const filesystemSegments = collectDynamicSegmentsFromTree(tree);\n\n return validateKeys(schemaKeys, filesystemSegments);\n}\n\n/**\n * Core validation: compare schema keys against filesystem segments.\n * Exported separately for testing without filesystem dependencies.\n */\nexport function validateKeys(\n schemaKeys: string[],\n filesystemSegments: Set<string>\n): SchemaWarning[] {\n const warnings: SchemaWarning[] = [];\n\n // Build a map of param name → bracket form for filesystem segments\n const fsParamToKey = new Map<string, string>();\n for (const fsKey of filesystemSegments) {\n const name = extractParamName(fsKey);\n if (name) {\n fsParamToKey.set(name, fsKey);\n }\n }\n\n // Build a set of schema param names for the missing-key check\n const schemaParamNames = new Set<string>();\n\n for (const schemaKey of schemaKeys) {\n const schemaParamName = extractParamName(schemaKey);\n if (!schemaParamName) continue;\n\n schemaParamNames.add(schemaParamName);\n\n if (filesystemSegments.has(schemaKey)) {\n // Exact match — no issue\n continue;\n }\n\n // Check if the param name exists but with a different bracket form\n const fsKey = fsParamToKey.get(schemaParamName);\n if (fsKey) {\n warnings.push({\n type: 'bracket-mismatch',\n key: schemaKey,\n filesystemKey: fsKey,\n message:\n `Schema key '${schemaKey}' does not match filesystem bracket form '${fsKey}'. ` +\n `Update the schema key to '${fsKey}'.`,\n });\n } else {\n warnings.push({\n type: 'stale-key',\n key: schemaKey,\n message:\n `Schema key '${schemaKey}' does not match any dynamic segment in the filesystem. ` +\n `Remove it from app/schema.ts or create the corresponding route segment.`,\n });\n }\n }\n\n // Check for filesystem segments not in schema (informational)\n for (const fsKey of filesystemSegments) {\n const name = extractParamName(fsKey);\n if (name && !schemaParamNames.has(name)) {\n warnings.push({\n type: 'missing-key',\n key: fsKey,\n message:\n `Dynamic segment '${fsKey}' has no codec in app/schema.ts (defaults to string). ` +\n `Run: timber schema sync`,\n });\n }\n }\n\n return warnings;\n}\n","/**\n * Shared route-tree walkers (TIM-848).\n *\n * Tiny helpers that walk a `SegmentNode<TFile>` tree generically. Both the\n * build-time tree (`SegmentNode<RouteFile>`) and the runtime manifest\n * tree (`SegmentNode<ManifestFile>`) flow through these helpers because\n * the walker only reads the structural fields shared by both shapes\n * (`children`, `slots`, `page`, `route`, `urlPath`).\n *\n * Before this module, three near-identical `collectRoutes` functions\n * lived in `plugins/dev-404-page.ts`, `plugins/build-report.ts`, and\n * `routing/codegen.ts`. The codegen one is special-purpose (it\n * accumulates `ParamEntry[]` and resolves codec chains) and stays\n * local; the other two now share `collectLeafRoutes` from this file.\n */\n\nimport type { SegmentNode } from './types.ts';\n\n/** A leaf route discovered while walking the segment tree. */\nexport interface LeafRoute<TFile> {\n /** URL path of the leaf (root is \"/\"). */\n urlPath: string;\n /** Segment chain from root to this leaf, inclusive. */\n segments: SegmentNode<TFile>[];\n /** The page file at this leaf, if any. */\n page?: TFile;\n /** The route handler file at this leaf, if any. */\n route?: TFile;\n}\n\n/** Options for `collectLeafRoutes`. */\nexport interface CollectLeafRoutesOptions {\n /**\n * If true, recurse into parallel slots and emit slot leaves alongside\n * the main route tree. Defaults to `false` because slots render\n * alongside their parent at the same URL and are not separately\n * URL-addressable. The build report excludes slots; route-listing\n * UIs that want to show \"all leaves with a page handler\" can opt in.\n */\n includeSlots?: boolean;\n}\n\n/**\n * Walk a segment tree and collect every leaf with a `page` or `route`\n * handler. Generic over `TFile` so it works on both the build-time\n * scanner output and the runtime manifest tree.\n *\n * - Pages and route handlers at the same URL produce two distinct\n * entries (the build report deduplicates by URL afterward).\n * - Parallel slots are skipped unless `includeSlots: true` (slots\n * share their parent's URL and are not addressable on their own).\n * - Intercepting subtrees are skipped unconditionally. They render only\n * on soft navigation and add no URL depth, so their computed `urlPath`s\n * are not addresses — listing them would advertise routes (`/[year]`)\n * that no request can reach. This matches every other consumer of the\n * tree that produces URLs: the sitemap generator, the static generator,\n * prerendering and the prebuilt-payload builder all skip them too.\n * - Result is sorted by `urlPath` for deterministic output.\n */\nexport function collectLeafRoutes<TFile>(\n root: SegmentNode<TFile>,\n options: CollectLeafRoutesOptions = {}\n): LeafRoute<TFile>[] {\n const { includeSlots = false } = options;\n const result: LeafRoute<TFile>[] = [];\n walk(root, [], result, includeSlots);\n result.sort((a, b) => a.urlPath.localeCompare(b.urlPath));\n return result;\n}\n\nfunction walk<TFile>(\n node: SegmentNode<TFile>,\n chain: SegmentNode<TFile>[],\n result: LeafRoute<TFile>[],\n includeSlots: boolean\n): void {\n if (node.segmentType === 'intercepting') return;\n\n const currentChain = [...chain, node];\n const path = node.urlPath || '/';\n\n if (node.page) {\n result.push({ urlPath: path, segments: currentChain, page: node.page });\n }\n if (node.route) {\n result.push({ urlPath: path, segments: currentChain, route: node.route });\n }\n\n for (const child of node.children) {\n walk(child, currentChain, result, includeSlots);\n }\n\n if (includeSlots) {\n for (const slotNode of Object.values(node.slots)) {\n walk(slotNode, currentChain, result, includeSlots);\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { i as scanProjectSourceFiles, n as isScannableSourceFile, t as buildSourceExtensions } from "../_chunks/scan-BoP17W3w.js";
|
|
2
2
|
import { n as isTimberPluginApi, o as resolveAppDir } from "../_chunks/plugin-context-8idfR3UZ.js";
|
|
3
|
-
import { a as detectDirective, i as classifyModules, n as stitchImportChains } from "../_chunks/chains-
|
|
4
|
-
import { n as importSignals, r as poisoningsFor, t as collectImportSpecifiers } from "../_chunks/poison-scan-
|
|
3
|
+
import { a as detectDirective, i as classifyModules, n as stitchImportChains } from "../_chunks/chains-Bpb0W4ax.js";
|
|
4
|
+
import { n as importSignals, r as poisoningsFor, t as collectImportSpecifiers } from "../_chunks/poison-scan-Bm9Yyqk9.js";
|
|
5
5
|
import { normalizePath } from "vite";
|
|
6
6
|
import { join, resolve } from "node:path";
|
|
7
7
|
import { readFile, realpath, writeFile } from "node:fs/promises";
|
|
@@ -378,7 +378,7 @@ async function printSingleFile(input, target, module, answer, json) {
|
|
|
378
378
|
console.log(` environments: ${envs.length > 0 ? envs.join(", ") : "none"}`);
|
|
379
379
|
if (module.subtreeUnknown) console.log(" note: reachable, but its subtree is unknown (never transformed)");
|
|
380
380
|
if (module.chains.length > 0) {
|
|
381
|
-
const { formatChains } = await import("../_chunks/chains-
|
|
381
|
+
const { formatChains } = await import("../_chunks/chains-Bpb0W4ax.js").then((n) => n.t);
|
|
382
382
|
console.log(" import chains:");
|
|
383
383
|
console.log(formatChains(module.chains, root));
|
|
384
384
|
}
|
|
@@ -397,7 +397,7 @@ async function printDevServerText(data, configDir) {
|
|
|
397
397
|
console.log(` environments: ${envList.length > 0 ? envList.join(", ") : "none"}`);
|
|
398
398
|
if (data.subtreeUnknown) console.log(" note: reachable, but its subtree is unknown (never transformed)");
|
|
399
399
|
if (data.chains.length > 0) {
|
|
400
|
-
const { formatChains } = await import("../_chunks/chains-
|
|
400
|
+
const { formatChains } = await import("../_chunks/chains-Bpb0W4ax.js").then((n) => n.t);
|
|
401
401
|
console.log(" import chains:");
|
|
402
402
|
console.log(formatChains(data.chains, configDir));
|
|
403
403
|
}
|
package/dist/cache/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { _ as setDefaultSingleflightTimeout, f as MemoryCacheHandler, g as setCacheHandler, h as getCacheHandler, m as RedisCacheHandler, p as TagAwareCacheHandler, t as cache } from "../_chunks/cache-api-
|
|
1
|
+
import { _ as setDefaultSingleflightTimeout, f as MemoryCacheHandler, g as setCacheHandler, h as getCacheHandler, m as RedisCacheHandler, p as TagAwareCacheHandler, t as cache } from "../_chunks/cache-api-ByagcC-J.js";
|
|
2
2
|
import { t as estimateByteSize } from "../_chunks/sizeof-lXffQ1Vr.js";
|
|
3
3
|
export { MemoryCacheHandler, RedisCacheHandler, TagAwareCacheHandler, cache, estimateByteSize, getCacheHandler, setCacheHandler, setDefaultSingleflightTimeout };
|
package/dist/cli.js
CHANGED
|
@@ -136,7 +136,7 @@ async function runPreview(options, _deps) {
|
|
|
136
136
|
* `--project` names the tsconfig handed to tsgo.
|
|
137
137
|
*/
|
|
138
138
|
async function runCheck(options, _deps) {
|
|
139
|
-
const { checkRoutes, runTypeCheck } = await import("./_chunks/cli-check-
|
|
139
|
+
const { checkRoutes, runTypeCheck } = await import("./_chunks/cli-check-D6VolrDV.js");
|
|
140
140
|
const { root } = await checkRoutes({ viteConfigFile: options.config }, _deps);
|
|
141
141
|
await runTypeCheck({
|
|
142
142
|
root,
|
|
@@ -152,7 +152,7 @@ async function runCheck(options, _deps) {
|
|
|
152
152
|
async function runSchema(args) {
|
|
153
153
|
const subcommand = args[0];
|
|
154
154
|
if (subcommand !== "sync") throw new Error(`Unknown schema subcommand: ${subcommand ?? "(none)"}. Usage: timber schema sync`);
|
|
155
|
-
const { runSchemaSync, defaultCodecForSegment } = await import("./_chunks/cli-schema-sync-
|
|
155
|
+
const { runSchemaSync, defaultCodecForSegment } = await import("./_chunks/cli-schema-sync-D6rO-VcS.js").then((n) => n.t);
|
|
156
156
|
const result = runSchemaSync(process.cwd());
|
|
157
157
|
if (result.filesystemSegments.length === 0) {
|
|
158
158
|
console.log("[timber] No dynamic segments found in app/.");
|
|
@@ -6,10 +6,12 @@
|
|
|
6
6
|
* The RSC plugin delegates to `globalThis.__viteRscCallServer` which is
|
|
7
7
|
* set by `setServerCallback`.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
9
|
+
* Actions are dispatched sequentially through a client-wide queue
|
|
10
|
+
* (TIM-1475): each action's POST starts only after the previous one has
|
|
11
|
+
* settled and its result has been applied. Navigations do not enter the
|
|
12
|
+
* queue — they preempt via the NavigationEpoch, marking any running
|
|
13
|
+
* action as discarded (TIM-1474). Queries (TIM-1415) will bypass the
|
|
14
|
+
* queue entirely.
|
|
13
15
|
*
|
|
14
16
|
* See design/08-forms-and-actions.md §"Client-Side Form Mechanics"
|
|
15
17
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"action-dispatch.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/action-dispatch.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"action-dispatch.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/action-dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAwBH,wBAAgB,kBAAkB,IAAI,IAAI,CA4JzC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sequential Action Queue — serialises server action dispatch.
|
|
3
|
+
*
|
|
4
|
+
* One action at a time. The next action's POST starts only after the
|
|
5
|
+
* previous one has settled AND its result has been applied (revalidation
|
|
6
|
+
* rendered or refresh awaited). Navigations do not enter the queue —
|
|
7
|
+
* they preempt via the NavigationEpoch. Queries (TIM-1415) bypass it
|
|
8
|
+
* entirely.
|
|
9
|
+
*
|
|
10
|
+
* See design/08-forms-and-actions.md §"Client-Side Form Mechanics"
|
|
11
|
+
* and design/19-client-navigation.md §State Update Invariants (TIM-1475).
|
|
12
|
+
*/
|
|
13
|
+
export interface ActionQueueDeps {
|
|
14
|
+
refreshWhenIdle: () => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Passed into `work` so it can return a result to React immediately
|
|
18
|
+
* while keeping the queue blocked on a side effect (e.g. a refresh
|
|
19
|
+
* that must commit before the next action captures its epoch).
|
|
20
|
+
*/
|
|
21
|
+
export interface ActionQueueRun {
|
|
22
|
+
hold: (p: Promise<unknown>) => void;
|
|
23
|
+
}
|
|
24
|
+
export interface ActionQueue {
|
|
25
|
+
/**
|
|
26
|
+
* Enqueue `work` to run after all preceding actions have settled.
|
|
27
|
+
* Returns `work`'s own promise so the caller (React) gets the action
|
|
28
|
+
* result or rejection. If the preceding action rejected, `work` still
|
|
29
|
+
* runs — errors do not break the chain.
|
|
30
|
+
*
|
|
31
|
+
* `work` receives a `hold` handle: calling `hold(promise)` keeps the
|
|
32
|
+
* queue blocked until that promise settles, independent of `work`'s
|
|
33
|
+
* return value. Use it for "return the result NOW, await the refresh
|
|
34
|
+
* commit for sequencing."
|
|
35
|
+
*/
|
|
36
|
+
run: <T>(work: (q: ActionQueueRun) => Promise<T>) => Promise<T>;
|
|
37
|
+
/**
|
|
38
|
+
* Mark that a discarded action would have revalidated. When the queue
|
|
39
|
+
* drains, one refresh fires via `deps.refreshWhenIdle`.
|
|
40
|
+
*/
|
|
41
|
+
markNeedsRefresh: () => void;
|
|
42
|
+
}
|
|
43
|
+
export declare function createActionQueue(deps: ActionQueueDeps): ActionQueue;
|
|
44
|
+
//# sourceMappingURL=action-queue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"action-queue.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/action-queue.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,eAAe;IAC9B,eAAe,EAAE,MAAM,IAAI,CAAC;CAC7B;AAID;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,CAAC,CAAC,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;OAUG;IACH,GAAG,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,cAAc,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;IAChE;;;OAGG;IACH,gBAAgB,EAAE,MAAM,IAAI,CAAC;CAC9B;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAyCpE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"router-init.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/router-init.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEtF,OAAO,KAAK,EAAc,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAQxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAK9B,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,MAAM,EAAE,gBAAgB,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,gBAAgB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACjD;;;;;;OAMG;IACH,UAAU,EAAE,CACV,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,YAAY,KACjB,KAAK,CAAC,YAAY,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,
|
|
1
|
+
{"version":3,"file":"router-init.d.ts","sourceRoot":"","sources":["../../../src/client/browser-entry/router-init.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EAAuB,KAAK,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAEtF,OAAO,KAAK,EAAc,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAQxD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,EAGL,KAAK,uBAAuB,EAC7B,MAAM,sBAAsB,CAAC;AAK9B,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,MAAM,EAAE,gBAAgB,CAAC;IACzB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,2EAA2E;IAC3E,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAC;IACvB,gBAAgB,EAAE,uBAAuB,GAAG,IAAI,CAAC;IACjD;;;;;;OAMG;IACH,UAAU,EAAE,CACV,IAAI,EAAE,OAAO,EACb,QAAQ,EAAE,eAAe,EACzB,MAAM,EAAE,YAAY,KACjB,KAAK,CAAC,YAAY,CAAC;CACzB;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,GAAG,gBAAgB,CAyV/E"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The deny chain's terminal, in client form.
|
|
3
|
+
*
|
|
4
|
+
* design/10-error-handling.md §"Fallback Chain" ends every chain in the
|
|
5
|
+
* framework default page. On a document load that page is the HTML string in
|
|
6
|
+
* `server/default-status-page.ts`. This is the same terminal for a deny that
|
|
7
|
+
* streamed to the client as an error row and reached the root boundary with
|
|
8
|
+
* nothing in between able to render it — which happens in exactly one shape:
|
|
9
|
+
* a server component rendered by a layout's own chrome denied, and no status
|
|
10
|
+
* file exists at or above that layout. Files *below* the denying layout are
|
|
11
|
+
* not candidates: rendering any of them means executing that layout again,
|
|
12
|
+
* which denies again. So there is no user file to show, and this renders the
|
|
13
|
+
* default one. See TIM-1450, design/04-authorization.md §"Where a Deny Page
|
|
14
|
+
* Renders".
|
|
15
|
+
*
|
|
16
|
+
* A plain reload button, not an automatic navigation: the status and phrase
|
|
17
|
+
* are already on screen and tell the reader what happened; the button hands
|
|
18
|
+
* them the one recovery the framework can offer (a document request, which
|
|
19
|
+
* takes the re-render fallback) without doing it behind their back. An
|
|
20
|
+
* effect-driven reload could not be told apart from a loop.
|
|
21
|
+
*
|
|
22
|
+
* Same content rules as the HTML page: the status code is the only thing
|
|
23
|
+
* interpolated, and it is clamped by `statusReasonPhrase`. No URL, no error
|
|
24
|
+
* message, no stack. See design/13-security.md principle 4.
|
|
25
|
+
*/
|
|
26
|
+
export declare function DenyLastResort({ status }: {
|
|
27
|
+
status: number;
|
|
28
|
+
}): import("react").JSX.Element;
|
|
29
|
+
//# sourceMappingURL=deny-last-resort.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deny-last-resort.d.ts","sourceRoot":"","sources":["../../src/client/deny-last-resort.tsx"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,+BAwB5D"}
|
|
@@ -16,6 +16,15 @@
|
|
|
16
16
|
* See design/10-error-handling.md §"Status-Code Files"
|
|
17
17
|
*/
|
|
18
18
|
import { Component, type ReactNode } from 'react';
|
|
19
|
+
/**
|
|
20
|
+
* Tracks the chain of segment keys from root to the current boundary.
|
|
21
|
+
* Each keyed `TimberErrorBoundary` appends its `segmentKey`; unkeyed
|
|
22
|
+
* slot boundaries pass the parent value through. The claim rule uses
|
|
23
|
+
* this to find the first *reachable* owner from the `owners` list,
|
|
24
|
+
* eliminating the owner-below-throw-site escape. See TIM-1450.
|
|
25
|
+
*/
|
|
26
|
+
declare const DenyAncestryContext: import("react").Context<readonly string[]>;
|
|
27
|
+
export { DenyAncestryContext };
|
|
19
28
|
export interface TimberErrorBoundaryProps {
|
|
20
29
|
/** The component to render when an error is caught. */
|
|
21
30
|
fallbackComponent?: (...args: unknown[]) => ReactNode;
|
|
@@ -40,10 +49,25 @@ export interface TimberErrorBoundaryProps {
|
|
|
40
49
|
isSlotBoundary?: boolean;
|
|
41
50
|
/**
|
|
42
51
|
* Segment key of the segment that declared this boundary. Only meaningful
|
|
43
|
-
* against a deny digest carrying
|
|
52
|
+
* against a deny digest carrying `owners`. See the decline rule in
|
|
44
53
|
* `render()` and TIM-1356.
|
|
45
54
|
*/
|
|
46
55
|
segmentKey?: string;
|
|
56
|
+
/**
|
|
57
|
+
* Catch denies only; re-throw everything else. Set on the copy of a
|
|
58
|
+
* segment's boundaries placed outside its layout, which gives a deny thrown
|
|
59
|
+
* from that layout's own chrome a catcher keyed to the segment — without
|
|
60
|
+
* changing where a 5xx from the same chrome goes. See TIM-1450.
|
|
61
|
+
*/
|
|
62
|
+
denyOnly?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* The deny chain's terminal: the outermost framework boundary, which claims
|
|
65
|
+
* any deny that streamed past every segment boundary and renders the
|
|
66
|
+
* framework default page. Implies `denyOnly`. Only a deny thrown from a
|
|
67
|
+
* layout's chrome with no status file at or above that layout gets here —
|
|
68
|
+
* the segment copies (`denyOnly`) claim every other shape. See TIM-1450.
|
|
69
|
+
*/
|
|
70
|
+
lastResort?: boolean;
|
|
47
71
|
children: ReactNode;
|
|
48
72
|
}
|
|
49
73
|
interface TimberErrorBoundaryState {
|
|
@@ -57,7 +81,29 @@ export declare class TimberErrorBoundary extends Component<TimberErrorBoundaryPr
|
|
|
57
81
|
componentDidUpdate(prevProps: TimberErrorBoundaryProps): void;
|
|
58
82
|
/** Reset the error state so children re-render. */
|
|
59
83
|
private reset;
|
|
84
|
+
static contextType: import("react").Context<readonly string[]>;
|
|
85
|
+
context: readonly string[];
|
|
86
|
+
private get childAncestry();
|
|
60
87
|
render(): ReactNode;
|
|
88
|
+
/**
|
|
89
|
+
* The status page this boundary shows for a row it claimed. Its shape is
|
|
90
|
+
* the file's documented contract: TSX status files get `{ status,
|
|
91
|
+
* dangerouslyPassData }`, `error.tsx` gets `{ error, digest, reset }` (plus
|
|
92
|
+
* the deny extras for dual-shape implementations — without the error prop,
|
|
93
|
+
* an error.tsx reading error.message crashes on deny fall-through, TIM-1081),
|
|
94
|
+
* and an MDX status file is the element pre-rendered for it at tree-build
|
|
95
|
+
* time, since a server component cannot cross the RSC→client boundary as a
|
|
96
|
+
* function prop.
|
|
97
|
+
*/
|
|
98
|
+
private renderFallback;
|
|
99
|
+
/**
|
|
100
|
+
* A status page (or, for a redirect row, nothing at all) replaced part of
|
|
101
|
+
* the main route, and the segment cache must not learn a chain that never
|
|
102
|
+
* mounted; the marker tells the navigation that commits this tree. A boundary that declined in `render()` re-threw
|
|
103
|
+
* before getting here, so only the boundary that put a page on screen
|
|
104
|
+
* speaks. A slot degrading leaves the page and its chain intact, so slot
|
|
105
|
+
* boundaries stay silent. See `status-page-marker.tsx` (TIM-1495).
|
|
106
|
+
*/
|
|
107
|
+
private markStatusPage;
|
|
61
108
|
}
|
|
62
|
-
export {};
|
|
63
109
|
//# sourceMappingURL=error-boundary.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../src/client/error-boundary.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"error-boundary.d.ts","sourceRoot":"","sources":["../../src/client/error-boundary.tsx"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,SAAS,EAA4C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAc5F;;;;;;GAMG;AACH,QAAA,MAAM,mBAAmB,4CAAuC,CAAC;AAEjE,OAAO,EAAE,mBAAmB,EAAE,CAAC;AAsC/B,MAAM,WAAW,wBAAwB;IACvC,uDAAuD;IACvD,iBAAiB,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,SAAS,CAAC;IACtD;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,SAAS,CAAC;IAC5B;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;OAIG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,UAAU,wBAAwB;IAChC,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB;AAsBD,qBAAa,mBAAoB,SAAQ,SAAS,CAChD,wBAAwB,EACxB,wBAAwB,CACzB;IACC,YAAY,KAAK,EAAE,wBAAwB,EAG1C;IAED,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,KAAK,GAAG,wBAAwB,CAUtE;IAED,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAWpC;IAED,kBAAkB,CAAC,SAAS,EAAE,wBAAwB,GAAG,IAAI,CAO5D;IAED,mDAAmD;IACnD,OAAO,CAAC,KAAK,CAEX;IAEF,OAAgB,WAAW,6CAAuB;IAC1C,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;IAEnC,OAAO,KAAK,aAAa,GAMxB;IAED,MAAM,IAAI,SAAS,CAuKlB;IAED;;;;;;;;;OASG;IACH,OAAO,CAAC,cAAc;IAgCtB;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;CAIvB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use client";
|
|
3
|
-
import {
|
|
4
|
-
export { TimberErrorBoundary };
|
|
3
|
+
import { n as TimberErrorBoundary, t as DenyAncestryContext } from "../_chunks/error-boundary-BfPHZjm0.js";
|
|
4
|
+
export { DenyAncestryContext, TimberErrorBoundary };
|
package/dist/client/history.d.ts
CHANGED
|
@@ -39,13 +39,32 @@ export interface HistoryEntry {
|
|
|
39
39
|
* Scroll positions are stored in history.state or Navigation API entry
|
|
40
40
|
* state, not in this stack — see design/19-client-navigation.md §Scroll Restoration.
|
|
41
41
|
*
|
|
42
|
-
* Entries
|
|
43
|
-
*
|
|
42
|
+
* Entries have no expiry, but only the 50 most recently used URLs are kept
|
|
43
|
+
* by default. Traversing to an evicted entry fetches a fresh payload.
|
|
44
44
|
*/
|
|
45
45
|
export declare class HistoryStack {
|
|
46
46
|
private entries;
|
|
47
|
+
private readonly maxEntries;
|
|
48
|
+
constructor({ maxEntries }?: {
|
|
49
|
+
maxEntries?: number;
|
|
50
|
+
});
|
|
47
51
|
push(url: string, entry: HistoryEntry): void;
|
|
48
52
|
get(url: string): HistoryEntry | undefined;
|
|
53
|
+
/** Presence checks (such as hover prefetch probes) do not promote entries. */
|
|
49
54
|
has(url: string): boolean;
|
|
55
|
+
delete(url: string): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Evict all cached payloads (TIM-1476). Called after a server action
|
|
58
|
+
* that revalidated data — history entries are equally stale since they
|
|
59
|
+
* replay on back/forward without a server check. Clearing forces a
|
|
60
|
+
* fresh fetch on the next traversal.
|
|
61
|
+
*
|
|
62
|
+
* The current URL's `segmentInfo` is preserved (payload nulled) so
|
|
63
|
+
* `applyActionResult` can read it to maintain the segment cache
|
|
64
|
+
* across the piggybacked revalidation. Other branches (reval.paths-
|
|
65
|
+
* only, redirect) do not call `applyActionResult`, so their entries
|
|
66
|
+
* are fully removed (codex on #1131 round 2).
|
|
67
|
+
*/
|
|
68
|
+
clearExcept(currentUrl: string): void;
|
|
50
69
|
}
|
|
51
70
|
//# sourceMappingURL=history.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/client/history.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAI7D,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;CACpC;AAID;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAmC;
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/client/history.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAI7D,MAAM,WAAW,YAAY;IAC3B,kEAAkE;IAClE,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,YAAY,CAAC;IACrB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;CACpC;AAID;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAmC;IAClD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IAEpC,YAAY,EAAE,UAAe,EAAE,GAAE;QAAE,UAAU,CAAC,EAAE,MAAM,CAAA;KAAO,EAK5D;IAED,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAS3C;IAED,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAOzC;IAED,8EAA8E;IAC9E,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAExB;IAED,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAE3B;IAED;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAUpC;CACF"}
|
package/dist/client/index.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { n as classifyUrlSegment } from "../_chunks/segment-classify-C539Pa2O.js";
|
|
3
2
|
import { c as stripRscCacheKey } from "../_chunks/rsc-cache-key-ClUiXQnK.js";
|
|
4
3
|
import { a as mergePreservedSearchParams, i as validateNavigationHref, n as isInternalHref, o as resolveSegmentParams } from "../_chunks/href-validation-BIrxavIy.js";
|
|
5
|
-
import { n as
|
|
6
|
-
import { n as getRouterOrNull } from "../_chunks/router-ref-8gr8qsxN.js";
|
|
4
|
+
import { n as classifyUrlSegment } from "../_chunks/segment-classify-C539Pa2O.js";
|
|
7
5
|
import "../_chunks/param-value-C8TNYchQ.js";
|
|
8
|
-
import {
|
|
6
|
+
import { i as useNavigationContext } from "../_chunks/navigation-context-D0TU0Jog.js";
|
|
7
|
+
import { i as getRouterOrNull, n as usePendingNavigation } from "../_chunks/navigation-root-mHSK9psY.js";
|
|
8
|
+
import { n as getSsrData } from "../_chunks/ssr-data-D6T6Y3ef.js";
|
|
9
9
|
import { t as getLinkCodec } from "../_chunks/codec-registry-oOUxugz3.js";
|
|
10
|
-
import {
|
|
10
|
+
import { n as useSegmentContext } from "../_chunks/segment-context-D9_89u34.js";
|
|
11
11
|
import { n as useQueryStates } from "../_chunks/use-query-states-I3JMng6J.js";
|
|
12
|
-
import React, { createContext,
|
|
12
|
+
import React, { createContext, useActionState as useActionState$1, useContext, useRef, useState, useTransition } from "react";
|
|
13
13
|
import { jsx } from "react/jsx-runtime";
|
|
14
14
|
//#region src/client/use-link-status.ts
|
|
15
15
|
/**
|
|
@@ -76,6 +76,12 @@ function getCurrentSearch() {
|
|
|
76
76
|
if (typeof window !== "undefined") return locationSearch();
|
|
77
77
|
return getSsrData()?.search ?? "";
|
|
78
78
|
}
|
|
79
|
+
/** Native in-page scrolling needs neither an RSC navigation nor a prefetch. */
|
|
80
|
+
function isNativeFragmentLink(anchor) {
|
|
81
|
+
const href = anchor.href;
|
|
82
|
+
const hashIndex = href.indexOf("#");
|
|
83
|
+
return hashIndex !== -1 && href.slice(0, hashIndex) === window.location.href.split("#", 1)[0];
|
|
84
|
+
}
|
|
79
85
|
/**
|
|
80
86
|
* Interpolate dynamic segments in a route pattern with actual values.
|
|
81
87
|
* e.g. interpolateParams("/products/[id]", { id: "123" }) → "/products/123"
|
|
@@ -253,9 +259,11 @@ var Link = function LinkImpl(props) {
|
|
|
253
259
|
const linkStatus = isPending ? LINK_PENDING : LINK_IDLE;
|
|
254
260
|
const internal = isInternalHref(baseHref);
|
|
255
261
|
const resolvedHref = preserveSearchParams && internal ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams) : baseHref;
|
|
262
|
+
const resolveEventUrl = () => new URL(preserveSearchParams ? mergePreservedSearchParams(baseHref, getCurrentSearch(), preserveSearchParams) : resolvedHref, window.location.href);
|
|
256
263
|
const handleClick = internal ? (event) => {
|
|
257
264
|
userOnClick?.(event);
|
|
258
265
|
if (!shouldInterceptClick(event, resolvedHref)) return;
|
|
266
|
+
if (isNativeFragmentLink(event.currentTarget)) return;
|
|
259
267
|
if (onNavigate) {
|
|
260
268
|
let prevented = false;
|
|
261
269
|
onNavigate({ preventDefault: () => {
|
|
@@ -268,19 +276,31 @@ var Link = function LinkImpl(props) {
|
|
|
268
276
|
}
|
|
269
277
|
const router = getRouterOrNull();
|
|
270
278
|
if (!router) return;
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
if (resolved.pathname === window.location.pathname && stripRscCacheKey(resolved.search) === locationSearch() && resolved.hash) return;
|
|
279
|
+
const resolved = resolveEventUrl();
|
|
280
|
+
if (isNativeFragmentLink(event.currentTarget)) return;
|
|
274
281
|
event.preventDefault();
|
|
275
282
|
const shouldScroll = scroll !== false;
|
|
276
283
|
const absoluteHref = resolved.pathname + stripRscCacheKey(resolved.search) + resolved.hash;
|
|
277
284
|
const seq = ++clickSeq.current;
|
|
285
|
+
let settled = false;
|
|
286
|
+
let committed = false;
|
|
287
|
+
const clear = () => {
|
|
288
|
+
if (clickSeq.current === seq) setIsPending(false);
|
|
289
|
+
};
|
|
290
|
+
const onCommit = () => {
|
|
291
|
+
committed = true;
|
|
292
|
+
if (settled) clear();
|
|
293
|
+
};
|
|
278
294
|
const settle = () => {
|
|
279
|
-
|
|
295
|
+
settled = true;
|
|
296
|
+
if (committed) clear();
|
|
280
297
|
};
|
|
281
298
|
setIsPending(true);
|
|
282
|
-
router.navigate(absoluteHref, {
|
|
283
|
-
|
|
299
|
+
router.navigate(absoluteHref, {
|
|
300
|
+
scroll: shouldScroll,
|
|
301
|
+
onCommit
|
|
302
|
+
}).then(settle, (error) => {
|
|
303
|
+
clear();
|
|
284
304
|
throw error;
|
|
285
305
|
});
|
|
286
306
|
} : userOnClick;
|
|
@@ -288,8 +308,8 @@ var Link = function LinkImpl(props) {
|
|
|
288
308
|
userOnMouseEnter?.(event);
|
|
289
309
|
const router = getRouterOrNull();
|
|
290
310
|
if (router) {
|
|
291
|
-
|
|
292
|
-
const resolved =
|
|
311
|
+
if (isNativeFragmentLink(event.currentTarget)) return;
|
|
312
|
+
const resolved = resolveEventUrl();
|
|
293
313
|
router.prefetch(resolved.pathname + stripRscCacheKey(resolved.search));
|
|
294
314
|
}
|
|
295
315
|
} : userOnMouseEnter;
|