@junejs/server 1.0.0-dev.28 → 1.0.0-dev.36

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 (75) hide show
  1. package/dist/agent-compile.d.ts +8 -9
  2. package/dist/agent-discover.d.ts +3 -4
  3. package/dist/agent-durable.d.ts +20 -19
  4. package/dist/agent-durable.js +41 -6
  5. package/dist/agent-native.d.ts +10 -11
  6. package/dist/agent-native.js +6 -2
  7. package/dist/app.d.ts +4 -5
  8. package/dist/app.js +2 -1
  9. package/dist/blob.d.ts +4 -5
  10. package/dist/build.d.ts +6 -6
  11. package/dist/build.js +34 -7
  12. package/dist/client-bundle.d.ts +8 -9
  13. package/dist/{config-freeze-DwI-Rmvq.d.ts → config-freeze-ZWC_Qd4h.d.ts} +1 -1
  14. package/dist/config-freeze.d.ts +1 -1
  15. package/dist/config-loader.d.ts +3 -4
  16. package/dist/connection-auth.d.ts +8 -9
  17. package/dist/content.d.ts +9 -10
  18. package/dist/core-version.d.ts +3 -4
  19. package/dist/css-modules-ambient.d.ts +2 -1
  20. package/dist/css-modules.d.ts +10 -11
  21. package/dist/css.d.ts +9 -10
  22. package/dist/d1-migrate.d.ts +6 -7
  23. package/dist/d1-migrate.js +3 -2
  24. package/dist/db.d.ts +2 -2
  25. package/dist/deploy.d.ts +3 -4
  26. package/dist/deploy.js +3 -2
  27. package/dist/dev-reload.d.ts +3 -4
  28. package/dist/dev.d.ts +4 -5
  29. package/dist/favicon.d.ts +16 -17
  30. package/dist/host.d.ts +5 -5
  31. package/dist/icon-letter.d.ts +2 -3
  32. package/dist/index.d.ts +4 -4
  33. package/dist/instrumentation.d.ts +2 -3
  34. package/dist/instrumentation.js +2 -1
  35. package/dist/island-registry.d.ts +6 -7
  36. package/dist/isolate-local.d.ts +4 -5
  37. package/dist/kv.d.ts +3 -4
  38. package/dist/llms-links.d.ts +9 -0
  39. package/dist/llms-links.js +65 -0
  40. package/dist/{manifest-gcNWqWoI.d.ts → manifest-DSNX4I-G.d.ts} +2 -2
  41. package/dist/manifest.d.ts +1 -1
  42. package/dist/negotiate.d.ts +4 -3
  43. package/dist/negotiate.js +24 -14
  44. package/dist/{pipeline-cCR6MGNK.d.ts → pipeline-BHUjBO0G.d.ts} +5 -1
  45. package/dist/pipeline.d.ts +2 -2
  46. package/dist/pipeline.js +263 -39
  47. package/dist/resources.d.ts +5 -6
  48. package/dist/router.d.ts +12 -13
  49. package/dist/rsc-build.d.ts +6 -7
  50. package/dist/rsc-bundle.d.ts +6 -7
  51. package/dist/rsc-manifest.d.ts +5 -6
  52. package/dist/rsc-runtime/flight-render-routes.d.ts +3 -4
  53. package/dist/rsc-runtime/flight-render.d.ts +2 -3
  54. package/dist/rsc-runtime/flight-to-html.d.ts +2 -3
  55. package/dist/rsc-runtime/flight-to-html.js +3 -2
  56. package/dist/rsc-runtime/rsdw-shims.d.ts +2 -1
  57. package/dist/segment.d.ts +4 -5
  58. package/dist/sqlite-driver.d.ts +10 -11
  59. package/dist/static-files.d.ts +4 -5
  60. package/dist/tsconfig-jsx.d.ts +4 -5
  61. package/dist/turn-events.d.ts +2 -3
  62. package/dist/web-manifest.d.ts +3 -4
  63. package/dist/web-manifest.js +2 -6
  64. package/dist/{worker-BkFHGtxp.d.ts → worker-Dv4l6Ybs.d.ts} +4 -2
  65. package/dist/worker.d.ts +1 -1
  66. package/dist/worker.js +35 -18
  67. package/package.json +5 -5
  68. package/src/agent-durable.ts +39 -10
  69. package/src/agent-native.ts +5 -1
  70. package/src/build.ts +63 -5
  71. package/src/llms-links.ts +96 -0
  72. package/src/negotiate.ts +39 -15
  73. package/src/pipeline.ts +437 -47
  74. package/src/web-manifest.ts +2 -6
  75. package/src/worker.ts +55 -17
package/dist/pipeline.js CHANGED
@@ -1,13 +1,16 @@
1
1
  import { iconLetter } from "./icon-letter.js";
2
+ import { collectLlmsLinks, collectSitemapPages } from "./llms-links.js";
2
3
  import { webManifest } from "./web-manifest.js";
3
4
  import { SEGMENT_HEADER, TITLE_HEADER, encodeTitle, negotiate } from "./negotiate.js";
4
5
  import { ensureScope, runInScope, setRequestLocale } from "@junejs/db";
5
6
  import { LoaderDataContext } from "@junejs/core/route";
