transloadit 4.2.0 → 4.4.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 (91) hide show
  1. package/README.md +116 -1
  2. package/dist/Transloadit.d.ts +49 -4
  3. package/dist/Transloadit.d.ts.map +1 -1
  4. package/dist/Transloadit.js +106 -22
  5. package/dist/Transloadit.js.map +1 -1
  6. package/dist/alphalib/assembly-linter.d.ts +123 -0
  7. package/dist/alphalib/assembly-linter.d.ts.map +1 -0
  8. package/dist/alphalib/assembly-linter.js +1142 -0
  9. package/dist/alphalib/assembly-linter.js.map +1 -0
  10. package/dist/alphalib/assembly-linter.lang.en.d.ts +87 -0
  11. package/dist/alphalib/assembly-linter.lang.en.d.ts.map +1 -0
  12. package/dist/alphalib/assembly-linter.lang.en.js +326 -0
  13. package/dist/alphalib/assembly-linter.lang.en.js.map +1 -0
  14. package/dist/alphalib/goldenTemplates.d.ts +52 -0
  15. package/dist/alphalib/goldenTemplates.d.ts.map +1 -0
  16. package/dist/alphalib/goldenTemplates.js +46 -0
  17. package/dist/alphalib/goldenTemplates.js.map +1 -0
  18. package/dist/alphalib/object.d.ts +20 -0
  19. package/dist/alphalib/object.d.ts.map +1 -0
  20. package/dist/alphalib/object.js +23 -0
  21. package/dist/alphalib/object.js.map +1 -0
  22. package/dist/alphalib/stepParsing.d.ts +93 -0
  23. package/dist/alphalib/stepParsing.d.ts.map +1 -0
  24. package/dist/alphalib/stepParsing.js +1154 -0
  25. package/dist/alphalib/stepParsing.js.map +1 -0
  26. package/dist/alphalib/templateMerge.d.ts +4 -0
  27. package/dist/alphalib/templateMerge.d.ts.map +1 -0
  28. package/dist/alphalib/templateMerge.js +22 -0
  29. package/dist/alphalib/templateMerge.js.map +1 -0
  30. package/dist/cli/commands/assemblies.d.ts +20 -1
  31. package/dist/cli/commands/assemblies.d.ts.map +1 -1
  32. package/dist/cli/commands/assemblies.js +137 -2
  33. package/dist/cli/commands/assemblies.js.map +1 -1
  34. package/dist/cli/commands/auth.d.ts.map +1 -1
  35. package/dist/cli/commands/auth.js +19 -19
  36. package/dist/cli/commands/auth.js.map +1 -1
  37. package/dist/cli/commands/index.d.ts.map +1 -1
  38. package/dist/cli/commands/index.js +5 -1
  39. package/dist/cli/commands/index.js.map +1 -1
  40. package/dist/cli/commands/upload.d.ts +22 -0
  41. package/dist/cli/commands/upload.d.ts.map +1 -0
  42. package/dist/cli/commands/upload.js +95 -0
  43. package/dist/cli/commands/upload.js.map +1 -0
  44. package/dist/cli/docs/assemblyLintingExamples.d.ts +2 -0
  45. package/dist/cli/docs/assemblyLintingExamples.d.ts.map +1 -0
  46. package/dist/cli/docs/assemblyLintingExamples.js +10 -0
  47. package/dist/cli/docs/assemblyLintingExamples.js.map +1 -0
  48. package/dist/cli/helpers.d.ts +11 -0
  49. package/dist/cli/helpers.d.ts.map +1 -1
  50. package/dist/cli/helpers.js +29 -0
  51. package/dist/cli/helpers.js.map +1 -1
  52. package/dist/inputFiles.d.ts +41 -0
  53. package/dist/inputFiles.d.ts.map +1 -0
  54. package/dist/inputFiles.js +214 -0
  55. package/dist/inputFiles.js.map +1 -0
  56. package/dist/lintAssemblyInput.d.ts +10 -0
  57. package/dist/lintAssemblyInput.d.ts.map +1 -0
  58. package/dist/lintAssemblyInput.js +73 -0
  59. package/dist/lintAssemblyInput.js.map +1 -0
  60. package/dist/lintAssemblyInstructions.d.ts +29 -0
  61. package/dist/lintAssemblyInstructions.d.ts.map +1 -0
  62. package/dist/lintAssemblyInstructions.js +33 -0
  63. package/dist/lintAssemblyInstructions.js.map +1 -0
  64. package/dist/robots.d.ts +38 -0
  65. package/dist/robots.d.ts.map +1 -0
  66. package/dist/robots.js +230 -0
  67. package/dist/robots.js.map +1 -0
  68. package/dist/tus.d.ts +5 -1
  69. package/dist/tus.d.ts.map +1 -1
  70. package/dist/tus.js +80 -6
  71. package/dist/tus.js.map +1 -1
  72. package/package.json +6 -6
  73. package/src/Transloadit.ts +179 -27
  74. package/src/alphalib/assembly-linter.lang.en.ts +393 -0
  75. package/src/alphalib/assembly-linter.ts +1475 -0
  76. package/src/alphalib/goldenTemplates.ts +53 -0
  77. package/src/alphalib/object.ts +27 -0
  78. package/src/alphalib/stepParsing.ts +1465 -0
  79. package/src/alphalib/templateMerge.ts +32 -0
  80. package/src/alphalib/typings/json-to-ast.d.ts +34 -0
  81. package/src/cli/commands/assemblies.ts +161 -2
  82. package/src/cli/commands/auth.ts +19 -22
  83. package/src/cli/commands/index.ts +6 -0
  84. package/src/cli/commands/upload.ts +129 -0
  85. package/src/cli/docs/assemblyLintingExamples.ts +9 -0
  86. package/src/cli/helpers.ts +50 -0
  87. package/src/inputFiles.ts +278 -0
  88. package/src/lintAssemblyInput.ts +89 -0
  89. package/src/lintAssemblyInstructions.ts +72 -0
  90. package/src/robots.ts +317 -0
  91. package/src/tus.ts +91 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "transloadit",
