@timber-js/app 0.2.0-alpha.132 → 0.2.0-alpha.134

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 (78) hide show
  1. package/LICENSE +8 -0
  2. package/dist/_chunks/{actions-BNhpxrnV.js → actions-B8Fhsosm.js} +51 -12
  3. package/dist/_chunks/actions-B8Fhsosm.js.map +1 -0
  4. package/dist/_chunks/build-output-helper-K7oUCRqU.js.map +1 -1
  5. package/dist/_chunks/cli-schema-sync-q-Ce6-iG.js.map +1 -1
  6. package/dist/_chunks/codec-registry-DqkeAydt.js.map +1 -1
  7. package/dist/_chunks/define-DMq-Yjp7.js.map +1 -1
  8. package/dist/_chunks/define-jeHDXZGq.js.map +1 -1
  9. package/dist/_chunks/format-BjN9HLu9.js.map +1 -1
  10. package/dist/_chunks/handler-store-bIAstrxQ.js.map +1 -1
  11. package/dist/_chunks/href-validation-BJmD5mZh.js +124 -0
  12. package/dist/_chunks/href-validation-BJmD5mZh.js.map +1 -0
  13. package/dist/_chunks/logger-CMd9P1xQ.js.map +1 -1
  14. package/dist/_chunks/metadata-routes-DESG8uk5.js.map +1 -1
  15. package/dist/_chunks/navigation-root-B5-RmsPi.js.map +1 -1
  16. package/dist/_chunks/registry-BI9cgCt7.js.map +1 -1
  17. package/dist/_chunks/resolve-schema-DU2nbVOi.js.map +1 -1
  18. package/dist/_chunks/router-ref-B2-2kIIC.js.map +1 -1
  19. package/dist/_chunks/schema-bridge-o0SwFtXW.js.map +1 -1
  20. package/dist/_chunks/segment-classify-BjfuctV2.js.map +1 -1
  21. package/dist/_chunks/segment-context-Bg0pFbLT.js.map +1 -1
  22. package/dist/_chunks/ssr-data-DVX8S7vj.js.map +1 -1
  23. package/dist/_chunks/stale-reload-CCqb62Z3.js.map +1 -1
  24. package/dist/_chunks/tracing-DP5djRq5.js.map +1 -1
  25. package/dist/_chunks/use-query-states-C9G6LP0F.js.map +1 -1
  26. package/dist/_chunks/use-segment-params-CIeinIx2.js.map +1 -1
  27. package/dist/_chunks/validation-AfEFM5ze.js.map +1 -1
  28. package/dist/_chunks/walkers-BbEu2_Pp.js.map +1 -1
  29. package/dist/_chunks/warnings-CAzunaGo.js.map +1 -1
  30. package/dist/adapters/cloudflare-dev.js.map +1 -1
  31. package/dist/adapters/cloudflare-kv-cache.js.map +1 -1
  32. package/dist/adapters/cloudflare.js.map +1 -1
  33. package/dist/adapters/nitro.js.map +1 -1
  34. package/dist/cache/index.js.map +1 -1
  35. package/dist/cli.js.map +1 -1
  36. package/dist/client/error-boundary.js.map +1 -1
  37. package/dist/client/index.js +5 -17
  38. package/dist/client/index.js.map +1 -1
  39. package/dist/client/internal.js.map +1 -1
  40. package/dist/client/link.d.ts +2 -4
  41. package/dist/client/link.d.ts.map +1 -1
  42. package/dist/client/use-router.d.ts.map +1 -1
  43. package/dist/codec.js.map +1 -1
  44. package/dist/cookies/index.js.map +1 -1
  45. package/dist/index.js +153 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/params/index.js.map +1 -1
  48. package/dist/search-params/index.js.map +1 -1
  49. package/dist/server/deny-renderer.d.ts.map +1 -1
  50. package/dist/server/index.js +1 -1
  51. package/dist/server/index.js.map +1 -1
  52. package/dist/server/internal.js +22 -11
  53. package/dist/server/internal.js.map +1 -1
  54. package/dist/server/pipeline-interception.d.ts.map +1 -1
  55. package/dist/server/pipeline-phases.d.ts +5 -1
  56. package/dist/server/pipeline-phases.d.ts.map +1 -1
  57. package/dist/server/pipeline.d.ts.map +1 -1
  58. package/dist/server/primitives.d.ts +5 -2
  59. package/dist/server/primitives.d.ts.map +1 -1
  60. package/dist/server/rsc-entry/rsc-payload.d.ts.map +1 -1
  61. package/dist/shared/href-validation.d.ts +38 -0
  62. package/dist/shared/href-validation.d.ts.map +1 -0
  63. package/dist/shims/font-google.js.map +1 -1
  64. package/dist/shims/font-local.js.map +1 -1
  65. package/package.json +12 -13
  66. package/src/cli.ts +0 -0
  67. package/src/client/link.tsx +5 -32
  68. package/src/client/use-router.ts +3 -0
  69. package/src/server/deny-renderer.ts +0 -1
  70. package/src/server/pipeline-interception.ts +1 -0
  71. package/src/server/pipeline-phases.ts +30 -19
  72. package/src/server/pipeline.ts +18 -1
  73. package/src/server/primitives.ts +8 -17
  74. package/src/server/rsc-entry/rsc-payload.ts +0 -4
  75. package/src/shared/href-validation.ts +123 -0
  76. package/dist/_chunks/actions-BNhpxrnV.js.map +0 -1
  77. package/dist/_chunks/merge-search-params-Cm1j99HC.js +0 -41
  78. package/dist/_chunks/merge-search-params-Cm1j99HC.js.map +0 -1
