@sanity/export 3.40.0 → 3.41.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/README.md CHANGED
@@ -42,6 +42,11 @@ exportDataset({
42
42
 
43
43
  // Run 12 concurrent asset downloads
44
44
  assetConcurrency: 12,
45
+
46
+ // What mode to use when exporting documents, can be eiter `stream`(default) or `cursor`.
47
+ // Cursor mode might help when dealing with large datasets, but might yield inconsistent results if the dataset is mutated during export.
48
+ // Default: 'stream'
49
+ mode: 'stream',
45
50
  })
46
51
  ```
47
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/export",
3
- "version": "3.40.0",
3
+ "version": "3.41.1",
4
4
  "description": "Export Sanity documents and assets",
5
5
  "keywords": [
6
6
  "sanity",
@@ -38,7 +38,7 @@
38
38
  "lodash": "^4.17.21",
39
39
  "mississippi": "^4.0.0",
40
40
  "p-queue": "^2.3.0",
41
- "rimraf": "^3.0.2",
41
+ "rimraf": "^6.0.1",
42
42
  "split2": "^4.2.0",
43
43
  "tar": "^7.0.1",
44
44
  "yaml": "^2.4.2"
@@ -2,7 +2,7 @@ const crypto = require('crypto')
2
2
  const {mkdirSync, createWriteStream} = require('fs')
3
3
  const path = require('path')
4
4
  const {parse: parseUrl, format: formatUrl} = require('url')
5
- const {omit, noop} = require('lodash')
5
+ const {omit} = require('lodash')
6
6
  const miss = require('mississippi')
7
7
  const PQueue = require('p-queue')
8
8
  const pkg = require('../package.json')
package/src/export.js CHANGED
@@ -91,7 +91,7 @@ async function exportDataset(opts) {
91
91
  debug('Archive finished')
92
92
  })
93
93
 
94
- debug('Getting dataset export stream')
94
+ debug('Getting dataset export stream, mode: "%s"', options.mode)
95
95
  onProgress({step: 'Exporting documents...'})
96
96
 
97
97
  let documentCount = 0
@@ -120,8 +120,12 @@ async function exportDataset(opts) {
120
120
  }
121
121
 
122
122
  const inputStream = await getDocumentInputStream(options)
123
- debug('Got HTTP %d', inputStream.statusCode)
124
- debug('Response headers: %o', inputStream.headers)
123
+ if (inputStream.statusCode) {
124
+ debug('Got HTTP %d', inputStream.statusCode)
125
+ }
126
+ if (inputStream.headers) {
127
+ debug('Response headers: %o', inputStream.headers)
128
+ }
125
129
 
126
130
  let debugTimer = null
127
131
  function scheduleDebugTimer() {
@@ -2,6 +2,7 @@ const miss = require('mississippi')
2
2
  const debug = require('./debug')
3
3
 
4
4
  const isSystemDocument = (doc) => doc && doc._id && doc._id.indexOf('_.') === 0
5
+ const isCursor = (doc) => doc && !doc._id && doc.nextCursor !== undefined
5
6
 
6
7
  module.exports = () =>
7
8
  miss.through.obj((doc, enc, callback) => {
@@ -9,6 +10,10 @@ module.exports = () =>
9
10
  debug('%s is a system document, skipping', doc && doc._id)
10
11
  return callback()
11
12
  }
13
+ if (isCursor(doc)) {
14
+ debug('%o is a cursor, skipping', doc)
15
+ return callback()
16
+ }
12
17
 
13
18
  return callback(null, doc)
14
19
  })
@@ -1,60 +1,66 @@
1
1
  const {Transform} = require('node:stream')
2
2
 
3
3
  const pkg = require('../package.json')
4
+ const debug = require('./debug')
4
5
  const requestStream = require('./requestStream')
5
6
 
7
+ // same regex as split2 is using by default: https://github.com/mcollina/split2/blob/53432f54bd5bf422bd55d91d38f898b6c9496fc1/index.js#L86
8
+ const splitRegex = /\r?\n/
9
+
6
10
  module.exports = async (options) => {
7
11
  let streamsInflight = 0
12
+ function decrementInflight(stream) {
13
+ streamsInflight--
14
+ if (streamsInflight === 0) {
15
+ stream.end()
16
+ }
17
+ }
18
+
8
19
  const stream = new Transform({
9
20
  async transform(chunk, encoding, callback) {
10
21
  if (encoding !== 'buffer' && encoding !== 'string') {
11
22
  callback(null, chunk)
12
23
  return
13
24
  }
25
+ this.push(chunk, encoding)
14
26
 
15
27
  let parsedChunk = null
16
- try {
17
- parsedChunk = JSON.parse(chunk.toString())
18
- } catch (err) {
19
- // Ignore JSON parse errors
20
- // this can happen if the chunk is not a JSON object. We just pass it through and let the caller handle it.
21
- }
28
+ for (const chunkStr of chunk.toString().split(splitRegex)) {
29
+ if (chunkStr.trim() === '') {
30
+ continue
31
+ }
22
32
 
23
- if (
24
- parsedChunk !== null &&
25
- typeof parsedChunk === 'object' &&
26
- 'nextCursor' in parsedChunk &&
27
- typeof parsedChunk.nextCursor === 'string' &&
28
- !('_id' in parsedChunk)
29
- ) {
30
- streamsInflight++
33
+ try {
34
+ parsedChunk = JSON.parse(chunkStr)
35
+ } catch (err) {
36
+ // Ignore JSON parse errors
37
+ // this can happen if the chunk is not a JSON object. We just pass it through and let the caller handle it.
38
+ debug('Failed to parse JSON chunk, ignoring', err, chunkStr)
39
+ }
31
40
 
32
- const reqStream = await startStream(options, parsedChunk.nextCursor)
33
- reqStream.on('end', () => {
34
- streamsInflight--
35
- if (streamsInflight === 0) {
36
- stream.end()
37
- }
38
- })
39
- reqStream.pipe(this, {end: false})
41
+ if (
42
+ parsedChunk !== null &&
43
+ typeof parsedChunk === 'object' &&
44
+ 'nextCursor' in parsedChunk &&
45
+ typeof parsedChunk.nextCursor === 'string' &&
46
+ !('_id' in parsedChunk)
47
+ ) {
48
+ debug('Got next cursor "%s", fetching next stream', parsedChunk.nextCursor)
49
+ streamsInflight++
40
50
 
41
- callback()
42
- return
51
+ const reqStream = await startStream(options, parsedChunk.nextCursor)
52
+ reqStream.on('end', () => decrementInflight(this))
53
+ reqStream.pipe(this, {end: false})
54
+ }
43
55
  }
44
56
 
45
- callback(null, chunk)
57
+ callback()
46
58
  },
47
59
  })
48
60
 
49
61
  streamsInflight++
50
62
  const reqStream = await startStream(options, '')
51
- reqStream.on('end', () => {
52
- streamsInflight--
53
- if (streamsInflight === 0) {
54
- stream.end()
55
- }
56
- })
57
-
63
+ reqStream.on('end', () => decrementInflight(stream))
58
64
  reqStream.pipe(stream, {end: false})
59
65
  return stream
60
66
  }
@@ -69,5 +75,11 @@ function startStream(options, nextCursor) {
69
75
  ...(token ? {Authorization: `Bearer ${token}`} : {}),
70
76
  }
71
77
 
72
- return requestStream({url, headers, maxRetries: options.maxRetries})
78
+ debug('Starting stream with cursor "%s"', nextCursor)
79
+
80
+ return requestStream({url, headers, maxRetries: options.maxRetries}).then((res) => {
81
+ debug('Got stream with HTTP %d', res.statusCode)
82
+
83
+ return res
84
+ })
73
85
  }
@@ -1,4 +1 @@
1
- const {promisify} = require('util')
2
- const rimrafCb = require('rimraf')
3
-
4
- module.exports = promisify(rimrafCb)
1
+ module.exports = require('rimraf').rimraf