@sanity/cli 6.0.0-alpha.19 → 6.0.0-alpha.21

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 (229) hide show
  1. package/README.md +713 -533
  2. package/dist/actions/auth/authServer.js +13 -11
  3. package/dist/actions/auth/authServer.js.map +1 -1
  4. package/dist/actions/auth/login/getProvider.js +43 -39
  5. package/dist/actions/auth/login/getProvider.js.map +1 -1
  6. package/dist/actions/auth/login/getSSOProvider.js +25 -19
  7. package/dist/actions/auth/login/getSSOProvider.js.map +1 -1
  8. package/dist/actions/auth/login/login.js +12 -33
  9. package/dist/actions/auth/login/login.js.map +1 -1
  10. package/dist/actions/auth/types.js.map +1 -1
  11. package/dist/actions/codemods/reactIconsV3.js +2 -2
  12. package/dist/actions/codemods/reactIconsV3.js.map +1 -1
  13. package/dist/actions/debug/formatters.js +22 -0
  14. package/dist/actions/debug/formatters.js.map +1 -0
  15. package/dist/actions/dev/getDashboardAppUrl.js +3 -3
  16. package/dist/actions/dev/getDashboardAppUrl.js.map +1 -1
  17. package/dist/actions/documents/validateDocuments.worker.js +2 -2
  18. package/dist/actions/documents/validateDocuments.worker.js.map +1 -1
  19. package/dist/actions/documents/validation/reporters/prettyReporter/formatDocumentValidation.js +1 -1
  20. package/dist/actions/documents/validation/reporters/prettyReporter/formatDocumentValidation.js.map +1 -1
  21. package/dist/actions/documents/validation/reporters/prettyReporter/tree.js +108 -0
  22. package/dist/actions/documents/validation/reporters/prettyReporter/tree.js.map +1 -0
  23. package/dist/actions/graphql/SchemaError.js +4 -26
  24. package/dist/actions/graphql/SchemaError.js.map +1 -1
  25. package/dist/actions/graphql/extractFromSanitySchema.js +3 -4
  26. package/dist/actions/graphql/extractFromSanitySchema.js.map +1 -1
  27. package/dist/actions/graphql/extractGraphQLAPIs.js +150 -0
  28. package/dist/actions/graphql/extractGraphQLAPIs.js.map +1 -0
  29. package/dist/actions/graphql/extractGraphQLAPIs.worker.js +12 -0
  30. package/dist/actions/graphql/extractGraphQLAPIs.worker.js.map +1 -0
  31. package/dist/actions/graphql/gen1/index.js +5 -5
  32. package/dist/actions/graphql/gen1/index.js.map +1 -1
  33. package/dist/actions/graphql/gen2/index.js +6 -6
  34. package/dist/actions/graphql/gen2/index.js.map +1 -1
  35. package/dist/actions/graphql/gen3/generateTypeQueries.js +2 -3
  36. package/dist/actions/graphql/gen3/generateTypeQueries.js.map +1 -1
  37. package/dist/actions/graphql/gen3/index.js +6 -7
  38. package/dist/actions/graphql/gen3/index.js.map +1 -1
  39. package/dist/actions/graphql/getGraphQLAPIs.js +12 -35
  40. package/dist/actions/graphql/getGraphQLAPIs.js.map +1 -1
  41. package/dist/actions/graphql/getGraphQLAPIs.worker.js +75 -106
  42. package/dist/actions/graphql/getGraphQLAPIs.worker.js.map +1 -1
  43. package/dist/actions/graphql/helpers.js +13 -0
  44. package/dist/actions/graphql/helpers.js.map +1 -1
  45. package/dist/actions/graphql/resolveGraphQLApisFromWorkspaces.js +187 -0
  46. package/dist/actions/graphql/resolveGraphQLApisFromWorkspaces.js.map +1 -0
  47. package/dist/actions/graphql/types.js +1 -1
  48. package/dist/actions/graphql/types.js.map +1 -1
  49. package/dist/actions/init/bootstrapLocalTemplate.js +2 -1
  50. package/dist/actions/init/bootstrapLocalTemplate.js.map +1 -1
  51. package/dist/actions/init/bootstrapRemoteTemplate.js +6 -5
  52. package/dist/actions/init/bootstrapRemoteTemplate.js.map +1 -1
  53. package/dist/actions/init/createPackageManifest.js +5 -0
  54. package/dist/actions/init/createPackageManifest.js.map +1 -1
  55. package/dist/actions/init/templates/appQuickstart.js +2 -1
  56. package/dist/actions/init/templates/appQuickstart.js.map +1 -1
  57. package/dist/actions/init/templates/appSanityUi.js +2 -1
  58. package/dist/actions/init/templates/appSanityUi.js.map +1 -1
  59. package/dist/actions/init/types.js.map +1 -1
  60. package/dist/actions/media/importMedia.js +1 -2
  61. package/dist/actions/media/importMedia.js.map +1 -1
  62. package/dist/actions/projects/getManageUrl.js +1 -2
  63. package/dist/actions/projects/getManageUrl.js.map +1 -1
  64. package/dist/actions/schema/validateSchema.worker.js +1 -3
  65. package/dist/actions/schema/validateSchema.worker.js.map +1 -1
  66. package/dist/actions/telemetry/isTrueish.js +10 -0
  67. package/dist/actions/telemetry/isTrueish.js.map +1 -0
  68. package/dist/actions/telemetry/resolveConsent.js +2 -1
  69. package/dist/actions/telemetry/resolveConsent.js.map +1 -1
  70. package/dist/actions/telemetry/setConsent.js +2 -1
  71. package/dist/actions/telemetry/setConsent.js.map +1 -1
  72. package/dist/actions/versions/findSanityModulesVersions.js +1 -2
  73. package/dist/actions/versions/findSanityModulesVersions.js.map +1 -1
  74. package/dist/commands/backup/disable.js +21 -1
  75. package/dist/commands/backup/disable.js.map +1 -1
  76. package/dist/commands/backup/download.js +18 -4
  77. package/dist/commands/backup/download.js.map +1 -1
  78. package/dist/commands/backup/enable.js +21 -1
  79. package/dist/commands/backup/enable.js.map +1 -1
  80. package/dist/commands/backup/list.js +15 -1
  81. package/dist/commands/backup/list.js.map +1 -1
  82. package/dist/commands/cors/add.js +19 -1
  83. package/dist/commands/cors/add.js.map +1 -1
  84. package/dist/commands/cors/delete.js +21 -1
  85. package/dist/commands/cors/delete.js.map +1 -1
  86. package/dist/commands/cors/list.js +21 -1
  87. package/dist/commands/cors/list.js.map +1 -1
  88. package/dist/commands/dataset/alias/create.js +4 -2
  89. package/dist/commands/dataset/alias/create.js.map +1 -1
  90. package/dist/commands/dataset/alias/delete.js +4 -2
  91. package/dist/commands/dataset/alias/delete.js.map +1 -1
  92. package/dist/commands/dataset/alias/link.js +4 -2
  93. package/dist/commands/dataset/alias/link.js.map +1 -1
  94. package/dist/commands/dataset/alias/unlink.js +4 -2
  95. package/dist/commands/dataset/alias/unlink.js.map +1 -1
  96. package/dist/commands/dataset/copy.js +4 -2
  97. package/dist/commands/dataset/copy.js.map +1 -1
  98. package/dist/commands/dataset/create.js +4 -2
  99. package/dist/commands/dataset/create.js.map +1 -1
  100. package/dist/commands/dataset/delete.js +4 -2
  101. package/dist/commands/dataset/delete.js.map +1 -1
  102. package/dist/commands/dataset/embeddings/disable.js +4 -2
  103. package/dist/commands/dataset/embeddings/disable.js.map +1 -1
  104. package/dist/commands/dataset/embeddings/enable.js +6 -4
  105. package/dist/commands/dataset/embeddings/enable.js.map +1 -1
  106. package/dist/commands/dataset/embeddings/status.js +4 -2
  107. package/dist/commands/dataset/embeddings/status.js.map +1 -1
  108. package/dist/commands/dataset/export.js +7 -11
  109. package/dist/commands/dataset/export.js.map +1 -1
  110. package/dist/commands/dataset/list.js +4 -2
  111. package/dist/commands/dataset/list.js.map +1 -1
  112. package/dist/commands/dataset/visibility/get.js +4 -2
  113. package/dist/commands/dataset/visibility/get.js.map +1 -1
  114. package/dist/commands/dataset/visibility/set.js +4 -2
  115. package/dist/commands/dataset/visibility/set.js.map +1 -1
  116. package/dist/commands/debug.js +2 -1
  117. package/dist/commands/debug.js.map +1 -1
  118. package/dist/commands/documents/create.js +15 -5
  119. package/dist/commands/documents/create.js.map +1 -1
  120. package/dist/commands/documents/delete.js +17 -6
  121. package/dist/commands/documents/delete.js.map +1 -1
  122. package/dist/commands/documents/get.js +15 -6
  123. package/dist/commands/documents/get.js.map +1 -1
  124. package/dist/commands/documents/query.js +24 -12
  125. package/dist/commands/documents/query.js.map +1 -1
  126. package/dist/commands/documents/validate.js +29 -9
  127. package/dist/commands/documents/validate.js.map +1 -1
  128. package/dist/commands/graphql/deploy.js +55 -28
  129. package/dist/commands/graphql/deploy.js.map +1 -1
  130. package/dist/commands/graphql/list.js +14 -1
  131. package/dist/commands/graphql/list.js.map +1 -1
  132. package/dist/commands/graphql/undeploy.js +36 -14
  133. package/dist/commands/graphql/undeploy.js.map +1 -1
  134. package/dist/commands/hook/attempt.js +21 -1
  135. package/dist/commands/hook/attempt.js.map +1 -1
  136. package/dist/commands/hook/create.js +22 -2
  137. package/dist/commands/hook/create.js.map +1 -1
  138. package/dist/commands/hook/delete.js +21 -1
  139. package/dist/commands/hook/delete.js.map +1 -1
  140. package/dist/commands/hook/list.js +21 -1
  141. package/dist/commands/hook/list.js.map +1 -1
  142. package/dist/commands/hook/logs.js +19 -1
  143. package/dist/commands/hook/logs.js.map +1 -1
  144. package/dist/commands/init.js +13 -6
  145. package/dist/commands/init.js.map +1 -1
  146. package/dist/commands/login.js +13 -6
  147. package/dist/commands/login.js.map +1 -1
  148. package/dist/commands/logout.js +8 -6
  149. package/dist/commands/logout.js.map +1 -1
  150. package/dist/commands/media/create-aspect.js +3 -3
  151. package/dist/commands/media/create-aspect.js.map +1 -1
  152. package/dist/commands/media/delete-aspect.js +8 -1
  153. package/dist/commands/media/delete-aspect.js.map +1 -1
  154. package/dist/commands/media/deploy-aspect.js +20 -3
  155. package/dist/commands/media/deploy-aspect.js.map +1 -1
  156. package/dist/commands/media/export.js +8 -1
  157. package/dist/commands/media/export.js.map +1 -1
  158. package/dist/commands/media/import.js +9 -2
  159. package/dist/commands/media/import.js.map +1 -1
  160. package/dist/commands/schema/delete.js +19 -6
  161. package/dist/commands/schema/delete.js.map +1 -1
  162. package/dist/commands/schema/deploy.js +8 -2
  163. package/dist/commands/schema/deploy.js.map +1 -1
  164. package/dist/commands/tokens/add.js +23 -1
  165. package/dist/commands/tokens/add.js.map +1 -1
  166. package/dist/commands/tokens/delete.js +19 -1
  167. package/dist/commands/tokens/delete.js.map +1 -1
  168. package/dist/commands/tokens/list.js +19 -1
  169. package/dist/commands/tokens/list.js.map +1 -1
  170. package/dist/commands/users/invite.js +23 -1
  171. package/dist/commands/users/invite.js.map +1 -1
  172. package/dist/commands/users/list.js +23 -1
  173. package/dist/commands/users/list.js.map +1 -1
  174. package/dist/hooks/prerun/flushTelemetry.worker.js +1 -1
  175. package/dist/hooks/prerun/flushTelemetry.worker.js.map +1 -1
  176. package/dist/hooks/prerun/injectEnvVariables.js +9 -1
  177. package/dist/hooks/prerun/injectEnvVariables.js.map +1 -1
  178. package/dist/hooks/prerun/setupTelemetry.js +2 -1
  179. package/dist/hooks/prerun/setupTelemetry.js.map +1 -1
  180. package/dist/prompts/promptForProject.js.map +1 -1
  181. package/dist/{actions/auth/login/promptProviders.js → prompts/promptForProviders.js} +3 -3
  182. package/dist/prompts/promptForProviders.js.map +1 -0
  183. package/dist/services/auth.js +36 -3
  184. package/dist/services/auth.js.map +1 -1
  185. package/dist/services/docs.js +2 -2
  186. package/dist/services/docs.js.map +1 -1
  187. package/dist/services/getUrlHeaders.js +0 -2
  188. package/dist/services/getUrlHeaders.js.map +1 -1
  189. package/dist/services/projects.js +4 -2
  190. package/dist/services/projects.js.map +1 -1
  191. package/dist/services/telemetry.js +2 -1
  192. package/dist/services/telemetry.js.map +1 -1
  193. package/dist/util/createExpiringConfig.js +64 -0
  194. package/dist/util/createExpiringConfig.js.map +1 -0
  195. package/dist/util/detectFramework.js +135 -0
  196. package/dist/util/detectFramework.js.map +1 -0
  197. package/dist/util/extractDocumentsFromNdjsonOrTarball.js +1 -2
  198. package/dist/util/extractDocumentsFromNdjsonOrTarball.js.map +1 -1
  199. package/dist/util/isSchemaError.js +11 -0
  200. package/dist/util/isSchemaError.js.map +1 -0
  201. package/dist/util/isTar.js +8 -0
  202. package/dist/util/isTar.js.map +1 -0
  203. package/dist/util/sharedFlags.js +44 -14
  204. package/dist/util/sharedFlags.js.map +1 -1
  205. package/dist/util/telemetry/cleanupOldTelemetryFiles.js +30 -0
  206. package/dist/util/telemetry/cleanupOldTelemetryFiles.js.map +1 -0
  207. package/dist/util/telemetry/createTelemetryStore.js +95 -0
  208. package/dist/util/telemetry/createTelemetryStore.js.map +1 -0
  209. package/dist/util/telemetry/createTraceId.js +10 -0
  210. package/dist/util/telemetry/createTraceId.js.map +1 -0
  211. package/dist/util/telemetry/findTelemetryFiles.js +35 -0
  212. package/dist/util/telemetry/findTelemetryFiles.js.map +1 -0
  213. package/dist/util/telemetry/flushTelemetryFiles.js +118 -0
  214. package/dist/util/telemetry/flushTelemetryFiles.js.map +1 -0
  215. package/dist/util/telemetry/generateTelemetryFilePath.js +30 -0
  216. package/dist/util/telemetry/generateTelemetryFilePath.js.map +1 -0
  217. package/dist/util/telemetry/logger.js +59 -0
  218. package/dist/util/telemetry/logger.js.map +1 -0
  219. package/dist/util/telemetry/readNDJSON.js +28 -0
  220. package/dist/util/telemetry/readNDJSON.js.map +1 -0
  221. package/dist/util/telemetry/telemetryStoreDebug.js +7 -0
  222. package/dist/util/telemetry/telemetryStoreDebug.js.map +1 -0
  223. package/dist/util/telemetry/trace.js +150 -0
  224. package/dist/util/telemetry/trace.js.map +1 -0
  225. package/dist/util/update/updateChecker.js +2 -1
  226. package/dist/util/update/updateChecker.js.map +1 -1
  227. package/oclif.manifest.json +1211 -774
  228. package/package.json +19 -22
  229. package/dist/actions/auth/login/promptProviders.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/auth.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core'\n\nexport const AUTH_API_VERSION = '2025-09-23'\n\nexport async function logout() {\n const client = await getGlobalCliClient({apiVersion: AUTH_API_VERSION})\n\n return client.request({method: 'POST', uri: '/auth/logout'})\n}\n"],"names":["getGlobalCliClient","AUTH_API_VERSION","logout","client","apiVersion","request","method","uri"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,mBAAkB;AAEnD,OAAO,MAAMC,mBAAmB,aAAY;AAE5C,OAAO,eAAeC;IACpB,MAAMC,SAAS,MAAMH,mBAAmB;QAACI,YAAYH;IAAgB;IAErE,OAAOE,OAAOE,OAAO,CAAC;QAACC,QAAQ;QAAQC,KAAK;IAAc;AAC5D"}
