@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
package/src/robots.ts ADDED
@@ -0,0 +1,317 @@
1
+ import type { z } from 'zod'
2
+ import { robotsMeta, robotsSchema } from './alphalib/types/robots/_index.ts'
3
+
4
+ export type RobotListOptions = {
5
+ category?: string
6
+ search?: string
7
+ limit?: number
8
+ cursor?: string
9
+ }
10
+
11
+ export type RobotListItem = {
12
+ name: string
13
+ title?: string
14
+ summary: string
15
+ category?: string
16
+ }
17
+
18
+ export type RobotListResult = {
19
+ robots: RobotListItem[]
20
+ nextCursor?: string
21
+ }
22
+
23
+ export type RobotParamHelp = {
24
+ name: string
25
+ type: string
26
+ description?: string
27
+ }
28
+
29
+ export type RobotHelp = {
30
+ name: string
31
+ summary: string
32
+ requiredParams: RobotParamHelp[]
33
+ optionalParams: RobotParamHelp[]
34
+ examples?: Array<{ description: string; snippet: Record<string, unknown> }>
35
+ }
36
+
37
+ export type RobotHelpOptions = {
38
+ robotName: string
39
+ detailLevel?: 'summary' | 'params' | 'examples'
40
+ }
41
+
42
+ type RobotsMetaMap = typeof robotsMeta
43
+ type RobotMeta = RobotsMetaMap[keyof RobotsMetaMap]
44
+
45
+ const isRecord = (value: unknown): value is Record<string, unknown> =>
46
+ typeof value === 'object' && value !== null
47
+
48
+ const getDef = (schema: z.ZodTypeAny): Record<string, unknown> =>
49
+ (schema as unknown as { _def?: Record<string, unknown>; def?: Record<string, unknown> })._def ??
50
+ (schema as unknown as { def?: Record<string, unknown> }).def ??
51
+ {}
52
+
53
+ const getDefType = (def: Record<string, unknown>): string | undefined =>
54
+ (def.type as string | undefined) ?? (def.typeName as string | undefined)
55
+
56
+ const robotNameToPath = (name: string): string => {
57
+ const base = name.replace(/Robot$/, '')
58
+ const spaced = base
59
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
60
+ .replace(/([A-Z]+)([A-Z][a-z0-9])/g, '$1 $2')
61
+ const parts = spaced.split(/\s+/).filter(Boolean)
62
+ return `/${parts.map((part) => part.toLowerCase()).join('/')}`
63
+ }
64
+
65
+ const selectSummary = (meta: RobotMeta): string =>
66
+ meta.purpose_sentence ?? meta.purpose_words ?? meta.purpose_word ?? meta.title ?? meta.name
67
+
68
+ const resolveRobotPath = (robotName: string): string =>
69
+ robotName.startsWith('/') ? robotName : robotNameToPath(robotName)
70
+
71
+ const unwrapSchema = (schema: z.ZodTypeAny): { base: z.ZodTypeAny; optional: boolean } => {
72
+ let base = schema
73
+ let optional = typeof base.isOptional === 'function' ? base.isOptional() : false
74
+
75
+ while (true) {
76
+ const def = getDef(base)
77
+ const defType = getDefType(def)
78
+ if (
79
+ defType === 'optional' ||
80
+ defType === 'default' ||
81
+ defType === 'nullable' ||
82
+ defType === 'catch' ||
83
+ defType === 'ZodOptional' ||
84
+ defType === 'ZodDefault' ||
85
+ defType === 'ZodNullable' ||
86
+ defType === 'ZodCatch'
87
+ ) {
88
+ const inner = def.innerType as z.ZodTypeAny | undefined
89
+ if (inner) {
90
+ base = inner
91
+ if (defType !== 'nullable' && defType !== 'ZodNullable') {
92
+ optional = true
93
+ }
94
+ continue
95
+ }
96
+ }
97
+ break
98
+ }
99
+
100
+ return { base, optional }
101
+ }
102
+
103
+ const describeSchemaType = (schema: z.ZodTypeAny): string => {
104
+ const { base } = unwrapSchema(schema)
105
+ const def = getDef(base)
106
+ const defType = getDefType(def)
107
+
108
+ switch (defType) {
109
+ case 'string':
110
+ case 'ZodString':
111
+ return 'string'
112
+ case 'number':
113
+ case 'ZodNumber':
114
+ return 'number'
115
+ case 'boolean':
116
+ case 'ZodBoolean':
117
+ return 'boolean'
118
+ case 'bigint':
119
+ case 'ZodBigInt':
120
+ return 'bigint'
121
+ case 'literal':
122
+ case 'ZodLiteral': {
123
+ const value = (def.values as unknown[] | undefined)?.[0] ?? def.value
124
+ return value === undefined ? 'literal' : JSON.stringify(value)
125
+ }
126
+ case 'enum':
127
+ case 'ZodEnum': {
128
+ const values = Array.isArray(def.values) ? def.values : []
129
+ return values.length ? `enum(${values.join(' | ')})` : 'enum'
130
+ }
131
+ case 'array':
132
+ case 'ZodArray': {
133
+ const element = def.element as z.ZodTypeAny | undefined
134
+ const inner = element ? describeSchemaType(element) : 'unknown'
135
+ return `array<${inner}>`
136
+ }
137
+ case 'object':
138
+ case 'ZodObject':
139
+ return 'object'
140
+ case 'record':
141
+ case 'ZodRecord':
142
+ return 'record'
143
+ case 'union':
144
+ case 'ZodUnion': {
145
+ const options = Array.isArray(def.options) ? def.options : []
146
+ const rendered = options
147
+ .map((option) => describeSchemaType(option as z.ZodTypeAny))
148
+ .join(' | ')
149
+ return rendered ? `union<${rendered}>` : 'union'
150
+ }
151
+ case 'ZodDiscriminatedUnion':
152
+ return 'object'
153
+ default:
154
+ return defType ?? 'unknown'
155
+ }
156
+ }
157
+
158
+ const getParamDescription = (schema: z.ZodTypeAny): string | undefined => {
159
+ if (schema.description?.trim()) {
160
+ return schema.description.trim()
161
+ }
162
+ const inner = unwrapSchema(schema).base
163
+ return inner.description?.trim()
164
+ }
165
+
166
+ const getShape = (schema: z.ZodTypeAny): Record<string, z.ZodTypeAny> => {
167
+ const { base } = unwrapSchema(schema)
168
+ const def = getDef(base)
169
+ const shape = def.shape as
170
+ | Record<string, z.ZodTypeAny>
171
+ | (() => Record<string, z.ZodTypeAny>)
172
+ | undefined
173
+ if (typeof shape === 'function') {
174
+ return shape()
175
+ }
176
+ return shape ?? {}
177
+ }
178
+
179
+ const getRobotParams = (
180
+ schema: z.ZodTypeAny,
181
+ ): { required: RobotParamHelp[]; optional: RobotParamHelp[] } => {
182
+ const shape = getShape(schema)
183
+ const required: RobotParamHelp[] = []
184
+ const optional: RobotParamHelp[] = []
185
+
186
+ for (const [key, value] of Object.entries(shape)) {
187
+ if (key === 'robot') continue
188
+ const { optional: isOptional } = unwrapSchema(value)
189
+ const param: RobotParamHelp = {
190
+ name: key,
191
+ type: describeSchemaType(value),
192
+ description: getParamDescription(value),
193
+ }
194
+
195
+ if (isOptional) {
196
+ optional.push(param)
197
+ } else {
198
+ required.push(param)
199
+ }
200
+ }
201
+
202
+ return { required, optional }
203
+ }
204
+
205
+ const getRobotsMetaIndex = (): {
206
+ byName: Map<string, RobotMeta>
207
+ byPath: Map<string, RobotMeta>
208
+ } => {
209
+ const byName = new Map<string, RobotMeta>()
210
+ const byPath = new Map<string, RobotMeta>()
211
+
212
+ for (const meta of Object.values(robotsMeta)) {
213
+ byName.set(meta.name, meta)
214
+ byPath.set(robotNameToPath(meta.name), meta)
215
+ }
216
+
217
+ return { byName, byPath }
218
+ }
219
+
220
+ const getRobotSchemaIndex = (): Map<string, z.ZodTypeAny> => {
221
+ const index = new Map<string, z.ZodTypeAny>()
222
+ for (const option of robotsSchema.options) {
223
+ const shape = getShape(option)
224
+ const robotSchema = shape.robot
225
+ if (!robotSchema) continue
226
+ const robotDef = getDef(robotSchema)
227
+ const robotLiteral = (robotDef.values as unknown[] | undefined)?.[0] ?? robotDef.value
228
+ if (typeof robotLiteral === 'string') {
229
+ index.set(robotLiteral, option)
230
+ }
231
+ }
232
+ return index
233
+ }
234
+
235
+ let cachedMetaIndex: ReturnType<typeof getRobotsMetaIndex> | null = null
236
+ let cachedSchemaIndex: ReturnType<typeof getRobotSchemaIndex> | null = null
237
+
238
+ const getMetaIndex = (): ReturnType<typeof getRobotsMetaIndex> => {
239
+ if (!cachedMetaIndex) {
240
+ cachedMetaIndex = getRobotsMetaIndex()
241
+ }
242
+ return cachedMetaIndex
243
+ }
244
+
245
+ const getSchemaIndex = (): ReturnType<typeof getRobotSchemaIndex> => {
246
+ if (!cachedSchemaIndex) {
247
+ cachedSchemaIndex = getRobotSchemaIndex()
248
+ }
249
+ return cachedSchemaIndex
250
+ }
251
+
252
+ export const listRobots = (options: RobotListOptions = {}): RobotListResult => {
253
+ const normalizedSearch = options.search?.toLowerCase()
254
+ const normalizedCategory = options.category?.toLowerCase()
255
+ const { byPath } = getMetaIndex()
256
+
257
+ const allRobots: RobotListItem[] = Array.from(byPath.entries()).map(([path, meta]) => ({
258
+ name: path,
259
+ title: meta.title,
260
+ summary: selectSummary(meta),
261
+ category: meta.service_slug,
262
+ }))
263
+
264
+ const filtered = allRobots
265
+ .filter((robot) => {
266
+ if (normalizedCategory && robot.category?.toLowerCase() !== normalizedCategory) {
267
+ return false
268
+ }
269
+ if (!normalizedSearch) return true
270
+ const haystack = `${robot.name} ${robot.title ?? ''} ${robot.summary}`.toLowerCase()
271
+ return haystack.includes(normalizedSearch)
272
+ })
273
+ .sort((a, b) => a.name.localeCompare(b.name))
274
+
275
+ const start = options.cursor ? Number.parseInt(options.cursor, 10) : 0
276
+ const safeStart = Number.isFinite(start) && start > 0 ? start : 0
277
+ const safeLimit = options.limit && options.limit > 0 ? options.limit : 20
278
+ const page = filtered.slice(safeStart, safeStart + safeLimit)
279
+ const nextCursor =
280
+ safeStart + safeLimit < filtered.length ? String(safeStart + safeLimit) : undefined
281
+
282
+ return {
283
+ robots: page,
284
+ nextCursor,
285
+ }
286
+ }
287
+
288
+ export const getRobotHelp = (options: RobotHelpOptions): RobotHelp => {
289
+ const detailLevel = options.detailLevel ?? 'summary'
290
+ const { byPath, byName } = getMetaIndex()
291
+ const schemaIndex = getSchemaIndex()
292
+
293
+ const path = resolveRobotPath(options.robotName)
294
+ const meta = byPath.get(path) ?? byName.get(options.robotName) ?? null
295
+ const summary = meta ? selectSummary(meta) : `Robot ${path}`
296
+ const schema = schemaIndex.get(path)
297
+ const params = schema ? getRobotParams(schema) : { required: [], optional: [] }
298
+
299
+ const help: RobotHelp = {
300
+ name: path,
301
+ summary,
302
+ requiredParams: detailLevel === 'params' ? params.required : [],
303
+ optionalParams: detailLevel === 'params' ? params.optional : [],
304
+ }
305
+
306
+ if (detailLevel === 'examples' && meta?.example_code) {
307
+ const snippet = isRecord(meta.example_code) ? meta.example_code : {}
308
+ help.examples = [
309
+ {
310
+ description: meta.example_code_description ?? 'Example',
311
+ snippet,
312
+ },
313
+ ]
314
+ }
315
+
316
+ return help
317
+ }
package/src/tus.ts CHANGED
@@ -9,6 +9,9 @@ import type { AssemblyStatus } from './alphalib/types/assemblyStatus.ts'
9
9
  import type { UploadProgress } from './Transloadit.ts'
