transloadit 4.13.0 → 4.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +21 -5
  2. package/dist/Transloadit.d.ts +2 -0
  3. package/dist/Transloadit.d.ts.map +1 -1
  4. package/dist/Transloadit.js +12 -2
  5. package/dist/Transloadit.js.map +1 -1
  6. package/dist/alphalib/types/assemblyStatus.d.ts +758 -0
  7. package/dist/alphalib/types/assemblyStatus.d.ts.map +1 -1
  8. package/dist/alphalib/types/assemblyStatus.js +6 -0
  9. package/dist/alphalib/types/assemblyStatus.js.map +1 -1
  10. package/dist/alphalib/types/robots/_instructions-primitives.d.ts.map +1 -1
  11. package/dist/alphalib/types/robots/_instructions-primitives.js +2 -0
  12. package/dist/alphalib/types/robots/_instructions-primitives.js.map +1 -1
  13. package/dist/alphalib/types/robots/ai-chat.d.ts.map +1 -1
  14. package/dist/alphalib/types/robots/ai-chat.js +1 -0
  15. package/dist/alphalib/types/robots/ai-chat.js.map +1 -1
  16. package/dist/alphalib/types/robots/image-generate.js +4 -4
  17. package/dist/alphalib/types/robots/image-generate.js.map +1 -1
  18. package/dist/alphalib/types/storageAsset.d.ts +27 -1
  19. package/dist/alphalib/types/storageAsset.d.ts.map +1 -1
  20. package/dist/alphalib/types/storageAsset.js +6 -0
  21. package/dist/alphalib/types/storageAsset.js.map +1 -1
  22. package/dist/alphalib/types/templateCredential.d.ts +2 -2
  23. package/dist/cli/commands/storage.d.ts +1 -0
  24. package/dist/cli/commands/storage.d.ts.map +1 -1
  25. package/dist/cli/commands/storage.js +27 -2
  26. package/dist/cli/commands/storage.js.map +1 -1
  27. package/dist/cli/semanticIntents/imageGenerate.d.ts +2 -2
  28. package/dist/cli/semanticIntents/imageGenerate.js +4 -4
  29. package/dist/cli/semanticIntents/imageGenerate.js.map +1 -1
  30. package/dist/cli/storageReceipts.d.ts.map +1 -1
  31. package/dist/cli/storageReceipts.js +4 -1
  32. package/dist/cli/storageReceipts.js.map +1 -1
  33. package/dist/storageImage.d.ts +6 -3
  34. package/dist/storageImage.d.ts.map +1 -1
  35. package/dist/storageImage.js +15 -44
  36. package/dist/storageImage.js.map +1 -1
  37. package/package.json +1 -5
  38. package/src/Transloadit.ts +18 -2
  39. package/src/alphalib/types/assemblyStatus.ts +6 -0
  40. package/src/alphalib/types/robots/_instructions-primitives.ts +2 -0
  41. package/src/alphalib/types/robots/ai-chat.ts +1 -0
  42. package/src/alphalib/types/robots/image-generate.ts +4 -4
  43. package/src/alphalib/types/storageAsset.ts +9 -1
  44. package/src/cli/commands/storage.ts +28 -2
  45. package/src/cli/semanticIntents/imageGenerate.ts +4 -4
  46. package/src/cli/storageReceipts.ts +4 -1
  47. package/src/storageImage.ts +20 -49
@@ -1,4 +1,5 @@
1
1
  import type { AssemblyStatus } from './alphalib/types/assemblyStatus.ts'
2
+ import type { InterpolatableRobotUploadHandleInstructionsInput } from './alphalib/types/robots/upload-handle.ts'
2
3
  import type { StoredAsset } from './alphalib/types/storageAsset.ts'
3
4
  import type { CreateAssemblyOptions, Transloadit } from './Transloadit.ts'
4
5
 
@@ -17,15 +18,16 @@ import InconsistentResponseError from './InconsistentResponseError.ts'
17
18
  * Width and height reflect EXIF auto-orientation, matching Storage preview delivery.
18
19
  */
19
20
  export interface StoredImageReceipt extends Readonly<StoredAsset> {
20
- /** Present as true only when the locally decoded original has an alpha channel. */
21
+ /** Legacy catalog spelling; new server receipts use has_alpha. */
21
22
  readonly hasAlpha?: boolean
22
23
  readonly height: number
23
24
  readonly md5hash: string
24
- /** Optional base64 ThumbHash of the original pixels, for an inline blur placeholder. */
25
- readonly thumbhash?: string
26
25
  readonly width: number
27
26
  }
28
27
 
28
+ /** Shared SDK/CLI opt-in for server extraction; rendering remains a separate choice. */
29
+ export const storageImagePlaceholderSchema = z.enum(['blur', 'empty'])
30
+
29
31
  /** One explicit destination, with the existing Assembly upload and polling controls. */
30
32
  export interface StoreImageOptions
31
33
  extends Pick<
@@ -36,6 +38,8 @@ export interface StoreImageOptions
36
38
  path: string
37
39
  /** Explicit opt-in replacement of an existing path; defaults to false. */
38
40
  overwrite?: boolean
41
+ /** Request a server ThumbHash. Successful extraction adds metadata usage of 20% of the file bytes. */
42
+ placeholder?: z.infer<typeof storageImagePlaceholderSchema>
39
43
  /** Observe verified metadata. Not awaited; sync and async observer errors cannot undo a write. */
