@junejs/server 1.0.0-dev.36 → 1.0.0-dev.45
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.
- package/dist/agent-durable.d.ts +19 -2
- package/dist/agent-durable.js +118 -7
- package/dist/agent-native.d.ts +13 -2
- package/dist/agent-native.js +108 -6
- package/dist/app.d.ts +1 -0
- package/dist/app.js +10 -1
- package/dist/build.d.ts +3 -3
- package/dist/build.js +8 -2
- package/dist/{config-freeze-ZWC_Qd4h.d.ts → config-freeze-CyXvNqi2.d.ts} +1 -1
- package/dist/config-freeze.d.ts +1 -1
- package/dist/core-version.js +1 -1
- package/dist/dev.d.ts +6 -2
- package/dist/dev.js +40 -9
- package/dist/host.d.ts +1 -0
- package/dist/host.js +2 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/{manifest-DSNX4I-G.d.ts → manifest-CwTT1zVs.d.ts} +1 -1
- package/dist/manifest.d.ts +1 -1
- package/dist/manifest.js +3 -1
- package/dist/pipeline.js +15 -6
- package/dist/route-rank.d.ts +10 -0
- package/dist/route-rank.js +38 -0
- package/dist/route-scan-BMbBzPIB.d.ts +20 -0
- package/dist/route-scan.d.ts +2 -2
- package/dist/route-scan.js +47 -2
- package/dist/router.js +58 -44
- package/dist/{worker-Dv4l6Ybs.d.ts → worker-2qbQjj2c.d.ts} +1 -0
- package/dist/worker.d.ts +1 -1
- package/dist/worker.js +52 -76
- package/package.json +3 -3
- package/src/agent-durable.ts +164 -11
- package/src/agent-native.ts +161 -7
- package/src/app.ts +22 -1
- package/src/build.ts +11 -1
- package/src/core-version.ts +1 -1
- package/src/dev.ts +41 -8
- package/src/host.ts +4 -3
- package/src/manifest.ts +2 -0
- package/src/pipeline.ts +20 -4
- package/src/route-rank.ts +60 -0
- package/src/route-scan.ts +78 -1
- package/src/router.ts +75 -58
- package/src/worker.ts +66 -82
- package/dist/route-scan-DMlwoESy.d.ts +0 -13
package/dist/core-version.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as coreRuntime from "@junejs/core/agent-runtime";
|
|
2
2
|
//#region src/core-version.ts
|
|
3
|
-
const EXPECTED_CORE_RUNTIME_API =
|
|
3
|
+
const EXPECTED_CORE_RUNTIME_API = 6;
|
|
4
4
|
function assertCoreRuntimeVersion(surface) {
|
|
5
5
|
checkCoreRuntimeVersion(surface, coreRuntime.RUNTIME_API_VERSION);
|
|
6
6
|
}
|
package/dist/dev.d.ts
CHANGED
|
@@ -3,10 +3,14 @@ import { JuneHost, ServeHandle } from "./host.js";
|
|
|
3
3
|
export type DevServerOptions = {
|
|
4
4
|
appDir: string;
|
|
5
5
|
port?: number;
|
|
6
|
+
hostname?: string;
|
|
6
7
|
host?: JuneHost;
|
|
7
8
|
};
|
|
8
|
-
export
|
|
9
|
+
export declare function devUrl(hostname: string, port: number): string;
|
|
10
|
+
export declare function devAllowedHosts(configured: readonly string[] | undefined, hostname: string): string[];
|
|
11
|
+
export type DevServer = Omit<ServeHandle, "stop"> & {
|
|
9
12
|
url: string;
|
|
13
|
+
stop(force?: boolean): Promise<void>;
|
|
10
14
|
};
|
|
11
|
-
export declare function startDevServer({ appDir, port, host }: DevServerOptions): Promise<DevServer>;
|
|
15
|
+
export declare function startDevServer({ appDir, port, hostname, host }: DevServerOptions): Promise<DevServer>;
|
|
12
16
|
//#endregion
|
package/dist/dev.js
CHANGED
|
@@ -9,17 +9,42 @@ import { watch } from "node:fs";
|
|
|
9
9
|
import { dirname } from "node:path";
|
|
10
10
|
import { createServer } from "node:net";
|
|
11
11
|
//#region src/dev.ts
|
|
12
|
-
|
|
12
|
+
function devUrl(hostname, port) {
|
|
13
|
+
if ([
|
|
14
|
+
"127.0.0.1",
|
|
15
|
+
"0.0.0.0",
|
|
16
|
+
"::",
|
|
17
|
+
"localhost"
|
|
18
|
+
].includes(hostname)) return `http://localhost:${port}`;
|
|
19
|
+
return `http://${hostname.includes(":") ? `[${hostname}]` : hostname}:${port}`;
|
|
20
|
+
}
|
|
21
|
+
function devAllowedHosts(configured, hostname) {
|
|
22
|
+
const hosts = /* @__PURE__ */ new Set([
|
|
23
|
+
"localhost",
|
|
24
|
+
".localhost",
|
|
25
|
+
...configured ?? []
|
|
26
|
+
]);
|
|
27
|
+
if (!/^[\d.]+$|:/.test(hostname)) hosts.add(hostname.toLowerCase());
|
|
28
|
+
return [...hosts];
|
|
29
|
+
}
|
|
30
|
+
async function findFreePort(start, hostname, tries = 20) {
|
|
13
31
|
for (let p = start; p < start + tries; p++) if (await new Promise((resolve) => {
|
|
14
32
|
const probe = createServer();
|
|
15
33
|
probe.once("error", () => resolve(false));
|
|
16
|
-
probe.listen(p, () => probe.close(() => resolve(true)));
|
|
34
|
+
probe.listen(p, hostname, () => probe.close(() => resolve(true)));
|
|
17
35
|
})) return p;
|
|
18
36
|
throw new Error(`june dev: no free port between ${start} and ${start + tries - 1}`);
|
|
19
37
|
}
|
|
20
|
-
async function startDevServer({ appDir, port = 3e3, host: host$1 = host }) {
|
|
38
|
+
async function startDevServer({ appDir, port = 3e3, hostname = "127.0.0.1", host: host$1 = host }) {
|
|
21
39
|
await installAsyncContext();
|
|
22
|
-
const
|
|
40
|
+
const loaded = await loadJuneConfig(appDir);
|
|
41
|
+
const config = {
|
|
42
|
+
...loaded,
|
|
43
|
+
agent: {
|
|
44
|
+
...loaded.agent,
|
|
45
|
+
allowedHosts: devAllowedHosts(loaded.agent?.allowedHosts, hostname)
|
|
46
|
+
}
|
|
47
|
+
};
|
|
23
48
|
const m = await migrateApp(dirname(appDir), config);
|
|
24
49
|
if (m?.applied.length) console.log(`[june] migrated: ${m.applied.join(", ")}`);
|
|
25
50
|
if (m?.blocked) console.warn(`[june] ${blockedMessage(m.blocked)}`);
|
|
@@ -28,11 +53,12 @@ async function startDevServer({ appDir, port = 3e3, host: host$1 = host }) {
|
|
|
28
53
|
config
|
|
29
54
|
});
|
|
30
55
|
await app.warmup();
|
|
31
|
-
const freePort = await findFreePort(port);
|
|
56
|
+
const freePort = await findFreePort(port, hostname);
|
|
32
57
|
if (freePort !== port) console.log(`[june] port ${port} is taken → using ${freePort}`);
|
|
33
58
|
port = freePort;
|
|
34
59
|
const handle = host$1.serve(withLiveReload((req) => app.fetch(req)), {
|
|
35
60
|
port,
|
|
61
|
+
hostname,
|
|
36
62
|
earlyHints: () => app.earlyHints()
|
|
37
63
|
});
|
|
38
64
|
if (findGlobalCss(appDir)) {
|
|
@@ -44,12 +70,17 @@ async function startDevServer({ appDir, port = 3e3, host: host$1 = host }) {
|
|
|
44
70
|
});
|
|
45
71
|
processCssCached(appDir).catch(() => {});
|
|
46
72
|
}
|
|
47
|
-
const url =
|
|
48
|
-
|
|
73
|
+
const url = devUrl(hostname, handle.port);
|
|
74
|
+
const lan = hostname === "127.0.0.1" ? "" : ` · listening on ${hostname}`;
|
|
75
|
+
console.log(`june dev → ${url} (host: ${host$1.name})${lan}`);
|
|
49
76
|
return {
|
|
50
77
|
...handle,
|
|
51
|
-
url
|
|
78
|
+
url,
|
|
79
|
+
async stop(force) {
|
|
80
|
+
handle.stop(force);
|
|
81
|
+
await app.close();
|
|
82
|
+
}
|
|
52
83
|
};
|
|
53
84
|
}
|
|
54
85
|
//#endregion
|
|
55
|
-
export { startDevServer };
|
|
86
|
+
export { devAllowedHosts, devUrl, startDevServer };
|
package/dist/host.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export interface JuneHost {
|
|
|
13
13
|
readonly name: "bun" | "node";
|
|
14
14
|
serve(handler: (req: Request) => Promise<Response>, opts: {
|
|
15
15
|
port: number;
|
|
16
|
+
hostname?: string;
|
|
16
17
|
earlyHints?: () => string[];
|
|
17
18
|
}): ServeHandle;
|
|
18
19
|
spawnModule(entry: string, args: string[], opts: {
|
package/dist/host.js
CHANGED
|
@@ -9,6 +9,7 @@ function bunHost() {
|
|
|
9
9
|
serve(handler, opts) {
|
|
10
10
|
const server = Bun.serve({
|
|
11
11
|
port: opts.port,
|
|
12
|
+
hostname: opts.hostname,
|
|
12
13
|
fetch: handler,
|
|
13
14
|
idleTimeout: 0
|
|
14
15
|
});
|
|
@@ -64,7 +65,7 @@ function nodeHost() {
|
|
|
64
65
|
res.end("Internal Server Error");
|
|
65
66
|
}
|
|
66
67
|
});
|
|
67
|
-
server.listen(opts.port);
|
|
68
|
+
server.listen(opts.port, opts.hostname);
|
|
68
69
|
return {
|
|
69
70
|
get port() {
|
|
70
71
|
const addr = server.address();
|
package/dist/index.d.ts
CHANGED
|
@@ -2,12 +2,12 @@ import { a as ResourcePlan, c as staticSite, i as JuneAdapter, l as vercel, n as
|
|
|
2
2
|
import { AGENT_MODULE_FILE, AgentDirScan, emitAgentModule, findAgentDir, generateAgentModule, scanAgentDir } from "./agent-compile.js";
|
|
3
3
|
import { CreateAppOptions, JuneApp, createApp } from "./app.js";
|
|
4
4
|
import { R2Bucket, localBlob, r2 } from "./blob.js";
|
|
5
|
-
import {
|
|
5
|
+
import { o as scanRoutes } from "./route-scan-BMbBzPIB.js";
|
|
6
6
|
import { t as generateContent } from "./content-freeze-CyiW2fRK.js";
|
|
7
7
|
import { a as Pipeline, d as createPipeline, n as LayoutComponent, o as PipelineConfig, s as Resolved, u as RouteResolver } from "./pipeline-BHUjBO0G.js";
|
|
8
|
-
import { n as WorkerManifest, r as createWorker } from "./worker-
|
|
9
|
-
import { t as freezeConfig } from "./config-freeze-
|
|
10
|
-
import { n as buildManifest } from "./manifest-
|
|
8
|
+
import { n as WorkerManifest, r as createWorker } from "./worker-2qbQjj2c.js";
|
|
9
|
+
import { t as freezeConfig } from "./config-freeze-CyXvNqi2.js";
|
|
10
|
+
import { n as buildManifest } from "./manifest-CwTT1zVs.js";
|
|
11
11
|
import { BuildResult, juneBuild } from "./build.js";
|
|
12
12
|
import { loadJuneConfig } from "./config-loader.js";
|
|
13
13
|
import { AccountTokenStore, BetterAuthLike, ConnectionAuth, LinkedAccountAuthOptions, betterAuthAccessToken, betterAuthAccountTokenStore, linkedAccountAuth } from "./connection-auth.js";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { deno, staticSite, vercel, workers } from "./adapter.js";
|
|
2
2
|
import { AGENT_MODULE_FILE, emitAgentModule, findAgentDir, generateAgentModule, scanAgentDir } from "./agent-compile.js";
|
|
3
3
|
import { listRoutes, matchRoute, matchRouteTree, resolveNotFound } from "./router.js";
|
|
4
|
+
import { scanRoutes } from "./route-scan.js";
|
|
4
5
|
import { negotiate } from "./negotiate.js";
|
|
5
6
|
import { createPipeline } from "./pipeline.js";
|
|
6
7
|
import { d1 } from "./d1.js";
|
|
@@ -12,7 +13,6 @@ import { freezeConfig } from "./config-freeze.js";
|
|
|
12
13
|
import { collection, entry } from "./content.js";
|
|
13
14
|
import { generateContent } from "./content-freeze.js";
|
|
14
15
|
import { createWorker } from "./worker.js";
|
|
15
|
-
import { scanRoutes } from "./route-scan.js";
|
|
16
16
|
import { buildManifest } from "./manifest.js";
|
|
17
17
|
import { juneBuild } from "./build.js";
|
|
18
18
|
import { betterAuthAccessToken, betterAuthAccountTokenStore, linkedAccountAuth } from "./connection-auth.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as LayoutComponent } from "./pipeline-BHUjBO0G.js";
|
|
2
|
-
import { n as WorkerManifest } from "./worker-
|
|
2
|
+
import { n as WorkerManifest } from "./worker-2qbQjj2c.js";
|
|
3
3
|
//#region src/manifest.d.ts
|
|
4
4
|
type ImportedLayout = {
|
|
5
5
|
component: LayoutComponent;
|
package/dist/manifest.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as buildManifest, r as importLayout, t as ImportedLayout } from "./manifest-
|
|
1
|
+
import { n as buildManifest, r as importLayout, t as ImportedLayout } from "./manifest-CwTT1zVs.js";
|
|
2
2
|
export { ImportedLayout, buildManifest, importLayout };
|
package/dist/manifest.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { findMiddlewareFile } from "./router.js";
|
|
2
|
+
import { scanAppRoutes } from "./route-scan.js";
|
|
2
3
|
import { resolveBoundary } from "./segment.js";
|
|
3
4
|
import { freezeConfig } from "./config-freeze.js";
|
|
4
|
-
import { scanAppRoutes } from "./route-scan.js";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { basename, join } from "node:path";
|
|
7
7
|
import { pathToFileURL } from "node:url";
|
|
@@ -47,6 +47,7 @@ async function buildManifest(appRoot) {
|
|
|
47
47
|
const routes = {};
|
|
48
48
|
const dynamicRoutes = [];
|
|
49
49
|
const resourceRoutes = [];
|
|
50
|
+
const generatedRoutes = scanned.filter((r) => r.generated).map((r) => r.path);
|
|
50
51
|
const layoutChains = {};
|
|
51
52
|
const layoutBoundaries = {};
|
|
52
53
|
const loadings = {};
|
|
@@ -96,6 +97,7 @@ async function buildManifest(appRoot) {
|
|
|
96
97
|
routes,
|
|
97
98
|
dynamicRoutes,
|
|
98
99
|
resourceRoutes,
|
|
100
|
+
...generatedRoutes.length ? { generatedRoutes } : {},
|
|
99
101
|
layoutChains,
|
|
100
102
|
layoutBoundaries,
|
|
101
103
|
loadings,
|
package/dist/pipeline.js
CHANGED
|
@@ -9,8 +9,8 @@ import React, { Suspense, use } from "react";
|
|
|
9
9
|
import { renderToReadableStream } from "react-dom/server";
|
|
10
10
|
import { Document, PRERENDER_ORIGIN, documentTitle, pageCanonical } from "@junejs/core/document";
|
|
11
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
|
+
import { mcpForbidden, mcpHandler, mcpServerIdentity, mcpTools, originRejection } from "@junejs/core/mcp";
|
|
13
|
+
import { OPENAPI_MEDIA_TYPE, apiActionId, apiForbidden, apiHandler, apiNamespaceResponse, isApiNamespace, openApiDocument } from "@junejs/core/api";
|
|
14
14
|
import { LOCALE_COOKIE, localeAlternates, localeDir, matchPinnedLocale, negotiateLocale } from "@junejs/core/i18n";
|
|
15
15
|
//#region src/pipeline.ts
|
|
16
16
|
function readCookie(request, name) {
|
|
@@ -368,18 +368,25 @@ function createPipeline(cfg) {
|
|
|
368
368
|
default: return null;
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
|
+
const originPolicy = {
|
|
372
|
+
allowedOrigins: agent.allowedOrigins,
|
|
373
|
+
allowedHosts: agent.allowedHosts
|
|
374
|
+
};
|
|
375
|
+
const callerIdentity = async (request) => request.method === "POST" && cfg.identity ? await cfg.identity(request) : void 0;
|
|
371
376
|
async function handleRequest(request) {
|
|
372
377
|
const url = new URL(request.url);
|
|
373
378
|
if (url.pathname === "/mcp") {
|
|
374
379
|
if (!agent.mcp) return notFoundResponse("view", url.pathname);
|
|
375
|
-
const
|
|
380
|
+
const refused = originRejection(request, originPolicy);
|
|
381
|
+
if (refused !== void 0) return mcpForbidden(refused);
|
|
382
|
+
const identity = await callerIdentity(request);
|
|
376
383
|
return mcpHandler(request, {
|
|
377
384
|
request,
|
|
378
385
|
...identity
|
|
379
386
|
}, mcpServerIdentity(url.origin, {
|
|
380
387
|
site: docConfig.site,
|
|
381
388
|
agent
|
|
382
|
-
}));
|
|
389
|
+
}), originPolicy);
|
|
383
390
|
}
|
|
384
391
|
if (agent.api) {
|
|
385
392
|
if (url.pathname === "/openapi.json" && (request.method === "GET" || request.method === "HEAD")) {
|
|
@@ -394,11 +401,13 @@ function createPipeline(cfg) {
|
|
|
394
401
|
}
|
|
395
402
|
const actionId = apiActionId(url.pathname);
|
|
396
403
|
if (actionId) {
|
|
397
|
-
const
|
|
404
|
+
const refused = originRejection(request, originPolicy);
|
|
405
|
+
if (refused !== void 0) return apiForbidden(request, refused);
|
|
406
|
+
const identity = await callerIdentity(request);
|
|
398
407
|
return apiHandler(request, actionId, {
|
|
399
408
|
request,
|
|
400
409
|
...identity
|
|
401
|
-
});
|
|
410
|
+
}, originPolicy);
|
|
402
411
|
}
|
|
403
412
|
}
|
|
404
413
|
if (request.method === "OPTIONS" && agent.discovery && agent.mcp && url.pathname === SERVER_CARD_PATH) return new Response(null, {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
//#region src/route-rank.d.ts
|
|
2
|
+
export type SegmentKind = "static" | "param" | "optional" | "catchAll" | "optionalCatchAll";
|
|
3
|
+
export declare function parseSegment(segment: string): {
|
|
4
|
+
kind: SegmentKind;
|
|
5
|
+
name?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function segmentRank(segment: string): number;
|
|
8
|
+
export declare function compareSegments(a: string, b: string): number;
|
|
9
|
+
export declare function compareRoutePatterns(a: string, b: string): number;
|
|
10
|
+
//#endregion
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/route-rank.ts
|
|
2
|
+
const DYNAMIC = /^\[(\[)?(\.\.\.)?([A-Za-z_][A-Za-z0-9_]*)\](\])?$/;
|
|
3
|
+
function parseSegment(segment) {
|
|
4
|
+
const m = segment.match(DYNAMIC);
|
|
5
|
+
if (!m || !!m[1] !== !!m[4]) return { kind: "static" };
|
|
6
|
+
const optional = !!m[1];
|
|
7
|
+
return {
|
|
8
|
+
kind: !!m[2] ? optional ? "optionalCatchAll" : "catchAll" : optional ? "optional" : "param",
|
|
9
|
+
name: m[3]
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
const RANK = {
|
|
13
|
+
static: 0,
|
|
14
|
+
param: 1,
|
|
15
|
+
optional: 2,
|
|
16
|
+
catchAll: 3,
|
|
17
|
+
optionalCatchAll: 4
|
|
18
|
+
};
|
|
19
|
+
function segmentRank(segment) {
|
|
20
|
+
return RANK[parseSegment(segment).kind];
|
|
21
|
+
}
|
|
22
|
+
function compareSegments(a, b) {
|
|
23
|
+
return segmentRank(a) - segmentRank(b) || (a < b ? -1 : a > b ? 1 : 0);
|
|
24
|
+
}
|
|
25
|
+
function compareRoutePatterns(a, b) {
|
|
26
|
+
const as = a.split("/").filter(Boolean);
|
|
27
|
+
const bs = b.split("/").filter(Boolean);
|
|
28
|
+
for (let i = 0; i < Math.max(as.length, bs.length); i++) {
|
|
29
|
+
const x = as[i];
|
|
30
|
+
const y = bs[i];
|
|
31
|
+
if (x === void 0) return -1;
|
|
32
|
+
if (y === void 0) return 1;
|
|
33
|
+
if (x !== y) return compareSegments(x, y);
|
|
34
|
+
}
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { compareRoutePatterns, compareSegments, parseSegment, segmentRank };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/route-scan.d.ts
|
|
2
|
+
type RouteEntry = {
|
|
3
|
+
path: string;
|
|
4
|
+
file: string;
|
|
5
|
+
dynamic: boolean;
|
|
6
|
+
resource?: boolean;
|
|
7
|
+
generated?: boolean;
|
|
8
|
+
layouts: string[];
|
|
9
|
+
loading?: string;
|
|
10
|
+
};
|
|
11
|
+
declare function scanRoutes(appDir: string, dir?: string, layouts?: string[], out?: RouteEntry[], loading?: string): Promise<RouteEntry[]>;
|
|
12
|
+
declare function scanAppRoutes(appRoot: string): Promise<RouteEntry[]>;
|
|
13
|
+
declare function routeConflicts(routes: RouteEntry[]): Array<{
|
|
14
|
+
path: string;
|
|
15
|
+
files: string[];
|
|
16
|
+
}>;
|
|
17
|
+
declare function misplacedSegments(routes: RouteEntry[]): RouteEntry[];
|
|
18
|
+
declare function routeProblems(trees: RouteEntry[][], appRoot: string): string | null;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { scanAppRoutes as a, routeProblems as i, misplacedSegments as n, scanRoutes as o, routeConflicts as r, RouteEntry as t };
|
package/dist/route-scan.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { RouteEntry, scanAppRoutes, scanRoutes };
|
|
1
|
+
import { a as scanAppRoutes, i as routeProblems, n as misplacedSegments, o as scanRoutes, r as routeConflicts, t as RouteEntry } from "./route-scan-BMbBzPIB.js";
|
|
2
|
+
export { RouteEntry, misplacedSegments, routeConflicts, routeProblems, scanAppRoutes, scanRoutes };
|
package/dist/route-scan.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseSegment } from "./route-rank.js";
|
|
1
2
|
import { existsSync } from "node:fs";
|
|
2
3
|
import { readdir } from "node:fs/promises";
|
|
3
4
|
import { basename, join, relative, sep } from "node:path";
|
|
@@ -48,8 +49,52 @@ async function scanAppRoutes(appRoot) {
|
|
|
48
49
|
const juneRoutesDir = join(appRoot, ".june", "routes");
|
|
49
50
|
const appRoutes = await scanRoutes(appDir);
|
|
50
51
|
const frameworkRoutes = existsSync(juneRoutesDir) ? await scanRoutes(juneRoutesDir) : [];
|
|
52
|
+
const problems = routeProblems([appRoutes, frameworkRoutes], appRoot);
|
|
53
|
+
if (problems) throw new Error(problems);
|
|
51
54
|
const appPaths = new Set(appRoutes.map((r) => r.path));
|
|
52
|
-
|
|
55
|
+
const generated = frameworkRoutes.filter((r) => !appPaths.has(r.path)).map((r) => ({
|
|
56
|
+
...r,
|
|
57
|
+
generated: true
|
|
58
|
+
}));
|
|
59
|
+
return [...appRoutes, ...generated].sort((a, b) => a.path.localeCompare(b.path));
|
|
60
|
+
}
|
|
61
|
+
function routeConflicts(routes) {
|
|
62
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
63
|
+
for (const r of routes) {
|
|
64
|
+
const key = `${r.resource ? "route" : "page"} ${r.path}`;
|
|
65
|
+
const hit = byKey.get(key);
|
|
66
|
+
if (hit) hit.files.push(r.file);
|
|
67
|
+
else byKey.set(key, {
|
|
68
|
+
path: r.path,
|
|
69
|
+
files: [r.file]
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return [...byKey.values()].filter((c) => c.files.length > 1).map((c) => ({
|
|
73
|
+
path: c.path,
|
|
74
|
+
files: c.files.sort()
|
|
75
|
+
})).sort((a, b) => a.path.localeCompare(b.path));
|
|
76
|
+
}
|
|
77
|
+
const ENDS_PATH = /* @__PURE__ */ new Set([
|
|
78
|
+
"optional",
|
|
79
|
+
"catchAll",
|
|
80
|
+
"optionalCatchAll"
|
|
81
|
+
]);
|
|
82
|
+
const beforeLast = (path) => path.split("/").filter(Boolean).slice(0, -1).map((s) => parseSegment(s).kind);
|
|
83
|
+
function misplacedSegments(routes) {
|
|
84
|
+
return routes.filter((r) => beforeLast(r.path).some((k) => ENDS_PATH.has(k))).sort((a, b) => a.path.localeCompare(b.path) || a.file.localeCompare(b.file));
|
|
85
|
+
}
|
|
86
|
+
function routeProblems(trees, appRoot) {
|
|
87
|
+
const rel = (f) => relative(appRoot, f).split(sep).join("/");
|
|
88
|
+
const sections = [];
|
|
89
|
+
const conflicts = trees.flatMap(routeConflicts);
|
|
90
|
+
if (conflicts.length) sections.push("[june] more than one route file resolves to the same path — keep one per path:\n" + conflicts.map((c) => ` ${c.path}: ${c.files.map(rel).join(", ")}`).join("\n"));
|
|
91
|
+
const misplaced = trees.flatMap(misplacedSegments);
|
|
92
|
+
if (misplaced.length) {
|
|
93
|
+
const lines = misplaced.map((r) => ` ${r.path}: ${rel(r.file)}`);
|
|
94
|
+
if (misplaced.some((r) => beforeLast(r.path).includes("optional"))) lines.push(" Unlike SvelteKit, June does not skip a [[param]] mid-path. For a locale prefix, set i18n.locales in june.config.ts instead of a [[lang]] directory.");
|
|
95
|
+
sections.push("[june] an optional or catch-all segment must be the last segment of a route path — nothing may follow it:\n" + lines.join("\n"));
|
|
96
|
+
}
|
|
97
|
+
return sections.length ? sections.join("\n") : null;
|
|
53
98
|
}
|
|
54
99
|
//#endregion
|
|
55
|
-
export { scanAppRoutes, scanRoutes };
|
|
100
|
+
export { misplacedSegments, routeConflicts, routeProblems, scanAppRoutes, scanRoutes };
|
package/dist/router.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { compareSegments, parseSegment } from "./route-rank.js";
|
|
1
2
|
import { existsSync } from "node:fs";
|
|
2
3
|
import { readdir } from "node:fs/promises";
|
|
3
4
|
import { join, relative, sep } from "node:path";
|
|
@@ -42,11 +43,11 @@ function isSpecialFile(file) {
|
|
|
42
43
|
return SPECIAL_FILES.has(baseName(file));
|
|
43
44
|
}
|
|
44
45
|
const isRouteGroup = (name) => /^\(.+\)$/.test(name);
|
|
45
|
-
const isParamDir = (name) =>
|
|
46
|
-
const isOptionalDir = (name) =>
|
|
47
|
-
const isOptionalCatchAllDir = (name) =>
|
|
48
|
-
const isCatchAllDir = (name) =>
|
|
49
|
-
const paramName = (name) => name.
|
|
46
|
+
const isParamDir = (name) => parseSegment(name).kind === "param";
|
|
47
|
+
const isOptionalDir = (name) => parseSegment(name).kind === "optional";
|
|
48
|
+
const isOptionalCatchAllDir = (name) => parseSegment(name).kind === "optionalCatchAll";
|
|
49
|
+
const isCatchAllDir = (name) => parseSegment(name).kind === "catchAll";
|
|
50
|
+
const paramName = (name) => parseSegment(name).name;
|
|
50
51
|
async function listDir(dir) {
|
|
51
52
|
return (await readdir(dir, { withFileTypes: true }).catch(() => [])).filter((e) => !e.name.startsWith("_") && !e.name.startsWith(".")).map((e) => ({
|
|
52
53
|
name: e.name,
|
|
@@ -84,63 +85,76 @@ function routePath(appDir, file) {
|
|
|
84
85
|
}
|
|
85
86
|
async function matchRouteTree(appDir, pathname, options = {}) {
|
|
86
87
|
const urlSegments = pathname.split("/").filter(Boolean).map(decodeURIComponent);
|
|
87
|
-
async function
|
|
88
|
-
const entries = await listDir(dir);
|
|
88
|
+
async function levelAt(dir, chain) {
|
|
89
|
+
const entries = (await listDir(dir)).sort((a, b) => compareSegments(a.name, b.name));
|
|
89
90
|
const segments = [...chain, segmentAt(dir, entries)];
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
const levels = [{
|
|
92
|
+
dir,
|
|
93
|
+
entries,
|
|
94
|
+
segments
|
|
95
|
+
}];
|
|
96
|
+
for (const e of entries) if (e.dir && isRouteGroup(e.name)) levels.push(...await levelAt(join(dir, e.name), segments));
|
|
97
|
+
return levels;
|
|
98
|
+
}
|
|
99
|
+
async function descend(nodes, rest, params) {
|
|
100
|
+
const levels = (await Promise.all(nodes.map((n) => levelAt(n.dir, n.chain)))).flat();
|
|
101
|
+
if (rest.length === 0) for (const base of [["page", "index"], ["route"]]) for (const { dir: d, entries, segments } of levels) {
|
|
102
|
+
const file = base.map((b) => fileFor(entries, d, b)).find(Boolean);
|
|
103
|
+
if (file) return {
|
|
104
|
+
file,
|
|
94
105
|
params,
|
|
95
106
|
segments
|
|
96
107
|
};
|
|
97
|
-
} else if (!options.pageConvention) {
|
|
98
|
-
if (rest.length === 1) {
|
|
99
|
-
const leaf = fileFor(entries, dir, rest[0]);
|
|
100
|
-
if (leaf && !isSpecialFile(leaf) && !isPageFile(leaf)) return {
|
|
101
|
-
file: leaf,
|
|
102
|
-
params,
|
|
103
|
-
segments
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
108
|
}
|
|
107
|
-
for (const
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
109
|
+
else if (!options.pageConvention && rest.length === 1) for (const { dir: d, entries, segments } of levels) {
|
|
110
|
+
const leaf = fileFor(entries, d, rest[0]);
|
|
111
|
+
if (leaf && !isSpecialFile(leaf) && !isPageFile(leaf)) return {
|
|
112
|
+
file: leaf,
|
|
113
|
+
params,
|
|
114
|
+
segments
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const children = /* @__PURE__ */ new Map();
|
|
118
|
+
for (const lv of levels) for (const e of lv.entries) {
|
|
119
|
+
if (!e.dir || isRouteGroup(e.name)) continue;
|
|
120
|
+
const node = {
|
|
121
|
+
dir: join(lv.dir, e.name),
|
|
122
|
+
chain: lv.segments
|
|
123
|
+
};
|
|
124
|
+
const same = children.get(e.name);
|
|
125
|
+
if (same) same.push(node);
|
|
126
|
+
else children.set(e.name, [node]);
|
|
111
127
|
}
|
|
128
|
+
const names = [...children.keys()].sort(compareSegments);
|
|
112
129
|
if (rest.length === 0) {
|
|
113
|
-
for (const
|
|
114
|
-
if (!
|
|
115
|
-
const hit = await descend(
|
|
130
|
+
for (const name of names) {
|
|
131
|
+
if (!(isOptionalDir(name) || isOptionalCatchAllDir(name))) continue;
|
|
132
|
+
const hit = await descend(children.get(name), [], params);
|
|
116
133
|
if (hit) return hit;
|
|
117
134
|
}
|
|
118
135
|
return null;
|
|
119
136
|
}
|
|
120
137
|
const [head, ...tail] = rest;
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
for (const e of entries) {
|
|
126
|
-
if (!e.dir || !(isParamDir(e.name) || isOptionalDir(e.name))) continue;
|
|
127
|
-
const hit = await descend(join(dir, e.name), tail, {
|
|
138
|
+
for (const name of names) {
|
|
139
|
+
const into = children.get(name);
|
|
140
|
+
let hit = null;
|
|
141
|
+
if (isParamDir(name) || isOptionalDir(name)) hit = await descend(into, tail, {
|
|
128
142
|
...params,
|
|
129
|
-
[paramName(
|
|
130
|
-
}
|
|
131
|
-
if (
|
|
132
|
-
}
|
|
133
|
-
for (const e of entries) {
|
|
134
|
-
if (!e.dir || !(isCatchAllDir(e.name) || isOptionalCatchAllDir(e.name))) continue;
|
|
135
|
-
const hit = await descend(join(dir, e.name), [], {
|
|
143
|
+
[paramName(name)]: head
|
|
144
|
+
});
|
|
145
|
+
else if (isCatchAllDir(name) || isOptionalCatchAllDir(name)) hit = await descend(into, [], {
|
|
136
146
|
...params,
|
|
137
|
-
[paramName(
|
|
138
|
-
}
|
|
147
|
+
[paramName(name)]: rest.join("/")
|
|
148
|
+
});
|
|
149
|
+
else if (name === head) hit = await descend(into, tail, params);
|
|
139
150
|
if (hit) return hit;
|
|
140
151
|
}
|
|
141
152
|
return null;
|
|
142
153
|
}
|
|
143
|
-
return descend(
|
|
154
|
+
return descend([{
|
|
155
|
+
dir: appDir,
|
|
156
|
+
chain: []
|
|
157
|
+
}], urlSegments, {});
|
|
144
158
|
}
|
|
145
159
|
async function resolveNotFound(appDir, pathname) {
|
|
146
160
|
const urlSegments = pathname.split("/").filter(Boolean).map(decodeURIComponent);
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as withDenoAssets, i as withAssets, n as WorkerManifest, r as createWorker, t as WorkerExecutionContext } from "./worker-
|
|
1
|
+
import { a as withDenoAssets, i as withAssets, n as WorkerManifest, r as createWorker, t as WorkerExecutionContext } from "./worker-2qbQjj2c.js";
|
|
2
2
|
export { WorkerExecutionContext, WorkerManifest, createWorker, withAssets, withDenoAssets };
|