@voyantjs/worker-runtime 0.0.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/README.md +51 -0
- package/dist/api-dispatch.d.ts +40 -0
- package/dist/api-dispatch.d.ts.map +1 -0
- package/dist/api-dispatch.js +63 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/ssr-manifest.d.ts +51 -0
- package/dist/ssr-manifest.d.ts.map +1 -0
- package/dist/ssr-manifest.js +49 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/worker-fetch.d.ts +16 -0
- package/dist/worker-fetch.d.ts.map +1 -0
- package/dist/worker-fetch.js +18 -0
- package/package.json +67 -0
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @voyantjs/worker-runtime
|
|
2
|
+
|
|
3
|
+
Framework-owned Cloudflare Worker entry logic for Voyant apps: API/auth/SSR
|
|
4
|
+
dispatch and SSR-manifest restriction, delivered as a versioned package
|
|
5
|
+
instead of copied template files.
|
|
6
|
+
|
|
7
|
+
Part of the Packaged Admin direction (`docs/architecture/packaged-admin-rfc.md`,
|
|
8
|
+
Phase 0): a project's `entry.ts` shrinks to bindings + factory calls, and
|
|
9
|
+
fixes to this load-bearing code path arrive via a dependency bump.
|
|
10
|
+
|
|
11
|
+
## What it does
|
|
12
|
+
|
|
13
|
+
- **`createApiDispatch({ loadApiApp, loadAuthApp? })`** — routes hosting-URL
|
|
14
|
+
requests (`/api/*`) onto the app surface (`/v1/*`, `/auth/*`, `/health`),
|
|
15
|
+
stripping the prefix and preserving method/headers/body/search. When
|
|
16
|
+
`loadAuthApp` is set, `/api/auth/*` dispatches to the lean auth app without
|
|
17
|
+
instantiating the full API graph (the cold-start outage fix), and non-OPTIONS
|
|
18
|
+
auth traffic warms the full app in the background via `ctx.waitUntil`.
|
|
19
|
+
- **`createWorkerFetch({ api, ssr })`** — the Worker `fetch` entrypoint:
|
|
20
|
+
API-prefixed requests go to the dispatch, everything else to the SSR handler.
|
|
21
|
+
- **`withActiveRouteSsrManifest(streamHandler)`** — restricts the TanStack
|
|
22
|
+
Start SSR manifest to the active route matches so the first paint isn't
|
|
23
|
+
flooded with speculative preloads. Structurally typed; no router dependency.
|
|
24
|
+
- **`lazyApp(load)`** — memoizes an app loader per isolate.
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
// src/entry.ts
|
|
30
|
+
import { createStartHandler, defaultStreamHandler } from "@tanstack/react-start/server"
|
|
31
|
+
import { createWorkerFetch, lazyApp, withActiveRouteSsrManifest } from "@voyantjs/worker-runtime"
|
|
32
|
+
|
|
33
|
+
const startHandler = createStartHandler(withActiveRouteSsrManifest(defaultStreamHandler))
|
|
34
|
+
|
|
35
|
+
const fetch = createWorkerFetch<CloudflareBindings>({
|
|
36
|
+
api: {
|
|
37
|
+
loadApiApp: lazyApp(() => import("./api/app").then((m) => ({ fetch: m.app.fetch }))),
|
|
38
|
+
loadAuthApp: lazyApp(() => import("./api/auth-app").then((m) => ({ fetch: m.authApp.fetch }))),
|
|
39
|
+
},
|
|
40
|
+
ssr: (request, env) => startHandler(request, { context: { env } } as never),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export default { fetch }
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Scheduled (cron) handlers, Durable Objects, and workflow wiring remain
|
|
47
|
+
app-owned composition — see RFC §4.4.
|
|
48
|
+
|
|
49
|
+
## License
|
|
50
|
+
|
|
51
|
+
Apache-2.0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { AppLoader, FetchApp, WaitUntilContext } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Memoize an app loader so the underlying dynamic `import()` runs once per
|
|
4
|
+
* isolate, no matter how many requests race on it.
|
|
5
|
+
*/
|
|
6
|
+
export declare function lazyApp<Env, Ctx extends WaitUntilContext = WaitUntilContext>(load: () => Promise<FetchApp<Env, Ctx>>): AppLoader<Env, Ctx>;
|
|
7
|
+
export interface CreateApiDispatchOptions<Env, Ctx extends WaitUntilContext = WaitUntilContext> {
|
|
8
|
+
/** Loads the full API app (the heavy module graph). Wrap with {@link lazyApp}. */
|
|
9
|
+
loadApiApp: AppLoader<Env, Ctx>;
|
|
10
|
+
/**
|
|
11
|
+
* Optional lean auth app. When set, requests under `authPrefix` dispatch to
|
|
12
|
+
* it WITHOUT loading the full API graph — the fix for the cold-start outage
|
|
13
|
+
* where the first `/api/auth/*` call instantiated the whole API and hung.
|
|
14
|
+
* Non-OPTIONS auth requests warm the full app in the background via
|
|
15
|
+
* `ctx.waitUntil` so the next API call is hot.
|
|
16
|
+
*/
|
|
17
|
+
loadAuthApp?: AppLoader<Env, Ctx>;
|
|
18
|
+
/** Hosting prefix stripped before dispatch. Default `/api`. */
|
|
19
|
+
apiPrefix?: string;
|
|
20
|
+
/** Auth sub-prefix served by the lean app. Default `${apiPrefix}/auth`. */
|
|
21
|
+
authPrefix?: string;
|
|
22
|
+
/** Background-warm the full app on auth traffic. Default true. */
|
|
23
|
+
warmApiOnAuth?: boolean;
|
|
24
|
+
/** Called when the background warm-up fails. Defaults to `console.error`. */
|
|
25
|
+
onWarmError?: (error: unknown) => void;
|
|
26
|
+
}
|
|
27
|
+
export interface ApiDispatch<Env, Ctx extends WaitUntilContext = WaitUntilContext> {
|
|
28
|
+
isApiRequest(pathname: string): boolean;
|
|
29
|
+
isAuthRequest(pathname: string): boolean;
|
|
30
|
+
/** Strip the hosting prefix (`/api/v1/x` → `/v1/x`), preserving search/body. */
|
|
31
|
+
toAppRequest(request: Request): Request;
|
|
32
|
+
dispatch(request: Request, env: Env, ctx: Ctx): Promise<Response>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Prefix-routed dispatch from a hosting Worker URL space (`/api/*`) onto a
|
|
36
|
+
* Hono-style app surface (`/v1/*`, `/auth/*`, `/health`). Framework-owned:
|
|
37
|
+
* apps supply only the loaders for their own modules.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createApiDispatch<Env, Ctx extends WaitUntilContext = WaitUntilContext>(options: CreateApiDispatchOptions<Env, Ctx>): ApiDispatch<Env, Ctx>;
|
|
40
|
+
//# sourceMappingURL=api-dispatch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-dispatch.d.ts","sourceRoot":"","sources":["../src/api-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAIvE;;;GAGG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,GAAG,SAAS,gBAAgB,GAAG,gBAAgB,EAC1E,IAAI,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,GACtC,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAMrB;AAED,MAAM,WAAW,wBAAwB,CAAC,GAAG,EAAE,GAAG,SAAS,gBAAgB,GAAG,gBAAgB;IAC5F,kFAAkF;IAClF,UAAU,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IAC/B;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IACjC,+DAA+D;IAC/D,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kEAAkE;IAClE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;CACvC;AAED,MAAM,WAAW,WAAW,CAAC,GAAG,EAAE,GAAG,SAAS,gBAAgB,GAAG,gBAAgB;IAC/E,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACvC,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAA;IACxC,gFAAgF;IAChF,YAAY,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;IACvC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAClE;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,SAAS,gBAAgB,GAAG,gBAAgB,EACpF,OAAO,EAAE,wBAAwB,CAAC,GAAG,EAAE,GAAG,CAAC,GAC1C,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,CAgDvB"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const DEFAULT_API_PREFIX = "/api";
|
|
2
|
+
/**
|
|
3
|
+
* Memoize an app loader so the underlying dynamic `import()` runs once per
|
|
4
|
+
* isolate, no matter how many requests race on it.
|
|
5
|
+
*/
|
|
6
|
+
export function lazyApp(load) {
|
|
7
|
+
let promise;
|
|
8
|
+
return () => {
|
|
9
|
+
promise ??= load();
|
|
10
|
+
return promise;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function matchesPrefix(pathname, prefix) {
|
|
14
|
+
return pathname === prefix || pathname.startsWith(`${prefix}/`);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Prefix-routed dispatch from a hosting Worker URL space (`/api/*`) onto a
|
|
18
|
+
* Hono-style app surface (`/v1/*`, `/auth/*`, `/health`). Framework-owned:
|
|
19
|
+
* apps supply only the loaders for their own modules.
|
|
20
|
+
*/
|
|
21
|
+
export function createApiDispatch(options) {
|
|
22
|
+
const apiPrefix = options.apiPrefix ?? DEFAULT_API_PREFIX;
|
|
23
|
+
const authPrefix = options.authPrefix ?? `${apiPrefix}/auth`;
|
|
24
|
+
const warmApiOnAuth = options.warmApiOnAuth ?? true;
|
|
25
|
+
const onWarmError = options.onWarmError ??
|
|
26
|
+
((error) => {
|
|
27
|
+
console.error("[worker-runtime] background API warm failed:", error);
|
|
28
|
+
});
|
|
29
|
+
function toAppRequest(request) {
|
|
30
|
+
const url = new URL(request.url);
|
|
31
|
+
const stripped = url.pathname.slice(apiPrefix.length) || "/";
|
|
32
|
+
const appUrl = new URL(stripped, url.origin);
|
|
33
|
+
appUrl.search = url.search;
|
|
34
|
+
const bodyless = request.method === "GET" || request.method === "HEAD";
|
|
35
|
+
return new Request(appUrl.toString(), {
|
|
36
|
+
method: request.method,
|
|
37
|
+
headers: request.headers,
|
|
38
|
+
body: bodyless ? null : request.body,
|
|
39
|
+
redirect: request.redirect,
|
|
40
|
+
signal: request.signal,
|
|
41
|
+
...(bodyless ? {} : { duplex: "half" }),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
async function dispatch(request, env, ctx) {
|
|
45
|
+
const { loadApiApp, loadAuthApp } = options;
|
|
46
|
+
if (loadAuthApp && matchesPrefix(new URL(request.url).pathname, authPrefix)) {
|
|
47
|
+
const authApp = await loadAuthApp();
|
|
48
|
+
const response = await authApp.fetch(toAppRequest(request), env, ctx);
|
|
49
|
+
if (warmApiOnAuth && request.method !== "OPTIONS") {
|
|
50
|
+
ctx.waitUntil(loadApiApp().then(() => undefined, onWarmError));
|
|
51
|
+
}
|
|
52
|
+
return response;
|
|
53
|
+
}
|
|
54
|
+
const apiApp = await loadApiApp();
|
|
55
|
+
return apiApp.fetch(toAppRequest(request), env, ctx);
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
isApiRequest: (pathname) => matchesPrefix(pathname, apiPrefix),
|
|
59
|
+
isAuthRequest: (pathname) => matchesPrefix(pathname, authPrefix),
|
|
60
|
+
toAppRequest,
|
|
61
|
+
dispatch,
|
|
62
|
+
};
|
|
63
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type { ApiDispatch, CreateApiDispatchOptions } from "./api-dispatch.js";
|
|
2
|
+
export { createApiDispatch, lazyApp } from "./api-dispatch.js";
|
|
3
|
+
export type { SsrManifest, SsrManifestRouter } from "./ssr-manifest.js";
|
|
4
|
+
export { restrictSsrManifestToActiveRoutes, withActiveRouteSsrManifest } from "./ssr-manifest.js";
|
|
5
|
+
export type { AppLoader, FetchApp, WaitUntilContext } from "./types.js";
|
|
6
|
+
export type { CreateWorkerFetchOptions, SsrHandler } from "./worker-fetch.js";
|
|
7
|
+
export { createWorkerFetch } from "./worker-fetch.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,WAAW,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;AAC9E,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAC9D,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACvE,OAAO,EAAE,iCAAiC,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAA;AACjG,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AACvE,YAAY,EAAE,wBAAwB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR-manifest restriction for TanStack Start workers.
|
|
3
|
+
*
|
|
4
|
+
* By default the SSR stream emits preload/asset hints for every route in the
|
|
5
|
+
* manifest; on large admins that floods the first paint with hundreds of
|
|
6
|
+
* speculative preloads. Restricting the manifest to the matched routes keeps
|
|
7
|
+
* the initial response lean. Typed structurally so this package needs no
|
|
8
|
+
* dependency on the router — any router exposing `stores.matches` and an
|
|
9
|
+
* `ssr.manifest` fits.
|
|
10
|
+
*/
|
|
11
|
+
type ManifestRoute = {
|
|
12
|
+
assets?: Array<unknown>;
|
|
13
|
+
preloads?: Array<unknown>;
|
|
14
|
+
};
|
|
15
|
+
export type SsrManifest = {
|
|
16
|
+
inlineCss?: unknown;
|
|
17
|
+
routes: Record<string, ManifestRoute | undefined>;
|
|
18
|
+
};
|
|
19
|
+
export interface SsrManifestRouter {
|
|
20
|
+
stores: {
|
|
21
|
+
matches: {
|
|
22
|
+
get(): ReadonlyArray<{
|
|
23
|
+
routeId: string;
|
|
24
|
+
}>;
|
|
25
|
+
};
|
|
26
|
+
};
|
|
27
|
+
ssr?: {
|
|
28
|
+
readonly manifest?: SsrManifest;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Replace `router.ssr.manifest` with a view filtered to the active route
|
|
33
|
+
* matches. The filter runs lazily (getter) because matches settle after the
|
|
34
|
+
* handler installs the restriction.
|
|
35
|
+
*/
|
|
36
|
+
export declare function restrictSsrManifestToActiveRoutes(router: SsrManifestRouter): void;
|
|
37
|
+
/**
|
|
38
|
+
* Wrap a TanStack Start stream-handler callback so every SSR render gets the
|
|
39
|
+
* active-route manifest restriction:
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* const startHandler = createStartHandler(
|
|
43
|
+
* withActiveRouteSsrManifest(defaultStreamHandler),
|
|
44
|
+
* )
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare function withActiveRouteSsrManifest<TCtx extends {
|
|
48
|
+
router: unknown;
|
|
49
|
+
}, TResult>(handler: (ctx: TCtx) => TResult): (ctx: TCtx) => TResult;
|
|
50
|
+
export {};
|
|
51
|
+
//# sourceMappingURL=ssr-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr-manifest.d.ts","sourceRoot":"","sources":["../src/ssr-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,KAAK,aAAa,GAAG;IACnB,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;IACvB,QAAQ,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,GAAG,SAAS,CAAC,CAAA;CAClD,CAAA;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE;QACN,OAAO,EAAE;YACP,GAAG,IAAI,aAAa,CAAC;gBAAE,OAAO,EAAE,MAAM,CAAA;aAAE,CAAC,CAAA;SAC1C,CAAA;KACF,CAAA;IACD,GAAG,CAAC,EAAE;QACJ,QAAQ,CAAC,QAAQ,CAAC,EAAE,WAAW,CAAA;KAChC,CAAA;CACF;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAoBjF;AAED;;;;;;;;;GASG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,SAAS;IAAE,MAAM,EAAE,OAAO,CAAA;CAAE,EAAE,OAAO,EAClF,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,GAC9B,CAAC,GAAG,EAAE,IAAI,KAAK,OAAO,CAKxB"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SSR-manifest restriction for TanStack Start workers.
|
|
3
|
+
*
|
|
4
|
+
* By default the SSR stream emits preload/asset hints for every route in the
|
|
5
|
+
* manifest; on large admins that floods the first paint with hundreds of
|
|
6
|
+
* speculative preloads. Restricting the manifest to the matched routes keeps
|
|
7
|
+
* the initial response lean. Typed structurally so this package needs no
|
|
8
|
+
* dependency on the router — any router exposing `stores.matches` and an
|
|
9
|
+
* `ssr.manifest` fits.
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Replace `router.ssr.manifest` with a view filtered to the active route
|
|
13
|
+
* matches. The filter runs lazily (getter) because matches settle after the
|
|
14
|
+
* handler installs the restriction.
|
|
15
|
+
*/
|
|
16
|
+
export function restrictSsrManifestToActiveRoutes(router) {
|
|
17
|
+
const ssr = router.ssr;
|
|
18
|
+
if (!ssr?.manifest)
|
|
19
|
+
return;
|
|
20
|
+
router.ssr = {
|
|
21
|
+
get manifest() {
|
|
22
|
+
const manifest = ssr.manifest;
|
|
23
|
+
if (!manifest)
|
|
24
|
+
return manifest;
|
|
25
|
+
const activeRouteIds = new Set(router.stores.matches.get().map((match) => match.routeId));
|
|
26
|
+
const routes = Object.fromEntries(Object.entries(manifest.routes).filter(([routeId]) => activeRouteIds.has(routeId)));
|
|
27
|
+
return {
|
|
28
|
+
...manifest,
|
|
29
|
+
routes,
|
|
30
|
+
};
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Wrap a TanStack Start stream-handler callback so every SSR render gets the
|
|
36
|
+
* active-route manifest restriction:
|
|
37
|
+
*
|
|
38
|
+
* ```ts
|
|
39
|
+
* const startHandler = createStartHandler(
|
|
40
|
+
* withActiveRouteSsrManifest(defaultStreamHandler),
|
|
41
|
+
* )
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function withActiveRouteSsrManifest(handler) {
|
|
45
|
+
return (ctx) => {
|
|
46
|
+
restrictSsrManifestToActiveRoutes(ctx.router);
|
|
47
|
+
return handler(ctx);
|
|
48
|
+
};
|
|
49
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal structural slice of the Cloudflare `ExecutionContext`. Only
|
|
3
|
+
* `waitUntil` is required by this package; the full context object is passed
|
|
4
|
+
* through to apps untouched, so any richer context type remains compatible.
|
|
5
|
+
*/
|
|
6
|
+
export interface WaitUntilContext {
|
|
7
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Anything with a Worker-style `fetch` — a Hono app, a Better Auth handler,
|
|
11
|
+
* or a plain `{ fetch }` module default export.
|
|
12
|
+
*/
|
|
13
|
+
export interface FetchApp<Env = unknown, Ctx extends WaitUntilContext = WaitUntilContext> {
|
|
14
|
+
fetch(request: Request, env?: Env, ctx?: Ctx): Response | Promise<Response>;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Lazily resolves a {@link FetchApp}. Loaders own the dynamic `import()` of
|
|
18
|
+
* app modules so the Worker entry stays out of the API graph until a request
|
|
19
|
+
* actually needs it.
|
|
20
|
+
*/
|
|
21
|
+
export type AppLoader<Env = unknown, Ctx extends WaitUntilContext = WaitUntilContext> = () => Promise<FetchApp<Env, Ctx>>;
|
|
22
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;CAC3C;AAED;;;GAGG;AACH,MAAM,WAAW,QAAQ,CAAC,GAAG,GAAG,OAAO,EAAE,GAAG,SAAS,gBAAgB,GAAG,gBAAgB;IACtF,KAAK,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;CAC5E;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,CACnB,GAAG,GAAG,OAAO,EACb,GAAG,SAAS,gBAAgB,GAAG,gBAAgB,IAC7C,MAAM,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ApiDispatch, type CreateApiDispatchOptions } from "./api-dispatch.js";
|
|
2
|
+
import type { WaitUntilContext } from "./types.js";
|
|
3
|
+
export type SsrHandler<Env, Ctx extends WaitUntilContext = WaitUntilContext> = (request: Request, env: Env, ctx: Ctx) => Response | Promise<Response>;
|
|
4
|
+
export interface CreateWorkerFetchOptions<Env, Ctx extends WaitUntilContext = WaitUntilContext> {
|
|
5
|
+
/** An {@link ApiDispatch} instance, or options to build one. */
|
|
6
|
+
api: ApiDispatch<Env, Ctx> | CreateApiDispatchOptions<Env, Ctx>;
|
|
7
|
+
/** Handles every non-API request (typically the SSR start handler). */
|
|
8
|
+
ssr: SsrHandler<Env, Ctx>;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The Worker `fetch` entrypoint: API-prefixed requests go through the
|
|
12
|
+
* prefix-stripping dispatch, everything else goes to SSR. The app's
|
|
13
|
+
* `entry.ts` shrinks to bindings plus this factory call.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createWorkerFetch<Env, Ctx extends WaitUntilContext = WaitUntilContext>(options: CreateWorkerFetchOptions<Env, Ctx>): (request: Request, env: Env, ctx: Ctx) => Promise<Response>;
|
|
16
|
+
//# sourceMappingURL=worker-fetch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker-fetch.d.ts","sourceRoot":"","sources":["../src/worker-fetch.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAChB,KAAK,wBAAwB,EAE9B,MAAM,mBAAmB,CAAA;AAC1B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAElD,MAAM,MAAM,UAAU,CAAC,GAAG,EAAE,GAAG,SAAS,gBAAgB,GAAG,gBAAgB,IAAI,CAC7E,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,KACL,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEjC,MAAM,WAAW,wBAAwB,CAAC,GAAG,EAAE,GAAG,SAAS,gBAAgB,GAAG,gBAAgB;IAC5F,gEAAgE;IAChE,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,wBAAwB,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;IAC/D,uEAAuE;IACvE,GAAG,EAAE,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;CAC1B;AAQD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,GAAG,SAAS,gBAAgB,GAAG,gBAAgB,EACpF,OAAO,EAAE,wBAAwB,CAAC,GAAG,EAAE,GAAG,CAAC,GAC1C,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,OAAO,CAAC,QAAQ,CAAC,CAS7D"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createApiDispatch, } from "./api-dispatch.js";
|
|
2
|
+
function isApiDispatch(api) {
|
|
3
|
+
return "dispatch" in api;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The Worker `fetch` entrypoint: API-prefixed requests go through the
|
|
7
|
+
* prefix-stripping dispatch, everything else goes to SSR. The app's
|
|
8
|
+
* `entry.ts` shrinks to bindings plus this factory call.
|
|
9
|
+
*/
|
|
10
|
+
export function createWorkerFetch(options) {
|
|
11
|
+
const api = isApiDispatch(options.api) ? options.api : createApiDispatch(options.api);
|
|
12
|
+
return async (request, env, ctx) => {
|
|
13
|
+
if (api.isApiRequest(new URL(request.url).pathname)) {
|
|
14
|
+
return api.dispatch(request, env, ctx);
|
|
15
|
+
}
|
|
16
|
+
return options.ssr(request, env, ctx);
|
|
17
|
+
};
|
|
18
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@voyantjs/worker-runtime",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./src/index.ts",
|
|
9
|
+
"./api-dispatch": "./src/api-dispatch.ts",
|
|
10
|
+
"./ssr-manifest": "./src/ssr-manifest.ts",
|
|
11
|
+
"./worker-fetch": "./src/worker-fetch.ts",
|
|
12
|
+
"./types": "./src/types.ts"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"lint": "biome check src/ tests/",
|
|
17
|
+
"test": "vitest run",
|
|
18
|
+
"build": "tsc -p tsconfig.json",
|
|
19
|
+
"clean": "rm -rf dist tsconfig.tsbuildinfo",
|
|
20
|
+
"prepack": "pnpm run build"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public",
|
|
27
|
+
"exports": {
|
|
28
|
+
".": {
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"default": "./dist/index.js"
|
|
32
|
+
},
|
|
33
|
+
"./api-dispatch": {
|
|
34
|
+
"types": "./dist/api-dispatch.d.ts",
|
|
35
|
+
"import": "./dist/api-dispatch.js",
|
|
36
|
+
"default": "./dist/api-dispatch.js"
|
|
37
|
+
},
|
|
38
|
+
"./ssr-manifest": {
|
|
39
|
+
"types": "./dist/ssr-manifest.d.ts",
|
|
40
|
+
"import": "./dist/ssr-manifest.js",
|
|
41
|
+
"default": "./dist/ssr-manifest.js"
|
|
42
|
+
},
|
|
43
|
+
"./worker-fetch": {
|
|
44
|
+
"types": "./dist/worker-fetch.d.ts",
|
|
45
|
+
"import": "./dist/worker-fetch.js",
|
|
46
|
+
"default": "./dist/worker-fetch.js"
|
|
47
|
+
},
|
|
48
|
+
"./types": {
|
|
49
|
+
"types": "./dist/types.d.ts",
|
|
50
|
+
"import": "./dist/types.js",
|
|
51
|
+
"default": "./dist/types.js"
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"main": "./dist/index.js",
|
|
55
|
+
"types": "./dist/index.d.ts"
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@voyantjs/voyant-typescript-config": "workspace:^",
|
|
59
|
+
"typescript": "^6.0.2",
|
|
60
|
+
"vitest": "^4.1.2"
|
|
61
|
+
},
|
|
62
|
+
"repository": {
|
|
63
|
+
"type": "git",
|
|
64
|
+
"url": "https://github.com/voyantjs/voyant.git",
|
|
65
|
+
"directory": "packages/worker-runtime"
|
|
66
|
+
}
|
|
67
|
+
}
|