1
+ {"version":3,"sources":["../../src/services/auth.ts"],"sourcesContent":["import {getGlobalCliClient} from '@sanity/cli-core'\n\nimport {\n type ProvidersResponse,\n type SamlLoginProvider,\n type TokenDetails,\n} from '../actions/auth/types.js'\n\nexport const AUTH_API_VERSION = 'v2025-09-23'\n\nasync function getUnauthenticatedClient() {\n return getGlobalCliClient({apiVersion: AUTH_API_VERSION, unauthenticated: true})\n}\n\n/**\n * Invalidate the current user session\n *\n * @param token - Optional token to invalidate.\n */\nexport async function logout(token?: string) {\n let client = await getGlobalCliClient({apiVersion: AUTH_API_VERSION})\n\n if (token) {\n client = client.withConfig({token})\n }\n\n return client.request({method: 'POST', uri: '/auth/logout'})\n}\n\nexport async function getProviders() {\n const client = await getUnauthenticatedClient()\n\n return client.request<ProvidersResponse>({uri: '/auth/providers'})\n}\n\nexport async function getSSOProviders(orgSlug: string) {\n const client = await getUnauthenticatedClient()\n\n return client.request<SamlLoginProvider[]>({\n uri: `/auth/organizations/by-slug/${orgSlug}/providers`,\n })\n}\n\nexport async function getTokenDetails(queryString: string) {\n const client = await getUnauthenticatedClient()\n\n return client.request<TokenDetails>({uri: `/auth/fetch${queryString}`})\n}\n"],"names":["getGlobalCliClient","AUTH_API_VERSION","getUnauthenticatedClient","apiVersion","unauthenticated","logout","token","client","withConfig","request","method","uri","getProviders","getSSOProviders","orgSlug","getTokenDetails","queryString"],"mappings":"AAAA,SAAQA,kBAAkB,QAAO,mBAAkB;AAQnD,OAAO,MAAMC,mBAAmB,cAAa;AAE7C,eAAeC;IACb,OAAOF,mBAAmB;QAACG,YAAYF;QAAkBG,iBAAiB;IAAI;AAChF;AAEA;;;;CAIC,GACD,OAAO,eAAeC,OAAOC,KAAc;IACzC,IAAIC,SAAS,MAAMP,mBAAmB;QAACG,YAAYF;IAAgB;IAEnE,IAAIK,OAAO;QACTC,SAASA,OAAOC,UAAU,CAAC;YAACF;QAAK;IACnC;IAEA,OAAOC,OAAOE,OAAO,CAAC;QAACC,QAAQ;QAAQC,KAAK;IAAc;AAC5D;AAEA,OAAO,eAAeC;IACpB,MAAML,SAAS,MAAML;IAErB,OAAOK,OAAOE,OAAO,CAAoB;QAACE,KAAK;IAAiB;AAClE;AAEA,OAAO,eAAeE,gBAAgBC,OAAe;IACnD,MAAMP,SAAS,MAAML;IAErB,OAAOK,OAAOE,OAAO,CAAsB;QACzCE,KAAK,CAAC,4BAA4B,EAAEG,QAAQ,UAAU,CAAC;IACzD;AACF;AAEA,OAAO,eAAeC,gBAAgBC,WAAmB;IACvD,MAAMT,SAAS,MAAML;IAErB,OAAOK,OAAOE,OAAO,CAAe;QAACE,KAAK,CAAC,WAAW,EAAEK,aAAa;IAAA;AACvE"}
@@ -4,7 +4,7 @@ function isSearchResult(obj) {
4
4
  return typeof obj === 'object' && obj !== null && 'description' in obj && 'path' in obj && 'title' in obj && typeof obj.description === 'string' && typeof obj.path === 'string' && typeof obj.title === 'string';
5
5
  }
