@umituz/web-cloudflare 1.4.7 → 1.4.9
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/README.md +40 -2
- package/package.json +7 -2
- package/src/config/patterns.ts +9 -6
- package/src/config/types.ts +9 -1
- package/src/domains/ai-gateway/services/index.ts +36 -19
- package/src/domains/kv/services/kv.service.ts +5 -1
- package/src/domains/pages/entities/index.ts +79 -0
- package/src/domains/pages/index.ts +21 -0
- package/src/domains/pages/services/index.ts +5 -0
- package/src/domains/pages/services/pages.service.ts +144 -0
- package/src/domains/pages/types/index.ts +5 -0
- package/src/domains/pages/types/service.interface.ts +77 -0
- package/src/domains/r2/services/r2.service.ts +5 -1
- package/src/domains/workers/examples/worker.example.ts +3 -2
- package/src/domains/workers/services/workers.service.ts +4 -1
- package/src/domains/wrangler/entities/index.ts +57 -0
- package/src/domains/wrangler/types/service.interface.ts +20 -0
- package/src/index.ts +9 -0
- package/src/infrastructure/middleware/index.ts +5 -17
- package/src/infrastructure/router/index.ts +2 -1
- package/src/infrastructure/utils/helpers.ts +5 -5
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @umituz/web-cloudflare
|
|
2
2
|
|
|
3
|
-
Comprehensive Cloudflare Workers integration with config-based patterns, middleware, router, workflows, and AI.
|
|
3
|
+
Comprehensive Cloudflare Workers & Pages integration with config-based patterns, middleware, router, workflows, and AI.
|
|
4
4
|
|
|
5
5
|
## 🚀 Features
|
|
6
6
|
|
|
@@ -177,6 +177,39 @@ const versions = await wrangler.versionsList();
|
|
|
177
177
|
await wrangler.versionsRollback(versions[0].id);
|
|
178
178
|
```
|
|
179
179
|
|
|
180
|
+
### Using Cloudflare Pages
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { PagesService } from '@umituz/web-cloudflare/pages';
|
|
184
|
+
|
|
185
|
+
const pages = new PagesService();
|
|
186
|
+
|
|
187
|
+
// Create a new Pages project
|
|
188
|
+
await pages.createProject('my-app', {
|
|
189
|
+
productionBranch: 'main',
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Deploy to Pages
|
|
193
|
+
const deployment = await pages.deploy({
|
|
194
|
+
projectName: 'my-app',
|
|
195
|
+
directory: 'dist', // Build output directory
|
|
196
|
+
branch: 'main',
|
|
197
|
+
environment: 'production',
|
|
198
|
+
vars: {
|
|
199
|
+
API_URL: 'https://api.example.com',
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// List all projects
|
|
204
|
+
const projects = await pages.listProjects();
|
|
205
|
+
|
|
206
|
+
// List deployments for a project
|
|
207
|
+
const deployments = await pages.listDeployments('my-app');
|
|
208
|
+
|
|
209
|
+
// Delete a deployment
|
|
210
|
+
await pages.deleteDeployment('my-app', deployment.id);
|
|
211
|
+
```
|
|
212
|
+
|
|
180
213
|
**Note:** All services now follow Domain-Driven Design (DDD) architecture with their own domain structures:
|
|
181
214
|
- Wrangler CLI: `src/domains/wrangler/`
|
|
182
215
|
- Workers: `src/domains/workers/`
|
|
@@ -187,6 +220,7 @@ await wrangler.versionsRollback(versions[0].id);
|
|
|
187
220
|
- Images: `src/domains/images/`
|
|
188
221
|
- Analytics: `src/domains/analytics/`
|
|
189
222
|
- Workflows: `src/domains/workflows/`
|
|
223
|
+
- Pages: `src/domains/pages/`
|
|
190
224
|
|
|
191
225
|
## 📚 Subpath Exports
|
|
192
226
|
|
|
@@ -216,6 +250,9 @@ import { WorkflowService } from '@umituz/web-cloudflare/workflows';
|
|
|
216
250
|
|
|
217
251
|
// Wrangler CLI
|
|
218
252
|
import { WranglerService } from '@umituz/web-cloudflare/wrangler';
|
|
253
|
+
|
|
254
|
+
// Pages deployment
|
|
255
|
+
import { PagesService, pagesService } from '@umituz/web-cloudflare/pages';
|
|
219
256
|
```
|
|
220
257
|
|
|
221
258
|
### Workflows & AI
|
|
@@ -680,7 +717,8 @@ Contributions are welcome!
|
|
|
680
717
|
│ │ ├── kv/ # KV storage domain
|
|
681
718
|
│ │ ├── images/ # Images optimization domain
|
|
682
719
|
│ │ ├── analytics/ # Analytics domain
|
|
683
|
-
│ │
|
|
720
|
+
│ │ ├── workflows/ # Workflows domain
|
|
721
|
+
│ │ ├── pages/ # Pages deployment domain
|
|
684
722
|
│ ├── infrastructure/
|
|
685
723
|
│ │ ├── router/ # Express-like router
|
|
686
724
|
│ │ ├── middleware/ # Middleware collection
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/web-cloudflare",
|
|
3
|
-
"version": "1.4.
|
|
4
|
-
"description": "Comprehensive Cloudflare Workers integration with config-based patterns, middleware, router, workflows, and AI (Patch-only versioning: only z in x.y.z increments)",
|
|
3
|
+
"version": "1.4.9",
|
|
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",
|
|
7
7
|
"sideEffects": false,
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"./ai-gateway": "./src/domains/ai-gateway/index.ts",
|
|
18
18
|
"./workers-ai": "./src/domains/ai-gateway/index.ts",
|
|
19
19
|
"./wrangler": "./src/domains/wrangler/index.ts",
|
|
20
|
+
"./pages": "./src/domains/pages/index.ts",
|
|
20
21
|
"./middleware": "./src/domains/middleware/index.ts",
|
|
21
22
|
"./router": "./src/infrastructure/router/index.ts",
|
|
22
23
|
"./utils": "./src/infrastructure/utils/helpers.ts",
|
|
@@ -47,6 +48,10 @@
|
|
|
47
48
|
"workflows",
|
|
48
49
|
"ai-gateway",
|
|
49
50
|
"workers-ai",
|
|
51
|
+
"pages",
|
|
52
|
+
"cloudflare-pages",
|
|
53
|
+
"static-site",
|
|
54
|
+
"deployment",
|
|
50
55
|
"router",
|
|
51
56
|
"middleware",
|
|
52
57
|
"config-patterns",
|
package/src/config/patterns.ts
CHANGED
|
@@ -256,23 +256,26 @@ function deepMerge<T extends Record<string, any>>(
|
|
|
256
256
|
target: T,
|
|
257
257
|
source: Partial<Record<string, any>>
|
|
258
258
|
): Record<string, any> {
|
|
259
|
-
const output = { ...target };
|
|
259
|
+
const output: Record<string, unknown> = { ...target };
|
|
260
260
|
|
|
261
261
|
if (isObject(target) && isObject(source)) {
|
|
262
262
|
Object.keys(source).forEach((key) => {
|
|
263
263
|
const sourceValue = source[key];
|
|
264
|
-
const targetValue = target
|
|
264
|
+
const targetValue = (target as Record<string, unknown>)[key];
|
|
265
265
|
|
|
266
266
|
if (isObject(sourceValue)) {
|
|
267
267
|
if (!(key in target)) {
|
|
268
|
-
|
|
268
|
+
output[key] = sourceValue;
|
|
269
269
|
} else if (isObject(targetValue)) {
|
|
270
|
-
|
|
270
|
+
output[key] = deepMerge(
|
|
271
|
+
targetValue as Record<string, any>,
|
|
272
|
+
sourceValue
|
|
273
|
+
);
|
|
271
274
|
} else {
|
|
272
|
-
|
|
275
|
+
output[key] = sourceValue;
|
|
273
276
|
}
|
|
274
277
|
} else {
|
|
275
|
-
|
|
278
|
+
output[key] = sourceValue;
|
|
276
279
|
}
|
|
277
280
|
});
|
|
278
281
|
}
|
package/src/config/types.ts
CHANGED
|
@@ -629,7 +629,7 @@ export interface EnvConfig {
|
|
|
629
629
|
/**
|
|
630
630
|
* AI bindings
|
|
631
631
|
*/
|
|
632
|
-
AI?:
|
|
632
|
+
AI?: WorkersAIBinding;
|
|
633
633
|
|
|
634
634
|
/**
|
|
635
635
|
* Custom environment variables
|
|
@@ -637,6 +637,14 @@ export interface EnvConfig {
|
|
|
637
637
|
vars?: Record<string, string>;
|
|
638
638
|
}
|
|
639
639
|
|
|
640
|
+
/**
|
|
641
|
+
* Workers AI Binding
|
|
642
|
+
* @description Cloudflare Workers AI runtime binding
|
|
643
|
+
*/
|
|
644
|
+
export interface WorkersAIBinding {
|
|
645
|
+
run: <T = unknown>(model: string, inputs: Record<string, unknown>) => Promise<T>;
|
|
646
|
+
}
|
|
647
|
+
|
|
640
648
|
// ============================================================
|
|
641
649
|
// Configuration Merging Types
|
|
642
650
|
// ============================================================
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
AIProvider,
|
|
11
11
|
AIAnalytics,
|
|
12
12
|
} from '../entities';
|
|
13
|
+
import type { WorkersAIBinding } from '../../../config/types';
|
|
13
14
|
|
|
14
15
|
export class AIGatewayService {
|
|
15
16
|
private config: AIGatewayConfig;
|
|
@@ -225,16 +226,23 @@ import type {
|
|
|
225
226
|
|
|
226
227
|
export class WorkersAIService {
|
|
227
228
|
private env: {
|
|
228
|
-
AI?:
|
|
229
|
+
AI?: WorkersAIBinding;
|
|
229
230
|
bindings?: {
|
|
230
|
-
AI?:
|
|
231
|
+
AI?: WorkersAIBinding;
|
|
231
232
|
};
|
|
232
233
|
};
|
|
233
234
|
|
|
234
|
-
constructor(env: { AI?:
|
|
235
|
+
constructor(env: { AI?: WorkersAIBinding; bindings?: { AI?: WorkersAIBinding } }) {
|
|
235
236
|
this.env = env;
|
|
236
237
|
}
|
|
237
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Workers AI Binding type
|
|
241
|
+
*/
|
|
242
|
+
private getAI(): WorkersAIBinding | null {
|
|
243
|
+
return this.env.bindings?.AI || this.env.AI || null;
|
|
244
|
+
}
|
|
245
|
+
|
|
238
246
|
/**
|
|
239
247
|
* Run text generation model
|
|
240
248
|
*/
|
|
@@ -243,19 +251,24 @@ export class WorkersAIService {
|
|
|
243
251
|
inputs: WorkersAIInputs['text_generation']
|
|
244
252
|
): Promise<WorkersAIResponse> {
|
|
245
253
|
try {
|
|
246
|
-
|
|
247
|
-
const ai = this.env.bindings?.AI || this.env.AI;
|
|
254
|
+
const ai = this.getAI();
|
|
248
255
|
|
|
249
256
|
if (!ai) {
|
|
250
257
|
throw new Error('Workers AI binding not configured');
|
|
251
258
|
}
|
|
252
259
|
|
|
253
|
-
|
|
260
|
+
if (!inputs) {
|
|
261
|
+
throw new Error('Inputs are required for text generation');
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const response = await ai.run(model, inputs as Record<string, unknown>);
|
|
254
265
|
|
|
255
266
|
return {
|
|
256
267
|
success: true,
|
|
257
268
|
data: {
|
|
258
|
-
output: response.response
|
|
269
|
+
output: ((response as Record<string, unknown>).response as string | string[] | undefined) ||
|
|
270
|
+
((response as Record<string, unknown>).output as string | string[] | undefined) ||
|
|
271
|
+
((response as Record<string, unknown>).text as string | string[] | undefined),
|
|
259
272
|
},
|
|
260
273
|
model,
|
|
261
274
|
};
|
|
@@ -329,19 +342,23 @@ Generate the script:`;
|
|
|
329
342
|
inputs: WorkersAIInputs['image_generation']
|
|
330
343
|
): Promise<WorkersAIResponse> {
|
|
331
344
|
try {
|
|
332
|
-
|
|
333
|
-
const ai = this.env.bindings?.AI || this.env.AI;
|
|
345
|
+
const ai = this.getAI();
|
|
334
346
|
|
|
335
347
|
if (!ai) {
|
|
336
348
|
throw new Error('Workers AI binding not configured');
|
|
337
349
|
}
|
|
338
350
|
|
|
339
|
-
|
|
351
|
+
if (!inputs) {
|
|
352
|
+
throw new Error('Inputs are required for image generation');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const response = await ai.run(model, inputs as Record<string, unknown>);
|
|
340
356
|
|
|
341
357
|
return {
|
|
342
358
|
success: true,
|
|
343
359
|
data: {
|
|
344
|
-
image: response.image ||
|
|
360
|
+
image: (response as Record<string, unknown>).image as string | undefined ||
|
|
361
|
+
(response as Record<string, unknown>).output as string | undefined,
|
|
345
362
|
},
|
|
346
363
|
model,
|
|
347
364
|
};
|
|
@@ -359,20 +376,19 @@ Generate the script:`;
|
|
|
359
376
|
*/
|
|
360
377
|
async generateEmbedding(text: string): Promise<WorkersAIResponse> {
|
|
361
378
|
try {
|
|
362
|
-
|
|
363
|
-
const ai = this.env.bindings?.AI || this.env.AI;
|
|
379
|
+
const ai = this.getAI();
|
|
364
380
|
|
|
365
381
|
if (!ai) {
|
|
366
382
|
throw new Error('Workers AI binding not configured');
|
|
367
383
|
}
|
|
368
384
|
|
|
369
385
|
const model = '@cf/openai/clip-vit-base-patch32';
|
|
370
|
-
const response = await ai.run(model, { text })
|
|
386
|
+
const response = await ai.run(model, { text }) as Record<string, unknown>;
|
|
371
387
|
|
|
372
388
|
return {
|
|
373
389
|
success: true,
|
|
374
390
|
data: {
|
|
375
|
-
embedding: response.embedding || response.output,
|
|
391
|
+
embedding: response.embedding as number[] | undefined || response.output as number[] | undefined,
|
|
376
392
|
},
|
|
377
393
|
model,
|
|
378
394
|
};
|
|
@@ -394,8 +410,7 @@ Generate the script:`;
|
|
|
394
410
|
targetLang: string
|
|
395
411
|
): Promise<WorkersAIResponse> {
|
|
396
412
|
try {
|
|
397
|
-
|
|
398
|
-
const ai = this.env.bindings?.AI || this.env.AI;
|
|
413
|
+
const ai = this.getAI();
|
|
399
414
|
|
|
400
415
|
if (!ai) {
|
|
401
416
|
throw new Error('Workers AI binding not configured');
|
|
@@ -406,12 +421,14 @@ Generate the script:`;
|
|
|
406
421
|
text,
|
|
407
422
|
source_lang: sourceLang,
|
|
408
423
|
target_lang: targetLang,
|
|
409
|
-
})
|
|
424
|
+
}) as Record<string, unknown>;
|
|
410
425
|
|
|
411
426
|
return {
|
|
412
427
|
success: true,
|
|
413
428
|
data: {
|
|
414
|
-
output: response.translated_text
|
|
429
|
+
output: response.translated_text as string | string[] | undefined ||
|
|
430
|
+
response.output as string | string[] | undefined ||
|
|
431
|
+
response.text as string | string[] | undefined,
|
|
415
432
|
},
|
|
416
433
|
model: '@cf/meta/m2m100-1.2b',
|
|
417
434
|
};
|
|
@@ -78,13 +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
86
|
keys: list.keys.map((k) => ({
|
|
83
87
|
key: k.name,
|
|
84
88
|
value: '',
|
|
85
89
|
metadata: k.metadata as Record<string, unknown> | undefined,
|
|
86
90
|
})),
|
|
87
|
-
cursor:
|
|
91
|
+
cursor: cursor,
|
|
88
92
|
};
|
|
89
93
|
}
|
|
90
94
|
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pages Domain Entities
|
|
3
|
+
* Defines entities for Cloudflare Pages operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Pages project info
|
|
8
|
+
*/
|
|
9
|
+
export interface PagesProject {
|
|
10
|
+
name: string;
|
|
11
|
+
production_branch?: string;
|
|
12
|
+
created_on?: string;
|
|
13
|
+
deployment_configs?: {
|
|
14
|
+
preview?: {
|
|
15
|
+
branch?: string;
|
|
16
|
+
env_vars?: Record<string, string>;
|
|
17
|
+
};
|
|
18
|
+
production?: {
|
|
19
|
+
branch?: string;
|
|
20
|
+
env_vars?: Record<string, string>;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Pages deployment info
|
|
27
|
+
*/
|
|
28
|
+
export interface PagesDeployment {
|
|
29
|
+
id: string;
|
|
30
|
+
project: string;
|
|
31
|
+
url: string;
|
|
32
|
+
latest_stage?: string;
|
|
33
|
+
created_on?: string;
|
|
34
|
+
deployment_trigger?: {
|
|
35
|
+
metadata?: {
|
|
36
|
+
branch?: string;
|
|
37
|
+
commit_hash?: string;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
stages?: {
|
|
41
|
+
environment: string;
|
|
42
|
+
function?: string;
|
|
43
|
+
url?: string;
|
|
44
|
+
}[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Pages deploy options
|
|
49
|
+
*/
|
|
50
|
+
export interface PagesDeployOptions {
|
|
51
|
+
projectName: string;
|
|
52
|
+
directory?: string; // Build output directory (default: dist)
|
|
53
|
+
branch?: string;
|
|
54
|
+
preview?: boolean;
|
|
55
|
+
environment?: 'preview' | 'production';
|
|
56
|
+
compatibilityDate?: string;
|
|
57
|
+
compatibilityFlags?: string[];
|
|
58
|
+
vars?: Record<string, string>;
|
|
59
|
+
functions?: boolean; // Deploy with functions
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Pages function info
|
|
64
|
+
*/
|
|
65
|
+
export interface PagesFunction {
|
|
66
|
+
name: string;
|
|
67
|
+
scriptPath?: string;
|
|
68
|
+
compatibilityDate?: string;
|
|
69
|
+
compatibilityFlags?: string[];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Pages deployment result
|
|
74
|
+
*/
|
|
75
|
+
export interface PagesDeploymentResult {
|
|
76
|
+
deployment: PagesDeployment;
|
|
77
|
+
url?: string;
|
|
78
|
+
alias?: string;
|
|
79
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @umituz/web-cloudflare - Pages Domain
|
|
3
|
+
* Cloudflare Pages deployment and management
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// Services
|
|
7
|
+
export { PagesService, pagesService } from './services';
|
|
8
|
+
|
|
9
|
+
// Types
|
|
10
|
+
export type {
|
|
11
|
+
IPagesService,
|
|
12
|
+
} from './types';
|
|
13
|
+
|
|
14
|
+
// Entities
|
|
15
|
+
export type {
|
|
16
|
+
PagesProject,
|
|
17
|
+
PagesDeployment,
|
|
18
|
+
PagesDeployOptions,
|
|
19
|
+
PagesFunction,
|
|
20
|
+
PagesDeploymentResult,
|
|
21
|
+
} from './entities';
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pages Service Implementation
|
|
3
|
+
* Cloudflare Pages deployment and management
|
|
4
|
+
*
|
|
5
|
+
* ⚠️ NODE.JS ONLY: This service requires Node.js runtime and is NOT compatible
|
|
6
|
+
* with Cloudflare Workers runtime. Use this service only in build/development
|
|
7
|
+
* scripts running in Node.js environment.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
import type { IPagesService } from '../types/service.interface';
|
|
12
|
+
import type {
|
|
13
|
+
PagesProject,
|
|
14
|
+
PagesDeployment,
|
|
15
|
+
PagesDeployOptions,
|
|
16
|
+
PagesFunction,
|
|
17
|
+
PagesDeploymentResult,
|
|
18
|
+
} from '../entities';
|
|
19
|
+
|
|
20
|
+
export class PagesService implements IPagesService {
|
|
21
|
+
private readonly wranglerCommand: string;
|
|
22
|
+
|
|
23
|
+
constructor(options?: { wranglerPath?: string }) {
|
|
24
|
+
this.wranglerCommand = options?.wranglerPath || 'npx wrangler';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a new Pages project
|
|
29
|
+
*/
|
|
30
|
+
async createProject(
|
|
31
|
+
projectName: string,
|
|
32
|
+
options?: {
|
|
33
|
+
productionBranch?: string;
|
|
34
|
+
compatibilityDate?: string;
|
|
35
|
+
}
|
|
36
|
+
): Promise<{ success: boolean; data?: PagesProject; error?: string }> {
|
|
37
|
+
return this.nodeNotRequired();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* List all Pages projects
|
|
42
|
+
*/
|
|
43
|
+
async listProjects(): Promise<{ success: boolean; data?: PagesProject[]; error?: string }> {
|
|
44
|
+
return this.nodeNotRequired();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Get project details
|
|
49
|
+
*/
|
|
50
|
+
async getProject(projectName: string): Promise<{ success: boolean; data?: PagesProject; error?: string }> {
|
|
51
|
+
return this.nodeNotRequired();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Deploy to Pages
|
|
56
|
+
*/
|
|
57
|
+
async deploy(
|
|
58
|
+
options: PagesDeployOptions
|
|
59
|
+
): Promise<{ success: boolean; data?: PagesDeploymentResult; error?: string }> {
|
|
60
|
+
return this.nodeNotRequired();
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a Pages function
|
|
65
|
+
*/
|
|
66
|
+
async createFunction(
|
|
67
|
+
projectName: string,
|
|
68
|
+
functionName: string,
|
|
69
|
+
options?: {
|
|
70
|
+
compatibilityDate?: string;
|
|
71
|
+
compatibilityFlags?: string[];
|
|
72
|
+
}
|
|
73
|
+
): Promise<{ success: boolean; data?: PagesFunction; error?: string }> {
|
|
74
|
+
return this.nodeNotRequired();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* List deployments for a project
|
|
79
|
+
*/
|
|
80
|
+
async listDeployments(
|
|
81
|
+
projectName: string
|
|
82
|
+
): Promise<{ success: boolean; data?: PagesDeployment[]; error?: string }> {
|
|
83
|
+
return this.nodeNotRequired();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Get deployment details
|
|
88
|
+
*/
|
|
89
|
+
async getDeployment(
|
|
90
|
+
projectName: string,
|
|
91
|
+
deploymentId: string
|
|
92
|
+
): Promise<{ success: boolean; data?: PagesDeployment; error?: string }> {
|
|
93
|
+
return this.nodeNotRequired();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Delete a deployment
|
|
98
|
+
*/
|
|
99
|
+
async deleteDeployment(
|
|
100
|
+
projectName: string,
|
|
101
|
+
deploymentId: string
|
|
102
|
+
): Promise<{ success: boolean; error?: string }> {
|
|
103
|
+
return this.nodeNotRequired();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Execute wrangler pages command
|
|
108
|
+
*/
|
|
109
|
+
private executeWranglerPages(
|
|
110
|
+
args: string[],
|
|
111
|
+
options?: { cwd?: string; env?: Record<string, string> }
|
|
112
|
+
): { success: boolean; stdout: string; stderr: string; exitCode?: number } {
|
|
113
|
+
try {
|
|
114
|
+
const command = `${this.wranglerCommand} pages ${args.join(' ')}`;
|
|
115
|
+
const stdout = execSync(command, {
|
|
116
|
+
cwd: options?.cwd,
|
|
117
|
+
env: { ...process.env, ...options?.env },
|
|
118
|
+
encoding: 'utf-8',
|
|
119
|
+
});
|
|
120
|
+
return { success: true, stdout, stderr: '', exitCode: 0 };
|
|
121
|
+
} catch (error: unknown) {
|
|
122
|
+
if (error instanceof Error) {
|
|
123
|
+
const err = error as { stdout?: string; stderr?: string; code?: number };
|
|
124
|
+
return {
|
|
125
|
+
success: false,
|
|
126
|
+
stdout: err.stdout || '',
|
|
127
|
+
stderr: err.stderr || error.message,
|
|
128
|
+
exitCode: err.code,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
return { success: false, stdout: '', stderr: 'Unknown error', exitCode: -1 };
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private nodeNotRequired<T>(): never {
|
|
136
|
+
throw new Error(
|
|
137
|
+
'PagesService requires Node.js runtime. ' +
|
|
138
|
+
'This service only works in Node.js environment, not in Cloudflare Workers. ' +
|
|
139
|
+
'Use this service in build scripts or deployment tools only.'
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export const pagesService = new PagesService();
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pages Service Interface
|
|
3
|
+
* Defines the contract for Cloudflare Pages operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
PagesProject,
|
|
8
|
+
PagesDeployment,
|
|
9
|
+
PagesDeployOptions,
|
|
10
|
+
PagesFunction,
|
|
11
|
+
PagesDeploymentResult,
|
|
12
|
+
} from '../entities';
|
|
13
|
+
|
|
14
|
+
export interface IPagesService {
|
|
15
|
+
/**
|
|
16
|
+
* Create a new Pages project
|
|
17
|
+
*/
|
|
18
|
+
createProject(
|
|
19
|
+
projectName: string,
|
|
20
|
+
options?: {
|
|
21
|
+
productionBranch?: string;
|
|
22
|
+
compatibilityDate?: string;
|
|
23
|
+
}
|
|
24
|
+
): Promise<{ success: boolean; data?: PagesProject; error?: string }>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* List all Pages projects
|
|
28
|
+
*/
|
|
29
|
+
listProjects(): Promise<{ success: boolean; data?: PagesProject[]; error?: string }>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get project details
|
|
33
|
+
*/
|
|
34
|
+
getProject(projectName: string): Promise<{ success: boolean; data?: PagesProject; error?: string }>;
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Deploy to Pages
|
|
38
|
+
*/
|
|
39
|
+
deploy(
|
|
40
|
+
options: PagesDeployOptions
|
|
41
|
+
): Promise<{ success: boolean; data?: PagesDeploymentResult; error?: string }>;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Create a Pages function
|
|
45
|
+
*/
|
|
46
|
+
createFunction(
|
|
47
|
+
projectName: string,
|
|
48
|
+
functionName: string,
|
|
49
|
+
options?: {
|
|
50
|
+
compatibilityDate?: string;
|
|
51
|
+
compatibilityFlags?: string[];
|
|
52
|
+
}
|
|
53
|
+
): Promise<{ success: boolean; data?: PagesFunction; error?: string }>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* List deployments for a project
|
|
57
|
+
*/
|
|
58
|
+
listDeployments(
|
|
59
|
+
projectName: string
|
|
60
|
+
): Promise<{ success: boolean; data?: PagesDeployment[]; error?: string }>;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get deployment details
|
|
64
|
+
*/
|
|
65
|
+
getDeployment(
|
|
66
|
+
projectName: string,
|
|
67
|
+
deploymentId: string
|
|
68
|
+
): Promise<{ success: boolean; data?: PagesDeployment; error?: string }>;
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Delete a deployment
|
|
72
|
+
*/
|
|
73
|
+
deleteDeployment(
|
|
74
|
+
projectName: string,
|
|
75
|
+
deploymentId: string
|
|
76
|
+
): Promise<{ success: boolean; error?: string }>;
|
|
77
|
+
}
|
|
@@ -90,13 +90,17 @@ class R2Service implements IR2Service {
|
|
|
90
90
|
cursor: options?.cursor,
|
|
91
91
|
});
|
|
92
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
|
+
|
|
93
97
|
return {
|
|
94
98
|
objects: listed.objects.map((obj) => ({
|
|
95
99
|
key: obj.key,
|
|
96
100
|
size: obj.size,
|
|
97
101
|
uploaded: obj.uploaded,
|
|
98
102
|
})),
|
|
99
|
-
cursor:
|
|
103
|
+
cursor: cursor,
|
|
100
104
|
};
|
|
101
105
|
}
|
|
102
106
|
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { workersService, kvService } from "@umituz/web-cloudflare";
|
|
7
|
-
import type { Env } from "../types";
|
|
7
|
+
import type { Env } from "../types/env.types";
|
|
8
|
+
import type { WorkerRequest } from "../entities";
|
|
8
9
|
|
|
9
10
|
// Configure routes
|
|
10
11
|
workersService.route("/", async () => {
|
|
@@ -39,5 +40,5 @@ workersService.route("/api/cache/:key", async (request, env?: Env) => {
|
|
|
39
40
|
// Export for Cloudflare Workers
|
|
40
41
|
export default {
|
|
41
42
|
fetch: (request: Request, env?: Env, ctx?: ExecutionContext) =>
|
|
42
|
-
workersService.fetch(request as
|
|
43
|
+
workersService.fetch(request as unknown as WorkerRequest, env, ctx),
|
|
43
44
|
};
|
|
@@ -63,7 +63,10 @@ class WorkersService {
|
|
|
63
63
|
async fetch(request: WorkerRequest, env?: Env, ctx?: ExecutionContext): Promise<WorkerResponse> {
|
|
64
64
|
// Initialize cache if available in Workers runtime
|
|
65
65
|
if (!this.cache && env && typeof caches !== 'undefined') {
|
|
66
|
-
|
|
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
|
|
@@ -53,6 +53,12 @@ export enum WranglerCommand {
|
|
|
53
53
|
// Versions
|
|
54
54
|
VERSIONS_LIST = 'versions list',
|
|
55
55
|
VERSIONS_ROLLBACK = 'versions rollback',
|
|
56
|
+
|
|
57
|
+
// Pages operations
|
|
58
|
+
PAGES_PROJECT_CREATE = 'pages project create',
|
|
59
|
+
PAGES_PROJECT_LIST = 'pages project list',
|
|
60
|
+
PAGES_DEPLOY = 'pages deploy',
|
|
61
|
+
PAGES_FUNCTION = 'pages function',
|
|
56
62
|
}
|
|
57
63
|
|
|
58
64
|
/**
|
|
@@ -140,3 +146,54 @@ export interface WranglerAnalyticsData {
|
|
|
140
146
|
statusCodes?: Record<string, number>;
|
|
141
147
|
countries?: Record<string, number>;
|
|
142
148
|
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Pages project info
|
|
152
|
+
*/
|
|
153
|
+
export interface PagesProjectInfo {
|
|
154
|
+
name: string;
|
|
155
|
+
production_branch?: string;
|
|
156
|
+
creation_date?: string;
|
|
157
|
+
deployment_configs?: {
|
|
158
|
+
preview?: {
|
|
159
|
+
branch?: string;
|
|
160
|
+
env_vars?: Record<string, string>;
|
|
161
|
+
};
|
|
162
|
+
production?: {
|
|
163
|
+
branch?: string;
|
|
164
|
+
env_vars?: Record<string, string>;
|
|
165
|
+
};
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Pages deployment info
|
|
171
|
+
*/
|
|
172
|
+
export interface PagesDeploymentInfo {
|
|
173
|
+
id: string;
|
|
174
|
+
project: string;
|
|
175
|
+
url: string;
|
|
176
|
+
latest_stage?: string;
|
|
177
|
+
created_on?: string;
|
|
178
|
+
deployment_trigger?: {
|
|
179
|
+
metadata?: {
|
|
180
|
+
branch?: string;
|
|
181
|
+
commit_hash?: string;
|
|
182
|
+
};
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Pages deploy options
|
|
188
|
+
*/
|
|
189
|
+
export interface PagesDeployOptions {
|
|
190
|
+
projectName: string;
|
|
191
|
+
directory?: string;
|
|
192
|
+
branch?: string;
|
|
193
|
+
preview?: boolean;
|
|
194
|
+
environment?: 'preview' | 'production';
|
|
195
|
+
compatibilityDate?: string;
|
|
196
|
+
compatibilityFlags?: string[];
|
|
197
|
+
vars?: Record<string, string>;
|
|
198
|
+
}
|
|
199
|
+
|
|
@@ -13,6 +13,9 @@ import type {
|
|
|
13
13
|
SecretInfo,
|
|
14
14
|
WorkerVersionInfo,
|
|
15
15
|
WranglerAnalyticsData,
|
|
16
|
+
PagesProjectInfo,
|
|
17
|
+
PagesDeploymentInfo,
|
|
18
|
+
PagesDeployOptions,
|
|
16
19
|
} from '../entities';
|
|
17
20
|
|
|
18
21
|
export interface IWranglerService {
|
|
@@ -127,4 +130,21 @@ export interface IWranglerService {
|
|
|
127
130
|
args: string[],
|
|
128
131
|
options?: WranglerCommandOptions
|
|
129
132
|
): Promise<WranglerResult<string>>;
|
|
133
|
+
|
|
134
|
+
// Pages operations
|
|
135
|
+
pagesProjectCreate(
|
|
136
|
+
projectName: string,
|
|
137
|
+
options?: WranglerCommandOptions & { productionBranch?: string }
|
|
138
|
+
): Promise<WranglerResult<PagesProjectInfo>>;
|
|
139
|
+
pagesProjectList(
|
|
140
|
+
options?: WranglerCommandOptions
|
|
141
|
+
): Promise<WranglerResult<PagesProjectInfo[]>>;
|
|
142
|
+
pagesDeploy(
|
|
143
|
+
options: PagesDeployOptions & WranglerCommandOptions
|
|
144
|
+
): Promise<WranglerResult<PagesDeploymentInfo>>;
|
|
145
|
+
pagesFunctionCreate(
|
|
146
|
+
projectName: string,
|
|
147
|
+
functionName: string,
|
|
148
|
+
options?: WranglerCommandOptions
|
|
149
|
+
): Promise<WranglerResult<void>>;
|
|
130
150
|
}
|
package/src/index.ts
CHANGED
|
@@ -35,6 +35,15 @@ export { d1Service, D1Service } from "./domains/d1";
|
|
|
35
35
|
export { kvService, KVService } from "./domains/kv";
|
|
36
36
|
export { imagesService, ImagesService } from "./domains/images";
|
|
37
37
|
export { analyticsService, AnalyticsService } from "./domains/analytics";
|
|
38
|
+
// Pages - Node.js-only service
|
|
39
|
+
export { pagesService, PagesService } from "./domains/pages";
|
|
40
|
+
export type {
|
|
41
|
+
PagesProject,
|
|
42
|
+
PagesDeployment,
|
|
43
|
+
PagesDeployOptions,
|
|
44
|
+
PagesFunction,
|
|
45
|
+
PagesDeploymentResult,
|
|
46
|
+
} from "./domains/pages";
|
|
38
47
|
// Workflows - selective exports to avoid conflicts
|
|
39
48
|
export type {
|
|
40
49
|
WorkflowStep,
|
|
@@ -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?:
|
|
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
|
-
|
|
200
|
-
|
|
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
|
/**
|
|
@@ -370,8 +360,6 @@ export function handleMiddlewareError(
|
|
|
370
360
|
): Response {
|
|
371
361
|
if (config.logger) {
|
|
372
362
|
config.logger(error);
|
|
373
|
-
} else {
|
|
374
|
-
console.error('[Middleware Error]', error);
|
|
375
363
|
}
|
|
376
364
|
|
|
377
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?:
|
|
19
|
+
AI?: WorkersAIBinding;
|
|
19
20
|
vars?: Record<string, string>;
|
|
20
21
|
}
|
|
21
22
|
|
|
@@ -636,15 +636,15 @@ export function deepMerge<T extends Record<string, any>>(
|
|
|
636
636
|
if (isObject(target) && isObject(source)) {
|
|
637
637
|
for (const key in source) {
|
|
638
638
|
const sourceValue = source[key];
|
|
639
|
-
const targetValue = (target as
|
|
639
|
+
const targetValue = (target as Record<string, unknown>)[key];
|
|
640
640
|
|
|
641
641
|
if (isObject(sourceValue)) {
|
|
642
642
|
if (!targetValue) {
|
|
643
|
-
(target as
|
|
643
|
+
(target as Record<string, unknown>)[key] = {};
|
|
644
644
|
}
|
|
645
|
-
deepMerge((target as
|
|
645
|
+
deepMerge((target as Record<string, unknown>)[key] as Record<string, any>, sourceValue);
|
|
646
646
|
} else {
|
|
647
|
-
(target as
|
|
647
|
+
(target as Record<string, unknown>)[key] = sourceValue;
|
|
648
648
|
}
|
|
649
649
|
}
|
|
650
650
|
}
|
|
@@ -663,7 +663,7 @@ export function pick<T extends object, K extends keyof T>(obj: T, keys: K[]): Pi
|
|
|
663
663
|
const result = {} as Pick<T, K>;
|
|
664
664
|
keys.forEach((key) => {
|
|
665
665
|
if (key in obj) {
|
|
666
|
-
(result as
|
|
666
|
+
(result as Record<string, unknown>)[key as string] = obj[key as keyof T];
|
|
667
667
|
}
|
|
668
668
|
});
|
|
669
669
|
return result;
|