transloadit 6.7.0 → 6.9.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.
- package/README.md +67 -3
- package/dist/Transloadit.d.ts +6 -0
- package/dist/Transloadit.d.ts.map +1 -1
- package/dist/Transloadit.js +21 -1
- package/dist/Transloadit.js.map +1 -1
- package/dist/apiTypes.d.ts +4 -0
- package/dist/apiTypes.d.ts.map +1 -1
- package/dist/cli/commands/storage.d.ts.map +1 -1
- package/dist/cli/commands/storage.js +110 -37
- package/dist/cli/commands/storage.js.map +1 -1
- package/dist/cli/storageReceipts.d.ts +80 -1
- package/dist/cli/storageReceipts.d.ts.map +1 -1
- package/dist/cli/storageReceipts.js +48 -12
- package/dist/cli/storageReceipts.js.map +1 -1
- package/dist/storageAsset.d.ts +44 -0
- package/dist/storageAsset.d.ts.map +1 -0
- package/dist/storageAsset.js +173 -0
- package/dist/storageAsset.js.map +1 -0
- package/dist/storageImage.d.ts +11 -35
- package/dist/storageImage.d.ts.map +1 -1
- package/dist/storageImage.js +21 -141
- package/dist/storageImage.js.map +1 -1
- package/package.json +2 -2
- package/src/Transloadit.ts +38 -1
- package/src/apiTypes.ts +8 -1
- package/src/cli/commands/storage.ts +141 -67
- package/src/cli/storageReceipts.ts +50 -11
- package/src/storageAsset.ts +271 -0
- package/src/storageImage.ts +43 -201
package/dist/storageImage.js
CHANGED
|
@@ -1,152 +1,32 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { createReadStream } from 'node:fs';
|
|
3
|
-
import debug from 'debug';
|
|
4
1
|
import { z } from 'zod';
|
|
5
|
-
import { ApiError } from "./ApiError.js";
|
|
6
2
|
import { storedAssetSchema } from "./alphalib/types/storageAsset.js";
|
|
7
3
|
import InconsistentResponseError from "./InconsistentResponseError.js";
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
path: z.string(),
|
|
15
|
-
size: positiveIntegerSchema,
|
|
16
|
-
md5hash: z.string().regex(/^[a-f0-9]{32}$/),
|
|
17
|
-
}),
|
|
4
|
+
import { getOriginalReceipt, storeOriginal } from "./storageAsset.js";
|
|
5
|
+
export { storageImagePlaceholderSchema } from "./storageAsset.js";
|
|
6
|
+
const imageReceiptSchema = storedAssetSchema.extend({
|
|
7
|
+
md5hash: storedAssetSchema.shape.md5hash.unwrap(),
|
|
8
|
+
height: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
9
|
+
width: z.number().int().positive().max(Number.MAX_SAFE_INTEGER),
|
|
18
10
|
});
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
height: positiveIntegerSchema,
|
|
26
|
-
width: positiveIntegerSchema,
|
|
27
|
-
size: positiveIntegerSchema,
|
|
28
|
-
}),
|
|
29
|
-
]),
|
|
30
|
-
}),
|
|
31
|
-
});
|
|
32
|
-
function validateDestination(path) {
|
|
33
|
-
if (typeof path !== 'string' ||
|
|
34
|
-
path.trim() !== path ||
|
|
35
|
-
Buffer.byteLength(path) > 1024 ||
|
|
36
|
-
path.normalize('NFC') !== path ||
|
|
37
|
-
/[\p{Cc}\p{Cs}\\|]/u.test(path) ||
|
|
38
|
-
path.includes('${') ||
|
|
39
|
-
path.split('/').some((segment) => segment.trim() === '' || segment === '.' || segment === '..')) {
|
|
40
|
-
throw new TypeError('Storage path must be a complete relative filename without interpolation');
|
|
11
|
+
function requireImage(receipt) {
|
|
12
|
+
const image = imageReceiptSchema.safeParse(receipt);
|
|
13
|
+
if (!image.success) {
|
|
14
|
+
throw new InconsistentResponseError('The Assembly did not return a matching Storage image receipt', {
|
|
15
|
+
cause: { receipt },
|
|
16
|
+
});
|
|
41
17
|
}
|
|
18
|
+
return image.data;
|
|
42
19
|
}
|
|
43
|
-
/**
|
|
20
|
+
/** Retains the image API's transformed-byte semantics; use storeAsset for exact originals. */
|
|
44
21
|
export async function storeImage(client, filePath, options) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (overwrite !== undefined && typeof overwrite !== 'boolean')
|
|
51
|
-
throw new TypeError('overwrite must be a boolean');
|
|
52
|
-
signal?.throwIfAborted();
|
|
53
|
-
const checksum = createHash('md5');
|
|
54
|
-
let size = 0;
|
|
55
|
-
for await (const chunk of createReadStream(filePath, { signal })) {
|
|
56
|
-
checksum.update(chunk);
|
|
57
|
-
size += chunk.length;
|
|
58
|
-
}
|
|
59
|
-
if (size === 0)
|
|
60
|
-
throw new Error('Cannot store an empty image');
|
|
61
|
-
const md5hash = checksum.digest('hex');
|
|
62
|
-
signal?.throwIfAborted();
|
|
63
|
-
const assembly = await client.createAssembly({
|
|
64
|
-
chunkSize,
|
|
65
|
-
files: { image: filePath },
|
|
66
|
-
onAssemblyProgress,
|
|
67
|
-
onUploadProgress,
|
|
68
|
-
params: {
|
|
69
|
-
steps: {
|
|
70
|
-
...(placeholder === 'blur'
|
|
71
|
-
? {
|
|
72
|
-
':original': {
|
|
73
|
-
robot: '/upload/handle',
|
|
74
|
-
output_meta: { thumbhash: true },
|
|
75
|
-
},
|
|
76
|
-
}
|
|
77
|
-
: {}),
|
|
78
|
-
stored: {
|
|
79
|
-
robot: '/transloadit/store',
|
|
80
|
-
use: ':original',
|
|
81
|
-
path,
|
|
82
|
-
conflict_strategy: overwrite === true ? 'overwrite' : 'error',
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
signal,
|
|
87
|
-
timeout,
|
|
88
|
-
waitForCompletion: true,
|
|
89
|
-
});
|
|
90
|
-
const input = { path, size, md5hash };
|
|
91
|
-
// API2 can watermark uploads before Robots run; this exact write's result describes stored bytes.
|
|
92
|
-
const receipt = validateReceipt(assembly, input, true);
|
|
93
|
-
const observerFailed = () => {
|
|
94
|
-
debug('transloadit:warn')('Ignored onReceipt observer failure after a completed Storage write');
|
|
95
|
-
};
|
|
96
|
-
try {
|
|
97
|
-
// Neither a stalled observer nor its rejection may hide a successfully stored receipt.
|
|
98
|
-
void Promise.resolve(onReceipt?.({ ...receipt }, input, assembly.assembly_id)).catch(observerFailed);
|
|
99
|
-
}
|
|
100
|
-
catch {
|
|
101
|
-
observerFailed();
|
|
102
|
-
}
|
|
103
|
-
return receipt;
|
|
22
|
+
const receipt = await storeOriginal(client, filePath, {
|
|
23
|
+
...options,
|
|
24
|
+
onReceipt: (asset, input, assemblyId) => options.onReceipt?.(requireImage(asset), input, assemblyId),
|
|
25
|
+
}, true);
|
|
26
|
+
return requireImage(receipt);
|
|
104
27
|
}
|
|
105
|
-
/**
|
|
28
|
+
/** Recovers one image receipt with the same strict byte checks as generic receipt recovery. */
|
|
106
29
|
export async function getStoredImageReceipt(client, options) {
|
|
107
|
-
|
|
108
|
-
validateDestination(expected.path);
|
|
109
|
-
const assembly = await client.getAssembly(assemblyId);
|
|
110
|
-
if (assembly.assembly_id !== assemblyId) {
|
|
111
|
-
throw new InconsistentResponseError('The response did not match the requested Assembly', {
|
|
112
|
-
cause: { assemblyId },
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
return validateReceipt(assembly, expected);
|
|
116
|
-
}
|
|
117
|
-
function validateReceipt(assembly, expected, acceptTransformed = false) {
|
|
118
|
-
const { path, size, md5hash } = expected;
|
|
119
|
-
if (typeof assembly.error === 'string')
|
|
120
|
-
throw new ApiError({ body: assembly });
|
|
121
|
-
if (assembly.ok === 'ASSEMBLY_CANCELED') {
|
|
122
|
-
throw new InconsistentResponseError('The Storage Assembly ended with ASSEMBLY_CANCELED', {
|
|
123
|
-
cause: { assemblyId: assembly.assembly_id },
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
if (assembly.ok === 'ASSEMBLY_UPLOADING' ||
|
|
127
|
-
assembly.ok === 'ASSEMBLY_EXECUTING' ||
|
|
128
|
-
assembly.ok === 'ASSEMBLY_REPLAYING') {
|
|
129
|
-
throw new InconsistentResponseError(`The Storage Assembly is not complete (${assembly.ok})`, {
|
|
130
|
-
cause: { assemblyId: assembly.assembly_id },
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
const parsed = completedImageSchema.safeParse(assembly);
|
|
134
|
-
const result = parsed.success ? parsed.data.results[':original'][0] : undefined;
|
|
135
|
-
const originals = assembly.results?.[':original'];
|
|
136
|
-
const receiptCheck = {
|
|
137
|
-
originalCount: Array.isArray(originals) ? originals.length : 0,
|
|
138
|
-
metadataValid: parsed.success,
|
|
139
|
-
pathMatches: result === undefined ? undefined : result.path === path,
|
|
140
|
-
sizeMatches: result === undefined ? undefined : result.size === size,
|
|
141
|
-
md5Matches: result === undefined ? undefined : result.md5hash === md5hash,
|
|
142
|
-
};
|
|
143
|
-
if (result === undefined ||
|
|
144
|
-
result.path !== path ||
|
|
145
|
-
(!acceptTransformed && (!receiptCheck.sizeMatches || !receiptCheck.md5Matches))) {
|
|
146
|
-
throw new InconsistentResponseError('The Assembly did not return a matching Storage image receipt', {
|
|
147
|
-
cause: { assemblyId: assembly.assembly_id, receiptCheck },
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
return result;
|
|
30
|
+
return requireImage(await getOriginalReceipt(client, options, true));
|
|
151
31
|
}
|
|
152
32
|
//# sourceMappingURL=storageImage.js.map
|
package/dist/storageImage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storageImage.js","sourceRoot":"","sources":["../src/storageImage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"storageImage.js","sourceRoot":"","sources":["../src/storageImage.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,yBAAyB,MAAM,gCAAgC,CAAA;AACtE,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,EAAE,6BAA6B,EAAE,MAAM,mBAAmB,CAAA;AAoBjE,MAAM,kBAAkB,GAAG,iBAAiB,CAAC,MAAM,CAAC;IAClD,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE;IACjD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC;IAChE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,CAAC;CAChE,CAAC,CAAA;AAEF,SAAS,YAAY,CAAC,OAA2B;IAC/C,MAAM,KAAK,GAAG,kBAAkB,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACnD,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;QACnB,MAAM,IAAI,yBAAyB,CACjC,8DAA8D,EAC9D;YACE,KAAK,EAAE,EAAE,OAAO,EAAE;SACnB,CACF,CAAA;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAA;AACnB,CAAC;AAED,8FAA8F;AAC9F,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAmB,EACnB,QAAgB,EAChB,OAA0B;IAE1B,MAAM,OAAO,GAAG,MAAM,aAAa,CACjC,MAAM,EACN,QAAQ,EACR;QACE,GAAG,OAAO;QACV,SAAS,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,CACtC,OAAO,CAAC,SAAS,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC;KAC9D,EACD,IAAI,CACL,CAAA;IACD,OAAO,YAAY,CAAC,OAAO,CAAC,CAAA;AAC9B,CAAC;AAED,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAmB,EACnB,OAAqC;IAErC,OAAO,YAAY,CAAC,MAAM,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAA;AACtE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transloadit",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.9.0",
|
|
4
4
|
"description": "Node.js SDK and `transloadit` CLI for Transloadit",
|
|
5
5
|
"homepage": "https://github.com/transloadit/node-sdk/tree/main/packages/node",
|
|
6
6
|
"bugs": {
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
35
|
"@transloadit/sev-logger": "^0.1.9",
|
|
36
|
-
"@transloadit/utils": "^4.
|
|
36
|
+
"@transloadit/utils": "^4.11.0",
|
|
37
37
|
"cacheable-lookup": "^7.0.0",
|
|
38
38
|
"clipanion": "^4.0.0-rc.4",
|
|
39
39
|
"debug": "^4.4.3",
|
package/src/Transloadit.ts
CHANGED
|
@@ -54,6 +54,11 @@ import type {
|
|
|
54
54
|
LintAssemblyInstructionsInput,
|
|
55
55
|
LintAssemblyInstructionsResult,
|
|
56
56
|
} from './lintAssemblyInstructions.ts'
|
|
57
|
+
import type {
|
|
58
|
+
GetStoredAssetReceiptOptions,
|
|
59
|
+
StoreAssetOptions,
|
|
60
|
+
StoredAssetReceipt,
|
|
61
|
+
} from './storageAsset.ts'
|
|
57
62
|
import type {
|
|
58
63
|
GetStoredImageReceiptOptions,
|
|
59
64
|
StoredImageReceipt,
|
|
@@ -108,6 +113,7 @@ import InconsistentResponseError from './InconsistentResponseError.ts'
|
|
|
108
113
|
import { lintAssemblyInstructions as lintAssemblyInstructionsInternal } from './lintAssemblyInstructions.ts'
|
|
109
114
|
import PaginationStream from './PaginationStream.ts'
|
|
110
115
|
import PollingTimeoutError from './PollingTimeoutError.ts'
|
|
116
|
+
import { getStoredAssetReceipt, storeAsset } from './storageAsset.ts'
|
|
111
117
|
import { getStoredImageReceipt, storeImage } from './storageImage.ts'
|
|
112
118
|
import {
|
|
113
119
|
normalizeStoragePublicPrefix,
|
|
@@ -151,6 +157,12 @@ export type {
|
|
|
151
157
|
RobotListResult,
|
|
152
158
|
RobotParamHelp,
|
|
153
159
|
} from './robots.ts'
|
|
160
|
+
export type {
|
|
161
|
+
GetStoredAssetReceiptOptions,
|
|
162
|
+
StoreAssetOptions,
|
|
163
|
+
StoredAssetExpectation,
|
|
164
|
+
StoredAssetReceipt,
|
|
165
|
+
} from './storageAsset.ts'
|
|
154
166
|
export type {
|
|
155
167
|
GetStoredImageReceiptOptions,
|
|
156
168
|
StoredImageExpectation,
|
|
@@ -573,6 +585,16 @@ export class Transloadit {
|
|
|
573
585
|
this._defaultTimeout = timeout
|
|
574
586
|
}
|
|
575
587
|
|
|
588
|
+
/** Stores one byte-identical local original of any media type and returns its verified receipt. */
|
|
589
|
+
storeAsset(filePath: string, options: StoreAssetOptions): Promise<StoredAssetReceipt> {
|
|
590
|
+
return storeAsset(this, filePath, options)
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
/** Recovers a generic original receipt without uploading or accepting changed bytes. */
|
|
594
|
+
getStoredAssetReceipt(options: GetStoredAssetReceiptOptions): Promise<StoredAssetReceipt> {
|
|
595
|
+
return getStoredAssetReceipt(this, options)
|
|
596
|
+
}
|
|
597
|
+
|
|
576
598
|
/** Stores one local original at an explicit path and returns its verified image metadata. */
|
|
577
599
|
storeImage(filePath: string, options: StoreImageOptions): Promise<StoredImageReceipt> {
|
|
578
600
|
return storeImage(this, filePath, options)
|
|
@@ -605,6 +627,7 @@ export class Transloadit {
|
|
|
605
627
|
const result = await this._remoteJson<unknown, OptionalAuthParams & GetStoredAssetOptions>({
|
|
606
628
|
urlSuffix: `/dam/assets/${id}`,
|
|
607
629
|
method: 'get',
|
|
630
|
+
signEndpoint: true,
|
|
608
631
|
params,
|
|
609
632
|
signal: options.signal,
|
|
610
633
|
})
|
|
@@ -629,6 +652,7 @@ export class Transloadit {
|
|
|
629
652
|
const result = await this._remoteJson<unknown, OptionalAuthParams & ListStoredAssetsOptions>({
|
|
630
653
|
urlSuffix: '/dam/assets',
|
|
631
654
|
method: 'get',
|
|
655
|
+
signEndpoint: true,
|
|
632
656
|
params,
|
|
633
657
|
signal: options.signal,
|
|
634
658
|
})
|
|
@@ -653,6 +677,7 @@ export class Transloadit {
|
|
|
653
677
|
const result = await this._remoteJson<unknown, OptionalAuthParams & MoveStoredAssetOptions>({
|
|
654
678
|
urlSuffix: `/dam/assets/${id}`,
|
|
655
679
|
method: 'patch',
|
|
680
|
+
signEndpoint: true,
|
|
656
681
|
params,
|
|
657
682
|
signal: options.signal,
|
|
658
683
|
})
|
|
@@ -676,6 +701,7 @@ export class Transloadit {
|
|
|
676
701
|
const result = await this._remoteJson<unknown, OptionalAuthParams>({
|
|
677
702
|
urlSuffix: `/dam/assets/${id}`,
|
|
678
703
|
method: 'delete',
|
|
704
|
+
signEndpoint: true,
|
|
679
705
|
signal: options.signal,
|
|
680
706
|
})
|
|
681
707
|
checkResult(result)
|
|
@@ -1712,6 +1738,8 @@ export class Transloadit {
|
|
|
1712
1738
|
timeout?: Delays
|
|
1713
1739
|
method?: 'delete' | 'get' | 'patch' | 'post' | 'put'
|
|
1714
1740
|
params?: TParams
|
|
1741
|
+
/** Sign the request's method and path into `params.auth`, as Storage requests require. */
|
|
1742
|
+
signEndpoint?: boolean
|
|
1715
1743
|
fields?: Fields
|
|
1716
1744
|
headers?: Headers
|
|
1717
1745
|
signal?: AbortSignal
|
|
@@ -1722,7 +1750,8 @@ export class Transloadit {
|
|
|
1722
1750
|
isTrustedUrl = false,
|
|
1723
1751
|
timeout = { request: this._defaultTimeout },
|
|
1724
1752
|
method = 'get',
|
|
1725
|
-
params = {},
|
|
1753
|
+
params: paramsInput = {},
|
|
1754
|
+
signEndpoint = false,
|
|
1726
1755
|
fields,
|
|
1727
1756
|
headers,
|
|
1728
1757
|
signal,
|
|
@@ -1739,6 +1768,14 @@ export class Transloadit {
|
|
|
1739
1768
|
}
|
|
1740
1769
|
}
|
|
1741
1770
|
|
|
1771
|
+
const requested: OptionalAuthParams = paramsInput
|
|
1772
|
+
const params: OptionalAuthParams = signEndpoint
|
|
1773
|
+
? {
|
|
1774
|
+
...requested,
|
|
1775
|
+
auth: { ...requested.auth, method: method.toUpperCase(), path: new URL(url).pathname },
|
|
1776
|
+
}
|
|
1777
|
+
: requested
|
|
1778
|
+
|
|
1742
1779
|
if (method === 'get') {
|
|
1743
1780
|
url = this._appendParamsToUrl(url, params)
|
|
1744
1781
|
}
|
package/src/apiTypes.ts
CHANGED
|
@@ -10,7 +10,14 @@ export {
|
|
|
10
10
|
export { assemblyInstructionsSchema } from './alphalib/types/template.ts'
|
|
11
11
|
|
|
12
12
|
export interface OptionalAuthParams {
|
|
13
|
-
auth?: {
|
|
13
|
+
auth?: {
|
|
14
|
+
key?: string
|
|
15
|
+
expires?: string
|
|
16
|
+
/** HTTP method of the request these params are signed for, in upper case. */
|
|
17
|
+
method?: string
|
|
18
|
+
/** URL path of the request these params are signed for, without the query string. */
|
|
19
|
+
path?: string
|
|
20
|
+
}
|
|
14
21
|
}
|
|
15
22
|
|
|
16
23
|
// todo make zod schemas for these types in the backend for these too (in alphalib?)
|