@timber-js/app 0.2.0-alpha.213 → 0.2.0-alpha.214

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.
Files changed (57) hide show
  1. package/dist/_chunks/{actions-CEootpB1.js → actions-CUh3cClk.js} +2 -2
  2. package/dist/_chunks/{actions-CEootpB1.js.map → actions-CUh3cClk.js.map} +1 -1
  3. package/dist/_chunks/{canonicalize-CgHoscYO.js → canonicalize-BAWkWiKK.js} +28 -2
  4. package/dist/_chunks/{canonicalize-CgHoscYO.js.map → canonicalize-BAWkWiKK.js.map} +1 -1
  5. package/dist/_chunks/{chains-BfoPFraI.js → chains-CjK1Eu6a.js} +2 -2
  6. package/dist/_chunks/{chains-BfoPFraI.js.map → chains-CjK1Eu6a.js.map} +1 -1
  7. package/dist/_chunks/{classify-BT66U83D.js → classify-GAdt6aiA.js} +2 -2
  8. package/dist/_chunks/{classify-BT66U83D.js.map → classify-GAdt6aiA.js.map} +1 -1
  9. package/dist/_chunks/{cli-check-BfQ54-UJ.js → cli-check-DOzjlycg.js} +3 -3
  10. package/dist/_chunks/{cli-check-BfQ54-UJ.js.map → cli-check-DOzjlycg.js.map} +1 -1
  11. package/dist/_chunks/{cli-schema-sync-czh2dsLs.js → cli-schema-sync-B9wDaQvW.js} +2 -2
  12. package/dist/_chunks/{cli-schema-sync-czh2dsLs.js.map → cli-schema-sync-B9wDaQvW.js.map} +1 -1
  13. package/dist/_chunks/{client-dep-entries-CQwpb8dI.js → client-dep-entries-DyDqXOF9.js} +2 -2
  14. package/dist/_chunks/{client-dep-entries-CQwpb8dI.js.map → client-dep-entries-DyDqXOF9.js.map} +1 -1
  15. package/dist/_chunks/{convention-lint-BEVW4EID.js → convention-lint-B3QEGJX7.js} +2 -2
  16. package/dist/_chunks/{convention-lint-BEVW4EID.js.map → convention-lint-B3QEGJX7.js.map} +1 -1
  17. package/dist/_chunks/{dev-server-FKxptbnI.js → dev-server-_9L4KWC-.js} +3 -3
  18. package/dist/_chunks/{dev-server-FKxptbnI.js.map → dev-server-_9L4KWC-.js.map} +1 -1
  19. package/dist/_chunks/{error-boundary-9g_Lb2na.js → error-boundary-xxxLtXt6.js} +38 -5
  20. package/dist/_chunks/{error-boundary-9g_Lb2na.js.map → error-boundary-xxxLtXt6.js.map} +1 -1
  21. package/dist/_chunks/{live-graph-C_4v-fHv.js → live-graph-Cd3YHvrH.js} +4 -4
  22. package/dist/_chunks/{live-graph-C_4v-fHv.js.map → live-graph-Cd3YHvrH.js.map} +1 -1
  23. package/dist/_chunks/{poison-scan-C92liMAr.js → poison-scan-BnJjBOkn.js} +2 -2
  24. package/dist/_chunks/{poison-scan-C92liMAr.js.map → poison-scan-BnJjBOkn.js.map} +1 -1
  25. package/dist/_chunks/{scanner-CQt12vE2.js → scanner-CKAT5gRx.js} +2 -2
  26. package/dist/_chunks/{scanner-CQt12vE2.js.map → scanner-CKAT5gRx.js.map} +1 -1
  27. package/dist/_chunks/{walkers-DAT4avhZ.js → walkers-DwCXEyRu.js} +3 -3
  28. package/dist/_chunks/{walkers-DAT4avhZ.js.map → walkers-DwCXEyRu.js.map} +1 -1
  29. package/dist/adapters/nitro-preview.d.ts.map +1 -1
  30. package/dist/adapters/nitro.js +11 -2
  31. package/dist/adapters/nitro.js.map +1 -1
  32. package/dist/analyze/crawl-entry.js +3 -3
  33. package/dist/analyze/graph-command.js +2 -2
  34. package/dist/cli.js +3 -3
  35. package/dist/client/error-boundary.js +1 -1
  36. package/dist/client/internal.js +22 -7
  37. package/dist/client/internal.js.map +1 -1
  38. package/dist/client/router-effects.d.ts +70 -12
  39. package/dist/client/router-effects.d.ts.map +1 -1
  40. package/dist/client/router.d.ts.map +1 -1
  41. package/dist/index.js +5 -5
  42. package/dist/routing/index.js +2 -2
  43. package/dist/server/canonicalize.d.ts +22 -0
  44. package/dist/server/canonicalize.d.ts.map +1 -1
  45. package/dist/server/index.js +1 -1
  46. package/dist/server/internal.js +54 -3
  47. package/dist/server/internal.js.map +1 -1
  48. package/dist/server/pipeline-helpers.d.ts +31 -0
  49. package/dist/server/pipeline-helpers.d.ts.map +1 -1
  50. package/dist/server/pipeline.d.ts.map +1 -1
  51. package/package.json +1 -1
  52. package/src/adapters/nitro-preview.ts +10 -1
  53. package/src/client/router-effects.ts +99 -14
  54. package/src/client/router.ts +29 -13
  55. package/src/server/canonicalize.ts +29 -0
  56. package/src/server/pipeline-helpers.ts +61 -0
  57. package/src/server/pipeline.ts +13 -1
