ai-props 2.1.3 → 2.4.0

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 (105) hide show
  1. package/.dev.vars +2 -0
  2. package/.turbo/turbo-build.log +1 -1
  3. package/CHANGELOG.md +20 -0
  4. package/README.md +2 -0
  5. package/dist/ai.d.ts.map +1 -1
  6. package/dist/ai.js +4 -4
  7. package/dist/ai.js.map +1 -1
  8. package/dist/cascade.d.ts +329 -0
  9. package/dist/cascade.d.ts.map +1 -0
  10. package/dist/cascade.js +522 -0
  11. package/dist/cascade.js.map +1 -0
  12. package/dist/client.d.ts +233 -0
  13. package/dist/client.d.ts.map +1 -0
  14. package/dist/client.js +191 -0
  15. package/dist/client.js.map +1 -0
  16. package/dist/durable-cascade.d.ts +280 -0
  17. package/dist/durable-cascade.d.ts.map +1 -0
  18. package/dist/durable-cascade.js +469 -0
  19. package/dist/durable-cascade.js.map +1 -0
  20. package/dist/event-bridge.d.ts +257 -0
  21. package/dist/event-bridge.d.ts.map +1 -0
  22. package/dist/event-bridge.js +317 -0
  23. package/dist/event-bridge.js.map +1 -0
  24. package/dist/generate.d.ts.map +1 -1
  25. package/dist/generate.js +12 -6
  26. package/dist/generate.js.map +1 -1
  27. package/dist/hoc.d.ts.map +1 -1
  28. package/dist/hoc.js +13 -13
  29. package/dist/hoc.js.map +1 -1
  30. package/dist/hono-jsx.d.ts +208 -0
  31. package/dist/hono-jsx.d.ts.map +1 -0
  32. package/dist/hono-jsx.js +459 -0
  33. package/dist/hono-jsx.js.map +1 -0
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +2 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/mdx-types.d.ts +152 -0
  39. package/dist/mdx-types.d.ts.map +1 -0
  40. package/dist/mdx-types.js +9 -0
  41. package/dist/mdx-types.js.map +1 -0
  42. package/dist/mdx-utils.d.ts +106 -0
  43. package/dist/mdx-utils.d.ts.map +1 -0
  44. package/dist/mdx-utils.js +384 -0
  45. package/dist/mdx-utils.js.map +1 -0
  46. package/dist/mdx.d.ts +230 -0
  47. package/dist/mdx.d.ts.map +1 -0
  48. package/dist/mdx.js +820 -0
  49. package/dist/mdx.js.map +1 -0
  50. package/dist/rpc.d.ts +313 -0
  51. package/dist/rpc.d.ts.map +1 -0
  52. package/dist/rpc.js +359 -0
  53. package/dist/rpc.js.map +1 -0
  54. package/dist/streaming.d.ts +199 -0
  55. package/dist/streaming.d.ts.map +1 -0
  56. package/dist/streaming.js +402 -0
  57. package/dist/streaming.js.map +1 -0
  58. package/dist/validate.d.ts.map +1 -1
  59. package/dist/validate.js +11 -13
  60. package/dist/validate.js.map +1 -1
  61. package/dist/worker.d.ts +270 -0
  62. package/dist/worker.d.ts.map +1 -0
  63. package/dist/worker.js +405 -0
  64. package/dist/worker.js.map +1 -0
  65. package/package.json +39 -13
  66. package/src/ai.ts +12 -31
  67. package/src/cascade.ts +795 -0
  68. package/src/client.ts +440 -0
  69. package/src/durable-cascade.ts +743 -0
  70. package/src/event-bridge.ts +478 -0
  71. package/src/generate.ts +14 -12
  72. package/src/hoc.ts +15 -19
  73. package/src/hono-jsx.ts +675 -0
  74. package/src/index.ts +30 -0
  75. package/src/mdx-types.ts +169 -0
  76. package/src/mdx-utils.ts +437 -0
  77. package/src/mdx.ts +1008 -0
  78. package/src/rpc.ts +614 -0
  79. package/src/streaming.ts +618 -0
  80. package/src/validate.ts +15 -29
  81. package/src/worker.ts +547 -0
  82. package/test/cascade.test.ts +338 -0
  83. package/test/durable-cascade.test.ts +319 -0
  84. package/test/event-bridge.test.ts +351 -0
  85. package/test/generate.test.ts +6 -16
  86. package/test/mdx.test.ts +817 -0
  87. package/test/worker/capnweb-rpc.test.ts +1084 -0
  88. package/test/worker/full-flow.integration.test.ts +1463 -0
  89. package/test/worker/hono-jsx.test.ts +1258 -0
  90. package/test/worker/mdx-parsing.test.ts +1148 -0
  91. package/test/worker/setup.ts +56 -0
  92. package/test/worker.test.ts +595 -0
  93. package/tsconfig.json +2 -1
  94. package/vitest.config.js +6 -0
  95. package/vitest.config.ts +15 -1
  96. package/vitest.workers.config.ts +58 -0
  97. package/wrangler.jsonc +27 -0
  98. package/LICENSE +0 -21
  99. package/src/ai.js +0 -198
  100. package/src/cache.js +0 -182
  101. package/src/generate.js +0 -220
  102. package/src/hoc.js +0 -235
  103. package/src/index.js +0 -20
  104. package/src/types.js +0 -6
  105. package/src/validate.js +0 -252
