@sanity/cli 8.0.0 → 8.0.2

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 (76) hide show
  1. package/dist/actions/backup/downloadAsset.js +3 -24
  2. package/dist/actions/backup/downloadAsset.js.map +1 -1
  3. package/dist/actions/backup/downloadDocument.js +11 -22
  4. package/dist/actions/backup/downloadDocument.js.map +1 -1
  5. package/dist/actions/backup/downloadStream.js +83 -0
  6. package/dist/actions/backup/downloadStream.js.map +1 -0
  7. package/dist/actions/documents/validate.js +0 -3
  8. package/dist/actions/documents/validate.js.map +1 -1
  9. package/dist/actions/documents/validateDocuments.worker.js +4 -1
  10. package/dist/actions/documents/validateDocuments.worker.js.map +1 -1
  11. package/dist/actions/init/sdkAppDependencies.js +1 -1
  12. package/dist/actions/init/sdkAppDependencies.js.map +1 -1
  13. package/dist/actions/init/studioDependencies.js +1 -1
  14. package/dist/actions/init/studioDependencies.js.map +1 -1
  15. package/dist/actions/schema/uploadSchemaToLexicon.js +3 -0
  16. package/dist/actions/schema/uploadSchemaToLexicon.js.map +1 -1
  17. package/dist/actions/telemetry/telemetryDebug.js +2 -2
  18. package/dist/actions/telemetry/telemetryDebug.js.map +1 -1
  19. package/dist/commands/assets/upload.js +35 -1
  20. package/dist/commands/assets/upload.js.map +1 -1
  21. package/dist/commands/datasets/import.js +7 -10
  22. package/dist/commands/datasets/import.js.map +1 -1
  23. package/dist/exports/_internal.d.ts +50 -82
  24. package/dist/exports/index.d.ts +48 -78
  25. package/dist/exports/invokeSanityCli/index.d.ts +108 -18
  26. package/dist/exports/invokeSanityCli/index.js +2 -1
  27. package/dist/exports/invokeSanityCli/index.js.map +1 -1
  28. package/dist/exports/runtime.d.ts +3 -65
  29. package/dist/services/api.js +23 -15
  30. package/dist/services/api.js.map +1 -1
  31. package/dist/services/auth.js +4 -4
  32. package/dist/services/auth.js.map +1 -1
  33. package/dist/services/backup.js +3 -3
  34. package/dist/services/backup.js.map +1 -1
  35. package/dist/services/cors.js +3 -3
  36. package/dist/services/cors.js.map +1 -1
  37. package/dist/services/datasetAliases.js +5 -5
  38. package/dist/services/datasetAliases.js.map +1 -1
  39. package/dist/services/datasets.js +3 -3
  40. package/dist/services/datasets.js.map +1 -1
  41. package/dist/services/documents.js +1 -1
  42. package/dist/services/documents.js.map +1 -1
  43. package/dist/services/getProjectFeatures.js +1 -1
  44. package/dist/services/getProjectFeatures.js.map +1 -1
  45. package/dist/services/getUrlHeaders.js +3 -9
  46. package/dist/services/getUrlHeaders.js.map +1 -1
  47. package/dist/services/grants.js +1 -1
  48. package/dist/services/grants.js.map +1 -1
  49. package/dist/services/graphql.js +4 -3
  50. package/dist/services/graphql.js.map +1 -1
  51. package/dist/services/hooks.js +6 -6
  52. package/dist/services/hooks.js.map +1 -1
  53. package/dist/services/mcp.js +4 -4
  54. package/dist/services/mcp.js.map +1 -1
  55. package/dist/services/mediaLibraries.js +3 -3
  56. package/dist/services/mediaLibraries.js.map +1 -1
  57. package/dist/services/mintProject.js +6 -11
  58. package/dist/services/mintProject.js.map +1 -1
  59. package/dist/services/organizations.js +6 -6
  60. package/dist/services/organizations.js.map +1 -1
  61. package/dist/services/plans.js +2 -2
  62. package/dist/services/plans.js.map +1 -1
  63. package/dist/services/projects.js +7 -7
  64. package/dist/services/projects.js.map +1 -1
  65. package/dist/services/schemas.js +2 -2
  66. package/dist/services/schemas.js.map +1 -1
  67. package/dist/services/telemetry.js +2 -2
  68. package/dist/services/telemetry.js.map +1 -1
  69. package/dist/services/tokens.js +5 -5
  70. package/dist/services/tokens.js.map +1 -1
  71. package/dist/services/user.js +1 -1
  72. package/dist/services/user.js.map +1 -1
  73. package/dist/services/userApplications.js +18 -18
  74. package/dist/services/userApplications.js.map +1 -1
  75. package/oclif.manifest.json +1 -1
  76. package/package.json +12 -12
@@ -1,19 +1,7 @@
1
1
  import { createWriteStream } from 'node:fs';
2
2
  import path from 'node:path';
3
- import { pipeline } from 'node:stream/promises';
4
- import { createRequester, keepAlive, retry } from '@sanity/cli-core/request';
5
3
  import { backupDownloadDebug } from './backupDownloadDebug.js';
