Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | 5x 5x 5x 2x 3x 5x 2x 2x 2x 2x 2x 11x 11x 11x 1x 10x 9x 9x 6x 6x | import type { StoredAsset } from './alphalib/types/storageAsset.ts'
import type {
GetStoredAssemblyResultsOptions,
StoredAssemblyResult,
} from './alphalib/types/storageResults.ts'
import type { Transloadit } from './Transloadit.ts'
import { ApiError } from './ApiError.ts'
import { robotFileServeInstructionsSchema } from './alphalib/types/robots/file-serve.ts'
import { storedAssetSchema } from './alphalib/types/storageAsset.ts'
import {
extractStoredAssemblyResults,
storedAssemblyResultsOptionsSchema,
} from './alphalib/types/storageResults.ts'
import InconsistentResponseError from './InconsistentResponseError.ts'
/** Original-byte delivery, not a preview or transcode; authorize the application's asset first. */
export interface StoredAssetUrlOptions {
/** True uses the receipt filename; a string supplies a different attachment filename. */
download?: boolean | string
/** Maximum signed lifetime, from one second to 48 hours. Defaults to five minutes. */
lifetimeMs?: number
}
/** Signs a retained original, including non-image media, without fetching or proxying its bytes. */
export function getStoredAssetUrl(
client: Transloadit,
input: StoredAsset,
options: StoredAssetUrlOptions = {},
): string {
const asset = storedAssetSchema.parse(input)
const lifetimeMs = options.lifetimeMs ?? 5 * 60_000
if (!Number.isSafeInteger(lifetimeMs) || lifetimeMs < 1000 || lifetimeMs > 48 * 3600_000) {
throw new RangeError('lifetimeMs must be an integer between 1000 and 172800000')
}
const download = options.download === true ? asset.path.split('/').at(-1) : options.download
if (download !== undefined && download !== false) {
robotFileServeInstructionsSchema.shape.download_name.unwrap().min(1).parse(download)
Iif (typeof download !== 'string' || /[\uD800-\uDFFF]/u.test(download))
throw new TypeError('download must contain a valid Unicode filename')
}
// Bunny includes the full query in its cache key. Rotate at most once a minute, never at
// the lifetime boundary where rounding could issue an almost-expired grant.
const rotationMs = Math.min(60_000, Math.floor(lifetimeMs / 2))
const expiresAt = Math.floor(Date.now() / rotationMs) * rotationMs + lifetimeMs
return client.getSignedSmartCDNUrl({
workspace: asset.workspace,
template: 'builtin/storage-serve@0.0.3',
input: asset.asset_id,
urlParams: { v: asset.version_id, ...(typeof download === 'string' ? { download } : {}) },
expiresAt,
})
}
export type {
GetStoredAssemblyResultsOptions,
StoredAssemblyResult,
} from './alphalib/types/storageResults.ts'
/**
* Fetches a completed Assembly and verifies all retained results, including non-image media.
* The caller must first bind the Assembly to an authorized application upload. Register results
* idempotently by Assembly/step/result ID (or asset/version), not by filename or notification count.
*/
export async function getStoredAssemblyResults(
client: Transloadit,
options: GetStoredAssemblyResultsOptions,
): Promise<StoredAssemblyResult[]> {
const { assemblyId, workspace } = storedAssemblyResultsOptionsSchema.parse(options)
const assembly = await client.getAssembly(assemblyId)
if (assembly.assembly_id !== assemblyId) {
throw new InconsistentResponseError('The response did not match the requested Assembly')
}
if (typeof assembly.error === 'string') throw new ApiError({ body: assembly })
try {
return extractStoredAssemblyResults(assembly, { assemblyId, workspace })
} catch (error) {
// Keep the Node SDK error contract while sharing strict extraction with Web runtimes.
Iif (!(error instanceof Error)) throw new Error('Invalid Assembly response', { cause: error })
throw new InconsistentResponseError(error.message, { cause: error.cause ?? error })
}
}
|