@sanity/export 3.38.1 → 3.39.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.
- package/package.json +2 -2
- package/src/constants.js +13 -0
- package/src/export.js +27 -4
- package/src/getDocumentsStream.js +6 -1
- package/src/rejectOnApiError.js +17 -9
- package/src/requestStream.js +6 -20
- package/src/validateOptions.js +7 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/export",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.39.0",
|
|
4
4
|
"description": "Export Sanity documents and assets",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@sanity/util": "3.37.2",
|
|
35
35
|
"archiver": "^7.0.0",
|
|
36
36
|
"debug": "^4.3.4",
|
|
37
|
-
"get-it": "^8.
|
|
37
|
+
"get-it": "^8.6.2",
|
|
38
38
|
"lodash": "^4.17.21",
|
|
39
39
|
"mississippi": "^4.0.0",
|
|
40
40
|
"p-queue": "^2.3.0",
|
package/src/constants.js
CHANGED
|
@@ -20,3 +20,16 @@ exports.ASSET_DOWNLOAD_MAX_RETRIES = 10
|
|
|
20
20
|
* @internal
|
|
21
21
|
*/
|
|
22
22
|
exports.ASSET_DOWNLOAD_CONCURRENCY = 8
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* How frequently we will `debug` log while streaming the documents.
|
|
26
|
+
* @internal
|
|
27
|
+
*/
|
|
28
|
+
exports.DOCUMENT_STREAM_DEBUG_INTERVAL = 10000
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* How long to wait before timing out the read of a request due to inactivity.
|
|
32
|
+
* User overridable as `options.readTimeout`.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
exports.REQUEST_READ_TIMEOUT = 3 * 60 * 1000 // 3 minutes
|
package/src/export.js
CHANGED
|
@@ -17,6 +17,7 @@ const stringifyStream = require('./stringifyStream')
|
|
|
17
17
|
const tryParseJson = require('./tryParseJson')
|
|
18
18
|
const rimraf = require('./util/rimraf')
|
|
19
19
|
const validateOptions = require('./validateOptions')
|
|
20
|
+
const {DOCUMENT_STREAM_DEBUG_INTERVAL} = require('./constants')
|
|
20
21
|
|
|
21
22
|
const noop = () => null
|
|
22
23
|
|
|
@@ -93,11 +94,14 @@ async function exportDataset(opts) {
|
|
|
93
94
|
onProgress({step: 'Exporting documents...'})
|
|
94
95
|
|
|
95
96
|
let documentCount = 0
|
|
97
|
+
let lastDocumentID = null
|
|
96
98
|
let lastReported = Date.now()
|
|
97
|
-
const reportDocumentCount = (
|
|
99
|
+
const reportDocumentCount = (doc, enc, cb) => {
|
|
98
100
|
++documentCount
|
|
99
101
|
|
|
100
102
|
const now = Date.now()
|
|
103
|
+
// We report to the `onProgress` handler every 50 ms.
|
|
104
|
+
// It's up to the caller to not do too much expensive work.
|
|
101
105
|
if (now - lastReported > 50) {
|
|
102
106
|
onProgress({
|
|
103
107
|
step: 'Exporting documents...',
|
|
@@ -109,13 +113,30 @@ async function exportDataset(opts) {
|
|
|
109
113
|
lastReported = now
|
|
110
114
|
}
|
|
111
115
|
|
|
112
|
-
|
|
116
|
+
lastDocumentID = doc._id
|
|
117
|
+
|
|
118
|
+
cb(null, doc)
|
|
113
119
|
}
|
|
114
120
|
|
|
115
121
|
const inputStream = await getDocumentsStream(options)
|
|
116
122
|
debug('Got HTTP %d', inputStream.statusCode)
|
|
117
123
|
debug('Response headers: %o', inputStream.headers)
|
|
118
124
|
|
|
125
|
+
let debugTimer = null
|
|
126
|
+
function scheduleDebugTimer() {
|
|
127
|
+
debugTimer = setTimeout(() => {
|
|
128
|
+
debug('Still streaming documents', {
|
|
129
|
+
documentCount,
|
|
130
|
+
lastDocumentID,
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
// Schedule another tick:
|
|
134
|
+
scheduleDebugTimer()
|
|
135
|
+
}, DOCUMENT_STREAM_DEBUG_INTERVAL)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
scheduleDebugTimer()
|
|
139
|
+
|
|
119
140
|
const jsonStream = miss.pipeline(
|
|
120
141
|
inputStream,
|
|
121
142
|
logFirstChunk(),
|
|
@@ -125,13 +146,15 @@ async function exportDataset(opts) {
|
|
|
125
146
|
assetStreamHandler,
|
|
126
147
|
filterDocumentTypes(options.types),
|
|
127
148
|
options.drafts ? miss.through.obj() : filterDrafts(),
|
|
149
|
+
miss.through.obj(reportDocumentCount),
|
|
128
150
|
stringifyStream(),
|
|
129
|
-
miss.through(reportDocumentCount),
|
|
130
151
|
)
|
|
131
152
|
|
|
132
153
|
miss.pipe(jsonStream, fs.createWriteStream(dataPath), async (err) => {
|
|
154
|
+
if (debugTimer !== null) clearTimeout(debugTimer)
|
|
155
|
+
|
|
133
156
|
if (err) {
|
|
134
|
-
debug(
|
|
157
|
+
debug(`Export stream error @ ${lastDocumentID}/${documentCount}: `, err)
|
|
135
158
|
reject(err)
|
|
136
159
|
return
|
|
137
160
|
}
|
|
@@ -11,5 +11,10 @@ module.exports = (options) => {
|
|
|
11
11
|
...(token ? {Authorization: `Bearer ${token}`} : {}),
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
return requestStream({
|
|
14
|
+
return requestStream({
|
|
15
|
+
url,
|
|
16
|
+
headers,
|
|
17
|
+
maxRetries: options.maxRetries,
|
|
18
|
+
readTimeout: options.readTimeout,
|
|
19
|
+
})
|
|
15
20
|
}
|
package/src/rejectOnApiError.js
CHANGED
|
@@ -2,18 +2,26 @@ const miss = require('mississippi')
|
|
|
2
2
|
|
|
3
3
|
module.exports = () =>
|
|
4
4
|
miss.through.obj((doc, enc, callback) => {
|
|
5
|
-
if
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
['Export', `HTTP ${doc.statusCode}`, doc.error, doc.message]
|
|
9
|
-
.filter((part) => typeof part === 'string')
|
|
10
|
-
.join(': '),
|
|
11
|
-
),
|
|
12
|
-
)
|
|
5
|
+
// check if the document passed contains a document attribtue first, and return early.
|
|
6
|
+
if (doc._id) {
|
|
7
|
+
callback(null, doc)
|
|
13
8
|
return
|
|
14
9
|
}
|
|
15
10
|
|
|
16
|
-
if (
|
|
11
|
+
if (doc.error) {
|
|
12
|
+
// if we got a statusCode we can decorate the error with it
|
|
13
|
+
if (doc.statusCode) {
|
|
14
|
+
callback(
|
|
15
|
+
new Error(
|
|
16
|
+
['Export', `HTTP ${doc.statusCode}`, doc.error, doc.message]
|
|
17
|
+
.filter((part) => typeof part === 'string')
|
|
18
|
+
.join(': '),
|
|
19
|
+
),
|
|
20
|
+
)
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// no statusCode, just serialize and return the error
|
|
17
25
|
callback(new Error(doc.error.description || doc.error.message || JSON.stringify(doc)))
|
|
18
26
|
return
|
|
19
27
|
}
|
package/src/requestStream.js
CHANGED
|
@@ -2,13 +2,11 @@ const {getIt} = require('get-it')
|
|
|
2
2
|
const {keepAlive, promise} = require('get-it/middleware')
|
|
3
3
|
const debug = require('./debug')
|
|
4
4
|
const {extractFirstError} = require('./util/extractFirstError')
|
|
5
|
-
const {DOCUMENT_STREAM_MAX_RETRIES} = require('./constants')
|
|
5
|
+
const {DOCUMENT_STREAM_MAX_RETRIES, REQUEST_READ_TIMEOUT} = require('./constants')
|
|
6
6
|
|
|
7
7
|
const request = getIt([keepAlive(), promise({onlyBody: true})])
|
|
8
|
-
const socketsWithTimeout = new WeakSet()
|
|
9
8
|
|
|
10
9
|
const CONNECTION_TIMEOUT = 15 * 1000 // 15 seconds
|
|
11
|
-
const READ_TIMEOUT = 3 * 60 * 1000 // 3 minutes
|
|
12
10
|
const RETRY_DELAY_MS = 1500 // 1.5 seconds
|
|
13
11
|
|
|
14
12
|
function delay(ms) {
|
|
@@ -20,30 +18,18 @@ module.exports = async (options) => {
|
|
|
20
18
|
const maxRetries =
|
|
21
19
|
typeof options.maxRetries === 'number' ? options.maxRetries : DOCUMENT_STREAM_MAX_RETRIES
|
|
22
20
|
|
|
21
|
+
const readTimeout =
|
|
22
|
+
typeof options.readTimeout === 'number' ? options.readTimeout : REQUEST_READ_TIMEOUT
|
|
23
|
+
|
|
23
24
|
let error
|
|
24
25
|
for (let i = 0; i < maxRetries; i++) {
|
|
25
26
|
try {
|
|
26
|
-
|
|
27
|
+
return await request({
|
|
27
28
|
...options,
|
|
28
29
|
stream: true,
|
|
29
30
|
maxRedirects: 0,
|
|
30
|
-
timeout: {connect: CONNECTION_TIMEOUT, socket:
|
|
31
|
+
timeout: {connect: CONNECTION_TIMEOUT, socket: readTimeout},
|
|
31
32
|
})
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
response.connection &&
|
|
35
|
-
typeof response.connection.setTimeout === 'function' &&
|
|
36
|
-
!socketsWithTimeout.has(response.connection)
|
|
37
|
-
) {
|
|
38
|
-
socketsWithTimeout.add(response.connection)
|
|
39
|
-
response.connection.setTimeout(READ_TIMEOUT, () => {
|
|
40
|
-
response.destroy(
|
|
41
|
-
new Error(`Export: Read timeout: No data received on socket for ${READ_TIMEOUT} ms`),
|
|
42
|
-
)
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return response
|
|
47
33
|
} catch (err) {
|
|
48
34
|
error = extractFirstError(err)
|
|
49
35
|
|
package/src/validateOptions.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
const defaults = require('lodash/defaults')
|
|
2
|
-
const {
|
|
2
|
+
const {
|
|
3
|
+
DOCUMENT_STREAM_MAX_RETRIES,
|
|
4
|
+
ASSET_DOWNLOAD_MAX_RETRIES,
|
|
5
|
+
REQUEST_READ_TIMEOUT,
|
|
6
|
+
} = require('./constants')
|
|
3
7
|
|
|
4
8
|
const clientMethods = ['getUrl', 'config']
|
|
5
9
|
const booleanFlags = ['assets', 'raw', 'compress', 'drafts']
|
|
6
|
-
const numberFlags = ['maxAssetRetries', 'maxRetries', 'assetConcurrency']
|
|
10
|
+
const numberFlags = ['maxAssetRetries', 'maxRetries', 'assetConcurrency', 'readTimeout']
|
|
7
11
|
const exportDefaults = {
|
|
8
12
|
compress: true,
|
|
9
13
|
drafts: true,
|
|
@@ -11,6 +15,7 @@ const exportDefaults = {
|
|
|
11
15
|
raw: false,
|
|
12
16
|
maxRetries: DOCUMENT_STREAM_MAX_RETRIES,
|
|
13
17
|
maxAssetRetries: ASSET_DOWNLOAD_MAX_RETRIES,
|
|
18
|
+
readTimeout: REQUEST_READ_TIMEOUT,
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
function validateOptions(opts) {
|