@velajs/cloudflare 1.9.0 → 1.10.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/CHANGELOG.md +8 -3
- package/dist/index.d.ts +805 -39
- package/dist/index.js +1645 -37
- package/dist/index.js.map +1 -0
- package/package.json +55 -41
- package/dist/binding-ref.d.ts +0 -8
- package/dist/binding-ref.js +0 -19
- package/dist/cloudflare-application.d.ts +0 -125
- package/dist/cloudflare-application.js +0 -186
- package/dist/cloudflare-factory.d.ts +0 -88
- package/dist/cloudflare-factory.js +0 -101
- package/dist/decorators/env.d.ts +0 -16
- package/dist/decorators/env.js +0 -22
- package/dist/decorators/queue-consumer.d.ts +0 -23
- package/dist/decorators/queue-consumer.js +0 -40
- package/dist/decorators/scheduled.d.ts +0 -20
- package/dist/decorators/scheduled.js +0 -36
- package/dist/env-ref.d.ts +0 -10
- package/dist/env-ref.js +0 -13
- package/dist/modules/ai.module.d.ts +0 -1
- package/dist/modules/ai.module.js +0 -8
- package/dist/modules/create-binding-module.d.ts +0 -14
- package/dist/modules/create-binding-module.js +0 -42
- package/dist/modules/d1.module.d.ts +0 -1
- package/dist/modules/d1.module.js +0 -8
- package/dist/modules/durable-object.module.d.ts +0 -1
- package/dist/modules/durable-object.module.js +0 -8
- package/dist/modules/env.module.d.ts +0 -17
- package/dist/modules/env.module.js +0 -35
- package/dist/modules/hyperdrive.module.d.ts +0 -1
- package/dist/modules/hyperdrive.module.js +0 -8
- package/dist/modules/kv.module.d.ts +0 -1
- package/dist/modules/kv.module.js +0 -8
- package/dist/modules/queue.module.d.ts +0 -1
- package/dist/modules/queue.module.js +0 -8
- package/dist/modules/r2.module.d.ts +0 -1
- package/dist/modules/r2.module.js +0 -8
- package/dist/modules/vectorize.module.d.ts +0 -1
- package/dist/modules/vectorize.module.js +0 -8
- package/dist/services/ai.service.d.ts +0 -6
- package/dist/services/ai.service.js +0 -33
- package/dist/services/d1.service.d.ts +0 -6
- package/dist/services/d1.service.js +0 -33
- package/dist/services/durable-object.service.d.ts +0 -6
- package/dist/services/durable-object.service.js +0 -33
- package/dist/services/env.service.d.ts +0 -31
- package/dist/services/env.service.js +0 -36
- package/dist/services/flagship-flag.driver.d.ts +0 -57
- package/dist/services/flagship-flag.driver.js +0 -49
- package/dist/services/hyperdrive.service.d.ts +0 -12
- package/dist/services/hyperdrive.service.js +0 -51
- package/dist/services/kv-cache.store.d.ts +0 -19
- package/dist/services/kv-cache.store.js +0 -58
- package/dist/services/kv-flag.driver.d.ts +0 -48
- package/dist/services/kv-flag.driver.js +0 -62
- package/dist/services/kv.service.d.ts +0 -11
- package/dist/services/kv.service.js +0 -33
- package/dist/services/queue.service.d.ts +0 -6
- package/dist/services/queue.service.js +0 -33
- package/dist/services/r2.service.d.ts +0 -6
- package/dist/services/r2.service.js +0 -33
- package/dist/services/vectorize.service.d.ts +0 -6
- package/dist/services/vectorize.service.js +0 -33
- package/dist/storage/index.d.ts +0 -7
- package/dist/storage/index.js +0 -6
- package/dist/storage/r2-storage.driver.d.ts +0 -19
- package/dist/storage/r2-storage.driver.js +0 -61
- package/dist/storage/storage-manager.service.d.ts +0 -16
- package/dist/storage/storage-manager.service.js +0 -56
- package/dist/storage/storage.controller.d.ts +0 -16
- package/dist/storage/storage.controller.js +0 -88
- package/dist/storage/storage.module.d.ts +0 -7
- package/dist/storage/storage.module.js +0 -37
- package/dist/storage/storage.service.d.ts +0 -20
- package/dist/storage/storage.service.js +0 -78
- package/dist/storage/storage.tokens.d.ts +0 -3
- package/dist/storage/storage.tokens.js +0 -2
- package/dist/storage/storage.types.d.ts +0 -17
- package/dist/storage/storage.types.js +0 -1
- package/dist/tokens.d.ts +0 -12
- package/dist/tokens.js +0 -12
- package/dist/types.d.ts +0 -13
- package/dist/types.js +0 -1
- package/dist/websocket/broadcast.d.ts +0 -15
- package/dist/websocket/broadcast.js +0 -26
- package/dist/websocket/cf-room-registry.d.ts +0 -23
- package/dist/websocket/cf-room-registry.js +0 -65
- package/dist/websocket/cf-ws-client.d.ts +0 -28
- package/dist/websocket/cf-ws-client.js +0 -83
- package/dist/websocket/cloudflare-websocket.module.d.ts +0 -11
- package/dist/websocket/cloudflare-websocket.module.js +0 -27
- package/dist/websocket/do-bootstrap.d.ts +0 -21
- package/dist/websocket/do-bootstrap.js +0 -50
- package/dist/websocket/do-state.d.ts +0 -25
- package/dist/websocket/do-state.js +0 -4
- package/dist/websocket/do-websocket-host.d.ts +0 -28
- package/dist/websocket/do-websocket-host.js +0 -69
- package/dist/websocket/index.d.ts +0 -13
- package/dist/websocket/index.js +0 -13
- package/dist/websocket/room-id.d.ts +0 -6
- package/dist/websocket/room-id.js +0 -12
- package/dist/websocket/websocket-routing.d.ts +0 -14
- package/dist/websocket/websocket-routing.js +0 -45
- package/dist/websocket/websocket.durable-object.d.ts +0 -15
- package/dist/websocket/websocket.durable-object.js +0 -72
- package/dist/websocket/ws-server-holder.d.ts +0 -16
- package/dist/websocket/ws-server-holder.js +0 -34
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { CRON_METADATA, PipelineRunner, buildEntrypointExecutionContext, registerEntrypointKind, runInEntrypointScope, shouldFilterCatch } from "@velajs/vela";
|
|
2
|
-
import { ComponentManager } from "@velajs/vela/internal";
|
|
3
|
-
import { collectWsGatewayRoutes } from "./websocket/websocket-routing.js";
|
|
4
|
-
// vela's own @Cron jobs run via the same Workers cron trigger — declare an
|
|
5
|
-
// entrypoint kind over vela's metadata key (the open-kind system makes
|
|
6
|
-
// cross-package declarations first-class).
|
|
7
|
-
registerEntrypointKind({
|
|
8
|
-
kind: 'cf:vela-cron',
|
|
9
|
-
metaKey: CRON_METADATA,
|
|
10
|
-
level: 'method'
|
|
11
|
-
});
|
|
12
|
-
function invoke(instance, methodName, args) {
|
|
13
|
-
const method = instance[methodName];
|
|
14
|
-
if (typeof method !== 'function') {
|
|
15
|
-
throw new Error(`Method '${methodName}' is not a function on ${instance.constructor.name}`);
|
|
16
|
-
}
|
|
17
|
-
return method.apply(instance, args);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Wraps VelaApplication with Cloudflare-specific handlers:
|
|
21
|
-
* - `fetch` — HTTP request handler (from Hono)
|
|
22
|
-
* - `scheduled` — Cron trigger handler (matches `@Scheduled()` decorators
|
|
23
|
-
* AND vela's own `@Cron()` jobs)
|
|
24
|
-
* - `queue` — Queue consumer handler (matches `@QueueConsumer()` decorators)
|
|
25
|
-
* - `mountOpenApi` — Serve an OpenAPI document (and optional Scalar UI) on
|
|
26
|
-
* the underlying Hono app
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```ts
|
|
30
|
-
* const app = await createCloudflareApp(AppModule);
|
|
31
|
-
* export default {
|
|
32
|
-
* fetch: app.fetch,
|
|
33
|
-
* scheduled: app.scheduled.bind(app),
|
|
34
|
-
* queue: app.queue.bind(app),
|
|
35
|
-
* };
|
|
36
|
-
* ```
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* // Serve OpenAPI docs alongside your routes
|
|
41
|
-
* const app = await createCloudflareApp(AppModule);
|
|
42
|
-
* const document = createOpenApiDocument(AppModule);
|
|
43
|
-
* app.mountOpenApi({ document, ui: 'scalar' });
|
|
44
|
-
* // GET /openapi.json -> JSON document
|
|
45
|
-
* // GET /scalar -> Scalar UI (loads from CDN)
|
|
46
|
-
* ```
|
|
47
|
-
*/ export class CloudflareApplication {
|
|
48
|
-
app;
|
|
49
|
-
wsGatewayRoutes = [];
|
|
50
|
-
constructor(app){
|
|
51
|
-
this.app = app;
|
|
52
|
-
}
|
|
53
|
-
get fetch() {
|
|
54
|
-
return this.app.fetch;
|
|
55
|
-
}
|
|
56
|
-
getHonoApp() {
|
|
57
|
-
return this.app.getHonoApp();
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Resolve a provider from the application's DI container (delegates to
|
|
61
|
-
* `VelaApplication.get`). Handy for grabbing a service — e.g. an auth service —
|
|
62
|
-
* to use inside `createCloudflareApp({ middleware: [...] })` request middleware,
|
|
63
|
-
* which runs outside the DI request pipeline.
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* ```ts
|
|
67
|
-
* const app = await createCloudflareApp(AppModule);
|
|
68
|
-
* const auth = app.get<BetterAuthService>(BetterAuthService);
|
|
69
|
-
* ```
|
|
70
|
-
*/ get(token) {
|
|
71
|
-
return this.app.get(token);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Serve a pre-built OpenAPI document (and optionally a Scalar UI) on the
|
|
75
|
-
* underlying Hono app. Delegates verbatim to `VelaApplication.mountOpenApi`,
|
|
76
|
-
* so the JSON endpoint defaults to `/openapi.json` and the Scalar UI (when
|
|
77
|
-
* opted in) defaults to `/scalar`. Edge-safe — the UI HTML loads Scalar from
|
|
78
|
-
* a CDN at runtime, nothing is bundled server-side.
|
|
79
|
-
*
|
|
80
|
-
* @example
|
|
81
|
-
* ```ts
|
|
82
|
-
* import { createOpenApiDocument } from '@velajs/vela';
|
|
83
|
-
*
|
|
84
|
-
* const app = await createCloudflareApp(AppModule);
|
|
85
|
-
* const document = createOpenApiDocument(AppModule, {
|
|
86
|
-
* info: { title: 'My API', version: '1.0.0' },
|
|
87
|
-
* });
|
|
88
|
-
* app.mountOpenApi({ document, ui: 'scalar' });
|
|
89
|
-
* // GET /openapi.json -> { openapi: '3.1.0', ... }
|
|
90
|
-
* // GET /scalar -> Scalar UI HTML
|
|
91
|
-
* ```
|
|
92
|
-
*/ mountOpenApi(options) {
|
|
93
|
-
this.app.mountOpenApi(options);
|
|
94
|
-
return this;
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* @internal — scans instances for `@WebSocketGateway({ path, binding })`
|
|
98
|
-
* upgrade routes. Queue/scheduled handlers are NOT scanned anymore: they
|
|
99
|
-
* come from `app.entrypoints` (`cf:queue` / `cf:scheduled` / `cf:vela-cron`
|
|
100
|
-
* kinds) at dispatch time.
|
|
101
|
-
*/ scanInstances(instances) {
|
|
102
|
-
for (const instance of instances){
|
|
103
|
-
if (!instance || typeof instance !== 'object') continue;
|
|
104
|
-
this.wsGatewayRoutes.push(...collectWsGatewayRoutes(instance));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
/** @internal — upgrade routes discovered from `@WebSocketGateway({ path, binding })`. */ getWsGatewayRoutes() {
|
|
108
|
-
return this.wsGatewayRoutes;
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Handle Cloudflare scheduled (cron) events.
|
|
112
|
-
* Matches the event's cron expression to `@Scheduled()` and vela `@Cron()`
|
|
113
|
-
* handlers read from `app.entrypoints`; each handler runs inside a fresh
|
|
114
|
-
* request-scoped child (request-scoped providers rebuild per tick).
|
|
115
|
-
*/ async scheduled(event, env, ctx) {
|
|
116
|
-
const handlers = [
|
|
117
|
-
...this.app.entrypoints.ofKind('cf:scheduled').map((ep)=>({
|
|
118
|
-
ep,
|
|
119
|
-
cron: ep.meta.cron
|
|
120
|
-
})),
|
|
121
|
-
...this.app.entrypoints.ofKind('cf:vela-cron').map((ep)=>({
|
|
122
|
-
ep,
|
|
123
|
-
cron: ep.meta.expression
|
|
124
|
-
}))
|
|
125
|
-
].filter((h)=>h.cron === event.cron);
|
|
126
|
-
await Promise.all(handlers.map(({ ep })=>this.dispatchEntrypoint(ep, [
|
|
127
|
-
event,
|
|
128
|
-
env,
|
|
129
|
-
ctx
|
|
130
|
-
])));
|
|
131
|
-
}
|
|
132
|
-
/**
|
|
133
|
-
* Run one entrypoint handler inside a fresh request scope, through the
|
|
134
|
-
* shared guard → interceptor pipeline (components declared with
|
|
135
|
-
* `@UseGuards`/`@UseInterceptors`/`@UseFilters` on the consumer class or
|
|
136
|
-
* method). HTTP-global components deliberately do NOT apply — an HTTP auth
|
|
137
|
-
* guard has no business rejecting a queue batch. Unclaimed errors rethrow
|
|
138
|
-
* so the platform's retry semantics stay intact.
|
|
139
|
-
*/ async dispatchEntrypoint(ep, args) {
|
|
140
|
-
const targetClass = ep.token;
|
|
141
|
-
const methodName = String(ep.methodName);
|
|
142
|
-
const context = buildEntrypointExecutionContext(ep.kind, targetClass, methodName, args[0]);
|
|
143
|
-
await runInEntrypointScope(this.app.getContainer(), async (scope)=>{
|
|
144
|
-
const instance = scope.resolve(ep.token);
|
|
145
|
-
const guards = ComponentManager.resolveGuards(ComponentManager.getScopedComponents('guard', targetClass, methodName), scope);
|
|
146
|
-
const interceptors = ComponentManager.resolveInterceptors(ComponentManager.getScopedComponents('interceptor', targetClass, methodName), scope);
|
|
147
|
-
// Closest-first, mirroring the HTTP/WS dispatchers.
|
|
148
|
-
const filters = ComponentManager.resolveFilters([
|
|
149
|
-
...ComponentManager.getScopedComponents('filter', targetClass, methodName)
|
|
150
|
-
].reverse(), scope);
|
|
151
|
-
try {
|
|
152
|
-
await PipelineRunner.run({
|
|
153
|
-
context,
|
|
154
|
-
guards,
|
|
155
|
-
interceptors,
|
|
156
|
-
resolveArgs: async ()=>args,
|
|
157
|
-
invoke: async (resolved)=>invoke(instance, methodName, resolved)
|
|
158
|
-
});
|
|
159
|
-
} catch (error) {
|
|
160
|
-
for (const filter of filters){
|
|
161
|
-
if (shouldFilterCatch(filter, error)) {
|
|
162
|
-
await filter.catch(error, context);
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
throw error;
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Handle Cloudflare Queue consumer events.
|
|
172
|
-
* Matches the batch queue name to `@QueueConsumer()` handlers read from
|
|
173
|
-
* `app.entrypoints`; each batch is processed inside a fresh request-scoped
|
|
174
|
-
* child (request-scoped providers rebuild per batch — no boot-time captives).
|
|
175
|
-
*/ async queue(batch, env, ctx) {
|
|
176
|
-
const handlers = this.app.entrypoints.ofKind('cf:queue').filter((ep)=>ep.meta.queueName === batch.queue);
|
|
177
|
-
await Promise.all(handlers.map((ep)=>this.dispatchEntrypoint(ep, [
|
|
178
|
-
batch,
|
|
179
|
-
env,
|
|
180
|
-
ctx
|
|
181
|
-
])));
|
|
182
|
-
}
|
|
183
|
-
async close(signal) {
|
|
184
|
-
return this.app.close(signal);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import type { MiddlewareHandler } from 'hono';
|
|
2
|
-
import type { RuntimeAdapter, Type } from '@velajs/vela';
|
|
3
|
-
import { CloudflareApplication } from './cloudflare-application';
|
|
4
|
-
/**
|
|
5
|
-
* Options for {@link createCloudflareApp}.
|
|
6
|
-
*
|
|
7
|
-
* Mirrors a tight subset of vela's `BootstrapOptions` — only the surface
|
|
8
|
-
* that makes sense for a Workers consumer is re-exposed.
|
|
9
|
-
*/
|
|
10
|
-
export interface CreateCloudflareAppOptions {
|
|
11
|
-
/**
|
|
12
|
-
* Forwarded to `VelaFactory.create({ globalPrefix })`. Prepended to every
|
|
13
|
-
* route registered by `@Controller(...)` (and any other route emitter)
|
|
14
|
-
* inside the application, so a value of `'/v1'` turns `@Controller('/users')`
|
|
15
|
-
* into `/v1/users`.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* const app = await createCloudflareApp(AppModule, { globalPrefix: '/v1' });
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
globalPrefix?: string;
|
|
23
|
-
/**
|
|
24
|
-
* Extra Hono middleware to register on the underlying Hono app. Runs
|
|
25
|
-
* AFTER the one-time binding-init middleware this adapter mounts
|
|
26
|
-
* internally, so any handler in `middleware` can safely read from
|
|
27
|
-
* `BindingRef` instances and Cloudflare env bindings.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* ```ts
|
|
31
|
-
* const app = await createCloudflareApp(AppModule, {
|
|
32
|
-
* middleware: [
|
|
33
|
-
* async (c, next) => {
|
|
34
|
-
* c.set('requestId', crypto.randomUUID());
|
|
35
|
-
* await next();
|
|
36
|
-
* },
|
|
37
|
-
* ],
|
|
38
|
-
* });
|
|
39
|
-
* ```
|
|
40
|
-
*/
|
|
41
|
-
middleware?: MiddlewareHandler[];
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Create a Cloudflare Workers application.
|
|
45
|
-
*
|
|
46
|
-
* Sets up a one-time Hono middleware that captures `c.env` on the first
|
|
47
|
-
* request and initializes all configured binding refs. Optional
|
|
48
|
-
* {@link CreateCloudflareAppOptions} are forwarded to the underlying
|
|
49
|
-
* `VelaFactory.create` so consumers don't have to wrap the resulting
|
|
50
|
-
* application in an outer Hono just to set a `globalPrefix` or attach
|
|
51
|
-
* extra request middleware.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* // Minimal — backwards compatible
|
|
56
|
-
* const app = await createCloudflareApp(AppModule);
|
|
57
|
-
* export default app; // has .fetch, .scheduled, .queue
|
|
58
|
-
* ```
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* ```ts
|
|
62
|
-
* // With a global prefix and outer middleware
|
|
63
|
-
* const app = await createCloudflareApp(AppModule, {
|
|
64
|
-
* globalPrefix: '/v1',
|
|
65
|
-
* middleware: [
|
|
66
|
-
* async (c, next) => {
|
|
67
|
-
* c.set('tenantId', c.req.header('x-tenant-id') ?? 'public');
|
|
68
|
-
* await next();
|
|
69
|
-
* },
|
|
70
|
-
* ],
|
|
71
|
-
* });
|
|
72
|
-
* ```
|
|
73
|
-
*/
|
|
74
|
-
/**
|
|
75
|
-
* The Cloudflare platform binding as a vela {@link RuntimeAdapter}: a one-time
|
|
76
|
-
* request middleware captures `c.env` on the first request and initializes
|
|
77
|
-
* every `BindingRef`/`EnvRef` (collected at `onBootstrap`, before any request
|
|
78
|
-
* can arrive). Adapter `requestMiddleware` is prepended to the global chain by
|
|
79
|
-
* `VelaFactory.create`, so user middleware can safely read binding refs.
|
|
80
|
-
*
|
|
81
|
-
* Exposed so consumers composing `VelaFactory.create` themselves can opt in:
|
|
82
|
-
*
|
|
83
|
-
* ```ts
|
|
84
|
-
* const app = await VelaFactory.create(AppModule, { adapters: [cloudflareAdapter()] });
|
|
85
|
-
* ```
|
|
86
|
-
*/
|
|
87
|
-
export declare function cloudflareAdapter(): RuntimeAdapter;
|
|
88
|
-
export declare function createCloudflareApp(rootModule: Type, options?: CreateCloudflareAppOptions): Promise<CloudflareApplication>;
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import { VelaFactory } from "@velajs/vela";
|
|
2
|
-
import { BindingRef } from "./binding-ref.js";
|
|
3
|
-
import { CloudflareApplication } from "./cloudflare-application.js";
|
|
4
|
-
import { EnvRef } from "./env-ref.js";
|
|
5
|
-
import { registerWebSocketRoutes } from "./websocket/websocket-routing.js";
|
|
6
|
-
// Walks every provider registered in the container and pulls out the
|
|
7
|
-
// BindingRef instances. Replaces the old module-level `bindingsRegistry`
|
|
8
|
-
// global so two CloudflareApplication instances in one process don't
|
|
9
|
-
// share state.
|
|
10
|
-
function collectBindingRefs(container) {
|
|
11
|
-
// Enumerate per-instance useValue providers across ALL module buckets. Two
|
|
12
|
-
// same-type binding modules (e.g. KVModule.forRoot for CACHE and SESSIONS)
|
|
13
|
-
// share one token but live in distinct buckets — resolving the token would
|
|
14
|
-
// return only the first, leaving the others uninitialized.
|
|
15
|
-
const refs = [];
|
|
16
|
-
for (const value of container.getUseValues()){
|
|
17
|
-
if (value instanceof BindingRef) refs.push(value);
|
|
18
|
-
}
|
|
19
|
-
return refs;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Create a Cloudflare Workers application.
|
|
23
|
-
*
|
|
24
|
-
* Sets up a one-time Hono middleware that captures `c.env` on the first
|
|
25
|
-
* request and initializes all configured binding refs. Optional
|
|
26
|
-
* {@link CreateCloudflareAppOptions} are forwarded to the underlying
|
|
27
|
-
* `VelaFactory.create` so consumers don't have to wrap the resulting
|
|
28
|
-
* application in an outer Hono just to set a `globalPrefix` or attach
|
|
29
|
-
* extra request middleware.
|
|
30
|
-
*
|
|
31
|
-
* @example
|
|
32
|
-
* ```ts
|
|
33
|
-
* // Minimal — backwards compatible
|
|
34
|
-
* const app = await createCloudflareApp(AppModule);
|
|
35
|
-
* export default app; // has .fetch, .scheduled, .queue
|
|
36
|
-
* ```
|
|
37
|
-
*
|
|
38
|
-
* @example
|
|
39
|
-
* ```ts
|
|
40
|
-
* // With a global prefix and outer middleware
|
|
41
|
-
* const app = await createCloudflareApp(AppModule, {
|
|
42
|
-
* globalPrefix: '/v1',
|
|
43
|
-
* middleware: [
|
|
44
|
-
* async (c, next) => {
|
|
45
|
-
* c.set('tenantId', c.req.header('x-tenant-id') ?? 'public');
|
|
46
|
-
* await next();
|
|
47
|
-
* },
|
|
48
|
-
* ],
|
|
49
|
-
* });
|
|
50
|
-
* ```
|
|
51
|
-
*/ /**
|
|
52
|
-
* The Cloudflare platform binding as a vela {@link RuntimeAdapter}: a one-time
|
|
53
|
-
* request middleware captures `c.env` on the first request and initializes
|
|
54
|
-
* every `BindingRef`/`EnvRef` (collected at `onBootstrap`, before any request
|
|
55
|
-
* can arrive). Adapter `requestMiddleware` is prepended to the global chain by
|
|
56
|
-
* `VelaFactory.create`, so user middleware can safely read binding refs.
|
|
57
|
-
*
|
|
58
|
-
* Exposed so consumers composing `VelaFactory.create` themselves can opt in:
|
|
59
|
-
*
|
|
60
|
-
* ```ts
|
|
61
|
-
* const app = await VelaFactory.create(AppModule, { adapters: [cloudflareAdapter()] });
|
|
62
|
-
* ```
|
|
63
|
-
*/ export function cloudflareAdapter() {
|
|
64
|
-
let initialized = false;
|
|
65
|
-
let refs = [];
|
|
66
|
-
return {
|
|
67
|
-
name: 'cloudflare',
|
|
68
|
-
requestMiddleware: [
|
|
69
|
-
async (c, next)=>{
|
|
70
|
-
if (!initialized) {
|
|
71
|
-
initialized = true;
|
|
72
|
-
const env = c.env ?? {};
|
|
73
|
-
for (const ref of refs){
|
|
74
|
-
// EnvRef holds the whole env; every other ref holds one binding.
|
|
75
|
-
if (ref instanceof EnvRef) ref._initialize(env);
|
|
76
|
-
else ref._initialize(env[ref.bindingName]);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
await next();
|
|
80
|
-
}
|
|
81
|
-
],
|
|
82
|
-
onBootstrap: ({ container })=>{
|
|
83
|
-
refs = collectBindingRefs(container);
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
export async function createCloudflareApp(rootModule, options = {}) {
|
|
88
|
-
const velaApp = await VelaFactory.create(rootModule, {
|
|
89
|
-
globalPrefix: options.globalPrefix,
|
|
90
|
-
middleware: options.middleware,
|
|
91
|
-
adapters: [
|
|
92
|
-
cloudflareAdapter()
|
|
93
|
-
]
|
|
94
|
-
});
|
|
95
|
-
const cfApp = new CloudflareApplication(velaApp);
|
|
96
|
-
cfApp.scanInstances(velaApp.getInstances());
|
|
97
|
-
// Register WebSocket upgrade routes for any @WebSocketGateway({ path, binding }).
|
|
98
|
-
// Each forwards the upgrade to the room's Durable Object (which owns the socket).
|
|
99
|
-
registerWebSocketRoutes(cfApp.getHonoApp(), cfApp.getWsGatewayRoutes());
|
|
100
|
-
return cfApp;
|
|
101
|
-
}
|
package/dist/decorators/env.d.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parameter decorator to inject Cloudflare environment bindings.
|
|
3
|
-
*
|
|
4
|
-
* Without arguments, returns the entire `env` object.
|
|
5
|
-
* With a binding name, returns that specific binding.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* @Get()
|
|
10
|
-
* handle(@Env() env: CloudflareEnv) { ... }
|
|
11
|
-
*
|
|
12
|
-
* @Get()
|
|
13
|
-
* handle(@Env('MY_KV') kv: KVNamespace) { ... }
|
|
14
|
-
* ```
|
|
15
|
-
*/
|
|
16
|
-
export declare const Env: (data?: string | undefined, ...pipes: import("@velajs/vela").PipeType[]) => ParameterDecorator;
|
package/dist/decorators/env.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { createParamDecorator } from "@velajs/vela";
|
|
2
|
-
/**
|
|
3
|
-
* Parameter decorator to inject Cloudflare environment bindings.
|
|
4
|
-
*
|
|
5
|
-
* Without arguments, returns the entire `env` object.
|
|
6
|
-
* With a binding name, returns that specific binding.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* @Get()
|
|
11
|
-
* handle(@Env() env: CloudflareEnv) { ... }
|
|
12
|
-
*
|
|
13
|
-
* @Get()
|
|
14
|
-
* handle(@Env('MY_KV') kv: KVNamespace) { ... }
|
|
15
|
-
* ```
|
|
16
|
-
*/ export const Env = createParamDecorator((bindingName, ctx)=>{
|
|
17
|
-
// Hono Context has .env on Cloudflare Workers
|
|
18
|
-
const c = ctx.getContext();
|
|
19
|
-
const env = c.env;
|
|
20
|
-
if (!env) return undefined;
|
|
21
|
-
return bindingName ? env[bindingName] : env;
|
|
22
|
-
});
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export interface QueueConsumerMetadata {
|
|
2
|
-
queueName: string;
|
|
3
|
-
methodName: string;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Marks a method as a queue consumer handler.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* @Injectable()
|
|
11
|
-
* class WorkerService {
|
|
12
|
-
* @QueueConsumer('email-queue')
|
|
13
|
-
* async processEmails(batch: MessageBatch) {
|
|
14
|
-
* for (const msg of batch.messages) {
|
|
15
|
-
* console.log('Processing:', msg.body);
|
|
16
|
-
* msg.ack();
|
|
17
|
-
* }
|
|
18
|
-
* }
|
|
19
|
-
* }
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
export declare function QueueConsumer(queueName: string): MethodDecorator;
|
|
23
|
-
export declare function getQueueConsumerMetadata(target: object): QueueConsumerMetadata[];
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { defineMetadata, getMetadata, registerEntrypointKind } from "@velajs/vela";
|
|
2
|
-
const QUEUE_CONSUMER_METADATA_KEY = 'cloudflare:queue-consumer';
|
|
3
|
-
// Open entrypoint kind: any adapter can enumerate queue consumers via
|
|
4
|
-
// `app.entrypoints.ofKind('cf:queue')` — declared here, next to the decorator,
|
|
5
|
-
// with zero vela-core involvement.
|
|
6
|
-
registerEntrypointKind({
|
|
7
|
-
kind: 'cf:queue',
|
|
8
|
-
metaKey: QUEUE_CONSUMER_METADATA_KEY,
|
|
9
|
-
level: 'method'
|
|
10
|
-
});
|
|
11
|
-
/**
|
|
12
|
-
* Marks a method as a queue consumer handler.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* ```ts
|
|
16
|
-
* @Injectable()
|
|
17
|
-
* class WorkerService {
|
|
18
|
-
* @QueueConsumer('email-queue')
|
|
19
|
-
* async processEmails(batch: MessageBatch) {
|
|
20
|
-
* for (const msg of batch.messages) {
|
|
21
|
-
* console.log('Processing:', msg.body);
|
|
22
|
-
* msg.ack();
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
25
|
-
* }
|
|
26
|
-
* ```
|
|
27
|
-
*/ export function QueueConsumer(queueName) {
|
|
28
|
-
return (target, propertyKey, _descriptor)=>{
|
|
29
|
-
const existing = getMetadata(QUEUE_CONSUMER_METADATA_KEY, target.constructor) ?? [];
|
|
30
|
-
existing.push({
|
|
31
|
-
queueName,
|
|
32
|
-
methodName: String(propertyKey)
|
|
33
|
-
});
|
|
34
|
-
defineMetadata(QUEUE_CONSUMER_METADATA_KEY, existing, target.constructor);
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
export function getQueueConsumerMetadata(target) {
|
|
38
|
-
const ctor = target.constructor ?? target;
|
|
39
|
-
return getMetadata(QUEUE_CONSUMER_METADATA_KEY, ctor) ?? [];
|
|
40
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
export interface ScheduledMetadata {
|
|
2
|
-
cron: string;
|
|
3
|
-
methodName: string;
|
|
4
|
-
}
|
|
5
|
-
/**
|
|
6
|
-
* Marks a method as a scheduled (cron) handler.
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* @Injectable()
|
|
11
|
-
* class WorkerService {
|
|
12
|
-
* @Scheduled('0 * * * *')
|
|
13
|
-
* async hourlyCron() {
|
|
14
|
-
* console.log('Running hourly');
|
|
15
|
-
* }
|
|
16
|
-
* }
|
|
17
|
-
* ```
|
|
18
|
-
*/
|
|
19
|
-
export declare function Scheduled(cron: string): MethodDecorator;
|
|
20
|
-
export declare function getScheduledMetadata(target: object): ScheduledMetadata[];
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { defineMetadata, getMetadata, registerEntrypointKind } from "@velajs/vela";
|
|
2
|
-
const SCHEDULED_METADATA_KEY = 'cloudflare:scheduled';
|
|
3
|
-
// Open entrypoint kind: adapters enumerate cron handlers via
|
|
4
|
-
// `app.entrypoints.ofKind('cf:scheduled')` — declared next to the decorator.
|
|
5
|
-
registerEntrypointKind({
|
|
6
|
-
kind: 'cf:scheduled',
|
|
7
|
-
metaKey: SCHEDULED_METADATA_KEY,
|
|
8
|
-
level: 'method'
|
|
9
|
-
});
|
|
10
|
-
/**
|
|
11
|
-
* Marks a method as a scheduled (cron) handler.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* @Injectable()
|
|
16
|
-
* class WorkerService {
|
|
17
|
-
* @Scheduled('0 * * * *')
|
|
18
|
-
* async hourlyCron() {
|
|
19
|
-
* console.log('Running hourly');
|
|
20
|
-
* }
|
|
21
|
-
* }
|
|
22
|
-
* ```
|
|
23
|
-
*/ export function Scheduled(cron) {
|
|
24
|
-
return (target, propertyKey, _descriptor)=>{
|
|
25
|
-
const existing = getMetadata(SCHEDULED_METADATA_KEY, target.constructor) ?? [];
|
|
26
|
-
existing.push({
|
|
27
|
-
cron,
|
|
28
|
-
methodName: String(propertyKey)
|
|
29
|
-
});
|
|
30
|
-
defineMetadata(SCHEDULED_METADATA_KEY, existing, target.constructor);
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
export function getScheduledMetadata(target) {
|
|
34
|
-
const ctor = target.constructor ?? target;
|
|
35
|
-
return getMetadata(SCHEDULED_METADATA_KEY, ctor) ?? [];
|
|
36
|
-
}
|
package/dist/env-ref.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { BindingRef } from './binding-ref';
|
|
2
|
-
/**
|
|
3
|
-
* Holds the entire Cloudflare Worker `env` record (bindings + vars/secrets),
|
|
4
|
-
* not a single binding. Subclasses {@link BindingRef} so createCloudflareApp's
|
|
5
|
-
* binding-init middleware collects and initializes it through the same path;
|
|
6
|
-
* the factory special-cases it to pass the full `env` instead of one binding.
|
|
7
|
-
*/
|
|
8
|
-
export declare class EnvRef extends BindingRef<Record<string, unknown>> {
|
|
9
|
-
constructor();
|
|
10
|
-
}
|
package/dist/env-ref.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { BindingRef } from "./binding-ref.js";
|
|
2
|
-
// Sentinel binding name for the whole-env holder — never used as an env key.
|
|
3
|
-
const ENV_SENTINEL = '__cf_env__';
|
|
4
|
-
/**
|
|
5
|
-
* Holds the entire Cloudflare Worker `env` record (bindings + vars/secrets),
|
|
6
|
-
* not a single binding. Subclasses {@link BindingRef} so createCloudflareApp's
|
|
7
|
-
* binding-init middleware collects and initializes it through the same path;
|
|
8
|
-
* the factory special-cases it to pass the full `env` instead of one binding.
|
|
9
|
-
*/ export class EnvRef extends BindingRef {
|
|
10
|
-
constructor(){
|
|
11
|
-
super(ENV_SENTINEL);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const AIModule: import("./create-binding-module").BindingModuleStatic;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { AIService } from "../services/ai.service.js";
|
|
2
|
-
import { AI_BINDING_REF } from "../tokens.js";
|
|
3
|
-
import { createBindingModule } from "./create-binding-module.js";
|
|
4
|
-
export const AIModule = createBindingModule({
|
|
5
|
-
name: 'AI',
|
|
6
|
-
serviceClass: AIService,
|
|
7
|
-
bindingRefToken: AI_BINDING_REF
|
|
8
|
-
});
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { type DynamicModule, type InjectionToken, type Type } from '@velajs/vela';
|
|
2
|
-
import { BindingRef } from '../binding-ref';
|
|
3
|
-
interface CreateBindingModuleOptions<TService> {
|
|
4
|
-
name: string;
|
|
5
|
-
serviceClass: Type<TService>;
|
|
6
|
-
bindingRefToken: InjectionToken<BindingRef>;
|
|
7
|
-
}
|
|
8
|
-
export interface BindingModuleStatic {
|
|
9
|
-
forRoot(options: {
|
|
10
|
-
binding: string;
|
|
11
|
-
}): DynamicModule;
|
|
12
|
-
}
|
|
13
|
-
export declare function createBindingModule<TService>(opts: CreateBindingModuleOptions<TService>): BindingModuleStatic;
|
|
14
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { defineConfigurableModule } from "@velajs/vela";
|
|
2
|
-
import { BindingRef } from "../binding-ref.js";
|
|
3
|
-
// Single factory for every Cloudflare binding module (KV, D1, R2, ...).
|
|
4
|
-
// Each binding module is just a bag of (token, service); this generates the
|
|
5
|
-
// canonical forRoot() shape via vela's `defineConfigurableModule` engine.
|
|
6
|
-
//
|
|
7
|
-
// Identity model (post vela audit #2):
|
|
8
|
-
// - One real module class is declared per binding TYPE (KV, D1, R2, ...).
|
|
9
|
-
// Its `name` property is set so error messages and diagnostics carry
|
|
10
|
-
// the friendly name (e.g. `KVModule`).
|
|
11
|
-
// - Each `forRoot({ binding })` returns `{ module, key: binding, ... }`.
|
|
12
|
-
// Vela dedups by (module, key), so two `KVModule.forRoot({...})` calls
|
|
13
|
-
// with different bindings coexist as distinct module instances; two
|
|
14
|
-
// calls with the same binding dedup.
|
|
15
|
-
export function createBindingModule(opts) {
|
|
16
|
-
// Fresh module class per binding TYPE (one KVModule, one D1Module, ...).
|
|
17
|
-
// The computed-property-name idiom is the canonical JS way to give a
|
|
18
|
-
// class expression a dynamic `name`: NamedEvaluation reads the property
|
|
19
|
-
// key and stamps it on the class at creation, instead of patching the
|
|
20
|
-
// (configurable, non-writable) `name` slot via Object.defineProperty.
|
|
21
|
-
const className = `${opts.name}Module`;
|
|
22
|
-
const moduleClass = {
|
|
23
|
-
[className]: class {
|
|
24
|
-
}
|
|
25
|
-
}[className];
|
|
26
|
-
return defineConfigurableModule({
|
|
27
|
-
module: moduleClass,
|
|
28
|
-
methodName: 'forRoot',
|
|
29
|
-
keyFrom: ({ binding })=>binding,
|
|
30
|
-
providers: ({ binding })=>[
|
|
31
|
-
{
|
|
32
|
-
provide: opts.bindingRefToken,
|
|
33
|
-
useValue: new BindingRef(binding)
|
|
34
|
-
},
|
|
35
|
-
opts.serviceClass
|
|
36
|
-
],
|
|
37
|
-
exports: [
|
|
38
|
-
opts.serviceClass,
|
|
39
|
-
opts.bindingRefToken
|
|
40
|
-
]
|
|
41
|
-
});
|
|
42
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const D1Module: import("./create-binding-module").BindingModuleStatic;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { D1Service } from "../services/d1.service.js";
|
|
2
|
-
import { D1_BINDING_REF } from "../tokens.js";
|
|
3
|
-
import { createBindingModule } from "./create-binding-module.js";
|
|
4
|
-
export const D1Module = createBindingModule({
|
|
5
|
-
name: 'D1',
|
|
6
|
-
serviceClass: D1Service,
|
|
7
|
-
bindingRefToken: D1_BINDING_REF
|
|
8
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const DurableObjectModule: import("./create-binding-module").BindingModuleStatic;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { DurableObjectService } from "../services/durable-object.service.js";
|
|
2
|
-
import { DO_BINDING_REF } from "../tokens.js";
|
|
3
|
-
import { createBindingModule } from "./create-binding-module.js";
|
|
4
|
-
export const DurableObjectModule = createBindingModule({
|
|
5
|
-
name: 'DurableObject',
|
|
6
|
-
serviceClass: DurableObjectService,
|
|
7
|
-
bindingRefToken: DO_BINDING_REF
|
|
8
|
-
});
|