7
+ import { siteShortName } from "@junejs/core/config";
6
8
  import React, { Suspense, use } from "react";
7
9
  import { renderToReadableStream } from "react-dom/server";
8
- import { Document } from "@junejs/core/document";
9
- import { apiCatalog, buildLinkHeader, llmsTxt, mcpServerCard, robotsTxt, sitemapXml } from "@junejs/core/discovery";
10
- import { mcpHandler, mcpTools } from "@junejs/core/mcp";
10
+ import { Document, PRERENDER_ORIGIN, documentTitle, pageCanonical } from "@junejs/core/document";
11
+ import { AGENT_SKILLS_INDEX_PATH, AI_CATALOG_PATH, API_CATALOG_CONTENT_TYPE, ARD_PATH, MCP_SERVER_CARD_TYPE, agentSkillsIndex, aiCatalog, apiCatalog, buildLinkHeader, llmsTxt, mcpServerCard, robotsTxt, siteSkill, sitemapXml } from "@junejs/core/discovery";
12
+ import { mcpHandler, mcpServerIdentity, mcpTools } from "@junejs/core/mcp";
13
+ import { OPENAPI_MEDIA_TYPE, apiActionId, apiHandler, apiNamespaceResponse, isApiNamespace, openApiDocument } from "@junejs/core/api";
11
14
  import { LOCALE_COOKIE, localeAlternates, localeDir, matchPinnedLocale, negotiateLocale } from "@junejs/core/i18n";
12
15
  //#region src/pipeline.ts
13
16
  function readCookie(request, name) {
@@ -20,6 +23,7 @@ function readCookie(request, name) {
20
23
  }
21
24
  }
22
25
  const DefaultNotFound = ({ pathname }) => React.createElement("main", null, React.createElement("h1", null, "404 — Not found"), React.createElement("p", null, pathname));
