@transloadit/node 4.1.9 → 4.2.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 +23 -1
- package/dist/Transloadit.d.ts +17 -5
- package/dist/Transloadit.d.ts.map +1 -1
- package/dist/Transloadit.js +206 -39
- package/dist/Transloadit.js.map +1 -1
- package/dist/alphalib/mcache.d.ts.map +1 -1
- package/dist/alphalib/mcache.js +22 -7
- package/dist/alphalib/mcache.js.map +1 -1
- package/dist/alphalib/types/assemblyReplay.d.ts +56 -0
- package/dist/alphalib/types/assemblyReplay.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyReplayNotification.d.ts +56 -0
- package/dist/alphalib/types/assemblyReplayNotification.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyStatus.d.ts +63 -57
- package/dist/alphalib/types/assemblyStatus.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyStatus.js +9 -1
- package/dist/alphalib/types/assemblyStatus.js.map +1 -1
- package/dist/alphalib/types/assemblyUrls.d.ts +1 -1
- package/dist/alphalib/types/assemblyUrls.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyUrls.js.map +1 -1
- package/dist/alphalib/types/robots/_index.d.ts +608 -81
- package/dist/alphalib/types/robots/_index.d.ts.map +1 -1
- package/dist/alphalib/types/robots/_index.js +4 -0
- package/dist/alphalib/types/robots/_index.js.map +1 -1
- package/dist/alphalib/types/robots/_instructions-primitives.d.ts +4 -4
- package/dist/alphalib/types/robots/_instructions-primitives.d.ts.map +1 -1
- package/dist/alphalib/types/robots/_instructions-primitives.js +1 -0
- package/dist/alphalib/types/robots/_instructions-primitives.js.map +1 -1
- package/dist/alphalib/types/robots/document-optimize.d.ts +489 -0
- package/dist/alphalib/types/robots/document-optimize.d.ts.map +1 -0
- package/dist/alphalib/types/robots/document-optimize.js +151 -0
- package/dist/alphalib/types/robots/document-optimize.js.map +1 -0
- package/dist/alphalib/types/template.d.ts +1050 -174
- package/dist/alphalib/types/template.d.ts.map +1 -1
- package/dist/tus.d.ts +2 -1
- package/dist/tus.d.ts.map +1 -1
- package/dist/tus.js +2 -1
- package/dist/tus.js.map +1 -1
- package/package.json +2 -2
- package/src/Transloadit.ts +279 -49
- package/src/alphalib/mcache.ts +26 -7
- package/src/alphalib/types/assemblyStatus.ts +9 -1
- package/src/alphalib/types/assemblyUrls.ts +2 -5
- package/src/alphalib/types/robots/_index.ts +14 -0
- package/src/alphalib/types/robots/_instructions-primitives.ts +1 -0
- package/src/alphalib/types/robots/document-optimize.ts +180 -0
- package/src/tus.ts +3 -0
package/dist/tus.d.ts
CHANGED
|
@@ -12,7 +12,8 @@ interface SendTusRequestOptions {
|
|
|
12
12
|
uploadConcurrency: number;
|
|
13
13
|
onProgress: (options: UploadProgress) => void;
|
|
14
14
|
signal?: AbortSignal;
|
|
15
|
+
uploadUrls?: Record<string, string>;
|
|
15
16
|
}
|
|
16
|
-
export declare function sendTusRequest({ streamsMap, assembly, requestedChunkSize, uploadConcurrency, onProgress, signal, }: SendTusRequestOptions): Promise<void>;
|
|
17
|
+
export declare function sendTusRequest({ streamsMap, assembly, requestedChunkSize, uploadConcurrency, onProgress, signal, uploadUrls, }: SendTusRequestOptions): Promise<void>;
|
|
17
18
|
export {};
|
|
18
19
|
//# sourceMappingURL=tus.d.ts.map
|
package/dist/tus.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tus.d.ts","sourceRoot":"","sources":["../src/tus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAK3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAItD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,QAAQ,CAAA;CACjB;AAED,UAAU,qBAAqB;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,QAAQ,EAAE,cAAc,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"tus.d.ts","sourceRoot":"","sources":["../src/tus.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAK3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAItD,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,QAAQ,CAAA;CACjB;AAED,UAAU,qBAAqB;IAC7B,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAClC,QAAQ,EAAE,cAAc,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,iBAAiB,EAAE,MAAM,CAAA;IACzB,UAAU,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC;AAED,wBAAsB,cAAc,CAAC,EACnC,UAAU,EACV,QAAQ,EACR,kBAAkB,EAClB,iBAAiB,EACjB,UAAU,EACV,MAAM,EACN,UAAU,GACX,EAAE,qBAAqB,iBAuIvB"}
|
package/dist/tus.js
CHANGED
|
@@ -4,7 +4,7 @@ import debug from 'debug';
|
|
|
4
4
|
import pMap from 'p-map';
|
|
5
5
|
import { Upload } from 'tus-js-client';
|
|
6
6
|
const log = debug('transloadit');
|
|
7
|
-
export async function sendTusRequest({ streamsMap, assembly, requestedChunkSize, uploadConcurrency, onProgress, signal, }) {
|
|
7
|
+
export async function sendTusRequest({ streamsMap, assembly, requestedChunkSize, uploadConcurrency, onProgress, signal, uploadUrls, }) {
|
|
8
8
|
const streamLabels = Object.keys(streamsMap);
|
|
9
9
|
let totalBytes = 0;
|
|
10
10
|
let lastEmittedProgress = 0;
|
|
@@ -88,6 +88,7 @@ export async function sendTusRequest({ streamsMap, assembly, requestedChunkSize,
|
|
|
88
88
|
};
|
|
89
89
|
const tusOptions = {
|
|
90
90
|
endpoint: assembly.tus_url,
|
|
91
|
+
uploadUrl: uploadUrls?.[label],
|
|
91
92
|
metadata: {
|
|
92
93
|
assembly_url: assembly.assembly_ssl_url,
|
|
93
94
|
fieldname: label,
|
package/dist/tus.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tus.js","sourceRoot":"","sources":["../src/tus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAA;AACvC,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAA;AAEpC,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,IAAI,MAAM,OAAO,CAAA;AAExB,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAA;AAItC,MAAM,GAAG,GAAG,KAAK,CAAC,aAAa,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"tus.js","sourceRoot":"","sources":["../src/tus.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@transloadit/node",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Node.js SDK for Transloadit",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"@aws-sdk/client-s3": "^3.891.0",
|
|
21
21
|
"@aws-sdk/s3-request-presigner": "^3.891.0",
|
|
22
22
|
"@transloadit/sev-logger": "^0.0.15",
|
|
23
|
-
"@transloadit/utils": "^4.
|
|
23
|
+
"@transloadit/utils": "^4.2.0",
|
|
24
24
|
"clipanion": "^4.0.0-rc.4",
|
|
25
25
|
"debug": "^4.4.3",
|
|
26
26
|
"dotenv": "^17.2.3",
|
package/src/Transloadit.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as assert from 'node:assert'
|
|
2
2
|
import { randomUUID } from 'node:crypto'
|
|
3
3
|
import { constants, createReadStream } from 'node:fs'
|
|
4
|
-
import { access } from 'node:fs/promises'
|
|
4
|
+
import { access, stat } from 'node:fs/promises'
|
|
5
|
+
import { basename } from 'node:path'
|
|
5
6
|
import type { Readable } from 'node:stream'
|
|
6
7
|
import { setTimeout as delay } from 'node:timers/promises'
|
|
7
8
|
import { getSignedSmartCdnUrl, signParamsSync } from '@transloadit/utils/node'
|
|
@@ -77,8 +78,73 @@ const { version } = packageJson
|
|
|
77
78
|
|
|
78
79
|
export type AssemblyProgress = (assembly: AssemblyStatus) => void
|
|
79
80
|
|
|
80
|
-
|
|
81
|
-
|
|
81
|
+
type UploadDescriptor = {
|
|
82
|
+
label: string
|
|
83
|
+
filename: string
|
|
84
|
+
size?: number
|
|
85
|
+
path?: string
|
|
86
|
+
value?: Readable | IntoStreamInput
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const getUploadKey = (
|
|
90
|
+
fieldname: string | null | undefined,
|
|
91
|
+
filename: string | null | undefined,
|
|
92
|
+
size: number | null | undefined,
|
|
93
|
+
): string | null => {
|
|
94
|
+
if (!fieldname || !filename || size == null) return null
|
|
95
|
+
return JSON.stringify([fieldname, filename, size])
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const getSizeFromValue = (value: Readable | IntoStreamInput): number | undefined => {
|
|
99
|
+
if (typeof value === 'string') return Buffer.byteLength(value)
|
|
100
|
+
if (Buffer.isBuffer(value)) return value.length
|
|
101
|
+
if (value instanceof ArrayBuffer) return value.byteLength
|
|
102
|
+
if (ArrayBuffer.isView(value)) return value.byteLength
|
|
103
|
+
return undefined
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const toReadableUpload = (label: string, value: Readable | IntoStreamInput): Readable => {
|
|
107
|
+
const readable = isReadableStream(value)
|
|
108
|
+
if (!readable && isStream(value)) {
|
|
109
|
+
throw new Error(`Upload named "${label}" is not a Readable stream`)
|
|
110
|
+
}
|
|
111
|
+
return readable ? value : intoStream(value)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const buildStreamsMap = (descriptors: UploadDescriptor[]): Record<string, Stream> =>
|
|
115
|
+
Object.fromEntries(
|
|
116
|
+
descriptors.map((descriptor) => {
|
|
117
|
+
if (descriptor.path) {
|
|
118
|
+
const stream = createReadStream(descriptor.path)
|
|
119
|
+
return [descriptor.label, { stream, path: descriptor.path }]
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const value = descriptor.value
|
|
123
|
+
if (value == null) {
|
|
124
|
+
throw new Error(`Upload named "${descriptor.label}" has no data`)
|
|
125
|
+
}
|
|
126
|
+
const stream = toReadableUpload(descriptor.label, value)
|
|
127
|
+
return [descriptor.label, { stream }]
|
|
128
|
+
}),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
const pauseStreams = (streamsMap: Record<string, Stream>): void => {
|
|
132
|
+
for (const { stream } of Object.values(streamsMap)) {
|
|
133
|
+
stream.pause()
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const createStreamErrorPromise = (streamsMap: Record<string, Stream>): Promise<never> => {
|
|
138
|
+
const promise = new Promise<never>((_resolve, reject) => {
|
|
139
|
+
for (const { stream } of Object.values(streamsMap)) {
|
|
140
|
+
stream.on('error', reject)
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
promise.catch(() => {})
|
|
144
|
+
return promise
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
interface AssemblyUploadOptions {
|
|
82
148
|
files?: {
|
|
83
149
|
[name: string]: string
|
|
84
150
|
}
|
|
@@ -91,19 +157,32 @@ export interface CreateAssemblyOptions {
|
|
|
91
157
|
timeout?: number
|
|
92
158
|
onUploadProgress?: (uploadProgress: UploadProgress) => void
|
|
93
159
|
onAssemblyProgress?: AssemblyProgress
|
|
94
|
-
assemblyId?: string
|
|
95
160
|
/**
|
|
96
|
-
* Optional AbortSignal to cancel the
|
|
161
|
+
* Optional AbortSignal to cancel the upload and any follow-up polling.
|
|
97
162
|
* When aborted, any in-flight HTTP requests and TUS uploads will be cancelled.
|
|
98
163
|
*/
|
|
99
164
|
signal?: AbortSignal
|
|
100
165
|
}
|
|
101
166
|
|
|
167
|
+
export interface CreateAssemblyOptions extends AssemblyUploadOptions {
|
|
168
|
+
params?: CreateAssemblyParams
|
|
169
|
+
assemblyId?: string
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface ResumeAssemblyUploadsOptions extends AssemblyUploadOptions {
|
|
173
|
+
assemblyUrl: string
|
|
174
|
+
}
|
|
175
|
+
|
|
102
176
|
export interface AwaitAssemblyCompletionOptions {
|
|
103
177
|
onAssemblyProgress?: AssemblyProgress
|
|
104
178
|
timeout?: number
|
|
105
179
|
interval?: number
|
|
106
180
|
startTimeMs?: number
|
|
181
|
+
/**
|
|
182
|
+
* Optional assembly URL to poll instead of the configured client endpoint.
|
|
183
|
+
* Useful when resuming an Assembly created on a different host/region.
|
|
184
|
+
*/
|
|
185
|
+
assemblyUrl?: string
|
|
107
186
|
/**
|
|
108
187
|
* Optional AbortSignal to cancel polling.
|
|
109
188
|
* When aborted, the polling loop will stop and throw an AbortError.
|
|
@@ -159,6 +238,14 @@ function getHrTimeMs(): number {
|
|
|
159
238
|
return Number(process.hrtime.bigint() / 1000000n)
|
|
160
239
|
}
|
|
161
240
|
|
|
241
|
+
function getAssemblyIdFromUrl(assemblyUrl: string): string {
|
|
242
|
+
const match = assemblyUrl.match(/\/assemblies\/([^/?#]+)/)
|
|
243
|
+
if (!match) {
|
|
244
|
+
throw new Error(`Invalid assembly URL: ${assemblyUrl}`)
|
|
245
|
+
}
|
|
246
|
+
return match[1] ?? ''
|
|
247
|
+
}
|
|
248
|
+
|
|
162
249
|
function checkResult<T>(result: T | { error: string }): asserts result is T {
|
|
163
250
|
// In case server returned a successful HTTP status code, but an `error` in the JSON object
|
|
164
251
|
// This happens sometimes, for example when createAssembly with an invalid file (IMPORT_FILE_ERROR)
|
|
@@ -276,36 +363,25 @@ export class Transloadit {
|
|
|
276
363
|
{ concurrency: 5 },
|
|
277
364
|
)
|
|
278
365
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}),
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const allStreamsMap = Object.fromEntries<Stream>(
|
|
294
|
-
Object.entries(streamsMap).map(([label, stream]) => [label, { stream }]),
|
|
295
|
-
)
|
|
296
|
-
|
|
297
|
-
// Create streams from files too
|
|
298
|
-
for (const [label, path] of Object.entries(files)) {
|
|
299
|
-
const stream = createReadStream(path)
|
|
300
|
-
allStreamsMap[label] = { stream, path } // File streams have path
|
|
301
|
-
}
|
|
366
|
+
const descriptors: UploadDescriptor[] = [
|
|
367
|
+
...Object.entries(files).map(([label, path]) => ({
|
|
368
|
+
label,
|
|
369
|
+
path,
|
|
370
|
+
filename: basename(path),
|
|
371
|
+
})),
|
|
372
|
+
...Object.entries(uploads).map(([label, value]) => ({
|
|
373
|
+
label,
|
|
374
|
+
filename: label,
|
|
375
|
+
value,
|
|
376
|
+
})),
|
|
377
|
+
]
|
|
378
|
+
|
|
379
|
+
const allStreamsMap = buildStreamsMap(descriptors)
|
|
302
380
|
|
|
303
381
|
const allStreams = Object.values(allStreamsMap)
|
|
304
382
|
|
|
305
383
|
// Pause all streams
|
|
306
|
-
|
|
307
|
-
stream.pause()
|
|
308
|
-
}
|
|
384
|
+
pauseStreams(allStreamsMap)
|
|
309
385
|
|
|
310
386
|
// If any stream emits error, we want to handle this and exit with error.
|
|
311
387
|
// This promise races against createAssemblyAndUpload() below via Promise.race().
|
|
@@ -313,12 +389,7 @@ export class Transloadit {
|
|
|
313
389
|
// it's no longer awaited, but stream error handlers remain attached.
|
|
314
390
|
// The no-op catch prevents Node's unhandled rejection warning if a stream
|
|
315
391
|
// errors after the race is already won.
|
|
316
|
-
const streamErrorPromise =
|
|
317
|
-
for (const { stream } of allStreams) {
|
|
318
|
-
stream.on('error', reject)
|
|
319
|
-
}
|
|
320
|
-
})
|
|
321
|
-
streamErrorPromise.catch(() => {})
|
|
392
|
+
const streamErrorPromise = createStreamErrorPromise(allStreamsMap)
|
|
322
393
|
|
|
323
394
|
const createAssemblyAndUpload = async () => {
|
|
324
395
|
const result: AssemblyStatus = await this._remoteJson({
|
|
@@ -368,6 +439,124 @@ export class Transloadit {
|
|
|
368
439
|
return Object.assign(promise, { assemblyId: effectiveAssemblyId })
|
|
369
440
|
}
|
|
370
441
|
|
|
442
|
+
async resumeAssemblyUploads(opts: ResumeAssemblyUploadsOptions): Promise<AssemblyStatus> {
|
|
443
|
+
const {
|
|
444
|
+
assemblyUrl,
|
|
445
|
+
files = {},
|
|
446
|
+
uploads = {},
|
|
447
|
+
chunkSize: requestedChunkSize = Number.POSITIVE_INFINITY,
|
|
448
|
+
uploadConcurrency = 10,
|
|
449
|
+
timeout = 24 * 60 * 60 * 1000, // 1 day
|
|
450
|
+
waitForCompletion = false,
|
|
451
|
+
onUploadProgress = () => {},
|
|
452
|
+
onAssemblyProgress = () => {},
|
|
453
|
+
signal,
|
|
454
|
+
} = opts
|
|
455
|
+
|
|
456
|
+
const startTimeMs = getHrTimeMs()
|
|
457
|
+
|
|
458
|
+
getAssemblyIdFromUrl(assemblyUrl)
|
|
459
|
+
const assembly = await this._fetchAssemblyStatus({ url: assemblyUrl, signal })
|
|
460
|
+
const statusUrl = assembly.assembly_ssl_url ?? assembly.assembly_url ?? assemblyUrl
|
|
461
|
+
|
|
462
|
+
const finishedKeys = new Set<string>()
|
|
463
|
+
for (const upload of assembly.uploads ?? []) {
|
|
464
|
+
const key = getUploadKey(upload.field ?? null, upload.basename ?? null, upload.size)
|
|
465
|
+
if (key) finishedKeys.add(key)
|
|
466
|
+
}
|
|
467
|
+
for (const upload of assembly.tus_uploads ?? []) {
|
|
468
|
+
if (!upload.finished) continue
|
|
469
|
+
const key = getUploadKey(upload.fieldname, upload.filename, upload.size)
|
|
470
|
+
if (key) finishedKeys.add(key)
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
const resumeUrls = new Map<string, string>()
|
|
474
|
+
for (const upload of assembly.tus_uploads ?? []) {
|
|
475
|
+
if (upload.finished) continue
|
|
476
|
+
if (!upload.upload_url) continue
|
|
477
|
+
const key = getUploadKey(upload.fieldname, upload.filename, upload.size)
|
|
478
|
+
if (key) resumeUrls.set(key, upload.upload_url)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const descriptors: UploadDescriptor[] = []
|
|
482
|
+
|
|
483
|
+
await pMap(
|
|
484
|
+
Object.entries(files),
|
|
485
|
+
async ([label, path]) => {
|
|
486
|
+
await access(path, constants.F_OK | constants.R_OK)
|
|
487
|
+
const info = await stat(path)
|
|
488
|
+
descriptors.push({
|
|
489
|
+
label,
|
|
490
|
+
path,
|
|
491
|
+
filename: basename(path),
|
|
492
|
+
size: info.size,
|
|
493
|
+
})
|
|
494
|
+
},
|
|
495
|
+
{ concurrency: 5 },
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
for (const [label, value] of Object.entries(uploads)) {
|
|
499
|
+
descriptors.push({
|
|
500
|
+
label,
|
|
501
|
+
filename: label,
|
|
502
|
+
size: isReadableStream(value) ? undefined : getSizeFromValue(value),
|
|
503
|
+
value,
|
|
504
|
+
})
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const descriptorsToUpload = descriptors.filter((descriptor) => {
|
|
508
|
+
const key = getUploadKey(descriptor.label, descriptor.filename, descriptor.size ?? null)
|
|
509
|
+
return key ? !finishedKeys.has(key) : true
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
const uploadUrlsByLabel: Record<string, string> = {}
|
|
513
|
+
for (const descriptor of descriptorsToUpload) {
|
|
514
|
+
if (!descriptor.path) continue
|
|
515
|
+
const key = getUploadKey(descriptor.label, descriptor.filename, descriptor.size ?? null)
|
|
516
|
+
if (!key) continue
|
|
517
|
+
const uploadUrl = resumeUrls.get(key)
|
|
518
|
+
if (uploadUrl) uploadUrlsByLabel[descriptor.label] = uploadUrl
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const streamsMap = buildStreamsMap(descriptorsToUpload)
|
|
522
|
+
pauseStreams(streamsMap)
|
|
523
|
+
|
|
524
|
+
if (Object.keys(streamsMap).length > 0) {
|
|
525
|
+
const streamErrorPromise = createStreamErrorPromise(streamsMap)
|
|
526
|
+
|
|
527
|
+
const uploadPromise = sendTusRequest({
|
|
528
|
+
streamsMap,
|
|
529
|
+
assembly,
|
|
530
|
+
requestedChunkSize,
|
|
531
|
+
uploadConcurrency,
|
|
532
|
+
onProgress: onUploadProgress,
|
|
533
|
+
signal,
|
|
534
|
+
uploadUrls: uploadUrlsByLabel,
|
|
535
|
+
})
|
|
536
|
+
|
|
537
|
+
await Promise.race([uploadPromise, streamErrorPromise])
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const latestAssembly = await this._fetchAssemblyStatus({ url: statusUrl, signal })
|
|
541
|
+
if (!waitForCompletion) return latestAssembly
|
|
542
|
+
|
|
543
|
+
if (latestAssembly.assembly_id == null) {
|
|
544
|
+
throw new InconsistentResponseError(
|
|
545
|
+
'Server returned an assembly response without an assembly_id after resuming uploads',
|
|
546
|
+
)
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
const awaitResult = await this.awaitAssemblyCompletion(latestAssembly.assembly_id, {
|
|
550
|
+
timeout,
|
|
551
|
+
onAssemblyProgress,
|
|
552
|
+
startTimeMs,
|
|
553
|
+
assemblyUrl: statusUrl,
|
|
554
|
+
signal,
|
|
555
|
+
})
|
|
556
|
+
checkResult(awaitResult)
|
|
557
|
+
return awaitResult
|
|
558
|
+
}
|
|
559
|
+
|
|
371
560
|
async awaitAssemblyCompletion(
|
|
372
561
|
assemblyId: string,
|
|
373
562
|
{
|
|
@@ -375,6 +564,7 @@ export class Transloadit {
|
|
|
375
564
|
timeout,
|
|
376
565
|
startTimeMs = getHrTimeMs(),
|
|
377
566
|
interval = 1000,
|
|
567
|
+
assemblyUrl,
|
|
378
568
|
signal,
|
|
379
569
|
onPoll,
|
|
380
570
|
}: AwaitAssemblyCompletionOptions = {},
|
|
@@ -383,6 +573,12 @@ export class Transloadit {
|
|
|
383
573
|
|
|
384
574
|
let lastResult: AssemblyStatus | undefined
|
|
385
575
|
|
|
576
|
+
const fetchAssemblyStatus = (): Promise<AssemblyStatus> => {
|
|
577
|
+
return assemblyUrl
|
|
578
|
+
? this._fetchAssemblyStatus({ url: assemblyUrl, signal })
|
|
579
|
+
: this.getAssembly(assemblyId, { signal })
|
|
580
|
+
}
|
|
581
|
+
|
|
386
582
|
while (true) {
|
|
387
583
|
// Check if caller wants to stop polling early
|
|
388
584
|
if (onPoll?.() === false && lastResult) {
|
|
@@ -394,7 +590,7 @@ export class Transloadit {
|
|
|
394
590
|
throw signal.reason ?? new DOMException('Aborted', 'AbortError')
|
|
395
591
|
}
|
|
396
592
|
|
|
397
|
-
const result = await
|
|
593
|
+
const result = await fetchAssemblyStatus()
|
|
398
594
|
lastResult = result
|
|
399
595
|
|
|
400
596
|
// If 'ok' is not in result, it implies a terminal state (e.g., error, completed, canceled).
|
|
@@ -576,16 +772,33 @@ export class Transloadit {
|
|
|
576
772
|
assemblyId: string,
|
|
577
773
|
options?: { signal?: AbortSignal },
|
|
578
774
|
): Promise<AssemblyStatus> {
|
|
579
|
-
|
|
580
|
-
|
|
775
|
+
return await this._fetchAssemblyStatus({
|
|
776
|
+
assemblyId,
|
|
581
777
|
signal: options?.signal,
|
|
582
778
|
})
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
private async _fetchAssemblyStatus({
|
|
782
|
+
assemblyId,
|
|
783
|
+
url,
|
|
784
|
+
signal,
|
|
785
|
+
}: {
|
|
786
|
+
assemblyId?: string
|
|
787
|
+
url?: string
|
|
788
|
+
signal?: AbortSignal
|
|
789
|
+
}): Promise<AssemblyStatus> {
|
|
790
|
+
const rawResult = await this._remoteJson<Record<string, unknown>, OptionalAuthParams>({
|
|
791
|
+
url,
|
|
792
|
+
urlSuffix: url ? undefined : `/assemblies/${assemblyId}`,
|
|
793
|
+
signal,
|
|
794
|
+
})
|
|
583
795
|
|
|
584
796
|
const parsedResult = zodParseWithContext(assemblyStatusSchema, rawResult)
|
|
585
797
|
|
|
586
798
|
if (!parsedResult.success) {
|
|
799
|
+
const label = assemblyId ?? url ?? 'unknown'
|
|
587
800
|
this.maybeThrowInconsistentResponseError(
|
|
588
|
-
`The API responded with data that does not match the expected schema while getting Assembly: ${
|
|
801
|
+
`The API responded with data that does not match the expected schema while getting Assembly: ${label}.\n${parsedResult.humanReadable}`,
|
|
589
802
|
)
|
|
590
803
|
}
|
|
591
804
|
|
|
@@ -915,23 +1128,40 @@ export class Transloadit {
|
|
|
915
1128
|
|
|
916
1129
|
// check whether we should retry
|
|
917
1130
|
// https://transloadit.com/blog/2012/04/introducing-rate-limiting/
|
|
918
|
-
|
|
1131
|
+
const retryAfterHeader = err.response?.headers?.['retry-after']
|
|
1132
|
+
const retryAfterSeconds =
|
|
1133
|
+
typeof retryAfterHeader === 'string' ? Number(retryAfterHeader) : undefined
|
|
1134
|
+
const retryInFromInfo =
|
|
919
1135
|
typeof body === 'object' &&
|
|
920
1136
|
body != null &&
|
|
921
|
-
'error' in body &&
|
|
922
1137
|
'info' in body &&
|
|
923
1138
|
typeof body.info === 'object' &&
|
|
924
1139
|
body.info != null &&
|
|
925
1140
|
'retryIn' in body.info &&
|
|
926
1141
|
typeof body.info.retryIn === 'number' &&
|
|
927
|
-
|
|
1142
|
+
body.info.retryIn > 0
|
|
1143
|
+
? body.info.retryIn
|
|
1144
|
+
: undefined
|
|
1145
|
+
const retryInSec =
|
|
1146
|
+
retryInFromInfo ??
|
|
1147
|
+
(typeof retryAfterSeconds === 'number' && retryAfterSeconds > 0
|
|
1148
|
+
? retryAfterSeconds
|
|
1149
|
+
: undefined)
|
|
1150
|
+
const shouldRetry =
|
|
928
1151
|
retryCount < this._maxRetries && // 413 taken from https://transloadit.com/blog/2012/04/introducing-rate-limiting/
|
|
929
1152
|
// todo can 413 be removed?
|
|
930
|
-
((statusCode === 413 &&
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
1153
|
+
((statusCode === 413 &&
|
|
1154
|
+
body &&
|
|
1155
|
+
typeof body === 'object' &&
|
|
1156
|
+
body.error === 'RATE_LIMIT_REACHED') ||
|
|
1157
|
+
statusCode === 429)
|
|
1158
|
+
|
|
1159
|
+
if (shouldRetry) {
|
|
1160
|
+
const retryDelaySec = retryInSec ?? 1
|
|
1161
|
+
logWarn(
|
|
1162
|
+
`Rate limit reached, retrying request in approximately ${retryDelaySec} seconds.`,
|
|
1163
|
+
)
|
|
1164
|
+
const retryInMs = 1000 * (retryDelaySec * (1 + 0.1 * Math.random()))
|
|
935
1165
|
await delay(retryInMs)
|
|
936
1166
|
// Retry
|
|
937
1167
|
} else {
|
package/src/alphalib/mcache.ts
CHANGED
|
@@ -37,11 +37,13 @@ interface CacheEntry<T> {
|
|
|
37
37
|
*/
|
|
38
38
|
export class Mcache<T> {
|
|
39
39
|
#cache: Map<string, CacheEntry<T>>
|
|
40
|
+
#pending: Map<string, Promise<T>>
|
|
40
41
|
#opts: Required<Omit<McacheOpts, 'logger' | 'zodSchema' | 'keyFn'>> &
|
|
41
42
|
Pick<McacheOpts, 'logger' | 'zodSchema' | 'keyFn'>
|
|
42
43
|
|
|
43
44
|
constructor(opts: McacheOpts = {}) {
|
|
44
45
|
this.#cache = new Map()
|
|
46
|
+
this.#pending = new Map()
|
|
45
47
|
this.#opts = {
|
|
46
48
|
ttlMs: opts.ttlMs ?? Number.POSITIVE_INFINITY,
|
|
47
49
|
maxSize: opts.maxSize ?? 10_000,
|
|
@@ -56,6 +58,8 @@ export class Mcache<T> {
|
|
|
56
58
|
* The cache key is generated from the args using JSON.stringify by default,
|
|
57
59
|
* or using the custom keyFn if provided.
|
|
58
60
|
*/
|
|
61
|
+
|
|
62
|
+
// biome-ignore lint/suspicious/useAwait: @TODO check this out later
|
|
59
63
|
async get(producer: () => Promise<T> | T, ...args: unknown[]): Promise<T> {
|
|
60
64
|
const key = this.#opts.keyFn ? this.#opts.keyFn(...args) : JSON.stringify(args)
|
|
61
65
|
const cached = this.#cache.get(key)
|
|
@@ -73,16 +77,31 @@ export class Mcache<T> {
|
|
|
73
77
|
this.#cache.delete(key)
|
|
74
78
|
}
|
|
75
79
|
|
|
80
|
+
const pending = this.#pending.get(key)
|
|
81
|
+
if (pending) {
|
|
82
|
+
this.#opts.logger?.debug(`Cache miss for key ${key}, waiting for pending request`)
|
|
83
|
+
return pending
|
|
84
|
+
}
|
|
85
|
+
|
|
76
86
|
this.#opts.logger?.debug(`Cache miss for key ${key}, computing value`)
|
|
77
|
-
const value = await producer()
|
|
78
87
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
this.#opts.zodSchema.parse(value)
|
|
82
|
-
}
|
|
88
|
+
const promise = Promise.resolve().then(async () => {
|
|
89
|
+
const value = await producer()
|
|
83
90
|
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
// Validate if schema provided
|
|
92
|
+
if (this.#opts.zodSchema) {
|
|
93
|
+
this.#opts.zodSchema.parse(value)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
this.#set(key, value)
|
|
97
|
+
return value
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
this.#pending.set(key, promise)
|
|
101
|
+
void promise.finally(() => {
|
|
102
|
+
this.#pending.delete(key)
|
|
103
|
+
})
|
|
104
|
+
return promise
|
|
86
105
|
}
|
|
87
106
|
|
|
88
107
|
/**
|
|
@@ -78,6 +78,8 @@ export const assemblyStatusErrCodeSchema = z.enum([
|
|
|
78
78
|
'DIGITALOCEAN_STORE_ACCESS_DENIED',
|
|
79
79
|
'DO_NOT_REUSE_ASSEMBLY_IDS',
|
|
80
80
|
'DOCUMENT_CONVERT_UNSUPPORTED_CONVERSION',
|
|
81
|
+
'DOCUMENT_OPTIMIZE_UNSUPPORTED_INPUT',
|
|
82
|
+
'DOCUMENT_OPTIMIZE_VALIDATION',
|
|
81
83
|
'DOCUMENT_SPLIT_VALIDATION',
|
|
82
84
|
'FILE_DOWNLOAD_ERROR',
|
|
83
85
|
'FILE_FILTER_DECLINED_FILE',
|
|
@@ -112,6 +114,7 @@ export const assemblyStatusErrCodeSchema = z.enum([
|
|
|
112
114
|
'INVALID_AUTH_REFERER_PARAMETER',
|
|
113
115
|
'INVALID_FILE_META_DATA',
|
|
114
116
|
'INVALID_FORM_DATA',
|
|
117
|
+
'INVALID_URL_ENCODING',
|
|
115
118
|
'INVALID_INPUT_ERROR',
|
|
116
119
|
'INVALID_PARAMS_FIELD',
|
|
117
120
|
'INVALID_SIGNATURE',
|
|
@@ -156,6 +159,9 @@ export const assemblyStatusErrCodeSchema = z.enum([
|
|
|
156
159
|
'TMP_FILE_DOWNLOAD_ERROR',
|
|
157
160
|
'USER_COMMAND_ERROR',
|
|
158
161
|
'VERIFIED_EMAIL_REQUIRED',
|
|
162
|
+
'VIDEO_CONCAT_INVALID_INPUT',
|
|
163
|
+
'VIDEO_CONCAT_NO_OUTPUT',
|
|
164
|
+
'VIDEO_CONCAT_VALIDATION',
|
|
159
165
|
'VIDEO_ENCODE_VALIDATION',
|
|
160
166
|
'VIMEO_IMPORT_FAILURE',
|
|
161
167
|
'WORKER_JOB_ERROR',
|
|
@@ -379,7 +385,7 @@ export const assemblyStatusUploadSchema = z
|
|
|
379
385
|
basename: z.string(),
|
|
380
386
|
ext: z.string(),
|
|
381
387
|
size: z.number(),
|
|
382
|
-
mime: z.string(),
|
|
388
|
+
mime: z.string().nullable(),
|
|
383
389
|
type: z.string().nullable(),
|
|
384
390
|
field: z.string().nullable(),
|
|
385
391
|
md5hash: z.string().nullable(),
|
|
@@ -614,6 +620,8 @@ export const assemblyStatusErrSchema = assemblyStatusBaseSchema
|
|
|
614
620
|
reason: z.string().optional(),
|
|
615
621
|
step: z.string().optional(),
|
|
616
622
|
previousStep: z.string().optional(),
|
|
623
|
+
file: z.string().optional(),
|
|
624
|
+
name: z.string().optional(),
|
|
617
625
|
path: z.string().optional(),
|
|
618
626
|
exitCode: z.number().nullable().optional(),
|
|
619
627
|
exitSignal: z.string().nullable().optional(),
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { AssemblyStatus } from './assemblyStatus.ts'
|
|
1
2
|
import {
|
|
2
|
-
type AssemblyStatus,
|
|
3
3
|
isAssemblyBusyStatus,
|
|
4
4
|
isAssemblyTerminalError,
|
|
5
5
|
isAssemblyTerminalOk,
|
|
@@ -56,10 +56,7 @@ const assemblyUrlKeys = ['assembly_ssl_url', 'assembly_url', 'assemblyUrl'] as c
|
|
|
56
56
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
57
57
|
value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
58
58
|
|
|
59
|
-
const pickString = (
|
|
60
|
-
record: Record<string, unknown>,
|
|
61
|
-
keys: readonly string[],
|
|
62
|
-
): string | null => {
|
|
59
|
+
const pickString = (record: Record<string, unknown>, keys: readonly string[]): string | null => {
|
|
63
60
|
for (const key of keys) {
|
|
64
61
|
const value = record[key]
|
|
65
62
|
if (typeof value === 'string' && value.length > 0) return value
|