@junejs/server 1.0.0-dev.19 → 1.0.0-dev.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/adapter-DcZ-W1-7.d.ts +62 -0
  2. package/dist/adapter.d.ts +1 -61
  3. package/dist/agent-durable.js +1 -43
  4. package/dist/agent-native.d.ts +15 -5
  5. package/dist/agent-native.js +85 -16
  6. package/dist/app.js +2 -7
  7. package/dist/build.d.ts +5 -26
  8. package/dist/build.js +10 -276
  9. package/dist/config-freeze-DwI-Rmvq.d.ts +17 -0
  10. package/dist/config-freeze.d.ts +2 -0
  11. package/dist/config-freeze.js +51 -0
  12. package/dist/connection-auth.d.ts +39 -0
  13. package/dist/connection-auth.js +31 -0
  14. package/dist/content-freeze-CyiW2fRK.d.ts +4 -0
  15. package/dist/content-freeze.d.ts +2 -0
  16. package/dist/content-freeze.js +83 -0
  17. package/dist/core-version.js +1 -1
  18. package/dist/index.d.ts +9 -4
  19. package/dist/index.js +7 -2
  20. package/dist/manifest-gcNWqWoI.d.ts +11 -0
  21. package/dist/manifest.d.ts +2 -0
  22. package/dist/manifest.js +111 -0
  23. package/dist/route-scan-DMlwoESy.d.ts +13 -0
  24. package/dist/route-scan.d.ts +2 -0
  25. package/dist/route-scan.js +55 -0
  26. package/dist/turn-events.d.ts +5 -0
  27. package/dist/turn-events.js +46 -0
  28. package/dist/worker-BkFHGtxp.d.ts +61 -0
  29. package/dist/worker.d.ts +1 -60
  30. package/package.json +2 -2
  31. package/src/agent-discover.ts +10 -7
  32. package/src/agent-durable.ts +1 -39
  33. package/src/agent-native.ts +133 -21
  34. package/src/app.ts +4 -6
  35. package/src/build.ts +26 -440
  36. package/src/config-freeze.ts +104 -0
  37. package/src/connection-auth.ts +101 -0
  38. package/src/content-freeze.ts +151 -0
  39. package/src/core-version.ts +1 -1
  40. package/src/index.ts +14 -0
  41. package/src/manifest.ts +128 -0
  42. package/src/route-scan.ts +84 -0
  43. package/src/turn-events.ts +43 -0