26
+ const CORS = { "access-control-allow-origin": "*" };
23
27
  function text(body, contentType, init) {
24
28
  const headers = new Headers(init?.headers);
25
29
  headers.set("content-type", contentType);
@@ -38,13 +42,49 @@ function StreamedView({ loadPromise, def, ctx }) {
38
42
  function letterFavicon(siteName) {
39
43
  return text(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="12" fill="#1d1d1f"/><text x="32" y="32" dy=".36em" text-anchor="middle" font-family="ui-sans-serif, system-ui, sans-serif" font-size="34" font-weight="600" fill="#fbfbf8">${(iconLetter(siteName) || "•").replace(/[<>&"']/g, "") || "•"}</text></svg>\n`, "image/svg+xml", { headers: { "cache-control": "public, max-age=86400" } });
40
44
  }
45
+ function markdownPath(pathname, isHome) {
46
+ const path = pathname.replace(/\/+$/, "");
47
+ return isHome ? `${path.replace(/\/index$/, "")}/index.md` : `${path}.md`;
48
+ }
49
+ function htmlPath(pathname, isHome) {
50
+ const path = pathname.endsWith(".md") ? pathname.slice(0, -3) : pathname;
51
+ if (!isHome) return path;
52
+ return path.replace(/\/+$/, "").replace(/\/index$/, "") || "/";
53
+ }
54
+ const SERVER_CARD_PATH = "/.well-known/mcp/server-card.json";
55
+ const SERVER_CARD_CORS = {
56
+ "access-control-allow-origin": "*",
57
+ "access-control-allow-methods": "GET, HEAD",
58
+ "access-control-allow-headers": "Content-Type, If-None-Match",
59
+ "access-control-expose-headers": "ETag"
60
+ };
61
+ function humanizeSegment(segment) {
62
+ let s = segment;
63
+ try {
64
+ s = decodeURIComponent(segment);
65
+ } catch {}
66
+ s = s.replace(/[-_]+/g, " ").trim();
67
+ return s.charAt(0).toUpperCase() + s.slice(1);
68
+ }
41
69
  const routePathOf = /* @__PURE__ */ new WeakMap();
70
+ function isDecodablePath(pathname) {
71
+ try {
72
+ for (const segment of pathname.split("/"));
73
+ return true;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
42
78
  function createPipeline(cfg) {
43
79
  const { docConfig, agent } = cfg;
44
80
  const NotFound = cfg.notFoundComponent ?? DefaultNotFound;
81
+ const publishesCatalogs = agent.discovery && !docConfig.basePath;
45
82
  function htmlHeaders() {
46
- const headers = new Headers({ "content-type": "text/html; charset=utf-8" });
47
- const links = [buildLinkHeader(agent), ...cfg.earlyHints ?? []].filter(Boolean);
83
+ const headers = new Headers({
84
+ "content-type": "text/html; charset=utf-8",
85
+ vary: "accept"
86
+ });
87
+ const links = [buildLinkHeader(agent, { catalogs: publishesCatalogs }), ...cfg.earlyHints ?? []].filter(Boolean);
48
88
  if (links.length) headers.set("link", links.join(", "));
49
89
  if (cfg.htmlCacheControl) headers.set("cache-control", cfg.htmlCacheControl);
50
90
  return headers;
@@ -65,18 +105,52 @@ function createPipeline(cfg) {
65
105
  protocol: ctx.url.protocol.replace(":", "")
66
106
  });
67
107
  }
68
- function pageProps(ctx) {
69
- const host = ctx.url.hostname.toLowerCase();
108
+ function pageProps(ctx, def) {
109
+ const isHome = routePathOf.get(ctx) === "/";
70
110
  return {
71
111
  pageUrl: ctx.url.href,
72
- isHome: routePathOf.get(ctx) === "/",
73
- onLocaleDomain: cfg.i18n ? Object.values(cfg.i18n.locales).some((l) => l.domain?.toLowerCase() === host) : false
112
+ isHome,
113
+ onLocaleDomain: onLocaleDomain(ctx),
114
+ markdownHref: agent.discovery && def.md !== false ? markdownPath(ctx.url.pathname, isHome) : void 0
74
115
  };
75
116
  }
117
+ async function breadcrumbsFor(ctx, meta) {
118
+ const routePath = routePathOf.get(ctx);
119
+ if (!routePath || routePath === "/" || meta?.breadcrumb === false) return void 0;
120
+ const urlPath = ctx.url.pathname.replace(/\/+$/, "") || "/";
121
+ const route = routePath.replace(/\/+$/, "");
122
+ const prefix = urlPath.endsWith(route) ? urlPath.slice(0, urlPath.length - route.length) : "";
123
+ const home = {
124
+ name: siteShortName(docConfig.site) ?? documentTitle(void 0, docConfig.site),
125
+ path: prefix || "/"
126
+ };
127
+ if (Array.isArray(meta?.breadcrumb)) return [home, ...meta.breadcrumb];
128
+ const segments = route.split("/").filter(Boolean);
129
+ const trail = [home];
130
+ for (let i = 1; i < segments.length; i++) {
131
+ const ancestor = "/" + segments.slice(0, i).join("/");
132
+ const r = await resolveRoute(ancestor);
133
+ if (!r || "handler" in r || Object.keys(r.params).length > 0) continue;
134
+ const title = typeof r.def.metadata === "object" ? r.def.metadata?.title : void 0;
135
+ trail.push({
136
+ name: title || humanizeSegment(segments[i - 1]),
137
+ path: prefix + ancestor
138
+ });
139
+ }
140
+ trail.push({
141
+ name: meta?.title || humanizeSegment(segments[segments.length - 1]),
142
+ path: urlPath
143
+ });
144
+ return trail;
145
+ }
146
+ function onLocaleDomain(ctx) {
147
+ const host = ctx.url.hostname.toLowerCase();
148
+ return cfg.i18n ? Object.values(cfg.i18n.locales).some((l) => l.domain?.toLowerCase() === host) : false;
149
+ }
76
150
  async function renderDocument(node, metadata, status, chain, locale, boundaryKey, alternates, page) {
77
151
  const wrapped = chain.reduceRight((acc, L) => React.createElement(L, null, acc), node);
78
152
  const stream = await renderToReadableStream(React.createElement(Document, {
79
- config: docConfigForRender(),
153
+ config: docConfigForRender(page?.pageUrl),
80
154
  metadata,
81
155
  children: wrapped,
82
156
  shellKey: boundaryKey,
@@ -92,7 +166,8 @@ function createPipeline(cfg) {
92
166
  }
93
167
  async function renderFragment(node, metadata, chain, boundaryIndex, boundaryKey) {
94
168
  const segmented = typeof boundaryIndex === "number" && boundaryKey != null;
95
- const stream = await renderToReadableStream((segmented ? chain.slice(boundaryIndex + 1) : chain).reduceRight((acc, L) => React.createElement(L, null, acc), node));
169
+ const wrapped = (segmented ? chain.slice(boundaryIndex + 1) : chain).reduceRight((acc, L) => React.createElement(L, null, acc), node);
170
+ const stream = await renderToReadableStream(wrapped);
96
171
  await stream.allReady;
97
172
  const html = await new Response(stream).text();
98
173
  const headers = new Headers({ "content-type": "text/html; charset=utf-8" });
@@ -104,14 +179,16 @@ function createPipeline(cfg) {
104
179
  headers
105
180
  });
106
181
  }
107
- function docConfigForRender() {
182
+ function docConfigForRender(pageUrl) {
108
183
  const webmcpTools = agent.webmcp && agent.mcp ? mcpTools() : null;
109
- return webmcpTools?.length ? {
184
+ const servesCatalog = !!pageUrl && catalogOrigin(new URL(pageUrl)) !== null;
185
+ return {
110
186
  ...docConfig,
111
- webmcpTools
112
- } : docConfig;
187
+ ...webmcpTools?.length ? { webmcpTools } : {},
188
+ ...servesCatalog ? { aiCatalog: AI_CATALOG_PATH } : {}
189
+ };
113
190
  }
114
- async function renderStreamingDocument(resolved, loadPromise, ctx) {
191
+ async function renderStreamingDocument(resolved, loadPromise, ctx, breadcrumbs) {
115
192
  const { def, chain, loading: Loading, boundaryKey } = resolved;
116
193
  const leaf = React.createElement(StreamedView, {
117
194
  loadPromise,
@@ -122,12 +199,13 @@ function createPipeline(cfg) {
122
199
  const wrapped = chain.reduceRight((acc, L) => React.createElement(L, null, acc), boundary);
123
200
  const metadata = typeof def.metadata === "object" ? def.metadata : void 0;
124
201
  const stream = await renderToReadableStream(React.createElement(Document, {
125
- config: docConfigForRender(),
202
+ config: docConfigForRender(ctx.url.href),
126
203
  metadata,
127
204
  children: wrapped,
128
205
  shellKey: boundaryKey,
129
206
  alternates: alternatesFor(ctx),
130
- ...pageProps(ctx),
207
+ ...pageProps(ctx, def),
208
+ breadcrumbs,
131
209
  ...langDir(ctx.locale)
132
210
  }), { onError: (e) => console.error("[june] streaming render error:", e) });
133
211
  return new Response(stream, {
@@ -135,23 +213,74 @@ function createPipeline(cfg) {
135
213
  headers: htmlHeaders()
136
214
  });
137
215
  }
138
- function notFoundResponse(target, pathname, locale) {
139
- return target !== "view" ? Response.json({
216
+ async function resolveRoute(pathname) {
217
+ return isDecodablePath(pathname) ? cfg.resolve(pathname) : null;
218
+ }
219
+ function notFoundResponse(target, pathname, locale, unmatched) {
220
+ if (unmatched && agent.api) {
221
+ const url = new URL(unmatched.url);
222
+ if (isApiNamespace(url.pathname)) return apiNamespaceResponse(unmatched, url.origin);
223
+ }
224
+ if (target === "md") return discoveryLinks().then((links) => text(notFoundMarkdown(pathname, links), "text/markdown; charset=utf-8", {
225
+ status: 404,
226
+ headers: { vary: "accept" }
227
+ }));
228
+ if (target !== "view") return discoveryLinks().then((links) => Response.json({
140
229
  error: "Not Found",
141
- path: pathname
142
- }, { status: 404 }) : renderDocument(React.createElement(NotFound, { pathname }), {
230
+ code: "not_found",
231
+ path: pathname,
232
+ hint: notFoundHint(links)
233
+ }, {
234
+ status: 404,
235
+ headers: { vary: "accept" }
236
+ }));
237
+ return renderDocument(React.createElement(NotFound, { pathname }), {
143
238
  title: "Not found",
144
239
  robots: "noindex"
145
240
  }, 404, [], locale);
146
241
  }
242
+ async function discoveryLinks() {
243
+ const links = [];
244
+ if (agent.discovery) links.push(["llms.txt — the site index for agents", "/llms.txt"], ["sitemap.xml — every page", "/sitemap.xml"]);
245
+ if (agent.mcp) links.push(["MCP server — this site's tools", "/mcp"]);
246
+ if (agent.api) links.push(["OpenAPI — the same tools over HTTP", "/openapi.json"]);
247
+ const home = await cfg.resolve("/");
248
+ if (home && "def" in home && home.def.md !== false) links.push(["Home page (Markdown)", "/index.md"]);
249
+ return links;
250
+ }
251
+ function notFoundHint(links) {
252
+ return links.length ? `No page at this path. Start from ${links.map(([, h]) => h).join(", ")}.` : "No page at this path.";
253
+ }
254
+ function notFoundMarkdown(pathname, links) {
255
+ const site = docConfig.site.name ? ` on ${docConfig.site.name}` : "";
256
+ return [
257
+ "# 404 — Not found",
258
+ "",
259
+ links.length ? `There is no page at \`${pathname}\`${site}. Try one of these instead:` : `There is no page at \`${pathname}\`${site}.`,
260
+ "",
261
+ ...links.map(([label, href]) => `- [${label}](${href})`),
262
+ ""
263
+ ].join("\n");
264
+ }
147
265
  function resolveMeta(def, data, ctx) {
148
266
  return typeof def.metadata === "function" ? def.metadata(data, ctx) : def.metadata;
149
267
  }
150
268
  async function renderMarkdown(def, data, ctx) {
151
269
  const jsonData = typeof def.json === "function" ? await def.json(data, ctx) : def.json === false ? null : data;
152
- const body = typeof def.md === "function" ? await def.md(data, ctx) : "```json\n" + JSON.stringify(jsonData, null, 2) + "\n```\n";
270
+ const body = withFrontmatter(typeof def.md === "function" ? await def.md(data, ctx) : "```json\n" + JSON.stringify(jsonData, null, 2) + "\n```\n", resolveMeta(def, data, ctx), ctx);
153
271
  return text(body, "text/markdown; charset=utf-8", { headers: { "x-markdown-tokens": String(Math.ceil(body.length / 4)) } });
154
272
  }
273
+ function withFrontmatter(body, meta, ctx) {
274
+ if (/^---\r?\n/.test(body)) return body;
275
+ const title = meta?.title || documentTitle(meta, docConfig.site);
276
+ const description = meta?.description ?? docConfig.site.description;
277
+ const canonical = pageCanonical(docConfig, meta, ctx.url.href, htmlPath(ctx.url.pathname, routePathOf.get(ctx) === "/"), onLocaleDomain(ctx));
278
+ const lines = ["---", `title: ${JSON.stringify(title)}`];
279
+ if (description) lines.push(`description: ${JSON.stringify(description)}`);
280
+ if (canonical) lines.push(`canonical: ${JSON.stringify(canonical)}`);
281
+ lines.push("---", "");
282
+ return lines.join("\n") + body;
283
+ }
155
284
  async function renderProjection(resolved, target, data, ctx) {
156
285
  const { def, chain, boundaryIndex, boundaryKey } = resolved;
157
286
  if (target !== "fragment" && def[target] === false) return notFoundResponse(target, ctx.url.pathname, ctx.locale);
@@ -168,15 +297,74 @@ function createPipeline(cfg) {
168
297
  const node = provideLoaderData(data, def.view ? def.view(data, ctx) : null);
169
298
  const meta = resolveMeta(def, data, ctx);
170
299
  if (target === "fragment") return renderFragment(node, meta, chain, boundaryIndex, boundaryKey);
171
- return renderDocument(node, meta, 200, chain, ctx.locale, boundaryKey, alternatesFor(ctx), pageProps(ctx));
300
+ const page = {
301
+ ...pageProps(ctx, def),
302
+ breadcrumbs: await breadcrumbsFor(ctx, meta)
303
+ };
304
+ return renderDocument(node, meta, 200, chain, ctx.locale, boundaryKey, alternatesFor(ctx), page);
305
+ }
306
+ function catalogOrigin(url) {
307
+ return publishesCatalogs ? publicOrigin(url) : null;
308
+ }
309
+ function publicOrigin(url) {
310
+ if (url.origin !== PRERENDER_ORIGIN) return url.origin;
311
+ return docConfig.site.url ? new URL(docConfig.site.url).origin : docConfig.deployOrigin ?? null;
312
+ }
313
+ function publicBase(url) {
314
+ const origin = publicOrigin(url);
315
+ return origin === null ? null : origin + (docConfig.basePath ?? "");
316
+ }
317
+ async function agentCatalog(url) {
318
+ const { pathname } = url;
319
+ if (!pathname.startsWith("/.well-known/")) return null;
320
+ const origin = catalogOrigin(url);
321
+ if (!origin) return null;
322
+ if (pathname === AGENT_SKILLS_INDEX_PATH) return Response.json(await agentSkillsIndex(origin, agent, docConfig.site), { headers: CORS });
323
+ if (pathname === AI_CATALOG_PATH || pathname === ARD_PATH) return Response.json(aiCatalog(origin, agent, docConfig.site), { headers: CORS });
324
+ const skill = siteSkill(origin, agent, docConfig.site);
325
+ if (pathname === new URL(skill.url).pathname) return text(skill.markdown, "text/markdown; charset=utf-8", { headers: CORS });
326
+ if (pathname.startsWith("/.well-known/agent-skills/")) return Response.json({
327
+ error: "Not Found",
328
+ path: pathname
329
+ }, {
330
+ status: 404,
331
+ headers: CORS
332
+ });
333
+ return null;
172
334
  }
173
335
  async function discovery(url) {
336
+ const catalog = await agentCatalog(url);
337
+ if (catalog) return catalog;
174
338
  switch (url.pathname) {
175
- case "/llms.txt": return text(llmsTxt(url.origin, await cfg.routeList(), agent, docConfig.site), "text/markdown; charset=utf-8");
176
- case "/robots.txt": return text(robotsTxt(url.origin), "text/plain; charset=utf-8");
177
- case "/sitemap.xml": return text(sitemapXml(url.origin, await cfg.routeList(), cfg.i18n), "application/xml; charset=utf-8");
178
- case "/.well-known/api-catalog": return text(JSON.stringify(apiCatalog(url.origin, agent)), "application/linkset+json");
179
- case "/.well-known/mcp/server-card.json": return agent.mcp ? Response.json(mcpServerCard(url.origin)) : null;
339
+ case "/llms.txt": {
340
+ const routes = await cfg.routeList();
341
+ const base = publicBase(url) ?? docConfig.basePath ?? "";
342
+ const links = await collectLlmsLinks(base, routes, cfg.resolve);
343
+ return text(llmsTxt(base, routes, agent, docConfig.site, links), "text/markdown; charset=utf-8");
344
+ }
345
+ case "/robots.txt": {
346
+ const base = publicBase(url);
347
+ if (base === null) return text("Not Found\n", "text/plain; charset=utf-8", { status: 404 });
348
+ return text(robotsTxt(base, { catalogs: catalogOrigin(url) !== null }), "text/plain; charset=utf-8");
349
+ }
350
+ case "/sitemap.xml": {
351
+ const base = publicBase(url);
352
+ if (base === null) return text("Not Found\n", "text/plain; charset=utf-8", { status: 404 });
353
+ return text(sitemapXml(base, await collectSitemapPages(await cfg.routeList(), cfg.resolve, {
354
+ i18n: !!cfg.i18n,
355
+ staticPaths: cfg.staticBuild === true
356
+ }), cfg.i18n), "application/xml; charset=utf-8");
357
+ }
358
+ case "/.well-known/api-catalog": return text(JSON.stringify(apiCatalog(url.origin, agent)), API_CATALOG_CONTENT_TYPE, { headers: { link: `</.well-known/api-catalog>; rel="api-catalog"` } });
359
+ case SERVER_CARD_PATH: return agent.mcp ? text(JSON.stringify(mcpServerCard(url.origin, {
360
+ site: docConfig.site,
361
+ agent,
362
+ icons: docConfig.icons,
363
+ basePath: docConfig.basePath
364
+ })), MCP_SERVER_CARD_TYPE, { headers: {
365
+ ...SERVER_CARD_CORS,
366
+ "cache-control": "public, max-age=3600"
367
+ } }) : null;
180
368
  default: return null;
181
369
  }
182
370
  }
@@ -184,14 +372,45 @@ function createPipeline(cfg) {
184
372
  const url = new URL(request.url);
185
373
  if (url.pathname === "/mcp") {
186
374
  if (!agent.mcp) return notFoundResponse("view", url.pathname);
375
+ const identity = cfg.identity ? await cfg.identity(request) : void 0;
187
376
  return mcpHandler(request, {
188
377
  request,
189
- ...cfg.identity ? await cfg.identity(request) : void 0
190
- });
378
+ ...identity
379
+ }, mcpServerIdentity(url.origin, {
380
+ site: docConfig.site,
381
+ agent
382
+ }));
191
383
  }
192
- if (request.method === "GET" && agent.discovery) {
384
+ if (agent.api) {
385
+ if (url.pathname === "/openapi.json" && (request.method === "GET" || request.method === "HEAD")) {
386
+ const doc = Response.json(openApiDocument(url.origin, docConfig.site), { headers: {
387
+ "content-type": OPENAPI_MEDIA_TYPE,
388
+ "access-control-allow-origin": "*"
389
+ } });
390
+ return request.method === "HEAD" ? new Response(null, {
391
+ status: doc.status,
392
+ headers: doc.headers
393
+ }) : doc;
394
+ }
395
+ const actionId = apiActionId(url.pathname);
396
+ if (actionId) {
397
+ const identity = cfg.identity ? await cfg.identity(request) : void 0;
398
+ return apiHandler(request, actionId, {
399
+ request,
400
+ ...identity
401
+ });
402
+ }
403
+ }
404
+ if (request.method === "OPTIONS" && agent.discovery && agent.mcp && url.pathname === SERVER_CARD_PATH) return new Response(null, {
405
+ status: 204,
406
+ headers: SERVER_CARD_CORS
407
+ });
408
+ if ((request.method === "GET" || request.method === "HEAD") && agent.discovery) {
193
409
  const d = await discovery(url);
194
- if (d) return d;
410
+ if (d) return request.method === "HEAD" ? new Response(null, {
411
+ status: d.status,
412
+ headers: d.headers
413
+ }) : d;
195
414
  }
196
415
  if (agent.runtime.enabled && cfg.agentSurface) {
197
416
  const a = await cfg.agentSurface(request, url);
@@ -223,8 +442,8 @@ function createPipeline(cfg) {
223
442
  if (locale) setRequestLocale(locale);
224
443
  }
225
444
  const { target, pathname, speculative } = negotiate(url, request, routeBase);
226
- const resolved = await cfg.resolve(pathname);
227
- if (!resolved) return notFoundResponse(target, pathname, locale);
445
+ const resolved = await resolveRoute(pathname);
446
+ if (!resolved) return notFoundResponse(target, pathname, locale, request);
228
447
  const ctx = {
229
448
  request,
230
449
  url,
@@ -235,7 +454,10 @@ function createPipeline(cfg) {
235
454
  };
236
455
  routePathOf.set(ctx, pathname);
237
456
  if ("handler" in resolved) return resolved.handler(request, ctx);
238
- if (target === "view" && resolved.loading && typeof resolved.def.metadata !== "function") return renderStreamingDocument(resolved, Promise.resolve(resolved.def.load ? resolved.def.load(ctx) : void 0), ctx);
457
+ if (target === "view" && resolved.loading && typeof resolved.def.metadata !== "function") {
458
+ const breadcrumbs = await breadcrumbsFor(ctx, resolved.def.metadata);
459
+ return renderStreamingDocument(resolved, Promise.resolve(resolved.def.load ? resolved.def.load(ctx) : void 0), ctx, breadcrumbs);
460
+ }
239
461
  let data;
240
462
  try {
241
463
  data = resolved.def.load ? await resolved.def.load(ctx) : void 0;
@@ -246,11 +468,13 @@ function createPipeline(cfg) {
246
468
  }
247
469
  return { async fetch(request) {
248
470
  await ensureScope();
471
+ const resources = cfg.resources ? await cfg.resources() : {};
472
+ const services = cfg.services ? await cfg.services() : void 0;
249
473
  return runInScope({
250
- resources: cfg.resources ? await cfg.resources() : {},
251
- services: cfg.services ? await cfg.services() : void 0
474
+ resources,
475
+ services
252
476
  }, () => handleRequest(request));
253
477
  } };
254
478
  }
255
479
  //#endregion
256
- export { createPipeline };
480
+ export { createPipeline, htmlPath, isDecodablePath, markdownPath };
@@ -1,15 +1,14 @@
1
1
  import { t as D1Database } from "./d1-BkdUg9kg.js";
2
2
  import { ResourceConfig, Resources } from "@junejs/core/resources";
3
3
  //#region src/resources.d.ts
4
- declare function memoizeResources(config?: ResourceConfig): () => Promise<Resources>;
5
- type WorkerEnv = {
4
+ export declare function memoizeResources(config?: ResourceConfig): () => Promise<Resources>;
5
+ export type WorkerEnv = {
6
6
  DB?: D1Database;
7
7
  } & Record<string, unknown>;
8
- type ResourceFlags = {
8
+ export type ResourceFlags = {
9
9
  db?: boolean;
10
10
  kv?: boolean;
11
11
  blob?: boolean;
12
12
  };
13
- declare function bindWorkerResources(flags: ResourceFlags): (env?: unknown) => Promise<Resources>;
14
- //#endregion
15
- export { ResourceFlags, WorkerEnv, bindWorkerResources, memoizeResources };
13
+ export declare function bindWorkerResources(flags: ResourceFlags): (env?: unknown) => Promise<Resources>;
14
+ //#endregion
package/dist/router.d.ts CHANGED
@@ -1,32 +1,31 @@
1
1
  //#region src/router.d.ts
2
- type RouteMatch = {
2
+ export type RouteMatch = {
3
3
  file: string;
4
4
  params: Record<string, string>;
5
5
  };
6
- type SegmentMatch = {
6
+ export type SegmentMatch = {
7
7
  dir: string;
8
8
  layout?: string;
9
9
  loading?: string;
10
10
  error?: string;
11
11
  notFound?: string;
12
12
  };
13
- type RouteTreeMatch = {
13
+ export type RouteTreeMatch = {
14
14
  file: string;
15
15
  params: Record<string, string>;
16
16
  segments: SegmentMatch[];
17
17
  };
18
- declare function findMiddlewareFile(appDir: string): string | null;
19
- type MatchOptions = {
18
+ export declare function findMiddlewareFile(appDir: string): string | null;
19
+ export type MatchOptions = {
20
20
  pageConvention?: boolean;
21
21
  };
22
- declare function isResourceFile(file: string): boolean;
23
- declare function matchRouteTree(appDir: string, pathname: string, options?: MatchOptions): Promise<RouteTreeMatch | null>;
24
- declare function resolveNotFound(appDir: string, pathname: string): Promise<{
22
+ export declare function isResourceFile(file: string): boolean;
23
+ export declare function matchRouteTree(appDir: string, pathname: string, options?: MatchOptions): Promise<RouteTreeMatch | null>;
24
+ export declare function resolveNotFound(appDir: string, pathname: string): Promise<{
25
25
  segments: SegmentMatch[];
26
26
  notFound?: string;
27
27
  }>;
28
- declare function routeFiles(appDir: string, options?: MatchOptions): Promise<string[]>;
29
- declare function listRoutes(appDir: string, options?: MatchOptions): Promise<string[]>;
30
- declare function matchRoute(appDir: string, pathname: string, options?: MatchOptions): Promise<RouteMatch | null>;
31
- //#endregion
32
- export { MatchOptions, RouteMatch, RouteTreeMatch, SegmentMatch, findMiddlewareFile, isResourceFile, listRoutes, matchRoute, matchRouteTree, resolveNotFound, routeFiles };
28
+ export declare function routeFiles(appDir: string, options?: MatchOptions): Promise<string[]>;
29
+ export declare function listRoutes(appDir: string, options?: MatchOptions): Promise<string[]>;
30
+ export declare function matchRoute(appDir: string, pathname: string, options?: MatchOptions): Promise<RouteMatch | null>;
31
+ //#endregion
@@ -1,17 +1,16 @@
1
1
  import { DocumentConfig } from "@junejs/core/document";
2
2
  //#region src/rsc-build.d.ts
3
- type RscRoute = {
3
+ export type RscRoute = {
4
4
  path: string;
5
5
  file: string;
6
6
  };
7
- declare function findRscRoutes(appDir: string): RscRoute[];
8
- type RscBuildResult = {
7
+ export declare function findRscRoutes(appDir: string): RscRoute[];
8
+ export type RscBuildResult = {
9
9
  worker: string;
10
10
  server: string;
11
11
  paths: string[];
12
12
  } | null;
13
- declare function buildRsc(appRoot: string, outDir: string, docConfig: DocumentConfig): Promise<RscBuildResult>;
13
+ export declare function buildRsc(appRoot: string, outDir: string, docConfig: DocumentConfig): Promise<RscBuildResult>;
14
14
  type Fetcher = (request: Request) => Promise<Response>;
15
- declare function createRscDispatch(ssrFetch: Fetcher, rscFetch: Fetcher, rscPaths: Iterable<string>): Fetcher;
16
- //#endregion
17
- export { RscBuildResult, RscRoute, buildRsc, createRscDispatch, findRscRoutes };
15
+ export declare function createRscDispatch(ssrFetch: Fetcher, rscFetch: Fetcher, rscPaths: Iterable<string>): Fetcher;
16
+ //#endregion
@@ -1,8 +1,7 @@
1
1
  //#region src/rsc-bundle.d.ts
2
- type RscGraph = "server" | "ssr";
3
- type RscAliases = Record<string, string>;
4
- declare function bundleServerGraph(entryFile: string, cwd: string, aliases?: RscAliases, appDir?: string): Promise<string>;
5
- declare function bundleSsrGraph(entryFile: string, cwd: string, aliases?: RscAliases): Promise<string>;
6
- declare function referencesNodeBuiltins(code: string): boolean;
7
- //#endregion
8
- export { RscAliases, RscGraph, bundleServerGraph, bundleSsrGraph, referencesNodeBuiltins };
2
+ export type RscGraph = "server" | "ssr";
3
+ export type RscAliases = Record<string, string>;
4
+ export declare function bundleServerGraph(entryFile: string, cwd: string, aliases?: RscAliases, appDir?: string): Promise<string>;
5
+ export declare function bundleSsrGraph(entryFile: string, cwd: string, aliases?: RscAliases): Promise<string>;
6
+ export declare function referencesNodeBuiltins(code: string): boolean;
7
+ //#endregion
@@ -1,8 +1,7 @@
1
1
  import { Plugin } from "rolldown";
2
2
  //#region src/rsc-manifest.d.ts
3
- declare const RSC_SERVER_MANIFEST_FILE = "_rsc-server.gen.ts";
4
- declare const RSC_CLIENT_MANIFEST_FILE = "_rsc-client.gen.ts";
5
- declare function rscClientReferencesPlugin(appDir: string): Plugin;
6
- declare function generateRscManifests(appDir: string): number;
7
- //#endregion
8
- export { RSC_CLIENT_MANIFEST_FILE, RSC_SERVER_MANIFEST_FILE, generateRscManifests, rscClientReferencesPlugin };
3
+ export declare const RSC_SERVER_MANIFEST_FILE = "_rsc-server.gen.ts";
4
+ export declare const RSC_CLIENT_MANIFEST_FILE = "_rsc-client.gen.ts";
5
+ export declare function rscClientReferencesPlugin(appDir: string): Plugin;
6
+ export declare function generateRscManifests(appDir: string): number;
7
+ //#endregion
@@ -1,5 +1,4 @@
1
1
  //#region src/rsc-runtime/flight-render-routes.d.ts
2
- declare function renderFlight(path: string): Promise<string | null>;
3
- declare function rscPaths(): string[];
4
- //#endregion
5
- export { renderFlight, rscPaths };
2
+ export declare function renderFlight(path: string): Promise<string | null>;
3
+ export declare function rscPaths(): string[];
4
+ //#endregion
@@ -1,4 +1,3 @@
1
1
  //#region src/rsc-runtime/flight-render.d.ts
2
- declare function renderFlight(): Promise<string>;
3
- //#endregion
4
- export { renderFlight };
2
+ export declare function renderFlight(): Promise<string>;
3
+ //#endregion
@@ -1,4 +1,3 @@
1
1
  //#region src/rsc-runtime/flight-to-html.d.ts
2
- declare function flightToHtml(flight: string, moduleMap?: Record<string, unknown>): Promise<string>;
3
- //#endregion
4
- export { flightToHtml };
2
+ export declare function flightToHtml(flight: string, moduleMap?: Record<string, unknown>): Promise<string>;
3
+ //#endregion
@@ -20,11 +20,12 @@ async function streamToString(stream) {
20
20
  }
21
21
  async function flightToHtml(flight, moduleMap = {}) {
22
22
  const flightStream = new Response(flight).body;
23
- const html = await renderToReadableStream(/* @__PURE__ */ jsx(Root, { tree: createFromReadableStream(flightStream, { serverConsumerManifest: {
23
+ const tree = createFromReadableStream(flightStream, { serverConsumerManifest: {
24
24
  moduleMap,
25
25
  serverModuleMap: null,
26
26
  moduleLoading: null
27
- } }) }));
27
+ } });
28
+ const html = await renderToReadableStream(/* @__PURE__ */ jsx(Root, { tree }));
28
29
  if (typeof html.allReady?.then === "function") await html.allReady;
29
30
  return streamToString(html);
30
31
  }
@@ -39,4 +39,5 @@ declare module "june:rsc-client" {
39
39
  declare module "june:rsc-config" {
40
40
  import type { DocumentConfig } from "@junejs/core/document";
41
41
  export const DOC_CONFIG: DocumentConfig;
42
- }
42
+ }
43
+ export {};
package/dist/segment.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  //#region src/segment.d.ts
2
- declare const boundaryWarning: (file: string) => string;
3
- declare function boundaryKey(file: string): string;
4
- declare function resolveBoundary<C>(items: Array<{
2
+ export declare const boundaryWarning: (file: string) => string;
3
+ export declare function boundaryKey(file: string): string;
4
+ export declare function resolveBoundary<C>(items: Array<{
5
5
  file: string;
6
6
  entry: C | null;
7
7
  boundary: boolean;
@@ -10,5 +10,4 @@ declare function resolveBoundary<C>(items: Array<{
10
10
  boundaryIndex: number | null;
11
11
  key: string | null;
12
12
  };
13
- //#endregion
14
- export { boundaryKey, boundaryWarning, resolveBoundary };
13
+ //#endregion