@sanity/cli 7.9.0 → 7.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/README.md +163 -22
  2. package/dist/actions/auth/ensureAuthenticated.js +2 -0
  3. package/dist/actions/auth/ensureAuthenticated.js.map +1 -1
  4. package/dist/actions/auth/login/getProvider.js +7 -2
  5. package/dist/actions/auth/login/getProvider.js.map +1 -1
  6. package/dist/actions/auth/login/loginInstructions.js +19 -0
  7. package/dist/actions/auth/login/loginInstructions.js.map +1 -0
  8. package/dist/actions/dataset/create.js +2 -2
  9. package/dist/actions/dataset/create.js.map +1 -1
  10. package/dist/actions/dataset/resolveDataset.js +7 -1
  11. package/dist/actions/dataset/resolveDataset.js.map +1 -1
  12. package/dist/actions/deploy/createUserApplication.js +8 -5
  13. package/dist/actions/deploy/createUserApplication.js.map +1 -1
  14. package/dist/actions/deploy/deployApp.js +47 -24
  15. package/dist/actions/deploy/deployApp.js.map +1 -1
  16. package/dist/actions/deploy/deployStudio.js +2 -0
  17. package/dist/actions/deploy/deployStudio.js.map +1 -1
  18. package/dist/actions/graphql/resolveApiGeneration.js +9 -6
  19. package/dist/actions/graphql/resolveApiGeneration.js.map +1 -1
  20. package/dist/actions/init/initAction.js +23 -15
  21. package/dist/actions/init/initAction.js.map +1 -1
  22. package/dist/actions/manifest/extractCoreAppManifest.js +11 -4
  23. package/dist/actions/manifest/extractCoreAppManifest.js.map +1 -1
  24. package/dist/actions/migration/constants.js +10 -0
  25. package/dist/actions/migration/constants.js.map +1 -0
  26. package/dist/actions/migration/ensureApiVersionFormat.js +16 -0
  27. package/dist/actions/migration/ensureApiVersionFormat.js.map +1 -0
  28. package/dist/actions/migration/fileExists.js +13 -0
  29. package/dist/actions/migration/fileExists.js.map +1 -0
  30. package/dist/actions/migration/prettyMutationFormatter.js +155 -0
  31. package/dist/actions/migration/prettyMutationFormatter.js.map +1 -0
  32. package/dist/actions/migration/resolveMigrationScript.js +75 -0
  33. package/dist/actions/migration/resolveMigrationScript.js.map +1 -0
  34. package/dist/actions/migration/resolveMigrations.js +50 -0
  35. package/dist/actions/migration/resolveMigrations.js.map +1 -0
  36. package/dist/actions/migration/templates/index.js +7 -0
  37. package/dist/actions/migration/templates/index.js.map +1 -0
  38. package/dist/actions/migration/templates/minimalAdvanced.js +21 -0
  39. package/dist/actions/migration/templates/minimalAdvanced.js.map +1 -0
  40. package/dist/actions/migration/templates/minimalSimple.js +61 -0
  41. package/dist/actions/migration/templates/minimalSimple.js.map +1 -0
  42. package/dist/actions/migration/templates/renameField.js +20 -0
  43. package/dist/actions/migration/templates/renameField.js.map +1 -0
  44. package/dist/actions/migration/templates/renameType.js +19 -0
  45. package/dist/actions/migration/templates/renameType.js.map +1 -0
  46. package/dist/actions/migration/templates/stringToPTE.js +32 -0
  47. package/dist/actions/migration/templates/stringToPTE.js.map +1 -0
  48. package/dist/actions/migration/tree.js +111 -0
  49. package/dist/actions/migration/tree.js.map +1 -0
  50. package/dist/actions/organizations/getOrganization.js +7 -3
  51. package/dist/actions/organizations/getOrganization.js.map +1 -1
  52. package/dist/actions/organizations/validateOrganizationSlug.js +20 -0
  53. package/dist/actions/organizations/validateOrganizationSlug.js.map +1 -0
  54. package/dist/actions/preview/previewAction.js +33 -1
  55. package/dist/actions/preview/previewAction.js.map +1 -1
  56. package/dist/actions/schema/listSchemas.js +6 -4
  57. package/dist/actions/schema/listSchemas.js.map +1 -1
  58. package/dist/actions/tokens/validateRole.js +2 -1
  59. package/dist/actions/tokens/validateRole.js.map +1 -1
  60. package/dist/actions/undeploy/runUndeploy.js +13 -4
  61. package/dist/actions/undeploy/runUndeploy.js.map +1 -1
  62. package/dist/commands/backups/disable.js +5 -1
  63. package/dist/commands/backups/disable.js.map +1 -1
  64. package/dist/commands/backups/download.js +30 -6
  65. package/dist/commands/backups/download.js.map +1 -1
  66. package/dist/commands/backups/enable.js +5 -1
  67. package/dist/commands/backups/enable.js.map +1 -1
  68. package/dist/commands/backups/list.js +5 -1
  69. package/dist/commands/backups/list.js.map +1 -1
  70. package/dist/commands/cors/add.js +15 -7
  71. package/dist/commands/cors/add.js.map +1 -1
  72. package/dist/commands/cors/delete.js +6 -1
  73. package/dist/commands/cors/delete.js.map +1 -1
  74. package/dist/commands/datasets/alias/create.js +25 -18
  75. package/dist/commands/datasets/alias/create.js.map +1 -1
  76. package/dist/commands/datasets/alias/delete.js +15 -8
  77. package/dist/commands/datasets/alias/delete.js.map +1 -1
  78. package/dist/commands/datasets/alias/link.js +39 -17
  79. package/dist/commands/datasets/alias/link.js.map +1 -1
  80. package/dist/commands/datasets/alias/unlink.js +23 -2
  81. package/dist/commands/datasets/alias/unlink.js.map +1 -1
  82. package/dist/commands/datasets/copy.js +18 -2
  83. package/dist/commands/datasets/copy.js.map +1 -1
  84. package/dist/commands/datasets/create.js +8 -2
  85. package/dist/commands/datasets/create.js.map +1 -1
  86. package/dist/commands/datasets/delete.js +14 -17
  87. package/dist/commands/datasets/delete.js.map +1 -1
  88. package/dist/commands/datasets/embeddings/disable.js +3 -0
  89. package/dist/commands/datasets/embeddings/disable.js.map +1 -1
  90. package/dist/commands/datasets/embeddings/enable.js +5 -2
  91. package/dist/commands/datasets/embeddings/enable.js.map +1 -1
  92. package/dist/commands/datasets/embeddings/status.js +3 -0
  93. package/dist/commands/datasets/embeddings/status.js.map +1 -1
  94. package/dist/commands/datasets/export.js +27 -15
  95. package/dist/commands/datasets/export.js.map +1 -1
  96. package/dist/commands/datasets/import.js +3 -3
  97. package/dist/commands/datasets/import.js.map +1 -1
  98. package/dist/commands/datasets/visibility/get.js +2 -2
  99. package/dist/commands/datasets/visibility/get.js.map +1 -1
  100. package/dist/commands/datasets/visibility/set.js +2 -2
  101. package/dist/commands/datasets/visibility/set.js.map +1 -1
  102. package/dist/commands/docs/read.js +3 -3
  103. package/dist/commands/docs/read.js.map +1 -1
  104. package/dist/commands/documents/create.js +18 -13
  105. package/dist/commands/documents/create.js.map +1 -1
  106. package/dist/commands/documents/delete.js +3 -3
  107. package/dist/commands/documents/delete.js.map +1 -1
  108. package/dist/commands/documents/get.js +3 -3
  109. package/dist/commands/documents/get.js.map +1 -1
  110. package/dist/commands/documents/query.js +3 -3
  111. package/dist/commands/documents/query.js.map +1 -1
  112. package/dist/commands/documents/validate.js +29 -26
  113. package/dist/commands/documents/validate.js.map +1 -1
  114. package/dist/commands/graphql/deploy.js +20 -16
  115. package/dist/commands/graphql/deploy.js.map +1 -1
  116. package/dist/commands/graphql/undeploy.js +15 -18
  117. package/dist/commands/graphql/undeploy.js.map +1 -1
  118. package/dist/commands/hooks/create.js +4 -0
  119. package/dist/commands/hooks/create.js.map +1 -1
  120. package/dist/commands/hooks/delete.js +6 -1
  121. package/dist/commands/hooks/delete.js.map +1 -1
  122. package/dist/commands/hooks/logs.js +6 -1
  123. package/dist/commands/hooks/logs.js.map +1 -1
  124. package/dist/commands/install.js +1 -1
  125. package/dist/commands/install.js.map +1 -1
  126. package/dist/commands/login.js +2 -1
  127. package/dist/commands/login.js.map +1 -1
  128. package/dist/commands/media/create-aspect.js +20 -8
  129. package/dist/commands/media/create-aspect.js.map +1 -1
  130. package/dist/commands/media/delete-aspect.js +31 -16
  131. package/dist/commands/media/delete-aspect.js.map +1 -1
  132. package/dist/commands/media/deploy-aspect.js +10 -5
  133. package/dist/commands/media/deploy-aspect.js.map +1 -1
  134. package/dist/commands/media/export.js +9 -4
  135. package/dist/commands/media/export.js.map +1 -1
  136. package/dist/commands/media/import.js +6 -1
  137. package/dist/commands/media/import.js.map +1 -1
  138. package/dist/commands/migrations/create.js +150 -0
  139. package/dist/commands/migrations/create.js.map +1 -0
  140. package/dist/commands/migrations/list.js +64 -0
  141. package/dist/commands/migrations/list.js.map +1 -0
  142. package/dist/commands/migrations/run.js +306 -0
  143. package/dist/commands/migrations/run.js.map +1 -0
  144. package/dist/commands/organizations/create.js +79 -0
  145. package/dist/commands/organizations/create.js.map +1 -0
  146. package/dist/commands/organizations/delete.js +100 -0
  147. package/dist/commands/organizations/delete.js.map +1 -0
  148. package/dist/commands/organizations/get.js +48 -0
  149. package/dist/commands/organizations/get.js.map +1 -0
  150. package/dist/commands/organizations/list.js +60 -0
  151. package/dist/commands/organizations/list.js.map +1 -0
  152. package/dist/commands/organizations/update.js +115 -0
  153. package/dist/commands/organizations/update.js.map +1 -0
  154. package/dist/commands/preview.js +1 -0
  155. package/dist/commands/preview.js.map +1 -1
  156. package/dist/commands/projects/create.js +3 -1
  157. package/dist/commands/projects/create.js.map +1 -1
  158. package/dist/commands/schemas/delete.js +23 -2
  159. package/dist/commands/schemas/delete.js.map +1 -1
  160. package/dist/commands/schemas/deploy.js +6 -4
  161. package/dist/commands/schemas/deploy.js.map +1 -1
  162. package/dist/commands/schemas/extract.js +22 -1
  163. package/dist/commands/schemas/extract.js.map +1 -1
  164. package/dist/commands/schemas/list.js +8 -8
  165. package/dist/commands/schemas/list.js.map +1 -1
  166. package/dist/commands/tokens/{add.js → create.js} +25 -18
  167. package/dist/commands/tokens/create.js.map +1 -0
  168. package/dist/commands/tokens/delete.js +41 -23
  169. package/dist/commands/tokens/delete.js.map +1 -1
  170. package/dist/commands/undeploy.js +2 -7
  171. package/dist/commands/undeploy.js.map +1 -1
  172. package/dist/commands/users/invite.js +25 -3
  173. package/dist/commands/users/invite.js.map +1 -1
  174. package/dist/prompts/promptForOrganizationName.js +2 -1
  175. package/dist/prompts/promptForOrganizationName.js.map +1 -1
  176. package/dist/services/organizations.js +45 -2
  177. package/dist/services/organizations.js.map +1 -1
  178. package/dist/services/userApplications.js.map +1 -1
  179. package/dist/topicAliases.js +10 -0
  180. package/dist/topicAliases.js.map +1 -1
  181. package/dist/util/formatCliErrorMessages.js +5 -0
  182. package/dist/util/formatCliErrorMessages.js.map +1 -0
  183. package/dist/util/organizationAliases.js +12 -0
  184. package/dist/util/organizationAliases.js.map +1 -0
  185. package/oclif.config.js +6 -1
  186. package/oclif.manifest.json +464 -11
  187. package/package.json +28 -28
  188. package/dist/commands/tokens/add.js.map +0 -1
