reroute-js 0.40.2 → 0.41.1

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 (194) hide show
  1. package/cli/bin.d.ts +1 -1
  2. package/cli/bin.js +206 -71
  3. package/cli/bin.js.map +13 -13
  4. package/cli/index.d.ts +1 -1
  5. package/cli/index.js +4 -4
  6. package/cli/index.js.map +1 -1
  7. package/cli/src/cli.d.ts +1 -1
  8. package/cli/src/commands/analyze.d.ts +1 -1
  9. package/cli/src/commands/build.d.ts +1 -1
  10. package/cli/src/commands/dev.d.ts +1 -1
  11. package/cli/src/commands/gen.d.ts +1 -1
  12. package/cli/src/commands/gen.d.ts.map +1 -1
  13. package/cli/src/commands/index.d.ts +1 -1
  14. package/cli/src/commands/init.d.ts +1 -1
  15. package/cli/src/commands/lib/assets.d.ts +1 -1
  16. package/cli/src/commands/lib/bundler.d.ts +1 -1
  17. package/cli/src/commands/lib/command.d.ts +1 -1
  18. package/cli/src/commands/lib/env.d.ts +1 -1
  19. package/cli/src/commands/lib/index.d.ts +1 -1
  20. package/cli/src/commands/lib/log.d.ts +1 -1
  21. package/cli/src/commands/lib/markdown/availability.d.ts +1 -1
  22. package/cli/src/commands/lib/markdown/index.d.ts +1 -1
  23. package/cli/src/commands/lib/markdown/processor.d.ts +1 -1
  24. package/cli/src/commands/lib/production.d.ts +1 -1
  25. package/cli/src/commands/lib/server.d.ts +1 -1
  26. package/cli/src/commands/lib/streaming/analyzer.d.ts +1 -1
  27. package/cli/src/commands/lib/streaming/suspense.d.ts +1 -1
  28. package/cli/src/commands/lib/tailwind.d.ts +1 -1
  29. package/cli/src/commands/lib/terminal-ui.d.ts +1 -1
  30. package/cli/src/commands/lib/version.d.ts +1 -1
  31. package/cli/src/commands/og.d.ts +1 -1
  32. package/cli/src/commands/start.d.ts +1 -1
  33. package/cli/src/index.d.ts +1 -1
  34. package/core/index.d.ts +1 -1
  35. package/core/index.js +148 -46
  36. package/core/index.js.map +12 -12
  37. package/core/src/bundler/hash.d.ts +1 -1
  38. package/core/src/bundler/index.d.ts +1 -1
  39. package/core/src/config.d.ts +42 -2
  40. package/core/src/config.d.ts.map +1 -1
  41. package/core/src/content/discovery.d.ts +1 -1
  42. package/core/src/content/index.d.ts +1 -1
  43. package/core/src/content/metadata.d.ts +7 -2
  44. package/core/src/content/metadata.d.ts.map +1 -1
  45. package/core/src/index.d.ts +1 -1
  46. package/core/src/llms/extractor.d.ts +1 -1
  47. package/core/src/llms/formatter.d.ts +1 -1
  48. package/core/src/llms/full-generator.d.ts +1 -1
  49. package/core/src/llms/index-generator.d.ts +1 -1
  50. package/core/src/llms/index.d.ts +1 -1
  51. package/core/src/og/discovery.d.ts +1 -1
  52. package/core/src/og/index.d.ts +1 -1
  53. package/core/src/og/meta.d.ts +1 -1
  54. package/core/src/og/render.d.ts +1 -1
  55. package/core/src/og/types.d.ts +1 -1
  56. package/core/src/robots/discovery.d.ts +1 -1
  57. package/core/src/robots/generator.d.ts +1 -1
  58. package/core/src/robots/index.d.ts +1 -1
  59. package/core/src/robots/policies.d.ts +1 -1
  60. package/core/src/rss/discovery.d.ts +1 -1
  61. package/core/src/rss/discovery.d.ts.map +1 -1
  62. package/core/src/rss/generator.d.ts +1 -1
  63. package/core/src/rss/index.d.ts +1 -1
  64. package/core/src/sitemap/discovery.d.ts +1 -1
  65. package/core/src/sitemap/discovery.d.ts.map +1 -1
  66. package/core/src/sitemap/generator.d.ts +1 -1
  67. package/core/src/sitemap/index.d.ts +1 -1
  68. package/core/src/ssr/index.d.ts +1 -1
  69. package/core/src/ssr/lib/cache.d.ts +1 -1
  70. package/core/src/ssr/lib/collections.d.ts +1 -1
  71. package/core/src/ssr/lib/compression.d.ts +1 -1
  72. package/core/src/ssr/lib/compute/content.d.ts +1 -1
  73. package/core/src/ssr/lib/compute/index.d.ts +1 -1
  74. package/core/src/ssr/lib/compute/layouts.d.ts +1 -1
  75. package/core/src/ssr/lib/compute/routes.d.ts +1 -1
  76. package/core/src/ssr/lib/data.d.ts +1 -1
  77. package/core/src/ssr/lib/html.d.ts +6 -1
  78. package/core/src/ssr/lib/html.d.ts.map +1 -1
  79. package/core/src/ssr/lib/imports.d.ts +1 -1
  80. package/core/src/ssr/lib/index.d.ts +1 -1
  81. package/core/src/ssr/lib/layouts.d.ts +1 -1
  82. package/core/src/ssr/lib/metadata.d.ts +3 -3
  83. package/core/src/ssr/lib/metadata.d.ts.map +1 -1
  84. package/core/src/ssr/lib/mime.d.ts +1 -1
  85. package/core/src/ssr/lib/modules.d.ts +1 -1
  86. package/core/src/ssr/lib/path.d.ts +1 -1
  87. package/core/src/ssr/lib/preload.d.ts +1 -1
  88. package/core/src/ssr/lib/scripts/collections.d.ts +1 -1
  89. package/core/src/ssr/lib/scripts/data.d.ts +1 -1
  90. package/core/src/ssr/lib/scripts/escape.d.ts +1 -1
  91. package/core/src/ssr/lib/scripts/feeds.d.ts +1 -1
  92. package/core/src/ssr/lib/scripts/index.d.ts +1 -1
  93. package/core/src/ssr/lib/seed.d.ts +1 -1
  94. package/core/src/ssr/lib/serialize.d.ts +1 -1
  95. package/core/src/ssr/lib/setup.d.ts +3 -2
  96. package/core/src/ssr/lib/setup.d.ts.map +1 -1
  97. package/core/src/ssr/lib/styles.d.ts +1 -1
  98. package/core/src/ssr/lib/template.d.ts +1 -1
  99. package/core/src/ssr/lib/types.d.ts +1 -1
  100. package/core/src/ssr/render.d.ts +3 -2
  101. package/core/src/ssr/render.d.ts.map +1 -1
  102. package/core/src/ssr/stream.d.ts +3 -2
  103. package/core/src/ssr/stream.d.ts.map +1 -1
  104. package/elysia/index.d.ts +1 -1
  105. package/elysia/index.js +326 -127
  106. package/elysia/index.js.map +18 -18
  107. package/elysia/src/index.d.ts +1 -1
  108. package/elysia/src/libs/assets.d.ts +1 -1
  109. package/elysia/src/libs/cache.d.ts +1 -1
  110. package/elysia/src/libs/caching.d.ts +1 -1
  111. package/elysia/src/libs/http.d.ts +1 -1
  112. package/elysia/src/libs/image.d.ts +1 -1
  113. package/elysia/src/libs/index.d.ts +1 -1
  114. package/elysia/src/libs/llms.d.ts +1 -1
  115. package/elysia/src/libs/response.d.ts +1 -1
  116. package/elysia/src/libs/serving.d.ts +1 -1
  117. package/elysia/src/plugin.d.ts +1 -1
  118. package/elysia/src/plugin.d.ts.map +1 -1
  119. package/elysia/src/routes/artifacts.d.ts +1 -1
  120. package/elysia/src/routes/content.d.ts +1 -1
  121. package/elysia/src/routes/image.d.ts +1 -1
  122. package/elysia/src/routes/index.d.ts +1 -1
  123. package/elysia/src/routes/internal.d.ts +1 -1
  124. package/elysia/src/routes/internal.d.ts.map +1 -1
  125. package/elysia/src/routes/llms.d.ts +1 -1
  126. package/elysia/src/routes/og.d.ts +1 -1
  127. package/elysia/src/routes/og.d.ts.map +1 -1
  128. package/elysia/src/routes/redirects.d.ts +1 -1
  129. package/elysia/src/routes/robots.d.ts +1 -1
  130. package/elysia/src/routes/rss.d.ts +2 -2
  131. package/elysia/src/routes/rss.d.ts.map +1 -1
  132. package/elysia/src/routes/search.d.ts +1 -1
  133. package/elysia/src/routes/sitemap.d.ts +2 -2
  134. package/elysia/src/routes/sitemap.d.ts.map +1 -1
  135. package/elysia/src/routes/ssr.d.ts +3 -2
  136. package/elysia/src/routes/ssr.d.ts.map +1 -1
  137. package/elysia/src/routes/static.d.ts +1 -1
  138. package/elysia/src/types.d.ts +1 -1
  139. package/package.json +1 -1
  140. package/react/index.d.ts +1 -1
  141. package/react/index.js +2 -2
  142. package/react/index.js.map +1 -1
  143. package/react/src/components/ClientOnly.d.ts +1 -1
  144. package/react/src/components/ContentRoute.d.ts +1 -1
  145. package/react/src/components/Image.d.ts +1 -1
  146. package/react/src/components/LazyRoute.d.ts +1 -1
  147. package/react/src/components/Link.d.ts +1 -1
  148. package/react/src/components/Markdown.d.ts +1 -1
  149. package/react/src/components/Outlet.d.ts +1 -1
  150. package/react/src/components/index.d.ts +1 -1
  151. package/react/src/hooks/index.d.ts +1 -1
  152. package/react/src/hooks/useContent.d.ts +1 -1
  153. package/react/src/hooks/useData.d.ts +1 -1
  154. package/react/src/hooks/useFeed.d.ts +1 -1
  155. package/react/src/hooks/useLayoutData.d.ts +1 -1
  156. package/react/src/hooks/useLlms.d.ts +1 -1
  157. package/react/src/hooks/useNavigate.d.ts +1 -1
  158. package/react/src/hooks/useParams.d.ts +1 -1
  159. package/react/src/hooks/useRouter.d.ts +1 -1
  160. package/react/src/hooks/useSearch.d.ts +1 -1
  161. package/react/src/hooks/useSearchParams.d.ts +1 -1
  162. package/react/src/hooks/useToc.d.ts +1 -1
  163. package/react/src/index.d.ts +1 -1
  164. package/react/src/lib/collection.d.ts +1 -1
  165. package/react/src/lib/content.d.ts +1 -1
  166. package/react/src/lib/head.d.ts +1 -1
  167. package/react/src/lib/index.d.ts +1 -1
  168. package/react/src/lib/lazy-route.d.ts +1 -1
  169. package/react/src/lib/route-loader.d.ts +1 -1
  170. package/react/src/providers/ContentProvider.d.ts +1 -1
  171. package/react/src/providers/RerouteProvider.d.ts +1 -1
  172. package/react/src/providers/RouterProvider.d.ts +1 -1
  173. package/react/src/providers/index.d.ts +1 -1
  174. package/react/src/types/any.d.ts +1 -1
  175. package/react/src/types/index.d.ts +1 -1
  176. package/react/src/types/router.d.ts +1 -1
  177. package/telemetry/react.d.ts +1 -1
  178. package/telemetry/react.js +2 -2
  179. package/telemetry/react.js.map +1 -1
  180. package/telemetry/server.d.ts +1 -1
  181. package/telemetry/server.js +2 -2
  182. package/telemetry/server.js.map +8 -8
  183. package/telemetry/src/react/api.d.ts +1 -1
  184. package/telemetry/src/react/context.d.ts +1 -1
  185. package/telemetry/src/react/index.d.ts +1 -1
  186. package/telemetry/src/react/telemetry.d.ts +1 -1
  187. package/telemetry/src/server/context.d.ts +1 -1
  188. package/telemetry/src/server/headers/extractor.d.ts +1 -1
  189. package/telemetry/src/server/headers/index.d.ts +1 -1
  190. package/telemetry/src/server/headers/presets.d.ts +1 -1
  191. package/telemetry/src/server/index.d.ts +1 -1
  192. package/telemetry/src/server/instrumentation.d.ts +1 -1
  193. package/telemetry/src/server/plugin.d.ts +1 -1
  194. package/telemetry/src/server/sourcemap.d.ts +1 -1
