@junejs/server 1.0.0-dev.29 → 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 +8 -5
  39. package/dist/llms-links.js +31 -1
  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 +260 -41
  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 +46 -1
  72. package/src/negotiate.ts +39 -15
  73. package/src/pipeline.ts +433 -45
  74. package/src/web-manifest.ts +2 -6
  75. package/src/worker.ts +55 -17
package/dist/pipeline.js CHANGED
@@ -1,14 +1,16 @@
1
1
  import { iconLetter } from "./icon-letter.js";
2
- import { collectLlmsLinks } from "./llms-links.js";
2
+ import { collectLlmsLinks, collectSitemapPages } from "./llms-links.js";
3
3
  import { webManifest } from "./web-manifest.js";
4
4
  import { SEGMENT_HEADER, TITLE_HEADER, encodeTitle, negotiate } from "./negotiate.js";
5
5
  import { ensureScope, runInScope, setRequestLocale } from "@junejs/db";
6
6
  import { LoaderDataContext } from "@junejs/core/route";
7
+ import { siteShortName } from "@junejs/core/config";
7
8
  import React, { Suspense, use } from "react";
8
9
  import { renderToReadableStream } from "react-dom/server";
9
- import { Document } from "@junejs/core/document";
10
- import { apiCatalog, buildLinkHeader, llmsTxt, mcpServerCard, robotsTxt, sitemapXml } from "@junejs/core/discovery";
11
- 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";
12
14
  import { LOCALE_COOKIE, localeAlternates, localeDir, matchPinnedLocale, negotiateLocale } from "@junejs/core/i18n";
13
15
  //#region src/pipeline.ts
14
16
  function readCookie(request, name) {
@@ -21,6 +23,7 @@ function readCookie(request, name) {
21
23
  }
22
24
  }
23
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": "*" };
24
27
  function text(body, contentType, init) {
25
28
  const headers = new Headers(init?.headers);
26
29
  headers.set("content-type", contentType);
@@ -39,13 +42,49 @@ function StreamedView({ loadPromise, def, ctx }) {
39
42
  function letterFavicon(siteName) {
40
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" } });
41
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
+ }
42
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
+ }
43
78
  function createPipeline(cfg) {
44
79
  const { docConfig, agent } = cfg;
45
80
  const NotFound = cfg.notFoundComponent ?? DefaultNotFound;
81
+ const publishesCatalogs = agent.discovery && !docConfig.basePath;
46
82
  function htmlHeaders() {
47
- const headers = new Headers({ "content-type": "text/html; charset=utf-8" });
48
- 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);
49
88
  if (links.length) headers.set("link", links.join(", "));
50
89
  if (cfg.htmlCacheControl) headers.set("cache-control", cfg.htmlCacheControl);
51
90
  return headers;
@@ -66,18 +105,52 @@ function createPipeline(cfg) {
66
105
  protocol: ctx.url.protocol.replace(":", "")
67
106
  });
68
107
  }
