@junejs/server 0.1.2-dev.9 → 0.1.3
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 +3 -39
- package/dist/agent-durable.js +26 -297
- package/dist/agent-native.d.ts +0 -2
- package/dist/agent-native.js +10 -28
- package/dist/build.d.ts +1 -0
- package/dist/build.js +13 -3
- package/dist/route-rank.d.ts +11 -0
- package/dist/route-rank.js +38 -0
- package/dist/router.js +7 -6
- package/dist/worker.d.ts +1 -0
- package/dist/worker.js +53 -77
- package/package.json +3 -3
- package/src/agent-durable.ts +25 -438
- package/src/agent-native.ts +13 -27
- package/src/build.ts +12 -3
- package/src/route-rank.ts +60 -0
- package/src/router.ts +13 -8
- package/src/worker.ts +71 -84
- package/dist/core-version.d.ts +0 -5
- package/dist/core-version.js +0 -12
- package/src/core-version.ts +0 -34
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/route-rank.d.ts
|
|
2
|
+
type SegmentKind = "static" | "param" | "optional" | "catchAll" | "optionalCatchAll";
|
|
3
|
+
declare function parseSegment(segment: string): {
|
|
4
|
+
kind: SegmentKind;
|
|
5
|
+
name?: string;
|
|
6
|
+
};
|
|
7
|
+
declare function segmentRank(segment: string): number;
|
|
8
|
+
declare function compareSegments(a: string, b: string): number;
|
|
9
|
+
declare function compareRoutePatterns(a: string, b: string): number;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { SegmentKind, compareRoutePatterns, compareSegments, parseSegment, segmentRank };
|
|
@@ -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 };
|
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,
|
|
@@ -85,7 +86,7 @@ function routePath(appDir, file) {
|
|
|
85
86
|
async function matchRouteTree(appDir, pathname, options = {}) {
|
|
86
87
|
const urlSegments = pathname.split("/").filter(Boolean).map(decodeURIComponent);
|
|
87
88
|
async function descend(dir, rest, params, chain) {
|
|
88
|
-
const entries = await listDir(dir);
|
|
89
|
+
const entries = (await listDir(dir)).sort((a, b) => compareSegments(a.name, b.name));
|
|
89
90
|
const segments = [...chain, segmentAt(dir, entries)];
|
|
90
91
|
if (rest.length === 0) {
|
|
91
92
|
const page = fileFor(entries, dir, "page") ?? fileFor(entries, dir, "index") ?? fileFor(entries, dir, "route");
|
package/dist/worker.d.ts
CHANGED
package/dist/worker.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { durableAgentSurface } from "./agent-durable.js";
|
|
2
|
+
import { compareRoutePatterns, parseSegment } from "./route-rank.js";
|
|
2
3
|
import { createPipeline } from "./pipeline.js";
|
|
3
4
|
import { RESERVED_PREFIX, contentTypeFor, safeRelativePath } from "./static-files.js";
|
|
4
5
|
//#region src/worker.ts
|
|
@@ -7,34 +8,16 @@ function compilePattern(pattern) {
|
|
|
7
8
|
let source = "";
|
|
8
9
|
let allOptional = true;
|
|
9
10
|
for (const seg of pattern.split("/").filter(Boolean)) {
|
|
10
|
-
const
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
names.push(ca[1]);
|
|
19
|
-
source += "/(.+)";
|
|
20
|
-
allOptional = false;
|
|
21
|
-
continue;
|
|
22
|
-
}
|
|
23
|
-
const op = seg.match(/^\[\[(\w+)\]\]$/);
|
|
24
|
-
if (op) {
|
|
25
|
-
names.push(op[1]);
|
|
26
|
-
source += "(?:/([^/]+))?";
|
|
27
|
-
continue;
|
|
28
|
-
}
|
|
29
|
-
const p = seg.match(/^\[(\w+)\]$/);
|
|
30
|
-
if (p) {
|
|
31
|
-
names.push(p[1]);
|
|
32
|
-
source += "/([^/]+)";
|
|
11
|
+
const { kind, name } = parseSegment(seg);
|
|
12
|
+
if (name) names.push(name);
|
|
13
|
+
if (kind === "optionalCatchAll") source += "(?:/(.+))?";
|
|
14
|
+
else if (kind === "optional") source += "(?:/([^/]+))?";
|
|
15
|
+
else {
|
|
16
|
+
if (kind === "catchAll") source += "/(.+)";
|
|
17
|
+
else if (kind === "param") source += "/([^/]+)";
|
|
18
|
+
else source += "/" + seg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
33
19
|
allOptional = false;
|
|
34
|
-
continue;
|
|
35
20
|
}
|
|
36
|
-
source += "/" + seg.replace(/[.*+?^${}()|\\]/g, "\\$&");
|
|
37
|
-
allOptional = false;
|
|
38
21
|
}
|
|
39
22
|
if (source === "") return {
|
|
40
23
|
regex: /^\/$/,
|
|
@@ -46,20 +29,42 @@ function compilePattern(pattern) {
|
|
|
46
29
|
};
|
|
47
30
|
}
|
|
48
31
|
function createWorker(manifest) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
pattern
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
32
|
+
const generated = new Set(manifest.generatedRoutes ?? []);
|
|
33
|
+
const row = (pattern) => ({
|
|
34
|
+
...compilePattern(pattern),
|
|
35
|
+
pattern,
|
|
36
|
+
generated: generated.has(pattern)
|
|
37
|
+
});
|
|
38
|
+
const table = [
|
|
39
|
+
...Object.entries(manifest.routes).map(([pattern, def]) => ({
|
|
40
|
+
...row(pattern),
|
|
41
|
+
def
|
|
42
|
+
})),
|
|
43
|
+
...(manifest.dynamicRoutes ?? []).map((d) => ({
|
|
44
|
+
...row(d.pattern),
|
|
45
|
+
def: d.def
|
|
46
|
+
})),
|
|
47
|
+
...(manifest.resourceRoutes ?? []).map((r) => ({
|
|
48
|
+
...row(r.pattern),
|
|
49
|
+
handler: r.handler
|
|
50
|
+
}))
|
|
51
|
+
];
|
|
52
|
+
table.sort((a, b) => Number(a.generated) - Number(b.generated) || compareRoutePatterns(a.pattern, b.pattern) || Number(!!a.handler) - Number(!!b.handler));
|
|
59
53
|
const routeList = [...Object.keys(manifest.routes), ...(manifest.dynamicRoutes ?? []).map((d) => d.pattern)].sort();
|
|
60
54
|
const chainFor = (key) => manifest.layoutChains?.[key] ?? [];
|
|
61
55
|
const loadingFor = (key) => manifest.loadings?.[key];
|
|
62
56
|
const boundaryFor = (key) => manifest.layoutBoundaries?.[key] ?? null;
|
|
57
|
+
const resolvedPage = (def, key, params) => {
|
|
58
|
+
const b = boundaryFor(key);
|
|
59
|
+
return {
|
|
60
|
+
def,
|
|
61
|
+
params,
|
|
62
|
+
chain: chainFor(key),
|
|
63
|
+
boundaryIndex: b?.index ?? null,
|
|
64
|
+
boundaryKey: b?.key ?? null,
|
|
65
|
+
loading: loadingFor(key)
|
|
66
|
+
};
|
|
67
|
+
};
|
|
63
68
|
let currentEnv;
|
|
64
69
|
const provider = manifest.resources;
|
|
65
70
|
const servicesProvider = manifest.services;
|
|
@@ -85,47 +90,18 @@ function createWorker(manifest) {
|
|
|
85
90
|
services: servicesProvider ? () => (servicesCache ??= { v: servicesProvider(currentEnv) }).v : void 0,
|
|
86
91
|
resolve: async (pathname) => {
|
|
87
92
|
const staticDef = manifest.routes[pathname];
|
|
88
|
-
if (staticDef) {
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
def: staticDef,
|
|
92
|
-
params: {},
|
|
93
|
-
chain: chainFor(pathname),
|
|
94
|
-
boundaryIndex: b?.index ?? null,
|
|
95
|
-
boundaryKey: b?.key ?? null,
|
|
96
|
-
loading: loadingFor(pathname)
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
for (const d of dynamic) {
|
|
100
|
-
const m = pathname.match(d.regex);
|
|
101
|
-
if (m) {
|
|
102
|
-
const params = Object.fromEntries(d.names.flatMap((n, i) => {
|
|
103
|
-
const v = m[i + 1];
|
|
104
|
-
return v === void 0 ? [] : [[n, decodeURIComponent(v)]];
|
|
105
|
-
}));
|
|
106
|
-
const b = boundaryFor(d.pattern);
|
|
107
|
-
return {
|
|
108
|
-
def: d.def,
|
|
109
|
-
params,
|
|
110
|
-
chain: chainFor(d.pattern),
|
|
111
|
-
boundaryIndex: b?.index ?? null,
|
|
112
|
-
boundaryKey: b?.key ?? null,
|
|
113
|
-
loading: loadingFor(d.pattern)
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
for (const r of resources) {
|
|
93
|
+
if (staticDef && !generated.has(pathname)) return resolvedPage(staticDef, pathname, {});
|
|
94
|
+
for (const r of table) {
|
|
118
95
|
const m = pathname.match(r.regex);
|
|
119
|
-
if (m)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
96
|
+
if (!m) continue;
|
|
97
|
+
const params = Object.fromEntries(r.names.flatMap((n, i) => {
|
|
98
|
+
const v = m[i + 1];
|
|
99
|
+
return v === void 0 ? [] : [[n, decodeURIComponent(v)]];
|
|
100
|
+
}));
|
|
101
|
+
return r.handler ? {
|
|
102
|
+
handler: r.handler,
|
|
103
|
+
params
|
|
104
|
+
} : resolvedPage(r.def, r.pattern, params);
|
|
129
105
|
}
|
|
130
106
|
return null;
|
|
131
107
|
}
|
|
@@ -159,7 +135,7 @@ function withAssets(pipeline, opts = {}) {
|
|
|
159
135
|
});
|
|
160
136
|
}
|
|
161
137
|
}
|
|
162
|
-
if (assets) {
|
|
138
|
+
if (assets && (request.method === "GET" || request.method === "HEAD")) {
|
|
163
139
|
const a = await assets.fetch(request);
|
|
164
140
|
if (a.status !== 404) {
|
|
165
141
|
const ct = a.headers.get("content-type") ?? "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junejs/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "June host adapters + dev server. Host-coupled; depends on the pure @junejs/core contract layer.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -125,14 +125,14 @@
|
|
|
125
125
|
"build": "tsdown"
|
|
126
126
|
},
|
|
127
127
|
"dependencies": {
|
|
128
|
-
"@junejs/
|
|
128
|
+
"@junejs/core": ">=0.1.0",
|
|
129
|
+
"@junejs/db": ">=0.0.33",
|
|
129
130
|
"@momiji-rs/sparkdown": "^0.0.4",
|
|
130
131
|
"lightningcss": "^1.27.0",
|
|
131
132
|
"oxc-parser": "^0.137.0",
|
|
132
133
|
"rolldown": "^1.1.0"
|
|
133
134
|
},
|
|
134
135
|
"peerDependencies": {
|
|
135
|
-
"@junejs/core": ">=0.1.1-dev.16",
|
|
136
136
|
"react": "^19.2.0",
|
|
137
137
|
"react-dom": "^19.2.0",
|
|
138
138
|
"react-server-dom-webpack": "19.2.7",
|