@@ -0,0 +1,111 @@
1
+ import { findMiddlewareFile } from "./router.js";
2
+ import { resolveBoundary } from "./segment.js";
3
+ import { freezeConfig } from "./config-freeze.js";
4
+ import { scanAppRoutes } from "./route-scan.js";
5
+ import { existsSync } from "node:fs";
6
+ import { basename, join } from "node:path";
7
+ import { pathToFileURL } from "node:url";
8
+ import { routeFromModule } from "@junejs/core/route";
9
+ //#region src/manifest.ts
10
+ async function importLayout(file) {
11
+ const mod = await import(pathToFileURL(file).href);
12
+ return typeof mod.default === "function" ? {
13
+ component: mod.default,
14
+ boundary: mod.segmentBoundary === true
15
+ } : null;
16
+ }
17
+ async function buildManifest(appRoot) {
18
+ const appDir = join(appRoot, "app");
19
+ const frozen = await freezeConfig(appRoot);
20
+ const scanned = await scanAppRoutes(appRoot);
21
+ const layoutCache = /* @__PURE__ */ new Map();
22
+ const loadCached = async (f) => {
23
+ if (!layoutCache.has(f)) layoutCache.set(f, await importLayout(f));
24
+ return layoutCache.get(f) ?? null;
25
+ };
26
+ const componentsFor = async (files) => {
27
+ const items = [];
28
+ for (const f of files) {
29
+ const c = await loadCached(f);
30
+ items.push({
31
+ file: f,
32
+ entry: c?.component ?? null,
33
+ boundary: !!c?.boundary
34
+ });
35
+ }
36
+ return resolveBoundary(items);
37
+ };
38
+ const loadingCache = /* @__PURE__ */ new Map();
39
+ const loadingFor = async (file) => {
40
+ if (!file) return void 0;
41
+ if (!loadingCache.has(file)) {
42
+ const loaded = await importLayout(file);
43
+ loadingCache.set(file, loaded ? loaded.component : null);
44
+ }
45
+ return loadingCache.get(file) ?? void 0;
46
+ };
47
+ const routes = {};
48
+ const dynamicRoutes = [];
49
+ const resourceRoutes = [];
50
+ const layoutChains = {};
51
+ const layoutBoundaries = {};
52
+ const loadings = {};
53
+ for (const r of scanned) {
54
+ const mod = await import(pathToFileURL(r.file).href);
55
+ if (r.resource) {
56
+ const handler = mod.default;
57
+ if (typeof handler === "function") resourceRoutes.push({
58
+ pattern: r.path,
59
+ handler
60
+ });
61
+ continue;
62
+ }
63
+ const def = routeFromModule(mod);
64
+ if (!def) continue;
65
+ const { chain, boundaryIndex, key } = await componentsFor(r.layouts);
66
+ const loading = await loadingFor(r.loading);
67
+ if (r.dynamic) {
68
+ dynamicRoutes.push({
69
+ pattern: r.path,
70
+ def
71
+ });
72
+ layoutChains[r.path] = chain;
73
+ } else {
74
+ routes[r.path] = def;
75
+ layoutChains[r.path] = chain;
76
+ }
77
+ if (boundaryIndex !== null && key !== null) layoutBoundaries[r.path] = {
78
+ index: boundaryIndex,
79
+ key
80
+ };
81
+ if (loading) loadings[r.path] = loading;
82
+ }
83
+ let extra;
84
+ const extraFile = findMiddlewareFile(appDir);
85
+ if (extraFile) {
86
+ const mod = await import(pathToFileURL(extraFile).href);
87
+ if (typeof mod.default === "function") extra = mod.default;
88
+ }
89
+ let agentName;
90
+ const agentDir = join(appDir, frozen.agent.runtime.dir);
91
+ if (frozen.agent.runtime.enabled && existsSync(agentDir)) {
92
+ const cfgFile = join(agentDir, "agent.ts");
93
+ agentName = (existsSync(cfgFile) ? (await import(pathToFileURL(cfgFile).href)).default : void 0)?.name ?? basename(agentDir);
94
+ }
95
+ return {
96
+ routes,
97
+ dynamicRoutes,
98
+ resourceRoutes,
99
+ layoutChains,
100
+ layoutBoundaries,
101
+ loadings,
102
+ document: frozen.document,
103
+ agent: frozen.agent,
104
+ agentName,
105
+ i18n: frozen.i18n,
106
+ earlyHints: frozen.earlyHints,
107
+ extra
108
+ };
109
+ }
110
+ //#endregion
111
+ export { buildManifest, importLayout };
@@ -0,0 +1,13 @@
1
+ //#region src/route-scan.d.ts
2
+ type RouteEntry = {
3
+ path: string;
4
+ file: string;
5
+ dynamic: boolean;
6
+ resource?: boolean;
7
+ layouts: string[];
8
+ loading?: string;
9
+ };
10
+ declare function scanRoutes(appDir: string, dir?: string, layouts?: string[], out?: RouteEntry[], loading?: string): Promise<RouteEntry[]>;
11
+ declare function scanAppRoutes(appRoot: string): Promise<RouteEntry[]>;
12
+ //#endregion
13
+ export { scanAppRoutes as n, scanRoutes as r, RouteEntry as t };
@@ -0,0 +1,2 @@
1
+ import { n as scanAppRoutes, r as scanRoutes, t as RouteEntry } from "./route-scan-DMlwoESy.js";
2
+ export { RouteEntry, scanAppRoutes, scanRoutes };
@@ -0,0 +1,55 @@
1
+ import { existsSync } from "node:fs";
2
+ import { readdir } from "node:fs/promises";
3
+ import { basename, join, relative, sep } from "node:path";
4
+ //#region src/route-scan.ts
5
+ const PAGE_BASENAMES = /* @__PURE__ */ new Set(["page", "index"]);
6
+ const ROUTE_EXTS = [
7
+ ".tsx",
8
+ ".jsx",
9
+ ".ts",
10
+ ".js"
11
+ ];
12
+ const isRouteGroup = (name) => /^\(.+\)$/.test(name);
13
+ function segmentFile(dir, base) {
14
+ return ROUTE_EXTS.map((e) => join(dir, `${base}${e}`)).find(existsSync);
15
+ }
16
+ async function scanRoutes(appDir, dir = appDir, layouts = [], out = [], loading) {
17
+ const ownLayout = segmentFile(dir, "layout");
18
+ const chain = ownLayout ? [...layouts, ownLayout] : layouts;
19
+ const nearestLoading = segmentFile(dir, "loading") ?? loading;
20
+ const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
21
+ for (const e of entries) {
22
+ if (e.name.startsWith("_") || e.name.startsWith(".")) continue;
23
+ const full = join(dir, e.name);
24
+ if (e.isDirectory()) {
25
+ await scanRoutes(appDir, full, chain, out, nearestLoading);
26
+ continue;
27
+ }
28
+ const ext = e.name.match(/\.[^.]+$/)?.[0] ?? "";
29
+ if (!ROUTE_EXTS.includes(ext)) continue;
30
+ const base = basename(e.name, ext);
31
+ const resource = base === "route";
32
+ if (!PAGE_BASENAMES.has(base) && !resource) continue;
33
+ const relDir = relative(appDir, dir);
34
+ const path = "/" + (relDir === "" ? [] : relDir.split(sep).filter((s) => !isRouteGroup(s))).join("/");
35
+ out.push({
36
+ path: path === "/" ? "/" : path,
37
+ file: full,
38
+ dynamic: /\[.+\]/.test(path),
39
+ resource,
40
+ layouts: chain,
41
+ loading: nearestLoading
42
+ });
43
+ }
44
+ return out;
45
+ }
46
+ async function scanAppRoutes(appRoot) {
47
+ const appDir = join(appRoot, "app");
48
+ const juneRoutesDir = join(appRoot, ".june", "routes");
49
+ const appRoutes = await scanRoutes(appDir);
50
+ const frameworkRoutes = existsSync(juneRoutesDir) ? await scanRoutes(juneRoutesDir) : [];
51
+ const appPaths = new Set(appRoutes.map((r) => r.path));
52
+ return [...appRoutes, ...frameworkRoutes.filter((r) => !appPaths.has(r.path))].sort((a, b) => a.path.localeCompare(b.path));
53
+ }
54
+ //#endregion
55
+ export { scanAppRoutes, scanRoutes };
@@ -0,0 +1,5 @@
1
+ import { AgentSession, TurnEvent } from "@junejs/core/agent-runtime";
2
+ //#region src/turn-events.d.ts
3
+ declare function observeTurnEvents(session: AgentSession, turnId: string): AsyncIterable<TurnEvent>;
4
+ //#endregion
5
+ export { observeTurnEvents };
@@ -0,0 +1,46 @@
1
+ //#region src/turn-events.ts
2
+ function observeTurnEvents(session, turnId) {
3
+ const queue = [];
4
+ let terminal = false;
5
+ let notify;
6
+ const unsub = session.observe((e) => {
7
+ queue.push(e);
8
+ if (e.type === "turn.completed" || e.type === "turn.failed" || e.type === "turn.cancelled" || e.type === "input.requested") terminal = true;
9
+ notify?.();
10
+ }, { turnId });
11
+ return { [Symbol.asyncIterator]() {
12
+ return {
13
+ async next() {
14
+ for (;;) {
15
+ const e = queue.shift();
16
+ if (e) return {
17
+ value: e,
18
+ done: false
19
+ };
20
+ if (terminal) {
21
+ unsub();
22
+ return {
23
+ value: void 0,
24
+ done: true
25
+ };
26
+ }
27
+ await new Promise((resolve) => {
28
+ notify = resolve;
29
+ });
30
+ notify = void 0;
31
+ }
32
+ },
33
+ async return() {
34
+ unsub();
35
+ terminal = true;
36
+ queue.length = 0;
37
+ return {
38
+ value: void 0,
39
+ done: true
40
+ };
41
+ }
42
+ };
43
+ } };
44
+ }
45
+ //#endregion
46
+ export { observeTurnEvents };
@@ -0,0 +1,61 @@
1
+ import { l as ResourceHandler, n as LayoutComponent, r as LoadingComponent, t as ExtraHandler } from "./pipeline-cCR6MGNK.js";
2
+ import { Channel, ChannelFactory } from "@junejs/core/agent-config";
3
+ import { BrandedRoute } from "@junejs/core/route";
4
+ import { AgentConfig } from "@junejs/core/config";
5
+ import React from "react";
6
+ import { DocumentConfig } from "@junejs/core/document";
7
+ import { I18nConfig } from "@junejs/core/i18n";
8
+ import { Resources } from "@junejs/core/resources";
9
+ //#region src/worker.d.ts
10
+ type WorkerExecutionContext = {
11
+ waitUntil?(p: Promise<unknown>): void;
12
+ };
13
+ type WorkerManifest = {
14
+ routes: Record<string, BrandedRoute>;
15
+ dynamicRoutes?: Array<{
16
+ pattern: string;
17
+ def: BrandedRoute;
18
+ }>;
19
+ resourceRoutes?: Array<{
20
+ pattern: string;
21
+ handler: ResourceHandler;
22
+ }>;
23
+ layoutChains?: Record<string, LayoutComponent[]>;
24
+ layoutBoundaries?: Record<string, {
25
+ index: number;
26
+ key: string;
27
+ }>;
28
+ loadings?: Record<string, LoadingComponent>;
29
+ document: DocumentConfig;
30
+ agent: AgentConfig;
31
+ agentName?: string;
32
+ agentChannels?: (Channel | ChannelFactory)[];
33
+ i18n?: I18nConfig;
34
+ earlyHints?: string[];
35
+ htmlCacheControl?: string;
36
+ notFound?: React.ComponentType<{
37
+ pathname: string;
38
+ }>;
39
+ extra?: ExtraHandler;
40
+ resources?: (env?: unknown) => Promise<Resources> | Resources;
41
+ services?: (env?: unknown) => unknown;
42
+ };
43
+ declare function createWorker(manifest: WorkerManifest): {
44
+ fetch(request: Request, env?: unknown, ctx?: WorkerExecutionContext): Promise<Response>;
45
+ };
46
+ type AssetEnv = {
47
+ ASSETS?: {
48
+ fetch(request: Request): Promise<Response>;
49
+ };
50
+ };
51
+ type FetchPipeline = {
52
+ fetch(request: Request, env?: unknown, ctx?: WorkerExecutionContext): Promise<Response>;
53
+ };
54
+ declare function withAssets(pipeline: FetchPipeline, opts?: {
55
+ link?: string | null;
56
+ }): {
57
+ fetch(request: Request, env?: AssetEnv, ctx?: WorkerExecutionContext): Promise<Response>;
58
+ };
59
+ declare function withDenoAssets(pipeline: FetchPipeline): (request: Request) => Promise<Response>;
60
+ //#endregion
61
+ export { withDenoAssets as a, withAssets as i, WorkerManifest as n, createWorker as r, WorkerExecutionContext as t };
package/dist/worker.d.ts CHANGED
@@ -1,61 +1,2 @@
1
- import { l as ResourceHandler, n as LayoutComponent, r as LoadingComponent, t as ExtraHandler } from "./pipeline-cCR6MGNK.js";
2
- import { Channel, ChannelFactory } from "@junejs/core/agent-config";
3
- import { BrandedRoute } from "@junejs/core/route";
4
- import { AgentConfig } from "@junejs/core/config";
5
- import React from "react";
6
- import { DocumentConfig } from "@junejs/core/document";
7
- import { I18nConfig } from "@junejs/core/i18n";
8
- import { Resources } from "@junejs/core/resources";
9
- //#region src/worker.d.ts
10
- type WorkerExecutionContext = {
11
- waitUntil?(p: Promise<unknown>): void;
12
- };
13
- type WorkerManifest = {
14
- routes: Record<string, BrandedRoute>;
15
- dynamicRoutes?: Array<{
16
- pattern: string;
17
- def: BrandedRoute;
18
- }>;
19
- resourceRoutes?: Array<{
20
- pattern: string;
21
- handler: ResourceHandler;
22
- }>;
23
- layoutChains?: Record<string, LayoutComponent[]>;
24
- layoutBoundaries?: Record<string, {
25
- index: number;
26
- key: string;
27
- }>;
28
- loadings?: Record<string, LoadingComponent>;
29
- document: DocumentConfig;
30
- agent: AgentConfig;
31
- agentName?: string;
32
- agentChannels?: (Channel | ChannelFactory)[];
33
- i18n?: I18nConfig;
34
- earlyHints?: string[];
35
- htmlCacheControl?: string;
36
- notFound?: React.ComponentType<{
37
- pathname: string;
38
- }>;
39
- extra?: ExtraHandler;
40
- resources?: (env?: unknown) => Promise<Resources> | Resources;
41
- services?: (env?: unknown) => unknown;
42
- };
43
- declare function createWorker(manifest: WorkerManifest): {
44
- fetch(request: Request, env?: unknown, ctx?: WorkerExecutionContext): Promise<Response>;
45
- };
46
- type AssetEnv = {
47
- ASSETS?: {
48
- fetch(request: Request): Promise<Response>;
49
- };
50
- };
51
- type FetchPipeline = {
52
- fetch(request: Request, env?: unknown, ctx?: WorkerExecutionContext): Promise<Response>;
53
- };
54
- declare function withAssets(pipeline: FetchPipeline, opts?: {
55
- link?: string | null;
56
- }): {
57
- fetch(request: Request, env?: AssetEnv, ctx?: WorkerExecutionContext): Promise<Response>;
58
- };
59
- declare function withDenoAssets(pipeline: FetchPipeline): (request: Request) => Promise<Response>;
60
- //#endregion
1
+ import { a as withDenoAssets, i as withAssets, n as WorkerManifest, r as createWorker, t as WorkerExecutionContext } from "./worker-BkFHGtxp.js";
61
2
  export { WorkerExecutionContext, WorkerManifest, createWorker, withAssets, withDenoAssets };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@junejs/server",
