@umituz/web-cloudflare 1.4.10 → 1.4.12
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/domain/entities/analytics.entity.ts +14 -0
- package/src/domain/entities/worker.entity.ts +149 -3
- package/src/domains/analytics/entities/index.ts +7 -44
- package/src/domains/d1/entities/index.ts +6 -34
- package/src/domains/images/entities/index.ts +8 -45
- package/src/domains/kv/entities/index.ts +6 -31
- package/src/domains/middleware/entities/index.ts +14 -104
- package/src/domains/pages/services/pages.service.ts +142 -14
- package/src/domains/r2/entities/index.ts +9 -52
- package/src/domains/workers/entities/index.ts +7 -48
- package/src/domains/workflows/entities/index.ts +7 -69
- package/src/domains/workflows/services/workflows.service.ts +8 -4
- package/src/domains/wrangler/services/wrangler.service.ts +22 -2
- package/src/domains/wrangler/types/service.interface.ts +4 -0
- package/src/infrastructure/middleware/index.ts +27 -57
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/web-cloudflare",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.12",
|
|
4
4
|
"description": "Comprehensive Cloudflare Workers & Pages integration with config-based patterns, middleware, router, workflows, and AI (Patch-only versioning: only z in x.y.z increments)",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -18,6 +18,7 @@ export interface AnalyticsEvent {
|
|
|
18
18
|
url: string;
|
|
19
19
|
eventType: string;
|
|
20
20
|
data?: Record<string, unknown>;
|
|
21
|
+
eventData?: Record<string, unknown>;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
export interface AnalyticsData {
|
|
@@ -28,3 +29,16 @@ export interface AnalyticsData {
|
|
|
28
29
|
uniqueVisitors: number;
|
|
29
30
|
};
|
|
30
31
|
}
|
|
32
|
+
|
|
33
|
+
export interface AnalyticsPageviewEvent {
|
|
34
|
+
timestamp: number;
|
|
35
|
+
url: string;
|
|
36
|
+
title: string;
|
|
37
|
+
referrer?: string;
|
|
38
|
+
eventType: 'pageview';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface AnalyticsCustomEvent extends AnalyticsEvent {
|
|
42
|
+
eventName: string;
|
|
43
|
+
eventData?: Record<string, unknown>;
|
|
44
|
+
}
|
|
@@ -30,6 +30,152 @@ export interface IncomingRequestCfProperties {
|
|
|
30
30
|
requestPriority?: number;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
export interface WorkerRequest {
|
|
34
|
+
url: string;
|
|
35
|
+
method: string;
|
|
36
|
+
headers: Headers;
|
|
37
|
+
body?: ReadableStream | null;
|
|
38
|
+
json(): Promise<unknown>;
|
|
39
|
+
cf?: IncomingRequestCfProperties;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface CloudflareWorkerConfig extends WorkerConfig {
|
|
43
|
+
env?: {
|
|
44
|
+
KV?: Record<string, KVNamespace>;
|
|
45
|
+
R2?: Record<string, R2Bucket>;
|
|
46
|
+
D1?: Record<string, D1Database>;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Workflow types
|
|
51
|
+
export interface WorkflowStep<T = unknown> {
|
|
52
|
+
id: string;
|
|
53
|
+
name: string;
|
|
54
|
+
handler: string | ((input: T) => Promise<unknown>);
|
|
55
|
+
inputs?: Record<string, unknown>;
|
|
56
|
+
dependencies?: string[];
|
|
57
|
+
timeout?: number;
|
|
58
|
+
retryPolicy?: {
|
|
59
|
+
maxAttempts?: number;
|
|
60
|
+
backoffMs?: number;
|
|
61
|
+
backoffMultiplier?: number;
|
|
62
|
+
initialDelay?: number;
|
|
63
|
+
maxDelay?: number;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface WorkflowDefinition<TInput = unknown, TOutput = unknown> {
|
|
68
|
+
id?: string;
|
|
69
|
+
name: string;
|
|
70
|
+
description?: string;
|
|
71
|
+
version?: string;
|
|
72
|
+
steps: WorkflowStep<TInput>[];
|
|
73
|
+
execute?: (input: TInput) => Promise<TOutput>;
|
|
74
|
+
retryConfig?: {
|
|
75
|
+
maxRetries?: number;
|
|
76
|
+
backoffMultiplier?: number;
|
|
77
|
+
initialDelay?: number;
|
|
78
|
+
maxDelay?: number;
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export interface WorkflowExecution {
|
|
83
|
+
id: string;
|
|
84
|
+
workflowId: string;
|
|
85
|
+
status: 'pending' | 'running' | 'completed' | 'failed' | 'retrying';
|
|
86
|
+
input: unknown;
|
|
87
|
+
inputs?: Record<string, unknown>;
|
|
88
|
+
output?: unknown;
|
|
89
|
+
outputs?: Record<string, unknown>;
|
|
90
|
+
startedAt: number;
|
|
91
|
+
completedAt?: number;
|
|
92
|
+
completedSteps: string[];
|
|
93
|
+
failedSteps: string[];
|
|
94
|
+
error?: Error | string;
|
|
95
|
+
retryCount: number;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface CloudflareWorkflowConfig {
|
|
99
|
+
kvBinding?: string;
|
|
100
|
+
d1Binding?: string;
|
|
101
|
+
maxExecutionTime?: number;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Middleware config types
|
|
105
|
+
export interface MiddlewareCORSConfig {
|
|
106
|
+
enabled?: boolean;
|
|
107
|
+
allowedOrigins: string[];
|
|
108
|
+
allowedMethods: string[];
|
|
109
|
+
allowedHeaders: string[];
|
|
110
|
+
exposedHeaders?: string[];
|
|
111
|
+
credentials?: boolean;
|
|
112
|
+
allowCredentials?: boolean;
|
|
113
|
+
maxAge?: number;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export interface MiddlewareCacheConfig {
|
|
117
|
+
enabled: boolean;
|
|
118
|
+
defaultTTL: number;
|
|
119
|
+
staleWhileRevalidate?: boolean;
|
|
120
|
+
prefix?: string;
|
|
121
|
+
bypassPaths?: string[];
|
|
122
|
+
paths?: Record<string, number>;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface MiddlewareRateLimitConfig {
|
|
126
|
+
enabled: boolean;
|
|
127
|
+
maxRequests: number;
|
|
128
|
+
window: number;
|
|
129
|
+
by?: 'ip' | 'user' | 'custom' | 'both';
|
|
130
|
+
keyGenerator?: (request: Request) => string;
|
|
131
|
+
customKeys?: string[];
|
|
132
|
+
whitelist?: string[];
|
|
133
|
+
response?: {
|
|
134
|
+
status: number;
|
|
135
|
+
body: string;
|
|
136
|
+
message?: string;
|
|
137
|
+
headers?: Record<string, string>;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export interface MiddlewareAuthConfig {
|
|
142
|
+
enabled?: boolean;
|
|
143
|
+
type: 'bearer' | 'basic' | 'custom' | 'apikey';
|
|
144
|
+
validate: (request: Request) => Promise<boolean>;
|
|
145
|
+
challenge?: (request: Request) => Response;
|
|
146
|
+
token?: string;
|
|
147
|
+
apiKeyHeader?: string;
|
|
148
|
+
apiKeyValue?: string;
|
|
149
|
+
username?: string;
|
|
150
|
+
password?: string;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export interface SecurityHeadersConfig {
|
|
154
|
+
contentSecurityPolicy?: string;
|
|
155
|
+
strictTransportSecurity?: string;
|
|
156
|
+
xFrameOptions?: string;
|
|
157
|
+
xContentTypeOptions?: 'nosniff' | string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface IPFilterConfig {
|
|
161
|
+
allowed: string[];
|
|
162
|
+
blocked: string[];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export interface LogConfig {
|
|
166
|
+
enabled: boolean;
|
|
167
|
+
level: 'debug' | 'info' | 'warn' | 'error';
|
|
168
|
+
includeHeaders?: boolean;
|
|
169
|
+
includeBody?: boolean;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface HealthCheckConfig {
|
|
173
|
+
path: string;
|
|
174
|
+
methods: string[];
|
|
175
|
+
response: { status: number; body?: string };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export interface ErrorHandlerConfig {
|
|
179
|
+
includeStackTrace?: boolean;
|
|
180
|
+
sanitizeErrors?: boolean;
|
|
181
|
+
}
|
|
@@ -1,47 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Analytics
|
|
3
|
-
*
|
|
2
|
+
* Analytics Domain Entities
|
|
3
|
+
* Re-exports from central domain entities for consistency
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
export interface AnalyticsEvent {
|
|
12
|
-
readonly timestamp: number;
|
|
13
|
-
readonly url: string;
|
|
14
|
-
readonly eventType: "pageview" | "custom" | "outbound-link" | "timing";
|
|
15
|
-
readonly eventData?: Record<string, unknown>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface AnalyticsPageviewEvent extends AnalyticsEvent {
|
|
19
|
-
readonly eventType: "pageview";
|
|
20
|
-
readonly title: string;
|
|
21
|
-
readonly referrer?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface AnalyticsCustomEvent extends AnalyticsEvent {
|
|
25
|
-
readonly eventType: "custom";
|
|
26
|
-
readonly eventName: string;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface AnalyticsTimingEvent extends AnalyticsEvent {
|
|
30
|
-
readonly eventType: "timing";
|
|
31
|
-
readonly name: string;
|
|
32
|
-
readonly value: number;
|
|
33
|
-
readonly label?: string;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export interface WebAnalyticsData {
|
|
37
|
-
readonly siteId: string;
|
|
38
|
-
readonly events: readonly AnalyticsEvent[];
|
|
39
|
-
readonly metrics?: AnalyticsMetrics;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface AnalyticsMetrics {
|
|
43
|
-
readonly pageviews: number;
|
|
44
|
-
readonly uniqueVisitors: number;
|
|
45
|
-
readonly bounceRate?: number;
|
|
46
|
-
readonly avgSessionDuration?: number;
|
|
47
|
-
}
|
|
6
|
+
export type {
|
|
7
|
+
AnalyticsEvent,
|
|
8
|
+
AnalyticsPageviewEvent,
|
|
9
|
+
AnalyticsCustomEvent,
|
|
10
|
+
} from '../../../domain/entities/analytics.entity';
|
|
@@ -1,37 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* D1
|
|
3
|
-
*
|
|
2
|
+
* D1 Domain Entities
|
|
3
|
+
* Re-exports from central domain entities for consistency
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
export interface D1QueryResult<T = unknown> {
|
|
11
|
-
readonly results: readonly T[];
|
|
12
|
-
readonly success: boolean;
|
|
13
|
-
readonly meta?: D1QueryMeta;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface D1QueryMeta {
|
|
17
|
-
readonly duration: number;
|
|
18
|
-
readonly rows_read: number;
|
|
19
|
-
readonly rows_written: number;
|
|
20
|
-
readonly last_row_id?: number;
|
|
21
|
-
readonly changes: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface D1PreparedStatement {
|
|
25
|
-
readonly statement: string;
|
|
26
|
-
readonly params: readonly unknown[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface D1BatchResult {
|
|
30
|
-
readonly success: boolean;
|
|
31
|
-
readonly results: readonly D1QueryResult[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface D1TransactionOptions {
|
|
35
|
-
readonly rollbackOnError?: boolean;
|
|
36
|
-
readonly timeout?: number;
|
|
37
|
-
}
|
|
6
|
+
export type {
|
|
7
|
+
D1QueryResult,
|
|
8
|
+
D1BatchResult,
|
|
9
|
+
} from '../../../domain/entities/d1.entity';
|
|
@@ -1,48 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Images Domain Entities
|
|
3
|
+
* Re-exports from central domain entities for consistency
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
readonly variant: string;
|
|
13
|
-
readonly width?: number;
|
|
14
|
-
readonly height?: number;
|
|
15
|
-
readonly fit?: "scale-down" | "contain" | "cover" | "crop" | "pad";
|
|
16
|
-
readonly format?: "jpeg" | "png" | "gif" | "webp" | "avif";
|
|
17
|
-
readonly quality?: number;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface ImageUploadResult {
|
|
21
|
-
readonly id: string;
|
|
22
|
-
readonly filename: string;
|
|
23
|
-
readonly uploaded: Date;
|
|
24
|
-
readonly variants: readonly string[];
|
|
25
|
-
readonly requireSignedURLs: boolean;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface ImageUploadOptions {
|
|
29
|
-
readonly metadata?: Record<string, string>;
|
|
30
|
-
readonly requireSignedURLs?: boolean;
|
|
31
|
-
readonly variants?: readonly ImageVariant[];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface ImageTransformation {
|
|
35
|
-
readonly width?: number;
|
|
36
|
-
readonly height?: number;
|
|
37
|
-
readonly fit?: "scale-down" | "contain" | "cover" | "crop" | "pad";
|
|
38
|
-
readonly format?: "jpeg" | "png" | "gif" | "webp" | "avif";
|
|
39
|
-
readonly quality?: number;
|
|
40
|
-
readonly rotate?: number;
|
|
41
|
-
readonly flip?: boolean;
|
|
42
|
-
readonly flop?: boolean;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface SignedURL {
|
|
46
|
-
readonly url: string;
|
|
47
|
-
readonly expiresAt: Date;
|
|
48
|
-
}
|
|
6
|
+
export type {
|
|
7
|
+
ImageUploadResult,
|
|
8
|
+
ImageUploadOptions,
|
|
9
|
+
SignedURL,
|
|
10
|
+
ImageTransformation,
|
|
11
|
+
} from '../../../domain/entities/image.entity';
|
|
@@ -1,34 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* KV
|
|
3
|
-
*
|
|
2
|
+
* KV Domain Entities
|
|
3
|
+
* Re-exports from central domain entities for consistency
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface KVEntry<T = unknown> {
|
|
12
|
-
readonly key: string;
|
|
13
|
-
readonly value: T;
|
|
14
|
-
readonly metadata?: Record<string, unknown>;
|
|
15
|
-
readonly expiration?: number;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface KVListOptions {
|
|
19
|
-
readonly limit?: number;
|
|
20
|
-
readonly cursor?: string;
|
|
21
|
-
readonly prefix?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface KVListResult {
|
|
25
|
-
readonly keys: readonly KVKey[];
|
|
26
|
-
readonly list_complete: boolean;
|
|
27
|
-
readonly cursor?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface KVKey {
|
|
31
|
-
readonly name: string;
|
|
32
|
-
readonly metadata?: Record<string, unknown>;
|
|
33
|
-
readonly expiration?: number;
|
|
34
|
-
}
|
|
6
|
+
export type {
|
|
7
|
+
KVListOptions,
|
|
8
|
+
KVListResult,
|
|
9
|
+
} from '../../../domain/entities/kv.entity';
|
|
@@ -1,106 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Middleware Domain Entities
|
|
3
|
-
*
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Cache configuration
|
|
21
|
-
*/
|
|
22
|
-
export interface MiddlewareCacheConfig {
|
|
23
|
-
enabled: boolean;
|
|
24
|
-
defaultTTL: number;
|
|
25
|
-
paths?: Record<string, number>;
|
|
26
|
-
prefix?: string;
|
|
27
|
-
bypassPaths?: string[];
|
|
28
|
-
respectHeaders?: boolean;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Rate limit configuration
|
|
33
|
-
*/
|
|
34
|
-
export interface MiddlewareRateLimitConfig {
|
|
35
|
-
enabled: boolean;
|
|
36
|
-
maxRequests: number;
|
|
37
|
-
window: number;
|
|
38
|
-
by?: 'ip' | 'user' | 'both';
|
|
39
|
-
customKeys?: string[];
|
|
40
|
-
whitelist?: string[];
|
|
41
|
-
response?: {
|
|
42
|
-
status: number;
|
|
43
|
-
message: string;
|
|
44
|
-
retryAfter?: number;
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Authentication configuration
|
|
50
|
-
*/
|
|
51
|
-
export interface MiddlewareAuthConfig {
|
|
52
|
-
enabled: boolean;
|
|
53
|
-
type: 'bearer' | 'apikey' | 'basic';
|
|
54
|
-
token?: string;
|
|
55
|
-
apiKeyHeader?: string;
|
|
56
|
-
apiKeyValue?: string;
|
|
57
|
-
username?: string;
|
|
58
|
-
password?: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Security headers configuration
|
|
63
|
-
*/
|
|
64
|
-
export interface SecurityHeadersConfig {
|
|
65
|
-
frameGuard?: boolean;
|
|
66
|
-
contentTypeNosniff?: boolean;
|
|
67
|
-
xssProtection?: boolean;
|
|
68
|
-
strictTransportSecurity?: boolean;
|
|
69
|
-
referrerPolicy?: string;
|
|
70
|
-
contentSecurityPolicy?: string;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* IP filter configuration
|
|
75
|
-
*/
|
|
76
|
-
export interface IPFilterConfig {
|
|
77
|
-
mode: 'whitelist' | 'blacklist';
|
|
78
|
-
ips: string[];
|
|
79
|
-
cidrs?: string[];
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Log configuration
|
|
84
|
-
*/
|
|
85
|
-
export interface LogConfig {
|
|
86
|
-
level: 'debug' | 'info' | 'warn' | 'error';
|
|
87
|
-
includeHeaders?: boolean;
|
|
88
|
-
includeBody?: boolean;
|
|
89
|
-
sampleRate?: number;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Health check configuration
|
|
94
|
-
*/
|
|
95
|
-
export interface HealthCheckConfig {
|
|
96
|
-
uptime: number;
|
|
97
|
-
checks: Record<string, () => Promise<boolean>>;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Error handler configuration
|
|
102
|
-
*/
|
|
103
|
-
export interface ErrorHandlerConfig {
|
|
104
|
-
debug: boolean;
|
|
105
|
-
logger?: (error: Error) => void;
|
|
106
|
-
}
|
|
3
|
+
* Re-exports from central domain entities for consistency
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type {
|
|
7
|
+
MiddlewareCORSConfig,
|
|
8
|
+
MiddlewareCacheConfig,
|
|
9
|
+
MiddlewareRateLimitConfig,
|
|
10
|
+
MiddlewareAuthConfig,
|
|
11
|
+
SecurityHeadersConfig,
|
|
12
|
+
IPFilterConfig,
|
|
13
|
+
LogConfig,
|
|
14
|
+
HealthCheckConfig,
|
|
15
|
+
ErrorHandlerConfig,
|
|
16
|
+
} from '../../../domain/entities/worker.entity';
|
|
@@ -34,21 +34,53 @@ export class PagesService implements IPagesService {
|
|
|
34
34
|
compatibilityDate?: string;
|
|
35
35
|
}
|
|
36
36
|
): Promise<{ success: boolean; data?: PagesProject; error?: string }> {
|
|
37
|
-
|
|
37
|
+
const args = ['project', 'create', projectName];
|
|
38
|
+
if (options?.productionBranch) {
|
|
39
|
+
args.push('--production-branch', options.productionBranch);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result = this.executeWranglerPages(args);
|
|
43
|
+
if (result.success) {
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
data: {
|
|
47
|
+
name: projectName,
|
|
48
|
+
production_branch: options?.productionBranch,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
return { success: false, error: result.stderr };
|
|
38
53
|
}
|
|
39
54
|
|
|
40
55
|
/**
|
|
41
56
|
* List all Pages projects
|
|
42
57
|
*/
|
|
43
58
|
async listProjects(): Promise<{ success: boolean; data?: PagesProject[]; error?: string }> {
|
|
44
|
-
|
|
59
|
+
const result = this.executeWranglerPages(['project', 'list']);
|
|
60
|
+
if (result.success) {
|
|
61
|
+
// Parse output to get project list
|
|
62
|
+
try {
|
|
63
|
+
const projects = this.parseProjectsList(result.stdout);
|
|
64
|
+
return { success: true, data: projects };
|
|
65
|
+
} catch {
|
|
66
|
+
return { success: true, data: [] };
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { success: false, error: result.stderr };
|
|
45
70
|
}
|
|
46
71
|
|
|
47
72
|
/**
|
|
48
73
|
* Get project details
|
|
49
74
|
*/
|
|
50
75
|
async getProject(projectName: string): Promise<{ success: boolean; data?: PagesProject; error?: string }> {
|
|
51
|
-
|
|
76
|
+
const result = this.executeWranglerPages(['project', 'view', projectName]);
|
|
77
|
+
if (result.success) {
|
|
78
|
+
return {
|
|
79
|
+
success: true,
|
|
80
|
+
data: { name: projectName },
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return { success: false, error: result.stderr };
|
|
52
84
|
}
|
|
53
85
|
|
|
54
86
|
/**
|
|
@@ -57,7 +89,39 @@ export class PagesService implements IPagesService {
|
|
|
57
89
|
async deploy(
|
|
58
90
|
options: PagesDeployOptions
|
|
59
91
|
): Promise<{ success: boolean; data?: PagesDeploymentResult; error?: string }> {
|
|
60
|
-
|
|
92
|
+
const args = ['deploy', options.directory || 'dist', '--project-name', options.projectName];
|
|
93
|
+
|
|
94
|
+
if (options.branch) {
|
|
95
|
+
args.push('--branch', options.branch);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (options.environment === 'preview') {
|
|
99
|
+
args.push('--branch', `preview-${Date.now()}`);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Add environment variables
|
|
103
|
+
if (options.vars && Object.keys(options.vars).length > 0) {
|
|
104
|
+
Object.entries(options.vars).forEach(([key, value]) => {
|
|
105
|
+
args.push('--var', `${key}:${value}`);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const result = this.executeWranglerPages(args);
|
|
110
|
+
if (result.success) {
|
|
111
|
+
const url = this.extractDeploymentUrl(result.stdout);
|
|
112
|
+
return {
|
|
113
|
+
success: true,
|
|
114
|
+
data: {
|
|
115
|
+
deployment: {
|
|
116
|
+
id: Date.now().toString(),
|
|
117
|
+
project: options.projectName,
|
|
118
|
+
url: url || '',
|
|
119
|
+
},
|
|
120
|
+
url,
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return { success: false, error: result.stderr };
|
|
61
125
|
}
|
|
62
126
|
|
|
63
127
|
/**
|
|
@@ -71,7 +135,23 @@ export class PagesService implements IPagesService {
|
|
|
71
135
|
compatibilityFlags?: string[];
|
|
72
136
|
}
|
|
73
137
|
): Promise<{ success: boolean; data?: PagesFunction; error?: string }> {
|
|
74
|
-
|
|
138
|
+
const args = ['function', 'create', projectName, functionName];
|
|
139
|
+
|
|
140
|
+
if (options?.compatibilityDate) {
|
|
141
|
+
args.push('--compatibility-date', options.compatibilityDate);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const result = this.executeWranglerPages(args);
|
|
145
|
+
if (result.success) {
|
|
146
|
+
return {
|
|
147
|
+
success: true,
|
|
148
|
+
data: {
|
|
149
|
+
name: functionName,
|
|
150
|
+
compatibilityDate: options?.compatibilityDate,
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
return { success: false, error: result.stderr };
|
|
75
155
|
}
|
|
76
156
|
|
|
77
157
|
/**
|
|
@@ -80,7 +160,11 @@ export class PagesService implements IPagesService {
|
|
|
80
160
|
async listDeployments(
|
|
81
161
|
projectName: string
|
|
82
162
|
): Promise<{ success: boolean; data?: PagesDeployment[]; error?: string }> {
|
|
83
|
-
|
|
163
|
+
const result = this.executeWranglerPages(['deployment', 'list', '--project-name', projectName]);
|
|
164
|
+
if (result.success) {
|
|
165
|
+
return { success: true, data: [] };
|
|
166
|
+
}
|
|
167
|
+
return { success: false, error: result.stderr };
|
|
84
168
|
}
|
|
85
169
|
|
|
86
170
|
/**
|
|
@@ -90,7 +174,24 @@ export class PagesService implements IPagesService {
|
|
|
90
174
|
projectName: string,
|
|
91
175
|
deploymentId: string
|
|
92
176
|
): Promise<{ success: boolean; data?: PagesDeployment; error?: string }> {
|
|
93
|
-
|
|
177
|
+
const result = this.executeWranglerPages([
|
|
178
|
+
'deployment',
|
|
179
|
+
'view',
|
|
180
|
+
deploymentId,
|
|
181
|
+
'--project-name',
|
|
182
|
+
projectName,
|
|
183
|
+
]);
|
|
184
|
+
if (result.success) {
|
|
185
|
+
return {
|
|
186
|
+
success: true,
|
|
187
|
+
data: {
|
|
188
|
+
id: deploymentId,
|
|
189
|
+
project: projectName,
|
|
190
|
+
url: '',
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
return { success: false, error: result.stderr };
|
|
94
195
|
}
|
|
95
196
|
|
|
96
197
|
/**
|
|
@@ -100,7 +201,17 @@ export class PagesService implements IPagesService {
|
|
|
100
201
|
projectName: string,
|
|
101
202
|
deploymentId: string
|
|
102
203
|
): Promise<{ success: boolean; error?: string }> {
|
|
103
|
-
|
|
204
|
+
const result = this.executeWranglerPages([
|
|
205
|
+
'deployment',
|
|
206
|
+
'delete',
|
|
207
|
+
deploymentId,
|
|
208
|
+
'--project-name',
|
|
209
|
+
projectName,
|
|
210
|
+
]);
|
|
211
|
+
if (result.success) {
|
|
212
|
+
return { success: true };
|
|
213
|
+
}
|
|
214
|
+
return { success: false, error: result.stderr };
|
|
104
215
|
}
|
|
105
216
|
|
|
106
217
|
/**
|
|
@@ -132,12 +243,29 @@ export class PagesService implements IPagesService {
|
|
|
132
243
|
}
|
|
133
244
|
}
|
|
134
245
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
);
|
|
246
|
+
/**
|
|
247
|
+
* Parse projects list output
|
|
248
|
+
*/
|
|
249
|
+
private parseProjectsList(stdout: string): PagesProject[] {
|
|
250
|
+
const projects: PagesProject[] = [];
|
|
251
|
+
const lines = stdout.split('\n');
|
|
252
|
+
for (const line of lines) {
|
|
253
|
+
if (line.includes('│')) {
|
|
254
|
+
const parts = line.split('│').map((s) => s.trim());
|
|
255
|
+
if (parts.length >= 2 && parts[1]) {
|
|
256
|
+
projects.push({ name: parts[1] });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return projects;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Extract deployment URL from wrangler output
|
|
265
|
+
*/
|
|
266
|
+
private extractDeploymentUrl(stdout: string): string | undefined {
|
|
267
|
+
const urlMatch = stdout.match(/https?:\/\/[^\s]+\.pages\.dev[^\s]*/);
|
|
268
|
+
return urlMatch ? urlMatch[0] : undefined;
|
|
141
269
|
}
|
|
142
270
|
}
|
|
143
271
|
|
|
@@ -1,55 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* R2
|
|
3
|
-
*
|
|
2
|
+
* R2 Domain Entities
|
|
3
|
+
* Re-exports from central domain entities for consistency
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
readonly size: number;
|
|
14
|
-
readonly uploaded: Date;
|
|
15
|
-
readonly httpMetadata?: R2HTTPMetadata;
|
|
16
|
-
readonly customMetadata?: Record<string, string>;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface R2HTTPMetadata {
|
|
20
|
-
readonly contentType?: string;
|
|
21
|
-
readonly cacheControl?: string;
|
|
22
|
-
readonly contentEncoding?: string;
|
|
23
|
-
readonly contentLanguage?: string;
|
|
24
|
-
readonly cacheExpiry?: Date;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface R2UploadResult {
|
|
28
|
-
readonly key: string;
|
|
29
|
-
readonly size: number;
|
|
30
|
-
readonly etag?: string;
|
|
31
|
-
readonly version?: string;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export interface R2ListOptions {
|
|
35
|
-
readonly limit?: number;
|
|
36
|
-
readonly prefix?: string;
|
|
37
|
-
readonly cursor?: string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface R2ListResult {
|
|
41
|
-
readonly objects: readonly R2Object[];
|
|
42
|
-
readonly truncated: boolean;
|
|
43
|
-
readonly cursor?: string;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface R2PutOptions {
|
|
47
|
-
readonly httpMetadata?: R2HTTPMetadata;
|
|
48
|
-
readonly customMetadata?: Record<string, string>;
|
|
49
|
-
readonly checksum?: string;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface R2PresignedURL {
|
|
53
|
-
readonly url: string;
|
|
54
|
-
readonly expiresAt: Date;
|
|
55
|
-
}
|
|
6
|
+
export type {
|
|
7
|
+
R2Object,
|
|
8
|
+
R2ListOptions,
|
|
9
|
+
R2ListResult,
|
|
10
|
+
R2PutOptions,
|
|
11
|
+
R2PresignedURL,
|
|
12
|
+
} from '../../../domain/entities/r2.entity';
|
|
@@ -1,51 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Workers Domain Entities
|
|
3
|
+
* Re-exports from central domain entities for consistency
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface WorkerResponse extends Response {
|
|
27
|
-
waitUntil?: (promise: Promise<unknown>) => void;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface CloudflareWorkerConfig {
|
|
31
|
-
readonly name: string;
|
|
32
|
-
readonly routes?: string[];
|
|
33
|
-
readonly schedule?: string;
|
|
34
|
-
readonly bindings?: WorkerBindings;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface WorkerBindings {
|
|
38
|
-
readonly kv?: Record<string, KVNamespace>;
|
|
39
|
-
readonly r2?: Record<string, R2Bucket>;
|
|
40
|
-
readonly d1?: Record<string, D1Database>;
|
|
41
|
-
readonly env?: Record<string, string>;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface IncomingRequestCfProperties {
|
|
45
|
-
readonly colo?: string;
|
|
46
|
-
readonly country?: string;
|
|
47
|
-
readonly httpProtocol?: string;
|
|
48
|
-
readonly requestPriority?: string;
|
|
49
|
-
readonly tlsVersion?: string;
|
|
50
|
-
readonly tlsCipher?: string;
|
|
51
|
-
}
|
|
6
|
+
export type {
|
|
7
|
+
WorkerRequest,
|
|
8
|
+
WorkerResponse,
|
|
9
|
+
CloudflareWorkerConfig,
|
|
10
|
+
} from '../../../domain/entities/worker.entity';
|
|
@@ -1,73 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Workflows Domain Entities
|
|
3
|
-
*
|
|
3
|
+
* Re-exports from central domain entities for consistency
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
handler: string;
|
|
13
|
-
timeout?: number;
|
|
14
|
-
retryPolicy?: {
|
|
15
|
-
maxAttempts: number;
|
|
16
|
-
backoffMultiplier: number;
|
|
17
|
-
initialDelay: number;
|
|
18
|
-
maxDelay: number;
|
|
19
|
-
};
|
|
20
|
-
dependencies?: string[];
|
|
21
|
-
inputs?: Record<string, unknown>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Workflow definition
|
|
26
|
-
*/
|
|
27
|
-
export interface WorkflowDefinition {
|
|
28
|
-
id: string;
|
|
29
|
-
name: string;
|
|
30
|
-
description?: string;
|
|
31
|
-
version?: string;
|
|
32
|
-
steps: WorkflowStep[];
|
|
33
|
-
retryConfig?: {
|
|
34
|
-
maxAttempts: number;
|
|
35
|
-
backoffMultiplier: number;
|
|
36
|
-
initialDelay: number;
|
|
37
|
-
maxDelay: number;
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Workflow execution state
|
|
43
|
-
*/
|
|
44
|
-
export interface WorkflowExecution {
|
|
45
|
-
id: string;
|
|
46
|
-
workflowId: string;
|
|
47
|
-
status: 'pending' | 'running' | 'completed' | 'failed' | 'retrying';
|
|
48
|
-
currentStep?: string;
|
|
49
|
-
startedAt: number;
|
|
50
|
-
completedAt?: number;
|
|
51
|
-
input: unknown;
|
|
52
|
-
output?: unknown;
|
|
53
|
-
outputs?: Record<string, unknown>;
|
|
54
|
-
error?: string;
|
|
55
|
-
completedSteps: string[];
|
|
56
|
-
failedSteps: string[];
|
|
57
|
-
inputs: Record<string, unknown>;
|
|
58
|
-
retryCount: number;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Workflow config
|
|
63
|
-
*/
|
|
64
|
-
export interface CloudflareWorkflowConfig {
|
|
65
|
-
enabled: boolean;
|
|
66
|
-
maxExecutionTime: number;
|
|
67
|
-
defaultRetries: number;
|
|
68
|
-
workflows?: Record<string, WorkflowDefinition>;
|
|
69
|
-
storage?: 'kv' | 'd1';
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Type alias for compatibility
|
|
73
|
-
export type WorkflowConfig = CloudflareWorkflowConfig;
|
|
6
|
+
export type {
|
|
7
|
+
WorkflowStep,
|
|
8
|
+
WorkflowDefinition,
|
|
9
|
+
WorkflowExecution,
|
|
10
|
+
CloudflareWorkflowConfig,
|
|
11
|
+
} from '../../../domain/entities/worker.entity';
|
|
@@ -167,10 +167,13 @@ export class WorkflowService {
|
|
|
167
167
|
execution.status = 'retrying';
|
|
168
168
|
await this.saveExecution(execution);
|
|
169
169
|
|
|
170
|
-
// Exponential backoff
|
|
170
|
+
// Exponential backoff with defaults
|
|
171
|
+
const initialDelay = retryPolicy.initialDelay ?? 1000;
|
|
172
|
+
const backoffMultiplier = retryPolicy.backoffMultiplier ?? 2;
|
|
173
|
+
const maxDelay = retryPolicy.maxDelay ?? 30000;
|
|
171
174
|
const delay = Math.min(
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
initialDelay * Math.pow(backoffMultiplier, execution.retryCount),
|
|
176
|
+
maxDelay
|
|
174
177
|
);
|
|
175
178
|
await this.sleep(delay);
|
|
176
179
|
|
|
@@ -202,7 +205,8 @@ export class WorkflowService {
|
|
|
202
205
|
): Promise<unknown> {
|
|
203
206
|
// This would call the actual handler function
|
|
204
207
|
// For now, simulate execution
|
|
205
|
-
const
|
|
208
|
+
const handlerId = typeof step.handler === 'string' ? step.handler : 'anonymous';
|
|
209
|
+
const handler = this.getHandler(handlerId);
|
|
206
210
|
if (!handler) {
|
|
207
211
|
throw new Error(`Handler ${step.handler} not found`);
|
|
208
212
|
}
|
|
@@ -16,6 +16,9 @@ import type {
|
|
|
16
16
|
D1DatabaseInfo,
|
|
17
17
|
SecretInfo,
|
|
18
18
|
WorkerVersionInfo,
|
|
19
|
+
PagesProjectInfo,
|
|
20
|
+
PagesDeploymentInfo,
|
|
21
|
+
PagesDeployOptions,
|
|
19
22
|
} from '../entities';
|
|
20
23
|
import type { IWranglerService } from '../types/service.interface';
|
|
21
24
|
|
|
@@ -144,8 +147,8 @@ export class WranglerService implements IWranglerService {
|
|
|
144
147
|
return this.nodeNotAvailable<void>();
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
//
|
|
148
|
-
async tail(_options?: WranglerCommandOptions & { format?:
|
|
150
|
+
// Monitoring
|
|
151
|
+
async tail(_options?: WranglerCommandOptions & { format?: 'pretty' | 'json' }): Promise<WranglerResult<void>> {
|
|
149
152
|
return this.nodeNotAvailable<void>();
|
|
150
153
|
}
|
|
151
154
|
|
|
@@ -163,6 +166,23 @@ export class WranglerService implements IWranglerService {
|
|
|
163
166
|
return this.nodeNotAvailable<string>();
|
|
164
167
|
}
|
|
165
168
|
|
|
169
|
+
// Pages operations
|
|
170
|
+
async pagesProjectCreate(_projectName: string, _options?: WranglerCommandOptions & { productionBranch?: string }): Promise<WranglerResult<PagesProjectInfo>> {
|
|
171
|
+
return this.nodeNotAvailable<PagesProjectInfo>();
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async pagesProjectList(_options?: WranglerCommandOptions): Promise<WranglerResult<PagesProjectInfo[]>> {
|
|
175
|
+
return this.nodeNotAvailable<PagesProjectInfo[]>();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async pagesDeploy(_options: PagesDeployOptions & WranglerCommandOptions): Promise<WranglerResult<PagesDeploymentInfo>> {
|
|
179
|
+
return this.nodeNotAvailable<PagesDeploymentInfo>();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async pagesFunctionCreate(_projectName: string, _functionName: string, _options?: WranglerCommandOptions): Promise<WranglerResult<void>> {
|
|
183
|
+
return this.nodeNotAvailable<void>();
|
|
184
|
+
}
|
|
185
|
+
|
|
166
186
|
private nodeNotAvailable<T>(): never {
|
|
167
187
|
throw new Error(
|
|
168
188
|
'WranglerService requires Node.js runtime. ' +
|
|
@@ -55,6 +55,10 @@ export interface IWranglerService {
|
|
|
55
55
|
value: string,
|
|
56
56
|
options?: WranglerCommandOptions
|
|
57
57
|
): Promise<WranglerResult<void>>;
|
|
58
|
+
kvKeyList(
|
|
59
|
+
namespaceId: string,
|
|
60
|
+
options?: WranglerCommandOptions
|
|
61
|
+
): Promise<WranglerResult<string[]>>;
|
|
58
62
|
kvKeyGet(
|
|
59
63
|
namespaceId: string,
|
|
60
64
|
key: string,
|
|
@@ -27,47 +27,24 @@ export interface CloudflareMiddlewareEnv {
|
|
|
27
27
|
export type Env = CloudflareMiddlewareEnv;
|
|
28
28
|
|
|
29
29
|
// ============================================================
|
|
30
|
-
//
|
|
30
|
+
// Additional Middleware Functions
|
|
31
31
|
// ============================================================
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* Compression Middleware
|
|
35
|
+
* Note: Cloudflare handles compression automatically at the edge
|
|
35
36
|
*/
|
|
36
37
|
export async function compression(
|
|
37
38
|
request: Request,
|
|
38
39
|
response: Response
|
|
39
40
|
): Promise<Response> {
|
|
40
|
-
|
|
41
|
-
const contentType = response.headers.get('Content-Type') || '';
|
|
42
|
-
|
|
43
|
-
// Check if client accepts compression
|
|
44
|
-
if (!acceptEncoding.includes('gzip') && !acceptEncoding.includes('br')) {
|
|
45
|
-
return response;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Check if content type is compressible
|
|
49
|
-
const compressibleTypes = [
|
|
50
|
-
'text/',
|
|
51
|
-
'application/json',
|
|
52
|
-
'application/javascript',
|
|
53
|
-
'application/xml',
|
|
54
|
-
'application/xhtml+xml',
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
const isCompressible = compressibleTypes.some((type) =>
|
|
58
|
-
contentType.includes(type)
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
if (!isCompressible) {
|
|
62
|
-
return response;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Return original response (Cloudflare handles compression automatically)
|
|
41
|
+
// Cloudflare automatically handles compression
|
|
66
42
|
return response;
|
|
67
43
|
}
|
|
68
44
|
|
|
69
45
|
/**
|
|
70
46
|
* Security Headers Middleware
|
|
47
|
+
* @deprecated Use addSecurityHeaders from '@umituz/web-cloudflare/middleware' instead
|
|
71
48
|
*/
|
|
72
49
|
export interface SecurityHeadersConfig {
|
|
73
50
|
frameGuard?: boolean;
|
|
@@ -155,6 +132,7 @@ export async function detectBot(request: Request): Promise<{
|
|
|
155
132
|
|
|
156
133
|
/**
|
|
157
134
|
* Request Logging Middleware
|
|
135
|
+
* @deprecated Use LogConfig from '@umituz/web-cloudflare/middleware' instead
|
|
158
136
|
*/
|
|
159
137
|
export interface LogConfig {
|
|
160
138
|
level: 'debug' | 'info' | 'warn' | 'error';
|
|
@@ -212,14 +190,22 @@ export async function trackResponseTime(
|
|
|
212
190
|
const response = await handler();
|
|
213
191
|
const duration = Date.now() - start;
|
|
214
192
|
|
|
215
|
-
//
|
|
216
|
-
response.headers
|
|
193
|
+
// Create new response with timing header since Response headers are immutable
|
|
194
|
+
const headers = new Headers(response.headers);
|
|
195
|
+
headers.set('X-Response-Time', `${duration}ms`);
|
|
196
|
+
|
|
197
|
+
const newResponse = new Response(response.body, {
|
|
198
|
+
status: response.status,
|
|
199
|
+
statusText: response.statusText,
|
|
200
|
+
headers,
|
|
201
|
+
});
|
|
217
202
|
|
|
218
|
-
return { response, duration };
|
|
203
|
+
return { response: newResponse, duration };
|
|
219
204
|
}
|
|
220
205
|
|
|
221
206
|
/**
|
|
222
207
|
* IP Filter Middleware
|
|
208
|
+
* @deprecated Use IPFilterConfig from '@umituz/web-cloudflare/middleware' instead
|
|
223
209
|
*/
|
|
224
210
|
export interface IPFilterConfig {
|
|
225
211
|
mode: 'whitelist' | 'blacklist';
|
|
@@ -261,7 +247,6 @@ export function methodOverride(request: Request): Request {
|
|
|
261
247
|
const overrideMethod = method || bodyMethod;
|
|
262
248
|
|
|
263
249
|
if (overrideMethod && ['PUT', 'PATCH', 'DELETE'].includes(overrideMethod.toUpperCase())) {
|
|
264
|
-
// Return modified request
|
|
265
250
|
return new Request(request.url, {
|
|
266
251
|
method: overrideMethod.toUpperCase(),
|
|
267
252
|
headers: request.headers,
|
|
@@ -282,48 +267,32 @@ export function addRequestID(request: Request): string {
|
|
|
282
267
|
return existingID;
|
|
283
268
|
}
|
|
284
269
|
|
|
285
|
-
|
|
286
|
-
return requestID;
|
|
270
|
+
return crypto.randomUUID();
|
|
287
271
|
}
|
|
288
272
|
|
|
289
273
|
/**
|
|
290
|
-
*
|
|
274
|
+
* Current Time Endpoint
|
|
291
275
|
*/
|
|
292
|
-
export function
|
|
293
|
-
|
|
276
|
+
export function getCurrentTimeResponse(): Response {
|
|
277
|
+
return new Response(JSON.stringify({ time: Date.now() }), {
|
|
294
278
|
headers: { 'Content-Type': 'application/json' },
|
|
295
279
|
});
|
|
296
|
-
return response;
|
|
297
280
|
}
|
|
298
281
|
|
|
299
282
|
/**
|
|
300
283
|
* Health Check Middleware
|
|
284
|
+
* @deprecated Use HealthCheckConfig from '@umituz/web-cloudflare/middleware' instead
|
|
301
285
|
*/
|
|
302
286
|
export interface HealthCheckConfig {
|
|
303
|
-
uptime
|
|
304
|
-
checks
|
|
287
|
+
uptime?: number;
|
|
288
|
+
checks?: Record<string, () => Promise<boolean>>;
|
|
305
289
|
}
|
|
306
290
|
|
|
307
291
|
export async function healthCheck(
|
|
308
292
|
env: CloudflareMiddlewareEnv,
|
|
309
293
|
config?: HealthCheckConfig
|
|
310
294
|
): Promise<Response> {
|
|
311
|
-
|
|
312
|
-
let uptime = 0;
|
|
313
|
-
if (config?.uptime !== undefined) {
|
|
314
|
-
uptime = config.uptime;
|
|
315
|
-
} else {
|
|
316
|
-
// Try to get process uptime in Node.js environment
|
|
317
|
-
try {
|
|
318
|
-
// @ts-ignore - process is not available in Workers runtime
|
|
319
|
-
if (typeof process !== 'undefined' && process?.uptime) {
|
|
320
|
-
// @ts-ignore
|
|
321
|
-
uptime = process.uptime();
|
|
322
|
-
}
|
|
323
|
-
} catch {
|
|
324
|
-
uptime = 0;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
295
|
+
let uptime = config?.uptime ?? 0;
|
|
327
296
|
|
|
328
297
|
const checks: Record<string, boolean | string> = {
|
|
329
298
|
healthy: true,
|
|
@@ -348,15 +317,16 @@ export async function healthCheck(
|
|
|
348
317
|
|
|
349
318
|
/**
|
|
350
319
|
* Error Handling Middleware
|
|
320
|
+
* @deprecated Use ErrorHandlerConfig from '@umituz/web-cloudflare/middleware' instead
|
|
351
321
|
*/
|
|
352
322
|
export interface ErrorHandlerConfig {
|
|
353
|
-
debug
|
|
323
|
+
debug?: boolean;
|
|
354
324
|
logger?: (error: Error) => void;
|
|
355
325
|
}
|
|
356
326
|
|
|
357
327
|
export function handleMiddlewareError(
|
|
358
328
|
error: Error,
|
|
359
|
-
config: ErrorHandlerConfig = {
|
|
329
|
+
config: ErrorHandlerConfig = {}
|
|
360
330
|
): Response {
|
|
361
331
|
if (config.logger) {
|
|
362
332
|
config.logger(error);
|