package/README.md CHANGED
@@ -6,6 +6,7 @@ Code for sanity cli
6
6
 
7
7
  - [@sanity/cli](#sanitycli)
8
8
  - [Commands](#commands)
9
+
9
10
  <!-- tocstop -->
10
11
 
11
12
  # Commands
@@ -95,6 +96,11 @@ Code for sanity cli
95
96
  - [`sanity migrations run [ID]`](#sanity-migrations-run-id)
96
97
  - [`sanity openapi get SLUG`](#sanity-openapi-get-slug)
97
98
  - [`sanity openapi list`](#sanity-openapi-list)
99
+ - [`sanity organizations create`](#sanity-organizations-create)
100
+ - [`sanity organizations delete ORGANIZATIONID`](#sanity-organizations-delete-organizationid)
101
+ - [`sanity organizations get ORGANIZATIONID`](#sanity-organizations-get-organizationid)
102
+ - [`sanity organizations list`](#sanity-organizations-list)
103
+ - [`sanity organizations update ORGANIZATIONID`](#sanity-organizations-update-organizationid)
98
104
  - [`sanity preview [OUTPUTDIR]`](#sanity-preview-outputdir)
99
105
  - [`sanity projects create [PROJECTNAME]`](#sanity-projects-create-projectname)
100
106
  - [`sanity projects list`](#sanity-projects-list)
@@ -107,7 +113,7 @@ Code for sanity cli
107
113
  - [`sanity telemetry disable`](#sanity-telemetry-disable)
108
114
  - [`sanity telemetry enable`](#sanity-telemetry-enable)
109
115
  - [`sanity telemetry status`](#sanity-telemetry-status)
110
- - [`sanity tokens add [LABEL]`](#sanity-tokens-add-label)
116
+ - [`sanity tokens create [LABEL]`](#sanity-tokens-create-label)
111
117
  - [`sanity tokens delete [TOKENID]`](#sanity-tokens-delete-tokenid)
112
118
  - [`sanity tokens list`](#sanity-tokens-list)
113
119
  - [`sanity typegen generate`](#sanity-typegen-generate)
@@ -801,13 +807,14 @@ Add a CORS origin to the project
801
807
 
802
808
  ```
803
809
  USAGE
804
- $ sanity cors add ORIGIN [-p <id>] [--credentials]
810
+ $ sanity cors add ORIGIN [-p <id>] [--credentials] [-y]
805
811
 
806
812
  ARGUMENTS
807
813
  ORIGIN Origin to allow (e.g., https://example.com)
808
814
 
809
815
  FLAGS
810
- --[no-]credentials Allow credentials (token/cookie) to be sent from this origin
816
+ -y, --yes Confirm risky wildcard origins without prompting
817
+ --[no-]credentials Allow credentials (token/cookie) to be sent from this origin
811
818
 
812
819
  OVERRIDE FLAGS
813
820
  -p, --project-id=<id> Project ID to add CORS origin to (overrides CLI configuration)
@@ -1800,8 +1807,9 @@ Validate documents in a dataset against the studio schema
1800
1807
 
1801
1808
  ```
1802
1809
  USAGE
1803
- $ sanity documents validate [-p <id>] [-d <name>] [--file <value>] [--format <value>] [--level error|warning|info]
1804
- [--max-custom-validation-concurrency <value>] [--max-fetch-concurrency <value>] [--workspace <value>] [-y]
1810
+ $ sanity documents validate [-p <id>] [-d <name>] [--file <value>] [--format json|ndjson|pretty] [--level
1811
+ error|warning|info] [--max-custom-validation-concurrency <value>] [--max-fetch-concurrency <value>] [--workspace
1812
+ <value>] [-y]
1805
1813
 
1806
1814
  FLAGS
1807
1815
  -d, --dataset=<name> Override the dataset used. By default, this is derived from the given
@@ -1809,10 +1817,11 @@ FLAGS
1809
1817
  -p, --project-id=<id> Override the project ID used. By default, this is derived from the
1810
1818
  given workspace
1811
1819
  -y, --yes Skips the first confirmation prompt
1812
- --file=<value> Provide a path to either an .ndjson file or a tarball containing an
1813
- .ndjson file
1814
- --format=<value> The output format used to print the found validation markers and
1820
+ --file=<value> Path to an NDJSON file or tar archive containing an NDJSON file
1821
+ (optionally gzip-compressed)
1822
+ --format=<option> The output format used to print the found validation markers and
1815
1823
  report progress
1824
+ <options: json|ndjson|pretty>
1816
1825
  --level=<option> [default: warning] The minimum level reported. Defaults to warning
1817
1826
  <options: error|warning|info>
1818
1827
  --max-custom-validation-concurrency=<value> [default: 5] Specify how many custom validators can run concurrently
@@ -2523,7 +2532,7 @@ USAGE
2523
2532
 
2524
2533
  FLAGS
2525
2534
  --[no-]open Open a browser window to log in (`--no-open` only prints URL)
2526
- --provider=<providerId> Log in using the given provider
2535
+ --provider=<providerId> Log in using a provider ID (google, github, sanity, vercel)
2527
2536
  --sso=<slug> Log in using Single Sign-On, using the given organization slug
2528
2537
  --sso-provider=<name> Select a specific SSO provider by name (use with --sso)
2529
2538
  --with-token Read token from standard input
@@ -2627,7 +2636,11 @@ Create a new aspect definition file
2627
2636
 
2628
2637
  ```
2629
2638
  USAGE
2630
- $ sanity media create-aspect
2639
+ $ sanity media create-aspect [--name <value>] [--title <value>]
2640
+
2641
+ FLAGS
2642
+ --name=<value> Aspect name. Defaults to the title in camel case
2643
+ --title=<value> Aspect title
2631
2644
 
2632
2645
  DESCRIPTION
2633
2646
  Create a new aspect definition file
@@ -2651,7 +2664,7 @@ ARGUMENTS
2651
2664
 
2652
2665
  FLAGS
2653
2666
  --media-library-id=<value> The id of the target media library
2654
- --yes Skip confirmation prompt
2667
+ --yes Run without prompts and confirm deletion
2655
2668
 
2656
2669
  OVERRIDE FLAGS
2657
2670
  -p, --project-id=<id> Project ID to delete media aspect from (overrides CLI configuration)
@@ -2919,6 +2932,132 @@ EXAMPLES
2919
2932
  $ sanity openapi list --web
2920
2933
  ```
2921
2934
 
2935
+ ## `sanity organizations create`
2936
+
2937
+ Create a new organization
2938
+
2939
+ ```
2940
+ USAGE
2941
+ $ sanity organizations create [--default-role <value>] [--name <value>]
2942
+
2943
+ FLAGS
2944
+ --default-role=<value> Default role assigned to new members
2945
+ --name=<value> Organization name
2946
+
2947
+ DESCRIPTION
2948
+ Create a new organization
2949
+
2950
+ EXAMPLES
2951
+ Interactively create an organization
2952
+
2953
+ $ sanity organizations create
2954
+
2955
+ Create an organization named "Acme Corp"
2956
+
2957
+ $ sanity organizations create --name "Acme Corp"
2958
+
2959
+ Create an organization with a default member role
2960
+
2961
+ $ sanity organizations create --name "Acme Corp" --default-role member
2962
+ ```
2963
+
2964
+ ## `sanity organizations delete ORGANIZATIONID`
2965
+
2966
+ Delete an organization
2967
+
2968
+ ```
2969
+ USAGE
2970
+ $ sanity organizations delete ORGANIZATIONID [--force]
2971
+
2972
+ ARGUMENTS
2973
+ ORGANIZATIONID Organization ID to delete
2974
+
2975
+ FLAGS
2976
+ --force Do not prompt for delete confirmation - forcefully delete
2977
+
2978
+ DESCRIPTION
2979
+ Delete an organization
2980
+
2981
+ EXAMPLES
2982
+ Delete an organization (prompts for confirmation)
2983
+
2984
+ $ sanity organizations delete org-abc123
2985
+
2986
+ Delete an organization without confirmation
2987
+
2988
+ $ sanity organizations delete org-abc123 --force
2989
+ ```
2990
+
2991
+ ## `sanity organizations get ORGANIZATIONID`
2992
+
2993
+ Get details of an organization
2994
+
2995
+ ```
2996
+ USAGE
2997
+ $ sanity organizations get ORGANIZATIONID
2998
+
2999
+ ARGUMENTS
3000
+ ORGANIZATIONID Organization ID
3001
+
3002
+ DESCRIPTION
3003
+ Get details of an organization
3004
+
3005
+ EXAMPLES
3006
+ Get details of a specific organization
3007
+
3008
+ $ sanity organizations get org-abc123
3009
+ ```
3010
+
3011
+ ## `sanity organizations list`
3012
+
3013
+ List organizations you are a member of
3014
+
3015
+ ```
3016
+ USAGE
3017
+ $ sanity organizations list
3018
+
3019
+ DESCRIPTION
3020
+ List organizations you are a member of
3021
+
3022
+ EXAMPLES
3023
+ List all your organizations
3024
+
3025
+ $ sanity organizations list
3026
+ ```
3027
+
3028
+ ## `sanity organizations update ORGANIZATIONID`
3029
+
3030
+ Update an organization
3031
+
3032
+ ```
3033
+ USAGE
3034
+ $ sanity organizations update ORGANIZATIONID [--default-role <value>] [--name <value>] [--slug <value>]
3035
+
3036
+ ARGUMENTS
3037
+ ORGANIZATIONID Organization ID
3038
+
3039
+ FLAGS
3040
+ --default-role=<value> New default role for new members
3041
+ --name=<value> New organization name
3042
+ --slug=<value> New URL slug (requires authSAML feature on the organization)
3043
+
3044
+ DESCRIPTION
3045
+ Update an organization
3046
+
3047
+ EXAMPLES
3048
+ Rename an organization
3049
+
3050
+ $ sanity organizations update org-abc123 --name "New Name"
3051
+
3052
+ Set the organization slug (requires authSAML feature)
3053
+
3054
+ $ sanity organizations update org-abc123 --slug new-slug
3055
+
3056
+ Change the default member role
3057
+
3058
+ $ sanity organizations update org-abc123 --default-role viewer
3059
+ ```
3060
+
2922
3061
  ## `sanity preview [OUTPUTDIR]`
2923
3062
 
2924
3063
  Start a local server to preview a production build
@@ -3024,10 +3163,11 @@ Delete schema documents by id
3024
3163
 
3025
3164
  ```
3026
3165
  USAGE
3027
- $ sanity schemas delete --ids <value> [-p <id>] [-d <name>] [--verbose]
3166
+ $ sanity schemas delete --ids <value> [-p <id>] [-d <name>] [--verbose] [-y]
3028
3167
 
3029
3168
  FLAGS
3030
3169
  -d, --dataset=<name> Delete schemas from a specific dataset
3170
+ -y, --yes Delete schemas without prompting for confirmation
3031
3171
  --ids=<value> (required) Comma-separated list of schema ids to delete
3032
3172
  --verbose Enable verbose logging
3033
3173
 
@@ -3087,11 +3227,12 @@ Extract a JSON representation of a Sanity schema within a Studio context.
3087
3227
 
3088
3228
  ```
3089
3229
  USAGE
3090
- $ sanity schemas extract [--enforce-required-fields] [--format <format>] [--path <value>] [--watch]
3230
+ $ sanity schemas extract [--enforce-required-fields] [--force] [--format <format>] [--path <value>] [--watch]
3091
3231
  [--watch-patterns <glob>...] [--workspace <name>]
3092
3232
 
3093
3233
  FLAGS
3094
3234
  --enforce-required-fields Makes the schema generated treat fields marked as required as non-optional
3235
+ --force Overwrite an existing schema file
3095
3236
  --format=<format> [default: groq-type-nodes] Output format (currently only groq-type-nodes)
3096
3237
  --path=<value> Optional path to specify destination of the schema file
3097
3238
  --watch Enable watch mode to re-extract schema on file changes
@@ -3264,13 +3405,13 @@ EXAMPLES
3264
3405
  $ sanity telemetry telemetry status
3265
3406
  ```
3266
3407
 
3267
- ## `sanity tokens add [LABEL]`
3408
+ ## `sanity tokens create [LABEL]`
3268
3409
 
3269
3410
  Create a new API token for the project
3270
3411
 
3271
3412
  ```
3272
3413
  USAGE
3273
- $ sanity tokens add [LABEL] [-p <id>] [--json] [--role viewer] [-y]
3414
+ $ sanity tokens create [LABEL] [-p <id>] [--json] [--role viewer] [-y]
3274
3415
 
3275
3416
  ARGUMENTS
3276
3417
  [LABEL] Label for the new token
@@ -3278,10 +3419,10 @@ ARGUMENTS
3278
3419
  FLAGS
3279
3420
  -y, --yes Skip prompts and use defaults (unattended mode)
3280
3421
  --json Output as JSON
3281
- --role=viewer Role to assign to the token
3422
+ --role=viewer Role to assign to the token (defaults to viewer in unattended mode)
3282
3423
 
3283
3424
  OVERRIDE FLAGS
3284
- -p, --project-id=<id> Project ID to add token to (overrides CLI configuration)
3425
+ -p, --project-id=<id> Project ID to create token in (overrides CLI configuration)
3285
3426
 
3286
3427
  DESCRIPTION
3287
3428
  Create a new API token for the project
@@ -3289,23 +3430,23 @@ DESCRIPTION
3289
3430
  EXAMPLES
3290
3431
  Create a token with a label
3291
3432
 
3292
- $ sanity tokens add "My API Token"
3433
+ $ sanity tokens create "My API Token"
3293
3434
 
3294
3435
  Create a token with editor role
3295
3436
 
3296
- $ sanity tokens add "My API Token" --role=editor
3437
+ $ sanity tokens create "My API Token" --role=editor
3297
3438
 
3298
3439
  Create a token in unattended mode
3299
3440
 
3300
- $ sanity tokens add "CI Token" --role=editor --yes
3441
+ $ sanity tokens create "CI Token" --role=editor --yes
3301
3442
 
3302
3443
  Output token information as JSON
3303
3444
 
3304
- $ sanity tokens add "API Token" --json
3445
+ $ sanity tokens create "API Token" --json
3305
3446
 
3306
3447
  Create a token for a specific project
3307
3448
 
3308
- $ sanity tokens add "My Token" --project-id abc123 --role=editor
3449
+ $ sanity tokens create "My Token" --project-id abc123 --role=editor
3309
3450
  ```
3310
3451
 
3311
3452
  ## `sanity tokens delete [TOKENID]`
@@ -4,6 +4,7 @@ import { isHttpError } from '@sanity/client';
4
4
  import { LoginError } from '../../errors/LoginError.js';
5
5
  import { getCliUser } from '../../services/user.js';
6
6
  import { login } from './login/login.js';
7
+ import { LOGIN_REQUIRED_MESSAGE } from './login/loginInstructions.js';
7
8
  /**
8
9
  * Ensure the user is authenticated. Validates the current token via /users/me
9
10
  * and triggers interactive login if the token is missing or invalid.
@@ -15,6 +16,7 @@ import { login } from './login/login.js';
15
16
  */ export async function ensureAuthenticated(options) {
16
17
  const user = await validateSession();
17
18
  if (user) return user;
19
+ options.output.warn(LOGIN_REQUIRED_MESSAGE);
18
20
  try {
19
21
  await login({
20
22
  output: options.output,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/auth/ensureAuthenticated.ts"],"sourcesContent":["import {\n type CLITelemetryStore,\n getCliToken,\n type Output,\n type SanityOrgUser,\n} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {LoginError} from '../../errors/LoginError.js'\nimport {getCliUser} from '../../services/user.js'\nimport {login} from './login/login.js'\n\n/**\n * Ensure the user is authenticated. Validates the current token via /users/me\n * and triggers interactive login if the token is missing or invalid.\n *\n * @returns The authenticated user.\n * @throws LoginError if interactive login fails or is cancelled.\n * @throws Error if a network/server error occurs during validation.\n * @internal\n */\nexport async function ensureAuthenticated(options: {\n output: Output\n telemetry: CLITelemetryStore\n}): Promise<SanityOrgUser> {\n const user = await validateSession()\n if (user) return user\n\n try {\n await login({output: options.output, telemetry: options.telemetry})\n } catch (cause) {\n throw new LoginError(getErrorMessage(cause), {cause})\n }\n\n try {\n return await getCliUser()\n } catch (cause) {\n if (isHttpError(cause) && (cause.statusCode === 401 || cause.statusCode === 403)) {\n throw new LoginError(\n `Login succeeded but failed to verify session: ${getErrorMessage(cause)}`,\n {cause},\n )\n }\n throw cause\n }\n}\n\n/**\n * Validate the current CLI auth token by calling /users/me.\n *\n * Returns null only for auth failures (401/403). Re-throws network errors,\n * timeouts, and server errors so callers can handle them appropriately\n * instead of triggering an unnecessary re-login prompt.\n *\n * @returns The authenticated user if valid, null if invalid/missing.\n * @internal\n */\nexport async function validateSession(): Promise<SanityOrgUser | null> {\n const token = await getCliToken()\n if (!token) return null\n\n try {\n return await getCliUser()\n } catch (error) {\n if (isHttpError(error) && (error.statusCode === 401 || error.statusCode === 403)) {\n return null\n }\n throw error\n }\n}\n"],"names":["getCliToken","getErrorMessage","isHttpError","LoginError","getCliUser","login","ensureAuthenticated","options","user","validateSession","output","telemetry","cause","statusCode","token","error"],"mappings":"AAAA,SAEEA,WAAW,QAGN,mBAAkB;AACzB,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,UAAU,QAAO,6BAA4B;AACrD,SAAQC,UAAU,QAAO,yBAAwB;AACjD,SAAQC,KAAK,QAAO,mBAAkB;AAEtC;;;;;;;;CAQC,GACD,OAAO,eAAeC,oBAAoBC,OAGzC;IACC,MAAMC,OAAO,MAAMC;IACnB,IAAID,MAAM,OAAOA;IAEjB,IAAI;QACF,MAAMH,MAAM;YAACK,QAAQH,QAAQG,MAAM;YAAEC,WAAWJ,QAAQI,SAAS;QAAA;IACnE,EAAE,OAAOC,OAAO;QACd,MAAM,IAAIT,WAAWF,gBAAgBW,QAAQ;YAACA;QAAK;IACrD;IAEA,IAAI;QACF,OAAO,MAAMR;IACf,EAAE,OAAOQ,OAAO;QACd,IAAIV,YAAYU,UAAWA,CAAAA,MAAMC,UAAU,KAAK,OAAOD,MAAMC,UAAU,KAAK,GAAE,GAAI;YAChF,MAAM,IAAIV,WACR,CAAC,8CAA8C,EAAEF,gBAAgBW,QAAQ,EACzE;gBAACA;YAAK;QAEV;QACA,MAAMA;IACR;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,eAAeH;IACpB,MAAMK,QAAQ,MAAMd;IACpB,IAAI,CAACc,OAAO,OAAO;IAEnB,IAAI;QACF,OAAO,MAAMV;IACf,EAAE,OAAOW,OAAO;QACd,IAAIb,YAAYa,UAAWA,CAAAA,MAAMF,UAAU,KAAK,OAAOE,MAAMF,UAAU,KAAK,GAAE,GAAI;YAChF,OAAO;QACT;QACA,MAAME;IACR;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/auth/ensureAuthenticated.ts"],"sourcesContent":["import {\n type CLITelemetryStore,\n getCliToken,\n type Output,\n type SanityOrgUser,\n} from '@sanity/cli-core'\nimport {getErrorMessage} from '@sanity/cli-core/errors'\nimport {isHttpError} from '@sanity/client'\n\nimport {LoginError} from '../../errors/LoginError.js'\nimport {getCliUser} from '../../services/user.js'\nimport {login} from './login/login.js'\nimport {LOGIN_REQUIRED_MESSAGE} from './login/loginInstructions.js'\n\n/**\n * Ensure the user is authenticated. Validates the current token via /users/me\n * and triggers interactive login if the token is missing or invalid.\n *\n * @returns The authenticated user.\n * @throws LoginError if interactive login fails or is cancelled.\n * @throws Error if a network/server error occurs during validation.\n * @internal\n */\nexport async function ensureAuthenticated(options: {\n output: Output\n telemetry: CLITelemetryStore\n}): Promise<SanityOrgUser> {\n const user = await validateSession()\n if (user) return user\n\n options.output.warn(LOGIN_REQUIRED_MESSAGE)\n\n try {\n await login({output: options.output, telemetry: options.telemetry})\n } catch (cause) {\n throw new LoginError(getErrorMessage(cause), {cause})\n }\n\n try {\n return await getCliUser()\n } catch (cause) {\n if (isHttpError(cause) && (cause.statusCode === 401 || cause.statusCode === 403)) {\n throw new LoginError(\n `Login succeeded but failed to verify session: ${getErrorMessage(cause)}`,\n {cause},\n )\n }\n throw cause\n }\n}\n\n/**\n * Validate the current CLI auth token by calling /users/me.\n *\n * Returns null only for auth failures (401/403). Re-throws network errors,\n * timeouts, and server errors so callers can handle them appropriately\n * instead of triggering an unnecessary re-login prompt.\n *\n * @returns The authenticated user if valid, null if invalid/missing.\n * @internal\n */\nexport async function validateSession(): Promise<SanityOrgUser | null> {\n const token = await getCliToken()\n if (!token) return null\n\n try {\n return await getCliUser()\n } catch (error) {\n if (isHttpError(error) && (error.statusCode === 401 || error.statusCode === 403)) {\n return null\n }\n throw error\n }\n}\n"],"names":["getCliToken","getErrorMessage","isHttpError","LoginError","getCliUser","login","LOGIN_REQUIRED_MESSAGE","ensureAuthenticated","options","user","validateSession","output","warn","telemetry","cause","statusCode","token","error"],"mappings":"AAAA,SAEEA,WAAW,QAGN,mBAAkB;AACzB,SAAQC,eAAe,QAAO,0BAAyB;AACvD,SAAQC,WAAW,QAAO,iBAAgB;AAE1C,SAAQC,UAAU,QAAO,6BAA4B;AACrD,SAAQC,UAAU,QAAO,yBAAwB;AACjD,SAAQC,KAAK,QAAO,mBAAkB;AACtC,SAAQC,sBAAsB,QAAO,+BAA8B;AAEnE;;;;;;;;CAQC,GACD,OAAO,eAAeC,oBAAoBC,OAGzC;IACC,MAAMC,OAAO,MAAMC;IACnB,IAAID,MAAM,OAAOA;IAEjBD,QAAQG,MAAM,CAACC,IAAI,CAACN;IAEpB,IAAI;QACF,MAAMD,MAAM;YAACM,QAAQH,QAAQG,MAAM;YAAEE,WAAWL,QAAQK,SAAS;QAAA;IACnE,EAAE,OAAOC,OAAO;QACd,MAAM,IAAIX,WAAWF,gBAAgBa,QAAQ;YAACA;QAAK;IACrD;IAEA,IAAI;QACF,OAAO,MAAMV;IACf,EAAE,OAAOU,OAAO;QACd,IAAIZ,YAAYY,UAAWA,CAAAA,MAAMC,UAAU,KAAK,OAAOD,MAAMC,UAAU,KAAK,GAAE,GAAI;YAChF,MAAM,IAAIZ,WACR,CAAC,8CAA8C,EAAEF,gBAAgBa,QAAQ,EACzE;gBAACA;YAAK;QAEV;QACA,MAAMA;IACR;AACF;AAEA;;;;;;;;;CASC,GACD,OAAO,eAAeJ;IACpB,MAAMM,QAAQ,MAAMhB;IACpB,IAAI,CAACgB,OAAO,OAAO;IAEnB,IAAI;QACF,OAAO,MAAMZ;IACf,EAAE,OAAOa,OAAO;QACd,IAAIf,YAAYe,UAAWA,CAAAA,MAAMF,UAAU,KAAK,OAAOE,MAAMF,UAAU,KAAK,GAAE,GAAI;YAChF,OAAO;QACT;QACA,MAAME;IACR;AACF"}
@@ -49,8 +49,13 @@ const debug = subdebug('login:getProvider');
49
49
  if (providers.length === 0) {
50
50
  return undefined;
51
51
  }
52
- if (!isInteractive() && realProviderNames.length > 1) {
53
- throw new Error(`Multiple login providers available: ${realProviderNames.join(', ')}. ` + 'Use `--provider <name>` to select one in unattended mode.');
52
+ if (!isInteractive()) {
53
+ if (realProviderNames.length === 1) {
54
+ return providers.find((provider)=>provider.name === realProviderNames[0]);
55
+ }
56
+ if (realProviderNames.length > 1) {
57
+ throw new Error(`Multiple login providers available: ${realProviderNames.join(', ')}. ` + 'Use `--provider <name>` to select one in unattended mode.');
58
+ }
54
59
  }
55
60
  const provider = await promptForProviders(providers);
56
61
  if (provider.name === 'sso') {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/actions/auth/login/getProvider.ts"],"sourcesContent":["import {isInteractive, subdebug} from '@sanity/cli-core'\nimport {input, spinner, type SpinnerInstance} from '@sanity/cli-core/ux'\n\nimport {promptForProviders} from '../../../prompts/promptForProviders.js'\nimport {getProviders, getVercelProviderUrl} from '../../../services/auth.js'\nimport {type LoginProvider} from '../types.js'\nimport {getSSOProvider} from './getSSOProvider.js'\n\nconst debug = subdebug('login:getProvider')\n\n/**\n * Prompt the user to select a login provider, or use the specified provider if given.\n *\n * @param options - Options for the provider resolve operation\n * @returns Promise that resolves to the selected login provider\n * @internal\n */\nexport async function getProvider({\n experimental,\n orgSlug,\n specifiedProvider,\n ssoProvider,\n}: {\n experimental: boolean | undefined\n orgSlug: string | undefined\n specifiedProvider: string | undefined\n ssoProvider: string | undefined\n}): Promise<LoginProvider | undefined> {\n if (specifiedProvider === 'vercel') {\n return {\n name: 'vercel',\n title: 'Vercel',\n url: await getVercelProviderUrl(),\n }\n }\n\n let spin: SpinnerInstance | undefined\n\n try {\n if (orgSlug) {\n return getSSOProvider(orgSlug, ssoProvider)\n }\n\n spin = spinner('Fetching providers...').start()\n // Fetch and prompt for login provider to use\n let {providers} = await getProviders()\n if (experimental) {\n providers = [...providers, {name: 'sso', title: 'SSO', url: '_not_used_'}]\n }\n spin.stop()\n\n // Real providers excludes the synthetic SSO entry used by --experimental\n const realProviderNames = providers.filter((p) => p.name !== 'sso').map((p) => p.name)\n\n if (specifiedProvider) {\n const provider = providers.find((prov) => prov.name === specifiedProvider)\n if (!provider) {\n throw new Error(\n `Cannot find login provider with name \"${specifiedProvider}\". ` +\n (realProviderNames.length > 0\n ? `Available providers: ${realProviderNames.join(', ')}`\n : 'No providers are available'),\n )\n }\n return provider\n }\n\n if (providers.length === 0) {\n return undefined\n }\n\n if (!isInteractive() && realProviderNames.length > 1) {\n throw new Error(\n `Multiple login providers available: ${realProviderNames.join(', ')}. ` +\n 'Use `--provider <name>` to select one in unattended mode.',\n )\n }\n\n const provider = await promptForProviders(providers)\n if (provider.name === 'sso') {\n const orgSlug = await input({message: 'Organization slug:'})\n return getSSOProvider(orgSlug)\n }\n\n return provider\n } catch (err) {\n spin?.stop()\n debug('Error retrieving providers', err)\n throw err\n }\n}\n"],"names":["isInteractive","subdebug","input","spinner","promptForProviders","getProviders","getVercelProviderUrl","getSSOProvider","debug","getProvider","experimental","orgSlug","specifiedProvider","ssoProvider","name","title","url","spin","start","providers","stop","realProviderNames","filter","p","map","provider","find","prov","Error","length","join","undefined","message","err"],"mappings":"AAAA,SAAQA,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACxD,SAAQC,KAAK,EAAEC,OAAO,QAA6B,sBAAqB;AAExE,SAAQC,kBAAkB,QAAO,yCAAwC;AACzE,SAAQC,YAAY,EAAEC,oBAAoB,QAAO,4BAA2B;AAE5E,SAAQC,cAAc,QAAO,sBAAqB;AAElD,MAAMC,QAAQP,SAAS;AAEvB;;;;;;CAMC,GACD,OAAO,eAAeQ,YAAY,EAChCC,YAAY,EACZC,OAAO,EACPC,iBAAiB,EACjBC,WAAW,EAMZ;IACC,IAAID,sBAAsB,UAAU;QAClC,OAAO;YACLE,MAAM;YACNC,OAAO;YACPC,KAAK,MAAMV;QACb;IACF;IAEA,IAAIW;IAEJ,IAAI;QACF,IAAIN,SAAS;YACX,OAAOJ,eAAeI,SAASE;QACjC;QAEAI,OAAOd,QAAQ,yBAAyBe,KAAK;QAC7C,6CAA6C;QAC7C,IAAI,EAACC,SAAS,EAAC,GAAG,MAAMd;QACxB,IAAIK,cAAc;YAChBS,YAAY;mBAAIA;gBAAW;oBAACL,MAAM;oBAAOC,OAAO;oBAAOC,KAAK;gBAAY;aAAE;QAC5E;QACAC,KAAKG,IAAI;QAET,yEAAyE;QACzE,MAAMC,oBAAoBF,UAAUG,MAAM,CAAC,CAACC,IAAMA,EAAET,IAAI,KAAK,OAAOU,GAAG,CAAC,CAACD,IAAMA,EAAET,IAAI;QAErF,IAAIF,mBAAmB;YACrB,MAAMa,WAAWN,UAAUO,IAAI,CAAC,CAACC,OAASA,KAAKb,IAAI,KAAKF;YACxD,IAAI,CAACa,UAAU;gBACb,MAAM,IAAIG,MACR,CAAC,sCAAsC,EAAEhB,kBAAkB,GAAG,CAAC,GAC5DS,CAAAA,kBAAkBQ,MAAM,GAAG,IACxB,CAAC,qBAAqB,EAAER,kBAAkBS,IAAI,CAAC,OAAO,GACtD,4BAA2B;YAErC;YACA,OAAOL;QACT;QAEA,IAAIN,UAAUU,MAAM,KAAK,GAAG;YAC1B,OAAOE;QACT;QAEA,IAAI,CAAC/B,mBAAmBqB,kBAAkBQ,MAAM,GAAG,GAAG;YACpD,MAAM,IAAID,MACR,CAAC,oCAAoC,EAAEP,kBAAkBS,IAAI,CAAC,MAAM,EAAE,CAAC,GACrE;QAEN;QAEA,MAAML,WAAW,MAAMrB,mBAAmBe;QAC1C,IAAIM,SAASX,IAAI,KAAK,OAAO;YAC3B,MAAMH,UAAU,MAAMT,MAAM;gBAAC8B,SAAS;YAAoB;YAC1D,OAAOzB,eAAeI;QACxB;QAEA,OAAOc;IACT,EAAE,OAAOQ,KAAK;QACZhB,MAAMG;QACNZ,MAAM,8BAA8ByB;QACpC,MAAMA;IACR;AACF"}
1
+ {"version":3,"sources":["../../../../src/actions/auth/login/getProvider.ts"],"sourcesContent":["import {isInteractive, subdebug} from '@sanity/cli-core'\nimport {input, spinner, type SpinnerInstance} from '@sanity/cli-core/ux'\n\nimport {promptForProviders} from '../../../prompts/promptForProviders.js'\nimport {getProviders, getVercelProviderUrl} from '../../../services/auth.js'\nimport {type LoginProvider} from '../types.js'\nimport {getSSOProvider} from './getSSOProvider.js'\n\nconst debug = subdebug('login:getProvider')\n\n/**\n * Prompt the user to select a login provider, or use the specified provider if given.\n *\n * @param options - Options for the provider resolve operation\n * @returns Promise that resolves to the selected login provider\n * @internal\n */\nexport async function getProvider({\n experimental,\n orgSlug,\n specifiedProvider,\n ssoProvider,\n}: {\n experimental: boolean | undefined\n orgSlug: string | undefined\n specifiedProvider: string | undefined\n ssoProvider: string | undefined\n}): Promise<LoginProvider | undefined> {\n if (specifiedProvider === 'vercel') {\n return {\n name: 'vercel',\n title: 'Vercel',\n url: await getVercelProviderUrl(),\n }\n }\n\n let spin: SpinnerInstance | undefined\n\n try {\n if (orgSlug) {\n return getSSOProvider(orgSlug, ssoProvider)\n }\n\n spin = spinner('Fetching providers...').start()\n // Fetch and prompt for login provider to use\n let {providers} = await getProviders()\n if (experimental) {\n providers = [...providers, {name: 'sso', title: 'SSO', url: '_not_used_'}]\n }\n spin.stop()\n\n // Real providers excludes the synthetic SSO entry used by --experimental\n const realProviderNames = providers.filter((p) => p.name !== 'sso').map((p) => p.name)\n\n if (specifiedProvider) {\n const provider = providers.find((prov) => prov.name === specifiedProvider)\n if (!provider) {\n throw new Error(\n `Cannot find login provider with name \"${specifiedProvider}\". ` +\n (realProviderNames.length > 0\n ? `Available providers: ${realProviderNames.join(', ')}`\n : 'No providers are available'),\n )\n }\n return provider\n }\n\n if (providers.length === 0) {\n return undefined\n }\n\n if (!isInteractive()) {\n if (realProviderNames.length === 1) {\n return providers.find((provider) => provider.name === realProviderNames[0])\n }\n\n if (realProviderNames.length > 1) {\n throw new Error(\n `Multiple login providers available: ${realProviderNames.join(', ')}. ` +\n 'Use `--provider <name>` to select one in unattended mode.',\n )\n }\n }\n\n const provider = await promptForProviders(providers)\n if (provider.name === 'sso') {\n const orgSlug = await input({message: 'Organization slug:'})\n return getSSOProvider(orgSlug)\n }\n\n return provider\n } catch (err) {\n spin?.stop()\n debug('Error retrieving providers', err)\n throw err\n }\n}\n"],"names":["isInteractive","subdebug","input","spinner","promptForProviders","getProviders","getVercelProviderUrl","getSSOProvider","debug","getProvider","experimental","orgSlug","specifiedProvider","ssoProvider","name","title","url","spin","start","providers","stop","realProviderNames","filter","p","map","provider","find","prov","Error","length","join","undefined","message","err"],"mappings":"AAAA,SAAQA,aAAa,EAAEC,QAAQ,QAAO,mBAAkB;AACxD,SAAQC,KAAK,EAAEC,OAAO,QAA6B,sBAAqB;AAExE,SAAQC,kBAAkB,QAAO,yCAAwC;AACzE,SAAQC,YAAY,EAAEC,oBAAoB,QAAO,4BAA2B;AAE5E,SAAQC,cAAc,QAAO,sBAAqB;AAElD,MAAMC,QAAQP,SAAS;AAEvB;;;;;;CAMC,GACD,OAAO,eAAeQ,YAAY,EAChCC,YAAY,EACZC,OAAO,EACPC,iBAAiB,EACjBC,WAAW,EAMZ;IACC,IAAID,sBAAsB,UAAU;QAClC,OAAO;YACLE,MAAM;YACNC,OAAO;YACPC,KAAK,MAAMV;QACb;IACF;IAEA,IAAIW;IAEJ,IAAI;QACF,IAAIN,SAAS;YACX,OAAOJ,eAAeI,SAASE;QACjC;QAEAI,OAAOd,QAAQ,yBAAyBe,KAAK;QAC7C,6CAA6C;QAC7C,IAAI,EAACC,SAAS,EAAC,GAAG,MAAMd;QACxB,IAAIK,cAAc;YAChBS,YAAY;mBAAIA;gBAAW;oBAACL,MAAM;oBAAOC,OAAO;oBAAOC,KAAK;gBAAY;aAAE;QAC5E;QACAC,KAAKG,IAAI;QAET,yEAAyE;QACzE,MAAMC,oBAAoBF,UAAUG,MAAM,CAAC,CAACC,IAAMA,EAAET,IAAI,KAAK,OAAOU,GAAG,CAAC,CAACD,IAAMA,EAAET,IAAI;QAErF,IAAIF,mBAAmB;YACrB,MAAMa,WAAWN,UAAUO,IAAI,CAAC,CAACC,OAASA,KAAKb,IAAI,KAAKF;YACxD,IAAI,CAACa,UAAU;gBACb,MAAM,IAAIG,MACR,CAAC,sCAAsC,EAAEhB,kBAAkB,GAAG,CAAC,GAC5DS,CAAAA,kBAAkBQ,MAAM,GAAG,IACxB,CAAC,qBAAqB,EAAER,kBAAkBS,IAAI,CAAC,OAAO,GACtD,4BAA2B;YAErC;YACA,OAAOL;QACT;QAEA,IAAIN,UAAUU,MAAM,KAAK,GAAG;YAC1B,OAAOE;QACT;QAEA,IAAI,CAAC/B,iBAAiB;YACpB,IAAIqB,kBAAkBQ,MAAM,KAAK,GAAG;gBAClC,OAAOV,UAAUO,IAAI,CAAC,CAACD,WAAaA,SAASX,IAAI,KAAKO,iBAAiB,CAAC,EAAE;YAC5E;YAEA,IAAIA,kBAAkBQ,MAAM,GAAG,GAAG;gBAChC,MAAM,IAAID,MACR,CAAC,oCAAoC,EAAEP,kBAAkBS,IAAI,CAAC,MAAM,EAAE,CAAC,GACrE;YAEN;QACF;QAEA,MAAML,WAAW,MAAMrB,mBAAmBe;QAC1C,IAAIM,SAASX,IAAI,KAAK,OAAO;YAC3B,MAAMH,UAAU,MAAMT,MAAM;gBAAC8B,SAAS;YAAoB;YAC1D,OAAOzB,eAAeI;QACxB;QAEA,OAAOc;IACT,EAAE,OAAOQ,KAAK;QACZhB,MAAMG;QACNZ,MAAM,8BAA8ByB;QACpC,MAAMA;IACR;AACF"}
@@ -0,0 +1,19 @@
1
+ export const LOGIN_PROVIDER_IDS = [
2
+ 'google',
3
+ 'github',
4
+ 'sanity',
5
+ 'vercel'
6
+ ];
7
+ export const LOGIN_REQUIRED_MESSAGE = [
8
+ 'No valid authentication credentials found.',
9
+ '',
10
+ 'Authenticate with one of these commands:',
11
+ ' echo "$TOKEN" | sanity login --with-token',
12
+ ' sanity login --provider <providerId> --no-open',
13
+ ` Provider IDs: ${LOGIN_PROVIDER_IDS.join(', ')}`,
14
+ ' sanity login --sso <organizationSlug> --no-open',
15
+ '',
16
+ '`--no-open` prints a login URL instead of opening a browser.'
17
+ ].join('\n');
18
+
19
+ //# sourceMappingURL=loginInstructions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/actions/auth/login/loginInstructions.ts"],"sourcesContent":["export const LOGIN_PROVIDER_IDS = ['google', 'github', 'sanity', 'vercel'] as const\n\nexport const LOGIN_REQUIRED_MESSAGE = [\n 'No valid authentication credentials found.',\n '',\n 'Authenticate with one of these commands:',\n ' echo \"$TOKEN\" | sanity login --with-token',\n ' sanity login --provider <providerId> --no-open',\n ` Provider IDs: ${LOGIN_PROVIDER_IDS.join(', ')}`,\n ' sanity login --sso <organizationSlug> --no-open',\n '',\n '`--no-open` prints a login URL instead of opening a browser.',\n].join('\\n')\n"],"names":["LOGIN_PROVIDER_IDS","LOGIN_REQUIRED_MESSAGE","join"],"mappings":"AAAA,OAAO,MAAMA,qBAAqB;IAAC;IAAU;IAAU;IAAU;CAAS,CAAS;AAEnF,OAAO,MAAMC,yBAAyB;IACpC;IACA;IACA;IACA;IACA;IACA,CAAC,kBAAkB,EAAED,mBAAmBE,IAAI,CAAC,OAAO;IACpD;IACA;IACA;CACD,CAACA,IAAI,CAAC,MAAK"}
@@ -16,7 +16,7 @@ const debug = subdebug('dataset:create');
16
16
  * @returns Promise resolving when dataset is created
17
17
  * @throws Error if dataset creation fails
18
18
  */ export async function createDataset(options) {
19
- const { datasetName, embeddings, embeddingsProjection, forcePublic = false, isUnattended = false, output, projectFeatures, projectId, visibility } = options;
19
+ const { datasetName, embeddings, embeddingsProjection, forcePublic = false, isUnattended = false, output, projectFeatures, projectId, silent = false, visibility } = options;
20
20
  const canCreatePrivate = projectFeatures.includes('privateDataset') && !forcePublic;
21
21
  // Determine the appropriate ACL mode
22
22
  const aclMode = await determineDatasetAclMode({
@@ -42,7 +42,7 @@ const debug = subdebug('dataset:create');
42
42
  projectId
43
43
  });
44
44
  spin.succeed();
45
- output.log(`Dataset created successfully`);
45
+ if (!silent) output.log(`Dataset created successfully`);
46
46
  return newDataset;
47
47
  } catch (error) {
48
48
  debug('Error creating dataset', {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/dataset/create.ts"],"sourcesContent":["import {type Output, subdebug} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {type DatasetAclMode, DatasetResponse} from '@sanity/client'\n\nimport {createDataset as createDatasetService} from '../../services/datasets.js'\nimport {validateProjection} from '../../util/validateProjection.js'\nimport {determineDatasetAclMode} from './determineDatasetAclMode.js'\n\nconst debug = subdebug('dataset:create')\n\n/**\n * Options for creating a dataset\n */\ninterface CreateDatasetOptions {\n /**\n * Name of the dataset to create\n */\n datasetName: string\n\n /**\n * Output instance for logging\n */\n output: Output\n\n /**\n * Array of project features to determine capabilities\n * Used to check if private datasets are available\n */\n projectFeatures: string[]\n\n /**\n * Project ID where the dataset will be created\n */\n projectId: string\n\n /**\n * Whether to enable embeddings for the new dataset\n */\n embeddings?: boolean\n\n /**\n * GROQ projection for embeddings indexing (e.g. \"\\{ title, body \\}\")\n * Only used when embeddings is true\n */\n embeddingsProjection?: string\n\n /**\n * Whether to force disable private dataset creation\n * Used when default config is selected (which forces public datasets)\n */\n forcePublic?: boolean\n\n /**\n * Whether to run in unattended mode (no prompts)\n */\n isUnattended?: boolean\n\n /**\n * Requested visibility mode from flags/options\n */\n visibility?: string\n}\n\n/**\n * Creates a new dataset with the appropriate ACL mode.\n *\n * This action handles the business logic for:\n * - Determining the appropriate ACL mode based on project capabilities\n * - Creating the dataset via the service layer\n * - Handling errors and providing user feedback\n *\n * @param options - Configuration options\n * @returns Promise resolving when dataset is created\n * @throws Error if dataset creation fails\n */\nexport async function createDataset(options: CreateDatasetOptions): Promise<DatasetResponse> {\n const {\n datasetName,\n embeddings,\n embeddingsProjection,\n forcePublic = false,\n isUnattended = false,\n output,\n projectFeatures,\n projectId,\n visibility,\n } = options\n\n const canCreatePrivate = projectFeatures.includes('privateDataset') && !forcePublic\n\n // Determine the appropriate ACL mode\n const aclMode: DatasetAclMode = await determineDatasetAclMode({\n canCreatePrivate,\n isUnattended,\n output,\n visibility,\n })\n\n if (embeddingsProjection) {\n validateProjection(embeddingsProjection)\n }\n\n try {\n const spin = spinner('Creating dataset').start()\n const newDataset = await createDatasetService({\n aclMode,\n datasetName,\n embeddings: embeddings\n ? {enabled: true, ...(embeddingsProjection ? {projection: embeddingsProjection} : {})}\n : undefined,\n projectId,\n })\n spin.succeed()\n output.log(`Dataset created successfully`)\n return newDataset\n } catch (error) {\n debug('Error creating dataset', {datasetName, error})\n throw error\n }\n}\n"],"names":["subdebug","spinner","createDataset","createDatasetService","validateProjection","determineDatasetAclMode","debug","options","datasetName","embeddings","embeddingsProjection","forcePublic","isUnattended","output","projectFeatures","projectId","visibility","canCreatePrivate","includes","aclMode","spin","start","newDataset","enabled","projection","undefined","succeed","log","error"],"mappings":"AAAA,SAAqBA,QAAQ,QAAO,mBAAkB;AACtD,SAAQC,OAAO,QAAO,sBAAqB;AAG3C,SAAQC,iBAAiBC,oBAAoB,QAAO,6BAA4B;AAChF,SAAQC,kBAAkB,QAAO,mCAAkC;AACnE,SAAQC,uBAAuB,QAAO,+BAA8B;AAEpE,MAAMC,QAAQN,SAAS;AAuDvB;;;;;;;;;;;CAWC,GACD,OAAO,eAAeE,cAAcK,OAA6B;IAC/D,MAAM,EACJC,WAAW,EACXC,UAAU,EACVC,oBAAoB,EACpBC,cAAc,KAAK,EACnBC,eAAe,KAAK,EACpBC,MAAM,EACNC,eAAe,EACfC,SAAS,EACTC,UAAU,EACX,GAAGT;IAEJ,MAAMU,mBAAmBH,gBAAgBI,QAAQ,CAAC,qBAAqB,CAACP;IAExE,qCAAqC;IACrC,MAAMQ,UAA0B,MAAMd,wBAAwB;QAC5DY;QACAL;QACAC;QACAG;IACF;IAEA,IAAIN,sBAAsB;QACxBN,mBAAmBM;IACrB;IAEA,IAAI;QACF,MAAMU,OAAOnB,QAAQ,oBAAoBoB,KAAK;QAC9C,MAAMC,aAAa,MAAMnB,qBAAqB;YAC5CgB;YACAX;YACAC,YAAYA,aACR;gBAACc,SAAS;gBAAM,GAAIb,uBAAuB;oBAACc,YAAYd;gBAAoB,IAAI,CAAC,CAAC;YAAC,IACnFe;YACJV;QACF;QACAK,KAAKM,OAAO;QACZb,OAAOc,GAAG,CAAC,CAAC,4BAA4B,CAAC;QACzC,OAAOL;IACT,EAAE,OAAOM,OAAO;QACdtB,MAAM,0BAA0B;YAACE;YAAaoB;QAAK;QACnD,MAAMA;IACR;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/dataset/create.ts"],"sourcesContent":["import {type Output, subdebug} from '@sanity/cli-core'\nimport {spinner} from '@sanity/cli-core/ux'\nimport {type DatasetAclMode, DatasetResponse} from '@sanity/client'\n\nimport {createDataset as createDatasetService} from '../../services/datasets.js'\nimport {validateProjection} from '../../util/validateProjection.js'\nimport {determineDatasetAclMode} from './determineDatasetAclMode.js'\n\nconst debug = subdebug('dataset:create')\n\n/**\n * Options for creating a dataset\n */\ninterface CreateDatasetOptions {\n /**\n * Name of the dataset to create\n */\n datasetName: string\n\n /**\n * Output instance for logging\n */\n output: Output\n\n /**\n * Array of project features to determine capabilities\n * Used to check if private datasets are available\n */\n projectFeatures: string[]\n\n /**\n * Project ID where the dataset will be created\n */\n projectId: string\n\n /**\n * Whether to enable embeddings for the new dataset\n */\n embeddings?: boolean\n\n /**\n * GROQ projection for embeddings indexing (e.g. \"\\{ title, body \\}\")\n * Only used when embeddings is true\n */\n embeddingsProjection?: string\n\n /**\n * Whether to force disable private dataset creation\n * Used when default config is selected (which forces public datasets)\n */\n forcePublic?: boolean\n\n /**\n * Whether to run in unattended mode (no prompts)\n */\n isUnattended?: boolean\n\n /**\n * Whether to omit human-readable success output\n */\n silent?: boolean\n\n /**\n * Requested visibility mode from flags/options\n */\n visibility?: string\n}\n\n/**\n * Creates a new dataset with the appropriate ACL mode.\n *\n * This action handles the business logic for:\n * - Determining the appropriate ACL mode based on project capabilities\n * - Creating the dataset via the service layer\n * - Handling errors and providing user feedback\n *\n * @param options - Configuration options\n * @returns Promise resolving when dataset is created\n * @throws Error if dataset creation fails\n */\nexport async function createDataset(options: CreateDatasetOptions): Promise<DatasetResponse> {\n const {\n datasetName,\n embeddings,\n embeddingsProjection,\n forcePublic = false,\n isUnattended = false,\n output,\n projectFeatures,\n projectId,\n silent = false,\n visibility,\n } = options\n\n const canCreatePrivate = projectFeatures.includes('privateDataset') && !forcePublic\n\n // Determine the appropriate ACL mode\n const aclMode: DatasetAclMode = await determineDatasetAclMode({\n canCreatePrivate,\n isUnattended,\n output,\n visibility,\n })\n\n if (embeddingsProjection) {\n validateProjection(embeddingsProjection)\n }\n\n try {\n const spin = spinner('Creating dataset').start()\n const newDataset = await createDatasetService({\n aclMode,\n datasetName,\n embeddings: embeddings\n ? {enabled: true, ...(embeddingsProjection ? {projection: embeddingsProjection} : {})}\n : undefined,\n projectId,\n })\n spin.succeed()\n if (!silent) output.log(`Dataset created successfully`)\n return newDataset\n } catch (error) {\n debug('Error creating dataset', {datasetName, error})\n throw error\n }\n}\n"],"names":["subdebug","spinner","createDataset","createDatasetService","validateProjection","determineDatasetAclMode","debug","options","datasetName","embeddings","embeddingsProjection","forcePublic","isUnattended","output","projectFeatures","projectId","silent","visibility","canCreatePrivate","includes","aclMode","spin","start","newDataset","enabled","projection","undefined","succeed","log","error"],"mappings":"AAAA,SAAqBA,QAAQ,QAAO,mBAAkB;AACtD,SAAQC,OAAO,QAAO,sBAAqB;AAG3C,SAAQC,iBAAiBC,oBAAoB,QAAO,6BAA4B;AAChF,SAAQC,kBAAkB,QAAO,mCAAkC;AACnE,SAAQC,uBAAuB,QAAO,+BAA8B;AAEpE,MAAMC,QAAQN,SAAS;AA4DvB;;;;;;;;;;;CAWC,GACD,OAAO,eAAeE,cAAcK,OAA6B;IAC/D,MAAM,EACJC,WAAW,EACXC,UAAU,EACVC,oBAAoB,EACpBC,cAAc,KAAK,EACnBC,eAAe,KAAK,EACpBC,MAAM,EACNC,eAAe,EACfC,SAAS,EACTC,SAAS,KAAK,EACdC,UAAU,EACX,GAAGV;IAEJ,MAAMW,mBAAmBJ,gBAAgBK,QAAQ,CAAC,qBAAqB,CAACR;IAExE,qCAAqC;IACrC,MAAMS,UAA0B,MAAMf,wBAAwB;QAC5Da;QACAN;QACAC;QACAI;IACF;IAEA,IAAIP,sBAAsB;QACxBN,mBAAmBM;IACrB;IAEA,IAAI;QACF,MAAMW,OAAOpB,QAAQ,oBAAoBqB,KAAK;QAC9C,MAAMC,aAAa,MAAMpB,qBAAqB;YAC5CiB;YACAZ;YACAC,YAAYA,aACR;gBAACe,SAAS;gBAAM,GAAId,uBAAuB;oBAACe,YAAYf;gBAAoB,IAAI,CAAC,CAAC;YAAC,IACnFgB;YACJX;QACF;QACAM,KAAKM,OAAO;QACZ,IAAI,CAACX,QAAQH,OAAOe,GAAG,CAAC,CAAC,4BAA4B,CAAC;QACtD,OAAOL;IACT,EAAE,OAAOM,OAAO;QACdvB,MAAM,0BAA0B;YAACE;YAAaqB;QAAK;QACnD,MAAMA;IACR;AACF"}
@@ -1,3 +1,4 @@
1
+ import { exitCodes } from '@sanity/cli-core';
1
2
  import { promptForDataset } from '../../prompts/promptForDataset.js';
2
3
  import { listDatasets } from '../../services/datasets.js';
3
4
  import { assertDatasetExists } from '../backup/assertDatasetExist.js';
@@ -5,7 +6,12 @@ import { assertDatasetExists } from '../backup/assertDatasetExist.js';
5
6
  * Lists datasets for a project, validates the given dataset name (or prompts
6
7
  * the user to pick one), and returns the resolved name together with the full
7
8
  * dataset list.
8
- */ export async function resolveDataset({ dataset, output, projectId }) {
9
+ */ export async function resolveDataset({ dataset, isUnattended, output, projectId }) {
10
+ if (!dataset && isUnattended) {
11
+ output.error('Dataset name is required. Pass it as the `<dataset>` argument.', {
12
+ exit: exitCodes.USAGE_ERROR
13
+ });
14
+ }
9
15
  const datasets = await listDatasets(projectId);
10
16
  if (datasets.length === 0) {
11
17
  throw new Error('No datasets found in this project.');
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/dataset/resolveDataset.ts"],"sourcesContent":["import {type Output} from '@sanity/cli-core'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {assertDatasetExists} from '../backup/assertDatasetExist.js'\n\ninterface ResolveDatasetOptions {\n /** Output to use for user-facing messages from the calling command */\n output: Output\n\n projectId: string\n\n dataset?: string\n}\n\ninterface ResolveDatasetResult {\n dataset: string\n datasets: DatasetsResponse\n}\n\n/**\n * Lists datasets for a project, validates the given dataset name (or prompts\n * the user to pick one), and returns the resolved name together with the full\n * dataset list.\n */\nexport async function resolveDataset({\n dataset,\n output,\n projectId,\n}: ResolveDatasetOptions): Promise<ResolveDatasetResult> {\n const datasets = await listDatasets(projectId)\n\n if (datasets.length === 0) {\n throw new Error('No datasets found in this project.')\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, output)\n } else {\n dataset = await promptForDataset({datasets})\n }\n\n return {dataset, datasets}\n}\n"],"names":["promptForDataset","listDatasets","assertDatasetExists","resolveDataset","dataset","output","projectId","datasets","length","Error"],"mappings":"AAGA,SAAQA,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,mBAAmB,QAAO,kCAAiC;AAgBnE;;;;CAIC,GACD,OAAO,eAAeC,eAAe,EACnCC,OAAO,EACPC,MAAM,EACNC,SAAS,EACa;IACtB,MAAMC,WAAW,MAAMN,aAAaK;IAEpC,IAAIC,SAASC,MAAM,KAAK,GAAG;QACzB,MAAM,IAAIC,MAAM;IAClB;IAEA,IAAIL,SAAS;QACXF,oBAAoBK,UAAUH,SAASC;IACzC,OAAO;QACLD,UAAU,MAAMJ,iBAAiB;YAACO;QAAQ;IAC5C;IAEA,OAAO;QAACH;QAASG;IAAQ;AAC3B"}
1
+ {"version":3,"sources":["../../../src/actions/dataset/resolveDataset.ts"],"sourcesContent":["import {exitCodes, type Output} from '@sanity/cli-core'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {assertDatasetExists} from '../backup/assertDatasetExist.js'\n\ninterface ResolveDatasetOptions {\n /** Output to use for user-facing messages from the calling command */\n output: Output\n\n projectId: string\n\n dataset?: string\n isUnattended?: boolean\n}\n\ninterface ResolveDatasetResult {\n dataset: string\n datasets: DatasetsResponse\n}\n\n/**\n * Lists datasets for a project, validates the given dataset name (or prompts\n * the user to pick one), and returns the resolved name together with the full\n * dataset list.\n */\nexport async function resolveDataset({\n dataset,\n isUnattended,\n output,\n projectId,\n}: ResolveDatasetOptions): Promise<ResolveDatasetResult> {\n if (!dataset && isUnattended) {\n output.error('Dataset name is required. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n }\n\n const datasets = await listDatasets(projectId)\n\n if (datasets.length === 0) {\n throw new Error('No datasets found in this project.')\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, output)\n } else {\n dataset = await promptForDataset({datasets})\n }\n\n return {dataset, datasets}\n}\n"],"names":["exitCodes","promptForDataset","listDatasets","assertDatasetExists","resolveDataset","dataset","isUnattended","output","projectId","error","exit","USAGE_ERROR","datasets","length","Error"],"mappings":"AAAA,SAAQA,SAAS,QAAoB,mBAAkB;AAGvD,SAAQC,gBAAgB,QAAO,oCAAmC;AAClE,SAAQC,YAAY,QAAO,6BAA4B;AACvD,SAAQC,mBAAmB,QAAO,kCAAiC;AAiBnE;;;;CAIC,GACD,OAAO,eAAeC,eAAe,EACnCC,OAAO,EACPC,YAAY,EACZC,MAAM,EACNC,SAAS,EACa;IACtB,IAAI,CAACH,WAAWC,cAAc;QAC5BC,OAAOE,KAAK,CAAC,kEAAkE;YAC7EC,MAAMV,UAAUW,WAAW;QAC7B;IACF;IAEA,MAAMC,WAAW,MAAMV,aAAaM;IAEpC,IAAII,SAASC,MAAM,KAAK,GAAG;QACzB,MAAM,IAAIC,MAAM;IAClB;IAEA,IAAIT,SAAS;QACXF,oBAAoBS,UAAUP,SAASE;IACzC,OAAO;QACLF,UAAU,MAAMJ,iBAAiB;YAACW;QAAQ;IAC5C;IAEA,OAAO;QAACP;QAASO;IAAQ;AAC3B"}
@@ -78,7 +78,7 @@ export async function createStudioUserApplication(options) {
78
78
  });
79
79
  return await promise;
80
80
  }
81
- export async function createUserApplication(organizationId, title) {
81
+ export async function createUserApplication(organizationId, title, visibility) {
82
82
  if (!organizationId) {
83
83
  throw new Error(NO_ORGANIZATION_ID);
84
84
  }
@@ -86,9 +86,9 @@ export async function createUserApplication(organizationId, title) {
86
86
  message: 'Enter a title for your application:',
87
87
  validate: (value)=>value.length > 0 || 'Title is required'
88
88
  });
89
- return tryCreateApp(resolvedTitle, organizationId);
89
+ return tryCreateApp(resolvedTitle, organizationId, visibility);
90
90
  }
91
- const tryCreateApp = async (title, organizationId)=>{
91
+ const tryCreateApp = async (title, organizationId, visibility)=>{
92
92
  // we will likely prepend this with an org ID or other parameter in the future
93
93
  const appHost = generateAppSlug();
94
94
  const spin = spinner('Creating application').start();
@@ -99,7 +99,10 @@ const tryCreateApp = async (title, organizationId)=>{
99
99
  appHost,
100
100
  title,
101
101
  type: 'coreApp',
102
- urlType: 'internal'
102
+ urlType: 'internal',
103
+ ...visibility ? {
104
+ dashboardStatus: visibility
105
+ } : {}
103
106
  },
104
107
  organizationId
105
108
  });
@@ -112,7 +115,7 @@ const tryCreateApp = async (title, organizationId)=>{
112
115
  409
113
116
  ].includes(e?.statusCode)) {
114
117
  deployDebug('App host taken, retrying with new host');
115
- return tryCreateApp(title, organizationId);
118
+ return tryCreateApp(title, organizationId, visibility);
116
119
  }
117
120
  spin.fail();
118
121
  deployDebug('Error creating core application', e);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/actions/deploy/createUserApplication.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {input, spinner} from '@sanity/cli-core/ux'\nimport {customAlphabet} from 'nanoid'\n\nimport {\n createUserApplication as createUserApplicationRequest,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {NO_ORGANIZATION_ID} from '../../util/errorMessages.js'\nimport {deployDebug} from './deployDebug.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\nconst LETTERS = 'abcdefghijklmnopqrstuvwxyz'\n\n// appHosts have some restrictions (no uppercase, must start with a letter)\nexport function generateAppSlug(): string {\n const firstChar = customAlphabet(LETTERS, 1)()\n const rest = customAlphabet(`${LETTERS}0123456789`, 11)()\n return `${firstChar}${rest}`\n}\n\n// TODO: replace with `Promise.withResolvers()` once it lands in node 22\nfunction promiseWithResolvers<T>() {\n let resolve!: (t: T) => void\n let reject!: (err: unknown) => void\n const promise = new Promise<T>((res, rej) => {\n resolve = res\n reject = rej\n })\n return {promise, reject, resolve}\n}\n\ninterface CreateStudioUserApplicationOptions {\n projectId: string\n\n title?: string\n urlType?: 'external' | 'internal'\n}\n\nexport async function createStudioUserApplication(options: CreateStudioUserApplicationOptions) {\n const {projectId, title, urlType = 'internal'} = options\n const {promise, resolve} = promiseWithResolvers<UserApplication>()\n\n const isExternal = urlType === 'external'\n\n await input({\n message: isExternal ? 'Studio URL (https://...):' : 'Studio hostname (<value>.sanity.studio):',\n // if a string is returned here, it is relayed to the user and prompt allows\n // the user to try again until this function returns true\n validate: async (inp: string) => {\n let appHost: string\n\n if (isExternal) {\n const normalized = normalizeUrl(inp)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return validation\n }\n appHost = normalized\n } else {\n appHost = inp.replace(/\\.sanity\\.studio$/i, '')\n }\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'studio',\n body: {appHost, title, type: 'studio', urlType},\n projectId,\n })\n resolve(response)\n return true\n } catch (e) {\n // if the name is taken, it should return a 409 so we relay to the user\n if ([402, 409].includes(e?.statusCode)) {\n return e?.response?.body?.message || 'Bad request' // just in case\n }\n\n deployDebug('Error creating user application', e)\n // otherwise, it's a fatal error\n throw new CLIError('Error creating user application', {exit: 1})\n }\n },\n })\n\n return await promise\n}\n\nexport async function createUserApplication(\n organizationId?: string,\n title?: string,\n): Promise<UserApplicationResolved> {\n if (!organizationId) {\n throw new Error(NO_ORGANIZATION_ID)\n }\n\n const resolvedTitle =\n title ??\n (await input({\n message: 'Enter a title for your application:',\n validate: (value: string) => value.length > 0 || 'Title is required',\n }))\n\n return tryCreateApp(resolvedTitle, organizationId)\n}\n\nconst tryCreateApp = async (title: string, organizationId: string) => {\n // we will likely prepend this with an org ID or other parameter in the future\n const appHost = generateAppSlug()\n\n const spin = spinner('Creating application').start()\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'coreApp',\n body: {appHost, title, type: 'coreApp', urlType: 'internal'},\n organizationId,\n })\n\n spin.succeed()\n return response\n } catch (e) {\n // if the name is taken, generate a new one and try again\n if ([402, 409].includes(e?.statusCode)) {\n deployDebug('App host taken, retrying with new host')\n return tryCreateApp(title, organizationId)\n }\n\n spin.fail()\n\n deployDebug('Error creating core application', e)\n throw e\n }\n}\n"],"names":["CLIError","input","spinner","customAlphabet","createUserApplication","createUserApplicationRequest","NO_ORGANIZATION_ID","deployDebug","normalizeUrl","validateUrl","LETTERS","generateAppSlug","firstChar","rest","promiseWithResolvers","resolve","reject","promise","Promise","res","rej","createStudioUserApplication","options","projectId","title","urlType","isExternal","message","validate","inp","appHost","normalized","validation","replace","response","appType","body","type","e","includes","statusCode","exit","organizationId","Error","resolvedTitle","value","length","tryCreateApp","spin","start","succeed","fail"],"mappings":"AAAA,SAAQA,QAAQ,QAAO,qBAAoB;AAC3C,SAAQC,KAAK,EAAEC,OAAO,QAAO,sBAAqB;AAClD,SAAQC,cAAc,QAAO,SAAQ;AAErC,SACEC,yBAAyBC,4BAA4B,QAGhD,qCAAoC;AAC3C,SAAQC,kBAAkB,QAAO,8BAA6B;AAC9D,SAAQC,WAAW,QAAO,mBAAkB;AAC5C,SAAQC,YAAY,EAAEC,WAAW,QAAO,gBAAe;AAEvD,MAAMC,UAAU;AAEhB,2EAA2E;AAC3E,OAAO,SAASC;IACd,MAAMC,YAAYT,eAAeO,SAAS;IAC1C,MAAMG,OAAOV,eAAe,GAAGO,QAAQ,UAAU,CAAC,EAAE;IACpD,OAAO,GAAGE,YAAYC,MAAM;AAC9B;AAEA,wEAAwE;AACxE,SAASC;IACP,IAAIC;IACJ,IAAIC;IACJ,MAAMC,UAAU,IAAIC,QAAW,CAACC,KAAKC;QACnCL,UAAUI;QACVH,SAASI;IACX;IACA,OAAO;QAACH;QAASD;QAAQD;IAAO;AAClC;AASA,OAAO,eAAeM,4BAA4BC,OAA2C;IAC3F,MAAM,EAACC,SAAS,EAAEC,KAAK,EAAEC,UAAU,UAAU,EAAC,GAAGH;IACjD,MAAM,EAACL,OAAO,EAAEF,OAAO,EAAC,GAAGD;IAE3B,MAAMY,aAAaD,YAAY;IAE/B,MAAMxB,MAAM;QACV0B,SAASD,aAAa,8BAA8B;QACpD,4EAA4E;QAC5E,yDAAyD;QACzDE,UAAU,OAAOC;YACf,IAAIC;YAEJ,IAAIJ,YAAY;gBACd,MAAMK,aAAavB,aAAaqB;gBAChC,MAAMG,aAAavB,YAAYsB;gBAC/B,IAAIC,eAAe,MAAM;oBACvB,OAAOA;gBACT;gBACAF,UAAUC;YACZ,OAAO;gBACLD,UAAUD,IAAII,OAAO,CAAC,sBAAsB;YAC9C;YAEA,IAAI;gBACF,MAAMC,WAAW,MAAM7B,6BAA6B;oBAClD8B,SAAS;oBACTC,MAAM;wBAACN;wBAASN;wBAAOa,MAAM;wBAAUZ;oBAAO;oBAC9CF;gBACF;gBACAR,QAAQmB;gBACR,OAAO;YACT,EAAE,OAAOI,GAAG;gBACV,uEAAuE;gBACvE,IAAI;oBAAC;oBAAK;iBAAI,CAACC,QAAQ,CAACD,GAAGE,aAAa;oBACtC,OAAOF,GAAGJ,UAAUE,MAAMT,WAAW,cAAc,eAAe;;gBACpE;gBAEApB,YAAY,mCAAmC+B;gBAC/C,gCAAgC;gBAChC,MAAM,IAAItC,SAAS,mCAAmC;oBAACyC,MAAM;gBAAC;YAChE;QACF;IACF;IAEA,OAAO,MAAMxB;AACf;AAEA,OAAO,eAAeb,sBACpBsC,cAAuB,EACvBlB,KAAc;IAEd,IAAI,CAACkB,gBAAgB;QACnB,MAAM,IAAIC,MAAMrC;IAClB;IAEA,MAAMsC,gBACJpB,SACC,MAAMvB,MAAM;QACX0B,SAAS;QACTC,UAAU,CAACiB,QAAkBA,MAAMC,MAAM,GAAG,KAAK;IACnD;IAEF,OAAOC,aAAaH,eAAeF;AACrC;AAEA,MAAMK,eAAe,OAAOvB,OAAekB;IACzC,8EAA8E;IAC9E,MAAMZ,UAAUnB;IAEhB,MAAMqC,OAAO9C,QAAQ,wBAAwB+C,KAAK;IAElD,IAAI;QACF,MAAMf,WAAW,MAAM7B,6BAA6B;YAClD8B,SAAS;YACTC,MAAM;gBAACN;gBAASN;gBAAOa,MAAM;gBAAWZ,SAAS;YAAU;YAC3DiB;QACF;QAEAM,KAAKE,OAAO;QACZ,OAAOhB;IACT,EAAE,OAAOI,GAAG;QACV,yDAAyD;QACzD,IAAI;YAAC;YAAK;SAAI,CAACC,QAAQ,CAACD,GAAGE,aAAa;YACtCjC,YAAY;YACZ,OAAOwC,aAAavB,OAAOkB;QAC7B;QAEAM,KAAKG,IAAI;QAET5C,YAAY,mCAAmC+B;QAC/C,MAAMA;IACR;AACF"}
1
+ {"version":3,"sources":["../../../src/actions/deploy/createUserApplication.ts"],"sourcesContent":["import {CLIError} from '@oclif/core/errors'\nimport {type AppVisibility} from '@sanity/cli-core'\nimport {input, spinner} from '@sanity/cli-core/ux'\nimport {customAlphabet} from 'nanoid'\n\nimport {\n createUserApplication as createUserApplicationRequest,\n type UserApplication,\n type UserApplicationResolved,\n} from '../../services/userApplications.js'\nimport {NO_ORGANIZATION_ID} from '../../util/errorMessages.js'\nimport {deployDebug} from './deployDebug.js'\nimport {normalizeUrl, validateUrl} from './urlUtils.js'\n\nconst LETTERS = 'abcdefghijklmnopqrstuvwxyz'\n\n// appHosts have some restrictions (no uppercase, must start with a letter)\nexport function generateAppSlug(): string {\n const firstChar = customAlphabet(LETTERS, 1)()\n const rest = customAlphabet(`${LETTERS}0123456789`, 11)()\n return `${firstChar}${rest}`\n}\n\n// TODO: replace with `Promise.withResolvers()` once it lands in node 22\nfunction promiseWithResolvers<T>() {\n let resolve!: (t: T) => void\n let reject!: (err: unknown) => void\n const promise = new Promise<T>((res, rej) => {\n resolve = res\n reject = rej\n })\n return {promise, reject, resolve}\n}\n\ninterface CreateStudioUserApplicationOptions {\n projectId: string\n\n title?: string\n urlType?: 'external' | 'internal'\n}\n\nexport async function createStudioUserApplication(options: CreateStudioUserApplicationOptions) {\n const {projectId, title, urlType = 'internal'} = options\n const {promise, resolve} = promiseWithResolvers<UserApplication>()\n\n const isExternal = urlType === 'external'\n\n await input({\n message: isExternal ? 'Studio URL (https://...):' : 'Studio hostname (<value>.sanity.studio):',\n // if a string is returned here, it is relayed to the user and prompt allows\n // the user to try again until this function returns true\n validate: async (inp: string) => {\n let appHost: string\n\n if (isExternal) {\n const normalized = normalizeUrl(inp)\n const validation = validateUrl(normalized)\n if (validation !== true) {\n return validation\n }\n appHost = normalized\n } else {\n appHost = inp.replace(/\\.sanity\\.studio$/i, '')\n }\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'studio',\n body: {appHost, title, type: 'studio', urlType},\n projectId,\n })\n resolve(response)\n return true\n } catch (e) {\n // if the name is taken, it should return a 409 so we relay to the user\n if ([402, 409].includes(e?.statusCode)) {\n return e?.response?.body?.message || 'Bad request' // just in case\n }\n\n deployDebug('Error creating user application', e)\n // otherwise, it's a fatal error\n throw new CLIError('Error creating user application', {exit: 1})\n }\n },\n })\n\n return await promise\n}\n\nexport async function createUserApplication(\n organizationId?: string,\n title?: string,\n visibility?: AppVisibility,\n): Promise<UserApplicationResolved> {\n if (!organizationId) {\n throw new Error(NO_ORGANIZATION_ID)\n }\n\n const resolvedTitle =\n title ??\n (await input({\n message: 'Enter a title for your application:',\n validate: (value: string) => value.length > 0 || 'Title is required',\n }))\n\n return tryCreateApp(resolvedTitle, organizationId, visibility)\n}\n\nconst tryCreateApp = async (title: string, organizationId: string, visibility?: AppVisibility) => {\n // we will likely prepend this with an org ID or other parameter in the future\n const appHost = generateAppSlug()\n\n const spin = spinner('Creating application').start()\n\n try {\n const response = await createUserApplicationRequest({\n appType: 'coreApp',\n body: {\n appHost,\n title,\n type: 'coreApp',\n urlType: 'internal',\n ...(visibility ? {dashboardStatus: visibility} : {}),\n },\n organizationId,\n })\n\n spin.succeed()\n return response\n } catch (e) {\n // if the name is taken, generate a new one and try again\n if ([402, 409].includes(e?.statusCode)) {\n deployDebug('App host taken, retrying with new host')\n return tryCreateApp(title, organizationId, visibility)\n }\n\n spin.fail()\n\n deployDebug('Error creating core application', e)\n throw e\n }\n}\n"],"names":["CLIError","input","spinner","customAlphabet","createUserApplication","createUserApplicationRequest","NO_ORGANIZATION_ID","deployDebug","normalizeUrl","validateUrl","LETTERS","generateAppSlug","firstChar","rest","promiseWithResolvers","resolve","reject","promise","Promise","res","rej","createStudioUserApplication","options","projectId","title","urlType","isExternal","message","validate","inp","appHost","normalized","validation","replace","response","appType","body","type","e","includes","statusCode","exit","organizationId","visibility","Error","resolvedTitle","value","length","tryCreateApp","spin","start","dashboardStatus","succeed","fail"],"mappings":"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"}