Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .cursor/rules/general.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ General:
- Avoid blocking the conversation with terminal commands. For example: A) most of my git commands run through pagers, so pipe their output to `cat` to avoid blocking the
terminal. B) You can use `tail` for logs, but be smart and use `-n` instead of `-f`, or the conversation will block
- Use the `gh` tool to interact with GitHub (search/view an Issue, create a PR).
- All new files are to be in TypeScript. Even if someone suggests: make this new foo3 feature, model it after `foo1.js`, create: `foo3.ts`. Chances are, a `foo2.ts` already exist that you can take a look at also for inspiration.
44 changes: 44 additions & 0 deletions TS7056.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
### TS7056 ("type exceeds maximum length")

The TS7056 error occurs when TypeScript's type inference system encounters types that are too complex to serialize during declaration file generation. This happens with our large discriminated unions in the robot schemas.

#### Current Solution: Patch-based declaration generation

We use a custom build process that temporarily patches the source files with type annotations during declaration generation, then restores the original files. This approach:

1. Keeps the source code clean and compatible with both repos
2. Applies minimal type annotations only during the build process
3. Generates proper declaration files without TS7056 errors
4. Maintains full type safety and discriminated union functionality

#### Implementation Details

The solution consists of two scripts:

1. `scripts/build.ts` - Main build script that:

- Compiles TypeScript to JavaScript
- Calls the type generation script

2. `scripts/generate-types.ts` - Type generation script that:
- Creates backups of problematic files
- Applies temporary type annotations to avoid TS7056
- Generates declaration files using TypeScript compiler
- Restores original files

The patches add generic type annotations like `z.ZodType` or `z.ZodDiscriminatedUnion<"robot", z.ZodDiscriminatedUnionOption<"robot">[]>` to the problematic schemas, which prevents TypeScript from trying to serialize the entire union structure.

#### Why this approach works

- The source files remain unchanged, so the code works identically in both `node-sdk` and `content` repos
- The type annotations are only applied temporarily during build, avoiding any runtime impact
- The generated declaration files are properly typed without hitting serialization limits
- The discriminated union structure is preserved, maintaining compatibility with code that accesses internal properties

#### Alternative approaches considered

1. **Facade types** - Would have solved TS7056 but broke compatibility with code accessing internal union properties
2. **d.ts bundlers** (rollup-dts, dts-bundle-generator, API Extractor) - These tools also hit the same TS7056 errors when processing the complex types
3. **Permanent type annotations** - Would require `any` types and eslint-disable comments, making the code less clean

