@umituz/web-cloudflare 1.4.6 → 1.4.8
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/package.json +1 -1
- package/src/config/patterns.ts +11 -8
- package/src/config/types.ts +9 -1
- package/src/domains/ai-gateway/services/index.ts +52 -25
- package/src/domains/analytics/services/analytics.service.ts +16 -92
- package/src/domains/analytics/types/service.interface.ts +1 -1
- package/src/domains/d1/services/d1.service.ts +19 -22
- package/src/domains/images/services/images.service.ts +58 -21
- package/src/domains/kv/services/kv.service.ts +14 -5
- package/src/domains/middleware/entities/index.ts +4 -4
- package/src/domains/middleware/services/auth.service.ts +6 -2
- package/src/domains/middleware/services/cache.service.ts +7 -3
- package/src/domains/middleware/services/cors.service.ts +8 -5
- package/src/domains/middleware/services/rate-limit.service.ts +7 -3
- package/src/domains/middleware/types/service.interface.ts +17 -11
- package/src/domains/r2/services/r2.service.ts +25 -13
- package/src/domains/workers/entities/index.ts +17 -1
- package/src/domains/workers/examples/worker.example.ts +11 -8
- package/src/domains/workers/services/workers.service.ts +9 -4
- package/src/domains/workflows/entities/index.ts +14 -1
- package/src/domains/workflows/services/workflows.service.ts +43 -10
- package/src/domains/wrangler/services/wrangler.service.ts +150 -443
- package/src/index.ts +44 -12
- package/src/infrastructure/middleware/index.ts +23 -18
- package/src/infrastructure/router/index.ts +2 -1
- package/src/infrastructure/utils/helpers.ts +29 -7
- package/src/presentation/hooks/cloudflare.hooks.ts +7 -309
- package/src/presentation/hooks/index.ts +4 -1
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @description Cloudflare KV key-value storage operations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { KVEntry, KVListOptions, KVListResult } from "
|
|
6
|
+
import type { KVEntry, KVListOptions, KVListResult } from "../../../domain/entities/kv.entity";
|
|
7
7
|
import type { IKVService } from "../../../domain/interfaces/services.interface";
|
|
8
8
|
import { validationUtils, cacheUtils } from "../../../infrastructure/utils";
|
|
9
9
|
|
|
@@ -78,10 +78,17 @@ class KVService implements IKVService {
|
|
|
78
78
|
prefix: options?.prefix,
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
// Handle cursor property which may not be in the type definition
|
|
82
|
+
type ListResultWithCursor = typeof list & { cursor?: string };
|
|
83
|
+
const cursor = (list as ListResultWithCursor).cursor;
|
|
84
|
+
|
|
81
85
|
return {
|
|
82
|
-
keys: list.keys
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
keys: list.keys.map((k) => ({
|
|
87
|
+
key: k.name,
|
|
88
|
+
value: '',
|
|
89
|
+
metadata: k.metadata as Record<string, unknown> | undefined,
|
|
90
|
+
})),
|
|
91
|
+
cursor: cursor,
|
|
85
92
|
};
|
|
86
93
|
}
|
|
87
94
|
|
|
@@ -107,10 +114,12 @@ class KVService implements IKVService {
|
|
|
107
114
|
|
|
108
115
|
await Promise.all(
|
|
109
116
|
list.keys.map(async (key) => {
|
|
110
|
-
await this.delete(key.
|
|
117
|
+
await this.delete(key.key, binding);
|
|
111
118
|
})
|
|
112
119
|
);
|
|
113
120
|
}
|
|
114
121
|
}
|
|
115
122
|
|
|
123
|
+
// Export class and singleton instance
|
|
124
|
+
export { KVService };
|
|
116
125
|
export const kvService = new KVService();
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
/**
|
|
7
7
|
* CORS configuration
|
|
8
8
|
*/
|
|
9
|
-
export interface
|
|
9
|
+
export interface MiddlewareCORSConfig {
|
|
10
10
|
enabled: boolean;
|
|
11
11
|
allowedOrigins: string[];
|
|
12
12
|
allowedMethods: string[];
|
|
@@ -19,7 +19,7 @@ export interface CORSConfig {
|
|
|
19
19
|
/**
|
|
20
20
|
* Cache configuration
|
|
21
21
|
*/
|
|
22
|
-
export interface
|
|
22
|
+
export interface MiddlewareCacheConfig {
|
|
23
23
|
enabled: boolean;
|
|
24
24
|
defaultTTL: number;
|
|
25
25
|
paths?: Record<string, number>;
|
|
@@ -31,7 +31,7 @@ export interface CacheConfig {
|
|
|
31
31
|
/**
|
|
32
32
|
* Rate limit configuration
|
|
33
33
|
*/
|
|
34
|
-
export interface
|
|
34
|
+
export interface MiddlewareRateLimitConfig {
|
|
35
35
|
enabled: boolean;
|
|
36
36
|
maxRequests: number;
|
|
37
37
|
window: number;
|
|
@@ -48,7 +48,7 @@ export interface RateLimitConfig {
|
|
|
48
48
|
/**
|
|
49
49
|
* Authentication configuration
|
|
50
50
|
*/
|
|
51
|
-
export interface
|
|
51
|
+
export interface MiddlewareAuthConfig {
|
|
52
52
|
enabled: boolean;
|
|
53
53
|
type: 'bearer' | 'apikey' | 'basic';
|
|
54
54
|
token?: string;
|
|
@@ -3,14 +3,18 @@
|
|
|
3
3
|
* @description Authentication middleware for Cloudflare Workers
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { MiddlewareAuthConfig } from '../entities';
|
|
7
|
+
|
|
8
|
+
// Type aliases for backwards compatibility
|
|
9
|
+
export type { MiddlewareAuthConfig };
|
|
10
|
+
export type AuthConfig = MiddlewareAuthConfig;
|
|
7
11
|
|
|
8
12
|
/**
|
|
9
13
|
* Require authentication
|
|
10
14
|
*/
|
|
11
15
|
export async function requireAuth(
|
|
12
16
|
request: Request,
|
|
13
|
-
config:
|
|
17
|
+
config: MiddlewareAuthConfig
|
|
14
18
|
): Promise<Response | null> {
|
|
15
19
|
if (!config.enabled) {
|
|
16
20
|
return null;
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
* @description Caching middleware for Cloudflare Workers
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { MiddlewareCacheConfig } from '../entities';
|
|
7
|
+
|
|
8
|
+
// Type aliases for backwards compatibility
|
|
9
|
+
export type { MiddlewareCacheConfig };
|
|
10
|
+
export type CacheConfig = MiddlewareCacheConfig;
|
|
7
11
|
|
|
8
12
|
interface CacheEntry {
|
|
9
13
|
response: Response;
|
|
@@ -17,7 +21,7 @@ const cacheStore = new Map<string, CacheEntry>();
|
|
|
17
21
|
*/
|
|
18
22
|
export async function cache(
|
|
19
23
|
request: Request,
|
|
20
|
-
config:
|
|
24
|
+
config: MiddlewareCacheConfig
|
|
21
25
|
): Promise<Response | null> {
|
|
22
26
|
if (!config.enabled) {
|
|
23
27
|
return null;
|
|
@@ -46,7 +50,7 @@ export async function cache(
|
|
|
46
50
|
export function setCache(
|
|
47
51
|
request: Request,
|
|
48
52
|
response: Response,
|
|
49
|
-
config:
|
|
53
|
+
config: MiddlewareCacheConfig
|
|
50
54
|
): void {
|
|
51
55
|
if (!config.enabled) {
|
|
52
56
|
return;
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* @description Cross-Origin Resource Sharing middleware for Cloudflare Workers
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { MiddlewareCORSConfig } from '../entities';
|
|
7
|
+
|
|
8
|
+
// Type alias for backwards compatibility
|
|
9
|
+
export type CORSConfig = MiddlewareCORSConfig;
|
|
7
10
|
|
|
8
11
|
/**
|
|
9
12
|
* Add CORS headers to response
|
|
@@ -11,7 +14,7 @@ import type { CORSConfig } from '../entities';
|
|
|
11
14
|
export function addCorsHeaders(
|
|
12
15
|
request: Request,
|
|
13
16
|
response: Response,
|
|
14
|
-
config:
|
|
17
|
+
config: MiddlewareCORSConfig
|
|
15
18
|
): Response {
|
|
16
19
|
if (!config.enabled) {
|
|
17
20
|
return response;
|
|
@@ -23,7 +26,7 @@ export function addCorsHeaders(
|
|
|
23
26
|
// Check if origin is allowed
|
|
24
27
|
const allowedOrigin = config.allowedOrigins.includes('*')
|
|
25
28
|
? '*'
|
|
26
|
-
: config.allowedOrigins.includes(origin
|
|
29
|
+
: origin && config.allowedOrigins.includes(origin)
|
|
27
30
|
? origin
|
|
28
31
|
: config.allowedOrigins[0];
|
|
29
32
|
|
|
@@ -55,7 +58,7 @@ export function addCorsHeaders(
|
|
|
55
58
|
*/
|
|
56
59
|
export async function cors(
|
|
57
60
|
request: Request,
|
|
58
|
-
config:
|
|
61
|
+
config: MiddlewareCORSConfig
|
|
59
62
|
): Promise<Response | null> {
|
|
60
63
|
if (!config.enabled) {
|
|
61
64
|
return null;
|
|
@@ -68,7 +71,7 @@ export async function cors(
|
|
|
68
71
|
|
|
69
72
|
const allowedOrigin = config.allowedOrigins.includes('*')
|
|
70
73
|
? '*'
|
|
71
|
-
: config.allowedOrigins.includes(origin
|
|
74
|
+
: origin && config.allowedOrigins.includes(origin)
|
|
72
75
|
? origin
|
|
73
76
|
: config.allowedOrigins[0];
|
|
74
77
|
|
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
* @description Rate limiting middleware for Cloudflare Workers
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { MiddlewareRateLimitConfig } from '../entities';
|
|
7
|
+
|
|
8
|
+
// Type aliases for backwards compatibility
|
|
9
|
+
export type { MiddlewareRateLimitConfig };
|
|
10
|
+
export type RateLimitConfig = MiddlewareRateLimitConfig;
|
|
7
11
|
|
|
8
12
|
interface RateLimitEntry {
|
|
9
13
|
count: number;
|
|
@@ -15,7 +19,7 @@ const rateLimitStore = new Map<string, RateLimitEntry>();
|
|
|
15
19
|
/**
|
|
16
20
|
* Get rate limit key
|
|
17
21
|
*/
|
|
18
|
-
function getRateLimitKey(request: Request, config:
|
|
22
|
+
function getRateLimitKey(request: Request, config: MiddlewareRateLimitConfig): string {
|
|
19
23
|
const parts: string[] = [];
|
|
20
24
|
|
|
21
25
|
if (config.by === 'ip' || config.by === 'both') {
|
|
@@ -43,7 +47,7 @@ function getRateLimitKey(request: Request, config: RateLimitConfig): string {
|
|
|
43
47
|
*/
|
|
44
48
|
export async function checkRateLimit(
|
|
45
49
|
request: Request,
|
|
46
|
-
config:
|
|
50
|
+
config: MiddlewareRateLimitConfig
|
|
47
51
|
): Promise<Response | null> {
|
|
48
52
|
if (!config.enabled) {
|
|
49
53
|
return null;
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
MiddlewareCORSConfig,
|
|
8
|
+
MiddlewareCacheConfig,
|
|
9
|
+
MiddlewareRateLimitConfig,
|
|
10
|
+
MiddlewareAuthConfig,
|
|
11
11
|
SecurityHeadersConfig,
|
|
12
12
|
IPFilterConfig,
|
|
13
13
|
LogConfig,
|
|
@@ -15,29 +15,35 @@ import type {
|
|
|
15
15
|
ErrorHandlerConfig,
|
|
16
16
|
} from '../entities';
|
|
17
17
|
|
|
18
|
+
// Type aliases for backwards compatibility
|
|
19
|
+
export type CORSConfig = MiddlewareCORSConfig;
|
|
20
|
+
export type CacheConfig = MiddlewareCacheConfig;
|
|
21
|
+
export type RateLimitConfig = MiddlewareRateLimitConfig;
|
|
22
|
+
export type AuthConfig = MiddlewareAuthConfig;
|
|
23
|
+
|
|
18
24
|
export interface IMiddlewareService {
|
|
19
25
|
/**
|
|
20
26
|
* CORS middleware
|
|
21
27
|
*/
|
|
22
|
-
cors(request: Request, config:
|
|
23
|
-
addCorsHeaders(request: Request, response: Response, config:
|
|
28
|
+
cors(request: Request, config: MiddlewareCORSConfig): Promise<Response | null>;
|
|
29
|
+
addCorsHeaders(request: Request, response: Response, config: MiddlewareCORSConfig): Response;
|
|
24
30
|
|
|
25
31
|
/**
|
|
26
32
|
* Cache middleware
|
|
27
33
|
*/
|
|
28
|
-
cache(request: Request, config:
|
|
29
|
-
setCache(request: Request, response: Response, config:
|
|
34
|
+
cache(request: Request, config: MiddlewareCacheConfig): Promise<Response | null>;
|
|
35
|
+
setCache(request: Request, response: Response, config: MiddlewareCacheConfig): void;
|
|
30
36
|
invalidateCache(pattern?: string): void;
|
|
31
37
|
|
|
32
38
|
/**
|
|
33
39
|
* Rate limit middleware
|
|
34
40
|
*/
|
|
35
|
-
checkRateLimit(request: Request, config:
|
|
41
|
+
checkRateLimit(request: Request, config: MiddlewareRateLimitConfig): Promise<Response | null>;
|
|
36
42
|
|
|
37
43
|
/**
|
|
38
44
|
* Authentication middleware
|
|
39
45
|
*/
|
|
40
|
-
requireAuth(request: Request, config:
|
|
46
|
+
requireAuth(request: Request, config: MiddlewareAuthConfig): Promise<Response | null>;
|
|
41
47
|
addUserContext(request: Request, user: {
|
|
42
48
|
id: string;
|
|
43
49
|
[key: string]: unknown;
|
|
@@ -88,7 +94,7 @@ export interface IMiddlewareService {
|
|
|
88
94
|
* Health check
|
|
89
95
|
*/
|
|
90
96
|
healthCheck(
|
|
91
|
-
env:
|
|
97
|
+
env: Record<string, unknown>,
|
|
92
98
|
config?: HealthCheckConfig
|
|
93
99
|
): Promise<Response>;
|
|
94
100
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @description Cloudflare R2 object storage operations
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { R2Object, R2ListOptions, R2ListResult, R2PutOptions, R2PresignedURL } from "
|
|
6
|
+
import type { R2Object, R2ListOptions, R2ListResult, R2PutOptions, R2PresignedURL } from "../../../domain/entities/r2.entity";
|
|
7
7
|
import type { IR2Service } from "../../../domain/interfaces/services.interface";
|
|
8
8
|
import { validationUtils } from "../../../infrastructure/utils";
|
|
9
9
|
|
|
@@ -51,8 +51,6 @@ class R2Service implements IR2Service {
|
|
|
51
51
|
key: object.key,
|
|
52
52
|
size: object.size,
|
|
53
53
|
uploaded: object.uploaded,
|
|
54
|
-
httpMetadata: object.httpMetadata,
|
|
55
|
-
customMetadata: object.customMetadata,
|
|
56
54
|
};
|
|
57
55
|
}
|
|
58
56
|
|
|
@@ -70,7 +68,6 @@ class R2Service implements IR2Service {
|
|
|
70
68
|
await bucket.put(key, data, {
|
|
71
69
|
httpMetadata: options?.httpMetadata,
|
|
72
70
|
customMetadata: options?.customMetadata,
|
|
73
|
-
checksum: options?.checksum,
|
|
74
71
|
});
|
|
75
72
|
}
|
|
76
73
|
|
|
@@ -93,10 +90,17 @@ class R2Service implements IR2Service {
|
|
|
93
90
|
cursor: options?.cursor,
|
|
94
91
|
});
|
|
95
92
|
|
|
93
|
+
// Handle cursor property which may not be in the type definition
|
|
94
|
+
type ListResultWithCursor = typeof listed & { cursor?: string };
|
|
95
|
+
const cursor = (listed as ListResultWithCursor).cursor;
|
|
96
|
+
|
|
96
97
|
return {
|
|
97
|
-
objects: listed.objects
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
objects: listed.objects.map((obj) => ({
|
|
99
|
+
key: obj.key,
|
|
100
|
+
size: obj.size,
|
|
101
|
+
uploaded: obj.uploaded,
|
|
102
|
+
})),
|
|
103
|
+
cursor: cursor,
|
|
100
104
|
};
|
|
101
105
|
}
|
|
102
106
|
|
|
@@ -105,11 +109,11 @@ class R2Service implements IR2Service {
|
|
|
105
109
|
// This would typically use the AWS SDK or custom signing logic
|
|
106
110
|
// For now, return a placeholder
|
|
107
111
|
|
|
108
|
-
const
|
|
112
|
+
const expires = Date.now() + expiresIn * 1000;
|
|
109
113
|
|
|
110
114
|
return {
|
|
111
|
-
url: `https://presigned-url-placeholder/${key}?expires=${
|
|
112
|
-
|
|
115
|
+
url: `https://presigned-url-placeholder/${key}?expires=${expires}`,
|
|
116
|
+
expires,
|
|
113
117
|
};
|
|
114
118
|
}
|
|
115
119
|
|
|
@@ -120,11 +124,11 @@ class R2Service implements IR2Service {
|
|
|
120
124
|
const objectKey = key || `uploads/${Date.now()}-${file.name}`;
|
|
121
125
|
|
|
122
126
|
await this.put(objectKey, file.stream(), {
|
|
123
|
-
...options,
|
|
124
127
|
httpMetadata: {
|
|
125
128
|
contentType: file.type,
|
|
126
129
|
...options?.httpMetadata,
|
|
127
130
|
},
|
|
131
|
+
customMetadata: options?.customMetadata,
|
|
128
132
|
});
|
|
129
133
|
}
|
|
130
134
|
|
|
@@ -134,10 +138,16 @@ class R2Service implements IR2Service {
|
|
|
134
138
|
throw new Error(`Failed to fetch URL: ${response.statusText}`);
|
|
135
139
|
}
|
|
136
140
|
|
|
137
|
-
|
|
141
|
+
const body = response.body;
|
|
142
|
+
if (!body) {
|
|
143
|
+
throw new Error(`Failed to read response body`);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
await this.put(key, body, {
|
|
138
147
|
httpMetadata: {
|
|
139
148
|
contentType: response.headers.get("Content-Type") || undefined,
|
|
140
149
|
},
|
|
150
|
+
binding,
|
|
141
151
|
});
|
|
142
152
|
}
|
|
143
153
|
|
|
@@ -155,10 +165,12 @@ class R2Service implements IR2Service {
|
|
|
155
165
|
|
|
156
166
|
await this.deleteMultiple(list.objects.map((obj) => obj.key), binding);
|
|
157
167
|
|
|
158
|
-
if (list.
|
|
168
|
+
if (list.cursor) {
|
|
159
169
|
await this.deletePrefix(prefix, binding);
|
|
160
170
|
}
|
|
161
171
|
}
|
|
162
172
|
}
|
|
163
173
|
|
|
174
|
+
// Export class and singleton instance
|
|
175
|
+
export { R2Service };
|
|
164
176
|
export const r2Service = new R2Service();
|
|
@@ -3,8 +3,24 @@
|
|
|
3
3
|
* @description Cloudflare Worker configuration and types
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export interface WorkerRequest
|
|
6
|
+
export interface WorkerRequest {
|
|
7
|
+
url: string;
|
|
8
|
+
method: string;
|
|
9
|
+
headers: Headers;
|
|
10
|
+
body?: ReadableStream | null;
|
|
7
11
|
cf?: IncomingRequestCfProperties;
|
|
12
|
+
cache?: RequestCache;
|
|
13
|
+
credentials?: RequestCredentials;
|
|
14
|
+
integrity?: string;
|
|
15
|
+
mode?: RequestMode;
|
|
16
|
+
redirect?: RequestRedirect;
|
|
17
|
+
referrer?: string;
|
|
18
|
+
referrerPolicy?: ReferrerPolicy;
|
|
19
|
+
json(): Promise<unknown>;
|
|
20
|
+
text(): Promise<string>;
|
|
21
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
22
|
+
blob(): Promise<Blob>;
|
|
23
|
+
formData(): Promise<FormData>;
|
|
8
24
|
}
|
|
9
25
|
|
|
10
26
|
export interface WorkerResponse extends Response {
|
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
* @description Example worker using @umituz/web-cloudflare package
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { workersService, kvService
|
|
6
|
+
import { workersService, kvService } from "@umituz/web-cloudflare";
|
|
7
|
+
import type { Env } from "../types/env.types";
|
|
8
|
+
import type { WorkerRequest } from "../entities";
|
|
7
9
|
|
|
8
10
|
// Configure routes
|
|
9
11
|
workersService.route("/", async () => {
|
|
@@ -11,14 +13,15 @@ workersService.route("/", async () => {
|
|
|
11
13
|
});
|
|
12
14
|
|
|
13
15
|
workersService.route("/api/hello", async (request) => {
|
|
14
|
-
const
|
|
16
|
+
const data = await request.json() as { name?: string };
|
|
17
|
+
const name = data?.name || "World";
|
|
15
18
|
|
|
16
19
|
return workersService.json({ message: `Hello, ${name}!` });
|
|
17
20
|
});
|
|
18
21
|
|
|
19
|
-
workersService.route("/api/cache/:key", async (request, env) => {
|
|
22
|
+
workersService.route("/api/cache/:key", async (request, env?: Env) => {
|
|
20
23
|
const url = new URL(request.url);
|
|
21
|
-
const key = url.pathname.split("/").pop();
|
|
24
|
+
const key = url.pathname.split("/").pop() || "";
|
|
22
25
|
|
|
23
26
|
if (request.method === "GET") {
|
|
24
27
|
const value = await kvService.get(key, env?.KV ? "KV" : undefined);
|
|
@@ -26,8 +29,8 @@ workersService.route("/api/cache/:key", async (request, env) => {
|
|
|
26
29
|
}
|
|
27
30
|
|
|
28
31
|
if (request.method === "POST") {
|
|
29
|
-
const
|
|
30
|
-
await kvService.put(key, value, { ttl: 3600 });
|
|
32
|
+
const data = await request.json() as { value?: unknown };
|
|
33
|
+
await kvService.put(key, data.value || "", { ttl: 3600 });
|
|
31
34
|
return workersService.json({ success: true });
|
|
32
35
|
}
|
|
33
36
|
|
|
@@ -36,6 +39,6 @@ workersService.route("/api/cache/:key", async (request, env) => {
|
|
|
36
39
|
|
|
37
40
|
// Export for Cloudflare Workers
|
|
38
41
|
export default {
|
|
39
|
-
fetch: (request: Request, env
|
|
40
|
-
workersService.fetch(request, env, ctx),
|
|
42
|
+
fetch: (request: Request, env?: Env, ctx?: ExecutionContext) =>
|
|
43
|
+
workersService.fetch(request as unknown as WorkerRequest, env, ctx),
|
|
41
44
|
};
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* @description Cloudflare Workers HTTP handler and routing
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { WorkerRequest, WorkerResponse,
|
|
6
|
+
import type { WorkerRequest, WorkerResponse, CloudflareWorkerConfig } from "../entities";
|
|
7
7
|
import type { Env } from "../types";
|
|
8
8
|
|
|
9
9
|
export interface WorkerFetchOptions {
|
|
@@ -61,9 +61,12 @@ class WorkersService {
|
|
|
61
61
|
* Fetch handler
|
|
62
62
|
*/
|
|
63
63
|
async fetch(request: WorkerRequest, env?: Env, ctx?: ExecutionContext): Promise<WorkerResponse> {
|
|
64
|
-
// Initialize cache
|
|
65
|
-
if (!this.cache && env) {
|
|
66
|
-
|
|
64
|
+
// Initialize cache if available in Workers runtime
|
|
65
|
+
if (!this.cache && env && typeof caches !== 'undefined') {
|
|
66
|
+
// Handle caches.default which may not be in the type definition
|
|
67
|
+
type CachesWithDefault = typeof caches & { default?: Cache };
|
|
68
|
+
const cacheDefault = (caches as CachesWithDefault).default;
|
|
69
|
+
this.cache = cacheDefault ?? null;
|
|
67
70
|
}
|
|
68
71
|
|
|
69
72
|
// Try middleware
|
|
@@ -161,4 +164,6 @@ class WorkersService {
|
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
|
|
167
|
+
// Export class and singleton instance
|
|
168
|
+
export { WorkersService };
|
|
164
169
|
export const workersService = new WorkersService();
|
|
@@ -18,6 +18,7 @@ export interface WorkflowStep {
|
|
|
18
18
|
maxDelay: number;
|
|
19
19
|
};
|
|
20
20
|
dependencies?: string[];
|
|
21
|
+
inputs?: Record<string, unknown>;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -27,7 +28,14 @@ export interface WorkflowDefinition {
|
|
|
27
28
|
id: string;
|
|
28
29
|
name: string;
|
|
29
30
|
description?: string;
|
|
31
|
+
version?: string;
|
|
30
32
|
steps: WorkflowStep[];
|
|
33
|
+
retryConfig?: {
|
|
34
|
+
maxAttempts: number;
|
|
35
|
+
backoffMultiplier: number;
|
|
36
|
+
initialDelay: number;
|
|
37
|
+
maxDelay: number;
|
|
38
|
+
};
|
|
31
39
|
}
|
|
32
40
|
|
|
33
41
|
/**
|
|
@@ -36,13 +44,18 @@ export interface WorkflowDefinition {
|
|
|
36
44
|
export interface WorkflowExecution {
|
|
37
45
|
id: string;
|
|
38
46
|
workflowId: string;
|
|
39
|
-
status: 'pending' | 'running' | 'completed' | 'failed';
|
|
47
|
+
status: 'pending' | 'running' | 'completed' | 'failed' | 'retrying';
|
|
40
48
|
currentStep?: string;
|
|
41
49
|
startedAt: number;
|
|
42
50
|
completedAt?: number;
|
|
43
51
|
input: unknown;
|
|
44
52
|
output?: unknown;
|
|
53
|
+
outputs?: Record<string, unknown>;
|
|
45
54
|
error?: string;
|
|
55
|
+
completedSteps: string[];
|
|
56
|
+
failedSteps: string[];
|
|
57
|
+
inputs: Record<string, unknown>;
|
|
58
|
+
retryCount: number;
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
/**
|
|
@@ -7,12 +7,42 @@ import type {
|
|
|
7
7
|
WorkflowDefinition,
|
|
8
8
|
WorkflowExecution,
|
|
9
9
|
WorkflowStep,
|
|
10
|
-
WorkflowInstanceState,
|
|
11
|
-
MediaProcessingWorkflow,
|
|
12
|
-
AIGenerationWorkflow,
|
|
13
|
-
BatchOperationWorkflow,
|
|
14
10
|
} from '../entities';
|
|
15
11
|
|
|
12
|
+
// Additional workflow types
|
|
13
|
+
export type WorkflowInstanceState = 'pending' | 'running' | 'completed' | 'failed';
|
|
14
|
+
|
|
15
|
+
export interface WorkflowStepState {
|
|
16
|
+
data: Record<string, unknown>;
|
|
17
|
+
status: WorkflowInstanceState;
|
|
18
|
+
completedAt?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface MediaProcessingWorkflow {
|
|
22
|
+
type: 'media-processing';
|
|
23
|
+
input: {
|
|
24
|
+
url: string;
|
|
25
|
+
operations: Array<{ type: string; params: Record<string, unknown> }>;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface AIGenerationWorkflow {
|
|
30
|
+
type: 'ai-generation';
|
|
31
|
+
input: {
|
|
32
|
+
prompt: string;
|
|
33
|
+
model: string;
|
|
34
|
+
parameters?: Record<string, unknown>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface BatchOperationWorkflow {
|
|
39
|
+
type: 'batch-operation';
|
|
40
|
+
input: {
|
|
41
|
+
items: Array<{ id: string; data: unknown }>;
|
|
42
|
+
operation: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
16
46
|
export interface WorkflowServiceConfig {
|
|
17
47
|
KV?: KVNamespace;
|
|
18
48
|
D1?: D1Database;
|
|
@@ -73,6 +103,7 @@ export class WorkflowService {
|
|
|
73
103
|
completedSteps: [],
|
|
74
104
|
failedSteps: [],
|
|
75
105
|
inputs,
|
|
106
|
+
input: inputs,
|
|
76
107
|
startedAt: Date.now(),
|
|
77
108
|
retryCount: 0,
|
|
78
109
|
};
|
|
@@ -122,7 +153,7 @@ export class WorkflowService {
|
|
|
122
153
|
execution.completedSteps.push(step.id);
|
|
123
154
|
|
|
124
155
|
// Save step state for idempotency
|
|
125
|
-
await this.saveStepState(execution.id, step.id, stepResult);
|
|
156
|
+
await this.saveStepState(execution.id, step.id, { result: stepResult } as Record<string, unknown>);
|
|
126
157
|
|
|
127
158
|
} catch (error) {
|
|
128
159
|
stepStatus[step.id] = 'failed';
|
|
@@ -257,11 +288,10 @@ export class WorkflowService {
|
|
|
257
288
|
data: Record<string, unknown>
|
|
258
289
|
): Promise<void> {
|
|
259
290
|
if (this.kv) {
|
|
260
|
-
const state:
|
|
261
|
-
executionId,
|
|
262
|
-
stepId,
|
|
291
|
+
const state: WorkflowStepState = {
|
|
263
292
|
data,
|
|
264
|
-
|
|
293
|
+
status: 'completed',
|
|
294
|
+
completedAt: Date.now(),
|
|
265
295
|
};
|
|
266
296
|
await this.kv.put(
|
|
267
297
|
`step:${executionId}:${stepId}`,
|
|
@@ -276,7 +306,7 @@ export class WorkflowService {
|
|
|
276
306
|
private async getStepState(
|
|
277
307
|
executionId: string,
|
|
278
308
|
stepId: string
|
|
279
|
-
): Promise<
|
|
309
|
+
): Promise<WorkflowStepState | null> {
|
|
280
310
|
if (this.kv) {
|
|
281
311
|
const data = await this.kv.get(`step:${executionId}:${stepId}`);
|
|
282
312
|
return data ? JSON.parse(data) : null;
|
|
@@ -435,3 +465,6 @@ export const WORKFLOW_TEMPLATES: Record<string, Partial<WorkflowDefinition>> = {
|
|
|
435
465
|
],
|
|
436
466
|
},
|
|
437
467
|
};
|
|
468
|
+
|
|
469
|
+
// Export singleton instance
|
|
470
|
+
export const workflowService = new WorkflowService();
|