Skip to content

Conversation

@tombeckenham
Copy link

@tombeckenham tombeckenham commented Jan 20, 2026

Summary

  • Add @tanstack/ai-fal package with image and video generation adapters
  • Leverage fal's type system (EndpointTypeMap) for autocomplete on 600+ models
  • Type-safe modelOptions that exclude fields TanStack AI handles (prompt, size, etc.)
  • Video adapter is experimental, supporting MiniMax, Luma, Kling, Hunyuan, etc.

Test plan

  • pnpm test:types passes
  • pnpm test:lib passes (27 tests)
  • pnpm build succeeds

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Fal.ai image generation integration with type-safe model support and configurable parameters
    • Added Fal.ai video generation integration with job management and status tracking
    • Introduced new @tanstack/ai-fal package for seamless Fal AI model integration
  • Tests

    • Added comprehensive test coverage for image and video generation adapters

✏️ Tip: You can customize this high-level summary in your review settings.

Add @tanstack/ai-fal package with:

- Image adapter supporting 600+ fal.ai models with full type inference
- Video adapter (experimental) for MiniMax, Luma, Kling, Hunyuan, etc.
- Type-safe modelOptions using fal's EndpointTypeMap for autocomplete
- FalModel, FalModelInput, FalModelOutput utility types
- FalImageProviderOptions/FalVideoProviderOptions that exclude fields
  TanStack AI handles (prompt, size, etc.)
- Size preset mapping utilities for fal.ai format
- Comprehensive test coverage for both adapters

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@tombeckenham tombeckenham requested a review from a team January 20, 2026 09:34
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 20, 2026

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive new @tanstack/ai-fal package providing TypeScript adapters for Fal.ai's image and video generation models, including client utilities, model type definitions, test coverage, and 15+ OpenAPI specifications for Fal.ai model categories.

Changes

Cohort / File(s) Summary
Configuration & Build Setup
packages/typescript/ai-fal/package.json, packages/typescript/ai-fal/tsconfig.json, packages/typescript/ai-fal/vite.config.ts, packages/typescript/ai-fal/eslint.config.ts, packages/typescript/ai-fal/openapi-ts.config.ts
New package manifest with dependencies (@fal-ai/client, Vitest, Vite) and build scripts; TypeScript, Vite, ESLint, and OpenAPI-to-TypeScript config files for the ai-fal package.
Git & Project Configuration
.gitignore, .claude/settings.local.json
Updated .gitignore with .claude/settings.local.json, fal.models.json, and fal-models-comparison.csv; deleted local Claude settings file with Bash allow-list.
Core Adapters
packages/typescript/ai-fal/src/adapters/image.ts, packages/typescript/ai-fal/src/adapters/video.ts
Implements FalImageAdapter and FalVideoAdapter extending BaseImageAdapter and BaseVideoAdapter respectively, with factory functions (createFalImage/falImage, createFalVideo/falVideo), size/aspect ratio mapping, and full integration with fal-ai client.
Utilities & Model Types
packages/typescript/ai-fal/src/utils/client.ts, packages/typescript/ai-fal/src/utils/index.ts, packages/typescript/ai-fal/src/image/image-provider-options.ts, packages/typescript/ai-fal/src/model-meta.ts
Provides FalClientConfig, getFalApiKeyFromEnv, configureFalClient, and generateId utilities; FalImageSizePreset and mapSizeToFalFormat; model type definitions (FalModel, FalModelInput/Output, provider options) derived from @fal-ai/client EndpointTypeMap.
Export Barrel
packages/typescript/ai-fal/src/index.ts
Central re-export hub consolidating adapters, utilities, model types, and image provider options for external consumption.
Test Suites
packages/typescript/ai-fal/tests/image-adapter.test.ts, packages/typescript/ai-fal/tests/video-adapter.test.ts
Comprehensive mocked tests validating image/video generation, status mapping, URL extraction, model options propagation, client configuration, and size/aspect ratio conversions.
Generation Scripts
packages/typescript/ai-fal/scripts/fetch-fal-models.ts, packages/typescript/ai-fal/scripts/compare-fal-models.ts, packages/typescript/ai-fal/scripts/generate-fal-endpoint-maps.ts, packages/typescript/ai-fal/scripts/generate-fal-openapi.ts, packages/typescript/ai-fal/scripts/generate-fal-with-heyapi.ts
Scripts for fetching Fal API models with retry logic, comparing SDK vs. API models, generating endpoint maps and OpenAPI specs from model metadata, and orchestrating Hey API code generation.
OpenAPI Specifications
packages/typescript/ai-fal/scripts/openapi/*.json
15+ OpenAPI 3.0.4 schema files covering Fal.ai model categories (3d-to-3d, audio-to-text, audio-to-video, image-to-json, json, llm, speech-to-, text-to-, video-to-*, unknown) with comprehensive input/output schemas, security schemes, and x-fal-order-properties annotations.
Root Configuration
package.json
Added two npm scripts: fetch:fal-models and compare:fal-models for model management.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant FalImageAdapter
    participant FalClient
    participant FalAPI as Fal.ai API
    
    Client->>FalImageAdapter: generateImages(options)
    FalImageAdapter->>FalImageAdapter: mapSizeToFalFormat(size)
    FalImageAdapter->>FalImageAdapter: mergeModelOptions & buildInput
    FalImageAdapter->>FalClient: fal.subscribe(model, input)
    FalClient->>FalAPI: HTTP POST /subscribe
    FalAPI-->>FalClient: {data, requestId}
    FalClient-->>FalImageAdapter: response
    FalImageAdapter->>FalImageAdapter: transformResponse(data)
    FalImageAdapter->>FalImageAdapter: parseImage(url/b64Json)
    FalImageAdapter-->>Client: ImageGenerationResult
Loading
sequenceDiagram
    participant Client
    participant FalVideoAdapter
    participant FalQueue
    participant FalAPI as Fal.ai API
    
    Client->>FalVideoAdapter: createVideoJob(options)
    FalVideoAdapter->>FalVideoAdapter: sizeToAspectRatio(size)
    FalVideoAdapter->>FalVideoAdapter: buildInput with duration & aspect_ratio
    FalVideoAdapter->>FalQueue: fal.queue.submit(model, input)
    FalQueue->>FalAPI: HTTP POST /queue/submit
    FalAPI-->>FalQueue: {job_id}
    FalQueue-->>FalVideoAdapter: jobId
    FalVideoAdapter-->>Client: VideoJobResult
    
    Note over Client,FalVideoAdapter: Polling phase
    Client->>FalVideoAdapter: getVideoStatus(jobId)
    FalVideoAdapter->>FalQueue: fal.queue.status(jobId)
    FalQueue->>FalAPI: HTTP GET /queue/status
    FalAPI-->>FalQueue: {status, queue_position}
    FalQueue-->>FalVideoAdapter: response
    FalVideoAdapter->>FalVideoAdapter: mapFalStatusToVideoStatus(status)
    FalVideoAdapter-->>Client: VideoStatusResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Suggested reviewers

  • AlemTuzlak
  • jherr

Poem

🐰 A rabbit hops through TypeScript dreams,
Fal.ai flows with image beams,
Video magic, types so tight,
New adapters dance in flight,
Tests all green—a hoppy sight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description provides a clear summary of changes, test results, and implementation notes, but does not follow the required template structure with the '🎯 Changes', '✅ Checklist', and '🚀 Release Impact' sections. Restructure the description to follow the provided template: add '🎯 Changes' section, include the '✅ Checklist' with boxes to mark completion, and add '🚀 Release Impact' section indicating if a changeset was generated.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main feature addition—introducing a fal.ai adapter for image and video generation—matching the core changes in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 82.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In @.gitignore:
- Line 57: The .gitignore entry for .claude/settings.local.json conflicts with
the fact that that same file was committed; either stop tracking the local
settings file or stop ignoring it. Fix by removing the committed
.claude/settings.local.json from the repo index (so it remains only locally) and
commit that removal while keeping the .gitignore entry, or if it should be
shared, delete the .gitignore entry and rename the file to a shared name (e.g.,
settings.json) and commit the renamed file; ensure the change is committed and
the file is no longer tracked if choosing to ignore it.

In `@packages/typescript/ai-fal/package.json`:
- Around line 43-53: The package.json currently lists "@tanstack/ai" under
"dependencies" and "peerDependencies"; remove the "@tanstack/ai" entry from the
"dependencies" object so it only appears in "peerDependencies" (keep the
existing "workspace:*" value there) to ensure the adapter declares the framework
as a peer requirement and avoids bundling a duplicate dependency. Update the
"dependencies" section to no longer include the "@tanstack/ai" key while leaving
"@fal-ai/client" and all devDependencies unchanged.

In `@packages/typescript/ai-fal/src/adapters/image.ts`:
- Around line 20-22: falImage currently ignores FalImageConfig.apiKey and always
reads the API key from env, so passing apiKey in the config has no effect;
update the falImage factory (function falImage) to prefer and use the provided
config.apiKey (FalImageConfig.apiKey) as an override before falling back to
process.env.FAL_API_KEY when instantiating the Fal client or building requests,
and ensure any FalClient creation code (references to FalClientConfig) uses that
resolved key; alternatively, if you want to disallow passing the key, remove
apiKey from FalImageConfig, but the recommended fix is to honor config.apiKey.

In `@packages/typescript/ai-fal/src/adapters/video.ts`:
- Around line 17-19: falVideo currently ignores FalVideoConfig.apiKey and always
reads the API key from env; update falVideo to prefer the provided config.apiKey
as an override (e.g., use config.apiKey if present, otherwise fall back to
process.env.FAL_API_KEY) when creating the client, and ensure FalVideoConfig
(which extends FalClientConfig) remains usable; locate the falVideo
factory/constructor and replace the env-only lookup with a conditional that uses
FalVideoConfig.apiKey before falling back to the environment variable so passing
apiKey in config takes effect.
🧹 Nitpick comments (6)
packages/typescript/ai-fal/package.json (1)

15-20: Consider adding /adapters subpath export for tree-shaking.

Based on learnings, the package should export tree-shakeable adapters with clear subpath exports (e.g., @tanstack/ai-fal/adapters). Currently, only the root export is defined. This allows consumers to import only what they need without pulling in the entire bundle.

♻️ Suggested exports structure
 "exports": {
   ".": {
     "types": "./dist/esm/index.d.ts",
     "import": "./dist/esm/index.js"
-  }
+  },
+  "./adapters": {
+    "types": "./dist/esm/adapters/index.d.ts",
+    "import": "./dist/esm/adapters/index.js"
+  }
 },
packages/typescript/ai-fal/src/image/image-provider-options.ts (1)

50-61: DRY improvement: Use a const array to derive the type.

The preset names are duplicated: once in FalImageSizePreset type (lines 5-11) and again in this validation array. Consider using a const array to derive the type, eliminating the duplication.

♻️ Suggested refactor
+const FAL_IMAGE_SIZE_PRESETS = [
+  'square_hd',
+  'square',
+  'landscape_4_3',
+  'landscape_16_9',
+  'portrait_4_3',
+  'portrait_16_9',
+] as const
+
-export type FalImageSizePreset =
-  | 'square_hd'
-  | 'square'
-  | 'landscape_4_3'
-  | 'landscape_16_9'
-  | 'portrait_4_3'
-  | 'portrait_16_9'
+export type FalImageSizePreset = (typeof FAL_IMAGE_SIZE_PRESETS)[number]

 // ... later in mapSizeToFalFormat:
-  if (
-    [
-      'square_hd',
-      'square',
-      'landscape_4_3',
-      'landscape_16_9',
-      'portrait_4_3',
-      'portrait_16_9',
-    ].includes(size)
-  ) {
+  if ((FAL_IMAGE_SIZE_PRESETS as readonly string[]).includes(size)) {
     return size as FalImageSizePreset
   }
packages/typescript/ai-fal/src/utils/client.ts (2)

42-52: Document that proxyUrl takes precedence over apiKey.

When proxyUrl is provided, apiKey is ignored entirely. This is likely intentional (the proxy handles authentication), but this behavior should be documented in the interface or function JSDoc to avoid confusion, especially since apiKey is marked as required in FalClientConfig.

📝 Suggested documentation
 export interface FalClientConfig {
+  /**
+   * API key for fal.ai authentication.
+   * Ignored when proxyUrl is provided (proxy handles auth).
+   */
   apiKey: string