10
10
 
11
11
  const log = debug('transloadit')
12
+ const logWarn = debug('transloadit:warn')
13
+
14
+ export type UploadBehavior = 'await' | 'background' | 'none'
12
15
 
13
16
  export interface Stream {
14
17
  path?: string
@@ -23,6 +26,7 @@ interface SendTusRequestOptions {
23
26
  onProgress: (options: UploadProgress) => void
24
27
  signal?: AbortSignal
25
28
  uploadUrls?: Record<string, string>
29
+ uploadBehavior?: UploadBehavior
26
30
  }
27
31
 
28
32
  export async function sendTusRequest({
@@ -33,6 +37,7 @@ export async function sendTusRequest({
33
37
  onProgress,
34
38
  signal,
35
39
  uploadUrls,
40
+ uploadBehavior = 'await',
36
41
  }: SendTusRequestOptions) {
37
42
  const streamLabels = Object.keys(streamsMap)
38
43
 
@@ -40,6 +45,7 @@ export async function sendTusRequest({
40
45
  let lastEmittedProgress = 0
41
46
 
42
47
  const sizes: Record<string, number> = {}
48
+ const uploadUrlsResult: Record<string, string> = { ...(uploadUrls ?? {}) }
43
49
 
44
50
  const haveUnknownLengthStreams = streamLabels.some((label) => !streamsMap[label]?.path)
45
51
 
@@ -67,6 +73,9 @@ export async function sendTusRequest({
67
73
 
68
74
  const uploadProgresses: Record<string, number> = {}
69
75
 
76
+ const completionPromises: Array<Promise<void>> = []
77
+ const uploadUrlPromises: Array<Promise<void>> = []
78
+
70
79
  async function uploadSingleStream(label: string) {
71
80
  uploadProgresses[label] = 0
72
81
 
@@ -110,7 +119,45 @@ export async function sendTusRequest({
110
119
 
111
120
  const filename = path ? basename(path) : label
112
121
 
113
- await new Promise<OnSuccessPayload>((resolvePromise, rejectPromise) => {
122
+ if (uploadBehavior === 'none' && uploadUrls?.[label]) {
123
+ uploadUrlsResult[label] = uploadUrls[label]
124
+ uploadUrlPromises.push(Promise.resolve())
125
+ completionPromises.push(Promise.resolve())
126
+ return
127
+ }
128
+
129
+ let urlResolved = false
130
+ let resolveUrl: () => void = () => {}
131
+ let rejectUrl: (err: Error) => void = () => {}
132
+ const uploadUrlPromise = new Promise<void>((resolve, reject) => {
133
+ resolveUrl = () => {
134
+ if (urlResolved) return
135
+ urlResolved = true
136
+ resolve()
137
+ }
138
+ rejectUrl = (err) => {
139
+ if (urlResolved) return
140
+ urlResolved = true
141
+ reject(err)
142
+ }
143
+ })
144
+
145
+ let resolveCompletion: () => void = () => {}
146
+ let rejectCompletion: (err: Error) => void = () => {}
147
+ const completionPromise = new Promise<void>((resolve, reject) => {
148
+ resolveCompletion = resolve
149
+ rejectCompletion = reject
150
+ })
151
+
152
+ uploadUrlPromises.push(uploadUrlPromise)
153
+ completionPromises.push(completionPromise)
154
+
155
+ if (uploadUrls?.[label]) {
156
+ uploadUrlsResult[label] = uploadUrls[label]
157
+ resolveUrl()
158
+ }
159
+
160
+ const startPromise = new Promise<void>((resolvePromise, rejectPromise) => {
114
161
  if (!assembly.assembly_ssl_url) {
115
162
  rejectPromise(new Error('assembly_ssl_url is not present in the assembly status'))
116
163
  return
@@ -124,15 +171,20 @@ export async function sendTusRequest({
124
171
 
125
172
  // Wrap resolve/reject to clean up abort listener
126
173
  let abortHandler: (() => void) | undefined
127
- const resolve = (payload: OnSuccessPayload) => {
174
+ const resolve = (_payload: OnSuccessPayload) => {
128
175
  if (abortHandler) signal?.removeEventListener('abort', abortHandler)
129
- resolvePromise(payload)
176
+ resolveCompletion()
177
+ resolveUrl()
178
+ resolvePromise()
130
179
  }
131
180
  const reject = (err: unknown) => {
132
181
  if (abortHandler) signal?.removeEventListener('abort', abortHandler)
182
+ rejectCompletion(err as Error)
183
+ rejectUrl(err as Error)
133
184
  rejectPromise(err)
134
185
  }
135
186
 
187
+ let tusUpload: Upload
136
188
  const tusOptions: UploadOptions = {
137
189
  endpoint: assembly.tus_url,
138
190
  uploadUrl: uploadUrls?.[label],
@@ -144,13 +196,24 @@ export async function sendTusRequest({
144
196
  onError: reject,
145
197
  onProgress: onTusProgress,
146
198
  onSuccess: resolve,
199
+ onUploadUrlAvailable: () => {
200
+ const url = tusUpload?.url
201
+ if (url) {
202
+ uploadUrlsResult[label] = url
203
+ }
204
+ resolveUrl()
205
+ if (uploadBehavior === 'none') {
206
+ tusUpload.abort()
207
+ resolveCompletion()
208
+ }
209
+ },
147
210
  }
148
211
  // tus-js-client doesn't like undefined/null
149
212
  if (size != null) tusOptions.uploadSize = size
150
213
  if (chunkSize) tusOptions.chunkSize = chunkSize
151
214
  if (uploadLengthDeferred) tusOptions.uploadLengthDeferred = uploadLengthDeferred
152
215
 
153
- const tusUpload = new Upload(stream, tusOptions)
216
+ tusUpload = new Upload(stream, tusOptions)
154
217
 
155
218
  // Handle abort signal
156
219
  if (signal) {
@@ -164,8 +227,31 @@ export async function sendTusRequest({
164
227
  tusUpload.start()
165
228
  })
166
229
 
167
- log(label, 'upload done')
230
+ if (uploadBehavior === 'await') {
231
+ await startPromise
232
+ log(label, 'upload done')
233
+ return
234
+ }
235
+
236
+ startPromise.catch((err) => {
237
+ logWarn('Background upload failed', err)
238
+ })
239
+
240
+ await uploadUrlPromise
241
+ log(label, 'upload started')
168
242
  }
169
243
 
170
244
  await pMap(streamLabels, uploadSingleStream, { concurrency: uploadConcurrency, signal })
245
+
246
+ await Promise.all(uploadUrlPromises)
247
+
248
+ if (uploadBehavior === 'await') {
249
+ await Promise.all(completionPromises)
250
+ } else {
251
+ Promise.allSettled(completionPromises).catch((err) => {
252
+ logWarn('Background upload failed', err)
253
+ })
254
+ }
255
+
256
+ return { uploadUrls: uploadUrlsResult }
171
257
  }