package/src/client.ts ADDED
@@ -0,0 +1,440 @@
1
+ /**
2
+ * RPC Client for AI Props
3
+ *
4
+ * Provides a typed RPC client that connects to the deployed
5
+ * ai-props worker using rpc.do for remote procedure calls.
6
+ *
7
+ * This module is for client-side usage - it provides types and
8
+ * helpers for consuming the PropsService worker from other workers
9
+ * or client applications.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { createPropsClient } from 'ai-props/client'
14
+ *
15
+ * const client = createPropsClient('https://ai-props.workers.dev')
16
+ * const result = await client.generate({
17
+ * schema: { title: 'Page title', description: 'Page description' },
18
+ * context: { topic: 'AI' }
19
+ * })
20
+ * console.log(result.props)
21
+ * ```
22
+ *
23
+ * @packageDocumentation
24
+ */
25
+
26
+ // ==================== Re-export Types ====================
27
+
28
+ // Re-export core types needed for client usage
29
+ export type {
30
+ PropSchema,
31
+ GeneratePropsOptions,
32
+ GeneratePropsResult,
33
+ AIPropsConfig,
34
+ PropsCache,
35
+ PropsCacheEntry,
36
+ ValidationResult,
37
+ ValidationError,
38
+ UseAIPropsResult,
39
+ UseAIPropsOptions,
40
+ AIComponentOptions,
41
+ AIComponent,
42
+ InferProps,
43
+ } from './types.js'
44
+
45
+ // Re-export RPC types from rpc.ts
46
+ export type {
47
+ PropsServiceCoreInterface,
48
+ PropsClientOptions,
49
+ PropsClientInstance,
50
+ PropsRPCErrorCode,
51
+ PropsRPCError,
52
+ RetryConfig,
53
+ RetryBackoff,
54
+ PropsServiceBinding,
55
+ ExtractService,
56
+ } from './rpc.js'
57
+
58
+ export { isPropsRPCError, withRetry, calculateRetryDelay, DEFAULT_RETRY_CONFIG } from './rpc.js'
59
+
60
+ // ==================== HTTP Client Types ====================
61
+
62
+ /**
63
+ * Configuration for the AI Props HTTP RPC client
64
+ */
65
+ export interface PropsHttpClientConfig {
66
+ /** RPC endpoint URL (default: https://ai-props.do) */
67
+ url?: string
68
+ /** Authentication token */
69
+ token?: string
70
+ /** Request timeout in milliseconds (default: 30000) */
71
+ timeout?: number
72
+ /** Custom headers to include in requests */
73
+ headers?: Record<string, string>
74
+ }
75
+
76
+ // ==================== Client API Interface ====================
77
+
78
+ import type {
79
+ PropSchema,
80
+ GeneratePropsOptions,
81
+ GeneratePropsResult,
82
+ AIPropsConfig,
83
+ PropsCacheEntry,
84
+ ValidationResult,
85
+ } from './types.js'
86
+
87
+ /**
88
+ * PropsClientAPI - Type-safe interface for the AI Props RPC client
89
+ *
90
+ * This interface mirrors all public methods on PropsServiceCore so that
91
+ * the RPC client provides full type safety when calling remote methods.
92
+ */
93
+ export interface PropsClientAPI {
94
+ // ==================== Generation ====================
95
+
96
+ /**
97
+ * Generate props using AI
98
+ */
99
+ generate<T = Record<string, unknown>>(
100
+ options: GeneratePropsOptions
101
+ ): Promise<GeneratePropsResult<T>>
102
+
103
+ /**
104
+ * Generate props synchronously from cache
105
+ * @throws {Error} If props are not in cache
106
+ */
107
+ getSync<T = Record<string, unknown>>(
108
+ schema: PropSchema,
109
+ context?: Record<string, unknown>
110
+ ): Promise<T>
111
+
112
+ /**
113
+ * Pre-generate props for warming the cache
114
+ */
115
+ prefetch(requests: GeneratePropsOptions[]): Promise<void>
116
+
117
+ /**
118
+ * Generate multiple prop sets in parallel
119
+ */
120
+ generateMany<T = Record<string, unknown>>(
121
+ requests: GeneratePropsOptions[]
122
+ ): Promise<GeneratePropsResult<T>[]>
123
+
124
+ /**
125
+ * Merge partial props with generated props
126
+ */
127
+ mergeWithGenerated<T extends Record<string, unknown>>(
128
+ schema: PropSchema,
129
+ partialProps: Partial<T>,
130
+ options?: Omit<GeneratePropsOptions, 'schema' | 'context'>
131
+ ): Promise<T>
132
+
133
+ // ==================== Configuration ====================
134
+
135
+ /**
136
+ * Configure global AI props settings
137
+ */
138
+ configure(config: Partial<AIPropsConfig>): Promise<void>
139
+
140
+ /**
141
+ * Get current configuration
142
+ */
143
+ getConfig(): Promise<AIPropsConfig>
144
+
145
+ /**
146
+ * Reset configuration to defaults
147
+ */
148
+ resetConfig(): Promise<void>
149
+
150
+ // ==================== Cache Operations ====================
151
+
152
+ /**
153
+ * Get cached props by key
154
+ */
155
+ getCached<T>(key: string): Promise<PropsCacheEntry<T> | undefined>
156
+
157
+ /**
158
+ * Set props in cache
159
+ */
160
+ setCached<T>(key: string, props: T): Promise<void>
161
+
162
+ /**
163
+ * Delete cached entry by key
164
+ */
165
+ deleteCached(key: string): Promise<boolean>
166
+
167
+ /**
168
+ * Clear all cached props
169
+ */
170
+ clearCache(): Promise<void>
171
+
172
+ /**
173
+ * Get cache size
174
+ */
175
+ getCacheSize(): Promise<number>
176
+
177
+ /**
178
+ * Create a cache key from schema and context
179
+ */
180
+ createCacheKey(schema: PropSchema, context?: Record<string, unknown>): Promise<string>
181
+
182
+ /**
183
+ * Configure cache TTL
184
+ */
185
+ configureCache(ttl: number): Promise<void>
186
+
187
+ // ==================== Validation ====================
188
+
189
+ /**
190
+ * Validate props against a schema
191
+ */
192
+ validate(props: Record<string, unknown>, schema: PropSchema): Promise<ValidationResult>
193
+
194
+ /**
195
+ * Check if all required props are present
196
+ */
197
+ hasRequired(props: Record<string, unknown>, required: string[]): Promise<boolean>
198
+
199
+ /**
200
+ * Get list of missing props according to schema
201
+ */
202
+ getMissing(props: Record<string, unknown>, schema: PropSchema): Promise<string[]>
203
+
204
+ /**
205
+ * Check if props are complete according to schema
206
+ */
207
+ isComplete(props: Record<string, unknown>, schema: PropSchema): Promise<boolean>
208
+
209
+ /**
210
+ * Sanitize props by removing unknown keys
211
+ */
212
+ sanitize<T extends Record<string, unknown>>(props: T, schema: PropSchema): Promise<Partial<T>>
213
+
214
+ /**
215
+ * Merge props with default values
216
+ */
217
+ mergeDefaults<T extends Record<string, unknown>>(
218
+ props: Partial<T>,
219
+ defaults: Partial<T>,
220
+ schema: PropSchema
221
+ ): Promise<Partial<T>>
222
+ }
223
+
224
+ // ==================== HTTP Client Implementation ====================
225
+
226
+ /** Default URL for the ai-props worker */
227
+ const DEFAULT_URL = 'https://ai-props.do'
228
+
229
+ /**
230
+ * Create a typed HTTP RPC client for the ai-props worker
231
+ *
232
+ * This client communicates with the deployed ai-props worker over HTTP,
233
+ * sending JSON-RPC requests to the /rpc endpoint.
234
+ *
235
+ * @param config - Client configuration options
236
+ * @returns A typed RPC client with all PropsService methods
237
+ *
238
+ * @example Basic usage
239
+ * ```ts
240
+ * import { createPropsClient } from 'ai-props/client'
241
+ *
242
+ * const client = createPropsClient({
243
+ * url: 'https://ai-props.workers.dev',
244
+ * token: 'my-api-token'
245
+ * })
246
+ *
247
+ * // Generate props
248
+ * const result = await client.generate({
249
+ * schema: { title: 'Page title', description: 'Description' },
250
+ * context: { topic: 'Technology' }
251
+ * })
252
+ * console.log(result.props)
253
+ *
254
+ * // Validate props
255
+ * const validation = await client.validate(
256
+ * { title: 'Hello' },
257
+ * { title: 'string', description: 'string' }
258
+ * )
259
+ * console.log(validation.valid) // false - missing description
260
+ * ```
261
+ *
262
+ * @example With custom headers
263
+ * ```ts
264
+ * const client = createPropsClient({
265
+ * url: 'https://ai-props.workers.dev',
266
+ * headers: {
267
+ * 'X-Custom-Header': 'value',
268
+ * },
269
+ * timeout: 60000, // 60 seconds
270
+ * })
271
+ * ```
272
+ */
273
+ export function createPropsClient(config: PropsHttpClientConfig = {}): PropsClientAPI {
274
+ const url = config.url || DEFAULT_URL
275
+ const timeout = config.timeout || 30000
276
+
277
+ /**
278
+ * Make an RPC call to the remote service
279
+ */
280
+ async function rpc<T>(method: string, args: unknown[] = []): Promise<T> {
281
+ const controller = new AbortController()
282
+ const timeoutId = setTimeout(() => controller.abort(), timeout)
283
+
284
+ try {
285
+ const response = await fetch(`${url}/rpc`, {
286
+ method: 'POST',
287
+ headers: {
288
+ 'Content-Type': 'application/json',
289
+ ...(config.token && { Authorization: `Bearer ${config.token}` }),
290
+ ...config.headers,
291
+ },
292
+ body: JSON.stringify({ method, args }),
293
+ signal: controller.signal,
294
+ })
295
+
296
+ if (!response.ok) {
297
+ const error = await response.json().catch(() => ({ error: 'Unknown error' }))
298
+ throw new Error((error as { error?: string }).error || `RPC error: ${response.status}`)
299
+ }
300
+
301
+ const result = (await response.json()) as { result: T; error?: string }
302
+
303
+ if (result.error) {
304
+ throw new Error(result.error)
305
+ }
306
+
307
+ return result.result
308
+ } finally {
309
+ clearTimeout(timeoutId)
310
+ }
311
+ }
312
+
313
+ // Build client with typed methods
314
+ return {
315
+ // Generation
316
+ generate: <T>(options: GeneratePropsOptions) =>
317
+ rpc<GeneratePropsResult<T>>('generate', [options]),
318
+
319
+ getSync: <T>(schema: PropSchema, context?: Record<string, unknown>) =>
320
+ rpc<T>('getSync', [schema, context]),
321
+
322
+ prefetch: (requests: GeneratePropsOptions[]) => rpc<void>('prefetch', [requests]),
323
+
324
+ generateMany: <T>(requests: GeneratePropsOptions[]) =>
325
+ rpc<GeneratePropsResult<T>[]>('generateMany', [requests]),
326
+
327
+ mergeWithGenerated: <T extends Record<string, unknown>>(
328
+ schema: PropSchema,
329
+ partialProps: Partial<T>,
330
+ options?: Omit<GeneratePropsOptions, 'schema' | 'context'>
331
+ ) => rpc<T>('mergeWithGenerated', [schema, partialProps, options]),
332
+
333
+ // Configuration
334
+ configure: (cfg: Partial<AIPropsConfig>) => rpc<void>('configure', [cfg]),
335
+
336
+ getConfig: () => rpc<AIPropsConfig>('getConfig'),
337
+
338
+ resetConfig: () => rpc<void>('resetConfig'),
339
+
340
+ // Cache
341
+ getCached: <T>(key: string) => rpc<PropsCacheEntry<T> | undefined>('getCached', [key]),
342
+
343
+ setCached: <T>(key: string, props: T) => rpc<void>('setCached', [key, props]),
344
+
345
+ deleteCached: (key: string) => rpc<boolean>('deleteCached', [key]),
346
+
347
+ clearCache: () => rpc<void>('clearCache'),
348
+
349
+ getCacheSize: () => rpc<number>('getCacheSize'),
350
+
351
+ createCacheKey: (schema: PropSchema, context?: Record<string, unknown>) =>
352
+ rpc<string>('createCacheKey', [schema, context]),
353
+
354
+ configureCache: (ttl: number) => rpc<void>('configureCache', [ttl]),
355
+
356
+ // Validation
357
+ validate: (props: Record<string, unknown>, schema: PropSchema) =>
358
+ rpc<ValidationResult>('validate', [props, schema]),
359
+
360
+ hasRequired: (props: Record<string, unknown>, required: string[]) =>
361
+ rpc<boolean>('hasRequired', [props, required]),
362
+
363
+ getMissing: (props: Record<string, unknown>, schema: PropSchema) =>
364
+ rpc<string[]>('getMissing', [props, schema]),
365
+
366
+ isComplete: (props: Record<string, unknown>, schema: PropSchema) =>
367
+ rpc<boolean>('isComplete', [props, schema]),
368
+
369
+ sanitize: <T extends Record<string, unknown>>(props: T, schema: PropSchema) =>
370
+ rpc<Partial<T>>('sanitize', [props, schema]),
371
+
372
+ mergeDefaults: <T extends Record<string, unknown>>(
373
+ props: Partial<T>,
374
+ defaults: Partial<T>,
375
+ schema: PropSchema
376
+ ) => rpc<Partial<T>>('mergeDefaults', [props, defaults, schema]),
377
+ }
378
+ }
379
+
380
+ // ==================== Service Binding Client ====================
381
+
382
+ /**
383
+ * Create a typed client from a Cloudflare Service Binding
384
+ *
385
+ * Use this when you have a service binding to the ai-props worker
386
+ * in your wrangler.jsonc configuration.
387
+ *
388
+ * @param binding - The service binding from your env
389
+ * @returns A typed client interface
390
+ *
391
+ * @example
392
+ * ```ts
393
+ * // wrangler.jsonc
394
+ * // {
395
+ * // "services": [
396
+ * // { "binding": "AI_PROPS", "service": "ai-props" }
397
+ * // ]
398
+ * // }
399
+ *
400
+ * import { createServiceClient } from 'ai-props/client'
401
+ * import type { PropsService } from 'ai-props/worker'
402
+ *
403
+ * interface Env {
404
+ * AI_PROPS: Service<PropsService>
405
+ * }
406
+ *
407
+ * export default {
408
+ * async fetch(request: Request, env: Env) {
409
+ * const client = createServiceClient(env.AI_PROPS)
410
+ * const result = await client.generate({
411
+ * schema: { title: 'Page title' }
412
+ * })
413
+ * return Response.json(result)
414
+ * }
415
+ * }
416
+ * ```
417
+ */
418
+ export function createServiceClient<T extends { getService(): unknown }>(
419
+ binding: T
420
+ ): ReturnType<T['getService']> {
421
+ return binding.getService() as ReturnType<T['getService']>
422
+ }
423
+
424
+ // ==================== Default Export ====================
425
+
426
+ /**
427
+ * Default client instance connected to the production ai-props worker
428
+ *
429
+ * @example
430
+ * ```ts
431
+ * import client from 'ai-props/client'
432
+ *
433
+ * const result = await client.generate({
434
+ * schema: { title: 'Page title' }
435
+ * })
436
+ * ```
437
+ */
438
+ const client: PropsClientAPI = createPropsClient()
439
+
440
+ export default client