package/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
- import { a as reportConfigErrors, i as formatMissingPeers, n as timberDevServer, o as validateConfig, r as checkPeerDependencies } from "./_chunks/dev-server-FKxptbnI.js";
1
+ import { a as reportConfigErrors, i as formatMissingPeers, n as timberDevServer, o as validateConfig, r as checkPeerDependencies } from "./_chunks/dev-server-_9L4KWC-.js";
2
2
  import { i as resolveRenderTimeoutMs } from "./_chunks/render-timeout-DOf_BBcO.js";
3
3
  import { t as writeFileAtomic } from "./_chunks/write-file-atomic-D1qWqHb4.js";
4
- import { d as collectInterceptionRewrites, l as getPrerenderExport, n as scanRoutes } from "./_chunks/scanner-CQt12vE2.js";
4
+ import { d as collectInterceptionRewrites, l as getPrerenderExport, n as scanRoutes } from "./_chunks/scanner-CKAT5gRx.js";
5
5
  import { a as isDynamicMetadataExtension } from "./_chunks/metadata-routes-DSDjM_hJ.js";
6
6
  import { v as swallow } from "./_chunks/logger-CbLdcy-W.js";
7
- import { c as buildDirToRouteMap, n as formatConventionWarnings, o as flushCodegenWrites, r as lintConventions, s as writeRouteCodegen, t as checkAppDirExists } from "./_chunks/convention-lint-BEVW4EID.js";
7
+ import { c as buildDirToRouteMap, n as formatConventionWarnings, o as flushCodegenWrites, r as lintConventions, s as writeRouteCodegen, t as checkAppDirExists } from "./_chunks/convention-lint-B3QEGJX7.js";
8
8
  import { a as loadTimberConfigFile, c as resolveAppDir, d as createNoopTimer, l as resolveBuildDir, n as contextAdapter, o as mergeFileConfig, r as createPluginContext, t as adapterConfig, u as resolveClientJavascript } from "./_chunks/plugin-context-BPBPc3lL.js";
9
9
  import { i as withBasePath, n as normalizeBasePath, t as isRelativeBase } from "./_chunks/base-path-DaQrzbez.js";
