@velajs/cloudflare 1.10.0 → 1.22.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/CHANGELOG.md +33 -3
- package/LICENSE +21 -0
- package/README.md +197 -322
- package/dist/durable-objects.d.ts +28 -0
- package/dist/durable-objects.js +494 -0
- package/dist/durable-objects.js.map +1 -0
- package/dist/index.d.ts +622 -41
- package/dist/index.js +1023 -39
- package/dist/index.js.map +1 -0
- package/dist/nonce-validation-LE0vFk-7.js +445 -0
- package/dist/nonce-validation-LE0vFk-7.js.map +1 -0
- package/dist/nonce.durable-object-Df3_42Sy.d.ts +141 -0
- package/package.json +66 -44
- 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 -107
- 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 -24
- package/dist/websocket/do-bootstrap.js +0 -55
- package/dist/websocket/do-live.d.ts +0 -81
- package/dist/websocket/do-live.js +0 -197
- package/dist/websocket/do-state.d.ts +0 -35
- 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 -15
- package/dist/websocket/index.js +0 -15
- 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 -83
- package/dist/websocket/ws-server-holder.d.ts +0 -16
- package/dist/websocket/ws-server-holder.js +0 -34
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,40 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Continue the coordinated framework release on the 1.x line with the current checked provider, endpoint, identity, CRUD, live, and Studio APIs. Breaking API changes are accepted during this development phase; maintained applications use the current contracts.
|
|
8
|
+
- Publish from the pnpm packages workspace with TypeScript 7 and GitHub OIDC. Obsolete standalone examples have been removed; runnable applications live in apps/.
|
|
9
|
+
- Updated workspace dependencies
|
|
10
|
+
- @velajs/feature-flags@1.22.0
|
|
11
|
+
- @velajs/vela@1.22.0
|
|
12
|
+
|
|
13
|
+
## 2.0.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Publish from the unified packages workspace with corrected repository paths, shared native tooling, TypeScript 7 checks, and npm OIDC releases. Runnable examples now live in apps/.
|
|
18
|
+
- Updated dependencies
|
|
19
|
+
- @velajs/vela@2.0.1
|
|
20
|
+
- @velajs/feature-flags@2.0.1
|
|
21
|
+
|
|
22
|
+
## 2.0.0
|
|
23
|
+
|
|
24
|
+
Native binding tokens, per-environment application lifetime, environment-created module graphs, isolated live drivers, and Durable Object live inspection. Includes a complete D1/auth/CRUD/live/Studio starter.
|
|
25
|
+
|
|
26
|
+
Requires the coordinated Vela 2.0 package set. See the workspace migration guide.
|
|
27
|
+
|
|
28
|
+
## 1.10.1
|
|
29
|
+
|
|
30
|
+
### Patch Changes
|
|
31
|
+
|
|
32
|
+
- 6f04d15: Modernize the package build, validation, and release toolchain.
|
|
33
|
+
|
|
3
34
|
## 1.7.0 (2026-07-04)
|
|
4
35
|
|
|
5
36
|
- `cloudflareAdapter()` exported (createCloudflareApp composes vela RuntimeAdapter); `@QueueConsumer`/`@Scheduled` declare open entrypoint kinds; queue/scheduled dispatch runs per-event in a request scope through PipelineRunner (consumer-scoped guards/interceptors/filters; request-scoped deps rebuild per batch); DO WebSocket reads `app.entrypoints`. Requires `@velajs/vela >=1.11.0`.
|
|
6
37
|
|
|
7
|
-
|
|
8
38
|
## 1.6.0 (2026-07-01)
|
|
9
39
|
|
|
10
40
|
### Added
|
|
@@ -25,11 +55,11 @@
|
|
|
25
55
|
|
|
26
56
|
```ts
|
|
27
57
|
// before
|
|
28
|
-
import { CloudflareFactory } from
|
|
58
|
+
import { CloudflareFactory } from "@velajs/cloudflare";
|
|
29
59
|
const app = await CloudflareFactory.create(AppModule);
|
|
30
60
|
|
|
31
61
|
// after
|
|
32
|
-
import { createCloudflareApp } from
|
|
62
|
+
import { createCloudflareApp } from "@velajs/cloudflare";
|
|
33
63
|
const app = await createCloudflareApp(AppModule);
|
|
34
64
|
```
|
|
35
65
|
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ksh
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,385 +1,260 @@
|
|
|
1
1
|
# @velajs/cloudflare
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Nest-style modules, dependency injection, controllers, queue consumers, cron triggers,
|
|
4
|
+
and live WebSockets on Cloudflare Workers. HTTP routing uses Hono. Bindings use the
|
|
5
|
+
platform's native types.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Native environment and application lifetime
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
pnpm add @velajs/cloudflare @velajs/vela hono
|
|
13
|
-
pnpm add -D @cloudflare/workers-types
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
`@cloudflare/workers-types` is a required peer (types-only — zero runtime cost). It's what gives `KVNamespace`, `D1Database`, `R2Bucket`, `Queue`, `DurableObjectNamespace`, `Ai`, `VectorizeIndex`, and `Hyperdrive` their proper types when you reach into the underlying binding.
|
|
17
|
-
|
|
18
|
-
## Service shape
|
|
19
|
-
|
|
20
|
-
Every service is a thin typed wrapper around its Cloudflare binding. Use the accessor (`.namespace`, `.database`, `.bucket`, `.queue`, `.binding`, `.index`) to call the binding's methods directly — full `@cloudflare/workers-types` autocomplete, no shim layer in between.
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
class KVService { readonly namespace: KVNamespace; }
|
|
24
|
-
class D1Service { readonly database: D1Database; }
|
|
25
|
-
class R2Service { readonly bucket: R2Bucket; }
|
|
26
|
-
class QueueService<T> { readonly queue: Queue<T>; }
|
|
27
|
-
class DurableObjectService { readonly namespace: DurableObjectNamespace; }
|
|
28
|
-
class AIService { readonly binding: Ai; }
|
|
29
|
-
class VectorizeService { readonly index: VectorizeIndex; }
|
|
30
|
-
class HyperdriveService { readonly binding: Hyperdrive; }
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Quick Start
|
|
9
|
+
Define one typed token for your generated Workers environment. Inject that token
|
|
10
|
+
wherever bindings or secrets are needed, including async provider factories.
|
|
34
11
|
|
|
35
12
|
```ts
|
|
36
|
-
import { Controller, Get,
|
|
37
|
-
import {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
async findById(id: string) {
|
|
47
|
-
const cached = await this.kv.namespace.get(`user:${id}`);
|
|
48
|
-
if (cached) return JSON.parse(cached as string);
|
|
49
|
-
|
|
50
|
-
const user = await this.d1.database.prepare('SELECT * FROM users WHERE id = ?').bind(id).first();
|
|
51
|
-
if (user) await this.kv.namespace.put(`user:${id}`, JSON.stringify(user));
|
|
52
|
-
return user;
|
|
53
|
-
}
|
|
13
|
+
import { Controller, Get, Inject, InjectionToken, Module } from '@velajs/vela';
|
|
14
|
+
import { createCloudflareWorker } from '@velajs/cloudflare';
|
|
15
|
+
|
|
16
|
+
interface WorkerEnv {
|
|
17
|
+
CACHE: KVNamespace;
|
|
18
|
+
DB: D1Database;
|
|
19
|
+
FILES: R2Bucket;
|
|
20
|
+
JOBS: Queue<{ taskId: string }>;
|
|
21
|
+
SERVICE_NAME: string;
|
|
22
|
+
APP_SECRET: string;
|
|
54
23
|
}
|
|
24
|
+
export const ENV = new InjectionToken<WorkerEnv>('Worker environment');
|
|
55
25
|
|
|
56
|
-
@Controller('/
|
|
57
|
-
class
|
|
58
|
-
constructor(private
|
|
26
|
+
@Controller('/status')
|
|
27
|
+
class StatusController {
|
|
28
|
+
constructor(@Inject(ENV) private readonly env: WorkerEnv) {}
|
|
59
29
|
|
|
60
|
-
@Get(
|
|
61
|
-
async
|
|
62
|
-
return this.
|
|
30
|
+
@Get()
|
|
31
|
+
async status() {
|
|
32
|
+
return { message: await this.env.CACHE.get('status') };
|
|
63
33
|
}
|
|
64
34
|
}
|
|
65
35
|
|
|
66
|
-
@Module({
|
|
67
|
-
imports: [
|
|
68
|
-
KVModule.forRoot({ binding: 'CACHE' }),
|
|
69
|
-
D1Module.forRoot({ binding: 'DB' }),
|
|
70
|
-
],
|
|
71
|
-
providers: [UserService],
|
|
72
|
-
controllers: [UserController],
|
|
73
|
-
})
|
|
36
|
+
@Module({ controllers: [StatusController] })
|
|
74
37
|
class AppModule {}
|
|
75
38
|
|
|
76
|
-
export default
|
|
39
|
+
export default createCloudflareWorker(AppModule, { envToken: ENV });
|
|
77
40
|
```
|
|
78
41
|
|
|
79
|
-
The `
|
|
42
|
+
The worker exposes `fetch`, `queue`, and `scheduled`. Its first event builds an
|
|
43
|
+
application with that event's environment. Concurrent events for the same
|
|
44
|
+
environment object share construction. Different environment objects receive
|
|
45
|
+
separate applications, including separate providers, lifecycle state, and live
|
|
46
|
+
drivers. A failed construction is evicted and the next event retries.
|
|
80
47
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
48
|
+
When module configuration itself needs bindings, pass `{ create: (env) => AppModule }`
|
|
49
|
+
instead of a static class. The callback receives the native environment inferred
|
|
50
|
+
from `envToken` and runs once per successful environment bootstrap. The same form
|
|
51
|
+
works with `VelaWebSocketDurableObject` for authenticated live gateways. See the
|
|
52
|
+
[complete API starter](../../apps/api-starter/README.md) for D1, Better Auth, CRUD,
|
|
53
|
+
the generated Hono client, live updates, and Studio inspection in one application.
|
|
85
54
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
55
|
+
The cache uses weak object keys: it does not permanently retain replaced
|
|
56
|
+
environments or secrets. Providers with request scope still rebuild per HTTP
|
|
57
|
+
request or queue/cron dispatch. Do not retain request objects or authentication
|
|
58
|
+
state in singleton providers.
|
|
90
59
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Each module follows the same pattern: `XModule.forRoot({ binding: 'NAME' })` returns a dynamic module that provides a service wrapping the Cloudflare binding.
|
|
94
|
-
|
|
95
|
-
| Module | Service | Cloudflare Binding |
|
|
96
|
-
|--------|---------|-------------------|
|
|
97
|
-
| `KVModule` | `KVService` | KV Namespace |
|
|
98
|
-
| `D1Module` | `D1Service` | D1 Database |
|
|
99
|
-
| `R2Module` | `R2Service` | R2 Bucket |
|
|
100
|
-
| `QueueModule` | `QueueService` | Queue (producer) |
|
|
101
|
-
| `DurableObjectModule` | `DurableObjectService` | Durable Object Namespace |
|
|
102
|
-
| `AIModule` | `AIService` | Workers AI |
|
|
103
|
-
| `VectorizeModule` | `VectorizeService` | Vectorize Index |
|
|
104
|
-
| `HyperdriveModule` | `HyperdriveService` | Hyperdrive |
|
|
105
|
-
|
|
106
|
-
### KVModule
|
|
60
|
+
For explicit construction inside a platform event:
|
|
107
61
|
|
|
108
62
|
```ts
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
async remove(key: string) { return this.kv.namespace.delete(key); }
|
|
121
|
-
async keys() { return this.kv.namespace.list(); }
|
|
122
|
-
}
|
|
63
|
+
const app = await createCloudflareApp(AppModule, {
|
|
64
|
+
env,
|
|
65
|
+
envToken: ENV,
|
|
66
|
+
globalPrefix: '/api',
|
|
67
|
+
middleware: (bindings) => [async (context, next) => {
|
|
68
|
+
context.header('x-service', bindings.SERVICE_NAME);
|
|
69
|
+
await next();
|
|
70
|
+
}],
|
|
71
|
+
});
|
|
72
|
+
const bindings = app.get(ENV); // WorkerEnv, inferred from ENV
|
|
73
|
+
return app.fetch(request, env, executionContext);
|
|
123
74
|
```
|
|
124
75
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
@Injectable()
|
|
134
|
-
class PostService {
|
|
135
|
-
constructor(private d1: D1Service) {}
|
|
76
|
+
`env` is registered before provider factories and lifecycle hooks. Referencing a
|
|
77
|
+
binding inside `middleware(env)` is typed from that same token; request callbacks
|
|
78
|
+
capture the native environment without retyping Hono's context. Referencing a
|
|
79
|
+
binding is safe during construction; platform I/O must still happen inside a
|
|
80
|
+
Workers event or Durable Object context. An explicitly built application rejects
|
|
81
|
+
requests or events carrying another environment object, including calls through
|
|
82
|
+
the underlying Hono app. Internal `ctx.run` reentry retains the application's
|
|
83
|
+
environment.
|
|
136
84
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
async create(title: string) {
|
|
142
|
-
return this.d1.database.prepare('INSERT INTO posts (title) VALUES (?)').bind(title).run();
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
### R2Module
|
|
148
|
-
|
|
149
|
-
```ts
|
|
150
|
-
import { R2Module, R2Service } from '@velajs/cloudflare';
|
|
151
|
-
|
|
152
|
-
@Module({ imports: [R2Module.forRoot({ binding: 'ASSETS' })] })
|
|
153
|
-
class AppModule {}
|
|
154
|
-
|
|
155
|
-
@Injectable()
|
|
156
|
-
class StorageService {
|
|
157
|
-
constructor(private r2: R2Service) {}
|
|
158
|
-
|
|
159
|
-
async upload(key: string, data: string) { return this.r2.bucket.put(key, data); }
|
|
160
|
-
async download(key: string) { return this.r2.bucket.get(key); }
|
|
161
|
-
async remove(key: string) { return this.r2.bucket.delete(key); }
|
|
162
|
-
}
|
|
163
|
-
```
|
|
164
|
-
|
|
165
|
-
### QueueModule
|
|
166
|
-
|
|
167
|
-
```ts
|
|
168
|
-
import { QueueModule, QueueService } from '@velajs/cloudflare';
|
|
169
|
-
|
|
170
|
-
@Module({ imports: [QueueModule.forRoot({ binding: 'EMAIL_QUEUE' })] })
|
|
171
|
-
class AppModule {}
|
|
172
|
-
|
|
173
|
-
@Injectable()
|
|
174
|
-
class NotificationService {
|
|
175
|
-
constructor(private queue: QueueService) {}
|
|
176
|
-
|
|
177
|
-
async sendEmail(to: string, subject: string) {
|
|
178
|
-
await this.queue.queue.send({ to, subject });
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### DurableObjectModule
|
|
184
|
-
|
|
185
|
-
```ts
|
|
186
|
-
import { DurableObjectModule, DurableObjectService } from '@velajs/cloudflare';
|
|
187
|
-
|
|
188
|
-
@Module({ imports: [DurableObjectModule.forRoot({ binding: 'COUNTER' })] })
|
|
189
|
-
class AppModule {}
|
|
190
|
-
|
|
191
|
-
@Injectable()
|
|
192
|
-
class CounterService {
|
|
193
|
-
constructor(private doNs: DurableObjectService) {}
|
|
85
|
+
`cloudflareAdapter({ env, envToken })` provides the same bootstrap and request
|
|
86
|
+
contract when composing `VelaFactory.create` directly.
|
|
194
87
|
|
|
195
|
-
|
|
196
|
-
const id = this.doNs.namespace.idFromName(name);
|
|
197
|
-
const stub = this.doNs.namespace.get(id);
|
|
198
|
-
return (stub as any).fetch('/increment');
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
```
|
|
88
|
+
## Typed provider factories
|
|
202
89
|
|
|
203
|
-
|
|
90
|
+
Bindings retain their full native API and generic parameters. There are no
|
|
91
|
+
binding-name wrappers to initialize or cast.
|
|
204
92
|
|
|
205
93
|
```ts
|
|
206
|
-
import {
|
|
94
|
+
import { defineProvider, InjectionToken, Module } from '@velajs/vela';
|
|
207
95
|
|
|
208
|
-
|
|
209
|
-
class AppModule {}
|
|
96
|
+
const TASK_QUEUE = new InjectionToken<Queue<{ taskId: string }>>('task queue');
|
|
210
97
|
|
|
211
|
-
@
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
}
|
|
220
|
-
}
|
|
98
|
+
@Module({
|
|
99
|
+
providers: [defineProvider(TASK_QUEUE, {
|
|
100
|
+
inject: [ENV],
|
|
101
|
+
useFactory: (env) => env.JOBS,
|
|
102
|
+
})],
|
|
103
|
+
exports: [TASK_QUEUE],
|
|
104
|
+
})
|
|
105
|
+
class JobsModule {}
|
|
221
106
|
```
|
|
222
107
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
import { VectorizeModule, VectorizeService } from '@velajs/cloudflare';
|
|
227
|
-
|
|
228
|
-
@Module({ imports: [VectorizeModule.forRoot({ binding: 'EMBEDDINGS' })] })
|
|
229
|
-
class AppModule {}
|
|
108
|
+
Every `useFactory` strategy declares its dependencies with `inject`, including
|
|
109
|
+
`inject: []` for factories without dependencies. This also applies to
|
|
110
|
+
`lazyProvider` and `forRootAsync` factory options.
|
|
230
111
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
112
|
+
Use native `env.DB`, `env.CACHE`, `env.FILES`, `env.JOBS`, `env.AI`,
|
|
113
|
+
`env.VECTORIZE`, or `env.HYPERDRIVE` directly. `@Env()` remains available for HTTP
|
|
114
|
+
handler parameters; typed token injection also works outside HTTP.
|
|
234
115
|
|
|
235
|
-
|
|
236
|
-
return this.vectorize.index.query(vector, { topK: 10 });
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
async addVectors(vectors: unknown[]) {
|
|
240
|
-
return this.vectorize.index.upsert(vectors);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### HyperdriveModule
|
|
116
|
+
## Queues and cron
|
|
246
117
|
|
|
247
118
|
```ts
|
|
248
|
-
import {
|
|
249
|
-
|
|
250
|
-
@Module({ imports: [HyperdriveModule.forRoot({ binding: 'POSTGRES' })] })
|
|
251
|
-
class AppModule {}
|
|
119
|
+
import { Inject, Injectable } from '@velajs/vela';
|
|
120
|
+
import { QueueConsumer, Scheduled } from '@velajs/cloudflare';
|
|
252
121
|
|
|
253
122
|
@Injectable()
|
|
254
|
-
class
|
|
255
|
-
constructor(private
|
|
123
|
+
class Jobs {
|
|
124
|
+
constructor(@Inject(ENV) private readonly env: WorkerEnv) {}
|
|
256
125
|
|
|
257
|
-
|
|
258
|
-
|
|
126
|
+
@Scheduled('0 * * * *')
|
|
127
|
+
async refresh() {
|
|
128
|
+
await this.env.CACHE.put('last-refresh', new Date().toISOString());
|
|
259
129
|
}
|
|
260
130
|
|
|
261
|
-
|
|
262
|
-
|
|
131
|
+
@QueueConsumer('jobs')
|
|
132
|
+
async consume(batch: MessageBatch<unknown>) {
|
|
133
|
+
for (const message of batch.messages) {
|
|
134
|
+
// Validate message.body before interpreting its application shape.
|
|
135
|
+
}
|
|
263
136
|
}
|
|
264
137
|
}
|
|
265
138
|
```
|
|
266
139
|
|
|
267
|
-
|
|
140
|
+
Core `@Cron()` also runs on Workers scheduled triggers. Consumers use fresh
|
|
141
|
+
request scopes and their declared guards, interceptors, and filters. Unclaimed
|
|
142
|
+
errors propagate to the platform for retry. Cold queue and cron events have the
|
|
143
|
+
same native bindings and live invalidation capabilities as HTTP.
|
|
268
144
|
|
|
269
|
-
|
|
145
|
+
## WebSockets, live queries, and Durable Objects
|
|
270
146
|
|
|
271
|
-
|
|
147
|
+
Use the native Durable Object entrypoint only in your Worker entry file:
|
|
272
148
|
|
|
273
149
|
```ts
|
|
274
|
-
import {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### @Scheduled()
|
|
150
|
+
import { InjectionToken, Module } from '@velajs/vela';
|
|
151
|
+
import { LiveModule } from '@velajs/vela/live';
|
|
152
|
+
import {
|
|
153
|
+
CloudflareWebSocketModule,
|
|
154
|
+
createCloudflareWorker,
|
|
155
|
+
durableObjectCursorLog,
|
|
156
|
+
durableObjectLive,
|
|
157
|
+
} from '@velajs/cloudflare';
|
|
158
|
+
import { VelaWebSocketDurableObject } from '@velajs/cloudflare/durable-objects';
|
|
159
|
+
|
|
160
|
+
interface RoomEnv { ROOMS: DurableObjectNamespace<Room> }
|
|
161
|
+
const ROOM_ENV = new InjectionToken<RoomEnv>('room environment');
|
|
291
162
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
### @QueueConsumer()
|
|
312
|
-
|
|
313
|
-
Method decorator for queue consumer handlers.
|
|
314
|
-
|
|
315
|
-
```ts
|
|
316
|
-
import { QueueConsumer } from '@velajs/cloudflare';
|
|
163
|
+
@Module({
|
|
164
|
+
imports: [
|
|
165
|
+
CloudflareWebSocketModule.forRoot(),
|
|
166
|
+
LiveModule.forRootAsync({
|
|
167
|
+
inject: [ROOM_ENV],
|
|
168
|
+
useFactory: (env) => ({
|
|
169
|
+
driver: () => durableObjectLive({
|
|
170
|
+
namespace: env.ROOMS,
|
|
171
|
+
gatewayPath: '/rooms/:room/ws',
|
|
172
|
+
}),
|
|
173
|
+
log: () => durableObjectCursorLog(),
|
|
174
|
+
}),
|
|
175
|
+
}),
|
|
176
|
+
],
|
|
177
|
+
// Add your @WebSocketGateway and @LiveResolver classes here.
|
|
178
|
+
providers: [],
|
|
179
|
+
})
|
|
180
|
+
class RoomModule {}
|
|
317
181
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
@QueueConsumer('email-queue')
|
|
321
|
-
async process(batch: MessageBatch) {
|
|
322
|
-
for (const msg of batch.messages) {
|
|
323
|
-
console.log('Sending email:', msg.body);
|
|
324
|
-
msg.ack();
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
182
|
+
export class Room extends VelaWebSocketDurableObject(RoomModule, { envToken: ROOM_ENV }) {}
|
|
183
|
+
export default createCloudflareWorker(RoomModule, { envToken: ROOM_ENV });
|
|
328
184
|
```
|
|
329
185
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
import { createCloudflareApp } from '@velajs/cloudflare';
|
|
336
|
-
|
|
337
|
-
const app = await createCloudflareApp(AppModule);
|
|
338
|
-
export default app;
|
|
339
|
-
```
|
|
186
|
+
Declare gateways with `@WebSocketGateway({ path, roomParam, binding, ... })` and
|
|
187
|
+
configure origins and upgrade authentication for your application. Upgrade
|
|
188
|
+
routing consumes the core trusted request identity, checks conflicts with the
|
|
189
|
+
upgrade credential, and forwards issuer, subject, tenant, and expiry to the DO.
|
|
190
|
+
Client-supplied internal identity headers are stripped before authorization.
|
|
340
191
|
|
|
341
|
-
|
|
192
|
+
Declare live query argument and result schemas once with `defineLiveQuery({ args,
|
|
193
|
+
result })` from `@velajs/vela/live`. Both entries accept a parser object such as a
|
|
194
|
+
Zod schema. Use `@LiveQuery('todos.list', definition, { tags: ['todos'] })` on the
|
|
195
|
+
resolver and share the definition with its client. Restored hibernation arguments
|
|
196
|
+
and final query results use the same validation boundary.
|
|
342
197
|
|
|
343
|
-
|
|
198
|
+
Each application constructs its own driver and cursor log. Workers send
|
|
199
|
+
invalidations through the typed DO namespace; the DO uses its own live engine
|
|
200
|
+
and SQLite cursor log. Configure the class in Wrangler `new_sqlite_classes` to
|
|
201
|
+
retain cursor/epoch state across hibernation. Hibernated subscriptions are
|
|
202
|
+
restored on wake; the shared live protocol handles resume or snapshot fallback.
|
|
344
203
|
|
|
345
|
-
|
|
346
|
-
|
|
204
|
+
The DO class preserves its RPC types, so `DurableObjectNamespace<Room>` exposes
|
|
205
|
+
`invalidate`, `broadcast`, and PITR methods without assertions. `broadcastToRoom`,
|
|
206
|
+
`liveInvalidateToRoom`, and the PITR helpers remain available from the root
|
|
207
|
+
package. `VelaNonceDurableObject` is exported from `/durable-objects`; its
|
|
208
|
+
`durableObjectNonceStore` factory remains on the root entrypoint.
|
|
347
209
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
scheduled: app.scheduled.bind(app),
|
|
351
|
-
queue: app.queue.bind(app),
|
|
352
|
-
};
|
|
353
|
-
```
|
|
210
|
+
The root package contains no runtime `cloudflare:workers` import and can be
|
|
211
|
+
loaded by Node tooling. Native classes belong to `/durable-objects`.
|
|
354
212
|
|
|
355
|
-
##
|
|
213
|
+
## R2 storage and caches
|
|
356
214
|
|
|
357
|
-
|
|
215
|
+
Configure named disks from an async factory using actual bucket values:
|
|
358
216
|
|
|
359
217
|
```ts
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
218
|
+
StorageModule.forRootAsync({
|
|
219
|
+
inject: [ENV],
|
|
220
|
+
useFactory: (env) => ({
|
|
221
|
+
defaultDisk: 'uploads',
|
|
222
|
+
secret: env.APP_SECRET,
|
|
223
|
+
disks: [{ disk: 'uploads', bucket: env.FILES, root: 'uploads/{year}' }],
|
|
224
|
+
presignedUrl: { defaultExpiry: 3600, maxExpiry: 86400 },
|
|
225
|
+
}),
|
|
226
|
+
});
|
|
368
227
|
```
|
|
369
228
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
`
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
229
|
+
`StorageService` supports upload, download, delete, existence checks, and expiring
|
|
230
|
+
signed download URLs. The proxy validates signatures, HTTP method, expiry, and
|
|
231
|
+
the configured root; returned files download as attachments.
|
|
232
|
+
|
|
233
|
+
Construct `KVCacheStore` and `KvFlagDriver` with a native namespace:
|
|
234
|
+
`new KVCacheStore(env.CACHE)` and `new KvFlagDriver(env.CACHE)`. Cache reads and
|
|
235
|
+
object-valued flag reads return `unknown`; validate them with an application
|
|
236
|
+
parser. Core `CacheService.getParsed(key, parser)` infers the result from that
|
|
237
|
+
parser. Memory and tiered cache reads use the same unknown-value contract.
|
|
238
|
+
|
|
239
|
+
## Migration
|
|
240
|
+
|
|
241
|
+
This release intentionally changes the APIs:
|
|
242
|
+
|
|
243
|
+
- Replace `BindingRef`, `EnvRef`, `EnvModule`, `EnvService`, all per-binding
|
|
244
|
+
modules, and all per-binding services with a typed `InjectionToken<Env>`.
|
|
245
|
+
- Pass `{ env, envToken }` to `createCloudflareApp`, or export
|
|
246
|
+
`createCloudflareWorker(AppModule, { envToken })`.
|
|
247
|
+
- Use `app.get(TOKEN)` without a caller-selected generic.
|
|
248
|
+
- Replace storage disk `binding: 'FILES'` with `bucket: env.FILES`, and pass the
|
|
249
|
+
storage signing secret explicitly through module options.
|
|
250
|
+
- Build live `driver` and `log` through per-application factories. Pass a typed
|
|
251
|
+
`namespace` to `durableObjectLive` instead of a binding name.
|
|
252
|
+
- Import native Durable Object classes from `@velajs/cloudflare/durable-objects`
|
|
253
|
+
and pass `{ envToken }` to `VelaWebSocketDurableObject`.
|
|
254
|
+
- Cache `get<T>` was removed. Use raw `unknown` reads and a parser.
|
|
255
|
+
- Email and workflow integrations are outside this API package. Their deferred
|
|
256
|
+
standalone packages are not required to import, build, test, or install it.
|
|
257
|
+
|
|
258
|
+
Use `pnpm --dir cloudflare test`, `pnpm --dir cloudflare test:workers`, and
|
|
259
|
+
`pnpm --dir cloudflare typecheck` from the shared API workspace. The Workers suite
|
|
260
|
+
uses real KV, D1, R2, WebSockets, SQLite Durable Objects, and cold event dispatch.
|