package/cli/bin.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * reroute-js v0.40.2
2
+ * reroute-js v0.41.1
3
3
  *
4
4
  * @license MIT
5
5
  * @copyright 2026 stewones <hi@stewan.io>
package/cli/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
  /**
4
- * reroute-js v0.40.2
4
+ * reroute-js v0.41.1
5
5
  *
6
6
  * @license MIT
7
7
  * @copyright 2026 stewones <hi@stewan.io>
@@ -48,7 +48,7 @@ async function getVersionString() {
48
48
  }
49
49
  async function getVersion() {
50
50
  if (true) {
51
- return "0.40.2";
51
+ return "0.41.1";
52
52
  }
53
53
  const possiblePaths = [
54
54
  path.join(import.meta.dir, "../../../../package.json"),
@@ -66,7 +66,7 @@ async function getVersion() {
66
66
  }
67
67
  async function getCommit() {
68
68
  if (true) {
69
- return "00b2cc6";
69
+ return "3909689";
70
70
  }
71
71
  return "dev";
72
72
  }
@@ -475,7 +475,7 @@ async function getContentMeta(absPath, isWatchMode) {
475
475
  }
476
476
  }
477
477
  }
478
- function buildHeadFromMeta(meta) {
478
+ function buildHeadFromMeta(meta, ogConfig) {
479
479
  if (!meta || typeof meta !== "object")
480
480
  return "";
481
481
  const parts = [];
@@ -485,6 +485,23 @@ function buildHeadFromMeta(meta) {
485
485
  parts.push(`<title>${escapeHtml(title)}</title>`);
486
486
  if (description)
487
487
  parts.push(`<meta name="description" content="${escapeHtml(description)}" />`);
488
+ const ogType = typeof meta.ogType === "string" ? meta.ogType : ogConfig?.defaultType || "website";
489
+ parts.push(`<meta property="og:type" content="${escapeHtml(ogType)}" />`);
490
+ if (title) {
491
+ parts.push(`<meta property="og:title" content="${escapeHtml(title)}" />`);
492
+ }
493
+ const finalDescription = description || ogConfig?.siteDescription;
494
+ if (finalDescription) {
495
+ parts.push(`<meta property="og:description" content="${escapeHtml(finalDescription)}" />`);
496
+ }
497
+ const twitterCard = ogConfig?.twitterCard || "summary_large_image";
498
+ parts.push(`<meta name="twitter:card" content="${twitterCard}" />`);
499
+ if (title) {
500
+ parts.push(`<meta name="twitter:title" content="${escapeHtml(title)}" />`);
501
+ }
502
+ if (finalDescription) {
503
+ parts.push(`<meta name="twitter:description" content="${escapeHtml(finalDescription)}" />`);
504
+ }
488
505
  return parts.length ? `
489
506
  ${parts.join(`
490
507
  `)}` : "";
@@ -1947,6 +1964,28 @@ function removeDefaultMetaTags(templateHtml) {
1947
1964
  } catch {}
1948
1965
  return result;
1949
1966
  }
1967
+ function removeOverriddenMetaTags(templateHtml, injectedHead) {
1968
+ let result = templateHtml;
1969
+ try {
1970
+ const injectedOgProps = [
1971
+ ...injectedHead.matchAll(/<meta\s+property\s*=\s*['"]([^'"]+)['"][^>]*>/gi)
1972
+ ].map((m) => m[1].toLowerCase());
1973
+ const injectedNameMetas = [
1974
+ ...injectedHead.matchAll(/<meta\s+name\s*=\s*['"]([^'"]+)['"][^>]*>/gi)
1975
+ ].map((m) => m[1].toLowerCase());
1976
+ for (const prop of injectedOgProps) {
1977
+ const escapedProp = prop.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1978
+ const regex = new RegExp(`<meta\\s+property\\s*=\\s*['"]${escapedProp}['"][^>]*>`, "gi");
1979
+ result = result.replace(regex, "");
1980
+ }
1981
+ for (const name of injectedNameMetas) {
1982
+ const escapedName = name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1983
+ const regex = new RegExp(`<meta\\s+name\\s*=\\s*['"]${escapedName}['"][^>]*>`, "gi");
1984
+ result = result.replace(regex, "");
1985
+ }
1986
+ } catch {}
1987
+ return result;
1988
+ }
1950
1989
  function deduplicateHeadContent(templateHtml, headToInject) {
1951
1990
  let cleanedTemplate = templateHtml;
1952
1991
  let cleanedHead = headToInject;
@@ -1957,6 +1996,7 @@ function deduplicateHeadContent(templateHtml, headToInject) {
1957
1996
  } else if (/<meta\s+name\s*=\s*['"]description['"][^>]*>/i.test(cleanedHead)) {
1958
1997
  cleanedTemplate = cleanedTemplate.replace(/<meta\s+name\s*=\s*['"]description['"][^>]*>/i, "");
1959
1998
  }
1999
+ cleanedTemplate = removeOverriddenMetaTags(cleanedTemplate, cleanedHead);
1960
2000
  } catch {}
1961
2001
  return { cleanedTemplate, cleanedHead };
1962
2002
  }
@@ -2075,7 +2115,6 @@ async function generateOGImageMetaTags(pathname, clientDir, ogConfig) {
2075
2115
  return `<meta property="og:image" content="${ogImagePath}" />
2076
2116
  <meta property="og:image:width" content="${width}" />
2077
2117
  <meta property="og:image:height" content="${height}" />
2078
- <meta name="twitter:card" content="summary_large_image" />
2079
2118
  <meta name="twitter:image" content="${ogImagePath}" />`;
2080
2119
  }
2081
2120
  var init_meta = __esm(() => {
@@ -2113,7 +2152,25 @@ function generateOgUrl(baseUrl, pathname) {
2113
2152
  const ogUrl = `${cleanBaseUrl}${normalizedPath}`;
2114
2153
  return `<meta property="og:url" content="${ogUrl}" />`;
2115
2154
  }
2116
- async function extractPageMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ogConfig, baseUrl, autoCanonical, ssrData) {
2155
+ function generateGlobalOGTags(ogConfig) {
2156
+ const tags = [];
2157
+ if (ogConfig.siteName) {
2158
+ tags.push(`<meta property="og:site_name" content="${escapeHtml2(ogConfig.siteName)}" />`);
2159
+ }
2160
+ if (ogConfig.locale) {
2161
+ tags.push(`<meta property="og:locale" content="${escapeHtml2(ogConfig.locale)}" />`);
2162
+ }
2163
+ if (ogConfig.twitterSite) {
2164
+ const handle = ogConfig.twitterSite.startsWith("@") ? ogConfig.twitterSite : `@${ogConfig.twitterSite}`;
2165
+ tags.push(`<meta name="twitter:site" content="${escapeHtml2(handle)}" />`);
2166
+ }
2167
+ return tags.length ? tags.join(`
2168
+ `) : "";
2169
+ }
2170
+ function escapeHtml2(input) {
2171
+ return input.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
2172
+ }
2173
+ async function extractPageMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ogImageConfig, ogConfig, baseUrl, autoCanonical, ssrData) {
2117
2174
  let perPageHead = "";
2118
2175
  let pageLang;
2119
2176
  let statusOverride = currentStatusOverride;
@@ -2122,7 +2179,7 @@ async function extractPageMetadata(pathname, clientDir, cwd, isWatchMode, curren
2122
2179
  }, (lang) => {
2123
2180
  if (!pageLang)
2124
2181
  pageLang = lang;
2125
- });
2182
+ }, ogConfig);
2126
2183
  const shouldGenerateCanonical = autoCanonical !== false && baseUrl !== undefined;
2127
2184
  if (shouldGenerateCanonical) {
2128
2185
  const canonicalTag = generateCanonicalUrl(baseUrl, pathname);
@@ -2132,8 +2189,15 @@ ${canonicalTag}`;
2132
2189
  perPageHead += `
2133
2190
  ${ogUrlTag}`;
2134
2191
  }
2192
+ if (ogConfig) {
2193
+ const ogGlobalTags = generateGlobalOGTags(ogConfig);
2194
+ if (ogGlobalTags) {
2195
+ perPageHead += `
2196
+ ${ogGlobalTags}`;
2197
+ }
2198
+ }
2135
2199
  try {
2136
- const ogMetaTags = await generateOGImageMetaTags(pathname, clientDir, ogConfig);
2200
+ const ogMetaTags = await generateOGImageMetaTags(pathname, clientDir, ogImageConfig);
2137
2201
  if (ogMetaTags) {
2138
2202
  perPageHead += `
2139
2203
  ${ogMetaTags}`;
@@ -2145,22 +2209,22 @@ ${ogMetaTags}`;
2145
2209
  perPageHead += head;
2146
2210
  }, (lang) => {
2147
2211
  pageLang = lang;
2148
- });
2149
- const routeResult = await extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ssrData);
2212
+ }, ogConfig);
2213
+ const routeResult = await extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ssrData, ogConfig);
2150
2214
  perPageHead += routeResult.head;
2151
2215
  if (routeResult.lang)
2152
2216
  pageLang = routeResult.lang;
2153
2217
  statusOverride = routeResult.statusOverride;
2154
2218
  return { perPageHead, pageLang, statusOverride };
2155
2219
  }
2156
- async function processLayoutForMetadata(layout, clientDir, isWatchMode, addHead, setLang) {
2220
+ async function processLayoutForMetadata(layout, clientDir, isWatchMode, addHead, setLang, ogConfig) {
2157
2221
  if (typeof layout?.path !== "string")
2158
2222
  return;
2159
2223
  try {
2160
2224
  const abs = join2(clientDir, "routes", String(layout.path));
2161
2225
  const { meta, ssr } = await loadModuleMetaAndSSR(layout, abs, isWatchMode);
2162
2226
  if (meta)
2163
- addHead(buildHeadFromMeta(meta));
2227
+ addHead(buildHeadFromMeta(meta, ogConfig));
2164
2228
  if (ssr) {
2165
2229
  const ssrHead = extractSSRHead(ssr);
2166
2230
  if (ssrHead)
@@ -2172,16 +2236,16 @@ ${ssrHead}`);
2172
2236
  }
2173
2237
  } catch {}
2174
2238
  }
2175
- async function extractLayoutMetadata(pathname, clientDir, cwd, isWatchMode, addHead, setLang) {
2239
+ async function extractLayoutMetadata(pathname, clientDir, cwd, isWatchMode, addHead, setLang, ogConfig) {
2176
2240
  try {
2177
2241
  const m = await loadRoutesModule(cwd, isWatchMode);
2178
2242
  const matchingLayouts = getMatchingLayouts(pathname, m?.layouts);
2179
2243
  for (const layout of matchingLayouts) {
2180
- await processLayoutForMetadata(layout, clientDir, isWatchMode, addHead, setLang);
2244
+ await processLayoutForMetadata(layout, clientDir, isWatchMode, addHead, setLang, ogConfig);
2181
2245
  }
2182
2246
  } catch {}
2183
2247
  }
2184
- function extractContentMetadata(pathname, addHead, setLang) {
2248
+ function extractContentMetadata(pathname, addHead, setLang, ogConfig) {
2185
2249
  try {
2186
2250
  const parts = pathname.split("/").filter(Boolean);
2187
2251
  if (parts.length < 2)
@@ -2191,7 +2255,7 @@ function extractContentMetadata(pathname, addHead, setLang) {
2191
2255
  const exp = g.__REROUTE_SSR_EXPORTS__?.[key];
2192
2256
  const meta = exp?.meta;
2193
2257
  const ssr = exp?.ssr;
2194
- addHead(buildHeadFromMeta(meta));
2258
+ addHead(buildHeadFromMeta(meta, ogConfig));
2195
2259
  const ssrHead = extractSSRHead(ssr);
2196
2260
  if (ssrHead)
2197
2261
  addHead(`
@@ -2201,7 +2265,7 @@ ${ssrHead}`);
2201
2265
  setLang(lang);
2202
2266
  } catch {}
2203
2267
  }
2204
- async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData) {
2268
+ async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig) {
2205
2269
  const pathnameOnly = pathname.split("?")[0];
2206
2270
  const match = typeof m.matchRoute === "function" ? m.matchRoute(pathnameOnly) : null;
2207
2271
  const r = match?.route;
@@ -2214,7 +2278,7 @@ async function extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode,
2214
2278
  const { meta, ssr } = await loadModuleMetaAndSSR(r, abs, isWatchMode);
2215
2279
  let head = "";
2216
2280
  if (meta)
2217
- head += buildHeadFromMeta(meta);
2281
+ head += buildHeadFromMeta(meta, ogConfig);
2218
2282
  if (ssr) {
2219
2283
  const routeData = ssrData && typeof ssrData === "object" ? ssrData[pathname] : undefined;
2220
2284
  const ssrHead = extractSSRHead(ssr, routeData);
@@ -2228,7 +2292,7 @@ ${ssrHead}`;
2228
2292
  }
2229
2293
  return { head: "", statusOverride };
2230
2294
  }
2231
- async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ssrData) {
2295
+ async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, currentStatusOverride, ssrData, ogConfig) {
2232
2296
  let head = "";
2233
2297
  let lang;
2234
2298
  let statusOverride = currentStatusOverride;
@@ -2244,14 +2308,14 @@ async function extractRouteMetadata(pathname, clientDir, cwd, isWatchMode, curre
2244
2308
  statusOverride = statusOverride || 404;
2245
2309
  }
2246
2310
  if (r && typeof r.path === "string") {
2247
- const result = await extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData);
2311
+ const result = await extractMatchedRouteMetadata(pathname, clientDir, m, isWatchMode, statusOverride, ssrData, ogConfig);
2248
2312
  head = result.head;
2249
2313
  lang = result.lang;
2250
2314
  statusOverride = result.statusOverride;
2251
2315
  } else {
2252
2316
  head += await extractNotFoundMetadata(pathname, clientDir, m, isWatchMode, (l) => {
2253
2317
  lang = l;
2254
- });
2318
+ }, ogConfig);
2255
2319
  }
2256
2320
  } catch {}
2257
2321
  return { head, lang, statusOverride };
@@ -2271,7 +2335,7 @@ function findBestNotFoundRoute(list, pathname) {
2271
2335
  }
2272
2336
  return chosen;
2273
2337
  }
2274
- async function extractNotFoundMetadata(pathname, clientDir, routesModule, isWatchMode, setLang) {
2338
+ async function extractNotFoundMetadata(pathname, clientDir, routesModule, isWatchMode, setLang, ogConfig) {
2275
2339
  let head = "";
2276
2340
  try {
2277
2341
  const list = routesModule?.notFoundRoutes;
@@ -2282,7 +2346,7 @@ async function extractNotFoundMetadata(pathname, clientDir, routesModule, isWatc
2282
2346
  const abs = join2(clientDir, "routes", String(chosen.path));
2283
2347
  const { meta, ssr } = await loadModuleMetaAndSSR(chosen, abs, isWatchMode);
2284
2348
  if (meta)
2285
- head += buildHeadFromMeta(meta);
2349
+ head += buildHeadFromMeta(meta, ogConfig);
2286
2350
  const ssrHead = extractSSRHead(ssr);
2287
2351
  if (ssrHead)
2288
2352
  head += `
@@ -2869,7 +2933,7 @@ async function performSSRSetupInternal(options, streaming) {
2869
2933
  statusOverride = computeResult.statusContainer.value;
2870
2934
  }
2871
2935
  const metadataResult = await withSpan("ssr.extract.metadata", async (span) => {
2872
- const result = await extractPageMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ogConfig, options.baseUrl, options.autoCanonical, computeResult.data);
2936
+ const result = await extractPageMetadata(pathname, clientDir, cwd, isWatchMode, statusOverride, ogConfig, options.ogMetaConfig, options.baseUrl, options.autoCanonical, computeResult.data);
2873
2937
  span.setAttributes({
2874
2938
  "reroute.pathname": pathname,
2875
2939
  "reroute.metadata.exists": !!result.perPageHead
@@ -3039,6 +3103,7 @@ async function renderSSRDocument(options) {
3039
3103
  maxAge,
3040
3104
  searchParams,
3041
3105
  ogConfig,
3106
+ ogMetaConfig: options.ogMetaConfig,
3042
3107
  baseUrl: options.baseUrl,
3043
3108
  autoCanonical: options.autoCanonical,
3044
3109
  cachedCollections: options.cachedCollections,
@@ -4303,13 +4368,13 @@ async function discoverSSRDataRoutes2(cwd, staticRoutes, isWatchMode, baseUrl, s
4303
4368
  const ssrDataRoutes = new Map;
4304
4369
  const bundledRoutes = globalThis.__REROUTE_ROUTES__;
4305
4370
  if (bundledRoutes && Array.isArray(bundledRoutes)) {
4306
- for (const route of staticRoutes) {
4371
+ const routePromises = staticRoutes.map(async (route) => {
4307
4372
  if (sitemapConfig?.excludeDiscovery?.some((excluded) => route.pattern.startsWith(excluded))) {
4308
- continue;
4373
+ return null;
4309
4374
  }
4310
4375
  const bundledRoute = bundledRoutes.find((r) => r?.pattern === route.pattern);
4311
4376
  if (!bundledRoute)
4312
- continue;
4377
+ return null;
4313
4378
  const ssr = bundledRoute?.ssr;
4314
4379
  const dataFn = ssr?.data;
4315
4380
  if (typeof dataFn === "function") {
@@ -4322,15 +4387,22 @@ async function discoverSSRDataRoutes2(cwd, staticRoutes, isWatchMode, baseUrl, s
4322
4387
  });
4323
4388
  const entries = extractEntriesFromData(result, route.pattern, baseUrl, sitemapConfig);
4324
4389
  if (entries.length > 0) {
4325
- ssrDataRoutes.set(route.pattern, entries);
4390
+ return { pattern: route.pattern, entries };
4326
4391
  }
4327
4392
  } catch {}
4328
4393
  }
4394
+ return null;
4395
+ });
4396
+ const results = await Promise.all(routePromises);
4397
+ for (const result of results) {
4398
+ if (result) {
4399
+ ssrDataRoutes.set(result.pattern, result.entries);
4400
+ }
4329
4401
  }
4330
4402
  } else {
4331
- for (const route of staticRoutes) {
4403
+ const routePromises = staticRoutes.map(async (route) => {
4332
4404
  if (sitemapConfig?.excludeDiscovery?.some((excluded) => route.pattern.startsWith(excluded))) {
4333
- continue;
4405
+ return null;
4334
4406
  }
4335
4407
  try {
4336
4408
  const routeModulePath = join2(cwd, "src", "client", "routes", route.path);
@@ -4346,10 +4418,17 @@ async function discoverSSRDataRoutes2(cwd, staticRoutes, isWatchMode, baseUrl, s
4346
4418
  });
4347
4419
  const entries = extractEntriesFromData(result, route.pattern, baseUrl, sitemapConfig);
4348
4420
  if (entries.length > 0) {
4349
- ssrDataRoutes.set(route.pattern, entries);
4421
+ return { pattern: route.pattern, entries };
4350
4422
  }
4351
4423
  }
4352
4424
  } catch {}
4425
+ return null;
4426
+ });
4427
+ const results = await Promise.all(routePromises);
4428
+ for (const result of results) {
4429
+ if (result) {
4430
+ ssrDataRoutes.set(result.pattern, result.entries);
4431
+ }
4353
4432
  }
4354
4433
  }
4355
4434
  return ssrDataRoutes;
@@ -4725,24 +4804,28 @@ function createRSSItemFromData(item, routePattern, baseUrl, rssConfig) {
4725
4804
  }
4726
4805
  async function discoverRSSData(cwd, collections2, baseUrl, isWatchMode, rssConfig) {
4727
4806
  const collectionsMap = new Map;
4728
- for (const collection of collections2) {
4807
+ const collectionPromises = collections2.map(async (collection) => {
4729
4808
  const items = await discoverCollectionFeedItems(cwd, collection, isWatchMode, baseUrl, rssConfig);
4730
- if (items.length > 0) {
4731
- collectionsMap.set(collection, items);
4809
+ return items.length > 0 ? { collection, items } : null;
4810
+ });
4811
+ const collectionResults = await Promise.all(collectionPromises);
4812
+ for (const result of collectionResults) {
4813
+ if (result) {
4814
+ collectionsMap.set(result.collection, result.items);
4732
4815
  }
4733
4816
  }
4734
4817
  const ssrDataFeeds = new Map;
4735
4818
  const bundledRoutes = globalThis.__REROUTE_ROUTES__;
4819
+ const { discoverRoutes: discoverRoutes2 } = await Promise.resolve().then(() => (init_discovery4(), exports_discovery));
4820
+ const { staticRoutes } = await discoverRoutes2(cwd, isWatchMode);
4736
4821
  if (bundledRoutes && Array.isArray(bundledRoutes)) {
4737
- const { discoverRoutes: discoverRoutes2 } = await Promise.resolve().then(() => (init_discovery4(), exports_discovery));
4738
- const { staticRoutes } = await discoverRoutes2(cwd, isWatchMode);
4739
- for (const route of staticRoutes) {
4822
+ const routePromises = staticRoutes.map(async (route) => {
4740
4823
  if (rssConfig?.excludeDiscovery?.some((excluded) => route.pattern.startsWith(excluded))) {
4741
- continue;
4824
+ return null;
4742
4825
  }
4743
4826
  const bundledRoute = bundledRoutes.find((r) => r?.pattern === route.pattern);
4744
4827
  if (!bundledRoute)
4745
- continue;
4828
+ return null;
4746
4829
  const ssr = bundledRoute?.ssr;
4747
4830
  const dataFn = ssr?.data;
4748
4831
  if (typeof dataFn === "function") {
@@ -4755,21 +4838,33 @@ async function discoverRSSData(cwd, collections2, baseUrl, isWatchMode, rssConfi
4755
4838
  });
4756
4839
  const items = extractRSSItemsFromData(result, route.pattern, baseUrl, rssConfig);
4757
4840
  if (items.length > 0) {
4758
- ssrDataFeeds.set(route.pattern, items);
4841
+ return { pattern: route.pattern, items };
4759
4842
  }
4760
4843
  } catch {}
4761
4844
  }
4845
+ return null;
4846
+ });
4847
+ const results = await Promise.all(routePromises);
4848
+ for (const result of results) {
4849
+ if (result) {
4850
+ ssrDataFeeds.set(result.pattern, result.items);
4851
+ }
4762
4852
  }
4763
4853
  } else {
4764
- const { discoverRoutes: discoverRoutes2 } = await Promise.resolve().then(() => (init_discovery4(), exports_discovery));
4765
- const { staticRoutes } = await discoverRoutes2(cwd, isWatchMode);
4766
- for (const route of staticRoutes) {
4854
+ const routePromises = staticRoutes.map(async (route) => {
4767
4855
  if (rssConfig?.excludeDiscovery?.some((excluded) => route.pattern.startsWith(excluded))) {
4768
- continue;
4856
+ return null;
4769
4857
  }
4770
4858
  const items = await discoverSSRDataFeedItems(cwd, route.pattern, route.path, isWatchMode, baseUrl, rssConfig);
4771
4859
  if (items.length > 0) {
4772
- ssrDataFeeds.set(route.pattern, items);
4860
+ return { pattern: route.pattern, items };
4861
+ }
4862
+ return null;
4863
+ });
4864
+ const results = await Promise.all(routePromises);
4865
+ for (const result of results) {
4866
+ if (result) {
4867
+ ssrDataFeeds.set(result.pattern, result.items);
4773
4868
  }
4774
4869
  }
4775
4870
  }
@@ -5149,6 +5244,7 @@ async function renderSSRDocumentStream(options) {
5149
5244
  maxAge,
5150
5245
  searchParams,
5151
5246
  ogConfig,
5247
+ ogMetaConfig: options.ogMetaConfig,
5152
5248
  baseUrl: options.baseUrl,
5153
5249
  autoCanonical: options.autoCanonical,
5154
5250
  cachedCollections: options.cachedCollections,
@@ -5183,7 +5279,11 @@ async function renderSSRDocumentStream(options) {
5183
5279
  };
5184
5280
  });
5185
5281
  const pageLang = setup2.metadataResult.pageLang || lang;
5186
- let headWithLang = htmlHead.replace(/<html([^>]*)>/i, `<html$1 lang="${pageLang}">`);
5282
+ let headWithLang = htmlHead.replace(/<html([^>]*)>/i, (_m, attrs) => {
5283
+ const hasLang = /(^|\s)lang\s*=/.test(attrs);
5284
+ const newAttrs = hasLang ? attrs.replace(/lang\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/i, `lang="${pageLang}"`) : `${attrs} lang="${pageLang}"`;
5285
+ return `<html${newAttrs}>`;
5286
+ });
5187
5287
  const extraHead = setup2.bundlePreload + setup2.preloadExtraHead + setup2.metadataResult.perPageHead;
5188
5288
  if (/<title[\s\S]*?<\/title>/i.test(extraHead)) {
5189
5289
  headWithLang = headWithLang.replace(/<title[\s\S]*?<\/title>/i, "");
@@ -5191,6 +5291,7 @@ async function renderSSRDocumentStream(options) {
5191
5291
  if (/<meta\s+name\s*=\s*['"]description['"][^>]*>/i.test(extraHead)) {
5192
5292
  headWithLang = headWithLang.replace(/<meta\s+name\s*=\s*['"]description['"][^>]*>/i, "");
5193
5293
  }
5294
+ headWithLang = removeOverriddenMetaTags(headWithLang, extraHead);
5194
5295
  const config = await loadConfig(cwd);
5195
5296
  const browserTelemetryConfigRaw = config.telemetry?.browser;
5196
5297
  const { serializeBrowserTelemetryConfig: serializeBrowserTelemetryConfig2 } = await Promise.resolve().then(() => exports_serialize);
@@ -5239,7 +5340,7 @@ async function streamSSRContent(writer, encoder, ctx) {
5239
5340
  try {
5240
5341
  const combinedHead = deduplicateMetaTags([ctx.inlineStyleTag, ctx.head, ctx.extraHead].filter(Boolean).join(`
5241
5342
  `));
5242
- const headWithLangAndContent = ctx.headWithLang.replace(/<html([^>]*)>/i, `<html$1 lang="${ctx.pageLang}">`).replace(/<\/head>/i, `${combinedHead ? `${combinedHead}
5343
+ const headWithLangAndContent = ctx.headWithLang.replace(/<\/head>/i, `${combinedHead ? `${combinedHead}
5243
5344
  ` : ""}</head>`);
5244
5345
  const headContent = `${headWithLangAndContent}`;
5245
5346
  await writer.write(encoder.encode(headContent));
@@ -5379,6 +5480,7 @@ __export(exports_core, {
5379
5480
  renderSSRDocumentStream: () => renderSSRDocumentStream,
5380
5481
  renderSSRDocument: () => renderSSRDocument,
5381
5482
  renderOGImageToPNG: () => renderOGImageToPNG,
5483
+ removeOverriddenMetaTags: () => removeOverriddenMetaTags,
5382
5484
  processCollections: () => processCollections,
5383
5485
  preloadContentModule: () => preloadContentModule,
5384
5486
  performSSRSetup: () => performSSRSetup,
@@ -6973,9 +7075,12 @@ async function buildContentChunks(cwd, args = []) {
6973
7075
  lines.push(" return m ? (m as Record<string, any>)[name] : undefined;");
6974
7076
  lines.push("}");
6975
7077
  lines.push("");
6976
- await Bun.write(join9(cwd, OUTPUT_CONTENT_TS), lines.join(`
6977
- `));
6978
- console.log(`[reroute/content] wrote ${join9(cwd, OUTPUT_CONTENT_TS)}`);
7078
+ const writes = [];
7079
+ writes.push({
7080
+ path: join9(cwd, OUTPUT_CONTENT_TS),
7081
+ content: lines.join(`
7082
+ `)
7083
+ });
6979
7084
  const collectionsSet = new Set(results.map((r) => r.collection));
6980
7085
  for (const collection of collectionsSet) {
6981
7086
  const items = results.filter((r) => r.collection === collection);
@@ -6996,10 +7101,16 @@ async function buildContentChunks(cwd, args = []) {
6996
7101
  js.push(" return byName[name];");
6997
7102
  js.push("}");
6998
7103
  js.push("");
6999
- await Bun.write(join9(cwd, OUTPUT_COLLECTIONS_DIR, `${collection}.js`), js.join(`
7000
- `));
7001
- console.log(`[reroute/content] wrote ${join9(cwd, OUTPUT_COLLECTIONS_DIR, `${collection}.js`)}`);
7104
+ writes.push({
7105
+ path: join9(cwd, OUTPUT_COLLECTIONS_DIR, `${collection}.js`),
7106
+ content: js.join(`
7107
+ `)
7108
+ });
7002
7109
  }
7110
+ await Promise.all(writes.map(async ({ path: path3, content: content2 }) => {
7111
+ await Bun.write(path3, content2);
7112
+ console.log(`[reroute/content] wrote ${path3}`);
7113
+ }));
7003
7114
  }
7004
7115
  async function buildSearchIndex(cwd, config2, args = []) {
7005
7116
  const searchConfig = config2.search;
@@ -7593,30 +7704,54 @@ async function gen(args) {
7593
7704
  console.log(`
7594
7705
  [reroute/watch] \uD83C\uDFAF Initializing advanced watch mode...`);
7595
7706
  let isBuilding = false;
7596
- let queuedBuild = null;
7597
- async function enqueueBuild(buildFn, reason) {
7707
+ let pendingBuild = null;
7708
+ let skipCurrentBuild = false;
7709
+ function scheduleBuild(buildFn, reason) {
7598
7710
  if (isBuilding) {
7599
- queuedBuild = buildFn;
7600
- console.log(`[reroute/watch] Build queued (${reason})...`);
7711
+ if (!skipCurrentBuild) {
7712
+ skipCurrentBuild = true;
7713
+ console.log(`[reroute/watch] ⏭️ Superseding current build with: ${reason}`);
7714
+ }
7715
+ pendingBuild = { fn: buildFn, reason };
7601
7716
  return;
7602
7717
  }
7718
+ if (pendingBuild) {
7719
+ console.log(`[reroute/watch] ⏭️ Replacing queued build with: ${reason}`);
7720
+ }
7721
+ pendingBuild = { fn: buildFn, reason };
7722
+ processPendingBuild();
7723
+ }
7724
+ async function processPendingBuild() {
7725
+ if (!pendingBuild || isBuilding)
7726
+ return;
7727
+ const { fn: buildFn, reason } = pendingBuild;
7728
+ pendingBuild = null;
7729
+ skipCurrentBuild = false;
7603
7730
  isBuilding = true;
7604
7731
  const startTime = performance.now();
7605
7732
  await notifyRebuilding();
7606
7733
  try {
7607
7734
  await buildFn();
7735
+ if (skipCurrentBuild) {
7736
+ console.log(`[reroute/watch] ⏭️ Build superseded, skipping reload: ${reason}`);
7737
+ return;
7738
+ }
7608
7739
  const duration = ((performance.now() - startTime) / 1000).toFixed(2);
7609
7740
  console.log(`[reroute/watch] ✓ Rebuild complete in ${duration}s (${reason})`);
7741
+ await Bun.sleep(200);
7742
+ if (skipCurrentBuild || pendingBuild) {
7743
+ console.log(`[reroute/watch] ⏭️ Skipping reload, new build pending`);
7744
+ return;
7745
+ }
7610
7746
  await notifyReload(reason);
7611
7747
  } catch (err) {
7612
- console.error(`[reroute/watch] ✗ Build failed (${reason}):`, err);
7748
+ if (!skipCurrentBuild) {
7749
+ console.error(`[reroute/watch] ✗ Build failed (${reason}):`, err);
7750
+ }
7613
7751
  } finally {
7614
7752
  isBuilding = false;
7615
- if (queuedBuild) {
7616
- const nextBuild = queuedBuild;
7617
- queuedBuild = null;
7618
- console.log("[reroute/watch] \uD83D\uDD04 Processing queued build...");
7619
- await enqueueBuild(nextBuild, "queued");
7753
+ if (pendingBuild) {
7754
+ setTimeout(() => processPendingBuild(), 50);
7620
7755
  }
7621
7756
  }
7622
7757
  }
@@ -7637,7 +7772,7 @@ async function gen(args) {
7637
7772
  try {
7638
7773
  configWatcher = watch(configPath, () => {
7639
7774
  mediumDebounce(() => {
7640
- enqueueBuild(async () => {
7775
+ scheduleBuild(async () => {
7641
7776
  console.log("[reroute/watch] \uD83D\uDCDD Config changed, full rebuild...");
7642
7777
  await generate(cwd, args, { clean: true, writeStubs: false });
7643
7778
  await buildTailwindIfConfigured(cwd, isProd);
@@ -7658,7 +7793,7 @@ async function gen(args) {
7658
7793
  try {
7659
7794
  const envWatcher = watch(envPath, () => {
7660
7795
  mediumDebounce(() => {
7661
- enqueueBuild(async () => {
7796
+ scheduleBuild(async () => {
7662
7797
  console.log(`[reroute/watch] \uD83D\uDD10 ${envFile} changed`);
7663
7798
  await buildEntrypointBundle(cwd, args);
7664
7799
  }, `${envFile} change`);
@@ -7693,7 +7828,7 @@ async function gen(args) {
7693
7828
  if (!/\.(tsx|ts|jsx|js)$/.test(name))
7694
7829
  return;
7695
7830
  slowDebounce(() => {
7696
- enqueueBuild(async () => {
7831
+ scheduleBuild(async () => {
7697
7832
  console.log(`[reroute/watch] \uD83D\uDD27 Package changed: ${name}`);
7698
7833
  await generate(cwd, args, {
7699
7834
  clean: false,
@@ -7711,7 +7846,7 @@ async function gen(args) {
7711
7846
  return;
7712
7847
  const name = String(filename);
7713
7848
  quickDebounce(() => {
7714
- enqueueBuild(async () => {
7849
+ scheduleBuild(async () => {
7715
7850
  const isContentFile = name.includes("/content/") || name.includes("\\content\\");
7716
7851
  const isMarkdown = /\.(md|mdx)$/.test(name);
7717
7852
  const isRoute = /\.(tsx|ts)$/.test(name) && !isContentFile;
@@ -7768,7 +7903,7 @@ async function gen(args) {
7768
7903
  if (!/\.(tsx|ts|jsx|js|html|md|mdx|css)$/.test(name))
7769
7904
  return;
7770
7905
  mediumDebounce(() => {
7771
- enqueueBuild(async () => {
7906
+ scheduleBuild(async () => {
7772
7907
  console.log(`[reroute/watch] \uD83D\uDCE6 Client changed: ${name}`);
7773
7908
  if (name.endsWith(".ts") || name.endsWith(".tsx")) {
7774
7909
  console.log("[reroute/watch] ├─ Rebuilding bundle...");
@@ -105700,7 +105835,7 @@ async function getVersionString2() {
105700
105835
  }
105701
105836
  async function getVersion2() {
105702
105837
  if (true) {
105703
- return "0.40.2";
105838
+ return "0.41.1";
105704
105839
  }
105705
105840
  const possiblePaths = [
105706
105841
  path3.join(import.meta.dir, "../../../package.json"),
@@ -105717,10 +105852,10 @@ async function getVersion2() {
105717
105852
  }
105718
105853
  async function getCommit2() {
105719
105854
  if (true) {
105720
- return "00b2cc6";
105855
+ return "3909689";
105721
105856
  }
105722
105857
  return "dev";
105723
105858
  }
105724
105859
  main();
105725
105860
 
105726
- //# debugId=D069AFC2D33D785364756E2164756E21
105861
+ //# debugId=6B4BD5CD88C5BCB064756E2164756E21