@velajs/cloudflare 1.22.1 → 1.24.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 +64 -0
- package/README.md +50 -22
- package/dist/durable-objects.d.ts +1 -1
- package/dist/durable-objects.js +40 -21
- package/dist/durable-objects.js.map +1 -1
- package/dist/index.d.ts +45 -18
- package/dist/index.js +161 -70
- package/dist/index.js.map +1 -1
- package/dist/{nonce-validation-LE0vFk-7.js → nonce-validation-Bcqf3FvY.js} +106 -38
- package/dist/nonce-validation-Bcqf3FvY.js.map +1 -0
- package/dist/queue.d.ts +26 -0
- package/dist/queue.js +41 -0
- package/dist/queue.js.map +1 -0
- package/package.json +10 -6
- package/dist/nonce-validation-LE0vFk-7.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as DoPitrId, c as DoPitrUnavailableError, d as isDoPitrUnavailable, f as readDoPitrBookmark, i as DoPitrBookmarkRead, l as VelaDoPitrRpc, n as DoPitrArmOptions, o as DoPitrNamespace, p as CloudflareRoot, r as DoPitrArmResult, s as DoPitrStorage, t as VelaNonceDurableObject, u as armDoPitr } from "./nonce.durable-object-Df3_42Sy.js";
|
|
2
|
-
import { AsyncCacheStore, DynamicModule, InjectionToken, NonceStore, RuntimeAdapter, ThrottlerStore, Type, VelaApplication, VelaMiddlewareHandler, VelaSecurityOptions } from "@velajs/vela";
|
|
3
|
-
import { BroadcastCommand, ConnectedSocket, MessageBody, OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, WebSocketGatewayOptions, WebSocketServer, WsClient, WsDispatcher, WsException, WsMessage, WsResponse, WsServer } from "@velajs/vela/websocket";
|
|
2
|
+
import { AsyncCacheStore, CacheEntry, CacheEntryReader, CacheEntryWriter, CacheInvalidationStore, DynamicModule, InjectionToken, NonceStore, RuntimeAdapter, ThrottlerStore, Type, VelaApplication, VelaMiddlewareHandler, VelaSecurityOptions } from "@velajs/vela";
|
|
3
|
+
import { BroadcastCommand, ConnectedSocket, MessageBody, OnGatewayConnection, OnGatewayDisconnect, OnGatewayInit, SubscribeMessage, WebSocketGateway, WebSocketGatewayOptions, WebSocketSendGate, WebSocketServer, WsClient, WsDispatcher, WsException, WsMessage, WsResponse, WsServer } from "@velajs/vela/websocket";
|
|
4
4
|
import { DownloadResult, PresignMethod, PresignedUrlResult, StorageBody, StorageDriver, UploadOptions, UploadResult } from "@velajs/vela/storage";
|
|
5
5
|
import { CommitStamp, CursorLog, InvalidationCommand, LiveDriver, LiveEngine, LiveInvalidationSink, ResumeVerdict } from "@velajs/vela/live";
|
|
6
6
|
import { Context, ExecutionContext } from "hono";
|
|
@@ -55,9 +55,8 @@ type MountOpenApiOptions = Parameters<VelaApplication['mountOpenApi']>[0];
|
|
|
55
55
|
* ```
|
|
56
56
|
*/
|
|
57
57
|
export declare class CloudflareApplication<T extends object = object> {
|
|
58
|
-
private
|
|
58
|
+
#private;
|
|
59
59
|
readonly env: T;
|
|
60
|
-
private wsGatewayRoutes;
|
|
61
60
|
constructor(app: VelaApplication, env: T);
|
|
62
61
|
readonly fetch: (request: Request, env: T, ctx?: ExecutionContext) => Promise<Response>;
|
|
63
62
|
getHonoApp(): ReturnType<VelaApplication['getHonoApp']>;
|
|
@@ -97,13 +96,12 @@ export declare class CloudflareApplication<T extends object = object> {
|
|
|
97
96
|
*/
|
|
98
97
|
mountOpenApi(options: MountOpenApiOptions): this;
|
|
99
98
|
/**
|
|
100
|
-
* @internal
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* kinds) at dispatch time.
|
|
99
|
+
* @internal Upgrade routes come from validated gateway entrypoints, including
|
|
100
|
+
* request-scoped gateways without a bootstrap instance. Retain the instance
|
|
101
|
+
* scan for legacy applications that only declare forwarding metadata.
|
|
104
102
|
*/
|
|
105
103
|
scanInstances(instances: unknown[]): void;
|
|
106
|
-
/** @internal — upgrade routes discovered from
|
|
104
|
+
/** @internal — upgrade routes discovered from the application's gateways. */
|
|
107
105
|
getWsGatewayRoutes(): WsGatewayRoute[];
|
|
108
106
|
/**
|
|
109
107
|
* Handle Cloudflare scheduled (cron) events.
|
|
@@ -273,22 +271,36 @@ export declare const STORAGE_OPTIONS: InjectionToken<StorageModuleOptions>;
|
|
|
273
271
|
//#endregion
|
|
274
272
|
//#region src/services/kv-cache.store.d.ts
|
|
275
273
|
/**
|
|
276
|
-
*
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* Reads return unknown JSON; validate values at the consuming boundary.
|
|
280
|
-
*
|
|
281
|
-
* Note: Cloudflare KV requires `expirationTtl >= 60s`, so sub-minute TTLs are
|
|
282
|
-
* clamped up. Keep short TTLs on the memory tier; use KV for longer-lived entries.
|
|
274
|
+
* Native KV JSON value store. Metadata retains logical expiry even when KV's
|
|
275
|
+
* physical retention rounds up to its 60-second minimum. Legacy values without
|
|
276
|
+
* metadata remain readable, but cannot safely backfill another tier.
|
|
283
277
|
*/
|
|
284
|
-
export declare class KVCacheStore implements AsyncCacheStore {
|
|
278
|
+
export declare class KVCacheStore implements AsyncCacheStore, CacheEntryReader, CacheEntryWriter {
|
|
285
279
|
private readonly ns;
|
|
286
280
|
constructor(ns: KVNamespace);
|
|
287
281
|
get(key: string): Promise<unknown>;
|
|
282
|
+
getEntry(key: string): Promise<{
|
|
283
|
+
value: unknown;
|
|
284
|
+
expiresAt?: number;
|
|
285
|
+
} | undefined>;
|
|
288
286
|
set(key: string, value: unknown, ttl?: number): Promise<void>;
|
|
287
|
+
setEntry(key: string, entry: CacheEntry): Promise<void>;
|
|
289
288
|
del(key: string): Promise<void>;
|
|
289
|
+
/** Namespace-wide, best effort. Use a dedicated value namespace; never use for scoped invalidation. */
|
|
290
290
|
clear(): Promise<void>;
|
|
291
291
|
}
|
|
292
|
+
/**
|
|
293
|
+
* Optional, eventually consistent generation store. Use a dedicated KV namespace
|
|
294
|
+
* without TTL/lifecycle cleanup. Never delete/reset generations while entries can
|
|
295
|
+
* survive. Concurrent writes and cached/negative reads prevent strong invalidation;
|
|
296
|
+
* this is unsuitable for strict read-after-write or authorization revocation.
|
|
297
|
+
*/
|
|
298
|
+
export declare class KVCacheInvalidationStore implements CacheInvalidationStore {
|
|
299
|
+
private readonly ns;
|
|
300
|
+
constructor(ns: KVNamespace);
|
|
301
|
+
getVersion(key: string): Promise<string>;
|
|
302
|
+
invalidate(key: string): Promise<void>;
|
|
303
|
+
}
|
|
292
304
|
//#endregion
|
|
293
305
|
//#region src/services/flagship-flag.driver.d.ts
|
|
294
306
|
/**
|
|
@@ -414,6 +426,21 @@ interface ScheduledMetadata {
|
|
|
414
426
|
cron: string;
|
|
415
427
|
methodName: string;
|
|
416
428
|
}
|
|
429
|
+
/** Compatible with the existing programmatic scheduled() entrypoint. */
|
|
430
|
+
interface ScheduledEvent {
|
|
431
|
+
readonly cron: string;
|
|
432
|
+
readonly scheduledTime?: number;
|
|
433
|
+
}
|
|
434
|
+
/** Native controller passed unchanged to a Worker handler. Call noRetry on its receiver. */
|
|
435
|
+
interface ScheduledController extends ScheduledEvent {
|
|
436
|
+
readonly scheduledTime: number;
|
|
437
|
+
noRetry(): void;
|
|
438
|
+
}
|
|
439
|
+
interface ScheduledContext {
|
|
440
|
+
waitUntil(promise: Promise<unknown>): void;
|
|
441
|
+
}
|
|
442
|
+
type ScheduledHandler<Env extends object = object> = (controller: ScheduledController, env: Env, context: ScheduledContext) => void | Promise<void>;
|
|
443
|
+
export declare function parseScheduledMetadata(value: unknown): ScheduledMetadata;
|
|
417
444
|
/**
|
|
418
445
|
* Marks a method as a scheduled (cron) handler.
|
|
419
446
|
*
|
|
@@ -618,5 +645,5 @@ interface DurableObjectNonceStoreOptions {
|
|
|
618
645
|
*/
|
|
619
646
|
export declare function durableObjectNonceStore(options: DurableObjectNonceStoreOptions): NonceStore;
|
|
620
647
|
//#endregion
|
|
621
|
-
export { type BroadcastNamespace, type CfLiveDriver, type CloudflareRateLimitBinding, type CloudflareRateLimitStoreOptions, type CloudflareRoot, type CloudflareWorkerOptions, ConnectedSocket, type CreateCloudflareAppOptions, type DiskConfig, type DoPitrArmOptions, type DoPitrArmResult, type DoPitrBookmarkRead, type DoPitrId, type DoPitrNamespace, type DoPitrStorage, DoPitrUnavailableError, type DurableObjectLiveOptions, type DurableObjectNonceNamespace, type DurableObjectNonceStoreOptions, type FlagshipBinding, type FlagshipFlagDriverOptions, type KvFlagDriverOptions, type LiveNamespace, MessageBody, type MountOpenApiOptions, type OnGatewayConnection, type OnGatewayDisconnect, type OnGatewayInit, type PresignedUrlConfig, type QueueConsumerMetadata, type ScheduledMetadata, type StorageModuleOptions, SubscribeMessage, type VelaDoPitrRpc, WebSocketGateway, WebSocketServer, type WsClient, WsException, type WsGatewayRoute, type WsMessage, type WsResponse, type WsServer, armDoPitr, isDoPitrUnavailable, readDoPitrBookmark };
|
|
648
|
+
export { type BroadcastNamespace, type CfLiveDriver, type CloudflareRateLimitBinding, type CloudflareRateLimitStoreOptions, type CloudflareRoot, type CloudflareWorkerOptions, ConnectedSocket, type CreateCloudflareAppOptions, type DiskConfig, type DoPitrArmOptions, type DoPitrArmResult, type DoPitrBookmarkRead, type DoPitrId, type DoPitrNamespace, type DoPitrStorage, DoPitrUnavailableError, type DurableObjectLiveOptions, type DurableObjectNonceNamespace, type DurableObjectNonceStoreOptions, type FlagshipBinding, type FlagshipFlagDriverOptions, type KvFlagDriverOptions, type LiveNamespace, MessageBody, type MountOpenApiOptions, type OnGatewayConnection, type OnGatewayDisconnect, type OnGatewayInit, type PresignedUrlConfig, type QueueConsumerMetadata, type ScheduledContext, type ScheduledController, type ScheduledEvent, type ScheduledHandler, type ScheduledMetadata, type StorageModuleOptions, SubscribeMessage, type VelaDoPitrRpc, WebSocketGateway, WebSocketServer, type WsClient, WsException, type WsGatewayRoute, type WsMessage, type WsResponse, type WsServer, armDoPitr, isDoPitrUnavailable, readDoPitrBookmark };
|
|
622
649
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as durableObjectRoomName, T as roomToDurableId, _ as readDoPitrBookmark, a as durableObjectLive, b as assertCloudflareEnvironment, c as liveInvalidateToRoom, g as isDoPitrUnavailable, h as armDoPitr, i as durableObjectCursorLog, m as DoPitrUnavailableError, n as isValidExpiry, p as WsServerHolder, r as DoCursorLog, t as isCanonicalBoundedText, v as __decorate, x as registerCloudflareEnvironment, y as resolveCloudflareRoot } from "./nonce-validation-Bcqf3FvY.js";
|
|
2
2
|
import { getConnInfo } from "hono/cloudflare-workers";
|
|
3
|
-
import { CRON_METADATA, ConfigurableModuleBuilder, Controller, Get, Inject, Injectable, InjectionToken, Module, PipelineRunner, Req, VelaFactory, buildEntrypointExecutionContext, createParamDecorator, defineMetadata, defineProvider, getMetadata, getTrustedRequestIdentity, registerEntrypointKind, runInEntrypointScope, shouldFilterCatch } from "@velajs/vela";
|
|
4
|
-
import {
|
|
5
|
-
import { ConnectedSocket, DEFAULT_WS_MAX_FRAME_BYTES, MessageBody, SubscribeMessage, WS_GATEWAY_METADATA, WS_SERVER, WebSocketGateway, WebSocketServer, WsDispatcher, WsException, assertBroadcastCommandFits, authenticateWebSocketUpgrade, resolveGatewayRoomId, resolveGatewayRoomParam, resolveMaxFrameBytes } from "@velajs/vela/websocket";
|
|
3
|
+
import { CRON_METADATA, ConfigurableModuleBuilder, Controller, Get, Inject, Injectable, InjectionToken, Module, PipelineRunner, Req, VelaFactory, buildEntrypointExecutionContext, createParamDecorator, defineMetadata, defineProvider, getEntrypointModuleId, getMetadata, getTrustedRequestIdentity, parseCron, registerEntrypointKind, resolveEntrypoint, resolveErrorReporter, resolveScopedComponentsAsync, runInEntrypointScope, shouldFilterCatch } from "@velajs/vela";
|
|
4
|
+
import { ConnectedSocket, DEFAULT_WS_MAX_FRAME_BYTES, MessageBody, SubscribeMessage, WS_GATEWAY_METADATA, WS_SERVER, WebSocketGateway, WebSocketServer, WsDispatcher, WsException, assertBroadcastCommandFits, authenticateWebSocketUpgrade, readWsEntrypointMeta, resolveGatewayRoomId, resolveGatewayRoomParam, resolveMaxFrameBytes } from "@velajs/vela/websocket";
|
|
6
5
|
import { STORAGE_SIGNED_URL_PURPOSE, joinStoragePath, signUrl, verifySignedUrl } from "@velajs/vela/storage";
|
|
7
6
|
//#region src/websocket/websocket-routing.ts
|
|
8
7
|
const MAX_IDENTITY_FIELD_BYTES = 2048;
|
|
@@ -123,7 +122,7 @@ registerEntrypointKind({
|
|
|
123
122
|
});
|
|
124
123
|
function invoke(instance, methodName, args) {
|
|
125
124
|
const method = Reflect.get(instance, methodName);
|
|
126
|
-
if (typeof method !== "function") throw new Error(`Method '${methodName}' is not a function on ${instance.constructor.name}`);
|
|
125
|
+
if (typeof method !== "function") throw new Error(`Method '${String(methodName)}' is not a function on ${instance.constructor.name}`);
|
|
127
126
|
return Reflect.apply(method, instance, args);
|
|
128
127
|
}
|
|
129
128
|
function entrypointString(meta, property) {
|
|
@@ -132,6 +131,12 @@ function entrypointString(meta, property) {
|
|
|
132
131
|
if (typeof value !== "string") throw new Error(`Invalid entrypoint metadata: ${property} must be a string.`);
|
|
133
132
|
return value;
|
|
134
133
|
}
|
|
134
|
+
/** Wait for every matching handler, even when one fails before its siblings. */
|
|
135
|
+
async function settleEntrypoints(work) {
|
|
136
|
+
const errors = (await Promise.allSettled(work)).flatMap((outcome) => outcome.status === "rejected" ? [outcome.reason] : []);
|
|
137
|
+
if (errors.length === 1) throw errors[0];
|
|
138
|
+
if (errors.length > 1) throw new AggregateError(errors, "Multiple entrypoint handlers failed.");
|
|
139
|
+
}
|
|
135
140
|
/**
|
|
136
141
|
* Wraps VelaApplication with Cloudflare-specific handlers:
|
|
137
142
|
* - `fetch` — HTTP request handler (from Hono)
|
|
@@ -162,20 +167,20 @@ function entrypointString(meta, property) {
|
|
|
162
167
|
* ```
|
|
163
168
|
*/
|
|
164
169
|
var CloudflareApplication = class {
|
|
165
|
-
app;
|
|
166
170
|
env;
|
|
167
|
-
wsGatewayRoutes = [];
|
|
171
|
+
#wsGatewayRoutes = [];
|
|
172
|
+
#app;
|
|
168
173
|
constructor(app, env) {
|
|
169
|
-
this.app = app;
|
|
170
174
|
this.env = env;
|
|
175
|
+
this.#app = app;
|
|
171
176
|
this.get = app.get.bind(app);
|
|
172
177
|
}
|
|
173
178
|
fetch = async (request, env, ctx) => {
|
|
174
179
|
assertCloudflareEnvironment(this.env, env);
|
|
175
|
-
return this
|
|
180
|
+
return this.#app.fetch(request, env, ctx);
|
|
176
181
|
};
|
|
177
182
|
getHonoApp() {
|
|
178
|
-
return this
|
|
183
|
+
return this.#app.getHonoApp();
|
|
179
184
|
}
|
|
180
185
|
/**
|
|
181
186
|
* Resolve a provider from the application's DI container (delegates to
|
|
@@ -191,7 +196,7 @@ var CloudflareApplication = class {
|
|
|
191
196
|
*/
|
|
192
197
|
get;
|
|
193
198
|
get entrypoints() {
|
|
194
|
-
return this
|
|
199
|
+
return this.#app.entrypoints;
|
|
195
200
|
}
|
|
196
201
|
/**
|
|
197
202
|
* Serve a pre-built OpenAPI document (and optionally a Scalar UI) on the
|
|
@@ -214,24 +219,34 @@ var CloudflareApplication = class {
|
|
|
214
219
|
* ```
|
|
215
220
|
*/
|
|
216
221
|
mountOpenApi(options) {
|
|
217
|
-
this
|
|
222
|
+
this.#app.mountOpenApi(options);
|
|
218
223
|
return this;
|
|
219
224
|
}
|
|
220
225
|
/**
|
|
221
|
-
* @internal
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
* kinds) at dispatch time.
|
|
226
|
+
* @internal Upgrade routes come from validated gateway entrypoints, including
|
|
227
|
+
* request-scoped gateways without a bootstrap instance. Retain the instance
|
|
228
|
+
* scan for legacy applications that only declare forwarding metadata.
|
|
225
229
|
*/
|
|
226
230
|
scanInstances(instances) {
|
|
231
|
+
const routes = /* @__PURE__ */ new Map();
|
|
232
|
+
for (const ep of this.#app.entrypoints.ofKind("websocket")) {
|
|
233
|
+
if (typeof ep.meta !== "object" || ep.meta === null || !("dispatcher" in ep.meta)) continue;
|
|
234
|
+
const meta = readWsEntrypointMeta(ep.meta);
|
|
235
|
+
if (meta.options.binding) routes.set(meta.path, {
|
|
236
|
+
path: meta.path,
|
|
237
|
+
binding: meta.options.binding,
|
|
238
|
+
options: { ...meta.options }
|
|
239
|
+
});
|
|
240
|
+
}
|
|
227
241
|
for (const instance of instances) {
|
|
228
242
|
if (!instance || typeof instance !== "object") continue;
|
|
229
|
-
|
|
243
|
+
for (const route of collectWsGatewayRoutes(instance)) if (!routes.has(route.path)) routes.set(route.path, route);
|
|
230
244
|
}
|
|
245
|
+
this.#wsGatewayRoutes.splice(0, this.#wsGatewayRoutes.length, ...routes.values());
|
|
231
246
|
}
|
|
232
|
-
/** @internal — upgrade routes discovered from
|
|
247
|
+
/** @internal — upgrade routes discovered from the application's gateways. */
|
|
233
248
|
getWsGatewayRoutes() {
|
|
234
|
-
return this
|
|
249
|
+
return [...this.#wsGatewayRoutes];
|
|
235
250
|
}
|
|
236
251
|
/**
|
|
237
252
|
* Handle Cloudflare scheduled (cron) events.
|
|
@@ -241,18 +256,13 @@ var CloudflareApplication = class {
|
|
|
241
256
|
*/
|
|
242
257
|
async scheduled(event, env, ctx) {
|
|
243
258
|
assertCloudflareEnvironment(this.env, env);
|
|
244
|
-
|
|
259
|
+
await settleEntrypoints([...this.#app.entrypoints.ofKind("cf:scheduled").map((ep) => ({
|
|
245
260
|
ep,
|
|
246
261
|
cron: entrypointString(ep.meta, "cron")
|
|
247
|
-
})), ...this
|
|
262
|
+
})), ...this.#app.entrypoints.ofKind("cf:vela-cron").map((ep) => ({
|
|
248
263
|
ep,
|
|
249
264
|
cron: entrypointString(ep.meta, "expression")
|
|
250
|
-
}))].filter((h) => h.cron === event.cron);
|
|
251
|
-
await Promise.all(handlers.map(({ ep }) => this.dispatchEntrypoint(ep, [
|
|
252
|
-
event,
|
|
253
|
-
env,
|
|
254
|
-
ctx
|
|
255
|
-
])));
|
|
265
|
+
}))].filter((h) => h.cron === event.cron).map(({ ep }) => this.dispatchEntrypoint(ep, event, env, ctx)));
|
|
256
266
|
}
|
|
257
267
|
/**
|
|
258
268
|
* Run one entrypoint handler inside a fresh request scope, through the
|
|
@@ -262,33 +272,61 @@ var CloudflareApplication = class {
|
|
|
262
272
|
* guard has no business rejecting a queue batch. Unclaimed errors rethrow
|
|
263
273
|
* so the platform's retry semantics stay intact.
|
|
264
274
|
*/
|
|
265
|
-
async dispatchEntrypoint(ep,
|
|
275
|
+
async dispatchEntrypoint(ep, payload, env, platformContext) {
|
|
266
276
|
const targetClass = ep.token;
|
|
267
277
|
if (typeof targetClass !== "function") throw new Error("Entrypoint token must be a class.");
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
await
|
|
287
|
-
|
|
278
|
+
if (ep.methodName === void 0) throw new Error("Entrypoint must declare a handler method.");
|
|
279
|
+
const methodName = ep.methodName;
|
|
280
|
+
const reportContext = {
|
|
281
|
+
edge: ep.kind === "cf:queue" ? "queue" : "schedule",
|
|
282
|
+
source: `${targetClass.name}.${String(methodName)}`
|
|
283
|
+
};
|
|
284
|
+
let reported;
|
|
285
|
+
try {
|
|
286
|
+
await runInEntrypointScope(this.#app.getContainer(), async (scope, lifetime) => {
|
|
287
|
+
const moduleId = getEntrypointModuleId(scope, ep);
|
|
288
|
+
const context = buildEntrypointExecutionContext(ep.kind, targetClass, methodName, payload, moduleId, scope);
|
|
289
|
+
const invocationContext = { waitUntil(promise) {
|
|
290
|
+
lifetime.waitUntil(promise);
|
|
291
|
+
platformContext.waitUntil(promise);
|
|
292
|
+
} };
|
|
293
|
+
let filters = [];
|
|
294
|
+
try {
|
|
295
|
+
filters = (await resolveScopedComponentsAsync("filter", targetClass, methodName, scope, moduleId)).toReversed();
|
|
296
|
+
const guards = await resolveScopedComponentsAsync("guard", targetClass, methodName, scope, moduleId);
|
|
297
|
+
const interceptors = await resolveScopedComponentsAsync("interceptor", targetClass, methodName, scope, moduleId);
|
|
298
|
+
await PipelineRunner.run({
|
|
299
|
+
context,
|
|
300
|
+
guards,
|
|
301
|
+
interceptors,
|
|
302
|
+
resolveArgs: async () => [
|
|
303
|
+
payload,
|
|
304
|
+
env,
|
|
305
|
+
invocationContext
|
|
306
|
+
],
|
|
307
|
+
invoke: async (args) => {
|
|
308
|
+
const instance = await resolveEntrypoint(scope, ep);
|
|
309
|
+
if (typeof instance !== "object" || instance === null) throw new Error("Entrypoint must resolve to an object.");
|
|
310
|
+
return invoke(instance, methodName, args);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
} catch (error) {
|
|
314
|
+
resolveErrorReporter(scope).report(error, reportContext);
|
|
315
|
+
for (const filter of filters) if (shouldFilterCatch(filter, error)) {
|
|
316
|
+
await filter.catch(error, context);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
reported = { error };
|
|
320
|
+
throw error;
|
|
288
321
|
}
|
|
289
|
-
|
|
322
|
+
});
|
|
323
|
+
} catch (error) {
|
|
324
|
+
if (!reported || reported.error !== error) {
|
|
325
|
+
const completionError = reported && error instanceof AggregateError && error.errors[0] === reported.error ? error.errors[1] : error;
|
|
326
|
+
resolveErrorReporter(this.#app.getContainer()).report(completionError, reportContext);
|
|
290
327
|
}
|
|
291
|
-
|
|
328
|
+
throw error;
|
|
329
|
+
}
|
|
292
330
|
}
|
|
293
331
|
/**
|
|
294
332
|
* Handle Cloudflare Queue consumer events.
|
|
@@ -298,15 +336,10 @@ var CloudflareApplication = class {
|
|
|
298
336
|
*/
|
|
299
337
|
async queue(batch, env, ctx) {
|
|
300
338
|
assertCloudflareEnvironment(this.env, env);
|
|
301
|
-
|
|
302
|
-
await Promise.all(handlers.map((ep) => this.dispatchEntrypoint(ep, [
|
|
303
|
-
batch,
|
|
304
|
-
env,
|
|
305
|
-
ctx
|
|
306
|
-
])));
|
|
339
|
+
await settleEntrypoints(this.#app.entrypoints.ofKind("cf:queue").filter((ep) => entrypointString(ep.meta, "queueName") === batch.queue).map((ep) => this.dispatchEntrypoint(ep, batch, env, ctx)));
|
|
307
340
|
}
|
|
308
341
|
async close(signal) {
|
|
309
|
-
return this
|
|
342
|
+
return this.#app.close(signal);
|
|
310
343
|
}
|
|
311
344
|
};
|
|
312
345
|
//#endregion
|
|
@@ -675,13 +708,9 @@ StorageModule = __decorate([Module({
|
|
|
675
708
|
//#endregion
|
|
676
709
|
//#region src/services/kv-cache.store.ts
|
|
677
710
|
/**
|
|
678
|
-
*
|
|
679
|
-
*
|
|
680
|
-
*
|
|
681
|
-
* Reads return unknown JSON; validate values at the consuming boundary.
|
|
682
|
-
*
|
|
683
|
-
* Note: Cloudflare KV requires `expirationTtl >= 60s`, so sub-minute TTLs are
|
|
684
|
-
* clamped up. Keep short TTLs on the memory tier; use KV for longer-lived entries.
|
|
711
|
+
* Native KV JSON value store. Metadata retains logical expiry even when KV's
|
|
712
|
+
* physical retention rounds up to its 60-second minimum. Legacy values without
|
|
713
|
+
* metadata remain readable, but cannot safely backfill another tier.
|
|
685
714
|
*/
|
|
686
715
|
var KVCacheStore = class {
|
|
687
716
|
ns;
|
|
@@ -689,16 +718,42 @@ var KVCacheStore = class {
|
|
|
689
718
|
this.ns = ns;
|
|
690
719
|
}
|
|
691
720
|
async get(key) {
|
|
692
|
-
|
|
693
|
-
|
|
721
|
+
return (await this.getEntry(key))?.value;
|
|
722
|
+
}
|
|
723
|
+
async getEntry(key) {
|
|
724
|
+
const { value, metadata } = await this.ns.getWithMetadata(key, "json");
|
|
725
|
+
if (value === null) return void 0;
|
|
726
|
+
if (typeof metadata === "object" && metadata !== null && "velaCacheExpiresAt" in metadata) {
|
|
727
|
+
const expiresAt = metadata.velaCacheExpiresAt;
|
|
728
|
+
if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt) || expiresAt <= Date.now()) return void 0;
|
|
729
|
+
return {
|
|
730
|
+
value,
|
|
731
|
+
expiresAt
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
return { value };
|
|
694
735
|
}
|
|
695
736
|
async set(key, value, ttl) {
|
|
696
|
-
|
|
697
|
-
|
|
737
|
+
if (ttl !== void 0 && (!Number.isFinite(ttl) || ttl < 0)) throw new TypeError("Cache TTL must be finite and nonnegative.");
|
|
738
|
+
if (ttl !== void 0) return this.setEntry(key, {
|
|
739
|
+
value,
|
|
740
|
+
expiresAt: Date.now() + ttl * 1e3
|
|
741
|
+
});
|
|
742
|
+
await this.ns.put(key, JSON.stringify(value));
|
|
743
|
+
}
|
|
744
|
+
async setEntry(key, entry) {
|
|
745
|
+
if (!Number.isFinite(entry.expiresAt)) throw new TypeError("Cache expiry must be finite.");
|
|
746
|
+
const remaining = (entry.expiresAt - Date.now()) / 1e3;
|
|
747
|
+
if (remaining <= 0) return this.del(key);
|
|
748
|
+
await this.ns.put(key, JSON.stringify(entry.value), {
|
|
749
|
+
expirationTtl: Math.max(60, Math.ceil(remaining)),
|
|
750
|
+
metadata: { velaCacheExpiresAt: entry.expiresAt }
|
|
751
|
+
});
|
|
698
752
|
}
|
|
699
753
|
async del(key) {
|
|
700
754
|
await this.ns.delete(key);
|
|
701
755
|
}
|
|
756
|
+
/** Namespace-wide, best effort. Use a dedicated value namespace; never use for scoped invalidation. */
|
|
702
757
|
async clear() {
|
|
703
758
|
let cursor;
|
|
704
759
|
do {
|
|
@@ -708,6 +763,27 @@ var KVCacheStore = class {
|
|
|
708
763
|
} while (cursor);
|
|
709
764
|
}
|
|
710
765
|
};
|
|
766
|
+
/**
|
|
767
|
+
* Optional, eventually consistent generation store. Use a dedicated KV namespace
|
|
768
|
+
* without TTL/lifecycle cleanup. Never delete/reset generations while entries can
|
|
769
|
+
* survive. Concurrent writes and cached/negative reads prevent strong invalidation;
|
|
770
|
+
* this is unsuitable for strict read-after-write or authorization revocation.
|
|
771
|
+
*/
|
|
772
|
+
var KVCacheInvalidationStore = class {
|
|
773
|
+
ns;
|
|
774
|
+
constructor(ns) {
|
|
775
|
+
this.ns = ns;
|
|
776
|
+
}
|
|
777
|
+
async getVersion(key) {
|
|
778
|
+
const value = await this.ns.get(key, "json");
|
|
779
|
+
if (value === null) return "initial";
|
|
780
|
+
if (typeof value !== "string" || value.length === 0 || value.length > 2048) throw new TypeError("Invalid cache generation.");
|
|
781
|
+
return value;
|
|
782
|
+
}
|
|
783
|
+
async invalidate(key) {
|
|
784
|
+
await this.ns.put(key, JSON.stringify(crypto.randomUUID()));
|
|
785
|
+
}
|
|
786
|
+
};
|
|
711
787
|
//#endregion
|
|
712
788
|
//#region src/services/flagship-flag.driver.ts
|
|
713
789
|
/**
|
|
@@ -846,6 +922,13 @@ registerEntrypointKind({
|
|
|
846
922
|
metaKey: SCHEDULED_METADATA_KEY,
|
|
847
923
|
level: "method"
|
|
848
924
|
});
|
|
925
|
+
function parseScheduledMetadata(value) {
|
|
926
|
+
if (typeof value !== "object" || value === null || !("cron" in value) || typeof value.cron !== "string" || !("methodName" in value) || typeof value.methodName !== "string" || !parseCron(value.cron, { dialect: "cloudflare" })) throw new TypeError("Invalid Cloudflare scheduled metadata");
|
|
927
|
+
return {
|
|
928
|
+
cron: value.cron,
|
|
929
|
+
methodName: value.methodName
|
|
930
|
+
};
|
|
931
|
+
}
|
|
849
932
|
/**
|
|
850
933
|
* Marks a method as a scheduled (cron) handler.
|
|
851
934
|
*
|
|
@@ -861,8 +944,9 @@ registerEntrypointKind({
|
|
|
861
944
|
* ```
|
|
862
945
|
*/
|
|
863
946
|
function Scheduled(cron) {
|
|
947
|
+
if (!parseCron(cron, { dialect: "cloudflare" })) throw new TypeError(`Invalid Cloudflare cron expression: ${cron}`);
|
|
864
948
|
return (target, propertyKey, _descriptor) => {
|
|
865
|
-
const existing =
|
|
949
|
+
const existing = getScheduledMetadata(target);
|
|
866
950
|
existing.push({
|
|
867
951
|
cron,
|
|
868
952
|
methodName: String(propertyKey)
|
|
@@ -870,6 +954,13 @@ function Scheduled(cron) {
|
|
|
870
954
|
defineMetadata(SCHEDULED_METADATA_KEY, existing, target.constructor);
|
|
871
955
|
};
|
|
872
956
|
}
|
|
957
|
+
function getScheduledMetadata(target) {
|
|
958
|
+
const ctor = typeof target === "function" ? target : target.constructor;
|
|
959
|
+
const value = getMetadata(SCHEDULED_METADATA_KEY, ctor);
|
|
960
|
+
if (value === void 0) return [];
|
|
961
|
+
if (!Array.isArray(value)) throw new TypeError("Invalid Cloudflare scheduled metadata list");
|
|
962
|
+
return value.map(parseScheduledMetadata);
|
|
963
|
+
}
|
|
873
964
|
//#endregion
|
|
874
965
|
//#region src/decorators/queue-consumer.ts
|
|
875
966
|
const QUEUE_CONSUMER_METADATA_KEY = "cloudflare:queue-consumer";
|
|
@@ -1018,6 +1109,6 @@ function durableObjectNonceStore(options) {
|
|
|
1018
1109
|
} };
|
|
1019
1110
|
}
|
|
1020
1111
|
//#endregion
|
|
1021
|
-
export { CloudflareApplication, CloudflareWebSocketModule, ConnectedSocket, DoCursorLog, DoPitrUnavailableError, Env, FlagshipFlagDriver, KVCacheStore, KvFlagDriver, MessageBody, QueueConsumer, R2StorageDriver, STORAGE_OPTIONS, Scheduled, StorageController, StorageManagerService, StorageModule, StorageService, SubscribeMessage, WebSocketGateway, WebSocketServer, WsException, armDoPitr, broadcastToRoom, cloudflareAdapter, cloudflareRateLimitStore, createCloudflareApp, createCloudflareWorker, durableObjectCursorLog, durableObjectLive, durableObjectNonceStore, durableObjectRoomName, flagshipFlagDriver, isDoPitrUnavailable, kvFlagDriver, liveInvalidateToRoom, readDoPitrBookmark };
|
|
1112
|
+
export { CloudflareApplication, CloudflareWebSocketModule, ConnectedSocket, DoCursorLog, DoPitrUnavailableError, Env, FlagshipFlagDriver, KVCacheInvalidationStore, KVCacheStore, KvFlagDriver, MessageBody, QueueConsumer, R2StorageDriver, STORAGE_OPTIONS, Scheduled, StorageController, StorageManagerService, StorageModule, StorageService, SubscribeMessage, WebSocketGateway, WebSocketServer, WsException, armDoPitr, broadcastToRoom, cloudflareAdapter, cloudflareRateLimitStore, createCloudflareApp, createCloudflareWorker, durableObjectCursorLog, durableObjectLive, durableObjectNonceStore, durableObjectRoomName, flagshipFlagDriver, isDoPitrUnavailable, kvFlagDriver, liveInvalidateToRoom, parseScheduledMetadata, readDoPitrBookmark };
|
|
1022
1113
|
|
|
1023
1114
|
//# sourceMappingURL=index.js.map
|