diff --git a/examples/convert_to_webp.ts b/examples/convert_to_webp.ts index 5c9ae04d..4d0bb1f5 100644 --- a/examples/convert_to_webp.ts +++ b/examples/convert_to_webp.ts @@ -32,4 +32,4 @@ const status = await transloadit.createAssembly({ }, waitForCompletion: true, }) -console.log('Your WebP file:', status.results.webp[0].url) +console.log('Your WebP file:', status.results?.webp?.[0]?.url) diff --git a/examples/face_detect_download.ts b/examples/face_detect_download.ts index ca179049..f09165fd 100644 --- a/examples/face_detect_download.ts +++ b/examples/face_detect_download.ts @@ -44,7 +44,7 @@ const status = await transloadit.createAssembly({ // Now save the file const outPath = './output-face.jpg' const stream = createWriteStream(outPath) -const { url } = status.results.facesDetected[0] +const url = status.results?.facesDetected?.[0]?.url assert(url != null) -await got.stream(url).pipe(stream) +got.stream(url).pipe(stream) console.log('Your cropped face has been saved to', outPath) diff --git a/examples/rasterize_svg_to_png.ts b/examples/rasterize_svg_to_png.ts index 8792b29d..72da6beb 100644 --- a/examples/rasterize_svg_to_png.ts +++ b/examples/rasterize_svg_to_png.ts @@ -30,4 +30,4 @@ const status = await transloadit.createAssembly({ }, waitForCompletion: true, }) -console.log('Your PNG file:', status.results.png[0].url) +console.log('Your PNG file:', status.results?.png?.[0]?.url) diff --git a/examples/resize_an_image.ts b/examples/resize_an_image.ts index 9ccc823a..07430624 100644 --- a/examples/resize_an_image.ts +++ b/examples/resize_an_image.ts @@ -33,4 +33,4 @@ const status = await transloadit.createAssembly({ }, waitForCompletion: true, }) -console.log('Your resized image:', status.results.resize[0].url) +console.log('Your resized image:', status.results?.resize?.[0]?.url) diff --git a/package.json b/package.json index 5f2c1e4f..021846df 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "is-stream": "^4.0.1", "p-map": "^7.0.3", "tus-js-client": "^4.3.1", - "type-fest": "^4.39.1", + "type-fest": "^4.41.0", "zod": "^3.24.2" }, "devDependencies": { @@ -37,7 +37,7 @@ "@types/temp": "^0.9.4", "@typescript-eslint/eslint-plugin": "^8.29.1", "@typescript-eslint/parser": "^8.29.1", - "@vitest/coverage-v8": "^3.1.1", + "@vitest/coverage-v8": "^3.1.3", "badge-maker": "^4.1.0", "eslint": "8", "eslint-config-prettier": "^8.10.0", @@ -56,7 +56,7 @@ "prettier": "^3.5.3", "temp": "^0.9.4", "typescript": "^5.8.3", - "vitest": "^3.1.1" + "vitest": "^3.1.3" }, "repository": { "type": "git", diff --git a/src/Transloadit.ts b/src/Transloadit.ts index 9bfad503..3d62915d 100644 --- a/src/Transloadit.ts +++ b/src/Transloadit.ts @@ -310,6 +310,12 @@ export class Transloadit { } if (!waitForCompletion) return result + + if (result.assembly_id == null) { + throw new InconsistentResponseError( + 'Server returned an assembly response without an assembly_id after creation' + ) + } const awaitResult = await this.awaitAssemblyCompletion(result.assembly_id, { timeout, onAssemblyProgress, @@ -341,10 +347,18 @@ export class Transloadit { while (true) { const result = await this.getAssembly(assemblyId) + // If 'ok' is not in result, it implies a terminal state (e.g., error, completed, canceled). + // If 'ok' is present, then we check if it's one of the non-terminal polling states. if ( - result.ok !== 'ASSEMBLY_UPLOADING' && - result.ok !== 'ASSEMBLY_EXECUTING' && - result.ok !== 'ASSEMBLY_REPLAYING' + !('ok' in result) || + (result.ok !== 'ASSEMBLY_UPLOADING' && + result.ok !== 'ASSEMBLY_EXECUTING' && + // ASSEMBLY_REPLAYING is not a valid 'ok' status for polling, it means it's done replaying. + // The API does not seem to have an ASSEMBLY_REPLAYING status in the typical polling loop. + // It's usually a final status from the replay endpoint. + // For polling, we only care about UPLOADING and EXECUTING. + // If a replay operation puts it into a pollable state, that state would be EXECUTING. + result.ok !== 'ASSEMBLY_REPLAYING') // This line might need review based on actual API behavior for replayed assembly polling ) { return result // Done! } diff --git a/src/alphalib/types/assemblyStatus.ts b/src/alphalib/types/assemblyStatus.ts index d108b960..e7a66816 100644 --- a/src/alphalib/types/assemblyStatus.ts +++ b/src/alphalib/types/assemblyStatus.ts @@ -1,52 +1,105 @@ import { z } from 'zod' +const assemblyBusyCodeSchema = z.enum(['ASSEMBLY_UPLOADING']) + +const assemblyStatusOkCodeSchema = z.enum([ + 'ASSEMBLY_COMPLETED', + 'REQUEST_ABORTED', + 'ASSEMBLY_CANCELED', + 'ASSEMBLY_EXECUTING', +]) + +const assemblyStatusErrCodeSchema = z.enum([ + 'INVALID_INPUT_ERROR', + 'FILE_FILTER_DECLINED_FILE', + 'INTERNAL_COMMAND_TIMEOUT', + 'FILE_META_DATA_ERROR', + 'INVALID_FILE_META_DATA', + 'INTERNAL_COMMAND_ERROR', + 'TEMPLATE_NOT_FOUND', + 'TEMPLATE_DENIES_STEPS_OVERRIDE', + 'NO_AUTH_EXPIRES_PARAMETER', + 'MAX_SIZE_EXCEEDED', + 'CLOUDFLARE_IMPORT_VALIDATION', + 'S3_NOT_FOUND', + 'IMPORT_FILE_ERROR', + 'USER_COMMAND_ERROR', + 'BACKBLAZE_STORE_FAILURE', + 'DOCUMENT_CONVERT_UNSUPPORTED_CONVERSION', + 'INVALID_SIGNATURE', + 'GOOGLE_STORE_VALIDATION', + 'FILE_FILTER_VALIDATION', + 'HTTP_IMPORT_ACCESS_DENIED', + 'TEMPLATE_CREDENTIALS_INJECTION_ERROR', + 'HTTP_IMPORT_VALIDATION', + 'ASSEMBLY_EXPIRED', + 'WORKER_JOB_ERROR', + 'ASSEMBLY_STEP_UNKNOWN_USE', + 'IMAGE_RESIZE_ERROR', + 'TMP_FILE_DOWNLOAD_ERROR', + 'ASSEMBLY_DISALLOWED_ROBOTS_USED', + 'FILE_PREVIEW_VALIDATION', + 'HTTP_IMPORT_NOT_FOUND', + 'HTML_CONVERT_VALIDATION', + 'HTTP_IMPORT_FAILURE', + 'IMAGE_RESIZE_VALIDATION', + 'GOOGLE_IMPORT_VALIDATION', + 'S3_STORE_ACCESS_DENIED', + 'S3_ACCESS_DENIED', + 'CLOUDFILES_STORE_ERROR', +]) + +// --- Define Main Meta Schema (remove HLS specific fields) --- const assemblyStatusMetaSchema = z .object({ - width: z.number().nullable().optional(), - height: z.number().nullable().optional(), - date_file_modified: z.string().optional(), - aspect_ratio: z.union([z.number(), z.string()]).optional(), + width: z.union([z.number(), z.null()]).optional(), + height: z.union([z.number(), z.null()]).optional(), + date_file_modified: z.string().nullable().optional(), + aspect_ratio: z.union([z.number(), z.string(), z.null()]).optional(), has_clipping_path: z.boolean().optional(), - frame_count: z.number().optional(), + frame_count: z.union([z.number(), z.null()]).optional(), colorspace: z.string().nullable().optional(), has_transparency: z.boolean().nullable().optional(), average_color: z.string().nullable().optional(), - svgViewBoxWidth: z.number().nullable().optional(), - svgViewBoxHeight: z.number().nullable().optional(), + svgViewBoxWidth: z.union([z.number(), z.null()]).optional(), + svgViewBoxHeight: z.union([z.number(), z.null()]).optional(), date_recorded: z.union([z.string(), z.number()]).nullable().optional(), date_file_created: z.string().nullable().optional(), title: z.string().nullable().optional(), description: z.string().nullable().optional(), - duration: z.number().nullable().optional(), + duration: z.union([z.number(), z.null()]).optional(), location: z.string().nullable().optional(), city: z.string().nullable().optional(), state: z.string().nullable().optional(), - rights: z.string().nullable().optional(), + rights: z.union([z.string(), z.number()]).nullable().optional(), country: z.string().nullable().optional(), country_code: z.string().nullable().optional(), - keywords: z.string().nullable().optional(), - aperture: z.number().nullable().optional(), + keywords: z + .union([z.string(), z.array(z.union([z.string(), z.number()]))]) + .nullable() + .optional(), + aperture: z.union([z.number(), z.null()]).optional(), exposure_compensation: z.union([z.number(), z.string()]).nullable().optional(), exposure_mode: z.string().nullable().optional(), exposure_time: z.union([z.number(), z.string()]).nullable().optional(), flash: z.string().nullable().optional(), focal_length: z.string().nullable().optional(), - f_number: z.number().nullable().optional(), - iso: z.number().nullable().optional(), - light_value: z.number().nullable().optional(), + f_number: z.union([z.number(), z.null()]).optional(), + iso: z.union([z.number(), z.null()]).optional(), + light_value: z.union([z.number(), z.null()]).optional(), metering_mode: z.string().nullable().optional(), shutter_speed: z.union([z.number(), z.string()]).nullable().optional(), white_balance: z.string().nullable().optional(), device_name: z.string().nullable().optional(), device_vendor: z.string().nullable().optional(), - device_software: z.string().nullable().optional(), - latitude: z.number().nullable().optional(), - longitude: z.number().nullable().optional(), + device_software: z.union([z.string(), z.number()]).nullable().optional(), + latitude: z.union([z.number(), z.null()]).optional(), + longitude: z.union([z.number(), z.null()]).optional(), orientation: z.string().nullable().optional(), creator: z.string().nullable().optional(), author: z.string().nullable().optional(), copyright: z.string().nullable().optional(), - copyright_notice: z.string().nullable().optional(), + copyright_notice: z.union([z.string(), z.number()]).nullable().optional(), dominant_colors: z.array(z.string()).nullable().optional(), xp_title: z.string().nullable().optional(), xp_comment: z.string().nullable().optional(), @@ -54,48 +107,57 @@ const assemblyStatusMetaSchema = z xp_subject: z.string().nullable().optional(), recognized_text: z .array( - z.object({ - text: z.string(), - boundingPolygon: z.array(z.object({ x: z.number(), y: z.number() })), - }), + z + .object({ + text: z.string(), + boundingPolygon: z.array(z.object({ x: z.number(), y: z.number() })), + }) + .strict(), ) .optional(), - descriptions: z.array(z.string()).optional(), - framerate: z.number().nullable().optional(), - mean_volume: z.number().nullable().optional(), - video_bitrate: z.number().nullable().optional(), - overall_bitrate: z.number().nullable().optional(), + descriptions: z + .array(z.union([z.string(), z.object({ name: z.string(), confidence: z.number() }).strict()])) + .optional(), + framerate: z.union([z.number(), z.null()]).optional(), + mean_volume: z.union([z.number(), z.null()]).optional(), + video_bitrate: z.union([z.number(), z.null()]).optional(), + overall_bitrate: z.union([z.number(), z.null()]).optional(), video_codec: z.string().nullable().optional(), - audio_bitrate: z.number().nullable().optional(), - audio_samplerate: z.number().nullable().optional(), - audio_channels: z.number().nullable().optional(), - audio_codec: z.string().nullable().optional(), - bit_depth: z.number().nullable().optional(), - seekable: z.boolean().nullable().optional(), - rotation: z.number().nullable().optional(), + audio_bitrate: z.union([z.number(), z.null()]).optional(), + audio_samplerate: z.union([z.number(), z.null()]).optional(), + audio_channels: z.union([z.number(), z.null()]).optional(), + audio_codec: z.union([z.string(), z.null()]).optional(), + num_audio_streams: z.number().optional(), + bit_depth: z.union([z.number(), z.null()]).optional(), + seekable: z.union([z.boolean(), z.null()]).optional(), + rotation: z.union([z.number(), z.null()]).optional(), album: z.string().nullable().optional(), comment: z.string().nullable().optional(), year: z.union([z.string(), z.number()]).nullable().optional(), encoding_profile: z.string().nullable().optional(), encoding_level: z.string().nullable().optional(), - has_artwork: z.boolean().nullable().optional(), - beats_per_minute: z.number().nullable().optional(), - genre: z.string().nullable().optional(), + has_artwork: z.union([z.boolean(), z.null()]).optional(), + has_alpha_channel: z.boolean().nullable().optional(), + beats_per_minute: z.union([z.number(), z.null()]).optional(), + genre: z.union([z.string(), z.number()]).nullable().optional(), artist: z.string().nullable().optional(), performer: z.string().nullable().optional(), lyrics: z.string().nullable().optional(), band: z.string().nullable().optional(), - disc: z.string().nullable().optional(), + disc: z.union([z.string(), z.number()]).nullable().optional(), track: z.union([z.string(), z.number()]).nullable().optional(), turbo: z.boolean().nullable().optional(), encoder: z.string().nullable().optional(), thumb_index: z.number().nullable().optional(), - thumb_offset: z.number().nullable().optional(), - page_count: z.number().nullable().optional(), + thumb_offset: z + .preprocess((val) => (typeof val === 'string' ? parseInt(val, 10) : val), z.number()) + .nullable() + .optional(), + page_count: z.union([z.number(), z.null()]).optional(), page_size: z.string().nullable().optional(), producer: z.string().nullable().optional(), create_date: z.string().nullable().optional(), - modify_date: z.string().nullable().optional(), + modify_date: z.union([z.string(), z.number()]).nullable().optional(), colortransfer: z.string().nullable().optional(), colorprimaries: z.string().nullable().optional(), archive_directory: z.string().nullable().optional(), @@ -107,24 +169,76 @@ const assemblyStatusMetaSchema = z bandwidth: z.number().nullable().optional(), closed_captions: z.boolean().nullable().optional(), codecs: z.string().nullable().optional(), + storage_url: z.string().optional(), + version_id: z.string().optional(), faces: z .array( - z.object({ - x1: z.number(), - y1: z.number(), - x2: z.number(), - y2: z.number(), - confidence: z.number().optional(), - width: z.number(), - height: z.number(), - }), + z + .object({ + x1: z.number(), + y1: z.number(), + x2: z.number(), + y2: z.number(), + confidence: z.number().optional(), + width: z.number(), + height: z.number(), + }) + .strict(), ) .nullable() .optional(), + reason: z.string().optional(), + step: z.string().optional(), + previousStep: z.string().optional(), + exitCode: z.number().nullable().optional(), + exitSignal: z.string().nullable().optional(), + stdout: z.string().optional(), + stderr: z.string().optional(), + cmd: z.union([z.string(), z.array(z.union([z.string(), z.number()]))]).optional(), + worker: z.string().optional(), + word_count: z.union([z.number(), z.null()]).optional(), + character_count: z.union([z.number(), z.null()]).optional(), + character_count_with_spaces: z.union([z.number(), z.null()]).optional(), + line_count: z.union([z.number(), z.null()]).optional(), + paragraph_count: z.union([z.number(), z.null()]).optional(), }) - .passthrough() + .strict() export type AssemblyStatusMeta = z.infer +// --- Define HLS Nested Meta Schema --- +const hlsNestedMetaSchema = z.object({ + relative_path: z.string().optional(), + duration: z.number().optional(), + width: z.number().optional(), + height: z.number().optional(), + framerate: z.number().optional(), + overall_bitrate: z.number().optional(), + aspect_ratio: z.number().optional(), + video_codec: z.string().optional(), + audio_samplerate: z.number().optional(), + audio_channels: z.number().optional(), + num_audio_streams: z.number().optional(), + audio_codec: z.string().optional(), + seekable: z.boolean().optional(), + date_file_modified: z.string().optional(), + encoding_profile: z.string().optional(), + encoding_level: z.string().optional(), + has_artwork: z.boolean().optional(), + has_alpha_channel: z.boolean().optional(), + version_id: z.string().optional(), +}) +// --- End HLS Nested Meta Schema --- + +// --- Define HLS Playlist Schema --- +const hlsPlaylistSchema = z.object({ + name: z.union([z.string(), z.number()]).optional(), + content: z.string().optional(), + relative_path: z.string().optional(), + stream: z.string().optional(), + meta: hlsNestedMetaSchema.optional(), +}) +// --- End HLS Playlist Schema --- + const assemblyStatusUploadSchema = z .object({ id: z.string(), @@ -135,16 +249,16 @@ const assemblyStatusUploadSchema = z mime: z.string(), type: z.string().nullable(), field: z.string().nullable(), - md5hash: z.string(), + md5hash: z.string().nullable(), original_id: z.string(), original_basename: z.string(), original_name: z.string(), original_path: z.string(), - original_md5hash: z.string(), + original_md5hash: z.string().nullable(), from_batch_import: z.boolean(), is_tus_file: z.boolean(), tus_upload_url: z.string().nullable(), - url: z.string(), + url: z.string().nullable(), ssl_url: z.string(), meta: assemblyStatusMetaSchema, user_meta: z.record(z.unknown()).optional(), @@ -157,19 +271,20 @@ const assemblyStatusUploadSchema = z queue_time: z.number().optional(), exec_time: z.number().optional(), import_url: z.string().optional(), - cost: z.number().nullable().optional(), + cost: z.union([z.number(), z.null()]).optional(), }) .strict() export type AssemblyStatusUpload = z.infer export const assemblyStatusUploadsSchema = z.array(assemblyStatusUploadSchema) export type AssemblyStatusUploads = z.infer + export const assemblyStatusResultSchema = z .object({ id: z.string().optional(), basename: z.string().nullable().optional(), field: z.string().nullable().optional(), - md5hash: z.string().optional(), + md5hash: z.string().nullable().optional(), original_id: z.string().optional(), original_basename: z.string().nullable().optional(), original_path: z.string().nullable().optional(), @@ -184,6 +299,7 @@ export const assemblyStatusResultSchema = z exec_time: z.number().nullable().optional(), ext: z.string().nullable().optional(), filepath: z.string().nullable().optional(), + path: z.string().optional(), height: z.number().nullable().optional(), meta: assemblyStatusMetaSchema.nullable().optional(), mime: z.string().nullable().optional(), @@ -209,6 +325,14 @@ export const assemblyStatusResultSchema = z queueTime: z.number().nullable().optional(), execTime: z.number().nullable().optional(), import_url: z.string().optional(), + signed_url: z.string().optional(), + signed_ssl_url: z.string().optional(), + ios_url: z.string().optional(), + streaming_url: z.string().optional(), + remote_path: z.string().optional(), + playlists: z.array(hlsPlaylistSchema).optional(), + hls_url: z.string().optional(), + forcedFileExt: z.string().optional(), }) .strict() export type AssemblyStatusResult = z.infer @@ -216,85 +340,178 @@ export type AssemblyStatusResult = z.infer export const assemblyStatusResultsSchema = z.record(z.array(assemblyStatusResultSchema)) export type AssemblyStatusResults = z.infer -export const assemblyStatusSchema = z - .object({ - ok: z.string(), - http_code: z.number().optional(), - message: z.string(), - assembly_id: z.string(), - parent_id: z.string().nullable(), - account_id: z.string(), - account_name: z.string().optional(), - account_slug: z.string().optional(), - api_auth_key_id: z.string().nullable().optional(), - assembly_ssl_url: z.string(), - assembly_url: z.string(), - build_id: z.string().optional(), - bytes_expected: z.number(), - bytes_received: z.number(), - bytes_usage: z.number(), - client_agent: z.string().nullable(), - client_ip: z.string().nullable(), - client_referer: z.string().nullable(), - companion_url: z.string(), - executing_jobs: z.array(z.string()), - execution_duration: z.number(), - execution_start: z.string(), - fields: z.record(z.string()), - has_dupe_jobs: z.boolean(), - instance: z.string(), - is_infinite: z.boolean(), - jobs_queue_duration: z.number(), - last_job_completed: z.string(), - merged_params: z.string(), - notify_duration: z.string().nullable(), - notify_response_code: z.string().nullable(), - notify_response_data: z.string().nullable().optional(), - notify_start: z.string().nullable(), - notify_url: z.string().nullable(), - notify_status: z.string().nullable().optional(), - params: z.string(), - parent_assembly_status: z.string().nullable(), - queue_duration: z.number(), - region: z.string().optional(), - running_jobs: z.array(z.string()), - start_date: z.string(), - started_jobs: z.array(z.string()), - template_id: z.string().nullable(), - template_name: z.string().nullable().optional(), - template: z.string().nullable(), - transloadit_client: z.string(), - tus_url: z.string(), - usage_tags: z.string().optional(), - num_input_files: z.number().optional(), - expected_tus_uploads: z.number().optional(), - started_tus_uploads: z.number().optional(), - finished_tus_uploads: z.number().optional(), - tus_uploads: z - .array( - z.object({ +// --- Create Base Schema --- + +const assemblyStatusBaseSchema = z.object({ + // Extracted fields from assemblyStatusOkSchema + http_code: z.number().optional(), + message: z.string().optional(), + admin_cmd: z.unknown().optional(), + assemblyId: z.string().optional(), + assembly_id: z.string().optional(), + parent_id: z.string().nullable().optional(), + account_id: z.string().optional(), + account_name: z.string().optional(), + account_slug: z.string().optional(), + api_auth_key_id: z.string().nullable().optional(), + template_id: z.string().nullable().optional(), + template_name: z.string().nullable().optional(), + instance: z.string().optional(), + region: z.string().optional(), + assembly_url: z.string().optional(), + assembly_ssl_url: z.string().optional(), + uppyserver_url: z.string().optional(), + companion_url: z.string().optional(), + websocket_url: z.string().optional(), + update_stream_url: z.string().optional(), + tus_url: z.string().optional(), + bytes_received: z.number().optional(), + bytes_expected: z.number().nullable().optional(), + upload_duration: z.number().optional(), + client_agent: z.string().nullable().optional(), + client_ip: z.string().nullable().optional(), + client_referer: z.string().nullable().optional(), + transloadit_client: z.string().nullable().optional(), + start_date: z.string().optional(), + upload_meta_data_extracted: z.boolean().optional(), + warnings: z + .array( + z.object({ level: z.literal('notice').or(z.literal('warning')), msg: z.string() }).strict(), + ) + .optional(), + is_infinite: z.boolean().optional(), + has_dupe_jobs: z.boolean().optional(), + execution_start: z.string().optional(), + execution_duration: z.number().optional(), + queue_duration: z.number().optional(), + jobs_queue_duration: z.number().optional(), + notify_start: z.string().nullable().optional(), + notify_url: z.string().nullable().optional(), + notify_status: z.string().nullable().optional(), + notify_response_code: z.number().nullable().optional(), + notify_response_data: z.string().nullable().optional(), + notify_duration: z.number().nullable().optional(), + last_job_completed: z.string().nullable().optional(), + fields: z.record(z.unknown()).optional(), + running_jobs: z.array(z.string()).optional(), + bytes_usage: z.number().optional(), + usage_tags: z.string().optional(), + executing_jobs: z.array(z.string()).optional(), + started_jobs: z.array(z.string()).optional(), + parent_assembly_status: z.unknown().nullable().optional(), + params: z.string().nullable().optional(), + template: z.string().nullable().optional(), + merged_params: z.string().nullable().optional(), + num_input_files: z.number().optional(), + uploads: assemblyStatusUploadsSchema.optional(), + results: assemblyStatusResultsSchema.optional(), + build_id: z.string().optional(), + expected_tus_uploads: z.number().optional(), + started_tus_uploads: z.number().optional(), + finished_tus_uploads: z.number().optional(), + tus_uploads: z + .array( + z + .object({ filename: z.string(), fieldname: z.string(), + user_meta: z.record(z.unknown()).optional(), size: z.number(), offset: z.number(), finished: z.boolean(), upload_url: z.string(), - }), - ) - .optional(), - update_stream_url: z.string().optional(), - upload_duration: z.number(), - upload_meta_data_extracted: z.boolean(), - uppyserver_url: z.string(), - warnings: z.array( - z.object({ - level: z.literal('notice').or(z.literal('warning')), - msg: z.string(), - }), - ), - websocket_url: z.string(), - uploads: assemblyStatusUploadsSchema, - results: assemblyStatusResultsSchema, + local_path: z.string().optional(), + }) + .strict(), + ) + .optional(), +}) +// --- End Base Schema --- + +export const assemblyStatusBusySchema = z + .object({ + ok: assemblyBusyCodeSchema, + // TODO: Does busy status also share base fields? Need example. + // Assuming for now it might share some base fields but not all recursively? + // Let's make it extend the *non-recursive* base for now. + }) + .extend(assemblyStatusBaseSchema.shape) // Extend with non-recursive base fields + .strict() + +// --- Refactor Ok Schema to use Base --- +export const assemblyStatusOkSchema = assemblyStatusBaseSchema // Use original base + .extend({ + ok: assemblyStatusOkCodeSchema, }) .strict() + +// --- Refactor Err Schema to use Base --- +export const assemblyStatusErrSchema = assemblyStatusBaseSchema // Use ORIGINAL base + .extend({ + error: assemblyStatusErrCodeSchema, + retries: z.number().optional(), + numRetries: z.number().optional(), + reason: z.string().optional(), + step: z.string().optional(), + previousStep: z.string().optional(), + path: z.string().optional(), + exitCode: z.number().nullable().optional(), + exitSignal: z.string().nullable().optional(), + stdout: z.string().optional(), + stderr: z.string().optional(), + cmd: z.union([z.string(), z.array(z.union([z.string(), z.number()]))]).optional(), + worker: z.string().optional(), + err: z.record(z.unknown()).optional(), + headers: z.record(z.unknown()).optional(), + retryable: z.boolean().optional(), + }) + .strict() // Restore strict() + +// --- Define Step Failed Schema --- +// Represents an error that occurred during a specific step, +// but isn't one of the predefined general error codes. +export const assemblyStatusStepFailedSchema = assemblyStatusBaseSchema // Use ORIGINAL base + .extend({ + // No 'ok' or 'error' discriminator + step: z.string(), + previousStep: z.string(), + worker: z.string(), + // Message is optional in base, but seems required for this state + message: z.string(), + }) + .strict() // Restore strict() +// --- End Step Failed Schema --- + +// --- Define System Error Schema --- +// Represents a low-level system error not mapped to standard assembly errors. +// Happened in Assemblies: +// - 13ca71f3b8714859b48ec11e49be10f1 +// - 14ef7ab868e84350b2c0b70c9f3b2df1 +// - 83b21b12c30b416f82651464635f05f1 +// - 9198732f03cf40adbf778ae28fd52ef1 +// - dfa372cef24a420092f1be42af6d1df1 +// - e975612bc76e4738b759d1b36bc527f1 +// All for Workspace: 6f86325febd14de4bfb38cbd04ee1f39 +export const assemblyStatusSysErrSchema = assemblyStatusBaseSchema // Use ORIGINAL base + .extend({ + // Changed from .object() + // No 'ok' or 'error' discriminator + errno: z.number(), + code: z.string(), + syscall: z.string(), + path: z.string().optional(), // Path might be present + // Consider adding other potential sys error fields if observed later + }) + .passthrough() // SysErr can keep passthrough as it's inherently less defined +// --- End System Error Schema --- + +// Final schema defined lazily to handle recursion +export const assemblyStatusSchema = z.union([ + assemblyStatusBusySchema, // Use schema defined above + assemblyStatusOkSchema, // Use schema defined above + assemblyStatusErrSchema, // Use schema defined above + assemblyStatusStepFailedSchema, // Add the new step failed state + assemblyStatusSysErrSchema, // Add the new system error state +]) + export type AssemblyStatus = z.infer diff --git a/src/alphalib/types/robots/_index.ts b/src/alphalib/types/robots/_index.ts index f040742c..5a476693 100644 --- a/src/alphalib/types/robots/_index.ts +++ b/src/alphalib/types/robots/_index.ts @@ -1,325 +1,465 @@ import { z } from 'zod' -import { robotAudioArtworkInstructionsSchema, meta as audioArtworkMeta } from './audio-artwork.ts' -import { robotAudioConcatInstructionsSchema, meta as audioConcatMeta } from './audio-concat.ts' -import { robotAudioEncodeInstructionsSchema, meta as audioEncodeMeta } from './audio-encode.ts' -import { robotAudioLoopInstructionsSchema, meta as audioLoopMeta } from './audio-loop.ts' -import { robotAudioMergeInstructionsSchema, meta as audioMergeMeta } from './audio-merge.ts' import { - robotAudioWaveformInstructionsSchema, + interpolatableRobotAudioArtworkInstructionsSchema, + meta as audioArtworkMeta, +} from './audio-artwork.ts' +import { + interpolatableRobotAudioConcatInstructionsSchema, + meta as audioConcatMeta, +} from './audio-concat.ts' +import { + interpolatableRobotAudioEncodeInstructionsSchema, + meta as audioEncodeMeta, +} from './audio-encode.ts' +import { + interpolatableRobotAudioLoopInstructionsSchema, + meta as audioLoopMeta, +} from './audio-loop.ts' +import { + interpolatableRobotAudioMergeInstructionsSchema, + meta as audioMergeMeta, +} from './audio-merge.ts' +import { + interpolatableRobotAudioWaveformInstructionsSchema, meta as audioWaveformMeta, } from './audio-waveform.ts' -import { robotAzureImportInstructionsSchema, meta as azureImportMeta } from './azure-import.ts' -import { robotAzureStoreInstructionsSchema, meta as azureStoreMeta } from './azure-store.ts' import { - robotBackblazeImportInstructionsSchema, + interpolatableRobotAzureImportInstructionsSchema, + meta as azureImportMeta, +} from './azure-import.ts' +import { + interpolatableRobotAzureStoreInstructionsSchema, + meta as azureStoreMeta, +} from './azure-store.ts' +import { + interpolatableRobotBackblazeImportInstructionsSchema, meta as backblazeImportMeta, } from './backblaze-import.ts' import { - robotBackblazeStoreInstructionsSchema, + interpolatableRobotBackblazeStoreInstructionsSchema, meta as backblazeStoreMeta, } from './backblaze-store.ts' import { - robotCloudfilesImportInstructionsSchema, + interpolatableRobotCloudfilesImportInstructionsSchema, meta as cloudfilesImportMeta, } from './cloudfiles-import.ts' import { - robotCloudfilesStoreInstructionsSchema, + interpolatableRobotCloudfilesStoreInstructionsSchema, meta as cloudfilesStoreMeta, } from './cloudfiles-store.ts' import { - robotCloudflareImportInstructionsSchema, + interpolatableRobotCloudflareImportInstructionsSchema, meta as cloudflareImportMeta, } from './cloudflare-import.ts' import { - robotCloudflareStoreInstructionsSchema, + interpolatableRobotCloudflareStoreInstructionsSchema, meta as cloudflareStoreMeta, } from './cloudflare-store.ts' import { - robotDigitaloceanImportInstructionsSchema, + interpolatableRobotDigitaloceanImportInstructionsSchema, meta as digitaloceanImportMeta, } from './digitalocean-import.ts' import { - robotDigitaloceanStoreInstructionsSchema, + interpolatableRobotDigitaloceanStoreInstructionsSchema, meta as digitaloceanStoreMeta, } from './digitalocean-store.ts' import { - robotDocumentAutorotateInstructionsSchema, + interpolatableRobotDocumentAutorotateInstructionsSchema, meta as documentAutorotateMeta, } from './document-autorotate.ts' import { - robotDocumentConvertInstructionsSchema, + interpolatableRobotDocumentConvertInstructionsSchema, meta as documentConvertMeta, } from './document-convert.ts' import { - robotDocumentMergeInstructionsSchema, + interpolatableRobotDocumentMergeInstructionsSchema, meta as documentMergeMeta, } from './document-merge.ts' -import { robotDocumentOcrInstructionsSchema, meta as documentOcrMeta } from './document-ocr.ts' import { - robotDocumentSplitInstructionsSchema, + interpolatableRobotDocumentOcrInstructionsSchema, + meta as documentOcrMeta, +} from './document-ocr.ts' +import { + interpolatableRobotDocumentSplitInstructionsSchema, meta as documentSplitMeta, } from './document-split.ts' import { - robotDocumentThumbsInstructionsSchema, + interpolatableRobotDocumentThumbsInstructionsSchema, meta as documentThumbsMeta, } from './document-thumbs.ts' import { - robotDropboxImportInstructionsSchema, + interpolatableRobotDropboxImportInstructionsSchema, meta as dropboxImportMeta, } from './dropbox-import.ts' -import { robotDropboxStoreInstructionsSchema, meta as dropboxStoreMeta } from './dropbox-store.ts' -import { robotEdglyDeliverInstructionsSchema, meta as edglyDeliverMeta } from './edgly-deliver.ts' -import { robotFileCompressInstructionsSchema, meta as fileCompressMeta } from './file-compress.ts' import { - robotFileDecompressInstructionsSchema, + interpolatableRobotDropboxStoreInstructionsSchema, + meta as dropboxStoreMeta, +} from './dropbox-store.ts' +import { + interpolatableRobotEdglyDeliverInstructionsSchema, + meta as edglyDeliverMeta, +} from './edgly-deliver.ts' +import { + interpolatableRobotFileCompressInstructionsSchema, + meta as fileCompressMeta, +} from './file-compress.ts' +import { + interpolatableRobotFileDecompressInstructionsSchema, meta as fileDecompressMeta, } from './file-decompress.ts' -import { robotFileFilterInstructionsSchema, meta as fileFilterMeta } from './file-filter.ts' -import { robotFileHashInstructionsSchema, meta as fileHashMeta } from './file-hash.ts' -import { robotFilePreviewInstructionsSchema, meta as filePreviewMeta } from './file-preview.ts' -import { robotFileReadInstructionsSchema, meta as fileReadMeta } from './file-read.ts' -import { robotFileServeInstructionsSchema, meta as fileServeMeta } from './file-serve.ts' -import { robotFileVerifyInstructionsSchema, meta as fileVerifyMeta } from './file-verify.ts' -import { - robotFileVirusscanInstructionsSchema, +import { + interpolatableRobotFileFilterInstructionsSchema, + meta as fileFilterMeta, +} from './file-filter.ts' +import { interpolatableRobotFileHashInstructionsSchema, meta as fileHashMeta } from './file-hash.ts' +import { + interpolatableRobotFilePreviewInstructionsSchema, + meta as filePreviewMeta, +} from './file-preview.ts' +import { interpolatableRobotFileReadInstructionsSchema, meta as fileReadMeta } from './file-read.ts' +import { + interpolatableRobotFileServeInstructionsSchema, + meta as fileServeMeta, +} from './file-serve.ts' +import { + interpolatableRobotFileVerifyInstructionsSchema, + meta as fileVerifyMeta, +} from './file-verify.ts' +import { + interpolatableRobotFileVirusscanInstructionsSchema, meta as fileVirusscanMeta, } from './file-virusscan.ts' -import { robotFileWatermarkInstructionsSchema } from './file-watermark.ts' -import { robotFtpImportInstructionsSchema, meta as ftpImportMeta } from './ftp-import.ts' -import { robotFtpStoreInstructionsSchema, meta as ftpStoreMeta } from './ftp-store.ts' -import { robotGoogleImportInstructionsSchema, meta as googleImportMeta } from './google-import.ts' -import { robotGoogleStoreInstructionsSchema, meta as googleStoreMeta } from './google-store.ts' -import { robotHtmlConvertInstructionsSchema, meta as htmlConvertMeta } from './html-convert.ts' -import { robotHttpImportInstructionsSchema, meta as httpImportMeta } from './http-import.ts' -import { robotImageBgremoveInstructionsSchema } from './image-bgremove.ts' -import { - robotImageDescribeInstructionsSchema, +import { interpolatableRobotFileWatermarkInstructionsSchema } from './file-watermark.ts' +import { + interpolatableRobotFtpImportInstructionsSchema, + meta as ftpImportMeta, +} from './ftp-import.ts' +import { interpolatableRobotFtpStoreInstructionsSchema, meta as ftpStoreMeta } from './ftp-store.ts' +import { + interpolatableRobotGoogleImportInstructionsSchema, + meta as googleImportMeta, +} from './google-import.ts' +import { + interpolatableRobotGoogleStoreInstructionsSchema, + meta as googleStoreMeta, +} from './google-store.ts' +import { + interpolatableRobotHtmlConvertInstructionsSchema, + meta as htmlConvertMeta, +} from './html-convert.ts' +import { + interpolatableRobotHttpImportInstructionsSchema, + meta as httpImportMeta, +} from './http-import.ts' +import { + interpolatableRobotImageBgremoveInstructionsSchema, + meta as imageBgremoveMeta, +} from './image-bgremove.ts' +import { + interpolatableRobotImageDescribeInstructionsSchema, meta as imageDescribeMeta, } from './image-describe.ts' import { - robotImageFacedetectInstructionsSchema, + interpolatableRobotImageFacedetectInstructionsSchema, meta as imageFacedetectMeta, } from './image-facedetect.ts' import { - robotImageGenerateInstructionsSchema, - robotImageGenerateInstructionsWithHiddenFieldsSchema, + interpolatableRobotImageGenerateInstructionsSchema, + meta as imageGenerateMeta, } from './image-generate.ts' -import { robotImageMergeInstructionsSchema, meta as imageMergeMeta } from './image-merge.ts' -import { robotImageOcrInstructionsSchema, meta as imageOcrMeta } from './image-ocr.ts' import { - robotImageOptimizeInstructionsSchema, + interpolatableRobotImageMergeInstructionsSchema, + meta as imageMergeMeta, +} from './image-merge.ts' +import { interpolatableRobotImageOcrInstructionsSchema, meta as imageOcrMeta } from './image-ocr.ts' +import { + interpolatableRobotImageOptimizeInstructionsSchema, meta as imageOptimizeMeta, } from './image-optimize.ts' -import { robotImageResizeInstructionsSchema, meta as imageResizeMeta } from './image-resize.ts' -import { robotMetaWriteInstructionsSchema, meta as metaWriteMeta } from './meta-write.ts' -import { robotMinioImportInstructionsSchema, meta as minioImportMeta } from './minio-import.ts' -import { robotMinioStoreInstructionsSchema, meta as minioStoreMeta } from './minio-store.ts' -import { robotProgressSimulateInstructionsSchema } from './progress-simulate.ts' -import { robotS3ImportInstructionsSchema, meta as s3ImportMeta } from './s3-import.ts' -import { robotS3StoreInstructionsSchema, meta as s3StoreMeta } from './s3-store.ts' -import { robotScriptRunInstructionsSchema, meta as scriptRunMeta } from './script-run.ts' -import { robotSftpImportInstructionsSchema, meta as sftpImportMeta } from './sftp-import.ts' -import { robotSftpStoreInstructionsSchema, meta as sftpStoreMeta } from './sftp-store.ts' -import { - robotSpeechTranscribeInstructionsSchema, +import { + interpolatableRobotImageResizeInstructionsSchema, + meta as imageResizeMeta, +} from './image-resize.ts' +import { + interpolatableRobotMetaWriteInstructionsSchema, + meta as metaWriteMeta, +} from './meta-write.ts' +import { + interpolatableRobotMinioImportInstructionsSchema, + meta as minioImportMeta, +} from './minio-import.ts' +import { + interpolatableRobotMinioStoreInstructionsSchema, + meta as minioStoreMeta, +} from './minio-store.ts' +import { interpolatableRobotProgressSimulateInstructionsSchema } from './progress-simulate.ts' +import { interpolatableRobotS3ImportInstructionsSchema, meta as s3ImportMeta } from './s3-import.ts' +import { interpolatableRobotS3StoreInstructionsSchema, meta as s3StoreMeta } from './s3-store.ts' +import { + interpolatableRobotScriptRunInstructionsSchema, + meta as scriptRunMeta, +} from './script-run.ts' +import { + interpolatableRobotSftpImportInstructionsSchema, + meta as sftpImportMeta, +} from './sftp-import.ts' +import { + interpolatableRobotSftpStoreInstructionsSchema, + meta as sftpStoreMeta, +} from './sftp-store.ts' +import { + interpolatableRobotSpeechTranscribeInstructionsSchema, robotSpeechTranscribeInstructionsWithHiddenFieldsSchema, meta as speechTranscribeMeta, } from './speech-transcribe.ts' import { - robotSupabaseImportInstructionsSchema, + interpolatableRobotSupabaseImportInstructionsSchema, meta as supabaseImportMeta, } from './supabase-import.ts' import { - robotSupabaseStoreInstructionsSchema, + interpolatableRobotSupabaseStoreInstructionsSchema, meta as supabaseStoreMeta, } from './supabase-store.ts' -import { robotSwiftImportInstructionsSchema, meta as swiftImportMeta } from './swift-import.ts' -import { robotSwiftStoreInstructionsSchema, meta as swiftStoreMeta } from './swift-store.ts' -import { robotTextSpeakInstructionsSchema, meta as textSpeakMeta } from './text-speak.ts' import { - robotTextTranslateInstructionsSchema, + interpolatableRobotSwiftImportInstructionsSchema, + meta as swiftImportMeta, +} from './swift-import.ts' +import { + interpolatableRobotSwiftStoreInstructionsSchema, + meta as swiftStoreMeta, +} from './swift-store.ts' +import { + interpolatableRobotTextSpeakInstructionsSchema, + meta as textSpeakMeta, +} from './text-speak.ts' +import { + interpolatableRobotTextTranslateInstructionsSchema, meta as textTranslateMeta, } from './text-translate.ts' -import { robotTigrisImportInstructionsSchema, meta as tigrisImport } from './tigris-import.ts' -import { robotTigrisStoreInstructionsSchema, meta as tigrisStore } from './tigris-store.ts' -import { robotTlcdnDeliverInstructionsSchema, meta as tlcdnDeliverMeta } from './tlcdn-deliver.ts' -import { robotTusStoreInstructionsSchema, meta as tusStoreMeta } from './tus-store.ts' -import { robotUploadHandleInstructionsSchema, meta as uploadHandleMeta } from './upload-handle.ts' import { - robotVideoAdaptiveInstructionsSchema, + interpolatableRobotTigrisImportInstructionsSchema, + meta as tigrisImport, +} from './tigris-import.ts' +import { + interpolatableRobotTigrisStoreInstructionsSchema, + meta as tigrisStore, +} from './tigris-store.ts' +import { + interpolatableRobotTlcdnDeliverInstructionsSchema, + meta as tlcdnDeliverMeta, +} from './tlcdn-deliver.ts' +import { interpolatableRobotTusStoreInstructionsSchema, meta as tusStoreMeta } from './tus-store.ts' +import { + interpolatableRobotUploadHandleInstructionsSchema, + meta as uploadHandleMeta, +} from './upload-handle.ts' +import { + interpolatableRobotVideoAdaptiveInstructionsSchema, meta as videoAdaptiveMeta, } from './video-adaptive.ts' -import { robotVideoConcatInstructionsSchema, meta as videoConcatMeta } from './video-concat.ts' -import { robotVideoEncodeInstructionsSchema, meta as videoEncodeMeta } from './video-encode.ts' -import { robotVideoMergeInstructionsSchema, meta as videoMergeMeta } from './video-merge.ts' import { - robotVideoSubtitleInstructionsSchema, + interpolatableRobotVideoConcatInstructionsSchema, + meta as videoConcatMeta, +} from './video-concat.ts' +import { + interpolatableRobotVideoEncodeInstructionsSchema, + meta as videoEncodeMeta, +} from './video-encode.ts' +import { + interpolatableRobotVideoMergeInstructionsSchema, + meta as videoMergeMeta, +} from './video-merge.ts' +import { + interpolatableRobotVideoOndemandInstructionsSchema, + meta as videoOndemandMeta, +} from './video-ondemand.ts' +import { + interpolatableRobotVideoSubtitleInstructionsSchema, meta as videoSubtitleMeta, } from './video-subtitle.ts' -import { robotVideoThumbsInstructionsSchema, meta as videoThumbsMeta } from './video-thumbs.ts' -import { robotVimeoStoreInstructionsSchema, meta as vimeoStoreMeta } from './vimeo-store.ts' -import { robotWasabiImportInstructionsSchema, meta as wasabiImportMeta } from './wasabi-import.ts' -import { robotWasabiStoreInstructionsSchema, meta as wasabiStoreMeta } from './wasabi-store.ts' -import { robotYoutubeStoreInstructionsSchema, meta as youtubeStoreMeta } from './youtube-store.ts' +import { + interpolatableRobotVideoThumbsInstructionsSchema, + meta as videoThumbsMeta, +} from './video-thumbs.ts' +import { + interpolatableRobotVimeoStoreInstructionsSchema, + meta as vimeoStoreMeta, +} from './vimeo-store.ts' +import { + interpolatableRobotWasabiImportInstructionsSchema, + meta as wasabiImportMeta, +} from './wasabi-import.ts' +import { + interpolatableRobotWasabiStoreInstructionsSchema, + meta as wasabiStoreMeta, +} from './wasabi-store.ts' +import { + interpolatableRobotYoutubeStoreInstructionsSchema, + meta as youtubeStoreMeta, +} from './youtube-store.ts' const robotStepsInstructions = [ - robotAudioArtworkInstructionsSchema, - robotAudioConcatInstructionsSchema, - robotAudioEncodeInstructionsSchema, - robotAudioLoopInstructionsSchema, - robotAudioMergeInstructionsSchema, - robotAudioWaveformInstructionsSchema, - robotAzureImportInstructionsSchema, - robotAzureStoreInstructionsSchema, - robotBackblazeImportInstructionsSchema, - robotBackblazeStoreInstructionsSchema, - robotCloudfilesImportInstructionsSchema, - robotCloudfilesStoreInstructionsSchema, - robotCloudflareImportInstructionsSchema, - robotCloudflareStoreInstructionsSchema, - robotDigitaloceanImportInstructionsSchema, - robotDigitaloceanStoreInstructionsSchema, - robotDocumentAutorotateInstructionsSchema, - robotDocumentConvertInstructionsSchema, - robotDocumentMergeInstructionsSchema, - robotDocumentOcrInstructionsSchema, - robotFileReadInstructionsSchema, - robotDocumentSplitInstructionsSchema, - robotDocumentThumbsInstructionsSchema, - robotDropboxImportInstructionsSchema, - robotDropboxStoreInstructionsSchema, - robotEdglyDeliverInstructionsSchema, - robotFileCompressInstructionsSchema, - robotFileDecompressInstructionsSchema, - robotFileFilterInstructionsSchema, - robotFileHashInstructionsSchema, - robotFilePreviewInstructionsSchema, - robotFileServeInstructionsSchema, - robotFileVerifyInstructionsSchema, - robotFileVirusscanInstructionsSchema, - robotFtpImportInstructionsSchema, - robotFtpStoreInstructionsSchema, - robotGoogleImportInstructionsSchema, - robotGoogleStoreInstructionsSchema, - robotHtmlConvertInstructionsSchema, - robotHttpImportInstructionsSchema, - robotImageBgremoveInstructionsSchema, - robotImageDescribeInstructionsSchema, - robotImageFacedetectInstructionsSchema, - robotImageMergeInstructionsSchema, - robotImageOcrInstructionsSchema, - robotImageOptimizeInstructionsSchema, - robotImageResizeInstructionsSchema, - robotMetaWriteInstructionsSchema, - robotMinioImportInstructionsSchema, - robotMinioStoreInstructionsSchema, - robotS3ImportInstructionsSchema, - robotS3StoreInstructionsSchema, - robotScriptRunInstructionsSchema, - robotSftpImportInstructionsSchema, - robotSftpStoreInstructionsSchema, - robotSpeechTranscribeInstructionsSchema, - robotSupabaseImportInstructionsSchema, - robotSupabaseStoreInstructionsSchema, - robotSwiftImportInstructionsSchema, - robotSwiftStoreInstructionsSchema, - robotTextSpeakInstructionsSchema, - robotTextTranslateInstructionsSchema, - robotTigrisImportInstructionsSchema, - robotTigrisStoreInstructionsSchema, - robotTlcdnDeliverInstructionsSchema, - robotTusStoreInstructionsSchema, - robotUploadHandleInstructionsSchema, - robotVideoAdaptiveInstructionsSchema, - robotVideoConcatInstructionsSchema, - robotVideoEncodeInstructionsSchema, - robotVideoMergeInstructionsSchema, - robotVideoSubtitleInstructionsSchema, - robotVideoThumbsInstructionsSchema, - robotVimeoStoreInstructionsSchema, - robotWasabiImportInstructionsSchema, - robotWasabiStoreInstructionsSchema, - robotYoutubeStoreInstructionsSchema, + interpolatableRobotAudioArtworkInstructionsSchema, + interpolatableRobotAudioConcatInstructionsSchema, + interpolatableRobotAudioEncodeInstructionsSchema, + interpolatableRobotAudioLoopInstructionsSchema, + interpolatableRobotAudioMergeInstructionsSchema, + interpolatableRobotAudioWaveformInstructionsSchema, + interpolatableRobotAzureImportInstructionsSchema, + interpolatableRobotAzureStoreInstructionsSchema, + interpolatableRobotBackblazeImportInstructionsSchema, + interpolatableRobotBackblazeStoreInstructionsSchema, + interpolatableRobotCloudfilesImportInstructionsSchema, + interpolatableRobotCloudfilesStoreInstructionsSchema, + interpolatableRobotCloudflareImportInstructionsSchema, + interpolatableRobotCloudflareStoreInstructionsSchema, + interpolatableRobotDigitaloceanImportInstructionsSchema, + interpolatableRobotDigitaloceanStoreInstructionsSchema, + interpolatableRobotDocumentAutorotateInstructionsSchema, + interpolatableRobotDocumentConvertInstructionsSchema, + interpolatableRobotDocumentMergeInstructionsSchema, + interpolatableRobotDocumentOcrInstructionsSchema, + interpolatableRobotFileReadInstructionsSchema, + interpolatableRobotDocumentSplitInstructionsSchema, + interpolatableRobotDocumentThumbsInstructionsSchema, + interpolatableRobotDropboxImportInstructionsSchema, + interpolatableRobotDropboxStoreInstructionsSchema, + interpolatableRobotEdglyDeliverInstructionsSchema, + interpolatableRobotFileCompressInstructionsSchema, + interpolatableRobotFileDecompressInstructionsSchema, + interpolatableRobotFileFilterInstructionsSchema, + interpolatableRobotFileHashInstructionsSchema, + interpolatableRobotFilePreviewInstructionsSchema, + interpolatableRobotFileServeInstructionsSchema, + interpolatableRobotFileVerifyInstructionsSchema, + interpolatableRobotFileVirusscanInstructionsSchema, + interpolatableRobotFtpImportInstructionsSchema, + interpolatableRobotFtpStoreInstructionsSchema, + interpolatableRobotGoogleImportInstructionsSchema, + interpolatableRobotGoogleStoreInstructionsSchema, + interpolatableRobotHtmlConvertInstructionsSchema, + interpolatableRobotHttpImportInstructionsSchema, + interpolatableRobotImageBgremoveInstructionsSchema, + interpolatableRobotImageDescribeInstructionsSchema, + interpolatableRobotImageFacedetectInstructionsSchema, + interpolatableRobotImageGenerateInstructionsSchema, + interpolatableRobotImageMergeInstructionsSchema, + interpolatableRobotImageOcrInstructionsSchema, + interpolatableRobotImageOptimizeInstructionsSchema, + interpolatableRobotImageResizeInstructionsSchema, + interpolatableRobotMetaWriteInstructionsSchema, + interpolatableRobotMinioImportInstructionsSchema, + interpolatableRobotMinioStoreInstructionsSchema, + interpolatableRobotS3ImportInstructionsSchema, + interpolatableRobotS3StoreInstructionsSchema, + interpolatableRobotScriptRunInstructionsSchema, + interpolatableRobotSftpImportInstructionsSchema, + interpolatableRobotSftpStoreInstructionsSchema, + interpolatableRobotSpeechTranscribeInstructionsSchema, + interpolatableRobotSupabaseImportInstructionsSchema, + interpolatableRobotSupabaseStoreInstructionsSchema, + interpolatableRobotSwiftImportInstructionsSchema, + interpolatableRobotSwiftStoreInstructionsSchema, + interpolatableRobotTextSpeakInstructionsSchema, + interpolatableRobotTextTranslateInstructionsSchema, + interpolatableRobotTigrisImportInstructionsSchema, + interpolatableRobotTigrisStoreInstructionsSchema, + interpolatableRobotTlcdnDeliverInstructionsSchema, + interpolatableRobotTusStoreInstructionsSchema, + interpolatableRobotUploadHandleInstructionsSchema, + interpolatableRobotVideoAdaptiveInstructionsSchema, + interpolatableRobotVideoConcatInstructionsSchema, + interpolatableRobotVideoEncodeInstructionsSchema, + interpolatableRobotVideoMergeInstructionsSchema, + interpolatableRobotVideoOndemandInstructionsSchema, + interpolatableRobotVideoSubtitleInstructionsSchema, + interpolatableRobotVideoThumbsInstructionsSchema, + interpolatableRobotVimeoStoreInstructionsSchema, + interpolatableRobotWasabiImportInstructionsSchema, + interpolatableRobotWasabiStoreInstructionsSchema, + interpolatableRobotYoutubeStoreInstructionsSchema, ] as const const robotStepsInstructionsWithHiddenFields = [ - robotAudioArtworkInstructionsSchema, - robotAudioConcatInstructionsSchema, - robotAudioEncodeInstructionsSchema, - robotAudioLoopInstructionsSchema, - robotAudioMergeInstructionsSchema, - robotAudioWaveformInstructionsSchema, - robotAzureImportInstructionsSchema, - robotAzureStoreInstructionsSchema, - robotBackblazeImportInstructionsSchema, - robotBackblazeStoreInstructionsSchema, - robotCloudfilesImportInstructionsSchema, - robotCloudfilesStoreInstructionsSchema, - robotCloudflareImportInstructionsSchema, - robotCloudflareStoreInstructionsSchema, - robotDigitaloceanImportInstructionsSchema, - robotDigitaloceanStoreInstructionsSchema, - robotDocumentAutorotateInstructionsSchema, - robotDocumentConvertInstructionsSchema, - robotDocumentMergeInstructionsSchema, - robotDocumentOcrInstructionsSchema, - robotFileReadInstructionsSchema, - robotDocumentSplitInstructionsSchema, - robotDocumentThumbsInstructionsSchema, - robotDropboxImportInstructionsSchema, - robotDropboxStoreInstructionsSchema, - robotEdglyDeliverInstructionsSchema, - robotFileCompressInstructionsSchema, - robotFileDecompressInstructionsSchema, - robotFileFilterInstructionsSchema, - robotFileHashInstructionsSchema, - robotFilePreviewInstructionsSchema, - robotFileServeInstructionsSchema, - robotFileVerifyInstructionsSchema, - robotFileVirusscanInstructionsSchema, - robotFtpImportInstructionsSchema, - robotFtpStoreInstructionsSchema, - robotGoogleImportInstructionsSchema, - robotGoogleStoreInstructionsSchema, - robotHtmlConvertInstructionsSchema, - robotHttpImportInstructionsSchema, - robotImageDescribeInstructionsSchema, - robotImageFacedetectInstructionsSchema, - robotImageMergeInstructionsSchema, - robotImageOcrInstructionsSchema, - robotImageOptimizeInstructionsSchema, - robotImageResizeInstructionsSchema, - robotMetaWriteInstructionsSchema, - robotMinioImportInstructionsSchema, - robotMinioStoreInstructionsSchema, - robotS3ImportInstructionsSchema, - robotS3StoreInstructionsSchema, - robotScriptRunInstructionsSchema, - robotSftpImportInstructionsSchema, - robotSftpStoreInstructionsSchema, + interpolatableRobotAudioArtworkInstructionsSchema, + interpolatableRobotAudioConcatInstructionsSchema, + interpolatableRobotAudioEncodeInstructionsSchema, + interpolatableRobotAudioLoopInstructionsSchema, + interpolatableRobotAudioMergeInstructionsSchema, + interpolatableRobotAudioWaveformInstructionsSchema, + interpolatableRobotAzureImportInstructionsSchema, + interpolatableRobotAzureStoreInstructionsSchema, + interpolatableRobotBackblazeImportInstructionsSchema, + interpolatableRobotBackblazeStoreInstructionsSchema, + interpolatableRobotCloudfilesImportInstructionsSchema, + interpolatableRobotCloudfilesStoreInstructionsSchema, + interpolatableRobotCloudflareImportInstructionsSchema, + interpolatableRobotCloudflareStoreInstructionsSchema, + interpolatableRobotDigitaloceanImportInstructionsSchema, + interpolatableRobotDigitaloceanStoreInstructionsSchema, + interpolatableRobotDocumentAutorotateInstructionsSchema, + interpolatableRobotDocumentConvertInstructionsSchema, + interpolatableRobotDocumentMergeInstructionsSchema, + interpolatableRobotDocumentOcrInstructionsSchema, + interpolatableRobotFileReadInstructionsSchema, + interpolatableRobotDocumentSplitInstructionsSchema, + interpolatableRobotDocumentThumbsInstructionsSchema, + interpolatableRobotDropboxImportInstructionsSchema, + interpolatableRobotDropboxStoreInstructionsSchema, + interpolatableRobotEdglyDeliverInstructionsSchema, + interpolatableRobotFileCompressInstructionsSchema, + interpolatableRobotFileDecompressInstructionsSchema, + interpolatableRobotFileFilterInstructionsSchema, + interpolatableRobotFileHashInstructionsSchema, + interpolatableRobotFilePreviewInstructionsSchema, + interpolatableRobotFileServeInstructionsSchema, + interpolatableRobotFileVerifyInstructionsSchema, + interpolatableRobotFileVirusscanInstructionsSchema, + interpolatableRobotFtpImportInstructionsSchema, + interpolatableRobotFtpStoreInstructionsSchema, + interpolatableRobotGoogleImportInstructionsSchema, + interpolatableRobotGoogleStoreInstructionsSchema, + interpolatableRobotHtmlConvertInstructionsSchema, + interpolatableRobotHttpImportInstructionsSchema, + interpolatableRobotImageDescribeInstructionsSchema, + interpolatableRobotImageFacedetectInstructionsSchema, + interpolatableRobotImageGenerateInstructionsSchema, + interpolatableRobotImageMergeInstructionsSchema, + interpolatableRobotImageOcrInstructionsSchema, + interpolatableRobotImageOptimizeInstructionsSchema, + interpolatableRobotImageResizeInstructionsSchema, + interpolatableRobotMetaWriteInstructionsSchema, + interpolatableRobotMinioImportInstructionsSchema, + interpolatableRobotMinioStoreInstructionsSchema, + interpolatableRobotS3ImportInstructionsSchema, + interpolatableRobotS3StoreInstructionsSchema, + interpolatableRobotScriptRunInstructionsSchema, + interpolatableRobotSftpImportInstructionsSchema, + interpolatableRobotSftpStoreInstructionsSchema, robotSpeechTranscribeInstructionsWithHiddenFieldsSchema, - robotSupabaseImportInstructionsSchema, - robotSupabaseStoreInstructionsSchema, - robotSwiftImportInstructionsSchema, - robotSwiftStoreInstructionsSchema, - robotTextSpeakInstructionsSchema, - robotTextTranslateInstructionsSchema, - robotTigrisImportInstructionsSchema, - robotTigrisStoreInstructionsSchema, - robotTlcdnDeliverInstructionsSchema, - robotTusStoreInstructionsSchema, - robotUploadHandleInstructionsSchema, - robotVideoAdaptiveInstructionsSchema, - robotVideoConcatInstructionsSchema, - robotVideoEncodeInstructionsSchema, - robotVideoMergeInstructionsSchema, - robotVideoSubtitleInstructionsSchema, - robotVideoThumbsInstructionsSchema, - robotVimeoStoreInstructionsSchema, - robotWasabiImportInstructionsSchema, - robotWasabiStoreInstructionsSchema, - robotYoutubeStoreInstructionsSchema, + interpolatableRobotSupabaseImportInstructionsSchema, + interpolatableRobotSupabaseStoreInstructionsSchema, + interpolatableRobotSwiftImportInstructionsSchema, + interpolatableRobotSwiftStoreInstructionsSchema, + interpolatableRobotTextSpeakInstructionsSchema, + interpolatableRobotTextTranslateInstructionsSchema, + interpolatableRobotTigrisImportInstructionsSchema, + interpolatableRobotTigrisStoreInstructionsSchema, + interpolatableRobotTlcdnDeliverInstructionsSchema, + interpolatableRobotTusStoreInstructionsSchema, + interpolatableRobotUploadHandleInstructionsSchema, + interpolatableRobotVideoAdaptiveInstructionsSchema, + interpolatableRobotVideoConcatInstructionsSchema, + interpolatableRobotVideoEncodeInstructionsSchema, + interpolatableRobotVideoMergeInstructionsSchema, + interpolatableRobotVideoOndemandInstructionsSchema, + interpolatableRobotVideoSubtitleInstructionsSchema, + interpolatableRobotVideoThumbsInstructionsSchema, + interpolatableRobotVimeoStoreInstructionsSchema, + interpolatableRobotWasabiImportInstructionsSchema, + interpolatableRobotWasabiStoreInstructionsSchema, + interpolatableRobotYoutubeStoreInstructionsSchema, ] as const /** @@ -336,15 +476,13 @@ export const robotsWithHiddenFieldsSchema = z.discriminatedUnion('robot', [ */ export const robotsWithHiddenBotsSchema = z.discriminatedUnion('robot', [ ...robotStepsInstructions, - robotFileWatermarkInstructionsSchema, - robotImageGenerateInstructionsSchema, - robotProgressSimulateInstructionsSchema, + interpolatableRobotFileWatermarkInstructionsSchema, + interpolatableRobotProgressSimulateInstructionsSchema, ]) export const robotsWithHiddenBotsAndFieldsSchema = z.discriminatedUnion('robot', [ ...robotStepsInstructionsWithHiddenFields, - robotFileWatermarkInstructionsSchema, - robotImageGenerateInstructionsWithHiddenFieldsSchema, - robotProgressSimulateInstructionsSchema, + interpolatableRobotFileWatermarkInstructionsSchema, + interpolatableRobotProgressSimulateInstructionsSchema, ]) export type RobotsWithHiddenBots = z.infer @@ -393,6 +531,8 @@ export const robotsMeta = { httpImportMeta, imageDescribeMeta, imageFacedetectMeta, + imageBgremoveMeta, + imageGenerateMeta, imageMergeMeta, imageOcrMeta, imageOptimizeMeta, @@ -421,6 +561,7 @@ export const robotsMeta = { videoConcatMeta, videoEncodeMeta, videoMergeMeta, + videoOndemandMeta, videoSubtitleMeta, videoThumbsMeta, vimeoStoreMeta, diff --git a/src/alphalib/types/robots/_instructions-primitives.ts b/src/alphalib/types/robots/_instructions-primitives.ts index f8dec102..3d56f849 100644 --- a/src/alphalib/types/robots/_instructions-primitives.ts +++ b/src/alphalib/types/robots/_instructions-primitives.ts @@ -1,11 +1,8 @@ import type { Replace } from 'type-fest' import { z } from 'zod' -import { stackVersions } from '../stackVersions.js' -import type { assemblyInstructionsSchema } from '../template.js' - -export const interpolationSchemaToYieldNumber = z.string().regex(/^[\d.]*\${.+}[\d.]*$/) -export const interpolationSchemaToYieldString = z.string().regex(/\${.+}/) +import { stackVersions } from '../stackVersions.ts' +import type { assemblyInstructionsSchema } from '../template.ts' export interface RobotMeta { allowed_for_url_transform: boolean @@ -19,6 +16,8 @@ export interface RobotMeta { extended_description?: string has_small_icon?: true minimum_charge: number + minimum_charge_usd?: number | Record + minimum_charge_usd_note?: string ogimage?: string marketing_intro?: string output_factor: number @@ -58,6 +57,7 @@ export interface RobotMeta { | 'verify' | 'remove' | 'write' + | 'stream' purpose_word: string purpose_words: string @@ -93,6 +93,194 @@ export interface RobotMeta { uses_tools?: ('ffmpeg' | 'imagemagick')[] } +export const interpolationSchemaFull = z + .string() + .regex(/^\${.+}$/, 'Must be a full interpolation string') +export const interpolationSchemaPartial = z + .string() + .regex(/\${.+}/, 'Must be a partially interpolatable string') +export const booleanStringSchema = z.enum(['true', 'false']) + +type InterpolatableTuple = Schemas extends readonly [ + infer Head extends z.ZodTypeAny, + ...infer Rest extends z.ZodTypeAny[], +] + ? [InterpolatableSchema, ...InterpolatableTuple] + : Schemas + +type InterpolatableSchema = Schema extends + | z.ZodBoolean + | z.ZodEffects + | z.ZodEnum<[string, ...string[]]> + | z.ZodLiteral + | z.ZodNumber + | z.ZodString + ? z.ZodUnion<[z.ZodString, Schema]> + : Schema extends z.ZodArray + ? z.ZodUnion<[z.ZodString, z.ZodArray, Cardinality>]> + : Schema extends z.ZodDefault + ? z.ZodDefault> + : Schema extends z.ZodNullable + ? z.ZodNullable> + : Schema extends z.ZodOptional + ? z.ZodOptional> + : Schema extends z.ZodRecord + ? z.ZodRecord> + : Schema extends z.ZodTuple + ? z.ZodUnion< + [ + z.ZodString, + z.ZodTuple< + InterpolatableTuple, + Rest extends z.ZodTypeAny ? InterpolatableSchema : null + >, + ] + > + : Schema extends z.ZodObject + ? z.ZodUnion< + [ + z.ZodString, + z.ZodObject< + { [Key in keyof T]: InterpolatableSchema }, + UnknownKeys, + Catchall + >, + ] + > + : Schema extends z.ZodUnion + ? z.ZodUnion<[z.ZodString, ...InterpolatableTuple]> + : Schema + +export function interpolateRecursive( + schema: Schema, +): InterpolatableSchema { + const def = schema._def + + switch (def.typeName) { + case z.ZodFirstPartyTypeKind.ZodBoolean: + return z + .union([interpolationSchemaFull, schema, booleanStringSchema]) + .transform((value) => value === true || value === false) as InterpolatableSchema + case z.ZodFirstPartyTypeKind.ZodArray: { + let replacement = z.array(interpolateRecursive(def.type), def) + + if (def.exactLength != null) { + replacement = replacement.min(def.exactLength.value, def.exactLength.message) + } + + if (def.maxLength != null) { + replacement = replacement.min(def.maxLength.value, def.maxLength.message) + } + + if (def.minLength != null) { + replacement = replacement.min(def.minLength.value, def.minLength.message) + } + + return z.union([interpolationSchemaFull, replacement]) as InterpolatableSchema + } + case z.ZodFirstPartyTypeKind.ZodDefault: + return (interpolateRecursive(def.innerType) as InterpolatableSchema).default( + def.defaultValue(), + ) as InterpolatableSchema + case z.ZodFirstPartyTypeKind.ZodEffects: + case z.ZodFirstPartyTypeKind.ZodEnum: + case z.ZodFirstPartyTypeKind.ZodLiteral: + return z.union([interpolationSchemaFull, schema]) as InterpolatableSchema + case z.ZodFirstPartyTypeKind.ZodNumber: + return z.union([ + z + .string() + .regex(/^\d+(\.\d+)?$/) + .transform((value) => Number(value)), + interpolationSchemaFull, + schema, + ]) as InterpolatableSchema + case z.ZodFirstPartyTypeKind.ZodNullable: + return interpolateRecursive(def.innerType).nullable() as InterpolatableSchema + case z.ZodFirstPartyTypeKind.ZodObject: { + const replacement = z.object( + Object.fromEntries( + Object.entries(def.shape()).map(([key, nested]) => [ + key, + interpolateRecursive(nested as z.ZodFirstPartySchemaTypes), + ]), + ), + def, + ) + return z.union([ + interpolationSchemaFull, + def.unknownKeys === 'strict' + ? replacement.strict() + : def.unknownKeys === 'passthrough' + ? replacement.passthrough() + : replacement, + ]) as InterpolatableSchema + } + case z.ZodFirstPartyTypeKind.ZodOptional: + return z.optional(interpolateRecursive(def.innerType), def) as InterpolatableSchema + case z.ZodFirstPartyTypeKind.ZodRecord: + return z.record( + def.keyType, + interpolateRecursive(def.valueType), + def, + ) as InterpolatableSchema + case z.ZodFirstPartyTypeKind.ZodString: + return z.union([interpolationSchemaPartial, schema]) as InterpolatableSchema + case z.ZodFirstPartyTypeKind.ZodTuple: { + const tuple = z.tuple(def.items.map(interpolateRecursive)) + + return z.union([ + interpolationSchemaFull, + def.rest ? tuple.rest(def.rest) : tuple, + ]) as InterpolatableSchema + } + case z.ZodFirstPartyTypeKind.ZodUnion: + return z.union([ + interpolationSchemaFull, + ...(def.options.map(interpolateRecursive) as z.ZodUnionOptions), + ]) as InterpolatableSchema + default: + return schema as InterpolatableSchema + } +} + +/** + * The robot keys specified in this array can’t be interpolated. + */ +const uninterpolatableKeys = ['robot', 'use'] as const + +type InterpolatableRobot> = + Schema extends z.ZodObject + ? z.ZodObject< + { + [Key in keyof T]: Key extends (typeof uninterpolatableKeys)[number] + ? T[Key] + : InterpolatableSchema + }, + UnknownKeys, + Catchall + > + : never + +export function interpolateRobot>( + schema: Schema, +): InterpolatableRobot { + const def = schema._def + return z + .object( + Object.fromEntries( + Object.entries(def.shape()).map(([key, nested]) => [ + key, + (uninterpolatableKeys as readonly string[]).includes(key) + ? nested + : interpolateRecursive(nested as z.ZodFirstPartySchemaTypes), + ]), + ), + def, + ) + .strict() as InterpolatableRobot +} + /** * Fields that are shared by all Transloadit robots. */ @@ -115,6 +303,13 @@ You can also set this to \`false\` to skip metadata extraction and speed up tran .boolean() .default(false) .describe('Whether the results of this Step should be present in the Assembly Status JSON'), + + queue: z + .enum(['batch']) + .optional() + .describe( + `Setting the queue to 'batch', manually downgrades the priority of jobs for this step to avoid consuming Priority job slots for jobs that don't need zero queue waiting times`, + ), }) .strict() @@ -166,7 +361,8 @@ Specifies which Step(s) to use as input. } \`\`\` -💡That’s likely all you need to know about \`use\`, but you can view [Advanced use cases](/docs/topics/use-parameter/). +> [!Tip] +> That’s likely all you need to know about \`use\`, but you can view [Advanced use cases](/docs/topics/use-parameter/). `, ) .optional(), @@ -243,9 +439,18 @@ export const robotFFmpeg = z.object({ }) .strict() .optional(), - ac: z.union([z.number(), interpolationSchemaToYieldNumber]).optional(), + 'svtav1-params': z + .object({ + tune: z.number().optional(), + 'enable-qm': z.number().optional(), + 'fast-decode': z.number().optional(), + 'film-grain-denoise': z.number().optional(), + }) + .strict() + .optional(), + ac: z.number().optional(), an: z.boolean().optional(), - ar: z.union([z.number(), interpolationSchemaToYieldNumber]).optional(), + ar: z.number().optional(), async: z.number().optional(), b: z .union([ @@ -282,14 +487,14 @@ export const robotFFmpeg = z.object({ movflags: z.string().optional(), partitions: z.string().optional(), pix_fmt: z.string().optional(), - preset: z.string().optional(), + preset: z.union([z.string(), z.number()]).optional(), profile: z.string().optional(), 'q:a': z.number().optional(), qcomp: z.union([z.string(), z.number()]).optional(), qdiff: z.number().optional(), qmax: z.number().optional(), qmin: z.number().optional(), - r: z.union([z.number(), interpolationSchemaToYieldNumber]).optional(), + r: z.number().optional(), rc_eq: z.string().optional(), refs: z.number().optional(), s: z.string().optional(), @@ -310,7 +515,7 @@ A parameter object to be passed to FFmpeg. If a preset is used, the options spec ffmpeg_stack: z // Any semver in range is allowed and normalized. The enum is used for editor completions. - .union([z.enum(['v5', 'v6']), z.string().regex(/^v?[56](\.\d+)?(\.\d+)?$/)]) + .union([z.enum(['v5', 'v6', 'v7']), z.string().regex(/^v?[567](\.\d+)?(\.\d+)?$/)]) .default('v5.0.0').describe(` Selects the FFmpeg stack version to use for encoding. These versions reflect real FFmpeg versions. We currently recommend to use "v6.0.0". `), @@ -569,6 +774,15 @@ export const page_number = z.number().int().default(1) export const recursive = z.boolean().default(false) +export const return_file_stubs = z + .boolean() + .describe( + ` +If set to \`true\`, the Robot will not yet import the actual files but instead return an empty file stub that includes a URL from where the file can be imported by subsequent Robots. This is useful for cases where subsequent Steps need more control over the import process, such as with 🤖/video/ondemand. This parameter should only be set if all subsequent Steps use Robots that support file stubs. +`, + ) + .default(false) + export const port = z.number().int().min(1).max(65535) // TODO: Use an enum. @@ -592,9 +806,7 @@ export const positionSchema = z.enum([ export const percentageSchema = z.string().regex(/^\d+%$/) -export const color_with_alpha = z.string().regex(/^#?[0-9a-fA-F]{8}$/) - -export const color_with_optional_alpha = z.string().regex(/^#?[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/) +export const color_with_alpha = z.string().regex(/^#?[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$/) export const color_without_alpha = z.string().regex(/^#?[0-9a-fA-F]{6}$/) @@ -669,6 +881,13 @@ export const granularitySchema = z.enum(['full', 'list']).default('full') export type RobotImport = z.infer export const robotImport = z .object({ + force_name: z + .union([z.string(), z.array(z.string())]) + .nullable() + .default(null) + .describe( + 'Custom name for the imported file(s). By default file names are derived from the source.', + ), ignore_errors: z .union([z.boolean(), z.array(z.enum(['meta', 'import']))]) .transform((value) => (value === true ? ['meta', 'import'] : value === false ? [] : value)) @@ -905,49 +1124,53 @@ export const filterExpression = z.union([ ]) export type FilterCondition = z.infer -export const filterCondition = z - .array( - z.union([ - z.tuple([ - filterExpression, - z.union([ - z.literal('==').describe('Equals without type check'), - z.literal('===').describe('Strict equals with type check'), - z.literal('<').describe('Less than'), - z.literal('>').describe('Greater than'), - z.literal('<=').describe('Less or equal'), - z.literal('>=').describe('Greater or equal'), - z.literal('!=').describe('Simple inequality check without type check'), - z.literal('!==').describe('Strict inequality check with type check'), - z - .literal('regex') - .describe( - 'Case-insensitive regular expression based on [RE2](https://github.com/google/re2) `.match()`', - ), - z - .literal('!regex') - .describe( - 'Case-insensitive regular expression based on [RE2](https://github.com/google/re2) `!.match()`', - ), - z - .literal('includes') - .describe( - 'Check if the right element is included in the array, which is represented by the left element', - ), - z - .literal('empty') - .describe( - 'Check if the left element is an empty array, an object without properties, an empty string, the number zero or the boolean false. Leave the third element of the array to be an empty string. It won’t be evaluated.', - ), - z - .literal('!empty') - .describe( - 'Check if the left element is an array with members, an object with at least one property, a non-empty string, a number that does not equal zero or the boolean true. Leave the third element of the array to be an empty string. It won’t be evaluated.', - ), - ]), - filterExpression, +export const filterCondition = z.union([ + z.string(), + z.array( + z.tuple([ + filterExpression, + z.union([ + z.literal('=').describe('Equals without type check'), + z.literal('==').describe('Equals without type check'), + z.literal('===').describe('Strict equals with type check'), + z.literal('<').describe('Less than'), + z.literal('>').describe('Greater than'), + z.literal('<=').describe('Less or equal'), + z.literal('>=').describe('Greater or equal'), + z.literal('!=').describe('Simple inequality check without type check'), + z.literal('!==').describe('Strict inequality check with type check'), + z + .literal('regex') + .describe( + 'Case-insensitive regular expression based on [RE2](https://github.com/google/re2) `.match()`', + ), + z + .literal('!regex') + .describe( + 'Case-insensitive regular expression based on [RE2](https://github.com/google/re2) `!.match()`', + ), + z + .literal('includes') + .describe( + 'Check if the right element is included in the array, which is represented by the left element', + ), + z + .literal('!includes') + .describe( + 'Check if the right element is not included in the array, which is represented by the left element', + ), + z + .literal('empty') + .describe( + 'Check if the left element is an empty array, an object without properties, an empty string, the number zero or the boolean false. Leave the third element of the array to be an empty string. It won’t be evaluated.', + ), + z + .literal('!empty') + .describe( + 'Check if the left element is an array with members, an object with at least one property, a non-empty string, a number that does not equal zero or the boolean true. Leave the third element of the array to be an empty string. It won’t be evaluated.', + ), ]), - z.string(), + filterExpression, ]), - ) - .default([]) + ), +]) diff --git a/src/alphalib/types/robots/audio-artwork.ts b/src/alphalib/types/robots/audio-artwork.ts index 918aabf7..bc698fda 100644 --- a/src/alphalib/types/robots/audio-artwork.ts +++ b/src/alphalib/types/robots/audio-artwork.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { robotFFmpegAudio, robotBase, robotUse } from './_instructions-primitives.ts' +import { + robotFFmpegAudio, + robotBase, + robotUse, + interpolateRobot, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -55,3 +60,10 @@ Whether the original file should be transcoded into a new format if there is an }) .strict() export type RobotAudioArtworkInstructions = z.infer + +export const interpolatableRobotAudioArtworkInstructionsSchema = interpolateRobot( + robotAudioArtworkInstructionsSchema, +) +export type InterpolatableRobotAudioArtworkInstructions = z.input< + typeof interpolatableRobotAudioArtworkInstructionsSchema +> diff --git a/src/alphalib/types/robots/audio-concat.ts b/src/alphalib/types/robots/audio-concat.ts index d9f421be..094c3846 100644 --- a/src/alphalib/types/robots/audio-concat.ts +++ b/src/alphalib/types/robots/audio-concat.ts @@ -6,6 +6,7 @@ import { robotUse, sampleRateSchema, robotFFmpegAudio, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -72,3 +73,10 @@ This parameter does not add an audio fade effect at the beginning or end of your }) .strict() export type RobotAudioConcatInstructions = z.infer + +export const interpolatableRobotAudioConcatInstructionsSchema = interpolateRobot( + robotAudioConcatInstructionsSchema, +) +export type InterpolatableRobotAudioConcatInstructions = z.input< + typeof interpolatableRobotAudioConcatInstructionsSchema +> diff --git a/src/alphalib/types/robots/audio-encode.ts b/src/alphalib/types/robots/audio-encode.ts index b3f46377..d4015782 100644 --- a/src/alphalib/types/robots/audio-encode.ts +++ b/src/alphalib/types/robots/audio-encode.ts @@ -6,6 +6,7 @@ import { robotBase, robotUse, sampleRateSchema, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -61,3 +62,10 @@ Sample rate of the resulting audio file, in Hertz. If not specified will default }) .strict() export type RobotAudioEncodeInstructions = z.infer + +export const interpolatableRobotAudioEncodeInstructionsSchema = interpolateRobot( + robotAudioEncodeInstructionsSchema, +) +export type InterpolatableRobotAudioEncodeInstructions = z.input< + typeof interpolatableRobotAudioEncodeInstructionsSchema +> diff --git a/src/alphalib/types/robots/audio-loop.ts b/src/alphalib/types/robots/audio-loop.ts index e693e7e4..2043d9ee 100644 --- a/src/alphalib/types/robots/audio-loop.ts +++ b/src/alphalib/types/robots/audio-loop.ts @@ -6,6 +6,7 @@ import { robotBase, robotUse, sampleRateSchema, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -60,3 +61,10 @@ Target duration for the whole process in seconds. The Robot will loop }) .strict() export type RobotAudioLoopInstructions = z.infer + +export const interpolatableRobotAudioLoopInstructionsSchema = interpolateRobot( + robotAudioLoopInstructionsSchema, +) +export type InterpolatableRobotAudioLoopInstructions = z.input< + typeof interpolatableRobotAudioLoopInstructionsSchema +> diff --git a/src/alphalib/types/robots/audio-merge.ts b/src/alphalib/types/robots/audio-merge.ts index 2f03843b..73e3c155 100644 --- a/src/alphalib/types/robots/audio-merge.ts +++ b/src/alphalib/types/robots/audio-merge.ts @@ -6,6 +6,7 @@ import { robotBase, robotUse, sampleRateSchema, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -71,3 +72,10 @@ Valid values are \`"average"\` and \`"sum"\` here. \`"average"\` means each inpu }) .strict() export type RobotAudioMergeInstructions = z.infer + +export const interpolatableRobotAudioMergeInstructionsSchema = interpolateRobot( + robotAudioMergeInstructionsSchema, +) +export type InterpolatableRobotAudioMergeInstructions = z.input< + typeof interpolatableRobotAudioMergeInstructionsSchema +> diff --git a/src/alphalib/types/robots/audio-waveform.ts b/src/alphalib/types/robots/audio-waveform.ts index dee70174..29507e6f 100644 --- a/src/alphalib/types/robots/audio-waveform.ts +++ b/src/alphalib/types/robots/audio-waveform.ts @@ -1,6 +1,12 @@ import { z } from 'zod' -import { color_with_alpha, robotFFmpeg, robotBase, robotUse } from './_instructions-primitives.ts' +import { + color_with_alpha, + robotFFmpeg, + robotBase, + robotUse, + interpolateRobot, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -81,3 +87,10 @@ The color used in the outer parts of the gradient. The format is "rrggbbaa" (red }) .strict() export type RobotAudioWaveformInstructions = z.infer + +export const interpolatableRobotAudioWaveformInstructionsSchema = interpolateRobot( + robotAudioWaveformInstructionsSchema, +) +export type InterpolatableRobotAudioWaveformInstructions = z.input< + typeof interpolatableRobotAudioWaveformInstructionsSchema +> diff --git a/src/alphalib/types/robots/azure-import.ts b/src/alphalib/types/robots/azure-import.ts index c2d918df..61238cc6 100644 --- a/src/alphalib/types/robots/azure-import.ts +++ b/src/alphalib/types/robots/azure-import.ts @@ -7,6 +7,7 @@ import { next_page_token, path, robotBase, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -67,3 +68,10 @@ The pagination page size. This only works when recursive is \`true\` for now, in export type RobotAzureImportInstructions = z.infer export type RobotAzureImportInstructionsInput = z.input + +export const interpolatableRobotAzureImportInstructionsSchema = interpolateRobot( + robotAzureImportInstructionsSchema, +) +export type InterpolatableRobotAzureImportInstructions = z.input< + typeof interpolatableRobotAzureImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/azure-store.ts b/src/alphalib/types/robots/azure-store.ts index e1de0a9b..7e5ef77d 100644 --- a/src/alphalib/types/robots/azure-store.ts +++ b/src/alphalib/types/robots/azure-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { azureBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { azureBase, interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -50,6 +50,9 @@ The content encoding with which to store the file. By default this will be guess `), content_language: z.string().optional().describe(` The content language with which to store the file. By default this will be guessed by Azure. +`), + content_disposition: z.string().optional().describe(` +The content disposition with which to store the file. By default this will be guessed by Azure. `), cache_control: z.string().optional().describe(` The cache control header with which to store the file. @@ -74,3 +77,10 @@ Set this to a combination of \`r\` (read), \`w\` (write) and \`d\` (delete) for export type RobotAzureStoreInstructions = z.infer export type RobotAzureStoreInstructionsInput = z.input + +export const interpolatableRobotAzureStoreInstructionsSchema = interpolateRobot( + robotAzureStoreInstructionsSchema, +) +export type InterpolatableRobotAzureStoreInstructions = z.input< + typeof interpolatableRobotAzureStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/backblaze-import.ts b/src/alphalib/types/robots/backblaze-import.ts index 1195b94a..922675c6 100644 --- a/src/alphalib/types/robots/backblaze-import.ts +++ b/src/alphalib/types/robots/backblaze-import.ts @@ -7,6 +7,7 @@ import { path, recursive, robotBase, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -79,3 +80,10 @@ export type RobotBackblazeImportInstructions = z.infer< export type RobotBackblazeImportInstructionsInput = z.input< typeof robotBackblazeImportInstructionsSchema > + +export const interpolatableRobotBackblazeImportInstructionsSchema = interpolateRobot( + robotBackblazeImportInstructionsSchema, +) +export type InterpolatableRobotBackblazeImportInstructions = z.input< + typeof interpolatableRobotBackblazeImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/backblaze-store.ts b/src/alphalib/types/robots/backblaze-store.ts index ae2bbb46..e2bfa245 100644 --- a/src/alphalib/types/robots/backblaze-store.ts +++ b/src/alphalib/types/robots/backblaze-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { backblazeBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { backblazeBase, interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -61,3 +61,10 @@ export type RobotBackblazeStoreInstructions = z.infer + +export const interpolatableRobotBackblazeStoreInstructionsSchema = interpolateRobot( + robotBackblazeStoreInstructionsSchema, +) +export type InterpolatableRobotBackblazeStoreInstructions = z.input< + typeof interpolatableRobotBackblazeStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/cloudfiles-import.ts b/src/alphalib/types/robots/cloudfiles-import.ts index a5ba44ba..fa852011 100644 --- a/src/alphalib/types/robots/cloudfiles-import.ts +++ b/src/alphalib/types/robots/cloudfiles-import.ts @@ -7,6 +7,7 @@ import { page_number, path, robotBase, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -78,3 +79,10 @@ export type RobotCloudfilesImportInstructions = z.infer< export type RobotCloudfilesImportInstructionsInput = z.input< typeof robotCloudfilesImportInstructionsSchema > + +export const interpolatableRobotCloudfilesImportInstructionsSchema = interpolateRobot( + robotCloudfilesImportInstructionsSchema, +) +export type InterpolatableRobotCloudfilesImportInstructions = z.input< + typeof interpolatableRobotCloudfilesImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/cloudfiles-store.ts b/src/alphalib/types/robots/cloudfiles-store.ts index 97e901ae..db860486 100644 --- a/src/alphalib/types/robots/cloudfiles-store.ts +++ b/src/alphalib/types/robots/cloudfiles-store.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { cloudfilesBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { + cloudfilesBase, + interpolateRobot, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -61,3 +66,10 @@ export type RobotCloudfilesStoreInstructions = z.infer< export type RobotCloudfilesStoreInstructionsInput = z.input< typeof robotCloudfilesStoreInstructionsSchema > + +export const interpolatableRobotCloudfilesStoreInstructionsSchema = interpolateRobot( + robotCloudfilesStoreInstructionsSchema, +) +export type InterpolatableRobotCloudfilesStoreInstructions = z.input< + typeof interpolatableRobotCloudfilesStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/cloudflare-import.ts b/src/alphalib/types/robots/cloudflare-import.ts index 37366f3b..b4c9606d 100644 --- a/src/alphalib/types/robots/cloudflare-import.ts +++ b/src/alphalib/types/robots/cloudflare-import.ts @@ -3,11 +3,13 @@ import { z } from 'zod' import { cloudflareBase, files_per_page, + interpolateRobot, robotImport, page_number, path, recursive, robotBase, + return_file_stubs, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -73,6 +75,7 @@ When doing big imports, make sure no files are added or removed from other scrip files_per_page: files_per_page.describe(` The pagination page size. This only works when recursive is \`true\` for now, in order to not break backwards compatibility in non-recursive imports. `), + return_file_stubs, }) .strict() @@ -82,3 +85,10 @@ export type RobotCloudflareImportInstructions = z.infer< export type RobotCloudflareImportInstructionsInput = z.input< typeof robotCloudflareImportInstructionsSchema > + +export const interpolatableRobotCloudflareImportInstructionsSchema = interpolateRobot( + robotCloudflareImportInstructionsSchema, +) +export type InterpolatableRobotCloudflareImportInstructions = z.input< + typeof interpolatableRobotCloudflareImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/cloudflare-store.ts b/src/alphalib/types/robots/cloudflare-store.ts index cb7ea3c4..09f0acdd 100644 --- a/src/alphalib/types/robots/cloudflare-store.ts +++ b/src/alphalib/types/robots/cloudflare-store.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { cloudflareBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { + cloudflareBase, + interpolateRobot, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -62,3 +67,10 @@ export type RobotCloudflareStoreInstructions = z.infer< export type RobotCloudflareStoreInstructionsInput = z.input< typeof robotCloudflareStoreInstructionsSchema > + +export const interpolatableRobotCloudflareStoreInstructionsSchema = interpolateRobot( + robotCloudflareStoreInstructionsSchema, +) +export type InterpolatableRobotCloudflareStoreInstructions = z.input< + typeof interpolatableRobotCloudflareStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/digitalocean-import.ts b/src/alphalib/types/robots/digitalocean-import.ts index daf08c3f..50802d65 100644 --- a/src/alphalib/types/robots/digitalocean-import.ts +++ b/src/alphalib/types/robots/digitalocean-import.ts @@ -3,11 +3,13 @@ import { z } from 'zod' import { digitalOceanBase, files_per_page, + interpolateRobot, robotImport, page_number, path, recursive, robotBase, + return_file_stubs, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -70,6 +72,7 @@ When doing big imports, make sure no files are added or removed from other scrip files_per_page: files_per_page.describe(` The pagination page size. This only works when recursive is \`true\` for now, in order to not break backwards compatibility in non-recursive imports. `), + return_file_stubs, }) .strict() @@ -79,3 +82,10 @@ export type RobotDigitaloceanImportInstructions = z.infer< export type RobotDigitaloceanImportInstructionsInput = z.input< typeof robotDigitaloceanImportInstructionsSchema > + +export const interpolatableRobotDigitaloceanImportInstructionsSchema = interpolateRobot( + robotDigitaloceanImportInstructionsSchema, +) +export type InterpolatableRobotDigitaloceanImportInstructions = z.input< + typeof interpolatableRobotDigitaloceanImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/digitalocean-store.ts b/src/alphalib/types/robots/digitalocean-store.ts index dcfbf3a9..94331439 100644 --- a/src/alphalib/types/robots/digitalocean-store.ts +++ b/src/alphalib/types/robots/digitalocean-store.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { digitalOceanBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { + digitalOceanBase, + interpolateRobot, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -67,3 +72,10 @@ export type RobotDigitaloceanStoreInstructions = z.infer< export type RobotDigitaloceanStoreInstructionsInput = z.input< typeof robotDigitaloceanStoreInstructionsSchema > + +export const interpolatableRobotDigitaloceanStoreInstructionsSchema = interpolateRobot( + robotDigitaloceanStoreInstructionsSchema, +) +export type InterpolatableRobotDigitaloceanStoreInstructions = z.input< + typeof interpolatableRobotDigitaloceanStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/document-autorotate.ts b/src/alphalib/types/robots/document-autorotate.ts index 6bdb28a4..8f6aba12 100644 --- a/src/alphalib/types/robots/document-autorotate.ts +++ b/src/alphalib/types/robots/document-autorotate.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import type { RobotMeta } from './_instructions-primitives.ts' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' export const meta: RobotMeta = { allowed_for_url_transform: true, @@ -34,3 +34,10 @@ export const robotDocumentAutorotateInstructionsSchema = robotBase export type RobotDocumentAutorotateInstructions = z.infer< typeof robotDocumentAutorotateInstructionsSchema > + +export const interpolatableRobotDocumentAutorotateInstructionsSchema = interpolateRobot( + robotDocumentAutorotateInstructionsSchema, +) +export type InterpolatableRobotDocumentAutorotateInstructions = z.input< + typeof interpolatableRobotDocumentAutorotateInstructionsSchema +> diff --git a/src/alphalib/types/robots/document-convert.ts b/src/alphalib/types/robots/document-convert.ts index a4c0b939..4306d60a 100644 --- a/src/alphalib/types/robots/document-convert.ts +++ b/src/alphalib/types/robots/document-convert.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -19,13 +19,15 @@ export const meta: RobotMeta = { }, example_code_description: 'Convert uploaded files to PDF documents:', extended_description: ` -**Note:** This Robot can convert files to PDF, but cannot convert PDFs to different formats. If you want to convert PDFs to say, JPEG or TIFF, use [🤖/image/resize](/docs/transcoding/image-manipulation/image-resize/). If you want to turn them into text files or recognize (OCR) them to make them searchable, reach out, as we have a new Robot in the works for this. [{.alert .alert-note}] +> [!Note] +> This Robot can convert files to PDF, but cannot convert PDFs to different formats. If you want to convert PDFs to say, JPEG or TIFF, use [🤖/image/resize](/docs/transcoding/image-manipulation/image-resize/). If you want to turn them into text files or recognize (OCR) them to make them searchable, reach out, as we have a new Robot in the works for this. Sometimes, a certain file type might not support what you are trying to accomplish. Perhaps your company is trying to automate document formatting, but it only works with docx, so all your docs need to be converted. Or maybe your stored jpg files are taking up too much space and you want a lighter format. Whatever the case, we have you covered. Using this Robot, you can bypass the issues that certain file types may bring, by converting your file into the most suitable format. This also works in conjunction with our other Robots, allowing for even greater versatility when using our services. -**Warning:** A general rule of this Robot is that converting files into an alien format category will result in an error. For example, SRT files can be converted into the VTT format, but not to an image. [{.alert .alert-warning}] +> ![Warning] +> A general rule of this Robot is that converting files into an alien format category will result in an error. For example, SRT files can be converted into the VTT format, but not to an image. The following file formats can be converted from: @@ -184,3 +186,10 @@ To change the formatting of the HTML element, the \`font-size\` must be specifie export type RobotDocumentConvertInstructions = z.infer< typeof robotDocumentConvertInstructionsSchema > + +export const interpolatableRobotDocumentConvertInstructionsSchema = interpolateRobot( + robotDocumentConvertInstructionsSchema, +) +export type InterpolatableRobotDocumentConvertInstructions = z.input< + typeof interpolatableRobotDocumentConvertInstructionsSchema +> diff --git a/src/alphalib/types/robots/document-merge.ts b/src/alphalib/types/robots/document-merge.ts index 6444298c..6ffe5702 100644 --- a/src/alphalib/types/robots/document-merge.ts +++ b/src/alphalib/types/robots/document-merge.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -21,7 +21,8 @@ export const meta: RobotMeta = { }, example_code_description: 'Merge all uploaded PDF documents into one:', extended_description: ` -**Note:** This Robot can merge PDF files only at the moment. [{.alert .alert-note}] +> ![Note] +> This Robot can merge PDF files only at the moment. Input files are sorted alphanumerically unless you provide the as-syntax in the "use" parameter. For example: @@ -66,3 +67,10 @@ If not empty, encrypts the output file and makes it accessible only by typing in .strict() export type RobotDocumentMergeInstructions = z.infer + +export const interpolatableRobotDocumentMergeInstructionsSchema = interpolateRobot( + robotDocumentMergeInstructionsSchema, +) +export type InterpolatableRobotDocumentMergeInstructions = z.input< + typeof interpolatableRobotDocumentMergeInstructionsSchema +> diff --git a/src/alphalib/types/robots/document-ocr.ts b/src/alphalib/types/robots/document-ocr.ts index 8588b171..e1b6431a 100644 --- a/src/alphalib/types/robots/document-ocr.ts +++ b/src/alphalib/types/robots/document-ocr.ts @@ -3,6 +3,7 @@ import { z } from 'zod' import { aiProviderSchema, granularitySchema, + interpolateRobot, robotBase, robotUse, } from './_instructions-primitives.ts' @@ -24,9 +25,11 @@ export const meta: RobotMeta = { }, example_code_description: 'Recognize text in an uploaded document and save it to a JSON file:', extended_description: ` -**Warning:** Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input PDFs. Avoid relying on exact responses in your tests and application. [{.alert .alert-warning}] +> [!Warning] +> Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input PDFs. Avoid relying on exact responses in your tests and application. -**Note:** Currently, this Robot only supports character recognition for PDFs. To use this Robot with other document formats, use [/document/convert](/docs/transcoding/document-processing/document-convert/) first to convert the document into a PDF. [{.alert .alert-note}] +> [!Note] +> Currently, this Robot only supports character recognition for PDFs. To use this Robot with other document formats, use [/document/convert](/docs/transcoding/document-processing/document-convert/) first to convert the document into a PDF. `, minimum_charge: 1048576, output_factor: 1, @@ -70,3 +73,10 @@ In what format to return the extracted text. .strict() export type RobotDocumentOcrInstructions = z.infer + +export const interpolatableRobotDocumentOcrInstructionsSchema = interpolateRobot( + robotDocumentOcrInstructionsSchema, +) +export type InterpolatableRobotDocumentOcrInstructions = z.input< + typeof interpolatableRobotDocumentOcrInstructionsSchema +> diff --git a/src/alphalib/types/robots/document-split.ts b/src/alphalib/types/robots/document-split.ts index 2f32807e..7307c0c0 100644 --- a/src/alphalib/types/robots/document-split.ts +++ b/src/alphalib/types/robots/document-split.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import type { RobotMeta } from './_instructions-primitives.ts' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' export const meta: RobotMeta = { allowed_for_url_transform: true, @@ -36,3 +36,10 @@ export const robotDocumentSplitInstructionsSchema = robotBase .strict() export type RobotDocumentSplitInstructions = z.infer + +export const interpolatableRobotDocumentSplitInstructionsSchema = interpolateRobot( + robotDocumentSplitInstructionsSchema, +) +export type InterpolatableRobotDocumentSplitInstructions = z.input< + typeof interpolatableRobotDocumentSplitInstructionsSchema +> diff --git a/src/alphalib/types/robots/document-thumbs.ts b/src/alphalib/types/robots/document-thumbs.ts index c0812f15..f834b101 100644 --- a/src/alphalib/types/robots/document-thumbs.ts +++ b/src/alphalib/types/robots/document-thumbs.ts @@ -5,6 +5,7 @@ import { robotImagemagick, robotBase, robotUse, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -117,3 +118,10 @@ Some PDF documents lie about their dimensions. For instance they'll say they are .strict() export type RobotDocumentThumbsInstructions = z.infer + +export const interpolatableRobotDocumentThumbsInstructionsSchema = interpolateRobot( + robotDocumentThumbsInstructionsSchema, +) +export type InterpolatableRobotDocumentThumbsInstructions = z.input< + typeof interpolatableRobotDocumentThumbsInstructionsSchema +> diff --git a/src/alphalib/types/robots/dropbox-import.ts b/src/alphalib/types/robots/dropbox-import.ts index 511878db..ad5d465a 100644 --- a/src/alphalib/types/robots/dropbox-import.ts +++ b/src/alphalib/types/robots/dropbox-import.ts @@ -1,6 +1,12 @@ import { z } from 'zod' -import { digitalOceanBase, robotImport, path, robotBase } from './_instructions-primitives.ts' +import { + digitalOceanBase, + robotImport, + path, + robotBase, + interpolateRobot, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -56,3 +62,10 @@ export type RobotDropboxImportInstructions = z.infer + +export const interpolatableRobotDropboxImportInstructionsSchema = interpolateRobot( + robotDropboxImportInstructionsSchema, +) +export type InterpolatableRobotDropboxImportInstructions = z.input< + typeof interpolatableRobotDropboxImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/dropbox-store.ts b/src/alphalib/types/robots/dropbox-store.ts index 12442647..2414436f 100644 --- a/src/alphalib/types/robots/dropbox-store.ts +++ b/src/alphalib/types/robots/dropbox-store.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { digitalOceanBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { + digitalOceanBase, + interpolateRobot, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -50,3 +55,10 @@ Whether to create a URL to this file for sharing with other people. This will ov export type RobotDropboxStoreInstructions = z.infer export type RobotDropboxStoreInstructionsInput = z.input + +export const interpolatableRobotDropboxStoreInstructionsSchema = interpolateRobot( + robotDropboxStoreInstructionsSchema, +) +export type InterpolatableRobotDropboxStoreInstructions = z.input< + typeof interpolatableRobotDropboxStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/edgly-deliver.ts b/src/alphalib/types/robots/edgly-deliver.ts index b1424c9c..7eb982cf 100644 --- a/src/alphalib/types/robots/edgly-deliver.ts +++ b/src/alphalib/types/robots/edgly-deliver.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, type RobotMeta } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, type RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { allowed_for_url_transform: false, @@ -30,3 +30,10 @@ When you want Transloadit to tranform files on the fly, this Robot ca .strict() export type RobotEdglyDeliverInstructions = z.infer + +export const interpolatableRobotEdglyDeliverInstructionsSchema = interpolateRobot( + robotEdglyDeliverInstructionsSchema, +) +export type InterpolatableRobotEdglyDeliverInstructions = z.input< + typeof interpolatableRobotEdglyDeliverInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-compress.ts b/src/alphalib/types/robots/file-compress.ts index a887215b..1b738841 100644 --- a/src/alphalib/types/robots/file-compress.ts +++ b/src/alphalib/types/robots/file-compress.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -102,3 +102,10 @@ Files with same names are numbered in the \`"simple"\` file layout to avoid nami .strict() export type RobotFileCompressInstructions = z.infer + +export const interpolatableRobotFileCompressInstructionsSchema = interpolateRobot( + robotFileCompressInstructionsSchema, +) +export type InterpolatableRobotFileCompressInstructions = z.input< + typeof interpolatableRobotFileCompressInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-decompress.ts b/src/alphalib/types/robots/file-decompress.ts index 6c11242e..3eedc70a 100644 --- a/src/alphalib/types/robots/file-decompress.ts +++ b/src/alphalib/types/robots/file-decompress.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -84,3 +84,10 @@ To keep backwards compatibility, setting this parameter to \`true\` will set it .strict() export type RobotFileDecompressInstructions = z.infer + +export const interpolatableRobotFileDecompressInstructionsSchema = interpolateRobot( + robotFileDecompressInstructionsSchema, +) +export type InterpolatableRobotFileDecompressInstructions = z.input< + typeof interpolatableRobotFileDecompressInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-filter.ts b/src/alphalib/types/robots/file-filter.ts index 7d093b7d..31e064ff 100644 --- a/src/alphalib/types/robots/file-filter.ts +++ b/src/alphalib/types/robots/file-filter.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { filterCondition, robotBase, robotUse } from './_instructions-primitives.ts' +import { + filterCondition, + interpolateRobot, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -62,7 +67,8 @@ Examples: - \`[["720", ">=", "\${file.size}"]]\` - \`[["\${file.mime}", "regex", "image"]]\` -**Warning:** If you would like to match against a \`null\` value or a value that is not present (like an audio file does not have a \`video_codec\` property in its metadata), match against \`""\` (an empty string) instead. We’ll support proper matching against \`null\` in the future, but we cannot easily do so right now without breaking backwards compatibility. [{.alert .alert-warning}] +> [!Warning] +> If you would like to match against a \`null\` value or a value that is not present (like an audio file does not have a \`video_codec\` property in its metadata), match against \`""\` (an empty string) instead. We’ll support proper matching against \`null\` in the future, but we cannot easily do so right now without breaking backwards compatibility. ### Conditions as JavaScript @@ -77,20 +83,32 @@ Examples: As indicated, we charge for this via [🤖/script/run](/docs/transcoding/code-evaluation/script-run/). See also [Dynamic Evaluation](/docs/topics/dynamic-evaluation/) for more details on allowed syntax and behavior. `), - accepts: filterCondition.describe(` -Files that match at least one requirement will be accepted, or declined otherwise. If the array is empty, all files will be accepted. Example: + accepts: filterCondition + .describe( + ` +Files that match at least one requirement will be accepted, or declined otherwise. If the value is \`null\`, all files will be accepted. If the array is empty, no files will be accepted. Example: \`[["\${file.mime}", "==", "image/gif"]]\`. If the \`condition_type\` parameter is set to \`"and"\`, then all requirements must match for the file to be accepted. -`), - declines: filterCondition.describe(` -Files that match at least one requirement will be declined, or accepted otherwise. Example: + +If \`accepts\` and \`declines\` are both provided, the requirements in \`accepts\` will be evaluated first, before the conditions in \`declines\`. +`, + ) + .optional(), + declines: filterCondition + .describe( + ` +Files that match at least one requirement will be declined, or accepted otherwise. If the value is \`null\` or an empty array, no files will be declined. Example: \`[["\${file.size}",">","1024"]]\`. If the \`condition_type\` parameter is set to \`"and"\`, then all requirements must match for the file to be declined. -`), + +If \`accepts\` and \`declines\` are both provided, the requirements in \`accepts\` will be evaluated first, before the conditions in \`declines\`. +`, + ) + .optional(), condition_type: z.enum(['and', 'or']).default('or').describe(` Specifies the condition type according to which the members of the \`accepts\` or \`declines\` arrays should be evaluated. Can be \`"or"\` or \`"and"\`. `), @@ -105,3 +123,10 @@ The error message shown to your users (such as by Uppy) when a file is declined export type RobotFileFilterInstructions = z.infer export type RobotFileFilterInstructionsInput = z.input + +export const interpolatableRobotFileFilterInstructionsSchema = interpolateRobot( + robotFileFilterInstructionsSchema, +) +export type InterpolatableRobotFileFilterInstructions = z.input< + typeof interpolatableRobotFileFilterInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-hash.ts b/src/alphalib/types/robots/file-hash.ts index 7d104358..aeef0a40 100644 --- a/src/alphalib/types/robots/file-hash.ts +++ b/src/alphalib/types/robots/file-hash.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -49,3 +49,10 @@ The file hash is exported as \`file.meta.hash\`. .strict() export type RobotFileHashInstructions = z.infer + +export const interpolatableRobotFileHashInstructionsSchema = interpolateRobot( + robotFileHashInstructionsSchema, +) +export type InterpolatableRobotFileHashInstructions = z.input< + typeof interpolatableRobotFileHashInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-preview.ts b/src/alphalib/types/robots/file-preview.ts index a0400128..d74277f0 100644 --- a/src/alphalib/types/robots/file-preview.ts +++ b/src/alphalib/types/robots/file-preview.ts @@ -1,10 +1,10 @@ import { z } from 'zod' import { - color_with_optional_alpha, + color_with_alpha, complexHeightSchema, complexWidthSchema, - interpolationSchemaToYieldNumber, + interpolateRobot, optimize_priority, resize_strategy, robotBase, @@ -45,7 +45,7 @@ export const meta: RobotMeta = { typical_file_type: 'file', } -export const robotFilePreviewInstructionsInterpolatedSchema = robotBase +export const robotFilePreviewInstructionsSchema = robotBase .merge(robotUse) .extend({ robot: z.literal('/file/preview').describe(` @@ -69,7 +69,7 @@ To achieve the desired dimensions of the preview thumbnail, the Robot See the list of available [resize strategies](/docs/transcoding/image-manipulation/image-resize/#resize-strategies) for more details. `), - background: color_with_optional_alpha.default('#ffffffff').describe(` + background: color_with_alpha.default('#ffffffff').describe(` The hexadecimal code of the color used to fill the background (only used for the pad resize strategy). The format is \`#rrggbb[aa]\` (red, green, blue, alpha). Use \`#00000000\` for a transparent padding. `), strategy: z @@ -101,10 +101,10 @@ The parameter defaults to the following definition: } \`\`\` `), - waveform_center_color: color_with_optional_alpha.default('#000000ff').describe(` + waveform_center_color: color_with_alpha.default('#000000ff').describe(` The color used in the center of the waveform's gradient. The format is \`#rrggbb[aa]\` (red, green, blue, alpha). Only used if the \`waveform\` strategy for audio files is applied. `), - waveform_outer_color: color_with_optional_alpha.default('#000000ff').describe(` + waveform_outer_color: color_with_alpha.default('#000000ff').describe(` The color used in the outer parts of the waveform's gradient. The format is \`#rrggbb[aa]\` (red, green, blue, alpha). Only used if the \`waveform\` strategy for audio files is applied. `), waveform_height: z.number().int().min(1).max(5000).default(100).describe(` @@ -121,7 +121,7 @@ The style of the icon generated if the \`icon\` strategy is applied. The default

\`square\` style:
![Image with square style]({{site.asset_cdn}}/assets/images/file-preview/icon-square.png) `), - icon_text_color: color_with_optional_alpha.default('#a2a2a2').describe(` + icon_text_color: color_with_alpha.default('#a2a2a2').describe(` The color of the text used in the icon. The format is \`#rrggbb[aa]\`. Only used if the \`icon\` strategy is applied. `), // TODO: Determine the font enum. @@ -160,15 +160,12 @@ Specifies whether the generated animated image should loop forever (\`true\`) or }) .strict() -export const robotFilePreviewInstructionsSchema = - robotFilePreviewInstructionsInterpolatedSchema.extend({ - width: robotFilePreviewInstructionsInterpolatedSchema.shape.width.or( - interpolationSchemaToYieldNumber, - ), - height: robotFilePreviewInstructionsInterpolatedSchema.shape.height.or( - interpolationSchemaToYieldNumber, - ), - }) - export type RobotFilePreviewInstructions = z.infer export type RobotFilePreviewInstructionsInput = z.input + +export const interpolatableRobotFilePreviewInstructionsSchema = interpolateRobot( + robotFilePreviewInstructionsSchema, +) +export type InterpolatableRobotFilePreviewInstructions = z.input< + typeof interpolatableRobotFilePreviewInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-read.ts b/src/alphalib/types/robots/file-read.ts index 2f184661..5329f3c3 100644 --- a/src/alphalib/types/robots/file-read.ts +++ b/src/alphalib/types/robots/file-read.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -34,3 +34,10 @@ The Robot currently only accepts files under 500KB. .strict() export type RobotFileReadInstructions = z.infer + +export const interpolatableRobotFileReadInstructionsSchema = interpolateRobot( + robotFileReadInstructionsSchema, +) +export type InterpolatableRobotFileReadInstructions = z.input< + typeof interpolatableRobotFileReadInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-serve.ts b/src/alphalib/types/robots/file-serve.ts index 36cc2b38..91b480c3 100644 --- a/src/alphalib/types/robots/file-serve.ts +++ b/src/alphalib/types/robots/file-serve.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -58,3 +58,10 @@ An object containing a list of headers to be set for a file as we serve it to a export type RobotFileServeInstructions = z.infer export type RobotFileServeInstructionsInput = z.input + +export const interpolatableRobotFileServeInstructionsSchema = interpolateRobot( + robotFileServeInstructionsSchema, +) +export type InterpolatableRobotFileServeInstructions = z.input< + typeof interpolatableRobotFileServeInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-verify.ts b/src/alphalib/types/robots/file-verify.ts index 9ca59f9a..320ae60c 100644 --- a/src/alphalib/types/robots/file-verify.ts +++ b/src/alphalib/types/robots/file-verify.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -53,3 +53,10 @@ The type that you want to match against to ensure your file is of this type. For .strict() export type RobotFileVerifyInstructions = z.infer + +export const interpolatableRobotFileVerifyInstructionsSchema = interpolateRobot( + robotFileVerifyInstructionsSchema, +) +export type InterpolatableRobotFileVerifyInstructions = z.input< + typeof interpolatableRobotFileVerifyInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-virusscan.ts b/src/alphalib/types/robots/file-virusscan.ts index 25a2a538..56edda88 100644 --- a/src/alphalib/types/robots/file-virusscan.ts +++ b/src/alphalib/types/robots/file-virusscan.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -58,3 +58,10 @@ The error message shown to your users (such as by Uppy) when a file is declined .strict() export type RobotFileVirusscanInstructions = z.infer + +export const interpolatableRobotFileVirusscanInstructionsSchema = interpolateRobot( + robotFileVirusscanInstructionsSchema, +) +export type InterpolatableRobotFileVirusscanInstructions = z.input< + typeof interpolatableRobotFileVirusscanInstructionsSchema +> diff --git a/src/alphalib/types/robots/file-watermark.ts b/src/alphalib/types/robots/file-watermark.ts index f37cae76..48002de5 100644 --- a/src/alphalib/types/robots/file-watermark.ts +++ b/src/alphalib/types/robots/file-watermark.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' export const robotFileWatermarkInstructionsSchema = robotBase .merge(robotUse) @@ -11,3 +11,10 @@ export const robotFileWatermarkInstructionsSchema = robotBase .strict() export type RobotFileWatermarkInstructions = z.infer + +export const interpolatableRobotFileWatermarkInstructionsSchema = interpolateRobot( + robotFileWatermarkInstructionsSchema, +) +export type InterpolatableRobotFileWatermarkInstructions = z.input< + typeof interpolatableRobotFileWatermarkInstructionsSchema +> diff --git a/src/alphalib/types/robots/ftp-import.ts b/src/alphalib/types/robots/ftp-import.ts index c1140d70..bacc6f0b 100644 --- a/src/alphalib/types/robots/ftp-import.ts +++ b/src/alphalib/types/robots/ftp-import.ts @@ -1,6 +1,12 @@ import { z } from 'zod' -import { ftpBase, robotImport, path, robotBase } from './_instructions-primitives.ts' +import { + ftpBase, + robotImport, + path, + robotBase, + interpolateRobot, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -50,3 +56,10 @@ Determines if passive mode should be used for the FTP connection. export type RobotFtpImportInstructions = z.infer export type RobotFtpImportInstructionsInput = z.input + +export const interpolatableRobotFtpImportInstructionsSchema = interpolateRobot( + robotFtpImportInstructionsSchema, +) +export type InterpolatableRobotFtpImportInstructions = z.input< + typeof interpolatableRobotFtpImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/ftp-store.ts b/src/alphalib/types/robots/ftp-store.ts index 6cfef374..474ec9be 100644 --- a/src/alphalib/types/robots/ftp-store.ts +++ b/src/alphalib/types/robots/ftp-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { ftpBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { ftpBase, interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -59,3 +59,10 @@ Determines whether to establish a secure connection to the FTP server using SSL. export type RobotFtpStoreInstructions = z.infer export type RobotFtpStoreInstructionsInput = z.input + +export const interpolatableRobotFtpStoreInstructionsSchema = interpolateRobot( + robotFtpStoreInstructionsSchema, +) +export type InterpolatableRobotFtpStoreInstructions = z.input< + typeof interpolatableRobotFtpStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/google-import.ts b/src/alphalib/types/robots/google-import.ts index 45a45ac4..834193cd 100644 --- a/src/alphalib/types/robots/google-import.ts +++ b/src/alphalib/types/robots/google-import.ts @@ -8,6 +8,7 @@ import { path, recursive, robotBase, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -75,3 +76,10 @@ The pagination page size. This only works when recursive is \`true\` for now, in export type RobotGoogleImportInstructions = z.infer export type RobotGoogleImportInstructionsInput = z.input + +export const interpolatableRobotGoogleImportInstructionsSchema = interpolateRobot( + robotGoogleImportInstructionsSchema, +) +export type InterpolatableRobotGoogleImportInstructions = z.input< + typeof interpolatableRobotGoogleImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/google-store.ts b/src/alphalib/types/robots/google-store.ts index 069e599d..076e5ef0 100644 --- a/src/alphalib/types/robots/google-store.ts +++ b/src/alphalib/types/robots/google-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { googleBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { googleBase, interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -87,3 +87,10 @@ The SSL URL of the file in the result JSON. The following [Assembly variables](/ export type RobotGoogleStoreInstructions = z.infer export type RobotGoogleStoreInstructionsInput = z.input + +export const interpolatableRobotGoogleStoreInstructionsSchema = interpolateRobot( + robotGoogleStoreInstructionsSchema, +) +export type InterpolatableRobotGoogleStoreInstructions = z.input< + typeof interpolatableRobotGoogleStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/html-convert.ts b/src/alphalib/types/robots/html-convert.ts index c328d30e..da0b3344 100644 --- a/src/alphalib/types/robots/html-convert.ts +++ b/src/alphalib/types/robots/html-convert.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -19,9 +19,11 @@ export const meta: RobotMeta = { }, example_code_description: 'Take a full screenshot of the Transloadit homepage:', extended_description: ` -**Warning:** A validation error will occur if neither an HTML file is uploaded nor a URL parameter is given. [{.alert .alert-warning}] +> [!Warning] +> A validation error will occur if neither an HTML file is uploaded nor a URL parameter is given. -**Note:** Any files imported within the HTML page will be included in the cost. [{.alert .alert-note}] +> [!Note] +> Any files imported within the HTML page will be included in the cost. `, minimum_charge: 1048576, output_factor: 0.5, @@ -81,3 +83,10 @@ An object containing optional headers that will be passed along with the origina .strict() export type RobotHtmlConvertInstructions = z.infer + +export const interpolatableRobotHtmlConvertInstructionsSchema = interpolateRobot( + robotHtmlConvertInstructionsSchema, +) +export type InterpolatableRobotHtmlConvertInstructions = z.input< + typeof interpolatableRobotHtmlConvertInstructionsSchema +> diff --git a/src/alphalib/types/robots/http-import.ts b/src/alphalib/types/robots/http-import.ts index 23246960..850cb6ea 100644 --- a/src/alphalib/types/robots/http-import.ts +++ b/src/alphalib/types/robots/http-import.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { robotImport, robotBase } from './_instructions-primitives.ts' +import { + interpolateRobot, + robotImport, + robotBase, + return_file_stubs, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -59,12 +64,6 @@ Provides the delimiter that is used to split the URLs in your \`url\` parameter Custom headers to be sent for file import. This is an empty array by default, such that no additional headers except the necessary ones (e.g. Host) are sent. -`), - force_name: z - .union([z.string(), z.array(z.string())]) - .nullable() - .default(null).describe(` -Custom name for the imported file(s). Defaults to \`null\`, which means the file names are derived from the supplied URL(s). `), import_on_errors: z.array(z.string()).default([]).describe(` Setting this to \`"meta"\` will still import the file on metadata extraction errors. \`ignore_errors\` is similar, it also ignores the error and makes sure the Robot doesn't stop, but it doesn't import the file. @@ -72,8 +71,16 @@ Setting this to \`"meta"\` will still import the file on metadata extraction err fail_fast: z.boolean().default(false).describe(` Disable the internal retry mechanism, and fail immediately if a resource can't be imported. This can be useful for performance critical applications. `), + return_file_stubs, }) .strict() export type RobotHttpImportInstructions = z.infer export type RobotHttpImportInstructionsInput = z.input + +export const interpolatableRobotHttpImportInstructionsSchema = interpolateRobot( + robotHttpImportInstructionsSchema, +) +export type InterpolatableRobotHttpImportInstructions = z.input< + typeof interpolatableRobotHttpImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/image-bgremove.ts b/src/alphalib/types/robots/image-bgremove.ts index 49dbd61a..eef5ebea 100644 --- a/src/alphalib/types/robots/image-bgremove.ts +++ b/src/alphalib/types/robots/image-bgremove.ts @@ -1,7 +1,7 @@ import { z } from 'zod' import type { RobotMeta } from './_instructions-primitives.ts' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' export const meta: RobotMeta = { allowed_for_url_transform: true, @@ -49,3 +49,10 @@ export const robotImageBgremoveInstructionsSchema = robotBase .strict() export type RobotImageBgremoveInstructions = z.infer + +export const interpolatableRobotImageBgremoveInstructionsSchema = interpolateRobot( + robotImageBgremoveInstructionsSchema, +) +export type InterpolatableRobotImageBgremoveInstructions = z.input< + typeof interpolatableRobotImageBgremoveInstructionsSchema +> diff --git a/src/alphalib/types/robots/image-describe.ts b/src/alphalib/types/robots/image-describe.ts index 1fceff6a..c71acfb5 100644 --- a/src/alphalib/types/robots/image-describe.ts +++ b/src/alphalib/types/robots/image-describe.ts @@ -3,6 +3,7 @@ import { z } from 'zod' import { aiProviderSchema, granularitySchema, + interpolateRobot, robotBase, robotUse, } from './_instructions-primitives.ts' @@ -25,7 +26,8 @@ export const meta: RobotMeta = { example_code_description: 'Recognize objects in an uploaded image and store the labels in a JSON file:', extended_description: ` -**Warning:** Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input images. Avoid relying on exact responses in your tests and application. [{.alert .alert-warning}] +> [!Warning] +> Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input images. Avoid relying on exact responses in your tests and application. `, minimum_charge: 1572864, output_factor: 0.05, @@ -72,3 +74,10 @@ For an example of how to automatically reject NSFW content and malware, please c .strict() export type RobotImageDescribeInstructions = z.infer + +export const interpolatableRobotImageDescribeInstructionsSchema = interpolateRobot( + robotImageDescribeInstructionsSchema, +) +export type InterpolatableRobotImageDescribeInstructions = z.input< + typeof interpolatableRobotImageDescribeInstructionsSchema +> diff --git a/src/alphalib/types/robots/image-facedetect.ts b/src/alphalib/types/robots/image-facedetect.ts index f8c80658..812f506b 100644 --- a/src/alphalib/types/robots/image-facedetect.ts +++ b/src/alphalib/types/robots/image-facedetect.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { aiProviderSchema, robotBase, robotUse } from './_instructions-primitives.ts' +import { + aiProviderSchema, + interpolateRobot, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -129,3 +134,10 @@ For the following examples, the input image is: export type RobotImageFacedetectInstructions = z.infer< typeof robotImageFacedetectInstructionsSchema > + +export const interpolatableRobotImageFacedetectInstructionsSchema = interpolateRobot( + robotImageFacedetectInstructionsSchema, +) +export type InterpolatableRobotImageFacedetectInstructions = z.input< + typeof interpolatableRobotImageFacedetectInstructionsSchema +> diff --git a/src/alphalib/types/robots/image-generate.ts b/src/alphalib/types/robots/image-generate.ts index 9db35500..e21382fb 100644 --- a/src/alphalib/types/robots/image-generate.ts +++ b/src/alphalib/types/robots/image-generate.ts @@ -1,6 +1,25 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import type { RobotMeta } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' + +export const meta: RobotMeta = { + allowed_for_url_transform: true, + bytescount: 1, + discount_factor: 1, + discount_pct: 0, + minimum_charge: 0, + output_factor: 0.6, + purpose_sentence: 'generates images from text prompts using AI', + purpose_verb: 'generate', + purpose_word: 'generate', + purpose_words: 'Generate images from text prompts', + service_slug: 'artificial-intelligence', + slot_count: 10, + title: 'Generate images from text prompts', + typical_file_size_mb: 1.2, + typical_file_type: 'image', +} export const robotImageGenerateInstructionsSchema = robotBase .merge(robotUse) @@ -30,3 +49,10 @@ export type RobotImageGenerateInstructions = z.infer + +export const interpolatableRobotImageGenerateInstructionsSchema = interpolateRobot( + robotImageGenerateInstructionsSchema, +) +export type InterpolatableRobotImageGenerateInstructions = z.input< + typeof interpolatableRobotImageGenerateInstructionsSchema +> diff --git a/src/alphalib/types/robots/image-merge.ts b/src/alphalib/types/robots/image-merge.ts index 48ed4e29..89355602 100644 --- a/src/alphalib/types/robots/image-merge.ts +++ b/src/alphalib/types/robots/image-merge.ts @@ -3,6 +3,7 @@ import { z } from 'zod' import { color_without_alpha, imageQualitySchema, + interpolateRobot, robotBase, robotUse, } from './_instructions-primitives.ts' @@ -79,3 +80,10 @@ Controls the image compression for PNG images. Setting to \`true\` results in sm }) .strict() export type RobotImageMergeInstructions = z.infer + +export const interpolatableRobotImageMergeInstructionsSchema = interpolateRobot( + robotImageMergeInstructionsSchema, +) +export type InterpolatableRobotImageMergeInstructions = z.input< + typeof interpolatableRobotImageMergeInstructionsSchema +> diff --git a/src/alphalib/types/robots/image-ocr.ts b/src/alphalib/types/robots/image-ocr.ts index ca6aeb2d..e0ef2a43 100644 --- a/src/alphalib/types/robots/image-ocr.ts +++ b/src/alphalib/types/robots/image-ocr.ts @@ -3,6 +3,7 @@ import { z } from 'zod' import { aiProviderSchema, granularitySchema, + interpolateRobot, robotBase, robotUse, } from './_instructions-primitives.ts' @@ -25,7 +26,8 @@ export const meta: RobotMeta = { }, example_code_description: 'Recognize text in an uploaded image and save it to a text file:', extended_description: ` -**Warning:** Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input images. Avoid relying on exact responses in your tests and application. [{.alert .alert-warning}] +> [!Warning] +> Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input images. Avoid relying on exact responses in your tests and application. `, minimum_charge: 1048576, output_factor: 0.6, @@ -69,3 +71,10 @@ In what format to return the extracted text. .strict() export type RobotImageOcrInstructions = z.infer + +export const interpolatableRobotImageOcrInstructionsSchema = interpolateRobot( + robotImageOcrInstructionsSchema, +) +export type InterpolatableRobotImageOcrInstructions = z.input< + typeof interpolatableRobotImageOcrInstructionsSchema +> diff --git a/src/alphalib/types/robots/image-optimize.ts b/src/alphalib/types/robots/image-optimize.ts index 9d4d98c0..e0c893ba 100644 --- a/src/alphalib/types/robots/image-optimize.ts +++ b/src/alphalib/types/robots/image-optimize.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { optimize_priority, robotBase, robotUse } from './_instructions-primitives.ts' +import { + interpolateRobot, + optimize_priority, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -42,7 +47,8 @@ This Robot enables you to lower your storage and bandwidth costs, and It works well together with [🤖/image/resize](/docs/transcoding/image-manipulation/image-resize/) to bring the full power of resized and optimized images to your website or app. -**Note:** This Robot accepts all image types and will just pass on unsupported image types unoptimized. Hence, there is no need to set up [🤖/file/filter](/docs/transcoding/file-filtering/file-filter/) workflows for this. [{.alert .alert-note}] +> [!Note] +> This Robot accepts all image types and will just pass on unsupported image types unoptimized. Hence, there is no need to set up [🤖/file/filter](/docs/transcoding/file-filtering/file-filter/) workflows for this. `), priority: optimize_priority.describe(` Provides different algorithms for better or worse compression for your images, but that run slower or faster. The value \`"conversion-speed"\` will result in an average compression ratio of 18%. \`"compression-ratio"\` will result in an average compression ratio of 31%. @@ -59,3 +65,10 @@ If set to \`true\` this parameter tries to fix images that would otherwise make }) .strict() export type RobotImageOptimizeInstructions = z.infer + +export const interpolatableRobotImageOptimizeInstructionsSchema = interpolateRobot( + robotImageOptimizeInstructionsSchema, +) +export type InterpolatableRobotImageOptimizeInstructions = z.input< + typeof interpolatableRobotImageOptimizeInstructionsSchema +> diff --git a/src/alphalib/types/robots/image-resize.ts b/src/alphalib/types/robots/image-resize.ts index 1b7271bf..61f937a1 100644 --- a/src/alphalib/types/robots/image-resize.ts +++ b/src/alphalib/types/robots/image-resize.ts @@ -10,11 +10,10 @@ import { percentageSchema, positionSchema, unsafeCoordinatesSchema, - interpolationSchemaToYieldString, - interpolationSchemaToYieldNumber, robotBase, robotUse, robotImagemagick, + interpolateRobot, } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -49,7 +48,7 @@ export const meta: RobotMeta = { uses_tools: ['imagemagick'], } -export const robotImageResizeInstructionsInterpolatedSchema = robotBase +export const robotImageResizeInstructionsSchema = robotBase .merge(robotUse) .merge(robotImagemagick) .extend({ @@ -371,31 +370,12 @@ If your converted image is unsharp, please try increasing density. }) .strict() -export const robotImageResizeInstructionsSchema = - robotImageResizeInstructionsInterpolatedSchema.extend({ - width: robotImageResizeInstructionsInterpolatedSchema.shape.width.or( - interpolationSchemaToYieldNumber, - ), - height: robotImageResizeInstructionsInterpolatedSchema.shape.height.or( - interpolationSchemaToYieldNumber, - ), - background: robotImageResizeInstructionsInterpolatedSchema.shape.background.or( - interpolationSchemaToYieldString, - ), - resize_strategy: robotImageResizeInstructionsInterpolatedSchema.shape.resize_strategy.or( - interpolationSchemaToYieldString, - ), - blur_regions: robotImageResizeInstructionsInterpolatedSchema.shape.blur_regions.or( - z.array( - z.object({ - x: complexWidthSchema.or(interpolationSchemaToYieldNumber), - y: complexHeightSchema.or(interpolationSchemaToYieldNumber), - width: complexWidthSchema.or(interpolationSchemaToYieldNumber), - height: complexHeightSchema.or(interpolationSchemaToYieldNumber), - }), - ), - ), - }) - export type RobotImageResizeInstructions = z.infer export type RobotImageResizeInstructionsInput = z.input + +export const interpolatableRobotImageResizeInstructionsSchema = interpolateRobot( + robotImageResizeInstructionsSchema, +) +export type InterpolatableRobotImageResizeInstructions = z.input< + typeof interpolatableRobotImageResizeInstructionsSchema +> diff --git a/src/alphalib/types/robots/meta-write.ts b/src/alphalib/types/robots/meta-write.ts index 57115506..46dcb5c5 100644 --- a/src/alphalib/types/robots/meta-write.ts +++ b/src/alphalib/types/robots/meta-write.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotFFmpeg, robotBase, robotUse } from './_instructions-primitives.ts' +import { robotFFmpeg, robotBase, robotUse, interpolateRobot } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -53,3 +53,10 @@ Valid metadata keys can be found [here](https://exiftool.org/TagNames/EXIF.html) .strict() export type RobotMetaWriteInstructions = z.infer + +export const interpolatableRobotMetaWriteInstructionsSchema = interpolateRobot( + robotMetaWriteInstructionsSchema, +) +export type InterpolatableRobotMetaWriteInstructions = z.input< + typeof interpolatableRobotMetaWriteInstructionsSchema +> diff --git a/src/alphalib/types/robots/minio-import.ts b/src/alphalib/types/robots/minio-import.ts index c21b02bd..8be34b01 100644 --- a/src/alphalib/types/robots/minio-import.ts +++ b/src/alphalib/types/robots/minio-import.ts @@ -2,12 +2,14 @@ import { z } from 'zod' import { files_per_page, + interpolateRobot, robotImport, minioBase, page_number, path, recursive, robotBase, + return_file_stubs, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -73,8 +75,16 @@ When doing big imports, make sure no files are added or removed from other scrip files_per_page: files_per_page.describe(` The pagination page size. This only works when recursive is \`true\` for now, in order to not break backwards compatibility in non-recursive imports. `), + return_file_stubs, }) .strict() export type RobotMinioImportInstructions = z.infer export type RobotMinioImportInstructionsInput = z.input + +export const interpolatableRobotMinioImportInstructionsSchema = interpolateRobot( + robotMinioImportInstructionsSchema, +) +export type InterpolatableRobotMinioImportInstructions = z.input< + typeof interpolatableRobotMinioImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/minio-store.ts b/src/alphalib/types/robots/minio-store.ts index f4741ee3..58f8a1eb 100644 --- a/src/alphalib/types/robots/minio-store.ts +++ b/src/alphalib/types/robots/minio-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { minioBase, robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, minioBase, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -62,3 +62,10 @@ If this parameter is not used, no URL signing is done. export type RobotMinioStoreInstructions = z.infer export type RobotMinioStoreInstructionsInput = z.input + +export const interpolatableRobotMinioStoreInstructionsSchema = interpolateRobot( + robotMinioStoreInstructionsSchema, +) +export type InterpolatableRobotMinioStoreInstructions = z.input< + typeof interpolatableRobotMinioStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/progress-simulate.ts b/src/alphalib/types/robots/progress-simulate.ts index b4d8e963..b6d83a85 100644 --- a/src/alphalib/types/robots/progress-simulate.ts +++ b/src/alphalib/types/robots/progress-simulate.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase } from './_instructions-primitives.ts' export const robotProgressSimulateInstructionsSchema = robotBase .extend({ @@ -14,3 +14,10 @@ export const robotProgressSimulateInstructionsSchema = robotBase export type RobotProgressSimulateInstructions = z.infer< typeof robotProgressSimulateInstructionsSchema > + +export const interpolatableRobotProgressSimulateInstructionsSchema = interpolateRobot( + robotProgressSimulateInstructionsSchema, +) +export type InterpolatableRobotProgressSimulateInstructions = z.input< + typeof interpolatableRobotProgressSimulateInstructionsSchema +> diff --git a/src/alphalib/types/robots/s3-import.ts b/src/alphalib/types/robots/s3-import.ts index c5a48a27..14e950ba 100644 --- a/src/alphalib/types/robots/s3-import.ts +++ b/src/alphalib/types/robots/s3-import.ts @@ -2,12 +2,14 @@ import { z } from 'zod' import { files_per_page, + interpolateRobot, robotImport, - minioBase, page_number, path, recursive, robotBase, + return_file_stubs, + s3Base, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -46,14 +48,15 @@ export const meta: RobotMeta = { export const robotS3ImportInstructionsSchema = robotBase .merge(robotImport) - .merge(minioBase) + .merge(s3Base) .extend({ robot: z.literal('/s3/import').describe(` If you are new to Amazon S3, see our tutorial on [using your own S3 bucket](/docs/faq/how-to-set-up-an-amazon-s3-bucket/). The URL to the result file in your S3 bucket will be returned in the Assembly Status JSON. -**Use DNS-compliant bucket names.** Your bucket name [must be DNS-compliant](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) and must not contain uppercase letters. Any non-alphanumeric characters in the file names will be replaced with an underscore, and spaces will be replaced with dashes. If your existing S3 bucket contains uppercase letters or is otherwise not DNS-compliant, rewrite the result URLs using the Robot’s \`url_prefix\` parameter. [{.alert .alert-warning}] +> [!Warning] +> **Use DNS-compliant bucket names**. Your bucket name [must be DNS-compliant](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) and must not contain uppercase letters. Any non-alphanumeric characters in the file names will be replaced with an underscore, and spaces will be replaced with dashes. If your existing S3 bucket contains uppercase letters or is otherwise not DNS-compliant, rewrite the result URLs using the Robot’s \`url_prefix\` parameter. @@ -109,8 +112,16 @@ When doing big imports, make sure no files are added or removed from other scrip files_per_page: files_per_page.optional().describe(` The pagination page size. This only works when recursive is \`true\` for now, in order to not break backwards compatibility in non-recursive imports. `), + return_file_stubs, }) .strict() export type RobotS3ImportInstructions = z.infer export type RobotS3ImportInstructionsInput = z.input + +export const interpolatableRobotS3ImportInstructionsSchema = interpolateRobot( + robotS3ImportInstructionsSchema, +) +export type InterpolatableRobotS3ImportInstructions = z.input< + typeof interpolatableRobotS3ImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/s3-store.ts b/src/alphalib/types/robots/s3-store.ts index ccd52d38..0ed494e1 100644 --- a/src/alphalib/types/robots/s3-store.ts +++ b/src/alphalib/types/robots/s3-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse, s3Base } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse, s3Base } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -44,9 +44,11 @@ If you are new to Amazon S3, see our tutorial on [using your own S3 bucket](/doc The URL to the result file in your S3 bucket will be returned in the Assembly Status JSON. If your S3 bucket has versioning enabled, the version ID of the file will be returned within \`meta.version_id\` -**Avoid permission errors.** By default, \`acl\` is set to \`"public"\`. AWS S3 has a bucket setting called "Block new public ACLs and uploading public objects". Set this to False in your bucket if you intend to leave \`acl\` as \`"public"\`. Otherwise, you’ll receive permission errors in your Assemblies despite your S3 credentials being configured correctly. [{.alert .alert-warning}] +> [!Warning] +> **Avoid permission errors.** By default, \`acl\` is set to \`"public"\`. AWS S3 has a bucket setting called "Block new public ACLs and uploading public objects". Set this to False in your bucket if you intend to leave \`acl\` as \`"public"\`. Otherwise, you’ll receive permission errors in your Assemblies despite your S3 credentials being configured correctly. -**Use DNS-compliant bucket names.** Your bucket name [must be DNS-compliant](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) and must not contain uppercase letters. Any non-alphanumeric characters in the file names will be replaced with an underscore, and spaces will be replaced with dashes. If your existing S3 bucket contains uppercase letters or is otherwise not DNS-compliant, rewrite the result URLs using the Robot’s \`url_prefix\` parameter. [{.alert .alert-warning}] +> [!Warning] +> **Use DNS-compliant bucket names.** Your bucket name [must be DNS-compliant](https://docs.aws.amazon.com/AmazonS3/latest/userguide/bucketnamingrules.html) and must not contain uppercase letters. Any non-alphanumeric characters in the file names will be replaced with an underscore, and spaces will be replaced with dashes. If your existing S3 bucket contains uppercase letters or is otherwise not DNS-compliant, rewrite the result URLs using the Robot’s \`url_prefix\` parameter. @@ -119,3 +121,10 @@ This parameter provides signed URLs in the result JSON (in the \`signed_url\` an export type RobotS3StoreInstructions = z.infer export type RobotS3StoreInstructionsInput = z.input + +export const interpolatableRobotS3StoreInstructionsSchema = interpolateRobot( + robotS3StoreInstructionsSchema, +) +export type InterpolatableRobotS3StoreInstructions = z.input< + typeof interpolatableRobotS3StoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/script-run.ts b/src/alphalib/types/robots/script-run.ts index e16b71ca..34d1417e 100644 --- a/src/alphalib/types/robots/script-run.ts +++ b/src/alphalib/types/robots/script-run.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -79,3 +79,10 @@ You can check whether evaluating this script was free by inspecting \`file.meta. .strict() export type RobotScriptRunInstructions = z.infer + +export const interpolatableRobotScriptRunInstructionsSchema = interpolateRobot( + robotScriptRunInstructionsSchema, +) +export type InterpolatableRobotScriptRunInstructions = z.input< + typeof interpolatableRobotScriptRunInstructionsSchema +> diff --git a/src/alphalib/types/robots/sftp-import.ts b/src/alphalib/types/robots/sftp-import.ts index 4b1c059a..458f9395 100644 --- a/src/alphalib/types/robots/sftp-import.ts +++ b/src/alphalib/types/robots/sftp-import.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotImport, robotBase, sftpBase } from './_instructions-primitives.ts' +import { robotImport, robotBase, sftpBase, interpolateRobot } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -48,3 +48,10 @@ The path on your SFTP server where to search for files. export type RobotSftpImportInstructions = z.infer export type RobotSftpImportInstructionsInput = z.input + +export const interpolatableRobotSftpImportInstructionsSchema = interpolateRobot( + robotSftpImportInstructionsSchema, +) +export type InterpolatableRobotSftpImportInstructions = z.input< + typeof interpolatableRobotSftpImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/sftp-store.ts b/src/alphalib/types/robots/sftp-store.ts index 2c135b09..94544731 100644 --- a/src/alphalib/types/robots/sftp-store.ts +++ b/src/alphalib/types/robots/sftp-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse, sftpBase } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse, sftpBase } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -59,3 +59,10 @@ This optional parameter controls how an uploaded file's permission bits are set. export type RobotSftpStoreInstructions = z.infer export type RobotSftpStoreInstructionsInput = z.input + +export const interpolatableRobotSftpStoreInstructionsSchema = interpolateRobot( + robotSftpStoreInstructionsSchema, +) +export type InterpolatableRobotSftpStoreInstructions = z.input< + typeof interpolatableRobotSftpStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/speech-transcribe.ts b/src/alphalib/types/robots/speech-transcribe.ts index 054507d8..8a635cda 100644 --- a/src/alphalib/types/robots/speech-transcribe.ts +++ b/src/alphalib/types/robots/speech-transcribe.ts @@ -3,6 +3,7 @@ import { z } from 'zod' import { aiProviderSchema, granularitySchema, + interpolateRobot, robotBase, robotUse, } from './_instructions-primitives.ts' @@ -27,7 +28,8 @@ export const meta: RobotMeta = { example_code_description: 'Transcribe speech in French from uploaded audio or video, and save it to a text file:', extended_description: ` -**Warning:** Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input media. Avoid relying on exact responses in your tests and application. [{.alert .alert-warning}] +> [!Warning] +> Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input media. Avoid relying on exact responses in your tests and application. `, minimum_charge: 1048576, output_factor: 0.05, @@ -94,3 +96,10 @@ export const robotSpeechTranscribeInstructionsWithHiddenFieldsSchema = export type RobotSpeechTranscribeInstructionsWithHiddenFields = z.infer< typeof robotSpeechTranscribeInstructionsWithHiddenFieldsSchema > + +export const interpolatableRobotSpeechTranscribeInstructionsSchema = interpolateRobot( + robotSpeechTranscribeInstructionsSchema, +) +export type InterpolatableRobotSpeechTranscribeInstructions = z.input< + typeof interpolatableRobotSpeechTranscribeInstructionsSchema +> diff --git a/src/alphalib/types/robots/supabase-import.ts b/src/alphalib/types/robots/supabase-import.ts index e859f4bd..7ff80bb8 100644 --- a/src/alphalib/types/robots/supabase-import.ts +++ b/src/alphalib/types/robots/supabase-import.ts @@ -7,7 +7,9 @@ import { path, recursive, robotBase, + return_file_stubs, supabaseBase, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -75,6 +77,7 @@ When doing big imports, make sure no files are added or removed from other scrip files_per_page: files_per_page.describe(` The pagination page size. This only works when recursive is \`true\` for now, in order to not break backwards compatibility in non-recursive imports. `), + return_file_stubs, }) .strict() @@ -82,3 +85,10 @@ export type RobotSupabaseImportInstructions = z.infer + +export const interpolatableRobotSupabaseImportInstructionsSchema = interpolateRobot( + robotSupabaseImportInstructionsSchema, +) +export type InterpolatableRobotSupabaseImportInstructions = z.input< + typeof interpolatableRobotSupabaseImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/supabase-store.ts b/src/alphalib/types/robots/supabase-store.ts index fbf3b49b..c09134e6 100644 --- a/src/alphalib/types/robots/supabase-store.ts +++ b/src/alphalib/types/robots/supabase-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse, supabaseBase } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse, supabaseBase } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -57,3 +57,10 @@ export type RobotSupabaseStoreInstructions = z.infer + +export const interpolatableRobotSupabaseStoreInstructionsSchema = interpolateRobot( + robotSupabaseStoreInstructionsSchema, +) +export type InterpolatableRobotSupabaseStoreInstructions = z.input< + typeof interpolatableRobotSupabaseStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/swift-import.ts b/src/alphalib/types/robots/swift-import.ts index ea95ed7b..38ac9a7d 100644 --- a/src/alphalib/types/robots/swift-import.ts +++ b/src/alphalib/types/robots/swift-import.ts @@ -7,7 +7,9 @@ import { path, recursive, robotBase, + return_file_stubs, swiftBase, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -73,8 +75,16 @@ When doing big imports, make sure no files are added or removed from other scrip files_per_page: files_per_page.describe(` The pagination page size. This only works when recursive is \`true\` for now, in order to not break backwards compatibility in non-recursive imports. `), + return_file_stubs, }) .strict() export type RobotSwiftImportInstructions = z.infer export type RobotSwiftImportInstructionsInput = z.input + +export const interpolatableRobotSwiftImportInstructionsSchema = interpolateRobot( + robotSwiftImportInstructionsSchema, +) +export type InterpolatableRobotSwiftImportInstructions = z.input< + typeof interpolatableRobotSwiftImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/swift-store.ts b/src/alphalib/types/robots/swift-store.ts index 3d8012eb..be1a3b6e 100644 --- a/src/alphalib/types/robots/swift-store.ts +++ b/src/alphalib/types/robots/swift-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse, swiftBase } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse, swiftBase } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -59,3 +59,10 @@ This parameter provides signed URLs in the result JSON (in the \`signed_ssl_url\ export type RobotSwiftStoreInstructions = z.infer export type RobotSwiftStoreInstructionsInput = z.input + +export const interpolatableRobotSwiftStoreInstructionsSchema = interpolateRobot( + robotSwiftStoreInstructionsSchema, +) +export type InterpolatableRobotSwiftStoreInstructions = z.input< + typeof interpolatableRobotSwiftStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/text-speak.ts b/src/alphalib/types/robots/text-speak.ts index db54b485..a382caca 100644 --- a/src/alphalib/types/robots/text-speak.ts +++ b/src/alphalib/types/robots/text-speak.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { aiProviderSchema, robotBase, robotUse } from './_instructions-primitives.ts' +import { + aiProviderSchema, + interpolateRobot, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -22,7 +27,8 @@ export const meta: RobotMeta = { example_code_description: 'Synthesize speech from uploaded text documents, using a female voice in American English:', extended_description: ` -**Warning:** Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input media. Avoid relying on exact responses in your tests and application. [{.alert .alert-warning}] +> [!Warning] +> Transloadit aims to be deterministic, but this Robot uses third-party AI services. The providers (AWS, GCP) will evolve their models over time, giving different responses for the same input media. Avoid relying on exact responses in your tests and application. ## Supported languages and voices @@ -98,3 +104,10 @@ Please see the supported syntaxes for [AWS](https://docs.aws.amazon.com/polly/la .strict() export type RobotTextSpeakInstructions = z.infer + +export const interpolatableRobotTextSpeakInstructionsSchema = interpolateRobot( + robotTextSpeakInstructionsSchema, +) +export type InterpolatableRobotTextSpeakInstructions = z.input< + typeof interpolatableRobotTextSpeakInstructionsSchema +> diff --git a/src/alphalib/types/robots/text-translate.ts b/src/alphalib/types/robots/text-translate.ts index cf7816cc..86cf5fb6 100644 --- a/src/alphalib/types/robots/text-translate.ts +++ b/src/alphalib/types/robots/text-translate.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { aiProviderSchema, robotBase, robotUse } from './_instructions-primitives.ts' +import { + aiProviderSchema, + interpolateRobot, + robotBase, + robotUse, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -20,7 +25,8 @@ export const meta: RobotMeta = { }, example_code_description: 'Translate uploaded text file contents to German:', extended_description: ` -**Warning:** This Robot uses third-party AI services. They may tweak their models over time, giving different responses for the same input media. Avoid relying on exact responses in your tests and application. [{.alert .alert-warning}] +> [!Warning] +> This Robot uses third-party AI services. They may tweak their models over time, giving different responses for the same input media. Avoid relying on exact responses in your tests and application. ## Supported languages @@ -174,7 +180,8 @@ export const robotTextTranslateInstructionsSchema = robotBase robot: z.literal('/text/translate').describe(` You can use the text that we return in your application, or you can pass the text down to other Robots to add a translated subtitle track to a video for example. -**This Robot accepts only files with a \`text/*\` MIME-type,** including plain text and Markdown. For documents in other formats, use [🤖/document/convert](/docs/transcoding/document-processing/document-convert/) to first convert them into a compatible text format before proceeding. [{.alert .alert-note}] +> [!Note] +> **This Robot accepts only files with a \`text/*\` MIME-type,** including plain text and Markdown. For documents in other formats, use [🤖/document/convert](/docs/transcoding/document-processing/document-convert/) to first convert them into a compatible text format before proceeding. `), provider: aiProviderSchema.describe(` Which AI provider to leverage. Valid values are \`"aws"\` (Amazon Web Services) and \`"gcp"\` (Google Cloud Platform). @@ -197,3 +204,10 @@ If the exact language can't be found, a generic variant can be fallen back to. F .strict() export type RobotTextTranslateInstructions = z.infer + +export const interpolatableRobotTextTranslateInstructionsSchema = interpolateRobot( + robotTextTranslateInstructionsSchema, +) +export type InterpolatableRobotTextTranslateInstructions = z.input< + typeof interpolatableRobotTextTranslateInstructionsSchema +> diff --git a/src/alphalib/types/robots/tigris-import.ts b/src/alphalib/types/robots/tigris-import.ts index 3c98b40d..323659bc 100644 --- a/src/alphalib/types/robots/tigris-import.ts +++ b/src/alphalib/types/robots/tigris-import.ts @@ -2,10 +2,12 @@ import { z } from 'zod' import { files_per_page, + interpolateRobot, page_number, path, recursive, robotBase, + return_file_stubs, robotImport, tigrisBase, } from './_instructions-primitives.ts' @@ -73,8 +75,16 @@ When doing big imports, make sure no files are added or removed from other scrip files_per_page: files_per_page.describe(` The pagination page size. This only works when recursive is \`true\` for now, in order to not break backwards compatibility in non-recursive imports. `), + return_file_stubs, }) .strict() export type RobotTigrisImportInstructions = z.infer export type RobotTigrisImportInstructionsInput = z.input + +export const interpolatableRobotTigrisImportInstructionsSchema = interpolateRobot( + robotTigrisImportInstructionsSchema, +) +export type InterpolatableRobotTigrisImportInstructions = z.input< + typeof interpolatableRobotTigrisImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/tigris-store.ts b/src/alphalib/types/robots/tigris-store.ts index 3e8545bf..a144c22b 100644 --- a/src/alphalib/types/robots/tigris-store.ts +++ b/src/alphalib/types/robots/tigris-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse, tigrisBase } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse, tigrisBase } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -62,3 +62,10 @@ If this parameter is not used, no URL signing is done. export type RobotTigrisStoreInstructions = z.infer export type RobotTigrisStoreInstructionsInput = z.input + +export const interpolatableRobotTigrisStoreInstructionsSchema = interpolateRobot( + robotTigrisStoreInstructionsSchema, +) +export type InterpolatableRobotTigrisStoreInstructions = z.input< + typeof interpolatableRobotTigrisStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/tlcdn-deliver.ts b/src/alphalib/types/robots/tlcdn-deliver.ts index 0a9ea0c1..5ee03e58 100644 --- a/src/alphalib/types/robots/tlcdn-deliver.ts +++ b/src/alphalib/types/robots/tlcdn-deliver.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, type RobotMeta } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, type RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { allowed_for_url_transform: false, @@ -30,3 +30,10 @@ When you want Transloadit to tranform files on the fly, this Robot ca .strict() export type RobotTlcdnDeliverInstructions = z.infer + +export const interpolatableRobotTlcdnDeliverInstructionsSchema = interpolateRobot( + robotTlcdnDeliverInstructionsSchema, +) +export type InterpolatableRobotTlcdnDeliverInstructions = z.input< + typeof interpolatableRobotTlcdnDeliverInstructionsSchema +> diff --git a/src/alphalib/types/robots/tus-store.ts b/src/alphalib/types/robots/tus-store.ts index 99fb0692..23861220 100644 --- a/src/alphalib/types/robots/tus-store.ts +++ b/src/alphalib/types/robots/tus-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -37,9 +37,11 @@ export const robotTusStoreInstructionsSchema = robotBase .merge(robotUse) .extend({ robot: z.literal('/tus/store').describe(` -**Note:** This Robot only accepts videos. [{.alert .alert-note}] +> [!Note] +> This Robot only accepts videos. -**Warning:** Vimeo's API limits the number of concurrent uploads per minute based on your Vimeo account plan. To see how many videos can be uploaded at once based on your plan, click the following [link](https://developer.vimeo.com/guidelines/rate-limiting#table-1). [{.alert .alert-warning}] +> [!Warning] +> Vimeo's API limits the number of concurrent uploads per minute based on your Vimeo account plan. To see how many videos can be uploaded at once based on your plan, click the following [link](https://developer.vimeo.com/guidelines/rate-limiting#table-1). ## Installation @@ -75,3 +77,10 @@ The SSL URL of the file in the Assembly Status JSON. The following [A export type RobotTusStoreInstructions = z.infer export type RobotTusStoreInstructionsInput = z.input + +export const interpolatableRobotTusStoreInstructionsSchema = interpolateRobot( + robotTusStoreInstructionsSchema, +) +export type InterpolatableRobotTusStoreInstructions = z.input< + typeof interpolatableRobotTusStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/upload-handle.ts b/src/alphalib/types/robots/upload-handle.ts index a6593c8c..40dc952a 100644 --- a/src/alphalib/types/robots/upload-handle.ts +++ b/src/alphalib/types/robots/upload-handle.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -56,3 +56,10 @@ There are **3 important constraints** when using this Robot: export type RobotUploadHandleInstructions = z.infer export type RobotUploadHandleInstructionsInput = z.input + +export const interpolatableRobotUploadHandleInstructionsSchema = interpolateRobot( + robotUploadHandleInstructionsSchema, +) +export type InterpolatableRobotUploadHandleInstructions = z.input< + typeof interpolatableRobotUploadHandleInstructionsSchema +> diff --git a/src/alphalib/types/robots/video-adaptive.ts b/src/alphalib/types/robots/video-adaptive.ts index 8db83090..f00aa6df 100644 --- a/src/alphalib/types/robots/video-adaptive.ts +++ b/src/alphalib/types/robots/video-adaptive.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { robotBase, robotUse, robotFFmpegVideo } from './_instructions-primitives.ts' +import { + robotBase, + robotUse, + robotFFmpegVideo, + interpolateRobot, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -121,3 +126,10 @@ Determines whether you want closed caption support when using the \`"hls"\` tech .strict() export type RobotVideoAdaptiveInstructions = z.infer + +export const interpolatableRobotVideoAdaptiveInstructionsSchema = interpolateRobot( + robotVideoAdaptiveInstructionsSchema, +) +export type InterpolatableRobotVideoAdaptiveInstructions = z.input< + typeof interpolatableRobotVideoAdaptiveInstructionsSchema +> diff --git a/src/alphalib/types/robots/video-concat.ts b/src/alphalib/types/robots/video-concat.ts index 8449126e..50449f24 100644 --- a/src/alphalib/types/robots/video-concat.ts +++ b/src/alphalib/types/robots/video-concat.ts @@ -1,6 +1,11 @@ import { z } from 'zod' -import { robotFFmpegVideo, robotBase, robotUse } from './_instructions-primitives.ts' +import { + robotFFmpegVideo, + robotBase, + robotUse, + interpolateRobot, +} from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -44,7 +49,8 @@ export const robotVideoConcatInstructionsSchema = robotBase .merge(robotFFmpegVideo) .extend({ robot: z.literal('/video/concat').describe(` -**Warning:** All videos you concatenate must have the same dimensions (width and height) and the same streams (audio and video streams), otherwise you will run into errors. If your videos donʼt have the desired dimensions when passing them to [🤖/video/concat](/docs/transcoding/video-encoding/video-concat/), encode them first with [🤖/video/encode](/docs/transcoding/video-encoding/video-encode/). [{.alert .alert-warning}] +> [!Warning] +> All videos you concatenate must have the same dimensions (width and height) and the same streams (audio and video streams), otherwise you will run into errors. If your videos donʼt have the desired dimensions when passing them to [🤖/video/concat](/docs/transcoding/video-encoding/video-concat/), encode them first with [🤖/video/encode](/docs/transcoding/video-encoding/video-encode/). Itʼs possible to concatenate a virtually infinite number of video files using [🤖/video/concat](/docs/transcoding/video-encoding/video-concat/). `), @@ -66,3 +72,10 @@ Please note this parameter is independent of adding video fades between sections .strict() export type RobotVideoConcatInstructions = z.infer + +export const interpolatableRobotVideoConcatInstructionsSchema = interpolateRobot( + robotVideoConcatInstructionsSchema, +) +export type InterpolatableRobotVideoConcatInstructions = z.input< + typeof interpolatableRobotVideoConcatInstructionsSchema +> diff --git a/src/alphalib/types/robots/video-encode.ts b/src/alphalib/types/robots/video-encode.ts index 92566e07..7b5630d2 100644 --- a/src/alphalib/types/robots/video-encode.ts +++ b/src/alphalib/types/robots/video-encode.ts @@ -3,14 +3,13 @@ import { z } from 'zod' import { color_with_alpha, robotFFmpegVideo, - interpolationSchemaToYieldNumber, - interpolationSchemaToYieldString, percentageSchema, positionSchema, resize_strategy, robotBase, robotUse, unsafeCoordinatesSchema, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -45,7 +44,7 @@ export const meta: RobotMeta = { uses_tools: ['ffmpeg'], } -export const robotVideoEncodeInstructionsInterpolatedSchema = robotBase +export const robotVideoEncodeInstructionsSchema = robotBase .merge(robotUse) .merge(robotFFmpegVideo) .extend({ @@ -159,21 +158,12 @@ The name used for the final segment. Available variables are \`\${segment_prefix }) .strict() -export const robotVideoEncodeInstructionsSchema = - robotVideoEncodeInstructionsInterpolatedSchema.extend({ - width: robotVideoEncodeInstructionsInterpolatedSchema.shape.width.or( - interpolationSchemaToYieldNumber, - ), - height: robotVideoEncodeInstructionsInterpolatedSchema.shape.height.or( - interpolationSchemaToYieldNumber, - ), - background: robotVideoEncodeInstructionsInterpolatedSchema.shape.background.or( - interpolationSchemaToYieldString, - ), - resize_strategy: robotVideoEncodeInstructionsInterpolatedSchema.shape.resize_strategy.or( - interpolationSchemaToYieldString, - ), - }) - export type RobotVideoEncodeInstructions = z.infer export type RobotVideoEncodeInstructionsInput = z.input + +export const interpolatableRobotVideoEncodeInstructionsSchema = interpolateRobot( + robotVideoEncodeInstructionsSchema, +) +export type InterpolatableRobotVideoEncodeInstructions = z.input< + typeof interpolatableRobotVideoEncodeInstructionsSchema +> diff --git a/src/alphalib/types/robots/video-merge.ts b/src/alphalib/types/robots/video-merge.ts index 80568945..805893e6 100644 --- a/src/alphalib/types/robots/video-merge.ts +++ b/src/alphalib/types/robots/video-merge.ts @@ -6,6 +6,7 @@ import { resize_strategy, robotBase, robotUse, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -70,3 +71,10 @@ Stacks the input media vertically. All streams need to have the same pixel forma }) .strict() export type RobotVideoMergeInstructions = z.infer + +export const interpolatableRobotVideoMergeInstructionsSchema = interpolateRobot( + robotVideoMergeInstructionsSchema, +) +export type InterpolatableRobotVideoMergeInstructions = z.input< + typeof interpolatableRobotVideoMergeInstructionsSchema +> diff --git a/src/alphalib/types/robots/video-ondemand.ts b/src/alphalib/types/robots/video-ondemand.ts new file mode 100644 index 00000000..42c3ec57 --- /dev/null +++ b/src/alphalib/types/robots/video-ondemand.ts @@ -0,0 +1,116 @@ +import { z } from 'zod' + +import type { RobotMeta } from './_instructions-primitives.ts' +import { + interpolateRobot, + robotBase, + robotUse, + robotFFmpegVideo, +} from './_instructions-primitives.ts' + +export const meta: RobotMeta = { + allowed_for_url_transform: true, + discount_factor: 1, + discount_pct: 0, + bytescount: 1, + docs_redirect_from: ['/docs/video-ondemand/'], + example_code: { + steps: { + import: { + robot: '/s3/import', + path: '${fields.input}', + credentials: 'YOUR_AWS_CREDENTIALS', + return_file_stubs: true, + }, + vod: { + robot: '/video/ondemand', + use: 'import', + variants: { + '480p': { + preset: 'hls/480p', + ffmpeg_stack: '{{ stacks.ffmpeg.recommended_version }}', + }, + '720p': { + preset: 'hls/720p', + ffmpeg_stack: '{{ stacks.ffmpeg.recommended_version }}', + }, + '1080p': { + preset: 'hls/1080p', + ffmpeg_stack: '{{ stacks.ffmpeg.recommended_version }}', + }, + }, + }, + serve: { + use: 'vod', + robot: '/file/serve', + }, + }, + }, + example_code_description: + 'Enable streaming of a video stored on S3 in three variants (480p, 720p, 1080p) with on-demand encoding:', + minimum_charge: 0, + output_factor: 0.6, + override_lvl1: 'Video Encoding', + purpose_sentence: + 'generates HTTP Live Streaming (HLS) playlists and segments on-demand for adaptive and cost-efficient playback', + purpose_verb: 'stream', + purpose_word: 'stream', + purpose_words: 'Stream videos with on-demand encoding', + service_slug: 'video-encoding', + slot_count: 60, + title: 'Stream videos with on-demand encoding', + typical_file_size_mb: 300, + typical_file_type: 'video', +} + +export const robotVideoOndemandInstructionsSchema = robotBase + .merge(robotUse) + .extend({ + robot: z.literal('/video/ondemand'), + variants: z + .record(robotFFmpegVideo) + .describe( + 'Defines the variants the video player can choose from. The keys are the names of the variant as they will appear in the generated playlists and URLs.', + ), + enabled_variants: z + .union([z.string(), z.array(z.string())]) + .optional() + .describe( + 'Specifies which variants, defined in the variants parameter, are enabled. Non-enabled variants will not be included in the master playlist.', + ), + segment_duration: z + .number() + .optional() + .default(6) + .describe('The duration of each segment in seconds.'), + sign_urls_for: z + .number() + .optional() + .default(0) + .describe( + 'When signing URLs is enabled, the URLs in the generated playlist files will be signed. This parameter specifies the duration (in seconds) that the signed URLs will remain valid.', + ), + asset: z + .string() + .optional() + .describe( + 'Controls which file is generated. For example, if the parameter is unset, a master playlist referencing the variants is generated.', + ), + asset_param_name: z + .string() + .optional() + .default('asset') + .describe( + 'Specifies from which URL parameter the asset parameter value is taken and which URL parameter to use when generating playlist files.', + ), + }) + .strict() + +export type RobotVideoOndemandInstructions = z.infer + +export const interpolatableRobotVideoOndemandInstructionsSchema = interpolateRobot( + robotVideoOndemandInstructionsSchema, +) +export type InterpolatableRobotVideoOndemandInstructions = z.input< + typeof interpolatableRobotVideoOndemandInstructionsSchema +> diff --git a/src/alphalib/types/robots/video-subtitle.ts b/src/alphalib/types/robots/video-subtitle.ts index bd800138..da1b88db 100644 --- a/src/alphalib/types/robots/video-subtitle.ts +++ b/src/alphalib/types/robots/video-subtitle.ts @@ -7,6 +7,7 @@ import { positionSchema, robotBase, robotUse, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -84,3 +85,10 @@ Specifies the position of the subtitles. }) .strict() export type RobotVideoSubtitleInstructions = z.infer + +export const interpolatableRobotVideoSubtitleInstructionsSchema = interpolateRobot( + robotVideoSubtitleInstructionsSchema, +) +export type InterpolatableRobotVideoSubtitleInstructions = z.input< + typeof interpolatableRobotVideoSubtitleInstructionsSchema +> diff --git a/src/alphalib/types/robots/video-thumbs.ts b/src/alphalib/types/robots/video-thumbs.ts index 678faa38..e7202695 100644 --- a/src/alphalib/types/robots/video-thumbs.ts +++ b/src/alphalib/types/robots/video-thumbs.ts @@ -7,6 +7,7 @@ import { resize_strategy, robotBase, robotUse, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' import { stackVersions } from '../stackVersions.ts' @@ -48,7 +49,8 @@ export const robotVideoThumbsInstructionsSchema = robotBase .merge(robotFFmpeg) .extend({ robot: z.literal('/video/thumbs').describe(` -**Note:** Even though thumbnails are extracted from videos in parallel, we sort the thumbnails before adding them to the Assembly results. So the order in which they appear there reflects the order in which they appear in the video. You can also make sure by checking the thumb_index meta key. [{.alert .alert-note}] +> [!Note] +> Even though thumbnails are extracted from videos in parallel, we sort the thumbnails before adding them to the Assembly results. So the order in which they appear there reflects the order in which they appear in the video. You can also make sure by checking the thumb_index meta key. `), count: z.number().int().min(1).max(999).default(8).describe(` The number of thumbnails to be extracted. As some videos have incorrect durations, the actual number of thumbnails generated may be less in rare cases. The maximum number of thumbnails we currently allow is 999. @@ -86,3 +88,10 @@ Forces the video to be rotated by the specified degree integer. Currently, only .strict() export type RobotVideoThumbsInstructions = z.infer + +export const interpolatableRobotVideoThumbsInstructionsSchema = interpolateRobot( + robotVideoThumbsInstructionsSchema, +) +export type InterpolatableRobotVideoThumbsInstructions = z.input< + typeof interpolatableRobotVideoThumbsInstructionsSchema +> diff --git a/src/alphalib/types/robots/vimeo-store.ts b/src/alphalib/types/robots/vimeo-store.ts index af4369cb..7b5961ec 100644 --- a/src/alphalib/types/robots/vimeo-store.ts +++ b/src/alphalib/types/robots/vimeo-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -82,3 +82,10 @@ When visiting one of your folders, the URL is similar to \`https://vimeo.com/man export type RobotVimeoStoreInstructions = z.infer export type RobotVimeoStoreInstructionsInput = z.input + +export const interpolatableRobotVimeoStoreInstructionsSchema = interpolateRobot( + robotVimeoStoreInstructionsSchema, +) +export type InterpolatableRobotVimeoStoreInstructions = z.input< + typeof interpolatableRobotVimeoStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/wasabi-import.ts b/src/alphalib/types/robots/wasabi-import.ts index 305fd0b2..2383503f 100644 --- a/src/alphalib/types/robots/wasabi-import.ts +++ b/src/alphalib/types/robots/wasabi-import.ts @@ -7,7 +7,9 @@ import { path, recursive, robotBase, + return_file_stubs, wasabiBase, + interpolateRobot, } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' @@ -77,8 +79,16 @@ When doing big imports, make sure no files are added or removed from other scrip files_per_page: files_per_page.describe(` The pagination page size. This only works when recursive is \`true\` for now, in order to not break backwards compatibility in non-recursive imports. `), + return_file_stubs, }) .strict() export type RobotWasabiImportInstructions = z.infer export type RobotWasabiImportInstructionsInput = z.input + +export const interpolatableRobotWasabiImportInstructionsSchema = interpolateRobot( + robotWasabiImportInstructionsSchema, +) +export type InterpolatableRobotWasabiImportInstructions = z.input< + typeof interpolatableRobotWasabiImportInstructionsSchema +> diff --git a/src/alphalib/types/robots/wasabi-store.ts b/src/alphalib/types/robots/wasabi-store.ts index a176dc23..a7052a31 100644 --- a/src/alphalib/types/robots/wasabi-store.ts +++ b/src/alphalib/types/robots/wasabi-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse, wasabiBase } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse, wasabiBase } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -60,3 +60,10 @@ This parameter provides signed URLs in the result JSON (in the \`signed_ssl_url\ export type RobotWasabiStoreInstructions = z.infer export type RobotWasabiStoreInstructionsInput = z.input + +export const interpolatableRobotWasabiStoreInstructionsSchema = interpolateRobot( + robotWasabiStoreInstructionsSchema, +) +export type InterpolatableRobotWasabiStoreInstructions = z.input< + typeof interpolatableRobotWasabiStoreInstructionsSchema +> diff --git a/src/alphalib/types/robots/youtube-store.ts b/src/alphalib/types/robots/youtube-store.ts index 9367b0c9..80c4bb48 100644 --- a/src/alphalib/types/robots/youtube-store.ts +++ b/src/alphalib/types/robots/youtube-store.ts @@ -1,6 +1,6 @@ import { z } from 'zod' -import { robotBase, robotUse } from './_instructions-primitives.ts' +import { interpolateRobot, robotBase, robotUse } from './_instructions-primitives.ts' import type { RobotMeta } from './_instructions-primitives.ts' export const meta: RobotMeta = { @@ -42,7 +42,8 @@ export const robotYoutubeStoreInstructionsSchema = robotBase .merge(robotUse) .extend({ robot: z.literal('/youtube/store').describe(` -**Note:** This Robot only accepts videos. [{.alert .alert-note}] +> [!Note] +> This Robot only accepts videos. ## Installation @@ -106,3 +107,10 @@ Defines the visibility of the uploaded video. export type RobotYoutubeStoreInstructions = z.infer export type RobotYoutubeStoreInstructionsInput = z.input + +export const interpolatableRobotYoutubeStoreInstructionsSchema = interpolateRobot( + robotYoutubeStoreInstructionsSchema, +) +export type InterpolatableRobotYoutubeStoreInstructions = z.input< + typeof interpolatableRobotYoutubeStoreInstructionsSchema +> diff --git a/src/alphalib/types/stackVersions.ts b/src/alphalib/types/stackVersions.ts index 307e772b..79f88c3e 100644 --- a/src/alphalib/types/stackVersions.ts +++ b/src/alphalib/types/stackVersions.ts @@ -1,8 +1,8 @@ export const stackVersions = { ffmpeg: { recommendedVersion: 'v6' as const, - test: /^v?[56](\.\d+)?(\.\d+)?$/, - suggestedValues: ['v5', 'v6'] as const, + test: /^v?[567](\.\d+)?(\.\d+)?$/, + suggestedValues: ['v5', 'v6', 'v7'] as const, }, imagemagick: { recommendedVersion: 'v3' as const, diff --git a/src/alphalib/types/template.ts b/src/alphalib/types/template.ts index f305cac2..4e585d6d 100644 --- a/src/alphalib/types/template.ts +++ b/src/alphalib/types/template.ts @@ -15,6 +15,7 @@ export type StepInput = z.input export type StepInputWithUse = StepInput & RobotUse export type Steps = z.infer export type StepsInput = z.input +const optionalStepsSchema = stepsSchema.optional() export const stepSchemaWithHiddenFields = z .object({ @@ -63,7 +64,8 @@ export const assemblyInstructionsSchema = z.object({ .describe( 'Set this to true to reduce the response from an Assembly POST request to only the necessary fields. This prevents any potentially confidential information being leaked to the end user who is making the Assembly request. A successful Assembly will only include the ok and assembly_id fields. An erroneous Assembly will only include the error, http_code, message and assembly_id fields. The full Assembly Status will then still be sent to the notify_url if one was specified.', ), - steps: stepsSchema.optional(), + // This is done to avoid heavy inference cost + steps: optionalStepsSchema as typeof optionalStepsSchema, template_id: z.string().optional().describe('The Template ID to use'), }) diff --git a/src/alphalib/zodParseWithContext.ts b/src/alphalib/zodParseWithContext.ts index ad91b42c..dca2af55 100644 --- a/src/alphalib/zodParseWithContext.ts +++ b/src/alphalib/zodParseWithContext.ts @@ -16,7 +16,7 @@ function getByPath(obj: unknown, path: string): unknown { return current } -type ZodParseWithContextResult = { +export type ZodParseWithContextResult = { errors: ZodIssueWithContext[] humanReadable: string } & ( @@ -42,6 +42,7 @@ export function zodParseWithContext( for (const zodIssue of zodRes.error.errors) { const lastPath = zodIssue.path let parentObj: unknown = {} + if (lastPath) { const strPath = lastPath.slice(0, -1).join('.') parentObj = getByPath(obj, strPath) ?? {} @@ -56,50 +57,145 @@ export function zodParseWithContext( const messages: string[] = [] - // Handle union type validation errors - if ('unionErrors' in zodIssue && zodIssue.unionErrors) { - const validValues: (string | number | boolean)[] = [] + // --- Handle specific high-priority codes BEFORE union/switch --- + if (zodIssue.code === 'unrecognized_keys') { + const maxKeysToShow = 3 + const { keys } = zodIssue + const truncatedKeys = keys.slice(0, maxKeysToShow) + const ellipsis = keys.length > maxKeysToShow ? '...' : '' + let message = `has unrecognized keys: ${truncatedKeys.map((k) => `\`${k}\``).join(', ')}${ellipsis}` + // Add hint for root-level unrecognized keys, likely from union .strict() failures + if (zodIssue.path.length === 0) { + message += + ' (Hint: No union variant matched. Check for extra keys or type mismatches in variants.)' + } + messages.push(message) + } + // --- End high-priority handling --- + + // Handle union type validation errors (only if not handled above) + else if ('unionErrors' in zodIssue && zodIssue.unionErrors) { + // --- Moved initialization out of the loop --- + const collectedLiterals: Record = {} + const collectedMessages: Record = {} + + // Process nested issues within the union for (const unionError of zodIssue.unionErrors) { - if ( - Array.isArray(unionError.errors) && - unionError.errors[0]?.code === 'invalid_literal' - ) { - const { expected } = unionError.errors[0] - if ( - expected !== undefined && - expected !== null && - (typeof expected === 'string' || - typeof expected === 'number' || - typeof expected === 'boolean') - ) { - validValues.push(expected) + for (const issue of unionError.issues) { + // console.log('---- Zod Union Issue ----\\n', JSON.stringify(issue, null, 2)) + const nestedPath = issue.path.join('.') + + // Ensure paths exist in collection maps + if (!collectedLiterals[nestedPath]) collectedLiterals[nestedPath] = [] + if (!collectedMessages[nestedPath]) collectedMessages[nestedPath] = [] + + if (issue.code === 'invalid_literal') { + const { expected } = issue + if ( + expected !== undefined && + expected !== null && + (typeof expected === 'string' || + typeof expected === 'number' || + typeof expected === 'boolean') + ) { + collectedLiterals[nestedPath].push(expected) + } + // Still add the raw message for fallback + collectedMessages[nestedPath].push(issue.message) + } + // Keep existing enum handling if needed, but literal should cover most cases + else if (issue.code === 'invalid_enum_value') { + const { options } = issue + if (options && options.length > 0) { + collectedLiterals[nestedPath].push(...options.map(String)) // Assuming options are compatible + } + collectedMessages[nestedPath].push(issue.message) + } + // Keep existing unrecognized keys handling + else if (issue.code === 'unrecognized_keys') { + const maxKeysToShow = 3 + const { keys } = issue + const truncatedKeys = keys.slice(0, maxKeysToShow) + const ellipsis = keys.length > maxKeysToShow ? '...' : '' + collectedMessages[nestedPath].push( + `has unrecognized keys: ${truncatedKeys.map((k) => `\`${k}\``).join(', ')}${ellipsis}`, + ) + } + // <-- Add handling for invalid_type here --> + else if (issue.code === 'invalid_type') { + const received = issue.received === 'undefined' ? 'missing' : issue.received + // Get the actual value for context + const actualValue = getByPath(parentObj, nestedPath) // Get value from parent context + const actualValueStr = + typeof actualValue === 'object' && actualValue !== null + ? JSON.stringify(actualValue) + : String(actualValue) + // Simple message not relying on issue.expected + collectedMessages[nestedPath].push( + `got invalid type: ${received} (value: \`${actualValueStr}\`, expected: ${issue.expected})`, + ) + } + // <-- End added handling --> + else { + collectedMessages[nestedPath].push(issue.message) // Handle other nested codes } } } - if (validValues.length > 0) { - messages.push(`should be one of: \`${validValues.join('`, `')}\``) - } else { - for (const unionError of zodIssue.unionErrors) { - if ('expected' in unionError && typeof unionError.expected === 'string') { - messages.push(`should be ${unionError.expected}`) - } else { - messages.push(unionError.message) - } + + // --- Moved processing logic here --- + // Now, add messages to badPaths based on collected info AFTER processing ALL union errors + for (const nestedPath in collectedMessages) { + if (!badPaths.has(nestedPath)) { + badPaths.set(nestedPath, []) + } + const targetMessages = badPaths.get(nestedPath)! + + // Prioritize more specific messages (like invalid type with details) + const invalidTypeMessages = collectedMessages[nestedPath].filter((m) => + m.startsWith('got invalid type:'), + ) + const unrecognizedKeyMessages = collectedMessages[nestedPath].filter((m) => + m.startsWith('has unrecognized keys:'), + ) + const literalMessages = collectedLiterals[nestedPath] ?? [] + + if (invalidTypeMessages.length > 0) { + targetMessages.push(...invalidTypeMessages) + } else if (unrecognizedKeyMessages.length > 0) { + targetMessages.push(...unrecognizedKeyMessages) + } else if (literalMessages.length > 0) { + const uniqueLiterals = [...new Set(literalMessages)] + targetMessages.push(`should be one of: \`${uniqueLiterals.join('`, `')}\``) + } else { + // Fallback to joining the collected raw messages for this path + targetMessages.push(...collectedMessages[nestedPath]) } } - } else if ('expected' in zodIssue && typeof zodIssue.expected === 'string') { - messages.push(`should be ${zodIssue.expected}`) - } else { + + // Prevent the main `messages` array from being populated further for this union issue + continue // Skip adding messages directly from the top-level union issue itself + } + // Handle other specific error codes (only if not handled above) + else { // Handle specific error codes for better messages let received: string let type: string let bigType: string switch (zodIssue.code) { - case 'invalid_type': + case 'invalid_type': { received = zodIssue.received === 'undefined' ? 'missing' : zodIssue.received - messages.push(`should be ${zodIssue.expected} but got ${received}`) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const actualValue = getByPath(obj, path) as any + const actualValueStr = + typeof actualValue === 'object' && actualValue !== null + ? JSON.stringify(actualValue) + : String(actualValue) // Use String() for null/primitives + + // Simple message not relying on zodIssue.expected + messages.push(`got invalid type: ${received} (value: \`${actualValueStr}\`)`) break + } case 'invalid_string': if (zodIssue.validation === 'email') { messages.push('should be a valid email address') @@ -125,10 +221,17 @@ export function zodParseWithContext( } } - badPaths.get(path)?.push(...messages) + // Ensure messages collected directly in the `messages` array (e.g., from the switch) + // are added to the correct path in badPaths. + if (messages.length > 0) { + badPaths.get(path)?.push(...messages) + } const field = path || 'Input' - const humanReadable = `Path \`${field}\` ${messages.join(', ')}` + // Ensure humanReadable for the individual ZodIssueWithContext is still generated correctly + // even if messages array is empty because handled via badPaths/nested issues. + const issueSpecificMessages = badPaths.get(path) ?? messages + const humanReadable = `Path \`${field}\` ${issueSpecificMessages.join(', ')}` zodIssuesWithContext.push({ ...zodIssue, @@ -137,13 +240,16 @@ export function zodParseWithContext( }) } - const humanReadable = Array.from(badPaths.entries()) + // Improved formatting for the top-level humanReadable string + const errorList = Array.from(badPaths.entries()) .map(([path, messages]) => { const field = path || 'Input' - return `Path \`${field}\` ${messages.join(', ')}` + return ` - \`${field}\`: ${messages.join(', ')}` // Format as list item }) .join('\n') + const humanReadable = `Validation failed for the following fields:\n${errorList}` // Add header + return { success: false, errors: zodIssuesWithContext, humanReadable } } diff --git a/src/tus.ts b/src/tus.ts index 3fb3a5ae..a1981517 100644 --- a/src/tus.ts +++ b/src/tus.ts @@ -95,6 +95,11 @@ export async function sendTusRequest({ const filename = path ? basename(path) : label await new Promise((resolve, reject) => { + if (!assembly.assembly_ssl_url) { + reject(new Error('assembly_ssl_url is not present in the assembly status')) + return + } + const tusOptions: UploadOptions = { endpoint: assembly.tus_url, metadata: { diff --git a/test/integration/live-api.test.ts b/test/integration/live-api.test.ts index d28c4816..86641922 100644 --- a/test/integration/live-api.test.ts +++ b/test/integration/live-api.test.ts @@ -186,7 +186,8 @@ describe('API integration', { timeout: 60000 }, () => { const id = result.assembly_id - result = await client.getAssembly(id) + expect(id).toBeDefined() + result = await client.getAssembly(id!) expect(result).not.toHaveProperty('error') expect(result).toEqual( expect.objectContaining({ @@ -231,7 +232,7 @@ describe('API integration', { timeout: 60000 }, () => { } const result = await createAssembly(client, params) - expect(result.uploads[0].field).toBe('original') + expect(result.uploads?.[0]?.field).toBe('original') }) it('should allow setting fields', async () => { @@ -244,9 +245,9 @@ describe('API integration', { timeout: 60000 }, () => { steps: { resize: resizeOriginalStep }, }, }) - expect(result.fields.myField).toBe('test') - expect(result.fields.num).toBe(1) - expect(result.fields.obj).toStrictEqual({ foo: 'bar' }) + expect(result.fields?.myField).toBe('test') + expect(result.fields?.num).toBe(1) + expect(result.fields?.obj).toStrictEqual({ foo: 'bar' }) }) it('should allow adding different types', async () => { @@ -287,7 +288,9 @@ describe('API integration', { timeout: 60000 }, () => { original_md5hash: '1b199e02dd833b2278ce2a0e75480b14', }) // Because order is not same as input - const uploadsMap = Object.fromEntries(result.uploads.map((upload) => [upload.name, upload])) + const uploadsMap = Object.fromEntries( + result.uploads?.map((upload) => [upload.name, upload]) ?? [] + ) expect(uploadsMap).toEqual({ file1: expect.objectContaining(getMatchObject({ name: 'file1' })), file2: expect.objectContaining(getMatchObject({ name: 'file2' })), @@ -466,7 +469,8 @@ describe('API integration', { timeout: 60000 }, () => { const awaitCompletionPromise = (async () => { try { - const ret = await client.awaitAssemblyCompletion(id) + expect(id).toBeDefined() + const ret = await client.awaitAssemblyCompletion(id!) return ret } catch (err) { // eslint-disable-next-line no-console @@ -477,8 +481,9 @@ describe('API integration', { timeout: 60000 }, () => { // Now delete it before uploading is done // console.log('canceling', id) - const resp = await client.cancelAssembly(id) - expect(resp.ok).toBe('ASSEMBLY_CANCELED') + expect(id).toBeDefined() + const resp = await client.cancelAssembly(id!) + expect((resp as Extract).ok).toBe('ASSEMBLY_CANCELED') // console.log('canceled', id) // Allow the upload to finish @@ -487,13 +492,22 @@ describe('API integration', { timeout: 60000 }, () => { // Successful cancel requests get ASSEMBLY_CANCELED even when it // completed, so we now request the assembly status to check the // *actual* status. - const resp2 = await client.getAssembly(id) + expect(id).toBeDefined() + const resp2 = await client.getAssembly(id!) console.log(`Expect Assembly ${id} to return 'ASSEMBLY_CANCELED'`) - expect(resp2.ok).toBe('ASSEMBLY_CANCELED') + expect((resp2 as Extract).ok).toBe('ASSEMBLY_CANCELED') // Check that awaitAssemblyCompletion gave the correct response too const awaitCompletionResponse = await awaitCompletionPromise - expect(awaitCompletionResponse?.ok).toBe('ASSEMBLY_CANCELED') + expect(awaitCompletionResponse).toBeDefined() // Ensure it's not null + if (awaitCompletionResponse) { + // Type guard + expect( + (awaitCompletionResponse as Extract).ok + ).toBe('ASSEMBLY_CANCELED') + } else { + throw new Error('awaitCompletionResponse was null or undefined') + } } finally { server.close() } @@ -505,15 +519,18 @@ describe('API integration', { timeout: 60000 }, () => { const client = createClient() const createdAssembly = await createAssembly(client, genericOptions) - - const replayedAssembly = await client.replayAssembly(createdAssembly.assembly_id) - expect(replayedAssembly.ok).toBe('ASSEMBLY_REPLAYING') + expect(createdAssembly.assembly_id).toBeDefined() + const replayedAssembly = await client.replayAssembly(createdAssembly.assembly_id!) + expect((replayedAssembly as Extract).ok).toBe( + 'ASSEMBLY_REPLAYING' + ) expect(replayedAssembly.assembly_id).not.toEqual(createdAssembly.assembly_id) expect(replayedAssembly.assembly_url).toBeDefined() expect(replayedAssembly.assembly_ssl_url).toBeDefined() - const result2 = await client.awaitAssemblyCompletion(replayedAssembly.assembly_id) - expect(result2.ok).toBe('ASSEMBLY_COMPLETED') + expect(replayedAssembly.assembly_id).toBeDefined() + const result2 = await client.awaitAssemblyCompletion(replayedAssembly.assembly_id!) + expect((result2 as Extract).ok).toBe('ASSEMBLY_COMPLETED') }) }) diff --git a/test/unit/mock-http.test.ts b/test/unit/mock-http.test.ts index 98dce408..f7a715a2 100644 --- a/test/unit/mock-http.test.ts +++ b/test/unit/mock-http.test.ts @@ -69,7 +69,7 @@ describe('Mocked API tests', () => { await client.createAssembly() const result = await client.awaitAssemblyCompletion('1') - expect(result.ok).toBe('REQUEST_ABORTED') + expect((result as Extract).ok).toBe('REQUEST_ABORTED') scope.done() }) @@ -105,7 +105,6 @@ describe('Mocked API tests', () => { }) expect(await client.getAssembly('1')).toMatchObject({ - // @ts-expect-error todo error: 'INVALID_FILE_META_DATA', message: 'Invalid file metadata', }) diff --git a/yarn.lock b/yarn.lock index 11f66b84..b35b56c3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2312,9 +2312,9 @@ __metadata: languageName: node linkType: hard -"@vitest/coverage-v8@npm:^3.1.1": - version: 3.1.1 - resolution: "@vitest/coverage-v8@npm:3.1.1" +"@vitest/coverage-v8@npm:^3.1.3": + version: 3.1.3 + resolution: "@vitest/coverage-v8@npm:3.1.3" dependencies: "@ampproject/remapping": "npm:^2.3.0" "@bcoe/v8-coverage": "npm:^1.0.2" @@ -2325,36 +2325,36 @@ __metadata: istanbul-reports: "npm:^3.1.7" magic-string: "npm:^0.30.17" magicast: "npm:^0.3.5" - std-env: "npm:^3.8.1" + std-env: "npm:^3.9.0" test-exclude: "npm:^7.0.1" tinyrainbow: "npm:^2.0.0" peerDependencies: - "@vitest/browser": 3.1.1 - vitest: 3.1.1 + "@vitest/browser": 3.1.3 + vitest: 3.1.3 peerDependenciesMeta: "@vitest/browser": optional: true - checksum: 10c0/0f852d8a438d27605955f2a1177e017f48b0dcdc7069318b2b1e031e3561d02a54e4d9a108afacbc8365c8b42f4bcb13282ae7cfaf380bce27741991321e83d9 + checksum: 10c0/82b5c33ae258832d98d42f24402708bc89dcf7e7c21a9e869dca3860040f57dbefc1bcdea0648e1683e6d07a03fe953c17b99981905be7b95a67bceffea52e68 languageName: node linkType: hard -"@vitest/expect@npm:3.1.1": - version: 3.1.1 - resolution: "@vitest/expect@npm:3.1.1" +"@vitest/expect@npm:3.1.3": + version: 3.1.3 + resolution: "@vitest/expect@npm:3.1.3" dependencies: - "@vitest/spy": "npm:3.1.1" - "@vitest/utils": "npm:3.1.1" + "@vitest/spy": "npm:3.1.3" + "@vitest/utils": "npm:3.1.3" chai: "npm:^5.2.0" tinyrainbow: "npm:^2.0.0" - checksum: 10c0/ef4528d0ebb89eb3cc044cf597d051c35df8471bb6ba4029e9b3412aa69d0d85a0ce4eb49531fc78fe1ebd97e6428260463068cc96a8d8c1a80150dedfd1ab3a + checksum: 10c0/3a61e5526ed57491c9c230cb592849a2c15e6b4376bfaec4f623ac75fdcf5c24c322949cfb5362136fc8be5eb19be88d094917ea5f700bd3da0ea0c68ee4a8d9 languageName: node linkType: hard -"@vitest/mocker@npm:3.1.1": - version: 3.1.1 - resolution: "@vitest/mocker@npm:3.1.1" +"@vitest/mocker@npm:3.1.3": + version: 3.1.3 + resolution: "@vitest/mocker@npm:3.1.3" dependencies: - "@vitest/spy": "npm:3.1.1" + "@vitest/spy": "npm:3.1.3" estree-walker: "npm:^3.0.3" magic-string: "npm:^0.30.17" peerDependencies: @@ -2365,57 +2365,57 @@ __metadata: optional: true vite: optional: true - checksum: 10c0/9264558809e2d7c77ae9ceefad521dc5f886a567aaf0bdd021b73089b8906ffd92c893f3998d16814f38fc653c7413836f508712355c87749a0e86c7d435eec1 + checksum: 10c0/6e6a62e27aa6cd146d14ae64eb9acfc0f49e7479ca426af1fb4df362456aa3456abf29731247659032e4bfb7ac9482fca1d1c7e1501e1a186eb211221e1f613a languageName: node linkType: hard -"@vitest/pretty-format@npm:3.1.1, @vitest/pretty-format@npm:^3.1.1": - version: 3.1.1 - resolution: "@vitest/pretty-format@npm:3.1.1" +"@vitest/pretty-format@npm:3.1.3, @vitest/pretty-format@npm:^3.1.3": + version: 3.1.3 + resolution: "@vitest/pretty-format@npm:3.1.3" dependencies: tinyrainbow: "npm:^2.0.0" - checksum: 10c0/540cd46d317fc80298c93b185f3fb48dfe90eaaa3942fd700fde6e88d658772c01b56ad5b9b36e4ac368a02e0fc8e0dc72bbdd6dd07a5d75e89ef99c8df5ba6e + checksum: 10c0/eba164d2c0b2babbcf6bb054da3b326d08cc3a0289ade3c64309bfe5e7c3124cd4d45a60b2f673cf4f5b3a97381fb7af7009780a5d9665afdf7f8263fa34c068 languageName: node linkType: hard -"@vitest/runner@npm:3.1.1": - version: 3.1.1 - resolution: "@vitest/runner@npm:3.1.1" +"@vitest/runner@npm:3.1.3": + version: 3.1.3 + resolution: "@vitest/runner@npm:3.1.3" dependencies: - "@vitest/utils": "npm:3.1.1" + "@vitest/utils": "npm:3.1.3" pathe: "npm:^2.0.3" - checksum: 10c0/35a541069c3c94a2dd02fca2d70cc8d5e66ba2e891cfb80da354174f510aeb96774ffb34fff39cecde9d5c969be4dd20e240a900beb9b225b7512a615ecc5503 + checksum: 10c0/f03c26e72657242ce68a93b46ee8a4e6fa1a290850be608988622a3efef744ffadc0436123acafe61977608b287b1637f4f781d27107ee0c33937c54f547159d languageName: node linkType: hard -"@vitest/snapshot@npm:3.1.1": - version: 3.1.1 - resolution: "@vitest/snapshot@npm:3.1.1" +"@vitest/snapshot@npm:3.1.3": + version: 3.1.3 + resolution: "@vitest/snapshot@npm:3.1.3" dependencies: - "@vitest/pretty-format": "npm:3.1.1" + "@vitest/pretty-format": "npm:3.1.3" magic-string: "npm:^0.30.17" pathe: "npm:^2.0.3" - checksum: 10c0/43e5fc5db580f20903eb1493d07f08752df8864f7b9b7293a202b2ffe93d8c196a5614d66dda096c6bacc16e12f1836f33ba41898812af6d32676d1eb501536a + checksum: 10c0/60b70c1d878c3d9a4fe3464d14be2318a7a3be24131beb801712735d5dcbc7db7b798f21c98c6fbad4998554992038b29655e1b6e2503242627f203fd89c97c3 languageName: node linkType: hard -"@vitest/spy@npm:3.1.1": - version: 3.1.1 - resolution: "@vitest/spy@npm:3.1.1" +"@vitest/spy@npm:3.1.3": + version: 3.1.3 + resolution: "@vitest/spy@npm:3.1.3" dependencies: tinyspy: "npm:^3.0.2" - checksum: 10c0/896659d4b42776cfa2057a1da2c33adbd3f2ebd28005ca606d1616d08d2e726dc1460fb37f1ea7f734756b5bccf926c7165f410e63f0a3b8d992eb5489528b08 + checksum: 10c0/6a8c187069827c56f3492f212ccf76c797fe52392849948af736a0f579e4533fa91041d829e2574b252af4aaadec066ca0714450d6457b31526153978bc55192 languageName: node linkType: hard -"@vitest/utils@npm:3.1.1": - version: 3.1.1 - resolution: "@vitest/utils@npm:3.1.1" +"@vitest/utils@npm:3.1.3": + version: 3.1.3 + resolution: "@vitest/utils@npm:3.1.3" dependencies: - "@vitest/pretty-format": "npm:3.1.1" + "@vitest/pretty-format": "npm:3.1.3" loupe: "npm:^3.1.3" tinyrainbow: "npm:^2.0.0" - checksum: 10c0/a9cfe0c0f095b58644ce3ba08309de5be8564c10dad9e62035bd378e60b2834e6a256e6e4ded7dcf027fdc2371301f7965040ad3e6323b747d5b3abbb7ceb0d6 + checksum: 10c0/1c4ea711b87a8b2c7dc2da91f20427dccc34c0d1d0e81b8142780d24b6caa3c724e8287f7e01e9e875262b6bb912d55711fb99e66f718ba30cc21706a335829d languageName: node linkType: hard @@ -3360,10 +3360,10 @@ __metadata: languageName: node linkType: hard -"es-module-lexer@npm:^1.6.0": - version: 1.6.0 - resolution: "es-module-lexer@npm:1.6.0" - checksum: 10c0/667309454411c0b95c476025929881e71400d74a746ffa1ff4cb450bd87f8e33e8eef7854d68e401895039ac0bac64e7809acbebb6253e055dd49ea9e3ea9212 +"es-module-lexer@npm:^1.7.0": + version: 1.7.0 + resolution: "es-module-lexer@npm:1.7.0" + checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b languageName: node linkType: hard @@ -3936,7 +3936,7 @@ __metadata: languageName: node linkType: hard -"expect-type@npm:^1.2.0": +"expect-type@npm:^1.2.1": version: 1.2.1 resolution: "expect-type@npm:1.2.1" checksum: 10c0/b775c9adab3c190dd0d398c722531726cdd6022849b4adba19dceab58dda7e000a7c6c872408cd73d665baa20d381eca36af4f7b393a4ba60dd10232d1fb8898 @@ -4004,6 +4004,18 @@ __metadata: languageName: node linkType: hard +"fdir@npm:^6.4.4": + version: 6.4.4 + resolution: "fdir@npm:6.4.4" + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + checksum: 10c0/6ccc33be16945ee7bc841e1b4178c0b4cf18d3804894cb482aa514651c962a162f96da7ffc6ebfaf0df311689fb70091b04dd6caffe28d56b9ebdc0e7ccadfdd + languageName: node + linkType: hard + "figures@npm:^6.1.0": version: 6.1.0 resolution: "figures@npm:6.1.0" @@ -5882,6 +5894,13 @@ __metadata: languageName: node linkType: hard +"picomatch@npm:^4.0.2": + version: 4.0.2 + resolution: "picomatch@npm:4.0.2" + checksum: 10c0/7c51f3ad2bb42c776f49ebf964c644958158be30d0a510efd5a395e8d49cb5acfed5b82c0c5b365523ce18e6ab85013c9ebe574f60305892ec3fa8eee8304ccc + languageName: node + linkType: hard + "pidtree@npm:^0.3.0": version: 0.3.1 resolution: "pidtree@npm:0.3.1" @@ -6551,7 +6570,7 @@ __metadata: languageName: node linkType: hard -"std-env@npm:^3.8.1": +"std-env@npm:^3.9.0": version: 3.9.0 resolution: "std-env@npm:3.9.0" checksum: 10c0/4a6f9218aef3f41046c3c7ecf1f98df00b30a07f4f35c6d47b28329bc2531eef820828951c7d7b39a1c5eb19ad8a46e3ddfc7deb28f0a2f3ceebee11bab7ba50 @@ -6808,6 +6827,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.13": + version: 0.2.13 + resolution: "tinyglobby@npm:0.2.13" + dependencies: + fdir: "npm:^6.4.4" + picomatch: "npm:^4.0.2" + checksum: 10c0/ef07dfaa7b26936601d3f6d999f7928a4d1c6234c5eb36896bb88681947c0d459b7ebe797022400e555fe4b894db06e922b95d0ce60cb05fd827a0a66326b18c + languageName: node + linkType: hard + "tinypool@npm:^1.0.2": version: 1.0.2 resolution: "tinypool@npm:1.0.2" @@ -6887,7 +6916,7 @@ __metadata: "@types/temp": "npm:^0.9.4" "@typescript-eslint/eslint-plugin": "npm:^8.29.1" "@typescript-eslint/parser": "npm:^8.29.1" - "@vitest/coverage-v8": "npm:^3.1.1" + "@vitest/coverage-v8": "npm:^3.1.3" badge-maker: "npm:^4.1.0" debug: "npm:^4.4.0" eslint: "npm:8" @@ -6912,9 +6941,9 @@ __metadata: prettier: "npm:^3.5.3" temp: "npm:^0.9.4" tus-js-client: "npm:^4.3.1" - type-fest: "npm:^4.39.1" + type-fest: "npm:^4.41.0" typescript: "npm:^5.8.3" - vitest: "npm:^3.1.1" + vitest: "npm:^3.1.3" zod: "npm:^3.24.2" languageName: unknown linkType: soft @@ -6978,13 +7007,20 @@ __metadata: languageName: node linkType: hard -"type-fest@npm:^4.26.1, type-fest@npm:^4.39.1": +"type-fest@npm:^4.26.1": version: 4.39.1 resolution: "type-fest@npm:4.39.1" checksum: 10c0/f5bf302eb2e2f70658be1757aa578f4a09da3f65699b0b12b7ae5502ccea76e5124521a6e6b69540f442c3dc924c394202a2ab58718d0582725c7ac23c072594 languageName: node linkType: hard +"type-fest@npm:^4.41.0": + version: 4.41.0 + resolution: "type-fest@npm:4.41.0" + checksum: 10c0/f5ca697797ed5e88d33ac8f1fec21921839871f808dc59345c9cf67345bfb958ce41bd821165dbf3ae591cedec2bf6fe8882098dfdd8dc54320b859711a2c1e4 + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.2": version: 1.0.2 resolution: "typed-array-buffer@npm:1.0.2" @@ -7154,18 +7190,18 @@ __metadata: languageName: node linkType: hard -"vite-node@npm:3.1.1": - version: 3.1.1 - resolution: "vite-node@npm:3.1.1" +"vite-node@npm:3.1.3": + version: 3.1.3 + resolution: "vite-node@npm:3.1.3" dependencies: cac: "npm:^6.7.14" debug: "npm:^4.4.0" - es-module-lexer: "npm:^1.6.0" + es-module-lexer: "npm:^1.7.0" pathe: "npm:^2.0.3" vite: "npm:^5.0.0 || ^6.0.0" bin: vite-node: vite-node.mjs - checksum: 10c0/15ee73c472ae00f042a7cee09a31355d2c0efbb2dab160377545be9ba4b980a5f4cb2841b98319d87bedf630bbbb075e6b40796b39f65610920cf3fde66fdf8d + checksum: 10c0/d69a1e52361bc0af22d1178db61674ef768cfd3c5610733794bb1e7a36af113da287dd89662a1ad57fd4f6c3360ca99678f5428ba837f239df4091d7891f2e4c languageName: node linkType: hard @@ -7221,36 +7257,37 @@ __metadata: languageName: node linkType: hard -"vitest@npm:^3.1.1": - version: 3.1.1 - resolution: "vitest@npm:3.1.1" - dependencies: - "@vitest/expect": "npm:3.1.1" - "@vitest/mocker": "npm:3.1.1" - "@vitest/pretty-format": "npm:^3.1.1" - "@vitest/runner": "npm:3.1.1" - "@vitest/snapshot": "npm:3.1.1" - "@vitest/spy": "npm:3.1.1" - "@vitest/utils": "npm:3.1.1" +"vitest@npm:^3.1.3": + version: 3.1.3 + resolution: "vitest@npm:3.1.3" + dependencies: + "@vitest/expect": "npm:3.1.3" + "@vitest/mocker": "npm:3.1.3" + "@vitest/pretty-format": "npm:^3.1.3" + "@vitest/runner": "npm:3.1.3" + "@vitest/snapshot": "npm:3.1.3" + "@vitest/spy": "npm:3.1.3" + "@vitest/utils": "npm:3.1.3" chai: "npm:^5.2.0" debug: "npm:^4.4.0" - expect-type: "npm:^1.2.0" + expect-type: "npm:^1.2.1" magic-string: "npm:^0.30.17" pathe: "npm:^2.0.3" - std-env: "npm:^3.8.1" + std-env: "npm:^3.9.0" tinybench: "npm:^2.9.0" tinyexec: "npm:^0.3.2" + tinyglobby: "npm:^0.2.13" tinypool: "npm:^1.0.2" tinyrainbow: "npm:^2.0.0" vite: "npm:^5.0.0 || ^6.0.0" - vite-node: "npm:3.1.1" + vite-node: "npm:3.1.3" why-is-node-running: "npm:^2.3.0" peerDependencies: "@edge-runtime/vm": "*" "@types/debug": ^4.1.12 "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 - "@vitest/browser": 3.1.1 - "@vitest/ui": 3.1.1 + "@vitest/browser": 3.1.3 + "@vitest/ui": 3.1.3 happy-dom: "*" jsdom: "*" peerDependenciesMeta: @@ -7270,7 +7307,7 @@ __metadata: optional: true bin: vitest: vitest.mjs - checksum: 10c0/680f31d2a7ca59509f837acdbacd9dff405e1b00c606d7cd29717127c6b543f186055854562c2604f74c5cd668b70174968d28feb4ed948a7e013c9477a68d50 + checksum: 10c0/954b3579a2d925606df7f78e367ae64eab52c8c5ba2bb2fed94d335a06c910202a4ce080bb02d8148c8b4782488c6d229e963617be8d0c7da96a1c944dd291d7 languageName: node linkType: hard