@sanity/cli 6.0.0-alpha.3 → 6.0.0-alpha.4

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 (110) hide show
  1. package/dist/actions/auth/login/{index.js → login.js} +1 -1
  2. package/dist/actions/auth/login/{index.js.map → login.js.map} +1 -1
  3. package/dist/actions/organizations/getOrganizationChoices.d.ts +6 -0
  4. package/dist/actions/organizations/getOrganizationChoices.js +23 -0
  5. package/dist/actions/organizations/getOrganizationChoices.js.map +1 -0
  6. package/dist/actions/organizations/getOrganizationsWithAttachGrantInfo.d.ts +2 -0
  7. package/dist/actions/organizations/getOrganizationsWithAttachGrantInfo.js +9 -0
  8. package/dist/actions/organizations/getOrganizationsWithAttachGrantInfo.js.map +1 -0
  9. package/dist/actions/organizations/hasProjectAttachGrant.d.ts +1 -0
  10. package/dist/actions/organizations/hasProjectAttachGrant.js +24 -0
  11. package/dist/actions/organizations/hasProjectAttachGrant.js.map +1 -0
  12. package/dist/actions/schema/utils/schemaStoreValidation.js +2 -2
  13. package/dist/actions/schema/utils/schemaStoreValidation.js.map +1 -1
  14. package/dist/commands/__tests__/init/init.authentication.test.js +60 -0
  15. package/dist/commands/__tests__/init/init.authentication.test.js.map +1 -0
  16. package/dist/commands/__tests__/init/init.create-new-project.test.js +196 -0
  17. package/dist/commands/__tests__/init/init.create-new-project.test.js.map +1 -0
  18. package/dist/commands/__tests__/init/init.plan.test.js +220 -0
  19. package/dist/commands/__tests__/init/init.plan.test.js.map +1 -0
  20. package/dist/commands/__tests__/init/init.setup.test.js +279 -0
  21. package/dist/commands/__tests__/init/init.setup.test.js.map +1 -0
  22. package/dist/commands/__tests__/migration.test.js +119 -0
  23. package/dist/commands/__tests__/migration.test.js.map +1 -0
  24. package/dist/commands/backup/__tests__/download.test.js +3 -3
  25. package/dist/commands/backup/__tests__/download.test.js.map +1 -1
  26. package/dist/commands/dataset/__tests__/import.test.js +2 -2
  27. package/dist/commands/dataset/__tests__/import.test.js.map +1 -1
  28. package/dist/commands/documents/__tests__/query.test.js +3 -3
  29. package/dist/commands/documents/__tests__/query.test.js.map +1 -1
  30. package/dist/commands/init.d.ts +4 -0
  31. package/dist/commands/init.js +151 -18
  32. package/dist/commands/init.js.map +1 -1
  33. package/dist/commands/login.js +1 -1
  34. package/dist/commands/login.js.map +1 -1
  35. package/dist/services/organizations.d.ts +40 -0
  36. package/dist/services/organizations.js +41 -0
  37. package/dist/services/organizations.js.map +1 -0
  38. package/dist/services/projects.d.ts +20 -0
  39. package/dist/services/projects.js +30 -1
  40. package/dist/services/projects.js.map +1 -1
  41. package/dist/services/user.d.ts +2 -0
  42. package/dist/services/user.js +11 -0
  43. package/dist/services/user.js.map +1 -0
  44. package/oclif.config.js +6 -1
  45. package/oclif.manifest.json +33 -184
  46. package/package.json +7 -7
  47. package/dist/actions/migration/getMigrationRootDirectory.d.ts +0 -2
  48. package/dist/actions/migration/getMigrationRootDirectory.js +0 -14
  49. package/dist/actions/migration/getMigrationRootDirectory.js.map +0 -1
  50. package/dist/actions/migration/resolveMigrations.d.ts +0 -19
  51. package/dist/actions/migration/resolveMigrations.js +0 -43
  52. package/dist/actions/migration/resolveMigrations.js.map +0 -1
  53. package/dist/actions/migration/templates/__tests__/minimalAdvanced.test.js +0 -65
  54. package/dist/actions/migration/templates/__tests__/minimalAdvanced.test.js.map +0 -1
  55. package/dist/actions/migration/templates/__tests__/minimalSimple.test.js +0 -145
  56. package/dist/actions/migration/templates/__tests__/minimalSimple.test.js.map +0 -1
  57. package/dist/actions/migration/templates/__tests__/renameField.test.js +0 -63
  58. package/dist/actions/migration/templates/__tests__/renameField.test.js.map +0 -1
  59. package/dist/actions/migration/templates/__tests__/renameType.test.js +0 -61
  60. package/dist/actions/migration/templates/__tests__/renameType.test.js.map +0 -1
  61. package/dist/actions/migration/templates/__tests__/stringToPTE.test.js +0 -87
  62. package/dist/actions/migration/templates/__tests__/stringToPTE.test.js.map +0 -1
  63. package/dist/actions/migration/templates/index.d.ts +0 -5
  64. package/dist/actions/migration/templates/index.js +0 -7
  65. package/dist/actions/migration/templates/index.js.map +0 -1
  66. package/dist/actions/migration/templates/minimalAdvanced.d.ts +0 -4
  67. package/dist/actions/migration/templates/minimalAdvanced.js +0 -21
  68. package/dist/actions/migration/templates/minimalAdvanced.js.map +0 -1
  69. package/dist/actions/migration/templates/minimalSimple.d.ts +0 -4
  70. package/dist/actions/migration/templates/minimalSimple.js +0 -61
  71. package/dist/actions/migration/templates/minimalSimple.js.map +0 -1
  72. package/dist/actions/migration/templates/renameField.d.ts +0 -4
  73. package/dist/actions/migration/templates/renameField.js +0 -20
  74. package/dist/actions/migration/templates/renameField.js.map +0 -1
  75. package/dist/actions/migration/templates/renameType.d.ts +0 -4
  76. package/dist/actions/migration/templates/renameType.js +0 -19
  77. package/dist/actions/migration/templates/renameType.js.map +0 -1
  78. package/dist/actions/migration/templates/stringToPTE.d.ts +0 -4
  79. package/dist/actions/migration/templates/stringToPTE.js +0 -32
  80. package/dist/actions/migration/templates/stringToPTE.js.map +0 -1
  81. package/dist/commands/__tests__/init.test.js +0 -411
  82. package/dist/commands/__tests__/init.test.js.map +0 -1
  83. package/dist/commands/migration/__tests__/create.test.js +0 -296
  84. package/dist/commands/migration/__tests__/create.test.js.map +0 -1
  85. package/dist/commands/migration/__tests__/list.test.js +0 -166
  86. package/dist/commands/migration/__tests__/list.test.js.map +0 -1
  87. package/dist/commands/migration/__tests__/run.test.js +0 -481
  88. package/dist/commands/migration/__tests__/run.test.js.map +0 -1
  89. package/dist/commands/migration/create.d.ts +0 -17
  90. package/dist/commands/migration/create.js +0 -143
  91. package/dist/commands/migration/create.js.map +0 -1
  92. package/dist/commands/migration/list.d.ts +0 -9
  93. package/dist/commands/migration/list.js +0 -61
  94. package/dist/commands/migration/list.js.map +0 -1
  95. package/dist/commands/migration/run.d.ts +0 -26
  96. package/dist/commands/migration/run.js +0 -271
  97. package/dist/commands/migration/run.js.map +0 -1
  98. package/dist/util/migration/constants.d.ts +0 -3
  99. package/dist/util/migration/constants.js +0 -10
  100. package/dist/util/migration/constants.js.map +0 -1
  101. package/dist/util/migration/ensureApiVersionFormat.d.ts +0 -9
  102. package/dist/util/migration/ensureApiVersionFormat.js +0 -16
  103. package/dist/util/migration/ensureApiVersionFormat.js.map +0 -1
  104. package/dist/util/migration/prettyMutationFormatter.d.ts +0 -8
  105. package/dist/util/migration/prettyMutationFormatter.js +0 -141
  106. package/dist/util/migration/prettyMutationFormatter.js.map +0 -1
  107. package/dist/utils/migration/resolveMigrationScript.d.ts +0 -44
  108. package/dist/utils/migration/resolveMigrationScript.js +0 -74
  109. package/dist/utils/migration/resolveMigrationScript.js.map +0 -1
  110. /package/dist/actions/auth/login/{index.d.ts → login.d.ts} +0 -0
@@ -90,4 +90,4 @@ const LOGIN_API_VERSION = '2024-02-01';
90
90
  }
91
91
  }
92
92
 
