@umituz/web-cloudflare 1.0.1

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.
Files changed (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +621 -0
  3. package/package.json +87 -0
  4. package/src/config/patterns.ts +469 -0
  5. package/src/config/types.ts +648 -0
  6. package/src/domain/entities/analytics.entity.ts +47 -0
  7. package/src/domain/entities/d1.entity.ts +37 -0
  8. package/src/domain/entities/image.entity.ts +48 -0
  9. package/src/domain/entities/index.ts +11 -0
  10. package/src/domain/entities/kv.entity.ts +34 -0
  11. package/src/domain/entities/r2.entity.ts +55 -0
  12. package/src/domain/entities/worker.entity.ts +35 -0
  13. package/src/domain/index.ts +7 -0
  14. package/src/domain/interfaces/index.ts +6 -0
  15. package/src/domain/interfaces/services.interface.ts +82 -0
  16. package/src/index.ts +53 -0
  17. package/src/infrastructure/constants/index.ts +13 -0
  18. package/src/infrastructure/domain/ai-gateway.entity.ts +169 -0
  19. package/src/infrastructure/domain/workflows.entity.ts +108 -0
  20. package/src/infrastructure/middleware/index.ts +405 -0
  21. package/src/infrastructure/router/index.ts +549 -0
  22. package/src/infrastructure/services/ai-gateway/index.ts +416 -0
  23. package/src/infrastructure/services/analytics/analytics.service.ts +189 -0
  24. package/src/infrastructure/services/analytics/index.ts +7 -0
  25. package/src/infrastructure/services/d1/d1.service.ts +191 -0
  26. package/src/infrastructure/services/d1/index.ts +7 -0
  27. package/src/infrastructure/services/images/images.service.ts +227 -0
  28. package/src/infrastructure/services/images/index.ts +7 -0
  29. package/src/infrastructure/services/kv/index.ts +7 -0
  30. package/src/infrastructure/services/kv/kv.service.ts +116 -0
  31. package/src/infrastructure/services/r2/index.ts +7 -0
  32. package/src/infrastructure/services/r2/r2.service.ts +164 -0
  33. package/src/infrastructure/services/workers/index.ts +7 -0
  34. package/src/infrastructure/services/workers/workers.service.ts +164 -0
  35. package/src/infrastructure/services/workflows/index.ts +437 -0
  36. package/src/infrastructure/utils/helpers.ts +732 -0
  37. package/src/infrastructure/utils/index.ts +6 -0
  38. package/src/infrastructure/utils/utils.util.ts +150 -0
  39. package/src/presentation/hooks/cloudflare.hooks.ts +314 -0
  40. package/src/presentation/hooks/index.ts +6 -0
  41. package/src/worker.example.ts +41 -0
@@ -0,0 +1,164 @@
1
+ /**
2
+ * R2 Service
3
+ * @description Cloudflare R2 object storage operations
4
+ */
5
+
6
+ import type { R2Object, R2ListOptions, R2ListResult, R2PutOptions, R2PresignedURL } from "../../../domain/entities/r2.entity";
7
+ import type { IR2Service } from "../../../domain/interfaces/services.interface";
8
+ import { validationUtils } from "../../utils";
9
+
10
+ export interface R2UploadOptions extends R2PutOptions {
11
+ readonly binding?: string;
12
+ }
13
+
14
+ export interface R2CacheOptions {
15
+ readonly bucket: string;
16
+ readonly customDomain?: string;
17
+ }
18
+
19
+ class R2Service implements IR2Service {
20
+ private buckets: Map<string, R2Bucket> = new Map();
21
+ private customDomain: string | null = null;
22
+
23
+ initialize(options: R2CacheOptions): void {
24
+ this.customDomain = options.customDomain ?? null;
25
+ }
26
+
27
+ bindBucket(name: string, bucket: R2Bucket): void {
28
+ this.buckets.set(name, bucket);
29
+ }
30
+
31
+ private getBucket(binding?: string): R2Bucket {
32
+ const name = binding || "default";
33
+ const bucket = this.buckets.get(name);
34
+ if (!bucket) {
35
+ throw new Error(`R2 bucket "${name}" not bound`);
36
+ }
37
+ return bucket;
38
+ }
39
+
40
+ async get(key: string, binding?: string): Promise<R2Object | null> {
41
+ if (!validationUtils.isValidR2Key(key)) {
42
+ throw new Error(`Invalid R2 key: ${key}`);
43
+ }
44
+
45
+ const bucket = this.getBucket(binding);
46
+ const object = await bucket.get(key);
47
+
48
+ if (!object) return null;
49
+
50
+ return {
51
+ key: object.key,
52
+ size: object.size,
53
+ uploaded: object.uploaded,
54
+ httpMetadata: object.httpMetadata,
55
+ customMetadata: object.customMetadata,
56
+ };
57
+ }
58
+
59
+ async put(
60
+ key: string,
61
+ data: ReadableStream | ArrayBuffer | string,
62
+ options?: R2UploadOptions
63
+ ): Promise<void> {
64
+ if (!validationUtils.isValidR2Key(key)) {
65
+ throw new Error(`Invalid R2 key: ${key}`);
66
+ }
67
+
68
+ const bucket = this.getBucket(options?.binding);
69
+
70
+ await bucket.put(key, data, {
71
+ httpMetadata: options?.httpMetadata,
72
+ customMetadata: options?.customMetadata,
73
+ checksum: options?.checksum,
74
+ });
75
+ }
76
+
77
+ async delete(key: string, binding?: string): Promise<boolean> {
78
+ if (!validationUtils.isValidR2Key(key)) {
79
+ throw new Error(`Invalid R2 key: ${key}`);
80
+ }
81
+
82
+ const bucket = this.getBucket(binding);
83
+ await bucket.delete(key);
84
+
85
+ return true;
86
+ }
87
+
88
+ async list(options?: R2ListOptions & { binding?: string }): Promise<R2ListResult> {
89
+ const bucket = this.getBucket(options?.binding);
90
+ const listed = await bucket.list({
91
+ limit: options?.limit,
92
+ prefix: options?.prefix,
93
+ cursor: options?.cursor,
94
+ });
95
+
96
+ return {
97
+ objects: listed.objects,
98
+ truncated: listed.truncated,
99
+ cursor: listed.cursor,
100
+ };
101
+ }
102
+
103
+ async getPresignedURL(key: string, expiresIn = 3600, binding?: string): Promise<R2PresignedURL> {
104
+ // Note: R2 presigned URLs require AWS S3 signature
105
+ // This would typically use the AWS SDK or custom signing logic
106
+ // For now, return a placeholder
107
+
108
+ const expiresAt = new Date(Date.now() + expiresIn * 1000);
109
+
110
+ return {
111
+ url: `https://presigned-url-placeholder/${key}?expires=${expiresAt.getTime()}`,
112
+ expiresAt,
113
+ };
114
+ }
115
+
116
+ /**
117
+ * Upload helpers
118
+ */
119
+ async uploadFile(file: File, key?: string, options?: R2UploadOptions, binding?: string): Promise<void> {
120
+ const objectKey = key || `uploads/${Date.now()}-${file.name}`;
121
+
122
+ await this.put(objectKey, file.stream(), {
123
+ ...options,
124
+ httpMetadata: {
125
+ contentType: file.type,
126
+ ...options?.httpMetadata,
127
+ },
128
+ });
129
+ }
130
+
131
+ async uploadFromURL(url: string, key: string, binding?: string): Promise<void> {
132
+ const response = await fetch(url);
133
+ if (!response.ok) {
134
+ throw new Error(`Failed to fetch URL: ${response.statusText}`);
135
+ }
136
+
137
+ await this.put(key, response.body, {
138
+ httpMetadata: {
139
+ contentType: response.headers.get("Content-Type") || undefined,
140
+ },
141
+ });
142
+ }
143
+
144
+ /**
145
+ * Delete helpers
146
+ */
147
+ async deleteMultiple(keys: readonly string[], binding?: string): Promise<void> {
148
+ const bucket = this.getBucket(binding);
149
+
150
+ await Promise.all(keys.map((key) => bucket.delete(key)));
151
+ }
152
+
153
+ async deletePrefix(prefix: string, binding?: string): Promise<void> {
154
+ const list = await this.list({ prefix, binding });
155
+
156
+ await this.deleteMultiple(list.objects.map((obj) => obj.key), binding);
157
+
158
+ if (list.truncated && list.cursor) {
159
+ await this.deletePrefix(prefix, binding);
160
+ }
161
+ }
162
+ }
163
+
164
+ export const r2Service = new R2Service();
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Cloudflare Workers Service
3
+ * Subpath: @umituz/web-cloudflare/workers
4
+ */
5
+
6
+ export { WorkersService, workersService } from "./workers.service";
7
+ export type { WorkerFetchOptions, WorkerRouteConfig, WorkersMiddleware } from "./workers.service";
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Workers Service
3
+ * @description Cloudflare Workers HTTP handler and routing
4
+ */
5
+
6
+ import type { WorkerRequest, WorkerResponse, WorkerConfig } from "../../../domain/entities/worker.entity";
7
+ import type { Env } from "../../../domain/interfaces/services.interface";
8
+
9
+ export interface WorkerFetchOptions {
10
+ readonly cache?: CacheControls;
11
+ readonly cors?: CorsOptions;
12
+ readonly timeout?: number;
13
+ }
14
+
15
+ export interface CacheControls {
16
+ readonly maxAge?: number;
17
+ readonly immutable?: boolean;
18
+ readonly noCache?: boolean;
19
+ }
20
+
21
+ export interface CorsOptions {
22
+ readonly origins?: readonly string[];
23
+ readonly methods?: readonly string[];
24
+ readonly headers?: readonly string[];
25
+ readonly credentials?: boolean;
26
+ }
27
+
28
+ export interface WorkerRouteConfig {
29
+ readonly pattern: URLPattern;
30
+ readonly handler: (request: WorkerRequest, env?: Env) => Promise<WorkerResponse>;
31
+ }
32
+
33
+ export type WorkersMiddleware = (
34
+ request: WorkerRequest,
35
+ env?: Env
36
+ ) => Promise<WorkerResponse | null>;
37
+
38
+ class WorkersService {
39
+ private routes: WorkerRouteConfig[] = [];
40
+ private middleware: WorkersMiddleware[] = [];
41
+ private cache: Cache | null = null;
42
+
43
+ /**
44
+ * Register a route
45
+ */
46
+ route(pattern: string, handler: WorkerRouteConfig["handler"]): void {
47
+ this.routes.push({
48
+ pattern: new URLPattern({ pathname: pattern }),
49
+ handler,
50
+ });
51
+ }
52
+
53
+ /**
54
+ * Add middleware
55
+ */
56
+ use(middleware: WorkersMiddleware): void {
57
+ this.middleware.push(middleware);
58
+ }
59
+
60
+ /**
61
+ * Fetch handler
62
+ */
63
+ async fetch(request: WorkerRequest, env?: Env, ctx?: ExecutionContext): Promise<WorkerResponse> {
64
+ // Initialize cache
65
+ if (!this.cache && env) {
66
+ this.cache = caches.default;
67
+ }
68
+
69
+ // Try middleware
70
+ for (const mw of this.middleware) {
71
+ const response = await mw(request, env);
72
+ if (response) return response;
73
+ }
74
+
75
+ // Match route
76
+ const url = new URL(request.url);
77
+ for (const route of this.routes) {
78
+ if (route.pattern.test(url)) {
79
+ return route.handler(request, env);
80
+ }
81
+ }
82
+
83
+ // 404
84
+ return new Response("Not Found", { status: 404 });
85
+ }
86
+
87
+ /**
88
+ * JSON response helper
89
+ */
90
+ json(data: unknown, status = 200): WorkerResponse {
91
+ return new Response(JSON.stringify(data), {
92
+ status,
93
+ headers: {
94
+ "Content-Type": "application/json",
95
+ },
96
+ });
97
+ }
98
+
99
+ /**
100
+ * Error response helper
101
+ */
102
+ error(message: string, status = 500): WorkerResponse {
103
+ return this.json({ error: message }, status);
104
+ }
105
+
106
+ /**
107
+ * CORS response helper
108
+ */
109
+ cors(options: CorsOptions = {}): WorkerResponse {
110
+ const headers = new Headers();
111
+
112
+ if (options.origins) {
113
+ const origin = options.origins.join(", ");
114
+ headers.set("Access-Control-Allow-Origin", origin);
115
+ }
116
+
117
+ if (options.methods) {
118
+ headers.set("Access-Control-Allow-Methods", options.methods.join(", "));
119
+ }
120
+
121
+ if (options.headers) {
122
+ headers.set("Access-Control-Allow-Headers", options.headers.join(", "));
123
+ }
124
+
125
+ if (options.credentials) {
126
+ headers.set("Access-Control-Allow-Credentials", "true");
127
+ }
128
+
129
+ return new Response(null, { headers });
130
+ }
131
+
132
+ /**
133
+ * Cache response helper
134
+ */
135
+ async cached(request: Request, ttl: number, fetcher: () => Promise<Response>): Promise<Response> {
136
+ if (!this.cache) return fetcher();
137
+
138
+ const cacheKey = new Request(request.url, request);
139
+ let response = await this.cache.match(cacheKey);
140
+
141
+ if (!response) {
142
+ response = await fetcher();
143
+ response = new Response(response.body, response);
144
+ response.headers.set("Cache-Control", `public, max-age=${ttl}`);
145
+ await this.cache.put(cacheKey, response.clone());
146
+ }
147
+
148
+ return response;
149
+ }
150
+
151
+ /**
152
+ * Redirect response helper
153
+ */
154
+ redirect(url: string, status = 302): WorkerResponse {
155
+ return new Response(null, {
156
+ status,
157
+ headers: {
158
+ Location: url,
159
+ },
160
+ });
161
+ }
162
+ }
163
+
164
+ export const workersService = new WorkersService();