6
6
  export async function readDoc(options) {
7
- const url = `${getSanityUrl()}${options.path}.md`;
7
+ const url = getSanityUrl(`${options.path}.md`);
8
8
  const response = await fetch(url, {
9
9
  headers: {
10
10
  Accept: 'text/plain'
@@ -22,7 +22,7 @@ export async function readDoc(options) {
22
22
  }
23
23
  export async function searchDocs(options) {
24
24
  const { limit = 10, query } = options;
25
- const baseUrl = `${getSanityUrl()}/docs/api/search/semantic`;
25
+ const baseUrl = getSanityUrl('/docs/api/search/semantic');
26
26
  const url = new URL(baseUrl);
27
27
  url.searchParams.set('query', query);
28
28
  const response = await fetch(url.toString(), {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/docs.ts"],"sourcesContent":["import {getSanityUrl} from '@sanity/cli-core'\n\nconst DOCS_API_TIMEOUT = 10_000\n\ninterface ReadDocOptions {\n path: string\n}\n\nexport interface SearchResult {\n description: string\n path: string\n title: string\n}\n\ninterface SearchDocsOptions {\n query: string\n\n limit?: number\n}\n\nfunction isSearchResult(obj: unknown): obj is SearchResult {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'description' in obj &&\n 'path' in obj &&\n 'title' in obj &&\n typeof (obj as SearchResult).description === 'string' &&\n typeof (obj as SearchResult).path === 'string' &&\n typeof (obj as SearchResult).title === 'string'\n )\n}\n\nexport async function readDoc(options: ReadDocOptions): Promise<string> {\n const url = `${getSanityUrl()}${options.path}.md`\n\n const response = await fetch(url, {\n headers: {\n Accept: 'text/plain',\n },\n method: 'GET',\n signal: AbortSignal.timeout(DOCS_API_TIMEOUT),\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(`Article not found: ${options.path}`)\n }\n throw new Error('The article API is currently unavailable. Please try again later.')\n }\n\n return response.text()\n}\n\nexport async function searchDocs(options: SearchDocsOptions): Promise<SearchResult[]> {\n const {limit = 10, query} = options\n\n const baseUrl = `${getSanityUrl()}/docs/api/search/semantic`\n const url = new URL(baseUrl)\n url.searchParams.set('query', query)\n\n const response = await fetch(url.toString(), {\n headers: {\n Accept: 'application/json',\n },\n method: 'GET',\n signal: AbortSignal.timeout(DOCS_API_TIMEOUT),\n })\n\n if (!response.ok) {\n throw new Error(\n 'The documentation search API is currently unavailable. Please try again later.',\n )\n }\n\n const results: unknown = await response.json()\n\n if (!Array.isArray(results)) {\n throw new TypeError('Invalid response format from documentation search API')\n }\n\n const validResults = results.filter((item): item is SearchResult => isSearchResult(item))\n return validResults.slice(0, limit)\n}\n"],"names":["getSanityUrl","DOCS_API_TIMEOUT","isSearchResult","obj","description","path","title","readDoc","options","url","response","fetch","headers","Accept","method","signal","AbortSignal","timeout","ok","status","Error","text","searchDocs","limit","query","baseUrl","URL","searchParams","set","toString","results","json","Array","isArray","TypeError","validResults","filter","item","slice"],"mappings":"AAAA,SAAQA,YAAY,QAAO,mBAAkB;AAE7C,MAAMC,mBAAmB;AAkBzB,SAASC,eAAeC,GAAY;IAClC,OACE,OAAOA,QAAQ,YACfA,QAAQ,QACR,iBAAiBA,OACjB,UAAUA,OACV,WAAWA,OACX,OAAO,AAACA,IAAqBC,WAAW,KAAK,YAC7C,OAAO,AAACD,IAAqBE,IAAI,KAAK,YACtC,OAAO,AAACF,IAAqBG,KAAK,KAAK;AAE3C;AAEA,OAAO,eAAeC,QAAQC,OAAuB;IACnD,MAAMC,MAAM,GAAGT,iBAAiBQ,QAAQH,IAAI,CAAC,GAAG,CAAC;IAEjD,MAAMK,WAAW,MAAMC,MAAMF,KAAK;QAChCG,SAAS;YACPC,QAAQ;QACV;QACAC,QAAQ;QACRC,QAAQC,YAAYC,OAAO,CAAChB;IAC9B;IAEA,IAAI,CAACS,SAASQ,EAAE,EAAE;QAChB,IAAIR,SAASS,MAAM,KAAK,KAAK;YAC3B,MAAM,IAAIC,MAAM,CAAC,mBAAmB,EAAEZ,QAAQH,IAAI,EAAE;QACtD;QACA,MAAM,IAAIe,MAAM;IAClB;IAEA,OAAOV,SAASW,IAAI;AACtB;AAEA,OAAO,eAAeC,WAAWd,OAA0B;IACzD,MAAM,EAACe,QAAQ,EAAE,EAAEC,KAAK,EAAC,GAAGhB;IAE5B,MAAMiB,UAAU,GAAGzB,eAAe,yBAAyB,CAAC;IAC5D,MAAMS,MAAM,IAAIiB,IAAID;IACpBhB,IAAIkB,YAAY,CAACC,GAAG,CAAC,SAASJ;IAE9B,MAAMd,WAAW,MAAMC,MAAMF,IAAIoB,QAAQ,IAAI;QAC3CjB,SAAS;YACPC,QAAQ;QACV;QACAC,QAAQ;QACRC,QAAQC,YAAYC,OAAO,CAAChB;IAC9B;IAEA,IAAI,CAACS,SAASQ,EAAE,EAAE;QAChB,MAAM,IAAIE,MACR;IAEJ;IAEA,MAAMU,UAAmB,MAAMpB,SAASqB,IAAI;IAE5C,IAAI,CAACC,MAAMC,OAAO,CAACH,UAAU;QAC3B,MAAM,IAAII,UAAU;IACtB;IAEA,MAAMC,eAAeL,QAAQM,MAAM,CAAC,CAACC,OAA+BnC,eAAemC;IACnF,OAAOF,aAAaG,KAAK,CAAC,GAAGf;AAC/B"}
1
+ {"version":3,"sources":["../../src/services/docs.ts"],"sourcesContent":["import {getSanityUrl} from '@sanity/cli-core'\n\nconst DOCS_API_TIMEOUT = 10_000\n\ninterface ReadDocOptions {\n path: string\n}\n\nexport interface SearchResult {\n description: string\n path: string\n title: string\n}\n\ninterface SearchDocsOptions {\n query: string\n\n limit?: number\n}\n\nfunction isSearchResult(obj: unknown): obj is SearchResult {\n return (\n typeof obj === 'object' &&\n obj !== null &&\n 'description' in obj &&\n 'path' in obj &&\n 'title' in obj &&\n typeof (obj as SearchResult).description === 'string' &&\n typeof (obj as SearchResult).path === 'string' &&\n typeof (obj as SearchResult).title === 'string'\n )\n}\n\nexport async function readDoc(options: ReadDocOptions): Promise<string> {\n const url = getSanityUrl(`${options.path}.md`)\n\n const response = await fetch(url, {\n headers: {\n Accept: 'text/plain',\n },\n method: 'GET',\n signal: AbortSignal.timeout(DOCS_API_TIMEOUT),\n })\n\n if (!response.ok) {\n if (response.status === 404) {\n throw new Error(`Article not found: ${options.path}`)\n }\n throw new Error('The article API is currently unavailable. Please try again later.')\n }\n\n return response.text()\n}\n\nexport async function searchDocs(options: SearchDocsOptions): Promise<SearchResult[]> {\n const {limit = 10, query} = options\n\n const baseUrl = getSanityUrl('/docs/api/search/semantic')\n const url = new URL(baseUrl)\n url.searchParams.set('query', query)\n\n const response = await fetch(url.toString(), {\n headers: {\n Accept: 'application/json',\n },\n method: 'GET',\n signal: AbortSignal.timeout(DOCS_API_TIMEOUT),\n })\n\n if (!response.ok) {\n throw new Error(\n 'The documentation search API is currently unavailable. Please try again later.',\n )\n }\n\n const results: unknown = await response.json()\n\n if (!Array.isArray(results)) {\n throw new TypeError('Invalid response format from documentation search API')\n }\n\n const validResults = results.filter((item): item is SearchResult => isSearchResult(item))\n return validResults.slice(0, limit)\n}\n"],"names":["getSanityUrl","DOCS_API_TIMEOUT","isSearchResult","obj","description","path","title","readDoc","options","url","response","fetch","headers","Accept","method","signal","AbortSignal","timeout","ok","status","Error","text","searchDocs","limit","query","baseUrl","URL","searchParams","set","toString","results","json","Array","isArray","TypeError","validResults","filter","item","slice"],"mappings":"AAAA,SAAQA,YAAY,QAAO,mBAAkB;AAE7C,MAAMC,mBAAmB;AAkBzB,SAASC,eAAeC,GAAY;IAClC,OACE,OAAOA,QAAQ,YACfA,QAAQ,QACR,iBAAiBA,OACjB,UAAUA,OACV,WAAWA,OACX,OAAO,AAACA,IAAqBC,WAAW,KAAK,YAC7C,OAAO,AAACD,IAAqBE,IAAI,KAAK,YACtC,OAAO,AAACF,IAAqBG,KAAK,KAAK;AAE3C;AAEA,OAAO,eAAeC,QAAQC,OAAuB;IACnD,MAAMC,MAAMT,aAAa,GAAGQ,QAAQH,IAAI,CAAC,GAAG,CAAC;IAE7C,MAAMK,WAAW,MAAMC,MAAMF,KAAK;QAChCG,SAAS;YACPC,QAAQ;QACV;QACAC,QAAQ;QACRC,QAAQC,YAAYC,OAAO,CAAChB;IAC9B;IAEA,IAAI,CAACS,SAASQ,EAAE,EAAE;QAChB,IAAIR,SAASS,MAAM,KAAK,KAAK;YAC3B,MAAM,IAAIC,MAAM,CAAC,mBAAmB,EAAEZ,QAAQH,IAAI,EAAE;QACtD;QACA,MAAM,IAAIe,MAAM;IAClB;IAEA,OAAOV,SAASW,IAAI;AACtB;AAEA,OAAO,eAAeC,WAAWd,OAA0B;IACzD,MAAM,EAACe,QAAQ,EAAE,EAAEC,KAAK,EAAC,GAAGhB;IAE5B,MAAMiB,UAAUzB,aAAa;IAC7B,MAAMS,MAAM,IAAIiB,IAAID;IACpBhB,IAAIkB,YAAY,CAACC,GAAG,CAAC,SAASJ;IAE9B,MAAMd,WAAW,MAAMC,MAAMF,IAAIoB,QAAQ,IAAI;QAC3CjB,SAAS;YACPC,QAAQ;QACV;QACAC,QAAQ;QACRC,QAAQC,YAAYC,OAAO,CAAChB;IAC9B;IAEA,IAAI,CAACS,SAASQ,EAAE,EAAE;QAChB,MAAM,IAAIE,MACR;IAEJ;IAEA,MAAMU,UAAmB,MAAMpB,SAASqB,IAAI;IAE5C,IAAI,CAACC,MAAMC,OAAO,CAACH,UAAU;QAC3B,MAAM,IAAII,UAAU;IACtB;IAEA,MAAMC,eAAeL,QAAQM,MAAM,CAAC,CAACC,OAA+BnC,eAAemC;IACnF,OAAOF,aAAaG,KAAK,CAAC,GAAGf;AAC/B"}
@@ -17,10 +17,8 @@ const request = createRequester({
17
17
  headers,
18
18
  maxRedirects: 0,
19
19
  method: 'HEAD',
20
- stream: true,
21
20
  url
22
21
  });
23
- response.body.resume();
24
22
  return response.headers;
25
23
  }
26
24
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/getUrlHeaders.ts"],"sourcesContent":["import {createRequester} from '@sanity/cli-core/request'\n\nconst request = createRequester({middleware: {promise: {onlyBody: false}}})\n\n/**\n * Gets the headers of a URL\n *\n * @param url - The URL to get the headers from\n * @param headers - The headers to send with the request\n * @returns The headers of the response\n */\nexport async function getUrlHeaders(url: string, headers = {}): Promise<Record<string, string>> {\n const response = await request({\n headers,\n maxRedirects: 0,\n method: 'HEAD',\n stream: true,\n url,\n })\n\n response.body.resume()\n return response.headers\n}\n"],"names":["createRequester","request","middleware","promise","onlyBody","getUrlHeaders","url","headers","response","maxRedirects","method","stream","body","resume"],"mappings":"AAAA,SAAQA,eAAe,QAAO,2BAA0B;AAExD,MAAMC,UAAUD,gBAAgB;IAACE,YAAY;QAACC,SAAS;YAACC,UAAU;QAAK;IAAC;AAAC;AAEzE;;;;;;CAMC,GACD,OAAO,eAAeC,cAAcC,GAAW,EAAEC,UAAU,CAAC,CAAC;IAC3D,MAAMC,WAAW,MAAMP,QAAQ;QAC7BM;QACAE,cAAc;QACdC,QAAQ;QACRC,QAAQ;QACRL;IACF;IAEAE,SAASI,IAAI,CAACC,MAAM;IACpB,OAAOL,SAASD,OAAO;AACzB"}
1
+ {"version":3,"sources":["../../src/services/getUrlHeaders.ts"],"sourcesContent":["import {createRequester} from '@sanity/cli-core/request'\n\nconst request = createRequester({middleware: {promise: {onlyBody: false}}})\n\n/**\n * Gets the headers of a URL\n *\n * @param url - The URL to get the headers from\n * @param headers - The headers to send with the request\n * @returns The headers of the response\n */\nexport async function getUrlHeaders(url: string, headers = {}): Promise<Record<string, string>> {\n const response = await request({\n headers,\n maxRedirects: 0,\n method: 'HEAD',\n url,\n })\n\n return response.headers\n}\n"],"names":["createRequester","request","middleware","promise","onlyBody","getUrlHeaders","url","headers","response","maxRedirects","method"],"mappings":"AAAA,SAAQA,eAAe,QAAO,2BAA0B;AAExD,MAAMC,UAAUD,gBAAgB;IAACE,YAAY;QAACC,SAAS;YAACC,UAAU;QAAK;IAAC;AAAC;AAEzE;;;;;;CAMC,GACD,OAAO,eAAeC,cAAcC,GAAW,EAAEC,UAAU,CAAC,CAAC;IAC3D,MAAMC,WAAW,MAAMP,QAAQ;QAC7BM;QACAE,cAAc;QACdC,QAAQ;QACRJ;IACF;IAEA,OAAOE,SAASD,OAAO;AACzB"}
@@ -62,12 +62,14 @@ export async function inviteUser({ email, projectId, role }) {
62
62
  useGlobalApi: true
63
63
  });
64
64
  }
65
- export async function listProjects() {
65
+ export async function listProjects({ onlyExplicitMembership = true } = {}) {
66
66
  const client = await getGlobalCliClient({
67
67
  apiVersion: PROJECTS_API_VERSION,
68
68
  requireUser: true
69
69
  });
70
- return client.projects.list();
70
+ return client.projects.list({
71
+ onlyExplicitMembership
72
+ });
71
73
  }
72
74
  export async function getProjectInvites(projectId) {
73
75
  const client = await getGlobalCliClient({
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/projects.ts"],"sourcesContent":["import {debug, getGlobalCliClient, getProjectCliClient} from '@sanity/cli-core'\nimport {SanityProject} from '@sanity/client'\n\nimport {type Invite, type Role} from '../actions/users/types.js'\n\nexport const PROJECTS_API_VERSION = '2025-09-22'\n\nexport const CREATE_PROJECT_API_VERSION = 'v2025-05-14'\n\ninterface CreateProjectOptions {\n displayName: string\n\n metadata?: {\n coupon?: string\n integration?: string\n }\n organizationId?: string\n subscription?: {planId: string}\n}\n\nexport interface CreateProjectResult {\n displayName: string\n projectId: string\n}\n\n/**\n * Create a new Sanity project\n */\nexport async function createProject(options: CreateProjectOptions): Promise<CreateProjectResult> {\n const client = await getGlobalCliClient({\n apiVersion: CREATE_PROJECT_API_VERSION,\n requireUser: true,\n })\n\n try {\n const response = await client.request({\n body: {\n ...options,\n metadata: {\n ...options?.metadata,\n integration: 'cli',\n },\n },\n method: 'POST',\n uri: '/projects',\n })\n\n return {\n displayName: options.displayName || '',\n projectId: response.projectId || response.id,\n }\n } catch (err) {\n debug('Error creating project', err)\n throw err\n }\n}\n\nexport async function getProjectById(projectId: string) {\n const client = await getProjectCliClient({\n apiVersion: PROJECTS_API_VERSION,\n projectId,\n requireUser: true,\n })\n\n return client.projects.getById(projectId)\n}\n\nexport async function getProjectRoles(projectId: string) {\n const client = await getGlobalCliClient({\n apiVersion: PROJECTS_API_VERSION,\n requireUser: true,\n })\n\n return client.request<Role[]>({uri: `/projects/${projectId}/roles`})\n}\n\ninterface InviteUserOptions {\n email: string\n projectId: string\n role: string\n}\n\nexport async function inviteUser({email, projectId, role}: InviteUserOptions) {\n const client = await getGlobalCliClient({\n apiVersion: PROJECTS_API_VERSION,\n requireUser: true,\n })\n\n return client.request({\n body: {email, role},\n maxRedirects: 0,\n method: 'POST',\n uri: `/invitations/project/${projectId}`,\n useGlobalApi: true,\n })\n}\n\nexport async function listProjects() {\n const client = await getGlobalCliClient({\n apiVersion: PROJECTS_API_VERSION,\n requireUser: true,\n })\n\n return client.projects.list()\n}\n\nexport async function getProjectInvites(projectId: string) {\n const client = await getGlobalCliClient({\n apiVersion: PROJECTS_API_VERSION,\n requireUser: true,\n })\n\n return client.request<Invite[]>({uri: `/invitations/project/${projectId}`})\n}\n\nexport async function updateProjectInitializedAt(projectId: string) {\n const client = await getProjectCliClient({\n apiVersion: PROJECTS_API_VERSION,\n projectId,\n requireUser: true,\n })\n\n const project = await client.request<SanityProject>({uri: `/projects/${projectId}`})\n\n if (!project?.metadata?.cliInitializedAt) {\n await client.request({\n body: {metadata: {cliInitializedAt: new Date().toISOString()}},\n method: 'PATCH',\n uri: `/projects/${projectId}`,\n })\n }\n}\n\nexport async function updateProjectInitalTemplate(projectId: string, templateName: string) {\n const client = await getProjectCliClient({\n apiVersion: PROJECTS_API_VERSION,\n projectId,\n requireUser: true,\n })\n\n await client.request({\n body: {metadata: {initialTemplate: templateName}},\n method: 'PATCH',\n uri: `/projects/${projectId}`,\n })\n}\n"],"names":["debug","getGlobalCliClient","getProjectCliClient","PROJECTS_API_VERSION","CREATE_PROJECT_API_VERSION","createProject","options","client","apiVersion","requireUser","response","request","body","metadata","integration","method","uri","displayName","projectId","id","err","getProjectById","projects","getById","getProjectRoles","inviteUser","email","role","maxRedirects","useGlobalApi","listProjects","list","getProjectInvites","updateProjectInitializedAt","project","cliInitializedAt","Date","toISOString","updateProjectInitalTemplate","templateName","initialTemplate"],"mappings":"AAAA,SAAQA,KAAK,EAAEC,kBAAkB,EAAEC,mBAAmB,QAAO,mBAAkB;AAK/E,OAAO,MAAMC,uBAAuB,aAAY;AAEhD,OAAO,MAAMC,6BAA6B,cAAa;AAkBvD;;CAEC,GACD,OAAO,eAAeC,cAAcC,OAA6B;IAC/D,MAAMC,SAAS,MAAMN,mBAAmB;QACtCO,YAAYJ;QACZK,aAAa;IACf;IAEA,IAAI;QACF,MAAMC,WAAW,MAAMH,OAAOI,OAAO,CAAC;YACpCC,MAAM;gBACJ,GAAGN,OAAO;gBACVO,UAAU;oBACR,GAAGP,SAASO,QAAQ;oBACpBC,aAAa;gBACf;YACF;YACAC,QAAQ;YACRC,KAAK;QACP;QAEA,OAAO;YACLC,aAAaX,QAAQW,WAAW,IAAI;YACpCC,WAAWR,SAASQ,SAAS,IAAIR,SAASS,EAAE;QAC9C;IACF,EAAE,OAAOC,KAAK;QACZpB,MAAM,0BAA0BoB;QAChC,MAAMA;IACR;AACF;AAEA,OAAO,eAAeC,eAAeH,SAAiB;IACpD,MAAMX,SAAS,MAAML,oBAAoB;QACvCM,YAAYL;QACZe;QACAT,aAAa;IACf;IAEA,OAAOF,OAAOe,QAAQ,CAACC,OAAO,CAACL;AACjC;AAEA,OAAO,eAAeM,gBAAgBN,SAAiB;IACrD,MAAMX,SAAS,MAAMN,mBAAmB;QACtCO,YAAYL;QACZM,aAAa;IACf;IAEA,OAAOF,OAAOI,OAAO,CAAS;QAACK,KAAK,CAAC,UAAU,EAAEE,UAAU,MAAM,CAAC;IAAA;AACpE;AAQA,OAAO,eAAeO,WAAW,EAACC,KAAK,EAAER,SAAS,EAAES,IAAI,EAAoB;IAC1E,MAAMpB,SAAS,MAAMN,mBAAmB;QACtCO,YAAYL;QACZM,aAAa;IACf;IAEA,OAAOF,OAAOI,OAAO,CAAC;QACpBC,MAAM;YAACc;YAAOC;QAAI;QAClBC,cAAc;QACdb,QAAQ;QACRC,KAAK,CAAC,qBAAqB,EAAEE,WAAW;QACxCW,cAAc;IAChB;AACF;AAEA,OAAO,eAAeC;IACpB,MAAMvB,SAAS,MAAMN,mBAAmB;QACtCO,YAAYL;QACZM,aAAa;IACf;IAEA,OAAOF,OAAOe,QAAQ,CAACS,IAAI;AAC7B;AAEA,OAAO,eAAeC,kBAAkBd,SAAiB;IACvD,MAAMX,SAAS,MAAMN,mBAAmB;QACtCO,YAAYL;QACZM,aAAa;IACf;IAEA,OAAOF,OAAOI,OAAO,CAAW;QAACK,KAAK,CAAC,qBAAqB,EAAEE,WAAW;IAAA;AAC3E;AAEA,OAAO,eAAee,2BAA2Bf,SAAiB;IAChE,MAAMX,SAAS,MAAML,oBAAoB;QACvCM,YAAYL;QACZe;QACAT,aAAa;IACf;IAEA,MAAMyB,UAAU,MAAM3B,OAAOI,OAAO,CAAgB;QAACK,KAAK,CAAC,UAAU,EAAEE,WAAW;IAAA;IAElF,IAAI,CAACgB,SAASrB,UAAUsB,kBAAkB;QACxC,MAAM5B,OAAOI,OAAO,CAAC;YACnBC,MAAM;gBAACC,UAAU;oBAACsB,kBAAkB,IAAIC,OAAOC,WAAW;gBAAE;YAAC;YAC7DtB,QAAQ;YACRC,KAAK,CAAC,UAAU,EAAEE,WAAW;QAC/B;IACF;AACF;AAEA,OAAO,eAAeoB,4BAA4BpB,SAAiB,EAAEqB,YAAoB;IACvF,MAAMhC,SAAS,MAAML,oBAAoB;QACvCM,YAAYL;QACZe;QACAT,aAAa;IACf;IAEA,MAAMF,OAAOI,OAAO,CAAC;QACnBC,MAAM;YAACC,UAAU;gBAAC2B,iBAAiBD;YAAY;QAAC;QAChDxB,QAAQ;QACRC,KAAK,CAAC,UAAU,EAAEE,WAAW;IAC/B;AACF"}
1
+ {"version":3,"sources":["../../src/services/projects.ts"],"sourcesContent":["import {debug, getGlobalCliClient, getProjectCliClient} from '@sanity/cli-core'\nimport {SanityProject} from '@sanity/client'\n\nimport {type Invite, type Role} from '../actions/users/types.js'\n\nexport const PROJECTS_API_VERSION = '2025-09-22'\n\nexport const CREATE_PROJECT_API_VERSION = 'v2025-05-14'\n\ninterface CreateProjectOptions {\n displayName: string\n\n metadata?: {\n coupon?: string\n integration?: string\n }\n organizationId?: string\n subscription?: {planId: string}\n}\n\nexport interface CreateProjectResult {\n displayName: string\n projectId: string\n}\n\n/**\n * Create a new Sanity project\n */\nexport async function createProject(options: CreateProjectOptions): Promise<CreateProjectResult> {\n const client = await getGlobalCliClient({\n apiVersion: CREATE_PROJECT_API_VERSION,\n requireUser: true,\n })\n\n try {\n const response = await client.request({\n body: {\n ...options,\n metadata: {\n ...options?.metadata,\n integration: 'cli',\n },\n },\n method: 'POST',\n uri: '/projects',\n })\n\n return {\n displayName: options.displayName || '',\n projectId: response.projectId || response.id,\n }\n } catch (err) {\n debug('Error creating project', err)\n throw err\n }\n}\n\nexport async function getProjectById(projectId: string) {\n const client = await getProjectCliClient({\n apiVersion: PROJECTS_API_VERSION,\n projectId,\n requireUser: true,\n })\n\n return client.projects.getById(projectId)\n}\n\nexport async function getProjectRoles(projectId: string) {\n const client = await getGlobalCliClient({\n apiVersion: PROJECTS_API_VERSION,\n requireUser: true,\n })\n\n return client.request<Role[]>({uri: `/projects/${projectId}/roles`})\n}\n\ninterface InviteUserOptions {\n email: string\n projectId: string\n role: string\n}\n\nexport async function inviteUser({email, projectId, role}: InviteUserOptions) {\n const client = await getGlobalCliClient({\n apiVersion: PROJECTS_API_VERSION,\n requireUser: true,\n })\n\n return client.request({\n body: {email, role},\n maxRedirects: 0,\n method: 'POST',\n uri: `/invitations/project/${projectId}`,\n useGlobalApi: true,\n })\n}\n\nexport async function listProjects({\n onlyExplicitMembership = true,\n}: {\n onlyExplicitMembership?: boolean\n} = {}) {\n const client = await getGlobalCliClient({\n apiVersion: PROJECTS_API_VERSION,\n requireUser: true,\n })\n\n return client.projects.list({onlyExplicitMembership})\n}\n\nexport async function getProjectInvites(projectId: string) {\n const client = await getGlobalCliClient({\n apiVersion: PROJECTS_API_VERSION,\n requireUser: true,\n })\n\n return client.request<Invite[]>({uri: `/invitations/project/${projectId}`})\n}\n\nexport async function updateProjectInitializedAt(projectId: string) {\n const client = await getProjectCliClient({\n apiVersion: PROJECTS_API_VERSION,\n projectId,\n requireUser: true,\n })\n\n const project = await client.request<SanityProject>({uri: `/projects/${projectId}`})\n\n if (!project?.metadata?.cliInitializedAt) {\n await client.request({\n body: {metadata: {cliInitializedAt: new Date().toISOString()}},\n method: 'PATCH',\n uri: `/projects/${projectId}`,\n })\n }\n}\n\nexport async function updateProjectInitalTemplate(projectId: string, templateName: string) {\n const client = await getProjectCliClient({\n apiVersion: PROJECTS_API_VERSION,\n projectId,\n requireUser: true,\n })\n\n await client.request({\n body: {metadata: {initialTemplate: templateName}},\n method: 'PATCH',\n uri: `/projects/${projectId}`,\n })\n}\n"],"names":["debug","getGlobalCliClient","getProjectCliClient","PROJECTS_API_VERSION","CREATE_PROJECT_API_VERSION","createProject","options","client","apiVersion","requireUser","response","request","body","metadata","integration","method","uri","displayName","projectId","id","err","getProjectById","projects","getById","getProjectRoles","inviteUser","email","role","maxRedirects","useGlobalApi","listProjects","onlyExplicitMembership","list","getProjectInvites","updateProjectInitializedAt","project","cliInitializedAt","Date","toISOString","updateProjectInitalTemplate","templateName","initialTemplate"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
- import { createExpiringConfig, getGlobalCliClient, getUserConfig } from '@sanity/cli-core';
1
+ import { getGlobalCliClient, getUserConfig } from '@sanity/cli-core';
2
2
  import { telemetryDebug } from '../actions/telemetry/telemetryDebug.js';
3
+ import { createExpiringConfig } from '../util/createExpiringConfig.js';
3
4
  export const TELEMETRY_API_VERSION = 'v2026-01-22';
4
5
  export const VALID_API_STATUSES = [
5
6
  'granted',
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/services/telemetry.ts"],"sourcesContent":["import {createExpiringConfig, getGlobalCliClient, getUserConfig} from '@sanity/cli-core'\nimport {type TelemetryEvent} from '@sanity/telemetry'\n\nimport {telemetryDebug} from '../actions/telemetry/telemetryDebug.js'\n\nexport const TELEMETRY_API_VERSION = 'v2026-01-22'\n\nexport const VALID_API_STATUSES = ['granted', 'denied', 'unset'] as const\nexport type ValidApiConsentStatus = (typeof VALID_API_STATUSES)[number]\n\nexport async function sendEvents(batch: TelemetryEvent[]) {\n const client = await getGlobalCliClient({\n apiVersion: TELEMETRY_API_VERSION,\n requireUser: true,\n })\n\n const projectId = process.env.SANITY_TELEMETRY_PROJECT_ID\n\n return client.request({\n body: {batch, projectId},\n json: true,\n method: 'POST',\n uri: '/intake/batch',\n })\n}\n\nasync function getTelemetryConsent(): Promise<{\n status: ValidApiConsentStatus\n}> {\n const client = await getGlobalCliClient({\n apiVersion: TELEMETRY_API_VERSION,\n requireUser: false,\n })\n\n return client.request({tag: 'telemetry-consent', uri: '/intake/telemetry-status'})\n}\n\n/**\n * Check if the given status is a valid consent status\n *\n * @param status - The status to check\n * @returns True if the status is valid, false otherwise\n * @internal\n */\nexport function isValidApiConsentStatus(status: string): status is ValidApiConsentStatus {\n return VALID_API_STATUSES.includes(status as ValidApiConsentStatus)\n}\n\n/**\n * Check if the given response is a valid API consent response\n *\n * @param response - The response to check\n * @returns True if the response is valid, false otherwise\n * @internal\n */\nfunction isValidApiConsentResponse(response: unknown): response is {status: ValidApiConsentStatus} {\n return (\n typeof response === 'object' &&\n response !== null &&\n 'status' in response &&\n typeof response.status === 'string' &&\n isValidApiConsentStatus(response.status)\n )\n}\n\nexport const TELEMETRY_CONSENT_CONFIG_KEY = 'telemetryConsent'\nconst FIVE_MINUTES = 1000 * 60 * 5\n\n/**\n * Fetch the telemetry consent status for the current user\n * @returns The telemetry consent status\n *\n * @internal\n */\nexport async function fetchTelemetryConsent(): Promise<{\n status: ValidApiConsentStatus\n}> {\n const telemetryConsentConfig = createExpiringConfig<{\n status: ValidApiConsentStatus\n }>({\n fetchValue: () => getTelemetryConsent(),\n key: TELEMETRY_CONSENT_CONFIG_KEY,\n onCacheHit() {\n telemetryDebug('Retrieved telemetry consent status from cache')\n },\n onFetch() {\n telemetryDebug('Fetching telemetry consent status...')\n },\n onRevalidate() {\n telemetryDebug('Revalidating cached telemetry consent status...')\n },\n store: getUserConfig(),\n ttl: FIVE_MINUTES,\n validateValue: isValidApiConsentResponse,\n })\n\n return telemetryConsentConfig.get()\n}\n"],"names":["createExpiringConfig","getGlobalCliClient","getUserConfig","telemetryDebug","TELEMETRY_API_VERSION","VALID_API_STATUSES","sendEvents","batch","client","apiVersion","requireUser","projectId","process","env","SANITY_TELEMETRY_PROJECT_ID","request","body","json","method","uri","getTelemetryConsent","tag","isValidApiConsentStatus","status","includes","isValidApiConsentResponse","response","TELEMETRY_CONSENT_CONFIG_KEY","FIVE_MINUTES","fetchTelemetryConsent","telemetryConsentConfig","fetchValue","key","onCacheHit","onFetch","onRevalidate","store","ttl","validateValue","get"],"mappings":"AAAA,SAAQA,oBAAoB,EAAEC,kBAAkB,EAAEC,aAAa,QAAO,mBAAkB;AAGxF,SAAQC,cAAc,QAAO,yCAAwC;AAErE,OAAO,MAAMC,wBAAwB,cAAa;AAElD,OAAO,MAAMC,qBAAqB;IAAC;IAAW;IAAU;CAAQ,CAAS;AAGzE,OAAO,eAAeC,WAAWC,KAAuB;IACtD,MAAMC,SAAS,MAAMP,mBAAmB;QACtCQ,YAAYL;QACZM,aAAa;IACf;IAEA,MAAMC,YAAYC,QAAQC,GAAG,CAACC,2BAA2B;IAEzD,OAAON,OAAOO,OAAO,CAAC;QACpBC,MAAM;YAACT;YAAOI;QAAS;QACvBM,MAAM;QACNC,QAAQ;QACRC,KAAK;IACP;AACF;AAEA,eAAeC;IAGb,MAAMZ,SAAS,MAAMP,mBAAmB;QACtCQ,YAAYL;QACZM,aAAa;IACf;IAEA,OAAOF,OAAOO,OAAO,CAAC;QAACM,KAAK;QAAqBF,KAAK;IAA0B;AAClF;AAEA;;;;;;CAMC,GACD,OAAO,SAASG,wBAAwBC,MAAc;IACpD,OAAOlB,mBAAmBmB,QAAQ,CAACD;AACrC;AAEA;;;;;;CAMC,GACD,SAASE,0BAA0BC,QAAiB;IAClD,OACE,OAAOA,aAAa,YACpBA,aAAa,QACb,YAAYA,YACZ,OAAOA,SAASH,MAAM,KAAK,YAC3BD,wBAAwBI,SAASH,MAAM;AAE3C;AAEA,OAAO,MAAMI,+BAA+B,mBAAkB;AAC9D,MAAMC,eAAe,OAAO,KAAK;AAEjC;;;;;CAKC,GACD,OAAO,eAAeC;IAGpB,MAAMC,yBAAyB9B,qBAE5B;QACD+B,YAAY,IAAMX;QAClBY,KAAKL;QACLM;YACE9B,eAAe;QACjB;QACA+B;YACE/B,eAAe;QACjB;QACAgC;YACEhC,eAAe;QACjB;QACAiC,OAAOlC;QACPmC,KAAKT;QACLU,eAAeb;IACjB;IAEA,OAAOK,uBAAuBS,GAAG;AACnC"}
1
+ {"version":3,"sources":["../../src/services/telemetry.ts"],"sourcesContent":["import {getGlobalCliClient, getUserConfig} from '@sanity/cli-core'\nimport {type TelemetryEvent} from '@sanity/telemetry'\n\nimport {telemetryDebug} from '../actions/telemetry/telemetryDebug.js'\nimport {createExpiringConfig} from '../util/createExpiringConfig.js'\n\nexport const TELEMETRY_API_VERSION = 'v2026-01-22'\n\nexport const VALID_API_STATUSES = ['granted', 'denied', 'unset'] as const\nexport type ValidApiConsentStatus = (typeof VALID_API_STATUSES)[number]\n\nexport async function sendEvents(batch: TelemetryEvent[]) {\n const client = await getGlobalCliClient({\n apiVersion: TELEMETRY_API_VERSION,\n requireUser: true,\n })\n\n const projectId = process.env.SANITY_TELEMETRY_PROJECT_ID\n\n return client.request({\n body: {batch, projectId},\n json: true,\n method: 'POST',\n uri: '/intake/batch',\n })\n}\n\nasync function getTelemetryConsent(): Promise<{\n status: ValidApiConsentStatus\n}> {\n const client = await getGlobalCliClient({\n apiVersion: TELEMETRY_API_VERSION,\n requireUser: false,\n })\n\n return client.request({tag: 'telemetry-consent', uri: '/intake/telemetry-status'})\n}\n\n/**\n * Check if the given status is a valid consent status\n *\n * @param status - The status to check\n * @returns True if the status is valid, false otherwise\n * @internal\n */\nexport function isValidApiConsentStatus(status: string): status is ValidApiConsentStatus {\n return VALID_API_STATUSES.includes(status as ValidApiConsentStatus)\n}\n\n/**\n * Check if the given response is a valid API consent response\n *\n * @param response - The response to check\n * @returns True if the response is valid, false otherwise\n * @internal\n */\nfunction isValidApiConsentResponse(response: unknown): response is {status: ValidApiConsentStatus} {\n return (\n typeof response === 'object' &&\n response !== null &&\n 'status' in response &&\n typeof response.status === 'string' &&\n isValidApiConsentStatus(response.status)\n )\n}\n\nexport const TELEMETRY_CONSENT_CONFIG_KEY = 'telemetryConsent'\nconst FIVE_MINUTES = 1000 * 60 * 5\n\n/**\n * Fetch the telemetry consent status for the current user\n * @returns The telemetry consent status\n *\n * @internal\n */\nexport async function fetchTelemetryConsent(): Promise<{\n status: ValidApiConsentStatus\n}> {\n const telemetryConsentConfig = createExpiringConfig<{\n status: ValidApiConsentStatus\n }>({\n fetchValue: () => getTelemetryConsent(),\n key: TELEMETRY_CONSENT_CONFIG_KEY,\n onCacheHit() {\n telemetryDebug('Retrieved telemetry consent status from cache')\n },\n onFetch() {\n telemetryDebug('Fetching telemetry consent status...')\n },\n onRevalidate() {\n telemetryDebug('Revalidating cached telemetry consent status...')\n },\n store: getUserConfig(),\n ttl: FIVE_MINUTES,\n validateValue: isValidApiConsentResponse,\n })\n\n return telemetryConsentConfig.get()\n}\n"],"names":["getGlobalCliClient","getUserConfig","telemetryDebug","createExpiringConfig","TELEMETRY_API_VERSION","VALID_API_STATUSES","sendEvents","batch","client","apiVersion","requireUser","projectId","process","env","SANITY_TELEMETRY_PROJECT_ID","request","body","json","method","uri","getTelemetryConsent","tag","isValidApiConsentStatus","status","includes","isValidApiConsentResponse","response","TELEMETRY_CONSENT_CONFIG_KEY","FIVE_MINUTES","fetchTelemetryConsent","telemetryConsentConfig","fetchValue","key","onCacheHit","onFetch","onRevalidate","store","ttl","validateValue","get"],"mappings":"AAAA,SAAQA,kBAAkB,EAAEC,aAAa,QAAO,mBAAkB;AAGlE,SAAQC,cAAc,QAAO,yCAAwC;AACrE,SAAQC,oBAAoB,QAAO,kCAAiC;AAEpE,OAAO,MAAMC,wBAAwB,cAAa;AAElD,OAAO,MAAMC,qBAAqB;IAAC;IAAW;IAAU;CAAQ,CAAS;AAGzE,OAAO,eAAeC,WAAWC,KAAuB;IACtD,MAAMC,SAAS,MAAMR,mBAAmB;QACtCS,YAAYL;QACZM,aAAa;IACf;IAEA,MAAMC,YAAYC,QAAQC,GAAG,CAACC,2BAA2B;IAEzD,OAAON,OAAOO,OAAO,CAAC;QACpBC,MAAM;YAACT;YAAOI;QAAS;QACvBM,MAAM;QACNC,QAAQ;QACRC,KAAK;IACP;AACF;AAEA,eAAeC;IAGb,MAAMZ,SAAS,MAAMR,mBAAmB;QACtCS,YAAYL;QACZM,aAAa;IACf;IAEA,OAAOF,OAAOO,OAAO,CAAC;QAACM,KAAK;QAAqBF,KAAK;IAA0B;AAClF;AAEA;;;;;;CAMC,GACD,OAAO,SAASG,wBAAwBC,MAAc;IACpD,OAAOlB,mBAAmBmB,QAAQ,CAACD;AACrC;AAEA;;;;;;CAMC,GACD,SAASE,0BAA0BC,QAAiB;IAClD,OACE,OAAOA,aAAa,YACpBA,aAAa,QACb,YAAYA,YACZ,OAAOA,SAASH,MAAM,KAAK,YAC3BD,wBAAwBI,SAASH,MAAM;AAE3C;AAEA,OAAO,MAAMI,+BAA+B,mBAAkB;AAC9D,MAAMC,eAAe,OAAO,KAAK;AAEjC;;;;;CAKC,GACD,OAAO,eAAeC;IAGpB,MAAMC,yBAAyB3B,qBAE5B;QACD4B,YAAY,IAAMX;QAClBY,KAAKL;QACLM;YACE/B,eAAe;QACjB;QACAgC;YACEhC,eAAe;QACjB;QACAiC;YACEjC,eAAe;QACjB;QACAkC,OAAOnC;QACPoC,KAAKT;QACLU,eAAeb;IACjB;IAEA,OAAOK,uBAAuBS,GAAG;AACnC"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Minimal interface for a config store that supports get/set/delete operations.
3
+ * Compatible with the `configstore` package.
4
+ */ /**
5
+ * Create a config in the provided config store that expires after the provided TTL.
6
+ */ export function createExpiringConfig({ fetchValue, key, onCacheHit = ()=>null, onFetch = ()=>null, onRevalidate = ()=>null, store, ttl, validateValue = (value)=>true }) {
7
+ let currentFetch = null;
8
+ return {
9
+ delete () {
10
+ store.delete(key);
11
+ },
12
+ async get () {
13
+ const stored = store.get(key);
14
+ if (isExpiringValue(stored)) {
15
+ const { updatedAt, value } = stored;
16
+ if (!validateValue(value)) {
17
+ throw new Error('Stored value is invalid');
18
+ }
19
+ const hasExpired = Date.now() - updatedAt > ttl;
20
+ if (!hasExpired) {
21
+ onCacheHit();
22
+ return value;
23
+ }
24
+ onRevalidate();
25
+ }
26
+ // Return existing fetch if one is already in progress
27
+ if (currentFetch) {
28
+ return currentFetch;
29
+ }
30
+ onFetch();
31
+ currentFetch = Promise.resolve(fetchValue());
32
+ const nextValue = await currentFetch;
33
+ if (!validateValue(nextValue)) {
34
+ throw new Error('Fetched value is invalid');
35
+ }
36
+ currentFetch = null;
37
+ store.set(key, {
38
+ updatedAt: Date.now(),
39
+ value: nextValue
40
+ });
41
+ return nextValue;
42
+ }
43
+ };
44
+ }
45
+ /**
46
+ * Checks if the given stored value is valid (does not check if expired, only verified shape)
47
+ *
48
+ * @param stored - The stored value to check
49
+ * @returns True if the stored value is valid
50
+ * @internal
51
+ */ function isExpiringValue(stored) {
52
+ if (typeof stored !== 'object' || stored === null || Array.isArray(stored)) {
53
+ return false;
54
+ }
55
+ if (!('updatedAt' in stored) || typeof stored.updatedAt !== 'number') {
56
+ return false;
57
+ }
58
+ if (!('value' in stored)) {
59
+ return false;
60
+ }
61
+ return true;
62
+ }
63
+
64
+ //# sourceMappingURL=createExpiringConfig.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/createExpiringConfig.ts"],"sourcesContent":["/**\n * Minimal interface for a config store that supports get/set/delete operations.\n * Compatible with the `configstore` package.\n */\ninterface ConfigStoreApi {\n delete: (key: string) => void\n get: (key: string) => unknown\n set: (key: string, value: unknown) => void\n}\n\ninterface ExpiringConfigValue {\n updatedAt: number\n value: unknown\n}\n\ninterface ExpiringConfigOptions<Type> {\n /** Fetch value */\n fetchValue: () => Promise<Type> | Type\n /** Config key */\n key: string\n /** Config store */\n store: ConfigStoreApi\n /** TTL (milliseconds) */\n ttl: number\n\n /** Subscribe to cache hit event */\n onCacheHit?: () => void\n /** Subscribe to fetch event */\n onFetch?: () => void\n /** Subscribe to revalidate event */\n onRevalidate?: () => void\n\n /**\n * Assert the fetched value is valid, or throw if invalid.\n * If none is provided, it will always accept the fetched value.\n */\n validateValue?: (value: unknown) => value is Type\n}\n\ninterface ExpiringConfigApi<Type> {\n /**\n * Delete the cached value.\n */\n delete: () => void\n /**\n * Attempt to get the cached value. If there is no cached value, or the cached value has expired,\n * fetch, cache, and return the value.\n */\n get: () => Promise<Type>\n}\n\n/**\n * Create a config in the provided config store that expires after the provided TTL.\n */\nexport function createExpiringConfig<Type>({\n fetchValue,\n key,\n onCacheHit = () => null,\n onFetch = () => null,\n onRevalidate = () => null,\n store,\n ttl,\n validateValue = (value: unknown): value is Type => true,\n}: ExpiringConfigOptions<Type>): ExpiringConfigApi<Type> {\n let currentFetch: Promise<Type> | null = null\n return {\n delete() {\n store.delete(key)\n },\n async get(): Promise<Type> {\n const stored = store.get(key)\n\n if (isExpiringValue(stored)) {\n const {updatedAt, value} = stored\n if (!validateValue(value)) {\n throw new Error('Stored value is invalid')\n }\n\n const hasExpired = Date.now() - updatedAt > ttl\n\n if (!hasExpired) {\n onCacheHit()\n return value\n }\n\n onRevalidate()\n }\n\n // Return existing fetch if one is already in progress\n if (currentFetch) {\n return currentFetch\n }\n\n onFetch()\n\n currentFetch = Promise.resolve(fetchValue())\n const nextValue = await currentFetch\n if (!validateValue(nextValue)) {\n throw new Error('Fetched value is invalid')\n }\n\n currentFetch = null\n\n store.set(key, {\n updatedAt: Date.now(),\n value: nextValue,\n })\n\n return nextValue\n },\n }\n}\n\n/**\n * Checks if the given stored value is valid (does not check if expired, only verified shape)\n *\n * @param stored - The stored value to check\n * @returns True if the stored value is valid\n * @internal\n */\nfunction isExpiringValue(stored: unknown): stored is ExpiringConfigValue {\n if (typeof stored !== 'object' || stored === null || Array.isArray(stored)) {\n return false\n }\n\n if (!('updatedAt' in stored) || typeof stored.updatedAt !== 'number') {\n return false\n }\n\n if (!('value' in stored)) {\n return false\n }\n\n return true\n}\n"],"names":["createExpiringConfig","fetchValue","key","onCacheHit","onFetch","onRevalidate","store","ttl","validateValue","value","currentFetch","delete","get","stored","isExpiringValue","updatedAt","Error","hasExpired","Date","now","Promise","resolve","nextValue","set","Array","isArray"],"mappings":"AAAA;;;CAGC,GAgDD;;CAEC,GACD,OAAO,SAASA,qBAA2B,EACzCC,UAAU,EACVC,GAAG,EACHC,aAAa,IAAM,IAAI,EACvBC,UAAU,IAAM,IAAI,EACpBC,eAAe,IAAM,IAAI,EACzBC,KAAK,EACLC,GAAG,EACHC,gBAAgB,CAACC,QAAkC,IAAI,EAC3B;IAC5B,IAAIC,eAAqC;IACzC,OAAO;QACLC;YACEL,MAAMK,MAAM,CAACT;QACf;QACA,MAAMU;YACJ,MAAMC,SAASP,MAAMM,GAAG,CAACV;YAEzB,IAAIY,gBAAgBD,SAAS;gBAC3B,MAAM,EAACE,SAAS,EAAEN,KAAK,EAAC,GAAGI;gBAC3B,IAAI,CAACL,cAAcC,QAAQ;oBACzB,MAAM,IAAIO,MAAM;gBAClB;gBAEA,MAAMC,aAAaC,KAAKC,GAAG,KAAKJ,YAAYR;gBAE5C,IAAI,CAACU,YAAY;oBACfd;oBACA,OAAOM;gBACT;gBAEAJ;YACF;YAEA,sDAAsD;YACtD,IAAIK,cAAc;gBAChB,OAAOA;YACT;YAEAN;YAEAM,eAAeU,QAAQC,OAAO,CAACpB;YAC/B,MAAMqB,YAAY,MAAMZ;YACxB,IAAI,CAACF,cAAcc,YAAY;gBAC7B,MAAM,IAAIN,MAAM;YAClB;YAEAN,eAAe;YAEfJ,MAAMiB,GAAG,CAACrB,KAAK;gBACba,WAAWG,KAAKC,GAAG;gBACnBV,OAAOa;YACT;YAEA,OAAOA;QACT;IACF;AACF;AAEA;;;;;;CAMC,GACD,SAASR,gBAAgBD,MAAe;IACtC,IAAI,OAAOA,WAAW,YAAYA,WAAW,QAAQW,MAAMC,OAAO,CAACZ,SAAS;QAC1E,OAAO;IACT;IAEA,IAAI,CAAE,CAAA,eAAeA,MAAK,KAAM,OAAOA,OAAOE,SAAS,KAAK,UAAU;QACpE,OAAO;IACT;IAEA,IAAI,CAAE,CAAA,WAAWF,MAAK,GAAI;QACxB,OAAO;IACT;IAEA,OAAO;AACT"}
@@ -0,0 +1,135 @@
1
+ import { readFile, stat } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ async function fileExists(filePath) {
4
+ try {
5
+ await stat(filePath);
6
+ return true;
7
+ } catch {
8
+ return false;
9
+ }
10
+ }
11
+ async function isFile(filePath) {
12
+ try {
13
+ const s = await stat(filePath);
14
+ return s.isFile();
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+ async function checkDetector(rootPath, framework, detector) {
20
+ let { matchContent, path: filePath } = detector;
21
+ const { matchPackage } = detector;
22
+ if (matchPackage && matchContent) {
23
+ throw new Error(`Cannot specify "matchPackage" and "matchContent" in the same detector for "${framework.slug}"`);
24
+ }
25
+ if (matchPackage && filePath) {
26
+ throw new Error(`Cannot specify "matchPackage" and "path" in the same detector for "${framework.slug}"`);
27
+ }
28
+ if (!filePath && !matchPackage) {
29
+ throw new Error(`Must specify either "path" or "matchPackage" in detector for "${framework.slug}".`);
30
+ }
31
+ if (!filePath) {
32
+ filePath = 'package.json';
33
+ }
34
+ if (matchPackage) {
35
+ const escapedPkg = matchPackage.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
36
+ matchContent = String.raw`"(dev)?(d|D)ependencies":\s*{[^}]*"${escapedPkg}":\s*"(.+?)"[^}]*}`;
37
+ }
38
+ const fullPath = join(rootPath, filePath);
39
+ if (!await fileExists(fullPath)) {
40
+ return undefined;
41
+ }
42
+ if (matchContent) {
43
+ if (!await isFile(fullPath)) {
44
+ return undefined;
45
+ }
46
+ const content = await readFile(fullPath, 'utf8');
47
+ const match = content.match(new RegExp(matchContent, 'm'));
48
+ if (!match) {
49
+ return undefined;
50
+ }
51
+ if (matchPackage && match[3]) {
52
+ return {
53
+ detectedVersion: match[3],
54
+ framework
55
+ };
56
+ }
57
+ }
58
+ return {
59
+ framework
60
+ };
61
+ }
62
+ async function matchFramework(rootPath, framework) {
63
+ const { detectors } = framework;
64
+ if (!detectors) return undefined;
65
+ const { every, some } = detectors;
66
+ if (every !== undefined && !Array.isArray(every)) return undefined;
67
+ if (some !== undefined && !Array.isArray(some)) return undefined;
68
+ const results = [];
69
+ if (every) {
70
+ const everyResults = await Promise.all(every.map((item)=>checkDetector(rootPath, framework, item)));
71
+ results.push(...everyResults);
72
+ }
73
+ if (some) {
74
+ let someResult;
75
+ for (const item of some){
76
+ const result = await checkDetector(rootPath, framework, item);
77
+ if (result) {
78
+ someResult = result;
79
+ break;
80
+ }
81
+ }
82
+ results.push(someResult);
83
+ }
84
+ if (results.length === 0) return undefined;
85
+ if (!results.every((r)=>r !== undefined)) {
86
+ return undefined;
87
+ }
88
+ const detectedVersion = results.find((r)=>r !== undefined && r.detectedVersion !== undefined)?.detectedVersion;
89
+ return {
90
+ detectedVersion,
91
+ framework
92
+ };
93
+ }
94
+ function removeSupersededFrameworks(matches) {
95
+ // Snapshot to avoid mutation-during-iteration when splice shifts elements
96
+ const snapshot = [
97
+ ...matches
98
+ ];
99
+ for (const match of snapshot){
100
+ if (match?.supersedes) {
101
+ for (const slug of match.supersedes){
102
+ removeSupersededFramework(matches, slug);
103
+ }
104
+ }
105
+ }
106
+ }
107
+ function removeSupersededFramework(matches, slug) {
108
+ const index = matches.findIndex((f)=>f?.slug === slug);
109
+ const framework = matches[index];
110
+ if (framework) {
111
+ matches.splice(index, 1);
112
+ if (framework.supersedes) {
113
+ for (const s of framework.supersedes){
114
+ removeSupersededFramework(matches, s);
115
+ }
116
+ }
117
+ }
118
+ }
119
+ export async function detectFrameworkRecord(options) {
120
+ const { frameworkList, rootPath } = options;
121
+ const results = await Promise.all(frameworkList.map(async (fw)=>{
122
+ const match = await matchFramework(rootPath, fw);
123
+ if (match) {
124
+ return {
125
+ ...fw,
126
+ detectedVersion: match.detectedVersion
127
+ };
128
+ }
129
+ return null;
130
+ }));
131
+ removeSupersededFrameworks(results);
132
+ return results.find((r)=>r !== null) ?? null;
133
+ }
134
+
135
+ //# sourceMappingURL=detectFramework.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/detectFramework.ts"],"sourcesContent":["import {readFile, stat} from 'node:fs/promises'\nimport {join} from 'node:path'\n\nimport {type Framework} from '@vercel/frameworks'\n\ntype VersionedFramework = Framework & {detectedVersion?: string}\n\ninterface DetectorMatch {\n framework: Framework\n\n detectedVersion?: string\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await stat(filePath)\n return true\n } catch {\n return false\n }\n}\n\nasync function isFile(filePath: string): Promise<boolean> {\n try {\n const s = await stat(filePath)\n return s.isFile()\n } catch {\n return false\n }\n}\n\nasync function checkDetector(\n rootPath: string,\n framework: Framework,\n detector: {matchContent?: string; matchPackage?: string; path?: string},\n): Promise<DetectorMatch | undefined> {\n let {matchContent, path: filePath} = detector\n const {matchPackage} = detector\n\n if (matchPackage && matchContent) {\n throw new Error(\n `Cannot specify \"matchPackage\" and \"matchContent\" in the same detector for \"${framework.slug}\"`,\n )\n }\n if (matchPackage && filePath) {\n throw new Error(\n `Cannot specify \"matchPackage\" and \"path\" in the same detector for \"${framework.slug}\"`,\n )\n }\n if (!filePath && !matchPackage) {\n throw new Error(\n `Must specify either \"path\" or \"matchPackage\" in detector for \"${framework.slug}\".`,\n )\n }\n\n if (!filePath) {\n filePath = 'package.json'\n }\n\n if (matchPackage) {\n const escapedPkg = matchPackage.replaceAll(/[.*+?^${}()|[\\]\\\\]/g, String.raw`\\$&`)\n matchContent = String.raw`\"(dev)?(d|D)ependencies\":\\s*{[^}]*\"${escapedPkg}\":\\s*\"(.+?)\"[^}]*}`\n }\n\n const fullPath = join(rootPath, filePath)\n\n if (!(await fileExists(fullPath))) {\n return undefined\n }\n\n if (matchContent) {\n if (!(await isFile(fullPath))) {\n return undefined\n }\n const content = await readFile(fullPath, 'utf8')\n const match = content.match(new RegExp(matchContent, 'm'))\n if (!match) {\n return undefined\n }\n if (matchPackage && match[3]) {\n return {detectedVersion: match[3], framework}\n }\n }\n\n return {framework}\n}\n\nasync function matchFramework(\n rootPath: string,\n framework: Framework,\n): Promise<DetectorMatch | undefined> {\n const {detectors} = framework\n if (!detectors) return undefined\n\n const {every, some} = detectors\n if (every !== undefined && !Array.isArray(every)) return undefined\n if (some !== undefined && !Array.isArray(some)) return undefined\n\n const results: (DetectorMatch | undefined)[] = []\n\n if (every) {\n const everyResults = await Promise.all(\n every.map((item) => checkDetector(rootPath, framework, item)),\n )\n results.push(...everyResults)\n }\n\n if (some) {\n let someResult: DetectorMatch | undefined\n for (const item of some) {\n const result = await checkDetector(rootPath, framework, item)\n if (result) {\n someResult = result\n break\n }\n }\n results.push(someResult)\n }\n\n if (results.length === 0) return undefined\n\n if (!results.every((r) => r !== undefined)) {\n return undefined\n }\n\n const detectedVersion = results.find(\n (r): r is DetectorMatch => r !== undefined && r.detectedVersion !== undefined,\n )?.detectedVersion\n\n return {detectedVersion, framework}\n}\n\nfunction removeSupersededFrameworks(matches: (Framework | null)[]): void {\n // Snapshot to avoid mutation-during-iteration when splice shifts elements\n const snapshot = [...matches]\n for (const match of snapshot) {\n if (match?.supersedes) {\n for (const slug of match.supersedes) {\n removeSupersededFramework(matches, slug)\n }\n }\n }\n}\n\nfunction removeSupersededFramework(matches: (Framework | null)[], slug: string): void {\n const index = matches.findIndex((f) => f?.slug === slug)\n const framework = matches[index]\n if (framework) {\n matches.splice(index, 1)\n if (framework.supersedes) {\n for (const s of framework.supersedes) {\n removeSupersededFramework(matches, s)\n }\n }\n }\n}\n\nexport async function detectFrameworkRecord(options: {\n frameworkList: readonly Framework[]\n rootPath: string\n}): Promise<VersionedFramework | null> {\n const {frameworkList, rootPath} = options\n\n const results = await Promise.all(\n frameworkList.map(async (fw): Promise<VersionedFramework | null> => {\n const match = await matchFramework(rootPath, fw)\n if (match) {\n return {...fw, detectedVersion: match.detectedVersion}\n }\n return null\n }),\n )\n\n removeSupersededFrameworks(results)\n return results.find((r) => r !== null) ?? null\n}\n"],"names":["readFile","stat","join","fileExists","filePath","isFile","s","checkDetector","rootPath","framework","detector","matchContent","path","matchPackage","Error","slug","escapedPkg","replaceAll","String","raw","fullPath","undefined","content","match","RegExp","detectedVersion","matchFramework","detectors","every","some","Array","isArray","results","everyResults","Promise","all","map","item","push","someResult","result","length","r","find","removeSupersededFrameworks","matches","snapshot","supersedes","removeSupersededFramework","index","findIndex","f","splice","detectFrameworkRecord","options","frameworkList","fw"],"mappings":"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"}
@@ -3,13 +3,12 @@ import readline from 'node:readline';
3
3
  import { Readable } from 'node:stream';
4
4
  import zlib from 'node:zlib';
5
5
  import tar from 'tar-stream';
6
+ import { isTar } from './isTar.js';
6
7
  const HEADER_SIZE = 300;
7
8
  // https://github.com/kevva/is-gzip/blob/13dab7c877787bd5cff9de5482b1736f00df99c6/index.js
8
9
  const isGzip = (buf)=>buf.length >= 3 && buf[0] === 0x1f && buf[1] === 0x8b && buf[2] === 0x08;
9
10
  // https://github.com/watson/is-deflate/blob/f9e8f0c7814eed715e13e29e97c69acee319686a/index.js
10
11
  const isDeflate = (buf)=>buf.length >= 2 && buf[0] === 0x78 && (buf[1] === 1 || buf[1] === 0x9c || buf[1] === 0xda);
11
- // https://github.com/kevva/is-tar/blob/d295ffa2002a5d415946fc3d49f024ace8c28bd3/index.js
12
- const isTar = (buf)=>buf.length >= 262 && buf[257] === 0x75 && buf[258] === 0x73 && buf[259] === 0x74 && buf[260] === 0x61 && buf[261] === 0x72;
13
12
  async function* extract(stream, extractor) {
14
13
  // set up a task to drain the input iterable into the extractor asynchronously
15
14
  // before this function delegates to the extractor's iterable (containing the
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/extractDocumentsFromNdjsonOrTarball.ts"],"sourcesContent":["import path from 'node:path'\nimport readline from 'node:readline'\nimport {Readable, type Writable} from 'node:stream'\nimport zlib from 'node:zlib'\n\nimport {type SanityDocument} from '@sanity/types'\nimport tar from 'tar-stream'\n\nconst HEADER_SIZE = 300\n\n// https://github.com/kevva/is-gzip/blob/13dab7c877787bd5cff9de5482b1736f00df99c6/index.js\nconst isGzip = (buf: Buffer) =>\n buf.length >= 3 && buf[0] === 0x1f && buf[1] === 0x8b && buf[2] === 0x08\n\n// https://github.com/watson/is-deflate/blob/f9e8f0c7814eed715e13e29e97c69acee319686a/index.js\nconst isDeflate = (buf: Buffer) =>\n buf.length >= 2 && buf[0] === 0x78 && (buf[1] === 1 || buf[1] === 0x9c || buf[1] === 0xda)\n\n// https://github.com/kevva/is-tar/blob/d295ffa2002a5d415946fc3d49f024ace8c28bd3/index.js\nconst isTar = (buf: Buffer) =>\n buf.length >= 262 &&\n buf[257] === 0x75 &&\n buf[258] === 0x73 &&\n buf[259] === 0x74 &&\n buf[260] === 0x61 &&\n buf[261] === 0x72\n\nasync function* extract<TReturn>(\n stream: AsyncIterable<Buffer>,\n extractor: AsyncIterable<TReturn> & Writable,\n) {\n // set up a task to drain the input iterable into the extractor asynchronously\n // before this function delegates to the extractor's iterable (containing the\n // result of the extraction)\n const drained = new Promise<void>((resolve, reject) => {\n // setTimeout is used here to ensure draining occurs after delegation\n setTimeout(async () => {\n try {\n for await (const chunk of stream) extractor.write(chunk)\n extractor.end()\n resolve()\n } catch (err) {\n reject(err)\n }\n })\n })\n\n // have this function delegate the results of the extractor\n yield* extractor\n await drained\n extractor.destroy()\n}\n\n/**\n * Given a async iterable of buffers, looks at the header of the file in the\n * first few bytes to see the file type then extracts the contents tries again.\n * If the given iterable of buffers is a tarball then it looks for an ndjson\n * files and returns another iterable of buffers with the contents of the\n * ndjson file\n */\nasync function* maybeExtractNdjson(stream: AsyncIterable<Buffer>): AsyncIterable<Buffer> {\n let buffer = Buffer.alloc(0)\n\n for await (const chunk of stream) {\n buffer = Buffer.concat([buffer, chunk])\n if (buffer.length < HEADER_SIZE) continue\n\n const fileHeader = buffer\n const restOfStream = async function* restOfStream() {\n yield fileHeader\n yield* stream\n }\n\n if (isGzip(fileHeader)) {\n yield* maybeExtractNdjson(extract(restOfStream(), zlib.createGunzip()))\n return\n }\n\n if (isDeflate(fileHeader)) {\n yield* maybeExtractNdjson(extract(restOfStream(), zlib.createDeflate()))\n return\n }\n\n if (isTar(fileHeader)) {\n for await (const entry of extract(restOfStream(), tar.extract())) {\n const filename = path.basename(entry.header.name)\n const extname = path.extname(filename).toLowerCase()\n // ignore hidden and non-ndjson files\n if (extname !== '.ndjson' || filename.startsWith('.')) continue\n\n for await (const ndjsonChunk of entry) yield ndjsonChunk\n return\n }\n }\n\n yield* restOfStream()\n }\n}\n\n/**\n * Takes in an async iterable of buffers from an ndjson file or tarball and\n * returns an async iterable of sanity documents.\n */\nexport async function* extractDocumentsFromNdjsonOrTarball(\n file: AsyncIterable<Buffer>,\n): AsyncIterable<SanityDocument> {\n const lines = readline.createInterface({\n input: Readable.from(maybeExtractNdjson(file)),\n })\n\n for await (const line of lines) {\n const trimmed = line.trim()\n if (trimmed) yield JSON.parse(trimmed) as SanityDocument\n }\n lines.close()\n}\n"],"names":["path","readline","Readable","zlib","tar","HEADER_SIZE","isGzip","buf","length","isDeflate","isTar","extract","stream","extractor","drained","Promise","resolve","reject","setTimeout","chunk","write","end","err","destroy","maybeExtractNdjson","buffer","Buffer","alloc","concat","fileHeader","restOfStream","createGunzip","createDeflate","entry","filename","basename","header","name","extname","toLowerCase","startsWith","ndjsonChunk","extractDocumentsFromNdjsonOrTarball","file","lines","createInterface","input","from","line","trimmed","trim","JSON","parse","close"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAC5B,OAAOC,cAAc,gBAAe;AACpC,SAAQC,QAAQ,QAAsB,cAAa;AACnD,OAAOC,UAAU,YAAW;AAG5B,OAAOC,SAAS,aAAY;AAE5B,MAAMC,cAAc;AAEpB,0FAA0F;AAC1F,MAAMC,SAAS,CAACC,MACdA,IAAIC,MAAM,IAAI,KAAKD,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK;AAEtE,8FAA8F;AAC9F,MAAME,YAAY,CAACF,MACjBA,IAAIC,MAAM,IAAI,KAAKD,GAAG,CAAC,EAAE,KAAK,QAASA,CAAAA,GAAG,CAAC,EAAE,KAAK,KAAKA,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK,IAAG;AAE1F,yFAAyF;AACzF,MAAMG,QAAQ,CAACH,MACbA,IAAIC,MAAM,IAAI,OACdD,GAAG,CAAC,IAAI,KAAK,QACbA,GAAG,CAAC,IAAI,KAAK,QACbA,GAAG,CAAC,IAAI,KAAK,QACbA,GAAG,CAAC,IAAI,KAAK,QACbA,GAAG,CAAC,IAAI,KAAK;AAEf,gBAAgBI,QACdC,MAA6B,EAC7BC,SAA4C;IAE5C,8EAA8E;IAC9E,6EAA6E;IAC7E,4BAA4B;IAC5B,MAAMC,UAAU,IAAIC,QAAc,CAACC,SAASC;QAC1C,qEAAqE;QACrEC,WAAW;YACT,IAAI;gBACF,WAAW,MAAMC,SAASP,OAAQC,UAAUO,KAAK,CAACD;gBAClDN,UAAUQ,GAAG;gBACbL;YACF,EAAE,OAAOM,KAAK;gBACZL,OAAOK;YACT;QACF;IACF;IAEA,2DAA2D;IAC3D,OAAOT;IACP,MAAMC;IACND,UAAUU,OAAO;AACnB;AAEA;;;;;;CAMC,GACD,gBAAgBC,mBAAmBZ,MAA6B;IAC9D,IAAIa,SAASC,OAAOC,KAAK,CAAC;IAE1B,WAAW,MAAMR,SAASP,OAAQ;QAChCa,SAASC,OAAOE,MAAM,CAAC;YAACH;YAAQN;SAAM;QACtC,IAAIM,OAAOjB,MAAM,GAAGH,aAAa;QAEjC,MAAMwB,aAAaJ;QACnB,MAAMK,eAAe,gBAAgBA;YACnC,MAAMD;YACN,OAAOjB;QACT;QAEA,IAAIN,OAAOuB,aAAa;YACtB,OAAOL,mBAAmBb,QAAQmB,gBAAgB3B,KAAK4B,YAAY;YACnE;QACF;QAEA,IAAItB,UAAUoB,aAAa;YACzB,OAAOL,mBAAmBb,QAAQmB,gBAAgB3B,KAAK6B,aAAa;YACpE;QACF;QAEA,IAAItB,MAAMmB,aAAa;YACrB,WAAW,MAAMI,SAAStB,QAAQmB,gBAAgB1B,IAAIO,OAAO,IAAK;gBAChE,MAAMuB,WAAWlC,KAAKmC,QAAQ,CAACF,MAAMG,MAAM,CAACC,IAAI;gBAChD,MAAMC,UAAUtC,KAAKsC,OAAO,CAACJ,UAAUK,WAAW;gBAClD,qCAAqC;gBACrC,IAAID,YAAY,aAAaJ,SAASM,UAAU,CAAC,MAAM;gBAEvD,WAAW,MAAMC,eAAeR,MAAO,MAAMQ;gBAC7C;YACF;QACF;QAEA,OAAOX;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,gBAAgBY,oCACrBC,IAA2B;IAE3B,MAAMC,QAAQ3C,SAAS4C,eAAe,CAAC;QACrCC,OAAO5C,SAAS6C,IAAI,CAACvB,mBAAmBmB;IAC1C;IAEA,WAAW,MAAMK,QAAQJ,MAAO;QAC9B,MAAMK,UAAUD,KAAKE,IAAI;QACzB,IAAID,SAAS,MAAME,KAAKC,KAAK,CAACH;IAChC;IACAL,MAAMS,KAAK;AACb"}
1
+ {"version":3,"sources":["../../src/util/extractDocumentsFromNdjsonOrTarball.ts"],"sourcesContent":["import path from 'node:path'\nimport readline from 'node:readline'\nimport {Readable, type Writable} from 'node:stream'\nimport zlib from 'node:zlib'\n\nimport {type SanityDocument} from '@sanity/types'\nimport tar from 'tar-stream'\n\nimport {isTar} from './isTar.js'\n\nconst HEADER_SIZE = 300\n\n// https://github.com/kevva/is-gzip/blob/13dab7c877787bd5cff9de5482b1736f00df99c6/index.js\nconst isGzip = (buf: Buffer) =>\n buf.length >= 3 && buf[0] === 0x1f && buf[1] === 0x8b && buf[2] === 0x08\n\n// https://github.com/watson/is-deflate/blob/f9e8f0c7814eed715e13e29e97c69acee319686a/index.js\nconst isDeflate = (buf: Buffer) =>\n buf.length >= 2 && buf[0] === 0x78 && (buf[1] === 1 || buf[1] === 0x9c || buf[1] === 0xda)\n\nasync function* extract<TReturn>(\n stream: AsyncIterable<Buffer>,\n extractor: AsyncIterable<TReturn> & Writable,\n) {\n // set up a task to drain the input iterable into the extractor asynchronously\n // before this function delegates to the extractor's iterable (containing the\n // result of the extraction)\n const drained = new Promise<void>((resolve, reject) => {\n // setTimeout is used here to ensure draining occurs after delegation\n setTimeout(async () => {\n try {\n for await (const chunk of stream) extractor.write(chunk)\n extractor.end()\n resolve()\n } catch (err) {\n reject(err)\n }\n })\n })\n\n // have this function delegate the results of the extractor\n yield* extractor\n await drained\n extractor.destroy()\n}\n\n/**\n * Given a async iterable of buffers, looks at the header of the file in the\n * first few bytes to see the file type then extracts the contents tries again.\n * If the given iterable of buffers is a tarball then it looks for an ndjson\n * files and returns another iterable of buffers with the contents of the\n * ndjson file\n */\nasync function* maybeExtractNdjson(stream: AsyncIterable<Buffer>): AsyncIterable<Buffer> {\n let buffer = Buffer.alloc(0)\n\n for await (const chunk of stream) {\n buffer = Buffer.concat([buffer, chunk])\n if (buffer.length < HEADER_SIZE) continue\n\n const fileHeader = buffer\n const restOfStream = async function* restOfStream() {\n yield fileHeader\n yield* stream\n }\n\n if (isGzip(fileHeader)) {\n yield* maybeExtractNdjson(extract(restOfStream(), zlib.createGunzip()))\n return\n }\n\n if (isDeflate(fileHeader)) {\n yield* maybeExtractNdjson(extract(restOfStream(), zlib.createDeflate()))\n return\n }\n\n if (isTar(fileHeader)) {\n for await (const entry of extract(restOfStream(), tar.extract())) {\n const filename = path.basename(entry.header.name)\n const extname = path.extname(filename).toLowerCase()\n // ignore hidden and non-ndjson files\n if (extname !== '.ndjson' || filename.startsWith('.')) continue\n\n for await (const ndjsonChunk of entry) yield ndjsonChunk\n return\n }\n }\n\n yield* restOfStream()\n }\n}\n\n/**\n * Takes in an async iterable of buffers from an ndjson file or tarball and\n * returns an async iterable of sanity documents.\n */\nexport async function* extractDocumentsFromNdjsonOrTarball(\n file: AsyncIterable<Buffer>,\n): AsyncIterable<SanityDocument> {\n const lines = readline.createInterface({\n input: Readable.from(maybeExtractNdjson(file)),\n })\n\n for await (const line of lines) {\n const trimmed = line.trim()\n if (trimmed) yield JSON.parse(trimmed) as SanityDocument\n }\n lines.close()\n}\n"],"names":["path","readline","Readable","zlib","tar","isTar","HEADER_SIZE","isGzip","buf","length","isDeflate","extract","stream","extractor","drained","Promise","resolve","reject","setTimeout","chunk","write","end","err","destroy","maybeExtractNdjson","buffer","Buffer","alloc","concat","fileHeader","restOfStream","createGunzip","createDeflate","entry","filename","basename","header","name","extname","toLowerCase","startsWith","ndjsonChunk","extractDocumentsFromNdjsonOrTarball","file","lines","createInterface","input","from","line","trimmed","trim","JSON","parse","close"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAC5B,OAAOC,cAAc,gBAAe;AACpC,SAAQC,QAAQ,QAAsB,cAAa;AACnD,OAAOC,UAAU,YAAW;AAG5B,OAAOC,SAAS,aAAY;AAE5B,SAAQC,KAAK,QAAO,aAAY;AAEhC,MAAMC,cAAc;AAEpB,0FAA0F;AAC1F,MAAMC,SAAS,CAACC,MACdA,IAAIC,MAAM,IAAI,KAAKD,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK;AAEtE,8FAA8F;AAC9F,MAAME,YAAY,CAACF,MACjBA,IAAIC,MAAM,IAAI,KAAKD,GAAG,CAAC,EAAE,KAAK,QAASA,CAAAA,GAAG,CAAC,EAAE,KAAK,KAAKA,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK,IAAG;AAE1F,gBAAgBG,QACdC,MAA6B,EAC7BC,SAA4C;IAE5C,8EAA8E;IAC9E,6EAA6E;IAC7E,4BAA4B;IAC5B,MAAMC,UAAU,IAAIC,QAAc,CAACC,SAASC;QAC1C,qEAAqE;QACrEC,WAAW;YACT,IAAI;gBACF,WAAW,MAAMC,SAASP,OAAQC,UAAUO,KAAK,CAACD;gBAClDN,UAAUQ,GAAG;gBACbL;YACF,EAAE,OAAOM,KAAK;gBACZL,OAAOK;YACT;QACF;IACF;IAEA,2DAA2D;IAC3D,OAAOT;IACP,MAAMC;IACND,UAAUU,OAAO;AACnB;AAEA;;;;;;CAMC,GACD,gBAAgBC,mBAAmBZ,MAA6B;IAC9D,IAAIa,SAASC,OAAOC,KAAK,CAAC;IAE1B,WAAW,MAAMR,SAASP,OAAQ;QAChCa,SAASC,OAAOE,MAAM,CAAC;YAACH;YAAQN;SAAM;QACtC,IAAIM,OAAOhB,MAAM,GAAGH,aAAa;QAEjC,MAAMuB,aAAaJ;QACnB,MAAMK,eAAe,gBAAgBA;YACnC,MAAMD;YACN,OAAOjB;QACT;QAEA,IAAIL,OAAOsB,aAAa;YACtB,OAAOL,mBAAmBb,QAAQmB,gBAAgB3B,KAAK4B,YAAY;YACnE;QACF;QAEA,IAAIrB,UAAUmB,aAAa;YACzB,OAAOL,mBAAmBb,QAAQmB,gBAAgB3B,KAAK6B,aAAa;YACpE;QACF;QAEA,IAAI3B,MAAMwB,aAAa;YACrB,WAAW,MAAMI,SAAStB,QAAQmB,gBAAgB1B,IAAIO,OAAO,IAAK;gBAChE,MAAMuB,WAAWlC,KAAKmC,QAAQ,CAACF,MAAMG,MAAM,CAACC,IAAI;gBAChD,MAAMC,UAAUtC,KAAKsC,OAAO,CAACJ,UAAUK,WAAW;gBAClD,qCAAqC;gBACrC,IAAID,YAAY,aAAaJ,SAASM,UAAU,CAAC,MAAM;gBAEvD,WAAW,MAAMC,eAAeR,MAAO,MAAMQ;gBAC7C;YACF;QACF;QAEA,OAAOX;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,gBAAgBY,oCACrBC,IAA2B;IAE3B,MAAMC,QAAQ3C,SAAS4C,eAAe,CAAC;QACrCC,OAAO5C,SAAS6C,IAAI,CAACvB,mBAAmBmB;IAC1C;IAEA,WAAW,MAAMK,QAAQJ,MAAO;QAC9B,MAAMK,UAAUD,KAAKE,IAAI;QACzB,IAAID,SAAS,MAAME,KAAKC,KAAK,CAACH;IAChC;IACAL,MAAMS,KAAK;AACb"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Detects errors thrown by Sanity's `resolveConfig()` / `getStudioWorkspaces()` where
3
+ * schema validation problems are embedded on the thrown error object as `schema._validation`.
4
+ *
5
+ * The return type asserts both the full `Schema` (needed by validateSchema.worker.ts) and
6
+ * that `_validation` is a `SchemaValidationProblemGroup[]` array (validated by the guard).
7
+ */ export function isSchemaError(err) {
8
+ return err !== null && typeof err === 'object' && 'schema' in err && err.schema !== null && typeof err.schema === 'object' && '_validation' in err.schema && Array.isArray(err.schema._validation);
9
+ }
10
+
11
+ //# sourceMappingURL=isSchemaError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/isSchemaError.ts"],"sourcesContent":["import {type Schema, type SchemaValidationProblemGroup} from '@sanity/types'\n\n/**\n * Detects errors thrown by Sanity's `resolveConfig()` / `getStudioWorkspaces()` where\n * schema validation problems are embedded on the thrown error object as `schema._validation`.\n *\n * The return type asserts both the full `Schema` (needed by validateSchema.worker.ts) and\n * that `_validation` is a `SchemaValidationProblemGroup[]` array (validated by the guard).\n */\nexport function isSchemaError(\n err: unknown,\n): err is {schema: Schema & {_validation: SchemaValidationProblemGroup[]}} {\n return (\n err !== null &&\n typeof err === 'object' &&\n 'schema' in err &&\n err.schema !== null &&\n typeof err.schema === 'object' &&\n '_validation' in err.schema &&\n Array.isArray(err.schema._validation)\n )\n}\n"],"names":["isSchemaError","err","schema","Array","isArray","_validation"],"mappings":"AAEA;;;;;;CAMC,GACD,OAAO,SAASA,cACdC,GAAY;IAEZ,OACEA,QAAQ,QACR,OAAOA,QAAQ,YACf,YAAYA,OACZA,IAAIC,MAAM,KAAK,QACf,OAAOD,IAAIC,MAAM,KAAK,YACtB,iBAAiBD,IAAIC,MAAM,IAC3BC,MAAMC,OAAO,CAACH,IAAIC,MAAM,CAACG,WAAW;AAExC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Checks if a buffer contains a TAR archive by looking for the `ustar` magic
3
+ * bytes at offset 257–261.
4
+ *
5
+ * @internal
6
+ */ export const isTar = (buf)=>buf.length >= 262 && buf[257] === 0x75 && buf[258] === 0x73 && buf[259] === 0x74 && buf[260] === 0x61 && buf[261] === 0x72;
7
+
8
+ //# sourceMappingURL=isTar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/util/isTar.ts"],"sourcesContent":["/**\n * Checks if a buffer contains a TAR archive by looking for the `ustar` magic\n * bytes at offset 257–261.\n *\n * @internal\n */\nexport const isTar = (buf: Buffer): boolean =>\n buf.length >= 262 &&\n buf[257] === 0x75 &&\n buf[258] === 0x73 &&\n buf[259] === 0x74 &&\n buf[260] === 0x61 &&\n buf[261] === 0x72\n"],"names":["isTar","buf","length"],"mappings":"AAAA;;;;;CAKC,GACD,OAAO,MAAMA,QAAQ,CAACC,MACpBA,IAAIC,MAAM,IAAI,OACdD,GAAG,CAAC,IAAI,KAAK,QACbA,GAAG,CAAC,IAAI,KAAK,QACbA,GAAG,CAAC,IAAI,KAAK,QACbA,GAAG,CAAC,IAAI,KAAK,QACbA,GAAG,CAAC,IAAI,KAAK,KAAI"}
@@ -1,19 +1,49 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  /**
3
- * Shared `--project-id` / `-p` flag for commands that can operate on
4
- * a project without requiring a local Sanity project directory.
5
- */ export const projectIdFlag = {
6
- 'project-id': Flags.string({
7
- char: 'p',
8
- description: 'Project ID to use. Overrides the project ID from the Sanity config.',
9
- parse: async (input)=>{
10
- const trimmed = input.trim();
11
- if (trimmed === '') {
12
- throw new Error('Project ID cannot be empty');
3
+ * Returns a `--project-id` / `-p` flag definition.
4
+ *
5
+ * Locked: flag name (`project-id`), char (`p`), `helpValue` (`<id>`), and parse (trims + validates non-empty).
6
+ * All other oclif flag properties (description, etc.) can be overridden.
7
+ */ export function getProjectIdFlag(overrides) {
8
+ return {
9
+ 'project-id': Flags.string({
10
+ description: 'Project ID to use (overrides CLI configuration)',
11
+ helpGroup: 'OVERRIDE',
12
+ helpValue: '<id>',
13
+ ...overrides,
14
+ char: 'p',
15
+ parse: async (input)=>{
16
+ const trimmed = input.trim();
17
+ if (trimmed === '') {
18
+ throw new Error('`--project-id` cannot be empty if provided');
19
+ }
20
+ return trimmed;
13
21
  }
14
- return trimmed;
15
- }
16
- })
17
- };
22
+ })
23
+ };
24
+ }
25
+ /**
26
+ * Returns a `--dataset` / `-d` flag definition.
27
+ *
28
+ * Locked: flag name (`dataset`), char (`d`), `helpValue` (`<name>`), and parse (trims + validates non-empty).
29
+ * All other oclif flag properties (description, etc.) can be overridden.
30
+ */ export function getDatasetFlag(overrides) {
31
+ return {
32
+ dataset: Flags.string({
33
+ description: 'Dataset to use (overrides CLI configuration)',
34
+ helpGroup: 'OVERRIDE',
35
+ helpValue: '<name>',
36
+ ...overrides,
37
+ char: 'd',
38
+ parse: async (input)=>{
39
+ const trimmed = input.trim();
40
+ if (trimmed === '') {
41
+ throw new Error('`--dataset` cannot be empty if provided');
42
+ }
43
+ return trimmed;
44
+ }
45
+ })
46
+ };
47
+ }
18
48
 
19
49
  //# sourceMappingURL=sharedFlags.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/sharedFlags.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\n\n/**\n * Shared `--project-id` / `-p` flag for commands that can operate on\n * a project without requiring a local Sanity project directory.\n */\nexport const projectIdFlag = {\n 'project-id': Flags.string({\n char: 'p',\n description: 'Project ID to use. Overrides the project ID from the Sanity config.',\n parse: async (input) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('Project ID cannot be empty')\n }\n return trimmed\n },\n }),\n}\n"],"names":["Flags","projectIdFlag","string","char","description","parse","input","trimmed","trim","Error"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AAEjC;;;CAGC,GACD,OAAO,MAAMC,gBAAgB;IAC3B,cAAcD,MAAME,MAAM,CAAC;QACzBC,MAAM;QACNC,aAAa;QACbC,OAAO,OAAOC;YACZ,MAAMC,UAAUD,MAAME,IAAI;YAC1B,IAAID,YAAY,IAAI;gBAClB,MAAM,IAAIE,MAAM;YAClB;YACA,OAAOF;QACT;IACF;AACF,EAAC"}
1
+ {"version":3,"sources":["../../src/util/sharedFlags.ts"],"sourcesContent":["import {Flags} from '@oclif/core'\n\n/**\n * Properties that callers may override when using shared flag getters.\n * Locked properties (char, parse, name, helpValue) are excluded to ensure\n * consistent behavior across all commands.\n */\ninterface FlagOverrides {\n dependsOn?: string[]\n description?: string\n env?: string\n exclusive?: string[]\n helpGroup?: string\n hidden?: boolean\n required?: boolean\n}\n\n/**\n * Returns a `--project-id` / `-p` flag definition.\n *\n * Locked: flag name (`project-id`), char (`p`), `helpValue` (`<id>`), and parse (trims + validates non-empty).\n * All other oclif flag properties (description, etc.) can be overridden.\n */\nexport function getProjectIdFlag(overrides?: FlagOverrides) {\n return {\n 'project-id': Flags.string({\n description: 'Project ID to use (overrides CLI configuration)',\n helpGroup: 'OVERRIDE',\n helpValue: '<id>',\n ...overrides,\n char: 'p',\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--project-id` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n }\n}\n\n/**\n * Returns a `--dataset` / `-d` flag definition.\n *\n * Locked: flag name (`dataset`), char (`d`), `helpValue` (`<name>`), and parse (trims + validates non-empty).\n * All other oclif flag properties (description, etc.) can be overridden.\n */\nexport function getDatasetFlag(overrides?: FlagOverrides) {\n return {\n dataset: Flags.string({\n description: 'Dataset to use (overrides CLI configuration)',\n helpGroup: 'OVERRIDE',\n helpValue: '<name>',\n ...overrides,\n char: 'd',\n parse: async (input: string) => {\n const trimmed = input.trim()\n if (trimmed === '') {\n throw new Error('`--dataset` cannot be empty if provided')\n }\n return trimmed\n },\n }),\n }\n}\n"],"names":["Flags","getProjectIdFlag","overrides","string","description","helpGroup","helpValue","char","parse","input","trimmed","trim","Error","getDatasetFlag","dataset"],"mappings":"AAAA,SAAQA,KAAK,QAAO,cAAa;AAiBjC;;;;;CAKC,GACD,OAAO,SAASC,iBAAiBC,SAAyB;IACxD,OAAO;QACL,cAAcF,MAAMG,MAAM,CAAC;YACzBC,aAAa;YACbC,WAAW;YACXC,WAAW;YACX,GAAGJ,SAAS;YACZK,MAAM;YACNC,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI;gBAC1B,IAAID,YAAY,IAAI;oBAClB,MAAM,IAAIE,MAAM;gBAClB;gBACA,OAAOF;YACT;QACF;IACF;AACF;AAEA;;;;;CAKC,GACD,OAAO,SAASG,eAAeX,SAAyB;IACtD,OAAO;QACLY,SAASd,MAAMG,MAAM,CAAC;YACpBC,aAAa;YACbC,WAAW;YACXC,WAAW;YACX,GAAGJ,SAAS;YACZK,MAAM;YACNC,OAAO,OAAOC;gBACZ,MAAMC,UAAUD,MAAME,IAAI;gBAC1B,IAAID,YAAY,IAAI;oBAClB,MAAM,IAAIE,MAAM;gBAClB;gBACA,OAAOF;YACT;QACF;IACF;AACF"}
@@ -0,0 +1,30 @@
1
+ import { rm, stat } from 'node:fs/promises';
2
+ import { findTelemetryFiles } from './findTelemetryFiles.js';
3
+ import { telemetryStoreDebug } from './telemetryStoreDebug.js';
4
+ /**
5
+ * Cleans up telemetry files older than the specified number of days
6
+ * @internal
7
+ */ export async function cleanupOldTelemetryFiles(maxAgeDays = 7) {
8
+ try {
9
+ const files = await findTelemetryFiles();
10
+ const cutoffTime = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000;
11
+ for (const filePath of files){
12
+ try {
13
+ const stats = await stat(filePath);
14
+ if (stats.mtime.getTime() < cutoffTime) {
15
+ telemetryStoreDebug('Cleaning up old telemetry file: %s', filePath);
16
+ await rm(filePath, {
17
+ force: true
18
+ });
19
+ }
20
+ } catch (error) {
21
+ telemetryStoreDebug('Error checking/removing old file %s: %o', filePath, error);
22
+ }
23
+ }
24
+ } catch (error) {
25
+ telemetryStoreDebug('Error during cleanup: %o', error);
26
+ // Don't throw - cleanup is best effort
27
+ }
28
+ }
29
+
30
+ //# sourceMappingURL=cleanupOldTelemetryFiles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/util/telemetry/cleanupOldTelemetryFiles.ts"],"sourcesContent":["import {rm, stat} from 'node:fs/promises'\n\nimport {findTelemetryFiles} from './findTelemetryFiles.js'\nimport {telemetryStoreDebug} from './telemetryStoreDebug.js'\n\n/**\n * Cleans up telemetry files older than the specified number of days\n * @internal\n */\nexport async function cleanupOldTelemetryFiles(maxAgeDays: number = 7): Promise<void> {\n try {\n const files = await findTelemetryFiles()\n const cutoffTime = Date.now() - maxAgeDays * 24 * 60 * 60 * 1000\n\n for (const filePath of files) {\n try {\n const stats = await stat(filePath)\n if (stats.mtime.getTime() < cutoffTime) {\n telemetryStoreDebug('Cleaning up old telemetry file: %s', filePath)\n await rm(filePath, {force: true})\n }\n } catch (error) {\n telemetryStoreDebug('Error checking/removing old file %s: %o', filePath, error)\n }\n }\n } catch (error) {\n telemetryStoreDebug('Error during cleanup: %o', error)\n // Don't throw - cleanup is best effort\n }\n}\n"],"names":["rm","stat","findTelemetryFiles","telemetryStoreDebug","cleanupOldTelemetryFiles","maxAgeDays","files","cutoffTime","Date","now","filePath","stats","mtime","getTime","force","error"],"mappings":"AAAA,SAAQA,EAAE,EAAEC,IAAI,QAAO,mBAAkB;AAEzC,SAAQC,kBAAkB,QAAO,0BAAyB;AAC1D,SAAQC,mBAAmB,QAAO,2BAA0B;AAE5D;;;CAGC,GACD,OAAO,eAAeC,yBAAyBC,aAAqB,CAAC;IACnE,IAAI;QACF,MAAMC,QAAQ,MAAMJ;QACpB,MAAMK,aAAaC,KAAKC,GAAG,KAAKJ,aAAa,KAAK,KAAK,KAAK;QAE5D,KAAK,MAAMK,YAAYJ,MAAO;YAC5B,IAAI;gBACF,MAAMK,QAAQ,MAAMV,KAAKS;gBACzB,IAAIC,MAAMC,KAAK,CAACC,OAAO,KAAKN,YAAY;oBACtCJ,oBAAoB,sCAAsCO;oBAC1D,MAAMV,GAAGU,UAAU;wBAACI,OAAO;oBAAI;gBACjC;YACF,EAAE,OAAOC,OAAO;gBACdZ,oBAAoB,2CAA2CO,UAAUK;YAC3E;QACF;IACF,EAAE,OAAOA,OAAO;QACdZ,oBAAoB,4BAA4BY;IAChD,uCAAuC;IACzC;AACF"}