93
- //# sourceMappingURL=index.js.map
93
+ //# sourceMappingURL=login.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/auth/login/index.ts"],"sourcesContent":["import {getCliToken, getGlobalCliClient, type Output, setConfig} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport open from 'open'\n\nimport {canLaunchBrowser} from '../../../util/canLaunchBrowser.js'\nimport {startServerForTokenCallback} from '../authServer.js'\nimport {getProvider} from './getProvider.js'\n\nconst LOGIN_API_VERSION = '2024-02-01'\n\ninterface LoginOptions {\n output: Output\n\n experimental?: boolean\n open?: boolean\n provider?: string\n sso?: string\n}\n\n/**\n * Trigger the authentication flow for the CLI.\n *\n * NOTE: This uses terminal prompts and will not work for non-interactive/programmatic uses.\n *\n * @param options - Options for the login operation\n * @returns Promise that resolves when the login operation is complete\n * @throws Will throw if login fails or is cancelled\n * @internal\n */\nexport async function login(options: LoginOptions) {\n const {output} = options\n const previousToken = await getCliToken()\n const hasExistingToken = Boolean(previousToken)\n\n // @todo start telemetry trace\n\n // We explicitly want to use an unauthenticated client here, even if we already logged in\n const globalClient = await getGlobalCliClient({apiVersion: LOGIN_API_VERSION})\n const client = globalClient.withConfig({token: undefined})\n\n const provider = await getProvider({\n client,\n experimental: options.experimental,\n orgSlug: options.sso,\n specifiedProvider: options.provider,\n })\n\n // @todo trace.log({step: 'selectProvider', provider: provider?.name})\n\n if (provider === undefined) {\n throw new Error('No authentication providers found')\n }\n\n const {\n loginUrl,\n server,\n token: tokenPromise,\n } = await startServerForTokenCallback({client, providerUrl: provider.url})\n\n // @todo trace.log({step: 'waitForToken'})\n\n const serverUrl = server.address()\n if (!serverUrl || typeof serverUrl === 'string') {\n // Note: `serverUrl` is string only when binding to unix sockets,\n // thus we can safely assume Something Is Wrong™ if it's a string\n throw new Error('Failed to start auth callback server')\n }\n\n // Open a browser on the login page (or tell the user to)\n const shouldLaunchBrowser = canLaunchBrowser() && options.open !== false\n const actionText = shouldLaunchBrowser ? 'Opening browser at' : 'Please open a browser at'\n\n output.log(`\\n${actionText} ${loginUrl.href}\\n`)\n\n const spin = spinner('Waiting for browser login to complete... Press Ctrl + C to cancel').start()\n\n if (shouldLaunchBrowser) {\n open(loginUrl.href)\n }\n\n // Wait for a success/error on the HTTP callback server\n let authToken: string\n try {\n authToken = (await tokenPromise).token\n spin.stop()\n } catch (err: unknown) {\n spin.stop()\n // @todo trace.error(err)\n throw err instanceof Error\n ? new Error(`Login failed: ${err.message}`, {cause: err})\n : new Error(`${err}`)\n } finally {\n server.close()\n server.unref()\n }\n\n // Store the token\n await setConfig('authToken', authToken)\n\n // Clear cached telemetry consent\n await setConfig('telemetryConsent', undefined)\n\n // If we had a session previously, attempt to clear it\n if (hasExistingToken) {\n await globalClient\n .withConfig({token: previousToken})\n .request({method: 'POST', uri: '/auth/logout'})\n .catch((err) => {\n const statusCode = err && err.response && err.response.statusCode\n if (statusCode !== 401) {\n output.warn('Failed to invalidate previous session')\n }\n })\n }\n}\n"],"names":["getCliToken","getGlobalCliClient","setConfig","spinner","open","canLaunchBrowser","startServerForTokenCallback","getProvider","LOGIN_API_VERSION","login","options","output","previousToken","hasExistingToken","Boolean","globalClient","apiVersion","client","withConfig","token","undefined","provider","experimental","orgSlug","sso","specifiedProvider","Error","loginUrl","server","tokenPromise","providerUrl","url","serverUrl","address","shouldLaunchBrowser","actionText","log","href","spin","start","authToken","stop","err","message","cause","close","unref","request","method","uri","catch","statusCode","response","warn"],"mappings":"AAAA,SAAQA,WAAW,EAAEC,kBAAkB,EAAeC,SAAS,QAAO,mBAAkB;AACxF,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,OAAOC,UAAU,OAAM;AAEvB,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,2BAA2B,QAAO,mBAAkB;AAC5D,SAAQC,WAAW,QAAO,mBAAkB;AAE5C,MAAMC,oBAAoB;AAW1B;;;;;;;;;CASC,GACD,OAAO,eAAeC,MAAMC,OAAqB;IAC/C,MAAM,EAACC,MAAM,EAAC,GAAGD;IACjB,MAAME,gBAAgB,MAAMZ;IAC5B,MAAMa,mBAAmBC,QAAQF;IAEjC,8BAA8B;IAE9B,yFAAyF;IACzF,MAAMG,eAAe,MAAMd,mBAAmB;QAACe,YAAYR;IAAiB;IAC5E,MAAMS,SAASF,aAAaG,UAAU,CAAC;QAACC,OAAOC;IAAS;IAExD,MAAMC,WAAW,MAAMd,YAAY;QACjCU;QACAK,cAAcZ,QAAQY,YAAY;QAClCC,SAASb,QAAQc,GAAG;QACpBC,mBAAmBf,QAAQW,QAAQ;IACrC;IAEA,sEAAsE;IAEtE,IAAIA,aAAaD,WAAW;QAC1B,MAAM,IAAIM,MAAM;IAClB;IAEA,MAAM,EACJC,QAAQ,EACRC,MAAM,EACNT,OAAOU,YAAY,EACpB,GAAG,MAAMvB,4BAA4B;QAACW;QAAQa,aAAaT,SAASU,GAAG;IAAA;IAExE,0CAA0C;IAE1C,MAAMC,YAAYJ,OAAOK,OAAO;IAChC,IAAI,CAACD,aAAa,OAAOA,cAAc,UAAU;QAC/C,iEAAiE;QACjE,iEAAiE;QACjE,MAAM,IAAIN,MAAM;IAClB;IAEA,yDAAyD;IACzD,MAAMQ,sBAAsB7B,sBAAsBK,QAAQN,IAAI,KAAK;IACnE,MAAM+B,aAAaD,sBAAsB,uBAAuB;IAEhEvB,OAAOyB,GAAG,CAAC,CAAC,EAAE,EAAED,WAAW,CAAC,EAAER,SAASU,IAAI,CAAC,EAAE,CAAC;IAE/C,MAAMC,OAAOnC,QAAQ,qEAAqEoC,KAAK;IAE/F,IAAIL,qBAAqB;QACvB9B,KAAKuB,SAASU,IAAI;IACpB;IAEA,uDAAuD;IACvD,IAAIG;IACJ,IAAI;QACFA,YAAY,AAAC,CAAA,MAAMX,YAAW,EAAGV,KAAK;QACtCmB,KAAKG,IAAI;IACX,EAAE,OAAOC,KAAc;QACrBJ,KAAKG,IAAI;QACT,yBAAyB;QACzB,MAAMC,eAAehB,QACjB,IAAIA,MAAM,CAAC,cAAc,EAAEgB,IAAIC,OAAO,EAAE,EAAE;YAACC,OAAOF;QAAG,KACrD,IAAIhB,MAAM,GAAGgB,KAAK;IACxB,SAAU;QACRd,OAAOiB,KAAK;QACZjB,OAAOkB,KAAK;IACd;IAEA,kBAAkB;IAClB,MAAM5C,UAAU,aAAasC;IAE7B,iCAAiC;IACjC,MAAMtC,UAAU,oBAAoBkB;IAEpC,sDAAsD;IACtD,IAAIP,kBAAkB;QACpB,MAAME,aACHG,UAAU,CAAC;YAACC,OAAOP;QAAa,GAChCmC,OAAO,CAAC;YAACC,QAAQ;YAAQC,KAAK;QAAc,GAC5CC,KAAK,CAAC,CAACR;YACN,MAAMS,aAAaT,OAAOA,IAAIU,QAAQ,IAAIV,IAAIU,QAAQ,CAACD,UAAU;YACjE,IAAIA,eAAe,KAAK;gBACtBxC,OAAO0C,IAAI,CAAC;YACd;QACF;IACJ;AACF"}
1
+ {"version":3,"sources":["../../../../src/actions/auth/login/login.ts"],"sourcesContent":["import {getCliToken, getGlobalCliClient, type Output, setConfig} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport open from 'open'\n\nimport {canLaunchBrowser} from '../../../util/canLaunchBrowser.js'\nimport {startServerForTokenCallback} from '../authServer.js'\nimport {getProvider} from './getProvider.js'\n\nconst LOGIN_API_VERSION = '2024-02-01'\n\ninterface LoginOptions {\n output: Output\n\n experimental?: boolean\n open?: boolean\n provider?: string\n sso?: string\n}\n\n/**\n * Trigger the authentication flow for the CLI.\n *\n * NOTE: This uses terminal prompts and will not work for non-interactive/programmatic uses.\n *\n * @param options - Options for the login operation\n * @returns Promise that resolves when the login operation is complete\n * @throws Will throw if login fails or is cancelled\n * @internal\n */\nexport async function login(options: LoginOptions) {\n const {output} = options\n const previousToken = await getCliToken()\n const hasExistingToken = Boolean(previousToken)\n\n // @todo start telemetry trace\n\n // We explicitly want to use an unauthenticated client here, even if we already logged in\n const globalClient = await getGlobalCliClient({apiVersion: LOGIN_API_VERSION})\n const client = globalClient.withConfig({token: undefined})\n\n const provider = await getProvider({\n client,\n experimental: options.experimental,\n orgSlug: options.sso,\n specifiedProvider: options.provider,\n })\n\n // @todo trace.log({step: 'selectProvider', provider: provider?.name})\n\n if (provider === undefined) {\n throw new Error('No authentication providers found')\n }\n\n const {\n loginUrl,\n server,\n token: tokenPromise,\n } = await startServerForTokenCallback({client, providerUrl: provider.url})\n\n // @todo trace.log({step: 'waitForToken'})\n\n const serverUrl = server.address()\n if (!serverUrl || typeof serverUrl === 'string') {\n // Note: `serverUrl` is string only when binding to unix sockets,\n // thus we can safely assume Something Is Wrong™ if it's a string\n throw new Error('Failed to start auth callback server')\n }\n\n // Open a browser on the login page (or tell the user to)\n const shouldLaunchBrowser = canLaunchBrowser() && options.open !== false\n const actionText = shouldLaunchBrowser ? 'Opening browser at' : 'Please open a browser at'\n\n output.log(`\\n${actionText} ${loginUrl.href}\\n`)\n\n const spin = spinner('Waiting for browser login to complete... Press Ctrl + C to cancel').start()\n\n if (shouldLaunchBrowser) {\n open(loginUrl.href)\n }\n\n // Wait for a success/error on the HTTP callback server\n let authToken: string\n try {\n authToken = (await tokenPromise).token\n spin.stop()\n } catch (err: unknown) {\n spin.stop()\n // @todo trace.error(err)\n throw err instanceof Error\n ? new Error(`Login failed: ${err.message}`, {cause: err})\n : new Error(`${err}`)\n } finally {\n server.close()\n server.unref()\n }\n\n // Store the token\n await setConfig('authToken', authToken)\n\n // Clear cached telemetry consent\n await setConfig('telemetryConsent', undefined)\n\n // If we had a session previously, attempt to clear it\n if (hasExistingToken) {\n await globalClient\n .withConfig({token: previousToken})\n .request({method: 'POST', uri: '/auth/logout'})\n .catch((err) => {\n const statusCode = err && err.response && err.response.statusCode\n if (statusCode !== 401) {\n output.warn('Failed to invalidate previous session')\n }\n })\n }\n}\n"],"names":["getCliToken","getGlobalCliClient","setConfig","spinner","open","canLaunchBrowser","startServerForTokenCallback","getProvider","LOGIN_API_VERSION","login","options","output","previousToken","hasExistingToken","Boolean","globalClient","apiVersion","client","withConfig","token","undefined","provider","experimental","orgSlug","sso","specifiedProvider","Error","loginUrl","server","tokenPromise","providerUrl","url","serverUrl","address","shouldLaunchBrowser","actionText","log","href","spin","start","authToken","stop","err","message","cause","close","unref","request","method","uri","catch","statusCode","response","warn"],"mappings":"AAAA,SAAQA,WAAW,EAAEC,kBAAkB,EAAeC,SAAS,QAAO,mBAAkB;AACxF,SAAQC,OAAO,QAAO,sBAAqB;AAC3C,OAAOC,UAAU,OAAM;AAEvB,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,2BAA2B,QAAO,mBAAkB;AAC5D,SAAQC,WAAW,QAAO,mBAAkB;AAE5C,MAAMC,oBAAoB;AAW1B;;;;;;;;;CASC,GACD,OAAO,eAAeC,MAAMC,OAAqB;IAC/C,MAAM,EAACC,MAAM,EAAC,GAAGD;IACjB,MAAME,gBAAgB,MAAMZ;IAC5B,MAAMa,mBAAmBC,QAAQF;IAEjC,8BAA8B;IAE9B,yFAAyF;IACzF,MAAMG,eAAe,MAAMd,mBAAmB;QAACe,YAAYR;IAAiB;IAC5E,MAAMS,SAASF,aAAaG,UAAU,CAAC;QAACC,OAAOC;IAAS;IAExD,MAAMC,WAAW,MAAMd,YAAY;QACjCU;QACAK,cAAcZ,QAAQY,YAAY;QAClCC,SAASb,QAAQc,GAAG;QACpBC,mBAAmBf,QAAQW,QAAQ;IACrC;IAEA,sEAAsE;IAEtE,IAAIA,aAAaD,WAAW;QAC1B,MAAM,IAAIM,MAAM;IAClB;IAEA,MAAM,EACJC,QAAQ,EACRC,MAAM,EACNT,OAAOU,YAAY,EACpB,GAAG,MAAMvB,4BAA4B;QAACW;QAAQa,aAAaT,SAASU,GAAG;IAAA;IAExE,0CAA0C;IAE1C,MAAMC,YAAYJ,OAAOK,OAAO;IAChC,IAAI,CAACD,aAAa,OAAOA,cAAc,UAAU;QAC/C,iEAAiE;QACjE,iEAAiE;QACjE,MAAM,IAAIN,MAAM;IAClB;IAEA,yDAAyD;IACzD,MAAMQ,sBAAsB7B,sBAAsBK,QAAQN,IAAI,KAAK;IACnE,MAAM+B,aAAaD,sBAAsB,uBAAuB;IAEhEvB,OAAOyB,GAAG,CAAC,CAAC,EAAE,EAAED,WAAW,CAAC,EAAER,SAASU,IAAI,CAAC,EAAE,CAAC;IAE/C,MAAMC,OAAOnC,QAAQ,qEAAqEoC,KAAK;IAE/F,IAAIL,qBAAqB;QACvB9B,KAAKuB,SAASU,IAAI;IACpB;IAEA,uDAAuD;IACvD,IAAIG;IACJ,IAAI;QACFA,YAAY,AAAC,CAAA,MAAMX,YAAW,EAAGV,KAAK;QACtCmB,KAAKG,IAAI;IACX,EAAE,OAAOC,KAAc;QACrBJ,KAAKG,IAAI;QACT,yBAAyB;QACzB,MAAMC,eAAehB,QACjB,IAAIA,MAAM,CAAC,cAAc,EAAEgB,IAAIC,OAAO,EAAE,EAAE;YAACC,OAAOF;QAAG,KACrD,IAAIhB,MAAM,GAAGgB,KAAK;IACxB,SAAU;QACRd,OAAOiB,KAAK;QACZjB,OAAOkB,KAAK;IACd;IAEA,kBAAkB;IAClB,MAAM5C,UAAU,aAAasC;IAE7B,iCAAiC;IACjC,MAAMtC,UAAU,oBAAoBkB;IAEpC,sDAAsD;IACtD,IAAIP,kBAAkB;QACpB,MAAME,aACHG,UAAU,CAAC;YAACC,OAAOP;QAAa,GAChCmC,OAAO,CAAC;YAACC,QAAQ;YAAQC,KAAK;QAAc,GAC5CC,KAAK,CAAC,CAACR;YACN,MAAMS,aAAaT,OAAOA,IAAIU,QAAQ,IAAIV,IAAIU,QAAQ,CAACD,UAAU;YACjE,IAAIA,eAAe,KAAK;gBACtBxC,OAAO0C,IAAI,CAAC;YACd;QACF;IACJ;AACF"}
@@ -0,0 +1,6 @@
1
+ import { type OrganizationWithGrant } from '../../services/organizations.js';
2
+ export declare function getOrganizationChoices(withGrantInfo: OrganizationWithGrant[]): Array<{
3
+ disabled: boolean | string;
4
+ name: string;
5
+ value: string;
6
+ }>;
@@ -0,0 +1,23 @@
1
+ export function getOrganizationChoices(withGrantInfo) {
2
+ const choices = withGrantInfo.map(({ hasAttachGrant, organization })=>({
3
+ disabled: hasAttachGrant ? false : 'Insufficient permissions',
4
+ name: `${organization.name} [${organization.id}]`,
5
+ value: organization.id
6
+ }));
7
+ choices.push({
8
+ disabled: true,
9
+ name: '─────────',
10
+ value: '---separator---'
11
+ }, {
12
+ disabled: false,
13
+ name: 'Create new organization',
14
+ value: '-new-'
15
+ }, {
16
+ disabled: true,
17
+ name: '─────────',
18
+ value: '---separator2---'
19
+ });
20
+ return choices;
21
+ }
22
+
23
+ //# sourceMappingURL=getOrganizationChoices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/organizations/getOrganizationChoices.ts"],"sourcesContent":["import {type OrganizationWithGrant} from '../../services/organizations.js'\n\nexport function getOrganizationChoices(withGrantInfo: OrganizationWithGrant[]): Array<{\n disabled: boolean | string\n name: string\n value: string\n}> {\n const choices = withGrantInfo.map(({hasAttachGrant, organization}) => ({\n disabled: hasAttachGrant ? false : 'Insufficient permissions',\n name: `${organization.name} [${organization.id}]`,\n value: organization.id,\n }))\n\n choices.push(\n {disabled: true, name: '─────────', value: '---separator---'},\n {disabled: false, name: 'Create new organization', value: '-new-'},\n {disabled: true, name: '─────────', value: '---separator2---'},\n )\n\n return choices\n}\n"],"names":["getOrganizationChoices","withGrantInfo","choices","map","hasAttachGrant","organization","disabled","name","id","value","push"],"mappings":"AAEA,OAAO,SAASA,uBAAuBC,aAAsC;IAK3E,MAAMC,UAAUD,cAAcE,GAAG,CAAC,CAAC,EAACC,cAAc,EAAEC,YAAY,EAAC,GAAM,CAAA;YACrEC,UAAUF,iBAAiB,QAAQ;YACnCG,MAAM,GAAGF,aAAaE,IAAI,CAAC,EAAE,EAAEF,aAAaG,EAAE,CAAC,CAAC,CAAC;YACjDC,OAAOJ,aAAaG,EAAE;QACxB,CAAA;IAEAN,QAAQQ,IAAI,CACV;QAACJ,UAAU;QAAMC,MAAM;QAAaE,OAAO;IAAiB,GAC5D;QAACH,UAAU;QAAOC,MAAM;QAA2BE,OAAO;IAAO,GACjE;QAACH,UAAU;QAAMC,MAAM;QAAaE,OAAO;IAAkB;IAG/D,OAAOP;AACT"}
@@ -0,0 +1,2 @@
1
+ import { type OrganizationWithGrant, type ProjectOrganization } from '../../services/organizations.js';
2
+ export declare function getOrganizationsWithAttachGrantInfo(organizations: ProjectOrganization[]): Promise<OrganizationWithGrant[]>;
@@ -0,0 +1,9 @@
1
+ import { hasProjectAttachGrant } from './hasProjectAttachGrant.js';
2
+ export async function getOrganizationsWithAttachGrantInfo(organizations) {
3
+ return Promise.all(organizations.map(async (organization)=>({
4
+ hasAttachGrant: await hasProjectAttachGrant(organization.id),
5
+ organization
6
+ })));
7
+ }
8
+
9
+ //# sourceMappingURL=getOrganizationsWithAttachGrantInfo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/organizations/getOrganizationsWithAttachGrantInfo.ts"],"sourcesContent":["import {type OrganizationWithGrant, type ProjectOrganization} from '../../services/organizations.js'\nimport {hasProjectAttachGrant} from './hasProjectAttachGrant.js'\n\nexport async function getOrganizationsWithAttachGrantInfo(\n organizations: ProjectOrganization[],\n): Promise<OrganizationWithGrant[]> {\n return Promise.all(\n organizations.map(async (organization) => ({\n hasAttachGrant: await hasProjectAttachGrant(organization.id),\n organization,\n })),\n )\n}\n"],"names":["hasProjectAttachGrant","getOrganizationsWithAttachGrantInfo","organizations","Promise","all","map","organization","hasAttachGrant","id"],"mappings":"AACA,SAAQA,qBAAqB,QAAO,6BAA4B;AAEhE,OAAO,eAAeC,oCACpBC,aAAoC;IAEpC,OAAOC,QAAQC,GAAG,CAChBF,cAAcG,GAAG,CAAC,OAAOC,eAAkB,CAAA;YACzCC,gBAAgB,MAAMP,sBAAsBM,aAAaE,EAAE;YAC3DF;QACF,CAAA;AAEJ"}
@@ -0,0 +1 @@
1
+ export declare function hasProjectAttachGrant(orgId: string): Promise<boolean>;
@@ -0,0 +1,24 @@
1
+ import { subdebug } from '@sanity/cli-core';
2
+ import { getOrganizationGrants } from '../../services/organizations.js';
3
+ const debug = subdebug('organizations');
4
+ export async function hasProjectAttachGrant(orgId) {
5
+ const requiredGrantGroup = 'sanity.organization.projects';
6
+ const requiredGrant = 'attach';
7
+ try {
8
+ const grants = await getOrganizationGrants(orgId);
9
+ const group = grants[requiredGrantGroup] || [];
10
+ return group.some((resource)=>resource.grants && resource.grants.some((grant)=>grant.name === requiredGrant));
11
+ } catch (err) {
12
+ // If we get a 401, it means we don't have access to this organization
13
+ // probably because of implicit membership
14
+ if (err.statusCode === 401) {
15
+ debug('No access to organization %s (401)', orgId);
16
+ return false;
17
+ }
18
+ // For other errors, log them but still return false
19
+ debug('Error checking grants for organization %s: %s', orgId, err.message);
20
+ return false;
21
+ }
22
+ }
23
+
24
+ //# sourceMappingURL=hasProjectAttachGrant.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/actions/organizations/hasProjectAttachGrant.ts"],"sourcesContent":["import {subdebug} from '@sanity/cli-core'\n\nimport {getOrganizationGrants} from '../../services/organizations.js'\n\nconst debug = subdebug('organizations')\n\nexport async function hasProjectAttachGrant(orgId: string) {\n const requiredGrantGroup = 'sanity.organization.projects'\n const requiredGrant = 'attach'\n\n try {\n const grants = await getOrganizationGrants(orgId)\n const group: {grants: {name: string}[]}[] = grants[requiredGrantGroup] || []\n return group.some(\n (resource) =>\n resource.grants && resource.grants.some((grant) => grant.name === requiredGrant),\n )\n } catch (err) {\n // If we get a 401, it means we don't have access to this organization\n // probably because of implicit membership\n if (err.statusCode === 401) {\n debug('No access to organization %s (401)', orgId)\n return false\n }\n // For other errors, log them but still return false\n debug('Error checking grants for organization %s: %s', orgId, err.message)\n return false\n }\n}\n"],"names":["subdebug","getOrganizationGrants","debug","hasProjectAttachGrant","orgId","requiredGrantGroup","requiredGrant","grants","group","some","resource","grant","name","err","statusCode","message"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,mBAAkB;AAEzC,SAAQC,qBAAqB,QAAO,kCAAiC;AAErE,MAAMC,QAAQF,SAAS;AAEvB,OAAO,eAAeG,sBAAsBC,KAAa;IACvD,MAAMC,qBAAqB;IAC3B,MAAMC,gBAAgB;IAEtB,IAAI;QACF,MAAMC,SAAS,MAAMN,sBAAsBG;QAC3C,MAAMI,QAAsCD,MAAM,CAACF,mBAAmB,IAAI,EAAE;QAC5E,OAAOG,MAAMC,IAAI,CACf,CAACC,WACCA,SAASH,MAAM,IAAIG,SAASH,MAAM,CAACE,IAAI,CAAC,CAACE,QAAUA,MAAMC,IAAI,KAAKN;IAExE,EAAE,OAAOO,KAAK;QACZ,sEAAsE;QACtE,0CAA0C;QAC1C,IAAIA,IAAIC,UAAU,KAAK,KAAK;YAC1BZ,MAAM,sCAAsCE;YAC5C,OAAO;QACT;QACA,oDAAoD;QACpDF,MAAM,iDAAiDE,OAAOS,IAAIE,OAAO;QACzE,OAAO;IACT;AACF"}
@@ -19,9 +19,9 @@ export const validForIdPattern = new RegExp(`^[${validForIdChars}]+$`);
19
19
  export const validForNamesChars = 'a-zA-Z0-9_-';
