ai-props 0.1.2 → 2.0.2

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 (69) hide show
  1. package/.turbo/turbo-build.log +5 -0
  2. package/CHANGELOG.md +15 -0
  3. package/README.md +345 -352
  4. package/dist/ai.d.ts +125 -0
  5. package/dist/ai.d.ts.map +1 -0
  6. package/dist/ai.js +199 -0
  7. package/dist/ai.js.map +1 -0
  8. package/dist/cache.d.ts +66 -0
  9. package/dist/cache.d.ts.map +1 -0
  10. package/dist/cache.js +183 -0
  11. package/dist/cache.js.map +1 -0
  12. package/dist/generate.d.ts +69 -0
  13. package/dist/generate.d.ts.map +1 -0
  14. package/dist/generate.js +221 -0
  15. package/dist/generate.js.map +1 -0
  16. package/dist/hoc.d.ts +164 -0
  17. package/dist/hoc.d.ts.map +1 -0
  18. package/dist/hoc.js +236 -0
  19. package/dist/hoc.js.map +1 -0
  20. package/dist/index.d.ts +14 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js +21 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/types.d.ts +152 -0
  25. package/dist/types.d.ts.map +1 -0
  26. package/dist/types.js +7 -0
  27. package/dist/types.js.map +1 -0
  28. package/dist/validate.d.ts +58 -0
  29. package/dist/validate.d.ts.map +1 -0
  30. package/dist/validate.js +253 -0
  31. package/dist/validate.js.map +1 -0
  32. package/package.json +16 -63
  33. package/src/ai.ts +264 -0
  34. package/src/cache.ts +216 -0
  35. package/src/generate.ts +276 -0
  36. package/src/hoc.ts +309 -0
  37. package/src/index.ts +66 -0
  38. package/src/types.ts +167 -0
  39. package/src/validate.ts +333 -0
  40. package/test/ai.test.ts +327 -0
  41. package/test/cache.test.ts +236 -0
  42. package/test/generate.test.ts +406 -0
  43. package/test/hoc.test.ts +411 -0
  44. package/test/validate.test.ts +324 -0
  45. package/tsconfig.json +9 -0
  46. package/vitest.config.ts +15 -0
  47. package/LICENSE +0 -21
  48. package/dist/AI.d.ts +0 -27
  49. package/dist/AI.d.ts.map +0 -1
  50. package/dist/AI.test.d.ts +0 -2
  51. package/dist/AI.test.d.ts.map +0 -1
  52. package/dist/ai-props.es.js +0 -3697
  53. package/dist/ai-props.umd.js +0 -30
  54. package/dist/components/ErrorBoundary.d.ts +0 -17
  55. package/dist/components/ErrorBoundary.d.ts.map +0 -1
  56. package/dist/examples/BlogList.d.ts +0 -2
  57. package/dist/examples/BlogList.d.ts.map +0 -1
  58. package/dist/examples/BlogList.fixture.d.ts +0 -5
  59. package/dist/examples/BlogList.fixture.d.ts.map +0 -1
  60. package/dist/examples/HeroSection.d.ts +0 -2
  61. package/dist/examples/HeroSection.d.ts.map +0 -1
  62. package/dist/examples/HeroSection.fixture.d.ts +0 -5
  63. package/dist/examples/HeroSection.fixture.d.ts.map +0 -1
  64. package/dist/test/setup.d.ts +0 -2
  65. package/dist/test/setup.d.ts.map +0 -1
  66. package/dist/utils/schema.d.ts +0 -28
  67. package/dist/utils/schema.d.ts.map +0 -1
  68. package/dist/utils/styles.d.ts +0 -3
  69. package/dist/utils/styles.d.ts.map +0 -1
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Props generation for ai-props
3
+ *
4
+ * Core functionality for generating component props using AI.
5
+ *
6
+ * @packageDocumentation
7
+ */
8
+
9
+ import { generateObject, schema as createSchema, type SimpleSchema } from 'ai-functions'
10
+ import type {
11
+ PropSchema,
12
+ GeneratePropsOptions,
13
+ GeneratePropsResult,
14
+ AIPropsConfig,
15
+ } from './types.js'
16
+ import { createCacheKey, getDefaultCache } from './cache.js'
17
+
18
+ /**
19
+ * Default configuration
20
+ */
21
+ const DEFAULT_CONFIG: AIPropsConfig = {
22
+ model: 'sonnet',
23
+ cache: true,
24
+ cacheTTL: 5 * 60 * 1000, // 5 minutes
25
+ }
26
+
27
+ /**
28
+ * Global configuration
29
+ */
30
+ let globalConfig: AIPropsConfig = { ...DEFAULT_CONFIG }
31
+
32
+ /**
33
+ * Configure global AI props settings
34
+ */
35
+ export function configureAIProps(config: Partial<AIPropsConfig>): void {
36
+ globalConfig = { ...globalConfig, ...config }
37
+ }
38
+
39
+ /**
40
+ * Get current configuration
41
+ */
42
+ export function getConfig(): AIPropsConfig {
43
+ return { ...globalConfig }
44
+ }
45
+
46
+ /**
47
+ * Reset configuration to defaults
48
+ */
49
+ export function resetConfig(): void {
50
+ globalConfig = { ...DEFAULT_CONFIG }
51
+ }
52
+
53
+ /**
54
+ * Resolve a prop schema to a SimpleSchema
55
+ *
56
+ * If the schema is a string, it's treated as a named type
57
+ * that generates a single property or description.
58
+ */
59
+ function resolveSchema(schema: PropSchema): SimpleSchema {
60
+ if (typeof schema === 'string') {
61
+ // Named type or description string
62
+ return { value: schema }
63
+ }
64
+ return schema as SimpleSchema
65
+ }
66
+
67
+ /**
68
+ * Build a prompt from context and schema
69
+ */
70
+ function buildPrompt(
71
+ schema: PropSchema,
72
+ context?: Record<string, unknown>,
73
+ additionalPrompt?: string
74
+ ): string {
75
+ const parts: string[] = []
76
+
77
+ // Add context information
78
+ if (context && Object.keys(context).length > 0) {
79
+ parts.push('Given the following context:')
80
+ parts.push(JSON.stringify(context, null, 2))
81
+ parts.push('')
82
+ }
83
+
84
+ // Add schema information
85
+ if (typeof schema === 'string') {
86
+ parts.push(`Generate a value for: ${schema}`)
87
+ } else {
88
+ parts.push('Generate props matching the schema.')
89
+ }
90
+
91
+ // Add additional prompt
92
+ if (additionalPrompt) {
93
+ parts.push('')
94
+ parts.push(additionalPrompt)
95
+ }
96
+
97
+ return parts.join('\n')
98
+ }
99
+
100
+ /**
101
+ * Generate props using AI
102
+ *
103
+ * @example
104
+ * ```ts
105
+ * const result = await generateProps({
106
+ * schema: {
107
+ * title: 'A compelling page title',
108
+ * description: 'A brief description',
109
+ * keywords: ['Relevant SEO keywords'],
110
+ * },
111
+ * context: { topic: 'AI-powered applications' },
112
+ * })
113
+ *
114
+ * console.log(result.props)
115
+ * // { title: '...', description: '...', keywords: [...] }
116
+ * ```
117
+ */
118
+ export async function generateProps<T = Record<string, unknown>>(
119
+ options: GeneratePropsOptions
120
+ ): Promise<GeneratePropsResult<T>> {
121
+ const { schema, context, prompt, model, system } = options
122
+ const config = getConfig()
123
+ const startTime = Date.now()
124
+
125
+ // Check cache
126
+ if (config.cache) {
127
+ const cache = getDefaultCache()
128
+ const cacheKey = createCacheKey(schema, context)
129
+ const cached = cache.get<T>(cacheKey)
130
+
131
+ if (cached) {
132
+ return {
133
+ props: cached.props,
134
+ cached: true,
135
+ metadata: {
136
+ model: config.model || 'cached',
137
+ },
138
+ }
139
+ }
140
+ }
141
+
142
+ // Resolve schema
143
+ const resolvedSchema = resolveSchema(schema)
144
+
145
+ // Build prompt
146
+ const fullPrompt = buildPrompt(schema, context, prompt)
147
+
148
+ // Use custom generator if provided
149
+ if (config.generate) {
150
+ const props = await config.generate<T>(resolvedSchema, context || {})
151
+ return {
152
+ props,
153
+ cached: false,
154
+ metadata: {
155
+ model: 'custom',
156
+ duration: Date.now() - startTime,
157
+ },
158
+ }
159
+ }
160
+
161
+ // Generate using AI
162
+ const result = await generateObject({
163
+ model: model || config.model || 'sonnet',
164
+ schema: resolvedSchema,
165
+ prompt: fullPrompt,
166
+ system: system || config.system,
167
+ })
168
+
169
+ const props = result.object as T
170
+
171
+ // Cache result
172
+ if (config.cache) {
173
+ const cache = getDefaultCache()
174
+ const cacheKey = createCacheKey(schema, context)
175
+ cache.set(cacheKey, props)
176
+ }
177
+
178
+ return {
179
+ props,
180
+ cached: false,
181
+ metadata: {
182
+ model: model || config.model || 'sonnet',
183
+ duration: Date.now() - startTime,
184
+ },
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Generate props synchronously from cache or throw
190
+ *
191
+ * Useful for SSR scenarios where async isn't available.
192
+ * Throws if props aren't in cache.
193
+ */
194
+ export function getPropsSync<T = Record<string, unknown>>(
195
+ schema: PropSchema,
196
+ context?: Record<string, unknown>
197
+ ): T {
198
+ const cache = getDefaultCache()
199
+ const cacheKey = createCacheKey(schema, context)
200
+ const cached = cache.get<T>(cacheKey)
201
+
202
+ if (!cached) {
203
+ throw new Error(
204
+ 'Props not in cache. Use generateProps() first or ensure caching is enabled.'
205
+ )
206
+ }
207
+
208
+ return cached.props
209
+ }
210
+
211
+ /**
212
+ * Pre-generate props for warming the cache
213
+ *
214
+ * @example
215
+ * ```ts
216
+ * await prefetchProps([
217
+ * { schema: userProfileSchema, context: { userId: '123' } },
218
+ * { schema: productSchema, context: { category: 'electronics' } },
219
+ * ])
220
+ * ```
221
+ */
222
+ export async function prefetchProps(
223
+ requests: GeneratePropsOptions[]
224
+ ): Promise<void> {
225
+ await Promise.all(requests.map(generateProps))
226
+ }
227
+
228
+ /**
229
+ * Generate multiple prop sets in parallel
230
+ */
231
+ export async function generatePropsMany<T = Record<string, unknown>>(
232
+ requests: GeneratePropsOptions[]
233
+ ): Promise<GeneratePropsResult<T>[]> {
234
+ return Promise.all(requests.map(req => generateProps<T>(req)))
235
+ }
236
+
237
+ /**
238
+ * Merge partial props with generated props
239
+ *
240
+ * Generates only the missing props, keeping provided ones.
241
+ */
242
+ export async function mergeWithGenerated<T extends Record<string, unknown>>(
243
+ schema: PropSchema,
244
+ partialProps: Partial<T>,
245
+ options?: Omit<GeneratePropsOptions, 'schema' | 'context'>
246
+ ): Promise<T> {
247
+ // Get list of missing keys
248
+ const schemaObj = typeof schema === 'string' ? { value: schema } : schema
249
+ const schemaKeys = Object.keys(schemaObj as Record<string, unknown>)
250
+ const providedKeys = Object.keys(partialProps)
251
+ const missingKeys = schemaKeys.filter(k => !providedKeys.includes(k))
252
+
253
+ // If all props are provided, return as-is
254
+ if (missingKeys.length === 0) {
255
+ return partialProps as T
256
+ }
257
+
258
+ // Build partial schema for missing props only
259
+ const partialSchema: Record<string, unknown> = {}
260
+ for (const key of missingKeys) {
261
+ partialSchema[key] = (schemaObj as Record<string, unknown>)[key]
262
+ }
263
+
264
+ // Generate missing props
265
+ const result = await generateProps<Partial<T>>({
266
+ schema: partialSchema as SimpleSchema,
267
+ context: partialProps as Record<string, unknown>,
268
+ ...options,
269
+ })
270
+
271
+ // Merge
272
+ return {
273
+ ...result.props,
274
+ ...partialProps,
275
+ } as T
276
+ }
package/src/hoc.ts ADDED
@@ -0,0 +1,309 @@
1
+ /**
2
+ * Higher-Order Component (HOC) for React components
3
+ *
4
+ * Provides withAIProps HOC for wrapping React components
5
+ * with AI-powered prop generation.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ import type { SimpleSchema } from 'ai-functions'
11
+ import type { PropSchema, AIComponentOptions, AIPropsConfig } from './types.js'
12
+ import { generateProps, mergeWithGenerated } from './generate.js'
13
+
14
+ /**
15
+ * Options for withAIProps HOC
16
+ */
17
+ export interface WithAIPropsOptions<P> extends Omit<AIComponentOptions<P>, 'schema'> {
18
+ /** Schema for props to generate */
19
+ schema: PropSchema
20
+ /** Loading component to show while generating */
21
+ loading?: () => unknown
22
+ /** Error component to show on generation failure */
23
+ error?: (error: Error) => unknown
24
+ /** Fallback props to use if generation fails */
25
+ fallback?: Partial<P>
26
+ }
27
+
28
+ /**
29
+ * Create props wrapper that can be used with any component system
30
+ *
31
+ * This is framework-agnostic and returns the enhanced props.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * const enhancer = createPropsEnhancer({
36
+ * schema: {
37
+ * title: 'Page title',
38
+ * description: 'Page description',
39
+ * },
40
+ * defaults: {
41
+ * title: 'Default Title',
42
+ * },
43
+ * })
44
+ *
45
+ * // Use with any component
46
+ * const props = await enhancer({ description: 'My page' })
47
+ * // { title: 'Default Title', description: 'My page' }
48
+ *
49
+ * const generatedProps = await enhancer({})
50
+ * // { title: 'AI-generated title', description: 'AI-generated description' }
51
+ * ```
52
+ */
53
+ export function createPropsEnhancer<P extends Record<string, unknown>>(
54
+ options: WithAIPropsOptions<P>
55
+ ) {
56
+ const { schema, defaults = {}, required = [], exclude = [], config = {}, fallback } = options
57
+
58
+ return async (partialProps: Partial<P>): Promise<P> => {
59
+ try {
60
+ // Merge with defaults
61
+ const propsWithDefaults = { ...defaults, ...partialProps }
62
+
63
+ // Check required props
64
+ const missingRequired = (required as string[]).filter(
65
+ key => propsWithDefaults[key as keyof P] === undefined
66
+ )
67
+ if (missingRequired.length > 0) {
68
+ throw new Error(`Missing required props: ${missingRequired.join(', ')}`)
69
+ }
70
+
71
+ // Filter out excluded props from schema
72
+ const filteredSchema = filterSchemaKeys(schema, exclude as string[])
73
+
74
+ // Generate missing props
75
+ return await mergeWithGenerated<P>(
76
+ filteredSchema,
77
+ propsWithDefaults as Partial<P>,
78
+ {
79
+ model: config.model,
80
+ system: config.system,
81
+ }
82
+ )
83
+ } catch (error) {
84
+ if (fallback) {
85
+ return { ...defaults, ...fallback, ...partialProps } as P
86
+ }
87
+ throw error
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Filter schema to exclude certain keys
94
+ */
95
+ function filterSchemaKeys(schema: PropSchema, exclude: string[]): PropSchema {
96
+ if (typeof schema === 'string' || exclude.length === 0) {
97
+ return schema
98
+ }
99
+
100
+ const filtered: Record<string, unknown> = {}
101
+ for (const [key, value] of Object.entries(schema)) {
102
+ if (!exclude.includes(key)) {
103
+ filtered[key] = value
104
+ }
105
+ }
106
+ return filtered as SimpleSchema
107
+ }
108
+
109
+ /**
110
+ * Create an async props provider
111
+ *
112
+ * Returns a function that generates props on each call.
113
+ * Useful for SSR and static generation.
114
+ *
115
+ * @example
116
+ * ```ts
117
+ * const getPageProps = createAsyncPropsProvider({
118
+ * schema: {
119
+ * title: 'SEO-optimized page title',
120
+ * meta: { description: 'Meta description' },
121
+ * },
122
+ * })
123
+ *
124
+ * // In getStaticProps or getServerSideProps
125
+ * export async function getStaticProps() {
126
+ * const props = await getPageProps({ slug: 'about' })
127
+ * return { props }
128
+ * }
129
+ * ```
130
+ */
131
+ export function createAsyncPropsProvider<P extends Record<string, unknown>>(
132
+ options: WithAIPropsOptions<P>
133
+ ) {
134
+ const enhancer = createPropsEnhancer(options)
135
+
136
+ return {
137
+ /**
138
+ * Get props with AI generation
139
+ */
140
+ getProps: enhancer,
141
+
142
+ /**
143
+ * Get props for multiple items
144
+ */
145
+ getManyProps: async (contexts: Partial<P>[]): Promise<P[]> => {
146
+ return Promise.all(contexts.map(enhancer))
147
+ },
148
+
149
+ /**
150
+ * Get props with caching hint
151
+ */
152
+ getCachedProps: async (
153
+ context: Partial<P>,
154
+ revalidate?: number
155
+ ): Promise<{ props: P; revalidate?: number }> => {
156
+ const props = await enhancer(context)
157
+ return { props, revalidate }
158
+ },
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Create a props transformer
164
+ *
165
+ * Transforms existing props by filling in missing values.
166
+ *
167
+ * @example
168
+ * ```ts
169
+ * const transformUserProps = createPropsTransformer({
170
+ * schema: {
171
+ * displayName: 'Display name for the user',
172
+ * initials: 'User initials (2 letters)',
173
+ * },
174
+ * })
175
+ *
176
+ * const user = await transformUserProps({
177
+ * username: 'johndoe',
178
+ * email: 'john@example.com',
179
+ * })
180
+ * // { username: 'johndoe', email: '...', displayName: 'John Doe', initials: 'JD' }
181
+ * ```
182
+ */
183
+ export function createPropsTransformer<
184
+ I extends Record<string, unknown>,
185
+ O extends Record<string, unknown>
186
+ >(options: {
187
+ schema: PropSchema
188
+ transform?: (input: I, generated: Record<string, unknown>) => O
189
+ config?: AIPropsConfig
190
+ }) {
191
+ const { schema, transform, config = {} } = options
192
+
193
+ return async (input: I): Promise<I & O> => {
194
+ const result = await generateProps({
195
+ schema,
196
+ context: input,
197
+ model: config.model,
198
+ system: config.system,
199
+ })
200
+
201
+ const generated = result.props as Record<string, unknown>
202
+
203
+ if (transform) {
204
+ return { ...input, ...transform(input, generated) }
205
+ }
206
+
207
+ return { ...input, ...generated } as I & O
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Create a conditional props generator
213
+ *
214
+ * Only generates props when a condition is met.
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * const maybeGenerateProps = createConditionalGenerator({
219
+ * schema: { summary: 'Article summary' },
220
+ * condition: (props) => !props.summary && props.content?.length > 100,
221
+ * })
222
+ * ```
223
+ */
224
+ export function createConditionalGenerator<P extends Record<string, unknown>>(options: {
225
+ schema: PropSchema
226
+ condition: (props: Partial<P>) => boolean
227
+ config?: AIPropsConfig
228
+ }) {
229
+ const { schema, condition, config = {} } = options
230
+
231
+ return async (props: Partial<P>): Promise<P> => {
232
+ if (!condition(props)) {
233
+ return props as P
234
+ }
235
+
236
+ return mergeWithGenerated(schema, props, {
237
+ model: config.model,
238
+ system: config.system,
239
+ })
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Batch props generator for list rendering
245
+ *
246
+ * Efficiently generates props for multiple items.
247
+ *
248
+ * @example
249
+ * ```ts
250
+ * const batchGenerator = createBatchGenerator({
251
+ * schema: { title: 'Item title', description: 'Item description' },
252
+ * })
253
+ *
254
+ * const items = await batchGenerator.generate([
255
+ * { id: 1, category: 'tech' },
256
+ * { id: 2, category: 'science' },
257
+ * { id: 3, category: 'art' },
258
+ * ])
259
+ * ```
260
+ */
261
+ export function createBatchGenerator<P extends Record<string, unknown>>(options: {
262
+ schema: PropSchema
263
+ concurrency?: number
264
+ config?: AIPropsConfig
265
+ }) {
266
+ const { schema, concurrency = 3, config = {} } = options
267
+
268
+ return {
269
+ /**
270
+ * Generate props for multiple items
271
+ */
272
+ generate: async (items: Partial<P>[]): Promise<P[]> => {
273
+ const results: P[] = []
274
+
275
+ // Process in batches based on concurrency
276
+ for (let i = 0; i < items.length; i += concurrency) {
277
+ const batch = items.slice(i, i + concurrency)
278
+ const batchResults = await Promise.all(
279
+ batch.map(item =>
280
+ mergeWithGenerated<P>(schema, item, {
281
+ model: config.model,
282
+ system: config.system,
283
+ })
284
+ )
285
+ )
286
+ results.push(...batchResults)
287
+ }
288
+
289
+ return results
290
+ },
291
+
292
+ /**
293
+ * Generate props one at a time (for rate limiting)
294
+ */
295
+ generateSequential: async (items: Partial<P>[]): Promise<P[]> => {
296
+ const results: P[] = []
297
+
298
+ for (const item of items) {
299
+ const result = await mergeWithGenerated<P>(schema, item, {
300
+ model: config.model,
301
+ system: config.system,
302
+ })
303
+ results.push(result)
304
+ }
305
+
306
+ return results
307
+ },
308
+ }
309
+ }
package/src/index.ts ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * ai-props - AI-powered props for intelligent component properties
3
+ *
4
+ * This package provides utilities for automatically generating
5
+ * component props using AI based on schema definitions.
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+
10
+ // Export types
11
+ export * from './types.js'
12
+
13
+ // Export core functionality
14
+ export {
15
+ AI,
16
+ createAIComponent,
17
+ definePropsSchema,
18
+ createComponentFactory,
19
+ composeAIComponents,
20
+ } from './ai.js'
21
+
22
+ // Export generation functions
23
+ export {
24
+ generateProps,
25
+ getPropsSync,
26
+ prefetchProps,
27
+ generatePropsMany,
28
+ mergeWithGenerated,
29
+ configureAIProps,
30
+ getConfig,
31
+ resetConfig,
32
+ } from './generate.js'
33
+
34
+ // Export cache utilities
35
+ export {
36
+ MemoryPropsCache,
37
+ LRUPropsCache,
38
+ createCacheKey,
39
+ getDefaultCache,
40
+ configureCache,
41
+ clearCache,
42
+ DEFAULT_CACHE_TTL,
43
+ } from './cache.js'
44
+
45
+ // Export HOC and enhancers
46
+ export {
47
+ createPropsEnhancer,
48
+ createAsyncPropsProvider,
49
+ createPropsTransformer,
50
+ createConditionalGenerator,
51
+ createBatchGenerator,
52
+ type WithAIPropsOptions,
53
+ } from './hoc.js'
54
+
55
+ // Export validation utilities
56
+ export {
57
+ validateProps,
58
+ hasRequiredProps,
59
+ getMissingProps,
60
+ isComplete,
61
+ getMissingFromSchema,
62
+ sanitizeProps,
63
+ mergeWithDefaults,
64
+ createValidator,
65
+ assertValidProps,
66
+ } from './validate.js'