@junejs/server 0.1.0-dev.7 → 0.1.1
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 +4 -3
- package/dist/app.js +3 -0
- package/dist/build.js +7 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/{pipeline-B2V6tKfO.d.ts → pipeline-CCp9idQ4.d.ts} +1 -0
- package/dist/pipeline.d.ts +1 -1
- package/dist/pipeline.js +4 -1
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +3 -0
- package/package.json +3 -3
- package/src/agent-durable.ts +13 -2
- package/src/app.ts +10 -0
- package/src/build.ts +18 -1
- package/src/index.ts +3 -0
- package/src/pipeline.ts +7 -1
- package/src/worker.ts +12 -0
package/dist/agent-durable.d.ts
CHANGED
|
@@ -2,12 +2,13 @@ import { Channel, ChannelFactory } from "@junejs/core/agent-config";
|
|
|
2
2
|
import { Model, Msg, SessionStore, Tool } from "@junejs/core/agent-runtime";
|
|
3
3
|
import { Resources } from "@junejs/core/resources";
|
|
4
4
|
//#region src/agent-durable.d.ts
|
|
5
|
-
|
|
5
|
+
type SqlStorageValue = ArrayBuffer | string | number | null;
|
|
6
|
+
interface SqlStorageCursor<T extends Record<string, SqlStorageValue> = Record<string, SqlStorageValue>> {
|
|
6
7
|
toArray(): T[];
|
|
7
8
|
one(): T;
|
|
8
9
|
}
|
|
9
10
|
interface SqlStorage {
|
|
10
|
-
exec<T = Record<string,
|
|
11
|
+
exec<T extends Record<string, SqlStorageValue> = Record<string, SqlStorageValue>>(query: string, ...bindings: unknown[]): SqlStorageCursor<T>;
|
|
11
12
|
}
|
|
12
13
|
interface DurableStorage {
|
|
13
14
|
sql: SqlStorage;
|
|
@@ -69,4 +70,4 @@ declare function durableChannelSurface(getNamespace: () => DurableObjectNamespac
|
|
|
69
70
|
waitUntil?: (p: Promise<unknown>) => void;
|
|
70
71
|
}): (req: Request) => Promise<Response | null>;
|
|
71
72
|
//#endregion
|
|
72
|
-
export { AgentDurableObject, DoAgentDef, DoSessionStore, DurableObjectNamespace, DurableObjectState, DurableObjectStub, DurableStorage, SqlStorage, SqlStorageCursor, durableAgentSurface, durableChannelSurface, durableFetch };
|
|
73
|
+
export { AgentDurableObject, DoAgentDef, DoSessionStore, DurableObjectNamespace, DurableObjectState, DurableObjectStub, DurableStorage, SqlStorage, SqlStorageCursor, SqlStorageValue, durableAgentSurface, durableChannelSurface, durableFetch };
|
package/dist/app.js
CHANGED
|
@@ -98,6 +98,8 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
98
98
|
return [...own, ...gen].sort();
|
|
99
99
|
};
|
|
100
100
|
const resources = memoizeResources(config.resources);
|
|
101
|
+
let servicesCache;
|
|
102
|
+
const services = config.services ? () => (servicesCache ??= { v: config.services.make(process.env) }).v : void 0;
|
|
101
103
|
config.dataLayer?.install();
|
|
102
104
|
let pipelinePromise;
|
|
103
105
|
const getPipeline = () => pipelinePromise ??= buildPipeline();
|
|
@@ -143,6 +145,7 @@ function createApp({ appDir: appDirInput, config = {} }) {
|
|
|
143
145
|
routeList: routePaths,
|
|
144
146
|
earlyHints: config.earlyHints,
|
|
145
147
|
resources,
|
|
148
|
+
services,
|
|
146
149
|
notFoundComponent,
|
|
147
150
|
resolve: async (pathname) => {
|
|
148
151
|
const match = await matchRouteTree(appDir, pathname, { pageConvention: true }) ?? (hasJuneRoutes ? await matchRouteTree(juneRoutesDir, pathname, { pageConvention: true }) : null);
|
package/dist/build.js
CHANGED
|
@@ -411,6 +411,12 @@ async function juneBuild(appRoot, options = {}) {
|
|
|
411
411
|
const dataLayerModule = fullConfig.dataLayer?.module;
|
|
412
412
|
if (dataLayerModule) imports.push(`import { installDataLayer } from ${JSON.stringify(dataLayerModule)};`);
|
|
413
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,` : "";
|
|
414
420
|
const entry = `// AUTO-GENERATED by \`june build\` — do not edit. Regenerate: june build .
|
|
415
421
|
${adapterEntry.imports.join("\n")}
|
|
416
422
|
${imports.join("\n")}
|
|
@@ -430,7 +436,7 @@ ${loadings.join("\n")}
|
|
|
430
436
|
},
|
|
431
437
|
document: ${JSON.stringify(frozen.document, null, 2).replace(/\n/g, "\n ")},
|
|
432
438
|
agent: ${JSON.stringify(frozen.agent)},${frozen.i18n ? `\n i18n: ${JSON.stringify(frozen.i18n)},` : ""}
|
|
433
|
-
earlyHints: ${JSON.stringify(frozen.earlyHints)},${builtExtraFile ? "\n extra," : ""}${resourcesField}
|
|
439
|
+
earlyHints: ${JSON.stringify(frozen.earlyHints)},${builtExtraFile ? "\n extra," : ""}${resourcesField}${servicesField}
|
|
434
440
|
});
|
|
435
441
|
|
|
436
442
|
${adapterEntry.wrap("pipeline")}
|
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";
|
|
@@ -22,5 +22,6 @@ import { MatchOptions, RouteMatch, RouteTreeMatch, SegmentMatch, listRoutes, mat
|
|
|
22
22
|
import { installAsyncContext } from "./instrumentation.js";
|
|
23
23
|
import { Negotiated, negotiate } from "./negotiate.js";
|
|
24
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, currentServices, 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
|
@@ -22,5 +22,6 @@ import { host } from "./host.js";
|
|
|
22
22
|
import { startDevServer } from "./dev.js";
|
|
23
23
|
import { memoryKv, redisKv } from "./kv.js";
|
|
24
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, currentServices, 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
|
@@ -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
|
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
|
@@ -62,6 +62,8 @@ function createWorker(manifest) {
|
|
|
62
62
|
const boundaryFor = (key) => manifest.layoutBoundaries?.[key] ?? null;
|
|
63
63
|
let currentEnv;
|
|
64
64
|
const provider = manifest.resources;
|
|
65
|
+
const servicesProvider = manifest.services;
|
|
66
|
+
let servicesCache;
|
|
65
67
|
const agentSurface = manifest.agent.runtime.enabled && manifest.agentName ? (() => {
|
|
66
68
|
const route = durableAgentSurface(() => currentEnv?.AGENT, {
|
|
67
69
|
agentName: manifest.agentName,
|
|
@@ -80,6 +82,7 @@ function createWorker(manifest) {
|
|
|
80
82
|
notFoundComponent: manifest.notFound,
|
|
81
83
|
extra: manifest.extra,
|
|
82
84
|
resources: provider ? () => provider(currentEnv) : void 0,
|
|
85
|
+
services: servicesProvider ? () => (servicesCache ??= { v: servicesProvider(currentEnv) }).v : void 0,
|
|
83
86
|
resolve: async (pathname) => {
|
|
84
87
|
const staticDef = manifest.routes[pathname];
|
|
85
88
|
if (staticDef) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@junejs/server",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
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/agent-durable.ts
CHANGED
|
@@ -37,12 +37,23 @@ import {
|
|
|
37
37
|
import { ensureScope, runInScope } from "@junejs/db";
|
|
38
38
|
|
|
39
39
|
// ── minimal structural Cloudflare surface (no @cloudflare/workers-types dep) ──
|
|
40
|
-
|
|
40
|
+
// SqlStorage row values are exactly what workerd's SQLite returns. Mirroring
|
|
41
|
+
// @cloudflare/workers-types' `T extends Record<string, SqlStorageValue>` constraint
|
|
42
|
+
// STRUCTURALLY (not importing it) makes `this.ctx.storage` directly assignable to
|
|
43
|
+
// these interfaces, so a DO shell needs no `as unknown as JuneDoState` cast. The
|
|
44
|
+
// constraint (not just a default) is what lets the two `exec` signatures unify:
|
|
45
|
+
// an unconstrained `T` promises `toArray(): T[]` for arbitrary T, which workerd's
|
|
46
|
+
// cursor — only ever `Record<string, SqlStorageValue>` rows — cannot satisfy.
|
|
47
|
+
export type SqlStorageValue = ArrayBuffer | string | number | null;
|
|
48
|
+
export interface SqlStorageCursor<T extends Record<string, SqlStorageValue> = Record<string, SqlStorageValue>> {
|
|
41
49
|
toArray(): T[];
|
|
42
50
|
one(): T;
|
|
43
51
|
}
|
|
44
52
|
export interface SqlStorage {
|
|
45
|
-
exec<T
|
|
53
|
+
exec<T extends Record<string, SqlStorageValue> = Record<string, SqlStorageValue>>(
|
|
54
|
+
query: string,
|
|
55
|
+
...bindings: unknown[]
|
|
56
|
+
): SqlStorageCursor<T>;
|
|
46
57
|
}
|
|
47
58
|
export interface DurableStorage {
|
|
48
59
|
sql: SqlStorage;
|
package/src/app.ts
CHANGED
|
@@ -182,6 +182,15 @@ export function createApp({ appDir: appDirInput, config = {} }: CreateAppOptions
|
|
|
182
182
|
|
|
183
183
|
const resources = memoizeResources(config.resources);
|
|
184
184
|
|
|
185
|
+
// Build the app-services bag once from process.env (dev's env; stable across the
|
|
186
|
+
// process) and hand the pipeline a memoized provider — the dev twin of the worker
|
|
187
|
+
// binding services from its per-isolate env. The `{ v }` box memoizes even a
|
|
188
|
+
// null/undefined result. Absent config → no provider → currentServices() undefined.
|
|
189
|
+
let servicesCache: { v: unknown } | undefined;
|
|
190
|
+
const services = config.services
|
|
191
|
+
? () => (servicesCache ??= { v: config.services!.make(process.env) }).v
|
|
192
|
+
: undefined;
|
|
193
|
+
|
|
185
194
|
// Boot the opt-in Tier-3 data layer (e.g. `dataLayer: junoDataLayer()`) once:
|
|
186
195
|
// its install() wires the ambient `db` (Juno registers its SQL tagger). Explicit
|
|
187
196
|
// and config-declared — no import-time side-effect, and the framework still never
|
|
@@ -245,6 +254,7 @@ export function createApp({ appDir: appDirInput, config = {} }: CreateAppOptions
|
|
|
245
254
|
routeList: routePaths,
|
|
246
255
|
earlyHints: config.earlyHints,
|
|
247
256
|
resources,
|
|
257
|
+
services,
|
|
248
258
|
notFoundComponent,
|
|
249
259
|
resolve: async (pathname) => {
|
|
250
260
|
const match =
|
package/src/build.ts
CHANGED
|
@@ -706,6 +706,23 @@ export async function juneBuild(
|
|
|
706
706
|
}
|
|
707
707
|
const dataLayerBoot = dataLayerModule ? "\ninstallDataLayer();\n" : "";
|
|
708
708
|
|
|
709
|
+
// App services: import the app's `(env) => services` factory (the named `services`
|
|
710
|
+
// export of the declared module) into the entry and hand it to createWorker, which
|
|
711
|
+
// binds it from the worker env per request — the prod twin of app.ts building it from
|
|
712
|
+
// process.env. Like resources, the module (not the config) is imported, so the config's
|
|
713
|
+
// host-only bits never enter the worker graph. Emitted only when declared.
|
|
714
|
+
//
|
|
715
|
+
// The specifier is rebased like a route import: an app-relative path (`./app/services.ts`)
|
|
716
|
+
// is resolved against appRoot then made relative to the entry's dir (.june), since the
|
|
717
|
+
// generated entry lives there; a bare specifier (a shared package, like dataLayer.module)
|
|
718
|
+
// is used verbatim.
|
|
719
|
+
const servicesModule = fullConfig.services?.module;
|
|
720
|
+
if (servicesModule) {
|
|
721
|
+
const spec = /^[./]/.test(servicesModule) ? importPath(genDir, resolve(appRoot, servicesModule)) : servicesModule;
|
|
722
|
+
imports.push(`import { services as __appServices } from ${JSON.stringify(spec)};`);
|
|
723
|
+
}
|
|
724
|
+
const servicesField = servicesModule ? `\n services: __appServices,` : "";
|
|
725
|
+
|
|
709
726
|
const entry = `// AUTO-GENERATED by \`june build\` — do not edit. Regenerate: june build .
|
|
710
727
|
${adapterEntry.imports.join("\n")}
|
|
711
728
|
${imports.join("\n")}
|
|
@@ -725,7 +742,7 @@ ${loadings.join("\n")}
|
|
|
725
742
|
},
|
|
726
743
|
document: ${JSON.stringify(frozen.document, null, 2).replace(/\n/g, "\n ")},
|
|
727
744
|
agent: ${JSON.stringify(frozen.agent)},${frozen.i18n ? `\n i18n: ${JSON.stringify(frozen.i18n)},` : ""}
|
|
728
|
-
earlyHints: ${JSON.stringify(frozen.earlyHints)},${builtExtraFile ? "\n extra," : ""}${resourcesField}
|
|
745
|
+
earlyHints: ${JSON.stringify(frozen.earlyHints)},${builtExtraFile ? "\n extra," : ""}${resourcesField}${servicesField}
|
|
729
746
|
});
|
|
730
747
|
|
|
731
748
|
${adapterEntry.wrap("pipeline")}
|
package/src/index.ts
CHANGED
|
@@ -31,6 +31,9 @@ export { db, kv, blob } from "@junejs/db";
|
|
|
31
31
|
// read the isolate's app-defined bag from any tool or loader. The app builds it from
|
|
32
32
|
// env at the isolate entry and types it here — `currentServices<AppServices>()`.
|
|
33
33
|
export { currentServices } from "@junejs/db";
|
|
34
|
+
// Declare that bag for the Worker side in june.config.ts (`services: defineServices(...)`)
|
|
35
|
+
// — the twin of the DO's constructor injection, seeded into loaders/views/actions.
|
|
36
|
+
export { defineServices, type ServicesConfig } from "@junejs/core/config";
|
|
34
37
|
export { loadJuneConfig } from "./config-loader";
|
|
35
38
|
export {
|
|
36
39
|
migrate,
|
package/src/pipeline.ts
CHANGED
|
@@ -121,6 +121,11 @@ export type PipelineConfig = {
|
|
|
121
121
|
// Opened data resources (db/kv/blob) injected onto ctx before load(). A
|
|
122
122
|
// provider so opening is lazy/memoized; absent → no resources on ctx.
|
|
123
123
|
resources?: () => Promise<Resources> | Resources;
|
|
124
|
+
// The app-defined services bag, built from the isolate's env (config `services`).
|
|
125
|
+
// A provider like `resources` (lazy/memoized); seeded into the request scope so
|
|
126
|
+
// `currentServices()` resolves in loaders/views/actions. Absent → currentServices()
|
|
127
|
+
// is undefined. This is the Worker-side twin of a Durable Object seeding services.
|
|
128
|
+
services?: () => Promise<unknown> | unknown;
|
|
124
129
|
earlyHints?: string[];
|
|
125
130
|
htmlCacheControl?: string;
|
|
126
131
|
notFoundComponent?: React.ComponentType<{ pathname: string }>;
|
|
@@ -578,7 +583,8 @@ export function createPipeline(cfg: PipelineConfig): Pipeline {
|
|
|
578
583
|
// async-context provider on first request (no static node:* import).
|
|
579
584
|
await ensureScope();
|
|
580
585
|
const resources = cfg.resources ? await cfg.resources() : {};
|
|
581
|
-
|
|
586
|
+
const services = cfg.services ? await cfg.services() : undefined;
|
|
587
|
+
return runInScope({ resources, services }, () => handleRequest(request));
|
|
582
588
|
},
|
|
583
589
|
};
|
|
584
590
|
}
|
package/src/worker.ts
CHANGED
|
@@ -59,6 +59,11 @@ export type WorkerManifest = {
|
|
|
59
59
|
// bindings come from env, so the generated entry passes an ENV-AWARE provider
|
|
60
60
|
// (bindWorkerResources) — the worker threads its env through on each fetch.
|
|
61
61
|
resources?: (env?: unknown) => Promise<Resources> | Resources;
|
|
62
|
+
// App-defined services (config `services`), built from the worker's env. The build
|
|
63
|
+
// imports the app's `(env) => services` factory into the entry and sets it here; the
|
|
64
|
+
// worker threads its env through and createWorker memoizes it per isolate. Seeded
|
|
65
|
+
// into the request scope so `currentServices()` resolves in loaders/views/actions.
|
|
66
|
+
services?: (env?: unknown) => unknown;
|
|
62
67
|
};
|
|
63
68
|
|
|
64
69
|
type Compiled = { regex: RegExp; names: string[]; def: BrandedRoute; pattern: string };
|
|
@@ -133,6 +138,12 @@ export function createWorker(
|
|
|
133
138
|
let currentEnv: unknown;
|
|
134
139
|
const provider = manifest.resources;
|
|
135
140
|
|
|
141
|
+
// App services: the build sets a raw `(env) => services` factory here; memoize it per
|
|
142
|
+
// isolate (env is stable per isolate, like the resources provider). The `{ v }` box
|
|
143
|
+
// caches even a null/undefined result. Absent → currentServices() stays undefined.
|
|
144
|
+
const servicesProvider = manifest.services;
|
|
145
|
+
let servicesCache: { v: unknown } | undefined;
|
|
146
|
+
|
|
136
147
|
// Durable agent (edge): route the chat endpoint to the per-session DO bound at
|
|
137
148
|
// env.AGENT. Built only when the app has an agent (manifest.agentName) and the
|
|
138
149
|
// runtime is enabled; the surface is inert (null → fall through) until env.AGENT
|
|
@@ -159,6 +170,7 @@ export function createWorker(
|
|
|
159
170
|
notFoundComponent: manifest.notFound,
|
|
160
171
|
extra: manifest.extra,
|
|
161
172
|
resources: provider ? () => provider(currentEnv) : undefined,
|
|
173
|
+
services: servicesProvider ? () => (servicesCache ??= { v: servicesProvider(currentEnv) }).v : undefined,
|
|
162
174
|
resolve: async (pathname): Promise<Resolved | ResolvedResource | null> => {
|
|
163
175
|
const staticDef = manifest.routes[pathname];
|
|
164
176
|
if (staticDef) {
|