@sanity/export 3.26.0 → 3.26.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.
- package/package.json +2 -2
- package/src/AssetHandler.js +65 -23
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/export",
|
|
3
|
-
"version": "3.26.
|
|
3
|
+
"version": "3.26.1",
|
|
4
4
|
"description": "Export Sanity documents and assets",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -49,5 +49,5 @@
|
|
|
49
49
|
"publishConfig": {
|
|
50
50
|
"access": "public"
|
|
51
51
|
},
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "8a943ce177f298f8747b79afc48bacb99ea28e4b"
|
|
53
53
|
}
|
package/src/AssetHandler.js
CHANGED
|
@@ -15,6 +15,24 @@ const ACTION_REMOVE = 'remove'
|
|
|
15
15
|
const ACTION_REWRITE = 'rewrite'
|
|
16
16
|
const ASSET_DOWNLOAD_CONCURRENCY = 8
|
|
17
17
|
|
|
18
|
+
const retryHelper = (times, fn, onError) => {
|
|
19
|
+
let attempt = 0
|
|
20
|
+
const caller = (...args) => {
|
|
21
|
+
return fn(...args).catch((err) => {
|
|
22
|
+
if (onError) {
|
|
23
|
+
onError(err, attempt)
|
|
24
|
+
}
|
|
25
|
+
if (attempt < times) {
|
|
26
|
+
attempt++
|
|
27
|
+
return caller(...args)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
throw err
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
return caller
|
|
34
|
+
}
|
|
35
|
+
|
|
18
36
|
class AssetHandler {
|
|
19
37
|
constructor(options) {
|
|
20
38
|
const concurrency = options.concurrency || ASSET_DOWNLOAD_CONCURRENCY
|
|
@@ -104,7 +122,27 @@ class AssetHandler {
|
|
|
104
122
|
debug('Adding download task for %s (destination: %s)', assetDoc._id, dstPath)
|
|
105
123
|
this.queueSize++
|
|
106
124
|
this.downloading.push(assetDoc.url)
|
|
107
|
-
|
|
125
|
+
|
|
126
|
+
const doDownload = retryHelper(
|
|
127
|
+
10, // try 10 times
|
|
128
|
+
() => this.downloadAsset(assetDoc, dstPath),
|
|
129
|
+
(err, attempt) => {
|
|
130
|
+
debug(
|
|
131
|
+
`Error downloading asset %s (destination: %s), attempt %d`,
|
|
132
|
+
assetDoc._id,
|
|
133
|
+
dstPath,
|
|
134
|
+
attempt,
|
|
135
|
+
err,
|
|
136
|
+
)
|
|
137
|
+
},
|
|
138
|
+
)
|
|
139
|
+
this.queue.add(() =>
|
|
140
|
+
doDownload().catch((err) => {
|
|
141
|
+
debug('Error downloading asset', err)
|
|
142
|
+
this.queue.clear()
|
|
143
|
+
this.reject(err)
|
|
144
|
+
}),
|
|
145
|
+
)
|
|
108
146
|
}
|
|
109
147
|
|
|
110
148
|
maybeCreateAssetDirs() {
|
|
@@ -133,7 +171,8 @@ class AssetHandler {
|
|
|
133
171
|
return {url: formatUrl(url), headers}
|
|
134
172
|
}
|
|
135
173
|
|
|
136
|
-
|
|
174
|
+
// eslint-disable-next-line max-statements
|
|
175
|
+
async downloadAsset(assetDoc, dstPath) {
|
|
137
176
|
const {url} = assetDoc
|
|
138
177
|
const options = this.getAssetRequestOptions(assetDoc)
|
|
139
178
|
|
|
@@ -141,27 +180,39 @@ class AssetHandler {
|
|
|
141
180
|
try {
|
|
142
181
|
stream = await requestStream(options)
|
|
143
182
|
} catch (err) {
|
|
144
|
-
|
|
145
|
-
return false
|
|
183
|
+
throw new Error('Failed create asset stream', {cause: err})
|
|
146
184
|
}
|
|
147
185
|
|
|
148
186
|
if (stream.statusCode !== 200) {
|
|
149
187
|
this.queue.clear()
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
188
|
+
try {
|
|
189
|
+
const err = await tryGetErrorFromStream(stream)
|
|
190
|
+
let errMsg = `Referenced asset URL "${url}" returned HTTP ${stream.statusCode}`
|
|
191
|
+
if (err) {
|
|
192
|
+
errMsg = `${errMsg}:\n\n${err}`
|
|
193
|
+
}
|
|
155
194
|
|
|
156
|
-
|
|
157
|
-
|
|
195
|
+
throw new Error(errMsg)
|
|
196
|
+
} catch (err) {
|
|
197
|
+
throw new Error('Failed to parse error response from asset stream', {cause: err})
|
|
198
|
+
}
|
|
158
199
|
}
|
|
159
200
|
|
|
160
201
|
this.maybeCreateAssetDirs()
|
|
161
202
|
|
|
162
203
|
debug('Asset stream ready, writing to filesystem at %s', dstPath)
|
|
163
204
|
const tmpPath = path.join(this.tmpDir, dstPath)
|
|
164
|
-
|
|
205
|
+
let sha1 = ''
|
|
206
|
+
let md5 = ''
|
|
207
|
+
let size = 0
|
|
208
|
+
try {
|
|
209
|
+
const res = await writeHashedStream(tmpPath, stream)
|
|
210
|
+
sha1 = res.sha1
|
|
211
|
+
md5 = res.md5
|
|
212
|
+
size = res.size
|
|
213
|
+
} catch (err) {
|
|
214
|
+
throw new Error('Failed to write asset stream to filesystem', {cause: err})
|
|
215
|
+
}
|
|
165
216
|
|
|
166
217
|
// Verify it against our downloaded stream to make sure we have the same copy
|
|
167
218
|
const contentLength = stream.headers['content-length']
|
|
@@ -177,10 +228,7 @@ class AssetHandler {
|
|
|
177
228
|
const md5Differs = remoteMd5 && md5 !== remoteMd5
|
|
178
229
|
const differs = sha1Differs && md5Differs
|
|
179
230
|
|
|
180
|
-
if (differs
|
|
181
|
-
debug('%s does not match downloaded asset, retrying (#%d) [%s]', method, attemptNum + 1, url)
|
|
182
|
-
return this.downloadAsset(assetDoc, dstPath, attemptNum + 1)
|
|
183
|
-
} else if (differs) {
|
|
231
|
+
if (differs) {
|
|
184
232
|
const details = [
|
|
185
233
|
hasHash &&
|
|
186
234
|
(method === 'md5'
|
|
@@ -197,14 +245,8 @@ class AssetHandler {
|
|
|
197
245
|
const detailsString = `Details:\n - ${details.filter(Boolean).join('\n - ')}`
|
|
198
246
|
|
|
199
247
|
await rimraf(tmpPath)
|
|
200
|
-
this.queue.clear()
|
|
201
|
-
|
|
202
|
-
const error = new Error(
|
|
203
|
-
`Failed to download asset at ${assetDoc.url}, giving up. ${detailsString}`,
|
|
204
|
-
)
|
|
205
248
|
|
|
206
|
-
|
|
207
|
-
return false
|
|
249
|
+
throw new Error(`Failed to download asset at ${assetDoc.url}. ${detailsString}`)
|
|
208
250
|
}
|
|
209
251
|
|
|
210
252
|
const isImage = assetDoc._type === 'sanity.imageAsset'
|