transloadit 4.13.0 → 4.13.1
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.
package/README.md
CHANGED
|
@@ -138,8 +138,9 @@ Existing code/env files are never overwritten. Missing trailing directory slashe
|
|
|
138
138
|
|
|
139
139
|
Store uploads originals and appends validated receipts to `transloadit.images.json`; commit it.
|
|
140
140
|
`storage store ./images/*.jpg website/` stores shell-expanded files, checkpointing each success.
|
|
141
|
-
It prints `width={960}` (bounded by the original)
|
|
142
|
-
|
|
141
|
+
It prints `width={960}` (bounded by the original), with a filename-derived alt and a reminder.
|
|
142
|
+
SDK 4.13.1 no longer decodes images locally or generates new blur hashes; existing receipt
|
|
143
|
+
hashes still work with Viewer's `placeholder="blur"`. Uploads and dimensions are unaffected.
|
|
143
144
|
An occupied path conflicts unless `--overwrite` is explicit; prefer `--hashed` for immutable
|
|
144
145
|
filenames. Matching receipts skip repeat uploads; changed bytes get a new name.
|
|
145
146
|
Storage receipts pin the returned `asset_id` and `version_id`. The image URL selects that exact
|
|
@@ -86,7 +86,7 @@ export declare const templateCredentialsSchema: z.ZodObject<{
|
|
|
86
86
|
referer?: string | undefined;
|
|
87
87
|
};
|
|
88
88
|
name: string;
|
|
89
|
-
type: "http" | "cloudflare" | "ai" | "vimeo" | "
|
|
89
|
+
type: "http" | "cloudflare" | "ai" | "vimeo" | "azure" | "backblaze" | "companion" | "digitalocean" | "dropbox" | "ftp" | "google" | "mega" | "minio" | "rackspace" | "s3" | "sftp" | "supabase" | "swift" | "tigris" | "wasabi" | "youtube";
|
|
90
90
|
content: {};
|
|
91
91
|
}, {
|
|
92
92
|
auth: {
|
|
@@ -99,7 +99,7 @@ export declare const templateCredentialsSchema: z.ZodObject<{
|
|
|
99
99
|
referer?: string | undefined;
|
|
100
100
|
};
|
|
101
101
|
name: string;
|
|
102
|
-
type: "http" | "cloudflare" | "ai" | "vimeo" | "
|
|
102
|
+
type: "http" | "cloudflare" | "ai" | "vimeo" | "azure" | "backblaze" | "companion" | "digitalocean" | "dropbox" | "ftp" | "google" | "mega" | "minio" | "rackspace" | "s3" | "sftp" | "supabase" | "swift" | "tigris" | "wasabi" | "youtube";
|
|
103
103
|
content: {};
|
|
104
104
|
}>;
|
|
105
105
|
//# sourceMappingURL=templateCredential.d.ts.map
|
package/dist/storageImage.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { CreateAssemblyOptions, Transloadit } from './Transloadit.ts';
|
|
|
5
5
|
* Width and height reflect EXIF auto-orientation, matching Storage preview delivery.
|
|
6
6
|
*/
|
|
7
7
|
export interface StoredImageReceipt extends Readonly<StoredAsset> {
|
|
8
|
-
/**
|
|
8
|
+
/** Optional metadata indicating that the original has an alpha channel. */
|
|
9
9
|
readonly hasAlpha?: boolean;
|
|
10
10
|
readonly height: number;
|
|
11
11
|
readonly md5hash: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storageImage.d.ts","sourceRoot":"","sources":["../src/storageImage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAY1E;;;GAGG;AACH,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAAC,WAAW,CAAC;IAC/D,
|
|
1
|
+
{"version":3,"file":"storageImage.d.ts","sourceRoot":"","sources":["../src/storageImage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAY1E;;;GAGG;AACH,MAAM,WAAW,kBAAmB,SAAQ,QAAQ,CAAC,WAAW,CAAC;IAC/D,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,wFAAwF;IACxF,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,wFAAwF;AACxF,MAAM,WAAW,iBACf,SAAQ,IAAI,CACV,qBAAqB,EACrB,WAAW,GAAG,oBAAoB,GAAG,kBAAkB,GAAG,QAAQ,GAAG,SAAS,CAC/E;IACD,kGAAkG;IAClG,IAAI,EAAE,MAAM,CAAA;IACZ,0EAA0E;IAC1E,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,kGAAkG;IAClG,SAAS,CAAC,EAAE,CACV,OAAO,EAAE,kBAAkB,EAC3B,KAAK,EAAE,sBAAsB,EAC7B,UAAU,EAAE,MAAM,GAAG,SAAS,KAC3B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED,+FAA+F;AAC/F,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,8FAA8F;AAC9F,MAAM,WAAW,4BAA4B;IAC3C,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,sBAAsB,CAAA;CACjC;AAuCD,kGAAkG;AAClG,wBAAsB,UAAU,CAC9B,MAAM,EAAE,WAAW,EACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,iBAAiB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CA0D7B;AAED,oGAAoG;AACpG,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,kBAAkB,CAAC,CAU7B"}
|
package/dist/storageImage.js
CHANGED
|
@@ -38,33 +38,6 @@ function validateDestination(path) {
|
|
|
38
38
|
throw new TypeError('Storage path must be a complete relative filename without interpolation');
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
async function imagePlaceholderMetadata(bytes) {
|
|
42
|
-
try {
|
|
43
|
-
const [{ default: sharp }, { rgbaToThumbHash }] = await Promise.all([
|
|
44
|
-
import('sharp'),
|
|
45
|
-
import('thumbhash'),
|
|
46
|
-
]);
|
|
47
|
-
const image = sharp(bytes, { limitInputPixels: 40_000_000 });
|
|
48
|
-
// Read original-channel metadata, not the alpha channel added for ThumbHash's RGBA input.
|
|
49
|
-
const { hasAlpha } = await image.metadata();
|
|
50
|
-
const { data, info } = await image
|
|
51
|
-
.autoOrient()
|
|
52
|
-
.resize(100, 100, { fit: 'inside', withoutEnlargement: true })
|
|
53
|
-
.ensureAlpha()
|
|
54
|
-
.raw()
|
|
55
|
-
.timeout({ seconds: 2 })
|
|
56
|
-
.toBuffer({ resolveWithObject: true });
|
|
57
|
-
return {
|
|
58
|
-
thumbhash: Buffer.from(rgbaToThumbHash(info.width, info.height, data)).toString('base64'),
|
|
59
|
-
...(hasAlpha ? { hasAlpha: true } : {}),
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
// A locally unsupported/oversized image can still be stored and decoded by the origin.
|
|
64
|
-
debug('transloadit:warn')('Omitted optional ThumbHash: local image decoding was unavailable');
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
41
|
/** Stores one original without overwriting; receipt validation occurs after the Storage write. */
|
|
69
42
|
export async function storeImage(client, filePath, options) {
|
|
70
43
|
const { path, chunkSize, onAssemblyProgress, onUploadProgress, onReceipt, overwrite, signal, timeout, } = options;
|
|
@@ -73,26 +46,15 @@ export async function storeImage(client, filePath, options) {
|
|
|
73
46
|
throw new TypeError('overwrite must be a boolean');
|
|
74
47
|
signal?.throwIfAborted();
|
|
75
48
|
const checksum = createHash('md5');
|
|
76
|
-
// Reuse the checksum read, bounded independently of file size; uploads still stream from disk.
|
|
77
|
-
let thumbnailChunks = [];
|
|
78
49
|
let size = 0;
|
|
79
50
|
for await (const chunk of createReadStream(filePath, { signal })) {
|
|
80
51
|
checksum.update(chunk);
|
|
81
52
|
size += chunk.length;
|
|
82
|
-
if (size > 32 * 1024 * 1024)
|
|
83
|
-
thumbnailChunks = undefined;
|
|
84
|
-
else
|
|
85
|
-
thumbnailChunks?.push(chunk);
|
|
86
53
|
}
|
|
87
54
|
if (size === 0)
|
|
88
55
|
throw new Error('Cannot store an empty image');
|
|
89
56
|
const md5hash = checksum.digest('hex');
|
|
90
57
|
signal?.throwIfAborted();
|
|
91
|
-
const placeholder = thumbnailChunks === undefined
|
|
92
|
-
? undefined
|
|
93
|
-
: await imagePlaceholderMetadata(Buffer.concat(thumbnailChunks));
|
|
94
|
-
thumbnailChunks = undefined;
|
|
95
|
-
signal?.throwIfAborted();
|
|
96
58
|
const assembly = await client.createAssembly({
|
|
97
59
|
chunkSize,
|
|
98
60
|
files: { image: filePath },
|
|
@@ -115,21 +77,17 @@ export async function storeImage(client, filePath, options) {
|
|
|
115
77
|
const input = { path, size, md5hash };
|
|
116
78
|
// API2 can watermark uploads before Robots run; this exact write's result describes stored bytes.
|
|
117
79
|
const receipt = validateReceipt(assembly, input, true);
|
|
118
|
-
// A watermark or other origin-side rewrite makes the local pixels the wrong placeholder.
|
|
119
|
-
const withPlaceholder = placeholder === undefined || receipt.md5hash !== md5hash || receipt.size !== size
|
|
120
|
-
? receipt
|
|
121
|
-
: { ...receipt, ...placeholder };
|
|
122
80
|
const observerFailed = () => {
|
|
123
81
|
debug('transloadit:warn')('Ignored onReceipt observer failure after a completed Storage write');
|
|
124
82
|
};
|
|
125
83
|
try {
|
|
126
84
|
// Neither a stalled observer nor its rejection may hide a successfully stored receipt.
|
|
127
|
-
void Promise.resolve(onReceipt?.({ ...
|
|
85
|
+
void Promise.resolve(onReceipt?.({ ...receipt }, input, assembly.assembly_id)).catch(observerFailed);
|
|
128
86
|
}
|
|
129
87
|
catch {
|
|
130
88
|
observerFailed();
|
|
131
89
|
}
|
|
132
|
-
return
|
|
90
|
+
return receipt;
|
|
133
91
|
}
|
|
134
92
|
/** Fetches authoritative Assembly status and applies the same validation as a local image store. */
|
|
135
93
|
export async function getStoredImageReceipt(client, options) {
|
package/dist/storageImage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storageImage.js","sourceRoot":"","sources":["../src/storageImage.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE1C,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,yBAAyB,MAAM,gCAAgC,CAAA;AA+CtE,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAA;AACtF,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,kBAAkB,CAAC;IAChD,QAAQ,EAAE,CAAC,CAAC,MAAM,CAAC;QACjB,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;QAChB,IAAI,EAAE,qBAAqB;QAC3B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,gBAAgB,CAAC;KAC5C,CAAC;CACH,CAAC,CAAA;AACF,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,oBAAoB,CAAC;IACnC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC;QAChB,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC;YACnB,iBAAiB,CAAC,MAAM,CAAC;gBACvB,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE;gBACjD,MAAM,EAAE,qBAAqB;gBAC7B,KAAK,EAAE,qBAAqB;gBAC5B,IAAI,EAAE,qBAAqB;aAC5B,CAAC;SACH,CAAC;KACH,CAAC;CACH,CAAC,CAAA;AAEF,SAAS,mBAAmB,CAAC,IAAY;IACvC,IACE,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,CAAC,IAAI,EAAE,KAAK,IAAI;QACpB,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,IAAI;QAC9B,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,IAAI;QAC9B,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QACnB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,OAAO,KAAK,GAAG,IAAI,OAAO,KAAK,IAAI,CAAC,EAC/F,CAAC;QACD,MAAM,IAAI,SAAS,CAAC,yEAAyE,CAAC,CAAA;IAChG,CAAC;AACH,CAAC;AAED,
|
|
1
|
+
{"version":3,"file":"storageImage.js","sourceRoot":"","sources":["../src/storageImage.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transloadit",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.1",
|
|
4
4
|
"description": "Node.js SDK for Transloadit",
|
|
5
5
|
"homepage": "https://github.com/transloadit/node-sdk/tree/main/packages/node",
|
|
6
6
|
"bugs": {
|
|
@@ -49,15 +49,11 @@
|
|
|
49
49
|
"p-map": "^7.0.4",
|
|
50
50
|
"p-queue": "^9.3.0",
|
|
51
51
|
"recursive-readdir": "^2.2.3",
|
|
52
|
-
"thumbhash": "0.1.1",
|
|
53
52
|
"tus-js-client": "^4.3.1",
|
|
54
53
|
"typanion": "^3.14.0",
|
|
55
54
|
"type-fest": "^5.6.0",
|
|
56
55
|
"zod": "3.25.76"
|
|
57
56
|
},
|
|
58
|
-
"optionalDependencies": {
|
|
59
|
-
"sharp": "0.35.4"
|
|
60
|
-
},
|
|
61
57
|
"devDependencies": {
|
|
62
58
|
"@types/debug": "^4.1.13",
|
|
63
59
|
"@types/node": "^25.8.0",
|
package/src/storageImage.ts
CHANGED
|
@@ -17,7 +17,7 @@ import InconsistentResponseError from './InconsistentResponseError.ts'
|
|
|
17
17
|
* Width and height reflect EXIF auto-orientation, matching Storage preview delivery.
|
|
18
18
|
*/
|
|
19
19
|
export interface StoredImageReceipt extends Readonly<StoredAsset> {
|
|
20
|
-
/**
|
|
20
|
+
/** Optional metadata indicating that the original has an alpha channel. */
|
|
21
21
|
readonly hasAlpha?: boolean
|
|
22
22
|
readonly height: number
|
|
23
23
|
readonly md5hash: string
|
|
@@ -94,35 +94,6 @@ function validateDestination(path: string): void {
|
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
96
|
|
|
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
97
|
/** Stores one original without overwriting; receipt validation occurs after the Storage write. */
|
|
127
98
|
export async function storeImage(
|
|
128
99
|
client: Transloadit,
|
|
@@ -144,24 +115,14 @@ export async function storeImage(
|
|
|
144
115
|
throw new TypeError('overwrite must be a boolean')
|
|
145
116
|
signal?.throwIfAborted()
|
|
146
117
|
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
118
|
let size = 0
|
|
150
119
|
for await (const chunk of createReadStream(filePath, { signal })) {
|
|
151
120
|
checksum.update(chunk)
|
|
152
121
|
size += chunk.length
|
|
153
|
-
if (size > 32 * 1024 * 1024) thumbnailChunks = undefined
|
|
154
|
-
else thumbnailChunks?.push(chunk)
|
|
155
122
|
}
|
|
156
123
|
if (size === 0) throw new Error('Cannot store an empty image')
|
|
157
124
|
const md5hash = checksum.digest('hex')
|
|
158
125
|
signal?.throwIfAborted()
|
|
159
|
-
const placeholder =
|
|
160
|
-
thumbnailChunks === undefined
|
|
161
|
-
? undefined
|
|
162
|
-
: await imagePlaceholderMetadata(Buffer.concat(thumbnailChunks))
|
|
163
|
-
thumbnailChunks = undefined
|
|
164
|
-
signal?.throwIfAborted()
|
|
165
126
|
const assembly = await client.createAssembly({
|
|
166
127
|
chunkSize,
|
|
167
128
|
files: { image: filePath },
|
|
@@ -184,23 +145,18 @@ export async function storeImage(
|
|
|
184
145
|
const input = { path, size, md5hash }
|
|
185
146
|
// API2 can watermark uploads before Robots run; this exact write's result describes stored bytes.
|
|
186
147
|
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
148
|
const observerFailed = (): void => {
|
|
193
149
|
debug('transloadit:warn')('Ignored onReceipt observer failure after a completed Storage write')
|
|
194
150
|
}
|
|
195
151
|
try {
|
|
196
152
|
// Neither a stalled observer nor its rejection may hide a successfully stored receipt.
|
|
197
|
-
void Promise.resolve(onReceipt?.({ ...
|
|
153
|
+
void Promise.resolve(onReceipt?.({ ...receipt }, input, assembly.assembly_id)).catch(
|
|
198
154
|
observerFailed,
|
|
199
155
|
)
|
|
200
156
|
} catch {
|
|
201
157
|
observerFailed()
|
|
202
158
|
}
|
|
203
|
-
return
|
|
159
|
+
return receipt
|
|
204
160
|
}
|
|
205
161
|
|
|
206
162
|
/** Fetches authoritative Assembly status and applies the same validation as a local image store. */
|