transloadit 6.8.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 +9 -0
- package/dist/Transloadit.js.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 +22 -0
- 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)
|