3
- "version": "1.0.0-dev.19",
3
+ "version": "1.0.0-dev.24",
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",
@@ -138,7 +138,7 @@
138
138
  "rolldown": "^1.1.0"
139
139
  },
140
140
  "peerDependencies": {
141
- "@junejs/core": ">=0.2.0-dev.36",
141
+ "@junejs/core": ">=0.2.0-dev.44",
142
142
  "react": "^19.2.0",
143
143
  "react-dom": "^19.2.0",
144
144
  "react-server-dom-webpack": "19.2.7",
@@ -6,7 +6,9 @@
6
6
  // agent.ts → default-exports a plain config { name, model?, surfaces? }
7
7
  // instructions.md → base system prompt
8
8
  // instructions.<source>.md → per-surface instruction variant (#149)
9
- // tools/*.ts → each default-exports a defineAction (a tool)
9
+ // tools/*.ts → each default-exports a defineAction (a tool),
10
+ // OR an array of defineActions (an integration
11
+ // shipping several tools, e.g. googleDriveTools())
10
12
  // skills/*.md → each a procedure, loaded on demand (progressive disclosure)
11
13
  // channels/*.ts → each default-exports a Channel (an inbound edge — pure transport)
12
14
  // channels/*.md → DEPRECATED overlay location (warned; use instructions.<source>.md)
@@ -24,8 +26,7 @@ import { readdir, readFile } from "node:fs/promises";
24
26
  import { basename, extname, join } from "node:path";
25
27
  import { pathToFileURL } from "node:url";
26
28
 
27
- import type { AnyAction } from "@junejs/core/agent";
28
- import { assembleAgent, parseSkill, type AgentConfigFile, type AgentDefinition, type AgentModule, type Channel, type ChannelFactory, type Skill } from "@junejs/core/agent-config";
29
+ import { assembleAgent, parseSkill, type AgentConfigFile, type AgentDefinition, type AgentModule, type Channel, type ChannelFactory, type Skill, type ToolEntry } from "@junejs/core/agent-config";
29
30
  import type { Connection } from "@junejs/core/connections";
30
31
 
31
32
  // `_`-prefixed files are private by convention (mirrors the app/ router) — most
@@ -72,10 +73,12 @@ export async function discoverAgentModule(dir: string): Promise<AgentModule> {
72
73
  }
73
74
  }
