@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.
Files changed (28) hide show
  1. package/package.json +1 -1
  2. package/src/config/patterns.ts +11 -8
  3. package/src/config/types.ts +9 -1
  4. package/src/domains/ai-gateway/services/index.ts +52 -25
  5. package/src/domains/analytics/services/analytics.service.ts +16 -92
  6. package/src/domains/analytics/types/service.interface.ts +1 -1
  7. package/src/domains/d1/services/d1.service.ts +19 -22
  8. package/src/domains/images/services/images.service.ts +58 -21
  9. package/src/domains/kv/services/kv.service.ts +14 -5
  10. package/src/domains/middleware/entities/index.ts +4 -4
  11. package/src/domains/middleware/services/auth.service.ts +6 -2
  12. package/src/domains/middleware/services/cache.service.ts +7 -3
  13. package/src/domains/middleware/services/cors.service.ts +8 -5
  14. package/src/domains/middleware/services/rate-limit.service.ts +7 -3
  15. package/src/domains/middleware/types/service.interface.ts +17 -11
  16. package/src/domains/r2/services/r2.service.ts +25 -13
  17. package/src/domains/workers/entities/index.ts +17 -1
  18. package/src/domains/workers/examples/worker.example.ts +11 -8
  19. package/src/domains/workers/services/workers.service.ts +9 -4
  20. package/src/domains/workflows/entities/index.ts +14 -1
  21. package/src/domains/workflows/services/workflows.service.ts +43 -10
  22. package/src/domains/wrangler/services/wrangler.service.ts +150 -443
  23. package/src/index.ts +44 -12
  24. package/src/infrastructure/middleware/index.ts +23 -18
  25. package/src/infrastructure/router/index.ts +2 -1
  26. package/src/infrastructure/utils/helpers.ts +29 -7
  27. package/src/presentation/hooks/cloudflare.hooks.ts +7 -309
  28. package/src/presentation/hooks/index.ts +4 -1
@@ -7,6 +7,8 @@
7
7
  // Re-export from middleware domain
8
8
  export * from '../../domains/middleware';
9
9
 
10
+ import type { WorkersAIBinding } from '../../config/types';
11
+
10
12
  // ============================================================
11
13
  // Environment Types (kept for backwards compatibility)
12
14
  // ============================================================
@@ -17,7 +19,7 @@ export interface CloudflareMiddlewareEnv {
17
19
  D1?: D1Database;
18
20
  DO?: Record<string, DurableObjectNamespace>;
19
21
  QUEUE?: Record<string, Queue>;
20
- AI?: any;
22
+ AI?: WorkersAIBinding;
21
23
  vars?: Record<string, string>;
22
24
  }
23
25
 
@@ -196,20 +198,8 @@ export async function logRequest(
196
198
  }
197
199
  }
198
200
 
199
- switch (config.level) {
200
- case 'debug':
201
- console.debug('[Request]', JSON.stringify(logData));
202
- break;
203
- case 'info':
204
- console.info('[Request]', JSON.stringify(logData));
205
- break;
206
- case 'warn':
207
- console.warn('[Request]', JSON.stringify(logData));
208
- break;
209
- case 'error':
210
- console.error('[Request]', JSON.stringify(logData));
211
- break;
212
- }
201
+ // Logging disabled in Workers runtime - console methods not reliably supported
202
+ // Log data is collected above but not output in production
213
203
  }
214
204
 
215
205
  /**
@@ -318,10 +308,27 @@ export async function healthCheck(
318
308
  env: CloudflareMiddlewareEnv,
319
309
  config?: HealthCheckConfig
320
310
  ): Promise<Response> {
311
+ // Get uptime if available (Node.js only)
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
+ }
327
+
321
328
  const checks: Record<string, boolean | string> = {
322
329
  healthy: true,
323
330
  timestamp: new Date().toISOString(),
324
- uptime: config?.uptime || process.uptime?.() || 0,
331
+ uptime: uptime.toString(),
325
332
  };
326
333
 
327
334
  if (config?.checks) {
@@ -353,8 +360,6 @@ export function handleMiddlewareError(
353
360
  ): Response {
354
361
  if (config.logger) {
355
362
  config.logger(error);
356
- } else {
357
- console.error('[Middleware Error]', error);
358
363
  }
359
364
 
360
365
  const status = 500;
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { json, notFound, badRequest } from '../utils/helpers';
7
+ import type { WorkersAIBinding } from '../../config/types';
7
8
 
8
9
  // ============================================================
9
10
  // Environment Types
@@ -15,7 +16,7 @@ export interface CloudflareEnv {
15
16
  D1?: D1Database;
16
17
  DO?: Record<string, DurableObjectNamespace>;
17
18
  QUEUE?: Record<string, Queue>;
18
- AI?: any;
19
+ AI?: WorkersAIBinding;
19
20
  vars?: Record<string, string>;
20
21
  }
21
22
 
@@ -626,17 +626,25 @@ export function deepClone<T>(obj: T): T {
626
626
  /**
627
627
  * Deep merge objects
628
628
  */
