@umituz/web-cloudflare 1.4.6 → 1.4.7

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.
@@ -318,10 +318,27 @@ export async function healthCheck(
318
318
  env: CloudflareMiddlewareEnv,
319
319
  config?: HealthCheckConfig
320
320
  ): Promise<Response> {
321
+ // Get uptime if available (Node.js only)
322
+ let uptime = 0;
323
+ if (config?.uptime !== undefined) {
324
+ uptime = config.uptime;
325
+ } else {
326
+ // Try to get process uptime in Node.js environment
327
+ try {
328
+ // @ts-ignore - process is not available in Workers runtime
329
+ if (typeof process !== 'undefined' && process?.uptime) {
330
+ // @ts-ignore
331
+ uptime = process.uptime();
332
+ }
333
+ } catch {
334
+ uptime = 0;
335
+ }
336
+ }
337
+
321
338
  const checks: Record<string, boolean | string> = {
322
339
  healthy: true,
323
340
  timestamp: new Date().toISOString(),
324
- uptime: config?.uptime || process.uptime?.() || 0,
341
+ uptime: uptime.toString(),
325
342
  };
326
343
 
327
344
  if (config?.checks) {
@@ -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 any)[key];
640
+
641
+ if (isObject(sourceValue)) {
642
+ if (!targetValue) {
643
+ (target as any)[key] = {};
644
+ }
645
+ deepMerge((target as any)[key], sourceValue);
638
646
  } else {
639
- Object.assign(target, { [key]: source[key] });
647
+ (target as any)[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 any)[key] = obj[key];
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