74
75
 
75
- const tools: AnyAction[] = [];
76
+ // A tool file default-exports one tool OR an array of tools (an integration
77
+ // like googleDriveTools()); assembly flattens arrays.
78
+ const tools: ToolEntry[] = [];
76
79
  for (const f of await scan(join(dir, "tools"), ".ts")) {
77
80
  const mod = await import(pathToFileURL(f).href);
78
- if (mod.default) tools.push(mod.default as AnyAction);
81
+ if (mod.default) tools.push(mod.default as ToolEntry);
79
82
  }
80
83
 
81
84
  const skills: Skill[] = [];
@@ -117,8 +120,8 @@ export async function discoverAgentModule(dir: string): Promise<AgentModule> {
117
120
 
118
121
  // Discover an agent from its directory, returning the assembled AgentDefinition
119
122
  // (tools already adapted from defineActions; read_skill added if any skills
120
- // exist). Mount it on a runtime with createNativeRuntime({ [agent.name]: { model,
121
- // tools: agent.tools } }).
123
+ // exist). Mount it on a runtime with createNativeRuntime({ [agent.name]:
124
+ // toAgentDef(agent, model) }).
122
125
  export async function discoverAgent(dir: string): Promise<AgentDefinition> {
123
126
  return assembleAgent(await discoverAgentModule(dir), process.env);
124
127
  }
@@ -33,6 +33,7 @@ import {
33
33
  type TurnFailurePhase,
34
34
  } from "@junejs/core/agent-runtime";
35
35
  import type { Resources } from "@junejs/core/resources";
36
+ import { observeTurnEvents } from "./turn-events";
36
37
  import {
37
38
  channelDispatch,
38
39
  DeliverUnsupportedError,
@@ -692,45 +693,6 @@ function sseTurnStream(session: AgentSession, turnId: string): ReadableStream<Ui
692
693
  });