3
- "version": "4.2.0",
3
+ "version": "4.4.0",
4
4
  "description": "Node.js SDK for Transloadit",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -19,8 +19,8 @@
19
19
  "dependencies": {
20
20
  "@aws-sdk/client-s3": "^3.891.0",
21
21
  "@aws-sdk/s3-request-presigner": "^3.891.0",
22
- "@transloadit/sev-logger": "^0.0.15",
23
- "@transloadit/utils": "^4.2.0",
22
+ "@transloadit/sev-logger": "^0.1.9",
23
+ "@transloadit/utils": "^4.3.0",
24
24
  "clipanion": "^4.0.0-rc.4",
25
25
  "debug": "^4.4.3",
26
26
  "dotenv": "^17.2.3",
@@ -28,6 +28,8 @@
28
28
  "got": "14.4.9",
29
29
  "into-stream": "^9.0.0",
30
30
  "is-stream": "^4.0.1",
31
+ "json-to-ast": "^2.1.0",
32
+ "lodash-es": "^4.17.21",
31
33
  "node-watch": "^0.7.4",
32
34
  "p-map": "^7.0.3",
33
35
  "p-queue": "^9.0.1",
@@ -77,7 +79,5 @@
77
79
  "dist",
78
80
  "src"
79
81
  ],
80
- "bin": {
81
- "transloadit": "./dist/cli.js"
82
- }
82
+ "bin": "./dist/cli.js"
83
83
  }
@@ -45,9 +45,14 @@ import type {
45
45
  TemplateResponse,
46
46
  } from './apiTypes.ts'
47
47
  import InconsistentResponseError from './InconsistentResponseError.ts'
