@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
@@ -3,7 +3,7 @@
3
3
  * @description Cloudflare KV key-value storage operations
4
4
  */
5
5
 
6
- import type { KVEntry, KVListOptions, KVListResult } from "../entities";
6
+ import type { KVEntry, KVListOptions, KVListResult } from "../../../domain/entities/kv.entity";
7
7
  import type { IKVService } from "../../../domain/interfaces/services.interface";
8
8
  import { validationUtils, cacheUtils } from "../../../infrastructure/utils";
9
9
 
@@ -78,10 +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
- keys: list.keys,
83
- list_complete: list.list_complete,
84
- cursor: list.cursor,
86
+ keys: list.keys.map((k) => ({
87
+ key: k.name,
88
+ value: '',
89
+ metadata: k.metadata as Record<string, unknown> | undefined,
90
+ })),
91
+ cursor: cursor,
85
92
  };
86
93
  }
87
94
 
@@ -107,10 +114,12 @@ class KVService implements IKVService {
107
114
 
108
115
  await Promise.all(
109
116
  list.keys.map(async (key) => {
110
- await this.delete(key.name, binding);
117
+ await this.delete(key.key, binding);
111
118
  })
112
119
  );
113
120
  }
114
121
  }
115
122
 
123
+ // Export class and singleton instance
124
+ export { KVService };
116
125
  export const kvService = new KVService();
@@ -6,7 +6,7 @@
6
6
  /**
7
7
  * CORS configuration
8
8
  */
