@rangojs/router 0.0.0-experimental.14 → 0.0.0-experimental.141

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 (460) hide show
  1. package/AGENTS.md +17 -0
  2. package/README.md +432 -7
  3. package/dist/bin/rango.js +2073 -213
  4. package/dist/testing/vitest.js +82 -0
  5. package/dist/vite/index.js +7258 -2714
  6. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  7. package/package.json +140 -67
  8. package/skills/api-client/SKILL.md +211 -0
  9. package/skills/breadcrumbs/SKILL.md +329 -0
  10. package/skills/bundle-analysis/SKILL.md +159 -0
  11. package/skills/cache-guide/SKILL.md +487 -0
  12. package/skills/caching/SKILL.md +357 -25
  13. package/skills/comparison/SKILL.md +50 -0
  14. package/skills/comparison/agents/openai.yaml +4 -0
  15. package/skills/comparison/references/framework-comparison.md +837 -0
  16. package/skills/composability/SKILL.md +246 -0
  17. package/skills/css/SKILL.md +76 -0
  18. package/skills/debug-manifest/SKILL.md +16 -10
  19. package/skills/document-cache/SKILL.md +87 -62
  20. package/skills/fonts/SKILL.md +6 -4
  21. package/skills/handler-use/SKILL.md +364 -0
  22. package/skills/hooks/SKILL.md +557 -79
  23. package/skills/host-router/SKILL.md +320 -0
  24. package/skills/i18n/SKILL.md +276 -0
  25. package/skills/intercept/SKILL.md +207 -15
  26. package/skills/layout/SKILL.md +146 -6
  27. package/skills/links/SKILL.md +304 -25
  28. package/skills/loader/SKILL.md +616 -54
  29. package/skills/middleware/SKILL.md +217 -37
  30. package/skills/migrate-nextjs/SKILL.md +611 -0
  31. package/skills/migrate-react-router/SKILL.md +927 -0
  32. package/skills/mime-routes/SKILL.md +42 -11
  33. package/skills/observability/SKILL.md +194 -0
  34. package/skills/parallel/SKILL.md +284 -3
  35. package/skills/ppr/SKILL.md +293 -0
  36. package/skills/prerender/SKILL.md +437 -52
  37. package/skills/rango/SKILL.md +369 -22
  38. package/skills/react-compiler/SKILL.md +168 -0
  39. package/skills/response-routes/SKILL.md +263 -121
  40. package/skills/route/SKILL.md +350 -21
  41. package/skills/router-setup/SKILL.md +246 -33
  42. package/skills/scripts/SKILL.md +179 -0
  43. package/skills/server-actions/SKILL.md +775 -0
  44. package/skills/shell-manifest/SKILL.md +185 -0
  45. package/skills/streams-and-websockets/SKILL.md +283 -0
  46. package/skills/tailwind/SKILL.md +27 -3
  47. package/skills/testing/SKILL.md +126 -222
  48. package/skills/testing/bindings.md +103 -0
  49. package/skills/testing/cache-prerender.md +127 -0
  50. package/skills/testing/client-components.md +124 -0
  51. package/skills/testing/e2e-parity.md +125 -0
  52. package/skills/testing/flight.md +91 -0
  53. package/skills/testing/handles.md +131 -0
  54. package/skills/testing/loader.md +128 -0
  55. package/skills/testing/middleware.md +99 -0
  56. package/skills/testing/render-handler.md +122 -0
  57. package/skills/testing/response-routes.md +95 -0
  58. package/skills/testing/reverse-and-types.md +85 -0
  59. package/skills/testing/server-actions.md +107 -0
  60. package/skills/testing/server-tree.md +128 -0
  61. package/skills/testing/setup.md +123 -0
  62. package/skills/theme/SKILL.md +9 -8
  63. package/skills/typesafety/SKILL.md +532 -103
  64. package/skills/use-cache/SKILL.md +367 -0
  65. package/skills/vercel/SKILL.md +128 -0
  66. package/skills/view-transitions/SKILL.md +337 -0
  67. package/src/__augment-tests__/augment.ts +81 -0
  68. package/src/__augment-tests__/augmented.check.ts +116 -0
  69. package/src/__internal.ts +77 -44
  70. package/src/bin/rango.ts +312 -15
  71. package/src/browser/action-coordinator.ts +114 -0
  72. package/src/browser/action-fence.ts +47 -0
  73. package/src/browser/app-shell.ts +39 -0
  74. package/src/browser/app-version.ts +14 -0
  75. package/src/browser/connection-warmup.ts +134 -0
  76. package/src/browser/cookie-name.ts +140 -0
  77. package/src/browser/event-controller.ts +293 -202
  78. package/src/browser/history-state.ts +101 -0
  79. package/src/browser/index.ts +3 -3
  80. package/src/browser/intercept-utils.ts +52 -0
  81. package/src/browser/invalidate-client-cache.ts +52 -0
  82. package/src/browser/link-interceptor.ts +24 -4
  83. package/src/browser/logging.ts +11 -0
  84. package/src/browser/merge-segment-loaders.ts +20 -12
  85. package/src/browser/navigation-bridge.ts +385 -576
  86. package/src/browser/navigation-client.ts +245 -75
  87. package/src/browser/navigation-store-handle.ts +38 -0
  88. package/src/browser/navigation-store.ts +184 -118
  89. package/src/browser/navigation-transaction.ts +247 -0
  90. package/src/browser/network-error-handler.ts +88 -0
  91. package/src/browser/partial-update.ts +412 -364
  92. package/src/browser/prefetch/cache.ts +359 -0
  93. package/src/browser/prefetch/fetch.ts +452 -0
  94. package/src/browser/prefetch/observer.ts +65 -0
  95. package/src/browser/prefetch/policy.ts +48 -0
  96. package/src/browser/prefetch/queue.ts +209 -0
  97. package/src/browser/prefetch/resource-ready.ts +77 -0
  98. package/src/browser/rango-state.ts +194 -0
  99. package/src/browser/react/Link.tsx +275 -68
  100. package/src/browser/react/NavigationProvider.tsx +265 -109
  101. package/src/browser/react/ScrollRestoration.tsx +10 -6
  102. package/src/browser/react/context.ts +11 -0
  103. package/src/browser/react/filter-segment-order.ts +70 -0
  104. package/src/browser/react/index.ts +0 -48
  105. package/src/browser/react/location-state-shared.ts +272 -60
  106. package/src/browser/react/location-state.ts +90 -20
  107. package/src/browser/react/mount-context.ts +6 -1
  108. package/src/browser/react/nonce-context.ts +23 -0
  109. package/src/browser/react/shallow-equal.ts +27 -0
  110. package/src/browser/react/use-action.ts +35 -66
  111. package/src/browser/react/use-handle.ts +39 -126
  112. package/src/browser/react/use-href.tsx +8 -1
  113. package/src/browser/react/use-link-status.ts +39 -13
  114. package/src/browser/react/use-navigation.ts +53 -69
  115. package/src/browser/react/use-params.ts +75 -0
  116. package/src/browser/react/use-pathname.ts +47 -0
  117. package/src/browser/react/use-reverse.ts +106 -0
  118. package/src/browser/react/use-router.ts +98 -0
  119. package/src/browser/react/use-search-params.ts +51 -0
  120. package/src/browser/react/use-segments.ts +72 -99
  121. package/src/browser/response-adapter.ts +164 -0
  122. package/src/browser/rsc-router.tsx +300 -72
  123. package/src/browser/scroll-restoration.ts +138 -50
  124. package/src/browser/segment-reconciler.ts +243 -0
  125. package/src/browser/segment-structure-assert.ts +17 -1
  126. package/src/browser/server-action-bridge.ts +668 -613
  127. package/src/browser/types.ts +223 -51
  128. package/src/browser/validate-redirect-origin.ts +56 -0
  129. package/src/build/collect-fallback-refs.ts +107 -0
  130. package/src/build/generate-manifest.ts +252 -161
  131. package/src/build/generate-route-types.ts +41 -1038
  132. package/src/build/index.ts +12 -7
  133. package/src/build/prefix-tree-utils.ts +123 -0
  134. package/src/build/route-trie.ts +225 -42
  135. package/src/build/route-types/ast-helpers.ts +25 -0
  136. package/src/build/route-types/ast-route-extraction.ts +105 -0
  137. package/src/build/route-types/codegen.ts +113 -0
  138. package/src/build/route-types/include-resolution.ts +812 -0
  139. package/src/build/route-types/param-extraction.ts +51 -0
  140. package/src/build/route-types/per-module-writer.ts +144 -0
  141. package/src/build/route-types/router-processing.ts +695 -0
  142. package/src/build/route-types/scan-filter.ts +85 -0
  143. package/src/build/route-types/source-scan.ts +216 -0
  144. package/src/build/runtime-discovery.ts +223 -0
  145. package/src/cache/background-task.ts +34 -0
  146. package/src/cache/cache-error.ts +104 -0
  147. package/src/cache/cache-key-utils.ts +60 -0
  148. package/src/cache/cache-policy.ts +199 -0
  149. package/src/cache/cache-runtime.ts +525 -0
  150. package/src/cache/cache-scope.ts +298 -332
  151. package/src/cache/cache-tag.ts +103 -0
  152. package/src/cache/cf/cf-base64.ts +33 -0
  153. package/src/cache/cf/cf-cache-constants.ts +127 -0
  154. package/src/cache/cf/cf-cache-store.ts +2508 -158
  155. package/src/cache/cf/cf-cache-types.ts +349 -0
  156. package/src/cache/cf/cf-kv-utils.ts +46 -0
  157. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  158. package/src/cache/cf/index.ts +17 -17
  159. package/src/cache/document-cache.ts +199 -92
  160. package/src/cache/handle-capture.ts +81 -0
  161. package/src/cache/handle-snapshot.ts +111 -0
  162. package/src/cache/index.ts +24 -35
  163. package/src/cache/memory-segment-store.ts +363 -30
  164. package/src/cache/profile-registry.ts +88 -0
  165. package/src/cache/read-through-swr.ts +178 -0
  166. package/src/cache/segment-codec.ts +248 -0
  167. package/src/cache/shell-snapshot.ts +368 -0
  168. package/src/cache/tag-invalidation.ts +230 -0
  169. package/src/cache/taint.ts +153 -0
  170. package/src/cache/types.ts +222 -211
  171. package/src/cache/vercel/index.ts +11 -0
  172. package/src/cache/vercel/vercel-cache-store.ts +1113 -0
  173. package/src/client.rsc.tsx +43 -21
  174. package/src/client.tsx +131 -347
  175. package/src/cloudflare/index.ts +11 -0
  176. package/src/cloudflare/tracing.ts +109 -0
  177. package/src/component-utils.ts +23 -4
  178. package/src/components/DefaultDocument.tsx +13 -3
  179. package/src/context-var.ts +168 -0
  180. package/src/debug.ts +19 -9
  181. package/src/decode-loader-results.ts +52 -0
  182. package/src/defer.ts +185 -0
  183. package/src/deps/ssr.ts +0 -1
  184. package/src/encode-kv.ts +49 -0
  185. package/src/errors.ts +106 -10
  186. package/src/escape-script.ts +52 -0
  187. package/src/handle.ts +110 -35
  188. package/src/handles/MetaTags.tsx +83 -59
  189. package/src/handles/Scripts.tsx +183 -0
  190. package/src/handles/breadcrumbs.ts +93 -0
  191. package/src/handles/deferred-resolution.ts +127 -0
  192. package/src/handles/is-thenable.ts +18 -0
  193. package/src/handles/meta.ts +44 -53
  194. package/src/handles/script.ts +244 -0
  195. package/src/host/cookie-handler.ts +20 -65
  196. package/src/host/errors.ts +21 -30
  197. package/src/host/index.ts +13 -9
  198. package/src/host/pattern-matcher.ts +50 -79
  199. package/src/host/router.ts +151 -121
  200. package/src/host/testing.ts +45 -32
  201. package/src/host/types.ts +52 -11
  202. package/src/host/utils.ts +2 -2
  203. package/src/href-client.ts +192 -57
  204. package/src/index.rsc.ts +173 -35
  205. package/src/index.ts +241 -73
  206. package/src/internal-debug.ts +9 -2
  207. package/src/loader-store.ts +500 -0
  208. package/src/loader.rsc.ts +31 -99
  209. package/src/loader.ts +30 -12
  210. package/src/missing-id-error.ts +68 -0
  211. package/src/outlet-context.ts +1 -1
  212. package/src/outlet-provider.tsx +41 -0
  213. package/src/prerender/param-hash.ts +16 -14
  214. package/src/prerender/store.ts +121 -21
  215. package/src/prerender.ts +460 -26
  216. package/src/redirect-origin.ts +100 -0
  217. package/src/regex-escape.ts +8 -0
  218. package/src/render-error-thrower.tsx +20 -0
  219. package/src/response-utils.ts +62 -0
  220. package/src/reverse.ts +198 -128
  221. package/src/root-error-boundary.tsx +42 -48
  222. package/src/route-content-wrapper.tsx +22 -77
  223. package/src/route-definition/dsl-helpers.ts +1116 -0
  224. package/src/route-definition/helper-factories.ts +88 -0
  225. package/src/route-definition/helpers-types.ts +505 -0
  226. package/src/route-definition/index.ts +54 -0
  227. package/src/route-definition/redirect.ts +134 -0
  228. package/src/route-definition/resolve-handler-use.ts +160 -0
  229. package/src/route-definition/use-item-types.ts +29 -0
  230. package/src/route-definition.ts +1 -1481
  231. package/src/route-map-builder.ts +82 -144
  232. package/src/route-name.ts +53 -0
  233. package/src/route-types.ts +71 -45
  234. package/src/router/basename.ts +14 -0
  235. package/src/router/content-negotiation.ts +263 -0
  236. package/src/router/debug-manifest.ts +72 -0
  237. package/src/router/error-handling.ts +54 -27
  238. package/src/router/find-match.ts +245 -0
  239. package/src/router/handler-context.ts +377 -125
  240. package/src/router/instrument.ts +350 -0
  241. package/src/router/intercept-resolution.ts +59 -28
  242. package/src/router/lazy-includes.ts +254 -0
  243. package/src/router/loader-resolution.ts +421 -157
  244. package/src/router/logging.ts +106 -6
  245. package/src/router/manifest.ts +131 -57
  246. package/src/router/match-api.ts +167 -246
  247. package/src/router/match-context.ts +4 -24
  248. package/src/router/match-handlers.ts +440 -0
  249. package/src/router/match-middleware/background-revalidation.ts +117 -93
  250. package/src/router/match-middleware/cache-lookup.ts +297 -150
  251. package/src/router/match-middleware/cache-store.ts +123 -51
  252. package/src/router/match-middleware/intercept-resolution.ts +44 -43
  253. package/src/router/match-middleware/segment-resolution.ts +64 -22
  254. package/src/router/match-pipelines.ts +11 -87
  255. package/src/router/match-result.ts +121 -50
  256. package/src/router/metrics.ts +219 -28
  257. package/src/router/middleware-types.ts +93 -0
  258. package/src/router/middleware.ts +505 -441
  259. package/src/router/navigation-snapshot.ts +133 -0
  260. package/src/router/params-util.ts +23 -0
  261. package/src/router/parse-pattern.ts +115 -0
  262. package/src/router/pattern-matching.ts +311 -142
  263. package/src/router/prefetch-cache-ttl.ts +51 -0
  264. package/src/router/prefetch-limits.ts +37 -0
  265. package/src/router/prerender-match.ts +547 -0
  266. package/src/router/preview-match.ts +102 -0
  267. package/src/router/request-classification.ts +278 -0
  268. package/src/router/revalidation.ts +203 -62
  269. package/src/router/route-snapshot.ts +246 -0
  270. package/src/router/router-context.ts +45 -48
  271. package/src/router/router-interfaces.ts +554 -0
  272. package/src/router/router-options.ts +779 -0
  273. package/src/router/router-registry.ts +21 -0
  274. package/src/router/segment-resolution/fresh.ts +772 -0
  275. package/src/router/segment-resolution/helpers.ts +348 -0
  276. package/src/router/segment-resolution/loader-cache.ts +250 -0
  277. package/src/router/segment-resolution/loader-mask.ts +44 -0
  278. package/src/router/segment-resolution/revalidation.ts +1331 -0
  279. package/src/router/segment-resolution/static-store.ts +81 -0
  280. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  281. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  282. package/src/router/segment-resolution.ts +25 -1354
  283. package/src/router/segment-wrappers.ts +292 -0
  284. package/src/router/state-cookie-name.ts +33 -0
  285. package/src/router/substitute-pattern-params.ts +75 -0
  286. package/src/router/telemetry-otel.ts +261 -0
  287. package/src/router/telemetry.ts +377 -0
  288. package/src/router/timeout.ts +128 -0
  289. package/src/router/tracing.ts +206 -0
  290. package/src/router/trie-matching.ts +240 -61
  291. package/src/router/types.ts +23 -70
  292. package/src/router/url-params.ts +57 -0
  293. package/src/router.ts +781 -2378
  294. package/src/rsc/full-payload.ts +70 -0
  295. package/src/rsc/handler-context.ts +46 -0
  296. package/src/rsc/handler.ts +905 -1142
  297. package/src/rsc/helpers.ts +275 -19
  298. package/src/rsc/index.ts +2 -25
  299. package/src/rsc/json-route-result.ts +38 -0
  300. package/src/rsc/loader-fetch.ts +305 -0
  301. package/src/rsc/manifest-init.ts +77 -0
  302. package/src/rsc/nonce.ts +14 -0
  303. package/src/rsc/origin-guard.ts +155 -0
  304. package/src/rsc/progressive-enhancement.ts +502 -0
  305. package/src/rsc/redirect-guard.ts +99 -0
  306. package/src/rsc/response-cache-serve.ts +238 -0
  307. package/src/rsc/response-error.ts +104 -0
  308. package/src/rsc/response-route-handler.ts +257 -0
  309. package/src/rsc/rsc-rendering.ts +527 -0
  310. package/src/rsc/runtime-warnings.ts +55 -0
  311. package/src/rsc/server-action.ts +522 -0
  312. package/src/rsc/shell-capture.ts +897 -0
  313. package/src/rsc/shell-serve.ts +124 -0
  314. package/src/rsc/ssr-setup.ts +144 -0
  315. package/src/rsc/transition-gate.ts +89 -0
  316. package/src/rsc/types.ts +95 -12
  317. package/src/runtime-env.ts +18 -0
  318. package/src/search-params.ts +99 -82
  319. package/src/segment-content-promise.ts +67 -0
  320. package/src/segment-loader-promise.ts +149 -0
  321. package/src/segment-system.tsx +349 -134
  322. package/src/serialize.ts +243 -0
  323. package/src/server/context.ts +459 -85
  324. package/src/server/cookie-parse.ts +32 -0
  325. package/src/server/cookie-store.ts +310 -0
  326. package/src/server/fetchable-loader-store.ts +11 -6
  327. package/src/server/handle-store.ts +123 -42
  328. package/src/server/loader-registry.ts +51 -100
  329. package/src/server/request-context.ts +848 -157
  330. package/src/server.ts +15 -8
  331. package/src/ssr/index.tsx +443 -135
  332. package/src/ssr/ssr-root.tsx +228 -0
  333. package/src/static-handler.ts +45 -18
  334. package/src/testing/cache-status.ts +162 -0
  335. package/src/testing/collect-handle.ts +46 -0
  336. package/src/testing/dispatch.ts +701 -0
  337. package/src/testing/dom.entry.ts +22 -0
  338. package/src/testing/e2e/fixture.ts +188 -0
  339. package/src/testing/e2e/index.ts +128 -0
  340. package/src/testing/e2e/matchers.ts +35 -0
  341. package/src/testing/e2e/page-helpers.ts +272 -0
  342. package/src/testing/e2e/parity.ts +387 -0
  343. package/src/testing/e2e/server.ts +195 -0
  344. package/src/testing/flight-matchers.ts +97 -0
  345. package/src/testing/flight-normalize.ts +11 -0
  346. package/src/testing/flight-runtime.d.ts +57 -0
  347. package/src/testing/flight-tree.ts +682 -0
  348. package/src/testing/flight.entry.ts +52 -0
  349. package/src/testing/flight.ts +257 -0
  350. package/src/testing/generated-routes.ts +199 -0
  351. package/src/testing/index.ts +105 -0
  352. package/src/testing/internal/context.ts +371 -0
  353. package/src/testing/internal/flight-client-globals.ts +30 -0
  354. package/src/testing/internal/seed-vars.ts +54 -0
  355. package/src/testing/render-handler.ts +357 -0
  356. package/src/testing/render-route.tsx +584 -0
  357. package/src/testing/run-loader.ts +385 -0
  358. package/src/testing/run-middleware.ts +205 -0
  359. package/src/testing/run-transition-when.ts +164 -0
  360. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  361. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  362. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  363. package/src/testing/vitest-stubs/version.ts +5 -0
  364. package/src/testing/vitest.ts +305 -0
  365. package/src/theme/ThemeProvider.tsx +76 -98
  366. package/src/theme/ThemeScript.tsx +12 -14
  367. package/src/theme/constants.ts +57 -15
  368. package/src/theme/index.ts +3 -20
  369. package/src/theme/theme-context.ts +5 -35
  370. package/src/theme/theme-script.ts +43 -39
  371. package/src/theme/use-theme.ts +0 -3
  372. package/src/types/boundaries.ts +123 -0
  373. package/src/types/cache-types.ts +207 -0
  374. package/src/types/error-types.ts +132 -0
  375. package/src/types/global-namespace.ts +113 -0
  376. package/src/types/handler-context.ts +839 -0
  377. package/src/types/index.ts +81 -0
  378. package/src/types/loader-types.ts +212 -0
  379. package/src/types/request-scope.ts +112 -0
  380. package/src/types/route-config.ts +138 -0
  381. package/src/types/route-entry.ts +114 -0
  382. package/src/types/segments.ts +271 -0
  383. package/src/types.ts +1 -1795
  384. package/src/urls/include-helper.ts +162 -0
  385. package/src/urls/include-provider.ts +71 -0
  386. package/src/urls/index.ts +44 -0
  387. package/src/urls/path-helper-types.ts +413 -0
  388. package/src/urls/path-helper.ts +280 -0
  389. package/src/urls/pattern-types.ts +160 -0
  390. package/src/urls/response-types.ts +109 -0
  391. package/src/urls/type-extraction.ts +316 -0
  392. package/src/urls/urls-function.ts +80 -0
  393. package/src/urls.ts +1 -1341
  394. package/src/use-loader.tsx +406 -141
  395. package/src/vercel/index.ts +11 -0
  396. package/src/vercel/tracing.ts +88 -0
  397. package/src/vite/debug.ts +185 -0
  398. package/src/vite/discovery/bundle-postprocess.ts +182 -0
  399. package/src/vite/discovery/discover-routers.ts +389 -0
  400. package/src/vite/discovery/discovery-errors.ts +255 -0
  401. package/src/vite/discovery/gate-state.ts +171 -0
  402. package/src/vite/discovery/prerender-collection.ts +467 -0
  403. package/src/vite/discovery/route-types-writer.ts +214 -0
  404. package/src/vite/discovery/self-gen-tracking.ts +73 -0
  405. package/src/vite/discovery/state.ts +161 -0
  406. package/src/vite/discovery/virtual-module-codegen.ts +183 -0
  407. package/src/vite/index.ts +23 -2255
  408. package/src/vite/inject-client-debug.ts +36 -0
  409. package/src/vite/plugin-types.ts +303 -0
  410. package/src/vite/plugins/cjs-to-esm.ts +90 -0
  411. package/src/vite/plugins/client-ref-dedup.ts +120 -0
  412. package/src/vite/plugins/client-ref-hashing.ts +118 -0
  413. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  414. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  415. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  416. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +88 -110
  417. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +89 -79
  418. package/src/vite/plugins/expose-ids/export-analysis.ts +363 -0
  419. package/src/vite/plugins/expose-ids/handler-transform.ts +130 -0
  420. package/src/vite/plugins/expose-ids/loader-transform.ts +64 -0
  421. package/src/vite/plugins/expose-ids/router-transform.ts +199 -0
  422. package/src/vite/plugins/expose-ids/types.ts +45 -0
  423. package/src/vite/plugins/expose-internal-ids.ts +805 -0
  424. package/src/vite/plugins/performance-tracks.ts +89 -0
  425. package/src/vite/plugins/refresh-cmd.ts +127 -0
  426. package/src/vite/plugins/use-cache-transform.ts +313 -0
  427. package/src/vite/plugins/vercel-output.ts +384 -0
  428. package/src/vite/plugins/version-injector.ts +94 -0
  429. package/src/vite/plugins/version-plugin.ts +263 -0
  430. package/src/vite/plugins/virtual-entries.ts +234 -0
  431. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  432. package/src/vite/rango.ts +560 -0
  433. package/src/vite/router-discovery.ts +1638 -0
  434. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +200 -37
  435. package/src/vite/utils/banner.ts +36 -0
  436. package/src/vite/utils/bundle-analysis.ts +132 -0
  437. package/src/vite/utils/client-chunks.ts +184 -0
  438. package/src/vite/utils/directive-prologue.ts +40 -0
  439. package/src/vite/utils/forward-user-plugins.ts +171 -0
  440. package/src/vite/utils/manifest-utils.ts +15 -0
  441. package/src/vite/utils/package-resolution.ts +89 -0
  442. package/src/vite/utils/prerender-utils.ts +249 -0
  443. package/src/vite/utils/shared-utils.ts +269 -0
  444. package/CLAUDE.md +0 -43
  445. package/dist/vite/index.named-routes.gen.ts +0 -103
  446. package/src/browser/lru-cache.ts +0 -69
  447. package/src/browser/react/use-client-cache.ts +0 -56
  448. package/src/browser/request-controller.ts +0 -164
  449. package/src/browser/shallow.ts +0 -35
  450. package/src/cache/memory-store.ts +0 -253
  451. package/src/handles/index.ts +0 -6
  452. package/src/href-context.ts +0 -33
  453. package/src/network-error-thrower.tsx +0 -21
  454. package/src/router.gen.ts +0 -6
  455. package/src/static-handler.gen.ts +0 -5
  456. package/src/urls.gen.ts +0 -8
  457. package/src/vite/expose-internal-ids.ts +0 -1167
  458. package/src/vite/package-resolution.ts +0 -125
  459. package/src/vite/virtual-entries.ts +0 -114
  460. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
