@rangojs/router 0.0.0-experimental.ea6d5eec → 0.0.0-experimental.ede38110

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 (142) hide show
  1. package/README.md +76 -18
  2. package/dist/bin/rango.js +130 -47
  3. package/dist/vite/index.js +719 -240
  4. package/package.json +3 -3
  5. package/skills/cache-guide/SKILL.md +32 -0
  6. package/skills/caching/SKILL.md +8 -0
  7. package/skills/handler-use/SKILL.md +362 -0
  8. package/skills/intercept/SKILL.md +20 -0
  9. package/skills/layout/SKILL.md +22 -0
  10. package/skills/links/SKILL.md +3 -1
  11. package/skills/loader/SKILL.md +53 -43
  12. package/skills/middleware/SKILL.md +34 -3
  13. package/skills/migrate-nextjs/SKILL.md +560 -0
  14. package/skills/migrate-react-router/SKILL.md +764 -0
  15. package/skills/parallel/SKILL.md +185 -0
  16. package/skills/prerender/SKILL.md +110 -68
  17. package/skills/rango/SKILL.md +24 -22
  18. package/skills/route/SKILL.md +55 -0
  19. package/skills/router-setup/SKILL.md +87 -2
  20. package/skills/typesafety/SKILL.md +10 -0
  21. package/src/__internal.ts +1 -1
  22. package/src/browser/app-version.ts +14 -0
  23. package/src/browser/event-controller.ts +5 -0
  24. package/src/browser/navigation-bridge.ts +37 -5
  25. package/src/browser/navigation-client.ts +107 -75
  26. package/src/browser/navigation-store.ts +43 -8
  27. package/src/browser/partial-update.ts +51 -6
  28. package/src/browser/prefetch/cache.ts +22 -12
  29. package/src/browser/prefetch/fetch.ts +81 -20
  30. package/src/browser/prefetch/queue.ts +61 -29
  31. package/src/browser/prefetch/resource-ready.ts +77 -0
  32. package/src/browser/react/Link.tsx +67 -8
  33. package/src/browser/react/NavigationProvider.tsx +13 -4
  34. package/src/browser/react/context.ts +7 -2
  35. package/src/browser/react/use-handle.ts +9 -58
  36. package/src/browser/react/use-navigation.ts +11 -10
  37. package/src/browser/react/use-router.ts +21 -8
  38. package/src/browser/rsc-router.tsx +45 -3
  39. package/src/browser/scroll-restoration.ts +10 -8
  40. package/src/browser/segment-reconciler.ts +36 -9
  41. package/src/browser/server-action-bridge.ts +8 -6
  42. package/src/browser/types.ts +27 -5
  43. package/src/build/generate-manifest.ts +6 -6
  44. package/src/build/generate-route-types.ts +3 -0
  45. package/src/build/route-trie.ts +50 -24
  46. package/src/build/route-types/include-resolution.ts +8 -1
  47. package/src/build/route-types/router-processing.ts +211 -72
  48. package/src/build/route-types/scan-filter.ts +8 -1
  49. package/src/cache/cache-runtime.ts +15 -11
  50. package/src/cache/cache-scope.ts +46 -5
  51. package/src/cache/document-cache.ts +17 -7
  52. package/src/cache/taint.ts +55 -0
  53. package/src/client.tsx +84 -230
  54. package/src/context-var.ts +72 -2
  55. package/src/debug.ts +2 -2
  56. package/src/handle.ts +40 -0
  57. package/src/index.rsc.ts +3 -1
  58. package/src/index.ts +46 -6
  59. package/src/prerender/store.ts +5 -4
  60. package/src/prerender.ts +138 -77
  61. package/src/reverse.ts +25 -1
  62. package/src/route-definition/dsl-helpers.ts +224 -37
  63. package/src/route-definition/helpers-types.ts +67 -19
  64. package/src/route-definition/index.ts +3 -0
  65. package/src/route-definition/redirect.ts +9 -1
  66. package/src/route-definition/resolve-handler-use.ts +149 -0
  67. package/src/route-types.ts +18 -0
  68. package/src/router/content-negotiation.ts +100 -1
  69. package/src/router/handler-context.ts +82 -23
  70. package/src/router/intercept-resolution.ts +9 -4
  71. package/src/router/lazy-includes.ts +7 -6
  72. package/src/router/loader-resolution.ts +156 -21
  73. package/src/router/logging.ts +1 -1
  74. package/src/router/manifest.ts +28 -15
  75. package/src/router/match-api.ts +124 -189
  76. package/src/router/match-middleware/background-revalidation.ts +30 -2
  77. package/src/router/match-middleware/cache-lookup.ts +94 -17
  78. package/src/router/match-middleware/cache-store.ts +53 -10
  79. package/src/router/match-middleware/intercept-resolution.ts +9 -7
  80. package/src/router/match-middleware/segment-resolution.ts +60 -5
  81. package/src/router/match-result.ts +104 -10
  82. package/src/router/metrics.ts +6 -1
  83. package/src/router/middleware-types.ts +6 -8
  84. package/src/router/middleware.ts +2 -5
  85. package/src/router/navigation-snapshot.ts +182 -0
  86. package/src/router/prerender-match.ts +110 -10
  87. package/src/router/preview-match.ts +30 -102
  88. package/src/router/request-classification.ts +310 -0
  89. package/src/router/route-snapshot.ts +245 -0
  90. package/src/router/router-context.ts +1 -0
  91. package/src/router/router-interfaces.ts +36 -4
  92. package/src/router/router-options.ts +37 -11
  93. package/src/router/segment-resolution/fresh.ts +198 -20
  94. package/src/router/segment-resolution/helpers.ts +29 -24
  95. package/src/router/segment-resolution/loader-cache.ts +1 -0
  96. package/src/router/segment-resolution/revalidation.ts +433 -296
  97. package/src/router/types.ts +1 -0
  98. package/src/router.ts +55 -6
  99. package/src/rsc/handler.ts +472 -372
  100. package/src/rsc/loader-fetch.ts +23 -3
  101. package/src/rsc/manifest-init.ts +5 -1
  102. package/src/rsc/progressive-enhancement.ts +14 -2
  103. package/src/rsc/rsc-rendering.ts +10 -1
  104. package/src/rsc/server-action.ts +8 -0
  105. package/src/rsc/ssr-setup.ts +2 -2
  106. package/src/rsc/types.ts +9 -1
  107. package/src/segment-content-promise.ts +67 -0
  108. package/src/segment-loader-promise.ts +122 -0
  109. package/src/segment-system.tsx +109 -23
  110. package/src/server/context.ts +166 -17
  111. package/src/server/handle-store.ts +19 -0
  112. package/src/server/loader-registry.ts +9 -8
  113. package/src/server/request-context.ts +175 -15
  114. package/src/ssr/index.tsx +4 -0
  115. package/src/static-handler.ts +18 -6
  116. package/src/types/cache-types.ts +4 -4
  117. package/src/types/handler-context.ts +137 -33
  118. package/src/types/loader-types.ts +36 -9
  119. package/src/types/route-entry.ts +12 -1
  120. package/src/types/segments.ts +2 -0
  121. package/src/urls/include-helper.ts +24 -14
  122. package/src/urls/path-helper-types.ts +39 -6
  123. package/src/urls/path-helper.ts +48 -13
  124. package/src/urls/pattern-types.ts +12 -0
  125. package/src/urls/response-types.ts +16 -6
  126. package/src/use-loader.tsx +77 -5
  127. package/src/vite/discovery/bundle-postprocess.ts +30 -33
  128. package/src/vite/discovery/discover-routers.ts +5 -1
  129. package/src/vite/discovery/prerender-collection.ts +128 -74
  130. package/src/vite/discovery/state.ts +13 -4
  131. package/src/vite/index.ts +4 -0
  132. package/src/vite/plugin-types.ts +60 -5
  133. package/src/vite/plugins/expose-id-utils.ts +12 -0
  134. package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
  135. package/src/vite/plugins/expose-internal-ids.ts +257 -40
  136. package/src/vite/plugins/performance-tracks.ts +88 -0
  137. package/src/vite/plugins/refresh-cmd.ts +88 -26
  138. package/src/vite/rango.ts +19 -2
  139. package/src/vite/router-discovery.ts +178 -37
  140. package/src/vite/utils/banner.ts +3 -3
  141. package/src/vite/utils/prerender-utils.ts +37 -5
  142. package/src/vite/utils/shared-utils.ts +3 -2