48
+ import type {
49
+ LintAssemblyInstructionsInput,
50
+ LintAssemblyInstructionsResult,
51
+ } from './lintAssemblyInstructions.ts'
52
+ import { lintAssemblyInstructions as lintAssemblyInstructionsInternal } from './lintAssemblyInstructions.ts'
48
53
  import PaginationStream from './PaginationStream.ts'
49
54
  import PollingTimeoutError from './PollingTimeoutError.ts'
50
- import type { Stream } from './tus.ts'
55
+ import type { Stream, UploadBehavior } from './tus.ts'
51
56
  import { sendTusRequest } from './tus.ts'
52
57
 
53
58
  // See https://github.com/sindresorhus/got/tree/v11.8.6?tab=readme-ov-file#errors
@@ -61,10 +66,30 @@ export {
61
66
  TimeoutError,
62
67
  UploadError,
63
68
  } from 'got'
64
-
69
+ export { goldenTemplates } from './alphalib/goldenTemplates.ts'
65
70
  export type { AssemblyStatus } from './alphalib/types/assemblyStatus.ts'
66
71
  export * from './apiTypes.ts'
67
72
  export { InconsistentResponseError, ApiError }
73
+ export { mergeTemplateContent } from './alphalib/templateMerge.ts'
74
+ export type {
75
+ Base64Strategy,
76
+ InputFile,
77
+ PrepareInputFilesOptions,
78
+ PrepareInputFilesResult,
79
+ UploadInput,
80
+ UrlStrategy,
81
+ } from './inputFiles.ts'
82
+ export { prepareInputFiles } from './inputFiles.ts'
83
+ export type { LintAssemblyInstructionsResult, LintFatalLevel } from './lintAssemblyInstructions.ts'
84
+ export type {
85
+ RobotHelp,
86
+ RobotHelpOptions,
87
+ RobotListItem,
88
+ RobotListOptions,
89
+ RobotListResult,
90
+ RobotParamHelp,
91
+ } from './robots.ts'
92
+ export { getRobotHelp, listRobots } from './robots.ts'
68
93
 
69
94
  const log = debug('transloadit')
70
95
  const logWarn = debug('transloadit:warn')
@@ -74,6 +99,12 @@ export interface UploadProgress {
74
99
  totalBytes?: number | undefined
75
100
  }
76
101
 
102
+ export type { UploadBehavior }
103
+
104
+ export type AssemblyStatusWithUploadUrls = AssemblyStatus & {
105
+ upload_urls?: Record<string, string>
106
+ }
107
+
77
108
  const { version } = packageJson
78
109
 
79
110
  export type AssemblyProgress = (assembly: AssemblyStatus) => void