40
44
  onReceipt?: (
41
45
  receipt: StoredImageReceipt,
@@ -94,35 +98,6 @@ function validateDestination(path: string): void {
94
98
  }
95
99
  }
96
100
 
97
- async function imagePlaceholderMetadata(
98
- bytes: Buffer,
99
- ): Promise<Pick<StoredImageReceipt, 'thumbhash' | 'hasAlpha'> | undefined> {
100
- try {
101
- const [{ default: sharp }, { rgbaToThumbHash }] = await Promise.all([
102
- import('sharp'),
103
- import('thumbhash'),
104
- ])
105
- const image = sharp(bytes, { limitInputPixels: 40_000_000 })
106
- // Read original-channel metadata, not the alpha channel added for ThumbHash's RGBA input.
107
- const { hasAlpha } = await image.metadata()
108
- const { data, info } = await image
109
- .autoOrient()
110
- .resize(100, 100, { fit: 'inside', withoutEnlargement: true })
111
- .ensureAlpha()
112
- .raw()
113
- .timeout({ seconds: 2 })
114
- .toBuffer({ resolveWithObject: true })
115
- return {
116
- thumbhash: Buffer.from(rgbaToThumbHash(info.width, info.height, data)).toString('base64'),
117
- ...(hasAlpha ? { hasAlpha: true } : {}),
118
- }
119
- } catch {
120
- // A locally unsupported/oversized image can still be stored and decoded by the origin.
121
- debug('transloadit:warn')('Omitted optional ThumbHash: local image decoding was unavailable')
122
- return undefined
123
- }
124
- }
125
-
126
101
  /** Stores one original without overwriting; receipt validation occurs after the Storage write. */
127
102
  export async function storeImage(
128
103
  client: Transloadit,
@@ -140,28 +115,21 @@ export async function storeImage(
140
115
  timeout,
141
116
  } = options
142
117
  validateDestination(path)
118
+ const placeholder = storageImagePlaceholderSchema.optional().parse(options.placeholder, {
119
+ path: ['placeholder'],
120
+ })
143
121
  if (overwrite !== undefined && typeof overwrite !== 'boolean')
144
122
  throw new TypeError('overwrite must be a boolean')
145
123
  signal?.throwIfAborted()
146
124
  const checksum = createHash('md5')
147
- // Reuse the checksum read, bounded independently of file size; uploads still stream from disk.
148
- let thumbnailChunks: Buffer[] | undefined = []
149
125
  let size = 0
150
126
  for await (const chunk of createReadStream(filePath, { signal })) {
151
127
  checksum.update(chunk)
152
128
  size += chunk.length
153
- if (size > 32 * 1024 * 1024) thumbnailChunks = undefined
154
- else thumbnailChunks?.push(chunk)
155
129
  }
156
130
  if (size === 0) throw new Error('Cannot store an empty image')
157
131
  const md5hash = checksum.digest('hex')
158
132
  signal?.throwIfAborted()
159
- const placeholder =
160
- thumbnailChunks === undefined
161
- ? undefined
162
- : await imagePlaceholderMetadata(Buffer.concat(thumbnailChunks))
163
- thumbnailChunks = undefined
164
- signal?.throwIfAborted()
165
133
  const assembly = await client.createAssembly({
166
134
  chunkSize,
167
135
  files: { image: filePath },
@@ -169,6 +137,14 @@ export async function storeImage(
169
137
  onUploadProgress,
170
138
  params: {
171
139
  steps: {
140
+ ...(placeholder === 'blur'
141
+ ? {
142
+ ':original': {
143
+ robot: '/upload/handle',
144
+ output_meta: { thumbhash: true },
145
+ } satisfies InterpolatableRobotUploadHandleInstructionsInput,
146
+ }
147
+ : {}),
172
148
  stored: {
173
149
  robot: '/transloadit/store',
174
150
  use: ':original',
@@ -184,23 +160,18 @@ export async function storeImage(
184
160
  const input = { path, size, md5hash }
185
161
  // API2 can watermark uploads before Robots run; this exact write's result describes stored bytes.
186
162
  const receipt = validateReceipt(assembly, input, true)
187
- // A watermark or other origin-side rewrite makes the local pixels the wrong placeholder.
188
- const withPlaceholder =
189
- placeholder === undefined || receipt.md5hash !== md5hash || receipt.size !== size
190
- ? receipt
191
- : { ...receipt, ...placeholder }
192
163
  const observerFailed = (): void => {
193
164
  debug('transloadit:warn')('Ignored onReceipt observer failure after a completed Storage write')
194
165
  }
195
166
  try {
196
167
  // Neither a stalled observer nor its rejection may hide a successfully stored receipt.
197
- void Promise.resolve(onReceipt?.({ ...withPlaceholder }, input, assembly.assembly_id)).catch(
168
+ void Promise.resolve(onReceipt?.({ ...receipt }, input, assembly.assembly_id)).catch(
198
169
  observerFailed,
199
170
  )
200
171
  } catch {
201
172
  observerFailed()
202
173
  }
203
- return withPlaceholder
174
+ return receipt
204
175
  }
205
176
 
206
177
  /** Fetches authoritative Assembly status and applies the same validation as a local image store. */