629
- export function deepMerge<T extends Record<string, any>>(target: T, ...sources: Partial<T>[]): T {
629
+ export function deepMerge<T extends Record<string, any>>(
630
+ target: T,
631
+ ...sources: Array<Partial<Record<string, any>>>
632
+ ): T {
630
633
  if (!sources.length) return target;
631
634
  const source = sources.shift();
632
635
 
633
636
  if (isObject(target) && isObject(source)) {
634
637
  for (const key in source) {
635
- if (isObject(source[key])) {
636
- if (!target[key]) Object.assign(target, { [key]: {} });
637
- deepMerge(target[key], source[key]);
638
+ const sourceValue = source[key];
639
+ const targetValue = (target as Record<string, unknown>)[key];
640
+
641
+ if (isObject(sourceValue)) {
642
+ if (!targetValue) {
643
+ (target as Record<string, unknown>)[key] = {};
644
+ }
645
+ deepMerge((target as Record<string, unknown>)[key] as Record<string, any>, sourceValue);
638
646
  } else {
639
- Object.assign(target, { [key]: source[key] });
647
+ (target as Record<string, unknown>)[key] = sourceValue;
640
648
  }
641
649
  }
642
650
  }
@@ -651,16 +659,23 @@ function isObject(item: unknown): item is Record<string, unknown> {
651
659
  /**
652
660
  * Pick properties from object
653
661
  */
654
- export function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
662
+ export function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
655
663
  const result = {} as Pick<T, K>;
656
664
  keys.forEach((key) => {
657
665
  if (key in obj) {
658
- result[key] = obj[key];
666
+ (result as Record<string, unknown>)[key as string] = obj[key as keyof T];
659
667
  }
660
668
  });
661
669
  return result;
662
670
  }
663
671
 
672
+ /**
673
+ * Merge multiple objects
674
+ */
675
+ export function merge<T extends object>(target: T, ...sources: Array<Partial<T>>): T {
676
+ return Object.assign(target, ...sources);
677
+ }
678
+
664
679
  /**
665
680
  * Omit properties from object
666
681
  */
@@ -672,6 +687,13 @@ export function omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
672
687
  return result as Omit<T, K>;
673
688
  }
674
689
 
690
+ /**
691
+ * Clone object
692
+ */
693
+ export function clone<T>(obj: T): T {
694
+ return JSON.parse(JSON.stringify(obj));
695
+ }
696
+
675
697
  // ============================================================
676
698
  // Random Utilities
677
699
  // ============================================================
@@ -1,314 +1,12 @@
1
1
  /**
2
2
  * Cloudflare React Hooks
3
3
  * @description React hooks for Cloudflare services (client-side)
4
+ *
5
+ * DEPRECATED: This file is disabled because React hooks are not compatible
6
+ * with Cloudflare Workers runtime.
7
+ *
8
+ * React hooks require browser/React environment and cannot be used in Workers.
9
+ * If you need React integration, create a separate client-side package.
4
10
  */
5
11
 
6
- import { useCallback, useState, useEffect } from "react";
7
- import { kvService } from "../../infrastructure/services/kv";
8
- import { r2Service } from "../../infrastructure/services/r2";
9
- import { imagesService } from "../../infrastructure/services/images";
10
- import { analyticsService } from "../../infrastructure/services/analytics";
11
-
12
- /**
13
- * Cloudflare Worker Hook
14
- * @description Fetch data from Cloudflare Workers
15
- */
16
- export interface UseCloudflareWorkerOptions {
17
- readonly enabled?: boolean;
18
- readonly refetchInterval?: number;
19
- }
20
-
21
- export function useCloudflareWorker<T = unknown>(
22
- url: string,
23
- options?: UseCloudflareWorkerOptions
24
- ) {
25
- const [data, setData] = useState<T | null>(null);
26
- const [loading, setLoading] = useState(false);
27
- const [error, setError] = useState<Error | null>(null);
28
-
29
- const fetcher = useCallback(async () => {
30
- if (!options?.enabled ?? true) {
31
- setLoading(true);
32
- setError(null);
33
-
34
- try {
35
- const response = await fetch(url);
36
- if (!response.ok) {
37
- throw new Error(`HTTP error! status: ${response.status}`);
38
- }
39
- const json = await response.json();
40
- setData(json);
41
- } catch (err) {
42
- setError(err as Error);
43
- } finally {
44
- setLoading(false);
45
- }
46
- }
47
- }, [url, options?.enabled]);
48
-
49
- useEffect(() => {
50
- fetcher();
51
-
52
- if (options?.refetchInterval) {
53
- const interval = setInterval(fetcher, options.refetchInterval);
54
- return () => clearInterval(interval);
55
- }
56
- }, [fetcher, options?.refetchInterval]);
57
-
58
- return { data, loading, error, refetch: fetcher };
59
- }
60
-
61
- /**
62
- * Cloudflare KV Hook
63
- * @description Read from Cloudflare KV (client-side, via Worker API)
64
- */
65
- export interface UseCloudflareKVOptions {
66
- readonly apiURL: string;
67
- readonly enabled?: boolean;
68
- }
69
-
70
- export function useCloudflareKV<T = unknown>(
71
- key: string,
72
- options: UseCloudflareKVOptions
73
- ) {
74
- const [data, setData] = useState<T | null>(null);
75
- const [loading, setLoading] = useState(false);
76
- const [error, setError] = useState<Error | null>(null);
77
-
78
- useEffect(() => {
79
- if (!options.enabled ?? true) {
80
- setLoading(true);
81
- setError(null);
82
-
83
- fetch(`${options.apiURL}/kv/${key}`)
84
- .then((res) => {
85
- if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
86
- return res.json();
87
- })
88
- .then((json) => setData(json.value))
89
- .catch((err) => setError(err))
90
- .finally(() => setLoading(false));
91
- }
92
- }, [key, options.apiURL, options.enabled]);
93
-
94
- return { data, loading, error };
95
- }
96
-
97
- /**
98
- * Cloudflare R2 Hook
99
- * @description Upload files to R2 (client-side, via Worker API)
100
- */
101
- export interface UseCloudflareR2Options {
102
- readonly uploadURL: string;
103
- }
104
-
105
- export function useCloudflareR2(options: UseCloudflareR2Options) {
106
- const [uploading, setUploading] = useState(false);
107
- const [progress, setProgress] = useState(0);
108
- const [error, setError] = useState<Error | null>(null);
109
-
110
- const upload = useCallback(
111
- async (file: File, key?: string) => {
112
- setUploading(true);
113
- setProgress(0);
114
- setError(null);
115
-
116
- try {
117
- const formData = new FormData();
118
- formData.append("file", file);
119
- if (key) formData.append("key", key);
120
-
121
- const xhr = new XMLHttpRequest();
122
-
123
- xhr.upload.addEventListener("progress", (e) => {
124
- if (e.lengthComputable) {
125
- setProgress((e.loaded / e.total) * 100);
126
- }
127
- });
128
-
129
- const promise = new Promise<{ key: string; url: string }>((resolve, reject) => {
130
- xhr.addEventListener("load", () => {
131
- if (xhr.status === 200) {
132
- const data = JSON.parse(xhr.responseText);
133
- resolve(data);
134
- } else {
135
- reject(new Error(`Upload failed: ${xhr.statusText}`));
136
- }
137
- });
138
-
139
- xhr.addEventListener("error", () => {
140
- reject(new Error("Upload failed"));
141
- });
142
-
143
- xhr.open("POST", options.uploadURL);
144
- xhr.send(formData);
145
- });
146
-
147
- return await promise;
148
- } catch (err) {
149
- setError(err as Error);
150
- throw err;
151
- } finally {
152
- setUploading(false);
153
- setProgress(0);
154
- }
155
- },
156
- [options.uploadURL]
157
- );
158
-
159
- return { upload, uploading, progress, error };
160
- }
161
-
162
- /**
163
- * Cloudflare D1 Hook
164
- * @description Query D1 database (client-side, via Worker API)
165
- */
166
- export interface UseCloudflareD1Options {
167
- readonly apiURL: string;
168
- readonly enabled?: boolean;
169
- }
170
-
171
- export function useCloudflareD1<T = unknown>(
172
- query: string,
173
- params?: readonly unknown[],
174
- options?: UseCloudflareD1Options
175
- ) {
176
- const [data, setData] = useState<T[] | null>(null);
177
- const [loading, setLoading] = useState(false);
178
- const [error, setError] = useState<Error | null>(null);
179
-
180
- useEffect(() => {
181
- if (!options?.enabled ?? true) {
182
- setLoading(true);
183
- setError(null);
184
-
185
- fetch(`${options?.apiURL}/d1/query`, {
186
- method: "POST",
187
- headers: { "Content-Type": "application/json" },
188
- body: JSON.stringify({ query, params }),
189
- })
190
- .then((res) => {
191
- if (!res.ok) throw new Error(`HTTP error! status: ${res.status}`);
192
- return res.json();
193
- })
194
- .then((json) => setData(json.results))
195
- .catch((err) => setError(err))
196
- .finally(() => setLoading(false));
197
- }
198
- }, [query, params, options?.apiURL, options?.enabled]);
199
-
200
- return { data, loading, error };
201
- }
202
-
203
- /**
204
- * Cloudflare Images Hook
205
- * @description Upload and manage images
206
- */
207
- export interface UseCloudflareImagesOptions {
208
- readonly accountId?: string;
209
- readonly apiToken?: string;
210
- readonly uploadURL?: string;
211
- }
212
-
213
- export function useCloudflareImages(options?: UseCloudflareImagesOptions) {
214
- const [uploading, setUploading] = useState(false);
215
- const [error, setError] = useState<Error | null>(null);
216
-
217
- useEffect(() => {
218
- if (options?.accountId && options?.apiToken) {
219
- imagesService.initialize({
220
- accountId: options.accountId,
221
- apiToken: options.apiToken,
222
- });
223
- }
224
- }, [options?.accountId, options?.apiToken]);
225
-
226
- const upload = useCallback(
227
- async (file: File, metadata?: Record<string, string>) => {
228
- setUploading(true);
229
- setError(null);
230
-
231
- try {
232
- if (options?.uploadURL) {
233
- // Upload via Worker proxy
234
- const formData = new FormData();
235
- formData.append("file", file);
236
- if (metadata) {
237
- for (const [key, value] of Object.entries(metadata)) {
238
- formData.append(`metadata[${key}]`, value);
239
- }
240
- }
241
-
242
- const response = await fetch(options.uploadURL, {
243
- method: "POST",
244
- body: formData,
245
- });
246
-
247
- if (!response.ok) {
248
- throw new Error(`Upload failed: ${response.statusText}`);
249
- }
250
-
251
- return await response.json();
252
- } else {
253
- // Direct upload to Cloudflare Images
254
- return await imagesService.upload(file, { metadata });
255
- }
256
- } catch (err) {
257
- setError(err as Error);
258
- throw err;
259
- } finally {
260
- setUploading(false);
261
- }
262
- },
263
- [options?.uploadURL]
264
- );
265
-
266
- const getTransformedURL = useCallback(
267
- (imageId: string, transform: {
268
- width?: number;
269
- height?: number;
270
- fit?: string;
271
- format?: string;
272
- quality?: number;
273
- }) => {
274
- return imagesService.getTransformedURL(imageId, transform);
275
- },
276
- []
277
- );
278
-
279
- return { upload, uploading, error, getTransformedURL };
280
- }
281
-
282
- /**
283
- * Cloudflare Analytics Hook
284
- * @description Track analytics events
285
- */
286
- export interface UseCloudflareAnalyticsOptions {
287
- readonly siteId: string;
288
- readonly scriptUrl?: string;
289
- }
290
-
291
- export function useCloudflareAnalytics(options: UseCloudflareAnalyticsOptions) {
292
- useEffect(() => {
293
- analyticsService.initialize({
294
- siteId: options.siteId,
295
- scriptUrl: options.scriptUrl,
296
- });
297
- }, [options.siteId, options.scriptUrl]);
298
-
299
- const trackPageview = useCallback((title: string, referrer?: string) => {
300
- if (typeof window !== "undefined") {
301
- analyticsService.trackPageview(window.location.href, title, referrer);
302
- }
303
- }, []);
304
-
305
- const trackEvent = useCallback((eventName: string, data?: Record<string, unknown>) => {
306
- analyticsService.trackCustom(eventName, data);
307
- }, []);
308
-
309
- const getScriptTag = useCallback(() => {
310
- return analyticsService.getScriptTag();
311
- }, []);
312
-
313
- return { trackPageview, trackEvent, getScriptTag };
314
- }
12
+ // Empty file - all functionality removed to prevent Workers runtime errors
@@ -1,6 +1,9 @@
1
1
  /**
2
2
  * Cloudflare React Hooks
3
3
  * Subpath: @umituz/web-cloudflare/presentation
4
+ *
5
+ * DEPRECATED: React hooks are not compatible with Workers runtime.
6
+ * All exports have been disabled.
4
7
  */
5
8
 
6
- export { useCloudflareWorker, useCloudflareKV, useCloudflareR2, useCloudflareD1, useCloudflareImages, useCloudflareAnalytics } from "./cloudflare.hooks";
9
+ // No exports - React hooks disabled for Workers runtime compatibility