9
- export interface CORSConfig {
9
+ export interface MiddlewareCORSConfig {
10
10
  enabled: boolean;
11
11
  allowedOrigins: string[];
12
12
  allowedMethods: string[];
@@ -19,7 +19,7 @@ export interface CORSConfig {
19
19
  /**
20
20
  * Cache configuration
21
21
  */
22
- export interface CacheConfig {
22
+ export interface MiddlewareCacheConfig {
23
23
  enabled: boolean;
24
24
  defaultTTL: number;
25
25
  paths?: Record<string, number>;
@@ -31,7 +31,7 @@ export interface CacheConfig {
31
31
  /**
32
32
  * Rate limit configuration
33
33
  */
34
- export interface RateLimitConfig {
34
+ export interface MiddlewareRateLimitConfig {
35
35
  enabled: boolean;
36
36
  maxRequests: number;
37
37
  window: number;
@@ -48,7 +48,7 @@ export interface RateLimitConfig {
48
48
  /**
49
49
  * Authentication configuration
50
50
  */
51
- export interface AuthConfig {
51
+ export interface MiddlewareAuthConfig {
52
52
  enabled: boolean;
53
53
  type: 'bearer' | 'apikey' | 'basic';
54
54
  token?: string;
@@ -3,14 +3,18 @@
3
3
  * @description Authentication middleware for Cloudflare Workers
4
4
  */
5
5
 
6
- import type { AuthConfig } from '../entities';
6
+ import type { MiddlewareAuthConfig } from '../entities';
7
+
8
+ // Type aliases for backwards compatibility
9
+ export type { MiddlewareAuthConfig };
10
+ export type AuthConfig = MiddlewareAuthConfig;
7
11
 
8
12
  /**
9
13
  * Require authentication
10
14
  */
11
15
  export async function requireAuth(
12
16
  request: Request,
13
- config: AuthConfig
17
+ config: MiddlewareAuthConfig
14
18
  ): Promise<Response | null> {
15
19
  if (!config.enabled) {
16
20
  return null;
@@ -3,7 +3,11 @@
3
3
  * @description Caching middleware for Cloudflare Workers
4
4
  */
5
5
 
6
- import type { CacheConfig } from '../entities';
6
+ import type { MiddlewareCacheConfig } from '../entities';
7
+
8
+ // Type aliases for backwards compatibility
9
+ export type { MiddlewareCacheConfig };
10
+ export type CacheConfig = MiddlewareCacheConfig;
7
11
 
8
12
  interface CacheEntry {
9
13
  response: Response;
@@ -17,7 +21,7 @@ const cacheStore = new Map<string, CacheEntry>();
17
21
  */
18
22
  export async function cache(
19
23
  request: Request,
20
- config: CacheConfig
24
+ config: MiddlewareCacheConfig
21
25
  ): Promise<Response | null> {
22
26
  if (!config.enabled) {
23
27
  return null;
@@ -46,7 +50,7 @@ export async function cache(
46
50
  export function setCache(
47
51
  request: Request,
48
52
  response: Response,
49
- config: CacheConfig
53
+ config: MiddlewareCacheConfig
50
54
  ): void {
51
55
  if (!config.enabled) {
52
56
  return;
@@ -3,7 +3,10 @@
3
3
  * @description Cross-Origin Resource Sharing middleware for Cloudflare Workers
4
4
  */
5
5
 
6
- import type { CORSConfig } from '../entities';
6
+ import type { MiddlewareCORSConfig } from '../entities';
7
+
8
+ // Type alias for backwards compatibility
9
+ export type CORSConfig = MiddlewareCORSConfig;
7
10
 
8
11
  /**
9
12
  * Add CORS headers to response
@@ -11,7 +14,7 @@ import type { CORSConfig } from '../entities';
11
14
  export function addCorsHeaders(
12
15
  request: Request,
13
16
  response: Response,
14
- config: CORSConfig
17
+ config: MiddlewareCORSConfig
15
18
  ): Response {
16
19
  if (!config.enabled) {
17
20
  return response;
@@ -23,7 +26,7 @@ export function addCorsHeaders(
23
26
  // Check if origin is allowed
24
27
  const allowedOrigin = config.allowedOrigins.includes('*')
25
28
  ? '*'
26
- : config.allowedOrigins.includes(origin || '')
29
+ : origin && config.allowedOrigins.includes(origin)
27
30
  ? origin
28
31
  : config.allowedOrigins[0];
29
32
 
@@ -55,7 +58,7 @@ export function addCorsHeaders(
55
58
  */
56
59
  export async function cors(
57
60
  request: Request,
58
- config: CORSConfig
61
+ config: MiddlewareCORSConfig
59
62
  ): Promise<Response | null> {
60
63
  if (!config.enabled) {
61
64
  return null;
@@ -68,7 +71,7 @@ export async function cors(
68
71
 
69
72
  const allowedOrigin = config.allowedOrigins.includes('*')
70
73
  ? '*'
71
- : config.allowedOrigins.includes(origin || '')
74
+ : origin && config.allowedOrigins.includes(origin)
72
75
  ? origin
73
76
  : config.allowedOrigins[0];
74
77
 
@@ -3,7 +3,11 @@
3
3
  * @description Rate limiting middleware for Cloudflare Workers
4
4
  */
5
5
 
6
- import type { RateLimitConfig } from '../entities';
6
+ import type { MiddlewareRateLimitConfig } from '../entities';
7
+
8
+ // Type aliases for backwards compatibility
9
+ export type { MiddlewareRateLimitConfig };
10
+ export type RateLimitConfig = MiddlewareRateLimitConfig;
7
11
 
8
12
  interface RateLimitEntry {
9
13
  count: number;
@@ -15,7 +19,7 @@ const rateLimitStore = new Map<string, RateLimitEntry>();
15
19
  /**
16
20
  * Get rate limit key
17
21
  */
18
- function getRateLimitKey(request: Request, config: RateLimitConfig): string {
22
+ function getRateLimitKey(request: Request, config: MiddlewareRateLimitConfig): string {
19
23
  const parts: string[] = [];
20
24
 
21
25
  if (config.by === 'ip' || config.by === 'both') {
@@ -43,7 +47,7 @@ function getRateLimitKey(request: Request, config: RateLimitConfig): string {
43
47
  */
44
48
  export async function checkRateLimit(
45
49
  request: Request,
46
- config: RateLimitConfig
50
+ config: MiddlewareRateLimitConfig
47
51
  ): Promise<Response | null> {
48
52
  if (!config.enabled) {
49
53
  return null;
@@ -4,10 +4,10 @@
4
4
  */
5
5
 
6
6
  import type {
7
- CORSConfig,
8
- CacheConfig,
9
- RateLimitConfig,
10
- AuthConfig,
7
+ MiddlewareCORSConfig,
8
+ MiddlewareCacheConfig,
9
+ MiddlewareRateLimitConfig,
10
+ MiddlewareAuthConfig,
11
11
  SecurityHeadersConfig,
12
12
  IPFilterConfig,
13
13
  LogConfig,
@@ -15,29 +15,35 @@ import type {
15
15
  ErrorHandlerConfig,
16
16
  } from '../entities';
17
17
 
18
+ // Type aliases for backwards compatibility
19
+ export type CORSConfig = MiddlewareCORSConfig;
20
+ export type CacheConfig = MiddlewareCacheConfig;
21
+ export type RateLimitConfig = MiddlewareRateLimitConfig;
22
+ export type AuthConfig = MiddlewareAuthConfig;
23
+
18
24
  export interface IMiddlewareService {
19
25
  /**
20
26
  * CORS middleware
21
27
  */
22
- cors(request: Request, config: CORSConfig): Promise<Response | null>;
23
- addCorsHeaders(request: Request, response: Response, config: CORSConfig): Response;
28
+ cors(request: Request, config: MiddlewareCORSConfig): Promise<Response | null>;
29
+ addCorsHeaders(request: Request, response: Response, config: MiddlewareCORSConfig): Response;
24
30
 
25
31
  /**
26
32
  * Cache middleware
27
33
  */
28
- cache(request: Request, config: CacheConfig): Promise<Response | null>;
29
- setCache(request: Request, response: Response, config: CacheConfig): void;
34
+ cache(request: Request, config: MiddlewareCacheConfig): Promise<Response | null>;
35
+ setCache(request: Request, response: Response, config: MiddlewareCacheConfig): void;
30
36
  invalidateCache(pattern?: string): void;
31
37
 
32
38
  /**
33
39
  * Rate limit middleware
34
40
  */
35
- checkRateLimit(request: Request, config: RateLimitConfig): Promise<Response | null>;
41
+ checkRateLimit(request: Request, config: MiddlewareRateLimitConfig): Promise<Response | null>;
36
42
 
37
43
  /**
38
44
  * Authentication middleware
39
45
  */
40
- requireAuth(request: Request, config: AuthConfig): Promise<Response | null>;
46
+ requireAuth(request: Request, config: MiddlewareAuthConfig): Promise<Response | null>;
41
47
  addUserContext(request: Request, user: {
42
48
  id: string;
43
49
  [key: string]: unknown;
@@ -88,7 +94,7 @@ export interface IMiddlewareService {
88
94
  * Health check
89
95
  */
90
96
  healthCheck(
91
- env: import('../../router').CloudflareEnv,
97
+ env: Record<string, unknown>,
92
98
  config?: HealthCheckConfig
93
99
  ): Promise<Response>;
94
100
 
@@ -3,7 +3,7 @@
3
3
  * @description Cloudflare R2 object storage operations
4
4
  */
5
5
 
6
- import type { R2Object, R2ListOptions, R2ListResult, R2PutOptions, R2PresignedURL } from "../entities";
6
+ import type { R2Object, R2ListOptions, R2ListResult, R2PutOptions, R2PresignedURL } from "../../../domain/entities/r2.entity";
7
7
  import type { IR2Service } from "../../../domain/interfaces/services.interface";
8
8
  import { validationUtils } from "../../../infrastructure/utils";
9
9
 
@@ -51,8 +51,6 @@ class R2Service implements IR2Service {
51
51
  key: object.key,
52
52
  size: object.size,
53
53
  uploaded: object.uploaded,
54
- httpMetadata: object.httpMetadata,
55
- customMetadata: object.customMetadata,
56
54
  };
57
55
  }
58
56
 
@@ -70,7 +68,6 @@ class R2Service implements IR2Service {
70
68
  await bucket.put(key, data, {
71
69
  httpMetadata: options?.httpMetadata,
72
70
  customMetadata: options?.customMetadata,
73
- checksum: options?.checksum,
74
71
  });
75
72
  }
76
73
 
@@ -93,10 +90,17 @@ class R2Service implements IR2Service {
93
90
  cursor: options?.cursor,
94
91
  });
95
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
+
96
97
  return {
97
- objects: listed.objects,
98
- truncated: listed.truncated,
99
- cursor: listed.cursor,
98
+ objects: listed.objects.map((obj) => ({
99
+ key: obj.key,
100
+ size: obj.size,
101
+ uploaded: obj.uploaded,
102
+ })),
103
+ cursor: cursor,
100
104
  };
101
105
  }
102
106
 
@@ -105,11 +109,11 @@ class R2Service implements IR2Service {
105
109
  // This would typically use the AWS SDK or custom signing logic
106
110
  // For now, return a placeholder
107
111
 
108
- const expiresAt = new Date(Date.now() + expiresIn * 1000);
112
+ const expires = Date.now() + expiresIn * 1000;
109
113
 
110
114
  return {
111
- url: `https://presigned-url-placeholder/${key}?expires=${expiresAt.getTime()}`,
112
- expiresAt,
115
+ url: `https://presigned-url-placeholder/${key}?expires=${expires}`,
116
+ expires,
113
117
  };
114
118
  }
115
119
 
@@ -120,11 +124,11 @@ class R2Service implements IR2Service {
120
124
  const objectKey = key || `uploads/${Date.now()}-${file.name}`;
121
125
 
122
126
  await this.put(objectKey, file.stream(), {
123
- ...options,
124
127
  httpMetadata: {
125
128
  contentType: file.type,
126
129
  ...options?.httpMetadata,
127
130
  },
131
+ customMetadata: options?.customMetadata,
128
132
  });
129
133
  }
130
134
 
@@ -134,10 +138,16 @@ class R2Service implements IR2Service {
134
138
  throw new Error(`Failed to fetch URL: ${response.statusText}`);
135
139
  }
136
140
 
137
- await this.put(key, response.body, {
141
+ const body = response.body;
142
+ if (!body) {
143
+ throw new Error(`Failed to read response body`);
144
+ }
145
+
146
+ await this.put(key, body, {
138
147
  httpMetadata: {
139
148
  contentType: response.headers.get("Content-Type") || undefined,
140
149
  },
150
+ binding,
141
151
  });
142
152
  }
143
153
 
@@ -155,10 +165,12 @@ class R2Service implements IR2Service {
155
165
 
156
166
  await this.deleteMultiple(list.objects.map((obj) => obj.key), binding);
157
167
 
158
- if (list.truncated && list.cursor) {
168
+ if (list.cursor) {
159
169
  await this.deletePrefix(prefix, binding);
160
170
  }
161
171
  }
162
172
  }
163
173
 
174
+ // Export class and singleton instance
175
+ export { R2Service };
164
176
  export const r2Service = new R2Service();
@@ -3,8 +3,24 @@
3
3
  * @description Cloudflare Worker configuration and types
4
4
  */
5
5
 
6
- export interface WorkerRequest extends Request {
6
+ export interface WorkerRequest {
7
+ url: string;
8
+ method: string;
9
+ headers: Headers;
10
+ body?: ReadableStream | null;
7
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>;
8
24
  }
9
25
 
10
26
  export interface WorkerResponse extends Response {
@@ -3,7 +3,9 @@
3
3
  * @description Example worker using @umituz/web-cloudflare package
4
4
  */
5
5
 
6
- import { workersService, kvService, r2Service } from "@umituz/web-cloudflare";
6
+ import { workersService, kvService } from "@umituz/web-cloudflare";
7
+ import type { Env } from "../types/env.types";
8
+ import type { WorkerRequest } from "../entities";
7
9
 
8
10
  // Configure routes
9
11
  workersService.route("/", async () => {
@@ -11,14 +13,15 @@ workersService.route("/", async () => {
11
13
  });
12
14
 
13
15
  workersService.route("/api/hello", async (request) => {
14
- const { name } = await request.json();
16
+ const data = await request.json() as { name?: string };
17
+ const name = data?.name || "World";
15
18
 
16
19
  return workersService.json({ message: `Hello, ${name}!` });
17
20
  });
18
21
 
19
- workersService.route("/api/cache/:key", async (request, env) => {
22
+ workersService.route("/api/cache/:key", async (request, env?: Env) => {
20
23
  const url = new URL(request.url);
21
- const key = url.pathname.split("/").pop();
24
+ const key = url.pathname.split("/").pop() || "";
22
25
 
23
26
  if (request.method === "GET") {
24
27
  const value = await kvService.get(key, env?.KV ? "KV" : undefined);
@@ -26,8 +29,8 @@ workersService.route("/api/cache/:key", async (request, env) => {
26
29
  }
27
30
 
28
31
  if (request.method === "POST") {
29
- const { value } = await request.json();
30
- await kvService.put(key, value, { ttl: 3600 });
32
+ const data = await request.json() as { value?: unknown };
33
+ await kvService.put(key, data.value || "", { ttl: 3600 });
31
34
  return workersService.json({ success: true });
32
35
  }
33
36
 
@@ -36,6 +39,6 @@ workersService.route("/api/cache/:key", async (request, env) => {
36
39
 
37
40
  // Export for Cloudflare Workers
38
41
  export default {
39
- fetch: (request: Request, env: Env, ctx: ExecutionContext) =>
40
- workersService.fetch(request, env, ctx),
42
+ fetch: (request: Request, env?: Env, ctx?: ExecutionContext) =>
43
+ workersService.fetch(request as unknown as WorkerRequest, env, ctx),
41
44
  };
@@ -3,7 +3,7 @@
3
3
  * @description Cloudflare Workers HTTP handler and routing
4
4
  */
5
5
 
6
- import type { WorkerRequest, WorkerResponse, WorkerConfig } from "../entities";
6
+ import type { WorkerRequest, WorkerResponse, CloudflareWorkerConfig } from "../entities";
7
7
  import type { Env } from "../types";
8
8
 
9
9
  export interface WorkerFetchOptions {
@@ -61,9 +61,12 @@ class WorkersService {
61
61
  * Fetch handler
62
62
  */
63
63
  async fetch(request: WorkerRequest, env?: Env, ctx?: ExecutionContext): Promise<WorkerResponse> {
64
- // Initialize cache
65
- if (!this.cache && env) {
66
- this.cache = caches.default;
64
+ // Initialize cache if available in Workers runtime
65
+ if (!this.cache && env && typeof caches !== 'undefined') {
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
@@ -161,4 +164,6 @@ class WorkersService {
161
164
  }
162
165
  }
163
166
 
167
+ // Export class and singleton instance
168
+ export { WorkersService };
164
169
  export const workersService = new WorkersService();
@@ -18,6 +18,7 @@ export interface WorkflowStep {
18
18
  maxDelay: number;
19
19
  };
20
20
  dependencies?: string[];
21
+ inputs?: Record<string, unknown>;
21
22
  }
22
23
 
23
24
  /**
@@ -27,7 +28,14 @@ export interface WorkflowDefinition {
27
28
  id: string;
28
29
  name: string;
29
30
  description?: string;
31
+ version?: string;
30
32
  steps: WorkflowStep[];
33
+ retryConfig?: {
34
+ maxAttempts: number;
35
+ backoffMultiplier: number;
36
+ initialDelay: number;
37
+ maxDelay: number;
38
+ };
31
39
  }
32
40
 
33
41
  /**
@@ -36,13 +44,18 @@ export interface WorkflowDefinition {
36
44
  export interface WorkflowExecution {
37
45
  id: string;
38
46
  workflowId: string;
39
- status: 'pending' | 'running' | 'completed' | 'failed';
47
+ status: 'pending' | 'running' | 'completed' | 'failed' | 'retrying';
40
48
  currentStep?: string;
41
49
  startedAt: number;
42
50
  completedAt?: number;
43
51
  input: unknown;
44
52
  output?: unknown;
53
+ outputs?: Record<string, unknown>;
45
54
  error?: string;
55
+ completedSteps: string[];
56
+ failedSteps: string[];
57
+ inputs: Record<string, unknown>;
58
+ retryCount: number;
46
59
  }
47
60
 
48
61
  /**
@@ -7,12 +7,42 @@ import type {
7
7
  WorkflowDefinition,
8
8
  WorkflowExecution,
9
9
  WorkflowStep,
10
- WorkflowInstanceState,
11
- MediaProcessingWorkflow,
12
- AIGenerationWorkflow,
13
- BatchOperationWorkflow,
14
10
  } from '../entities';
15
11
 
12
+ // Additional workflow types
13
+ export type WorkflowInstanceState = 'pending' | 'running' | 'completed' | 'failed';
14
+
15
+ export interface WorkflowStepState {
16
+ data: Record<string, unknown>;
17
+ status: WorkflowInstanceState;
18
+ completedAt?: number;
19
+ }
20
+
21
+ export interface MediaProcessingWorkflow {
22
+ type: 'media-processing';
23
+ input: {
24
+ url: string;
25
+ operations: Array<{ type: string; params: Record<string, unknown> }>;
26
+ };
27
+ }
28
+
29
+ export interface AIGenerationWorkflow {
30
+ type: 'ai-generation';
31
+ input: {
32
+ prompt: string;
33
+ model: string;
34
+ parameters?: Record<string, unknown>;
35
+ };
36
+ }
37
+
38
+ export interface BatchOperationWorkflow {
39
+ type: 'batch-operation';
40
+ input: {
41
+ items: Array<{ id: string; data: unknown }>;
42
+ operation: string;
43
+ };
44
+ }
45
+
16
46
  export interface WorkflowServiceConfig {
17
47
  KV?: KVNamespace;
18
48
  D1?: D1Database;
@@ -73,6 +103,7 @@ export class WorkflowService {
73
103
  completedSteps: [],
74
104
  failedSteps: [],
75
105
  inputs,
106
+ input: inputs,
76
107
  startedAt: Date.now(),
77
108
  retryCount: 0,
78
109
  };
@@ -122,7 +153,7 @@ export class WorkflowService {
122
153
  execution.completedSteps.push(step.id);
123
154
 
124
155
  // Save step state for idempotency
125
- await this.saveStepState(execution.id, step.id, stepResult);
156
+ await this.saveStepState(execution.id, step.id, { result: stepResult } as Record<string, unknown>);
126
157
 
127
158
  } catch (error) {
128
159
  stepStatus[step.id] = 'failed';
@@ -257,11 +288,10 @@ export class WorkflowService {
257
288
  data: Record<string, unknown>
258
289
  ): Promise<void> {
259
290
  if (this.kv) {
260
- const state: WorkflowInstanceState = {
261
- executionId,
262
- stepId,
291
+ const state: WorkflowStepState = {
263
292
  data,
264
- timestamp: Date.now(),
293
+ status: 'completed',
294
+ completedAt: Date.now(),
265
295
  };
266
296
  await this.kv.put(
267
297
  `step:${executionId}:${stepId}`,
@@ -276,7 +306,7 @@ export class WorkflowService {
276
306
  private async getStepState(
277
307
  executionId: string,
278
308
  stepId: string
279
- ): Promise<WorkflowInstanceState | null> {
309
+ ): Promise<WorkflowStepState | null> {
280
310
  if (this.kv) {
281
311
  const data = await this.kv.get(`step:${executionId}:${stepId}`);
282
312
  return data ? JSON.parse(data) : null;
@@ -435,3 +465,6 @@ export const WORKFLOW_TEMPLATES: Record<string, Partial<WorkflowDefinition>> = {
435
465
  ],
436
466
  },
437
467
  };
468
+
469
+ // Export singleton instance
470
+ export const workflowService = new WorkflowService();