@@ -29,6 +29,7 @@ import {
29
29
  } from "./response-adapter.js";
30
30
  import { mergeLocationState } from "./history-state.js";
31
31
  import { classifyActionOutcome } from "./action-coordinator.js";
32
+ import { getAppVersion } from "./app-version.js";
32
33
 
33
34
  // Polyfill Symbol.dispose/asyncDispose for Safari and older browsers
34
35
  if (typeof Symbol.dispose === "undefined") {
@@ -43,8 +44,6 @@ if (typeof Symbol.asyncDispose === "undefined") {
43
44
  */
44
45
  export interface ServerActionBridgeConfigWithController extends ServerActionBridgeConfig {
45
46
  eventController: EventController;
46
- /** RSC version from initial payload metadata */
47
- version?: string;
48
47
  /** Callback to trigger SPA navigation (for action redirects) */
49
48
  onNavigate?: (
50
49
  url: string,
@@ -75,7 +74,6 @@ export function createServerActionBridge(
75
74
  deps,
76
75
  onUpdate,
77
76
  renderSegments,
78
- version,
79
77
  onNavigate,
80
78
  } = config;
81
79
 
@@ -86,7 +84,7 @@ export function createServerActionBridge(
86
84
  client,
87
85
  onUpdate,
88
86
  renderSegments,
89
- version,
87
+ getVersion: getAppVersion,
90
88
  });
91
89
 
92
90
  /**
@@ -165,9 +163,15 @@ export function createServerActionBridge(
165
163
  segmentState.currentSegmentIds.join(","),
166
164
  );
167
165
  // Add version param for version mismatch detection
166
+ const version = getAppVersion();
168
167
  if (version) {
169
168
  url.searchParams.set("_rsc_v", version);
170
169
  }
170
+ // Add router ID for app switch detection
171
+ const rid = store.getRouterId?.();
172
+ if (rid) {
173
+ url.searchParams.set("_rsc_rid", rid);
174
+ }
171
175
 
172
176
  // Encode arguments
173
177
  const encodedBody = await deps.encodeReply(args, { temporaryReferences });
@@ -206,7 +210,6 @@ export function createServerActionBridge(
206
210
  "rsc-action": id,
207
211
  "X-RSC-Router-Client-Path": segmentState.currentUrl,
208
212
  ...(tx && { "X-RSC-Router-Request-Id": tx.requestId }),
209
- // Send intercept source URL so server can maintain intercept context
210
213
  ...(interceptSourceUrl && {
211
214
  "X-RSC-Router-Intercept-Source": interceptSourceUrl,
212
215
  }),
@@ -309,7 +312,6 @@ export function createServerActionBridge(
309
312
  matchedCount: payload.metadata?.matched?.length ?? 0,
310
313
  diffCount: payload.metadata?.diff?.length ?? 0,
311
314
  });
312
-
313
315
  // Guard: if the action was aborted while streaming (e.g., user navigated
314
316
  // away or abortAllActions fired), bail out before any reconcile/render/cache
315
317
  // writes to avoid overwriting the current UI with stale action results.
@@ -32,6 +32,9 @@ export type HandleData = Record<string, Record<string, unknown[]>>;
32
32
  export interface RscMetadata {
33
33
  pathname: string;
34
34
  segments: ResolvedSegment[];
35
+ /** Router instance ID. When this changes between navigations, the client
36
+ * forces a full tree replacement (app switch via host router). */
37
+ routerId?: string;
35
38
  isPartial?: boolean;
36
39
  isError?: boolean;
37
40
  matched?: string[];
@@ -70,6 +73,8 @@ export interface RscMetadata {
70
73
  * Included when theme is enabled in router config.
71
74
  */
72
75
  initialTheme?: Theme;
76
+ /** URL prefix for all routes (from createRouter({ basename })). */
77
+ basename?: string;
73
78
  /** Whether connection warmup is enabled */
74
79
  warmupEnabled?: boolean;
75
80
  /** Server-side redirect with optional state (for partial requests) */
@@ -341,7 +346,13 @@ export type ReadonlyURLSearchParams = Omit<
341
346
  export interface RscBrowserDependencies {
342
347
  createFromFetch: <T>(
343
348
  response: Promise<Response>,
344
- options?: { temporaryReferences?: any },
349
+ options?: {
350
+ temporaryReferences?: any;
351
+ findSourceMapURL?: (
352
+ filename: string,
353
+ environmentName: string,
354
+ ) => string | null;
355
+ },
345
356
  ) => Promise<T>;
346
357
  createFromReadableStream: <T>(stream: ReadableStream) => Promise<T>;
347
358
  encodeReply: (
@@ -403,10 +414,13 @@ export interface NavigationStore {
403
414
  segments: ResolvedSegment[],
404
415
  handleData?: HandleData,
405
416
  ): void;
406
- getCachedSegments(
407
- historyKey: string,
408
- ):
409
- | { segments: ResolvedSegment[]; stale: boolean; handleData?: HandleData }
417
+ getCachedSegments(historyKey: string):
418
+ | {
419
+ segments: ResolvedSegment[];
420
+ stale: boolean;
421
+ handleData?: HandleData;
422
+ routerId?: string;
423
+ }
410
424
  | undefined;
411
425
  hasHistoryCache(historyKey: string): boolean;
412
426
  updateCacheHandleData(historyKey: string, handleData: HandleData): void;
@@ -422,6 +436,10 @@ export interface NavigationStore {
422
436
  getInterceptSourceUrl(): string | null;
423
437
  setInterceptSourceUrl(url: string | null): void;
424
438
 
439
+ // Router identity tracking (for cross-app navigation detection)
440
+ getRouterId?(): string | undefined;
441
+ setRouterId?(id: string): void;
442
+
425
443
  // UI update notifications
426
444
  onUpdate(callback: UpdateSubscriber): () => void;
427
445
  emitUpdate(update: NavigationUpdate): void;
@@ -452,6 +470,8 @@ export interface FetchPartialOptions {
452
470
  interceptSourceUrl?: string;
453
471
  /** RSC version for cache invalidation detection */
454
472
  version?: string;
473
+ /** Current router ID — server detects app switch and returns full response */
474
+ routerId?: string;
455
475
  /** If true, this is an HMR refetch - server should invalidate manifest cache */
456
476
  hmr?: boolean;
457
477
  }
@@ -520,6 +540,8 @@ export interface NavigationBridge {
520
540
  refresh(): Promise<void>;
521
541
  handlePopstate(): Promise<void>;
522
542
  registerLinkInterception(): () => void;
543
+ /** Update the RSC version (e.g. after HMR). Clears prefetch cache. */
544
+ updateVersion(newVersion: string): void;
523
545
  }
524
546
 
525
547
  /**
@@ -45,7 +45,7 @@ export interface GeneratedManifest {
45
45
  routeTrailingSlash?: Record<string, string>;
46
46
  /** Route names using Prerender (for dev-mode Node.js delegation) */
47
47
  prerenderRoutes?: string[];
48
- /** Route names with passthrough: true (handler kept in bundle for live fallback) */
48
+ /** Route names wrapped with Passthrough() (live handler for runtime fallback) */
49
49
  passthroughRoutes?: string[];
50
50
  /** Route name → response type for non-RSC routes */
51
51
  responseTypeRoutes?: Record<string, string>;
@@ -150,10 +150,7 @@ function buildPrefixTreeNode(
150
150
  if (prerenderDefs && entry.prerenderDef) {
151
151
  prerenderDefs[name] = entry.prerenderDef;
152
152
  }
153
- if (
154
- passthroughRoutes &&
155
- entry.prerenderDef?.options?.passthrough === true
156
- ) {
153
+ if (passthroughRoutes && entry.isPassthrough === true) {
157
154
  passthroughRoutes.push(name);
158
155
  }
159
156
  }
@@ -285,6 +282,7 @@ export function generateManifest<TEnv>(
285
282
  export function generateManifestFull<TEnv>(
286
283
  urlpatterns: UrlPatterns<TEnv, any>,
287
284
  mountIndex: number = 0,
285
+ options?: { urlPrefix?: string },
288
286
  ): FullManifest {
289
287
  const routeManifest: Record<string, string> = {};
290
288
  const routeAncestry: Record<string, string[]> = {};
@@ -310,6 +308,8 @@ export function generateManifestFull<TEnv>(
310
308
  counters: {},
311
309
  mountIndex,
312
310
  trackedIncludes, // Enable include tracking
311
+ // basename sets the initial URL prefix for all path() registrations
312
+ ...(options?.urlPrefix ? { urlPrefix: options.urlPrefix } : {}),
313
313
  },
314
314
  () => {
315
315
  const helpers = createRouteHelpers();
@@ -347,7 +347,7 @@ export function generateManifestFull<TEnv>(
347
347
  if (entry.prerenderDef) {
348
348
  prerenderDefs[name] = entry.prerenderDef;
349
349
  }
350
- if (entry.prerenderDef?.options?.passthrough === true) {
350
+ if (entry.isPassthrough === true) {
351
351
  passthroughRoutes.push(name);
352
352
  }
353
353
  }
@@ -25,6 +25,9 @@ export {
25
25
  } from "./route-types/include-resolution.js";
26
26
  export {
27
27
  extractUrlsVariableFromRouter,
28
+ extractUrlsFromRouter,
29
+ extractBasenameFromRouter,
30
+ type UrlsExtractionResult,
28
31
  buildCombinedRouteMapForRouterFile,
29
32
  detectUnresolvableIncludes,
30
33
  detectUnresolvableIncludesForUrlsFile,
@@ -98,8 +98,14 @@ export function buildRouteTrie(
98
98
  }
99
99
 
100
100
  /**
101
- * Insert a route into the trie, handling optional params by forking
102
- * the insertion path (one terminal without the param, one with).
101
+ * Insert a route into the trie. Optional params expand into two branches at
102
+ * registration time (skip-first, then present), so each terminal lives at the
103
+ * correct depth for its number of bound params and carries a branch-local
104
+ * `pa` listing only those names. The trie's single-slot `node.p` is reused
105
+ * across branches because matching ignores `node.p.n` — the leaf's `pa` is
106
+ * the source of truth for naming. Skip-first ordering lets `mergeLeaf`'s
107
+ * last-wins rule produce greedy-leftmost semantics for free at any shared
108
+ * terminal depth.
103
109
  */
104
110
  function insertRoute(
105
111
  node: TrieNode,
@@ -107,14 +113,13 @@ function insertRoute(
107
113
  index: number,
108
114
  leaf: Omit<TrieLeaf, "op" | "cv" | "pa">,
109
115
  ): void {
110
- // Collect param names, optional param names, and constraints across all segments
111
- const paramNames: string[] = [];
116
+ // op (full optional list) and cv (full constraint map) are route-level and
117
+ // identical on every terminal, so compute them once on the shared base.
112
118
  const optionalParams: string[] = [];
113
119
  const constraints: Record<string, string[]> = {};
114
120
 
115
121
  for (const seg of segments) {
116
122
  if (seg.type === "param") {
117
- paramNames.push(seg.value);
118
123
  if (seg.optional) {
119
124
  optionalParams.push(seg.value);
120
125
  }
@@ -124,21 +129,15 @@ function insertRoute(
124
129
  }
125
130
  }
126
131
 
127
- const fullLeaf: TrieLeaf = {
132
+ const leafBase: Omit<TrieLeaf, "pa"> = {
128
133
  ...leaf,
129
- ...(paramNames.length > 0 ? { pa: paramNames } : {}),
130
134
  ...(optionalParams.length > 0 ? { op: optionalParams } : {}),
131
135
  ...(Object.keys(constraints).length > 0 ? { cv: constraints } : {}),
132
136
  };
133
137
 
134
- insertSegments(node, segments, index, fullLeaf);
138
+ insertSegments(node, segments, index, leafBase, []);
135
139
  }
136
140
 
137
- /**
138
- * Recursively insert segments into the trie.
139
- * For optional params, we add a terminal at the current node (param absent)
140
- * AND continue inserting into the param child (param present).
141
- */
142
141
  /**
143
142
  * Extract ancestry map from a built trie by visiting all leaf nodes.
144
143
  * Returns { routeName: ancestryShortCodes[] } for every route in the trie.
@@ -218,15 +217,25 @@ function mergeLeaf(node: TrieNode, leaf: TrieLeaf): void {
218
217
  node.r = mergeLeaves(node.r, leaf);
219
218
  }
220
219
 
220
+ function buildLeaf(
221
+ leafBase: Omit<TrieLeaf, "pa">,
222
+ paramNames: string[],
223
+ ): TrieLeaf {
224
+ return paramNames.length > 0
225
+ ? { ...leafBase, pa: [...paramNames] }
226
+ : { ...leafBase };
227
+ }
228
+
221
229
  function insertSegments(
222
230
  node: TrieNode,
223
231
  segments: ParsedSegment[],
224
232
  index: number,
225
- leaf: TrieLeaf,
233
+ leafBase: Omit<TrieLeaf, "pa">,
234
+ paramNames: string[],
226
235
  ): void {
227
- // Base case: all segments consumed, add terminal
236
+ // Base case: all segments consumed, add terminal with branch-local pa
228
237
  if (index >= segments.length) {
229
- mergeLeaf(node, leaf);
238
+ mergeLeaf(node, buildLeaf(leafBase, paramNames));
230
239
  return;
231
240
  }
232
241
 
@@ -235,12 +244,19 @@ function insertSegments(
235
244
  if (segment.type === "static") {
236
245
  if (!node.s) node.s = {};
237
246
  if (!node.s[segment.value]) node.s[segment.value] = {};
238
- insertSegments(node.s[segment.value], segments, index + 1, leaf);
247
+ insertSegments(
248
+ node.s[segment.value],
249
+ segments,
250
+ index + 1,
251
+ leafBase,
252
+ paramNames,
253
+ );
239
254
  } else if (segment.type === "param") {
240
255
  if (segment.optional) {
241
- // Optional param: add terminal at current node (param absent)
242
- mergeLeaf(node, leaf);
243
- // AND continue with param child (param present)
256
+ // SKIP first: continue at the same node without binding this name.
257
+ // Skip-first ordering means the present-branch's TAKE overwrites any
258
+ // shared terminal later, giving greedy-leftmost semantics.
259
+ insertSegments(node, segments, index + 1, leafBase, paramNames);
244
260
  }
245
261
  if (segment.suffix) {
246
262
  // Suffix param: keyed by suffix string (e.g., ".html")
@@ -248,16 +264,26 @@ function insertSegments(
248
264
  if (!node.xp[segment.suffix]) {
249
265
  node.xp[segment.suffix] = { n: segment.value, c: {} };
250
266
  }
251
- insertSegments(node.xp[segment.suffix].c, segments, index + 1, leaf);
267
+ insertSegments(node.xp[segment.suffix].c, segments, index + 1, leafBase, [
268
+ ...paramNames,
269
+ segment.value,
270
+ ]);
252
271
  } else {
253
272
  if (!node.p) {
254
273
  node.p = { n: segment.value, c: {} };
255
274
  }
256
- insertSegments(node.p.c, segments, index + 1, leaf);
275
+ insertSegments(node.p.c, segments, index + 1, leafBase, [
276
+ ...paramNames,
277
+ segment.value,
278
+ ]);
257
279
  }
258
280
  } else if (segment.type === "wildcard") {
259
- // Wildcard consumes all remaining segments
260
- const wildLeaf = { ...leaf, pn: "*" };
281
+ // Wildcard consumes all remaining segments. Carry any params bound before
282
+ // the wildcard in pa so they zip correctly against paramValues at match.
283
+ const wildLeaf: TrieLeaf & { pn: string } = {
284
+ ...buildLeaf(leafBase, paramNames),
285
+ pn: "*",
286
+ };
261
287
  const existing = node.w ? ({ ...node.w } as TrieLeaf) : undefined;
262
288
  const merged = mergeLeaves(existing, wildLeaf);
263
289
  node.w = merged as TrieLeaf & { pn: string };
@@ -357,12 +357,17 @@ function buildRouteMapFromBlock(
357
357
  /**
358
358
  * Build route map and search schemas together.
359
359
  * Internal helper used by the include resolution path.
360
+ *
361
+ * @param inlineBlock - Optional pre-extracted code block (e.g. from an inline
362
+ * builder function). When provided, variableName is ignored and the block
363
+ * is parsed directly for path()/include() calls.
360
364
  */
361
365
  export function buildCombinedRouteMapWithSearch(
362
366
  filePath: string,
363
367
  variableName?: string,
364
368
  visited?: Set<string>,
365
369
  diagnosticsOut?: UnresolvableInclude[],
370
+ inlineBlock?: string,
366
371
  ): {
367
372
  routes: Record<string, string>;
368
373
  searchSchemas: Record<string, Record<string, string>>;
@@ -384,7 +389,9 @@ export function buildCombinedRouteMapWithSearch(
384
389
  }
385
390
 
386
391
  let block: string;
387
- if (variableName) {
392
+ if (inlineBlock) {
393
+ block = inlineBlock;
394
+ } else if (variableName) {
388
395
  const extracted = extractUrlsBlockForVariable(source, variableName);
389
396
  if (!extracted) return { routes: {}, searchSchemas: {} };
390
397
  block = extracted;