@@ -2,13 +2,21 @@
2
2
  * Search parameter schema types and runtime utilities.
3
3
  *
4
4
  * Provides a lightweight schema system for typed query parameters.
5
- * When a route defines a `search` schema, ctx.searchParams becomes
6
- * a typed object with parsed values instead of raw URLSearchParams.
5
+ * When a route defines a `search` schema, ctx.search provides a typed
6
+ * object with parsed values. ctx.searchParams always remains a standard
7
+ * URLSearchParams instance.
7
8
  */
8
9
 
9
- // ============================================================================
10
- // Schema Types
11
- // ============================================================================
10
+ import { encodeKV } from "./encode-kv.js";
11
+
12
+ /**
13
+ * Decimal-number grammar for `"number"` search params: optional sign, digits
14
+ * with optional fraction, optional exponent. Deliberately excludes hex (`0x`),
15
+ * `Infinity`, and empty/whitespace so `Number()`'s lenient coercions
16
+ * (`Number("")===0`, `Number("0x10")===16`, `Number("Infinity")===Infinity`)
17
+ * do not slip non-decimal values into typed search.
18
+ */
19
+ const DECIMAL_NUMBER_RE = /^[+-]?(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?$/;
12
20
 
13
21
  /** Supported scalar types for search params (append ? for optional). */
14
22
  export type SearchSchemaValue =
@@ -22,10 +30,6 @@ export type SearchSchemaValue =
22
30
  /** A search schema maps param names to their type descriptors. */
23
31
  export type SearchSchema = Record<string, SearchSchemaValue>;
24
32
 
25
- // ============================================================================
26
- // Type-Level Schema Resolution
27
- // ============================================================================
28
-
29
33
  /** Strip trailing `?` from a schema value to get the base type. */
30
34
  type BaseType<T extends string> = T extends `${infer B}?` ? B : T;
31
35
 
@@ -54,27 +58,37 @@ type Simplify<T> = { [K in keyof T]: T[K] };
54
58
  /**
55
59
  * Resolve a SearchSchema to its typed object.
56
60
  *
61
+ * Both required and optional params resolve to `T | undefined` at the handler
62
+ * level. The required/optional distinction is a consumer-facing contract
63
+ * (e.g., for href() and reverse() autocomplete) — it tells callers which
64
+ * params the route expects, but the handler must still check for undefined
65
+ * since the framework cannot trust the client to send all required params.
66
+ *
57
67
  * @example
58
68
  * type S = { q: "string"; page: "number?"; sort: "string?" };
59
69
  * type R = ResolveSearchSchema<S>;
60
- * // { q: string; page?: number; sort?: string }
70
+ * // { q: string | undefined; page?: number; sort?: string }
61
71
  */
62
- export type ResolveSearchSchema<T extends SearchSchema> = Simplify<{
63
- [K in RequiredKeys<T> & string]: ResolveBaseType<BaseType<T[K]>>;
64
- } & {
65
- [K in OptionalKeys<T> & string]?: ResolveBaseType<BaseType<T[K]>>;
66
- }>;
72
+ export type ResolveSearchSchema<T extends SearchSchema> = Simplify<
73
+ {
74
+ [K in RequiredKeys<T> & string]:
75
+ | ResolveBaseType<BaseType<T[K]>>
76
+ | undefined;
77
+ } & {
78
+ [K in OptionalKeys<T> & string]?: ResolveBaseType<BaseType<T[K]>>;
79
+ }
80
+ >;
67
81
 
68
82
  // ============================================================================
69
83
  // Route-Level Type Extraction
70
84
  // ============================================================================
71
85
 
72
86
  /** Resolve the global route map from RegisteredRoutes or GeneratedRouteMap. */
73
- type GlobalRouteMap = keyof RSCRouter.RegisteredRoutes extends never
74
- ? keyof RSCRouter.GeneratedRouteMap extends never
87
+ type GlobalRouteMap = keyof Rango.RegisteredRoutes extends never
88
+ ? keyof Rango.GeneratedRouteMap extends never
75
89
  ? Record<string, string>
76
- : RSCRouter.GeneratedRouteMap
77
- : RSCRouter.RegisteredRoutes;
90
+ : Rango.GeneratedRouteMap
91
+ : Rango.RegisteredRoutes;
78
92
 
79
93
  /**
80
94
  * Extract the resolved search params type for a named route.
@@ -87,19 +101,17 @@ type GlobalRouteMap = keyof RSCRouter.RegisteredRoutes extends never
87
101
  * // { q: string; page?: number }
88
102
  * ```
89
103
  */
90
- export type RouteSearchParams<
91
- TName extends string,
92
- TRouteMap = never,
93
- > = [TRouteMap] extends [never]
104
+ export type RouteSearchParams<TName extends string, TRouteMap = never> = [
105
+ TRouteMap,
106
+ ] extends [never]
94
107
  ? ExtractAndResolveSearch<GlobalRouteMap, TName>
95
108
  : ExtractAndResolveSearch<TRouteMap, TName>;
96
109
 
97
- type ExtractAndResolveSearch<TRouteMap, TName> =
98
- TName extends keyof TRouteMap
99
- ? TRouteMap[TName] extends { readonly search: infer S extends SearchSchema }
100
- ? ResolveSearchSchema<S>
101
- : {}
102
- : {};
110
+ type ExtractAndResolveSearch<TRouteMap, TName> = TName extends keyof TRouteMap
111
+ ? TRouteMap[TName] extends { readonly search: infer S extends SearchSchema }
112
+ ? ResolveSearchSchema<S>
113
+ : {}
114
+ : {};
103
115
 
104
116
  /**
105
117
  * Extract the route params type for a named route.
@@ -112,50 +124,62 @@ type ExtractAndResolveSearch<TRouteMap, TName> =
112
124
  * // { slug: string }
113
125
  * ```
114
126
  */
115
- export type RouteParams<
116
- TName extends string,
117
- TRouteMap = never,
118
- > = [TRouteMap] extends [never]
127
+ export type RouteParams<TName extends string, TRouteMap = never> = [
128
+ TRouteMap,
129
+ ] extends [never]
119
130
  ? ExtractRouteParamsFromMap<GlobalRouteMap, TName>
120
131
  : ExtractRouteParamsFromMap<TRouteMap, TName>;
121
132
 
122
- type ExtractRouteParamsFromMap<TRouteMap, TName> =
123
- TName extends keyof TRouteMap
124
- ? TRouteMap[TName] extends string
125
- ? ExtractParamsFromPattern<TRouteMap[TName]>
126
- : TRouteMap[TName] extends { readonly path: infer P extends string }
127
- ? ExtractParamsFromPattern<P>
128
- : {}
129
- : {};
133
+ type ExtractRouteParamsFromMap<TRouteMap, TName> = TName extends keyof TRouteMap
134
+ ? TRouteMap[TName] extends string
135
+ ? ExtractParamsFromPattern<TRouteMap[TName]>
136
+ : TRouteMap[TName] extends { readonly path: infer P extends string }
137
+ ? ExtractParamsFromPattern<P>
138
+ : {}
139
+ : {};
140
+
141
+ /** Parse "a|b|c" into "a" | "b" | "c" */
142
+ type ParseConstraint<T extends string> =
143
+ T extends `${infer First}|${infer Rest}` ? First | ParseConstraint<Rest> : T;
130
144
 
131
145
  /** Minimal inline param extraction (avoids importing from types.ts to prevent circular deps). */
132
146
  type ExtractParamsFromPattern<T extends string> =
133
147
  T extends `${string}:${infer Param}/${infer Rest}`
134
- ? Param extends `${infer Name}?`
135
- ? { [K in Name]?: string } & ExtractParamsFromPattern<`/${Rest}`>
136
- : Param extends `${infer Name}(${string})`
137
- ? { [K in Name]: string } & ExtractParamsFromPattern<`/${Rest}`>
138
- : { [K in Param]: string } & ExtractParamsFromPattern<`/${Rest}`>
148
+ ? Param extends `${infer Name}(${infer C})?`
149
+ ? {
150
+ [K in Name]?: ParseConstraint<C>;
151
+ } & ExtractParamsFromPattern<`/${Rest}`>
152
+ : Param extends `${infer Name}(${infer C})`
153
+ ? {
154
+ [K in Name]: ParseConstraint<C>;
155
+ } & ExtractParamsFromPattern<`/${Rest}`>
156
+ : Param extends `${infer Name}?`
157
+ ? { [K in Name]?: string } & ExtractParamsFromPattern<`/${Rest}`>
158
+ : { [K in Param]: string } & ExtractParamsFromPattern<`/${Rest}`>
139
159
  : T extends `${string}:${infer Param}`
140
- ? Param extends `${infer Name}?`
141
- ? { [K in Name]?: string }
142
- : Param extends `${infer Name}(${string})`
143
- ? { [K in Name]: string }
144
- : { [K in Param]: string }
160
+ ? Param extends `${infer Name}(${infer C})?`
161
+ ? { [K in Name]?: ParseConstraint<C> }
162
+ : Param extends `${infer Name}(${infer C})`
163
+ ? { [K in Name]: ParseConstraint<C> }
164
+ : Param extends `${infer Name}?`
165
+ ? { [K in Name]?: string }
166
+ : { [K in Param]: string }
145
167
  : {};
146
168
 
147
- // ============================================================================
148
- // Runtime Parser
149
- // ============================================================================
150
-
151
169
  /**
152
170
  * Parse URLSearchParams into a typed object using the given schema.
153
171
  *
154
172
  * - `"string"` / `"string?"` - kept as-is
155
- * - `"number"` / `"number?"` - coerced via `Number()`; NaN treated as missing
173
+ * - `"number"` / `"number?"` - parsed as a finite decimal number. Accepts an
174
+ * optional sign, digits with optional fraction, and optional exponent
175
+ * (e.g. `42`, `-3.5`, `1e3`). Empty/whitespace-only, non-decimal forms
176
+ * (`0x10`), and non-finite (`Infinity`) are treated as missing (omitted),
177
+ * NOT coerced to `0`/`16`/`Infinity`.
156
178
  * - `"boolean"` / `"boolean?"` - `"true"` / `"1"` -> true, `"false"` / `"0"` / `""` -> false
157
179
  *
158
- * Missing required params are set to their zero value (empty string / 0 / false).
180
+ * Missing params (both required and optional) are omitted from the result
181
+ * (undefined). The required/optional distinction is a consumer-facing contract
182
+ * only — the handler must check for undefined.
159
183
  */
160
184
  export function parseSearchParams<T extends SearchSchema>(
161
185
  searchParams: URLSearchParams,
@@ -169,25 +193,24 @@ export function parseSearchParams<T extends SearchSchema>(
169
193
  const raw = searchParams.get(key);
170
194
 
171
195
  if (raw === null) {
172
- if (!isOptional) {
173
- // Required param missing: use zero value
174
- if (baseType === "string") result[key] = "";
175
- else if (baseType === "number") result[key] = 0;
176
- else if (baseType === "boolean") result[key] = false;
177
- }
178
- // Optional params are omitted (undefined)
196
+ // Missing params are omitted (undefined) regardless of required/optional
179
197
  continue;
180
198
  }
181
199
 
182
200
  if (baseType === "string") {
183
201
  result[key] = raw;
184
202
  } else if (baseType === "number") {
185
- const num = Number(raw);
186
- if (Number.isNaN(num)) {
187
- if (!isOptional) result[key] = 0;
188
- } else {
189
- result[key] = num;
203
+ // Trim, then require a valid decimal numeral and a finite result.
204
+ // Empty/whitespace, hex (0x10), and Infinity are treated as missing
205
+ // (omitted) — not coerced to 0/16/Infinity by Number()'s lenient rules.
206
+ const trimmed = raw.trim();
207
+ if (trimmed !== "" && DECIMAL_NUMBER_RE.test(trimmed)) {
208
+ const num = Number(trimmed);
209
+ if (Number.isFinite(num)) {
210
+ result[key] = num;
211
+ }
190
212
  }
213
+ // Anything else treated as missing (undefined)
191
214
  } else if (baseType === "boolean") {
192
215
  result[key] = raw === "true" || raw === "1";
193
216
  }
@@ -196,23 +219,17 @@ export function parseSearchParams<T extends SearchSchema>(
196
219
  return result as ResolveSearchSchema<T>;
197
220
  }
198
221
 
199
- // ============================================================================
200
- // Runtime Serializer
201
- // ============================================================================
202
-
203
222
  /**
204
223
  * Serialize a typed search params object to a query string (without leading `?`).
205
- * Skips `undefined` and `null` values.
224
+ * Skips `undefined` and `null` values. Preserves insertion order (no sort).
206
225
  */
207
- export function serializeSearchParams(
208
- params: Record<string, unknown>,
209
- ): string {
210
- const parts: string[] = [];
226
+ export function serializeSearchParams(params: Record<string, unknown>): string {
227
+ // Pre-filter null/undefined and coerce values to strings here so encodeKV
228
+ // (which never inspects values) reproduces this call site's exact output.
229
+ const pairs: [string, string][] = [];
211
230
  for (const [key, value] of Object.entries(params)) {
212
231
  if (value === undefined || value === null) continue;
213
- parts.push(
214
- `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`,
215
- );
232
+ pairs.push([key, String(value)]);
216
233
  }
217
- return parts.join("&");
234
+ return encodeKV(pairs);
218
235
  }
@@ -0,0 +1,67 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ /**
4
+ * Stable Promise wrappers keyed on the component itself. Objects (React
5
+ * elements, functions, lazy payloads) land in a WeakMap so entries GC when
6
+ * the underlying component is released; primitives (string, number, boolean,
7
+ * null) land in a Map so memoization still applies to text-/null-backed
8
+ * segments like those in partial-update flows. Keeping this cache outside
9
+ * the segment eliminates preservation fields on ResolvedSegment — it survives
10
+ * reconciliation naturally because the component ref is what's stable.
11
+ *
12
+ * Browser-only. On the server each SSR render needs a fresh pending promise
13
+ * so Suspense can emit the loading fallback HTML before content streams. A
14
+ * shared already-resolved promise has `.status === "fulfilled"` attached by
15
+ * React on its first observation — subsequent `use()` calls return
16
+ * synchronously without suspending, so the Suspense fallback never makes it
17
+ * into the initial HTML. Route-definition components share refs across
18
+ * requests, so a global cache would leak tracked state between renders.
19
+ */
20
+ const IS_BROWSER = typeof window !== "undefined";
21
+ const objectContentCache = IS_BROWSER
22
+ ? new WeakMap<object, Promise<ReactNode>>()
23
+ : null;
24
+ const primitiveContentCache = IS_BROWSER
25
+ ? new Map<unknown, Promise<ReactNode>>()
26
+ : null;
27
+
28
+ /**
29
+ * Return a stable Promise wrapping `component`, memoized on the component ref.
30
+ *
31
+ * A fresh `Promise.resolve(component)` each render would suspend for one
32
+ * microtask and briefly commit the loading fallback inside Suspender — the
33
+ * intercept / parallel-slot flicker this indirection prevents. Reusing the
34
+ * same Promise ref keeps React's `use()` in "known fulfilled" state after
35
+ * the first observation.
36
+ *
37
+ * @internal
38
+ */
39
+ export function getMemoizedContentPromise(
40
+ component: ReactNode,
41
+ ): Promise<ReactNode> {
42
+ if (component instanceof Promise) {
43
+ return component as Promise<ReactNode>;
44
+ }
45
+
46
+ if (!objectContentCache || !primitiveContentCache) {
47
+ return Promise.resolve(component);
48
+ }
49
+
50
+ if (component !== null && typeof component === "object") {
51
+ const cached = objectContentCache.get(component);
52
+ if (cached) {
53
+ return cached;
54
+ }
55
+ const promise = Promise.resolve(component);
56
+ objectContentCache.set(component, promise);
57
+ return promise;
58
+ }
59
+
60
+ const cached = primitiveContentCache.get(component);
61
+ if (cached) {
62
+ return cached;
63
+ }
64
+ const promise = Promise.resolve(component);
65
+ primitiveContentCache.set(component, promise);
66
+ return promise;
67
+ }
@@ -0,0 +1,149 @@
1
+ import type { ResolvedSegment } from "./types.js";
2
+
3
+ /**
4
+ * Cache of aggregate Promise.all results keyed on the first loader's
5
+ * `loaderData` reference. Each entry holds the source refs it was built from
6
+ * plus the resulting Promise/array; lookup scans entries for the matching
7
+ * source array (typically a single entry, since distinct loader groups rarely
8
+ * share a first source). Object first-refs live in a WeakMap (auto-GC);
9
+ * primitive first-refs (strings/numbers/booleans/null) live in a Map so
10
+ * loaders that resolve to primitive data are memoized too. The per-key array
11
+ * is capped (MAX_ENTRIES_PER_KEY, oldest evicted) so a long session under a
12
+ * stable first-ref does not grow it without bound.
13
+ *
14
+ * Keying externally means reconciliation's fresh segment objects no longer
15
+ * drop memoization — the cache survives as long as the underlying loader
16
+ * segments do, and GC collects entries when those loaders are released
17
+ * (object keys only).
18
+ *
19
+ * Browser-only. On the server each SSR render needs a fresh Promise so
20
+ * Suspense can actually suspend and emit the loading fallback HTML before
21
+ * content streams. A shared already-resolved promise has `.status` attached
22
+ * by React on first `use()`; subsequent observations return synchronously
23
+ * and skip the fallback. The zero-loader case is especially prone because
24
+ * every empty-loader site would otherwise share one promise across requests.
25
+ */
26
+ const IS_BROWSER = typeof window !== "undefined";
27
+
28
+ interface LoaderCacheEntry {
29
+ sources: any[];
30
+ // buildLoaderPromise always returns a Promise, so the cached value is never a
31
+ // bare array. The public getMemoizedLoaderPromise return type stays broader
32
+ // (Promise<any[]> | any[]) to mirror its siblings.
33
+ promise: Promise<any[]>;
34
+ }
35
+
36
+ // Cap the per-key entries array. A stable first-ref (e.g. a layout loader whose
37
+ // loaderData object survives reconciliation across navigations) keeps its
38
+ // WeakMap/Map key alive, while a per-route loader whose ref changes each
39
+ // navigation appends a brand-new sources array under that same live key on
40
+ // every navigation. Nothing was ever removed, so the array grew linearly with
41
+ // navigation count, pinning each stale Promise + sources array from GC — a
42
+ // steady client-side leak over a long session. Only the current render's combo
43
+ // needs to stay warm; evict the oldest beyond the cap.
44
+ const MAX_ENTRIES_PER_KEY = 8;
45
+
46
+ const objectLoaderCache = IS_BROWSER
47
+ ? new WeakMap<object, LoaderCacheEntry[]>()
48
+ : null;
49
+ const primitiveLoaderCache = IS_BROWSER
50
+ ? new Map<unknown, LoaderCacheEntry[]>()
51
+ : null;
52
+
53
+ // In the browser, a single shared empty aggregate is safe (and desirable) —
54
+ // reusing the same resolved promise keeps React's `use()` in a known-fulfilled
55
+ // state across renders. On the server it would leak `.status = "fulfilled"`
56
+ // across requests and skip the Suspense fallback, so we rebuild on each call.
57
+ const SHARED_EMPTY_LOADER_PROMISE: Promise<any[]> | null = IS_BROWSER
58
+ ? Promise.resolve([])
59
+ : null;
60
+
61
+ function hasSameReferences(a: any[], b: any[]): boolean {
62
+ if (a.length !== b.length) {
63
+ return false;
64
+ }
65
+ for (let i = 0; i < a.length; i++) {
66
+ if (a[i] !== b[i]) {
67
+ return false;
68
+ }
69
+ }
70
+ return true;
71
+ }
72
+
73
+ /**
74
+ * Build a fresh aggregate Promise.all over the loaders' resolved data refs.
75
+ * Unlike getMemoizedLoaderPromise this never caches, so each call yields a new
76
+ * Promise — correct for sites that await the result immediately (a shared,
77
+ * already-resolved promise would leak React's `.status` across server requests
78
+ * and skip the Suspense fallback).
79
+ *
80
+ * @internal
81
+ */
82
+ export function buildLoaderPromise(loaders: ResolvedSegment[]): Promise<any[]> {
83
+ if (loaders.length === 0) {
84
+ return Promise.resolve([]);
85
+ }
86
+ return Promise.all(
87
+ loaders.map((loader) =>
88
+ loader.loaderData instanceof Promise
89
+ ? loader.loaderData
90
+ : Promise.resolve(loader.loaderData),
91
+ ),
92
+ );
93
+ }
94
+
95
+ function isObjectLike(value: unknown): value is object {
96
+ return (
97
+ value !== null && (typeof value === "object" || typeof value === "function")
98
+ );
99
+ }
100
+
101
+ /**
102
+ * Memoize an aggregate Promise.all for a set of loader segments. Reusing the
103
+ * same aggregate across renders — invalidated only when any underlying
104
+ * loader.loaderData ref changes — keeps React's `use()` in "known fulfilled"
105
+ * state and prevents a fresh Promise.all from suspending (and briefly
106
+ * committing the Suspense fallback) on every partial update that doesn't
107
+ * actually change loader data.
108
+ *
109
+ * @internal
110
+ */
111
+ export function getMemoizedLoaderPromise(
112
+ loaders: ResolvedSegment[],
113
+ ): Promise<any[]> | any[] {
114
+ if (loaders.length === 0) {
115
+ return SHARED_EMPTY_LOADER_PROMISE ?? buildLoaderPromise(loaders);
116
+ }
117
+ if (!objectLoaderCache || !primitiveLoaderCache) {
118
+ return buildLoaderPromise(loaders);
119
+ }
120
+
121
+ const sources = loaders.map((loader) => loader.loaderData);
122
+ const first = sources[0];
123
+ const entries = isObjectLike(first)
124
+ ? objectLoaderCache.get(first)
125
+ : primitiveLoaderCache.get(first);
126
+
127
+ if (entries) {
128
+ for (const entry of entries) {
129
+ if (hasSameReferences(entry.sources, sources)) {
130
+ return entry.promise;
131
+ }
132
+ }
133
+ }
134
+
135
+ const promise = buildLoaderPromise(loaders);
136
+ const newEntry: LoaderCacheEntry = { sources, promise };
137
+ if (entries) {
138
+ // Bound the array: drop the oldest entry before appending when at the cap.
139
+ if (entries.length >= MAX_ENTRIES_PER_KEY) {
140
+ entries.shift();
141
+ }
142
+ entries.push(newEntry);
143
+ } else if (isObjectLike(first)) {
144
+ objectLoaderCache.set(first, [newEntry]);
145
+ } else {
146
+ primitiveLoaderCache.set(first, [newEntry]);
147
+ }
148
+ return promise;
149
+ }