@@ -151,6 +182,7 @@ interface AssemblyUploadOptions {
151
182
  uploads?: {
152
183
  [name: string]: Readable | IntoStreamInput
153
184
  }
185
+ uploadBehavior?: UploadBehavior
154
186
  waitForCompletion?: boolean
155
187
  chunkSize?: number
156
188
  uploadConcurrency?: number
@@ -167,6 +199,10 @@ interface AssemblyUploadOptions {
167
199
  export interface CreateAssemblyOptions extends AssemblyUploadOptions {
168
200
  params?: CreateAssemblyParams
169
201
  assemblyId?: string
202
+ /**
203
+ * Expected number of tus uploads when files will be uploaded separately.
204
+ */
205
+ expectedUploads?: number
170
206
  }
171
207
 
172
208
  export interface ResumeAssemblyUploadsOptions extends AssemblyUploadOptions {
@@ -196,6 +232,14 @@ export interface AwaitAssemblyCompletionOptions {
196
232
  onPoll?: () => boolean | undefined
197
233
  }
198
234
 
235
+ export interface LintAssemblyInstructionsOptions
236
+ extends Omit<LintAssemblyInstructionsInput, 'template'> {
237
+ /**
238
+ * Template ID to merge with the provided instructions before linting.
239
+ */
240
+ templateId?: string
241
+ }
242
+
199
243
  export interface SmartCDNUrlOptions {
200
244
  /**
201
245
  * Workspace slug
@@ -223,7 +267,7 @@ export interface SmartCDNUrlOptions {
223
267
  export type Fields = Record<string, string | number>
224
268
 
225
269
  // A special promise that lets the user immediately get the assembly ID (synchronously before the request is sent)
226
- interface CreateAssemblyPromise extends Promise<AssemblyStatus> {
270
+ interface CreateAssemblyPromise extends Promise<AssemblyStatusWithUploadUrls> {
227
271
  assemblyId: string
228
272
  }
229
273
 
@@ -259,9 +303,19 @@ function checkResult<T>(result: T | { error: string }): asserts result is T {
259
303
  }
260
304
  }
261
305
 
262
- export interface Options {
306
+ type AuthKeySecret = {
263
307
  authKey: string
264
308
  authSecret: string
309
+ authToken?: undefined
310
+ }
311
+
312
+ type AuthToken = {
313
+ authToken: string
314
+ authKey?: string
315
+ authSecret?: string
316
+ }
317
+
318
+ type BaseOptions = {
265
319
  endpoint?: string
266
320
  maxRetries?: number
267
321
  timeout?: number
@@ -269,11 +323,15 @@ export interface Options {
269
323
  validateResponses?: boolean
270
324
  }
271
325
 
326
+ export type Options = BaseOptions & (AuthKeySecret | AuthToken)
327
+
272
328
  export class Transloadit {
273
329
  private _authKey: string
274
330
 
275
331
  private _authSecret: string
276
332
 
333
+ private _authToken: string | null
334
+
277
335
  private _endpoint: string
278
336
 
279
337
  private _maxRetries: number
@@ -287,20 +345,26 @@ export class Transloadit {
287
345
  private _validateResponses = false
288
346
 
289
347
  constructor(opts: Options) {
290
- if (opts?.authKey == null) {
291
- throw new Error('Please provide an authKey')
292
- }
293
-
294
- if (opts.authSecret == null) {
295
- throw new Error('Please provide an authSecret')
296
- }
348
+ const rawToken = typeof opts?.authToken === 'string' ? opts.authToken.trim() : ''
349
+ const hasToken = rawToken.length > 0
297
350
 
298
351
  if (opts.endpoint?.endsWith('/')) {
299
352
  throw new Error('Trailing slash in endpoint is not allowed')
300
353
  }
301
354
 
302
- this._authKey = opts.authKey
303
- this._authSecret = opts.authSecret
355
+ if (!hasToken) {
356
+ if (opts?.authKey == null) {
357
+ throw new Error('Please provide an authKey')
358
+ }
359
+
360
+ if (opts.authSecret == null) {
361
+ throw new Error('Please provide an authSecret')
362
+ }
363
+ }
364
+
365
+ this._authKey = opts.authKey ?? ''
366
+ this._authSecret = opts.authSecret ?? ''
367
+ this._authToken = hasToken ? rawToken : null
304
368
  this._endpoint = opts.endpoint || 'https://api2.transloadit.com'
305
369
  this._maxRetries = opts.maxRetries != null ? opts.maxRetries : 5
306
370
  this._defaultTimeout = opts.timeout != null ? opts.timeout : 60000
@@ -336,7 +400,9 @@ export class Transloadit {
336
400
  files = {},
337
401
  uploads = {},
338
402
  assemblyId,
403
+ expectedUploads,
339
404
  signal,
405
+ uploadBehavior = 'await',
340
406
  } = opts
341
407
 
342
408
  // Keep track of how long the request took
@@ -392,30 +458,38 @@ export class Transloadit {
392
458
  const streamErrorPromise = createStreamErrorPromise(allStreamsMap)
393
459
 
394
460
  const createAssemblyAndUpload = async () => {
395
- const result: AssemblyStatus = await this._remoteJson({
461
+ const totalExpectedUploads =
462
+ expectedUploads == null ? allStreams.length : Math.max(expectedUploads, allStreams.length)
463
+
464
+ const result: AssemblyStatusWithUploadUrls = await this._remoteJson({
396
465
  urlSuffix,
397
466
  method: 'post',
398
467
  timeout: { request: timeout },
399
468
  params,
400
469
  fields: {
401
- tus_num_expected_upload_files: allStreams.length,
470
+ tus_num_expected_upload_files: totalExpectedUploads,
402
471
  },
403
472
  signal,
404
473
  })
405
474
  checkResult(result)
406
475
 
407
476
  if (Object.keys(allStreamsMap).length > 0) {
408
- await sendTusRequest({
477
+ const { uploadUrls } = await sendTusRequest({
409
478
  streamsMap: allStreamsMap,
410
479
  assembly: result,
411
480
  onProgress: onUploadProgress,
412
481
  requestedChunkSize,
413
482
  uploadConcurrency,
414
483
  signal,
484
+ uploadBehavior,
415
485
  })
486
+ if (uploadBehavior !== 'await' && Object.keys(uploadUrls).length > 0) {
487
+ result.upload_urls = uploadUrls
488
+ }
416
489
  }
417
490
 
418
- if (!waitForCompletion) return result
491
+ const shouldWaitForCompletion = waitForCompletion && uploadBehavior === 'await'
492
+ if (!shouldWaitForCompletion) return result
419
493
 
420
494
  if (result.assembly_id == null) {
421
495
  throw new InconsistentResponseError(
@@ -439,7 +513,34 @@ export class Transloadit {
439
513
  return Object.assign(promise, { assemblyId: effectiveAssemblyId })
440
514
  }
441
515
 
442
- async resumeAssemblyUploads(opts: ResumeAssemblyUploadsOptions): Promise<AssemblyStatus> {
516
+ /**
517
+ * Lint Assembly Instructions locally.
518
+ *
519
+ * If a templateId is provided, the template content is merged with the instructions,
520
+ * just like the API. When a template sets `allow_steps_override=false`, providing
521
+ * `steps` will throw a TEMPLATE_DENIES_STEPS_OVERRIDE error.
522
+ *
523
+ * The `assemblyInstructions` input may be a JSON string, a full instructions object,
524
+ * or a steps-only object (missing the `steps` property).
525
+ */
526
+ async lintAssemblyInstructions(
527
+ options: LintAssemblyInstructionsOptions,
528
+ ): Promise<LintAssemblyInstructionsResult> {
529
+ const { templateId, ...rest } = options
530
+ if (!templateId) {
531
+ return await lintAssemblyInstructionsInternal(rest)
532
+ }
533
+
534
+ const template = await this.getTemplate(templateId)
535
+ return await lintAssemblyInstructionsInternal({
536
+ ...rest,
537
+ template: template.content,
538
+ })
539
+ }
540
+
541
+ async resumeAssemblyUploads(
542
+ opts: ResumeAssemblyUploadsOptions,
543
+ ): Promise<AssemblyStatusWithUploadUrls> {
443
544
  const {
444
545
  assemblyUrl,
445
546
  files = {},
@@ -451,12 +552,16 @@ export class Transloadit {
451
552
  onUploadProgress = () => {},
452
553
  onAssemblyProgress = () => {},
453
554
  signal,
555
+ uploadBehavior = 'await',
454
556
  } = opts
455
557
 
456
558
  const startTimeMs = getHrTimeMs()
457
559
 
458
560
  getAssemblyIdFromUrl(assemblyUrl)
459
- const assembly = await this._fetchAssemblyStatus({ url: assemblyUrl, signal })
561
+ const assembly: AssemblyStatusWithUploadUrls = await this._fetchAssemblyStatus({
562
+ url: assemblyUrl,
563
+ signal,
564
+ })
460
565
  const statusUrl = assembly.assembly_ssl_url ?? assembly.assembly_url ?? assemblyUrl
461
566
 
462
567
  const finishedKeys = new Set<string>()
@@ -532,13 +637,25 @@ export class Transloadit {
532
637
  onProgress: onUploadProgress,
533
638
  signal,
534
639
  uploadUrls: uploadUrlsByLabel,
640
+ uploadBehavior,
535
641
  })
536
642
 
537
643
  await Promise.race([uploadPromise, streamErrorPromise])
644
+ const { uploadUrls } = await uploadPromise
645
+ if (uploadBehavior !== 'await' && Object.keys(uploadUrls).length > 0) {
646
+ assembly.upload_urls = uploadUrls
647
+ }
538
648
  }
539
649
 
540
- const latestAssembly = await this._fetchAssemblyStatus({ url: statusUrl, signal })
541
- if (!waitForCompletion) return latestAssembly
650
+ const latestAssembly: AssemblyStatusWithUploadUrls = await this._fetchAssemblyStatus({
651
+ url: statusUrl,
652
+ signal,
653
+ })
654
+ if (uploadBehavior !== 'await' && assembly.upload_urls) {
655
+ latestAssembly.upload_urls = assembly.upload_urls
656
+ }
657
+ const shouldWaitForCompletion = waitForCompletion && uploadBehavior === 'await'
658
+ if (!shouldWaitForCompletion) return latestAssembly
542
659
 
543
660
  if (latestAssembly.assembly_id == null) {
544
661
  throw new InconsistentResponseError(
@@ -662,6 +779,7 @@ export class Transloadit {
662
779
  const { assembly_ssl_url: url } = await this.getAssembly(assemblyId)
663
780
  const rawResult = await this._remoteJson<Record<string, unknown>, OptionalAuthParams>({
664
781
  url,
782
+ isTrustedUrl: true,
665
783
  method: 'delete',
666
784
  })
667
785
 
@@ -790,6 +908,7 @@ export class Transloadit {
790
908
  const rawResult = await this._remoteJson<Record<string, unknown>, OptionalAuthParams>({
791
909
  url,
792
910
  urlSuffix: url ? undefined : `/assemblies/${assemblyId}`,
911
+ isTrustedUrl: Boolean(url),
793
912
  signal,
794
913
  })
795
914
 
@@ -982,6 +1101,9 @@ export class Transloadit {
982
1101
  params: OptionalAuthParams,
983
1102
  algorithm?: string,
984
1103
  ): { signature: string; params: string } {
1104
+ if (!this._authKey || !this._authSecret) {
1105
+ throw new Error('Cannot sign params without authKey and authSecret.')
1106
+ }
985
1107
  const jsonParams = this._prepareParams(params)
986
1108
  const signature = this._calcSignature(jsonParams, algorithm)
987
1109
 
@@ -992,6 +1114,9 @@ export class Transloadit {
992
1114
  * Construct a signed Smart CDN URL. See https://transloadit.com/docs/topics/signature-authentication/#smart-cdn.
993
1115
  */
994
1116
  getSignedSmartCDNUrl(opts: SmartCDNUrlOptions): string {
1117
+ if (!this._authKey || !this._authSecret) {
1118
+ throw new Error('authKey and authSecret are required to sign Smart CDN URLs.')
1119
+ }
995
1120
  return getSignedSmartCdnUrl({
996
1121
  ...opts,
997
1122
  authKey: this._authKey,
@@ -1000,15 +1125,24 @@ export class Transloadit {
1000
1125
  }
1001
1126
 
1002
1127
  private _calcSignature(toSign: string, algorithm = 'sha384'): string {
1128
+ if (!this._authSecret) {
1129
+ throw new Error('Cannot sign params without authSecret.')
1130
+ }
1003
1131
  return signParamsSync(toSign, this._authSecret, algorithm)
1004
1132
  }
1005
1133
 
1006
1134
  // Sets the multipart/form-data for POST, PUT and DELETE requests, including
1007
1135
  // the streams, the signed params, and any additional fields.
1008
1136
  private _appendForm(form: FormData, params: OptionalAuthParams, fields?: Fields): void {
1009
- const sigData = this.calcSignature(params)
1010
- const jsonParams = sigData.params
1011
- const { signature } = sigData
1137
+ const shouldSign = Boolean(this._authKey && this._authSecret)
1138
+ let jsonParams = JSON.stringify(params ?? {})
1139
+ let signature: string | undefined
1140
+
1141
+ if (shouldSign) {
1142
+ const sigData = this.calcSignature(params)
1143
+ jsonParams = sigData.params
1144
+ signature = sigData.signature
1145
+ }
1012
1146
 
1013
1147
  form.append('params', jsonParams)
1014
1148
 
@@ -1018,16 +1152,24 @@ export class Transloadit {
1018
1152
  }
1019
1153
  }
1020
1154
 
1021
- form.append('signature', signature)
1155
+ if (signature) {
1156
+ form.append('signature', signature)
1157
+ }
1022
1158
  }
1023
1159
 
1024
1160
  // Implements HTTP GET query params, handling the case where the url already
1025
1161
  // has params.
1026
1162
  private _appendParamsToUrl(url: string, params: OptionalAuthParams): string {
1027
- const { signature, params: jsonParams } = this.calcSignature(params)
1028
-
1029
1163
  const prefix = url.indexOf('?') === -1 ? '?' : '&'
1030
1164
 
1165
+ const shouldSign = Boolean(this._authKey && this._authSecret)
1166
+ if (!shouldSign) {
1167
+ const jsonParams = JSON.stringify(params ?? {})
1168
+ return `${url}${prefix}params=${encodeURIComponent(jsonParams)}`
1169
+ }
1170
+
1171
+ const { signature, params: jsonParams } = this.calcSignature(params)
1172
+
1031
1173
  return `${url}${prefix}signature=${signature}&params=${encodeURIComponent(jsonParams)}`
1032
1174
  }
1033
1175
 
@@ -1063,6 +1205,7 @@ export class Transloadit {
1063
1205
  private async _remoteJson<TRet, TParams extends OptionalAuthParams>(opts: {
1064
1206
  urlSuffix?: string
1065
1207
  url?: string
1208
+ isTrustedUrl?: boolean
1066
1209
  timeout?: Delays
1067
1210
  method?: 'delete' | 'get' | 'post' | 'put'
1068
1211
  params?: TParams
@@ -1073,6 +1216,7 @@ export class Transloadit {
1073
1216
  const {
1074
1217
  urlSuffix,
1075
1218
  url: urlInput,
1219
+ isTrustedUrl = false,
1076
1220
  timeout = { request: this._defaultTimeout },
1077
1221
  method = 'get',
1078
1222
  params = {},
@@ -1084,6 +1228,13 @@ export class Transloadit {
1084
1228
  // Allow providing either a `urlSuffix` or a full `url`
1085
1229
  if (!urlSuffix && !urlInput) throw new Error('No URL provided')
1086
1230
  let url = urlInput || `${this._endpoint}${urlSuffix}`
1231
+ if (urlInput && !isTrustedUrl) {
1232
+ const allowed = new URL(this._endpoint)
1233
+ const candidate = new URL(urlInput)
1234
+ if (allowed.origin !== candidate.origin) {
1235
+ throw new Error(`Untrusted URL: ${candidate.origin}`)
1236
+ }
1237
+ }
1087
1238
 
1088
1239
  if (method === 'get') {
1089
1240
  url = this._appendParamsToUrl(url, params)
@@ -1108,6 +1259,7 @@ export class Transloadit {
1108
1259
  headers: {
1109
1260
  'Transloadit-Client': `node-sdk:${version}`,
1110
1261
  'User-Agent': undefined, // Remove got's user-agent
1262
+ ...(this._authToken ? { Authorization: `Bearer ${this._authToken}` } : {}),
1111
1263
  ...headers,
1112
1264
  },
1113
1265
  responseType: 'json',