10
- import { r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "./_chunks/walkers-DAT4avhZ.js";
10
+ import { r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "./_chunks/walkers-DwCXEyRu.js";
11
11
  import { i as poisonPillSource, r as markerFor, t as EXPLICIT_MARKERS } from "./_chunks/poison-rules-DoEhbqaY.js";
12
12
  import { n as rscPayloadPath, t as RSC_PAYLOAD_DIR } from "./_chunks/rsc-payload-path-dffcs4jp.js";
13
13
  import { a as REDIRECTS_FILE, n as detectFsIdentity, o as RESERVED_OUTPUT_PATHS, r as fsKey, t as EXACT_FS_IDENTITY } from "./_chunks/fs-identity-BRKuL215.js";
@@ -9637,7 +9637,7 @@ function timber(config) {
9637
9637
  let environments = envOutDirs;
9638
9638
  if (!isPreview) {
9639
9639
  ctx.timer.start("client-deps-scan");
9640
- const { clientOptimizeDepsEntries } = await import("./_chunks/client-dep-entries-CQwpb8dI.js");
9640
+ const { clientOptimizeDepsEntries } = await import("./_chunks/client-dep-entries-DyDqXOF9.js");
9641
9641
  const entries = await clientOptimizeDepsEntries(resolvedRoot, BROWSER_ENTRY_FILE, buildDir);
9642
9642
  ctx.timer.end("client-deps-scan");
9643
9643
  environments = {
@@ -1,5 +1,5 @@
1
1
  import { a as INTERCEPTION_MARKERS, i as DEFAULT_PAGE_EXTENSIONS, n as classifyUrlSegment, t as classifySegment } from "../_chunks/segment-classify-C539Pa2O.js";
2
- import { d as collectInterceptionRewrites, n as scanRoutes } from "../_chunks/scanner-CQt12vE2.js";
2
+ import { d as collectInterceptionRewrites, n as scanRoutes } from "../_chunks/scanner-CKAT5gRx.js";
3
3
  import { t as generateRouteMap } from "../_chunks/codegen-Bps1sLKJ.js";
4
- import { n as collectDynamicSegmentsFromTree, r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "../_chunks/walkers-DAT4avhZ.js";
4
+ import { n as collectDynamicSegmentsFromTree, r as validateSchemaAgainstRoutes, t as collectLeafRoutes } from "../_chunks/walkers-DwCXEyRu.js";
5
5
  export { DEFAULT_PAGE_EXTENSIONS, INTERCEPTION_MARKERS, classifySegment, classifyUrlSegment, collectDynamicSegmentsFromTree, collectInterceptionRewrites, collectLeafRoutes, generateRouteMap, scanRoutes, validateSchemaAgainstRoutes };
@@ -37,4 +37,26 @@ export declare const NULL_BYTE_RE: RegExp;
37
37
  * @param stripTrailingSlash - Whether to strip trailing slashes. Default: true.
38
38
  */
39
39
  export declare function canonicalize(rawPathname: string, stripTrailingSlash?: boolean): CanonicalizeResult;
40
+ /**
41
+ * The path a safe request for `rawPathname` should be redirected to — or
42
+ * `rawPathname` itself when it is already canonical.
43
+ *
44
+ * Applies the structural steps of `canonicalize()` — collapse `//` (step 3)
45
+ * and strip the trailing slash (step 5) — to the **encoded** path, without
46
+ * decoding. That is what the browser will send back, so the redirect is
47
+ * idempotent: `/%61dmin` differs from its canonical form `/admin` only by
48
+ * percent-encoding, and redirecting it to `/admin` would compare against the
49
+ * decoded form forever. Decoding cannot introduce a `/` (`%2f` is rejected),
50
+ * so the structure is the same before and after decoding.
51
+ *
52
+ * Dot segments are not handled here: the request URL is parsed by the WHATWG
53
+ * URL parser, which has already resolved `.`, `..` and their encoded forms.
54
+ *
55
+ * Only meaningful for a path `canonicalize()` accepted. The collapse also
56
+ * means the result can never start with `//`, so it is never a
57
+ * protocol-relative redirect target.
58
+ *
59
+ * See design/07-routing.md §"Non-Canonical URLs Redirect"
60
+ */
61
+ export declare function canonicalRequestPath(rawPathname: string, stripTrailingSlash?: boolean): string;
40
62
  //# sourceMappingURL=canonicalize.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"canonicalize.d.ts","sourceRoot":"","sources":["../../src/server/canonicalize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC;AAE7F;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAE/C,2DAA2D;AAC3D,eAAO,MAAM,YAAY,QAAS,CAAC;AAEnC;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,kBAAkB,CAwD/F"}
1
+ {"version":3,"file":"canonicalize.d.ts","sourceRoot":"","sources":["../../src/server/canonicalize.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,uEAAuE;AACvE,MAAM,MAAM,kBAAkB,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,GAAG,CAAA;CAAE,CAAC;AAE7F;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAE/C,2DAA2D;AAC3D,eAAO,MAAM,YAAY,QAAS,CAAC;AAEnC;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,kBAAkB,CAwD/F;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,EAAE,kBAAkB,UAAO,GAAG,MAAM,CAM3F"}
@@ -1,6 +1,6 @@
1
1
  import { A as getSpanId, C as isRedirectSignal, E as addSpanEvent, F as withSpan, S as isDenySignal, T as isDevMode, j as getTraceId, w as isDebug } from "../_chunks/logger-CbLdcy-W.js";
2
2
  import { t as formatSize } from "../_chunks/format-BKclgVbk.js";
3
- import { _ as getHeaders, a as parseFormData, c as deny, d as waitUntil, i as coerce, l as redirect, n as revalidatePath, p as getCookieJar, r as revalidateTag, u as redirectExternal, y as getSegmentParams } from "../_chunks/actions-CEootpB1.js";
3
+ import { _ as getHeaders, a as parseFormData, c as deny, d as waitUntil, i as coerce, l as redirect, n as revalidatePath, p as getCookieJar, r as revalidateTag, u as redirectExternal, y as getSegmentParams } from "../_chunks/actions-CUh3cClk.js";
4
4
  //#region src/shared/redirect-type.ts
5
5
  /**
6
6
  * Next.js redirect type discriminator.
@@ -3,12 +3,12 @@ import { n as isRscContentType, r as isRscRequest } from "../_chunks/rsc-media-t
3
3
  import { n as classifyUrlSegment, r as effectiveUrlSegment } from "../_chunks/segment-classify-C539Pa2O.js";
4
4
  import { t as computeSegmentTreePaths } from "../_chunks/segment-keys-D5hu1hz4.js";
5
5
  import { i as getMetadataRouteServePath, n as classifyMetadataRoute, r as getMetadataRouteAutoLink, t as METADATA_ROUTE_CONVENTIONS } from "../_chunks/metadata-routes-DSDjM_hJ.js";
6
- import { r as canonicalize } from "../_chunks/canonicalize-CgHoscYO.js";
6
+ import { i as canonicalize, r as canonicalRequestPath } from "../_chunks/canonicalize-BAWkWiKK.js";
7
7
  import { i as requestContextAls, n as prebuiltCaptureAls, o as timingAls, t as earlyHintsSenderAls } from "../_chunks/als-registry-BZqHCtq-.js";
8
8
  import { C as isRedirectSignal, F as withSpan, M as replaceTraceId, N as runWithTraceId, O as generateTraceId, P as setSpanAttribute, S as isDenySignal, _ as setLogger, a as logMiddlewareError, c as logRenderError, d as logRouteError, f as logRouteSetupError, g as logWaitUntilUnsupported, h as logWaitUntilRejected, j as getTraceId, k as getOtelTraceId, l as logRequestCompleted, m as logSwrRefetchFailed, n as logCacheMiss, o as logMiddlewareShortCircuit, p as logSlowRequest, r as logCsrfRejected, s as logProxyError, t as getLogger, u as logRequestReceived, v as swallow, w as isDebug, x as isControlFlowSignal } from "../_chunks/logger-CbLdcy-W.js";
9
9
  import { i as collectRouteModulepreloads, n as collectRouteCss, r as collectRouteFonts } from "../_chunks/build-manifest-DWppEdLB.js";
10
- import { i as isRscCacheKeyShape, n as RSC_KEY_PARAM, s as rscCacheKey, t as RSC_KEY_HEADERS } from "../_chunks/rsc-cache-key-ClUiXQnK.js";
11
- import { C as setMutableCookieContext, S as setMatchedSegmentPath, T as runOutsideReactCacheScope, b as markResponseFlushed, f as getCookie, g as getHeader, h as applyRequestHeaderOverlay, i as coerce, m as getSetCookieHeaders, o as DenySignal, s as RedirectSignal, t as executeAction, v as getSearchParams, w as setSegmentParams, x as runWithRequestContext, y as getSegmentParams } from "../_chunks/actions-CEootpB1.js";
10
+ import { i as isRscCacheKeyShape, n as RSC_KEY_PARAM, r as appVisibleSearch, s as rscCacheKey, t as RSC_KEY_HEADERS } from "../_chunks/rsc-cache-key-ClUiXQnK.js";
11
+ import { C as setMutableCookieContext, S as setMatchedSegmentPath, T as runOutsideReactCacheScope, b as markResponseFlushed, f as getCookie, g as getHeader, h as applyRequestHeaderOverlay, i as coerce, m as getSetCookieHeaders, o as DenySignal, s as RedirectSignal, t as executeAction, v as getSearchParams, w as setSegmentParams, x as runWithRequestContext, y as getSegmentParams } from "../_chunks/actions-CUh3cClk.js";
12
12
  import { a as getManifestEntry, b as wasInvalidatedSince, c as lookupPrebuiltPayload, d as checkVersionSkew, i as storeOverlayEntry, l as setPrebuiltPayloadSource, n as lookupOverlay, o as hasPrebuiltPayloadSource, r as overlayKey, s as isParamIndependent, u as applyReloadHeaders, v as currentInvalidationEpoch, x as getWaitUntil, y as lastInvalidationEpochFor } from "../_chunks/cache-api-LA3sBpUS.js";
13
13
  import { n as createSingleflight } from "../_chunks/singleflight-BeVMMKi0.js";
14
14
  import { n as toBracketKey } from "../_chunks/resolve-schema-CBR6Lm4i.js";
@@ -580,6 +580,55 @@ function csrfGate(req, canonicalPath, csrf, matchRoute) {
580
580
  return exempt ? null : csrfRejectionResponse(req, result);
581
581
  }
582
582
  /**
583
+ * The redirect for a safe request whose URL is not canonical, or null to
584
+ * serve it.
585
+ *
586
+ * A GET for `/admin/` used to render with a 200 at that URL. The server
587
+ * rendered with the canonical `/admin` while the browser hydrated with the
588
+ * raw `/admin/`, so anything branching on `usePathname()` hydrated
589
+ * mismatched (TIM-1571). Redirecting makes the address bar — the client's
590
+ * copy of the URL — the canonical one, and leaves one URL per page for
591
+ * caches and crawlers.
592
+ *
593
+ * Only GET and HEAD are redirected. Every other method — an action's POST,
594
+ * a CORS preflight — is served at the canonical path, as before: a
595
+ * redirected POST is re-sent by the browser, which an action must not
596
+ * depend on.
597
+ *
598
+ * Runs inside proxy.ts's `next()`, not before it, so proxy.ts still runs
599
+ * on every request and its headers land on the redirect. A cross-origin
600
+ * `GET /api/items/` needs proxy.ts's CORS headers on the 308 itself:
601
+ * browsers apply the CORS check to a redirect response.
602
+ *
603
+ * A document request gets a relative 308 (never built from `Host`). A page
604
+ * navigation gets the router's soft redirect, decided exactly as for a
605
+ * `redirect()` outcome: an RSC request that does not match a route.ts
606
+ * handler (`buildRedirectResponse`). The framework's `_rsc` cache key is
607
+ * dropped from the soft target; the router appends a fresh one when it
608
+ * fetches the canonical URL.
609
+ *
610
+ * See design/07-routing.md §"Non-Canonical URLs Redirect".
611
+ */
612
+ function canonicalRedirect(req, canonicalPath, stripTrailingSlash, matchRoute) {
613
+ if (req.method !== "GET" && req.method !== "HEAD") return null;
614
+ const url = new URL(req.url);
615
+ const target = canonicalRequestPath(url.pathname, stripTrailingSlash);
616
+ if (target === url.pathname) return null;
617
+ const navigation = isRscRequest(req) && !isApiRoutePath(canonicalPath, matchRoute);
618
+ const search = navigation ? appVisibleSearch(url).search : url.search;
619
+ return buildRedirectResponse(new RedirectSignal(target + search, 308), new Headers(), navigation);
620
+ }
621
+ /** Whether `path` matches a route.ts handler. A matcher that throws: no. */
622
+ function isApiRoutePath(path, matchRoute) {
623
+ try {
624
+ const match = matchRoute(path);
625
+ return match !== null && isApiRouteChain(match.segments);
626
+ } catch (err) {
627
+ swallow(err, "canonical redirect: route match for route.ts lookup threw");
628
+ return false;
629
+ }
630
+ }
631
+ /**
583
632
  * Build a proxy resolver closure from the declared source. Called exactly
584
633
  * once at `createPipeline` setup time, so the hot path sees only the branch
585
634
  * that corresponds to this pipeline's configured variant.
@@ -2027,6 +2076,8 @@ function createPipeline(config) {
2027
2076
  return csrfRejection;
2028
2077
  }
2029
2078
  const innerHandler = async () => {
2079
+ const redirect = canonicalRedirect(req, canonicalPath, stripTrailingSlash, config.matchRoute);
2080
+ if (redirect) return redirect;
2030
2081
  if (config.dispatchAction) {
2031
2082
  const actionResult = await config.dispatchAction(canonicalReq, canonicalPath, pipelineFn);
2032
2083
  if (actionResult) return actionResult;