package/dist/index.js CHANGED
@@ -1368,6 +1368,15 @@ function timberDevServer(ctx) {
1368
1368
  return {
1369
1369
  name: "timber-dev-server",
1370
1370
  apply: "serve",
1371
+ /**
1372
+ * Register the dev server middleware and config file watcher.
1373
+ *
1374
+ * Registers as a pre-hook (no return value) so our middleware runs
1375
+ * before Vite's built-in SPA fallback / historyApiFallback. This
1376
+ * ensures we see the original URL (e.g. /blog) rather than a
1377
+ * rewritten /index.html. Vite-internal and asset requests are
1378
+ * filtered out explicitly and passed through to Vite.
1379
+ */
1371
1380
  configureServer(server) {
1372
1381
  const configPaths = CONFIG_FILE_NAMES.map((name) => join(ctx.root, name));
1373
1382
  server.watcher.add(configPaths);
@@ -1825,6 +1834,17 @@ function generateCacheHandlerModule(configPath, hasCacheHandler) {
1825
1834
  function timberEntries(ctx) {
1826
1835
  return {
1827
1836
  name: "timber-entries",
1837
+ /**
1838
+ * Resolve virtual entry and config module IDs.
1839
+ *
1840
+ * Handles:
1841
+ * - virtual:timber-rsc-entry → real file path
1842
+ * - virtual:timber-ssr-entry → real file path
1843
+ * - virtual:timber-browser-entry → real file path
1844
+ * - virtual:timber-config → \0-prefixed virtual ID
1845
+ * - \0 prefix stripping (RSC plugin re-imports)
1846
+ * - Root prefix stripping (SSR build entries)
1847
+ */
1828
1848
  resolveId(id) {
1829
1849
  let cleanId = stripNullPrefix(id);
1830
1850
  cleanId = stripRootPrefix(cleanId, ctx.root);
@@ -1834,6 +1854,12 @@ function timberEntries(ctx) {
1834
1854
  if (cleanId === VIRTUAL_IDS.cacheHandler) return RESOLVED_CACHE_HANDLER_ID;
1835
1855
  return null;
1836
1856
  },
1857
+ /**
1858
+ * Load virtual modules: virtual:timber-config and virtual:timber-instrumentation.
1859
+ *
1860
+ * Entry files (rsc/ssr/browser) are real TypeScript files that Vite
1861
+ * processes normally. Only config and instrumentation need generated code.
1862
+ */
1837
1863
  load(id) {
1838
1864
  if (id === RESOLVED_CONFIG_ID) return generateConfigModule(ctx);
1839
1865
  if (id === RESOLVED_INSTRUMENTATION_ID) return generateInstrumentationModule(detectInstrumentationFile(ctx.root));
@@ -2372,6 +2398,14 @@ function timberRouting(ctx) {
2372
2398
  }
2373
2399
  return {
2374
2400
  name: "timber-routing",
2401
+ /**
2402
+ * Resolve the virtual module ID.
2403
+ *
2404
+ * Handles:
2405
+ * - virtual:timber-route-manifest
2406
+ * - <root>/virtual:timber-route-manifest (Vite SSR build root prefix)
2407
+ * - \0virtual:timber-route-manifest (RSC plugin re-imports)
2408
+ */
2375
2409
  resolveId(id, importer) {
2376
2410
  const cleanId = id.startsWith("\0") ? id.slice(1) : id;
2377
2411
  if (cleanId === VIRTUAL_MODULE_ID$1) return RESOLVED_VIRTUAL_ID$1;
@@ -2386,6 +2420,12 @@ function timberRouting(ctx) {
2386
2420
  }
2387
2421
  return null;
2388
2422
  },
2423
+ /**
2424
+ * Generate the route manifest module.
2425
+ *
2426
+ * The manifest exports the route tree as a serialized data structure
2427
+ * with absolute import paths for all file references.
2428
+ */
2389
2429
  load(id) {
2390
2430
  if (id === RESOLVED_VIRTUAL_ID$1) {
2391
2431
  if (!ctx.routeTree) rescan();
@@ -2406,10 +2446,23 @@ function timberRouting(ctx) {
2406
2446
  }
2407
2447
  return null;
2408
2448
  },
2449
+ /**
2450
+ * Scan routes at build start.
2451
+ *
2452
+ * In dev mode, skip — configureServer runs after buildStart and will
2453
+ * do its own scan. Avoids a redundant FS traversal on cold start.
2454
+ */
2409
2455
  buildStart() {
2410
2456
  if (ctx.dev) return;
2411
2457
  rescan();
2412
2458
  },
2459
+ /**
2460
+ * Set up file watcher in dev mode.
2461
+ *
2462
+ * Watches app/ for file additions/deletions/renames that affect
2463
+ * the route tree, regenerates the manifest, and invalidates
2464
+ * dependent modules via HMR.
2465
+ */
2413
2466
  configureServer(devServer) {
2414
2467
  rescan();
2415
2468
  devServer.watcher.add(ctx.appDir);
@@ -2802,6 +2855,24 @@ function timberShims(_ctx) {
2802
2855
  return {
2803
2856
  name: "timber-shims",
2804
2857
  enforce: "pre",
2858
+ /**
2859
+ * Resolve next/* imports to shim files.
2860
+ *
2861
+ * Resolution order:
2862
+ * 1. Check server-only / client-only poison pill packages
2863
+ * 2. Strip .js extension from the import specifier
2864
+ * 3. Check next/* shim map
2865
+ * 4. Return null (pass through) for everything else
2866
+ *
2867
+ * All @timber-js/app/* subpath imports resolve to dist/ via package.json
2868
+ * exports in all environments. This ensures a single module instance
2869
+ * for each module — the dist/ chunks share code via rollup's code
2870
+ * splitting. No src/ remapping is needed (see TIM-568).
2871
+ *
2872
+ * The only exception is @timber-js/app/server in the client (browser)
2873
+ * environment, which returns a virtual stub — server code must never
2874
+ * be bundled into the browser.
2875
+ */
2805
2876
  resolveId(id) {
2806
2877
  if (id === "server-only") return SERVER_ONLY_VIRTUAL;
2807
2878
  if (id === "client-only") return CLIENT_ONLY_VIRTUAL;
@@ -2838,6 +2909,13 @@ function timberShims(_ctx) {
2838
2909
  }
2839
2910
  return null;
2840
2911
  },
2912
+ /**
2913
+ * Serve virtual modules for server-only / client-only poison pills.
2914
+ *
2915
+ * In the correct environment, the module is a no-op (empty export).
2916
+ * In the wrong environment, it throws a build-time error message that
2917
+ * clearly identifies the boundary violation.
2918
+ */
2841
2919
  load(id) {
2842
2920
  const isClient = this.environment?.name === "client";
2843
2921
  if (id === SERVER_ONLY_VIRTUAL) {
@@ -5521,7 +5599,35 @@ function timberFonts(ctx) {
5521
5599
  const pipeline = new FontPipeline();
5522
5600
  return {
5523
5601
  name: "timber-fonts",
5602
+ /**
5603
+ * `enforce: 'pre'` is load-bearing for the font CSS pipeline (TIM-828).
5604
+ *
5605
+ * The transform hook injects a side-effect
5606
+ * `import 'virtual:timber-font-css/<hash>.css'` into every file that
5607
+ * registers a font. @vitejs/plugin-rsc's `rsc:rsc-css-export-transform`
5608
+ * later inspects each server component's source for CSS imports and
5609
+ * wraps the default export in `import.meta.viteRsc.loadCss(...)` so
5610
+ * the matched virtual CSS module ends up `preinit`'d as
5611
+ * `<link rel="stylesheet" data-rsc-css-href=...>`.
5612
+ *
5613
+ * If timber-fonts ran in normal order it would inject the CSS import
5614
+ * *after* plugin-rsc had already scanned the source, and the font CSS
5615
+ * would be silently dropped from the rendered HTML. Running in the
5616
+ * `pre` slot guarantees our injected import is visible to plugin-rsc's
5617
+ * later CSS-export scan.
5618
+ */
5524
5619
  enforce: "pre",
5620
+ /**
5621
+ * Resolve `@timber/fonts/google`, `@timber/fonts/local`, and the
5622
+ * per-importer `virtual:timber-font-css/<hash>.css` modules injected
5623
+ * by the transform hook.
5624
+ *
5625
+ * Strips the `\0` prefix added by the RSC plugin's re-imports and the
5626
+ * absolute root prefix added by SSR build entries before comparing
5627
+ * against the public IDs. The `.css` suffix on font-css ids is
5628
+ * preserved so Vite's CSS pipeline (`isCSSRequest`) recognises the
5629
+ * resolved id as CSS.
5630
+ */
5525
5631
  resolveId(id) {
5526
5632
  let cleanId = id.startsWith("\0") ? id.slice(1) : id;
5527
5633
  const qIdx = cleanId.indexOf("?");
@@ -5537,6 +5643,19 @@ function timberFonts(ctx) {
5537
5643
  if (pipeline.cssIdMap.matches(cleanId)) return "\0" + cleanId + query;
5538
5644
  return null;
5539
5645
  },
5646
+ /**
5647
+ * Return generated source for font virtual modules.
5648
+ *
5649
+ * The per-importer `virtual:timber-font-css/<hash>.css` modules
5650
+ * return the combined font CSS for the specific importer that
5651
+ * registered them. Vite's CSS pipeline handles injection from here:
5652
+ * in dev the module rides the CSS HMR channel; in production it is
5653
+ * emitted as a real CSS asset and @vitejs/plugin-rsc's `collectCss`
5654
+ * walks the RSC module graph to find it.
5655
+ *
5656
+ * In dev mode we also lazily resolve Google `@font-face` descriptors
5657
+ * from the CDN here, since `buildStart` is a no-op in dev.
5658
+ */
5540
5659
  async load(id) {
5541
5660
  if (id === "\0@timber/fonts/google") return generateGoogleVirtualModule(pipeline.fonts());
5542
5661
  if (id === "\0@timber/fonts/local") return generateLocalVirtualModule();
@@ -5558,9 +5677,22 @@ function timberFonts(ctx) {
5558
5677
  }
5559
5678
  return null;
5560
5679
  },
5680
+ /**
5681
+ * Serve local font files in dev mode under `/_timber/fonts/<basename>`.
5682
+ * Font CSS goes through Vite's CSS pipeline (virtual modules), not
5683
+ * this middleware.
5684
+ */
5561
5685
  configureServer(server) {
5562
5686
  installFontDevMiddleware(server, pipeline);
5563
5687
  },
5688
+ /**
5689
+ * Download and cache Google Fonts during production builds.
5690
+ *
5691
+ * In dev mode this is a no-op — fonts point to the Google CDN and are
5692
+ * resolved lazily inside `load()` when the side-effect module runs.
5693
+ * The registry is populated by the transform hook which runs before
5694
+ * `buildStart` in the build pipeline, so all fonts are known here.
5695
+ */
5564
5696
  async buildStart() {
5565
5697
  if (ctx.dev) return;
5566
5698
  const googleFonts = [...pipeline.googleFonts()];
@@ -5572,9 +5704,23 @@ function timberFonts(ctx) {
5572
5704
  pipeline.attachFaces(font.id, generateProductionFontFaces(familyCached, font.display));
5573
5705
  }
5574
5706
  },
5707
+ /**
5708
+ * Scan source files for font function calls and extract static config.
5709
+ *
5710
+ * Delegates to `runFontsTransform`. Errors raised through the
5711
+ * `this.error` callback are surfaced as Vite plugin errors.
5712
+ */
5575
5713
  transform(code, id) {
5576
5714
  return runFontsTransform(code, id, pipeline, (msg) => this.error(msg));
5577
5715
  },
5716
+ /**
5717
+ * Emit font binaries and build-manifest entries.
5718
+ *
5719
+ * Google fonts use the content-hashed filenames produced during
5720
+ * `buildStart`. Local fonts are emitted by basename. Font CSS is
5721
+ * handled by Vite's CSS pipeline via virtual modules — we only emit
5722
+ * binaries here.
5723
+ */
5578
5724
  generateBundle() {
5579
5725
  emitFontAssets(pipeline, (asset) => this.emitFile(asset), (msg) => this.warn(msg));
5580
5726
  if (!ctx.buildManifest) return;
@@ -5643,6 +5789,13 @@ function detectDynamicApis(code, fileId) {
5643
5789
  function timberStaticBuild(ctx) {
5644
5790
  return {
5645
5791
  name: "timber-static-build",
5792
+ /**
5793
+ * Validate source files during transform.
5794
+ *
5795
+ * In static mode, we check every app/ file for:
5796
+ * - Dynamic API usage (getCookies(), getHeaders()) → build error
5797
+ * - When client JS disabled: 'use client' / 'use server' directives → build error
5798
+ */
5646
5799
  transform(code, id) {
5647
5800
  if (!(ctx.config.output === "static")) return null;
5648
5801
  if (id.includes("node_modules")) return null;