+  /**
+   * Optional proxy URL. When provided, takes precedence over apiKey
+   * for client configuration.
+   */
   proxyUrl?: string
 }

54-56: generateId may produce variable-length random suffixes.

Math.random().toString(36).substring(7) can produce strings of varying lengths (typically 5-6 characters) because small random numbers result in shorter base-36 representations. For consistent ID lengths, consider using substring(2, 9) to always get 7 characters.

♻️ Suggested fix for consistent length
 export function generateId(prefix: string): string {
-  return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}`
+  return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`
 }
packages/typescript/ai-fal/src/adapters/image.ts (1)

91-124: Reuse the shared size mapping util to avoid drift.
This method duplicates logic already exported from image/image-provider-options.ts (also used in public API/tests). Consider delegating to the shared helper to keep mappings consistent.

♻️ Suggested refactor
 import {
   configureFalClient,
   getFalApiKeyFromEnv,
   generateId as utilGenerateId,
 } from '../utils'
+import { mapSizeToFalFormat } from '../image/image-provider-options'
@@
-    if (size) {
-      input.image_size = this.mapSizeToFalFormat(size)
-    }
+    if (size) {
+      input.image_size = mapSizeToFalFormat(size) ?? size
+    }
@@
-  private mapSizeToFalFormat(
-    size: string,
-  ): string | { width: number; height: number } {
-    const SIZE_TO_FAL_PRESET: Record<string, string> = {
-      '1024x1024': 'square_hd',
-      '512x512': 'square',
-      '1024x768': 'landscape_4_3',
-      '768x1024': 'portrait_4_3',
-      '1280x720': 'landscape_16_9',
-      '720x1280': 'portrait_16_9',
-      '1920x1080': 'landscape_16_9',
-      '1080x1920': 'portrait_16_9',
-    }
-
-    const preset = SIZE_TO_FAL_PRESET[size]
-    if (preset) return preset
-
-    const match = size.match(/^(\d+)x(\d+)$/)
-    if (match && match[1] && match[2]) {
-      return {
-        width: parseInt(match[1], 10),
-        height: parseInt(match[2], 10),
-      }
-    }
-
-    return size
-  }
+  // remove private mapSizeToFalFormat in favor of shared util
packages/typescript/ai-fal/tests/video-adapter.test.ts (1)

146-191: Consider adding edge case and failure status tests.

The status mapping tests cover the happy paths well. However, consider adding tests for:

  1. Edge cases for progress calculation (e.g., queue_position: 0 or queue_position: 15)
  2. Failed/error status handling if the Fal API supports failure states

Copy link
Contributor

@AlemTuzlak AlemTuzlak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly looks good, left a few comments

@nx-cloud
Copy link

nx-cloud bot commented Jan 23, 2026

View your CI Pipeline Execution ↗ for commit 0295f19

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 3m 8s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 1m 11s View ↗

☁️ Nx Cloud last updated this comment at 2026-01-23 15:32:10 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Jan 23, 2026

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@237

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@237

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@237

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@237

@tanstack/ai-fal

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-fal@237

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@237

@tanstack/ai-grok

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-grok@237

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@237

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@237

@tanstack/ai-openrouter

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openrouter@237

@tanstack/ai-preact

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-preact@237

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@237

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@237

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@237

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@237

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@237

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@237

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@237

@tanstack/preact-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/preact-ai-devtools@237

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@237

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@237

commit: 0295f19

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@packages/typescript/ai-fal/src/adapters/image.ts`:
- Around line 57-112: The private mapSizeToFalFormat method duplicates shared
logic and incorrectly returns unknown sizes instead of undefined; remove this
private mapSizeToFalFormat and import the shared mapSizeToFalFormat from
image-provider-options.ts, then in generateImages (where input.image_size is
set) only include image_size when the imported utility returns a defined value
(i.e., set ...(size && mappedSize ? { image_size: mappedSize } : {}) ), ensuring
invalid sizes are filtered out before calling fal.subscribe.

In `@packages/typescript/ai-fal/src/adapters/video.ts`:
- Around line 75-86: The payload in createVideoJob sometimes includes
aspect_ratio: undefined because sizeToAspectRatio(size) can return undefined;
change the input construction in createVideoJob to call
this.sizeToAspectRatio(size) into a local variable (e.g., const aspect =
this.sizeToAspectRatio(size)) and only include aspect_ratio in the FalModelInput
if aspect is not undefined (i.e., spread { aspect_ratio: aspect }
conditionally), so the API never receives aspect_ratio: undefined; update
references to FalModelInput<TModel> and ensure other fields (prompt, duration,
modelOptions) remain unchanged.
- Around line 105-110: The progress calculation currently uses a truthy check on
statusResponse.queue_position which treats 0 as falsy and yields undefined;
update the return in the function that builds the response (the object
containing jobId and mapFalStatusToVideoStatus(...)) to use a nullish check
(e.g., check statusResponse.queue_position != null or use ??) so that 0 is
preserved and progress computes to Math.max(0, 100 -
statusResponse.queue_position * 10).
♻️ Duplicate comments (2)
packages/typescript/ai-fal/src/adapters/video.ts (1)

168-178: config.apiKey is documented but not supported.
FalVideoConfig omits apiKey, and falVideo always reads from env. Either add apiKey?: string and prefer it, or update the docs to avoid a misleading API.

🔧 Option: honor config.apiKey
-export interface FalVideoConfig extends Omit<FalClientConfig, 'apiKey'> {}
+export interface FalVideoConfig extends Omit<FalClientConfig, 'apiKey'> {
+  apiKey?: string
+}
@@
-  const apiKey = getFalApiKeyFromEnv()
+  const apiKey = config?.apiKey ?? getFalApiKeyFromEnv()
packages/typescript/ai-fal/src/adapters/image.ts (1)

194-199: config.apiKey is documented but not supported.
FalImageConfig omits apiKey, and falImage always reads from env. Either add apiKey?: string and prefer it, or update the docs to avoid a misleading API.

🔧 Option: honor config.apiKey
-export interface FalImageConfig extends Omit<FalClientConfig, 'apiKey'> {}
+export interface FalImageConfig extends Omit<FalClientConfig, 'apiKey'> {
+  apiKey?: string
+}
@@
-  const apiKey = getFalApiKeyFromEnv()
+  const apiKey = config?.apiKey ?? getFalApiKeyFromEnv()

Comment on lines 57 to 112
async generateImages(
options: ImageGenerationOptions<FalImageProviderOptions<TModel>>,
): Promise<ImageGenerationResult> {
const { model, prompt, numberOfImages, size, modelOptions } = options

// Build the input object - spread modelOptions first, then override with standard options
const input: FalModelInput<TModel> = {
...modelOptions,
prompt,
...(size ? { image_size: this.mapSizeToFalFormat(size) } : {}),
...(numberOfImages ? { num_images: numberOfImages } : {}),
}

const result = await fal.subscribe(model as TModel, { input })

return this.transformResponse(model, result)
}

protected override generateId(): string {
return utilGenerateId(this.name)
}

/**
* Maps TanStack AI size format (WIDTHxHEIGHT) to fal.ai format.
* fal.ai accepts either preset names or { width, height } objects.
*/
private mapSizeToFalFormat(
size: string,
): string | { width: number; height: number } {
const SIZE_TO_FAL_PRESET: Record<string, string> = {
'1024x1024': 'square_hd',
'512x512': 'square',
'1024x768': 'landscape_4_3',
'768x1024': 'portrait_4_3',
'1280x720': 'landscape_16_9',
'720x1280': 'portrait_16_9',
'1920x1080': 'landscape_16_9',
'1080x1920': 'portrait_16_9',
}

// Check if it's a known preset mapping
const preset = SIZE_TO_FAL_PRESET[size]
if (preset) return preset

// Try to parse as WIDTHxHEIGHT
const match = size.match(/^(\d+)x(\d+)$/)
if (match && match[1] && match[2]) {
return {
width: parseInt(match[1], 10),
height: parseInt(match[2], 10),
}
}

// Return as-is if it's already a preset name
return size
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's explore the structure of the ai-fal package
find packages/typescript/ai-fal -type f -name "*.ts" | head -20

Repository: TanStack/ai

Length of output: 166


🏁 Script executed:

rg "mapSizeToFalFormat" packages/typescript/ai-fal/

Repository: TanStack/ai

Length of output: 2042


🏁 Script executed:

ls -la packages/typescript/ai-fal/src/image/ 2>/dev/null || echo "Directory does not exist"

Repository: TanStack/ai

Length of output: 240


🏁 Script executed:

cat -n packages/typescript/ai-fal/src/image/image-provider-options.ts

Repository: TanStack/ai

Length of output: 1914


🏁 Script executed:

cat -n packages/typescript/ai-fal/src/adapters/image.ts | sed -n '80,115p'

Repository: TanStack/ai

Length of output: 1353


🏁 Script executed:

grep -A 20 "describe('mapSizeToFalFormat'" packages/typescript/ai-fal/tests/image-adapter.test.ts

Repository: TanStack/ai

Length of output: 932


🏁 Script executed:

# Check if mapSizeToFalFormat is exported from index.ts
grep -A 2 "mapSizeToFalFormat" packages/typescript/ai-fal/src/index.ts

Repository: TanStack/ai

Length of output: 143


🏁 Script executed:

# Verify the current usage context in adapters/image.ts
cat -n packages/typescript/ai-fal/src/adapters/image.ts | sed -n '57,75p'

Repository: TanStack/ai

Length of output: 877


Replace the private mapSizeToFalFormat() method with the shared utility to ensure consistent validation.

The codebase has a shared mapSizeToFalFormat() exported from image-provider-options.ts that properly validates sizes and returns undefined for invalid inputs. The private implementation duplicates this logic but differs in a critical way: it passes unknown sizes through as-is instead of returning undefined, which can forward invalid values to fal.ai. Import and use the shared utility instead, setting image_size only when a valid value is returned.

🔧 Suggested refactor
-import type { OutputType, Result } from '@fal-ai/client'
+import type { OutputType, Result } from '@fal-ai/client'
+import { mapSizeToFalFormat } from '../image/image-provider-options'
@@
-    const input: FalModelInput<TModel> = {
-      ...modelOptions,
-      prompt,
-      ...(size ? { image_size: this.mapSizeToFalFormat(size) } : {}),
-      ...(numberOfImages ? { num_images: numberOfImages } : {}),
-    }
+    const imageSize = size ? mapSizeToFalFormat(size) : undefined
+    const input: FalModelInput<TModel> = {
+      ...modelOptions,
+      prompt,
+      ...(imageSize ? { image_size: imageSize } : {}),
+      ...(numberOfImages ? { num_images: numberOfImages } : {}),
+    }
@@
-  private mapSizeToFalFormat(
-    size: string,
-  ): string | { width: number; height: number } {
-    const SIZE_TO_FAL_PRESET: Record<string, string> = {
-      '1024x1024': 'square_hd',
-      '512x512': 'square',
-      '1024x768': 'landscape_4_3',
-      '768x1024': 'portrait_4_3',
-      '1280x720': 'landscape_16_9',
-      '720x1280': 'portrait_16_9',
-      '1920x1080': 'landscape_16_9',
-      '1080x1920': 'portrait_16_9',
-    }
-
-    const preset = SIZE_TO_FAL_PRESET[size]
-    if (preset) return preset
-
-    const match = size.match(/^(\d+)x(\d+)$/)
-    if (match && match[1] && match[2]) {
-      return {
-        width: parseInt(match[1], 10),
-        height: parseInt(match[2], 10),
-      }
-    }
-
-    return size
-  }
🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/src/adapters/image.ts` around lines 57 - 112, The
private mapSizeToFalFormat method duplicates shared logic and incorrectly
returns unknown sizes instead of undefined; remove this private
mapSizeToFalFormat and import the shared mapSizeToFalFormat from
image-provider-options.ts, then in generateImages (where input.image_size is
set) only include image_size when the imported utility returns a defined value
(i.e., set ...(size && mappedSize ? { image_size: mappedSize } : {}) ), ensuring
invalid sizes are filtered out before calling fal.subscribe.