The patch-based approach provides the best balance of maintainability, compatibility, and type safety.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"mp3"
],
"author": "Tim Koschuetzki <tim@transloadit.com>",
"packageManager": "yarn@4.9.1",
"packageManager": "yarn@4.9.2",
"engines": {
"node": ">= 20"
},
Expand All @@ -25,10 +25,10 @@
"into-stream": "^8.0.1",
"is-stream": "^4.0.1",
"p-map": "^7.0.3",
"tsx": "^4.20.1",
"tsx": "^4.20.3",
"tus-js-client": "^4.3.1",
"type-fest": "^4.41.0",
"zod": "^3.25.61"
"zod": "^3.25.76"
},
"devDependencies": {
"@babel/core": "^7.26.10",
Expand Down Expand Up @@ -75,7 +75,7 @@
"lint": "npm-run-all --parallel 'lint:*'",
"fix": "npm-run-all --serial 'fix:*'",
"next:update": "next-update --keep true --tldr",
"prepack": "tsc --build tsconfig.build.json",
"prepack": "tsx scripts/build.ts",
"test:unit": "vitest run --coverage ./test/unit",
"test:integration": "vitest run ./test/integration",
"test:all": "vitest run --coverage",
Expand Down
32 changes: 32 additions & 0 deletions scripts/build.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#!/usr/bin/env tsx
/* eslint-disable no-console */

import { execSync } from 'node:child_process'
import { existsSync, mkdirSync } from 'node:fs'

console.log('Building Transloadit SDK...')

// Ensure dist directory exists
if (!existsSync('dist')) {
mkdirSync('dist')
}

// Step 1: Compile TypeScript files without declarations
console.log('Compiling TypeScript...')
try {
execSync('tsc --project tsconfig.json', { stdio: 'inherit' })
} catch {
console.error('TypeScript compilation failed')
process.exit(1)
}

// Step 2: Generate declaration files using our custom script
console.log('Generating declaration files...')
try {
execSync('tsx scripts/generate-types.ts', { stdio: 'inherit' })
} catch {
console.error('Declaration file generation failed')
process.exit(1)
}

console.log('Build completed successfully!')
63 changes: 63 additions & 0 deletions scripts/generate-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env tsx
/* eslint-disable no-console */

import { execSync } from 'node:child_process'
import { readFileSync, writeFileSync } from 'node:fs'
import { join } from 'node:path'

// This script generates TypeScript declaration files while working around TS7056 errors

const ROBOT_INDEX_PATH = join('src', 'alphalib', 'types', 'robots', '_index.ts')
const TEMPLATE_PATH = join('src', 'alphalib', 'types', 'template.ts')

// Step 1: Create backup of original files
const robotIndexOriginal = readFileSync(ROBOT_INDEX_PATH, 'utf-8')
const templateOriginal = readFileSync(TEMPLATE_PATH, 'utf-8')

// Step 2: Apply temporary patches to avoid TS7056
const robotIndexPatched = robotIndexOriginal
.replace(
'export const robotsSchema = z.discriminatedUnion',
'export const robotsSchema: z.ZodDiscriminatedUnion<"robot", z.ZodDiscriminatedUnionOption<"robot">[]> = z.discriminatedUnion'
)
.replace(
'export const robotsWithHiddenFieldsSchema = z.discriminatedUnion',
'export const robotsWithHiddenFieldsSchema: z.ZodDiscriminatedUnion<"robot", z.ZodDiscriminatedUnionOption<"robot">[]> = z.discriminatedUnion'
)
.replace(
'export const robotsWithHiddenBotsSchema = z.discriminatedUnion',
'export const robotsWithHiddenBotsSchema: z.ZodDiscriminatedUnion<"robot", z.ZodDiscriminatedUnionOption<"robot">[]> = z.discriminatedUnion'
)
.replace(
'export const robotsWithHiddenBotsAndFieldsSchema = z.discriminatedUnion',
'export const robotsWithHiddenBotsAndFieldsSchema: z.ZodDiscriminatedUnion<"robot", z.ZodDiscriminatedUnionOption<"robot">[]> = z.discriminatedUnion'
)

const templatePatched = templateOriginal
.replace('export const stepSchema = z', 'export const stepSchema: z.ZodType = z')
.replace('export const stepsSchema = z.record', 'export const stepsSchema: z.ZodType = z.record')
.replace(
'const optionalStepsSchema = stepsSchema.optional()',
'const optionalStepsSchema: z.ZodType = stepsSchema.optional()'
)

// Step 3: Write patched files
writeFileSync(ROBOT_INDEX_PATH, robotIndexPatched)
writeFileSync(TEMPLATE_PATH, templatePatched)

try {
// Step 4: Generate declarations
console.log('Generating TypeScript declarations...')
execSync('tsc --project tsconfig.json --declaration --emitDeclarationOnly', {
stdio: 'inherit',
})
console.log('Declaration generation successful!')
} catch (error) {
console.error('Declaration generation failed')
throw error
} finally {
// Step 5: Restore original files
writeFileSync(ROBOT_INDEX_PATH, robotIndexOriginal)
writeFileSync(TEMPLATE_PATH, templateOriginal)
console.log('Original files restored')
}
Loading