@junejs/server 0.1.0-dev.5 → 0.1.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/dist/adapter.d.ts +1 -0
- package/dist/adapter.js +9 -2
- package/dist/agent-discover.js +2 -2
- package/dist/agent-durable.d.ts +13 -1
- package/dist/agent-durable.js +38 -3
- package/dist/app.js +39 -2
- package/dist/build.js +41 -4
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/{pipeline-B2V6tKfO.d.ts → pipeline-CCp9idQ4.d.ts} +1 -0
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +5 -2
- package/dist/static-files.d.ts +6 -0
- package/dist/static-files.js +56 -0
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +29 -24
- package/package.json +3 -3
- package/src/adapter.ts +16 -3
- package/src/agent-discover.ts +5 -2
- package/src/agent-durable.ts +108 -4
- package/src/app.ts +84 -2
- package/src/build.ts +70 -4
- package/src/index.ts +7 -0
- package/src/pipeline.ts +7 -1
- package/src/static-files.ts +84 -0
- package/src/worker.ts +58 -29
package/dist/adapter.d.ts
CHANGED
package/dist/adapter.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { copyFile, cp, mkdir, readdir, rm, writeFile } from "node:fs/promises";
|
|
3
|
-
import { join } from "node:path";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
4
|
//#region src/adapter.ts
|
|
5
5
|
function workers(opts) {
|
|
6
6
|
return {
|
|
@@ -78,7 +78,7 @@ function vercel(opts) {
|
|
|
78
78
|
wrap: (pipelineVar) => isEdge ? env + `export default (request) => ${pipelineVar}.fetch(request, __env);` : env + `export default { fetch: (request) => ${pipelineVar}.fetch(request, __env) };`
|
|
79
79
|
};
|
|
80
80
|
},
|
|
81
|
-
async emit({ appRoot, outDir, hasAssets }) {
|
|
81
|
+
async emit({ appRoot, outDir, hasAssets, publicFiles }) {
|
|
82
82
|
const out = join(appRoot, ".vercel", "output");
|
|
83
83
|
const fnDir = join(out, "functions", `${VERCEL_FUNCTION}.func`);
|
|
84
84
|
await rm(out, {
|
|
@@ -106,6 +106,13 @@ function vercel(opts) {
|
|
|
106
106
|
const src = join(outDir, "assets", "_june");
|
|
107
107
|
if (existsSync(src)) await cp(src, join(out, "static", "_june"), { recursive: true });
|
|
108
108
|
}
|
|
109
|
+
for (const rel of publicFiles ?? []) {
|
|
110
|
+
const from = join(outDir, "assets", rel);
|
|
111
|
+
if (!existsSync(from)) continue;
|
|
112
|
+
const to = join(out, "static", rel);
|
|
113
|
+
await mkdir(dirname(to), { recursive: true });
|
|
114
|
+
await copyFile(from, to);
|
|
115
|
+
}
|
|
109
116
|
await writeFile(join(out, "config.json"), JSON.stringify({
|
|
110
117
|
version: 3,
|
|
111
118
|
routes: [
|
package/dist/agent-discover.js
CHANGED
|
@@ -2,7 +2,7 @@ import { existsSync } from "node:fs";
|
|
|
2
2
|
import { readFile, readdir } from "node:fs/promises";
|
|
3
3
|
import { basename, extname, join } from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
-
import { defineAgent } from "@junejs/core/agent-config";
|
|
5
|
+
import { defineAgent, resolveChannel } from "@junejs/core/agent-config";
|
|
6
6
|
import { connectAll } from "@junejs/core/connections";
|
|
7
7
|
//#region src/agent-discover.ts
|
|
8
8
|
async function scan(dir, ext) {
|
|
@@ -48,7 +48,7 @@ async function discoverAgent(dir) {
|
|
|
48
48
|
const channels = [];
|
|
49
49
|
for (const f of await scan(join(dir, "channels"), ".ts")) {
|
|
50
50
|
const mod = await import(pathToFileURL(f).href);
|
|
51
|
-
if (mod.default) channels.push(mod.default);
|
|
51
|
+
if (mod.default) channels.push(resolveChannel(mod.default, process.env));
|
|
52
52
|
}
|
|
53
53
|
const connectionDefs = [];
|
|
54
54
|
for (const f of await scan(join(dir, "connections"), ".ts")) {
|
package/dist/agent-durable.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { Channel, ChannelFactory } from "@junejs/core/agent-config";
|
|
1
2
|
import { Model, Msg, SessionStore, Tool } from "@junejs/core/agent-runtime";
|
|
3
|
+
import { Resources } from "@junejs/core/resources";
|
|
2
4
|
//#region src/agent-durable.d.ts
|
|
3
5
|
interface SqlStorageCursor<T = Record<string, unknown>> {
|
|
4
6
|
toArray(): T[];
|
|
@@ -40,9 +42,13 @@ type DoAgentDef = {
|
|
|
40
42
|
model: Model;
|
|
41
43
|
tools: Tool[];
|
|
42
44
|
instructions?: string;
|
|
45
|
+
resources?: Resources;
|
|
46
|
+
services?: unknown;
|
|
43
47
|
};
|
|
44
48
|
declare class AgentDurableObject {
|
|
45
49
|
private session;
|
|
50
|
+
private readonly resources;
|
|
51
|
+
private readonly services;
|
|
46
52
|
constructor(state: DurableObjectState, def: DoAgentDef);
|
|
47
53
|
turn(input: {
|
|
48
54
|
turnId?: string;
|
|
@@ -56,5 +62,11 @@ declare function durableAgentSurface(getNamespace: () => DurableObjectNamespace
|
|
|
56
62
|
agentName: string;
|
|
57
63
|
chatPath?: string;
|
|
58
64
|
}): (req: Request) => Promise<Response | null>;
|
|
65
|
+
declare function durableChannelSurface(getNamespace: () => DurableObjectNamespace | undefined, opts: {
|
|
66
|
+
agentName: string;
|
|
67
|
+
channels: (Channel | ChannelFactory)[];
|
|
68
|
+
env: unknown;
|
|
69
|
+
waitUntil?: (p: Promise<unknown>) => void;
|
|
70
|
+
}): (req: Request) => Promise<Response | null>;
|
|
59
71
|
//#endregion
|
|
60
|
-
export { AgentDurableObject, DoAgentDef, DoSessionStore, DurableObjectNamespace, DurableObjectState, DurableObjectStub, DurableStorage, SqlStorage, SqlStorageCursor, durableAgentSurface, durableFetch };
|
|
72
|
+
export { AgentDurableObject, DoAgentDef, DoSessionStore, DurableObjectNamespace, DurableObjectState, DurableObjectStub, DurableStorage, SqlStorage, SqlStorageCursor, durableAgentSurface, durableChannelSurface, durableFetch };
|
package/dist/agent-durable.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { channelDispatch, resolveChannel } from "@junejs/core/agent-config";
|
|
1
2
|
import { AgentSession, withSystem } from "@junejs/core/agent-runtime";
|
|
3
|
+
import { ensureScope, runInScope } from "@junejs/db";
|
|
2
4
|
//#region src/agent-durable.ts
|
|
3
5
|
var DoSessionStore = class {
|
|
4
6
|
storage;
|
|
@@ -61,13 +63,21 @@ const crossDoUnsupported = { session() {
|
|
|
61
63
|
} };
|
|
62
64
|
var AgentDurableObject = class {
|
|
63
65
|
session;
|
|
66
|
+
resources;
|
|
67
|
+
services;
|
|
64
68
|
constructor(state, def) {
|
|
65
69
|
const store = new DoSessionStore(state.storage);
|
|
66
70
|
const model = def.instructions ? withSystem(def.model, def.instructions) : def.model;
|
|
71
|
+
this.resources = def.resources ?? {};
|
|
72
|
+
this.services = def.services;
|
|
67
73
|
this.session = new AgentSession(def.name ?? "agent", "self", store, new InProcBroadcaster(), model, def.tools, crossDoUnsupported);
|
|
68
74
|
}
|
|
69
|
-
turn(input) {
|
|
70
|
-
|
|
75
|
+
async turn(input) {
|
|
76
|
+
await ensureScope();
|
|
77
|
+
return runInScope({
|
|
78
|
+
resources: this.resources,
|
|
79
|
+
services: this.services
|
|
80
|
+
}, () => this.session.turn(input));
|
|
71
81
|
}
|
|
72
82
|
transcript() {
|
|
73
83
|
return this.session.transcript();
|
|
@@ -102,5 +112,30 @@ function durableAgentSurface(getNamespace, opts) {
|
|
|
102
112
|
}));
|
|
103
113
|
};
|
|
104
114
|
}
|
|
115
|
+
function durableChannelSurface(getNamespace, opts) {
|
|
116
|
+
return channelDispatch(opts.channels.map((c) => resolveChannel(c, opts.env)), {
|
|
117
|
+
agent: {
|
|
118
|
+
name: opts.agentName,
|
|
119
|
+
instructions: "",
|
|
120
|
+
tools: [],
|
|
121
|
+
skills: [],
|
|
122
|
+
channels: [],
|
|
123
|
+
connections: []
|
|
124
|
+
},
|
|
125
|
+
waitUntil: opts.waitUntil,
|
|
126
|
+
run: async (message, o) => {
|
|
127
|
+
const namespace = getNamespace();
|
|
128
|
+
if (!namespace) throw new Error("durableChannelSurface: no Durable Object namespace bound (env.AGENT)");
|
|
129
|
+
const { text } = await (await durableFetch(namespace, opts.agentName, o?.session ?? "default", new Request("https://do/turn", {
|
|
130
|
+
method: "POST",
|
|
131
|
+
body: JSON.stringify({
|
|
132
|
+
userText: message,
|
|
133
|
+
turnId: o?.turnId
|
|
134
|
+
})
|
|
135
|
+
}))).json();
|
|
136
|
+
return text;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
105
140
|
//#endregion
|
|
106
|
-
export { AgentDurableObject, DoSessionStore, durableAgentSurface, durableFetch };
|
|
141
|
+
export { AgentDurableObject, DoSessionStore, durableAgentSurface, durableChannelSurface, durableFetch };
|
package/dist/app.js
CHANGED
|
@@ -8,7 +8,9 @@ import { MODULE_STYLES_URL, buildModuleCss, registerCssModules } from "./css-mod
|
|
|
8
8
|
import { CLIENT_SCRIPT_URL, bundleClientSplit, findClientEntry } from "./client-bundle.js";
|
|
9
9
|
import { generateIslandRegistry } from "./island-registry.js";
|
|
10
10
|
import { STYLES_URL, findGlobalCss, globalCssUsesTailwind, processCssCached } from "./css.js";
|
|
11
|
-
import {
|
|
11
|
+
import { contentTypeFor, safeRelativePath } from "./static-files.js";
|
|
12
|
+
import { existsSync, lstatSync, realpathSync } from "node:fs";
|
|
13
|
+
import { readFile } from "node:fs/promises";
|
|
12
14
|
import { dirname, join, resolve } from "node:path";
|
|
13
15
|
import { pathToFileURL } from "node:url";
|
|
14
16
|
import { buildSystemPrompt } from "@junejs/core/agent-config";
|
|
@@ -62,6 +64,11 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
62
64
|
const appDir = resolve(appDirInput);
|
|
63
65
|
const juneRoutesDir = join(dirname(appDir), ".june", "routes");
|
|
64
66
|
const hasJuneRoutes = existsSync(juneRoutesDir);
|
|
67
|
+
const publicDir = join(dirname(appDir), "public");
|
|
68
|
+
let publicRoot = null;
|
|
69
|
+
try {
|
|
70
|
+
if (lstatSync(publicDir).isDirectory()) publicRoot = realpathSync(publicDir);
|
|
71
|
+
} catch {}
|
|
65
72
|
const agent = resolveAgent(config.agent);
|
|
66
73
|
const speculation = config.speculation;
|
|
67
74
|
const clientEntry = findClientEntry(appDir) ?? findClientEntry(join(dirname(appDir), ".june", "routes"));
|
|
@@ -91,6 +98,8 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
91
98
|
return [...own, ...gen].sort();
|
|
92
99
|
};
|
|
93
100
|
const resources = memoizeResources(config.resources);
|
|
101
|
+
let servicesCache;
|
|
102
|
+
const services = config.services ? () => (servicesCache ??= { v: config.services.make(process.env) }).v : void 0;
|
|
94
103
|
config.dataLayer?.install();
|
|
95
104
|
let pipelinePromise;
|
|
96
105
|
const getPipeline = () => pipelinePromise ??= buildPipeline();
|
|
@@ -136,6 +145,7 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
136
145
|
routeList: routePaths,
|
|
137
146
|
earlyHints: config.earlyHints,
|
|
138
147
|
resources,
|
|
148
|
+
services,
|
|
139
149
|
notFoundComponent,
|
|
140
150
|
resolve: async (pathname) => {
|
|
141
151
|
const match = await matchRouteTree(appDir, pathname, { pageConvention: true }) ?? (hasJuneRoutes ? await matchRouteTree(juneRoutesDir, pathname, { pageConvention: true }) : null);
|
|
@@ -201,7 +211,34 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
201
211
|
});
|
|
202
212
|
}
|
|
203
213
|
}
|
|
204
|
-
|
|
214
|
+
const toPipeline = () => runWithTrace(newTrace(), async () => (await getPipeline()).fetch(request));
|
|
215
|
+
if (publicRoot !== null && (request.method === "GET" || request.method === "HEAD")) {
|
|
216
|
+
const rel = safeRelativePath(new URL(request.url).pathname);
|
|
217
|
+
if (rel !== null && rel.split("/")[0] !== "_june") {
|
|
218
|
+
const segments = rel.split("/");
|
|
219
|
+
const file = join(publicRoot, ...segments);
|
|
220
|
+
let stat;
|
|
221
|
+
try {
|
|
222
|
+
stat = lstatSync(file);
|
|
223
|
+
} catch {}
|
|
224
|
+
let real = null;
|
|
225
|
+
if (stat?.isFile()) try {
|
|
226
|
+
real = realpathSync(file);
|
|
227
|
+
} catch {}
|
|
228
|
+
if (real === file) {
|
|
229
|
+
const headers = {
|
|
230
|
+
"content-type": contentTypeFor(rel),
|
|
231
|
+
"cache-control": "public, max-age=0, must-revalidate"
|
|
232
|
+
};
|
|
233
|
+
if (request.method === "HEAD") return Promise.resolve(new Response(null, { headers: {
|
|
234
|
+
...headers,
|
|
235
|
+
"content-length": String(stat.size)
|
|
236
|
+
} }));
|
|
237
|
+
return readFile(file).then((buf) => new Response(buf, { headers }), () => toPipeline());
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
return toPipeline();
|
|
205
242
|
},
|
|
206
243
|
async warmup() {
|
|
207
244
|
const files = await routeFiles(appDir, { pageConvention: true });
|
package/dist/build.js
CHANGED
|
@@ -6,12 +6,13 @@ import { jsxTransform } from "./tsconfig-jsx.js";
|
|
|
6
6
|
import { CLIENT_SCRIPT_URL, bundleClientToFile, findClientEntry } from "./client-bundle.js";
|
|
7
7
|
import { generateIslandRegistry } from "./island-registry.js";
|
|
8
8
|
import { STYLES_URL, cssTargets, findGlobalCss, globalCssUsesTailwind, minifyCss, processCss } from "./css.js";
|
|
9
|
+
import { RESERVED_PREFIX } from "./static-files.js";
|
|
9
10
|
import { findJuneConfigPath, loadJuneConfig } from "./config-loader.js";
|
|
10
11
|
import { generateContentModule } from "./content.js";
|
|
11
12
|
import { createWorker } from "./worker.js";
|
|
12
13
|
import { buildRsc, findRscRoutes } from "./rsc-build.js";
|
|
13
|
-
import { existsSync } from "node:fs";
|
|
14
|
-
import { mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
14
|
+
import { existsSync, lstatSync } from "node:fs";
|
|
15
|
+
import { copyFile, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
15
16
|
import { basename, dirname, join, relative, resolve, sep } from "node:path";
|
|
16
17
|
import { pathToFileURL } from "node:url";
|
|
17
18
|
import { routeFromModule } from "@junejs/core/route";
|
|
@@ -279,6 +280,15 @@ function importPath(fromDir, file) {
|
|
|
279
280
|
const p = relative(fromDir, file).split(sep).join("/").replace(/\.[^.]+$/, "");
|
|
280
281
|
return p.startsWith(".") ? p : `./${p}`;
|
|
281
282
|
}
|
|
283
|
+
async function collectFiles(dir, base = dir) {
|
|
284
|
+
const out = [];
|
|
285
|
+
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
|
286
|
+
const abs = join(dir, entry.name);
|
|
287
|
+
if (entry.isDirectory()) out.push(...await collectFiles(abs, base));
|
|
288
|
+
else if (entry.isFile()) out.push(relative(base, abs).split(sep).join("/"));
|
|
289
|
+
}
|
|
290
|
+
return out;
|
|
291
|
+
}
|
|
282
292
|
async function juneBuild(appRoot, options = {}) {
|
|
283
293
|
const appDir = join(appRoot, "app");
|
|
284
294
|
if (!existsSync(appDir)) throw new Error(`no app/ directory in ${appRoot} — is this a June app?`);
|
|
@@ -401,6 +411,12 @@ async function juneBuild(appRoot, options = {}) {
|
|
|
401
411
|
const dataLayerModule = fullConfig.dataLayer?.module;
|
|
402
412
|
if (dataLayerModule) imports.push(`import { installDataLayer } from ${JSON.stringify(dataLayerModule)};`);
|
|
403
413
|
const dataLayerBoot = dataLayerModule ? "\ninstallDataLayer();\n" : "";
|
|
414
|
+
const servicesModule = fullConfig.services?.module;
|
|
415
|
+
if (servicesModule) {
|
|
416
|
+
const spec = /^[./]/.test(servicesModule) ? importPath(genDir, resolve(appRoot, servicesModule)) : servicesModule;
|
|
417
|
+
imports.push(`import { services as __appServices } from ${JSON.stringify(spec)};`);
|
|
418
|
+
}
|
|
419
|
+
const servicesField = servicesModule ? `\n services: __appServices,` : "";
|
|
404
420
|
const entry = `// AUTO-GENERATED by \`june build\` — do not edit. Regenerate: june build .
|
|
405
421
|
${adapterEntry.imports.join("\n")}
|
|
406
422
|
${imports.join("\n")}
|
|
@@ -420,7 +436,7 @@ ${loadings.join("\n")}
|
|
|
420
436
|
},
|
|
421
437
|
document: ${JSON.stringify(frozen.document, null, 2).replace(/\n/g, "\n ")},
|
|
422
438
|
agent: ${JSON.stringify(frozen.agent)},${frozen.i18n ? `\n i18n: ${JSON.stringify(frozen.i18n)},` : ""}
|
|
423
|
-
earlyHints: ${JSON.stringify(frozen.earlyHints)},${builtExtraFile ? "\n extra," : ""}${resourcesField}
|
|
439
|
+
earlyHints: ${JSON.stringify(frozen.earlyHints)},${builtExtraFile ? "\n extra," : ""}${resourcesField}${servicesField}
|
|
424
440
|
});
|
|
425
441
|
|
|
426
442
|
${adapterEntry.wrap("pipeline")}
|
|
@@ -459,6 +475,26 @@ ${adapterEntry.wrap("pipeline")}
|
|
|
459
475
|
if (moduleCssAsset) manifest.document.moduleStyles = `/${moduleCssAsset}`;
|
|
460
476
|
const worker = createWorker(manifest);
|
|
461
477
|
let hasAssets = false;
|
|
478
|
+
const publicDir = join(appRoot, "public");
|
|
479
|
+
const publicFiles = [];
|
|
480
|
+
let publicIsDir = false;
|
|
481
|
+
try {
|
|
482
|
+
publicIsDir = lstatSync(publicDir).isDirectory();
|
|
483
|
+
} catch {}
|
|
484
|
+
if (existsSync(publicDir) && !publicIsDir) console.warn(`[june] public/ is not a real directory (symlink?) — skipped`);
|
|
485
|
+
if (publicIsDir) {
|
|
486
|
+
for (const rel of await collectFiles(publicDir)) {
|
|
487
|
+
if (rel.split("/")[0] === "_june") {
|
|
488
|
+
console.warn(`[june] public/${rel} ignored — ${RESERVED_PREFIX}/ is reserved for framework assets`);
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
const dest = join(assetsDir, rel);
|
|
492
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
493
|
+
await copyFile(join(publicDir, ...rel.split("/")), dest);
|
|
494
|
+
publicFiles.push(rel);
|
|
495
|
+
}
|
|
496
|
+
if (publicFiles.length) hasAssets = true;
|
|
497
|
+
}
|
|
462
498
|
const isStatic = adapter.capabilities.runtime === "static";
|
|
463
499
|
const i18n = frozen.i18n;
|
|
464
500
|
const prerenderOne = async (reqPath, def) => {
|
|
@@ -536,7 +572,8 @@ ${adapterEntry.wrap("pipeline")}
|
|
|
536
572
|
linkHeader,
|
|
537
573
|
config: fullConfig,
|
|
538
574
|
plan,
|
|
539
|
-
defaultName
|
|
575
|
+
defaultName,
|
|
576
|
+
publicFiles
|
|
540
577
|
});
|
|
541
578
|
if (plan.db) console.log(` ↳ d1 binding "${plan.db.binding}" emitted — set database_id in wrangler.jsonc (wrangler d1 create ${plan.db.databaseName})`);
|
|
542
579
|
return {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AdapterCapabilities, AdapterEmitContext, AdapterEntry, JuneAdapter, ResourcePlan, deno, staticSite, vercel, workers } from "./adapter.js";
|
|
2
2
|
import { CreateAppOptions, JuneApp, createApp } from "./app.js";
|
|
3
3
|
import { R2Bucket, localBlob, r2 } from "./blob.js";
|
|
4
|
-
import { a as Pipeline, d as createPipeline, n as LayoutComponent, o as PipelineConfig, s as Resolved, u as RouteResolver } from "./pipeline-
|
|
4
|
+
import { a as Pipeline, d as createPipeline, n as LayoutComponent, o as PipelineConfig, s as Resolved, u as RouteResolver } from "./pipeline-CCp9idQ4.js";
|
|
5
5
|
import { WorkerManifest, createWorker } from "./worker.js";
|
|
6
6
|
import { BuildResult, buildManifest, freezeConfig, generateContent, juneBuild, scanRoutes } from "./build.js";
|
|
7
7
|
import { loadJuneConfig } from "./config-loader.js";
|
|
@@ -21,6 +21,7 @@ import { ResourceFlags, WorkerEnv, bindWorkerResources, memoizeResources } from
|
|
|
21
21
|
import { MatchOptions, RouteMatch, RouteTreeMatch, SegmentMatch, listRoutes, matchRoute, matchRouteTree, resolveNotFound } from "./router.js";
|
|
22
22
|
import { installAsyncContext } from "./instrumentation.js";
|
|
23
23
|
import { Negotiated, negotiate } from "./negotiate.js";
|
|
24
|
-
import { blob, db, kv } from "@junejs/db";
|
|
24
|
+
import { blob, currentServices, db, kv } from "@junejs/db";
|
|
25
|
+
import { ServicesConfig, defineServices } from "@junejs/core/config";
|
|
25
26
|
import { I18nConfig, LOCALE_COOKIE, LocaleConfig, LocaleMatch, dirForLang, localeDir, localeHref, matchPinnedLocale, negotiateLocale, parseAcceptLanguage, resolveRequestLocale } from "@junejs/core/i18n";
|
|
26
|
-
export { type AdapterCapabilities, type AdapterEmitContext, type AdapterEntry, type BuildResult, type ContentEntry, type CreateAppOptions, type D1Database, type D1Exec, type DeployResult, type DevServer, type DevServerOptions, type I18nConfig, type JuneAdapter, type JuneApp, type JuneDb, type JuneHost, LOCALE_COOKIE, type LayoutComponent, type LocaleConfig, type LocaleMatch, type MatchOptions, type MigrateResult, type Migration, type Negotiated, type Pipeline, type PipelineConfig, type R2Bucket, type Resolved, type ResourceFlags, type ResourcePlan, type RouteMatch, type RouteResolver, type RouteTreeMatch, type RunResult, type SegmentMatch, type ServeHandle, type SpawnedModule, type WorkerEnv, type WorkerManifest, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, d1, db, deno, dirForLang, entry, freezeConfig, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
|
|
27
|
+
export { type AdapterCapabilities, type AdapterEmitContext, type AdapterEntry, type BuildResult, type ContentEntry, type CreateAppOptions, type D1Database, type D1Exec, type DeployResult, type DevServer, type DevServerOptions, type I18nConfig, type JuneAdapter, type JuneApp, type JuneDb, type JuneHost, LOCALE_COOKIE, type LayoutComponent, type LocaleConfig, type LocaleMatch, type MatchOptions, type MigrateResult, type Migration, type Negotiated, type Pipeline, type PipelineConfig, type R2Bucket, type Resolved, type ResourceFlags, type ResourcePlan, type RouteMatch, type RouteResolver, type RouteTreeMatch, type RunResult, type SegmentMatch, type ServeHandle, type ServicesConfig, type SpawnedModule, type WorkerEnv, type WorkerManifest, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, currentServices, d1, db, defineServices, deno, dirForLang, entry, freezeConfig, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
|
package/dist/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { installAsyncContext } from "./instrumentation.js";
|
|
|
21
21
|
import { host } from "./host.js";
|
|
22
22
|
import { startDevServer } from "./dev.js";
|
|
23
23
|
import { memoryKv, redisKv } from "./kv.js";
|
|
24
|
-
import { blob, db, kv } from "@junejs/db";
|
|
24
|
+
import { blob, currentServices, db, kv } from "@junejs/db";
|
|
25
|
+
import { defineServices } from "@junejs/core/config";
|
|
25
26
|
import { LOCALE_COOKIE, dirForLang, localeDir, localeHref, matchPinnedLocale, negotiateLocale, parseAcceptLanguage, resolveRequestLocale } from "@junejs/core/i18n";
|
|
26
|
-
export { LOCALE_COOKIE, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, d1, db, deno, dirForLang, entry, freezeConfig, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
|
|
27
|
+
export { LOCALE_COOKIE, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, currentServices, d1, db, defineServices, deno, dirForLang, entry, freezeConfig, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
|
|
@@ -31,6 +31,7 @@ type PipelineConfig = {
|
|
|
31
31
|
routeList: () => Promise<string[]> | string[];
|
|
32
32
|
resolve: RouteResolver;
|
|
33
33
|
resources?: () => Promise<Resources> | Resources;
|
|
34
|
+
services?: () => Promise<unknown> | unknown;
|
|
34
35
|
earlyHints?: string[];
|
|
35
36
|
htmlCacheControl?: string;
|
|
36
37
|
notFoundComponent?: React.ComponentType<{
|
package/dist/pipeline.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as Pipeline, c as ResolvedResource, d as createPipeline, i as MiddlewareHandler, l as ResourceHandler, n as LayoutComponent, o as PipelineConfig, r as LoadingComponent, s as Resolved, t as ExtraHandler, u as RouteResolver } from "./pipeline-
|
|
1
|
+
import { a as Pipeline, c as ResolvedResource, d as createPipeline, i as MiddlewareHandler, l as ResourceHandler, n as LayoutComponent, o as PipelineConfig, r as LoadingComponent, s as Resolved, t as ExtraHandler, u as RouteResolver } from "./pipeline-CCp9idQ4.js";
|
|
2
2
|
export { ExtraHandler, LayoutComponent, LoadingComponent, MiddlewareHandler, Pipeline, PipelineConfig, Resolved, ResolvedResource, ResourceHandler, RouteResolver, createPipeline };
|
package/dist/pipeline.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { SEGMENT_HEADER, TITLE_HEADER, encodeTitle, negotiate } from "./negotiate.js";
|
|
2
|
+
import { ensureScope, runInScope, setRequestLocale } from "@junejs/db";
|
|
2
3
|
import { LoaderDataContext } from "@junejs/core/route";
|
|
3
4
|
import React, { Suspense, use } from "react";
|
|
4
5
|
import { renderToReadableStream } from "react-dom/server";
|
|
5
6
|
import { Document } from "@junejs/core/document";
|
|
6
7
|
import { apiCatalog, buildLinkHeader, llmsTxt, mcpServerCard, robotsTxt, sitemapXml } from "@junejs/core/discovery";
|
|
7
8
|
import { mcpHandler, mcpTools } from "@junejs/core/mcp";
|
|
8
|
-
import { ensureScope, runInScope, setRequestLocale } from "@junejs/db";
|
|
9
9
|
import { LOCALE_COOKIE, localeAlternates, localeDir, matchPinnedLocale, negotiateLocale } from "@junejs/core/i18n";
|
|
10
10
|
//#region src/pipeline.ts
|
|
11
11
|
function readCookie(request, name) {
|
|
@@ -228,7 +228,10 @@ function createPipeline(cfg) {
|
|
|
228
228
|
}
|
|
229
229
|
return { async fetch(request) {
|
|
230
230
|
await ensureScope();
|
|
231
|
-
return runInScope({
|
|
231
|
+
return runInScope({
|
|
232
|
+
resources: cfg.resources ? await cfg.resources() : {},
|
|
233
|
+
services: cfg.services ? await cfg.services() : void 0
|
|
234
|
+
}, () => handleRequest(request));
|
|
232
235
|
} };
|
|
233
236
|
}
|
|
234
237
|
//#endregion
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
//#region src/static-files.d.ts
|
|
2
|
+
declare const RESERVED_PREFIX = "_june";
|
|
3
|
+
declare function contentTypeFor(pathname: string): string;
|
|
4
|
+
declare function safeRelativePath(pathname: string): string | null;
|
|
5
|
+
//#endregion
|
|
6
|
+
export { RESERVED_PREFIX, contentTypeFor, safeRelativePath };
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
//#region src/static-files.ts
|
|
2
|
+
const RESERVED_PREFIX = "_june";
|
|
3
|
+
const CONTENT_TYPES = {
|
|
4
|
+
html: "text/html; charset=utf-8",
|
|
5
|
+
css: "text/css; charset=utf-8",
|
|
6
|
+
js: "text/javascript; charset=utf-8",
|
|
7
|
+
mjs: "text/javascript; charset=utf-8",
|
|
8
|
+
json: "application/json; charset=utf-8",
|
|
9
|
+
map: "application/json; charset=utf-8",
|
|
10
|
+
xml: "application/xml; charset=utf-8",
|
|
11
|
+
txt: "text/plain; charset=utf-8",
|
|
12
|
+
md: "text/markdown; charset=utf-8",
|
|
13
|
+
csv: "text/csv; charset=utf-8",
|
|
14
|
+
webmanifest: "application/manifest+json",
|
|
15
|
+
svg: "image/svg+xml",
|
|
16
|
+
png: "image/png",
|
|
17
|
+
jpg: "image/jpeg",
|
|
18
|
+
jpeg: "image/jpeg",
|
|
19
|
+
gif: "image/gif",
|
|
20
|
+
webp: "image/webp",
|
|
21
|
+
avif: "image/avif",
|
|
22
|
+
ico: "image/x-icon",
|
|
23
|
+
bmp: "image/bmp",
|
|
24
|
+
woff2: "font/woff2",
|
|
25
|
+
woff: "font/woff",
|
|
26
|
+
ttf: "font/ttf",
|
|
27
|
+
otf: "font/otf",
|
|
28
|
+
mp4: "video/mp4",
|
|
29
|
+
webm: "video/webm",
|
|
30
|
+
mp3: "audio/mpeg",
|
|
31
|
+
ogg: "audio/ogg",
|
|
32
|
+
wav: "audio/wav",
|
|
33
|
+
wasm: "application/wasm",
|
|
34
|
+
pdf: "application/pdf",
|
|
35
|
+
zip: "application/zip"
|
|
36
|
+
};
|
|
37
|
+
function contentTypeFor(pathname) {
|
|
38
|
+
const dot = pathname.lastIndexOf(".");
|
|
39
|
+
if (dot === -1) return "application/octet-stream";
|
|
40
|
+
return CONTENT_TYPES[pathname.slice(dot + 1).toLowerCase()] ?? "application/octet-stream";
|
|
41
|
+
}
|
|
42
|
+
function safeRelativePath(pathname) {
|
|
43
|
+
let decoded;
|
|
44
|
+
try {
|
|
45
|
+
decoded = decodeURIComponent(pathname);
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
if (decoded.includes("\0") || decoded.includes("\\")) return null;
|
|
50
|
+
const segments = decoded.split("/").filter((s) => s !== "" && s !== ".");
|
|
51
|
+
if (segments.length === 0) return null;
|
|
52
|
+
if (segments.some((s) => s === "..")) return null;
|
|
53
|
+
return segments.join("/");
|
|
54
|
+
}
|
|
55
|
+
//#endregion
|
|
56
|
+
export { RESERVED_PREFIX, contentTypeFor, safeRelativePath };
|
package/dist/worker.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { l as ResourceHandler, n as LayoutComponent, r as LoadingComponent, t as ExtraHandler } from "./pipeline-
|
|
1
|
+
import { l as ResourceHandler, n as LayoutComponent, r as LoadingComponent, t as ExtraHandler } from "./pipeline-CCp9idQ4.js";
|
|
2
2
|
import { BrandedRoute } from "@junejs/core/route";
|
|
3
3
|
import { AgentConfig } from "@junejs/core/config";
|
|
4
4
|
import React from "react";
|
|
@@ -33,6 +33,7 @@ type WorkerManifest = {
|
|
|
33
33
|
}>;
|
|
34
34
|
extra?: ExtraHandler;
|
|
35
35
|
resources?: (env?: unknown) => Promise<Resources> | Resources;
|
|
36
|
+
services?: (env?: unknown) => unknown;
|
|
36
37
|
};
|
|
37
38
|
declare function createWorker(manifest: WorkerManifest): {
|
|
38
39
|
fetch(request: Request, env?: unknown): Promise<Response>;
|
package/dist/worker.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { durableAgentSurface } from "./agent-durable.js";
|
|
2
2
|
import { createPipeline } from "./pipeline.js";
|
|
3
|
+
import { RESERVED_PREFIX, contentTypeFor, safeRelativePath } from "./static-files.js";
|
|
3
4
|
//#region src/worker.ts
|
|
4
5
|
function compilePattern(pattern) {
|
|
5
6
|
const names = [];
|
|
@@ -61,6 +62,8 @@ function createWorker(manifest) {
|
|
|
61
62
|
const boundaryFor = (key) => manifest.layoutBoundaries?.[key] ?? null;
|
|
62
63
|
let currentEnv;
|
|
63
64
|
const provider = manifest.resources;
|
|
65
|
+
const servicesProvider = manifest.services;
|
|
66
|
+
let servicesCache;
|
|
64
67
|
const agentSurface = manifest.agent.runtime.enabled && manifest.agentName ? (() => {
|
|
65
68
|
const route = durableAgentSurface(() => currentEnv?.AGENT, {
|
|
66
69
|
agentName: manifest.agentName,
|
|
@@ -79,6 +82,7 @@ function createWorker(manifest) {
|
|
|
79
82
|
notFoundComponent: manifest.notFound,
|
|
80
83
|
extra: manifest.extra,
|
|
81
84
|
resources: provider ? () => provider(currentEnv) : void 0,
|
|
85
|
+
services: servicesProvider ? () => (servicesCache ??= { v: servicesProvider(currentEnv) }).v : void 0,
|
|
82
86
|
resolve: async (pathname) => {
|
|
83
87
|
const staticDef = manifest.routes[pathname];
|
|
84
88
|
if (staticDef) {
|
|
@@ -176,32 +180,33 @@ function withAssets(pipeline, opts = {}) {
|
|
|
176
180
|
return pipeline.fetch(request, env);
|
|
177
181
|
} };
|
|
178
182
|
}
|
|
179
|
-
const ASSET_CONTENT_TYPES = {
|
|
180
|
-
css: "text/css; charset=utf-8",
|
|
181
|
-
js: "text/javascript; charset=utf-8",
|
|
182
|
-
json: "application/json; charset=utf-8",
|
|
183
|
-
map: "application/json; charset=utf-8",
|
|
184
|
-
svg: "image/svg+xml",
|
|
185
|
-
png: "image/png",
|
|
186
|
-
jpg: "image/jpeg",
|
|
187
|
-
jpeg: "image/jpeg",
|
|
188
|
-
webp: "image/webp",
|
|
189
|
-
avif: "image/avif",
|
|
190
|
-
ico: "image/x-icon",
|
|
191
|
-
woff2: "font/woff2",
|
|
192
|
-
woff: "font/woff"
|
|
193
|
-
};
|
|
194
183
|
function withDenoAssets(pipeline) {
|
|
195
184
|
return async (request) => {
|
|
196
|
-
const
|
|
197
|
-
if (
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
185
|
+
const method = request.method;
|
|
186
|
+
if (method === "GET" || method === "HEAD") {
|
|
187
|
+
const url = new URL(request.url);
|
|
188
|
+
{
|
|
189
|
+
const rel = safeRelativePath(url.pathname);
|
|
190
|
+
if (rel !== null) try {
|
|
191
|
+
const assetUrl = new URL(`./assets/${rel.split("/").map(encodeURIComponent).join("/")}`, import.meta.url);
|
|
192
|
+
const immutable = rel.startsWith(`${RESERVED_PREFIX}/`);
|
|
193
|
+
const headers = {
|
|
194
|
+
"content-type": contentTypeFor(rel),
|
|
195
|
+
"cache-control": immutable ? "public, max-age=31536000, immutable" : "public, max-age=0, must-revalidate"
|
|
196
|
+
};
|
|
197
|
+
if (method === "HEAD") {
|
|
198
|
+
const info = await Deno.stat(assetUrl);
|
|
199
|
+
if (!info.isFile) throw new Error("not a file");
|
|
200
|
+
return new Response(null, { headers: {
|
|
201
|
+
...headers,
|
|
202
|
+
"content-length": String(info.size)
|
|
203
|
+
} });
|
|
204
|
+
}
|
|
205
|
+
const file = await Deno.readFile(assetUrl);
|
|
206
|
+
return new Response(file, { headers });
|
|
207
|
+
} catch {}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
205
210
|
return pipeline.fetch(request);
|
|
206
211
|
};
|
|
207
212
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junejs/server",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.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",
|
|
@@ -125,8 +125,8 @@
|
|
|
125
125
|
"build": "tsdown"
|
|
126
126
|
},
|
|
127
127
|
"dependencies": {
|
|
128
|
-
"@junejs/core": ">=0.1.0
|
|
129
|
-
"@junejs/db": ">=0.0.33
|
|
128
|
+
"@junejs/core": ">=0.1.0",
|
|
129
|
+
"@junejs/db": ">=0.0.33",
|
|
130
130
|
"@momiji-rs/sparkdown": "^0.0.4",
|
|
131
131
|
"lightningcss": "^1.27.0",
|
|
132
132
|
"oxc-parser": "^0.137.0",
|
package/src/adapter.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// are explicit `deploy: { adapter: vercel() }` from their own package.
|
|
9
9
|
import { existsSync } from "node:fs";
|
|
10
10
|
import { cp, copyFile, mkdir, readdir, rm, writeFile } from "node:fs/promises";
|
|
11
|
-
import { basename, join } from "node:path";
|
|
11
|
+
import { basename, dirname, join } from "node:path";
|
|
12
12
|
|
|
13
13
|
import type { JuneConfig } from "@junejs/core/config";
|
|
14
14
|
|
|
@@ -49,6 +49,10 @@ export type AdapterEmitContext = {
|
|
|
49
49
|
plan: ResourcePlan;
|
|
50
50
|
// package.json name (or sanitized dir name) — the fallback worker/app name.
|
|
51
51
|
defaultName: string;
|
|
52
|
+
// Verbatim public/ files (forward-slash paths relative to assets/), already
|
|
53
|
+
// copied into outDir/assets. Adapters that build a separate static tier (vercel)
|
|
54
|
+
// place these there; whole-assets targets (workers/static/deno) ignore it.
|
|
55
|
+
publicFiles?: string[];
|
|
52
56
|
};
|
|
53
57
|
|
|
54
58
|
export interface JuneAdapter {
|
|
@@ -214,7 +218,7 @@ export function vercel(opts?: { runtime?: "node" | "edge"; regions?: string[] })
|
|
|
214
218
|
};
|
|
215
219
|
},
|
|
216
220
|
|
|
217
|
-
async emit({ appRoot, outDir, hasAssets }) {
|
|
221
|
+
async emit({ appRoot, outDir, hasAssets, publicFiles }) {
|
|
218
222
|
const out = join(appRoot, ".vercel", "output");
|
|
219
223
|
const fnDir = join(out, "functions", `${VERCEL_FUNCTION}.func`);
|
|
220
224
|
await rm(out, { recursive: true, force: true }); // clean — no stale outputs
|
|
@@ -249,11 +253,20 @@ export function vercel(opts?: { runtime?: "node" | "edge"; regions?: string[] })
|
|
|
249
253
|
await writeFile(join(fnDir, "package.json"), JSON.stringify({ type: "module" }, null, 2) + "\n");
|
|
250
254
|
}
|
|
251
255
|
|
|
252
|
-
// static:
|
|
256
|
+
// static: the hashed framework assets under /_june/ (exact-path URLs)...
|
|
253
257
|
if (hasAssets) {
|
|
254
258
|
const src = join(outDir, "assets", "_june");
|
|
255
259
|
if (existsSync(src)) await cp(src, join(out, "static", "_june"), { recursive: true });
|
|
256
260
|
}
|
|
261
|
+
// ...plus verbatim public/ files, served by the `filesystem` route handle
|
|
262
|
+
// below. Only these — prerendered page HTML stays on the SSR function.
|
|
263
|
+
for (const rel of publicFiles ?? []) {
|
|
264
|
+
const from = join(outDir, "assets", rel);
|
|
265
|
+
if (!existsSync(from)) continue;
|
|
266
|
+
const to = join(out, "static", rel);
|
|
267
|
+
await mkdir(dirname(to), { recursive: true });
|
|
268
|
+
await copyFile(from, to);
|
|
269
|
+
}
|
|
257
270
|
|
|
258
271
|
await writeFile(
|
|
259
272
|
join(out, "config.json"),
|
package/src/agent-discover.ts
CHANGED
|
@@ -21,7 +21,7 @@ import { basename, extname, join } from "node:path";
|
|
|
21
21
|
import { pathToFileURL } from "node:url";
|
|
22
22
|
|
|
23
23
|
import type { AnyAction } from "@junejs/core/agent";
|
|
24
|
-
import { defineAgent, type AgentConfigFile, type AgentDefinition, type Channel, type Skill } from "@junejs/core/agent-config";
|
|
24
|
+
import { defineAgent, resolveChannel, type AgentConfigFile, type AgentDefinition, type Channel, type ChannelFactory, type Skill } from "@junejs/core/agent-config";
|
|
25
25
|
import { connectAll, type Connection } from "@junejs/core/connections";
|
|
26
26
|
|
|
27
27
|
async function scan(dir: string, ext: string): Promise<string[]> {
|
|
@@ -79,10 +79,13 @@ export async function discoverAgent(dir: string): Promise<AgentDefinition> {
|
|
|
79
79
|
skills.push(parseSkill(basename(f, ".md"), await readFile(f, "utf8")));
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
// A channel module default-exports a Channel OR a `(env) => Channel` factory (the
|
|
83
|
+
// form workerd needs — secrets live in env, not at module scope). On native, env is
|
|
84
|
+
// process.env (present at load), so resolve here; the plain form stays supported.
|
|
82
85
|
const channels: Channel[] = [];
|
|
83
86
|
for (const f of await scan(join(dir, "channels"), ".ts")) {
|
|
84
87
|
const mod = await import(pathToFileURL(f).href);
|
|
85
|
-
if (mod.default) channels.push(mod.default as Channel);
|
|
88
|
+
if (mod.default) channels.push(resolveChannel(mod.default as Channel | ChannelFactory, process.env));
|
|
86
89
|
}
|
|
87
90
|
|
|
88
91
|
// connections/*.ts — outbound: wire external MCP/OpenAPI servers, turning their
|