Comment on lines 75 to 86
async createVideoJob(
options: VideoGenerationOptions<FalVideoProviderOptions<TModel>>,
): Promise<VideoJobResult> {
const { model, prompt, size, duration, modelOptions } = options

// Build the input object for fal.ai
const input: FalModelInput<TModel> = {
...modelOptions,
prompt,
...(duration ? { duration } : {}),
...(size ? { aspect_ratio: this.sizeToAspectRatio(size) } : {}),
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the video.ts file to understand the context better
head -100 packages/typescript/ai-fal/src/adapters/video.ts | cat -n

Repository: TanStack/ai

Length of output: 3355


🏁 Script executed:

# Search for the sizeToAspectRatio method implementation
rg "sizeToAspectRatio" packages/typescript/ai-fal/src/adapters/video.ts -A 10 -B 2

Repository: TanStack/ai

Length of output: 838


🏁 Script executed:

# Check the FalModelInput type definition
rg "FalModelInput" packages/typescript/ai-fal/src/ -A 5 -B 2

Repository: TanStack/ai

Length of output: 4752


🏁 Script executed:

# Look for similar patterns with conditional spreads in the codebase
rg "\.\.\.\(" packages/typescript/ai-fal/src/ -B 2 -A 1 | head -50

Repository: TanStack/ai

Length of output: 880


Avoid sending aspect_ratio: undefined to the fal.ai API.

When size is provided but not in WIDTHxHEIGHT format, sizeToAspectRatio() returns undefined, yet the spread operator still includes aspect_ratio: undefined in the payload, which the API may reject.

🔧 Suggested change
-    const input: FalModelInput<TModel> = {
-      ...modelOptions,
-      prompt,
-      ...(duration ? { duration } : {}),
-      ...(size ? { aspect_ratio: this.sizeToAspectRatio(size) } : {}),
-    }
+    const aspectRatio = size ? this.sizeToAspectRatio(size) : undefined
+    const input: FalModelInput<TModel> = {
+      ...modelOptions,
+      prompt,
+      ...(duration ? { duration } : {}),
+      ...(aspectRatio ? { aspect_ratio: aspectRatio } : {}),
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async createVideoJob(
options: VideoGenerationOptions<FalVideoProviderOptions<TModel>>,
): Promise<VideoJobResult> {
const { model, prompt, size, duration, modelOptions } = options
// Build the input object for fal.ai
const input: FalModelInput<TModel> = {
...modelOptions,
prompt,
...(duration ? { duration } : {}),
...(size ? { aspect_ratio: this.sizeToAspectRatio(size) } : {}),
}
async createVideoJob(
options: VideoGenerationOptions<FalVideoProviderOptions<TModel>>,
): Promise<VideoJobResult> {
const { model, prompt, size, duration, modelOptions } = options
// Build the input object for fal.ai
const aspectRatio = size ? this.sizeToAspectRatio(size) : undefined
const input: FalModelInput<TModel> = {
...modelOptions,
prompt,
...(duration ? { duration } : {}),
...(aspectRatio ? { aspect_ratio: aspectRatio } : {}),
}
🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/src/adapters/video.ts` around lines 75 - 86, The
payload in createVideoJob sometimes includes aspect_ratio: undefined because
sizeToAspectRatio(size) can return undefined; change the input construction in
createVideoJob to call this.sizeToAspectRatio(size) into a local variable (e.g.,
const aspect = this.sizeToAspectRatio(size)) and only include aspect_ratio in
the FalModelInput if aspect is not undefined (i.e., spread { aspect_ratio:
aspect } conditionally), so the API never receives aspect_ratio: undefined;
update references to FalModelInput<TModel> and ensure other fields (prompt,
duration, modelOptions) remain unchanged.

Comment on lines +105 to +110
return {
jobId,
status: mapFalStatusToVideoStatus(statusResponse.status),
progress: statusResponse.queue_position
? Math.max(0, 100 - statusResponse.queue_position * 10)
: undefined,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n packages/typescript/ai-fal/src/adapters/video.ts | sed -n '100,115p'

Repository: TanStack/ai

Length of output: 653


🏁 Script executed:

rg "FalStatusResponse|queue_position" packages/typescript/ai-fal/src/ -A 3 -B 1

Repository: TanStack/ai

Length of output: 1339


Use nullish check instead of truthy check for queue_position.
When queue_position = 0, the current truthy check treats it as falsy, causing progress to become undefined. Since 0 is a valid value that should calculate to progress = 100, use a nullish check to preserve it.

🔧 Suggested change
-      progress: statusResponse.queue_position
-        ? Math.max(0, 100 - statusResponse.queue_position * 10)
-        : undefined,
+      progress:
+        statusResponse.queue_position != null
+          ? Math.max(0, 100 - statusResponse.queue_position * 10)
+          : undefined,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return {
jobId,
status: mapFalStatusToVideoStatus(statusResponse.status),
progress: statusResponse.queue_position
? Math.max(0, 100 - statusResponse.queue_position * 10)
: undefined,
return {
jobId,
status: mapFalStatusToVideoStatus(statusResponse.status),
progress:
statusResponse.queue_position != null
? Math.max(0, 100 - statusResponse.queue_position * 10)
: undefined,
🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/src/adapters/video.ts` around lines 105 - 110, The
progress calculation currently uses a truthy check on
statusResponse.queue_position which treats 0 as falsy and yields undefined;
update the return in the function that builds the response (the object
containing jobId and mapFalStatusToVideoStatus(...)) to use a nullish check
(e.g., check statusResponse.queue_position != null or use ??) so that 0 is
preserved and progress computes to Math.max(0, 100 -
statusResponse.queue_position * 10).

@AlemTuzlak
Copy link
Contributor

@tombeckenham would you mind confirming that my PR didn't break anything and image/video gen still works, would really appreciate some sort of small demo app in examples repo to be able to see and test it!

@tombeckenham
Copy link
Author

I was thinking that. I've got an idea for a demo.

@jherr
Copy link
Contributor

jherr commented Jan 24, 2026

There is a testing panel that tests image and video APIs (really all the APIs)

@tombeckenham
Copy link
Author

I'm working on this. I've discovered this afternoon that the fal client is out of sync with what the fal api supports. I'll need a bit more time on this

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@scripts/compare-fal-models.ts`:
- Around line 242-244: Change the array type annotation in generateCSV to comply
with the lint rule by replacing the short form string[] with the generic form
Array<string>; specifically update the declaration of the lines variable (and
any other occurrences inside generateCSV using string[] types) to use
Array<string> so the function signature and internal typings remain consistent
with the project's ESLint configuration.

In `@scripts/fetch-fal-models.ts`:
- Around line 124-126: The inline comment above the sleep call is misleading —
it says "5 seconds" while the code calls sleep(2000) (2 seconds); update the
comment to match the actual delay or, if the intended delay is 5s, change the
argument to sleep(5000). Locate the block that checks cursor and calls sleep in
scripts/fetch-fal-models.ts (the cursor check and sleep(...) call) and make the
comment and the sleep() value consistent.
- Around line 1-6: The file header comment incorrectly states the script saves a
"TypeScript file" while scripts/fetch-fal-models.ts actually writes JSON output;
update the top comment block to say the script downloads models from the Fal API
and saves them as a generated JSON file (or "JSON output") so the description
matches the actual behavior, and adjust any phrasing referencing "TypeScript
file" to "JSON file" in that header.
🧹 Nitpick comments (1)
scripts/compare-fal-models.ts (1)

15-16: Use dynamic module resolution instead of hard-coded node_modules path for EndpointTypeMap.

The current path at line 81 assumes a specific workspace layout (packages/typescript/ai-fal/node_modules/@fal-ai/client/src/types/endpoints.d.ts) and is fragile across different pnpm configurations. Since @fal-ai/client is published with src/types/endpoints.d.ts available via typesVersions, resolve it dynamically:

Suggested approach
-import { join } from 'node:path'
+import { dirname, join } from 'node:path'
+import { createRequire } from 'node:module'
@@
-    const typesPath = join(
-      process.cwd(),
-      'packages/typescript/ai-fal/node_modules/@fal-ai/client/src/types/endpoints.d.ts',
-    )
+    const require = createRequire(import.meta.url)
+    const pkgJsonPath = require.resolve('@fal-ai/client/package.json')
+    const typesPath = join(
+      dirname(pkgJsonPath),
+      'src/types/endpoints.d.ts',
+    )

Also applies to: 77-83, 87-88

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@scripts/fetch-fal-models.ts`:
- Around line 107-110: The URL construction inside the do loop building the Fal
models endpoint uses the raw cursor value which can contain reserved characters
and break the request; update the ternary that builds url (the cursor ?
`https://api.fal.ai/v1/models?cursor=${cursor}&expand=openapi-3.0` : ...) to
encode the cursor (use encodeURIComponent on the cursor variable) so the query
param is safely URL-encoded when passed to the API; ensure any other places that
append cursor to URLs (in this file) follow the same pattern.
🧹 Nitpick comments (1)
scripts/compare-fal-models.ts (1)

15-83: Replace hard-coded node_modules path with Node's module resolver.

The fixed path to @fal-ai/client is fragile across pnpm layouts and upstream package structure changes. Use require.resolve() to let Node's resolver find the package.

♻️ Proposed refactor
import { readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'
+import { createRequire } from 'node:module'

function getEndpointTypeMapKeys(): Set<string> {
  try {
+    const require = createRequire(import.meta.url)
-    const typesPath = join(
-      process.cwd(),
-      'packages/typescript/ai-fal/node_modules/@fal-ai/client/src/types/endpoints.d.ts',
-    )
+    const typesPath = require.resolve('@fal-ai/client/src/types/endpoints.d.ts')

Comment on lines +107 to +110
do {
const url = cursor
? `https://api.fal.ai/v1/models?cursor=${cursor}&expand=openapi-3.0`
: 'https://api.fal.ai/v1/models?expand=openapi-3.0'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Encode the pagination cursor to avoid malformed URLs.

If the cursor includes reserved characters (e.g., +, /, =), the request can fail or be misrouted. A quick encode avoids that risk.

🔧 Proposed fix
-    const url = cursor
-      ? `https://api.fal.ai/v1/models?cursor=${cursor}&expand=openapi-3.0`
+    const url = cursor
+      ? `https://api.fal.ai/v1/models?cursor=${encodeURIComponent(cursor)}&expand=openapi-3.0`
       : 'https://api.fal.ai/v1/models?expand=openapi-3.0'
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
do {
const url = cursor
? `https://api.fal.ai/v1/models?cursor=${cursor}&expand=openapi-3.0`
: 'https://api.fal.ai/v1/models?expand=openapi-3.0'
do {
const url = cursor
? `https://api.fal.ai/v1/models?cursor=${encodeURIComponent(cursor)}&expand=openapi-3.0`
: 'https://api.fal.ai/v1/models?expand=openapi-3.0'
🤖 Prompt for AI Agents
In `@scripts/fetch-fal-models.ts` around lines 107 - 110, The URL construction
inside the do loop building the Fal models endpoint uses the raw cursor value
which can contain reserved characters and break the request; update the ternary
that builds url (the cursor ?
`https://api.fal.ai/v1/models?cursor=${cursor}&expand=openapi-3.0` : ...) to
encode the cursor (use encodeURIComponent on the cursor variable) so the query
param is safely URL-encoded when passed to the API; ensure any other places that
append cursor to URLs (in this file) follow the same pattern.

- Add OpenAPI TypeScript generation for fal.ai models
- Implement tree-shakeable image and video adapters
- Generate type-safe schemas for multiple model categories (text-to-image, image-to-video, etc.)
- Add scripts to sync OpenAPI schemas from fal.ai API
- Configure ESLint and TypeScript for generated code
- Move fal model sync scripts to package-specific directory
- Update model metadata with comprehensive fal.ai model support

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link
Author

@tombeckenham tombeckenham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Half way through this. We need runtime schemas of each endpoint to map size properly. The openapi endpoints are public. I'm planning to use fal client to make the calls, but generate our own types and schemas from their openapi endpoints using heyapi

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

🤖 Fix all issues with AI agents
In `@packages/typescript/ai-fal/scripts/compare-fal-models.ts`:
- Around line 50-83: The script uses process.cwd() in loadFalModels() and
getEndpointTypeMapKeys(), causing inconsistent resolution depending on where the
script is invoked; update both functions to build their paths relative to the
script/package directory (e.g., use __dirname or import.meta.url based
resolution) instead of process.cwd() so scripts/fal.models.json and the `@fal-ai`
client types file are located reliably; modify loadFalModels() and
getEndpointTypeMapKeys() to compute modelsPath and typesPath from the script
file’s directory and keep the same error handling and JSON parsing logic.

In `@packages/typescript/ai-fal/scripts/generate-fal-endpoint-maps.ts`:
- Around line 264-268: The loop building indexLines contains a redundant ternary
(`${isLast ? '' : ''}`) that always yields an empty string; update the push in
the for loop that references categoryNames, isLast, toPascalCase and indexLines
to remove the ternary and simply push the string `  |
${toPascalCase(category!)}Model` (i.e., drop the `${isLast ? '' : ''}` portion).
- Around line 69-78: The match logic asymmetrically normalizes only targetName
(producing normalizedTarget) but not each availableTypes entry (typeName),
causing misses for names like "Foo_Bar"; update the loop so you apply the same
normalization to typeName (e.g., remove underscores and lowercase) before
comparing to normalizedTarget—use the existing variables normalizedTarget and
typeName in the function that builds endpoint maps to perform the symmetric
normalization and then return the original typeName when equal.
- Around line 245-249: The generated code in generate-fal-endpoint-maps.ts is
creating a self-referential import by pushing imports like `import type { XModel
} from './index'` into indexLines; change the generation so each model type is
imported from its own source file instead of './index' — use the
categoryNames/toPascalCase mapping to produce lines like `import type {
${toPascalCase(category)}Model } from './${category}'` (or the actual relative
file path you use for each model) rather than aggregating from './index',
updating the loop that builds indexLines to emit per-category import statements.

In `@packages/typescript/ai-fal/scripts/openapi/audio-to-text.json`:
- Around line 1-9: The OpenAPI document is missing the required top-level
"paths" object and a global "security" declaration; add an empty "paths": {} and
a root-level "security" array (for example [{"api_key": []}]) to reflect the API
key requirement, ensuring it aligns with the existing components.securitySchemes
entry so validators and codegen accept the spec; update the document near the
existing "components" and "info" blocks to include these two entries.
- Line 85: NemotronAsrStreamOutput is currently an empty schema which produces
incorrect types; change it to use the same structure as NemotronAsrOutput by
replacing the empty object with either a $ref to the NemotronAsrOutput schema
(e.g. "$ref": "#/components/schemas/NemotronAsrOutput") or by copying the exact
properties from NemotronAsrOutput into NemotronAsrStreamOutput so streaming
responses include the same fields (including the partial flag).

In `@packages/typescript/ai-fal/scripts/openapi/audio-to-video.json`:
- Around line 953-961: The OpenAPI schema for the property "audio_url" has an
examples entry that is an object but the property type is "string"; update the
example under "audio_url" so it is a plain string (e.g.,
"https://argildotai.s3.us-east-1.amazonaws.com/fal-resource/example_fal.mp3") to
match the declared type and avoid misleading consumers and docs.
- Around line 1846-1853: Add a top-level OpenAPI security requirement so the
apiKeyAuth scheme is enforced globally: update the document to include a
top-level "security" array referencing the "apiKeyAuth" scheme (e.g. "security":
[{"apiKeyAuth": []}]) so that the existing "securitySchemes" entry (apiKeyAuth)
is applied to all operations.
- Around line 52-145: The schema for ElevenlabsDubbingInput currently allows
both audio_url and video_url to be missing/null; add a validation rule to
require at least one be present by adding an anyOf (or oneOf) at the
ElevenlabsDubbingInput object level such as anyOf: [ { "required": ["audio_url"]
}, { "required": ["video_url"] } ] and ensure the properties audio_url and
video_url are treated as strings (remove the nullable union or ensure non-null
when required) so generated typings and validators enforce that at least one URL
is provided.

In `@packages/typescript/ai-fal/scripts/openapi/image-to-json.json`:
- Around line 1-9: The OpenAPI document is missing the required top-level paths
object; update the JSON root in image-to-json.json by inserting an empty
"paths": {} field (e.g., place "paths": {} immediately before the existing
"components" object) so the document complies with OpenAPI 3.0.4 validators and
code generators.

In `@packages/typescript/ai-fal/scripts/openapi/speech-to-speech.json`:
- Around line 133-176: Update the Audio schema examples to use audio MIME types
and file extensions: change the "content_type" example from "image/png" to an
audio type like "audio/wav" (or "audio/mpeg"), and change the "file_name"
example from a .png name to an appropriate audio filename (e.g., "output.wav" or
"output.mp3") in the Audio object definition (properties: content_type,
file_name, file_size, file_data) so examples reflect speech-to-speech outputs.
- Around line 290-295: The OpenAPI spec defines
components.securitySchemes.apiKeyAuth but never applies it globally; add a
top-level security declaration (e.g., "security": [{"apiKeyAuth": []}]) to the
root document so that the apiKeyAuth scheme is enforced for all operations
unless overridden; update the root-level object in the speech-to-speech.json to
include this security field referencing components.securitySchemes.apiKeyAuth
(or add operation-level security where specific endpoints differ).
- Around line 1-8: The OpenAPI document in speech-to-speech.json is missing the
required top-level "paths" object (openapi: "3.0.4"); add a top-level "paths":
{} entry right under the existing "info" object to make the schema valid for
validators and generators—ensure the key is an empty object for now so future
route definitions (if any) can be added without breaking generation.

In `@packages/typescript/ai-fal/scripts/openapi/speech-to-text.json`:
- Around line 585-613: SpeechToTextTurboStreamOutput and
SpeechToTextStreamOutput are empty object schemas causing generated TypeScript
to be unknown/z.unknown(); update these two schema definitions to either (a)
explicitly document that stream outputs are unstructured by adding a descriptive
"description" field (e.g., "Unstructured streaming output; messages vary and are
not typed") so generators produce a clear note, or (b) mirror the structured
form used by non-streaming types (like SpeechToTextTurboOutput) by adding the
expected properties (e.g., "partial", "output", with appropriate types and
"partial" marked optional) so the generated types are concrete; use the schema
names SpeechToTextTurboStreamOutput and SpeechToTextStreamOutput to locate and
amend the definitions.

In `@packages/typescript/ai-fal/scripts/openapi/text-to-3d.json`:
- Around line 1067-1074: The OpenAPI spec defines the security scheme
"apiKeyAuth" under "securitySchemes" but doesn't apply it globally; add a
top-level "security" array at the root of the document referencing "apiKeyAuth"
(e.g., {"apiKeyAuth": []}) so all operations require the apiKey by default;
ensure you add this alongside the existing "components" object (not inside it)
so generated clients and docs pick up the global requirement.

In `@packages/typescript/ai-fal/scripts/openapi/text-to-json.json`:
- Line 119: The listed schemas (FiboEditEditStructuredInstructionOutput,
FiboLiteGenerateStructuredPromptOutput,
FiboLiteGenerateStructuredPromptLiteOutput, FiboGenerateStructuredPromptOutput)
are defined as empty objects which generate unknown TS types; replace each {}
with a proper JSON Schema (e.g.,
{"type":"object","properties":{...},"required":[...]}) or a $ref to an existing
response model so the OpenAPI TypeScript generator can emit concrete interfaces.
Locate the object definitions by the schema names in the JSON file and either
(1) define the expected properties and types for each schema based on the API
contract, or (2) point them to an existing schema via "$ref":
"#/components/schemas/ExistingModel" or at minimum declare
{"type":"object","additionalProperties":true} to avoid unknown. Ensure
consistency across all OpenAPI files that contain these schema names.

In `@packages/typescript/ai-fal/scripts/openapi/text-to-text.json`:
- Around line 1-128: The OpenAPI output defines
components.securitySchemes.apiKeyAuth but never applies it globally; update the
generator in packages/typescript/ai-fal/scripts/generate-fal-openapi.ts where
openApiSpec is built to add a top-level "security" array that references the
apiKeyAuth scheme (e.g., security: [{ apiKeyAuth: [] }]), then re-run the
generator to regenerate all files under scripts/openapi/ so every generated spec
(including text-to-text.json) includes the global Authorization requirement.
♻️ Duplicate comments (2)
packages/typescript/ai-fal/scripts/fetch-fal-models.ts (1)

94-131: Encode the pagination cursor to avoid malformed URLs.
Unencoded cursor values can include reserved characters and break requests.

🔧 Suggested fix
-    const url = cursor
-      ? `https://api.fal.ai/v1/models?cursor=${cursor}&expand=openapi-3.0`
+    const url = cursor
+      ? `https://api.fal.ai/v1/models?cursor=${encodeURIComponent(cursor)}&expand=openapi-3.0`
       : 'https://api.fal.ai/v1/models?expand=openapi-3.0'
packages/typescript/ai-fal/src/adapters/image.ts (1)

83-112: Duplicate mapSizeToFalFormat implementation persists.

A past review flagged that this private method duplicates logic from the shared mapSizeToFalFormat in image-provider-options.ts. The shared utility properly returns undefined for invalid sizes, while this version passes unknown sizes through as-is, potentially forwarding invalid values to fal.ai.

🧹 Nitpick comments (17)
packages/typescript/ai-fal/scripts/openapi/3d-to-3d.json (3)

1241-1248: Security scheme defined but not applied globally.

The apiKeyAuth security scheme is defined but there's no global security field that enforces it. Consumers of this spec (e.g., generated clients) may not automatically include authentication headers.

Consider adding a global security requirement at the root level:

Proposed fix
   },
   "securitySchemes": {
     "apiKeyAuth": {
       "type": "apiKey",
       "in": "header",
       "name": "Authorization"
     }
   }
- }
+ },
+ "security": [
+   {
+     "apiKeyAuth": []
+   }
+ ]
 }

11-51: Unused schemas: File and QueueStatus.

These common schemas are defined but not referenced anywhere in this spec. Each model already has its own namespaced File schema (e.g., FalAiUltrashape_File). If these are intentionally generated for consistency across all specs, this is fine to keep. Otherwise, consider removing them to reduce schema bloat.


362-368: Consider adding array size constraints for fixed-length vectors.

The translation array represents a 3D vector [tx, ty, tz] but lacks minItems/maxItems constraints. Similarly, iou_scores (lines 1115-1121) should have exactly 3 items based on the "three masks" description. Adding constraints improves schema validation:

"translation": {
  "title": "Translation",
  "type": "array",
  "description": "Translation [tx, ty, tz]",
  "items": { "type": "number" },
  "minItems": 3,
  "maxItems": 3
}
packages/typescript/ai-fal/scripts/openapi/unknown.json (3)

1-8: Clarify the "unknown" category naming.

The file is named unknown.json and titled "Fal.ai unknown Models," but the schemas define RouterAudioInput and RouterAudioOutput for audio processing. This naming may cause confusion for maintainers trying to locate audio-related schemas.

Consider renaming to something more descriptive (e.g., audio.json or router-audio.json) if this is the intended category, or add a comment/description clarifying why these audio schemas fall under "unknown."


198-205: Global security field is absent (CKV_OPENAPI_4).

The static analysis tool flagged that while apiKeyAuth is defined as a security scheme, there's no global security field applying it. However, since this OpenAPI spec appears to be used for type generation rather than as a server implementation, this is likely acceptable for your use case.

If you do want generated clients to enforce authentication by default, add a global security block:

Optional fix to add global security
     }
-  }
+  },
+  "security": [
+    {
+      "apiKeyAuth": []
+    }
+  ]
 }

127-163: usage is optional in RouterAudioOutput despite containing cost tracking data.

The usage field is not marked as required, though it contains important metrics (prompt_tokens, total_tokens, completion_tokens, cost). This may cause gaps in cost tracking and monitoring if responses omit the field. Additionally, the schema lists usage in x-fal-order-properties, suggesting it's expected in responses, creating an inconsistency with the required array.

If fal.ai guarantees usage information in all responses, mark it as required. Otherwise, document that consumers must handle cases where usage is undefined.

packages/typescript/ai-fal/scripts/openapi/text-to-3d.json (2)

803-821: Consider adding maxItems constraint to the texture_urls array.

The array schema lacks a maxItems property, which could allow unbounded responses. If fal.ai's API has a known practical limit on texture outputs, adding a constraint improves client-side validation and documentation:

           "title": "Texture Urls",
           "type": "array",
           "description": "Array of texture file objects",
+          "maxItems": 100,
           "items": {
             "$ref": "#/components/schemas/TextureFiles"
           }

If the generation script sources this from fal.ai's upstream spec, this may need to be addressed in the sync script instead.


168-218: Schema duplication across model-specific File types.

Four nearly identical File schemas exist (FalAiHunyuanMotionFast_File, FalAiHunyuanMotion_File, FalAiHunyuan3dV3TextTo3d_File, FalAiMeshyV6PreviewTextTo3d_File), all with the same structure as the base File schema at lines 11-31.

If this is intentional for TypeScript type isolation per model (to avoid cross-model type pollution), this is fine. Otherwise, consider consolidating to use $ref: "#/components/schemas/File" in the generation script to reduce spec size and simplify maintenance.

Also applies to: 335-385, 574-624, 894-944

packages/typescript/ai-fal/scripts/openapi/text-to-json.json (1)

1790-1797: Consider adding a global security field to enforce authentication by default.

The schema defines apiKeyAuth in securitySchemes but lacks a root-level security field. Without it, the OpenAPI spec doesn't enforce authentication at the specification level—consumers must manually apply security to each operation.

Per static analysis (CKV_OPENAPI_4), adding a global security requirement improves the security posture:

{
  "openapi": "3.0.4",
  "info": { ... },
  "security": [
    { "apiKeyAuth": [] }
  ],
  "components": { ... }
}

Since this file appears to be auto-generated, you may need to update the generation script to include this field.

packages/typescript/ai-fal/scripts/generate-fal-with-heyapi.ts (1)

28-39: Handle spawn signal termination for robustness.

The script checks result.status === 0 but ignores result.signal, which could indicate the process was killed (e.g., OOM, SIGTERM). Consider also checking for signal termination.

♻️ Suggested improvement
-  if (result.status === 0) {
+  if (result.status === 0 && !result.signal) {
     console.log('\n✓ Generation complete!')
   } else {
-    console.error('\n✗ Generation failed')
+    console.error(
+      `\n✗ Generation failed${result.signal ? ` (signal: ${result.signal})` : ''}`,
+    )
     process.exit(1)
   }
packages/typescript/ai-fal/scripts/openapi/json.json (1)

483-490: Waveform array lacks maxItems constraint.

Similar to the categories array in llm.json, the waveform array schema has no upper bound. Since waveform data can be large (based on audio duration × points_per_second), consider whether the upstream spec or generator should enforce a limit.

packages/typescript/ai-fal/scripts/generate-fal-openapi.ts (3)

138-159: Recursive ref extraction lacks depth protection.

extractRefNames recursively traverses schemas without a depth limit. Deeply nested or circular $ref structures (even if invalid) could cause stack overflow. Consider adding a depth guard or visited-path tracking.

♻️ Suggested improvement
   function extractRefNames(
     schema: any,
     refs: Set<string> = new Set(),
+    depth: number = 0,
   ): Set<string> {
-    if (!schema || typeof schema !== 'object') return refs
+    if (!schema || typeof schema !== 'object' || depth > 50) return refs

     if (schema.$ref && typeof schema.$ref === 'string') {
       const match = schema.$ref.match(/#\/components\/schemas\/(.+)/)
       if (match) {
         refs.add(match[1])
       }
     }

     // Recursively check all properties
     for (const value of Object.values(schema)) {
       if (typeof value === 'object' && value !== null) {
-        extractRefNames(value, refs)
+        extractRefNames(value, refs, depth + 1)
       }
     }

     return refs
   }

295-305: Placeholder schemas for missing refs may mask upstream issues.

Creating placeholder schemas with additionalProperties: true allows generation to proceed but could hide real problems in the source OpenAPI specs. Consider logging these more prominently or aggregating a summary at the end.

♻️ Consider aggregating missing refs
+  const allMissingRefs = new Map<string, Set<string>>() // category -> missing refs

   // ... inside the model processing loop ...
       for (const missingRef of missingRefs) {
         const finalName = nameMap.get(missingRef)!
         if (!categorySchemas[finalName]) {
+          if (!allMissingRefs.has(category)) allMissingRefs.set(category, new Set())
+          allMissingRefs.get(category)!.add(missingRef)
           categorySchemas[finalName] = {
             // ...
           }
         }
       }

+  // At the end of main():
+  if (allMissingRefs.size > 0) {
+    console.warn('\n⚠️ Missing schema references found:')
+    for (const [cat, refs] of allMissingRefs) {
+      console.warn(`  ${cat}: ${Array.from(refs).join(', ')}`)
+    }
+  }

343-348: Consider adding error handling for file writes.

writeFileSync operations can fail (disk full, permissions). For a build script, a try-catch with a clear error message would improve debugging.

packages/typescript/ai-fal/scripts/openapi/llm.json (1)

232-256: The categories array is unbounded because the upstream specification lacks maxItems.

The categories array in llm.json (inherited from fal.models.json) has no maxItems constraint. While this is auto-generated, unbounded arrays are a validation risk. The generator script does not add array constraints—it passes schemas through unchanged from the upstream fal.ai spec.

If you want to add protection here, you could modify the generator to inject reasonable maxItems constraints for arrays, but this would enforce limits not present in the upstream spec. Alternatively, report this to the upstream fal.ai source or document the intentional design choice.

packages/typescript/ai-fal/package.json (1)

15-20: Consider adding subpath exports for adapters to enable tree-shaking.

Based on learnings, the package should export tree-shakeable adapters with clear subpath exports (e.g., @tanstack/ai-fal/adapters) to minimize bundle size. The current configuration only exposes the root path.

♻️ Suggested subpath exports
   "exports": {
     ".": {
       "types": "./dist/esm/index.d.ts",
       "import": "./dist/esm/index.js"
+    },
+    "./adapters": {
+      "types": "./dist/esm/adapters/index.d.ts",
+      "import": "./dist/esm/adapters/index.js"
     }
   },
packages/typescript/ai-fal/scripts/openapi/video-to-audio.json (1)

183-233: Note: Duplicate File schema definitions are present.

FalAiSamAudioVisualSeparate_File (lines 183-233) and FalAiKlingVideoVideoToAudio_File (lines 485-535) have identical structures. Since this is auto-generated from the fal.ai API, this duplication reflects the upstream schema structure and is acceptable. The generation scripts could potentially consolidate these in the future if deduplication is desired.

Also applies to: 485-535

Comment on lines +50 to +83
function loadFalModels(): Array<FalApiModel> {
try {
const modelsPath = join(process.cwd(), 'scripts/fal.models.json')
const content = readFileSync(modelsPath, 'utf-8')
const data = JSON.parse(content) as {
generated_at: string
total_models: number
models: Array<FalApiModel>
}
return data.models
} catch (error) {
if (
error instanceof Error &&
(error.message.includes('no such file') ||
error.message.includes('ENOENT'))
) {
throw new Error(
'Models file not found. Run "pnpm fetch:fal-models" first to download the models.',
)
}
throw error
}
}

/**
* Parse EndpointTypeMap from the @fal-ai/client type definitions
*/
function getEndpointTypeMapKeys(): Set<string> {
try {
const typesPath = join(
process.cwd(),
'packages/typescript/ai-fal/node_modules/@fal-ai/client/src/types/endpoints.d.ts',
)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix inconsistent base path resolution (script will fail depending on CWD).

loadFalModels() assumes CWD is the package root, while getEndpointTypeMapKeys() assumes CWD is the repo root. One of these will be wrong in practice, so the script can’t reliably find both files. Resolve paths relative to the script/package directory instead.

🔧 Suggested fix
-import { join } from 'node:path'
+import { dirname, join, resolve } from 'node:path'
+import { fileURLToPath } from 'node:url'
+
+const scriptDir = dirname(fileURLToPath(import.meta.url))
+const packageRoot = resolve(scriptDir, '..')
-    const modelsPath = join(process.cwd(), 'scripts/fal.models.json')
+    const modelsPath = join(packageRoot, 'scripts/fal.models.json')
-    const typesPath = join(
-      process.cwd(),
-      'packages/typescript/ai-fal/node_modules/@fal-ai/client/src/types/endpoints.d.ts',
-    )
+    const typesPath = join(
+      packageRoot,
+      'node_modules/@fal-ai/client/src/types/endpoints.d.ts',
+    )
-  const outputPath = join(process.cwd(), filename)
+  const outputPath = join(packageRoot, filename)

Also applies to: 288-295

🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/compare-fal-models.ts` around lines 50 -
83, The script uses process.cwd() in loadFalModels() and
getEndpointTypeMapKeys(), causing inconsistent resolution depending on where the
script is invoked; update both functions to build their paths relative to the
script/package directory (e.g., use __dirname or import.meta.url based
resolution) instead of process.cwd() so scripts/fal.models.json and the `@fal-ai`
client types file are located reliably; modify loadFalModels() and
getEndpointTypeMapKeys() to compute modelsPath and typesPath from the script
file’s directory and keep the same error handling and JSON parsing logic.

Comment on lines +69 to +78
// Normalize target name (remove underscores, convert to lowercase)
const normalizedTarget = targetName.replace(/_/g, '').toLowerCase()

// Find best match by normalized comparison
for (const typeName of availableTypes) {
const normalized = typeName.toLowerCase()
if (normalized === normalizedTarget) {
return typeName
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Asymmetric normalization may cause missed matches.

The targetName has underscores removed (line 70), but typeName from availableTypes does not (line 74). If targetName is "FooBar" and availableTypes contains "Foo_Bar", the match will fail because "foobar" !== "foo_bar".

Proposed fix
   // Find best match by normalized comparison
   for (const typeName of availableTypes) {
-    const normalized = typeName.toLowerCase()
+    const normalized = typeName.replace(/_/g, '').toLowerCase()
     if (normalized === normalizedTarget) {
       return typeName
     }
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Normalize target name (remove underscores, convert to lowercase)
const normalizedTarget = targetName.replace(/_/g, '').toLowerCase()
// Find best match by normalized comparison
for (const typeName of availableTypes) {
const normalized = typeName.toLowerCase()
if (normalized === normalizedTarget) {
return typeName
}
}
// Normalize target name (remove underscores, convert to lowercase)
const normalizedTarget = targetName.replace(/_/g, '').toLowerCase()
// Find best match by normalized comparison
for (const typeName of availableTypes) {
const normalized = typeName.replace(/_/g, '').toLowerCase()
if (normalized === normalizedTarget) {
return typeName
}
}
🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/generate-fal-endpoint-maps.ts` around
lines 69 - 78, The match logic asymmetrically normalizes only targetName
(producing normalizedTarget) but not each availableTypes entry (typeName),
causing misses for names like "Foo_Bar"; update the loop so you apply the same
normalization to typeName (e.g., remove underscores and lowercase) before
comparing to normalizedTarget—use the existing variables normalizedTarget and
typeName in the function that builds endpoint maps to perform the symmetric
normalization and then return the original typeName when equal.

Comment on lines +245 to +249
indexLines.push(`import type {`)
for (const category of categoryNames) {
indexLines.push(` ${toPascalCase(category)}Model,`)
}
indexLines.push(`} from './index'`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Self-referential import in generated code.

The generated index.ts imports {CategoryName}Model types from './index', which is the file being generated itself. While TypeScript may resolve this due to the order of exports, self-imports are confusing and fragile. The types should be imported directly from their source files.

Proposed fix
-  indexLines.push(`import type {`)
-  for (const category of categoryNames) {
-    indexLines.push(`  ${toPascalCase(category)}Model,`)
-  }
-  indexLines.push(`} from './index'`)
+  for (const category of categoryNames) {
+    indexLines.push(`import type { ${toPascalCase(category)}Model } from './${category}/endpoint-map'`)
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
indexLines.push(`import type {`)
for (const category of categoryNames) {
indexLines.push(` ${toPascalCase(category)}Model,`)
}
indexLines.push(`} from './index'`)
for (const category of categoryNames) {
indexLines.push(`import type { ${toPascalCase(category)}Model } from './${category}/endpoint-map'`)
}
🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/generate-fal-endpoint-maps.ts` around
lines 245 - 249, The generated code in generate-fal-endpoint-maps.ts is creating
a self-referential import by pushing imports like `import type { XModel } from
'./index'` into indexLines; change the generation so each model type is imported
from its own source file instead of './index' — use the
categoryNames/toPascalCase mapping to produce lines like `import type {
${toPascalCase(category)}Model } from './${category}'` (or the actual relative
file path you use for each model) rather than aggregating from './index',
updating the loop that builds indexLines to emit per-category import statements.

Comment on lines +264 to +268
for (let i = 0; i < categoryNames.length; i++) {
const category = categoryNames[i]
const isLast = i === categoryNames.length - 1
indexLines.push(` | ${toPascalCase(category!)}Model${isLast ? '' : ''}`)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Redundant ternary expression.

The expression ${isLast ? '' : ''} evaluates to an empty string in both cases, making the ternary pointless.

Proposed fix
   for (let i = 0; i < categoryNames.length; i++) {
     const category = categoryNames[i]
-    const isLast = i === categoryNames.length - 1
-    indexLines.push(`  | ${toPascalCase(category!)}Model${isLast ? '' : ''}`)
+    indexLines.push(`  | ${toPascalCase(category!)}Model`)
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (let i = 0; i < categoryNames.length; i++) {
const category = categoryNames[i]
const isLast = i === categoryNames.length - 1
indexLines.push(` | ${toPascalCase(category!)}Model${isLast ? '' : ''}`)
}
for (let i = 0; i < categoryNames.length; i++) {
const category = categoryNames[i]
indexLines.push(` | ${toPascalCase(category!)}Model`)
}
🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/generate-fal-endpoint-maps.ts` around
lines 264 - 268, The loop building indexLines contains a redundant ternary
(`${isLast ? '' : ''}`) that always yields an empty string; update the push in
the for loop that references categoryNames, isLast, toPascalCase and indexLines
to remove the ternary and simply push the string `  |
${toPascalCase(category!)}Model` (i.e., drop the `${isLast ? '' : ''}` portion).

Comment on lines +1 to +9
{
"openapi": "3.0.4",
"info": {
"title": "Fal.ai audio-to-text Models",
"version": "1.0.0",
"description": "OpenAPI schemas for all fal.ai audio-to-text models (3 models total)",
"x-generated-at": "2026-01-26T05:40:51.971Z"
},
"components": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add required paths (and consider global security) to keep the OpenAPI document valid.

OpenAPI 3.0 documents require a paths object; many validators/codegen tools will reject or warn without it, and CKV_OPENAPI_4 flags missing global security even when a scheme is defined. If this file is consumed by tooling or CI validation, add an empty paths and a root security entry to reflect the API key requirement.

📌 Suggested patch
 {
   "openapi": "3.0.4",
   "info": {
     "title": "Fal.ai audio-to-text Models",
     "version": "1.0.0",
     "description": "OpenAPI schemas for all fal.ai audio-to-text models (3 models total)",
     "x-generated-at": "2026-01-26T05:40:51.971Z"
   },
+  "paths": {},
+  "security": [
+    {
+      "apiKeyAuth": []
+    }
+  ],
   "components": {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{
"openapi": "3.0.4",
"info": {
"title": "Fal.ai audio-to-text Models",
"version": "1.0.0",
"description": "OpenAPI schemas for all fal.ai audio-to-text models (3 models total)",
"x-generated-at": "2026-01-26T05:40:51.971Z"
},
"components": {
{
"openapi": "3.0.4",
"info": {
"title": "Fal.ai audio-to-text Models",
"version": "1.0.0",
"description": "OpenAPI schemas for all fal.ai audio-to-text models (3 models total)",
"x-generated-at": "2026-01-26T05:40:51.971Z"
},
"paths": {},
"security": [
{
"apiKeyAuth": []
}
],
"components": {
🧰 Tools
🪛 Checkov (3.2.334)

[high] 1-237: Ensure that the global security field has rules defined

(CKV_OPENAPI_4)

🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/openapi/audio-to-text.json` around lines 1
- 9, The OpenAPI document is missing the required top-level "paths" object and a
global "security" declaration; add an empty "paths": {} and a root-level
"security" array (for example [{"api_key": []}]) to reflect the API key
requirement, ensuring it aligns with the existing components.securitySchemes
entry so validators and codegen accept the spec; update the document near the
existing "components" and "info" blocks to include these two entries.

Comment on lines +290 to +295
"securitySchemes": {
"apiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "Authorization"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

python - <<'PY'
import json
path="packages/typescript/ai-fal/scripts/openapi/speech-to-speech.json"
with open(path) as f:
    data = json.load(f)
print("global security present:", "security" in data)
print("securitySchemes.apiKeyAuth present:", "apiKeyAuth" in data.get("components", {}).get("securitySchemes", {}))

# Check if security is applied per-operation
paths = data.get("paths", {})
has_per_operation_security = False
for path_key, path_obj in paths.items():
    for method, operation in path_obj.items():
        if isinstance(operation, dict) and "security" in operation:
            has_per_operation_security = True
            break

print("per-operation security found:", has_per_operation_security)
print("\nTotal paths:", len(paths))
if paths:
    sample_path = list(paths.keys())[0]
    print(f"Sample path: {sample_path}")
    print(f"Methods: {list(paths[sample_path].keys())}")
PY

Repository: TanStack/ai

Length of output: 141


🏁 Script executed:

# Check file size and basic structure
wc -l packages/typescript/ai-fal/scripts/openapi/speech-to-speech.json
echo "---"
# Look for specific fields
grep -c "securitySchemes" packages/typescript/ai-fal/scripts/openapi/speech-to-speech.json
grep -c '"security"' packages/typescript/ai-fal/scripts/openapi/speech-to-speech.json
echo "---"
# Check context around security-related fields
grep -n "security" packages/typescript/ai-fal/scripts/openapi/speech-to-speech.json | head -20

Repository: TanStack/ai

Length of output: 164


🏁 Script executed:

cat packages/typescript/ai-fal/scripts/openapi/speech-to-speech.json

Repository: TanStack/ai

Length of output: 8835


Apply the auth scheme globally.

components.securitySchemes.apiKeyAuth is defined but never applied. Without a top-level security field, OpenAPI tooling may omit authentication requirements from generated code.

Suggested fix
   "info": {
     "title": "Fal.ai speech-to-speech Models",
     "version": "1.0.0",
     "description": "OpenAPI schemas for all fal.ai speech-to-speech models (2 models total)",
     "x-generated-at": "2026-01-26T05:40:51.981Z"
   },
+  "security": [
+    { "apiKeyAuth": [] }
+  ],
   "components": {
🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/openapi/speech-to-speech.json` around
lines 290 - 295, The OpenAPI spec defines components.securitySchemes.apiKeyAuth
but never applies it globally; add a top-level security declaration (e.g.,
"security": [{"apiKeyAuth": []}]) to the root document so that the apiKeyAuth
scheme is enforced for all operations unless overridden; update the root-level
object in the speech-to-speech.json to include this security field referencing
components.securitySchemes.apiKeyAuth (or add operation-level security where
specific endpoints differ).

Comment on lines +585 to +613
"SpeechToTextTurboStreamOutput": {},
"SpeechToTextStreamInput": {
"title": "SpeechInput",
"type": "object",
"properties": {
"audio_url": {
"examples": [
"https://storage.googleapis.com/falserverless/canary/18e15559-ab3e-4f96-9583-be5ddde91e43.mp3"
],
"title": "Audio Path",
"type": "string",
"description": "Local filesystem path (or remote URL) to a long audio file"
},
"use_pnc": {
"title": "Use Punctuation/Capitalization (PnC)",
"type": "boolean",
"description": "Whether to use Canary's built-in punctuation & capitalization",
"default": true
}
},
"x-fal-order-properties": [
"audio_url",
"use_pnc"
],
"required": [
"audio_url"
]
},
"SpeechToTextStreamOutput": {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if other OpenAPI specs in this package have similar empty schemas
echo "=== Empty schemas in openapi directory ==="
fd -e json . packages/typescript/ai-fal/scripts/openapi --exec grep -l '": {}' {} \;

echo ""
echo "=== Count of empty schemas per file ==="
fd -e json . packages/typescript/ai-fal/scripts/openapi --exec sh -c 'count=$(grep -o '\''"[^"]*": {}'\'' "$1" | wc -l); if [ $count -gt 0 ]; then echo "$1: $count empty schemas"; fi' _ {} \;

echo ""
echo "=== Sample of empty schemas from speech-to-text.json ==="
grep -n '"[^"]*": {}' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json | head -20

Repository: TanStack/ai

Length of output: 285


🏁 Script executed:

#!/bin/bash
# Examine the full context around the empty schemas and check for patterns
echo "=== Context around lines 585-615 ==="
sed -n '580,620p' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json

echo ""
echo "=== Check for 'StreamOutput' patterns across the file ==="
grep -n 'StreamOutput' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json

echo ""
echo "=== Check for descriptions in empty schemas ==="
grep -B2 -A2 '": {}' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json

Repository: TanStack/ai

Length of output: 1833


🏁 Script executed:

#!/bin/bash
# Check if this is a generated file and look for similar patterns in other speech model outputs
echo "=== File header (first 10 lines) ==="
head -10 packages/typescript/ai-fal/scripts/openapi/speech-to-text.json

echo ""
echo "=== All Output schemas in file (both empty and non-empty) ==="
grep -n '"[^"]*Output"' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json | head -20

echo ""
echo "=== Check file size and structure ==="
wc -l packages/typescript/ai-fal/scripts/openapi/speech-to-text.json

echo ""
echo "=== Look for other Output types (non-Stream) to see pattern ==="
sed -n '1,100p' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json | grep -A5 'Output'

Repository: TanStack/ai

Length of output: 1187


🏁 Script executed:

#!/bin/bash
# Compare empty stream outputs with their non-stream counterparts
echo "=== SpeechToTextTurboOutput (line 534 - non-stream) ==="
sed -n '534,560p' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json

echo ""
echo "=== SpeechToTextOutput (line 641 - non-stream) ==="
sed -n '641,667p' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json

echo ""
echo "=== Check if StreamInput and StreamOutput are used in API responses ==="
grep -n 'StreamInput\|StreamOutput' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json

Repository: TanStack/ai

Length of output: 1961


🏁 Script executed:

#!/bin/bash
# Check how these stream outputs are used in the actual TypeScript code
echo "=== Search for usage of SpeechToTextTurboStreamOutput or SpeechToTextStreamOutput ==="
rg -i "SpeechToTextTurboStreamOutput|SpeechToTextStreamOutput" packages/typescript/ai-fal/

echo ""
echo "=== Check if there's any TypeScript type generation from this spec ==="
fd -e ts -e tsx . packages/typescript/ai-fal --exec grep -l "speech-to-text.json\|SpeechToText" {} \;

echo ""
echo "=== Look at how this OpenAPI spec is used ==="
head -50 packages/typescript/ai-fal/src/index.ts 2>/dev/null || echo "File not found"

Repository: TanStack/ai

Length of output: 4435


🏁 Script executed:

#!/bin/bash
# Check the generated types to understand how unknown is handled
echo "=== Generated types for stream outputs ==="
grep -A2 "SpeechToTextTurboStreamOutput\|SpeechToTextStreamOutput" packages/typescript/ai-fal/src/generated/speech-to-text/types.gen.ts

echo ""
echo "=== Check Zod schema generation ==="
grep -A2 "SpeechToTextTurboStreamOutput\|SpeechToTextStreamOutput" packages/typescript/ai-fal/src/generated/speech-to-text/zod.gen.ts

echo ""
echo "=== Check how streaming endpoints handle responses ==="
fd -e ts -e tsx . packages/typescript/ai-fal/src --exec grep -l "stream\|Stream" {} \; | head -5

echo ""
echo "=== Look at the non-stream output typing for comparison ==="
grep -A10 "type SpeechToTextTurboOutput" packages/typescript/ai-fal/src/generated/speech-to-text/types.gen.ts | head -15

Repository: TanStack/ai

Length of output: 1092


🏁 Script executed:

#!/bin/bash
# Check if StreamProcessor or streaming utilities are used for these endpoints
echo "=== Search for StreamProcessor usage ==="
rg -i "StreamProcessor|streaming" packages/typescript/ai-fal/src --type ts -A3 | head -40

echo ""
echo "=== Check endpoint-map to see how streaming is configured ==="
grep -B5 -A5 "SpeechToTextTurboStreamOutput" packages/typescript/ai-fal/src/generated/speech-to-text/endpoint-map.ts

echo ""
echo "=== Check if there's any comment in the OpenAPI spec about streaming ==="
grep -B5 -A5 '"SpeechToTextTurboStreamOutput"' packages/typescript/ai-fal/scripts/openapi/speech-to-text.json | head -20

echo ""
echo "=== Look for any README or documentation about streaming handling ==="
fd -e md . packages/typescript/ai-fal --exec grep -l -i "stream" {} \;

Repository: TanStack/ai

Length of output: 4369


Stream output schemas lack type definitions, generating unknown types with no type safety.

SpeechToTextTurboStreamOutput (line 585) and SpeechToTextStreamOutput (line 613) are defined as empty objects, resulting in unknown TypeScript types and z.unknown() Zod schemas. This contrasts with their non-streaming counterparts (e.g., SpeechToTextTurboOutput), which have properly structured schemas with partial and output properties.

Since this is an auto-generated file from the fal.ai OpenAPI specification, the empty schemas indicate the upstream API does not define stream output structure. If streaming endpoints are genuinely unstructured, consider documenting this explicitly in the schema description. Otherwise, this suggests the upstream spec needs completion to properly type streaming responses.

🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/openapi/speech-to-text.json` around lines
585 - 613, SpeechToTextTurboStreamOutput and SpeechToTextStreamOutput are empty
object schemas causing generated TypeScript to be unknown/z.unknown(); update
these two schema definitions to either (a) explicitly document that stream
outputs are unstructured by adding a descriptive "description" field (e.g.,
"Unstructured streaming output; messages vary and are not typed") so generators
produce a clear note, or (b) mirror the structured form used by non-streaming
types (like SpeechToTextTurboOutput) by adding the expected properties (e.g.,
"partial", "output", with appropriate types and "partial" marked optional) so
the generated types are concrete; use the schema names
SpeechToTextTurboStreamOutput and SpeechToTextStreamOutput to locate and amend
the definitions.

Comment on lines +1067 to +1074
"securitySchemes": {
"apiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "Authorization"
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing global security rules in OpenAPI specification.

The security scheme apiKeyAuth is defined but not applied globally. Without a global security field, endpoints derived from this spec won't require authentication by default.

Consider adding a global security requirement at the root level:

+  "security": [
+    {
+      "apiKeyAuth": []
+    }
+  ],
   "components": {

This ensures all generated clients and documentation reflect that authentication is required.

🧰 Tools
🪛 Checkov (3.2.334)

[high] 1-1074: Ensure that the global security field has rules defined

(CKV_OPENAPI_4)

🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/openapi/text-to-3d.json` around lines 1067
- 1074, The OpenAPI spec defines the security scheme "apiKeyAuth" under
"securitySchemes" but doesn't apply it globally; add a top-level "security"
array at the root of the document referencing "apiKeyAuth" (e.g., {"apiKeyAuth":
[]}) so all operations require the apiKey by default; ensure you add this
alongside the existing "components" object (not inside it) so generated clients
and docs pick up the global requirement.

"sync_mode"
]
},
"FiboEditEditStructured_instructionOutput": {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and examine the structure
ls -la packages/typescript/ai-fal/scripts/openapi/text-to-json.json 2>/dev/null || echo "File not found"

Repository: TanStack/ai

Length of output: 165


🏁 Script executed:

# Count total lines in the file
wc -l packages/typescript/ai-fal/scripts/openapi/text-to-json.json 2>/dev/null || echo "Unable to count lines"

Repository: TanStack/ai

Length of output: 120


🏁 Script executed:

# Look at the specific lines mentioned in the review
echo "=== Line 119 context ==="
sed -n '115,125p' packages/typescript/ai-fal/scripts/openapi/text-to-json.json 2>/dev/null || echo "Error reading file"

echo -e "\n=== Line 176 context ==="
sed -n '172,182p' packages/typescript/ai-fal/scripts/openapi/text-to-json.json 2>/dev/null || echo "Error reading file"

echo -e "\n=== Line 727 context ==="
sed -n '723,733p' packages/typescript/ai-fal/scripts/openapi/text-to-json.json 2>/dev/null || echo "Error reading file"

echo -e "\n=== Line 1294 context ==="
sed -n '1290,1300p' packages/typescript/ai-fal/scripts/openapi/text-to-json.json 2>/dev/null || echo "Error reading file"

Repository: TanStack/ai

Length of output: 1368


🏁 Script executed:

# Run the verification scripts from the comment
echo "=== Checking for empty output schemas ==="
fd -e json . packages/typescript/ai-fal/scripts/openapi --exec grep -l '": {}' {} \; 2>/dev/null | head -10

echo -e "\n=== Count empty schemas per file ==="
for f in $(fd -e json . packages/typescript/ai-fal/scripts/openapi 2>/dev/null); do
  count=$(grep -c '": {}' "$f" 2>/dev/null || echo 0)
  if [ "$count" -gt 0 ]; then
    echo "$f: $count empty schemas"
  fi
done

Repository: TanStack/ai

Length of output: 1777


🏁 Script executed:

# Search for the specific schema names mentioned in the review
echo "=== Searching for mentioned empty schemas ==="
grep -n "FiboEditEditStructured_instructionOutput\|FiboLiteGenerateStructured_promptOutput\|FiboLiteGenerateStructured_promptLiteOutput\|FiboGenerateStructured_promptOutput" packages/typescript/ai-fal/scripts/openapi/text-to-json.json 2>/dev/null | head -20

Repository: TanStack/ai

Length of output: 330


🏁 Script executed:

# Find TypeScript generation scripts or code that uses these OpenAPI files
find packages/typescript/ai-fal -name "*generate*" -o -name "*schema*" -o -name "*openapi*" | grep -E "\.(ts|js|tsx)$" | head -20

Repository: TanStack/ai

Length of output: 289


🏁 Script executed:

# Check if there's code that generates types from these OpenAPI schemas
rg "FiboEditEditStructured|FiboLiteGenerateStructured|FiboGenerateStructured" packages/typescript/ai-fal --type typescript --type javascript -l

Repository: TanStack/ai

Length of output: 89


🏁 Script executed:

# Look for README or documentation explaining these OpenAPI files
find packages/typescript/ai-fal/scripts/openapi -name "README*" -o -name "*.md" | xargs cat 2>/dev/null

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

# Check the structure of the JSON file to understand how these empty schemas are used
# Get one complete example of how output schemas are defined vs empty ones
rg -A 5 "Output.*\{" packages/typescript/ai-fal/scripts/openapi/text-to-json.json | head -40

Repository: TanStack/ai

Length of output: 932


🏁 Script executed:

# Check if these empty schemas are referenced in operations/endpoints
rg "FiboEditEditStructured_instructionOutput|FiboLiteGenerateStructured_promptOutput|FiboLiteGenerateStructured_promptLiteOutput|FiboGenerateStructured_promptOutput" packages/typescript/ai-fal/scripts/openapi/text-to-json.json -B 5 -A 5 | head -60

Repository: TanStack/ai

Length of output: 1329


🏁 Script executed:

# Check the OpenAPI generation script to see if empty schemas are intentional
cat packages/typescript/ai-fal/scripts/generate-fal-openapi.ts | head -100

Repository: TanStack/ai

Length of output: 2636


🏁 Script executed:

# Check the TypeScript code generation configuration
cat packages/typescript/ai-fal/openapi-ts.config.ts

Repository: TanStack/ai

Length of output: 4580


🏁 Script executed:

# Check the heyapi generation script
cat packages/typescript/ai-fal/scripts/generate-fal-with-heyapi.ts | head -100

Repository: TanStack/ai

Length of output: 1267


🏁 Script executed:

# Search for any patterns about output schemas in the generation code
rg "Output\|output\|schema" packages/typescript/ai-fal/scripts/generate-fal-openapi.ts -A 2 -B 2 | head -50

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

# Check if there are any comments or documentation in the OpenAPI file itself about empty schemas
head -50 packages/typescript/ai-fal/scripts/openapi/text-to-json.json

Repository: TanStack/ai

Length of output: 1177


🏁 Script executed:

# Look at generated TypeScript types to see what empty schemas produce
find packages/typescript/ai-fal -name "*.ts" -path "*/generated/*" -o -name "*generated*" -type f | head -10

Repository: TanStack/ai

Length of output: 711


🏁 Script executed:

# Check the generated types for text-to-json to see how empty schemas translate
cat packages/typescript/ai-fal/src/generated/text-to-json/types.gen.ts | head -150

Repository: TanStack/ai

Length of output: 3301


🏁 Script executed:

# Check if empty schemas have specific handling in the generation
rg "FiboEditEditStructured_instructionOutput|FiboLiteGenerateStructured_promptOutput" packages/typescript/ai-fal/src/generated/text-to-json/types.gen.ts -A 5 -B 5

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

# Check the zod schema generation for these empty types
cat packages/typescript/ai-fal/src/generated/text-to-json/zod.gen.ts | grep -A 5 "FiboEditEditStructured_instructionOutput\|FiboLiteGenerateStructured_promptOutput\|FiboLiteGenerateStructured_promptLiteOutput\|FiboGenerateStructured_promptOutput"

Repository: TanStack/ai

Length of output: 37


Empty output schemas result in unknown TypeScript types, reducing type safety.

The following schemas are defined as empty objects ({}), which generate as unknown type in TypeScript:

  • FiboEditEditStructuredInstructionOutput (line 119)
  • FiboLiteGenerateStructuredPromptOutput (line 176)
  • FiboLiteGenerateStructuredPromptLiteOutput (line 727)
  • FiboGenerateStructuredPromptOutput (line 1294)

This prevents the TypeScript code generator from producing typed response models, leaving developers without type hints or IDE intellisense. The pattern is widespread across all generated OpenAPI files, suggesting incomplete response schema definitions from the upstream Fal API.

🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/openapi/text-to-json.json` at line 119,
The listed schemas (FiboEditEditStructuredInstructionOutput,
FiboLiteGenerateStructuredPromptOutput,
FiboLiteGenerateStructuredPromptLiteOutput, FiboGenerateStructuredPromptOutput)
are defined as empty objects which generate unknown TS types; replace each {}
with a proper JSON Schema (e.g.,
{"type":"object","properties":{...},"required":[...]}) or a $ref to an existing
response model so the OpenAPI TypeScript generator can emit concrete interfaces.
Locate the object definitions by the schema names in the JSON file and either
(1) define the expected properties and types for each schema based on the API
contract, or (2) point them to an existing schema via "$ref":
"#/components/schemas/ExistingModel" or at minimum declare
{"type":"object","additionalProperties":true} to avoid unknown. Ensure
consistency across all OpenAPI files that contain these schema names.

Comment on lines +1 to +128
{
"openapi": "3.0.4",
"info": {
"title": "Fal.ai text-to-text Models",
"version": "1.0.0",
"description": "OpenAPI schemas for all fal.ai text-to-text models (1 models total)",
"x-generated-at": "2026-01-26T05:40:51.980Z"
},
"components": {
"schemas": {
"File": {
"type": "object",
"properties": {
"url": {
"type": "string",
"format": "uri"
},
"content_type": {
"type": "string"
},
"file_name": {
"type": "string"
},
"file_size": {
"type": "integer"
}
},
"required": [
"url"
]
},
"QueueStatus": {
"type": "object",
"properties": {
"status": {
"type": "string",
"enum": [
"IN_PROGRESS",
"COMPLETED",
"FAILED"
]
},
"response_url": {
"type": "string",
"format": "uri"
}
},
"required": [
"status"
]
},
"AiDetectorDetectTextInput": {
"x-fal-order-properties": [
"text"
],
"type": "object",
"properties": {
"text": {
"description": "Text content to analyze for AI generation.",
"type": "string",
"examples": [
"yo guys so i just tried this new coffee place downtown and honestly?? not worth the hype. waited like 30 mins for a latte that tasted burnt lol. maybe i caught them on a bad day idk but wont be going back anytime soon"
],
"maxLength": 20000,
"minLength": 1,
"title": "Text"
}
},
"title": "TextDetectionInput",
"required": [
"text"
]
},
"AiDetectorDetectTextOutput": {
"x-fal-order-properties": [
"verdict",
"confidence",
"is_ai_generated",
"latency"
],
"type": "object",
"properties": {
"latency": {
"examples": [
13.617770671844482
],
"title": "Latency",
"type": "number"
},
"verdict": {
"examples": [
"human"
],
"title": "Verdict",
"type": "string"
},
"is_ai_generated": {
"examples": [
false
],
"title": "Is Ai Generated",
"type": "boolean"
},
"confidence": {
"examples": [
0.85
],
"title": "Confidence",
"type": "number"
}
},
"title": "AITextDetectionOutput",
"required": [
"verdict",
"confidence",
"is_ai_generated",
"latency"
]
}
},
"securitySchemes": {
"apiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "Authorization"
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check other OpenAPI specs for how security is applied
rg -n '"security"\s*:' --glob 'packages/typescript/ai-fal/scripts/openapi/*.json'

Repository: TanStack/ai

Length of output: 37


🏁 Script executed:

#!/bin/bash
# List all OpenAPI files in the directory
ls -la packages/typescript/ai-fal/scripts/openapi/*.json | head -20

echo "---"

# Check if any of these files have a security field at all (including in components)
echo "Files with apiKeyAuth definition:"
rg -l 'apiKeyAuth' packages/typescript/ai-fal/scripts/openapi/ --glob '*.json'

Repository: TanStack/ai

Length of output: 3857


🏁 Script executed:

#!/bin/bash
# Check for generation scripts or instructions
find packages/typescript/ai-fal -name "*.js" -o -name "*.ts" -o -name "*.sh" | grep -E "(generate|build)" | head -20

echo "---"

# Check one other file to confirm the pattern
echo "Checking text-to-image.json for security field:"
rg -A 5 '"components"' packages/typescript/ai-fal/scripts/openapi/text-to-image.json | head -20

echo "---"

# Check if there's any documentation or comments about these specs
find packages/typescript/ai-fal -name "README*" -o -name "*.md" | xargs grep -l "openapi\|security" 2>/dev/null | head -5

Repository: TanStack/ai

Length of output: 1550


🏁 Script executed:

#!/bin/bash
# Read the generation script to understand security handling
cat packages/typescript/ai-fal/scripts/generate-fal-openapi.ts | head -150

Repository: TanStack/ai

Length of output: 4054


🏁 Script executed:

#!/bin/bash
# Continue reading the generation script to find security handling
tail -n +150 packages/typescript/ai-fal/scripts/generate-fal-openapi.ts | head -200

Repository: TanStack/ai

Length of output: 6648


Add global security requirement to the OpenAPI spec generator.

components.securitySchemes.apiKeyAuth is defined but never applied globally. Generated OpenAPI files must include a top-level security array so that code generators and documentation tools enforce Authorization header requirements.

This file is auto-generated by packages/typescript/ai-fal/scripts/generate-fal-openapi.ts. Update the generator (around the const openApiSpec = { section) to include:

     const openApiSpec = {
       openapi: '3.0.4',
       info: { ... },
+      security: [{ apiKeyAuth: [] }],
       components: { ... }
     }

Then regenerate all OpenAPI specs in scripts/openapi/ to apply the fix consistently across all models.

🧰 Tools
🪛 Checkov (3.2.334)

[high] 1-128: Ensure that the global security field has rules defined

(CKV_OPENAPI_4)

🤖 Prompt for AI Agents
In `@packages/typescript/ai-fal/scripts/openapi/text-to-text.json` around lines 1
- 128, The OpenAPI output defines components.securitySchemes.apiKeyAuth but
never applies it globally; update the generator in
packages/typescript/ai-fal/scripts/generate-fal-openapi.ts where openApiSpec is
built to add a top-level "security" array that references the apiKeyAuth scheme
(e.g., security: [{ apiKeyAuth: [] }]), then re-run the generator to regenerate
all files under scripts/openapi/ so every generated spec (including
text-to-text.json) includes the global Authorization requirement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants