@transloadit/node 4.2.0 → 4.3.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.
Files changed (86) hide show
  1. package/README.md +116 -4
  2. package/dist/Transloadit.d.ts +45 -4
  3. package/dist/Transloadit.d.ts.map +1 -1
  4. package/dist/Transloadit.js +104 -21
  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 +2 -1
  39. package/dist/cli/commands/index.js.map +1 -1
  40. package/dist/cli/docs/assemblyLintingExamples.d.ts +2 -0
  41. package/dist/cli/docs/assemblyLintingExamples.d.ts.map +1 -0
  42. package/dist/cli/docs/assemblyLintingExamples.js +10 -0
  43. package/dist/cli/docs/assemblyLintingExamples.js.map +1 -0
  44. package/dist/cli/helpers.d.ts +11 -0
  45. package/dist/cli/helpers.d.ts.map +1 -1
  46. package/dist/cli/helpers.js +29 -0
  47. package/dist/cli/helpers.js.map +1 -1
  48. package/dist/inputFiles.d.ts +41 -0
  49. package/dist/inputFiles.d.ts.map +1 -0
  50. package/dist/inputFiles.js +214 -0
  51. package/dist/inputFiles.js.map +1 -0
  52. package/dist/lintAssemblyInput.d.ts +10 -0
  53. package/dist/lintAssemblyInput.d.ts.map +1 -0
  54. package/dist/lintAssemblyInput.js +73 -0
  55. package/dist/lintAssemblyInput.js.map +1 -0
  56. package/dist/lintAssemblyInstructions.d.ts +29 -0
  57. package/dist/lintAssemblyInstructions.d.ts.map +1 -0
  58. package/dist/lintAssemblyInstructions.js +33 -0
  59. package/dist/lintAssemblyInstructions.js.map +1 -0
  60. package/dist/robots.d.ts +38 -0
  61. package/dist/robots.d.ts.map +1 -0
  62. package/dist/robots.js +230 -0
  63. package/dist/robots.js.map +1 -0
  64. package/dist/tus.d.ts +5 -1
  65. package/dist/tus.d.ts.map +1 -1
  66. package/dist/tus.js +80 -6
  67. package/dist/tus.js.map +1 -1
  68. package/package.json +5 -2
  69. package/src/Transloadit.ts +170 -26
  70. package/src/alphalib/assembly-linter.lang.en.ts +393 -0
  71. package/src/alphalib/assembly-linter.ts +1475 -0
  72. package/src/alphalib/goldenTemplates.ts +53 -0
  73. package/src/alphalib/object.ts +27 -0
  74. package/src/alphalib/stepParsing.ts +1465 -0
  75. package/src/alphalib/templateMerge.ts +32 -0
  76. package/src/alphalib/typings/json-to-ast.d.ts +34 -0
  77. package/src/cli/commands/assemblies.ts +161 -2
  78. package/src/cli/commands/auth.ts +19 -22
  79. package/src/cli/commands/index.ts +2 -0
  80. package/src/cli/docs/assemblyLintingExamples.ts +9 -0
  81. package/src/cli/helpers.ts +50 -0
  82. package/src/inputFiles.ts +278 -0
  83. package/src/lintAssemblyInput.ts +89 -0
  84. package/src/lintAssemblyInstructions.ts +72 -0
  85. package/src/robots.ts +317 -0
  86. package/src/tus.ts +91 -5
@@ -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
@@ -196,6 +228,14 @@ export interface AwaitAssemblyCompletionOptions {
196
228
  onPoll?: () => boolean | undefined
197
229
  }
198
230
 
231
+ export interface LintAssemblyInstructionsOptions
232
+ extends Omit<LintAssemblyInstructionsInput, 'template'> {
233
+ /**
234
+ * Template ID to merge with the provided instructions before linting.
235
+ */
236
+ templateId?: string
237
+ }
238
+
199
239
  export interface SmartCDNUrlOptions {
200
240
  /**
201
241
  * Workspace slug
@@ -223,7 +263,7 @@ export interface SmartCDNUrlOptions {
223
263
  export type Fields = Record<string, string | number>
224
264
 
225
265
  // A special promise that lets the user immediately get the assembly ID (synchronously before the request is sent)
226
- interface CreateAssemblyPromise extends Promise<AssemblyStatus> {
266
+ interface CreateAssemblyPromise extends Promise<AssemblyStatusWithUploadUrls> {
227
267
  assemblyId: string
228
268
  }
229
269
 
@@ -259,9 +299,19 @@ function checkResult<T>(result: T | { error: string }): asserts result is T {
259
299
  }
260
300
  }
261
301
 
262
- export interface Options {
302
+ type AuthKeySecret = {
263
303
  authKey: string
264
304
  authSecret: string
305
+ authToken?: undefined
306
+ }
307
+
308
+ type AuthToken = {
309
+ authToken: string
310
+ authKey?: string
311
+ authSecret?: string
312
+ }
313
+
314
+ type BaseOptions = {
265
315
  endpoint?: string
266
316
  maxRetries?: number
267
317
  timeout?: number
@@ -269,11 +319,15 @@ export interface Options {
269
319
  validateResponses?: boolean
270
320
  }
271
321
 
322
+ export type Options = BaseOptions & (AuthKeySecret | AuthToken)
323
+
272
324
  export class Transloadit {
273
325
  private _authKey: string
274
326
 
275
327
  private _authSecret: string
276
328
 
329
+ private _authToken: string | null
330
+
277
331
  private _endpoint: string
278
332
 
279
333
  private _maxRetries: number
@@ -287,20 +341,26 @@ export class Transloadit {
287
341
  private _validateResponses = false
288
342
 
289
343
  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
- }
344
+ const rawToken = typeof opts?.authToken === 'string' ? opts.authToken.trim() : ''
345
+ const hasToken = rawToken.length > 0
297
346
 
298
347
  if (opts.endpoint?.endsWith('/')) {
299
348
  throw new Error('Trailing slash in endpoint is not allowed')
300
349
  }
301
350
 
302
- this._authKey = opts.authKey
303
- this._authSecret = opts.authSecret
351
+ if (!hasToken) {
352
+ if (opts?.authKey == null) {
353
+ throw new Error('Please provide an authKey')
354
+ }
355
+
356
+ if (opts.authSecret == null) {
357
+ throw new Error('Please provide an authSecret')
358
+ }
359
+ }
360
+
361
+ this._authKey = opts.authKey ?? ''
362
+ this._authSecret = opts.authSecret ?? ''
363
+ this._authToken = hasToken ? rawToken : null
304
364
  this._endpoint = opts.endpoint || 'https://api2.transloadit.com'
305
365
  this._maxRetries = opts.maxRetries != null ? opts.maxRetries : 5
306
366
  this._defaultTimeout = opts.timeout != null ? opts.timeout : 60000
@@ -337,6 +397,7 @@ export class Transloadit {
337
397
  uploads = {},
338
398
  assemblyId,
339
399
  signal,
400
+ uploadBehavior = 'await',
340
401
  } = opts
341
402
 
342
403
  // Keep track of how long the request took
@@ -392,7 +453,7 @@ export class Transloadit {
392
453
  const streamErrorPromise = createStreamErrorPromise(allStreamsMap)
393
454
 
394
455
  const createAssemblyAndUpload = async () => {
395
- const result: AssemblyStatus = await this._remoteJson({
456
+ const result: AssemblyStatusWithUploadUrls = await this._remoteJson({
396
457
  urlSuffix,
397
458
  method: 'post',
398
459
  timeout: { request: timeout },
@@ -405,17 +466,22 @@ export class Transloadit {
405
466
  checkResult(result)
406
467
 
407
468
  if (Object.keys(allStreamsMap).length > 0) {
408
- await sendTusRequest({
469
+ const { uploadUrls } = await sendTusRequest({
409
470
  streamsMap: allStreamsMap,
410
471
  assembly: result,
411
472
  onProgress: onUploadProgress,
412
473
  requestedChunkSize,
413
474
  uploadConcurrency,
414
475
  signal,
476
+ uploadBehavior,
415
477
  })
478
+ if (uploadBehavior !== 'await' && Object.keys(uploadUrls).length > 0) {
479
+ result.upload_urls = uploadUrls
480
+ }
416
481
  }
417
482
 
418
- if (!waitForCompletion) return result
483
+ const shouldWaitForCompletion = waitForCompletion && uploadBehavior === 'await'
484
+ if (!shouldWaitForCompletion) return result
419
485
 
420
486
  if (result.assembly_id == null) {
421
487
  throw new InconsistentResponseError(
@@ -439,7 +505,34 @@ export class Transloadit {
439
505
  return Object.assign(promise, { assemblyId: effectiveAssemblyId })
440
506
  }
441
507
 
442
- async resumeAssemblyUploads(opts: ResumeAssemblyUploadsOptions): Promise<AssemblyStatus> {
508
+ /**
509
+ * Lint Assembly Instructions locally.
510
+ *
511
+ * If a templateId is provided, the template content is merged with the instructions,
512
+ * just like the API. When a template sets `allow_steps_override=false`, providing
513
+ * `steps` will throw a TEMPLATE_DENIES_STEPS_OVERRIDE error.
514
+ *
515
+ * The `assemblyInstructions` input may be a JSON string, a full instructions object,
516
+ * or a steps-only object (missing the `steps` property).
517
+ */
518
+ async lintAssemblyInstructions(
519
+ options: LintAssemblyInstructionsOptions,
520
+ ): Promise<LintAssemblyInstructionsResult> {
521
+ const { templateId, ...rest } = options
522
+ if (!templateId) {
523
+ return await lintAssemblyInstructionsInternal(rest)
524
+ }
525
+
526
+ const template = await this.getTemplate(templateId)
527
+ return await lintAssemblyInstructionsInternal({
528
+ ...rest,
529
+ template: template.content,
530
+ })
531
+ }
532
+
533
+ async resumeAssemblyUploads(
534
+ opts: ResumeAssemblyUploadsOptions,
535
+ ): Promise<AssemblyStatusWithUploadUrls> {
443
536
  const {
444
537
  assemblyUrl,
445
538
  files = {},
@@ -451,12 +544,16 @@ export class Transloadit {
451
544
  onUploadProgress = () => {},
452
545
  onAssemblyProgress = () => {},
453
546
  signal,
547
+ uploadBehavior = 'await',
454
548
  } = opts
455
549
 
456
550
  const startTimeMs = getHrTimeMs()
457
551
 
458
552
  getAssemblyIdFromUrl(assemblyUrl)
459
- const assembly = await this._fetchAssemblyStatus({ url: assemblyUrl, signal })
553
+ const assembly: AssemblyStatusWithUploadUrls = await this._fetchAssemblyStatus({
554
+ url: assemblyUrl,
555
+ signal,
556
+ })
460
557
  const statusUrl = assembly.assembly_ssl_url ?? assembly.assembly_url ?? assemblyUrl
461
558
 
462
559
  const finishedKeys = new Set<string>()
@@ -532,13 +629,25 @@ export class Transloadit {
532
629
  onProgress: onUploadProgress,
533
630
  signal,
534
631
  uploadUrls: uploadUrlsByLabel,
632
+ uploadBehavior,
535
633
  })
536
634
 
537
635
  await Promise.race([uploadPromise, streamErrorPromise])
636
+ const { uploadUrls } = await uploadPromise
637
+ if (uploadBehavior !== 'await' && Object.keys(uploadUrls).length > 0) {
638
+ assembly.upload_urls = uploadUrls
639
+ }
538
640
  }
539
641
 
540
- const latestAssembly = await this._fetchAssemblyStatus({ url: statusUrl, signal })
541
- if (!waitForCompletion) return latestAssembly
642
+ const latestAssembly: AssemblyStatusWithUploadUrls = await this._fetchAssemblyStatus({
643
+ url: statusUrl,
644
+ signal,
645
+ })
646
+ if (uploadBehavior !== 'await' && assembly.upload_urls) {
647
+ latestAssembly.upload_urls = assembly.upload_urls
648
+ }
649
+ const shouldWaitForCompletion = waitForCompletion && uploadBehavior === 'await'
650
+ if (!shouldWaitForCompletion) return latestAssembly
542
651
 
543
652
  if (latestAssembly.assembly_id == null) {
544
653
  throw new InconsistentResponseError(
@@ -662,6 +771,7 @@ export class Transloadit {
662
771
  const { assembly_ssl_url: url } = await this.getAssembly(assemblyId)
663
772
  const rawResult = await this._remoteJson<Record<string, unknown>, OptionalAuthParams>({
664
773
  url,
774
+ isTrustedUrl: true,
665
775
  method: 'delete',
666
776
  })
667
777
 
@@ -790,6 +900,7 @@ export class Transloadit {
790
900
  const rawResult = await this._remoteJson<Record<string, unknown>, OptionalAuthParams>({
791
901
  url,
792
902
  urlSuffix: url ? undefined : `/assemblies/${assemblyId}`,
903
+ isTrustedUrl: Boolean(url),
793
904
  signal,
794
905
  })
795
906
 
@@ -982,6 +1093,9 @@ export class Transloadit {
982
1093
  params: OptionalAuthParams,
983
1094
  algorithm?: string,
984
1095
  ): { signature: string; params: string } {
1096
+ if (!this._authKey || !this._authSecret) {
1097
+ throw new Error('Cannot sign params without authKey and authSecret.')
1098
+ }
985
1099
  const jsonParams = this._prepareParams(params)
986
1100
  const signature = this._calcSignature(jsonParams, algorithm)
987
1101
 
@@ -992,6 +1106,9 @@ export class Transloadit {
992
1106
  * Construct a signed Smart CDN URL. See https://transloadit.com/docs/topics/signature-authentication/#smart-cdn.
993
1107
  */
994
1108
  getSignedSmartCDNUrl(opts: SmartCDNUrlOptions): string {
1109
+ if (!this._authKey || !this._authSecret) {
1110
+ throw new Error('authKey and authSecret are required to sign Smart CDN URLs.')
1111
+ }
995
1112
  return getSignedSmartCdnUrl({
996
1113
  ...opts,
997
1114
  authKey: this._authKey,
@@ -1000,15 +1117,24 @@ export class Transloadit {
1000
1117
  }
1001
1118
 
1002
1119
  private _calcSignature(toSign: string, algorithm = 'sha384'): string {
1120
+ if (!this._authSecret) {
1121
+ throw new Error('Cannot sign params without authSecret.')
1122
+ }
1003
1123
  return signParamsSync(toSign, this._authSecret, algorithm)
1004
1124
  }
1005
1125
 
1006
1126
  // Sets the multipart/form-data for POST, PUT and DELETE requests, including
1007
1127
  // the streams, the signed params, and any additional fields.
1008
1128
  private _appendForm(form: FormData, params: OptionalAuthParams, fields?: Fields): void {
1009
- const sigData = this.calcSignature(params)
1010
- const jsonParams = sigData.params
1011
- const { signature } = sigData
1129
+ const shouldSign = Boolean(this._authKey && this._authSecret)
1130
+ let jsonParams = JSON.stringify(params ?? {})
1131
+ let signature: string | undefined
1132
+
1133
+ if (shouldSign) {
1134
+ const sigData = this.calcSignature(params)
1135
+ jsonParams = sigData.params
1136
+ signature = sigData.signature
1137
+ }
1012
1138
 
1013
1139
  form.append('params', jsonParams)
1014
1140
 
@@ -1018,16 +1144,24 @@ export class Transloadit {
1018
1144
  }
1019
1145
  }
1020
1146
 
1021
- form.append('signature', signature)
1147
+ if (signature) {
1148
+ form.append('signature', signature)
1149
+ }
1022
1150
  }
1023
1151
 
1024
1152
  // Implements HTTP GET query params, handling the case where the url already
1025
1153
  // has params.
1026
1154
  private _appendParamsToUrl(url: string, params: OptionalAuthParams): string {
1027
- const { signature, params: jsonParams } = this.calcSignature(params)
1028
-
1029
1155
  const prefix = url.indexOf('?') === -1 ? '?' : '&'
1030
1156
 
1157
+ const shouldSign = Boolean(this._authKey && this._authSecret)
1158
+ if (!shouldSign) {
1159
+ const jsonParams = JSON.stringify(params ?? {})
1160
+ return `${url}${prefix}params=${encodeURIComponent(jsonParams)}`
1161
+ }
1162
+
1163
+ const { signature, params: jsonParams } = this.calcSignature(params)
1164
+
1031
1165
  return `${url}${prefix}signature=${signature}&params=${encodeURIComponent(jsonParams)}`
1032
1166
  }
1033
1167
 
@@ -1063,6 +1197,7 @@ export class Transloadit {
1063
1197
  private async _remoteJson<TRet, TParams extends OptionalAuthParams>(opts: {
1064
1198
  urlSuffix?: string
1065
1199
  url?: string
1200
+ isTrustedUrl?: boolean
1066
1201
  timeout?: Delays
1067
1202
  method?: 'delete' | 'get' | 'post' | 'put'
1068
1203
  params?: TParams
@@ -1073,6 +1208,7 @@ export class Transloadit {
1073
1208
  const {
1074
1209
  urlSuffix,
1075
1210
  url: urlInput,
1211
+ isTrustedUrl = false,
1076
1212
  timeout = { request: this._defaultTimeout },
1077
1213
  method = 'get',
1078
1214
  params = {},
@@ -1084,6 +1220,13 @@ export class Transloadit {
1084
1220
  // Allow providing either a `urlSuffix` or a full `url`
1085
1221
  if (!urlSuffix && !urlInput) throw new Error('No URL provided')
1086
1222
  let url = urlInput || `${this._endpoint}${urlSuffix}`
1223
+ if (urlInput && !isTrustedUrl) {
1224
+ const allowed = new URL(this._endpoint)
1225
+ const candidate = new URL(urlInput)
1226
+ if (allowed.origin !== candidate.origin) {
1227
+ throw new Error(`Untrusted URL: ${candidate.origin}`)
1228
+ }
1229
+ }
1087
1230
 
1088
1231
  if (method === 'get') {
1089
1232
  url = this._appendParamsToUrl(url, params)
@@ -1108,6 +1251,7 @@ export class Transloadit {
1108
1251
  headers: {
1109
1252
  'Transloadit-Client': `node-sdk:${version}`,
1110
1253
  'User-Agent': undefined, // Remove got's user-agent
1254
+ ...(this._authToken ? { Authorization: `Bearer ${this._authToken}` } : {}),
1111
1255
  ...headers,
1112
1256
  },
1113
1257
  responseType: 'json',