transloadit 4.7.3 → 4.7.6
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 +897 -5
- package/dist/Transloadit.d.ts +13 -3
- package/dist/Transloadit.d.ts.map +1 -1
- package/dist/Transloadit.js +22 -2
- package/dist/Transloadit.js.map +1 -1
- package/dist/alphalib/types/assembliesGet.d.ts +5 -0
- package/dist/alphalib/types/assembliesGet.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyReplay.d.ts +5 -0
- package/dist/alphalib/types/assemblyReplay.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyReplayNotification.d.ts +5 -0
- package/dist/alphalib/types/assemblyReplayNotification.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyStatus.d.ts +25 -25
- package/dist/alphalib/types/assemblyStatus.d.ts.map +1 -1
- package/dist/alphalib/types/assemblyStatus.js +4 -1
- package/dist/alphalib/types/assemblyStatus.js.map +1 -1
- package/dist/alphalib/types/bill.d.ts +5 -0
- package/dist/alphalib/types/bill.d.ts.map +1 -1
- package/dist/alphalib/types/builtinTemplates.d.ts +83 -0
- package/dist/alphalib/types/builtinTemplates.d.ts.map +1 -0
- package/dist/alphalib/types/builtinTemplates.js +19 -0
- package/dist/alphalib/types/builtinTemplates.js.map +1 -0
- package/dist/alphalib/types/robots/ai-chat.d.ts.map +1 -1
- package/dist/alphalib/types/robots/ai-chat.js +1 -0
- package/dist/alphalib/types/robots/ai-chat.js.map +1 -1
- package/dist/alphalib/types/skillFrontmatter.d.ts +29 -0
- package/dist/alphalib/types/skillFrontmatter.d.ts.map +1 -0
- package/dist/alphalib/types/skillFrontmatter.js +19 -0
- package/dist/alphalib/types/skillFrontmatter.js.map +1 -0
- package/dist/alphalib/types/template.d.ts +36 -0
- package/dist/alphalib/types/template.d.ts.map +1 -1
- package/dist/alphalib/types/template.js +10 -0
- package/dist/alphalib/types/template.js.map +1 -1
- package/dist/alphalib/types/templateCredential.d.ts +10 -0
- package/dist/alphalib/types/templateCredential.d.ts.map +1 -1
- package/dist/bearerToken.d.ts +31 -0
- package/dist/bearerToken.d.ts.map +1 -0
- package/dist/bearerToken.js +158 -0
- package/dist/bearerToken.js.map +1 -0
- package/dist/cli/commands/assemblies.d.ts +8 -2
- package/dist/cli/commands/assemblies.d.ts.map +1 -1
- package/dist/cli/commands/assemblies.js +566 -411
- package/dist/cli/commands/assemblies.js.map +1 -1
- package/dist/cli/commands/auth.d.ts +1 -4
- package/dist/cli/commands/auth.d.ts.map +1 -1
- package/dist/cli/commands/auth.js +7 -123
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +5 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/templates.d.ts.map +1 -1
- package/dist/cli/commands/templates.js +4 -14
- package/dist/cli/commands/templates.js.map +1 -1
- package/dist/cli/fileProcessingOptions.d.ts +35 -0
- package/dist/cli/fileProcessingOptions.d.ts.map +1 -0
- package/dist/cli/fileProcessingOptions.js +182 -0
- package/dist/cli/fileProcessingOptions.js.map +1 -0
- package/dist/cli/generateIntentDocs.d.ts +2 -0
- package/dist/cli/generateIntentDocs.d.ts.map +1 -0
- package/dist/cli/generateIntentDocs.js +321 -0
- package/dist/cli/generateIntentDocs.js.map +1 -0
- package/dist/cli/intentCommandSpecs.d.ts +36 -0
- package/dist/cli/intentCommandSpecs.d.ts.map +1 -0
- package/dist/cli/intentCommandSpecs.js +181 -0
- package/dist/cli/intentCommandSpecs.js.map +1 -0
- package/dist/cli/intentCommands.d.ts +13 -0
- package/dist/cli/intentCommands.d.ts.map +1 -0
- package/dist/cli/intentCommands.js +368 -0
- package/dist/cli/intentCommands.js.map +1 -0
- package/dist/cli/intentFields.d.ts +25 -0
- package/dist/cli/intentFields.d.ts.map +1 -0
- package/dist/cli/intentFields.js +298 -0
- package/dist/cli/intentFields.js.map +1 -0
- package/dist/cli/intentInputPolicy.d.ts +10 -0
- package/dist/cli/intentInputPolicy.d.ts.map +1 -0
- package/dist/cli/intentInputPolicy.js +2 -0
- package/dist/cli/intentInputPolicy.js.map +1 -0
- package/dist/cli/intentRuntime.d.ts +114 -0
- package/dist/cli/intentRuntime.d.ts.map +1 -0
- package/dist/cli/intentRuntime.js +464 -0
- package/dist/cli/intentRuntime.js.map +1 -0
- package/dist/cli/resultFiles.d.ts +19 -0
- package/dist/cli/resultFiles.d.ts.map +1 -0
- package/dist/cli/resultFiles.js +66 -0
- package/dist/cli/resultFiles.js.map +1 -0
- package/dist/cli/resultUrls.d.ts +19 -0
- package/dist/cli/resultUrls.d.ts.map +1 -0
- package/dist/cli/resultUrls.js +36 -0
- package/dist/cli/resultUrls.js.map +1 -0
- package/dist/cli/semanticIntents/imageDescribe.d.ts +43 -0
- package/dist/cli/semanticIntents/imageDescribe.d.ts.map +1 -0
- package/dist/cli/semanticIntents/imageDescribe.js +188 -0
- package/dist/cli/semanticIntents/imageDescribe.js.map +1 -0
- package/dist/cli/semanticIntents/index.d.ts +18 -0
- package/dist/cli/semanticIntents/index.d.ts.map +1 -0
- package/dist/cli/semanticIntents/index.js +18 -0
- package/dist/cli/semanticIntents/index.js.map +1 -0
- package/dist/cli/semanticIntents/markdownPdf.d.ts +4 -0
- package/dist/cli/semanticIntents/markdownPdf.d.ts.map +1 -0
- package/dist/cli/semanticIntents/markdownPdf.js +93 -0
- package/dist/cli/semanticIntents/markdownPdf.js.map +1 -0
- package/dist/cli/semanticIntents/parsing.d.ts +11 -0
- package/dist/cli/semanticIntents/parsing.d.ts.map +1 -0
- package/dist/cli/semanticIntents/parsing.js +29 -0
- package/dist/cli/semanticIntents/parsing.js.map +1 -0
- package/dist/cli/stepsInput.d.ts +4 -0
- package/dist/cli/stepsInput.d.ts.map +1 -0
- package/dist/cli/stepsInput.js +23 -0
- package/dist/cli/stepsInput.js.map +1 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -4
- package/dist/cli.js.map +1 -1
- package/dist/ensureUniqueCounter.d.ts +8 -0
- package/dist/ensureUniqueCounter.d.ts.map +1 -0
- package/dist/ensureUniqueCounter.js +48 -0
- package/dist/ensureUniqueCounter.js.map +1 -0
- package/dist/inputFiles.d.ts +9 -0
- package/dist/inputFiles.d.ts.map +1 -1
- package/dist/inputFiles.js +177 -26
- package/dist/inputFiles.js.map +1 -1
- package/dist/robots.js +1 -1
- package/dist/robots.js.map +1 -1
- package/package.json +9 -7
- package/src/Transloadit.ts +35 -3
- package/src/alphalib/types/assemblyStatus.ts +4 -1
- package/src/alphalib/types/builtinTemplates.ts +24 -0
- package/src/alphalib/types/robots/ai-chat.ts +1 -0
- package/src/alphalib/types/skillFrontmatter.ts +24 -0
- package/src/alphalib/types/template.ts +14 -0
- package/src/bearerToken.ts +208 -0
- package/src/cli/commands/assemblies.ts +825 -505
- package/src/cli/commands/auth.ts +9 -151
- package/src/cli/commands/index.ts +6 -3
- package/src/cli/commands/templates.ts +6 -17
- package/src/cli/fileProcessingOptions.ts +294 -0
- package/src/cli/generateIntentDocs.ts +419 -0
- package/src/cli/intentCommandSpecs.ts +282 -0
- package/src/cli/intentCommands.ts +525 -0
- package/src/cli/intentFields.ts +403 -0
- package/src/cli/intentInputPolicy.ts +11 -0
- package/src/cli/intentRuntime.ts +734 -0
- package/src/cli/resultFiles.ts +105 -0
- package/src/cli/resultUrls.ts +72 -0
- package/src/cli/semanticIntents/imageDescribe.ts +254 -0
- package/src/cli/semanticIntents/index.ts +48 -0
- package/src/cli/semanticIntents/markdownPdf.ts +120 -0
- package/src/cli/semanticIntents/parsing.ts +56 -0
- package/src/cli/stepsInput.ts +32 -0
- package/src/cli.ts +5 -4
- package/src/ensureUniqueCounter.ts +75 -0
- package/src/inputFiles.ts +277 -26
- package/src/robots.ts +1 -1
package/src/cli.ts
CHANGED
|
@@ -6,6 +6,7 @@ import process from 'node:process'
|
|
|
6
6
|
import { fileURLToPath } from 'node:url'
|
|
7
7
|
import 'dotenv/config'
|
|
8
8
|
import { createCli } from './cli/commands/index.ts'
|
|
9
|
+
import { ensureError } from './cli/types.ts'
|
|
9
10
|
|
|
10
11
|
const currentFile = realpathSync(fileURLToPath(import.meta.url))
|
|
11
12
|
|
|
@@ -32,13 +33,13 @@ export async function main(args = process.argv.slice(2)): Promise<void> {
|
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
export function runCliWhenExecuted(): void {
|
|
36
|
+
export async function runCliWhenExecuted(): Promise<void> {
|
|
36
37
|
if (!shouldRunCli(process.argv[1])) return
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
console.error((error
|
|
39
|
+
await main().catch((error) => {
|
|
40
|
+
console.error(ensureError(error).message)
|
|
40
41
|
process.exitCode = 1
|
|
41
42
|
})
|
|
42
43
|
}
|
|
43
44
|
|
|
44
|
-
runCliWhenExecuted()
|
|
45
|
+
await runCliWhenExecuted()
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const uniqueCounterScopes = new WeakMap<object, Promise<void>>()
|
|
2
|
+
|
|
3
|
+
async function runEnsureUniqueCounterValue<T>({
|
|
4
|
+
initialValue,
|
|
5
|
+
isTaken,
|
|
6
|
+
reserve,
|
|
7
|
+
nextValue,
|
|
8
|
+
}: {
|
|
9
|
+
initialValue: T
|
|
10
|
+
isTaken: (candidate: T) => Promise<boolean> | boolean
|
|
11
|
+
reserve: (candidate: T) => void
|
|
12
|
+
nextValue: (counter: number) => T
|
|
13
|
+
}): Promise<T> {
|
|
14
|
+
let candidate = initialValue
|
|
15
|
+
let counter = 1
|
|
16
|
+
|
|
17
|
+
while (await isTaken(candidate)) {
|
|
18
|
+
candidate = nextValue(counter)
|
|
19
|
+
counter += 1
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
reserve(candidate)
|
|
23
|
+
return candidate
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function ensureUniqueCounterValue<T>({
|
|
27
|
+
initialValue,
|
|
28
|
+
isTaken,
|
|
29
|
+
reserve,
|
|
30
|
+
nextValue,
|
|
31
|
+
scope,
|
|
32
|
+
}: {
|
|
33
|
+
initialValue: T
|
|
34
|
+
isTaken: (candidate: T) => Promise<boolean> | boolean
|
|
35
|
+
reserve: (candidate: T) => void
|
|
36
|
+
nextValue: (counter: number) => T
|
|
37
|
+
scope?: object
|
|
38
|
+
}): Promise<T> {
|
|
39
|
+
if (scope == null) {
|
|
40
|
+
return await runEnsureUniqueCounterValue({
|
|
41
|
+
initialValue,
|
|
42
|
+
isTaken,
|
|
43
|
+
reserve,
|
|
44
|
+
nextValue,
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const previous = uniqueCounterScopes.get(scope) ?? Promise.resolve()
|
|
49
|
+
let releaseScope: (() => void) | undefined
|
|
50
|
+
const pendingScope = new Promise<void>((resolve) => {
|
|
51
|
+
releaseScope = resolve
|
|
52
|
+
})
|
|
53
|
+
const currentScope = previous
|
|
54
|
+
.catch(() => undefined)
|
|
55
|
+
.then(async () => {
|
|
56
|
+
await pendingScope
|
|
57
|
+
})
|
|
58
|
+
uniqueCounterScopes.set(scope, currentScope)
|
|
59
|
+
|
|
60
|
+
await previous.catch(() => undefined)
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
return await runEnsureUniqueCounterValue({
|
|
64
|
+
initialValue,
|
|
65
|
+
isTaken,
|
|
66
|
+
reserve,
|
|
67
|
+
nextValue,
|
|
68
|
+
})
|
|
69
|
+
} finally {
|
|
70
|
+
releaseScope?.()
|
|
71
|
+
if (uniqueCounterScopes.get(scope) === currentScope) {
|
|
72
|
+
uniqueCounterScopes.delete(scope)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
package/src/inputFiles.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
|
+
import * as dnsPromises from 'node:dns/promises'
|
|
1
2
|
import { createWriteStream } from 'node:fs'
|
|
2
3
|
import { mkdtemp, rm, writeFile } from 'node:fs/promises'
|
|
3
4
|
import { isIP } from 'node:net'
|
|
4
5
|
import { tmpdir } from 'node:os'
|
|
5
|
-
import { basename, join } from 'node:path'
|
|
6
|
+
import { basename, join, parse } from 'node:path'
|
|
6
7
|
import type { Readable } from 'node:stream'
|
|
7
8
|
import { pipeline } from 'node:stream/promises'
|
|
9
|
+
import type CacheableLookup from 'cacheable-lookup'
|
|
10
|
+
import type { EntryObject, IPFamily } from 'cacheable-lookup'
|
|
8
11
|
import got from 'got'
|
|
9
12
|
import type { Input as IntoStreamInput } from 'into-stream'
|
|
10
13
|
import type { CreateAssemblyParams } from './apiTypes.ts'
|
|
14
|
+
import { ensureUniqueCounterValue } from './ensureUniqueCounter.ts'
|
|
11
15
|
|
|
12
16
|
export type InputFile =
|
|
13
17
|
| {
|
|
@@ -63,15 +67,28 @@ const ensureUnique = (field: string, used: Set<string>): void => {
|
|
|
63
67
|
used.add(field)
|
|
64
68
|
}
|
|
65
69
|
|
|
66
|
-
const ensureUniqueStepName = (baseName: string, used: Set<string>): string =>
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
counter
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
const ensureUniqueStepName = async (baseName: string, used: Set<string>): Promise<string> =>
|
|
71
|
+
await ensureUniqueCounterValue({
|
|
72
|
+
initialValue: baseName,
|
|
73
|
+
isTaken: (candidate) => used.has(candidate),
|
|
74
|
+
reserve: (candidate) => used.add(candidate),
|
|
75
|
+
nextValue: (counter) => `${baseName}_${counter}`,
|
|
76
|
+
scope: used,
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
const ensureUniqueTempFilePath = async (
|
|
80
|
+
root: string,
|
|
81
|
+
filename: string,
|
|
82
|
+
used: Set<string>,
|
|
83
|
+
): Promise<string> => {
|
|
84
|
+
const parsedFilename = parse(basename(filename))
|
|
85
|
+
return await ensureUniqueCounterValue({
|
|
86
|
+
initialValue: join(root, parsedFilename.base),
|
|
87
|
+
isTaken: (candidate) => used.has(candidate),
|
|
88
|
+
reserve: (candidate) => used.add(candidate),
|
|
89
|
+
nextValue: (counter) => join(root, `${parsedFilename.name}-${counter}${parsedFilename.ext}`),
|
|
90
|
+
scope: used,
|
|
91
|
+
})
|
|
75
92
|
}
|
|
76
93
|
|
|
77
94
|
const decodeBase64 = (value: string): Buffer => Buffer.from(value, 'base64')
|
|
@@ -106,27 +123,72 @@ const findImportStepName = (field: string, steps: Record<string, unknown>): stri
|
|
|
106
123
|
return null
|
|
107
124
|
}
|
|
108
125
|
|
|
109
|
-
const
|
|
110
|
-
|
|
126
|
+
const MAX_URL_REDIRECTS = 10
|
|
127
|
+
|
|
128
|
+
const isRedirectStatusCode = (statusCode: number): boolean =>
|
|
129
|
+
statusCode === 301 ||
|
|
130
|
+
statusCode === 302 ||
|
|
131
|
+
statusCode === 303 ||
|
|
132
|
+
statusCode === 307 ||
|
|
133
|
+
statusCode === 308
|
|
134
|
+
|
|
135
|
+
const ipv4FromMappedIpv6 = (address: string): string | null => {
|
|
136
|
+
const lowerAddress = address.toLowerCase()
|
|
137
|
+
const mappedPrefix = lowerAddress.startsWith('::ffff:')
|
|
138
|
+
? '::ffff:'
|
|
139
|
+
: lowerAddress.startsWith('0:0:0:0:0:ffff:')
|
|
140
|
+
? '0:0:0:0:0:ffff:'
|
|
141
|
+
: null
|
|
142
|
+
|
|
143
|
+
if (mappedPrefix == null) {
|
|
144
|
+
return null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const mappedValue = lowerAddress.slice(mappedPrefix.length)
|
|
148
|
+
if (mappedValue.includes('.')) {
|
|
149
|
+
return mappedValue
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const segments = mappedValue.split(':')
|
|
153
|
+
if (segments.length !== 2) {
|
|
154
|
+
return null
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const values = segments.map((segment) => Number.parseInt(segment, 16))
|
|
158
|
+
if (values.some((value) => Number.isNaN(value) || value < 0 || value > 0xffff)) {
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return values.flatMap((value) => [(value >> 8) & 0xff, value & 0xff]).join('.')
|
|
111
163
|
}
|
|
112
164
|
|
|
113
165
|
const isPrivateIp = (address: string): boolean => {
|
|
114
|
-
|
|
115
|
-
|
|
166
|
+
const normalizedAddress =
|
|
167
|
+
address.startsWith('[') && address.endsWith(']') ? address.slice(1, -1) : address
|
|
168
|
+
if (normalizedAddress === 'localhost') return true
|
|
169
|
+
const family = isIP(normalizedAddress)
|
|
116
170
|
if (family === 4) {
|
|
117
|
-
const parts =
|
|
171
|
+
const parts = normalizedAddress.split('.').map((chunk) => Number(chunk))
|
|
118
172
|
const [a, b] = parts
|
|
119
173
|
if (a === 10) return true
|
|
120
174
|
if (a === 127) return true
|
|
121
175
|
if (a === 0) return true
|
|
176
|
+
if (a === 100 && b >= 64 && b <= 127) return true
|
|
122
177
|
if (a === 169 && b === 254) return true
|
|
123
178
|
if (a === 172 && b >= 16 && b <= 31) return true
|
|
179
|
+
if (a === 192 && b === 0 && parts[2] === 0) return true
|
|
124
180
|
if (a === 192 && b === 168) return true
|
|
181
|
+
if (a === 198 && (b === 18 || b === 19)) return true
|
|
125
182
|
return false
|
|
126
183
|
}
|
|
127
184
|
if (family === 6) {
|
|
128
|
-
const normalized =
|
|
129
|
-
if (normalized === '::1') return true
|
|
185
|
+
const normalized = normalizedAddress.toLowerCase().split('%')[0]
|
|
186
|
+
if (normalized === '::1' || normalized === '0:0:0:0:0:0:0:1') return true
|
|
187
|
+
if (normalized === '::' || normalized === '0:0:0:0:0:0:0:0') return true
|
|
188
|
+
const mappedAddress = ipv4FromMappedIpv6(normalized)
|
|
189
|
+
if (mappedAddress != null && isPrivateIp(mappedAddress)) {
|
|
190
|
+
return true
|
|
191
|
+
}
|
|
130
192
|
if (normalized.startsWith('fe80:')) return true
|
|
131
193
|
if (normalized.startsWith('fc') || normalized.startsWith('fd')) return true
|
|
132
194
|
return false
|
|
@@ -134,14 +196,201 @@ const isPrivateIp = (address: string): boolean => {
|
|
|
134
196
|
return false
|
|
135
197
|
}
|
|
136
198
|
|
|
137
|
-
const
|
|
199
|
+
export const resolvePublicDownloadAddresses = async (
|
|
200
|
+
value: string,
|
|
201
|
+
): Promise<Array<{ address: string; family: 4 | 6 }>> => {
|
|
138
202
|
const parsed = new URL(value)
|
|
203
|
+
const hostname =
|
|
204
|
+
parsed.hostname.startsWith('[') && parsed.hostname.endsWith(']')
|
|
205
|
+
? parsed.hostname.slice(1, -1)
|
|
206
|
+
: parsed.hostname
|
|
139
207
|
if (!['http:', 'https:'].includes(parsed.protocol)) {
|
|
140
208
|
throw new Error(`URL downloads are limited to http/https: ${value}`)
|
|
141
209
|
}
|
|
142
|
-
if (isPrivateIp(
|
|
210
|
+
if (isPrivateIp(hostname)) {
|
|
143
211
|
throw new Error(`URL downloads are limited to public hosts: ${value}`)
|
|
144
212
|
}
|
|
213
|
+
|
|
214
|
+
const literalFamily = isIP(hostname)
|
|
215
|
+
const resolvedAddresses =
|
|
216
|
+
literalFamily !== 0
|
|
217
|
+
? [{ address: hostname, family: literalFamily as 4 | 6 }]
|
|
218
|
+
: await dnsPromises.lookup(hostname, {
|
|
219
|
+
all: true,
|
|
220
|
+
verbatim: true,
|
|
221
|
+
})
|
|
222
|
+
if (resolvedAddresses.some((address) => isPrivateIp(address.address))) {
|
|
223
|
+
throw new Error(`URL downloads are limited to public hosts: ${value}`)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (resolvedAddresses.length === 0) {
|
|
227
|
+
throw new Error(`Unable to resolve URL hostname: ${value}`)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return resolvedAddresses.map((address) => ({
|
|
231
|
+
address: address.address,
|
|
232
|
+
family: address.family as 4 | 6,
|
|
233
|
+
}))
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export function createPinnedDnsLookup(
|
|
237
|
+
validatedAddresses: Array<{ address: string; family: 4 | 6 }>,
|
|
238
|
+
): CacheableLookup['lookup'] {
|
|
239
|
+
const pinnedAddresses = [...validatedAddresses]
|
|
240
|
+
|
|
241
|
+
function pickAddress(family?: IPFamily): { address: string; family: 4 | 6 } | null {
|
|
242
|
+
if (family == null) {
|
|
243
|
+
return pinnedAddresses[0] ?? null
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return pinnedAddresses.find((address) => address.family === family) ?? null
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function pinnedDnsLookup(
|
|
250
|
+
_hostname: string,
|
|
251
|
+
family: IPFamily,
|
|
252
|
+
callback: (error: NodeJS.ErrnoException | null, address: string, family: IPFamily) => void,
|
|
253
|
+
): void
|
|
254
|
+
function pinnedDnsLookup(
|
|
255
|
+
_hostname: string,
|
|
256
|
+
callback: (error: NodeJS.ErrnoException | null, address: string, family: IPFamily) => void,
|
|
257
|
+
): void
|
|
258
|
+
function pinnedDnsLookup(
|
|
259
|
+
_hostname: string,
|
|
260
|
+
options: { all: true },
|
|
261
|
+
callback: (error: NodeJS.ErrnoException | null, result: ReadonlyArray<EntryObject>) => void,
|
|
262
|
+
): void
|
|
263
|
+
function pinnedDnsLookup(
|
|
264
|
+
_hostname: string,
|
|
265
|
+
options: object,
|
|
266
|
+
callback: (error: NodeJS.ErrnoException | null, address: string, family: IPFamily) => void,
|
|
267
|
+
): void
|
|
268
|
+
function pinnedDnsLookup(
|
|
269
|
+
_hostname: string,
|
|
270
|
+
familyOrCallback:
|
|
271
|
+
| IPFamily
|
|
272
|
+
| object
|
|
273
|
+
| ((error: NodeJS.ErrnoException | null, address: string, family: IPFamily) => void),
|
|
274
|
+
callback?:
|
|
275
|
+
| ((error: NodeJS.ErrnoException | null, address: string, family: IPFamily) => void)
|
|
276
|
+
| ((error: NodeJS.ErrnoException | null, result: ReadonlyArray<EntryObject>) => void),
|
|
277
|
+
): void {
|
|
278
|
+
if (typeof familyOrCallback === 'function') {
|
|
279
|
+
const address = pickAddress()
|
|
280
|
+
if (address == null) {
|
|
281
|
+
familyOrCallback(
|
|
282
|
+
new Error('No validated addresses available') as NodeJS.ErrnoException,
|
|
283
|
+
'',
|
|
284
|
+
4,
|
|
285
|
+
)
|
|
286
|
+
return
|
|
287
|
+
}
|
|
288
|
+
familyOrCallback(null, address.address, address.family)
|
|
289
|
+
return
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (
|
|
293
|
+
typeof familyOrCallback === 'object' &&
|
|
294
|
+
familyOrCallback != null &&
|
|
295
|
+
'all' in familyOrCallback
|
|
296
|
+
) {
|
|
297
|
+
;(
|
|
298
|
+
callback as (
|
|
299
|
+
error: NodeJS.ErrnoException | null,
|
|
300
|
+
result: ReadonlyArray<EntryObject>,
|
|
301
|
+
) => void
|
|
302
|
+
)(
|
|
303
|
+
null,
|
|
304
|
+
pinnedAddresses.map((address) => ({
|
|
305
|
+
address: address.address,
|
|
306
|
+
family: address.family,
|
|
307
|
+
expires: 0,
|
|
308
|
+
})),
|
|
309
|
+
)
|
|
310
|
+
return
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const family = typeof familyOrCallback === 'number' ? familyOrCallback : undefined
|
|
314
|
+
const address = pickAddress(family)
|
|
315
|
+
if (address == null) {
|
|
316
|
+
;(
|
|
317
|
+
callback as (error: NodeJS.ErrnoException | null, address: string, family: IPFamily) => void
|
|
318
|
+
)(new Error('No validated addresses available') as NodeJS.ErrnoException, '', family ?? 4)
|
|
319
|
+
return
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
;(callback as (error: NodeJS.ErrnoException | null, address: string, family: IPFamily) => void)(
|
|
323
|
+
null,
|
|
324
|
+
address.address,
|
|
325
|
+
address.family,
|
|
326
|
+
)
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return pinnedDnsLookup
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const downloadUrlToFile = async ({
|
|
333
|
+
allowPrivateUrls,
|
|
334
|
+
filePath,
|
|
335
|
+
url,
|
|
336
|
+
}: {
|
|
337
|
+
allowPrivateUrls: boolean
|
|
338
|
+
filePath: string
|
|
339
|
+
url: string
|
|
340
|
+
}): Promise<void> => {
|
|
341
|
+
let currentUrl = url
|
|
342
|
+
|
|
343
|
+
for (let redirectCount = 0; redirectCount <= MAX_URL_REDIRECTS; redirectCount += 1) {
|
|
344
|
+
let validatedAddresses: Array<{ address: string; family: 4 | 6 }> | null = null
|
|
345
|
+
if (!allowPrivateUrls) {
|
|
346
|
+
validatedAddresses = await resolvePublicDownloadAddresses(currentUrl)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const dnsLookup: CacheableLookup['lookup'] | undefined =
|
|
350
|
+
validatedAddresses == null ? undefined : createPinnedDnsLookup(validatedAddresses)
|
|
351
|
+
|
|
352
|
+
const responseStream = got.stream(currentUrl, {
|
|
353
|
+
dnsLookup,
|
|
354
|
+
followRedirect: false,
|
|
355
|
+
retry: { limit: 0 },
|
|
356
|
+
throwHttpErrors: false,
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
const response = await new Promise<
|
|
360
|
+
Readable & { headers: Record<string, string>; statusCode?: number }
|
|
361
|
+
>((resolvePromise, reject) => {
|
|
362
|
+
responseStream.once('response', (incomingResponse) => {
|
|
363
|
+
resolvePromise(
|
|
364
|
+
incomingResponse as Readable & {
|
|
365
|
+
headers: Record<string, string>
|
|
366
|
+
statusCode?: number
|
|
367
|
+
},
|
|
368
|
+
)
|
|
369
|
+
})
|
|
370
|
+
responseStream.once('error', reject)
|
|
371
|
+
})
|
|
372
|
+
|
|
373
|
+
const statusCode = response.statusCode ?? 0
|
|
374
|
+
if (isRedirectStatusCode(statusCode)) {
|
|
375
|
+
responseStream.destroy()
|
|
376
|
+
const location = response.headers.location
|
|
377
|
+
if (location == null) {
|
|
378
|
+
throw new Error(`Redirect response missing Location header: ${currentUrl}`)
|
|
379
|
+
}
|
|
380
|
+
currentUrl = new URL(location, currentUrl).toString()
|
|
381
|
+
continue
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (statusCode >= 400) {
|
|
385
|
+
responseStream.destroy()
|
|
386
|
+
throw new Error(`Failed to download URL: ${currentUrl} (${statusCode})`)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
await pipeline(responseStream, createWriteStream(filePath))
|
|
390
|
+
return
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
throw new Error(`Too many redirects while downloading URL input: ${url}`)
|
|
145
394
|
}
|
|
146
395
|
|
|
147
396
|
export const prepareInputFiles = async (
|
|
@@ -176,6 +425,7 @@ export const prepareInputFiles = async (
|
|
|
176
425
|
const steps = isRecord(nextParams.steps) ? { ...nextParams.steps } : {}
|
|
177
426
|
const usedSteps = new Set(Object.keys(steps))
|
|
178
427
|
const usedFields = new Set<string>()
|
|
428
|
+
const usedTempPaths = new Set<string>()
|
|
179
429
|
const importUrlsByStep = new Map<string, string[]>()
|
|
180
430
|
const importStepNames = Object.keys(steps).filter((name) => isHttpImportStep(steps[name]))
|
|
181
431
|
const sharedImportStep = importStepNames.length === 1 ? importStepNames[0] : null
|
|
@@ -211,7 +461,7 @@ export const prepareInputFiles = async (
|
|
|
211
461
|
if (base64Strategy === 'tempfile') {
|
|
212
462
|
const root = await ensureTempRoot()
|
|
213
463
|
const filename = file.filename ? basename(file.filename) : `${file.field}.bin`
|
|
214
|
-
const filePath =
|
|
464
|
+
const filePath = await ensureUniqueTempFilePath(root, filename, usedTempPaths)
|
|
215
465
|
await writeFile(filePath, buffer)
|
|
216
466
|
files[file.field] = filePath
|
|
217
467
|
} else {
|
|
@@ -226,7 +476,7 @@ export const prepareInputFiles = async (
|
|
|
226
476
|
urlStrategy === 'import' || (urlStrategy === 'import-if-present' && targetStep)
|
|
227
477
|
|
|
228
478
|
if (shouldImport) {
|
|
229
|
-
const stepName = targetStep ?? ensureUniqueStepName(file.field, usedSteps)
|
|
479
|
+
const stepName = targetStep ?? (await ensureUniqueStepName(file.field, usedSteps))
|
|
230
480
|
const urls = importUrlsByStep.get(stepName) ?? []
|
|
231
481
|
urls.push(file.url)
|
|
232
482
|
importUrlsByStep.set(stepName, urls)
|
|
@@ -238,11 +488,12 @@ export const prepareInputFiles = async (
|
|
|
238
488
|
(file.filename ? basename(file.filename) : null) ??
|
|
239
489
|
getFilenameFromUrl(file.url) ??
|
|
240
490
|
`${file.field}.bin`
|
|
241
|
-
const filePath =
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
491
|
+
const filePath = await ensureUniqueTempFilePath(root, filename, usedTempPaths)
|
|
492
|
+
await downloadUrlToFile({
|
|
493
|
+
allowPrivateUrls,
|
|
494
|
+
filePath,
|
|
495
|
+
url: file.url,
|
|
496
|
+
})
|
|
246
497
|
files[file.field] = filePath
|
|
247
498
|
}
|
|
248
499
|
}
|
package/src/robots.ts
CHANGED
|
@@ -274,7 +274,7 @@ export const listRobots = (options: RobotListOptions = {}): RobotListResult => {
|
|
|
274
274
|
|
|
275
275
|
const start = options.cursor ? Number.parseInt(options.cursor, 10) : 0
|
|
276
276
|
const safeStart = Number.isFinite(start) && start > 0 ? start : 0
|
|
277
|
-
const safeLimit = options.limit && options.limit > 0 ? options.limit :
|
|
277
|
+
const safeLimit = options.limit && options.limit > 0 ? options.limit : 200
|
|
278
278
|
const page = filtered.slice(safeStart, safeStart + safeLimit)
|
|
279
279
|
const nextCursor =
|
|
280
280
|
safeStart + safeLimit < filtered.length ? String(safeStart + safeLimit) : undefined
|