20
20
  export const validForNamesPattern = new RegExp(`^[${validForNamesChars}]+$`);
21
21
  const requiredInId = SANITY_WORKSPACE_SCHEMA_ID_PREFIX.replaceAll(/[.]/g, String.raw`\.`);
22
- const idIdPatternString = `^${requiredInId}\\.([${validForNamesChars}]+)`;
22
+ const idIdPatternString = String.raw`^${requiredInId}\.([${validForNamesChars}]+)`;
23
23
  const baseIdPattern = new RegExp(`${idIdPatternString}$`);
24
- const taggedIdIdPattern = new RegExp(`${idIdPatternString}\\.tag\\.([${validForNamesChars}]+)$`);
24
+ const taggedIdIdPattern = new RegExp(String.raw`${idIdPatternString}\.tag\.([${validForNamesChars}]+)$`);
25
25
  export class FlagValidationError extends Error {
26
26
  constructor(message){
27
27
  super(message);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/schema/utils/schemaStoreValidation.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\n\nimport {isDefined} from '../../manifest/schemaTypeHelpers.js'\nimport {SANITY_WORKSPACE_SCHEMA_ID_PREFIX} from '../../manifest/types.js'\nimport {type DeleteSchemaFlags} from '../deleteSchemaAction.js'\nimport {resolveManifestDirectory} from './manifestReader.js'\n\n// TODO: These types will be imported from their respective files when migrated\nexport interface DeploySchemasFlags extends SchemaStoreCommonFlags {\n 'schema-required'?: boolean\n tag?: string\n workspace?: string\n}\n\nexport interface SchemaListFlags extends SchemaStoreCommonFlags {\n id?: string\n json?: boolean\n}\n\n// Native implementation instead of lodash/uniqBy\nfunction uniqBy<T>(array: T[], key: keyof T): T[] {\n const seen = new Set()\n return array.filter((item) => {\n const value = item[key]\n if (seen.has(value)) {\n return false\n }\n seen.add(value)\n return true\n })\n}\n\nexport const validForIdChars = 'a-zA-Z0-9._-'\nexport const validForIdPattern = new RegExp(`^[${validForIdChars}]+$`)\n\n//no periods allowed in workspaceName or tag in ids\nexport const validForNamesChars = 'a-zA-Z0-9_-'\nexport const validForNamesPattern = new RegExp(`^[${validForNamesChars}]+$`)\n\nconst requiredInId = SANITY_WORKSPACE_SCHEMA_ID_PREFIX.replaceAll(/[.]/g, String.raw`\\.`)\n\nconst idIdPatternString = `^${requiredInId}\\\\.([${validForNamesChars}]+)`\nconst baseIdPattern = new RegExp(`${idIdPatternString}$`)\nconst taggedIdIdPattern = new RegExp(`${idIdPatternString}\\\\.tag\\\\.([${validForNamesChars}]+)$`)\n\nexport class FlagValidationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'FlagValidationError'\n }\n}\n\ninterface WorkspaceSchemaId {\n schemaId: string\n workspace: string\n}\n\nexport interface SchemaStoreCommonFlags {\n 'extract-manifest'?: boolean\n 'manifest-dir'?: string\n verbose?: boolean\n}\n\nfunction parseCommonFlags(\n flags: SchemaStoreCommonFlags,\n context: {workDir: string},\n errors: string[],\n) {\n const manifestDir = parseManifestDir(flags, errors)\n const verbose = !!flags.verbose\n // extract manifest by default: our CLI layer handles both --extract-manifest (true) and --no-extract-manifest (false)\n const extractManifest = flags['extract-manifest'] ?? true\n\n const fullManifestDir = resolveManifestDirectory(context.workDir, manifestDir)\n return {\n extractManifest,\n manifestDir: fullManifestDir,\n verbose,\n }\n}\n\nexport function parseDeploySchemasConfig(flags: DeploySchemasFlags, context: {workDir: string}) {\n const errors: string[] = []\n\n const commonFlags = parseCommonFlags(flags, context, errors)\n const workspaceName = parseWorkspace(flags, errors)\n const tag = parseTag(flags, errors)\n const schemaRequired = !!flags['schema-required']\n\n assertNoErrors(errors)\n return {...commonFlags, schemaRequired, tag, workspaceName}\n}\n\nexport function parseListSchemasConfig(flags: SchemaListFlags, context: {workDir: string}) {\n const errors: string[] = []\n\n const commonFlags = parseCommonFlags(flags, context, errors)\n const id = parseId(flags, errors)\n const json = !!flags.json\n\n assertNoErrors(errors)\n return {...commonFlags, id, json}\n}\n\nexport function parseDeleteSchemasConfig(flags: DeleteSchemaFlags, context: {workDir: string}) {\n const errors: string[] = []\n\n const commonFlags = parseCommonFlags(flags, context, errors)\n const ids = parseIds(flags, errors)\n const dataset = parseDataset(flags, errors)\n\n assertNoErrors(errors)\n return {...commonFlags, dataset, ids}\n}\n\nfunction assertNoErrors(errors: string[]) {\n if (errors.length > 0) {\n throw new FlagValidationError(\n `Invalid arguments:\\n${errors.map((error) => ` - ${error}`).join('\\n')}`,\n )\n }\n}\n\nexport function parseIds(flags: {ids?: unknown}, errors: string[]): WorkspaceSchemaId[] {\n const parsedIds = parseNonEmptyString(flags, 'ids', errors)\n if (errors.length > 0) {\n return []\n }\n\n const ids = parsedIds\n .split(',')\n .map((id) => id.trim())\n .filter((id) => !!id)\n .map((id) => parseWorkspaceSchemaId(id, errors))\n .filter((item) => isDefined(item))\n\n const uniqueIds = uniqBy(ids, 'schemaId' satisfies keyof (typeof ids)[number])\n if (uniqueIds.length < ids.length) {\n errors.push(`ids contains duplicates`)\n }\n if (errors.length === 0 && uniqueIds.length === 0) {\n errors.push(`ids contains no valid id strings`)\n }\n return uniqueIds\n}\n\nexport function parseId(flags: {id?: unknown}, errors: string[]) {\n const id = flags.id === undefined ? undefined : parseNonEmptyString(flags, 'id', errors)\n if (id) {\n return parseWorkspaceSchemaId(id, errors)?.schemaId\n }\n return\n}\n\nexport function parseWorkspaceSchemaId(id: string, errors: string[]) {\n const trimmedId = id.trim()\n\n if (!validForIdPattern.test(trimmedId)) {\n errors.push(`id can only contain characters in [${validForIdChars}] but found: \"${trimmedId}\"`)\n return\n }\n\n if (trimmedId.startsWith('-')) {\n errors.push(`id cannot start with - (dash) but found: \"${trimmedId}\"`)\n return\n }\n\n if (/\\.\\./g.test(trimmedId)) {\n errors.push(`id cannot have consecutive . (period) characters, but found: \"${trimmedId}\"`)\n return\n }\n\n const [, workspace] = trimmedId.match(taggedIdIdPattern) ?? trimmedId.match(baseIdPattern) ?? []\n if (!workspace) {\n errors.push(\n [\n `id must either match ${SANITY_WORKSPACE_SCHEMA_ID_PREFIX}.<workspaceName> `,\n `or ${SANITY_WORKSPACE_SCHEMA_ID_PREFIX}.<workspaceName>.tag.<tag> but found: \"${trimmedId}\". `,\n `Note that workspace name characters not in [${validForNamesChars}] has to be replaced with _ for schema id.`,\n ].join(''),\n )\n return\n }\n return {\n schemaId: trimmedId,\n workspace,\n }\n}\n\nfunction parseDataset(flags: {dataset?: unknown}, errors: string[]) {\n return flags.dataset === undefined ? undefined : parseNonEmptyString(flags, 'dataset', errors)\n}\n\nfunction parseWorkspace(flags: {workspace?: unknown}, errors: string[]) {\n return flags.workspace === undefined ? undefined : parseNonEmptyString(flags, 'workspace', errors)\n}\n\nfunction parseManifestDir(flags: {'manifest-dir'?: unknown}, errors: string[]) {\n return flags['manifest-dir'] === undefined\n ? undefined\n : parseNonEmptyString(flags, 'manifest-dir', errors)\n}\n\nexport function parseTag(flags: {tag?: unknown}, errors: string[]) {\n if (flags.tag === undefined) {\n return\n }\n\n const tag = parseNonEmptyString(flags, 'tag', errors)\n if (errors.length > 0) {\n return\n }\n\n if (tag.includes('.')) {\n errors.push(`tag cannot contain . (period), but was: \"${tag}\"`)\n return\n }\n\n if (!validForNamesPattern.test(tag)) {\n errors.push(`tag can only contain characters in [${validForNamesChars}], but was: \"${tag}\"`)\n return\n }\n\n if (tag.startsWith('-')) {\n errors.push(`tag cannot start with - (dash) but was: \"${tag}\"`)\n return\n }\n\n return tag\n}\n\nfunction parseNonEmptyString<\n Flag extends string,\n Flags extends Partial<Record<Flag, unknown | undefined>>,\n>(flags: Flags, flagName: Flag, errors: string[]): string {\n const flag = flags[flagName]\n if (!isString(flag) || !flag) {\n errors.push(`${flagName} argument is empty`)\n return ''\n }\n return flag\n}\n\nfunction isString(flag: unknown): flag is string {\n return typeof flag === 'string'\n}\n\nfunction getProjectIdMismatchMessage(\n workspace: {name: string; projectId: string},\n operation: 'read' | 'write',\n) {\n return `No permissions to ${operation} schema for workspace \"${workspace.name}\" with projectId \"${workspace.projectId}\"`\n}\n\n/**\n * At the moment schema store commands does not support studios where workspaces have multiple projects\n */\nexport function throwWriteProjectIdMismatch(\n workspace: {name: string; projectId: string},\n projectId: string,\n): void {\n if (workspace.projectId !== projectId) {\n throw new Error(getProjectIdMismatchMessage(workspace, 'write'))\n }\n}\n\nexport function filterLogReadProjectIdMismatch(\n workspace: {name: string; projectId: string},\n projectId: string,\n output: Output,\n) {\n const canRead = workspace.projectId === projectId\n if (!canRead) output.warn(`${getProjectIdMismatchMessage(workspace, 'read')} – ignoring it.`)\n return canRead\n}\n\nexport const SCHEMA_PERMISSION_HELP_TEXT =\n 'For multi-project workspaces, set SANITY_AUTH_TOKEN environment variable to a token with access to the workspace projects.'\n"],"names":["isDefined","SANITY_WORKSPACE_SCHEMA_ID_PREFIX","resolveManifestDirectory","uniqBy","array","key","seen","Set","filter","item","value","has","add","validForIdChars","validForIdPattern","RegExp","validForNamesChars","validForNamesPattern","requiredInId","replaceAll","String","raw","idIdPatternString","baseIdPattern","taggedIdIdPattern","FlagValidationError","Error","message","name","parseCommonFlags","flags","context","errors","manifestDir","parseManifestDir","verbose","extractManifest","fullManifestDir","workDir","parseDeploySchemasConfig","commonFlags","workspaceName","parseWorkspace","tag","parseTag","schemaRequired","assertNoErrors","parseListSchemasConfig","id","parseId","json","parseDeleteSchemasConfig","ids","parseIds","dataset","parseDataset","length","map","error","join","parsedIds","parseNonEmptyString","split","trim","parseWorkspaceSchemaId","uniqueIds","push","undefined","schemaId","trimmedId","test","startsWith","workspace","match","includes","flagName","flag","isString","getProjectIdMismatchMessage","operation","projectId","throwWriteProjectIdMismatch","filterLogReadProjectIdMismatch","output","canRead","warn","SCHEMA_PERMISSION_HELP_TEXT"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../src/actions/schema/utils/schemaStoreValidation.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\n\nimport {isDefined} from '../../manifest/schemaTypeHelpers.js'\nimport {SANITY_WORKSPACE_SCHEMA_ID_PREFIX} from '../../manifest/types.js'\nimport {type DeleteSchemaFlags} from '../deleteSchemaAction.js'\nimport {resolveManifestDirectory} from './manifestReader.js'\n\n// TODO: These types will be imported from their respective files when migrated\nexport interface DeploySchemasFlags extends SchemaStoreCommonFlags {\n 'schema-required'?: boolean\n tag?: string\n workspace?: string\n}\n\nexport interface SchemaListFlags extends SchemaStoreCommonFlags {\n id?: string\n json?: boolean\n}\n\n// Native implementation instead of lodash/uniqBy\nfunction uniqBy<T>(array: T[], key: keyof T): T[] {\n const seen = new Set()\n return array.filter((item) => {\n const value = item[key]\n if (seen.has(value)) {\n return false\n }\n seen.add(value)\n return true\n })\n}\n\nexport const validForIdChars = 'a-zA-Z0-9._-'\nexport const validForIdPattern = new RegExp(`^[${validForIdChars}]+$`)\n\n//no periods allowed in workspaceName or tag in ids\nexport const validForNamesChars = 'a-zA-Z0-9_-'\nexport const validForNamesPattern = new RegExp(`^[${validForNamesChars}]+$`)\n\nconst requiredInId = SANITY_WORKSPACE_SCHEMA_ID_PREFIX.replaceAll(/[.]/g, String.raw`\\.`)\n\nconst idIdPatternString = String.raw`^${requiredInId}\\.([${validForNamesChars}]+)`\nconst baseIdPattern = new RegExp(`${idIdPatternString}$`)\nconst taggedIdIdPattern = new RegExp(\n String.raw`${idIdPatternString}\\.tag\\.([${validForNamesChars}]+)$`,\n)\n\nexport class FlagValidationError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'FlagValidationError'\n }\n}\n\ninterface WorkspaceSchemaId {\n schemaId: string\n workspace: string\n}\n\nexport interface SchemaStoreCommonFlags {\n 'extract-manifest'?: boolean\n 'manifest-dir'?: string\n verbose?: boolean\n}\n\nfunction parseCommonFlags(\n flags: SchemaStoreCommonFlags,\n context: {workDir: string},\n errors: string[],\n) {\n const manifestDir = parseManifestDir(flags, errors)\n const verbose = !!flags.verbose\n // extract manifest by default: our CLI layer handles both --extract-manifest (true) and --no-extract-manifest (false)\n const extractManifest = flags['extract-manifest'] ?? true\n\n const fullManifestDir = resolveManifestDirectory(context.workDir, manifestDir)\n return {\n extractManifest,\n manifestDir: fullManifestDir,\n verbose,\n }\n}\n\nexport function parseDeploySchemasConfig(flags: DeploySchemasFlags, context: {workDir: string}) {\n const errors: string[] = []\n\n const commonFlags = parseCommonFlags(flags, context, errors)\n const workspaceName = parseWorkspace(flags, errors)\n const tag = parseTag(flags, errors)\n const schemaRequired = !!flags['schema-required']\n\n assertNoErrors(errors)\n return {...commonFlags, schemaRequired, tag, workspaceName}\n}\n\nexport function parseListSchemasConfig(flags: SchemaListFlags, context: {workDir: string}) {\n const errors: string[] = []\n\n const commonFlags = parseCommonFlags(flags, context, errors)\n const id = parseId(flags, errors)\n const json = !!flags.json\n\n assertNoErrors(errors)\n return {...commonFlags, id, json}\n}\n\nexport function parseDeleteSchemasConfig(flags: DeleteSchemaFlags, context: {workDir: string}) {\n const errors: string[] = []\n\n const commonFlags = parseCommonFlags(flags, context, errors)\n const ids = parseIds(flags, errors)\n const dataset = parseDataset(flags, errors)\n\n assertNoErrors(errors)\n return {...commonFlags, dataset, ids}\n}\n\nfunction assertNoErrors(errors: string[]) {\n if (errors.length > 0) {\n throw new FlagValidationError(\n `Invalid arguments:\\n${errors.map((error) => ` - ${error}`).join('\\n')}`,\n )\n }\n}\n\nexport function parseIds(flags: {ids?: unknown}, errors: string[]): WorkspaceSchemaId[] {\n const parsedIds = parseNonEmptyString(flags, 'ids', errors)\n if (errors.length > 0) {\n return []\n }\n\n const ids = parsedIds\n .split(',')\n .map((id) => id.trim())\n .filter((id) => !!id)\n .map((id) => parseWorkspaceSchemaId(id, errors))\n .filter((item) => isDefined(item))\n\n const uniqueIds = uniqBy(ids, 'schemaId' satisfies keyof (typeof ids)[number])\n if (uniqueIds.length < ids.length) {\n errors.push(`ids contains duplicates`)\n }\n if (errors.length === 0 && uniqueIds.length === 0) {\n errors.push(`ids contains no valid id strings`)\n }\n return uniqueIds\n}\n\nexport function parseId(flags: {id?: unknown}, errors: string[]) {\n const id = flags.id === undefined ? undefined : parseNonEmptyString(flags, 'id', errors)\n if (id) {\n return parseWorkspaceSchemaId(id, errors)?.schemaId\n }\n return\n}\n\nexport function parseWorkspaceSchemaId(id: string, errors: string[]) {\n const trimmedId = id.trim()\n\n if (!validForIdPattern.test(trimmedId)) {\n errors.push(`id can only contain characters in [${validForIdChars}] but found: \"${trimmedId}\"`)\n return\n }\n\n if (trimmedId.startsWith('-')) {\n errors.push(`id cannot start with - (dash) but found: \"${trimmedId}\"`)\n return\n }\n\n if (/\\.\\./g.test(trimmedId)) {\n errors.push(`id cannot have consecutive . (period) characters, but found: \"${trimmedId}\"`)\n return\n }\n\n const [, workspace] = trimmedId.match(taggedIdIdPattern) ?? trimmedId.match(baseIdPattern) ?? []\n if (!workspace) {\n errors.push(\n [\n `id must either match ${SANITY_WORKSPACE_SCHEMA_ID_PREFIX}.<workspaceName> `,\n `or ${SANITY_WORKSPACE_SCHEMA_ID_PREFIX}.<workspaceName>.tag.<tag> but found: \"${trimmedId}\". `,\n `Note that workspace name characters not in [${validForNamesChars}] has to be replaced with _ for schema id.`,\n ].join(''),\n )\n return\n }\n return {\n schemaId: trimmedId,\n workspace,\n }\n}\n\nfunction parseDataset(flags: {dataset?: unknown}, errors: string[]) {\n return flags.dataset === undefined ? undefined : parseNonEmptyString(flags, 'dataset', errors)\n}\n\nfunction parseWorkspace(flags: {workspace?: unknown}, errors: string[]) {\n return flags.workspace === undefined ? undefined : parseNonEmptyString(flags, 'workspace', errors)\n}\n\nfunction parseManifestDir(flags: {'manifest-dir'?: unknown}, errors: string[]) {\n return flags['manifest-dir'] === undefined\n ? undefined\n : parseNonEmptyString(flags, 'manifest-dir', errors)\n}\n\nexport function parseTag(flags: {tag?: unknown}, errors: string[]) {\n if (flags.tag === undefined) {\n return\n }\n\n const tag = parseNonEmptyString(flags, 'tag', errors)\n if (errors.length > 0) {\n return\n }\n\n if (tag.includes('.')) {\n errors.push(`tag cannot contain . (period), but was: \"${tag}\"`)\n return\n }\n\n if (!validForNamesPattern.test(tag)) {\n errors.push(`tag can only contain characters in [${validForNamesChars}], but was: \"${tag}\"`)\n return\n }\n\n if (tag.startsWith('-')) {\n errors.push(`tag cannot start with - (dash) but was: \"${tag}\"`)\n return\n }\n\n return tag\n}\n\nfunction parseNonEmptyString<\n Flag extends string,\n Flags extends Partial<Record<Flag, unknown | undefined>>,\n>(flags: Flags, flagName: Flag, errors: string[]): string {\n const flag = flags[flagName]\n if (!isString(flag) || !flag) {\n errors.push(`${flagName} argument is empty`)\n return ''\n }\n return flag\n}\n\nfunction isString(flag: unknown): flag is string {\n return typeof flag === 'string'\n}\n\nfunction getProjectIdMismatchMessage(\n workspace: {name: string; projectId: string},\n operation: 'read' | 'write',\n) {\n return `No permissions to ${operation} schema for workspace \"${workspace.name}\" with projectId \"${workspace.projectId}\"`\n}\n\n/**\n * At the moment schema store commands does not support studios where workspaces have multiple projects\n */\nexport function throwWriteProjectIdMismatch(\n workspace: {name: string; projectId: string},\n projectId: string,\n): void {\n if (workspace.projectId !== projectId) {\n throw new Error(getProjectIdMismatchMessage(workspace, 'write'))\n }\n}\n\nexport function filterLogReadProjectIdMismatch(\n workspace: {name: string; projectId: string},\n projectId: string,\n output: Output,\n) {\n const canRead = workspace.projectId === projectId\n if (!canRead) output.warn(`${getProjectIdMismatchMessage(workspace, 'read')} – ignoring it.`)\n return canRead\n}\n\nexport const SCHEMA_PERMISSION_HELP_TEXT =\n 'For multi-project workspaces, set SANITY_AUTH_TOKEN environment variable to a token with access to the workspace projects.'\n"],"names":["isDefined","SANITY_WORKSPACE_SCHEMA_ID_PREFIX","resolveManifestDirectory","uniqBy","array","key","seen","Set","filter","item","value","has","add","validForIdChars","validForIdPattern","RegExp","validForNamesChars","validForNamesPattern","requiredInId","replaceAll","String","raw","idIdPatternString","baseIdPattern","taggedIdIdPattern","FlagValidationError","Error","message","name","parseCommonFlags","flags","context","errors","manifestDir","parseManifestDir","verbose","extractManifest","fullManifestDir","workDir","parseDeploySchemasConfig","commonFlags","workspaceName","parseWorkspace","tag","parseTag","schemaRequired","assertNoErrors","parseListSchemasConfig","id","parseId","json","parseDeleteSchemasConfig","ids","parseIds","dataset","parseDataset","length","map","error","join","parsedIds","parseNonEmptyString","split","trim","parseWorkspaceSchemaId","uniqueIds","push","undefined","schemaId","trimmedId","test","startsWith","workspace","match","includes","flagName","flag","isString","getProjectIdMismatchMessage","operation","projectId","throwWriteProjectIdMismatch","filterLogReadProjectIdMismatch","output","canRead","warn","SCHEMA_PERMISSION_HELP_TEXT"],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ import { testCommand } from '@sanity/cli-test';
2
+ import { afterEach, describe, expect, test, vi } from 'vitest';
3
+ import { InitCommand } from '../../init';
4
+ const mocks = vi.hoisted(()=>({
5
+ getCliToken: vi.fn(),
6
+ getCliUser: vi.fn().mockResolvedValue({
7
+ email: 'test@example.com',
8
+ id: 'user-123',
9
+ name: 'Test User',
10
+ provider: 'saml-123'
11
+ }),
12
+ login: vi.fn()
13
+ }));
14
+ vi.mock('@vercel/fs-detectors', ()=>({
15
+ detectFrameworkRecord: vi.fn().mockResolvedValue({
16
+ name: 'Next.js',
17
+ slug: 'nextjs'
18
+ }),
19
+ LocalFileSystemDetector: vi.fn()
20
+ }));
21
+ vi.mock('../../../../../cli-core/src/util/isInteractive.js', ()=>({
22
+ isInteractive: vi.fn().mockReturnValue(true)
23
+ }));
24
+ vi.mock('../../../../../cli-core/src/services/getCliToken.js', ()=>({
25
+ getCliToken: mocks.getCliToken
26
+ }));
27
+ vi.mock('../../../services/user.js', ()=>({
28
+ getCliUser: mocks.getCliUser
29
+ }));
30
+ vi.mock('../../../actions/auth/login/login.js', ()=>({
31
+ login: mocks.login
32
+ }));
33
+ describe('#init: authentication', ()=>{
34
+ afterEach(()=>{
35
+ vi.clearAllMocks();
36
+ });
37
+ test('user is authenticated with valid token', async ()=>{
38
+ mocks.getCliToken.mockResolvedValue('test-token');
39
+ const { error, stdout } = await testCommand(InitCommand, []);
40
+ expect(error).toBeUndefined();
41
+ expect(stdout).toContain('You are logged in as test@example.com using SAML');
42
+ });
43
+ test('throws error if user is authenticated with invalid token in unattended mode', async ()=>{
44
+ mocks.getCliUser.mockRejectedValueOnce('Invalid token');
45
+ const { error } = await testCommand(InitCommand, [
46
+ '--yes',
47
+ '--dataset=test',
48
+ '--project==test'
49
+ ]);
50
+ expect(error?.message).toContain('Must be logged in to run this command in unattended mode, run `sanity login`');
51
+ });
52
+ test('calls login when token invalid and not in unattended mode', async ()=>{
53
+ mocks.getCliUser.mockRejectedValueOnce('Invalid token');
54
+ const { error } = await testCommand(InitCommand, []);
55
+ expect(error).toBe(undefined);
56
+ expect(mocks.login).toHaveBeenCalled();
57
+ });
58
+ });
59
+
60
+ //# sourceMappingURL=init.authentication.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/__tests__/init/init.authentication.test.ts"],"sourcesContent":["import {testCommand} from '@sanity/cli-test'\nimport {afterEach, describe, expect, test, vi} from 'vitest'\n\nimport {InitCommand} from '../../init'\n\nconst mocks = vi.hoisted(() => ({\n getCliToken: vi.fn(),\n getCliUser: vi.fn().mockResolvedValue({\n email: 'test@example.com',\n id: 'user-123',\n name: 'Test User',\n provider: 'saml-123',\n }),\n login: vi.fn(),\n}))\n\nvi.mock('@vercel/fs-detectors', () => ({\n detectFrameworkRecord: vi.fn().mockResolvedValue({\n name: 'Next.js',\n slug: 'nextjs',\n }),\n LocalFileSystemDetector: vi.fn(),\n}))\n\nvi.mock('../../../../../cli-core/src/util/isInteractive.js', () => ({\n isInteractive: vi.fn().mockReturnValue(true),\n}))\n\nvi.mock('../../../../../cli-core/src/services/getCliToken.js', () => ({\n getCliToken: mocks.getCliToken,\n}))\n\nvi.mock('../../../services/user.js', () => ({\n getCliUser: mocks.getCliUser,\n}))\n\nvi.mock('../../../actions/auth/login/login.js', () => ({\n login: mocks.login,\n}))\n\ndescribe('#init: authentication', () => {\n afterEach(() => {\n vi.clearAllMocks()\n })\n\n test('user is authenticated with valid token', async () => {\n mocks.getCliToken.mockResolvedValue('test-token')\n\n const {error, stdout} = await testCommand(InitCommand, [])\n\n expect(error).toBeUndefined()\n expect(stdout).toContain('You are logged in as test@example.com using SAML')\n })\n\n test('throws error if user is authenticated with invalid token in unattended mode', async () => {\n mocks.getCliUser.mockRejectedValueOnce('Invalid token')\n\n const {error} = await testCommand(InitCommand, ['--yes', '--dataset=test', '--project==test'])\n\n expect(error?.message).toContain(\n 'Must be logged in to run this command in unattended mode, run `sanity login`',\n )\n })\n\n test('calls login when token invalid and not in unattended mode', async () => {\n mocks.getCliUser.mockRejectedValueOnce('Invalid token')\n\n const {error} = await testCommand(InitCommand, [])\n\n expect(error).toBe(undefined)\n expect(mocks.login).toHaveBeenCalled()\n })\n})\n"],"names":["testCommand","afterEach","describe","expect","test","vi","InitCommand","mocks","hoisted","getCliToken","fn","getCliUser","mockResolvedValue","email","id","name","provider","login","mock","detectFrameworkRecord","slug","LocalFileSystemDetector","isInteractive","mockReturnValue","clearAllMocks","error","stdout","toBeUndefined","toContain","mockRejectedValueOnce","message","toBe","undefined","toHaveBeenCalled"],"mappings":"AAAA,SAAQA,WAAW,QAAO,mBAAkB;AAC5C,SAAQC,SAAS,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,IAAI,EAAEC,EAAE,QAAO,SAAQ;AAE5D,SAAQC,WAAW,QAAO,aAAY;AAEtC,MAAMC,QAAQF,GAAGG,OAAO,CAAC,IAAO,CAAA;QAC9BC,aAAaJ,GAAGK,EAAE;QAClBC,YAAYN,GAAGK,EAAE,GAAGE,iBAAiB,CAAC;YACpCC,OAAO;YACPC,IAAI;YACJC,MAAM;YACNC,UAAU;QACZ;QACAC,OAAOZ,GAAGK,EAAE;IACd,CAAA;AAEAL,GAAGa,IAAI,CAAC,wBAAwB,IAAO,CAAA;QACrCC,uBAAuBd,GAAGK,EAAE,GAAGE,iBAAiB,CAAC;YAC/CG,MAAM;YACNK,MAAM;QACR;QACAC,yBAAyBhB,GAAGK,EAAE;IAChC,CAAA;AAEAL,GAAGa,IAAI,CAAC,qDAAqD,IAAO,CAAA;QAClEI,eAAejB,GAAGK,EAAE,GAAGa,eAAe,CAAC;IACzC,CAAA;AAEAlB,GAAGa,IAAI,CAAC,uDAAuD,IAAO,CAAA;QACpET,aAAaF,MAAME,WAAW;IAChC,CAAA;AAEAJ,GAAGa,IAAI,CAAC,6BAA6B,IAAO,CAAA;QAC1CP,YAAYJ,MAAMI,UAAU;IAC9B,CAAA;AAEAN,GAAGa,IAAI,CAAC,wCAAwC,IAAO,CAAA;QACrDD,OAAOV,MAAMU,KAAK;IACpB,CAAA;AAEAf,SAAS,yBAAyB;IAChCD,UAAU;QACRI,GAAGmB,aAAa;IAClB;IAEApB,KAAK,0CAA0C;QAC7CG,MAAME,WAAW,CAACG,iBAAiB,CAAC;QAEpC,MAAM,EAACa,KAAK,EAAEC,MAAM,EAAC,GAAG,MAAM1B,YAAYM,aAAa,EAAE;QAEzDH,OAAOsB,OAAOE,aAAa;QAC3BxB,OAAOuB,QAAQE,SAAS,CAAC;IAC3B;IAEAxB,KAAK,+EAA+E;QAClFG,MAAMI,UAAU,CAACkB,qBAAqB,CAAC;QAEvC,MAAM,EAACJ,KAAK,EAAC,GAAG,MAAMzB,YAAYM,aAAa;YAAC;YAAS;YAAkB;SAAkB;QAE7FH,OAAOsB,OAAOK,SAASF,SAAS,CAC9B;IAEJ;IAEAxB,KAAK,6DAA6D;QAChEG,MAAMI,UAAU,CAACkB,qBAAqB,CAAC;QAEvC,MAAM,EAACJ,KAAK,EAAC,GAAG,MAAMzB,YAAYM,aAAa,EAAE;QAEjDH,OAAOsB,OAAOM,IAAI,CAACC;QACnB7B,OAAOI,MAAMU,KAAK,EAAEgB,gBAAgB;IACtC;AACF"}
@@ -0,0 +1,196 @@
1
+ import * as cliUX from '@sanity/cli-core/ux';
2
+ import { testCommand } from '@sanity/cli-test';
3
+ import { afterEach, describe, expect, test, vi } from 'vitest';
4
+ import { InitCommand } from '../../init';
5
+ const mocks = vi.hoisted(()=>({
6
+ createDataset: vi.fn(),
7
+ createOrganization: vi.fn(),
8
+ createProject: vi.fn(),
9
+ detectFrameworkRecord: vi.fn(),
10
+ getOrganizationChoices: vi.fn(),
11
+ getOrganizationsWithAttachGrantInfo: vi.fn(),
12
+ input: vi.fn(),
13
+ listOrganizations: vi.fn(),
14
+ select: vi.fn(),
15
+ spinner: vi.fn()
16
+ }));
17
+ vi.mock('@vercel/fs-detectors', ()=>({
18
+ detectFrameworkRecord: mocks.detectFrameworkRecord,
19
+ LocalFileSystemDetector: vi.fn()
20
+ }));
21
+ vi.mock('@sanity/cli-core/ux', async ()=>{
22
+ const actual = await vi.importActual('@sanity/cli-core/ux');
23
+ return {
24
+ ...actual,
25
+ input: mocks.input,
26
+ select: mocks.select
27
+ };
28
+ });
29
+ vi.mock('../../../../../cli-core/src/util/isInteractive.js', ()=>({
30
+ isInteractive: vi.fn().mockReturnValue(true)
31
+ }));
32
+ vi.mock('../../../../../cli-core/src/services/getCliToken.js', ()=>({
33
+ getCliToken: vi.fn().mockResolvedValue('test-token')
34
+ }));
35
+ vi.mock('../../../services/user.js', ()=>({
36
+ getCliUser: vi.fn().mockResolvedValue({
37
+ email: 'test@example.com',
38
+ id: 'user-123',
39
+ name: 'Test User',
40
+ provider: 'saml-123'
41
+ })
42
+ }));
43
+ vi.mock('../../../actions/organizations/getOrganizationChoices.js', ()=>({
44
+ getOrganizationChoices: mocks.getOrganizationChoices
45
+ }));
46
+ vi.mock('../../../actions/organizations/getOrganizationsWithAttachGrantInfo.js', ()=>({
47
+ getOrganizationsWithAttachGrantInfo: mocks.getOrganizationsWithAttachGrantInfo
48
+ }));
49
+ vi.mock('../../../services/datasets.js', ()=>({
50
+ createDataset: mocks.createDataset
51
+ }));
52
+ vi.mock('../../../services/organizations.js', ()=>({
53
+ createOrganization: mocks.createOrganization,
54
+ listOrganizations: mocks.listOrganizations
55
+ }));
56
+ vi.mock('../../../services/projects.js', ()=>({
57
+ createProject: mocks.createProject
58
+ }));
59
+ describe('#init: create new project', ()=>{
60
+ afterEach(()=>{
61
+ vi.clearAllMocks();
62
+ });
63
+ test('prompts user to create new organization if they have none', async ()=>{
64
+ // Mock no framework detection
65
+ mocks.detectFrameworkRecord.mockResolvedValueOnce(null);
66
+ // Mock listOrganizations to return empty array (user has no organizations)
67
+ mocks.listOrganizations.mockResolvedValueOnce([]);
68
+ // Mock input prompt for organization name
69
+ mocks.input.mockResolvedValueOnce('My New Organization');
70
+ // Mock createOrganization to return the created organization
71
+ mocks.createOrganization.mockResolvedValueOnce({
72
+ createdByUserId: 'user-123',
73
+ defaultRoleName: null,
74
+ features: [],
75
+ id: 'org-123',
76
+ members: [],
77
+ name: 'My New Organization',
78
+ slug: 'my-new-organization'
79
+ });
80
+ // Mock createProject to return the created project with correct structure
81
+ mocks.createProject.mockResolvedValueOnce({
82
+ displayName: 'Test Project',
83
+ projectId: 'project-123'
84
+ });
85
+ // Mock createDataset
86
+ mocks.createDataset.mockResolvedValueOnce(undefined);
87
+ const spinnerSpy = vi.spyOn(cliUX, 'spinner');
88
+ await testCommand(InitCommand, [
89
+ '--create-project=Test Project',
90
+ '--dataset=production',
91
+ '--output-path=./test-project'
92
+ ]);
93
+ // Verify listOrganizations was called
94
+ expect(mocks.listOrganizations).toHaveBeenCalled();
95
+ // Verify input prompt was called with correct parameters
96
+ expect(mocks.input).toHaveBeenCalledWith(expect.objectContaining({
97
+ default: 'Test User',
98
+ message: 'Organization name:'
99
+ }));
100
+ // Verify createOrganization was called with the input value
101
+ expect(mocks.createOrganization).toHaveBeenCalledWith('My New Organization');
102
+ // Verify createProject was called
103
+ expect(mocks.createProject).toHaveBeenCalledWith(expect.objectContaining({
104
+ displayName: 'Test Project',
105
+ organizationId: 'org-123'
106
+ }));
107
+ // Verify createDataset was called
108
+ expect(mocks.createDataset).toHaveBeenCalledWith(expect.objectContaining({
109
+ aclMode: undefined,
110
+ datasetName: 'production',
111
+ projectId: 'project-123'
112
+ }));
113
+ // Verify spinner was called with correct text
114
+ expect(spinnerSpy).toHaveBeenCalledWith('Creating organization');
115
+ expect(spinnerSpy).toHaveBeenCalledWith('Creating dataset');
116
+ });
117
+ test('prompts user to select then create a new organization', async ()=>{
118
+ // Mock no framework detection
119
+ mocks.detectFrameworkRecord.mockResolvedValueOnce(null);
120
+ // Mock listOrganizations to return existing organizations
121
+ mocks.listOrganizations.mockResolvedValueOnce([
122
+ {
123
+ id: 'existing-org-123',
124
+ name: 'Existing Organization',
125
+ slug: 'existing-organization'
126
+ }
127
+ ]);
128
+ // Mock getOrganizationsWithAttachGrantInfo to return organizations with attach grant
129
+ mocks.getOrganizationsWithAttachGrantInfo.mockResolvedValueOnce([
130
+ {
131
+ hasAttachGrant: true,
132
+ organization: {
133
+ id: 'existing-org-123',
134
+ name: 'Existing Organization',
135
+ slug: 'existing-organization'
136
+ }
137
+ }
138
+ ]);
139
+ // Mock getOrganizationChoices to return choices including create new option
140
+ mocks.getOrganizationChoices.mockReturnValueOnce([
141
+ {
142
+ name: 'Existing Organization [existing-org-123]',
143
+ value: 'existing-org-123'
144
+ },
145
+ {
146
+ name: 'Create new organization',
147
+ value: '-new-'
148
+ }
149
+ ]);
150
+ // Mock select prompt - user chooses to create new organization
151
+ mocks.select.mockResolvedValueOnce('-new-');
152
+ // Mock input prompt for new organization name
153
+ mocks.input.mockResolvedValueOnce('Brand New Organization');
154
+ // Mock createOrganization to return the newly created organization
155
+ mocks.createOrganization.mockResolvedValueOnce({
156
+ createdByUserId: 'user-123',
157
+ defaultRoleName: null,
158
+ features: [],
159
+ id: 'new-org-456',
160
+ members: [],
161
+ name: 'Brand New Organization',
162
+ slug: 'brand-new-organization'
163
+ });
164
+ // Mock createProject to return the created project
165
+ mocks.createProject.mockResolvedValueOnce({
166
+ displayName: 'Test Project',
167
+ projectId: 'project-123'
168
+ });
169
+ // Mock createDataset
170
+ mocks.createDataset.mockResolvedValueOnce(undefined);
171
+ const spinnerSpy = vi.spyOn(cliUX, 'spinner');
172
+ await testCommand(InitCommand, [
173
+ '--create-project=Test Project',
174
+ '--dataset=production',
175
+ '--output-path=./test-project'
176
+ ]);
177
+ // Verify createOrganization was called with the input value
178
+ expect(mocks.createOrganization).toHaveBeenCalledWith('Brand New Organization');
179
+ // Verify createProject was called
180
+ expect(mocks.createProject).toHaveBeenCalledWith(expect.objectContaining({
181
+ displayName: 'Test Project',
182
+ organizationId: 'new-org-456'
183
+ }));
184
+ // Verify createDataset was called
185
+ expect(mocks.createDataset).toHaveBeenCalledWith(expect.objectContaining({
186
+ aclMode: undefined,
187
+ datasetName: 'production',
188
+ projectId: 'project-123'
189
+ }));
190
+ // Verify spinner was called with correct text
191
+ expect(spinnerSpy).toHaveBeenCalledWith('Creating organization');
192
+ expect(spinnerSpy).toHaveBeenCalledWith('Creating dataset');
193
+ });
194
+ });
195
+
196
+ //# sourceMappingURL=init.create-new-project.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/commands/__tests__/init/init.create-new-project.test.ts"],"sourcesContent":["import * as cliUX from '@sanity/cli-core/ux'\nimport {testCommand} from '@sanity/cli-test'\nimport {afterEach, describe, expect, test, vi} from 'vitest'\n\nimport {InitCommand} from '../../init'\n\nconst mocks = vi.hoisted(() => ({\n createDataset: vi.fn(),\n createOrganization: vi.fn(),\n createProject: vi.fn(),\n detectFrameworkRecord: vi.fn(),\n getOrganizationChoices: vi.fn(),\n getOrganizationsWithAttachGrantInfo: vi.fn(),\n input: vi.fn(),\n listOrganizations: vi.fn(),\n select: vi.fn(),\n spinner: vi.fn(),\n}))\n\nvi.mock('@vercel/fs-detectors', () => ({\n detectFrameworkRecord: mocks.detectFrameworkRecord,\n LocalFileSystemDetector: vi.fn(),\n}))\n\nvi.mock('@sanity/cli-core/ux', async () => {\n const actual = await vi.importActual('@sanity/cli-core/ux')\n\n return {\n ...actual,\n input: mocks.input,\n select: mocks.select,\n // spinner: mocks.spinner,\n }\n})\n\nvi.mock('../../../../../cli-core/src/util/isInteractive.js', () => ({\n isInteractive: vi.fn().mockReturnValue(true),\n}))\n\nvi.mock('../../../../../cli-core/src/services/getCliToken.js', () => ({\n getCliToken: vi.fn().mockResolvedValue('test-token'),\n}))\n\nvi.mock('../../../services/user.js', () => ({\n getCliUser: vi.fn().mockResolvedValue({\n email: 'test@example.com',\n id: 'user-123',\n name: 'Test User',\n provider: 'saml-123',\n }),\n}))\n\nvi.mock('../../../actions/organizations/getOrganizationChoices.js', () => ({\n getOrganizationChoices: mocks.getOrganizationChoices,\n}))\n\nvi.mock('../../../actions/organizations/getOrganizationsWithAttachGrantInfo.js', () => ({\n getOrganizationsWithAttachGrantInfo: mocks.getOrganizationsWithAttachGrantInfo,\n}))\n\nvi.mock('../../../services/datasets.js', () => ({\n createDataset: mocks.createDataset,\n}))\n\nvi.mock('../../../services/organizations.js', () => ({\n createOrganization: mocks.createOrganization,\n listOrganizations: mocks.listOrganizations,\n}))\n\nvi.mock('../../../services/projects.js', () => ({\n createProject: mocks.createProject,\n}))\n\ndescribe('#init: create new project', () => {\n afterEach(() => {\n vi.clearAllMocks()\n })\n\n test('prompts user to create new organization if they have none', async () => {\n // Mock no framework detection\n mocks.detectFrameworkRecord.mockResolvedValueOnce(null)\n\n // Mock listOrganizations to return empty array (user has no organizations)\n mocks.listOrganizations.mockResolvedValueOnce([])\n\n // Mock input prompt for organization name\n mocks.input.mockResolvedValueOnce('My New Organization')\n\n // Mock createOrganization to return the created organization\n mocks.createOrganization.mockResolvedValueOnce({\n createdByUserId: 'user-123',\n defaultRoleName: null,\n features: [],\n id: 'org-123',\n members: [],\n name: 'My New Organization',\n slug: 'my-new-organization',\n })\n\n // Mock createProject to return the created project with correct structure\n mocks.createProject.mockResolvedValueOnce({\n displayName: 'Test Project',\n projectId: 'project-123',\n })\n\n // Mock createDataset\n mocks.createDataset.mockResolvedValueOnce(undefined)\n\n const spinnerSpy = vi.spyOn(cliUX, 'spinner')\n\n await testCommand(InitCommand, [\n '--create-project=Test Project',\n '--dataset=production',\n '--output-path=./test-project',\n ])\n\n // Verify listOrganizations was called\n expect(mocks.listOrganizations).toHaveBeenCalled()\n\n // Verify input prompt was called with correct parameters\n expect(mocks.input).toHaveBeenCalledWith(\n expect.objectContaining({\n default: 'Test User',\n message: 'Organization name:',\n }),\n )\n\n // Verify createOrganization was called with the input value\n expect(mocks.createOrganization).toHaveBeenCalledWith('My New Organization')\n\n // Verify createProject was called\n expect(mocks.createProject).toHaveBeenCalledWith(\n expect.objectContaining({\n displayName: 'Test Project',\n organizationId: 'org-123',\n }),\n )\n\n // Verify createDataset was called\n expect(mocks.createDataset).toHaveBeenCalledWith(\n expect.objectContaining({\n aclMode: undefined,\n datasetName: 'production',\n projectId: 'project-123',\n }),\n )\n\n // Verify spinner was called with correct text\n expect(spinnerSpy).toHaveBeenCalledWith('Creating organization')\n expect(spinnerSpy).toHaveBeenCalledWith('Creating dataset')\n })\n\n test('prompts user to select then create a new organization', async () => {\n // Mock no framework detection\n mocks.detectFrameworkRecord.mockResolvedValueOnce(null)\n\n // Mock listOrganizations to return existing organizations\n mocks.listOrganizations.mockResolvedValueOnce([\n {\n id: 'existing-org-123',\n name: 'Existing Organization',\n slug: 'existing-organization',\n },\n ])\n\n // Mock getOrganizationsWithAttachGrantInfo to return organizations with attach grant\n mocks.getOrganizationsWithAttachGrantInfo.mockResolvedValueOnce([\n {\n hasAttachGrant: true,\n organization: {\n id: 'existing-org-123',\n name: 'Existing Organization',\n slug: 'existing-organization',\n },\n },\n ])\n\n // Mock getOrganizationChoices to return choices including create new option\n mocks.getOrganizationChoices.mockReturnValueOnce([\n {name: 'Existing Organization [existing-org-123]', value: 'existing-org-123'},\n {name: 'Create new organization', value: '-new-'},\n ])\n\n // Mock select prompt - user chooses to create new organization\n mocks.select.mockResolvedValueOnce('-new-')\n\n // Mock input prompt for new organization name\n mocks.input.mockResolvedValueOnce('Brand New Organization')\n\n // Mock createOrganization to return the newly created organization\n mocks.createOrganization.mockResolvedValueOnce({\n createdByUserId: 'user-123',\n defaultRoleName: null,\n features: [],\n id: 'new-org-456',\n members: [],\n name: 'Brand New Organization',\n slug: 'brand-new-organization',\n })\n\n // Mock createProject to return the created project\n mocks.createProject.mockResolvedValueOnce({\n displayName: 'Test Project',\n projectId: 'project-123',\n })\n\n // Mock createDataset\n mocks.createDataset.mockResolvedValueOnce(undefined)\n\n const spinnerSpy = vi.spyOn(cliUX, 'spinner')\n\n await testCommand(InitCommand, [\n '--create-project=Test Project',\n '--dataset=production',\n '--output-path=./test-project',\n ])\n\n // Verify createOrganization was called with the input value\n expect(mocks.createOrganization).toHaveBeenCalledWith('Brand New Organization')\n\n // Verify createProject was called\n expect(mocks.createProject).toHaveBeenCalledWith(\n expect.objectContaining({\n displayName: 'Test Project',\n organizationId: 'new-org-456',\n }),\n )\n\n // Verify createDataset was called\n expect(mocks.createDataset).toHaveBeenCalledWith(\n expect.objectContaining({\n aclMode: undefined,\n datasetName: 'production',\n projectId: 'project-123',\n }),\n )\n\n // Verify spinner was called with correct text\n expect(spinnerSpy).toHaveBeenCalledWith('Creating organization')\n expect(spinnerSpy).toHaveBeenCalledWith('Creating dataset')\n })\n})\n"],"names":["cliUX","testCommand","afterEach","describe","expect","test","vi","InitCommand","mocks","hoisted","createDataset","fn","createOrganization","createProject","detectFrameworkRecord","getOrganizationChoices","getOrganizationsWithAttachGrantInfo","input","listOrganizations","select","spinner","mock","LocalFileSystemDetector","actual","importActual","isInteractive","mockReturnValue","getCliToken","mockResolvedValue","getCliUser","email","id","name","provider","clearAllMocks","mockResolvedValueOnce","createdByUserId","defaultRoleName","features","members","slug","displayName","projectId","undefined","spinnerSpy","spyOn","toHaveBeenCalled","toHaveBeenCalledWith","objectContaining","default","message","organizationId","aclMode","datasetName","hasAttachGrant","organization","mockReturnValueOnce","value"],"mappings":"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"}