69
- function pageProps(ctx) {
70
- const host = ctx.url.hostname.toLowerCase();
108
+ function pageProps(ctx, def) {
109
+ const isHome = routePathOf.get(ctx) === "/";
71
110
  return {
72
111
  pageUrl: ctx.url.href,
73
- isHome: routePathOf.get(ctx) === "/",
74
- 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
75
115
  };
76
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
+ }
77
150
  async function renderDocument(node, metadata, status, chain, locale, boundaryKey, alternates, page) {
78
151
  const wrapped = chain.reduceRight((acc, L) => React.createElement(L, null, acc), node);
79
152
  const stream = await renderToReadableStream(React.createElement(Document, {
80
- config: docConfigForRender(),
153
+ config: docConfigForRender(page?.pageUrl),
81
154
  metadata,
82
155
  children: wrapped,
83
156
  shellKey: boundaryKey,
@@ -93,7 +166,8 @@ function createPipeline(cfg) {
93
166
  }
94
167
  async function renderFragment(node, metadata, chain, boundaryIndex, boundaryKey) {
95
168
  const segmented = typeof boundaryIndex === "number" && boundaryKey != null;
96
- 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);
97
171
  await stream.allReady;
98
172
  const html = await new Response(stream).text();
99
173
  const headers = new Headers({ "content-type": "text/html; charset=utf-8" });
@@ -105,14 +179,16 @@ function createPipeline(cfg) {
105
179
  headers
106
180
  });
107
181
  }
108
- function docConfigForRender() {
182
+ function docConfigForRender(pageUrl) {
109
183
  const webmcpTools = agent.webmcp && agent.mcp ? mcpTools() : null;
110
- return webmcpTools?.length ? {
184
+ const servesCatalog = !!pageUrl && catalogOrigin(new URL(pageUrl)) !== null;
185
+ return {
111
186
  ...docConfig,
112
- webmcpTools
113
- } : docConfig;
187
+ ...webmcpTools?.length ? { webmcpTools } : {},
188
+ ...servesCatalog ? { aiCatalog: AI_CATALOG_PATH } : {}
189
+ };
114
190
  }
115
- async function renderStreamingDocument(resolved, loadPromise, ctx) {
191
+ async function renderStreamingDocument(resolved, loadPromise, ctx, breadcrumbs) {
116
192
  const { def, chain, loading: Loading, boundaryKey } = resolved;
117
193
  const leaf = React.createElement(StreamedView, {
118
194
  loadPromise,
@@ -123,12 +199,13 @@ function createPipeline(cfg) {
123
199
  const wrapped = chain.reduceRight((acc, L) => React.createElement(L, null, acc), boundary);
124
200
  const metadata = typeof def.metadata === "object" ? def.metadata : void 0;
125
201
  const stream = await renderToReadableStream(React.createElement(Document, {
126
- config: docConfigForRender(),
202
+ config: docConfigForRender(ctx.url.href),
127
203
  metadata,
128
204
  children: wrapped,
129
205
  shellKey: boundaryKey,
130
206
  alternates: alternatesFor(ctx),
131
- ...pageProps(ctx),
207
+ ...pageProps(ctx, def),
208
+ breadcrumbs,
132
209
  ...langDir(ctx.locale)
133
210
  }), { onError: (e) => console.error("[june] streaming render error:", e) });
134
211
  return new Response(stream, {
@@ -136,23 +213,74 @@ function createPipeline(cfg) {
136
213
  headers: htmlHeaders()
137
214
  });
138
215
  }
139
- function notFoundResponse(target, pathname, locale) {
140
- 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({
141
229
  error: "Not Found",
142
- path: pathname
143
- }, { 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 }), {
144
238
  title: "Not found",
145
239
  robots: "noindex"
146
240
  }, 404, [], locale);
147
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
+ }
148
265
  function resolveMeta(def, data, ctx) {
149
266
  return typeof def.metadata === "function" ? def.metadata(data, ctx) : def.metadata;
150
267
  }
151
268
  async function renderMarkdown(def, data, ctx) {
152
269
  const jsonData = typeof def.json === "function" ? await def.json(data, ctx) : def.json === false ? null : data;
153
- 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);
154
271
  return text(body, "text/markdown; charset=utf-8", { headers: { "x-markdown-tokens": String(Math.ceil(body.length / 4)) } });
155
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
+ }
156
284
  async function renderProjection(resolved, target, data, ctx) {
157
285
  const { def, chain, boundaryIndex, boundaryKey } = resolved;
158
286
  if (target !== "fragment" && def[target] === false) return notFoundResponse(target, ctx.url.pathname, ctx.locale);
@@ -169,19 +297,74 @@ function createPipeline(cfg) {
169
297
  const node = provideLoaderData(data, def.view ? def.view(data, ctx) : null);
170
298
  const meta = resolveMeta(def, data, ctx);
171
299
  if (target === "fragment") return renderFragment(node, meta, chain, boundaryIndex, boundaryKey);
172
- 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;
173
334
  }
174
335
  async function discovery(url) {
336
+ const catalog = await agentCatalog(url);
337
+ if (catalog) return catalog;
175
338
  switch (url.pathname) {
176
339
  case "/llms.txt": {
177
340
  const routes = await cfg.routeList();
178
- const links = await collectLlmsLinks(url.origin, routes, cfg.resolve);
179
- return text(llmsTxt(url.origin, routes, agent, docConfig.site, links), "text/markdown; charset=utf-8");
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");
180
357
  }
181
- case "/robots.txt": return text(robotsTxt(url.origin), "text/plain; charset=utf-8");
182
- case "/sitemap.xml": return text(sitemapXml(url.origin, await cfg.routeList(), cfg.i18n), "application/xml; charset=utf-8");
183
- case "/.well-known/api-catalog": return text(JSON.stringify(apiCatalog(url.origin, agent)), "application/linkset+json");
184
- case "/.well-known/mcp/server-card.json": return agent.mcp ? Response.json(mcpServerCard(url.origin)) : null;
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;
185
368
  default: return null;
186
369
  }
187
370
  }
@@ -189,14 +372,45 @@ function createPipeline(cfg) {
189
372
  const url = new URL(request.url);
190
373
  if (url.pathname === "/mcp") {
191
374
  if (!agent.mcp) return notFoundResponse("view", url.pathname);
375
+ const identity = cfg.identity ? await cfg.identity(request) : void 0;
192
376
  return mcpHandler(request, {
193
377
  request,
194
- ...cfg.identity ? await cfg.identity(request) : void 0
195
- });
378
+ ...identity
379
+ }, mcpServerIdentity(url.origin, {
380
+ site: docConfig.site,
381
+ agent
382
+ }));
383
+ }
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
+ }
196
403
  }
197
- if (request.method === "GET" && agent.discovery) {
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) {
198
409
  const d = await discovery(url);
199
- if (d) return d;
410
+ if (d) return request.method === "HEAD" ? new Response(null, {
411
+ status: d.status,
412
+ headers: d.headers
413
+ }) : d;
200
414
  }
201
415
  if (agent.runtime.enabled && cfg.agentSurface) {
202
416
  const a = await cfg.agentSurface(request, url);
@@ -228,8 +442,8 @@ function createPipeline(cfg) {
228
442
  if (locale) setRequestLocale(locale);
229
443
  }
230
444
  const { target, pathname, speculative } = negotiate(url, request, routeBase);
231
- const resolved = await cfg.resolve(pathname);
232
- if (!resolved) return notFoundResponse(target, pathname, locale);
445
+ const resolved = await resolveRoute(pathname);
446
+ if (!resolved) return notFoundResponse(target, pathname, locale, request);
233
447
  const ctx = {
234
448
  request,
235
449
  url,
@@ -240,7 +454,10 @@ function createPipeline(cfg) {
240
454
  };
241
455
  routePathOf.set(ctx, pathname);
242
456
  if ("handler" in resolved) return resolved.handler(request, ctx);
243
- 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
+ }
244
461
  let data;
245
462
  try {
246
463
  data = resolved.def.load ? await resolved.def.load(ctx) : void 0;
@@ -251,11 +468,13 @@ function createPipeline(cfg) {
251
468
  }
252
469
  return { async fetch(request) {
253
470
  await ensureScope();
471
+ const resources = cfg.resources ? await cfg.resources() : {};
472
+ const services = cfg.services ? await cfg.services() : void 0;
254
473
  return runInScope({
255
- resources: cfg.resources ? await cfg.resources() : {},
256
- services: cfg.services ? await cfg.services() : void 0
474
+ resources,
475
+ services
257
476
  }, () => handleRequest(request));
258
477
  } };
259
478
  }
260
479
  //#endregion
261
- 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