@junejs/server 0.1.2 → 0.2.0
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/assets/icon-latin.LICENSE.txt +92 -0
- package/assets/icon-latin.ttf +0 -0
- package/dist/adapter-DcZ-W1-7.d.ts +62 -0
- package/dist/adapter.d.ts +2 -56
- package/dist/adapter.js +62 -5
- package/dist/agent-compile.d.ts +38 -0
- package/dist/agent-compile.js +145 -0
- package/dist/agent-discover.d.ts +4 -4
- package/dist/agent-discover.js +30 -44
- package/dist/agent-durable.d.ts +94 -19
- package/dist/agent-durable.js +647 -32
- package/dist/agent-native.d.ts +44 -13
- package/dist/agent-native.js +264 -27
- package/dist/app.d.ts +5 -5
- package/dist/app.js +42 -10
- package/dist/blob.d.ts +4 -5
- package/dist/build.d.ts +8 -29
- package/dist/build.js +138 -289
- package/dist/client-bundle.d.ts +8 -9
- package/dist/client-bundle.js +1 -1
- package/dist/config-freeze-CyXvNqi2.d.ts +17 -0
- package/dist/config-freeze.d.ts +2 -0
- package/dist/config-freeze.js +54 -0
- package/dist/config-loader.d.ts +3 -4
- package/dist/connection-auth.d.ts +38 -0
- package/dist/connection-auth.js +31 -0
- package/dist/content-freeze-CyiW2fRK.d.ts +4 -0
- package/dist/content-freeze.d.ts +2 -0
- package/dist/content-freeze.js +83 -0
- package/dist/content.d.ts +22 -9
- package/dist/content.js +43 -3
- package/dist/core-version.d.ts +4 -0
- package/dist/core-version.js +12 -0
- package/dist/css-modules-ambient.d.ts +2 -1
- package/dist/css-modules.d.ts +10 -11
- package/dist/css.d.ts +9 -10
- package/dist/d1-migrate.d.ts +6 -7
- package/dist/d1-migrate.js +3 -2
- package/dist/db.d.ts +2 -2
- package/dist/deploy.d.ts +3 -4
- package/dist/deploy.js +3 -2
- package/dist/dev-reload.d.ts +3 -4
- package/dist/dev.d.ts +8 -5
- package/dist/dev.js +40 -9
- package/dist/favicon.d.ts +37 -0
- package/dist/favicon.js +248 -0
- package/dist/host.d.ts +6 -5
- package/dist/host.js +2 -1
- package/dist/icon-letter.d.ts +3 -0
- package/dist/icon-letter.js +8 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.js +9 -3
- package/dist/instrumentation.d.ts +2 -3
- package/dist/instrumentation.js +2 -1
- package/dist/island-registry.d.ts +6 -7
- package/dist/isolate-local.d.ts +5 -0
- package/dist/isolate-local.js +12 -0
- package/dist/kv.d.ts +3 -4
- package/dist/llms-links.d.ts +9 -0
- package/dist/llms-links.js +65 -0
- package/dist/manifest-CwTT1zVs.d.ts +11 -0
- package/dist/manifest.d.ts +2 -0
- package/dist/manifest.js +113 -0
- package/dist/negotiate.d.ts +4 -3
- package/dist/negotiate.js +24 -14
- package/dist/{pipeline-CCp9idQ4.d.ts → pipeline-BHUjBO0G.d.ts} +13 -1
- package/dist/pipeline.d.ts +2 -2
- package/dist/pipeline.js +286 -35
- package/dist/resources.d.ts +5 -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 -0
- package/dist/route-scan.js +100 -0
- package/dist/router.d.ts +12 -13
- package/dist/router.js +58 -44
- package/dist/rsc-build.d.ts +6 -7
- package/dist/rsc-bundle.d.ts +6 -7
- package/dist/rsc-manifest.d.ts +5 -6
- package/dist/rsc-runtime/flight-render-routes.d.ts +3 -4
- package/dist/rsc-runtime/flight-render.d.ts +2 -3
- package/dist/rsc-runtime/flight-to-html.d.ts +2 -3
- package/dist/rsc-runtime/flight-to-html.js +3 -2
- package/dist/rsc-runtime/rsc-worker.js +11 -8
- package/dist/rsc-runtime/rsdw-shims.d.ts +2 -1
- package/dist/segment.d.ts +4 -5
- package/dist/sqlite-driver.d.ts +11 -11
- package/dist/sqlite-driver.js +17 -4
- package/dist/static-files.d.ts +4 -5
- package/dist/tsconfig-jsx.d.ts +4 -4
- package/dist/tsconfig-jsx.js +1 -1
- package/dist/turn-events.d.ts +4 -0
- package/dist/turn-events.js +46 -0
- package/dist/web-manifest.d.ts +5 -0
- package/dist/web-manifest.js +42 -0
- package/dist/worker-2qbQjj2c.d.ts +64 -0
- package/dist/worker.d.ts +2 -56
- package/dist/worker.js +121 -103
- package/package.json +22 -9
- package/src/adapter.ts +84 -3
- package/src/agent-compile.ts +271 -0
- package/src/agent-discover.ts +77 -63
- package/src/agent-durable.ts +998 -30
- package/src/agent-native.ts +370 -35
- package/src/app.ts +75 -6
- package/src/build.ts +237 -444
- package/src/config-freeze.ts +109 -0
- package/src/connection-auth.ts +101 -0
- package/src/content-freeze.ts +151 -0
- package/src/content.ts +55 -4
- package/src/core-version.ts +34 -0
- package/src/dev.ts +41 -8
- package/src/favicon.ts +325 -0
- package/src/host.ts +4 -3
- package/src/icon-letter.ts +14 -0
- package/src/index.ts +22 -0
- package/src/isolate-local.ts +33 -0
- package/src/llms-links.ts +96 -0
- package/src/manifest.ts +130 -0
- package/src/negotiate.ts +39 -15
- package/src/pipeline.ts +494 -44
- package/src/route-rank.ts +60 -0
- package/src/route-scan.ts +161 -0
- package/src/router.ts +75 -58
- package/src/rsc-runtime/rsc-worker.tsx +5 -4
- package/src/sqlite-driver.ts +36 -6
- package/src/tsconfig-jsx.ts +2 -2
- package/src/turn-events.ts +43 -0
- package/src/web-manifest.ts +38 -0
- package/src/worker.ts +197 -117
package/dist/worker.d.ts
CHANGED
|
@@ -1,56 +1,2 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { AgentConfig } from "@junejs/core/config";
|
|
4
|
-
import React from "react";
|
|
5
|
-
import { DocumentConfig } from "@junejs/core/document";
|
|
6
|
-
import { I18nConfig } from "@junejs/core/i18n";
|
|
7
|
-
import { Resources } from "@junejs/core/resources";
|
|
8
|
-
//#region src/worker.d.ts
|
|
9
|
-
type WorkerManifest = {
|
|
10
|
-
routes: Record<string, BrandedRoute>;
|
|
11
|
-
dynamicRoutes?: Array<{
|
|
12
|
-
pattern: string;
|
|
13
|
-
def: BrandedRoute;
|
|
14
|
-
}>;
|
|
15
|
-
resourceRoutes?: Array<{
|
|
16
|
-
pattern: string;
|
|
17
|
-
handler: ResourceHandler;
|
|
18
|
-
}>;
|
|
19
|
-
layoutChains?: Record<string, LayoutComponent[]>;
|
|
20
|
-
layoutBoundaries?: Record<string, {
|
|
21
|
-
index: number;
|
|
22
|
-
key: string;
|
|
23
|
-
}>;
|
|
24
|
-
loadings?: Record<string, LoadingComponent>;
|
|
25
|
-
document: DocumentConfig;
|
|
26
|
-
agent: AgentConfig;
|
|
27
|
-
agentName?: string;
|
|
28
|
-
i18n?: I18nConfig;
|
|
29
|
-
earlyHints?: string[];
|
|
30
|
-
htmlCacheControl?: string;
|
|
31
|
-
notFound?: React.ComponentType<{
|
|
32
|
-
pathname: string;
|
|
33
|
-
}>;
|
|
34
|
-
extra?: ExtraHandler;
|
|
35
|
-
resources?: (env?: unknown) => Promise<Resources> | Resources;
|
|
36
|
-
services?: (env?: unknown) => unknown;
|
|
37
|
-
};
|
|
38
|
-
declare function createWorker(manifest: WorkerManifest): {
|
|
39
|
-
fetch(request: Request, env?: unknown): Promise<Response>;
|
|
40
|
-
};
|
|
41
|
-
type AssetEnv = {
|
|
42
|
-
ASSETS?: {
|
|
43
|
-
fetch(request: Request): Promise<Response>;
|
|
44
|
-
};
|
|
45
|
-
};
|
|
46
|
-
type FetchPipeline = {
|
|
47
|
-
fetch(request: Request, env?: unknown): Promise<Response>;
|
|
48
|
-
};
|
|
49
|
-
declare function withAssets(pipeline: FetchPipeline, opts?: {
|
|
50
|
-
link?: string | null;
|
|
51
|
-
}): {
|
|
52
|
-
fetch(request: Request, env?: AssetEnv): Promise<Response>;
|
|
53
|
-
};
|
|
54
|
-
declare function withDenoAssets(pipeline: FetchPipeline): (request: Request) => Promise<Response>;
|
|
55
|
-
//#endregion
|
|
56
|
-
export { WorkerManifest, createWorker, withAssets, withDenoAssets };
|
|
1
|
+
import { a as withDenoAssets, i as withAssets, n as WorkerManifest, r as createWorker, t as WorkerExecutionContext } from "./worker-2qbQjj2c.js";
|
|
2
|
+
export { WorkerExecutionContext, WorkerManifest, createWorker, withAssets, withDenoAssets };
|
package/dist/worker.js
CHANGED
|
@@ -1,40 +1,25 @@
|
|
|
1
|
-
import { durableAgentSurface } from "./agent-durable.js";
|
|
1
|
+
import { durableAgentSurface, durableChannelSurface } from "./agent-durable.js";
|
|
2
|
+
import { compareRoutePatterns, parseSegment } from "./route-rank.js";
|
|
3
|
+
import { acceptTarget } from "./negotiate.js";
|
|
2
4
|
import { createPipeline } from "./pipeline.js";
|
|
3
5
|
import { RESERVED_PREFIX, contentTypeFor, safeRelativePath } from "./static-files.js";
|
|
6
|
+
import { blobFileStore } from "@junejs/core/agent-files";
|
|
4
7
|
//#region src/worker.ts
|
|
5
8
|
function compilePattern(pattern) {
|
|
6
9
|
const names = [];
|
|
7
10
|
let source = "";
|
|
8
11
|
let allOptional = true;
|
|
9
12
|
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 += "/([^/]+)";
|
|
13
|
+
const { kind, name } = parseSegment(seg);
|
|
14
|
+
if (name) names.push(name);
|
|
15
|
+
if (kind === "optionalCatchAll") source += "(?:/(.+))?";
|
|
16
|
+
else if (kind === "optional") source += "(?:/([^/]+))?";
|
|
17
|
+
else {
|
|
18
|
+
if (kind === "catchAll") source += "/(.+)";
|
|
19
|
+
else if (kind === "param") source += "/([^/]+)";
|
|
20
|
+
else source += "/" + seg.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
33
21
|
allOptional = false;
|
|
34
|
-
continue;
|
|
35
22
|
}
|
|
36
|
-
source += "/" + seg.replace(/[.*+?^${}()|\\]/g, "\\$&");
|
|
37
|
-
allOptional = false;
|
|
38
23
|
}
|
|
39
24
|
if (source === "") return {
|
|
40
25
|
regex: /^\/$/,
|
|
@@ -45,31 +30,73 @@ function compilePattern(pattern) {
|
|
|
45
30
|
names
|
|
46
31
|
};
|
|
47
32
|
}
|
|
48
|
-
function createWorker(manifest) {
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
pattern
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
33
|
+
function createWorker(manifest, opts = {}) {
|
|
34
|
+
const generated = new Set(manifest.generatedRoutes ?? []);
|
|
35
|
+
const row = (pattern) => ({
|
|
36
|
+
...compilePattern(pattern),
|
|
37
|
+
pattern,
|
|
38
|
+
generated: generated.has(pattern)
|
|
39
|
+
});
|
|
40
|
+
const table = [
|
|
41
|
+
...Object.entries(manifest.routes).map(([pattern, def]) => ({
|
|
42
|
+
...row(pattern),
|
|
43
|
+
def
|
|
44
|
+
})),
|
|
45
|
+
...(manifest.dynamicRoutes ?? []).map((d) => ({
|
|
46
|
+
...row(d.pattern),
|
|
47
|
+
def: d.def
|
|
48
|
+
})),
|
|
49
|
+
...(manifest.resourceRoutes ?? []).map((r) => ({
|
|
50
|
+
...row(r.pattern),
|
|
51
|
+
handler: r.handler
|
|
52
|
+
}))
|
|
53
|
+
];
|
|
54
|
+
table.sort((a, b) => Number(a.generated) - Number(b.generated) || compareRoutePatterns(a.pattern, b.pattern) || Number(!!a.handler) - Number(!!b.handler));
|
|
59
55
|
const routeList = [...Object.keys(manifest.routes), ...(manifest.dynamicRoutes ?? []).map((d) => d.pattern)].sort();
|
|
60
56
|
const chainFor = (key) => manifest.layoutChains?.[key] ?? [];
|
|
61
57
|
const loadingFor = (key) => manifest.loadings?.[key];
|
|
62
58
|
const boundaryFor = (key) => manifest.layoutBoundaries?.[key] ?? null;
|
|
59
|
+
const resolvedPage = (def, key, params) => {
|
|
60
|
+
const b = boundaryFor(key);
|
|
61
|
+
return {
|
|
62
|
+
def,
|
|
63
|
+
params,
|
|
64
|
+
chain: chainFor(key),
|
|
65
|
+
boundaryIndex: b?.index ?? null,
|
|
66
|
+
boundaryKey: b?.key ?? null,
|
|
67
|
+
loading: loadingFor(key)
|
|
68
|
+
};
|
|
69
|
+
};
|
|
63
70
|
let currentEnv;
|
|
71
|
+
const requestLocals = /* @__PURE__ */ new WeakMap();
|
|
64
72
|
const provider = manifest.resources;
|
|
65
73
|
const servicesProvider = manifest.services;
|
|
66
74
|
let servicesCache;
|
|
67
75
|
const agentSurface = manifest.agent.runtime.enabled && manifest.agentName ? (() => {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
76
|
+
const agentName = manifest.agentName;
|
|
77
|
+
const wantChannels = manifest.agent.runtime.channels !== false && (manifest.agentChannels?.length ?? 0) > 0;
|
|
78
|
+
return async (req) => {
|
|
79
|
+
const locals = requestLocals.get(req);
|
|
80
|
+
const ns = () => (locals?.env)?.AGENT;
|
|
81
|
+
const chatRes = await durableAgentSurface(ns, {
|
|
82
|
+
agentName,
|
|
83
|
+
chatPath: manifest.agent.runtime.chat.path
|
|
84
|
+
})(req);
|
|
85
|
+
if (chatRes) return chatRes;
|
|
86
|
+
if (!wantChannels) return null;
|
|
87
|
+
return durableChannelSurface(ns, {
|
|
88
|
+
agentName,
|
|
89
|
+
channels: manifest.agentChannels,
|
|
90
|
+
env: locals?.env,
|
|
91
|
+
services: servicesProvider,
|
|
92
|
+
waitUntil: locals?.ctx?.waitUntil?.bind(locals.ctx),
|
|
93
|
+
files: provider ? blobFileStore(async () => {
|
|
94
|
+
const blob = (await provider(locals?.env)).blob;
|
|
95
|
+
if (!blob) throw new Error(`agent "${agentName}": no blob resource bound in this isolate — attached files can't be read`);
|
|
96
|
+
return blob;
|
|
97
|
+
}) : void 0
|
|
98
|
+
})(req);
|
|
99
|
+
};
|
|
73
100
|
})() : void 0;
|
|
74
101
|
const pipeline = createPipeline({
|
|
75
102
|
docConfig: manifest.document,
|
|
@@ -77,6 +104,7 @@ function createWorker(manifest) {
|
|
|
77
104
|
agentSurface,
|
|
78
105
|
i18n: manifest.i18n,
|
|
79
106
|
routeList: () => routeList,
|
|
107
|
+
staticBuild: opts.staticBuild,
|
|
80
108
|
earlyHints: manifest.earlyHints,
|
|
81
109
|
htmlCacheControl: manifest.htmlCacheControl,
|
|
82
110
|
notFoundComponent: manifest.notFound,
|
|
@@ -85,79 +113,67 @@ function createWorker(manifest) {
|
|
|
85
113
|
services: servicesProvider ? () => (servicesCache ??= { v: servicesProvider(currentEnv) }).v : void 0,
|
|
86
114
|
resolve: async (pathname) => {
|
|
87
115
|
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) {
|
|
116
|
+
if (staticDef && !generated.has(pathname)) return resolvedPage(staticDef, pathname, {});
|
|
117
|
+
for (const r of table) {
|
|
118
118
|
const m = pathname.match(r.regex);
|
|
119
|
-
if (m)
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
}
|
|
119
|
+
if (!m) continue;
|
|
120
|
+
const params = Object.fromEntries(r.names.flatMap((n, i) => {
|
|
121
|
+
const v = m[i + 1];
|
|
122
|
+
return v === void 0 ? [] : [[n, decodeURIComponent(v)]];
|
|
123
|
+
}));
|
|
124
|
+
return r.handler ? {
|
|
125
|
+
handler: r.handler,
|
|
126
|
+
params
|
|
127
|
+
} : resolvedPage(r.def, r.pattern, params);
|
|
129
128
|
}
|
|
130
129
|
return null;
|
|
131
130
|
}
|
|
132
131
|
});
|
|
133
|
-
return { fetch(request, env) {
|
|
132
|
+
return { fetch(request, env, ctx) {
|
|
134
133
|
currentEnv = env;
|
|
134
|
+
requestLocals.set(request, {
|
|
135
|
+
env,
|
|
136
|
+
ctx
|
|
137
|
+
});
|
|
135
138
|
return pipeline.fetch(request);
|
|
136
139
|
} };
|
|
137
140
|
}
|
|
138
141
|
const estimateTokens = (s) => String(Math.ceil(s.length / 4));
|
|
142
|
+
function varyAccept(headers) {
|
|
143
|
+
const vary = headers.get("vary");
|
|
144
|
+
if (!vary) headers.set("vary", "accept");
|
|
145
|
+
else if (vary.trim() !== "*" && !vary.split(",").some((v) => v.trim().toLowerCase() === "accept")) headers.set("vary", `${vary}, accept`);
|
|
146
|
+
}
|
|
139
147
|
function withAssets(pipeline, opts = {}) {
|
|
140
|
-
return { async fetch(request, env) {
|
|
148
|
+
return { async fetch(request, env, ctx) {
|
|
141
149
|
const assets = env?.ASSETS;
|
|
142
150
|
const url = new URL(request.url);
|
|
143
151
|
const isPagePath = !/\.[a-z0-9]+$/i.test(url.pathname);
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
152
|
+
const target = isPagePath ? acceptTarget(request.headers.get("accept") ?? "") : null;
|
|
153
|
+
if (target && (request.method === "GET" || request.method === "HEAD")) {
|
|
154
|
+
if (assets && request.method === "GET" && (target === "md" || target === "json")) {
|
|
155
|
+
const base = url.pathname === "/" ? "/index" : url.pathname.replace(/\/+$/, "");
|
|
156
|
+
const assetUrl = new URL(url);
|
|
157
|
+
assetUrl.pathname = `${base}.${target}`;
|
|
158
|
+
const a = await assets.fetch(new Request(assetUrl.toString(), { headers: request.headers }));
|
|
159
|
+
if (a.ok) {
|
|
160
|
+
const headers = new Headers(a.headers);
|
|
161
|
+
if (opts.link) headers.set("link", opts.link);
|
|
162
|
+
varyAccept(headers);
|
|
163
|
+
if (target === "json") return new Response(a.body, {
|
|
164
|
+
status: 200,
|
|
165
|
+
headers
|
|
166
|
+
});
|
|
167
|
+
const body = await a.text();
|
|
168
|
+
headers.set("content-type", "text/markdown; charset=utf-8");
|
|
169
|
+
headers.set("x-markdown-tokens", estimateTokens(body));
|
|
170
|
+
return new Response(body, {
|
|
171
|
+
status: 200,
|
|
172
|
+
headers
|
|
173
|
+
});
|
|
174
|
+
}
|
|
160
175
|
}
|
|
176
|
+
return pipeline.fetch(request, env, ctx);
|
|
161
177
|
}
|
|
162
178
|
if (assets && (request.method === "GET" || request.method === "HEAD")) {
|
|
163
179
|
const a = await assets.fetch(request);
|
|
@@ -165,10 +181,12 @@ function withAssets(pipeline, opts = {}) {
|
|
|
165
181
|
const ct = a.headers.get("content-type") ?? "";
|
|
166
182
|
const addLink = !!opts.link && ct.includes("text/html") && !a.headers.has("link");
|
|
167
183
|
const immutable = /\.[a-f0-9]{8,}\.(css|js)$/.test(url.pathname);
|
|
168
|
-
|
|
184
|
+
const negotiated = isPagePath && ct.includes("text/html");
|
|
185
|
+
if (addLink || immutable || negotiated) {
|
|
169
186
|
const headers = new Headers(a.headers);
|
|
170
187
|
if (addLink) headers.set("link", opts.link);
|
|
171
188
|
if (immutable) headers.set("cache-control", "public, max-age=31536000, immutable");
|
|
189
|
+
if (negotiated) varyAccept(headers);
|
|
172
190
|
return new Response(a.body, {
|
|
173
191
|
status: a.status,
|
|
174
192
|
headers
|
|
@@ -177,7 +195,7 @@ function withAssets(pipeline, opts = {}) {
|
|
|
177
195
|
return a;
|
|
178
196
|
}
|
|
179
197
|
}
|
|
180
|
-
return pipeline.fetch(request, env);
|
|
198
|
+
return pipeline.fetch(request, env, ctx);
|
|
181
199
|
} };
|
|
182
200
|
}
|
|
183
201
|
function withDenoAssets(pipeline) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junejs/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
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",
|
|
@@ -36,6 +36,12 @@
|
|
|
36
36
|
"types": "./dist/agent-discover.d.ts",
|
|
37
37
|
"default": "./dist/agent-discover.js"
|
|
38
38
|
},
|
|
39
|
+
"./agent-compile": {
|
|
40
|
+
"source": "./src/agent-compile.ts",
|
|
41
|
+
"bun": "./src/agent-compile.ts",
|
|
42
|
+
"types": "./dist/agent-compile.d.ts",
|
|
43
|
+
"default": "./dist/agent-compile.js"
|
|
44
|
+
},
|
|
39
45
|
"./db": {
|
|
40
46
|
"source": "./src/db.ts",
|
|
41
47
|
"bun": "./src/db.ts",
|
|
@@ -117,7 +123,8 @@
|
|
|
117
123
|
},
|
|
118
124
|
"files": [
|
|
119
125
|
"src",
|
|
120
|
-
"dist"
|
|
126
|
+
"dist",
|
|
127
|
+
"assets"
|
|
121
128
|
],
|
|
122
129
|
"scripts": {
|
|
123
130
|
"test": "bun test",
|
|
@@ -125,20 +132,22 @@
|
|
|
125
132
|
"build": "tsdown"
|
|
126
133
|
},
|
|
127
134
|
"dependencies": {
|
|
128
|
-
"@junejs/
|
|
129
|
-
"@junejs/db": ">=0.0.33",
|
|
135
|
+
"@junejs/db": "^0.2.0",
|
|
130
136
|
"@momiji-rs/sparkdown": "^0.0.4",
|
|
137
|
+
"@resvg/resvg-wasm": "^2.6.2",
|
|
131
138
|
"lightningcss": "^1.27.0",
|
|
132
|
-
"oxc-parser": "^0.
|
|
139
|
+
"oxc-parser": "^0.152.0",
|
|
133
140
|
"rolldown": "^1.1.0"
|
|
134
141
|
},
|
|
135
142
|
"peerDependencies": {
|
|
143
|
+
"@junejs/core": "^0.2.0",
|
|
136
144
|
"react": "^19.2.0",
|
|
137
145
|
"react-dom": "^19.2.0",
|
|
138
|
-
"react-server-dom-webpack": "19.
|
|
146
|
+
"react-server-dom-webpack": "19.3.0",
|
|
139
147
|
"pg": "^8.0.0",
|
|
140
148
|
"mysql2": "^3.0.0",
|
|
141
|
-
"@libsql/client": "^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0"
|
|
149
|
+
"@libsql/client": "^0.14.0 || ^0.15.0 || ^0.16.0 || ^0.17.0",
|
|
150
|
+
"@anthropic-ai/sdk": ">=0.60.0"
|
|
142
151
|
},
|
|
143
152
|
"peerDependenciesMeta": {
|
|
144
153
|
"react-server-dom-webpack": {
|
|
@@ -152,17 +161,21 @@
|
|
|
152
161
|
},
|
|
153
162
|
"@libsql/client": {
|
|
154
163
|
"optional": true
|
|
164
|
+
},
|
|
165
|
+
"@anthropic-ai/sdk": {
|
|
166
|
+
"optional": true
|
|
155
167
|
}
|
|
156
168
|
},
|
|
157
169
|
"repository": {
|
|
158
170
|
"type": "git",
|
|
159
|
-
"url": "https://github.com/junebuild/june"
|
|
171
|
+
"url": "git+https://github.com/junebuild/june.git",
|
|
172
|
+
"directory": "packages/june"
|
|
160
173
|
},
|
|
161
174
|
"devDependencies": {
|
|
162
175
|
"@libsql/client": "^0.17.3",
|
|
163
176
|
"@types/pg": "^8.20.0",
|
|
164
177
|
"mysql2": "^3.22.5",
|
|
165
178
|
"pg": "^8.21.0",
|
|
166
|
-
"react-server-dom-webpack": "19.
|
|
179
|
+
"react-server-dom-webpack": "19.3.0"
|
|
167
180
|
}
|
|
168
181
|
}
|
package/src/adapter.ts
CHANGED
|
@@ -7,10 +7,11 @@
|
|
|
7
7
|
// `workers()` is the zero-config default (this file, no import). Other targets
|
|
8
8
|
// are explicit `deploy: { adapter: vercel() }` from their own package.
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
|
-
import { cp, copyFile, mkdir, readdir, rm, writeFile } from "node:fs/promises";
|
|
10
|
+
import { cp, copyFile, mkdir, readdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
11
11
|
import { basename, dirname, join } from "node:path";
|
|
12
12
|
|
|
13
13
|
import type { JuneConfig } from "@junejs/core/config";
|
|
14
|
+
import { stripJsonc } from "./tsconfig-jsx";
|
|
14
15
|
|
|
15
16
|
export type AdapterCapabilities = {
|
|
16
17
|
runtime: "edge" | "node" | "serverless" | "native" | "static";
|
|
@@ -20,6 +21,9 @@ export type AdapterCapabilities = {
|
|
|
20
21
|
persistentConnections: boolean;
|
|
21
22
|
// Who serves prerendered static files: the platform CDN, the server, or none.
|
|
22
23
|
assets: "platform" | "server" | "none";
|
|
24
|
+
// Durable Objects (the per-session agent actor). Only workers() today; the
|
|
25
|
+
// build skips the durable-agent mount (with a notice) when absent.
|
|
26
|
+
durableObjects?: boolean;
|
|
23
27
|
};
|
|
24
28
|
|
|
25
29
|
// What the adapter contributes to the generated worker entry: extra imports and
|
|
@@ -37,6 +41,11 @@ export type ResourcePlan = {
|
|
|
37
41
|
// A declared `db` → a D1 binding named `binding`. The runtime provider
|
|
38
42
|
// (bindWorkerResources) reads env[binding]; keep in sync (default "DB").
|
|
39
43
|
db?: { binding: string; databaseName: string };
|
|
44
|
+
// A durable agent (an app/agent directory on a DO-capable target) → the DO
|
|
45
|
+
// namespace binding createWorker reads (env.AGENT) and the class the
|
|
46
|
+
// generated entry exports. workers() emits durable_objects + the SQLite-class
|
|
47
|
+
// migration from it.
|
|
48
|
+
agent?: { binding: string; className: string };
|
|
40
49
|
};
|
|
41
50
|
|
|
42
51
|
export type AdapterEmitContext = {
|
|
@@ -77,13 +86,60 @@ export interface JuneAdapter {
|
|
|
77
86
|
emit(ctx: AdapterEmitContext): Promise<void>;
|
|
78
87
|
}
|
|
79
88
|
|
|
89
|
+
// Whether an app-owned wrangler config actually BINDS the generated DO class
|
|
90
|
+
// under the binding name createWorker reads (env.AGENT) — not merely mentions
|
|
91
|
+
// the class (a migrations entry does that too, and a binding under another
|
|
92
|
+
// name never reaches the chat route). JSONC is parsed for the real
|
|
93
|
+
// durable_objects.bindings entry; TOML is scanned per
|
|
94
|
+
// [[durable_objects.bindings]] table with comments stripped, so a
|
|
95
|
+
// commented-out binding never counts. The TOML scan covers wrangler's
|
|
96
|
+
// canonical table form; an exotic inline-table config may draw a spurious
|
|
97
|
+
// (advisory) warning, never a suppressed one. Pure — unit-tested directly.
|
|
98
|
+
export function hasDurableBinding(text: string, file: string, binding: string, className: string): boolean {
|
|
99
|
+
if (file.endsWith(".toml")) {
|
|
100
|
+
// Strip comments: '#' to end of line, unless inside a double-quoted string.
|
|
101
|
+
const lines = text.split("\n").map((line) => {
|
|
102
|
+
let inString = false;
|
|
103
|
+
for (let i = 0; i < line.length; i++) {
|
|
104
|
+
const ch = line[i];
|
|
105
|
+
if (ch === '"' && line[i - 1] !== "\\") inString = !inString;
|
|
106
|
+
else if (ch === "#" && !inString) return line.slice(0, i);
|
|
107
|
+
}
|
|
108
|
+
return line;
|
|
109
|
+
});
|
|
110
|
+
let inBindings = false;
|
|
111
|
+
let name: string | undefined;
|
|
112
|
+
let cls: string | undefined;
|
|
113
|
+
const matches = () => name === binding && cls === className;
|
|
114
|
+
for (const line of lines) {
|
|
115
|
+
const t = line.trim();
|
|
116
|
+
if (t.startsWith("[")) {
|
|
117
|
+
if (inBindings && matches()) return true; // the table that just ended bound it
|
|
118
|
+
inBindings = /^\[\[\s*durable_objects\.bindings\s*\]\]$/.test(t);
|
|
119
|
+
name = cls = undefined;
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
if (!inBindings) continue;
|
|
123
|
+
const kv = t.match(/^(name|class_name)\s*=\s*"([^"]*)"/);
|
|
124
|
+
if (kv) kv[1] === "name" ? (name = kv[2]) : (cls = kv[2]);
|
|
125
|
+
}
|
|
126
|
+
return inBindings && matches();
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
const parsed = JSON.parse(stripJsonc(text)) as { durable_objects?: { bindings?: { name?: string; class_name?: string }[] } };
|
|
130
|
+
return (parsed.durable_objects?.bindings ?? []).some((b) => b.name === binding && b.class_name === className);
|
|
131
|
+
} catch {
|
|
132
|
+
return text.includes(className); // unparsable — loose fallback, never crash the build
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
80
136
|
// The built-in default. Reproduces exactly what build.ts emitted inline:
|
|
81
137
|
// withAssets(pipeline) as the entry, and a wrangler.jsonc with the assets
|
|
82
138
|
// binding (run_worker_first) + optional custom domain.
|
|
83
139
|
export function workers(opts?: { name?: string; domain?: string }): JuneAdapter {
|
|
84
140
|
return {
|
|
85
141
|
name: "workers",
|
|
86
|
-
capabilities: { runtime: "edge", persistentConnections: true, assets: "platform" },
|
|
142
|
+
capabilities: { runtime: "edge", persistentConnections: true, assets: "platform", durableObjects: true },
|
|
87
143
|
conditions: ["source", "workerd", "edge", "import", "default"],
|
|
88
144
|
// workers-og is a workerd-native WASM package: rolldown cannot bundle its
|
|
89
145
|
// .wasm assets (wrangler's own bundler applies the CompiledWasm rule). Mark
|
|
@@ -100,7 +156,23 @@ export function workers(opts?: { name?: string; domain?: string }): JuneAdapter
|
|
|
100
156
|
|
|
101
157
|
async emit({ appRoot, outDir, hasAssets, config, plan, defaultName }) {
|
|
102
158
|
// An app that manages its own wrangler config wins — don't overwrite it.
|
|
103
|
-
|
|
159
|
+
// But a durable agent NEEDS its DO binding: warn when the app-owned config
|
|
160
|
+
// doesn't BIND the generated class under the name createWorker reads
|
|
161
|
+
// (hasDurableBinding above — a migrations-only mention, a binding under
|
|
162
|
+
// another name, or a commented-out table must all still warn), instead of
|
|
163
|
+
// silently shipping a worker whose chat surface 404s.
|
|
164
|
+
const own = ["wrangler.toml", "wrangler.jsonc"].map((f) => join(appRoot, f)).find(existsSync);
|
|
165
|
+
if (own) {
|
|
166
|
+
if (plan.agent) {
|
|
167
|
+
const cls = plan.agent.className;
|
|
168
|
+
const bound = hasDurableBinding(await readFile(own, "utf8"), own, plan.agent.binding, cls);
|
|
169
|
+
if (!bound) {
|
|
170
|
+
const snippet = own.endsWith(".toml")
|
|
171
|
+
? `[[durable_objects.bindings]]\nname = "${plan.agent.binding}"\nclass_name = "${cls}"\n\n[[migrations]]\ntag = "v1"\nnew_sqlite_classes = ["${cls}"]`
|
|
172
|
+
: `"durable_objects": { "bindings": [{ "name": "${plan.agent.binding}", "class_name": "${cls}" }] },\n"migrations": [{ "tag": "v1", "new_sqlite_classes": ["${cls}"] }]`;
|
|
173
|
+
console.warn(`[june build] durable agent built, but ${basename(own)} doesn't bind it — add:\n${snippet}`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
104
176
|
return;
|
|
105
177
|
}
|
|
106
178
|
const deployCfg = config.deploy;
|
|
@@ -133,6 +205,15 @@ export function workers(opts?: { name?: string; domain?: string }): JuneAdapter
|
|
|
133
205
|
],
|
|
134
206
|
}
|
|
135
207
|
: {}),
|
|
208
|
+
// The durable agent: one SQLite-backed DO class = the per-session
|
|
209
|
+
// AgentSession actor (its ctx.storage.sql is the durable loop's
|
|
210
|
+
// synchronous store). The binding name is what createWorker reads.
|
|
211
|
+
...(plan.agent
|
|
212
|
+
? {
|
|
213
|
+
durable_objects: { bindings: [{ name: plan.agent.binding, class_name: plan.agent.className }] },
|
|
214
|
+
migrations: [{ tag: "v1", new_sqlite_classes: [plan.agent.className] }],
|
|
215
|
+
}
|
|
216
|
+
: {}),
|
|
136
217
|
// config deploy.domain → a Workers custom domain; without it the
|
|
137
218
|
// regenerated file would silently drop a hand-attached domain.
|
|
138
219
|
...(opts?.domain ?? deployCfg?.domain
|