693
694
  }
694
695
 
695
- // The IN-PROCESS sibling of sseTurnStream: one turn's TurnEvents as an AsyncIterable, for a
696
- // consumer living in the SAME isolate as the session (a delivered render). Subscribes eagerly
697
- // at call time — not at first iteration — so the sseTurnStream timing guarantee carries over
698
- // (call this synchronously after start(), before any event can emit); events landing before
699
- // the consumer catches up are buffered. Ends after the turn's terminal event (completed,
700
- // failed, or input.requested — a park ends this stream; a later /resume is a new one), and an
701
- // early consumer exit (for-await break/return) unsubscribes rather than buffering forever.
702
- function observeTurnEvents(session: AgentSession, turnId: string): AsyncIterable<TurnEvent> {
703
- const queue: TurnEvent[] = [];
704
- let terminal = false;
705
- let notify: (() => void) | undefined;
706
- const unsub = session.observe((e) => {
707
- queue.push(e);
708
- if (e.type === "turn.completed" || e.type === "turn.failed" || e.type === "turn.cancelled" || e.type === "input.requested") terminal = true;
709
- notify?.();
710
- }, { turnId });
711
- return {
712
- [Symbol.asyncIterator]() {
713
- return {
714
- async next(): Promise<IteratorResult<TurnEvent>> {
715
- for (;;) {
716
- const e = queue.shift();
717
- if (e) return { value: e, done: false };
718
- if (terminal) { unsub(); return { value: undefined, done: true }; }
719
- await new Promise<void>((resolve) => { notify = resolve; });
720
- notify = undefined;
721
- }
722
- },
723
- async return(): Promise<IteratorResult<TurnEvent>> {
724
- unsub();
725
- terminal = true;
726
- queue.length = 0;
727
- return { value: undefined, done: true };
728
- },
729
- };
730
- },
731
- };
732
- }
733
-
734
696
  // Consume an SSE turn stream to its terminal state: the final text, or throw on failure.
735
697
  // The non-streaming path (channels, a JSON chat response) uses this. Guards a non-SSE /
736
698
  // body-less upstream response (a misroute or an error) into a clear error, not a TypeError.