6
- const CONNECTION_TIMEOUT = 15 * 1000 // 15 seconds
7
- ;
8
- const READ_TIMEOUT = 3 * 60 * 1000 // 3 minutes
9
- ;
10
- const request = createRequester({
11
- middleware: {
12
- promise: {
13
- onlyBody: false
14
- }
15
- }
16
- }).use(keepAlive()).use(retry());
4
+ import { downloadStream } from './downloadStream.js';
17
5
  /**
18
6
  * Downloads an asset (image or file) from a backup to the specified output directory
19
7
  *
@@ -27,17 +15,8 @@ const request = createRequester({
27
15
  // the base name as file name.
28
16
  const normalizedFileName = path.basename(fileName);
29
17
  const assetFilePath = getAssetFilePath(normalizedFileName, fileType, outDir);
30
- const response = await request({
31
- maxRedirects: 5,
32
- stream: true,
33
- timeout: {
34
- connect: CONNECTION_TIMEOUT,
35
- socket: READ_TIMEOUT
36
- },
37
- url
38
- });
39
- backupDownloadDebug('Received asset %s with status code %d', normalizedFileName, response.statusCode);
40
- await pipeline(response.body, createWriteStream(assetFilePath));
18
+ const status = await downloadStream(url, ()=>createWriteStream(assetFilePath));
19
+ backupDownloadDebug('Received asset %s with status code %d', normalizedFileName, status);
41
20
  }
42
21
  function getAssetFilePath(fileName, fileType, outDir) {
43
22
  // Set assetFilePath if we are downloading an asset file.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/backup/downloadAsset.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport path from 'node:path'\nimport {pipeline} from 'node:stream/promises'\n\nimport {createRequester, keepAlive, retry} from '@sanity/cli-core/request'\n\nimport {backupDownloadDebug} from './backupDownloadDebug.js'\n\nconst CONNECTION_TIMEOUT = 15 * 1000 // 15 seconds\nconst READ_TIMEOUT = 3 * 60 * 1000 // 3 minutes\n\nconst request = createRequester({middleware: {promise: {onlyBody: false}}})\n .use(keepAlive())\n .use(retry())\n\n/**\n * Downloads an asset (image or file) from a backup to the specified output directory\n *\n * @param url - The URL to download the asset from\n * @param fileName - The original file name of the asset\n * @param fileType - The type of asset ('image' or 'file')\n * @param outDir - The output directory to save the asset to\n */\nexport async function downloadAsset(\n url: string,\n fileName: string,\n fileType: string,\n outDir: string,\n): Promise<void> {\n // File names that contain a path to file (e.g. sanity-storage/assets/file-name.tar.gz) fail when archive is\n // created due to missing parent dir (e.g. sanity-storage/assets), so we want to handle them by taking\n // the base name as file name.\n const normalizedFileName = path.basename(fileName)\n\n const assetFilePath = getAssetFilePath(normalizedFileName, fileType, outDir)\n\n const response = await request({\n maxRedirects: 5,\n stream: true,\n timeout: {connect: CONNECTION_TIMEOUT, socket: READ_TIMEOUT},\n url,\n })\n\n backupDownloadDebug(\n 'Received asset %s with status code %d',\n normalizedFileName,\n response.statusCode,\n )\n\n await pipeline(response.body, createWriteStream(assetFilePath))\n}\n\nfunction getAssetFilePath(fileName: string, fileType: string, outDir: string): string {\n // Set assetFilePath if we are downloading an asset file.\n // If it's a JSON document, assetFilePath will be an empty string.\n let assetFilePath = ''\n if (fileType === 'image') {\n assetFilePath = path.join(outDir, 'images', fileName)\n } else if (fileType === 'file') {\n assetFilePath = path.join(outDir, 'files', fileName)\n }\n\n return assetFilePath\n}\n"],"names":["createWriteStream","path","pipeline","createRequester","keepAlive","retry","backupDownloadDebug","CONNECTION_TIMEOUT","READ_TIMEOUT","request","middleware","promise","onlyBody","use","downloadAsset","url","fileName","fileType","outDir","normalizedFileName","basename","assetFilePath","getAssetFilePath","response","maxRedirects","stream","timeout","connect","socket","statusCode","body","join"],"mappings":"AAAA,SAAQA,iBAAiB,QAAO,UAAS;AACzC,OAAOC,UAAU,YAAW;AAC5B,SAAQC,QAAQ,QAAO,uBAAsB;AAE7C,SAAQC,eAAe,EAAEC,SAAS,EAAEC,KAAK,QAAO,2BAA0B;AAE1E,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D,MAAMC,qBAAqB,KAAK,KAAK,aAAa;;AAClD,MAAMC,eAAe,IAAI,KAAK,KAAK,YAAY;;AAE/C,MAAMC,UAAUN,gBAAgB;IAACO,YAAY;QAACC,SAAS;YAACC,UAAU;QAAK;IAAC;AAAC,GACtEC,GAAG,CAACT,aACJS,GAAG,CAACR;AAEP;;;;;;;CAOC,GACD,OAAO,eAAeS,cACpBC,GAAW,EACXC,QAAgB,EAChBC,QAAgB,EAChBC,MAAc;IAEd,4GAA4G;IAC5G,sGAAsG;IACtG,8BAA8B;IAC9B,MAAMC,qBAAqBlB,KAAKmB,QAAQ,CAACJ;IAEzC,MAAMK,gBAAgBC,iBAAiBH,oBAAoBF,UAAUC;IAErE,MAAMK,WAAW,MAAMd,QAAQ;QAC7Be,cAAc;QACdC,QAAQ;QACRC,SAAS;YAACC,SAASpB;YAAoBqB,QAAQpB;QAAY;QAC3DO;IACF;IAEAT,oBACE,yCACAa,oBACAI,SAASM,UAAU;IAGrB,MAAM3B,SAASqB,SAASO,IAAI,EAAE9B,kBAAkBqB;AAClD;AAEA,SAASC,iBAAiBN,QAAgB,EAAEC,QAAgB,EAAEC,MAAc;IAC1E,yDAAyD;IACzD,kEAAkE;IAClE,IAAIG,gBAAgB;IACpB,IAAIJ,aAAa,SAAS;QACxBI,gBAAgBpB,KAAK8B,IAAI,CAACb,QAAQ,UAAUF;IAC9C,OAAO,IAAIC,aAAa,QAAQ;QAC9BI,gBAAgBpB,KAAK8B,IAAI,CAACb,QAAQ,SAASF;IAC7C;IAEA,OAAOK;AACT"}
1
+ {"version":3,"sources":["../../../src/actions/backup/downloadAsset.ts"],"sourcesContent":["import {createWriteStream} from 'node:fs'\nimport path from 'node:path'\n\nimport {backupDownloadDebug} from './backupDownloadDebug.js'\nimport {downloadStream} from './downloadStream.js'\n\n/**\n * Downloads an asset (image or file) from a backup to the specified output directory\n *\n * @param url - The URL to download the asset from\n * @param fileName - The original file name of the asset\n * @param fileType - The type of asset ('image' or 'file')\n * @param outDir - The output directory to save the asset to\n */\nexport async function downloadAsset(\n url: string,\n fileName: string,\n fileType: string,\n outDir: string,\n): Promise<void> {\n // File names that contain a path to file (e.g. sanity-storage/assets/file-name.tar.gz) fail when archive is\n // created due to missing parent dir (e.g. sanity-storage/assets), so we want to handle them by taking\n // the base name as file name.\n const normalizedFileName = path.basename(fileName)\n\n const assetFilePath = getAssetFilePath(normalizedFileName, fileType, outDir)\n\n const status = await downloadStream(url, () => createWriteStream(assetFilePath))\n\n backupDownloadDebug('Received asset %s with status code %d', normalizedFileName, status)\n}\n\nfunction getAssetFilePath(fileName: string, fileType: string, outDir: string): string {\n // Set assetFilePath if we are downloading an asset file.\n // If it's a JSON document, assetFilePath will be an empty string.\n let assetFilePath = ''\n if (fileType === 'image') {\n assetFilePath = path.join(outDir, 'images', fileName)\n } else if (fileType === 'file') {\n assetFilePath = path.join(outDir, 'files', fileName)\n }\n\n return assetFilePath\n}\n"],"names":["createWriteStream","path","backupDownloadDebug","downloadStream","downloadAsset","url","fileName","fileType","outDir","normalizedFileName","basename","assetFilePath","getAssetFilePath","status","join"],"mappings":"AAAA,SAAQA,iBAAiB,QAAO,UAAS;AACzC,OAAOC,UAAU,YAAW;AAE5B,SAAQC,mBAAmB,QAAO,2BAA0B;AAC5D,SAAQC,cAAc,QAAO,sBAAqB;AAElD;;;;;;;CAOC,GACD,OAAO,eAAeC,cACpBC,GAAW,EACXC,QAAgB,EAChBC,QAAgB,EAChBC,MAAc;IAEd,4GAA4G;IAC5G,sGAAsG;IACtG,8BAA8B;IAC9B,MAAMC,qBAAqBR,KAAKS,QAAQ,CAACJ;IAEzC,MAAMK,gBAAgBC,iBAAiBH,oBAAoBF,UAAUC;IAErE,MAAMK,SAAS,MAAMV,eAAeE,KAAK,IAAML,kBAAkBW;IAEjET,oBAAoB,yCAAyCO,oBAAoBI;AACnF;AAEA,SAASD,iBAAiBN,QAAgB,EAAEC,QAAgB,EAAEC,MAAc;IAC1E,yDAAyD;IACzD,kEAAkE;IAClE,IAAIG,gBAAgB;IACpB,IAAIJ,aAAa,SAAS;QACxBI,gBAAgBV,KAAKa,IAAI,CAACN,QAAQ,UAAUF;IAC9C,OAAO,IAAIC,aAAa,QAAQ;QAC9BI,gBAAgBV,KAAKa,IAAI,CAACN,QAAQ,SAASF;IAC7C;IAEA,OAAOK;AACT"}
@@ -1,32 +1,21 @@
1
- import { createRequester, keepAlive, retry } from '@sanity/cli-core/request';
1
+ import { Writable } from 'node:stream';
2
2
  import { backupDownloadDebug } from './backupDownloadDebug.js';
3
- const CONNECTION_TIMEOUT = 15 * 1000 // 15 seconds
4
- ;
5
- const READ_TIMEOUT = 3 * 60 * 1000 // 3 minutes
6
- ;
7
- const request = createRequester({
8
- middleware: {
9
- promise: {
10
- onlyBody: false
11
- }
12
- }
13
- }).use(keepAlive()).use(retry());
3
+ import { downloadStream } from './downloadStream.js';
14
4
  /**
15
5
  * Downloads a document from a backup URL
16
6
  *
17
7
  * @param url - The URL to download the document from
18
8
  * @returns The document content as received from the API
19
9
  */ export async function downloadDocument(url) {
20
- const response = await request({
21
- maxRedirects: 5,
22
- timeout: {
23
- connect: CONNECTION_TIMEOUT,
24
- socket: READ_TIMEOUT
25
- },
26
- url
27
- });
28
- backupDownloadDebug('Received document from %s with status code %d', url, response?.statusCode);
29
- return response.body;
10
+ const chunks = [];
11
+ const status = await downloadStream(url, ()=>new Writable({
12
+ write (chunk, _encoding, callback) {
13
+ chunks.push(Buffer.from(chunk));
14
+ callback();
15
+ }
16
+ }));
17
+ backupDownloadDebug('Received document from %s with status code %d', url, status);
18
+ return Buffer.concat(chunks).toString();
30
19
  }
31
20
 
32
21
  //# sourceMappingURL=downloadDocument.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/backup/downloadDocument.ts"],"sourcesContent":["import {createRequester, keepAlive, retry} from '@sanity/cli-core/request'\n\nimport {backupDownloadDebug} from './backupDownloadDebug.js'\n\nconst CONNECTION_TIMEOUT = 15 * 1000 // 15 seconds\nconst READ_TIMEOUT = 3 * 60 * 1000 // 3 minutes\n\nconst request = createRequester({middleware: {promise: {onlyBody: false}}})\n .use(keepAlive())\n .use(retry())\n\n/**\n * Downloads a document from a backup URL\n *\n * @param url - The URL to download the document from\n * @returns The document content as received from the API\n */\nexport async function downloadDocument(url: string): Promise<unknown> {\n const response = await request({\n maxRedirects: 5,\n timeout: {connect: CONNECTION_TIMEOUT, socket: READ_TIMEOUT},\n url,\n })\n\n backupDownloadDebug('Received document from %s with status code %d', url, response?.statusCode)\n\n return response.body\n}\n"],"names":["createRequester","keepAlive","retry","backupDownloadDebug","CONNECTION_TIMEOUT","READ_TIMEOUT","request","middleware","promise","onlyBody","use","downloadDocument","url","response","maxRedirects","timeout","connect","socket","statusCode","body"],"mappings":"AAAA,SAAQA,eAAe,EAAEC,SAAS,EAAEC,KAAK,QAAO,2BAA0B;AAE1E,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D,MAAMC,qBAAqB,KAAK,KAAK,aAAa;;AAClD,MAAMC,eAAe,IAAI,KAAK,KAAK,YAAY;;AAE/C,MAAMC,UAAUN,gBAAgB;IAACO,YAAY;QAACC,SAAS;YAACC,UAAU;QAAK;IAAC;AAAC,GACtEC,GAAG,CAACT,aACJS,GAAG,CAACR;AAEP;;;;;CAKC,GACD,OAAO,eAAeS,iBAAiBC,GAAW;IAChD,MAAMC,WAAW,MAAMP,QAAQ;QAC7BQ,cAAc;QACdC,SAAS;YAACC,SAASZ;YAAoBa,QAAQZ;QAAY;QAC3DO;IACF;IAEAT,oBAAoB,iDAAiDS,KAAKC,UAAUK;IAEpF,OAAOL,SAASM,IAAI;AACtB"}
1
+ {"version":3,"sources":["../../../src/actions/backup/downloadDocument.ts"],"sourcesContent":["import {Writable} from 'node:stream'\n\nimport {backupDownloadDebug} from './backupDownloadDebug.js'\nimport {downloadStream} from './downloadStream.js'\n\n/**\n * Downloads a document from a backup URL\n *\n * @param url - The URL to download the document from\n * @returns The document content as received from the API\n */\nexport async function downloadDocument(url: string): Promise<string> {\n const chunks: Buffer[] = []\n const status = await downloadStream(\n url,\n () =>\n new Writable({\n write(chunk: Buffer, _encoding, callback) {\n chunks.push(Buffer.from(chunk))\n callback()\n },\n }),\n )\n\n backupDownloadDebug('Received document from %s with status code %d', url, status)\n\n return Buffer.concat(chunks).toString()\n}\n"],"names":["Writable","backupDownloadDebug","downloadStream","downloadDocument","url","chunks","status","write","chunk","_encoding","callback","push","Buffer","from","concat","toString"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,cAAa;AAEpC,SAAQC,mBAAmB,QAAO,2BAA0B;AAC5D,SAAQC,cAAc,QAAO,sBAAqB;AAElD;;;;;CAKC,GACD,OAAO,eAAeC,iBAAiBC,GAAW;IAChD,MAAMC,SAAmB,EAAE;IAC3B,MAAMC,SAAS,MAAMJ,eACnBE,KACA,IACE,IAAIJ,SAAS;YACXO,OAAMC,KAAa,EAAEC,SAAS,EAAEC,QAAQ;gBACtCL,OAAOM,IAAI,CAACC,OAAOC,IAAI,CAACL;gBACxBE;YACF;QACF;IAGJT,oBAAoB,iDAAiDG,KAAKE;IAE1E,OAAOM,OAAOE,MAAM,CAACT,QAAQU,QAAQ;AACvC"}
@@ -0,0 +1,83 @@
1
+ import { Transform } from 'node:stream';
2
+ import { pipeline } from 'node:stream/promises';
3
+ import { createRequester, isTimeoutError, nodeReadableFromWeb, retry } from '@sanity/cli-core/request';
4
+ const CONNECTION_TIMEOUT = 15 * 1000 // 15 seconds
5
+ ;
6
+ const READ_TIMEOUT = 3 * 60 * 1000 // 3 minutes
7
+ ;
8
+ const request = createRequester({
9
+ middleware: [
10
+ retry()
11
+ ],
12
+ // Fresh 15s headers deadline per retry attempt (TimeoutError is retried for
13
+ // GET). No total deadline: the body phase is governed by the idle read
14
+ // timeout below, which resets on every received chunk.
15
+ timeout: {
16
+ headers: CONNECTION_TIMEOUT,
17
+ total: false
18
+ }
19
+ });
20
+ /**
21
+ * Downloads a backup response into a Node.js writable stream.
22
+ *
23
+ * The connection timeout only covers receiving the response headers. Once
24
+ * streaming starts, the read timeout is reset for every received chunk.
25
+ *
26
+ * @param url - Backup response URL
27
+ * @param createDestination - Creates the stream that receives the response body.
28
+ * Deferred so no destination (e.g. an empty file) exists when the request fails.
29
+ * @returns HTTP response status
30
+ * @internal
31
+ */ export async function downloadStream(url, createDestination) {
32
+ const abortController = new AbortController();
33
+ let response;
34
+ try {
35
+ response = await request({
36
+ as: 'stream',
37
+ signal: abortController.signal,
38
+ url
39
+ });
40
+ } catch (error) {
41
+ if (isTimeoutError(error)) {
42
+ throw new Error('Backup download timed out before receiving a response. Try again.', {
43
+ cause: error
44
+ });
45
+ }
46
+ throw error;
47
+ }
48
+ let destination;
49
+ try {
50
+ destination = createDestination();
51
+ } catch (error) {
52
+ await response.body.cancel().catch(()=>{
53
+ // Surface the destination error, not the cancellation error.
54
+ });
55
+ throw error;
56
+ }
57
+ let readTimeout;
58
+ const resetReadTimeout = ()=>{
59
+ if (readTimeout) clearTimeout(readTimeout);
60
+ readTimeout = setTimeout(()=>{
61
+ const timeoutMinutes = READ_TIMEOUT / (60 * 1000);
62
+ const error = new Error(`Backup download stalled: no data received for ${timeoutMinutes} minutes. Try again.`);
63
+ abortController.abort(error);
64
+ timeoutStream.destroy(error);
65
+ }, READ_TIMEOUT);
66
+ readTimeout.unref();
67
+ };
68
+ const timeoutStream = new Transform({
69
+ transform (chunk, _encoding, callback) {
70
+ resetReadTimeout();
71
+ callback(null, chunk);
72
+ }
73
+ });
74
+ resetReadTimeout();
75
+ try {
76
+ await pipeline(nodeReadableFromWeb(response.body), timeoutStream, destination);
77
+ } finally{
78
+ if (readTimeout) clearTimeout(readTimeout);
79
+ }
80
+ return response.status;
81
+ }
82
+
83
+ //# sourceMappingURL=downloadStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/backup/downloadStream.ts"],"sourcesContent":["import {Transform, type TransformCallback, type Writable} from 'node:stream'\nimport {pipeline} from 'node:stream/promises'\n\nimport {\n createRequester,\n isTimeoutError,\n nodeReadableFromWeb,\n retry,\n type StreamResponse,\n} from '@sanity/cli-core/request'\n\nconst CONNECTION_TIMEOUT = 15 * 1000 // 15 seconds\nconst READ_TIMEOUT = 3 * 60 * 1000 // 3 minutes\n\nconst request = createRequester({\n middleware: [retry()],\n // Fresh 15s headers deadline per retry attempt (TimeoutError is retried for\n // GET). No total deadline: the body phase is governed by the idle read\n // timeout below, which resets on every received chunk.\n timeout: {headers: CONNECTION_TIMEOUT, total: false},\n})\n\n/**\n * Downloads a backup response into a Node.js writable stream.\n *\n * The connection timeout only covers receiving the response headers. Once\n * streaming starts, the read timeout is reset for every received chunk.\n *\n * @param url - Backup response URL\n * @param createDestination - Creates the stream that receives the response body.\n * Deferred so no destination (e.g. an empty file) exists when the request fails.\n * @returns HTTP response status\n * @internal\n */\nexport async function downloadStream(\n url: string,\n createDestination: () => Writable,\n): Promise<number> {\n const abortController = new AbortController()\n\n let response: StreamResponse\n try {\n response = await request({\n as: 'stream',\n signal: abortController.signal,\n url,\n })\n } catch (error) {\n if (isTimeoutError(error)) {\n throw new Error('Backup download timed out before receiving a response. Try again.', {\n cause: error,\n })\n }\n throw error\n }\n\n let destination: Writable\n try {\n destination = createDestination()\n } catch (error) {\n await response.body.cancel().catch(() => {\n // Surface the destination error, not the cancellation error.\n })\n throw error\n }\n\n let readTimeout: NodeJS.Timeout | undefined\n const resetReadTimeout = () => {\n if (readTimeout) clearTimeout(readTimeout)\n readTimeout = setTimeout(() => {\n const timeoutMinutes = READ_TIMEOUT / (60 * 1000)\n const error = new Error(\n `Backup download stalled: no data received for ${timeoutMinutes} minutes. Try again.`,\n )\n abortController.abort(error)\n timeoutStream.destroy(error)\n }, READ_TIMEOUT)\n readTimeout.unref()\n }\n const timeoutStream = new Transform({\n transform(chunk: Buffer, _encoding: BufferEncoding, callback: TransformCallback) {\n resetReadTimeout()\n callback(null, chunk)\n },\n })\n\n resetReadTimeout()\n try {\n await pipeline(nodeReadableFromWeb(response.body), timeoutStream, destination)\n } finally {\n if (readTimeout) clearTimeout(readTimeout)\n }\n\n return response.status\n}\n"],"names":["Transform","pipeline","createRequester","isTimeoutError","nodeReadableFromWeb","retry","CONNECTION_TIMEOUT","READ_TIMEOUT","request","middleware","timeout","headers","total","downloadStream","url","createDestination","abortController","AbortController","response","as","signal","error","Error","cause","destination","body","cancel","catch","readTimeout","resetReadTimeout","clearTimeout","setTimeout","timeoutMinutes","abort","timeoutStream","destroy","unref","transform","chunk","_encoding","callback","status"],"mappings":"AAAA,SAAQA,SAAS,QAA8C,cAAa;AAC5E,SAAQC,QAAQ,QAAO,uBAAsB;AAE7C,SACEC,eAAe,EACfC,cAAc,EACdC,mBAAmB,EACnBC,KAAK,QAEA,2BAA0B;AAEjC,MAAMC,qBAAqB,KAAK,KAAK,aAAa;;AAClD,MAAMC,eAAe,IAAI,KAAK,KAAK,YAAY;;AAE/C,MAAMC,UAAUN,gBAAgB;IAC9BO,YAAY;QAACJ;KAAQ;IACrB,4EAA4E;IAC5E,uEAAuE;IACvE,uDAAuD;IACvDK,SAAS;QAACC,SAASL;QAAoBM,OAAO;IAAK;AACrD;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,eAAeC,eACpBC,GAAW,EACXC,iBAAiC;IAEjC,MAAMC,kBAAkB,IAAIC;IAE5B,IAAIC;IACJ,IAAI;QACFA,WAAW,MAAMV,QAAQ;YACvBW,IAAI;YACJC,QAAQJ,gBAAgBI,MAAM;YAC9BN;QACF;IACF,EAAE,OAAOO,OAAO;QACd,IAAIlB,eAAekB,QAAQ;YACzB,MAAM,IAAIC,MAAM,qEAAqE;gBACnFC,OAAOF;YACT;QACF;QACA,MAAMA;IACR;IAEA,IAAIG;IACJ,IAAI;QACFA,cAAcT;IAChB,EAAE,OAAOM,OAAO;QACd,MAAMH,SAASO,IAAI,CAACC,MAAM,GAAGC,KAAK,CAAC;QACjC,6DAA6D;QAC/D;QACA,MAAMN;IACR;IAEA,IAAIO;IACJ,MAAMC,mBAAmB;QACvB,IAAID,aAAaE,aAAaF;QAC9BA,cAAcG,WAAW;YACvB,MAAMC,iBAAiBzB,eAAgB,CAAA,KAAK,IAAG;YAC/C,MAAMc,QAAQ,IAAIC,MAChB,CAAC,8CAA8C,EAAEU,eAAe,oBAAoB,CAAC;YAEvFhB,gBAAgBiB,KAAK,CAACZ;YACtBa,cAAcC,OAAO,CAACd;QACxB,GAAGd;QACHqB,YAAYQ,KAAK;IACnB;IACA,MAAMF,gBAAgB,IAAIlC,UAAU;QAClCqC,WAAUC,KAAa,EAAEC,SAAyB,EAAEC,QAA2B;YAC7EX;YACAW,SAAS,MAAMF;QACjB;IACF;IAEAT;IACA,IAAI;QACF,MAAM5B,SAASG,oBAAoBc,SAASO,IAAI,GAAGS,eAAeV;IACpE,SAAU;QACR,IAAII,aAAaE,aAAaF;IAChC;IAEA,OAAOV,SAASuB,MAAM;AACxB"}
@@ -30,9 +30,6 @@ export async function validateDocuments(options) {
30
30
  // `clientConfiguration` object and also mock a browser environment in
31
31
  // this worker which triggers the browser warning
32
32
  ignoreBrowserTokenWarning: true,
33
- // Removing from object so config can be serialized
34
- // before sent to validation worker
35
- requester: undefined,
36
33
  // we set this explictly to true because the default client configuration
37
34
  // from the CLI comes configured with `useProjectHostname: false` when
38
35
  // `requireProject` is set to false
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/documents/validate.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core/apiClient'\nimport {createStudioWorker} from '@sanity/cli-core/tasks'\nimport {type ClientConfig} from '@sanity/client'\nimport {type ValidationMarker} from '@sanity/types'\nimport {WorkerChannelReceiver} from '@sanity/worker-channels'\n\nimport {DOCUMENTS_API_VERSION} from './constants.js'\nimport {\n Level,\n type ValidateDocumentsWorkerData,\n type ValidationReceiver,\n type ValidationWorkerChannel,\n} from './types.js'\n\ninterface ValidateDocumentsOptions<TReturn = unknown> {\n dataset?: string // override\n level?: Level\n maxCustomValidationConcurrency?: number\n maxFetchConcurrency?: number\n ndjsonFilePath?: string\n projectId?: string // override\n reporter?: (worker: ValidationReceiver) => TReturn\n studioHost?: string\n workDir?: string\n workspace?: string\n}\n\ninterface DocumentValidationResult {\n documentId: string\n documentType: string\n level: ValidationMarker['level']\n markers: ValidationMarker[]\n revision: string\n}\n\nconst defaultReporter = ({dispose, stream}: ValidationReceiver) => {\n async function* createValidationGenerator() {\n for await (const {documentId, documentType, level, markers, revision} of stream.validation()) {\n const result: DocumentValidationResult = {\n documentId,\n documentType,\n level,\n markers,\n revision,\n }\n\n yield result\n }\n\n await dispose()\n }\n\n return createValidationGenerator()\n}\n\nexport function validateDocuments<TReturn>(\n options: Required<Pick<ValidateDocumentsOptions<TReturn>, 'reporter'>> &\n ValidateDocumentsOptions<TReturn>,\n): Promise<TReturn>\nexport function validateDocuments(\n options: ValidateDocumentsOptions,\n): Promise<AsyncIterable<DocumentValidationResult>>\nexport async function validateDocuments(options: ValidateDocumentsOptions): Promise<unknown> {\n const {\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n reporter = defaultReporter,\n workDir = process.cwd(),\n workspace,\n } = options\n\n const apiClient = await getGlobalCliClient({\n apiVersion: DOCUMENTS_API_VERSION,\n requireUser: true,\n })\n\n const clientConfig: ClientConfig = {\n ...apiClient.config(),\n // we set this explictly to true because we pass in a token via the\n // `clientConfiguration` object and also mock a browser environment in\n // this worker which triggers the browser warning\n ignoreBrowserTokenWarning: true,\n // Removing from object so config can be serialized\n // before sent to validation worker\n requester: undefined,\n // we set this explictly to true because the default client configuration\n // from the CLI comes configured with `useProjectHostname: false` when\n // `requireProject` is set to false\n useProjectHostname: true,\n }\n\n const worker = createStudioWorker(new URL('validateDocuments.worker.js', import.meta.url), {\n name: 'validateDocuments',\n studioRootPath: workDir,\n workerData: {\n // removes props in the config that make this object fail to serialize\n clientConfig: structuredClone(clientConfig),\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n studioHost: options.studioHost,\n workDir,\n workspace,\n } satisfies ValidateDocumentsWorkerData,\n })\n\n const receiver = WorkerChannelReceiver.from<ValidationWorkerChannel>(worker)\n const validationReceiver: ValidationReceiver = {\n dispose: async () => {\n receiver.unsubscribe()\n return worker.terminate()\n },\n event: receiver.event,\n stream: receiver.stream,\n }\n\n return reporter(validationReceiver)\n}\n"],"names":["getGlobalCliClient","createStudioWorker","WorkerChannelReceiver","DOCUMENTS_API_VERSION","defaultReporter","dispose","stream","createValidationGenerator","documentId","documentType","level","markers","revision","validation","result","validateDocuments","options","dataset","maxCustomValidationConcurrency","maxFetchConcurrency","ndjsonFilePath","projectId","reporter","workDir","process","cwd","workspace","apiClient","apiVersion","requireUser","clientConfig","config","ignoreBrowserTokenWarning","requester","undefined","useProjectHostname","worker","URL","url","name","studioRootPath","workerData","structuredClone","studioHost","receiver","from","validationReceiver","unsubscribe","terminate","event"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,6BAA4B;AAC7D,SAAQC,kBAAkB,QAAO,yBAAwB;AAGzD,SAAQC,qBAAqB,QAAO,0BAAyB;AAE7D,SAAQC,qBAAqB,QAAO,iBAAgB;AA6BpD,MAAMC,kBAAkB,CAAC,EAACC,OAAO,EAAEC,MAAM,EAAqB;IAC5D,gBAAgBC;QACd,WAAW,MAAM,EAACC,UAAU,EAAEC,YAAY,EAAEC,KAAK,EAAEC,OAAO,EAAEC,QAAQ,EAAC,IAAIN,OAAOO,UAAU,GAAI;YAC5F,MAAMC,SAAmC;gBACvCN;gBACAC;gBACAC;gBACAC;gBACAC;YACF;YAEA,MAAME;QACR;QAEA,MAAMT;IACR;IAEA,OAAOE;AACT;AASA,OAAO,eAAeQ,kBAAkBC,OAAiC;IACvE,MAAM,EACJC,OAAO,EACPP,KAAK,EACLQ,8BAA8B,EAC9BC,mBAAmB,EACnBC,cAAc,EACdC,SAAS,EACTC,WAAWlB,eAAe,EAC1BmB,UAAUC,QAAQC,GAAG,EAAE,EACvBC,SAAS,EACV,GAAGV;IAEJ,MAAMW,YAAY,MAAM3B,mBAAmB;QACzC4B,YAAYzB;QACZ0B,aAAa;IACf;IAEA,MAAMC,eAA6B;QACjC,GAAGH,UAAUI,MAAM,EAAE;QACrB,mEAAmE;QACnE,sEAAsE;QACtE,iDAAiD;QACjDC,2BAA2B;QAC3B,mDAAmD;QACnD,mCAAmC;QACnCC,WAAWC;QACX,yEAAyE;QACzE,sEAAsE;QACtE,mCAAmC;QACnCC,oBAAoB;IACtB;IAEA,MAAMC,SAASnC,mBAAmB,IAAIoC,IAAI,+BAA+B,YAAYC,GAAG,GAAG;QACzFC,MAAM;QACNC,gBAAgBjB;QAChBkB,YAAY;YACV,sEAAsE;YACtEX,cAAcY,gBAAgBZ;YAC9Bb;YACAP;YACAQ;YACAC;YACAC;YACAC;YACAsB,YAAY3B,QAAQ2B,UAAU;YAC9BpB;YACAG;QACF;IACF;IAEA,MAAMkB,WAAW1C,sBAAsB2C,IAAI,CAA0BT;IACrE,MAAMU,qBAAyC;QAC7CzC,SAAS;YACPuC,SAASG,WAAW;YACpB,OAAOX,OAAOY,SAAS;QACzB;QACAC,OAAOL,SAASK,KAAK;QACrB3C,QAAQsC,SAAStC,MAAM;IACzB;IAEA,OAAOgB,SAASwB;AAClB"}
1
+ {"version":3,"sources":["../../../src/actions/documents/validate.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core/apiClient'\nimport {createStudioWorker} from '@sanity/cli-core/tasks'\nimport {type ClientConfig} from '@sanity/client'\nimport {type ValidationMarker} from '@sanity/types'\nimport {WorkerChannelReceiver} from '@sanity/worker-channels'\n\nimport {DOCUMENTS_API_VERSION} from './constants.js'\nimport {\n Level,\n type ValidateDocumentsWorkerData,\n type ValidationReceiver,\n type ValidationWorkerChannel,\n} from './types.js'\n\ninterface ValidateDocumentsOptions<TReturn = unknown> {\n dataset?: string // override\n level?: Level\n maxCustomValidationConcurrency?: number\n maxFetchConcurrency?: number\n ndjsonFilePath?: string\n projectId?: string // override\n reporter?: (worker: ValidationReceiver) => TReturn\n studioHost?: string\n workDir?: string\n workspace?: string\n}\n\ninterface DocumentValidationResult {\n documentId: string\n documentType: string\n level: ValidationMarker['level']\n markers: ValidationMarker[]\n revision: string\n}\n\nconst defaultReporter = ({dispose, stream}: ValidationReceiver) => {\n async function* createValidationGenerator() {\n for await (const {documentId, documentType, level, markers, revision} of stream.validation()) {\n const result: DocumentValidationResult = {\n documentId,\n documentType,\n level,\n markers,\n revision,\n }\n\n yield result\n }\n\n await dispose()\n }\n\n return createValidationGenerator()\n}\n\nexport function validateDocuments<TReturn>(\n options: Required<Pick<ValidateDocumentsOptions<TReturn>, 'reporter'>> &\n ValidateDocumentsOptions<TReturn>,\n): Promise<TReturn>\nexport function validateDocuments(\n options: ValidateDocumentsOptions,\n): Promise<AsyncIterable<DocumentValidationResult>>\nexport async function validateDocuments(options: ValidateDocumentsOptions): Promise<unknown> {\n const {\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n reporter = defaultReporter,\n workDir = process.cwd(),\n workspace,\n } = options\n\n const apiClient = await getGlobalCliClient({\n apiVersion: DOCUMENTS_API_VERSION,\n requireUser: true,\n })\n\n const clientConfig: ClientConfig = {\n ...apiClient.config(),\n // we set this explictly to true because we pass in a token via the\n // `clientConfiguration` object and also mock a browser environment in\n // this worker which triggers the browser warning\n ignoreBrowserTokenWarning: true,\n // we set this explictly to true because the default client configuration\n // from the CLI comes configured with `useProjectHostname: false` when\n // `requireProject` is set to false\n useProjectHostname: true,\n }\n\n const worker = createStudioWorker(new URL('validateDocuments.worker.js', import.meta.url), {\n name: 'validateDocuments',\n studioRootPath: workDir,\n workerData: {\n // removes props in the config that make this object fail to serialize\n clientConfig: structuredClone(clientConfig),\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n studioHost: options.studioHost,\n workDir,\n workspace,\n } satisfies ValidateDocumentsWorkerData,\n })\n\n const receiver = WorkerChannelReceiver.from<ValidationWorkerChannel>(worker)\n const validationReceiver: ValidationReceiver = {\n dispose: async () => {\n receiver.unsubscribe()\n return worker.terminate()\n },\n event: receiver.event,\n stream: receiver.stream,\n }\n\n return reporter(validationReceiver)\n}\n"],"names":["getGlobalCliClient","createStudioWorker","WorkerChannelReceiver","DOCUMENTS_API_VERSION","defaultReporter","dispose","stream","createValidationGenerator","documentId","documentType","level","markers","revision","validation","result","validateDocuments","options","dataset","maxCustomValidationConcurrency","maxFetchConcurrency","ndjsonFilePath","projectId","reporter","workDir","process","cwd","workspace","apiClient","apiVersion","requireUser","clientConfig","config","ignoreBrowserTokenWarning","useProjectHostname","worker","URL","url","name","studioRootPath","workerData","structuredClone","studioHost","receiver","from","validationReceiver","unsubscribe","terminate","event"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,6BAA4B;AAC7D,SAAQC,kBAAkB,QAAO,yBAAwB;AAGzD,SAAQC,qBAAqB,QAAO,0BAAyB;AAE7D,SAAQC,qBAAqB,QAAO,iBAAgB;AA6BpD,MAAMC,kBAAkB,CAAC,EAACC,OAAO,EAAEC,MAAM,EAAqB;IAC5D,gBAAgBC;QACd,WAAW,MAAM,EAACC,UAAU,EAAEC,YAAY,EAAEC,KAAK,EAAEC,OAAO,EAAEC,QAAQ,EAAC,IAAIN,OAAOO,UAAU,GAAI;YAC5F,MAAMC,SAAmC;gBACvCN;gBACAC;gBACAC;gBACAC;gBACAC;YACF;YAEA,MAAME;QACR;QAEA,MAAMT;IACR;IAEA,OAAOE;AACT;AASA,OAAO,eAAeQ,kBAAkBC,OAAiC;IACvE,MAAM,EACJC,OAAO,EACPP,KAAK,EACLQ,8BAA8B,EAC9BC,mBAAmB,EACnBC,cAAc,EACdC,SAAS,EACTC,WAAWlB,eAAe,EAC1BmB,UAAUC,QAAQC,GAAG,EAAE,EACvBC,SAAS,EACV,GAAGV;IAEJ,MAAMW,YAAY,MAAM3B,mBAAmB;QACzC4B,YAAYzB;QACZ0B,aAAa;IACf;IAEA,MAAMC,eAA6B;QACjC,GAAGH,UAAUI,MAAM,EAAE;QACrB,mEAAmE;QACnE,sEAAsE;QACtE,iDAAiD;QACjDC,2BAA2B;QAC3B,yEAAyE;QACzE,sEAAsE;QACtE,mCAAmC;QACnCC,oBAAoB;IACtB;IAEA,MAAMC,SAASjC,mBAAmB,IAAIkC,IAAI,+BAA+B,YAAYC,GAAG,GAAG;QACzFC,MAAM;QACNC,gBAAgBf;QAChBgB,YAAY;YACV,sEAAsE;YACtET,cAAcU,gBAAgBV;YAC9Bb;YACAP;YACAQ;YACAC;YACAC;YACAC;YACAoB,YAAYzB,QAAQyB,UAAU;YAC9BlB;YACAG;QACF;IACF;IAEA,MAAMgB,WAAWxC,sBAAsByC,IAAI,CAA0BT;IACrE,MAAMU,qBAAyC;QAC7CvC,SAAS;YACPqC,SAASG,WAAW;YACpB,OAAOX,OAAOY,SAAS;QACzB;QACAC,OAAOL,SAASK,KAAK;QACrBzC,QAAQoC,SAASpC,MAAM;IACzB;IAEA,OAAOgB,SAASsB;AAClB"}
@@ -196,7 +196,10 @@ async function main() {
196
196
  validateDocument({
197
197
  document,
198
198
  environment: 'cli',
199
- getClient,
199
+ // The local `sanity` package types its client against its own
200
+ // @sanity/client major; the runtime surface validation uses is
201
+ // compatible across v7/v8.
202
+ getClient: getClient,
200
203
  getDocumentExists,
201
204
  maxCustomValidationConcurrency,
202
205
  maxFetchConcurrency,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/documents/validateDocuments.worker.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport readline from 'node:readline'\nimport {Readable} from 'node:stream'\nimport {workerData as _workerData, isMainThread, parentPort} from 'node:worker_threads'\n\nimport {getStudioWorkspaces, getWorkspace, resolveLocalPackage} from '@sanity/cli-core'\nimport {type ClientConfig, createClient, type SanityDocument} from '@sanity/client'\nimport {type ValidationContext, type ValidationMarker} from '@sanity/types'\nimport {WorkerChannelReporter} from '@sanity/worker-channels'\nimport pMap from 'p-map'\n\nimport {\n type AvailabilityResponse,\n checkDocumentAvailability,\n exportDocuments,\n getDocumentCount,\n} from '../../services/documents.js'\nimport {extractDocumentsFromNdjsonOrTarball} from '../../util/extractDocumentsFromNdjsonOrTarball.js'\nimport {\n DOCUMENT_VALIDATION_TIMEOUT,\n getReferenceIds,\n isValidId,\n levelValues,\n MAX_VALIDATION_CONCURRENCY,\n REFERENCE_INTEGRITY_BATCH_SIZE,\n shouldIncludeDocument,\n} from '../../util/validation/validateDocumentsUtils.js'\nimport {type ValidateDocumentsWorkerData, type ValidationWorkerChannel} from './types.js'\n\nconst {\n clientConfig,\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n studioHost,\n workDir,\n workspace: workspaceName,\n} = _workerData as ValidateDocumentsWorkerData\n\nif (isMainThread || !parentPort) {\n throw new Error('This module must be run as a worker thread')\n}\n\nconst report = WorkerChannelReporter.from<ValidationWorkerChannel>(parentPort)\n\n// eslint-disable-next-line n/no-unsupported-features/node-builtins\nasync function* readerToGenerator(reader: ReadableStreamDefaultReader<Uint8Array>) {\n while (true) {\n const {done, value} = await reader.read()\n if (value) yield value\n if (done) return\n }\n}\n\nawait main()\nprocess.exit()\n\nasync function loadWorkspace() {\n const workspaces = await getStudioWorkspaces(workDir)\n const workspace = getWorkspace(workspaces, workspaceName)\n\n const resolvedDataset = dataset || workspace.dataset\n const resolvedProjectId = projectId || workspace.projectId\n\n const client = createClient({\n ...clientConfig,\n dataset: resolvedDataset,\n projectId: resolvedProjectId,\n requestTagPrefix: 'sanity.cli.validate',\n }).config({apiVersion: 'v2021-03-25'})\n\n report.event.loadedWorkspace({\n basePath: workspace.basePath,\n dataset: workspace.dataset,\n name: workspace.name,\n projectId: workspace.projectId,\n })\n\n return {client, dataset: resolvedDataset, projectId: resolvedProjectId, workspace}\n}\n\ninterface DownloadFromExportOptions {\n dataset: string\n projectId: string\n}\n\nasync function downloadFromExport({\n dataset: datasetName,\n projectId: projId,\n}: DownloadFromExportOptions) {\n const documentCount = await getDocumentCount({dataset: datasetName, projectId: projId})\n report.event.loadedDocumentCount({documentCount})\n\n const response = await exportDocuments({dataset: datasetName, projectId: projId})\n\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Could not get reader from response body.')\n\n let downloadedCount = 0\n const referencedIds = new Set<string>()\n const documentIds = new Set<string>()\n const lines = readline.createInterface({input: Readable.from(readerToGenerator(reader))})\n\n // Note: we stream the export to a file and then re-read from that file to\n // make this less memory intensive.\n // this is a similar pattern to the import/export CLI commands\n const slugDate = new Date()\n .toISOString()\n .replaceAll(/[^a-z0-9]/gi, '-')\n .toLowerCase()\n const tempOutputFile = path.join(os.tmpdir(), `sanity-validate-${slugDate}.ndjson`)\n const outputStream = fs.createWriteStream(tempOutputFile)\n\n for await (const line of lines) {\n const document = JSON.parse(line) as SanityDocument\n\n if (shouldIncludeDocument(document)) {\n documentIds.add(document._id)\n for (const referenceId of getReferenceIds(document)) {\n referencedIds.add(referenceId)\n }\n\n outputStream.write(`${line}\\n`)\n }\n\n downloadedCount++\n report.stream.exportProgress.emit({documentCount, downloadedCount})\n }\n\n await new Promise<void>((resolve, reject) =>\n outputStream.close((err) => (err ? reject(err) : resolve())),\n )\n\n report.stream.exportProgress.end()\n report.event.exportFinished({totalDocumentsToValidate: documentIds.size})\n\n const getDocuments = () =>\n extractDocumentsFromNdjsonOrTarball(fs.createReadStream(tempOutputFile))\n\n return {cleanup: () => fs.promises.rm(tempOutputFile), documentIds, getDocuments, referencedIds}\n}\n\nasync function downloadFromFile(filePath: string) {\n const referencedIds = new Set<string>()\n const documentIds = new Set<string>()\n const getDocuments = () => extractDocumentsFromNdjsonOrTarball(fs.createReadStream(filePath))\n\n for await (const document of getDocuments()) {\n if (shouldIncludeDocument(document)) {\n documentIds.add(document._id)\n for (const referenceId of getReferenceIds(document)) {\n referencedIds.add(referenceId)\n }\n }\n }\n\n report.event.exportFinished({totalDocumentsToValidate: documentIds.size})\n\n return {cleanup: undefined, documentIds, getDocuments, referencedIds}\n}\n\ninterface CheckReferenceExistenceOptions {\n dataset: string\n documentIds: Set<string>\n projectId: string\n referencedIds: Set<string>\n}\n\nasync function checkReferenceExistence({\n dataset: datasetName,\n documentIds,\n projectId: projId,\n referencedIds: _referencedIds,\n}: CheckReferenceExistenceOptions) {\n const existingIds = new Set(documentIds)\n const idsToCheck = [..._referencedIds]\n .filter((id) => !existingIds.has(id) && isValidId(id))\n .toSorted()\n\n const batches: string[][] = []\n for (let i = 0; i < idsToCheck.length; i += REFERENCE_INTEGRITY_BATCH_SIZE) {\n batches.push(idsToCheck.slice(i, i + REFERENCE_INTEGRITY_BATCH_SIZE))\n }\n\n for (const batch of batches) {\n const {omitted} = await checkDocumentAvailability({\n dataset: datasetName,\n documentIds: batch,\n projectId: projId,\n })\n\n const omittedIds: Record<string, AvailabilityResponse['omitted'][number]['reason']> = {}\n for (const item of omitted) {\n omittedIds[item.id] = item.reason\n }\n\n for (const id of batch) {\n // unless the document ID is in the `omitted` object explictly due to\n // the reason `'existence'`, then it should exist\n if (omittedIds[id] !== 'existence') {\n existingIds.add(id)\n }\n }\n }\n report.event.loadedReferenceIntegrity()\n\n return {existingIds}\n}\n\nasync function main() {\n let cleanupDownloadedDocuments: (() => Promise<void>) | undefined\n\n try {\n const {client, dataset: datasetName, projectId: projId, workspace} = await loadWorkspace()\n const {cleanup, documentIds, getDocuments, referencedIds} = ndjsonFilePath\n ? await downloadFromFile(ndjsonFilePath)\n : await downloadFromExport({dataset: datasetName, projectId: projId})\n cleanupDownloadedDocuments = cleanup\n const {existingIds} = await checkReferenceExistence({\n dataset: datasetName,\n documentIds,\n projectId: projId,\n referencedIds,\n })\n\n const getClient = <TOptions extends Partial<ClientConfig>>(options: TOptions) =>\n client.withConfig(options)\n\n const getDocumentExists: ValidationContext['getDocumentExists'] = ({id}) =>\n Promise.resolve(existingIds.has(id))\n\n const getLevel = (markers: ValidationMarker[]) => {\n let foundWarning = false\n for (const marker of markers) {\n if (marker.level === 'error') return 'error'\n if (marker.level === 'warning') foundWarning = true\n }\n\n if (foundWarning) return 'warning'\n return 'info'\n }\n\n let validatedCount = 0\n\n const validate = async (document: SanityDocument) => {\n let markers: ValidationMarker[]\n\n try {\n const timeout = Symbol('timeout')\n\n const {validateDocument} = await resolveLocalPackage<typeof import('sanity')>(\n 'sanity',\n workDir,\n )\n\n const result = await Promise.race([\n validateDocument({\n document,\n environment: 'cli',\n getClient,\n getDocumentExists,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n workspace,\n }),\n new Promise<typeof timeout>((resolve) =>\n setTimeout(() => resolve(timeout), DOCUMENT_VALIDATION_TIMEOUT),\n ),\n ])\n\n if (result === timeout) {\n throw new Error(\n `Document '${document._id}' failed to validate within ${DOCUMENT_VALIDATION_TIMEOUT}ms.`,\n )\n }\n\n markers = result\n // filter out unwanted levels\n .filter((marker) => {\n const markerValue = levelValues[marker.level]\n const flagLevelValue =\n levelValues[level as keyof typeof levelValues] ?? levelValues.info\n return markerValue <= flagLevelValue\n })\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error'\n\n const message = `Exception occurred while validating value: ${errorMessage}`\n\n markers = [\n {\n level: 'error',\n message,\n path: [],\n },\n ]\n }\n\n validatedCount++\n\n const intentUrl =\n studioHost &&\n `${studioHost}${path.resolve(\n workspace.basePath,\n `/intent/edit/id=${encodeURIComponent(document._id)};type=${encodeURIComponent(\n document._type,\n )}`,\n )}`\n\n report.stream.validation.emit({\n documentId: document._id,\n documentType: document._type,\n ...(intentUrl && {intentUrl}),\n level: getLevel(markers),\n markers,\n revision: document._rev,\n validatedCount,\n })\n }\n\n await pMap(getDocuments(), validate, {concurrency: MAX_VALIDATION_CONCURRENCY})\n\n report.stream.validation.end()\n } finally {\n await cleanupDownloadedDocuments?.()\n }\n}\n"],"names":["fs","os","path","readline","Readable","workerData","_workerData","isMainThread","parentPort","getStudioWorkspaces","getWorkspace","resolveLocalPackage","createClient","WorkerChannelReporter","pMap","checkDocumentAvailability","exportDocuments","getDocumentCount","extractDocumentsFromNdjsonOrTarball","DOCUMENT_VALIDATION_TIMEOUT","getReferenceIds","isValidId","levelValues","MAX_VALIDATION_CONCURRENCY","REFERENCE_INTEGRITY_BATCH_SIZE","shouldIncludeDocument","clientConfig","dataset","level","maxCustomValidationConcurrency","maxFetchConcurrency","ndjsonFilePath","projectId","studioHost","workDir","workspace","workspaceName","Error","report","from","readerToGenerator","reader","done","value","read","main","process","exit","loadWorkspace","workspaces","resolvedDataset","resolvedProjectId","client","requestTagPrefix","config","apiVersion","event","loadedWorkspace","basePath","name","downloadFromExport","datasetName","projId","documentCount","loadedDocumentCount","response","body","getReader","downloadedCount","referencedIds","Set","documentIds","lines","createInterface","input","slugDate","Date","toISOString","replaceAll","toLowerCase","tempOutputFile","join","tmpdir","outputStream","createWriteStream","line","document","JSON","parse","add","_id","referenceId","write","stream","exportProgress","emit","Promise","resolve","reject","close","err","end","exportFinished","totalDocumentsToValidate","size","getDocuments","createReadStream","cleanup","promises","rm","downloadFromFile","filePath","undefined","checkReferenceExistence","_referencedIds","existingIds","idsToCheck","filter","id","has","toSorted","batches","i","length","push","slice","batch","omitted","omittedIds","item","reason","loadedReferenceIntegrity","cleanupDownloadedDocuments","getClient","options","withConfig","getDocumentExists","getLevel","markers","foundWarning","marker","validatedCount","validate","timeout","Symbol","validateDocument","result","race","environment","setTimeout","markerValue","flagLevelValue","info","errorMessage","message","intentUrl","encodeURIComponent","_type","validation","documentId","documentType","revision","_rev","concurrency"],"mappings":"AAAA,OAAOA,QAAQ,UAAS;AACxB,OAAOC,QAAQ,UAAS;AACxB,OAAOC,UAAU,YAAW;AAC5B,OAAOC,cAAc,gBAAe;AACpC,SAAQC,QAAQ,QAAO,cAAa;AACpC,SAAQC,cAAcC,WAAW,EAAEC,YAAY,EAAEC,UAAU,QAAO,sBAAqB;AAEvF,SAAQC,mBAAmB,EAAEC,YAAY,EAAEC,mBAAmB,QAAO,mBAAkB;AACvF,SAA2BC,YAAY,QAA4B,iBAAgB;AAEnF,SAAQC,qBAAqB,QAAO,0BAAyB;AAC7D,OAAOC,UAAU,QAAO;AAExB,SAEEC,yBAAyB,EACzBC,eAAe,EACfC,gBAAgB,QACX,8BAA6B;AACpC,SAAQC,mCAAmC,QAAO,oDAAmD;AACrG,SACEC,2BAA2B,EAC3BC,eAAe,EACfC,SAAS,EACTC,WAAW,EACXC,0BAA0B,EAC1BC,8BAA8B,EAC9BC,qBAAqB,QAChB,kDAAiD;AAGxD,MAAM,EACJC,YAAY,EACZC,OAAO,EACPC,KAAK,EACLC,8BAA8B,EAC9BC,mBAAmB,EACnBC,cAAc,EACdC,SAAS,EACTC,UAAU,EACVC,OAAO,EACPC,WAAWC,aAAa,EACzB,GAAG9B;AAEJ,IAAIC,gBAAgB,CAACC,YAAY;IAC/B,MAAM,IAAI6B,MAAM;AAClB;AAEA,MAAMC,SAASzB,sBAAsB0B,IAAI,CAA0B/B;AAEnE,mEAAmE;AACnE,gBAAgBgC,kBAAkBC,MAA+C;IAC/E,MAAO,KAAM;QACX,MAAM,EAACC,IAAI,EAAEC,KAAK,EAAC,GAAG,MAAMF,OAAOG,IAAI;QACvC,IAAID,OAAO,MAAMA;QACjB,IAAID,MAAM;IACZ;AACF;AAEA,MAAMG;AACNC,QAAQC,IAAI;AAEZ,eAAeC;IACb,MAAMC,aAAa,MAAMxC,oBAAoByB;IAC7C,MAAMC,YAAYzB,aAAauC,YAAYb;IAE3C,MAAMc,kBAAkBvB,WAAWQ,UAAUR,OAAO;IACpD,MAAMwB,oBAAoBnB,aAAaG,UAAUH,SAAS;IAE1D,MAAMoB,SAASxC,aAAa;QAC1B,GAAGc,YAAY;QACfC,SAASuB;QACTlB,WAAWmB;QACXE,kBAAkB;IACpB,GAAGC,MAAM,CAAC;QAACC,YAAY;IAAa;IAEpCjB,OAAOkB,KAAK,CAACC,eAAe,CAAC;QAC3BC,UAAUvB,UAAUuB,QAAQ;QAC5B/B,SAASQ,UAAUR,OAAO;QAC1BgC,MAAMxB,UAAUwB,IAAI;QACpB3B,WAAWG,UAAUH,SAAS;IAChC;IAEA,OAAO;QAACoB;QAAQzB,SAASuB;QAAiBlB,WAAWmB;QAAmBhB;IAAS;AACnF;AAOA,eAAeyB,mBAAmB,EAChCjC,SAASkC,WAAW,EACpB7B,WAAW8B,MAAM,EACS;IAC1B,MAAMC,gBAAgB,MAAM9C,iBAAiB;QAACU,SAASkC;QAAa7B,WAAW8B;IAAM;IACrFxB,OAAOkB,KAAK,CAACQ,mBAAmB,CAAC;QAACD;IAAa;IAE/C,MAAME,WAAW,MAAMjD,gBAAgB;QAACW,SAASkC;QAAa7B,WAAW8B;IAAM;IAE/E,MAAMrB,SAASwB,SAASC,IAAI,EAAEC;IAC9B,IAAI,CAAC1B,QAAQ,MAAM,IAAIJ,MAAM;IAE7B,IAAI+B,kBAAkB;IACtB,MAAMC,gBAAgB,IAAIC;IAC1B,MAAMC,cAAc,IAAID;IACxB,MAAME,QAAQrE,SAASsE,eAAe,CAAC;QAACC,OAAOtE,SAASmC,IAAI,CAACC,kBAAkBC;IAAQ;IAEvF,0EAA0E;IAC1E,mCAAmC;IACnC,8DAA8D;IAC9D,MAAMkC,WAAW,IAAIC,OAClBC,WAAW,GACXC,UAAU,CAAC,eAAe,KAC1BC,WAAW;IACd,MAAMC,iBAAiB9E,KAAK+E,IAAI,CAAChF,GAAGiF,MAAM,IAAI,CAAC,gBAAgB,EAAEP,SAAS,OAAO,CAAC;IAClF,MAAMQ,eAAenF,GAAGoF,iBAAiB,CAACJ;IAE1C,WAAW,MAAMK,QAAQb,MAAO;QAC9B,MAAMc,WAAWC,KAAKC,KAAK,CAACH;QAE5B,IAAI5D,sBAAsB6D,WAAW;YACnCf,YAAYkB,GAAG,CAACH,SAASI,GAAG;YAC5B,KAAK,MAAMC,eAAevE,gBAAgBkE,UAAW;gBACnDjB,cAAcoB,GAAG,CAACE;YACpB;YAEAR,aAAaS,KAAK,CAAC,GAAGP,KAAK,EAAE,CAAC;QAChC;QAEAjB;QACA9B,OAAOuD,MAAM,CAACC,cAAc,CAACC,IAAI,CAAC;YAAChC;YAAeK;QAAe;IACnE;IAEA,MAAM,IAAI4B,QAAc,CAACC,SAASC,SAChCf,aAAagB,KAAK,CAAC,CAACC,MAASA,MAAMF,OAAOE,OAAOH;IAGnD3D,OAAOuD,MAAM,CAACC,cAAc,CAACO,GAAG;IAChC/D,OAAOkB,KAAK,CAAC8C,cAAc,CAAC;QAACC,0BAA0BhC,YAAYiC,IAAI;IAAA;IAEvE,MAAMC,eAAe,IACnBvF,oCAAoClB,GAAG0G,gBAAgB,CAAC1B;IAE1D,OAAO;QAAC2B,SAAS,IAAM3G,GAAG4G,QAAQ,CAACC,EAAE,CAAC7B;QAAiBT;QAAakC;QAAcpC;IAAa;AACjG;AAEA,eAAeyC,iBAAiBC,QAAgB;IAC9C,MAAM1C,gBAAgB,IAAIC;IAC1B,MAAMC,cAAc,IAAID;IACxB,MAAMmC,eAAe,IAAMvF,oCAAoClB,GAAG0G,gBAAgB,CAACK;IAEnF,WAAW,MAAMzB,YAAYmB,eAAgB;QAC3C,IAAIhF,sBAAsB6D,WAAW;YACnCf,YAAYkB,GAAG,CAACH,SAASI,GAAG;YAC5B,KAAK,MAAMC,eAAevE,gBAAgBkE,UAAW;gBACnDjB,cAAcoB,GAAG,CAACE;YACpB;QACF;IACF;IAEArD,OAAOkB,KAAK,CAAC8C,cAAc,CAAC;QAACC,0BAA0BhC,YAAYiC,IAAI;IAAA;IAEvE,OAAO;QAACG,SAASK;QAAWzC;QAAakC;QAAcpC;IAAa;AACtE;AASA,eAAe4C,wBAAwB,EACrCtF,SAASkC,WAAW,EACpBU,WAAW,EACXvC,WAAW8B,MAAM,EACjBO,eAAe6C,cAAc,EACE;IAC/B,MAAMC,cAAc,IAAI7C,IAAIC;IAC5B,MAAM6C,aAAa;WAAIF;KAAe,CACnCG,MAAM,CAAC,CAACC,KAAO,CAACH,YAAYI,GAAG,CAACD,OAAOjG,UAAUiG,KACjDE,QAAQ;IAEX,MAAMC,UAAsB,EAAE;IAC9B,IAAK,IAAIC,IAAI,GAAGA,IAAIN,WAAWO,MAAM,EAAED,KAAKlG,+BAAgC;QAC1EiG,QAAQG,IAAI,CAACR,WAAWS,KAAK,CAACH,GAAGA,IAAIlG;IACvC;IAEA,KAAK,MAAMsG,SAASL,QAAS;QAC3B,MAAM,EAACM,OAAO,EAAC,GAAG,MAAMhH,0BAA0B;YAChDY,SAASkC;YACTU,aAAauD;YACb9F,WAAW8B;QACb;QAEA,MAAMkE,aAAgF,CAAC;QACvF,KAAK,MAAMC,QAAQF,QAAS;YAC1BC,UAAU,CAACC,KAAKX,EAAE,CAAC,GAAGW,KAAKC,MAAM;QACnC;QAEA,KAAK,MAAMZ,MAAMQ,MAAO;YACtB,qEAAqE;YACrE,iDAAiD;YACjD,IAAIE,UAAU,CAACV,GAAG,KAAK,aAAa;gBAClCH,YAAY1B,GAAG,CAAC6B;YAClB;QACF;IACF;IACAhF,OAAOkB,KAAK,CAAC2E,wBAAwB;IAErC,OAAO;QAAChB;IAAW;AACrB;AAEA,eAAetE;IACb,IAAIuF;IAEJ,IAAI;QACF,MAAM,EAAChF,MAAM,EAAEzB,SAASkC,WAAW,EAAE7B,WAAW8B,MAAM,EAAE3B,SAAS,EAAC,GAAG,MAAMa;QAC3E,MAAM,EAAC2D,OAAO,EAAEpC,WAAW,EAAEkC,YAAY,EAAEpC,aAAa,EAAC,GAAGtC,iBACxD,MAAM+E,iBAAiB/E,kBACvB,MAAM6B,mBAAmB;YAACjC,SAASkC;YAAa7B,WAAW8B;QAAM;QACrEsE,6BAA6BzB;QAC7B,MAAM,EAACQ,WAAW,EAAC,GAAG,MAAMF,wBAAwB;YAClDtF,SAASkC;YACTU;YACAvC,WAAW8B;YACXO;QACF;QAEA,MAAMgE,YAAY,CAAyCC,UACzDlF,OAAOmF,UAAU,CAACD;QAEpB,MAAME,oBAA4D,CAAC,EAAClB,EAAE,EAAC,GACrEtB,QAAQC,OAAO,CAACkB,YAAYI,GAAG,CAACD;QAElC,MAAMmB,WAAW,CAACC;YAChB,IAAIC,eAAe;YACnB,KAAK,MAAMC,UAAUF,QAAS;gBAC5B,IAAIE,OAAOhH,KAAK,KAAK,SAAS,OAAO;gBACrC,IAAIgH,OAAOhH,KAAK,KAAK,WAAW+G,eAAe;YACjD;YAEA,IAAIA,cAAc,OAAO;YACzB,OAAO;QACT;QAEA,IAAIE,iBAAiB;QAErB,MAAMC,WAAW,OAAOxD;YACtB,IAAIoD;YAEJ,IAAI;gBACF,MAAMK,UAAUC,OAAO;gBAEvB,MAAM,EAACC,gBAAgB,EAAC,GAAG,MAAMtI,oBAC/B,UACAuB;gBAGF,MAAMgH,SAAS,MAAMlD,QAAQmD,IAAI,CAAC;oBAChCF,iBAAiB;wBACf3D;wBACA8D,aAAa;wBACbf;wBACAG;wBACA3G;wBACAC;wBACAK;oBACF;oBACA,IAAI6D,QAAwB,CAACC,UAC3BoD,WAAW,IAAMpD,QAAQ8C,UAAU5H;iBAEtC;gBAED,IAAI+H,WAAWH,SAAS;oBACtB,MAAM,IAAI1G,MACR,CAAC,UAAU,EAAEiD,SAASI,GAAG,CAAC,4BAA4B,EAAEvE,4BAA4B,GAAG,CAAC;gBAE5F;gBAEAuH,UAAUQ,MACR,6BAA6B;iBAC5B7B,MAAM,CAAC,CAACuB;oBACP,MAAMU,cAAchI,WAAW,CAACsH,OAAOhH,KAAK,CAAC;oBAC7C,MAAM2H,iBACJjI,WAAW,CAACM,MAAkC,IAAIN,YAAYkI,IAAI;oBACpE,OAAOF,eAAeC;gBACxB;YACJ,EAAE,OAAOnD,KAAK;gBACZ,MAAMqD,eAAerD,eAAe/D,QAAQ+D,IAAIsD,OAAO,GAAG;gBAE1D,MAAMA,UAAU,CAAC,2CAA2C,EAAED,cAAc;gBAE5Ef,UAAU;oBACR;wBACE9G,OAAO;wBACP8H;wBACAxJ,MAAM,EAAE;oBACV;iBACD;YACH;YAEA2I;YAEA,MAAMc,YACJ1H,cACA,GAAGA,aAAa/B,KAAK+F,OAAO,CAC1B9D,UAAUuB,QAAQ,EAClB,CAAC,gBAAgB,EAAEkG,mBAAmBtE,SAASI,GAAG,EAAE,MAAM,EAAEkE,mBAC1DtE,SAASuE,KAAK,GACb,GACF;YAELvH,OAAOuD,MAAM,CAACiE,UAAU,CAAC/D,IAAI,CAAC;gBAC5BgE,YAAYzE,SAASI,GAAG;gBACxBsE,cAAc1E,SAASuE,KAAK;gBAC5B,GAAIF,aAAa;oBAACA;gBAAS,CAAC;gBAC5B/H,OAAO6G,SAASC;gBAChBA;gBACAuB,UAAU3E,SAAS4E,IAAI;gBACvBrB;YACF;QACF;QAEA,MAAM/H,KAAK2F,gBAAgBqC,UAAU;YAACqB,aAAa5I;QAA0B;QAE7Ee,OAAOuD,MAAM,CAACiE,UAAU,CAACzD,GAAG;IAC9B,SAAU;QACR,MAAM+B;IACR;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/documents/validateDocuments.worker.ts"],"sourcesContent":["import fs from 'node:fs'\nimport os from 'node:os'\nimport path from 'node:path'\nimport readline from 'node:readline'\nimport {Readable} from 'node:stream'\nimport {workerData as _workerData, isMainThread, parentPort} from 'node:worker_threads'\n\nimport {getStudioWorkspaces, getWorkspace, resolveLocalPackage} from '@sanity/cli-core'\nimport {type ClientConfig, createClient, type SanityDocument} from '@sanity/client'\nimport {type ValidationContext, type ValidationMarker} from '@sanity/types'\nimport {WorkerChannelReporter} from '@sanity/worker-channels'\nimport pMap from 'p-map'\n\nimport {\n type AvailabilityResponse,\n checkDocumentAvailability,\n exportDocuments,\n getDocumentCount,\n} from '../../services/documents.js'\nimport {extractDocumentsFromNdjsonOrTarball} from '../../util/extractDocumentsFromNdjsonOrTarball.js'\nimport {\n DOCUMENT_VALIDATION_TIMEOUT,\n getReferenceIds,\n isValidId,\n levelValues,\n MAX_VALIDATION_CONCURRENCY,\n REFERENCE_INTEGRITY_BATCH_SIZE,\n shouldIncludeDocument,\n} from '../../util/validation/validateDocumentsUtils.js'\nimport {type ValidateDocumentsWorkerData, type ValidationWorkerChannel} from './types.js'\n\nconst {\n clientConfig,\n dataset,\n level,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n ndjsonFilePath,\n projectId,\n studioHost,\n workDir,\n workspace: workspaceName,\n} = _workerData as ValidateDocumentsWorkerData\n\nif (isMainThread || !parentPort) {\n throw new Error('This module must be run as a worker thread')\n}\n\nconst report = WorkerChannelReporter.from<ValidationWorkerChannel>(parentPort)\n\n// eslint-disable-next-line n/no-unsupported-features/node-builtins\nasync function* readerToGenerator(reader: ReadableStreamDefaultReader<Uint8Array>) {\n while (true) {\n const {done, value} = await reader.read()\n if (value) yield value\n if (done) return\n }\n}\n\nawait main()\nprocess.exit()\n\nasync function loadWorkspace() {\n const workspaces = await getStudioWorkspaces(workDir)\n const workspace = getWorkspace(workspaces, workspaceName)\n\n const resolvedDataset = dataset || workspace.dataset\n const resolvedProjectId = projectId || workspace.projectId\n\n const client = createClient({\n ...clientConfig,\n dataset: resolvedDataset,\n projectId: resolvedProjectId,\n requestTagPrefix: 'sanity.cli.validate',\n }).config({apiVersion: 'v2021-03-25'})\n\n report.event.loadedWorkspace({\n basePath: workspace.basePath,\n dataset: workspace.dataset,\n name: workspace.name,\n projectId: workspace.projectId,\n })\n\n return {client, dataset: resolvedDataset, projectId: resolvedProjectId, workspace}\n}\n\ninterface DownloadFromExportOptions {\n dataset: string\n projectId: string\n}\n\nasync function downloadFromExport({\n dataset: datasetName,\n projectId: projId,\n}: DownloadFromExportOptions) {\n const documentCount = await getDocumentCount({dataset: datasetName, projectId: projId})\n report.event.loadedDocumentCount({documentCount})\n\n const response = await exportDocuments({dataset: datasetName, projectId: projId})\n\n const reader = response.body?.getReader()\n if (!reader) throw new Error('Could not get reader from response body.')\n\n let downloadedCount = 0\n const referencedIds = new Set<string>()\n const documentIds = new Set<string>()\n const lines = readline.createInterface({input: Readable.from(readerToGenerator(reader))})\n\n // Note: we stream the export to a file and then re-read from that file to\n // make this less memory intensive.\n // this is a similar pattern to the import/export CLI commands\n const slugDate = new Date()\n .toISOString()\n .replaceAll(/[^a-z0-9]/gi, '-')\n .toLowerCase()\n const tempOutputFile = path.join(os.tmpdir(), `sanity-validate-${slugDate}.ndjson`)\n const outputStream = fs.createWriteStream(tempOutputFile)\n\n for await (const line of lines) {\n const document = JSON.parse(line) as SanityDocument\n\n if (shouldIncludeDocument(document)) {\n documentIds.add(document._id)\n for (const referenceId of getReferenceIds(document)) {\n referencedIds.add(referenceId)\n }\n\n outputStream.write(`${line}\\n`)\n }\n\n downloadedCount++\n report.stream.exportProgress.emit({documentCount, downloadedCount})\n }\n\n await new Promise<void>((resolve, reject) =>\n outputStream.close((err) => (err ? reject(err) : resolve())),\n )\n\n report.stream.exportProgress.end()\n report.event.exportFinished({totalDocumentsToValidate: documentIds.size})\n\n const getDocuments = () =>\n extractDocumentsFromNdjsonOrTarball(fs.createReadStream(tempOutputFile))\n\n return {cleanup: () => fs.promises.rm(tempOutputFile), documentIds, getDocuments, referencedIds}\n}\n\nasync function downloadFromFile(filePath: string) {\n const referencedIds = new Set<string>()\n const documentIds = new Set<string>()\n const getDocuments = () => extractDocumentsFromNdjsonOrTarball(fs.createReadStream(filePath))\n\n for await (const document of getDocuments()) {\n if (shouldIncludeDocument(document)) {\n documentIds.add(document._id)\n for (const referenceId of getReferenceIds(document)) {\n referencedIds.add(referenceId)\n }\n }\n }\n\n report.event.exportFinished({totalDocumentsToValidate: documentIds.size})\n\n return {cleanup: undefined, documentIds, getDocuments, referencedIds}\n}\n\ninterface CheckReferenceExistenceOptions {\n dataset: string\n documentIds: Set<string>\n projectId: string\n referencedIds: Set<string>\n}\n\nasync function checkReferenceExistence({\n dataset: datasetName,\n documentIds,\n projectId: projId,\n referencedIds: _referencedIds,\n}: CheckReferenceExistenceOptions) {\n const existingIds = new Set(documentIds)\n const idsToCheck = [..._referencedIds]\n .filter((id) => !existingIds.has(id) && isValidId(id))\n .toSorted()\n\n const batches: string[][] = []\n for (let i = 0; i < idsToCheck.length; i += REFERENCE_INTEGRITY_BATCH_SIZE) {\n batches.push(idsToCheck.slice(i, i + REFERENCE_INTEGRITY_BATCH_SIZE))\n }\n\n for (const batch of batches) {\n const {omitted} = await checkDocumentAvailability({\n dataset: datasetName,\n documentIds: batch,\n projectId: projId,\n })\n\n const omittedIds: Record<string, AvailabilityResponse['omitted'][number]['reason']> = {}\n for (const item of omitted) {\n omittedIds[item.id] = item.reason\n }\n\n for (const id of batch) {\n // unless the document ID is in the `omitted` object explictly due to\n // the reason `'existence'`, then it should exist\n if (omittedIds[id] !== 'existence') {\n existingIds.add(id)\n }\n }\n }\n report.event.loadedReferenceIntegrity()\n\n return {existingIds}\n}\n\nasync function main() {\n let cleanupDownloadedDocuments: (() => Promise<void>) | undefined\n\n try {\n const {client, dataset: datasetName, projectId: projId, workspace} = await loadWorkspace()\n const {cleanup, documentIds, getDocuments, referencedIds} = ndjsonFilePath\n ? await downloadFromFile(ndjsonFilePath)\n : await downloadFromExport({dataset: datasetName, projectId: projId})\n cleanupDownloadedDocuments = cleanup\n const {existingIds} = await checkReferenceExistence({\n dataset: datasetName,\n documentIds,\n projectId: projId,\n referencedIds,\n })\n\n const getClient = <TOptions extends Partial<ClientConfig>>(options: TOptions) =>\n client.withConfig(options)\n\n const getDocumentExists: ValidationContext['getDocumentExists'] = ({id}) =>\n Promise.resolve(existingIds.has(id))\n\n const getLevel = (markers: ValidationMarker[]) => {\n let foundWarning = false\n for (const marker of markers) {\n if (marker.level === 'error') return 'error'\n if (marker.level === 'warning') foundWarning = true\n }\n\n if (foundWarning) return 'warning'\n return 'info'\n }\n\n let validatedCount = 0\n\n const validate = async (document: SanityDocument) => {\n let markers: ValidationMarker[]\n\n try {\n const timeout = Symbol('timeout')\n\n const {validateDocument} = await resolveLocalPackage<typeof import('sanity')>(\n 'sanity',\n workDir,\n )\n\n const result = await Promise.race([\n validateDocument({\n document,\n environment: 'cli',\n // The local `sanity` package types its client against its own\n // @sanity/client major; the runtime surface validation uses is\n // compatible across v7/v8.\n getClient: getClient as unknown as Parameters<typeof validateDocument>[0]['getClient'],\n getDocumentExists,\n maxCustomValidationConcurrency,\n maxFetchConcurrency,\n workspace,\n }),\n new Promise<typeof timeout>((resolve) =>\n setTimeout(() => resolve(timeout), DOCUMENT_VALIDATION_TIMEOUT),\n ),\n ])\n\n if (result === timeout) {\n throw new Error(\n `Document '${document._id}' failed to validate within ${DOCUMENT_VALIDATION_TIMEOUT}ms.`,\n )\n }\n\n markers = result\n // filter out unwanted levels\n .filter((marker) => {\n const markerValue = levelValues[marker.level]\n const flagLevelValue =\n levelValues[level as keyof typeof levelValues] ?? levelValues.info\n return markerValue <= flagLevelValue\n })\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : 'Unknown error'\n\n const message = `Exception occurred while validating value: ${errorMessage}`\n\n markers = [\n {\n level: 'error',\n message,\n path: [],\n },\n ]\n }\n\n validatedCount++\n\n const intentUrl =\n studioHost &&\n `${studioHost}${path.resolve(\n workspace.basePath,\n `/intent/edit/id=${encodeURIComponent(document._id)};type=${encodeURIComponent(\n document._type,\n )}`,\n )}`\n\n report.stream.validation.emit({\n documentId: document._id,\n documentType: document._type,\n ...(intentUrl && {intentUrl}),\n level: getLevel(markers),\n markers,\n revision: document._rev,\n validatedCount,\n })\n }\n\n await pMap(getDocuments(), validate, {concurrency: MAX_VALIDATION_CONCURRENCY})\n\n report.stream.validation.end()\n } finally {\n await cleanupDownloadedDocuments?.()\n }\n}\n"],"names":["fs","os","path","readline","Readable","workerData","_workerData","isMainThread","parentPort","getStudioWorkspaces","getWorkspace","resolveLocalPackage","createClient","WorkerChannelReporter","pMap","checkDocumentAvailability","exportDocuments","getDocumentCount","extractDocumentsFromNdjsonOrTarball","DOCUMENT_VALIDATION_TIMEOUT","getReferenceIds","isValidId","levelValues","MAX_VALIDATION_CONCURRENCY","REFERENCE_INTEGRITY_BATCH_SIZE","shouldIncludeDocument","clientConfig","dataset","level","maxCustomValidationConcurrency","maxFetchConcurrency","ndjsonFilePath","projectId","studioHost","workDir","workspace","workspaceName","Error","report","from","readerToGenerator","reader","done","value","read","main","process","exit","loadWorkspace","workspaces","resolvedDataset","resolvedProjectId","client","requestTagPrefix","config","apiVersion","event","loadedWorkspace","basePath","name","downloadFromExport","datasetName","projId","documentCount","loadedDocumentCount","response","body","getReader","downloadedCount","referencedIds","Set","documentIds","lines","createInterface","input","slugDate","Date","toISOString","replaceAll","toLowerCase","tempOutputFile","join","tmpdir","outputStream","createWriteStream","line","document","JSON","parse","add","_id","referenceId","write","stream","exportProgress","emit","Promise","resolve","reject","close","err","end","exportFinished","totalDocumentsToValidate","size","getDocuments","createReadStream","cleanup","promises","rm","downloadFromFile","filePath","undefined","checkReferenceExistence","_referencedIds","existingIds","idsToCheck","filter","id","has","toSorted","batches","i","length","push","slice","batch","omitted","omittedIds","item","reason","loadedReferenceIntegrity","cleanupDownloadedDocuments","getClient","options","withConfig","getDocumentExists","getLevel","markers","foundWarning","marker","validatedCount","validate","timeout","Symbol","validateDocument","result","race","environment","setTimeout","markerValue","flagLevelValue","info","errorMessage","message","intentUrl","encodeURIComponent","_type","validation","documentId","documentType","revision","_rev","concurrency"],"mappings":"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"}
@@ -7,7 +7,7 @@ export const sdkAppDependencies = {
7
7
  'react-dom': '^19.2.4'
8
8
  },
9
9
  devDependencies: {
10
- '@sanity/eslint-config-studio': 'latest',
10
+ '@sanity/eslint-config-studio': '^6',
11
11
  '@types/react': '^19.2.14',
12
12
  eslint: '^9.28',
13
13
  prettier: '^3.5',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/sdkAppDependencies.ts"],"sourcesContent":["export const sdkAppDependencies = {\n dependencies: {\n // change these to 'latest' as in studioDependencies.ts once SDK v3 is released\n '@sanity/sdk': '^2',\n '@sanity/sdk-react': '^2',\n react: '^19.2.4',\n 'react-dom': '^19.2.4',\n },\n\n devDependencies: {\n '@sanity/eslint-config-studio': 'latest',\n '@types/react': '^19.2.14',\n eslint: '^9.28',\n prettier: '^3.5',\n sanity: 'latest',\n typescript: '^5.8', // Peer dependency of eslint-config-studio (implicitly)\n },\n}\n"],"names":["sdkAppDependencies","dependencies","react","devDependencies","eslint","prettier","sanity","typescript"],"mappings":"AAAA,OAAO,MAAMA,qBAAqB;IAChCC,cAAc;QACZ,+EAA+E;QAC/E,eAAe;QACf,qBAAqB;QACrBC,OAAO;QACP,aAAa;IACf;IAEAC,iBAAiB;QACf,gCAAgC;QAChC,gBAAgB;QAChBC,QAAQ;QACRC,UAAU;QACVC,QAAQ;QACRC,YAAY;IACd;AACF,EAAC"}
1
+ {"version":3,"sources":["../../../src/actions/init/sdkAppDependencies.ts"],"sourcesContent":["export const sdkAppDependencies = {\n dependencies: {\n // change these to 'latest' as in studioDependencies.ts once SDK v3 is released\n '@sanity/sdk': '^2',\n '@sanity/sdk-react': '^2',\n react: '^19.2.4',\n 'react-dom': '^19.2.4',\n },\n\n devDependencies: {\n '@sanity/eslint-config-studio': '^6',\n '@types/react': '^19.2.14',\n eslint: '^9.28',\n prettier: '^3.5',\n sanity: 'latest',\n typescript: '^5.8', // Peer dependency of eslint-config-studio (implicitly)\n },\n}\n"],"names":["sdkAppDependencies","dependencies","react","devDependencies","eslint","prettier","sanity","typescript"],"mappings":"AAAA,OAAO,MAAMA,qBAAqB;IAChCC,cAAc;QACZ,+EAA+E;QAC/E,eAAe;QACf,qBAAqB;QACrBC,OAAO;QACP,aAAa;IACf;IAEAC,iBAAiB;QACf,gCAAgC;QAChC,gBAAgB;QAChBC,QAAQ;QACRC,UAAU;QACVC,QAAQ;QACRC,YAAY;IACd;AACF,EAAC"}
@@ -12,7 +12,7 @@ export const studioDependencies = {
12
12
  },
13
13
  devDependencies: {
14
14
  // Linting/tooling
15
- '@sanity/eslint-config-studio': 'latest',
15
+ '@sanity/eslint-config-studio': '^6',
16
16
  // When using typescript, we'll want the these types too, so might as well install them
17
17
  '@types/react': '^19.2.14',
18
18
  eslint: '^9.28',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/init/studioDependencies.ts"],"sourcesContent":["export const studioDependencies = {\n // Dependencies for a default Sanity installation\n dependencies: {\n // Official studio dependencies\n sanity: 'latest',\n\n // Official studio plugin dependencies\n '@sanity/vision': 'latest',\n\n // Non-Sanity dependencies\n react: '^19.2.4',\n 'react-dom': '^19.2.4',\n 'styled-components': '^6.1.18',\n },\n\n devDependencies: {\n // Linting/tooling\n '@sanity/eslint-config-studio': 'latest',\n // When using typescript, we'll want the these types too, so might as well install them\n '@types/react': '^19.2.14',\n eslint: '^9.28',\n prettier: '^3.5',\n typescript: '^5.8', // Peer dependency of eslint-config-studio (implicitly)\n },\n}\n"],"names":["studioDependencies","dependencies","sanity","react","devDependencies","eslint","prettier","typescript"],"mappings":"AAAA,OAAO,MAAMA,qBAAqB;IAChC,iDAAiD;IACjDC,cAAc;QACZ,+BAA+B;QAC/BC,QAAQ;QAER,sCAAsC;QACtC,kBAAkB;QAElB,0BAA0B;QAC1BC,OAAO;QACP,aAAa;QACb,qBAAqB;IACvB;IAEAC,iBAAiB;QACf,kBAAkB;QAClB,gCAAgC;QAChC,uFAAuF;QACvF,gBAAgB;QAChBC,QAAQ;QACRC,UAAU;QACVC,YAAY;IACd;AACF,EAAC"}
1
+ {"version":3,"sources":["../../../src/actions/init/studioDependencies.ts"],"sourcesContent":["export const studioDependencies = {\n // Dependencies for a default Sanity installation\n dependencies: {\n // Official studio dependencies\n sanity: 'latest',\n\n // Official studio plugin dependencies\n '@sanity/vision': 'latest',\n\n // Non-Sanity dependencies\n react: '^19.2.4',\n 'react-dom': '^19.2.4',\n 'styled-components': '^6.1.18',\n },\n\n devDependencies: {\n // Linting/tooling\n '@sanity/eslint-config-studio': '^6',\n // When using typescript, we'll want the these types too, so might as well install them\n '@types/react': '^19.2.14',\n eslint: '^9.28',\n prettier: '^3.5',\n typescript: '^5.8', // Peer dependency of eslint-config-studio (implicitly)\n },\n}\n"],"names":["studioDependencies","dependencies","sanity","react","devDependencies","eslint","prettier","typescript"],"mappings":"AAAA,OAAO,MAAMA,qBAAqB;IAChC,iDAAiD;IACjDC,cAAc;QACZ,+BAA+B;QAC/BC,QAAQ;QAER,sCAAsC;QACtC,kBAAkB;QAElB,0BAA0B;QAC1BC,OAAO;QACP,aAAa;QACb,qBAAqB;IACvB;IAEAC,iBAAiB;QACf,kBAAkB;QAClB,gCAAgC;QAChC,uFAAuF;QACvF,gBAAgB;QAChBC,QAAQ;QACRC,UAAU;QACVC,YAAY;IACd;AACF,EAAC"}
@@ -37,6 +37,9 @@ const debug = subdebug('uploadSchemaToLexicon');
37
37
  dataset: workspace.dataset,
38
38
  projectId: workspace.projectId
39
39
  });
40
+ // The local `sanity` package types its client against its own
41
+ // @sanity/client major; the runtime surface uploadSchema uses is
42
+ // compatible across v7/v8.
40
43
  const descriptorId = await uploadSchema(workspace.schema, workspaceClient);
41
44
  if (!descriptorId) {
42
45
  throw new Error(`Failed to get schema descriptor ID for workspace "${workspace.name}": upload returned empty result`);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/schema/uploadSchemaToLexicon.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {ux} from '@oclif/core/ux'\nimport {\n doImport,\n getLocalPackageVersion,\n getProjectCliClient,\n resolveLocalPackage,\n subdebug,\n} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {type StudioManifest, type Workspace} from 'sanity'\n\nimport {SCHEMA_API_VERSION} from '../../services/schemas.js'\n\nconst iconResolverPath = new URL('../manifest/iconResolver.js', import.meta.url).href\n\ninterface UploadSchemaToLexiconOptions {\n projectId: string\n workDir: string\n workspaces: Workspace[]\n\n verbose?: boolean\n}\n\nconst debug = subdebug('uploadSchemaToLexicon')\n\n/**\n * Uploads the schemas to Lexicon and returns the studio manifest\n * @param options - The options for the uploadSchemaToLexicon function\n * @returns The studio manifest\n */\nexport async function uploadSchemaToLexicon(\n options: UploadSchemaToLexiconOptions,\n): Promise<StudioManifest | null> {\n const {projectId, verbose, workDir, workspaces} = options\n const spin = spinner('Generating studio manifest').start()\n\n try {\n const schemaDescriptors = new Map<string, string>()\n\n const client = await getProjectCliClient({\n apiVersion: SCHEMA_API_VERSION,\n projectId,\n requestTagPrefix: 'sanity.cli.deploy',\n requireUser: true,\n })\n\n const [bundleVersion, {generateStudioManifest, uploadSchema}] = await Promise.all([\n getLocalPackageVersion('sanity', workDir),\n resolveLocalPackage<typeof import('sanity')>('sanity', workDir),\n ])\n\n if (!bundleVersion) {\n throw new Error('Failed to find sanity version')\n }\n\n for (const workspace of workspaces) {\n const workspaceClient = client.withConfig({\n dataset: workspace.dataset,\n projectId: workspace.projectId,\n })\n\n try {\n debug('Uploading schema to lexicon for workspace %o', {\n dataset: workspace.dataset,\n projectId: workspace.projectId,\n })\n const descriptorId = await uploadSchema(workspace.schema, workspaceClient)\n\n if (!descriptorId) {\n throw new Error(\n `Failed to get schema descriptor ID for workspace \"${workspace.name}\": upload returned empty result`,\n )\n }\n\n schemaDescriptors.set(workspace.name, descriptorId)\n debug(\n `Uploaded schema for workspace \"${workspace.name}\" to Lexicon with descriptor ID: ${descriptorId}`,\n )\n } catch (error) {\n debug('Error uploading schema to lexicon for workspace %o', error)\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n throw new Error(\n `Failed to upload schema for workspace \"${workspace.name}\": ${errorMessage}`,\n {cause: error},\n )\n }\n }\n\n // Lazy import to avoid pulling in @sanity/ui at module load time\n const {resolveIcon} = await doImport(iconResolverPath)\n\n // Generate studio manifest using the shared utility\n const manifest = await generateStudioManifest({\n buildId: JSON.stringify(Date.now()),\n bundleVersion,\n // @todo replace with import from @sanity/schema/_internal in future\n resolveIcon: async (workspace) =>\n (await resolveIcon({\n icon: workspace.icon,\n subtitle: workspace.subtitle,\n title: workspace.title || workspace.name || 'default',\n workDir,\n })) ?? undefined,\n resolveSchemaDescriptorId: (workspace) => schemaDescriptors.get(workspace.name),\n workspaces,\n })\n\n spin.succeed('Generated studio manifest')\n\n const studioManifest = manifest.workspaces.length === 0 ? null : manifest\n\n if (verbose) {\n if (studioManifest) {\n for (const workspace of studioManifest.workspaces) {\n ux.stdout(\n styleText(\n 'gray',\n `↳ projectId: ${workspace.projectId}, dataset: ${workspace.dataset}, schemaDescriptorId: ${workspace.schemaDescriptorId}`,\n ),\n )\n }\n } else {\n ux.stdout(`${styleText('gray', '↳ No workspaces found')}`)\n }\n }\n\n return studioManifest\n } catch (error) {\n spin.fail(error instanceof Error ? error.message : 'Unknown error')\n throw error\n }\n}\n"],"names":["styleText","ux","doImport","getLocalPackageVersion","getProjectCliClient","resolveLocalPackage","subdebug","spinner","SCHEMA_API_VERSION","iconResolverPath","URL","url","href","debug","uploadSchemaToLexicon","options","projectId","verbose","workDir","workspaces","spin","start","schemaDescriptors","Map","client","apiVersion","requestTagPrefix","requireUser","bundleVersion","generateStudioManifest","uploadSchema","Promise","all","Error","workspace","workspaceClient","withConfig","dataset","descriptorId","schema","name","set","error","errorMessage","message","cause","resolveIcon","manifest","buildId","JSON","stringify","Date","now","icon","subtitle","title","undefined","resolveSchemaDescriptorId","get","succeed","studioManifest","length","stdout","schemaDescriptorId","fail"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/actions/schema/uploadSchemaToLexicon.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {ux} from '@oclif/core/ux'\nimport {\n doImport,\n getLocalPackageVersion,\n getProjectCliClient,\n resolveLocalPackage,\n subdebug,\n} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {type StudioManifest, type Workspace} from 'sanity'\n\nimport {SCHEMA_API_VERSION} from '../../services/schemas.js'\n\nconst iconResolverPath = new URL('../manifest/iconResolver.js', import.meta.url).href\n\ninterface UploadSchemaToLexiconOptions {\n projectId: string\n workDir: string\n workspaces: Workspace[]\n\n verbose?: boolean\n}\n\nconst debug = subdebug('uploadSchemaToLexicon')\n\n/**\n * Uploads the schemas to Lexicon and returns the studio manifest\n * @param options - The options for the uploadSchemaToLexicon function\n * @returns The studio manifest\n */\nexport async function uploadSchemaToLexicon(\n options: UploadSchemaToLexiconOptions,\n): Promise<StudioManifest | null> {\n const {projectId, verbose, workDir, workspaces} = options\n const spin = spinner('Generating studio manifest').start()\n\n try {\n const schemaDescriptors = new Map<string, string>()\n\n const client = await getProjectCliClient({\n apiVersion: SCHEMA_API_VERSION,\n projectId,\n requestTagPrefix: 'sanity.cli.deploy',\n requireUser: true,\n })\n\n const [bundleVersion, {generateStudioManifest, uploadSchema}] = await Promise.all([\n getLocalPackageVersion('sanity', workDir),\n resolveLocalPackage<typeof import('sanity')>('sanity', workDir),\n ])\n\n if (!bundleVersion) {\n throw new Error('Failed to find sanity version')\n }\n\n for (const workspace of workspaces) {\n const workspaceClient = client.withConfig({\n dataset: workspace.dataset,\n projectId: workspace.projectId,\n })\n\n try {\n debug('Uploading schema to lexicon for workspace %o', {\n dataset: workspace.dataset,\n projectId: workspace.projectId,\n })\n // The local `sanity` package types its client against its own\n // @sanity/client major; the runtime surface uploadSchema uses is\n // compatible across v7/v8.\n const descriptorId = await uploadSchema(\n workspace.schema,\n workspaceClient as unknown as Parameters<typeof uploadSchema>[1],\n )\n\n if (!descriptorId) {\n throw new Error(\n `Failed to get schema descriptor ID for workspace \"${workspace.name}\": upload returned empty result`,\n )\n }\n\n schemaDescriptors.set(workspace.name, descriptorId)\n debug(\n `Uploaded schema for workspace \"${workspace.name}\" to Lexicon with descriptor ID: ${descriptorId}`,\n )\n } catch (error) {\n debug('Error uploading schema to lexicon for workspace %o', error)\n const errorMessage = error instanceof Error ? error.message : 'Unknown error'\n throw new Error(\n `Failed to upload schema for workspace \"${workspace.name}\": ${errorMessage}`,\n {cause: error},\n )\n }\n }\n\n // Lazy import to avoid pulling in @sanity/ui at module load time\n const {resolveIcon} = await doImport(iconResolverPath)\n\n // Generate studio manifest using the shared utility\n const manifest = await generateStudioManifest({\n buildId: JSON.stringify(Date.now()),\n bundleVersion,\n // @todo replace with import from @sanity/schema/_internal in future\n resolveIcon: async (workspace) =>\n (await resolveIcon({\n icon: workspace.icon,\n subtitle: workspace.subtitle,\n title: workspace.title || workspace.name || 'default',\n workDir,\n })) ?? undefined,\n resolveSchemaDescriptorId: (workspace) => schemaDescriptors.get(workspace.name),\n workspaces,\n })\n\n spin.succeed('Generated studio manifest')\n\n const studioManifest = manifest.workspaces.length === 0 ? null : manifest\n\n if (verbose) {\n if (studioManifest) {\n for (const workspace of studioManifest.workspaces) {\n ux.stdout(\n styleText(\n 'gray',\n `↳ projectId: ${workspace.projectId}, dataset: ${workspace.dataset}, schemaDescriptorId: ${workspace.schemaDescriptorId}`,\n ),\n )\n }\n } else {\n ux.stdout(`${styleText('gray', '↳ No workspaces found')}`)\n }\n }\n\n return studioManifest\n } catch (error) {\n spin.fail(error instanceof Error ? error.message : 'Unknown error')\n throw error\n }\n}\n"],"names":["styleText","ux","doImport","getLocalPackageVersion","getProjectCliClient","resolveLocalPackage","subdebug","spinner","SCHEMA_API_VERSION","iconResolverPath","URL","url","href","debug","uploadSchemaToLexicon","options","projectId","verbose","workDir","workspaces","spin","start","schemaDescriptors","Map","client","apiVersion","requestTagPrefix","requireUser","bundleVersion","generateStudioManifest","uploadSchema","Promise","all","Error","workspace","workspaceClient","withConfig","dataset","descriptorId","schema","name","set","error","errorMessage","message","cause","resolveIcon","manifest","buildId","JSON","stringify","Date","now","icon","subtitle","title","undefined","resolveSchemaDescriptorId","get","succeed","studioManifest","length","stdout","schemaDescriptorId","fail"],"mappings":"AAAA,SAAQA,SAAS,QAAO,YAAW;AAEnC,SAAQC,EAAE,QAAO,iBAAgB;AACjC,SACEC,QAAQ,EACRC,sBAAsB,EACtBC,mBAAmB,EACnBC,mBAAmB,EACnBC,QAAQ,QACH,mBAAkB;AACzB,SAAQC,OAAO,QAAO,sBAAqB;AAG3C,SAAQC,kBAAkB,QAAO,4BAA2B;AAE5D,MAAMC,mBAAmB,IAAIC,IAAI,+BAA+B,YAAYC,GAAG,EAAEC,IAAI;AAUrF,MAAMC,QAAQP,SAAS;AAEvB;;;;CAIC,GACD,OAAO,eAAeQ,sBACpBC,OAAqC;IAErC,MAAM,EAACC,SAAS,EAAEC,OAAO,EAAEC,OAAO,EAAEC,UAAU,EAAC,GAAGJ;IAClD,MAAMK,OAAOb,QAAQ,8BAA8Bc,KAAK;IAExD,IAAI;QACF,MAAMC,oBAAoB,IAAIC;QAE9B,MAAMC,SAAS,MAAMpB,oBAAoB;YACvCqB,YAAYjB;YACZQ;YACAU,kBAAkB;YAClBC,aAAa;QACf;QAEA,MAAM,CAACC,eAAe,EAACC,sBAAsB,EAAEC,YAAY,EAAC,CAAC,GAAG,MAAMC,QAAQC,GAAG,CAAC;YAChF7B,uBAAuB,UAAUe;YACjCb,oBAA6C,UAAUa;SACxD;QAED,IAAI,CAACU,eAAe;YAClB,MAAM,IAAIK,MAAM;QAClB;QAEA,KAAK,MAAMC,aAAaf,WAAY;YAClC,MAAMgB,kBAAkBX,OAAOY,UAAU,CAAC;gBACxCC,SAASH,UAAUG,OAAO;gBAC1BrB,WAAWkB,UAAUlB,SAAS;YAChC;YAEA,IAAI;gBACFH,MAAM,gDAAgD;oBACpDwB,SAASH,UAAUG,OAAO;oBAC1BrB,WAAWkB,UAAUlB,SAAS;gBAChC;gBACA,8DAA8D;gBAC9D,iEAAiE;gBACjE,2BAA2B;gBAC3B,MAAMsB,eAAe,MAAMR,aACzBI,UAAUK,MAAM,EAChBJ;gBAGF,IAAI,CAACG,cAAc;oBACjB,MAAM,IAAIL,MACR,CAAC,kDAAkD,EAAEC,UAAUM,IAAI,CAAC,+BAA+B,CAAC;gBAExG;gBAEAlB,kBAAkBmB,GAAG,CAACP,UAAUM,IAAI,EAAEF;gBACtCzB,MACE,CAAC,+BAA+B,EAAEqB,UAAUM,IAAI,CAAC,iCAAiC,EAAEF,cAAc;YAEtG,EAAE,OAAOI,OAAO;gBACd7B,MAAM,sDAAsD6B;gBAC5D,MAAMC,eAAeD,iBAAiBT,QAAQS,MAAME,OAAO,GAAG;gBAC9D,MAAM,IAAIX,MACR,CAAC,uCAAuC,EAAEC,UAAUM,IAAI,CAAC,GAAG,EAAEG,cAAc,EAC5E;oBAACE,OAAOH;gBAAK;YAEjB;QACF;QAEA,iEAAiE;QACjE,MAAM,EAACI,WAAW,EAAC,GAAG,MAAM5C,SAASO;QAErC,oDAAoD;QACpD,MAAMsC,WAAW,MAAMlB,uBAAuB;YAC5CmB,SAASC,KAAKC,SAAS,CAACC,KAAKC,GAAG;YAChCxB;YACA,oEAAoE;YACpEkB,aAAa,OAAOZ,YAClB,AAAC,MAAMY,YAAY;oBACjBO,MAAMnB,UAAUmB,IAAI;oBACpBC,UAAUpB,UAAUoB,QAAQ;oBAC5BC,OAAOrB,UAAUqB,KAAK,IAAIrB,UAAUM,IAAI,IAAI;oBAC5CtB;gBACF,MAAOsC;YACTC,2BAA2B,CAACvB,YAAcZ,kBAAkBoC,GAAG,CAACxB,UAAUM,IAAI;YAC9ErB;QACF;QAEAC,KAAKuC,OAAO,CAAC;QAEb,MAAMC,iBAAiBb,SAAS5B,UAAU,CAAC0C,MAAM,KAAK,IAAI,OAAOd;QAEjE,IAAI9B,SAAS;YACX,IAAI2C,gBAAgB;gBAClB,KAAK,MAAM1B,aAAa0B,eAAezC,UAAU,CAAE;oBACjDlB,GAAG6D,MAAM,CACP9D,UACE,QACA,CAAC,aAAa,EAAEkC,UAAUlB,SAAS,CAAC,WAAW,EAAEkB,UAAUG,OAAO,CAAC,sBAAsB,EAAEH,UAAU6B,kBAAkB,EAAE;gBAG/H;YACF,OAAO;gBACL9D,GAAG6D,MAAM,CAAC,GAAG9D,UAAU,QAAQ,0BAA0B;YAC3D;QACF;QAEA,OAAO4D;IACT,EAAE,OAAOlB,OAAO;QACdtB,KAAK4C,IAAI,CAACtB,iBAAiBT,QAAQS,MAAME,OAAO,GAAG;QACnD,MAAMF;IACR;AACF"}
@@ -1,4 +1,4 @@
1
- import debugIt from 'debug';
2
- export const telemetryDebug = debugIt('telemetry:sanity:cli');
1
+ import { createDebug } from 'obug';
2
+ export const telemetryDebug = createDebug('telemetry:sanity:cli');
3
3
 
4
4
  //# sourceMappingURL=telemetryDebug.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/telemetry/telemetryDebug.ts"],"sourcesContent":["import debugIt from 'debug'\n\nexport const telemetryDebug = debugIt('telemetry:sanity:cli')\n"],"names":["debugIt","telemetryDebug"],"mappings":"AAAA,OAAOA,aAAa,QAAO;AAE3B,OAAO,MAAMC,iBAAiBD,QAAQ,wBAAuB"}
1
+ {"version":3,"sources":["../../../src/actions/telemetry/telemetryDebug.ts"],"sourcesContent":["import {createDebug} from 'obug'\n\nexport const telemetryDebug = createDebug('telemetry:sanity:cli')\n"],"names":["createDebug","telemetryDebug"],"mappings":"AAAA,SAAQA,WAAW,QAAO,OAAM;AAEhC,OAAO,MAAMC,iBAAiBD,YAAY,wBAAuB"}
@@ -1,12 +1,46 @@
1
1
  import { basename, resolve } from 'node:path';
2
2
  import { Flags } from '@oclif/core';
3
3
  import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
4
+ import { getErrorMessage } from '@sanity/cli-core/errors';
5
+ import { isHttpError } from '@sanity/client';
4
6
  import { AssetFileError } from '../../actions/assets/assetFileError.js';
5
7
  import { uploadAssetWithProgress } from '../../actions/assets/uploadAssetWithProgress.js';
6
8
  import { promptForProject } from '../../prompts/promptForProject.js';
7
9
  import { getDatasetFlag, getProjectIdFlag } from '../../util/sharedFlags.js';
8
10
  import { defineCommandTelemetry } from '../../util/telemetry/commandTelemetry.js';
9
11
  const uploadAssetDebug = subdebug('assets:upload');
12
+ const DATASET_ASSET_LIMITS_URL = 'https://www.sanity.io/docs/content-lake/technical-limits#k2c53dc30e24b';
13
+ function isProjectUserNotFoundError(body) {
14
+ const responseError = body.error;
15
+ return typeof responseError === 'object' && responseError !== null && 'type' in responseError && responseError.type === 'projectUserNotFoundError';
16
+ }
17
+ function getAssetUploadErrorMessage(error) {
18
+ if (!isHttpError(error)) {
19
+ return `Asset upload failed: ${getErrorMessage(error)}`;
20
+ }
21
+ const body = typeof error.response.body === 'object' && error.response.body !== null && !Array.isArray(error.response.body) ? error.response.body : {};
22
+ const responseError = typeof body.error === 'string' ? body.error : error.response.statusMessage || 'HTTP error';
23
+ const statusCode = typeof body.statusCode === 'number' || typeof body.statusCode === 'string' ? body.statusCode : error.statusCode;
24
+ const projectUserNotFound = isProjectUserNotFoundError(body);
25
+ const responseMessage = projectUserNotFound ? error.message : getErrorMessage(error);
26
+ const message = /[.!?]$/.test(responseMessage) ? responseMessage : `${responseMessage}.`;
27
+ const details = typeof body.details === 'string' ? `\n\nDetails:\n${body.details}` : '';
28
+ const response = `Asset upload failed: HTTP ${statusCode} - ${responseError}\n${message}${details}`;
29
+ if (error.statusCode === 401 && !projectUserNotFound) {
30
+ return `${response}\n\nRun \`sanity login\` to authenticate, then try again.`;
31
+ }
32
+ if (error.statusCode === 403) {
33
+ return `${response}\n\nCheck that your account has write access to this dataset, then try again.`;
34
+ }
35
+ if ([
36
+ 400,
37
+ 413,
38
+ 422
39
+ ].includes(error.statusCode)) {
40
+ return `${response}\n\nCheck the asset requirements and current technical limits, then try again: ${DATASET_ASSET_LIMITS_URL}`;
41
+ }
42
+ return `${response}\n\nTry again.`;
43
+ }
10
44
  const flags = {
11
45
  ...getProjectIdFlag({
12
46
  description: 'Project ID to upload the asset to',
@@ -116,7 +150,7 @@ export class UploadAssetCommand extends SanityCommand {
116
150
  });
117
151
  }
118
152
  uploadAssetDebug('Asset upload failed', error);
119
- this.error('Asset upload failed. Check authentication, write access, and that the local file is still readable, then retry.', {
153
+ this.error(getAssetUploadErrorMessage(error), {
120
154
  exit: exitCodes.RUNTIME_ERROR
121
155
  });
122
156
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/assets/upload.ts"],"sourcesContent":["import {basename, resolve} from 'node:path'\n\nimport {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\n\nimport {AssetFileError} from '../../actions/assets/assetFileError.js'\nimport {uploadAssetWithProgress} from '../../actions/assets/uploadAssetWithProgress.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type AssetType} from '../../services/assets.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../util/telemetry/commandTelemetry.js'\n\nconst uploadAssetDebug = subdebug('assets:upload')\n\nconst flags = {\n ...getProjectIdFlag({\n description: 'Project ID to upload the asset to',\n semantics: 'override',\n }),\n ...getDatasetFlag({description: 'Dataset to upload the asset to', semantics: 'override'}),\n 'content-type': Flags.string({\n description: 'MIME type of the asset, such as image/png or application/pdf',\n helpValue: '<mime-type>',\n }),\n file: Flags.string({\n description: 'Path to the local file to upload',\n helpValue: '<path>',\n required: true,\n }),\n filename: Flags.string({\n description: 'Original filename stored on the asset document. Defaults to the local filename',\n helpValue: '<filename>',\n }),\n type: Flags.custom<AssetType>({\n default: 'image',\n description: 'Asset type to create',\n options: ['image', 'file'],\n })(),\n} satisfies FlagInput\n\nexport class UploadAssetCommand extends SanityCommand<typeof UploadAssetCommand> {\n static override description =\n 'Upload one local image or file to a Sanity dataset and print the asset document as JSON'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./hero.png --type image --dataset production',\n description: 'Upload an image using the configured project',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project-id abc123 --dataset production',\n description: 'Upload a file with explicit project, dataset, and MIME type',\n },\n ]\n\n static override flags = flags\n\n static override hiddenAliases: string[] = ['asset:upload']\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['file', 'filename'],\n })\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UploadAssetCommand)\n const filePath = resolve(flags.file)\n\n const cliConfig = await this.tryGetCliConfig()\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n const dataset = flags.dataset ?? cliConfig.api?.dataset\n if (!dataset) {\n this.error(\n 'Asset upload failed: Dataset is required. Pass --dataset <name> or configure a dataset in sanity.cli.ts.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n try {\n const asset = await uploadAssetWithProgress({\n assetType: flags.type,\n contentType: flags['content-type'],\n dataset,\n filename: flags.filename ?? basename(filePath),\n filePath,\n isInteractive: this.resolveIsInteractive(),\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n })\n const fieldType = flags.type === 'image' ? 'image' : 'file'\n\n this.log(\n JSON.stringify(\n {\n asset: {\n _id: asset._id,\n _type: asset._type,\n extension: asset.extension,\n mimeType: asset.mimeType,\n originalFilename: asset.originalFilename,\n size: asset.size,\n url: asset.url,\n },\n reference: {\n _type: fieldType,\n asset: {_ref: asset._id, _type: 'reference'},\n },\n },\n null,\n 2,\n ),\n )\n } catch (error) {\n if (error instanceof Error && error.message === 'SIGINT') throw error\n if (error instanceof AssetFileError) {\n if (error.reason === 'not-file') {\n this.error('Asset upload failed: --file must point to a file, not a directory.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n this.error(\n 'Asset upload failed: Cannot read the local file. Check that --file points to a readable file, then retry.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n uploadAssetDebug('Asset upload failed', error)\n this.error(\n 'Asset upload failed. Check authentication, write access, and that the local file is still readable, then retry.',\n {\n exit: exitCodes.RUNTIME_ERROR,\n },\n )\n }\n }\n}\n"],"names":["basename","resolve","Flags","exitCodes","SanityCommand","subdebug","AssetFileError","uploadAssetWithProgress","promptForProject","getDatasetFlag","getProjectIdFlag","defineCommandTelemetry","uploadAssetDebug","flags","description","semantics","string","helpValue","file","required","filename","type","custom","default","options","UploadAssetCommand","examples","command","hiddenAliases","telemetry","redact","run","parse","filePath","cliConfig","tryGetCliConfig","projectId","getProjectId","fallback","dataset","api","error","exit","USAGE_ERROR","asset","assetType","contentType","isInteractive","resolveIsInteractive","logToStderr","message","fieldType","log","JSON","stringify","_id","_type","extension","mimeType","originalFilename","size","url","reference","_ref","Error","reason","RUNTIME_ERROR"],"mappings":"AAAA,SAAQA,QAAQ,EAAEC,OAAO,QAAO,YAAW;AAE3C,SAAQC,KAAK,QAAO,cAAa;AAEjC,SAAQC,SAAS,EAAEC,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AAEnE,SAAQC,cAAc,QAAO,yCAAwC;AACrE,SAAQC,uBAAuB,QAAO,kDAAiD;AACvF,SAAQC,gBAAgB,QAAO,oCAAmC;AAElE,SAAQC,cAAc,EAAEC,gBAAgB,QAAO,4BAA2B;AAC1E,SAAQC,sBAAsB,QAAO,2CAA0C;AAE/E,MAAMC,mBAAmBP,SAAS;AAElC,MAAMQ,QAAQ;IACZ,GAAGH,iBAAiB;QAClBI,aAAa;QACbC,WAAW;IACb,EAAE;IACF,GAAGN,eAAe;QAACK,aAAa;QAAkCC,WAAW;IAAU,EAAE;IACzF,gBAAgBb,MAAMc,MAAM,CAAC;QAC3BF,aAAa;QACbG,WAAW;IACb;IACAC,MAAMhB,MAAMc,MAAM,CAAC;QACjBF,aAAa;QACbG,WAAW;QACXE,UAAU;IACZ;IACAC,UAAUlB,MAAMc,MAAM,CAAC;QACrBF,aAAa;QACbG,WAAW;IACb;IACAI,MAAMnB,MAAMoB,MAAM,CAAY;QAC5BC,SAAS;QACTT,aAAa;QACbU,SAAS;YAAC;YAAS;SAAO;IAC5B;AACF;AAEA,OAAO,MAAMC,2BAA2BrB;IACtC,OAAgBU,cACd,0FAAyF;IAE3F,OAAgBY,WAAW;QACzB;YACEC,SACE;YACFb,aAAa;QACf;QACA;YACEa,SACE;YACFb,aAAa;QACf;KACD,CAAA;IAED,OAAgBD,QAAQA,MAAK;IAE7B,OAAgBe,gBAA0B;QAAC;KAAe,CAAA;IAE1D,OAAOC,YAAYlB,uBAAuBE,OAAO;QAC/CiB,QAAQ;YAAC;YAAQ;SAAW;IAC9B,GAAE;IAEF,MAAaC,MAAqB;QAChC,MAAM,EAAClB,KAAK,EAAC,GAAG,MAAM,IAAI,CAACmB,KAAK,CAACP;QACjC,MAAMQ,WAAWhC,QAAQY,MAAMK,IAAI;QAEnC,MAAMgB,YAAY,MAAM,IAAI,CAACC,eAAe;QAC5C,MAAMC,YAAY,MAAM,IAAI,CAACC,YAAY,CAAC;YAACC,UAAU,IAAM9B,iBAAiB,CAAC;QAAE;QAC/E,MAAM+B,UAAU1B,MAAM0B,OAAO,IAAIL,UAAUM,GAAG,EAAED;QAChD,IAAI,CAACA,SAAS;YACZ,IAAI,CAACE,KAAK,CACR,4GACA;gBAACC,MAAMvC,UAAUwC,WAAW;YAAA;QAEhC;QAEA,IAAI;YACF,MAAMC,QAAQ,MAAMrC,wBAAwB;gBAC1CsC,WAAWhC,MAAMQ,IAAI;gBACrByB,aAAajC,KAAK,CAAC,eAAe;gBAClC0B;gBACAnB,UAAUP,MAAMO,QAAQ,IAAIpB,SAASiC;gBACrCA;gBACAc,eAAe,IAAI,CAACC,oBAAoB;gBACxCC,aAAa,CAACC,UAAY,IAAI,CAACD,WAAW,CAACC;gBAC3Cd;YACF;YACA,MAAMe,YAAYtC,MAAMQ,IAAI,KAAK,UAAU,UAAU;YAErD,IAAI,CAAC+B,GAAG,CACNC,KAAKC,SAAS,CACZ;gBACEV,OAAO;oBACLW,KAAKX,MAAMW,GAAG;oBACdC,OAAOZ,MAAMY,KAAK;oBAClBC,WAAWb,MAAMa,SAAS;oBAC1BC,UAAUd,MAAMc,QAAQ;oBACxBC,kBAAkBf,MAAMe,gBAAgB;oBACxCC,MAAMhB,MAAMgB,IAAI;oBAChBC,KAAKjB,MAAMiB,GAAG;gBAChB;gBACAC,WAAW;oBACTN,OAAOL;oBACPP,OAAO;wBAACmB,MAAMnB,MAAMW,GAAG;wBAAEC,OAAO;oBAAW;gBAC7C;YACF,GACA,MACA;QAGN,EAAE,OAAOf,OAAO;YACd,IAAIA,iBAAiBuB,SAASvB,MAAMS,OAAO,KAAK,UAAU,MAAMT;YAChE,IAAIA,iBAAiBnC,gBAAgB;gBACnC,IAAImC,MAAMwB,MAAM,KAAK,YAAY;oBAC/B,IAAI,CAACxB,KAAK,CAAC,sEAAsE;wBAC/EC,MAAMvC,UAAUwC,WAAW;oBAC7B;gBACF;gBACA,IAAI,CAACF,KAAK,CACR,6GACA;oBAACC,MAAMvC,UAAUwC,WAAW;gBAAA;YAEhC;YACA/B,iBAAiB,uBAAuB6B;YACxC,IAAI,CAACA,KAAK,CACR,mHACA;gBACEC,MAAMvC,UAAU+D,aAAa;YAC/B;QAEJ;IACF;AACF"}
1
+ {"version":3,"sources":["../../../src/commands/assets/upload.ts"],"sourcesContent":["import {basename, resolve} from 'node:path'\n\nimport {Flags} from '@oclif/core'\nimport {type FlagInput} from '@oclif/core/interfaces'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {AssetFileError} from '../../actions/assets/assetFileError.js'\nimport {uploadAssetWithProgress} from '../../actions/assets/uploadAssetWithProgress.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {type AssetType} from '../../services/assets.js'\nimport {getDatasetFlag, getProjectIdFlag} from '../../util/sharedFlags.js'\nimport {defineCommandTelemetry} from '../../util/telemetry/commandTelemetry.js'\n\nconst uploadAssetDebug = subdebug('assets:upload')\nconst DATASET_ASSET_LIMITS_URL =\n 'https://www.sanity.io/docs/content-lake/technical-limits#k2c53dc30e24b'\n\nfunction isProjectUserNotFoundError(body: Record<string, unknown>): boolean {\n const responseError = body.error\n return (\n typeof responseError === 'object' &&\n responseError !== null &&\n 'type' in responseError &&\n responseError.type === 'projectUserNotFoundError'\n )\n}\n\nfunction getAssetUploadErrorMessage(error: unknown): string {\n if (!isHttpError(error)) {\n return `Asset upload failed: ${getErrorMessage(error)}`\n }\n\n const body =\n typeof error.response.body === 'object' &&\n error.response.body !== null &&\n !Array.isArray(error.response.body)\n ? (error.response.body as Record<string, unknown>)\n : {}\n const responseError =\n typeof body.error === 'string' ? body.error : error.response.statusMessage || 'HTTP error'\n const statusCode =\n typeof body.statusCode === 'number' || typeof body.statusCode === 'string'\n ? body.statusCode\n : error.statusCode\n const projectUserNotFound = isProjectUserNotFoundError(body)\n const responseMessage = projectUserNotFound ? error.message : getErrorMessage(error)\n const message = /[.!?]$/.test(responseMessage) ? responseMessage : `${responseMessage}.`\n const details = typeof body.details === 'string' ? `\\n\\nDetails:\\n${body.details}` : ''\n const response = `Asset upload failed: HTTP ${statusCode} - ${responseError}\\n${message}${details}`\n\n if (error.statusCode === 401 && !projectUserNotFound) {\n return `${response}\\n\\nRun \\`sanity login\\` to authenticate, then try again.`\n }\n if (error.statusCode === 403) {\n return `${response}\\n\\nCheck that your account has write access to this dataset, then try again.`\n }\n if ([400, 413, 422].includes(error.statusCode)) {\n return `${response}\\n\\nCheck the asset requirements and current technical limits, then try again: ${DATASET_ASSET_LIMITS_URL}`\n }\n return `${response}\\n\\nTry again.`\n}\n\nconst flags = {\n ...getProjectIdFlag({\n description: 'Project ID to upload the asset to',\n semantics: 'override',\n }),\n ...getDatasetFlag({description: 'Dataset to upload the asset to', semantics: 'override'}),\n 'content-type': Flags.string({\n description: 'MIME type of the asset, such as image/png or application/pdf',\n helpValue: '<mime-type>',\n }),\n file: Flags.string({\n description: 'Path to the local file to upload',\n helpValue: '<path>',\n required: true,\n }),\n filename: Flags.string({\n description: 'Original filename stored on the asset document. Defaults to the local filename',\n helpValue: '<filename>',\n }),\n type: Flags.custom<AssetType>({\n default: 'image',\n description: 'Asset type to create',\n options: ['image', 'file'],\n })(),\n} satisfies FlagInput\n\nexport class UploadAssetCommand extends SanityCommand<typeof UploadAssetCommand> {\n static override description =\n 'Upload one local image or file to a Sanity dataset and print the asset document as JSON'\n\n static override examples = [\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./hero.png --type image --dataset production',\n description: 'Upload an image using the configured project',\n },\n {\n command:\n '<%= config.bin %> <%= command.id %> --file ./brief.pdf --type file --content-type application/pdf --project-id abc123 --dataset production',\n description: 'Upload a file with explicit project, dataset, and MIME type',\n },\n ]\n\n static override flags = flags\n\n static override hiddenAliases: string[] = ['asset:upload']\n\n static telemetry = defineCommandTelemetry(flags, {\n redact: ['file', 'filename'],\n })\n\n public async run(): Promise<void> {\n const {flags} = await this.parse(UploadAssetCommand)\n const filePath = resolve(flags.file)\n\n const cliConfig = await this.tryGetCliConfig()\n const projectId = await this.getProjectId({fallback: () => promptForProject({})})\n const dataset = flags.dataset ?? cliConfig.api?.dataset\n if (!dataset) {\n this.error(\n 'Asset upload failed: Dataset is required. Pass --dataset <name> or configure a dataset in sanity.cli.ts.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n\n try {\n const asset = await uploadAssetWithProgress({\n assetType: flags.type,\n contentType: flags['content-type'],\n dataset,\n filename: flags.filename ?? basename(filePath),\n filePath,\n isInteractive: this.resolveIsInteractive(),\n logToStderr: (message) => this.logToStderr(message),\n projectId,\n })\n const fieldType = flags.type === 'image' ? 'image' : 'file'\n\n this.log(\n JSON.stringify(\n {\n asset: {\n _id: asset._id,\n _type: asset._type,\n extension: asset.extension,\n mimeType: asset.mimeType,\n originalFilename: asset.originalFilename,\n size: asset.size,\n url: asset.url,\n },\n reference: {\n _type: fieldType,\n asset: {_ref: asset._id, _type: 'reference'},\n },\n },\n null,\n 2,\n ),\n )\n } catch (error) {\n if (error instanceof Error && error.message === 'SIGINT') throw error\n if (error instanceof AssetFileError) {\n if (error.reason === 'not-file') {\n this.error('Asset upload failed: --file must point to a file, not a directory.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n this.error(\n 'Asset upload failed: Cannot read the local file. Check that --file points to a readable file, then retry.',\n {exit: exitCodes.USAGE_ERROR},\n )\n }\n uploadAssetDebug('Asset upload failed', error)\n this.error(getAssetUploadErrorMessage(error), {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n}\n"],"names":["basename","resolve","Flags","exitCodes","SanityCommand","subdebug","getErrorMessage","isHttpError","AssetFileError","uploadAssetWithProgress","promptForProject","getDatasetFlag","getProjectIdFlag","defineCommandTelemetry","uploadAssetDebug","DATASET_ASSET_LIMITS_URL","isProjectUserNotFoundError","body","responseError","error","type","getAssetUploadErrorMessage","response","Array","isArray","statusMessage","statusCode","projectUserNotFound","responseMessage","message","test","details","includes","flags","description","semantics","string","helpValue","file","required","filename","custom","default","options","UploadAssetCommand","examples","command","hiddenAliases","telemetry","redact","run","parse","filePath","cliConfig","tryGetCliConfig","projectId","getProjectId","fallback","dataset","api","exit","USAGE_ERROR","asset","assetType","contentType","isInteractive","resolveIsInteractive","logToStderr","fieldType","log","JSON","stringify","_id","_type","extension","mimeType","originalFilename","size","url","reference","_ref","Error","reason","RUNTIME_ERROR"],"mappings":"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"}