@sanity/cli 8.6.0 → 8.7.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.
package/README.md CHANGED
@@ -126,6 +126,21 @@ Code for sanity cli
126
126
  - [`sanity users invite [EMAIL]`](#sanity-users-invite-email)
127
127
  - [`sanity users list`](#sanity-users-list)
128
128
  - [`sanity versions`](#sanity-versions)
129
+ - [`sanity workflows abort INSTANCEID`](#sanity-workflows-abort-instanceid)
130
+ - [`sanity workflows definition delete NAME`](#sanity-workflows-definition-delete-name)
131
+ - [`sanity workflows definition diff NAME`](#sanity-workflows-definition-diff-name)
132
+ - [`sanity workflows definition list`](#sanity-workflows-definition-list)
133
+ - [`sanity workflows definition show NAME`](#sanity-workflows-definition-show-name)
134
+ - [`sanity workflows deploy`](#sanity-workflows-deploy)
135
+ - [`sanity workflows diagnose INSTANCEID`](#sanity-workflows-diagnose-instanceid)
136
+ - [`sanity workflows fire-action INSTANCEID`](#sanity-workflows-fire-action-instanceid)
137
+ - [`sanity workflows list`](#sanity-workflows-list)
138
+ - [`sanity workflows nuke`](#sanity-workflows-nuke)
139
+ - [`sanity workflows reset-activity INSTANCEID ACTIVITY`](#sanity-workflows-reset-activity-instanceid-activity)
140
+ - [`sanity workflows set-stage INSTANCEID`](#sanity-workflows-set-stage-instanceid)
141
+ - [`sanity workflows show INSTANCEID`](#sanity-workflows-show-instanceid)
142
+ - [`sanity workflows start NAME`](#sanity-workflows-start-name)
143
+ - [`sanity workflows tail INSTANCEID`](#sanity-workflows-tail-instanceid)
129
144
 
130
145
  ## `sanity api ENDPOINT`
131
146
 
@@ -392,7 +407,7 @@ EXAMPLES
392
407
  USAGE
393
408
  $ sanity blueprints add TYPE [--json] [--example <value> | -n <value> | --fn-type
394
409
  document-publish|document-create|document-delete|document-update|media-library-asset-create|media-library-asset-dele
395
- te|media-library-asset-update|scheduled-function|sync-tag-invalidate... | --language ts|js | --javascript |
410
+ te|media-library-asset-update|pub-sub|scheduled-function|sync-tag-invalidate... | --language ts|js | --javascript |
396
411
  --fn-helpers | --fn-installer skip|npm|pnpm|yarn] [-i | ]
397
412
 
398
413
  ARGUMENTS
@@ -409,8 +424,8 @@ FLAGS
409
424
  --fn-type=<option>... Document change event(s) that should trigger the function; you can specify multiple
410
425
  events by specifying this flag multiple times
411
426
  <options: document-publish|document-create|document-delete|document-update|media-library-
412
- asset-create|media-library-asset-delete|media-library-asset-update|scheduled-function|syn
413
- c-tag-invalidate>
427
+ asset-create|media-library-asset-delete|media-library-asset-update|pub-sub|scheduled-func
428
+ tion|sync-tag-invalidate>
414
429
  --javascript Use JavaScript instead of TypeScript
415
430
  --json Format output as json
416
431
  --language=<option> [default: ts] Language of the new function
@@ -503,7 +518,9 @@ DESCRIPTION
503
518
 
504
519
  Use --fn-installer to force which package manager to use when deploying functions.
505
520
 
506
- Set SANITY_ASSET_TIMEOUT (seconds) to override the 60-second timeout for processing resource assets.
521
+ Set SANITY_ASSET_TIMEOUT (seconds) to override the 180-second timeout for processing resource assets.
522
+
523
+ Set SANITY_ASSET_CONCURRENCY to override how many resource assets are processed at once (default 4).
507
524
 
508
525
  Exit codes: 0 deployed, 2 deployment failed, 75 deployment accepted but completion could not be confirmed (rerun
509
526
  'blueprints info' to check).
@@ -530,16 +547,16 @@ Destroy a remote Stack deployment and its resources
530
547
 
531
548
  ```
532
549
  USAGE
533
- $ sanity blueprints destroy [--json] [--project-id <value> --stack <value> --force] [--organization-id <value> ]
550
+ $ sanity blueprints destroy [--json] [--project-id <value> --stack <value> -f] [--organization-id <value> ]
534
551
  [--no-wait]
535
552
 
536
553
  FLAGS
537
- --force Force Stack destruction (skip confirmation)
538
- --json Format output as json
539
- --no-wait Do not wait for Stack destruction to complete
540
- --organization-id=<value> Sanity organization ID used to scope Blueprint and Stack
541
- --project-id=<value> Sanity project ID used to scope Blueprint and Stack
542
- --stack=<value> Stack name or ID to destroy (defaults to the locally configured Stack)
554
+ -f, --force Force Stack destruction (skip confirmation)
555
+ --json Format output as json
556
+ --no-wait Do not wait for Stack destruction to complete
557
+ --organization-id=<value> Sanity organization ID used to scope Blueprint and Stack
558
+ --project-id=<value> Sanity project ID used to scope Blueprint and Stack
559
+ --stack=<value> Stack name or ID to destroy (defaults to the locally configured Stack)
543
560
 
544
561
  DESCRIPTION
545
562
  Destroy a remote Stack deployment and its resources
@@ -596,13 +613,14 @@ Display the status and resources of the remote Stack deployment
596
613
 
597
614
  ```
598
615
  USAGE
599
- $ sanity blueprints info [--json] [--stack <value>] [--project-id <value> | --organization-id <value>]
616
+ $ sanity blueprints info [--json] [-v] [--stack <value>] [--project-id <value> | --organization-id <value>]
600
617
 
601
618
  FLAGS
602
- --json Format output as json
603
- --organization-id=<value> Sanity organization ID used to scope Blueprint and Stack
604
- --project-id=<value> Sanity project ID used to scope Blueprint and Stack
605
- --stack=<value> Stack name or ID
619
+ -v, --verbose Show resource and external IDs
620
+ --json Format output as json
621
+ --organization-id=<value> Sanity organization ID used to scope Blueprint and Stack
622
+ --project-id=<value> Sanity project ID used to scope Blueprint and Stack
623
+ --stack=<value> Stack name or ID
606
624
 
607
625
  DESCRIPTION
608
626
  Display the status and resources of the remote Stack deployment
@@ -653,12 +671,12 @@ DESCRIPTION
653
671
 
654
672
  A Blueprint is your local infrastructure-as-code configuration that defines Sanity resources (datasets, functions,
655
673
  etc.). A Stack is the remote deployment target where your Blueprint is applied.
656
- [NOTE: Currently, accounts are limited to three (3) Stacks per project scope.]
657
674
 
658
675
  This is typically the first command you run in a new project. It creates a local Blueprint manifest file
659
676
  (sanity.blueprint.ts, .js, or .json) and provisions a new remote Stack.
660
- Additionally, a Blueprint configuration file is created in .sanity/ containing the scope and Stack IDs. This is
661
- .gitignored by default.
677
+ Additionally, a Blueprint configuration file is created in .sanity/ containing the scope and Stack IDs. A .gitignore
678
+ covering node_modules, .env, and Function build output is created or updated; the .sanity/ config itself is not
679
+ ignored.
662
680
 
663
681
  After initialization, use 'blueprints plan' to preview changes, then 'blueprints deploy' to apply them.
664
682
 
@@ -693,8 +711,8 @@ Display logs for the current Blueprint's Stack deployment
693
711
 
694
712
  ```
695
713
  USAGE
696
- $ sanity blueprints logs [--json] [--stack <value>] [--project-id <value> | --organization-id <value>] [-l <value>
697
- | -w] [--since <value> | ] [--before <value> | ]
714
+ $ sanity blueprints logs [--stack <value>] [--project-id <value> | --organization-id <value>] [-l <value> | [-w |
715
+ --json]] [--since <value> | ] [--before <value> | ]
698
716
 
699
717
  FLAGS
700
718
  -l, --limit=<value> Maximum number of log entries to retrieve (1-500)
@@ -844,9 +862,10 @@ List remote Stack deployments for your project or organization
844
862
 
845
863
  ```
846
864
  USAGE
847
- $ sanity blueprints stacks [--json] [--project-id <value> | --organization-id <value> | --include-projects]
865
+ $ sanity blueprints stacks [--json] [--project-id <value> | --organization-id <value> | --include-projects | --all]
848
866
 
849
867
  FLAGS
868
+ --all List Stacks from every organization and project you have access to
850
869
  --include-projects Include Stacks from all projects within the organization. Requires --organization-id.
851
870
  --json Format output as json
852
871
  --organization-id=<value> Sanity organization ID used to scope Blueprint and Stack
@@ -860,11 +879,14 @@ DESCRIPTION
860
879
  Use this to discover existing Stacks you can scope a local Blueprint to (using 'blueprints config --edit'), or to
861
880
  audit what's deployed across your project.
862
881
 
863
- Use --include-projects with --organization-id to also list Stacks from all projects within the organization.
882
+ Without a scope, prompts for an organization or project. Use --all to list Stacks across every organization and
883
+ project you can access, or --include-projects with --organization-id for one organization and its projects.
864
884
 
865
885
  EXAMPLES
866
886
  $ sanity blueprints stacks
867
887
 
888
+ $ sanity blueprints stacks --all
889
+
868
890
  $ sanity blueprints stacks --project-id <projectId>
869
891
 
870
892
  $ sanity blueprints stacks --organization-id <organizationId>
@@ -2027,13 +2049,13 @@ Add a Function to your Blueprint
2027
2049
 
2028
2050
  ```
2029
2051
  USAGE
2030
- $ sanity functions add [--json] [--example <value> | -n <value> | | --language ts|js | --javascript | | ]
2031
- [--type document-publish|document-create|document-delete|document-update|media-library-asset-create|media-library-as
2032
- set-delete|media-library-asset-update|scheduled-function|sync-tag-invalidate... ] [--helpers] [--installer
2033
- skip|npm|pnpm|yarn] [-i | ]
2052
+ $ sanity functions add [--json] [--example <value> | -n <value> | --type
2053
+ document-publish|document-create|document-delete|document-update|media-library-asset-create|media-library-asset-dele
2054
+ te|media-library-asset-update|pub-sub|scheduled-function|sync-tag-invalidate... | --language ts|js | --javascript |
2055
+ --helpers | --installer skip|npm|pnpm|yarn | -i]
2034
2056
 
2035
2057
  FLAGS
2036
- -i, --install Shortcut for --fn-installer npm
2058
+ -i, --install Shortcut for --installer npm
2037
2059
  -n, --name=<value> Name of the Function to add
2038
2060
  --example=<value> Example to use for the Function
2039
2061
  --[no-]helpers Add helpers to the new Function
@@ -2046,8 +2068,8 @@ FLAGS
2046
2068
  --type=<option>... Document change event(s) that should trigger the function; you can specify multiple events
2047
2069
  by specifying this flag multiple times
2048
2070
  <options: document-publish|document-create|document-delete|document-update|media-library-ass
2049
- et-create|media-library-asset-delete|media-library-asset-update|scheduled-function|sync-tag-
2050
- invalidate>
2071
+ et-create|media-library-asset-delete|media-library-asset-update|pub-sub|scheduled-function|s
2072
+ ync-tag-invalidate>
2051
2073
 
2052
2074
  DESCRIPTION
2053
2075
  Add a Function to your Blueprint
@@ -2093,13 +2115,13 @@ DESCRIPTION
2093
2115
 
2094
2116
  Open the emulator in your browser to interactively test your functions with the payload editor.
2095
2117
 
2096
- Optionally, set the host and port with the --host and --port flags. Function timeout can be configured with the
2097
- --timeout flag.
2118
+ Optionally, set the host and port with the --host and --port flags. Port 8974 is reserved for the emulator's
2119
+ live-reload WebSocket server. Function timeout can be configured with the --timeout flag.
2098
2120
 
2099
2121
  To invoke a function with the CLI, use 'functions test'.
2100
2122
 
2101
2123
  EXAMPLES
2102
- $ sanity functions dev --host 127.0.0.1 --port 8974
2124
+ $ sanity functions dev --host 127.0.0.1 --port 3333
2103
2125
 
2104
2126
  $ sanity functions dev --timeout 60
2105
2127
  ```
@@ -2199,7 +2221,7 @@ Retrieve or delete logs for a Sanity Function
2199
2221
 
2200
2222
  ```
2201
2223
  USAGE
2202
- $ sanity functions logs [NAME] [--stack <value>] [-u] [-f [-d | -l <value> | --json]] [-w]
2224
+ $ sanity functions logs [NAME] [--stack <value>] [-u] [-f [-d | -l <value> | --json]] [-w | ]
2203
2225
 
2204
2226
  ARGUMENTS
2205
2227
  [NAME] The name of the Sanity Function
@@ -2240,7 +2262,7 @@ USAGE
2240
2262
  $ sanity functions test [NAME] [--json] [--data-before <value> | [-d <value> | -f <value> | --document-id <value>]
2241
2263
  | | | --file-before <value> | --file-after <value> | --document-id-before <value> | --document-id-after <value>]
2242
2264
  [--data-after <value> | | | | | | | ] [-e create|update|delete] [-t <value>] [-a <value>] [--with-user-token]
2243
- [--media-library-id <value> | [--project-id <value> | --organization-id <value>] | --dataset <value>]
2265
+ [--media-library-id <value> | [--project-id <value> | --organization-id <value>] | --dataset <value>] [--no-wait]
2244
2266
 
2245
2267
  ARGUMENTS
2246
2268
  [NAME] The name of the Sanity Function
@@ -2262,6 +2284,7 @@ FLAGS
2262
2284
  --file-before=<value> Original document
2263
2285
  --json Format output as json
2264
2286
  --media-library-id=<value> Sanity Media Library ID to use
2287
+ --no-wait Skip durable wait delays instead of sleeping
2265
2288
  --organization-id=<value> Sanity organization ID used to scope Blueprint and Stack
2266
2289
  --project-id=<value> Sanity project ID used to scope Blueprint and Stack
2267
2290
  --with-user-token Prime access token from CLI config
@@ -2793,14 +2816,14 @@ Delete an aspect definition
2793
2816
 
2794
2817
  ```
2795
2818
  USAGE
2796
- $ sanity media delete-aspect ASPECTNAME [-p <id>] [--media-library-id <value>] [--yes]
2819
+ $ sanity media delete-aspect ASPECTNAME [-p <id>] [--media-library-id <value>] [-y]
2797
2820
 
2798
2821
  ARGUMENTS
2799
2822
  ASPECTNAME Name of the aspect to delete
2800
2823
 
2801
2824
  FLAGS
2802
- --media-library-id=<value> The id of the target media library
2803
- --yes Run without prompts and confirm deletion
2825
+ -y, --yes Run without prompts and confirm deletion
2826
+ --media-library-id=<value> The id of the target media library
2804
2827
 
2805
2828
  OVERRIDE FLAGS
2806
2829
  -p, --project-id=<id> Project ID to delete media aspect from (overrides CLI configuration)
@@ -3686,13 +3709,13 @@ Delete an API token from the project
3686
3709
 
3687
3710
  ```
3688
3711
  USAGE
3689
- $ sanity tokens delete [TOKENID] [-p <id>] [--yes]
3712
+ $ sanity tokens delete [TOKENID] [-p <id>] [-y]
3690
3713
 
3691
3714
  ARGUMENTS
3692
3715
  [TOKENID] Token ID to delete (will prompt if not provided)
3693
3716
 
3694
3717
  FLAGS
3695
- --yes Skip confirmation prompt (unattended mode)
3718
+ -y, --yes Skip confirmation prompt (unattended mode)
3696
3719
 
3697
3720
  OVERRIDE FLAGS
3698
3721
  -p, --project-id=<id> Project ID to delete token from (overrides CLI configuration)
@@ -3945,4 +3968,463 @@ EXAMPLES
3945
3968
  $ sanity versions
3946
3969
  ```
3947
3970
 
3971
+ ## `sanity workflows abort INSTANCEID`
3972
+
3973
+ Abort an in-flight workflow instance — a hard stop: pending effects are cancelled, stage guards removed, and the instance is marked terminal where it stands.
3974
+
3975
+ ```
3976
+ USAGE
3977
+ $ sanity workflows abort INSTANCEID [--deployment <value> | --tag <value>] [--reason <value>]
3978
+
3979
+ ARGUMENTS
3980
+ INSTANCEID Workflow instance id.
3981
+
3982
+ FLAGS
3983
+ --deployment=<value> Deployment name — narrow the instance search to the resource that deployment targets; the tag
3984
+ partition still comes from the loaded instance.
3985
+ --reason=<value> Reason for aborting (recorded in history).
3986
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
3987
+ resources are searched; omit to span them all.
3988
+
3989
+ DESCRIPTION
3990
+ Abort an in-flight workflow instance — a hard stop: pending effects are cancelled, stage guards removed, and the
3991
+ instance is marked terminal where it stands.
3992
+
3993
+ EXAMPLES
3994
+ $ sanity workflows abort wf-instance.abc123
3995
+
3996
+ $ sanity workflows abort wf-instance.abc123 --reason 'superseded by relaunch'
3997
+ ```
3998
+
3999
+ ## `sanity workflows definition delete NAME`
4000
+
4001
+ Delete a deployed workflow definition (every version, or one via --version). Refuses while non-terminal instances exist unless --cascade aborts them first — instances are aborted in place, never deleted.
4002
+
4003
+ ```
4004
+ USAGE
4005
+ $ sanity workflows definition delete NAME [--deployment <value> | --tag <value>] [--version <value>] [--cascade] [--reason
4006
+ <value>]
4007
+
4008
+ ARGUMENTS
4009
+ NAME Workflow definition name.
4010
+
4011
+ FLAGS
4012
+ --cascade Abort every non-terminal instance pinned to the targeted versions, then delete.
4013
+ --deployment=<value> Deployment name — the unique identity of one deployment in the config.
4014
+ --reason=<value> Free-text reason — recorded on each cascade-abort history entry.
4015
+ --tag=<value> Workflow environment tag (e.g. prod, test) — selects the deployment to act on while the tag
4016
+ names exactly one; pass --deployment when it spans several.
4017
+ --version=<value> Delete only this deployed version (default: every version).
4018
+
4019
+ DESCRIPTION
4020
+ Delete a deployed workflow definition (every version, or one via --version). Refuses while non-terminal instances
4021
+ exist unless --cascade aborts them first — instances are aborted in place, never deleted.
4022
+
4023
+ EXAMPLES
4024
+ $ sanity workflows definition delete my-workflow
4025
+
4026
+ $ sanity workflows definition delete my-workflow --version 2
4027
+
4028
+ $ sanity workflows definition delete my-workflow --cascade --reason 'workflow retired'
4029
+ ```
4030
+
4031
+ ## `sanity workflows definition diff NAME`
4032
+
4033
+ Diff an in-code definition against the deployed version (latest by default).
4034
+
4035
+ ```
4036
+ USAGE
4037
+ $ sanity workflows definition diff NAME [--deployment <value> | --tag <value>] [--version <value>]
4038
+
4039
+ ARGUMENTS
4040
+ NAME Workflow definition name.
4041
+
4042
+ FLAGS
4043
+ --deployment=<value> Deployment name — the unique identity of one deployment in the config.
4044
+ --tag=<value> Workflow environment tag (e.g. prod, test) — selects the deployment to act on while the tag
4045
+ names exactly one; pass --deployment when it spans several.
4046
+ --version=<value> Deployed version to diff against (default: latest).
4047
+
4048
+ DESCRIPTION
4049
+ Diff an in-code definition against the deployed version (latest by default).
4050
+
4051
+ EXAMPLES
4052
+ $ sanity workflows definition diff productLaunch
4053
+
4054
+ $ sanity workflows definition diff productLaunch --version 2
4055
+ ```
4056
+
4057
+ ## `sanity workflows definition list`
4058
+
4059
+ List deployed workflow definitions.
4060
+
4061
+ ```
4062
+ USAGE
4063
+ $ sanity workflows definition list [--tag <value>] [--limit <value>] [--name <value>] [--json]
4064
+
4065
+ FLAGS
4066
+ --json Emit structured JSON instead of rendered output.
4067
+ --limit=<value> [default: 100] The maximum number of definitions to return.
4068
+ --name=<value> Filter to a single workflow definition name (e.g. product-launch).
4069
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
4070
+ resources are searched; omit to span them all.
4071
+
4072
+ DESCRIPTION
4073
+ List deployed workflow definitions.
4074
+
4075
+ EXAMPLES
4076
+ $ sanity workflows definition list
4077
+
4078
+ $ sanity workflows definition list --tag prod
4079
+
4080
+ $ sanity workflows definition list --json
4081
+ ```
4082
+
4083
+ ## `sanity workflows definition show NAME`
4084
+
4085
+ Show a deployed workflow definition.
4086
+
4087
+ ```
4088
+ USAGE
4089
+ $ sanity workflows definition show NAME [--tag <value>] [--version <value>] [--json]
4090
+
4091
+ ARGUMENTS
4092
+ NAME Workflow definition name.
4093
+
4094
+ FLAGS
4095
+ --json Emit structured JSON instead of rendered output.
4096
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
4097
+ resources are searched; omit to span them all.
4098
+ --version=<value> Specific version (default: latest).
4099
+
4100
+ DESCRIPTION
4101
+ Show a deployed workflow definition.
4102
+ ```
4103
+
4104
+ ## `sanity workflows deploy`
4105
+
4106
+ Validate, diff, and deploy workflow definitions to the resource bound by the selected deployment.
4107
+
4108
+ ```
4109
+ USAGE
4110
+ $ sanity workflows deploy [--all-tags | | [--deployment <value> | --tag <value>]] [--dry-run] [--check] [--only
4111
+ <value>] [--share-defs]
4112
+
4113
+ FLAGS
4114
+ --all-tags Deploy every deployment in the config, not just a selection.
4115
+ --check Validate definitions only; do not contact the dataset.
4116
+ --deployment=<value> Deployment name — the unique identity of one deployment in the config.
4117
+ --dry-run Validate + diff against the deployed version; do not write.
4118
+ --only=<value> Limit deploy/check/diff to a single workflow definition by name (deployments are selected with
4119
+ --deployment). Every targeted deployment must contain it.
4120
+ --[no-]share-defs Share the definition documents newly created by this deploy with Sanity — the full document,
4121
+ verbatim (structure, names, filters, effect configuration, seeded values), plus its deployment
4122
+ coordinates (project and dataset, or resource id); never content documents, instances, or your
4123
+ Sanity auth token. Sharing is the default in every environment, including CI / non-TTY /
4124
+ DO_NOT_TRACK. Use --no-share-defs to opt out.
4125
+ --tag=<value> Workflow environment tag (e.g. prod, test) — deploys every deployment carrying the tag (a tag is
4126
+ an environment group).
4127
+
4128
+ DESCRIPTION
4129
+ Validate, diff, and deploy workflow definitions to the resource bound by the selected deployment.
4130
+
4131
+ EXAMPLES
4132
+ $ sanity workflows deploy --deployment review-prod
4133
+
4134
+ $ sanity workflows deploy --tag prod
4135
+
4136
+ $ sanity workflows deploy --all-tags
4137
+
4138
+ $ sanity workflows deploy --check
4139
+
4140
+ $ sanity workflows deploy --dry-run
4141
+
4142
+ $ sanity workflows deploy --only productLaunch
4143
+ ```
4144
+
4145
+ ## `sanity workflows diagnose INSTANCEID`
4146
+
4147
+ Explain why a workflow instance is or isn't progressing, and what would unstick it.
4148
+
4149
+ ```
4150
+ USAGE
4151
+ $ sanity workflows diagnose INSTANCEID [--deployment <value> | --tag <value>] [--json]
4152
+
4153
+ ARGUMENTS
4154
+ INSTANCEID Workflow instance id.
4155
+
4156
+ FLAGS
4157
+ --deployment=<value> Deployment name — narrow the instance search to the resource that deployment targets; the tag
4158
+ partition still comes from the loaded instance.
4159
+ --json Emit structured JSON instead of rendered output.
4160
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
4161
+ resources are searched; omit to span them all.
4162
+
4163
+ DESCRIPTION
4164
+ Explain why a workflow instance is or isn't progressing, and what would unstick it.
4165
+
4166
+ EXAMPLES
4167
+ $ sanity workflows diagnose wf-instance.abc123
4168
+
4169
+ $ sanity workflows diagnose wf-instance.abc123 --tag prod
4170
+
4171
+ $ sanity workflows diagnose wf-instance.abc123 --json
4172
+ ```
4173
+
4174
+ ## `sanity workflows fire-action INSTANCEID`
4175
+
4176
+ Fire an action on an instance to unstick a waiting activity — the write acts as the configured token. Omit --action to list what can be fired.
4177
+
4178
+ ```
4179
+ USAGE
4180
+ $ sanity workflows fire-action INSTANCEID [--deployment <value> | --tag <value>] [--activity <value>] [--action <value>]
4181
+ [--param <value>...] [--json]
4182
+
4183
+ ARGUMENTS
4184
+ INSTANCEID Workflow instance id.
4185
+
4186
+ FLAGS
4187
+ --action=<value> Action to fire. Omit to list the actions available on the instance.
4188
+ --activity=<value> Activity the action belongs to. Required to fire; omit --action to list.
4189
+ --deployment=<value> Deployment name — narrow the instance search to the resource that deployment targets; the tag
4190
+ partition still comes from the loaded instance.
4191
+ --json Emit structured JSON instead of rendered output.
4192
+ --param=<value>... [default: ] Action param as key=value (repeatable). Values are JSON-parsed, falling back to a
4193
+ string.
4194
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
4195
+ resources are searched; omit to span them all.
4196
+
4197
+ DESCRIPTION
4198
+ Fire an action on an instance to unstick a waiting activity — the write acts as the configured token. Omit --action to
4199
+ list what can be fired.
4200
+
4201
+ EXAMPLES
4202
+ $ sanity workflows fire-action wf-instance.abc123
4203
+
4204
+ $ sanity workflows fire-action wf-instance.abc123 --activity approve --action approve
4205
+
4206
+ $ sanity workflows fire-action wf-instance.abc123 --activity publish --action publish --param note=shipping
4207
+ ```
4208
+
4209
+ ## `sanity workflows list`
4210
+
4211
+ List workflow instances in the configured dataset (in-flight by default).
4212
+
4213
+ ```
4214
+ USAGE
4215
+ $ sanity workflows list [--tag <value>] [--include-completed] [--failed] [--definition <value>] [--document
4216
+ <value>] [--limit <value>] [--json]
4217
+
4218
+ FLAGS
4219
+ --definition=<value> Only instances of this workflow definition (its `name`; the instance's `definition` field).
4220
+ --document=<value> Only instances that reference this document (resource-qualified GDR URI, e.g.
4221
+ "dataset:proj:ds:article-1").
4222
+ --failed Only instances with at least one failed activity.
4223
+ --include-completed Include completed/aborted instances (default: in-flight only).
4224
+ --json Emit structured JSON instead of rendered output.
4225
+ --limit=<value> [default: 50] Maximum rows to return.
4226
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
4227
+ resources are searched; omit to span them all.
4228
+
4229
+ DESCRIPTION
4230
+ List workflow instances in the configured dataset (in-flight by default).
4231
+
4232
+ EXAMPLES
4233
+ $ sanity workflows list
4234
+
4235
+ $ sanity workflows list --include-completed
4236
+
4237
+ $ sanity workflows list --definition productLaunch
4238
+
4239
+ $ sanity workflows list --document dataset:proj:ds:article-1
4240
+
4241
+ $ sanity workflows list --tag prod
4242
+
4243
+ $ sanity workflows list --json
4244
+ ```
4245
+
4246
+ ## `sanity workflows nuke`
4247
+
4248
+ Delete engine-owned documents — a whole deployment tag, or a single instance.
4249
+
4250
+ ```
4251
+ USAGE
4252
+ $ sanity workflows nuke [--deployment <value>] [--tag <value>] [--instance <value>] [--force]
4253
+
4254
+ FLAGS
4255
+ --deployment=<value> The deployment name to reset. With --instance, narrows which deployment the instance is looked
4256
+ up in when its tag spans several.
4257
+ --force Skip the confirmation prompt (for scripts/CI). The plan still prints.
4258
+ --instance=<value> Delete a single terminal instance by id, plus its guard docs, instead of a tag.
4259
+ --tag=<value> The deployment tag to reset (while it names exactly one deployment). Not valid with --instance,
4260
+ which reads its tag from the instance id.
4261
+
4262
+ DESCRIPTION
4263
+ Delete engine-owned documents — a whole deployment tag, or a single instance.
4264
+
4265
+ The reset for a dataset holding engine documents the versioned upgrade framework cannot yet migrate: deletes the tag's
4266
+ instances, definitions, and guards (across every alias-bound resource). --instance <id> instead deletes one terminal
4267
+ instance plus its guards. Content documents are never touched. Prints a dry-run plan, then confirms (--force skips the
4268
+ prompt; the plan still prints).
4269
+
4270
+ EXAMPLES
4271
+ $ sanity workflows nuke --deployment plugin-dev
4272
+
4273
+ $ sanity workflows nuke --tag plugin-dev --force
4274
+
4275
+ $ sanity workflows nuke --instance plugin-dev.wf-instance.abc123
4276
+ ```
4277
+
4278
+ ## `sanity workflows reset-activity INSTANCEID ACTIVITY`
4279
+
4280
+ Reset a failed activity on an in-flight instance — back to active to re-run it, or --skip to bypass it (mark it skipped) so a gated exit transition can fire.
4281
+
4282
+ ```
4283
+ USAGE
4284
+ $ sanity workflows reset-activity INSTANCEID ACTIVITY [--deployment <value> | --tag <value>] [--skip]
4285
+
4286
+ ARGUMENTS
4287
+ INSTANCEID Workflow instance id.
4288
+ ACTIVITY Activity name within the current stage.
4289
+
4290
+ FLAGS
4291
+ --deployment=<value> Deployment name — narrow the instance search to the resource that deployment targets; the tag
4292
+ partition still comes from the loaded instance.
4293
+ --skip Bypass the activity (mark it skipped) instead of re-running it (back to active).
4294
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
4295
+ resources are searched; omit to span them all.
4296
+
4297
+ DESCRIPTION
4298
+ Reset a failed activity on an in-flight instance — back to active to re-run it, or --skip to bypass it (mark it
4299
+ skipped) so a gated exit transition can fire.
4300
+
4301
+ EXAMPLES
4302
+ $ sanity workflows reset-activity wf-instance.abc123 legal-review
4303
+
4304
+ $ sanity workflows reset-activity wf-instance.abc123 legal-review --skip
4305
+ ```
4306
+
4307
+ ## `sanity workflows set-stage INSTANCEID`
4308
+
4309
+ Force an instance into a stage, regardless of its declared transitions and filters — the engine's setStage admin override. The target stage's enter lifecycle still runs (auto-activities start, stage guards reconcile), and the post-move cascade can immediately auto-transition the instance onward.
4310
+
4311
+ ```
4312
+ USAGE
4313
+ $ sanity workflows set-stage INSTANCEID [--deployment <value> | --tag <value>] [--to <value>] [--reason <value>]
4314
+
4315
+ ARGUMENTS
4316
+ INSTANCEID Workflow instance id to move.
4317
+
4318
+ FLAGS
4319
+ --deployment=<value> Deployment name — narrow the instance search to the resource that deployment targets; the tag
4320
+ partition still comes from the loaded instance.
4321
+ --reason=<value> Free-text reason — recorded on the history entry for audit.
4322
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
4323
+ resources are searched; omit to span them all.
4324
+ --to=<value> Target stage name. Omit on an interactive terminal to pick from the workflow’s stages.
4325
+
4326
+ DESCRIPTION
4327
+ Force an instance into a stage, regardless of its declared transitions and filters — the engine's setStage admin
4328
+ override. The target stage's enter lifecycle still runs (auto-activities start, stage guards reconcile), and the
4329
+ post-move cascade can immediately auto-transition the instance onward.
4330
+
4331
+ EXAMPLES
4332
+ $ sanity workflows set-stage wf-instance.abc123 --to ready
4333
+
4334
+ $ sanity workflows set-stage wf-instance.abc123
4335
+
4336
+ $ sanity workflows set-stage wf-instance.abc123 --to ready --reason 'unblock for demo'
4337
+ ```
4338
+
4339
+ ## `sanity workflows show INSTANCEID`
4340
+
4341
+ Show the state, activities, and effects of a workflow instance.
4342
+
4343
+ ```
4344
+ USAGE
4345
+ $ sanity workflows show INSTANCEID [--tag <value>] [--include history...] [--json]
4346
+
4347
+ ARGUMENTS
4348
+ INSTANCEID Workflow instance document id.
4349
+
4350
+ FLAGS
4351
+ --include=<option>... [default: ] Optional sections to include in rendered output (--json always carries the full
4352
+ document).
4353
+ <options: history>
4354
+ --json Emit structured JSON instead of rendered output.
4355
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which
4356
+ resources are searched; omit to span them all.
4357
+
4358
+ DESCRIPTION
4359
+ Show the state, activities, and effects of a workflow instance.
4360
+
4361
+ EXAMPLES
4362
+ $ sanity workflows show wf-instance.abc123
4363
+
4364
+ $ sanity workflows show wf-instance.abc123 --include history
4365
+
4366
+ $ sanity workflows show wf-instance.abc123 --json
4367
+ ```
4368
+
4369
+ ## `sanity workflows start NAME`
4370
+
4371
+ Start a workflow instance from a deployed definition. Supply values for the workflow's input-sourced fields with --field (e.g. the subject document ref).
4372
+
4373
+ ```
4374
+ USAGE
4375
+ $ sanity workflows start NAME [--deployment <value> | --tag <value>] [--version <value>] [--field <value>...]
4376
+ [--instance-id <value>] [--json]
4377
+
4378
+ ARGUMENTS
4379
+ NAME Workflow definition name.
4380
+
4381
+ FLAGS
4382
+ --deployment=<value> Deployment name — the unique identity of one deployment in the config.
4383
+ --field=<value>... [default: ] Initial value for a declared input-sourced field, as name=value (repeatable).
4384
+ Values are JSON-parsed, falling back to a string; ref kinds take a JSON object with a GDR `id`
4385
+ and doc `type`.
4386
+ --instance-id=<value> Start under this instance id — for retries. The id is the start's idempotency key: pass the id
4387
+ of a start that failed partway and the engine resumes it instead of creating a duplicate (an
4388
+ already-settled start replays as a no-op). Omit to mint a fresh id.
4389
+ --json Emit structured JSON instead of rendered output.
4390
+ --tag=<value> Workflow environment tag (e.g. prod, test) — selects the deployment to act on while the tag
4391
+ names exactly one; pass --deployment when it spans several.
4392
+ --version=<value> Definition version to start from (default: highest deployed).
4393
+
4394
+ DESCRIPTION
4395
+ Start a workflow instance from a deployed definition. Supply values for the workflow's input-sourced fields with
4396
+ --field (e.g. the subject document ref).
4397
+
4398
+ EXAMPLES
4399
+ $ sanity workflows start productLaunch
4400
+
4401
+ $ sanity workflows start article-review --field subject='{"id":"dataset:proj:ds:article-1","type":"article"}'
4402
+
4403
+ $ sanity workflows start productLaunch --version 2 --tag prod
4404
+
4405
+ $ sanity workflows start productLaunch --instance-id prod.wf-instance.a1b2c3d4e5f6
4406
+ ```
4407
+
4408
+ ## `sanity workflows tail INSTANCEID`
4409
+
4410
+ Stream new history entries on a workflow instance as they land in the dataset.
4411
+
4412
+ ```
4413
+ USAGE
4414
+ $ sanity workflows tail INSTANCEID [--tag <value>]
4415
+
4416
+ ARGUMENTS
4417
+ INSTANCEID Workflow instance id to tail.
4418
+
4419
+ FLAGS
4420
+ --tag=<value> Workflow environment tag (e.g. prod, test) — an optional query filter that also narrows which resources
4421
+ are searched; omit to span them all.
4422
+
4423
+ DESCRIPTION
4424
+ Stream new history entries on a workflow instance as they land in the dataset.
4425
+
4426
+ EXAMPLES
4427
+ $ sanity workflows tail wf-instance.abc123
4428
+ ```
4429
+
3948
4430
  <!-- commandsstop -->
@@ -1,11 +1,11 @@
1
1
  import { Args, Flags } from '@oclif/core';
2
2
  import { exitCodes, SanityCommand, subdebug } from '@sanity/cli-core';
3
+ import { formatDateTime, parseDateOnly } from '@sanity/cli-core/dates';
3
4
  import { select } from '@sanity/cli-core/ux';
4
5
  import { assertDatasetExists } from '../../actions/backup/assertDatasetExist.js';
5
6
  import { promptForProject } from '../../prompts/promptForProject.js';
6
7
  import { listBackups } from '../../services/backup.js';
7
8
  import { listDatasets } from '../../services/datasets.js';
8
- import { formatDateTime, parseDateOnly } from '../../util/dates.js';
9
9
  import { Table } from '../../util/responsiveTable.js';
10
10
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
11
11
  const listBackupDebug = subdebug('backup:list');
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {formatDateTime, parseDateOnly} from '../../util/dates.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:list']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && parsedAfter.getTime() > parsedBefore.getTime()) {\n this.error('--after date must be before --before', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: formatDateTime(createdAt),\n resource: 'Dataset',\n })\n }\n\n this.log(table.render())\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parseDateOnly(date)\n if (parsedDate) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","select","assertDatasetExists","promptForProject","listBackups","listDatasets","formatDateTime","parseDateOnly","Table","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","getTime","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","resource","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/backups/list.ts"],"sourcesContent":["import {Args, Flags} from '@oclif/core'\nimport {exitCodes, SanityCommand, subdebug} from '@sanity/cli-core'\nimport {formatDateTime, parseDateOnly} from '@sanity/cli-core/dates'\nimport {select} from '@sanity/cli-core/ux'\nimport {type DatasetsResponse} from '@sanity/client'\n\nimport {assertDatasetExists} from '../../actions/backup/assertDatasetExist.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {listBackups} from '../../services/backup.js'\nimport {listDatasets} from '../../services/datasets.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst listBackupDebug = subdebug('backup:list')\n\nconst DEFAULT_LIST_BACKUP_LIMIT = 30\n\ntype ListBackupRequestQueryParams = {\n after?: string\n before?: string\n limit: string\n}\n\nexport class ListBackupCommand extends SanityCommand<typeof ListBackupCommand> {\n static override args = {\n dataset: Args.string({\n description: 'Dataset name to list backups for',\n required: false,\n }),\n }\n\n static override description = 'List available backups for a dataset'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'List backups for a dataset interactively',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production',\n description: 'List backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --limit 50',\n description: 'List up to 50 backups for the production dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> production --after 2024-01-31 --limit 10',\n description: 'List up to 10 backups created after 2024-01-31',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to list backups for',\n semantics: 'override',\n }),\n after: Flags.string({\n description: 'Only return backups after this date (inclusive, YYYY-MM-DD format)',\n }),\n before: Flags.string({\n description: 'Only return backups before this date (exclusive, YYYY-MM-DD format)',\n }),\n limit: Flags.integer({\n char: 'l',\n default: DEFAULT_LIST_BACKUP_LIMIT,\n description: 'Maximum number of backups returned',\n }),\n }\n\n static override hiddenAliases: string[] = ['backup:list']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(ListBackupCommand)\n let {dataset} = args\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [{grant: 'read', permission: 'sanity.project.datasets'}],\n }),\n })\n\n let datasets: DatasetsResponse\n\n try {\n datasets = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list datasets: ${message}`, error)\n this.error(`Failed to list datasets: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (datasets.length === 0) {\n this.error('No datasets found in this project.', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n if (dataset) {\n assertDatasetExists(datasets, dataset, this.output)\n } else if (this.isUnattended()) {\n this.error('Dataset is required in unattended mode. Pass it as the `<dataset>` argument.', {\n exit: exitCodes.USAGE_ERROR,\n })\n } else {\n dataset = await this.promptForDataset(datasets)\n }\n\n // Validate date flags\n if (flags.before || flags.after) {\n try {\n const parsedBefore = this.processDateFlag(flags.before, 'before')\n const parsedAfter = this.processDateFlag(flags.after, 'after')\n\n if (parsedAfter && parsedBefore && parsedAfter.getTime() > parsedBefore.getTime()) {\n this.error('--after date must be before --before', {exit: exitCodes.RUNTIME_ERROR})\n }\n } catch (err) {\n this.error(`Parsing date flags: ${err instanceof Error ? err.message : err}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n // Validate limit flag\n if (flags.limit < 1 || flags.limit > Number.MAX_SAFE_INTEGER) {\n this.error(`Parsing --limit: must be an integer between 1 and ${Number.MAX_SAFE_INTEGER}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const query: ListBackupRequestQueryParams = {\n limit: flags.limit.toString(),\n }\n\n if (flags.after) {\n query.after = flags.after\n }\n\n if (flags.before) {\n query.before = flags.before\n }\n\n try {\n const response = await listBackups({\n after: flags.after,\n before: flags.before,\n datasetName: dataset,\n limit: flags.limit,\n projectId,\n })\n\n if (response.backups.length === 0) {\n this.log('No backups found.')\n return\n }\n\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'resource', title: 'RESOURCE'},\n {alignment: 'left', name: 'createdAt', title: 'CREATED AT'},\n {alignment: 'left', name: 'backupId', title: 'BACKUP ID'},\n ],\n })\n\n for (const backup of response.backups) {\n const {createdAt, id} = backup\n table.addRow({\n backupId: id,\n createdAt: formatDateTime(createdAt),\n resource: 'Dataset',\n })\n }\n\n this.log(table.render())\n\n listBackupDebug(\n `Successfully listed ${response.backups.length} backups for dataset ${dataset}`,\n )\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n listBackupDebug(`Failed to list backups for dataset ${dataset}:`, error)\n this.error(`List dataset backup failed: ${message}`, {exit: exitCodes.RUNTIME_ERROR})\n }\n }\n\n private processDateFlag(date: string | undefined, flagName: string): Date | undefined {\n if (!date) return undefined\n const parsedDate = parseDateOnly(date)\n if (parsedDate) {\n return parsedDate\n }\n\n throw new Error(`Invalid date format for '--${flagName}' flag. Use YYYY-MM-DD`)\n }\n\n private async promptForDataset(datasets: DatasetsResponse): Promise<string> {\n try {\n const choices = datasets.map((dataset) => ({\n name: dataset.name,\n value: dataset.name,\n }))\n\n return select({\n choices,\n message: 'Select the dataset name:',\n })\n } catch (error) {\n listBackupDebug(`Error selecting dataset`, error)\n this.error(`Failed to select dataset:\\n${error instanceof Error ? error.message : error}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n}\n"],"names":["Args","Flags","exitCodes","SanityCommand","subdebug","formatDateTime","parseDateOnly","select","assertDatasetExists","promptForProject","listBackups","listDatasets","Table","getProjectIdFlag","listBackupDebug","DEFAULT_LIST_BACKUP_LIMIT","ListBackupCommand","args","dataset","string","description","required","examples","command","flags","semantics","after","before","limit","integer","char","default","hiddenAliases","run","parse","projectId","getProjectId","fallback","requiredPermissions","grant","permission","datasets","error","message","Error","String","exit","RUNTIME_ERROR","length","output","isUnattended","USAGE_ERROR","promptForDataset","parsedBefore","processDateFlag","parsedAfter","getTime","err","Number","MAX_SAFE_INTEGER","query","toString","response","datasetName","backups","log","table","columns","alignment","name","title","backup","createdAt","id","addRow","backupId","resource","render","date","flagName","undefined","parsedDate","choices","map","value"],"mappings":"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"}
@@ -2,6 +2,7 @@ import { styleText } from 'node:util';
2
2
  import { Args, Flags } from '@oclif/core';
3
3
  import { exit } from '@oclif/core/errors';
4
4
  import { exitCodes } from '@sanity/cli-core';
5
+ import { formatDuration, formatTimeAgo } from '@sanity/cli-core/dates';
5
6
  import { subdebug } from '@sanity/cli-core/debug';
6
7
  import { getCliExecutionContext } from '@sanity/cli-core/executionContext';
7
8
  import { SanityCommand } from '@sanity/cli-core/SanityCommand';
@@ -11,7 +12,6 @@ import { promptForDataset } from '../../prompts/promptForDataset.js';
11
12
  import { promptForDatasetName } from '../../prompts/promptForDatasetName.js';
12
13
  import { promptForProject } from '../../prompts/promptForProject.js';
13
14
  import { copyDataset, followCopyJobProgress, listDatasetCopyJobs, listDatasets } from '../../services/datasets.js';
14
- import { formatDuration, formatTimeAgo } from '../../util/dates.js';
15
15
  import { formatCliErrorMessages } from '../../util/formatCliErrorMessages.js';
16
16
  import { Table } from '../../util/responsiveTable.js';
17
17
  import { getProjectIdFlag } from '../../util/sharedFlags.js';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/commands/datasets/copy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exit} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {\n copyDataset,\n type CopyJobProgressEvent,\n type DatasetCopyJob,\n followCopyJobProgress,\n listDatasetCopyJobs,\n listDatasets,\n} from '../../services/datasets.js'\nimport {formatDuration, formatTimeAgo} from '../../util/dates.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst copyDatasetDebug = subdebug('dataset:copy')\n\nexport class CopyDatasetCommand extends SanityCommand<typeof CopyDatasetCommand> {\n static override args = {\n source: Args.string({\n description: 'Name of the dataset to copy from',\n required: false,\n }),\n target: Args.string({\n description: 'Name of the dataset to copy to',\n required: false,\n }),\n }\n\n static override description = 'Copy a dataset or manage copy jobs'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively copy a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset',\n description: 'Copy from source-dataset (prompts for target)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset target-dataset',\n description: 'Copy from source-dataset to target-dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-history source target',\n description: 'Copy without preserving document history (faster for large datasets)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-content-releases source target',\n description: 'Copy without content release documents',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --detach source target',\n description: 'Start copy job without waiting for completion',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --attach <job-id>',\n description: 'Attach to a running copy job to follow progress',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list',\n description: 'List all dataset copy jobs',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list --offset 2 --limit 10',\n description: 'List copy jobs with pagination',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to copy dataset in',\n semantics: 'override',\n }),\n attach: Flags.string({\n description: 'Attach to the running copy process to show progress',\n exclusive: ['list', 'detach', 'skip-history'],\n required: false,\n }),\n detach: Flags.boolean({\n description: 'Start the copy without waiting for it to finish',\n exclusive: ['list', 'attach'],\n required: false,\n }),\n limit: Flags.integer({\n dependsOn: ['list'],\n description: 'Maximum number of jobs returned (default 10, max 1000)',\n max: 1000,\n required: false,\n }),\n list: Flags.boolean({\n description: 'Lists all dataset copy jobs',\n exclusive: ['attach', 'detach', 'skip-history'],\n required: false,\n }),\n offset: Flags.integer({\n dependsOn: ['list'],\n description: 'Start position in the list of jobs (default 0)',\n required: false,\n }),\n 'skip-content-releases': Flags.boolean({\n description: \"Don't copy content release documents to the target dataset\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n 'skip-history': Flags.boolean({\n description: \"Don't preserve document history on copy\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['dataset:copy']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CopyDatasetCommand)\n\n if (!flags.list && !flags.attach && this.isUnattended()) {\n const errors: string[] = []\n\n if (!args.source) {\n errors.push('Source dataset is required. Pass it as the `<source>` argument.')\n }\n if (!args.target) {\n errors.push('Target dataset is required. Pass it as the `<target>` argument.')\n }\n\n if (errors.length > 0) {\n this.output.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'create', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n // Route to appropriate mode\n if (flags.list) {\n return this.handleListMode(projectId, flags)\n }\n\n if (flags.attach) {\n return this.handleAttachMode(projectId, flags.attach)\n }\n\n return this.handleCopyMode(projectId, args, flags)\n }\n\n private displayCopyJobsTable(jobs: DatasetCopyJob[]): void {\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'Job ID'},\n {alignment: 'left', name: 'sourceDataset', title: 'Source Dataset'},\n {alignment: 'left', name: 'targetDataset', title: 'Target Dataset'},\n {alignment: 'left', name: 'state', title: 'State'},\n {alignment: 'left', name: 'withHistory', title: 'With history'},\n {alignment: 'left', name: 'timeStarted', title: 'Time started'},\n {alignment: 'left', name: 'timeTaken', title: 'Time taken'},\n ],\n title: 'Dataset copy jobs for this project in descending order',\n })\n\n for (const job of jobs) {\n const {createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory} = job\n\n let timeStarted = ''\n if (createdAt !== '') {\n timeStarted = formatTimeAgo(new Date(createdAt))\n }\n\n let timeTaken = ''\n if (createdAt !== '' && updatedAt !== '') {\n timeTaken = formatDuration(new Date(updatedAt).getTime() - new Date(createdAt).getTime())\n }\n\n let color: '' | 'green' | 'red' | 'yellow'\n switch (state) {\n case 'completed': {\n color = 'green'\n break\n }\n case 'failed': {\n color = 'red'\n break\n }\n case 'pending': {\n color = 'yellow'\n break\n }\n default: {\n color = ''\n }\n }\n\n table.addRow(\n {\n id,\n sourceDataset,\n state,\n targetDataset,\n timeStarted,\n timeTaken,\n withHistory,\n },\n {color},\n )\n }\n\n this.output.log(table.render())\n }\n\n private async handleAttachMode(projectId: string, jobId: string): Promise<void> {\n copyDatasetDebug('Attaching to copy job %s', jobId)\n\n if (jobId.trim() === '') {\n return this.output.error('Please supply a valid jobId', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n try {\n await this.subscribeToProgress(projectId, jobId)\n this.output.log(`Job ${styleText('green', jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to attach to copy job: %s', message, error)\n return this.output.error(`Failed to attach to copy job: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleCopyMode(\n projectId: string,\n args: {source?: string; target?: string},\n flags: {detach?: boolean; 'skip-content-releases'?: boolean; 'skip-history'?: boolean},\n ): Promise<void> {\n copyDatasetDebug('Starting copy mode')\n\n const skipHistory = Boolean(flags['skip-history'])\n const skipContentReleases = Boolean(flags['skip-content-releases'])\n\n // Surfaced before any prompting so the flag is still actionable: a copy job\n // can't be canceled once started, so mentioning it after the job kicks off\n // leaves nothing to decide.\n if (!skipHistory) {\n this.output.log(\n `Note: You can run this command with flag '--skip-history'. The flag will reduce copy time in larger datasets.`,\n )\n }\n\n // Get and validate source dataset\n let sourceDataset = args.source\n if (sourceDataset) {\n const nameError = validateDatasetName(sourceDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n let datasetsResponse\n try {\n datasetsResponse = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to fetch datasets: %s', message, error)\n return this.output.error(`Failed to fetch datasets: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const datasetNames = new Set(datasetsResponse.map((ds) => ds.name))\n\n // Prompt for source if not provided\n if (!sourceDataset) {\n sourceDataset = await promptForDataset({\n datasets: datasetsResponse,\n })\n }\n\n if (!datasetNames.has(sourceDataset)) {\n return this.output.error(`Source dataset \"${sourceDataset}\" doesn't exist`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Get and validate target dataset\n let targetDataset = args.target\n if (targetDataset) {\n const nameError = validateDatasetName(targetDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n } else {\n targetDataset = await promptForDatasetName({\n message: 'Target dataset name:',\n })\n }\n\n if (datasetNames.has(targetDataset)) {\n return this.output.error(`Target dataset \"${targetDataset}\" already exists`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Start the copy job\n try {\n this.output.log(\n `Copying dataset ${styleText('green', sourceDataset)} to ${styleText('green', targetDataset)}...`,\n )\n\n const response = await copyDataset({\n projectId,\n skipContentReleases,\n skipHistory,\n sourceDataset,\n targetDataset,\n })\n\n this.output.log(`Job ${styleText('green', response.jobId)} started`)\n\n if (flags.detach) {\n return\n }\n\n await this.subscribeToProgress(projectId, response.jobId)\n this.output.log(`Job ${styleText('green', response.jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Dataset copying failed: %s', message, error)\n return this.output.error(`Dataset copying failed: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleListMode(\n projectId: string,\n flags: {limit?: number; offset?: number},\n ): Promise<void> {\n copyDatasetDebug('Listing dataset copy jobs')\n\n try {\n const jobs = await listDatasetCopyJobs({\n limit: flags.limit,\n offset: flags.offset,\n projectId,\n })\n\n if (jobs.length === 0) {\n this.output.log(\"This project doesn't have any dataset copy jobs\")\n return\n }\n\n this.displayCopyJobsTable(jobs)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to list dataset copy jobs: %s', message, error)\n return this.output.error(`Failed to list dataset copy jobs: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async subscribeToProgress(projectId: string, jobId: string): Promise<void> {\n const spin = spinner('').start()\n const hasCliExecutionContext = Boolean(getCliExecutionContext())\n\n return new Promise<void>((resolve, reject) => {\n const sigintHandler = () => {\n subscription.unsubscribe()\n spin.fail('Copy interrupted.')\n exit(130)\n }\n\n const subscription = followCopyJobProgress({jobId, projectId}).subscribe({\n complete: () => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.succeed('Copy finished.')\n resolve()\n },\n error: (err) => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.fail('Copy failed.')\n reject(err)\n },\n next: (event: CopyJobProgressEvent) => {\n if (typeof event.progress === 'number') {\n spin.text = `Copy in progress: ${event.progress}%`\n }\n },\n })\n\n if (!hasCliExecutionContext) process.once('SIGINT', sigintHandler)\n })\n }\n}\n"],"names":["styleText","Args","Flags","exit","exitCodes","subdebug","getCliExecutionContext","SanityCommand","spinner","validateDatasetName","promptForDataset","promptForDatasetName","promptForProject","copyDataset","followCopyJobProgress","listDatasetCopyJobs","listDatasets","formatDuration","formatTimeAgo","formatCliErrorMessages","Table","getProjectIdFlag","copyDatasetDebug","CopyDatasetCommand","args","source","string","description","required","target","examples","command","flags","semantics","attach","exclusive","detach","boolean","limit","integer","dependsOn","max","list","offset","hiddenAliases","run","parse","isUnattended","errors","push","length","output","error","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","handleListMode","handleAttachMode","handleCopyMode","displayCopyJobsTable","jobs","table","columns","alignment","name","title","job","createdAt","id","sourceDataset","state","targetDataset","updatedAt","withHistory","timeStarted","Date","timeTaken","getTime","color","addRow","log","render","jobId","trim","RUNTIME_ERROR","subscribeToProgress","message","Error","String","skipHistory","Boolean","skipContentReleases","nameError","datasetsResponse","datasetNames","Set","map","ds","datasets","has","response","spin","start","hasCliExecutionContext","Promise","resolve","reject","sigintHandler","subscription","unsubscribe","fail","subscribe","complete","process","off","succeed","err","next","event","progress","text","once"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/commands/datasets/copy.ts"],"sourcesContent":["import {styleText} from 'node:util'\n\nimport {Args, Flags} from '@oclif/core'\nimport {exit} from '@oclif/core/errors'\nimport {exitCodes} from '@sanity/cli-core'\nimport {formatDuration, formatTimeAgo} from '@sanity/cli-core/dates'\nimport {subdebug} from '@sanity/cli-core/debug'\nimport {getCliExecutionContext} from '@sanity/cli-core/executionContext'\nimport {SanityCommand} from '@sanity/cli-core/SanityCommand'\nimport {spinner} from '@sanity/cli-core/ux'\n\nimport {validateDatasetName} from '../../actions/dataset/validateDatasetName.js'\nimport {promptForDataset} from '../../prompts/promptForDataset.js'\nimport {promptForDatasetName} from '../../prompts/promptForDatasetName.js'\nimport {promptForProject} from '../../prompts/promptForProject.js'\nimport {\n copyDataset,\n type CopyJobProgressEvent,\n type DatasetCopyJob,\n followCopyJobProgress,\n listDatasetCopyJobs,\n listDatasets,\n} from '../../services/datasets.js'\nimport {formatCliErrorMessages} from '../../util/formatCliErrorMessages.js'\nimport {Table} from '../../util/responsiveTable.js'\nimport {getProjectIdFlag} from '../../util/sharedFlags.js'\n\nconst copyDatasetDebug = subdebug('dataset:copy')\n\nexport class CopyDatasetCommand extends SanityCommand<typeof CopyDatasetCommand> {\n static override args = {\n source: Args.string({\n description: 'Name of the dataset to copy from',\n required: false,\n }),\n target: Args.string({\n description: 'Name of the dataset to copy to',\n required: false,\n }),\n }\n\n static override description = 'Copy a dataset or manage copy jobs'\n\n static override examples = [\n {\n command: '<%= config.bin %> <%= command.id %>',\n description: 'Interactively copy a dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset',\n description: 'Copy from source-dataset (prompts for target)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> source-dataset target-dataset',\n description: 'Copy from source-dataset to target-dataset',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-history source target',\n description: 'Copy without preserving document history (faster for large datasets)',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --skip-content-releases source target',\n description: 'Copy without content release documents',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --detach source target',\n description: 'Start copy job without waiting for completion',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --attach <job-id>',\n description: 'Attach to a running copy job to follow progress',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list',\n description: 'List all dataset copy jobs',\n },\n {\n command: '<%= config.bin %> <%= command.id %> --list --offset 2 --limit 10',\n description: 'List copy jobs with pagination',\n },\n ]\n\n static override flags = {\n ...getProjectIdFlag({\n description: 'Project ID to copy dataset in',\n semantics: 'override',\n }),\n attach: Flags.string({\n description: 'Attach to the running copy process to show progress',\n exclusive: ['list', 'detach', 'skip-history'],\n required: false,\n }),\n detach: Flags.boolean({\n description: 'Start the copy without waiting for it to finish',\n exclusive: ['list', 'attach'],\n required: false,\n }),\n limit: Flags.integer({\n dependsOn: ['list'],\n description: 'Maximum number of jobs returned (default 10, max 1000)',\n max: 1000,\n required: false,\n }),\n list: Flags.boolean({\n description: 'Lists all dataset copy jobs',\n exclusive: ['attach', 'detach', 'skip-history'],\n required: false,\n }),\n offset: Flags.integer({\n dependsOn: ['list'],\n description: 'Start position in the list of jobs (default 0)',\n required: false,\n }),\n 'skip-content-releases': Flags.boolean({\n description: \"Don't copy content release documents to the target dataset\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n 'skip-history': Flags.boolean({\n description: \"Don't preserve document history on copy\",\n exclusive: ['list', 'attach'],\n required: false,\n }),\n }\n\n static override hiddenAliases: string[] = ['dataset:copy']\n\n public async run(): Promise<void> {\n const {args, flags} = await this.parse(CopyDatasetCommand)\n\n if (!flags.list && !flags.attach && this.isUnattended()) {\n const errors: string[] = []\n\n if (!args.source) {\n errors.push('Source dataset is required. Pass it as the `<source>` argument.')\n }\n if (!args.target) {\n errors.push('Target dataset is required. Pass it as the `<target>` argument.')\n }\n\n if (errors.length > 0) {\n this.output.error(formatCliErrorMessages(errors), {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n const projectId = await this.getProjectId({\n fallback: () =>\n promptForProject({\n requiredPermissions: [\n {grant: 'read', permission: 'sanity.project.datasets'},\n {grant: 'create', permission: 'sanity.project.datasets'},\n ],\n }),\n })\n\n // Route to appropriate mode\n if (flags.list) {\n return this.handleListMode(projectId, flags)\n }\n\n if (flags.attach) {\n return this.handleAttachMode(projectId, flags.attach)\n }\n\n return this.handleCopyMode(projectId, args, flags)\n }\n\n private displayCopyJobsTable(jobs: DatasetCopyJob[]): void {\n const table = new Table({\n columns: [\n {alignment: 'left', name: 'id', title: 'Job ID'},\n {alignment: 'left', name: 'sourceDataset', title: 'Source Dataset'},\n {alignment: 'left', name: 'targetDataset', title: 'Target Dataset'},\n {alignment: 'left', name: 'state', title: 'State'},\n {alignment: 'left', name: 'withHistory', title: 'With history'},\n {alignment: 'left', name: 'timeStarted', title: 'Time started'},\n {alignment: 'left', name: 'timeTaken', title: 'Time taken'},\n ],\n title: 'Dataset copy jobs for this project in descending order',\n })\n\n for (const job of jobs) {\n const {createdAt, id, sourceDataset, state, targetDataset, updatedAt, withHistory} = job\n\n let timeStarted = ''\n if (createdAt !== '') {\n timeStarted = formatTimeAgo(new Date(createdAt))\n }\n\n let timeTaken = ''\n if (createdAt !== '' && updatedAt !== '') {\n timeTaken = formatDuration(new Date(updatedAt).getTime() - new Date(createdAt).getTime())\n }\n\n let color: '' | 'green' | 'red' | 'yellow'\n switch (state) {\n case 'completed': {\n color = 'green'\n break\n }\n case 'failed': {\n color = 'red'\n break\n }\n case 'pending': {\n color = 'yellow'\n break\n }\n default: {\n color = ''\n }\n }\n\n table.addRow(\n {\n id,\n sourceDataset,\n state,\n targetDataset,\n timeStarted,\n timeTaken,\n withHistory,\n },\n {color},\n )\n }\n\n this.output.log(table.render())\n }\n\n private async handleAttachMode(projectId: string, jobId: string): Promise<void> {\n copyDatasetDebug('Attaching to copy job %s', jobId)\n\n if (jobId.trim() === '') {\n return this.output.error('Please supply a valid jobId', {exit: exitCodes.RUNTIME_ERROR})\n }\n\n try {\n await this.subscribeToProgress(projectId, jobId)\n this.output.log(`Job ${styleText('green', jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to attach to copy job: %s', message, error)\n return this.output.error(`Failed to attach to copy job: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleCopyMode(\n projectId: string,\n args: {source?: string; target?: string},\n flags: {detach?: boolean; 'skip-content-releases'?: boolean; 'skip-history'?: boolean},\n ): Promise<void> {\n copyDatasetDebug('Starting copy mode')\n\n const skipHistory = Boolean(flags['skip-history'])\n const skipContentReleases = Boolean(flags['skip-content-releases'])\n\n // Surfaced before any prompting so the flag is still actionable: a copy job\n // can't be canceled once started, so mentioning it after the job kicks off\n // leaves nothing to decide.\n if (!skipHistory) {\n this.output.log(\n `Note: You can run this command with flag '--skip-history'. The flag will reduce copy time in larger datasets.`,\n )\n }\n\n // Get and validate source dataset\n let sourceDataset = args.source\n if (sourceDataset) {\n const nameError = validateDatasetName(sourceDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n }\n\n let datasetsResponse\n try {\n datasetsResponse = await listDatasets(projectId)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to fetch datasets: %s', message, error)\n return this.output.error(`Failed to fetch datasets: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n const datasetNames = new Set(datasetsResponse.map((ds) => ds.name))\n\n // Prompt for source if not provided\n if (!sourceDataset) {\n sourceDataset = await promptForDataset({\n datasets: datasetsResponse,\n })\n }\n\n if (!datasetNames.has(sourceDataset)) {\n return this.output.error(`Source dataset \"${sourceDataset}\" doesn't exist`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Get and validate target dataset\n let targetDataset = args.target\n if (targetDataset) {\n const nameError = validateDatasetName(targetDataset)\n if (nameError) {\n return this.output.error(nameError, {exit: exitCodes.USAGE_ERROR})\n }\n } else {\n targetDataset = await promptForDatasetName({\n message: 'Target dataset name:',\n })\n }\n\n if (datasetNames.has(targetDataset)) {\n return this.output.error(`Target dataset \"${targetDataset}\" already exists`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n\n // Start the copy job\n try {\n this.output.log(\n `Copying dataset ${styleText('green', sourceDataset)} to ${styleText('green', targetDataset)}...`,\n )\n\n const response = await copyDataset({\n projectId,\n skipContentReleases,\n skipHistory,\n sourceDataset,\n targetDataset,\n })\n\n this.output.log(`Job ${styleText('green', response.jobId)} started`)\n\n if (flags.detach) {\n return\n }\n\n await this.subscribeToProgress(projectId, response.jobId)\n this.output.log(`Job ${styleText('green', response.jobId)} completed`)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Dataset copying failed: %s', message, error)\n return this.output.error(`Dataset copying failed: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async handleListMode(\n projectId: string,\n flags: {limit?: number; offset?: number},\n ): Promise<void> {\n copyDatasetDebug('Listing dataset copy jobs')\n\n try {\n const jobs = await listDatasetCopyJobs({\n limit: flags.limit,\n offset: flags.offset,\n projectId,\n })\n\n if (jobs.length === 0) {\n this.output.log(\"This project doesn't have any dataset copy jobs\")\n return\n }\n\n this.displayCopyJobsTable(jobs)\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error)\n copyDatasetDebug('Failed to list dataset copy jobs: %s', message, error)\n return this.output.error(`Failed to list dataset copy jobs: ${message}`, {\n exit: exitCodes.RUNTIME_ERROR,\n })\n }\n }\n\n private async subscribeToProgress(projectId: string, jobId: string): Promise<void> {\n const spin = spinner('').start()\n const hasCliExecutionContext = Boolean(getCliExecutionContext())\n\n return new Promise<void>((resolve, reject) => {\n const sigintHandler = () => {\n subscription.unsubscribe()\n spin.fail('Copy interrupted.')\n exit(130)\n }\n\n const subscription = followCopyJobProgress({jobId, projectId}).subscribe({\n complete: () => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.succeed('Copy finished.')\n resolve()\n },\n error: (err) => {\n if (!hasCliExecutionContext) process.off('SIGINT', sigintHandler)\n spin.fail('Copy failed.')\n reject(err)\n },\n next: (event: CopyJobProgressEvent) => {\n if (typeof event.progress === 'number') {\n spin.text = `Copy in progress: ${event.progress}%`\n }\n },\n })\n\n if (!hasCliExecutionContext) process.once('SIGINT', sigintHandler)\n })\n }\n}\n"],"names":["styleText","Args","Flags","exit","exitCodes","formatDuration","formatTimeAgo","subdebug","getCliExecutionContext","SanityCommand","spinner","validateDatasetName","promptForDataset","promptForDatasetName","promptForProject","copyDataset","followCopyJobProgress","listDatasetCopyJobs","listDatasets","formatCliErrorMessages","Table","getProjectIdFlag","copyDatasetDebug","CopyDatasetCommand","args","source","string","description","required","target","examples","command","flags","semantics","attach","exclusive","detach","boolean","limit","integer","dependsOn","max","list","offset","hiddenAliases","run","parse","isUnattended","errors","push","length","output","error","USAGE_ERROR","projectId","getProjectId","fallback","requiredPermissions","grant","permission","handleListMode","handleAttachMode","handleCopyMode","displayCopyJobsTable","jobs","table","columns","alignment","name","title","job","createdAt","id","sourceDataset","state","targetDataset","updatedAt","withHistory","timeStarted","Date","timeTaken","getTime","color","addRow","log","render","jobId","trim","RUNTIME_ERROR","subscribeToProgress","message","Error","String","skipHistory","Boolean","skipContentReleases","nameError","datasetsResponse","datasetNames","Set","map","ds","datasets","has","response","spin","start","hasCliExecutionContext","Promise","resolve","reject","sigintHandler","subscription","unsubscribe","fail","subscribe","complete","process","off","succeed","err","next","event","progress","text","once"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/util/extractDocumentsFromNdjsonOrTarball.ts"],"sourcesContent":["import path from 'node:path'\nimport readline from 'node:readline'\nimport {Readable, type Writable} from 'node:stream'\nimport zlib from 'node:zlib'\n\nimport {type SanityDocument} from '@sanity/types'\nimport tar from 'tar-stream'\n\nimport {isTar} from './isTar.js'\n\nconst HEADER_SIZE = 300\n\n// https://github.com/kevva/is-gzip/blob/13dab7c877787bd5cff9de5482b1736f00df99c6/index.js\nconst isGzip = (buf: Buffer) =>\n buf.length >= 3 && buf[0] === 0x1f && buf[1] === 0x8b && buf[2] === 0x08\n\n// https://github.com/watson/is-deflate/blob/f9e8f0c7814eed715e13e29e97c69acee319686a/index.js\nconst isDeflate = (buf: Buffer) =>\n buf.length >= 2 && buf[0] === 0x78 && (buf[1] === 1 || buf[1] === 0x9c || buf[1] === 0xda)\n\nasync function* extract<TReturn>(\n stream: AsyncIterable<Buffer>,\n extractor: AsyncIterable<TReturn> & Writable,\n) {\n // set up a task to drain the input iterable into the extractor asynchronously\n // before this function delegates to the extractor's iterable (containing the\n // result of the extraction)\n const drained = new Promise<void>((resolve, reject) => {\n // setTimeout is used here to ensure draining occurs after delegation\n setTimeout(async () => {\n try {\n for await (const chunk of stream) extractor.write(chunk)\n extractor.end()\n resolve()\n } catch (err) {\n reject(err)\n }\n })\n })\n\n // have this function delegate the results of the extractor\n yield* extractor\n await drained\n extractor.destroy()\n}\n\n/**\n * Given a async iterable of buffers, looks at the header of the file in the\n * first few bytes to see the file type then extracts the contents tries again.\n * If the given iterable of buffers is a tarball then it looks for an ndjson\n * files and returns another iterable of buffers with the contents of the\n * ndjson file\n */\nasync function* maybeExtractNdjson(stream: AsyncIterable<Buffer>): AsyncIterable<Buffer> {\n let buffer = Buffer.alloc(0)\n\n for await (const chunk of stream) {\n buffer = Buffer.concat([buffer, chunk])\n if (buffer.length < HEADER_SIZE) continue\n\n const fileHeader = buffer\n const restOfStream = async function* restOfStream() {\n yield fileHeader\n yield* stream\n }\n\n if (isGzip(fileHeader)) {\n yield* maybeExtractNdjson(extract(restOfStream(), zlib.createGunzip()))\n return\n }\n\n if (isDeflate(fileHeader)) {\n yield* maybeExtractNdjson(extract(restOfStream(), zlib.createDeflate()))\n return\n }\n\n if (isTar(fileHeader)) {\n for await (const entry of extract(restOfStream(), tar.extract())) {\n const filename = path.basename(entry.header.name)\n const extname = path.extname(filename).toLowerCase()\n // ignore hidden and non-ndjson files\n if (extname !== '.ndjson' || filename.startsWith('.')) continue\n\n for await (const ndjsonChunk of entry) yield ndjsonChunk\n return\n }\n }\n\n yield* restOfStream()\n }\n}\n\n/**\n * Takes in an async iterable of buffers from an ndjson file or tarball and\n * returns an async iterable of sanity documents.\n */\nexport async function* extractDocumentsFromNdjsonOrTarball(\n file: AsyncIterable<Buffer>,\n): AsyncIterable<SanityDocument> {\n const lines = readline.createInterface({\n input: Readable.from(maybeExtractNdjson(file)),\n })\n\n for await (const line of lines) {\n const trimmed = line.trim()\n if (trimmed) yield JSON.parse(trimmed) as SanityDocument\n }\n lines.close()\n}\n"],"names":["path","readline","Readable","zlib","tar","isTar","HEADER_SIZE","isGzip","buf","length","isDeflate","extract","stream","extractor","drained","Promise","resolve","reject","setTimeout","chunk","write","end","err","destroy","maybeExtractNdjson","buffer","Buffer","alloc","concat","fileHeader","restOfStream","createGunzip","createDeflate","entry","filename","basename","header","name","extname","toLowerCase","startsWith","ndjsonChunk","extractDocumentsFromNdjsonOrTarball","file","lines","createInterface","input","from","line","trimmed","trim","JSON","parse","close"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAC5B,OAAOC,cAAc,gBAAe;AACpC,SAAQC,QAAQ,QAAsB,cAAa;AACnD,OAAOC,UAAU,YAAW;AAG5B,OAAOC,SAAS,aAAY;AAE5B,SAAQC,KAAK,QAAO,aAAY;AAEhC,MAAMC,cAAc;AAEpB,0FAA0F;AAC1F,MAAMC,SAAS,CAACC,MACdA,IAAIC,MAAM,IAAI,KAAKD,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK;AAEtE,8FAA8F;AAC9F,MAAME,YAAY,CAACF,MACjBA,IAAIC,MAAM,IAAI,KAAKD,GAAG,CAAC,EAAE,KAAK,QAASA,CAAAA,GAAG,CAAC,EAAE,KAAK,KAAKA,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK,IAAG;AAE1F,gBAAgBG,QACdC,MAA6B,EAC7BC,SAA4C;IAE5C,8EAA8E;IAC9E,6EAA6E;IAC7E,4BAA4B;IAC5B,MAAMC,UAAU,IAAIC,QAAc,CAACC,SAASC;QAC1C,qEAAqE;QACrEC,WAAW;YACT,IAAI;gBACF,WAAW,MAAMC,SAASP,OAAQC,UAAUO,KAAK,CAACD;gBAClDN,UAAUQ,GAAG;gBACbL;YACF,EAAE,OAAOM,KAAK;gBACZL,OAAOK;YACT;QACF;IACF;IAEA,2DAA2D;IAC3D,OAAOT;IACP,MAAMC;IACND,UAAUU,OAAO;AACnB;AAEA;;;;;;CAMC,GACD,gBAAgBC,mBAAmBZ,MAA6B;IAC9D,IAAIa,SAASC,OAAOC,KAAK,CAAC;IAE1B,WAAW,MAAMR,SAASP,OAAQ;QAChCa,SAASC,OAAOE,MAAM,CAAC;YAACH;YAAQN;SAAM;QACtC,IAAIM,OAAOhB,MAAM,GAAGH,aAAa;QAEjC,MAAMuB,aAAaJ;QACnB,MAAMK,eAAe,gBAAgBA;YACnC,MAAMD;YACN,OAAOjB;QACT;QAEA,IAAIL,OAAOsB,aAAa;YACtB,OAAOL,mBAAmBb,QAAQmB,gBAAgB3B,KAAK4B,YAAY;YACnE;QACF;QAEA,IAAIrB,UAAUmB,aAAa;YACzB,OAAOL,mBAAmBb,QAAQmB,gBAAgB3B,KAAK6B,aAAa;YACpE;QACF;QAEA,IAAI3B,MAAMwB,aAAa;YACrB,WAAW,MAAMI,SAAStB,QAAQmB,gBAAgB1B,IAAIO,OAAO,IAAK;gBAChE,MAAMuB,WAAWlC,KAAKmC,QAAQ,CAACF,MAAMG,MAAM,CAACC,IAAI;gBAChD,MAAMC,UAAUtC,KAAKsC,OAAO,CAACJ,UAAUK,WAAW;gBAClD,qCAAqC;gBACrC,IAAID,YAAY,aAAaJ,SAASM,UAAU,CAAC,MAAM;gBAEvD,WAAW,MAAMC,eAAeR,MAAO,MAAMQ;gBAC7C;YACF;QACF;QAEA,OAAOX;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,gBAAgBY,oCACrBC,IAA2B;IAE3B,MAAMC,QAAQ3C,SAAS4C,eAAe,CAAC;QACrCC,OAAO5C,SAAS6C,IAAI,CAACvB,mBAAmBmB;IAC1C;IAEA,WAAW,MAAMK,QAAQJ,MAAO;QAC9B,MAAMK,UAAUD,KAAKE,IAAI;QACzB,IAAID,SAAS,MAAME,KAAKC,KAAK,CAACH;IAChC;IACAL,MAAMS,KAAK;AACb"}
1
+ {"version":3,"sources":["../../src/util/extractDocumentsFromNdjsonOrTarball.ts"],"sourcesContent":["import path from 'node:path'\nimport readline from 'node:readline'\nimport {Readable} from 'node:stream'\nimport zlib from 'node:zlib'\n\nimport {type SanityDocument} from '@sanity/types'\nimport tar from 'tar-stream'\n\nimport {isTar} from './isTar.js'\n\nconst HEADER_SIZE = 300\n\n// https://github.com/kevva/is-gzip/blob/13dab7c877787bd5cff9de5482b1736f00df99c6/index.js\nconst isGzip = (buf: Buffer) =>\n buf.length >= 3 && buf[0] === 0x1f && buf[1] === 0x8b && buf[2] === 0x08\n\n// https://github.com/watson/is-deflate/blob/f9e8f0c7814eed715e13e29e97c69acee319686a/index.js\nconst isDeflate = (buf: Buffer) =>\n buf.length >= 2 && buf[0] === 0x78 && (buf[1] === 1 || buf[1] === 0x9c || buf[1] === 0xda)\n\nasync function* extract<TReturn>(\n stream: AsyncIterable<Buffer>,\n extractor: AsyncIterable<TReturn> & {\n destroy(): unknown\n end(data: void): unknown\n write(chunk: Buffer): unknown\n },\n) {\n // set up a task to drain the input iterable into the extractor asynchronously\n // before this function delegates to the extractor's iterable (containing the\n // result of the extraction)\n const drained = new Promise<void>((resolve, reject) => {\n // setTimeout is used here to ensure draining occurs after delegation\n setTimeout(async () => {\n try {\n for await (const chunk of stream) extractor.write(chunk)\n extractor.end()\n resolve()\n } catch (err) {\n reject(err)\n }\n })\n })\n\n // have this function delegate the results of the extractor\n yield* extractor\n await drained\n extractor.destroy()\n}\n\n/**\n * Given a async iterable of buffers, looks at the header of the file in the\n * first few bytes to see the file type then extracts the contents tries again.\n * If the given iterable of buffers is a tarball then it looks for an ndjson\n * files and returns another iterable of buffers with the contents of the\n * ndjson file\n */\nasync function* maybeExtractNdjson(stream: AsyncIterable<Buffer>): AsyncIterable<Buffer> {\n let buffer = Buffer.alloc(0)\n\n for await (const chunk of stream) {\n buffer = Buffer.concat([buffer, chunk])\n if (buffer.length < HEADER_SIZE) continue\n\n const fileHeader = buffer\n const restOfStream = async function* restOfStream() {\n yield fileHeader\n yield* stream\n }\n\n if (isGzip(fileHeader)) {\n yield* maybeExtractNdjson(extract(restOfStream(), zlib.createGunzip()))\n return\n }\n\n if (isDeflate(fileHeader)) {\n yield* maybeExtractNdjson(extract(restOfStream(), zlib.createDeflate()))\n return\n }\n\n if (isTar(fileHeader)) {\n for await (const entry of extract(restOfStream(), tar.extract())) {\n const filename = path.basename(entry.header.name)\n const extname = path.extname(filename).toLowerCase()\n // ignore hidden and non-ndjson files\n if (extname !== '.ndjson' || filename.startsWith('.')) continue\n\n for await (const ndjsonChunk of entry) yield ndjsonChunk as Buffer\n return\n }\n }\n\n yield* restOfStream()\n }\n}\n\n/**\n * Takes in an async iterable of buffers from an ndjson file or tarball and\n * returns an async iterable of sanity documents.\n */\nexport async function* extractDocumentsFromNdjsonOrTarball(\n file: AsyncIterable<Buffer>,\n): AsyncIterable<SanityDocument> {\n const lines = readline.createInterface({\n input: Readable.from(maybeExtractNdjson(file)),\n })\n\n for await (const line of lines) {\n const trimmed = line.trim()\n if (trimmed) yield JSON.parse(trimmed) as SanityDocument\n }\n lines.close()\n}\n"],"names":["path","readline","Readable","zlib","tar","isTar","HEADER_SIZE","isGzip","buf","length","isDeflate","extract","stream","extractor","drained","Promise","resolve","reject","setTimeout","chunk","write","end","err","destroy","maybeExtractNdjson","buffer","Buffer","alloc","concat","fileHeader","restOfStream","createGunzip","createDeflate","entry","filename","basename","header","name","extname","toLowerCase","startsWith","ndjsonChunk","extractDocumentsFromNdjsonOrTarball","file","lines","createInterface","input","from","line","trimmed","trim","JSON","parse","close"],"mappings":"AAAA,OAAOA,UAAU,YAAW;AAC5B,OAAOC,cAAc,gBAAe;AACpC,SAAQC,QAAQ,QAAO,cAAa;AACpC,OAAOC,UAAU,YAAW;AAG5B,OAAOC,SAAS,aAAY;AAE5B,SAAQC,KAAK,QAAO,aAAY;AAEhC,MAAMC,cAAc;AAEpB,0FAA0F;AAC1F,MAAMC,SAAS,CAACC,MACdA,IAAIC,MAAM,IAAI,KAAKD,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK;AAEtE,8FAA8F;AAC9F,MAAME,YAAY,CAACF,MACjBA,IAAIC,MAAM,IAAI,KAAKD,GAAG,CAAC,EAAE,KAAK,QAASA,CAAAA,GAAG,CAAC,EAAE,KAAK,KAAKA,GAAG,CAAC,EAAE,KAAK,QAAQA,GAAG,CAAC,EAAE,KAAK,IAAG;AAE1F,gBAAgBG,QACdC,MAA6B,EAC7BC,SAIC;IAED,8EAA8E;IAC9E,6EAA6E;IAC7E,4BAA4B;IAC5B,MAAMC,UAAU,IAAIC,QAAc,CAACC,SAASC;QAC1C,qEAAqE;QACrEC,WAAW;YACT,IAAI;gBACF,WAAW,MAAMC,SAASP,OAAQC,UAAUO,KAAK,CAACD;gBAClDN,UAAUQ,GAAG;gBACbL;YACF,EAAE,OAAOM,KAAK;gBACZL,OAAOK;YACT;QACF;IACF;IAEA,2DAA2D;IAC3D,OAAOT;IACP,MAAMC;IACND,UAAUU,OAAO;AACnB;AAEA;;;;;;CAMC,GACD,gBAAgBC,mBAAmBZ,MAA6B;IAC9D,IAAIa,SAASC,OAAOC,KAAK,CAAC;IAE1B,WAAW,MAAMR,SAASP,OAAQ;QAChCa,SAASC,OAAOE,MAAM,CAAC;YAACH;YAAQN;SAAM;QACtC,IAAIM,OAAOhB,MAAM,GAAGH,aAAa;QAEjC,MAAMuB,aAAaJ;QACnB,MAAMK,eAAe,gBAAgBA;YACnC,MAAMD;YACN,OAAOjB;QACT;QAEA,IAAIL,OAAOsB,aAAa;YACtB,OAAOL,mBAAmBb,QAAQmB,gBAAgB3B,KAAK4B,YAAY;YACnE;QACF;QAEA,IAAIrB,UAAUmB,aAAa;YACzB,OAAOL,mBAAmBb,QAAQmB,gBAAgB3B,KAAK6B,aAAa;YACpE;QACF;QAEA,IAAI3B,MAAMwB,aAAa;YACrB,WAAW,MAAMI,SAAStB,QAAQmB,gBAAgB1B,IAAIO,OAAO,IAAK;gBAChE,MAAMuB,WAAWlC,KAAKmC,QAAQ,CAACF,MAAMG,MAAM,CAACC,IAAI;gBAChD,MAAMC,UAAUtC,KAAKsC,OAAO,CAACJ,UAAUK,WAAW;gBAClD,qCAAqC;gBACrC,IAAID,YAAY,aAAaJ,SAASM,UAAU,CAAC,MAAM;gBAEvD,WAAW,MAAMC,eAAeR,MAAO,MAAMQ;gBAC7C;YACF;QACF;QAEA,OAAOX;IACT;AACF;AAEA;;;CAGC,GACD,OAAO,gBAAgBY,oCACrBC,IAA2B;IAE3B,MAAMC,QAAQ3C,SAAS4C,eAAe,CAAC;QACrCC,OAAO5C,SAAS6C,IAAI,CAACvB,mBAAmBmB;IAC1C;IAEA,WAAW,MAAMK,QAAQJ,MAAO;QAC9B,MAAMK,UAAUD,KAAKE,IAAI;QACzB,IAAID,SAAS,MAAME,KAAKC,KAAK,CAACH;IAChC;IACAL,MAAMS,KAAK;AACb"}
@@ -6157,5 +6157,5 @@
6157
6157
  ]
6158
6158
  }
6159
6159
  },
6160
- "version": "8.6.0"
6160
+ "version": "8.7.0"
6161
6161
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/cli",
3
- "version": "8.6.0",
3
+ "version": "8.7.0",
4
4
  "description": "Sanity CLI tool for managing Sanity projects and organizations",
5
5
  "keywords": [
6
6
  "cli",
@@ -65,19 +65,19 @@
65
65
  "@sanity/client": "^8.4.0",
66
66
  "@sanity/codegen": "^8.0.0",
67
67
  "@sanity/descriptors": "^1.3.0",
68
- "@sanity/export": "^6.2.0",
68
+ "@sanity/export": "^6.2.1",
69
69
  "@sanity/generate-help-url": "^4.0.0",
70
70
  "@sanity/id-utils": "^1.0.0",
71
71
  "@sanity/import": "^6.0.4",
72
72
  "@sanity/migrate": "^8.0.2",
73
- "@sanity/runtime-cli": "^17.4.0",
74
- "@sanity/schema": "^6.7.0",
73
+ "@sanity/runtime-cli": "^17.11.0",
74
+ "@sanity/schema": "^6.11.0",
75
75
  "@sanity/telemetry": "^1.1.0",
76
76
  "@sanity/template-validator": "^3.1.0",
77
- "@sanity/types": "^6.7.0",
77
+ "@sanity/types": "^6.11.0",
78
78
  "@sanity/worker-channels": "^2.0.0",
79
- "@sanity/workflow-cli": "0.30.0",
80
- "@sanity/workflow-engine": "0.30.0",
79
+ "@sanity/workflow-cli": "0.31.0",
80
+ "@sanity/workflow-engine": "0.31.0",
81
81
  "@vercel/frameworks": "3.29.0",
82
82
  "chokidar": "^5.0.0",
83
83
  "console-table-printer": "^2.15.0",
@@ -120,10 +120,10 @@
120
120
  "which": "^6.0.1",
121
121
  "wrap-ansi": "^9.0.2",
122
122
  "yaml": "^2.9.0",
123
- "zod": "^4.4.3",
124
- "@sanity/cli-build": "^6.1.0",
125
- "@sanity/cli-core": "^3.5.1",
126
- "@sanity/workbench-cli": "^2.2.4"
123
+ "zod": "^4.5.2",
124
+ "@sanity/cli-build": "^6.2.0",
125
+ "@sanity/workbench-cli": "^2.3.0",
126
+ "@sanity/cli-core": "^3.6.0"
127
127
  },
128
128
  "devDependencies": {
129
129
  "@eslint/compat": "^2.1.0",
@@ -152,12 +152,12 @@
152
152
  "oxc-transform-react": "^0.147.0",
153
153
  "publint": "^0.3.21",
154
154
  "rimraf": "^6.0.1",
155
- "sanity": "^6.7.0",
155
+ "sanity": "^6.11.0",
156
156
  "typescript": "^6.0.3",
157
157
  "vitest": "^4.1.11",
158
- "@repo/tsconfig": "3.70.0",
159
158
  "@repo/package.config": "0.0.1",
160
- "@sanity/cli-test": "15.1.0",
159
+ "@repo/tsconfig": "3.70.0",
160
+ "@sanity/cli-test": "16.0.0",
161
161
  "@sanity/eslint-config-cli": "1.1.3"
162
162
  },
163
163
  "peerDependencies": {
@@ -1,125 +0,0 @@
1
- import { pluralize } from './pluralize.js';
2
- const MS_IN = {
3
- day: 86_400_000,
4
- hour: 3_600_000,
5
- minute: 60_000,
6
- month: 2_592_000_000,
7
- second: 1000,
8
- week: 604_800_000,
9
- year: 31_536_000_000
10
- };
11
- /** Largest unit first, so the first unit the duration fills is the one we report. */ const UNITS = [
12
- {
13
- ms: MS_IN.year,
14
- unit: 'year'
15
- },
16
- {
17
- ms: MS_IN.month,
18
- unit: 'month'
19
- },
20
- {
21
- ms: MS_IN.week,
22
- unit: 'week'
23
- },
24
- {
25
- ms: MS_IN.day,
26
- unit: 'day'
27
- },
28
- {
29
- ms: MS_IN.hour,
30
- unit: 'hour'
31
- },
32
- {
33
- ms: MS_IN.minute,
34
- unit: 'minute'
35
- },
36
- {
37
- ms: MS_IN.second,
38
- unit: 'second'
39
- }
40
- ];
41
- const relativeTime = new Intl.RelativeTimeFormat('en', {
42
- numeric: 'always'
43
- });
44
- const DATE_ONLY_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
45
- function pad(value, length = 2) {
46
- return String(value).padStart(length, '0');
47
- }
48
- /**
49
- * Returns the coarsest unit the elapsed time fills and the rounded number of those
50
- * units, or `undefined` below a second.
51
- *
52
- * Rounding can land the value on the boundary of the next unit up, so we promote
53
- * rather than report `60 minutes` where `1 hour` is meant. Units aren't all whole
54
- * multiples of each other, so the boundary is the rounded count we'd display, which
55
- * is why 25+ days reads as `1 month` rather than `4 weeks`.
56
- */ function selectUnit(elapsed) {
57
- let index = UNITS.findIndex(({ ms })=>elapsed >= ms);
58
- if (index === -1) return undefined;
59
- while(index > 0 && Math.round(elapsed / UNITS[index].ms) >= Math.round(UNITS[index - 1].ms / UNITS[index].ms)){
60
- index -= 1;
61
- }
62
- const { ms, unit } = UNITS[index];
63
- return {
64
- unit,
65
- value: Math.round(elapsed / ms)
66
- };
67
- }
68
- /**
69
- * Formats a timestamp as `YYYY-MM-DD HH:mm:ss` in the local time zone. Unparseable
70
- * timestamps are returned as-is, so a bad value from the API doesn't fail the command.
71
- *
72
- * @param timestamp - An ISO 8601 timestamp
73
- * @internal
74
- */ export function formatDateTime(timestamp) {
75
- const date = new Date(timestamp);
76
- if (!Number.isFinite(date.getTime())) return timestamp;
77
- const day = `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
78
- const time = `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
79
- return `${day} ${time}`;
80
- }
81
- /**
82
- * Formats a duration in milliseconds as an approximate, human readable length of
83
- * time, eg `less than a minute`, `5 minutes` or `2 days`.
84
- *
85
- * @param ms - The duration in milliseconds
86
- * @internal
87
- */ export function formatDuration(ms) {
88
- if (!Number.isFinite(ms)) return '';
89
- const selected = selectUnit(Math.abs(ms));
90
- if (!selected || selected.unit === 'second') return 'less than a minute';
91
- return `${selected.value} ${pluralize(selected.unit, selected.value)}`;
92
- }
93
- /**
94
- * Formats a date relative to now, eg `2 days ago` or `in 3 hours`.
95
- *
96
- * @param date - The date to describe
97
- * @param now - The reference point to compare against, defaults to the current time
98
- * @internal
99
- */ export function formatTimeAgo(date, now = Date.now()) {
100
- const delta = date.getTime() - now;
101
- if (!Number.isFinite(delta)) return '';
102
- const selected = selectUnit(Math.abs(delta));
103
- if (!selected) return 'just now';
104
- return relativeTime.format(delta < 0 ? -selected.value : selected.value, selected.unit);
105
- }
106
- /**
107
- * Parses a `YYYY-MM-DD` date into a `Date` at local midnight. Returns `undefined`
108
- * for anything that isn't a real calendar date in that exact format.
109
- *
110
- * @param value - The date string to parse
111
- * @internal
112
- */ export function parseDateOnly(value) {
113
- const match = DATE_ONLY_PATTERN.exec(value);
114
- if (!match) return undefined;
115
- const year = Number(match[1]);
116
- const month = Number(match[2]);
117
- const day = Number(match[3]);
118
- const date = new Date(year, month - 1, day);
119
- // `new Date(2024, 1, 31)` silently rolls over into March, so reject anything
120
- // that didn't survive the round trip
121
- const isRoundTrip = date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day;
122
- return isRoundTrip ? date : undefined;
123
- }
124
-
125
- //# sourceMappingURL=dates.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/util/dates.ts"],"sourcesContent":["import {pluralize} from './pluralize.js'\n\nconst MS_IN = {\n day: 86_400_000,\n hour: 3_600_000,\n minute: 60_000,\n month: 2_592_000_000, // 30 days\n second: 1000,\n week: 604_800_000,\n year: 31_536_000_000, // 365 days\n} as const\n\n/** Largest unit first, so the first unit the duration fills is the one we report. */\nconst UNITS: {ms: number; unit: Intl.RelativeTimeFormatUnit}[] = [\n {ms: MS_IN.year, unit: 'year'},\n {ms: MS_IN.month, unit: 'month'},\n {ms: MS_IN.week, unit: 'week'},\n {ms: MS_IN.day, unit: 'day'},\n {ms: MS_IN.hour, unit: 'hour'},\n {ms: MS_IN.minute, unit: 'minute'},\n {ms: MS_IN.second, unit: 'second'},\n]\n\nconst relativeTime = new Intl.RelativeTimeFormat('en', {numeric: 'always'})\n\nconst DATE_ONLY_PATTERN = /^(\\d{4})-(\\d{2})-(\\d{2})$/\n\nfunction pad(value: number, length = 2): string {\n return String(value).padStart(length, '0')\n}\n\n/**\n * Returns the coarsest unit the elapsed time fills and the rounded number of those\n * units, or `undefined` below a second.\n *\n * Rounding can land the value on the boundary of the next unit up, so we promote\n * rather than report `60 minutes` where `1 hour` is meant. Units aren't all whole\n * multiples of each other, so the boundary is the rounded count we'd display, which\n * is why 25+ days reads as `1 month` rather than `4 weeks`.\n */\nfunction selectUnit(\n elapsed: number,\n): {unit: Intl.RelativeTimeFormatUnit; value: number} | undefined {\n let index = UNITS.findIndex(({ms}) => elapsed >= ms)\n if (index === -1) return undefined\n\n while (\n index > 0 &&\n Math.round(elapsed / UNITS[index].ms) >= Math.round(UNITS[index - 1].ms / UNITS[index].ms)\n ) {\n index -= 1\n }\n\n const {ms, unit} = UNITS[index]\n return {unit, value: Math.round(elapsed / ms)}\n}\n\n/**\n * Formats a timestamp as `YYYY-MM-DD HH:mm:ss` in the local time zone. Unparseable\n * timestamps are returned as-is, so a bad value from the API doesn't fail the command.\n *\n * @param timestamp - An ISO 8601 timestamp\n * @internal\n */\nexport function formatDateTime(timestamp: string): string {\n const date = new Date(timestamp)\n if (!Number.isFinite(date.getTime())) return timestamp\n\n const day = `${pad(date.getFullYear(), 4)}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`\n const time = `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`\n return `${day} ${time}`\n}\n\n/**\n * Formats a duration in milliseconds as an approximate, human readable length of\n * time, eg `less than a minute`, `5 minutes` or `2 days`.\n *\n * @param ms - The duration in milliseconds\n * @internal\n */\nexport function formatDuration(ms: number): string {\n if (!Number.isFinite(ms)) return ''\n\n const selected = selectUnit(Math.abs(ms))\n if (!selected || selected.unit === 'second') return 'less than a minute'\n\n return `${selected.value} ${pluralize(selected.unit, selected.value)}`\n}\n\n/**\n * Formats a date relative to now, eg `2 days ago` or `in 3 hours`.\n *\n * @param date - The date to describe\n * @param now - The reference point to compare against, defaults to the current time\n * @internal\n */\nexport function formatTimeAgo(date: Date, now: number = Date.now()): string {\n const delta = date.getTime() - now\n if (!Number.isFinite(delta)) return ''\n\n const selected = selectUnit(Math.abs(delta))\n if (!selected) return 'just now'\n\n return relativeTime.format(delta < 0 ? -selected.value : selected.value, selected.unit)\n}\n\n/**\n * Parses a `YYYY-MM-DD` date into a `Date` at local midnight. Returns `undefined`\n * for anything that isn't a real calendar date in that exact format.\n *\n * @param value - The date string to parse\n * @internal\n */\nexport function parseDateOnly(value: string): Date | undefined {\n const match = DATE_ONLY_PATTERN.exec(value)\n if (!match) return undefined\n\n const year = Number(match[1])\n const month = Number(match[2])\n const day = Number(match[3])\n const date = new Date(year, month - 1, day)\n\n // `new Date(2024, 1, 31)` silently rolls over into March, so reject anything\n // that didn't survive the round trip\n const isRoundTrip =\n date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day\n\n return isRoundTrip ? date : undefined\n}\n"],"names":["pluralize","MS_IN","day","hour","minute","month","second","week","year","UNITS","ms","unit","relativeTime","Intl","RelativeTimeFormat","numeric","DATE_ONLY_PATTERN","pad","value","length","String","padStart","selectUnit","elapsed","index","findIndex","undefined","Math","round","formatDateTime","timestamp","date","Date","Number","isFinite","getTime","getFullYear","getMonth","getDate","time","getHours","getMinutes","getSeconds","formatDuration","selected","abs","formatTimeAgo","now","delta","format","parseDateOnly","match","exec","isRoundTrip"],"mappings":"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"}