@velajs/cloudflare 1.22.0 → 1.23.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 +60 -0
- package/README.md +39 -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 +22 -9
- package/dist/index.js +107 -59
- 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 +12 -8
- package/dist/nonce-validation-LE0vFk-7.js.map +0 -1
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
|
|
@@ -846,6 +879,13 @@ registerEntrypointKind({
|
|
|
846
879
|
metaKey: SCHEDULED_METADATA_KEY,
|
|
847
880
|
level: "method"
|
|
848
881
|
});
|
|
882
|
+
function parseScheduledMetadata(value) {
|
|
883
|
+
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");
|
|
884
|
+
return {
|
|
885
|
+
cron: value.cron,
|
|
886
|
+
methodName: value.methodName
|
|
887
|
+
};
|
|
888
|
+
}
|
|
849
889
|
/**
|
|
850
890
|
* Marks a method as a scheduled (cron) handler.
|
|
851
891
|
*
|
|
@@ -861,8 +901,9 @@ registerEntrypointKind({
|
|
|
861
901
|
* ```
|
|
862
902
|
*/
|
|
863
903
|
function Scheduled(cron) {
|
|
904
|
+
if (!parseCron(cron, { dialect: "cloudflare" })) throw new TypeError(`Invalid Cloudflare cron expression: ${cron}`);
|
|
864
905
|
return (target, propertyKey, _descriptor) => {
|
|
865
|
-
const existing =
|
|
906
|
+
const existing = getScheduledMetadata(target);
|
|
866
907
|
existing.push({
|
|
867
908
|
cron,
|
|
868
909
|
methodName: String(propertyKey)
|
|
@@ -870,6 +911,13 @@ function Scheduled(cron) {
|
|
|
870
911
|
defineMetadata(SCHEDULED_METADATA_KEY, existing, target.constructor);
|
|
871
912
|
};
|
|
872
913
|
}
|
|
914
|
+
function getScheduledMetadata(target) {
|
|
915
|
+
const ctor = typeof target === "function" ? target : target.constructor;
|
|
916
|
+
const value = getMetadata(SCHEDULED_METADATA_KEY, ctor);
|
|
917
|
+
if (value === void 0) return [];
|
|
918
|
+
if (!Array.isArray(value)) throw new TypeError("Invalid Cloudflare scheduled metadata list");
|
|
919
|
+
return value.map(parseScheduledMetadata);
|
|
920
|
+
}
|
|
873
921
|
//#endregion
|
|
874
922
|
//#region src/decorators/queue-consumer.ts
|
|
875
923
|
const QUEUE_CONSUMER_METADATA_KEY = "cloudflare:queue-consumer";
|
|
@@ -1018,6 +1066,6 @@ function durableObjectNonceStore(options) {
|
|
|
1018
1066
|
} };
|
|
1019
1067
|
}
|
|
1020
1068
|
//#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 };
|
|
1069
|
+
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, parseScheduledMetadata, readDoPitrBookmark };
|
|
1022
1070
|
|
|
1023
1071
|
//# sourceMappingURL=index.js.map
|